summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.bz2
linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/3w-9xxx.c2167
-rw-r--r--drivers/scsi/3w-9xxx.h682
-rw-r--r--drivers/scsi/3w-xxxx.c2478
-rw-r--r--drivers/scsi/3w-xxxx.h436
-rw-r--r--drivers/scsi/53c700.c2175
-rw-r--r--drivers/scsi/53c700.h649
-rw-r--r--drivers/scsi/53c700.scr411
-rw-r--r--drivers/scsi/53c700_d.h_shipped1329
-rw-r--r--drivers/scsi/53c7xx.c6102
-rw-r--r--drivers/scsi/53c7xx.h1608
-rw-r--r--drivers/scsi/53c7xx.scr1591
-rw-r--r--drivers/scsi/53c7xx_d.h_shipped2874
-rw-r--r--drivers/scsi/53c7xx_u.h_shipped102
-rw-r--r--drivers/scsi/BusLogic.c3574
-rw-r--r--drivers/scsi/BusLogic.h1359
-rw-r--r--drivers/scsi/FlashPoint.c12159
-rw-r--r--drivers/scsi/Kconfig1802
-rw-r--r--drivers/scsi/Makefile184
-rw-r--r--drivers/scsi/NCR5380.c2862
-rw-r--r--drivers/scsi/NCR5380.h432
-rw-r--r--drivers/scsi/NCR53C9x.c3649
-rw-r--r--drivers/scsi/NCR53C9x.h669
-rw-r--r--drivers/scsi/NCR53c406a.c1110
-rw-r--r--drivers/scsi/NCR_D700.c406
-rw-r--r--drivers/scsi/NCR_D700.h29
-rw-r--r--drivers/scsi/NCR_Q720.c377
-rw-r--r--drivers/scsi/NCR_Q720.h28
-rw-r--r--drivers/scsi/a100u2w.c1202
-rw-r--r--drivers/scsi/a100u2w.h416
-rw-r--r--drivers/scsi/a2091.c260
-rw-r--r--drivers/scsi/a2091.h76
-rw-r--r--drivers/scsi/a3000.c245
-rw-r--r--drivers/scsi/a3000.h79
-rw-r--r--drivers/scsi/aacraid/Makefile8
-rw-r--r--drivers/scsi/aacraid/README66
-rw-r--r--drivers/scsi/aacraid/TODO6
-rw-r--r--drivers/scsi/aacraid/aachba.c2037
-rw-r--r--drivers/scsi/aacraid/aacraid.h1623
-rw-r--r--drivers/scsi/aacraid/commctrl.c683
-rw-r--r--drivers/scsi/aacraid/comminit.c325
-rw-r--r--drivers/scsi/aacraid/commsup.c939
-rw-r--r--drivers/scsi/aacraid/dpcsup.c215
-rw-r--r--drivers/scsi/aacraid/linit.c749
-rw-r--r--drivers/scsi/aacraid/rkt.c440
-rw-r--r--drivers/scsi/aacraid/rx.c441
-rw-r--r--drivers/scsi/aacraid/sa.c374
-rw-r--r--drivers/scsi/advansys.c18237
-rw-r--r--drivers/scsi/advansys.h36
-rw-r--r--drivers/scsi/aha152x.c3982
-rw-r--r--drivers/scsi/aha152x.h337
-rw-r--r--drivers/scsi/aha1542.c1832
-rw-r--r--drivers/scsi/aha1542.h151
-rw-r--r--drivers/scsi/aha1740.c707
-rw-r--r--drivers/scsi/aha1740.h154
-rw-r--r--drivers/scsi/ahci.c1065
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic79xx97
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx100
-rw-r--r--drivers/scsi/aic7xxx/Makefile99
-rw-r--r--drivers/scsi/aic7xxx/aic7770.c415
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c264
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.h1537
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.reg3958
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.seq2058
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c9888
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_inline.h965
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c5017
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.h1147
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm_pci.c368
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_pci.c987
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_pci.h70
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_proc.c362
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg.h_shipped3776
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped3635
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_seq.h_shipped1139
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.h1352
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.reg1594
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.seq2398
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.c306
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.h102
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_core.c7451
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_inline.h649
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c5043
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.h1130
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c389
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_pci.c2407
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_pci.h124
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_proc.c385
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped1787
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped1681
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped1307
-rw-r--r--drivers/scsi/aic7xxx/aicasm/Makefile78
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.c835
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.h95
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_gram.y1945
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h131
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y164
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l156
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_scan.l607
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c677
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h207
-rw-r--r--drivers/scsi/aic7xxx/aiclib.c1412
-rw-r--r--drivers/scsi/aic7xxx/aiclib.h1085
-rw-r--r--drivers/scsi/aic7xxx/cam.h111
-rw-r--r--drivers/scsi/aic7xxx/queue.h501
-rw-r--r--drivers/scsi/aic7xxx/scsi_iu.h39
-rw-r--r--drivers/scsi/aic7xxx/scsi_message.h70
-rw-r--r--drivers/scsi/aic7xxx_old.c11178
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx.h28
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx.reg1401
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx.seq1539
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx_proc.c374
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx_reg.h629
-rw-r--r--drivers/scsi/aic7xxx_old/aic7xxx_seq.c817
-rw-r--r--drivers/scsi/aic7xxx_old/scsi_message.h49
-rw-r--r--drivers/scsi/aic7xxx_old/sequencer.h135
-rw-r--r--drivers/scsi/amiga7xx.c141
-rw-r--r--drivers/scsi/amiga7xx.h23
-rw-r--r--drivers/scsi/arm/Kconfig89
-rw-r--r--drivers/scsi/arm/Makefile14
-rw-r--r--drivers/scsi/arm/acornscsi-io.S145
-rw-r--r--drivers/scsi/arm/acornscsi.c3130
-rw-r--r--drivers/scsi/arm/acornscsi.h358
-rw-r--r--drivers/scsi/arm/arxescsi.c395
-rw-r--r--drivers/scsi/arm/cumana_1.c357
-rw-r--r--drivers/scsi/arm/cumana_2.c556
-rw-r--r--drivers/scsi/arm/ecoscsi.c239
-rw-r--r--drivers/scsi/arm/eesox.c680
-rw-r--r--drivers/scsi/arm/fas216.c3043
-rw-r--r--drivers/scsi/arm/fas216.h394
-rw-r--r--drivers/scsi/arm/msgqueue.c171
-rw-r--r--drivers/scsi/arm/msgqueue.h82
-rw-r--r--drivers/scsi/arm/oak.c217
-rw-r--r--drivers/scsi/arm/powertec.c472
-rw-r--r--drivers/scsi/arm/queue.c319
-rw-r--r--drivers/scsi/arm/queue.h105
-rw-r--r--drivers/scsi/arm/scsi.h115
-rw-r--r--drivers/scsi/ata_piix.c690
-rw-r--r--drivers/scsi/atari_NCR5380.c2986
-rw-r--r--drivers/scsi/atari_dma_emul.c466
-rw-r--r--drivers/scsi/atari_scsi.c1163
-rw-r--r--drivers/scsi/atari_scsi.h248
-rw-r--r--drivers/scsi/atp870u.c3970
-rw-r--r--drivers/scsi/atp870u.h66
-rw-r--r--drivers/scsi/blz1230.c352
-rw-r--r--drivers/scsi/blz2060.c306
-rw-r--r--drivers/scsi/bvme6000.c78
-rw-r--r--drivers/scsi/bvme6000.h24
-rw-r--r--drivers/scsi/constants.c1448
-rw-r--r--drivers/scsi/cpqfcTS.h19
-rw-r--r--drivers/scsi/cpqfcTSchip.h238
-rw-r--r--drivers/scsi/cpqfcTScontrol.c2231
-rw-r--r--drivers/scsi/cpqfcTSi2c.c493
-rw-r--r--drivers/scsi/cpqfcTSinit.c2098
-rw-r--r--drivers/scsi/cpqfcTSioctl.h94
-rw-r--r--drivers/scsi/cpqfcTSstructs.h1530
-rw-r--r--drivers/scsi/cpqfcTStrigger.c33
-rw-r--r--drivers/scsi/cpqfcTStrigger.h8
-rw-r--r--drivers/scsi/cpqfcTSworker.c6516
-rw-r--r--drivers/scsi/cyberstorm.c377
-rw-r--r--drivers/scsi/cyberstormII.c314
-rw-r--r--drivers/scsi/dc395x.c4942
-rw-r--r--drivers/scsi/dc395x.h648
-rw-r--r--drivers/scsi/dec_esp.c573
-rw-r--r--drivers/scsi/dmx3191d.c173
-rw-r--r--drivers/scsi/dpt/dpti_i2o.h459
-rw-r--r--drivers/scsi/dpt/dpti_ioctl.h139
-rw-r--r--drivers/scsi/dpt/dptsig.h339
-rw-r--r--drivers/scsi/dpt/osd_defs.h79
-rw-r--r--drivers/scsi/dpt/osd_util.h358
-rw-r--r--drivers/scsi/dpt/sys_info.h417
-rw-r--r--drivers/scsi/dpt_i2o.c3381
-rw-r--r--drivers/scsi/dpti.h359
-rw-r--r--drivers/scsi/dtc.c494
-rw-r--r--drivers/scsi/dtc.h99
-rw-r--r--drivers/scsi/eata.c2621
-rw-r--r--drivers/scsi/eata_generic.h406
-rw-r--r--drivers/scsi/eata_pio.c996
-rw-r--r--drivers/scsi/eata_pio.h53
-rw-r--r--drivers/scsi/esp.c4402
-rw-r--r--drivers/scsi/esp.h410
-rw-r--r--drivers/scsi/fastlane.c421
-rw-r--r--drivers/scsi/fcal.c320
-rw-r--r--drivers/scsi/fcal.h27
-rw-r--r--drivers/scsi/fd_mcs.c1369
-rw-r--r--drivers/scsi/fdomain.c1739
-rw-r--r--drivers/scsi/fdomain.h24
-rw-r--r--drivers/scsi/g_NCR5380.c947
-rw-r--r--drivers/scsi/g_NCR5380.h131
-rw-r--r--drivers/scsi/g_NCR5380_mmio.c10
-rw-r--r--drivers/scsi/gdth.c5738
-rw-r--r--drivers/scsi/gdth.h1079
-rw-r--r--drivers/scsi/gdth_ioctl.h347
-rw-r--r--drivers/scsi/gdth_kcompat.h21
-rw-r--r--drivers/scsi/gdth_proc.c1030
-rw-r--r--drivers/scsi/gdth_proc.h34
-rw-r--r--drivers/scsi/gvp11.c387
-rw-r--r--drivers/scsi/gvp11.h63
-rw-r--r--drivers/scsi/hosts.c462
-rw-r--r--drivers/scsi/hosts.h2
-rw-r--r--drivers/scsi/ibmmca.c2491
-rw-r--r--drivers/scsi/ibmmca.h21
-rw-r--r--drivers/scsi/ibmvscsi/Makefile5
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c1473
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.h109
-rw-r--r--drivers/scsi/ibmvscsi/iseries_vscsi.c144
-rw-r--r--drivers/scsi/ibmvscsi/rpa_vscsi.c260
-rw-r--r--drivers/scsi/ibmvscsi/srp.h225
-rw-r--r--drivers/scsi/ibmvscsi/viosrp.h126
-rw-r--r--drivers/scsi/ide-scsi.c1174
-rw-r--r--drivers/scsi/imm.c1300
-rw-r--r--drivers/scsi/imm.h144
-rw-r--r--drivers/scsi/in2000.c2323
-rw-r--r--drivers/scsi/in2000.h414
-rw-r--r--drivers/scsi/initio.c3184
-rw-r--r--drivers/scsi/initio.h739
-rw-r--r--drivers/scsi/ipr.c6083
-rw-r--r--drivers/scsi/ipr.h1261
-rw-r--r--drivers/scsi/ips.c7491
-rw-r--r--drivers/scsi/ips.h1297
-rw-r--r--drivers/scsi/jazz_esp.c329
-rw-r--r--drivers/scsi/lasi700.c189
-rw-r--r--drivers/scsi/libata-core.c4024
-rw-r--r--drivers/scsi/libata-scsi.c1593
-rw-r--r--drivers/scsi/libata.h89
-rw-r--r--drivers/scsi/mac53c94.c582
-rw-r--r--drivers/scsi/mac53c94.h214
-rw-r--r--drivers/scsi/mac_esp.c754
-rw-r--r--drivers/scsi/mac_scsi.c605
-rw-r--r--drivers/scsi/mac_scsi.h85
-rw-r--r--drivers/scsi/mca_53c9x.c520
-rw-r--r--drivers/scsi/megaraid.c5122
-rw-r--r--drivers/scsi/megaraid.h1071
-rw-r--r--drivers/scsi/megaraid/Kconfig.megaraid78
-rw-r--r--drivers/scsi/megaraid/Makefile2
-rw-r--r--drivers/scsi/megaraid/mbox_defs.h790
-rw-r--r--drivers/scsi/megaraid/mega_common.h286
-rw-r--r--drivers/scsi/megaraid/megaraid_ioctl.h296
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c4276
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.h288
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.c1255
-rw-r--r--drivers/scsi/megaraid/megaraid_mm.h102
-rw-r--r--drivers/scsi/mesh.c2062
-rw-r--r--drivers/scsi/mesh.h127
-rw-r--r--drivers/scsi/mvme147.c155
-rw-r--r--drivers/scsi/mvme147.h28
-rw-r--r--drivers/scsi/mvme16x.c80
-rw-r--r--drivers/scsi/mvme16x.h24
-rw-r--r--drivers/scsi/ncr53c8xx.c7986
-rw-r--r--drivers/scsi/ncr53c8xx.h101
-rw-r--r--drivers/scsi/nsp32.c3585
-rw-r--r--drivers/scsi/nsp32.h664
-rw-r--r--drivers/scsi/nsp32_debug.c263
-rw-r--r--drivers/scsi/nsp32_io.h259
-rw-r--r--drivers/scsi/oktagon_esp.c609
-rw-r--r--drivers/scsi/oktagon_io.S195
-rw-r--r--drivers/scsi/osst.c5914
-rw-r--r--drivers/scsi/osst.h638
-rw-r--r--drivers/scsi/osst_detect.h6
-rw-r--r--drivers/scsi/osst_options.h106
-rw-r--r--drivers/scsi/pas16.c639
-rw-r--r--drivers/scsi/pas16.h179
-rw-r--r--drivers/scsi/pci2000.c834
-rw-r--r--drivers/scsi/pci2000.h200
-rw-r--r--drivers/scsi/pci2220i.c2915
-rw-r--r--drivers/scsi/pci2220i.h39
-rw-r--r--drivers/scsi/pcmcia/Kconfig82
-rw-r--r--drivers/scsi/pcmcia/Makefile13
-rw-r--r--drivers/scsi/pcmcia/aha152x_core.c3
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c343
-rw-r--r--drivers/scsi/pcmcia/fdomain_core.c2
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c323
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c2198
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.h472
-rw-r--r--drivers/scsi/pcmcia/nsp_debug.c215
-rw-r--r--drivers/scsi/pcmcia/nsp_io.h274
-rw-r--r--drivers/scsi/pcmcia/nsp_message.c78
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c425
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c1022
-rw-r--r--drivers/scsi/pluto.c364
-rw-r--r--drivers/scsi/pluto.h47
-rw-r--r--drivers/scsi/ppa.c1150
-rw-r--r--drivers/scsi/ppa.h151
-rw-r--r--drivers/scsi/psi240i.c685
-rw-r--r--drivers/scsi/psi240i.h315
-rw-r--r--drivers/scsi/psi_chip.h195
-rw-r--r--drivers/scsi/psi_dale.h564
-rw-r--r--drivers/scsi/psi_roy.h331
-rw-r--r--drivers/scsi/ql1040_fw.h2099
-rw-r--r--drivers/scsi/ql12160_fw.h1781
-rw-r--r--drivers/scsi/ql1280_fw.h2017
-rw-r--r--drivers/scsi/qla1280.c4863
-rw-r--r--drivers/scsi/qla1280.h1098
-rw-r--r--drivers/scsi/qla2xxx/Kconfig41
-rw-r--r--drivers/scsi/qla2xxx/Makefile16
-rw-r--r--drivers/scsi/qla2xxx/ql2100.c92
-rw-r--r--drivers/scsi/qla2xxx/ql2100_fw.c4858
-rw-r--r--drivers/scsi/qla2xxx/ql2200.c92
-rw-r--r--drivers/scsi/qla2xxx/ql2200_fw.c5321
-rw-r--r--drivers/scsi/qla2xxx/ql2300.c103
-rw-r--r--drivers/scsi/qla2xxx/ql2300_fw.c7590
-rw-r--r--drivers/scsi/qla2xxx/ql2322.c108
-rw-r--r--drivers/scsi/qla2xxx/ql2322_fw.c8124
-rw-r--r--drivers/scsi/qla2xxx/ql6312.c102
-rw-r--r--drivers/scsi/qla2xxx/ql6312_fw.c7147
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c1158
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h233
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h2497
-rw-r--r--drivers/scsi/qla2xxx/qla_devtbl.h110
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h257
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c1059
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c3908
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h292
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c633
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c1464
-rw-r--r--drivers/scsi/qla2xxx/qla_listops.h351
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c1950
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c4456
-rw-r--r--drivers/scsi/qla2xxx/qla_rscn.c1437
-rw-r--r--drivers/scsi/qla2xxx/qla_settings.h64
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c296
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h27
-rw-r--r--drivers/scsi/qlogicfas.c230
-rw-r--r--drivers/scsi/qlogicfas408.c637
-rw-r--r--drivers/scsi/qlogicfas408.h120
-rw-r--r--drivers/scsi/qlogicfc.c2227
-rw-r--r--drivers/scsi/qlogicfc_asm.c9751
-rw-r--r--drivers/scsi/qlogicisp.c1935
-rw-r--r--drivers/scsi/qlogicisp_asm.c2034
-rw-r--r--drivers/scsi/qlogicpti.c1541
-rw-r--r--drivers/scsi/qlogicpti.h508
-rw-r--r--drivers/scsi/qlogicpti_asm.c1160
-rw-r--r--drivers/scsi/sata_nv.c570
-rw-r--r--drivers/scsi/sata_promise.c682
-rw-r--r--drivers/scsi/sata_promise.h154
-rw-r--r--drivers/scsi/sata_qstor.c718
-rw-r--r--drivers/scsi/sata_sil.c494
-rw-r--r--drivers/scsi/sata_sis.c286
-rw-r--r--drivers/scsi/sata_svw.c483
-rw-r--r--drivers/scsi/sata_sx4.c1503
-rw-r--r--drivers/scsi/sata_uli.c287
-rw-r--r--drivers/scsi/sata_via.c387
-rw-r--r--drivers/scsi/sata_vsc.c407
-rw-r--r--drivers/scsi/script_asm.pl984
-rw-r--r--drivers/scsi/scsi.c1375
-rw-r--r--drivers/scsi/scsi.h109
-rw-r--r--drivers/scsi/scsi_debug.c1976
-rw-r--r--drivers/scsi/scsi_debug.h24
-rw-r--r--drivers/scsi/scsi_devinfo.c564
-rw-r--r--drivers/scsi/scsi_error.c2050
-rw-r--r--drivers/scsi/scsi_ioctl.c516
-rw-r--r--drivers/scsi/scsi_lib.c2023
-rw-r--r--drivers/scsi/scsi_logging.h82
-rw-r--r--drivers/scsi/scsi_module.c73
-rw-r--r--drivers/scsi/scsi_obsolete.h106
-rw-r--r--drivers/scsi/scsi_priv.h165
-rw-r--r--drivers/scsi/scsi_proc.c336
-rw-r--r--drivers/scsi/scsi_scan.c1473
-rw-r--r--drivers/scsi/scsi_sysctl.c53
-rw-r--r--drivers/scsi/scsi_sysfs.c816
-rw-r--r--drivers/scsi/scsi_transport_fc.c1665
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c388
-rw-r--r--drivers/scsi/scsi_transport_spi.c1020
-rw-r--r--drivers/scsi/scsi_typedefs.h6
-rw-r--r--drivers/scsi/scsicam.c245
-rw-r--r--drivers/scsi/sd.c1740
-rw-r--r--drivers/scsi/seagate.c1675
-rw-r--r--drivers/scsi/seagate.h21
-rw-r--r--drivers/scsi/sg.c3092
-rw-r--r--drivers/scsi/sgiwd93.c337
-rw-r--r--drivers/scsi/sgiwd93.h24
-rw-r--r--drivers/scsi/sim710.c372
-rw-r--r--drivers/scsi/sr.c965
-rw-r--r--drivers/scsi/sr.h68
-rw-r--r--drivers/scsi/sr_ioctl.c568
-rw-r--r--drivers/scsi/sr_vendor.c329
-rw-r--r--drivers/scsi/st.c4438
-rw-r--r--drivers/scsi/st.h212
-rw-r--r--drivers/scsi/st_options.h100
-rw-r--r--drivers/scsi/sun3_NCR5380.c3009
-rw-r--r--drivers/scsi/sun3_scsi.c642
-rw-r--r--drivers/scsi/sun3_scsi.h379
-rw-r--r--drivers/scsi/sun3_scsi_vme.c584
-rw-r--r--drivers/scsi/sun3x_esp.c394
-rw-r--r--drivers/scsi/sym53c416.c881
-rw-r--r--drivers/scsi/sym53c416.h36
-rw-r--r--drivers/scsi/sym53c8xx_2/Makefile4
-rw-r--r--drivers/scsi/sym53c8xx_2/sym53c8xx.h217
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_defs.h792
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw.c568
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw.h211
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw1.h1838
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_fw2.h1927
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c2196
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.h300
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.c5865
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.h1304
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_malloc.c382
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_misc.h192
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_nvram.c771
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_nvram.h214
-rw-r--r--drivers/scsi/sym53c8xx_comm.h792
-rw-r--r--drivers/scsi/sym53c8xx_defs.h1333
-rw-r--r--drivers/scsi/t128.c449
-rw-r--r--drivers/scsi/t128.h155
-rw-r--r--drivers/scsi/tmscsim.c2693
-rw-r--r--drivers/scsi/tmscsim.h565
-rw-r--r--drivers/scsi/u14-34f.c1987
-rw-r--r--drivers/scsi/ultrastor.c1204
-rw-r--r--drivers/scsi/ultrastor.h79
-rw-r--r--drivers/scsi/wd33c93.c2077
-rw-r--r--drivers/scsi/wd33c93.h348
-rw-r--r--drivers/scsi/wd7000.c1667
-rw-r--r--drivers/scsi/zalon.c205
413 files changed, 496153 insertions, 0 deletions
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c
new file mode 100644
index 000000000000..a2b18f5a4f93
--- /dev/null
+++ b/drivers/scsi/3w-9xxx.c
@@ -0,0 +1,2167 @@
+/*
+ 3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@amcc.com>
+
+ Copyright (C) 2004-2005 Applied Micro Circuits Corporation.
+
+ This program is free software; 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 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@amcc.com
+
+ For more information, goto:
+ http://www.amcc.com
+
+ Note: This version of the driver does not contain a bundled firmware
+ image.
+
+ History
+ -------
+ 2.26.02.000 - Driver cleanup for kernel submission.
+ 2.26.02.001 - Replace schedule_timeout() calls with msleep().
+ 2.26.02.002 - Add support for PAE mode.
+ Add lun support.
+ Fix twa_remove() to free irq handler/unregister_chrdev()
+ before shutting down card.
+ Change to new 'change_queue_depth' api.
+ Fix 'handled=1' ISR usage, remove bogus IRQ check.
+ Remove un-needed eh_abort handler.
+ Add support for embedded firmware error strings.
+*/
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include "3w-9xxx.h"
+
+/* Globals */
+#define TW_DRIVER_VERSION "2.26.02.002"
+static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
+static unsigned int twa_device_extension_count;
+static int twa_major = -1;
+extern struct timezone sys_tz;
+
+/* Module parameters */
+MODULE_AUTHOR ("AMCC");
+MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(TW_DRIVER_VERSION);
+
+/* Function prototypes */
+static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
+static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
+static char *twa_aen_severity_lookup(unsigned char severity_code);
+static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
+static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int twa_chrdev_open(struct inode *inode, struct file *file);
+static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
+static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
+static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
+static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
+ u32 *init_connect_result);
+static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
+static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
+static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
+static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
+static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset);
+static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg);
+static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
+static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
+static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
+
+/* Functions */
+
+/* Show some statistics about the card */
+static ssize_t twa_show_stats(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(class_dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+ unsigned long flags = 0;
+ ssize_t len;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ len = snprintf(buf, PAGE_SIZE, "3w-9xxx Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Current pending commands: %4d\n"
+ "Max pending commands: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->pending_request_count,
+ tw_dev->max_pending_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ return len;
+} /* End twa_show_stats() */
+
+/* This function will set a devices queue depth */
+static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+{
+ if (queue_depth > TW_Q_LENGTH-2)
+ queue_depth = TW_Q_LENGTH-2;
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
+ return queue_depth;
+} /* End twa_change_queue_depth() */
+
+/* Create sysfs 'stats' entry */
+static struct class_device_attribute twa_host_stats_attr = {
+ .attr = {
+ .name = "stats",
+ .mode = S_IRUGO,
+ },
+ .show = twa_show_stats
+};
+
+/* Host attributes initializer */
+static struct class_device_attribute *twa_host_attrs[] = {
+ &twa_host_stats_attr,
+ NULL,
+};
+
+/* File operations struct for character device */
+static struct file_operations twa_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = twa_chrdev_ioctl,
+ .open = twa_chrdev_open,
+ .release = NULL
+};
+
+/* This function will complete an aen request from the isr */
+static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Command_Apache_Header *header;
+ unsigned short aen;
+ int retval = 1;
+
+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+ tw_dev->posted_request_count--;
+ aen = header->status_block.error;
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ command_packet = &full_command_packet->command.oldcommand;
+
+ /* First check for internal completion of set param for time sync */
+ if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) {
+ /* Keep reading the queue in case there are more aen's */
+ if (twa_aen_read_queue(tw_dev, request_id))
+ goto out2;
+ else {
+ retval = 0;
+ goto out;
+ }
+ }
+
+ switch (aen) {
+ case TW_AEN_QUEUE_EMPTY:
+ /* Quit reading the queue if this is the last one */
+ break;
+ case TW_AEN_SYNC_TIME_WITH_HOST:
+ twa_aen_sync_time(tw_dev, request_id);
+ retval = 0;
+ goto out;
+ default:
+ twa_aen_queue_event(tw_dev, header);
+
+ /* If there are more aen's, keep reading the queue */
+ if (twa_aen_read_queue(tw_dev, request_id))
+ goto out2;
+ else {
+ retval = 0;
+ goto out;
+ }
+ }
+ retval = 0;
+out2:
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+out:
+ return retval;
+} /* End twa_aen_complete() */
+
+/* This function will drain aen queue */
+static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
+{
+ int request_id = 0;
+ char cdb[TW_MAX_CDB_LEN];
+ TW_SG_Entry sglist[1];
+ int finished = 0, count = 0;
+ TW_Command_Full *full_command_packet;
+ TW_Command_Apache_Header *header;
+ unsigned short aen;
+ int first_reset = 0, queue = 0, retval = 1;
+
+ if (no_check_reset)
+ first_reset = 0;
+ else
+ first_reset = 1;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+ /* Initialize cdb */
+ memset(&cdb, 0, TW_MAX_CDB_LEN);
+ cdb[0] = REQUEST_SENSE; /* opcode */
+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+ /* Initialize sglist */
+ memset(&sglist, 0, sizeof(TW_SG_Entry));
+ sglist[0].length = TW_SECTOR_SIZE;
+ sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+ if (sglist[0].address & TW_ALIGNMENT_9000_SGL) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain");
+ goto out;
+ }
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ do {
+ /* Send command to the board */
+ if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Error posting request sense");
+ goto out;
+ }
+
+ /* Now poll for completion */
+ if (twa_poll_response(tw_dev, request_id, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "No valid response while draining AEN queue");
+ tw_dev->posted_request_count--;
+ goto out;
+ }
+
+ tw_dev->posted_request_count--;
+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+ aen = header->status_block.error;
+ queue = 0;
+ count++;
+
+ switch (aen) {
+ case TW_AEN_QUEUE_EMPTY:
+ if (first_reset != 1)
+ goto out;
+ else
+ finished = 1;
+ break;
+ case TW_AEN_SOFT_RESET:
+ if (first_reset == 0)
+ first_reset = 1;
+ else
+ queue = 1;
+ break;
+ case TW_AEN_SYNC_TIME_WITH_HOST:
+ break;
+ default:
+ queue = 1;
+ }
+
+ /* Now queue an event info */
+ if (queue)
+ twa_aen_queue_event(tw_dev, header);
+ } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
+
+ if (count == TW_MAX_AEN_DRAIN)
+ goto out;
+
+ retval = 0;
+out:
+ tw_dev->state[request_id] = TW_S_INITIAL;
+ return retval;
+} /* End twa_aen_drain_queue() */
+
+/* This function will queue an event */
+static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
+{
+ u32 local_time;
+ struct timeval time;
+ TW_Event *event;
+ unsigned short aen;
+ char host[16];
+ char *error_str;
+
+ tw_dev->aen_count++;
+
+ /* Fill out event info */
+ event = tw_dev->event_queue[tw_dev->error_index];
+
+ /* Check for clobber */
+ host[0] = '\0';
+ if (tw_dev->host) {
+ sprintf(host, " scsi%d:", tw_dev->host->host_no);
+ if (event->retrieved == TW_AEN_NOT_RETRIEVED)
+ tw_dev->aen_clobber = 1;
+ }
+
+ aen = header->status_block.error;
+ memset(event, 0, sizeof(TW_Event));
+
+ event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
+ do_gettimeofday(&time);
+ local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+ event->time_stamp_sec = local_time;
+ event->aen_code = aen;
+ event->retrieved = TW_AEN_NOT_RETRIEVED;
+ event->sequence_id = tw_dev->error_sequence_id;
+ tw_dev->error_sequence_id++;
+
+ /* Check for embedded error string */
+ error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
+
+ header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+ event->parameter_len = strlen(header->err_specific_desc);
+ memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len);
+ if (event->severity != TW_AEN_SEVERITY_DEBUG)
+ printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
+ host,
+ twa_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),
+ TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen,
+ error_str[0] == '\0' ? twa_string_lookup(twa_aen_table, aen) : error_str,
+ header->err_specific_desc);
+ else
+ tw_dev->aen_count--;
+
+ if ((tw_dev->error_index + 1) == TW_Q_LENGTH)
+ tw_dev->event_queue_wrapped = 1;
+ tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
+} /* End twa_aen_queue_event() */
+
+/* This function will read the aen queue from the isr */
+static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+{
+ char cdb[TW_MAX_CDB_LEN];
+ TW_SG_Entry sglist[1];
+ TW_Command_Full *full_command_packet;
+ int retval = 1;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+ /* Initialize cdb */
+ memset(&cdb, 0, TW_MAX_CDB_LEN);
+ cdb[0] = REQUEST_SENSE; /* opcode */
+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+ /* Initialize sglist */
+ memset(&sglist, 0, sizeof(TW_SG_Entry));
+ sglist[0].length = TW_SECTOR_SIZE;
+ sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Now post the command packet */
+ if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "Post failed while reading AEN queue");
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_aen_read_queue() */
+
+/* This function will look up an AEN severity string */
+static char *twa_aen_severity_lookup(unsigned char severity_code)
+{
+ char *retval = NULL;
+
+ if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
+ (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
+ goto out;
+
+ retval = twa_aen_severity_table[severity_code];
+out:
+ return retval;
+} /* End twa_aen_severity_lookup() */
+
+/* This function will sync firmware time with the host time */
+static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
+{
+ u32 schedulertime;
+ struct timeval utc;
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Param_Apache *param;
+ u32 local_time;
+
+ /* Fill out the command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ command_packet = &full_command_packet->command.oldcommand;
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
+ command_packet->request_id = request_id;
+ command_packet->byte8_offset.param.sgl[0].address = tw_dev->generic_buffer_phys[request_id];
+ command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->byte6_offset.parameter_count = 1;
+
+ /* Setup the param */
+ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+ memset(param, 0, TW_SECTOR_SIZE);
+ param->table_id = TW_TIMEKEEP_TABLE | 0x8000; /* Controller time keep table */
+ param->parameter_id = 0x3; /* SchedulerTime */
+ param->parameter_size_bytes = 4;
+
+ /* Convert system time in UTC to local time seconds since last
+ Sunday 12:00AM */
+ do_gettimeofday(&utc);
+ local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
+ schedulertime = local_time - (3 * 86400);
+ schedulertime = schedulertime % 604800;
+
+ memcpy(param->data, &schedulertime, sizeof(u32));
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Now post the command */
+ twa_post_command_packet(tw_dev, request_id, 1);
+} /* End twa_aen_sync_time() */
+
+/* This function will allocate memory and check if it is correctly aligned */
+static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
+{
+ int i;
+ dma_addr_t dma_handle;
+ unsigned long *cpu_addr;
+ int retval = 1;
+
+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ if (!cpu_addr) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
+ goto out;
+ }
+
+ if ((unsigned long)cpu_addr % (TW_ALIGNMENT_9000)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x6, "Failed to allocate correctly aligned memory");
+ pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
+ goto out;
+ }
+
+ memset(cpu_addr, 0, size*TW_Q_LENGTH);
+
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ switch(which) {
+ case 0:
+ tw_dev->command_packet_phys[i] = dma_handle+(i*size);
+ tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ case 1:
+ tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
+ tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ }
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_allocate_memory() */
+
+/* This function will check the status register for unexpected bits */
+static int twa_check_bits(u32 status_reg_value)
+{
+ int retval = 1;
+
+ if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS)
+ goto out;
+ if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0)
+ goto out;
+
+ retval = 0;
+out:
+ return retval;
+} /* End twa_check_bits() */
+
+/* This function will check the srl and decide if we are compatible */
+static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
+{
+ int retval = 1;
+ unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
+ unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
+ u32 init_connect_result = 0;
+
+ if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
+ TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL,
+ TW_9000_ARCH_ID, TW_CURRENT_DRIVER_BRANCH,
+ TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl,
+ &fw_on_ctlr_arch_id, &fw_on_ctlr_branch,
+ &fw_on_ctlr_build, &init_connect_result)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "Initconnection failed while checking SRL");
+ goto out;
+ }
+
+ tw_dev->working_srl = fw_on_ctlr_srl;
+ tw_dev->working_branch = fw_on_ctlr_branch;
+ tw_dev->working_build = fw_on_ctlr_build;
+
+ /* Try base mode compatibility */
+ if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
+ if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
+ TW_EXTENDED_INIT_CONNECT,
+ TW_BASE_FW_SRL, TW_9000_ARCH_ID,
+ TW_BASE_FW_BRANCH, TW_BASE_FW_BUILD,
+ &fw_on_ctlr_srl, &fw_on_ctlr_arch_id,
+ &fw_on_ctlr_branch, &fw_on_ctlr_build,
+ &init_connect_result)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Initconnection (base mode) failed while checking SRL");
+ goto out;
+ }
+ if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
+ if (TW_CURRENT_DRIVER_SRL > fw_on_ctlr_srl) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x32, "Firmware and driver incompatibility: please upgrade firmware");
+ } else {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x33, "Firmware and driver incompatibility: please upgrade driver");
+ }
+ goto out;
+ }
+ tw_dev->working_srl = TW_BASE_FW_SRL;
+ tw_dev->working_branch = TW_BASE_FW_BRANCH;
+ tw_dev->working_build = TW_BASE_FW_BUILD;
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_check_srl() */
+
+/* This function handles ioctl for the character device */
+static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long timeout;
+ unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
+ dma_addr_t dma_handle;
+ int request_id = 0;
+ unsigned int sequence_id = 0;
+ unsigned char event_index, start_index;
+ TW_Ioctl_Driver_Command driver_command;
+ TW_Ioctl_Buf_Apache *tw_ioctl;
+ TW_Lock *tw_lock;
+ TW_Command_Full *full_command_packet;
+ TW_Compatibility_Info *tw_compat_info;
+ TW_Event *event;
+ struct timeval current_time;
+ u32 current_time_ms;
+ TW_Device_Extension *tw_dev = twa_device_extension_list[iminor(inode)];
+ int retval = TW_IOCTL_ERROR_OS_EFAULT;
+ void __user *argp = (void __user *)arg;
+
+ /* Only let one of these through at a time */
+ if (down_interruptible(&tw_dev->ioctl_sem)) {
+ retval = TW_IOCTL_ERROR_OS_EINTR;
+ goto out;
+ }
+
+ /* First copy down the driver command */
+ if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command)))
+ goto out2;
+
+ /* Check data buffer size */
+ if (driver_command.buffer_length > TW_MAX_SECTORS * 512) {
+ retval = TW_IOCTL_ERROR_OS_EINVAL;
+ goto out2;
+ }
+
+ /* Hardware can only do multiple of 512 byte transfers */
+ data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
+
+ /* Now allocate ioctl buf memory */
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL);
+ if (!cpu_addr) {
+ retval = TW_IOCTL_ERROR_OS_ENOMEM;
+ goto out2;
+ }
+
+ tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
+
+ /* Now copy down the entire ioctl */
+ if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+ goto out3;
+
+ /* See which ioctl we are doing */
+ switch (cmd) {
+ case TW_IOCTL_FIRMWARE_PASS_THROUGH:
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ twa_get_request_id(tw_dev, &request_id);
+
+ /* Flag internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Flag chrdev ioctl */
+ tw_dev->chrdev_request_id = request_id;
+
+ full_command_packet = &tw_ioctl->firmware_command;
+
+ /* Load request id and sglist for both command types */
+ twa_load_sgl(full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
+
+ memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full));
+
+ /* Now post the command packet to the controller */
+ twa_post_command_packet(tw_dev, request_id, 1);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
+ /* Now wait for command to complete */
+ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+
+ /* See if we reset while waiting for the ioctl to complete */
+ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
+ clear_bit(TW_IN_RESET, &tw_dev->flags);
+ retval = TW_IOCTL_ERROR_OS_ERESTARTSYS;
+ goto out3;
+ }
+
+ /* We timed out, and didn't get an interrupt */
+ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+ /* Now we need to reset the board */
+ printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
+ tw_dev->host->host_no, TW_DRIVER, 0xc,
+ cmd);
+ retval = TW_IOCTL_ERROR_OS_EIO;
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ twa_reset_device_extension(tw_dev, 1);
+ goto out3;
+ }
+
+ /* Now copy in the command packet response */
+ memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
+
+ /* Now complete the io */
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ break;
+ case TW_IOCTL_GET_COMPATIBILITY_INFO:
+ tw_ioctl->driver_command.status = 0;
+ /* Copy compatiblity struct into ioctl data buffer */
+ tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer;
+ strncpy(tw_compat_info->driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+ tw_compat_info->working_srl = tw_dev->working_srl;
+ tw_compat_info->working_branch = tw_dev->working_branch;
+ tw_compat_info->working_build = tw_dev->working_build;
+ tw_compat_info->driver_srl_high = TW_CURRENT_DRIVER_SRL;
+ tw_compat_info->driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
+ tw_compat_info->driver_build_high = TW_CURRENT_DRIVER_BUILD;
+ tw_compat_info->driver_srl_low = TW_BASE_FW_SRL;
+ tw_compat_info->driver_branch_low = TW_BASE_FW_BRANCH;
+ tw_compat_info->driver_build_low = TW_BASE_FW_BUILD;
+ break;
+ case TW_IOCTL_GET_LAST_EVENT:
+ if (tw_dev->event_queue_wrapped) {
+ if (tw_dev->aen_clobber) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+ tw_dev->aen_clobber = 0;
+ } else
+ tw_ioctl->driver_command.status = 0;
+ } else {
+ if (!tw_dev->error_index) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+ break;
+ }
+ tw_ioctl->driver_command.status = 0;
+ }
+ event_index = (tw_dev->error_index - 1 + TW_Q_LENGTH) % TW_Q_LENGTH;
+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+ break;
+ case TW_IOCTL_GET_FIRST_EVENT:
+ if (tw_dev->event_queue_wrapped) {
+ if (tw_dev->aen_clobber) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+ tw_dev->aen_clobber = 0;
+ } else
+ tw_ioctl->driver_command.status = 0;
+ event_index = tw_dev->error_index;
+ } else {
+ if (!tw_dev->error_index) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+ break;
+ }
+ tw_ioctl->driver_command.status = 0;
+ event_index = 0;
+ }
+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+ break;
+ case TW_IOCTL_GET_NEXT_EVENT:
+ event = (TW_Event *)tw_ioctl->data_buffer;
+ sequence_id = event->sequence_id;
+ tw_ioctl->driver_command.status = 0;
+
+ if (tw_dev->event_queue_wrapped) {
+ if (tw_dev->aen_clobber) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+ tw_dev->aen_clobber = 0;
+ }
+ start_index = tw_dev->error_index;
+ } else {
+ if (!tw_dev->error_index) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+ break;
+ }
+ start_index = 0;
+ }
+ event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id + 1) % TW_Q_LENGTH;
+
+ if (!(tw_dev->event_queue[event_index]->sequence_id > sequence_id)) {
+ if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER)
+ tw_dev->aen_clobber = 1;
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+ break;
+ }
+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+ break;
+ case TW_IOCTL_GET_PREVIOUS_EVENT:
+ event = (TW_Event *)tw_ioctl->data_buffer;
+ sequence_id = event->sequence_id;
+ tw_ioctl->driver_command.status = 0;
+
+ if (tw_dev->event_queue_wrapped) {
+ if (tw_dev->aen_clobber) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+ tw_dev->aen_clobber = 0;
+ }
+ start_index = tw_dev->error_index;
+ } else {
+ if (!tw_dev->error_index) {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+ break;
+ }
+ start_index = 0;
+ }
+ event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id - 1) % TW_Q_LENGTH;
+
+ if (!(tw_dev->event_queue[event_index]->sequence_id < sequence_id)) {
+ if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER)
+ tw_dev->aen_clobber = 1;
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+ break;
+ }
+ memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+ tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+ break;
+ case TW_IOCTL_GET_LOCK:
+ tw_lock = (TW_Lock *)tw_ioctl->data_buffer;
+ do_gettimeofday(&current_time);
+ current_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_usec / 1000);
+
+ if ((tw_lock->force_flag == 1) || (tw_dev->ioctl_sem_lock == 0) || (current_time_ms >= tw_dev->ioctl_msec)) {
+ tw_dev->ioctl_sem_lock = 1;
+ tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec;
+ tw_ioctl->driver_command.status = 0;
+ tw_lock->time_remaining_msec = tw_lock->timeout_msec;
+ } else {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_LOCKED;
+ tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms;
+ }
+ break;
+ case TW_IOCTL_RELEASE_LOCK:
+ if (tw_dev->ioctl_sem_lock == 1) {
+ tw_dev->ioctl_sem_lock = 0;
+ tw_ioctl->driver_command.status = 0;
+ } else {
+ tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NOT_LOCKED;
+ }
+ break;
+ default:
+ retval = TW_IOCTL_ERROR_OS_ENOTTY;
+ goto out3;
+ }
+
+ /* Now copy the entire response to userspace */
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+ retval = 0;
+out3:
+ /* Now free ioctl buf memory */
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+out2:
+ up(&tw_dev->ioctl_sem);
+out:
+ return retval;
+} /* End twa_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int twa_chrdev_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor_number;
+ int retval = TW_IOCTL_ERROR_OS_ENODEV;
+
+ minor_number = iminor(inode);
+ if (minor_number >= twa_device_extension_count)
+ goto out;
+ retval = 0;
+out:
+ return retval;
+} /* End twa_chrdev_open() */
+
+/* This function will print readable messages from status register errors */
+static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
+{
+ int retval = 1;
+
+ /* Check for various error conditions and handle them appropriately */
+ if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "PCI Parity Error: clearing");
+ writel(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+ }
+
+ if (status_reg_value & TW_STATUS_PCI_ABORT) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "PCI Abort: clearing");
+ writel(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev));
+ pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
+ }
+
+ if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing");
+ writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+ }
+
+ if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "SBUF Write Error: clearing");
+ writel(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+ }
+
+ if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
+ if (tw_dev->reset_print == 0) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Microcontroller Error: clearing");
+ tw_dev->reset_print = 1;
+ }
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_decode_bits() */
+
+/* This function will empty the response queue */
+static int twa_empty_response_queue(TW_Device_Extension *tw_dev)
+{
+ u32 status_reg_value, response_que_value;
+ int count = 0, retval = 1;
+
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+ while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) {
+ response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+ count++;
+ }
+ if (count == TW_MAX_RESPONSE_DRAIN)
+ goto out;
+
+ retval = 0;
+out:
+ return retval;
+} /* End twa_empty_response_queue() */
+
+/* This function passes sense keys from firmware to scsi layer */
+static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host)
+{
+ TW_Command_Full *full_command_packet;
+ unsigned short error;
+ int retval = 1;
+ char *error_str;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+
+ /* Check for embedded error string */
+ error_str = &(full_command_packet->header.err_specific_desc[strlen(full_command_packet->header.err_specific_desc) + 1]);
+
+ /* Don't print error for Logical unit not supported during rollcall */
+ error = full_command_packet->header.status_block.error;
+ if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE)) {
+ if (print_host)
+ printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+ tw_dev->host->host_no,
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ full_command_packet->header.status_block.error,
+ error_str[0] == '\0' ?
+ twa_string_lookup(twa_error_table,
+ full_command_packet->header.status_block.error) : error_str,
+ full_command_packet->header.err_specific_desc);
+ else
+ printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ full_command_packet->header.status_block.error,
+ error_str[0] == '\0' ?
+ twa_string_lookup(twa_error_table,
+ full_command_packet->header.status_block.error) : error_str,
+ full_command_packet->header.err_specific_desc);
+ }
+
+ if (copy_sense) {
+ memcpy(tw_dev->srb[request_id]->sense_buffer, full_command_packet->header.sense_data, TW_SENSE_DATA_LENGTH);
+ tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1);
+ retval = TW_ISR_DONT_RESULT;
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_fill_sense() */
+
+/* This function will free up device extension resources */
+static void twa_free_device_extension(TW_Device_Extension *tw_dev)
+{
+ if (tw_dev->command_packet_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ sizeof(TW_Command_Full)*TW_Q_LENGTH,
+ tw_dev->command_packet_virt[0],
+ tw_dev->command_packet_phys[0]);
+
+ if (tw_dev->generic_buffer_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ TW_SECTOR_SIZE*TW_Q_LENGTH,
+ tw_dev->generic_buffer_virt[0],
+ tw_dev->generic_buffer_phys[0]);
+
+ if (tw_dev->event_queue[0])
+ kfree(tw_dev->event_queue[0]);
+} /* End twa_free_device_extension() */
+
+/* This function will free a request id */
+static void twa_free_request_id(TW_Device_Extension *tw_dev, int request_id)
+{
+ tw_dev->free_queue[tw_dev->free_tail] = request_id;
+ tw_dev->state[request_id] = TW_S_FINISHED;
+ tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+} /* End twa_free_request_id() */
+
+/* This function will get parameter table entires from the firmware */
+static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Param_Apache *param;
+ unsigned long param_value;
+ void *retval = NULL;
+
+ /* Setup the command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ command_packet = &full_command_packet->command.oldcommand;
+
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->request_id = request_id;
+ command_packet->byte6_offset.block_count = 1;
+
+ /* Now setup the param */
+ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+ memset(param, 0, TW_SECTOR_SIZE);
+ param->table_id = table_id | 0x8000;
+ param->parameter_id = parameter_id;
+ param->parameter_size_bytes = parameter_size_bytes;
+ param_value = tw_dev->generic_buffer_phys[request_id];
+
+ command_packet->byte8_offset.param.sgl[0].address = param_value;
+ command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
+
+ /* Post the command packet to the board */
+ twa_post_command_packet(tw_dev, request_id, 1);
+
+ /* Poll for completion */
+ if (twa_poll_response(tw_dev, request_id, 30))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param")
+ else
+ retval = (void *)&(param->data[0]);
+
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_INITIAL;
+
+ return retval;
+} /* End twa_get_param() */
+
+/* This function will assign an available request id */
+static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
+{
+ *request_id = tw_dev->free_queue[tw_dev->free_head];
+ tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
+ tw_dev->state[*request_id] = TW_S_STARTED;
+} /* End twa_get_request_id() */
+
+/* This function will send an initconnection command to controller */
+static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
+ u32 *init_connect_result)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Initconnect *tw_initconnect;
+ int request_id = 0, retval = 1;
+
+ /* Initialize InitConnection command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ full_command_packet->header.header_desc.size_header = 128;
+
+ tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
+ tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
+ tw_initconnect->request_id = request_id;
+ tw_initconnect->message_credits = message_credits;
+ tw_initconnect->features = set_features;
+
+ /* Turn on 64-bit sgl support if we need to */
+ tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
+
+ if (set_features & TW_EXTENDED_INIT_CONNECT) {
+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
+ tw_initconnect->fw_srl = current_fw_srl;
+ tw_initconnect->fw_arch_id = current_fw_arch_id;
+ tw_initconnect->fw_branch = current_fw_branch;
+ tw_initconnect->fw_build = current_fw_build;
+ } else
+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
+
+ /* Send command packet to the board */
+ twa_post_command_packet(tw_dev, request_id, 1);
+
+ /* Poll for completion */
+ if (twa_poll_response(tw_dev, request_id, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "No valid response during init connection");
+ } else {
+ if (set_features & TW_EXTENDED_INIT_CONNECT) {
+ *fw_on_ctlr_srl = tw_initconnect->fw_srl;
+ *fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id;
+ *fw_on_ctlr_branch = tw_initconnect->fw_branch;
+ *fw_on_ctlr_build = tw_initconnect->fw_build;
+ *init_connect_result = tw_initconnect->result;
+ }
+ retval = 0;
+ }
+
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_INITIAL;
+
+ return retval;
+} /* End twa_initconnection() */
+
+/* This function will initialize the fields of a device extension */
+static int twa_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+ int i, retval = 1;
+
+ /* Initialize command packet buffers */
+ if (twa_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Command packet memory allocation failed");
+ goto out;
+ }
+
+ /* Initialize generic buffer */
+ if (twa_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x17, "Generic memory allocation failed");
+ goto out;
+ }
+
+ /* Allocate event info space */
+ tw_dev->event_queue[0] = kmalloc(sizeof(TW_Event) * TW_Q_LENGTH, GFP_KERNEL);
+ if (!tw_dev->event_queue[0]) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x18, "Event info memory allocation failed");
+ goto out;
+ }
+
+ memset(tw_dev->event_queue[0], 0, sizeof(TW_Event) * TW_Q_LENGTH);
+
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event)));
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+
+ tw_dev->pending_head = TW_Q_START;
+ tw_dev->pending_tail = TW_Q_START;
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->error_sequence_id = 1;
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+ init_MUTEX(&tw_dev->ioctl_sem);
+ init_waitqueue_head(&tw_dev->ioctl_wqueue);
+
+ retval = 0;
+out:
+ return retval;
+} /* End twa_initialize_device_extension() */
+
+/* This function is the interrupt service routine */
+static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+ int request_id, error = 0;
+ u32 status_reg_value;
+ TW_Response_Queue response_que;
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+ int handled = 0;
+
+ /* Get the per adapter lock */
+ spin_lock(tw_dev->host->host_lock);
+
+ /* Read the registers */
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+ /* Check if this is our interrupt, otherwise bail */
+ if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
+ goto twa_interrupt_bail;
+
+ handled = 1;
+
+ /* Check controller for errors */
+ if (twa_check_bits(status_reg_value)) {
+ if (twa_decode_bits(tw_dev, status_reg_value)) {
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+ goto twa_interrupt_bail;
+ }
+ }
+
+ /* Handle host interrupt */
+ if (status_reg_value & TW_STATUS_HOST_INTERRUPT)
+ TW_CLEAR_HOST_INTERRUPT(tw_dev);
+
+ /* Handle attention interrupt */
+ if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
+ TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
+ if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
+ twa_get_request_id(tw_dev, &request_id);
+
+ error = twa_aen_read_queue(tw_dev, request_id);
+ if (error) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+ }
+ }
+ }
+
+ /* Handle command interrupt */
+ if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
+ TW_MASK_COMMAND_INTERRUPT(tw_dev);
+ /* Drain as many pending commands as we can */
+ while (tw_dev->pending_request_count > 0) {
+ request_id = tw_dev->pending_queue[tw_dev->pending_head];
+ if (tw_dev->state[request_id] != TW_S_PENDING) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x19, "Found request id that wasn't pending");
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+ goto twa_interrupt_bail;
+ }
+ if (twa_post_command_packet(tw_dev, request_id, 1)==0) {
+ tw_dev->pending_head = (tw_dev->pending_head + 1) % TW_Q_LENGTH;
+ tw_dev->pending_request_count--;
+ } else {
+ /* If we get here, we will continue re-posting on the next command interrupt */
+ break;
+ }
+ }
+ }
+
+ /* Handle response interrupt */
+ if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
+
+ /* Drain the response queue from the board */
+ while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+ /* Complete the response */
+ response_que.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ request_id = TW_RESID_OUT(response_que.response_id);
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ error = 0;
+ command_packet = &full_command_packet->command.oldcommand;
+ /* Check for command packet errors */
+ if (full_command_packet->command.newcommand.status != 0) {
+ if (tw_dev->srb[request_id] != 0) {
+ error = twa_fill_sense(tw_dev, request_id, 1, 1);
+ } else {
+ /* Skip ioctl error prints */
+ if (request_id != tw_dev->chrdev_request_id) {
+ error = twa_fill_sense(tw_dev, request_id, 0, 1);
+ }
+ }
+ }
+
+ /* Check for correct state */
+ if (tw_dev->state[request_id] != TW_S_POSTED) {
+ if (tw_dev->srb[request_id] != 0) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Received a request id that wasn't posted");
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+ goto twa_interrupt_bail;
+ }
+ }
+
+ /* Check for internal command completion */
+ if (tw_dev->srb[request_id] == 0) {
+ if (request_id != tw_dev->chrdev_request_id) {
+ if (twa_aen_complete(tw_dev, request_id))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Error completing AEN during attention interrupt");
+ } else {
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
+ }
+ } else {
+ twa_scsiop_execute_scsi_complete(tw_dev, request_id);
+ /* If no error command was a success */
+ if (error == 0) {
+ tw_dev->srb[request_id]->result = (DID_OK << 16);
+ }
+
+ /* If error, command failed */
+ if (error == 1) {
+ /* Ask for a host reset */
+ tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ }
+
+ /* Report residual bytes for single sgl */
+ if ((tw_dev->srb[request_id]->use_sg <= 1) && (full_command_packet->command.newcommand.status == 0)) {
+ if (full_command_packet->command.newcommand.sg_list[0].length < tw_dev->srb[request_id]->request_bufflen)
+ tw_dev->srb[request_id]->resid = tw_dev->srb[request_id]->request_bufflen - full_command_packet->command.newcommand.sg_list[0].length;
+ }
+
+ /* Now complete the io */
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ twa_unmap_scsi_data(tw_dev, request_id);
+ }
+
+ /* Check for valid status after each drain */
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+ if (twa_check_bits(status_reg_value)) {
+ if (twa_decode_bits(tw_dev, status_reg_value)) {
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+ goto twa_interrupt_bail;
+ }
+ }
+ }
+ }
+
+twa_interrupt_bail:
+ spin_unlock(tw_dev->host->host_lock);
+ return IRQ_RETVAL(handled);
+} /* End twa_interrupt() */
+
+/* This function will load the request id and various sgls for ioctls */
+static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
+{
+ TW_Command *oldcommand;
+ TW_Command_Apache *newcommand;
+ TW_SG_Entry *sgl;
+
+ if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
+ newcommand = &full_command_packet->command.newcommand;
+ newcommand->request_id__lunl =
+ TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id);
+ newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
+ newcommand->sg_list[0].length = length;
+ newcommand->sgl_entries__lunh =
+ TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), 1);
+ } else {
+ oldcommand = &full_command_packet->command.oldcommand;
+ oldcommand->request_id = request_id;
+
+ if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {
+ /* Load the sg list */
+ sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset));
+ sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
+ sgl->length = length;
+
+ if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4))
+ oldcommand->size += 1;
+ }
+ }
+} /* End twa_load_sgl() */
+
+/* This function will perform a pci-dma mapping for a scatter gather list */
+static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ int use_sg;
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+ struct pci_dev *pdev = tw_dev->tw_pci_dev;
+ int retval = 0;
+
+ if (cmd->use_sg == 0)
+ goto out;
+
+ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
+
+ if (use_sg == 0) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
+ goto out;
+ }
+
+ cmd->SCp.phase = TW_PHASE_SGLIST;
+ cmd->SCp.have_data_in = use_sg;
+ retval = use_sg;
+out:
+ return retval;
+} /* End twa_map_scsi_sg_data() */
+
+/* This function will perform a pci-dma map for a single buffer */
+static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ dma_addr_t mapping;
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+ struct pci_dev *pdev = tw_dev->tw_pci_dev;
+ int retval = 0;
+
+ if (cmd->request_bufflen == 0) {
+ retval = 0;
+ goto out;
+ }
+
+ mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, DMA_BIDIRECTIONAL);
+
+ if (mapping == 0) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Failed to map page");
+ goto out;
+ }
+
+ cmd->SCp.phase = TW_PHASE_SINGLE;
+ cmd->SCp.have_data_in = mapping;
+ retval = mapping;
+out:
+ return retval;
+} /* End twa_map_scsi_single_data() */
+
+/* This function will poll for a response interrupt of a request */
+static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
+{
+ int retval = 1, found = 0, response_request_id;
+ TW_Response_Queue response_queue;
+ TW_Command_Full *full_command_packet = tw_dev->command_packet_virt[request_id];
+
+ if (twa_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, seconds) == 0) {
+ response_queue.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ response_request_id = TW_RESID_OUT(response_queue.response_id);
+ if (request_id != response_request_id) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "Found unexpected request id while polling for response");
+ goto out;
+ }
+ if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
+ if (full_command_packet->command.newcommand.status != 0) {
+ /* bad response */
+ twa_fill_sense(tw_dev, request_id, 0, 0);
+ goto out;
+ }
+ found = 1;
+ } else {
+ if (full_command_packet->command.oldcommand.status != 0) {
+ /* bad response */
+ twa_fill_sense(tw_dev, request_id, 0, 0);
+ goto out;
+ }
+ found = 1;
+ }
+ }
+
+ if (found)
+ retval = 0;
+out:
+ return retval;
+} /* End twa_poll_response() */
+
+/* This function will poll the status register for a flag */
+static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+ u32 status_reg_value;
+ unsigned long before;
+ int retval = 1;
+
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+ before = jiffies;
+
+ if (twa_check_bits(status_reg_value))
+ twa_decode_bits(tw_dev, status_reg_value);
+
+ while ((status_reg_value & flag) != flag) {
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+ if (twa_check_bits(status_reg_value))
+ twa_decode_bits(tw_dev, status_reg_value);
+
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_poll_status() */
+
+/* This function will poll the status register for disappearance of a flag */
+static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+ u32 status_reg_value;
+ unsigned long before;
+ int retval = 1;
+
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+ before = jiffies;
+
+ if (twa_check_bits(status_reg_value))
+ twa_decode_bits(tw_dev, status_reg_value);
+
+ while ((status_reg_value & flag) != 0) {
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+ if (twa_check_bits(status_reg_value))
+ twa_decode_bits(tw_dev, status_reg_value);
+
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_poll_status_gone() */
+
+/* This function will attempt to post a command packet to the board */
+static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal)
+{
+ u32 status_reg_value;
+ dma_addr_t command_que_value;
+ int retval = 1;
+
+ command_que_value = tw_dev->command_packet_phys[request_id];
+ status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+ if (twa_check_bits(status_reg_value))
+ twa_decode_bits(tw_dev, status_reg_value);
+
+ if (((tw_dev->pending_request_count > 0) && (tw_dev->state[request_id] != TW_S_PENDING)) || (status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL)) {
+
+ /* Only pend internal driver commands */
+ if (!internal) {
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ /* Couldn't post the command packet, so we do it later */
+ if (tw_dev->state[request_id] != TW_S_PENDING) {
+ tw_dev->state[request_id] = TW_S_PENDING;
+ tw_dev->pending_request_count++;
+ if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
+ tw_dev->max_pending_request_count = tw_dev->pending_request_count;
+ }
+ tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
+ tw_dev->pending_tail = (tw_dev->pending_tail + 1) % TW_Q_LENGTH;
+ }
+ TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
+ goto out;
+ } else {
+ /* We successfully posted the command packet */
+ if (sizeof(dma_addr_t) > 4) {
+ command_que_value += TW_COMMAND_OFFSET;
+ writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+ writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4);
+ } else {
+ writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+ }
+ tw_dev->state[request_id] = TW_S_POSTED;
+ tw_dev->posted_request_count++;
+ if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
+ tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+ }
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_post_command_packet() */
+
+/* This function will reset a device extension */
+static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset)
+{
+ int i = 0;
+ int retval = 1;
+ unsigned long flags = 0;
+
+ set_bit(TW_IN_RESET, &tw_dev->flags);
+ TW_DISABLE_INTERRUPTS(tw_dev);
+ TW_MASK_COMMAND_INTERRUPT(tw_dev);
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+ /* Abort all requests that are in progress */
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ if ((tw_dev->state[i] != TW_S_FINISHED) &&
+ (tw_dev->state[i] != TW_S_INITIAL) &&
+ (tw_dev->state[i] != TW_S_COMPLETED)) {
+ if (tw_dev->srb[i]) {
+ tw_dev->srb[i]->result = (DID_RESET << 16);
+ tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+ twa_unmap_scsi_data(tw_dev, i);
+ }
+ }
+ }
+
+ /* Reset queues and counts */
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->posted_request_count = 0;
+ tw_dev->pending_request_count = 0;
+ tw_dev->pending_head = TW_Q_START;
+ tw_dev->pending_tail = TW_Q_START;
+ tw_dev->reset_print = 0;
+
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ if (twa_reset_sequence(tw_dev, 1))
+ goto out;
+
+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+ /* Wake up any ioctl that was pending before the reset */
+ if ((tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE) || (ioctl_reset)) {
+ clear_bit(TW_IN_RESET, &tw_dev->flags);
+ } else {
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twa_reset_device_extension() */
+
+/* This function will reset a controller */
+static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
+{
+ int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset;
+
+ while (tries < TW_MAX_RESET_TRIES) {
+ if (do_soft_reset)
+ TW_SOFT_RESET(tw_dev);
+
+ /* Make sure controller is in a good state */
+ if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 60)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Microcontroller not ready during reset sequence");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Empty response queue */
+ if (twa_empty_response_queue(tw_dev)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Response queue empty failed during reset sequence");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ flashed = 0;
+
+ /* Check for compatibility/flash */
+ if (twa_check_srl(tw_dev, &flashed)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Compatibility check failed during reset sequence");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ } else {
+ if (flashed) {
+ tries++;
+ continue;
+ }
+ }
+
+ /* Drain the AEN queue */
+ if (twa_aen_drain_queue(tw_dev, soft_reset)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x22, "AEN drain failed during reset sequence");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* If we got here, controller is in a good state */
+ retval = 0;
+ goto out;
+ }
+out:
+ return retval;
+} /* End twa_reset_sequence() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+{
+ int heads, sectors, cylinders;
+ TW_Device_Extension *tw_dev;
+
+ tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
+
+ if (capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = sector_div(capacity, heads * sectors);
+ } else {
+ heads = 64;
+ sectors = 32;
+ cylinders = sector_div(capacity, heads * sectors);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return 0;
+} /* End twa_scsi_biosparam() */
+
+/* This is the new scsi eh reset function */
+static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
+{
+ TW_Device_Extension *tw_dev = NULL;
+ int retval = FAILED;
+
+ tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ spin_unlock_irq(tw_dev->host->host_lock);
+
+ tw_dev->num_resets++;
+
+ printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, TW_DRIVER, 0x2c, SCpnt->device->id, SCpnt->cmnd[0]);
+
+ /* Now reset the card and some of the device extension data */
+ if (twa_reset_device_extension(tw_dev, 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset");
+ goto out;
+ }
+
+ retval = SUCCESS;
+out:
+ spin_lock_irq(tw_dev->host->host_lock);
+ return retval;
+} /* End twa_scsi_eh_reset() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int request_id, retval;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ /* Check if this FW supports luns */
+ if ((SCpnt->device->lun != 0) && (tw_dev->working_srl < TW_FW_SRL_LUNS_SUPPORTED)) {
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ done(SCpnt);
+ retval = 0;
+ goto out;
+ }
+
+ /* Save done function into scsi_cmnd struct */
+ SCpnt->scsi_done = done;
+
+ /* Get a free request id */
+ twa_get_request_id(tw_dev, &request_id);
+
+ /* Save the scsi command for use by the ISR */
+ tw_dev->srb[request_id] = SCpnt;
+
+ /* Initialize phase to zero */
+ SCpnt->SCp.phase = TW_PHASE_INITIAL;
+
+ retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
+ switch (retval) {
+ case SCSI_MLQUEUE_HOST_BUSY:
+ twa_free_request_id(tw_dev, request_id);
+ break;
+ case 1:
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
+ SCpnt->result = (DID_ERROR << 16);
+ done(SCpnt);
+ retval = 0;
+ }
+out:
+ return retval;
+} /* End twa_scsi_queue() */
+
+/* This function hands scsi cdb's to the firmware */
+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command_Apache *command_packet;
+ u32 num_sectors = 0x0;
+ int i, sg_count;
+ struct scsi_cmnd *srb = NULL;
+ struct scatterlist *sglist = NULL;
+ u32 buffaddr = 0x0;
+ int retval = 1;
+
+ if (tw_dev->srb[request_id]) {
+ if (tw_dev->srb[request_id]->request_buffer) {
+ sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer;
+ }
+ srb = tw_dev->srb[request_id];
+ }
+
+ /* Initialize command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ full_command_packet->header.header_desc.size_header = 128;
+ full_command_packet->header.status_block.error = 0;
+ full_command_packet->header.status_block.severity__reserved = 0;
+
+ command_packet = &full_command_packet->command.newcommand;
+ command_packet->status = 0;
+ command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI);
+
+ /* We forced 16 byte cdb use earlier */
+ if (!cdb)
+ memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
+ else
+ memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
+
+ if (srb) {
+ command_packet->unit = srb->device->id;
+ command_packet->request_id__lunl =
+ TW_REQ_LUN_IN(srb->device->lun, request_id);
+ } else {
+ command_packet->request_id__lunl =
+ TW_REQ_LUN_IN(0, request_id);
+ command_packet->unit = 0;
+ }
+
+ command_packet->sgl_offset = 16;
+
+ if (!sglistarg) {
+ /* Map sglist from scsi layer to cmd packet */
+ if (tw_dev->srb[request_id]->use_sg == 0) {
+ if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) {
+ command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id];
+ command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH;
+ } else {
+ buffaddr = twa_map_scsi_single_data(tw_dev, request_id);
+ if (buffaddr == 0)
+ goto out;
+
+ command_packet->sg_list[0].address = buffaddr;
+ command_packet->sg_list[0].length = tw_dev->srb[request_id]->request_bufflen;
+ }
+ command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), 1);
+
+ if (command_packet->sg_list[0].address & TW_ALIGNMENT_9000_SGL) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2d, "Found unaligned address during execute scsi");
+ goto out;
+ }
+ }
+
+ if (tw_dev->srb[request_id]->use_sg > 0) {
+ if ((tw_dev->srb[request_id]->use_sg == 1) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) {
+ command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id];
+ command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH;
+ } else {
+ sg_count = twa_map_scsi_sg_data(tw_dev, request_id);
+ if (sg_count == 0)
+ goto out;
+
+ for (i = 0; i < sg_count; i++) {
+ command_packet->sg_list[i].address = sg_dma_address(&sglist[i]);
+ command_packet->sg_list[i].length = sg_dma_len(&sglist[i]);
+ if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi");
+ goto out;
+ }
+ }
+ }
+ command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), tw_dev->srb[request_id]->use_sg);
+ }
+ } else {
+ /* Internal cdb post */
+ for (i = 0; i < use_sg; i++) {
+ command_packet->sg_list[i].address = sglistarg[i].address;
+ command_packet->sg_list[i].length = sglistarg[i].length;
+ if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post");
+ goto out;
+ }
+ }
+ command_packet->sgl_entries__lunh = TW_REQ_LUN_IN(0, use_sg);
+ }
+
+ if (srb) {
+ if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6)
+ num_sectors = (u32)srb->cmnd[4];
+
+ if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10)
+ num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
+ }
+
+ /* Update sector statistic */
+ tw_dev->sector_count = num_sectors;
+ if (tw_dev->sector_count > tw_dev->max_sector_count)
+ tw_dev->max_sector_count = tw_dev->sector_count;
+
+ /* Update SG statistics */
+ if (srb) {
+ tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
+ if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+ tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+ }
+
+ /* Now post the command to the board */
+ if (srb) {
+ retval = twa_post_command_packet(tw_dev, request_id, 0);
+ } else {
+ twa_post_command_packet(tw_dev, request_id, 1);
+ retval = 0;
+ }
+out:
+ return retval;
+} /* End twa_scsiop_execute_scsi() */
+
+/* This function completes an execute scsi operation */
+static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ /* Copy the response if too small */
+ if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) {
+ memcpy(tw_dev->srb[request_id]->request_buffer,
+ tw_dev->generic_buffer_virt[request_id],
+ tw_dev->srb[request_id]->request_bufflen);
+ }
+} /* End twa_scsiop_execute_scsi_complete() */
+
+/* This function tells the controller to shut down */
+static void __twa_shutdown(TW_Device_Extension *tw_dev)
+{
+ /* Disable interrupts */
+ TW_DISABLE_INTERRUPTS(tw_dev);
+
+ printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
+
+ /* Tell the card we are shutting down */
+ if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x31, "Connection shutdown failed");
+ } else {
+ printk(KERN_WARNING "3w-9xxx: Shutdown complete.\n");
+ }
+
+ /* Clear all interrupts just before exit */
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+} /* End __twa_shutdown() */
+
+/* Wrapper for __twa_shutdown */
+static void twa_shutdown(struct device *dev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ __twa_shutdown(tw_dev);
+} /* End twa_shutdown() */
+
+/* This function will look up a string */
+static char *twa_string_lookup(twa_message_type *table, unsigned int code)
+{
+ int index;
+
+ for (index = 0; ((code != table[index].code) &&
+ (table[index].text != (char *)0)); index++);
+ return(table[index].text);
+} /* End twa_string_lookup() */
+
+/* This function will perform a pci-dma unmap */
+static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+ struct pci_dev *pdev = tw_dev->tw_pci_dev;
+
+ switch(cmd->SCp.phase) {
+ case TW_PHASE_SINGLE:
+ pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, DMA_BIDIRECTIONAL);
+ break;
+ case TW_PHASE_SGLIST:
+ pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
+ break;
+ }
+} /* End twa_unmap_scsi_data() */
+
+/* scsi_host_template initializer */
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .name = "3ware 9000 Storage Controller",
+ .queuecommand = twa_scsi_queue,
+ .eh_host_reset_handler = twa_scsi_eh_reset,
+ .bios_param = twa_scsi_biosparam,
+ .change_queue_depth = twa_change_queue_depth,
+ .can_queue = TW_Q_LENGTH-2,
+ .this_id = -1,
+ .sg_tablesize = TW_APACHE_MAX_SGL_LENGTH,
+ .max_sectors = TW_MAX_SECTORS,
+ .cmd_per_lun = TW_MAX_CMDS_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = twa_host_attrs,
+ .emulated = 1
+};
+
+/* This function will probe and initialize a card */
+static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+{
+ struct Scsi_Host *host = NULL;
+ TW_Device_Extension *tw_dev;
+ u32 mem_addr;
+ int retval = -ENODEV;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x34, "Failed to enable pci device");
+ goto out_disable_device;
+ }
+
+ pci_set_master(pdev);
+
+ retval = pci_set_dma_mask(pdev, sizeof(dma_addr_t) > 4 ? DMA_64BIT_MASK : DMA_32BIT_MASK);
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
+ goto out_disable_device;
+ }
+
+ host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
+ if (!host) {
+ TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
+ retval = -ENOMEM;
+ goto out_disable_device;
+ }
+ tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ memset(tw_dev, 0, sizeof(TW_Device_Extension));
+
+ /* Save values to device extension */
+ tw_dev->host = host;
+ tw_dev->tw_pci_dev = pdev;
+
+ if (twa_initialize_device_extension(tw_dev)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
+ goto out_free_device_extension;
+ }
+
+ /* Request IO regions */
+ retval = pci_request_regions(pdev, "3w-9xxx");
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Failed to get mem region");
+ goto out_free_device_extension;
+ }
+
+ mem_addr = pci_resource_start(pdev, 1);
+
+ /* Save base address */
+ tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE);
+ if (!tw_dev->base_addr) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x35, "Failed to ioremap");
+ goto out_release_mem_region;
+ }
+
+ /* Disable interrupts on the card */
+ TW_DISABLE_INTERRUPTS(tw_dev);
+
+ /* Initialize the card */
+ if (twa_reset_sequence(tw_dev, 0))
+ goto out_release_mem_region;
+
+ /* Set host specific parameters */
+ host->max_id = TW_MAX_UNITS;
+ host->max_cmd_len = TW_MAX_CDB_LEN;
+
+ /* Channels aren't supported by adapter */
+ host->max_lun = TW_MAX_LUNS(tw_dev->working_srl);
+ host->max_channel = 0;
+
+ /* Register the card with the kernel SCSI layer */
+ retval = scsi_add_host(host, &pdev->dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed");
+ goto out_release_mem_region;
+ }
+
+ pci_set_drvdata(pdev, host);
+
+ printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n",
+ host->host_no, mem_addr, pdev->irq);
+ printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n",
+ host->host_no,
+ (char *)twa_get_param(tw_dev, 0, TW_VERSION_TABLE,
+ TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH),
+ (char *)twa_get_param(tw_dev, 1, TW_VERSION_TABLE,
+ TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH),
+ *(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE,
+ TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH));
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, twa_interrupt, SA_SHIRQ, "3w-9xxx", tw_dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x30, "Error requesting IRQ");
+ goto out_remove_host;
+ }
+
+ twa_device_extension_list[twa_device_extension_count] = tw_dev;
+ twa_device_extension_count++;
+
+ /* Re-enable interrupts on the card */
+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+ /* Finally, scan the host */
+ scsi_scan_host(host);
+
+ if (twa_major == -1) {
+ if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0)
+ TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device");
+ }
+ return 0;
+
+out_remove_host:
+ scsi_remove_host(host);
+out_release_mem_region:
+ pci_release_regions(pdev);
+out_free_device_extension:
+ twa_free_device_extension(tw_dev);
+ scsi_host_put(host);
+out_disable_device:
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End twa_probe() */
+
+/* This function is called to remove a device */
+static void twa_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ scsi_remove_host(tw_dev->host);
+
+ /* Unregister character device */
+ if (twa_major >= 0) {
+ unregister_chrdev(twa_major, "twa");
+ twa_major = -1;
+ }
+
+ /* Free up the IRQ */
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ /* Shutdown the card */
+ __twa_shutdown(tw_dev);
+
+ /* Free up the mem region */
+ pci_release_regions(pdev);
+
+ /* Free up device extension resources */
+ twa_free_device_extension(tw_dev);
+
+ scsi_host_put(tw_dev->host);
+ pci_disable_device(pdev);
+ twa_device_extension_count--;
+} /* End twa_remove() */
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id twa_pci_tbl[] __devinitdata = {
+ { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { }
+};
+MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
+
+/* pci_driver initializer */
+static struct pci_driver twa_driver = {
+ .name = "3w-9xxx",
+ .id_table = twa_pci_tbl,
+ .probe = twa_probe,
+ .remove = twa_remove,
+ .driver = {
+ .shutdown = twa_shutdown
+ }
+};
+
+/* This function is called on driver initialization */
+static int __init twa_init(void)
+{
+ printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
+
+ return pci_module_init(&twa_driver);
+} /* End twa_init() */
+
+/* This function is called on driver exit */
+static void __exit twa_exit(void)
+{
+ pci_unregister_driver(&twa_driver);
+} /* End twa_exit() */
+
+module_init(twa_init);
+module_exit(twa_exit);
+
diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h
new file mode 100644
index 000000000000..8c8ecbed3b58
--- /dev/null
+++ b/drivers/scsi/3w-9xxx.h
@@ -0,0 +1,682 @@
+/*
+ 3w-9xxx.h -- 3ware 9000 Storage Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@amcc.com>
+
+ Copyright (C) 2004-2005 Applied Micro Circuits Corporation.
+
+ This program is free software; 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 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@amcc.com
+
+ For more information, goto:
+ http://www.amcc.com
+*/
+
+#ifndef _3W_9XXX_H
+#define _3W_9XXX_H
+
+/* AEN string type */
+typedef struct TAG_twa_message_type {
+ unsigned int code;
+ char* text;
+} twa_message_type;
+
+/* AEN strings */
+static twa_message_type twa_aen_table[] = {
+ {0x0000, "AEN queue empty"},
+ {0x0001, "Controller reset occurred"},
+ {0x0002, "Degraded unit detected"},
+ {0x0003, "Controller error occured"},
+ {0x0004, "Background rebuild failed"},
+ {0x0005, "Background rebuild done"},
+ {0x0006, "Incomplete unit detected"},
+ {0x0007, "Background initialize done"},
+ {0x0008, "Unclean shutdown detected"},
+ {0x0009, "Drive timeout detected"},
+ {0x000A, "Drive error detected"},
+ {0x000B, "Rebuild started"},
+ {0x000C, "Background initialize started"},
+ {0x000D, "Entire logical unit was deleted"},
+ {0x000E, "Background initialize failed"},
+ {0x000F, "SMART attribute exceeded threshold"},
+ {0x0010, "Power supply reported AC under range"},
+ {0x0011, "Power supply reported DC out of range"},
+ {0x0012, "Power supply reported a malfunction"},
+ {0x0013, "Power supply predicted malfunction"},
+ {0x0014, "Battery charge is below threshold"},
+ {0x0015, "Fan speed is below threshold"},
+ {0x0016, "Temperature sensor is above threshold"},
+ {0x0017, "Power supply was removed"},
+ {0x0018, "Power supply was inserted"},
+ {0x0019, "Drive was removed from a bay"},
+ {0x001A, "Drive was inserted into a bay"},
+ {0x001B, "Drive bay cover door was opened"},
+ {0x001C, "Drive bay cover door was closed"},
+ {0x001D, "Product case was opened"},
+ {0x0020, "Prepare for shutdown (power-off)"},
+ {0x0021, "Downgrade UDMA mode to lower speed"},
+ {0x0022, "Upgrade UDMA mode to higher speed"},
+ {0x0023, "Sector repair completed"},
+ {0x0024, "Sbuf memory test failed"},
+ {0x0025, "Error flushing cached write data to array"},
+ {0x0026, "Drive reported data ECC error"},
+ {0x0027, "DCB has checksum error"},
+ {0x0028, "DCB version is unsupported"},
+ {0x0029, "Background verify started"},
+ {0x002A, "Background verify failed"},
+ {0x002B, "Background verify done"},
+ {0x002C, "Bad sector overwritten during rebuild"},
+ {0x002D, "Background rebuild error on source drive"},
+ {0x002E, "Replace failed because replacement drive too small"},
+ {0x002F, "Verify failed because array was never initialized"},
+ {0x0030, "Unsupported ATA drive"},
+ {0x0031, "Synchronize host/controller time"},
+ {0x0032, "Spare capacity is inadequate for some units"},
+ {0x0033, "Background migration started"},
+ {0x0034, "Background migration failed"},
+ {0x0035, "Background migration done"},
+ {0x0036, "Verify detected and fixed data/parity mismatch"},
+ {0x0037, "SO-DIMM incompatible"},
+ {0x0038, "SO-DIMM not detected"},
+ {0x0039, "Corrected Sbuf ECC error"},
+ {0x003A, "Drive power on reset detected"},
+ {0x003B, "Background rebuild paused"},
+ {0x003C, "Background initialize paused"},
+ {0x003D, "Background verify paused"},
+ {0x003E, "Background migration paused"},
+ {0x003F, "Corrupt flash file system detected"},
+ {0x0040, "Flash file system repaired"},
+ {0x0041, "Unit number assignments were lost"},
+ {0x0042, "Error during read of primary DCB"},
+ {0x0043, "Latent error found in backup DCB"},
+ {0x00FC, "Recovered/finished array membership update"},
+ {0x00FD, "Handler lockup"},
+ {0x00FE, "Retrying PCI transfer"},
+ {0x00FF, "AEN queue is full"},
+ {0xFFFFFFFF, (char*) 0}
+};
+
+/* AEN severity table */
+static char *twa_aen_severity_table[] =
+{
+ "None", "ERROR", "WARNING", "INFO", "DEBUG", (char*) 0
+};
+
+/* Error strings */
+static twa_message_type twa_error_table[] = {
+ {0x0100, "SGL entry contains zero data"},
+ {0x0101, "Invalid command opcode"},
+ {0x0102, "SGL entry has unaligned address"},
+ {0x0103, "SGL size does not match command"},
+ {0x0104, "SGL entry has illegal length"},
+ {0x0105, "Command packet is not aligned"},
+ {0x0106, "Invalid request ID"},
+ {0x0107, "Duplicate request ID"},
+ {0x0108, "ID not locked"},
+ {0x0109, "LBA out of range"},
+ {0x010A, "Logical unit not supported"},
+ {0x010B, "Parameter table does not exist"},
+ {0x010C, "Parameter index does not exist"},
+ {0x010D, "Invalid field in CDB"},
+ {0x010E, "Specified port has invalid drive"},
+ {0x010F, "Parameter item size mismatch"},
+ {0x0110, "Failed memory allocation"},
+ {0x0111, "Memory request too large"},
+ {0x0112, "Out of memory segments"},
+ {0x0113, "Invalid address to deallocate"},
+ {0x0114, "Out of memory"},
+ {0x0115, "Out of heap"},
+ {0x0120, "Double degrade"},
+ {0x0121, "Drive not degraded"},
+ {0x0122, "Reconstruct error"},
+ {0x0123, "Replace not accepted"},
+ {0x0124, "Replace drive capacity too small"},
+ {0x0125, "Sector count not allowed"},
+ {0x0126, "No spares left"},
+ {0x0127, "Reconstruct error"},
+ {0x0128, "Unit is offline"},
+ {0x0129, "Cannot update status to DCB"},
+ {0x0130, "Invalid stripe handle"},
+ {0x0131, "Handle that was not locked"},
+ {0x0132, "Handle that was not empty"},
+ {0x0133, "Handle has different owner"},
+ {0x0140, "IPR has parent"},
+ {0x0150, "Illegal Pbuf address alignment"},
+ {0x0151, "Illegal Pbuf transfer length"},
+ {0x0152, "Illegal Sbuf address alignment"},
+ {0x0153, "Illegal Sbuf transfer length"},
+ {0x0160, "Command packet too large"},
+ {0x0161, "SGL exceeds maximum length"},
+ {0x0162, "SGL has too many entries"},
+ {0x0170, "Insufficient resources for rebuilder"},
+ {0x0171, "Verify error (data != parity)"},
+ {0x0180, "Requested segment not in directory of this DCB"},
+ {0x0181, "DCB segment has unsupported version"},
+ {0x0182, "DCB segment has checksum error"},
+ {0x0183, "DCB support (settings) segment invalid"},
+ {0x0184, "DCB UDB (unit descriptor block) segment invalid"},
+ {0x0185, "DCB GUID (globally unique identifier) segment invalid"},
+ {0x01A0, "Could not clear Sbuf"},
+ {0x01C0, "Flash identify failed"},
+ {0x01C1, "Flash out of bounds"},
+ {0x01C2, "Flash verify error"},
+ {0x01C3, "Flash file object not found"},
+ {0x01C4, "Flash file already present"},
+ {0x01C5, "Flash file system full"},
+ {0x01C6, "Flash file not present"},
+ {0x01C7, "Flash file size error"},
+ {0x01C8, "Bad flash file checksum"},
+ {0x01CA, "Corrupt flash file system detected"},
+ {0x01D0, "Invalid field in parameter list"},
+ {0x01D1, "Parameter list length error"},
+ {0x01D2, "Parameter item is not changeable"},
+ {0x01D3, "Parameter item is not saveable"},
+ {0x0200, "UDMA CRC error"},
+ {0x0201, "Internal CRC error"},
+ {0x0202, "Data ECC error"},
+ {0x0203, "ADP level 1 error"},
+ {0x0204, "Port timeout"},
+ {0x0205, "Drive power on reset"},
+ {0x0206, "ADP level 2 error"},
+ {0x0207, "Soft reset failed"},
+ {0x0208, "Drive not ready"},
+ {0x0209, "Unclassified port error"},
+ {0x020A, "Drive aborted command"},
+ {0x0210, "Internal CRC error"},
+ {0x0211, "PCI abort error"},
+ {0x0212, "PCI parity error"},
+ {0x0213, "Port handler error"},
+ {0x0214, "Token interrupt count error"},
+ {0x0215, "Timeout waiting for PCI transfer"},
+ {0x0216, "Corrected buffer ECC"},
+ {0x0217, "Uncorrected buffer ECC"},
+ {0x0230, "Unsupported command during flash recovery"},
+ {0x0231, "Next image buffer expected"},
+ {0x0232, "Binary image architecture incompatible"},
+ {0x0233, "Binary image has no signature"},
+ {0x0234, "Binary image has bad checksum"},
+ {0x0235, "Image downloaded overflowed buffer"},
+ {0x0240, "I2C device not found"},
+ {0x0241, "I2C transaction aborted"},
+ {0x0242, "SO-DIMM parameter(s) incompatible using defaults"},
+ {0x0243, "SO-DIMM unsupported"},
+ {0x0248, "SPI transfer status error"},
+ {0x0249, "SPI transfer timeout error"},
+ {0x0250, "Invalid unit descriptor size in CreateUnit"},
+ {0x0251, "Unit descriptor size exceeds data buffer in CreateUnit"},
+ {0x0252, "Invalid value in CreateUnit descriptor"},
+ {0x0253, "Inadequate disk space to support descriptor in CreateUnit"},
+ {0x0254, "Unable to create data channel for this unit descriptor"},
+ {0x0255, "CreateUnit descriptor specifies a drive already in use"},
+ {0x0256, "Unable to write configuration to all disks during CreateUnit"},
+ {0x0257, "CreateUnit does not support this descriptor version"},
+ {0x0258, "Invalid subunit for RAID 0 or 5 in CreateUnit"},
+ {0x0259, "Too many descriptors in CreateUnit"},
+ {0x025A, "Invalid configuration specified in CreateUnit descriptor"},
+ {0x025B, "Invalid LBA offset specified in CreateUnit descriptor"},
+ {0x025C, "Invalid stripelet size specified in CreateUnit descriptor"},
+ {0x0260, "SMART attribute exceeded threshold"},
+ {0xFFFFFFFF, (char*) 0}
+};
+
+/* Control register bit definitions */
+#define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000
+#define TW_CONTROL_CLEAR_ATTENTION_INTERRUPT 0x00040000
+#define TW_CONTROL_MASK_COMMAND_INTERRUPT 0x00020000
+#define TW_CONTROL_MASK_RESPONSE_INTERRUPT 0x00010000
+#define TW_CONTROL_UNMASK_COMMAND_INTERRUPT 0x00008000
+#define TW_CONTROL_UNMASK_RESPONSE_INTERRUPT 0x00004000
+#define TW_CONTROL_CLEAR_ERROR_STATUS 0x00000200
+#define TW_CONTROL_ISSUE_SOFT_RESET 0x00000100
+#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080
+#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040
+#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020
+#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
+#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
+#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008
+
+/* Status register bit definitions */
+#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000
+#define TW_STATUS_MINOR_VERSION_MASK 0x0F000000
+#define TW_STATUS_PCI_PARITY_ERROR 0x00800000
+#define TW_STATUS_QUEUE_ERROR 0x00400000
+#define TW_STATUS_MICROCONTROLLER_ERROR 0x00200000
+#define TW_STATUS_PCI_ABORT 0x00100000
+#define TW_STATUS_HOST_INTERRUPT 0x00080000
+#define TW_STATUS_ATTENTION_INTERRUPT 0x00040000
+#define TW_STATUS_COMMAND_INTERRUPT 0x00020000
+#define TW_STATUS_RESPONSE_INTERRUPT 0x00010000
+#define TW_STATUS_COMMAND_QUEUE_FULL 0x00008000
+#define TW_STATUS_RESPONSE_QUEUE_EMPTY 0x00004000
+#define TW_STATUS_MICROCONTROLLER_READY 0x00002000
+#define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000
+#define TW_STATUS_EXPECTED_BITS 0x00002000
+#define TW_STATUS_UNEXPECTED_BITS 0x00F00008
+#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008
+#define TW_STATUS_VALID_INTERRUPT 0x00DF0008
+
+/* RESPONSE QUEUE BIT DEFINITIONS */
+#define TW_RESPONSE_ID_MASK 0x00000FF0
+
+/* PCI related defines */
+#define TW_NUMDEVICES 1
+#define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+#define TW_PCI_CLEAR_PCI_ABORT 0x2000
+
+/* Command packet opcodes used by the driver */
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_GET_PARAM 0x12
+#define TW_OP_SET_PARAM 0x13
+#define TW_OP_EXECUTE_SCSI 0x10
+#define TW_OP_DOWNLOAD_FIRMWARE 0x16
+#define TW_OP_RESET 0x1C
+
+/* Asynchronous Event Notification (AEN) codes used by the driver */
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
+#define TW_AEN_SEVERITY_ERROR 0x1
+#define TW_AEN_SEVERITY_DEBUG 0x4
+#define TW_AEN_NOT_RETRIEVED 0x1
+#define TW_AEN_RETRIEVED 0x2
+
+/* Command state defines */
+#define TW_S_INITIAL 0x1 /* Initial state */
+#define TW_S_STARTED 0x2 /* Id in use */
+#define TW_S_POSTED 0x4 /* Posted to the controller */
+#define TW_S_PENDING 0x8 /* Waiting to be posted in isr */
+#define TW_S_COMPLETED 0x10 /* Completed by isr */
+#define TW_S_FINISHED 0x20 /* I/O completely done */
+
+/* Compatibility defines */
+#define TW_9000_ARCH_ID 0x5
+#define TW_CURRENT_DRIVER_SRL 28
+#define TW_CURRENT_DRIVER_BUILD 9
+#define TW_CURRENT_DRIVER_BRANCH 4
+
+/* Phase defines */
+#define TW_PHASE_INITIAL 0
+#define TW_PHASE_SINGLE 1
+#define TW_PHASE_SGLIST 2
+
+/* Misc defines */
+#define TW_SECTOR_SIZE 512
+#define TW_ALIGNMENT_9000 4 /* 4 bytes */
+#define TW_ALIGNMENT_9000_SGL 0x3
+#define TW_MAX_UNITS 16
+#define TW_INIT_MESSAGE_CREDITS 0x100
+#define TW_INIT_COMMAND_PACKET_SIZE 0x3
+#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED 0x6
+#define TW_EXTENDED_INIT_CONNECT 0x2
+#define TW_BUNDLED_FW_SAFE_TO_FLASH 0x4
+#define TW_CTLR_FW_RECOMMENDS_FLASH 0x8
+#define TW_CTLR_FW_COMPATIBLE 0x2
+#define TW_BASE_FW_SRL 24
+#define TW_BASE_FW_BRANCH 0
+#define TW_BASE_FW_BUILD 1
+#define TW_FW_SRL_LUNS_SUPPORTED 28
+#define TW_Q_LENGTH 256
+#define TW_Q_START 0
+#define TW_MAX_SLOT 32
+#define TW_MAX_RESET_TRIES 2
+#define TW_MAX_CMDS_PER_LUN 254
+#define TW_MAX_RESPONSE_DRAIN 256
+#define TW_MAX_AEN_DRAIN 40
+#define TW_IN_RESET 2
+#define TW_IN_CHRDEV_IOCTL 3
+#define TW_IN_ATTENTION_LOOP 4
+#define TW_MAX_SECTORS 256
+#define TW_AEN_WAIT_TIME 1000
+#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
+#define TW_MAX_CDB_LEN 16
+#define TW_ISR_DONT_COMPLETE 2
+#define TW_ISR_DONT_RESULT 3
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_COMMAND_OFFSET 128 /* 128 bytes */
+#define TW_VERSION_TABLE 0x0402
+#define TW_TIMEKEEP_TABLE 0x040A
+#define TW_INFORMATION_TABLE 0x0403
+#define TW_PARAM_FWVER 3
+#define TW_PARAM_FWVER_LENGTH 16
+#define TW_PARAM_BIOSVER 4
+#define TW_PARAM_BIOSVER_LENGTH 16
+#define TW_PARAM_PORTCOUNT 3
+#define TW_PARAM_PORTCOUNT_LENGTH 1
+#define TW_MIN_SGL_LENGTH 0x200 /* 512 bytes */
+#define TW_MAX_SENSE_LENGTH 256
+#define TW_EVENT_SOURCE_AEN 0x1000
+#define TW_EVENT_SOURCE_COMMAND 0x1001
+#define TW_EVENT_SOURCE_PCHIP 0x1002
+#define TW_EVENT_SOURCE_DRIVER 0x1003
+#define TW_IOCTL_GET_COMPATIBILITY_INFO 0x101
+#define TW_IOCTL_GET_LAST_EVENT 0x102
+#define TW_IOCTL_GET_FIRST_EVENT 0x103
+#define TW_IOCTL_GET_NEXT_EVENT 0x104
+#define TW_IOCTL_GET_PREVIOUS_EVENT 0x105
+#define TW_IOCTL_GET_LOCK 0x106
+#define TW_IOCTL_RELEASE_LOCK 0x107
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108
+#define TW_IOCTL_ERROR_STATUS_NOT_LOCKED 0x1001 // Not locked
+#define TW_IOCTL_ERROR_STATUS_LOCKED 0x1002 // Already locked
+#define TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS 0x1003 // No more events
+#define TW_IOCTL_ERROR_STATUS_AEN_CLOBBER 0x1004 // AEN clobber occurred
+#define TW_IOCTL_ERROR_OS_EFAULT -EFAULT // Bad address
+#define TW_IOCTL_ERROR_OS_EINTR -EINTR // Interrupted system call
+#define TW_IOCTL_ERROR_OS_EINVAL -EINVAL // Invalid argument
+#define TW_IOCTL_ERROR_OS_ENOMEM -ENOMEM // Out of memory
+#define TW_IOCTL_ERROR_OS_ERESTARTSYS -ERESTARTSYS // Restart system call
+#define TW_IOCTL_ERROR_OS_EIO -EIO // I/O error
+#define TW_IOCTL_ERROR_OS_ENOTTY -ENOTTY // Not a typewriter
+#define TW_IOCTL_ERROR_OS_ENODEV -ENODEV // No such device
+#define TW_ALLOCATION_LENGTH 128
+#define TW_SENSE_DATA_LENGTH 18
+#define TW_STATUS_CHECK_CONDITION 2
+#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a
+#define TW_ERROR_UNIT_OFFLINE 0x128
+#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3
+#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4
+#define TW_MESSAGE_SOURCE_LINUX_DRIVER 6
+#define TW_DRIVER TW_MESSAGE_SOURCE_LINUX_DRIVER
+#define TW_MESSAGE_SOURCE_LINUX_OS 9
+#define TW_OS TW_MESSAGE_SOURCE_LINUX_OS
+#ifndef PCI_DEVICE_ID_3WARE_9000
+#define PCI_DEVICE_ID_3WARE_9000 0x1002
+#endif
+
+/* Bitmask macros to eliminate bitfields */
+
+/* opcode: 5, reserved: 3 */
+#define TW_OPRES_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_OP_OUT(x) (x & 0x1f)
+
+/* opcode: 5, sgloffset: 3 */
+#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_SGL_OUT(x) ((x >> 5) & 0x7)
+
+/* severity: 3, reserved: 5 */
+#define TW_SEV_OUT(x) (x & 0x7)
+
+/* reserved_1: 4, response_id: 8, reserved_2: 20 */
+#define TW_RESID_OUT(x) ((x >> 4) & 0xff)
+
+/* request_id: 12, lun: 4 */
+#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff))
+#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf)
+
+/* Macros */
+#define TW_CONTROL_REG_ADDR(x) (x->base_addr)
+#define TW_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0x4)
+#define TW_COMMAND_QUEUE_REG_ADDR(x) (sizeof(dma_addr_t) > 4 ? ((unsigned char __iomem *)x->base_addr + 0x20) : ((unsigned char __iomem *)x->base_addr + 0x8))
+#define TW_RESPONSE_QUEUE_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0xC)
+#define TW_CLEAR_ALL_INTERRUPTS(x) (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_ATTENTION_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_HOST_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_DISABLE_INTERRUPTS(x) (writel(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_MASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_UNMASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_SOFT_RESET(x) (writel(TW_CONTROL_ISSUE_SOFT_RESET | \
+ TW_CONTROL_CLEAR_HOST_INTERRUPT | \
+ TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
+ TW_CONTROL_MASK_COMMAND_INTERRUPT | \
+ TW_CONTROL_MASK_RESPONSE_INTERRUPT | \
+ TW_CONTROL_CLEAR_ERROR_STATUS | \
+ TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_PRINTK(h,a,b,c) { \
+if (h) \
+printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \
+else \
+printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \
+}
+#define TW_MAX_LUNS(srl) (srl < TW_FW_SRL_LUNS_SUPPORTED ? 1 : 16)
+#define TW_COMMAND_SIZE (sizeof(dma_addr_t) > 4 ? 5 : 4)
+#define TW_APACHE_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 72 : 109)
+#define TW_ESCALADE_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 41 : 62)
+#define TW_PADDING_LENGTH (sizeof(dma_addr_t) > 4 ? 8 : 0)
+
+#pragma pack(1)
+
+/* Scatter Gather List Entry */
+typedef struct TAG_TW_SG_Entry {
+ dma_addr_t address;
+ u32 length;
+} TW_SG_Entry;
+
+/* Command Packet */
+typedef struct TW_Command {
+ unsigned char opcode__sgloffset;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char unit__hostid;
+ /* Second DWORD */
+ unsigned char status;
+ unsigned char flags;
+ union {
+ unsigned short block_count;
+ unsigned short parameter_count;
+ } byte6_offset;
+ union {
+ struct {
+ u32 lba;
+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
+ dma_addr_t padding;
+ } io;
+ struct {
+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
+ u32 padding;
+ dma_addr_t padding2;
+ } param;
+ } byte8_offset;
+} TW_Command;
+
+/* Command Packet for 9000+ controllers */
+typedef struct TAG_TW_Command_Apache {
+ unsigned char opcode__reserved;
+ unsigned char unit;
+ unsigned short request_id__lunl;
+ unsigned char status;
+ unsigned char sgl_offset;
+ unsigned short sgl_entries__lunh;
+ unsigned char cdb[16];
+ TW_SG_Entry sg_list[TW_APACHE_MAX_SGL_LENGTH];
+ unsigned char padding[TW_PADDING_LENGTH];
+} TW_Command_Apache;
+
+/* New command packet header */
+typedef struct TAG_TW_Command_Apache_Header {
+ unsigned char sense_data[TW_SENSE_DATA_LENGTH];
+ struct {
+ char reserved[4];
+ unsigned short error;
+ unsigned char padding;
+ unsigned char severity__reserved;
+ } status_block;
+ unsigned char err_specific_desc[98];
+ struct {
+ unsigned char size_header;
+ unsigned short reserved;
+ unsigned char size_sense;
+ } header_desc;
+} TW_Command_Apache_Header;
+
+/* This struct is a union of the 2 command packets */
+typedef struct TAG_TW_Command_Full {
+ TW_Command_Apache_Header header;
+ union {
+ TW_Command oldcommand;
+ TW_Command_Apache newcommand;
+ } command;
+} TW_Command_Full;
+
+/* Initconnection structure */
+typedef struct TAG_TW_Initconnect {
+ unsigned char opcode__reserved;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char res2;
+ unsigned char status;
+ unsigned char flags;
+ unsigned short message_credits;
+ u32 features;
+ unsigned short fw_srl;
+ unsigned short fw_arch_id;
+ unsigned short fw_branch;
+ unsigned short fw_build;
+ u32 result;
+} TW_Initconnect;
+
+/* Event info structure */
+typedef struct TAG_TW_Event
+{
+ unsigned int sequence_id;
+ unsigned int time_stamp_sec;
+ unsigned short aen_code;
+ unsigned char severity;
+ unsigned char retrieved;
+ unsigned char repeat_count;
+ unsigned char parameter_len;
+ unsigned char parameter_data[98];
+} TW_Event;
+
+typedef struct TAG_TW_Ioctl_Driver_Command {
+ unsigned int control_code;
+ unsigned int status;
+ unsigned int unique_id;
+ unsigned int sequence_id;
+ unsigned int os_specific;
+ unsigned int buffer_length;
+} TW_Ioctl_Driver_Command;
+
+typedef struct TAG_TW_Ioctl_Apache {
+ TW_Ioctl_Driver_Command driver_command;
+ char padding[488];
+ TW_Command_Full firmware_command;
+ char data_buffer[1];
+} TW_Ioctl_Buf_Apache;
+
+/* Lock structure for ioctl get/release lock */
+typedef struct TAG_TW_Lock {
+ unsigned long timeout_msec;
+ unsigned long time_remaining_msec;
+ unsigned long force_flag;
+} TW_Lock;
+
+/* GetParam descriptor */
+typedef struct {
+ unsigned short table_id;
+ unsigned short parameter_id;
+ unsigned short parameter_size_bytes;
+ unsigned short actual_parameter_size_bytes;
+ unsigned char data[1];
+} TW_Param_Apache, *PTW_Param_Apache;
+
+/* Response queue */
+typedef union TAG_TW_Response_Queue {
+ u32 response_id;
+ u32 value;
+} TW_Response_Queue;
+
+typedef struct TAG_TW_Info {
+ char *buffer;
+ int length;
+ int offset;
+ int position;
+} TW_Info;
+
+/* Compatibility information structure */
+typedef struct TAG_TW_Compatibility_Info
+{
+ char driver_version[32];
+ unsigned short working_srl;
+ unsigned short working_branch;
+ unsigned short working_build;
+ unsigned short driver_srl_high;
+ unsigned short driver_branch_high;
+ unsigned short driver_build_high;
+ unsigned short driver_srl_low;
+ unsigned short driver_branch_low;
+ unsigned short driver_build_low;
+} TW_Compatibility_Info;
+
+typedef struct TAG_TW_Device_Extension {
+ u32 __iomem *base_addr;
+ unsigned long *generic_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
+ TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
+ dma_addr_t command_packet_phys[TW_Q_LENGTH];
+ struct pci_dev *tw_pci_dev;
+ struct scsi_cmnd *srb[TW_Q_LENGTH];
+ unsigned char free_queue[TW_Q_LENGTH];
+ unsigned char free_head;
+ unsigned char free_tail;
+ unsigned char pending_queue[TW_Q_LENGTH];
+ unsigned char pending_head;
+ unsigned char pending_tail;
+ int state[TW_Q_LENGTH];
+ unsigned int posted_request_count;
+ unsigned int max_posted_request_count;
+ unsigned int pending_request_count;
+ unsigned int max_pending_request_count;
+ unsigned int max_sgl_entries;
+ unsigned int sgl_entries;
+ unsigned int num_resets;
+ unsigned int sector_count;
+ unsigned int max_sector_count;
+ unsigned int aen_count;
+ struct Scsi_Host *host;
+ long flags;
+ int reset_print;
+ TW_Event *event_queue[TW_Q_LENGTH];
+ unsigned char error_index;
+ unsigned char event_queue_wrapped;
+ unsigned int error_sequence_id;
+ int ioctl_sem_lock;
+ u32 ioctl_msec;
+ int chrdev_request_id;
+ wait_queue_head_t ioctl_wqueue;
+ struct semaphore ioctl_sem;
+ char aen_clobber;
+ unsigned short working_srl;
+ unsigned short working_branch;
+ unsigned short working_build;
+} TW_Device_Extension;
+
+#pragma pack()
+
+#endif /* _3W_9XXX_H */
+
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
new file mode 100644
index 000000000000..48f9ece1cbd0
--- /dev/null
+++ b/drivers/scsi/3w-xxxx.c
@@ -0,0 +1,2478 @@
+/*
+ 3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@amcc.com>
+ Modifications By: Joel Jacobson <linux@3ware.com>
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Brad Strand <linux@3ware.com>
+
+ Copyright (C) 1999-2005 3ware Inc.
+
+ Kernel compatiblity By: Andre Hedrick <andre@suse.com>
+ Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
+
+ Further tiny build fixes and trivial hoovering Alan Cox
+
+ This program is free software; 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 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@amcc.com
+
+ For more information, goto:
+ http://www.amcc.com
+
+ History
+ -------
+ 0.1.000 - Initial release.
+ 0.4.000 - Added support for Asynchronous Event Notification through
+ ioctls for 3DM.
+ 1.0.000 - Added DPO & FUA bit support for WRITE_10 & WRITE_6 cdb
+ to disable drive write-cache before writes.
+ 1.1.000 - Fixed performance bug with DPO & FUA not existing for WRITE_6.
+ 1.2.000 - Added support for clean shutdown notification/feature table.
+ 1.02.00.001 - Added support for full command packet posts through ioctls
+ for 3DM.
+ Bug fix so hot spare drives don't show up.
+ 1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some
+ systems.
+ 08/21/00 - release previously allocated resources on failure at
+ tw_allocate_memory (acme)
+ 1.02.00.003 - Fix tw_interrupt() to report error to scsi layer when
+ controller status is non-zero.
+ Added handling of request_sense opcode.
+ Fix possible null pointer dereference in
+ tw_reset_device_extension()
+ 1.02.00.004 - Add support for device id of 3ware 7000 series controllers.
+ Make tw_setfeature() call with interrupts disabled.
+ Register interrupt handler before enabling interrupts.
+ Clear attention interrupt before draining aen queue.
+ 1.02.00.005 - Allocate bounce buffers and custom queue depth for raid5 for
+ 6000 and 5000 series controllers.
+ Reduce polling mdelays causing problems on some systems.
+ Fix use_sg = 1 calculation bug.
+ Check for scsi_register returning NULL.
+ Add aen count to /proc/scsi/3w-xxxx.
+ Remove aen code unit masking in tw_aen_complete().
+ 1.02.00.006 - Remove unit from printk in tw_scsi_eh_abort(), causing
+ possible oops.
+ Fix possible null pointer dereference in tw_scsi_queue()
+ if done function pointer was invalid.
+ 1.02.00.007 - Fix possible null pointer dereferences in tw_ioctl().
+ Remove check for invalid done function pointer from
+ tw_scsi_queue().
+ 1.02.00.008 - Set max sectors per io to TW_MAX_SECTORS in tw_findcards().
+ Add tw_decode_error() for printing readable error messages.
+ Print some useful information on certain aen codes.
+ Add tw_decode_bits() for interpreting status register output.
+ Make scsi_set_pci_device() for kernels >= 2.4.4
+ Fix bug where aen's could be lost before a reset.
+ Re-add spinlocks in tw_scsi_detect().
+ Fix possible null pointer dereference in tw_aen_drain_queue()
+ during initialization.
+ Clear pci parity errors during initialization and during io.
+ 1.02.00.009 - Remove redundant increment in tw_state_request_start().
+ Add ioctl support for direct ATA command passthru.
+ Add entire aen code string list.
+ 1.02.00.010 - Cleanup queueing code, fix jbod thoughput.
+ Fix get_param for specific units.
+ 1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost.
+ Fix tw_aen_drain_queue() to display useful info at init.
+ Set tw_host->max_id for 12 port cards.
+ Add ioctl support for raw command packet post from userspace
+ with sglist fragments (parameter and io).
+ 1.02.00.012 - Fix read capacity to under report by 1 sector to fix get
+ last sector ioctl.
+ 1.02.00.013 - Fix bug where more AEN codes weren't coming out during
+ driver initialization.
+ Improved handling of PCI aborts.
+ 1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
+ Increase timeout in tw_aen_drain_queue() to 30 seconds.
+ 1.02.00.015 - Re-write raw command post with data ioctl method.
+ Remove raid5 bounce buffers for raid5 for 6XXX for kernel 2.5
+ Add tw_map/unmap_scsi_sg/single_data() for kernel 2.5
+ Replace io_request_lock with host_lock for kernel 2.5
+ Set max_cmd_len to 16 for 3dm for kernel 2.5
+ 1.02.00.016 - Set host->max_sectors back up to 256.
+ 1.02.00.017 - Modified pci parity error handling/clearing from config space
+ during initialization.
+ 1.02.00.018 - Better handling of request sense opcode and sense information
+ for failed commands. Add tw_decode_sense().
+ Replace all mdelay()'s with scsi_sleep().
+ 1.02.00.019 - Revert mdelay's and scsi_sleep's, this caused problems on
+ some SMP systems.
+ 1.02.00.020 - Add pci_set_dma_mask(), rewrite kmalloc()/virt_to_bus() to
+ pci_alloc/free_consistent().
+ Better alignment checking in tw_allocate_memory().
+ Cleanup tw_initialize_device_extension().
+ 1.02.00.021 - Bump cmd_per_lun in SHT to 255 for better jbod performance.
+ Improve handling of errors in tw_interrupt().
+ Add handling/clearing of controller queue error.
+ Empty stale responses before draining aen queue.
+ Fix tw_scsi_eh_abort() to not reset on every io abort.
+ Set can_queue in SHT to 255 to prevent hang from AEN.
+ 1.02.00.022 - Fix possible null pointer dereference in tw_scsi_release().
+ 1.02.00.023 - Fix bug in tw_aen_drain_queue() where unit # was always zero.
+ 1.02.00.024 - Add severity levels to AEN strings.
+ 1.02.00.025 - Fix command interrupt spurious error messages.
+ Fix bug in raw command post with data ioctl method.
+ Fix bug where rollcall sometimes failed with cable errors.
+ Print unit # on all command timeouts.
+ 1.02.00.026 - Fix possible infinite retry bug with power glitch induced
+ drive timeouts.
+ Cleanup some AEN severity levels.
+ 1.02.00.027 - Add drive not supported AEN code for SATA controllers.
+ Remove spurious unknown ioctl error message.
+ 1.02.00.028 - Fix bug where multiple controllers with no units were the
+ same card number.
+ Fix bug where cards were being shut down more than once.
+ 1.02.00.029 - Add missing pci_free_consistent() in tw_allocate_memory().
+ Replace pci_map_single() with pci_map_page() for highmem.
+ Check for tw_setfeature() failure.
+ 1.02.00.030 - Make driver 64-bit clean.
+ 1.02.00.031 - Cleanup polling timeouts/routines in several places.
+ Add support for mode sense opcode.
+ Add support for cache mode page.
+ Add support for synchronize cache opcode.
+ 1.02.00.032 - Fix small multicard rollcall bug.
+ Make driver stay loaded with no units for hot add/swap.
+ Add support for "twe" character device for ioctls.
+ Clean up request_id queueing code.
+ Fix tw_scsi_queue() spinlocks.
+ 1.02.00.033 - Fix tw_aen_complete() to not queue 'queue empty' AEN's.
+ Initialize queues correctly when loading with no valid units.
+ 1.02.00.034 - Fix tw_decode_bits() to handle multiple errors.
+ Add support for user configurable cmd_per_lun.
+ Add support for sht->slave_configure().
+ 1.02.00.035 - Improve tw_allocate_memory() memory allocation.
+ Fix tw_chrdev_ioctl() to sleep correctly.
+ 1.02.00.036 - Increase character ioctl timeout to 60 seconds.
+ 1.02.00.037 - Fix tw_ioctl() to handle all non-data ATA passthru cmds
+ for 'smartmontools' support.
+ 1.26.00.038 - Roll driver minor version to 26 to denote kernel 2.6.
+ Add support for cmds_per_lun module parameter.
+ 1.26.00.039 - Fix bug in tw_chrdev_ioctl() polling code.
+ Fix data_buffer_length usage in tw_chrdev_ioctl().
+ Update contact information.
+ 1.26.02.000 - Convert driver to pci_driver format.
+ 1.26.02.001 - Increase max ioctl buffer size to 512 sectors.
+ Make tw_scsi_queue() return 0 for 'Unknown scsi opcode'.
+ Fix tw_remove() to free irq handler/unregister_chrdev()
+ before shutting down card.
+ Change to new 'change_queue_depth' api.
+ Fix 'handled=1' ISR usage, remove bogus IRQ check.
+*/
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include "3w-xxxx.h"
+
+/* Globals */
+#define TW_DRIVER_VERSION "1.26.02.001"
+static TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
+static int tw_device_extension_count = 0;
+static int twe_major = -1;
+
+/* Module parameters */
+MODULE_AUTHOR("AMCC");
+MODULE_DESCRIPTION("3ware Storage Controller Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(TW_DRIVER_VERSION);
+
+/* Function prototypes */
+static int tw_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset);
+
+/* Functions */
+
+/* This function will check the status register for unexpected bits */
+static int tw_check_bits(u32 status_reg_value)
+{
+ if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value);
+ return 1;
+ }
+ if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value);
+ return 1;
+ }
+
+ return 0;
+} /* End tw_check_bits() */
+
+/* This function will print readable messages from status register errors */
+static int tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int print_host)
+{
+ char host[16];
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n");
+
+ if (print_host)
+ sprintf(host, " scsi%d:", tw_dev->host->host_no);
+ else
+ host[0] = '\0';
+
+ if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
+ printk(KERN_WARNING "3w-xxxx:%s PCI Parity Error: clearing.\n", host);
+ outl(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+ }
+
+ if (status_reg_value & TW_STATUS_PCI_ABORT) {
+ printk(KERN_WARNING "3w-xxxx:%s PCI Abort: clearing.\n", host);
+ outl(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev));
+ pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
+ }
+
+ if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
+ printk(KERN_WARNING "3w-xxxx:%s Controller Queue Error: clearing.\n", host);
+ outl(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+ }
+
+ if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
+ printk(KERN_WARNING "3w-xxxx:%s SBUF Write Error: clearing.\n", host);
+ outl(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+ }
+
+ if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
+ if (tw_dev->reset_print == 0) {
+ printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host);
+ tw_dev->reset_print = 1;
+ }
+ return 1;
+ }
+
+ return 0;
+} /* End tw_decode_bits() */
+
+/* This function will poll the status register for a flag */
+static int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+ u32 status_reg_value;
+ unsigned long before;
+ int retval = 1;
+
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+ before = jiffies;
+
+ if (tw_check_bits(status_reg_value))
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+
+ while ((status_reg_value & flag) != flag) {
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+
+ if (tw_check_bits(status_reg_value))
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End tw_poll_status() */
+
+/* This function will poll the status register for disappearance of a flag */
+static int tw_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+ u32 status_reg_value;
+ unsigned long before;
+ int retval = 1;
+
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+ before = jiffies;
+
+ if (tw_check_bits(status_reg_value))
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+
+ while ((status_reg_value & flag) != 0) {
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+
+ if (tw_check_bits(status_reg_value))
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End tw_poll_status_gone() */
+
+/* This function will attempt to post a command packet to the board */
+static int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
+{
+ u32 status_reg_value;
+ unsigned long command_que_value;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_post_command_packet()\n");
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_post_command_packet(): Unexpected bits.\n");
+ tw_decode_bits(tw_dev, status_reg_value, 1);
+ }
+
+ if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
+ /* We successfully posted the command packet */
+ outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+ tw_dev->state[request_id] = TW_S_POSTED;
+ tw_dev->posted_request_count++;
+ if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
+ tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+ }
+ } else {
+ /* Couldn't post the command packet, so we do it in the isr */
+ if (tw_dev->state[request_id] != TW_S_PENDING) {
+ tw_dev->state[request_id] = TW_S_PENDING;
+ tw_dev->pending_request_count++;
+ if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
+ tw_dev->max_pending_request_count = tw_dev->pending_request_count;
+ }
+ tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
+ if (tw_dev->pending_tail == TW_Q_LENGTH-1) {
+ tw_dev->pending_tail = TW_Q_START;
+ } else {
+ tw_dev->pending_tail = tw_dev->pending_tail + 1;
+ }
+ }
+ TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
+ return 1;
+ }
+ return 0;
+} /* End tw_post_command_packet() */
+
+/* This function will return valid sense buffer information for failed cmds */
+static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense)
+{
+ int i;
+ TW_Command *command;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
+ command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, TW_UNIT_OUT(command->unit__hostid));
+
+ /* Attempt to return intelligent sense information */
+ if (fill_sense) {
+ if ((command->status == 0xc7) || (command->status == 0xcb)) {
+ for (i=0;i<(sizeof(tw_sense_table)/sizeof(tw_sense_table[0]));i++) {
+ if (command->flags == tw_sense_table[i][0]) {
+
+ /* Valid bit and 'current errors' */
+ tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70);
+
+ /* Sense key */
+ tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1];
+
+ /* Additional sense length */
+ tw_dev->srb[request_id]->sense_buffer[7] = 0xa; /* 10 bytes */
+
+ /* Additional sense code */
+ tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2];
+
+ /* Additional sense code qualifier */
+ tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
+
+ tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ return TW_ISR_DONT_RESULT; /* Special case for isr to not over-write result */
+ }
+ }
+ }
+
+ /* If no table match, error so we get a reset */
+ return 1;
+ }
+
+ return 0;
+} /* End tw_decode_sense() */
+
+/* This function will report controller error status */
+static int tw_check_errors(TW_Device_Extension *tw_dev)
+{
+ u32 status_reg_value;
+
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+
+ if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) {
+ tw_decode_bits(tw_dev, status_reg_value, 0);
+ return 1;
+ }
+
+ return 0;
+} /* End tw_check_errors() */
+
+/* This function will empty the response que */
+static void tw_empty_response_que(TW_Device_Extension *tw_dev)
+{
+ u32 status_reg_value, response_que_value;
+
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+
+ while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+ response_que_value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+ }
+} /* End tw_empty_response_que() */
+
+/* This function will free a request_id */
+static void tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id)
+{
+ tw_dev->free_queue[tw_dev->free_tail] = request_id;
+ tw_dev->state[request_id] = TW_S_FINISHED;
+ tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+} /* End tw_state_request_finish() */
+
+/* This function will assign an available request_id */
+static void tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
+{
+ *request_id = tw_dev->free_queue[tw_dev->free_head];
+ tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
+ tw_dev->state[*request_id] = TW_S_STARTED;
+} /* End tw_state_request_start() */
+
+/* Show some statistics about the card */
+static ssize_t tw_show_stats(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(class_dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+ unsigned long flags = 0;
+ ssize_t len;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ len = snprintf(buf, PAGE_SIZE, "3w-xxxx Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Current pending commands: %4d\n"
+ "Max pending commands: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->pending_request_count,
+ tw_dev->max_pending_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ return len;
+} /* End tw_show_stats() */
+
+/* This function will set a devices queue depth */
+static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+{
+ if (queue_depth > TW_Q_LENGTH-2)
+ queue_depth = TW_Q_LENGTH-2;
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
+ return queue_depth;
+} /* End tw_change_queue_depth() */
+
+/* Create sysfs 'stats' entry */
+static struct class_device_attribute tw_host_stats_attr = {
+ .attr = {
+ .name = "stats",
+ .mode = S_IRUGO,
+ },
+ .show = tw_show_stats
+};
+
+/* Host attributes initializer */
+static struct class_device_attribute *tw_host_attrs[] = {
+ &tw_host_stats_attr,
+ NULL,
+};
+
+/* This function will read the aen queue from the isr */
+static int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Command *command_packet;
+ TW_Param *param;
+ unsigned long command_que_value;
+ u32 status_reg_value;
+ unsigned long param_value = 0;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n");
+
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n");
+ tw_decode_bits(tw_dev, status_reg_value, 1);
+ return 1;
+ }
+ if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n");
+ return 1;
+ }
+ /* Now setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = 0x401; /* AEN table */
+ param->parameter_id = 2; /* Unit code */
+ param->parameter_size_bytes = 2;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment physical address.\n");
+ return 1;
+ }
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+ /* Now post the command packet */
+ if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n");
+ tw_dev->srb[request_id] = NULL; /* Flag internal command */
+ tw_dev->state[request_id] = TW_S_POSTED;
+ outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+ } else {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n");
+ return 1;
+ }
+
+ return 0;
+} /* End tw_aen_read_queue() */
+
+/* This function will complete an aen request from the isr */
+static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Param *param;
+ unsigned short aen;
+ int error = 0, table_max = 0;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ aen = *(unsigned short *)(param->data);
+ dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen);
+
+ /* Print some useful info when certain aen codes come out */
+ if (aen == 0x0ff) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: INFO: AEN queue overflow.\n", tw_dev->host->host_no);
+ } else {
+ table_max = sizeof(tw_aen_string)/sizeof(char *);
+ if ((aen & 0x0ff) < table_max) {
+ if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
+ } else {
+ if (aen != 0x0)
+ printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
+ }
+ } else {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
+ }
+ }
+ if (aen != TW_AEN_QUEUE_EMPTY) {
+ tw_dev->aen_count++;
+
+ /* Now queue the code */
+ tw_dev->aen_queue[tw_dev->aen_tail] = aen;
+ if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
+ tw_dev->aen_tail = TW_Q_START;
+ } else {
+ tw_dev->aen_tail = tw_dev->aen_tail + 1;
+ }
+ if (tw_dev->aen_head == tw_dev->aen_tail) {
+ if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+ tw_dev->aen_head = TW_Q_START;
+ } else {
+ tw_dev->aen_head = tw_dev->aen_head + 1;
+ }
+ }
+
+ error = tw_aen_read_queue(tw_dev, request_id);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ }
+ } else {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ }
+
+ return 0;
+} /* End tw_aen_complete() */
+
+/* This function will drain the aen queue after a soft reset */
+static int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
+{
+ TW_Command *command_packet;
+ TW_Param *param;
+ int request_id = 0;
+ unsigned long command_que_value;
+ unsigned long param_value;
+ TW_Response_Queue response_queue;
+ unsigned short aen;
+ unsigned short aen_code;
+ int finished = 0;
+ int first_reset = 0;
+ int queue = 0;
+ int found = 0, table_max = 0;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n");
+
+ if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT | TW_STATUS_MICROCONTROLLER_READY, 30)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
+ return 1;
+ }
+ TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
+
+ /* Empty response queue */
+ tw_empty_response_que(tw_dev);
+
+ /* Initialize command packet */
+ if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = 0x401; /* AEN table */
+ param->parameter_id = 2; /* Unit code */
+ param->parameter_size_bytes = 2;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n");
+ return 1;
+ }
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+ /* Now drain the controller's aen queue */
+ do {
+ /* Post command packet */
+ outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+
+ /* Now poll for completion */
+ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
+ response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ request_id = TW_RESID_OUT(response_queue.response_id);
+
+ if (request_id != 0) {
+ /* Unexpected request id */
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n");
+ return 1;
+ }
+
+ if (command_packet->status != 0) {
+ if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
+ /* Bad response */
+ tw_decode_sense(tw_dev, request_id, 0);
+ return 1;
+ } else {
+ /* We know this is a 3w-1x00, and doesn't support aen's */
+ return 0;
+ }
+ }
+
+ /* Now check the aen */
+ aen = *(unsigned short *)(param->data);
+ aen_code = (aen & 0x0ff);
+ queue = 0;
+ switch (aen_code) {
+ case TW_AEN_QUEUE_EMPTY:
+ dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+ if (first_reset != 1) {
+ return 1;
+ } else {
+ finished = 1;
+ }
+ break;
+ case TW_AEN_SOFT_RESET:
+ if (first_reset == 0) {
+ first_reset = 1;
+ } else {
+ printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+ tw_dev->aen_count++;
+ queue = 1;
+ }
+ break;
+ default:
+ if (aen == 0x0ff) {
+ printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n");
+ } else {
+ table_max = sizeof(tw_aen_string)/sizeof(char *);
+ if ((aen & 0x0ff) < table_max) {
+ if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
+ printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
+ } else {
+ printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+ }
+ } else
+ printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
+ }
+ tw_dev->aen_count++;
+ queue = 1;
+ }
+
+ /* Now put the aen on the aen_queue */
+ if (queue == 1) {
+ tw_dev->aen_queue[tw_dev->aen_tail] = aen;
+ if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
+ tw_dev->aen_tail = TW_Q_START;
+ } else {
+ tw_dev->aen_tail = tw_dev->aen_tail + 1;
+ }
+ if (tw_dev->aen_head == tw_dev->aen_tail) {
+ if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+ tw_dev->aen_head = TW_Q_START;
+ } else {
+ tw_dev->aen_head = tw_dev->aen_head + 1;
+ }
+ }
+ }
+ found = 1;
+ }
+ if (found == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n");
+ return 1;
+ }
+ } while (finished == 0);
+
+ return 0;
+} /* End tw_aen_drain_queue() */
+
+/* This function will allocate memory */
+static int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
+{
+ int i;
+ dma_addr_t dma_handle;
+ unsigned long *cpu_addr = NULL;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
+
+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ if (cpu_addr == NULL) {
+ printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n");
+ return 1;
+ }
+
+ if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
+ printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
+ pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
+ return 1;
+ }
+
+ memset(cpu_addr, 0, size*TW_Q_LENGTH);
+
+ for (i=0;i<TW_Q_LENGTH;i++) {
+ switch(which) {
+ case 0:
+ tw_dev->command_packet_physical_address[i] = dma_handle+(i*size);
+ tw_dev->command_packet_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ case 1:
+ tw_dev->alignment_physical_address[i] = dma_handle+(i*size);
+ tw_dev->alignment_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ default:
+ printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
+ return 1;
+ }
+ }
+
+ return 0;
+} /* End tw_allocate_memory() */
+
+/* This function handles ioctl for the character device */
+static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int request_id;
+ dma_addr_t dma_handle;
+ unsigned short tw_aen_code;
+ unsigned long flags;
+ unsigned int data_buffer_length = 0;
+ unsigned long data_buffer_length_adjusted = 0;
+ unsigned long *cpu_addr;
+ long timeout;
+ TW_New_Ioctl *tw_ioctl;
+ TW_Passthru *passthru;
+ TW_Device_Extension *tw_dev = tw_device_extension_list[iminor(inode)];
+ int retval = -EFAULT;
+ void __user *argp = (void __user *)arg;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
+
+ /* Only let one of these through at a time */
+ if (down_interruptible(&tw_dev->ioctl_sem))
+ return -EINTR;
+
+ /* First copy down the buffer length */
+ if (copy_from_user(&data_buffer_length, argp, sizeof(unsigned int)))
+ goto out;
+
+ /* Check size */
+ if (data_buffer_length > TW_MAX_IOCTL_SECTORS * 512) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ /* Hardware can only do multiple of 512 byte transfers */
+ data_buffer_length_adjusted = (data_buffer_length + 511) & ~511;
+
+ /* Now allocate ioctl buf memory */
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle, GFP_KERNEL);
+ if (cpu_addr == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ tw_ioctl = (TW_New_Ioctl *)cpu_addr;
+
+ /* Now copy down the entire ioctl */
+ if (copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl) - 1))
+ goto out2;
+
+ passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
+
+ /* See which ioctl we are doing */
+ switch (cmd) {
+ case TW_OP_NOP:
+ dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n");
+ break;
+ case TW_OP_AEN_LISTEN:
+ dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
+ memset(tw_ioctl->data_buffer, 0, data_buffer_length);
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ if (tw_dev->aen_head == tw_dev->aen_tail) {
+ tw_aen_code = TW_AEN_QUEUE_EMPTY;
+ } else {
+ tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
+ if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+ tw_dev->aen_head = TW_Q_START;
+ } else {
+ tw_dev->aen_head = tw_dev->aen_head + 1;
+ }
+ }
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
+ break;
+ case TW_CMD_PACKET_WITH_DATA:
+ dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+ tw_state_request_start(tw_dev, &request_id);
+
+ /* Flag internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Flag chrdev ioctl */
+ tw_dev->chrdev_request_id = request_id;
+
+ tw_ioctl->firmware_command.request_id = request_id;
+
+ /* Load the sg list */
+ switch (TW_SGL_OUT(tw_ioctl->firmware_command.opcode__sgloffset)) {
+ case 2:
+ tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
+ break;
+ case 3:
+ tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
+ break;
+ case 5:
+ passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+ passthru->sg_list[0].length = data_buffer_length_adjusted;
+ break;
+ }
+
+ memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command));
+
+ /* Now post the command packet to the controller */
+ tw_post_command_packet(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
+ /* Now wait for the command to complete */
+ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+
+ /* See if we reset while waiting for the ioctl to complete */
+ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
+ clear_bit(TW_IN_RESET, &tw_dev->flags);
+ retval = -ERESTARTSYS;
+ goto out2;
+ }
+
+ /* We timed out, and didn't get an interrupt */
+ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+ /* Now we need to reset the board */
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
+ retval = -EIO;
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ if (tw_reset_device_extension(tw_dev, 1)) {
+ printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
+ }
+ goto out2;
+ }
+
+ /* Now copy in the command packet response */
+ memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command));
+
+ /* Now complete the io */
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ break;
+ default:
+ retval = -ENOTTY;
+ goto out2;
+ }
+
+ /* Now copy the response to userspace */
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length - 1))
+ goto out2;
+ retval = 0;
+out2:
+ /* Now free ioctl buf memory */
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+out:
+ up(&tw_dev->ioctl_sem);
+ return retval;
+} /* End tw_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int tw_chrdev_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor_number;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
+
+ minor_number = iminor(inode);
+ if (minor_number >= tw_device_extension_count)
+ return -ENODEV;
+
+ return 0;
+} /* End tw_chrdev_open() */
+
+/* File operations struct for character device */
+static struct file_operations tw_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = tw_chrdev_ioctl,
+ .open = tw_chrdev_open,
+ .release = NULL
+};
+
+/* This function will free up device extension resources */
+static void tw_free_device_extension(TW_Device_Extension *tw_dev)
+{
+ dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
+
+ /* Free command packet and generic buffer memory */
+ if (tw_dev->command_packet_virtual_address[0])
+ pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command)*TW_Q_LENGTH, tw_dev->command_packet_virtual_address[0], tw_dev->command_packet_physical_address[0]);
+
+ if (tw_dev->alignment_virtual_address[0])
+ pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_Q_LENGTH, tw_dev->alignment_virtual_address[0], tw_dev->alignment_physical_address[0]);
+} /* End tw_free_device_extension() */
+
+/* This function will send an initconnection command to controller */
+static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
+{
+ unsigned long command_que_value;
+ TW_Command *command_packet;
+ TW_Response_Queue response_queue;
+ int request_id = 0;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n");
+
+ /* Initialize InitConnection command packet */
+ if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet virtual address.\n");
+ return 1;
+ }
+
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_INIT_CONNECTION);
+ command_packet->size = TW_INIT_COMMAND_PACKET_SIZE;
+ command_packet->request_id = request_id;
+ command_packet->status = 0x0;
+ command_packet->flags = 0x0;
+ command_packet->byte6.message_credits = message_credits;
+ command_packet->byte8.init_connection.response_queue_pointer = 0x0;
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Send command packet to the board */
+ outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+
+ /* Poll for completion */
+ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
+ response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ request_id = TW_RESID_OUT(response_queue.response_id);
+
+ if (request_id != 0) {
+ /* unexpected request id */
+ printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected request id.\n");
+ return 1;
+ }
+ if (command_packet->status != 0) {
+ /* bad response */
+ tw_decode_sense(tw_dev, request_id, 0);
+ return 1;
+ }
+ }
+ return 0;
+} /* End tw_initconnection() */
+
+/* Set a value in the features table */
+static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
+ unsigned char *val)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ TW_Response_Queue response_queue;
+ int request_id = 0;
+ unsigned long command_que_value;
+ unsigned long param_value;
+
+ /* Initialize SetParam command packet */
+ if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ memset(command_packet, 0, sizeof(TW_Sector));
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
+ param->table_id = 0x404; /* Features table */
+ param->parameter_id = parm;
+ param->parameter_size_bytes = param_size;
+ memcpy(param->data, val, param_size);
+
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad alignment physical address.\n");
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->srb[request_id]->result = (DID_OK << 16);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ }
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->byte6.parameter_count = 1;
+
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Send command packet to the board */
+ outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+
+ /* Poll for completion */
+ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
+ response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ request_id = TW_RESID_OUT(response_queue.response_id);
+
+ if (request_id != 0) {
+ /* unexpected request id */
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected request id.\n");
+ return 1;
+ }
+ if (command_packet->status != 0) {
+ /* bad response */
+ tw_decode_sense(tw_dev, request_id, 0);
+ return 1;
+ }
+ }
+
+ return 0;
+} /* End tw_setfeature() */
+
+/* This function will reset a controller */
+static int tw_reset_sequence(TW_Device_Extension *tw_dev)
+{
+ int error = 0;
+ int tries = 0;
+ unsigned char c = 1;
+
+ /* Reset the board */
+ while (tries < TW_MAX_RESET_TRIES) {
+ TW_SOFT_RESET(tw_dev);
+
+ error = tw_aen_drain_queue(tw_dev);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no);
+ tries++;
+ continue;
+ }
+
+ /* Check for controller errors */
+ if (tw_check_errors(tw_dev)) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Controller errors found, retrying.\n", tw_dev->host->host_no);
+ tries++;
+ continue;
+ }
+
+ /* Now the controller is in a good state */
+ break;
+ }
+
+ if (tries >= TW_MAX_RESET_TRIES) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Controller errors, card not responding, check all cabling.\n", tw_dev->host->host_no);
+ return 1;
+ }
+
+ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Connection initialization failed.\n", tw_dev->host->host_no);
+ return 1;
+ }
+
+ error = tw_setfeature(tw_dev, 2, 1, &c);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: Unable to set features for card, probable old firmware or card.\n");
+ }
+
+ return 0;
+} /* End tw_reset_sequence() */
+
+/* This function will initialize the fields of a device extension */
+static int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+ int i, error=0;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
+
+ /* Initialize command packet buffers */
+ error = tw_allocate_memory(tw_dev, sizeof(TW_Command), 0);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: Command packet memory allocation failed.\n");
+ return 1;
+ }
+
+ /* Initialize generic buffer */
+ error = tw_allocate_memory(tw_dev, sizeof(TW_Sector), 1);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: Generic memory allocation failed.\n");
+ return 1;
+ }
+
+ for (i=0;i<TW_Q_LENGTH;i++) {
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+
+ tw_dev->pending_head = TW_Q_START;
+ tw_dev->pending_tail = TW_Q_START;
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+ init_MUTEX(&tw_dev->ioctl_sem);
+ init_waitqueue_head(&tw_dev->ioctl_wqueue);
+
+ return 0;
+} /* End tw_initialize_device_extension() */
+
+static int tw_map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
+{
+ int use_sg;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n");
+
+ if (cmd->use_sg == 0)
+ return 0;
+
+ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
+
+ if (use_sg == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data(): pci_map_sg() failed.\n");
+ return 0;
+ }
+
+ cmd->SCp.phase = TW_PHASE_SGLIST;
+ cmd->SCp.have_data_in = use_sg;
+
+ return use_sg;
+} /* End tw_map_scsi_sg_data() */
+
+static u32 tw_map_scsi_single_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
+{
+ dma_addr_t mapping;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data()\n");
+
+ if (cmd->request_bufflen == 0)
+ return 0;
+
+ mapping = pci_map_page(pdev, virt_to_page(cmd->request_buffer), offset_in_page(cmd->request_buffer), cmd->request_bufflen, DMA_BIDIRECTIONAL);
+
+ if (mapping == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data(): pci_map_page() failed.\n");
+ return 0;
+ }
+
+ cmd->SCp.phase = TW_PHASE_SINGLE;
+ cmd->SCp.have_data_in = mapping;
+
+ return mapping;
+} /* End tw_map_scsi_single_data() */
+
+static void tw_unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
+{
+ dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n");
+
+ switch(cmd->SCp.phase) {
+ case TW_PHASE_SINGLE:
+ pci_unmap_page(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, DMA_BIDIRECTIONAL);
+ break;
+ case TW_PHASE_SGLIST:
+ pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
+ break;
+ }
+} /* End tw_unmap_scsi_data() */
+
+/* This function will reset a device extension */
+static int tw_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset)
+{
+ int i = 0;
+ struct scsi_cmnd *srb;
+ unsigned long flags = 0;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_reset_device_extension()\n");
+
+ set_bit(TW_IN_RESET, &tw_dev->flags);
+ TW_DISABLE_INTERRUPTS(tw_dev);
+ TW_MASK_COMMAND_INTERRUPT(tw_dev);
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+ /* Abort all requests that are in progress */
+ for (i=0;i<TW_Q_LENGTH;i++) {
+ if ((tw_dev->state[i] != TW_S_FINISHED) &&
+ (tw_dev->state[i] != TW_S_INITIAL) &&
+ (tw_dev->state[i] != TW_S_COMPLETED)) {
+ srb = tw_dev->srb[i];
+ if (srb != NULL) {
+ srb->result = (DID_RESET << 16);
+ tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+ tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[i]);
+ }
+ }
+ }
+
+ /* Reset queues and counts */
+ for (i=0;i<TW_Q_LENGTH;i++) {
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->posted_request_count = 0;
+ tw_dev->pending_request_count = 0;
+ tw_dev->pending_head = TW_Q_START;
+ tw_dev->pending_tail = TW_Q_START;
+ tw_dev->reset_print = 0;
+
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ if (tw_reset_sequence(tw_dev)) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Reset sequence failed.\n", tw_dev->host->host_no);
+ return 1;
+ }
+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+ /* Wake up any ioctl that was pending before the reset */
+ if ((tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE) || (ioctl_reset)) {
+ clear_bit(TW_IN_RESET, &tw_dev->flags);
+ } else {
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
+ }
+
+ return 0;
+} /* End tw_reset_device_extension() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+static int tw_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ int heads, sectors, cylinders;
+ TW_Device_Extension *tw_dev;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam()\n");
+ tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
+
+ heads = 64;
+ sectors = 32;
+ cylinders = sector_div(capacity, heads * sectors);
+
+ if (capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = sector_div(capacity, heads * sectors);
+ }
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam(): heads = %d, sectors = %d, cylinders = %d\n", heads, sectors, cylinders);
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return 0;
+} /* End tw_scsi_biosparam() */
+
+/* This is the new scsi eh reset function */
+static int tw_scsi_eh_reset(struct scsi_cmnd *SCpnt)
+{
+ TW_Device_Extension *tw_dev=NULL;
+ int retval = FAILED;
+
+ tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ spin_unlock_irq(tw_dev->host->host_lock);
+
+ tw_dev->num_resets++;
+
+ printk(KERN_WARNING "3w-xxxx: scsi%d: WARNING: Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, SCpnt->device->id, SCpnt->cmnd[0]);
+
+ /* Now reset the card and some of the device extension data */
+ if (tw_reset_device_extension(tw_dev, 0)) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Reset failed.\n", tw_dev->host->host_no);
+ goto out;
+ }
+
+ retval = SUCCESS;
+out:
+ spin_lock_irq(tw_dev->host->host_lock);
+ return retval;
+} /* End tw_scsi_eh_reset() */
+
+/* This function handles scsi inquiry commands */
+static int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+ unsigned long param_value;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry()\n");
+
+ /* Initialize command packet */
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ if (command_packet == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+
+ /* Now setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = 3; /* unit summary table */
+ param->parameter_id = 3; /* unitsstatus parameter */
+ param->parameter_size_bytes = TW_MAX_UNITS;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment physical address.\n");
+ return 1;
+ }
+
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command packet */
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
+} /* End tw_scsiop_inquiry() */
+
+/* This function is called by the isr to complete an inquiry command */
+static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ unsigned char *is_unit_present;
+ unsigned char *request_buffer;
+ TW_Param *param;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
+
+ /* Fill request buffer */
+ if (tw_dev->srb[request_id]->request_buffer == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Request buffer NULL.\n");
+ return 1;
+ }
+ request_buffer = tw_dev->srb[request_id]->request_buffer;
+ memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+ request_buffer[0] = TYPE_DISK; /* Peripheral device type */
+ request_buffer[1] = 0; /* Device type modifier */
+ request_buffer[2] = 0; /* No ansi/iso compliance */
+ request_buffer[4] = 31; /* Additional length */
+ memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */
+ sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->device->id);
+ memcpy(&request_buffer[32], TW_DRIVER_VERSION, 3);
+
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ if (param == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ is_unit_present = &(param->data[0]);
+
+ if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 1;
+ } else {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 0;
+ tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+ return TW_ISR_DONT_RESULT;
+ }
+
+ return 0;
+} /* End tw_scsiop_inquiry_complete() */
+
+/* This function handles scsi mode_sense commands */
+static int tw_scsiop_mode_sense(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+ unsigned long param_value;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense()\n");
+
+ /* Only page control = 0, page code = 0x8 (cache page) supported */
+ if (tw_dev->srb[request_id]->cmnd[2] != 0x8) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->srb[request_id]->result = (DID_OK << 16);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ return 0;
+ }
+
+ /* Now read firmware cache setting for this unit */
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ if (command_packet == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet virtual address.\n");
+ return 1;
+ }
+
+ /* Setup the command packet */
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+
+ /* Setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment virtual address.\n");
+ return 1;
+ }
+
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = TW_UNIT_INFORMATION_TABLE_BASE + tw_dev->srb[request_id]->device->id;
+ param->parameter_id = 7; /* unit flags */
+ param->parameter_size_bytes = 1;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment physical address.\n");
+ return 1;
+ }
+
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command packet */
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
+} /* End tw_scsiop_mode_sense() */
+
+/* This function is called by the isr to complete a mode sense command */
+static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Param *param;
+ unsigned char *flags;
+ unsigned char *request_buffer;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense_complete()\n");
+
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ if (param == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense_complete(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ flags = (char *)&(param->data[0]);
+ request_buffer = tw_dev->srb[request_id]->buffer;
+ memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+
+ request_buffer[0] = 0xf; /* mode data length */
+ request_buffer[1] = 0; /* default medium type */
+ request_buffer[2] = 0x10; /* dpo/fua support on */
+ request_buffer[3] = 0; /* no block descriptors */
+ request_buffer[4] = 0x8; /* caching page */
+ request_buffer[5] = 0xa; /* page length */
+ if (*flags & 0x1)
+ request_buffer[6] = 0x4; /* WCE on */
+ else
+ request_buffer[6] = 0x0; /* WCE off */
+
+ return 0;
+} /* End tw_scsiop_mode_sense_complete() */
+
+/* This function handles scsi read_capacity commands */
+static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+ unsigned long param_value;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity()\n");
+
+ /* Initialize command packet */
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+
+ if (command_packet == NULL) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->unit__hostid = TW_UNITHOST_IN(0, tw_dev->srb[request_id]->device->id);
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.block_count = 1;
+
+ /* Now setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = TW_UNIT_INFORMATION_TABLE_BASE +
+ tw_dev->srb[request_id]->device->id;
+ param->parameter_id = 4; /* unitcapacity parameter */
+ param->parameter_size_bytes = 4;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment physical address.\n");
+ return 1;
+ }
+
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command to the board */
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
+} /* End tw_scsiop_read_capacity() */
+
+/* This function is called by the isr to complete a readcapacity command */
+static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ unsigned char *param_data;
+ u32 capacity;
+ char *buff;
+ TW_Param *param;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n");
+
+ buff = tw_dev->srb[request_id]->request_buffer;
+ if (buff == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Request buffer NULL.\n");
+ return 1;
+ }
+ memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ if (param == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param_data = &(param->data[0]);
+
+ capacity = (param_data[3] << 24) | (param_data[2] << 16) |
+ (param_data[1] << 8) | param_data[0];
+
+ /* Subtract one sector to fix get last sector ioctl */
+ capacity -= 1;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
+
+ /* Number of LBA's */
+ buff[0] = (capacity >> 24);
+ buff[1] = (capacity >> 16) & 0xff;
+ buff[2] = (capacity >> 8) & 0xff;
+ buff[3] = capacity & 0xff;
+
+ /* Block size in bytes (512) */
+ buff[4] = (TW_BLOCK_SIZE >> 24);
+ buff[5] = (TW_BLOCK_SIZE >> 16) & 0xff;
+ buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff;
+ buff[7] = TW_BLOCK_SIZE & 0xff;
+
+ return 0;
+} /* End tw_scsiop_read_capacity_complete() */
+
+/* This function handles scsi read or write commands */
+static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+ u32 lba = 0x0, num_sectors = 0x0, buffaddr = 0x0;
+ int i, use_sg;
+ struct scsi_cmnd *srb;
+ struct scatterlist *sglist;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write()\n");
+
+ if (tw_dev->srb[request_id]->request_buffer == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Request buffer NULL.\n");
+ return 1;
+ }
+ sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer;
+ srb = tw_dev->srb[request_id];
+
+ /* Initialize command packet */
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ if (command_packet == NULL) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): Bad command packet virtual address.\n");
+ return 1;
+ }
+
+ if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == READ_10) {
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(3, TW_OP_READ);
+ } else {
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(3, TW_OP_WRITE);
+ }
+
+ command_packet->size = 3;
+ command_packet->request_id = request_id;
+ command_packet->unit__hostid = TW_UNITHOST_IN(0, srb->device->id);
+ command_packet->status = 0;
+ command_packet->flags = 0;
+
+ if (srb->cmnd[0] == WRITE_10) {
+ if ((srb->cmnd[1] & 0x8) || (srb->cmnd[1] & 0x10))
+ command_packet->flags = 1;
+ }
+
+ if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6) {
+ lba = ((u32)srb->cmnd[1] << 16) | ((u32)srb->cmnd[2] << 8) | (u32)srb->cmnd[3];
+ num_sectors = (u32)srb->cmnd[4];
+ } else {
+ lba = ((u32)srb->cmnd[2] << 24) | ((u32)srb->cmnd[3] << 16) | ((u32)srb->cmnd[4] << 8) | (u32)srb->cmnd[5];
+ num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
+ }
+
+ /* Update sector statistic */
+ tw_dev->sector_count = num_sectors;
+ if (tw_dev->sector_count > tw_dev->max_sector_count)
+ tw_dev->max_sector_count = tw_dev->sector_count;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): lba = 0x%x num_sectors = 0x%x\n", lba, num_sectors);
+ command_packet->byte8.io.lba = lba;
+ command_packet->byte6.block_count = num_sectors;
+
+ /* Do this if there are no sg list entries */
+ if (tw_dev->srb[request_id]->use_sg == 0) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
+ buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+ if (buffaddr == 0)
+ return 1;
+
+ command_packet->byte8.io.sgl[0].address = buffaddr;
+ command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+ command_packet->size+=2;
+ }
+
+ /* Do this if we have multiple sg list entries */
+ if (tw_dev->srb[request_id]->use_sg > 0) {
+ use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+ if (use_sg == 0)
+ return 1;
+
+ for (i=0;i<use_sg; i++) {
+ command_packet->byte8.io.sgl[i].address = sg_dma_address(&sglist[i]);
+ command_packet->byte8.io.sgl[i].length = sg_dma_len(&sglist[i]);
+ command_packet->size+=2;
+ }
+ }
+
+ /* Update SG statistics */
+ tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
+ if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+ tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command to the board */
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
+} /* End tw_scsiop_read_write() */
+
+/* This function will handle the request sense scsi command */
+static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
+{
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
+
+ /* For now we just zero the request buffer */
+ memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+
+ /* If we got a request_sense, we probably want a reset, return error */
+ tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+
+ return 0;
+} /* End tw_scsiop_request_sense() */
+
+/* This function will handle synchronize cache scsi command */
+static int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_synchronize_cache()\n");
+
+ /* Send firmware flush command for this unit */
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ if (command_packet == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet virtual address.\n");
+ return 1;
+ }
+
+ /* Setup the command packet */
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_FLUSH_CACHE);
+ command_packet->size = 2;
+ command_packet->request_id = request_id;
+ command_packet->unit__hostid = TW_UNITHOST_IN(0, tw_dev->srb[request_id]->device->id);
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command packet */
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
+} /* End tw_scsiop_synchronize_cache() */
+
+/* This function will handle test unit ready scsi command */
+static int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ unsigned long command_que_value;
+ unsigned long param_value;
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
+
+ /* Initialize command packet */
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ if (command_packet == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ memset(command_packet, 0, sizeof(TW_Sector));
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->status = 0;
+ command_packet->flags = 0;
+ command_packet->byte6.parameter_count = 1;
+
+ /* Now setup the param */
+ if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ memset(param, 0, sizeof(TW_Sector));
+ param->table_id = 3; /* unit summary table */
+ param->parameter_id = 3; /* unitsstatus parameter */
+ param->parameter_size_bytes = TW_MAX_UNITS;
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment physical address.\n");
+ return 1;
+ }
+
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet physical address.\n");
+ return 1;
+ }
+
+ /* Now try to post the command packet */
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
+} /* End tw_scsiop_test_unit_ready() */
+
+/* This function is called by the isr to complete a testunitready command */
+static int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ unsigned char *is_unit_present;
+ TW_Param *param;
+
+ dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete()\n");
+
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+ if (param == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete(): Bad alignment virtual address.\n");
+ return 1;
+ }
+ is_unit_present = &(param->data[0]);
+
+ if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 1;
+ } else {
+ tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 0;
+ tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+ return TW_ISR_DONT_RESULT;
+ }
+
+ return 0;
+} /* End tw_scsiop_test_unit_ready_complete() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+static int tw_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ unsigned char *command = SCpnt->cmnd;
+ int request_id = 0;
+ int retval = 1;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ /* Save done function into Scsi_Cmnd struct */
+ SCpnt->scsi_done = done;
+
+ /* Queue the command and get a request id */
+ tw_state_request_start(tw_dev, &request_id);
+
+ /* Save the scsi command for use by the ISR */
+ tw_dev->srb[request_id] = SCpnt;
+
+ /* Initialize phase to zero */
+ SCpnt->SCp.phase = TW_PHASE_INITIAL;
+
+ switch (*command) {
+ case READ_10:
+ case READ_6:
+ case WRITE_10:
+ case WRITE_6:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ/WRITE.\n");
+ retval = tw_scsiop_read_write(tw_dev, request_id);
+ break;
+ case TEST_UNIT_READY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n");
+ retval = tw_scsiop_test_unit_ready(tw_dev, request_id);
+ break;
+ case INQUIRY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n");
+ retval = tw_scsiop_inquiry(tw_dev, request_id);
+ break;
+ case READ_CAPACITY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
+ retval = tw_scsiop_read_capacity(tw_dev, request_id);
+ break;
+ case REQUEST_SENSE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
+ retval = tw_scsiop_request_sense(tw_dev, request_id);
+ break;
+ case MODE_SENSE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught MODE_SENSE.\n");
+ retval = tw_scsiop_mode_sense(tw_dev, request_id);
+ break;
+ case SYNCHRONIZE_CACHE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught SYNCHRONIZE_CACHE.\n");
+ retval = tw_scsiop_synchronize_cache(tw_dev, request_id);
+ break;
+ case TW_IOCTL:
+ printk(KERN_WARNING "3w-xxxx: SCSI_IOCTL_SEND_COMMAND deprecated, please update your 3ware tools.\n");
+ break;
+ default:
+ printk(KERN_NOTICE "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x\n", tw_dev->host->host_no, *command);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ done(SCpnt);
+ retval = 0;
+ }
+ if (retval) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ SCpnt->result = (DID_ERROR << 16);
+ done(SCpnt);
+ retval = 0;
+ }
+ return retval;
+} /* End tw_scsi_queue() */
+
+/* This function is the interrupt service routine */
+static irqreturn_t tw_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ int request_id;
+ u32 status_reg_value;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+ TW_Response_Queue response_que;
+ int error = 0, retval = 0;
+ TW_Command *command_packet;
+ int handled = 0;
+
+ /* Get the host lock for io completions */
+ spin_lock(tw_dev->host->host_lock);
+
+ /* Read the registers */
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+
+ /* Check if this is our interrupt, otherwise bail */
+ if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
+ goto tw_interrupt_bail;
+
+ handled = 1;
+
+ /* Check controller for errors */
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+ if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+ goto tw_interrupt_bail;
+ }
+ }
+
+ /* Handle host interrupt */
+ if (status_reg_value & TW_STATUS_HOST_INTERRUPT) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n");
+ TW_CLEAR_HOST_INTERRUPT(tw_dev);
+ }
+
+ /* Handle attention interrupt */
+ if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
+ TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
+ tw_state_request_start(tw_dev, &request_id);
+ error = tw_aen_read_queue(tw_dev, request_id);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Error reading aen queue.\n", tw_dev->host->host_no);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ }
+ }
+
+ /* Handle command interrupt */
+ if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
+ /* Drain as many pending commands as we can */
+ while (tw_dev->pending_request_count > 0) {
+ request_id = tw_dev->pending_queue[tw_dev->pending_head];
+ if (tw_dev->state[request_id] != TW_S_PENDING) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Found request id that wasn't pending.\n", tw_dev->host->host_no);
+ break;
+ }
+ if (tw_post_command_packet(tw_dev, request_id)==0) {
+ if (tw_dev->pending_head == TW_Q_LENGTH-1) {
+ tw_dev->pending_head = TW_Q_START;
+ } else {
+ tw_dev->pending_head = tw_dev->pending_head + 1;
+ }
+ tw_dev->pending_request_count--;
+ } else {
+ /* If we get here, we will continue re-posting on the next command interrupt */
+ break;
+ }
+ }
+ /* If there are no more pending requests, we mask command interrupt */
+ if (tw_dev->pending_request_count == 0)
+ TW_MASK_COMMAND_INTERRUPT(tw_dev);
+ }
+
+ /* Handle response interrupt */
+ if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
+ /* Drain the response queue from the board */
+ while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+ /* Read response queue register */
+ response_que.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+ request_id = TW_RESID_OUT(response_que.response_id);
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ error = 0;
+
+ /* Check for bad response */
+ if (command_packet->status != 0) {
+ /* If internal command, don't error, don't fill sense */
+ if (tw_dev->srb[request_id] == NULL) {
+ tw_decode_sense(tw_dev, request_id, 0);
+ } else {
+ error = tw_decode_sense(tw_dev, request_id, 1);
+ }
+ }
+
+ /* Check for correct state */
+ if (tw_dev->state[request_id] != TW_S_POSTED) {
+ if (tw_dev->srb[request_id] != NULL) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id that wasn't posted.\n", tw_dev->host->host_no);
+ error = 1;
+ }
+ }
+
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
+
+ /* Check for internal command completion */
+ if (tw_dev->srb[request_id] == NULL) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
+ /* Check for chrdev ioctl completion */
+ if (request_id != tw_dev->chrdev_request_id) {
+ retval = tw_aen_complete(tw_dev, request_id);
+ if (retval) {
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
+ }
+ } else {
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
+ }
+ } else {
+ switch (tw_dev->srb[request_id]->cmnd[0]) {
+ case READ_10:
+ case READ_6:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_10/READ_6\n");
+ break;
+ case WRITE_10:
+ case WRITE_6:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n");
+ break;
+ case TEST_UNIT_READY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TEST_UNIT_READY\n");
+ error = tw_scsiop_test_unit_ready_complete(tw_dev, request_id);
+ break;
+ case INQUIRY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
+ error = tw_scsiop_inquiry_complete(tw_dev, request_id);
+ break;
+ case READ_CAPACITY:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n");
+ error = tw_scsiop_read_capacity_complete(tw_dev, request_id);
+ break;
+ case MODE_SENSE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught MODE_SENSE\n");
+ error = tw_scsiop_mode_sense_complete(tw_dev, request_id);
+ break;
+ case SYNCHRONIZE_CACHE:
+ dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught SYNCHRONIZE_CACHE\n");
+ break;
+ default:
+ printk(KERN_WARNING "3w-xxxx: case slip in tw_interrupt()\n");
+ error = 1;
+ }
+
+ /* If no error command was a success */
+ if (error == 0) {
+ tw_dev->srb[request_id]->result = (DID_OK << 16);
+ }
+
+ /* If error, command failed */
+ if (error == 1) {
+ /* Ask for a host reset */
+ tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ }
+
+ /* Now complete the io */
+ if ((error != TW_ISR_DONT_COMPLETE)) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+
+ tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+ }
+ }
+
+ /* Check for valid status after each drain */
+ status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
+ if (tw_check_bits(status_reg_value)) {
+ dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+ if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
+ TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+ goto tw_interrupt_bail;
+ }
+ }
+ }
+ }
+
+tw_interrupt_bail:
+ spin_unlock(tw_dev->host->host_lock);
+ return IRQ_RETVAL(handled);
+} /* End tw_interrupt() */
+
+/* This function tells the controller to shut down */
+static void __tw_shutdown(TW_Device_Extension *tw_dev)
+{
+ /* Disable interrupts */
+ TW_DISABLE_INTERRUPTS(tw_dev);
+
+ printk(KERN_WARNING "3w-xxxx: Shutting down host %d.\n", tw_dev->host->host_no);
+
+ /* Tell the card we are shutting down */
+ if (tw_initconnection(tw_dev, 1)) {
+ printk(KERN_WARNING "3w-xxxx: Connection shutdown failed.\n");
+ } else {
+ printk(KERN_WARNING "3w-xxxx: Shutdown complete.\n");
+ }
+
+ /* Clear all interrupts just before exit */
+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+} /* End __tw_shutdown() */
+
+/* Wrapper for __tw_shutdown */
+static void tw_shutdown(struct device *dev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ __tw_shutdown(tw_dev);
+} /* End tw_shutdown() */
+
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .name = "3ware Storage Controller",
+ .queuecommand = tw_scsi_queue,
+ .eh_host_reset_handler = tw_scsi_eh_reset,
+ .bios_param = tw_scsi_biosparam,
+ .change_queue_depth = tw_change_queue_depth,
+ .can_queue = TW_Q_LENGTH-2,
+ .this_id = -1,
+ .sg_tablesize = TW_MAX_SGL_LENGTH,
+ .max_sectors = TW_MAX_SECTORS,
+ .cmd_per_lun = TW_MAX_CMDS_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = tw_host_attrs,
+ .emulated = 1
+};
+
+/* This function will probe and initialize a card */
+static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+{
+ struct Scsi_Host *host = NULL;
+ TW_Device_Extension *tw_dev;
+ int retval = -ENODEV;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ printk(KERN_WARNING "3w-xxxx: Failed to enable pci device.");
+ goto out_disable_device;
+ }
+
+ pci_set_master(pdev);
+
+ retval = pci_set_dma_mask(pdev, TW_DMA_MASK);
+ if (retval) {
+ printk(KERN_WARNING "3w-xxxx: Failed to set dma mask.");
+ goto out_disable_device;
+ }
+
+ host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
+ if (!host) {
+ printk(KERN_WARNING "3w-xxxx: Failed to allocate memory for device extension.");
+ retval = -ENOMEM;
+ goto out_disable_device;
+ }
+ tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ memset(tw_dev, 0, sizeof(TW_Device_Extension));
+
+ /* Save values to device extension */
+ tw_dev->host = host;
+ tw_dev->tw_pci_dev = pdev;
+
+ if (tw_initialize_device_extension(tw_dev)) {
+ printk(KERN_WARNING "3w-xxxx: Failed to initialize device extension.");
+ goto out_free_device_extension;
+ }
+
+ /* Request IO regions */
+ retval = pci_request_regions(pdev, "3w-xxxx");
+ if (retval) {
+ printk(KERN_WARNING "3w-xxxx: Failed to get mem region.");
+ goto out_free_device_extension;
+ }
+
+ /* Save base address */
+ tw_dev->base_addr = pci_resource_start(pdev, 0);
+ if (!tw_dev->base_addr) {
+ printk(KERN_WARNING "3w-xxxx: Failed to get io address.");
+ goto out_release_mem_region;
+ }
+
+ /* Disable interrupts on the card */
+ TW_DISABLE_INTERRUPTS(tw_dev);
+
+ /* Initialize the card */
+ if (tw_reset_sequence(tw_dev))
+ goto out_release_mem_region;
+
+ /* Set host specific parameters */
+ host->max_id = TW_MAX_UNITS;
+ host->max_cmd_len = TW_MAX_CDB_LEN;
+
+ /* Luns and channels aren't supported by adapter */
+ host->max_lun = 0;
+ host->max_channel = 0;
+
+ /* Register the card with the kernel SCSI layer */
+ retval = scsi_add_host(host, &pdev->dev);
+ if (retval) {
+ printk(KERN_WARNING "3w-xxxx: scsi add host failed");
+ goto out_release_mem_region;
+ }
+
+ pci_set_drvdata(pdev, host);
+
+ printk(KERN_WARNING "3w-xxxx: scsi%d: Found a 3ware Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, tw_dev->base_addr, pdev->irq);
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, tw_interrupt, SA_SHIRQ, "3w-xxxx", tw_dev);
+ if (retval) {
+ printk(KERN_WARNING "3w-xxxx: Error requesting IRQ.");
+ goto out_remove_host;
+ }
+
+ tw_device_extension_list[tw_device_extension_count] = tw_dev;
+ tw_device_extension_count++;
+
+ /* Re-enable interrupts on the card */
+ TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+ /* Finally, scan the host */
+ scsi_scan_host(host);
+
+ if (twe_major == -1) {
+ if ((twe_major = register_chrdev (0, "twe", &tw_fops)) < 0)
+ printk(KERN_WARNING "3w-xxxx: Failed to register character device.");
+ }
+ return 0;
+
+out_remove_host:
+ scsi_remove_host(host);
+out_release_mem_region:
+ pci_release_regions(pdev);
+out_free_device_extension:
+ tw_free_device_extension(tw_dev);
+ scsi_host_put(host);
+out_disable_device:
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End tw_probe() */
+
+/* This function is called to remove a device */
+static void tw_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ scsi_remove_host(tw_dev->host);
+
+ /* Unregister character device */
+ if (twe_major >= 0) {
+ unregister_chrdev(twe_major, "twe");
+ twe_major = -1;
+ }
+
+ /* Free up the IRQ */
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ /* Shutdown the card */
+ __tw_shutdown(tw_dev);
+
+ /* Free up the mem region */
+ pci_release_regions(pdev);
+
+ /* Free up device extension resources */
+ tw_free_device_extension(tw_dev);
+
+ scsi_host_put(tw_dev->host);
+ pci_disable_device(pdev);
+ tw_device_extension_count--;
+} /* End tw_remove() */
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id tw_pci_tbl[] __devinitdata = {
+ { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_1000,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_7000,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { }
+};
+MODULE_DEVICE_TABLE(pci, tw_pci_tbl);
+
+/* pci_driver initializer */
+static struct pci_driver tw_driver = {
+ .name = "3w-xxxx",
+ .id_table = tw_pci_tbl,
+ .probe = tw_probe,
+ .remove = tw_remove,
+ .driver = {
+ .shutdown = tw_shutdown
+ }
+};
+
+/* This function is called on driver initialization */
+static int __init tw_init(void)
+{
+ printk(KERN_WARNING "3ware Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
+
+ return pci_module_init(&tw_driver);
+} /* End tw_init() */
+
+/* This function is called on driver exit */
+static void __exit tw_exit(void)
+{
+ pci_unregister_driver(&tw_driver);
+} /* End tw_exit() */
+
+module_init(tw_init);
+module_exit(tw_exit);
+
diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
new file mode 100644
index 000000000000..98bad773f240
--- /dev/null
+++ b/drivers/scsi/3w-xxxx.h
@@ -0,0 +1,436 @@
+/*
+ 3w-xxxx.h -- 3ware Storage Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@amcc.com>
+ Modifications By: Joel Jacobson <linux@3ware.com>
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Brad Strand <linux@3ware.com>
+
+ Copyright (C) 1999-2005 3ware Inc.
+
+ Kernel compatiblity By: Andre Hedrick <andre@suse.com>
+ Non-Copyright (C) 2000 Andre Hedrick <andre@suse.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; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@amcc.com
+
+ For more information, goto:
+ http://www.amcc.com
+*/
+
+#ifndef _3W_XXXX_H
+#define _3W_XXXX_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+/* AEN strings */
+static char *tw_aen_string[] = {
+ [0x000] = "INFO: AEN queue empty",
+ [0x001] = "INFO: Soft reset occurred",
+ [0x002] = "ERROR: Unit degraded: Unit #",
+ [0x003] = "ERROR: Controller error",
+ [0x004] = "ERROR: Rebuild failed: Unit #",
+ [0x005] = "INFO: Rebuild complete: Unit #",
+ [0x006] = "ERROR: Incomplete unit detected: Unit #",
+ [0x007] = "INFO: Initialization complete: Unit #",
+ [0x008] = "WARNING: Unclean shutdown detected: Unit #",
+ [0x009] = "WARNING: ATA port timeout: Port #",
+ [0x00A] = "ERROR: Drive error: Port #",
+ [0x00B] = "INFO: Rebuild started: Unit #",
+ [0x00C] = "INFO: Initialization started: Unit #",
+ [0x00D] = "ERROR: Logical unit deleted: Unit #",
+ [0x00F] = "WARNING: SMART threshold exceeded: Port #",
+ [0x021] = "WARNING: ATA UDMA downgrade: Port #",
+ [0x021] = "WARNING: ATA UDMA upgrade: Port #",
+ [0x023] = "WARNING: Sector repair occurred: Port #",
+ [0x024] = "ERROR: SBUF integrity check failure",
+ [0x025] = "ERROR: Lost cached write: Port #",
+ [0x026] = "ERROR: Drive ECC error detected: Port #",
+ [0x027] = "ERROR: DCB checksum error: Port #",
+ [0x028] = "ERROR: DCB unsupported version: Port #",
+ [0x029] = "INFO: Verify started: Unit #",
+ [0x02A] = "ERROR: Verify failed: Port #",
+ [0x02B] = "INFO: Verify complete: Unit #",
+ [0x02C] = "WARNING: Overwrote bad sector during rebuild: Port #",
+ [0x02D] = "ERROR: Encountered bad sector during rebuild: Port #",
+ [0x02E] = "ERROR: Replacement drive is too small: Port #",
+ [0x02F] = "WARNING: Verify error: Unit not previously initialized: Unit #",
+ [0x030] = "ERROR: Drive not supported: Port #"
+};
+
+/*
+ Sense key lookup table
+ Format: ESDC/flags,SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier
+*/
+static unsigned char tw_sense_table[][4] =
+{
+ /* Codes for newer firmware */
+ // ATA Error SCSI Error
+ {0x01, 0x03, 0x13, 0x00}, // Address mark not found Address mark not found for data field
+ {0x04, 0x0b, 0x00, 0x00}, // Aborted command Aborted command
+ {0x10, 0x0b, 0x14, 0x00}, // ID not found Recorded entity not found
+ {0x40, 0x03, 0x11, 0x00}, // Uncorrectable ECC error Unrecovered read error
+ {0x61, 0x04, 0x00, 0x00}, // Device fault Hardware error
+ {0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error
+ {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command
+ {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command
+ {0x37, 0x02, 0x04, 0x00}, // Unit offline Not ready
+ {0x09, 0x02, 0x04, 0x00}, // Unrecovered disk error Not ready
+
+ /* Codes for older firmware */
+ // 3ware Error SCSI Error
+ {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command
+};
+
+/* Control register bit definitions */
+#define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000
+#define TW_CONTROL_CLEAR_ATTENTION_INTERRUPT 0x00040000
+#define TW_CONTROL_MASK_COMMAND_INTERRUPT 0x00020000
+#define TW_CONTROL_MASK_RESPONSE_INTERRUPT 0x00010000
+#define TW_CONTROL_UNMASK_COMMAND_INTERRUPT 0x00008000
+#define TW_CONTROL_UNMASK_RESPONSE_INTERRUPT 0x00004000
+#define TW_CONTROL_CLEAR_ERROR_STATUS 0x00000200
+#define TW_CONTROL_ISSUE_SOFT_RESET 0x00000100
+#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080
+#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040
+#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020
+#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
+#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
+#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008
+
+/* Status register bit definitions */
+#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000
+#define TW_STATUS_MINOR_VERSION_MASK 0x0F000000
+#define TW_STATUS_PCI_PARITY_ERROR 0x00800000
+#define TW_STATUS_QUEUE_ERROR 0x00400000
+#define TW_STATUS_MICROCONTROLLER_ERROR 0x00200000
+#define TW_STATUS_PCI_ABORT 0x00100000
+#define TW_STATUS_HOST_INTERRUPT 0x00080000
+#define TW_STATUS_ATTENTION_INTERRUPT 0x00040000
+#define TW_STATUS_COMMAND_INTERRUPT 0x00020000
+#define TW_STATUS_RESPONSE_INTERRUPT 0x00010000
+#define TW_STATUS_COMMAND_QUEUE_FULL 0x00008000
+#define TW_STATUS_RESPONSE_QUEUE_EMPTY 0x00004000
+#define TW_STATUS_MICROCONTROLLER_READY 0x00002000
+#define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000
+#define TW_STATUS_ALL_INTERRUPTS 0x000F0000
+#define TW_STATUS_CLEARABLE_BITS 0x00D00000
+#define TW_STATUS_EXPECTED_BITS 0x00002000
+#define TW_STATUS_UNEXPECTED_BITS 0x00F00008
+#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008
+#define TW_STATUS_VALID_INTERRUPT 0x00DF0008
+
+/* RESPONSE QUEUE BIT DEFINITIONS */
+#define TW_RESPONSE_ID_MASK 0x00000FF0
+
+/* PCI related defines */
+#define TW_IO_ADDRESS_RANGE 0x10
+#define TW_DEVICE_NAME "3ware Storage Controller"
+#define TW_VENDOR_ID (0x13C1) /* 3ware */
+#define TW_DEVICE_ID (0x1000) /* Storage Controller */
+#define TW_DEVICE_ID2 (0x1001) /* 7000 series controller */
+#define TW_NUMDEVICES 2
+#define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+#define TW_PCI_CLEAR_PCI_ABORT 0x2000
+
+/* Command packet opcodes */
+#define TW_OP_NOP 0x0
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_READ 0x2
+#define TW_OP_WRITE 0x3
+#define TW_OP_VERIFY 0x4
+#define TW_OP_GET_PARAM 0x12
+#define TW_OP_SET_PARAM 0x13
+#define TW_OP_SECTOR_INFO 0x1a
+#define TW_OP_AEN_LISTEN 0x1c
+#define TW_OP_FLUSH_CACHE 0x0e
+#define TW_CMD_PACKET 0x1d
+#define TW_CMD_PACKET_WITH_DATA 0x1f
+
+/* Asynchronous Event Notification (AEN) Codes */
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_DEGRADED_MIRROR 0x0002
+#define TW_AEN_CONTROLLER_ERROR 0x0003
+#define TW_AEN_REBUILD_FAIL 0x0004
+#define TW_AEN_REBUILD_DONE 0x0005
+#define TW_AEN_QUEUE_FULL 0x00ff
+#define TW_AEN_TABLE_UNDEFINED 0x15
+#define TW_AEN_APORT_TIMEOUT 0x0009
+#define TW_AEN_DRIVE_ERROR 0x000A
+#define TW_AEN_SMART_FAIL 0x000F
+#define TW_AEN_SBUF_FAIL 0x0024
+
+/* Phase defines */
+#define TW_PHASE_INITIAL 0
+#define TW_PHASE_SINGLE 1
+#define TW_PHASE_SGLIST 2
+
+/* Misc defines */
+#define TW_ALIGNMENT_6000 64 /* 64 bytes */
+#define TW_ALIGNMENT_7000 4 /* 4 bytes */
+#define TW_MAX_UNITS 16
+#define TW_COMMAND_ALIGNMENT_MASK 0x1ff
+#define TW_INIT_MESSAGE_CREDITS 0x100
+#define TW_INIT_COMMAND_PACKET_SIZE 0x3
+#define TW_POLL_MAX_RETRIES 20000
+#define TW_MAX_SGL_LENGTH 62
+#define TW_ATA_PASS_SGL_MAX 60
+#define TW_Q_LENGTH 256
+#define TW_Q_START 0
+#define TW_MAX_SLOT 32
+#define TW_MAX_PCI_BUSES 255
+#define TW_MAX_RESET_TRIES 3
+#define TW_UNIT_INFORMATION_TABLE_BASE 0x300
+#define TW_MAX_CMDS_PER_LUN 254 /* 254 for io, 1 for
+ chrdev ioctl, one for
+ internal aen post */
+#define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */
+#define TW_IOCTL 0x80
+#define TW_UNIT_ONLINE 1
+#define TW_IN_INTR 1
+#define TW_IN_RESET 2
+#define TW_IN_CHRDEV_IOCTL 3
+#define TW_MAX_SECTORS 256
+#define TW_MAX_IOCTL_SECTORS 512
+#define TW_AEN_WAIT_TIME 1000
+#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
+#define TW_ISR_DONT_COMPLETE 2
+#define TW_ISR_DONT_RESULT 3
+#define TW_IOCTL_TIMEOUT 25 /* 25 seconds */
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_DMA_MASK DMA_32BIT_MASK
+#define TW_MAX_CDB_LEN 16
+
+/* Bitmask macros to eliminate bitfields */
+
+/* opcode: 5, sgloffset: 3 */
+#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_SGL_OUT(x) ((x >> 5) & 0x7)
+
+/* reserved_1: 4, response_id: 8, reserved_2: 20 */
+#define TW_RESID_OUT(x) ((x >> 4) & 0xff)
+
+/* unit: 4, host_id: 4 */
+#define TW_UNITHOST_IN(x,y) ((x << 4) | ( y & 0xf))
+#define TW_UNIT_OUT(x) (x & 0xf)
+
+/* Macros */
+#define TW_CONTROL_REG_ADDR(x) (x->base_addr)
+#define TW_STATUS_REG_ADDR(x) (x->base_addr + 0x4)
+#define TW_COMMAND_QUEUE_REG_ADDR(x) (x->base_addr + 0x8)
+#define TW_RESPONSE_QUEUE_REG_ADDR(x) (x->base_addr + 0xC)
+#define TW_CLEAR_ALL_INTERRUPTS(x) (outl(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_ATTENTION_INTERRUPT(x) (outl(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_HOST_INTERRUPT(x) (outl(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_DISABLE_INTERRUPTS(x) (outl(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) (outl(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_MASK_COMMAND_INTERRUPT(x) (outl(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_UNMASK_COMMAND_INTERRUPT(x) (outl(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_SOFT_RESET(x) (outl(TW_CONTROL_ISSUE_SOFT_RESET | \
+ TW_CONTROL_CLEAR_HOST_INTERRUPT | \
+ TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
+ TW_CONTROL_MASK_COMMAND_INTERRUPT | \
+ TW_CONTROL_MASK_RESPONSE_INTERRUPT | \
+ TW_CONTROL_CLEAR_ERROR_STATUS | \
+ TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_STATUS_ERRORS(x) \
+ (((x & TW_STATUS_PCI_ABORT) || \
+ (x & TW_STATUS_PCI_PARITY_ERROR) || \
+ (x & TW_STATUS_QUEUE_ERROR) || \
+ (x & TW_STATUS_MICROCONTROLLER_ERROR)) && \
+ (x & TW_STATUS_MICROCONTROLLER_READY))
+
+#ifdef TW_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+#pragma pack(1)
+
+/* Scatter Gather List Entry */
+typedef struct TAG_TW_SG_Entry {
+ u32 address;
+ u32 length;
+} TW_SG_Entry;
+
+typedef unsigned char TW_Sector[512];
+
+/* Command Packet */
+typedef struct TW_Command {
+ unsigned char opcode__sgloffset;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char unit__hostid;
+ /* Second DWORD */
+ unsigned char status;
+ unsigned char flags;
+ union {
+ unsigned short block_count;
+ unsigned short parameter_count;
+ unsigned short message_credits;
+ } byte6;
+ union {
+ struct {
+ u32 lba;
+ TW_SG_Entry sgl[TW_MAX_SGL_LENGTH];
+ u32 padding; /* pad to 512 bytes */
+ } io;
+ struct {
+ TW_SG_Entry sgl[TW_MAX_SGL_LENGTH];
+ u32 padding[2];
+ } param;
+ struct {
+ u32 response_queue_pointer;
+ u32 padding[125];
+ } init_connection;
+ struct {
+ char version[504];
+ } ioctl_miniport_version;
+ } byte8;
+} TW_Command;
+
+#pragma pack()
+
+typedef struct TAG_TW_Ioctl {
+ unsigned char opcode;
+ unsigned short table_id;
+ unsigned char parameter_id;
+ unsigned char parameter_size_bytes;
+ unsigned char unit_index;
+ unsigned char data[1];
+} TW_Ioctl;
+
+#pragma pack(1)
+
+/* Structure for new chardev ioctls */
+typedef struct TAG_TW_New_Ioctl {
+ unsigned int data_buffer_length;
+ unsigned char padding [508];
+ TW_Command firmware_command;
+ char data_buffer[1];
+} TW_New_Ioctl;
+
+/* GetParam descriptor */
+typedef struct {
+ unsigned short table_id;
+ unsigned char parameter_id;
+ unsigned char parameter_size_bytes;
+ unsigned char data[1];
+} TW_Param, *PTW_Param;
+
+/* Response queue */
+typedef union TAG_TW_Response_Queue {
+ u32 response_id;
+ u32 value;
+} TW_Response_Queue;
+
+typedef int TW_Cmd_State;
+
+#define TW_S_INITIAL 0x1 /* Initial state */
+#define TW_S_STARTED 0x2 /* Id in use */
+#define TW_S_POSTED 0x4 /* Posted to the controller */
+#define TW_S_PENDING 0x8 /* Waiting to be posted in isr */
+#define TW_S_COMPLETED 0x10 /* Completed by isr */
+#define TW_S_FINISHED 0x20 /* I/O completely done */
+#define TW_START_MASK (TW_S_STARTED | TW_S_POSTED | TW_S_PENDING | TW_S_COMPLETED)
+
+/* Command header for ATA pass-thru */
+typedef struct TAG_TW_Passthru
+{
+ unsigned char opcode__sgloffset;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char aport__hostid;
+ unsigned char status;
+ unsigned char flags;
+ unsigned short param;
+ unsigned short features;
+ unsigned short sector_count;
+ unsigned short sector_num;
+ unsigned short cylinder_lo;
+ unsigned short cylinder_hi;
+ unsigned char drive_head;
+ unsigned char command;
+ TW_SG_Entry sg_list[TW_ATA_PASS_SGL_MAX];
+ unsigned char padding[12];
+} TW_Passthru;
+
+typedef struct TAG_TW_Device_Extension {
+ u32 base_addr;
+ unsigned long *alignment_virtual_address[TW_Q_LENGTH];
+ unsigned long alignment_physical_address[TW_Q_LENGTH];
+ int is_unit_present[TW_MAX_UNITS];
+ unsigned long *command_packet_virtual_address[TW_Q_LENGTH];
+ unsigned long command_packet_physical_address[TW_Q_LENGTH];
+ struct pci_dev *tw_pci_dev;
+ struct scsi_cmnd *srb[TW_Q_LENGTH];
+ unsigned char free_queue[TW_Q_LENGTH];
+ unsigned char free_head;
+ unsigned char free_tail;
+ unsigned char pending_queue[TW_Q_LENGTH];
+ unsigned char pending_head;
+ unsigned char pending_tail;
+ TW_Cmd_State state[TW_Q_LENGTH];
+ u32 posted_request_count;
+ u32 max_posted_request_count;
+ u32 request_count_marked_pending;
+ u32 pending_request_count;
+ u32 max_pending_request_count;
+ u32 max_sgl_entries;
+ u32 sgl_entries;
+ u32 num_resets;
+ u32 sector_count;
+ u32 max_sector_count;
+ u32 aen_count;
+ struct Scsi_Host *host;
+ struct semaphore ioctl_sem;
+ unsigned short aen_queue[TW_Q_LENGTH];
+ unsigned char aen_head;
+ unsigned char aen_tail;
+ volatile long flags; /* long req'd for set_bit --RR */
+ int reset_print;
+ volatile int chrdev_request_id;
+ wait_queue_head_t ioctl_wqueue;
+} TW_Device_Extension;
+
+#pragma pack()
+
+#endif /* _3W_XXXX_H */
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
new file mode 100644
index 000000000000..a591fcb8aab1
--- /dev/null
+++ b/drivers/scsi/53c700.c
@@ -0,0 +1,2175 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* NCR (or Symbios) 53c700 and 53c700-66 Driver
+ *
+ * Copyright (C) 2001 by James.Bottomley@HansenPartnership.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+ */
+
+/* Notes:
+ *
+ * This driver is designed exclusively for these chips (virtually the
+ * earliest of the scripts engine chips). They need their own drivers
+ * because they are missing so many of the scripts and snazzy register
+ * features of their elder brothers (the 710, 720 and 770).
+ *
+ * The 700 is the lowliest of the line, it can only do async SCSI.
+ * The 700-66 can at least do synchronous SCSI up to 10MHz.
+ *
+ * The 700 chip has no host bus interface logic of its own. However,
+ * it is usually mapped to a location with well defined register
+ * offsets. Therefore, if you can determine the base address and the
+ * irq your board incorporating this chip uses, you can probably use
+ * this driver to run it (although you'll probably have to write a
+ * minimal wrapper for the purpose---see the NCR_D700 driver for
+ * details about how to do this).
+ *
+ *
+ * TODO List:
+ *
+ * 1. Better statistics in the proc fs
+ *
+ * 2. Implement message queue (queues SCSI messages like commands) and make
+ * the abort and device reset functions use them.
+ * */
+
+/* CHANGELOG
+ *
+ * Version 2.8
+ *
+ * Fixed bad bug affecting tag starvation processing (previously the
+ * driver would hang the system if too many tags starved. Also fixed
+ * bad bug having to do with 10 byte command processing and REQUEST
+ * SENSE (the command would loop forever getting a transfer length
+ * mismatch in the CMD phase).
+ *
+ * Version 2.7
+ *
+ * Fixed scripts problem which caused certain devices (notably CDRWs)
+ * to hang on initial INQUIRY. Updated NCR_700_readl/writel to use
+ * __raw_readl/writel for parisc compatibility (Thomas
+ * Bogendoerfer). Added missing SCp->request_bufflen initialisation
+ * for sense requests (Ryan Bradetich).
+ *
+ * Version 2.6
+ *
+ * Following test of the 64 bit parisc kernel by Richard Hirst,
+ * several problems have now been corrected. Also adds support for
+ * consistent memory allocation.
+ *
+ * Version 2.5
+ *
+ * More Compatibility changes for 710 (now actually works). Enhanced
+ * support for odd clock speeds which constrain SDTR negotiations.
+ * correct cacheline separation for scsi messages and status for
+ * incoherent architectures. Use of the pci mapping functions on
+ * buffers to begin support for 64 bit drivers.
+ *
+ * Version 2.4
+ *
+ * Added support for the 53c710 chip (in 53c700 emulation mode only---no
+ * special 53c710 instructions or registers are used).
+ *
+ * Version 2.3
+ *
+ * More endianness/cache coherency changes.
+ *
+ * Better bad device handling (handles devices lying about tag
+ * queueing support and devices which fail to provide sense data on
+ * contingent allegiance conditions)
+ *
+ * Many thanks to Richard Hirst <rhirst@linuxcare.com> for patiently
+ * debugging this driver on the parisc architecture and suggesting
+ * many improvements and bug fixes.
+ *
+ * Thanks also go to Linuxcare Inc. for providing several PARISC
+ * machines for me to debug the driver on.
+ *
+ * Version 2.2
+ *
+ * Made the driver mem or io mapped; added endian invariance; added
+ * dma cache flushing operations for architectures which need it;
+ * added support for more varied clocking speeds.
+ *
+ * Version 2.1
+ *
+ * Initial modularisation from the D700. See NCR_D700.c for the rest of
+ * the changelog.
+ * */
+#define NCR_700_VERSION "2.8"
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/byteorder.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "53c700.h"
+
+/* NOTE: For 64 bit drivers there are points in the code where we use
+ * a non dereferenceable pointer to point to a structure in dma-able
+ * memory (which is 32 bits) so that we can use all of the structure
+ * operations but take the address at the end. This macro allows us
+ * to truncate the 64 bit pointer down to 32 bits without the compiler
+ * complaining */
+#define to32bit(x) ((__u32)((unsigned long)(x)))
+
+#ifdef NCR_700_DEBUG
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+MODULE_AUTHOR("James Bottomley");
+MODULE_DESCRIPTION("53c700 and 53c700-66 Driver");
+MODULE_LICENSE("GPL");
+
+/* This is the script */
+#include "53c700_d.h"
+
+
+STATIC int NCR_700_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
+STATIC int NCR_700_abort(struct scsi_cmnd * SCpnt);
+STATIC int NCR_700_bus_reset(struct scsi_cmnd * SCpnt);
+STATIC int NCR_700_dev_reset(struct scsi_cmnd * SCpnt);
+STATIC int NCR_700_host_reset(struct scsi_cmnd * SCpnt);
+STATIC void NCR_700_chip_setup(struct Scsi_Host *host);
+STATIC void NCR_700_chip_reset(struct Scsi_Host *host);
+STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt);
+STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt);
+static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth);
+static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth);
+
+STATIC struct device_attribute *NCR_700_dev_attrs[];
+
+STATIC struct scsi_transport_template *NCR_700_transport_template = NULL;
+
+static char *NCR_700_phase[] = {
+ "",
+ "after selection",
+ "before command phase",
+ "after command phase",
+ "after status phase",
+ "after data in phase",
+ "after data out phase",
+ "during data phase",
+};
+
+static char *NCR_700_condition[] = {
+ "",
+ "NOT MSG_OUT",
+ "UNEXPECTED PHASE",
+ "NOT MSG_IN",
+ "UNEXPECTED MSG",
+ "MSG_IN",
+ "SDTR_MSG RECEIVED",
+ "REJECT_MSG RECEIVED",
+ "DISCONNECT_MSG RECEIVED",
+ "MSG_OUT",
+ "DATA_IN",
+
+};
+
+static char *NCR_700_fatal_messages[] = {
+ "unexpected message after reselection",
+ "still MSG_OUT after message injection",
+ "not MSG_IN after selection",
+ "Illegal message length received",
+};
+
+static char *NCR_700_SBCL_bits[] = {
+ "IO ",
+ "CD ",
+ "MSG ",
+ "ATN ",
+ "SEL ",
+ "BSY ",
+ "ACK ",
+ "REQ ",
+};
+
+static char *NCR_700_SBCL_to_phase[] = {
+ "DATA_OUT",
+ "DATA_IN",
+ "CMD_OUT",
+ "STATE",
+ "ILLEGAL PHASE",
+ "ILLEGAL PHASE",
+ "MSG OUT",
+ "MSG IN",
+};
+
+static __u8 NCR_700_SDTR_msg[] = {
+ 0x01, /* Extended message */
+ 0x03, /* Extended message Length */
+ 0x01, /* SDTR Extended message */
+ NCR_700_MIN_PERIOD,
+ NCR_700_MAX_OFFSET
+};
+
+/* This translates the SDTR message offset and period to a value
+ * which can be loaded into the SXFER_REG.
+ *
+ * NOTE: According to SCSI-2, the true transfer period (in ns) is
+ * actually four times this period value */
+static inline __u8
+NCR_700_offset_period_to_sxfer(struct NCR_700_Host_Parameters *hostdata,
+ __u8 offset, __u8 period)
+{
+ int XFERP;
+
+ __u8 min_xferp = (hostdata->chip710
+ ? NCR_710_MIN_XFERP : NCR_700_MIN_XFERP);
+ __u8 max_offset = (hostdata->chip710
+ ? NCR_710_MAX_OFFSET : NCR_700_MAX_OFFSET);
+
+ if(offset == 0)
+ return 0;
+
+ if(period < hostdata->min_period) {
+ printk(KERN_WARNING "53c700: Period %dns is less than this chip's minimum, setting to %d\n", period*4, NCR_700_SDTR_msg[3]*4);
+ period = hostdata->min_period;
+ }
+ XFERP = (period*4 * hostdata->sync_clock)/1000 - 4;
+ if(offset > max_offset) {
+ printk(KERN_WARNING "53c700: Offset %d exceeds chip maximum, setting to %d\n",
+ offset, max_offset);
+ offset = max_offset;
+ }
+ if(XFERP < min_xferp) {
+ printk(KERN_WARNING "53c700: XFERP %d is less than minium, setting to %d\n",
+ XFERP, min_xferp);
+ XFERP = min_xferp;
+ }
+ return (offset & 0x0f) | (XFERP & 0x07)<<4;
+}
+
+static inline __u8
+NCR_700_get_SXFER(struct scsi_device *SDp)
+{
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0];
+
+ return NCR_700_offset_period_to_sxfer(hostdata,
+ spi_offset(SDp->sdev_target),
+ spi_period(SDp->sdev_target));
+}
+
+struct Scsi_Host *
+NCR_700_detect(struct scsi_host_template *tpnt,
+ struct NCR_700_Host_Parameters *hostdata, struct device *dev)
+{
+ dma_addr_t pScript, pSlots;
+ __u8 *memory;
+ __u32 *script;
+ struct Scsi_Host *host;
+ static int banner = 0;
+ int j;
+
+ if(tpnt->sdev_attrs == NULL)
+ tpnt->sdev_attrs = NCR_700_dev_attrs;
+
+ memory = dma_alloc_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
+ &pScript, GFP_KERNEL);
+ if(memory == NULL) {
+ printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n");
+ return NULL;
+ }
+
+ script = (__u32 *)memory;
+ hostdata->msgin = memory + MSGIN_OFFSET;
+ hostdata->msgout = memory + MSGOUT_OFFSET;
+ hostdata->status = memory + STATUS_OFFSET;
+ /* all of these offsets are L1_CACHE_BYTES separated. It is fatal
+ * if this isn't sufficient separation to avoid dma flushing issues */
+ BUG_ON(!dma_is_consistent(pScript) && L1_CACHE_BYTES < dma_get_cache_alignment());
+ hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET);
+ hostdata->dev = dev;
+
+ pSlots = pScript + SLOTS_OFFSET;
+
+ /* Fill in the missing routines from the host template */
+ tpnt->queuecommand = NCR_700_queuecommand;
+ tpnt->eh_abort_handler = NCR_700_abort;
+ tpnt->eh_device_reset_handler = NCR_700_dev_reset;
+ tpnt->eh_bus_reset_handler = NCR_700_bus_reset;
+ tpnt->eh_host_reset_handler = NCR_700_host_reset;
+ tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST;
+ tpnt->sg_tablesize = NCR_700_SG_SEGMENTS;
+ tpnt->cmd_per_lun = NCR_700_CMD_PER_LUN;
+ tpnt->use_clustering = ENABLE_CLUSTERING;
+ tpnt->slave_configure = NCR_700_slave_configure;
+ tpnt->slave_destroy = NCR_700_slave_destroy;
+ tpnt->change_queue_depth = NCR_700_change_queue_depth;
+ tpnt->change_queue_type = NCR_700_change_queue_type;
+
+ if(tpnt->name == NULL)
+ tpnt->name = "53c700";
+ if(tpnt->proc_name == NULL)
+ tpnt->proc_name = "53c700";
+
+
+ host = scsi_host_alloc(tpnt, 4);
+ if (!host)
+ return NULL;
+ memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot)
+ * NCR_700_COMMAND_SLOTS_PER_HOST);
+ for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) {
+ dma_addr_t offset = (dma_addr_t)((unsigned long)&hostdata->slots[j].SG[0]
+ - (unsigned long)&hostdata->slots[0].SG[0]);
+ hostdata->slots[j].pSG = (struct NCR_700_SG_List *)((unsigned long)(pSlots + offset));
+ if(j == 0)
+ hostdata->free_list = &hostdata->slots[j];
+ else
+ hostdata->slots[j-1].ITL_forw = &hostdata->slots[j];
+ hostdata->slots[j].state = NCR_700_SLOT_FREE;
+ }
+
+ for(j = 0; j < sizeof(SCRIPT)/sizeof(SCRIPT[0]); j++) {
+ script[j] = bS_to_host(SCRIPT[j]);
+ }
+
+ /* adjust all labels to be bus physical */
+ for(j = 0; j < PATCHES; j++) {
+ script[LABELPATCHES[j]] = bS_to_host(pScript + SCRIPT[LABELPATCHES[j]]);
+ }
+ /* now patch up fixed addresses. */
+ script_patch_32(script, MessageLocation,
+ pScript + MSGOUT_OFFSET);
+ script_patch_32(script, StatusAddress,
+ pScript + STATUS_OFFSET);
+ script_patch_32(script, ReceiveMsgAddress,
+ pScript + MSGIN_OFFSET);
+
+ hostdata->script = script;
+ hostdata->pScript = pScript;
+ dma_sync_single_for_device(hostdata->dev, pScript, sizeof(SCRIPT), DMA_TO_DEVICE);
+ hostdata->state = NCR_700_HOST_FREE;
+ hostdata->cmd = NULL;
+ host->max_id = 7;
+ host->max_lun = NCR_700_MAX_LUNS;
+ BUG_ON(NCR_700_transport_template == NULL);
+ host->transportt = NCR_700_transport_template;
+ host->unique_id = hostdata->base;
+ host->base = hostdata->base;
+ hostdata->eh_complete = NULL;
+ host->hostdata[0] = (unsigned long)hostdata;
+ /* kick the chip */
+ NCR_700_writeb(0xff, host, CTEST9_REG);
+ if(hostdata->chip710)
+ hostdata->rev = (NCR_700_readb(host, CTEST8_REG)>>4) & 0x0f;
+ else
+ hostdata->rev = (NCR_700_readb(host, CTEST7_REG)>>4) & 0x0f;
+ hostdata->fast = (NCR_700_readb(host, CTEST9_REG) == 0);
+ if(banner == 0) {
+ printk(KERN_NOTICE "53c700: Version " NCR_700_VERSION " By James.Bottomley@HansenPartnership.com\n");
+ banner = 1;
+ }
+ printk(KERN_NOTICE "scsi%d: %s rev %d %s\n", host->host_no,
+ hostdata->chip710 ? "53c710" :
+ (hostdata->fast ? "53c700-66" : "53c700"),
+ hostdata->rev, hostdata->differential ?
+ "(Differential)" : "");
+ /* reset the chip */
+ NCR_700_chip_reset(host);
+
+ if (scsi_add_host(host, dev)) {
+ dev_printk(KERN_ERR, dev, "53c700: scsi_add_host failed\n");
+ scsi_host_put(host);
+ return NULL;
+ }
+
+ spi_signalling(host) = hostdata->differential ? SPI_SIGNAL_HVD :
+ SPI_SIGNAL_SE;
+
+ return host;
+}
+
+int
+NCR_700_release(struct Scsi_Host *host)
+{
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ dma_free_noncoherent(hostdata->dev, TOTAL_MEM_SIZE,
+ hostdata->script, hostdata->pScript);
+ return 1;
+}
+
+static inline __u8
+NCR_700_identify(int can_disconnect, __u8 lun)
+{
+ return IDENTIFY_BASE |
+ ((can_disconnect) ? 0x40 : 0) |
+ (lun & NCR_700_LUN_MASK);
+}
+
+/*
+ * Function : static int data_residual (Scsi_Host *host)
+ *
+ * Purpose : return residual data count of what's in the chip. If you
+ * really want to know what this function is doing, it's almost a
+ * direct transcription of the algorithm described in the 53c710
+ * guide, except that the DBC and DFIFO registers are only 6 bits
+ * wide on a 53c700.
+ *
+ * Inputs : host - SCSI host */
+static inline int
+NCR_700_data_residual (struct Scsi_Host *host) {
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ int count, synchronous = 0;
+ unsigned int ddir;
+
+ if(hostdata->chip710) {
+ count = ((NCR_700_readb(host, DFIFO_REG) & 0x7f) -
+ (NCR_700_readl(host, DBC_REG) & 0x7f)) & 0x7f;
+ } else {
+ count = ((NCR_700_readb(host, DFIFO_REG) & 0x3f) -
+ (NCR_700_readl(host, DBC_REG) & 0x3f)) & 0x3f;
+ }
+
+ if(hostdata->fast)
+ synchronous = NCR_700_readb(host, SXFER_REG) & 0x0f;
+
+ /* get the data direction */
+ ddir = NCR_700_readb(host, CTEST0_REG) & 0x01;
+
+ if (ddir) {
+ /* Receive */
+ if (synchronous)
+ count += (NCR_700_readb(host, SSTAT2_REG) & 0xf0) >> 4;
+ else
+ if (NCR_700_readb(host, SSTAT1_REG) & SIDL_REG_FULL)
+ ++count;
+ } else {
+ /* Send */
+ __u8 sstat = NCR_700_readb(host, SSTAT1_REG);
+ if (sstat & SODL_REG_FULL)
+ ++count;
+ if (synchronous && (sstat & SODR_REG_FULL))
+ ++count;
+ }
+#ifdef NCR_700_DEBUG
+ if(count)
+ printk("RESIDUAL IS %d (ddir %d)\n", count, ddir);
+#endif
+ return count;
+}
+
+/* print out the SCSI wires and corresponding phase from the SBCL register
+ * in the chip */
+static inline char *
+sbcl_to_string(__u8 sbcl)
+{
+ int i;
+ static char ret[256];
+
+ ret[0]='\0';
+ for(i=0; i<8; i++) {
+ if((1<<i) & sbcl)
+ strcat(ret, NCR_700_SBCL_bits[i]);
+ }
+ strcat(ret, NCR_700_SBCL_to_phase[sbcl & 0x07]);
+ return ret;
+}
+
+static inline __u8
+bitmap_to_number(__u8 bitmap)
+{
+ __u8 i;
+
+ for(i=0; i<8 && !(bitmap &(1<<i)); i++)
+ ;
+ return i;
+}
+
+/* Pull a slot off the free list */
+STATIC struct NCR_700_command_slot *
+find_empty_slot(struct NCR_700_Host_Parameters *hostdata)
+{
+ struct NCR_700_command_slot *slot = hostdata->free_list;
+
+ if(slot == NULL) {
+ /* sanity check */
+ if(hostdata->command_slot_count != NCR_700_COMMAND_SLOTS_PER_HOST)
+ printk(KERN_ERR "SLOTS FULL, but count is %d, should be %d\n", hostdata->command_slot_count, NCR_700_COMMAND_SLOTS_PER_HOST);
+ return NULL;
+ }
+
+ if(slot->state != NCR_700_SLOT_FREE)
+ /* should panic! */
+ printk(KERN_ERR "BUSY SLOT ON FREE LIST!!!\n");
+
+
+ hostdata->free_list = slot->ITL_forw;
+ slot->ITL_forw = NULL;
+
+
+ /* NOTE: set the state to busy here, not queued, since this
+ * indicates the slot is in use and cannot be run by the IRQ
+ * finish routine. If we cannot queue the command when it
+ * is properly build, we then change to NCR_700_SLOT_QUEUED */
+ slot->state = NCR_700_SLOT_BUSY;
+ hostdata->command_slot_count++;
+
+ return slot;
+}
+
+STATIC void
+free_slot(struct NCR_700_command_slot *slot,
+ struct NCR_700_Host_Parameters *hostdata)
+{
+ if((slot->state & NCR_700_SLOT_MASK) != NCR_700_SLOT_MAGIC) {
+ printk(KERN_ERR "53c700: SLOT %p is not MAGIC!!!\n", slot);
+ }
+ if(slot->state == NCR_700_SLOT_FREE) {
+ printk(KERN_ERR "53c700: SLOT %p is FREE!!!\n", slot);
+ }
+
+ slot->resume_offset = 0;
+ slot->cmnd = NULL;
+ slot->state = NCR_700_SLOT_FREE;
+ slot->ITL_forw = hostdata->free_list;
+ hostdata->free_list = slot;
+ hostdata->command_slot_count--;
+}
+
+
+/* This routine really does very little. The command is indexed on
+ the ITL and (if tagged) the ITLQ lists in _queuecommand */
+STATIC void
+save_for_reselection(struct NCR_700_Host_Parameters *hostdata,
+ struct scsi_cmnd *SCp, __u32 dsp)
+{
+ /* Its just possible that this gets executed twice */
+ if(SCp != NULL) {
+ struct NCR_700_command_slot *slot =
+ (struct NCR_700_command_slot *)SCp->host_scribble;
+
+ slot->resume_offset = dsp;
+ }
+ hostdata->state = NCR_700_HOST_FREE;
+ hostdata->cmd = NULL;
+}
+
+STATIC inline void
+NCR_700_unmap(struct NCR_700_Host_Parameters *hostdata, struct scsi_cmnd *SCp,
+ struct NCR_700_command_slot *slot)
+{
+ if(SCp->sc_data_direction != DMA_NONE &&
+ SCp->sc_data_direction != DMA_BIDIRECTIONAL) {
+ if(SCp->use_sg) {
+ dma_unmap_sg(hostdata->dev, SCp->buffer,
+ SCp->use_sg, SCp->sc_data_direction);
+ } else {
+ dma_unmap_single(hostdata->dev, slot->dma_handle,
+ SCp->request_bufflen,
+ SCp->sc_data_direction);
+ }
+ }
+}
+
+STATIC inline void
+NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata,
+ struct scsi_cmnd *SCp, int result)
+{
+ hostdata->state = NCR_700_HOST_FREE;
+ hostdata->cmd = NULL;
+
+ if(SCp != NULL) {
+ struct NCR_700_command_slot *slot =
+ (struct NCR_700_command_slot *)SCp->host_scribble;
+
+ NCR_700_unmap(hostdata, SCp, slot);
+ dma_unmap_single(hostdata->dev, slot->pCmd,
+ sizeof(SCp->cmnd), DMA_TO_DEVICE);
+ if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == NCR_700_INTERNAL_SENSE_MAGIC) {
+#ifdef NCR_700_DEBUG
+ printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is\n",
+ SCp, SCp->cmnd[7], result);
+ scsi_print_sense("53c700", SCp);
+
+#endif
+ /* restore the old result if the request sense was
+ * successful */
+ if(result == 0)
+ result = SCp->cmnd[7];
+ /* now restore the original command */
+ memcpy((void *) SCp->cmnd, (void *) SCp->data_cmnd,
+ sizeof(SCp->data_cmnd));
+ SCp->request_buffer = SCp->buffer;
+ SCp->request_bufflen = SCp->bufflen;
+ SCp->use_sg = SCp->old_use_sg;
+ SCp->cmd_len = SCp->old_cmd_len;
+ SCp->sc_data_direction = SCp->sc_old_data_direction;
+ SCp->underflow = SCp->old_underflow;
+
+ }
+ free_slot(slot, hostdata);
+#ifdef NCR_700_DEBUG
+ if(NCR_700_get_depth(SCp->device) == 0 ||
+ NCR_700_get_depth(SCp->device) > SCp->device->queue_depth)
+ printk(KERN_ERR "Invalid depth in NCR_700_scsi_done(): %d\n",
+ NCR_700_get_depth(SCp->device));
+#endif /* NCR_700_DEBUG */
+ NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) - 1);
+
+ SCp->host_scribble = NULL;
+ SCp->result = result;
+ SCp->scsi_done(SCp);
+ } else {
+ printk(KERN_ERR "53c700: SCSI DONE HAS NULL SCp\n");
+ }
+}
+
+
+STATIC void
+NCR_700_internal_bus_reset(struct Scsi_Host *host)
+{
+ /* Bus reset */
+ NCR_700_writeb(ASSERT_RST, host, SCNTL1_REG);
+ udelay(50);
+ NCR_700_writeb(0, host, SCNTL1_REG);
+
+}
+
+STATIC void
+NCR_700_chip_setup(struct Scsi_Host *host)
+{
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ __u32 dcntl_extra = 0;
+ __u8 min_period;
+ __u8 min_xferp = (hostdata->chip710 ? NCR_710_MIN_XFERP : NCR_700_MIN_XFERP);
+
+ if(hostdata->chip710) {
+ __u8 burst_disable = hostdata->burst_disable
+ ? BURST_DISABLE : 0;
+ dcntl_extra = COMPAT_700_MODE;
+
+ NCR_700_writeb(dcntl_extra, host, DCNTL_REG);
+ NCR_700_writeb(BURST_LENGTH_8 | hostdata->dmode_extra,
+ host, DMODE_710_REG);
+ NCR_700_writeb(burst_disable | (hostdata->differential ?
+ DIFF : 0), host, CTEST7_REG);
+ NCR_700_writeb(BTB_TIMER_DISABLE, host, CTEST0_REG);
+ NCR_700_writeb(FULL_ARBITRATION | ENABLE_PARITY | PARITY
+ | AUTO_ATN, host, SCNTL0_REG);
+ } else {
+ NCR_700_writeb(BURST_LENGTH_8 | hostdata->dmode_extra,
+ host, DMODE_700_REG);
+ NCR_700_writeb(hostdata->differential ?
+ DIFF : 0, host, CTEST7_REG);
+ if(hostdata->fast) {
+ /* this is for 700-66, does nothing on 700 */
+ NCR_700_writeb(LAST_DIS_ENBL | ENABLE_ACTIVE_NEGATION
+ | GENERATE_RECEIVE_PARITY, host,
+ CTEST8_REG);
+ } else {
+ NCR_700_writeb(FULL_ARBITRATION | ENABLE_PARITY
+ | PARITY | AUTO_ATN, host, SCNTL0_REG);
+ }
+ }
+
+ NCR_700_writeb(1 << host->this_id, host, SCID_REG);
+ NCR_700_writeb(0, host, SBCL_REG);
+ NCR_700_writeb(ASYNC_OPERATION, host, SXFER_REG);
+
+ NCR_700_writeb(PHASE_MM_INT | SEL_TIMEOUT_INT | GROSS_ERR_INT | UX_DISC_INT
+ | RST_INT | PAR_ERR_INT | SELECT_INT, host, SIEN_REG);
+
+ NCR_700_writeb(ABORT_INT | INT_INST_INT | ILGL_INST_INT, host, DIEN_REG);
+ NCR_700_writeb(ENABLE_SELECT, host, SCNTL1_REG);
+ if(hostdata->clock > 75) {
+ printk(KERN_ERR "53c700: Clock speed %dMHz is too high: 75Mhz is the maximum this chip can be driven at\n", hostdata->clock);
+ /* do the best we can, but the async clock will be out
+ * of spec: sync divider 2, async divider 3 */
+ DEBUG(("53c700: sync 2 async 3\n"));
+ NCR_700_writeb(SYNC_DIV_2_0, host, SBCL_REG);
+ NCR_700_writeb(ASYNC_DIV_3_0 | dcntl_extra, host, DCNTL_REG);
+ hostdata->sync_clock = hostdata->clock/2;
+ } else if(hostdata->clock > 50 && hostdata->clock <= 75) {
+ /* sync divider 1.5, async divider 3 */
+ DEBUG(("53c700: sync 1.5 async 3\n"));
+ NCR_700_writeb(SYNC_DIV_1_5, host, SBCL_REG);
+ NCR_700_writeb(ASYNC_DIV_3_0 | dcntl_extra, host, DCNTL_REG);
+ hostdata->sync_clock = hostdata->clock*2;
+ hostdata->sync_clock /= 3;
+
+ } else if(hostdata->clock > 37 && hostdata->clock <= 50) {
+ /* sync divider 1, async divider 2 */
+ DEBUG(("53c700: sync 1 async 2\n"));
+ NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG);
+ NCR_700_writeb(ASYNC_DIV_2_0 | dcntl_extra, host, DCNTL_REG);
+ hostdata->sync_clock = hostdata->clock;
+ } else if(hostdata->clock > 25 && hostdata->clock <=37) {
+ /* sync divider 1, async divider 1.5 */
+ DEBUG(("53c700: sync 1 async 1.5\n"));
+ NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG);
+ NCR_700_writeb(ASYNC_DIV_1_5 | dcntl_extra, host, DCNTL_REG);
+ hostdata->sync_clock = hostdata->clock;
+ } else {
+ DEBUG(("53c700: sync 1 async 1\n"));
+ NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG);
+ NCR_700_writeb(ASYNC_DIV_1_0 | dcntl_extra, host, DCNTL_REG);
+ /* sync divider 1, async divider 1 */
+ hostdata->sync_clock = hostdata->clock;
+ }
+ /* Calculate the actual minimum period that can be supported
+ * by our synchronous clock speed. See the 710 manual for
+ * exact details of this calculation which is based on a
+ * setting of the SXFER register */
+ min_period = 1000*(4+min_xferp)/(4*hostdata->sync_clock);
+ hostdata->min_period = NCR_700_MIN_PERIOD;
+ if(min_period > NCR_700_MIN_PERIOD)
+ hostdata->min_period = min_period;
+}
+
+STATIC void
+NCR_700_chip_reset(struct Scsi_Host *host)
+{
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ if(hostdata->chip710) {
+ NCR_700_writeb(SOFTWARE_RESET_710, host, ISTAT_REG);
+ udelay(100);
+
+ NCR_700_writeb(0, host, ISTAT_REG);
+ } else {
+ NCR_700_writeb(SOFTWARE_RESET, host, DCNTL_REG);
+ udelay(100);
+
+ NCR_700_writeb(0, host, DCNTL_REG);
+ }
+
+ mdelay(1000);
+
+ NCR_700_chip_setup(host);
+}
+
+/* The heart of the message processing engine is that the instruction
+ * immediately after the INT is the normal case (and so must be CLEAR
+ * ACK). If we want to do something else, we call that routine in
+ * scripts and set temp to be the normal case + 8 (skipping the CLEAR
+ * ACK) so that the routine returns correctly to resume its activity
+ * */
+STATIC __u32
+process_extended_message(struct Scsi_Host *host,
+ struct NCR_700_Host_Parameters *hostdata,
+ struct scsi_cmnd *SCp, __u32 dsp, __u32 dsps)
+{
+ __u32 resume_offset = dsp, temp = dsp + 8;
+ __u8 pun = 0xff, lun = 0xff;
+
+ if(SCp != NULL) {
+ pun = SCp->device->id;
+ lun = SCp->device->lun;
+ }
+
+ switch(hostdata->msgin[2]) {
+ case A_SDTR_MSG:
+ if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) {
+ struct scsi_target *starget = SCp->device->sdev_target;
+ __u8 period = hostdata->msgin[3];
+ __u8 offset = hostdata->msgin[4];
+
+ if(offset == 0 || period == 0) {
+ offset = 0;
+ period = 0;
+ }
+
+ spi_offset(starget) = offset;
+ spi_period(starget) = period;
+
+ if(NCR_700_is_flag_set(SCp->device, NCR_700_DEV_PRINT_SYNC_NEGOTIATION)) {
+ spi_display_xfer_agreement(starget);
+ NCR_700_clear_flag(SCp->device, NCR_700_DEV_PRINT_SYNC_NEGOTIATION);
+ }
+
+ NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC);
+ NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION);
+
+ NCR_700_writeb(NCR_700_get_SXFER(SCp->device),
+ host, SXFER_REG);
+
+ } else {
+ /* SDTR message out of the blue, reject it */
+ printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n",
+ host->host_no);
+ hostdata->msgout[0] = A_REJECT_MSG;
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
+ script_patch_16(hostdata->script, MessageCount, 1);
+ /* SendMsgOut returns, so set up the return
+ * address */
+ resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
+ }
+ break;
+
+ case A_WDTR_MSG:
+ printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n",
+ host->host_no, pun, lun);
+ hostdata->msgout[0] = A_REJECT_MSG;
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
+ script_patch_16(hostdata->script, MessageCount, 1);
+ resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
+
+ break;
+
+ default:
+ printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ",
+ host->host_no, pun, lun,
+ NCR_700_phase[(dsps & 0xf00) >> 8]);
+ scsi_print_msg(hostdata->msgin);
+ printk("\n");
+ /* just reject it */
+ hostdata->msgout[0] = A_REJECT_MSG;
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
+ script_patch_16(hostdata->script, MessageCount, 1);
+ /* SendMsgOut returns, so set up the return
+ * address */
+ resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
+ }
+ NCR_700_writel(temp, host, TEMP_REG);
+ return resume_offset;
+}
+
+STATIC __u32
+process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata,
+ struct scsi_cmnd *SCp, __u32 dsp, __u32 dsps)
+{
+ /* work out where to return to */
+ __u32 temp = dsp + 8, resume_offset = dsp;
+ __u8 pun = 0xff, lun = 0xff;
+
+ if(SCp != NULL) {
+ pun = SCp->device->id;
+ lun = SCp->device->lun;
+ }
+
+#ifdef NCR_700_DEBUG
+ printk("scsi%d (%d:%d): message %s: ", host->host_no, pun, lun,
+ NCR_700_phase[(dsps & 0xf00) >> 8]);
+ scsi_print_msg(hostdata->msgin);
+ printk("\n");
+#endif
+
+ switch(hostdata->msgin[0]) {
+
+ case A_EXTENDED_MSG:
+ resume_offset = process_extended_message(host, hostdata, SCp,
+ dsp, dsps);
+ break;
+
+ case A_REJECT_MSG:
+ if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) {
+ /* Rejected our sync negotiation attempt */
+ spi_period(SCp->device->sdev_target) =
+ spi_offset(SCp->device->sdev_target) = 0;
+ NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC);
+ NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION);
+ } else if(SCp != NULL && NCR_700_get_tag_neg_state(SCp->device) == NCR_700_DURING_TAG_NEGOTIATION) {
+ /* rejected our first simple tag message */
+ printk(KERN_WARNING "scsi%d (%d:%d) Rejected first tag queue attempt, turning off tag queueing\n", host->host_no, pun, lun);
+ /* we're done negotiating */
+ NCR_700_set_tag_neg_state(SCp->device, NCR_700_FINISHED_TAG_NEGOTIATION);
+ hostdata->tag_negotiated &= ~(1<<SCp->device->id);
+ SCp->device->tagged_supported = 0;
+ scsi_deactivate_tcq(SCp->device, host->cmd_per_lun);
+ } else {
+ printk(KERN_WARNING "scsi%d (%d:%d) Unexpected REJECT Message %s\n",
+ host->host_no, pun, lun,
+ NCR_700_phase[(dsps & 0xf00) >> 8]);
+ /* however, just ignore it */
+ }
+ break;
+
+ case A_PARITY_ERROR_MSG:
+ printk(KERN_ERR "scsi%d (%d:%d) Parity Error!\n", host->host_no,
+ pun, lun);
+ NCR_700_internal_bus_reset(host);
+ break;
+ case A_SIMPLE_TAG_MSG:
+ printk(KERN_INFO "scsi%d (%d:%d) SIMPLE TAG %d %s\n", host->host_no,
+ pun, lun, hostdata->msgin[1],
+ NCR_700_phase[(dsps & 0xf00) >> 8]);
+ /* just ignore it */
+ break;
+ default:
+ printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ",
+ host->host_no, pun, lun,
+ NCR_700_phase[(dsps & 0xf00) >> 8]);
+
+ scsi_print_msg(hostdata->msgin);
+ printk("\n");
+ /* just reject it */
+ hostdata->msgout[0] = A_REJECT_MSG;
+ dma_cache_sync(hostdata->msgout, 1, DMA_TO_DEVICE);
+ script_patch_16(hostdata->script, MessageCount, 1);
+ /* SendMsgOut returns, so set up the return
+ * address */
+ resume_offset = hostdata->pScript + Ent_SendMessageWithATN;
+
+ break;
+ }
+ NCR_700_writel(temp, host, TEMP_REG);
+ /* set us up to receive another message */
+ dma_cache_sync(hostdata->msgin, MSG_ARRAY_SIZE, DMA_FROM_DEVICE);
+ return resume_offset;
+}
+
+STATIC __u32
+process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp,
+ struct Scsi_Host *host,
+ struct NCR_700_Host_Parameters *hostdata)
+{
+ __u32 resume_offset = 0;
+ __u8 pun = 0xff, lun=0xff;
+
+ if(SCp != NULL) {
+ pun = SCp->device->id;
+ lun = SCp->device->lun;
+ }
+
+ if(dsps == A_GOOD_STATUS_AFTER_STATUS) {
+ DEBUG((" COMMAND COMPLETE, status=%02x\n",
+ hostdata->status[0]));
+ /* OK, if TCQ still under negotiation, we now know it works */
+ if (NCR_700_get_tag_neg_state(SCp->device) == NCR_700_DURING_TAG_NEGOTIATION)
+ NCR_700_set_tag_neg_state(SCp->device,
+ NCR_700_FINISHED_TAG_NEGOTIATION);
+
+ /* check for contingent allegiance contitions */
+ if(status_byte(hostdata->status[0]) == CHECK_CONDITION ||
+ status_byte(hostdata->status[0]) == COMMAND_TERMINATED) {
+ struct NCR_700_command_slot *slot =
+ (struct NCR_700_command_slot *)SCp->host_scribble;
+ if(SCp->cmnd[0] == REQUEST_SENSE) {
+ /* OOPS: bad device, returning another
+ * contingent allegiance condition */
+ printk(KERN_ERR "scsi%d (%d:%d) broken device is looping in contingent allegiance: ignoring\n", host->host_no, pun, lun);
+ NCR_700_scsi_done(hostdata, SCp, hostdata->status[0]);
+ } else {
+#ifdef NCR_DEBUG
+ scsi_print_command(SCp);
+ printk(" cmd %p has status %d, requesting sense\n",
+ SCp, hostdata->status[0]);
+#endif
+ /* we can destroy the command here
+ * because the contingent allegiance
+ * condition will cause a retry which
+ * will re-copy the command from the
+ * saved data_cmnd. We also unmap any
+ * data associated with the command
+ * here */
+ NCR_700_unmap(hostdata, SCp, slot);
+
+ SCp->cmnd[0] = REQUEST_SENSE;
+ SCp->cmnd[1] = (SCp->device->lun & 0x7) << 5;
+ SCp->cmnd[2] = 0;
+ SCp->cmnd[3] = 0;
+ SCp->cmnd[4] = sizeof(SCp->sense_buffer);
+ SCp->cmnd[5] = 0;
+ SCp->cmd_len = 6;
+ /* Here's a quiet hack: the
+ * REQUEST_SENSE command is six bytes,
+ * so store a flag indicating that
+ * this was an internal sense request
+ * and the original status at the end
+ * of the command */
+ SCp->cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC;
+ SCp->cmnd[7] = hostdata->status[0];
+ SCp->use_sg = 0;
+ SCp->sc_data_direction = DMA_FROM_DEVICE;
+ dma_sync_single_for_device(hostdata->dev, slot->pCmd,
+ SCp->cmd_len, DMA_TO_DEVICE);
+ SCp->request_bufflen = sizeof(SCp->sense_buffer);
+ slot->dma_handle = dma_map_single(hostdata->dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE);
+ slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer));
+ slot->SG[0].pAddr = bS_to_host(slot->dma_handle);
+ slot->SG[1].ins = bS_to_host(SCRIPT_RETURN);
+ slot->SG[1].pAddr = 0;
+ slot->resume_offset = hostdata->pScript;
+ dma_cache_sync(slot->SG, sizeof(slot->SG[0])*2, DMA_TO_DEVICE);
+ dma_cache_sync(SCp->sense_buffer, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE);
+
+ /* queue the command for reissue */
+ slot->state = NCR_700_SLOT_QUEUED;
+ hostdata->state = NCR_700_HOST_FREE;
+ hostdata->cmd = NULL;
+ }
+ } else {
+ // Currently rely on the mid layer evaluation
+ // of the tag queuing capability
+ //
+ //if(status_byte(hostdata->status[0]) == GOOD &&
+ // SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) {
+ // /* Piggy back the tag queueing support
+ // * on this command */
+ // dma_sync_single_for_cpu(hostdata->dev,
+ // slot->dma_handle,
+ // SCp->request_bufflen,
+ // DMA_FROM_DEVICE);
+ // if(((char *)SCp->request_buffer)[7] & 0x02) {
+ // printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", host->host_no, pun, lun);
+ // hostdata->tag_negotiated |= (1<<SCp->device->id);
+ // NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING);
+ // } else {
+ // NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING);
+ // hostdata->tag_negotiated &= ~(1<<SCp->device->id);
+ // }
+ //}
+ NCR_700_scsi_done(hostdata, SCp, hostdata->status[0]);
+ }
+ } else if((dsps & 0xfffff0f0) == A_UNEXPECTED_PHASE) {
+ __u8 i = (dsps & 0xf00) >> 8;
+
+ printk(KERN_ERR "scsi%d: (%d:%d), UNEXPECTED PHASE %s (%s)\n",
+ host->host_no, pun, lun,
+ NCR_700_phase[i],
+ sbcl_to_string(NCR_700_readb(host, SBCL_REG)));
+ printk(KERN_ERR " len = %d, cmd =", SCp->cmd_len);
+ scsi_print_command(SCp);
+
+ NCR_700_internal_bus_reset(host);
+ } else if((dsps & 0xfffff000) == A_FATAL) {
+ int i = (dsps & 0xfff);
+
+ printk(KERN_ERR "scsi%d: (%d:%d) FATAL ERROR: %s\n",
+ host->host_no, pun, lun, NCR_700_fatal_messages[i]);
+ if(dsps == A_FATAL_ILLEGAL_MSG_LENGTH) {
+ printk(KERN_ERR " msg begins %02x %02x\n",
+ hostdata->msgin[0], hostdata->msgin[1]);
+ }
+ NCR_700_internal_bus_reset(host);
+ } else if((dsps & 0xfffff0f0) == A_DISCONNECT) {
+#ifdef NCR_700_DEBUG
+ __u8 i = (dsps & 0xf00) >> 8;
+
+ printk("scsi%d: (%d:%d), DISCONNECTED (%d) %s\n",
+ host->host_no, pun, lun,
+ i, NCR_700_phase[i]);
+#endif
+ save_for_reselection(hostdata, SCp, dsp);
+
+ } else if(dsps == A_RESELECTION_IDENTIFIED) {
+ __u8 lun;
+ struct NCR_700_command_slot *slot;
+ __u8 reselection_id = hostdata->reselection_id;
+ struct scsi_device *SDp;
+
+ lun = hostdata->msgin[0] & 0x1f;
+
+ hostdata->reselection_id = 0xff;
+ DEBUG(("scsi%d: (%d:%d) RESELECTED!\n",
+ host->host_no, reselection_id, lun));
+ /* clear the reselection indicator */
+ SDp = __scsi_device_lookup(host, 0, reselection_id, lun);
+ if(unlikely(SDp == NULL)) {
+ printk(KERN_ERR "scsi%d: (%d:%d) HAS NO device\n",
+ host->host_no, reselection_id, lun);
+ BUG();
+ }
+ if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) {
+ struct scsi_cmnd *SCp = scsi_find_tag(SDp, hostdata->msgin[2]);
+ if(unlikely(SCp == NULL)) {
+ printk(KERN_ERR "scsi%d: (%d:%d) no saved request for tag %d\n",
+ host->host_no, reselection_id, lun, hostdata->msgin[2]);
+ BUG();
+ }
+
+ slot = (struct NCR_700_command_slot *)SCp->host_scribble;
+ DEBUG(("53c700: %d:%d:%d, reselection is tag %d, slot %p(%d)\n",
+ host->host_no, SDp->id, SDp->lun,
+ hostdata->msgin[2], slot, slot->tag));
+ } else {
+ struct scsi_cmnd *SCp = scsi_find_tag(SDp, SCSI_NO_TAG);
+ if(unlikely(SCp == NULL)) {
+ printk(KERN_ERR "scsi%d: (%d:%d) no saved request for untagged cmd\n",
+ host->host_no, reselection_id, lun);
+ BUG();
+ }
+ slot = (struct NCR_700_command_slot *)SCp->host_scribble;
+ }
+
+ if(slot == NULL) {
+ printk(KERN_ERR "scsi%d: (%d:%d) RESELECTED but no saved command (MSG = %02x %02x %02x)!!\n",
+ host->host_no, reselection_id, lun,
+ hostdata->msgin[0], hostdata->msgin[1],
+ hostdata->msgin[2]);
+ } else {
+ if(hostdata->state != NCR_700_HOST_BUSY)
+ printk(KERN_ERR "scsi%d: FATAL, host not busy during valid reselection!\n",
+ host->host_no);
+ resume_offset = slot->resume_offset;
+ hostdata->cmd = slot->cmnd;
+
+ /* re-patch for this command */
+ script_patch_32_abs(hostdata->script, CommandAddress,
+ slot->pCmd);
+ script_patch_16(hostdata->script,
+ CommandCount, slot->cmnd->cmd_len);
+ script_patch_32_abs(hostdata->script, SGScriptStartAddress,
+ to32bit(&slot->pSG[0].ins));
+
+ /* Note: setting SXFER only works if we're
+ * still in the MESSAGE phase, so it is vital
+ * that ACK is still asserted when we process
+ * the reselection message. The resume offset
+ * should therefore always clear ACK */
+ NCR_700_writeb(NCR_700_get_SXFER(hostdata->cmd->device),
+ host, SXFER_REG);
+ dma_cache_sync(hostdata->msgin,
+ MSG_ARRAY_SIZE, DMA_FROM_DEVICE);
+ dma_cache_sync(hostdata->msgout,
+ MSG_ARRAY_SIZE, DMA_TO_DEVICE);
+ /* I'm just being paranoid here, the command should
+ * already have been flushed from the cache */
+ dma_cache_sync(slot->cmnd->cmnd,
+ slot->cmnd->cmd_len, DMA_TO_DEVICE);
+
+
+
+ }
+ } else if(dsps == A_RESELECTED_DURING_SELECTION) {
+
+ /* This section is full of debugging code because I've
+ * never managed to reach it. I think what happens is
+ * that, because the 700 runs with selection
+ * interrupts enabled the whole time that we take a
+ * selection interrupt before we manage to get to the
+ * reselected script interrupt */
+
+ __u8 reselection_id = NCR_700_readb(host, SFBR_REG);
+ struct NCR_700_command_slot *slot;
+
+ /* Take out our own ID */
+ reselection_id &= ~(1<<host->this_id);
+
+ /* I've never seen this happen, so keep this as a printk rather
+ * than a debug */
+ printk(KERN_INFO "scsi%d: (%d:%d) RESELECTION DURING SELECTION, dsp=%08x[%04x] state=%d, count=%d\n",
+ host->host_no, reselection_id, lun, dsp, dsp - hostdata->pScript, hostdata->state, hostdata->command_slot_count);
+
+ {
+ /* FIXME: DEBUGGING CODE */
+ __u32 SG = (__u32)bS_to_cpu(hostdata->script[A_SGScriptStartAddress_used[0]]);
+ int i;
+
+ for(i=0; i< NCR_700_COMMAND_SLOTS_PER_HOST; i++) {
+ if(SG >= to32bit(&hostdata->slots[i].pSG[0])
+ && SG <= to32bit(&hostdata->slots[i].pSG[NCR_700_SG_SEGMENTS]))
+ break;
+ }
+ printk(KERN_INFO "IDENTIFIED SG segment as being %08x in slot %p, cmd %p, slot->resume_offset=%08x\n", SG, &hostdata->slots[i], hostdata->slots[i].cmnd, hostdata->slots[i].resume_offset);
+ SCp = hostdata->slots[i].cmnd;
+ }
+
+ if(SCp != NULL) {
+ slot = (struct NCR_700_command_slot *)SCp->host_scribble;
+ /* change slot from busy to queued to redo command */
+ slot->state = NCR_700_SLOT_QUEUED;
+ }
+ hostdata->cmd = NULL;
+
+ if(reselection_id == 0) {
+ if(hostdata->reselection_id == 0xff) {
+ printk(KERN_ERR "scsi%d: Invalid reselection during selection!!\n", host->host_no);
+ return 0;
+ } else {
+ printk(KERN_ERR "scsi%d: script reselected and we took a selection interrupt\n",
+ host->host_no);
+ reselection_id = hostdata->reselection_id;
+ }
+ } else {
+
+ /* convert to real ID */
+ reselection_id = bitmap_to_number(reselection_id);
+ }
+ hostdata->reselection_id = reselection_id;
+ /* just in case we have a stale simple tag message, clear it */
+ hostdata->msgin[1] = 0;
+ dma_cache_sync(hostdata->msgin,
+ MSG_ARRAY_SIZE, DMA_BIDIRECTIONAL);
+ if(hostdata->tag_negotiated & (1<<reselection_id)) {
+ resume_offset = hostdata->pScript + Ent_GetReselectionWithTag;
+ } else {
+ resume_offset = hostdata->pScript + Ent_GetReselectionData;
+ }
+ } else if(dsps == A_COMPLETED_SELECTION_AS_TARGET) {
+ /* we've just disconnected from the bus, do nothing since
+ * a return here will re-run the queued command slot
+ * that may have been interrupted by the initial selection */
+ DEBUG((" SELECTION COMPLETED\n"));
+ } else if((dsps & 0xfffff0f0) == A_MSG_IN) {
+ resume_offset = process_message(host, hostdata, SCp,
+ dsp, dsps);
+ } else if((dsps & 0xfffff000) == 0) {
+ __u8 i = (dsps & 0xf0) >> 4, j = (dsps & 0xf00) >> 8;
+ printk(KERN_ERR "scsi%d: (%d:%d), unhandled script condition %s %s at %04x\n",
+ host->host_no, pun, lun, NCR_700_condition[i],
+ NCR_700_phase[j], dsp - hostdata->pScript);
+ if(SCp != NULL) {
+ scsi_print_command(SCp);
+
+ if(SCp->use_sg) {
+ for(i = 0; i < SCp->use_sg + 1; i++) {
+ printk(KERN_INFO " SG[%d].length = %d, move_insn=%08x, addr %08x\n", i, ((struct scatterlist *)SCp->buffer)[i].length, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].ins, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].pAddr);
+ }
+ }
+ }
+ NCR_700_internal_bus_reset(host);
+ } else if((dsps & 0xfffff000) == A_DEBUG_INTERRUPT) {
+ printk(KERN_NOTICE "scsi%d (%d:%d) DEBUG INTERRUPT %d AT %08x[%04x], continuing\n",
+ host->host_no, pun, lun, dsps & 0xfff, dsp, dsp - hostdata->pScript);
+ resume_offset = dsp;
+ } else {
+ printk(KERN_ERR "scsi%d: (%d:%d), unidentified script interrupt 0x%x at %04x\n",
+ host->host_no, pun, lun, dsps, dsp - hostdata->pScript);
+ NCR_700_internal_bus_reset(host);
+ }
+ return resume_offset;
+}
+
+/* We run the 53c700 with selection interrupts always enabled. This
+ * means that the chip may be selected as soon as the bus frees. On a
+ * busy bus, this can be before the scripts engine finishes its
+ * processing. Therefore, part of the selection processing has to be
+ * to find out what the scripts engine is doing and complete the
+ * function if necessary (i.e. process the pending disconnect or save
+ * the interrupted initial selection */
+STATIC inline __u32
+process_selection(struct Scsi_Host *host, __u32 dsp)
+{
+ __u8 id = 0; /* Squash compiler warning */
+ int count = 0;
+ __u32 resume_offset = 0;
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ struct scsi_cmnd *SCp = hostdata->cmd;
+ __u8 sbcl;
+
+ for(count = 0; count < 5; count++) {
+ id = NCR_700_readb(host, hostdata->chip710 ?
+ CTEST9_REG : SFBR_REG);
+
+ /* Take out our own ID */
+ id &= ~(1<<host->this_id);
+ if(id != 0)
+ break;
+ udelay(5);
+ }
+ sbcl = NCR_700_readb(host, SBCL_REG);
+ if((sbcl & SBCL_IO) == 0) {
+ /* mark as having been selected rather than reselected */
+ id = 0xff;
+ } else {
+ /* convert to real ID */
+ hostdata->reselection_id = id = bitmap_to_number(id);
+ DEBUG(("scsi%d: Reselected by %d\n",
+ host->host_no, id));
+ }
+ if(hostdata->state == NCR_700_HOST_BUSY && SCp != NULL) {
+ struct NCR_700_command_slot *slot =
+ (struct NCR_700_command_slot *)SCp->host_scribble;
+ DEBUG((" ID %d WARNING: RESELECTION OF BUSY HOST, saving cmd %p, slot %p, addr %x [%04x], resume %x!\n", id, hostdata->cmd, slot, dsp, dsp - hostdata->pScript, resume_offset));
+
+ switch(dsp - hostdata->pScript) {
+ case Ent_Disconnect1:
+ case Ent_Disconnect2:
+ save_for_reselection(hostdata, SCp, Ent_Disconnect2 + hostdata->pScript);
+ break;
+ case Ent_Disconnect3:
+ case Ent_Disconnect4:
+ save_for_reselection(hostdata, SCp, Ent_Disconnect4 + hostdata->pScript);
+ break;
+ case Ent_Disconnect5:
+ case Ent_Disconnect6:
+ save_for_reselection(hostdata, SCp, Ent_Disconnect6 + hostdata->pScript);
+ break;
+ case Ent_Disconnect7:
+ case Ent_Disconnect8:
+ save_for_reselection(hostdata, SCp, Ent_Disconnect8 + hostdata->pScript);
+ break;
+ case Ent_Finish1:
+ case Ent_Finish2:
+ process_script_interrupt(A_GOOD_STATUS_AFTER_STATUS, dsp, SCp, host, hostdata);
+ break;
+
+ default:
+ slot->state = NCR_700_SLOT_QUEUED;
+ break;
+ }
+ }
+ hostdata->state = NCR_700_HOST_BUSY;
+ hostdata->cmd = NULL;
+ /* clear any stale simple tag message */
+ hostdata->msgin[1] = 0;
+ dma_cache_sync(hostdata->msgin, MSG_ARRAY_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if(id == 0xff) {
+ /* Selected as target, Ignore */
+ resume_offset = hostdata->pScript + Ent_SelectedAsTarget;
+ } else if(hostdata->tag_negotiated & (1<<id)) {
+ resume_offset = hostdata->pScript + Ent_GetReselectionWithTag;
+ } else {
+ resume_offset = hostdata->pScript + Ent_GetReselectionData;
+ }
+ return resume_offset;
+}
+
+static inline void
+NCR_700_clear_fifo(struct Scsi_Host *host) {
+ const struct NCR_700_Host_Parameters *hostdata
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ if(hostdata->chip710) {
+ NCR_700_writeb(CLR_FIFO_710, host, CTEST8_REG);
+ } else {
+ NCR_700_writeb(CLR_FIFO, host, DFIFO_REG);
+ }
+}
+
+static inline void
+NCR_700_flush_fifo(struct Scsi_Host *host) {
+ const struct NCR_700_Host_Parameters *hostdata
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ if(hostdata->chip710) {
+ NCR_700_writeb(FLUSH_DMA_FIFO_710, host, CTEST8_REG);
+ udelay(10);
+ NCR_700_writeb(0, host, CTEST8_REG);
+ } else {
+ NCR_700_writeb(FLUSH_DMA_FIFO, host, DFIFO_REG);
+ udelay(10);
+ NCR_700_writeb(0, host, DFIFO_REG);
+ }
+}
+
+
+/* The queue lock with interrupts disabled must be held on entry to
+ * this function */
+STATIC int
+NCR_700_start_command(struct scsi_cmnd *SCp)
+{
+ struct NCR_700_command_slot *slot =
+ (struct NCR_700_command_slot *)SCp->host_scribble;
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
+ __u16 count = 1; /* for IDENTIFY message */
+
+ if(hostdata->state != NCR_700_HOST_FREE) {
+ /* keep this inside the lock to close the race window where
+ * the running command finishes on another CPU while we don't
+ * change the state to queued on this one */
+ slot->state = NCR_700_SLOT_QUEUED;
+
+ DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n",
+ SCp->device->host->host_no, slot->cmnd, slot));
+ return 0;
+ }
+ hostdata->state = NCR_700_HOST_BUSY;
+ hostdata->cmd = SCp;
+ slot->state = NCR_700_SLOT_BUSY;
+ /* keep interrupts disabled until we have the command correctly
+ * set up so we cannot take a selection interrupt */
+
+ hostdata->msgout[0] = NCR_700_identify(SCp->cmnd[0] != REQUEST_SENSE,
+ SCp->device->lun);
+ /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure
+ * if the negotiated transfer parameters still hold, so
+ * always renegotiate them */
+ if(SCp->cmnd[0] == INQUIRY || SCp->cmnd[0] == REQUEST_SENSE) {
+ NCR_700_clear_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC);
+ }
+
+ /* REQUEST_SENSE is asking for contingent I_T_L(_Q) status.
+ * If a contingent allegiance condition exists, the device
+ * will refuse all tags, so send the request sense as untagged
+ * */
+ if((hostdata->tag_negotiated & (1<<SCp->device->id))
+ && (slot->tag != SCSI_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE)) {
+ count += scsi_populate_tag_msg(SCp, &hostdata->msgout[count]);
+ }
+
+ if(hostdata->fast &&
+ NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) {
+ memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg,
+ sizeof(NCR_700_SDTR_msg));
+ hostdata->msgout[count+3] = spi_period(SCp->device->sdev_target);
+ hostdata->msgout[count+4] = spi_offset(SCp->device->sdev_target);
+ count += sizeof(NCR_700_SDTR_msg);
+ NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION);
+ }
+
+ script_patch_16(hostdata->script, MessageCount, count);
+
+
+ script_patch_ID(hostdata->script,
+ Device_ID, 1<<SCp->device->id);
+
+ script_patch_32_abs(hostdata->script, CommandAddress,
+ slot->pCmd);
+ script_patch_16(hostdata->script, CommandCount, SCp->cmd_len);
+ /* finally plumb the beginning of the SG list into the script
+ * */
+ script_patch_32_abs(hostdata->script, SGScriptStartAddress,
+ to32bit(&slot->pSG[0].ins));
+ NCR_700_clear_fifo(SCp->device->host);
+
+ if(slot->resume_offset == 0)
+ slot->resume_offset = hostdata->pScript;
+ /* now perform all the writebacks and invalidates */
+ dma_cache_sync(hostdata->msgout, count, DMA_TO_DEVICE);
+ dma_cache_sync(hostdata->msgin, MSG_ARRAY_SIZE,
+ DMA_FROM_DEVICE);
+ dma_cache_sync(SCp->cmnd, SCp->cmd_len, DMA_TO_DEVICE);
+ dma_cache_sync(hostdata->status, 1, DMA_FROM_DEVICE);
+
+ /* set the synchronous period/offset */
+ NCR_700_writeb(NCR_700_get_SXFER(SCp->device),
+ SCp->device->host, SXFER_REG);
+ NCR_700_writel(slot->temp, SCp->device->host, TEMP_REG);
+ NCR_700_writel(slot->resume_offset, SCp->device->host, DSP_REG);
+
+ return 1;
+}
+
+irqreturn_t
+NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+ __u8 istat;
+ __u32 resume_offset = 0;
+ __u8 pun = 0xff, lun = 0xff;
+ unsigned long flags;
+ int handled = 0;
+
+ /* Use the host lock to serialise acess to the 53c700
+ * hardware. Note: In future, we may need to take the queue
+ * lock to enter the done routines. When that happens, we
+ * need to ensure that for this driver, the host lock and the
+ * queue lock point to the same thing. */
+ spin_lock_irqsave(host->host_lock, flags);
+ if((istat = NCR_700_readb(host, ISTAT_REG))
+ & (SCSI_INT_PENDING | DMA_INT_PENDING)) {
+ __u32 dsps;
+ __u8 sstat0 = 0, dstat = 0;
+ __u32 dsp;
+ struct scsi_cmnd *SCp = hostdata->cmd;
+ enum NCR_700_Host_State state;
+
+ handled = 1;
+ state = hostdata->state;
+ SCp = hostdata->cmd;
+
+ if(istat & SCSI_INT_PENDING) {
+ udelay(10);
+
+ sstat0 = NCR_700_readb(host, SSTAT0_REG);
+ }
+
+ if(istat & DMA_INT_PENDING) {
+ udelay(10);
+
+ dstat = NCR_700_readb(host, DSTAT_REG);
+ }
+
+ dsps = NCR_700_readl(host, DSPS_REG);
+ dsp = NCR_700_readl(host, DSP_REG);
+
+ DEBUG(("scsi%d: istat %02x sstat0 %02x dstat %02x dsp %04x[%08x] dsps 0x%x\n",
+ host->host_no, istat, sstat0, dstat,
+ (dsp - (__u32)(hostdata->pScript))/4,
+ dsp, dsps));
+
+ if(SCp != NULL) {
+ pun = SCp->device->id;
+ lun = SCp->device->lun;
+ }
+
+ if(sstat0 & SCSI_RESET_DETECTED) {
+ struct scsi_device *SDp;
+ int i;
+
+ hostdata->state = NCR_700_HOST_BUSY;
+
+ printk(KERN_ERR "scsi%d: Bus Reset detected, executing command %p, slot %p, dsp %08x[%04x]\n",
+ host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, dsp, dsp - hostdata->pScript);
+
+ scsi_report_bus_reset(host, 0);
+
+ /* clear all the negotiated parameters */
+ __shost_for_each_device(SDp, host)
+ SDp->hostdata = NULL;
+
+ /* clear all the slots and their pending commands */
+ for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) {
+ struct scsi_cmnd *SCp;
+ struct NCR_700_command_slot *slot =
+ &hostdata->slots[i];
+
+ if(slot->state == NCR_700_SLOT_FREE)
+ continue;
+
+ SCp = slot->cmnd;
+ printk(KERN_ERR " failing command because of reset, slot %p, cmnd %p\n",
+ slot, SCp);
+ free_slot(slot, hostdata);
+ SCp->host_scribble = NULL;
+ NCR_700_set_depth(SCp->device, 0);
+ /* NOTE: deadlock potential here: we
+ * rely on mid-layer guarantees that
+ * scsi_done won't try to issue the
+ * command again otherwise we'll
+ * deadlock on the
+ * hostdata->state_lock */
+ SCp->result = DID_RESET << 16;
+ SCp->scsi_done(SCp);
+ }
+ mdelay(25);
+ NCR_700_chip_setup(host);
+
+ hostdata->state = NCR_700_HOST_FREE;
+ hostdata->cmd = NULL;
+ /* signal back if this was an eh induced reset */
+ if(hostdata->eh_complete != NULL)
+ complete(hostdata->eh_complete);
+ goto out_unlock;
+ } else if(sstat0 & SELECTION_TIMEOUT) {
+ DEBUG(("scsi%d: (%d:%d) selection timeout\n",
+ host->host_no, pun, lun));
+ NCR_700_scsi_done(hostdata, SCp, DID_NO_CONNECT<<16);
+ } else if(sstat0 & PHASE_MISMATCH) {
+ struct NCR_700_command_slot *slot = (SCp == NULL) ? NULL :
+ (struct NCR_700_command_slot *)SCp->host_scribble;
+
+ if(dsp == Ent_SendMessage + 8 + hostdata->pScript) {
+ /* It wants to reply to some part of
+ * our message */
+#ifdef NCR_700_DEBUG
+ __u32 temp = NCR_700_readl(host, TEMP_REG);
+ int count = (hostdata->script[Ent_SendMessage/4] & 0xffffff) - ((NCR_700_readl(host, DBC_REG) & 0xffffff) + NCR_700_data_residual(host));
+ printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE %d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void *)temp, temp - hostdata->pScript, sbcl_to_string(NCR_700_readb(host, SBCL_REG)));
+#endif
+ resume_offset = hostdata->pScript + Ent_SendMessagePhaseMismatch;
+ } else if(dsp >= to32bit(&slot->pSG[0].ins) &&
+ dsp <= to32bit(&slot->pSG[NCR_700_SG_SEGMENTS].ins)) {
+ int data_transfer = NCR_700_readl(host, DBC_REG) & 0xffffff;
+ int SGcount = (dsp - to32bit(&slot->pSG[0].ins))/sizeof(struct NCR_700_SG_List);
+ int residual = NCR_700_data_residual(host);
+ int i;
+#ifdef NCR_700_DEBUG
+ __u32 naddr = NCR_700_readl(host, DNAD_REG);
+
+ printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x\n",
+ host->host_no, pun, lun,
+ SGcount, data_transfer);
+ scsi_print_command(SCp);
+ if(residual) {
+ printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x, residual %d\n",
+ host->host_no, pun, lun,
+ SGcount, data_transfer, residual);
+ }
+#endif
+ data_transfer += residual;
+
+ if(data_transfer != 0) {
+ int count;
+ __u32 pAddr;
+
+ SGcount--;
+
+ count = (bS_to_cpu(slot->SG[SGcount].ins) & 0x00ffffff);
+ DEBUG(("DATA TRANSFER MISMATCH, count = %d, transferred %d\n", count, count-data_transfer));
+ slot->SG[SGcount].ins &= bS_to_host(0xff000000);
+ slot->SG[SGcount].ins |= bS_to_host(data_transfer);
+ pAddr = bS_to_cpu(slot->SG[SGcount].pAddr);
+ pAddr += (count - data_transfer);
+#ifdef NCR_700_DEBUG
+ if(pAddr != naddr) {
+ printk("scsi%d (%d:%d) transfer mismatch pAddr=%lx, naddr=%lx, data_transfer=%d, residual=%d\n", host->host_no, pun, lun, (unsigned long)pAddr, (unsigned long)naddr, data_transfer, residual);
+ }
+#endif
+ slot->SG[SGcount].pAddr = bS_to_host(pAddr);
+ }
+ /* set the executed moves to nops */
+ for(i=0; i<SGcount; i++) {
+ slot->SG[i].ins = bS_to_host(SCRIPT_NOP);
+ slot->SG[i].pAddr = 0;
+ }
+ dma_cache_sync(slot->SG, sizeof(slot->SG), DMA_TO_DEVICE);
+ /* and pretend we disconnected after
+ * the command phase */
+ resume_offset = hostdata->pScript + Ent_MsgInDuringData;
+ /* make sure all the data is flushed */
+ NCR_700_flush_fifo(host);
+ } else {
+ __u8 sbcl = NCR_700_readb(host, SBCL_REG);
+ printk(KERN_ERR "scsi%d: (%d:%d) phase mismatch at %04x, phase %s\n",
+ host->host_no, pun, lun, dsp - hostdata->pScript, sbcl_to_string(sbcl));
+ NCR_700_internal_bus_reset(host);
+ }
+
+ } else if(sstat0 & SCSI_GROSS_ERROR) {
+ printk(KERN_ERR "scsi%d: (%d:%d) GROSS ERROR\n",
+ host->host_no, pun, lun);
+ NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16);
+ } else if(sstat0 & PARITY_ERROR) {
+ printk(KERN_ERR "scsi%d: (%d:%d) PARITY ERROR\n",
+ host->host_no, pun, lun);
+ NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16);
+ } else if(dstat & SCRIPT_INT_RECEIVED) {
+ DEBUG(("scsi%d: (%d:%d) ====>SCRIPT INTERRUPT<====\n",
+ host->host_no, pun, lun));
+ resume_offset = process_script_interrupt(dsps, dsp, SCp, host, hostdata);
+ } else if(dstat & (ILGL_INST_DETECTED)) {
+ printk(KERN_ERR "scsi%d: (%d:%d) Illegal Instruction detected at 0x%08x[0x%x]!!!\n"
+ " Please email James.Bottomley@HansenPartnership.com with the details\n",
+ host->host_no, pun, lun,
+ dsp, dsp - hostdata->pScript);
+ NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16);
+ } else if(dstat & (WATCH_DOG_INTERRUPT|ABORTED)) {
+ printk(KERN_ERR "scsi%d: (%d:%d) serious DMA problem, dstat=%02x\n",
+ host->host_no, pun, lun, dstat);
+ NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16);
+ }
+
+
+ /* NOTE: selection interrupt processing MUST occur
+ * after script interrupt processing to correctly cope
+ * with the case where we process a disconnect and
+ * then get reselected before we process the
+ * disconnection */
+ if(sstat0 & SELECTED) {
+ /* FIXME: It currently takes at least FOUR
+ * interrupts to complete a command that
+ * disconnects: one for the disconnect, one
+ * for the reselection, one to get the
+ * reselection data and one to complete the
+ * command. If we guess the reselected
+ * command here and prepare it, we only need
+ * to get a reselection data interrupt if we
+ * guessed wrongly. Since the interrupt
+ * overhead is much greater than the command
+ * setup, this would be an efficient
+ * optimisation particularly as we probably
+ * only have one outstanding command on a
+ * target most of the time */
+
+ resume_offset = process_selection(host, dsp);
+
+ }
+
+ }
+
+ if(resume_offset) {
+ if(hostdata->state != NCR_700_HOST_BUSY) {
+ printk(KERN_ERR "scsi%d: Driver error: resume at 0x%08x [0x%04x] with non busy host!\n",
+ host->host_no, resume_offset, resume_offset - hostdata->pScript);
+ hostdata->state = NCR_700_HOST_BUSY;
+ }
+
+ DEBUG(("Attempting to resume at %x\n", resume_offset));
+ NCR_700_clear_fifo(host);
+ NCR_700_writel(resume_offset, host, DSP_REG);
+ }
+ /* There is probably a technical no-no about this: If we're a
+ * shared interrupt and we got this interrupt because the
+ * other device needs servicing not us, we're still going to
+ * check our queued commands here---of course, there shouldn't
+ * be any outstanding.... */
+ if(hostdata->state == NCR_700_HOST_FREE) {
+ int i;
+
+ for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) {
+ /* fairness: always run the queue from the last
+ * position we left off */
+ int j = (i + hostdata->saved_slot_position)
+ % NCR_700_COMMAND_SLOTS_PER_HOST;
+
+ if(hostdata->slots[j].state != NCR_700_SLOT_QUEUED)
+ continue;
+ if(NCR_700_start_command(hostdata->slots[j].cmnd)) {
+ DEBUG(("scsi%d: Issuing saved command slot %p, cmd %p\t\n",
+ host->host_no, &hostdata->slots[j],
+ hostdata->slots[j].cmnd));
+ hostdata->saved_slot_position = j + 1;
+ }
+
+ break;
+ }
+ }
+ out_unlock:
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return IRQ_RETVAL(handled);
+}
+
+STATIC int
+NCR_700_queuecommand(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *))
+{
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
+ __u32 move_ins;
+ enum dma_data_direction direction;
+ struct NCR_700_command_slot *slot;
+
+ if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) {
+ /* We're over our allocation, this should never happen
+ * since we report the max allocation to the mid layer */
+ printk(KERN_WARNING "scsi%d: Command depth has gone over queue depth\n", SCp->device->host->host_no);
+ return 1;
+ }
+ /* check for untagged commands. We cannot have any outstanding
+ * commands if we accept them. Commands could be untagged because:
+ *
+ * - The tag negotiated bitmap is clear
+ * - The blk layer sent and untagged command
+ */
+ if(NCR_700_get_depth(SCp->device) != 0
+ && (!(hostdata->tag_negotiated & (1<<SCp->device->id))
+ || !blk_rq_tagged(SCp->request))) {
+ DEBUG((KERN_ERR "scsi%d (%d:%d) has non zero depth %d\n",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun,
+ NCR_700_get_depth(SCp->device)));
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ if(NCR_700_get_depth(SCp->device) >= SCp->device->queue_depth) {
+ DEBUG((KERN_ERR "scsi%d (%d:%d) has max tag depth %d\n",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun,
+ NCR_700_get_depth(SCp->device)));
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) + 1);
+
+ /* begin the command here */
+ /* no need to check for NULL, test for command_slot_count above
+ * ensures a slot is free */
+ slot = find_empty_slot(hostdata);
+
+ slot->cmnd = SCp;
+
+ SCp->scsi_done = done;
+ SCp->host_scribble = (unsigned char *)slot;
+ SCp->SCp.ptr = NULL;
+ SCp->SCp.buffer = NULL;
+
+#ifdef NCR_700_DEBUG
+ printk("53c700: scsi%d, command ", SCp->device->host->host_no);
+ scsi_print_command(SCp);
+#endif
+ if(blk_rq_tagged(SCp->request)
+ && (hostdata->tag_negotiated &(1<<SCp->device->id)) == 0
+ && NCR_700_get_tag_neg_state(SCp->device) == NCR_700_START_TAG_NEGOTIATION) {
+ printk(KERN_ERR "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", SCp->device->host->host_no, SCp->device->id, SCp->device->lun);
+ hostdata->tag_negotiated |= (1<<SCp->device->id);
+ NCR_700_set_tag_neg_state(SCp->device, NCR_700_DURING_TAG_NEGOTIATION);
+ }
+
+ /* here we may have to process an untagged command. The gate
+ * above ensures that this will be the only one outstanding,
+ * so clear the tag negotiated bit.
+ *
+ * FIXME: This will royally screw up on multiple LUN devices
+ * */
+ if(!blk_rq_tagged(SCp->request)
+ && (hostdata->tag_negotiated &(1<<SCp->device->id))) {
+ printk(KERN_INFO "scsi%d: (%d:%d) Disabling Tag Command Queuing\n", SCp->device->host->host_no, SCp->device->id, SCp->device->lun);
+ hostdata->tag_negotiated &= ~(1<<SCp->device->id);
+ }
+
+ if((hostdata->tag_negotiated &(1<<SCp->device->id))
+ && scsi_get_tag_type(SCp->device)) {
+ slot->tag = SCp->request->tag;
+ DEBUG(("53c700 %d:%d:%d, sending out tag %d, slot %p\n",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun, slot->tag,
+ slot));
+ } else {
+ slot->tag = SCSI_NO_TAG;
+ /* must populate current_cmnd for scsi_find_tag to work */
+ SCp->device->current_cmnd = SCp;
+ }
+ /* sanity check: some of the commands generated by the mid-layer
+ * have an eccentric idea of their sc_data_direction */
+ if(!SCp->use_sg && !SCp->request_bufflen
+ && SCp->sc_data_direction != DMA_NONE) {
+#ifdef NCR_700_DEBUG
+ printk("53c700: Command");
+ scsi_print_command(SCp);
+ printk("Has wrong data direction %d\n", SCp->sc_data_direction);
+#endif
+ SCp->sc_data_direction = DMA_NONE;
+ }
+
+ switch (SCp->cmnd[0]) {
+ case REQUEST_SENSE:
+ /* clear the internal sense magic */
+ SCp->cmnd[6] = 0;
+ /* fall through */
+ default:
+ /* OK, get it from the command */
+ switch(SCp->sc_data_direction) {
+ case DMA_BIDIRECTIONAL:
+ default:
+ printk(KERN_ERR "53c700: Unknown command for data direction ");
+ scsi_print_command(SCp);
+
+ move_ins = 0;
+ break;
+ case DMA_NONE:
+ move_ins = 0;
+ break;
+ case DMA_FROM_DEVICE:
+ move_ins = SCRIPT_MOVE_DATA_IN;
+ break;
+ case DMA_TO_DEVICE:
+ move_ins = SCRIPT_MOVE_DATA_OUT;
+ break;
+ }
+ }
+
+ /* now build the scatter gather list */
+ direction = SCp->sc_data_direction;
+ if(move_ins != 0) {
+ int i;
+ int sg_count;
+ dma_addr_t vPtr = 0;
+ __u32 count = 0;
+
+ if(SCp->use_sg) {
+ sg_count = dma_map_sg(hostdata->dev, SCp->buffer,
+ SCp->use_sg, direction);
+ } else {
+ vPtr = dma_map_single(hostdata->dev,
+ SCp->request_buffer,
+ SCp->request_bufflen,
+ direction);
+ count = SCp->request_bufflen;
+ slot->dma_handle = vPtr;
+ sg_count = 1;
+ }
+
+
+ for(i = 0; i < sg_count; i++) {
+
+ if(SCp->use_sg) {
+ struct scatterlist *sg = SCp->buffer;
+
+ vPtr = sg_dma_address(&sg[i]);
+ count = sg_dma_len(&sg[i]);
+ }
+
+ slot->SG[i].ins = bS_to_host(move_ins | count);
+ DEBUG((" scatter block %d: move %d[%08x] from 0x%lx\n",
+ i, count, slot->SG[i].ins, (unsigned long)vPtr));
+ slot->SG[i].pAddr = bS_to_host(vPtr);
+ }
+ slot->SG[i].ins = bS_to_host(SCRIPT_RETURN);
+ slot->SG[i].pAddr = 0;
+ dma_cache_sync(slot->SG, sizeof(slot->SG), DMA_TO_DEVICE);
+ DEBUG((" SETTING %08lx to %x\n",
+ (&slot->pSG[i].ins),
+ slot->SG[i].ins));
+ }
+ slot->resume_offset = 0;
+ slot->pCmd = dma_map_single(hostdata->dev, SCp->cmnd,
+ sizeof(SCp->cmnd), DMA_TO_DEVICE);
+ NCR_700_start_command(SCp);
+ return 0;
+}
+
+STATIC int
+NCR_700_abort(struct scsi_cmnd * SCp)
+{
+ struct NCR_700_command_slot *slot;
+
+ printk(KERN_INFO "scsi%d (%d:%d) New error handler wants to abort command\n\t",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun);
+ scsi_print_command(SCp);
+
+ slot = (struct NCR_700_command_slot *)SCp->host_scribble;
+
+ if(slot == NULL)
+ /* no outstanding command to abort */
+ return SUCCESS;
+ if(SCp->cmnd[0] == TEST_UNIT_READY) {
+ /* FIXME: This is because of a problem in the new
+ * error handler. When it is in error recovery, it
+ * will send a TUR to a device it thinks may still be
+ * showing a problem. If the TUR isn't responded to,
+ * it will abort it and mark the device off line.
+ * Unfortunately, it does no other error recovery, so
+ * this would leave us with an outstanding command
+ * occupying a slot. Rather than allow this to
+ * happen, we issue a bus reset to force all
+ * outstanding commands to terminate here. */
+ NCR_700_internal_bus_reset(SCp->device->host);
+ /* still drop through and return failed */
+ }
+ return FAILED;
+
+}
+
+STATIC int
+NCR_700_bus_reset(struct scsi_cmnd * SCp)
+{
+ DECLARE_COMPLETION(complete);
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
+
+ printk(KERN_INFO "scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun, SCp);
+ scsi_print_command(SCp);
+ /* In theory, eh_complete should always be null because the
+ * eh is single threaded, but just in case we're handling a
+ * reset via sg or something */
+ while(hostdata->eh_complete != NULL) {
+ spin_unlock_irq(SCp->device->host->host_lock);
+ msleep_interruptible(100);
+ spin_lock_irq(SCp->device->host->host_lock);
+ }
+ hostdata->eh_complete = &complete;
+ NCR_700_internal_bus_reset(SCp->device->host);
+ spin_unlock_irq(SCp->device->host->host_lock);
+ wait_for_completion(&complete);
+ spin_lock_irq(SCp->device->host->host_lock);
+ hostdata->eh_complete = NULL;
+ /* Revalidate the transport parameters of the failing device */
+ if(hostdata->fast)
+ spi_schedule_dv_device(SCp->device);
+ return SUCCESS;
+}
+
+STATIC int
+NCR_700_dev_reset(struct scsi_cmnd * SCp)
+{
+ printk(KERN_INFO "scsi%d (%d:%d) New error handler wants device reset\n\t",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun);
+ scsi_print_command(SCp);
+
+ return FAILED;
+}
+
+STATIC int
+NCR_700_host_reset(struct scsi_cmnd * SCp)
+{
+ printk(KERN_INFO "scsi%d (%d:%d) New error handler wants HOST reset\n\t",
+ SCp->device->host->host_no, SCp->device->id, SCp->device->lun);
+ scsi_print_command(SCp);
+
+ NCR_700_internal_bus_reset(SCp->device->host);
+ NCR_700_chip_reset(SCp->device->host);
+ return SUCCESS;
+}
+
+STATIC void
+NCR_700_set_period(struct scsi_target *STp, int period)
+{
+ struct Scsi_Host *SHp = dev_to_shost(STp->dev.parent);
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SHp->hostdata[0];
+
+ if(!hostdata->fast)
+ return;
+
+ if(period < hostdata->min_period)
+ period = hostdata->min_period;
+
+ spi_period(STp) = period;
+ spi_flags(STp) &= ~(NCR_700_DEV_NEGOTIATED_SYNC |
+ NCR_700_DEV_BEGIN_SYNC_NEGOTIATION);
+ spi_flags(STp) |= NCR_700_DEV_PRINT_SYNC_NEGOTIATION;
+}
+
+STATIC void
+NCR_700_set_offset(struct scsi_target *STp, int offset)
+{
+ struct Scsi_Host *SHp = dev_to_shost(STp->dev.parent);
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SHp->hostdata[0];
+ int max_offset = hostdata->chip710
+ ? NCR_710_MAX_OFFSET : NCR_700_MAX_OFFSET;
+
+ if(!hostdata->fast)
+ return;
+
+ if(offset > max_offset)
+ offset = max_offset;
+
+ /* if we're currently async, make sure the period is reasonable */
+ if(spi_offset(STp) == 0 && (spi_period(STp) < hostdata->min_period ||
+ spi_period(STp) > 0xff))
+ spi_period(STp) = hostdata->min_period;
+
+ spi_offset(STp) = offset;
+ spi_flags(STp) &= ~(NCR_700_DEV_NEGOTIATED_SYNC |
+ NCR_700_DEV_BEGIN_SYNC_NEGOTIATION);
+ spi_flags(STp) |= NCR_700_DEV_PRINT_SYNC_NEGOTIATION;
+}
+
+
+
+STATIC int
+NCR_700_slave_configure(struct scsi_device *SDp)
+{
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0];
+
+ /* to do here: allocate memory; build a queue_full list */
+ if(SDp->tagged_supported) {
+ scsi_set_tag_type(SDp, MSG_ORDERED_TAG);
+ scsi_activate_tcq(SDp, NCR_700_DEFAULT_TAGS);
+ NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION);
+ } else {
+ /* initialise to default depth */
+ scsi_adjust_queue_depth(SDp, 0, SDp->host->cmd_per_lun);
+ }
+ if(hostdata->fast) {
+ /* Find the correct offset and period via domain validation */
+ if (!spi_initial_dv(SDp->sdev_target))
+ spi_dv_device(SDp);
+ } else {
+ spi_offset(SDp->sdev_target) = 0;
+ spi_period(SDp->sdev_target) = 0;
+ }
+ return 0;
+}
+
+STATIC void
+NCR_700_slave_destroy(struct scsi_device *SDp)
+{
+ /* to do here: deallocate memory */
+}
+
+static int
+NCR_700_change_queue_depth(struct scsi_device *SDp, int depth)
+{
+ if (depth > NCR_700_MAX_TAGS)
+ depth = NCR_700_MAX_TAGS;
+
+ scsi_adjust_queue_depth(SDp, scsi_get_tag_type(SDp), depth);
+ return depth;
+}
+
+static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type)
+{
+ int change_tag = ((tag_type ==0 && scsi_get_tag_type(SDp) != 0)
+ || (tag_type != 0 && scsi_get_tag_type(SDp) == 0));
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0];
+
+ scsi_set_tag_type(SDp, tag_type);
+
+ /* We have a global (per target) flag to track whether TCQ is
+ * enabled, so we'll be turning it off for the entire target here.
+ * our tag algorithm will fail if we mix tagged and untagged commands,
+ * so quiesce the device before doing this */
+ if (change_tag)
+ scsi_target_quiesce(SDp->sdev_target);
+
+ if (!tag_type) {
+ /* shift back to the default unqueued number of commands
+ * (the user can still raise this) */
+ scsi_deactivate_tcq(SDp, SDp->host->cmd_per_lun);
+ hostdata->tag_negotiated &= ~(1 << SDp->id);
+ } else {
+ /* Here, we cleared the negotiation flag above, so this
+ * will force the driver to renegotiate */
+ scsi_activate_tcq(SDp, SDp->queue_depth);
+ if (change_tag)
+ NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION);
+ }
+ if (change_tag)
+ scsi_target_resume(SDp->sdev_target);
+
+ return tag_type;
+}
+
+static ssize_t
+NCR_700_show_active_tags(struct device *dev, char *buf)
+{
+ struct scsi_device *SDp = to_scsi_device(dev);
+
+ return snprintf(buf, 20, "%d\n", NCR_700_get_depth(SDp));
+}
+
+static struct device_attribute NCR_700_active_tags_attr = {
+ .attr = {
+ .name = "active_tags",
+ .mode = S_IRUGO,
+ },
+ .show = NCR_700_show_active_tags,
+};
+
+STATIC struct device_attribute *NCR_700_dev_attrs[] = {
+ &NCR_700_active_tags_attr,
+ NULL,
+};
+
+EXPORT_SYMBOL(NCR_700_detect);
+EXPORT_SYMBOL(NCR_700_release);
+EXPORT_SYMBOL(NCR_700_intr);
+
+static struct spi_function_template NCR_700_transport_functions = {
+ .set_period = NCR_700_set_period,
+ .show_period = 1,
+ .set_offset = NCR_700_set_offset,
+ .show_offset = 1,
+};
+
+static int __init NCR_700_init(void)
+{
+ NCR_700_transport_template = spi_attach_transport(&NCR_700_transport_functions);
+ if(!NCR_700_transport_template)
+ return -ENODEV;
+ return 0;
+}
+
+static void __exit NCR_700_exit(void)
+{
+ spi_release_transport(NCR_700_transport_template);
+}
+
+module_init(NCR_700_init);
+module_exit(NCR_700_exit);
+
diff --git a/drivers/scsi/53c700.h b/drivers/scsi/53c700.h
new file mode 100644
index 000000000000..df4aa30ae0aa
--- /dev/null
+++ b/drivers/scsi/53c700.h
@@ -0,0 +1,649 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* Driver for 53c700 and 53c700-66 chips from NCR and Symbios
+ *
+ * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com
+ */
+
+#ifndef _53C700_H
+#define _53C700_H
+
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include <scsi/scsi_device.h>
+
+
+#if defined(CONFIG_53C700_MEM_MAPPED) && defined(CONFIG_53C700_IO_MAPPED)
+#define CONFIG_53C700_BOTH_MAPPED
+#endif
+
+/* Turn on for general debugging---too verbose for normal use */
+#undef NCR_700_DEBUG
+/* Debug the tag queues, checking hash queue allocation and deallocation
+ * and search for duplicate tags */
+#undef NCR_700_TAG_DEBUG
+
+#ifdef NCR_700_DEBUG
+#define DEBUG(x) printk x
+#else
+#define DEBUG(x)
+#endif
+
+/* The number of available command slots */
+#define NCR_700_COMMAND_SLOTS_PER_HOST 64
+/* The maximum number of Scatter Gathers we allow */
+#define NCR_700_SG_SEGMENTS 32
+/* The maximum number of luns (make this of the form 2^n) */
+#define NCR_700_MAX_LUNS 32
+#define NCR_700_LUN_MASK (NCR_700_MAX_LUNS - 1)
+/* Maximum number of tags the driver ever allows per device */
+#define NCR_700_MAX_TAGS 16
+/* Tag depth the driver starts out with (can be altered in sysfs) */
+#define NCR_700_DEFAULT_TAGS 4
+/* This is the default number of commands per LUN in the untagged case.
+ * two is a good value because it means we can have one command active and
+ * one command fully prepared and waiting
+ */
+#define NCR_700_CMD_PER_LUN 2
+/* magic byte identifying an internally generated REQUEST_SENSE command */
+#define NCR_700_INTERNAL_SENSE_MAGIC 0x42
+
+/* WARNING: Leave this in for now: the dependency preprocessor doesn't
+ * pick up file specific flags, so must define here if they are not
+ * set */
+#if !defined(CONFIG_53C700_IO_MAPPED) && !defined(CONFIG_53C700_MEM_MAPPED)
+#error "Config.in must define either CONFIG_53C700_IO_MAPPED or CONFIG_53C700_MEM_MAPPED to use this scsi core."
+#endif
+
+struct NCR_700_Host_Parameters;
+
+/* These are the externally used routines */
+struct Scsi_Host *NCR_700_detect(struct scsi_host_template *,
+ struct NCR_700_Host_Parameters *, struct device *);
+int NCR_700_release(struct Scsi_Host *host);
+irqreturn_t NCR_700_intr(int, void *, struct pt_regs *);
+
+
+enum NCR_700_Host_State {
+ NCR_700_HOST_BUSY,
+ NCR_700_HOST_FREE,
+};
+
+struct NCR_700_SG_List {
+ /* The following is a script fragment to move the buffer onto the
+ * bus and then link the next fragment or return */
+ #define SCRIPT_MOVE_DATA_IN 0x09000000
+ #define SCRIPT_MOVE_DATA_OUT 0x08000000
+ __u32 ins;
+ __u32 pAddr;
+ #define SCRIPT_NOP 0x80000000
+ #define SCRIPT_RETURN 0x90080000
+};
+
+/* We use device->hostdata to store negotiated parameters. This is
+ * supposed to be a pointer to a device private area, but we cannot
+ * really use it as such since it will never be freed, so just use the
+ * 32 bits to cram the information. The SYNC negotiation sequence looks
+ * like:
+ *
+ * If DEV_NEGOTIATED_SYNC not set, tack and SDTR message on to the
+ * initial identify for the device and set DEV_BEGIN_SYNC_NEGOTATION
+ * If we get an SDTR reply, work out the SXFER parameters, squirrel
+ * them away here, clear DEV_BEGIN_SYNC_NEGOTIATION and set
+ * DEV_NEGOTIATED_SYNC. If we get a REJECT msg, squirrel
+ *
+ *
+ * 0:7 SXFER_REG negotiated value for this device
+ * 8:15 Current queue depth
+ * 16 negotiated SYNC flag
+ * 17 begin SYNC negotiation flag
+ * 18 device supports tag queueing */
+#define NCR_700_DEV_NEGOTIATED_SYNC (1<<16)
+#define NCR_700_DEV_BEGIN_SYNC_NEGOTIATION (1<<17)
+#define NCR_700_DEV_PRINT_SYNC_NEGOTIATION (1<<19)
+
+static inline void
+NCR_700_set_depth(struct scsi_device *SDp, __u8 depth)
+{
+ long l = (long)SDp->hostdata;
+
+ l &= 0xffff00ff;
+ l |= 0xff00 & (depth << 8);
+ SDp->hostdata = (void *)l;
+}
+static inline __u8
+NCR_700_get_depth(struct scsi_device *SDp)
+{
+ return ((((unsigned long)SDp->hostdata) & 0xff00)>>8);
+}
+static inline int
+NCR_700_is_flag_set(struct scsi_device *SDp, __u32 flag)
+{
+ return (spi_flags(SDp->sdev_target) & flag) == flag;
+}
+static inline int
+NCR_700_is_flag_clear(struct scsi_device *SDp, __u32 flag)
+{
+ return (spi_flags(SDp->sdev_target) & flag) == 0;
+}
+static inline void
+NCR_700_set_flag(struct scsi_device *SDp, __u32 flag)
+{
+ spi_flags(SDp->sdev_target) |= flag;
+}
+static inline void
+NCR_700_clear_flag(struct scsi_device *SDp, __u32 flag)
+{
+ spi_flags(SDp->sdev_target) &= ~flag;
+}
+
+enum NCR_700_tag_neg_state {
+ NCR_700_START_TAG_NEGOTIATION = 0,
+ NCR_700_DURING_TAG_NEGOTIATION = 1,
+ NCR_700_FINISHED_TAG_NEGOTIATION = 2,
+};
+
+static inline enum NCR_700_tag_neg_state
+NCR_700_get_tag_neg_state(struct scsi_device *SDp)
+{
+ return (enum NCR_700_tag_neg_state)((spi_flags(SDp->sdev_target)>>20) & 0x3);
+}
+
+static inline void
+NCR_700_set_tag_neg_state(struct scsi_device *SDp,
+ enum NCR_700_tag_neg_state state)
+{
+ /* clear the slot */
+ spi_flags(SDp->sdev_target) &= ~(0x3 << 20);
+ spi_flags(SDp->sdev_target) |= ((__u32)state) << 20;
+}
+
+struct NCR_700_command_slot {
+ struct NCR_700_SG_List SG[NCR_700_SG_SEGMENTS+1];
+ struct NCR_700_SG_List *pSG;
+ #define NCR_700_SLOT_MASK 0xFC
+ #define NCR_700_SLOT_MAGIC 0xb8
+ #define NCR_700_SLOT_FREE (0|NCR_700_SLOT_MAGIC) /* slot may be used */
+ #define NCR_700_SLOT_BUSY (1|NCR_700_SLOT_MAGIC) /* slot has command active on HA */
+ #define NCR_700_SLOT_QUEUED (2|NCR_700_SLOT_MAGIC) /* slot has command to be made active on HA */
+ __u8 state;
+ int tag;
+ __u32 resume_offset;
+ struct scsi_cmnd *cmnd;
+ /* The pci_mapped address of the actual command in cmnd */
+ dma_addr_t pCmd;
+ __u32 temp;
+ /* if this command is a pci_single mapping, holds the dma address
+ * for later unmapping in the done routine */
+ dma_addr_t dma_handle;
+ /* historical remnant, now used to link free commands */
+ struct NCR_700_command_slot *ITL_forw;
+};
+
+struct NCR_700_Host_Parameters {
+ /* These must be filled in by the calling driver */
+ int clock; /* board clock speed in MHz */
+ unsigned long base; /* the base for the port (copied to host) */
+ struct device *dev;
+ __u32 dmode_extra; /* adjustable bus settings */
+ __u32 differential:1; /* if we are differential */
+#ifdef CONFIG_53C700_LE_ON_BE
+ /* This option is for HP only. Set it if your chip is wired for
+ * little endian on this platform (which is big endian) */
+ __u32 force_le_on_be:1;
+#endif
+ __u32 chip710:1; /* set if really a 710 not 700 */
+ __u32 burst_disable:1; /* set to 1 to disable 710 bursting */
+
+ /* NOTHING BELOW HERE NEEDS ALTERING */
+ __u32 fast:1; /* if we can alter the SCSI bus clock
+ speed (so can negiotiate sync) */
+#ifdef CONFIG_53C700_BOTH_MAPPED
+ __u32 mem_mapped; /* set if memory mapped */
+#endif
+ int sync_clock; /* The speed of the SYNC core */
+
+ __u32 *script; /* pointer to script location */
+ __u32 pScript; /* physical mem addr of script */
+
+ enum NCR_700_Host_State state; /* protected by state lock */
+ struct scsi_cmnd *cmd;
+ /* Note: pScript contains the single consistent block of
+ * memory. All the msgin, msgout and status are allocated in
+ * this memory too (at separate cache lines). TOTAL_MEM_SIZE
+ * represents the total size of this area */
+#define MSG_ARRAY_SIZE 8
+#define MSGOUT_OFFSET (L1_CACHE_ALIGN(sizeof(SCRIPT)))
+ __u8 *msgout;
+#define MSGIN_OFFSET (MSGOUT_OFFSET + L1_CACHE_ALIGN(MSG_ARRAY_SIZE))
+ __u8 *msgin;
+#define STATUS_OFFSET (MSGIN_OFFSET + L1_CACHE_ALIGN(MSG_ARRAY_SIZE))
+ __u8 *status;
+#define SLOTS_OFFSET (STATUS_OFFSET + L1_CACHE_ALIGN(MSG_ARRAY_SIZE))
+ struct NCR_700_command_slot *slots;
+#define TOTAL_MEM_SIZE (SLOTS_OFFSET + L1_CACHE_ALIGN(sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST))
+ int saved_slot_position;
+ int command_slot_count; /* protected by state lock */
+ __u8 tag_negotiated;
+ __u8 rev;
+ __u8 reselection_id;
+ __u8 min_period;
+
+ /* Free list, singly linked by ITL_forw elements */
+ struct NCR_700_command_slot *free_list;
+ /* Completion for waited for ops, like reset, abort or
+ * device reset.
+ *
+ * NOTE: relies on single threading in the error handler to
+ * have only one outstanding at once */
+ struct completion *eh_complete;
+};
+
+/*
+ * 53C700 Register Interface - the offset from the Selected base
+ * I/O address */
+#ifdef CONFIG_53C700_LE_ON_BE
+#define bE (hostdata->force_le_on_be ? 0 : 3)
+#define bSWAP (hostdata->force_le_on_be)
+#elif defined(__BIG_ENDIAN)
+#define bE 3
+#define bSWAP 0
+#elif defined(__LITTLE_ENDIAN)
+#define bE 0
+#define bSWAP 0
+#else
+#error "__BIG_ENDIAN or __LITTLE_ENDIAN must be defined, did you include byteorder.h?"
+#endif
+#define bS_to_cpu(x) (bSWAP ? le32_to_cpu(x) : (x))
+#define bS_to_host(x) (bSWAP ? cpu_to_le32(x) : (x))
+
+/* NOTE: These registers are in the LE register space only, the required byte
+ * swapping is done by the NCR_700_{read|write}[b] functions */
+#define SCNTL0_REG 0x00
+#define FULL_ARBITRATION 0xc0
+#define PARITY 0x08
+#define ENABLE_PARITY 0x04
+#define AUTO_ATN 0x02
+#define SCNTL1_REG 0x01
+#define SLOW_BUS 0x80
+#define ENABLE_SELECT 0x20
+#define ASSERT_RST 0x08
+#define ASSERT_EVEN_PARITY 0x04
+#define SDID_REG 0x02
+#define SIEN_REG 0x03
+#define PHASE_MM_INT 0x80
+#define FUNC_COMP_INT 0x40
+#define SEL_TIMEOUT_INT 0x20
+#define SELECT_INT 0x10
+#define GROSS_ERR_INT 0x08
+#define UX_DISC_INT 0x04
+#define RST_INT 0x02
+#define PAR_ERR_INT 0x01
+#define SCID_REG 0x04
+#define SXFER_REG 0x05
+#define ASYNC_OPERATION 0x00
+#define SODL_REG 0x06
+#define SOCL_REG 0x07
+#define SFBR_REG 0x08
+#define SIDL_REG 0x09
+#define SBDL_REG 0x0A
+#define SBCL_REG 0x0B
+/* read bits */
+#define SBCL_IO 0x01
+/*write bits */
+#define SYNC_DIV_AS_ASYNC 0x00
+#define SYNC_DIV_1_0 0x01
+#define SYNC_DIV_1_5 0x02
+#define SYNC_DIV_2_0 0x03
+#define DSTAT_REG 0x0C
+#define ILGL_INST_DETECTED 0x01
+#define WATCH_DOG_INTERRUPT 0x02
+#define SCRIPT_INT_RECEIVED 0x04
+#define ABORTED 0x10
+#define SSTAT0_REG 0x0D
+#define PARITY_ERROR 0x01
+#define SCSI_RESET_DETECTED 0x02
+#define UNEXPECTED_DISCONNECT 0x04
+#define SCSI_GROSS_ERROR 0x08
+#define SELECTED 0x10
+#define SELECTION_TIMEOUT 0x20
+#define FUNCTION_COMPLETE 0x40
+#define PHASE_MISMATCH 0x80
+#define SSTAT1_REG 0x0E
+#define SIDL_REG_FULL 0x80
+#define SODR_REG_FULL 0x40
+#define SODL_REG_FULL 0x20
+#define SSTAT2_REG 0x0F
+#define CTEST0_REG 0x14
+#define BTB_TIMER_DISABLE 0x40
+#define CTEST1_REG 0x15
+#define CTEST2_REG 0x16
+#define CTEST3_REG 0x17
+#define CTEST4_REG 0x18
+#define DISABLE_FIFO 0x00
+#define SLBE 0x10
+#define SFWR 0x08
+#define BYTE_LANE0 0x04
+#define BYTE_LANE1 0x05
+#define BYTE_LANE2 0x06
+#define BYTE_LANE3 0x07
+#define SCSI_ZMODE 0x20
+#define ZMODE 0x40
+#define CTEST5_REG 0x19
+#define MASTER_CONTROL 0x10
+#define DMA_DIRECTION 0x08
+#define CTEST7_REG 0x1B
+#define BURST_DISABLE 0x80 /* 710 only */
+#define SEL_TIMEOUT_DISABLE 0x10 /* 710 only */
+#define DFP 0x08
+#define EVP 0x04
+#define DIFF 0x01
+#define CTEST6_REG 0x1A
+#define TEMP_REG 0x1C
+#define DFIFO_REG 0x20
+#define FLUSH_DMA_FIFO 0x80
+#define CLR_FIFO 0x40
+#define ISTAT_REG 0x21
+#define ABORT_OPERATION 0x80
+#define SOFTWARE_RESET_710 0x40
+#define DMA_INT_PENDING 0x01
+#define SCSI_INT_PENDING 0x02
+#define CONNECTED 0x08
+#define CTEST8_REG 0x22
+#define LAST_DIS_ENBL 0x01
+#define SHORTEN_FILTERING 0x04
+#define ENABLE_ACTIVE_NEGATION 0x10
+#define GENERATE_RECEIVE_PARITY 0x20
+#define CLR_FIFO_710 0x04
+#define FLUSH_DMA_FIFO_710 0x08
+#define CTEST9_REG 0x23
+#define DBC_REG 0x24
+#define DCMD_REG 0x27
+#define DNAD_REG 0x28
+#define DIEN_REG 0x39
+#define BUS_FAULT 0x20
+#define ABORT_INT 0x10
+#define INT_INST_INT 0x04
+#define WD_INT 0x02
+#define ILGL_INST_INT 0x01
+#define DCNTL_REG 0x3B
+#define SOFTWARE_RESET 0x01
+#define COMPAT_700_MODE 0x01
+#define SCRPTS_16BITS 0x20
+#define ASYNC_DIV_2_0 0x00
+#define ASYNC_DIV_1_5 0x40
+#define ASYNC_DIV_1_0 0x80
+#define ASYNC_DIV_3_0 0xc0
+#define DMODE_710_REG 0x38
+#define DMODE_700_REG 0x34
+#define BURST_LENGTH_1 0x00
+#define BURST_LENGTH_2 0x40
+#define BURST_LENGTH_4 0x80
+#define BURST_LENGTH_8 0xC0
+#define DMODE_FC1 0x10
+#define DMODE_FC2 0x20
+#define BW16 32
+#define MODE_286 16
+#define IO_XFER 8
+#define FIXED_ADDR 4
+
+#define DSP_REG 0x2C
+#define DSPS_REG 0x30
+
+/* Parameters to begin SDTR negotiations. Empirically, I find that
+ * the 53c700-66 cannot handle an offset >8, so don't change this */
+#define NCR_700_MAX_OFFSET 8
+/* Was hoping the max offset would be greater for the 710, but
+ * empirically it seems to be 8 also */
+#define NCR_710_MAX_OFFSET 8
+#define NCR_700_MIN_XFERP 1
+#define NCR_710_MIN_XFERP 0
+#define NCR_700_MIN_PERIOD 25 /* for SDTR message, 100ns */
+
+#define script_patch_32(script, symbol, value) \
+{ \
+ int i; \
+ for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
+ __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]) + value; \
+ (script)[A_##symbol##_used[i]] = bS_to_host(val); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
+ DEBUG((" script, patching %s at %d to 0x%lx\n", \
+ #symbol, A_##symbol##_used[i], (value))); \
+ } \
+}
+
+#define script_patch_32_abs(script, symbol, value) \
+{ \
+ int i; \
+ for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
+ (script)[A_##symbol##_used[i]] = bS_to_host(value); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
+ DEBUG((" script, patching %s at %d to 0x%lx\n", \
+ #symbol, A_##symbol##_used[i], (value))); \
+ } \
+}
+
+/* Used for patching the SCSI ID in the SELECT instruction */
+#define script_patch_ID(script, symbol, value) \
+{ \
+ int i; \
+ for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
+ __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]); \
+ val &= 0xff00ffff; \
+ val |= ((value) & 0xff) << 16; \
+ (script)[A_##symbol##_used[i]] = bS_to_host(val); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
+ DEBUG((" script, patching ID field %s at %d to 0x%x\n", \
+ #symbol, A_##symbol##_used[i], val)); \
+ } \
+}
+
+#define script_patch_16(script, symbol, value) \
+{ \
+ int i; \
+ for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \
+ __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]); \
+ val &= 0xffff0000; \
+ val |= ((value) & 0xffff); \
+ (script)[A_##symbol##_used[i]] = bS_to_host(val); \
+ dma_cache_sync(&(script)[A_##symbol##_used[i]], 4, DMA_TO_DEVICE); \
+ DEBUG((" script, patching short field %s at %d to 0x%x\n", \
+ #symbol, A_##symbol##_used[i], val)); \
+ } \
+}
+
+
+static inline __u8
+NCR_700_mem_readb(struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ return readb(host->base + (reg^bE));
+}
+
+static inline __u32
+NCR_700_mem_readl(struct Scsi_Host *host, __u32 reg)
+{
+ __u32 value = __raw_readl(host->base + reg);
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+#if 1
+ /* sanity check the register */
+ if((reg & 0x3) != 0)
+ BUG();
+#endif
+
+ return bS_to_cpu(value);
+}
+
+static inline void
+NCR_700_mem_writeb(__u8 value, struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ writeb(value, host->base + (reg^bE));
+}
+
+static inline void
+NCR_700_mem_writel(__u32 value, struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+#if 1
+ /* sanity check the register */
+ if((reg & 0x3) != 0)
+ BUG();
+#endif
+
+ __raw_writel(bS_to_host(value), host->base + reg);
+}
+
+static inline __u8
+NCR_700_io_readb(struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ return inb(host->base + (reg^bE));
+}
+
+static inline __u32
+NCR_700_io_readl(struct Scsi_Host *host, __u32 reg)
+{
+ __u32 value = inl(host->base + reg);
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+#if 1
+ /* sanity check the register */
+ if((reg & 0x3) != 0)
+ BUG();
+#endif
+
+ return bS_to_cpu(value);
+}
+
+static inline void
+NCR_700_io_writeb(__u8 value, struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ outb(value, host->base + (reg^bE));
+}
+
+static inline void
+NCR_700_io_writel(__u32 value, struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+#if 1
+ /* sanity check the register */
+ if((reg & 0x3) != 0)
+ BUG();
+#endif
+
+ outl(bS_to_host(value), host->base + reg);
+}
+
+#ifdef CONFIG_53C700_BOTH_MAPPED
+
+static inline __u8
+NCR_700_readb(struct Scsi_Host *host, __u32 reg)
+{
+ __u8 val;
+
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ if(hostdata->mem_mapped)
+ val = NCR_700_mem_readb(host, reg);
+ else
+ val = NCR_700_io_readb(host, reg);
+
+ return val;
+}
+
+static inline __u32
+NCR_700_readl(struct Scsi_Host *host, __u32 reg)
+{
+ __u32 val;
+
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ if(hostdata->mem_mapped)
+ val = NCR_700_mem_readl(host, reg);
+ else
+ val = NCR_700_io_readl(host, reg);
+
+ return val;
+}
+
+static inline void
+NCR_700_writeb(__u8 value, struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ if(hostdata->mem_mapped)
+ NCR_700_mem_writeb(value, host, reg);
+ else
+ NCR_700_io_writeb(value, host, reg);
+}
+
+static inline void
+NCR_700_writel(__u32 value, struct Scsi_Host *host, __u32 reg)
+{
+ const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
+ = (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ if(hostdata->mem_mapped)
+ NCR_700_mem_writel(value, host, reg);
+ else
+ NCR_700_io_writel(value, host, reg);
+}
+
+static inline void
+NCR_700_set_mem_mapped(struct NCR_700_Host_Parameters *hostdata)
+{
+ hostdata->mem_mapped = 1;
+}
+
+static inline void
+NCR_700_set_io_mapped(struct NCR_700_Host_Parameters *hostdata)
+{
+ hostdata->mem_mapped = 0;
+}
+
+
+#elif defined(CONFIG_53C700_IO_MAPPED)
+
+#define NCR_700_readb NCR_700_io_readb
+#define NCR_700_readl NCR_700_io_readl
+#define NCR_700_writeb NCR_700_io_writeb
+#define NCR_700_writel NCR_700_io_writel
+
+#define NCR_700_set_io_mapped(x)
+#define NCR_700_set_mem_mapped(x) error I/O mapped only
+
+#elif defined(CONFIG_53C700_MEM_MAPPED)
+
+#define NCR_700_readb NCR_700_mem_readb
+#define NCR_700_readl NCR_700_mem_readl
+#define NCR_700_writeb NCR_700_mem_writeb
+#define NCR_700_writel NCR_700_mem_writel
+
+#define NCR_700_set_io_mapped(x) error MEM mapped only
+#define NCR_700_set_mem_mapped(x)
+
+#else
+#error neither CONFIG_53C700_MEM_MAPPED nor CONFIG_53C700_IO_MAPPED is set
+#endif
+
+#endif
diff --git a/drivers/scsi/53c700.scr b/drivers/scsi/53c700.scr
new file mode 100644
index 000000000000..a064a092c604
--- /dev/null
+++ b/drivers/scsi/53c700.scr
@@ -0,0 +1,411 @@
+; Script for the NCR (or symbios) 53c700 and 53c700-66 chip
+;
+; Copyright (C) 2001 James.Bottomley@HansenPartnership.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+;;
+;;-----------------------------------------------------------------------------
+;
+; This script is designed to be modified for the particular command in
+; operation. The particular variables pertaining to the commands are:
+;
+ABSOLUTE Device_ID = 0 ; ID of target for command
+ABSOLUTE MessageCount = 0 ; Number of bytes in message
+ABSOLUTE MessageLocation = 0 ; Addr of message
+ABSOLUTE CommandCount = 0 ; Number of bytes in command
+ABSOLUTE CommandAddress = 0 ; Addr of Command
+ABSOLUTE StatusAddress = 0 ; Addr to receive status return
+ABSOLUTE ReceiveMsgAddress = 0 ; Addr to receive msg
+;
+; This is the magic component for handling scatter-gather. Each of the
+; SG components is preceeded by a script fragment which moves the
+; necessary amount of data and jumps to the next SG segment. The final
+; SG segment jumps back to . However, this address is the first SG script
+; segment.
+;
+ABSOLUTE SGScriptStartAddress = 0
+
+; The following represent status interrupts we use 3 hex digits for
+; this: 0xPRS where
+
+; P:
+ABSOLUTE AFTER_SELECTION = 0x100
+ABSOLUTE BEFORE_CMD = 0x200
+ABSOLUTE AFTER_CMD = 0x300
+ABSOLUTE AFTER_STATUS = 0x400
+ABSOLUTE AFTER_DATA_IN = 0x500
+ABSOLUTE AFTER_DATA_OUT = 0x600
+ABSOLUTE DURING_DATA_IN = 0x700
+
+; R:
+ABSOLUTE NOT_MSG_OUT = 0x10
+ABSOLUTE UNEXPECTED_PHASE = 0x20
+ABSOLUTE NOT_MSG_IN = 0x30
+ABSOLUTE UNEXPECTED_MSG = 0x40
+ABSOLUTE MSG_IN = 0x50
+ABSOLUTE SDTR_MSG_R = 0x60
+ABSOLUTE REJECT_MSG_R = 0x70
+ABSOLUTE DISCONNECT = 0x80
+ABSOLUTE MSG_OUT = 0x90
+ABSOLUTE WDTR_MSG_R = 0xA0
+
+; S:
+ABSOLUTE GOOD_STATUS = 0x1
+
+; Combinations, since the script assembler can't process |
+ABSOLUTE NOT_MSG_OUT_AFTER_SELECTION = 0x110
+ABSOLUTE UNEXPECTED_PHASE_BEFORE_CMD = 0x220
+ABSOLUTE UNEXPECTED_PHASE_AFTER_CMD = 0x320
+ABSOLUTE NOT_MSG_IN_AFTER_STATUS = 0x430
+ABSOLUTE GOOD_STATUS_AFTER_STATUS = 0x401
+ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520
+ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620
+ABSOLUTE UNEXPECTED_MSG_BEFORE_CMD = 0x240
+ABSOLUTE MSG_IN_BEFORE_CMD = 0x250
+ABSOLUTE MSG_IN_AFTER_CMD = 0x350
+ABSOLUTE SDTR_MSG_BEFORE_CMD = 0x260
+ABSOLUTE REJECT_MSG_BEFORE_CMD = 0x270
+ABSOLUTE DISCONNECT_AFTER_CMD = 0x380
+ABSOLUTE SDTR_MSG_AFTER_CMD = 0x360
+ABSOLUTE WDTR_MSG_AFTER_CMD = 0x3A0
+ABSOLUTE MSG_IN_AFTER_STATUS = 0x440
+ABSOLUTE DISCONNECT_AFTER_DATA = 0x580
+ABSOLUTE MSG_IN_AFTER_DATA_IN = 0x550
+ABSOLUTE MSG_IN_AFTER_DATA_OUT = 0x650
+ABSOLUTE MSG_OUT_AFTER_DATA_IN = 0x590
+ABSOLUTE DATA_IN_AFTER_DATA_IN = 0x5a0
+ABSOLUTE MSG_IN_DURING_DATA_IN = 0x750
+ABSOLUTE DISCONNECT_DURING_DATA = 0x780
+
+;
+; Other interrupt conditions
+;
+ABSOLUTE RESELECTED_DURING_SELECTION = 0x1000
+ABSOLUTE COMPLETED_SELECTION_AS_TARGET = 0x1001
+ABSOLUTE RESELECTION_IDENTIFIED = 0x1003
+;
+; Fatal interrupt conditions. If you add to this, also add to the
+; array of corresponding messages
+;
+ABSOLUTE FATAL = 0x2000
+ABSOLUTE FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000
+ABSOLUTE FATAL_SEND_MSG = 0x2001
+ABSOLUTE FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002
+ABSOLUTE FATAL_ILLEGAL_MSG_LENGTH = 0x2003
+
+ABSOLUTE DEBUG_INTERRUPT = 0x3000
+ABSOLUTE DEBUG_INTERRUPT1 = 0x3001
+ABSOLUTE DEBUG_INTERRUPT2 = 0x3002
+ABSOLUTE DEBUG_INTERRUPT3 = 0x3003
+ABSOLUTE DEBUG_INTERRUPT4 = 0x3004
+ABSOLUTE DEBUG_INTERRUPT5 = 0x3005
+ABSOLUTE DEBUG_INTERRUPT6 = 0x3006
+
+
+;
+; SCSI Messages we interpret in the script
+;
+ABSOLUTE COMMAND_COMPLETE_MSG = 0x00
+ABSOLUTE EXTENDED_MSG = 0x01
+ABSOLUTE SDTR_MSG = 0x01
+ABSOLUTE SAVE_DATA_PTRS_MSG = 0x02
+ABSOLUTE RESTORE_DATA_PTRS_MSG = 0x03
+ABSOLUTE WDTR_MSG = 0x03
+ABSOLUTE DISCONNECT_MSG = 0x04
+ABSOLUTE REJECT_MSG = 0x07
+ABSOLUTE PARITY_ERROR_MSG = 0x09
+ABSOLUTE SIMPLE_TAG_MSG = 0x20
+ABSOLUTE IDENTIFY_MSG = 0x80
+ABSOLUTE IDENTIFY_MSG_MASK = 0x7F
+ABSOLUTE TWO_BYTE_MSG = 0x20
+ABSOLUTE TWO_BYTE_MSG_MASK = 0x0F
+
+; This is where the script begins
+
+ENTRY StartUp
+
+StartUp:
+ SELECT ATN Device_ID, Reselect
+ JUMP Finish, WHEN STATUS
+ JUMP SendIdentifyMsg, IF MSG_OUT
+ INT NOT_MSG_OUT_AFTER_SELECTION
+
+Reselect:
+ WAIT RESELECT SelectedAsTarget
+ INT RESELECTED_DURING_SELECTION, WHEN MSG_IN
+ INT FATAL_NOT_MSG_IN_AFTER_SELECTION
+
+ ENTRY GetReselectionData
+GetReselectionData:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ INT RESELECTION_IDENTIFIED
+
+ ENTRY GetReselectionWithTag
+GetReselectionWithTag:
+ MOVE 3, ReceiveMsgAddress, WHEN MSG_IN
+ INT RESELECTION_IDENTIFIED
+
+ ENTRY SelectedAsTarget
+SelectedAsTarget:
+; Basically tell the selecting device that there's nothing here
+ SET TARGET
+ DISCONNECT
+ CLEAR TARGET
+ INT COMPLETED_SELECTION_AS_TARGET
+;
+; These are the messaging entries
+;
+; Send a message. Message count should be correctly patched
+ ENTRY SendMessage
+SendMessage:
+ MOVE MessageCount, MessageLocation, WHEN MSG_OUT
+ResumeSendMessage:
+ RETURN, WHEN NOT MSG_OUT
+ INT FATAL_SEND_MSG
+
+ ENTRY SendMessagePhaseMismatch
+SendMessagePhaseMismatch:
+ CLEAR ACK
+ JUMP ResumeSendMessage
+;
+; Receive a message. Need to identify the message to
+; receive it correctly
+ ENTRY ReceiveMessage
+ReceiveMessage:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+;
+; Use this entry if we've just tried to look at the first byte
+; of the message and want to process it further
+ProcessReceiveMessage:
+ JUMP ReceiveExtendedMessage, IF EXTENDED_MSG
+ RETURN, IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK
+ CLEAR ACK
+ MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN
+ RETURN
+ReceiveExtendedMessage:
+ CLEAR ACK
+ MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN
+ JUMP Receive1Byte, IF 0x01
+ JUMP Receive2Byte, IF 0x02
+ JUMP Receive3Byte, IF 0x03
+ JUMP Receive4Byte, IF 0x04
+ JUMP Receive5Byte, IF 0x05
+ INT FATAL_ILLEGAL_MSG_LENGTH
+Receive1Byte:
+ CLEAR ACK
+ MOVE 1, ReceiveMsgAddress + 2, WHEN MSG_IN
+ RETURN
+Receive2Byte:
+ CLEAR ACK
+ MOVE 2, ReceiveMsgAddress + 2, WHEN MSG_IN
+ RETURN
+Receive3Byte:
+ CLEAR ACK
+ MOVE 3, ReceiveMsgAddress + 2, WHEN MSG_IN
+ RETURN
+Receive4Byte:
+ CLEAR ACK
+ MOVE 4, ReceiveMsgAddress + 2, WHEN MSG_IN
+ RETURN
+Receive5Byte:
+ CLEAR ACK
+ MOVE 5, ReceiveMsgAddress + 2, WHEN MSG_IN
+ RETURN
+;
+; Come here from the message processor to ignore the message
+;
+ ENTRY IgnoreMessage
+IgnoreMessage:
+ CLEAR ACK
+ RETURN
+;
+; Come here to send a reply to a message
+;
+ ENTRY SendMessageWithATN
+SendMessageWithATN:
+ SET ATN
+ CLEAR ACK
+ JUMP SendMessage
+
+SendIdentifyMsg:
+ CALL SendMessage
+ CLEAR ATN
+
+IgnoreMsgBeforeCommand:
+ CLEAR ACK
+ ENTRY SendCommand
+SendCommand:
+ JUMP Finish, WHEN STATUS
+ JUMP MsgInBeforeCommand, IF MSG_IN
+ INT UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD
+ MOVE CommandCount, CommandAddress, WHEN CMD
+ResumeSendCommand:
+ JUMP Finish, WHEN STATUS
+ JUMP MsgInAfterCmd, IF MSG_IN
+ JUMP DataIn, IF DATA_IN
+ JUMP DataOut, IF DATA_OUT
+ INT UNEXPECTED_PHASE_AFTER_CMD
+
+IgnoreMsgDuringData:
+ CLEAR ACK
+ ; fall through to MsgInDuringData
+
+Entry MsgInDuringData
+MsgInDuringData:
+;
+; Could be we have nothing more to transfer
+;
+ JUMP Finish, WHEN STATUS
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ JUMP DisconnectDuringDataIn, IF DISCONNECT_MSG
+ JUMP IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG
+ JUMP IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG
+ INT MSG_IN_DURING_DATA_IN
+
+MsgInAfterCmd:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ JUMP DisconnectAfterCmd, IF DISCONNECT_MSG
+ JUMP IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG
+ JUMP IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG
+ CALL ProcessReceiveMessage
+ INT MSG_IN_AFTER_CMD
+ CLEAR ACK
+ JUMP ResumeSendCommand
+
+IgnoreMsgInAfterCmd:
+ CLEAR ACK
+ JUMP ResumeSendCommand
+
+DisconnectAfterCmd:
+ CLEAR ACK
+ WAIT DISCONNECT
+ ENTRY Disconnect1
+Disconnect1:
+ INT DISCONNECT_AFTER_CMD
+ ENTRY Disconnect2
+Disconnect2:
+; We return here after a reselection
+ CLEAR ACK
+ JUMP ResumeSendCommand
+
+MsgInBeforeCommand:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ JUMP IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG
+ JUMP IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG
+ CALL ProcessReceiveMessage
+ INT MSG_IN_BEFORE_CMD
+ CLEAR ACK
+ JUMP SendCommand
+
+DataIn:
+ CALL SGScriptStartAddress
+ResumeDataIn:
+ JUMP Finish, WHEN STATUS
+ JUMP MsgInAfterDataIn, IF MSG_IN
+ JUMP DataInAfterDataIn, if DATA_IN
+ INT MSG_OUT_AFTER_DATA_IN, if MSG_OUT
+ INT UNEXPECTED_PHASE_AFTER_DATA_IN
+
+DataInAfterDataIn:
+ INT DATA_IN_AFTER_DATA_IN
+ JUMP ResumeDataIn
+
+DataOut:
+ CALL SGScriptStartAddress
+ResumeDataOut:
+ JUMP Finish, WHEN STATUS
+ JUMP MsgInAfterDataOut, IF MSG_IN
+ INT UNEXPECTED_PHASE_AFTER_DATA_OUT
+
+MsgInAfterDataIn:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ JUMP DisconnectAfterDataIn, IF DISCONNECT_MSG
+ JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG
+ JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG
+ CALL ProcessReceiveMessage
+ INT MSG_IN_AFTER_DATA_IN
+ CLEAR ACK
+ JUMP ResumeDataIn
+
+DisconnectDuringDataIn:
+ CLEAR ACK
+ WAIT DISCONNECT
+ ENTRY Disconnect3
+Disconnect3:
+ INT DISCONNECT_DURING_DATA
+ ENTRY Disconnect4
+Disconnect4:
+; we return here after a reselection
+ CLEAR ACK
+ JUMP ResumeSendCommand
+
+
+DisconnectAfterDataIn:
+ CLEAR ACK
+ WAIT DISCONNECT
+ ENTRY Disconnect5
+Disconnect5:
+ INT DISCONNECT_AFTER_DATA
+ ENTRY Disconnect6
+Disconnect6:
+; we return here after a reselection
+ CLEAR ACK
+ JUMP ResumeDataIn
+
+MsgInAfterDataOut:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ JUMP DisconnectAfterDataOut, if DISCONNECT_MSG
+ JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG
+ JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG
+ CALL ProcessReceiveMessage
+ INT MSG_IN_AFTER_DATA_OUT
+ CLEAR ACK
+ JUMP ResumeDataOut
+
+IgnoreMsgAfterData:
+ CLEAR ACK
+; Data in and out do the same thing on resume, so pick one
+ JUMP ResumeDataIn
+
+DisconnectAfterDataOut:
+ CLEAR ACK
+ WAIT DISCONNECT
+ ENTRY Disconnect7
+Disconnect7:
+ INT DISCONNECT_AFTER_DATA
+ ENTRY Disconnect8
+Disconnect8:
+; we return here after a reselection
+ CLEAR ACK
+ JUMP ResumeDataOut
+
+Finish:
+ MOVE 1, StatusAddress, WHEN STATUS
+ INT NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+ JUMP FinishCommandComplete, IF COMMAND_COMPLETE_MSG
+ CALL ProcessReceiveMessage
+ INT MSG_IN_AFTER_STATUS
+ ENTRY FinishCommandComplete
+FinishCommandComplete:
+ CLEAR ACK
+ WAIT DISCONNECT
+ ENTRY Finish1
+Finish1:
+ INT GOOD_STATUS_AFTER_STATUS
+ ENTRY Finish2
+Finish2:
+
diff --git a/drivers/scsi/53c700_d.h_shipped b/drivers/scsi/53c700_d.h_shipped
new file mode 100644
index 000000000000..0b42a51257f2
--- /dev/null
+++ b/drivers/scsi/53c700_d.h_shipped
@@ -0,0 +1,1329 @@
+/* DO NOT EDIT - Generated automatically by script_asm.pl */
+static u32 SCRIPT[] = {
+/*
+; Script for the NCR (or symbios) 53c700 and 53c700-66 chip
+;
+; Copyright (C) 2001 James.Bottomley@HansenPartnership.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+;;
+;;-----------------------------------------------------------------------------
+;
+; This script is designed to be modified for the particular command in
+; operation. The particular variables pertaining to the commands are:
+;
+ABSOLUTE Device_ID = 0 ; ID of target for command
+ABSOLUTE MessageCount = 0 ; Number of bytes in message
+ABSOLUTE MessageLocation = 0 ; Addr of message
+ABSOLUTE CommandCount = 0 ; Number of bytes in command
+ABSOLUTE CommandAddress = 0 ; Addr of Command
+ABSOLUTE StatusAddress = 0 ; Addr to receive status return
+ABSOLUTE ReceiveMsgAddress = 0 ; Addr to receive msg
+;
+; This is the magic component for handling scatter-gather. Each of the
+; SG components is preceeded by a script fragment which moves the
+; necessary amount of data and jumps to the next SG segment. The final
+; SG segment jumps back to . However, this address is the first SG script
+; segment.
+;
+ABSOLUTE SGScriptStartAddress = 0
+
+; The following represent status interrupts we use 3 hex digits for
+; this: 0xPRS where
+
+; P:
+ABSOLUTE AFTER_SELECTION = 0x100
+ABSOLUTE BEFORE_CMD = 0x200
+ABSOLUTE AFTER_CMD = 0x300
+ABSOLUTE AFTER_STATUS = 0x400
+ABSOLUTE AFTER_DATA_IN = 0x500
+ABSOLUTE AFTER_DATA_OUT = 0x600
+ABSOLUTE DURING_DATA_IN = 0x700
+
+; R:
+ABSOLUTE NOT_MSG_OUT = 0x10
+ABSOLUTE UNEXPECTED_PHASE = 0x20
+ABSOLUTE NOT_MSG_IN = 0x30
+ABSOLUTE UNEXPECTED_MSG = 0x40
+ABSOLUTE MSG_IN = 0x50
+ABSOLUTE SDTR_MSG_R = 0x60
+ABSOLUTE REJECT_MSG_R = 0x70
+ABSOLUTE DISCONNECT = 0x80
+ABSOLUTE MSG_OUT = 0x90
+ABSOLUTE WDTR_MSG_R = 0xA0
+
+; S:
+ABSOLUTE GOOD_STATUS = 0x1
+
+; Combinations, since the script assembler can't process |
+ABSOLUTE NOT_MSG_OUT_AFTER_SELECTION = 0x110
+ABSOLUTE UNEXPECTED_PHASE_BEFORE_CMD = 0x220
+ABSOLUTE UNEXPECTED_PHASE_AFTER_CMD = 0x320
+ABSOLUTE NOT_MSG_IN_AFTER_STATUS = 0x430
+ABSOLUTE GOOD_STATUS_AFTER_STATUS = 0x401
+ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520
+ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620
+ABSOLUTE UNEXPECTED_MSG_BEFORE_CMD = 0x240
+ABSOLUTE MSG_IN_BEFORE_CMD = 0x250
+ABSOLUTE MSG_IN_AFTER_CMD = 0x350
+ABSOLUTE SDTR_MSG_BEFORE_CMD = 0x260
+ABSOLUTE REJECT_MSG_BEFORE_CMD = 0x270
+ABSOLUTE DISCONNECT_AFTER_CMD = 0x380
+ABSOLUTE SDTR_MSG_AFTER_CMD = 0x360
+ABSOLUTE WDTR_MSG_AFTER_CMD = 0x3A0
+ABSOLUTE MSG_IN_AFTER_STATUS = 0x440
+ABSOLUTE DISCONNECT_AFTER_DATA = 0x580
+ABSOLUTE MSG_IN_AFTER_DATA_IN = 0x550
+ABSOLUTE MSG_IN_AFTER_DATA_OUT = 0x650
+ABSOLUTE MSG_OUT_AFTER_DATA_IN = 0x590
+ABSOLUTE DATA_IN_AFTER_DATA_IN = 0x5a0
+ABSOLUTE MSG_IN_DURING_DATA_IN = 0x750
+ABSOLUTE DISCONNECT_DURING_DATA = 0x780
+
+;
+; Other interrupt conditions
+;
+ABSOLUTE RESELECTED_DURING_SELECTION = 0x1000
+ABSOLUTE COMPLETED_SELECTION_AS_TARGET = 0x1001
+ABSOLUTE RESELECTION_IDENTIFIED = 0x1003
+;
+; Fatal interrupt conditions. If you add to this, also add to the
+; array of corresponding messages
+;
+ABSOLUTE FATAL = 0x2000
+ABSOLUTE FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000
+ABSOLUTE FATAL_SEND_MSG = 0x2001
+ABSOLUTE FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002
+ABSOLUTE FATAL_ILLEGAL_MSG_LENGTH = 0x2003
+
+ABSOLUTE DEBUG_INTERRUPT = 0x3000
+ABSOLUTE DEBUG_INTERRUPT1 = 0x3001
+ABSOLUTE DEBUG_INTERRUPT2 = 0x3002
+ABSOLUTE DEBUG_INTERRUPT3 = 0x3003
+ABSOLUTE DEBUG_INTERRUPT4 = 0x3004
+ABSOLUTE DEBUG_INTERRUPT5 = 0x3005
+ABSOLUTE DEBUG_INTERRUPT6 = 0x3006
+
+
+;
+; SCSI Messages we interpret in the script
+;
+ABSOLUTE COMMAND_COMPLETE_MSG = 0x00
+ABSOLUTE EXTENDED_MSG = 0x01
+ABSOLUTE SDTR_MSG = 0x01
+ABSOLUTE SAVE_DATA_PTRS_MSG = 0x02
+ABSOLUTE RESTORE_DATA_PTRS_MSG = 0x03
+ABSOLUTE WDTR_MSG = 0x03
+ABSOLUTE DISCONNECT_MSG = 0x04
+ABSOLUTE REJECT_MSG = 0x07
+ABSOLUTE PARITY_ERROR_MSG = 0x09
+ABSOLUTE SIMPLE_TAG_MSG = 0x20
+ABSOLUTE IDENTIFY_MSG = 0x80
+ABSOLUTE IDENTIFY_MSG_MASK = 0x7F
+ABSOLUTE TWO_BYTE_MSG = 0x20
+ABSOLUTE TWO_BYTE_MSG_MASK = 0x0F
+
+; This is where the script begins
+
+ENTRY StartUp
+
+StartUp:
+ SELECT ATN Device_ID, Reselect
+
+at 0x00000000 : */ 0x41000000,0x00000020,
+/*
+ JUMP Finish, WHEN STATUS
+
+at 0x00000002 : */ 0x830b0000,0x00000460,
+/*
+ JUMP SendIdentifyMsg, IF MSG_OUT
+
+at 0x00000004 : */ 0x860a0000,0x000001b0,
+/*
+ INT NOT_MSG_OUT_AFTER_SELECTION
+
+at 0x00000006 : */ 0x98080000,0x00000110,
+/*
+
+Reselect:
+ WAIT RESELECT SelectedAsTarget
+
+at 0x00000008 : */ 0x50000000,0x00000058,
+/*
+ INT RESELECTED_DURING_SELECTION, WHEN MSG_IN
+
+at 0x0000000a : */ 0x9f0b0000,0x00001000,
+/*
+ INT FATAL_NOT_MSG_IN_AFTER_SELECTION
+
+at 0x0000000c : */ 0x98080000,0x00002002,
+/*
+
+ ENTRY GetReselectionData
+GetReselectionData:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x0000000e : */ 0x0f000001,0x00000000,
+/*
+ INT RESELECTION_IDENTIFIED
+
+at 0x00000010 : */ 0x98080000,0x00001003,
+/*
+
+ ENTRY GetReselectionWithTag
+GetReselectionWithTag:
+ MOVE 3, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x00000012 : */ 0x0f000003,0x00000000,
+/*
+ INT RESELECTION_IDENTIFIED
+
+at 0x00000014 : */ 0x98080000,0x00001003,
+/*
+
+ ENTRY SelectedAsTarget
+SelectedAsTarget:
+; Basically tell the selecting device that there's nothing here
+ SET TARGET
+
+at 0x00000016 : */ 0x58000200,0x00000000,
+/*
+ DISCONNECT
+
+at 0x00000018 : */ 0x48000000,0x00000000,
+/*
+ CLEAR TARGET
+
+at 0x0000001a : */ 0x60000200,0x00000000,
+/*
+ INT COMPLETED_SELECTION_AS_TARGET
+
+at 0x0000001c : */ 0x98080000,0x00001001,
+/*
+;
+; These are the messaging entries
+;
+; Send a message. Message count should be correctly patched
+ ENTRY SendMessage
+SendMessage:
+ MOVE MessageCount, MessageLocation, WHEN MSG_OUT
+
+at 0x0000001e : */ 0x0e000000,0x00000000,
+/*
+ResumeSendMessage:
+ RETURN, WHEN NOT MSG_OUT
+
+at 0x00000020 : */ 0x96030000,0x00000000,
+/*
+ INT FATAL_SEND_MSG
+
+at 0x00000022 : */ 0x98080000,0x00002001,
+/*
+
+ ENTRY SendMessagePhaseMismatch
+SendMessagePhaseMismatch:
+ CLEAR ACK
+
+at 0x00000024 : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeSendMessage
+
+at 0x00000026 : */ 0x80080000,0x00000080,
+/*
+;
+; Receive a message. Need to identify the message to
+; receive it correctly
+ ENTRY ReceiveMessage
+ReceiveMessage:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x00000028 : */ 0x0f000001,0x00000000,
+/*
+;
+; Use this entry if we've just tried to look at the first byte
+; of the message and want to process it further
+ProcessReceiveMessage:
+ JUMP ReceiveExtendedMessage, IF EXTENDED_MSG
+
+at 0x0000002a : */ 0x800c0001,0x000000d0,
+/*
+ RETURN, IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK
+
+at 0x0000002c : */ 0x90040f20,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000002e : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN
+
+at 0x00000030 : */ 0x0f000001,0x00000001,
+/*
+ RETURN
+
+at 0x00000032 : */ 0x90080000,0x00000000,
+/*
+ReceiveExtendedMessage:
+ CLEAR ACK
+
+at 0x00000034 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN
+
+at 0x00000036 : */ 0x0f000001,0x00000001,
+/*
+ JUMP Receive1Byte, IF 0x01
+
+at 0x00000038 : */ 0x800c0001,0x00000110,
+/*
+ JUMP Receive2Byte, IF 0x02
+
+at 0x0000003a : */ 0x800c0002,0x00000128,
+/*
+ JUMP Receive3Byte, IF 0x03
+
+at 0x0000003c : */ 0x800c0003,0x00000140,
+/*
+ JUMP Receive4Byte, IF 0x04
+
+at 0x0000003e : */ 0x800c0004,0x00000158,
+/*
+ JUMP Receive5Byte, IF 0x05
+
+at 0x00000040 : */ 0x800c0005,0x00000170,
+/*
+ INT FATAL_ILLEGAL_MSG_LENGTH
+
+at 0x00000042 : */ 0x98080000,0x00002003,
+/*
+Receive1Byte:
+ CLEAR ACK
+
+at 0x00000044 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, ReceiveMsgAddress + 2, WHEN MSG_IN
+
+at 0x00000046 : */ 0x0f000001,0x00000002,
+/*
+ RETURN
+
+at 0x00000048 : */ 0x90080000,0x00000000,
+/*
+Receive2Byte:
+ CLEAR ACK
+
+at 0x0000004a : */ 0x60000040,0x00000000,
+/*
+ MOVE 2, ReceiveMsgAddress + 2, WHEN MSG_IN
+
+at 0x0000004c : */ 0x0f000002,0x00000002,
+/*
+ RETURN
+
+at 0x0000004e : */ 0x90080000,0x00000000,
+/*
+Receive3Byte:
+ CLEAR ACK
+
+at 0x00000050 : */ 0x60000040,0x00000000,
+/*
+ MOVE 3, ReceiveMsgAddress + 2, WHEN MSG_IN
+
+at 0x00000052 : */ 0x0f000003,0x00000002,
+/*
+ RETURN
+
+at 0x00000054 : */ 0x90080000,0x00000000,
+/*
+Receive4Byte:
+ CLEAR ACK
+
+at 0x00000056 : */ 0x60000040,0x00000000,
+/*
+ MOVE 4, ReceiveMsgAddress + 2, WHEN MSG_IN
+
+at 0x00000058 : */ 0x0f000004,0x00000002,
+/*
+ RETURN
+
+at 0x0000005a : */ 0x90080000,0x00000000,
+/*
+Receive5Byte:
+ CLEAR ACK
+
+at 0x0000005c : */ 0x60000040,0x00000000,
+/*
+ MOVE 5, ReceiveMsgAddress + 2, WHEN MSG_IN
+
+at 0x0000005e : */ 0x0f000005,0x00000002,
+/*
+ RETURN
+
+at 0x00000060 : */ 0x90080000,0x00000000,
+/*
+;
+; Come here from the message processor to ignore the message
+;
+ ENTRY IgnoreMessage
+IgnoreMessage:
+ CLEAR ACK
+
+at 0x00000062 : */ 0x60000040,0x00000000,
+/*
+ RETURN
+
+at 0x00000064 : */ 0x90080000,0x00000000,
+/*
+;
+; Come here to send a reply to a message
+;
+ ENTRY SendMessageWithATN
+SendMessageWithATN:
+ SET ATN
+
+at 0x00000066 : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000068 : */ 0x60000040,0x00000000,
+/*
+ JUMP SendMessage
+
+at 0x0000006a : */ 0x80080000,0x00000078,
+/*
+
+SendIdentifyMsg:
+ CALL SendMessage
+
+at 0x0000006c : */ 0x88080000,0x00000078,
+/*
+ CLEAR ATN
+
+at 0x0000006e : */ 0x60000008,0x00000000,
+/*
+
+IgnoreMsgBeforeCommand:
+ CLEAR ACK
+
+at 0x00000070 : */ 0x60000040,0x00000000,
+/*
+ ENTRY SendCommand
+SendCommand:
+ JUMP Finish, WHEN STATUS
+
+at 0x00000072 : */ 0x830b0000,0x00000460,
+/*
+ JUMP MsgInBeforeCommand, IF MSG_IN
+
+at 0x00000074 : */ 0x870a0000,0x000002c0,
+/*
+ INT UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD
+
+at 0x00000076 : */ 0x9a020000,0x00000220,
+/*
+ MOVE CommandCount, CommandAddress, WHEN CMD
+
+at 0x00000078 : */ 0x0a000000,0x00000000,
+/*
+ResumeSendCommand:
+ JUMP Finish, WHEN STATUS
+
+at 0x0000007a : */ 0x830b0000,0x00000460,
+/*
+ JUMP MsgInAfterCmd, IF MSG_IN
+
+at 0x0000007c : */ 0x870a0000,0x00000248,
+/*
+ JUMP DataIn, IF DATA_IN
+
+at 0x0000007e : */ 0x810a0000,0x000002f8,
+/*
+ JUMP DataOut, IF DATA_OUT
+
+at 0x00000080 : */ 0x800a0000,0x00000338,
+/*
+ INT UNEXPECTED_PHASE_AFTER_CMD
+
+at 0x00000082 : */ 0x98080000,0x00000320,
+/*
+
+IgnoreMsgDuringData:
+ CLEAR ACK
+
+at 0x00000084 : */ 0x60000040,0x00000000,
+/*
+ ; fall through to MsgInDuringData
+
+Entry MsgInDuringData
+MsgInDuringData:
+;
+; Could be we have nothing more to transfer
+;
+ JUMP Finish, WHEN STATUS
+
+at 0x00000086 : */ 0x830b0000,0x00000460,
+/*
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x00000088 : */ 0x0f000001,0x00000000,
+/*
+ JUMP DisconnectDuringDataIn, IF DISCONNECT_MSG
+
+at 0x0000008a : */ 0x800c0004,0x00000398,
+/*
+ JUMP IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG
+
+at 0x0000008c : */ 0x800c0002,0x00000210,
+/*
+ JUMP IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG
+
+at 0x0000008e : */ 0x800c0003,0x00000210,
+/*
+ INT MSG_IN_DURING_DATA_IN
+
+at 0x00000090 : */ 0x98080000,0x00000750,
+/*
+
+MsgInAfterCmd:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x00000092 : */ 0x0f000001,0x00000000,
+/*
+ JUMP DisconnectAfterCmd, IF DISCONNECT_MSG
+
+at 0x00000094 : */ 0x800c0004,0x00000298,
+/*
+ JUMP IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG
+
+at 0x00000096 : */ 0x800c0002,0x00000288,
+/*
+ JUMP IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG
+
+at 0x00000098 : */ 0x800c0003,0x00000288,
+/*
+ CALL ProcessReceiveMessage
+
+at 0x0000009a : */ 0x88080000,0x000000a8,
+/*
+ INT MSG_IN_AFTER_CMD
+
+at 0x0000009c : */ 0x98080000,0x00000350,
+/*
+ CLEAR ACK
+
+at 0x0000009e : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeSendCommand
+
+at 0x000000a0 : */ 0x80080000,0x000001e8,
+/*
+
+IgnoreMsgInAfterCmd:
+ CLEAR ACK
+
+at 0x000000a2 : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeSendCommand
+
+at 0x000000a4 : */ 0x80080000,0x000001e8,
+/*
+
+DisconnectAfterCmd:
+ CLEAR ACK
+
+at 0x000000a6 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x000000a8 : */ 0x48000000,0x00000000,
+/*
+ ENTRY Disconnect1
+Disconnect1:
+ INT DISCONNECT_AFTER_CMD
+
+at 0x000000aa : */ 0x98080000,0x00000380,
+/*
+ ENTRY Disconnect2
+Disconnect2:
+; We return here after a reselection
+ CLEAR ACK
+
+at 0x000000ac : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeSendCommand
+
+at 0x000000ae : */ 0x80080000,0x000001e8,
+/*
+
+MsgInBeforeCommand:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x000000b0 : */ 0x0f000001,0x00000000,
+/*
+ JUMP IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG
+
+at 0x000000b2 : */ 0x800c0002,0x000001c0,
+/*
+ JUMP IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG
+
+at 0x000000b4 : */ 0x800c0003,0x000001c0,
+/*
+ CALL ProcessReceiveMessage
+
+at 0x000000b6 : */ 0x88080000,0x000000a8,
+/*
+ INT MSG_IN_BEFORE_CMD
+
+at 0x000000b8 : */ 0x98080000,0x00000250,
+/*
+ CLEAR ACK
+
+at 0x000000ba : */ 0x60000040,0x00000000,
+/*
+ JUMP SendCommand
+
+at 0x000000bc : */ 0x80080000,0x000001c8,
+/*
+
+DataIn:
+ CALL SGScriptStartAddress
+
+at 0x000000be : */ 0x88080000,0x00000000,
+/*
+ResumeDataIn:
+ JUMP Finish, WHEN STATUS
+
+at 0x000000c0 : */ 0x830b0000,0x00000460,
+/*
+ JUMP MsgInAfterDataIn, IF MSG_IN
+
+at 0x000000c2 : */ 0x870a0000,0x00000358,
+/*
+ JUMP DataInAfterDataIn, if DATA_IN
+
+at 0x000000c4 : */ 0x810a0000,0x00000328,
+/*
+ INT MSG_OUT_AFTER_DATA_IN, if MSG_OUT
+
+at 0x000000c6 : */ 0x9e0a0000,0x00000590,
+/*
+ INT UNEXPECTED_PHASE_AFTER_DATA_IN
+
+at 0x000000c8 : */ 0x98080000,0x00000520,
+/*
+
+DataInAfterDataIn:
+ INT DATA_IN_AFTER_DATA_IN
+
+at 0x000000ca : */ 0x98080000,0x000005a0,
+/*
+ JUMP ResumeDataIn
+
+at 0x000000cc : */ 0x80080000,0x00000300,
+/*
+
+DataOut:
+ CALL SGScriptStartAddress
+
+at 0x000000ce : */ 0x88080000,0x00000000,
+/*
+ResumeDataOut:
+ JUMP Finish, WHEN STATUS
+
+at 0x000000d0 : */ 0x830b0000,0x00000460,
+/*
+ JUMP MsgInAfterDataOut, IF MSG_IN
+
+at 0x000000d2 : */ 0x870a0000,0x000003e8,
+/*
+ INT UNEXPECTED_PHASE_AFTER_DATA_OUT
+
+at 0x000000d4 : */ 0x98080000,0x00000620,
+/*
+
+MsgInAfterDataIn:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x000000d6 : */ 0x0f000001,0x00000000,
+/*
+ JUMP DisconnectAfterDataIn, IF DISCONNECT_MSG
+
+at 0x000000d8 : */ 0x800c0004,0x000003c0,
+/*
+ JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG
+
+at 0x000000da : */ 0x800c0002,0x00000428,
+/*
+ JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG
+
+at 0x000000dc : */ 0x800c0003,0x00000428,
+/*
+ CALL ProcessReceiveMessage
+
+at 0x000000de : */ 0x88080000,0x000000a8,
+/*
+ INT MSG_IN_AFTER_DATA_IN
+
+at 0x000000e0 : */ 0x98080000,0x00000550,
+/*
+ CLEAR ACK
+
+at 0x000000e2 : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeDataIn
+
+at 0x000000e4 : */ 0x80080000,0x00000300,
+/*
+
+DisconnectDuringDataIn:
+ CLEAR ACK
+
+at 0x000000e6 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x000000e8 : */ 0x48000000,0x00000000,
+/*
+ ENTRY Disconnect3
+Disconnect3:
+ INT DISCONNECT_DURING_DATA
+
+at 0x000000ea : */ 0x98080000,0x00000780,
+/*
+ ENTRY Disconnect4
+Disconnect4:
+; we return here after a reselection
+ CLEAR ACK
+
+at 0x000000ec : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeSendCommand
+
+at 0x000000ee : */ 0x80080000,0x000001e8,
+/*
+
+
+DisconnectAfterDataIn:
+ CLEAR ACK
+
+at 0x000000f0 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x000000f2 : */ 0x48000000,0x00000000,
+/*
+ ENTRY Disconnect5
+Disconnect5:
+ INT DISCONNECT_AFTER_DATA
+
+at 0x000000f4 : */ 0x98080000,0x00000580,
+/*
+ ENTRY Disconnect6
+Disconnect6:
+; we return here after a reselection
+ CLEAR ACK
+
+at 0x000000f6 : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeDataIn
+
+at 0x000000f8 : */ 0x80080000,0x00000300,
+/*
+
+MsgInAfterDataOut:
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x000000fa : */ 0x0f000001,0x00000000,
+/*
+ JUMP DisconnectAfterDataOut, if DISCONNECT_MSG
+
+at 0x000000fc : */ 0x800c0004,0x00000438,
+/*
+ JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG
+
+at 0x000000fe : */ 0x800c0002,0x00000428,
+/*
+ JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG
+
+at 0x00000100 : */ 0x800c0003,0x00000428,
+/*
+ CALL ProcessReceiveMessage
+
+at 0x00000102 : */ 0x88080000,0x000000a8,
+/*
+ INT MSG_IN_AFTER_DATA_OUT
+
+at 0x00000104 : */ 0x98080000,0x00000650,
+/*
+ CLEAR ACK
+
+at 0x00000106 : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeDataOut
+
+at 0x00000108 : */ 0x80080000,0x00000340,
+/*
+
+IgnoreMsgAfterData:
+ CLEAR ACK
+
+at 0x0000010a : */ 0x60000040,0x00000000,
+/*
+; Data in and out do the same thing on resume, so pick one
+ JUMP ResumeDataIn
+
+at 0x0000010c : */ 0x80080000,0x00000300,
+/*
+
+DisconnectAfterDataOut:
+ CLEAR ACK
+
+at 0x0000010e : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x00000110 : */ 0x48000000,0x00000000,
+/*
+ ENTRY Disconnect7
+Disconnect7:
+ INT DISCONNECT_AFTER_DATA
+
+at 0x00000112 : */ 0x98080000,0x00000580,
+/*
+ ENTRY Disconnect8
+Disconnect8:
+; we return here after a reselection
+ CLEAR ACK
+
+at 0x00000114 : */ 0x60000040,0x00000000,
+/*
+ JUMP ResumeDataOut
+
+at 0x00000116 : */ 0x80080000,0x00000340,
+/*
+
+Finish:
+ MOVE 1, StatusAddress, WHEN STATUS
+
+at 0x00000118 : */ 0x0b000001,0x00000000,
+/*
+ INT NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN
+
+at 0x0000011a : */ 0x9f030000,0x00000430,
+/*
+ MOVE 1, ReceiveMsgAddress, WHEN MSG_IN
+
+at 0x0000011c : */ 0x0f000001,0x00000000,
+/*
+ JUMP FinishCommandComplete, IF COMMAND_COMPLETE_MSG
+
+at 0x0000011e : */ 0x800c0000,0x00000490,
+/*
+ CALL ProcessReceiveMessage
+
+at 0x00000120 : */ 0x88080000,0x000000a8,
+/*
+ INT MSG_IN_AFTER_STATUS
+
+at 0x00000122 : */ 0x98080000,0x00000440,
+/*
+ ENTRY FinishCommandComplete
+FinishCommandComplete:
+ CLEAR ACK
+
+at 0x00000124 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x00000126 : */ 0x48000000,0x00000000,
+/*
+ ENTRY Finish1
+Finish1:
+ INT GOOD_STATUS_AFTER_STATUS
+
+at 0x00000128 : */ 0x98080000,0x00000401,
+};
+
+#define A_AFTER_CMD 0x00000300
+static u32 A_AFTER_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_AFTER_DATA_IN 0x00000500
+static u32 A_AFTER_DATA_IN_used[] __attribute((unused)) = {
+};
+
+#define A_AFTER_DATA_OUT 0x00000600
+static u32 A_AFTER_DATA_OUT_used[] __attribute((unused)) = {
+};
+
+#define A_AFTER_SELECTION 0x00000100
+static u32 A_AFTER_SELECTION_used[] __attribute((unused)) = {
+};
+
+#define A_AFTER_STATUS 0x00000400
+static u32 A_AFTER_STATUS_used[] __attribute((unused)) = {
+};
+
+#define A_BEFORE_CMD 0x00000200
+static u32 A_BEFORE_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_COMMAND_COMPLETE_MSG 0x00000000
+static u32 A_COMMAND_COMPLETE_MSG_used[] __attribute((unused)) = {
+ 0x0000011e,
+};
+
+#define A_COMPLETED_SELECTION_AS_TARGET 0x00001001
+static u32 A_COMPLETED_SELECTION_AS_TARGET_used[] __attribute((unused)) = {
+ 0x0000001d,
+};
+
+#define A_CommandAddress 0x00000000
+static u32 A_CommandAddress_used[] __attribute((unused)) = {
+ 0x00000079,
+};
+
+#define A_CommandCount 0x00000000
+static u32 A_CommandCount_used[] __attribute((unused)) = {
+ 0x00000078,
+};
+
+#define A_DATA_IN_AFTER_DATA_IN 0x000005a0
+static u32 A_DATA_IN_AFTER_DATA_IN_used[] __attribute((unused)) = {
+ 0x000000cb,
+};
+
+#define A_DEBUG_INTERRUPT 0x00003000
+static u32 A_DEBUG_INTERRUPT_used[] __attribute((unused)) = {
+};
+
+#define A_DEBUG_INTERRUPT1 0x00003001
+static u32 A_DEBUG_INTERRUPT1_used[] __attribute((unused)) = {
+};
+
+#define A_DEBUG_INTERRUPT2 0x00003002
+static u32 A_DEBUG_INTERRUPT2_used[] __attribute((unused)) = {
+};
+
+#define A_DEBUG_INTERRUPT3 0x00003003
+static u32 A_DEBUG_INTERRUPT3_used[] __attribute((unused)) = {
+};
+
+#define A_DEBUG_INTERRUPT4 0x00003004
+static u32 A_DEBUG_INTERRUPT4_used[] __attribute((unused)) = {
+};
+
+#define A_DEBUG_INTERRUPT5 0x00003005
+static u32 A_DEBUG_INTERRUPT5_used[] __attribute((unused)) = {
+};
+
+#define A_DEBUG_INTERRUPT6 0x00003006
+static u32 A_DEBUG_INTERRUPT6_used[] __attribute((unused)) = {
+};
+
+#define A_DISCONNECT 0x00000080
+static u32 A_DISCONNECT_used[] __attribute((unused)) = {
+};
+
+#define A_DISCONNECT_AFTER_CMD 0x00000380
+static u32 A_DISCONNECT_AFTER_CMD_used[] __attribute((unused)) = {
+ 0x000000ab,
+};
+
+#define A_DISCONNECT_AFTER_DATA 0x00000580
+static u32 A_DISCONNECT_AFTER_DATA_used[] __attribute((unused)) = {
+ 0x000000f5,
+ 0x00000113,
+};
+
+#define A_DISCONNECT_DURING_DATA 0x00000780
+static u32 A_DISCONNECT_DURING_DATA_used[] __attribute((unused)) = {
+ 0x000000eb,
+};
+
+#define A_DISCONNECT_MSG 0x00000004
+static u32 A_DISCONNECT_MSG_used[] __attribute((unused)) = {
+ 0x0000008a,
+ 0x00000094,
+ 0x000000d8,
+ 0x000000fc,
+};
+
+#define A_DURING_DATA_IN 0x00000700
+static u32 A_DURING_DATA_IN_used[] __attribute((unused)) = {
+};
+
+#define A_Device_ID 0x00000000
+static u32 A_Device_ID_used[] __attribute((unused)) = {
+ 0x00000000,
+};
+
+#define A_EXTENDED_MSG 0x00000001
+static u32 A_EXTENDED_MSG_used[] __attribute((unused)) = {
+ 0x0000002a,
+};
+
+#define A_FATAL 0x00002000
+static u32 A_FATAL_used[] __attribute((unused)) = {
+};
+
+#define A_FATAL_ILLEGAL_MSG_LENGTH 0x00002003
+static u32 A_FATAL_ILLEGAL_MSG_LENGTH_used[] __attribute((unused)) = {
+ 0x00000043,
+};
+
+#define A_FATAL_NOT_MSG_IN_AFTER_SELECTION 0x00002002
+static u32 A_FATAL_NOT_MSG_IN_AFTER_SELECTION_used[] __attribute((unused)) = {
+ 0x0000000d,
+};
+
+#define A_FATAL_SEND_MSG 0x00002001
+static u32 A_FATAL_SEND_MSG_used[] __attribute((unused)) = {
+ 0x00000023,
+};
+
+#define A_FATAL_UNEXPECTED_RESELECTION_MSG 0x00002000
+static u32 A_FATAL_UNEXPECTED_RESELECTION_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_GOOD_STATUS 0x00000001
+static u32 A_GOOD_STATUS_used[] __attribute((unused)) = {
+};
+
+#define A_GOOD_STATUS_AFTER_STATUS 0x00000401
+static u32 A_GOOD_STATUS_AFTER_STATUS_used[] __attribute((unused)) = {
+ 0x00000129,
+};
+
+#define A_IDENTIFY_MSG 0x00000080
+static u32 A_IDENTIFY_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_IDENTIFY_MSG_MASK 0x0000007f
+static u32 A_IDENTIFY_MSG_MASK_used[] __attribute((unused)) = {
+};
+
+#define A_MSG_IN 0x00000050
+static u32 A_MSG_IN_used[] __attribute((unused)) = {
+};
+
+#define A_MSG_IN_AFTER_CMD 0x00000350
+static u32 A_MSG_IN_AFTER_CMD_used[] __attribute((unused)) = {
+ 0x0000009d,
+};
+
+#define A_MSG_IN_AFTER_DATA_IN 0x00000550
+static u32 A_MSG_IN_AFTER_DATA_IN_used[] __attribute((unused)) = {
+ 0x000000e1,
+};
+
+#define A_MSG_IN_AFTER_DATA_OUT 0x00000650
+static u32 A_MSG_IN_AFTER_DATA_OUT_used[] __attribute((unused)) = {
+ 0x00000105,
+};
+
+#define A_MSG_IN_AFTER_STATUS 0x00000440
+static u32 A_MSG_IN_AFTER_STATUS_used[] __attribute((unused)) = {
+ 0x00000123,
+};
+
+#define A_MSG_IN_BEFORE_CMD 0x00000250
+static u32 A_MSG_IN_BEFORE_CMD_used[] __attribute((unused)) = {
+ 0x000000b9,
+};
+
+#define A_MSG_IN_DURING_DATA_IN 0x00000750
+static u32 A_MSG_IN_DURING_DATA_IN_used[] __attribute((unused)) = {
+ 0x00000091,
+};
+
+#define A_MSG_OUT 0x00000090
+static u32 A_MSG_OUT_used[] __attribute((unused)) = {
+};
+
+#define A_MSG_OUT_AFTER_DATA_IN 0x00000590
+static u32 A_MSG_OUT_AFTER_DATA_IN_used[] __attribute((unused)) = {
+ 0x000000c7,
+};
+
+#define A_MessageCount 0x00000000
+static u32 A_MessageCount_used[] __attribute((unused)) = {
+ 0x0000001e,
+};
+
+#define A_MessageLocation 0x00000000
+static u32 A_MessageLocation_used[] __attribute((unused)) = {
+ 0x0000001f,
+};
+
+#define A_NOT_MSG_IN 0x00000030
+static u32 A_NOT_MSG_IN_used[] __attribute((unused)) = {
+};
+
+#define A_NOT_MSG_IN_AFTER_STATUS 0x00000430
+static u32 A_NOT_MSG_IN_AFTER_STATUS_used[] __attribute((unused)) = {
+ 0x0000011b,
+};
+
+#define A_NOT_MSG_OUT 0x00000010
+static u32 A_NOT_MSG_OUT_used[] __attribute((unused)) = {
+};
+
+#define A_NOT_MSG_OUT_AFTER_SELECTION 0x00000110
+static u32 A_NOT_MSG_OUT_AFTER_SELECTION_used[] __attribute((unused)) = {
+ 0x00000007,
+};
+
+#define A_PARITY_ERROR_MSG 0x00000009
+static u32 A_PARITY_ERROR_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_REJECT_MSG 0x00000007
+static u32 A_REJECT_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_REJECT_MSG_BEFORE_CMD 0x00000270
+static u32 A_REJECT_MSG_BEFORE_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_REJECT_MSG_R 0x00000070
+static u32 A_REJECT_MSG_R_used[] __attribute((unused)) = {
+};
+
+#define A_RESELECTED_DURING_SELECTION 0x00001000
+static u32 A_RESELECTED_DURING_SELECTION_used[] __attribute((unused)) = {
+ 0x0000000b,
+};
+
+#define A_RESELECTION_IDENTIFIED 0x00001003
+static u32 A_RESELECTION_IDENTIFIED_used[] __attribute((unused)) = {
+ 0x00000011,
+ 0x00000015,
+};
+
+#define A_RESTORE_DATA_PTRS_MSG 0x00000003
+static u32 A_RESTORE_DATA_PTRS_MSG_used[] __attribute((unused)) = {
+ 0x0000008e,
+ 0x00000098,
+ 0x000000b4,
+ 0x000000dc,
+ 0x00000100,
+};
+
+#define A_ReceiveMsgAddress 0x00000000
+static u32 A_ReceiveMsgAddress_used[] __attribute((unused)) = {
+ 0x0000000f,
+ 0x00000013,
+ 0x00000029,
+ 0x00000031,
+ 0x00000037,
+ 0x00000047,
+ 0x0000004d,
+ 0x00000053,
+ 0x00000059,
+ 0x0000005f,
+ 0x00000089,
+ 0x00000093,
+ 0x000000b1,
+ 0x000000d7,
+ 0x000000fb,
+ 0x0000011d,
+};
+
+#define A_SAVE_DATA_PTRS_MSG 0x00000002
+static u32 A_SAVE_DATA_PTRS_MSG_used[] __attribute((unused)) = {
+ 0x0000008c,
+ 0x00000096,
+ 0x000000b2,
+ 0x000000da,
+ 0x000000fe,
+};
+
+#define A_SDTR_MSG 0x00000001
+static u32 A_SDTR_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_SDTR_MSG_AFTER_CMD 0x00000360
+static u32 A_SDTR_MSG_AFTER_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_SDTR_MSG_BEFORE_CMD 0x00000260
+static u32 A_SDTR_MSG_BEFORE_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_SDTR_MSG_R 0x00000060
+static u32 A_SDTR_MSG_R_used[] __attribute((unused)) = {
+};
+
+#define A_SGScriptStartAddress 0x00000000
+static u32 A_SGScriptStartAddress_used[] __attribute((unused)) = {
+ 0x000000bf,
+ 0x000000cf,
+};
+
+#define A_SIMPLE_TAG_MSG 0x00000020
+static u32 A_SIMPLE_TAG_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_StatusAddress 0x00000000
+static u32 A_StatusAddress_used[] __attribute((unused)) = {
+ 0x00000119,
+};
+
+#define A_TWO_BYTE_MSG 0x00000020
+static u32 A_TWO_BYTE_MSG_used[] __attribute((unused)) = {
+ 0x0000002c,
+};
+
+#define A_TWO_BYTE_MSG_MASK 0x0000000f
+static u32 A_TWO_BYTE_MSG_MASK_used[] __attribute((unused)) = {
+ 0x0000002c,
+};
+
+#define A_UNEXPECTED_MSG 0x00000040
+static u32 A_UNEXPECTED_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_UNEXPECTED_MSG_BEFORE_CMD 0x00000240
+static u32 A_UNEXPECTED_MSG_BEFORE_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_UNEXPECTED_PHASE 0x00000020
+static u32 A_UNEXPECTED_PHASE_used[] __attribute((unused)) = {
+};
+
+#define A_UNEXPECTED_PHASE_AFTER_CMD 0x00000320
+static u32 A_UNEXPECTED_PHASE_AFTER_CMD_used[] __attribute((unused)) = {
+ 0x00000083,
+};
+
+#define A_UNEXPECTED_PHASE_AFTER_DATA_IN 0x00000520
+static u32 A_UNEXPECTED_PHASE_AFTER_DATA_IN_used[] __attribute((unused)) = {
+ 0x000000c9,
+};
+
+#define A_UNEXPECTED_PHASE_AFTER_DATA_OUT 0x00000620
+static u32 A_UNEXPECTED_PHASE_AFTER_DATA_OUT_used[] __attribute((unused)) = {
+ 0x000000d5,
+};
+
+#define A_UNEXPECTED_PHASE_BEFORE_CMD 0x00000220
+static u32 A_UNEXPECTED_PHASE_BEFORE_CMD_used[] __attribute((unused)) = {
+ 0x00000077,
+};
+
+#define A_WDTR_MSG 0x00000003
+static u32 A_WDTR_MSG_used[] __attribute((unused)) = {
+};
+
+#define A_WDTR_MSG_AFTER_CMD 0x000003a0
+static u32 A_WDTR_MSG_AFTER_CMD_used[] __attribute((unused)) = {
+};
+
+#define A_WDTR_MSG_R 0x000000a0
+static u32 A_WDTR_MSG_R_used[] __attribute((unused)) = {
+};
+
+#define Ent_Disconnect1 0x000002a8
+#define Ent_Disconnect2 0x000002b0
+#define Ent_Disconnect3 0x000003a8
+#define Ent_Disconnect4 0x000003b0
+#define Ent_Disconnect5 0x000003d0
+#define Ent_Disconnect6 0x000003d8
+#define Ent_Disconnect7 0x00000448
+#define Ent_Disconnect8 0x00000450
+#define Ent_Finish1 0x000004a0
+#define Ent_Finish2 0x000004a8
+#define Ent_FinishCommandComplete 0x00000490
+#define Ent_GetReselectionData 0x00000038
+#define Ent_GetReselectionWithTag 0x00000048
+#define Ent_IgnoreMessage 0x00000188
+#define Ent_MsgInDuringData 0x00000218
+#define Ent_ReceiveMessage 0x000000a0
+#define Ent_SelectedAsTarget 0x00000058
+#define Ent_SendCommand 0x000001c8
+#define Ent_SendMessage 0x00000078
+#define Ent_SendMessagePhaseMismatch 0x00000090
+#define Ent_SendMessageWithATN 0x00000198
+#define Ent_StartUp 0x00000000
+static u32 LABELPATCHES[] __attribute((unused)) = {
+ 0x00000001,
+ 0x00000003,
+ 0x00000005,
+ 0x00000009,
+ 0x00000027,
+ 0x0000002b,
+ 0x00000039,
+ 0x0000003b,
+ 0x0000003d,
+ 0x0000003f,
+ 0x00000041,
+ 0x0000006b,
+ 0x0000006d,
+ 0x00000073,
+ 0x00000075,
+ 0x0000007b,
+ 0x0000007d,
+ 0x0000007f,
+ 0x00000081,
+ 0x00000087,
+ 0x0000008b,
+ 0x0000008d,
+ 0x0000008f,
+ 0x00000095,
+ 0x00000097,
+ 0x00000099,
+ 0x0000009b,
+ 0x000000a1,
+ 0x000000a5,
+ 0x000000af,
+ 0x000000b3,
+ 0x000000b5,
+ 0x000000b7,
+ 0x000000bd,
+ 0x000000c1,
+ 0x000000c3,
+ 0x000000c5,
+ 0x000000cd,
+ 0x000000d1,
+ 0x000000d3,
+ 0x000000d9,
+ 0x000000db,
+ 0x000000dd,
+ 0x000000df,
+ 0x000000e5,
+ 0x000000ef,
+ 0x000000f9,
+ 0x000000fd,
+ 0x000000ff,
+ 0x00000101,
+ 0x00000103,
+ 0x00000109,
+ 0x0000010d,
+ 0x00000117,
+ 0x0000011f,
+ 0x00000121,
+};
+
+static struct {
+ u32 offset;
+ void *address;
+} EXTERNAL_PATCHES[] __attribute((unused)) = {
+};
+
+static u32 INSTRUCTIONS __attribute((unused)) = 149;
+static u32 PATCHES __attribute((unused)) = 56;
+static u32 EXTERNAL_PATCHES_LEN __attribute((unused)) = 0;
diff --git a/drivers/scsi/53c7xx.c b/drivers/scsi/53c7xx.c
new file mode 100644
index 000000000000..8ead55f75d02
--- /dev/null
+++ b/drivers/scsi/53c7xx.c
@@ -0,0 +1,6102 @@
+/*
+ * 53c710 driver. Modified from Drew Eckhardts driver
+ * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
+ * Check out PERM_OPTIONS and EXPECTED_CLOCK, which may be defined in the
+ * relevant machine specific file (eg. mvme16x.[ch], amiga7xx.[ch]).
+ * There are also currently some defines at the top of 53c7xx.scr.
+ * The chip type is #defined in script_asm.pl, as well as the Makefile.
+ * Host scsi ID expected to be 7 - see NCR53c7x0_init().
+ *
+ * I have removed the PCI code and some of the 53c8xx specific code -
+ * simply to make this file smaller and easier to manage.
+ *
+ * MVME16x issues:
+ * Problems trying to read any chip registers in NCR53c7x0_init(), as they
+ * may never have been set by 16xBug (eg. If kernel has come in over tftp).
+ */
+
+/*
+ * Adapted for Linux/m68k Amiga platforms for the A4000T/A4091 and
+ * WarpEngine SCSI controllers.
+ * By Alan Hourihane <alanh@fairlite.demon.co.uk>
+ * Thanks to Richard Hirst for making it possible with the MVME additions
+ */
+
+/*
+ * 53c710 rev 0 doesn't support add with carry. Rev 1 and 2 does. To
+ * overcome this problem you can define FORCE_DSA_ALIGNMENT, which ensures
+ * that the DSA address is always xxxxxx00. If disconnection is not allowed,
+ * then the script only ever tries to add small (< 256) positive offsets to
+ * DSA, so lack of carry isn't a problem. FORCE_DSA_ALIGNMENT can, of course,
+ * be defined for all chip revisions at a small cost in memory usage.
+ */
+
+#define FORCE_DSA_ALIGNMENT
+
+/*
+ * Selection timer does not always work on the 53c710, depending on the
+ * timing at the last disconnect, if this is a problem for you, try
+ * using validids as detailed below.
+ *
+ * Options for the NCR7xx driver
+ *
+ * noasync:0 - disables sync and asynchronous negotiation
+ * nosync:0 - disables synchronous negotiation (does async)
+ * nodisconnect:0 - disables disconnection
+ * validids:0x?? - Bitmask field that disallows certain ID's.
+ * - e.g. 0x03 allows ID 0,1
+ * - 0x1F allows ID 0,1,2,3,4
+ * opthi:n - replace top word of options with 'n'
+ * optlo:n - replace bottom word of options with 'n'
+ * - ALWAYS SPECIFY opthi THEN optlo <<<<<<<<<<
+ */
+
+/*
+ * PERM_OPTIONS are driver options which will be enabled for all NCR boards
+ * in the system at driver initialization time.
+ *
+ * Don't THINK about touching these in PERM_OPTIONS :
+ * OPTION_MEMORY_MAPPED
+ * 680x0 doesn't have an IO map!
+ *
+ * OPTION_DEBUG_TEST1
+ * Test 1 does bus mastering and interrupt tests, which will help weed
+ * out brain damaged main boards.
+ *
+ * Other PERM_OPTIONS settings are listed below. Note the actual options
+ * required are set in the relevant file (mvme16x.c, amiga7xx.c, etc):
+ *
+ * OPTION_NO_ASYNC
+ * Don't negotiate for asynchronous transfers on the first command
+ * when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged
+ * devices which do something bad rather than sending a MESSAGE
+ * REJECT back to us like they should if they can't cope.
+ *
+ * OPTION_SYNCHRONOUS
+ * Enable support for synchronous transfers. Target negotiated
+ * synchronous transfers will be responded to. To initiate
+ * a synchronous transfer request, call
+ *
+ * request_synchronous (hostno, target)
+ *
+ * from within KGDB.
+ *
+ * OPTION_ALWAYS_SYNCHRONOUS
+ * Negotiate for synchronous transfers with every target after
+ * driver initialization or a SCSI bus reset. This is a bit dangerous,
+ * since there are some dain bramaged SCSI devices which will accept
+ * SDTR messages but keep talking asynchronously.
+ *
+ * OPTION_DISCONNECT
+ * Enable support for disconnect/reconnect. To change the
+ * default setting on a given host adapter, call
+ *
+ * request_disconnect (hostno, allow)
+ *
+ * where allow is non-zero to allow, 0 to disallow.
+ *
+ * If you really want to run 10MHz FAST SCSI-II transfers, you should
+ * know that the NCR driver currently ignores parity information. Most
+ * systems do 5MHz SCSI fine. I've seen a lot that have problems faster
+ * than 8MHz. To play it safe, we only request 5MHz transfers.
+ *
+ * If you'd rather get 10MHz transfers, edit sdtr_message and change
+ * the fourth byte from 50 to 25.
+ */
+
+/*
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@PoohSticks.ORG
+ * +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ *
+ * For more information, please consult
+ *
+ * NCR53C810
+ * SCSI I/O Processor
+ * Programmer's Guide
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810/53C820
+ * PCI-SCSI I/O Processor Design In Guide
+ *
+ * For literature on Symbios Logic Inc. formerly NCR, SCSI,
+ * and Communication products please call (800) 334-5454 or
+ * (719) 536-3300.
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ */
+
+/*
+ * Design issues :
+ * The cumulative latency needed to propagate a read/write request
+ * through the file system, buffer cache, driver stacks, SCSI host, and
+ * SCSI device is ultimately the limiting factor in throughput once we
+ * have a sufficiently fast host adapter.
+ *
+ * So, to maximize performance we want to keep the ratio of latency to data
+ * transfer time to a minimum by
+ * 1. Minimizing the total number of commands sent (typical command latency
+ * including drive and bus mastering host overhead is as high as 4.5ms)
+ * to transfer a given amount of data.
+ *
+ * This is accomplished by placing no arbitrary limit on the number
+ * of scatter/gather buffers supported, since we can transfer 1K
+ * per scatter/gather buffer without Eric's cluster patches,
+ * 4K with.
+ *
+ * 2. Minimizing the number of fatal interrupts serviced, since
+ * fatal interrupts halt the SCSI I/O processor. Basically,
+ * this means offloading the practical maximum amount of processing
+ * to the SCSI chip.
+ *
+ * On the NCR53c810/820/720, this is accomplished by using
+ * interrupt-on-the-fly signals when commands complete,
+ * and only handling fatal errors and SDTR / WDTR messages
+ * in the host code.
+ *
+ * On the NCR53c710, interrupts are generated as on the NCR53c8x0,
+ * only the lack of a interrupt-on-the-fly facility complicates
+ * things. Also, SCSI ID registers and commands are
+ * bit fielded rather than binary encoded.
+ *
+ * On the NCR53c700 and NCR53c700-66, operations that are done via
+ * indirect, table mode on the more advanced chips must be
+ * replaced by calls through a jump table which
+ * acts as a surrogate for the DSA. Unfortunately, this
+ * will mean that we must service an interrupt for each
+ * disconnect/reconnect.
+ *
+ * 3. Eliminating latency by pipelining operations at the different levels.
+ *
+ * This driver allows a configurable number of commands to be enqueued
+ * for each target/lun combination (experimentally, I have discovered
+ * that two seems to work best) and will ultimately allow for
+ * SCSI-II tagged queuing.
+ *
+ *
+ * Architecture :
+ * This driver is built around a Linux queue of commands waiting to
+ * be executed, and a shared Linux/NCR array of commands to start. Commands
+ * are transferred to the array by the run_process_issue_queue() function
+ * which is called whenever a command completes.
+ *
+ * As commands are completed, the interrupt routine is triggered,
+ * looks for commands in the linked list of completed commands with
+ * valid status, removes these commands from a list of running commands,
+ * calls the done routine, and flags their target/luns as not busy.
+ *
+ * Due to limitations in the intelligence of the NCR chips, certain
+ * concessions are made. In many cases, it is easier to dynamically
+ * generate/fix-up code rather than calculate on the NCR at run time.
+ * So, code is generated or fixed up for
+ *
+ * - Handling data transfers, using a variable number of MOVE instructions
+ * interspersed with CALL MSG_IN, WHEN MSGIN instructions.
+ *
+ * The DATAIN and DATAOUT routines are separate, so that an incorrect
+ * direction can be trapped, and space isn't wasted.
+ *
+ * It may turn out that we're better off using some sort
+ * of table indirect instruction in a loop with a variable
+ * sized table on the NCR53c710 and newer chips.
+ *
+ * - Checking for reselection (NCR53c710 and better)
+ *
+ * - Handling the details of SCSI context switches (NCR53c710 and better),
+ * such as reprogramming appropriate synchronous parameters,
+ * removing the dsa structure from the NCR's queue of outstanding
+ * commands, etc.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <asm/setup.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/irq.h>
+
+#define BIG_ENDIAN
+#define NO_IO_SPACE
+#endif
+
+#ifdef CONFIG_MVME16x
+#include <asm/mvme16xhw.h>
+
+#define BIG_ENDIAN
+#define NO_IO_SPACE
+#define VALID_IDS
+#endif
+
+#ifdef CONFIG_BVME6000
+#include <asm/bvme6000hw.h>
+
+#define BIG_ENDIAN
+#define NO_IO_SPACE
+#define VALID_IDS
+#endif
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "53c7xx.h"
+#include <linux/stat.h>
+#include <linux/stddef.h>
+
+#ifdef NO_IO_SPACE
+/*
+ * The following make the definitions in 53c7xx.h (write8, etc) smaller,
+ * we don't have separate i/o space anyway.
+ */
+#undef inb
+#undef outb
+#undef inw
+#undef outw
+#undef inl
+#undef outl
+#define inb(x) 1
+#define inw(x) 1
+#define inl(x) 1
+#define outb(x,y) 1
+#define outw(x,y) 1
+#define outl(x,y) 1
+#endif
+
+static int check_address (unsigned long addr, int size);
+static void dump_events (struct Scsi_Host *host, int count);
+static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host,
+ int free, int issue);
+static void hard_reset (struct Scsi_Host *host);
+static void ncr_scsi_reset (struct Scsi_Host *host);
+static void print_lots (struct Scsi_Host *host);
+static void set_synchronous (struct Scsi_Host *host, int target, int sxfer,
+ int scntl3, int now_connected);
+static int datapath_residual (struct Scsi_Host *host);
+static const char * sbcl_to_phase (int sbcl);
+static void print_progress (Scsi_Cmnd *cmd);
+static void print_queues (struct Scsi_Host *host);
+static void process_issue_queue (unsigned long flags);
+static int shutdown (struct Scsi_Host *host);
+static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result);
+static int disable (struct Scsi_Host *host);
+static int NCR53c7xx_run_tests (struct Scsi_Host *host);
+static irqreturn_t NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+static void NCR53c7x0_intfly (struct Scsi_Host *host);
+static int ncr_halt (struct Scsi_Host *host);
+static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd
+ *cmd);
+static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+static void print_dsa (struct Scsi_Host *host, u32 *dsa,
+ const char *prefix);
+static int print_insn (struct Scsi_Host *host, const u32 *insn,
+ const char *prefix, int kernel);
+
+static void NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd);
+static void NCR53c7x0_init_fixup (struct Scsi_Host *host);
+static int NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd);
+static void NCR53c7x0_soft_reset (struct Scsi_Host *host);
+
+/* Size of event list (per host adapter) */
+static int track_events = 0;
+static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */
+static Scsi_Host_Template *the_template = NULL;
+
+/* NCR53c710 script handling code */
+
+#include "53c7xx_d.h"
+#ifdef A_int_debug_sync
+#define DEBUG_SYNC_INTR A_int_debug_sync
+#endif
+int NCR53c7xx_script_len = sizeof (SCRIPT);
+int NCR53c7xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template;
+#ifdef FORCE_DSA_ALIGNMENT
+int CmdPageStart = (0 - Ent_dsa_zero - sizeof(struct NCR53c7x0_cmd)) & 0xff;
+#endif
+
+static char *setup_strings[] =
+ {"","","","","","","",""};
+
+#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_STRINGS];
+
+void ncr53c7xx_setup (char *str, int *ints)
+{
+ int i;
+ char *p1, *p2;
+
+ p1 = setup_buffer;
+ *p1 = '\0';
+ if (str)
+ strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer));
+ setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
+ p1 = setup_buffer;
+ i = 0;
+ while (*p1 && (i < MAX_SETUP_STRINGS)) {
+ p2 = strchr(p1, ',');
+ if (p2) {
+ *p2 = '\0';
+ if (p1 != p2)
+ setup_strings[i] = p1;
+ p1 = p2 + 1;
+ i++;
+ }
+ else {
+ setup_strings[i] = p1;
+ break;
+ }
+ }
+ for (i=0; i<MAX_SETUP_STRINGS; i++)
+ setup_used[i] = 0;
+}
+
+
+/* check_setup_strings() returns index if key found, 0 if not
+ */
+
+static int check_setup_strings(char *key, int *flags, int *val, char *buf)
+{
+int x;
+char *cp;
+
+ for (x=0; x<MAX_SETUP_STRINGS; x++) {
+ if (setup_used[x])
+ continue;
+ if (!strncmp(setup_strings[x], key, strlen(key)))
+ break;
+ if (!strncmp(setup_strings[x], "next", strlen("next")))
+ return 0;
+ }
+ if (x == MAX_SETUP_STRINGS)
+ return 0;
+ setup_used[x] = 1;
+ cp = setup_strings[x] + strlen(key);
+ *val = -1;
+ if (*cp != ':')
+ return ++x;
+ cp++;
+ if ((*cp >= '0') && (*cp <= '9')) {
+ *val = simple_strtoul(cp,NULL,0);
+ }
+ return ++x;
+}
+
+
+
+/*
+ * KNOWN BUGS :
+ * - There is some sort of conflict when the PPP driver is compiled with
+ * support for 16 channels?
+ *
+ * - On systems which predate the 1.3.x initialization order change,
+ * the NCR driver will cause Cannot get free page messages to appear.
+ * These are harmless, but I don't know of an easy way to avoid them.
+ *
+ * - With OPTION_DISCONNECT, on two systems under unknown circumstances,
+ * we get a PHASE MISMATCH with DSA set to zero (suggests that we
+ * are occurring somewhere in the reselection code) where
+ * DSP=some value DCMD|DBC=same value.
+ *
+ * Closer inspection suggests that we may be trying to execute
+ * some portion of the DSA?
+ * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
+ * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
+ * scsi0 : no current command : unexpected phase MSGIN.
+ * DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0
+ * DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80
+ * scsi0 : DSP->
+ * 001c46cc : 0x001c46cc 0x00000000
+ * 001c46d4 : 0x001c5ea0 0x000011f8
+ *
+ * Changed the print code in the phase_mismatch handler so
+ * that we call print_lots to try to diagnose this.
+ *
+ */
+
+/*
+ * Possible future direction of architecture for max performance :
+ *
+ * We're using a single start array for the NCR chip. This is
+ * sub-optimal, because we cannot add a command which would conflict with
+ * an executing command to this start queue, and therefore must insert the
+ * next command for a given I/T/L combination after the first has completed;
+ * incurring our interrupt latency between SCSI commands.
+ *
+ * To allow further pipelining of the NCR and host CPU operation, we want
+ * to set things up so that immediately on termination of a command destined
+ * for a given LUN, we get that LUN busy again.
+ *
+ * To do this, we need to add a 32 bit pointer to which is jumped to
+ * on completion of a command. If no new command is available, this
+ * would point to the usual DSA issue queue select routine.
+ *
+ * If one were, it would point to a per-NCR53c7x0_cmd select routine
+ * which starts execution immediately, inserting the command at the head
+ * of the start queue if the NCR chip is selected or reselected.
+ *
+ * We would change so that we keep a list of outstanding commands
+ * for each unit, rather than a single running_list. We'd insert
+ * a new command into the right running list; if the NCR didn't
+ * have something running for that yet, we'd put it in the
+ * start queue as well. Some magic needs to happen to handle the
+ * race condition between the first command terminating before the
+ * new one is written.
+ *
+ * Potential for profiling :
+ * Call do_gettimeofday(struct timeval *tv) to get 800ns resolution.
+ */
+
+
+/*
+ * TODO :
+ * 1. To support WIDE transfers, not much needs to happen. We
+ * should do CHMOVE instructions instead of MOVEs when
+ * we have scatter/gather segments of uneven length. When
+ * we do this, we need to handle the case where we disconnect
+ * between segments.
+ *
+ * 2. Currently, when Icky things happen we do a FATAL(). Instead,
+ * we want to do an integrity check on the parts of the NCR hostdata
+ * structure which were initialized at boot time; FATAL() if that
+ * fails, and otherwise try to recover. Keep track of how many
+ * times this has happened within a single SCSI command; if it
+ * gets excessive, then FATAL().
+ *
+ * 3. Parity checking is currently disabled, and a few things should
+ * happen here now that we support synchronous SCSI transfers :
+ * 1. On soft-reset, we shoould set the EPC (Enable Parity Checking)
+ * and AAP (Assert SATN/ on parity error) bits in SCNTL0.
+ *
+ * 2. We should enable the parity interrupt in the SIEN0 register.
+ *
+ * 3. intr_phase_mismatch() needs to believe that message out is
+ * always an "acceptable" phase to have a mismatch in. If
+ * the old phase was MSG_IN, we should send a MESSAGE PARITY
+ * error. If the old phase was something else, we should send
+ * a INITIATOR_DETECTED_ERROR message. Note that this could
+ * cause a RESTORE POINTERS message; so we should handle that
+ * correctly first. Instead, we should probably do an
+ * initiator_abort.
+ *
+ * 4. MPEE bit of CTEST4 should be set so we get interrupted if
+ * we detect an error.
+ *
+ *
+ * 5. The initial code has been tested on the NCR53c810. I don't
+ * have access to NCR53c700, 700-66 (Forex boards), NCR53c710
+ * (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to
+ * finish development on those platforms.
+ *
+ * NCR53c820/825/720 - need to add wide transfer support, including WDTR
+ * negotiation, programming of wide transfer capabilities
+ * on reselection and table indirect selection.
+ *
+ * NCR53c710 - need to add fatal interrupt or GEN code for
+ * command completion signaling. Need to modify all
+ * SDID, SCID, etc. registers, and table indirect select code
+ * since these use bit fielded (ie 1<<target) instead of
+ * binary encoded target ids. Need to accommodate
+ * different register mappings, probably scan through
+ * the SCRIPT code and change the non SFBR register operand
+ * of all MOVE instructions.
+ *
+ * It is rather worse than this actually, the 710 corrupts
+ * both TEMP and DSA when you do a MOVE MEMORY. This
+ * screws you up all over the place. MOVE MEMORY 4 with a
+ * destination of DSA seems to work OK, which helps some.
+ * Richard Hirst richard@sleepie.demon.co.uk
+ *
+ * NCR53c700/700-66 - need to add code to refix addresses on
+ * every nexus change, eliminate all table indirect code,
+ * very messy.
+ *
+ * 6. The NCR53c7x0 series is very popular on other platforms that
+ * could be running Linux - ie, some high performance AMIGA SCSI
+ * boards use it.
+ *
+ * So, I should include #ifdef'd code so that it is
+ * compatible with these systems.
+ *
+ * Specifically, the little Endian assumptions I made in my
+ * bit fields need to change, and if the NCR doesn't see memory
+ * the right way, we need to provide options to reverse words
+ * when the scripts are relocated.
+ *
+ * 7. Use vremap() to access memory mapped boards.
+ */
+
+/*
+ * Allow for simultaneous existence of multiple SCSI scripts so we
+ * can have a single driver binary for all of the family.
+ *
+ * - one for NCR53c700 and NCR53c700-66 chips (not yet supported)
+ * - one for rest (only the NCR53c810, 815, 820, and 825 are currently
+ * supported)
+ *
+ * So that we only need two SCSI scripts, we need to modify things so
+ * that we fixup register accesses in READ/WRITE instructions, and
+ * we'll also have to accommodate the bit vs. binary encoding of IDs
+ * with the 7xx chips.
+ */
+
+#define ROUNDUP(adr,type) \
+ ((void *) (((long) (adr) + sizeof(type) - 1) & ~(sizeof(type) - 1)))
+
+
+/*
+ * Function: issue_to_cmd
+ *
+ * Purpose: convert jump instruction in issue array to NCR53c7x0_cmd
+ * structure pointer.
+ *
+ * Inputs; issue - pointer to start of NOP or JUMP instruction
+ * in issue array.
+ *
+ * Returns: pointer to command on success; 0 if opcode is NOP.
+ */
+
+static inline struct NCR53c7x0_cmd *
+issue_to_cmd (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ u32 *issue)
+{
+ return (issue[0] != hostdata->NOP_insn) ?
+ /*
+ * If the IF TRUE bit is set, it's a JUMP instruction. The
+ * operand is a bus pointer to the dsa_begin routine for this DSA. The
+ * dsa field of the NCR53c7x0_cmd structure starts with the
+ * DSA code template. By converting to a virtual address,
+ * subtracting the code template size, and offset of the
+ * dsa field, we end up with a pointer to the start of the
+ * structure (alternatively, we could use the
+ * dsa_cmnd field, an anachronism from when we weren't
+ * sure what the relationship between the NCR structures
+ * and host structures were going to be.
+ */
+ (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) -
+ (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) -
+ offsetof(struct NCR53c7x0_cmd, dsa))
+ /* If the IF TRUE bit is not set, it's a NOP */
+ : NULL;
+}
+
+
+/*
+ * FIXME: we should junk these, in favor of synchronous_want and
+ * wide_want in the NCR53c7x0_hostdata structure.
+ */
+
+/* Template for "preferred" synchronous transfer parameters. */
+
+static const unsigned char sdtr_message[] = {
+#ifdef CONFIG_SCSI_NCR53C7xx_FAST
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 25 /* *4ns */, 8 /* off */
+#else
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */
+#endif
+};
+
+/* Template to request asynchronous transfers */
+
+static const unsigned char async_message[] = {
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */
+};
+
+/* Template for "preferred" WIDE transfer parameters */
+
+static const unsigned char wdtr_message[] = {
+ EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */
+};
+
+#if 0
+/*
+ * Function : struct Scsi_Host *find_host (int host)
+ *
+ * Purpose : KGDB support function which translates a host number
+ * to a host structure.
+ *
+ * Inputs : host - number of SCSI host
+ *
+ * Returns : NULL on failure, pointer to host structure on success.
+ */
+
+static struct Scsi_Host *
+find_host (int host) {
+ struct Scsi_Host *h;
+ for (h = first_host; h && h->host_no != host; h = h->next);
+ if (!h) {
+ printk (KERN_ALERT "scsi%d not found\n", host);
+ return NULL;
+ } else if (h->hostt != the_template) {
+ printk (KERN_ALERT "scsi%d is not a NCR board\n", host);
+ return NULL;
+ }
+ return h;
+}
+
+#if 0
+/*
+ * Function : request_synchronous (int host, int target)
+ *
+ * Purpose : KGDB interface which will allow us to negotiate for
+ * synchronous transfers. This ill be replaced with a more
+ * integrated function; perhaps a new entry in the scsi_host
+ * structure, accessible via an ioctl() or perhaps /proc/scsi.
+ *
+ * Inputs : host - number of SCSI host; target - number of target.
+ *
+ * Returns : 0 when negotiation has been setup for next SCSI command,
+ * -1 on failure.
+ */
+
+static int
+request_synchronous (int host, int target) {
+ struct Scsi_Host *h;
+ struct NCR53c7x0_hostdata *hostdata;
+ unsigned long flags;
+ if (target < 0) {
+ printk (KERN_ALERT "target %d is bogus\n", target);
+ return -1;
+ }
+ if (!(h = find_host (host)))
+ return -1;
+ else if (h->this_id == target) {
+ printk (KERN_ALERT "target %d is host ID\n", target);
+ return -1;
+ }
+ else if (target > h->max_id) {
+ printk (KERN_ALERT "target %d exceeds maximum of %d\n", target,
+ h->max_id);
+ return -1;
+ }
+ hostdata = (struct NCR53c7x0_hostdata *)h->hostdata[0];
+
+ local_irq_save(flags);
+ if (hostdata->initiate_sdtr & (1 << target)) {
+ local_irq_restore(flags);
+ printk (KERN_ALERT "target %d already doing SDTR\n", target);
+ return -1;
+ }
+ hostdata->initiate_sdtr |= (1 << target);
+ local_irq_restore(flags);
+ return 0;
+}
+#endif
+
+/*
+ * Function : request_disconnect (int host, int on_or_off)
+ *
+ * Purpose : KGDB support function, tells us to allow or disallow
+ * disconnections.
+ *
+ * Inputs : host - number of SCSI host; on_or_off - non-zero to allow,
+ * zero to disallow.
+ *
+ * Returns : 0 on success, * -1 on failure.
+ */
+
+static int
+request_disconnect (int host, int on_or_off) {
+ struct Scsi_Host *h;
+ struct NCR53c7x0_hostdata *hostdata;
+ if (!(h = find_host (host)))
+ return -1;
+ hostdata = (struct NCR53c7x0_hostdata *) h->hostdata[0];
+ if (on_or_off)
+ hostdata->options |= OPTION_DISCONNECT;
+ else
+ hostdata->options &= ~OPTION_DISCONNECT;
+ return 0;
+}
+#endif
+
+/*
+ * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
+ *
+ * Purpose : Initialize internal structures, as required on startup, or
+ * after a SCSI bus reset.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ */
+
+static void
+NCR53c7x0_driver_init (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ int i, j;
+ u32 *ncrcurrent;
+
+ for (i = 0; i < 16; ++i) {
+ hostdata->request_sense[i] = 0;
+ for (j = 0; j < 8; ++j)
+ hostdata->busy[i][j] = 0;
+ set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0);
+ }
+ hostdata->issue_queue = NULL;
+ hostdata->running_list = hostdata->finished_queue =
+ hostdata->ncrcurrent = NULL;
+ for (i = 0, ncrcurrent = (u32 *) hostdata->schedule;
+ i < host->can_queue; ++i, ncrcurrent += 2) {
+ ncrcurrent[0] = hostdata->NOP_insn;
+ ncrcurrent[1] = 0xdeadbeef;
+ }
+ ncrcurrent[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE;
+ ncrcurrent[1] = (u32) virt_to_bus (hostdata->script) +
+ hostdata->E_wait_reselect;
+ hostdata->reconnect_dsa_head = 0;
+ hostdata->addr_reconnect_dsa_head = (u32)
+ virt_to_bus((void *) &(hostdata->reconnect_dsa_head));
+ hostdata->expecting_iid = 0;
+ hostdata->expecting_sto = 0;
+ if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS)
+ hostdata->initiate_sdtr = 0xffff;
+ else
+ hostdata->initiate_sdtr = 0;
+ hostdata->talked_to = 0;
+ hostdata->idle = 1;
+}
+
+/*
+ * Function : static int clock_to_ccf_710 (int clock)
+ *
+ * Purpose : Return the clock conversion factor for a given SCSI clock.
+ *
+ * Inputs : clock - SCSI clock expressed in Hz.
+ *
+ * Returns : ccf on success, -1 on failure.
+ */
+
+static int
+clock_to_ccf_710 (int clock) {
+ if (clock <= 16666666)
+ return -1;
+ if (clock <= 25000000)
+ return 2; /* Divide by 1.0 */
+ else if (clock <= 37500000)
+ return 1; /* Divide by 1.5 */
+ else if (clock <= 50000000)
+ return 0; /* Divide by 2.0 */
+ else if (clock <= 66000000)
+ return 3; /* Divide by 3.0 */
+ else
+ return -1;
+}
+
+/*
+ * Function : static int NCR53c7x0_init (struct Scsi_Host *host)
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : when this function is called, the chip_type
+ * field of the hostdata structure MUST have been set.
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+int
+NCR53c7x0_init (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ int i, ccf;
+ unsigned char revision;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ /*
+ * There are some things which we need to know about in order to provide
+ * a semblance of support. Print 'em if they aren't what we expect,
+ * otherwise don't add to the noise.
+ *
+ * -1 means we don't know what to expect.
+ */
+ int val, flags;
+ char buf[32];
+ int expected_id = -1;
+ int expected_clock = -1;
+ int uninitialized = 0;
+#ifdef NO_IO_SPACE
+ int expected_mapping = OPTION_MEMORY_MAPPED;
+#else
+ int expected_mapping = OPTION_IO_MAPPED;
+#endif
+ for (i=0;i<7;i++)
+ hostdata->valid_ids[i] = 1; /* Default all ID's to scan */
+
+ /* Parse commandline flags */
+ if (check_setup_strings("noasync",&flags,&val,buf))
+ {
+ hostdata->options |= OPTION_NO_ASYNC;
+ hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS);
+ }
+
+ if (check_setup_strings("nosync",&flags,&val,buf))
+ {
+ hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS);
+ }
+
+ if (check_setup_strings("nodisconnect",&flags,&val,buf))
+ hostdata->options &= ~OPTION_DISCONNECT;
+
+ if (check_setup_strings("validids",&flags,&val,buf))
+ {
+ for (i=0;i<7;i++)
+ hostdata->valid_ids[i] = val & (1<<i);
+ }
+
+ if ((i = check_setup_strings("next",&flags,&val,buf)))
+ {
+ while (i)
+ setup_used[--i] = 1;
+ }
+
+ if (check_setup_strings("opthi",&flags,&val,buf))
+ hostdata->options = (long long)val << 32;
+ if (check_setup_strings("optlo",&flags,&val,buf))
+ hostdata->options |= val;
+
+ NCR53c7x0_local_setup(host);
+ switch (hostdata->chip) {
+ case 710:
+ case 770:
+ hostdata->dstat_sir_intr = NCR53c7x0_dstat_sir_intr;
+ hostdata->init_save_regs = NULL;
+ hostdata->dsa_fixup = NCR53c7xx_dsa_fixup;
+ hostdata->init_fixup = NCR53c7x0_init_fixup;
+ hostdata->soft_reset = NCR53c7x0_soft_reset;
+ hostdata->run_tests = NCR53c7xx_run_tests;
+ expected_clock = hostdata->scsi_clock;
+ expected_id = 7;
+ break;
+ default:
+ printk ("scsi%d : chip type of %d is not supported yet, detaching.\n",
+ host->host_no, hostdata->chip);
+ scsi_unregister (host);
+ return -1;
+ }
+
+ /* Assign constants accessed by NCR */
+ hostdata->NCR53c7xx_zero = 0;
+ hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT;
+ hostdata->NCR53c7xx_msg_abort = ABORT;
+ hostdata->NCR53c7xx_msg_nop = NOP;
+ hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24;
+ if (expected_mapping == -1 ||
+ (hostdata->options & (OPTION_MEMORY_MAPPED)) !=
+ (expected_mapping & OPTION_MEMORY_MAPPED))
+ printk ("scsi%d : using %s mapped access\n", host->host_no,
+ (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" :
+ "io");
+
+ hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ?
+ DMODE_REG_00 : DMODE_REG_10;
+ hostdata->istat = ((hostdata->chip / 100) == 8) ?
+ ISTAT_REG_800 : ISTAT_REG_700;
+
+/* We have to assume that this may be the first access to the chip, so
+ * we must set EA in DCNTL. */
+
+ NCR53c7x0_write8 (DCNTL_REG, DCNTL_10_EA|DCNTL_10_COM);
+
+
+/* Only the ISTAT register is readable when the NCR is running, so make
+ sure it's halted. */
+ ncr_halt(host);
+
+/*
+ * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
+ * as does the 710 with one bit per SCSI ID. Conversely, the NCR
+ * uses a normal, 3 bit binary representation of these values.
+ *
+ * Get the rest of the NCR documentation, and FIND OUT where the change
+ * was.
+ */
+
+#if 0
+ /* May not be able to do this - chip my not have been set up yet */
+ tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG);
+ for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id);
+#else
+ host->this_id = 7;
+#endif
+
+/*
+ * Note : we should never encounter a board setup for ID0. So,
+ * if we see ID0, assume that it was uninitialized and set it
+ * to the industry standard 7.
+ */
+ if (!host->this_id) {
+ printk("scsi%d : initiator ID was %d, changing to 7\n",
+ host->host_no, host->this_id);
+ host->this_id = 7;
+ hostdata->this_id_mask = 1 << 7;
+ uninitialized = 1;
+ };
+
+ if (expected_id == -1 || host->this_id != expected_id)
+ printk("scsi%d : using initiator ID %d\n", host->host_no,
+ host->this_id);
+
+ /*
+ * Save important registers to allow a soft reset.
+ */
+
+ /*
+ * CTEST7 controls cache snooping, burst mode, and support for
+ * external differential drivers. This isn't currently used - the
+ * default value may not be optimal anyway.
+ * Even worse, it may never have been set up since reset.
+ */
+ hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE;
+ revision = (NCR53c7x0_read8(CTEST8_REG) & 0xF0) >> 4;
+ switch (revision) {
+ case 1: revision = 0; break;
+ case 2: revision = 1; break;
+ case 4: revision = 2; break;
+ case 8: revision = 3; break;
+ default: revision = 255; break;
+ }
+ printk("scsi%d: Revision 0x%x\n",host->host_no,revision);
+
+ if ((revision == 0 || revision == 255) && (hostdata->options & (OPTION_SYNCHRONOUS|OPTION_DISCONNECT|OPTION_ALWAYS_SYNCHRONOUS)))
+ {
+ printk ("scsi%d: Disabling sync working and disconnect/reselect\n",
+ host->host_no);
+ hostdata->options &= ~(OPTION_SYNCHRONOUS|OPTION_DISCONNECT|OPTION_ALWAYS_SYNCHRONOUS);
+ }
+
+ /*
+ * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor,
+ * on 800 series chips, it allows for a totem-pole IRQ driver.
+ * NOTE saved_dcntl currently overwritten in init function.
+ * The value read here may be garbage anyway, MVME16x board at least
+ * does not initialise chip if kernel arrived via tftp.
+ */
+
+ hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG);
+
+ /*
+ * DMODE controls DMA burst length, and on 700 series chips,
+ * 286 mode and bus width
+ * NOTE: On MVME16x, chip may have been reset, so this could be a
+ * power-on/reset default value.
+ */
+ hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode);
+
+ /*
+ * Now that burst length and enabled/disabled status is known,
+ * clue the user in on it.
+ */
+
+ ccf = clock_to_ccf_710 (expected_clock);
+
+ for (i = 0; i < 16; ++i)
+ hostdata->cmd_allocated[i] = 0;
+
+ if (hostdata->init_save_regs)
+ hostdata->init_save_regs (host);
+ if (hostdata->init_fixup)
+ hostdata->init_fixup (host);
+
+ if (!the_template) {
+ the_template = host->hostt;
+ first_host = host;
+ }
+
+ /*
+ * Linux SCSI drivers have always been plagued with initialization
+ * problems - some didn't work with the BIOS disabled since they expected
+ * initialization from it, some didn't work when the networking code
+ * was enabled and registers got scrambled, etc.
+ *
+ * To avoid problems like this, in the future, we will do a soft
+ * reset on the SCSI chip, taking it back to a sane state.
+ */
+
+ hostdata->soft_reset (host);
+
+#if 1
+ hostdata->debug_count_limit = -1;
+#else
+ hostdata->debug_count_limit = 1;
+#endif
+ hostdata->intrs = -1;
+ hostdata->resets = -1;
+ memcpy ((void *) hostdata->synchronous_want, (void *) sdtr_message,
+ sizeof (hostdata->synchronous_want));
+
+ NCR53c7x0_driver_init (host);
+
+ if (request_irq(host->irq, NCR53c7x0_intr, SA_SHIRQ, "53c7xx", host))
+ {
+ printk("scsi%d : IRQ%d not free, detaching\n",
+ host->host_no, host->irq);
+ goto err_unregister;
+ }
+
+ if ((hostdata->run_tests && hostdata->run_tests(host) == -1) ||
+ (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) {
+ /* XXX Should disable interrupts, etc. here */
+ goto err_free_irq;
+ } else {
+ if (host->io_port) {
+ host->n_io_port = 128;
+ if (!request_region (host->io_port, host->n_io_port, "ncr53c7xx"))
+ goto err_free_irq;
+ }
+ }
+
+ if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) {
+ printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no);
+ hard_reset (host);
+ }
+ return 0;
+
+ err_free_irq:
+ free_irq(host->irq, NCR53c7x0_intr);
+ err_unregister:
+ scsi_unregister(host);
+ return -1;
+}
+
+/*
+ * Function : int ncr53c7xx_init(Scsi_Host_Template *tpnt, int board, int chip,
+ * unsigned long base, int io_port, int irq, int dma, long long options,
+ * int clock);
+ *
+ * Purpose : initializes a NCR53c7,8x0 based on base addresses,
+ * IRQ, and DMA channel.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ * product, chip - 710
+ *
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+int
+ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip,
+ unsigned long base, int io_port, int irq, int dma,
+ long long options, int clock)
+{
+ struct Scsi_Host *instance;
+ struct NCR53c7x0_hostdata *hostdata;
+ char chip_str[80];
+ int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0,
+ schedule_size = 0, ok = 0;
+ void *tmp;
+ unsigned long page;
+
+ switch (chip) {
+ case 710:
+ case 770:
+ schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */;
+ script_len = NCR53c7xx_script_len;
+ dsa_len = NCR53c7xx_dsa_len;
+ options |= OPTION_INTFLY;
+ sprintf (chip_str, "NCR53c%d", chip);
+ break;
+ default:
+ printk("scsi-ncr53c7xx : unsupported SCSI chip %d\n", chip);
+ return -1;
+ }
+
+ printk("scsi-ncr53c7xx : %s at memory 0x%lx, io 0x%x, irq %d",
+ chip_str, base, io_port, irq);
+ if (dma == DMA_NONE)
+ printk("\n");
+ else
+ printk(", dma %d\n", dma);
+
+ if (options & OPTION_DEBUG_PROBE_ONLY) {
+ printk ("scsi-ncr53c7xx : probe only enabled, aborting initialization\n");
+ return -1;
+ }
+
+ max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len +
+ /* Size of dynamic part of command structure : */
+ 2 * /* Worst case : we don't know if we need DATA IN or DATA out */
+ ( 2 * /* Current instructions per scatter/gather segment */
+ tpnt->sg_tablesize +
+ 3 /* Current startup / termination required per phase */
+ ) *
+ 8 /* Each instruction is eight bytes */;
+
+ /* Allocate fixed part of hostdata, dynamic part to hold appropriate
+ SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure.
+
+ We need a NCR53c7x0_cmd structure for scan_scsis() when we are
+ not loaded as a module, and when we're loaded as a module, we
+ can't use a non-dynamically allocated structure because modules
+ are vmalloc()'d, which can allow structures to cross page
+ boundaries and breaks our physical/virtual address assumptions
+ for DMA.
+
+ So, we stick it past the end of our hostdata structure.
+
+ ASSUMPTION :
+ Regardless of how many simultaneous SCSI commands we allow,
+ the probe code only executes a _single_ instruction at a time,
+ so we only need one here, and don't need to allocate NCR53c7x0_cmd
+ structures for each target until we are no longer in scan_scsis
+ and kmalloc() has become functional (memory_init() happens
+ after all device driver initialization).
+ */
+
+ size = sizeof(struct NCR53c7x0_hostdata) + script_len +
+ /* Note that alignment will be guaranteed, since we put the command
+ allocated at probe time after the fixed-up SCSI script, which
+ consists of 32 bit words, aligned on a 32 bit boundary. But
+ on a 64bit machine we need 8 byte alignment for hostdata->free, so
+ we add in another 4 bytes to take care of potential misalignment
+ */
+ (sizeof(void *) - sizeof(u32)) + max_cmd_size + schedule_size;
+
+ page = __get_free_pages(GFP_ATOMIC,1);
+ if(page==0)
+ {
+ printk(KERN_ERR "53c7xx: out of memory.\n");
+ return -ENOMEM;
+ }
+#ifdef FORCE_DSA_ALIGNMENT
+ /*
+ * 53c710 rev.0 doesn't have an add-with-carry instruction.
+ * Ensure we allocate enough memory to force DSA alignment.
+ */
+ size += 256;
+#endif
+ /* Size should be < 8K, so we can fit it in two pages. */
+ if (size > 8192) {
+ printk(KERN_ERR "53c7xx: hostdata > 8K\n");
+ return -1;
+ }
+
+ instance = scsi_register (tpnt, 4);
+ if (!instance)
+ {
+ free_page(page);
+ return -1;
+ }
+ instance->hostdata[0] = page;
+ memset((void *)instance->hostdata[0], 0, 8192);
+ cache_push(virt_to_phys((void *)(instance->hostdata[0])), 8192);
+ cache_clear(virt_to_phys((void *)(instance->hostdata[0])), 8192);
+ kernel_set_cachemode((void *)instance->hostdata[0], 8192, IOMAP_NOCACHE_SER);
+
+ /* FIXME : if we ever support an ISA NCR53c7xx based board, we
+ need to check if the chip is running in a 16 bit mode, and if so
+ unregister it if it is past the 16M (0x1000000) mark */
+
+ hostdata = (struct NCR53c7x0_hostdata *)instance->hostdata[0];
+ hostdata->size = size;
+ hostdata->script_count = script_len / sizeof(u32);
+ hostdata->board = board;
+ hostdata->chip = chip;
+
+ /*
+ * Being memory mapped is more desirable, since
+ *
+ * - Memory accesses may be faster.
+ *
+ * - The destination and source address spaces are the same for
+ * all instructions, meaning we don't have to twiddle dmode or
+ * any other registers.
+ *
+ * So, we try for memory mapped, and if we don't get it,
+ * we go for port mapped, and that failing we tell the user
+ * it can't work.
+ */
+
+ if (base) {
+ instance->base = base;
+ /* Check for forced I/O mapping */
+ if (!(options & OPTION_IO_MAPPED)) {
+ options |= OPTION_MEMORY_MAPPED;
+ ok = 1;
+ }
+ } else {
+ options &= ~OPTION_MEMORY_MAPPED;
+ }
+
+ if (io_port) {
+ instance->io_port = io_port;
+ options |= OPTION_IO_MAPPED;
+ ok = 1;
+ } else {
+ options &= ~OPTION_IO_MAPPED;
+ }
+
+ if (!ok) {
+ printk ("scsi%d : not initializing, no I/O or memory mapping known \n",
+ instance->host_no);
+ scsi_unregister (instance);
+ return -1;
+ }
+ instance->irq = irq;
+ instance->dma_channel = dma;
+
+ hostdata->options = options;
+ hostdata->dsa_len = dsa_len;
+ hostdata->max_cmd_size = max_cmd_size;
+ hostdata->num_cmds = 1;
+ hostdata->scsi_clock = clock;
+ /* Initialize single command */
+ tmp = (hostdata->script + hostdata->script_count);
+#ifdef FORCE_DSA_ALIGNMENT
+ {
+ void *t = ROUNDUP(tmp, void *);
+ if (((u32)t & 0xff) > CmdPageStart)
+ t = (void *)((u32)t + 255);
+ t = (void *)(((u32)t & ~0xff) + CmdPageStart);
+ hostdata->free = t;
+#if 0
+ printk ("scsi: Registered size increased by 256 to %d\n", size);
+ printk ("scsi: CmdPageStart = 0x%02x\n", CmdPageStart);
+ printk ("scsi: tmp = 0x%08x, hostdata->free set to 0x%08x\n",
+ (u32)tmp, (u32)t);
+#endif
+ }
+#else
+ hostdata->free = ROUNDUP(tmp, void *);
+#endif
+ hostdata->free->real = tmp;
+ hostdata->free->size = max_cmd_size;
+ hostdata->free->free = NULL;
+ hostdata->free->next = NULL;
+ hostdata->extra_allocate = 0;
+
+ /* Allocate command start code space */
+ hostdata->schedule = (chip == 700 || chip == 70066) ?
+ NULL : (u32 *) ((char *)hostdata->free + max_cmd_size);
+
+/*
+ * For diagnostic purposes, we don't really care how fast things blaze.
+ * For profiling, we want to access the 800ns resolution system clock,
+ * using a 'C' call on the host processor.
+ *
+ * Therefore, there's no need for the NCR chip to directly manipulate
+ * this data, and we should put it wherever is most convenient for
+ * Linux.
+ */
+ if (track_events)
+ hostdata->events = (struct NCR53c7x0_event *) (track_events ?
+ vmalloc (sizeof (struct NCR53c7x0_event) * track_events) : NULL);
+ else
+ hostdata->events = NULL;
+
+ if (hostdata->events) {
+ memset ((void *) hostdata->events, 0, sizeof(struct NCR53c7x0_event) *
+ track_events);
+ hostdata->event_size = track_events;
+ hostdata->event_index = 0;
+ } else
+ hostdata->event_size = 0;
+
+ return NCR53c7x0_init(instance);
+}
+
+
+/*
+ * Function : static void NCR53c7x0_init_fixup (struct Scsi_Host *host)
+ *
+ * Purpose : copy and fixup the SCSI SCRIPTS(tm) code for this device.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ */
+
+static void
+NCR53c7x0_init_fixup (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned char tmp;
+ int i, ncr_to_memory, memory_to_ncr;
+ u32 base;
+ NCR53c7x0_local_setup(host);
+
+
+ /* XXX - NOTE : this code MUST be made endian aware */
+ /* Copy code into buffer that was allocated at detection time. */
+ memcpy ((void *) hostdata->script, (void *) SCRIPT,
+ sizeof(SCRIPT));
+ /* Fixup labels */
+ for (i = 0; i < PATCHES; ++i)
+ hostdata->script[LABELPATCHES[i]] +=
+ virt_to_bus(hostdata->script);
+ /* Fixup addresses of constants that used to be EXTERNAL */
+
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_abort)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_reject)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero,
+ virt_to_bus(&(hostdata->NCR53c7xx_zero)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink,
+ virt_to_bus(&(hostdata->NCR53c7xx_sink)));
+ patch_abs_32 (hostdata->script, 0, NOP_insn,
+ virt_to_bus(&(hostdata->NOP_insn)));
+ patch_abs_32 (hostdata->script, 0, schedule,
+ virt_to_bus((void *) hostdata->schedule));
+
+ /* Fixup references to external variables: */
+ for (i = 0; i < EXTERNAL_PATCHES_LEN; ++i)
+ hostdata->script[EXTERNAL_PATCHES[i].offset] +=
+ virt_to_bus(EXTERNAL_PATCHES[i].address);
+
+ /*
+ * Fixup absolutes set at boot-time.
+ *
+ * All non-code absolute variables suffixed with "dsa_" and "int_"
+ * are constants, and need no fixup provided the assembler has done
+ * it for us (I don't know what the "real" NCR assembler does in
+ * this case, my assembler does the right magic).
+ */
+
+ patch_abs_rwri_data (hostdata->script, 0, dsa_save_data_pointer,
+ Ent_dsa_code_save_data_pointer - Ent_dsa_zero);
+ patch_abs_rwri_data (hostdata->script, 0, dsa_restore_pointers,
+ Ent_dsa_code_restore_pointers - Ent_dsa_zero);
+ patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect,
+ Ent_dsa_code_check_reselect - Ent_dsa_zero);
+
+ /*
+ * Just for the hell of it, preserve the settings of
+ * Burst Length and Enable Read Line bits from the DMODE
+ * register. Make sure SCRIPTS start automagically.
+ */
+
+#if defined(CONFIG_MVME16x) || defined(CONFIG_BVME6000)
+ /* We know better what we want than 16xBug does! */
+ tmp = DMODE_10_BL_8 | DMODE_10_FC2;
+#else
+ tmp = NCR53c7x0_read8(DMODE_REG_10);
+ tmp &= (DMODE_BL_MASK | DMODE_10_FC2 | DMODE_10_FC1 | DMODE_710_PD |
+ DMODE_710_UO);
+#endif
+
+ if (!(hostdata->options & OPTION_MEMORY_MAPPED)) {
+ base = (u32) host->io_port;
+ memory_to_ncr = tmp|DMODE_800_DIOM;
+ ncr_to_memory = tmp|DMODE_800_SIOM;
+ } else {
+ base = virt_to_bus((void *)host->base);
+ memory_to_ncr = ncr_to_memory = tmp;
+ }
+
+ /* SCRATCHB_REG_10 == SCRATCHA_REG_800, as it happens */
+ patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800);
+ patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG);
+ patch_abs_32 (hostdata->script, 0, addr_dsa, base + DSA_REG);
+
+ /*
+ * I needed some variables in the script to be accessible to
+ * both the NCR chip and the host processor. For these variables,
+ * I made the arbitrary decision to store them directly in the
+ * hostdata structure rather than in the RELATIVE area of the
+ * SCRIPTS.
+ */
+
+
+ patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp);
+ patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr);
+ patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory);
+
+ patch_abs_32 (hostdata->script, 0, msg_buf,
+ virt_to_bus((void *)&(hostdata->msg_buf)));
+ patch_abs_32 (hostdata->script, 0, reconnect_dsa_head,
+ virt_to_bus((void *)&(hostdata->reconnect_dsa_head)));
+ patch_abs_32 (hostdata->script, 0, addr_reconnect_dsa_head,
+ virt_to_bus((void *)&(hostdata->addr_reconnect_dsa_head)));
+ patch_abs_32 (hostdata->script, 0, reselected_identify,
+ virt_to_bus((void *)&(hostdata->reselected_identify)));
+/* reselected_tag is currently unused */
+#if 0
+ patch_abs_32 (hostdata->script, 0, reselected_tag,
+ virt_to_bus((void *)&(hostdata->reselected_tag)));
+#endif
+
+ patch_abs_32 (hostdata->script, 0, test_dest,
+ virt_to_bus((void*)&hostdata->test_dest));
+ patch_abs_32 (hostdata->script, 0, test_src,
+ virt_to_bus(&hostdata->test_source));
+ patch_abs_32 (hostdata->script, 0, saved_dsa,
+ virt_to_bus((void *)&hostdata->saved2_dsa));
+ patch_abs_32 (hostdata->script, 0, emulfly,
+ virt_to_bus((void *)&hostdata->emulated_intfly));
+
+ patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect,
+ (unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero));
+
+/* These are for event logging; the ncr_event enum contains the
+ actual interrupt numbers. */
+#ifdef A_int_EVENT_SELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT, (u32) EVENT_SELECT);
+#endif
+#ifdef A_int_EVENT_DISCONNECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_DISCONNECT, (u32) EVENT_DISCONNECT);
+#endif
+#ifdef A_int_EVENT_RESELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT, (u32) EVENT_RESELECT);
+#endif
+#ifdef A_int_EVENT_COMPLETE
+ patch_abs_32 (hostdata->script, 0, int_EVENT_COMPLETE, (u32) EVENT_COMPLETE);
+#endif
+#ifdef A_int_EVENT_IDLE
+ patch_abs_32 (hostdata->script, 0, int_EVENT_IDLE, (u32) EVENT_IDLE);
+#endif
+#ifdef A_int_EVENT_SELECT_FAILED
+ patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT_FAILED,
+ (u32) EVENT_SELECT_FAILED);
+#endif
+#ifdef A_int_EVENT_BEFORE_SELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_BEFORE_SELECT,
+ (u32) EVENT_BEFORE_SELECT);
+#endif
+#ifdef A_int_EVENT_RESELECT_FAILED
+ patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT_FAILED,
+ (u32) EVENT_RESELECT_FAILED);
+#endif
+
+ /*
+ * Make sure the NCR and Linux code agree on the location of
+ * certain fields.
+ */
+
+ hostdata->E_accept_message = Ent_accept_message;
+ hostdata->E_command_complete = Ent_command_complete;
+ hostdata->E_cmdout_cmdout = Ent_cmdout_cmdout;
+ hostdata->E_data_transfer = Ent_data_transfer;
+ hostdata->E_debug_break = Ent_debug_break;
+ hostdata->E_dsa_code_template = Ent_dsa_code_template;
+ hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end;
+ hostdata->E_end_data_transfer = Ent_end_data_transfer;
+ hostdata->E_initiator_abort = Ent_initiator_abort;
+ hostdata->E_msg_in = Ent_msg_in;
+ hostdata->E_other_transfer = Ent_other_transfer;
+ hostdata->E_other_in = Ent_other_in;
+ hostdata->E_other_out = Ent_other_out;
+ hostdata->E_reject_message = Ent_reject_message;
+ hostdata->E_respond_message = Ent_respond_message;
+ hostdata->E_select = Ent_select;
+ hostdata->E_select_msgout = Ent_select_msgout;
+ hostdata->E_target_abort = Ent_target_abort;
+#ifdef Ent_test_0
+ hostdata->E_test_0 = Ent_test_0;
+#endif
+ hostdata->E_test_1 = Ent_test_1;
+ hostdata->E_test_2 = Ent_test_2;
+#ifdef Ent_test_3
+ hostdata->E_test_3 = Ent_test_3;
+#endif
+ hostdata->E_wait_reselect = Ent_wait_reselect;
+ hostdata->E_dsa_code_begin = Ent_dsa_code_begin;
+
+ hostdata->dsa_cmdout = A_dsa_cmdout;
+ hostdata->dsa_cmnd = A_dsa_cmnd;
+ hostdata->dsa_datain = A_dsa_datain;
+ hostdata->dsa_dataout = A_dsa_dataout;
+ hostdata->dsa_end = A_dsa_end;
+ hostdata->dsa_msgin = A_dsa_msgin;
+ hostdata->dsa_msgout = A_dsa_msgout;
+ hostdata->dsa_msgout_other = A_dsa_msgout_other;
+ hostdata->dsa_next = A_dsa_next;
+ hostdata->dsa_select = A_dsa_select;
+ hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero;
+ hostdata->dsa_status = A_dsa_status;
+ hostdata->dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero +
+ 8 /* destination operand */;
+
+ /* sanity check */
+ if (A_dsa_fields_start != Ent_dsa_code_template_end -
+ Ent_dsa_zero)
+ printk("scsi%d : NCR dsa_fields start is %d not %d\n",
+ host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end -
+ Ent_dsa_zero);
+
+ printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no,
+ virt_to_bus(hostdata->script), hostdata->script);
+}
+
+/*
+ * Function : static int NCR53c7xx_run_tests (struct Scsi_Host *host)
+ *
+ * Purpose : run various verification tests on the NCR chip,
+ * including interrupt generation, and proper bus mastering
+ * operation.
+ *
+ * Inputs : host - a properly initialized Scsi_Host structure
+ *
+ * Preconditions : the NCR chip must be in a halted state.
+ *
+ * Returns : 0 if all tests were successful, -1 on error.
+ *
+ */
+
+static int
+NCR53c7xx_run_tests (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned long timeout;
+ u32 start;
+ int failed, i;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ /* The NCR chip _must_ be idle to run the test scripts */
+
+ local_irq_save(flags);
+ if (!hostdata->idle) {
+ printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+ local_irq_restore(flags);
+ return -1;
+ }
+
+ /*
+ * Check for functional interrupts, this could work as an
+ * autoprobe routine.
+ */
+
+ if ((hostdata->options & OPTION_DEBUG_TEST1) &&
+ hostdata->state != STATE_DISABLED) {
+ hostdata->idle = 0;
+ hostdata->test_running = 1;
+ hostdata->test_completed = -1;
+ hostdata->test_dest = 0;
+ hostdata->test_source = 0xdeadbeef;
+ start = virt_to_bus (hostdata->script) + hostdata->E_test_1;
+ hostdata->state = STATE_RUNNING;
+ printk ("scsi%d : test 1", host->host_no);
+ NCR53c7x0_write32 (DSP_REG, start);
+ if (hostdata->options & OPTION_DEBUG_TRACE)
+ NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM |
+ DCNTL_STD);
+ printk (" started\n");
+ local_irq_restore(flags);
+
+ /*
+ * This is currently a .5 second timeout, since (in theory) no slow
+ * board will take that long. In practice, we've seen one
+ * pentium which occassionally fails with this, but works with
+ * 10 times as much?
+ */
+
+ timeout = jiffies + 5 * HZ / 10;
+ while ((hostdata->test_completed == -1) && time_before(jiffies, timeout))
+ barrier();
+
+ failed = 1;
+ if (hostdata->test_completed == -1)
+ printk ("scsi%d : driver test 1 timed out%s\n",host->host_no ,
+ (hostdata->test_dest == 0xdeadbeef) ?
+ " due to lost interrupt.\n"
+ " Please verify that the correct IRQ is being used for your board,\n"
+ : "");
+ else if (hostdata->test_completed != 1)
+ printk ("scsi%d : test 1 bad interrupt value (%d)\n",
+ host->host_no, hostdata->test_completed);
+ else
+ failed = (hostdata->test_dest != 0xdeadbeef);
+
+ if (hostdata->test_dest != 0xdeadbeef) {
+ printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n"
+ " probable cache invalidation problem. Please configure caching\n"
+ " as write-through or disabled\n",
+ host->host_no, hostdata->test_dest);
+ }
+
+ if (failed) {
+ printk ("scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x)\n",
+ host->host_no, bus_to_virt(NCR53c7x0_read32(DSP_REG)),
+ hostdata->script, start);
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no,
+ NCR53c7x0_read32(DSPS_REG));
+ local_irq_restore(flags);
+ return -1;
+ }
+ hostdata->test_running = 0;
+ }
+
+ if ((hostdata->options & OPTION_DEBUG_TEST2) &&
+ hostdata->state != STATE_DISABLED) {
+ u32 dsa[48];
+ unsigned char identify = IDENTIFY(0, 0);
+ unsigned char cmd[6];
+ unsigned char data[36];
+ unsigned char status = 0xff;
+ unsigned char msg = 0xff;
+
+ cmd[0] = INQUIRY;
+ cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0;
+ cmd[4] = sizeof(data);
+
+ dsa[2] = 1;
+ dsa[3] = virt_to_bus(&identify);
+ dsa[4] = 6;
+ dsa[5] = virt_to_bus(&cmd);
+ dsa[6] = sizeof(data);
+ dsa[7] = virt_to_bus(&data);
+ dsa[8] = 1;
+ dsa[9] = virt_to_bus(&status);
+ dsa[10] = 1;
+ dsa[11] = virt_to_bus(&msg);
+
+ for (i = 0; i < 6; ++i) {
+#ifdef VALID_IDS
+ if (!hostdata->valid_ids[i])
+ continue;
+#endif
+ local_irq_disable();
+ if (!hostdata->idle) {
+ printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+ local_irq_restore(flags);
+ return -1;
+ }
+
+ /* 710: bit mapped scsi ID, async */
+ dsa[0] = (1 << i) << 16;
+ hostdata->idle = 0;
+ hostdata->test_running = 2;
+ hostdata->test_completed = -1;
+ start = virt_to_bus(hostdata->script) + hostdata->E_test_2;
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa));
+ NCR53c7x0_write32 (DSP_REG, start);
+ if (hostdata->options & OPTION_DEBUG_TRACE)
+ NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl |
+ DCNTL_SSM | DCNTL_STD);
+ local_irq_restore(flags);
+
+ timeout = jiffies + 5 * HZ; /* arbitrary */
+ while ((hostdata->test_completed == -1) && time_before(jiffies, timeout))
+ barrier();
+
+ NCR53c7x0_write32 (DSA_REG, 0);
+
+ if (hostdata->test_completed == 2) {
+ data[35] = 0;
+ printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n",
+ host->host_no, i, data + 8);
+ printk ("scsi%d : status ", host->host_no);
+ print_status (status);
+ printk ("\nscsi%d : message ", host->host_no);
+ print_msg (&msg);
+ printk ("\n");
+ } else if (hostdata->test_completed == 3) {
+ printk("scsi%d : test 2 no connection with target %d\n",
+ host->host_no, i);
+ if (!hostdata->idle) {
+ printk("scsi%d : not idle\n", host->host_no);
+ local_irq_restore(flags);
+ return -1;
+ }
+ } else if (hostdata->test_completed == -1) {
+ printk ("scsi%d : test 2 timed out\n", host->host_no);
+ local_irq_restore(flags);
+ return -1;
+ }
+ hostdata->test_running = 0;
+ }
+ }
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*
+ * Function : static void NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer,
+ * performing all necessary relocation.
+ *
+ * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large
+ * enough to hold the NCR53c8xx dsa.
+ */
+
+static void
+NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) {
+ Scsi_Cmnd *c = cmd->cmd;
+ struct Scsi_Host *host = c->device->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ int i;
+
+ memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4),
+ hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template);
+
+ /*
+ * Note : within the NCR 'C' code, dsa points to the _start_
+ * of the DSA structure, and _not_ the offset of dsa_zero within
+ * that structure used to facilitate shorter signed offsets
+ * for the 8 bit ALU.
+ *
+ * The implications of this are that
+ *
+ * - 32 bit A_dsa_* absolute values require an additional
+ * dsa_zero added to their value to be correct, since they are
+ * relative to dsa_zero which is in essentially a separate
+ * space from the code symbols.
+ *
+ * - All other symbols require no special treatment.
+ */
+
+ patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_lun, c->device->lun);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_next, virt_to_bus(&cmd->dsa_next_addr));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_next, virt_to_bus(cmd->dsa) + Ent_dsa_zero -
+ Ent_dsa_code_template + A_dsa_next);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_sync, virt_to_bus((void *)hostdata->sync[c->device->id].script));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_sscf_710, virt_to_bus((void *)&hostdata->sync[c->device->id].sscf_710));
+ patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_target, 1 << c->device->id);
+ /* XXX - new pointer stuff */
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_saved_pointer, virt_to_bus(&cmd->saved_data_pointer));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_saved_residual, virt_to_bus(&cmd->saved_residual));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_residual, virt_to_bus(&cmd->residual));
+
+ /* XXX - new start stuff */
+
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr));
+}
+
+/*
+ * Function : run_process_issue_queue (void)
+ *
+ * Purpose : insure that the coroutine is running and will process our
+ * request. process_issue_queue_running is checked/set here (in an
+ * inline function) rather than in process_issue_queue itself to reduce
+ * the chances of stack overflow.
+ *
+ */
+
+static volatile int process_issue_queue_running = 0;
+
+static __inline__ void
+run_process_issue_queue(void) {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (!process_issue_queue_running) {
+ process_issue_queue_running = 1;
+ process_issue_queue(flags);
+ /*
+ * process_issue_queue_running is cleared in process_issue_queue
+ * once it can't do more work, and process_issue_queue exits with
+ * interrupts disabled.
+ */
+ }
+ local_irq_restore(flags);
+}
+
+/*
+ * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int
+ * result)
+ *
+ * Purpose : mark SCSI command as finished, OR'ing the host portion
+ * of the result word into the result field of the corresponding
+ * Scsi_Cmnd structure, and removing it from the internal queues.
+ *
+ * Inputs : cmd - command, result - entire result field
+ *
+ * Preconditions : the NCR chip should be in a halted state when
+ * abnormal_finished is run, since it modifies structures which
+ * the NCR expects to have exclusive access to.
+ */
+
+static void
+abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
+ Scsi_Cmnd *c = cmd->cmd;
+ struct Scsi_Host *host = c->device->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned long flags;
+ int left, found;
+ volatile struct NCR53c7x0_cmd * linux_search;
+ volatile struct NCR53c7x0_cmd * volatile *linux_prev;
+ volatile u32 *ncr_prev, *ncrcurrent, ncr_search;
+
+#if 0
+ printk ("scsi%d: abnormal finished\n", host->host_no);
+#endif
+
+ local_irq_save(flags);
+ found = 0;
+ /*
+ * Traverse the NCR issue array until we find a match or run out
+ * of instructions. Instructions in the NCR issue array are
+ * either JUMP or NOP instructions, which are 2 words in length.
+ */
+
+
+ for (found = 0, left = host->can_queue, ncrcurrent = hostdata->schedule;
+ left > 0; --left, ncrcurrent += 2)
+ {
+ if (issue_to_cmd (host, hostdata, (u32 *) ncrcurrent) == cmd)
+ {
+ ncrcurrent[0] = hostdata->NOP_insn;
+ ncrcurrent[1] = 0xdeadbeef;
+ ++found;
+ break;
+ }
+ }
+
+ /*
+ * Traverse the NCR reconnect list of DSA structures until we find
+ * a pointer to this dsa or have found too many command structures.
+ * We let prev point at the next field of the previous element or
+ * head of the list, so we don't do anything different for removing
+ * the head element.
+ */
+
+ for (left = host->can_queue,
+ ncr_search = hostdata->reconnect_dsa_head,
+ ncr_prev = &hostdata->reconnect_dsa_head;
+ left >= 0 && ncr_search &&
+ ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start)
+ != (char *) cmd->dsa;
+ ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) +
+ hostdata->dsa_next), ncr_search = *ncr_prev, --left);
+
+ if (left < 0)
+ printk("scsi%d: loop detected in ncr reconncect list\n",
+ host->host_no);
+ else if (ncr_search) {
+ if (found)
+ printk("scsi%d: scsi %ld in ncr issue array and reconnect lists\n",
+ host->host_no, c->pid);
+ else {
+ volatile u32 * next = (u32 *)
+ ((char *)bus_to_virt(ncr_search) + hostdata->dsa_next);
+ *ncr_prev = *next;
+/* If we're at the tail end of the issue queue, update that pointer too. */
+ found = 1;
+ }
+ }
+
+ /*
+ * Traverse the host running list until we find this command or discover
+ * we have too many elements, pointing linux_prev at the next field of the
+ * linux_previous element or head of the list, search at this element.
+ */
+
+ for (left = host->can_queue, linux_search = hostdata->running_list,
+ linux_prev = &hostdata->running_list;
+ left >= 0 && linux_search && linux_search != cmd;
+ linux_prev = &(linux_search->next),
+ linux_search = linux_search->next, --left);
+
+ if (left < 0)
+ printk ("scsi%d: loop detected in host running list for scsi pid %ld\n",
+ host->host_no, c->pid);
+ else if (linux_search) {
+ *linux_prev = linux_search->next;
+ --hostdata->busy[c->device->id][c->device->lun];
+ }
+
+ /* Return the NCR command structure to the free list */
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
+ c->host_scribble = NULL;
+
+ /* And return */
+ c->result = result;
+ c->scsi_done(c);
+
+ local_irq_restore(flags);
+ run_process_issue_queue();
+}
+
+/*
+ * Function : static void intr_break (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handler for breakpoint interrupts from a SCSI script
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ * cmd - pointer to the command (if any) dsa was pointing
+ * to.
+ *
+ */
+
+static void
+intr_break (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_break *bp;
+#if 0
+ Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+#endif
+ u32 *dsp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Find the break point corresponding to this address, and
+ * dump the appropriate debugging information to standard
+ * output.
+ */
+ local_irq_save(flags);
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ for (bp = hostdata->breakpoints; bp && bp->address != dsp;
+ bp = bp->next);
+ if (!bp)
+ panic("scsi%d : break point interrupt from %p with no breakpoint!",
+ host->host_no, dsp);
+
+ /*
+ * Configure the NCR chip for manual start mode, so that we can
+ * point the DSP register at the instruction that follows the
+ * INT int_debug_break instruction.
+ */
+
+ NCR53c7x0_write8 (hostdata->dmode,
+ NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN);
+
+ /*
+ * And update the DSP register, using the size of the old
+ * instruction in bytes.
+ */
+
+ local_irq_restore(flags);
+}
+/*
+ * Function : static void print_synchronous (const char *prefix,
+ * const unsigned char *msg)
+ *
+ * Purpose : print a pretty, user and machine parsable representation
+ * of a SDTR message, including the "real" parameters, data
+ * clock so we can tell transfer rate at a glance.
+ *
+ * Inputs ; prefix - text to prepend, msg - SDTR message (5 bytes)
+ */
+
+static void
+print_synchronous (const char *prefix, const unsigned char *msg) {
+ if (msg[4]) {
+ int Hz = 1000000000 / (msg[3] * 4);
+ int integer = Hz / 1000000;
+ int fraction = (Hz - (integer * 1000000)) / 10000;
+ printk ("%speriod %dns offset %d %d.%02dMHz %s SCSI%s\n",
+ prefix, (int) msg[3] * 4, (int) msg[4], integer, fraction,
+ (((msg[3] * 4) < 200) ? "FAST" : "synchronous"),
+ (((msg[3] * 4) < 200) ? "-II" : ""));
+ } else
+ printk ("%sasynchronous SCSI\n", prefix);
+}
+
+/*
+ * Function : static void set_synchronous (struct Scsi_Host *host,
+ * int target, int sxfer, int scntl3, int now_connected)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and
+ * target with the given register values; in the indirect
+ * select operand, reselection script, and chip registers.
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ * sxfer and scntl3 - NCR registers. now_connected - if non-zero,
+ * we should reprogram the registers now too.
+ *
+ * NOTE: For 53c710, scntl3 is actually used for SCF bits from
+ * SBCL, as we don't have a SCNTL3.
+ */
+
+static void
+set_synchronous (struct Scsi_Host *host, int target, int sxfer, int scntl3,
+ int now_connected) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ u32 *script;
+ NCR53c7x0_local_setup(host);
+
+ /* These are eight bit registers */
+ sxfer &= 0xff;
+ scntl3 &= 0xff;
+
+ hostdata->sync[target].sxfer_sanity = sxfer;
+ hostdata->sync[target].scntl3_sanity = scntl3;
+
+/*
+ * HARD CODED : synchronous script is EIGHT words long. This
+ * must agree with 53c7.8xx.h
+ */
+
+ if ((hostdata->chip != 700) && (hostdata->chip != 70066)) {
+ hostdata->sync[target].select_indirect = (1 << target) << 16 |
+ (sxfer << 8);
+ hostdata->sync[target].sscf_710 = scntl3;
+
+ script = (u32 *) hostdata->sync[target].script;
+
+ /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
+ script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+ DCMD_RWRI_OP_MOVE) << 24) |
+ (SBCL_REG << 16) | (scntl3 << 8);
+ script[1] = 0;
+ script += 2;
+
+ script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+ DCMD_RWRI_OP_MOVE) << 24) |
+ (SXFER_REG << 16) | (sxfer << 8);
+ script[1] = 0;
+ script += 2;
+
+#ifdef DEBUG_SYNC_INTR
+ if (hostdata->options & OPTION_DEBUG_DISCONNECT) {
+ script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_INT) << 24) | DBC_TCI_TRUE;
+ script[1] = DEBUG_SYNC_INTR;
+ script += 2;
+ }
+#endif
+
+ script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE;
+ script[1] = 0;
+ script += 2;
+ }
+
+ if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS)
+ printk ("scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, target, sxfer, scntl3);
+
+ if (now_connected) {
+ NCR53c7x0_write8(SBCL_REG, scntl3);
+ NCR53c7x0_write8(SXFER_REG, sxfer);
+ }
+}
+
+
+/*
+ * Function : static int asynchronous (struct Scsi_Host *host, int target)
+ *
+ * Purpose : reprogram between the selected SCSI Host adapter and target
+ * (assumed to be currently connected) for asynchronous transfers.
+ *
+ * Inputs : host - SCSI host structure, target - numeric target ID.
+ *
+ * Preconditions : the NCR chip should be in one of the halted states
+ */
+
+static void
+asynchronous (struct Scsi_Host *host, int target) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ NCR53c7x0_local_setup(host);
+ set_synchronous (host, target, /* no offset */ 0, hostdata->saved_scntl3,
+ 1);
+ printk ("scsi%d : setting target %d to asynchronous SCSI\n",
+ host->host_no, target);
+}
+
+/*
+ * XXX - do we want to go out of our way (ie, add extra code to selection
+ * in the NCR53c710/NCR53c720 script) to reprogram the synchronous
+ * conversion bits, or can we be content in just setting the
+ * sxfer bits? I chose to do so [richard@sleepie.demon.co.uk]
+ */
+
+/* Table for NCR53c8xx synchronous values */
+
+/* This table is also correct for 710, allowing that scf=4 is equivalent
+ * of SSCF=0 (ie use DCNTL, divide by 3) for a 50.01-66.00MHz clock.
+ * For any other clock values, we cannot use entries with SCF values of
+ * 4. I guess that for a 66MHz clock, the slowest it will set is 2MHz,
+ * and for a 50MHz clock, the slowest will be 2.27Mhz. Should check
+ * that a device doesn't try and negotiate sync below these limits!
+ */
+
+static const struct {
+ int div; /* Total clock divisor * 10 */
+ unsigned char scf; /* */
+ unsigned char tp; /* 4 + tp = xferp divisor */
+} syncs[] = {
+/* div scf tp div scf tp div scf tp */
+ { 40, 1, 0}, { 50, 1, 1}, { 60, 1, 2},
+ { 70, 1, 3}, { 75, 2, 1}, { 80, 1, 4},
+ { 90, 1, 5}, { 100, 1, 6}, { 105, 2, 3},
+ { 110, 1, 7}, { 120, 2, 4}, { 135, 2, 5},
+ { 140, 3, 3}, { 150, 2, 6}, { 160, 3, 4},
+ { 165, 2, 7}, { 180, 3, 5}, { 200, 3, 6},
+ { 210, 4, 3}, { 220, 3, 7}, { 240, 4, 4},
+ { 270, 4, 5}, { 300, 4, 6}, { 330, 4, 7}
+};
+
+/*
+ * Function : static void synchronous (struct Scsi_Host *host, int target,
+ * char *msg)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and
+ * target for synchronous SCSI transfers such that the synchronous
+ * offset is less than that requested and period at least as long
+ * as that requested. Also modify *msg such that it contains
+ * an appropriate response.
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ * msg - synchronous transfer request.
+ */
+
+
+static void
+synchronous (struct Scsi_Host *host, int target, char *msg) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ int desire, divisor, i, limit;
+ unsigned char scntl3, sxfer;
+/* The diagnostic message fits on one line, even with max. width integers */
+ char buf[80];
+
+/* Desired transfer clock in Hz */
+ desire = 1000000000L / (msg[3] * 4);
+/* Scale the available SCSI clock by 10 so we get tenths */
+ divisor = (hostdata->scsi_clock * 10) / desire;
+
+/* NCR chips can handle at most an offset of 8 */
+ if (msg[4] > 8)
+ msg[4] = 8;
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : optimal synchronous divisor of %d.%01d\n",
+ host->host_no, divisor / 10, divisor % 10);
+
+ limit = (sizeof(syncs) / sizeof(syncs[0]) -1);
+ for (i = 0; (i < limit) && (divisor > syncs[i].div); ++i);
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : selected synchronous divisor of %d.%01d\n",
+ host->host_no, syncs[i].div / 10, syncs[i].div % 10);
+
+ msg[3] = ((1000000000L / hostdata->scsi_clock) * syncs[i].div / 10 / 4);
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : selected synchronous period of %dns\n", host->host_no,
+ msg[3] * 4);
+
+ scntl3 = syncs[i].scf;
+ sxfer = (msg[4] << SXFER_MO_SHIFT) | (syncs[i].tp << 4);
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk ("scsi%d : sxfer=0x%x scntl3=0x%x\n",
+ host->host_no, (int) sxfer, (int) scntl3);
+ set_synchronous (host, target, sxfer, scntl3, 1);
+ sprintf (buf, "scsi%d : setting target %d to ", host->host_no, target);
+ print_synchronous (buf, msg);
+}
+
+/*
+ * Function : static int NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handler for INT generated instructions for the
+ * NCR53c810/820 SCSI SCRIPT
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ * cmd - pointer to the command (if any) dsa was pointing
+ * to.
+ *
+ */
+
+static int
+NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ int print;
+ Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ u32 dsps,*dsp; /* Argument of the INT instruction */
+
+ NCR53c7x0_local_setup(host);
+ dsps = NCR53c7x0_read32(DSPS_REG);
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
+
+ /* RGH 150597: Frig. Commands which fail with Check Condition are
+ * Flagged as successful - hack dsps to indicate check condition */
+#if 0
+ /* RGH 200597: Need to disable for BVME6000, as it gets Check Conditions
+ * and then dies. Seems to handle Check Condition at startup, but
+ * not mid kernel build. */
+ if (dsps == A_int_norm_emulateintfly && cmd && cmd->result == 2)
+ dsps = A_int_err_check_condition;
+#endif
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps);
+
+ switch (dsps) {
+ case A_int_msg_1:
+ print = 1;
+ switch (hostdata->msg_buf[0]) {
+ /*
+ * Unless we've initiated synchronous negotiation, I don't
+ * think that this should happen.
+ */
+ case MESSAGE_REJECT:
+ hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ if (cmd && (cmd->flags & CMD_FLAG_SDTR)) {
+ printk ("scsi%d : target %d rejected SDTR\n", host->host_no,
+ c->device->id);
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ asynchronous (host, c->device->id);
+ print = 0;
+ }
+ break;
+ case INITIATE_RECOVERY:
+ printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n",
+ host->host_no);
+ /* Fall through to default */
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ break;
+ default:
+ printk ("scsi%d : unsupported message, rejecting\n",
+ host->host_no);
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ }
+ if (print) {
+ printk ("scsi%d : received message", host->host_no);
+ if (c)
+ printk (" from target %d lun %d ", c->device->id, c->device->lun);
+ print_msg ((unsigned char *) hostdata->msg_buf);
+ printk("\n");
+ }
+
+ return SPECIFIC_INT_NOTHING;
+
+
+ case A_int_msg_sdtr:
+/*
+ * At this point, hostdata->msg_buf contains
+ * 0 EXTENDED MESSAGE
+ * 1 length
+ * 2 SDTR
+ * 3 period * 4ns
+ * 4 offset
+ */
+
+ if (cmd) {
+ char buf[80];
+ sprintf (buf, "scsi%d : target %d %s ", host->host_no, c->device->id,
+ (cmd->flags & CMD_FLAG_SDTR) ? "accepting" : "requesting");
+ print_synchronous (buf, (unsigned char *) hostdata->msg_buf);
+
+ /*
+ * Initiator initiated, won't happen unless synchronous
+ * transfers are enabled. If we get a SDTR message in
+ * response to our SDTR, we should program our parameters
+ * such that
+ * offset <= requested offset
+ * period >= requested period
+ */
+ if (cmd->flags & CMD_FLAG_SDTR) {
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ if (hostdata->msg_buf[4])
+ synchronous (host, c->device->id, (unsigned char *)
+ hostdata->msg_buf);
+ else
+ asynchronous (host, c->device->id);
+ hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ } else {
+ if (hostdata->options & OPTION_SYNCHRONOUS) {
+ cmd->flags |= CMD_FLAG_DID_SDTR;
+ synchronous (host, c->device->id, (unsigned char *)
+ hostdata->msg_buf);
+ } else {
+ hostdata->msg_buf[4] = 0; /* 0 offset = async */
+ asynchronous (host, c->device->id);
+ }
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5);
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32)
+ virt_to_bus ((void *)&hostdata->msg_buf));
+ hostdata->dsp = hostdata->script +
+ hostdata->E_respond_message / sizeof(u32);
+ hostdata->dsp_changed = 1;
+ }
+ return SPECIFIC_INT_NOTHING;
+ }
+ /* Fall through to abort if we couldn't find a cmd, and
+ therefore a dsa structure to twiddle */
+ case A_int_msg_wdtr:
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_err_unexpected_phase:
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : unexpected phase\n", host->host_no);
+ return SPECIFIC_INT_ABORT;
+ case A_int_err_selected:
+ if ((hostdata->chip / 100) == 8)
+ printk ("scsi%d : selected by target %d\n", host->host_no,
+ (int) NCR53c7x0_read8(SDID_REG_800) &7);
+ else
+ printk ("scsi%d : selected by target LCRC=0x%02x\n", host->host_no,
+ (int) NCR53c7x0_read8(LCRC_REG_10));
+ hostdata->dsp = hostdata->script + hostdata->E_target_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_err_unexpected_reselect:
+ if ((hostdata->chip / 100) == 8)
+ printk ("scsi%d : unexpected reselect by target %d lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & 7,
+ hostdata->reselected_identify & 7);
+ else
+ printk ("scsi%d : unexpected reselect LCRC=0x%02x\n", host->host_no,
+ (int) NCR53c7x0_read8(LCRC_REG_10));
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+/*
+ * Since contingent allegiance conditions are cleared by the next
+ * command issued to a target, we must issue a REQUEST SENSE
+ * command after receiving a CHECK CONDITION status, before
+ * another command is issued.
+ *
+ * Since this NCR53c7x0_cmd will be freed after use, we don't
+ * care if we step on the various fields, so modify a few things.
+ */
+ case A_int_err_check_condition:
+#if 0
+ if (hostdata->options & OPTION_DEBUG_INTR)
+#endif
+ printk ("scsi%d : CHECK CONDITION\n", host->host_no);
+ if (!c) {
+ printk("scsi%d : CHECK CONDITION with no SCSI command\n",
+ host->host_no);
+ return SPECIFIC_INT_PANIC;
+ }
+
+ /*
+ * FIXME : this uses the normal one-byte selection message.
+ * We may want to renegotiate for synchronous & WIDE transfers
+ * since these could be the crux of our problem.
+ *
+ hostdata->NOP_insn* FIXME : once SCSI-II tagged queuing is implemented, we'll
+ * have to set this up so that the rest of the DSA
+ * agrees with this being an untagged queue'd command.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1);
+
+ /*
+ * Modify the table indirect for COMMAND OUT phase, since
+ * Request Sense is a six byte command.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6);
+
+ /*
+ * The CDB is now mirrored in our local non-cached
+ * structure, but keep the old structure up to date as well,
+ * just in case anyone looks at it.
+ */
+
+ /*
+ * XXX Need to worry about data buffer alignment/cache state
+ * XXX here, but currently never get A_int_err_check_condition,
+ * XXX so ignore problem for now.
+ */
+ cmd->cmnd[0] = c->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[0] = c->cmnd[1] &= 0xe0; /* Zero all but LUN */
+ cmd->cmnd[0] = c->cmnd[2] = 0;
+ cmd->cmnd[0] = c->cmnd[3] = 0;
+ cmd->cmnd[0] = c->cmnd[4] = sizeof(c->sense_buffer);
+ cmd->cmnd[0] = c->cmnd[5] = 0;
+
+ /*
+ * Disable dataout phase, and program datain to transfer to the
+ * sense buffer, and add a jump to other_transfer after the
+ * command so overflow/underrun conditions are detected.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_dataout, 0,
+ virt_to_bus(hostdata->script) + hostdata->E_other_transfer);
+ patch_dsa_32 (cmd->dsa, dsa_datain, 0,
+ virt_to_bus(cmd->data_transfer_start));
+ cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I |
+ DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer);
+ cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer);
+
+ cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE;
+ cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+
+ /*
+ * Currently, this command is flagged as completed, ie
+ * it has valid status and message data. Reflag it as
+ * incomplete. Q - need to do something so that original
+ * status, etc are used.
+ */
+
+ cmd->result = cmd->cmd->result = 0xffff;
+
+ /*
+ * Restart command as a REQUEST SENSE.
+ */
+ hostdata->dsp = (u32 *) hostdata->script + hostdata->E_select /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_debug_break:
+ return SPECIFIC_INT_BREAK;
+ case A_int_norm_aborted:
+ hostdata->dsp = (u32 *) hostdata->schedule;
+ hostdata->dsp_changed = 1;
+ if (cmd)
+ abnormal_finished (cmd, DID_ERROR << 16);
+ return SPECIFIC_INT_NOTHING;
+ case A_int_norm_emulateintfly:
+ NCR53c7x0_intfly(host);
+ return SPECIFIC_INT_NOTHING;
+ case A_int_test_1:
+ case A_int_test_2:
+ hostdata->idle = 1;
+ hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1;
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk("scsi%d : test%d complete\n", host->host_no,
+ hostdata->test_completed);
+ return SPECIFIC_INT_NOTHING;
+#ifdef A_int_debug_reselected_ok
+ case A_int_debug_reselected_ok:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ u32 *dsa;
+ dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG));
+
+ printk("scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p)\n",
+ host->host_no, NCR53c7x0_read32(DSA_REG), dsa);
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt(cmd->saved_data_pointer));
+ print_insn (host, hostdata->script + Ent_reselected_ok /
+ sizeof(u32), "", 1);
+ if ((hostdata->chip / 100) == 8)
+ printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG),
+ NCR53c7x0_read8(SCNTL3_REG_800));
+ else
+ printk ("scsi%d : sxfer=0x%x, cannot read SBCL\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG));
+ if (c) {
+ print_insn (host, (u32 *)
+ hostdata->sync[c->device->id].script, "", 1);
+ print_insn (host, (u32 *)
+ hostdata->sync[c->device->id].script + 2, "", 1);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_reselect_check
+ case A_int_debug_reselect_check:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ u32 *dsa;
+#if 0
+ u32 *code;
+#endif
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG));
+ printk("scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p))\n",
+ host->host_no, virt_to_bus(dsa), dsa);
+ if (dsa) {
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer));
+#if 0
+ printk("scsi%d : template code :\n", host->host_no);
+ for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero)
+ / sizeof(u32); code < (dsa + Ent_dsa_zero / sizeof(u32));
+ code += print_insn (host, code, "", 1));
+#endif
+ }
+ print_insn (host, hostdata->script + Ent_reselected_ok /
+ sizeof(u32), "", 1);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_schedule
+ case A_int_debug_dsa_schedule:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ u32 *dsa;
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG));
+ printk("scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p))\n",
+ host->host_no, virt_to_bus(dsa), dsa);
+ if (dsa)
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n"
+ " (temp was 0x%x (virt 0x%p))\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer),
+ NCR53c7x0_read32 (TEMP_REG),
+ bus_to_virt (NCR53c7x0_read32(TEMP_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_scheduled
+ case A_int_debug_scheduled:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : new I/O 0x%x (virt 0x%p) scheduled\n",
+ host->host_no, NCR53c7x0_read32(DSA_REG),
+ bus_to_virt(NCR53c7x0_read32(DSA_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_idle
+ case A_int_debug_idle:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : idle\n", host->host_no);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_cmd
+ case A_int_debug_cmd:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : command sent\n");
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_loaded
+ case A_int_debug_dsa_loaded:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : DSA loaded with 0x%x (virt 0x%p)\n", host->host_no,
+ NCR53c7x0_read32(DSA_REG),
+ bus_to_virt(NCR53c7x0_read32(DSA_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_reselected
+ case A_int_debug_reselected:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ if ((hostdata->chip / 100) == 8)
+ printk("scsi%d : reselected by target %d lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & ~0x80,
+ (int) hostdata->reselected_identify & 7);
+ else
+ printk("scsi%d : reselected by LCRC=0x%02x lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(LCRC_REG_10),
+ (int) hostdata->reselected_identify & 7);
+ print_queues(host);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_disconnect_msg
+ case A_int_debug_disconnect_msg:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ if (c)
+ printk("scsi%d : target %d lun %d disconnecting\n",
+ host->host_no, c->device->id, c->device->lun);
+ else
+ printk("scsi%d : unknown target disconnecting\n",
+ host->host_no);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_disconnected
+ case A_int_debug_disconnected:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk ("scsi%d : disconnected, new queues are\n",
+ host->host_no);
+ print_queues(host);
+#if 0
+ /* Not valid on ncr53c710! */
+ printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG),
+ NCR53c7x0_read8(SCNTL3_REG_800));
+#endif
+ if (c) {
+ print_insn (host, (u32 *)
+ hostdata->sync[c->device->id].script, "", 1);
+ print_insn (host, (u32 *)
+ hostdata->sync[c->device->id].script + 2, "", 1);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_panic
+ case A_int_debug_panic:
+ printk("scsi%d : int_debug_panic received\n", host->host_no);
+ print_lots (host);
+ return SPECIFIC_INT_PANIC;
+#endif
+#ifdef A_int_debug_saved
+ case A_int_debug_saved:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer));
+ print_progress (c);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_restored
+ case A_int_debug_restored:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ if (cmd) {
+ int size;
+ printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer, bus_to_virt (
+ cmd->saved_data_pointer));
+ size = print_insn (host, (u32 *)
+ bus_to_virt(cmd->saved_data_pointer), "", 1);
+ size = print_insn (host, (u32 *)
+ bus_to_virt(cmd->saved_data_pointer) + size, "", 1);
+ print_progress (c);
+ }
+#if 0
+ printk ("scsi%d : datapath residual %d\n",
+ host->host_no, datapath_residual (host)) ;
+#endif
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_sync
+ case A_int_debug_sync:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) {
+ unsigned char sxfer = NCR53c7x0_read8 (SXFER_REG), scntl3;
+ if ((hostdata->chip / 100) == 8) {
+ scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800);
+ if (c) {
+ if (sxfer != hostdata->sync[c->device->id].sxfer_sanity ||
+ scntl3 != hostdata->sync[c->device->id].scntl3_sanity) {
+ printk ("scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x",
+ host->host_no, sxfer, scntl3);
+ NCR53c7x0_write8 (SXFER_REG, sxfer);
+ NCR53c7x0_write8 (SCNTL3_REG_800, scntl3);
+ }
+ } else
+ printk ("scsi%d : unknown command sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, (int) sxfer, (int) scntl3);
+ } else {
+ if (c) {
+ if (sxfer != hostdata->sync[c->device->id].sxfer_sanity) {
+ printk ("scsi%d : sync sanity check failed sxfer=0x%x",
+ host->host_no, sxfer);
+ NCR53c7x0_write8 (SXFER_REG, sxfer);
+ NCR53c7x0_write8 (SBCL_REG,
+ hostdata->sync[c->device->id].sscf_710);
+ }
+ } else
+ printk ("scsi%d : unknown command sxfer=0x%x\n",
+ host->host_no, (int) sxfer);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_datain
+ case A_int_debug_datain:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) {
+ int size;
+ if ((hostdata->chip / 100) == 8)
+ printk ("scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x\n"
+ " datapath residual=%d\n",
+ host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ (int) NCR53c7x0_read8(SCNTL3_REG_800),
+ datapath_residual (host)) ;
+ else
+ printk ("scsi%d : In do_datain (%s) sxfer=0x%x\n"
+ " datapath residual=%d\n",
+ host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ datapath_residual (host)) ;
+ print_insn (host, dsp, "", 1);
+ size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1);
+ print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_check_dsa
+ case A_int_debug_check_dsa:
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ int sdid;
+ int tmp;
+ char *where;
+ if (hostdata->chip / 100 == 8)
+ sdid = NCR53c7x0_read8 (SDID_REG_800) & 15;
+ else {
+ tmp = NCR53c7x0_read8 (SDID_REG_700);
+ if (!tmp)
+ panic ("SDID_REG_700 = 0");
+ tmp >>= 1;
+ sdid = 0;
+ while (tmp) {
+ tmp >>= 1;
+ sdid++;
+ }
+ }
+ where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8
+ (DCMD_REG)) == hostdata->script +
+ Ent_select_check_dsa / sizeof(u32) ?
+ "selection" : "reselection";
+ if (c && sdid != c->device->id) {
+ printk ("scsi%d : SDID target %d != DSA target %d at %s\n",
+ host->host_no, sdid, c->device->id, where);
+ print_lots(host);
+ dump_events (host, 20);
+ return SPECIFIC_INT_PANIC;
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+ default:
+ if ((dsps & 0xff000000) == 0x03000000) {
+ printk ("scsi%d : misc debug interrupt 0x%x\n",
+ host->host_no, dsps);
+ return SPECIFIC_INT_RESTART;
+ } else if ((dsps & 0xff000000) == 0x05000000) {
+ if (hostdata->events) {
+ struct NCR53c7x0_event *event;
+ ++hostdata->event_index;
+ if (hostdata->event_index >= hostdata->event_size)
+ hostdata->event_index = 0;
+ event = (struct NCR53c7x0_event *) hostdata->events +
+ hostdata->event_index;
+ event->event = (enum ncr_event) dsps;
+ event->dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ if (hostdata->chip / 100 == 8)
+ event->target = NCR53c7x0_read8(SSID_REG_800);
+ else {
+ unsigned char tmp, sdid;
+ tmp = NCR53c7x0_read8 (SDID_REG_700);
+ if (!tmp)
+ panic ("SDID_REG_700 = 0");
+ tmp >>= 1;
+ sdid = 0;
+ while (tmp) {
+ tmp >>= 1;
+ sdid++;
+ }
+ event->target = sdid;
+ }
+ }
+ else
+ event->target = 255;
+
+ if (event->event == EVENT_RESELECT)
+ event->lun = hostdata->reselected_identify & 0xf;
+ else if (c)
+ event->lun = c->device->lun;
+ else
+ event->lun = 255;
+ do_gettimeofday(&(event->time));
+ if (c) {
+ event->pid = c->pid;
+ memcpy ((void *) event->cmnd, (void *) c->cmnd,
+ sizeof (event->cmnd));
+ } else {
+ event->pid = -1;
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+ }
+
+ printk ("scsi%d : unknown user interrupt 0x%x\n",
+ host->host_no, (unsigned) dsps);
+ return SPECIFIC_INT_PANIC;
+ }
+}
+
+/*
+ * XXX - the stock NCR assembler won't output the scriptu.h file,
+ * which undefine's all #define'd CPP symbols from the script.h
+ * file, which will create problems if you use multiple scripts
+ * with the same symbol names.
+ *
+ * If you insist on using NCR's assembler, you could generate
+ * scriptu.h from script.h using something like
+ *
+ * grep #define script.h | \
+ * sed 's/#define[ ][ ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \
+ * > scriptu.h
+ */
+
+#include "53c7xx_u.h"
+
+/* XXX - add alternate script handling code here */
+
+
+/*
+ * Function : static void NCR537xx_soft_reset (struct Scsi_Host *host)
+ *
+ * Purpose : perform a soft reset of the NCR53c7xx chip
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : NCR53c7x0_init must have been called for this
+ * host.
+ *
+ */
+
+static void
+NCR53c7x0_soft_reset (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ NCR53c7x0_local_setup(host);
+
+ local_irq_save(flags);
+
+ /* Disable scsi chip and s/w level 7 ints */
+
+#ifdef CONFIG_MVME16x
+ if (MACH_IS_MVME16x)
+ {
+ volatile unsigned long v;
+
+ v = *(volatile unsigned long *)0xfff4006c;
+ v &= ~0x8000;
+ *(volatile unsigned long *)0xfff4006c = v;
+ v = *(volatile unsigned long *)0xfff4202c;
+ v &= ~0x10;
+ *(volatile unsigned long *)0xfff4202c = v;
+ }
+#endif
+ /* Anything specific for your hardware? */
+
+ /*
+ * Do a soft reset of the chip so that everything is
+ * reinitialized to the power-on state.
+ *
+ * Basically follow the procedure outlined in the NCR53c700
+ * data manual under Chapter Six, How to Use, Steps Necessary to
+ * Start SCRIPTS, with the exception of actually starting the
+ * script and setting up the synchronous transfer gunk.
+ */
+
+ /* Should we reset the scsi bus here??????????????????? */
+
+ NCR53c7x0_write8(ISTAT_REG_700, ISTAT_10_SRST);
+ NCR53c7x0_write8(ISTAT_REG_700, 0);
+
+ /*
+ * saved_dcntl is set up in NCR53c7x0_init() before it is overwritten
+ * here. We should have some better way of working out the CF bit
+ * setting..
+ */
+
+ hostdata->saved_dcntl = DCNTL_10_EA|DCNTL_10_COM;
+ if (hostdata->scsi_clock > 50000000)
+ hostdata->saved_dcntl |= DCNTL_700_CF_3;
+ else
+ if (hostdata->scsi_clock > 37500000)
+ hostdata->saved_dcntl |= DCNTL_700_CF_2;
+#if 0
+ else
+ /* Any clocks less than 37.5MHz? */
+#endif
+
+ if (hostdata->options & OPTION_DEBUG_TRACE)
+ NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM);
+ else
+ NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl);
+ /* Following disables snooping - snooping is not required, as non-
+ * cached pages are used for shared data, and appropriate use is
+ * made of cache_push/cache_clear. Indeed, for 68060
+ * enabling snooping causes disk corruption of ext2fs free block
+ * bitmaps and the like. If you have a 68060 with snooping hardwared
+ * on, then you need to enable CONFIG_060_WRITETHROUGH.
+ */
+ NCR53c7x0_write8(CTEST7_REG, CTEST7_10_TT1|CTEST7_STD);
+ /* Actually burst of eight, according to my 53c710 databook */
+ NCR53c7x0_write8(hostdata->dmode, DMODE_10_BL_8 | DMODE_10_FC2);
+ NCR53c7x0_write8(SCID_REG, 1 << host->this_id);
+ NCR53c7x0_write8(SBCL_REG, 0);
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_ESR_700);
+ NCR53c7x0_write8(SCNTL0_REG, ((hostdata->options & OPTION_PARITY) ?
+ SCNTL0_EPC : 0) | SCNTL0_EPG_700 | SCNTL0_ARB1 | SCNTL0_ARB2);
+
+ /*
+ * Enable all interrupts, except parity which we only want when
+ * the user requests it.
+ */
+
+ NCR53c7x0_write8(DIEN_REG, DIEN_700_BF |
+ DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC);
+
+ NCR53c7x0_write8(SIEN_REG_700, ((hostdata->options & OPTION_PARITY) ?
+ SIEN_PAR : 0) | SIEN_700_STO | SIEN_RST | SIEN_UDC |
+ SIEN_SGE | SIEN_MA);
+
+#ifdef CONFIG_MVME16x
+ if (MACH_IS_MVME16x)
+ {
+ volatile unsigned long v;
+
+ /* Enable scsi chip and s/w level 7 ints */
+ v = *(volatile unsigned long *)0xfff40080;
+ v = (v & ~(0xf << 28)) | (4 << 28);
+ *(volatile unsigned long *)0xfff40080 = v;
+ v = *(volatile unsigned long *)0xfff4006c;
+ v |= 0x8000;
+ *(volatile unsigned long *)0xfff4006c = v;
+ v = *(volatile unsigned long *)0xfff4202c;
+ v = (v & ~0xff) | 0x10 | 4;
+ *(volatile unsigned long *)0xfff4202c = v;
+ }
+#endif
+ /* Anything needed for your hardware? */
+ local_irq_restore(flags);
+}
+
+
+/*
+ * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Return the first free NCR53c7x0_cmd structure (which are
+ * reused in a LIFO manner to minimize cache thrashing).
+ *
+ * Side effects : If we haven't yet scheduled allocation of NCR53c7x0_cmd
+ * structures for this device, do so. Attempt to complete all scheduled
+ * allocations using get_zeroed_page(), putting NCR53c7x0_cmd structures on
+ * the free list. Teach programmers not to drink and hack.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure allocated on behalf of cmd;
+ * NULL on failure.
+ */
+
+static void
+my_free_page (void *addr, int dummy)
+{
+ /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, which
+ * XXX may be invalid (CONFIG_060_WRITETHROUGH)
+ */
+ kernel_set_cachemode((void *)addr, 4096, IOMAP_FULL_CACHING);
+ free_page ((u32)addr);
+}
+
+static struct NCR53c7x0_cmd *
+allocate_cmd (Scsi_Cmnd *cmd) {
+ struct Scsi_Host *host = cmd->device->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ u32 real; /* Real address */
+ int size; /* Size of *tmp */
+ struct NCR53c7x0_cmd *tmp;
+ unsigned long flags;
+
+ if (hostdata->options & OPTION_DEBUG_ALLOCATION)
+ printk ("scsi%d : num_cmds = %d, can_queue = %d\n"
+ " target = %d, lun = %d, %s\n",
+ host->host_no, hostdata->num_cmds, host->can_queue,
+ cmd->device->id, cmd->device->lun, (hostdata->cmd_allocated[cmd->device->id] &
+ (1 << cmd->device->lun)) ? "already allocated" : "not allocated");
+
+/*
+ * If we have not yet reserved commands for this I_T_L nexus, and
+ * the device exists (as indicated by permanent Scsi_Cmnd structures
+ * being allocated under 1.3.x, or being outside of scan_scsis in
+ * 1.2.x), do so now.
+ */
+ if (!(hostdata->cmd_allocated[cmd->device->id] & (1 << cmd->device->lun)) &&
+ cmd->device && cmd->device->has_cmdblocks) {
+ if ((hostdata->extra_allocate + hostdata->num_cmds) < host->can_queue)
+ hostdata->extra_allocate += host->cmd_per_lun;
+ hostdata->cmd_allocated[cmd->device->id] |= (1 << cmd->device->lun);
+ }
+
+ for (; hostdata->extra_allocate > 0 ; --hostdata->extra_allocate,
+ ++hostdata->num_cmds) {
+ /* historically, kmalloc has returned unaligned addresses; pad so we
+ have enough room to ROUNDUP */
+ size = hostdata->max_cmd_size + sizeof (void *);
+#ifdef FORCE_DSA_ALIGNMENT
+ /*
+ * 53c710 rev.0 doesn't have an add-with-carry instruction.
+ * Ensure we allocate enough memory to force alignment.
+ */
+ size += 256;
+#endif
+/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */
+
+ if (size > 4096) {
+ printk (KERN_ERR "53c7xx: allocate_cmd size > 4K\n");
+ return NULL;
+ }
+ real = get_zeroed_page(GFP_ATOMIC);
+ if (real == 0)
+ return NULL;
+ memset((void *)real, 0, 4096);
+ cache_push(virt_to_phys((void *)real), 4096);
+ cache_clear(virt_to_phys((void *)real), 4096);
+ kernel_set_cachemode((void *)real, 4096, IOMAP_NOCACHE_SER);
+ tmp = ROUNDUP(real, void *);
+#ifdef FORCE_DSA_ALIGNMENT
+ {
+ if (((u32)tmp & 0xff) > CmdPageStart)
+ tmp = (struct NCR53c7x0_cmd *)((u32)tmp + 255);
+ tmp = (struct NCR53c7x0_cmd *)(((u32)tmp & ~0xff) + CmdPageStart);
+#if 0
+ printk ("scsi: size = %d, real = 0x%08x, tmp set to 0x%08x\n",
+ size, real, (u32)tmp);
+#endif
+ }
+#endif
+ tmp->real = (void *)real;
+ tmp->size = size;
+ tmp->free = ((void (*)(void *, int)) my_free_page);
+ local_irq_save(flags);
+ tmp->next = hostdata->free;
+ hostdata->free = tmp;
+ local_irq_restore(flags);
+ }
+ local_irq_save(flags);
+ tmp = (struct NCR53c7x0_cmd *) hostdata->free;
+ if (tmp) {
+ hostdata->free = tmp->next;
+ }
+ local_irq_restore(flags);
+ if (!tmp)
+ printk ("scsi%d : can't allocate command for target %d lun %d\n",
+ host->host_no, cmd->device->id, cmd->device->lun);
+ return tmp;
+}
+
+/*
+ * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd)
+ *
+ *
+ * Purpose : allocate a NCR53c7x0_cmd structure, initialize it based on the
+ * Scsi_Cmnd structure passed in cmd, including dsa and Linux field
+ * initialization, and dsa code relocation.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure corresponding to cmd,
+ * NULL on failure.
+ */
+static struct NCR53c7x0_cmd *
+create_cmd (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host = cmd->device->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ struct NCR53c7x0_cmd *tmp; /* NCR53c7x0_cmd structure for this command */
+ int datain, /* Number of instructions per phase */
+ dataout;
+ int data_transfer_instructions, /* Count of dynamic instructions */
+ i; /* Counter */
+ u32 *cmd_datain, /* Address of datain/dataout code */
+ *cmd_dataout; /* Incremented as we assemble */
+#ifdef notyet
+ unsigned char *msgptr; /* Current byte in select message */
+ int msglen; /* Length of whole select message */
+#endif
+ unsigned long flags;
+ u32 exp_select_indirect; /* Used in sanity check */
+ NCR53c7x0_local_setup(cmd->device->host);
+
+ if (!(tmp = allocate_cmd (cmd)))
+ return NULL;
+
+ /*
+ * Copy CDB and initialised result fields from Scsi_Cmnd to NCR53c7x0_cmd.
+ * We do this because NCR53c7x0_cmd may have a special cache mode
+ * selected to cope with lack of bus snooping, etc.
+ */
+
+ memcpy(tmp->cmnd, cmd->cmnd, 12);
+ tmp->result = cmd->result;
+
+ /*
+ * Decide whether we need to generate commands for DATA IN,
+ * DATA OUT, neither, or both based on the SCSI command
+ */
+
+ switch (cmd->cmnd[0]) {
+ /* These commands do DATA IN */
+ case INQUIRY:
+ case MODE_SENSE:
+ case READ_6:
+ case READ_10:
+ case READ_CAPACITY:
+ case REQUEST_SENSE:
+ case READ_BLOCK_LIMITS:
+ case READ_TOC:
+ datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+ dataout = 0;
+ break;
+ /* These commands do DATA OUT */
+ case MODE_SELECT:
+ case WRITE_6:
+ case WRITE_10:
+#if 0
+ printk("scsi%d : command is ", host->host_no);
+ print_command(cmd->cmnd);
+#endif
+#if 0
+ printk ("scsi%d : %d scatter/gather segments\n", host->host_no,
+ cmd->use_sg);
+#endif
+ datain = 0;
+ dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+#if 0
+ hostdata->options |= OPTION_DEBUG_INTR;
+#endif
+ break;
+ /*
+ * These commands do no data transfer, we should force an
+ * interrupt if a data phase is attempted on them.
+ */
+ case TEST_UNIT_READY:
+ case ALLOW_MEDIUM_REMOVAL:
+ case START_STOP:
+ datain = dataout = 0;
+ break;
+ /*
+ * We don't know about these commands, so generate code to handle
+ * both DATA IN and DATA OUT phases. More efficient to identify them
+ * and add them to the above cases.
+ */
+ default:
+ printk("scsi%d : datain+dataout for command ", host->host_no);
+ print_command(cmd->cmnd);
+ datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+ }
+
+ /*
+ * New code : so that active pointers work correctly regardless
+ * of where the saved data pointer is at, we want to immediately
+ * enter the dynamic code after selection, and on a non-data
+ * phase perform a CALL to the non-data phase handler, with
+ * returns back to this address.
+ *
+ * If a phase mismatch is encountered in the middle of a
+ * Block MOVE instruction, we want to _leave_ that instruction
+ * unchanged as the current case is, modify a temporary buffer,
+ * and point the active pointer (TEMP) at that.
+ *
+ * Furthermore, we want to implement a saved data pointer,
+ * set by the SAVE_DATA_POINTERs message.
+ *
+ * So, the data transfer segments will change to
+ * CALL data_transfer, WHEN NOT data phase
+ * MOVE x, x, WHEN data phase
+ * ( repeat )
+ * JUMP other_transfer
+ */
+
+ data_transfer_instructions = datain + dataout;
+
+ /*
+ * When we perform a request sense, we overwrite various things,
+ * including the data transfer code. Make sure we have enough
+ * space to do that.
+ */
+
+ if (data_transfer_instructions < 2)
+ data_transfer_instructions = 2;
+
+
+ /*
+ * The saved data pointer is set up so that a RESTORE POINTERS message
+ * will start the data transfer over at the beginning.
+ */
+
+ tmp->saved_data_pointer = virt_to_bus (hostdata->script) +
+ hostdata->E_data_transfer;
+
+ /*
+ * Initialize Linux specific fields.
+ */
+
+ tmp->cmd = cmd;
+ tmp->next = NULL;
+ tmp->flags = 0;
+ tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next -
+ hostdata->dsa_start;
+ tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start;
+
+ /*
+ * Calculate addresses of dynamic code to fill in DSA
+ */
+
+ tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end -
+ hostdata->dsa_start) / sizeof(u32);
+ tmp->data_transfer_end = tmp->data_transfer_start +
+ 2 * data_transfer_instructions;
+
+ cmd_datain = datain ? tmp->data_transfer_start : NULL;
+ cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp->
+ data_transfer_start) : NULL;
+
+ /*
+ * Fill in the NCR53c7x0_cmd structure as follows
+ * dsa, with fixed up DSA code
+ * datain code
+ * dataout code
+ */
+
+ /* Copy template code into dsa and perform all necessary fixups */
+ if (hostdata->dsa_fixup)
+ hostdata->dsa_fixup(tmp);
+
+ patch_dsa_32(tmp->dsa, dsa_next, 0, 0);
+ /*
+ * XXX is this giving 53c710 access to the Scsi_Cmnd in some way?
+ * Do we need to change it for caching reasons?
+ */
+ patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd));
+
+ if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) {
+
+ exp_select_indirect = ((1 << cmd->device->id) << 16) |
+ (hostdata->sync[cmd->device->id].sxfer_sanity << 8);
+
+ if (hostdata->sync[cmd->device->id].select_indirect !=
+ exp_select_indirect) {
+ printk ("scsi%d : sanity check failed select_indirect=0x%x\n",
+ host->host_no, hostdata->sync[cmd->device->id].select_indirect);
+ FATAL(host);
+
+ }
+ }
+
+ patch_dsa_32(tmp->dsa, dsa_select, 0,
+ hostdata->sync[cmd->device->id].select_indirect);
+
+ /*
+ * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on
+ * different commands; although it should be trivial to do them
+ * both at the same time.
+ */
+ if (hostdata->initiate_wdtr & (1 << cmd->device->id)) {
+ memcpy ((void *) (tmp->select + 1), (void *) wdtr_message,
+ sizeof(wdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message));
+ local_irq_save(flags);
+ hostdata->initiate_wdtr &= ~(1 << cmd->device->id);
+ local_irq_restore(flags);
+ } else if (hostdata->initiate_sdtr & (1 << cmd->device->id)) {
+ memcpy ((void *) (tmp->select + 1), (void *) sdtr_message,
+ sizeof(sdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message));
+ tmp->flags |= CMD_FLAG_SDTR;
+ local_irq_save(flags);
+ hostdata->initiate_sdtr &= ~(1 << cmd->device->id);
+ local_irq_restore(flags);
+
+ }
+#if 1
+ else if (!(hostdata->talked_to & (1 << cmd->device->id)) &&
+ !(hostdata->options & OPTION_NO_ASYNC)) {
+
+ memcpy ((void *) (tmp->select + 1), (void *) async_message,
+ sizeof(async_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message));
+ tmp->flags |= CMD_FLAG_SDTR;
+ }
+#endif
+ else
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
+
+ hostdata->talked_to |= (1 << cmd->device->id);
+ tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ?
+ IDENTIFY (1, cmd->device->lun) : IDENTIFY (0, cmd->device->lun);
+ patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select));
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len);
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(tmp->cmnd));
+ patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ?
+ virt_to_bus (cmd_dataout)
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ?
+ virt_to_bus (cmd_datain)
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ /*
+ * XXX - need to make endian aware, should use separate variables
+ * for both status and message bytes.
+ */
+ patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1);
+/*
+ * FIXME : these only works for little endian. We probably want to
+ * provide message and status fields in the NCR53c7x0_cmd
+ * structure, and assign them to cmd->result when we're done.
+ */
+#ifdef BIG_ENDIAN
+ patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&tmp->result) + 2);
+ patch_dsa_32(tmp->dsa, dsa_status, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&tmp->result) + 3);
+#else
+ patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&tmp->result) + 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&tmp->result));
+#endif
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 1,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_nop)));
+
+ /*
+ * Generate code for zero or more of the DATA IN, DATA OUT phases
+ * in the format
+ *
+ * CALL data_transfer, WHEN NOT phase
+ * MOVE first buffer length, first buffer address, WHEN phase
+ * ...
+ * MOVE last buffer length, last buffer address, WHEN phase
+ * JUMP other_transfer
+ */
+
+/*
+ * See if we're getting to data transfer by generating an unconditional
+ * interrupt.
+ */
+#if 0
+ if (datain) {
+ cmd_datain[0] = 0x98080000;
+ cmd_datain[1] = 0x03ffd00d;
+ cmd_datain += 2;
+ }
+#endif
+
+/*
+ * XXX - I'm undecided whether all of this nonsense is faster
+ * in the long run, or whether I should just go and implement a loop
+ * on the NCR chip using table indirect mode?
+ *
+ * In any case, this is how it _must_ be done for 53c700/700-66 chips,
+ * so this stays even when we come up with something better.
+ *
+ * When we're limited to 1 simultaneous command, no overlapping processing,
+ * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M
+ * drive.
+ *
+ * Not bad, not good. We'll see.
+ */
+
+ tmp->bounce.len = 0; /* Assume aligned buffer */
+
+ for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4,
+ cmd_dataout += 4, ++i) {
+ u32 vbuf = cmd->use_sg
+ ? (u32)page_address(((struct scatterlist *)cmd->buffer)[i].page)+
+ ((struct scatterlist *)cmd->buffer)[i].offset
+ : (u32)(cmd->request_buffer);
+ u32 bbuf = virt_to_bus((void *)vbuf);
+ u32 count = cmd->use_sg ?
+ ((struct scatterlist *)cmd->buffer)[i].length :
+ cmd->request_bufflen;
+
+ /*
+ * If we have buffers which are not aligned with 16 byte cache
+ * lines, then we just hope nothing accesses the other parts of
+ * those cache lines while the transfer is in progress. That would
+ * fill the cache, and subsequent reads of the dma data would pick
+ * up the wrong thing.
+ * XXX We need a bounce buffer to handle that correctly.
+ */
+
+ if (((bbuf & 15) || (count & 15)) && (datain || dataout))
+ {
+ /* Bounce buffer needed */
+ if (cmd->use_sg)
+ printk ("53c7xx: Non-aligned buffer with use_sg\n");
+ else if (datain && dataout)
+ printk ("53c7xx: Non-aligned buffer with datain && dataout\n");
+ else if (count > 256)
+ printk ("53c7xx: Non-aligned transfer > 256 bytes\n");
+ else
+ {
+ if (datain)
+ {
+ tmp->bounce.len = count;
+ tmp->bounce.addr = vbuf;
+ bbuf = virt_to_bus(tmp->bounce.buf);
+ tmp->bounce.buf[0] = 0xff;
+ tmp->bounce.buf[1] = 0xfe;
+ tmp->bounce.buf[2] = 0xfd;
+ tmp->bounce.buf[3] = 0xfc;
+ }
+ if (dataout)
+ {
+ memcpy ((void *)tmp->bounce.buf, (void *)vbuf, count);
+ bbuf = virt_to_bus(tmp->bounce.buf);
+ }
+ }
+ }
+
+ if (datain) {
+ cache_clear(virt_to_phys((void *)vbuf), count);
+ /* CALL other_in, WHEN NOT DATA_IN */
+ cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ DCMD_TCI_IO) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd_datain[1] = virt_to_bus (hostdata->script) +
+ hostdata->E_other_in;
+ /* MOVE count, buf, WHEN DATA_IN */
+ cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO)
+ << 24) | count;
+ cmd_datain[3] = bbuf;
+#if 0
+ print_insn (host, cmd_datain, "dynamic ", 1);
+ print_insn (host, cmd_datain + 2, "dynamic ", 1);
+#endif
+ }
+ if (dataout) {
+ cache_push(virt_to_phys((void *)vbuf), count);
+ /* CALL other_out, WHEN NOT DATA_OUT */
+ cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_out;
+ /* MOVE count, buf, WHEN DATA+OUT */
+ cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24)
+ | count;
+ cmd_dataout[3] = bbuf;
+#if 0
+ print_insn (host, cmd_dataout, "dynamic ", 1);
+ print_insn (host, cmd_dataout + 2, "dynamic ", 1);
+#endif
+ }
+ }
+
+ /*
+ * Install JUMP instructions after the data transfer routines to return
+ * control to the do_other_transfer routines.
+ */
+
+
+ if (datain) {
+ cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ cmd_datain[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+#if 0
+ print_insn (host, cmd_datain, "dynamic jump ", 1);
+#endif
+ cmd_datain += 2;
+ }
+#if 0
+ if (datain) {
+ cmd_datain[0] = 0x98080000;
+ cmd_datain[1] = 0x03ffdeed;
+ cmd_datain += 2;
+ }
+#endif
+ if (dataout) {
+ cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+#if 0
+ print_insn (host, cmd_dataout, "dynamic jump ", 1);
+#endif
+ cmd_dataout += 2;
+ }
+
+ return tmp;
+}
+
+/*
+ * Function : int NCR53c7xx_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance driver issue_queue, with major
+ * twiddling done to the host specific fields of cmd. If the
+ * process_issue_queue coroutine isn't running, it is restarted.
+ *
+ * NOTE : we use the host_scribble field of the Scsi_Cmnd structure to
+ * hold our own data, and pervert the ptr field of the SCp field
+ * to create a linked list.
+ */
+
+int
+NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
+ struct Scsi_Host *host = cmd->device->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ unsigned long flags;
+ Scsi_Cmnd *tmp;
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+ cmd->SCp.ptr = NULL;
+ cmd->SCp.buffer = NULL;
+
+#ifdef VALID_IDS
+ /* Ignore commands on invalid IDs */
+ if (!hostdata->valid_ids[cmd->device->id]) {
+ printk("scsi%d : ignoring target %d lun %d\n", host->host_no,
+ cmd->device->id, cmd->device->lun);
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return 0;
+ }
+#endif
+
+ local_irq_save(flags);
+ if ((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY))
+ || ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+ !(hostdata->debug_lun_limit[cmd->device->id] & (1 << cmd->device->lun)))
+#ifdef LINUX_1_2
+ || cmd->device->id > 7
+#else
+ || cmd->device->id > host->max_id
+#endif
+ || cmd->device->id == host->this_id
+ || hostdata->state == STATE_DISABLED) {
+ printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no,
+ cmd->device->id, cmd->device->lun);
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) &&
+ (hostdata->debug_count_limit == 0)) {
+ printk("scsi%d : maximum commands exceeded\n", host->host_no);
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ if (hostdata->options & OPTION_DEBUG_READ_ONLY) {
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
+ host->host_no);
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ local_irq_restore(flags);
+ return 0;
+ }
+ }
+
+ if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+ hostdata->debug_count_limit != -1)
+ --hostdata->debug_count_limit;
+
+ cmd->result = 0xffff; /* The NCR will overwrite message
+ and status with valid data */
+ cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd);
+
+ /*
+ * REQUEST SENSE commands are inserted at the head of the queue
+ * so that we do not clear the contingent allegiance condition
+ * they may be looking at.
+ */
+
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->SCp.ptr;
+ tmp = (Scsi_Cmnd *) tmp->SCp.ptr);
+ tmp->SCp.ptr = (unsigned char *) cmd;
+ }
+ local_irq_restore(flags);
+ run_process_issue_queue();
+ return 0;
+}
+
+/*
+ * Function : void to_schedule_list (struct Scsi_Host *host,
+ * struct NCR53c7x0_hostdata * hostdata, Scsi_Cmnd *cmd)
+ *
+ * Purpose : takes a SCSI command which was just removed from the
+ * issue queue, and deals with it by inserting it in the first
+ * free slot in the schedule list or by terminating it immediately.
+ *
+ * Inputs :
+ * host - SCSI host adapter; hostdata - hostdata structure for
+ * this adapter; cmd - a pointer to the command; should have
+ * the host_scribble field initialized to point to a valid
+ *
+ * Side effects :
+ * cmd is added to the per instance schedule list, with minor
+ * twiddling done to the host specific fields of cmd.
+ *
+ */
+
+static __inline__ void
+to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ Scsi_Cmnd *tmp = cmd->cmd;
+ unsigned long flags;
+ /* dsa start is negative, so subtraction is used */
+ volatile u32 *ncrcurrent;
+
+ int i;
+ NCR53c7x0_local_setup(host);
+#if 0
+ printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no,
+ virt_to_bus(hostdata->dsa), hostdata->dsa);
+#endif
+
+ local_irq_save(flags);
+
+ /*
+ * Work around race condition : if an interrupt fired and we
+ * got disabled forget about this command.
+ */
+
+ if (hostdata->state == STATE_DISABLED) {
+ printk("scsi%d : driver disabled\n", host->host_no);
+ tmp->result = (DID_BAD_TARGET << 16);
+ cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ hostdata->free = cmd;
+ tmp->scsi_done(tmp);
+ local_irq_restore(flags);
+ return;
+ }
+
+ for (i = host->can_queue, ncrcurrent = hostdata->schedule;
+ i > 0 && ncrcurrent[0] != hostdata->NOP_insn;
+ --i, ncrcurrent += 2 /* JUMP instructions are two words */);
+
+ if (i > 0) {
+ ++hostdata->busy[tmp->device->id][tmp->device->lun];
+ cmd->next = hostdata->running_list;
+ hostdata->running_list = cmd;
+
+ /* Restore this instruction to a NOP once the command starts */
+ cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) /
+ sizeof(u32)] = (u32) virt_to_bus ((void *)ncrcurrent);
+ /* Replace the current jump operand. */
+ ncrcurrent[1] =
+ virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template;
+ /* Replace the NOP instruction with a JUMP */
+ ncrcurrent[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ } else {
+ printk ("scsi%d: no free slot\n", host->host_no);
+ disable(host);
+ tmp->result = (DID_ERROR << 16);
+ cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ hostdata->free = cmd;
+ tmp->scsi_done(tmp);
+ local_irq_restore(flags);
+ return;
+ }
+
+ /*
+ * If the NCR chip is in an idle state, start it running the scheduler
+ * immediately. Otherwise, signal the chip to jump to schedule as
+ * soon as it is idle.
+ */
+
+ if (hostdata->idle) {
+ hostdata->idle = 0;
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus ((void *)hostdata->schedule));
+ if (hostdata->options & OPTION_DEBUG_TRACE)
+ NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl |
+ DCNTL_SSM | DCNTL_STD);
+ } else {
+ NCR53c7x0_write8(hostdata->istat, ISTAT_10_SIGP);
+ }
+
+ local_irq_restore(flags);
+}
+
+/*
+ * Function : busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata
+ * *hostdata, Scsi_Cmnd *cmd)
+ *
+ * Purpose : decide if we can pass the given SCSI command on to the
+ * device in question or not.
+ *
+ * Returns : non-zero when we're busy, 0 when we aren't.
+ */
+
+static __inline__ int
+busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ Scsi_Cmnd *cmd) {
+ /* FIXME : in the future, this needs to accommodate SCSI-II tagged
+ queuing, and we may be able to play with fairness here a bit.
+ */
+ return hostdata->busy[cmd->device->id][cmd->device->lun];
+}
+
+/*
+ * Function : process_issue_queue (void)
+ *
+ * Purpose : transfer commands from the issue queue to NCR start queue
+ * of each NCR53c7/8xx in the system, avoiding kernel stack
+ * overflows when the scsi_done() function is invoked recursively.
+ *
+ * NOTE : process_issue_queue exits with interrupts *disabled*, so the
+ * caller must reenable them if it desires.
+ *
+ * NOTE : process_issue_queue should be called from both
+ * NCR53c7x0_queue_command() and from the interrupt handler
+ * after command completion in case NCR53c7x0_queue_command()
+ * isn't invoked again but we've freed up resources that are
+ * needed.
+ */
+
+static void
+process_issue_queue (unsigned long flags) {
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *host;
+ struct NCR53c7x0_hostdata *hostdata;
+ int done;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set process_issue_queue_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ */
+
+ do {
+ local_irq_disable(); /* Freeze request queues */
+ done = 1;
+ for (host = first_host; host && host->hostt == the_template;
+ host = host->next) {
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ local_irq_disable();
+ if (hostdata->issue_queue) {
+ if (hostdata->state == STATE_DISABLED) {
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr;
+ tmp->result = (DID_BAD_TARGET << 16);
+ if (tmp->host_scribble) {
+ ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next =
+ hostdata->free;
+ hostdata->free =
+ (struct NCR53c7x0_cmd *)tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ }
+ tmp->scsi_done (tmp);
+ done = 0;
+ } else
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
+ tmp->SCp.ptr)
+ if (!tmp->host_scribble ||
+ !busyp (host, hostdata, tmp)) {
+ if (prev)
+ prev->SCp.ptr = tmp->SCp.ptr;
+ else
+ hostdata->issue_queue = (Scsi_Cmnd *)
+ tmp->SCp.ptr;
+ tmp->SCp.ptr = NULL;
+ if (tmp->host_scribble) {
+ if (hostdata->options & OPTION_DEBUG_QUEUES)
+ printk ("scsi%d : moving command for target %d lun %d to start list\n",
+ host->host_no, tmp->device->id, tmp->device->lun);
+
+
+ to_schedule_list (host, hostdata,
+ (struct NCR53c7x0_cmd *)
+ tmp->host_scribble);
+ } else {
+ if (((tmp->result & 0xff) == 0xff) ||
+ ((tmp->result & 0xff00) == 0xff00)) {
+ printk ("scsi%d : danger Will Robinson!\n",
+ host->host_no);
+ tmp->result = DID_ERROR << 16;
+ disable (host);
+ }
+ tmp->scsi_done(tmp);
+ }
+ done = 0;
+ } /* if target/lun is not busy */
+ } /* if hostdata->issue_queue */
+ if (!done)
+ local_irq_restore(flags);
+ } /* for host */
+ } while (!done);
+ process_issue_queue_running = 0;
+}
+
+/*
+ * Function : static void intr_scsi (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all SCSI interrupts, indicated by the setting
+ * of the SIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ unsigned char sstat0_sist0, sist1, /* Registers */
+ fatal; /* Did a fatal interrupt
+ occur ? */
+
+ NCR53c7x0_local_setup(host);
+
+ fatal = 0;
+
+ sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG);
+ sist1 = 0;
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no,
+ sstat0_sist0, sist1);
+
+ /* 250ms selection timeout */
+ if (sstat0_sist0 & SSTAT0_700_STO) {
+ fatal = 1;
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ printk ("scsi%d : Selection Timeout\n", host->host_no);
+ if (cmd) {
+ printk("scsi%d : target %d, lun %d, command ",
+ host->host_no, cmd->cmd->device->id, cmd->cmd->device->lun);
+ print_command (cmd->cmd->cmnd);
+ printk("scsi%d : dsp = 0x%x (virt 0x%p)\n", host->host_no,
+ NCR53c7x0_read32(DSP_REG),
+ bus_to_virt(NCR53c7x0_read32(DSP_REG)));
+ } else {
+ printk("scsi%d : no command\n", host->host_no);
+ }
+ }
+/*
+ * XXX - question : how do we want to handle the Illegal Instruction
+ * interrupt, which may occur before or after the Selection Timeout
+ * interrupt?
+ */
+
+ if (1) {
+ hostdata->idle = 1;
+ hostdata->expecting_sto = 0;
+
+ if (hostdata->test_running) {
+ hostdata->test_running = 0;
+ hostdata->test_completed = 3;
+ } else if (cmd) {
+ abnormal_finished(cmd, DID_BAD_TARGET << 16);
+ }
+#if 0
+ hostdata->intrs = 0;
+#endif
+ }
+ }
+
+/*
+ * FIXME : in theory, we can also get a UDC when a STO occurs.
+ */
+ if (sstat0_sist0 & SSTAT0_UDC) {
+ fatal = 1;
+ if (cmd) {
+ printk("scsi%d : target %d lun %d unexpected disconnect\n",
+ host->host_no, cmd->cmd->device->id, cmd->cmd->device->lun);
+ print_lots (host);
+ abnormal_finished(cmd, DID_ERROR << 16);
+ } else
+ printk("scsi%d : unexpected disconnect (no command)\n",
+ host->host_no);
+
+ hostdata->dsp = (u32 *) hostdata->schedule;
+ hostdata->dsp_changed = 1;
+ }
+
+ /* SCSI PARITY error */
+ if (sstat0_sist0 & SSTAT0_PAR) {
+ fatal = 1;
+ if (cmd && cmd->cmd) {
+ printk("scsi%d : target %d lun %d parity error.\n",
+ host->host_no, cmd->cmd->device->id, cmd->cmd->device->lun);
+ abnormal_finished (cmd, DID_PARITY << 16);
+ } else
+ printk("scsi%d : parity error\n", host->host_no);
+ /* Should send message out, parity error */
+
+ /* XXX - Reduce synchronous transfer rate! */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ /* SCSI GROSS error */
+ }
+
+ if (sstat0_sist0 & SSTAT0_SGE) {
+ fatal = 1;
+ printk("scsi%d : gross error, saved2_dsa = 0x%x\n", host->host_no,
+ (unsigned int)hostdata->saved2_dsa);
+ print_lots (host);
+
+ /*
+ * A SCSI gross error may occur when we have
+ *
+ * - A synchronous offset which causes the SCSI FIFO to be overwritten.
+ *
+ * - A REQ which causes the maximum synchronous offset programmed in
+ * the SXFER register to be exceeded.
+ *
+ * - A phase change with an outstanding synchronous offset.
+ *
+ * - Residual data in the synchronous data FIFO, with a transfer
+ * other than a synchronous receive is started.$#
+ */
+
+
+ /* XXX Should deduce synchronous transfer rate! */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ /* Phase mismatch */
+ }
+
+ if (sstat0_sist0 & SSTAT0_MA) {
+ fatal = 1;
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : SSTAT0_MA\n", host->host_no);
+ intr_phase_mismatch (host, cmd);
+ }
+
+#if 0
+ if (sstat0_sist0 & SIST0_800_RSL)
+ printk ("scsi%d : Oh no Mr. Bill!\n", host->host_no);
+#endif
+
+/*
+ * If a fatal SCSI interrupt occurs, we must insure that the DMA and
+ * SCSI FIFOs were flushed.
+ */
+
+ if (fatal) {
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+ /*
+ * Really need to check this code for 710 RGH.
+ * Havn't seen any problems, but maybe we should FLUSH before
+ * clearing sometimes.
+ */
+ NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF);
+ while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF)
+ ;
+ hostdata->dstat |= DSTAT_DFE;
+ }
+ }
+}
+
+#ifdef CYCLIC_TRACE
+
+/*
+ * The following implements a cyclic log of instructions executed, if you turn
+ * TRACE on. It will also print the log for you. Very useful when debugging
+ * 53c710 support, possibly not really needed any more.
+ */
+
+u32 insn_log[4096];
+u32 insn_log_index = 0;
+
+void log1 (u32 i)
+{
+ insn_log[insn_log_index++] = i;
+ if (insn_log_index == 4096)
+ insn_log_index = 0;
+}
+
+void log_insn (u32 *ip)
+{
+ log1 ((u32)ip);
+ log1 (*ip);
+ log1 (*(ip+1));
+ if (((*ip >> 24) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI)
+ log1 (*(ip+2));
+}
+
+void dump_log(void)
+{
+ int cnt = 0;
+ int i = insn_log_index;
+ int size;
+ struct Scsi_Host *host = first_host;
+
+ while (cnt < 4096) {
+ printk ("%08x (+%6x): ", insn_log[i], (insn_log[i] - (u32)&(((struct NCR53c7x0_hostdata *)host->hostdata[0])->script))/4);
+ if (++i == 4096)
+ i = 0;
+ cnt++;
+ if (((insn_log[i] >> 24) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI)
+ size = 3;
+ else
+ size = 2;
+ while (size--) {
+ printk ("%08x ", insn_log[i]);
+ if (++i == 4096)
+ i = 0;
+ cnt++;
+ }
+ printk ("\n");
+ }
+}
+#endif
+
+
+/*
+ * Function : static void NCR53c7x0_intfly (struct Scsi_Host *host)
+ *
+ * Purpose : Scan command queue for specified host, looking for completed
+ * commands.
+ *
+ * Inputs : Scsi_Host pointer.
+ *
+ * This is called from the interrupt handler, when a simulated INTFLY
+ * interrupt occurs.
+ */
+
+static void
+NCR53c7x0_intfly (struct Scsi_Host *host)
+{
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata; /* host->hostdata[0] */
+ struct NCR53c7x0_cmd *cmd, /* command which halted */
+ **cmd_prev_ptr;
+ unsigned long flags;
+ char search_found = 0; /* Got at least one ? */
+
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ NCR53c7x0_local_setup(host);
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : INTFLY\n", host->host_no);
+
+ /*
+ * Traverse our list of running commands, and look
+ * for those with valid (non-0xff ff) status and message
+ * bytes encoded in the result which signify command
+ * completion.
+ */
+
+ local_irq_save(flags);
+restart:
+ for (cmd_prev_ptr = (struct NCR53c7x0_cmd **)&(hostdata->running_list),
+ cmd = (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ;
+ cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next),
+ cmd = (struct NCR53c7x0_cmd *) cmd->next)
+ {
+ Scsi_Cmnd *tmp;
+
+ if (!cmd) {
+ printk("scsi%d : very weird.\n", host->host_no);
+ break;
+ }
+
+ if (!(tmp = cmd->cmd)) {
+ printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n",
+ host->host_no);
+ continue;
+ }
+ /* Copy the result over now; may not be complete,
+ * but subsequent tests may as well be done on
+ * cached memory.
+ */
+ tmp->result = cmd->result;
+
+ if (((tmp->result & 0xff) == 0xff) ||
+ ((tmp->result & 0xff00) == 0xff00))
+ continue;
+
+ search_found = 1;
+
+ if (cmd->bounce.len)
+ memcpy ((void *)cmd->bounce.addr,
+ (void *)cmd->bounce.buf, cmd->bounce.len);
+
+ /* Important - remove from list _before_ done is called */
+ if (cmd_prev_ptr)
+ *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next;
+
+ --hostdata->busy[tmp->device->id][tmp->device->lun];
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
+
+ tmp->host_scribble = NULL;
+
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ",
+ host->host_no, tmp->pid, tmp->device->id, tmp->device->lun, tmp->result);
+ print_command (tmp->cmnd);
+ }
+
+ tmp->scsi_done(tmp);
+ goto restart;
+ }
+ local_irq_restore(flags);
+
+ if (!search_found) {
+ printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
+ host->host_no);
+ } else {
+ run_process_issue_queue();
+ }
+ return;
+}
+
+/*
+ * Function : static irqreturn_t NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs)
+ *
+ * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
+ * the same IRQ line.
+ *
+ * Inputs : Since we're using the SA_INTERRUPT interrupt handler
+ * semantics, irq indicates the interrupt which invoked
+ * this handler.
+ *
+ * On the 710 we simualte an INTFLY with a script interrupt, and the
+ * script interrupt handler will call back to this function.
+ */
+
+static irqreturn_t
+NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs)
+{
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host; /* Host we are looking at */
+ unsigned char istat; /* Values of interrupt regs */
+ struct NCR53c7x0_hostdata *hostdata; /* host->hostdata[0] */
+ struct NCR53c7x0_cmd *cmd; /* command which halted */
+ u32 *dsa; /* DSA */
+ int handled = 0;
+
+#ifdef NCR_DEBUG
+ char buf[80]; /* Debugging sprintf buffer */
+ size_t buflen; /* Length of same */
+#endif
+
+ host = (struct Scsi_Host *)dev_id;
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Only read istat once per loop, since reading it again will unstack
+ * interrupts
+ */
+
+ while ((istat = NCR53c7x0_read8(hostdata->istat)) & (ISTAT_SIP|ISTAT_DIP)) {
+ handled = 1;
+ hostdata->dsp_changed = 0;
+ hostdata->dstat_valid = 0;
+ hostdata->state = STATE_HALTED;
+
+ if (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK)
+ printk ("scsi%d : SCSI FIFO not empty\n", host->host_no);
+
+ /*
+ * NCR53c700 and NCR53c700-66 change the current SCSI
+ * process, hostdata->ncrcurrent, in the Linux driver so
+ * cmd = hostdata->ncrcurrent.
+ *
+ * With other chips, we must look through the commands
+ * executing and find the command structure which
+ * corresponds to the DSA register.
+ */
+
+ if (hostdata->options & OPTION_700) {
+ cmd = (struct NCR53c7x0_cmd *) hostdata->ncrcurrent;
+ } else {
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ for (cmd = (struct NCR53c7x0_cmd *) hostdata->running_list;
+ cmd && (dsa + (hostdata->dsa_start / sizeof(u32))) != cmd->dsa;
+ cmd = (struct NCR53c7x0_cmd *)(cmd->next))
+ ;
+ }
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ if (cmd) {
+ printk("scsi%d : interrupt for pid %lu, id %d, lun %d ",
+ host->host_no, cmd->cmd->pid, (int) cmd->cmd->device->id,
+ (int) cmd->cmd->device->lun);
+ print_command (cmd->cmd->cmnd);
+ } else {
+ printk("scsi%d : no active command\n", host->host_no);
+ }
+ }
+
+ if (istat & ISTAT_SIP) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ISTAT_SIP\n", host->host_no);
+ intr_scsi (host, cmd);
+ }
+
+ if (istat & ISTAT_DIP) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ISTAT_DIP\n", host->host_no);
+ intr_dma (host, cmd);
+ }
+
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+ /* Really need to check this out for 710 RGH */
+ NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF);
+ while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF)
+ ;
+ hostdata->dstat |= DSTAT_DFE;
+ }
+
+ if (!hostdata->idle && hostdata->state == STATE_HALTED) {
+ if (!hostdata->dsp_changed)
+ hostdata->dsp = (u32 *)bus_to_virt(NCR53c7x0_read32(DSP_REG));
+#if 0
+ printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n",
+ host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp);
+#endif
+
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
+ if (hostdata->options & OPTION_DEBUG_TRACE) {
+#ifdef CYCLIC_TRACE
+ log_insn (hostdata->dsp);
+#else
+ print_insn (host, hostdata->dsp, "t ", 1);
+#endif
+ NCR53c7x0_write8 (DCNTL_REG,
+ hostdata->saved_dcntl | DCNTL_SSM | DCNTL_STD);
+ }
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * Function : static int abort_connected (struct Scsi_Host *host)
+ *
+ * Purpose : Assuming that the NCR SCSI processor is currently
+ * halted, break the currently established nexus. Clean
+ * up of the NCR53c7x0_cmd and Scsi_Cmnd structures should
+ * be done on receipt of the abort interrupt.
+ *
+ * Inputs : host - SCSI host
+ *
+ */
+
+static int
+abort_connected (struct Scsi_Host *host) {
+#ifdef NEW_ABORT
+ NCR53c7x0_local_declare();
+#endif
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+/* FIXME : this probably should change for production kernels; at the
+ least, counter should move to a per-host structure. */
+ static int counter = 5;
+#ifdef NEW_ABORT
+ int sstat, phase, offset;
+ u32 *script;
+ NCR53c7x0_local_setup(host);
+#endif
+
+ if (--counter <= 0) {
+ disable(host);
+ return 0;
+ }
+
+ printk ("scsi%d : DANGER : abort_connected() called \n",
+ host->host_no);
+
+#ifdef NEW_ABORT
+
+/*
+ * New strategy : Rather than using a generic abort routine,
+ * we'll specifically try to source or sink the appropriate
+ * amount of data for the phase we're currently in (taking into
+ * account the current synchronous offset)
+ */
+
+ sstat = (NCR53c8x0_read8 (SSTAT2_REG);
+ offset = OFFSET (sstat & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
+ phase = sstat & SSTAT2_PHASE_MASK;
+
+/*
+ * SET ATN
+ * MOVE source_or_sink, WHEN CURRENT PHASE
+ * < repeat for each outstanding byte >
+ * JUMP send_abort_message
+ */
+
+ script = hostdata->abort_script = kmalloc (
+ 8 /* instruction size */ * (
+ 1 /* set ATN */ +
+ (!offset ? 1 : offset) /* One transfer per outstanding byte */ +
+ 1 /* send abort message */),
+ GFP_ATOMIC);
+
+
+#else /* def NEW_ABORT */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+#endif /* def NEW_ABORT */
+ hostdata->dsp_changed = 1;
+
+/* XXX - need to flag the command as aborted after the abort_connected
+ code runs
+ */
+ return 0;
+}
+
+/*
+ * Function : static int datapath_residual (Scsi_Host *host)
+ *
+ * Purpose : return residual data count of what's in the chip.
+ *
+ * Inputs : host - SCSI host
+ */
+
+static int
+datapath_residual (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ int count, synchronous, sstat;
+ unsigned int ddir;
+
+ NCR53c7x0_local_setup(host);
+ /* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */
+ count = ((NCR53c7x0_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) -
+ (NCR53c7x0_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK;
+ synchronous = NCR53c7x0_read8 (SXFER_REG) & SXFER_MO_MASK;
+ /* COMPAT : DDIR is elsewhere on non-'8xx chips. */
+ ddir = NCR53c7x0_read8 (CTEST0_REG_700) & CTEST0_700_DDIR;
+
+ if (ddir) {
+ /* Receive */
+ if (synchronous)
+ count += (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
+ else
+ if (NCR53c7x0_read8 (SSTAT1_REG) & SSTAT1_ILF)
+ ++count;
+ } else {
+ /* Send */
+ sstat = NCR53c7x0_read8 (SSTAT1_REG);
+ if (sstat & SSTAT1_OLF)
+ ++count;
+ if (synchronous && (sstat & SSTAT1_ORF))
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : static const char * sbcl_to_phase (int sbcl)_
+ *
+ * Purpose : Convert SBCL register to user-parsable phase representation
+ *
+ * Inputs : sbcl - value of sbcl register
+ */
+
+
+static const char *
+sbcl_to_phase (int sbcl) {
+ switch (sbcl & SBCL_PHASE_MASK) {
+ case SBCL_PHASE_DATAIN:
+ return "DATAIN";
+ case SBCL_PHASE_DATAOUT:
+ return "DATAOUT";
+ case SBCL_PHASE_MSGIN:
+ return "MSGIN";
+ case SBCL_PHASE_MSGOUT:
+ return "MSGOUT";
+ case SBCL_PHASE_CMDOUT:
+ return "CMDOUT";
+ case SBCL_PHASE_STATIN:
+ return "STATUSIN";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Function : static const char * sstat2_to_phase (int sstat)_
+ *
+ * Purpose : Convert SSTAT2 register to user-parsable phase representation
+ *
+ * Inputs : sstat - value of sstat register
+ */
+
+
+static const char *
+sstat2_to_phase (int sstat) {
+ switch (sstat & SSTAT2_PHASE_MASK) {
+ case SSTAT2_PHASE_DATAIN:
+ return "DATAIN";
+ case SSTAT2_PHASE_DATAOUT:
+ return "DATAOUT";
+ case SSTAT2_PHASE_MSGIN:
+ return "MSGIN";
+ case SSTAT2_PHASE_MSGOUT:
+ return "MSGOUT";
+ case SSTAT2_PHASE_CMDOUT:
+ return "CMDOUT";
+ case SSTAT2_PHASE_STATIN:
+ return "STATUSIN";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Function : static void intr_phase_mismatch (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handle phase mismatch interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ *
+ * Side effects : The abort_connected() routine is called or the NCR chip
+ * is restarted, jumping to the command_complete entry point, or
+ * patching the address and transfer count of the current instruction
+ * and calling the msg_in entry point as appropriate.
+ */
+
+static void
+intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ u32 dbc_dcmd, *dsp, *dsp_next;
+ unsigned char dcmd, sbcl;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ int residual;
+ enum {ACTION_ABORT, ACTION_ABORT_PRINT, ACTION_CONTINUE} action =
+ ACTION_ABORT_PRINT;
+ const char *where = NULL;
+
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Corrective action is based on where in the SCSI SCRIPT(tm) the error
+ * occurred, as well as which SCSI phase we are currently in.
+ */
+ dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG));
+
+ /*
+ * Fetch the current instruction, and remove the operands for easier
+ * interpretation.
+ */
+ dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+ dcmd = (dbc_dcmd & 0xff000000) >> 24;
+ /*
+ * Like other processors, the NCR adjusts the instruction pointer before
+ * instruction decode. Set the DSP address back to what it should
+ * be for this instruction based on its size (2 or 3 32 bit words).
+ */
+ dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+
+
+ /*
+ * Read new SCSI phase from the SBCL lines. Since all of our code uses
+ * a WHEN conditional instead of an IF conditional, we don't need to
+ * wait for a new REQ.
+ */
+ sbcl = NCR53c7x0_read8(SBCL_REG) & SBCL_PHASE_MASK;
+
+ if (!cmd) {
+ action = ACTION_ABORT_PRINT;
+ where = "no current command";
+ /*
+ * The way my SCSI SCRIPTS(tm) are architected, recoverable phase
+ * mismatches should only occur where we're doing a multi-byte
+ * BMI instruction. Specifically, this means
+ *
+ * - select messages (a SCSI-I target may ignore additional messages
+ * after the IDENTIFY; any target may reject a SDTR or WDTR)
+ *
+ * - command out (targets may send a message to signal an error
+ * condition, or go into STATUSIN after they've decided
+ * they don't like the command.
+ *
+ * - reply_message (targets may reject a multi-byte message in the
+ * middle)
+ *
+ * - data transfer routines (command completion with buffer space
+ * left, disconnect message, or error message)
+ */
+ } else if (((dsp >= cmd->data_transfer_start &&
+ dsp < cmd->data_transfer_end)) || dsp == (cmd->residual + 2)) {
+ if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT|
+ DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI|
+ DCMD_BMI_OP_MOVE_I)) {
+ residual = datapath_residual (host);
+ if (hostdata->options & OPTION_DEBUG_DISCONNECT)
+ printk ("scsi%d : handling residual transfer (+ %d bytes from DMA FIFO)\n",
+ host->host_no, residual);
+
+ /*
+ * The first instruction is a CALL to the alternate handler for
+ * this data transfer phase, so we can do calls to
+ * munge_msg_restart as we would if control were passed
+ * from normal dynamic code.
+ */
+ if (dsp != cmd->residual + 2) {
+ cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd->residual[1] = virt_to_bus(hostdata->script)
+ + ((dcmd & DCMD_BMI_IO)
+ ? hostdata->E_other_in : hostdata->E_other_out);
+ }
+
+ /*
+ * The second instruction is the a data transfer block
+ * move instruction, reflecting the pointer and count at the
+ * time of the phase mismatch.
+ */
+ cmd->residual[2] = dbc_dcmd + residual;
+ cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual;
+
+ /*
+ * The third and final instruction is a jump to the instruction
+ * which follows the instruction which had to be 'split'
+ */
+ if (dsp != cmd->residual + 2) {
+ cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE;
+ cmd->residual[5] = virt_to_bus(dsp_next);
+ }
+
+ /*
+ * For the sake of simplicity, transfer control to the
+ * conditional CALL at the start of the residual buffer.
+ */
+ hostdata->dsp = cmd->residual;
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ } else {
+ where = "non-BMI dynamic DSA code";
+ action = ACTION_ABORT_PRINT;
+ }
+ } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4 + 2)) {
+ /* RGH 290697: Added +2 above, to compensate for the script
+ * instruction which disables the selection timer. */
+ /* Release ATN */
+ NCR53c7x0_write8 (SOCL_REG, 0);
+ switch (sbcl) {
+ /*
+ * Some devices (SQ555 come to mind) grab the IDENTIFY message
+ * sent on selection, and decide to go into COMMAND OUT phase
+ * rather than accepting the rest of the messages or rejecting
+ * them. Handle these devices gracefully.
+ */
+ case SBCL_PHASE_CMDOUT:
+ hostdata->dsp = dsp + 2 /* two _words_ */;
+ hostdata->dsp_changed = 1;
+ printk ("scsi%d : target %d ignored SDTR and went into COMMAND OUT\n",
+ host->host_no, cmd->cmd->device->id);
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ action = ACTION_CONTINUE;
+ break;
+ case SBCL_PHASE_MSGIN:
+ hostdata->dsp = hostdata->script + hostdata->E_msg_in /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ break;
+ default:
+ where="select message out";
+ action = ACTION_ABORT_PRINT;
+ }
+ /*
+ * Some SCSI devices will interpret a command as they read the bytes
+ * off the SCSI bus, and may decide that the command is Bogus before
+ * they've read the entire command off the bus.
+ */
+ } else if (dsp == hostdata->script + hostdata->E_cmdout_cmdout / sizeof
+ (u32)) {
+ hostdata->dsp = hostdata->script + hostdata->E_data_transfer /
+ sizeof (u32);
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ /* FIXME : we need to handle message reject, etc. within msg_respond. */
+#ifdef notyet
+ } else if (dsp == hostdata->script + hostdata->E_reply_message) {
+ switch (sbcl) {
+ /* Any other phase mismatches abort the currently executing command. */
+#endif
+ } else {
+ where = "unknown location";
+ action = ACTION_ABORT_PRINT;
+ }
+
+ /* Flush DMA FIFO */
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ /* Really need to check this out for 710 RGH */
+ NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF);
+ while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF);
+ hostdata->dstat |= DSTAT_DFE;
+ }
+
+ switch (action) {
+ case ACTION_ABORT_PRINT:
+ printk("scsi%d : %s : unexpected phase %s.\n",
+ host->host_no, where ? where : "unknown location",
+ sbcl_to_phase(sbcl));
+ print_lots (host);
+ /* Fall through to ACTION_ABORT */
+ case ACTION_ABORT:
+ abort_connected (host);
+ break;
+ case ACTION_CONTINUE:
+ break;
+ }
+
+#if 0
+ if (hostdata->dsp_changed) {
+ printk("scsi%d: new dsp 0x%p\n", host->host_no, hostdata->dsp);
+ print_insn (host, hostdata->dsp, "", 1);
+ }
+#endif
+}
+
+/*
+ * Function : static void intr_bf (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle BUS FAULT interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ u32 *dsp,
+ *next_dsp, /* Current dsp */
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ char *reason = NULL;
+ /* Default behavior is for a silent error, with a retry until we've
+ exhausted retries. */
+ enum {MAYBE, ALWAYS, NEVER} retry = MAYBE;
+ int report = 0;
+ NCR53c7x0_local_setup(host);
+
+ dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+ next_dsp = bus_to_virt (NCR53c7x0_read32(DSP_REG));
+ dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* FIXME - check chip type */
+ dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG));
+
+ /*
+ * Bus faults can be caused by either a Bad Address or
+ * Target Abort. We should check the Received Target Abort
+ * bit of the PCI status register and Master Abort Bit.
+ *
+ * - Master Abort bit indicates that no device claimed
+ * the address with DEVSEL within five clocks
+ *
+ * - Target Abort bit indicates that a target claimed it,
+ * but changed its mind once it saw the byte enables.
+ *
+ */
+
+ /* 53c710, not PCI system */
+ report = 1;
+ reason = "Unknown";
+
+#ifndef notyet
+ report = 1;
+#endif
+ if (report && reason)
+ {
+ printk(KERN_ALERT "scsi%d : BUS FAULT reason = %s\n",
+ host->host_no, reason ? reason : "unknown");
+ print_lots (host);
+ }
+
+#ifndef notyet
+ retry = NEVER;
+#endif
+
+ /*
+ * TODO : we should attempt to recover from any spurious bus
+ * faults. After X retries, we should figure that things are
+ * sufficiently wedged, and call NCR53c7xx_reset.
+ *
+ * This code should only get executed once we've decided that we
+ * cannot retry.
+ */
+
+ if (retry == NEVER) {
+ printk(KERN_ALERT " mail richard@sleepie.demon.co.uk\n");
+ FATAL (host);
+ }
+}
+
+/*
+ * Function : static void intr_dma (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all DMA interrupts, indicated by the setting
+ * of the DIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned char dstat; /* DSTAT */
+ u32 *dsp,
+ *next_dsp, /* Current dsp */
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ int tmp;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ dstat = hostdata->dstat;
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat);
+
+ dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+ next_dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* XXX - check chip type */
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+
+ /*
+ * DSTAT_ABRT is the aborted interrupt. This is set whenever the
+ * SCSI chip is aborted.
+ *
+ * With NCR53c700 and NCR53c700-66 style chips, we should only
+ * get this when the chip is currently running the accept
+ * reselect/select code and we have set the abort bit in the
+ * ISTAT register.
+ *
+ */
+
+ if (dstat & DSTAT_ABRT) {
+#if 0
+ /* XXX - add code here to deal with normal abort */
+ if ((hostdata->options & OPTION_700) && (hostdata->state ==
+ STATE_ABORTING)) {
+ } else
+#endif
+ {
+ printk(KERN_ALERT "scsi%d : unexpected abort interrupt at\n"
+ " ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "s ", 1);
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_SSI is the single step interrupt. Should be generated
+ * whenever we have single stepped or are tracing.
+ */
+
+ if (dstat & DSTAT_SSI) {
+ if (hostdata->options & OPTION_DEBUG_TRACE) {
+ /* Don't print instr. until we write DSP at end of intr function */
+ } else if (hostdata->options & OPTION_DEBUG_SINGLE) {
+ print_insn (host, dsp, "s ", 0);
+ local_irq_save(flags);
+/* XXX - should we do this, or can we get away with writing dsp? */
+
+ NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) &
+ ~DCNTL_SSM) | DCNTL_STD);
+ local_irq_restore(flags);
+ } else {
+ printk(KERN_ALERT "scsi%d : unexpected single step interrupt at\n"
+ " ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG\n");
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name
+ * is different) is generated whenever an illegal instruction is
+ * encountered.
+ *
+ * XXX - we may want to emulate INTFLY here, so we can use
+ * the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810
+ * chips.
+ */
+
+ if (dstat & DSTAT_OPC) {
+ /*
+ * Ascertain if this IID interrupts occurred before or after a STO
+ * interrupt. Since the interrupt handling code now leaves
+ * DSP unmodified until _after_ all stacked interrupts have been
+ * processed, reading the DSP returns the original DSP register.
+ * This means that if dsp lies between the select code, and
+ * message out following the selection code (where the IID interrupt
+ * would have to have occurred by due to the implicit wait for REQ),
+ * we have an IID interrupt resulting from a STO condition and
+ * can ignore it.
+ */
+
+ if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) &&
+ (dsp <= (hostdata->script + hostdata->E_select_msgout /
+ sizeof(u32) + 8))) || (hostdata->test_running == 2)) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n",
+ host->host_no);
+ if (hostdata->expecting_iid) {
+ hostdata->expecting_iid = 0;
+ hostdata->idle = 1;
+ if (hostdata->test_running == 2) {
+ hostdata->test_running = 0;
+ hostdata->test_completed = 3;
+ } else if (cmd)
+ abnormal_finished (cmd, DID_BAD_TARGET << 16);
+ } else {
+ hostdata->expecting_sto = 1;
+ }
+ /*
+ * We can't guarantee we'll be able to execute the WAIT DISCONNECT
+ * instruction within the 3.4us of bus free and arbitration delay
+ * that a target can RESELECT in and assert REQ after we've dropped
+ * ACK. If this happens, we'll get an illegal instruction interrupt.
+ * Doing away with the WAIT DISCONNECT instructions broke everything,
+ * so instead I'll settle for moving one WAIT DISCONNECT a few
+ * instructions closer to the CLEAR ACK before it to minimize the
+ * chances of this happening, and handle it if it occurs anyway.
+ *
+ * Simply continue with what we were doing, and control should
+ * be transferred to the schedule routine which will ultimately
+ * pass control onto the reselection or selection (not yet)
+ * code.
+ */
+ } else if (dbc_dcmd == 0x48000000 && (NCR53c7x0_read8 (SBCL_REG) &
+ SBCL_REQ)) {
+ if (!(hostdata->options & OPTION_NO_PRINT_RACE))
+ {
+ printk("scsi%d: REQ before WAIT DISCONNECT IID\n",
+ host->host_no);
+ hostdata->options |= OPTION_NO_PRINT_RACE;
+ }
+ } else {
+ printk(KERN_ALERT "scsi%d : invalid instruction\n", host->host_no);
+ print_lots (host);
+ printk(KERN_ALERT " mail Richard@sleepie.demon.co.uk with ALL\n"
+ " boot messages and diagnostic output\n");
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_BF are bus fault errors. DSTAT_800_BF is valid for 710 also.
+ */
+
+ if (dstat & DSTAT_800_BF) {
+ intr_bf (host, cmd);
+ }
+
+
+ /*
+ * DSTAT_SIR interrupts are generated by the execution of
+ * the INT instruction. Since the exact values available
+ * are determined entirely by the SCSI script running,
+ * and are local to a particular script, a unique handler
+ * is called for each script.
+ */
+
+ if (dstat & DSTAT_SIR) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : DSTAT_SIR\n", host->host_no);
+ switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) {
+ case SPECIFIC_INT_NOTHING:
+ case SPECIFIC_INT_RESTART:
+ break;
+ case SPECIFIC_INT_ABORT:
+ abort_connected(host);
+ break;
+ case SPECIFIC_INT_PANIC:
+ printk(KERN_ALERT "scsi%d : failure at ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC\n");
+ FATAL (host);
+ break;
+ case SPECIFIC_INT_BREAK:
+ intr_break (host, cmd);
+ break;
+ default:
+ printk(KERN_ALERT "scsi%d : failure at ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT" dstat_sir_intr() returned unknown value %d\n",
+ tmp);
+ FATAL (host);
+ }
+ }
+}
+
+/*
+ * Function : static int print_insn (struct Scsi_Host *host,
+ * u32 *insn, int kernel)
+ *
+ * Purpose : print numeric representation of the instruction pointed
+ * to by insn to the debugging or kernel message buffer
+ * as appropriate.
+ *
+ * If desired, a user level program can interpret this
+ * information.
+ *
+ * Inputs : host, insn - host, pointer to instruction, prefix -
+ * string to prepend, kernel - use printk instead of debugging buffer.
+ *
+ * Returns : size, in u32s, of instruction printed.
+ */
+
+/*
+ * FIXME: should change kernel parameter so that it takes an ENUM
+ * specifying severity - either KERN_ALERT or KERN_PANIC so
+ * all panic messages are output with the same severity.
+ */
+
+static int
+print_insn (struct Scsi_Host *host, const u32 *insn,
+ const char *prefix, int kernel) {
+ char buf[160], /* Temporary buffer and pointer. ICKY
+ arbitrary length. */
+
+
+ *tmp;
+ unsigned char dcmd; /* dcmd register for *insn */
+ int size;
+
+ /*
+ * Check to see if the instruction pointer is not bogus before
+ * indirecting through it; avoiding red-zone at start of
+ * memory.
+ *
+ * FIXME: icky magic needs to happen here on non-intel boxes which
+ * don't have kernel memory mapped in like this. Might be reasonable
+ * to use vverify()?
+ */
+
+ if (virt_to_phys((void *)insn) < PAGE_SIZE ||
+ virt_to_phys((void *)(insn + 8)) > virt_to_phys(high_memory) ||
+ ((((dcmd = (insn[0] >> 24) & 0xff) & DCMD_TYPE_MMI) == DCMD_TYPE_MMI) &&
+ virt_to_phys((void *)(insn + 12)) > virt_to_phys(high_memory))) {
+ size = 0;
+ sprintf (buf, "%s%p: address out of range\n",
+ prefix, insn);
+ } else {
+/*
+ * FIXME : (void *) cast in virt_to_bus should be unnecessary, because
+ * it should take const void * as argument.
+ */
+#if !defined(CONFIG_MVME16x) && !defined(CONFIG_BVME6000)
+ sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)",
+ (prefix ? prefix : ""), virt_to_bus((void *) insn), insn,
+ insn[0], insn[1], bus_to_virt (insn[1]));
+#else
+ /* Remove virtual addresses to reduce output, as they are the same */
+ sprintf(buf, "%s0x%x (+%x) : 0x%08x 0x%08x",
+ (prefix ? prefix : ""), (u32)insn, ((u32)insn -
+ (u32)&(((struct NCR53c7x0_hostdata *)host->hostdata[0])->script))/4,
+ insn[0], insn[1]);
+#endif
+ tmp = buf + strlen(buf);
+ if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) {
+#if !defined(CONFIG_MVME16x) && !defined(CONFIG_BVME6000)
+ sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2],
+ bus_to_virt(insn[2]));
+#else
+ /* Remove virtual addr to reduce output, as it is the same */
+ sprintf (tmp, " 0x%08x\n", insn[2]);
+#endif
+ size = 3;
+ } else {
+ sprintf (tmp, "\n");
+ size = 2;
+ }
+ }
+
+ if (kernel)
+ printk ("%s", buf);
+#ifdef NCR_DEBUG
+ else {
+ size_t len = strlen(buf);
+ debugger_kernel_write(host, buf, len);
+ }
+#endif
+ return size;
+}
+
+/*
+ * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command, doing all necessary
+ * cleanup of the issue_queue, running_list, shared Linux/NCR
+ * dsa issue and reconnect queues.
+ *
+ * Inputs : cmd - command to abort, code - entire result field
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+int
+NCR53c7xx_abort (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host = cmd->device->host;
+ struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *)
+ host->hostdata[0] : NULL;
+ unsigned long flags;
+ struct NCR53c7x0_cmd *curr, **prev;
+ Scsi_Cmnd *me, **last;
+#if 0
+ static long cache_pid = -1;
+#endif
+
+
+ if (!host) {
+ printk ("Bogus SCSI command pid %ld; no host structure\n",
+ cmd->pid);
+ return SCSI_ABORT_ERROR;
+ } else if (!hostdata) {
+ printk ("Bogus SCSI host %d; no hostdata\n", host->host_no);
+ return SCSI_ABORT_ERROR;
+ }
+ NCR53c7x0_local_setup(host);
+
+/*
+ * CHECK : I don't think that reading ISTAT will unstack any interrupts,
+ * since we need to write the INTF bit to clear it, and SCSI/DMA
+ * interrupts don't clear until we read SSTAT/SIST and DSTAT registers.
+ *
+ * See that this is the case. Appears to be correct on the 710, at least.
+ *
+ * I suspect that several of our failures may be coming from a new fatal
+ * interrupt (possibly due to a phase mismatch) happening after we've left
+ * the interrupt handler, but before the PIC has had the interrupt condition
+ * cleared.
+ */
+
+ if (NCR53c7x0_read8(hostdata->istat) & (ISTAT_DIP|ISTAT_SIP)) {
+ printk ("scsi%d : dropped interrupt for command %ld\n", host->host_no,
+ cmd->pid);
+ NCR53c7x0_intr (host->irq, NULL, NULL);
+ return SCSI_ABORT_BUSY;
+ }
+
+ local_irq_save(flags);
+#if 0
+ if (cache_pid == cmd->pid)
+ panic ("scsi%d : bloody fetus %d\n", host->host_no, cmd->pid);
+ else
+ cache_pid = cmd->pid;
+#endif
+
+
+/*
+ * The command could be hiding in the issue_queue. This would be very
+ * nice, as commands can't be moved from the high level driver's issue queue
+ * into the shared queue until an interrupt routine is serviced, and this
+ * moving is atomic.
+ *
+ * If this is the case, we don't have to worry about anything - we simply
+ * pull the command out of the old queue, and call it aborted.
+ */
+
+ for (me = (Scsi_Cmnd *) hostdata->issue_queue,
+ last = (Scsi_Cmnd **) &(hostdata->issue_queue);
+ me && me != cmd; last = (Scsi_Cmnd **)&(me->SCp.ptr),
+ me = (Scsi_Cmnd *)me->SCp.ptr);
+
+ if (me) {
+ *last = (Scsi_Cmnd *) me->SCp.ptr;
+ if (me->host_scribble) {
+ ((struct NCR53c7x0_cmd *)me->host_scribble)->next = hostdata->free;
+ hostdata->free = (struct NCR53c7x0_cmd *) me->host_scribble;
+ me->host_scribble = NULL;
+ }
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+ printk ("scsi%d : found command %ld in Linux issue queue\n",
+ host->host_no, me->pid);
+ local_irq_restore(flags);
+ run_process_issue_queue();
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * That failing, the command could be in our list of already executing
+ * commands. If this is the case, drastic measures are called for.
+ */
+
+ for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list,
+ prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list);
+ curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
+
+ if (curr) {
+ if ((curr->result & 0xff) != 0xff && (curr->result & 0xff00) != 0xff00) {
+ cmd->result = curr->result;
+ if (prev)
+ *prev = (struct NCR53c7x0_cmd *) curr->next;
+ curr->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ cmd->host_scribble = NULL;
+ hostdata->free = curr;
+ cmd->scsi_done(cmd);
+ printk ("scsi%d : found finished command %ld in running list\n",
+ host->host_no, cmd->pid);
+ local_irq_restore(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ } else {
+ printk ("scsi%d : DANGER : command running, can not abort.\n",
+ cmd->device->host->host_no);
+ local_irq_restore(flags);
+ return SCSI_ABORT_BUSY;
+ }
+ }
+
+/*
+ * And if we couldn't find it in any of our queues, it must have been
+ * a dropped interrupt.
+ */
+
+ curr = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (curr) {
+ curr->next = hostdata->free;
+ hostdata->free = curr;
+ cmd->host_scribble = NULL;
+ }
+
+ if (curr == NULL || ((curr->result & 0xff00) == 0xff00) ||
+ ((curr->result & 0xff) == 0xff)) {
+ printk ("scsi%d : did this command ever run?\n", host->host_no);
+ cmd->result = DID_ABORT << 16;
+ } else {
+ printk ("scsi%d : probably lost INTFLY, normal completion\n",
+ host->host_no);
+ cmd->result = curr->result;
+/*
+ * FIXME : We need to add an additional flag which indicates if a
+ * command was ever counted as BUSY, so if we end up here we can
+ * decrement the busy count if and only if it is necessary.
+ */
+ --hostdata->busy[cmd->device->id][cmd->device->lun];
+ }
+ local_irq_restore(flags);
+ cmd->scsi_done(cmd);
+
+/*
+ * We need to run process_issue_queue since termination of this command
+ * may allow another queued command to execute first?
+ */
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+/*
+ * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : perform a hard reset of the SCSI bus and NCR
+ * chip.
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ */
+
+int
+NCR53c7xx_reset (Scsi_Cmnd *cmd, unsigned int reset_flags) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ int found = 0;
+ struct NCR53c7x0_cmd * c;
+ Scsi_Cmnd *tmp;
+ /*
+ * When we call scsi_done(), it's going to wake up anything sleeping on the
+ * resources which were in use by the aborted commands, and we'll start to
+ * get new commands.
+ *
+ * We can't let this happen until after we've re-initialized the driver
+ * structures, and can't reinitialize those structures until after we've
+ * dealt with their contents.
+ *
+ * So, we need to find all of the commands which were running, stick
+ * them on a linked list of completed commands (we'll use the host_scribble
+ * pointer), do our reinitialization, and then call the done function for
+ * each command.
+ */
+ Scsi_Cmnd *nuke_list = NULL;
+ struct Scsi_Host *host = cmd->device->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata[0];
+
+ NCR53c7x0_local_setup(host);
+ local_irq_save(flags);
+ ncr_halt (host);
+ print_lots (host);
+ dump_events (host, 30);
+ ncr_scsi_reset (host);
+ for (tmp = nuke_list = return_outstanding_commands (host, 1 /* free */,
+ 0 /* issue */ ); tmp; tmp = (Scsi_Cmnd *) tmp->SCp.buffer)
+ if (tmp == cmd) {
+ found = 1;
+ break;
+ }
+
+ /*
+ * If we didn't find the command which caused this reset in our running
+ * list, then we've lost it. See that it terminates normally anyway.
+ */
+ if (!found) {
+ c = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (c) {
+ cmd->host_scribble = NULL;
+ c->next = hostdata->free;
+ hostdata->free = c;
+ } else
+ printk ("scsi%d: lost command %ld\n", host->host_no, cmd->pid);
+ cmd->SCp.buffer = (struct scatterlist *) nuke_list;
+ nuke_list = cmd;
+ }
+
+ NCR53c7x0_driver_init (host);
+ hostdata->soft_reset (host);
+ if (hostdata->resets == 0)
+ disable(host);
+ else if (hostdata->resets != -1)
+ --hostdata->resets;
+ local_irq_restore(flags);
+ for (; nuke_list; nuke_list = tmp) {
+ tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer;
+ nuke_list->result = DID_RESET << 16;
+ nuke_list->scsi_done (nuke_list);
+ }
+ local_irq_restore(flags);
+ return SCSI_RESET_SUCCESS;
+}
+
+/*
+ * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and
+ * therefore shares the scsicam_bios_param function.
+ */
+
+/*
+ * Function : int insn_to_offset (Scsi_Cmnd *cmd, u32 *insn)
+ *
+ * Purpose : convert instructions stored at NCR pointer into data
+ * pointer offset.
+ *
+ * Inputs : cmd - SCSI command; insn - pointer to instruction. Either current
+ * DSP, or saved data pointer.
+ *
+ * Returns : offset on success, -1 on failure.
+ */
+
+
+static int
+insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) {
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) cmd->device->host->hostdata[0];
+ struct NCR53c7x0_cmd *ncmd =
+ (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ int offset = 0, buffers;
+ struct scatterlist *segment;
+ char *ptr;
+ int found = 0;
+
+/*
+ * With the current code implementation, if the insn is inside dynamically
+ * generated code, the data pointer will be the instruction preceding
+ * the next transfer segment.
+ */
+
+ if (!check_address ((unsigned long) ncmd, sizeof (struct NCR53c7x0_cmd)) &&
+ ((insn >= ncmd->data_transfer_start &&
+ insn < ncmd->data_transfer_end) ||
+ (insn >= ncmd->residual &&
+ insn < (ncmd->residual +
+ sizeof(ncmd->residual))))) {
+ ptr = bus_to_virt(insn[3]);
+
+ if ((buffers = cmd->use_sg)) {
+ for (offset = 0,
+ segment = (struct scatterlist *) cmd->buffer;
+ buffers && !((found = ((ptr >= (char *)page_address(segment->page)+segment->offset) &&
+ (ptr < ((char *)page_address(segment->page)+segment->offset+segment->length)))));
+ --buffers, offset += segment->length, ++segment)
+#if 0
+ printk("scsi%d: comparing 0x%p to 0x%p\n",
+ cmd->device->host->host_no, saved, page_address(segment->page+segment->offset);
+#else
+ ;
+#endif
+ offset += ptr - ((char *)page_address(segment->page)+segment->offset);
+ } else {
+ found = 1;
+ offset = ptr - (char *) (cmd->request_buffer);
+ }
+ } else if ((insn >= hostdata->script +
+ hostdata->E_data_transfer / sizeof(u32)) &&
+ (insn <= hostdata->script +
+ hostdata->E_end_data_transfer / sizeof(u32))) {
+ found = 1;
+ offset = 0;
+ }
+ return found ? offset : -1;
+}
+
+
+
+/*
+ * Function : void print_progress (Scsi_Cmnd *cmd)
+ *
+ * Purpose : print the current location of the saved data pointer
+ *
+ * Inputs : cmd - command we are interested in
+ *
+ */
+
+static void
+print_progress (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_cmd *ncmd =
+ (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ int offset, i;
+ char *where;
+ u32 *ptr;
+ NCR53c7x0_local_setup (cmd->device->host);
+
+ if (check_address ((unsigned long) ncmd,sizeof (struct NCR53c7x0_cmd)) == 0)
+ {
+ printk("\nNCR53c7x0_cmd fields:\n");
+ printk(" bounce.len=0x%x, addr=0x%0x, buf[]=0x%02x %02x %02x %02x\n",
+ ncmd->bounce.len, ncmd->bounce.addr, ncmd->bounce.buf[0],
+ ncmd->bounce.buf[1], ncmd->bounce.buf[2], ncmd->bounce.buf[3]);
+ printk(" result=%04x, cdb[0]=0x%02x\n", ncmd->result, ncmd->cmnd[0]);
+ }
+
+ for (i = 0; i < 2; ++i) {
+ if (check_address ((unsigned long) ncmd,
+ sizeof (struct NCR53c7x0_cmd)) == -1)
+ continue;
+ if (!i) {
+ where = "saved";
+ ptr = bus_to_virt(ncmd->saved_data_pointer);
+ } else {
+ where = "active";
+ ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) -
+ NCR53c7x0_insn_size (NCR53c7x0_read8 (DCMD_REG)) *
+ sizeof(u32));
+ }
+ offset = insn_to_offset (cmd, ptr);
+
+ if (offset != -1)
+ printk ("scsi%d : %s data pointer at offset %d\n",
+ cmd->device->host->host_no, where, offset);
+ else {
+ int size;
+ printk ("scsi%d : can't determine %s data pointer offset\n",
+ cmd->device->host->host_no, where);
+ if (ncmd) {
+ size = print_insn (cmd->device->host,
+ bus_to_virt(ncmd->saved_data_pointer), "", 1);
+ print_insn (cmd->device->host,
+ bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32),
+ "", 1);
+ }
+ }
+ }
+}
+
+
+static void
+print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ int i, len;
+ char *ptr;
+ Scsi_Cmnd *cmd;
+
+ if (check_address ((unsigned long) dsa, hostdata->dsa_end -
+ hostdata->dsa_start) == -1) {
+ printk("scsi%d : bad dsa virt 0x%p\n", host->host_no, dsa);
+ return;
+ }
+ printk("%sscsi%d : dsa at phys 0x%lx (virt 0x%p)\n"
+ " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" ,
+ prefix ? prefix : "",
+ host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout,
+ dsa[hostdata->dsa_msgout / sizeof(u32)],
+ dsa[hostdata->dsa_msgout / sizeof(u32) + 1],
+ bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]));
+
+ /*
+ * Only print messages if they're sane in length so we don't
+ * blow the kernel printk buffer on something which won't buy us
+ * anything.
+ */
+
+ if (dsa[hostdata->dsa_msgout / sizeof(u32)] <
+ sizeof (hostdata->free->select))
+ for (i = dsa[hostdata->dsa_msgout / sizeof(u32)],
+ ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]);
+ i > 0 && !check_address ((unsigned long) ptr, 1);
+ ptr += len, i -= len) {
+ printk(" ");
+ len = print_msg (ptr);
+ printk("\n");
+ if (!len)
+ break;
+ }
+
+ printk(" + %d : select_indirect = 0x%x\n",
+ hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]);
+ cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]);
+ printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd,
+ (u32) virt_to_bus(cmd));
+ /* XXX Maybe we should access cmd->host_scribble->result here. RGH */
+ if (cmd) {
+ printk(" result = 0x%x, target = %d, lun = %d, cmd = ",
+ cmd->result, cmd->device->id, cmd->device->lun);
+ print_command(cmd->cmnd);
+ } else
+ printk("\n");
+ printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next,
+ dsa[hostdata->dsa_next / sizeof(u32)]);
+ if (cmd) {
+ printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n"
+ " script : ",
+ host->host_no, cmd->device->id,
+ hostdata->sync[cmd->device->id].sxfer_sanity,
+ hostdata->sync[cmd->device->id].scntl3_sanity);
+ for (i = 0; i < (sizeof(hostdata->sync[cmd->device->id].script) / 4); ++i)
+ printk ("0x%x ", hostdata->sync[cmd->device->id].script[i]);
+ printk ("\n");
+ print_progress (cmd);
+ }
+}
+/*
+ * Function : void print_queues (Scsi_Host *host)
+ *
+ * Purpose : print the contents of the NCR issue and reconnect queues
+ *
+ * Inputs : host - SCSI host we are interested in
+ *
+ */
+
+static void
+print_queues (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ u32 *dsa, *next_dsa;
+ volatile u32 *ncrcurrent;
+ int left;
+ Scsi_Cmnd *cmd, *next_cmd;
+ unsigned long flags;
+
+ printk ("scsi%d : issue queue\n", host->host_no);
+
+ for (left = host->can_queue, cmd = (Scsi_Cmnd *) hostdata->issue_queue;
+ left >= 0 && cmd;
+ cmd = next_cmd) {
+ next_cmd = (Scsi_Cmnd *) cmd->SCp.ptr;
+ local_irq_save(flags);
+ if (cmd->host_scribble) {
+ if (check_address ((unsigned long) (cmd->host_scribble),
+ sizeof (cmd->host_scribble)) == -1)
+ printk ("scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd\n",
+ host->host_no, cmd->pid);
+ /* print_dsa does sanity check on address, no need to check */
+ else
+ print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble)
+ -> dsa, "");
+ } else
+ printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n",
+ host->host_no, cmd->pid, cmd->device->id, cmd->device->lun);
+ local_irq_restore(flags);
+ }
+
+ if (left <= 0) {
+ printk ("scsi%d : loop detected in issue queue\n",
+ host->host_no);
+ }
+
+ /*
+ * Traverse the NCR reconnect and start DSA structures, printing out
+ * each element until we hit the end or detect a loop. Currently,
+ * the reconnect structure is a linked list; and the start structure
+ * is an array. Eventually, the reconnect structure will become a
+ * list as well, since this simplifies the code.
+ */
+
+ printk ("scsi%d : schedule dsa array :\n", host->host_no);
+ for (left = host->can_queue, ncrcurrent = hostdata->schedule;
+ left > 0; ncrcurrent += 2, --left)
+ if (ncrcurrent[0] != hostdata->NOP_insn)
+/* FIXME : convert pointer to dsa_begin to pointer to dsa. */
+ print_dsa (host, bus_to_virt (ncrcurrent[1] -
+ (hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template)), "");
+ printk ("scsi%d : end schedule dsa array\n", host->host_no);
+
+ printk ("scsi%d : reconnect_dsa_head :\n", host->host_no);
+
+ for (left = host->can_queue,
+ dsa = bus_to_virt (hostdata->reconnect_dsa_head);
+ left >= 0 && dsa;
+ dsa = next_dsa) {
+ local_irq_save(flags);
+ if (check_address ((unsigned long) dsa, sizeof(dsa)) == -1) {
+ printk ("scsi%d: bad DSA pointer 0x%p", host->host_no,
+ dsa);
+ next_dsa = NULL;
+ }
+ else
+ {
+ next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]);
+ print_dsa (host, dsa, "");
+ }
+ local_irq_restore(flags);
+ }
+ printk ("scsi%d : end reconnect_dsa_head\n", host->host_no);
+ if (left < 0)
+ printk("scsi%d: possible loop in ncr reconnect list\n",
+ host->host_no);
+}
+
+static void
+print_lots (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ u32 *dsp_next, *dsp, *dsa, dbc_dcmd;
+ unsigned char dcmd, sbcl;
+ int i, size;
+ NCR53c7x0_local_setup(host);
+
+ if ((dsp_next = bus_to_virt(NCR53c7x0_read32 (DSP_REG)))) {
+ dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+ dcmd = (dbc_dcmd & 0xff000000) >> 24;
+ dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ sbcl = NCR53c7x0_read8 (SBCL_REG);
+
+ /*
+ * For the 53c710, the following will report value 0 for SCNTL3
+ * and STEST0 - we don't have these registers.
+ */
+ printk ("scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p)\n"
+ " DSA=0x%lx (virt 0x%p)\n"
+ " DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x\n"
+ " SXFER=0x%x, SCNTL3=0x%x\n"
+ " %s%s%sphase=%s, %d bytes in SCSI FIFO\n"
+ " SCRATCH=0x%x, saved2_dsa=0x%0lx\n",
+ host->host_no, dbc_dcmd, NCR53c7x0_read32(DNAD_REG),
+ bus_to_virt(NCR53c7x0_read32(DNAD_REG)),
+ virt_to_bus(dsa), dsa,
+ NCR53c7x0_read32(DSPS_REG), NCR53c7x0_read32(TEMP_REG),
+ bus_to_virt (NCR53c7x0_read32(TEMP_REG)),
+ (int) NCR53c7x0_read8(hostdata->dmode),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ ((hostdata->chip / 100) == 8) ?
+ (int) NCR53c7x0_read8(SCNTL3_REG_800) : 0,
+ (sbcl & SBCL_BSY) ? "BSY " : "",
+ (sbcl & SBCL_SEL) ? "SEL " : "",
+ (sbcl & SBCL_REQ) ? "REQ " : "",
+ sstat2_to_phase(NCR53c7x0_read8 (((hostdata->chip / 100) == 8) ?
+ SSTAT1_REG : SSTAT2_REG)),
+ (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT,
+ ((hostdata->chip / 100) == 8) ? NCR53c7x0_read8 (STEST0_REG_800) :
+ NCR53c7x0_read32(SCRATCHA_REG_800),
+ hostdata->saved2_dsa);
+ printk ("scsi%d : DSP 0x%lx (virt 0x%p) ->\n", host->host_no,
+ virt_to_bus(dsp), dsp);
+ for (i = 6; i > 0; --i, dsp += size)
+ size = print_insn (host, dsp, "", 1);
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ if ((hostdata->chip / 100) == 8)
+ printk ("scsi%d : connected (SDID=0x%x, SSID=0x%x)\n",
+ host->host_no, NCR53c7x0_read8 (SDID_REG_800),
+ NCR53c7x0_read8 (SSID_REG_800));
+ else
+ printk ("scsi%d : connected (SDID=0x%x)\n",
+ host->host_no, NCR53c7x0_read8 (SDID_REG_700));
+ print_dsa (host, dsa, "");
+ }
+
+#if 1
+ print_queues (host);
+#endif
+ }
+}
+
+/*
+ * Function : static int shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the NCR SCSI
+ * chip. Use prior to dumping core, unloading the NCR driver,
+ *
+ * Returns : 0 on success
+ */
+static int
+shutdown (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ NCR53c7x0_local_setup(host);
+ local_irq_save(flags);
+/* Get in a state where we can reset the SCSI bus */
+ ncr_halt (host);
+ ncr_scsi_reset (host);
+ hostdata->soft_reset(host);
+
+ disable (host);
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*
+ * Function : void ncr_scsi_reset (struct Scsi_Host *host)
+ *
+ * Purpose : reset the SCSI bus.
+ */
+
+static void
+ncr_scsi_reset (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+ local_irq_save(flags);
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ udelay(25); /* Minimum amount of time to assert RST */
+ NCR53c7x0_write8(SCNTL1_REG, 0);
+ local_irq_restore(flags);
+}
+
+/*
+ * Function : void hard_reset (struct Scsi_Host *host)
+ *
+ */
+
+static void
+hard_reset (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned long flags;
+ local_irq_save(flags);
+ ncr_scsi_reset(host);
+ NCR53c7x0_driver_init (host);
+ if (hostdata->soft_reset)
+ hostdata->soft_reset (host);
+ local_irq_restore(flags);
+}
+
+
+/*
+ * Function : Scsi_Cmnd *return_outstanding_commands (struct Scsi_Host *host,
+ * int free, int issue)
+ *
+ * Purpose : return a linked list (using the SCp.buffer field as next,
+ * so we don't perturb hostdata. We don't use a field of the
+ * NCR53c7x0_cmd structure since we may not have allocated one
+ * for the command causing the reset.) of Scsi_Cmnd structures that
+ * had propagated below the Linux issue queue level. If free is set,
+ * free the NCR53c7x0_cmd structures which are associated with
+ * the Scsi_Cmnd structures, and clean up any internal
+ * NCR lists that the commands were on. If issue is set,
+ * also return commands in the issue queue.
+ *
+ * Returns : linked list of commands
+ *
+ * NOTE : the caller should insure that the NCR chip is halted
+ * if the free flag is set.
+ */
+
+static Scsi_Cmnd *
+return_outstanding_commands (struct Scsi_Host *host, int free, int issue) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ struct NCR53c7x0_cmd *c;
+ int i;
+ u32 *ncrcurrent;
+ Scsi_Cmnd *list = NULL, *tmp;
+ for (c = (struct NCR53c7x0_cmd *) hostdata->running_list; c;
+ c = (struct NCR53c7x0_cmd *) c->next) {
+ if (c->cmd->SCp.buffer) {
+ printk ("scsi%d : loop detected in running list!\n", host->host_no);
+ break;
+ } else {
+ printk ("Duh? Bad things happening in the NCR driver\n");
+ break;
+ }
+
+ c->cmd->SCp.buffer = (struct scatterlist *) list;
+ list = c->cmd;
+ if (free) {
+ c->next = hostdata->free;
+ hostdata->free = c;
+ }
+ }
+
+ if (free) {
+ for (i = 0, ncrcurrent = (u32 *) hostdata->schedule;
+ i < host->can_queue; ++i, ncrcurrent += 2) {
+ ncrcurrent[0] = hostdata->NOP_insn;
+ ncrcurrent[1] = 0xdeadbeef;
+ }
+ hostdata->ncrcurrent = NULL;
+ }
+
+ if (issue) {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; tmp = tmp->next) {
+ if (tmp->SCp.buffer) {
+ printk ("scsi%d : loop detected in issue queue!\n",
+ host->host_no);
+ break;
+ }
+ tmp->SCp.buffer = (struct scatterlist *) list;
+ list = tmp;
+ }
+ if (free)
+ hostdata->issue_queue = NULL;
+
+ }
+ return list;
+}
+
+/*
+ * Function : static int disable (struct Scsi_Host *host)
+ *
+ * Purpose : disables the given NCR host, causing all commands
+ * to return a driver error. Call this so we can unload the
+ * module during development and try again. Eventually,
+ * we should be able to find clean workarounds for these
+ * problems.
+ *
+ * Inputs : host - hostadapter to twiddle
+ *
+ * Returns : 0 on success.
+ */
+
+static int
+disable (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ unsigned long flags;
+ Scsi_Cmnd *nuke_list, *tmp;
+ local_irq_save(flags);
+ if (hostdata->state != STATE_HALTED)
+ ncr_halt (host);
+ nuke_list = return_outstanding_commands (host, 1 /* free */, 1 /* issue */);
+ hard_reset (host);
+ hostdata->state = STATE_DISABLED;
+ local_irq_restore(flags);
+ printk ("scsi%d : nuking commands\n", host->host_no);
+ for (; nuke_list; nuke_list = tmp) {
+ tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer;
+ nuke_list->result = DID_ERROR << 16;
+ nuke_list->scsi_done(nuke_list);
+ }
+ printk ("scsi%d : done. \n", host->host_no);
+ printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n",
+ host->host_no);
+ return 0;
+}
+
+/*
+ * Function : static int ncr_halt (struct Scsi_Host *host)
+ *
+ * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip
+ *
+ * Inputs : host - SCSI chip to halt
+ *
+ * Returns : 0 on success
+ */
+
+static int
+ncr_halt (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ unsigned char istat, tmp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ int stage;
+ NCR53c7x0_local_setup(host);
+
+ local_irq_save(flags);
+ /* Stage 0 : eat all interrupts
+ Stage 1 : set ABORT
+ Stage 2 : eat all but abort interrupts
+ Stage 3 : eat all interrupts
+ */
+ for (stage = 0;;) {
+ if (stage == 1) {
+ NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT);
+ ++stage;
+ }
+ istat = NCR53c7x0_read8 (hostdata->istat);
+ if (istat & ISTAT_SIP) {
+ tmp = NCR53c7x0_read8(SSTAT0_REG);
+ } else if (istat & ISTAT_DIP) {
+ tmp = NCR53c7x0_read8(DSTAT_REG);
+ if (stage == 2) {
+ if (tmp & DSTAT_ABRT) {
+ NCR53c7x0_write8(hostdata->istat, 0);
+ ++stage;
+ } else {
+ printk(KERN_ALERT "scsi%d : could not halt NCR chip\n",
+ host->host_no);
+ disable (host);
+ }
+ }
+ }
+ if (!(istat & (ISTAT_SIP|ISTAT_DIP))) {
+ if (stage == 0)
+ ++stage;
+ else if (stage == 3)
+ break;
+ }
+ }
+ hostdata->state = STATE_HALTED;
+ local_irq_restore(flags);
+#if 0
+ print_lots (host);
+#endif
+ return 0;
+}
+
+/*
+ * Function: event_name (int event)
+ *
+ * Purpose: map event enum into user-readable strings.
+ */
+
+static const char *
+event_name (int event) {
+ switch (event) {
+ case EVENT_NONE: return "none";
+ case EVENT_ISSUE_QUEUE: return "to issue queue";
+ case EVENT_START_QUEUE: return "to start queue";
+ case EVENT_SELECT: return "selected";
+ case EVENT_DISCONNECT: return "disconnected";
+ case EVENT_RESELECT: return "reselected";
+ case EVENT_COMPLETE: return "completed";
+ case EVENT_IDLE: return "idle";
+ case EVENT_SELECT_FAILED: return "select failed";
+ case EVENT_BEFORE_SELECT: return "before select";
+ case EVENT_RESELECT_FAILED: return "reselect failed";
+ default: return "unknown";
+ }
+}
+
+/*
+ * Function : void dump_events (struct Scsi_Host *host, count)
+ *
+ * Purpose : print last count events which have occurred.
+ */
+static void
+dump_events (struct Scsi_Host *host, int count) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata[0];
+ struct NCR53c7x0_event event;
+ int i;
+ unsigned long flags;
+ if (hostdata->events) {
+ if (count > hostdata->event_size)
+ count = hostdata->event_size;
+ for (i = hostdata->event_index; count > 0;
+ i = (i ? i - 1 : hostdata->event_size -1), --count) {
+/*
+ * By copying the event we're currently examining with interrupts
+ * disabled, we can do multiple printk(), etc. operations and
+ * still be guaranteed that they're happening on the same
+ * event structure.
+ */
+ local_irq_save(flags);
+#if 0
+ event = hostdata->events[i];
+#else
+ memcpy ((void *) &event, (void *) &(hostdata->events[i]),
+ sizeof(event));
+#endif
+
+ local_irq_restore(flags);
+ printk ("scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d\n",
+ host->host_no, event_name (event.event), count,
+ (long) event.time.tv_sec, (long) event.time.tv_usec,
+ event.target, event.lun);
+ if (event.dsa)
+ printk (" event for dsa 0x%lx (virt 0x%p)\n",
+ virt_to_bus(event.dsa), event.dsa);
+ if (event.pid != -1) {
+ printk (" event for pid %ld ", event.pid);
+ print_command (event.cmnd);
+ }
+ }
+ }
+}
+
+/*
+ * Function: check_address
+ *
+ * Purpose: Check to see if a possibly corrupt pointer will fault the
+ * kernel.
+ *
+ * Inputs: addr - address; size - size of area
+ *
+ * Returns: 0 if area is OK, -1 on error.
+ *
+ * NOTES: should be implemented in terms of vverify on kernels
+ * that have it.
+ */
+
+static int
+check_address (unsigned long addr, int size) {
+ return (virt_to_phys((void *)addr) < PAGE_SIZE || virt_to_phys((void *)(addr + size)) > virt_to_phys(high_memory) ? -1 : 0);
+}
+
+#ifdef MODULE
+int
+NCR53c7x0_release(struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata[0];
+ struct NCR53c7x0_cmd *cmd, *tmp;
+ shutdown (host);
+ if (host->irq != SCSI_IRQ_NONE)
+ {
+ int irq_count;
+ struct Scsi_Host *tmp;
+ for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next)
+ if (tmp->hostt == the_template && tmp->irq == host->irq)
+ ++irq_count;
+ if (irq_count == 1)
+ free_irq(host->irq, NULL);
+ }
+ if (host->dma_channel != DMA_NONE)
+ free_dma(host->dma_channel);
+ if (host->io_port)
+ release_region(host->io_port, host->n_io_port);
+
+ for (cmd = (struct NCR53c7x0_cmd *) hostdata->free; cmd; cmd = tmp,
+ --hostdata->num_cmds) {
+ tmp = (struct NCR53c7x0_cmd *) cmd->next;
+ /*
+ * If we're going to loop, try to stop it to get a more accurate
+ * count of the leaked commands.
+ */
+ cmd->next = NULL;
+ if (cmd->free)
+ cmd->free ((void *) cmd->real, cmd->size);
+ }
+ if (hostdata->num_cmds)
+ printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n",
+ host->host_no, hostdata->num_cmds);
+ if (hostdata->events)
+ vfree ((void *)hostdata->events);
+
+ /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, which
+ * XXX may be invalid (CONFIG_060_WRITETHROUGH)
+ */
+ kernel_set_cachemode((void *)hostdata, 8192, IOMAP_FULL_CACHING);
+ free_pages ((u32)hostdata, 1);
+ return 1;
+}
+#endif /* def MODULE */
diff --git a/drivers/scsi/53c7xx.h b/drivers/scsi/53c7xx.h
new file mode 100644
index 000000000000..d9098bdace05
--- /dev/null
+++ b/drivers/scsi/53c7xx.h
@@ -0,0 +1,1608 @@
+/*
+ * 53c710 driver. Modified from Drew Eckhardts driver
+ * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
+ *
+ * I have left the code for the 53c8xx family in here, because it didn't
+ * seem worth removing it. The possibility of IO_MAPPED chips rather
+ * than MEMORY_MAPPED remains, in case someone wants to add support for
+ * 53c710 chips on Intel PCs (some older machines have them on the
+ * motherboard).
+ *
+ * NOTE THERE MAY BE PROBLEMS WITH CASTS IN read8 AND Co.
+ */
+
+/*
+ * NCR 53c{7,8}0x0 driver, header file
+ *
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@PoohSticks.ORG
+ * +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ *
+ * PRE-ALPHA
+ *
+ * For more information, please consult
+ *
+ * NCR 53C700/53C700-66
+ * SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * +1 (719) 578-3400
+ *
+ * Toll free literature number
+ * +1 (800) 334-5454
+ *
+ */
+
+#ifndef NCR53c710_H
+#define NCR53c710_H
+
+#ifndef HOSTS_C
+
+/* SCSI control 0 rw, default = 0xc0 */
+#define SCNTL0_REG 0x00
+#define SCNTL0_ARB1 0x80 /* 0 0 = simple arbitration */
+#define SCNTL0_ARB2 0x40 /* 1 1 = full arbitration */
+#define SCNTL0_STRT 0x20 /* Start Sequence */
+#define SCNTL0_WATN 0x10 /* Select with ATN */
+#define SCNTL0_EPC 0x08 /* Enable parity checking */
+/* Bit 2 is reserved on 800 series chips */
+#define SCNTL0_EPG_700 0x04 /* Enable parity generation */
+#define SCNTL0_AAP 0x02 /* ATN/ on parity error */
+#define SCNTL0_TRG 0x01 /* Target mode */
+
+/* SCSI control 1 rw, default = 0x00 */
+
+#define SCNTL1_REG 0x01
+#define SCNTL1_EXC 0x80 /* Extra Clock Cycle of Data setup */
+#define SCNTL1_ADB 0x40 /* contents of SODL on bus */
+#define SCNTL1_ESR_700 0x20 /* Enable SIOP response to selection
+ and reselection */
+#define SCNTL1_DHP_800 0x20 /* Disable halt on parity error or ATN
+ target mode only */
+#define SCNTL1_CON 0x10 /* Connected */
+#define SCNTL1_RST 0x08 /* SCSI RST/ */
+#define SCNTL1_AESP 0x04 /* Force bad parity */
+#define SCNTL1_SND_700 0x02 /* Start SCSI send */
+#define SCNTL1_IARB_800 0x02 /* Immediate Arbitration, start
+ arbitration immediately after
+ busfree is detected */
+#define SCNTL1_RCV_700 0x01 /* Start SCSI receive */
+#define SCNTL1_SST_800 0x01 /* Start SCSI transfer */
+
+/* SCSI control 2 rw, */
+
+#define SCNTL2_REG_800 0x02
+#define SCNTL2_800_SDU 0x80 /* SCSI disconnect unexpected */
+
+/* SCSI control 3 rw */
+
+#define SCNTL3_REG_800 0x03
+#define SCNTL3_800_SCF_SHIFT 4
+#define SCNTL3_800_SCF_MASK 0x70
+#define SCNTL3_800_SCF2 0x40 /* Synchronous divisor */
+#define SCNTL3_800_SCF1 0x20 /* 0x00 = SCLK/3 */
+#define SCNTL3_800_SCF0 0x10 /* 0x10 = SCLK/1 */
+ /* 0x20 = SCLK/1.5
+ 0x30 = SCLK/2
+ 0x40 = SCLK/3 */
+
+#define SCNTL3_800_CCF_SHIFT 0
+#define SCNTL3_800_CCF_MASK 0x07
+#define SCNTL3_800_CCF2 0x04 /* 0x00 50.01 to 66 */
+#define SCNTL3_800_CCF1 0x02 /* 0x01 16.67 to 25 */
+#define SCNTL3_800_CCF0 0x01 /* 0x02 25.01 - 37.5
+ 0x03 37.51 - 50
+ 0x04 50.01 - 66 */
+
+/*
+ * SCSI destination ID rw - the appropriate bit is set for the selected
+ * target ID. This is written by the SCSI SCRIPTS processor.
+ * default = 0x00
+ */
+#define SDID_REG_700 0x02
+#define SDID_REG_800 0x06
+
+#define GP_REG_800 0x07 /* General purpose IO */
+#define GP_800_IO1 0x02
+#define GP_800_IO2 0x01
+
+/* SCSI interrupt enable rw, default = 0x00 */
+#define SIEN_REG_700 0x03
+#define SIEN0_REG_800 0x40
+#define SIEN_MA 0x80 /* Phase mismatch (ini) or ATN (tgt) */
+#define SIEN_FC 0x40 /* Function complete */
+#define SIEN_700_STO 0x20 /* Selection or reselection timeout */
+#define SIEN_800_SEL 0x20 /* Selected */
+#define SIEN_700_SEL 0x10 /* Selected or reselected */
+#define SIEN_800_RESEL 0x10 /* Reselected */
+#define SIEN_SGE 0x08 /* SCSI gross error */
+#define SIEN_UDC 0x04 /* Unexpected disconnect */
+#define SIEN_RST 0x02 /* SCSI RST/ received */
+#define SIEN_PAR 0x01 /* Parity error */
+
+/*
+ * SCSI chip ID rw
+ * NCR53c700 :
+ * When arbitrating, the highest bit is used, when reselection or selection
+ * occurs, the chip responds to all IDs for which a bit is set.
+ * default = 0x00
+ * NCR53c810 :
+ * Uses bit mapping
+ */
+#define SCID_REG 0x04
+/* Bit 7 is reserved on 800 series chips */
+#define SCID_800_RRE 0x40 /* Enable response to reselection */
+#define SCID_800_SRE 0x20 /* Enable response to selection */
+/* Bits four and three are reserved on 800 series chips */
+#define SCID_800_ENC_MASK 0x07 /* Encoded SCSI ID */
+
+/* SCSI transfer rw, default = 0x00 */
+#define SXFER_REG 0x05
+#define SXFER_DHP 0x80 /* Disable halt on parity */
+
+#define SXFER_TP2 0x40 /* Transfer period msb */
+#define SXFER_TP1 0x20
+#define SXFER_TP0 0x10 /* lsb */
+#define SXFER_TP_MASK 0x70
+/* FIXME : SXFER_TP_SHIFT == 5 is right for '8xx chips */
+#define SXFER_TP_SHIFT 5
+#define SXFER_TP_4 0x00 /* Divisors */
+#define SXFER_TP_5 0x10<<1
+#define SXFER_TP_6 0x20<<1
+#define SXFER_TP_7 0x30<<1
+#define SXFER_TP_8 0x40<<1
+#define SXFER_TP_9 0x50<<1
+#define SXFER_TP_10 0x60<<1
+#define SXFER_TP_11 0x70<<1
+
+#define SXFER_MO3 0x08 /* Max offset msb */
+#define SXFER_MO2 0x04
+#define SXFER_MO1 0x02
+#define SXFER_MO0 0x01 /* lsb */
+#define SXFER_MO_MASK 0x0f
+#define SXFER_MO_SHIFT 0
+
+/*
+ * SCSI output data latch rw
+ * The contents of this register are driven onto the SCSI bus when
+ * the Assert Data Bus bit of the SCNTL1 register is set and
+ * the CD, IO, and MSG bits of the SOCL register match the SCSI phase
+ */
+#define SODL_REG_700 0x06
+#define SODL_REG_800 0x54
+
+
+/*
+ * SCSI output control latch rw, default = 0
+ * Note that when the chip is being manually programmed as an initiator,
+ * the MSG, CD, and IO bits must be set correctly for the phase the target
+ * is driving the bus in. Otherwise no data transfer will occur due to
+ * phase mismatch.
+ */
+
+#define SOCL_REG 0x07
+#define SOCL_REQ 0x80 /* REQ */
+#define SOCL_ACK 0x40 /* ACK */
+#define SOCL_BSY 0x20 /* BSY */
+#define SOCL_SEL 0x10 /* SEL */
+#define SOCL_ATN 0x08 /* ATN */
+#define SOCL_MSG 0x04 /* MSG */
+#define SOCL_CD 0x02 /* C/D */
+#define SOCL_IO 0x01 /* I/O */
+
+/*
+ * SCSI first byte received latch ro
+ * This register contains the first byte received during a block MOVE
+ * SCSI SCRIPTS instruction, including
+ *
+ * Initiator mode Target mode
+ * Message in Command
+ * Status Message out
+ * Data in Data out
+ *
+ * It also contains the selecting or reselecting device's ID and our
+ * ID.
+ *
+ * Note that this is the register the various IF conditionals can
+ * operate on.
+ */
+#define SFBR_REG 0x08
+
+/*
+ * SCSI input data latch ro
+ * In initiator mode, data is latched into this register on the rising
+ * edge of REQ/. In target mode, data is latched on the rising edge of
+ * ACK/
+ */
+#define SIDL_REG_700 0x09
+#define SIDL_REG_800 0x50
+
+/*
+ * SCSI bus data lines ro
+ * This register reflects the instantaneous status of the SCSI data
+ * lines. Note that SCNTL0 must be set to disable parity checking,
+ * otherwise reading this register will latch new parity.
+ */
+#define SBDL_REG_700 0x0a
+#define SBDL_REG_800 0x58
+
+#define SSID_REG_800 0x0a
+#define SSID_800_VAL 0x80 /* Exactly two bits asserted at sel */
+#define SSID_800_ENCID_MASK 0x07 /* Device which performed operation */
+
+
+/*
+ * SCSI bus control lines rw,
+ * instantaneous readout of control lines
+ */
+#define SBCL_REG 0x0b
+#define SBCL_REQ 0x80 /* REQ ro */
+#define SBCL_ACK 0x40 /* ACK ro */
+#define SBCL_BSY 0x20 /* BSY ro */
+#define SBCL_SEL 0x10 /* SEL ro */
+#define SBCL_ATN 0x08 /* ATN ro */
+#define SBCL_MSG 0x04 /* MSG ro */
+#define SBCL_CD 0x02 /* C/D ro */
+#define SBCL_IO 0x01 /* I/O ro */
+#define SBCL_PHASE_CMDOUT SBCL_CD
+#define SBCL_PHASE_DATAIN SBCL_IO
+#define SBCL_PHASE_DATAOUT 0
+#define SBCL_PHASE_MSGIN (SBCL_CD|SBCL_IO|SBCL_MSG)
+#define SBCL_PHASE_MSGOUT (SBCL_CD|SBCL_MSG)
+#define SBCL_PHASE_STATIN (SBCL_CD|SBCL_IO)
+#define SBCL_PHASE_MASK (SBCL_CD|SBCL_IO|SBCL_MSG)
+/*
+ * Synchronous SCSI Clock Control bits
+ * 0 - set by DCNTL
+ * 1 - SCLK / 1.0
+ * 2 - SCLK / 1.5
+ * 3 - SCLK / 2.0
+ */
+#define SBCL_SSCF1 0x02 /* wo, -66 only */
+#define SBCL_SSCF0 0x01 /* wo, -66 only */
+#define SBCL_SSCF_MASK 0x03
+
+/*
+ * XXX note : when reading the DSTAT and STAT registers to clear interrupts,
+ * insure that 10 clocks elapse between the two
+ */
+/* DMA status ro */
+#define DSTAT_REG 0x0c
+#define DSTAT_DFE 0x80 /* DMA FIFO empty */
+#define DSTAT_800_MDPE 0x40 /* Master Data Parity Error */
+#define DSTAT_800_BF 0x20 /* Bus Fault */
+#define DSTAT_ABRT 0x10 /* Aborted - set on error */
+#define DSTAT_SSI 0x08 /* SCRIPTS single step interrupt */
+#define DSTAT_SIR 0x04 /* SCRIPTS interrupt received -
+ set when INT instruction is
+ executed */
+#define DSTAT_WTD 0x02 /* Watchdog timeout detected */
+#define DSTAT_OPC 0x01 /* Illegal instruction */
+#define DSTAT_800_IID 0x01 /* Same thing, different name */
+
+
+/* NCR53c800 moves this stuff into SIST0 */
+#define SSTAT0_REG 0x0d /* SCSI status 0 ro */
+#define SIST0_REG_800 0x42
+#define SSTAT0_MA 0x80 /* ini : phase mismatch,
+ * tgt : ATN/ asserted
+ */
+#define SSTAT0_CMP 0x40 /* function complete */
+#define SSTAT0_700_STO 0x20 /* Selection or reselection timeout */
+#define SIST0_800_SEL 0x20 /* Selected */
+#define SSTAT0_700_SEL 0x10 /* Selected or reselected */
+#define SIST0_800_RSL 0x10 /* Reselected */
+#define SSTAT0_SGE 0x08 /* SCSI gross error */
+#define SSTAT0_UDC 0x04 /* Unexpected disconnect */
+#define SSTAT0_RST 0x02 /* SCSI RST/ received */
+#define SSTAT0_PAR 0x01 /* Parity error */
+
+/* And uses SSTAT0 for what was SSTAT1 */
+
+#define SSTAT1_REG 0x0e /* SCSI status 1 ro */
+#define SSTAT1_ILF 0x80 /* SIDL full */
+#define SSTAT1_ORF 0x40 /* SODR full */
+#define SSTAT1_OLF 0x20 /* SODL full */
+#define SSTAT1_AIP 0x10 /* Arbitration in progress */
+#define SSTAT1_LOA 0x08 /* Lost arbitration */
+#define SSTAT1_WOA 0x04 /* Won arbitration */
+#define SSTAT1_RST 0x02 /* Instant readout of RST/ */
+#define SSTAT1_SDP 0x01 /* Instant readout of SDP/ */
+
+#define SSTAT2_REG 0x0f /* SCSI status 2 ro */
+#define SSTAT2_FF3 0x80 /* number of bytes in synchronous */
+#define SSTAT2_FF2 0x40 /* data FIFO */
+#define SSTAT2_FF1 0x20
+#define SSTAT2_FF0 0x10
+#define SSTAT2_FF_MASK 0xf0
+#define SSTAT2_FF_SHIFT 4
+
+/*
+ * Latched signals, latched on the leading edge of REQ/ for initiators,
+ * ACK/ for targets.
+ */
+#define SSTAT2_SDP 0x08 /* SDP */
+#define SSTAT2_MSG 0x04 /* MSG */
+#define SSTAT2_CD 0x02 /* C/D */
+#define SSTAT2_IO 0x01 /* I/O */
+#define SSTAT2_PHASE_CMDOUT SSTAT2_CD
+#define SSTAT2_PHASE_DATAIN SSTAT2_IO
+#define SSTAT2_PHASE_DATAOUT 0
+#define SSTAT2_PHASE_MSGIN (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG)
+#define SSTAT2_PHASE_MSGOUT (SSTAT2_CD|SSTAT2_MSG)
+#define SSTAT2_PHASE_STATIN (SSTAT2_CD|SSTAT2_IO)
+#define SSTAT2_PHASE_MASK (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHA_REG_00 0x10 /* through 0x13 Scratch A rw */
+/* NCR53c710 and higher */
+#define DSA_REG 0x10 /* DATA structure address */
+
+#define CTEST0_REG_700 0x14 /* Chip test 0 ro */
+#define CTEST0_REG_800 0x18 /* Chip test 0 rw, general purpose */
+/* 0x80 - 0x04 are reserved */
+#define CTEST0_700_RTRG 0x02 /* Real target mode */
+#define CTEST0_700_DDIR 0x01 /* Data direction, 1 =
+ * SCSI bus to host, 0 =
+ * host to SCSI.
+ */
+
+#define CTEST1_REG_700 0x15 /* Chip test 1 ro */
+#define CTEST1_REG_800 0x19 /* Chip test 1 ro */
+#define CTEST1_FMT3 0x80 /* Identify which byte lanes are empty */
+#define CTEST1_FMT2 0x40 /* in the DMA FIFO */
+#define CTEST1_FMT1 0x20
+#define CTEST1_FMT0 0x10
+
+#define CTEST1_FFL3 0x08 /* Identify which bytes lanes are full */
+#define CTEST1_FFL2 0x04 /* in the DMA FIFO */
+#define CTEST1_FFL1 0x02
+#define CTEST1_FFL0 0x01
+
+#define CTEST2_REG_700 0x16 /* Chip test 2 ro */
+#define CTEST2_REG_800 0x1a /* Chip test 2 ro */
+
+#define CTEST2_800_DDIR 0x80 /* 1 = SCSI->host */
+#define CTEST2_800_SIGP 0x40 /* A copy of SIGP in ISTAT.
+ Reading this register clears */
+#define CTEST2_800_CIO 0x20 /* Configured as IO */.
+#define CTEST2_800_CM 0x10 /* Configured as memory */
+
+/* 0x80 - 0x40 are reserved on 700 series chips */
+#define CTEST2_700_SOFF 0x20 /* SCSI Offset Compare,
+ * As an initiator, this bit is
+ * one when the synchronous offset
+ * is zero, as a target this bit
+ * is one when the synchronous
+ * offset is at the maximum
+ * defined in SXFER
+ */
+#define CTEST2_700_SFP 0x10 /* SCSI FIFO parity bit,
+ * reading CTEST3 unloads a byte
+ * from the FIFO and sets this
+ */
+#define CTEST2_700_DFP 0x08 /* DMA FIFO parity bit,
+ * reading CTEST6 unloads a byte
+ * from the FIFO and sets this
+ */
+#define CTEST2_TEOP 0x04 /* SCSI true end of process,
+ * indicates a totally finished
+ * transfer
+ */
+#define CTEST2_DREQ 0x02 /* Data request signal */
+/* 0x01 is reserved on 700 series chips */
+#define CTEST2_800_DACK 0x01
+
+/*
+ * Chip test 3 ro
+ * Unloads the bottom byte of the eight deep SCSI synchronous FIFO,
+ * check SSTAT2 FIFO full bits to determine size. Note that a GROSS
+ * error results if a read is attempted on this register. Also note
+ * that 16 and 32 bit reads of this register will cause corruption.
+ */
+#define CTEST3_REG_700 0x17
+/* Chip test 3 rw */
+#define CTEST3_REG_800 0x1b
+#define CTEST3_800_V3 0x80 /* Chip revision */
+#define CTEST3_800_V2 0x40
+#define CTEST3_800_V1 0x20
+#define CTEST3_800_V0 0x10
+#define CTEST3_800_FLF 0x08 /* Flush DMA FIFO */
+#define CTEST3_800_CLF 0x04 /* Clear DMA FIFO */
+#define CTEST3_800_FM 0x02 /* Fetch mode pin */
+/* bit 0 is reserved on 800 series chips */
+
+#define CTEST4_REG_700 0x18 /* Chip test 4 rw */
+#define CTEST4_REG_800 0x21 /* Chip test 4 rw */
+/* 0x80 is reserved on 700 series chips */
+#define CTEST4_800_BDIS 0x80 /* Burst mode disable */
+#define CTEST4_ZMOD 0x40 /* High impedance mode */
+#define CTEST4_SZM 0x20 /* SCSI bus high impedance */
+#define CTEST4_700_SLBE 0x10 /* SCSI loopback enabled */
+#define CTEST4_800_SRTM 0x10 /* Shadow Register Test Mode */
+#define CTEST4_700_SFWR 0x08 /* SCSI FIFO write enable,
+ * redirects writes from SODL
+ * to the SCSI FIFO.
+ */
+#define CTEST4_800_MPEE 0x08 /* Enable parity checking
+ during master cycles on PCI
+ bus */
+
+/*
+ * These bits send the contents of the CTEST6 register to the appropriate
+ * byte lane of the 32 bit DMA FIFO. Normal operation is zero, otherwise
+ * the high bit means the low two bits select the byte lane.
+ */
+#define CTEST4_FBL2 0x04
+#define CTEST4_FBL1 0x02
+#define CTEST4_FBL0 0x01
+#define CTEST4_FBL_MASK 0x07
+#define CTEST4_FBL_0 0x04 /* Select DMA FIFO byte lane 0 */
+#define CTEST4_FBL_1 0x05 /* Select DMA FIFO byte lane 1 */
+#define CTEST4_FBL_2 0x06 /* Select DMA FIFO byte lane 2 */
+#define CTEST4_FBL_3 0x07 /* Select DMA FIFO byte lane 3 */
+#define CTEST4_800_SAVE (CTEST4_800_BDIS)
+
+
+#define CTEST5_REG_700 0x19 /* Chip test 5 rw */
+#define CTEST5_REG_800 0x22 /* Chip test 5 rw */
+/*
+ * Clock Address Incrementor. When set, it increments the
+ * DNAD register to the next bus size boundary. It automatically
+ * resets itself when the operation is complete.
+ */
+#define CTEST5_ADCK 0x80
+/*
+ * Clock Byte Counter. When set, it decrements the DBC register to
+ * the next bus size boundary.
+ */
+#define CTEST5_BBCK 0x40
+/*
+ * Reset SCSI Offset. Setting this bit to 1 clears the current offset
+ * pointer in the SCSI synchronous offset counter (SSTAT). This bit
+ * is set to 1 if a SCSI Gross Error Condition occurs. The offset should
+ * be cleared when a synchronous transfer fails. When written, it is
+ * automatically cleared after the SCSI synchronous offset counter is
+ * reset.
+ */
+/* Bit 5 is reserved on 800 series chips */
+#define CTEST5_700_ROFF 0x20
+/*
+ * Master Control for Set or Reset pulses. When 1, causes the low
+ * four bits of register to set when set, 0 causes the low bits to
+ * clear when set.
+ */
+#define CTEST5_MASR 0x10
+#define CTEST5_DDIR 0x08 /* DMA direction */
+/*
+ * Bits 2-0 are reserved on 800 series chips
+ */
+#define CTEST5_700_EOP 0x04 /* End of process */
+#define CTEST5_700_DREQ 0x02 /* Data request */
+#define CTEST5_700_DACK 0x01 /* Data acknowledge */
+
+/*
+ * Chip test 6 rw - writing to this register writes to the byte
+ * lane in the DMA FIFO as determined by the FBL bits in the CTEST4
+ * register.
+ */
+#define CTEST6_REG_700 0x1a
+#define CTEST6_REG_800 0x23
+
+#define CTEST7_REG 0x1b /* Chip test 7 rw */
+/* 0x80 - 0x40 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define CTEST7_10_CDIS 0x80 /* Cache burst disable */
+#define CTEST7_10_SC1 0x40 /* Snoop control bits */
+#define CTEST7_10_SC0 0x20
+#define CTEST7_10_SC_MASK 0x60
+/* 0x20 is reserved on the NCR53c700 */
+#define CTEST7_0060_FM 0x20 /* Fetch mode */
+#define CTEST7_STD 0x10 /* Selection timeout disable */
+#define CTEST7_DFP 0x08 /* DMA FIFO parity bit for CTEST6 */
+#define CTEST7_EVP 0x04 /* 1 = host bus even parity, 0 = odd */
+#define CTEST7_10_TT1 0x02 /* Transfer type */
+#define CTEST7_00_DC 0x02 /* Set to drive DC low during instruction
+ fetch */
+#define CTEST7_DIFF 0x01 /* Differential mode */
+
+#define CTEST7_SAVE ( CTEST7_EVP | CTEST7_DIFF )
+
+
+#define TEMP_REG 0x1c /* through 0x1f Temporary stack rw */
+
+#define DFIFO_REG 0x20 /* DMA FIFO rw */
+/*
+ * 0x80 is reserved on the NCR53c710, the CLF and FLF bits have been
+ * moved into the CTEST8 register.
+ */
+#define DFIFO_00_FLF 0x80 /* Flush DMA FIFO to memory */
+#define DFIFO_00_CLF 0x40 /* Clear DMA and SCSI FIFOs */
+#define DFIFO_BO6 0x40
+#define DFIFO_BO5 0x20
+#define DFIFO_BO4 0x10
+#define DFIFO_BO3 0x08
+#define DFIFO_BO2 0x04
+#define DFIFO_BO1 0x02
+#define DFIFO_BO0 0x01
+#define DFIFO_10_BO_MASK 0x7f /* 7 bit counter */
+#define DFIFO_00_BO_MASK 0x3f /* 6 bit counter */
+
+/*
+ * Interrupt status rw
+ * Note that this is the only register which can be read while SCSI
+ * SCRIPTS are being executed.
+ */
+#define ISTAT_REG_700 0x21
+#define ISTAT_REG_800 0x14
+#define ISTAT_ABRT 0x80 /* Software abort, write
+ *1 to abort, wait for interrupt. */
+/* 0x40 and 0x20 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define ISTAT_10_SRST 0x40 /* software reset */
+#define ISTAT_10_SIGP 0x20 /* signal script */
+/* 0x10 is reserved on NCR53c700 series chips */
+#define ISTAT_800_SEM 0x10 /* semaphore */
+#define ISTAT_CON 0x08 /* 1 when connected */
+#define ISTAT_800_INTF 0x04 /* Interrupt on the fly */
+#define ISTAT_700_PRE 0x04 /* Pointer register empty.
+ * Set to 1 when DSPS and DSP
+ * registers are empty in pipeline
+ * mode, always set otherwise.
+ */
+#define ISTAT_SIP 0x02 /* SCSI interrupt pending from
+ * SCSI portion of SIOP see
+ * SSTAT0
+ */
+#define ISTAT_DIP 0x01 /* DMA interrupt pending
+ * see DSTAT
+ */
+
+/* NCR53c700-66 and NCR53c710 only */
+#define CTEST8_REG 0x22 /* Chip test 8 rw */
+#define CTEST8_0066_EAS 0x80 /* Enable alternate SCSI clock,
+ * ie read from SCLK/ rather than CLK/
+ */
+#define CTEST8_0066_EFM 0x40 /* Enable fetch and master outputs */
+#define CTEST8_0066_GRP 0x20 /* Generate Receive Parity for
+ * pass through. This insures that
+ * bad parity won't reach the host
+ * bus.
+ */
+#define CTEST8_0066_TE 0x10 /* TolerANT enable. Enable
+ * active negation, should only
+ * be used for slow SCSI
+ * non-differential.
+ */
+#define CTEST8_0066_HSC 0x08 /* Halt SCSI clock */
+#define CTEST8_0066_SRA 0x04 /* Shorten REQ/ACK filtering,
+ * must be set for fast SCSI-II
+ * speeds.
+ */
+#define CTEST8_0066_DAS 0x02 /* Disable automatic target/initiator
+ * switching.
+ */
+#define CTEST8_0066_LDE 0x01 /* Last disconnect enable.
+ * The status of pending
+ * disconnect is maintained by
+ * the core, eliminating
+ * the possibility of missing a
+ * selection or reselection
+ * while waiting to fetch a
+ * WAIT DISCONNECT opcode.
+ */
+
+#define CTEST8_10_V3 0x80 /* Chip revision */
+#define CTEST8_10_V2 0x40
+#define CTEST8_10_V1 0x20
+#define CTEST8_10_V0 0x10
+#define CTEST8_10_V_MASK 0xf0
+#define CTEST8_10_FLF 0x08 /* Flush FIFOs */
+#define CTEST8_10_CLF 0x04 /* Clear FIFOs */
+#define CTEST8_10_FM 0x02 /* Fetch pin mode */
+#define CTEST8_10_SM 0x01 /* Snoop pin mode */
+
+
+/*
+ * The CTEST9 register may be used to differentiate between a
+ * NCR53c700 and a NCR53c710.
+ *
+ * Write 0xff to this register.
+ * Read it.
+ * If the contents are 0xff, it is a NCR53c700
+ * If the contents are 0x00, it is a NCR53c700-66 first revision
+ * If the contents are some other value, it is some other NCR53c700-66
+ */
+#define CTEST9_REG_00 0x23 /* Chip test 9 ro */
+#define LCRC_REG_10 0x23
+
+/*
+ * 0x24 through 0x27 are the DMA byte counter register. Instructions
+ * write their high 8 bits into the DCMD register, the low 24 bits into
+ * the DBC register.
+ *
+ * Function is dependent on the command type being executed.
+ */
+
+
+#define DBC_REG 0x24
+/*
+ * For Block Move Instructions, DBC is a 24 bit quantity representing
+ * the number of bytes to transfer.
+ * For Transfer Control Instructions, DBC is bit fielded as follows :
+ */
+/* Bits 20 - 23 should be clear */
+#define DBC_TCI_TRUE (1 << 19) /* Jump when true */
+#define DBC_TCI_COMPARE_DATA (1 << 18) /* Compare data */
+#define DBC_TCI_COMPARE_PHASE (1 << 17) /* Compare phase with DCMD field */
+#define DBC_TCI_WAIT_FOR_VALID (1 << 16) /* Wait for REQ */
+/* Bits 8 - 15 are reserved on some implementations ? */
+#define DBC_TCI_MASK_MASK 0xff00 /* Mask for data compare */
+#define DBC_TCI_MASK_SHIFT 8
+#define DBC_TCI_DATA_MASK 0xff /* Data to be compared */
+#define DBC_TCI_DATA_SHIFT 0
+
+#define DBC_RWRI_IMMEDIATE_MASK 0xff00 /* Immediate data */
+#define DBC_RWRI_IMMEDIATE_SHIFT 8 /* Amount to shift */
+#define DBC_RWRI_ADDRESS_MASK 0x3f0000 /* Register address */
+#define DBC_RWRI_ADDRESS_SHIFT 16
+
+
+/*
+ * DMA command r/w
+ */
+#define DCMD_REG 0x27
+#define DCMD_TYPE_MASK 0xc0 /* Masks off type */
+#define DCMD_TYPE_BMI 0x00 /* Indicates a Block Move instruction */
+#define DCMD_BMI_IO 0x01 /* I/O, CD, and MSG bits selecting */
+#define DCMD_BMI_CD 0x02 /* the phase for the block MOVE */
+#define DCMD_BMI_MSG 0x04 /* instruction */
+
+#define DCMD_BMI_OP_MASK 0x18 /* mask for opcode */
+#define DCMD_BMI_OP_MOVE_T 0x00 /* MOVE */
+#define DCMD_BMI_OP_MOVE_I 0x08 /* MOVE Initiator */
+
+#define DCMD_BMI_INDIRECT 0x20 /* Indirect addressing */
+
+#define DCMD_TYPE_TCI 0x80 /* Indicates a Transfer Control
+ instruction */
+#define DCMD_TCI_IO 0x01 /* I/O, CD, and MSG bits selecting */
+#define DCMD_TCI_CD 0x02 /* the phase for the block MOVE */
+#define DCMD_TCI_MSG 0x04 /* instruction */
+#define DCMD_TCI_OP_MASK 0x38 /* mask for opcode */
+#define DCMD_TCI_OP_JUMP 0x00 /* JUMP */
+#define DCMD_TCI_OP_CALL 0x08 /* CALL */
+#define DCMD_TCI_OP_RETURN 0x10 /* RETURN */
+#define DCMD_TCI_OP_INT 0x18 /* INT */
+
+#define DCMD_TYPE_RWRI 0x40 /* Indicates I/O or register Read/Write
+ instruction */
+#define DCMD_RWRI_OPC_MASK 0x38 /* Opcode mask */
+#define DCMD_RWRI_OPC_WRITE 0x28 /* Write SFBR to register */
+#define DCMD_RWRI_OPC_READ 0x30 /* Read register to SFBR */
+#define DCMD_RWRI_OPC_MODIFY 0x38 /* Modify in place */
+
+#define DCMD_RWRI_OP_MASK 0x07
+#define DCMD_RWRI_OP_MOVE 0x00
+#define DCMD_RWRI_OP_SHL 0x01
+#define DCMD_RWRI_OP_OR 0x02
+#define DCMD_RWRI_OP_XOR 0x03
+#define DCMD_RWRI_OP_AND 0x04
+#define DCMD_RWRI_OP_SHR 0x05
+#define DCMD_RWRI_OP_ADD 0x06
+#define DCMD_RWRI_OP_ADDC 0x07
+
+#define DCMD_TYPE_MMI 0xc0 /* Indicates a Memory Move instruction
+ (three words) */
+
+
+#define DNAD_REG 0x28 /* through 0x2b DMA next address for
+ data */
+#define DSP_REG 0x2c /* through 0x2f DMA SCRIPTS pointer rw */
+#define DSPS_REG 0x30 /* through 0x33 DMA SCRIPTS pointer
+ save rw */
+#define DMODE_REG_00 0x34 /* DMA mode rw */
+#define DMODE_00_BL1 0x80 /* Burst length bits */
+#define DMODE_00_BL0 0x40
+#define DMODE_BL_MASK 0xc0
+/* Burst lengths (800) */
+#define DMODE_BL_2 0x00 /* 2 transfer */
+#define DMODE_BL_4 0x40 /* 4 transfers */
+#define DMODE_BL_8 0x80 /* 8 transfers */
+#define DMODE_BL_16 0xc0 /* 16 transfers */
+
+#define DMODE_10_BL_1 0x00 /* 1 transfer */
+#define DMODE_10_BL_2 0x40 /* 2 transfers */
+#define DMODE_10_BL_4 0x80 /* 4 transfers */
+#define DMODE_10_BL_8 0xc0 /* 8 transfers */
+#define DMODE_10_FC2 0x20 /* Driven to FC2 pin */
+#define DMODE_10_FC1 0x10 /* Driven to FC1 pin */
+#define DMODE_710_PD 0x08 /* Program/data on FC0 pin */
+#define DMODE_710_UO 0x02 /* User prog. output */
+
+#define DMODE_700_BW16 0x20 /* Host buswidth = 16 */
+#define DMODE_700_286 0x10 /* 286 mode */
+#define DMODE_700_IOM 0x08 /* Transfer to IO port */
+#define DMODE_700_FAM 0x04 /* Fixed address mode */
+#define DMODE_700_PIPE 0x02 /* Pipeline mode disables
+ * automatic fetch / exec
+ */
+#define DMODE_MAN 0x01 /* Manual start mode,
+ * requires a 1 to be written
+ * to the start DMA bit in the DCNTL
+ * register to run scripts
+ */
+
+#define DMODE_700_SAVE ( DMODE_00_BL_MASK | DMODE_00_BW16 | DMODE_00_286 )
+
+/* NCR53c800 series only */
+#define SCRATCHA_REG_800 0x34 /* through 0x37 Scratch A rw */
+/* NCR53c710 only */
+#define SCRATCHB_REG_10 0x34 /* through 0x37 scratch B rw */
+
+#define DMODE_REG_10 0x38 /* DMA mode rw, NCR53c710 and newer */
+#define DMODE_800_SIOM 0x20 /* Source IO = 1 */
+#define DMODE_800_DIOM 0x10 /* Destination IO = 1 */
+#define DMODE_800_ERL 0x08 /* Enable Read Line */
+
+/* 35-38 are reserved on 700 and 700-66 series chips */
+#define DIEN_REG 0x39 /* DMA interrupt enable rw */
+/* 0x80, 0x40, and 0x20 are reserved on 700-series chips */
+#define DIEN_800_MDPE 0x40 /* Master data parity error */
+#define DIEN_800_BF 0x20 /* BUS fault */
+#define DIEN_700_BF 0x20 /* BUS fault */
+#define DIEN_ABRT 0x10 /* Enable aborted interrupt */
+#define DIEN_SSI 0x08 /* Enable single step interrupt */
+#define DIEN_SIR 0x04 /* Enable SCRIPTS INT command
+ * interrupt
+ */
+/* 0x02 is reserved on 800 series chips */
+#define DIEN_700_WTD 0x02 /* Enable watchdog timeout interrupt */
+#define DIEN_700_OPC 0x01 /* Enable illegal instruction
+ * interrupt
+ */
+#define DIEN_800_IID 0x01 /* Same meaning, different name */
+
+/*
+ * DMA watchdog timer rw
+ * set in 16 CLK input periods.
+ */
+#define DWT_REG 0x3a
+
+/* DMA control rw */
+#define DCNTL_REG 0x3b
+#define DCNTL_700_CF1 0x80 /* Clock divisor bits */
+#define DCNTL_700_CF0 0x40
+#define DCNTL_700_CF_MASK 0xc0
+/* Clock divisors Divisor SCLK range (MHZ) */
+#define DCNTL_700_CF_2 0x00 /* 2.0 37.51-50.00 */
+#define DCNTL_700_CF_1_5 0x40 /* 1.5 25.01-37.50 */
+#define DCNTL_700_CF_1 0x80 /* 1.0 16.67-25.00 */
+#define DCNTL_700_CF_3 0xc0 /* 3.0 50.01-66.67 (53c700-66) */
+
+#define DCNTL_700_S16 0x20 /* Load scripts 16 bits at a time */
+#define DCNTL_SSM 0x10 /* Single step mode */
+#define DCNTL_700_LLM 0x08 /* Low level mode, can only be set
+ * after selection */
+#define DCNTL_800_IRQM 0x08 /* Totem pole IRQ pin */
+#define DCNTL_STD 0x04 /* Start DMA / SCRIPTS */
+/* 0x02 is reserved */
+#define DCNTL_00_RST 0x01 /* Software reset, resets everything
+ * but 286 mode bit in DMODE. On the
+ * NCR53c710, this bit moved to CTEST8
+ */
+#define DCNTL_10_COM 0x01 /* 700 software compatibility mode */
+#define DCNTL_10_EA 0x20 /* Enable Ack - needed for MVME16x */
+
+#define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHB_REG_00 0x3c /* through 0x3f scratch b rw */
+#define SCRATCHB_REG_800 0x5c /* through 0x5f scratch b rw */
+/* NCR53c710 only */
+#define ADDER_REG_10 0x3c /* Adder, NCR53c710 only */
+
+#define SIEN1_REG_800 0x41
+#define SIEN1_800_STO 0x04 /* selection/reselection timeout */
+#define SIEN1_800_GEN 0x02 /* general purpose timer */
+#define SIEN1_800_HTH 0x01 /* handshake to handshake */
+
+#define SIST1_REG_800 0x43
+#define SIST1_800_STO 0x04 /* selection/reselection timeout */
+#define SIST1_800_GEN 0x02 /* general purpose timer */
+#define SIST1_800_HTH 0x01 /* handshake to handshake */
+
+#define SLPAR_REG_800 0x44 /* Parity */
+
+#define MACNTL_REG_800 0x46 /* Memory access control */
+#define MACNTL_800_TYP3 0x80
+#define MACNTL_800_TYP2 0x40
+#define MACNTL_800_TYP1 0x20
+#define MACNTL_800_TYP0 0x10
+#define MACNTL_800_DWR 0x08
+#define MACNTL_800_DRD 0x04
+#define MACNTL_800_PSCPT 0x02
+#define MACNTL_800_SCPTS 0x01
+
+#define GPCNTL_REG_800 0x47 /* General Purpose Pin Control */
+
+/* Timeouts are expressed such that 0=off, 1=100us, doubling after that */
+#define STIME0_REG_800 0x48 /* SCSI Timer Register 0 */
+#define STIME0_800_HTH_MASK 0xf0 /* Handshake to Handshake timeout */
+#define STIME0_800_HTH_SHIFT 4
+#define STIME0_800_SEL_MASK 0x0f /* Selection timeout */
+#define STIME0_800_SEL_SHIFT 0
+
+#define STIME1_REG_800 0x49
+#define STIME1_800_GEN_MASK 0x0f /* General purpose timer */
+
+#define RESPID_REG_800 0x4a /* Response ID, bit fielded. 8
+ bits on narrow chips, 16 on WIDE */
+
+#define STEST0_REG_800 0x4c
+#define STEST0_800_SLT 0x08 /* Selection response logic test */
+#define STEST0_800_ART 0x04 /* Arbitration priority encoder test */
+#define STEST0_800_SOZ 0x02 /* Synchronous offset zero */
+#define STEST0_800_SOM 0x01 /* Synchronous offset maximum */
+
+#define STEST1_REG_800 0x4d
+#define STEST1_800_SCLK 0x80 /* Disable SCSI clock */
+
+#define STEST2_REG_800 0x4e
+#define STEST2_800_SCE 0x80 /* Enable SOCL/SODL */
+#define STEST2_800_ROF 0x40 /* Reset SCSI sync offset */
+#define STEST2_800_SLB 0x10 /* Enable SCSI loopback mode */
+#define STEST2_800_SZM 0x08 /* SCSI high impedance mode */
+#define STEST2_800_EXT 0x02 /* Extend REQ/ACK filter 30 to 60ns */
+#define STEST2_800_LOW 0x01 /* SCSI low level mode */
+
+#define STEST3_REG_800 0x4f
+#define STEST3_800_TE 0x80 /* Enable active negation */
+#define STEST3_800_STR 0x40 /* SCSI FIFO test read */
+#define STEST3_800_HSC 0x20 /* Halt SCSI clock */
+#define STEST3_800_DSI 0x10 /* Disable single initiator response */
+#define STEST3_800_TTM 0x04 /* Time test mode */
+#define STEST3_800_CSF 0x02 /* Clear SCSI FIFO */
+#define STEST3_800_STW 0x01 /* SCSI FIFO test write */
+
+#define OPTION_PARITY 0x1 /* Enable parity checking */
+#define OPTION_TAGGED_QUEUE 0x2 /* Enable SCSI-II tagged queuing */
+#define OPTION_700 0x8 /* Always run NCR53c700 scripts */
+#define OPTION_INTFLY 0x10 /* Use INTFLY interrupts */
+#define OPTION_DEBUG_INTR 0x20 /* Debug interrupts */
+#define OPTION_DEBUG_INIT_ONLY 0x40 /* Run initialization code and
+ simple test code, return
+ DID_NO_CONNECT if any SCSI
+ commands are attempted. */
+#define OPTION_DEBUG_READ_ONLY 0x80 /* Return DID_ERROR if any
+ SCSI write is attempted */
+#define OPTION_DEBUG_TRACE 0x100 /* Animated trace mode, print
+ each address and instruction
+ executed to debug buffer. */
+#define OPTION_DEBUG_SINGLE 0x200 /* stop after executing one
+ instruction */
+#define OPTION_SYNCHRONOUS 0x400 /* Enable sync SCSI. */
+#define OPTION_MEMORY_MAPPED 0x800 /* NCR registers have valid
+ memory mapping */
+#define OPTION_IO_MAPPED 0x1000 /* NCR registers have valid
+ I/O mapping */
+#define OPTION_DEBUG_PROBE_ONLY 0x2000 /* Probe only, don't even init */
+#define OPTION_DEBUG_TESTS_ONLY 0x4000 /* Probe, init, run selected tests */
+#define OPTION_DEBUG_TEST0 0x08000 /* Run test 0 */
+#define OPTION_DEBUG_TEST1 0x10000 /* Run test 1 */
+#define OPTION_DEBUG_TEST2 0x20000 /* Run test 2 */
+#define OPTION_DEBUG_DUMP 0x40000 /* Dump commands */
+#define OPTION_DEBUG_TARGET_LIMIT 0x80000 /* Only talk to target+luns specified */
+#define OPTION_DEBUG_NCOMMANDS_LIMIT 0x100000 /* Limit the number of commands */
+#define OPTION_DEBUG_SCRIPT 0x200000 /* Print when checkpoints are passed */
+#define OPTION_DEBUG_FIXUP 0x400000 /* print fixup values */
+#define OPTION_DEBUG_DSA 0x800000
+#define OPTION_DEBUG_CORRUPTION 0x1000000 /* Detect script corruption */
+#define OPTION_DEBUG_SDTR 0x2000000 /* Debug SDTR problem */
+#define OPTION_DEBUG_MISMATCH 0x4000000 /* Debug phase mismatches */
+#define OPTION_DISCONNECT 0x8000000 /* Allow disconnect */
+#define OPTION_DEBUG_DISCONNECT 0x10000000
+#define OPTION_ALWAYS_SYNCHRONOUS 0x20000000 /* Negotiate sync. transfers
+ on power up */
+#define OPTION_DEBUG_QUEUES 0x80000000
+#define OPTION_DEBUG_ALLOCATION 0x100000000LL
+#define OPTION_DEBUG_SYNCHRONOUS 0x200000000LL /* Sanity check SXFER and
+ SCNTL3 registers */
+#define OPTION_NO_ASYNC 0x400000000LL /* Don't automagically send
+ SDTR for async transfers when
+ we haven't been told to do
+ a synchronous transfer. */
+#define OPTION_NO_PRINT_RACE 0x800000000LL /* Don't print message when
+ the reselect/WAIT DISCONNECT
+ race condition hits */
+#if !defined(PERM_OPTIONS)
+#define PERM_OPTIONS 0
+#endif
+
+/*
+ * Some data which is accessed by the NCR chip must be 4-byte aligned.
+ * For some hosts the default is less than that (eg. 68K uses 2-byte).
+ * Alignment has only been forced where it is important; also if one
+ * 32 bit structure field is aligned then it is assumed that following
+ * 32 bit fields are also aligned. Take care when adding fields
+ * which are other than 32 bit.
+ */
+
+struct NCR53c7x0_synchronous {
+ u32 select_indirect /* Value used for indirect selection */
+ __attribute__ ((aligned (4)));
+ u32 sscf_710; /* Used to set SSCF bits for 710 */
+ u32 script[8]; /* Size ?? Script used when target is
+ reselected */
+ unsigned char synchronous_want[5]; /* Per target desired SDTR */
+/*
+ * Set_synchronous programs these, select_indirect and current settings after
+ * int_debug_should show a match.
+ */
+ unsigned char sxfer_sanity, scntl3_sanity;
+};
+
+#define CMD_FLAG_SDTR 1 /* Initiating synchronous
+ transfer negotiation */
+#define CMD_FLAG_WDTR 2 /* Initiating wide transfer
+ negotiation */
+#define CMD_FLAG_DID_SDTR 4 /* did SDTR */
+#define CMD_FLAG_DID_WDTR 8 /* did WDTR */
+
+struct NCR53c7x0_table_indirect {
+ u32 count;
+ void *address;
+};
+
+enum ncr_event {
+ EVENT_NONE = 0,
+/*
+ * Order is IMPORTANT, since these must correspond to the event interrupts
+ * in 53c7,8xx.scr
+ */
+
+ EVENT_ISSUE_QUEUE = 0x5000000, /* 0 Command was added to issue queue */
+ EVENT_START_QUEUE, /* 1 Command moved to start queue */
+ EVENT_SELECT, /* 2 Command completed selection */
+ EVENT_DISCONNECT, /* 3 Command disconnected */
+ EVENT_RESELECT, /* 4 Command reselected */
+ EVENT_COMPLETE, /* 5 Command completed */
+ EVENT_IDLE, /* 6 */
+ EVENT_SELECT_FAILED, /* 7 */
+ EVENT_BEFORE_SELECT, /* 8 */
+ EVENT_RESELECT_FAILED /* 9 */
+};
+
+struct NCR53c7x0_event {
+ enum ncr_event event; /* What type of event */
+ unsigned char target;
+ unsigned char lun;
+ struct timeval time;
+ u32 *dsa; /* What's in the DSA register now (virt) */
+/*
+ * A few things from that SCSI pid so we know what happened after
+ * the Scsi_Cmnd structure in question may have disappeared.
+ */
+ unsigned long pid; /* The SCSI PID which caused this
+ event */
+ unsigned char cmnd[12];
+};
+
+/*
+ * Things in the NCR53c7x0_cmd structure are split into two parts :
+ *
+ * 1. A fixed portion, for things which are not accessed directly by static NCR
+ * code (ie, are referenced only by the Linux side of the driver,
+ * or only by dynamically generated code).
+ *
+ * 2. The DSA portion, for things which are accessed directly by static NCR
+ * code.
+ *
+ * This is a little ugly, but it
+ * 1. Avoids conflicts between the NCR code's picture of the structure, and
+ * Linux code's idea of what it looks like.
+ *
+ * 2. Minimizes the pain in the Linux side of the code needed
+ * to calculate real dsa locations for things, etc.
+ *
+ */
+
+struct NCR53c7x0_cmd {
+ void *real; /* Real, unaligned address for
+ free function */
+ void (* free)(void *, int); /* Command to deallocate; NULL
+ for structures allocated with
+ scsi_register, etc. */
+ Scsi_Cmnd *cmd; /* Associated Scsi_Cmnd
+ structure, Scsi_Cmnd points
+ at NCR53c7x0_cmd using
+ host_scribble structure */
+
+ int size; /* scsi_malloc'd size of this
+ structure */
+
+ int flags; /* CMD_* flags */
+
+ unsigned char cmnd[12]; /* CDB, copied from Scsi_Cmnd */
+ int result; /* Copy to Scsi_Cmnd when done */
+
+ struct { /* Private non-cached bounce buffer */
+ unsigned char buf[256];
+ u32 addr;
+ u32 len;
+ } bounce;
+
+/*
+ * SDTR and WIDE messages are an either/or affair
+ * in this message, since we will go into message out and send
+ * _the whole mess_ without dropping out of message out to
+ * let the target go into message in after sending the first
+ * message.
+ */
+
+ unsigned char select[11]; /* Select message, includes
+ IDENTIFY
+ (optional) QUEUE TAG
+ (optional) SDTR or WDTR
+ */
+
+
+ volatile struct NCR53c7x0_cmd *next; /* Linux maintained lists (free,
+ running, eventually finished */
+
+
+ u32 *data_transfer_start; /* Start of data transfer routines */
+ u32 *data_transfer_end; /* Address after end of data transfer o
+ routines */
+/*
+ * The following three fields were moved from the DSA proper to here
+ * since only dynamically generated NCR code refers to them, meaning
+ * we don't need dsa_* absolutes, and it is simpler to let the
+ * host code refer to them directly.
+ */
+
+/*
+ * HARD CODED : residual and saved_residual need to agree with the sizes
+ * used in NCR53c7,8xx.scr.
+ *
+ * FIXME: we want to consider the case where we have odd-length
+ * scatter/gather buffers and a WIDE transfer, in which case
+ * we'll need to use the CHAIN MOVE instruction. Ick.
+ */
+ u32 residual[6] __attribute__ ((aligned (4)));
+ /* Residual data transfer which
+ allows pointer code to work
+ right.
+
+ [0-1] : Conditional call to
+ appropriate other transfer
+ routine.
+ [2-3] : Residual block transfer
+ instruction.
+ [4-5] : Jump to instruction
+ after splice.
+ */
+ u32 saved_residual[6]; /* Copy of old residual, so we
+ can get another partial
+ transfer and still recover
+ */
+
+ u32 saved_data_pointer; /* Saved data pointer */
+
+ u32 dsa_next_addr; /* _Address_ of dsa_next field
+ in this dsa for RISCy
+ style constant. */
+
+ u32 dsa_addr; /* Address of dsa; RISCy style
+ constant */
+
+ u32 dsa[0]; /* Variable length (depending
+ on host type, number of scatter /
+ gather buffers, etc). */
+};
+
+struct NCR53c7x0_break {
+ u32 *address, old_instruction[2];
+ struct NCR53c7x0_break *next;
+ unsigned char old_size; /* Size of old instruction */
+};
+
+/* Indicates that the NCR is not executing code */
+#define STATE_HALTED 0
+/*
+ * Indicates that the NCR is executing the wait for select / reselect
+ * script. Only used when running NCR53c700 compatible scripts, only
+ * state during which an ABORT is _not_ considered an error condition.
+ */
+#define STATE_WAITING 1
+/* Indicates that the NCR is executing other code. */
+#define STATE_RUNNING 2
+/*
+ * Indicates that the NCR was being aborted.
+ */
+#define STATE_ABORTING 3
+/* Indicates that the NCR was successfully aborted. */
+#define STATE_ABORTED 4
+/* Indicates that the NCR has been disabled due to a fatal error */
+#define STATE_DISABLED 5
+
+/*
+ * Where knowledge of SCSI SCRIPT(tm) specified values are needed
+ * in an interrupt handler, an interrupt handler exists for each
+ * different SCSI script so we don't have name space problems.
+ *
+ * Return values of these handlers are as follows :
+ */
+#define SPECIFIC_INT_NOTHING 0 /* don't even restart */
+#define SPECIFIC_INT_RESTART 1 /* restart at the next instruction */
+#define SPECIFIC_INT_ABORT 2 /* recoverable error, abort cmd */
+#define SPECIFIC_INT_PANIC 3 /* unrecoverable error, panic */
+#define SPECIFIC_INT_DONE 4 /* normal command completion */
+#define SPECIFIC_INT_BREAK 5 /* break point encountered */
+
+struct NCR53c7x0_hostdata {
+ int size; /* Size of entire Scsi_Host
+ structure */
+ int board; /* set to board type, useful if
+ we have host specific things,
+ ie, a general purpose I/O
+ bit is being used to enable
+ termination, etc. */
+
+ int chip; /* set to chip type; 700-66 is
+ 700-66, rest are last three
+ digits of part number */
+
+ char valid_ids[8]; /* Valid SCSI ID's for adapter */
+
+ u32 *dsp; /* dsp to restart with after
+ all stacked interrupts are
+ handled. */
+
+ unsigned dsp_changed:1; /* Has dsp changed within this
+ set of stacked interrupts ? */
+
+ unsigned char dstat; /* Most recent value of dstat */
+ unsigned dstat_valid:1;
+
+ unsigned expecting_iid:1; /* Expect IID interrupt */
+ unsigned expecting_sto:1; /* Expect STO interrupt */
+
+ /*
+ * The code stays cleaner if we use variables with function
+ * pointers and offsets that are unique for the different
+ * scripts rather than having a slew of switch(hostdata->chip)
+ * statements.
+ *
+ * It also means that the #defines from the SCSI SCRIPTS(tm)
+ * don't have to be visible outside of the script-specific
+ * instructions, preventing name space pollution.
+ */
+
+ void (* init_fixup)(struct Scsi_Host *host);
+ void (* init_save_regs)(struct Scsi_Host *host);
+ void (* dsa_fixup)(struct NCR53c7x0_cmd *cmd);
+ void (* soft_reset)(struct Scsi_Host *host);
+ int (* run_tests)(struct Scsi_Host *host);
+
+ /*
+ * Called when DSTAT_SIR is set, indicating an interrupt generated
+ * by the INT instruction, where values are unique for each SCSI
+ * script. Should return one of the SPEC_* values.
+ */
+
+ int (* dstat_sir_intr)(struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+
+ int dsa_len; /* Size of DSA structure */
+
+ /*
+ * Location of DSA fields for the SCSI SCRIPT corresponding to this
+ * chip.
+ */
+
+ s32 dsa_start;
+ s32 dsa_end;
+ s32 dsa_next;
+ s32 dsa_prev;
+ s32 dsa_cmnd;
+ s32 dsa_select;
+ s32 dsa_msgout;
+ s32 dsa_cmdout;
+ s32 dsa_dataout;
+ s32 dsa_datain;
+ s32 dsa_msgin;
+ s32 dsa_msgout_other;
+ s32 dsa_write_sync;
+ s32 dsa_write_resume;
+ s32 dsa_check_reselect;
+ s32 dsa_status;
+ s32 dsa_saved_pointer;
+ s32 dsa_jump_dest;
+
+ /*
+ * Important entry points that generic fixup code needs
+ * to know about, fixed up.
+ */
+
+ s32 E_accept_message;
+ s32 E_command_complete;
+ s32 E_data_transfer;
+ s32 E_dsa_code_template;
+ s32 E_dsa_code_template_end;
+ s32 E_end_data_transfer;
+ s32 E_msg_in;
+ s32 E_initiator_abort;
+ s32 E_other_transfer;
+ s32 E_other_in;
+ s32 E_other_out;
+ s32 E_target_abort;
+ s32 E_debug_break;
+ s32 E_reject_message;
+ s32 E_respond_message;
+ s32 E_select;
+ s32 E_select_msgout;
+ s32 E_test_0;
+ s32 E_test_1;
+ s32 E_test_2;
+ s32 E_test_3;
+ s32 E_dsa_zero;
+ s32 E_cmdout_cmdout;
+ s32 E_wait_reselect;
+ s32 E_dsa_code_begin;
+
+ long long options; /* Bitfielded set of options enabled */
+ volatile u32 test_completed; /* Test completed */
+ int test_running; /* Test currently running */
+ s32 test_source
+ __attribute__ ((aligned (4)));
+ volatile s32 test_dest;
+
+ volatile int state; /* state of driver, only used for
+ OPTION_700 */
+
+ unsigned char dmode; /*
+ * set to the address of the DMODE
+ * register for this chip.
+ */
+ unsigned char istat; /*
+ * set to the address of the ISTAT
+ * register for this chip.
+ */
+
+ int scsi_clock; /*
+ * SCSI clock in HZ. 0 may be used
+ * for unknown, although this will
+ * disable synchronous negotiation.
+ */
+
+ volatile int intrs; /* Number of interrupts */
+ volatile int resets; /* Number of SCSI resets */
+ unsigned char saved_dmode;
+ unsigned char saved_ctest4;
+ unsigned char saved_ctest7;
+ unsigned char saved_dcntl;
+ unsigned char saved_scntl3;
+
+ unsigned char this_id_mask;
+
+ /* Debugger information */
+ struct NCR53c7x0_break *breakpoints, /* Linked list of all break points */
+ *breakpoint_current; /* Current breakpoint being stepped
+ through, NULL if we are running
+ normally. */
+#ifdef NCR_DEBUG
+ int debug_size; /* Size of debug buffer */
+ volatile int debug_count; /* Current data count */
+ volatile char *debug_buf; /* Output ring buffer */
+ volatile char *debug_write; /* Current write pointer */
+ volatile char *debug_read; /* Current read pointer */
+#endif /* def NCR_DEBUG */
+
+ /* XXX - primitive debugging junk, remove when working ? */
+ int debug_print_limit; /* Number of commands to print
+ out exhaustive debugging
+ information for if
+ OPTION_DEBUG_DUMP is set */
+
+ unsigned char debug_lun_limit[16]; /* If OPTION_DEBUG_TARGET_LIMIT
+ set, puke if commands are sent
+ to other target/lun combinations */
+
+ int debug_count_limit; /* Number of commands to execute
+ before puking to limit debugging
+ output */
+
+
+ volatile unsigned idle:1; /* set to 1 if idle */
+
+ /*
+ * Table of synchronous+wide transfer parameters set on a per-target
+ * basis.
+ */
+
+ volatile struct NCR53c7x0_synchronous sync[16]
+ __attribute__ ((aligned (4)));
+
+ volatile Scsi_Cmnd *issue_queue
+ __attribute__ ((aligned (4)));
+ /* waiting to be issued by
+ Linux driver */
+ volatile struct NCR53c7x0_cmd *running_list;
+ /* commands running, maintained
+ by Linux driver */
+
+ volatile struct NCR53c7x0_cmd *ncrcurrent; /* currently connected
+ nexus, ONLY valid for
+ NCR53c700/NCR53c700-66
+ */
+
+ volatile struct NCR53c7x0_cmd *spare; /* pointer to spare,
+ allocated at probe time,
+ which we can use for
+ initialization */
+ volatile struct NCR53c7x0_cmd *free;
+ int max_cmd_size; /* Maximum size of NCR53c7x0_cmd
+ based on number of
+ scatter/gather segments, etc.
+ */
+ volatile int num_cmds; /* Number of commands
+ allocated */
+ volatile int extra_allocate;
+ volatile unsigned char cmd_allocated[16]; /* Have we allocated commands
+ for this target yet? If not,
+ do so ASAP */
+ volatile unsigned char busy[16][8]; /* number of commands
+ executing on each target
+ */
+ /*
+ * Eventually, I'll switch to a coroutine for calling
+ * cmd->done(cmd), etc. so that we can overlap interrupt
+ * processing with this code for maximum performance.
+ */
+
+ volatile struct NCR53c7x0_cmd *finished_queue;
+
+ /* Shared variables between SCRIPT and host driver */
+ volatile u32 *schedule
+ __attribute__ ((aligned (4))); /* Array of JUMPs to dsa_begin
+ routines of various DSAs.
+ When not in use, replace
+ with jump to next slot */
+
+
+ volatile unsigned char msg_buf[16]; /* buffer for messages
+ other than the command
+ complete message */
+
+ /* Per-target default synchronous and WIDE messages */
+ volatile unsigned char synchronous_want[16][5];
+ volatile unsigned char wide_want[16][4];
+
+ /* Bit fielded set of targets we want to speak synchronously with */
+ volatile u16 initiate_sdtr;
+ /* Bit fielded set of targets we want to speak wide with */
+ volatile u16 initiate_wdtr;
+ /* Bit fielded list of targets we've talked to. */
+ volatile u16 talked_to;
+
+ /* Array of bit-fielded lun lists that we need to request_sense */
+ volatile unsigned char request_sense[16];
+
+ u32 addr_reconnect_dsa_head
+ __attribute__ ((aligned (4))); /* RISCy style constant,
+ address of following */
+ volatile u32 reconnect_dsa_head;
+ /* Data identifying nexus we are trying to match during reselection */
+ volatile unsigned char reselected_identify; /* IDENTIFY message */
+ volatile unsigned char reselected_tag; /* second byte of queue tag
+ message or 0 */
+
+ /* These were static variables before we moved them */
+
+ s32 NCR53c7xx_zero
+ __attribute__ ((aligned (4)));
+ s32 NCR53c7xx_sink;
+ u32 NOP_insn;
+ char NCR53c7xx_msg_reject;
+ char NCR53c7xx_msg_abort;
+ char NCR53c7xx_msg_nop;
+
+ /*
+ * Following item introduced by RGH to support NCRc710, which is
+ * VERY brain-dead when it come to memory moves
+ */
+
+ /* DSA save area used only by the NCR chip */
+ volatile unsigned long saved2_dsa
+ __attribute__ ((aligned (4)));
+
+ volatile unsigned long emulated_intfly
+ __attribute__ ((aligned (4)));
+
+ volatile int event_size, event_index;
+ volatile struct NCR53c7x0_event *events;
+
+ /* If we need to generate code to kill off the currently connected
+ command, this is where we do it. Should have a BMI instruction
+ to source or sink the current data, followed by a JUMP
+ to abort_connected */
+
+ u32 *abort_script;
+
+ int script_count; /* Size of script in words */
+ u32 script[0]; /* Relocated SCSI script */
+
+};
+
+#define SCSI_IRQ_NONE 255
+#define DMA_NONE 255
+#define IRQ_AUTO 254
+#define DMA_AUTO 254
+
+#define BOARD_GENERIC 0
+
+#define NCR53c7x0_insn_size(insn) \
+ (((insn) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI ? 3 : 2)
+
+
+#define NCR53c7x0_local_declare() \
+ volatile unsigned char *NCR53c7x0_address_memory; \
+ unsigned int NCR53c7x0_address_io; \
+ int NCR53c7x0_memory_mapped
+
+#define NCR53c7x0_local_setup(host) \
+ NCR53c7x0_address_memory = (void *) (host)->base; \
+ NCR53c7x0_address_io = (unsigned int) (host)->io_port; \
+ NCR53c7x0_memory_mapped = ((struct NCR53c7x0_hostdata *) \
+ host->hostdata[0])-> options & OPTION_MEMORY_MAPPED
+
+#ifdef BIG_ENDIAN
+/* These could be more efficient, given that we are always memory mapped,
+ * but they don't give the same problems as the write macros, so leave
+ * them. */
+#ifdef __mc68000__
+#define NCR53c7x0_read8(address) \
+ ((unsigned int)raw_inb((u32)NCR53c7x0_address_memory + ((u32)(address)^3)) )
+
+#define NCR53c7x0_read16(address) \
+ ((unsigned int)raw_inw((u32)NCR53c7x0_address_memory + ((u32)(address)^2)))
+#else
+#define NCR53c7x0_read8(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readb((u32)NCR53c7x0_address_memory + ((u32)(address)^3)) : \
+ inb(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read16(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readw((u32)NCR53c7x0_address_memory + ((u32)(address)^2)) : \
+ inw(NCR53c7x0_address_io + (address)))
+#endif /* mc68000 */
+#else
+#define NCR53c7x0_read8(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readb((u32)NCR53c7x0_address_memory + (u32)(address)) : \
+ inb(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read16(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readw((u32)NCR53c7x0_address_memory + (u32)(address)) : \
+ inw(NCR53c7x0_address_io + (address)))
+#endif
+
+#ifdef __mc68000__
+#define NCR53c7x0_read32(address) \
+ ((unsigned int) raw_inl((u32)NCR53c7x0_address_memory + (u32)(address)))
+#else
+#define NCR53c7x0_read32(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int) readl((u32)NCR53c7x0_address_memory + (u32)(address)) : \
+ inl(NCR53c7x0_address_io + (address)))
+#endif /* mc68000*/
+
+#ifdef BIG_ENDIAN
+/* If we are big-endian, then we are not Intel, so probably don't have
+ * an i/o map as well as a memory map. So, let's assume memory mapped.
+ * Also, I am having terrible problems trying to persuade the compiler
+ * not to lay down code which does a read after write for these macros.
+ * If you remove 'volatile' from writeb() and friends it is ok....
+ */
+
+#define NCR53c7x0_write8(address,value) \
+ *(volatile unsigned char *) \
+ ((u32)NCR53c7x0_address_memory + ((u32)(address)^3)) = (value)
+
+#define NCR53c7x0_write16(address,value) \
+ *(volatile unsigned short *) \
+ ((u32)NCR53c7x0_address_memory + ((u32)(address)^2)) = (value)
+
+#define NCR53c7x0_write32(address,value) \
+ *(volatile unsigned long *) \
+ ((u32)NCR53c7x0_address_memory + ((u32)(address))) = (value)
+
+#else
+
+#define NCR53c7x0_write8(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writeb((value), (u32)NCR53c7x0_address_memory + (u32)(address)); mb();}) : \
+ outb((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write16(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writew((value), (u32)NCR53c7x0_address_memory + (u32)(address)); mb();}) : \
+ outw((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write32(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writel((value), (u32)NCR53c7x0_address_memory + (u32)(address)); mb();}) : \
+ outl((value), NCR53c7x0_address_io + (address)))
+
+#endif
+
+/* Patch arbitrary 32 bit words in the script */
+#define patch_abs_32(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) { \
+ (script)[A_##symbol##_used[i] - (offset)] += (value); \
+ if (hostdata->options & OPTION_DEBUG_FIXUP) \
+ printk("scsi%d : %s reference %d at 0x%x in %s is now 0x%x\n",\
+ host->host_no, #symbol, i, A_##symbol##_used[i] - \
+ (int)(offset), #script, (script)[A_##symbol##_used[i] - \
+ (offset)]); \
+ }
+
+/* Patch read/write instruction immediate field */
+#define patch_abs_rwri_data(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) \
+ (script)[A_##symbol##_used[i] - (offset)] = \
+ ((script)[A_##symbol##_used[i] - (offset)] & \
+ ~DBC_RWRI_IMMEDIATE_MASK) | \
+ (((value) << DBC_RWRI_IMMEDIATE_SHIFT) & \
+ DBC_RWRI_IMMEDIATE_MASK)
+
+/* Patch transfer control instruction data field */
+#define patch_abs_tci_data(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) \
+ (script)[A_##symbol##_used[i] - (offset)] = \
+ ((script)[A_##symbol##_used[i] - (offset)] & \
+ ~DBC_TCI_DATA_MASK) | \
+ (((value) << DBC_TCI_DATA_SHIFT) & \
+ DBC_TCI_DATA_MASK)
+
+/* Patch field in dsa structure (assignment should be +=?) */
+#define patch_dsa_32(dsa, symbol, word, value) \
+ { \
+ (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(u32) \
+ + (word)] = (value); \
+ if (hostdata->options & OPTION_DEBUG_DSA) \
+ printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \
+ #dsa, #symbol, hostdata->##symbol, \
+ (word), (u32) (value)); \
+ }
+
+/* Paranoid people could use panic() here. */
+#define FATAL(host) shutdown((host));
+
+extern int ncr53c7xx_init(Scsi_Host_Template *tpnt, int board, int chip,
+ unsigned long base, int io_port, int irq, int dma,
+ long long options, int clock);
+
+#endif /* NCR53c710_C */
+#endif /* NCR53c710_H */
diff --git a/drivers/scsi/53c7xx.scr b/drivers/scsi/53c7xx.scr
new file mode 100644
index 000000000000..9c5694a2da8a
--- /dev/null
+++ b/drivers/scsi/53c7xx.scr
@@ -0,0 +1,1591 @@
+#undef DEBUG
+#undef EVENTS
+#undef NO_SELECTION_TIMEOUT
+#define BIG_ENDIAN
+
+; 53c710 driver. Modified from Drew Eckhardts driver
+; for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
+;
+; I have left the script for the 53c8xx family in here, as it is likely
+; to be useful to see what I changed when bug hunting.
+
+; NCR 53c810 driver, main script
+; Sponsored by
+; iX Multiuser Multitasking Magazine
+; hm@ix.de
+;
+; Copyright 1993, 1994, 1995 Drew Eckhardt
+; Visionary Computing
+; (Unix and Linux consulting and custom programming)
+; drew@PoohSticks.ORG
+; +1 (303) 786-7975
+;
+; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+;
+; PRE-ALPHA
+;
+; For more information, please consult
+;
+; NCR 53C810
+; PCI-SCSI I/O Processor
+; Data Manual
+;
+; NCR 53C710
+; SCSI I/O Processor
+; Programmers Guide
+;
+; NCR Microelectronics
+; 1635 Aeroplaza Drive
+; Colorado Springs, CO 80916
+; 1+ (719) 578-3400
+;
+; Toll free literature number
+; +1 (800) 334-5454
+;
+; IMPORTANT : This code is self modifying due to the limitations of
+; the NCR53c7,8xx series chips. Persons debugging this code with
+; the remote debugger should take this into account, and NOT set
+; breakpoints in modified instructions.
+;
+; Design:
+; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard
+; microcontroller using a simple instruction set.
+;
+; So, to minimize the effects of interrupt latency, and to maximize
+; throughput, this driver offloads the practical maximum amount
+; of processing to the SCSI chip while still maintaining a common
+; structure.
+;
+; Where tradeoffs were needed between efficiency on the older
+; chips and the newer NCR53c800 series, the NCR53c800 series
+; was chosen.
+;
+; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
+; automate SCSI transfers without host processor intervention, this
+; isn't the case with the NCR53c710 and newer chips which allow
+;
+; - reads and writes to the internal registers from within the SCSI
+; scripts, allowing the SCSI SCRIPTS(tm) code to save processor
+; state so that multiple threads of execution are possible, and also
+; provide an ALU for loop control, etc.
+;
+; - table indirect addressing for some instructions. This allows
+; pointers to be located relative to the DSA ((Data Structure
+; Address) register.
+;
+; These features make it possible to implement a mailbox style interface,
+; where the same piece of code is run to handle I/O for multiple threads
+; at once minimizing our need to relocate code. Since the NCR53c700/
+; NCR53c800 series have a unique combination of features, making a
+; a standard ingoing/outgoing mailbox system, costly, I've modified it.
+;
+; - Mailboxes are a mixture of code and data. This lets us greatly
+; simplify the NCR53c810 code and do things that would otherwise
+; not be possible.
+;
+; The saved data pointer is now implemented as follows :
+;
+; Control flow has been architected such that if control reaches
+; munge_save_data_pointer, on a restore pointers message or
+; reconnection, a jump to the address formerly in the TEMP register
+; will allow the SCSI command to resume execution.
+;
+
+;
+; Note : the DSA structures must be aligned on 32 bit boundaries,
+; since the source and destination of MOVE MEMORY instructions
+; must share the same alignment and this is the alignment of the
+; NCR registers.
+;
+
+; For some systems (MVME166, for example) dmode is always the same, so don't
+; waste time writing it
+
+#if 1
+#define DMODE_MEMORY_TO_NCR
+#define DMODE_MEMORY_TO_MEMORY
+#define DMODE_NCR_TO_MEMORY
+#else
+#define DMODE_MEMORY_TO_NCR MOVE dmode_memory_to_ncr TO DMODE
+#define DMODE_MEMORY_TO_MEMORY MOVE dmode_memory_to_memory TO DMODE
+#define DMODE_NCR_TO_MEMORY MOVE dmode_ncr_to_memory TO DMODE
+#endif
+
+ABSOLUTE dsa_temp_lun = 0 ; Patch to lun for current dsa
+ABSOLUTE dsa_temp_next = 0 ; Patch to dsa next for current dsa
+ABSOLUTE dsa_temp_addr_next = 0 ; Patch to address of dsa next address
+ ; for current dsa
+ABSOLUTE dsa_temp_sync = 0 ; Patch to address of per-target
+ ; sync routine
+ABSOLUTE dsa_sscf_710 = 0 ; Patch to address of per-target
+ ; sscf value (53c710)
+ABSOLUTE dsa_temp_target = 0 ; Patch to id for current dsa
+ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command
+ ; saved data pointer
+ABSOLUTE dsa_temp_addr_residual = 0 ; Patch to address of per-command
+ ; current residual code
+ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command
+ ; saved residual code
+ABSOLUTE dsa_temp_addr_new_value = 0 ; Address of value for JUMP operand
+ABSOLUTE dsa_temp_addr_array_value = 0 ; Address to copy to
+ABSOLUTE dsa_temp_addr_dsa_value = 0 ; Address of this DSA value
+
+;
+; Once a device has initiated reselection, we need to compare it
+; against the singly linked list of commands which have disconnected
+; and are pending reselection. These commands are maintained in
+; an unordered singly linked list of DSA structures, through the
+; DSA pointers at their 'centers' headed by the reconnect_dsa_head
+; pointer.
+;
+; To avoid complications in removing commands from the list,
+; I minimize the amount of expensive (at eight operations per
+; addition @ 500-600ns each) pointer operations which must
+; be done in the NCR driver by precomputing them on the
+; host processor during dsa structure generation.
+;
+; The fixed-up per DSA code knows how to recognize the nexus
+; associated with the corresponding SCSI command, and modifies
+; the source and destination pointers for the MOVE MEMORY
+; instruction which is executed when reselected_ok is called
+; to remove the command from the list. Similarly, DSA is
+; loaded with the address of the next DSA structure and
+; reselected_check_next is called if a failure occurs.
+;
+; Perhaps more concisely, the net effect of the mess is
+;
+; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head,
+; src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) {
+; src = &dsa->next;
+; if (target_id == dsa->id && target_lun == dsa->lun) {
+; *dest = *src;
+; break;
+; }
+; }
+;
+; if (!dsa)
+; error (int_err_unexpected_reselect);
+; else
+; longjmp (dsa->jump_resume, 0);
+;
+;
+
+#if (CHIP != 700) && (CHIP != 70066)
+; Define DSA structure used for mailboxes
+ENTRY dsa_code_template
+dsa_code_template:
+ENTRY dsa_code_begin
+dsa_code_begin:
+; RGH: Don't care about TEMP and DSA here
+ DMODE_MEMORY_TO_NCR
+ MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch
+ DMODE_MEMORY_TO_MEMORY
+#if (CHIP == 710)
+ MOVE MEMORY 4, addr_scratch, saved_dsa
+ ; We are about to go and select the device, so must set SSCF bits
+ MOVE MEMORY 4, dsa_sscf_710, addr_scratch
+#ifdef BIG_ENDIAN
+ MOVE SCRATCH3 TO SFBR
+#else
+ MOVE SCRATCH0 TO SFBR
+#endif
+ MOVE SFBR TO SBCL
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#else
+ CALL scratch_to_dsa
+#endif
+ CALL select
+; Handle the phase mismatch which may have resulted from the
+; MOVE FROM dsa_msgout if we returned here. The CLEAR ATN
+; may or may not be necessary, and we should update script_asm.pl
+; to handle multiple pieces.
+ CLEAR ATN
+ CLEAR ACK
+
+; Replace second operand with address of JUMP instruction dest operand
+; in schedule table for this DSA. Becomes dsa_jump_dest in 53c7,8xx.c.
+ENTRY dsa_code_fix_jump
+dsa_code_fix_jump:
+ MOVE MEMORY 4, NOP_insn, 0
+ JUMP select_done
+
+; wrong_dsa loads the DSA register with the value of the dsa_next
+; field.
+;
+wrong_dsa:
+#if (CHIP == 710)
+; NOTE DSA is corrupt when we arrive here!
+#endif
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the destination address is the address of the OLD
+; next pointer.
+;
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 8
+ DMODE_MEMORY_TO_NCR
+;
+; Move the _contents_ of the next pointer into the DSA register as
+; the next I_T_L or I_T_L_Q tupple to check against the established
+; nexus.
+;
+ MOVE MEMORY 4, dsa_temp_next, addr_scratch
+ DMODE_MEMORY_TO_MEMORY
+#if (CHIP == 710)
+ MOVE MEMORY 4, addr_scratch, saved_dsa
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#else
+ CALL scratch_to_dsa
+#endif
+ JUMP reselected_check_next
+
+ABSOLUTE dsa_save_data_pointer = 0
+ENTRY dsa_code_save_data_pointer
+dsa_code_save_data_pointer:
+#if (CHIP == 710)
+ ; When we get here, TEMP has been saved in jump_temp+4, DSA is corrupt
+ ; We MUST return with DSA correct
+ MOVE MEMORY 4, jump_temp+4, dsa_temp_addr_saved_pointer
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
+ CLEAR ACK
+#ifdef DEBUG
+ INT int_debug_saved
+#endif
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+ JUMP jump_temp
+#else
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_temp, dsa_temp_addr_saved_pointer
+ DMODE_MEMORY_TO_MEMORY
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
+ CLEAR ACK
+#ifdef DEBUG
+ INT int_debug_saved
+#endif
+ RETURN
+#endif
+ABSOLUTE dsa_restore_pointers = 0
+ENTRY dsa_code_restore_pointers
+dsa_code_restore_pointers:
+#if (CHIP == 710)
+ ; TEMP and DSA are corrupt when we get here, but who cares!
+ MOVE MEMORY 4, dsa_temp_addr_saved_pointer, jump_temp + 4
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
+ CLEAR ACK
+ ; Restore DSA, note we don't care about TEMP
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#ifdef DEBUG
+ INT int_debug_restored
+#endif
+ JUMP jump_temp
+#else
+ DMODE_MEMORY_TO_NCR
+ MOVE MEMORY 4, dsa_temp_addr_saved_pointer, addr_temp
+ DMODE_MEMORY_TO_MEMORY
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
+ CLEAR ACK
+#ifdef DEBUG
+ INT int_debug_restored
+#endif
+ RETURN
+#endif
+
+ABSOLUTE dsa_check_reselect = 0
+; dsa_check_reselect determines whether or not the current target and
+; lun match the current DSA
+ENTRY dsa_code_check_reselect
+dsa_code_check_reselect:
+#if (CHIP == 710)
+ /* Arrives here with DSA correct */
+ /* Assumes we are always ID 7 */
+ MOVE LCRC TO SFBR ; LCRC has our ID and his ID bits set
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0x80
+#else
+ MOVE SSID TO SFBR ; SSID contains 3 bit target ID
+; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0xf8
+#endif
+;
+; Hack - move to scratch first, since SFBR is not writeable
+; via the CPU and hence a MOVE MEMORY instruction.
+;
+ DMODE_MEMORY_TO_NCR
+ MOVE MEMORY 1, reselected_identify, addr_scratch
+ DMODE_MEMORY_TO_MEMORY
+#ifdef BIG_ENDIAN
+ ; BIG ENDIAN ON MVME16x
+ MOVE SCRATCH3 TO SFBR
+#else
+ MOVE SCRATCH0 TO SFBR
+#endif
+; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
+; Are you sure about that? richard@sleepie.demon.co.uk
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the source address is the address of this dsa's
+; next pointer.
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 4
+ CALL reselected_ok
+#if (CHIP == 710)
+; Restore DSA following memory moves in reselected_ok
+; dsa_temp_sync doesn't really care about DSA, but it has an
+; optional debug INT so a valid DSA is a good idea.
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+ CALL dsa_temp_sync
+; Release ACK on the IDENTIFY message _after_ we've set the synchronous
+; transfer parameters!
+ CLEAR ACK
+; Implicitly restore pointers on reselection, so a RETURN
+; will transfer control back to the right spot.
+ CALL REL (dsa_code_restore_pointers)
+ RETURN
+ENTRY dsa_zero
+dsa_zero:
+ENTRY dsa_code_template_end
+dsa_code_template_end:
+
+; Perform sanity check for dsa_fields_start == dsa_code_template_end -
+; dsa_zero, puke.
+
+ABSOLUTE dsa_fields_start = 0 ; Sanity marker
+ ; pad 48 bytes (fix this RSN)
+ABSOLUTE dsa_next = 48 ; len 4 Next DSA
+ ; del 4 Previous DSA address
+ABSOLUTE dsa_cmnd = 56 ; len 4 Scsi_Cmnd * for this thread.
+ABSOLUTE dsa_select = 60 ; len 4 Device ID, Period, Offset for
+ ; table indirect select
+ABSOLUTE dsa_msgout = 64 ; len 8 table indirect move parameter for
+ ; select message
+ABSOLUTE dsa_cmdout = 72 ; len 8 table indirect move parameter for
+ ; command
+ABSOLUTE dsa_dataout = 80 ; len 4 code pointer for dataout
+ABSOLUTE dsa_datain = 84 ; len 4 code pointer for datain
+ABSOLUTE dsa_msgin = 88 ; len 8 table indirect move for msgin
+ABSOLUTE dsa_status = 96 ; len 8 table indirect move for status byte
+ABSOLUTE dsa_msgout_other = 104 ; len 8 table indirect for normal message out
+ ; (Synchronous transfer negotiation, etc).
+ABSOLUTE dsa_end = 112
+
+ABSOLUTE schedule = 0 ; Array of JUMP dsa_begin or JUMP (next),
+ ; terminated by a call to JUMP wait_reselect
+
+; Linked lists of DSA structures
+ABSOLUTE reconnect_dsa_head = 0 ; Link list of DSAs which can reconnect
+ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable containing
+ ; address of reconnect_dsa_head
+
+; These select the source and destination of a MOVE MEMORY instruction
+ABSOLUTE dmode_memory_to_memory = 0x0
+ABSOLUTE dmode_memory_to_ncr = 0x0
+ABSOLUTE dmode_ncr_to_memory = 0x0
+
+ABSOLUTE addr_scratch = 0x0
+ABSOLUTE addr_temp = 0x0
+#if (CHIP == 710)
+ABSOLUTE saved_dsa = 0x0
+ABSOLUTE emulfly = 0x0
+ABSOLUTE addr_dsa = 0x0
+#endif
+#endif /* CHIP != 700 && CHIP != 70066 */
+
+; Interrupts -
+; MSB indicates type
+; 0 handle error condition
+; 1 handle message
+; 2 handle normal condition
+; 3 debugging interrupt
+; 4 testing interrupt
+; Next byte indicates specific error
+
+; XXX not yet implemented, I'm not sure if I want to -
+; Next byte indicates the routine the error occurred in
+; The LSB indicates the specific place the error occurred
+
+ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
+ABSOLUTE int_err_selected = 0x00010000 ; SELECTED (nee RESELECTED)
+ABSOLUTE int_err_unexpected_reselect = 0x00020000
+ABSOLUTE int_err_check_condition = 0x00030000
+ABSOLUTE int_err_no_phase = 0x00040000
+ABSOLUTE int_msg_wdtr = 0x01000000 ; WDTR message received
+ABSOLUTE int_msg_sdtr = 0x01010000 ; SDTR received
+ABSOLUTE int_msg_1 = 0x01020000 ; single byte special message
+ ; received
+
+ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
+ ; registers.
+ABSOLUTE int_norm_reselect_complete = 0x02010000 ; Nexus established
+ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
+ABSOLUTE int_norm_disconnected = 0x02030000 ; Disconnected
+ABSOLUTE int_norm_aborted =0x02040000 ; Aborted *dsa
+ABSOLUTE int_norm_reset = 0x02050000 ; Generated BUS reset.
+ABSOLUTE int_norm_emulateintfly = 0x02060000 ; 53C710 Emulated intfly
+ABSOLUTE int_debug_break = 0x03000000 ; Break point
+#ifdef DEBUG
+ABSOLUTE int_debug_scheduled = 0x03010000 ; new I/O scheduled
+ABSOLUTE int_debug_idle = 0x03020000 ; scheduler is idle
+ABSOLUTE int_debug_dsa_loaded = 0x03030000 ; dsa reloaded
+ABSOLUTE int_debug_reselected = 0x03040000 ; NCR reselected
+ABSOLUTE int_debug_head = 0x03050000 ; issue head overwritten
+ABSOLUTE int_debug_disconnected = 0x03060000 ; disconnected
+ABSOLUTE int_debug_disconnect_msg = 0x03070000 ; got message to disconnect
+ABSOLUTE int_debug_dsa_schedule = 0x03080000 ; in dsa_schedule
+ABSOLUTE int_debug_reselect_check = 0x03090000 ; Check for reselection of DSA
+ABSOLUTE int_debug_reselected_ok = 0x030a0000 ; Reselection accepted
+#endif
+ABSOLUTE int_debug_panic = 0x030b0000 ; Panic driver
+#ifdef DEBUG
+ABSOLUTE int_debug_saved = 0x030c0000 ; save/restore pointers
+ABSOLUTE int_debug_restored = 0x030d0000
+ABSOLUTE int_debug_sync = 0x030e0000 ; Sanity check synchronous
+ ; parameters.
+ABSOLUTE int_debug_datain = 0x030f0000 ; going into data in phase
+ ; now.
+ABSOLUTE int_debug_check_dsa = 0x03100000 ; Sanity check DSA against
+ ; SDID.
+#endif
+
+ABSOLUTE int_test_1 = 0x04000000 ; Test 1 complete
+ABSOLUTE int_test_2 = 0x04010000 ; Test 2 complete
+ABSOLUTE int_test_3 = 0x04020000 ; Test 3 complete
+
+
+; These should start with 0x05000000, with low bits incrementing for
+; each one.
+
+#ifdef EVENTS
+ABSOLUTE int_EVENT_SELECT = 0
+ABSOLUTE int_EVENT_DISCONNECT = 0
+ABSOLUTE int_EVENT_RESELECT = 0
+ABSOLUTE int_EVENT_COMPLETE = 0
+ABSOLUTE int_EVENT_IDLE = 0
+ABSOLUTE int_EVENT_SELECT_FAILED = 0
+ABSOLUTE int_EVENT_BEFORE_SELECT = 0
+ABSOLUTE int_EVENT_RESELECT_FAILED = 0
+#endif
+
+ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
+ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
+ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
+ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
+ABSOLUTE NOP_insn = 0 ; NOP instruction
+
+; Pointer to message, potentially multi-byte
+ABSOLUTE msg_buf = 0
+
+; Pointer to holding area for reselection information
+ABSOLUTE reselected_identify = 0
+ABSOLUTE reselected_tag = 0
+
+; Request sense command pointer, it's a 6 byte command, should
+; be constant for all commands since we always want 16 bytes of
+; sense and we don't need to change any fields as we did under
+; SCSI-I when we actually cared about the LUN field.
+;EXTERNAL NCR53c7xx_sense ; Request sense command
+
+#if (CHIP != 700) && (CHIP != 70066)
+; dsa_schedule
+; PURPOSE : after a DISCONNECT message has been received, and pointers
+; saved, insert the current DSA structure at the head of the
+; disconnected queue and fall through to the scheduler.
+;
+; CALLS : OK
+;
+; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
+; of disconnected commands
+;
+; MODIFIES : SCRATCH, reconnect_dsa_head
+;
+; EXITS : always passes control to schedule
+
+ENTRY dsa_schedule
+dsa_schedule:
+#ifdef DEBUG
+ INT int_debug_dsa_schedule
+#endif
+
+;
+; Calculate the address of the next pointer within the DSA
+; structure of the command that is currently disconnecting
+;
+#if (CHIP == 710)
+ ; Read what should be the current DSA from memory - actual DSA
+ ; register is probably corrupt
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+#else
+ CALL dsa_to_scratch
+#endif
+ MOVE SCRATCH0 + dsa_next TO SCRATCH0
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+; Point the next field of this DSA structure at the current disconnected
+; list
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
+ DMODE_MEMORY_TO_MEMORY
+dsa_schedule_insert:
+ MOVE MEMORY 4, reconnect_dsa_head, 0
+
+; And update the head pointer.
+#if (CHIP == 710)
+ ; Read what should be the current DSA from memory - actual DSA
+ ; register is probably corrupt
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+#else
+ CALL dsa_to_scratch
+#endif
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
+ DMODE_MEMORY_TO_MEMORY
+/* Temporarily, see what happens. */
+#ifndef ORIGINAL
+#if (CHIP != 710)
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+#endif
+ CLEAR ACK
+#endif
+#if (CHIP == 710)
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+ WAIT DISCONNECT
+#ifdef EVENTS
+ INT int_EVENT_DISCONNECT;
+#endif
+#ifdef DEBUG
+ INT int_debug_disconnected
+#endif
+ JUMP schedule
+#endif
+
+;
+; select
+;
+; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
+; On success, the current DSA structure is removed from the issue
+; queue. Usually, this is entered as a fall-through from schedule,
+; although the contingent allegiance handling code will write
+; the select entry address to the DSP to restart a command as a
+; REQUEST SENSE. A message is sent (usually IDENTIFY, although
+; additional SDTR or WDTR messages may be sent). COMMAND OUT
+; is handled.
+;
+; INPUTS : DSA - SCSI command, issue_dsa_head
+;
+; CALLS : NOT OK
+;
+; MODIFIES : SCRATCH, issue_dsa_head
+;
+; EXITS : on reselection or selection, go to select_failed
+; otherwise, RETURN so control is passed back to
+; dsa_begin.
+;
+
+ENTRY select
+select:
+
+#ifdef EVENTS
+ INT int_EVENT_BEFORE_SELECT
+#endif
+
+#ifdef DEBUG
+ INT int_debug_scheduled
+#endif
+ CLEAR TARGET
+
+; XXX
+;
+; In effect, SELECTION operations are backgrounded, with execution
+; continuing until code which waits for REQ or a fatal interrupt is
+; encountered.
+;
+; So, for more performance, we could overlap the code which removes
+; the command from the NCRs issue queue with the selection, but
+; at this point I don't want to deal with the error recovery.
+;
+
+#if (CHIP != 700) && (CHIP != 70066)
+#if (CHIP == 710)
+ ; Enable selection timer
+#ifdef NO_SELECTION_TIMEOUT
+ MOVE CTEST7 & 0xff TO CTEST7
+#else
+ MOVE CTEST7 & 0xef TO CTEST7
+#endif
+#endif
+ SELECT ATN FROM dsa_select, select_failed
+ JUMP select_msgout, WHEN MSG_OUT
+ENTRY select_msgout
+select_msgout:
+#if (CHIP == 710)
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+#endif
+ MOVE FROM dsa_msgout, WHEN MSG_OUT
+#else
+ENTRY select_msgout
+ SELECT ATN 0, select_failed
+select_msgout:
+ MOVE 0, 0, WHEN MSGOUT
+#endif
+
+#ifdef EVENTS
+ INT int_EVENT_SELECT
+#endif
+ RETURN
+
+;
+; select_done
+;
+; PURPOSE: continue on to normal data transfer; called as the exit
+; point from dsa_begin.
+;
+; INPUTS: dsa
+;
+; CALLS: OK
+;
+;
+
+select_done:
+#if (CHIP == 710)
+; NOTE DSA is corrupt when we arrive here!
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+
+#ifdef DEBUG
+ENTRY select_check_dsa
+select_check_dsa:
+ INT int_debug_check_dsa
+#endif
+
+; After a successful selection, we should get either a CMD phase or
+; some transfer request negotiation message.
+
+ JUMP cmdout, WHEN CMD
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+select_msg_in:
+ CALL msg_in, WHEN MSG_IN
+ JUMP select_msg_in, WHEN MSG_IN
+
+cmdout:
+ INT int_err_unexpected_phase, WHEN NOT CMD
+#if (CHIP == 700)
+ INT int_norm_selected
+#endif
+ENTRY cmdout_cmdout
+cmdout_cmdout:
+#if (CHIP != 700) && (CHIP != 70066)
+ MOVE FROM dsa_cmdout, WHEN CMD
+#else
+ MOVE 0, 0, WHEN CMD
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+;
+; data_transfer
+; other_out
+; other_in
+; other_transfer
+;
+; PURPOSE : handle the main data transfer for a SCSI command in
+; several parts. In the first part, data_transfer, DATA_IN
+; and DATA_OUT phases are allowed, with the user provided
+; code (usually dynamically generated based on the scatter/gather
+; list associated with a SCSI command) called to handle these
+; phases.
+;
+; After control has passed to one of the user provided
+; DATA_IN or DATA_OUT routines, back calls are made to
+; other_transfer_in or other_transfer_out to handle non-DATA IN
+; and DATA OUT phases respectively, with the state of the active
+; data pointer being preserved in TEMP.
+;
+; On completion, the user code passes control to other_transfer
+; which causes DATA_IN and DATA_OUT to result in unexpected_phase
+; interrupts so that data overruns may be trapped.
+;
+; INPUTS : DSA - SCSI command
+;
+; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in
+; other_transfer
+;
+; MODIFIES : SCRATCH
+;
+; EXITS : if STATUS IN is detected, signifying command completion,
+; the NCR jumps to command_complete. If MSG IN occurs, a
+; CALL is made to msg_in. Otherwise, other_transfer runs in
+; an infinite loop.
+;
+
+ENTRY data_transfer
+data_transfer:
+ JUMP cmdout_cmdout, WHEN CMD
+ CALL msg_in, WHEN MSG_IN
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+ JUMP do_dataout, WHEN DATA_OUT
+ JUMP do_datain, WHEN DATA_IN
+ JUMP command_complete, WHEN STATUS
+ JUMP data_transfer
+ENTRY end_data_transfer
+end_data_transfer:
+
+;
+; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain
+; should be fixed up whenever the nexus changes so it can point to the
+; correct routine for that command.
+;
+
+#if (CHIP != 700) && (CHIP != 70066)
+; Nasty jump to dsa->dataout
+do_dataout:
+#if (CHIP == 710)
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+#else
+ CALL dsa_to_scratch
+#endif
+ MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
+ DMODE_MEMORY_TO_MEMORY
+dataout_to_jump:
+ MOVE MEMORY 4, 0, dataout_jump + 4
+#if (CHIP == 710)
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+dataout_jump:
+ JUMP 0
+
+; Nasty jump to dsa->dsain
+do_datain:
+#if (CHIP == 710)
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+#else
+ CALL dsa_to_scratch
+#endif
+ MOVE SCRATCH0 + dsa_datain TO SCRATCH0
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
+ DMODE_MEMORY_TO_MEMORY
+ENTRY datain_to_jump
+datain_to_jump:
+ MOVE MEMORY 4, 0, datain_jump + 4
+#if (CHIP == 710)
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+#ifdef DEBUG
+ INT int_debug_datain
+#endif
+datain_jump:
+ JUMP 0
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+
+; Note that other_out and other_in loop until a non-data phase
+; is discovered, so we only execute return statements when we
+; can go on to the next data phase block move statement.
+
+ENTRY other_out
+other_out:
+#if 0
+ INT 0x03ffdead
+#endif
+ INT int_err_unexpected_phase, WHEN CMD
+ JUMP msg_in_restart, WHEN MSG_IN
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+ INT int_err_unexpected_phase, WHEN DATA_IN
+ JUMP command_complete, WHEN STATUS
+ JUMP other_out, WHEN NOT DATA_OUT
+#if (CHIP == 710)
+; TEMP should be OK, as we got here from a call in the user dataout code.
+#endif
+ RETURN
+
+ENTRY other_in
+other_in:
+#if 0
+ INT 0x03ffdead
+#endif
+ INT int_err_unexpected_phase, WHEN CMD
+ JUMP msg_in_restart, WHEN MSG_IN
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+ JUMP command_complete, WHEN STATUS
+ JUMP other_in, WHEN NOT DATA_IN
+#if (CHIP == 710)
+; TEMP should be OK, as we got here from a call in the user datain code.
+#endif
+ RETURN
+
+
+ENTRY other_transfer
+other_transfer:
+ INT int_err_unexpected_phase, WHEN CMD
+ CALL msg_in, WHEN MSG_IN
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+ INT int_err_unexpected_phase, WHEN DATA_IN
+ JUMP command_complete, WHEN STATUS
+ JUMP other_transfer
+
+;
+; msg_in_restart
+; msg_in
+; munge_msg
+;
+; PURPOSE : process messages from a target. msg_in is called when the
+; caller hasn't read the first byte of the message. munge_message
+; is called when the caller has read the first byte of the message,
+; and left it in SFBR. msg_in_restart is called when the caller
+; hasn't read the first byte of the message, and wishes RETURN
+; to transfer control back to the address of the conditional
+; CALL instruction rather than to the instruction after it.
+;
+; Various int_* interrupts are generated when the host system
+; needs to intervene, as is the case with SDTR, WDTR, and
+; INITIATE RECOVERY messages.
+;
+; When the host system handles one of these interrupts,
+; it can respond by reentering at reject_message,
+; which rejects the message and returns control to
+; the caller of msg_in or munge_msg, accept_message
+; which clears ACK and returns control, or reply_message
+; which sends the message pointed to by the DSA
+; msgout_other table indirect field.
+;
+; DISCONNECT messages are handled by moving the command
+; to the reconnect_dsa_queue.
+#if (CHIP == 710)
+; NOTE: DSA should be valid when we get here - we cannot save both it
+; and TEMP in this routine.
+#endif
+;
+; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
+; only)
+;
+; CALLS : NO. The TEMP register isn't backed up to allow nested calls.
+;
+; MODIFIES : SCRATCH, DSA on DISCONNECT
+;
+; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
+; and normal return from message handlers running under
+; Linux, control is returned to the caller. Receipt
+; of DISCONNECT messages pass control to dsa_schedule.
+;
+ENTRY msg_in_restart
+msg_in_restart:
+; XXX - hackish
+;
+; Since it's easier to debug changes to the statically
+; compiled code, rather than the dynamically generated
+; stuff, such as
+;
+; MOVE x, y, WHEN data_phase
+; CALL other_z, WHEN NOT data_phase
+; MOVE x, y, WHEN data_phase
+;
+; I'd like to have certain routines (notably the message handler)
+; restart on the conditional call rather than the next instruction.
+;
+; So, subtract 8 from the return address
+
+ MOVE TEMP0 + 0xf8 TO TEMP0
+ MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY
+ MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY
+ MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY
+
+ENTRY msg_in
+msg_in:
+ MOVE 1, msg_buf, WHEN MSG_IN
+
+munge_msg:
+ JUMP munge_extended, IF 0x01 ; EXTENDED MESSAGE
+ JUMP munge_2, IF 0x20, AND MASK 0xdf ; two byte message
+;
+; XXX - I've seen a handful of broken SCSI devices which fail to issue
+; a SAVE POINTERS message before disconnecting in the middle of
+; a transfer, assuming that the DATA POINTER will be implicitly
+; restored.
+;
+; Historically, I've often done an implicit save when the DISCONNECT
+; message is processed. We may want to consider having the option of
+; doing that here.
+;
+ JUMP munge_save_data_pointer, IF 0x02 ; SAVE DATA POINTER
+ JUMP munge_restore_pointers, IF 0x03 ; RESTORE POINTERS
+ JUMP munge_disconnect, IF 0x04 ; DISCONNECT
+ INT int_msg_1, IF 0x07 ; MESSAGE REJECT
+ INT int_msg_1, IF 0x0f ; INITIATE RECOVERY
+#ifdef EVENTS
+ INT int_EVENT_SELECT_FAILED
+#endif
+ JUMP reject_message
+
+munge_2:
+ JUMP reject_message
+;
+; The SCSI standard allows targets to recover from transient
+; error conditions by backing up the data pointer with a
+; RESTORE POINTERS message.
+;
+; So, we must save and restore the _residual_ code as well as
+; the current instruction pointer. Because of this messiness,
+; it is simpler to put dynamic code in the dsa for this and to
+; just do a simple jump down there.
+;
+
+munge_save_data_pointer:
+#if (CHIP == 710)
+ ; We have something in TEMP here, so first we must save that
+ MOVE TEMP0 TO SFBR
+ MOVE SFBR TO SCRATCH0
+ MOVE TEMP1 TO SFBR
+ MOVE SFBR TO SCRATCH1
+ MOVE TEMP2 TO SFBR
+ MOVE SFBR TO SCRATCH2
+ MOVE TEMP3 TO SFBR
+ MOVE SFBR TO SCRATCH3
+ MOVE MEMORY 4, addr_scratch, jump_temp + 4
+ ; Now restore DSA
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+ MOVE DSA0 + dsa_save_data_pointer TO SFBR
+ MOVE SFBR TO SCRATCH0
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+ MOVE SFBR TO SCRATCH1
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+ MOVE SFBR TO SCRATCH2
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+ MOVE SFBR TO SCRATCH3
+
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4
+ DMODE_MEMORY_TO_MEMORY
+jump_dsa_save:
+ JUMP 0
+
+munge_restore_pointers:
+#if (CHIP == 710)
+ ; The code at dsa_restore_pointers will RETURN, but we don't care
+ ; about TEMP here, as it will overwrite it anyway.
+#endif
+ MOVE DSA0 + dsa_restore_pointers TO SFBR
+ MOVE SFBR TO SCRATCH0
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+ MOVE SFBR TO SCRATCH1
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+ MOVE SFBR TO SCRATCH2
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+ MOVE SFBR TO SCRATCH3
+
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4
+ DMODE_MEMORY_TO_MEMORY
+jump_dsa_restore:
+ JUMP 0
+
+
+munge_disconnect:
+#ifdef DEBUG
+ INT int_debug_disconnect_msg
+#endif
+
+/*
+ * Before, we overlapped processing with waiting for disconnect, but
+ * debugging was beginning to appear messy. Temporarily move things
+ * to just before the WAIT DISCONNECT.
+ */
+
+#ifdef ORIGINAL
+#if (CHIP == 710)
+; Following clears Unexpected Disconnect bit. What do we do?
+#else
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+#endif
+ CLEAR ACK
+#endif
+
+#if (CHIP != 700) && (CHIP != 70066)
+ JUMP dsa_schedule
+#else
+ WAIT DISCONNECT
+ INT int_norm_disconnected
+#endif
+
+munge_extended:
+ CLEAR ACK
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+ MOVE 1, msg_buf + 1, WHEN MSG_IN
+ JUMP munge_extended_2, IF 0x02
+ JUMP munge_extended_3, IF 0x03
+ JUMP reject_message
+
+munge_extended_2:
+ CLEAR ACK
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+ JUMP reject_message, IF NOT 0x02 ; Must be WDTR
+ CLEAR ACK
+ MOVE 1, msg_buf + 3, WHEN MSG_IN
+ INT int_msg_wdtr
+
+munge_extended_3:
+ CLEAR ACK
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+ JUMP reject_message, IF NOT 0x01 ; Must be SDTR
+ CLEAR ACK
+ MOVE 2, msg_buf + 3, WHEN MSG_IN
+ INT int_msg_sdtr
+
+ENTRY reject_message
+reject_message:
+ SET ATN
+ CLEAR ACK
+ MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
+ RETURN
+
+ENTRY accept_message
+accept_message:
+ CLEAR ATN
+ CLEAR ACK
+ RETURN
+
+ENTRY respond_message
+respond_message:
+ SET ATN
+ CLEAR ACK
+ MOVE FROM dsa_msgout_other, WHEN MSG_OUT
+ RETURN
+
+;
+; command_complete
+;
+; PURPOSE : handle command termination when STATUS IN is detected by reading
+; a status byte followed by a command termination message.
+;
+; Normal termination results in an INTFLY instruction, and
+; the host system can pick out which command terminated by
+; examining the MESSAGE and STATUS buffers of all currently
+; executing commands;
+;
+; Abnormal (CHECK_CONDITION) termination results in an
+; int_err_check_condition interrupt so that a REQUEST SENSE
+; command can be issued out-of-order so that no other command
+; clears the contingent allegiance condition.
+;
+;
+; INPUTS : DSA - command
+;
+; CALLS : OK
+;
+; EXITS : On successful termination, control is passed to schedule.
+; On abnormal termination, the user will usually modify the
+; DSA fields and corresponding buffers and return control
+; to select.
+;
+
+ENTRY command_complete
+command_complete:
+ MOVE FROM dsa_status, WHEN STATUS
+#if (CHIP != 700) && (CHIP != 70066)
+ MOVE SFBR TO SCRATCH0 ; Save status
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+ENTRY command_complete_msgin
+command_complete_msgin:
+ MOVE FROM dsa_msgin, WHEN MSG_IN
+; Indicate that we should be expecting a disconnect
+#if (CHIP != 710)
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+#else
+ ; Above code cleared the Unexpected Disconnect bit, what do we do?
+#endif
+ CLEAR ACK
+#if (CHIP != 700) && (CHIP != 70066)
+ WAIT DISCONNECT
+
+;
+; The SCSI specification states that when a UNIT ATTENTION condition
+; is pending, as indicated by a CHECK CONDITION status message,
+; the target shall revert to asynchronous transfers. Since
+; synchronous transfers parameters are maintained on a per INITIATOR/TARGET
+; basis, and returning control to our scheduler could work on a command
+; running on another lun on that target using the old parameters, we must
+; interrupt the host processor to get them changed, or change them ourselves.
+;
+; Once SCSI-II tagged queueing is implemented, things will be even more
+; hairy, since contingent allegiance conditions exist on a per-target/lun
+; basis, and issuing a new command with a different tag would clear it.
+; In these cases, we must interrupt the host processor to get a request
+; added to the HEAD of the queue with the request sense command, or we
+; must automatically issue the request sense command.
+
+#if 0
+ MOVE SCRATCH0 TO SFBR
+ JUMP command_failed, IF 0x02
+#endif
+#if (CHIP == 710)
+#if defined(MVME16x_INTFLY)
+; For MVME16x (ie CHIP=710) we will force an INTFLY by triggering a software
+; interrupt (SW7). We can use SCRATCH, as we are about to jump to
+; schedule, which corrupts it anyway. Will probably remove this later,
+; but want to check performance effects first.
+
+#define INTFLY_ADDR 0xfff40070
+
+ MOVE 0 TO SCRATCH0
+ MOVE 0x80 TO SCRATCH1
+ MOVE 0 TO SCRATCH2
+ MOVE 0 TO SCRATCH3
+ MOVE MEMORY 4, addr_scratch, INTFLY_ADDR
+#else
+ INT int_norm_emulateintfly
+#endif
+#else
+ INTFLY
+#endif
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+#if (CHIP == 710)
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+#ifdef EVENTS
+ INT int_EVENT_COMPLETE
+#endif
+#if (CHIP != 700) && (CHIP != 70066)
+ JUMP schedule
+command_failed:
+ INT int_err_check_condition
+#else
+ INT int_norm_command_complete
+#endif
+
+;
+; wait_reselect
+;
+; PURPOSE : This is essentially the idle routine, where control lands
+; when there are no new processes to schedule. wait_reselect
+; waits for reselection, selection, and new commands.
+;
+; When a successful reselection occurs, with the aid
+; of fixed up code in each DSA, wait_reselect walks the
+; reconnect_dsa_queue, asking each dsa if the target ID
+; and LUN match its.
+;
+; If a match is found, a call is made back to reselected_ok,
+; which through the miracles of self modifying code, extracts
+; the found DSA from the reconnect_dsa_queue and then
+; returns control to the DSAs thread of execution.
+;
+; INPUTS : NONE
+;
+; CALLS : OK
+;
+; MODIFIES : DSA,
+;
+; EXITS : On successful reselection, control is returned to the
+; DSA which called reselected_ok. If the WAIT RESELECT
+; was interrupted by a new commands arrival signaled by
+; SIG_P, control is passed to schedule. If the NCR is
+; selected, the host system is interrupted with an
+; int_err_selected which is usually responded to by
+; setting DSP to the target_abort address.
+
+ENTRY wait_reselect
+wait_reselect:
+#ifdef EVENTS
+ int int_EVENT_IDLE
+#endif
+#ifdef DEBUG
+ int int_debug_idle
+#endif
+ WAIT RESELECT wait_reselect_failed
+
+reselected:
+#ifdef EVENTS
+ int int_EVENT_RESELECT
+#endif
+ CLEAR TARGET
+ DMODE_MEMORY_TO_MEMORY
+ ; Read all data needed to reestablish the nexus -
+ MOVE 1, reselected_identify, WHEN MSG_IN
+ ; We used to CLEAR ACK here.
+#if (CHIP != 700) && (CHIP != 70066)
+#ifdef DEBUG
+ int int_debug_reselected
+#endif
+
+ ; Point DSA at the current head of the disconnected queue.
+ DMODE_MEMORY_TO_NCR
+ MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
+ DMODE_MEMORY_TO_MEMORY
+#if (CHIP == 710)
+ MOVE MEMORY 4, addr_scratch, saved_dsa
+#else
+ CALL scratch_to_dsa
+#endif
+
+ ; Fix the update-next pointer so that the reconnect_dsa_head
+ ; pointer is the one that will be updated if this DSA is a hit
+ ; and we remove it from the queue.
+
+ MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok_patch + 8
+#if (CHIP == 710)
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+
+ENTRY reselected_check_next
+reselected_check_next:
+#ifdef DEBUG
+ INT int_debug_reselect_check
+#endif
+ ; Check for a NULL pointer.
+ MOVE DSA0 TO SFBR
+ JUMP reselected_not_end, IF NOT 0
+ MOVE DSA1 TO SFBR
+ JUMP reselected_not_end, IF NOT 0
+ MOVE DSA2 TO SFBR
+ JUMP reselected_not_end, IF NOT 0
+ MOVE DSA3 TO SFBR
+ JUMP reselected_not_end, IF NOT 0
+ INT int_err_unexpected_reselect
+
+reselected_not_end:
+ ;
+ ; XXX the ALU is only eight bits wide, and the assembler
+ ; wont do the dirt work for us. As long as dsa_check_reselect
+ ; is negative, we need to sign extend with 1 bits to the full
+ ; 32 bit width of the address.
+ ;
+ ; A potential work around would be to have a known alignment
+ ; of the DSA structure such that the base address plus
+ ; dsa_check_reselect doesn't require carrying from bytes
+ ; higher than the LSB.
+ ;
+
+ MOVE DSA0 TO SFBR
+ MOVE SFBR + dsa_check_reselect TO SCRATCH0
+ MOVE DSA1 TO SFBR
+ MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
+ MOVE DSA2 TO SFBR
+ MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
+ MOVE DSA3 TO SFBR
+ MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
+
+ DMODE_NCR_TO_MEMORY
+ MOVE MEMORY 4, addr_scratch, reselected_check + 4
+ DMODE_MEMORY_TO_MEMORY
+#if (CHIP == 710)
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+#endif
+reselected_check:
+ JUMP 0
+
+
+;
+;
+#if (CHIP == 710)
+; We have problems here - the memory move corrupts TEMP and DSA. This
+; routine is called from DSA code, and patched from many places. Scratch
+; is probably free when it is called.
+; We have to:
+; copy temp to scratch, one byte at a time
+; write scratch to patch a jump in place of the return
+; do the move memory
+; jump to the patched in return address
+; DSA is corrupt when we get here, and can be left corrupt
+
+ENTRY reselected_ok
+reselected_ok:
+ MOVE TEMP0 TO SFBR
+ MOVE SFBR TO SCRATCH0
+ MOVE TEMP1 TO SFBR
+ MOVE SFBR TO SCRATCH1
+ MOVE TEMP2 TO SFBR
+ MOVE SFBR TO SCRATCH2
+ MOVE TEMP3 TO SFBR
+ MOVE SFBR TO SCRATCH3
+ MOVE MEMORY 4, addr_scratch, reselected_ok_jump + 4
+reselected_ok_patch:
+ MOVE MEMORY 4, 0, 0
+reselected_ok_jump:
+ JUMP 0
+#else
+ENTRY reselected_ok
+reselected_ok:
+reselected_ok_patch:
+ MOVE MEMORY 4, 0, 0 ; Patched : first word
+ ; is address of
+ ; successful dsa_next
+ ; Second word is last
+ ; unsuccessful dsa_next,
+ ; starting with
+ ; dsa_reconnect_head
+ ; We used to CLEAR ACK here.
+#ifdef DEBUG
+ INT int_debug_reselected_ok
+#endif
+#ifdef DEBUG
+ INT int_debug_check_dsa
+#endif
+ RETURN ; Return control to where
+#endif
+#else
+ INT int_norm_reselected
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+selected:
+ INT int_err_selected;
+
+;
+; A select or reselect failure can be caused by one of two conditions :
+; 1. SIG_P was set. This will be the case if the user has written
+; a new value to a previously NULL head of the issue queue.
+;
+; 2. The NCR53c810 was selected or reselected by another device.
+;
+; 3. The bus was already busy since we were selected or reselected
+; before starting the command.
+
+wait_reselect_failed:
+#ifdef EVENTS
+ INT int_EVENT_RESELECT_FAILED
+#endif
+; Check selected bit.
+#if (CHIP == 710)
+ ; Must work out how to tell if we are selected....
+#else
+ MOVE SIST0 & 0x20 TO SFBR
+ JUMP selected, IF 0x20
+#endif
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+ JUMP schedule, IF 0x40
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+ MOVE ISTAT & 0x08 TO SFBR
+ JUMP reselected, IF 0x08
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+#if 0
+ JUMP schedule
+#else
+ INT int_debug_panic
+#endif
+
+
+select_failed:
+#if (CHIP == 710)
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+#endif
+#ifdef EVENTS
+ int int_EVENT_SELECT_FAILED
+#endif
+; Otherwise, mask the selected and reselected bits off SIST0
+#if (CHIP ==710)
+ ; Let's assume we don't get selected for now
+ MOVE SSTAT0 & 0x10 TO SFBR
+#else
+ MOVE SIST0 & 0x30 TO SFBR
+ JUMP selected, IF 0x20
+#endif
+ JUMP reselected, IF 0x10
+; If SIGP is set, the user just gave us another command, and
+; we should restart or return to the scheduler.
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+ JUMP select, IF 0x40
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+; FIXME: is this really necessary?
+ MOVE ISTAT & 0x08 TO SFBR
+ JUMP reselected, IF 0x08
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+#if 0
+ JUMP schedule
+#else
+ INT int_debug_panic
+#endif
+
+;
+; test_1
+; test_2
+;
+; PURPOSE : run some verification tests on the NCR. test_1
+; copies test_src to test_dest and interrupts the host
+; processor, testing for cache coherency and interrupt
+; problems in the processes.
+;
+; test_2 runs a command with offsets relative to the
+; DSA on entry, and is useful for miscellaneous experimentation.
+;
+
+; Verify that interrupts are working correctly and that we don't
+; have a cache invalidation problem.
+
+ABSOLUTE test_src = 0, test_dest = 0
+ENTRY test_1
+test_1:
+ MOVE MEMORY 4, test_src, test_dest
+ INT int_test_1
+
+;
+; Run arbitrary commands, with test code establishing a DSA
+;
+
+ENTRY test_2
+test_2:
+ CLEAR TARGET
+#if (CHIP == 710)
+ ; Enable selection timer
+#ifdef NO_SELECTION_TIMEOUT
+ MOVE CTEST7 & 0xff TO CTEST7
+#else
+ MOVE CTEST7 & 0xef TO CTEST7
+#endif
+#endif
+ SELECT ATN FROM 0, test_2_fail
+ JUMP test_2_msgout, WHEN MSG_OUT
+ENTRY test_2_msgout
+test_2_msgout:
+#if (CHIP == 710)
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+#endif
+ MOVE FROM 8, WHEN MSG_OUT
+ MOVE FROM 16, WHEN CMD
+ MOVE FROM 24, WHEN DATA_IN
+ MOVE FROM 32, WHEN STATUS
+ MOVE FROM 40, WHEN MSG_IN
+#if (CHIP != 710)
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+#endif
+ CLEAR ACK
+ WAIT DISCONNECT
+test_2_fail:
+#if (CHIP == 710)
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+#endif
+ INT int_test_2
+
+ENTRY debug_break
+debug_break:
+ INT int_debug_break
+
+;
+; initiator_abort
+; target_abort
+;
+; PURPOSE : Abort the currently established nexus from with initiator
+; or target mode.
+;
+;
+
+ENTRY target_abort
+target_abort:
+ SET TARGET
+ DISCONNECT
+ CLEAR TARGET
+ JUMP schedule
+
+ENTRY initiator_abort
+initiator_abort:
+ SET ATN
+;
+; The SCSI-I specification says that targets may go into MSG out at
+; their leisure upon receipt of the ATN single. On all versions of the
+; specification, we can't change phases until REQ transitions true->false,
+; so we need to sink/source one byte of data to allow the transition.
+;
+; For the sake of safety, we'll only source one byte of data in all
+; cases, but to accommodate the SCSI-I dain bramage, we'll sink an
+; arbitrary number of bytes.
+ JUMP spew_cmd, WHEN CMD
+ JUMP eat_msgin, WHEN MSG_IN
+ JUMP eat_datain, WHEN DATA_IN
+ JUMP eat_status, WHEN STATUS
+ JUMP spew_dataout, WHEN DATA_OUT
+ JUMP sated
+spew_cmd:
+ MOVE 1, NCR53c7xx_zero, WHEN CMD
+ JUMP sated
+eat_msgin:
+ MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
+ JUMP eat_msgin, WHEN MSG_IN
+ JUMP sated
+eat_status:
+ MOVE 1, NCR53c7xx_sink, WHEN STATUS
+ JUMP eat_status, WHEN STATUS
+ JUMP sated
+eat_datain:
+ MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
+ JUMP eat_datain, WHEN DATA_IN
+ JUMP sated
+spew_dataout:
+ MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
+sated:
+#if (CHIP != 710)
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+#endif
+ MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+ WAIT DISCONNECT
+ INT int_norm_aborted
+
+#if (CHIP != 710)
+;
+; dsa_to_scratch
+; scratch_to_dsa
+;
+; PURPOSE :
+; The NCR chips cannot do a move memory instruction with the DSA register
+; as the source or destination. So, we provide a couple of subroutines
+; that let us switch between the DSA register and scratch register.
+;
+; Memory moves to/from the DSPS register also don't work, but we
+; don't use them.
+;
+;
+
+
+dsa_to_scratch:
+ MOVE DSA0 TO SFBR
+ MOVE SFBR TO SCRATCH0
+ MOVE DSA1 TO SFBR
+ MOVE SFBR TO SCRATCH1
+ MOVE DSA2 TO SFBR
+ MOVE SFBR TO SCRATCH2
+ MOVE DSA3 TO SFBR
+ MOVE SFBR TO SCRATCH3
+ RETURN
+
+scratch_to_dsa:
+ MOVE SCRATCH0 TO SFBR
+ MOVE SFBR TO DSA0
+ MOVE SCRATCH1 TO SFBR
+ MOVE SFBR TO DSA1
+ MOVE SCRATCH2 TO SFBR
+ MOVE SFBR TO DSA2
+ MOVE SCRATCH3 TO SFBR
+ MOVE SFBR TO DSA3
+ RETURN
+#endif
+
+#if (CHIP == 710)
+; Little patched jump, used to overcome problems with TEMP getting
+; corrupted on memory moves.
+
+jump_temp:
+ JUMP 0
+#endif
diff --git a/drivers/scsi/53c7xx_d.h_shipped b/drivers/scsi/53c7xx_d.h_shipped
new file mode 100644
index 000000000000..21d31b08ec31
--- /dev/null
+++ b/drivers/scsi/53c7xx_d.h_shipped
@@ -0,0 +1,2874 @@
+/* DO NOT EDIT - Generated automatically by script_asm.pl */
+static u32 SCRIPT[] = {
+/*
+
+
+
+
+
+; 53c710 driver. Modified from Drew Eckhardts driver
+; for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
+;
+; I have left the script for the 53c8xx family in here, as it is likely
+; to be useful to see what I changed when bug hunting.
+
+; NCR 53c810 driver, main script
+; Sponsored by
+; iX Multiuser Multitasking Magazine
+; hm@ix.de
+;
+; Copyright 1993, 1994, 1995 Drew Eckhardt
+; Visionary Computing
+; (Unix and Linux consulting and custom programming)
+; drew@PoohSticks.ORG
+; +1 (303) 786-7975
+;
+; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+;
+; PRE-ALPHA
+;
+; For more information, please consult
+;
+; NCR 53C810
+; PCI-SCSI I/O Processor
+; Data Manual
+;
+; NCR 53C710
+; SCSI I/O Processor
+; Programmers Guide
+;
+; NCR Microelectronics
+; 1635 Aeroplaza Drive
+; Colorado Springs, CO 80916
+; 1+ (719) 578-3400
+;
+; Toll free literature number
+; +1 (800) 334-5454
+;
+; IMPORTANT : This code is self modifying due to the limitations of
+; the NCR53c7,8xx series chips. Persons debugging this code with
+; the remote debugger should take this into account, and NOT set
+; breakpoints in modified instructions.
+;
+; Design:
+; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard
+; microcontroller using a simple instruction set.
+;
+; So, to minimize the effects of interrupt latency, and to maximize
+; throughput, this driver offloads the practical maximum amount
+; of processing to the SCSI chip while still maintaining a common
+; structure.
+;
+; Where tradeoffs were needed between efficiency on the older
+; chips and the newer NCR53c800 series, the NCR53c800 series
+; was chosen.
+;
+; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
+; automate SCSI transfers without host processor intervention, this
+; isn't the case with the NCR53c710 and newer chips which allow
+;
+; - reads and writes to the internal registers from within the SCSI
+; scripts, allowing the SCSI SCRIPTS(tm) code to save processor
+; state so that multiple threads of execution are possible, and also
+; provide an ALU for loop control, etc.
+;
+; - table indirect addressing for some instructions. This allows
+; pointers to be located relative to the DSA ((Data Structure
+; Address) register.
+;
+; These features make it possible to implement a mailbox style interface,
+; where the same piece of code is run to handle I/O for multiple threads
+; at once minimizing our need to relocate code. Since the NCR53c700/
+; NCR53c800 series have a unique combination of features, making a
+; a standard ingoing/outgoing mailbox system, costly, I've modified it.
+;
+; - Mailboxes are a mixture of code and data. This lets us greatly
+; simplify the NCR53c810 code and do things that would otherwise
+; not be possible.
+;
+; The saved data pointer is now implemented as follows :
+;
+; Control flow has been architected such that if control reaches
+; munge_save_data_pointer, on a restore pointers message or
+; reconnection, a jump to the address formerly in the TEMP register
+; will allow the SCSI command to resume execution.
+;
+
+;
+; Note : the DSA structures must be aligned on 32 bit boundaries,
+; since the source and destination of MOVE MEMORY instructions
+; must share the same alignment and this is the alignment of the
+; NCR registers.
+;
+
+; For some systems (MVME166, for example) dmode is always the same, so don't
+; waste time writing it
+
+
+
+
+
+
+
+
+
+
+
+ABSOLUTE dsa_temp_lun = 0 ; Patch to lun for current dsa
+ABSOLUTE dsa_temp_next = 0 ; Patch to dsa next for current dsa
+ABSOLUTE dsa_temp_addr_next = 0 ; Patch to address of dsa next address
+ ; for current dsa
+ABSOLUTE dsa_temp_sync = 0 ; Patch to address of per-target
+ ; sync routine
+ABSOLUTE dsa_sscf_710 = 0 ; Patch to address of per-target
+ ; sscf value (53c710)
+ABSOLUTE dsa_temp_target = 0 ; Patch to id for current dsa
+ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command
+ ; saved data pointer
+ABSOLUTE dsa_temp_addr_residual = 0 ; Patch to address of per-command
+ ; current residual code
+ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command
+ ; saved residual code
+ABSOLUTE dsa_temp_addr_new_value = 0 ; Address of value for JUMP operand
+ABSOLUTE dsa_temp_addr_array_value = 0 ; Address to copy to
+ABSOLUTE dsa_temp_addr_dsa_value = 0 ; Address of this DSA value
+
+;
+; Once a device has initiated reselection, we need to compare it
+; against the singly linked list of commands which have disconnected
+; and are pending reselection. These commands are maintained in
+; an unordered singly linked list of DSA structures, through the
+; DSA pointers at their 'centers' headed by the reconnect_dsa_head
+; pointer.
+;
+; To avoid complications in removing commands from the list,
+; I minimize the amount of expensive (at eight operations per
+; addition @ 500-600ns each) pointer operations which must
+; be done in the NCR driver by precomputing them on the
+; host processor during dsa structure generation.
+;
+; The fixed-up per DSA code knows how to recognize the nexus
+; associated with the corresponding SCSI command, and modifies
+; the source and destination pointers for the MOVE MEMORY
+; instruction which is executed when reselected_ok is called
+; to remove the command from the list. Similarly, DSA is
+; loaded with the address of the next DSA structure and
+; reselected_check_next is called if a failure occurs.
+;
+; Perhaps more concisely, the net effect of the mess is
+;
+; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head,
+; src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) {
+; src = &dsa->next;
+; if (target_id == dsa->id && target_lun == dsa->lun) {
+; *dest = *src;
+; break;
+; }
+; }
+;
+; if (!dsa)
+; error (int_err_unexpected_reselect);
+; else
+; longjmp (dsa->jump_resume, 0);
+;
+;
+
+
+; Define DSA structure used for mailboxes
+ENTRY dsa_code_template
+dsa_code_template:
+ENTRY dsa_code_begin
+dsa_code_begin:
+; RGH: Don't care about TEMP and DSA here
+
+ MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch
+
+at 0x00000000 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+ MOVE MEMORY 4, addr_scratch, saved_dsa
+
+at 0x00000003 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ ; We are about to go and select the device, so must set SSCF bits
+ MOVE MEMORY 4, dsa_sscf_710, addr_scratch
+
+at 0x00000006 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+ MOVE SCRATCH3 TO SFBR
+
+at 0x00000009 : */ 0x72370000,0x00000000,
+/*
+
+
+
+ MOVE SFBR TO SBCL
+
+at 0x0000000b : */ 0x6a0b0000,0x00000000,
+/*
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x0000000d : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+ CALL select
+
+at 0x00000010 : */ 0x88080000,0x000001f8,
+/*
+; Handle the phase mismatch which may have resulted from the
+; MOVE FROM dsa_msgout if we returned here. The CLEAR ATN
+; may or may not be necessary, and we should update script_asm.pl
+; to handle multiple pieces.
+ CLEAR ATN
+
+at 0x00000012 : */ 0x60000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000014 : */ 0x60000040,0x00000000,
+/*
+
+; Replace second operand with address of JUMP instruction dest operand
+; in schedule table for this DSA. Becomes dsa_jump_dest in 53c7,8xx.c.
+ENTRY dsa_code_fix_jump
+dsa_code_fix_jump:
+ MOVE MEMORY 4, NOP_insn, 0
+
+at 0x00000016 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ JUMP select_done
+
+at 0x00000019 : */ 0x80080000,0x00000230,
+/*
+
+; wrong_dsa loads the DSA register with the value of the dsa_next
+; field.
+;
+wrong_dsa:
+
+; NOTE DSA is corrupt when we arrive here!
+
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the destination address is the address of the OLD
+; next pointer.
+;
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 8
+
+at 0x0000001b : */ 0xc0000004,0x00000000,0x000007ec,
+/*
+
+;
+; Move the _contents_ of the next pointer into the DSA register as
+; the next I_T_L or I_T_L_Q tupple to check against the established
+; nexus.
+;
+ MOVE MEMORY 4, dsa_temp_next, addr_scratch
+
+at 0x0000001e : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+ MOVE MEMORY 4, addr_scratch, saved_dsa
+
+at 0x00000021 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x00000024 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+ JUMP reselected_check_next
+
+at 0x00000027 : */ 0x80080000,0x000006f0,
+/*
+
+ABSOLUTE dsa_save_data_pointer = 0
+ENTRY dsa_code_save_data_pointer
+dsa_code_save_data_pointer:
+
+ ; When we get here, TEMP has been saved in jump_temp+4, DSA is corrupt
+ ; We MUST return with DSA correct
+ MOVE MEMORY 4, jump_temp+4, dsa_temp_addr_saved_pointer
+
+at 0x00000029 : */ 0xc0000004,0x000009c8,0x00000000,
+/*
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
+
+at 0x0000002c : */ 0xc0000018,0x00000000,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000002f : */ 0x60000040,0x00000000,
+/*
+
+
+
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x00000031 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ JUMP jump_temp
+
+at 0x00000034 : */ 0x80080000,0x000009c4,
+/*
+
+ABSOLUTE dsa_restore_pointers = 0
+ENTRY dsa_code_restore_pointers
+dsa_code_restore_pointers:
+
+ ; TEMP and DSA are corrupt when we get here, but who cares!
+ MOVE MEMORY 4, dsa_temp_addr_saved_pointer, jump_temp + 4
+
+at 0x00000036 : */ 0xc0000004,0x00000000,0x000009c8,
+/*
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
+
+at 0x00000039 : */ 0xc0000018,0x00000000,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000003c : */ 0x60000040,0x00000000,
+/*
+ ; Restore DSA, note we don't care about TEMP
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x0000003e : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+ JUMP jump_temp
+
+at 0x00000041 : */ 0x80080000,0x000009c4,
+/*
+
+
+ABSOLUTE dsa_check_reselect = 0
+; dsa_check_reselect determines whether or not the current target and
+; lun match the current DSA
+ENTRY dsa_code_check_reselect
+dsa_code_check_reselect:
+
+
+
+ MOVE LCRC TO SFBR ; LCRC has our ID and his ID bits set
+
+at 0x00000043 : */ 0x72230000,0x00000000,
+/*
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0x80
+
+at 0x00000045 : */ 0x80848000,0x00ffff50,
+/*
+
+
+
+
+
+;
+; Hack - move to scratch first, since SFBR is not writeable
+; via the CPU and hence a MOVE MEMORY instruction.
+;
+
+ MOVE MEMORY 1, reselected_identify, addr_scratch
+
+at 0x00000047 : */ 0xc0000001,0x00000000,0x00000000,
+/*
+
+
+ ; BIG ENDIAN ON MVME16x
+ MOVE SCRATCH3 TO SFBR
+
+at 0x0000004a : */ 0x72370000,0x00000000,
+/*
+
+
+
+; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
+; Are you sure about that? richard@sleepie.demon.co.uk
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8
+
+at 0x0000004c : */ 0x8084f800,0x00ffff34,
+/*
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the source address is the address of this dsa's
+; next pointer.
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 4
+
+at 0x0000004e : */ 0xc0000004,0x00000000,0x000007e8,
+/*
+ CALL reselected_ok
+
+at 0x00000051 : */ 0x88080000,0x00000798,
+/*
+
+; Restore DSA following memory moves in reselected_ok
+; dsa_temp_sync doesn't really care about DSA, but it has an
+; optional debug INT so a valid DSA is a good idea.
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x00000053 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+ CALL dsa_temp_sync
+
+at 0x00000056 : */ 0x88080000,0x00000000,
+/*
+; Release ACK on the IDENTIFY message _after_ we've set the synchronous
+; transfer parameters!
+ CLEAR ACK
+
+at 0x00000058 : */ 0x60000040,0x00000000,
+/*
+; Implicitly restore pointers on reselection, so a RETURN
+; will transfer control back to the right spot.
+ CALL REL (dsa_code_restore_pointers)
+
+at 0x0000005a : */ 0x88880000,0x00ffff68,
+/*
+ RETURN
+
+at 0x0000005c : */ 0x90080000,0x00000000,
+/*
+ENTRY dsa_zero
+dsa_zero:
+ENTRY dsa_code_template_end
+dsa_code_template_end:
+
+; Perform sanity check for dsa_fields_start == dsa_code_template_end -
+; dsa_zero, puke.
+
+ABSOLUTE dsa_fields_start = 0 ; Sanity marker
+ ; pad 48 bytes (fix this RSN)
+ABSOLUTE dsa_next = 48 ; len 4 Next DSA
+ ; del 4 Previous DSA address
+ABSOLUTE dsa_cmnd = 56 ; len 4 Scsi_Cmnd * for this thread.
+ABSOLUTE dsa_select = 60 ; len 4 Device ID, Period, Offset for
+ ; table indirect select
+ABSOLUTE dsa_msgout = 64 ; len 8 table indirect move parameter for
+ ; select message
+ABSOLUTE dsa_cmdout = 72 ; len 8 table indirect move parameter for
+ ; command
+ABSOLUTE dsa_dataout = 80 ; len 4 code pointer for dataout
+ABSOLUTE dsa_datain = 84 ; len 4 code pointer for datain
+ABSOLUTE dsa_msgin = 88 ; len 8 table indirect move for msgin
+ABSOLUTE dsa_status = 96 ; len 8 table indirect move for status byte
+ABSOLUTE dsa_msgout_other = 104 ; len 8 table indirect for normal message out
+ ; (Synchronous transfer negotiation, etc).
+ABSOLUTE dsa_end = 112
+
+ABSOLUTE schedule = 0 ; Array of JUMP dsa_begin or JUMP (next),
+ ; terminated by a call to JUMP wait_reselect
+
+; Linked lists of DSA structures
+ABSOLUTE reconnect_dsa_head = 0 ; Link list of DSAs which can reconnect
+ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable containing
+ ; address of reconnect_dsa_head
+
+; These select the source and destination of a MOVE MEMORY instruction
+ABSOLUTE dmode_memory_to_memory = 0x0
+ABSOLUTE dmode_memory_to_ncr = 0x0
+ABSOLUTE dmode_ncr_to_memory = 0x0
+
+ABSOLUTE addr_scratch = 0x0
+ABSOLUTE addr_temp = 0x0
+
+ABSOLUTE saved_dsa = 0x0
+ABSOLUTE emulfly = 0x0
+ABSOLUTE addr_dsa = 0x0
+
+
+
+; Interrupts -
+; MSB indicates type
+; 0 handle error condition
+; 1 handle message
+; 2 handle normal condition
+; 3 debugging interrupt
+; 4 testing interrupt
+; Next byte indicates specific error
+
+; XXX not yet implemented, I'm not sure if I want to -
+; Next byte indicates the routine the error occurred in
+; The LSB indicates the specific place the error occurred
+
+ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
+ABSOLUTE int_err_selected = 0x00010000 ; SELECTED (nee RESELECTED)
+ABSOLUTE int_err_unexpected_reselect = 0x00020000
+ABSOLUTE int_err_check_condition = 0x00030000
+ABSOLUTE int_err_no_phase = 0x00040000
+ABSOLUTE int_msg_wdtr = 0x01000000 ; WDTR message received
+ABSOLUTE int_msg_sdtr = 0x01010000 ; SDTR received
+ABSOLUTE int_msg_1 = 0x01020000 ; single byte special message
+ ; received
+
+ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
+ ; registers.
+ABSOLUTE int_norm_reselect_complete = 0x02010000 ; Nexus established
+ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
+ABSOLUTE int_norm_disconnected = 0x02030000 ; Disconnected
+ABSOLUTE int_norm_aborted =0x02040000 ; Aborted *dsa
+ABSOLUTE int_norm_reset = 0x02050000 ; Generated BUS reset.
+ABSOLUTE int_norm_emulateintfly = 0x02060000 ; 53C710 Emulated intfly
+ABSOLUTE int_debug_break = 0x03000000 ; Break point
+
+ABSOLUTE int_debug_panic = 0x030b0000 ; Panic driver
+
+
+ABSOLUTE int_test_1 = 0x04000000 ; Test 1 complete
+ABSOLUTE int_test_2 = 0x04010000 ; Test 2 complete
+ABSOLUTE int_test_3 = 0x04020000 ; Test 3 complete
+
+
+; These should start with 0x05000000, with low bits incrementing for
+; each one.
+
+
+
+ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
+ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
+ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
+ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
+ABSOLUTE NOP_insn = 0 ; NOP instruction
+
+; Pointer to message, potentially multi-byte
+ABSOLUTE msg_buf = 0
+
+; Pointer to holding area for reselection information
+ABSOLUTE reselected_identify = 0
+ABSOLUTE reselected_tag = 0
+
+; Request sense command pointer, it's a 6 byte command, should
+; be constant for all commands since we always want 16 bytes of
+; sense and we don't need to change any fields as we did under
+; SCSI-I when we actually cared about the LUN field.
+;EXTERNAL NCR53c7xx_sense ; Request sense command
+
+
+; dsa_schedule
+; PURPOSE : after a DISCONNECT message has been received, and pointers
+; saved, insert the current DSA structure at the head of the
+; disconnected queue and fall through to the scheduler.
+;
+; CALLS : OK
+;
+; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
+; of disconnected commands
+;
+; MODIFIES : SCRATCH, reconnect_dsa_head
+;
+; EXITS : always passes control to schedule
+
+ENTRY dsa_schedule
+dsa_schedule:
+
+
+
+
+;
+; Calculate the address of the next pointer within the DSA
+; structure of the command that is currently disconnecting
+;
+
+ ; Read what should be the current DSA from memory - actual DSA
+ ; register is probably corrupt
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+
+at 0x0000005e : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+ MOVE SCRATCH0 + dsa_next TO SCRATCH0
+
+at 0x00000061 : */ 0x7e343000,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x00000063 : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x00000065 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x00000067 : */ 0x7f370000,0x00000000,
+/*
+
+; Point the next field of this DSA structure at the current disconnected
+; list
+
+ MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
+
+at 0x00000069 : */ 0xc0000004,0x00000000,0x000001b8,
+/*
+
+dsa_schedule_insert:
+ MOVE MEMORY 4, reconnect_dsa_head, 0
+
+at 0x0000006c : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+; And update the head pointer.
+
+ ; Read what should be the current DSA from memory - actual DSA
+ ; register is probably corrupt
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+
+at 0x0000006f : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+ MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
+
+at 0x00000072 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+
+
+ CLEAR ACK
+
+at 0x00000075 : */ 0x60000040,0x00000000,
+/*
+
+
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x00000077 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+ WAIT DISCONNECT
+
+at 0x0000007a : */ 0x48000000,0x00000000,
+/*
+
+
+
+
+
+
+ JUMP schedule
+
+at 0x0000007c : */ 0x80080000,0x00000000,
+/*
+
+
+;
+; select
+;
+; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
+; On success, the current DSA structure is removed from the issue
+; queue. Usually, this is entered as a fall-through from schedule,
+; although the contingent allegiance handling code will write
+; the select entry address to the DSP to restart a command as a
+; REQUEST SENSE. A message is sent (usually IDENTIFY, although
+; additional SDTR or WDTR messages may be sent). COMMAND OUT
+; is handled.
+;
+; INPUTS : DSA - SCSI command, issue_dsa_head
+;
+; CALLS : NOT OK
+;
+; MODIFIES : SCRATCH, issue_dsa_head
+;
+; EXITS : on reselection or selection, go to select_failed
+; otherwise, RETURN so control is passed back to
+; dsa_begin.
+;
+
+ENTRY select
+select:
+
+
+
+
+
+
+
+
+ CLEAR TARGET
+
+at 0x0000007e : */ 0x60000200,0x00000000,
+/*
+
+; XXX
+;
+; In effect, SELECTION operations are backgrounded, with execution
+; continuing until code which waits for REQ or a fatal interrupt is
+; encountered.
+;
+; So, for more performance, we could overlap the code which removes
+; the command from the NCRs issue queue with the selection, but
+; at this point I don't want to deal with the error recovery.
+;
+
+
+
+ ; Enable selection timer
+
+
+
+ MOVE CTEST7 & 0xef TO CTEST7
+
+at 0x00000080 : */ 0x7c1bef00,0x00000000,
+/*
+
+
+ SELECT ATN FROM dsa_select, select_failed
+
+at 0x00000082 : */ 0x4300003c,0x00000828,
+/*
+ JUMP select_msgout, WHEN MSG_OUT
+
+at 0x00000084 : */ 0x860b0000,0x00000218,
+/*
+ENTRY select_msgout
+select_msgout:
+
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+
+at 0x00000086 : */ 0x7a1b1000,0x00000000,
+/*
+
+ MOVE FROM dsa_msgout, WHEN MSG_OUT
+
+at 0x00000088 : */ 0x1e000000,0x00000040,
+/*
+
+
+
+
+
+
+
+
+
+
+ RETURN
+
+at 0x0000008a : */ 0x90080000,0x00000000,
+/*
+
+;
+; select_done
+;
+; PURPOSE: continue on to normal data transfer; called as the exit
+; point from dsa_begin.
+;
+; INPUTS: dsa
+;
+; CALLS: OK
+;
+;
+
+select_done:
+
+; NOTE DSA is corrupt when we arrive here!
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x0000008c : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+
+
+
+
+; After a successful selection, we should get either a CMD phase or
+; some transfer request negotiation message.
+
+ JUMP cmdout, WHEN CMD
+
+at 0x0000008f : */ 0x820b0000,0x0000025c,
+/*
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x00000091 : */ 0x9f030000,0x00000000,
+/*
+
+select_msg_in:
+ CALL msg_in, WHEN MSG_IN
+
+at 0x00000093 : */ 0x8f0b0000,0x0000041c,
+/*
+ JUMP select_msg_in, WHEN MSG_IN
+
+at 0x00000095 : */ 0x870b0000,0x0000024c,
+/*
+
+cmdout:
+ INT int_err_unexpected_phase, WHEN NOT CMD
+
+at 0x00000097 : */ 0x9a030000,0x00000000,
+/*
+
+
+
+ENTRY cmdout_cmdout
+cmdout_cmdout:
+
+ MOVE FROM dsa_cmdout, WHEN CMD
+
+at 0x00000099 : */ 0x1a000000,0x00000048,
+/*
+
+
+
+
+;
+; data_transfer
+; other_out
+; other_in
+; other_transfer
+;
+; PURPOSE : handle the main data transfer for a SCSI command in
+; several parts. In the first part, data_transfer, DATA_IN
+; and DATA_OUT phases are allowed, with the user provided
+; code (usually dynamically generated based on the scatter/gather
+; list associated with a SCSI command) called to handle these
+; phases.
+;
+; After control has passed to one of the user provided
+; DATA_IN or DATA_OUT routines, back calls are made to
+; other_transfer_in or other_transfer_out to handle non-DATA IN
+; and DATA OUT phases respectively, with the state of the active
+; data pointer being preserved in TEMP.
+;
+; On completion, the user code passes control to other_transfer
+; which causes DATA_IN and DATA_OUT to result in unexpected_phase
+; interrupts so that data overruns may be trapped.
+;
+; INPUTS : DSA - SCSI command
+;
+; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in
+; other_transfer
+;
+; MODIFIES : SCRATCH
+;
+; EXITS : if STATUS IN is detected, signifying command completion,
+; the NCR jumps to command_complete. If MSG IN occurs, a
+; CALL is made to msg_in. Otherwise, other_transfer runs in
+; an infinite loop.
+;
+
+ENTRY data_transfer
+data_transfer:
+ JUMP cmdout_cmdout, WHEN CMD
+
+at 0x0000009b : */ 0x820b0000,0x00000264,
+/*
+ CALL msg_in, WHEN MSG_IN
+
+at 0x0000009d : */ 0x8f0b0000,0x0000041c,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x0000009f : */ 0x9e0b0000,0x00000000,
+/*
+ JUMP do_dataout, WHEN DATA_OUT
+
+at 0x000000a1 : */ 0x800b0000,0x000002a4,
+/*
+ JUMP do_datain, WHEN DATA_IN
+
+at 0x000000a3 : */ 0x810b0000,0x000002fc,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000a5 : */ 0x830b0000,0x0000065c,
+/*
+ JUMP data_transfer
+
+at 0x000000a7 : */ 0x80080000,0x0000026c,
+/*
+ENTRY end_data_transfer
+end_data_transfer:
+
+;
+; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain
+; should be fixed up whenever the nexus changes so it can point to the
+; correct routine for that command.
+;
+
+
+; Nasty jump to dsa->dataout
+do_dataout:
+
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+
+at 0x000000a9 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+ MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
+
+at 0x000000ac : */ 0x7e345000,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x000000ae : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x000000b0 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x000000b2 : */ 0x7f370000,0x00000000,
+/*
+
+ MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
+
+at 0x000000b4 : */ 0xc0000004,0x00000000,0x000002e0,
+/*
+
+dataout_to_jump:
+ MOVE MEMORY 4, 0, dataout_jump + 4
+
+at 0x000000b7 : */ 0xc0000004,0x00000000,0x000002f8,
+/*
+
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x000000ba : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+dataout_jump:
+ JUMP 0
+
+at 0x000000bd : */ 0x80080000,0x00000000,
+/*
+
+; Nasty jump to dsa->dsain
+do_datain:
+
+ MOVE MEMORY 4, saved_dsa, addr_scratch
+
+at 0x000000bf : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+ MOVE SCRATCH0 + dsa_datain TO SCRATCH0
+
+at 0x000000c2 : */ 0x7e345400,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x000000c4 : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x000000c6 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x000000c8 : */ 0x7f370000,0x00000000,
+/*
+
+ MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
+
+at 0x000000ca : */ 0xc0000004,0x00000000,0x00000338,
+/*
+
+ENTRY datain_to_jump
+datain_to_jump:
+ MOVE MEMORY 4, 0, datain_jump + 4
+
+at 0x000000cd : */ 0xc0000004,0x00000000,0x00000350,
+/*
+
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x000000d0 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+datain_jump:
+ JUMP 0
+
+at 0x000000d3 : */ 0x80080000,0x00000000,
+/*
+
+
+
+; Note that other_out and other_in loop until a non-data phase
+; is discovered, so we only execute return statements when we
+; can go on to the next data phase block move statement.
+
+ENTRY other_out
+other_out:
+
+
+
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000d5 : */ 0x9a0b0000,0x00000000,
+/*
+ JUMP msg_in_restart, WHEN MSG_IN
+
+at 0x000000d7 : */ 0x870b0000,0x000003fc,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000d9 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000db : */ 0x990b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000dd : */ 0x830b0000,0x0000065c,
+/*
+ JUMP other_out, WHEN NOT DATA_OUT
+
+at 0x000000df : */ 0x80030000,0x00000354,
+/*
+
+; TEMP should be OK, as we got here from a call in the user dataout code.
+
+ RETURN
+
+at 0x000000e1 : */ 0x90080000,0x00000000,
+/*
+
+ENTRY other_in
+other_in:
+
+
+
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000e3 : */ 0x9a0b0000,0x00000000,
+/*
+ JUMP msg_in_restart, WHEN MSG_IN
+
+at 0x000000e5 : */ 0x870b0000,0x000003fc,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000e7 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000e9 : */ 0x980b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000eb : */ 0x830b0000,0x0000065c,
+/*
+ JUMP other_in, WHEN NOT DATA_IN
+
+at 0x000000ed : */ 0x81030000,0x0000038c,
+/*
+
+; TEMP should be OK, as we got here from a call in the user datain code.
+
+ RETURN
+
+at 0x000000ef : */ 0x90080000,0x00000000,
+/*
+
+
+ENTRY other_transfer
+other_transfer:
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000f1 : */ 0x9a0b0000,0x00000000,
+/*
+ CALL msg_in, WHEN MSG_IN
+
+at 0x000000f3 : */ 0x8f0b0000,0x0000041c,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000f5 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000f7 : */ 0x980b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000f9 : */ 0x990b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000fb : */ 0x830b0000,0x0000065c,
+/*
+ JUMP other_transfer
+
+at 0x000000fd : */ 0x80080000,0x000003c4,
+/*
+
+;
+; msg_in_restart
+; msg_in
+; munge_msg
+;
+; PURPOSE : process messages from a target. msg_in is called when the
+; caller hasn't read the first byte of the message. munge_message
+; is called when the caller has read the first byte of the message,
+; and left it in SFBR. msg_in_restart is called when the caller
+; hasn't read the first byte of the message, and wishes RETURN
+; to transfer control back to the address of the conditional
+; CALL instruction rather than to the instruction after it.
+;
+; Various int_* interrupts are generated when the host system
+; needs to intervene, as is the case with SDTR, WDTR, and
+; INITIATE RECOVERY messages.
+;
+; When the host system handles one of these interrupts,
+; it can respond by reentering at reject_message,
+; which rejects the message and returns control to
+; the caller of msg_in or munge_msg, accept_message
+; which clears ACK and returns control, or reply_message
+; which sends the message pointed to by the DSA
+; msgout_other table indirect field.
+;
+; DISCONNECT messages are handled by moving the command
+; to the reconnect_dsa_queue.
+
+; NOTE: DSA should be valid when we get here - we cannot save both it
+; and TEMP in this routine.
+
+;
+; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
+; only)
+;
+; CALLS : NO. The TEMP register isn't backed up to allow nested calls.
+;
+; MODIFIES : SCRATCH, DSA on DISCONNECT
+;
+; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
+; and normal return from message handlers running under
+; Linux, control is returned to the caller. Receipt
+; of DISCONNECT messages pass control to dsa_schedule.
+;
+ENTRY msg_in_restart
+msg_in_restart:
+; XXX - hackish
+;
+; Since it's easier to debug changes to the statically
+; compiled code, rather than the dynamically generated
+; stuff, such as
+;
+; MOVE x, y, WHEN data_phase
+; CALL other_z, WHEN NOT data_phase
+; MOVE x, y, WHEN data_phase
+;
+; I'd like to have certain routines (notably the message handler)
+; restart on the conditional call rather than the next instruction.
+;
+; So, subtract 8 from the return address
+
+ MOVE TEMP0 + 0xf8 TO TEMP0
+
+at 0x000000ff : */ 0x7e1cf800,0x00000000,
+/*
+ MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY
+
+at 0x00000101 : */ 0x7f1dff00,0x00000000,
+/*
+ MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY
+
+at 0x00000103 : */ 0x7f1eff00,0x00000000,
+/*
+ MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY
+
+at 0x00000105 : */ 0x7f1fff00,0x00000000,
+/*
+
+ENTRY msg_in
+msg_in:
+ MOVE 1, msg_buf, WHEN MSG_IN
+
+at 0x00000107 : */ 0x0f000001,0x00000000,
+/*
+
+munge_msg:
+ JUMP munge_extended, IF 0x01 ; EXTENDED MESSAGE
+
+at 0x00000109 : */ 0x800c0001,0x00000574,
+/*
+ JUMP munge_2, IF 0x20, AND MASK 0xdf ; two byte message
+
+at 0x0000010b : */ 0x800cdf20,0x00000464,
+/*
+;
+; XXX - I've seen a handful of broken SCSI devices which fail to issue
+; a SAVE POINTERS message before disconnecting in the middle of
+; a transfer, assuming that the DATA POINTER will be implicitly
+; restored.
+;
+; Historically, I've often done an implicit save when the DISCONNECT
+; message is processed. We may want to consider having the option of
+; doing that here.
+;
+ JUMP munge_save_data_pointer, IF 0x02 ; SAVE DATA POINTER
+
+at 0x0000010d : */ 0x800c0002,0x0000046c,
+/*
+ JUMP munge_restore_pointers, IF 0x03 ; RESTORE POINTERS
+
+at 0x0000010f : */ 0x800c0003,0x00000518,
+/*
+ JUMP munge_disconnect, IF 0x04 ; DISCONNECT
+
+at 0x00000111 : */ 0x800c0004,0x0000056c,
+/*
+ INT int_msg_1, IF 0x07 ; MESSAGE REJECT
+
+at 0x00000113 : */ 0x980c0007,0x01020000,
+/*
+ INT int_msg_1, IF 0x0f ; INITIATE RECOVERY
+
+at 0x00000115 : */ 0x980c000f,0x01020000,
+/*
+
+
+
+ JUMP reject_message
+
+at 0x00000117 : */ 0x80080000,0x00000604,
+/*
+
+munge_2:
+ JUMP reject_message
+
+at 0x00000119 : */ 0x80080000,0x00000604,
+/*
+;
+; The SCSI standard allows targets to recover from transient
+; error conditions by backing up the data pointer with a
+; RESTORE POINTERS message.
+;
+; So, we must save and restore the _residual_ code as well as
+; the current instruction pointer. Because of this messiness,
+; it is simpler to put dynamic code in the dsa for this and to
+; just do a simple jump down there.
+;
+
+munge_save_data_pointer:
+
+ ; We have something in TEMP here, so first we must save that
+ MOVE TEMP0 TO SFBR
+
+at 0x0000011b : */ 0x721c0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x0000011d : */ 0x6a340000,0x00000000,
+/*
+ MOVE TEMP1 TO SFBR
+
+at 0x0000011f : */ 0x721d0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x00000121 : */ 0x6a350000,0x00000000,
+/*
+ MOVE TEMP2 TO SFBR
+
+at 0x00000123 : */ 0x721e0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x00000125 : */ 0x6a360000,0x00000000,
+/*
+ MOVE TEMP3 TO SFBR
+
+at 0x00000127 : */ 0x721f0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x00000129 : */ 0x6a370000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, jump_temp + 4
+
+at 0x0000012b : */ 0xc0000004,0x00000000,0x000009c8,
+/*
+ ; Now restore DSA
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x0000012e : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+ MOVE DSA0 + dsa_save_data_pointer TO SFBR
+
+at 0x00000131 : */ 0x76100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000133 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+
+at 0x00000135 : */ 0x7711ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x00000137 : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+
+at 0x00000139 : */ 0x7712ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x0000013b : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+
+at 0x0000013d : */ 0x7713ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x0000013f : */ 0x6a370000,0x00000000,
+/*
+
+
+ MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4
+
+at 0x00000141 : */ 0xc0000004,0x00000000,0x00000514,
+/*
+
+jump_dsa_save:
+ JUMP 0
+
+at 0x00000144 : */ 0x80080000,0x00000000,
+/*
+
+munge_restore_pointers:
+
+ ; The code at dsa_restore_pointers will RETURN, but we don't care
+ ; about TEMP here, as it will overwrite it anyway.
+
+ MOVE DSA0 + dsa_restore_pointers TO SFBR
+
+at 0x00000146 : */ 0x76100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000148 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+
+at 0x0000014a : */ 0x7711ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x0000014c : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+
+at 0x0000014e : */ 0x7712ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x00000150 : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+
+at 0x00000152 : */ 0x7713ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x00000154 : */ 0x6a370000,0x00000000,
+/*
+
+
+ MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4
+
+at 0x00000156 : */ 0xc0000004,0x00000000,0x00000568,
+/*
+
+jump_dsa_restore:
+ JUMP 0
+
+at 0x00000159 : */ 0x80080000,0x00000000,
+/*
+
+
+munge_disconnect:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ JUMP dsa_schedule
+
+at 0x0000015b : */ 0x80080000,0x00000178,
+/*
+
+
+
+
+
+munge_extended:
+ CLEAR ACK
+
+at 0x0000015d : */ 0x60000040,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x0000015f : */ 0x9f030000,0x00000000,
+/*
+ MOVE 1, msg_buf + 1, WHEN MSG_IN
+
+at 0x00000161 : */ 0x0f000001,0x00000001,
+/*
+ JUMP munge_extended_2, IF 0x02
+
+at 0x00000163 : */ 0x800c0002,0x000005a4,
+/*
+ JUMP munge_extended_3, IF 0x03
+
+at 0x00000165 : */ 0x800c0003,0x000005d4,
+/*
+ JUMP reject_message
+
+at 0x00000167 : */ 0x80080000,0x00000604,
+/*
+
+munge_extended_2:
+ CLEAR ACK
+
+at 0x00000169 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x0000016b : */ 0x0f000001,0x00000002,
+/*
+ JUMP reject_message, IF NOT 0x02 ; Must be WDTR
+
+at 0x0000016d : */ 0x80040002,0x00000604,
+/*
+ CLEAR ACK
+
+at 0x0000016f : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 3, WHEN MSG_IN
+
+at 0x00000171 : */ 0x0f000001,0x00000003,
+/*
+ INT int_msg_wdtr
+
+at 0x00000173 : */ 0x98080000,0x01000000,
+/*
+
+munge_extended_3:
+ CLEAR ACK
+
+at 0x00000175 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000177 : */ 0x0f000001,0x00000002,
+/*
+ JUMP reject_message, IF NOT 0x01 ; Must be SDTR
+
+at 0x00000179 : */ 0x80040001,0x00000604,
+/*
+ CLEAR ACK
+
+at 0x0000017b : */ 0x60000040,0x00000000,
+/*
+ MOVE 2, msg_buf + 3, WHEN MSG_IN
+
+at 0x0000017d : */ 0x0f000002,0x00000003,
+/*
+ INT int_msg_sdtr
+
+at 0x0000017f : */ 0x98080000,0x01010000,
+/*
+
+ENTRY reject_message
+reject_message:
+ SET ATN
+
+at 0x00000181 : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000183 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
+
+at 0x00000185 : */ 0x0e000001,0x00000000,
+/*
+ RETURN
+
+at 0x00000187 : */ 0x90080000,0x00000000,
+/*
+
+ENTRY accept_message
+accept_message:
+ CLEAR ATN
+
+at 0x00000189 : */ 0x60000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000018b : */ 0x60000040,0x00000000,
+/*
+ RETURN
+
+at 0x0000018d : */ 0x90080000,0x00000000,
+/*
+
+ENTRY respond_message
+respond_message:
+ SET ATN
+
+at 0x0000018f : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000191 : */ 0x60000040,0x00000000,
+/*
+ MOVE FROM dsa_msgout_other, WHEN MSG_OUT
+
+at 0x00000193 : */ 0x1e000000,0x00000068,
+/*
+ RETURN
+
+at 0x00000195 : */ 0x90080000,0x00000000,
+/*
+
+;
+; command_complete
+;
+; PURPOSE : handle command termination when STATUS IN is detected by reading
+; a status byte followed by a command termination message.
+;
+; Normal termination results in an INTFLY instruction, and
+; the host system can pick out which command terminated by
+; examining the MESSAGE and STATUS buffers of all currently
+; executing commands;
+;
+; Abnormal (CHECK_CONDITION) termination results in an
+; int_err_check_condition interrupt so that a REQUEST SENSE
+; command can be issued out-of-order so that no other command
+; clears the contingent allegiance condition.
+;
+;
+; INPUTS : DSA - command
+;
+; CALLS : OK
+;
+; EXITS : On successful termination, control is passed to schedule.
+; On abnormal termination, the user will usually modify the
+; DSA fields and corresponding buffers and return control
+; to select.
+;
+
+ENTRY command_complete
+command_complete:
+ MOVE FROM dsa_status, WHEN STATUS
+
+at 0x00000197 : */ 0x1b000000,0x00000060,
+/*
+
+ MOVE SFBR TO SCRATCH0 ; Save status
+
+at 0x00000199 : */ 0x6a340000,0x00000000,
+/*
+
+ENTRY command_complete_msgin
+command_complete_msgin:
+ MOVE FROM dsa_msgin, WHEN MSG_IN
+
+at 0x0000019b : */ 0x1f000000,0x00000058,
+/*
+; Indicate that we should be expecting a disconnect
+
+
+
+ ; Above code cleared the Unexpected Disconnect bit, what do we do?
+
+ CLEAR ACK
+
+at 0x0000019d : */ 0x60000040,0x00000000,
+/*
+
+ WAIT DISCONNECT
+
+at 0x0000019f : */ 0x48000000,0x00000000,
+/*
+
+;
+; The SCSI specification states that when a UNIT ATTENTION condition
+; is pending, as indicated by a CHECK CONDITION status message,
+; the target shall revert to asynchronous transfers. Since
+; synchronous transfers parameters are maintained on a per INITIATOR/TARGET
+; basis, and returning control to our scheduler could work on a command
+; running on another lun on that target using the old parameters, we must
+; interrupt the host processor to get them changed, or change them ourselves.
+;
+; Once SCSI-II tagged queueing is implemented, things will be even more
+; hairy, since contingent allegiance conditions exist on a per-target/lun
+; basis, and issuing a new command with a different tag would clear it.
+; In these cases, we must interrupt the host processor to get a request
+; added to the HEAD of the queue with the request sense command, or we
+; must automatically issue the request sense command.
+
+
+
+
+
+
+
+ INT int_norm_emulateintfly
+
+at 0x000001a1 : */ 0x98080000,0x02060000,
+/*
+
+
+
+
+
+
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x000001a3 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+
+ JUMP schedule
+
+at 0x000001a6 : */ 0x80080000,0x00000000,
+/*
+command_failed:
+ INT int_err_check_condition
+
+at 0x000001a8 : */ 0x98080000,0x00030000,
+/*
+
+
+
+
+;
+; wait_reselect
+;
+; PURPOSE : This is essentially the idle routine, where control lands
+; when there are no new processes to schedule. wait_reselect
+; waits for reselection, selection, and new commands.
+;
+; When a successful reselection occurs, with the aid
+; of fixed up code in each DSA, wait_reselect walks the
+; reconnect_dsa_queue, asking each dsa if the target ID
+; and LUN match its.
+;
+; If a match is found, a call is made back to reselected_ok,
+; which through the miracles of self modifying code, extracts
+; the found DSA from the reconnect_dsa_queue and then
+; returns control to the DSAs thread of execution.
+;
+; INPUTS : NONE
+;
+; CALLS : OK
+;
+; MODIFIES : DSA,
+;
+; EXITS : On successful reselection, control is returned to the
+; DSA which called reselected_ok. If the WAIT RESELECT
+; was interrupted by a new commands arrival signaled by
+; SIG_P, control is passed to schedule. If the NCR is
+; selected, the host system is interrupted with an
+; int_err_selected which is usually responded to by
+; setting DSP to the target_abort address.
+
+ENTRY wait_reselect
+wait_reselect:
+
+
+
+
+
+
+ WAIT RESELECT wait_reselect_failed
+
+at 0x000001aa : */ 0x50000000,0x00000800,
+/*
+
+reselected:
+
+
+
+ CLEAR TARGET
+
+at 0x000001ac : */ 0x60000200,0x00000000,
+/*
+
+ ; Read all data needed to reestablish the nexus -
+ MOVE 1, reselected_identify, WHEN MSG_IN
+
+at 0x000001ae : */ 0x0f000001,0x00000000,
+/*
+ ; We used to CLEAR ACK here.
+
+
+
+
+
+ ; Point DSA at the current head of the disconnected queue.
+
+ MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
+
+at 0x000001b0 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+ MOVE MEMORY 4, addr_scratch, saved_dsa
+
+at 0x000001b3 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+ ; Fix the update-next pointer so that the reconnect_dsa_head
+ ; pointer is the one that will be updated if this DSA is a hit
+ ; and we remove it from the queue.
+
+ MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok_patch + 8
+
+at 0x000001b6 : */ 0xc0000004,0x00000000,0x000007ec,
+/*
+
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x000001b9 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+
+ENTRY reselected_check_next
+reselected_check_next:
+
+
+
+ ; Check for a NULL pointer.
+ MOVE DSA0 TO SFBR
+
+at 0x000001bc : */ 0x72100000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001be : */ 0x80040000,0x00000738,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x000001c0 : */ 0x72110000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001c2 : */ 0x80040000,0x00000738,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x000001c4 : */ 0x72120000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001c6 : */ 0x80040000,0x00000738,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x000001c8 : */ 0x72130000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001ca : */ 0x80040000,0x00000738,
+/*
+ INT int_err_unexpected_reselect
+
+at 0x000001cc : */ 0x98080000,0x00020000,
+/*
+
+reselected_not_end:
+ ;
+ ; XXX the ALU is only eight bits wide, and the assembler
+ ; wont do the dirt work for us. As long as dsa_check_reselect
+ ; is negative, we need to sign extend with 1 bits to the full
+ ; 32 bit width of the address.
+ ;
+ ; A potential work around would be to have a known alignment
+ ; of the DSA structure such that the base address plus
+ ; dsa_check_reselect doesn't require carrying from bytes
+ ; higher than the LSB.
+ ;
+
+ MOVE DSA0 TO SFBR
+
+at 0x000001ce : */ 0x72100000,0x00000000,
+/*
+ MOVE SFBR + dsa_check_reselect TO SCRATCH0
+
+at 0x000001d0 : */ 0x6e340000,0x00000000,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x000001d2 : */ 0x72110000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
+
+at 0x000001d4 : */ 0x6f35ff00,0x00000000,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x000001d6 : */ 0x72120000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
+
+at 0x000001d8 : */ 0x6f36ff00,0x00000000,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x000001da : */ 0x72130000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
+
+at 0x000001dc : */ 0x6f37ff00,0x00000000,
+/*
+
+
+ MOVE MEMORY 4, addr_scratch, reselected_check + 4
+
+at 0x000001de : */ 0xc0000004,0x00000000,0x00000794,
+/*
+
+
+ ; Time to correct DSA following memory move
+ MOVE MEMORY 4, saved_dsa, addr_dsa
+
+at 0x000001e1 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+reselected_check:
+ JUMP 0
+
+at 0x000001e4 : */ 0x80080000,0x00000000,
+/*
+
+
+;
+;
+
+; We have problems here - the memory move corrupts TEMP and DSA. This
+; routine is called from DSA code, and patched from many places. Scratch
+; is probably free when it is called.
+; We have to:
+; copy temp to scratch, one byte at a time
+; write scratch to patch a jump in place of the return
+; do the move memory
+; jump to the patched in return address
+; DSA is corrupt when we get here, and can be left corrupt
+
+ENTRY reselected_ok
+reselected_ok:
+ MOVE TEMP0 TO SFBR
+
+at 0x000001e6 : */ 0x721c0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x000001e8 : */ 0x6a340000,0x00000000,
+/*
+ MOVE TEMP1 TO SFBR
+
+at 0x000001ea : */ 0x721d0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x000001ec : */ 0x6a350000,0x00000000,
+/*
+ MOVE TEMP2 TO SFBR
+
+at 0x000001ee : */ 0x721e0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x000001f0 : */ 0x6a360000,0x00000000,
+/*
+ MOVE TEMP3 TO SFBR
+
+at 0x000001f2 : */ 0x721f0000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x000001f4 : */ 0x6a370000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, reselected_ok_jump + 4
+
+at 0x000001f6 : */ 0xc0000004,0x00000000,0x000007f4,
+/*
+reselected_ok_patch:
+ MOVE MEMORY 4, 0, 0
+
+at 0x000001f9 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+reselected_ok_jump:
+ JUMP 0
+
+at 0x000001fc : */ 0x80080000,0x00000000,
+/*
+
+
+
+
+
+selected:
+ INT int_err_selected;
+
+at 0x000001fe : */ 0x98080000,0x00010000,
+/*
+
+;
+; A select or reselect failure can be caused by one of two conditions :
+; 1. SIG_P was set. This will be the case if the user has written
+; a new value to a previously NULL head of the issue queue.
+;
+; 2. The NCR53c810 was selected or reselected by another device.
+;
+; 3. The bus was already busy since we were selected or reselected
+; before starting the command.
+
+wait_reselect_failed:
+
+
+
+; Check selected bit.
+
+ ; Must work out how to tell if we are selected....
+
+
+
+
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x00000200 : */ 0x74164000,0x00000000,
+/*
+ JUMP schedule, IF 0x40
+
+at 0x00000202 : */ 0x800c0040,0x00000000,
+/*
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+ MOVE ISTAT & 0x08 TO SFBR
+
+at 0x00000204 : */ 0x74210800,0x00000000,
+/*
+ JUMP reselected, IF 0x08
+
+at 0x00000206 : */ 0x800c0008,0x000006b0,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+
+
+
+ INT int_debug_panic
+
+at 0x00000208 : */ 0x98080000,0x030b0000,
+/*
+
+
+
+select_failed:
+
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+
+at 0x0000020a : */ 0x7a1b1000,0x00000000,
+/*
+
+
+
+
+; Otherwise, mask the selected and reselected bits off SIST0
+
+ ; Let's assume we don't get selected for now
+ MOVE SSTAT0 & 0x10 TO SFBR
+
+at 0x0000020c : */ 0x740d1000,0x00000000,
+/*
+
+
+
+
+ JUMP reselected, IF 0x10
+
+at 0x0000020e : */ 0x800c0010,0x000006b0,
+/*
+; If SIGP is set, the user just gave us another command, and
+; we should restart or return to the scheduler.
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x00000210 : */ 0x74164000,0x00000000,
+/*
+ JUMP select, IF 0x40
+
+at 0x00000212 : */ 0x800c0040,0x000001f8,
+/*
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+; FIXME: is this really necessary?
+ MOVE ISTAT & 0x08 TO SFBR
+
+at 0x00000214 : */ 0x74210800,0x00000000,
+/*
+ JUMP reselected, IF 0x08
+
+at 0x00000216 : */ 0x800c0008,0x000006b0,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+
+
+
+ INT int_debug_panic
+
+at 0x00000218 : */ 0x98080000,0x030b0000,
+/*
+
+
+;
+; test_1
+; test_2
+;
+; PURPOSE : run some verification tests on the NCR. test_1
+; copies test_src to test_dest and interrupts the host
+; processor, testing for cache coherency and interrupt
+; problems in the processes.
+;
+; test_2 runs a command with offsets relative to the
+; DSA on entry, and is useful for miscellaneous experimentation.
+;
+
+; Verify that interrupts are working correctly and that we don't
+; have a cache invalidation problem.
+
+ABSOLUTE test_src = 0, test_dest = 0
+ENTRY test_1
+test_1:
+ MOVE MEMORY 4, test_src, test_dest
+
+at 0x0000021a : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ INT int_test_1
+
+at 0x0000021d : */ 0x98080000,0x04000000,
+/*
+
+;
+; Run arbitrary commands, with test code establishing a DSA
+;
+
+ENTRY test_2
+test_2:
+ CLEAR TARGET
+
+at 0x0000021f : */ 0x60000200,0x00000000,
+/*
+
+ ; Enable selection timer
+
+
+
+ MOVE CTEST7 & 0xef TO CTEST7
+
+at 0x00000221 : */ 0x7c1bef00,0x00000000,
+/*
+
+
+ SELECT ATN FROM 0, test_2_fail
+
+at 0x00000223 : */ 0x43000000,0x000008dc,
+/*
+ JUMP test_2_msgout, WHEN MSG_OUT
+
+at 0x00000225 : */ 0x860b0000,0x0000089c,
+/*
+ENTRY test_2_msgout
+test_2_msgout:
+
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+
+at 0x00000227 : */ 0x7a1b1000,0x00000000,
+/*
+
+ MOVE FROM 8, WHEN MSG_OUT
+
+at 0x00000229 : */ 0x1e000000,0x00000008,
+/*
+ MOVE FROM 16, WHEN CMD
+
+at 0x0000022b : */ 0x1a000000,0x00000010,
+/*
+ MOVE FROM 24, WHEN DATA_IN
+
+at 0x0000022d : */ 0x19000000,0x00000018,
+/*
+ MOVE FROM 32, WHEN STATUS
+
+at 0x0000022f : */ 0x1b000000,0x00000020,
+/*
+ MOVE FROM 40, WHEN MSG_IN
+
+at 0x00000231 : */ 0x1f000000,0x00000028,
+/*
+
+
+
+ CLEAR ACK
+
+at 0x00000233 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x00000235 : */ 0x48000000,0x00000000,
+/*
+test_2_fail:
+
+ ; Disable selection timer
+ MOVE CTEST7 | 0x10 TO CTEST7
+
+at 0x00000237 : */ 0x7a1b1000,0x00000000,
+/*
+
+ INT int_test_2
+
+at 0x00000239 : */ 0x98080000,0x04010000,
+/*
+
+ENTRY debug_break
+debug_break:
+ INT int_debug_break
+
+at 0x0000023b : */ 0x98080000,0x03000000,
+/*
+
+;
+; initiator_abort
+; target_abort
+;
+; PURPOSE : Abort the currently established nexus from with initiator
+; or target mode.
+;
+;
+
+ENTRY target_abort
+target_abort:
+ SET TARGET
+
+at 0x0000023d : */ 0x58000200,0x00000000,
+/*
+ DISCONNECT
+
+at 0x0000023f : */ 0x48000000,0x00000000,
+/*
+ CLEAR TARGET
+
+at 0x00000241 : */ 0x60000200,0x00000000,
+/*
+ JUMP schedule
+
+at 0x00000243 : */ 0x80080000,0x00000000,
+/*
+
+ENTRY initiator_abort
+initiator_abort:
+ SET ATN
+
+at 0x00000245 : */ 0x58000008,0x00000000,
+/*
+;
+; The SCSI-I specification says that targets may go into MSG out at
+; their leisure upon receipt of the ATN single. On all versions of the
+; specification, we can't change phases until REQ transitions true->false,
+; so we need to sink/source one byte of data to allow the transition.
+;
+; For the sake of safety, we'll only source one byte of data in all
+; cases, but to accommodate the SCSI-I dain bramage, we'll sink an
+; arbitrary number of bytes.
+ JUMP spew_cmd, WHEN CMD
+
+at 0x00000247 : */ 0x820b0000,0x0000094c,
+/*
+ JUMP eat_msgin, WHEN MSG_IN
+
+at 0x00000249 : */ 0x870b0000,0x0000095c,
+/*
+ JUMP eat_datain, WHEN DATA_IN
+
+at 0x0000024b : */ 0x810b0000,0x0000098c,
+/*
+ JUMP eat_status, WHEN STATUS
+
+at 0x0000024d : */ 0x830b0000,0x00000974,
+/*
+ JUMP spew_dataout, WHEN DATA_OUT
+
+at 0x0000024f : */ 0x800b0000,0x000009a4,
+/*
+ JUMP sated
+
+at 0x00000251 : */ 0x80080000,0x000009ac,
+/*
+spew_cmd:
+ MOVE 1, NCR53c7xx_zero, WHEN CMD
+
+at 0x00000253 : */ 0x0a000001,0x00000000,
+/*
+ JUMP sated
+
+at 0x00000255 : */ 0x80080000,0x000009ac,
+/*
+eat_msgin:
+ MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
+
+at 0x00000257 : */ 0x0f000001,0x00000000,
+/*
+ JUMP eat_msgin, WHEN MSG_IN
+
+at 0x00000259 : */ 0x870b0000,0x0000095c,
+/*
+ JUMP sated
+
+at 0x0000025b : */ 0x80080000,0x000009ac,
+/*
+eat_status:
+ MOVE 1, NCR53c7xx_sink, WHEN STATUS
+
+at 0x0000025d : */ 0x0b000001,0x00000000,
+/*
+ JUMP eat_status, WHEN STATUS
+
+at 0x0000025f : */ 0x830b0000,0x00000974,
+/*
+ JUMP sated
+
+at 0x00000261 : */ 0x80080000,0x000009ac,
+/*
+eat_datain:
+ MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
+
+at 0x00000263 : */ 0x09000001,0x00000000,
+/*
+ JUMP eat_datain, WHEN DATA_IN
+
+at 0x00000265 : */ 0x810b0000,0x0000098c,
+/*
+ JUMP sated
+
+at 0x00000267 : */ 0x80080000,0x000009ac,
+/*
+spew_dataout:
+ MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
+
+at 0x00000269 : */ 0x08000001,0x00000000,
+/*
+sated:
+
+
+
+ MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+
+at 0x0000026b : */ 0x0e000001,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x0000026d : */ 0x48000000,0x00000000,
+/*
+ INT int_norm_aborted
+
+at 0x0000026f : */ 0x98080000,0x02040000,
+/*
+
+
+
+
+; Little patched jump, used to overcome problems with TEMP getting
+; corrupted on memory moves.
+
+jump_temp:
+ JUMP 0
+
+at 0x00000271 : */ 0x80080000,0x00000000,
+};
+
+#define A_NCR53c7xx_msg_abort 0x00000000
+static u32 A_NCR53c7xx_msg_abort_used[] __attribute((unused)) = {
+ 0x0000026c,
+};
+
+#define A_NCR53c7xx_msg_reject 0x00000000
+static u32 A_NCR53c7xx_msg_reject_used[] __attribute((unused)) = {
+ 0x00000186,
+};
+
+#define A_NCR53c7xx_sink 0x00000000
+static u32 A_NCR53c7xx_sink_used[] __attribute((unused)) = {
+ 0x00000258,
+ 0x0000025e,
+ 0x00000264,
+};
+
+#define A_NCR53c7xx_zero 0x00000000
+static u32 A_NCR53c7xx_zero_used[] __attribute((unused)) = {
+ 0x00000254,
+ 0x0000026a,
+};
+
+#define A_NOP_insn 0x00000000
+static u32 A_NOP_insn_used[] __attribute((unused)) = {
+ 0x00000017,
+};
+
+#define A_addr_dsa 0x00000000
+static u32 A_addr_dsa_used[] __attribute((unused)) = {
+ 0x0000000f,
+ 0x00000026,
+ 0x00000033,
+ 0x00000040,
+ 0x00000055,
+ 0x00000079,
+ 0x0000008e,
+ 0x000000bc,
+ 0x000000d2,
+ 0x00000130,
+ 0x000001a5,
+ 0x000001bb,
+ 0x000001e3,
+};
+
+#define A_addr_reconnect_dsa_head 0x00000000
+static u32 A_addr_reconnect_dsa_head_used[] __attribute((unused)) = {
+ 0x000001b7,
+};
+
+#define A_addr_scratch 0x00000000
+static u32 A_addr_scratch_used[] __attribute((unused)) = {
+ 0x00000002,
+ 0x00000004,
+ 0x00000008,
+ 0x00000020,
+ 0x00000022,
+ 0x00000049,
+ 0x00000060,
+ 0x0000006a,
+ 0x00000071,
+ 0x00000073,
+ 0x000000ab,
+ 0x000000b5,
+ 0x000000c1,
+ 0x000000cb,
+ 0x0000012c,
+ 0x00000142,
+ 0x00000157,
+ 0x000001b2,
+ 0x000001b4,
+ 0x000001df,
+ 0x000001f7,
+};
+
+#define A_addr_temp 0x00000000
+static u32 A_addr_temp_used[] __attribute((unused)) = {
+};
+
+#define A_dmode_memory_to_memory 0x00000000
+static u32 A_dmode_memory_to_memory_used[] __attribute((unused)) = {
+};
+
+#define A_dmode_memory_to_ncr 0x00000000
+static u32 A_dmode_memory_to_ncr_used[] __attribute((unused)) = {
+};
+
+#define A_dmode_ncr_to_memory 0x00000000
+static u32 A_dmode_ncr_to_memory_used[] __attribute((unused)) = {
+};
+
+#define A_dsa_check_reselect 0x00000000
+static u32 A_dsa_check_reselect_used[] __attribute((unused)) = {
+ 0x000001d0,
+};
+
+#define A_dsa_cmdout 0x00000048
+static u32 A_dsa_cmdout_used[] __attribute((unused)) = {
+ 0x0000009a,
+};
+
+#define A_dsa_cmnd 0x00000038
+static u32 A_dsa_cmnd_used[] __attribute((unused)) = {
+};
+
+#define A_dsa_datain 0x00000054
+static u32 A_dsa_datain_used[] __attribute((unused)) = {
+ 0x000000c2,
+};
+
+#define A_dsa_dataout 0x00000050
+static u32 A_dsa_dataout_used[] __attribute((unused)) = {
+ 0x000000ac,
+};
+
+#define A_dsa_end 0x00000070
+static u32 A_dsa_end_used[] __attribute((unused)) = {
+};
+
+#define A_dsa_fields_start 0x00000000
+static u32 A_dsa_fields_start_used[] __attribute((unused)) = {
+};
+
+#define A_dsa_msgin 0x00000058
+static u32 A_dsa_msgin_used[] __attribute((unused)) = {
+ 0x0000019c,
+};
+
+#define A_dsa_msgout 0x00000040
+static u32 A_dsa_msgout_used[] __attribute((unused)) = {
+ 0x00000089,
+};
+
+#define A_dsa_msgout_other 0x00000068
+static u32 A_dsa_msgout_other_used[] __attribute((unused)) = {
+ 0x00000194,
+};
+
+#define A_dsa_next 0x00000030
+static u32 A_dsa_next_used[] __attribute((unused)) = {
+ 0x00000061,
+};
+
+#define A_dsa_restore_pointers 0x00000000
+static u32 A_dsa_restore_pointers_used[] __attribute((unused)) = {
+ 0x00000146,
+};
+
+#define A_dsa_save_data_pointer 0x00000000
+static u32 A_dsa_save_data_pointer_used[] __attribute((unused)) = {
+ 0x00000131,
+};
+
+#define A_dsa_select 0x0000003c
+static u32 A_dsa_select_used[] __attribute((unused)) = {
+ 0x00000082,
+};
+
+#define A_dsa_sscf_710 0x00000000
+static u32 A_dsa_sscf_710_used[] __attribute((unused)) = {
+ 0x00000007,
+};
+
+#define A_dsa_status 0x00000060
+static u32 A_dsa_status_used[] __attribute((unused)) = {
+ 0x00000198,
+};
+
+#define A_dsa_temp_addr_array_value 0x00000000
+static u32 A_dsa_temp_addr_array_value_used[] __attribute((unused)) = {
+};
+
+#define A_dsa_temp_addr_dsa_value 0x00000000
+static u32 A_dsa_temp_addr_dsa_value_used[] __attribute((unused)) = {
+ 0x00000001,
+};
+
+#define A_dsa_temp_addr_new_value 0x00000000
+static u32 A_dsa_temp_addr_new_value_used[] __attribute((unused)) = {
+};
+
+#define A_dsa_temp_addr_next 0x00000000
+static u32 A_dsa_temp_addr_next_used[] __attribute((unused)) = {
+ 0x0000001c,
+ 0x0000004f,
+};
+
+#define A_dsa_temp_addr_residual 0x00000000
+static u32 A_dsa_temp_addr_residual_used[] __attribute((unused)) = {
+ 0x0000002d,
+ 0x0000003b,
+};
+
+#define A_dsa_temp_addr_saved_pointer 0x00000000
+static u32 A_dsa_temp_addr_saved_pointer_used[] __attribute((unused)) = {
+ 0x0000002b,
+ 0x00000037,
+};
+
+#define A_dsa_temp_addr_saved_residual 0x00000000
+static u32 A_dsa_temp_addr_saved_residual_used[] __attribute((unused)) = {
+ 0x0000002e,
+ 0x0000003a,
+};
+
+#define A_dsa_temp_lun 0x00000000
+static u32 A_dsa_temp_lun_used[] __attribute((unused)) = {
+ 0x0000004c,
+};
+
+#define A_dsa_temp_next 0x00000000
+static u32 A_dsa_temp_next_used[] __attribute((unused)) = {
+ 0x0000001f,
+};
+
+#define A_dsa_temp_sync 0x00000000
+static u32 A_dsa_temp_sync_used[] __attribute((unused)) = {
+ 0x00000057,
+};
+
+#define A_dsa_temp_target 0x00000000
+static u32 A_dsa_temp_target_used[] __attribute((unused)) = {
+ 0x00000045,
+};
+
+#define A_emulfly 0x00000000
+static u32 A_emulfly_used[] __attribute((unused)) = {
+};
+
+#define A_int_debug_break 0x03000000
+static u32 A_int_debug_break_used[] __attribute((unused)) = {
+ 0x0000023c,
+};
+
+#define A_int_debug_panic 0x030b0000
+static u32 A_int_debug_panic_used[] __attribute((unused)) = {
+ 0x00000209,
+ 0x00000219,
+};
+
+#define A_int_err_check_condition 0x00030000
+static u32 A_int_err_check_condition_used[] __attribute((unused)) = {
+ 0x000001a9,
+};
+
+#define A_int_err_no_phase 0x00040000
+static u32 A_int_err_no_phase_used[] __attribute((unused)) = {
+};
+
+#define A_int_err_selected 0x00010000
+static u32 A_int_err_selected_used[] __attribute((unused)) = {
+ 0x000001ff,
+};
+
+#define A_int_err_unexpected_phase 0x00000000
+static u32 A_int_err_unexpected_phase_used[] __attribute((unused)) = {
+ 0x00000092,
+ 0x00000098,
+ 0x000000a0,
+ 0x000000d6,
+ 0x000000da,
+ 0x000000dc,
+ 0x000000e4,
+ 0x000000e8,
+ 0x000000ea,
+ 0x000000f2,
+ 0x000000f6,
+ 0x000000f8,
+ 0x000000fa,
+ 0x00000160,
+};
+
+#define A_int_err_unexpected_reselect 0x00020000
+static u32 A_int_err_unexpected_reselect_used[] __attribute((unused)) = {
+ 0x000001cd,
+};
+
+#define A_int_msg_1 0x01020000
+static u32 A_int_msg_1_used[] __attribute((unused)) = {
+ 0x00000114,
+ 0x00000116,
+};
+
+#define A_int_msg_sdtr 0x01010000
+static u32 A_int_msg_sdtr_used[] __attribute((unused)) = {
+ 0x00000180,
+};
+
+#define A_int_msg_wdtr 0x01000000
+static u32 A_int_msg_wdtr_used[] __attribute((unused)) = {
+ 0x00000174,
+};
+
+#define A_int_norm_aborted 0x02040000
+static u32 A_int_norm_aborted_used[] __attribute((unused)) = {
+ 0x00000270,
+};
+
+#define A_int_norm_command_complete 0x02020000
+static u32 A_int_norm_command_complete_used[] __attribute((unused)) = {
+};
+
+#define A_int_norm_disconnected 0x02030000
+static u32 A_int_norm_disconnected_used[] __attribute((unused)) = {
+};
+
+#define A_int_norm_emulateintfly 0x02060000
+static u32 A_int_norm_emulateintfly_used[] __attribute((unused)) = {
+ 0x000001a2,
+};
+
+#define A_int_norm_reselect_complete 0x02010000
+static u32 A_int_norm_reselect_complete_used[] __attribute((unused)) = {
+};
+
+#define A_int_norm_reset 0x02050000
+static u32 A_int_norm_reset_used[] __attribute((unused)) = {
+};
+
+#define A_int_norm_select_complete 0x02000000
+static u32 A_int_norm_select_complete_used[] __attribute((unused)) = {
+};
+
+#define A_int_test_1 0x04000000
+static u32 A_int_test_1_used[] __attribute((unused)) = {
+ 0x0000021e,
+};
+
+#define A_int_test_2 0x04010000
+static u32 A_int_test_2_used[] __attribute((unused)) = {
+ 0x0000023a,
+};
+
+#define A_int_test_3 0x04020000
+static u32 A_int_test_3_used[] __attribute((unused)) = {
+};
+
+#define A_msg_buf 0x00000000
+static u32 A_msg_buf_used[] __attribute((unused)) = {
+ 0x00000108,
+ 0x00000162,
+ 0x0000016c,
+ 0x00000172,
+ 0x00000178,
+ 0x0000017e,
+};
+
+#define A_reconnect_dsa_head 0x00000000
+static u32 A_reconnect_dsa_head_used[] __attribute((unused)) = {
+ 0x0000006d,
+ 0x00000074,
+ 0x000001b1,
+};
+
+#define A_reselected_identify 0x00000000
+static u32 A_reselected_identify_used[] __attribute((unused)) = {
+ 0x00000048,
+ 0x000001af,
+};
+
+#define A_reselected_tag 0x00000000
+static u32 A_reselected_tag_used[] __attribute((unused)) = {
+};
+
+#define A_saved_dsa 0x00000000
+static u32 A_saved_dsa_used[] __attribute((unused)) = {
+ 0x00000005,
+ 0x0000000e,
+ 0x00000023,
+ 0x00000025,
+ 0x00000032,
+ 0x0000003f,
+ 0x00000054,
+ 0x0000005f,
+ 0x00000070,
+ 0x00000078,
+ 0x0000008d,
+ 0x000000aa,
+ 0x000000bb,
+ 0x000000c0,
+ 0x000000d1,
+ 0x0000012f,
+ 0x000001a4,
+ 0x000001b5,
+ 0x000001ba,
+ 0x000001e2,
+};
+
+#define A_schedule 0x00000000
+static u32 A_schedule_used[] __attribute((unused)) = {
+ 0x0000007d,
+ 0x000001a7,
+ 0x00000203,
+ 0x00000244,
+};
+
+#define A_test_dest 0x00000000
+static u32 A_test_dest_used[] __attribute((unused)) = {
+ 0x0000021c,
+};
+
+#define A_test_src 0x00000000
+static u32 A_test_src_used[] __attribute((unused)) = {
+ 0x0000021b,
+};
+
+#define Ent_accept_message 0x00000624
+#define Ent_cmdout_cmdout 0x00000264
+#define Ent_command_complete 0x0000065c
+#define Ent_command_complete_msgin 0x0000066c
+#define Ent_data_transfer 0x0000026c
+#define Ent_datain_to_jump 0x00000334
+#define Ent_debug_break 0x000008ec
+#define Ent_dsa_code_begin 0x00000000
+#define Ent_dsa_code_check_reselect 0x0000010c
+#define Ent_dsa_code_fix_jump 0x00000058
+#define Ent_dsa_code_restore_pointers 0x000000d8
+#define Ent_dsa_code_save_data_pointer 0x000000a4
+#define Ent_dsa_code_template 0x00000000
+#define Ent_dsa_code_template_end 0x00000178
+#define Ent_dsa_schedule 0x00000178
+#define Ent_dsa_zero 0x00000178
+#define Ent_end_data_transfer 0x000002a4
+#define Ent_initiator_abort 0x00000914
+#define Ent_msg_in 0x0000041c
+#define Ent_msg_in_restart 0x000003fc
+#define Ent_other_in 0x0000038c
+#define Ent_other_out 0x00000354
+#define Ent_other_transfer 0x000003c4
+#define Ent_reject_message 0x00000604
+#define Ent_reselected_check_next 0x000006f0
+#define Ent_reselected_ok 0x00000798
+#define Ent_respond_message 0x0000063c
+#define Ent_select 0x000001f8
+#define Ent_select_msgout 0x00000218
+#define Ent_target_abort 0x000008f4
+#define Ent_test_1 0x00000868
+#define Ent_test_2 0x0000087c
+#define Ent_test_2_msgout 0x0000089c
+#define Ent_wait_reselect 0x000006a8
+static u32 LABELPATCHES[] __attribute((unused)) = {
+ 0x00000011,
+ 0x0000001a,
+ 0x0000001d,
+ 0x00000028,
+ 0x0000002a,
+ 0x00000035,
+ 0x00000038,
+ 0x00000042,
+ 0x00000050,
+ 0x00000052,
+ 0x0000006b,
+ 0x00000083,
+ 0x00000085,
+ 0x00000090,
+ 0x00000094,
+ 0x00000096,
+ 0x0000009c,
+ 0x0000009e,
+ 0x000000a2,
+ 0x000000a4,
+ 0x000000a6,
+ 0x000000a8,
+ 0x000000b6,
+ 0x000000b9,
+ 0x000000cc,
+ 0x000000cf,
+ 0x000000d8,
+ 0x000000de,
+ 0x000000e0,
+ 0x000000e6,
+ 0x000000ec,
+ 0x000000ee,
+ 0x000000f4,
+ 0x000000fc,
+ 0x000000fe,
+ 0x0000010a,
+ 0x0000010c,
+ 0x0000010e,
+ 0x00000110,
+ 0x00000112,
+ 0x00000118,
+ 0x0000011a,
+ 0x0000012d,
+ 0x00000143,
+ 0x00000158,
+ 0x0000015c,
+ 0x00000164,
+ 0x00000166,
+ 0x00000168,
+ 0x0000016e,
+ 0x0000017a,
+ 0x000001ab,
+ 0x000001b8,
+ 0x000001bf,
+ 0x000001c3,
+ 0x000001c7,
+ 0x000001cb,
+ 0x000001e0,
+ 0x000001f8,
+ 0x00000207,
+ 0x0000020f,
+ 0x00000213,
+ 0x00000217,
+ 0x00000224,
+ 0x00000226,
+ 0x00000248,
+ 0x0000024a,
+ 0x0000024c,
+ 0x0000024e,
+ 0x00000250,
+ 0x00000252,
+ 0x00000256,
+ 0x0000025a,
+ 0x0000025c,
+ 0x00000260,
+ 0x00000262,
+ 0x00000266,
+ 0x00000268,
+};
+
+static struct {
+ u32 offset;
+ void *address;
+} EXTERNAL_PATCHES[] __attribute((unused)) = {
+};
+
+static u32 INSTRUCTIONS __attribute((unused)) = 290;
+static u32 PATCHES __attribute((unused)) = 78;
+static u32 EXTERNAL_PATCHES_LEN __attribute((unused)) = 0;
diff --git a/drivers/scsi/53c7xx_u.h_shipped b/drivers/scsi/53c7xx_u.h_shipped
new file mode 100644
index 000000000000..7b337174e228
--- /dev/null
+++ b/drivers/scsi/53c7xx_u.h_shipped
@@ -0,0 +1,102 @@
+#undef A_NCR53c7xx_msg_abort
+#undef A_NCR53c7xx_msg_reject
+#undef A_NCR53c7xx_sink
+#undef A_NCR53c7xx_zero
+#undef A_NOP_insn
+#undef A_addr_dsa
+#undef A_addr_reconnect_dsa_head
+#undef A_addr_scratch
+#undef A_addr_temp
+#undef A_dmode_memory_to_memory
+#undef A_dmode_memory_to_ncr
+#undef A_dmode_ncr_to_memory
+#undef A_dsa_check_reselect
+#undef A_dsa_cmdout
+#undef A_dsa_cmnd
+#undef A_dsa_datain
+#undef A_dsa_dataout
+#undef A_dsa_end
+#undef A_dsa_fields_start
+#undef A_dsa_msgin
+#undef A_dsa_msgout
+#undef A_dsa_msgout_other
+#undef A_dsa_next
+#undef A_dsa_restore_pointers
+#undef A_dsa_save_data_pointer
+#undef A_dsa_select
+#undef A_dsa_sscf_710
+#undef A_dsa_status
+#undef A_dsa_temp_addr_array_value
+#undef A_dsa_temp_addr_dsa_value
+#undef A_dsa_temp_addr_new_value
+#undef A_dsa_temp_addr_next
+#undef A_dsa_temp_addr_residual
+#undef A_dsa_temp_addr_saved_pointer
+#undef A_dsa_temp_addr_saved_residual
+#undef A_dsa_temp_lun
+#undef A_dsa_temp_next
+#undef A_dsa_temp_sync
+#undef A_dsa_temp_target
+#undef A_emulfly
+#undef A_int_debug_break
+#undef A_int_debug_panic
+#undef A_int_err_check_condition
+#undef A_int_err_no_phase
+#undef A_int_err_selected
+#undef A_int_err_unexpected_phase
+#undef A_int_err_unexpected_reselect
+#undef A_int_msg_1
+#undef A_int_msg_sdtr
+#undef A_int_msg_wdtr
+#undef A_int_norm_aborted
+#undef A_int_norm_command_complete
+#undef A_int_norm_disconnected
+#undef A_int_norm_emulateintfly
+#undef A_int_norm_reselect_complete
+#undef A_int_norm_reset
+#undef A_int_norm_select_complete
+#undef A_int_test_1
+#undef A_int_test_2
+#undef A_int_test_3
+#undef A_msg_buf
+#undef A_reconnect_dsa_head
+#undef A_reselected_identify
+#undef A_reselected_tag
+#undef A_saved_dsa
+#undef A_schedule
+#undef A_test_dest
+#undef A_test_src
+#undef Ent_accept_message
+#undef Ent_cmdout_cmdout
+#undef Ent_command_complete
+#undef Ent_command_complete_msgin
+#undef Ent_data_transfer
+#undef Ent_datain_to_jump
+#undef Ent_debug_break
+#undef Ent_dsa_code_begin
+#undef Ent_dsa_code_check_reselect
+#undef Ent_dsa_code_fix_jump
+#undef Ent_dsa_code_restore_pointers
+#undef Ent_dsa_code_save_data_pointer
+#undef Ent_dsa_code_template
+#undef Ent_dsa_code_template_end
+#undef Ent_dsa_schedule
+#undef Ent_dsa_zero
+#undef Ent_end_data_transfer
+#undef Ent_initiator_abort
+#undef Ent_msg_in
+#undef Ent_msg_in_restart
+#undef Ent_other_in
+#undef Ent_other_out
+#undef Ent_other_transfer
+#undef Ent_reject_message
+#undef Ent_reselected_check_next
+#undef Ent_reselected_ok
+#undef Ent_respond_message
+#undef Ent_select
+#undef Ent_select_msgout
+#undef Ent_target_abort
+#undef Ent_test_1
+#undef Ent_test_2
+#undef Ent_test_2_msgout
+#undef Ent_wait_reselect
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
new file mode 100644
index 000000000000..41b5197ce4e6
--- /dev/null
+++ b/drivers/scsi/BusLogic.c
@@ -0,0 +1,3574 @@
+
+/*
+
+ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
+
+ Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for complete details.
+
+ The author respectfully requests that any modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+ advice has been invaluable, to David Gentzel, for writing the original Linux
+ BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+ Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+ Manager available as freely redistributable source code.
+
+*/
+
+#define BusLogic_DriverVersion "2.1.16"
+#define BusLogic_DriverDate "18 July 2002"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <scsi/scsicam.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include "BusLogic.h"
+#include "FlashPoint.c"
+
+#ifndef FAILURE
+#define FAILURE (-1)
+#endif
+
+static struct scsi_host_template Bus_Logic_template;
+
+/*
+ BusLogic_DriverOptionsCount is a count of the number of BusLogic Driver
+ Options specifications provided via the Linux Kernel Command Line or via
+ the Loadable Kernel Module Installation Facility.
+*/
+
+static int BusLogic_DriverOptionsCount;
+
+
+/*
+ BusLogic_DriverOptions is an array of Driver Options structures representing
+ BusLogic Driver Options specifications provided via the Linux Kernel Command
+ Line or via the Loadable Kernel Module Installation Facility.
+*/
+
+static struct BusLogic_DriverOptions BusLogic_DriverOptions[BusLogic_MaxHostAdapters];
+
+
+/*
+ BusLogic can be assigned a string by insmod.
+*/
+
+MODULE_LICENSE("GPL");
+#ifdef MODULE
+static char *BusLogic;
+module_param(BusLogic, charp, 0);
+#endif
+
+
+/*
+ BusLogic_ProbeOptions is a set of Probe Options to be applied across
+ all BusLogic Host Adapters.
+*/
+
+static struct BusLogic_ProbeOptions BusLogic_ProbeOptions;
+
+
+/*
+ BusLogic_GlobalOptions is a set of Global Options to be applied across
+ all BusLogic Host Adapters.
+*/
+
+static struct BusLogic_GlobalOptions BusLogic_GlobalOptions;
+
+static LIST_HEAD(BusLogic_host_list);
+
+/*
+ BusLogic_ProbeInfoCount is the number of entries in BusLogic_ProbeInfoList.
+*/
+
+static int BusLogic_ProbeInfoCount;
+
+
+/*
+ BusLogic_ProbeInfoList is the list of I/O Addresses and Bus Probe Information
+ to be checked for potential BusLogic Host Adapters. It is initialized by
+ interrogating the PCI Configuration Space on PCI machines as well as from the
+ list of standard BusLogic I/O Addresses.
+*/
+
+static struct BusLogic_ProbeInfo *BusLogic_ProbeInfoList;
+
+
+/*
+ BusLogic_CommandFailureReason holds a string identifying the reason why a
+ call to BusLogic_Command failed. It is only non-NULL when BusLogic_Command
+ returns a failure code.
+*/
+
+static char *BusLogic_CommandFailureReason;
+
+/*
+ BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+ Name, Copyright Notice, and Electronic Mail Address.
+*/
+
+static void BusLogic_AnnounceDriver(struct BusLogic_HostAdapter *HostAdapter)
+{
+ BusLogic_Announce("***** BusLogic SCSI Driver Version " BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n", HostAdapter);
+ BusLogic_Announce("Copyright 1995-1998 by Leonard N. Zubkoff " "<lnz@dandelion.com>\n", HostAdapter);
+}
+
+
+/*
+ BusLogic_DriverInfo returns the Host Adapter Name to identify this SCSI
+ Driver and Host Adapter.
+*/
+
+static const char *BusLogic_DriverInfo(struct Scsi_Host *Host)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Host->hostdata;
+ return HostAdapter->FullModelName;
+}
+
+/*
+ BusLogic_InitializeCCBs initializes a group of Command Control Blocks (CCBs)
+ for Host Adapter from the BlockSize bytes located at BlockPointer. The newly
+ created CCBs are added to Host Adapter's free list.
+*/
+
+static void BusLogic_InitializeCCBs(struct BusLogic_HostAdapter *HostAdapter, void *BlockPointer, int BlockSize, dma_addr_t BlockPointerHandle)
+{
+ struct BusLogic_CCB *CCB = (struct BusLogic_CCB *) BlockPointer;
+ unsigned int offset = 0;
+ memset(BlockPointer, 0, BlockSize);
+ CCB->AllocationGroupHead = BlockPointerHandle;
+ CCB->AllocationGroupSize = BlockSize;
+ while ((BlockSize -= sizeof(struct BusLogic_CCB)) >= 0) {
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->HostAdapter = HostAdapter;
+ CCB->DMA_Handle = (u32) BlockPointerHandle + offset;
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
+ CCB->CallbackFunction = BusLogic_QueueCompletedCCB;
+ CCB->BaseAddress = HostAdapter->FlashPointInfo.BaseAddress;
+ }
+ CCB->Next = HostAdapter->Free_CCBs;
+ CCB->NextAll = HostAdapter->All_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+ HostAdapter->All_CCBs = CCB;
+ HostAdapter->AllocatedCCBs++;
+ CCB++;
+ offset += sizeof(struct BusLogic_CCB);
+ }
+}
+
+
+/*
+ BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
+*/
+
+static boolean __init BusLogic_CreateInitialCCBs(struct BusLogic_HostAdapter *HostAdapter)
+{
+ int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(struct BusLogic_CCB);
+ void *BlockPointer;
+ dma_addr_t BlockPointerHandle;
+ while (HostAdapter->AllocatedCCBs < HostAdapter->InitialCCBs) {
+ BlockPointer = pci_alloc_consistent(HostAdapter->PCI_Device, BlockSize, &BlockPointerHandle);
+ if (BlockPointer == NULL) {
+ BusLogic_Error("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n", HostAdapter);
+ return false;
+ }
+ BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize, BlockPointerHandle);
+ }
+ return true;
+}
+
+
+/*
+ BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+*/
+
+static void BusLogic_DestroyCCBs(struct BusLogic_HostAdapter *HostAdapter)
+{
+ struct BusLogic_CCB *NextCCB = HostAdapter->All_CCBs, *CCB, *Last_CCB = NULL;
+ HostAdapter->All_CCBs = NULL;
+ HostAdapter->Free_CCBs = NULL;
+ while ((CCB = NextCCB) != NULL) {
+ NextCCB = CCB->NextAll;
+ if (CCB->AllocationGroupHead) {
+ if (Last_CCB)
+ pci_free_consistent(HostAdapter->PCI_Device, Last_CCB->AllocationGroupSize, Last_CCB, Last_CCB->AllocationGroupHead);
+ Last_CCB = CCB;
+ }
+ }
+ if (Last_CCB)
+ pci_free_consistent(HostAdapter->PCI_Device, Last_CCB->AllocationGroupSize, Last_CCB, Last_CCB->AllocationGroupHead);
+}
+
+
+/*
+ BusLogic_CreateAdditionalCCBs allocates Additional CCBs for Host Adapter. If
+ allocation fails and there are no remaining CCBs available, the Driver Queue
+ Depth is decreased to a known safe value to avoid potential deadlocks when
+ multiple host adapters share the same IRQ Channel.
+*/
+
+static void BusLogic_CreateAdditionalCCBs(struct BusLogic_HostAdapter *HostAdapter, int AdditionalCCBs, boolean SuccessMessageP)
+{
+ int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(struct BusLogic_CCB);
+ int PreviouslyAllocated = HostAdapter->AllocatedCCBs;
+ void *BlockPointer;
+ dma_addr_t BlockPointerHandle;
+ if (AdditionalCCBs <= 0)
+ return;
+ while (HostAdapter->AllocatedCCBs - PreviouslyAllocated < AdditionalCCBs) {
+ BlockPointer = pci_alloc_consistent(HostAdapter->PCI_Device, BlockSize, &BlockPointerHandle);
+ if (BlockPointer == NULL)
+ break;
+ BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize, BlockPointerHandle);
+ }
+ if (HostAdapter->AllocatedCCBs > PreviouslyAllocated) {
+ if (SuccessMessageP)
+ BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n", HostAdapter, HostAdapter->AllocatedCCBs - PreviouslyAllocated, HostAdapter->AllocatedCCBs);
+ return;
+ }
+ BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter);
+ if (HostAdapter->DriverQueueDepth > HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount) {
+ HostAdapter->DriverQueueDepth = HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount;
+ HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth;
+ }
+}
+
+/*
+ BusLogic_AllocateCCB allocates a CCB from Host Adapter's free list,
+ allocating more memory from the Kernel if necessary. The Host Adapter's
+ Lock should already have been acquired by the caller.
+*/
+
+static struct BusLogic_CCB *BusLogic_AllocateCCB(struct BusLogic_HostAdapter
+ *HostAdapter)
+{
+ static unsigned long SerialNumber = 0;
+ struct BusLogic_CCB *CCB;
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB != NULL) {
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ if (HostAdapter->Free_CCBs == NULL)
+ BusLogic_CreateAdditionalCCBs(HostAdapter, HostAdapter->IncrementalCCBs, true);
+ return CCB;
+ }
+ BusLogic_CreateAdditionalCCBs(HostAdapter, HostAdapter->IncrementalCCBs, true);
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB == NULL)
+ return NULL;
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ return CCB;
+}
+
+
+/*
+ BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+ free list. The Host Adapter's Lock should already have been acquired by the
+ caller.
+*/
+
+static void BusLogic_DeallocateCCB(struct BusLogic_CCB *CCB)
+{
+ struct BusLogic_HostAdapter *HostAdapter = CCB->HostAdapter;
+ struct scsi_cmnd *cmd = CCB->Command;
+
+ if (cmd->use_sg != 0) {
+ pci_unmap_sg(HostAdapter->PCI_Device,
+ (struct scatterlist *)cmd->request_buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ } else if (cmd->request_bufflen != 0) {
+ pci_unmap_single(HostAdapter->PCI_Device, CCB->DataPointer,
+ CCB->DataLength, cmd->sc_data_direction);
+ }
+ pci_unmap_single(HostAdapter->PCI_Device, CCB->SenseDataPointer,
+ CCB->SenseDataLength, PCI_DMA_FROMDEVICE);
+
+ CCB->Command = NULL;
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->Next = HostAdapter->Free_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+}
+
+
+/*
+ BusLogic_Command sends the command OperationCode to HostAdapter, optionally
+ providing ParameterLength bytes of ParameterData and receiving at most
+ ReplyLength bytes of ReplyData; any excess reply data is received but
+ discarded.
+
+ On success, this function returns the number of reply bytes read from
+ the Host Adapter (including any discarded data); on failure, it returns
+ -1 if the command was invalid, or -2 if a timeout occurred.
+
+ BusLogic_Command is called exclusively during host adapter detection and
+ initialization, so performance and latency are not critical, and exclusive
+ access to the Host Adapter hardware is assumed. Once the host adapter and
+ driver are initialized, the only Host Adapter command that is issued is the
+ single byte Execute Mailbox Command operation code, which does not require
+ waiting for the Host Adapter Ready bit to be set in the Status Register.
+*/
+
+static int BusLogic_Command(struct BusLogic_HostAdapter *HostAdapter, enum BusLogic_OperationCode OperationCode, void *ParameterData, int ParameterLength, void *ReplyData, int ReplyLength)
+{
+ unsigned char *ParameterPointer = (unsigned char *) ParameterData;
+ unsigned char *ReplyPointer = (unsigned char *) ReplyData;
+ union BusLogic_StatusRegister StatusRegister;
+ union BusLogic_InterruptRegister InterruptRegister;
+ unsigned long ProcessorFlags = 0;
+ int ReplyBytes = 0, Result;
+ long TimeoutCounter;
+ /*
+ Clear out the Reply Data if provided.
+ */
+ if (ReplyLength > 0)
+ memset(ReplyData, 0, ReplyLength);
+ /*
+ If the IRQ Channel has not yet been acquired, then interrupts must be
+ disabled while issuing host adapter commands since a Command Complete
+ interrupt could occur if the IRQ Channel was previously enabled by another
+ BusLogic Host Adapter or another driver sharing the same IRQ Channel.
+ */
+ if (!HostAdapter->IRQ_ChannelAcquired) {
+ local_irq_save(ProcessorFlags);
+ local_irq_disable();
+ }
+ /*
+ Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+ Register Busy bit to be reset in the Status Register.
+ */
+ TimeoutCounter = 10000;
+ while (--TimeoutCounter >= 0) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.HostAdapterReady && !StatusRegister.sr.CommandParameterRegisterBusy)
+ break;
+ udelay(100);
+ }
+ if (TimeoutCounter < 0) {
+ BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ Write the OperationCode to the Command/Parameter Register.
+ */
+ HostAdapter->HostAdapterCommandCompleted = false;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+ /*
+ Write any additional Parameter Bytes.
+ */
+ TimeoutCounter = 10000;
+ while (ParameterLength > 0 && --TimeoutCounter >= 0) {
+ /*
+ Wait 100 microseconds to give the Host Adapter enough time to determine
+ whether the last value written to the Command/Parameter Register was
+ valid or not. If the Command Complete bit is set in the Interrupt
+ Register, then the Command Invalid bit in the Status Register will be
+ reset if the Operation Code or Parameter was valid and the command
+ has completed, or set if the Operation Code or Parameter was invalid.
+ If the Data In Register Ready bit is set in the Status Register, then
+ the Operation Code was valid, and data is waiting to be read back
+ from the Host Adapter. Otherwise, wait for the Command/Parameter
+ Register Busy bit in the Status Register to be reset.
+ */
+ udelay(100);
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister.ir.CommandComplete)
+ break;
+ if (HostAdapter->HostAdapterCommandCompleted)
+ break;
+ if (StatusRegister.sr.DataInRegisterReady)
+ break;
+ if (StatusRegister.sr.CommandParameterRegisterBusy)
+ continue;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+ ParameterLength--;
+ }
+ if (TimeoutCounter < 0) {
+ BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ The Modify I/O Address command does not cause a Command Complete Interrupt.
+ */
+ if (OperationCode == BusLogic_ModifyIOAddress) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.CommandInvalid) {
+ BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+ Result = -1;
+ goto Done;
+ }
+ if (BusLogic_GlobalOptions.TraceConfiguration)
+ BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: " "(Modify I/O Address)\n", HostAdapter, OperationCode, StatusRegister.All);
+ Result = 0;
+ goto Done;
+ }
+ /*
+ Select an appropriate timeout value for awaiting command completion.
+ */
+ switch (OperationCode) {
+ case BusLogic_InquireInstalledDevicesID0to7:
+ case BusLogic_InquireInstalledDevicesID8to15:
+ case BusLogic_InquireTargetDevices:
+ /* Approximately 60 seconds. */
+ TimeoutCounter = 60 * 10000;
+ break;
+ default:
+ /* Approximately 1 second. */
+ TimeoutCounter = 10000;
+ break;
+ }
+ /*
+ Receive any Reply Bytes, waiting for either the Command Complete bit to
+ be set in the Interrupt Register, or for the Interrupt Handler to set the
+ Host Adapter Command Completed bit in the Host Adapter structure.
+ */
+ while (--TimeoutCounter >= 0) {
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister.ir.CommandComplete)
+ break;
+ if (HostAdapter->HostAdapterCommandCompleted)
+ break;
+ if (StatusRegister.sr.DataInRegisterReady) {
+ if (++ReplyBytes <= ReplyLength)
+ *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+ else
+ BusLogic_ReadDataInRegister(HostAdapter);
+ }
+ if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && StatusRegister.sr.HostAdapterReady)
+ break;
+ udelay(100);
+ }
+ if (TimeoutCounter < 0) {
+ BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ Clear any pending Command Complete Interrupt.
+ */
+ BusLogic_InterruptReset(HostAdapter);
+ /*
+ Provide tracing information if requested.
+ */
+ if (BusLogic_GlobalOptions.TraceConfiguration) {
+ int i;
+ BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", HostAdapter, OperationCode, StatusRegister.All, ReplyLength, ReplyBytes);
+ if (ReplyLength > ReplyBytes)
+ ReplyLength = ReplyBytes;
+ for (i = 0; i < ReplyLength; i++)
+ BusLogic_Notice(" %02X", HostAdapter, ((unsigned char *) ReplyData)[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ }
+ /*
+ Process Command Invalid conditions.
+ */
+ if (StatusRegister.sr.CommandInvalid) {
+ /*
+ Some early BusLogic Host Adapters may not recover properly from
+ a Command Invalid condition, so if this appears to be the case,
+ a Soft Reset is issued to the Host Adapter. Potentially invalid
+ commands are never attempted after Mailbox Initialization is
+ performed, so there should be no Host Adapter state lost by a
+ Soft Reset in response to a Command Invalid condition.
+ */
+ udelay(1000);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.CommandInvalid ||
+ StatusRegister.sr.Reserved ||
+ StatusRegister.sr.DataInRegisterReady ||
+ StatusRegister.sr.CommandParameterRegisterBusy || !StatusRegister.sr.HostAdapterReady || !StatusRegister.sr.InitializationRequired || StatusRegister.sr.DiagnosticActive || StatusRegister.sr.DiagnosticFailure) {
+ BusLogic_SoftReset(HostAdapter);
+ udelay(1000);
+ }
+ BusLogic_CommandFailureReason = "Command Invalid";
+ Result = -1;
+ goto Done;
+ }
+ /*
+ Handle Excess Parameters Supplied conditions.
+ */
+ if (ParameterLength > 0) {
+ BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+ Result = -1;
+ goto Done;
+ }
+ /*
+ Indicate the command completed successfully.
+ */
+ BusLogic_CommandFailureReason = NULL;
+ Result = ReplyBytes;
+ /*
+ Restore the interrupt status if necessary and return.
+ */
+ Done:
+ if (!HostAdapter->IRQ_ChannelAcquired)
+ local_irq_restore(ProcessorFlags);
+ return Result;
+}
+
+
+/*
+ BusLogic_AppendProbeAddressISA appends a single ISA I/O Address to the list
+ of I/O Address and Bus Probe Information to be checked for potential BusLogic
+ Host Adapters.
+*/
+
+static void __init BusLogic_AppendProbeAddressISA(unsigned long IO_Address)
+{
+ struct BusLogic_ProbeInfo *ProbeInfo;
+ if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
+ return;
+ ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Device = NULL;
+}
+
+
+/*
+ BusLogic_InitializeProbeInfoListISA initializes the list of I/O Address and
+ Bus Probe Information to be checked for potential BusLogic SCSI Host Adapters
+ only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
+*/
+
+static void __init BusLogic_InitializeProbeInfoListISA(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ /*
+ If BusLogic Driver Options specifications requested that ISA Bus Probes
+ be inhibited, do not proceed further.
+ */
+ if (BusLogic_ProbeOptions.NoProbeISA)
+ return;
+ /*
+ Append the list of standard BusLogic MultiMaster ISA I/O Addresses.
+ */
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe330 : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x330);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe334 : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x334);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe230 : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x230);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe234 : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x234);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe130 : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x130);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe134 : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x134);
+}
+
+
+#ifdef CONFIG_PCI
+
+
+/*
+ BusLogic_SortProbeInfo sorts a section of BusLogic_ProbeInfoList in order
+ of increasing PCI Bus and Device Number.
+*/
+
+static void __init BusLogic_SortProbeInfo(struct BusLogic_ProbeInfo *ProbeInfoList, int ProbeInfoCount)
+{
+ int LastInterchange = ProbeInfoCount - 1, Bound, j;
+ while (LastInterchange > 0) {
+ Bound = LastInterchange;
+ LastInterchange = 0;
+ for (j = 0; j < Bound; j++) {
+ struct BusLogic_ProbeInfo *ProbeInfo1 = &ProbeInfoList[j];
+ struct BusLogic_ProbeInfo *ProbeInfo2 = &ProbeInfoList[j + 1];
+ if (ProbeInfo1->Bus > ProbeInfo2->Bus || (ProbeInfo1->Bus == ProbeInfo2->Bus && (ProbeInfo1->Device > ProbeInfo2->Device))) {
+ struct BusLogic_ProbeInfo TempProbeInfo;
+ memcpy(&TempProbeInfo, ProbeInfo1, sizeof(struct BusLogic_ProbeInfo));
+ memcpy(ProbeInfo1, ProbeInfo2, sizeof(struct BusLogic_ProbeInfo));
+ memcpy(ProbeInfo2, &TempProbeInfo, sizeof(struct BusLogic_ProbeInfo));
+ LastInterchange = j;
+ }
+ }
+ }
+}
+
+
+/*
+ BusLogic_InitializeMultiMasterProbeInfo initializes the list of I/O Address
+ and Bus Probe Information to be checked for potential BusLogic MultiMaster
+ SCSI Host Adapters by interrogating the PCI Configuration Space on PCI
+ machines as well as from the list of standard BusLogic MultiMaster ISA
+ I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found.
+*/
+
+static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ struct BusLogic_ProbeInfo *PrimaryProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
+ int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
+ int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
+ boolean ForceBusDeviceScanningOrder = false;
+ boolean ForceBusDeviceScanningOrderChecked = false;
+ boolean StandardAddressSeen[6];
+ struct pci_dev *PCI_Device = NULL;
+ int i;
+ if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
+ return 0;
+ BusLogic_ProbeInfoCount++;
+ for (i = 0; i < 6; i++)
+ StandardAddressSeen[i] = false;
+ /*
+ Iterate over the MultiMaster PCI Host Adapters. For each enumerated host
+ adapter, determine whether its ISA Compatible I/O Port is enabled and if
+ so, whether it is assigned the Primary I/O Address. A host adapter that is
+ assigned the Primary I/O Address will always be the preferred boot device.
+ The MultiMaster BIOS will first recognize a host adapter at the Primary I/O
+ Address, then any other PCI host adapters, and finally any host adapters
+ located at the remaining standard ISA I/O Addresses. When a PCI host
+ adapter is found with its ISA Compatible I/O Port enabled, a command is
+ issued to disable the ISA Compatible I/O Port, and it is noted that the
+ particular standard ISA I/O Address need not be probed.
+ */
+ PrimaryProbeInfo->IO_Address = 0;
+ while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, PCI_Device)) != NULL) {
+ struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
+ struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation;
+ enum BusLogic_ISACompatibleIOPort ModifyIOAddressRequest;
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned int IRQ_Channel;
+ unsigned long BaseAddress0;
+ unsigned long BaseAddress1;
+ unsigned long IO_Address;
+ unsigned long PCI_Address;
+
+ if (pci_enable_device(PCI_Device))
+ continue;
+
+ if (pci_set_dma_mask(PCI_Device, (u64) 0xffffffff))
+ continue;
+
+ Bus = PCI_Device->bus->number;
+ Device = PCI_Device->devfn >> 3;
+ IRQ_Channel = PCI_Device->irq;
+ IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0);
+ PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1);
+
+ if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) {
+ BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "MultiMaster Host Adapter\n", NULL, BaseAddress0);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (pci_resource_flags(PCI_Device, 1) & IORESOURCE_IO) {
+ BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "MultiMaster Host Adapter\n", NULL, BaseAddress1);
+ BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n", NULL, Bus, Device, PCI_Address);
+ continue;
+ }
+ if (IRQ_Channel == 0) {
+ BusLogic_Error("BusLogic: IRQ Channel %d invalid for " "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe) {
+ BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter " "detected at\n", NULL);
+ BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address " "0x%X PCI Address 0x%X\n", NULL, Bus, Device, IO_Address, PCI_Address);
+ }
+ /*
+ Issue the Inquire PCI Host Adapter Information command to determine
+ the ISA Compatible I/O Port. If the ISA Compatible I/O Port is
+ known and enabled, note that the particular Standard ISA I/O
+ Address should not be probed.
+ */
+ HostAdapter->IO_Address = IO_Address;
+ BusLogic_InterruptReset(HostAdapter);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation))
+ == sizeof(PCIHostAdapterInformation)) {
+ if (PCIHostAdapterInformation.ISACompatibleIOPort < 6)
+ StandardAddressSeen[PCIHostAdapterInformation.ISACompatibleIOPort] = true;
+ } else
+ PCIHostAdapterInformation.ISACompatibleIOPort = BusLogic_IO_Disable;
+ /*
+ * Issue the Modify I/O Address command to disable the ISA Compatible
+ * I/O Port. On PCI Host Adapters, the Modify I/O Address command
+ * allows modification of the ISA compatible I/O Address that the Host
+ * Adapter responds to; it does not affect the PCI compliant I/O Address
+ * assigned at system initialization.
+ */
+ ModifyIOAddressRequest = BusLogic_IO_Disable;
+ BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress, &ModifyIOAddressRequest, sizeof(ModifyIOAddressRequest), NULL, 0);
+ /*
+ For the first MultiMaster Host Adapter enumerated, issue the Fetch
+ Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
+ for the setting of the "Use Bus And Device # For PCI Scanning Seq."
+ option. Issue the Inquire Board ID command since this option is
+ only valid for the BT-948/958/958D.
+ */
+ if (!ForceBusDeviceScanningOrderChecked) {
+ struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
+ struct BusLogic_AutoSCSIByte45 AutoSCSIByte45;
+ struct BusLogic_BoardID BoardID;
+ FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_AutoSCSI_BaseOffset + 45;
+ FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte45);
+ BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &AutoSCSIByte45, sizeof(AutoSCSIByte45));
+ BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, &BoardID, sizeof(BoardID));
+ if (BoardID.FirmwareVersion1stDigit == '5')
+ ForceBusDeviceScanningOrder = AutoSCSIByte45.ForceBusDeviceScanningOrder;
+ ForceBusDeviceScanningOrderChecked = true;
+ }
+ /*
+ Determine whether this MultiMaster Host Adapter has its ISA
+ Compatible I/O Port enabled and is assigned the Primary I/O Address.
+ If it does, then it is the Primary MultiMaster Host Adapter and must
+ be recognized first. If it does not, then it is added to the list
+ for probing after any Primary MultiMaster Host Adapter is probed.
+ */
+ if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330) {
+ PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ PrimaryProbeInfo->IO_Address = IO_Address;
+ PrimaryProbeInfo->PCI_Address = PCI_Address;
+ PrimaryProbeInfo->Bus = Bus;
+ PrimaryProbeInfo->Device = Device;
+ PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
+ PrimaryProbeInfo->PCI_Device = PCI_Device;
+ PCIMultiMasterCount++;
+ } else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Address = PCI_Address;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ ProbeInfo->PCI_Device = PCI_Device;
+ NonPrimaryPCIMultiMasterCount++;
+ PCIMultiMasterCount++;
+ } else
+ BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL);
+ }
+ /*
+ If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option is ON
+ for the first enumerated MultiMaster Host Adapter, and if that host adapter
+ is a BT-948/958/958D, then the MultiMaster BIOS will recognize MultiMaster
+ Host Adapters in the order of increasing PCI Bus and Device Number. In
+ that case, sort the probe information into the same order the BIOS uses.
+ If this option is OFF, then the MultiMaster BIOS will recognize MultiMaster
+ Host Adapters in the order they are enumerated by the PCI BIOS, and hence
+ no sorting is necessary.
+ */
+ if (ForceBusDeviceScanningOrder)
+ BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[NonPrimaryPCIMultiMasterIndex], NonPrimaryPCIMultiMasterCount);
+ /*
+ If no PCI MultiMaster Host Adapter is assigned the Primary I/O Address,
+ then the Primary I/O Address must be probed explicitly before any PCI
+ host adapters are probed.
+ */
+ if (!BusLogic_ProbeOptions.NoProbeISA)
+ if (PrimaryProbeInfo->IO_Address == 0 && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe330 : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)) {
+ PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+ PrimaryProbeInfo->IO_Address = 0x330;
+ }
+ /*
+ Append the list of standard BusLogic MultiMaster ISA I/O Addresses,
+ omitting the Primary I/O Address which has already been handled.
+ */
+ if (!BusLogic_ProbeOptions.NoProbeISA) {
+ if (!StandardAddressSeen[1] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe334 : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x334);
+ if (!StandardAddressSeen[2] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe230 : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x230);
+ if (!StandardAddressSeen[3] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe234 : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x234);
+ if (!StandardAddressSeen[4] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe130 : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x130);
+ if (!StandardAddressSeen[5] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe134 : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x134);
+ }
+ /*
+ Iterate over the older non-compliant MultiMaster PCI Host Adapters,
+ noting the PCI bus location and assigned IRQ Channel.
+ */
+ PCI_Device = NULL;
+ while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, PCI_Device)) != NULL) {
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned int IRQ_Channel;
+ unsigned long IO_Address;
+
+ if (pci_enable_device(PCI_Device))
+ continue;
+
+ if (pci_set_dma_mask(PCI_Device, (u64) 0xffffffff))
+ continue;
+
+ Bus = PCI_Device->bus->number;
+ Device = PCI_Device->devfn >> 3;
+ IRQ_Channel = PCI_Device->irq;
+ IO_Address = pci_resource_start(PCI_Device, 0);
+
+ if (IO_Address == 0 || IRQ_Channel == 0)
+ continue;
+ for (i = 0; i < BusLogic_ProbeInfoCount; i++) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[i];
+ if (ProbeInfo->IO_Address == IO_Address && ProbeInfo->HostAdapterType == BusLogic_MultiMaster) {
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->PCI_Address = 0;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ ProbeInfo->PCI_Device = PCI_Device;
+ break;
+ }
+ }
+ }
+ return PCIMultiMasterCount;
+}
+
+
+/*
+ BusLogic_InitializeFlashPointProbeInfo initializes the list of I/O Address
+ and Bus Probe Information to be checked for potential BusLogic FlashPoint
+ Host Adapters by interrogating the PCI Configuration Space. It returns the
+ number of FlashPoint Host Adapters found.
+*/
+
+static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
+ struct pci_dev *PCI_Device = NULL;
+ /*
+ Interrogate PCI Configuration Space for any FlashPoint Host Adapters.
+ */
+ while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, PCI_Device)) != NULL) {
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned int IRQ_Channel;
+ unsigned long BaseAddress0;
+ unsigned long BaseAddress1;
+ unsigned long IO_Address;
+ unsigned long PCI_Address;
+
+ if (pci_enable_device(PCI_Device))
+ continue;
+
+ if (pci_set_dma_mask(PCI_Device, (u64) 0xffffffff))
+ continue;
+
+ Bus = PCI_Device->bus->number;
+ Device = PCI_Device->devfn >> 3;
+ IRQ_Channel = PCI_Device->irq;
+ IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0);
+ PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1);
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+ if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) {
+ BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "FlashPoint Host Adapter\n", NULL, BaseAddress0);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (pci_resource_flags(PCI_Device, 1) & IORESOURCE_IO) {
+ BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "FlashPoint Host Adapter\n", NULL, BaseAddress1);
+ BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n", NULL, Bus, Device, PCI_Address);
+ continue;
+ }
+ if (IRQ_Channel == 0) {
+ BusLogic_Error("BusLogic: IRQ Channel %d invalid for " "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe) {
+ BusLogic_Notice("BusLogic: FlashPoint Host Adapter " "detected at\n", NULL);
+ BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address " "0x%X PCI Address 0x%X\n", NULL, Bus, Device, IO_Address, PCI_Address);
+ }
+ if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Address = PCI_Address;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ ProbeInfo->PCI_Device = PCI_Device;
+ FlashPointCount++;
+ } else
+ BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL);
+#else
+ BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", NULL, Bus, Device);
+ BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, irq %d, " "but FlashPoint\n", NULL, IO_Address, PCI_Address, IRQ_Channel);
+ BusLogic_Error("BusLogic: support was omitted in this kernel " "configuration.\n", NULL);
+#endif
+ }
+ /*
+ The FlashPoint BIOS will scan for FlashPoint Host Adapters in the order of
+ increasing PCI Bus and Device Number, so sort the probe information into
+ the same order the BIOS uses.
+ */
+ BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[FlashPointIndex], FlashPointCount);
+ return FlashPointCount;
+}
+
+
+/*
+ BusLogic_InitializeProbeInfoList initializes the list of I/O Address and Bus
+ Probe Information to be checked for potential BusLogic SCSI Host Adapters by
+ interrogating the PCI Configuration Space on PCI machines as well as from the
+ list of standard BusLogic MultiMaster ISA I/O Addresses. By default, if both
+ FlashPoint and PCI MultiMaster Host Adapters are present, this driver will
+ probe for FlashPoint Host Adapters first unless the BIOS primary disk is
+ controlled by the first PCI MultiMaster Host Adapter, in which case
+ MultiMaster Host Adapters will be probed first. The BusLogic Driver Options
+ specifications "MultiMasterFirst" and "FlashPointFirst" can be used to force
+ a particular probe order.
+*/
+
+static void __init BusLogic_InitializeProbeInfoList(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ /*
+ If a PCI BIOS is present, interrogate it for MultiMaster and FlashPoint
+ Host Adapters; otherwise, default to the standard ISA MultiMaster probe.
+ */
+ if (!BusLogic_ProbeOptions.NoProbePCI) {
+ if (BusLogic_ProbeOptions.MultiMasterFirst) {
+ BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
+ } else if (BusLogic_ProbeOptions.FlashPointFirst) {
+ BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
+ BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ } else {
+ int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
+ int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ if (FlashPointCount > 0 && PCIMultiMasterCount > 0) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[FlashPointCount];
+ struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
+ struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
+ struct BusLogic_BIOSDriveMapByte Drive0MapByte;
+ while (ProbeInfo->HostAdapterBusType != BusLogic_PCI_Bus)
+ ProbeInfo++;
+ HostAdapter->IO_Address = ProbeInfo->IO_Address;
+ FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_BIOS_BaseOffset + BusLogic_BIOS_DriveMapOffset + 0;
+ FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(Drive0MapByte);
+ BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &Drive0MapByte, sizeof(Drive0MapByte));
+ /*
+ If the Map Byte for BIOS Drive 0 indicates that BIOS Drive 0
+ is controlled by this PCI MultiMaster Host Adapter, then
+ reverse the probe order so that MultiMaster Host Adapters are
+ probed before FlashPoint Host Adapters.
+ */
+ if (Drive0MapByte.DiskGeometry != BusLogic_BIOS_Disk_Not_Installed) {
+ struct BusLogic_ProbeInfo SavedProbeInfo[BusLogic_MaxHostAdapters];
+ int MultiMasterCount = BusLogic_ProbeInfoCount - FlashPointCount;
+ memcpy(SavedProbeInfo, BusLogic_ProbeInfoList, BusLogic_ProbeInfoCount * sizeof(struct BusLogic_ProbeInfo));
+ memcpy(&BusLogic_ProbeInfoList[0], &SavedProbeInfo[FlashPointCount], MultiMasterCount * sizeof(struct BusLogic_ProbeInfo));
+ memcpy(&BusLogic_ProbeInfoList[MultiMasterCount], &SavedProbeInfo[0], FlashPointCount * sizeof(struct BusLogic_ProbeInfo));
+ }
+ }
+ }
+ } else
+ BusLogic_InitializeProbeInfoListISA(PrototypeHostAdapter);
+}
+
+
+#endif /* CONFIG_PCI */
+
+
+/*
+ BusLogic_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean BusLogic_Failure(struct BusLogic_HostAdapter *HostAdapter, char *ErrorMessage)
+{
+ BusLogic_AnnounceDriver(HostAdapter);
+ if (HostAdapter->HostAdapterBusType == BusLogic_PCI_Bus) {
+ BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n", HostAdapter);
+ BusLogic_Error("Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device, HostAdapter->IO_Address, HostAdapter->PCI_Address);
+ } else
+ BusLogic_Error("While configuring BusLogic Host Adapter at " "I/O Address 0x%X:\n", HostAdapter, HostAdapter->IO_Address);
+ BusLogic_Error("%s FAILED - DETACHING\n", HostAdapter, ErrorMessage);
+ if (BusLogic_CommandFailureReason != NULL)
+ BusLogic_Error("ADDITIONAL FAILURE INFO - %s\n", HostAdapter, BusLogic_CommandFailureReason);
+ return false;
+}
+
+
+/*
+ BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
+*/
+
+static boolean __init BusLogic_ProbeHostAdapter(struct BusLogic_HostAdapter *HostAdapter)
+{
+ union BusLogic_StatusRegister StatusRegister;
+ union BusLogic_InterruptRegister InterruptRegister;
+ union BusLogic_GeometryRegister GeometryRegister;
+ /*
+ FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
+ struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo;
+ FlashPointInfo->BaseAddress = (u32) HostAdapter->IO_Address;
+ FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
+ FlashPointInfo->Present = false;
+ if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && FlashPointInfo->Present)) {
+ BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device);
+ BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, " "but FlashPoint\n", HostAdapter, HostAdapter->IO_Address, HostAdapter->PCI_Address);
+ BusLogic_Error("BusLogic: Probe Function failed to validate it.\n", HostAdapter);
+ return false;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe)
+ BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Found\n", HostAdapter, HostAdapter->IO_Address);
+ /*
+ Indicate the Host Adapter Probe completed successfully.
+ */
+ return true;
+ }
+ /*
+ Read the Status, Interrupt, and Geometry Registers to test if there are I/O
+ ports that respond, and to check the values to determine if they are from a
+ BusLogic Host Adapter. A nonexistent I/O port will return 0xFF, in which
+ case there is definitely no BusLogic Host Adapter at this base I/O Address.
+ The test here is a subset of that used by the BusLogic Host Adapter BIOS.
+ */
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
+ if (BusLogic_GlobalOptions.TraceProbe)
+ BusLogic_Notice("BusLogic_Probe(0x%X): Status 0x%02X, Interrupt 0x%02X, " "Geometry 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All, InterruptRegister.All, GeometryRegister.All);
+ if (StatusRegister.All == 0 || StatusRegister.sr.DiagnosticActive || StatusRegister.sr.CommandParameterRegisterBusy || StatusRegister.sr.Reserved || StatusRegister.sr.CommandInvalid || InterruptRegister.ir.Reserved != 0)
+ return false;
+ /*
+ Check the undocumented Geometry Register to test if there is an I/O port
+ that responded. Adaptec Host Adapters do not implement the Geometry
+ Register, so this test helps serve to avoid incorrectly recognizing an
+ Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C
+ series does respond to the Geometry Register I/O port, but it will be
+ rejected later when the Inquire Extended Setup Information command is
+ issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a
+ BusLogic clone that implements the same interface as earlier BusLogic
+ Host Adapters, including the undocumented commands, and is therefore
+ supported by this driver. However, the AMI FastDisk always returns 0x00
+ upon reading the Geometry Register, so the extended translation option
+ should always be left disabled on the AMI FastDisk.
+ */
+ if (GeometryRegister.All == 0xFF)
+ return false;
+ /*
+ Indicate the Host Adapter Probe completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_HardwareResetHostAdapter issues a Hardware Reset to the Host Adapter
+ and waits for Host Adapter Diagnostics to complete. If HardReset is true, a
+ Hard Reset is performed which also initiates a SCSI Bus Reset. Otherwise, a
+ Soft Reset is performed which only resets the Host Adapter without forcing a
+ SCSI Bus Reset.
+*/
+
+static boolean BusLogic_HardwareResetHostAdapter(struct BusLogic_HostAdapter
+ *HostAdapter, boolean HardReset)
+{
+ union BusLogic_StatusRegister StatusRegister;
+ int TimeoutCounter;
+ /*
+ FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
+ struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo;
+ FlashPointInfo->HostSoftReset = !HardReset;
+ FlashPointInfo->ReportDataUnderrun = true;
+ HostAdapter->CardHandle = FlashPoint_HardwareResetHostAdapter(FlashPointInfo);
+ if (HostAdapter->CardHandle == FlashPoint_BadCardHandle)
+ return false;
+ /*
+ Indicate the Host Adapter Hard Reset completed successfully.
+ */
+ return true;
+ }
+ /*
+ Issue a Hard Reset or Soft Reset Command to the Host Adapter. The Host
+ Adapter should respond by setting Diagnostic Active in the Status Register.
+ */
+ if (HardReset)
+ BusLogic_HardReset(HostAdapter);
+ else
+ BusLogic_SoftReset(HostAdapter);
+ /*
+ Wait until Diagnostic Active is set in the Status Register.
+ */
+ TimeoutCounter = 5 * 10000;
+ while (--TimeoutCounter >= 0) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.DiagnosticActive)
+ break;
+ udelay(100);
+ }
+ if (BusLogic_GlobalOptions.TraceHardwareReset)
+ BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Active, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All);
+ if (TimeoutCounter < 0)
+ return false;
+ /*
+ Wait 100 microseconds to allow completion of any initial diagnostic
+ activity which might leave the contents of the Status Register
+ unpredictable.
+ */
+ udelay(100);
+ /*
+ Wait until Diagnostic Active is reset in the Status Register.
+ */
+ TimeoutCounter = 10 * 10000;
+ while (--TimeoutCounter >= 0) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (!StatusRegister.sr.DiagnosticActive)
+ break;
+ udelay(100);
+ }
+ if (BusLogic_GlobalOptions.TraceHardwareReset)
+ BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Completed, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All);
+ if (TimeoutCounter < 0)
+ return false;
+ /*
+ Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
+ or Data In Register Ready bits is set in the Status Register.
+ */
+ TimeoutCounter = 10000;
+ while (--TimeoutCounter >= 0) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.DiagnosticFailure || StatusRegister.sr.HostAdapterReady || StatusRegister.sr.DataInRegisterReady)
+ break;
+ udelay(100);
+ }
+ if (BusLogic_GlobalOptions.TraceHardwareReset)
+ BusLogic_Notice("BusLogic_HardwareReset(0x%X): Host Adapter Ready, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All);
+ if (TimeoutCounter < 0)
+ return false;
+ /*
+ If Diagnostic Failure is set or Host Adapter Ready is reset, then an
+ error occurred during the Host Adapter diagnostics. If Data In Register
+ Ready is set, then there is an Error Code available.
+ */
+ if (StatusRegister.sr.DiagnosticFailure || !StatusRegister.sr.HostAdapterReady) {
+ BusLogic_CommandFailureReason = NULL;
+ BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
+ BusLogic_Error("HOST ADAPTER STATUS REGISTER = %02X\n", HostAdapter, StatusRegister.All);
+ if (StatusRegister.sr.DataInRegisterReady) {
+ unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
+ BusLogic_Error("HOST ADAPTER ERROR CODE = %d\n", HostAdapter, ErrorCode);
+ }
+ return false;
+ }
+ /*
+ Indicate the Host Adapter Hard Reset completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
+ Host Adapter.
+*/
+
+static boolean __init BusLogic_CheckHostAdapter(struct BusLogic_HostAdapter *HostAdapter)
+{
+ struct BusLogic_ExtendedSetupInformation ExtendedSetupInformation;
+ unsigned char RequestedReplyLength;
+ boolean Result = true;
+ /*
+ FlashPoint Host Adapters do not require this protection.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ return true;
+ /*
+ Issue the Inquire Extended Setup Information command. Only genuine
+ BusLogic Host Adapters and true clones support this command. Adaptec 1542C
+ series Host Adapters that respond to the Geometry Register I/O port will
+ fail this command.
+ */
+ RequestedReplyLength = sizeof(ExtendedSetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &ExtendedSetupInformation, sizeof(ExtendedSetupInformation))
+ != sizeof(ExtendedSetupInformation))
+ Result = false;
+ /*
+ Provide tracing information if requested and return.
+ */
+ if (BusLogic_GlobalOptions.TraceProbe)
+ BusLogic_Notice("BusLogic_Check(0x%X): MultiMaster %s\n", HostAdapter, HostAdapter->IO_Address, (Result ? "Found" : "Not Found"));
+ return Result;
+}
+
+
+/*
+ BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
+ from Host Adapter and initializes the Host Adapter structure.
+*/
+
+static boolean __init BusLogic_ReadHostAdapterConfiguration(struct BusLogic_HostAdapter
+ *HostAdapter)
+{
+ struct BusLogic_BoardID BoardID;
+ struct BusLogic_Configuration Configuration;
+ struct BusLogic_SetupInformation SetupInformation;
+ struct BusLogic_ExtendedSetupInformation ExtendedSetupInformation;
+ unsigned char HostAdapterModelNumber[5];
+ unsigned char FirmwareVersion3rdDigit;
+ unsigned char FirmwareVersionLetter;
+ struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation;
+ struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
+ struct BusLogic_AutoSCSIData AutoSCSIData;
+ union BusLogic_GeometryRegister GeometryRegister;
+ unsigned char RequestedReplyLength;
+ unsigned char *TargetPointer, Character;
+ int TargetID, i;
+ /*
+ Configuration Information for FlashPoint Host Adapters is provided in the
+ FlashPoint_Info structure by the FlashPoint SCCB Manager's Probe Function.
+ Initialize fields in the Host Adapter structure from the FlashPoint_Info
+ structure.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
+ struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo;
+ TargetPointer = HostAdapter->ModelName;
+ *TargetPointer++ = 'B';
+ *TargetPointer++ = 'T';
+ *TargetPointer++ = '-';
+ for (i = 0; i < sizeof(FlashPointInfo->ModelNumber); i++)
+ *TargetPointer++ = FlashPointInfo->ModelNumber[i];
+ *TargetPointer++ = '\0';
+ strcpy(HostAdapter->FirmwareVersion, FlashPoint_FirmwareVersion);
+ HostAdapter->SCSI_ID = FlashPointInfo->SCSI_ID;
+ HostAdapter->ExtendedTranslationEnabled = FlashPointInfo->ExtendedTranslationEnabled;
+ HostAdapter->ParityCheckingEnabled = FlashPointInfo->ParityCheckingEnabled;
+ HostAdapter->BusResetEnabled = !FlashPointInfo->HostSoftReset;
+ HostAdapter->LevelSensitiveInterrupt = true;
+ HostAdapter->HostWideSCSI = FlashPointInfo->HostWideSCSI;
+ HostAdapter->HostDifferentialSCSI = false;
+ HostAdapter->HostSupportsSCAM = true;
+ HostAdapter->HostUltraSCSI = true;
+ HostAdapter->ExtendedLUNSupport = true;
+ HostAdapter->TerminationInfoValid = true;
+ HostAdapter->LowByteTerminated = FlashPointInfo->LowByteTerminated;
+ HostAdapter->HighByteTerminated = FlashPointInfo->HighByteTerminated;
+ HostAdapter->SCAM_Enabled = FlashPointInfo->SCAM_Enabled;
+ HostAdapter->SCAM_Level2 = FlashPointInfo->SCAM_Level2;
+ HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+ HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
+ HostAdapter->MaxLogicalUnits = 32;
+ HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize;
+ HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize;
+ HostAdapter->DriverQueueDepth = 255;
+ HostAdapter->HostAdapterQueueDepth = HostAdapter->DriverQueueDepth;
+ HostAdapter->SynchronousPermitted = FlashPointInfo->SynchronousPermitted;
+ HostAdapter->FastPermitted = FlashPointInfo->FastPermitted;
+ HostAdapter->UltraPermitted = FlashPointInfo->UltraPermitted;
+ HostAdapter->WidePermitted = FlashPointInfo->WidePermitted;
+ HostAdapter->DisconnectPermitted = FlashPointInfo->DisconnectPermitted;
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ goto Common;
+ }
+ /*
+ Issue the Inquire Board ID command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, &BoardID, sizeof(BoardID)) != sizeof(BoardID))
+ return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID");
+ /*
+ Issue the Inquire Configuration command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0, &Configuration, sizeof(Configuration))
+ != sizeof(Configuration))
+ return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION");
+ /*
+ Issue the Inquire Setup Information command.
+ */
+ RequestedReplyLength = sizeof(SetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &SetupInformation, sizeof(SetupInformation))
+ != sizeof(SetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ /*
+ Issue the Inquire Extended Setup Information command.
+ */
+ RequestedReplyLength = sizeof(ExtendedSetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &ExtendedSetupInformation, sizeof(ExtendedSetupInformation))
+ != sizeof(ExtendedSetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
+ /*
+ Issue the Inquire Firmware Version 3rd Digit command.
+ */
+ FirmwareVersion3rdDigit = '\0';
+ if (BoardID.FirmwareVersion1stDigit > '0')
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit, NULL, 0, &FirmwareVersion3rdDigit, sizeof(FirmwareVersion3rdDigit))
+ != sizeof(FirmwareVersion3rdDigit))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+ /*
+ Issue the Inquire Host Adapter Model Number command.
+ */
+ if (ExtendedSetupInformation.BusType == 'A' && BoardID.FirmwareVersion1stDigit == '2')
+ /* BusLogic BT-542B ISA 2.xx */
+ strcpy(HostAdapterModelNumber, "542B");
+ else if (ExtendedSetupInformation.BusType == 'E' && BoardID.FirmwareVersion1stDigit == '2' && (BoardID.FirmwareVersion2ndDigit <= '1' || (BoardID.FirmwareVersion2ndDigit == '2' && FirmwareVersion3rdDigit == '0')))
+ /* BusLogic BT-742A EISA 2.1x or 2.20 */
+ strcpy(HostAdapterModelNumber, "742A");
+ else if (ExtendedSetupInformation.BusType == 'E' && BoardID.FirmwareVersion1stDigit == '0')
+ /* AMI FastDisk EISA Series 441 0.x */
+ strcpy(HostAdapterModelNumber, "747A");
+ else {
+ RequestedReplyLength = sizeof(HostAdapterModelNumber);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireHostAdapterModelNumber, &RequestedReplyLength, sizeof(RequestedReplyLength), &HostAdapterModelNumber, sizeof(HostAdapterModelNumber))
+ != sizeof(HostAdapterModelNumber))
+ return BusLogic_Failure(HostAdapter, "INQUIRE HOST ADAPTER MODEL NUMBER");
+ }
+ /*
+ BusLogic MultiMaster Host Adapters can be identified by their model number
+ and the major version number of their firmware as follows:
+
+ 5.xx BusLogic "W" Series Host Adapters:
+ BT-948/958/958D
+ 4.xx BusLogic "C" Series Host Adapters:
+ BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+ 3.xx BusLogic "S" Series Host Adapters:
+ BT-747S/747D/757S/757D/445S/545S/542D
+ BT-542B/742A (revision H)
+ 2.xx BusLogic "A" Series Host Adapters:
+ BT-542B/742A (revision G and below)
+ 0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter
+ */
+ /*
+ Save the Model Name and Host Adapter Name in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->ModelName;
+ *TargetPointer++ = 'B';
+ *TargetPointer++ = 'T';
+ *TargetPointer++ = '-';
+ for (i = 0; i < sizeof(HostAdapterModelNumber); i++) {
+ Character = HostAdapterModelNumber[i];
+ if (Character == ' ' || Character == '\0')
+ break;
+ *TargetPointer++ = Character;
+ }
+ *TargetPointer++ = '\0';
+ /*
+ Save the Firmware Version in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->FirmwareVersion;
+ *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
+ *TargetPointer++ = '.';
+ *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
+ if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
+ *TargetPointer++ = FirmwareVersion3rdDigit;
+ *TargetPointer = '\0';
+ /*
+ Issue the Inquire Firmware Version Letter command.
+ */
+ if (strcmp(HostAdapter->FirmwareVersion, "3.3") >= 0) {
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter, NULL, 0, &FirmwareVersionLetter, sizeof(FirmwareVersionLetter))
+ != sizeof(FirmwareVersionLetter))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+ if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+ *TargetPointer++ = FirmwareVersionLetter;
+ *TargetPointer = '\0';
+ }
+ /*
+ Save the Host Adapter SCSI ID in the Host Adapter structure.
+ */
+ HostAdapter->SCSI_ID = Configuration.HostAdapterID;
+ /*
+ Determine the Bus Type and save it in the Host Adapter structure, determine
+ and save the IRQ Channel if necessary, and determine and save the DMA
+ Channel for ISA Host Adapters.
+ */
+ HostAdapter->HostAdapterBusType = BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4'];
+ if (HostAdapter->IRQ_Channel == 0) {
+ if (Configuration.IRQ_Channel9)
+ HostAdapter->IRQ_Channel = 9;
+ else if (Configuration.IRQ_Channel10)
+ HostAdapter->IRQ_Channel = 10;
+ else if (Configuration.IRQ_Channel11)
+ HostAdapter->IRQ_Channel = 11;
+ else if (Configuration.IRQ_Channel12)
+ HostAdapter->IRQ_Channel = 12;
+ else if (Configuration.IRQ_Channel14)
+ HostAdapter->IRQ_Channel = 14;
+ else if (Configuration.IRQ_Channel15)
+ HostAdapter->IRQ_Channel = 15;
+ }
+ if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus) {
+ if (Configuration.DMA_Channel5)
+ HostAdapter->DMA_Channel = 5;
+ else if (Configuration.DMA_Channel6)
+ HostAdapter->DMA_Channel = 6;
+ else if (Configuration.DMA_Channel7)
+ HostAdapter->DMA_Channel = 7;
+ }
+ /*
+ Determine whether Extended Translation is enabled and save it in
+ the Host Adapter structure.
+ */
+ GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
+ HostAdapter->ExtendedTranslationEnabled = GeometryRegister.gr.ExtendedTranslationEnabled;
+ /*
+ Save the Scatter Gather Limits, Level Sensitive Interrupt flag, Wide
+ SCSI flag, Differential SCSI flag, SCAM Supported flag, and
+ Ultra SCSI flag in the Host Adapter structure.
+ */
+ HostAdapter->HostAdapterScatterGatherLimit = ExtendedSetupInformation.ScatterGatherLimit;
+ HostAdapter->DriverScatterGatherLimit = HostAdapter->HostAdapterScatterGatherLimit;
+ if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
+ HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+ if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupt)
+ HostAdapter->LevelSensitiveInterrupt = true;
+ HostAdapter->HostWideSCSI = ExtendedSetupInformation.HostWideSCSI;
+ HostAdapter->HostDifferentialSCSI = ExtendedSetupInformation.HostDifferentialSCSI;
+ HostAdapter->HostSupportsSCAM = ExtendedSetupInformation.HostSupportsSCAM;
+ HostAdapter->HostUltraSCSI = ExtendedSetupInformation.HostUltraSCSI;
+ /*
+ Determine whether Extended LUN Format CCBs are supported and save the
+ information in the Host Adapter structure.
+ */
+ if (HostAdapter->FirmwareVersion[0] == '5' || (HostAdapter->FirmwareVersion[0] == '4' && HostAdapter->HostWideSCSI))
+ HostAdapter->ExtendedLUNSupport = true;
+ /*
+ Issue the Inquire PCI Host Adapter Information command to read the
+ Termination Information from "W" series MultiMaster Host Adapters.
+ */
+ if (HostAdapter->FirmwareVersion[0] == '5') {
+ if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation))
+ != sizeof(PCIHostAdapterInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE PCI HOST ADAPTER INFORMATION");
+ /*
+ Save the Termination Information in the Host Adapter structure.
+ */
+ if (PCIHostAdapterInformation.GenericInfoValid) {
+ HostAdapter->TerminationInfoValid = true;
+ HostAdapter->LowByteTerminated = PCIHostAdapterInformation.LowByteTerminated;
+ HostAdapter->HighByteTerminated = PCIHostAdapterInformation.HighByteTerminated;
+ }
+ }
+ /*
+ Issue the Fetch Host Adapter Local RAM command to read the AutoSCSI data
+ from "W" and "C" series MultiMaster Host Adapters.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '4') {
+ FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_AutoSCSI_BaseOffset;
+ FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIData);
+ if (BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &AutoSCSIData, sizeof(AutoSCSIData))
+ != sizeof(AutoSCSIData))
+ return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM");
+ /*
+ Save the Parity Checking Enabled, Bus Reset Enabled, and Termination
+ Information in the Host Adapter structure.
+ */
+ HostAdapter->ParityCheckingEnabled = AutoSCSIData.ParityCheckingEnabled;
+ HostAdapter->BusResetEnabled = AutoSCSIData.BusResetEnabled;
+ if (HostAdapter->FirmwareVersion[0] == '4') {
+ HostAdapter->TerminationInfoValid = true;
+ HostAdapter->LowByteTerminated = AutoSCSIData.LowByteTerminated;
+ HostAdapter->HighByteTerminated = AutoSCSIData.HighByteTerminated;
+ }
+ /*
+ Save the Wide Permitted, Fast Permitted, Synchronous Permitted,
+ Disconnect Permitted, Ultra Permitted, and SCAM Information in the
+ Host Adapter structure.
+ */
+ HostAdapter->WidePermitted = AutoSCSIData.WidePermitted;
+ HostAdapter->FastPermitted = AutoSCSIData.FastPermitted;
+ HostAdapter->SynchronousPermitted = AutoSCSIData.SynchronousPermitted;
+ HostAdapter->DisconnectPermitted = AutoSCSIData.DisconnectPermitted;
+ if (HostAdapter->HostUltraSCSI)
+ HostAdapter->UltraPermitted = AutoSCSIData.UltraPermitted;
+ if (HostAdapter->HostSupportsSCAM) {
+ HostAdapter->SCAM_Enabled = AutoSCSIData.SCAM_Enabled;
+ HostAdapter->SCAM_Level2 = AutoSCSIData.SCAM_Level2;
+ }
+ }
+ /*
+ Initialize fields in the Host Adapter structure for "S" and "A" series
+ MultiMaster Host Adapters.
+ */
+ if (HostAdapter->FirmwareVersion[0] < '4') {
+ if (SetupInformation.SynchronousInitiationEnabled) {
+ HostAdapter->SynchronousPermitted = 0xFF;
+ if (HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus) {
+ if (ExtendedSetupInformation.Misc.FastOnEISA)
+ HostAdapter->FastPermitted = 0xFF;
+ if (strcmp(HostAdapter->ModelName, "BT-757") == 0)
+ HostAdapter->WidePermitted = 0xFF;
+ }
+ }
+ HostAdapter->DisconnectPermitted = 0xFF;
+ HostAdapter->ParityCheckingEnabled = SetupInformation.ParityCheckingEnabled;
+ HostAdapter->BusResetEnabled = true;
+ }
+ /*
+ Determine the maximum number of Target IDs and Logical Units supported by
+ this driver for Wide and Narrow Host Adapters.
+ */
+ HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
+ HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8);
+ /*
+ Select appropriate values for the Mailbox Count, Driver Queue Depth,
+ Initial CCBs, and Incremental CCBs variables based on whether or not Strict
+ Round Robin Mode is supported. If Strict Round Robin Mode is supported,
+ then there is no performance degradation in using the maximum possible
+ number of Outgoing and Incoming Mailboxes and allowing the Tagged and
+ Untagged Queue Depths to determine the actual utilization. If Strict Round
+ Robin Mode is not supported, then the Host Adapter must scan all the
+ Outgoing Mailboxes whenever an Outgoing Mailbox entry is made, which can
+ cause a substantial performance penalty. The host adapters actually have
+ room to store the following number of CCBs internally; that is, they can
+ internally queue and manage this many active commands on the SCSI bus
+ simultaneously. Performance measurements demonstrate that the Driver Queue
+ Depth should be set to the Mailbox Count, rather than the Host Adapter
+ Queue Depth (internal CCB capacity), as it is more efficient to have the
+ queued commands waiting in Outgoing Mailboxes if necessary than to block
+ the process in the higher levels of the SCSI Subsystem.
+
+ 192 BT-948/958/958D
+ 100 BT-946C/956C/956CD/747C/757C/757CD/445C
+ 50 BT-545C/540CF
+ 30 BT-747S/747D/757S/757D/445S/545S/542D/542B/742A
+ */
+ if (HostAdapter->FirmwareVersion[0] == '5')
+ HostAdapter->HostAdapterQueueDepth = 192;
+ else if (HostAdapter->FirmwareVersion[0] == '4')
+ HostAdapter->HostAdapterQueueDepth = (HostAdapter->HostAdapterBusType != BusLogic_ISA_Bus ? 100 : 50);
+ else
+ HostAdapter->HostAdapterQueueDepth = 30;
+ if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0) {
+ HostAdapter->StrictRoundRobinModeSupport = true;
+ HostAdapter->MailboxCount = BusLogic_MaxMailboxes;
+ } else {
+ HostAdapter->StrictRoundRobinModeSupport = false;
+ HostAdapter->MailboxCount = 32;
+ }
+ HostAdapter->DriverQueueDepth = HostAdapter->MailboxCount;
+ HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize;
+ HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize;
+ /*
+ Tagged Queuing support is available and operates properly on all "W" series
+ MultiMaster Host Adapters, on "C" series MultiMaster Host Adapters with
+ firmware version 4.22 and above, and on "S" series MultiMaster Host
+ Adapters with firmware version 3.35 and above.
+ */
+ HostAdapter->TaggedQueuingPermitted = 0;
+ switch (HostAdapter->FirmwareVersion[0]) {
+ case '5':
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ break;
+ case '4':
+ if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ break;
+ case '3':
+ if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ break;
+ }
+ /*
+ Determine the Host Adapter BIOS Address if the BIOS is enabled and
+ save it in the Host Adapter structure. The BIOS is disabled if the
+ BIOS_Address is 0.
+ */
+ HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+ /*
+ ISA Host Adapters require Bounce Buffers if there is more than 16MB memory.
+ */
+ if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus && (void *) high_memory > (void *) MAX_DMA_ADDRESS)
+ HostAdapter->BounceBuffersRequired = true;
+ /*
+ BusLogic BT-445S Host Adapters prior to board revision E have a hardware
+ bug whereby when the BIOS is enabled, transfers to/from the same address
+ range the BIOS occupies modulo 16MB are handled incorrectly. Only properly
+ functioning BT-445S Host Adapters have firmware version 3.37, so require
+ that ISA Bounce Buffers be used for the buggy BT-445S models if there is
+ more than 16MB memory.
+ */
+ if (HostAdapter->BIOS_Address > 0 && strcmp(HostAdapter->ModelName, "BT-445S") == 0 && strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 && (void *) high_memory > (void *) MAX_DMA_ADDRESS)
+ HostAdapter->BounceBuffersRequired = true;
+ /*
+ Initialize parameters common to MultiMaster and FlashPoint Host Adapters.
+ */
+ Common:
+ /*
+ Initialize the Host Adapter Full Model Name from the Model Name.
+ */
+ strcpy(HostAdapter->FullModelName, "BusLogic ");
+ strcat(HostAdapter->FullModelName, HostAdapter->ModelName);
+ /*
+ Select an appropriate value for the Tagged Queue Depth either from a
+ BusLogic Driver Options specification, or based on whether this Host
+ Adapter requires that ISA Bounce Buffers be used. The Tagged Queue Depth
+ is left at 0 for automatic determination in BusLogic_SelectQueueDepths.
+ Initialize the Untagged Queue Depth.
+ */
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) {
+ unsigned char QueueDepth = 0;
+ if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->QueueDepth[TargetID] > 0)
+ QueueDepth = HostAdapter->DriverOptions->QueueDepth[TargetID];
+ else if (HostAdapter->BounceBuffersRequired)
+ QueueDepth = BusLogic_TaggedQueueDepthBB;
+ HostAdapter->QueueDepth[TargetID] = QueueDepth;
+ }
+ if (HostAdapter->BounceBuffersRequired)
+ HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepthBB;
+ else
+ HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth;
+ if (HostAdapter->DriverOptions != NULL)
+ HostAdapter->CommonQueueDepth = HostAdapter->DriverOptions->CommonQueueDepth;
+ if (HostAdapter->CommonQueueDepth > 0 && HostAdapter->CommonQueueDepth < HostAdapter->UntaggedQueueDepth)
+ HostAdapter->UntaggedQueueDepth = HostAdapter->CommonQueueDepth;
+ /*
+ Tagged Queuing is only allowed if Disconnect/Reconnect is permitted.
+ Therefore, mask the Tagged Queuing Permitted Default bits with the
+ Disconnect/Reconnect Permitted bits.
+ */
+ HostAdapter->TaggedQueuingPermitted &= HostAdapter->DisconnectPermitted;
+ /*
+ Combine the default Tagged Queuing Permitted bits with any BusLogic Driver
+ Options Tagged Queuing specification.
+ */
+ if (HostAdapter->DriverOptions != NULL)
+ HostAdapter->TaggedQueuingPermitted =
+ (HostAdapter->DriverOptions->TaggedQueuingPermitted & HostAdapter->DriverOptions->TaggedQueuingPermittedMask) | (HostAdapter->TaggedQueuingPermitted & ~HostAdapter->DriverOptions->TaggedQueuingPermittedMask);
+
+ /*
+ Select an appropriate value for Bus Settle Time either from a BusLogic
+ Driver Options specification, or from BusLogic_DefaultBusSettleTime.
+ */
+ if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->BusSettleTime > 0)
+ HostAdapter->BusSettleTime = HostAdapter->DriverOptions->BusSettleTime;
+ else
+ HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
+ /*
+ Indicate reading the Host Adapter Configuration completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_ReportHostAdapterConfiguration reports the configuration of
+ Host Adapter.
+*/
+
+static boolean __init BusLogic_ReportHostAdapterConfiguration(struct BusLogic_HostAdapter
+ *HostAdapter)
+{
+ unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
+ unsigned short SynchronousPermitted, FastPermitted;
+ unsigned short UltraPermitted, WidePermitted;
+ unsigned short DisconnectPermitted, TaggedQueuingPermitted;
+ boolean CommonSynchronousNegotiation, CommonTaggedQueueDepth;
+ char SynchronousString[BusLogic_MaxTargetDevices + 1];
+ char WideString[BusLogic_MaxTargetDevices + 1];
+ char DisconnectString[BusLogic_MaxTargetDevices + 1];
+ char TaggedQueuingString[BusLogic_MaxTargetDevices + 1];
+ char *SynchronousMessage = SynchronousString;
+ char *WideMessage = WideString;
+ char *DisconnectMessage = DisconnectString;
+ char *TaggedQueuingMessage = TaggedQueuingString;
+ int TargetID;
+ BusLogic_Info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n",
+ HostAdapter, HostAdapter->ModelName,
+ BusLogic_HostAdapterBusNames[HostAdapter->HostAdapterBusType], (HostAdapter->HostWideSCSI ? " Wide" : ""), (HostAdapter->HostDifferentialSCSI ? " Differential" : ""), (HostAdapter->HostUltraSCSI ? " Ultra" : ""));
+ BusLogic_Info(" Firmware Version: %s, I/O Address: 0x%X, " "IRQ Channel: %d/%s\n", HostAdapter, HostAdapter->FirmwareVersion, HostAdapter->IO_Address, HostAdapter->IRQ_Channel, (HostAdapter->LevelSensitiveInterrupt ? "Level" : "Edge"));
+ if (HostAdapter->HostAdapterBusType != BusLogic_PCI_Bus) {
+ BusLogic_Info(" DMA Channel: ", HostAdapter);
+ if (HostAdapter->DMA_Channel > 0)
+ BusLogic_Info("%d, ", HostAdapter, HostAdapter->DMA_Channel);
+ else
+ BusLogic_Info("None, ", HostAdapter);
+ if (HostAdapter->BIOS_Address > 0)
+ BusLogic_Info("BIOS Address: 0x%X, ", HostAdapter, HostAdapter->BIOS_Address);
+ else
+ BusLogic_Info("BIOS Address: None, ", HostAdapter);
+ } else {
+ BusLogic_Info(" PCI Bus: %d, Device: %d, Address: ", HostAdapter, HostAdapter->Bus, HostAdapter->Device);
+ if (HostAdapter->PCI_Address > 0)
+ BusLogic_Info("0x%X, ", HostAdapter, HostAdapter->PCI_Address);
+ else
+ BusLogic_Info("Unassigned, ", HostAdapter);
+ }
+ BusLogic_Info("Host Adapter SCSI ID: %d\n", HostAdapter, HostAdapter->SCSI_ID);
+ BusLogic_Info(" Parity Checking: %s, Extended Translation: %s\n", HostAdapter, (HostAdapter->ParityCheckingEnabled ? "Enabled" : "Disabled"), (HostAdapter->ExtendedTranslationEnabled ? "Enabled" : "Disabled"));
+ AllTargetsMask &= ~(1 << HostAdapter->SCSI_ID);
+ SynchronousPermitted = HostAdapter->SynchronousPermitted & AllTargetsMask;
+ FastPermitted = HostAdapter->FastPermitted & AllTargetsMask;
+ UltraPermitted = HostAdapter->UltraPermitted & AllTargetsMask;
+ if ((BusLogic_MultiMasterHostAdapterP(HostAdapter) && (HostAdapter->FirmwareVersion[0] >= '4' || HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)) || BusLogic_FlashPointHostAdapterP(HostAdapter)) {
+ CommonSynchronousNegotiation = false;
+ if (SynchronousPermitted == 0) {
+ SynchronousMessage = "Disabled";
+ CommonSynchronousNegotiation = true;
+ } else if (SynchronousPermitted == AllTargetsMask) {
+ if (FastPermitted == 0) {
+ SynchronousMessage = "Slow";
+ CommonSynchronousNegotiation = true;
+ } else if (FastPermitted == AllTargetsMask) {
+ if (UltraPermitted == 0) {
+ SynchronousMessage = "Fast";
+ CommonSynchronousNegotiation = true;
+ } else if (UltraPermitted == AllTargetsMask) {
+ SynchronousMessage = "Ultra";
+ CommonSynchronousNegotiation = true;
+ }
+ }
+ }
+ if (!CommonSynchronousNegotiation) {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ SynchronousString[TargetID] = ((!(SynchronousPermitted & (1 << TargetID))) ? 'N' : (!(FastPermitted & (1 << TargetID)) ? 'S' : (!(UltraPermitted & (1 << TargetID)) ? 'F' : 'U')));
+ SynchronousString[HostAdapter->SCSI_ID] = '#';
+ SynchronousString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ } else
+ SynchronousMessage = (SynchronousPermitted == 0 ? "Disabled" : "Enabled");
+ WidePermitted = HostAdapter->WidePermitted & AllTargetsMask;
+ if (WidePermitted == 0)
+ WideMessage = "Disabled";
+ else if (WidePermitted == AllTargetsMask)
+ WideMessage = "Enabled";
+ else {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ WideString[TargetID] = ((WidePermitted & (1 << TargetID)) ? 'Y' : 'N');
+ WideString[HostAdapter->SCSI_ID] = '#';
+ WideString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
+ if (DisconnectPermitted == 0)
+ DisconnectMessage = "Disabled";
+ else if (DisconnectPermitted == AllTargetsMask)
+ DisconnectMessage = "Enabled";
+ else {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ DisconnectString[TargetID] = ((DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ DisconnectString[HostAdapter->SCSI_ID] = '#';
+ DisconnectString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ TaggedQueuingPermitted = HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
+ if (TaggedQueuingPermitted == 0)
+ TaggedQueuingMessage = "Disabled";
+ else if (TaggedQueuingPermitted == AllTargetsMask)
+ TaggedQueuingMessage = "Enabled";
+ else {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ TaggedQueuingString[TargetID] = ((TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ TaggedQueuingString[HostAdapter->SCSI_ID] = '#';
+ TaggedQueuingString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ BusLogic_Info(" Synchronous Negotiation: %s, Wide Negotiation: %s\n", HostAdapter, SynchronousMessage, WideMessage);
+ BusLogic_Info(" Disconnect/Reconnect: %s, Tagged Queuing: %s\n", HostAdapter, DisconnectMessage, TaggedQueuingMessage);
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
+ BusLogic_Info(" Scatter/Gather Limit: %d of %d segments, " "Mailboxes: %d\n", HostAdapter, HostAdapter->DriverScatterGatherLimit, HostAdapter->HostAdapterScatterGatherLimit, HostAdapter->MailboxCount);
+ BusLogic_Info(" Driver Queue Depth: %d, " "Host Adapter Queue Depth: %d\n", HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->HostAdapterQueueDepth);
+ } else
+ BusLogic_Info(" Driver Queue Depth: %d, " "Scatter/Gather Limit: %d segments\n", HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->DriverScatterGatherLimit);
+ BusLogic_Info(" Tagged Queue Depth: ", HostAdapter);
+ CommonTaggedQueueDepth = true;
+ for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (HostAdapter->QueueDepth[TargetID] != HostAdapter->QueueDepth[0]) {
+ CommonTaggedQueueDepth = false;
+ break;
+ }
+ if (CommonTaggedQueueDepth) {
+ if (HostAdapter->QueueDepth[0] > 0)
+ BusLogic_Info("%d", HostAdapter, HostAdapter->QueueDepth[0]);
+ else
+ BusLogic_Info("Automatic", HostAdapter);
+ } else
+ BusLogic_Info("Individual", HostAdapter);
+ BusLogic_Info(", Untagged Queue Depth: %d\n", HostAdapter, HostAdapter->UntaggedQueueDepth);
+ if (HostAdapter->TerminationInfoValid) {
+ if (HostAdapter->HostWideSCSI)
+ BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter, (HostAdapter->LowByteTerminated ? (HostAdapter->HighByteTerminated ? "Both Enabled" : "Low Enabled")
+ : (HostAdapter->HighByteTerminated ? "High Enabled" : "Both Disabled")));
+ else
+ BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter, (HostAdapter->LowByteTerminated ? "Enabled" : "Disabled"));
+ if (HostAdapter->HostSupportsSCAM)
+ BusLogic_Info(", SCAM: %s", HostAdapter, (HostAdapter->SCAM_Enabled ? (HostAdapter->SCAM_Level2 ? "Enabled, Level 2" : "Enabled, Level 1")
+ : "Disabled"));
+ BusLogic_Info("\n", HostAdapter);
+ }
+ /*
+ Indicate reporting the Host Adapter configuration completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_AcquireResources acquires the system resources necessary to use
+ Host Adapter.
+*/
+
+static boolean __init BusLogic_AcquireResources(struct BusLogic_HostAdapter *HostAdapter)
+{
+ if (HostAdapter->IRQ_Channel == 0) {
+ BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n", HostAdapter);
+ return false;
+ }
+ /*
+ Acquire shared access to the IRQ Channel.
+ */
+ if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, SA_SHIRQ, HostAdapter->FullModelName, HostAdapter) < 0) {
+ BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", HostAdapter, HostAdapter->IRQ_Channel);
+ return false;
+ }
+ HostAdapter->IRQ_ChannelAcquired = true;
+ /*
+ Acquire exclusive access to the DMA Channel.
+ */
+ if (HostAdapter->DMA_Channel > 0) {
+ if (request_dma(HostAdapter->DMA_Channel, HostAdapter->FullModelName) < 0) {
+ BusLogic_Error("UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n", HostAdapter, HostAdapter->DMA_Channel);
+ return false;
+ }
+ set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
+ enable_dma(HostAdapter->DMA_Channel);
+ HostAdapter->DMA_ChannelAcquired = true;
+ }
+ /*
+ Indicate the System Resource Acquisition completed successfully,
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_ReleaseResources releases any system resources previously acquired
+ by BusLogic_AcquireResources.
+*/
+
+static void BusLogic_ReleaseResources(struct BusLogic_HostAdapter *HostAdapter)
+{
+ /*
+ Release shared access to the IRQ Channel.
+ */
+ if (HostAdapter->IRQ_ChannelAcquired)
+ free_irq(HostAdapter->IRQ_Channel, HostAdapter);
+ /*
+ Release exclusive access to the DMA Channel.
+ */
+ if (HostAdapter->DMA_ChannelAcquired)
+ free_dma(HostAdapter->DMA_Channel);
+ /*
+ Release any allocated memory structs not released elsewhere
+ */
+ if (HostAdapter->MailboxSpace)
+ pci_free_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, HostAdapter->MailboxSpace, HostAdapter->MailboxSpaceHandle);
+ HostAdapter->MailboxSpace = NULL;
+ HostAdapter->MailboxSpaceHandle = 0;
+ HostAdapter->MailboxSize = 0;
+}
+
+
+/*
+ BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only
+ function called during SCSI Host Adapter detection which modifies the state
+ of the Host Adapter from its initial power on or hard reset state.
+*/
+
+static boolean BusLogic_InitializeHostAdapter(struct BusLogic_HostAdapter
+ *HostAdapter)
+{
+ struct BusLogic_ExtendedMailboxRequest ExtendedMailboxRequest;
+ enum BusLogic_RoundRobinModeRequest RoundRobinModeRequest;
+ enum BusLogic_SetCCBFormatRequest SetCCBFormatRequest;
+ int TargetID;
+ /*
+ Initialize the pointers to the first and last CCBs that are queued for
+ completion processing.
+ */
+ HostAdapter->FirstCompletedCCB = NULL;
+ HostAdapter->LastCompletedCCB = NULL;
+ /*
+ Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active,
+ Command Successful Flag, Active Commands, and Commands Since Reset
+ for each Target Device.
+ */
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL;
+ HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
+ HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false;
+ HostAdapter->ActiveCommands[TargetID] = 0;
+ HostAdapter->CommandsSinceReset[TargetID] = 0;
+ }
+ /*
+ FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ goto Done;
+ /*
+ Initialize the Outgoing and Incoming Mailbox pointers.
+ */
+ HostAdapter->MailboxSize = HostAdapter->MailboxCount * (sizeof(struct BusLogic_OutgoingMailbox) + sizeof(struct BusLogic_IncomingMailbox));
+ HostAdapter->MailboxSpace = pci_alloc_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, &HostAdapter->MailboxSpaceHandle);
+ if (HostAdapter->MailboxSpace == NULL)
+ return BusLogic_Failure(HostAdapter, "MAILBOX ALLOCATION");
+ HostAdapter->FirstOutgoingMailbox = (struct BusLogic_OutgoingMailbox *) HostAdapter->MailboxSpace;
+ HostAdapter->LastOutgoingMailbox = HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1;
+ HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->FirstIncomingMailbox = (struct BusLogic_IncomingMailbox *) (HostAdapter->LastOutgoingMailbox + 1);
+ HostAdapter->LastIncomingMailbox = HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1;
+ HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+
+ /*
+ Initialize the Outgoing and Incoming Mailbox structures.
+ */
+ memset(HostAdapter->FirstOutgoingMailbox, 0, HostAdapter->MailboxCount * sizeof(struct BusLogic_OutgoingMailbox));
+ memset(HostAdapter->FirstIncomingMailbox, 0, HostAdapter->MailboxCount * sizeof(struct BusLogic_IncomingMailbox));
+ /*
+ Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+ */
+ ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount;
+ ExtendedMailboxRequest.BaseMailboxAddress = (u32) HostAdapter->MailboxSpaceHandle;
+ if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox, &ExtendedMailboxRequest, sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION");
+ /*
+ Enable Strict Round Robin Mode if supported by the Host Adapter. In
+ Strict Round Robin Mode, the Host Adapter only looks at the next Outgoing
+ Mailbox for each new command, rather than scanning through all the
+ Outgoing Mailboxes to find any that have new commands in them. Strict
+ Round Robin Mode is significantly more efficient.
+ */
+ if (HostAdapter->StrictRoundRobinModeSupport) {
+ RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+ if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode, &RoundRobinModeRequest, sizeof(RoundRobinModeRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE");
+ }
+ /*
+ For Host Adapters that support Extended LUN Format CCBs, issue the Set CCB
+ Format command to allow 32 Logical Units per Target Device.
+ */
+ if (HostAdapter->ExtendedLUNSupport) {
+ SetCCBFormatRequest = BusLogic_ExtendedLUNFormatCCB;
+ if (BusLogic_Command(HostAdapter, BusLogic_SetCCBFormat, &SetCCBFormatRequest, sizeof(SetCCBFormatRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "SET CCB FORMAT");
+ }
+ /*
+ Announce Successful Initialization.
+ */
+ Done:
+ if (!HostAdapter->HostAdapterInitialized) {
+ BusLogic_Info("*** %s Initialized Successfully ***\n", HostAdapter, HostAdapter->FullModelName);
+ BusLogic_Info("\n", HostAdapter);
+ } else
+ BusLogic_Warning("*** %s Initialized Successfully ***\n", HostAdapter, HostAdapter->FullModelName);
+ HostAdapter->HostAdapterInitialized = true;
+ /*
+ Indicate the Host Adapter Initialization completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_TargetDeviceInquiry inquires about the Target Devices accessible
+ through Host Adapter.
+*/
+
+static boolean __init BusLogic_TargetDeviceInquiry(struct BusLogic_HostAdapter
+ *HostAdapter)
+{
+ u16 InstalledDevices;
+ u8 InstalledDevicesID0to7[8];
+ struct BusLogic_SetupInformation SetupInformation;
+ u8 SynchronousPeriod[BusLogic_MaxTargetDevices];
+ unsigned char RequestedReplyLength;
+ int TargetID;
+ /*
+ Wait a few seconds between the Host Adapter Hard Reset which initiates
+ a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get
+ confused if they receive SCSI Commands too soon after a SCSI Bus Reset.
+ */
+ BusLogic_Delay(HostAdapter->BusSettleTime);
+ /*
+ FlashPoint Host Adapters do not provide for Target Device Inquiry.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ return true;
+ /*
+ Inhibit the Target Device Inquiry if requested.
+ */
+ if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry)
+ return true;
+ /*
+ Issue the Inquire Target Devices command for host adapters with firmware
+ version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command
+ for older host adapters. This is necessary to force Synchronous Transfer
+ Negotiation so that the Inquire Setup Information and Inquire Synchronous
+ Period commands will return valid data. The Inquire Target Devices command
+ is preferable to Inquire Installed Devices ID 0 to 7 since it only probes
+ Logical Unit 0 of each Target Device.
+ */
+ if (strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0) {
+
+ /*
+ * Issue a Inquire Target Devices command. Inquire Target Devices only
+ * tests Logical Unit 0 of each Target Device unlike the Inquire Installed
+ * Devices commands which test Logical Units 0 - 7. Two bytes are
+ * returned, where byte 0 bit 0 set indicates that Target Device 0 exists,
+ * and so on.
+ */
+
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireTargetDevices, NULL, 0, &InstalledDevices, sizeof(InstalledDevices))
+ != sizeof(InstalledDevices))
+ return BusLogic_Failure(HostAdapter, "INQUIRE TARGET DEVICES");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->TargetFlags[TargetID].TargetExists = (InstalledDevices & (1 << TargetID) ? true : false);
+ } else {
+
+ /*
+ * Issue an Inquire Installed Devices command. For each Target Device,
+ * a byte is returned where bit 0 set indicates that Logical Unit 0
+ * exists, bit 1 set indicates that Logical Unit 1 exists, and so on.
+ */
+
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7, NULL, 0, &InstalledDevicesID0to7, sizeof(InstalledDevicesID0to7))
+ != sizeof(InstalledDevicesID0to7))
+ return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+ for (TargetID = 0; TargetID < 8; TargetID++)
+ HostAdapter->TargetFlags[TargetID].TargetExists = (InstalledDevicesID0to7[TargetID] != 0 ? true : false);
+ }
+ /*
+ Issue the Inquire Setup Information command.
+ */
+ RequestedReplyLength = sizeof(SetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &SetupInformation, sizeof(SetupInformation))
+ != sizeof(SetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->SynchronousOffset[TargetID] = (TargetID < 8 ? SetupInformation.SynchronousValuesID0to7[TargetID].Offset : SetupInformation.SynchronousValuesID8to15[TargetID - 8].Offset);
+ if (strcmp(HostAdapter->FirmwareVersion, "5.06L") >= 0)
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->TargetFlags[TargetID].WideTransfersActive = (TargetID < 8 ? (SetupInformation.WideTransfersActiveID0to7 & (1 << TargetID)
+ ? true : false)
+ : (SetupInformation.WideTransfersActiveID8to15 & (1 << (TargetID - 8))
+ ? true : false));
+ /*
+ Issue the Inquire Synchronous Period command.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '3') {
+
+ /* Issue a Inquire Synchronous Period command. For each Target Device,
+ * a byte is returned which represents the Synchronous Transfer Period
+ * in units of 10 nanoseconds.
+ */
+
+ RequestedReplyLength = sizeof(SynchronousPeriod);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod, &RequestedReplyLength, sizeof(RequestedReplyLength), &SynchronousPeriod, sizeof(SynchronousPeriod))
+ != sizeof(SynchronousPeriod))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->SynchronousPeriod[TargetID] = SynchronousPeriod[TargetID];
+ } else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0)
+ HostAdapter->SynchronousPeriod[TargetID] = 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID]
+ .TransferPeriod;
+ /*
+ Indicate the Target Device Inquiry completed successfully.
+ */
+ return true;
+}
+
+/*
+ BusLogic_InitializeHostStructure initializes the fields in the SCSI Host
+ structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the
+ SCSI Host structure are intentionally left uninitialized, as this driver
+ handles acquisition and release of these resources explicitly, as well as
+ ensuring exclusive access to the Host Adapter hardware and data structures
+ through explicit acquisition and release of the Host Adapter's Lock.
+*/
+
+static void __init BusLogic_InitializeHostStructure(struct BusLogic_HostAdapter
+ *HostAdapter, struct Scsi_Host *Host)
+{
+ Host->max_id = HostAdapter->MaxTargetDevices;
+ Host->max_lun = HostAdapter->MaxLogicalUnits;
+ Host->max_channel = 0;
+ Host->unique_id = HostAdapter->IO_Address;
+ Host->this_id = HostAdapter->SCSI_ID;
+ Host->can_queue = HostAdapter->DriverQueueDepth;
+ Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
+ Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired;
+ Host->cmd_per_lun = HostAdapter->UntaggedQueueDepth;
+}
+
+/*
+ BusLogic_SlaveConfigure will actually set the queue depth on individual
+ scsi devices as they are permanently added to the device chain. We
+ shamelessly rip off the SelectQueueDepths code to make this work mostly
+ like it used to. Since we don't get called once at the end of the scan
+ but instead get called for each device, we have to do things a bit
+ differently.
+*/
+static int BusLogic_SlaveConfigure(struct scsi_device *Device)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Device->host->hostdata;
+ int TargetID = Device->id;
+ int QueueDepth = HostAdapter->QueueDepth[TargetID];
+
+ if (HostAdapter->TargetFlags[TargetID].TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) {
+ if (QueueDepth == 0)
+ QueueDepth = BusLogic_MaxAutomaticTaggedQueueDepth;
+ HostAdapter->QueueDepth[TargetID] = QueueDepth;
+ scsi_adjust_queue_depth(Device, MSG_SIMPLE_TAG, QueueDepth);
+ } else {
+ HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
+ QueueDepth = HostAdapter->UntaggedQueueDepth;
+ HostAdapter->QueueDepth[TargetID] = QueueDepth;
+ scsi_adjust_queue_depth(Device, 0, QueueDepth);
+ }
+ QueueDepth = 0;
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (HostAdapter->TargetFlags[TargetID].TargetExists) {
+ QueueDepth += HostAdapter->QueueDepth[TargetID];
+ }
+ if (QueueDepth > HostAdapter->AllocatedCCBs)
+ BusLogic_CreateAdditionalCCBs(HostAdapter, QueueDepth - HostAdapter->AllocatedCCBs, false);
+ return 0;
+}
+
+/*
+ BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard
+ I/O Addresses where they may be located, initializing, registering, and
+ reporting the configuration of each BusLogic Host Adapter it finds. It
+ returns the number of BusLogic Host Adapters successfully initialized and
+ registered.
+*/
+
+static int __init BusLogic_init(void)
+{
+ int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex;
+ struct BusLogic_HostAdapter *PrototypeHostAdapter;
+
+#ifdef MODULE
+ if (BusLogic)
+ BusLogic_Setup(BusLogic);
+#endif
+
+ if (BusLogic_ProbeOptions.NoProbe)
+ return -ENODEV;
+ BusLogic_ProbeInfoList = (struct BusLogic_ProbeInfo *)
+ kmalloc(BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo), GFP_ATOMIC);
+ if (BusLogic_ProbeInfoList == NULL) {
+ BusLogic_Error("BusLogic: Unable to allocate Probe Info List\n", NULL);
+ return -ENOMEM;
+ }
+ memset(BusLogic_ProbeInfoList, 0, BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo));
+ PrototypeHostAdapter = (struct BusLogic_HostAdapter *)
+ kmalloc(sizeof(struct BusLogic_HostAdapter), GFP_ATOMIC);
+ if (PrototypeHostAdapter == NULL) {
+ kfree(BusLogic_ProbeInfoList);
+ BusLogic_Error("BusLogic: Unable to allocate Prototype " "Host Adapter\n", NULL);
+ return -ENOMEM;
+ }
+ memset(PrototypeHostAdapter, 0, sizeof(struct BusLogic_HostAdapter));
+#ifdef MODULE
+ if (BusLogic != NULL)
+ BusLogic_Setup(BusLogic);
+#endif
+ BusLogic_InitializeProbeInfoList(PrototypeHostAdapter);
+ for (ProbeIndex = 0; ProbeIndex < BusLogic_ProbeInfoCount; ProbeIndex++) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[ProbeIndex];
+ struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
+ struct Scsi_Host *Host;
+ if (ProbeInfo->IO_Address == 0)
+ continue;
+ memset(HostAdapter, 0, sizeof(struct BusLogic_HostAdapter));
+ HostAdapter->HostAdapterType = ProbeInfo->HostAdapterType;
+ HostAdapter->HostAdapterBusType = ProbeInfo->HostAdapterBusType;
+ HostAdapter->IO_Address = ProbeInfo->IO_Address;
+ HostAdapter->PCI_Address = ProbeInfo->PCI_Address;
+ HostAdapter->Bus = ProbeInfo->Bus;
+ HostAdapter->Device = ProbeInfo->Device;
+ HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel;
+ HostAdapter->AddressCount = BusLogic_HostAdapterAddressCount[HostAdapter->HostAdapterType];
+ /*
+ Probe the Host Adapter. If unsuccessful, abort further initialization.
+ */
+ if (!BusLogic_ProbeHostAdapter(HostAdapter))
+ continue;
+ /*
+ Hard Reset the Host Adapter. If unsuccessful, abort further
+ initialization.
+ */
+ if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true))
+ continue;
+ /*
+ Check the Host Adapter. If unsuccessful, abort further initialization.
+ */
+ if (!BusLogic_CheckHostAdapter(HostAdapter))
+ continue;
+ /*
+ Initialize the Driver Options field if provided.
+ */
+ if (DriverOptionsIndex < BusLogic_DriverOptionsCount)
+ HostAdapter->DriverOptions = &BusLogic_DriverOptions[DriverOptionsIndex++];
+ /*
+ Announce the Driver Version and Date, Author's Name, Copyright Notice,
+ and Electronic Mail Address.
+ */
+ BusLogic_AnnounceDriver(HostAdapter);
+ /*
+ Register usage of the I/O Address range. From this point onward, any
+ failure will be assumed to be due to a problem with the Host Adapter,
+ rather than due to having mistakenly identified this port as belonging
+ to a BusLogic Host Adapter. The I/O Address range will not be
+ released, thereby preventing it from being incorrectly identified as
+ any other type of Host Adapter.
+ */
+ if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, "BusLogic"))
+ continue;
+ /*
+ Register the SCSI Host structure.
+ */
+
+ Host = scsi_host_alloc(&Bus_Logic_template, sizeof(struct BusLogic_HostAdapter));
+ if (Host == NULL) {
+ release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ continue;
+ }
+ HostAdapter = (struct BusLogic_HostAdapter *) Host->hostdata;
+ memcpy(HostAdapter, PrototypeHostAdapter, sizeof(struct BusLogic_HostAdapter));
+ HostAdapter->SCSI_Host = Host;
+ HostAdapter->HostNumber = Host->host_no;
+ /*
+ Add Host Adapter to the end of the list of registered BusLogic
+ Host Adapters.
+ */
+ list_add_tail(&HostAdapter->host_list, &BusLogic_host_list);
+
+ /*
+ Read the Host Adapter Configuration, Configure the Host Adapter,
+ Acquire the System Resources necessary to use the Host Adapter, then
+ Create the Initial CCBs, Initialize the Host Adapter, and finally
+ perform Target Device Inquiry.
+ */
+ if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+ BusLogic_ReportHostAdapterConfiguration(HostAdapter) && BusLogic_AcquireResources(HostAdapter) && BusLogic_CreateInitialCCBs(HostAdapter) && BusLogic_InitializeHostAdapter(HostAdapter) && BusLogic_TargetDeviceInquiry(HostAdapter)) {
+ /*
+ Initialization has been completed successfully. Release and
+ re-register usage of the I/O Address range so that the Model
+ Name of the Host Adapter will appear, and initialize the SCSI
+ Host structure.
+ */
+ release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, HostAdapter->FullModelName)) {
+ printk(KERN_WARNING "BusLogic: Release and re-register of " "port 0x%04lx failed \n", (unsigned long) HostAdapter->IO_Address);
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ list_del(&HostAdapter->host_list);
+ scsi_host_put(Host);
+ } else {
+ BusLogic_InitializeHostStructure(HostAdapter, Host);
+ scsi_add_host(Host, NULL);
+ scsi_scan_host(Host);
+ BusLogicHostAdapterCount++;
+ }
+ } else {
+ /*
+ An error occurred during Host Adapter Configuration Querying, Host
+ Adapter Configuration, Resource Acquisition, CCB Creation, Host
+ Adapter Initialization, or Target Device Inquiry, so remove Host
+ Adapter from the list of registered BusLogic Host Adapters, destroy
+ the CCBs, Release the System Resources, and Unregister the SCSI
+ Host.
+ */
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ list_del(&HostAdapter->host_list);
+ scsi_host_put(Host);
+ }
+ }
+ kfree(PrototypeHostAdapter);
+ kfree(BusLogic_ProbeInfoList);
+ BusLogic_ProbeInfoList = NULL;
+ return 0;
+}
+
+
+/*
+ BusLogic_ReleaseHostAdapter releases all resources previously acquired to
+ support a specific Host Adapter, including the I/O Address range, and
+ unregisters the BusLogic Host Adapter.
+*/
+
+static int __exit BusLogic_ReleaseHostAdapter(struct BusLogic_HostAdapter *HostAdapter)
+{
+ struct Scsi_Host *Host = HostAdapter->SCSI_Host;
+
+ scsi_remove_host(Host);
+
+ /*
+ FlashPoint Host Adapters must first be released by the FlashPoint
+ SCCB Manager.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle);
+ /*
+ Destroy the CCBs and release any system resources acquired to
+ support Host Adapter.
+ */
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ /*
+ Release usage of the I/O Address range.
+ */
+ release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ /*
+ Remove Host Adapter from the list of registered BusLogic Host Adapters.
+ */
+ list_del(&HostAdapter->host_list);
+
+ scsi_host_put(Host);
+ return 0;
+}
+
+
+/*
+ BusLogic_QueueCompletedCCB queues CCB for completion processing.
+*/
+
+static void BusLogic_QueueCompletedCCB(struct BusLogic_CCB *CCB)
+{
+ struct BusLogic_HostAdapter *HostAdapter = CCB->HostAdapter;
+ CCB->Status = BusLogic_CCB_Completed;
+ CCB->Next = NULL;
+ if (HostAdapter->FirstCompletedCCB == NULL) {
+ HostAdapter->FirstCompletedCCB = CCB;
+ HostAdapter->LastCompletedCCB = CCB;
+ } else {
+ HostAdapter->LastCompletedCCB->Next = CCB;
+ HostAdapter->LastCompletedCCB = CCB;
+ }
+ HostAdapter->ActiveCommands[CCB->TargetID]--;
+}
+
+
+/*
+ BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
+ the Host Adapter Status and Target Device Status.
+*/
+
+static int BusLogic_ComputeResultCode(struct BusLogic_HostAdapter *HostAdapter, enum BusLogic_HostAdapterStatus HostAdapterStatus, enum BusLogic_TargetDeviceStatus TargetDeviceStatus)
+{
+ int HostStatus;
+ switch (HostAdapterStatus) {
+ case BusLogic_CommandCompletedNormally:
+ case BusLogic_LinkedCommandCompleted:
+ case BusLogic_LinkedCommandCompletedWithFlag:
+ HostStatus = DID_OK;
+ break;
+ case BusLogic_SCSISelectionTimeout:
+ HostStatus = DID_TIME_OUT;
+ break;
+ case BusLogic_InvalidOutgoingMailboxActionCode:
+ case BusLogic_InvalidCommandOperationCode:
+ case BusLogic_InvalidCommandParameter:
+ BusLogic_Warning("BusLogic Driver Protocol Error 0x%02X\n", HostAdapter, HostAdapterStatus);
+ case BusLogic_DataUnderRun:
+ case BusLogic_DataOverRun:
+ case BusLogic_UnexpectedBusFree:
+ case BusLogic_LinkedCCBhasInvalidLUN:
+ case BusLogic_AutoRequestSenseFailed:
+ case BusLogic_TaggedQueuingMessageRejected:
+ case BusLogic_UnsupportedMessageReceived:
+ case BusLogic_HostAdapterHardwareFailed:
+ case BusLogic_TargetDeviceReconnectedImproperly:
+ case BusLogic_AbortQueueGenerated:
+ case BusLogic_HostAdapterSoftwareError:
+ case BusLogic_HostAdapterHardwareTimeoutError:
+ case BusLogic_SCSIParityErrorDetected:
+ HostStatus = DID_ERROR;
+ break;
+ case BusLogic_InvalidBusPhaseRequested:
+ case BusLogic_TargetFailedResponseToATN:
+ case BusLogic_HostAdapterAssertedRST:
+ case BusLogic_OtherDeviceAssertedRST:
+ case BusLogic_HostAdapterAssertedBusDeviceReset:
+ HostStatus = DID_RESET;
+ break;
+ default:
+ BusLogic_Warning("Unknown Host Adapter Status 0x%02X\n", HostAdapter, HostAdapterStatus);
+ HostStatus = DID_ERROR;
+ break;
+ }
+ return (HostStatus << 16) | TargetDeviceStatus;
+}
+
+
+/*
+ BusLogic_ScanIncomingMailboxes scans the Incoming Mailboxes saving any
+ Incoming Mailbox entries for completion processing.
+*/
+
+static void BusLogic_ScanIncomingMailboxes(struct BusLogic_HostAdapter *HostAdapter)
+{
+ /*
+ Scan through the Incoming Mailboxes in Strict Round Robin fashion, saving
+ any completed CCBs for further processing. It is essential that for each
+ CCB and SCSI Command issued, command completion processing is performed
+ exactly once. Therefore, only Incoming Mailboxes with completion code
+ Command Completed Without Error, Command Completed With Error, or Command
+ Aborted At Host Request are saved for completion processing. When an
+ Incoming Mailbox has a completion code of Aborted Command Not Found, the
+ CCB had already completed or been aborted before the current Abort request
+ was processed, and so completion processing has already occurred and no
+ further action should be taken.
+ */
+ struct BusLogic_IncomingMailbox *NextIncomingMailbox = HostAdapter->NextIncomingMailbox;
+ enum BusLogic_CompletionCode CompletionCode;
+ while ((CompletionCode = NextIncomingMailbox->CompletionCode) != BusLogic_IncomingMailboxFree) {
+ /*
+ We are only allowed to do this because we limit our architectures we
+ run on to machines where bus_to_virt() actually works. There *needs*
+ to be a dma_addr_to_virt() in the new PCI DMA mapping interface to
+ replace bus_to_virt() or else this code is going to become very
+ innefficient.
+ */
+ struct BusLogic_CCB *CCB = (struct BusLogic_CCB *) Bus_to_Virtual(NextIncomingMailbox->CCB);
+ if (CompletionCode != BusLogic_AbortedCommandNotFound) {
+ if (CCB->Status == BusLogic_CCB_Active || CCB->Status == BusLogic_CCB_Reset) {
+ /*
+ Save the Completion Code for this CCB and queue the CCB
+ for completion processing.
+ */
+ CCB->CompletionCode = CompletionCode;
+ BusLogic_QueueCompletedCCB(CCB);
+ } else {
+ /*
+ If a CCB ever appears in an Incoming Mailbox and is not marked
+ as status Active or Reset, then there is most likely a bug in
+ the Host Adapter firmware.
+ */
+ BusLogic_Warning("Illegal CCB #%ld status %d in " "Incoming Mailbox\n", HostAdapter, CCB->SerialNumber, CCB->Status);
+ }
+ }
+ NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
+ if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
+ NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ }
+ HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+}
+
+
+/*
+ BusLogic_ProcessCompletedCCBs iterates over the completed CCBs for Host
+ Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and
+ calling the SCSI Subsystem Completion Routines. The Host Adapter's Lock
+ should already have been acquired by the caller.
+*/
+
+static void BusLogic_ProcessCompletedCCBs(struct BusLogic_HostAdapter *HostAdapter)
+{
+ if (HostAdapter->ProcessCompletedCCBsActive)
+ return;
+ HostAdapter->ProcessCompletedCCBsActive = true;
+ while (HostAdapter->FirstCompletedCCB != NULL) {
+ struct BusLogic_CCB *CCB = HostAdapter->FirstCompletedCCB;
+ struct scsi_cmnd *Command = CCB->Command;
+ HostAdapter->FirstCompletedCCB = CCB->Next;
+ if (HostAdapter->FirstCompletedCCB == NULL)
+ HostAdapter->LastCompletedCCB = NULL;
+ /*
+ Process the Completed CCB.
+ */
+ if (CCB->Opcode == BusLogic_BusDeviceReset) {
+ int TargetID = CCB->TargetID;
+ BusLogic_Warning("Bus Device Reset CCB #%ld to Target " "%d Completed\n", HostAdapter, CCB->SerialNumber, TargetID);
+ BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].BusDeviceResetsCompleted);
+ HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
+ HostAdapter->CommandsSinceReset[TargetID] = 0;
+ HostAdapter->LastResetCompleted[TargetID] = jiffies;
+ /*
+ Place CCB back on the Host Adapter's free list.
+ */
+ BusLogic_DeallocateCCB(CCB);
+#if 0 /* this needs to be redone different for new EH */
+ /*
+ Bus Device Reset CCBs have the Command field non-NULL only when a
+ Bus Device Reset was requested for a Command that did not have a
+ currently active CCB in the Host Adapter (i.e., a Synchronous
+ Bus Device Reset), and hence would not have its Completion Routine
+ called otherwise.
+ */
+ while (Command != NULL) {
+ struct scsi_cmnd *NextCommand = Command->reset_chain;
+ Command->reset_chain = NULL;
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ Command = NextCommand;
+ }
+#endif
+ /*
+ Iterate over the CCBs for this Host Adapter performing completion
+ processing for any CCBs marked as Reset for this Target.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Reset && CCB->TargetID == TargetID) {
+ Command = CCB->Command;
+ BusLogic_DeallocateCCB(CCB);
+ HostAdapter->ActiveCommands[TargetID]--;
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ }
+ HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL;
+ } else {
+ /*
+ Translate the Completion Code, Host Adapter Status, and Target
+ Device Status into a SCSI Subsystem Result Code.
+ */
+ switch (CCB->CompletionCode) {
+ case BusLogic_IncomingMailboxFree:
+ case BusLogic_AbortedCommandNotFound:
+ case BusLogic_InvalidCCB:
+ BusLogic_Warning("CCB #%ld to Target %d Impossible State\n", HostAdapter, CCB->SerialNumber, CCB->TargetID);
+ break;
+ case BusLogic_CommandCompletedWithoutError:
+ HostAdapter->TargetStatistics[CCB->TargetID]
+ .CommandsCompleted++;
+ HostAdapter->TargetFlags[CCB->TargetID]
+ .CommandSuccessfulFlag = true;
+ Command->result = DID_OK << 16;
+ break;
+ case BusLogic_CommandAbortedAtHostRequest:
+ BusLogic_Warning("CCB #%ld to Target %d Aborted\n", HostAdapter, CCB->SerialNumber, CCB->TargetID);
+ BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[CCB->TargetID]
+ .CommandAbortsCompleted);
+ Command->result = DID_ABORT << 16;
+ break;
+ case BusLogic_CommandCompletedWithError:
+ Command->result = BusLogic_ComputeResultCode(HostAdapter, CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
+ if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout) {
+ HostAdapter->TargetStatistics[CCB->TargetID]
+ .CommandsCompleted++;
+ if (BusLogic_GlobalOptions.TraceErrors) {
+ int i;
+ BusLogic_Notice("CCB #%ld Target %d: Result %X Host "
+ "Adapter Status %02X " "Target Status %02X\n", HostAdapter, CCB->SerialNumber, CCB->TargetID, Command->result, CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
+ BusLogic_Notice("CDB ", HostAdapter);
+ for (i = 0; i < CCB->CDB_Length; i++)
+ BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ BusLogic_Notice("Sense ", HostAdapter);
+ for (i = 0; i < CCB->SenseDataLength; i++)
+ BusLogic_Notice(" %02X", HostAdapter, Command->sense_buffer[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ }
+ }
+ break;
+ }
+ /*
+ When an INQUIRY command completes normally, save the
+ CmdQue (Tagged Queuing Supported) and WBus16 (16 Bit
+ Wide Data Transfers Supported) bits.
+ */
+ if (CCB->CDB[0] == INQUIRY && CCB->CDB[1] == 0 && CCB->HostAdapterStatus == BusLogic_CommandCompletedNormally) {
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[CCB->TargetID];
+ struct SCSI_Inquiry *InquiryResult = (struct SCSI_Inquiry *) Command->request_buffer;
+ TargetFlags->TargetExists = true;
+ TargetFlags->TaggedQueuingSupported = InquiryResult->CmdQue;
+ TargetFlags->WideTransfersSupported = InquiryResult->WBus16;
+ }
+ /*
+ Place CCB back on the Host Adapter's free list.
+ */
+ BusLogic_DeallocateCCB(CCB);
+ /*
+ Call the SCSI Command Completion Routine.
+ */
+ Command->scsi_done(Command);
+ }
+ }
+ HostAdapter->ProcessCompletedCCBsActive = false;
+}
+
+
+/*
+ BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+ Adapters.
+*/
+
+static irqreturn_t BusLogic_InterruptHandler(int IRQ_Channel, void *DeviceIdentifier, struct pt_regs *InterruptRegisters)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) DeviceIdentifier;
+ unsigned long ProcessorFlags;
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ spin_lock_irqsave(HostAdapter->SCSI_Host->host_lock, ProcessorFlags);
+ /*
+ Handle Interrupts appropriately for each Host Adapter type.
+ */
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
+ union BusLogic_InterruptRegister InterruptRegister;
+ /*
+ Read the Host Adapter Interrupt Register.
+ */
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ if (InterruptRegister.ir.InterruptValid) {
+ /*
+ Acknowledge the interrupt and reset the Host Adapter
+ Interrupt Register.
+ */
+ BusLogic_InterruptReset(HostAdapter);
+ /*
+ Process valid External SCSI Bus Reset and Incoming Mailbox
+ Loaded Interrupts. Command Complete Interrupts are noted,
+ and Outgoing Mailbox Available Interrupts are ignored, as
+ they are never enabled.
+ */
+ if (InterruptRegister.ir.ExternalBusReset)
+ HostAdapter->HostAdapterExternalReset = true;
+ else if (InterruptRegister.ir.IncomingMailboxLoaded)
+ BusLogic_ScanIncomingMailboxes(HostAdapter);
+ else if (InterruptRegister.ir.CommandComplete)
+ HostAdapter->HostAdapterCommandCompleted = true;
+ }
+ } else {
+ /*
+ Check if there is a pending interrupt for this Host Adapter.
+ */
+ if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
+ switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle)) {
+ case FlashPoint_NormalInterrupt:
+ break;
+ case FlashPoint_ExternalBusReset:
+ HostAdapter->HostAdapterExternalReset = true;
+ break;
+ case FlashPoint_InternalError:
+ BusLogic_Warning("Internal FlashPoint Error detected" " - Resetting Host Adapter\n", HostAdapter);
+ HostAdapter->HostAdapterInternalError = true;
+ break;
+ }
+ }
+ /*
+ Process any completed CCBs.
+ */
+ if (HostAdapter->FirstCompletedCCB != NULL)
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ /*
+ Reset the Host Adapter if requested.
+ */
+ if (HostAdapter->HostAdapterExternalReset) {
+ BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n", HostAdapter, HostAdapter->FullModelName);
+ BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets);
+ BusLogic_ResetHostAdapter(HostAdapter, false);
+ HostAdapter->HostAdapterExternalReset = false;
+ } else if (HostAdapter->HostAdapterInternalError) {
+ BusLogic_Warning("Resetting %s due to Host Adapter Internal Error\n", HostAdapter, HostAdapter->FullModelName);
+ BusLogic_IncrementErrorCounter(&HostAdapter->HostAdapterInternalErrors);
+ BusLogic_ResetHostAdapter(HostAdapter, true);
+ HostAdapter->HostAdapterInternalError = false;
+ }
+ /*
+ Release exclusive access to Host Adapter.
+ */
+ spin_unlock_irqrestore(HostAdapter->SCSI_Host->host_lock, ProcessorFlags);
+ return IRQ_HANDLED;
+}
+
+
+/*
+ BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
+ Mailbox for execution by Host Adapter. The Host Adapter's Lock should
+ already have been acquired by the caller.
+*/
+
+static boolean BusLogic_WriteOutgoingMailbox(struct BusLogic_HostAdapter
+ *HostAdapter, enum BusLogic_ActionCode ActionCode, struct BusLogic_CCB *CCB)
+{
+ struct BusLogic_OutgoingMailbox *NextOutgoingMailbox;
+ NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
+ if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree) {
+ CCB->Status = BusLogic_CCB_Active;
+ /*
+ The CCB field must be written before the Action Code field since
+ the Host Adapter is operating asynchronously and the locking code
+ does not protect against simultaneous access by the Host Adapter.
+ */
+ NextOutgoingMailbox->CCB = CCB->DMA_Handle;
+ NextOutgoingMailbox->ActionCode = ActionCode;
+ BusLogic_StartMailboxCommand(HostAdapter);
+ if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
+ NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+ if (ActionCode == BusLogic_MailboxStartCommand) {
+ HostAdapter->ActiveCommands[CCB->TargetID]++;
+ if (CCB->Opcode != BusLogic_BusDeviceReset)
+ HostAdapter->TargetStatistics[CCB->TargetID].CommandsAttempted++;
+ }
+ return true;
+ }
+ return false;
+}
+
+/* Error Handling (EH) support */
+
+static int BusLogic_host_reset(struct scsi_cmnd * SCpnt)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) SCpnt->device->host->hostdata;
+
+ unsigned int id = SCpnt->device->id;
+ struct BusLogic_TargetStatistics *stats = &HostAdapter->TargetStatistics[id];
+ BusLogic_IncrementErrorCounter(&stats->HostAdapterResetsRequested);
+
+ return BusLogic_ResetHostAdapter(HostAdapter, false);
+}
+
+/*
+ BusLogic_QueueCommand creates a CCB for Command and places it into an
+ Outgoing Mailbox for execution by the associated Host Adapter.
+*/
+
+static int BusLogic_QueueCommand(struct scsi_cmnd *Command, void (*CompletionRoutine) (struct scsi_cmnd *))
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Command->device->host->hostdata;
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[Command->device->id];
+ struct BusLogic_TargetStatistics *TargetStatistics = HostAdapter->TargetStatistics;
+ unsigned char *CDB = Command->cmnd;
+ int CDB_Length = Command->cmd_len;
+ int TargetID = Command->device->id;
+ int LogicalUnit = Command->device->lun;
+ void *BufferPointer = Command->request_buffer;
+ int BufferLength = Command->request_bufflen;
+ int SegmentCount = Command->use_sg;
+ struct BusLogic_CCB *CCB;
+ /*
+ SCSI REQUEST_SENSE commands will be executed automatically by the Host
+ Adapter for any errors, so they should not be executed explicitly unless
+ the Sense Data is zero indicating that no error occurred.
+ */
+ if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0) {
+ Command->result = DID_OK << 16;
+ CompletionRoutine(Command);
+ return 0;
+ }
+ /*
+ Allocate a CCB from the Host Adapter's free list. In the unlikely event
+ that there are none available and memory allocation fails, wait 1 second
+ and try again. If that fails, the Host Adapter is probably hung so signal
+ an error as a Host Adapter Hard Reset should be initiated soon.
+ */
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL) {
+ spin_unlock_irq(HostAdapter->SCSI_Host->host_lock);
+ BusLogic_Delay(1);
+ spin_lock_irq(HostAdapter->SCSI_Host->host_lock);
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL) {
+ Command->result = DID_ERROR << 16;
+ CompletionRoutine(Command);
+ return 0;
+ }
+ }
+ /*
+ Initialize the fields in the BusLogic Command Control Block (CCB).
+ */
+ if (SegmentCount == 0 && BufferLength != 0) {
+ CCB->Opcode = BusLogic_InitiatorCCB;
+ CCB->DataLength = BufferLength;
+ CCB->DataPointer = pci_map_single(HostAdapter->PCI_Device,
+ BufferPointer, BufferLength,
+ Command->sc_data_direction);
+ } else if (SegmentCount != 0) {
+ struct scatterlist *ScatterList = (struct scatterlist *) BufferPointer;
+ int Segment, Count;
+
+ Count = pci_map_sg(HostAdapter->PCI_Device, ScatterList, SegmentCount,
+ Command->sc_data_direction);
+ CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
+ CCB->DataLength = Count * sizeof(struct BusLogic_ScatterGatherSegment);
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ CCB->DataPointer = (unsigned int) CCB->DMA_Handle + ((unsigned long) &CCB->ScatterGatherList - (unsigned long) CCB);
+ else
+ CCB->DataPointer = Virtual_to_32Bit_Virtual(CCB->ScatterGatherList);
+ for (Segment = 0; Segment < Count; Segment++) {
+ CCB->ScatterGatherList[Segment].SegmentByteCount = sg_dma_len(ScatterList + Segment);
+ CCB->ScatterGatherList[Segment].SegmentDataPointer = sg_dma_address(ScatterList + Segment);
+ }
+ } else {
+ CCB->Opcode = BusLogic_InitiatorCCB;
+ CCB->DataLength = BufferLength;
+ CCB->DataPointer = 0;
+ }
+ switch (CDB[0]) {
+ case READ_6:
+ case READ_10:
+ CCB->DataDirection = BusLogic_DataInLengthChecked;
+ TargetStatistics[TargetID].ReadCommands++;
+ BusLogic_IncrementByteCounter(&TargetStatistics[TargetID].TotalBytesRead, BufferLength);
+ BusLogic_IncrementSizeBucket(TargetStatistics[TargetID].ReadCommandSizeBuckets, BufferLength);
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ CCB->DataDirection = BusLogic_DataOutLengthChecked;
+ TargetStatistics[TargetID].WriteCommands++;
+ BusLogic_IncrementByteCounter(&TargetStatistics[TargetID].TotalBytesWritten, BufferLength);
+ BusLogic_IncrementSizeBucket(TargetStatistics[TargetID].WriteCommandSizeBuckets, BufferLength);
+ break;
+ default:
+ CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+ break;
+ }
+ CCB->CDB_Length = CDB_Length;
+ CCB->HostAdapterStatus = 0;
+ CCB->TargetDeviceStatus = 0;
+ CCB->TargetID = TargetID;
+ CCB->LogicalUnit = LogicalUnit;
+ CCB->TagEnable = false;
+ CCB->LegacyTagEnable = false;
+ /*
+ BusLogic recommends that after a Reset the first couple of commands that
+ are sent to a Target Device be sent in a non Tagged Queue fashion so that
+ the Host Adapter and Target Device can establish Synchronous and Wide
+ Transfer before Queue Tag messages can interfere with the Synchronous and
+ Wide Negotiation messages. By waiting to enable Tagged Queuing until after
+ the first BusLogic_MaxTaggedQueueDepth commands have been queued, it is
+ assured that after a Reset any pending commands are requeued before Tagged
+ Queuing is enabled and that the Tagged Queuing message will not occur while
+ the partition table is being printed. In addition, some devices do not
+ properly handle the transition from non-tagged to tagged commands, so it is
+ necessary to wait until there are no pending commands for a target device
+ before queuing tagged commands.
+ */
+ if (HostAdapter->CommandsSinceReset[TargetID]++ >=
+ BusLogic_MaxTaggedQueueDepth && !TargetFlags->TaggedQueuingActive && HostAdapter->ActiveCommands[TargetID] == 0 && TargetFlags->TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) {
+ TargetFlags->TaggedQueuingActive = true;
+ BusLogic_Notice("Tagged Queuing now active for Target %d\n", HostAdapter, TargetID);
+ }
+ if (TargetFlags->TaggedQueuingActive) {
+ enum BusLogic_QueueTag QueueTag = BusLogic_SimpleQueueTag;
+ /*
+ When using Tagged Queuing with Simple Queue Tags, it appears that disk
+ drive controllers do not guarantee that a queued command will not
+ remain in a disconnected state indefinitely if commands that read or
+ write nearer the head position continue to arrive without interruption.
+ Therefore, for each Target Device this driver keeps track of the last
+ time either the queue was empty or an Ordered Queue Tag was issued. If
+ more than 4 seconds (one fifth of the 20 second disk timeout) have
+ elapsed since this last sequence point, this command will be issued
+ with an Ordered Queue Tag rather than a Simple Queue Tag, which forces
+ the Target Device to complete all previously queued commands before
+ this command may be executed.
+ */
+ if (HostAdapter->ActiveCommands[TargetID] == 0)
+ HostAdapter->LastSequencePoint[TargetID] = jiffies;
+ else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 4 * HZ) {
+ HostAdapter->LastSequencePoint[TargetID] = jiffies;
+ QueueTag = BusLogic_OrderedQueueTag;
+ }
+ if (HostAdapter->ExtendedLUNSupport) {
+ CCB->TagEnable = true;
+ CCB->QueueTag = QueueTag;
+ } else {
+ CCB->LegacyTagEnable = true;
+ CCB->LegacyQueueTag = QueueTag;
+ }
+ }
+ memcpy(CCB->CDB, CDB, CDB_Length);
+ CCB->SenseDataLength = sizeof(Command->sense_buffer);
+ CCB->SenseDataPointer = pci_map_single(HostAdapter->PCI_Device, Command->sense_buffer, CCB->SenseDataLength, PCI_DMA_FROMDEVICE);
+ CCB->Command = Command;
+ Command->scsi_done = CompletionRoutine;
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
+ /*
+ Place the CCB in an Outgoing Mailbox. The higher levels of the SCSI
+ Subsystem should not attempt to queue more commands than can be placed
+ in Outgoing Mailboxes, so there should always be one free. In the
+ unlikely event that there are none available, wait 1 second and try
+ again. If that fails, the Host Adapter is probably hung so signal an
+ error as a Host Adapter Hard Reset should be initiated soon.
+ */
+ if (!BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxStartCommand, CCB)) {
+ spin_unlock_irq(HostAdapter->SCSI_Host->host_lock);
+ BusLogic_Warning("Unable to write Outgoing Mailbox - " "Pausing for 1 second\n", HostAdapter);
+ BusLogic_Delay(1);
+ spin_lock_irq(HostAdapter->SCSI_Host->host_lock);
+ if (!BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxStartCommand, CCB)) {
+ BusLogic_Warning("Still unable to write Outgoing Mailbox - " "Host Adapter Dead?\n", HostAdapter);
+ BusLogic_DeallocateCCB(CCB);
+ Command->result = DID_ERROR << 16;
+ Command->scsi_done(Command);
+ }
+ }
+ } else {
+ /*
+ Call the FlashPoint SCCB Manager to start execution of the CCB.
+ */
+ CCB->Status = BusLogic_CCB_Active;
+ HostAdapter->ActiveCommands[TargetID]++;
+ TargetStatistics[TargetID].CommandsAttempted++;
+ FlashPoint_StartCCB(HostAdapter->CardHandle, CCB);
+ /*
+ The Command may have already completed and BusLogic_QueueCompletedCCB
+ been called, or it may still be pending.
+ */
+ if (CCB->Status == BusLogic_CCB_Completed)
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ }
+ return 0;
+}
+
+
+/*
+ BusLogic_AbortCommand aborts Command if possible.
+*/
+
+static int BusLogic_AbortCommand(struct scsi_cmnd *Command)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Command->device->host->hostdata;
+
+ int TargetID = Command->device->id;
+ struct BusLogic_CCB *CCB;
+ BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsRequested);
+ /*
+ If this Command has already completed, then no Abort is necessary.
+ */
+ if (Command->serial_number != Command->serial_number_at_timeout) {
+ BusLogic_Warning("Unable to Abort Command to Target %d - " "Already Completed\n", HostAdapter, TargetID);
+ return SUCCESS;
+ }
+ /*
+ Attempt to find an Active CCB for this Command. If no Active CCB for this
+ Command is found, then no Abort is necessary.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Command == Command)
+ break;
+ if (CCB == NULL) {
+ BusLogic_Warning("Unable to Abort Command to Target %d - " "No CCB Found\n", HostAdapter, TargetID);
+ return SUCCESS;
+ } else if (CCB->Status == BusLogic_CCB_Completed) {
+ BusLogic_Warning("Unable to Abort Command to Target %d - " "CCB Completed\n", HostAdapter, TargetID);
+ return SUCCESS;
+ } else if (CCB->Status == BusLogic_CCB_Reset) {
+ BusLogic_Warning("Unable to Abort Command to Target %d - " "CCB Reset\n", HostAdapter, TargetID);
+ return SUCCESS;
+ }
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) {
+ /*
+ Attempt to Abort this CCB. MultiMaster Firmware versions prior to 5.xx
+ do not generate Abort Tag messages, but only generate the non-tagged
+ Abort message. Since non-tagged commands are not sent by the Host
+ Adapter until the queue of outstanding tagged commands has completed,
+ and the Abort message is treated as a non-tagged command, it is
+ effectively impossible to abort commands when Tagged Queuing is active.
+ Firmware version 5.xx does generate Abort Tag messages, so it is
+ possible to abort commands when Tagged Queuing is active.
+ */
+ if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive && HostAdapter->FirmwareVersion[0] < '5') {
+ BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " "Abort Tag Not Supported\n", HostAdapter, CCB->SerialNumber, TargetID);
+ return FAILURE;
+ } else if (BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxAbortCommand, CCB)) {
+ BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID);
+ BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted);
+ return SUCCESS;
+ } else {
+ BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " "No Outgoing Mailboxes\n", HostAdapter, CCB->SerialNumber, TargetID);
+ return FAILURE;
+ }
+ } else {
+ /*
+ Call the FlashPoint SCCB Manager to abort execution of the CCB.
+ */
+ BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID);
+ BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted);
+ FlashPoint_AbortCCB(HostAdapter->CardHandle, CCB);
+ /*
+ The Abort may have already been completed and
+ BusLogic_QueueCompletedCCB been called, or it
+ may still be pending.
+ */
+ if (CCB->Status == BusLogic_CCB_Completed) {
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ }
+ return SUCCESS;
+ }
+ return SUCCESS;
+}
+
+/*
+ BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+ currently executing SCSI Commands as having been Reset.
+*/
+
+static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *HostAdapter, boolean HardReset)
+{
+ struct BusLogic_CCB *CCB;
+ int TargetID;
+
+ /*
+ * Attempt to Reset and Reinitialize the Host Adapter.
+ */
+
+ if (!(BusLogic_HardwareResetHostAdapter(HostAdapter, HardReset) && BusLogic_InitializeHostAdapter(HostAdapter))) {
+ BusLogic_Error("Resetting %s Failed\n", HostAdapter, HostAdapter->FullModelName);
+ return FAILURE;
+ }
+
+ /*
+ * Deallocate all currently executing CCBs.
+ */
+
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Active)
+ BusLogic_DeallocateCCB(CCB);
+ /*
+ * Wait a few seconds between the Host Adapter Hard Reset which
+ * initiates a SCSI Bus Reset and issuing any SCSI Commands. Some
+ * SCSI devices get confused if they receive SCSI Commands too soon
+ * after a SCSI Bus Reset.
+ */
+
+ if (HardReset) {
+ spin_unlock_irq(HostAdapter->SCSI_Host->host_lock);
+ BusLogic_Delay(HostAdapter->BusSettleTime);
+ spin_lock_irq(HostAdapter->SCSI_Host->host_lock);
+ }
+
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ HostAdapter->LastResetAttempted[TargetID] = jiffies;
+ HostAdapter->LastResetCompleted[TargetID] = jiffies;
+ }
+ return SUCCESS;
+}
+
+/*
+ BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk
+ Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and
+ the appropriate number of cylinders so as not to exceed drive capacity. In
+ order for disks equal to or larger than 1 GB to be addressable by the BIOS
+ without exceeding the BIOS limitation of 1024 cylinders, Extended Translation
+ may be enabled in AutoSCSI on FlashPoint Host Adapters and on "W" and "C"
+ series MultiMaster Host Adapters, or by a dip switch setting on "S" and "A"
+ series MultiMaster Host Adapters. With Extended Translation enabled, drives
+ between 1 GB inclusive and 2 GB exclusive are given a disk geometry of 128
+ heads and 32 sectors, and drives above 2 GB inclusive are given a disk
+ geometry of 255 heads and 63 sectors. However, if the BIOS detects that the
+ Extended Translation setting does not match the geometry in the partition
+ table, then the translation inferred from the partition table will be used by
+ the BIOS, and a warning may be displayed.
+*/
+
+static int BusLogic_BIOSDiskParameters(struct scsi_device *sdev, struct block_device *Device, sector_t capacity, int *Parameters)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) sdev->host->hostdata;
+ struct BIOS_DiskParameters *DiskParameters = (struct BIOS_DiskParameters *) Parameters;
+ unsigned char *buf;
+ if (HostAdapter->ExtendedTranslationEnabled && capacity >= 2 * 1024 * 1024 /* 1 GB in 512 byte sectors */ ) {
+ if (capacity >= 4 * 1024 * 1024 /* 2 GB in 512 byte sectors */ ) {
+ DiskParameters->Heads = 255;
+ DiskParameters->Sectors = 63;
+ } else {
+ DiskParameters->Heads = 128;
+ DiskParameters->Sectors = 32;
+ }
+ } else {
+ DiskParameters->Heads = 64;
+ DiskParameters->Sectors = 32;
+ }
+ DiskParameters->Cylinders = (unsigned long) capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+ buf = scsi_bios_ptable(Device);
+ if (buf == NULL)
+ return 0;
+ /*
+ If the boot sector partition table flag is valid, search for a partition
+ table entry whose end_head matches one of the standard BusLogic geometry
+ translations (64/32, 128/32, or 255/63).
+ */
+ if (*(unsigned short *) (buf + 64) == 0xAA55) {
+ struct partition *FirstPartitionEntry = (struct partition *) buf;
+ struct partition *PartitionEntry = FirstPartitionEntry;
+ int SavedCylinders = DiskParameters->Cylinders, PartitionNumber;
+ unsigned char PartitionEntryEndHead = 0, PartitionEntryEndSector = 0;
+ for (PartitionNumber = 0; PartitionNumber < 4; PartitionNumber++) {
+ PartitionEntryEndHead = PartitionEntry->end_head;
+ PartitionEntryEndSector = PartitionEntry->end_sector & 0x3F;
+ if (PartitionEntryEndHead == 64 - 1) {
+ DiskParameters->Heads = 64;
+ DiskParameters->Sectors = 32;
+ break;
+ } else if (PartitionEntryEndHead == 128 - 1) {
+ DiskParameters->Heads = 128;
+ DiskParameters->Sectors = 32;
+ break;
+ } else if (PartitionEntryEndHead == 255 - 1) {
+ DiskParameters->Heads = 255;
+ DiskParameters->Sectors = 63;
+ break;
+ }
+ PartitionEntry++;
+ }
+ if (PartitionNumber == 4) {
+ PartitionEntryEndHead = FirstPartitionEntry->end_head;
+ PartitionEntryEndSector = FirstPartitionEntry->end_sector & 0x3F;
+ }
+ DiskParameters->Cylinders = (unsigned long) capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+ if (PartitionNumber < 4 && PartitionEntryEndSector == DiskParameters->Sectors) {
+ if (DiskParameters->Cylinders != SavedCylinders)
+ BusLogic_Warning("Adopting Geometry %d/%d from Partition Table\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors);
+ } else if (PartitionEntryEndHead > 0 || PartitionEntryEndSector > 0) {
+ BusLogic_Warning("Warning: Partition Table appears to " "have Geometry %d/%d which is\n", HostAdapter, PartitionEntryEndHead + 1, PartitionEntryEndSector);
+ BusLogic_Warning("not compatible with current BusLogic " "Host Adapter Geometry %d/%d\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors);
+ }
+ }
+ kfree(buf);
+ return 0;
+}
+
+
+/*
+ BugLogic_ProcDirectoryInfo implements /proc/scsi/BusLogic/<N>.
+*/
+
+static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *shost, char *ProcBuffer, char **StartPointer, off_t Offset, int BytesAvailable, int WriteFlag)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) shost->hostdata;
+ struct BusLogic_TargetStatistics *TargetStatistics;
+ int TargetID, Length;
+ char *Buffer;
+
+ TargetStatistics = HostAdapter->TargetStatistics;
+ if (WriteFlag) {
+ HostAdapter->ExternalHostAdapterResets = 0;
+ HostAdapter->HostAdapterInternalErrors = 0;
+ memset(TargetStatistics, 0, BusLogic_MaxTargetDevices * sizeof(struct BusLogic_TargetStatistics));
+ return 0;
+ }
+ Buffer = HostAdapter->MessageBuffer;
+ Length = HostAdapter->MessageBufferLength;
+ Length += sprintf(&Buffer[Length], "\n\
+Current Driver Queue Depth: %d\n\
+Currently Allocated CCBs: %d\n", HostAdapter->DriverQueueDepth, HostAdapter->AllocatedCCBs);
+ Length += sprintf(&Buffer[Length], "\n\n\
+ DATA TRANSFER STATISTICS\n\
+\n\
+Target Tagged Queuing Queue Depth Active Attempted Completed\n\
+====== ============== =========== ====== ========= =========\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists)
+ continue;
+ Length += sprintf(&Buffer[Length], " %2d %s", TargetID, (TargetFlags->TaggedQueuingSupported ? (TargetFlags->TaggedQueuingActive ? " Active" : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)
+ ? " Permitted" : " Disabled"))
+ : "Not Supported"));
+ Length += sprintf(&Buffer[Length],
+ " %3d %3u %9u %9u\n", HostAdapter->QueueDepth[TargetID], HostAdapter->ActiveCommands[TargetID], TargetStatistics[TargetID].CommandsAttempted, TargetStatistics[TargetID].CommandsCompleted);
+ }
+ Length += sprintf(&Buffer[Length], "\n\
+Target Read Commands Write Commands Total Bytes Read Total Bytes Written\n\
+====== ============= ============== =================== ===================\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists)
+ continue;
+ Length += sprintf(&Buffer[Length], " %2d %9u %9u", TargetID, TargetStatistics[TargetID].ReadCommands, TargetStatistics[TargetID].WriteCommands);
+ if (TargetStatistics[TargetID].TotalBytesRead.Billions > 0)
+ Length += sprintf(&Buffer[Length], " %9u%09u", TargetStatistics[TargetID].TotalBytesRead.Billions, TargetStatistics[TargetID].TotalBytesRead.Units);
+ else
+ Length += sprintf(&Buffer[Length], " %9u", TargetStatistics[TargetID].TotalBytesRead.Units);
+ if (TargetStatistics[TargetID].TotalBytesWritten.Billions > 0)
+ Length += sprintf(&Buffer[Length], " %9u%09u\n", TargetStatistics[TargetID].TotalBytesWritten.Billions, TargetStatistics[TargetID].TotalBytesWritten.Units);
+ else
+ Length += sprintf(&Buffer[Length], " %9u\n", TargetStatistics[TargetID].TotalBytesWritten.Units);
+ }
+ Length += sprintf(&Buffer[Length], "\n\
+Target Command 0-1KB 1-2KB 2-4KB 4-8KB 8-16KB\n\
+====== ======= ========= ========= ========= ========= =========\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists)
+ continue;
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Read %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[0],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[1], TargetStatistics[TargetID].ReadCommandSizeBuckets[2], TargetStatistics[TargetID].ReadCommandSizeBuckets[3], TargetStatistics[TargetID].ReadCommandSizeBuckets[4]);
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Write %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[0],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[1], TargetStatistics[TargetID].WriteCommandSizeBuckets[2], TargetStatistics[TargetID].WriteCommandSizeBuckets[3], TargetStatistics[TargetID].WriteCommandSizeBuckets[4]);
+ }
+ Length += sprintf(&Buffer[Length], "\n\
+Target Command 16-32KB 32-64KB 64-128KB 128-256KB 256KB+\n\
+====== ======= ========= ========= ========= ========= =========\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists)
+ continue;
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Read %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[5],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[6], TargetStatistics[TargetID].ReadCommandSizeBuckets[7], TargetStatistics[TargetID].ReadCommandSizeBuckets[8], TargetStatistics[TargetID].ReadCommandSizeBuckets[9]);
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Write %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[5],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[6], TargetStatistics[TargetID].WriteCommandSizeBuckets[7], TargetStatistics[TargetID].WriteCommandSizeBuckets[8], TargetStatistics[TargetID].WriteCommandSizeBuckets[9]);
+ }
+ Length += sprintf(&Buffer[Length], "\n\n\
+ ERROR RECOVERY STATISTICS\n\
+\n\
+ Command Aborts Bus Device Resets Host Adapter Resets\n\
+Target Requested Completed Requested Completed Requested Completed\n\
+ ID \\\\\\\\ Attempted //// \\\\\\\\ Attempted //// \\\\\\\\ Attempted ////\n\
+====== ===== ===== ===== ===== ===== ===== ===== ===== =====\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) {
+ struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists)
+ continue;
+ Length += sprintf(&Buffer[Length], "\
+ %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", TargetID, TargetStatistics[TargetID].CommandAbortsRequested, TargetStatistics[TargetID].CommandAbortsAttempted, TargetStatistics[TargetID].CommandAbortsCompleted, TargetStatistics[TargetID].BusDeviceResetsRequested, TargetStatistics[TargetID].BusDeviceResetsAttempted, TargetStatistics[TargetID].BusDeviceResetsCompleted, TargetStatistics[TargetID].HostAdapterResetsRequested, TargetStatistics[TargetID].HostAdapterResetsAttempted, TargetStatistics[TargetID].HostAdapterResetsCompleted);
+ }
+ Length += sprintf(&Buffer[Length], "\nExternal Host Adapter Resets: %d\n", HostAdapter->ExternalHostAdapterResets);
+ Length += sprintf(&Buffer[Length], "Host Adapter Internal Errors: %d\n", HostAdapter->HostAdapterInternalErrors);
+ if (Length >= BusLogic_MessageBufferSize)
+ BusLogic_Error("Message Buffer length %d exceeds size %d\n", HostAdapter, Length, BusLogic_MessageBufferSize);
+ if ((Length -= Offset) <= 0)
+ return 0;
+ if (Length >= BytesAvailable)
+ Length = BytesAvailable;
+ memcpy(ProcBuffer, HostAdapter->MessageBuffer + Offset, Length);
+ *StartPointer = ProcBuffer;
+ return Length;
+}
+
+
+/*
+ BusLogic_Message prints Driver Messages.
+*/
+
+static void BusLogic_Message(enum BusLogic_MessageLevel MessageLevel, char *Format, struct BusLogic_HostAdapter *HostAdapter, ...)
+{
+ static char Buffer[BusLogic_LineBufferSize];
+ static boolean BeginningOfLine = true;
+ va_list Arguments;
+ int Length = 0;
+ va_start(Arguments, HostAdapter);
+ Length = vsprintf(Buffer, Format, Arguments);
+ va_end(Arguments);
+ if (MessageLevel == BusLogic_AnnounceLevel) {
+ static int AnnouncementLines = 0;
+ strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength], Buffer);
+ HostAdapter->MessageBufferLength += Length;
+ if (++AnnouncementLines <= 2)
+ printk("%sscsi: %s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+ } else if (MessageLevel == BusLogic_InfoLevel) {
+ strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength], Buffer);
+ HostAdapter->MessageBufferLength += Length;
+ if (BeginningOfLine) {
+ if (Buffer[0] != '\n' || Length > 1)
+ printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], HostAdapter->HostNumber, Buffer);
+ } else
+ printk("%s", Buffer);
+ } else {
+ if (BeginningOfLine) {
+ if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized)
+ printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], HostAdapter->HostNumber, Buffer);
+ else
+ printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+ } else
+ printk("%s", Buffer);
+ }
+ BeginningOfLine = (Buffer[Length - 1] == '\n');
+}
+
+
+/*
+ BusLogic_ParseKeyword parses an individual option keyword. It returns true
+ and updates the pointer if the keyword is recognized and false otherwise.
+*/
+
+static boolean __init BusLogic_ParseKeyword(char **StringPointer, char *Keyword)
+{
+ char *Pointer = *StringPointer;
+ while (*Keyword != '\0') {
+ char StringChar = *Pointer++;
+ char KeywordChar = *Keyword++;
+ if (StringChar >= 'A' && StringChar <= 'Z')
+ StringChar += 'a' - 'Z';
+ if (KeywordChar >= 'A' && KeywordChar <= 'Z')
+ KeywordChar += 'a' - 'Z';
+ if (StringChar != KeywordChar)
+ return false;
+ }
+ *StringPointer = Pointer;
+ return true;
+}
+
+
+/*
+ BusLogic_ParseDriverOptions handles processing of BusLogic Driver Options
+ specifications.
+
+ BusLogic Driver Options may be specified either via the Linux Kernel Command
+ Line or via the Loadable Kernel Module Installation Facility. Driver Options
+ for multiple host adapters may be specified either by separating the option
+ strings by a semicolon, or by specifying multiple "BusLogic=" strings on the
+ command line. Individual option specifications for a single host adapter are
+ separated by commas. The Probing and Debugging Options apply to all host
+ adapters whereas the remaining options apply individually only to the
+ selected host adapter.
+
+ The BusLogic Driver Probing Options are described in
+ <file:Documentation/scsi/BusLogic.txt>.
+*/
+
+static int __init BusLogic_ParseDriverOptions(char *OptionsString)
+{
+ while (true) {
+ struct BusLogic_DriverOptions *DriverOptions = &BusLogic_DriverOptions[BusLogic_DriverOptionsCount++];
+ int TargetID;
+ memset(DriverOptions, 0, sizeof(struct BusLogic_DriverOptions));
+ while (*OptionsString != '\0' && *OptionsString != ';') {
+ /* Probing Options. */
+ if (BusLogic_ParseKeyword(&OptionsString, "IO:")) {
+ unsigned long IO_Address = simple_strtoul(OptionsString, &OptionsString, 0);
+ BusLogic_ProbeOptions.LimitedProbeISA = true;
+ switch (IO_Address) {
+ case 0x330:
+ BusLogic_ProbeOptions.Probe330 = true;
+ break;
+ case 0x334:
+ BusLogic_ProbeOptions.Probe334 = true;
+ break;
+ case 0x230:
+ BusLogic_ProbeOptions.Probe230 = true;
+ break;
+ case 0x234:
+ BusLogic_ProbeOptions.Probe234 = true;
+ break;
+ case 0x130:
+ BusLogic_ProbeOptions.Probe130 = true;
+ break;
+ case 0x134:
+ BusLogic_ProbeOptions.Probe134 = true;
+ break;
+ default:
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid I/O Address 0x%X)\n", NULL, IO_Address);
+ return 0;
+ }
+ } else if (BusLogic_ParseKeyword(&OptionsString, "NoProbeISA"))
+ BusLogic_ProbeOptions.NoProbeISA = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoProbePCI"))
+ BusLogic_ProbeOptions.NoProbePCI = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoProbe"))
+ BusLogic_ProbeOptions.NoProbe = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoSortPCI"))
+ BusLogic_ProbeOptions.NoSortPCI = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "MultiMasterFirst"))
+ BusLogic_ProbeOptions.MultiMasterFirst = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "FlashPointFirst"))
+ BusLogic_ProbeOptions.FlashPointFirst = true;
+ /* Tagged Queuing Options. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:[") || BusLogic_ParseKeyword(&OptionsString, "QD:[")) {
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) {
+ unsigned short QueueDepth = simple_strtoul(OptionsString, &OptionsString, 0);
+ if (QueueDepth > BusLogic_MaxTaggedQueueDepth) {
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Queue Depth %d)\n", NULL, QueueDepth);
+ return 0;
+ }
+ DriverOptions->QueueDepth[TargetID] = QueueDepth;
+ if (*OptionsString == ',')
+ OptionsString++;
+ else if (*OptionsString == ']')
+ break;
+ else {
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(',' or ']' expected at '%s')\n", NULL, OptionsString);
+ return 0;
+ }
+ }
+ if (*OptionsString != ']') {
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(']' expected at '%s')\n", NULL, OptionsString);
+ return 0;
+ } else
+ OptionsString++;
+ } else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:") || BusLogic_ParseKeyword(&OptionsString, "QD:")) {
+ unsigned short QueueDepth = simple_strtoul(OptionsString, &OptionsString, 0);
+ if (QueueDepth == 0 || QueueDepth > BusLogic_MaxTaggedQueueDepth) {
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Queue Depth %d)\n", NULL, QueueDepth);
+ return 0;
+ }
+ DriverOptions->CommonQueueDepth = QueueDepth;
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+ DriverOptions->QueueDepth[TargetID] = QueueDepth;
+ } else if (BusLogic_ParseKeyword(&OptionsString, "TaggedQueuing:") || BusLogic_ParseKeyword(&OptionsString, "TQ:")) {
+ if (BusLogic_ParseKeyword(&OptionsString, "Default")) {
+ DriverOptions->TaggedQueuingPermitted = 0x0000;
+ DriverOptions->TaggedQueuingPermittedMask = 0x0000;
+ } else if (BusLogic_ParseKeyword(&OptionsString, "Enable")) {
+ DriverOptions->TaggedQueuingPermitted = 0xFFFF;
+ DriverOptions->TaggedQueuingPermittedMask = 0xFFFF;
+ } else if (BusLogic_ParseKeyword(&OptionsString, "Disable")) {
+ DriverOptions->TaggedQueuingPermitted = 0x0000;
+ DriverOptions->TaggedQueuingPermittedMask = 0xFFFF;
+ } else {
+ unsigned short TargetBit;
+ for (TargetID = 0, TargetBit = 1; TargetID < BusLogic_MaxTargetDevices; TargetID++, TargetBit <<= 1)
+ switch (*OptionsString++) {
+ case 'Y':
+ DriverOptions->TaggedQueuingPermitted |= TargetBit;
+ DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ break;
+ case 'N':
+ DriverOptions->TaggedQueuingPermitted &= ~TargetBit;
+ DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ break;
+ case 'X':
+ break;
+ default:
+ OptionsString--;
+ TargetID = BusLogic_MaxTargetDevices;
+ break;
+ }
+ }
+ }
+ /* Miscellaneous Options. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "BusSettleTime:") || BusLogic_ParseKeyword(&OptionsString, "BST:")) {
+ unsigned short BusSettleTime = simple_strtoul(OptionsString, &OptionsString, 0);
+ if (BusSettleTime > 5 * 60) {
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Bus Settle Time %d)\n", NULL, BusSettleTime);
+ return 0;
+ }
+ DriverOptions->BusSettleTime = BusSettleTime;
+ } else if (BusLogic_ParseKeyword(&OptionsString, "InhibitTargetInquiry"))
+ DriverOptions->LocalOptions.InhibitTargetInquiry = true;
+ /* Debugging Options. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceProbe"))
+ BusLogic_GlobalOptions.TraceProbe = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceHardwareReset"))
+ BusLogic_GlobalOptions.TraceHardwareReset = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceConfiguration"))
+ BusLogic_GlobalOptions.TraceConfiguration = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceErrors"))
+ BusLogic_GlobalOptions.TraceErrors = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "Debug")) {
+ BusLogic_GlobalOptions.TraceProbe = true;
+ BusLogic_GlobalOptions.TraceHardwareReset = true;
+ BusLogic_GlobalOptions.TraceConfiguration = true;
+ BusLogic_GlobalOptions.TraceErrors = true;
+ }
+ if (*OptionsString == ',')
+ OptionsString++;
+ else if (*OptionsString != ';' && *OptionsString != '\0') {
+ BusLogic_Error("BusLogic: Unexpected Driver Option '%s' " "ignored\n", NULL, OptionsString);
+ *OptionsString = '\0';
+ }
+ }
+ if (!(BusLogic_DriverOptionsCount == 0 || BusLogic_ProbeInfoCount == 0 || BusLogic_DriverOptionsCount == BusLogic_ProbeInfoCount)) {
+ BusLogic_Error("BusLogic: Invalid Driver Options " "(all or no I/O Addresses must be specified)\n", NULL);
+ return 0;
+ }
+ /*
+ Tagged Queuing is disabled when the Queue Depth is 1 since queuing
+ multiple commands is not possible.
+ */
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+ if (DriverOptions->QueueDepth[TargetID] == 1) {
+ unsigned short TargetBit = 1 << TargetID;
+ DriverOptions->TaggedQueuingPermitted &= ~TargetBit;
+ DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ }
+ if (*OptionsString == ';')
+ OptionsString++;
+ if (*OptionsString == '\0')
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ Get it all started
+*/
+
+static struct scsi_host_template Bus_Logic_template = {
+ .module = THIS_MODULE,
+ .proc_name = "BusLogic",
+ .proc_info = BusLogic_ProcDirectoryInfo,
+ .name = "BusLogic",
+ .info = BusLogic_DriverInfo,
+ .queuecommand = BusLogic_QueueCommand,
+ .slave_configure = BusLogic_SlaveConfigure,
+ .bios_param = BusLogic_BIOSDiskParameters,
+ .eh_host_reset_handler = BusLogic_host_reset,
+#if 0
+ .eh_abort_handler = BusLogic_AbortCommand,
+#endif
+ .unchecked_isa_dma = 1,
+ .max_sectors = 128,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+/*
+ BusLogic_Setup handles processing of Kernel Command Line Arguments.
+*/
+
+static int __init BusLogic_Setup(char *str)
+{
+ int ints[3];
+
+ (void) get_options(str, ARRAY_SIZE(ints), ints);
+
+ if (ints[0] != 0) {
+ BusLogic_Error("BusLogic: Obsolete Command Line Entry " "Format Ignored\n", NULL);
+ return 0;
+ }
+ if (str == NULL || *str == '\0')
+ return 0;
+ return BusLogic_ParseDriverOptions(str);
+}
+
+/*
+ * Exit function. Deletes all hosts associated with this driver.
+ */
+
+static void __exit BusLogic_exit(void)
+{
+ struct BusLogic_HostAdapter *ha, *next;
+
+ list_for_each_entry_safe(ha, next, &BusLogic_host_list, host_list)
+ BusLogic_ReleaseHostAdapter(ha);
+}
+
+__setup("BusLogic=", BusLogic_Setup);
+
+module_init(BusLogic_init);
+module_exit(BusLogic_exit);
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
new file mode 100644
index 000000000000..1aaa6569edac
--- /dev/null
+++ b/drivers/scsi/BusLogic.h
@@ -0,0 +1,1359 @@
+/*
+
+ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
+
+ Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for complete details.
+
+ The author respectfully requests that any modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+ advice has been invaluable, to David Gentzel, for writing the original Linux
+ BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+ Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+ Manager available as freely redistributable source code.
+
+*/
+
+#ifndef _BUSLOGIC_H
+#define _BUSLOGIC_H
+
+#include <linux/config.h>
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
+/*
+ FlashPoint support is only available for the Intel x86 Architecture with
+ CONFIG_PCI set.
+*/
+
+#ifndef __i386__
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+#endif
+
+#ifndef CONFIG_PCI
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+#define BusLogic_InitializeProbeInfoListISA BusLogic_InitializeProbeInfoList
+#endif
+
+
+/*
+ Define the maximum number of BusLogic Host Adapters supported by this driver.
+*/
+
+#define BusLogic_MaxHostAdapters 16
+
+
+/*
+ Define the maximum number of Target Devices supported by this driver.
+*/
+
+#define BusLogic_MaxTargetDevices 16
+
+
+/*
+ Define the maximum number of Scatter/Gather Segments used by this driver.
+ For optimal performance, it is important that this limit be at least as
+ large as the largest single request generated by the I/O Subsystem.
+*/
+
+#define BusLogic_ScatterGatherLimit 128
+
+
+/*
+ Define the maximum, maximum automatic, minimum automatic, and default Queue
+ Depth to allow for Target Devices depending on whether or not they support
+ Tagged Queuing and whether or not ISA Bounce Buffers are required.
+*/
+
+#define BusLogic_MaxTaggedQueueDepth 64
+#define BusLogic_MaxAutomaticTaggedQueueDepth 28
+#define BusLogic_MinAutomaticTaggedQueueDepth 7
+#define BusLogic_TaggedQueueDepthBB 3
+#define BusLogic_UntaggedQueueDepth 3
+#define BusLogic_UntaggedQueueDepthBB 2
+
+
+/*
+ Define the default amount of time in seconds to wait between a Host Adapter
+ Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI commands.
+ Some SCSI devices get confused if they receive SCSI commands too soon after
+ a SCSI Bus Reset.
+*/
+
+#define BusLogic_DefaultBusSettleTime 2
+
+
+/*
+ Define the maximum number of Mailboxes that should be used for MultiMaster
+ Host Adapters. This number is chosen to be larger than the maximum Host
+ Adapter Queue Depth and small enough so that the Host Adapter structure
+ does not cross an allocation block size boundary.
+*/
+
+#define BusLogic_MaxMailboxes 211
+
+
+/*
+ Define the number of CCBs that should be allocated as a group to optimize
+ Kernel memory allocation.
+*/
+
+#define BusLogic_CCB_AllocationGroupSize 7
+
+
+/*
+ Define the Host Adapter Line and Message Buffer Sizes.
+*/
+
+#define BusLogic_LineBufferSize 100
+#define BusLogic_MessageBufferSize 9700
+
+
+/*
+ Define the Driver Message Levels.
+*/
+
+enum BusLogic_MessageLevel {
+ BusLogic_AnnounceLevel = 0,
+ BusLogic_InfoLevel = 1,
+ BusLogic_NoticeLevel = 2,
+ BusLogic_WarningLevel = 3,
+ BusLogic_ErrorLevel = 4
+};
+
+static char *BusLogic_MessageLevelMap[] = { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR };
+
+
+/*
+ Define Driver Message macros.
+*/
+
+#define BusLogic_Announce(Format, Arguments...) \
+ BusLogic_Message(BusLogic_AnnounceLevel, Format, ##Arguments)
+
+#define BusLogic_Info(Format, Arguments...) \
+ BusLogic_Message(BusLogic_InfoLevel, Format, ##Arguments)
+
+#define BusLogic_Notice(Format, Arguments...) \
+ BusLogic_Message(BusLogic_NoticeLevel, Format, ##Arguments)
+
+#define BusLogic_Warning(Format, Arguments...) \
+ BusLogic_Message(BusLogic_WarningLevel, Format, ##Arguments)
+
+#define BusLogic_Error(Format, Arguments...) \
+ BusLogic_Message(BusLogic_ErrorLevel, Format, ##Arguments)
+
+
+/*
+ Define the types of BusLogic Host Adapters that are supported and the number
+ of I/O Addresses required by each type.
+*/
+
+enum BusLogic_HostAdapterType {
+ BusLogic_MultiMaster = 1,
+ BusLogic_FlashPoint = 2
+} PACKED;
+
+#define BusLogic_MultiMasterAddressCount 4
+#define BusLogic_FlashPointAddressCount 256
+
+static int BusLogic_HostAdapterAddressCount[3] = { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount };
+
+
+/*
+ Define macros for testing the Host Adapter Type.
+*/
+
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+
+#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
+ (HostAdapter->HostAdapterType == BusLogic_MultiMaster)
+
+#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
+ (HostAdapter->HostAdapterType == BusLogic_FlashPoint)
+
+#else
+
+#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
+ (true)
+
+#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
+ (false)
+
+#endif
+
+
+/*
+ Define the possible Host Adapter Bus Types.
+*/
+
+enum BusLogic_HostAdapterBusType {
+ BusLogic_Unknown_Bus = 0,
+ BusLogic_ISA_Bus = 1,
+ BusLogic_EISA_Bus = 2,
+ BusLogic_PCI_Bus = 3,
+ BusLogic_VESA_Bus = 4,
+ BusLogic_MCA_Bus = 5
+} PACKED;
+
+static char *BusLogic_HostAdapterBusNames[] = { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" };
+
+static enum BusLogic_HostAdapterBusType BusLogic_HostAdapterBusTypes[] = {
+ BusLogic_VESA_Bus, /* BT-4xx */
+ BusLogic_ISA_Bus, /* BT-5xx */
+ BusLogic_MCA_Bus, /* BT-6xx */
+ BusLogic_EISA_Bus, /* BT-7xx */
+ BusLogic_Unknown_Bus, /* BT-8xx */
+ BusLogic_PCI_Bus /* BT-9xx */
+};
+
+/*
+ Define the possible Host Adapter BIOS Disk Geometry Translations.
+*/
+
+enum BusLogic_BIOS_DiskGeometryTranslation {
+ BusLogic_BIOS_Disk_Not_Installed = 0,
+ BusLogic_BIOS_Disk_Installed_64x32 = 1,
+ BusLogic_BIOS_Disk_Installed_128x32 = 2,
+ BusLogic_BIOS_Disk_Installed_255x63 = 3
+} PACKED;
+
+
+/*
+ Define a Boolean data type.
+*/
+
+typedef enum {
+ false,
+ true
+} PACKED boolean;
+
+/*
+ Define a 10^18 Statistics Byte Counter data type.
+*/
+
+struct BusLogic_ByteCounter {
+ unsigned int Units;
+ unsigned int Billions;
+};
+
+
+/*
+ Define the structure for I/O Address and Bus Probing Information.
+*/
+
+struct BusLogic_ProbeInfo {
+ enum BusLogic_HostAdapterType HostAdapterType;
+ enum BusLogic_HostAdapterBusType HostAdapterBusType;
+ unsigned long IO_Address;
+ unsigned long PCI_Address;
+ struct pci_dev *PCI_Device;
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned char IRQ_Channel;
+};
+
+/*
+ Define the Probe Options.
+*/
+
+struct BusLogic_ProbeOptions {
+ boolean NoProbe:1; /* Bit 0 */
+ boolean NoProbeISA:1; /* Bit 1 */
+ boolean NoProbePCI:1; /* Bit 2 */
+ boolean NoSortPCI:1; /* Bit 3 */
+ boolean MultiMasterFirst:1; /* Bit 4 */
+ boolean FlashPointFirst:1; /* Bit 5 */
+ boolean LimitedProbeISA:1; /* Bit 6 */
+ boolean Probe330:1; /* Bit 7 */
+ boolean Probe334:1; /* Bit 8 */
+ boolean Probe230:1; /* Bit 9 */
+ boolean Probe234:1; /* Bit 10 */
+ boolean Probe130:1; /* Bit 11 */
+ boolean Probe134:1; /* Bit 12 */
+};
+
+/*
+ Define the Global Options.
+*/
+
+struct BusLogic_GlobalOptions {
+ boolean TraceProbe:1; /* Bit 0 */
+ boolean TraceHardwareReset:1; /* Bit 1 */
+ boolean TraceConfiguration:1; /* Bit 2 */
+ boolean TraceErrors:1; /* Bit 3 */
+};
+
+/*
+ Define the Local Options.
+*/
+
+struct BusLogic_LocalOptions {
+ boolean InhibitTargetInquiry:1; /* Bit 0 */
+};
+
+/*
+ Define the BusLogic SCSI Host Adapter I/O Register Offsets.
+*/
+
+#define BusLogic_ControlRegisterOffset 0 /* WO register */
+#define BusLogic_StatusRegisterOffset 0 /* RO register */
+#define BusLogic_CommandParameterRegisterOffset 1 /* WO register */
+#define BusLogic_DataInRegisterOffset 1 /* RO register */
+#define BusLogic_InterruptRegisterOffset 2 /* RO register */
+#define BusLogic_GeometryRegisterOffset 3 /* RO register */
+
+/*
+ Define the structure of the write-only Control Register.
+*/
+
+union BusLogic_ControlRegister {
+ unsigned char All;
+ struct {
+ unsigned char:4; /* Bits 0-3 */
+ boolean SCSIBusReset:1; /* Bit 4 */
+ boolean InterruptReset:1; /* Bit 5 */
+ boolean SoftReset:1; /* Bit 6 */
+ boolean HardReset:1; /* Bit 7 */
+ } cr;
+};
+
+/*
+ Define the structure of the read-only Status Register.
+*/
+
+union BusLogic_StatusRegister {
+ unsigned char All;
+ struct {
+ boolean CommandInvalid:1; /* Bit 0 */
+ boolean Reserved:1; /* Bit 1 */
+ boolean DataInRegisterReady:1; /* Bit 2 */
+ boolean CommandParameterRegisterBusy:1; /* Bit 3 */
+ boolean HostAdapterReady:1; /* Bit 4 */
+ boolean InitializationRequired:1; /* Bit 5 */
+ boolean DiagnosticFailure:1; /* Bit 6 */
+ boolean DiagnosticActive:1; /* Bit 7 */
+ } sr;
+};
+
+/*
+ Define the structure of the read-only Interrupt Register.
+*/
+
+union BusLogic_InterruptRegister {
+ unsigned char All;
+ struct {
+ boolean IncomingMailboxLoaded:1; /* Bit 0 */
+ boolean OutgoingMailboxAvailable:1; /* Bit 1 */
+ boolean CommandComplete:1; /* Bit 2 */
+ boolean ExternalBusReset:1; /* Bit 3 */
+ unsigned char Reserved:3; /* Bits 4-6 */
+ boolean InterruptValid:1; /* Bit 7 */
+ } ir;
+};
+
+/*
+ Define the structure of the read-only Geometry Register.
+*/
+
+union BusLogic_GeometryRegister {
+ unsigned char All;
+ struct {
+ enum BusLogic_BIOS_DiskGeometryTranslation Drive0Geometry:2; /* Bits 0-1 */
+ enum BusLogic_BIOS_DiskGeometryTranslation Drive1Geometry:2; /* Bits 2-3 */
+ unsigned char:3; /* Bits 4-6 */
+ boolean ExtendedTranslationEnabled:1; /* Bit 7 */
+ } gr;
+};
+
+/*
+ Define the BusLogic SCSI Host Adapter Command Register Operation Codes.
+*/
+
+enum BusLogic_OperationCode {
+ BusLogic_TestCommandCompleteInterrupt = 0x00,
+ BusLogic_InitializeMailbox = 0x01,
+ BusLogic_ExecuteMailboxCommand = 0x02,
+ BusLogic_ExecuteBIOSCommand = 0x03,
+ BusLogic_InquireBoardID = 0x04,
+ BusLogic_EnableOutgoingMailboxAvailableInt = 0x05,
+ BusLogic_SetSCSISelectionTimeout = 0x06,
+ BusLogic_SetPreemptTimeOnBus = 0x07,
+ BusLogic_SetTimeOffBus = 0x08,
+ BusLogic_SetBusTransferRate = 0x09,
+ BusLogic_InquireInstalledDevicesID0to7 = 0x0A,
+ BusLogic_InquireConfiguration = 0x0B,
+ BusLogic_EnableTargetMode = 0x0C,
+ BusLogic_InquireSetupInformation = 0x0D,
+ BusLogic_WriteAdapterLocalRAM = 0x1A,
+ BusLogic_ReadAdapterLocalRAM = 0x1B,
+ BusLogic_WriteBusMasterChipFIFO = 0x1C,
+ BusLogic_ReadBusMasterChipFIFO = 0x1D,
+ BusLogic_EchoCommandData = 0x1F,
+ BusLogic_HostAdapterDiagnostic = 0x20,
+ BusLogic_SetAdapterOptions = 0x21,
+ BusLogic_InquireInstalledDevicesID8to15 = 0x23,
+ BusLogic_InquireTargetDevices = 0x24,
+ BusLogic_DisableHostAdapterInterrupt = 0x25,
+ BusLogic_InitializeExtendedMailbox = 0x81,
+ BusLogic_ExecuteSCSICommand = 0x83,
+ BusLogic_InquireFirmwareVersion3rdDigit = 0x84,
+ BusLogic_InquireFirmwareVersionLetter = 0x85,
+ BusLogic_InquirePCIHostAdapterInformation = 0x86,
+ BusLogic_InquireHostAdapterModelNumber = 0x8B,
+ BusLogic_InquireSynchronousPeriod = 0x8C,
+ BusLogic_InquireExtendedSetupInformation = 0x8D,
+ BusLogic_EnableStrictRoundRobinMode = 0x8F,
+ BusLogic_StoreHostAdapterLocalRAM = 0x90,
+ BusLogic_FetchHostAdapterLocalRAM = 0x91,
+ BusLogic_StoreLocalDataInEEPROM = 0x92,
+ BusLogic_UploadAutoSCSICode = 0x94,
+ BusLogic_ModifyIOAddress = 0x95,
+ BusLogic_SetCCBFormat = 0x96,
+ BusLogic_WriteInquiryBuffer = 0x9A,
+ BusLogic_ReadInquiryBuffer = 0x9B,
+ BusLogic_FlashROMUploadDownload = 0xA7,
+ BusLogic_ReadSCAMData = 0xA8,
+ BusLogic_WriteSCAMData = 0xA9
+};
+
+/*
+ Define the Inquire Board ID reply structure.
+*/
+
+struct BusLogic_BoardID {
+ unsigned char BoardType; /* Byte 0 */
+ unsigned char CustomFeatures; /* Byte 1 */
+ unsigned char FirmwareVersion1stDigit; /* Byte 2 */
+ unsigned char FirmwareVersion2ndDigit; /* Byte 3 */
+};
+
+/*
+ Define the Inquire Configuration reply structure.
+*/
+
+struct BusLogic_Configuration {
+ unsigned char:5; /* Byte 0 Bits 0-4 */
+ boolean DMA_Channel5:1; /* Byte 0 Bit 5 */
+ boolean DMA_Channel6:1; /* Byte 0 Bit 6 */
+ boolean DMA_Channel7:1; /* Byte 0 Bit 7 */
+ boolean IRQ_Channel9:1; /* Byte 1 Bit 0 */
+ boolean IRQ_Channel10:1; /* Byte 1 Bit 1 */
+ boolean IRQ_Channel11:1; /* Byte 1 Bit 2 */
+ boolean IRQ_Channel12:1; /* Byte 1 Bit 3 */
+ unsigned char:1; /* Byte 1 Bit 4 */
+ boolean IRQ_Channel14:1; /* Byte 1 Bit 5 */
+ boolean IRQ_Channel15:1; /* Byte 1 Bit 6 */
+ unsigned char:1; /* Byte 1 Bit 7 */
+ unsigned char HostAdapterID:4; /* Byte 2 Bits 0-3 */
+ unsigned char:4; /* Byte 2 Bits 4-7 */
+};
+
+/*
+ Define the Inquire Setup Information reply structure.
+*/
+
+struct BusLogic_SynchronousValue {
+ unsigned char Offset:4; /* Bits 0-3 */
+ unsigned char TransferPeriod:3; /* Bits 4-6 */
+ boolean Synchronous:1; /* Bit 7 */
+};
+
+struct BusLogic_SetupInformation {
+ boolean SynchronousInitiationEnabled:1; /* Byte 0 Bit 0 */
+ boolean ParityCheckingEnabled:1; /* Byte 0 Bit 1 */
+ unsigned char:6; /* Byte 0 Bits 2-7 */
+ unsigned char BusTransferRate; /* Byte 1 */
+ unsigned char PreemptTimeOnBus; /* Byte 2 */
+ unsigned char TimeOffBus; /* Byte 3 */
+ unsigned char MailboxCount; /* Byte 4 */
+ unsigned char MailboxAddress[3]; /* Bytes 5-7 */
+ struct BusLogic_SynchronousValue SynchronousValuesID0to7[8]; /* Bytes 8-15 */
+ unsigned char DisconnectPermittedID0to7; /* Byte 16 */
+ unsigned char Signature; /* Byte 17 */
+ unsigned char CharacterD; /* Byte 18 */
+ unsigned char HostBusType; /* Byte 19 */
+ unsigned char WideTransfersPermittedID0to7; /* Byte 20 */
+ unsigned char WideTransfersActiveID0to7; /* Byte 21 */
+ struct BusLogic_SynchronousValue SynchronousValuesID8to15[8]; /* Bytes 22-29 */
+ unsigned char DisconnectPermittedID8to15; /* Byte 30 */
+ unsigned char:8; /* Byte 31 */
+ unsigned char WideTransfersPermittedID8to15; /* Byte 32 */
+ unsigned char WideTransfersActiveID8to15; /* Byte 33 */
+};
+
+/*
+ Define the Initialize Extended Mailbox request structure.
+*/
+
+struct BusLogic_ExtendedMailboxRequest {
+ unsigned char MailboxCount; /* Byte 0 */
+ u32 BaseMailboxAddress; /* Bytes 1-4 */
+} PACKED;
+
+
+/*
+ Define the Inquire PCI Host Adapter Information reply type. The ISA
+ Compatible I/O Port values are defined here and are also used with
+ the Modify I/O Address command.
+*/
+
+enum BusLogic_ISACompatibleIOPort {
+ BusLogic_IO_330 = 0,
+ BusLogic_IO_334 = 1,
+ BusLogic_IO_230 = 2,
+ BusLogic_IO_234 = 3,
+ BusLogic_IO_130 = 4,
+ BusLogic_IO_134 = 5,
+ BusLogic_IO_Disable = 6,
+ BusLogic_IO_Disable2 = 7
+} PACKED;
+
+struct BusLogic_PCIHostAdapterInformation {
+ enum BusLogic_ISACompatibleIOPort ISACompatibleIOPort; /* Byte 0 */
+ unsigned char PCIAssignedIRQChannel; /* Byte 1 */
+ boolean LowByteTerminated:1; /* Byte 2 Bit 0 */
+ boolean HighByteTerminated:1; /* Byte 2 Bit 1 */
+ unsigned char:2; /* Byte 2 Bits 2-3 */
+ boolean JP1:1; /* Byte 2 Bit 4 */
+ boolean JP2:1; /* Byte 2 Bit 5 */
+ boolean JP3:1; /* Byte 2 Bit 6 */
+ boolean GenericInfoValid:1; /* Byte 2 Bit 7 */
+ unsigned char:8; /* Byte 3 */
+};
+
+/*
+ Define the Inquire Extended Setup Information reply structure.
+*/
+
+struct BusLogic_ExtendedSetupInformation {
+ unsigned char BusType; /* Byte 0 */
+ unsigned char BIOS_Address; /* Byte 1 */
+ unsigned short ScatterGatherLimit; /* Bytes 2-3 */
+ unsigned char MailboxCount; /* Byte 4 */
+ u32 BaseMailboxAddress; /* Bytes 5-8 */
+ struct {
+ unsigned char:2; /* Byte 9 Bits 0-1 */
+ boolean FastOnEISA:1; /* Byte 9 Bit 2 */
+ unsigned char:3; /* Byte 9 Bits 3-5 */
+ boolean LevelSensitiveInterrupt:1; /* Byte 9 Bit 6 */
+ unsigned char:1; /* Byte 9 Bit 7 */
+ } Misc;
+ unsigned char FirmwareRevision[3]; /* Bytes 10-12 */
+ boolean HostWideSCSI:1; /* Byte 13 Bit 0 */
+ boolean HostDifferentialSCSI:1; /* Byte 13 Bit 1 */
+ boolean HostSupportsSCAM:1; /* Byte 13 Bit 2 */
+ boolean HostUltraSCSI:1; /* Byte 13 Bit 3 */
+ boolean HostSmartTermination:1; /* Byte 13 Bit 4 */
+ unsigned char:3; /* Byte 13 Bits 5-7 */
+} PACKED;
+
+/*
+ Define the Enable Strict Round Robin Mode request type.
+*/
+
+enum BusLogic_RoundRobinModeRequest {
+ BusLogic_AggressiveRoundRobinMode = 0,
+ BusLogic_StrictRoundRobinMode = 1
+} PACKED;
+
+
+/*
+ Define the Fetch Host Adapter Local RAM request type.
+*/
+
+#define BusLogic_BIOS_BaseOffset 0
+#define BusLogic_AutoSCSI_BaseOffset 64
+
+struct BusLogic_FetchHostAdapterLocalRAMRequest {
+ unsigned char ByteOffset; /* Byte 0 */
+ unsigned char ByteCount; /* Byte 1 */
+};
+
+/*
+ Define the Host Adapter Local RAM AutoSCSI structure.
+*/
+
+struct BusLogic_AutoSCSIData {
+ unsigned char InternalFactorySignature[2]; /* Bytes 0-1 */
+ unsigned char InformationByteCount; /* Byte 2 */
+ unsigned char HostAdapterType[6]; /* Bytes 3-8 */
+ unsigned char:8; /* Byte 9 */
+ boolean FloppyEnabled:1; /* Byte 10 Bit 0 */
+ boolean FloppySecondary:1; /* Byte 10 Bit 1 */
+ boolean LevelSensitiveInterrupt:1; /* Byte 10 Bit 2 */
+ unsigned char:2; /* Byte 10 Bits 3-4 */
+ unsigned char SystemRAMAreaForBIOS:3; /* Byte 10 Bits 5-7 */
+ unsigned char DMA_Channel:7; /* Byte 11 Bits 0-6 */
+ boolean DMA_AutoConfiguration:1; /* Byte 11 Bit 7 */
+ unsigned char IRQ_Channel:7; /* Byte 12 Bits 0-6 */
+ boolean IRQ_AutoConfiguration:1; /* Byte 12 Bit 7 */
+ unsigned char DMA_TransferRate; /* Byte 13 */
+ unsigned char SCSI_ID; /* Byte 14 */
+ boolean LowByteTerminated:1; /* Byte 15 Bit 0 */
+ boolean ParityCheckingEnabled:1; /* Byte 15 Bit 1 */
+ boolean HighByteTerminated:1; /* Byte 15 Bit 2 */
+ boolean NoisyCablingEnvironment:1; /* Byte 15 Bit 3 */
+ boolean FastSynchronousNegotiation:1; /* Byte 15 Bit 4 */
+ boolean BusResetEnabled:1; /* Byte 15 Bit 5 */
+ boolean:1; /* Byte 15 Bit 6 */
+ boolean ActiveNegationEnabled:1; /* Byte 15 Bit 7 */
+ unsigned char BusOnDelay; /* Byte 16 */
+ unsigned char BusOffDelay; /* Byte 17 */
+ boolean HostAdapterBIOSEnabled:1; /* Byte 18 Bit 0 */
+ boolean BIOSRedirectionOfINT19Enabled:1; /* Byte 18 Bit 1 */
+ boolean ExtendedTranslationEnabled:1; /* Byte 18 Bit 2 */
+ boolean MapRemovableAsFixedEnabled:1; /* Byte 18 Bit 3 */
+ boolean:1; /* Byte 18 Bit 4 */
+ boolean BIOSSupportsMoreThan2DrivesEnabled:1; /* Byte 18 Bit 5 */
+ boolean BIOSInterruptModeEnabled:1; /* Byte 18 Bit 6 */
+ boolean FlopticalSupportEnabled:1; /* Byte 19 Bit 7 */
+ unsigned short DeviceEnabled; /* Bytes 19-20 */
+ unsigned short WidePermitted; /* Bytes 21-22 */
+ unsigned short FastPermitted; /* Bytes 23-24 */
+ unsigned short SynchronousPermitted; /* Bytes 25-26 */
+ unsigned short DisconnectPermitted; /* Bytes 27-28 */
+ unsigned short SendStartUnitCommand; /* Bytes 29-30 */
+ unsigned short IgnoreInBIOSScan; /* Bytes 31-32 */
+ unsigned char PCIInterruptPin:2; /* Byte 33 Bits 0-1 */
+ unsigned char HostAdapterIOPortAddress:2; /* Byte 33 Bits 2-3 */
+ boolean StrictRoundRobinModeEnabled:1; /* Byte 33 Bit 4 */
+ boolean VESABusSpeedGreaterThan33MHz:1; /* Byte 33 Bit 5 */
+ boolean VESABurstWriteEnabled:1; /* Byte 33 Bit 6 */
+ boolean VESABurstReadEnabled:1; /* Byte 33 Bit 7 */
+ unsigned short UltraPermitted; /* Bytes 34-35 */
+ unsigned int:32; /* Bytes 36-39 */
+ unsigned char:8; /* Byte 40 */
+ unsigned char AutoSCSIMaximumLUN; /* Byte 41 */
+ boolean:1; /* Byte 42 Bit 0 */
+ boolean SCAM_Dominant:1; /* Byte 42 Bit 1 */
+ boolean SCAM_Enabled:1; /* Byte 42 Bit 2 */
+ boolean SCAM_Level2:1; /* Byte 42 Bit 3 */
+ unsigned char:4; /* Byte 42 Bits 4-7 */
+ boolean INT13ExtensionEnabled:1; /* Byte 43 Bit 0 */
+ boolean:1; /* Byte 43 Bit 1 */
+ boolean CDROMBootEnabled:1; /* Byte 43 Bit 2 */
+ unsigned char:5; /* Byte 43 Bits 3-7 */
+ unsigned char BootTargetID:4; /* Byte 44 Bits 0-3 */
+ unsigned char BootChannel:4; /* Byte 44 Bits 4-7 */
+ unsigned char ForceBusDeviceScanningOrder:1; /* Byte 45 Bit 0 */
+ unsigned char:7; /* Byte 45 Bits 1-7 */
+ unsigned short NonTaggedToAlternateLUNPermitted; /* Bytes 46-47 */
+ unsigned short RenegotiateSyncAfterCheckCondition; /* Bytes 48-49 */
+ unsigned char Reserved[10]; /* Bytes 50-59 */
+ unsigned char ManufacturingDiagnostic[2]; /* Bytes 60-61 */
+ unsigned short Checksum; /* Bytes 62-63 */
+} PACKED;
+
+/*
+ Define the Host Adapter Local RAM Auto SCSI Byte 45 structure.
+*/
+
+struct BusLogic_AutoSCSIByte45 {
+ unsigned char ForceBusDeviceScanningOrder:1; /* Bit 0 */
+ unsigned char:7; /* Bits 1-7 */
+};
+
+/*
+ Define the Host Adapter Local RAM BIOS Drive Map Byte structure.
+*/
+
+#define BusLogic_BIOS_DriveMapOffset 17
+
+struct BusLogic_BIOSDriveMapByte {
+ unsigned char TargetIDBit3:1; /* Bit 0 */
+ unsigned char:2; /* Bits 1-2 */
+ enum BusLogic_BIOS_DiskGeometryTranslation DiskGeometry:2; /* Bits 3-4 */
+ unsigned char TargetID:3; /* Bits 5-7 */
+};
+
+/*
+ Define the Set CCB Format request type. Extended LUN Format CCBs are
+ necessary to support more than 8 Logical Units per Target Device.
+*/
+
+enum BusLogic_SetCCBFormatRequest {
+ BusLogic_LegacyLUNFormatCCB = 0,
+ BusLogic_ExtendedLUNFormatCCB = 1
+} PACKED;
+
+/*
+ Define the Outgoing Mailbox Action Codes.
+*/
+
+enum BusLogic_ActionCode {
+ BusLogic_OutgoingMailboxFree = 0x00,
+ BusLogic_MailboxStartCommand = 0x01,
+ BusLogic_MailboxAbortCommand = 0x02
+} PACKED;
+
+
+/*
+ Define the Incoming Mailbox Completion Codes. The MultiMaster Firmware
+ only uses codes 0 - 4. The FlashPoint SCCB Manager has no mailboxes, so
+ completion codes are stored in the CCB; it only uses codes 1, 2, 4, and 5.
+*/
+
+enum BusLogic_CompletionCode {
+ BusLogic_IncomingMailboxFree = 0x00,
+ BusLogic_CommandCompletedWithoutError = 0x01,
+ BusLogic_CommandAbortedAtHostRequest = 0x02,
+ BusLogic_AbortedCommandNotFound = 0x03,
+ BusLogic_CommandCompletedWithError = 0x04,
+ BusLogic_InvalidCCB = 0x05
+} PACKED;
+
+/*
+ Define the Command Control Block (CCB) Opcodes.
+*/
+
+enum BusLogic_CCB_Opcode {
+ BusLogic_InitiatorCCB = 0x00,
+ BusLogic_TargetCCB = 0x01,
+ BusLogic_InitiatorCCB_ScatterGather = 0x02,
+ BusLogic_InitiatorCCB_ResidualDataLength = 0x03,
+ BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04,
+ BusLogic_BusDeviceReset = 0x81
+} PACKED;
+
+
+/*
+ Define the CCB Data Direction Codes.
+*/
+
+enum BusLogic_DataDirection {
+ BusLogic_UncheckedDataTransfer = 0,
+ BusLogic_DataInLengthChecked = 1,
+ BusLogic_DataOutLengthChecked = 2,
+ BusLogic_NoDataTransfer = 3
+};
+
+
+/*
+ Define the Host Adapter Status Codes. The MultiMaster Firmware does not
+ return status code 0x0C; it uses 0x12 for both overruns and underruns.
+*/
+
+enum BusLogic_HostAdapterStatus {
+ BusLogic_CommandCompletedNormally = 0x00,
+ BusLogic_LinkedCommandCompleted = 0x0A,
+ BusLogic_LinkedCommandCompletedWithFlag = 0x0B,
+ BusLogic_DataUnderRun = 0x0C,
+ BusLogic_SCSISelectionTimeout = 0x11,
+ BusLogic_DataOverRun = 0x12,
+ BusLogic_UnexpectedBusFree = 0x13,
+ BusLogic_InvalidBusPhaseRequested = 0x14,
+ BusLogic_InvalidOutgoingMailboxActionCode = 0x15,
+ BusLogic_InvalidCommandOperationCode = 0x16,
+ BusLogic_LinkedCCBhasInvalidLUN = 0x17,
+ BusLogic_InvalidCommandParameter = 0x1A,
+ BusLogic_AutoRequestSenseFailed = 0x1B,
+ BusLogic_TaggedQueuingMessageRejected = 0x1C,
+ BusLogic_UnsupportedMessageReceived = 0x1D,
+ BusLogic_HostAdapterHardwareFailed = 0x20,
+ BusLogic_TargetFailedResponseToATN = 0x21,
+ BusLogic_HostAdapterAssertedRST = 0x22,
+ BusLogic_OtherDeviceAssertedRST = 0x23,
+ BusLogic_TargetDeviceReconnectedImproperly = 0x24,
+ BusLogic_HostAdapterAssertedBusDeviceReset = 0x25,
+ BusLogic_AbortQueueGenerated = 0x26,
+ BusLogic_HostAdapterSoftwareError = 0x27,
+ BusLogic_HostAdapterHardwareTimeoutError = 0x30,
+ BusLogic_SCSIParityErrorDetected = 0x34
+} PACKED;
+
+
+/*
+ Define the SCSI Target Device Status Codes.
+*/
+
+enum BusLogic_TargetDeviceStatus {
+ BusLogic_OperationGood = 0x00,
+ BusLogic_CheckCondition = 0x02,
+ BusLogic_DeviceBusy = 0x08
+} PACKED;
+
+/*
+ Define the Queue Tag Codes.
+*/
+
+enum BusLogic_QueueTag {
+ BusLogic_SimpleQueueTag = 0,
+ BusLogic_HeadOfQueueTag = 1,
+ BusLogic_OrderedQueueTag = 2,
+ BusLogic_ReservedQT = 3
+};
+
+/*
+ Define the SCSI Command Descriptor Block (CDB).
+*/
+
+#define BusLogic_CDB_MaxLength 12
+
+typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
+
+
+/*
+ Define the Scatter/Gather Segment structure required by the MultiMaster
+ Firmware Interface and the FlashPoint SCCB Manager.
+*/
+
+struct BusLogic_ScatterGatherSegment {
+ u32 SegmentByteCount; /* Bytes 0-3 */
+ u32 SegmentDataPointer; /* Bytes 4-7 */
+};
+
+/*
+ Define the Driver CCB Status Codes.
+*/
+
+enum BusLogic_CCB_Status {
+ BusLogic_CCB_Free = 0,
+ BusLogic_CCB_Active = 1,
+ BusLogic_CCB_Completed = 2,
+ BusLogic_CCB_Reset = 3
+} PACKED;
+
+
+/*
+ Define the 32 Bit Mode Command Control Block (CCB) structure. The first 40
+ bytes are defined by and common to both the MultiMaster Firmware and the
+ FlashPoint SCCB Manager. The next 60 bytes are defined by the FlashPoint
+ SCCB Manager. The remaining components are defined by the Linux BusLogic
+ Driver. Extended LUN Format CCBs differ from Legacy LUN Format 32 Bit Mode
+ CCBs only in having the TagEnable and QueueTag fields moved from byte 17 to
+ byte 1, and the Logical Unit field in byte 17 expanded to 6 bits. In theory,
+ Extended LUN Format CCBs can support up to 64 Logical Units, but in practice
+ many devices will respond improperly to Logical Units between 32 and 63, and
+ the SCSI-2 specification defines Bit 5 as LUNTAR. Extended LUN Format CCBs
+ are used by recent versions of the MultiMaster Firmware, as well as by the
+ FlashPoint SCCB Manager; the FlashPoint SCCB Manager only supports 32 Logical
+ Units. Since 64 Logical Units are unlikely to be needed in practice, and
+ since they are problematic for the above reasons, and since limiting them to
+ 5 bits simplifies the CCB structure definition, this driver only supports
+ 32 Logical Units per Target Device.
+*/
+
+struct BusLogic_CCB {
+ /*
+ MultiMaster Firmware and FlashPoint SCCB Manager Common Portion.
+ */
+ enum BusLogic_CCB_Opcode Opcode; /* Byte 0 */
+ unsigned char:3; /* Byte 1 Bits 0-2 */
+ enum BusLogic_DataDirection DataDirection:2; /* Byte 1 Bits 3-4 */
+ boolean TagEnable:1; /* Byte 1 Bit 5 */
+ enum BusLogic_QueueTag QueueTag:2; /* Byte 1 Bits 6-7 */
+ unsigned char CDB_Length; /* Byte 2 */
+ unsigned char SenseDataLength; /* Byte 3 */
+ u32 DataLength; /* Bytes 4-7 */
+ u32 DataPointer; /* Bytes 8-11 */
+ unsigned char:8; /* Byte 12 */
+ unsigned char:8; /* Byte 13 */
+ enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 14 */
+ enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 15 */
+ unsigned char TargetID; /* Byte 16 */
+ unsigned char LogicalUnit:5; /* Byte 17 Bits 0-4 */
+ boolean LegacyTagEnable:1; /* Byte 17 Bit 5 */
+ enum BusLogic_QueueTag LegacyQueueTag:2; /* Byte 17 Bits 6-7 */
+ SCSI_CDB_T CDB; /* Bytes 18-29 */
+ unsigned char:8; /* Byte 30 */
+ unsigned char:8; /* Byte 31 */
+ unsigned int:32; /* Bytes 32-35 */
+ u32 SenseDataPointer; /* Bytes 36-39 */
+ /*
+ FlashPoint SCCB Manager Defined Portion.
+ */
+ void (*CallbackFunction) (struct BusLogic_CCB *); /* Bytes 40-43 */
+ u32 BaseAddress; /* Bytes 44-47 */
+ enum BusLogic_CompletionCode CompletionCode; /* Byte 48 */
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+ unsigned char:8; /* Byte 49 */
+ unsigned short OS_Flags; /* Bytes 50-51 */
+ unsigned char Private[48]; /* Bytes 52-99 */
+#endif
+ /*
+ BusLogic Linux Driver Defined Portion.
+ */
+ dma_addr_t AllocationGroupHead;
+ unsigned int AllocationGroupSize;
+ u32 DMA_Handle;
+ enum BusLogic_CCB_Status Status;
+ unsigned long SerialNumber;
+ struct scsi_cmnd *Command;
+ struct BusLogic_HostAdapter *HostAdapter;
+ struct BusLogic_CCB *Next;
+ struct BusLogic_CCB *NextAll;
+ struct BusLogic_ScatterGatherSegment
+ ScatterGatherList[BusLogic_ScatterGatherLimit];
+};
+
+/*
+ Define the 32 Bit Mode Outgoing Mailbox structure.
+*/
+
+struct BusLogic_OutgoingMailbox {
+ u32 CCB; /* Bytes 0-3 */
+ unsigned int:24; /* Bytes 4-6 */
+ enum BusLogic_ActionCode ActionCode; /* Byte 7 */
+};
+
+/*
+ Define the 32 Bit Mode Incoming Mailbox structure.
+*/
+
+struct BusLogic_IncomingMailbox {
+ u32 CCB; /* Bytes 0-3 */
+ enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 4 */
+ enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 5 */
+ unsigned char:8; /* Byte 6 */
+ enum BusLogic_CompletionCode CompletionCode; /* Byte 7 */
+};
+
+
+/*
+ Define the BusLogic Driver Options structure.
+*/
+
+struct BusLogic_DriverOptions {
+ unsigned short TaggedQueuingPermitted;
+ unsigned short TaggedQueuingPermittedMask;
+ unsigned short BusSettleTime;
+ struct BusLogic_LocalOptions LocalOptions;
+ unsigned char CommonQueueDepth;
+ unsigned char QueueDepth[BusLogic_MaxTargetDevices];
+};
+
+/*
+ Define the Host Adapter Target Flags structure.
+*/
+
+struct BusLogic_TargetFlags {
+ boolean TargetExists:1;
+ boolean TaggedQueuingSupported:1;
+ boolean WideTransfersSupported:1;
+ boolean TaggedQueuingActive:1;
+ boolean WideTransfersActive:1;
+ boolean CommandSuccessfulFlag:1;
+ boolean TargetInfoReported:1;
+};
+
+/*
+ Define the Host Adapter Target Statistics structure.
+*/
+
+#define BusLogic_SizeBuckets 10
+
+typedef unsigned int BusLogic_CommandSizeBuckets_T[BusLogic_SizeBuckets];
+
+struct BusLogic_TargetStatistics {
+ unsigned int CommandsAttempted;
+ unsigned int CommandsCompleted;
+ unsigned int ReadCommands;
+ unsigned int WriteCommands;
+ struct BusLogic_ByteCounter TotalBytesRead;
+ struct BusLogic_ByteCounter TotalBytesWritten;
+ BusLogic_CommandSizeBuckets_T ReadCommandSizeBuckets;
+ BusLogic_CommandSizeBuckets_T WriteCommandSizeBuckets;
+ unsigned short CommandAbortsRequested;
+ unsigned short CommandAbortsAttempted;
+ unsigned short CommandAbortsCompleted;
+ unsigned short BusDeviceResetsRequested;
+ unsigned short BusDeviceResetsAttempted;
+ unsigned short BusDeviceResetsCompleted;
+ unsigned short HostAdapterResetsRequested;
+ unsigned short HostAdapterResetsAttempted;
+ unsigned short HostAdapterResetsCompleted;
+};
+
+/*
+ Define the FlashPoint Card Handle data type.
+*/
+
+#define FlashPoint_BadCardHandle 0xFFFFFFFF
+
+typedef unsigned int FlashPoint_CardHandle_T;
+
+
+/*
+ Define the FlashPoint Information structure. This structure is defined
+ by the FlashPoint SCCB Manager.
+*/
+
+struct FlashPoint_Info {
+ u32 BaseAddress; /* Bytes 0-3 */
+ boolean Present; /* Byte 4 */
+ unsigned char IRQ_Channel; /* Byte 5 */
+ unsigned char SCSI_ID; /* Byte 6 */
+ unsigned char SCSI_LUN; /* Byte 7 */
+ unsigned short FirmwareRevision; /* Bytes 8-9 */
+ unsigned short SynchronousPermitted; /* Bytes 10-11 */
+ unsigned short FastPermitted; /* Bytes 12-13 */
+ unsigned short UltraPermitted; /* Bytes 14-15 */
+ unsigned short DisconnectPermitted; /* Bytes 16-17 */
+ unsigned short WidePermitted; /* Bytes 18-19 */
+ boolean ParityCheckingEnabled:1; /* Byte 20 Bit 0 */
+ boolean HostWideSCSI:1; /* Byte 20 Bit 1 */
+ boolean HostSoftReset:1; /* Byte 20 Bit 2 */
+ boolean ExtendedTranslationEnabled:1; /* Byte 20 Bit 3 */
+ boolean LowByteTerminated:1; /* Byte 20 Bit 4 */
+ boolean HighByteTerminated:1; /* Byte 20 Bit 5 */
+ boolean ReportDataUnderrun:1; /* Byte 20 Bit 6 */
+ boolean SCAM_Enabled:1; /* Byte 20 Bit 7 */
+ boolean SCAM_Level2:1; /* Byte 21 Bit 0 */
+ unsigned char:7; /* Byte 21 Bits 1-7 */
+ unsigned char Family; /* Byte 22 */
+ unsigned char BusType; /* Byte 23 */
+ unsigned char ModelNumber[3]; /* Bytes 24-26 */
+ unsigned char RelativeCardNumber; /* Byte 27 */
+ unsigned char Reserved[4]; /* Bytes 28-31 */
+ unsigned int OS_Reserved; /* Bytes 32-35 */
+ unsigned char TranslationInfo[4]; /* Bytes 36-39 */
+ unsigned int Reserved2[5]; /* Bytes 40-59 */
+ unsigned int SecondaryRange; /* Bytes 60-63 */
+};
+
+/*
+ Define the BusLogic Driver Host Adapter structure.
+*/
+
+struct BusLogic_HostAdapter {
+ struct Scsi_Host *SCSI_Host;
+ struct pci_dev *PCI_Device;
+ enum BusLogic_HostAdapterType HostAdapterType;
+ enum BusLogic_HostAdapterBusType HostAdapterBusType;
+ unsigned long IO_Address;
+ unsigned long PCI_Address;
+ unsigned short AddressCount;
+ unsigned char HostNumber;
+ unsigned char ModelName[9];
+ unsigned char FirmwareVersion[6];
+ unsigned char FullModelName[18];
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned char IRQ_Channel;
+ unsigned char DMA_Channel;
+ unsigned char SCSI_ID;
+ boolean IRQ_ChannelAcquired:1;
+ boolean DMA_ChannelAcquired:1;
+ boolean ExtendedTranslationEnabled:1;
+ boolean ParityCheckingEnabled:1;
+ boolean BusResetEnabled:1;
+ boolean LevelSensitiveInterrupt:1;
+ boolean HostWideSCSI:1;
+ boolean HostDifferentialSCSI:1;
+ boolean HostSupportsSCAM:1;
+ boolean HostUltraSCSI:1;
+ boolean ExtendedLUNSupport:1;
+ boolean TerminationInfoValid:1;
+ boolean LowByteTerminated:1;
+ boolean HighByteTerminated:1;
+ boolean BounceBuffersRequired:1;
+ boolean StrictRoundRobinModeSupport:1;
+ boolean SCAM_Enabled:1;
+ boolean SCAM_Level2:1;
+ boolean HostAdapterInitialized:1;
+ boolean HostAdapterExternalReset:1;
+ boolean HostAdapterInternalError:1;
+ boolean ProcessCompletedCCBsActive;
+ volatile boolean HostAdapterCommandCompleted;
+ unsigned short HostAdapterScatterGatherLimit;
+ unsigned short DriverScatterGatherLimit;
+ unsigned short MaxTargetDevices;
+ unsigned short MaxLogicalUnits;
+ unsigned short MailboxCount;
+ unsigned short InitialCCBs;
+ unsigned short IncrementalCCBs;
+ unsigned short AllocatedCCBs;
+ unsigned short DriverQueueDepth;
+ unsigned short HostAdapterQueueDepth;
+ unsigned short UntaggedQueueDepth;
+ unsigned short CommonQueueDepth;
+ unsigned short BusSettleTime;
+ unsigned short SynchronousPermitted;
+ unsigned short FastPermitted;
+ unsigned short UltraPermitted;
+ unsigned short WidePermitted;
+ unsigned short DisconnectPermitted;
+ unsigned short TaggedQueuingPermitted;
+ unsigned short ExternalHostAdapterResets;
+ unsigned short HostAdapterInternalErrors;
+ unsigned short TargetDeviceCount;
+ unsigned short MessageBufferLength;
+ u32 BIOS_Address;
+ struct BusLogic_DriverOptions *DriverOptions;
+ struct FlashPoint_Info FlashPointInfo;
+ FlashPoint_CardHandle_T CardHandle;
+ struct list_head host_list;
+ struct BusLogic_CCB *All_CCBs;
+ struct BusLogic_CCB *Free_CCBs;
+ struct BusLogic_CCB *FirstCompletedCCB;
+ struct BusLogic_CCB *LastCompletedCCB;
+ struct BusLogic_CCB *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices];
+ struct BusLogic_TargetFlags TargetFlags[BusLogic_MaxTargetDevices];
+ unsigned char QueueDepth[BusLogic_MaxTargetDevices];
+ unsigned char SynchronousPeriod[BusLogic_MaxTargetDevices];
+ unsigned char SynchronousOffset[BusLogic_MaxTargetDevices];
+ unsigned char ActiveCommands[BusLogic_MaxTargetDevices];
+ unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices];
+ unsigned long LastSequencePoint[BusLogic_MaxTargetDevices];
+ unsigned long LastResetAttempted[BusLogic_MaxTargetDevices];
+ unsigned long LastResetCompleted[BusLogic_MaxTargetDevices];
+ struct BusLogic_OutgoingMailbox *FirstOutgoingMailbox;
+ struct BusLogic_OutgoingMailbox *LastOutgoingMailbox;
+ struct BusLogic_OutgoingMailbox *NextOutgoingMailbox;
+ struct BusLogic_IncomingMailbox *FirstIncomingMailbox;
+ struct BusLogic_IncomingMailbox *LastIncomingMailbox;
+ struct BusLogic_IncomingMailbox *NextIncomingMailbox;
+ struct BusLogic_TargetStatistics TargetStatistics[BusLogic_MaxTargetDevices];
+ unsigned char *MailboxSpace;
+ dma_addr_t MailboxSpaceHandle;
+ unsigned int MailboxSize;
+ unsigned long CCB_Offset;
+ char MessageBuffer[BusLogic_MessageBufferSize];
+};
+
+/*
+ Define a structure for the BIOS Disk Parameters.
+*/
+
+struct BIOS_DiskParameters {
+ int Heads;
+ int Sectors;
+ int Cylinders;
+};
+
+/*
+ Define a structure for the SCSI Inquiry command results.
+*/
+
+struct SCSI_Inquiry {
+ unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */
+ unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */
+ unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */
+ boolean RMB:1; /* Byte 1 Bit 7 */
+ unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */
+ unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */
+ unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */
+ unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */
+ unsigned char:2; /* Byte 3 Bits 4-5 */
+ boolean TrmIOP:1; /* Byte 3 Bit 6 */
+ boolean AENC:1; /* Byte 3 Bit 7 */
+ unsigned char AdditionalLength; /* Byte 4 */
+ unsigned char:8; /* Byte 5 */
+ unsigned char:8; /* Byte 6 */
+ boolean SftRe:1; /* Byte 7 Bit 0 */
+ boolean CmdQue:1; /* Byte 7 Bit 1 */
+ boolean:1; /* Byte 7 Bit 2 */
+ boolean Linked:1; /* Byte 7 Bit 3 */
+ boolean Sync:1; /* Byte 7 Bit 4 */
+ boolean WBus16:1; /* Byte 7 Bit 5 */
+ boolean WBus32:1; /* Byte 7 Bit 6 */
+ boolean RelAdr:1; /* Byte 7 Bit 7 */
+ unsigned char VendorIdentification[8]; /* Bytes 8-15 */
+ unsigned char ProductIdentification[16]; /* Bytes 16-31 */
+ unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */
+};
+
+
+/*
+ Define functions to provide an abstraction for reading and writing the
+ Host Adapter I/O Registers.
+*/
+
+static inline void BusLogic_SCSIBusReset(struct BusLogic_HostAdapter *HostAdapter)
+{
+ union BusLogic_ControlRegister ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.cr.SCSIBusReset = true;
+ outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline void BusLogic_InterruptReset(struct BusLogic_HostAdapter *HostAdapter)
+{
+ union BusLogic_ControlRegister ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.cr.InterruptReset = true;
+ outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline void BusLogic_SoftReset(struct BusLogic_HostAdapter *HostAdapter)
+{
+ union BusLogic_ControlRegister ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.cr.SoftReset = true;
+ outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline void BusLogic_HardReset(struct BusLogic_HostAdapter *HostAdapter)
+{
+ union BusLogic_ControlRegister ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.cr.HardReset = true;
+ outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline unsigned char BusLogic_ReadStatusRegister(struct BusLogic_HostAdapter *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_StatusRegisterOffset);
+}
+
+static inline void BusLogic_WriteCommandParameterRegister(struct BusLogic_HostAdapter
+ *HostAdapter, unsigned char Value)
+{
+ outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegisterOffset);
+}
+
+static inline unsigned char BusLogic_ReadDataInRegister(struct BusLogic_HostAdapter *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_DataInRegisterOffset);
+}
+
+static inline unsigned char BusLogic_ReadInterruptRegister(struct BusLogic_HostAdapter *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_InterruptRegisterOffset);
+}
+
+static inline unsigned char BusLogic_ReadGeometryRegister(struct BusLogic_HostAdapter *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_GeometryRegisterOffset);
+}
+
+/*
+ BusLogic_StartMailboxCommand issues an Execute Mailbox Command, which
+ notifies the Host Adapter that an entry has been made in an Outgoing
+ Mailbox.
+*/
+
+static inline void BusLogic_StartMailboxCommand(struct BusLogic_HostAdapter *HostAdapter)
+{
+ BusLogic_WriteCommandParameterRegister(HostAdapter, BusLogic_ExecuteMailboxCommand);
+}
+
+/*
+ BusLogic_Delay waits for Seconds to elapse.
+*/
+
+static inline void BusLogic_Delay(int Seconds)
+{
+ mdelay(1000 * Seconds);
+}
+
+/*
+ Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
+ and PCI/VLB/EISA/ISA Bus Addresses.
+*/
+
+static inline u32 Virtual_to_Bus(void *VirtualAddress)
+{
+ return (u32) virt_to_bus(VirtualAddress);
+}
+
+static inline void *Bus_to_Virtual(u32 BusAddress)
+{
+ return (void *) bus_to_virt(BusAddress);
+}
+
+/*
+ Virtual_to_32Bit_Virtual maps between Kernel Virtual Addresses and
+ 32 bit Kernel Virtual Addresses. This avoids compilation warnings
+ on 64 bit architectures.
+*/
+
+static inline u32 Virtual_to_32Bit_Virtual(void *VirtualAddress)
+{
+ return (u32) (unsigned long) VirtualAddress;
+}
+
+/*
+ BusLogic_IncrementErrorCounter increments Error Counter by 1, stopping at
+ 65535 rather than wrapping around to 0.
+*/
+
+static inline void BusLogic_IncrementErrorCounter(unsigned short *ErrorCounter)
+{
+ if (*ErrorCounter < 65535)
+ (*ErrorCounter)++;
+}
+
+/*
+ BusLogic_IncrementByteCounter increments Byte Counter by Amount.
+*/
+
+static inline void BusLogic_IncrementByteCounter(struct BusLogic_ByteCounter
+ *ByteCounter, unsigned int Amount)
+{
+ ByteCounter->Units += Amount;
+ if (ByteCounter->Units > 999999999) {
+ ByteCounter->Units -= 1000000000;
+ ByteCounter->Billions++;
+ }
+}
+
+/*
+ BusLogic_IncrementSizeBucket increments the Bucket for Amount.
+*/
+
+static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T CommandSizeBuckets, unsigned int Amount)
+{
+ int Index = 0;
+ if (Amount < 8 * 1024) {
+ if (Amount < 2 * 1024)
+ Index = (Amount < 1 * 1024 ? 0 : 1);
+ else
+ Index = (Amount < 4 * 1024 ? 2 : 3);
+ } else if (Amount < 128 * 1024) {
+ if (Amount < 32 * 1024)
+ Index = (Amount < 16 * 1024 ? 4 : 5);
+ else
+ Index = (Amount < 64 * 1024 ? 6 : 7);
+ } else
+ Index = (Amount < 256 * 1024 ? 8 : 9);
+ CommandSizeBuckets[Index]++;
+}
+
+/*
+ Define the version number of the FlashPoint Firmware (SCCB Manager).
+*/
+
+#define FlashPoint_FirmwareVersion "5.02"
+
+/*
+ Define the possible return values from FlashPoint_HandleInterrupt.
+*/
+
+#define FlashPoint_NormalInterrupt 0x00
+#define FlashPoint_InternalError 0xFE
+#define FlashPoint_ExternalBusReset 0xFF
+
+/*
+ Define prototypes for the forward referenced BusLogic Driver
+ Internal Functions.
+*/
+
+static const char *BusLogic_DriverInfo(struct Scsi_Host *);
+static int BusLogic_QueueCommand(struct scsi_cmnd *, void (*CompletionRoutine) (struct scsi_cmnd *));
+static int BusLogic_BIOSDiskParameters(struct scsi_device *, struct block_device *, sector_t, int *);
+static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *, char *, char **, off_t, int, int);
+static int BusLogic_SlaveConfigure(struct scsi_device *);
+static void BusLogic_QueueCompletedCCB(struct BusLogic_CCB *);
+static irqreturn_t BusLogic_InterruptHandler(int, void *, struct pt_regs *);
+static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *, boolean HardReset);
+static void BusLogic_Message(enum BusLogic_MessageLevel, char *, struct BusLogic_HostAdapter *, ...);
+static int __init BusLogic_Setup(char *);
+
+#endif /* _BUSLOGIC_H */
diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c
new file mode 100644
index 000000000000..56a695c6ab52
--- /dev/null
+++ b/drivers/scsi/FlashPoint.c
@@ -0,0 +1,12159 @@
+/*
+
+ FlashPoint.c -- FlashPoint SCCB Manager for Linux
+
+ This file contains the FlashPoint SCCB Manager from BusLogic's FlashPoint
+ Driver Developer's Kit, with minor modifications by Leonard N. Zubkoff for
+ Linux compatibility. It was provided by BusLogic in the form of 16 separate
+ source files, which would have unnecessarily cluttered the scsi directory, so
+ the individual files have been combined into this single file.
+
+ Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+
+ This file is available under both the GNU General Public License
+ and a BSD-style copyright; see LICENSE.FlashPoint for details.
+
+*/
+
+
+#include <linux/config.h>
+
+
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+
+
+#define UNIX
+#define FW_TYPE _SCCB_MGR_
+#define MAX_CARDS 8
+#undef BUSTYPE_PCI
+
+
+#define OS_InPortByte(port) inb(port)
+#define OS_InPortWord(port) inw(port)
+#define OS_InPortLong(port) inl(port)
+#define OS_OutPortByte(port, value) outb(value, port)
+#define OS_OutPortWord(port, value) outw(value, port)
+#define OS_OutPortLong(port, value) outl(value, port)
+#define OS_Lock(x)
+#define OS_UnLock(x)
+
+
+/*
+ Define name replacements for compatibility with the Linux BusLogic Driver.
+*/
+
+#define SccbMgr_sense_adapter FlashPoint_ProbeHostAdapter
+#define SccbMgr_config_adapter FlashPoint_HardwareResetHostAdapter
+#define SccbMgr_unload_card FlashPoint_ReleaseHostAdapter
+#define SccbMgr_start_sccb FlashPoint_StartCCB
+#define SccbMgr_abort_sccb FlashPoint_AbortCCB
+#define SccbMgr_my_int FlashPoint_InterruptPending
+#define SccbMgr_isr FlashPoint_HandleInterrupt
+
+
+/*
+ Define name replacements to avoid kernel namespace pollution.
+*/
+
+#define BL_Card FPT_BL_Card
+#define BusMasterInit FPT_BusMasterInit
+#define CalcCrc16 FPT_CalcCrc16
+#define CalcLrc FPT_CalcLrc
+#define ChkIfChipInitialized FPT_ChkIfChipInitialized
+#define DiagBusMaster FPT_DiagBusMaster
+#define DiagEEPROM FPT_DiagEEPROM
+#define DiagXbow FPT_DiagXbow
+#define GetTarLun FPT_GetTarLun
+#define RNVRamData FPT_RNVRamData
+#define RdStack FPT_RdStack
+#define SccbMgrTableInitAll FPT_SccbMgrTableInitAll
+#define SccbMgrTableInitCard FPT_SccbMgrTableInitCard
+#define SccbMgrTableInitTarget FPT_SccbMgrTableInitTarget
+#define SccbMgr_bad_isr FPT_SccbMgr_bad_isr
+#define SccbMgr_scsi_reset FPT_SccbMgr_scsi_reset
+#define SccbMgr_timer_expired FPT_SccbMgr_timer_expired
+#define SendMsg FPT_SendMsg
+#define Wait FPT_Wait
+#define Wait1Second FPT_Wait1Second
+#define WrStack FPT_WrStack
+#define XbowInit FPT_XbowInit
+#define autoCmdCmplt FPT_autoCmdCmplt
+#define autoLoadDefaultMap FPT_autoLoadDefaultMap
+#define busMstrDataXferStart FPT_busMstrDataXferStart
+#define busMstrSGDataXferStart FPT_busMstrSGDataXferStart
+#define busMstrTimeOut FPT_busMstrTimeOut
+#define dataXferProcessor FPT_dataXferProcessor
+#define default_intena FPT_default_intena
+#define hostDataXferAbort FPT_hostDataXferAbort
+#define hostDataXferRestart FPT_hostDataXferRestart
+#define inisci FPT_inisci
+#define mbCards FPT_mbCards
+#define nvRamInfo FPT_nvRamInfo
+#define phaseBusFree FPT_phaseBusFree
+#define phaseChkFifo FPT_phaseChkFifo
+#define phaseCommand FPT_phaseCommand
+#define phaseDataIn FPT_phaseDataIn
+#define phaseDataOut FPT_phaseDataOut
+#define phaseDecode FPT_phaseDecode
+#define phaseIllegal FPT_phaseIllegal
+#define phaseMsgIn FPT_phaseMsgIn
+#define phaseMsgOut FPT_phaseMsgOut
+#define phaseStatus FPT_phaseStatus
+#define queueAddSccb FPT_queueAddSccb
+#define queueCmdComplete FPT_queueCmdComplete
+#define queueDisconnect FPT_queueDisconnect
+#define queueFindSccb FPT_queueFindSccb
+#define queueFlushSccb FPT_queueFlushSccb
+#define queueFlushTargSccb FPT_queueFlushTargSccb
+#define queueSearchSelect FPT_queueSearchSelect
+#define queueSelectFail FPT_queueSelectFail
+#define s_PhaseTbl FPT_s_PhaseTbl
+#define scamHAString FPT_scamHAString
+#define scamInfo FPT_scamInfo
+#define scarb FPT_scarb
+#define scasid FPT_scasid
+#define scbusf FPT_scbusf
+#define sccbMgrTbl FPT_sccbMgrTbl
+#define schkdd FPT_schkdd
+#define scini FPT_scini
+#define sciso FPT_sciso
+#define scmachid FPT_scmachid
+#define scsavdi FPT_scsavdi
+#define scsel FPT_scsel
+#define scsell FPT_scsell
+#define scsendi FPT_scsendi
+#define scvalq FPT_scvalq
+#define scwirod FPT_scwirod
+#define scwiros FPT_scwiros
+#define scwtsel FPT_scwtsel
+#define scxferc FPT_scxferc
+#define sdecm FPT_sdecm
+#define sfm FPT_sfm
+#define shandem FPT_shandem
+#define sinits FPT_sinits
+#define sisyncn FPT_sisyncn
+#define sisyncr FPT_sisyncr
+#define siwidn FPT_siwidn
+#define siwidr FPT_siwidr
+#define sres FPT_sres
+#define sresb FPT_sresb
+#define ssel FPT_ssel
+#define ssenss FPT_ssenss
+#define sssyncv FPT_sssyncv
+#define stsyncn FPT_stsyncn
+#define stwidn FPT_stwidn
+#define sxfrp FPT_sxfrp
+#define utilEERead FPT_utilEERead
+#define utilEEReadOrg FPT_utilEEReadOrg
+#define utilEESendCmdAddr FPT_utilEESendCmdAddr
+#define utilEEWrite FPT_utilEEWrite
+#define utilEEWriteOnOff FPT_utilEEWriteOnOff
+#define utilUpdateResidual FPT_utilUpdateResidual
+
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: globals.h $
+ *
+ * Description: Common shared global defines.
+ *
+ * $Date: 1996/09/04 01:26:13 $
+ *
+ * $Revision: 1.11 $
+ *
+ *----------------------------------------------------------------------*/
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#define _UCB_MGR_ 1
+#define _SCCB_MGR_ 2
+
+/*#include <osflags.h>*/
+
+#define MAX_CDBLEN 12
+
+#define SCAM_LEV_2 1
+
+#define CRCMASK 0xA001
+
+/* In your osflags.h file, please ENSURE that only ONE OS FLAG
+ is on at a time !!! Also, please make sure you turn set the
+ variable FW_TYPE to either _UCB_MGR_ or _SCCB_MGR_ !!! */
+
+#if defined(DOS) || defined(WIN95_16) || defined(OS2) || defined(OTHER_16)
+ #define COMPILER_16_BIT 1
+#elif defined(NETWARE) || defined(NT) || defined(WIN95_32) || defined(UNIX) || defined(OTHER_32) || defined(SOLARIS_REAL_MODE)
+ #define COMPILER_32_BIT 1
+#endif
+
+
+#define BL_VENDOR_ID 0x104B
+#define FP_DEVICE_ID 0x8130
+#define MM_DEVICE_ID 0x1040
+
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!(FALSE))
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define FAILURE 0xFFFFFFFFL
+
+
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned int UINT;
+typedef unsigned long ULONG;
+typedef unsigned char * PUCHAR;
+typedef unsigned short* PUSHORT;
+typedef unsigned long * PULONG;
+typedef void * PVOID;
+
+
+#if defined(COMPILER_16_BIT)
+typedef unsigned char far * uchar_ptr;
+typedef unsigned short far * ushort_ptr;
+typedef unsigned long far * ulong_ptr;
+#endif /* 16_BIT_COMPILER */
+
+#if defined(COMPILER_32_BIT)
+typedef unsigned char * uchar_ptr;
+typedef unsigned short * ushort_ptr;
+typedef unsigned long * ulong_ptr;
+#endif /* 32_BIT_COMPILER */
+
+
+/* NEW TYPE DEFINITIONS (shared with Mylex North)
+
+** Use following type defines to avoid confusion in 16 and 32-bit
+** environments. Avoid using 'int' as it denotes 16 bits in 16-bit
+** environment and 32 in 32-bit environments.
+
+*/
+
+#define s08bits char
+#define s16bits short
+#define s32bits long
+
+#define u08bits unsigned s08bits
+#define u16bits unsigned s16bits
+#define u32bits unsigned s32bits
+
+#if defined(COMPILER_16_BIT)
+
+typedef u08bits far * pu08bits;
+typedef u16bits far * pu16bits;
+typedef u32bits far * pu32bits;
+
+#endif /* COMPILER_16_BIT */
+
+#if defined(COMPILER_32_BIT)
+
+typedef u08bits * pu08bits;
+typedef u16bits * pu16bits;
+typedef u32bits * pu32bits;
+
+#endif /* COMPILER_32_BIT */
+
+
+#define BIT(x) ((UCHAR)(1<<(x))) /* single-bit mask in bit position x */
+#define BITW(x) ((USHORT)(1<<(x))) /* single-bit mask in bit position x */
+
+
+
+#if defined(DOS)
+/*#include <dos.h>*/
+ #undef inportb /* undefine for Borland Lib */
+ #undef inport /* they may have define I/O function in LIB */
+ #undef outportb
+ #undef outport
+
+ #define OS_InPortByte(ioport) inportb(ioport)
+ #define OS_InPortWord(ioport) inport(ioport)
+ #define OS_InPortLong(ioport) inportq(ioport, val)
+ #define OS_OutPortByte(ioport, val) outportb(ioport, val)
+ #define OS_OutPortWord(ioport, val) outport(ioport, val)
+ #define OS_OutPortLong(ioport) outportq(ioport, val)
+#endif /* DOS */
+
+#if defined(NETWARE) || defined(OTHER_32) || defined(OTHER_16)
+ extern u08bits OS_InPortByte(u32bits ioport);
+ extern u16bits OS_InPortWord(u32bits ioport);
+ extern u32bits OS_InPortLong(u32bits ioport);
+
+ extern OS_InPortByteBuffer(u32bits ioport, pu08bits buffer, u32bits count);
+ extern OS_InPortWordBuffer(u32bits ioport, pu16bits buffer, u32bits count);
+ extern OS_OutPortByte(u32bits ioport, u08bits val);
+ extern OS_OutPortWord(u32bits ioport, u16bits val);
+ extern OS_OutPortLong(u32bits ioport, u32bits val);
+ extern OS_OutPortByteBuffer(u32bits ioport, pu08bits buffer, u32bits count);
+ extern OS_OutPortWordBuffer(u32bits ioport, pu16bits buffer, u32bits count);
+#endif /* NETWARE || OTHER_32 || OTHER_16 */
+
+#if defined (NT) || defined(WIN95_32) || defined(WIN95_16)
+ #if defined(NT)
+
+ extern __declspec(dllimport) u08bits ScsiPortReadPortUchar(pu08bits ioport);
+ extern __declspec(dllimport) u16bits ScsiPortReadPortUshort(pu16bits ioport);
+ extern __declspec(dllimport) u32bits ScsiPortReadPortUlong(pu32bits ioport);
+ extern __declspec(dllimport) void ScsiPortWritePortUchar(pu08bits ioport, u08bits val);
+ extern __declspec(dllimport) void ScsiPortWritePortUshort(pu16bits port, u16bits val);
+ extern __declspec(dllimport) void ScsiPortWritePortUlong(pu32bits port, u32bits val);
+
+ #else
+
+ extern u08bits ScsiPortReadPortUchar(pu08bits ioport);
+ extern u16bits ScsiPortReadPortUshort(pu16bits ioport);
+ extern u32bits ScsiPortReadPortUlong(pu32bits ioport);
+ extern void ScsiPortWritePortUchar(pu08bits ioport, u08bits val);
+ extern void ScsiPortWritePortUshort(pu16bits port, u16bits val);
+ extern void ScsiPortWritePortUlong(pu32bits port, u32bits val);
+ #endif
+
+
+ #define OS_InPortByte(ioport) ScsiPortReadPortUchar((pu08bits) ioport)
+ #define OS_InPortWord(ioport) ScsiPortReadPortUshort((pu16bits) ioport)
+ #define OS_InPortLong(ioport) ScsiPortReadPortUlong((pu32bits) ioport)
+
+ #define OS_OutPortByte(ioport, val) ScsiPortWritePortUchar((pu08bits) ioport, (u08bits) val)
+ #define OS_OutPortWord(ioport, val) ScsiPortWritePortUshort((pu16bits) ioport, (u16bits) val)
+ #define OS_OutPortLong(ioport, val) ScsiPortWritePortUlong((pu32bits) ioport, (u32bits) val)
+ #define OS_OutPortByteBuffer(ioport, buffer, count) \
+ ScsiPortWritePortBufferUchar((pu08bits)&port, (pu08bits) buffer, (u32bits) count)
+ #define OS_OutPortWordBuffer(ioport, buffer, count) \
+ ScsiPortWritePortBufferUshort((pu16bits)&port, (pu16bits) buffer, (u32bits) count)
+
+ #define OS_Lock(x)
+ #define OS_UnLock(x)
+#endif /* NT || WIN95_32 || WIN95_16 */
+
+#if defined (UNIX) && !defined(OS_InPortByte)
+ #define OS_InPortByte(ioport) inb((u16bits)ioport)
+ #define OS_InPortWord(ioport) inw((u16bits)ioport)
+ #define OS_InPortLong(ioport) inl((u16bits)ioport)
+ #define OS_OutPortByte(ioport,val) outb((u16bits)ioport, (u08bits)val)
+ #define OS_OutPortWord(ioport,val) outw((u16bits)ioport, (u16bits)val)
+ #define OS_OutPortLong(ioport,val) outl((u16bits)ioport, (u32bits)val)
+
+ #define OS_Lock(x)
+ #define OS_UnLock(x)
+#endif /* UNIX */
+
+
+#if defined(OS2)
+ extern u08bits inb(u32bits ioport);
+ extern u16bits inw(u32bits ioport);
+ extern void outb(u32bits ioport, u08bits val);
+ extern void outw(u32bits ioport, u16bits val);
+
+ #define OS_InPortByte(ioport) inb(ioport)
+ #define OS_InPortWord(ioport) inw(ioport)
+ #define OS_OutPortByte(ioport, val) outb(ioport, val)
+ #define OS_OutPortWord(ioport, val) outw(ioport, val)
+ extern u32bits OS_InPortLong(u32bits ioport);
+ extern void OS_OutPortLong(u32bits ioport, u32bits val);
+
+ #define OS_Lock(x)
+ #define OS_UnLock(x)
+#endif /* OS2 */
+
+#if defined(SOLARIS_REAL_MODE)
+
+extern unsigned char inb(unsigned long ioport);
+extern unsigned short inw(unsigned long ioport);
+
+#define OS_InPortByte(ioport) inb(ioport)
+#define OS_InPortWord(ioport) inw(ioport)
+
+extern void OS_OutPortByte(unsigned long ioport, unsigned char val);
+extern void OS_OutPortWord(unsigned long ioport, unsigned short val);
+extern unsigned long OS_InPortLong(unsigned long ioport);
+extern void OS_OutPortLong(unsigned long ioport, unsigned long val);
+
+#define OS_Lock(x)
+#define OS_UnLock(x)
+
+#endif /* SOLARIS_REAL_MODE */
+
+#endif /* __GLOBALS_H__ */
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: sccbmgr.h $
+ *
+ * Description: Common shared SCCB Interface defines and SCCB
+ * Manager specifics defines.
+ *
+ * $Date: 1996/10/24 23:09:33 $
+ *
+ * $Revision: 1.14 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __SCCB_H__
+#define __SCCB_H__
+
+/*#include <osflags.h>*/
+/*#include <globals.h>*/
+
+#if defined(BUGBUG)
+#define debug_size 32
+#endif
+
+#if defined(DOS)
+
+ typedef struct _SCCB near *PSCCB;
+ #if (FW_TYPE == _SCCB_MGR_)
+ typedef void (*CALL_BK_FN)(PSCCB);
+ #endif
+
+#elif defined(OS2)
+
+ typedef struct _SCCB far *PSCCB;
+ #if (FW_TYPE == _SCCB_MGR_)
+ typedef void (far *CALL_BK_FN)(PSCCB);
+ #endif
+
+#else
+
+ typedef struct _SCCB *PSCCB;
+ #if (FW_TYPE == _SCCB_MGR_)
+ typedef void (*CALL_BK_FN)(PSCCB);
+ #endif
+
+#endif
+
+
+typedef struct SCCBMgr_info {
+ ULONG si_baseaddr;
+ UCHAR si_present;
+ UCHAR si_intvect;
+ UCHAR si_id;
+ UCHAR si_lun;
+ USHORT si_fw_revision;
+ USHORT si_per_targ_init_sync;
+ USHORT si_per_targ_fast_nego;
+ USHORT si_per_targ_ultra_nego;
+ USHORT si_per_targ_no_disc;
+ USHORT si_per_targ_wide_nego;
+ USHORT si_flags;
+ UCHAR si_card_family;
+ UCHAR si_bustype;
+ UCHAR si_card_model[3];
+ UCHAR si_relative_cardnum;
+ UCHAR si_reserved[4];
+ ULONG si_OS_reserved;
+ UCHAR si_XlatInfo[4];
+ ULONG si_reserved2[5];
+ ULONG si_secondary_range;
+} SCCBMGR_INFO;
+
+#if defined(DOS)
+ typedef SCCBMGR_INFO * PSCCBMGR_INFO;
+#else
+ #if defined (COMPILER_16_BIT)
+ typedef SCCBMGR_INFO far * PSCCBMGR_INFO;
+ #else
+ typedef SCCBMGR_INFO * PSCCBMGR_INFO;
+ #endif
+#endif // defined(DOS)
+
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+ #define SCSI_PARITY_ENA 0x0001
+ #define LOW_BYTE_TERM 0x0010
+ #define HIGH_BYTE_TERM 0x0020
+ #define BUSTYPE_PCI 0x3
+#endif
+
+#define SUPPORT_16TAR_32LUN 0x0002
+#define SOFT_RESET 0x0004
+#define EXTENDED_TRANSLATION 0x0008
+#define POST_ALL_UNDERRRUNS 0x0040
+#define FLAG_SCAM_ENABLED 0x0080
+#define FLAG_SCAM_LEVEL2 0x0100
+
+
+
+
+#define HARPOON_FAMILY 0x02
+
+
+#define ISA_BUS_CARD 0x01
+#define EISA_BUS_CARD 0x02
+#define PCI_BUS_CARD 0x03
+#define VESA_BUS_CARD 0x04
+
+/* SCCB struc used for both SCCB and UCB manager compiles!
+ * The UCB Manager treats the SCCB as it's 'native hardware structure'
+ */
+
+
+#pragma pack(1)
+typedef struct _SCCB {
+ UCHAR OperationCode;
+ UCHAR ControlByte;
+ UCHAR CdbLength;
+ UCHAR RequestSenseLength;
+ ULONG DataLength;
+ ULONG DataPointer;
+ UCHAR CcbRes[2];
+ UCHAR HostStatus;
+ UCHAR TargetStatus;
+ UCHAR TargID;
+ UCHAR Lun;
+ UCHAR Cdb[12];
+ UCHAR CcbRes1;
+ UCHAR Reserved1;
+ ULONG Reserved2;
+ ULONG SensePointer;
+
+
+ CALL_BK_FN SccbCallback; /* VOID (*SccbCallback)(); */
+ ULONG SccbIOPort; /* Identifies board base port */
+ UCHAR SccbStatus;
+ UCHAR SCCBRes2;
+ USHORT SccbOSFlags;
+
+
+ ULONG Sccb_XferCnt; /* actual transfer count */
+ ULONG Sccb_ATC;
+ ULONG SccbVirtDataPtr; /* virtual addr for OS/2 */
+ ULONG Sccb_res1;
+ USHORT Sccb_MGRFlags;
+ USHORT Sccb_sgseg;
+ UCHAR Sccb_scsimsg; /* identify msg for selection */
+ UCHAR Sccb_tag;
+ UCHAR Sccb_scsistat;
+ UCHAR Sccb_idmsg; /* image of last msg in */
+ PSCCB Sccb_forwardlink;
+ PSCCB Sccb_backlink;
+ ULONG Sccb_savedATC;
+ UCHAR Save_Cdb[6];
+ UCHAR Save_CdbLen;
+ UCHAR Sccb_XferState;
+ ULONG Sccb_SGoffset;
+#if (FW_TYPE == _UCB_MGR_)
+ PUCB Sccb_ucb_ptr;
+#endif
+ } SCCB;
+
+#define SCCB_SIZE sizeof(SCCB)
+
+#pragma pack()
+
+
+
+#define SCSI_INITIATOR_COMMAND 0x00
+#define TARGET_MODE_COMMAND 0x01
+#define SCATTER_GATHER_COMMAND 0x02
+#define RESIDUAL_COMMAND 0x03
+#define RESIDUAL_SG_COMMAND 0x04
+#define RESET_COMMAND 0x81
+
+
+#define F_USE_CMD_Q 0x20 /*Inidcates TAGGED command. */
+#define TAG_TYPE_MASK 0xC0 /*Type of tag msg to send. */
+#define TAG_Q_MASK 0xE0
+#define SCCB_DATA_XFER_OUT 0x10 /* Write */
+#define SCCB_DATA_XFER_IN 0x08 /* Read */
+
+
+#define FOURTEEN_BYTES 0x00 /* Request Sense Buffer size */
+#define NO_AUTO_REQUEST_SENSE 0x01 /* No Request Sense Buffer */
+
+
+#define BUS_FREE_ST 0
+#define SELECT_ST 1
+#define SELECT_BDR_ST 2 /* Select w\ Bus Device Reset */
+#define SELECT_SN_ST 3 /* Select w\ Sync Nego */
+#define SELECT_WN_ST 4 /* Select w\ Wide Data Nego */
+#define SELECT_Q_ST 5 /* Select w\ Tagged Q'ing */
+#define COMMAND_ST 6
+#define DATA_OUT_ST 7
+#define DATA_IN_ST 8
+#define DISCONNECT_ST 9
+#define STATUS_ST 10
+#define ABORT_ST 11
+#define MESSAGE_ST 12
+
+
+#define F_HOST_XFER_DIR 0x01
+#define F_ALL_XFERRED 0x02
+#define F_SG_XFER 0x04
+#define F_AUTO_SENSE 0x08
+#define F_ODD_BALL_CNT 0x10
+#define F_NO_DATA_YET 0x80
+
+
+#define F_STATUSLOADED 0x01
+#define F_MSGLOADED 0x02
+#define F_DEV_SELECTED 0x04
+
+
+#define SCCB_COMPLETE 0x00 /* SCCB completed without error */
+#define SCCB_DATA_UNDER_RUN 0x0C
+#define SCCB_SELECTION_TIMEOUT 0x11 /* Set SCSI selection timed out */
+#define SCCB_DATA_OVER_RUN 0x12
+#define SCCB_UNEXPECTED_BUS_FREE 0x13 /* Target dropped SCSI BSY */
+#define SCCB_PHASE_SEQUENCE_FAIL 0x14 /* Target bus phase sequence failure */
+
+#define SCCB_INVALID_OP_CODE 0x16 /* SCCB invalid operation code */
+#define SCCB_INVALID_SCCB 0x1A /* Invalid SCCB - bad parameter */
+#define SCCB_GROSS_FW_ERR 0x27 /* Major problem! */
+#define SCCB_BM_ERR 0x30 /* BusMaster error. */
+#define SCCB_PARITY_ERR 0x34 /* SCSI parity error */
+
+
+
+#if (FW_TYPE==_UCB_MGR_)
+ #define HBA_AUTO_SENSE_FAIL 0x1B
+ #define HBA_TQ_REJECTED 0x1C
+ #define HBA_UNSUPPORTED_MSG 0x1D
+ #define HBA_HW_ERROR 0x20
+ #define HBA_ATN_NOT_RESPONDED 0x21
+ #define HBA_SCSI_RESET_BY_ADAPTER 0x22
+ #define HBA_SCSI_RESET_BY_TARGET 0x23
+ #define HBA_WRONG_CONNECTION 0x24
+ #define HBA_BUS_DEVICE_RESET 0x25
+ #define HBA_ABORT_QUEUE 0x26
+
+#else // these are not defined in BUDI/UCB
+
+ #define SCCB_INVALID_DIRECTION 0x18 /* Invalid target direction */
+ #define SCCB_DUPLICATE_SCCB 0x19 /* Duplicate SCCB */
+ #define SCCB_SCSI_RST 0x35 /* SCSI RESET detected. */
+
+#endif // (FW_TYPE==_UCB_MGR_)
+
+
+#define SCCB_IN_PROCESS 0x00
+#define SCCB_SUCCESS 0x01
+#define SCCB_ABORT 0x02
+#define SCCB_NOT_FOUND 0x03
+#define SCCB_ERROR 0x04
+#define SCCB_INVALID 0x05
+
+#define SCCB_SIZE sizeof(SCCB)
+
+
+
+
+#if (FW_TYPE == _UCB_MGR_)
+ void SccbMgr_start_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb);
+ s32bits SccbMgr_abort_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb);
+ u08bits SccbMgr_my_int(CARD_HANDLE pCurrCard);
+ s32bits SccbMgr_isr(CARD_HANDLE pCurrCard);
+ void SccbMgr_scsi_reset(CARD_HANDLE pCurrCard);
+ void SccbMgr_timer_expired(CARD_HANDLE pCurrCard);
+ void SccbMgr_unload_card(CARD_HANDLE pCurrCard);
+ void SccbMgr_restore_foreign_state(CARD_HANDLE pCurrCard);
+ void SccbMgr_restore_native_state(CARD_HANDLE pCurrCard);
+ void SccbMgr_save_foreign_state(PADAPTER_INFO pAdapterInfo);
+
+#endif
+
+
+#if (FW_TYPE == _SCCB_MGR_)
+
+ #if defined (DOS)
+ int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo);
+ USHORT SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo);
+ void SccbMgr_start_sccb(USHORT pCurrCard, PSCCB p_SCCB);
+ int SccbMgr_abort_sccb(USHORT pCurrCard, PSCCB p_SCCB);
+ UCHAR SccbMgr_my_int(USHORT pCurrCard);
+ int SccbMgr_isr(USHORT pCurrCard);
+ void SccbMgr_scsi_reset(USHORT pCurrCard);
+ void SccbMgr_timer_expired(USHORT pCurrCard);
+ USHORT SccbMgr_status(USHORT pCurrCard);
+ void SccbMgr_unload_card(USHORT pCurrCard);
+
+ #else //non-DOS
+
+ int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo);
+ ULONG SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo);
+ void SccbMgr_start_sccb(ULONG pCurrCard, PSCCB p_SCCB);
+ int SccbMgr_abort_sccb(ULONG pCurrCard, PSCCB p_SCCB);
+ UCHAR SccbMgr_my_int(ULONG pCurrCard);
+ int SccbMgr_isr(ULONG pCurrCard);
+ void SccbMgr_scsi_reset(ULONG pCurrCard);
+ void SccbMgr_enable_int(ULONG pCurrCard);
+ void SccbMgr_disable_int(ULONG pCurrCard);
+ void SccbMgr_timer_expired(ULONG pCurrCard);
+ void SccbMgr_unload_card(ULONG pCurrCard);
+
+ #endif
+#endif // (FW_TYPE == _SCCB_MGR_)
+
+#endif /* __SCCB_H__ */
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: blx30.h $
+ *
+ * Description: This module contains SCCB/UCB Manager implementation
+ * specific stuff.
+ *
+ * $Date: 1996/11/13 18:34:22 $
+ *
+ * $Revision: 1.10 $
+ *
+ *----------------------------------------------------------------------*/
+
+
+#ifndef __blx30_H__
+#define __blx30_H__
+
+/*#include <globals.h>*/
+
+#define ORION_FW_REV 3110
+
+
+
+
+#define HARP_REVD 1
+
+
+#if defined(DOS)
+#define QUEUE_DEPTH 8+1 /*1 for Normal disconnect 0 for Q'ing. */
+#else
+#define QUEUE_DEPTH 254+1 /*1 for Normal disconnect 32 for Q'ing. */
+#endif // defined(DOS)
+
+#define MAX_MB_CARDS 4 /* Max. no of cards suppoerted on Mother Board */
+
+#define WIDE_SCSI 1
+
+#if defined(WIDE_SCSI)
+ #if defined(DOS)
+ #define MAX_SCSI_TAR 16
+ #define MAX_LUN 8
+ #define LUN_MASK 0x07
+ #else
+ #define MAX_SCSI_TAR 16
+ #define MAX_LUN 32
+ #define LUN_MASK 0x1f
+
+ #endif
+#else
+ #define MAX_SCSI_TAR 8
+ #define MAX_LUN 8
+ #define LUN_MASK 0x07
+#endif
+
+#if defined(HARP_REVA)
+#define SG_BUF_CNT 15 /*Number of prefetched elements. */
+#else
+#define SG_BUF_CNT 16 /*Number of prefetched elements. */
+#endif
+
+#define SG_ELEMENT_SIZE 8 /*Eight byte per element. */
+#define SG_LOCAL_MASK 0x00000000L
+#define SG_ELEMENT_MASK 0xFFFFFFFFL
+
+
+#if (FW_TYPE == _UCB_MGR_)
+ #define OPC_DECODE_NORMAL 0x0f7f
+#endif // _UCB_MGR_
+
+
+
+#if defined(DOS)
+
+/*#include <dos.h>*/
+ #define RD_HARPOON(ioport) (OS_InPortByte(ioport))
+ #define RDW_HARPOON(ioport) (OS_InPortWord(ioport))
+ #define WR_HARPOON(ioport,val) (OS_OutPortByte(ioport,val))
+ #define WRW_HARPOON(ioport,val) (OS_OutPortWord(ioport,val))
+
+ #define RD_HARP32(port,offset,data) asm{db 66h; \
+ push ax; \
+ mov dx,port; \
+ add dx, offset; \
+ db 66h; \
+ in ax,dx; \
+ db 66h; \
+ mov word ptr data,ax;\
+ db 66h; \
+ pop ax}
+
+ #define WR_HARP32(port,offset,data) asm{db 66h; \
+ push ax; \
+ mov dx,port; \
+ add dx, offset; \
+ db 66h; \
+ mov ax,word ptr data;\
+ db 66h; \
+ out dx,ax; \
+ db 66h; \
+ pop ax}
+#endif /* DOS */
+
+#if defined(NETWARE) || defined(OTHER_32) || defined(OTHER_16)
+ #define RD_HARPOON(ioport) OS_InPortByte((unsigned long)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((unsigned long)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong(ioport + offset))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((ioport + offset), data)
+#endif /* NETWARE || OTHER_32 || OTHER_16 */
+
+#if defined(NT) || defined(WIN95_32) || defined(WIN95_16)
+ #define RD_HARPOON(ioport) OS_InPortByte((ULONG)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((ULONG)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((ULONG)(ioport + offset), data)
+#endif /* NT || WIN95_32 || WIN95_16 */
+
+#if defined (UNIX)
+ #define RD_HARPOON(ioport) OS_InPortByte((u32bits)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((u32bits)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((u32bits)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((u32bits)ioport,(u08bits) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((u32bits)ioport,(u16bits)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((u32bits)(ioport + offset), data)
+#endif /* UNIX */
+
+#if defined(OS2)
+ #define RD_HARPOON(ioport) OS_InPortByte((unsigned long)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((unsigned long)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong(((ULONG)(ioport + offset)), data)
+#endif /* OS2 */
+
+#if defined(SOLARIS_REAL_MODE)
+
+ #define RD_HARPOON(ioport) OS_InPortByte((unsigned long)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((unsigned long)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((ULONG)(ioport + offset), (ULONG)data)
+
+#endif /* SOLARIS_REAL_MODE */
+
+#endif /* __BLX30_H__ */
+
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: target.h $
+ *
+ * Description: Definitions for Target related structures
+ *
+ * $Date: 1996/12/11 22:06:20 $
+ *
+ * $Revision: 1.9 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __TARGET__
+#define __TARGET__
+
+/*#include <globals.h>*/
+/*#include <blx30.h>*/
+
+
+#define TAR_SYNC_MASK (BIT(7)+BIT(6))
+#define SYNC_UNKNOWN 0x00
+#define SYNC_TRYING BIT(6)
+#define SYNC_SUPPORTED (BIT(7)+BIT(6))
+
+#define TAR_WIDE_MASK (BIT(5)+BIT(4))
+#define WIDE_DISABLED 0x00
+#define WIDE_ENABLED BIT(4)
+#define WIDE_NEGOCIATED BIT(5)
+
+#define TAR_TAG_Q_MASK (BIT(3)+BIT(2))
+#define TAG_Q_UNKNOWN 0x00
+#define TAG_Q_TRYING BIT(2)
+#define TAG_Q_REJECT BIT(3)
+#define TAG_Q_SUPPORTED (BIT(3)+BIT(2))
+
+#define TAR_ALLOW_DISC BIT(0)
+
+
+#define EE_SYNC_MASK (BIT(0)+BIT(1))
+#define EE_SYNC_ASYNC 0x00
+#define EE_SYNC_5MB BIT(0)
+#define EE_SYNC_10MB BIT(1)
+#define EE_SYNC_20MB (BIT(0)+BIT(1))
+
+#define EE_ALLOW_DISC BIT(6)
+#define EE_WIDE_SCSI BIT(7)
+
+
+#if defined(DOS)
+ typedef struct SCCBMgr_tar_info near *PSCCBMgr_tar_info;
+
+#elif defined(OS2)
+ typedef struct SCCBMgr_tar_info far *PSCCBMgr_tar_info;
+
+#else
+ typedef struct SCCBMgr_tar_info *PSCCBMgr_tar_info;
+
+#endif
+
+
+typedef struct SCCBMgr_tar_info {
+
+ PSCCB TarSelQ_Head;
+ PSCCB TarSelQ_Tail;
+ UCHAR TarLUN_CA; /*Contingent Allgiance */
+ UCHAR TarTagQ_Cnt;
+ UCHAR TarSelQ_Cnt;
+ UCHAR TarStatus;
+ UCHAR TarEEValue;
+ UCHAR TarSyncCtrl;
+ UCHAR TarReserved[2]; /* for alignment */
+ UCHAR LunDiscQ_Idx[MAX_LUN];
+ UCHAR TarLUNBusy[MAX_LUN];
+} SCCBMGR_TAR_INFO;
+
+typedef struct NVRAMInfo {
+ UCHAR niModel; /* Model No. of card */
+ UCHAR niCardNo; /* Card no. */
+#if defined(DOS)
+ USHORT niBaseAddr; /* Port Address of card */
+#else
+ ULONG niBaseAddr; /* Port Address of card */
+#endif
+ UCHAR niSysConf; /* Adapter Configuration byte - Byte 16 of eeprom map */
+ UCHAR niScsiConf; /* SCSI Configuration byte - Byte 17 of eeprom map */
+ UCHAR niScamConf; /* SCAM Configuration byte - Byte 20 of eeprom map */
+ UCHAR niAdapId; /* Host Adapter ID - Byte 24 of eerpom map */
+ UCHAR niSyncTbl[MAX_SCSI_TAR / 2]; /* Sync/Wide byte of targets */
+ UCHAR niScamTbl[MAX_SCSI_TAR][4]; /* Compressed Scam name string of Targets */
+}NVRAMINFO;
+
+#if defined(DOS)
+typedef NVRAMINFO near *PNVRamInfo;
+#elif defined (OS2)
+typedef NVRAMINFO far *PNVRamInfo;
+#else
+typedef NVRAMINFO *PNVRamInfo;
+#endif
+
+#define MODEL_LT 1
+#define MODEL_DL 2
+#define MODEL_LW 3
+#define MODEL_DW 4
+
+
+typedef struct SCCBcard {
+ PSCCB currentSCCB;
+#if (FW_TYPE==_SCCB_MGR_)
+ PSCCBMGR_INFO cardInfo;
+#else
+ PADAPTER_INFO cardInfo;
+#endif
+
+#if defined(DOS)
+ USHORT ioPort;
+#else
+ ULONG ioPort;
+#endif
+
+ USHORT cmdCounter;
+ UCHAR discQCount;
+ UCHAR tagQ_Lst;
+ UCHAR cardIndex;
+ UCHAR scanIndex;
+ UCHAR globalFlags;
+ UCHAR ourId;
+ PNVRamInfo pNvRamInfo;
+ PSCCB discQ_Tbl[QUEUE_DEPTH];
+
+}SCCBCARD;
+
+#if defined(DOS)
+typedef struct SCCBcard near *PSCCBcard;
+#elif defined (OS2)
+typedef struct SCCBcard far *PSCCBcard;
+#else
+typedef struct SCCBcard *PSCCBcard;
+#endif
+
+
+#define F_TAG_STARTED 0x01
+#define F_CONLUN_IO 0x02
+#define F_DO_RENEGO 0x04
+#define F_NO_FILTER 0x08
+#define F_GREEN_PC 0x10
+#define F_HOST_XFER_ACT 0x20
+#define F_NEW_SCCB_CMD 0x40
+#define F_UPDATE_EEPROM 0x80
+
+
+#define ID_STRING_LENGTH 32
+#define TYPE_CODE0 0x63 /*Level2 Mstr (bits 7-6), */
+
+#define TYPE_CODE1 00 /*No ID yet */
+
+#define SLV_TYPE_CODE0 0xA3 /*Priority Bit set (bits 7-6), */
+
+#define ASSIGN_ID 0x00
+#define SET_P_FLAG 0x01
+#define CFG_CMPLT 0x03
+#define DOM_MSTR 0x0F
+#define SYNC_PTRN 0x1F
+
+#define ID_0_7 0x18
+#define ID_8_F 0x11
+#define ID_10_17 0x12
+#define ID_18_1F 0x0B
+#define MISC_CODE 0x14
+#define CLR_P_FLAG 0x18
+#define LOCATE_ON 0x12
+#define LOCATE_OFF 0x0B
+
+#define LVL_1_MST 0x00
+#define LVL_2_MST 0x40
+#define DOM_LVL_2 0xC0
+
+
+#define INIT_SELTD 0x01
+#define LEVEL2_TAR 0x02
+
+
+enum scam_id_st { ID0,ID1,ID2,ID3,ID4,ID5,ID6,ID7,ID8,ID9,ID10,ID11,ID12,
+ ID13,ID14,ID15,ID_UNUSED,ID_UNASSIGNED,ID_ASSIGNED,LEGACY,
+ CLR_PRIORITY,NO_ID_AVAIL };
+
+typedef struct SCCBscam_info {
+
+ UCHAR id_string[ID_STRING_LENGTH];
+ enum scam_id_st state;
+
+} SCCBSCAM_INFO, *PSCCBSCAM_INFO;
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: scsi2.h $
+ *
+ * Description: Register definitions for HARPOON ASIC.
+ *
+ * $Date: 1996/11/13 18:32:57 $
+ *
+ * $Revision: 1.4 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __SCSI_H__
+#define __SCSI_H__
+
+
+
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REZERO_UNIT 0x01
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_FORMAT_UNIT 0x04
+#define SCSI_REASSIGN 0x07
+#define SCSI_READ 0x08
+#define SCSI_WRITE 0x0A
+#define SCSI_SEEK 0x0B
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT 0x15
+#define SCSI_RESERVE_UNIT 0x16
+#define SCSI_RELEASE_UNIT 0x17
+#define SCSI_MODE_SENSE 0x1A
+#define SCSI_START_STOP_UNIT 0x1B
+#define SCSI_SEND_DIAGNOSTIC 0x1D
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ_EXTENDED 0x28
+#define SCSI_WRITE_EXTENDED 0x2A
+#define SCSI_SEEK_EXTENDED 0x2B
+#define SCSI_WRITE_AND_VERIFY 0x2E
+#define SCSI_VERIFY 0x2F
+#define SCSI_READ_DEFECT_DATA 0x37
+#define SCSI_WRITE_BUFFER 0x3B
+#define SCSI_READ_BUFFER 0x3C
+#define SCSI_RECV_DIAGNOSTIC 0x1C
+#define SCSI_READ_LONG 0x3E
+#define SCSI_WRITE_LONG 0x3F
+#define SCSI_LAST_SCSI_CMND SCSI_WRITE_LONG
+#define SCSI_INVALID_CMND 0xFF
+
+
+
+#define SSGOOD 0x00
+#define SSCHECK 0x02
+#define SSCOND_MET 0x04
+#define SSBUSY 0x08
+#define SSRESERVATION_CONFLICT 0x18
+#define SSCMD_TERM 0x22
+#define SSQ_FULL 0x28
+
+
+#define SKNO_SEN 0x00
+#define SKRECOV_ERR 0x01
+#define SKNOT_RDY 0x02
+#define SKMED_ERR 0x03
+#define SKHW_ERR 0x04
+#define SKILL_REQ 0x05
+#define SKUNIT_ATTN 0x06
+#define SKDATA_PROTECT 0x07
+#define SKBLNK_CHK 0x08
+#define SKCPY_ABORT 0x0A
+#define SKABORT_CMD 0x0B
+#define SKEQUAL 0x0C
+#define SKVOL_OVF 0x0D
+#define SKMIS_CMP 0x0E
+
+
+#define SMCMD_COMP 0x00
+#define SMEXT 0x01
+#define SMSAVE_DATA_PTR 0x02
+#define SMREST_DATA_PTR 0x03
+#define SMDISC 0x04
+#define SMINIT_DETEC_ERR 0x05
+#define SMABORT 0x06
+#define SMREJECT 0x07
+#define SMNO_OP 0x08
+#define SMPARITY 0x09
+#define SMDEV_RESET 0x0C
+#define SMABORT_TAG 0x0D
+#define SMINIT_RECOVERY 0x0F
+#define SMREL_RECOVERY 0x10
+
+#define SMIDENT 0x80
+#define DISC_PRIV 0x40
+
+
+#define SMSYNC 0x01
+#define SM10MBS 0x19 /* 100ns */
+#define SM5MBS 0x32 /* 200ns */
+#define SMOFFSET 0x0F /* Maxoffset value */
+#define SMWDTR 0x03
+#define SM8BIT 0x00
+#define SM16BIT 0x01
+#define SM32BIT 0x02
+#define SMIGNORWR 0x23 /* Ignore Wide Residue */
+
+
+#define ARBITRATION_DELAY 0x01 /* 2.4us using a 40Mhz clock */
+#define BUS_SETTLE_DELAY 0x01 /* 400ns */
+#define BUS_CLEAR_DELAY 0x01 /* 800ns */
+
+
+
+#define SPHASE_TO 0x0A /* 10 second timeout waiting for */
+#define SCMD_TO 0x0F /* Overall command timeout */
+
+
+
+#define SIX_BYTE_CMD 0x06
+#define TEN_BYTE_CMD 0x0A
+#define TWELVE_BYTE_CMD 0x0C
+
+#define ASYNC 0x00
+#define PERI25NS 0x06 /* 25/4ns to next clock for xbow. */
+#define SYNC10MBS 0x19
+#define SYNC5MBS 0x32
+#define MAX_OFFSET 0x0F /* Maxbyteoffset for Sync Xfers */
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: eeprom.h $
+ *
+ * Description: Definitions for EEPROM related structures
+ *
+ * $Date: 1996/11/13 18:28:39 $
+ *
+ * $Revision: 1.4 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __EEPROM__
+#define __EEPROM__
+
+/*#include <globals.h>*/
+
+#define EEPROM_WD_CNT 256
+
+#define EEPROM_CHECK_SUM 0
+#define FW_SIGNATURE 2
+#define MODEL_NUMB_0 4
+#define MODEL_NUMB_1 5
+#define MODEL_NUMB_2 6
+#define MODEL_NUMB_3 7
+#define MODEL_NUMB_4 8
+#define MODEL_NUMB_5 9
+#define IO_BASE_ADDR 10
+#define IRQ_NUMBER 12
+#define PCI_INT_PIN 13
+#define BUS_DELAY 14 /*On time in byte 14 off delay in 15 */
+#define SYSTEM_CONFIG 16
+#define SCSI_CONFIG 17
+#define BIOS_CONFIG 18
+#define SPIN_UP_DELAY 19
+#define SCAM_CONFIG 20
+#define ADAPTER_SCSI_ID 24
+
+
+#define IGNORE_B_SCAN 32
+#define SEND_START_ENA 34
+#define DEVICE_ENABLE 36
+
+#define SYNC_RATE_TBL 38
+#define SYNC_RATE_TBL01 38
+#define SYNC_RATE_TBL23 40
+#define SYNC_RATE_TBL45 42
+#define SYNC_RATE_TBL67 44
+#define SYNC_RATE_TBL89 46
+#define SYNC_RATE_TBLab 48
+#define SYNC_RATE_TBLcd 50
+#define SYNC_RATE_TBLef 52
+
+
+
+#define EE_SCAMBASE 256
+
+
+
+ #define DOM_MASTER (BIT(0) + BIT(1))
+ #define SCAM_ENABLED BIT(2)
+ #define SCAM_LEVEL2 BIT(3)
+
+
+ #define RENEGO_ENA BITW(10)
+ #define CONNIO_ENA BITW(11)
+ #define GREEN_PC_ENA BITW(12)
+
+
+ #define AUTO_RATE_00 00
+ #define AUTO_RATE_05 01
+ #define AUTO_RATE_10 02
+ #define AUTO_RATE_20 03
+
+ #define WIDE_NEGO_BIT BIT(7)
+ #define DISC_ENABLE_BIT BIT(6)
+
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: harpoon.h $
+ *
+ * Description: Register definitions for HARPOON ASIC.
+ *
+ * $Date: 1997/07/09 21:44:36 $
+ *
+ * $Revision: 1.9 $
+ *
+ *----------------------------------------------------------------------*/
+
+
+/*#include <globals.h>*/
+
+#ifndef __HARPOON__
+#define __HARPOON__
+
+
+ #define hp_vendor_id_0 0x00 /* LSB */
+ #define ORION_VEND_0 0x4B
+
+ #define hp_vendor_id_1 0x01 /* MSB */
+ #define ORION_VEND_1 0x10
+
+ #define hp_device_id_0 0x02 /* LSB */
+ #define ORION_DEV_0 0x30
+
+ #define hp_device_id_1 0x03 /* MSB */
+ #define ORION_DEV_1 0x81
+
+ /* Sub Vendor ID and Sub Device ID only available in
+ Harpoon Version 2 and higher */
+
+ #define hp_sub_vendor_id_0 0x04 /* LSB */
+ #define hp_sub_vendor_id_1 0x05 /* MSB */
+ #define hp_sub_device_id_0 0x06 /* LSB */
+ #define hp_sub_device_id_1 0x07 /* MSB */
+
+
+ #define hp_dual_addr_lo 0x08
+ #define hp_dual_addr_lmi 0x09
+ #define hp_dual_addr_hmi 0x0A
+ #define hp_dual_addr_hi 0x0B
+
+ #define hp_semaphore 0x0C
+ #define SCCB_MGR_ACTIVE BIT(0)
+ #define TICKLE_ME BIT(1)
+ #define SCCB_MGR_PRESENT BIT(3)
+ #define BIOS_IN_USE BIT(4)
+
+ #define hp_user_defined_D 0x0D
+
+ #define hp_reserved_E 0x0E
+
+ #define hp_sys_ctrl 0x0F
+
+ #define STOP_CLK BIT(0) /*Turn off BusMaster Clock */
+ #define DRVR_RST BIT(1) /*Firmware Reset to 80C15 chip */
+ #define HALT_MACH BIT(3) /*Halt State Machine */
+ #define HARD_ABORT BIT(4) /*Hard Abort */
+ #define DIAG_MODE BIT(5) /*Diagnostic Mode */
+
+ #define BM_ABORT_TMOUT 0x50 /*Halt State machine time out */
+
+ #define hp_sys_cfg 0x10
+
+ #define DONT_RST_FIFO BIT(7) /*Don't reset FIFO */
+
+
+ #define hp_host_ctrl0 0x11
+
+ #define DUAL_ADDR_MODE BIT(0) /*Enable 64-bit addresses */
+ #define IO_MEM_SPACE BIT(1) /*I/O Memory Space */
+ #define RESOURCE_LOCK BIT(2) /*Enable Resource Lock */
+ #define IGNOR_ACCESS_ERR BIT(3) /*Ignore Access Error */
+ #define HOST_INT_EDGE BIT(4) /*Host interrupt level/edge mode sel */
+ #define SIX_CLOCKS BIT(5) /*6 Clocks between Strobe */
+ #define DMA_EVEN_PARITY BIT(6) /*Enable DMA Enen Parity */
+
+/*
+ #define BURST_MODE BIT(0)
+*/
+
+ #define hp_reserved_12 0x12
+
+ #define hp_host_blk_cnt 0x13
+
+ #define XFER_BLK1 0x00 /* 0 0 0 1 byte per block*/
+ #define XFER_BLK2 0x01 /* 0 0 1 2 byte per block*/
+ #define XFER_BLK4 0x02 /* 0 1 0 4 byte per block*/
+ #define XFER_BLK8 0x03 /* 0 1 1 8 byte per block*/
+ #define XFER_BLK16 0x04 /* 1 0 0 16 byte per block*/
+ #define XFER_BLK32 0x05 /* 1 0 1 32 byte per block*/
+ #define XFER_BLK64 0x06 /* 1 1 0 64 byte per block*/
+
+ #define BM_THRESHOLD 0x40 /* PCI mode can only xfer 16 bytes*/
+
+
+ #define hp_reserved_14 0x14
+ #define hp_reserved_15 0x15
+ #define hp_reserved_16 0x16
+
+ #define hp_int_mask 0x17
+
+ #define INT_CMD_COMPL BIT(0) /* DMA command complete */
+ #define INT_EXT_STATUS BIT(1) /* Extended Status Set */
+ #define INT_SCSI BIT(2) /* Scsi block interrupt */
+ #define INT_FIFO_RDY BIT(4) /* FIFO data ready */
+
+
+ #define hp_xfer_cnt_lo 0x18
+ #define hp_xfer_cnt_mi 0x19
+ #define hp_xfer_cnt_hi 0x1A
+ #define hp_xfer_cmd 0x1B
+
+ #define XFER_HOST_DMA 0x00 /* 0 0 0 Transfer Host -> DMA */
+ #define XFER_DMA_HOST 0x01 /* 0 0 1 Transfer DMA -> Host */
+ #define XFER_HOST_MPU 0x02 /* 0 1 0 Transfer Host -> MPU */
+ #define XFER_MPU_HOST 0x03 /* 0 1 1 Transfer MPU -> Host */
+ #define XFER_DMA_MPU 0x04 /* 1 0 0 Transfer DMA -> MPU */
+ #define XFER_MPU_DMA 0x05 /* 1 0 1 Transfer MPU -> DMA */
+ #define SET_SEMAPHORE 0x06 /* 1 1 0 Set Semaphore */
+ #define XFER_NOP 0x07 /* 1 1 1 Transfer NOP */
+ #define XFER_MB_MPU 0x06 /* 1 1 0 Transfer MB -> MPU */
+ #define XFER_MB_DMA 0x07 /* 1 1 1 Transfer MB -> DMA */
+
+
+ #define XFER_HOST_AUTO 0x00 /* 0 0 Auto Transfer Size */
+ #define XFER_HOST_8BIT 0x08 /* 0 1 8 BIT Transfer Size */
+ #define XFER_HOST_16BIT 0x10 /* 1 0 16 BIT Transfer Size */
+ #define XFER_HOST_32BIT 0x18 /* 1 1 32 BIT Transfer Size */
+
+ #define XFER_DMA_8BIT 0x20 /* 0 1 8 BIT Transfer Size */
+ #define XFER_DMA_16BIT 0x40 /* 1 0 16 BIT Transfer Size */
+
+ #define DISABLE_INT BIT(7) /*Do not interrupt at end of cmd. */
+
+ #define HOST_WRT_CMD ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_8BIT))
+ #define HOST_RD_CMD ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_8BIT))
+ #define WIDE_HOST_WRT_CMD ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_16BIT))
+ #define WIDE_HOST_RD_CMD ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_16BIT))
+
+ #define hp_host_addr_lo 0x1C
+ #define hp_host_addr_lmi 0x1D
+ #define hp_host_addr_hmi 0x1E
+ #define hp_host_addr_hi 0x1F
+
+ #define hp_pio_data 0x20
+ #define hp_reserved_21 0x21
+ #define hp_ee_ctrl 0x22
+
+ #define EXT_ARB_ACK BIT(7)
+ #define SCSI_TERM_ENA_H BIT(6) /* SCSI high byte terminator */
+ #define SEE_MS BIT(5)
+ #define SEE_CS BIT(3)
+ #define SEE_CLK BIT(2)
+ #define SEE_DO BIT(1)
+ #define SEE_DI BIT(0)
+
+ #define EE_READ 0x06
+ #define EE_WRITE 0x05
+ #define EWEN 0x04
+ #define EWEN_ADDR 0x03C0
+ #define EWDS 0x04
+ #define EWDS_ADDR 0x0000
+
+ #define hp_brdctl 0x23
+
+ #define DAT_7 BIT(7)
+ #define DAT_6 BIT(6)
+ #define DAT_5 BIT(5)
+ #define BRD_STB BIT(4)
+ #define BRD_CS BIT(3)
+ #define BRD_WR BIT(2)
+
+ #define hp_reserved_24 0x24
+ #define hp_reserved_25 0x25
+
+
+
+
+ #define hp_bm_ctrl 0x26
+
+ #define SCSI_TERM_ENA_L BIT(0) /*Enable/Disable external terminators */
+ #define FLUSH_XFER_CNTR BIT(1) /*Flush transfer counter */
+ #define BM_XFER_MIN_8 BIT(2) /*Enable bus master transfer of 9 */
+ #define BIOS_ENA BIT(3) /*Enable BIOS/FLASH Enable */
+ #define FORCE1_XFER BIT(5) /*Always xfer one byte in byte mode */
+ #define FAST_SINGLE BIT(6) /*?? */
+
+ #define BMCTRL_DEFAULT (FORCE1_XFER|FAST_SINGLE|SCSI_TERM_ENA_L)
+
+ #define hp_reserved_27 0x27
+
+ #define hp_sg_addr 0x28
+ #define hp_page_ctrl 0x29
+
+ #define SCATTER_EN BIT(0)
+ #define SGRAM_ARAM BIT(1)
+ #define BIOS_SHADOW BIT(2)
+ #define G_INT_DISABLE BIT(3) /* Enable/Disable all Interrupts */
+ #define NARROW_SCSI_CARD BIT(4) /* NARROW/WIDE SCSI config pin */
+
+ #define hp_reserved_2A 0x2A
+ #define hp_pci_cmd_cfg 0x2B
+
+ #define IO_SPACE_ENA BIT(0) /*enable I/O space */
+ #define MEM_SPACE_ENA BIT(1) /*enable memory space */
+ #define BUS_MSTR_ENA BIT(2) /*enable bus master operation */
+ #define MEM_WI_ENA BIT(4) /*enable Write and Invalidate */
+ #define PAR_ERR_RESP BIT(6) /*enable parity error responce. */
+
+ #define hp_reserved_2C 0x2C
+
+ #define hp_pci_stat_cfg 0x2D
+
+ #define DATA_PARITY_ERR BIT(0)
+ #define REC_TARGET_ABORT BIT(4) /*received Target abort */
+ #define REC_MASTER_ABORT BIT(5) /*received Master abort */
+ #define SIG_SYSTEM_ERR BIT(6)
+ #define DETECTED_PAR_ERR BIT(7)
+
+ #define hp_reserved_2E 0x2E
+
+ #define hp_sys_status 0x2F
+
+ #define SLV_DATA_RDY BIT(0) /*Slave data ready */
+ #define XFER_CNT_ZERO BIT(1) /*Transfer counter = 0 */
+ #define BM_FIFO_EMPTY BIT(2) /*FIFO empty */
+ #define BM_FIFO_FULL BIT(3) /*FIFO full */
+ #define HOST_OP_DONE BIT(4) /*host operation done */
+ #define DMA_OP_DONE BIT(5) /*DMA operation done */
+ #define SLV_OP_DONE BIT(6) /*Slave operation done */
+ #define PWR_ON_FLAG BIT(7) /*Power on flag */
+
+ #define hp_reserved_30 0x30
+
+ #define hp_host_status0 0x31
+
+ #define HOST_TERM BIT(5) /*Host Terminal Count */
+ #define HOST_TRSHLD BIT(6) /*Host Threshold */
+ #define CONNECTED_2_HOST BIT(7) /*Connected to Host */
+
+ #define hp_reserved_32 0x32
+
+ #define hp_rev_num 0x33
+
+ #define REV_A_CONST 0x0E
+ #define REV_B_CONST 0x0E
+
+ #define hp_stack_data 0x34
+ #define hp_stack_addr 0x35
+
+ #define hp_ext_status 0x36
+
+ #define BM_FORCE_OFF BIT(0) /*Bus Master is forced to get off */
+ #define PCI_TGT_ABORT BIT(0) /*PCI bus master transaction aborted */
+ #define PCI_DEV_TMOUT BIT(1) /*PCI Device Time out */
+ #define FIFO_TC_NOT_ZERO BIT(2) /*FIFO or transfer counter not zero */
+ #define CHIP_RST_OCCUR BIT(3) /*Chip reset occurs */
+ #define CMD_ABORTED BIT(4) /*Command aborted */
+ #define BM_PARITY_ERR BIT(5) /*parity error on data received */
+ #define PIO_OVERRUN BIT(6) /*Slave data overrun */
+ #define BM_CMD_BUSY BIT(7) /*Bus master transfer command busy */
+ #define BAD_EXT_STATUS (BM_FORCE_OFF | PCI_DEV_TMOUT | CMD_ABORTED | \
+ BM_PARITY_ERR | PIO_OVERRUN)
+
+ #define hp_int_status 0x37
+
+ #define BM_CMD_CMPL BIT(0) /*Bus Master command complete */
+ #define EXT_STATUS_ON BIT(1) /*Extended status is valid */
+ #define SCSI_INTERRUPT BIT(2) /*Global indication of a SCSI int. */
+ #define BM_FIFO_RDY BIT(4)
+ #define INT_ASSERTED BIT(5) /* */
+ #define SRAM_BUSY BIT(6) /*Scatter/Gather RAM busy */
+ #define CMD_REG_BUSY BIT(7)
+
+
+ #define hp_fifo_cnt 0x38
+ #define hp_curr_host_cnt 0x39
+ #define hp_reserved_3A 0x3A
+ #define hp_fifo_in_addr 0x3B
+
+ #define hp_fifo_out_addr 0x3C
+ #define hp_reserved_3D 0x3D
+ #define hp_reserved_3E 0x3E
+ #define hp_reserved_3F 0x3F
+
+
+
+ extern USHORT default_intena;
+
+ #define hp_intena 0x40
+
+ #define RESET BITW(7)
+ #define PROG_HLT BITW(6)
+ #define PARITY BITW(5)
+ #define FIFO BITW(4)
+ #define SEL BITW(3)
+ #define SCAM_SEL BITW(2)
+ #define RSEL BITW(1)
+ #define TIMEOUT BITW(0)
+ #define BUS_FREE BITW(15)
+ #define XFER_CNT_0 BITW(14)
+ #define PHASE BITW(13)
+ #define IUNKWN BITW(12)
+ #define ICMD_COMP BITW(11)
+ #define ITICKLE BITW(10)
+ #define IDO_STRT BITW(9)
+ #define ITAR_DISC BITW(8)
+ #define AUTO_INT (BITW(12)+BITW(11)+BITW(10)+BITW(9)+BITW(8))
+ #define CLR_ALL_INT 0xFFFF
+ #define CLR_ALL_INT_1 0xFF00
+
+ #define hp_intstat 0x42
+
+ #define hp_scsisig 0x44
+
+ #define SCSI_SEL BIT(7)
+ #define SCSI_BSY BIT(6)
+ #define SCSI_REQ BIT(5)
+ #define SCSI_ACK BIT(4)
+ #define SCSI_ATN BIT(3)
+ #define SCSI_CD BIT(2)
+ #define SCSI_MSG BIT(1)
+ #define SCSI_IOBIT BIT(0)
+
+ #define S_SCSI_PHZ (BIT(2)+BIT(1)+BIT(0))
+ #define S_CMD_PH (BIT(2) )
+ #define S_MSGO_PH (BIT(2)+BIT(1) )
+ #define S_STAT_PH (BIT(2) +BIT(0))
+ #define S_MSGI_PH (BIT(2)+BIT(1)+BIT(0))
+ #define S_DATAI_PH ( BIT(0))
+ #define S_DATAO_PH 0x00
+ #define S_ILL_PH ( BIT(1) )
+
+ #define hp_scsictrl_0 0x45
+
+ #define NO_ARB BIT(7)
+ #define SEL_TAR BIT(6)
+ #define ENA_ATN BIT(4)
+ #define ENA_RESEL BIT(2)
+ #define SCSI_RST BIT(1)
+ #define ENA_SCAM_SEL BIT(0)
+
+
+
+ #define hp_portctrl_0 0x46
+
+ #define SCSI_PORT BIT(7)
+ #define SCSI_INBIT BIT(6)
+ #define DMA_PORT BIT(5)
+ #define DMA_RD BIT(4)
+ #define HOST_PORT BIT(3)
+ #define HOST_WRT BIT(2)
+ #define SCSI_BUS_EN BIT(1)
+ #define START_TO BIT(0)
+
+ #define hp_scsireset 0x47
+
+ #define SCSI_TAR BIT(7)
+ #define SCSI_INI BIT(6)
+ #define SCAM_EN BIT(5)
+ #define ACK_HOLD BIT(4)
+ #define DMA_RESET BIT(3)
+ #define HPSCSI_RESET BIT(2)
+ #define PROG_RESET BIT(1)
+ #define FIFO_CLR BIT(0)
+
+ #define hp_xfercnt_0 0x48
+ #define hp_xfercnt_1 0x49
+ #define hp_xfercnt_2 0x4A
+ #define hp_xfercnt_3 0x4B
+
+ #define hp_fifodata_0 0x4C
+ #define hp_fifodata_1 0x4D
+ #define hp_addstat 0x4E
+
+ #define SCAM_TIMER BIT(7)
+ #define AUTO_RUNNING BIT(6)
+ #define FAST_SYNC BIT(5)
+ #define SCSI_MODE8 BIT(3)
+ #define SCSI_PAR_ERR BIT(0)
+
+ #define hp_prgmcnt_0 0x4F
+
+ #define AUTO_PC_MASK 0x3F
+
+ #define hp_selfid_0 0x50
+ #define hp_selfid_1 0x51
+ #define hp_arb_id 0x52
+
+ #define ARB_ID (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+
+ #define hp_select_id 0x53
+
+ #define RESEL_ID (BIT(7) + BIT(6) + BIT(5) + BIT(4))
+ #define SELECT_ID (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+
+ #define hp_synctarg_base 0x54
+ #define hp_synctarg_12 0x54
+ #define hp_synctarg_13 0x55
+ #define hp_synctarg_14 0x56
+ #define hp_synctarg_15 0x57
+
+ #define hp_synctarg_8 0x58
+ #define hp_synctarg_9 0x59
+ #define hp_synctarg_10 0x5A
+ #define hp_synctarg_11 0x5B
+
+ #define hp_synctarg_4 0x5C
+ #define hp_synctarg_5 0x5D
+ #define hp_synctarg_6 0x5E
+ #define hp_synctarg_7 0x5F
+
+ #define hp_synctarg_0 0x60
+ #define hp_synctarg_1 0x61
+ #define hp_synctarg_2 0x62
+ #define hp_synctarg_3 0x63
+
+ #define RATE_20MB 0x00
+ #define RATE_10MB ( BIT(5))
+ #define RATE_6_6MB ( BIT(6) )
+ #define RATE_5MB ( BIT(6)+BIT(5))
+ #define RATE_4MB (BIT(7) )
+ #define RATE_3_33MB (BIT(7) +BIT(5))
+ #define RATE_2_85MB (BIT(7)+BIT(6) )
+ #define RATE_2_5MB (BIT(7)+BIT(5)+BIT(6))
+ #define NEXT_CLK BIT(5)
+ #define SLOWEST_SYNC (BIT(7)+BIT(6)+BIT(5))
+ #define NARROW_SCSI BIT(4)
+ #define SYNC_OFFSET (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+ #define DEFAULT_ASYNC 0x00
+ #define DEFAULT_OFFSET 0x0F
+
+ #define hp_autostart_0 0x64
+ #define hp_autostart_1 0x65
+ #define hp_autostart_2 0x66
+ #define hp_autostart_3 0x67
+
+
+
+ #define DISABLE 0x00
+ #define AUTO_IMMED BIT(5)
+ #define SELECT BIT(6)
+ #define RESELECT (BIT(6)+BIT(5))
+ #define BUSFREE BIT(7)
+ #define XFER_0 (BIT(7)+BIT(5))
+ #define END_DATA (BIT(7)+BIT(6))
+ #define MSG_PHZ (BIT(7)+BIT(6)+BIT(5))
+
+ #define hp_gp_reg_0 0x68
+ #define hp_gp_reg_1 0x69
+ #define hp_gp_reg_2 0x6A
+ #define hp_gp_reg_3 0x6B
+
+ #define hp_seltimeout 0x6C
+
+
+ #define TO_2ms 0x54 /* 2.0503ms */
+ #define TO_4ms 0x67 /* 3.9959ms */
+
+ #define TO_5ms 0x03 /* 4.9152ms */
+ #define TO_10ms 0x07 /* 11.xxxms */
+ #define TO_250ms 0x99 /* 250.68ms */
+ #define TO_290ms 0xB1 /* 289.99ms */
+ #define TO_350ms 0xD6 /* 350.62ms */
+ #define TO_417ms 0xFF /* 417.79ms */
+
+ #define hp_clkctrl_0 0x6D
+
+ #define PWR_DWN BIT(6)
+ #define ACTdeassert BIT(4)
+ #define ATNonErr BIT(3)
+ #define CLK_30MHZ BIT(1)
+ #define CLK_40MHZ (BIT(1) + BIT(0))
+ #define CLK_50MHZ BIT(2)
+
+ #define CLKCTRL_DEFAULT (ACTdeassert | CLK_40MHZ)
+
+ #define hp_fiforead 0x6E
+ #define hp_fifowrite 0x6F
+
+ #define hp_offsetctr 0x70
+ #define hp_xferstat 0x71
+
+ #define FIFO_FULL BIT(7)
+ #define FIFO_EMPTY BIT(6)
+ #define FIFO_MASK 0x3F /* Mask for the FIFO count value. */
+ #define FIFO_LEN 0x20
+
+ #define hp_portctrl_1 0x72
+
+ #define EVEN_HOST_P BIT(5)
+ #define INVT_SCSI BIT(4)
+ #define CHK_SCSI_P BIT(3)
+ #define HOST_MODE8 BIT(0)
+ #define HOST_MODE16 0x00
+
+ #define hp_xfer_pad 0x73
+
+ #define ID_UNLOCK BIT(3)
+ #define XFER_PAD BIT(2)
+
+ #define hp_scsidata_0 0x74
+ #define hp_scsidata_1 0x75
+ #define hp_timer_0 0x76
+ #define hp_timer_1 0x77
+
+ #define hp_reserved_78 0x78
+ #define hp_reserved_79 0x79
+ #define hp_reserved_7A 0x7A
+ #define hp_reserved_7B 0x7B
+
+ #define hp_reserved_7C 0x7C
+ #define hp_reserved_7D 0x7D
+ #define hp_reserved_7E 0x7E
+ #define hp_reserved_7F 0x7F
+
+ #define hp_aramBase 0x80
+ #define BIOS_DATA_OFFSET 0x60
+ #define BIOS_RELATIVE_CARD 0x64
+
+
+
+
+ #define AUTO_LEN 0x80
+ #define AR0 0x00
+ #define AR1 BITW(8)
+ #define AR2 BITW(9)
+ #define AR3 (BITW(9) + BITW(8))
+ #define SDATA BITW(10)
+
+ #define NOP_OP 0x00 /* Nop command */
+
+ #define CRD_OP BITW(11) /* Cmp Reg. w/ Data */
+
+ #define CRR_OP BITW(12) /* Cmp Reg. w. Reg. */
+
+ #define CBE_OP (BITW(14)+BITW(12)+BITW(11)) /* Cmp SCSI cmd class & Branch EQ */
+
+ #define CBN_OP (BITW(14)+BITW(13)) /* Cmp SCSI cmd class & Branch NOT EQ */
+
+ #define CPE_OP (BITW(14)+BITW(11)) /* Cmp SCSI phs & Branch EQ */
+
+ #define CPN_OP (BITW(14)+BITW(12)) /* Cmp SCSI phs & Branch NOT EQ */
+
+
+ #define ADATA_OUT 0x00
+ #define ADATA_IN BITW(8)
+ #define ACOMMAND BITW(10)
+ #define ASTATUS (BITW(10)+BITW(8))
+ #define AMSG_OUT (BITW(10)+BITW(9))
+ #define AMSG_IN (BITW(10)+BITW(9)+BITW(8))
+ #define AILLEGAL (BITW(9)+BITW(8))
+
+
+ #define BRH_OP BITW(13) /* Branch */
+
+
+ #define ALWAYS 0x00
+ #define EQUAL BITW(8)
+ #define NOT_EQ BITW(9)
+
+ #define TCB_OP (BITW(13)+BITW(11)) /* Test condition & branch */
+
+
+ #define ATN_SET BITW(8)
+ #define ATN_RESET BITW(9)
+ #define XFER_CNT (BITW(9)+BITW(8))
+ #define FIFO_0 BITW(10)
+ #define FIFO_NOT0 (BITW(10)+BITW(8))
+ #define T_USE_SYNC0 (BITW(10)+BITW(9))
+
+
+ #define MPM_OP BITW(15) /* Match phase and move data */
+
+ #define MDR_OP (BITW(12)+BITW(11)) /* Move data to Reg. */
+
+ #define MRR_OP BITW(14) /* Move DReg. to Reg. */
+
+
+ #define S_IDREG (BIT(2)+BIT(1)+BIT(0))
+
+
+ #define D_AR0 0x00
+ #define D_AR1 BIT(0)
+ #define D_AR2 BIT(1)
+ #define D_AR3 (BIT(1) + BIT(0))
+ #define D_SDATA BIT(2)
+ #define D_BUCKET (BIT(2) + BIT(1) + BIT(0))
+
+
+ #define ADR_OP (BITW(13)+BITW(12)) /* Logical AND Reg. w. Data */
+
+ #define ADS_OP (BITW(14)+BITW(13)+BITW(12))
+
+ #define ODR_OP (BITW(13)+BITW(12)+BITW(11))
+
+ #define ODS_OP (BITW(14)+BITW(13)+BITW(12)+BITW(11))
+
+ #define STR_OP (BITW(15)+BITW(14)) /* Store to A_Reg. */
+
+ #define AINT_ENA1 0x00
+ #define AINT_STAT1 BITW(8)
+ #define ASCSI_SIG BITW(9)
+ #define ASCSI_CNTL (BITW(9)+BITW(8))
+ #define APORT_CNTL BITW(10)
+ #define ARST_CNTL (BITW(10)+BITW(8))
+ #define AXFERCNT0 (BITW(10)+BITW(9))
+ #define AXFERCNT1 (BITW(10)+BITW(9)+BITW(8))
+ #define AXFERCNT2 BITW(11)
+ #define AFIFO_DATA (BITW(11)+BITW(8))
+ #define ASCSISELID (BITW(11)+BITW(9))
+ #define ASCSISYNC0 (BITW(11)+BITW(9)+BITW(8))
+
+
+ #define RAT_OP (BITW(14)+BITW(13)+BITW(11))
+
+ #define SSI_OP (BITW(15)+BITW(11))
+
+
+ #define SSI_ITAR_DISC (ITAR_DISC >> 8)
+ #define SSI_IDO_STRT (IDO_STRT >> 8)
+ #define SSI_IDI_STRT (IDO_STRT >> 8)
+
+ #define SSI_ICMD_COMP (ICMD_COMP >> 8)
+ #define SSI_ITICKLE (ITICKLE >> 8)
+
+ #define SSI_IUNKWN (IUNKWN >> 8)
+ #define SSI_INO_CC (IUNKWN >> 8)
+ #define SSI_IRFAIL (IUNKWN >> 8)
+
+
+ #define NP 0x10 /*Next Phase */
+ #define NTCMD 0x02 /*Non- Tagged Command start */
+ #define CMDPZ 0x04 /*Command phase */
+ #define DINT 0x12 /*Data Out/In interrupt */
+ #define DI 0x13 /*Data Out */
+ #define MI 0x14 /*Message In */
+ #define DC 0x19 /*Disconnect Message */
+ #define ST 0x1D /*Status Phase */
+ #define UNKNWN 0x24 /*Unknown bus action */
+ #define CC 0x25 /*Command Completion failure */
+ #define TICK 0x26 /*New target reselected us. */
+ #define RFAIL 0x27 /*Reselection failed */
+ #define SELCHK 0x28 /*Select & Check SCSI ID latch reg */
+
+
+ #define ID_MSG_STRT hp_aramBase + 0x00
+ #define NON_TAG_ID_MSG hp_aramBase + 0x06
+ #define CMD_STRT hp_aramBase + 0x08
+ #define SYNC_MSGS hp_aramBase + 0x08
+
+
+
+
+
+ #define TAG_STRT 0x00
+ #define SELECTION_START 0x00
+ #define DISCONNECT_START 0x10/2
+ #define END_DATA_START 0x14/2
+ #define NONTAG_STRT 0x02/2
+ #define CMD_ONLY_STRT CMDPZ/2
+ #define TICKLE_STRT TICK/2
+ #define SELCHK_STRT SELCHK/2
+
+
+
+
+#define mEEPROM_CLK_DELAY(port) (RD_HARPOON(port+hp_intstat_1))
+
+#define mWAIT_10MS(port) (RD_HARPOON(port+hp_intstat_1))
+
+
+#define CLR_XFER_CNT(port) (WR_HARPOON(port+hp_xfercnt_0, 0x00))
+
+#define SET_XFER_CNT(port, data) (WR_HARP32(port,hp_xfercnt_0,data))
+
+#define GET_XFER_CNT(port, xfercnt) {RD_HARP32(port,hp_xfercnt_0,xfercnt); xfercnt &= 0xFFFFFF;}
+/* #define GET_XFER_CNT(port, xfercnt) (xfercnt = RD_HARPOON(port+hp_xfercnt_2), \
+ xfercnt <<= 16,\
+ xfercnt |= RDW_HARPOON((USHORT)(port+hp_xfercnt_0)))
+ */
+#if defined(DOS)
+#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((USHORT)(port+hp_host_addr_lo), (USHORT)(addr & 0x0000FFFFL)),\
+ addr >>= 16,\
+ WRW_HARPOON((USHORT)(port+hp_host_addr_hmi), (USHORT)(addr & 0x0000FFFFL)),\
+ WR_HARP32(port,hp_xfercnt_0,count),\
+ WRW_HARPOON((USHORT)(port+hp_xfer_cnt_lo), (USHORT)(count & 0x0000FFFFL)),\
+ count >>= 16,\
+ WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))
+#else
+#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((port+hp_host_addr_lo), (USHORT)(addr & 0x0000FFFFL)),\
+ addr >>= 16,\
+ WRW_HARPOON((port+hp_host_addr_hmi), (USHORT)(addr & 0x0000FFFFL)),\
+ WR_HARP32(port,hp_xfercnt_0,count),\
+ WRW_HARPOON((port+hp_xfer_cnt_lo), (USHORT)(count & 0x0000FFFFL)),\
+ count >>= 16,\
+ WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))
+#endif
+
+#define ACCEPT_MSG(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, S_ILL_PH);}
+
+
+#define ACCEPT_MSG_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}
+
+#define ACCEPT_STAT(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, S_ILL_PH);}
+
+#define ACCEPT_STAT_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}
+
+#define DISABLE_AUTO(port) (WR_HARPOON(port+hp_scsireset, PROG_RESET),\
+ WR_HARPOON(port+hp_scsireset, 0x00))
+
+#define ARAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) | SGRAM_ARAM)))
+
+#define SGRAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~SGRAM_ARAM)))
+
+#define MDISABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE)))
+
+#define MENABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)))
+
+
+
+#endif
+
+
+#if (FW_TYPE==_UCB_MGR_)
+void ReadNVRam(PSCCBcard pCurrCard,PUCB p_ucb);
+void WriteNVRam(PSCCBcard pCurrCard,PUCB p_ucb);
+void UpdateCheckSum(u32bits baseport);
+#endif // (FW_TYPE==_UCB_MGR_)
+
+#if defined(DOS)
+UCHAR sfm(USHORT port, PSCCB pcurrSCCB);
+void scsiStartAuto(USHORT port);
+UCHAR sisyncn(USHORT port, UCHAR p_card, UCHAR syncFlag);
+void ssel(USHORT port, UCHAR p_card);
+void sres(USHORT port, UCHAR p_card, PSCCBcard pCurrCard);
+void sdecm(UCHAR message, USHORT port, UCHAR p_card);
+void shandem(USHORT port, UCHAR p_card,PSCCB pCurrSCCB);
+void stsyncn(USHORT port, UCHAR p_card);
+void sisyncr(USHORT port,UCHAR sync_pulse, UCHAR offset);
+void sssyncv(USHORT p_port, UCHAR p_id, UCHAR p_sync_value, PSCCBMgr_tar_info currTar_Info);
+void sresb(USHORT port, UCHAR p_card);
+void sxfrp(USHORT p_port, UCHAR p_card);
+void schkdd(USHORT port, UCHAR p_card);
+UCHAR RdStack(USHORT port, UCHAR index);
+void WrStack(USHORT portBase, UCHAR index, UCHAR data);
+UCHAR ChkIfChipInitialized(USHORT ioPort);
+
+#if defined(V302)
+UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun);
+#endif
+
+void SendMsg(USHORT port, UCHAR message);
+void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code);
+UCHAR scsellDOS(USHORT p_port, UCHAR targ_id);
+#else
+UCHAR sfm(ULONG port, PSCCB pcurrSCCB);
+void scsiStartAuto(ULONG port);
+UCHAR sisyncn(ULONG port, UCHAR p_card, UCHAR syncFlag);
+void ssel(ULONG port, UCHAR p_card);
+void sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard);
+void sdecm(UCHAR message, ULONG port, UCHAR p_card);
+void shandem(ULONG port, UCHAR p_card,PSCCB pCurrSCCB);
+void stsyncn(ULONG port, UCHAR p_card);
+void sisyncr(ULONG port,UCHAR sync_pulse, UCHAR offset);
+void sssyncv(ULONG p_port, UCHAR p_id, UCHAR p_sync_value, PSCCBMgr_tar_info currTar_Info);
+void sresb(ULONG port, UCHAR p_card);
+void sxfrp(ULONG p_port, UCHAR p_card);
+void schkdd(ULONG port, UCHAR p_card);
+UCHAR RdStack(ULONG port, UCHAR index);
+void WrStack(ULONG portBase, UCHAR index, UCHAR data);
+UCHAR ChkIfChipInitialized(ULONG ioPort);
+
+#if defined(V302)
+UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tar, PUCHAR lun);
+#endif
+
+void SendMsg(ULONG port, UCHAR message);
+void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code);
+#endif
+
+void ssenss(PSCCBcard pCurrCard);
+void sinits(PSCCB p_sccb, UCHAR p_card);
+void RNVRamData(PNVRamInfo pNvRamInfo);
+
+#if defined(WIDE_SCSI)
+ #if defined(DOS)
+ UCHAR siwidn(USHORT port, UCHAR p_card);
+ void stwidn(USHORT port, UCHAR p_card);
+ void siwidr(USHORT port, UCHAR width);
+ #else
+ UCHAR siwidn(ULONG port, UCHAR p_card);
+ void stwidn(ULONG port, UCHAR p_card);
+ void siwidr(ULONG port, UCHAR width);
+ #endif
+#endif
+
+
+void queueSelectFail(PSCCBcard pCurrCard, UCHAR p_card);
+void queueDisconnect(PSCCB p_SCCB, UCHAR p_card);
+void queueCmdComplete(PSCCBcard pCurrCard, PSCCB p_SCCB, UCHAR p_card);
+void queueSearchSelect(PSCCBcard pCurrCard, UCHAR p_card);
+void queueFlushSccb(UCHAR p_card, UCHAR error_code);
+void queueAddSccb(PSCCB p_SCCB, UCHAR card);
+UCHAR queueFindSccb(PSCCB p_SCCB, UCHAR p_card);
+void utilUpdateResidual(PSCCB p_SCCB);
+USHORT CalcCrc16(UCHAR buffer[]);
+UCHAR CalcLrc(UCHAR buffer[]);
+
+
+#if defined(DOS)
+void Wait1Second(USHORT p_port);
+void Wait(USHORT p_port, UCHAR p_delay);
+void utilEEWriteOnOff(USHORT p_port,UCHAR p_mode);
+void utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr);
+USHORT utilEERead(USHORT p_port, USHORT ee_addr);
+USHORT utilEEReadOrg(USHORT p_port, USHORT ee_addr);
+void utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr);
+#else
+void Wait1Second(ULONG p_port);
+void Wait(ULONG p_port, UCHAR p_delay);
+void utilEEWriteOnOff(ULONG p_port,UCHAR p_mode);
+void utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr);
+USHORT utilEERead(ULONG p_port, USHORT ee_addr);
+USHORT utilEEReadOrg(ULONG p_port, USHORT ee_addr);
+void utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr);
+#endif
+
+
+
+#if defined(OS2)
+ void far phaseDataOut(ULONG port, UCHAR p_card);
+ void far phaseDataIn(ULONG port, UCHAR p_card);
+ void far phaseCommand(ULONG port, UCHAR p_card);
+ void far phaseStatus(ULONG port, UCHAR p_card);
+ void far phaseMsgOut(ULONG port, UCHAR p_card);
+ void far phaseMsgIn(ULONG port, UCHAR p_card);
+ void far phaseIllegal(ULONG port, UCHAR p_card);
+#else
+ #if defined(DOS)
+ void phaseDataOut(USHORT port, UCHAR p_card);
+ void phaseDataIn(USHORT port, UCHAR p_card);
+ void phaseCommand(USHORT port, UCHAR p_card);
+ void phaseStatus(USHORT port, UCHAR p_card);
+ void phaseMsgOut(USHORT port, UCHAR p_card);
+ void phaseMsgIn(USHORT port, UCHAR p_card);
+ void phaseIllegal(USHORT port, UCHAR p_card);
+ #else
+ void phaseDataOut(ULONG port, UCHAR p_card);
+ void phaseDataIn(ULONG port, UCHAR p_card);
+ void phaseCommand(ULONG port, UCHAR p_card);
+ void phaseStatus(ULONG port, UCHAR p_card);
+ void phaseMsgOut(ULONG port, UCHAR p_card);
+ void phaseMsgIn(ULONG port, UCHAR p_card);
+ void phaseIllegal(ULONG port, UCHAR p_card);
+ #endif
+#endif
+
+#if defined(DOS)
+void phaseDecode(USHORT port, UCHAR p_card);
+void phaseChkFifo(USHORT port, UCHAR p_card);
+void phaseBusFree(USHORT p_port, UCHAR p_card);
+#else
+void phaseDecode(ULONG port, UCHAR p_card);
+void phaseChkFifo(ULONG port, UCHAR p_card);
+void phaseBusFree(ULONG p_port, UCHAR p_card);
+#endif
+
+
+
+
+#if defined(DOS)
+void XbowInit(USHORT port, UCHAR scamFlg);
+void BusMasterInit(USHORT p_port);
+int DiagXbow(USHORT port);
+int DiagBusMaster(USHORT port);
+void DiagEEPROM(USHORT p_port);
+#else
+void XbowInit(ULONG port, UCHAR scamFlg);
+void BusMasterInit(ULONG p_port);
+int DiagXbow(ULONG port);
+int DiagBusMaster(ULONG port);
+void DiagEEPROM(ULONG p_port);
+#endif
+
+
+
+
+#if defined(DOS)
+void busMstrAbort(USHORT port);
+UCHAR busMstrTimeOut(USHORT port);
+void dataXferProcessor(USHORT port, PSCCBcard pCurrCard);
+void busMstrSGDataXferStart(USHORT port, PSCCB pCurrSCCB);
+void busMstrDataXferStart(USHORT port, PSCCB pCurrSCCB);
+void hostDataXferAbort(USHORT port, UCHAR p_card, PSCCB pCurrSCCB);
+#else
+void busMstrAbort(ULONG port);
+UCHAR busMstrTimeOut(ULONG port);
+void dataXferProcessor(ULONG port, PSCCBcard pCurrCard);
+void busMstrSGDataXferStart(ULONG port, PSCCB pCurrSCCB);
+void busMstrDataXferStart(ULONG port, PSCCB pCurrSCCB);
+void hostDataXferAbort(ULONG port, UCHAR p_card, PSCCB pCurrSCCB);
+#endif
+void hostDataXferRestart(PSCCB currSCCB);
+
+
+#if defined (DOS)
+UCHAR SccbMgr_bad_isr(USHORT p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int);
+#else
+UCHAR SccbMgr_bad_isr(ULONG p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int);
+
+#endif
+
+void SccbMgrTableInitAll(void);
+void SccbMgrTableInitCard(PSCCBcard pCurrCard, UCHAR p_card);
+void SccbMgrTableInitTarget(UCHAR p_card, UCHAR target);
+
+
+
+void scini(UCHAR p_card, UCHAR p_our_id, UCHAR p_power_up);
+
+#if defined(DOS)
+int scarb(USHORT p_port, UCHAR p_sel_type);
+void scbusf(USHORT p_port);
+void scsel(USHORT p_port);
+void scasid(UCHAR p_card, USHORT p_port);
+UCHAR scxferc(USHORT p_port, UCHAR p_data);
+UCHAR scsendi(USHORT p_port, UCHAR p_id_string[]);
+UCHAR sciso(USHORT p_port, UCHAR p_id_string[]);
+void scwirod(USHORT p_port, UCHAR p_data_bit);
+void scwiros(USHORT p_port, UCHAR p_data_bit);
+UCHAR scvalq(UCHAR p_quintet);
+UCHAR scsell(USHORT p_port, UCHAR targ_id);
+void scwtsel(USHORT p_port);
+void inisci(UCHAR p_card, USHORT p_port, UCHAR p_our_id);
+void scsavdi(UCHAR p_card, USHORT p_port);
+#else
+int scarb(ULONG p_port, UCHAR p_sel_type);
+void scbusf(ULONG p_port);
+void scsel(ULONG p_port);
+void scasid(UCHAR p_card, ULONG p_port);
+UCHAR scxferc(ULONG p_port, UCHAR p_data);
+UCHAR scsendi(ULONG p_port, UCHAR p_id_string[]);
+UCHAR sciso(ULONG p_port, UCHAR p_id_string[]);
+void scwirod(ULONG p_port, UCHAR p_data_bit);
+void scwiros(ULONG p_port, UCHAR p_data_bit);
+UCHAR scvalq(UCHAR p_quintet);
+UCHAR scsell(ULONG p_port, UCHAR targ_id);
+void scwtsel(ULONG p_port);
+void inisci(UCHAR p_card, ULONG p_port, UCHAR p_our_id);
+void scsavdi(UCHAR p_card, ULONG p_port);
+#endif
+UCHAR scmachid(UCHAR p_card, UCHAR p_id_string[]);
+
+
+#if defined(DOS)
+void autoCmdCmplt(USHORT p_port, UCHAR p_card);
+void autoLoadDefaultMap(USHORT p_port);
+#else
+void autoCmdCmplt(ULONG p_port, UCHAR p_card);
+void autoLoadDefaultMap(ULONG p_port);
+#endif
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+ void OS_start_timer(unsigned long ioport, unsigned long timeout);
+ void OS_stop_timer(unsigned long ioport, unsigned long timeout);
+ void OS_disable_int(unsigned char intvec);
+ void OS_enable_int(unsigned char intvec);
+ void OS_delay(unsigned long count);
+ int OS_VirtToPhys(u32bits CardHandle, u32bits *physaddr, u32bits *virtaddr);
+ #if !(defined(UNIX) || defined(OS2) || defined(SOLARIS_REAL_MODE))
+ void OS_Lock(PSCCBMGR_INFO pCardInfo);
+ void OS_UnLock(PSCCBMGR_INFO pCardInfo);
+#endif // if FW_TYPE == ...
+
+#endif
+
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+
+
+#if defined(OS2)
+ extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+ #else
+ extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+ #endif
+#endif
+
+extern SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#if defined(DOS) || defined(OS2)
+extern UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+extern UCHAR scamHAString[];
+
+
+extern UCHAR mbCards;
+#if defined(BUGBUG)
+extern UCHAR debug_int[MAX_CARDS][debug_size];
+extern UCHAR debug_index[MAX_CARDS];
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+#if (FW_TYPE==_SCCB_MGR_)
+#if defined(DOS)
+ extern UCHAR first_time;
+#endif
+#endif /* (FW_TYPE==_SCCB_MGR_) */
+
+#if (FW_TYPE==_UCB_MGR_)
+#if defined(DOS)
+ extern u08bits first_time;
+#endif
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+extern unsigned int SccbGlobalFlags;
+
+
+#ident "$Id: sccb.c 1.18 1997/06/10 16:47:04 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: sccb.c $
+ *
+ * Description: Functions relating to handling of the SCCB interface
+ * between the device driver and the HARPOON.
+ *
+ * $Date: 1997/06/10 16:47:04 $
+ *
+ * $Revision: 1.18 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+ /*#include <budioctl.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <eeprom.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+#define mOS_Lock(card) OS_Lock((PSCCBMGR_INFO)(((PSCCBcard)card)->cardInfo))
+#define mOS_UnLock(card) OS_UnLock((PSCCBMGR_INFO)(((PSCCBcard)card)->cardInfo))
+#else /* FW_TYPE==_UCB_MGR_ */
+#define mOS_Lock(card) OS_Lock((u32bits)(((PSCCBcard)card)->ioPort))
+#define mOS_UnLock(card) OS_UnLock((u32bits)(((PSCCBcard)card)->ioPort))
+#endif
+
+
+/*
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+extern UCHAR mbCards;
+
+#if defined (OS2)
+ extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+ #else
+ extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+ #endif
+#endif
+
+
+#if defined(BUGBUG)
+extern UCHAR debug_int[MAX_CARDS][debug_size];
+extern UCHAR debug_index[MAX_CARDS];
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+*/
+
+#if (FW_TYPE==_SCCB_MGR_)
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_sense_adapter
+ *
+ * Description: Setup and/or Search for cards and return info to caller.
+ *
+ *---------------------------------------------------------------------*/
+
+int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo)
+{
+#if defined(DOS)
+#else
+ static UCHAR first_time = 1;
+#endif
+
+ UCHAR i,j,id,ScamFlg;
+ USHORT temp,temp2,temp3,temp4,temp5,temp6;
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+ PNVRamInfo pCurrNvRam;
+
+#if defined(DOS)
+ ioport = (USHORT)pCardInfo->si_baseaddr;
+#else
+ ioport = pCardInfo->si_baseaddr;
+#endif
+
+
+ if (RD_HARPOON(ioport+hp_vendor_id_0) != ORION_VEND_0)
+ return((int)FAILURE);
+
+ if ((RD_HARPOON(ioport+hp_vendor_id_1) != ORION_VEND_1))
+ return((int)FAILURE);
+
+ if ((RD_HARPOON(ioport+hp_device_id_0) != ORION_DEV_0))
+ return((int)FAILURE);
+
+ if ((RD_HARPOON(ioport+hp_device_id_1) != ORION_DEV_1))
+ return((int)FAILURE);
+
+
+ if (RD_HARPOON(ioport+hp_rev_num) != 0x0f){
+
+/* For new Harpoon then check for sub_device ID LSB
+ the bits(0-3) must be all ZERO for compatible with
+ current version of SCCBMgr, else skip this Harpoon
+ device. */
+
+ if (RD_HARPOON(ioport+hp_sub_device_id_0) & 0x0f)
+ return((int)FAILURE);
+ }
+
+ if (first_time)
+ {
+ SccbMgrTableInitAll();
+ first_time = 0;
+ mbCards = 0;
+ }
+
+ if(RdStack(ioport, 0) != 0x00) {
+ if(ChkIfChipInitialized(ioport) == FALSE)
+ {
+ pCurrNvRam = NULL;
+ WR_HARPOON(ioport+hp_semaphore, 0x00);
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+ }
+ else
+ {
+ if(mbCards < MAX_MB_CARDS) {
+ pCurrNvRam = &nvRamInfo[mbCards];
+ mbCards++;
+ pCurrNvRam->niBaseAddr = ioport;
+ RNVRamData(pCurrNvRam);
+ }else
+ return((int) FAILURE);
+ }
+ }else
+ pCurrNvRam = NULL;
+#if defined (NO_BIOS_OPTION)
+ pCurrNvRam = NULL;
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+#endif /* No BIOS Option */
+
+ WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+
+ if(pCurrNvRam)
+ pCardInfo->si_id = pCurrNvRam->niAdapId;
+ else
+ pCardInfo->si_id = (UCHAR)(utilEERead(ioport, (ADAPTER_SCSI_ID/2)) &
+ (UCHAR)0x0FF);
+
+ pCardInfo->si_lun = 0x00;
+ pCardInfo->si_fw_revision = ORION_FW_REV;
+ temp2 = 0x0000;
+ temp3 = 0x0000;
+ temp4 = 0x0000;
+ temp5 = 0x0000;
+ temp6 = 0x0000;
+
+ for (id = 0; id < (16/2); id++) {
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (USHORT)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++) {
+
+ temp2 >>= 1;
+ temp3 >>= 1;
+ temp4 >>= 1;
+ temp5 >>= 1;
+ temp6 >>= 1;
+ switch (temp & 0x3)
+ {
+ case AUTO_RATE_20: /* Synchronous, 20 mega-transfers/second */
+ temp6 |= 0x8000; /* Fall through */
+ case AUTO_RATE_10: /* Synchronous, 10 mega-transfers/second */
+ temp5 |= 0x8000; /* Fall through */
+ case AUTO_RATE_05: /* Synchronous, 5 mega-transfers/second */
+ temp2 |= 0x8000; /* Fall through */
+ case AUTO_RATE_00: /* Asynchronous */
+ break;
+ }
+
+ if (temp & DISC_ENABLE_BIT)
+ temp3 |= 0x8000;
+
+ if (temp & WIDE_NEGO_BIT)
+ temp4 |= 0x8000;
+
+ }
+ }
+
+ pCardInfo->si_per_targ_init_sync = temp2;
+ pCardInfo->si_per_targ_no_disc = temp3;
+ pCardInfo->si_per_targ_wide_nego = temp4;
+ pCardInfo->si_per_targ_fast_nego = temp5;
+ pCardInfo->si_per_targ_ultra_nego = temp6;
+
+ if(pCurrNvRam)
+ i = pCurrNvRam->niSysConf;
+ else
+ i = (UCHAR)(utilEERead(ioport, (SYSTEM_CONFIG/2)));
+
+ if(pCurrNvRam)
+ ScamFlg = pCurrNvRam->niScamConf;
+ else
+ ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+
+ pCardInfo->si_flags = 0x0000;
+
+ if (i & 0x01)
+ pCardInfo->si_flags |= SCSI_PARITY_ENA;
+
+ if (!(i & 0x02))
+ pCardInfo->si_flags |= SOFT_RESET;
+
+ if (i & 0x10)
+ pCardInfo->si_flags |= EXTENDED_TRANSLATION;
+
+ if (ScamFlg & SCAM_ENABLED)
+ pCardInfo->si_flags |= FLAG_SCAM_ENABLED;
+
+ if (ScamFlg & SCAM_LEVEL2)
+ pCardInfo->si_flags |= FLAG_SCAM_LEVEL2;
+
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & 0x04) {
+ j |= SCSI_TERM_ENA_L;
+ }
+ WR_HARPOON(ioport+hp_bm_ctrl, j );
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & 0x08) {
+ j |= SCSI_TERM_ENA_H;
+ }
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+ if (!(RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD))
+
+ pCardInfo->si_flags |= SUPPORT_16TAR_32LUN;
+
+ pCardInfo->si_card_family = HARPOON_FAMILY;
+ pCardInfo->si_bustype = BUSTYPE_PCI;
+
+ if(pCurrNvRam){
+ pCardInfo->si_card_model[0] = '9';
+ switch(pCurrNvRam->niModel & 0x0f){
+ case MODEL_LT:
+ pCardInfo->si_card_model[1] = '3';
+ pCardInfo->si_card_model[2] = '0';
+ break;
+ case MODEL_LW:
+ pCardInfo->si_card_model[1] = '5';
+ pCardInfo->si_card_model[2] = '0';
+ break;
+ case MODEL_DL:
+ pCardInfo->si_card_model[1] = '3';
+ pCardInfo->si_card_model[2] = '2';
+ break;
+ case MODEL_DW:
+ pCardInfo->si_card_model[1] = '5';
+ pCardInfo->si_card_model[2] = '2';
+ break;
+ }
+ }else{
+ temp = utilEERead(ioport, (MODEL_NUMB_0/2));
+ pCardInfo->si_card_model[0] = (UCHAR)(temp >> 8);
+ temp = utilEERead(ioport, (MODEL_NUMB_2/2));
+
+ pCardInfo->si_card_model[1] = (UCHAR)(temp & 0x00FF);
+ pCardInfo->si_card_model[2] = (UCHAR)(temp >> 8);
+ }
+
+ if (pCardInfo->si_card_model[1] == '3')
+ {
+ if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+ pCardInfo->si_flags |= LOW_BYTE_TERM;
+ }
+ else if (pCardInfo->si_card_model[2] == '0')
+ {
+ temp = RD_HARPOON(ioport+hp_xfer_pad);
+ WR_HARPOON(ioport+hp_xfer_pad, (temp & ~BIT(4)));
+ if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+ pCardInfo->si_flags |= LOW_BYTE_TERM;
+ WR_HARPOON(ioport+hp_xfer_pad, (temp | BIT(4)));
+ if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+ pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ WR_HARPOON(ioport+hp_xfer_pad, temp);
+ }
+ else
+ {
+ temp = RD_HARPOON(ioport+hp_ee_ctrl);
+ temp2 = RD_HARPOON(ioport+hp_xfer_pad);
+ WR_HARPOON(ioport+hp_ee_ctrl, (temp | SEE_CS));
+ WR_HARPOON(ioport+hp_xfer_pad, (temp2 | BIT(4)));
+ temp3 = 0;
+ for (i = 0; i < 8; i++)
+ {
+ temp3 <<= 1;
+ if (!(RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7)))
+ temp3 |= 1;
+ WR_HARPOON(ioport+hp_xfer_pad, (temp2 & ~BIT(4)));
+ WR_HARPOON(ioport+hp_xfer_pad, (temp2 | BIT(4)));
+ }
+ WR_HARPOON(ioport+hp_ee_ctrl, temp);
+ WR_HARPOON(ioport+hp_xfer_pad, temp2);
+ if (!(temp3 & BIT(7)))
+ pCardInfo->si_flags |= LOW_BYTE_TERM;
+ if (!(temp3 & BIT(6)))
+ pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ }
+
+
+ ARAM_ACCESS(ioport);
+
+ for ( i = 0; i < 4; i++ ) {
+
+ pCardInfo->si_XlatInfo[i] =
+ RD_HARPOON(ioport+hp_aramBase+BIOS_DATA_OFFSET+i);
+ }
+
+ /* return with -1 if no sort, else return with
+ logical card number sorted by BIOS (zero-based) */
+
+ pCardInfo->si_relative_cardnum =
+ (UCHAR)(RD_HARPOON(ioport+hp_aramBase+BIOS_RELATIVE_CARD)-1);
+
+ SGRAM_ACCESS(ioport);
+
+ s_PhaseTbl[0] = phaseDataOut;
+ s_PhaseTbl[1] = phaseDataIn;
+ s_PhaseTbl[2] = phaseIllegal;
+ s_PhaseTbl[3] = phaseIllegal;
+ s_PhaseTbl[4] = phaseCommand;
+ s_PhaseTbl[5] = phaseStatus;
+ s_PhaseTbl[6] = phaseMsgOut;
+ s_PhaseTbl[7] = phaseMsgIn;
+
+ pCardInfo->si_present = 0x01;
+
+#if defined(BUGBUG)
+
+
+ for (i = 0; i < MAX_CARDS; i++) {
+
+ for (id=0; id<debug_size; id++)
+ debug_int[i][id] = (UCHAR)0x00;
+ debug_index[i] = 0;
+ }
+
+#endif
+
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_config_adapter
+ *
+ * Description: Setup adapter for normal operation (hard reset).
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo)
+#else
+ULONG SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo)
+#endif
+{
+ PSCCBcard CurrCard = NULL;
+ PNVRamInfo pCurrNvRam;
+ UCHAR i,j,thisCard, ScamFlg;
+ USHORT temp,sync_bit_map,id;
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+#if defined(DOS)
+ ioport = (USHORT)pCardInfo->si_baseaddr;
+#else
+ ioport = pCardInfo->si_baseaddr;
+#endif
+
+ for(thisCard =0; thisCard <= MAX_CARDS; thisCard++) {
+
+ if (thisCard == MAX_CARDS) {
+
+ return(FAILURE);
+ }
+
+ if (BL_Card[thisCard].ioPort == ioport) {
+
+ CurrCard = &BL_Card[thisCard];
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ break;
+ }
+
+ else if (BL_Card[thisCard].ioPort == 0x00) {
+
+ BL_Card[thisCard].ioPort = ioport;
+ CurrCard = &BL_Card[thisCard];
+
+ if(mbCards)
+ for(i = 0; i < mbCards; i++){
+ if(CurrCard->ioPort == nvRamInfo[i].niBaseAddr)
+ CurrCard->pNvRamInfo = &nvRamInfo[i];
+ }
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ CurrCard->cardIndex = thisCard;
+ CurrCard->cardInfo = pCardInfo;
+
+ break;
+ }
+ }
+
+ pCurrNvRam = CurrCard->pNvRamInfo;
+
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+ }
+
+
+ BusMasterInit(ioport);
+ XbowInit(ioport, ScamFlg);
+
+#if defined (NO_BIOS_OPTION)
+
+
+ if (DiagXbow(ioport)) return(FAILURE);
+ if (DiagBusMaster(ioport)) return(FAILURE);
+
+#endif /* No BIOS Option */
+
+ autoLoadDefaultMap(ioport);
+
+
+ for (i = 0,id = 0x01; i != pCardInfo->si_id; i++,id <<= 1){}
+
+ WR_HARPOON(ioport+hp_selfid_0, id);
+ WR_HARPOON(ioport+hp_selfid_1, 0x00);
+ WR_HARPOON(ioport+hp_arb_id, pCardInfo->si_id);
+ CurrCard->ourId = pCardInfo->si_id;
+
+ i = (UCHAR) pCardInfo->si_flags;
+ if (i & SCSI_PARITY_ENA)
+ WR_HARPOON(ioport+hp_portctrl_1,(HOST_MODE8 | CHK_SCSI_P));
+
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & LOW_BYTE_TERM)
+ j |= SCSI_TERM_ENA_L;
+ WR_HARPOON(ioport+hp_bm_ctrl, j);
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & HIGH_BYTE_TERM)
+ j |= SCSI_TERM_ENA_H;
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+
+ if (!(pCardInfo->si_flags & SOFT_RESET)) {
+
+ sresb(ioport,thisCard);
+
+ scini(thisCard, pCardInfo->si_id, 0);
+ }
+
+
+
+ if (pCardInfo->si_flags & POST_ALL_UNDERRRUNS)
+ CurrCard->globalFlags |= F_NO_FILTER;
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niSysConf & 0x10)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+ else{
+ if (utilEERead(ioport, (SYSTEM_CONFIG/2)) & GREEN_PC_ENA)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+
+ /* Set global flag to indicate Re-Negotiation to be done on all
+ ckeck condition */
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x04)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & RENEGO_ENA)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x08)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & CONNIO_ENA)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+
+
+ temp = pCardInfo->si_per_targ_no_disc;
+
+ for (i = 0,id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {
+
+ if (temp & id)
+ sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
+ }
+
+ sync_bit_map = 0x0001;
+
+ for (id = 0; id < (MAX_SCSI_TAR/2); id++) {
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (USHORT)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++) {
+
+ if (pCardInfo->si_per_targ_init_sync & sync_bit_map) {
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue = (UCHAR)temp;
+ }
+
+ else {
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= SYNC_SUPPORTED;
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue =
+ (UCHAR)(temp & ~EE_SYNC_MASK);
+ }
+
+#if defined(WIDE_SCSI)
+/* if ((pCardInfo->si_per_targ_wide_nego & sync_bit_map) ||
+ (id*2+i >= 8)){
+*/
+ if (pCardInfo->si_per_targ_wide_nego & sync_bit_map){
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue |= EE_WIDE_SCSI;
+
+ }
+
+ else { /* NARROW SCSI */
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+ }
+
+#else
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+#endif
+
+
+ sync_bit_map <<= 1;
+
+
+
+ }
+ }
+
+ WR_HARPOON((ioport+hp_semaphore),
+ (UCHAR)(RD_HARPOON((ioport+hp_semaphore)) | SCCB_MGR_PRESENT));
+
+#if defined(DOS)
+ return((USHORT)CurrCard);
+#else
+ return((ULONG)CurrCard);
+#endif
+}
+
+#else /* end (FW_TYPE==_SCCB_MGR_) */
+
+
+
+STATIC s16bits FP_PresenceCheck(PMGR_INFO pMgrInfo)
+{
+ PMGR_ENTRYPNTS pMgr_EntryPnts = &pMgrInfo->mi_Functions;
+
+ pMgr_EntryPnts->UCBMgr_probe_adapter = probe_adapter;
+ pMgr_EntryPnts->UCBMgr_init_adapter = init_adapter;
+ pMgr_EntryPnts->UCBMgr_start_UCB = SccbMgr_start_sccb;
+ pMgr_EntryPnts->UCBMgr_build_UCB = build_UCB;
+ pMgr_EntryPnts->UCBMgr_abort_UCB = SccbMgr_abort_sccb;
+ pMgr_EntryPnts->UCBMgr_my_int = SccbMgr_my_int;
+ pMgr_EntryPnts->UCBMgr_isr = SccbMgr_isr;
+ pMgr_EntryPnts->UCBMgr_scsi_reset = SccbMgr_scsi_reset;
+ pMgr_EntryPnts->UCBMgr_timer_expired = SccbMgr_timer_expired;
+#ifndef NO_IOCTLS
+ pMgr_EntryPnts->UCBMgr_unload_card = SccbMgr_unload_card;
+ pMgr_EntryPnts->UCBMgr_save_foreign_state =
+ SccbMgr_save_foreign_state;
+ pMgr_EntryPnts->UCBMgr_restore_foreign_state =
+ SccbMgr_restore_foreign_state;
+ pMgr_EntryPnts->UCBMgr_restore_native_state =
+ SccbMgr_restore_native_state;
+#endif /*NO_IOCTLS*/
+
+ pMgrInfo->mi_SGListFormat=0x01;
+ pMgrInfo->mi_DataPtrFormat=0x01;
+ pMgrInfo->mi_MaxSGElements= (u16bits) 0xffffffff;
+ pMgrInfo->mi_MgrPrivateLen=sizeof(SCCB);
+ pMgrInfo->mi_PCIVendorID=BL_VENDOR_ID;
+ pMgrInfo->mi_PCIDeviceID=FP_DEVICE_ID;
+ pMgrInfo->mi_MgrAttributes= ATTR_IO_MAPPED +
+ ATTR_PHYSICAL_ADDRESS +
+ ATTR_VIRTUAL_ADDRESS +
+ ATTR_OVERLAPPED_IO_IOCTLS_OK;
+ pMgrInfo->mi_IoRangeLen = 256;
+ return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: probe_adapter
+ *
+ * Description: Setup and/or Search for cards and return info to caller.
+ *
+ *---------------------------------------------------------------------*/
+STATIC s32bits probe_adapter(PADAPTER_INFO pAdapterInfo)
+{
+ u16bits temp,temp2,temp3,temp4;
+ u08bits i,j,id;
+
+#if defined(DOS)
+#else
+ static u08bits first_time = 1;
+#endif
+ BASE_PORT ioport;
+ PNVRamInfo pCurrNvRam;
+
+ ioport = (BASE_PORT)pAdapterInfo->ai_baseaddr;
+
+
+
+ if (RD_HARPOON(ioport+hp_vendor_id_0) != ORION_VEND_0)
+ return(1);
+
+ if ((RD_HARPOON(ioport+hp_vendor_id_1) != ORION_VEND_1))
+ return(2);
+
+ if ((RD_HARPOON(ioport+hp_device_id_0) != ORION_DEV_0))
+ return(3);
+
+ if ((RD_HARPOON(ioport+hp_device_id_1) != ORION_DEV_1))
+ return(4);
+
+
+ if (RD_HARPOON(ioport+hp_rev_num) != 0x0f){
+
+
+/* For new Harpoon then check for sub_device ID LSB
+ the bits(0-3) must be all ZERO for compatible with
+ current version of SCCBMgr, else skip this Harpoon
+ device. */
+
+ if (RD_HARPOON(ioport+hp_sub_device_id_0) & 0x0f)
+ return(5);
+ }
+
+ if (first_time) {
+
+ SccbMgrTableInitAll();
+ first_time = 0;
+ mbCards = 0;
+ }
+
+ if(RdStack(ioport, 0) != 0x00) {
+ if(ChkIfChipInitialized(ioport) == FALSE)
+ {
+ pCurrNvRam = NULL;
+ WR_HARPOON(ioport+hp_semaphore, 0x00);
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+ }
+ else
+ {
+ if(mbCards < MAX_MB_CARDS) {
+ pCurrNvRam = &nvRamInfo[mbCards];
+ mbCards++;
+ pCurrNvRam->niBaseAddr = ioport;
+ RNVRamData(pCurrNvRam);
+ }else
+ return((int) FAILURE);
+ }
+ }else
+ pCurrNvRam = NULL;
+
+#if defined (NO_BIOS_OPTION)
+ pCurrNvRam = NULL;
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+#endif /* No BIOS Option */
+
+ WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+
+ if(pCurrNvRam)
+ pAdapterInfo->ai_id = pCurrNvRam->niAdapId;
+ else
+ pAdapterInfo->ai_id = (u08bits)(utilEERead(ioport, (ADAPTER_SCSI_ID/2)) &
+ (u08bits)0x0FF);
+
+ pAdapterInfo->ai_lun = 0x00;
+ pAdapterInfo->ai_fw_revision[0] = '3';
+ pAdapterInfo->ai_fw_revision[1] = '1';
+ pAdapterInfo->ai_fw_revision[2] = '1';
+ pAdapterInfo->ai_fw_revision[3] = ' ';
+ pAdapterInfo->ai_NumChannels = 1;
+
+ temp2 = 0x0000;
+ temp3 = 0x0000;
+ temp4 = 0x0000;
+
+ for (id = 0; id < (16/2); id++) {
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (u16bits)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++) {
+
+ if ((temp & 0x03) != AUTO_RATE_00) {
+
+ temp2 >>= 0x01;
+ temp2 |= 0x8000;
+ }
+
+ else {
+ temp2 >>= 0x01;
+ }
+
+ if (temp & DISC_ENABLE_BIT) {
+
+ temp3 >>= 0x01;
+ temp3 |= 0x8000;
+ }
+
+ else {
+ temp3 >>= 0x01;
+ }
+
+ if (temp & WIDE_NEGO_BIT) {
+
+ temp4 >>= 0x01;
+ temp4 |= 0x8000;
+ }
+
+ else {
+ temp4 >>= 0x01;
+ }
+
+ }
+ }
+
+ pAdapterInfo->ai_per_targ_init_sync = temp2;
+ pAdapterInfo->ai_per_targ_no_disc = temp3;
+ pAdapterInfo->ai_per_targ_wide_nego = temp4;
+ if(pCurrNvRam)
+ i = pCurrNvRam->niSysConf;
+ else
+ i = (u08bits)(utilEERead(ioport, (SYSTEM_CONFIG/2)));
+
+ /*
+ ** interrupts always level-triggered for FlashPoint
+ */
+ pAdapterInfo->ai_stateinfo |= LEVEL_TRIG;
+
+ if (i & 0x01)
+ pAdapterInfo->ai_stateinfo |= SCSI_PARITY_ENA;
+
+ if (i & 0x02) /* SCSI Bus reset in AutoSCSI Set ? */
+ {
+ if(pCurrNvRam)
+ {
+ j = pCurrNvRam->niScamConf;
+ }
+ else
+ {
+ j = (u08bits) utilEERead(ioport, SCAM_CONFIG/2);
+ }
+ if(j & SCAM_ENABLED)
+ {
+ if(j & SCAM_LEVEL2)
+ {
+ pAdapterInfo->ai_stateinfo |= SCAM2_ENA;
+ }
+ else
+ {
+ pAdapterInfo->ai_stateinfo |= SCAM1_ENA;
+ }
+ }
+ }
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & 0x04) {
+ j |= SCSI_TERM_ENA_L;
+ pAdapterInfo->ai_stateinfo |= LOW_BYTE_TERM_ENA;
+ }
+ WR_HARPOON(ioport+hp_bm_ctrl, j );
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & 0x08) {
+ j |= SCSI_TERM_ENA_H;
+ pAdapterInfo->ai_stateinfo |= HIGH_BYTE_TERM_ENA;
+ }
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+ if(RD_HARPOON(ioport + hp_page_ctrl) & BIOS_SHADOW)
+ {
+ pAdapterInfo->ai_FlashRomSize = 64 * 1024; /* 64k ROM */
+ }
+ else
+ {
+ pAdapterInfo->ai_FlashRomSize = 32 * 1024; /* 32k ROM */
+ }
+
+ pAdapterInfo->ai_stateinfo |= (FAST20_ENA | TAG_QUEUE_ENA);
+ if (!(RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD))
+ {
+ pAdapterInfo->ai_attributes |= (WIDE_CAPABLE | FAST20_CAPABLE
+ | SCAM2_CAPABLE
+ | TAG_QUEUE_CAPABLE
+ | SUPRESS_UNDERRRUNS_CAPABLE
+ | SCSI_PARITY_CAPABLE);
+ pAdapterInfo->ai_MaxTarg = 16;
+ pAdapterInfo->ai_MaxLun = 32;
+ }
+ else
+ {
+ pAdapterInfo->ai_attributes |= (FAST20_CAPABLE | SCAM2_CAPABLE
+ | TAG_QUEUE_CAPABLE
+ | SUPRESS_UNDERRRUNS_CAPABLE
+ | SCSI_PARITY_CAPABLE);
+ pAdapterInfo->ai_MaxTarg = 8;
+ pAdapterInfo->ai_MaxLun = 8;
+ }
+
+ pAdapterInfo->ai_product_family = HARPOON_FAMILY;
+ pAdapterInfo->ai_HBAbustype = BUSTYPE_PCI;
+
+ for (i=0;i<CARD_MODEL_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_card_model[i]=' '; /* initialize the ai_card_model */
+ }
+
+ if(pCurrNvRam){
+ pAdapterInfo->ai_card_model[0] = '9';
+ switch(pCurrNvRam->niModel & 0x0f){
+ case MODEL_LT:
+ pAdapterInfo->ai_card_model[1] = '3';
+ pAdapterInfo->ai_card_model[2] = '0';
+ break;
+ case MODEL_LW:
+ pAdapterInfo->ai_card_model[1] = '5';
+ pAdapterInfo->ai_card_model[2] = '0';
+ break;
+ case MODEL_DL:
+ pAdapterInfo->ai_card_model[1] = '3';
+ pAdapterInfo->ai_card_model[2] = '2';
+ break;
+ case MODEL_DW:
+ pAdapterInfo->ai_card_model[1] = '5';
+ pAdapterInfo->ai_card_model[2] = '2';
+ break;
+ }
+ }else{
+ temp = utilEERead(ioport, (MODEL_NUMB_0/2));
+ pAdapterInfo->ai_card_model[0] = (u08bits)(temp >> 8);
+ temp = utilEERead(ioport, (MODEL_NUMB_2/2));
+
+ pAdapterInfo->ai_card_model[1] = (u08bits)(temp & 0x00FF);
+ pAdapterInfo->ai_card_model[2] = (u08bits)(temp >> 8);
+ }
+
+
+
+ pAdapterInfo->ai_FiberProductType = 0;
+
+ pAdapterInfo->ai_secondary_range = 0;
+
+ for (i=0;i<WORLD_WIDE_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_worldwidename[i]='\0';
+ }
+
+ for (i=0;i<VENDOR_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_vendorstring[i]='\0';
+ }
+ pAdapterInfo->ai_vendorstring[0]='B';
+ pAdapterInfo->ai_vendorstring[1]='U';
+ pAdapterInfo->ai_vendorstring[2]='S';
+ pAdapterInfo->ai_vendorstring[3]='L';
+ pAdapterInfo->ai_vendorstring[4]='O';
+ pAdapterInfo->ai_vendorstring[5]='G';
+ pAdapterInfo->ai_vendorstring[6]='I';
+ pAdapterInfo->ai_vendorstring[7]='C';
+
+ for (i=0;i<FAMILY_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_AdapterFamilyString[i]='\0';
+ }
+ pAdapterInfo->ai_AdapterFamilyString[0]='F';
+ pAdapterInfo->ai_AdapterFamilyString[1]='L';
+ pAdapterInfo->ai_AdapterFamilyString[2]='A';
+ pAdapterInfo->ai_AdapterFamilyString[3]='S';
+ pAdapterInfo->ai_AdapterFamilyString[4]='H';
+ pAdapterInfo->ai_AdapterFamilyString[5]='P';
+ pAdapterInfo->ai_AdapterFamilyString[6]='O';
+ pAdapterInfo->ai_AdapterFamilyString[7]='I';
+ pAdapterInfo->ai_AdapterFamilyString[8]='N';
+ pAdapterInfo->ai_AdapterFamilyString[9]='T';
+
+ ARAM_ACCESS(ioport);
+
+ for ( i = 0; i < 4; i++ ) {
+
+ pAdapterInfo->ai_XlatInfo[i] =
+ RD_HARPOON(ioport+hp_aramBase+BIOS_DATA_OFFSET+i);
+ }
+
+ /* return with -1 if no sort, else return with
+ logical card number sorted by BIOS (zero-based) */
+
+
+ pAdapterInfo->ai_relative_cardnum =
+ (u08bits)(RD_HARPOON(ioport+hp_aramBase+BIOS_RELATIVE_CARD)-1);
+
+ SGRAM_ACCESS(ioport);
+
+ s_PhaseTbl[0] = phaseDataOut;
+ s_PhaseTbl[1] = phaseDataIn;
+ s_PhaseTbl[2] = phaseIllegal;
+ s_PhaseTbl[3] = phaseIllegal;
+ s_PhaseTbl[4] = phaseCommand;
+ s_PhaseTbl[5] = phaseStatus;
+ s_PhaseTbl[6] = phaseMsgOut;
+ s_PhaseTbl[7] = phaseMsgIn;
+
+ pAdapterInfo->ai_present = 0x01;
+
+#if defined(BUGBUG)
+
+
+ for (i = 0; i < MAX_CARDS; i++) {
+
+ for (id=0; id<debug_size; id++)
+ debug_int[i][id] = (u08bits)0x00;
+ debug_index[i] = 0;
+ }
+
+#endif
+
+ return(0);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: init_adapter, exported to BUDI via UCBMgr_init_adapter entry
+ *
+ *
+ * Description: Setup adapter for normal operation (hard reset).
+ *
+ *---------------------------------------------------------------------*/
+STATIC CARD_HANDLE init_adapter(PADAPTER_INFO pCardInfo)
+{
+ PSCCBcard CurrCard;
+ PNVRamInfo pCurrNvRam;
+ u08bits i,j,thisCard, ScamFlg;
+ u16bits temp,sync_bit_map,id;
+ BASE_PORT ioport;
+
+ ioport = (BASE_PORT)pCardInfo->ai_baseaddr;
+
+ for(thisCard =0; thisCard <= MAX_CARDS; thisCard++) {
+
+ if (thisCard == MAX_CARDS) {
+
+ return(FAILURE);
+ }
+
+ if (BL_Card[thisCard].ioPort == ioport) {
+
+ CurrCard = &BL_Card[thisCard];
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ break;
+ }
+
+ else if (BL_Card[thisCard].ioPort == 0x00) {
+
+ BL_Card[thisCard].ioPort = ioport;
+ CurrCard = &BL_Card[thisCard];
+
+ if(mbCards)
+ for(i = 0; i < mbCards; i++){
+ if(CurrCard->ioPort == nvRamInfo[i].niBaseAddr)
+ CurrCard->pNvRamInfo = &nvRamInfo[i];
+ }
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ CurrCard->cardIndex = thisCard;
+ CurrCard->cardInfo = pCardInfo;
+
+ break;
+ }
+ }
+
+ pCurrNvRam = CurrCard->pNvRamInfo;
+
+
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+ }
+
+
+ BusMasterInit(ioport);
+ XbowInit(ioport, ScamFlg);
+
+#if defined (NO_BIOS_OPTION)
+
+
+ if (DiagXbow(ioport)) return(FAILURE);
+ if (DiagBusMaster(ioport)) return(FAILURE);
+
+#endif /* No BIOS Option */
+
+ autoLoadDefaultMap(ioport);
+
+
+ for (i = 0,id = 0x01; i != pCardInfo->ai_id; i++,id <<= 1){}
+
+ WR_HARPOON(ioport+hp_selfid_0, id);
+ WR_HARPOON(ioport+hp_selfid_1, 0x00);
+ WR_HARPOON(ioport+hp_arb_id, pCardInfo->ai_id);
+ CurrCard->ourId = (unsigned char) pCardInfo->ai_id;
+
+ i = (u08bits) pCardInfo->ai_stateinfo;
+ if (i & SCSI_PARITY_ENA)
+ WR_HARPOON(ioport+hp_portctrl_1,(HOST_MODE8 | CHK_SCSI_P));
+
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & LOW_BYTE_TERM_ENA)
+ j |= SCSI_TERM_ENA_L;
+ WR_HARPOON(ioport+hp_bm_ctrl, j);
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & HIGH_BYTE_TERM_ENA)
+ j |= SCSI_TERM_ENA_H;
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+
+ if (!(pCardInfo->ai_stateinfo & NO_RESET_IN_INIT)) {
+
+ sresb(ioport,thisCard);
+
+ scini(thisCard, (u08bits) pCardInfo->ai_id, 0);
+ }
+
+
+
+ if (pCardInfo->ai_stateinfo & SUPRESS_UNDERRRUNS_ENA)
+ CurrCard->globalFlags |= F_NO_FILTER;
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niSysConf & 0x10)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+ else{
+ if (utilEERead(ioport, (SYSTEM_CONFIG/2)) & GREEN_PC_ENA)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+
+ /* Set global flag to indicate Re-Negotiation to be done on all
+ ckeck condition */
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x04)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & RENEGO_ENA)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x08)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & CONNIO_ENA)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+
+ temp = pCardInfo->ai_per_targ_no_disc;
+
+ for (i = 0,id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {
+
+ if (temp & id)
+ sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
+ }
+
+ sync_bit_map = 0x0001;
+
+ for (id = 0; id < (MAX_SCSI_TAR/2); id++){
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (u16bits)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++){
+
+ if (pCardInfo->ai_per_targ_init_sync & sync_bit_map){
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue = (u08bits)temp;
+ }
+
+ else {
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= SYNC_SUPPORTED;
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue =
+ (u08bits)(temp & ~EE_SYNC_MASK);
+ }
+
+#if defined(WIDE_SCSI)
+/* if ((pCardInfo->ai_per_targ_wide_nego & sync_bit_map) ||
+ (id*2+i >= 8)){
+*/
+ if (pCardInfo->ai_per_targ_wide_nego & sync_bit_map){
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue |= EE_WIDE_SCSI;
+
+ }
+
+ else { /* NARROW SCSI */
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+ }
+
+#else
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+#endif
+
+
+ sync_bit_map <<= 1;
+ }
+ }
+
+
+ pCardInfo->ai_SGListFormat=0x01;
+ pCardInfo->ai_DataPtrFormat=0x01;
+ pCardInfo->ai_AEN_mask &= SCSI_RESET_COMPLETE;
+
+ WR_HARPOON((ioport+hp_semaphore),
+ (u08bits)(RD_HARPOON((ioport+hp_semaphore)) | SCCB_MGR_PRESENT));
+
+ return((u32bits)CurrCard);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: build_ucb, exported to BUDI via UCBMgr_build_ucb entry
+ *
+ * Description: prepare fw portion of ucb. do not start, resource not guaranteed
+ * so don't manipulate anything that's derived from states which
+ * may change
+ *
+ *---------------------------------------------------------------------*/
+void build_UCB(CARD_HANDLE pCurrCard, PUCB p_ucb)
+{
+
+ u08bits thisCard;
+ u08bits i,j;
+
+ PSCCB p_sccb;
+
+
+ thisCard = ((PSCCBcard) pCurrCard)->cardIndex;
+
+
+ p_sccb=(PSCCB)p_ucb->UCB_MgrPrivatePtr;
+
+
+ p_sccb->Sccb_ucb_ptr=p_ucb;
+
+ switch (p_ucb->UCB_opcode & (OPC_DEVICE_RESET+OPC_XFER_SG+OPC_CHK_RESIDUAL))
+ {
+ case OPC_DEVICE_RESET:
+ p_sccb->OperationCode=RESET_COMMAND;
+ break;
+ case OPC_XFER_SG:
+ p_sccb->OperationCode=SCATTER_GATHER_COMMAND;
+ break;
+ case OPC_XFER_SG+OPC_CHK_RESIDUAL:
+ p_sccb->OperationCode=RESIDUAL_SG_COMMAND;
+ break;
+ case OPC_CHK_RESIDUAL:
+
+ p_sccb->OperationCode=RESIDUAL_COMMAND;
+ break;
+ default:
+ p_sccb->OperationCode=SCSI_INITIATOR_COMMAND;
+ break;
+ }
+
+ if (p_ucb->UCB_opcode & OPC_TQ_ENABLE)
+ {
+ p_sccb->ControlByte = (u08bits)((p_ucb->UCB_opcode & OPC_TQ_MASK)>>2) | F_USE_CMD_Q;
+ }
+ else
+ {
+ p_sccb->ControlByte = 0;
+ }
+
+
+ p_sccb->CdbLength = (u08bits)p_ucb->UCB_cdblen;
+
+ if (p_ucb->UCB_opcode & OPC_NO_AUTO_SENSE)
+ {
+ p_sccb->RequestSenseLength = 0;
+ }
+ else
+ {
+ p_sccb->RequestSenseLength = (unsigned char) p_ucb->UCB_senselen;
+ }
+
+
+ if (p_ucb->UCB_opcode & OPC_XFER_SG)
+ {
+ p_sccb->DataPointer=p_ucb->UCB_virt_dataptr;
+ p_sccb->DataLength = (((u32bits)p_ucb->UCB_NumSgElements)<<3);
+ }
+ else
+ {
+ p_sccb->DataPointer=p_ucb->UCB_phys_dataptr;
+ p_sccb->DataLength=p_ucb->UCB_datalen;
+ };
+
+ p_sccb->HostStatus=0;
+ p_sccb->TargetStatus=0;
+ p_sccb->TargID=(unsigned char)p_ucb->UCB_targid;
+ p_sccb->Lun=(unsigned char) p_ucb->UCB_lun;
+ p_sccb->SccbIOPort=((PSCCBcard)pCurrCard)->ioPort;
+
+ j=p_ucb->UCB_cdblen;
+ for (i=0;i<j;i++)
+ {
+ p_sccb->Cdb[i] = p_ucb->UCB_cdb[i];
+ }
+
+ p_sccb->SensePointer=p_ucb->UCB_phys_senseptr;
+
+ sinits(p_sccb,thisCard);
+
+}
+#ifndef NO_IOCTLS
+
+/*---------------------------------------------------------------------
+ *
+ * Function: GetDevSyncRate
+ *
+ *---------------------------------------------------------------------*/
+STATIC int GetDevSyncRate(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ struct _SYNC_RATE_INFO * pSyncStr;
+ PSCCBMgr_tar_info currTar_Info;
+ BASE_PORT ioport;
+ u08bits scsiID, j;
+
+#if (FW_TYPE != _SCCB_MGR_)
+ if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+ {
+ return(1);
+ }
+#endif
+
+ ioport = pCurrCard->ioPort;
+ pSyncStr = (struct _SYNC_RATE_INFO *) p_ucb->UCB_virt_dataptr;
+ scsiID = (u08bits) p_ucb->UCB_targid;
+ currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+ j = currTar_Info->TarSyncCtrl;
+
+ switch (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ case EE_SYNC_ASYNC:
+ pSyncStr->RequestMegaXferRate = 0x00;
+ break;
+ case EE_SYNC_5MB:
+ pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 50 : 100;
+ break;
+ case EE_SYNC_10MB:
+ pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 100 : 200;
+ break;
+ case EE_SYNC_20MB:
+ pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 200 : 400;
+ break;
+ }
+
+ switch ((j >> 5) & 0x07)
+ {
+ case 0x00:
+ if((j & 0x07) == 0x00)
+ {
+ pSyncStr->ActualMegaXferRate = 0x00; /* Async Mode */
+ }
+ else
+ {
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 200 : 400;
+ }
+ break;
+ case 0x01:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 100 : 200;
+ break;
+ case 0x02:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 66 : 122;
+ break;
+ case 0x03:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 50 : 100;
+ break;
+ case 0x04:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 40 : 80;
+ break;
+ case 0x05:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 33 : 66;
+ break;
+ case 0x06:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 28 : 56;
+ break;
+ case 0x07:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 25 : 50;
+ break;
+ }
+ pSyncStr->NegotiatedOffset = j & 0x0f;
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SetDevSyncRate
+ *
+ *---------------------------------------------------------------------*/
+STATIC int SetDevSyncRate(PSCCBcard pCurrCard, PUCB p_ucb)
+{
+ struct _SYNC_RATE_INFO * pSyncStr;
+ PSCCBMgr_tar_info currTar_Info;
+ BASE_PORT ioPort;
+ u08bits scsiID, i, j, syncVal;
+ u16bits syncOffset, actualXferRate;
+ union {
+ u08bits tempb[2];
+ u16bits tempw;
+ }temp2;
+
+#if (FW_TYPE != _SCCB_MGR_)
+ if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+ {
+ return(1);
+ }
+#endif
+
+ ioPort = pCurrCard->ioPort;
+ pSyncStr = (struct _SYNC_RATE_INFO *) p_ucb->UCB_virt_dataptr;
+ scsiID = (u08bits) p_ucb->UCB_targid;
+ currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+ i = RD_HARPOON(ioPort+hp_xfer_pad); /* Save current value */
+ WR_HARPOON(ioPort+hp_xfer_pad, (i | ID_UNLOCK));
+ WR_HARPOON(ioPort+hp_select_id, ((scsiID << 4) | scsiID));
+ j = RD_HARPOON(ioPort+hp_synctarg_0);
+ WR_HARPOON(ioPort+hp_xfer_pad, i); /* restore value */
+
+ actualXferRate = pSyncStr->ActualMegaXferRate;
+ if(!(j & NARROW_SCSI))
+ {
+ actualXferRate <<= 1;
+ }
+ if(actualXferRate == 0x00)
+ {
+ syncVal = EE_SYNC_ASYNC; /* Async Mode */
+ }
+ if(actualXferRate == 0x0200)
+ {
+ syncVal = EE_SYNC_20MB; /* 20/40 MB Mode */
+ }
+ if(actualXferRate > 0x0050 && actualXferRate < 0x0200 )
+ {
+ syncVal = EE_SYNC_10MB; /* 10/20 MB Mode */
+ }
+ else
+ {
+ syncVal = EE_SYNC_5MB; /* 5/10 MB Mode */
+ }
+ if(currTar_Info->TarEEValue && EE_SYNC_MASK == syncVal)
+ return(0);
+ currTar_Info->TarEEValue = (currTar_Info->TarEEValue & !EE_SYNC_MASK)
+ | syncVal;
+ syncOffset = (SYNC_RATE_TBL + scsiID) / 2;
+ temp2.tempw = utilEERead(ioPort, syncOffset);
+ if(scsiID & 0x01)
+ {
+ temp2.tempb[0] = (temp2.tempb[0] & !EE_SYNC_MASK) | syncVal;
+ }
+ else
+ {
+ temp2.tempb[1] = (temp2.tempb[1] & !EE_SYNC_MASK) | syncVal;
+ }
+ utilEEWriteOnOff(ioPort, 1);
+ utilEEWrite(ioPort, temp2.tempw, syncOffset);
+ utilEEWriteOnOff(ioPort, 0);
+ UpdateCheckSum(ioPort);
+
+ return(0);
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: GetDevWideMode
+ *
+ *---------------------------------------------------------------------*/
+int GetDevWideMode(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pData;
+
+ pData = (u08bits *)p_ucb->UCB_virt_dataptr;
+ if(sccbMgrTbl[pCurrCard->cardIndex][p_ucb->UCB_targid].TarEEValue
+ & EE_WIDE_SCSI)
+ {
+ *pData = 1;
+ }
+ else
+ {
+ *pData = 0;
+ }
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SetDevWideMode
+ *
+ *---------------------------------------------------------------------*/
+int SetDevWideMode(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pData;
+ PSCCBMgr_tar_info currTar_Info;
+ BASE_PORT ioPort;
+ u08bits scsiID, scsiWideMode;
+ u16bits syncOffset;
+ union {
+ u08bits tempb[2];
+ u16bits tempw;
+ }temp2;
+
+#if (FW_TYPE != _SCCB_MGR_)
+ if( !(pCurrCard->cardInfo->ai_attributes & WIDE_CAPABLE) )
+ {
+ return(1);
+ }
+
+ if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+ {
+ return(1);
+ }
+#endif
+
+ ioPort = pCurrCard->ioPort;
+ pData = (u08bits *)p_ucb->UCB_virt_dataptr;
+ scsiID = (u08bits) p_ucb->UCB_targid;
+ currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+
+ if(*pData)
+ {
+ if(currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ return(0);
+ }
+ else
+ {
+ scsiWideMode = EE_WIDE_SCSI;
+ }
+ }
+ else
+ {
+ if(!(currTar_Info->TarEEValue & EE_WIDE_SCSI))
+ {
+ return(0);
+ }
+ else
+ {
+ scsiWideMode = 0;
+ }
+ }
+ currTar_Info->TarEEValue = (currTar_Info->TarEEValue & !EE_WIDE_SCSI)
+ | scsiWideMode;
+
+ syncOffset = (SYNC_RATE_TBL + scsiID) / 2;
+ temp2.tempw = utilEERead(ioPort, syncOffset);
+ if(scsiID & 0x01)
+ {
+ temp2.tempb[0] = (temp2.tempb[0] & !EE_WIDE_SCSI) | scsiWideMode;
+ }
+ else
+ {
+ temp2.tempb[1] = (temp2.tempb[1] & !EE_WIDE_SCSI) | scsiWideMode;
+ }
+ utilEEWriteOnOff(ioPort, 1);
+ utilEEWrite(ioPort, temp2.tempw, syncOffset);
+ utilEEWriteOnOff(ioPort, 0);
+ UpdateCheckSum(ioPort);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ReadNVRam
+ *
+ *---------------------------------------------------------------------*/
+void ReadNVRam(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pdata;
+ u16bits i,numwrds,numbytes,offset,temp;
+ u08bits OneMore = FALSE;
+#if defined(DOS)
+ u16bits ioport;
+#else
+ u32bits ioport;
+#endif
+
+ numbytes = (u16bits) p_ucb->UCB_datalen;
+ ioport = pCurrCard->ioPort;
+ pdata = (u08bits *) p_ucb->UCB_virt_dataptr;
+ offset = (u16bits) (p_ucb->UCB_IOCTLParams[0]);
+
+
+
+ if (offset & 0x1)
+ {
+ *((u16bits*) pdata) = utilEERead(ioport,(u16bits)((offset - 1) / 2)); /* 16 bit read */
+ *pdata = *(pdata + 1);
+ ++offset;
+ ++pdata;
+ --numbytes;
+ }
+
+ numwrds = numbytes / 2;
+ if (numbytes & 1)
+ OneMore = TRUE;
+
+ for (i = 0; i < numwrds; i++)
+ {
+ *((u16bits*) pdata) = utilEERead(ioport,(u16bits)(offset / 2));
+ pdata += 2;
+ offset += 2;
+ }
+ if (OneMore)
+ {
+ --pdata;
+ -- offset;
+ temp = utilEERead(ioport,(u16bits)(offset / 2));
+ *pdata = (u08bits) (temp);
+ }
+
+} /* end proc ReadNVRam */
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: WriteNVRam
+ *
+ *---------------------------------------------------------------------*/
+void WriteNVRam(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pdata;
+ u16bits i,numwrds,numbytes,offset, eeprom_end;
+ u08bits OneMore = FALSE;
+ union {
+ u08bits tempb[2];
+ u16bits tempw;
+ } temp2;
+
+#if defined(DOS)
+ u16bits ioport;
+#else
+ u32bits ioport;
+#endif
+
+ numbytes = (u16bits) p_ucb->UCB_datalen;
+ ioport = pCurrCard->ioPort;
+ pdata = (u08bits *) p_ucb->UCB_virt_dataptr;
+ offset = (u16bits) (p_ucb->UCB_IOCTLParams[0]);
+
+ if (RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD)
+ eeprom_end = 512;
+ else
+ eeprom_end = 768;
+
+ if(offset > eeprom_end)
+ return;
+
+ if((offset + numbytes) > eeprom_end)
+ numbytes = eeprom_end - offset;
+
+ utilEEWriteOnOff(ioport,1); /* Enable write access to the EEPROM */
+
+
+
+ if (offset & 0x1)
+ {
+ temp2.tempw = utilEERead(ioport,(u16bits)((offset - 1) / 2)); /* 16 bit read */
+ temp2.tempb[1] = *pdata;
+ utilEEWrite(ioport, temp2.tempw, (u16bits)((offset -1) / 2));
+ *pdata = *(pdata + 1);
+ ++offset;
+ ++pdata;
+ --numbytes;
+ }
+
+ numwrds = numbytes / 2;
+ if (numbytes & 1)
+ OneMore = TRUE;
+
+ for (i = 0; i < numwrds; i++)
+ {
+ utilEEWrite(ioport, *((pu16bits)pdata),(u16bits)(offset / 2));
+ pdata += 2;
+ offset += 2;
+ }
+ if (OneMore)
+ {
+
+ temp2.tempw = utilEERead(ioport,(u16bits)(offset / 2));
+ temp2.tempb[0] = *pdata;
+ utilEEWrite(ioport, temp2.tempw, (u16bits)(offset / 2));
+ }
+ utilEEWriteOnOff(ioport,0); /* Turn off write access */
+ UpdateCheckSum((u32bits)ioport);
+
+} /* end proc WriteNVRam */
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: UpdateCheckSum
+ *
+ * Description: Update Check Sum in EEPROM
+ *
+ *---------------------------------------------------------------------*/
+
+
+void UpdateCheckSum(u32bits baseport)
+{
+ USHORT i,sum_data, eeprom_end;
+
+ sum_data = 0x0000;
+
+
+ if (RD_HARPOON(baseport+hp_page_ctrl) & NARROW_SCSI_CARD)
+ eeprom_end = 512;
+ else
+ eeprom_end = 768;
+
+ for (i = 1; i < eeprom_end/2; i++)
+ {
+ sum_data += utilEERead(baseport, i);
+ }
+
+ utilEEWriteOnOff(baseport,1); /* Enable write access to the EEPROM */
+
+ utilEEWrite(baseport, sum_data, EEPROM_CHECK_SUM/2);
+ utilEEWriteOnOff(baseport,0); /* Turn off write access */
+}
+
+void SccbMgr_save_foreign_state(PADAPTER_INFO pAdapterInfo)
+{
+}
+
+
+void SccbMgr_restore_foreign_state(CARD_HANDLE pCurrCard)
+{
+}
+
+void SccbMgr_restore_native_state(CARD_HANDLE pCurrCard)
+{
+}
+
+#endif /* NO_IOCTLS */
+
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+#ifndef NO_IOCTLS
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_unload_card(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_unload_card(USHORT pCurrCard)
+#else
+void SccbMgr_unload_card(ULONG pCurrCard)
+#endif
+#endif
+{
+ UCHAR i;
+#if defined(DOS)
+ USHORT portBase;
+ USHORT regOffset;
+#else
+ ULONG portBase;
+ ULONG regOffset;
+#endif
+ ULONG scamData;
+#if defined(OS2)
+ ULONG far *pScamTbl;
+#else
+ ULONG *pScamTbl;
+#endif
+ PNVRamInfo pCurrNvRam;
+
+ pCurrNvRam = ((PSCCBcard)pCurrCard)->pNvRamInfo;
+
+ if(pCurrNvRam){
+ WrStack(pCurrNvRam->niBaseAddr, 0, pCurrNvRam->niModel);
+ WrStack(pCurrNvRam->niBaseAddr, 1, pCurrNvRam->niSysConf);
+ WrStack(pCurrNvRam->niBaseAddr, 2, pCurrNvRam->niScsiConf);
+ WrStack(pCurrNvRam->niBaseAddr, 3, pCurrNvRam->niScamConf);
+ WrStack(pCurrNvRam->niBaseAddr, 4, pCurrNvRam->niAdapId);
+
+ for(i = 0; i < MAX_SCSI_TAR / 2; i++)
+ WrStack(pCurrNvRam->niBaseAddr, (UCHAR)(i+5), pCurrNvRam->niSyncTbl[i]);
+
+ portBase = pCurrNvRam->niBaseAddr;
+
+ for(i = 0; i < MAX_SCSI_TAR; i++){
+ regOffset = hp_aramBase + 64 + i*4;
+#if defined(OS2)
+ pScamTbl = (ULONG far *) &pCurrNvRam->niScamTbl[i];
+#else
+ pScamTbl = (ULONG *) &pCurrNvRam->niScamTbl[i];
+#endif
+ scamData = *pScamTbl;
+ WR_HARP32(portBase, regOffset, scamData);
+ }
+
+ }else{
+ WrStack(((PSCCBcard)pCurrCard)->ioPort, 0, 0);
+ }
+}
+#endif /* NO_IOCTLS */
+
+
+void RNVRamData(PNVRamInfo pNvRamInfo)
+{
+ UCHAR i;
+#if defined(DOS)
+ USHORT portBase;
+ USHORT regOffset;
+#else
+ ULONG portBase;
+ ULONG regOffset;
+#endif
+ ULONG scamData;
+#if defined (OS2)
+ ULONG far *pScamTbl;
+#else
+ ULONG *pScamTbl;
+#endif
+
+ pNvRamInfo->niModel = RdStack(pNvRamInfo->niBaseAddr, 0);
+ pNvRamInfo->niSysConf = RdStack(pNvRamInfo->niBaseAddr, 1);
+ pNvRamInfo->niScsiConf = RdStack(pNvRamInfo->niBaseAddr, 2);
+ pNvRamInfo->niScamConf = RdStack(pNvRamInfo->niBaseAddr, 3);
+ pNvRamInfo->niAdapId = RdStack(pNvRamInfo->niBaseAddr, 4);
+
+ for(i = 0; i < MAX_SCSI_TAR / 2; i++)
+ pNvRamInfo->niSyncTbl[i] = RdStack(pNvRamInfo->niBaseAddr, (UCHAR)(i+5));
+
+ portBase = pNvRamInfo->niBaseAddr;
+
+ for(i = 0; i < MAX_SCSI_TAR; i++){
+ regOffset = hp_aramBase + 64 + i*4;
+ RD_HARP32(portBase, regOffset, scamData);
+#if defined(OS2)
+ pScamTbl = (ULONG far *) &pNvRamInfo->niScamTbl[i];
+#else
+ pScamTbl = (ULONG *) &pNvRamInfo->niScamTbl[i];
+#endif
+ *pScamTbl = scamData;
+ }
+
+}
+
+#if defined(DOS)
+UCHAR RdStack(USHORT portBase, UCHAR index)
+#else
+UCHAR RdStack(ULONG portBase, UCHAR index)
+#endif
+{
+ WR_HARPOON(portBase + hp_stack_addr, index);
+ return(RD_HARPOON(portBase + hp_stack_data));
+}
+
+#if defined(DOS)
+void WrStack(USHORT portBase, UCHAR index, UCHAR data)
+#else
+void WrStack(ULONG portBase, UCHAR index, UCHAR data)
+#endif
+{
+ WR_HARPOON(portBase + hp_stack_addr, index);
+ WR_HARPOON(portBase + hp_stack_data, data);
+}
+
+
+#if (FW_TYPE==_UCB_MGR_)
+u08bits ChkIfChipInitialized(BASE_PORT ioPort)
+#else
+#if defined(DOS)
+UCHAR ChkIfChipInitialized(USHORT ioPort)
+#else
+UCHAR ChkIfChipInitialized(ULONG ioPort)
+#endif
+#endif
+{
+ if((RD_HARPOON(ioPort + hp_arb_id) & 0x0f) != RdStack(ioPort, 4))
+ return(FALSE);
+ if((RD_HARPOON(ioPort + hp_clkctrl_0) & CLKCTRL_DEFAULT)
+ != CLKCTRL_DEFAULT)
+ return(FALSE);
+ if((RD_HARPOON(ioPort + hp_seltimeout) == TO_250ms) ||
+ (RD_HARPOON(ioPort + hp_seltimeout) == TO_290ms))
+ return(TRUE);
+ return(FALSE);
+
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_start_sccb
+ *
+ * Description: Start a command pointed to by p_Sccb. When the
+ * command is completed it will be returned via the
+ * callback function.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_start_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb)
+#else
+#if defined(DOS)
+void SccbMgr_start_sccb(USHORT pCurrCard, PSCCB p_Sccb)
+#else
+void SccbMgr_start_sccb(ULONG pCurrCard, PSCCB p_Sccb)
+#endif
+#endif
+{
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+ UCHAR thisCard, lun;
+ PSCCB pSaveSccb;
+ CALL_BK_FN callback;
+
+#if (FW_TYPE==_UCB_MGR_)
+ PSCCB p_Sccb;
+#endif
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+ thisCard = ((PSCCBcard) pCurrCard)->cardIndex;
+ ioport = ((PSCCBcard) pCurrCard)->ioPort;
+
+#if (FW_TYPE==_UCB_MGR_)
+ p_Sccb = (PSCCB)p_ucb->UCB_MgrPrivatePtr;
+#endif
+
+ if((p_Sccb->TargID > MAX_SCSI_TAR) || (p_Sccb->Lun > MAX_LUN))
+ {
+
+#if (FW_TYPE==_UCB_MGR_)
+ p_ucb->UCB_hbastat = SCCB_COMPLETE;
+ p_ucb->UCB_status=SCCB_ERROR;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+#endif
+
+#if (FW_TYPE==_SCCB_MGR_)
+ p_Sccb->HostStatus = SCCB_COMPLETE;
+ p_Sccb->SccbStatus = SCCB_ERROR;
+ callback = (CALL_BK_FN)p_Sccb->SccbCallback;
+ if (callback)
+ callback(p_Sccb);
+#endif
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ }
+
+#if (FW_TYPE==_SCCB_MGR_)
+ sinits(p_Sccb,thisCard);
+#endif
+
+
+#if (FW_TYPE==_UCB_MGR_)
+#ifndef NO_IOCTLS
+
+ if (p_ucb->UCB_opcode & OPC_IOCTL)
+ {
+
+ switch (p_ucb->UCB_IOCTLCommand)
+ {
+ case READ_NVRAM:
+ ReadNVRam((PSCCBcard)pCurrCard,p_ucb);
+ p_ucb->UCB_status=UCB_SUCCESS;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+
+ case WRITE_NVRAM:
+ WriteNVRam((PSCCBcard)pCurrCard,p_ucb);
+ p_ucb->UCB_status=UCB_SUCCESS;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+
+ case SEND_SCSI_PASSTHRU:
+#if (FW_TYPE != _SCCB_MGR_)
+ if( p_ucb->UCB_targid >=
+ ((PSCCBcard)pCurrCard)->cardInfo->ai_MaxTarg )
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ }
+#endif
+ break;
+
+ case HARD_RESET:
+ p_ucb->UCB_status = UCB_INVALID;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case GET_DEVICE_SYNCRATE:
+ if( !GetDevSyncRate((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case SET_DEVICE_SYNCRATE:
+ if( !SetDevSyncRate((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case GET_WIDE_MODE:
+ if( !GetDevWideMode((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case SET_WIDE_MODE:
+ if( !SetDevWideMode((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ default:
+ p_ucb->UCB_status=UCB_INVALID;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ }
+ }
+#endif /* NO_IOCTLS */
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+
+ if (!((PSCCBcard) pCurrCard)->cmdCounter)
+ {
+ WR_HARPOON(ioport+hp_semaphore, (RD_HARPOON(ioport+hp_semaphore)
+ | SCCB_MGR_ACTIVE));
+
+ if (((PSCCBcard) pCurrCard)->globalFlags & F_GREEN_PC)
+ {
+ WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+ }
+ }
+
+ ((PSCCBcard)pCurrCard)->cmdCounter++;
+
+ if (RD_HARPOON(ioport+hp_semaphore) & BIOS_IN_USE) {
+
+ WR_HARPOON(ioport+hp_semaphore, (RD_HARPOON(ioport+hp_semaphore)
+ | TICKLE_ME));
+ if(p_Sccb->OperationCode == RESET_COMMAND)
+ {
+ pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail(&BL_Card[thisCard], thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+ }
+ else
+ {
+ queueAddSccb(p_Sccb,thisCard);
+ }
+ }
+
+ else if ((RD_HARPOON(ioport+hp_page_ctrl) & G_INT_DISABLE)) {
+
+ if(p_Sccb->OperationCode == RESET_COMMAND)
+ {
+ pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail(&BL_Card[thisCard], thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+ }
+ else
+ {
+ queueAddSccb(p_Sccb,thisCard);
+ }
+ }
+
+ else {
+
+ MDISABLE_INT(ioport);
+
+ if((((PSCCBcard) pCurrCard)->globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[thisCard][p_Sccb->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ lun = p_Sccb->Lun;
+ else
+ lun = 0;
+ if ((((PSCCBcard) pCurrCard)->currentSCCB == NULL) &&
+ (sccbMgrTbl[thisCard][p_Sccb->TargID].TarSelQ_Cnt == 0) &&
+ (sccbMgrTbl[thisCard][p_Sccb->TargID].TarLUNBusy[lun]
+ == FALSE)) {
+
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ mOS_UnLock((PSCCBcard)pCurrCard);
+#if defined(DOS)
+ ssel((USHORT)p_Sccb->SccbIOPort,thisCard);
+#else
+ ssel(p_Sccb->SccbIOPort,thisCard);
+#endif
+ mOS_Lock((PSCCBcard)pCurrCard);
+ }
+
+ else {
+
+ if(p_Sccb->OperationCode == RESET_COMMAND)
+ {
+ pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail(&BL_Card[thisCard], thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+ }
+ else
+ {
+ queueAddSccb(p_Sccb,thisCard);
+ }
+ }
+
+
+ MENABLE_INT(ioport);
+ }
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_abort_sccb
+ *
+ * Description: Abort the command pointed to by p_Sccb. When the
+ * command is completed it will be returned via the
+ * callback function.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+s32bits SccbMgr_abort_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb)
+#else
+#if defined(DOS)
+int SccbMgr_abort_sccb(USHORT pCurrCard, PSCCB p_Sccb)
+#else
+int SccbMgr_abort_sccb(ULONG pCurrCard, PSCCB p_Sccb)
+#endif
+#endif
+
+{
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+ UCHAR thisCard;
+ CALL_BK_FN callback;
+ UCHAR TID;
+ PSCCB pSaveSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+
+#if (FW_TYPE==_UCB_MGR_)
+ PSCCB p_Sccb;
+ p_Sccb=(PSCCB)p_ucb->UCB_MgrPrivatePtr;
+#endif
+
+ ioport = ((PSCCBcard) pCurrCard)->ioPort;
+
+ thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+
+ if (RD_HARPOON(ioport+hp_page_ctrl) & G_INT_DISABLE)
+ {
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ }
+
+ else
+ {
+
+ if (queueFindSccb(p_Sccb,thisCard))
+ {
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ ((PSCCBcard)pCurrCard)->cmdCounter--;
+
+ if (!((PSCCBcard)pCurrCard)->cmdCounter)
+ WR_HARPOON(ioport+hp_semaphore,(RD_HARPOON(ioport+hp_semaphore)
+ & (UCHAR)(~(SCCB_MGR_ACTIVE | TICKLE_ME)) ));
+
+#if (FW_TYPE==_SCCB_MGR_)
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ callback = p_Sccb->SccbCallback;
+ callback(p_Sccb);
+#else
+ p_ucb->UCB_status=SCCB_ABORT;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ callback(p_ucb);
+#endif
+
+ return(0);
+ }
+
+ else
+ {
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ if (((PSCCBcard)pCurrCard)->currentSCCB == p_Sccb)
+ {
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ return(0);
+
+ }
+
+ else
+ {
+
+ TID = p_Sccb->TargID;
+
+
+ if(p_Sccb->Sccb_tag)
+ {
+ MDISABLE_INT(ioport);
+ if (((PSCCBcard) pCurrCard)->discQ_Tbl[p_Sccb->Sccb_tag]==p_Sccb)
+ {
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ p_Sccb->Sccb_scsistat = ABORT_ST;
+#if (FW_TYPE==_UCB_MGR_)
+ p_ucb->UCB_status=SCCB_ABORT;
+#endif
+ p_Sccb->Sccb_scsimsg = SMABORT_TAG;
+
+ if(((PSCCBcard) pCurrCard)->currentSCCB == NULL)
+ {
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ ssel(ioport, thisCard);
+ }
+ else
+ {
+ pSaveSCCB = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail((PSCCBcard) pCurrCard, thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSCCB;
+ }
+ }
+ MENABLE_INT(ioport);
+ return(0);
+ }
+ else
+ {
+ currTar_Info = &sccbMgrTbl[thisCard][p_Sccb->TargID];
+
+ if(BL_Card[thisCard].discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_Sccb->Lun]]
+ == p_Sccb)
+ {
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ return(0);
+ }
+ }
+ }
+ }
+ }
+ return(-1);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_my_int
+ *
+ * Description: Do a quick check to determine if there is a pending
+ * interrupt for this card and disable the IRQ Pin if so.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+u08bits SccbMgr_my_int(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+UCHAR SccbMgr_my_int(USHORT pCurrCard)
+#else
+UCHAR SccbMgr_my_int(ULONG pCurrCard)
+#endif
+#endif
+{
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+ ioport = ((PSCCBcard)pCurrCard)->ioPort;
+
+ if (RD_HARPOON(ioport+hp_int_status) & INT_ASSERTED)
+ {
+
+#if defined(DOS)
+ MDISABLE_INT(ioport);
+#endif
+
+ return(TRUE);
+ }
+
+ else
+
+ return(FALSE);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_isr
+ *
+ * Description: This is our entry point when an interrupt is generated
+ * by the card and the upper level driver passes it on to
+ * us.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+s32bits SccbMgr_isr(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+int SccbMgr_isr(USHORT pCurrCard)
+#else
+int SccbMgr_isr(ULONG pCurrCard)
+#endif
+#endif
+{
+ PSCCB currSCCB;
+ UCHAR thisCard,result,bm_status, bm_int_st;
+ USHORT hp_int;
+ UCHAR i, target;
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+
+ thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+ ioport = ((PSCCBcard)pCurrCard)->ioPort;
+
+ MDISABLE_INT(ioport);
+
+#if defined(BUGBUG)
+ WR_HARPOON(ioport+hp_user_defined_D, RD_HARPOON(ioport+hp_int_status));
+#endif
+
+ if ((bm_int_st=RD_HARPOON(ioport+hp_int_status)) & EXT_STATUS_ON)
+ bm_status = RD_HARPOON(ioport+hp_ext_status) & (UCHAR)BAD_EXT_STATUS;
+ else
+ bm_status = 0;
+
+ WR_HARPOON(ioport+hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ while ((hp_int = RDW_HARPOON((ioport+hp_intstat)) & default_intena) |
+ bm_status)
+ {
+
+ currSCCB = ((PSCCBcard)pCurrCard)->currentSCCB;
+
+#if defined(BUGBUG)
+ Debug_Load(thisCard,(UCHAR) 0XFF);
+ Debug_Load(thisCard,bm_int_st);
+
+ Debug_Load(thisCard,hp_int_0);
+ Debug_Load(thisCard,hp_int_1);
+#endif
+
+
+ if (hp_int & (FIFO | TIMEOUT | RESET | SCAM_SEL) || bm_status) {
+ result = SccbMgr_bad_isr(ioport,thisCard,((PSCCBcard)pCurrCard),hp_int);
+ WRW_HARPOON((ioport+hp_intstat), (FIFO | TIMEOUT | RESET | SCAM_SEL));
+ bm_status = 0;
+
+ if (result) {
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+ MENABLE_INT(ioport);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return(result);
+ }
+ }
+
+
+ else if (hp_int & ICMD_COMP) {
+
+ if ( !(hp_int & BUS_FREE) ) {
+ /* Wait for the BusFree before starting a new command. We
+ must also check for being reselected since the BusFree
+ may not show up if another device reselects us in 1.5us or
+ less. SRR Wednesday, 3/8/1995.
+ */
+ while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL))) ;
+ }
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT)
+
+ phaseChkFifo(ioport, thisCard);
+
+/* WRW_HARPOON((ioport+hp_intstat),
+ (BUS_FREE | ICMD_COMP | ITAR_DISC | XFER_CNT_0));
+ */
+
+ WRW_HARPOON((ioport+hp_intstat), CLR_ALL_INT_1);
+
+ autoCmdCmplt(ioport,thisCard);
+
+ }
+
+
+ else if (hp_int & ITAR_DISC)
+ {
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT) {
+
+ phaseChkFifo(ioport, thisCard);
+
+ }
+
+ if (RD_HARPOON(ioport+hp_gp_reg_1) == SMSAVE_DATA_PTR) {
+
+ WR_HARPOON(ioport+hp_gp_reg_1, 0x00);
+ currSCCB->Sccb_XferState |= F_NO_DATA_YET;
+
+ currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
+ }
+
+ currSCCB->Sccb_scsistat = DISCONNECT_ST;
+ queueDisconnect(currSCCB,thisCard);
+
+ /* Wait for the BusFree before starting a new command. We
+ must also check for being reselected since the BusFree
+ may not show up if another device reselects us in 1.5us or
+ less. SRR Wednesday, 3/8/1995.
+ */
+ while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL)) &&
+ !((RDW_HARPOON((ioport+hp_intstat)) & PHASE) &&
+ RD_HARPOON((ioport+hp_scsisig)) ==
+ (SCSI_BSY | SCSI_REQ | SCSI_CD | SCSI_MSG | SCSI_IOBIT))) ;
+
+ /*
+ The additional loop exit condition above detects a timing problem
+ with the revision D/E harpoon chips. The caller should reset the
+ host adapter to recover when 0xFE is returned.
+ */
+ if (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL)))
+ {
+ mOS_Lock((PSCCBcard)pCurrCard);
+ MENABLE_INT(ioport);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return 0xFE;
+ }
+
+ WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC));
+
+
+ ((PSCCBcard)pCurrCard)->globalFlags |= F_NEW_SCCB_CMD;
+
+ }
+
+
+ else if (hp_int & RSEL) {
+
+ WRW_HARPOON((ioport+hp_intstat), (PROG_HLT | RSEL | PHASE | BUS_FREE));
+
+ if (RDW_HARPOON((ioport+hp_intstat)) & ITAR_DISC)
+ {
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT)
+ {
+ phaseChkFifo(ioport, thisCard);
+ }
+
+ if (RD_HARPOON(ioport+hp_gp_reg_1) == SMSAVE_DATA_PTR)
+ {
+ WR_HARPOON(ioport+hp_gp_reg_1, 0x00);
+ currSCCB->Sccb_XferState |= F_NO_DATA_YET;
+ currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
+ }
+
+ WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC));
+ currSCCB->Sccb_scsistat = DISCONNECT_ST;
+ queueDisconnect(currSCCB,thisCard);
+ }
+
+ sres(ioport,thisCard,((PSCCBcard)pCurrCard));
+ phaseDecode(ioport,thisCard);
+
+ }
+
+
+ else if ((hp_int & IDO_STRT) && (!(hp_int & BUS_FREE)))
+ {
+
+ WRW_HARPOON((ioport+hp_intstat), (IDO_STRT | XFER_CNT_0));
+ phaseDecode(ioport,thisCard);
+
+ }
+
+
+ else if ( (hp_int & IUNKWN) || (hp_int & PROG_HLT) )
+ {
+ WRW_HARPOON((ioport+hp_intstat), (PHASE | IUNKWN | PROG_HLT));
+ if ((RD_HARPOON(ioport+hp_prgmcnt_0) & (UCHAR)0x3f)< (UCHAR)SELCHK)
+ {
+ phaseDecode(ioport,thisCard);
+ }
+ else
+ {
+ /* Harpoon problem some SCSI target device respond to selection
+ with short BUSY pulse (<400ns) this will make the Harpoon is not able
+ to latch the correct Target ID into reg. x53.
+ The work around require to correct this reg. But when write to this
+ reg. (0x53) also increment the FIFO write addr reg (0x6f), thus we
+ need to read this reg first then restore it later. After update to 0x53 */
+
+ i = (UCHAR)(RD_HARPOON(ioport+hp_fifowrite));
+ target = (UCHAR)(RD_HARPOON(ioport+hp_gp_reg_3));
+ WR_HARPOON(ioport+hp_xfer_pad, (UCHAR) ID_UNLOCK);
+ WR_HARPOON(ioport+hp_select_id, (UCHAR)(target | target<<4));
+ WR_HARPOON(ioport+hp_xfer_pad, (UCHAR) 0x00);
+ WR_HARPOON(ioport+hp_fifowrite, i);
+ WR_HARPOON(ioport+hp_autostart_3, (AUTO_IMMED+TAG_STRT));
+ }
+ }
+
+ else if (hp_int & XFER_CNT_0) {
+
+ WRW_HARPOON((ioport+hp_intstat), XFER_CNT_0);
+
+ schkdd(ioport,thisCard);
+
+ }
+
+
+ else if (hp_int & BUS_FREE) {
+
+ WRW_HARPOON((ioport+hp_intstat), BUS_FREE);
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT) {
+
+ hostDataXferAbort(ioport,thisCard,currSCCB);
+ }
+
+ phaseBusFree(ioport,thisCard);
+ }
+
+
+ else if (hp_int & ITICKLE) {
+
+ WRW_HARPOON((ioport+hp_intstat), ITICKLE);
+ ((PSCCBcard)pCurrCard)->globalFlags |= F_NEW_SCCB_CMD;
+ }
+
+
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_NEW_SCCB_CMD) {
+
+
+ ((PSCCBcard)pCurrCard)->globalFlags &= ~F_NEW_SCCB_CMD;
+
+
+ if (((PSCCBcard)pCurrCard)->currentSCCB == NULL) {
+
+ queueSearchSelect(((PSCCBcard)pCurrCard),thisCard);
+ }
+
+ if (((PSCCBcard)pCurrCard)->currentSCCB != NULL) {
+ ((PSCCBcard)pCurrCard)->globalFlags &= ~F_NEW_SCCB_CMD;
+ ssel(ioport,thisCard);
+ }
+
+ break;
+
+ }
+
+ } /*end while */
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+ MENABLE_INT(ioport);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Sccb_bad_isr
+ *
+ * Description: Some type of interrupt has occurred which is slightly
+ * out of the ordinary. We will now decode it fully, in
+ * this routine. This is broken up in an attempt to save
+ * processing time.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+UCHAR SccbMgr_bad_isr(USHORT p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int)
+#else
+UCHAR SccbMgr_bad_isr(ULONG p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int)
+#endif
+{
+#if defined(HARP_REVX)
+ ULONG timer;
+#endif
+UCHAR temp, ScamFlg;
+PSCCBMgr_tar_info currTar_Info;
+PNVRamInfo pCurrNvRam;
+
+
+ if (RD_HARPOON(p_port+hp_ext_status) &
+ (BM_FORCE_OFF | PCI_DEV_TMOUT | BM_PARITY_ERR | PIO_OVERRUN) )
+ {
+
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+ {
+
+ hostDataXferAbort(p_port,p_card, pCurrCard->currentSCCB);
+ }
+
+ if (RD_HARPOON(p_port+hp_pci_stat_cfg) & REC_MASTER_ABORT)
+
+ {
+ WR_HARPOON(p_port+hp_pci_stat_cfg,
+ (RD_HARPOON(p_port+hp_pci_stat_cfg) & ~REC_MASTER_ABORT));
+
+ WR_HARPOON(p_port+hp_host_blk_cnt, 0x00);
+
+ }
+
+ if (pCurrCard->currentSCCB != NULL)
+ {
+
+ if (!pCurrCard->currentSCCB->HostStatus)
+ pCurrCard->currentSCCB->HostStatus = SCCB_BM_ERR;
+
+ sxfrp(p_port,p_card);
+
+ temp = (UCHAR)(RD_HARPOON(p_port+hp_ee_ctrl) &
+ (EXT_ARB_ACK | SCSI_TERM_ENA_H));
+ WR_HARPOON(p_port+hp_ee_ctrl, ((UCHAR)temp | SEE_MS | SEE_CS));
+ WR_HARPOON(p_port+hp_ee_ctrl, temp);
+
+ if (!(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+ {
+ phaseDecode(p_port,p_card);
+ }
+ }
+ }
+
+
+ else if (p_int & RESET)
+ {
+
+ WR_HARPOON(p_port+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(p_port+hp_sys_ctrl, 0x00);
+ if (pCurrCard->currentSCCB != NULL) {
+
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+
+ hostDataXferAbort(p_port,p_card, pCurrCard->currentSCCB);
+ }
+
+
+ DISABLE_AUTO(p_port);
+
+ sresb(p_port,p_card);
+
+ while(RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST) {}
+
+ pCurrNvRam = pCurrCard->pNvRamInfo;
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(p_port, SCAM_CONFIG/2);
+ }
+
+ XbowInit(p_port, ScamFlg);
+
+ scini(p_card, pCurrCard->ourId, 0);
+
+ return(0xFF);
+ }
+
+
+ else if (p_int & FIFO) {
+
+ WRW_HARPOON((p_port+hp_intstat), FIFO);
+
+#if defined(HARP_REVX)
+
+ for (timer=0x00FFFFFFL; timer != 0x00000000L; timer--) {
+
+ if (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY)
+ break;
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)
+ break;
+ }
+
+
+ if ( (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY) &&
+ (RD_HARPOON(p_port+hp_fiforead) !=
+ RD_HARPOON(p_port+hp_fifowrite)) &&
+ (RD_HARPOON(p_port+hp_xfercnt_0))
+ )
+
+ WR_HARPOON((p_port+hp_xferstat), 0x01);
+
+/* else
+ */
+/* sxfrp(p_port,p_card);
+ */
+#else
+ if (pCurrCard->currentSCCB != NULL)
+ sxfrp(p_port,p_card);
+#endif
+ }
+
+ else if (p_int & TIMEOUT)
+ {
+
+ DISABLE_AUTO(p_port);
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (PROG_HLT | TIMEOUT | SEL |BUS_FREE | PHASE | IUNKWN));
+
+ pCurrCard->currentSCCB->HostStatus = SCCB_SELECTION_TIMEOUT;
+
+
+ currTar_Info = &sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
+ if((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ currTar_Info->TarLUNBusy[pCurrCard->currentSCCB->Lun] = FALSE;
+ else
+ currTar_Info->TarLUNBusy[0] = FALSE;
+
+
+ if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ currTar_Info->TarSyncCtrl = 0;
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ }
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+ sssyncv(p_port, pCurrCard->currentSCCB->TargID, NARROW_SCSI,currTar_Info);
+
+ queueCmdComplete(pCurrCard, pCurrCard->currentSCCB, p_card);
+
+ }
+
+#if defined(SCAM_LEV_2)
+
+ else if (p_int & SCAM_SEL)
+ {
+
+ scarb(p_port,LEVEL2_TAR);
+ scsel(p_port);
+ scasid(p_card, p_port);
+
+ scbusf(p_port);
+
+ WRW_HARPOON((p_port+hp_intstat), SCAM_SEL);
+ }
+#endif
+
+ return(0x00);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_scsi_reset
+ *
+ * Description: A SCSI bus reset will be generated and all outstanding
+ * Sccbs will be returned via the callback.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_scsi_reset(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_scsi_reset(USHORT pCurrCard)
+#else
+void SccbMgr_scsi_reset(ULONG pCurrCard)
+#endif
+#endif
+{
+ UCHAR thisCard;
+
+ thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+
+ if (((PSCCBcard) pCurrCard)->globalFlags & F_GREEN_PC)
+ {
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_sys_ctrl, 0x00);
+ }
+
+ sresb(((PSCCBcard)pCurrCard)->ioPort,thisCard);
+
+ if (RD_HARPOON(((PSCCBcard)pCurrCard)->ioPort+hp_ext_status) & BM_CMD_BUSY)
+ {
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_page_ctrl,
+ (RD_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_page_ctrl)
+ & ~SCATTER_EN));
+
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_sg_addr,0x00);
+
+ ((PSCCBcard) pCurrCard)->globalFlags &= ~F_HOST_XFER_ACT;
+ busMstrTimeOut(((PSCCBcard) pCurrCard)->ioPort);
+
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_int_mask,
+ (INT_CMD_COMPL | SCSI_INTERRUPT));
+ }
+
+/*
+ if (utilEERead(((PSCCBcard)pCurrCard)->ioPort, (SCAM_CONFIG/2))
+ & SCAM_ENABLED)
+*/
+ scini(thisCard, ((PSCCBcard)pCurrCard)->ourId, 0);
+
+#if (FW_TYPE==_UCB_MGR_)
+ ((PSCCBcard)pCurrCard)->cardInfo->ai_AEN_routine(0x01,pCurrCard,0,0,0,0);
+#endif
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_timer_expired
+ *
+ * Description: This function allow me to kill my own job that has not
+ * yet completed, and has cause a timeout to occur. This
+ * timeout has caused the upper level driver to call this
+ * function.
+ *
+ *---------------------------------------------------------------------*/
+
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_timer_expired(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_timer_expired(USHORT pCurrCard)
+#else
+void SccbMgr_timer_expired(ULONG pCurrCard)
+#endif
+#endif
+{
+}
+
+#if defined(DOS)
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_status
+ *
+ * Description: This function returns the number of outstanding SCCB's.
+ * This is specific to the DOS enviroment, which needs this
+ * to help them keep protected and real mode commands staight.
+ *
+ *---------------------------------------------------------------------*/
+
+USHORT SccbMgr_status(USHORT pCurrCard)
+{
+ return(BL_Card[pCurrCard].cmdCounter);
+}
+#endif
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitAll()
+{
+ UCHAR thisCard;
+
+ for (thisCard = 0; thisCard < MAX_CARDS; thisCard++)
+ {
+ SccbMgrTableInitCard(&BL_Card[thisCard],thisCard);
+
+ BL_Card[thisCard].ioPort = 0x00;
+ BL_Card[thisCard].cardInfo = NULL;
+ BL_Card[thisCard].cardIndex = 0xFF;
+ BL_Card[thisCard].ourId = 0x00;
+ BL_Card[thisCard].pNvRamInfo = NULL;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitCard(PSCCBcard pCurrCard, UCHAR p_card)
+{
+ UCHAR scsiID, qtag;
+
+ for (qtag = 0; qtag < QUEUE_DEPTH; qtag++)
+ {
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ }
+
+ for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++)
+ {
+ sccbMgrTbl[p_card][scsiID].TarStatus = 0;
+ sccbMgrTbl[p_card][scsiID].TarEEValue = 0;
+ SccbMgrTableInitTarget(p_card, scsiID);
+ }
+
+ pCurrCard->scanIndex = 0x00;
+ pCurrCard->currentSCCB = NULL;
+ pCurrCard->globalFlags = 0x00;
+ pCurrCard->cmdCounter = 0x00;
+ pCurrCard->tagQ_Lst = 0x01;
+ pCurrCard->discQCount = 0;
+
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitTarget(UCHAR p_card, UCHAR target)
+{
+
+ UCHAR lun, qtag;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][target];
+
+ currTar_Info->TarSelQ_Cnt = 0;
+ currTar_Info->TarSyncCtrl = 0;
+
+ currTar_Info->TarSelQ_Head = NULL;
+ currTar_Info->TarSelQ_Tail = NULL;
+ currTar_Info->TarTagQ_Cnt = 0;
+ currTar_Info->TarLUN_CA = FALSE;
+
+
+ for (lun = 0; lun < MAX_LUN; lun++)
+ {
+ currTar_Info->TarLUNBusy[lun] = FALSE;
+ currTar_Info->LunDiscQ_Idx[lun] = 0;
+ }
+
+ for (qtag = 0; qtag < QUEUE_DEPTH; qtag++)
+ {
+ if(BL_Card[p_card].discQ_Tbl[qtag] != NULL)
+ {
+ if(BL_Card[p_card].discQ_Tbl[qtag]->TargID == target)
+ {
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ BL_Card[p_card].discQCount--;
+ }
+ }
+ }
+}
+
+#if defined(BUGBUG)
+
+/*****************************************************************
+ * Save the current byte in the debug array
+ *****************************************************************/
+
+
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data)
+{
+ debug_int[p_card][debug_index[p_card]] = p_bug_data;
+ debug_index[p_card]++;
+
+ if (debug_index[p_card] == debug_size)
+
+ debug_index[p_card] = 0;
+}
+
+#endif
+#ident "$Id: sccb_dat.c 1.10 1997/02/22 03:16:02 awin Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: sccb_dat.c $
+ *
+ * Description: Functions relating to handling of the SCCB interface
+ * between the device driver and the HARPOON.
+ *
+ * $Date: 1997/02/22 03:16:02 $
+ *
+ * $Revision: 1.10 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <harpoon.h>*/
+
+/*
+** IMPORTANT NOTE!!!
+**
+** You MUST preassign all data to a valid value or zero. This is
+** required due to the MS compiler bug under OS/2 and Solaris Real-Mode
+** driver environment.
+*/
+
+
+SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] = { { { 0 } } };
+SCCBCARD BL_Card[MAX_CARDS] = { { 0 } };
+SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR] = { { { 0 } } };
+NVRAMINFO nvRamInfo[MAX_MB_CARDS] = { { 0 } };
+
+
+#if defined(OS2)
+void (far *s_PhaseTbl[8]) (ULONG, UCHAR) = { 0 };
+UCHAR temp_id_string[ID_STRING_LENGTH] = { 0 };
+#elif defined(SOLARIS_REAL_MODE) || defined(__STDC__)
+void (*s_PhaseTbl[8]) (ULONG, UCHAR) = { 0 };
+#else
+void (*s_PhaseTbl[8]) ();
+#endif
+
+#if defined(DOS)
+UCHAR first_time = 0;
+#endif
+
+UCHAR mbCards = 0;
+UCHAR scamHAString[] = {0x63, 0x07, 'B', 'U', 'S', 'L', 'O', 'G', 'I', 'C', \
+ ' ', 'B', 'T', '-', '9', '3', '0', \
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, \
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
+
+USHORT default_intena = 0;
+
+#if defined(BUGBUG)
+UCHAR debug_int[MAX_CARDS][debug_size] = { 0 };
+UCHAR debug_index[MAX_CARDS] = { 0 };
+UCHAR reserved_1[3] = { 0 };
+#endif
+#ident "$Id: scsi.c 1.23 1997/07/09 21:42:54 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: scsi.c $
+ *
+ * Description: Functions for handling SCSI bus functions such as
+ * selection/reselection, sync negotiation, message-in
+ * decoding.
+ *
+ * $Date: 1997/07/09 21:42:54 $
+ *
+ * $Revision: 1.23 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sfetm
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ * for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sfm(USHORT port, PSCCB pCurrSCCB)
+#else
+UCHAR sfm(ULONG port, PSCCB pCurrSCCB)
+#endif
+{
+ UCHAR message;
+ USHORT TimeOutLoop;
+
+ TimeOutLoop = 0;
+ while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (TimeOutLoop++ < 20000) ){}
+
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ message = RD_HARPOON(port+hp_scsidata_0);
+
+ WR_HARPOON(port+hp_scsisig, SCSI_ACK + S_MSGI_PH);
+
+
+ if (TimeOutLoop > 20000)
+ message = 0x00; /* force message byte = 0 if Time Out on Req */
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (RD_HARPOON(port+hp_addstat) & SCSI_PAR_ERR))
+ {
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ WR_HARPOON(port+hp_xferstat, 0);
+ WR_HARPOON(port+hp_fiforead, 0);
+ WR_HARPOON(port+hp_fifowrite, 0);
+ if (pCurrSCCB != NULL)
+ {
+ pCurrSCCB->Sccb_scsimsg = SMPARITY;
+ }
+ message = 0x00;
+ do
+ {
+ ACCEPT_MSG_ATN(port);
+ TimeOutLoop = 0;
+ while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (TimeOutLoop++ < 20000) ){}
+ if (TimeOutLoop > 20000)
+ {
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ return(message);
+ }
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) != S_MSGI_PH)
+ {
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ return(message);
+ }
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ RD_HARPOON(port+hp_scsidata_0);
+
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+ }while(1);
+
+ }
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ WR_HARPOON(port+hp_xferstat, 0);
+ WR_HARPOON(port+hp_fiforead, 0);
+ WR_HARPOON(port+hp_fifowrite, 0);
+ return(message);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ssel
+ *
+ * Description: Load up automation and select target device.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void ssel(USHORT port, UCHAR p_card)
+#else
+void ssel(ULONG port, UCHAR p_card)
+#endif
+{
+
+#if defined(DOS)
+ UCHAR auto_loaded, i, target, *theCCB;
+#elif defined(OS2)
+ UCHAR auto_loaded, i, target;
+ UCHAR far *theCCB;
+#else
+ UCHAR auto_loaded, i, target, *theCCB;
+#endif
+
+#if defined(DOS)
+ USHORT cdb_reg;
+#else
+ ULONG cdb_reg;
+#endif
+ PSCCBcard CurrCard;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+ UCHAR lastTag, lun;
+
+ CurrCard = &BL_Card[p_card];
+ currSCCB = CurrCard->currentSCCB;
+ target = currSCCB->TargID;
+ currTar_Info = &sccbMgrTbl[p_card][target];
+ lastTag = CurrCard->tagQ_Lst;
+
+ ARAM_ACCESS(port);
+
+
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
+ currSCCB->ControlByte &= ~F_USE_CMD_Q;
+
+ if(((CurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+
+ lun = currSCCB->Lun;
+ else
+ lun = 0;
+
+
+#if defined(DOS)
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+
+#else
+
+ if (CurrCard->globalFlags & F_TAG_STARTED)
+ {
+ if (!(currSCCB->ControlByte & F_USE_CMD_Q))
+ {
+ if ((currTar_Info->TarLUN_CA == FALSE)
+ && ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
+ == TAG_Q_TRYING))
+ {
+
+ if (currTar_Info->TarTagQ_Cnt !=0)
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+
+ else {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+ } /*End non-tagged */
+
+ else {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+ } /*!Use cmd Q Tagged */
+
+ else {
+ if (currTar_Info->TarLUN_CA == TRUE)
+ {
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+
+ } /*else use cmd Q tagged */
+
+ } /*if glob tagged started */
+
+ else {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+#endif /* DOS */
+
+
+
+ if((((CurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ || (!(currSCCB->ControlByte & F_USE_CMD_Q))))
+ {
+ if(CurrCard->discQCount >= QUEUE_DEPTH)
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+ for (i = 1; i < QUEUE_DEPTH; i++)
+ {
+ if (++lastTag >= QUEUE_DEPTH) lastTag = 1;
+ if (CurrCard->discQ_Tbl[lastTag] == NULL)
+ {
+ CurrCard->tagQ_Lst = lastTag;
+ currTar_Info->LunDiscQ_Idx[lun] = lastTag;
+ CurrCard->discQ_Tbl[lastTag] = currSCCB;
+ CurrCard->discQCount++;
+ break;
+ }
+ }
+ if(i == QUEUE_DEPTH)
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+ }
+
+
+
+ auto_loaded = FALSE;
+
+ WR_HARPOON(port+hp_select_id, target);
+ WR_HARPOON(port+hp_gp_reg_3, target); /* Use by new automation logic */
+
+ if (currSCCB->OperationCode == RESET_COMMAND) {
+ WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+
+ (currSCCB->Sccb_idmsg & ~DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+NP);
+
+ currSCCB->Sccb_scsimsg = SMDEV_RESET;
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ auto_loaded = TRUE;
+ currSCCB->Sccb_scsistat = SELECT_BDR_ST;
+
+ if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ currTar_Info->TarSyncCtrl = 0;
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ }
+
+#if defined(WIDE_SCSI)
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ }
+#endif
+
+ sssyncv(port, target, NARROW_SCSI,currTar_Info);
+ SccbMgrTableInitTarget(p_card, target);
+
+ }
+
+ else if(currSCCB->Sccb_scsistat == ABORT_ST)
+ {
+ WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+
+ (currSCCB->Sccb_idmsg & ~DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+
+ (((UCHAR)(currSCCB->ControlByte & TAG_TYPE_MASK)
+ >> 6) | (UCHAR)0x20)));
+ WRW_HARPOON((port+SYNC_MSGS+2),
+ (MPM_OP+AMSG_OUT+currSCCB->Sccb_tag));
+ WRW_HARPOON((port+SYNC_MSGS+4), (BRH_OP+ALWAYS+NP ));
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ auto_loaded = TRUE;
+
+ }
+
+#if defined(WIDE_SCSI)
+
+
+ else if (!(currTar_Info->TarStatus & WIDE_NEGOCIATED)) {
+ auto_loaded = siwidn(port,p_card);
+ currSCCB->Sccb_scsistat = SELECT_WN_ST;
+ }
+
+#endif
+
+
+ else if (!((currTar_Info->TarStatus & TAR_SYNC_MASK)
+ == SYNC_SUPPORTED)) {
+ auto_loaded = sisyncn(port,p_card, FALSE);
+ currSCCB->Sccb_scsistat = SELECT_SN_ST;
+ }
+
+
+ if (!auto_loaded)
+ {
+
+#if !defined(DOS)
+ if (currSCCB->ControlByte & F_USE_CMD_Q)
+ {
+
+ CurrCard->globalFlags |= F_TAG_STARTED;
+
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
+ == TAG_Q_REJECT)
+ {
+ currSCCB->ControlByte &= ~F_USE_CMD_Q;
+
+ /* Fix up the start instruction with a jump to
+ Non-Tag-CMD handling */
+ WRW_HARPOON((port+ID_MSG_STRT),BRH_OP+ALWAYS+NTCMD);
+
+ WRW_HARPOON((port+NON_TAG_ID_MSG),
+ (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+
+ /* Setup our STATE so we know what happend when
+ the wheels fall off. */
+ currSCCB->Sccb_scsistat = SELECT_ST;
+
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+ else
+ {
+ WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2), (MPM_OP+AMSG_OUT+
+ (((UCHAR)(currSCCB->ControlByte & TAG_TYPE_MASK)
+ >> 6) | (UCHAR)0x20)));
+
+ for (i = 1; i < QUEUE_DEPTH; i++)
+ {
+ if (++lastTag >= QUEUE_DEPTH) lastTag = 1;
+ if (CurrCard->discQ_Tbl[lastTag] == NULL)
+ {
+ WRW_HARPOON((port+ID_MSG_STRT+6),
+ (MPM_OP+AMSG_OUT+lastTag));
+ CurrCard->tagQ_Lst = lastTag;
+ currSCCB->Sccb_tag = lastTag;
+ CurrCard->discQ_Tbl[lastTag] = currSCCB;
+ CurrCard->discQCount++;
+ break;
+ }
+ }
+
+
+ if ( i == QUEUE_DEPTH )
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+
+ currSCCB->Sccb_scsistat = SELECT_Q_ST;
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ }
+ }
+
+ else
+ {
+#endif /* !DOS */
+
+ WRW_HARPOON((port+ID_MSG_STRT),BRH_OP+ALWAYS+NTCMD);
+
+ WRW_HARPOON((port+NON_TAG_ID_MSG),
+ (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+ currSCCB->Sccb_scsistat = SELECT_ST;
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+#if !defined(DOS)
+ }
+#endif
+
+
+#if defined(OS2)
+ theCCB = (UCHAR far *)&currSCCB->Cdb[0];
+#else
+ theCCB = (UCHAR *)&currSCCB->Cdb[0];
+#endif
+
+ cdb_reg = port + CMD_STRT;
+
+ for (i=0; i < currSCCB->CdbLength; i++)
+ {
+ WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + *theCCB));
+ cdb_reg +=2;
+ theCCB++;
+ }
+
+ if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
+ WRW_HARPOON(cdb_reg, (BRH_OP+ALWAYS+ NP));
+
+ } /* auto_loaded */
+
+#if defined(WIDE_SCSI)
+ WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+ WR_HARPOON(port+hp_xferstat, 0x00);
+#endif
+
+ WRW_HARPOON((port+hp_intstat), (PROG_HLT | TIMEOUT | SEL | BUS_FREE));
+
+ WR_HARPOON(port+hp_portctrl_0,(SCSI_PORT));
+
+
+ if (!(currSCCB->Sccb_MGRFlags & F_DEV_SELECTED))
+ {
+ WR_HARPOON(port+hp_scsictrl_0, (SEL_TAR | ENA_ATN | ENA_RESEL | ENA_SCAM_SEL));
+ }
+ else
+ {
+
+/* auto_loaded = (RD_HARPOON(port+hp_autostart_3) & (UCHAR)0x1F);
+ auto_loaded |= AUTO_IMMED; */
+ auto_loaded = AUTO_IMMED;
+
+ DISABLE_AUTO(port);
+
+ WR_HARPOON(port+hp_autostart_3, auto_loaded);
+ }
+
+ SGRAM_ACCESS(port);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sres
+ *
+ * Description: Hookup the correct CCB and handle the incoming messages.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void sres(USHORT port, UCHAR p_card, PSCCBcard pCurrCard)
+#else
+void sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard)
+#endif
+{
+
+#if defined(V302)
+#ifdef DOS
+ UCHAR our_target,message, msgRetryCount;
+ extern UCHAR lun, tag;
+#else
+ UCHAR our_target,message,lun,tag, msgRetryCount;
+#endif
+
+#else /* V302 */
+ UCHAR our_target, message, lun = 0, tag, msgRetryCount;
+#endif /* V302 */
+
+
+ PSCCBMgr_tar_info currTar_Info;
+ PSCCB currSCCB;
+
+
+
+
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
+ DISABLE_AUTO(port);
+
+
+ WR_HARPOON((port+hp_scsictrl_0),(ENA_RESEL | ENA_SCAM_SEL));
+
+
+ currSCCB = pCurrCard->currentSCCB;
+ if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ currSCCB->Sccb_scsistat = BUS_FREE_ST;
+ }
+ if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+ {
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ currSCCB->Sccb_scsistat = BUS_FREE_ST;
+ }
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ currTar_Info->TarLUNBusy[currSCCB->Lun] = FALSE;
+ if(currSCCB->Sccb_scsistat != ABORT_ST)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[currSCCB->Lun]]
+ = NULL;
+ }
+ }
+ else
+ {
+ currTar_Info->TarLUNBusy[0] = FALSE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(currSCCB->Sccb_scsistat != ABORT_ST)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }
+ }else
+ {
+ if(currSCCB->Sccb_scsistat != ABORT_ST)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ }
+
+ queueSelectFail(&BL_Card[p_card],p_card);
+ }
+
+#if defined(WIDE_SCSI)
+ WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+#endif
+
+
+ our_target = (UCHAR)(RD_HARPOON(port+hp_select_id) >> 4);
+ currTar_Info = &sccbMgrTbl[p_card][our_target];
+
+
+ msgRetryCount = 0;
+ do
+ {
+
+#if defined(V302)
+
+ message = GetTarLun(port, p_card, our_target, pCurrCard, &tag, &lun);
+
+#else /* V302 */
+
+ currTar_Info = &sccbMgrTbl[p_card][our_target];
+ tag = 0;
+
+
+ while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+ {
+ if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+ {
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ return;
+ }
+ }
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH)
+ {
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+
+ if (message <= (0x80 | LUN_MASK))
+ {
+ lun = message & (UCHAR)LUN_MASK;
+
+#if !defined(DOS)
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING)
+ {
+ if (currTar_Info->TarTagQ_Cnt != 0)
+ {
+
+ if (!(currTar_Info->TarLUN_CA))
+ {
+ ACCEPT_MSG(port); /*Release the ACK for ID msg. */
+
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+ ACCEPT_MSG(port);
+ }
+
+ else
+ message = FALSE;
+
+ if(message != FALSE)
+ {
+ tag = sfm(port,pCurrCard->currentSCCB);
+
+ if (!(tag))
+ message = FALSE;
+ }
+
+ } /*C.A. exists! */
+
+ } /*End Q cnt != 0 */
+
+ } /*End Tag cmds supported! */
+#endif /* !DOS */
+
+ } /*End valid ID message. */
+
+ else
+ {
+
+ ACCEPT_MSG_ATN(port);
+ }
+
+ } /* End good id message. */
+
+ else
+ {
+
+ message = FALSE;
+ }
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (PHASE | RESET)) &&
+ !(RD_HARPOON(port+hp_scsisig) & SCSI_REQ) &&
+ (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ;
+
+ return;
+ }
+
+#endif /* V302 */
+
+ if(message == FALSE)
+ {
+ msgRetryCount++;
+ if(msgRetryCount == 1)
+ {
+ SendMsg(port, SMPARITY);
+ }
+ else
+ {
+ SendMsg(port, SMDEV_RESET);
+
+ sssyncv(port, our_target, NARROW_SCSI,currTar_Info);
+
+ if (sccbMgrTbl[p_card][our_target].TarEEValue & EE_SYNC_MASK)
+ {
+
+ sccbMgrTbl[p_card][our_target].TarStatus &= ~TAR_SYNC_MASK;
+
+ }
+
+ if (sccbMgrTbl[p_card][our_target].TarEEValue & EE_WIDE_SCSI)
+ {
+
+ sccbMgrTbl[p_card][our_target].TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+
+ queueFlushTargSccb(p_card, our_target, SCCB_COMPLETE);
+ SccbMgrTableInitTarget(p_card,our_target);
+ return;
+ }
+ }
+ }while(message == FALSE);
+
+
+
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[lun]];
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ ACCEPT_MSG(port);
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ }
+ }
+ else
+ {
+ currTar_Info->TarLUNBusy[0] = TRUE;
+
+
+ if (tag)
+ {
+ if (pCurrCard->discQ_Tbl[tag] != NULL)
+ {
+ pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[tag];
+ currTar_Info->TarTagQ_Cnt--;
+ ACCEPT_MSG(port);
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ }
+ }else
+ {
+ pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]];
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ ACCEPT_MSG(port);
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ }
+ }
+ }
+
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ if(pCurrCard->currentSCCB->Sccb_scsistat == ABORT_ST)
+ {
+ /* During Abort Tag command, the target could have got re-selected
+ and completed the command. Check the select Q and remove the CCB
+ if it is in the Select Q */
+ queueFindSccb(pCurrCard->currentSCCB, p_card);
+ }
+ }
+
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (PHASE | RESET)) &&
+ !(RD_HARPOON(port+hp_scsisig) & SCSI_REQ) &&
+ (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ;
+}
+
+#if defined(V302)
+
+#if defined(DOS)
+UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun)
+#else
+UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun)
+#endif
+{
+ UCHAR message;
+ PSCCBMgr_tar_info currTar_Info;
+
+
+ currTar_Info = &sccbMgrTbl[p_card][our_target];
+ *tag = 0;
+
+
+ while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+ {
+ if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+ {
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ return(TRUE);
+ }
+ }
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH)
+ {
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+
+ if (message <= (0x80 | LUN_MASK))
+ {
+ *lun = message & (UCHAR)LUN_MASK;
+
+#if !defined(DOS)
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING)
+ {
+ if (currTar_Info->TarTagQ_Cnt != 0)
+ {
+
+ if (!(currTar_Info->TarLUN_CA))
+ {
+ ACCEPT_MSG(port); /*Release the ACK for ID msg. */
+
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+ ACCEPT_MSG(port);
+ }
+
+ else
+ return(FALSE);
+
+ *tag = sfm(port,pCurrCard->currentSCCB);
+
+ if (!(*tag)) return(FALSE);
+
+ } /*C.A. exists! */
+
+ } /*End Q cnt != 0 */
+
+ } /*End Tag cmds supported! */
+#endif /* !DOS */
+
+ } /*End valid ID message. */
+
+ else
+ {
+
+ ACCEPT_MSG_ATN(port);
+ }
+
+ } /* End good id message. */
+
+ else
+ {
+
+ return(FALSE);
+ }
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ return(TRUE);
+ }
+ return(TRUE);
+}
+
+#endif /* V302 */
+
+#if defined(DOS)
+void SendMsg(USHORT port, UCHAR message)
+#else
+void SendMsg(ULONG port, UCHAR message)
+#endif
+{
+ while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+ {
+ if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+ {
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ return;
+ }
+ }
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGO_PH)
+ {
+ WRW_HARPOON((port+hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
+
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_BUS_EN);
+
+ WR_HARPOON(port+hp_scsidata_0,message);
+
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+ ACCEPT_MSG(port);
+
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+ if ((message == SMABORT) || (message == SMDEV_RESET) ||
+ (message == SMABORT_TAG) )
+ {
+ while(!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | PHASE))) {}
+
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sdecm
+ *
+ * Description: Determine the proper responce to the message from the
+ * target device.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sdecm(UCHAR message, USHORT port, UCHAR p_card)
+#else
+void sdecm(UCHAR message, ULONG port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+ PSCCBcard CurrCard;
+ PSCCBMgr_tar_info currTar_Info;
+
+ CurrCard = &BL_Card[p_card];
+ currSCCB = CurrCard->currentSCCB;
+
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ if (message == SMREST_DATA_PTR)
+ {
+ if (!(currSCCB->Sccb_XferState & F_NO_DATA_YET))
+ {
+ currSCCB->Sccb_ATC = currSCCB->Sccb_savedATC;
+
+ hostDataXferRestart(currSCCB);
+ }
+
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+ else if (message == SMCMD_COMP)
+ {
+
+
+ if (currSCCB->Sccb_scsistat == SELECT_Q_ST)
+ {
+ currTar_Info->TarStatus &= ~(UCHAR)TAR_TAG_Q_MASK;
+ currTar_Info->TarStatus |= (UCHAR)TAG_Q_REJECT;
+ }
+
+ ACCEPT_MSG(port);
+
+ }
+
+ else if ((message == SMNO_OP) || (message >= SMIDENT)
+ || (message == SMINIT_RECOVERY) || (message == SMREL_RECOVERY))
+ {
+
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+ else if (message == SMREJECT)
+ {
+
+ if ((currSCCB->Sccb_scsistat == SELECT_SN_ST) ||
+ (currSCCB->Sccb_scsistat == SELECT_WN_ST) ||
+ ((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING ) ||
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING ) )
+
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+
+ ACCEPT_MSG(port);
+
+
+ while ((!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))) {}
+
+ if(currSCCB->Lun == 0x00)
+ {
+ if ((currSCCB->Sccb_scsistat == SELECT_SN_ST))
+ {
+
+ currTar_Info->TarStatus |= (UCHAR)SYNC_SUPPORTED;
+
+ currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
+ }
+
+#if defined(WIDE_SCSI)
+ else if ((currSCCB->Sccb_scsistat == SELECT_WN_ST))
+ {
+
+
+ currTar_Info->TarStatus = (currTar_Info->TarStatus &
+ ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+ currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
+
+ }
+#endif
+
+ else if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING )
+ {
+ currTar_Info->TarStatus = (currTar_Info->TarStatus &
+ ~(UCHAR)TAR_TAG_Q_MASK) | TAG_Q_REJECT;
+
+
+ currSCCB->ControlByte &= ~F_USE_CMD_Q;
+ CurrCard->discQCount--;
+ CurrCard->discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ currSCCB->Sccb_tag = 0x00;
+
+ }
+ }
+
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+ {
+
+
+ if(currSCCB->Lun == 0x00)
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+ CurrCard->globalFlags |= F_NEW_SCCB_CMD;
+ }
+ }
+
+ else
+ {
+
+ if((CurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ currTar_Info->TarLUNBusy[currSCCB->Lun] = TRUE;
+ else
+ currTar_Info->TarLUNBusy[0] = TRUE;
+
+
+ currSCCB->ControlByte &= ~(UCHAR)F_USE_CMD_Q;
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+
+ }
+ }
+
+ else
+ {
+ ACCEPT_MSG(port);
+
+ while ((!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))) {}
+
+ if (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+ }
+
+ else if (message == SMEXT)
+ {
+
+ ACCEPT_MSG(port);
+ shandem(port,p_card,currSCCB);
+ }
+
+ else if (message == SMIGNORWR)
+ {
+
+ ACCEPT_MSG(port); /* ACK the RESIDUE MSG */
+
+ message = sfm(port,currSCCB);
+
+ if(currSCCB->Sccb_scsimsg != SMPARITY)
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+
+ else
+ {
+
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ currSCCB->Sccb_scsimsg = SMREJECT;
+
+ ACCEPT_MSG_ATN(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: shandem
+ *
+ * Description: Decide what to do with the extended message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void shandem(USHORT port, UCHAR p_card, PSCCB pCurrSCCB)
+#else
+void shandem(ULONG port, UCHAR p_card, PSCCB pCurrSCCB)
+#endif
+{
+ UCHAR length,message;
+
+ length = sfm(port,pCurrSCCB);
+ if (length)
+ {
+
+ ACCEPT_MSG(port);
+ message = sfm(port,pCurrSCCB);
+ if (message)
+ {
+
+ if (message == SMSYNC)
+ {
+
+ if (length == 0x03)
+ {
+
+ ACCEPT_MSG(port);
+ stsyncn(port,p_card);
+ }
+ else
+ {
+
+ pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ ACCEPT_MSG_ATN(port);
+ }
+ }
+#if defined(WIDE_SCSI)
+ else if (message == SMWDTR)
+ {
+
+ if (length == 0x02)
+ {
+
+ ACCEPT_MSG(port);
+ stwidn(port,p_card);
+ }
+ else
+ {
+
+ pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ ACCEPT_MSG_ATN(port);
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+#endif
+ else
+ {
+
+ pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ ACCEPT_MSG_ATN(port);
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+ else
+ {
+ if(pCurrSCCB->Sccb_scsimsg != SMPARITY)
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }else
+ {
+ if(pCurrSCCB->Sccb_scsimsg == SMPARITY)
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sisyncn
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ * for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sisyncn(USHORT port, UCHAR p_card, UCHAR syncFlag)
+#else
+UCHAR sisyncn(ULONG port, UCHAR p_card, UCHAR syncFlag)
+#endif
+{
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING)) {
+
+
+ WRW_HARPOON((port+ID_MSG_STRT),
+ (MPM_OP+AMSG_OUT+(currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x03 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMSYNC));
+
+
+ if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
+
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 12));
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)
+
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 25));
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)
+
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 50));
+
+ else
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 00));
+
+
+ WRW_HARPOON((port+SYNC_MSGS+8), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+10),(MPM_OP+AMSG_OUT+DEFAULT_OFFSET));
+ WRW_HARPOON((port+SYNC_MSGS+12),(BRH_OP+ALWAYS+NP ));
+
+
+ if(syncFlag == FALSE)
+ {
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_TRYING);
+ }
+ else
+ {
+ WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED + CMD_ONLY_STRT));
+ }
+
+
+ return(TRUE);
+ }
+
+ else {
+
+ currTar_Info->TarStatus |= (UCHAR)SYNC_SUPPORTED;
+ currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
+ return(FALSE);
+ }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: stsyncn
+ *
+ * Description: The has sent us a Sync Nego message so handle it as
+ * necessary.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void stsyncn(USHORT port, UCHAR p_card)
+#else
+void stsyncn(ULONG port, UCHAR p_card)
+#endif
+{
+ UCHAR sync_msg,offset,sync_reg,our_sync_msg;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ sync_msg = sfm(port,currSCCB);
+
+ if((sync_msg == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ return;
+ }
+
+ ACCEPT_MSG(port);
+
+
+ offset = sfm(port,currSCCB);
+
+ if((offset == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ return;
+ }
+
+ if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
+
+ our_sync_msg = 12; /* Setup our Message to 20mb/s */
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)
+
+ our_sync_msg = 25; /* Setup our Message to 10mb/s */
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)
+
+ our_sync_msg = 50; /* Setup our Message to 5mb/s */
+ else
+
+ our_sync_msg = 0; /* Message = Async */
+
+ if (sync_msg < our_sync_msg) {
+ sync_msg = our_sync_msg; /*if faster, then set to max. */
+ }
+
+ if (offset == ASYNC)
+ sync_msg = ASYNC;
+
+ if (offset > MAX_OFFSET)
+ offset = MAX_OFFSET;
+
+ sync_reg = 0x00;
+
+ if (sync_msg > 12)
+
+ sync_reg = 0x20; /* Use 10MB/s */
+
+ if (sync_msg > 25)
+
+ sync_reg = 0x40; /* Use 6.6MB/s */
+
+ if (sync_msg > 38)
+
+ sync_reg = 0x60; /* Use 5MB/s */
+
+ if (sync_msg > 50)
+
+ sync_reg = 0x80; /* Use 4MB/s */
+
+ if (sync_msg > 62)
+
+ sync_reg = 0xA0; /* Use 3.33MB/s */
+
+ if (sync_msg > 75)
+
+ sync_reg = 0xC0; /* Use 2.85MB/s */
+
+ if (sync_msg > 87)
+
+ sync_reg = 0xE0; /* Use 2.5MB/s */
+
+ if (sync_msg > 100) {
+
+ sync_reg = 0x00; /* Use ASYNC */
+ offset = 0x00;
+ }
+
+
+#if defined(WIDE_SCSI)
+ if (currTar_Info->TarStatus & WIDE_ENABLED)
+
+ sync_reg |= offset;
+
+ else
+
+ sync_reg |= (offset | NARROW_SCSI);
+
+#else
+ sync_reg |= (offset | NARROW_SCSI);
+#endif
+
+ sssyncv(port,currSCCB->TargID,sync_reg,currTar_Info);
+
+
+ if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
+
+
+ ACCEPT_MSG(port);
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_SUPPORTED);
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+ else {
+
+
+ ACCEPT_MSG_ATN(port);
+
+ sisyncr(port,sync_msg,offset);
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_SUPPORTED);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sisyncr
+ *
+ * Description: Answer the targets sync message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sisyncr(USHORT port,UCHAR sync_pulse, UCHAR offset)
+#else
+void sisyncr(ULONG port,UCHAR sync_pulse, UCHAR offset)
+#endif
+{
+ ARAM_ACCESS(port);
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x03 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMSYNC));
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+sync_pulse));
+ WRW_HARPOON((port+SYNC_MSGS+8), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+10),(MPM_OP+AMSG_OUT+offset));
+ WRW_HARPOON((port+SYNC_MSGS+12),(BRH_OP+ALWAYS+NP ));
+ SGRAM_ACCESS(port);
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED+CMD_ONLY_STRT));
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | AUTO_INT))) {}
+}
+
+
+
+#if defined(WIDE_SCSI)
+
+/*---------------------------------------------------------------------
+ *
+ * Function: siwidn
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ * for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR siwidn(USHORT port, UCHAR p_card)
+#else
+UCHAR siwidn(ULONG port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ if (!((currTar_Info->TarStatus & TAR_WIDE_MASK) == WIDE_NEGOCIATED)) {
+
+
+ WRW_HARPOON((port+ID_MSG_STRT),
+ (MPM_OP+AMSG_OUT+(currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x02 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMWDTR));
+ WRW_HARPOON((port+SYNC_MSGS+6), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+8), (MPM_OP+AMSG_OUT+ SM16BIT));
+ WRW_HARPOON((port+SYNC_MSGS+10),(BRH_OP+ALWAYS+NP ));
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_WIDE_MASK) | (UCHAR)WIDE_ENABLED);
+
+ return(TRUE);
+ }
+
+ else {
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_WIDE_MASK) | WIDE_NEGOCIATED);
+
+ currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
+ return(FALSE);
+ }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: stwidn
+ *
+ * Description: The has sent us a Wide Nego message so handle it as
+ * necessary.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void stwidn(USHORT port, UCHAR p_card)
+#else
+void stwidn(ULONG port, UCHAR p_card)
+#endif
+{
+ UCHAR width;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ width = sfm(port,currSCCB);
+
+ if((width == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ return;
+ }
+
+
+ if (!(currTar_Info->TarEEValue & EE_WIDE_SCSI))
+ width = 0;
+
+ if (width) {
+ currTar_Info->TarStatus |= WIDE_ENABLED;
+ width = 0;
+ }
+ else {
+ width = NARROW_SCSI;
+ currTar_Info->TarStatus &= ~WIDE_ENABLED;
+ }
+
+
+ sssyncv(port,currSCCB->TargID,width,currTar_Info);
+
+
+ if (currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+
+
+
+ currTar_Info->TarStatus |= WIDE_NEGOCIATED;
+
+ if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_SUPPORTED))
+ {
+ ACCEPT_MSG_ATN(port);
+ ARAM_ACCESS(port);
+ sisyncn(port,p_card, TRUE);
+ currSCCB->Sccb_scsistat = SELECT_SN_ST;
+ SGRAM_ACCESS(port);
+ }
+ else
+ {
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+
+ else {
+
+
+ ACCEPT_MSG_ATN(port);
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ width = SM16BIT;
+ else
+ width = SM8BIT;
+
+ siwidr(port,width);
+
+ currTar_Info->TarStatus |= (WIDE_NEGOCIATED | WIDE_ENABLED);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: siwidr
+ *
+ * Description: Answer the targets Wide nego message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void siwidr(USHORT port, UCHAR width)
+#else
+void siwidr(ULONG port, UCHAR width)
+#endif
+{
+ ARAM_ACCESS(port);
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x02 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMWDTR));
+ WRW_HARPOON((port+SYNC_MSGS+6), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+8),(MPM_OP+AMSG_OUT+width));
+ WRW_HARPOON((port+SYNC_MSGS+10),(BRH_OP+ALWAYS+NP ));
+ SGRAM_ACCESS(port);
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED+CMD_ONLY_STRT));
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | AUTO_INT))) {}
+}
+
+#endif
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sssyncv
+ *
+ * Description: Write the desired value to the Sync Register for the
+ * ID specified.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sssyncv(USHORT p_port, UCHAR p_id, UCHAR p_sync_value,PSCCBMgr_tar_info currTar_Info)
+#else
+void sssyncv(ULONG p_port, UCHAR p_id, UCHAR p_sync_value,PSCCBMgr_tar_info currTar_Info)
+#endif
+{
+ UCHAR index;
+
+ index = p_id;
+
+ switch (index) {
+
+ case 0:
+ index = 12; /* hp_synctarg_0 */
+ break;
+ case 1:
+ index = 13; /* hp_synctarg_1 */
+ break;
+ case 2:
+ index = 14; /* hp_synctarg_2 */
+ break;
+ case 3:
+ index = 15; /* hp_synctarg_3 */
+ break;
+ case 4:
+ index = 8; /* hp_synctarg_4 */
+ break;
+ case 5:
+ index = 9; /* hp_synctarg_5 */
+ break;
+ case 6:
+ index = 10; /* hp_synctarg_6 */
+ break;
+ case 7:
+ index = 11; /* hp_synctarg_7 */
+ break;
+ case 8:
+ index = 4; /* hp_synctarg_8 */
+ break;
+ case 9:
+ index = 5; /* hp_synctarg_9 */
+ break;
+ case 10:
+ index = 6; /* hp_synctarg_10 */
+ break;
+ case 11:
+ index = 7; /* hp_synctarg_11 */
+ break;
+ case 12:
+ index = 0; /* hp_synctarg_12 */
+ break;
+ case 13:
+ index = 1; /* hp_synctarg_13 */
+ break;
+ case 14:
+ index = 2; /* hp_synctarg_14 */
+ break;
+ case 15:
+ index = 3; /* hp_synctarg_15 */
+
+ }
+
+ WR_HARPOON(p_port+hp_synctarg_base+index, p_sync_value);
+
+ currTar_Info->TarSyncCtrl = p_sync_value;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sresb
+ *
+ * Description: Reset the desired card's SCSI bus.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sresb(USHORT port, UCHAR p_card)
+#else
+void sresb(ULONG port, UCHAR p_card)
+#endif
+{
+ UCHAR scsiID, i;
+
+ PSCCBMgr_tar_info currTar_Info;
+
+ WR_HARPOON(port+hp_page_ctrl,
+ (RD_HARPOON(port+hp_page_ctrl) | G_INT_DISABLE));
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+ WR_HARPOON(port+hp_scsictrl_0, SCSI_RST);
+
+ scsiID = RD_HARPOON(port+hp_seltimeout);
+ WR_HARPOON(port+hp_seltimeout,TO_5ms);
+ WRW_HARPOON((port+hp_intstat), TIMEOUT);
+
+ WR_HARPOON(port+hp_portctrl_0,(SCSI_PORT | START_TO));
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & TIMEOUT)) {}
+
+ WR_HARPOON(port+hp_seltimeout,scsiID);
+
+ WR_HARPOON(port+hp_scsictrl_0, ENA_SCAM_SEL);
+
+ Wait(port, TO_5ms);
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+ WR_HARPOON(port+hp_int_mask, (RD_HARPOON(port+hp_int_mask) | 0x00));
+
+ for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][scsiID];
+
+ if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ currTar_Info->TarSyncCtrl = 0;
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ }
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+ sssyncv(port, scsiID, NARROW_SCSI,currTar_Info);
+
+ SccbMgrTableInitTarget(p_card, scsiID);
+ }
+
+ BL_Card[p_card].scanIndex = 0x00;
+ BL_Card[p_card].currentSCCB = NULL;
+ BL_Card[p_card].globalFlags &= ~(F_TAG_STARTED | F_HOST_XFER_ACT
+ | F_NEW_SCCB_CMD);
+ BL_Card[p_card].cmdCounter = 0x00;
+ BL_Card[p_card].discQCount = 0x00;
+ BL_Card[p_card].tagQ_Lst = 0x01;
+
+ for(i = 0; i < QUEUE_DEPTH; i++)
+ BL_Card[p_card].discQ_Tbl[i] = NULL;
+
+ WR_HARPOON(port+hp_page_ctrl,
+ (RD_HARPOON(port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ssenss
+ *
+ * Description: Setup for the Auto Sense command.
+ *
+ *---------------------------------------------------------------------*/
+void ssenss(PSCCBcard pCurrCard)
+{
+ UCHAR i;
+ PSCCB currSCCB;
+
+ currSCCB = pCurrCard->currentSCCB;
+
+
+ currSCCB->Save_CdbLen = currSCCB->CdbLength;
+
+ for (i = 0; i < 6; i++) {
+
+ currSCCB->Save_Cdb[i] = currSCCB->Cdb[i];
+ }
+
+ currSCCB->CdbLength = SIX_BYTE_CMD;
+ currSCCB->Cdb[0] = SCSI_REQUEST_SENSE;
+ currSCCB->Cdb[1] = currSCCB->Cdb[1] & (UCHAR)0xE0; /*Keep LUN. */
+ currSCCB->Cdb[2] = 0x00;
+ currSCCB->Cdb[3] = 0x00;
+ currSCCB->Cdb[4] = currSCCB->RequestSenseLength;
+ currSCCB->Cdb[5] = 0x00;
+
+ currSCCB->Sccb_XferCnt = (unsigned long)currSCCB->RequestSenseLength;
+
+ currSCCB->Sccb_ATC = 0x00;
+
+ currSCCB->Sccb_XferState |= F_AUTO_SENSE;
+
+ currSCCB->Sccb_XferState &= ~F_SG_XFER;
+
+ currSCCB->Sccb_idmsg = currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV;
+
+ currSCCB->ControlByte = 0x00;
+
+ currSCCB->Sccb_MGRFlags &= F_STATUSLOADED;
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sxfrp
+ *
+ * Description: Transfer data into the bit bucket until the device
+ * decides to switch phase.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void sxfrp(USHORT p_port, UCHAR p_card)
+#else
+void sxfrp(ULONG p_port, UCHAR p_card)
+#endif
+{
+ UCHAR curr_phz;
+
+
+ DISABLE_AUTO(p_port);
+
+ if (BL_Card[p_card].globalFlags & F_HOST_XFER_ACT) {
+
+ hostDataXferAbort(p_port,p_card,BL_Card[p_card].currentSCCB);
+
+ }
+
+ /* If the Automation handled the end of the transfer then do not
+ match the phase or we will get out of sync with the ISR. */
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | XFER_CNT_0 | AUTO_INT))
+ return;
+
+ WR_HARPOON(p_port+hp_xfercnt_0, 0x00);
+
+ curr_phz = RD_HARPOON(p_port+hp_scsisig) & (UCHAR)S_SCSI_PHZ;
+
+ WRW_HARPOON((p_port+hp_intstat), XFER_CNT_0);
+
+
+ WR_HARPOON(p_port+hp_scsisig, curr_phz);
+
+ while ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)) &&
+ (curr_phz == (RD_HARPOON(p_port+hp_scsisig) & (UCHAR)S_SCSI_PHZ)) )
+ {
+ if (curr_phz & (UCHAR)SCSI_IOBIT)
+ {
+ WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | SCSI_INBIT));
+
+ if (!(RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY))
+ {
+ RD_HARPOON(p_port+hp_fifodata_0);
+ }
+ }
+ else
+ {
+ WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | HOST_WRT));
+ if (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY)
+ {
+ WR_HARPOON(p_port+hp_fifodata_0,0xFA);
+ }
+ }
+ } /* End of While loop for padding data I/O phase */
+
+ while ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+ {
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+ break;
+ }
+
+ WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | SCSI_INBIT));
+ while (!(RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY))
+ {
+ RD_HARPOON(p_port+hp_fifodata_0);
+ }
+
+ if ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+ {
+ WR_HARPOON(p_port+hp_autostart_0, (AUTO_IMMED+DISCONNECT_START));
+ while (!(RDW_HARPOON((p_port+hp_intstat)) & AUTO_INT)) {}
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (ICMD_COMP | ITAR_DISC))
+ while (!(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RSEL))) ;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: schkdd
+ *
+ * Description: Make sure data has been flushed from both FIFOs and abort
+ * the operations if necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void schkdd(USHORT port, UCHAR p_card)
+#else
+void schkdd(ULONG port, UCHAR p_card)
+#endif
+{
+ USHORT TimeOutLoop;
+ UCHAR sPhase;
+
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+
+ if ((currSCCB->Sccb_scsistat != DATA_OUT_ST) &&
+ (currSCCB->Sccb_scsistat != DATA_IN_ST)) {
+ return;
+ }
+
+
+
+ if (currSCCB->Sccb_XferState & F_ODD_BALL_CNT)
+ {
+
+ currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt-1);
+
+ currSCCB->Sccb_XferCnt = 1;
+
+ currSCCB->Sccb_XferState &= ~F_ODD_BALL_CNT;
+ WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+ WR_HARPOON(port+hp_xferstat, 0x00);
+ }
+
+ else
+ {
+
+ currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;
+
+ currSCCB->Sccb_XferCnt = 0;
+ }
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE)) {
+
+ currSCCB->HostStatus = SCCB_PARITY_ERR;
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ }
+
+
+ hostDataXferAbort(port,p_card,currSCCB);
+
+
+ while (RD_HARPOON(port+hp_scsisig) & SCSI_ACK) {}
+
+ TimeOutLoop = 0;
+
+ while(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)
+ {
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE) {
+ return;
+ }
+ if (RD_HARPOON(port+hp_offsetctr) & (UCHAR)0x1F) {
+ break;
+ }
+ if (RDW_HARPOON((port+hp_intstat)) & RESET) {
+ return;
+ }
+ if ((RD_HARPOON(port+hp_scsisig) & SCSI_REQ) || (TimeOutLoop++>0x3000) )
+ break;
+ }
+
+ sPhase = RD_HARPOON(port+hp_scsisig) & (SCSI_BSY | S_SCSI_PHZ);
+ if ((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) ||
+ (RD_HARPOON(port+hp_offsetctr) & (UCHAR)0x1F) ||
+ (sPhase == (SCSI_BSY | S_DATAO_PH)) ||
+ (sPhase == (SCSI_BSY | S_DATAI_PH)))
+ {
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ if (!(currSCCB->Sccb_XferState & F_ALL_XFERRED))
+ {
+ if (currSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+ phaseDataIn(port,p_card);
+ }
+
+ else {
+ phaseDataOut(port,p_card);
+ }
+ }
+ else
+ {
+ sxfrp(port,p_card);
+ if (!(RDW_HARPOON((port+hp_intstat)) &
+ (BUS_FREE | ICMD_COMP | ITAR_DISC | RESET)))
+ {
+ WRW_HARPOON((port+hp_intstat), AUTO_INT);
+ phaseDecode(port,p_card);
+ }
+ }
+
+ }
+
+ else {
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sinits
+ *
+ * Description: Setup SCCB manager fields in this SCCB.
+ *
+ *---------------------------------------------------------------------*/
+
+void sinits(PSCCB p_sccb, UCHAR p_card)
+{
+ PSCCBMgr_tar_info currTar_Info;
+
+ if((p_sccb->TargID > MAX_SCSI_TAR) || (p_sccb->Lun > MAX_LUN))
+ {
+ return;
+ }
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+
+ p_sccb->Sccb_XferState = 0x00;
+ p_sccb->Sccb_XferCnt = p_sccb->DataLength;
+
+ if ((p_sccb->OperationCode == SCATTER_GATHER_COMMAND) ||
+ (p_sccb->OperationCode == RESIDUAL_SG_COMMAND)) {
+
+ p_sccb->Sccb_SGoffset = 0;
+ p_sccb->Sccb_XferState = F_SG_XFER;
+ p_sccb->Sccb_XferCnt = 0x00;
+ }
+
+ if (p_sccb->DataLength == 0x00)
+
+ p_sccb->Sccb_XferState |= F_ALL_XFERRED;
+
+ if (p_sccb->ControlByte & F_USE_CMD_Q)
+ {
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
+ p_sccb->ControlByte &= ~F_USE_CMD_Q;
+
+ else
+ currTar_Info->TarStatus |= TAG_Q_TRYING;
+ }
+
+/* For !single SCSI device in system & device allow Disconnect
+ or command is tag_q type then send Cmd with Disconnect Enable
+ else send Cmd with Disconnect Disable */
+
+/*
+ if (((!(BL_Card[p_card].globalFlags & F_SINGLE_DEVICE)) &&
+ (currTar_Info->TarStatus & TAR_ALLOW_DISC)) ||
+ (currTar_Info->TarStatus & TAG_Q_TRYING)) {
+*/
+ if ((currTar_Info->TarStatus & TAR_ALLOW_DISC) ||
+ (currTar_Info->TarStatus & TAG_Q_TRYING)) {
+ p_sccb->Sccb_idmsg = (UCHAR)(SMIDENT | DISC_PRIV) | p_sccb->Lun;
+ }
+
+ else {
+
+ p_sccb->Sccb_idmsg = (UCHAR)SMIDENT | p_sccb->Lun;
+ }
+
+ p_sccb->HostStatus = 0x00;
+ p_sccb->TargetStatus = 0x00;
+ p_sccb->Sccb_tag = 0x00;
+ p_sccb->Sccb_MGRFlags = 0x00;
+ p_sccb->Sccb_sgseg = 0x00;
+ p_sccb->Sccb_ATC = 0x00;
+ p_sccb->Sccb_savedATC = 0x00;
+/*
+ p_sccb->SccbVirtDataPtr = 0x00;
+ p_sccb->Sccb_forwardlink = NULL;
+ p_sccb->Sccb_backlink = NULL;
+ */
+ p_sccb->Sccb_scsistat = BUS_FREE_ST;
+ p_sccb->SccbStatus = SCCB_IN_PROCESS;
+ p_sccb->Sccb_scsimsg = SMNO_OP;
+
+}
+
+
+#ident "$Id: phase.c 1.11 1997/01/31 02:08:49 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: phase.c $
+ *
+ * Description: Functions to initially handle the SCSI bus phase when
+ * the target asserts request (and the automation is not
+ * enabled to handle the situation).
+ *
+ * $Date: 1997/01/31 02:08:49 $
+ *
+ * $Revision: 1.11 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+
+#if defined(OS2)
+ extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+ #else
+ extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+ #endif
+#endif
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Decode
+ *
+ * Description: Determine the phase and call the appropriate function.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void phaseDecode(USHORT p_port, UCHAR p_card)
+#else
+void phaseDecode(ULONG p_port, UCHAR p_card)
+#endif
+{
+ unsigned char phase_ref;
+#if defined(OS2)
+ void (far *phase) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ void (*phase) (USHORT, UCHAR);
+ #else
+ void (*phase) (ULONG, UCHAR);
+ #endif
+#endif
+
+
+ DISABLE_AUTO(p_port);
+
+ phase_ref = (UCHAR) (RD_HARPOON(p_port+hp_scsisig) & S_SCSI_PHZ);
+
+ phase = s_PhaseTbl[phase_ref];
+
+ (*phase)(p_port, p_card); /* Call the correct phase func */
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data Out Phase
+ *
+ * Description: Start up both the BusMaster and Xbow.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseDataOut(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseDataOut(USHORT port, UCHAR p_card)
+#else
+void phaseDataOut(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ if (currSCCB == NULL)
+ {
+ return; /* Exit if No SCCB record */
+ }
+
+ currSCCB->Sccb_scsistat = DATA_OUT_ST;
+ currSCCB->Sccb_XferState &= ~(F_HOST_XFER_DIR | F_NO_DATA_YET);
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+ WR_HARPOON(port+hp_autostart_0, (END_DATA+END_DATA_START));
+
+ dataXferProcessor(port, &BL_Card[p_card]);
+
+#if defined(NOBUGBUG)
+ if (RDW_HARPOON((port+hp_intstat)) & XFER_CNT_0)
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+#endif
+
+
+ if (currSCCB->Sccb_XferCnt == 0) {
+
+
+ if ((currSCCB->ControlByte & SCCB_DATA_XFER_OUT) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE))
+ currSCCB->HostStatus = SCCB_DATA_OVER_RUN;
+
+ sxfrp(port,p_card);
+ if (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | RESET)))
+ phaseDecode(port,p_card);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data In Phase
+ *
+ * Description: Startup the BusMaster and the XBOW.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseDataIn(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseDataIn(USHORT port, UCHAR p_card)
+#else
+void phaseDataIn(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB == NULL)
+ {
+ return; /* Exit if No SCCB record */
+ }
+
+
+ currSCCB->Sccb_scsistat = DATA_IN_ST;
+ currSCCB->Sccb_XferState |= F_HOST_XFER_DIR;
+ currSCCB->Sccb_XferState &= ~F_NO_DATA_YET;
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+ WR_HARPOON(port+hp_autostart_0, (END_DATA+END_DATA_START));
+
+ dataXferProcessor(port, &BL_Card[p_card]);
+
+ if (currSCCB->Sccb_XferCnt == 0) {
+
+
+ if ((currSCCB->ControlByte & SCCB_DATA_XFER_IN) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE))
+ currSCCB->HostStatus = SCCB_DATA_OVER_RUN;
+
+ sxfrp(port,p_card);
+ if (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | RESET)))
+ phaseDecode(port,p_card);
+
+ }
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Command Phase
+ *
+ * Description: Load the CDB into the automation and start it up.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseCommand(ULONG p_port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseCommand(USHORT p_port, UCHAR p_card)
+#else
+void phaseCommand(ULONG p_port, UCHAR p_card)
+#endif
+#endif
+{
+ PSCCB currSCCB;
+#if defined(DOS)
+ USHORT cdb_reg;
+#else
+ ULONG cdb_reg;
+#endif
+ UCHAR i;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB->OperationCode == RESET_COMMAND) {
+
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ currSCCB->CdbLength = SIX_BYTE_CMD;
+ }
+
+ WR_HARPOON(p_port+hp_scsisig, 0x00);
+
+ ARAM_ACCESS(p_port);
+
+
+ cdb_reg = p_port + CMD_STRT;
+
+ for (i=0; i < currSCCB->CdbLength; i++) {
+
+ if (currSCCB->OperationCode == RESET_COMMAND)
+
+ WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + 0x00));
+
+ else
+ WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + currSCCB->Cdb[i]));
+ cdb_reg +=2;
+ }
+
+ if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
+ WRW_HARPOON(cdb_reg, (BRH_OP+ALWAYS+ NP));
+
+ WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT));
+
+ currSCCB->Sccb_scsistat = COMMAND_ST;
+
+ WR_HARPOON(p_port+hp_autostart_3, (AUTO_IMMED | CMD_ONLY_STRT));
+ SGRAM_ACCESS(p_port);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Status phase
+ *
+ * Description: Bring in the status and command complete message bytes
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseStatus(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseStatus(USHORT port, UCHAR p_card)
+#else
+void phaseStatus(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ /* Start-up the automation to finish off this command and let the
+ isr handle the interrupt for command complete when it comes in.
+ We could wait here for the interrupt to be generated?
+ */
+
+ WR_HARPOON(port+hp_scsisig, 0x00);
+
+ WR_HARPOON(port+hp_autostart_0, (AUTO_IMMED+END_DATA_START));
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Message Out
+ *
+ * Description: Send out our message (if we have one) and handle whatever
+ * else is involed.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseMsgOut(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseMsgOut(USHORT port, UCHAR p_card)
+#else
+void phaseMsgOut(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ UCHAR message,scsiID;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB != NULL) {
+
+ message = currSCCB->Sccb_scsimsg;
+ scsiID = currSCCB->TargID;
+
+ if (message == SMDEV_RESET)
+ {
+
+
+ currTar_Info = &sccbMgrTbl[p_card][scsiID];
+ currTar_Info->TarSyncCtrl = 0;
+ sssyncv(port, scsiID, NARROW_SCSI,currTar_Info);
+
+ if (sccbMgrTbl[p_card][scsiID].TarEEValue & EE_SYNC_MASK)
+ {
+
+ sccbMgrTbl[p_card][scsiID].TarStatus &= ~TAR_SYNC_MASK;
+
+ }
+
+ if (sccbMgrTbl[p_card][scsiID].TarEEValue & EE_WIDE_SCSI)
+ {
+
+ sccbMgrTbl[p_card][scsiID].TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+
+ queueFlushSccb(p_card,SCCB_COMPLETE);
+ SccbMgrTableInitTarget(p_card,scsiID);
+ }
+ else if (currSCCB->Sccb_scsistat == ABORT_ST)
+ {
+ currSCCB->HostStatus = SCCB_COMPLETE;
+ if(BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] != NULL)
+ {
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ sccbMgrTbl[p_card][scsiID].TarTagQ_Cnt--;
+ }
+
+ }
+
+ else if (currSCCB->Sccb_scsistat < COMMAND_ST)
+ {
+
+
+ if(message == SMNO_OP)
+ {
+ currSCCB->Sccb_MGRFlags |= F_DEV_SELECTED;
+
+ ssel(port,p_card);
+ return;
+ }
+ }
+ else
+ {
+
+
+ if (message == SMABORT)
+
+ queueFlushSccb(p_card,SCCB_COMPLETE);
+ }
+
+ }
+ else
+ {
+ message = SMABORT;
+ }
+
+ WRW_HARPOON((port+hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
+
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_BUS_EN);
+
+ WR_HARPOON(port+hp_scsidata_0,message);
+
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+ ACCEPT_MSG(port);
+
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+ if ((message == SMABORT) || (message == SMDEV_RESET) ||
+ (message == SMABORT_TAG) )
+ {
+
+ while(!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | PHASE))) {}
+
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+
+ if (currSCCB != NULL)
+ {
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+ queueCmdComplete(&BL_Card[p_card],currSCCB, p_card);
+ }
+
+ else
+ {
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+ }
+ }
+
+ else
+ {
+
+ sxfrp(port,p_card);
+ }
+ }
+
+ else
+ {
+
+ if(message == SMPARITY)
+ {
+ currSCCB->Sccb_scsimsg = SMNO_OP;
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ else
+ {
+ sxfrp(port,p_card);
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Message In phase
+ *
+ * Description: Bring in the message and determine what to do with it.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseMsgIn(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseMsgIn(USHORT port, UCHAR p_card)
+#else
+void phaseMsgIn(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ UCHAR message;
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (BL_Card[p_card].globalFlags & F_HOST_XFER_ACT)
+ {
+
+ phaseChkFifo(port, p_card);
+ }
+
+ message = RD_HARPOON(port+hp_scsidata_0);
+ if ((message == SMDISC) || (message == SMSAVE_DATA_PTR))
+ {
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+END_DATA_START));
+
+ }
+
+ else
+ {
+
+ message = sfm(port,currSCCB);
+ if (message)
+ {
+
+
+ sdecm(message,port,p_card);
+
+ }
+ else
+ {
+ if(currSCCB->Sccb_scsimsg != SMPARITY)
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Illegal phase
+ *
+ * Description: Target switched to some illegal phase, so all we can do
+ * is report an error back to the host (if that is possible)
+ * and send an ABORT message to the misbehaving target.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseIllegal(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseIllegal(USHORT port, UCHAR p_card)
+#else
+void phaseIllegal(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ WR_HARPOON(port+hp_scsisig, RD_HARPOON(port+hp_scsisig));
+ if (currSCCB != NULL) {
+
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ currSCCB->Sccb_scsistat = ABORT_ST;
+ currSCCB->Sccb_scsimsg = SMABORT;
+ }
+
+ ACCEPT_MSG_ATN(port);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Check FIFO
+ *
+ * Description: Make sure data has been flushed from both FIFOs and abort
+ * the operations if necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void phaseChkFifo(USHORT port, UCHAR p_card)
+#else
+void phaseChkFifo(ULONG port, UCHAR p_card)
+#endif
+{
+ ULONG xfercnt;
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB->Sccb_scsistat == DATA_IN_ST)
+ {
+
+ while((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) &&
+ (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY)) {}
+
+
+ if (!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))
+ {
+ currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;
+
+ currSCCB->Sccb_XferCnt = 0;
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE))
+ {
+ currSCCB->HostStatus = SCCB_PARITY_ERR;
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ }
+
+ hostDataXferAbort(port,p_card,currSCCB);
+
+ dataXferProcessor(port, &BL_Card[p_card]);
+
+ while((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) &&
+ (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY)) {}
+
+ }
+ } /*End Data In specific code. */
+
+
+
+#if defined(DOS)
+ asm { mov dx,port;
+ add dx,hp_xfercnt_2;
+ in al,dx;
+ dec dx;
+ xor ah,ah;
+ mov word ptr xfercnt+2,ax;
+ in al,dx;
+ dec dx;
+ mov ah,al;
+ in al,dx;
+ mov word ptr xfercnt,ax;
+ }
+#else
+ GET_XFER_CNT(port,xfercnt);
+#endif
+
+
+ WR_HARPOON(port+hp_xfercnt_0, 0x00);
+
+
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+ currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt - xfercnt);
+
+ currSCCB->Sccb_XferCnt = xfercnt;
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE)) {
+
+ currSCCB->HostStatus = SCCB_PARITY_ERR;
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ }
+
+
+ hostDataXferAbort(port,p_card,currSCCB);
+
+
+ WR_HARPOON(port+hp_fifowrite, 0x00);
+ WR_HARPOON(port+hp_fiforead, 0x00);
+ WR_HARPOON(port+hp_xferstat, 0x00);
+
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Bus Free
+ *
+ * Description: We just went bus free so figure out if it was
+ * because of command complete or from a disconnect.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void phaseBusFree(USHORT port, UCHAR p_card)
+#else
+void phaseBusFree(ULONG port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB != NULL)
+ {
+
+ DISABLE_AUTO(port);
+
+
+ if (currSCCB->OperationCode == RESET_COMMAND)
+ {
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+ queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+
+ queueSearchSelect(&BL_Card[p_card],p_card);
+
+ }
+
+ else if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
+ (UCHAR)SYNC_SUPPORTED;
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_SYNC_MASK;
+ }
+
+ else if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
+ (sccbMgrTbl[p_card][currSCCB->TargID].
+ TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_WIDE_SCSI;
+ }
+
+#if !defined(DOS)
+ else if(currSCCB->Sccb_scsistat == SELECT_Q_ST)
+ {
+ /* Make sure this is not a phony BUS_FREE. If we were
+ reselected or if BUSY is NOT on then this is a
+ valid BUS FREE. SRR Wednesday, 5/10/1995. */
+
+ if ((!(RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ||
+ (RDW_HARPOON((port+hp_intstat)) & RSEL))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_TAG_Q_MASK;
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |= TAG_Q_REJECT;
+ }
+
+ else
+ {
+ return;
+ }
+ }
+#endif
+
+ else
+ {
+
+ currSCCB->Sccb_scsistat = BUS_FREE_ST;
+
+ if (!currSCCB->HostStatus)
+ {
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ }
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+ queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+ return;
+ }
+
+
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ } /*end if !=null */
+}
+
+
+
+
+#ident "$Id: automate.c 1.14 1997/01/31 02:11:46 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: automate.c $
+ *
+ * Description: Functions relating to programming the automation of
+ * the HARPOON.
+ *
+ * $Date: 1997/01/31 02:11:46 $
+ *
+ * $Revision: 1.14 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Auto Load Default Map
+ *
+ * Description: Load the Automation RAM with the defualt map values.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void autoLoadDefaultMap(USHORT p_port)
+#else
+void autoLoadDefaultMap(ULONG p_port)
+#endif
+{
+#if defined(DOS)
+ USHORT map_addr;
+#else
+ ULONG map_addr;
+#endif
+
+ ARAM_ACCESS(p_port);
+ map_addr = p_port + hp_aramBase;
+
+ WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0xC0)); /*ID MESSAGE */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0x20)); /*SIMPLE TAG QUEUEING MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, RAT_OP); /*RESET ATTENTION */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0x00)); /*TAG ID MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 0 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 1 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 2 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 3 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 4 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 5 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 6 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 7 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 8 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 9 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 10 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 11 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPE_OP+ADATA_OUT+ DINT)); /*JUMP IF DATA OUT */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (TCB_OP+FIFO_0+ DI)); /*JUMP IF NO DATA IN FIFO */
+ map_addr +=2; /*This means AYNC DATA IN */
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_IDO_STRT)); /*STOP AND INTERRUPT */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPE_OP+ADATA_IN+DINT)); /*JUMP IF NOT DATA IN PHZ */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+ ST)); /*IF NOT MSG IN CHECK 4 DATA IN */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CRD_OP+SDATA+ 0x02)); /*SAVE DATA PTR MSG? */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+ DC)); /*GO CHECK FOR DISCONNECT MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_AR1)); /*SAVE DATA PTRS MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+ ST)); /*IF NOT MSG IN CHECK DATA IN */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CRD_OP+SDATA+ 0x04)); /*DISCONNECT MSG? */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+ UNKNWN));/*UKNKNOWN MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_BUCKET));/*XFER DISCONNECT MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_ITAR_DISC));/*STOP AND INTERRUPT */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+ASTATUS+ UNKNWN));/*JUMP IF NOT STATUS PHZ. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_AR0)); /*GET STATUS BYTE */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+ CC)); /*ERROR IF NOT MSG IN PHZ */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CRD_OP+SDATA+ 0x00)); /*CHECK FOR CMD COMPLETE MSG. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+ CC)); /*ERROR IF NOT CMD COMPLETE MSG. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_BUCKET));/*GET CMD COMPLETE MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_ICMD_COMP));/*END OF COMMAND */
+ map_addr +=2;
+
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_IUNKWN)); /*RECEIVED UNKNOWN MSG BYTE */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_INO_CC)); /*NO COMMAND COMPLETE AFTER STATUS */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_ITICKLE)); /*BIOS Tickled the Mgr */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_IRFAIL)); /*EXPECTED ID/TAG MESSAGES AND */
+ map_addr +=2; /* DIDN'T GET ONE */
+ WRW_HARPOON(map_addr, (CRR_OP+AR3+ S_IDREG)); /* comp SCSI SEL ID & AR3*/
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+EQUAL+ 0x00)); /*SEL ID OK then Conti. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_INO_CC)); /*NO COMMAND COMPLETE AFTER STATUS */
+
+
+
+ SGRAM_ACCESS(p_port);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Auto Command Complete
+ *
+ * Description: Post command back to host and find another command
+ * to execute.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void autoCmdCmplt(USHORT p_port, UCHAR p_card)
+#else
+void autoCmdCmplt(ULONG p_port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+ UCHAR status_byte;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ status_byte = RD_HARPOON(p_port+hp_gp_reg_0);
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA = FALSE;
+
+ if (status_byte != SSGOOD) {
+
+ if (status_byte == SSQ_FULL) {
+
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+
+ currSCCB->Sccb_MGRFlags |= F_STATUSLOADED;
+
+ queueSelectFail(&BL_Card[p_card],p_card);
+
+ return;
+ }
+
+ if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
+ (UCHAR)SYNC_SUPPORTED;
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_SYNC_MASK;
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+
+ }
+
+ if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
+ (sccbMgrTbl[p_card][currSCCB->TargID].
+ TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_WIDE_SCSI;
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+
+ }
+
+ if (status_byte == SSCHECK)
+ {
+ if(BL_Card[p_card].globalFlags & F_DO_RENEGO)
+ {
+ if (sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue & EE_SYNC_MASK)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_SYNC_MASK;
+ }
+ if (sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue & EE_WIDE_SCSI)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_WIDE_MASK;
+ }
+ }
+ }
+
+ if (!(currSCCB->Sccb_XferState & F_AUTO_SENSE)) {
+
+ currSCCB->SccbStatus = SCCB_ERROR;
+ currSCCB->TargetStatus = status_byte;
+
+ if (status_byte == SSCHECK) {
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA
+ = TRUE;
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+ if (currSCCB->RequestSenseLength != NO_AUTO_REQUEST_SENSE) {
+
+ if (currSCCB->RequestSenseLength == 0)
+ currSCCB->RequestSenseLength = 14;
+
+ ssenss(&BL_Card[p_card]);
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+ }
+#else
+ if ((!(currSCCB->Sccb_ucb_ptr->UCB_opcode & OPC_NO_AUTO_SENSE)) &&
+ (currSCCB->RequestSenseLength))
+ {
+ ssenss(&BL_Card[p_card]);
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+ }
+
+#endif
+ }
+ }
+ }
+
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+
+ queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+}
+#ident "$Id: busmstr.c 1.8 1997/01/31 02:10:27 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: busmstr.c $
+ *
+ * Description: Functions to start, stop, and abort BusMaster operations.
+ *
+ * $Date: 1997/01/31 02:10:27 $
+ *
+ * $Revision: 1.8 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+*/
+
+#define SHORT_WAIT 0x0000000F
+#define LONG_WAIT 0x0000FFFFL
+
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data Transfer Processor
+ *
+ * Description: This routine performs two tasks.
+ * (1) Start data transfer by calling HOST_DATA_XFER_START
+ * function. Once data transfer is started, (2) Depends
+ * on the type of data transfer mode Scatter/Gather mode
+ * or NON Scatter/Gather mode. In NON Scatter/Gather mode,
+ * this routine checks Sccb_MGRFlag (F_HOST_XFER_ACT bit) for
+ * data transfer done. In Scatter/Gather mode, this routine
+ * checks bus master command complete and dual rank busy
+ * bit to keep chaining SC transfer command. Similarly,
+ * in Scatter/Gather mode, it checks Sccb_MGRFlag
+ * (F_HOST_XFER_ACT bit) for data transfer done.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void dataXferProcessor(USHORT port, PSCCBcard pCurrCard)
+#else
+void dataXferProcessor(ULONG port, PSCCBcard pCurrCard)
+#endif
+{
+ PSCCB currSCCB;
+
+ currSCCB = pCurrCard->currentSCCB;
+
+ if (currSCCB->Sccb_XferState & F_SG_XFER)
+ {
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+
+ {
+ currSCCB->Sccb_sgseg += (UCHAR)SG_BUF_CNT;
+ currSCCB->Sccb_SGoffset = 0x00;
+ }
+ pCurrCard->globalFlags |= F_HOST_XFER_ACT;
+
+ busMstrSGDataXferStart(port, currSCCB);
+ }
+
+ else
+ {
+ if (!(pCurrCard->globalFlags & F_HOST_XFER_ACT))
+ {
+ pCurrCard->globalFlags |= F_HOST_XFER_ACT;
+
+ busMstrDataXferStart(port, currSCCB);
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Scatter Gather Data Transfer Start
+ *
+ * Description:
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void busMstrSGDataXferStart(USHORT p_port, PSCCB pcurrSCCB)
+#else
+void busMstrSGDataXferStart(ULONG p_port, PSCCB pcurrSCCB)
+#endif
+{
+ ULONG count,addr,tmpSGCnt;
+ UINT sg_index;
+ UCHAR sg_count, i;
+#if defined(DOS)
+ USHORT reg_offset;
+#else
+ ULONG reg_offset;
+#endif
+
+
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+ count = ((ULONG) HOST_RD_CMD)<<24;
+ }
+
+ else {
+ count = ((ULONG) HOST_WRT_CMD)<<24;
+ }
+
+ sg_count = 0;
+ tmpSGCnt = 0;
+ sg_index = pcurrSCCB->Sccb_sgseg;
+ reg_offset = hp_aramBase;
+
+
+ i = (UCHAR) (RD_HARPOON(p_port+hp_page_ctrl) & ~(SGRAM_ARAM|SCATTER_EN));
+
+
+ WR_HARPOON(p_port+hp_page_ctrl, i);
+
+ while ((sg_count < (UCHAR)SG_BUF_CNT) &&
+ ((ULONG)(sg_index * (UINT)SG_ELEMENT_SIZE) < pcurrSCCB->DataLength) ) {
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ tmpSGCnt += *(((ULONG far *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ count |= *(((ULONG far *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ addr = *(((ULONG far *)pcurrSCCB->DataPointer)+
+ ((sg_index * 2) + 1));
+
+#else
+ tmpSGCnt += *(((ULONG *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ count |= *(((ULONG *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ addr = *(((ULONG *)pcurrSCCB->DataPointer)+
+ ((sg_index * 2) + 1));
+#endif
+
+
+ if ((!sg_count) && (pcurrSCCB->Sccb_SGoffset)) {
+
+ addr += ((count & 0x00FFFFFFL) - pcurrSCCB->Sccb_SGoffset);
+ count = (count & 0xFF000000L) | pcurrSCCB->Sccb_SGoffset;
+
+ tmpSGCnt = count & 0x00FFFFFFL;
+ }
+
+ WR_HARP32(p_port,reg_offset,addr);
+ reg_offset +=4;
+
+ WR_HARP32(p_port,reg_offset,count);
+ reg_offset +=4;
+
+ count &= 0xFF000000L;
+ sg_index++;
+ sg_count++;
+
+ } /*End While */
+
+ pcurrSCCB->Sccb_XferCnt = tmpSGCnt;
+
+ WR_HARPOON(p_port+hp_sg_addr,(sg_count<<4));
+
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+ WR_HARP32(p_port,hp_xfercnt_0,tmpSGCnt);
+
+
+ WR_HARPOON(p_port+hp_portctrl_0,(DMA_PORT | SCSI_PORT | SCSI_INBIT));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAI_PH);
+ }
+
+ else {
+
+
+ if ((!(RD_HARPOON(p_port+hp_synctarg_0) & NARROW_SCSI)) &&
+ (tmpSGCnt & 0x000000001))
+ {
+
+ pcurrSCCB->Sccb_XferState |= F_ODD_BALL_CNT;
+ tmpSGCnt--;
+ }
+
+
+ WR_HARP32(p_port,hp_xfercnt_0,tmpSGCnt);
+
+ WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT | DMA_PORT | DMA_RD));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAO_PH);
+ }
+
+
+ WR_HARPOON(p_port+hp_page_ctrl, (UCHAR) (i | SCATTER_EN));
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Data Transfer Start
+ *
+ * Description:
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void busMstrDataXferStart(USHORT p_port, PSCCB pcurrSCCB)
+#else
+void busMstrDataXferStart(ULONG p_port, PSCCB pcurrSCCB)
+#endif
+{
+ ULONG addr,count;
+
+ if (!(pcurrSCCB->Sccb_XferState & F_AUTO_SENSE)) {
+
+ count = pcurrSCCB->Sccb_XferCnt;
+
+ addr = (ULONG) pcurrSCCB->DataPointer + pcurrSCCB->Sccb_ATC;
+ }
+
+ else {
+ addr = pcurrSCCB->SensePointer;
+ count = pcurrSCCB->RequestSenseLength;
+
+ }
+
+#if defined(DOS)
+ asm { mov dx,p_port;
+ mov ax,word ptr count;
+ add dx,hp_xfer_cnt_lo;
+ out dx,al;
+ inc dx;
+ xchg ah,al
+ out dx,al;
+ inc dx;
+ mov ax,word ptr count+2;
+ out dx,al;
+ inc dx;
+ inc dx;
+ mov ax,word ptr addr;
+ out dx,al;
+ inc dx;
+ xchg ah,al
+ out dx,al;
+ inc dx;
+ mov ax,word ptr addr+2;
+ out dx,al;
+ inc dx;
+ xchg ah,al
+ out dx,al;
+ }
+
+ WR_HARP32(p_port,hp_xfercnt_0,count);
+
+#else
+ HP_SETUP_ADDR_CNT(p_port,addr,count);
+#endif
+
+
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+ WR_HARPOON(p_port+hp_portctrl_0,(DMA_PORT | SCSI_PORT | SCSI_INBIT));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAI_PH);
+
+ WR_HARPOON(p_port+hp_xfer_cmd,
+ (XFER_DMA_HOST | XFER_HOST_AUTO | XFER_DMA_8BIT));
+ }
+
+ else {
+
+ WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT | DMA_PORT | DMA_RD));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAO_PH);
+
+ WR_HARPOON(p_port+hp_xfer_cmd,
+ (XFER_HOST_DMA | XFER_HOST_AUTO | XFER_DMA_8BIT));
+
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Timeout Handler
+ *
+ * Description: This function is called after a bus master command busy time
+ * out is detected. This routines issue halt state machine
+ * with a software time out for command busy. If command busy
+ * is still asserted at the end of the time out, it issues
+ * hard abort with another software time out. It hard abort
+ * command busy is also time out, it'll just give up.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+UCHAR busMstrTimeOut(USHORT p_port)
+#else
+UCHAR busMstrTimeOut(ULONG p_port)
+#endif
+{
+ ULONG timeout;
+
+ timeout = LONG_WAIT;
+
+ WR_HARPOON(p_port+hp_sys_ctrl, HALT_MACH);
+
+ while ((!(RD_HARPOON(p_port+hp_ext_status) & CMD_ABORTED)) && timeout--) {}
+
+
+
+ if (RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) {
+ WR_HARPOON(p_port+hp_sys_ctrl, HARD_ABORT);
+
+ timeout = LONG_WAIT;
+ while ((RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+ }
+
+ RD_HARPOON(p_port+hp_int_status); /*Clear command complete */
+
+ if (RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) {
+ return(TRUE);
+ }
+
+ else {
+ return(FALSE);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Host Data Transfer Abort
+ *
+ * Description: Abort any in progress transfer.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void hostDataXferAbort(USHORT port, UCHAR p_card, PSCCB pCurrSCCB)
+#else
+void hostDataXferAbort(ULONG port, UCHAR p_card, PSCCB pCurrSCCB)
+#endif
+{
+
+ ULONG timeout;
+ ULONG remain_cnt;
+ UINT sg_ptr;
+
+ BL_Card[p_card].globalFlags &= ~F_HOST_XFER_ACT;
+
+ if (pCurrSCCB->Sccb_XferState & F_AUTO_SENSE) {
+
+
+ if (!(RD_HARPOON(port+hp_int_status) & INT_CMD_COMPL)) {
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) | FLUSH_XFER_CNTR));
+ timeout = LONG_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) & ~FLUSH_XFER_CNTR));
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ if (busMstrTimeOut(port)) {
+
+ if (pCurrSCCB->HostStatus == 0x00)
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+
+ }
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS)
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS)
+
+ if (pCurrSCCB->HostStatus == 0x00)
+
+ {
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+ }
+ }
+
+ else if (pCurrSCCB->Sccb_XferCnt) {
+
+ if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {
+
+
+ WR_HARPOON(port+hp_page_ctrl, (RD_HARPOON(port+hp_page_ctrl) &
+ ~SCATTER_EN));
+
+ WR_HARPOON(port+hp_sg_addr,0x00);
+
+ sg_ptr = pCurrSCCB->Sccb_sgseg + SG_BUF_CNT;
+
+ if (sg_ptr > (UINT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE)) {
+
+ sg_ptr = (UINT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE);
+ }
+
+ remain_cnt = pCurrSCCB->Sccb_XferCnt;
+
+ while (remain_cnt < 0x01000000L) {
+
+ sg_ptr--;
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ if (remain_cnt > (ULONG)(*(((ULONG far *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)))) {
+
+ remain_cnt -= (ULONG)(*(((ULONG far *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)));
+ }
+
+#else
+ if (remain_cnt > (ULONG)(*(((ULONG *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)))) {
+
+ remain_cnt -= (ULONG)(*(((ULONG *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)));
+ }
+#endif
+
+ else {
+
+ break;
+ }
+ }
+
+
+
+ if (remain_cnt < 0x01000000L) {
+
+
+ pCurrSCCB->Sccb_SGoffset = remain_cnt;
+
+ pCurrSCCB->Sccb_sgseg = (USHORT)sg_ptr;
+
+
+ if ((ULONG)(sg_ptr * SG_ELEMENT_SIZE) == pCurrSCCB->DataLength
+ && (remain_cnt == 0))
+
+ pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+ }
+
+ else {
+
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_GROSS_FW_ERR;
+ }
+ }
+ }
+
+
+ if (!(pCurrSCCB->Sccb_XferState & F_HOST_XFER_DIR)) {
+
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ busMstrTimeOut(port);
+ }
+
+ else {
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+ }
+
+ }
+ }
+
+ else {
+
+
+ if ((RD_HARPOON(port+hp_fifo_cnt)) >= BM_THRESHOLD) {
+
+ timeout = SHORT_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) &&
+ ((RD_HARPOON(port+hp_fifo_cnt)) >= BM_THRESHOLD) &&
+ timeout--) {}
+ }
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) |
+ FLUSH_XFER_CNTR));
+
+ timeout = LONG_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) &&
+ timeout--) {}
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) &
+ ~FLUSH_XFER_CNTR));
+
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+ }
+
+ busMstrTimeOut(port);
+ }
+ }
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+ }
+ }
+
+ }
+
+ else {
+
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ timeout = LONG_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+ }
+
+ busMstrTimeOut(port);
+ }
+ }
+
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+
+ }
+
+ if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {
+
+ WR_HARPOON(port+hp_page_ctrl, (RD_HARPOON(port+hp_page_ctrl) &
+ ~SCATTER_EN));
+
+ WR_HARPOON(port+hp_sg_addr,0x00);
+
+ pCurrSCCB->Sccb_sgseg += SG_BUF_CNT;
+
+ pCurrSCCB->Sccb_SGoffset = 0x00;
+
+
+ if ((ULONG)(pCurrSCCB->Sccb_sgseg * SG_ELEMENT_SIZE) >=
+ pCurrSCCB->DataLength) {
+
+ pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+
+ pCurrSCCB->Sccb_sgseg = (USHORT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE);
+
+ }
+ }
+
+ else {
+
+ if (!(pCurrSCCB->Sccb_XferState & F_AUTO_SENSE))
+
+ pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+ }
+ }
+
+ WR_HARPOON(port+hp_int_mask,(INT_CMD_COMPL | SCSI_INTERRUPT));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Host Data Transfer Restart
+ *
+ * Description: Reset the available count due to a restore data
+ * pointers message.
+ *
+ *---------------------------------------------------------------------*/
+void hostDataXferRestart(PSCCB currSCCB)
+{
+ ULONG data_count;
+ UINT sg_index;
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ ULONG far *sg_ptr;
+#else
+ ULONG *sg_ptr;
+#endif
+
+ if (currSCCB->Sccb_XferState & F_SG_XFER) {
+
+ currSCCB->Sccb_XferCnt = 0;
+
+ sg_index = 0xffff; /*Index by long words into sg list. */
+ data_count = 0; /*Running count of SG xfer counts. */
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ sg_ptr = (ULONG far *)currSCCB->DataPointer;
+#else
+ sg_ptr = (ULONG *)currSCCB->DataPointer;
+#endif
+
+ while (data_count < currSCCB->Sccb_ATC) {
+
+ sg_index++;
+ data_count += *(sg_ptr+(sg_index * 2));
+ }
+
+ if (data_count == currSCCB->Sccb_ATC) {
+
+ currSCCB->Sccb_SGoffset = 0;
+ sg_index++;
+ }
+
+ else {
+ currSCCB->Sccb_SGoffset = data_count - currSCCB->Sccb_ATC;
+ }
+
+ currSCCB->Sccb_sgseg = (USHORT)sg_index;
+ }
+
+ else {
+ currSCCB->Sccb_XferCnt = currSCCB->DataLength - currSCCB->Sccb_ATC;
+ }
+}
+#ident "$Id: scam.c 1.17 1997/03/20 23:49:37 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: scam.c $
+ *
+ * Description: Functions relating to handling of the SCAM selection
+ * and the determination of the SCSI IDs to be assigned
+ * to all perspective SCSI targets.
+ *
+ * $Date: 1997/03/20 23:49:37 $
+ *
+ * $Revision: 1.17 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+
+
+/*
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#if defined(DOS) || defined(OS2)
+extern UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+extern UCHAR scamHAString[];
+*/
+/*---------------------------------------------------------------------
+ *
+ * Function: scini
+ *
+ * Description: Setup all data structures necessary for SCAM selection.
+ *
+ *---------------------------------------------------------------------*/
+
+void scini(UCHAR p_card, UCHAR p_our_id, UCHAR p_power_up)
+{
+
+#if defined(SCAM_LEV_2)
+ UCHAR loser,assigned_id;
+#endif
+#if defined(DOS)
+
+ USHORT p_port;
+#else
+ ULONG p_port;
+#endif
+
+ UCHAR i,k,ScamFlg ;
+ PSCCBcard currCard;
+ PNVRamInfo pCurrNvRam;
+
+ currCard = &BL_Card[p_card];
+ p_port = currCard->ioPort;
+ pCurrNvRam = currCard->pNvRamInfo;
+
+
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ i = pCurrNvRam->niSysConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(p_port, SCAM_CONFIG/2);
+ i = (UCHAR)(utilEERead(p_port, (SYSTEM_CONFIG/2)));
+ }
+ if(!(i & 0x02)) /* check if reset bus in AutoSCSI parameter set */
+ return;
+
+ inisci(p_card,p_port, p_our_id);
+
+ /* Force to wait 1 sec after SCSI bus reset. Some SCAM device FW
+ too slow to return to SCAM selection */
+
+ /* if (p_power_up)
+ Wait1Second(p_port);
+ else
+ Wait(p_port, TO_250ms); */
+
+ Wait1Second(p_port);
+
+#if defined(SCAM_LEV_2)
+
+ if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
+ {
+ while (!(scarb(p_port,INIT_SELTD))) {}
+
+ scsel(p_port);
+
+ do {
+ scxferc(p_port,SYNC_PTRN);
+ scxferc(p_port,DOM_MSTR);
+ loser = scsendi(p_port,&scamInfo[p_our_id].id_string[0]);
+ } while ( loser == 0xFF );
+
+ scbusf(p_port);
+
+ if ((p_power_up) && (!loser))
+ {
+ sresb(p_port,p_card);
+ Wait(p_port, TO_250ms);
+
+ while (!(scarb(p_port,INIT_SELTD))) {}
+
+ scsel(p_port);
+
+ do {
+ scxferc(p_port, SYNC_PTRN);
+ scxferc(p_port, DOM_MSTR);
+ loser = scsendi(p_port,&scamInfo[p_our_id].
+ id_string[0]);
+ } while ( loser == 0xFF );
+
+ scbusf(p_port);
+ }
+ }
+
+ else
+ {
+ loser = FALSE;
+ }
+
+
+ if (!loser)
+ {
+
+#endif /* SCAM_LEV_2 */
+
+ scamInfo[p_our_id].state = ID_ASSIGNED;
+
+
+ if (ScamFlg & SCAM_ENABLED)
+ {
+
+ for (i=0; i < MAX_SCSI_TAR; i++)
+ {
+ if ((scamInfo[i].state == ID_UNASSIGNED) ||
+ (scamInfo[i].state == ID_UNUSED))
+ {
+ if (scsell(p_port,i))
+ {
+ scamInfo[i].state = LEGACY;
+ if ((scamInfo[i].id_string[0] != 0xFF) ||
+ (scamInfo[i].id_string[1] != 0xFA))
+ {
+
+ scamInfo[i].id_string[0] = 0xFF;
+ scamInfo[i].id_string[1] = 0xFA;
+ if(pCurrNvRam == NULL)
+ currCard->globalFlags |= F_UPDATE_EEPROM;
+ }
+ }
+ }
+ }
+
+ sresb(p_port,p_card);
+ Wait1Second(p_port);
+ while (!(scarb(p_port,INIT_SELTD))) {}
+ scsel(p_port);
+ scasid(p_card, p_port);
+ }
+
+#if defined(SCAM_LEV_2)
+
+ }
+
+ else if ((loser) && (ScamFlg & SCAM_ENABLED))
+ {
+ scamInfo[p_our_id].id_string[0] = SLV_TYPE_CODE0;
+ assigned_id = FALSE;
+ scwtsel(p_port);
+
+ do {
+ while (scxferc(p_port,0x00) != SYNC_PTRN) {}
+
+ i = scxferc(p_port,0x00);
+ if (i == ASSIGN_ID)
+ {
+ if (!(scsendi(p_port,&scamInfo[p_our_id].id_string[0])))
+ {
+ i = scxferc(p_port,0x00);
+ if (scvalq(i))
+ {
+ k = scxferc(p_port,0x00);
+
+ if (scvalq(k))
+ {
+ currCard->ourId =
+ ((UCHAR)(i<<3)+(k & (UCHAR)7)) & (UCHAR) 0x3F;
+ inisci(p_card, p_port, p_our_id);
+ scamInfo[currCard->ourId].state = ID_ASSIGNED;
+ scamInfo[currCard->ourId].id_string[0]
+ = SLV_TYPE_CODE0;
+ assigned_id = TRUE;
+ }
+ }
+ }
+ }
+
+ else if (i == SET_P_FLAG)
+ {
+ if (!(scsendi(p_port,
+ &scamInfo[p_our_id].id_string[0])))
+ scamInfo[p_our_id].id_string[0] |= 0x80;
+ }
+ }while (!assigned_id);
+
+ while (scxferc(p_port,0x00) != CFG_CMPLT) {}
+ }
+
+#endif /* SCAM_LEV_2 */
+ if (ScamFlg & SCAM_ENABLED)
+ {
+ scbusf(p_port);
+ if (currCard->globalFlags & F_UPDATE_EEPROM)
+ {
+ scsavdi(p_card, p_port);
+ currCard->globalFlags &= ~F_UPDATE_EEPROM;
+ }
+ }
+
+
+#if defined(DOS)
+ for (i=0; i < MAX_SCSI_TAR; i++)
+ {
+ if (((ScamFlg & SCAM_ENABLED) && (scamInfo[i].state == LEGACY))
+ || (i != p_our_id))
+ {
+ scsellDOS(p_port,i);
+ }
+ }
+#endif
+
+/*
+ for (i=0,k=0; i < MAX_SCSI_TAR; i++)
+ {
+ if ((scamInfo[i].state == ID_ASSIGNED) ||
+ (scamInfo[i].state == LEGACY))
+ k++;
+ }
+
+ if (k==2)
+ currCard->globalFlags |= F_SINGLE_DEVICE;
+ else
+ currCard->globalFlags &= ~F_SINGLE_DEVICE;
+*/
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scarb
+ *
+ * Description: Gain control of the bus and wait SCAM select time (250ms)
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int scarb(USHORT p_port, UCHAR p_sel_type)
+#else
+int scarb(ULONG p_port, UCHAR p_sel_type)
+#endif
+{
+ if (p_sel_type == INIT_SELTD)
+ {
+
+ while (RD_HARPOON(p_port+hp_scsisig) & (SCSI_SEL | SCSI_BSY)) {}
+
+
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_SEL)
+ return(FALSE);
+
+ if (RD_HARPOON(p_port+hp_scsidata_0) != 00)
+ return(FALSE);
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_BSY));
+
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_SEL) {
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) &
+ ~SCSI_BSY));
+ return(FALSE);
+ }
+
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_SEL));
+
+ if (RD_HARPOON(p_port+hp_scsidata_0) != 00) {
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) &
+ ~(SCSI_BSY | SCSI_SEL)));
+ return(FALSE);
+ }
+ }
+
+
+ WR_HARPOON(p_port+hp_clkctrl_0, (RD_HARPOON(p_port+hp_clkctrl_0)
+ & ~ACTdeassert));
+ WR_HARPOON(p_port+hp_scsireset, SCAM_EN);
+ WR_HARPOON(p_port+hp_scsidata_0, 0x00);
+#if defined(WIDE_SCSI)
+ WR_HARPOON(p_port+hp_scsidata_1, 0x00);
+#endif
+ WR_HARPOON(p_port+hp_portctrl_0, SCSI_BUS_EN);
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_MSG));
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig)
+ & ~SCSI_BSY));
+
+ Wait(p_port,TO_250ms);
+
+ return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scbusf
+ *
+ * Description: Release the SCSI bus and disable SCAM selection.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scbusf(USHORT p_port)
+#else
+void scbusf(ULONG p_port)
+#endif
+{
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+
+ WR_HARPOON(p_port+hp_scsidata_0, 0x00);
+
+ WR_HARPOON(p_port+hp_portctrl_0, (RD_HARPOON(p_port+hp_portctrl_0)
+ & ~SCSI_BUS_EN));
+
+ WR_HARPOON(p_port+hp_scsisig, 0x00);
+
+
+ WR_HARPOON(p_port+hp_scsireset, (RD_HARPOON(p_port+hp_scsireset)
+ & ~SCAM_EN));
+
+ WR_HARPOON(p_port+hp_clkctrl_0, (RD_HARPOON(p_port+hp_clkctrl_0)
+ | ACTdeassert));
+
+#if defined(SCAM_LEV_2)
+ WRW_HARPOON((p_port+hp_intstat), (BUS_FREE | AUTO_INT | SCAM_SEL));
+#else
+ WRW_HARPOON((p_port+hp_intstat), (BUS_FREE | AUTO_INT));
+#endif
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scasid
+ *
+ * Description: Assign an ID to all the SCAM devices.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scasid(UCHAR p_card, USHORT p_port)
+#else
+void scasid(UCHAR p_card, ULONG p_port)
+#endif
+{
+#if defined(DOS) || defined(OS2)
+ /* Use external defined in global space area, instead of Stack
+ space. WIN/95 DOS doesnot work TINY mode. The OS doesnot intialize
+ SS equal to DS. Thus the array allocated on stack doesnot get
+ access correctly */
+#else
+ UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+
+ UCHAR i,k,scam_id;
+ UCHAR crcBytes[3];
+ PNVRamInfo pCurrNvRam;
+ ushort_ptr pCrcBytes;
+
+ pCurrNvRam = BL_Card[p_card].pNvRamInfo;
+
+ i=FALSE;
+
+ while (!i)
+ {
+
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ temp_id_string[k] = (UCHAR) 0x00;
+ }
+
+ scxferc(p_port,SYNC_PTRN);
+ scxferc(p_port,ASSIGN_ID);
+
+ if (!(sciso(p_port,&temp_id_string[0])))
+ {
+ if(pCurrNvRam){
+ pCrcBytes = (ushort_ptr)&crcBytes[0];
+ *pCrcBytes = CalcCrc16(&temp_id_string[0]);
+ crcBytes[2] = CalcLrc(&temp_id_string[0]);
+ temp_id_string[1] = crcBytes[2];
+ temp_id_string[2] = crcBytes[0];
+ temp_id_string[3] = crcBytes[1];
+ for(k = 4; k < ID_STRING_LENGTH; k++)
+ temp_id_string[k] = (UCHAR) 0x00;
+ }
+ i = scmachid(p_card,temp_id_string);
+
+ if (i == CLR_PRIORITY)
+ {
+ scxferc(p_port,MISC_CODE);
+ scxferc(p_port,CLR_P_FLAG);
+ i = FALSE; /*Not the last ID yet. */
+ }
+
+ else if (i != NO_ID_AVAIL)
+ {
+ if (i < 8 )
+ scxferc(p_port,ID_0_7);
+ else
+ scxferc(p_port,ID_8_F);
+
+ scam_id = (i & (UCHAR) 0x07);
+
+
+ for (k=1; k < 0x08; k <<= 1)
+ if (!( k & i ))
+ scam_id += 0x08; /*Count number of zeros in DB0-3. */
+
+ scxferc(p_port,scam_id);
+
+ i = FALSE; /*Not the last ID yet. */
+ }
+ }
+
+ else
+ {
+ i = TRUE;
+ }
+
+ } /*End while */
+
+ scxferc(p_port,SYNC_PTRN);
+ scxferc(p_port,CFG_CMPLT);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsel
+ *
+ * Description: Select all the SCAM devices.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scsel(USHORT p_port)
+#else
+void scsel(ULONG p_port)
+#endif
+{
+
+ WR_HARPOON(p_port+hp_scsisig, SCSI_SEL);
+ scwiros(p_port, SCSI_MSG);
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY));
+
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+ WR_HARPOON(p_port+hp_scsidata_0, (UCHAR)(RD_HARPOON(p_port+hp_scsidata_0) |
+ (UCHAR)(BIT(7)+BIT(6))));
+
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+ scwiros(p_port, SCSI_SEL);
+
+ WR_HARPOON(p_port+hp_scsidata_0, (UCHAR)(RD_HARPOON(p_port+hp_scsidata_0) &
+ ~(UCHAR)BIT(6)));
+ scwirod(p_port, BIT(6));
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scxferc
+ *
+ * Description: Handshake the p_data (DB4-0) across the bus.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scxferc(USHORT p_port, UCHAR p_data)
+#else
+UCHAR scxferc(ULONG p_port, UCHAR p_data)
+#endif
+{
+ UCHAR curr_data, ret_data;
+
+ curr_data = p_data | BIT(7) | BIT(5); /*Start with DB7 & DB5 asserted. */
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ curr_data &= ~BIT(7);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ scwirod(p_port,BIT(7)); /*Wait for DB7 to be released. */
+ while (!(RD_HARPOON(p_port+hp_scsidata_0) & BIT(5)));
+
+ ret_data = (RD_HARPOON(p_port+hp_scsidata_0) & (UCHAR) 0x1F);
+
+ curr_data |= BIT(6);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ curr_data &= ~BIT(5);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ scwirod(p_port,BIT(5)); /*Wait for DB5 to be released. */
+
+ curr_data &= ~(BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0)); /*Release data bits */
+ curr_data |= BIT(7);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ curr_data &= ~BIT(6);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ scwirod(p_port,BIT(6)); /*Wait for DB6 to be released. */
+
+ return(ret_data);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsendi
+ *
+ * Description: Transfer our Identification string to determine if we
+ * will be the dominant master.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scsendi(USHORT p_port, UCHAR p_id_string[])
+#else
+UCHAR scsendi(ULONG p_port, UCHAR p_id_string[])
+#endif
+{
+ UCHAR ret_data,byte_cnt,bit_cnt,defer;
+
+ defer = FALSE;
+
+ for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {
+
+ for (bit_cnt = 0x80; bit_cnt != 0 ; bit_cnt >>= 1) {
+
+ if (defer)
+ ret_data = scxferc(p_port,00);
+
+ else if (p_id_string[byte_cnt] & bit_cnt)
+
+ ret_data = scxferc(p_port,02);
+
+ else {
+
+ ret_data = scxferc(p_port,01);
+ if (ret_data & 02)
+ defer = TRUE;
+ }
+
+ if ((ret_data & 0x1C) == 0x10)
+ return(0x00); /*End of isolation stage, we won! */
+
+ if (ret_data & 0x1C)
+ return(0xFF);
+
+ if ((defer) && (!(ret_data & 0x1F)))
+ return(0x01); /*End of isolation stage, we lost. */
+
+ } /*bit loop */
+
+ } /*byte loop */
+
+ if (defer)
+ return(0x01); /*We lost */
+ else
+ return(0); /*We WON! Yeeessss! */
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sciso
+ *
+ * Description: Transfer the Identification string.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sciso(USHORT p_port, UCHAR p_id_string[])
+#else
+UCHAR sciso(ULONG p_port, UCHAR p_id_string[])
+#endif
+{
+ UCHAR ret_data,the_data,byte_cnt,bit_cnt;
+
+ the_data = 0;
+
+ for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {
+
+ for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) {
+
+ ret_data = scxferc(p_port,0);
+
+ if (ret_data & 0xFC)
+ return(0xFF);
+
+ else {
+
+ the_data <<= 1;
+ if (ret_data & BIT(1)) {
+ the_data |= 1;
+ }
+ }
+
+ if ((ret_data & 0x1F) == 0)
+ {
+/*
+ if(bit_cnt != 0 || bit_cnt != 8)
+ {
+ byte_cnt = 0;
+ bit_cnt = 0;
+ scxferc(p_port, SYNC_PTRN);
+ scxferc(p_port, ASSIGN_ID);
+ continue;
+ }
+*/
+ if (byte_cnt)
+ return(0x00);
+ else
+ return(0xFF);
+ }
+
+ } /*bit loop */
+
+ p_id_string[byte_cnt] = the_data;
+
+ } /*byte loop */
+
+ return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwirod
+ *
+ * Description: Sample the SCSI data bus making sure the signal has been
+ * deasserted for the correct number of consecutive samples.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwirod(USHORT p_port, UCHAR p_data_bit)
+#else
+void scwirod(ULONG p_port, UCHAR p_data_bit)
+#endif
+{
+ UCHAR i;
+
+ i = 0;
+ while ( i < MAX_SCSI_TAR ) {
+
+ if (RD_HARPOON(p_port+hp_scsidata_0) & p_data_bit)
+
+ i = 0;
+
+ else
+
+ i++;
+
+ }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwiros
+ *
+ * Description: Sample the SCSI Signal lines making sure the signal has been
+ * deasserted for the correct number of consecutive samples.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwiros(USHORT p_port, UCHAR p_data_bit)
+#else
+void scwiros(ULONG p_port, UCHAR p_data_bit)
+#endif
+{
+ UCHAR i;
+
+ i = 0;
+ while ( i < MAX_SCSI_TAR ) {
+
+ if (RD_HARPOON(p_port+hp_scsisig) & p_data_bit)
+
+ i = 0;
+
+ else
+
+ i++;
+
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scvalq
+ *
+ * Description: Make sure we received a valid data byte.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scvalq(UCHAR p_quintet)
+{
+ UCHAR count;
+
+ for (count=1; count < 0x08; count<<=1) {
+ if (!(p_quintet & count))
+ p_quintet -= 0x80;
+ }
+
+ if (p_quintet & 0x18)
+ return(FALSE);
+
+ else
+ return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsell
+ *
+ * Description: Select the specified device ID using a selection timeout
+ * less than 4ms. If somebody responds then it is a legacy
+ * drive and this ID must be marked as such.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scsell(USHORT p_port, UCHAR targ_id)
+#else
+UCHAR scsell(ULONG p_port, UCHAR targ_id)
+#endif
+{
+#if defined(DOS)
+ USHORT i;
+#else
+ ULONG i;
+#endif
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+ ARAM_ACCESS(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) | SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_4ms);
+
+
+ for (i = p_port+CMD_STRT; i < p_port+CMD_STRT+12; i+=2) {
+ WRW_HARPOON(i, (MPM_OP+ACOMMAND));
+ }
+ WRW_HARPOON(i, (BRH_OP+ALWAYS+ NP));
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT));
+
+ WR_HARPOON(p_port+hp_select_id, targ_id);
+
+ WR_HARPOON(p_port+hp_portctrl_0, SCSI_PORT);
+ WR_HARPOON(p_port+hp_autostart_3, (SELECT | CMD_ONLY_STRT));
+ WR_HARPOON(p_port+hp_scsictrl_0, (SEL_TAR | ENA_RESEL));
+
+
+ while (!(RDW_HARPOON((p_port+hp_intstat)) &
+ (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {}
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & RESET)
+ Wait(p_port, TO_250ms);
+
+ DISABLE_AUTO(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) & ~SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_290ms);
+
+ SGRAM_ACCESS(p_port);
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (RESET | TIMEOUT) ) {
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | PHASE));
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(FALSE); /*No legacy device */
+ }
+
+ else {
+
+ while(!(RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)) {
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+ {
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ ACCEPT_MSG(p_port);
+ }
+ }
+
+ WRW_HARPOON((p_port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(TRUE); /*Found one of them oldies! */
+ }
+}
+
+#if defined(DOS)
+/*---------------------------------------------------------------------
+ *
+ * Function: scsell for DOS
+ *
+ * Description: Select the specified device ID using a selection timeout
+ * less than 2ms. This was specially required to solve
+ * the problem with Plextor 12X CD-ROM drive. This drive
+ * was responding the Selection at the end of 4ms and
+ * hanging the system.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scsellDOS(USHORT p_port, UCHAR targ_id)
+{
+ USHORT i;
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+ ARAM_ACCESS(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) | SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_2ms);
+
+
+ for (i = p_port+CMD_STRT; i < p_port+CMD_STRT+12; i+=2) {
+ WRW_HARPOON(i, (MPM_OP+ACOMMAND));
+ }
+ WRW_HARPOON(i, (BRH_OP+ALWAYS+ NP));
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT));
+
+ WR_HARPOON(p_port+hp_select_id, targ_id);
+
+ WR_HARPOON(p_port+hp_portctrl_0, SCSI_PORT);
+ WR_HARPOON(p_port+hp_autostart_3, (SELECT | CMD_ONLY_STRT));
+ WR_HARPOON(p_port+hp_scsictrl_0, (SEL_TAR | ENA_RESEL));
+
+
+ while (!(RDW_HARPOON((p_port+hp_intstat)) &
+ (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {}
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & RESET)
+ Wait(p_port, TO_250ms);
+
+ DISABLE_AUTO(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) & ~SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_290ms);
+
+ SGRAM_ACCESS(p_port);
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (RESET | TIMEOUT) ) {
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | PHASE));
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(FALSE); /*No legacy device */
+ }
+
+ else {
+
+ while(!(RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)) {
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+ {
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ ACCEPT_MSG(p_port);
+ }
+ }
+
+ WRW_HARPOON((p_port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(TRUE); /*Found one of them oldies! */
+ }
+}
+#endif /* DOS */
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwtsel
+ *
+ * Description: Wait to be selected by another SCAM initiator.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwtsel(USHORT p_port)
+#else
+void scwtsel(ULONG p_port)
+#endif
+{
+ while(!(RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL)) {}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: inisci
+ *
+ * Description: Setup the data Structure with the info from the EEPROM.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void inisci(UCHAR p_card, USHORT p_port, UCHAR p_our_id)
+#else
+void inisci(UCHAR p_card, ULONG p_port, UCHAR p_our_id)
+#endif
+{
+ UCHAR i,k,max_id;
+ USHORT ee_data;
+ PNVRamInfo pCurrNvRam;
+
+ pCurrNvRam = BL_Card[p_card].pNvRamInfo;
+
+ if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ max_id = 0x08;
+
+ else
+ max_id = 0x10;
+
+ if(pCurrNvRam){
+ for(i = 0; i < max_id; i++){
+
+ for(k = 0; k < 4; k++)
+ scamInfo[i].id_string[k] = pCurrNvRam->niScamTbl[i][k];
+ for(k = 4; k < ID_STRING_LENGTH; k++)
+ scamInfo[i].id_string[k] = (UCHAR) 0x00;
+
+ if(scamInfo[i].id_string[0] == 0x00)
+ scamInfo[i].state = ID_UNUSED; /*Default to unused ID. */
+ else
+ scamInfo[i].state = ID_UNASSIGNED; /*Default to unassigned ID. */
+
+ }
+ }else {
+ for (i=0; i < max_id; i++)
+ {
+ for (k=0; k < ID_STRING_LENGTH; k+=2)
+ {
+ ee_data = utilEERead(p_port, (USHORT)((EE_SCAMBASE/2) +
+ (USHORT) (i*((USHORT)ID_STRING_LENGTH/2)) + (USHORT)(k/2)));
+ scamInfo[i].id_string[k] = (UCHAR) ee_data;
+ ee_data >>= 8;
+ scamInfo[i].id_string[k+1] = (UCHAR) ee_data;
+ }
+
+ if ((scamInfo[i].id_string[0] == 0x00) ||
+ (scamInfo[i].id_string[0] == 0xFF))
+
+ scamInfo[i].state = ID_UNUSED; /*Default to unused ID. */
+
+ else
+ scamInfo[i].state = ID_UNASSIGNED; /*Default to unassigned ID. */
+
+ }
+ }
+ for(k = 0; k < ID_STRING_LENGTH; k++)
+ scamInfo[p_our_id].id_string[k] = scamHAString[k];
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scmachid
+ *
+ * Description: Match the Device ID string with our values stored in
+ * the EEPROM.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scmachid(UCHAR p_card, UCHAR p_id_string[])
+{
+
+ UCHAR i,k,match;
+
+
+ for (i=0; i < MAX_SCSI_TAR; i++) {
+
+#if !defined(SCAM_LEV_2)
+ if (scamInfo[i].state == ID_UNASSIGNED)
+ {
+#endif
+ match = TRUE;
+
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ if (p_id_string[k] != scamInfo[i].id_string[k])
+ match = FALSE;
+ }
+
+ if (match)
+ {
+ scamInfo[i].state = ID_ASSIGNED;
+ return(i);
+ }
+
+#if !defined(SCAM_LEV_2)
+ }
+#endif
+
+ }
+
+
+
+ if (p_id_string[0] & BIT(5))
+ i = 8;
+ else
+ i = MAX_SCSI_TAR;
+
+ if (((p_id_string[0] & 0x06) == 0x02) || ((p_id_string[0] & 0x06) == 0x04))
+ match = p_id_string[1] & (UCHAR) 0x1F;
+ else
+ match = 7;
+
+ while (i > 0)
+ {
+ i--;
+
+ if (scamInfo[match].state == ID_UNUSED)
+ {
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ scamInfo[match].id_string[k] = p_id_string[k];
+ }
+
+ scamInfo[match].state = ID_ASSIGNED;
+
+ if(BL_Card[p_card].pNvRamInfo == NULL)
+ BL_Card[p_card].globalFlags |= F_UPDATE_EEPROM;
+ return(match);
+
+ }
+
+
+ match--;
+
+ if (match == 0xFF)
+ {
+ if (p_id_string[0] & BIT(5))
+ match = 7;
+ else
+ match = MAX_SCSI_TAR-1;
+ }
+ }
+
+
+
+ if (p_id_string[0] & BIT(7))
+ {
+ return(CLR_PRIORITY);
+ }
+
+
+ if (p_id_string[0] & BIT(5))
+ i = 8;
+ else
+ i = MAX_SCSI_TAR;
+
+ if (((p_id_string[0] & 0x06) == 0x02) || ((p_id_string[0] & 0x06) == 0x04))
+ match = p_id_string[1] & (UCHAR) 0x1F;
+ else
+ match = 7;
+
+ while (i > 0)
+ {
+
+ i--;
+
+ if (scamInfo[match].state == ID_UNASSIGNED)
+ {
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ scamInfo[match].id_string[k] = p_id_string[k];
+ }
+
+ scamInfo[match].id_string[0] |= BIT(7);
+ scamInfo[match].state = ID_ASSIGNED;
+ if(BL_Card[p_card].pNvRamInfo == NULL)
+ BL_Card[p_card].globalFlags |= F_UPDATE_EEPROM;
+ return(match);
+
+ }
+
+
+ match--;
+
+ if (match == 0xFF)
+ {
+ if (p_id_string[0] & BIT(5))
+ match = 7;
+ else
+ match = MAX_SCSI_TAR-1;
+ }
+ }
+
+ return(NO_ID_AVAIL);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsavdi
+ *
+ * Description: Save off the device SCAM ID strings.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scsavdi(UCHAR p_card, USHORT p_port)
+#else
+void scsavdi(UCHAR p_card, ULONG p_port)
+#endif
+{
+ UCHAR i,k,max_id;
+ USHORT ee_data,sum_data;
+
+
+ sum_data = 0x0000;
+
+ for (i = 1; i < EE_SCAMBASE/2; i++)
+ {
+ sum_data += utilEERead(p_port, i);
+ }
+
+
+ utilEEWriteOnOff(p_port,1); /* Enable write access to the EEPROM */
+
+ if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ max_id = 0x08;
+
+ else
+ max_id = 0x10;
+
+ for (i=0; i < max_id; i++)
+ {
+
+ for (k=0; k < ID_STRING_LENGTH; k+=2)
+ {
+ ee_data = scamInfo[i].id_string[k+1];
+ ee_data <<= 8;
+ ee_data |= scamInfo[i].id_string[k];
+ sum_data += ee_data;
+ utilEEWrite(p_port, ee_data, (USHORT)((EE_SCAMBASE/2) +
+ (USHORT)(i*((USHORT)ID_STRING_LENGTH/2)) + (USHORT)(k/2)));
+ }
+ }
+
+
+ utilEEWrite(p_port, sum_data, EEPROM_CHECK_SUM/2);
+ utilEEWriteOnOff(p_port,0); /* Turn off write access */
+}
+#ident "$Id: diagnose.c 1.10 1997/06/10 16:51:47 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: diagnose.c $
+ *
+ * Description: Diagnostic funtions for testing the integrity of
+ * the HARPOON.
+ *
+ * $Date: 1997/06/10 16:51:47 $
+ *
+ * $Revision: 1.10 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: XbowInit
+ *
+ * Description: Setup the Xbow for normal operation.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void XbowInit(USHORT port, UCHAR ScamFlg)
+#else
+void XbowInit(ULONG port, UCHAR ScamFlg)
+#endif
+{
+UCHAR i;
+
+ i = RD_HARPOON(port+hp_page_ctrl);
+ WR_HARPOON(port+hp_page_ctrl, (UCHAR) (i | G_INT_DISABLE));
+
+ WR_HARPOON(port+hp_scsireset,0x00);
+ WR_HARPOON(port+hp_portctrl_1,HOST_MODE8);
+
+ WR_HARPOON(port+hp_scsireset,(DMA_RESET | HPSCSI_RESET | PROG_RESET | \
+ FIFO_CLR));
+
+ WR_HARPOON(port+hp_scsireset,SCSI_INI);
+
+ WR_HARPOON(port+hp_clkctrl_0,CLKCTRL_DEFAULT);
+
+ WR_HARPOON(port+hp_scsisig,0x00); /* Clear any signals we might */
+ WR_HARPOON(port+hp_scsictrl_0,ENA_SCAM_SEL);
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+#if defined(SCAM_LEV_2)
+ default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
+ BUS_FREE | XFER_CNT_0 | AUTO_INT;
+
+ if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
+ default_intena |= SCAM_SEL;
+
+#else
+ default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
+ BUS_FREE | XFER_CNT_0 | AUTO_INT;
+#endif
+ WRW_HARPOON((port+hp_intena), default_intena);
+
+ WR_HARPOON(port+hp_seltimeout,TO_290ms);
+
+ /* Turn on SCSI_MODE8 for narrow cards to fix the
+ strapping issue with the DUAL CHANNEL card */
+ if (RD_HARPOON(port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ WR_HARPOON(port+hp_addstat,SCSI_MODE8);
+
+#if defined(NO_BIOS_OPTION)
+
+ WR_HARPOON(port+hp_synctarg_0,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_1,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_2,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_3,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_4,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_5,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_6,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_7,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_8,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_9,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_10,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_11,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_12,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_13,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_14,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_15,NARROW_SCSI);
+
+#endif
+ WR_HARPOON(port+hp_page_ctrl, i);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMasterInit
+ *
+ * Description: Initialize the BusMaster for normal operations.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void BusMasterInit(USHORT p_port)
+#else
+void BusMasterInit(ULONG p_port)
+#endif
+{
+
+
+ WR_HARPOON(p_port+hp_sys_ctrl, DRVR_RST);
+ WR_HARPOON(p_port+hp_sys_ctrl, 0x00);
+
+ WR_HARPOON(p_port+hp_host_blk_cnt, XFER_BLK64);
+
+
+ WR_HARPOON(p_port+hp_bm_ctrl, (BMCTRL_DEFAULT));
+
+ WR_HARPOON(p_port+hp_ee_ctrl, (SCSI_TERM_ENA_H));
+
+
+#if defined(NT)
+
+ WR_HARPOON(p_port+hp_pci_cmd_cfg, (RD_HARPOON(p_port+hp_pci_cmd_cfg)
+ & ~MEM_SPACE_ENA));
+
+#endif
+
+ RD_HARPOON(p_port+hp_int_status); /*Clear interrupts. */
+ WR_HARPOON(p_port+hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
+ WR_HARPOON(p_port+hp_page_ctrl, (RD_HARPOON(p_port+hp_page_ctrl) &
+ ~SCATTER_EN));
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagXbow
+ *
+ * Description: Test Xbow integrity. Non-zero return indicates an error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int DiagXbow(USHORT port)
+#else
+int DiagXbow(ULONG port)
+#endif
+{
+ unsigned char fifo_cnt,loop_cnt;
+
+ unsigned char fifodata[5];
+ fifodata[0] = 0x00;
+ fifodata[1] = 0xFF;
+ fifodata[2] = 0x55;
+ fifodata[3] = 0xAA;
+ fifodata[4] = 0x00;
+
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+ WRW_HARPOON((port+hp_intena), 0x0000);
+
+ WR_HARPOON(port+hp_seltimeout,TO_5ms);
+
+ WR_HARPOON(port+hp_portctrl_0,START_TO);
+
+
+ for(fifodata[4] = 0x01; fifodata[4] != (UCHAR) 0; fifodata[4] = fifodata[4] << 1) {
+
+ WR_HARPOON(port+hp_selfid_0,fifodata[4]);
+ WR_HARPOON(port+hp_selfid_1,fifodata[4]);
+
+ if ((RD_HARPOON(port+hp_selfid_0) != fifodata[4]) ||
+ (RD_HARPOON(port+hp_selfid_1) != fifodata[4]))
+ return(1);
+ }
+
+
+ for(loop_cnt = 0; loop_cnt < 4; loop_cnt++) {
+
+ WR_HARPOON(port+hp_portctrl_0,(HOST_PORT | HOST_WRT | START_TO));
+
+
+ for (fifo_cnt = 0; fifo_cnt < FIFO_LEN; fifo_cnt++) {
+
+ WR_HARPOON(port+hp_fifodata_0, fifodata[loop_cnt]);
+ }
+
+
+ if (!(RD_HARPOON(port+hp_xferstat) & FIFO_FULL))
+ return(1);
+
+
+ WR_HARPOON(port+hp_portctrl_0,(HOST_PORT | START_TO));
+
+ for (fifo_cnt = 0; fifo_cnt < FIFO_LEN; fifo_cnt++) {
+
+ if (RD_HARPOON(port+hp_fifodata_0) != fifodata[loop_cnt])
+ return(1);
+ }
+
+
+ if (!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))
+ return(1);
+ }
+
+
+ while(!(RDW_HARPOON((port+hp_intstat)) & TIMEOUT)) {}
+
+
+ WR_HARPOON(port+hp_seltimeout,TO_290ms);
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+ WRW_HARPOON((port+hp_intena), default_intena);
+
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagBusMaster
+ *
+ * Description: Test BusMaster integrity. Non-zero return indicates an
+ * error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int DiagBusMaster(USHORT port)
+#else
+int DiagBusMaster(ULONG port)
+#endif
+{
+ UCHAR testdata;
+
+ for(testdata = (UCHAR) 1; testdata != (UCHAR)0; testdata = testdata << 1) {
+
+ WR_HARPOON(port+hp_xfer_cnt_lo,testdata);
+ WR_HARPOON(port+hp_xfer_cnt_mi,testdata);
+ WR_HARPOON(port+hp_xfer_cnt_hi,testdata);
+ WR_HARPOON(port+hp_host_addr_lo,testdata);
+ WR_HARPOON(port+hp_host_addr_lmi,testdata);
+ WR_HARPOON(port+hp_host_addr_hmi,testdata);
+ WR_HARPOON(port+hp_host_addr_hi,testdata);
+
+ if ((RD_HARPOON(port+hp_xfer_cnt_lo) != testdata) ||
+ (RD_HARPOON(port+hp_xfer_cnt_mi) != testdata) ||
+ (RD_HARPOON(port+hp_xfer_cnt_hi) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_lo) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_lmi) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_hmi) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_hi) != testdata))
+
+ return(1);
+ }
+ RD_HARPOON(port+hp_int_status); /*Clear interrupts. */
+ return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagEEPROM
+ *
+ * Description: Verfiy checksum and 'Key' and initialize the EEPROM if
+ * necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void DiagEEPROM(USHORT p_port)
+#else
+void DiagEEPROM(ULONG p_port)
+#endif
+
+{
+ USHORT index,temp,max_wd_cnt;
+
+ if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ max_wd_cnt = EEPROM_WD_CNT;
+ else
+ max_wd_cnt = EEPROM_WD_CNT * 2;
+
+ temp = utilEERead(p_port, FW_SIGNATURE/2);
+
+ if (temp == 0x4641) {
+
+ for (index = 2; index < max_wd_cnt; index++) {
+
+ temp += utilEERead(p_port, index);
+
+ }
+
+ if (temp == utilEERead(p_port, EEPROM_CHECK_SUM/2)) {
+
+ return; /*EEPROM is Okay so return now! */
+ }
+ }
+
+
+ utilEEWriteOnOff(p_port,(UCHAR)1);
+
+ for (index = 0; index < max_wd_cnt; index++) {
+
+ utilEEWrite(p_port, 0x0000, index);
+ }
+
+ temp = 0;
+
+ utilEEWrite(p_port, 0x4641, FW_SIGNATURE/2);
+ temp += 0x4641;
+ utilEEWrite(p_port, 0x3920, MODEL_NUMB_0/2);
+ temp += 0x3920;
+ utilEEWrite(p_port, 0x3033, MODEL_NUMB_2/2);
+ temp += 0x3033;
+ utilEEWrite(p_port, 0x2020, MODEL_NUMB_4/2);
+ temp += 0x2020;
+ utilEEWrite(p_port, 0x70D3, SYSTEM_CONFIG/2);
+ temp += 0x70D3;
+ utilEEWrite(p_port, 0x0010, BIOS_CONFIG/2);
+ temp += 0x0010;
+ utilEEWrite(p_port, 0x0003, SCAM_CONFIG/2);
+ temp += 0x0003;
+ utilEEWrite(p_port, 0x0007, ADAPTER_SCSI_ID/2);
+ temp += 0x0007;
+
+ utilEEWrite(p_port, 0x0000, IGNORE_B_SCAN/2);
+ temp += 0x0000;
+ utilEEWrite(p_port, 0x0000, SEND_START_ENA/2);
+ temp += 0x0000;
+ utilEEWrite(p_port, 0x0000, DEVICE_ENABLE/2);
+ temp += 0x0000;
+
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL01/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL23/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL45/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL67/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL89/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLab/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLcd/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLef/2);
+ temp += 0x4242;
+
+
+ utilEEWrite(p_port, 0x6C46, 64/2); /*PRODUCT ID */
+ temp += 0x6C46;
+ utilEEWrite(p_port, 0x7361, 66/2); /* FlashPoint LT */
+ temp += 0x7361;
+ utilEEWrite(p_port, 0x5068, 68/2);
+ temp += 0x5068;
+ utilEEWrite(p_port, 0x696F, 70/2);
+ temp += 0x696F;
+ utilEEWrite(p_port, 0x746E, 72/2);
+ temp += 0x746E;
+ utilEEWrite(p_port, 0x4C20, 74/2);
+ temp += 0x4C20;
+ utilEEWrite(p_port, 0x2054, 76/2);
+ temp += 0x2054;
+ utilEEWrite(p_port, 0x2020, 78/2);
+ temp += 0x2020;
+
+ index = ((EE_SCAMBASE/2)+(7*16));
+ utilEEWrite(p_port, (0x0700+TYPE_CODE0), index);
+ temp += (0x0700+TYPE_CODE0);
+ index++;
+ utilEEWrite(p_port, 0x5542, index); /*Vendor ID code */
+ temp += 0x5542; /* BUSLOGIC */
+ index++;
+ utilEEWrite(p_port, 0x4C53, index);
+ temp += 0x4C53;
+ index++;
+ utilEEWrite(p_port, 0x474F, index);
+ temp += 0x474F;
+ index++;
+ utilEEWrite(p_port, 0x4349, index);
+ temp += 0x4349;
+ index++;
+ utilEEWrite(p_port, 0x5442, index); /*Vendor unique code */
+ temp += 0x5442; /* BT- 930 */
+ index++;
+ utilEEWrite(p_port, 0x202D, index);
+ temp += 0x202D;
+ index++;
+ utilEEWrite(p_port, 0x3339, index);
+ temp += 0x3339;
+ index++; /*Serial # */
+ utilEEWrite(p_port, 0x2030, index); /* 01234567 */
+ temp += 0x2030;
+ index++;
+ utilEEWrite(p_port, 0x5453, index);
+ temp += 0x5453;
+ index++;
+ utilEEWrite(p_port, 0x5645, index);
+ temp += 0x5645;
+ index++;
+ utilEEWrite(p_port, 0x2045, index);
+ temp += 0x2045;
+ index++;
+ utilEEWrite(p_port, 0x202F, index);
+ temp += 0x202F;
+ index++;
+ utilEEWrite(p_port, 0x4F4A, index);
+ temp += 0x4F4A;
+ index++;
+ utilEEWrite(p_port, 0x204E, index);
+ temp += 0x204E;
+ index++;
+ utilEEWrite(p_port, 0x3539, index);
+ temp += 0x3539;
+
+
+
+ utilEEWrite(p_port, temp, EEPROM_CHECK_SUM/2);
+
+ utilEEWriteOnOff(p_port,(UCHAR)0);
+
+}
+
+#ident "$Id: utility.c 1.23 1997/06/10 16:55:06 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: utility.c $
+ *
+ * Description: Utility functions relating to queueing and EEPROM
+ * manipulation and any other garbage functions.
+ *
+ * $Date: 1997/06/10 16:55:06 $
+ *
+ * $Revision: 1.23 $
+ *
+ *----------------------------------------------------------------------*/
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern unsigned int SccbGlobalFlags;
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Search Select
+ *
+ * Description: Try to find a new command to execute.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueSearchSelect(PSCCBcard pCurrCard, UCHAR p_card)
+{
+ UCHAR scan_ptr, lun;
+ PSCCBMgr_tar_info currTar_Info;
+ PSCCB pOldSccb;
+
+ scan_ptr = pCurrCard->scanIndex;
+ do
+ {
+ currTar_Info = &sccbMgrTbl[p_card][scan_ptr];
+ if((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ {
+ if (currTar_Info->TarSelQ_Cnt != 0)
+ {
+
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR)
+ scan_ptr = 0;
+
+ for(lun=0; lun < MAX_LUN; lun++)
+ {
+ if(currTar_Info->TarLUNBusy[lun] == FALSE)
+ {
+
+ pCurrCard->currentSCCB = currTar_Info->TarSelQ_Head;
+ pOldSccb = NULL;
+
+ while((pCurrCard->currentSCCB != NULL) &&
+ (lun != pCurrCard->currentSCCB->Lun))
+ {
+ pOldSccb = pCurrCard->currentSCCB;
+ pCurrCard->currentSCCB = (PSCCB)(pCurrCard->currentSCCB)->
+ Sccb_forwardlink;
+ }
+ if(pCurrCard->currentSCCB == NULL)
+ continue;
+ if(pOldSccb != NULL)
+ {
+ pOldSccb->Sccb_forwardlink = (PSCCB)(pCurrCard->currentSCCB)->
+ Sccb_forwardlink;
+ pOldSccb->Sccb_backlink = (PSCCB)(pCurrCard->currentSCCB)->
+ Sccb_backlink;
+ currTar_Info->TarSelQ_Cnt--;
+ }
+ else
+ {
+ currTar_Info->TarSelQ_Head = (PSCCB)(pCurrCard->currentSCCB)->Sccb_forwardlink;
+
+ if (currTar_Info->TarSelQ_Head == NULL)
+ {
+ currTar_Info->TarSelQ_Tail = NULL;
+ currTar_Info->TarSelQ_Cnt = 0;
+ }
+ else
+ {
+ currTar_Info->TarSelQ_Cnt--;
+ currTar_Info->TarSelQ_Head->Sccb_backlink = (PSCCB)NULL;
+ }
+ }
+ pCurrCard->scanIndex = scan_ptr;
+
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+
+ break;
+ }
+ }
+ }
+
+ else
+ {
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR) {
+ scan_ptr = 0;
+ }
+ }
+
+ }
+ else
+ {
+ if ((currTar_Info->TarSelQ_Cnt != 0) &&
+ (currTar_Info->TarLUNBusy[0] == FALSE))
+ {
+
+ pCurrCard->currentSCCB = currTar_Info->TarSelQ_Head;
+
+ currTar_Info->TarSelQ_Head = (PSCCB)(pCurrCard->currentSCCB)->Sccb_forwardlink;
+
+ if (currTar_Info->TarSelQ_Head == NULL)
+ {
+ currTar_Info->TarSelQ_Tail = NULL;
+ currTar_Info->TarSelQ_Cnt = 0;
+ }
+ else
+ {
+ currTar_Info->TarSelQ_Cnt--;
+ currTar_Info->TarSelQ_Head->Sccb_backlink = (PSCCB)NULL;
+ }
+
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR)
+ scan_ptr = 0;
+
+ pCurrCard->scanIndex = scan_ptr;
+
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+
+ break;
+ }
+
+ else
+ {
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR)
+ {
+ scan_ptr = 0;
+ }
+ }
+ }
+ } while (scan_ptr != pCurrCard->scanIndex);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Select Fail
+ *
+ * Description: Add the current SCCB to the head of the Queue.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueSelectFail(PSCCBcard pCurrCard, UCHAR p_card)
+{
+ UCHAR thisTarg;
+ PSCCBMgr_tar_info currTar_Info;
+
+ if (pCurrCard->currentSCCB != NULL)
+ {
+ thisTarg = (UCHAR)(((PSCCB)(pCurrCard->currentSCCB))->TargID);
+ currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+ pCurrCard->currentSCCB->Sccb_backlink = (PSCCB)NULL;
+
+ pCurrCard->currentSCCB->Sccb_forwardlink = currTar_Info->TarSelQ_Head;
+
+ if (currTar_Info->TarSelQ_Cnt == 0)
+ {
+ currTar_Info->TarSelQ_Tail = pCurrCard->currentSCCB;
+ }
+
+ else
+ {
+ currTar_Info->TarSelQ_Head->Sccb_backlink = pCurrCard->currentSCCB;
+ }
+
+
+ currTar_Info->TarSelQ_Head = pCurrCard->currentSCCB;
+
+ pCurrCard->currentSCCB = NULL;
+ currTar_Info->TarSelQ_Cnt++;
+ }
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Command Complete
+ *
+ * Description: Call the callback function with the current SCCB.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueCmdComplete(PSCCBcard pCurrCard, PSCCB p_sccb, UCHAR p_card)
+{
+
+#if (FW_TYPE==_UCB_MGR_)
+
+ u08bits SCSIcmd;
+ CALL_BK_FN callback;
+ PSCCBMgr_tar_info currTar_Info;
+
+ PUCB p_ucb;
+ p_ucb=p_sccb->Sccb_ucb_ptr;
+
+ SCSIcmd = p_sccb->Cdb[0];
+
+
+ if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED))
+ {
+
+ if ((p_ucb->UCB_opcode & OPC_CHK_UNDER_OVER_RUN) &&
+ (p_sccb->HostStatus == SCCB_COMPLETE) &&
+ (p_sccb->TargetStatus != SSCHECK))
+
+ if ((SCSIcmd == SCSI_READ) ||
+ (SCSIcmd == SCSI_WRITE) ||
+ (SCSIcmd == SCSI_READ_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
+ (SCSIcmd == SCSI_START_STOP_UNIT) ||
+ (pCurrCard->globalFlags & F_NO_FILTER)
+ )
+ p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
+ }
+
+ p_ucb->UCB_status=SCCB_SUCCESS;
+
+ if ((p_ucb->UCB_hbastat=p_sccb->HostStatus) || (p_ucb->UCB_scsistat=p_sccb->TargetStatus))
+ {
+ p_ucb->UCB_status=SCCB_ERROR;
+ }
+
+ if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
+ (p_sccb->OperationCode == RESIDUAL_COMMAND))
+ {
+
+ utilUpdateResidual(p_sccb);
+
+ p_ucb->UCB_datalen=p_sccb->DataLength;
+ }
+
+ pCurrCard->cmdCounter--;
+ if (!pCurrCard->cmdCounter)
+ {
+
+ if (pCurrCard->globalFlags & F_GREEN_PC)
+ {
+ WR_HARPOON(pCurrCard->ioPort+hp_clkctrl_0,(PWR_DWN | CLKCTRL_DEFAULT));
+ WR_HARPOON(pCurrCard->ioPort+hp_sys_ctrl, STOP_CLK);
+ }
+
+ WR_HARPOON(pCurrCard->ioPort+hp_semaphore,
+ (RD_HARPOON(pCurrCard->ioPort+hp_semaphore) & ~SCCB_MGR_ACTIVE));
+ }
+
+ if(pCurrCard->discQCount != 0)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = NULL;
+ }
+ else
+ {
+ if(p_sccb->Sccb_tag)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
+ }else
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ callback(p_ucb);
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+ pCurrCard->currentSCCB = NULL;
+}
+
+
+
+
+#else
+
+ UCHAR i, SCSIcmd;
+ CALL_BK_FN callback;
+ PSCCBMgr_tar_info currTar_Info;
+
+ SCSIcmd = p_sccb->Cdb[0];
+
+
+ if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED)) {
+
+ if ((p_sccb->ControlByte & (SCCB_DATA_XFER_OUT | SCCB_DATA_XFER_IN)) &&
+ (p_sccb->HostStatus == SCCB_COMPLETE) &&
+ (p_sccb->TargetStatus != SSCHECK))
+
+ if ((SCSIcmd == SCSI_READ) ||
+ (SCSIcmd == SCSI_WRITE) ||
+ (SCSIcmd == SCSI_READ_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
+ (SCSIcmd == SCSI_START_STOP_UNIT) ||
+ (pCurrCard->globalFlags & F_NO_FILTER)
+ )
+ p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
+ }
+
+
+ if(p_sccb->SccbStatus == SCCB_IN_PROCESS)
+ {
+ if (p_sccb->HostStatus || p_sccb->TargetStatus)
+ p_sccb->SccbStatus = SCCB_ERROR;
+ else
+ p_sccb->SccbStatus = SCCB_SUCCESS;
+ }
+
+ if (p_sccb->Sccb_XferState & F_AUTO_SENSE) {
+
+ p_sccb->CdbLength = p_sccb->Save_CdbLen;
+ for (i=0; i < 6; i++) {
+ p_sccb->Cdb[i] = p_sccb->Save_Cdb[i];
+ }
+ }
+
+ if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
+ (p_sccb->OperationCode == RESIDUAL_COMMAND)) {
+
+ utilUpdateResidual(p_sccb);
+ }
+
+ pCurrCard->cmdCounter--;
+ if (!pCurrCard->cmdCounter) {
+
+ if (pCurrCard->globalFlags & F_GREEN_PC) {
+ WR_HARPOON(pCurrCard->ioPort+hp_clkctrl_0,(PWR_DWN | CLKCTRL_DEFAULT));
+ WR_HARPOON(pCurrCard->ioPort+hp_sys_ctrl, STOP_CLK);
+ }
+
+ WR_HARPOON(pCurrCard->ioPort+hp_semaphore,
+ (RD_HARPOON(pCurrCard->ioPort+hp_semaphore) & ~SCCB_MGR_ACTIVE));
+
+ }
+
+ if(pCurrCard->discQCount != 0)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = NULL;
+ }
+ else
+ {
+ if(p_sccb->Sccb_tag)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
+ }else
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+
+ }
+
+ callback = (CALL_BK_FN)p_sccb->SccbCallback;
+ callback(p_sccb);
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+ pCurrCard->currentSCCB = NULL;
+}
+#endif /* ( if FW_TYPE==...) */
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Disconnect
+ *
+ * Description: Add SCCB to our disconnect array.
+ *
+ *---------------------------------------------------------------------*/
+void queueDisconnect(PSCCB p_sccb, UCHAR p_card)
+{
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ BL_Card[p_card].discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = p_sccb;
+ }
+ else
+ {
+ if (p_sccb->Sccb_tag)
+ {
+ BL_Card[p_card].discQ_Tbl[p_sccb->Sccb_tag] = p_sccb;
+ sccbMgrTbl[p_card][p_sccb->TargID].TarLUNBusy[0] = FALSE;
+ sccbMgrTbl[p_card][p_sccb->TargID].TarTagQ_Cnt++;
+ }else
+ {
+ BL_Card[p_card].discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = p_sccb;
+ }
+ }
+ BL_Card[p_card].currentSCCB = NULL;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Flush SCCB
+ *
+ * Description: Flush all SCCB's back to the host driver for this target.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueFlushSccb(UCHAR p_card, UCHAR error_code)
+{
+ UCHAR qtag,thisTarg;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ if(currSCCB != NULL)
+ {
+ thisTarg = (UCHAR)currSCCB->TargID;
+ currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+ for (qtag=0; qtag<QUEUE_DEPTH; qtag++) {
+
+ if (BL_Card[p_card].discQ_Tbl[qtag] &&
+ (BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg))
+ {
+
+ BL_Card[p_card].discQ_Tbl[qtag]->HostStatus = (UCHAR)error_code;
+
+ queueCmdComplete(&BL_Card[p_card],BL_Card[p_card].discQ_Tbl[qtag], p_card);
+
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ currTar_Info->TarTagQ_Cnt--;
+
+ }
+ }
+ }
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Flush Target SCCB
+ *
+ * Description: Flush all SCCB's back to the host driver for this target.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code)
+{
+ UCHAR qtag;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+ for (qtag=0; qtag<QUEUE_DEPTH; qtag++) {
+
+ if (BL_Card[p_card].discQ_Tbl[qtag] &&
+ (BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg))
+ {
+
+ BL_Card[p_card].discQ_Tbl[qtag]->HostStatus = (UCHAR)error_code;
+
+ queueCmdComplete(&BL_Card[p_card],BL_Card[p_card].discQ_Tbl[qtag], p_card);
+
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ currTar_Info->TarTagQ_Cnt--;
+
+ }
+ }
+
+}
+
+
+
+
+
+void queueAddSccb(PSCCB p_SCCB, UCHAR p_card)
+{
+ PSCCBMgr_tar_info currTar_Info;
+ currTar_Info = &sccbMgrTbl[p_card][p_SCCB->TargID];
+
+ p_SCCB->Sccb_forwardlink = NULL;
+
+ p_SCCB->Sccb_backlink = currTar_Info->TarSelQ_Tail;
+
+ if (currTar_Info->TarSelQ_Cnt == 0) {
+
+ currTar_Info->TarSelQ_Head = p_SCCB;
+ }
+
+ else {
+
+ currTar_Info->TarSelQ_Tail->Sccb_forwardlink = p_SCCB;
+ }
+
+
+ currTar_Info->TarSelQ_Tail = p_SCCB;
+ currTar_Info->TarSelQ_Cnt++;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Find SCCB
+ *
+ * Description: Search the target select Queue for this SCCB, and
+ * remove it if found.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR queueFindSccb(PSCCB p_SCCB, UCHAR p_card)
+{
+ PSCCB q_ptr;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][p_SCCB->TargID];
+
+ q_ptr = currTar_Info->TarSelQ_Head;
+
+ while(q_ptr != NULL) {
+
+ if (q_ptr == p_SCCB) {
+
+
+ if (currTar_Info->TarSelQ_Head == q_ptr) {
+
+ currTar_Info->TarSelQ_Head = q_ptr->Sccb_forwardlink;
+ }
+
+ if (currTar_Info->TarSelQ_Tail == q_ptr) {
+
+ currTar_Info->TarSelQ_Tail = q_ptr->Sccb_backlink;
+ }
+
+ if (q_ptr->Sccb_forwardlink != NULL) {
+ q_ptr->Sccb_forwardlink->Sccb_backlink = q_ptr->Sccb_backlink;
+ }
+
+ if (q_ptr->Sccb_backlink != NULL) {
+ q_ptr->Sccb_backlink->Sccb_forwardlink = q_ptr->Sccb_forwardlink;
+ }
+
+ currTar_Info->TarSelQ_Cnt--;
+
+ return(TRUE);
+ }
+
+ else {
+ q_ptr = q_ptr->Sccb_forwardlink;
+ }
+ }
+
+
+ return(FALSE);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Utility Update Residual Count
+ *
+ * Description: Update the XferCnt to the remaining byte count.
+ * If we transferred all the data then just write zero.
+ * If Non-SG transfer then report Total Cnt - Actual Transfer
+ * Cnt. For SG transfers add the count fields of all
+ * remaining SG elements, as well as any partial remaining
+ * element.
+ *
+ *---------------------------------------------------------------------*/
+
+void utilUpdateResidual(PSCCB p_SCCB)
+{
+ ULONG partial_cnt;
+ UINT sg_index;
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ ULONG far *sg_ptr;
+#else
+ ULONG *sg_ptr;
+#endif
+
+ if (p_SCCB->Sccb_XferState & F_ALL_XFERRED) {
+
+ p_SCCB->DataLength = 0x0000;
+ }
+
+ else if (p_SCCB->Sccb_XferState & F_SG_XFER) {
+
+ partial_cnt = 0x0000;
+
+ sg_index = p_SCCB->Sccb_sgseg;
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ sg_ptr = (ULONG far *)p_SCCB->DataPointer;
+#else
+ sg_ptr = (ULONG *)p_SCCB->DataPointer;
+#endif
+
+ if (p_SCCB->Sccb_SGoffset) {
+
+ partial_cnt = p_SCCB->Sccb_SGoffset;
+ sg_index++;
+ }
+
+ while ( ((ULONG)sg_index * (ULONG)SG_ELEMENT_SIZE) <
+ p_SCCB->DataLength ) {
+
+ partial_cnt += *(sg_ptr+(sg_index * 2));
+ sg_index++;
+ }
+
+ p_SCCB->DataLength = partial_cnt;
+ }
+
+ else {
+
+ p_SCCB->DataLength -= p_SCCB->Sccb_ATC;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Wait 1 Second
+ *
+ * Description: Wait for 1 second.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void Wait1Second(USHORT p_port)
+#else
+void Wait1Second(ULONG p_port)
+#endif
+{
+ UCHAR i;
+
+ for(i=0; i < 4; i++) {
+
+ Wait(p_port, TO_250ms);
+
+ if ((RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST))
+ break;
+
+ if((RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL))
+ break;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Wait
+ *
+ * Description: Wait the desired delay.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void Wait(USHORT p_port, UCHAR p_delay)
+#else
+void Wait(ULONG p_port, UCHAR p_delay)
+#endif
+{
+ UCHAR old_timer;
+ UCHAR green_flag;
+
+ old_timer = RD_HARPOON(p_port+hp_seltimeout);
+
+ green_flag=RD_HARPOON(p_port+hp_clkctrl_0);
+ WR_HARPOON(p_port+hp_clkctrl_0, CLKCTRL_DEFAULT);
+
+ WR_HARPOON(p_port+hp_seltimeout,p_delay);
+ WRW_HARPOON((p_port+hp_intstat), TIMEOUT);
+ WRW_HARPOON((p_port+hp_intena), (default_intena & ~TIMEOUT));
+
+
+ WR_HARPOON(p_port+hp_portctrl_0,
+ (RD_HARPOON(p_port+hp_portctrl_0) | START_TO));
+
+ while (!(RDW_HARPOON((p_port+hp_intstat)) & TIMEOUT)) {
+
+ if ((RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST))
+ break;
+
+ if ((RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL))
+ break;
+ }
+
+ WR_HARPOON(p_port+hp_portctrl_0,
+ (RD_HARPOON(p_port+hp_portctrl_0) & ~START_TO));
+
+ WRW_HARPOON((p_port+hp_intstat), TIMEOUT);
+ WRW_HARPOON((p_port+hp_intena), default_intena);
+
+ WR_HARPOON(p_port+hp_clkctrl_0,green_flag);
+
+ WR_HARPOON(p_port+hp_seltimeout,old_timer);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Enable/Disable Write to EEPROM
+ *
+ * Description: The EEPROM must first be enabled for writes
+ * A total of 9 clocks are needed.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEEWriteOnOff(USHORT p_port,UCHAR p_mode)
+#else
+void utilEEWriteOnOff(ULONG p_port,UCHAR p_mode)
+#endif
+{
+ UCHAR ee_value;
+
+ ee_value = (UCHAR)(RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H));
+
+ if (p_mode)
+
+ utilEESendCmdAddr(p_port, EWEN, EWEN_ADDR);
+
+ else
+
+
+ utilEESendCmdAddr(p_port, EWDS, EWDS_ADDR);
+
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /*Turn off CS */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /*Turn off Master Select */
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Write EEPROM
+ *
+ * Description: Write a word to the EEPROM at the specified
+ * address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr)
+#else
+void utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr)
+#endif
+{
+
+ UCHAR ee_value;
+ USHORT i;
+
+ ee_value = (UCHAR)((RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H))|
+ (SEE_MS | SEE_CS));
+
+
+
+ utilEESendCmdAddr(p_port, EE_WRITE, ee_addr);
+
+
+ ee_value |= (SEE_MS + SEE_CS);
+
+ for(i = 0x8000; i != 0; i>>=1) {
+
+ if (i & ee_data)
+ ee_value |= SEE_DO;
+ else
+ ee_value &= ~SEE_DO;
+
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ }
+ ee_value &= (EXT_ARB_ACK | SCSI_TERM_ENA_H);
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS));
+
+ Wait(p_port, TO_10ms);
+
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS | SEE_CS)); /* Set CS to EEPROM */
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /* Turn off CS */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /* Turn off Master Select */
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Read EEPROM
+ *
+ * Description: Read a word from the EEPROM at the desired
+ * address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT utilEERead(USHORT p_port, USHORT ee_addr)
+#else
+USHORT utilEERead(ULONG p_port, USHORT ee_addr)
+#endif
+{
+ USHORT i, ee_data1, ee_data2;
+
+ i = 0;
+ ee_data1 = utilEEReadOrg(p_port, ee_addr);
+ do
+ {
+ ee_data2 = utilEEReadOrg(p_port, ee_addr);
+
+ if(ee_data1 == ee_data2)
+ return(ee_data1);
+
+ ee_data1 = ee_data2;
+ i++;
+
+ }while(i < 4);
+
+ return(ee_data1);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Read EEPROM Original
+ *
+ * Description: Read a word from the EEPROM at the desired
+ * address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT utilEEReadOrg(USHORT p_port, USHORT ee_addr)
+#else
+USHORT utilEEReadOrg(ULONG p_port, USHORT ee_addr)
+#endif
+{
+
+ UCHAR ee_value;
+ USHORT i, ee_data;
+
+ ee_value = (UCHAR)((RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H))|
+ (SEE_MS | SEE_CS));
+
+
+ utilEESendCmdAddr(p_port, EE_READ, ee_addr);
+
+
+ ee_value |= (SEE_MS + SEE_CS);
+ ee_data = 0;
+
+ for(i = 1; i <= 16; i++) {
+
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+ ee_data <<= 1;
+
+ if (RD_HARPOON(p_port+hp_ee_ctrl) & SEE_DI)
+ ee_data |= 1;
+ }
+
+ ee_value &= ~(SEE_MS + SEE_CS);
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /*Turn off CS */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /*Turn off Master Select */
+
+ return(ee_data);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Send EE command and Address to the EEPROM
+ *
+ * Description: Transfers the correct command and sends the address
+ * to the eeprom.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr)
+#else
+void utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr)
+#endif
+{
+ UCHAR ee_value;
+ UCHAR narrow_flg;
+
+ USHORT i;
+
+
+ narrow_flg= (UCHAR)(RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD);
+
+
+ ee_value = SEE_MS;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+ ee_value |= SEE_CS; /* Set CS to EEPROM */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+
+ for(i = 0x04; i != 0; i>>=1) {
+
+ if (i & ee_cmd)
+ ee_value |= SEE_DO;
+ else
+ ee_value &= ~SEE_DO;
+
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ }
+
+
+ if (narrow_flg)
+ i = 0x0080;
+
+ else
+ i = 0x0200;
+
+
+ while (i != 0) {
+
+ if (i & ee_addr)
+ ee_value |= SEE_DO;
+ else
+ ee_value &= ~SEE_DO;
+
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+ i >>= 1;
+ }
+}
+
+USHORT CalcCrc16(UCHAR buffer[])
+{
+ USHORT crc=0;
+ int i,j;
+ USHORT ch;
+ for (i=0; i < ID_STRING_LENGTH; i++)
+ {
+ ch = (USHORT) buffer[i];
+ for(j=0; j < 8; j++)
+ {
+ if ((crc ^ ch) & 1)
+ crc = (crc >> 1) ^ CRCMASK;
+ else
+ crc >>= 1;
+ ch >>= 1;
+ }
+ }
+ return(crc);
+}
+
+UCHAR CalcLrc(UCHAR buffer[])
+{
+ int i;
+ UCHAR lrc;
+ lrc = 0;
+ for(i = 0; i < ID_STRING_LENGTH; i++)
+ lrc ^= buffer[i];
+ return(lrc);
+}
+
+
+
+/*
+ The following inline definitions avoid type conflicts.
+*/
+
+static inline unsigned char
+FlashPoint__ProbeHostAdapter(struct FlashPoint_Info *FlashPointInfo)
+{
+ return FlashPoint_ProbeHostAdapter((PSCCBMGR_INFO) FlashPointInfo);
+}
+
+
+static inline FlashPoint_CardHandle_T
+FlashPoint__HardwareResetHostAdapter(struct FlashPoint_Info *FlashPointInfo)
+{
+ return FlashPoint_HardwareResetHostAdapter((PSCCBMGR_INFO) FlashPointInfo);
+}
+
+static inline void
+FlashPoint__ReleaseHostAdapter(FlashPoint_CardHandle_T CardHandle)
+{
+ FlashPoint_ReleaseHostAdapter(CardHandle);
+}
+
+
+static inline void
+FlashPoint__StartCCB(FlashPoint_CardHandle_T CardHandle, struct BusLogic_CCB *CCB)
+{
+ FlashPoint_StartCCB(CardHandle, (PSCCB) CCB);
+}
+
+
+static inline void
+FlashPoint__AbortCCB(FlashPoint_CardHandle_T CardHandle, struct BusLogic_CCB *CCB)
+{
+ FlashPoint_AbortCCB(CardHandle, (PSCCB) CCB);
+}
+
+
+static inline boolean
+FlashPoint__InterruptPending(FlashPoint_CardHandle_T CardHandle)
+{
+ return FlashPoint_InterruptPending(CardHandle);
+}
+
+
+static inline int
+FlashPoint__HandleInterrupt(FlashPoint_CardHandle_T CardHandle)
+{
+ return FlashPoint_HandleInterrupt(CardHandle);
+}
+
+
+#define FlashPoint_ProbeHostAdapter FlashPoint__ProbeHostAdapter
+#define FlashPoint_HardwareResetHostAdapter FlashPoint__HardwareResetHostAdapter
+#define FlashPoint_ReleaseHostAdapter FlashPoint__ReleaseHostAdapter
+#define FlashPoint_StartCCB FlashPoint__StartCCB
+#define FlashPoint_AbortCCB FlashPoint__AbortCCB
+#define FlashPoint_InterruptPending FlashPoint__InterruptPending
+#define FlashPoint_HandleInterrupt FlashPoint__HandleInterrupt
+
+
+/*
+ FlashPoint_InquireTargetInfo returns the Synchronous Period, Synchronous
+ Offset, and Wide Transfers Active information for TargetID on CardHandle.
+*/
+
+void FlashPoint_InquireTargetInfo(FlashPoint_CardHandle_T CardHandle,
+ int TargetID,
+ unsigned char *SynchronousPeriod,
+ unsigned char *SynchronousOffset,
+ unsigned char *WideTransfersActive)
+{
+ SCCBMGR_TAR_INFO *TargetInfo =
+ &sccbMgrTbl[((SCCBCARD *)CardHandle)->cardIndex][TargetID];
+ if ((TargetInfo->TarSyncCtrl & SYNC_OFFSET) > 0)
+ {
+ *SynchronousPeriod = 5 * ((TargetInfo->TarSyncCtrl >> 5) + 1);
+ *SynchronousOffset = TargetInfo->TarSyncCtrl & SYNC_OFFSET;
+ }
+ else
+ {
+ *SynchronousPeriod = 0;
+ *SynchronousOffset = 0;
+ }
+ *WideTransfersActive = (TargetInfo->TarSyncCtrl & NARROW_SCSI ? 0 : 1);
+}
+
+
+#else /* CONFIG_SCSI_OMIT_FLASHPOINT */
+
+
+/*
+ Define prototypes for the FlashPoint SCCB Manager Functions.
+*/
+
+extern unsigned char FlashPoint_ProbeHostAdapter(struct FlashPoint_Info *);
+extern FlashPoint_CardHandle_T
+ FlashPoint_HardwareResetHostAdapter(struct FlashPoint_Info *);
+extern void FlashPoint_StartCCB(FlashPoint_CardHandle_T, struct BusLogic_CCB *);
+extern int FlashPoint_AbortCCB(FlashPoint_CardHandle_T, struct BusLogic_CCB *);
+extern boolean FlashPoint_InterruptPending(FlashPoint_CardHandle_T);
+extern int FlashPoint_HandleInterrupt(FlashPoint_CardHandle_T);
+extern void FlashPoint_ReleaseHostAdapter(FlashPoint_CardHandle_T);
+extern void FlashPoint_InquireTargetInfo(FlashPoint_CardHandle_T,
+ int, unsigned char *,
+ unsigned char *, unsigned char *);
+
+
+#endif /* CONFIG_SCSI_OMIT_FLASHPOINT */
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
new file mode 100644
index 000000000000..d22b32f4662d
--- /dev/null
+++ b/drivers/scsi/Kconfig
@@ -0,0 +1,1802 @@
+menu "SCSI device support"
+
+config SCSI
+ tristate "SCSI device support"
+ ---help---
+ If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or
+ any other SCSI device under Linux, say Y and make sure that you know
+ the name of your SCSI host adapter (the card inside your computer
+ that "speaks" the SCSI protocol, also called SCSI controller),
+ because you will be asked for it.
+
+ You also need to say Y here if you have a device which speaks
+ the SCSI protocol. Examples of this include the parallel port
+ version of the IOMEGA ZIP drive, USB storage devices, Fibre
+ Channel, FireWire storage and the IDE-SCSI emulation driver.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>.
+ The module will be called scsi_mod.
+
+ However, do not compile this as a module if your root file system
+ (the one containing the directory /) is located on a SCSI device.
+
+config SCSI_PROC_FS
+ bool "legacy /proc/scsi/ support"
+ depends on SCSI && PROC_FS
+ default y
+ ---help---
+ This option enables support for the various files in
+ /proc/scsi. In Linux 2.6 this has been superceeded by
+ files in sysfs but many legacy applications rely on this.
+
+ If unusure say Y.
+
+comment "SCSI support type (disk, tape, CD-ROM)"
+ depends on SCSI
+
+config BLK_DEV_SD
+ tristate "SCSI disk support"
+ depends on SCSI
+ ---help---
+ If you want to use SCSI hard disks, Fibre Channel disks,
+ USB storage or the SCSI or parallel port version of
+ the IOMEGA ZIP drive, say Y and read the SCSI-HOWTO,
+ the Disk-HOWTO and the Multi-Disk-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. This is NOT for SCSI
+ CD-ROMs.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>.
+ The module will be called sd_mod.
+
+ Do not compile this driver as a module if your root file system
+ (the one containing the directory /) is located on a SCSI disk.
+ In this case, do not compile the driver for your SCSI host adapter
+ (below) as a module either.
+
+config CHR_DEV_ST
+ tristate "SCSI tape support"
+ depends on SCSI
+ ---help---
+ If you want to use a SCSI tape drive under Linux, say Y and read the
+ SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, and
+ <file:Documentation/scsi/st.txt> in the kernel source. This is NOT
+ for SCSI CD-ROMs.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>. The module will be called st.
+
+config CHR_DEV_OSST
+ tristate "SCSI OnStream SC-x0 tape support"
+ depends on SCSI
+ ---help---
+ The OnStream SC-x0 SCSI tape drives can not be driven by the
+ standard st driver, but instead need this special osst driver and
+ use the /dev/osstX char device nodes (major 206). Via usb-storage
+ and ide-scsi, you may be able to drive the USB-x0 and DI-x0 drives
+ as well. Note that there is also a second generation of OnStream
+ tape drives (ADR-x0) that supports the standard SCSI-2 commands for
+ tapes (QIC-157) and can be driven by the standard driver st.
+ For more information, you may have a look at the SCSI-HOWTO
+ <http://www.tldp.org/docs.html#howto> and
+ <file:Documentation/scsi/osst.txt> in the kernel source.
+ More info on the OnStream driver may be found on
+ <http://linux1.onstream.nl/test/>
+ Please also have a look at the standard st docu, as most of it
+ applies to osst as well.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>. The module will be called osst.
+
+config BLK_DEV_SR
+ tristate "SCSI CDROM support"
+ depends on SCSI
+ ---help---
+ If you want to use a SCSI or FireWire CD-ROM under Linux,
+ say Y and read the SCSI-HOWTO and the CDROM-HOWTO at
+ <http://www.tldp.org/docs.html#howto>. Also make sure to say
+ Y or M to "ISO 9660 CD-ROM file system support" later.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>.
+ The module will be called sr_mod.
+
+config BLK_DEV_SR_VENDOR
+ bool "Enable vendor-specific extensions (for SCSI CDROM)"
+ depends on BLK_DEV_SR
+ help
+ This enables the usage of vendor specific SCSI commands. This is
+ required to support multisession CDs with old NEC/TOSHIBA cdrom
+ drives (and HP Writers). If you have such a drive and get the first
+ session only, try saying Y here; everybody else says N.
+
+config CHR_DEV_SG
+ tristate "SCSI generic support"
+ depends on SCSI
+ ---help---
+ If you want to use SCSI scanners, synthesizers or CD-writers or just
+ about anything having "SCSI" in its name other than hard disks,
+ CD-ROMs or tapes, say Y here. These won't be supported by the kernel
+ directly, so you need some additional software which knows how to
+ talk to these devices using the SCSI protocol:
+
+ For scanners, look at SANE (<http://www.mostang.com/sane/>). For CD
+ writer software look at Cdrtools
+ (<http://www.fokus.gmd.de/research/cc/glone/employees/joerg.schilling/private/cdrecord.html>)
+ and for burning a "disk at once": CDRDAO
+ (<http://cdrdao.sourceforge.net/>). Cdparanoia is a high
+ quality digital reader of audio CDs (<http://www.xiph.org/paranoia/>).
+ For other devices, it's possible that you'll have to write the
+ driver software yourself. Please read the file
+ <file:Documentation/scsi/scsi-generic.txt> for more information.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>. The module will be called sg.
+
+ If unsure, say N.
+
+comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs"
+ depends on SCSI
+
+config SCSI_MULTI_LUN
+ bool "Probe all LUNs on each SCSI device"
+ depends on SCSI
+ help
+ If you have a SCSI device that supports more than one LUN (Logical
+ Unit Number), e.g. a CD jukebox, and only one LUN is detected, you
+ can say Y here to force the SCSI driver to probe for multiple LUNs.
+ A SCSI device with multiple LUNs acts logically like multiple SCSI
+ devices. The vast majority of SCSI devices have only one LUN, and
+ so most people can say N here. The max_luns boot/module parameter
+ allows to override this setting.
+
+config SCSI_CONSTANTS
+ bool "Verbose SCSI error reporting (kernel size +=12K)"
+ depends on SCSI
+ help
+ The error messages regarding your SCSI hardware will be easier to
+ understand if you say Y here; it will enlarge your kernel by about
+ 12 KB. If in doubt, say Y.
+
+config SCSI_LOGGING
+ bool "SCSI logging facility"
+ depends on SCSI
+ ---help---
+ This turns on a logging facility that can be used to debug a number
+ of SCSI related problems.
+
+ If you say Y here, no logging output will appear by default, but you
+ can enable logging by saying Y to "/proc file system support" and
+ "Sysctl support" below and executing the command
+
+ echo "scsi log token [level]" > /proc/scsi/scsi
+
+ at boot time after the /proc file system has been mounted.
+
+ There are a number of things that can be used for 'token' (you can
+ find them in the source: <file:drivers/scsi/scsi.c>), and this
+ allows you to select the types of information you want, and the
+ level allows you to select the level of verbosity.
+
+ If you say N here, it may be harder to track down some types of SCSI
+ problems. If you say Y here your kernel will be somewhat larger, but
+ there should be no noticeable performance impact as long as you have
+ logging turned off.
+
+menu "SCSI Transport Attributes"
+ depends on SCSI
+
+config SCSI_SPI_ATTRS
+ tristate "Parallel SCSI (SPI) Transport Attributes"
+ depends on SCSI
+ help
+ If you wish to export transport-specific information about
+ each attached SCSI device to sysfs, say Y. Otherwise, say N.
+
+config SCSI_FC_ATTRS
+ tristate "FiberChannel Transport Attributes"
+ depends on SCSI
+ help
+ If you wish to export transport-specific information about
+ each attached FiberChannel device to sysfs, say Y.
+ Otherwise, say N.
+
+config SCSI_ISCSI_ATTRS
+ tristate "iSCSI Transport Attributes"
+ depends on SCSI
+ help
+ If you wish to export transport-specific information about
+ each attached iSCSI device to sysfs, say Y.
+ Otherwise, say N.
+
+endmenu
+
+menu "SCSI low-level drivers"
+ depends on SCSI!=n
+
+config SGIWD93_SCSI
+ tristate "SGI WD93C93 SCSI Driver"
+ depends on SGI_IP22 && SCSI
+ help
+ If you have a Western Digital WD93 SCSI controller on
+ an SGI MIPS system, say Y. Otherwise, say N.
+
+config SCSI_DECNCR
+ tristate "DEC NCR53C94 Scsi Driver"
+ depends on MACH_DECSTATION && SCSI && TC
+ help
+ Say Y here to support the NCR53C94 SCSI controller chips on IOASIC
+ based TURBOchannel DECstations and TURBOchannel PMAZ-A cards.
+
+config SCSI_DECSII
+ tristate "DEC SII Scsi Driver"
+ depends on MACH_DECSTATION && SCSI && MIPS32
+
+config BLK_DEV_3W_XXXX_RAID
+ tristate "3ware 5/6/7/8xxx ATA-RAID support"
+ depends on PCI && SCSI
+ help
+ 3ware is the only hardware ATA-Raid product in Linux to date.
+ This card is 2,4, or 8 channel master mode support only.
+ SCSI support required!!!
+
+ <http://www.3ware.com/>
+
+ Please read the comments at the top of
+ <file:drivers/scsi/3w-xxxx.c>.
+
+config SCSI_3W_9XXX
+ tristate "3ware 9xxx SATA-RAID support"
+ depends on PCI && SCSI
+ help
+ This driver supports the 9000 series 3ware SATA-RAID cards.
+
+ <http://www.amcc.com>
+
+ Please read the comments at the top of
+ <file:drivers/scsi/3w-9xxx.c>.
+
+config SCSI_7000FASST
+ tristate "7000FASST SCSI support"
+ depends on ISA && SCSI
+ help
+ This driver supports the Western Digital 7000 SCSI host adapter
+ family. Some information is in the source:
+ <file:drivers/scsi/wd7000.c>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wd7000.
+
+config SCSI_ACARD
+ tristate "ACARD SCSI support"
+ depends on PCI && SCSI
+ help
+ This driver supports the ACARD SCSI host adapter.
+ Support Chip <ATP870 ATP876 ATP880 ATP885>
+ To compile this driver as a module, choose M here: the
+ module will be called atp870u.
+
+config SCSI_AHA152X
+ tristate "Adaptec AHA152X/2825 support"
+ depends on ISA && SCSI && !64BIT
+ ---help---
+ This is a driver for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825
+ SCSI host adapters. It also works for the AVA-1505, but the IRQ etc.
+ must be manually specified in this case.
+
+ It is explained in section 3.3 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. You might also want to
+ read the file <file:Documentation/scsi/aha152x.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aha152x.
+
+config SCSI_AHA1542
+ tristate "Adaptec AHA1542 support"
+ depends on ISA && SCSI
+ ---help---
+ This is support for a SCSI host adapter. It is explained in section
+ 3.4 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that Trantor was
+ purchased by Adaptec, and some former Trantor products are being
+ sold under the Adaptec name. If it doesn't work out of the box, you
+ may have to change some settings in <file:drivers/scsi/aha1542.h>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aha1542.
+
+config SCSI_AHA1740
+ tristate "Adaptec AHA1740 support"
+ depends on EISA && SCSI
+ ---help---
+ This is support for a SCSI host adapter. It is explained in section
+ 3.5 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. If it doesn't work out
+ of the box, you may have to change some settings in
+ <file:drivers/scsi/aha1740.h>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aha1740.
+
+config SCSI_AACRAID
+ tristate "Adaptec AACRAID support"
+ depends on SCSI && PCI
+
+source "drivers/scsi/aic7xxx/Kconfig.aic7xxx"
+
+config SCSI_AIC7XXX_OLD
+ tristate "Adaptec AIC7xxx support (old driver)"
+ depends on (ISA || EISA || PCI ) && SCSI
+ help
+ WARNING This driver is an older aic7xxx driver and is no longer
+ under active development. Adaptec, Inc. is writing a new driver to
+ take the place of this one, and it is recommended that whenever
+ possible, people should use the new Adaptec written driver instead
+ of this one. This driver will eventually be phased out entirely.
+
+ This is support for the various aic7xxx based Adaptec SCSI
+ controllers. These include the 274x EISA cards; 284x VLB cards;
+ 2902, 2910, 293x, 294x, 394x, 3985 and several other PCI and
+ motherboard based SCSI controllers from Adaptec. It does not support
+ the AAA-13x RAID controllers from Adaptec, nor will it likely ever
+ support them. It does not support the 2920 cards from Adaptec that
+ use the Future Domain SCSI controller chip. For those cards, you
+ need the "Future Domain 16xx SCSI support" driver.
+
+ In general, if the controller is based on an Adaptec SCSI controller
+ chip from the aic777x series or the aic78xx series, this driver
+ should work. The only exception is the 7810 which is specifically
+ not supported (that's the RAID controller chip on the AAA-13x
+ cards).
+
+ Note that the AHA2920 SCSI host adapter is *not* supported by this
+ driver; choose "Future Domain 16xx SCSI support" instead if you have
+ one of those.
+
+ Information on the configuration options for this controller can be
+ found by checking the help file for each of the available
+ configuration options. You should read
+ <file:Documentation/scsi/aic7xxx_old.txt> at a minimum before
+ contacting the maintainer with any questions. The SCSI-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>, can also
+ be of great help.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aic7xxx_old.
+
+source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
+
+# All the I2O code and drivers do not seem to be 64bit safe.
+config SCSI_DPT_I2O
+ tristate "Adaptec I2O RAID support "
+ depends on !64BIT && SCSI && PCI
+ help
+ This driver supports all of Adaptec's I2O based RAID controllers as
+ well as the DPT SmartRaid V cards. This is an Adaptec maintained
+ driver by Deanna Bonds. See <file:Documentation/scsi/dpti.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dpt_i2o.
+
+config SCSI_ADVANSYS
+ tristate "AdvanSys SCSI support"
+ depends on (ISA || EISA || PCI) && SCSI && BROKEN
+ help
+ This is a driver for all SCSI host adapters manufactured by
+ AdvanSys. It is documented in the kernel source in
+ <file:drivers/scsi/advansys.c>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called advansys.
+
+config SCSI_IN2000
+ tristate "Always IN2000 SCSI support"
+ depends on ISA && SCSI
+ help
+ This is support for an ISA bus SCSI host adapter. You'll find more
+ information in <file:Documentation/scsi/in2000.txt>. If it doesn't work
+ out of the box, you may have to change the jumpers for IRQ or
+ address selection.
+
+ To compile this driver as a module, choose M here: the
+ module will be called in2000.
+
+source "drivers/scsi/megaraid/Kconfig.megaraid"
+
+config SCSI_SATA
+ bool "Serial ATA (SATA) support"
+ depends on SCSI
+ help
+ This driver family supports Serial ATA host controllers
+ and devices.
+
+ If unsure, say N.
+
+config SCSI_SATA_AHCI
+ tristate "AHCI SATA support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for AHCI Serial ATA.
+
+ If unsure, say N.
+
+config SCSI_SATA_SVW
+ tristate "ServerWorks Frodo / Apple K2 SATA support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for Broadcom/Serverworks/Apple K2
+ SATA support.
+
+ If unsure, say N.
+
+config SCSI_ATA_PIIX
+ tristate "Intel PIIX/ICH SATA support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for ICH5 Serial ATA.
+ If PATA support was enabled previously, this enables
+ support for select Intel PIIX/ICH PATA host controllers.
+
+ If unsure, say N.
+
+config SCSI_SATA_NV
+ tristate "NVIDIA SATA support"
+ depends on SCSI_SATA && PCI && EXPERIMENTAL
+ help
+ This option enables support for NVIDIA Serial ATA.
+
+ If unsure, say N.
+
+config SCSI_SATA_PROMISE
+ tristate "Promise SATA TX2/TX4 support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for Promise Serial ATA TX2/TX4.
+
+ If unsure, say N.
+
+config SCSI_SATA_QSTOR
+ tristate "Pacific Digital SATA QStor support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for Pacific Digital Serial ATA QStor.
+
+ If unsure, say N.
+
+config SCSI_SATA_SX4
+ tristate "Promise SATA SX4 support"
+ depends on SCSI_SATA && PCI && EXPERIMENTAL
+ help
+ This option enables support for Promise Serial ATA SX4.
+
+ If unsure, say N.
+
+config SCSI_SATA_SIL
+ tristate "Silicon Image SATA support"
+ depends on SCSI_SATA && PCI && EXPERIMENTAL
+ help
+ This option enables support for Silicon Image Serial ATA.
+
+ If unsure, say N.
+
+config SCSI_SATA_SIS
+ tristate "SiS 964/180 SATA support"
+ depends on SCSI_SATA && PCI && EXPERIMENTAL
+ help
+ This option enables support for SiS Serial ATA 964/180.
+
+ If unsure, say N.
+
+config SCSI_SATA_ULI
+ tristate "ULi Electronics SATA support"
+ depends on SCSI_SATA && PCI && EXPERIMENTAL
+ help
+ This option enables support for ULi Electronics SATA.
+
+ If unsure, say N.
+
+config SCSI_SATA_VIA
+ tristate "VIA SATA support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for VIA Serial ATA.
+
+ If unsure, say N.
+
+config SCSI_SATA_VITESSE
+ tristate "VITESSE VSC-7174 SATA support"
+ depends on SCSI_SATA && PCI
+ help
+ This option enables support for Vitesse VSC7174 Serial ATA.
+
+ If unsure, say N.
+
+config SCSI_BUSLOGIC
+ tristate "BusLogic SCSI support"
+ depends on (PCI || ISA || MCA) && SCSI && (BROKEN || !SPARC64)
+ ---help---
+ This is support for BusLogic MultiMaster and FlashPoint SCSI Host
+ Adapters. Consult the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, and the files
+ <file:Documentation/scsi/BusLogic.txt> and
+ <file:Documentation/scsi/FlashPoint.txt> for more information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called BusLogic.
+
+config SCSI_OMIT_FLASHPOINT
+ bool "Omit FlashPoint support"
+ depends on SCSI_BUSLOGIC
+ help
+ This option allows you to omit the FlashPoint support from the
+ BusLogic SCSI driver. The FlashPoint SCCB Manager code is
+ substantial, so users of MultiMaster Host Adapters may wish to omit
+ it.
+
+#
+# This is marked broken because it uses over 4kB of stack in
+# just two routines:
+# 2076 CpqTsProcessIMQEntry
+# 2052 PeekIMQEntry
+#
+config SCSI_CPQFCTS
+ tristate "Compaq Fibre Channel 64-bit/66Mhz HBA support"
+ depends on PCI && SCSI && BROKEN
+ help
+ Say Y here to compile in support for the Compaq StorageWorks Fibre
+ Channel 64-bit/66Mhz Host Bus Adapter.
+
+config SCSI_DMX3191D
+ tristate "DMX3191D SCSI support"
+ depends on PCI && SCSI
+ help
+ This is support for Domex DMX3191D SCSI Host Adapters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dmx3191d.
+
+config SCSI_DTC3280
+ tristate "DTC3180/3280 SCSI support"
+ depends on ISA && SCSI
+ help
+ This is support for DTC 3180/3280 SCSI Host Adapters. Please read
+ the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, and the file
+ <file:Documentation/scsi/dtc3x80.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dtc.
+
+config SCSI_EATA
+ tristate "EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support"
+ depends on (ISA || EISA || PCI) && SCSI && (BROKEN || !SPARC64)
+ ---help---
+ This driver supports all EATA/DMA-compliant SCSI host adapters. DPT
+ ISA and all EISA I/O addresses are probed looking for the "EATA"
+ signature. The addresses of all the PCI SCSI controllers reported
+ by the PCI subsystem are probed as well.
+
+ You want to read the start of <file:drivers/scsi/eata.c> and the
+ SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called eata.
+
+config SCSI_EATA_TAGGED_QUEUE
+ bool "enable tagged command queueing"
+ depends on SCSI_EATA
+ help
+ This is a feature of SCSI-2 which improves performance: the host
+ adapter can send several SCSI commands to a device's queue even if
+ previous commands haven't finished yet.
+ This is equivalent to the "eata=tc:y" boot option.
+
+config SCSI_EATA_LINKED_COMMANDS
+ bool "enable elevator sorting"
+ depends on SCSI_EATA
+ help
+ This option enables elevator sorting for all probed SCSI disks and
+ CD-ROMs. It definitely reduces the average seek distance when doing
+ random seeks, but this does not necessarily result in a noticeable
+ performance improvement: your mileage may vary...
+ This is equivalent to the "eata=lc:y" boot option.
+
+config SCSI_EATA_MAX_TAGS
+ int "maximum number of queued commands"
+ depends on SCSI_EATA
+ default "16"
+ help
+ This specifies how many SCSI commands can be maximally queued for
+ each probed SCSI device. You should reduce the default value of 16
+ only if you have disks with buggy or limited tagged command support.
+ Minimum is 2 and maximum is 62. This value is also the window size
+ used by the elevator sorting option above. The effective value used
+ by the driver for each probed SCSI device is reported at boot time.
+ This is equivalent to the "eata=mq:8" boot option.
+
+config SCSI_EATA_PIO
+ tristate "EATA-PIO (old DPT PM2001, PM2012A) support"
+ depends on (ISA || EISA || PCI) && SCSI && BROKEN
+ ---help---
+ This driver supports all EATA-PIO protocol compliant SCSI Host
+ Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant
+ host adapters could also use this driver but are discouraged from
+ doing so, since this driver only supports hard disks and lacks
+ numerous features. You might want to have a look at the SCSI-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called eata_pio.
+
+config SCSI_FUTURE_DOMAIN
+ tristate "Future Domain 16xx SCSI/AHA-2920A support"
+ depends on (ISA || PCI) && SCSI
+ ---help---
+ This is support for Future Domain's 16-bit SCSI host adapters
+ (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
+ other adapters based on the Future Domain chipsets (Quantum
+ ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board).
+ It is explained in section 3.7 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip
+ and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI
+ controller support"). This Future Domain driver works with the older
+ Adaptec AHA-2920A boards with a Future Domain chip on them.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fdomain.
+
+config SCSI_FD_MCS
+ tristate "Future Domain MCS-600/700 SCSI support"
+ depends on MCA_LEGACY && SCSI
+ ---help---
+ This is support for Future Domain MCS 600/700 MCA SCSI adapters.
+ Some PS/2 computers are equipped with IBM Fast SCSI Adapter/A which
+ is identical to the MCS 700 and hence also supported by this driver.
+ This driver also supports the Reply SB16/SCSI card (the SCSI part).
+ It supports multiple adapters in the same system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fd_mcs.
+
+config SCSI_GDTH
+ tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support"
+ depends on (ISA || EISA || PCI) && SCSI && (BROKEN || !SPARC64)
+ ---help---
+ Formerly called GDT SCSI Disk Array Controller Support.
+
+ This is a driver for RAID/SCSI Disk Array Controllers (EISA/ISA/PCI)
+ manufactured by Intel Corporation/ICP vortex GmbH. It is documented
+ in the kernel source in <file:drivers/scsi/gdth.c> and
+ <file:drivers/scsi/gdth.h.>
+
+ To compile this driver as a module, choose M here: the
+ module will be called gdth.
+
+config SCSI_GENERIC_NCR5380
+ tristate "Generic NCR5380/53c400 SCSI PIO support"
+ depends on ISA && SCSI
+ ---help---
+ This is a driver for the old NCR 53c80 series of SCSI controllers
+ on boards using PIO. Most boards such as the Trantor T130 fit this
+ category, along with a large number of ISA 8bit controllers shipped
+ for free with SCSI scanners. If you have a PAS16, T128 or DMX3191
+ you should select the specific driver for that card rather than
+ generic 5380 support.
+
+ It is explained in section 3.8 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. If it doesn't work out
+ of the box, you may have to change some settings in
+ <file:drivers/scsi/g_NCR5380.h>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called g_NCR5380.
+
+config SCSI_GENERIC_NCR5380_MMIO
+ tristate "Generic NCR5380/53c400 SCSI MMIO support"
+ depends on ISA && SCSI
+ ---help---
+ This is a driver for the old NCR 53c80 series of SCSI controllers
+ on boards using memory mapped I/O.
+ It is explained in section 3.8 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. If it doesn't work out
+ of the box, you may have to change some settings in
+ <file:drivers/scsi/g_NCR5380.h>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called g_NCR5380_mmio.
+
+config SCSI_GENERIC_NCR53C400
+ bool "Enable NCR53c400 extensions"
+ depends on SCSI_GENERIC_NCR5380
+ help
+ This enables certain optimizations for the NCR53c400 SCSI cards.
+ You might as well try it out. Note that this driver will only probe
+ for the Trantor T130B in its default configuration; you might have
+ to pass a command line option to the kernel at boot time if it does
+ not detect your card. See the file
+ <file:Documentation/scsi/g_NCR5380.txt> for details.
+
+config SCSI_IBMMCA
+ tristate "IBMMCA SCSI support"
+ depends on MCA_LEGACY && SCSI
+ ---help---
+ This is support for the IBM SCSI adapter found in many of the PS/2
+ series computers. These machines have an MCA bus, so you need to
+ answer Y to "MCA support" as well and read
+ <file:Documentation/mca.txt>.
+
+ If the adapter isn't found during boot (a common problem for models
+ 56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=<pun>' kernel
+ option, where <pun> is the id of the SCSI subsystem (usually 7, but
+ if that doesn't work check your reference diskette). Owners of
+ model 95 with a LED-matrix-display can in addition activate some
+ activity info like under OS/2, but more informative, by setting
+ 'ibmmcascsi=display' as an additional kernel parameter. Try "man
+ bootparam" or see the documentation of your boot loader about how to
+ pass options to the kernel.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ibmmca.
+
+config IBMMCA_SCSI_ORDER_STANDARD
+ bool "Standard SCSI-order"
+ depends on SCSI_IBMMCA
+ ---help---
+ In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks
+ are assigned to the drive letters, starting with the lowest SCSI-id
+ (physical number -- pun) to be drive C:, as seen from DOS and
+ similar operating systems. When looking into papers describing the
+ ANSI-SCSI-standard, this assignment of drives appears to be wrong.
+ The SCSI-standard follows a hardware-hierarchy which says that id 7
+ has the highest priority and id 0 the lowest. Therefore, the host
+ adapters are still today everywhere placed as SCSI-id 7 by default.
+ In the SCSI-standard, the drive letters express the priority of the
+ disk. C: should be the hard disk, or a partition on it, with the
+ highest priority. This must therefore be the disk with the highest
+ SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the
+ original definition of the SCSI-standard as also industrial- and
+ process-control-machines, like VME-CPUs running under realtime-OSes
+ (e.g. LynxOS, OS9) do.
+
+ If you like to run Linux on your MCA-machine with the same
+ assignment of hard disks as seen from e.g. DOS or OS/2 on your
+ machine, which is in addition conformant to the SCSI-standard, you
+ must say Y here. This is also necessary for MCA-Linux users who want
+ to keep downward compatibility to older releases of the
+ IBM-MCA-SCSI-driver (older than driver-release 2.00 and older than
+ June 1997).
+
+ If you like to have the lowest SCSI-id assigned as drive C:, as
+ modern SCSI-BIOSes do, which does not conform to the standard, but
+ is widespread and common in the PC-world of today, you must say N
+ here. If unsure, say Y.
+
+config IBMMCA_SCSI_DEV_RESET
+ bool "Reset SCSI-devices at boottime"
+ depends on SCSI_IBMMCA
+ ---help---
+ By default, SCSI-devices are reset when the machine is powered on.
+ However, some devices exist, like special-control-devices,
+ SCSI-CNC-machines, SCSI-printer or scanners of older type, that do
+ not reset when switched on. If you say Y here, each device connected
+ to your SCSI-bus will be issued a reset-command after it has been
+ probed, while the kernel is booting. This may cause problems with
+ more modern devices, like hard disks, which do not appreciate these
+ reset commands, and can cause your system to hang. So say Y only if
+ you know that one of your older devices needs it; N is the safe
+ answer.
+
+config SCSI_IPS
+ tristate "IBM ServeRAID support"
+ depends on PCI && SCSI
+ ---help---
+ This is support for the IBM ServeRAID hardware RAID controllers.
+ See <http://www.developer.ibm.com/welcome/netfinity/serveraid.html>
+ for more information. If this driver does not work correctly
+ without modification please contact the author by email at
+ <ipslinux@adaptec.com>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ips.
+
+config SCSI_IBMVSCSI
+ tristate "IBM Virtual SCSI support"
+ depends on PPC_PSERIES || PPC_ISERIES
+ help
+ This is the IBM POWER Virtual SCSI Client
+
+ To compile this driver as a module, choose M here: the
+ module will be called ibmvscsic.
+
+config SCSI_INITIO
+ tristate "Initio 9100U(W) support"
+ depends on PCI && SCSI
+ help
+ This is support for the Initio 91XXU(W) SCSI host adapter. Please
+ read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called initio.
+
+config SCSI_INIA100
+ tristate "Initio INI-A100U2W support"
+ depends on PCI && SCSI
+ help
+ This is support for the Initio INI-A100U2W SCSI host adapter.
+ Please read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called a100u2w.
+
+config SCSI_PPA
+ tristate "IOMEGA parallel port (ppa - older drives)"
+ depends on SCSI && PARPORT
+ ---help---
+ This driver supports older versions of IOMEGA's parallel port ZIP
+ drive (a 100 MB removable media device).
+
+ Note that you can say N here if you have the SCSI version of the ZIP
+ drive: it will be supported automatically if you said Y to the
+ generic "SCSI disk support", above.
+
+ If you have the ZIP Plus drive or a more recent parallel port ZIP
+ drive (if the supplied cable with the drive is labeled "AutoDetect")
+ then you should say N here and Y to "IOMEGA parallel port (imm -
+ newer drives)", below.
+
+ For more information about this driver and how to use it you should
+ read the file <file:Documentation/scsi/ppa.txt>. You should also read
+ the SCSI-HOWTO, which is available from
+ <http://www.tldp.org/docs.html#howto>. If you use this driver,
+ you will still be able to use the parallel port for other tasks,
+ such as a printer; it is safe to compile both drivers into the
+ kernel.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ppa.
+
+config SCSI_IMM
+ tristate "IOMEGA parallel port (imm - newer drives)"
+ depends on SCSI && PARPORT
+ ---help---
+ This driver supports newer versions of IOMEGA's parallel port ZIP
+ drive (a 100 MB removable media device).
+
+ Note that you can say N here if you have the SCSI version of the ZIP
+ drive: it will be supported automatically if you said Y to the
+ generic "SCSI disk support", above.
+
+ If you have the ZIP Plus drive or a more recent parallel port ZIP
+ drive (if the supplied cable with the drive is labeled "AutoDetect")
+ then you should say Y here; if you have an older ZIP drive, say N
+ here and Y to "IOMEGA Parallel Port (ppa - older drives)", above.
+
+ For more information about this driver and how to use it you should
+ read the file <file:Documentation/scsi/ppa.txt>. You should also read
+ the SCSI-HOWTO, which is available from
+ <http://www.tldp.org/docs.html#howto>. If you use this driver,
+ you will still be able to use the parallel port for other tasks,
+ such as a printer; it is safe to compile both drivers into the
+ kernel.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imm.
+
+config SCSI_IZIP_EPP16
+ bool "ppa/imm option - Use slow (but safe) EPP-16"
+ depends on PARPORT && (SCSI_PPA || SCSI_IMM)
+ ---help---
+ EPP (Enhanced Parallel Port) is a standard for parallel ports which
+ allows them to act as expansion buses that can handle up to 64
+ peripheral devices.
+
+ Some parallel port chipsets are slower than their motherboard, and
+ so we have to control the state of the chipset's FIFO queue every
+ now and then to avoid data loss. This will be done if you say Y
+ here.
+
+ Generally, saying Y is the safe option and slows things down a bit.
+
+config SCSI_IZIP_SLOW_CTR
+ bool "ppa/imm option - Assume slow parport control register"
+ depends on PARPORT && (SCSI_PPA || SCSI_IMM)
+ help
+ Some parallel ports are known to have excessive delays between
+ changing the parallel port control register and good data being
+ available on the parallel port data/status register. This option
+ forces a small delay (1.0 usec to be exact) after changing the
+ control register to let things settle out. Enabling this option may
+ result in a big drop in performance but some very old parallel ports
+ (found in 386 vintage machines) will not work properly.
+
+ Generally, saying N is fine.
+
+config SCSI_NCR53C406A
+ tristate "NCR53c406a SCSI support"
+ depends on ISA && SCSI
+ help
+ This is support for the NCR53c406a SCSI host adapter. For user
+ configurable parameters, check out <file:drivers/scsi/NCR53c406a.c>
+ in the kernel source. Also read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called NCR53c406.
+
+config SCSI_NCR_D700
+ tristate "NCR Dual 700 MCA SCSI support"
+ depends on MCA && SCSI
+ select SCSI_SPI_ATTRS
+ help
+ This is a driver for the MicroChannel Dual 700 card produced by
+ NCR and commonly used in 345x/35xx/4100 class machines. It always
+ tries to negotiate sync and uses tag command queueing.
+
+ Unless you have an NCR manufactured machine, the chances are that
+ you do not have this SCSI card, so say N.
+
+config 53C700_IO_MAPPED
+ bool
+ depends on SCSI_NCR_D700
+ default y
+
+config SCSI_LASI700
+ tristate "HP Lasi SCSI support for 53c700/710"
+ depends on GSC && SCSI
+ select SCSI_SPI_ATTRS
+ help
+ This is a driver for the SCSI controller in the Lasi chip found in
+ many PA-RISC workstations & servers. If you do not know whether you
+ have a Lasi chip, it is safe to say "Y" here.
+
+config 53C700_MEM_MAPPED
+ bool
+ depends on SCSI_LASI700
+ default y
+
+config 53C700_LE_ON_BE
+ bool
+ depends on SCSI_LASI700
+ default y
+
+config SCSI_SYM53C8XX_2
+ tristate "SYM53C8XX Version 2 SCSI support"
+ depends on PCI && SCSI
+ select SCSI_SPI_ATTRS
+ ---help---
+ This driver supports the whole NCR53C8XX/SYM53C8XX family of
+ PCI-SCSI controllers. It also supports the subset of LSI53C10XX
+ Ultra-160 controllers that are based on the SYM53C8XX SCRIPTS
+ language. It does not support LSI53C10XX Ultra-320 PCI-X SCSI
+ controllers; you need to use the Fusion MPT driver for that.
+
+ Please read <file:Documentation/scsi/sym53c8xx_2.txt> for more
+ information.
+
+config SCSI_SYM53C8XX_DMA_ADDRESSING_MODE
+ int "DMA addressing mode"
+ depends on SCSI_SYM53C8XX_2
+ default "1"
+ ---help---
+ This option only applies to PCI-SCSI chips that are PCI DAC
+ capable (875A, 895A, 896, 1010-33, 1010-66, 1000).
+
+ When set to 0, the driver will program the chip to only perform
+ 32-bit DMA. When set to 1, the chip will be able to perform DMA
+ to addresses up to 1TB. When set to 2, the driver supports the
+ full 64-bit DMA address range, but can only address 16 segments
+ of 4 GB each. This limits the total addressable range to 64 GB.
+
+ Most machines with less than 4GB of memory should use a setting
+ of 0 for best performance. If your machine has 4GB of memory
+ or more, you should set this option to 1 (the default).
+
+ The still experimental value 2 (64 bit DMA addressing with 16
+ x 4GB segments limitation) can be used on systems that require
+ PCI address bits past bit 39 to be set for the addressing of
+ memory using PCI DAC cycles.
+
+config SCSI_SYM53C8XX_DEFAULT_TAGS
+ int "default tagged command queue depth"
+ depends on SCSI_SYM53C8XX_2
+ default "16"
+ help
+ This is the default value of the command queue depth the
+ driver will announce to the generic SCSI layer for devices
+ that support tagged command queueing. This value can be changed
+ from the boot command line. This is a soft limit that cannot
+ exceed CONFIG_SCSI_SYM53C8XX_MAX_TAGS.
+
+config SCSI_SYM53C8XX_MAX_TAGS
+ int "maximum number of queued commands"
+ depends on SCSI_SYM53C8XX_2
+ default "64"
+ help
+ This option allows you to specify the maximum number of commands
+ that can be queued to any device, when tagged command queuing is
+ possible. The driver supports up to 256 queued commands per device.
+ This value is used as a compiled-in hard limit.
+
+config SCSI_SYM53C8XX_IOMAPPED
+ bool "use port IO"
+ depends on SCSI_SYM53C8XX_2
+ help
+ If you say Y here, the driver will use port IO to access
+ the card. This is significantly slower then using memory
+ mapped IO. Most people should answer N.
+
+config SCSI_IPR
+ tristate "IBM Power Linux RAID adapter support"
+ depends on PCI && SCSI
+ select FW_LOADER
+ ---help---
+ This driver supports the IBM Power Linux family RAID adapters.
+ This includes IBM pSeries 5712, 5703, 5709, and 570A, as well
+ as IBM iSeries 5702, 5703, 5709, and 570A.
+
+config SCSI_IPR_TRACE
+ bool "enable driver internal trace"
+ depends on SCSI_IPR
+ help
+ If you say Y here, the driver will trace all commands issued
+ to the adapter. Performance impact is minimal. Trace can be
+ dumped using /sys/bus/class/scsi_host/hostXX/trace.
+
+config SCSI_IPR_DUMP
+ bool "enable adapter dump support"
+ depends on SCSI_IPR
+ help
+ If you say Y here, the driver will support adapter crash dump.
+ If you enable this support, the iprdump daemon can be used
+ to capture adapter failure analysis information.
+
+config SCSI_ZALON
+ tristate "Zalon SCSI support"
+ depends on GSC && SCSI
+ select SCSI_SPI_ATTRS
+ help
+ The Zalon is a GSC/HSC bus interface chip that sits between the
+ PA-RISC processor and the NCR 53c720 SCSI controller on C100,
+ C110, J200, J210 and some D, K & R-class machines. It's also
+ used on the add-in Bluefish, Barracuda & Shrike SCSI cards.
+ Say Y here if you have one of these machines or cards.
+
+config SCSI_NCR_Q720
+ tristate "NCR Quad 720 MCA SCSI support"
+ depends on MCA && SCSI
+ select SCSI_SPI_ATTRS
+ help
+ This is a driver for the MicroChannel Quad 720 card produced by
+ NCR and commonly used in 345x/35xx/4100 class machines. It always
+ tries to negotiate sync and uses tag command queueing.
+
+ Unless you have an NCR manufactured machine, the chances are that
+ you do not have this SCSI card, so say N.
+
+config SCSI_NCR53C8XX_DEFAULT_TAGS
+ int " default tagged command queue depth"
+ depends on SCSI_ZALON || SCSI_NCR_Q720
+ default "8"
+ ---help---
+ "Tagged command queuing" is a feature of SCSI-2 which improves
+ performance: the host adapter can send several SCSI commands to a
+ device's queue even if previous commands haven't finished yet.
+ Because the device is intelligent, it can optimize its operations
+ (like head positioning) based on its own request queue. Some SCSI
+ devices don't implement this properly; if you want to disable this
+ feature, enter 0 or 1 here (it doesn't matter which).
+
+ The default value is 8 and should be supported by most hard disks.
+ This value can be overridden from the boot command line using the
+ 'tags' option as follows (example):
+ 'ncr53c8xx=tags:4/t2t3q16/t0u2q10' will set default queue depth to
+ 4, set queue depth to 16 for target 2 and target 3 on controller 0
+ and set queue depth to 10 for target 0 / lun 2 on controller 1.
+
+ The normal answer therefore is to go with the default 8 and to use
+ a boot command line option for devices that need to use a different
+ command queue depth.
+
+ There is no safe option other than using good SCSI devices.
+
+config SCSI_NCR53C8XX_MAX_TAGS
+ int " maximum number of queued commands"
+ depends on SCSI_ZALON || SCSI_NCR_Q720
+ default "32"
+ ---help---
+ This option allows you to specify the maximum number of commands
+ that can be queued to any device, when tagged command queuing is
+ possible. The default value is 32. Minimum is 2, maximum is 64.
+ Modern hard disks are able to support 64 tags and even more, but
+ do not seem to be faster when more than 32 tags are being used.
+
+ So, the normal answer here is to go with the default value 32 unless
+ you are using very large hard disks with large cache (>= 1 MB) that
+ are able to take advantage of more than 32 tagged commands.
+
+ There is no safe option and the default answer is recommended.
+
+config SCSI_NCR53C8XX_SYNC
+ int " synchronous transfers frequency in MHz"
+ depends on SCSI_ZALON || SCSI_NCR_Q720
+ default "20"
+ ---help---
+ The SCSI Parallel Interface-2 Standard defines 5 classes of transfer
+ rates: FAST-5, FAST-10, FAST-20, FAST-40 and FAST-80. The numbers
+ are respectively the maximum data transfer rates in mega-transfers
+ per second for each class. For example, a FAST-20 Wide 16 device is
+ able to transfer data at 20 million 16 bit packets per second for a
+ total rate of 40 MB/s.
+
+ You may specify 0 if you want to only use asynchronous data
+ transfers. This is the safest and slowest option. Otherwise, specify
+ a value between 5 and 80, depending on the capability of your SCSI
+ controller. The higher the number, the faster the data transfer.
+ Note that 80 should normally be ok since the driver decreases the
+ value automatically according to the controller's capabilities.
+
+ Your answer to this question is ignored for controllers with NVRAM,
+ since the driver will get this information from the user set-up. It
+ also can be overridden using a boot setup option, as follows
+ (example): 'ncr53c8xx=sync:12' will allow the driver to negotiate
+ for FAST-20 synchronous data transfer (20 mega-transfers per
+ second).
+
+ The normal answer therefore is not to go with the default but to
+ select the maximum value 80 allowing the driver to use the maximum
+ value supported by each controller. If this causes problems with
+ your SCSI devices, you should come back and decrease the value.
+
+ There is no safe option other than using good cabling, right
+ terminations and SCSI conformant devices.
+
+config SCSI_NCR53C8XX_PROFILE
+ bool " enable profiling"
+ depends on SCSI_ZALON || SCSI_NCR_Q720
+ help
+ This option allows you to enable profiling information gathering.
+ These statistics are not very accurate due to the low frequency
+ of the kernel clock (100 Hz on i386) and have performance impact
+ on systems that use very fast devices.
+
+ The normal answer therefore is N.
+
+config SCSI_NCR53C8XX_NO_DISCONNECT
+ bool " not allow targets to disconnect"
+ depends on (SCSI_ZALON || SCSI_NCR_Q720) && SCSI_NCR53C8XX_DEFAULT_TAGS=0
+ help
+ This option is only provided for safety if you suspect some SCSI
+ device of yours to not support properly the target-disconnect
+ feature. In that case, you would say Y here. In general however, to
+ not allow targets to disconnect is not reasonable if there is more
+ than 1 device on a SCSI bus. The normal answer therefore is N.
+
+config SCSI_MCA_53C9X
+ tristate "NCR MCA 53C9x SCSI support"
+ depends on MCA_LEGACY && SCSI && BROKEN_ON_SMP
+ help
+ Some MicroChannel machines, notably the NCR 35xx line, use a SCSI
+ controller based on the NCR 53C94. This driver will allow use of
+ the controller on the 3550, and very possibly others.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mca_53c9x.
+
+config SCSI_PAS16
+ tristate "PAS16 SCSI support"
+ depends on ISA && SCSI
+ ---help---
+ This is support for a SCSI host adapter. It is explained in section
+ 3.10 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. If it doesn't work out
+ of the box, you may have to change some settings in
+ <file:drivers/scsi/pas16.h>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pas16.
+
+config SCSI_PCI2000
+ tristate "PCI2000 support"
+ depends on PCI && SCSI && BROKEN
+ help
+ This is support for the PCI2000I EIDE interface card which acts as a
+ SCSI host adapter. Please read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pci2000.
+
+config SCSI_PCI2220I
+ tristate "PCI2220i support"
+ depends on PCI && SCSI && BROKEN
+ help
+ This is support for the PCI2220i EIDE interface card which acts as a
+ SCSI host adapter. Please read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pci2220i.
+
+config SCSI_PSI240I
+ tristate "PSI240i support"
+ depends on ISA && SCSI
+ help
+ This is support for the PSI240i EIDE interface card which acts as a
+ SCSI host adapter. Please read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called psi240i.
+
+config SCSI_QLOGIC_FAS
+ tristate "Qlogic FAS SCSI support"
+ depends on ISA && SCSI
+ ---help---
+ This is a driver for the ISA, VLB, and PCMCIA versions of the Qlogic
+ FastSCSI! cards as well as any other card based on the FASXX chip
+ (including the Control Concepts SCSI/IDE/SIO/PIO/FDC cards).
+
+ This driver does NOT support the PCI versions of these cards. The
+ PCI versions are supported by the Qlogic ISP driver ("Qlogic ISP
+ SCSI support"), below.
+
+ Information about this driver is contained in
+ <file:Documentation/scsi/qlogicfas.txt>. You should also read the
+ SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qlogicfas.
+
+config SCSI_QLOGIC_ISP
+ tristate "Qlogic ISP SCSI support (old driver)"
+ depends on PCI && SCSI && BROKEN
+ ---help---
+ This driver works for all QLogic PCI SCSI host adapters (IQ-PCI,
+ IQ-PCI-10, IQ_PCI-D) except for the PCI-basic card. (This latter
+ card is supported by the "AM53/79C974 PCI SCSI" driver.)
+
+ If you say Y here, make sure to choose "BIOS" at the question "PCI
+ access mode".
+
+ Please read the file <file:Documentation/scsi/qlogicisp.txt>. You
+ should also read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qlogicisp.
+
+ These days the hardware is also supported by the more modern qla1280
+ driver. In doubt use that one instead of qlogicisp.
+
+config SCSI_QLOGIC_FC
+ tristate "Qlogic ISP FC SCSI support"
+ depends on PCI && SCSI
+ help
+ This is a driver for the QLogic ISP2100 SCSI-FCP host adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qlogicfc.
+
+config SCSI_QLOGIC_FC_FIRMWARE
+ bool "Include loadable firmware in driver"
+ depends on SCSI_QLOGIC_FC
+ help
+ Say Y to include ISP2X00 Fabric Initiator/Target Firmware, with
+ expanded LUN addressing and FcTape (FCP-2) support, in the
+ qlogicfc driver. This is required on some platforms.
+
+config SCSI_QLOGIC_1280
+ tristate "Qlogic QLA 1240/1x80/1x160 SCSI support"
+ depends on PCI && SCSI
+ help
+ Say Y if you have a QLogic ISP1240/1x80/1x160 SCSI host adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qla1280.
+
+config SCSI_QLOGIC_1280_1040
+ bool "Qlogic QLA 1020/1040 SCSI support"
+ depends on SCSI_QLOGIC_1280 && SCSI_QLOGIC_ISP!=y
+ help
+ Say Y here if you have a QLogic ISP1020/1040 SCSI host adapter and
+ do not want to use the old driver. This option enables support in
+ the qla1280 driver for those host adapters.
+
+config SCSI_QLOGICPTI
+ tristate "PTI Qlogic, ISP Driver"
+ depends on SBUS && SCSI
+ help
+ This driver supports SBUS SCSI controllers from PTI or QLogic. These
+ controllers are known under Solaris as qpti and in the openprom as
+ PTI,ptisp or QLGC,isp. Note that PCI QLogic SCSI controllers are
+ driven by a different driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qlogicpti.
+
+source "drivers/scsi/qla2xxx/Kconfig"
+
+config SCSI_SEAGATE
+ tristate "Seagate ST-02 and Future Domain TMC-8xx SCSI support"
+ depends on X86 && ISA && SCSI && BROKEN
+ ---help---
+ These are 8-bit SCSI controllers; the ST-01 is also supported by
+ this driver. It is explained in section 3.9 of the SCSI-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>. If it
+ doesn't work out of the box, you may have to change some settings in
+ <file:drivers/scsi/seagate.h>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called seagate.
+
+# definitely looks not 64bit safe:
+config SCSI_SIM710
+ tristate "Simple 53c710 SCSI support (Compaq, NCR machines)"
+ depends on (EISA || MCA) && SCSI
+ select SCSI_SPI_ATTRS
+ ---help---
+ This driver for NCR53c710 based SCSI host adapters.
+
+ It currently supports Compaq EISA cards and NCR MCA cards
+
+config 53C700_IO_MAPPED
+ bool
+ depends on SCSI_SIM710
+ default y
+
+config SCSI_SYM53C416
+ tristate "Symbios 53c416 SCSI support"
+ depends on ISA && SCSI
+ ---help---
+ This is support for the sym53c416 SCSI host adapter, the SCSI
+ adapter that comes with some HP scanners. This driver requires that
+ the sym53c416 is configured first using some sort of PnP
+ configuration program (e.g. isapnp) or by a PnP aware BIOS. If you
+ are using isapnp then you need to compile this driver as a module
+ and then load it using insmod after isapnp has run. The parameters
+ of the configured card(s) should be passed to the driver. The format
+ is:
+
+ insmod sym53c416 sym53c416=<base>,<irq> [sym53c416_1=<base>,<irq>]
+
+ To compile this driver as a module, choose M here: the
+ module will be called sym53c416.
+
+config SCSI_DC395x
+ tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support (EXPERIMENTAL)"
+ depends on PCI && SCSI && EXPERIMENTAL
+ ---help---
+ This driver supports PCI SCSI host adapters based on the ASIC
+ TRM-S1040 chip, e.g Tekram DC395(U/UW/F) and DC315(U) variants.
+
+ This driver works, but is still in experimental status. So better
+ have a bootable disk and a backup in case of emergency.
+
+ Documentation can be found in <file:Documentation/scsi/dc395x.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dc395x.
+
+config SCSI_DC390T
+ tristate "Tekram DC390(T) and Am53/79C974 SCSI support"
+ depends on PCI && SCSI
+ ---help---
+ This driver supports PCI SCSI host adapters based on the Am53C974A
+ chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard
+ PCscsi/PCnet (Am53/79C974) solutions.
+
+ Documentation can be found in <file:Documentation/scsi/tmscsim.txt>.
+
+ Note that this driver does NOT support Tekram DC390W/U/F, which are
+ based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tmscsim.
+
+config SCSI_T128
+ tristate "Trantor T128/T128F/T228 SCSI support"
+ depends on ISA && SCSI
+ ---help---
+ This is support for a SCSI host adapter. It is explained in section
+ 3.11 of the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. If it doesn't work out
+ of the box, you may have to change some settings in
+ <file:drivers/scsi/t128.h>. Note that Trantor was purchased by
+ Adaptec, and some former Trantor products are being sold under the
+ Adaptec name.
+
+ To compile this driver as a module, choose M here: the
+ module will be called t128.
+
+config SCSI_U14_34F
+ tristate "UltraStor 14F/34F support"
+ depends on ISA && SCSI
+ ---help---
+ This is support for the UltraStor 14F and 34F SCSI-2 host adapters.
+ The source at <file:drivers/scsi/u14-34f.c> contains some
+ information about this hardware. If the driver doesn't work out of
+ the box, you may have to change some settings in
+ <file: drivers/scsi/u14-34f.c>. Read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that there is also
+ another driver for the same hardware: "UltraStor SCSI support",
+ below. You should say Y to both only if you want 24F support as
+ well.
+
+ To compile this driver as a module, choose M here: the
+ module will be called u14-34f.
+
+config SCSI_U14_34F_TAGGED_QUEUE
+ bool "enable tagged command queueing"
+ depends on SCSI_U14_34F
+ help
+ This is a feature of SCSI-2 which improves performance: the host
+ adapter can send several SCSI commands to a device's queue even if
+ previous commands haven't finished yet.
+ This is equivalent to the "u14-34f=tc:y" boot option.
+
+config SCSI_U14_34F_LINKED_COMMANDS
+ bool "enable elevator sorting"
+ depends on SCSI_U14_34F
+ help
+ This option enables elevator sorting for all probed SCSI disks and
+ CD-ROMs. It definitely reduces the average seek distance when doing
+ random seeks, but this does not necessarily result in a noticeable
+ performance improvement: your mileage may vary...
+ This is equivalent to the "u14-34f=lc:y" boot option.
+
+config SCSI_U14_34F_MAX_TAGS
+ int "maximum number of queued commands"
+ depends on SCSI_U14_34F
+ default "8"
+ help
+ This specifies how many SCSI commands can be maximally queued for
+ each probed SCSI device. You should reduce the default value of 8
+ only if you have disks with buggy or limited tagged command support.
+ Minimum is 2 and maximum is 14. This value is also the window size
+ used by the elevator sorting option above. The effective value used
+ by the driver for each probed SCSI device is reported at boot time.
+ This is equivalent to the "u14-34f=mq:8" boot option.
+
+config SCSI_ULTRASTOR
+ tristate "UltraStor SCSI support"
+ depends on X86 && ISA && SCSI
+ ---help---
+ This is support for the UltraStor 14F, 24F and 34F SCSI-2 host
+ adapter family. This driver is explained in section 3.12 of the
+ SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. If it doesn't work out
+ of the box, you may have to change some settings in
+ <file:drivers/scsi/ultrastor.h>.
+
+ Note that there is also another driver for the same hardware:
+ "UltraStor 14F/34F support", above.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ultrastor.
+
+config SCSI_NSP32
+ tristate "Workbit NinjaSCSI-32Bi/UDE support"
+ depends on PCI && SCSI && !64BIT
+ help
+ This is support for the Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus
+ SCSI host adapter. Please read the SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called nsp32.
+
+config SCSI_DEBUG
+ tristate "SCSI debugging host simulator"
+ depends on SCSI
+ help
+ This is a host adapter simulator that can simulate multiple hosts
+ each with multiple dummy SCSI devices (disks). It defaults to one
+ host adapter with one dummy SCSI disk. Each dummy disk uses kernel
+ RAM as storage (i.e. it is a ramdisk). To save space when multiple
+ dummy disks are simulated, they share the same kernel RAM for
+ their storage. See <http://www.torque.net/sg/sdebug.html> for more
+ information. This driver is primarily of use to those testing the
+ SCSI and block subsystems. If unsure, say N.
+
+config SCSI_MESH
+ tristate "MESH (Power Mac internal SCSI) support"
+ depends on PPC32 && PPC_PMAC && SCSI
+ help
+ Many Power Macintoshes and clones have a MESH (Macintosh Enhanced
+ SCSI Hardware) SCSI bus adaptor (the 7200 doesn't, but all of the
+ other Power Macintoshes do). Say Y to include support for this SCSI
+ adaptor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mesh.
+
+config SCSI_MESH_SYNC_RATE
+ int "maximum synchronous transfer rate (MB/s) (0 = async)"
+ depends on SCSI_MESH
+ default "5"
+ help
+ On Power Macintoshes (and clones) where the MESH SCSI bus adaptor
+ drives a bus which is entirely internal to the machine (such as the
+ 7500, 7600, 8500, etc.), the MESH is capable of synchronous
+ operation at up to 10 MB/s. On machines where the SCSI bus
+ controlled by the MESH can have external devices connected, it is
+ usually rated at 5 MB/s. 5 is a safe value here unless you know the
+ MESH SCSI bus is internal only; in that case you can say 10. Say 0
+ to disable synchronous operation.
+
+config SCSI_MESH_RESET_DELAY_MS
+ int "initial bus reset delay (ms) (0 = no reset)"
+ depends on SCSI_MESH
+ default "4000"
+
+config SCSI_MAC53C94
+ tristate "53C94 (Power Mac external SCSI) support"
+ depends on PPC32 && PPC_PMAC && SCSI
+ help
+ On Power Macintoshes (and clones) with two SCSI buses, the external
+ SCSI bus is usually controlled by a 53C94 SCSI bus adaptor. Older
+ machines which only have one SCSI bus, such as the 7200, also use
+ the 53C94. Say Y to include support for the 53C94.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mac53c94.
+
+source "drivers/scsi/arm/Kconfig"
+
+config JAZZ_ESP
+ bool "MIPS JAZZ FAS216 SCSI support"
+ depends on MACH_JAZZ && SCSI
+ help
+ This is the driver for the onboard SCSI host adapter of MIPS Magnum
+ 4000, Acer PICA, Olivetti M700-10 and a few other identical OEM
+ systems.
+
+config A3000_SCSI
+ tristate "A3000 WD33C93A support"
+ depends on AMIGA && SCSI
+ help
+ If you have an Amiga 3000 and have SCSI devices connected to the
+ built-in SCSI controller, say Y. Otherwise, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wd33c93.
+
+config A2091_SCSI
+ tristate "A2091/A590 WD33C93A support"
+ depends on ZORRO && SCSI
+ help
+ If you have a Commodore A2091 SCSI controller, say Y. Otherwise,
+ say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wd33c93.
+
+config GVP11_SCSI
+ tristate "GVP Series II WD33C93A support"
+ depends on ZORRO && SCSI
+ ---help---
+ If you have a Great Valley Products Series II SCSI controller,
+ answer Y. Also say Y if you have a later model of GVP SCSI
+ controller (such as the GVP A4008 or a Combo board). Otherwise,
+ answer N. This driver does NOT work for the T-Rex series of
+ accelerators from TekMagic and GVP-M.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gvp11.
+
+config CYBERSTORM_SCSI
+ tristate "CyberStorm SCSI support"
+ depends on ZORRO && SCSI
+ help
+ If you have an Amiga with an original (MkI) Phase5 Cyberstorm
+ accelerator board and the optional Cyberstorm SCSI controller,
+ answer Y. Otherwise, say N.
+
+config CYBERSTORMII_SCSI
+ tristate "CyberStorm Mk II SCSI support"
+ depends on ZORRO && SCSI
+ help
+ If you have an Amiga with a Phase5 Cyberstorm MkII accelerator board
+ and the optional Cyberstorm SCSI controller, say Y. Otherwise,
+ answer N.
+
+config BLZ2060_SCSI
+ tristate "Blizzard 2060 SCSI support"
+ depends on ZORRO && SCSI
+ help
+ If you have an Amiga with a Phase5 Blizzard 2060 accelerator board
+ and want to use the onboard SCSI controller, say Y. Otherwise,
+ answer N.
+
+config BLZ1230_SCSI
+ tristate "Blizzard 1230IV/1260 SCSI support"
+ depends on ZORRO && SCSI
+ help
+ If you have an Amiga 1200 with a Phase5 Blizzard 1230IV or Blizzard
+ 1260 accelerator, and the optional SCSI module, say Y. Otherwise,
+ say N.
+
+config FASTLANE_SCSI
+ tristate "Fastlane SCSI support"
+ depends on ZORRO && SCSI
+ help
+ If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use
+ one in the near future, say Y to this question. Otherwise, say N.
+
+config SCSI_AMIGA7XX
+ bool "Amiga NCR53c710 SCSI support (EXPERIMENTAL)"
+ depends on AMIGA && SCSI && EXPERIMENTAL && BROKEN
+ help
+ Support for various NCR53c710-based SCSI controllers on the Amiga.
+ This includes:
+ - the builtin SCSI controller on the Amiga 4000T,
+ - the Amiga 4091 Zorro III SCSI-2 controller,
+ - the MacroSystem Development's WarpEngine Amiga SCSI-2 controller
+ (info at
+ <http://www.lysator.liu.se/amiga/ar/guide/ar310.guide?FEATURE5>),
+ - the SCSI controller on the Phase5 Blizzard PowerUP 603e+
+ accelerator card for the Amiga 1200,
+ - the SCSI controller on the GVP Turbo 040/060 accelerator.
+ Note that all of the above SCSI controllers, except for the builtin
+ SCSI controller on the Amiga 4000T, reside on the Zorro expansion
+ bus, so you also have to enable Zorro bus support if you want to use
+ them.
+
+config OKTAGON_SCSI
+ tristate "BSC Oktagon SCSI support (EXPERIMENTAL)"
+ depends on ZORRO && SCSI && EXPERIMENTAL
+ help
+ If you have the BSC Oktagon SCSI disk controller for the Amiga, say
+ Y to this question. If you're in doubt about whether you have one,
+ see the picture at
+ <http://amiga.resource.cx/exp/search.pl?product=oktagon>.
+
+config ATARI_SCSI
+ tristate "Atari native SCSI support"
+ depends on ATARI && SCSI && BROKEN
+ ---help---
+ If you have an Atari with built-in NCR5380 SCSI controller (TT,
+ Falcon, ...) say Y to get it supported. Of course also, if you have
+ a compatible SCSI controller (e.g. for Medusa).
+
+ To compile this driver as a module, choose M here: the
+ module will be called atari_scsi.
+
+ This driver supports both styles of NCR integration into the
+ system: the TT style (separate DMA), and the Falcon style (via
+ ST-DMA, replacing ACSI). It does NOT support other schemes, like
+ in the Hades (without DMA).
+
+config ATARI_SCSI_TOSHIBA_DELAY
+ bool "Long delays for Toshiba CD-ROMs"
+ depends on ATARI_SCSI
+ help
+ This option increases the delay after a SCSI arbitration to
+ accommodate some flaky Toshiba CD-ROM drives. Say Y if you intend to
+ use a Toshiba CD-ROM drive; otherwise, the option is not needed and
+ would impact performance a bit, so say N.
+
+config ATARI_SCSI_RESET_BOOT
+ bool "Reset SCSI-devices at boottime"
+ depends on ATARI_SCSI
+ help
+ Reset the devices on your Atari whenever it boots. This makes the
+ boot process fractionally longer but may assist recovery from errors
+ that leave the devices with SCSI operations partway completed.
+
+config TT_DMA_EMUL
+ bool "Hades SCSI DMA emulator"
+ depends on ATARI_SCSI && HADES
+ help
+ This option enables code which emulates the TT SCSI DMA chip on the
+ Hades. This increases the SCSI transfer rates at least ten times
+ compared to PIO transfers.
+
+config MAC_SCSI
+ bool "Macintosh NCR5380 SCSI"
+ depends on MAC && SCSI
+ help
+ This is the NCR 5380 SCSI controller included on most of the 68030
+ based Macintoshes. If you have one of these say Y and read the
+ SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+config SCSI_MAC_ESP
+ tristate "Macintosh NCR53c9[46] SCSI"
+ depends on MAC && SCSI
+ help
+ This is the NCR 53c9x SCSI controller found on most of the 68040
+ based Macintoshes. If you have one of these say Y and read the
+ SCSI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mac_esp.
+
+config MVME147_SCSI
+ bool "WD33C93 SCSI driver for MVME147"
+ depends on MVME147 && SCSI
+ help
+ Support for the on-board SCSI controller on the Motorola MVME147
+ single-board computer.
+
+config MVME16x_SCSI
+ bool "NCR53C710 SCSI driver for MVME16x"
+ depends on MVME16x && SCSI && BROKEN
+ help
+ The Motorola MVME162, 166, 167, 172 and 177 boards use the NCR53C710
+ SCSI controller chip. Almost everyone using one of these boards
+ will want to say Y to this question.
+
+config BVME6000_SCSI
+ bool "NCR53C710 SCSI driver for BVME6000"
+ depends on BVME6000 && SCSI && BROKEN
+ help
+ The BVME4000 and BVME6000 boards from BVM Ltd use the NCR53C710
+ SCSI controller chip. Almost everyone using one of these boards
+ will want to say Y to this question.
+
+config SCSI_NCR53C7xx_FAST
+ bool "allow FAST-SCSI [10MHz]"
+ depends on SCSI_AMIGA7XX || MVME16x_SCSI || BVME6000_SCSI
+ help
+ This will enable 10MHz FAST-SCSI transfers with your host
+ adapter. Some systems have problems with that speed, so it's safest
+ to say N here.
+
+config SUN3_SCSI
+ tristate "Sun3 NCR5380 SCSI"
+ depends on SUN3 && SCSI
+ help
+ This option will enable support for the OBIO (onboard io) NCR5380
+ SCSI controller found in the Sun 3/50 and 3/60, as well as for
+ "Sun3" type VME scsi controllers also based on the NCR5380.
+ General Linux information on the Sun 3 series (now discontinued)
+ is at <http://www.angelfire.com/ca2/tech68k/sun3.html>.
+
+config SUN3X_ESP
+ bool "Sun3x ESP SCSI"
+ depends on SUN3X && SCSI
+ help
+ The ESP was an on-board SCSI controller used on Sun 3/80
+ machines. Say Y here to compile in support for it.
+
+config SCSI_SUNESP
+ tristate "Sparc ESP Scsi Driver"
+ depends on SBUS && SCSI
+ help
+ This is the driver for the Sun ESP SCSI host adapter. The ESP
+ chipset is present in most SPARC SBUS-based computers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called esp.
+
+# bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI
+
+config ZFCP
+ tristate "FCP host bus adapter driver for IBM eServer zSeries"
+ depends on ARCH_S390 && QDIO && SCSI
+ select SCSI_FC_ATTRS
+ help
+ If you want to access SCSI devices attached to your IBM eServer
+ zSeries by means of Fibre Channel interfaces say Y.
+ For details please refer to the documentation provided by IBM at
+ <http://oss.software.ibm.com/developerworks/opensource/linux390>
+
+ This driver is also available as a module. This module will be
+ called zfcp. If you want to compile it as a module, say M here
+ and read <file:Documentation/modules.txt>.
+
+endmenu
+
+source "drivers/scsi/pcmcia/Kconfig"
+
+endmenu
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
new file mode 100644
index 000000000000..29fcee35ec01
--- /dev/null
+++ b/drivers/scsi/Makefile
@@ -0,0 +1,184 @@
+#
+# Makefile for linux/drivers/scsi
+#
+# 30 May 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+# 20 Sep 2000, Torben Mathiasen <tmm@image.dk>
+# Changed link order to reflect new scsi initialization.
+#
+# *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!
+# The link order must be, SCSI Core, SCSI HBA drivers, and
+# lastly SCSI peripheral drivers (disk/tape/cdrom/etc.) to
+# satisfy certain initialization assumptions in the SCSI layer.
+# *!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!*!
+
+
+CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
+CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
+CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
+
+subdir-$(CONFIG_PCMCIA) += pcmcia
+
+obj-$(CONFIG_SCSI) += scsi_mod.o
+
+# --- NOTE ORDERING HERE ---
+# For kernel non-modular link, transport attributes need to
+# be initialised before drivers
+# --------------------------
+obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o
+obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o
+obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
+
+obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o
+obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
+obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o
+obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o
+obj-$(CONFIG_MVME147_SCSI) += mvme147.o wd33c93.o
+obj-$(CONFIG_SGIWD93_SCSI) += sgiwd93.o wd33c93.o
+obj-$(CONFIG_CYBERSTORM_SCSI) += NCR53C9x.o cyberstorm.o
+obj-$(CONFIG_CYBERSTORMII_SCSI) += NCR53C9x.o cyberstormII.o
+obj-$(CONFIG_BLZ2060_SCSI) += NCR53C9x.o blz2060.o
+obj-$(CONFIG_BLZ1230_SCSI) += NCR53C9x.o blz1230.o
+obj-$(CONFIG_FASTLANE_SCSI) += NCR53C9x.o fastlane.o
+obj-$(CONFIG_OKTAGON_SCSI) += NCR53C9x.o oktagon_esp.o oktagon_io.o
+obj-$(CONFIG_ATARI_SCSI) += atari_scsi.o
+obj-$(CONFIG_MAC_SCSI) += mac_scsi.o
+obj-$(CONFIG_SCSI_MAC_ESP) += mac_esp.o NCR53C9x.o
+obj-$(CONFIG_SUN3_SCSI) += sun3_scsi.o sun3_scsi_vme.o
+obj-$(CONFIG_MVME16x_SCSI) += mvme16x.o 53c7xx.o
+obj-$(CONFIG_BVME6000_SCSI) += bvme6000.o 53c7xx.o
+obj-$(CONFIG_SCSI_SIM710) += 53c700.o sim710.o
+obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o
+obj-$(CONFIG_SCSI_PCI2000) += pci2000.o
+obj-$(CONFIG_SCSI_PCI2220I) += pci2220i.o
+obj-$(CONFIG_SCSI_PSI240I) += psi240i.o
+obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o
+obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o
+obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o
+obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o
+obj-$(CONFIG_SCSI_AHA152X) += aha152x.o
+obj-$(CONFIG_SCSI_AHA1542) += aha1542.o
+obj-$(CONFIG_SCSI_AHA1740) += aha1740.o
+obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx/
+obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/
+obj-$(CONFIG_SCSI_AACRAID) += aacraid/
+obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
+obj-$(CONFIG_SCSI_IPS) += ips.o
+obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
+obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
+obj-$(CONFIG_SCSI_IN2000) += in2000.o
+obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o
+obj-$(CONFIG_SCSI_GENERIC_NCR5380_MMIO) += g_NCR5380_mmio.o
+obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o
+obj-$(CONFIG_SCSI_NCR_D700) += 53c700.o NCR_D700.o
+obj-$(CONFIG_SCSI_NCR_Q720) += NCR_Q720_mod.o
+obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o
+obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas408.o qlogicfas.o
+obj-$(CONFIG_PCMCIA_QLOGIC) += qlogicfas408.o
+obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o
+obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o
+obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
+obj-$(CONFIG_SCSI_QLA2XXX) += qla2xxx/
+obj-$(CONFIG_SCSI_PAS16) += pas16.o
+obj-$(CONFIG_SCSI_SEAGATE) += seagate.o
+obj-$(CONFIG_SCSI_FD_8xx) += seagate.o
+obj-$(CONFIG_SCSI_T128) += t128.o
+obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o
+obj-$(CONFIG_SCSI_DTC3280) += dtc.o
+obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/
+obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o
+obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o
+obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
+obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o
+obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o
+obj-$(CONFIG_SCSI_EATA) += eata.o
+obj-$(CONFIG_SCSI_DC395x) += dc395x.o
+obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
+obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
+obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
+obj-$(CONFIG_SCSI_ACARD) += atp870u.o
+obj-$(CONFIG_SCSI_SUNESP) += esp.o
+obj-$(CONFIG_SCSI_GDTH) += gdth.o
+obj-$(CONFIG_SCSI_INITIO) += initio.o
+obj-$(CONFIG_SCSI_INIA100) += a100u2w.o
+obj-$(CONFIG_SCSI_QLOGICPTI) += qlogicpti.o
+obj-$(CONFIG_BLK_DEV_IDESCSI) += ide-scsi.o
+obj-$(CONFIG_SCSI_MESH) += mesh.o
+obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o
+obj-$(CONFIG_SCSI_PLUTO) += pluto.o
+obj-$(CONFIG_SCSI_DECNCR) += NCR53C9x.o dec_esp.o
+obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
+obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o
+obj-$(CONFIG_SCSI_PPA) += ppa.o
+obj-$(CONFIG_SCSI_IMM) += imm.o
+obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o
+obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o
+obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
+obj-$(CONFIG_SCSI_FCAL) += fcal.o
+obj-$(CONFIG_SCSI_CPQFCTS) += cpqfc.o
+obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o
+obj-$(CONFIG_SCSI_NSP32) += nsp32.o
+obj-$(CONFIG_SCSI_IPR) += ipr.o
+obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/
+obj-$(CONFIG_SCSI_SATA_AHCI) += libata.o ahci.o
+obj-$(CONFIG_SCSI_SATA_SVW) += libata.o sata_svw.o
+obj-$(CONFIG_SCSI_ATA_PIIX) += libata.o ata_piix.o
+obj-$(CONFIG_SCSI_SATA_PROMISE) += libata.o sata_promise.o
+obj-$(CONFIG_SCSI_SATA_QSTOR) += libata.o sata_qstor.o
+obj-$(CONFIG_SCSI_SATA_SIL) += libata.o sata_sil.o
+obj-$(CONFIG_SCSI_SATA_VIA) += libata.o sata_via.o
+obj-$(CONFIG_SCSI_SATA_VITESSE) += libata.o sata_vsc.o
+obj-$(CONFIG_SCSI_SATA_SIS) += libata.o sata_sis.o
+obj-$(CONFIG_SCSI_SATA_SX4) += libata.o sata_sx4.o
+obj-$(CONFIG_SCSI_SATA_NV) += libata.o sata_nv.o
+obj-$(CONFIG_SCSI_SATA_ULI) += libata.o sata_uli.o
+
+obj-$(CONFIG_ARM) += arm/
+
+obj-$(CONFIG_CHR_DEV_ST) += st.o
+obj-$(CONFIG_CHR_DEV_OSST) += osst.o
+obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o
+obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
+obj-$(CONFIG_CHR_DEV_SG) += sg.o
+
+scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
+ scsicam.o scsi_error.o scsi_lib.o \
+ scsi_scan.o scsi_sysfs.o \
+ scsi_devinfo.o
+scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o
+scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o
+
+sd_mod-objs := sd.o
+sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
+ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
+ := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
+ -DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
+zalon7xx-objs := zalon.o ncr53c8xx.o
+NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o
+cpqfc-objs := cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o \
+ cpqfcTSworker.o cpqfcTStrigger.o
+libata-objs := libata-core.o libata-scsi.o
+
+# Files generated that shall be removed upon make clean
+clean-files := 53c7xx_d.h 53c700_d.h \
+ 53c7xx_u.h 53c700_u.h
+
+$(obj)/53c7xx.o: $(obj)/53c7xx_d.h $(obj)/53c7xx_u.h
+$(obj)/53c700.o $(MODVERDIR)/$(obj)/53c700.ver: $(obj)/53c700_d.h
+
+# If you want to play with the firmware, uncomment
+# GENERATE_FIRMWARE := 1
+
+ifdef GENERATE_FIRMWARE
+
+$(obj)/53c7xx_d.h: $(src)/53c7xx.scr $(src)/script_asm.pl
+ $(CPP) -traditional -DCHIP=710 - < $< | grep -v '^#' | $(PERL) -s $(src)/script_asm.pl -ncr7x0_family $@ $(@:_d.h=_u.h)
+
+$(obj)/53c7xx_u.h: $(obj)/53c7xx_d.h
+
+$(obj)/53c700_d.h: $(src)/53c700.scr $(src)/script_asm.pl
+ $(PERL) -s $(src)/script_asm.pl -ncr7x0_family $@ $(@:_d.h=_u.h) < $<
+
+endif
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
new file mode 100644
index 000000000000..5e71a0beafca
--- /dev/null
+++ b/drivers/scsi/NCR5380.c
@@ -0,0 +1,2862 @@
+/*
+ * NCR 5380 generic driver routines. These should make it *trivial*
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
+ *
+ * Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.c,v $
+
+ * Revision 1.10 1998/9/2 Alan Cox
+ * (alan@redhat.com)
+ * Fixed up the timer lockups reported so far. Things still suck. Looking
+ * forward to 2.3 and per device request queues. Then it'll be possible to
+ * SMP thread this beast and improve life no end.
+
+ * Revision 1.9 1997/7/27 Ronald van Cuijlenborg
+ * (ronald.van.cuijlenborg@tip.nl or nutty@dds.nl)
+ * (hopefully) fixed and enhanced USLEEP
+ * added support for DTC3181E card (for Mustek scanner)
+ *
+
+ * Revision 1.8 Ingmar Baumgart
+ * (ingmar@gonzo.schwaben.de)
+ * added support for NCR53C400a card
+ *
+
+ * Revision 1.7 1996/3/2 Ray Van Tassle (rayvt@comm.mot.com)
+ * added proc_info
+ * added support needed for DTC 3180/3280
+ * fixed a couple of bugs
+ *
+
+ * Revision 1.5 1994/01/19 09:14:57 drew
+ * Fixed udelay() hack that was being used on DATAOUT phases
+ * instead of a proper wait for the final handshake.
+ *
+ * Revision 1.4 1994/01/19 06:44:25 drew
+ * *** empty log message ***
+ *
+ * Revision 1.3 1994/01/19 05:24:40 drew
+ * Added support for TCR LAST_BYTE_SENT bit.
+ *
+ * Revision 1.2 1994/01/15 06:14:11 drew
+ * REAL DMA support, bug fixes.
+ *
+ * Revision 1.1 1994/01/15 06:00:54 drew
+ * Initial revision
+ *
+ */
+
+/*
+ * Further development / testing that should be done :
+ * 1. Cleanup the NCR5380_transfer_dma function and DMA operation complete
+ * code so that everything does the same thing that's done at the
+ * end of a pseudo-DMA read operation.
+ *
+ * 2. Fix REAL_DMA (interrupt driven, polled works fine) -
+ * basically, transfer size needs to be reduced by one
+ * and the last byte read as is done with PSEUDO_DMA.
+ *
+ * 4. Test SCSI-II tagged queueing (I have no devices which support
+ * tagged queueing)
+ *
+ * 5. Test linked command handling code after Eric is ready with
+ * the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#undef REAL_DMA
+#endif
+
+#ifdef REAL_DMA_POLL
+#undef READ_OVERRUNS
+#define READ_OVERRUNS
+#endif
+
+#ifdef BOARD_REQUIRES_NO_DELAY
+#define io_recovery_delay(x)
+#else
+#define io_recovery_delay(x) udelay(x)
+#endif
+
+/*
+ * Design
+ *
+ * This is a generic 5380 driver. To use it on a different platform,
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * (Note from hch: unfortunately it was not enough for the different
+ * m68k folks and instead of improving this driver they copied it
+ * and hacked it up for their needs. As a consequence they lost
+ * most updates to this driver. Maybe someone will fix all these
+ * drivers to use a common core one day..)
+ *
+ * As far as command queueing, two queues are maintained for
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
+ * while a command is already executing.
+ *
+ *
+ * Issues specific to the NCR5380 :
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations.
+ *
+ * The workaround for this is to keep track of devices that have
+ * disconnected. If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ *
+ * Some tweaking of N and M needs to be done. An algorithm based
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started from a workqueue for each NCR5380 host in the
+ * system. It attempts to establish I_T_L or I_T_L_Q nexuses by
+ * removing the commands from the issue queue and calling
+ * NCR5380_select() if a nexus is not established.
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work. If the target is
+ * idle for too long, the system will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr() which will in turn call NCR5380_reselect
+ * to reestablish a nexus. This will run main if necessary.
+ *
+ * On command termination, the done function will be called as
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips. To use it, you write an architecture specific functions
+ * and macros and include this file in your driver.
+ *
+ * These macros control options :
+ * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be
+ * defined.
+ *
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
+ * transceivers.
+ *
+ * DONT_USE_INTR - if defined, never use interrupts, even if we probe or
+ * override-configure an IRQ.
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ * bytes at a time. Since interrupts are disabled by default during
+ * these transfers, we might need this to give reasonable interrupt
+ * service time if the transfer size gets too large.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't
+ * rely on phase mismatch and EOP interrupts to determine end
+ * of phase.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
+ * only really want to use this if you're having a problem with
+ * dropped characters during high speed communications, and even
+ * then, you're going to be better off twiddling with transfersize
+ * in the high level code.
+ *
+ * Defaults for these will be provided although the user may want to adjust
+ * these to allocate CPU resources to the SCSI driver or "real" code.
+ *
+ * USLEEP_SLEEP - amount of time, in jiffies, to sleep
+ *
+ * USLEEP_POLL - amount of time, in jiffies, to poll
+ *
+ * These macros MUST be defined :
+ * NCR5380_local_declare() - declare any local variables needed for your
+ * transfer routines.
+ *
+ * NCR5380_setup(instance) - initialize any local variables needed from a given
+ * instance of the host adapter for NCR5380_{read,write,pread,pwrite}
+ *
+ * NCR5380_read(register) - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register
+ *
+ * NCR5380_implementation_fields - additional fields needed for this
+ * specific implementation of the NCR5380
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions :
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes
+ * that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID. If the
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used.
+ */
+
+static int do_abort(struct Scsi_Host *host);
+static void do_reset(struct Scsi_Host *host);
+
+/*
+ * initialize_SCp - init the scsi pointer field
+ * @cmd: command block to set up
+ *
+ * Set up the internal fields in the SCSI command.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd * cmd)
+{
+ /*
+ * Initialize the Scsi Pointer field so that all of the commands in the
+ * various queues are valid.
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page)+
+ cmd->SCp.buffer->offset;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+}
+
+/**
+ * NCR5380_poll_politely - wait for NCR5380 status bits
+ * @instance: controller to poll
+ * @reg: 5380 register to poll
+ * @bit: Bitmask to check
+ * @val: Value required to exit
+ *
+ * Polls the NCR5380 in a reasonably efficient manner waiting for
+ * an event to occur, after a short quick poll we begin giving the
+ * CPU back in non IRQ contexts
+ *
+ * Returns the value of the register or a negative error code.
+ */
+
+static int NCR5380_poll_politely(struct Scsi_Host *instance, int reg, int bit, int val, int t)
+{
+ NCR5380_local_declare();
+ int n = 500; /* At about 8uS a cycle for the cpu access */
+ unsigned long end = jiffies + t;
+ int r;
+
+ NCR5380_setup(instance);
+
+ while( n-- > 0)
+ {
+ r = NCR5380_read(reg);
+ if((r & bit) == val)
+ return 0;
+ cpu_relax();
+ }
+
+ /* t time yet ? */
+ while(time_before(jiffies, end))
+ {
+ r = NCR5380_read(reg);
+ if((r & bit) == val)
+ return 0;
+ if(!in_interrupt())
+ yield();
+ else
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+ {PHASE_DATAOUT, "DATAOUT"},
+ {PHASE_DATAIN, "DATAIN"},
+ {PHASE_CMDOUT, "CMDOUT"},
+ {PHASE_STATIN, "STATIN"},
+ {PHASE_MSGOUT, "MSGOUT"},
+ {PHASE_MSGIN, "MSGIN"},
+ {PHASE_UNKNOWN, "UNKNOWN"}
+};
+
+#ifdef NDEBUG
+static struct {
+ unsigned char mask;
+ const char *name;
+} signals[] = {
+ {SR_DBP, "PARITY"},
+ {SR_RST, "RST"},
+ {SR_BSY, "BSY"},
+ {SR_REQ, "REQ"},
+ {SR_MSG, "MSG"},
+ {SR_CD, "CD"},
+ {SR_IO, "IO"},
+ {SR_SEL, "SEL"},
+ {0, NULL}
+},
+basrs[] = {
+ {BASR_ATN, "ATN"},
+ {BASR_ACK, "ACK"},
+ {0, NULL}
+},
+icrs[] = {
+ {ICR_ASSERT_RST, "ASSERT RST"},
+ {ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"},
+ {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"},
+ {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}
+},
+mrs[] = {
+ {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"},
+ {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"},
+ {MR_ENABLE_PAR_INTR, "MODE PARITY INTR"},
+ {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+ {MR_DMA_MODE, "MODE DMA"},
+ {MR_ARBITRATE, "MODE ARBITRATION"},
+ {0, NULL}
+};
+
+/**
+ * NCR5380_print - print scsi bus signals
+ * @instance: adapter state to dump
+ *
+ * Print the SCSI bus signals for debugging purposes
+ *
+ * Locks: caller holds hostdata lock (not essential)
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance)
+{
+ NCR5380_local_declare();
+ unsigned char status, data, basr, mr, icr, i;
+ NCR5380_setup(instance);
+
+ data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+ status = NCR5380_read(STATUS_REG);
+ mr = NCR5380_read(MODE_REG);
+ icr = NCR5380_read(INITIATOR_COMMAND_REG);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+
+ printk("STATUS_REG: %02x ", status);
+ for (i = 0; signals[i].mask; ++i)
+ if (status & signals[i].mask)
+ printk(",%s", signals[i].name);
+ printk("\nBASR: %02x ", basr);
+ for (i = 0; basrs[i].mask; ++i)
+ if (basr & basrs[i].mask)
+ printk(",%s", basrs[i].name);
+ printk("\nICR: %02x ", icr);
+ for (i = 0; icrs[i].mask; ++i)
+ if (icr & icrs[i].mask)
+ printk(",%s", icrs[i].name);
+ printk("\nMODE: %02x ", mr);
+ for (i = 0; mrs[i].mask; ++i)
+ if (mr & mrs[i].mask)
+ printk(",%s", mrs[i].name);
+ printk("\n");
+}
+
+
+/*
+ * NCR5380_print_phase - show SCSI phase
+ * @instance: adapter to dump
+ *
+ * Print the current SCSI phase for debugging purposes
+ *
+ * Locks: none
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance)
+{
+ NCR5380_local_declare();
+ unsigned char status;
+ int i;
+ NCR5380_setup(instance);
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ printk("scsi%d : REQ not asserted, phase unknown.\n", instance->host_no);
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i);
+ printk("scsi%d : phase %s\n", instance->host_no, phases[i].name);
+ }
+}
+#endif
+
+/*
+ * These need tweaking, and would probably work best as per-device
+ * flags initialized differently for disk, tape, cd, etc devices.
+ * People with broken devices are free to experiment as to what gives
+ * the best results for them.
+ *
+ * USLEEP_SLEEP should be a minimum seek time.
+ *
+ * USLEEP_POLL should be a maximum rotational latency.
+ */
+#ifndef USLEEP_SLEEP
+/* 20 ms (reasonable hard disk speed) */
+#define USLEEP_SLEEP (20*HZ/1000)
+#endif
+/* 300 RPM (floppy speed) */
+#ifndef USLEEP_POLL
+#define USLEEP_POLL (200*HZ/1000)
+#endif
+#ifndef USLEEP_WAITLONG
+/* RvC: (reasonable time to wait on select error) */
+#define USLEEP_WAITLONG USLEEP_SLEEP
+#endif
+
+/*
+ * Function : int should_disconnect (unsigned char cmd)
+ *
+ * Purpose : decide weather a command would normally disconnect or
+ * not, since if it won't disconnect we should go to sleep.
+ *
+ * Input : cmd - opcode of SCSI command
+ *
+ * Returns : DISCONNECT_LONG if we should disconnect for a really long
+ * time (ie always, sleep, look for REQ active, sleep),
+ * DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
+ * time-to-data delay, DISCONNECT_NONE if this command would return
+ * immediately.
+ *
+ * Future sleep algorithms based on time to data can exploit
+ * something like this so they can differentiate between "normal"
+ * (ie, read, write, seek) and unusual commands (ie, * format).
+ *
+ * Note : We don't deal with commands that handle an immediate disconnect,
+ *
+ */
+
+static int should_disconnect(unsigned char cmd)
+{
+ switch (cmd) {
+ case READ_6:
+ case WRITE_6:
+ case SEEK_6:
+ case READ_10:
+ case WRITE_10:
+ case SEEK_10:
+ return DISCONNECT_TIME_TO_DATA;
+ case FORMAT_UNIT:
+ case SEARCH_HIGH:
+ case SEARCH_LOW:
+ case SEARCH_EQUAL:
+ return DISCONNECT_LONG;
+ default:
+ return DISCONNECT_NONE;
+ }
+}
+
+static void NCR5380_set_timer(struct NCR5380_hostdata *hostdata, unsigned long timeout)
+{
+ hostdata->time_expires = jiffies + timeout;
+ schedule_delayed_work(&hostdata->coroutine, timeout);
+}
+
+
+static int probe_irq __initdata = 0;
+
+/**
+ * probe_intr - helper for IRQ autoprobe
+ * @irq: interrupt number
+ * @dev_id: unused
+ * @regs: unused
+ *
+ * Set a flag to indicate the IRQ in question was received. This is
+ * used by the IRQ probe code.
+ */
+
+static irqreturn_t __init probe_intr(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ probe_irq = irq;
+ return IRQ_HANDLED;
+}
+
+/**
+ * NCR5380_probe_irq - find the IRQ of an NCR5380
+ * @instance: NCR5380 controller
+ * @possible: bitmask of ISA IRQ lines
+ *
+ * Autoprobe for the IRQ line used by the NCR5380 by triggering an IRQ
+ * and then looking to see what interrupt actually turned up.
+ *
+ * Locks: none, irqs must be enabled on entry
+ */
+
+static int __init NCR5380_probe_irq(struct Scsi_Host *instance, int possible)
+{
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ unsigned long timeout;
+ int trying_irqs, i, mask;
+ NCR5380_setup(instance);
+
+ for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe", NULL) == 0))
+ trying_irqs |= mask;
+
+ timeout = jiffies + (250 * HZ / 1000);
+ probe_irq = SCSI_IRQ_NONE;
+
+ /*
+ * A interrupt is triggered whenever BSY = false, SEL = true
+ * and a bit set in the SELECT_ENABLE_REG is asserted on the
+ * SCSI bus.
+ *
+ * Note that the bus is only driven when the phase control signals
+ * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
+ * to zero.
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL);
+
+ while (probe_irq == SCSI_IRQ_NONE && time_before(jiffies, timeout))
+ {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ if (trying_irqs & mask)
+ free_irq(i, NULL);
+
+ return probe_irq;
+}
+
+/**
+ * NCR58380_print_options - show options
+ * @instance: unused for now
+ *
+ * Called by probe code indicating the NCR5380 driver options that
+ * were selected. At some point this will switch to runtime options
+ * read from the adapter in question
+ *
+ * Locks: none
+ */
+
+static void __init NCR5380_print_options(struct Scsi_Host *instance)
+{
+ printk(" generic options"
+#ifdef AUTOPROBE_IRQ
+ " AUTOPROBE_IRQ"
+#endif
+#ifdef AUTOSENSE
+ " AUTOSENSE"
+#endif
+#ifdef DIFFERENTIAL
+ " DIFFERENTIAL"
+#endif
+#ifdef REAL_DMA
+ " REAL DMA"
+#endif
+#ifdef REAL_DMA_POLL
+ " REAL DMA POLL"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef PSEUDO_DMA
+ " PSEUDO DMA"
+#endif
+#ifdef UNSAFE
+ " UNSAFE "
+#endif
+ );
+ printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
+ printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+ if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) {
+ printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
+ }
+}
+
+/**
+ * NCR5380_print_status - dump controller info
+ * @instance: controller to dump
+ *
+ * Print commands in the various queues, called from NCR5380_abort
+ * and NCR5380_debug to aid debugging.
+ *
+ * Locks: called functions disable irqs
+ */
+
+static void NCR5380_print_status(struct Scsi_Host *instance)
+{
+ NCR5380_dprint(NDEBUG_ANY, instance);
+ NCR5380_dprint_phase(NDEBUG_ANY, instance);
+}
+
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+ */
+
+#undef SPRINTF
+#define SPRINTF(args...) do { if(pos < buffer + length-80) pos += sprintf(pos, ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length);
+static
+char *lprint_command(unsigned char *cmd, char *pos, char *buffer, int len);
+static
+char *lprint_opcode(int opcode, char *pos, char *buffer, int length);
+
+static
+int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ char *pos = buffer;
+ struct NCR5380_hostdata *hostdata;
+ Scsi_Cmnd *ptr;
+
+ hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+
+ if (inout) { /* Has data been written to the file ? */
+#ifdef DTC_PUBLIC_RELEASE
+ dtc_wmaxi = dtc_maxi = 0;
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+ pas_wmaxi = pas_maxi = 0;
+#endif
+ return (-ENOSYS); /* Currently this is a no-op */
+ }
+ SPRINTF("NCR5380 core release=%d. ", NCR5380_PUBLIC_RELEASE);
+ if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400)
+ SPRINTF("ncr53c400 release=%d. ", NCR53C400_PUBLIC_RELEASE);
+#ifdef DTC_PUBLIC_RELEASE
+ SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE);
+#endif
+#ifdef T128_PUBLIC_RELEASE
+ SPRINTF("T128 release %d", T128_PUBLIC_RELEASE);
+#endif
+#ifdef GENERIC_NCR5380_PUBLIC_RELEASE
+ SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE);
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+ SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE);
+#endif
+
+ SPRINTF("\nBase Addr: 0x%05lX ", (long) instance->base);
+ SPRINTF("io_port: %04x ", (int) instance->io_port);
+ if (instance->irq == SCSI_IRQ_NONE)
+ SPRINTF("IRQ: None.\n");
+ else
+ SPRINTF("IRQ: %d.\n", instance->irq);
+
+#ifdef DTC_PUBLIC_RELEASE
+ SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", dtc_wmaxi, dtc_maxi);
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+ SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi);
+#endif
+ spin_lock_irq(instance->host_lock);
+ if (!hostdata->connected)
+ SPRINTF("scsi%d: no currently connected command\n", instance->host_no);
+ else
+ pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, pos, buffer, length);
+ SPRINTF("scsi%d: issue_queue\n", instance->host_no);
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
+
+ SPRINTF("scsi%d: disconnected_queue\n", instance->host_no);
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
+ spin_unlock_irq(instance->host_lock);
+
+ *start = buffer;
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ return length;
+}
+
+static char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length)
+{
+ SPRINTF("scsi%d : destination target %d, lun %d\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun);
+ SPRINTF(" command = ");
+ pos = lprint_command(cmd->cmnd, pos, buffer, length);
+ return (pos);
+}
+
+static char *lprint_command(unsigned char *command, char *pos, char *buffer, int length)
+{
+ int i, s;
+ pos = lprint_opcode(command[0], pos, buffer, length);
+ for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ SPRINTF("%02x ", command[i]);
+ SPRINTF("\n");
+ return (pos);
+}
+
+static char *lprint_opcode(int opcode, char *pos, char *buffer, int length)
+{
+ SPRINTF("%2d (0x%02x)", opcode, opcode);
+ return (pos);
+}
+
+
+/**
+ * NCR5380_init - initialise an NCR5380
+ * @instance: adapter to configure
+ * @flags: control flags
+ *
+ * Initializes *instance and corresponding 5380 chip,
+ * with flags OR'd into the initial flags value.
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
+ *
+ * Returns 0 for success
+ *
+ * Locks: interrupts must be enabled when we are called
+ */
+
+static int __devinit NCR5380_init(struct Scsi_Host *instance, int flags)
+{
+ NCR5380_local_declare();
+ int i, pass;
+ unsigned long timeout;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+
+ if(in_interrupt())
+ printk(KERN_ERR "NCR5380_init called with interrupts off!\n");
+ /*
+ * On NCR53C400 boards, NCR5380 registers are mapped 8 past
+ * the base address.
+ */
+
+#ifdef NCR53C400
+ if (flags & FLAG_NCR53C400)
+ instance->NCR5380_instance_name += NCR53C400_address_adjust;
+#endif
+
+ NCR5380_setup(instance);
+
+ hostdata->aborted = 0;
+ hostdata->id_mask = 1 << instance->this_id;
+ for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+ if (i > hostdata->id_mask)
+ hostdata->id_higher_mask |= i;
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dmalen = 0;
+#endif
+ hostdata->targets_present = 0;
+ hostdata->connected = NULL;
+ hostdata->issue_queue = NULL;
+ hostdata->disconnected_queue = NULL;
+
+ INIT_WORK(&hostdata->coroutine, NCR5380_main, hostdata);
+
+#ifdef NCR5380_STATS
+ for (i = 0; i < 8; ++i) {
+ hostdata->time_read[i] = 0;
+ hostdata->time_write[i] = 0;
+ hostdata->bytes_read[i] = 0;
+ hostdata->bytes_write[i] = 0;
+ }
+ hostdata->timebase = 0;
+ hostdata->pendingw = 0;
+ hostdata->pendingr = 0;
+#endif
+
+ /* The CHECK code seems to break the 53C400. Will check it later maybe */
+ if (flags & FLAG_NCR53C400)
+ hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags;
+ else
+ hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
+
+ hostdata->host = instance;
+ hostdata->time_expires = 0;
+
+#ifndef AUTOSENSE
+ if ((instance->cmd_per_lun > 1) || instance->can_queue > 1)
+ printk(KERN_WARNING "scsi%d : WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n"
+ " be incorrectly cleared.\n", instance->host_no);
+#endif /* def AUTOSENSE */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+#ifdef NCR53C400
+ if (hostdata->flags & FLAG_NCR53C400) {
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ }
+#endif
+
+ /*
+ * Detect and correct bus wedge problems.
+ *
+ * If the system crashed, it may have crashed in a state
+ * where a SCSI command was still executing, and the
+ * SCSI bus is not in a BUS FREE STATE.
+ *
+ * If this is the case, we'll try to abort the currently
+ * established nexus which we know nothing about, and that
+ * failing, do a hard reset of the SCSI bus
+ */
+
+ for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) && pass <= 6; ++pass) {
+ switch (pass) {
+ case 1:
+ case 3:
+ case 5:
+ printk(KERN_INFO "scsi%d: SCSI bus busy, waiting up to five seconds\n", instance->host_no);
+ timeout = jiffies + 5 * HZ;
+ NCR5380_poll_politely(instance, STATUS_REG, SR_BSY, 0, 5*HZ);
+ break;
+ case 2:
+ printk(KERN_WARNING "scsi%d: bus busy, attempting abort\n", instance->host_no);
+ do_abort(instance);
+ break;
+ case 4:
+ printk(KERN_WARNING "scsi%d: bus busy, attempting reset\n", instance->host_no);
+ do_reset(instance);
+ break;
+ case 6:
+ printk(KERN_ERR "scsi%d: bus locked solid or invalid override\n", instance->host_no);
+ return -ENXIO;
+ }
+ }
+ return 0;
+}
+
+/**
+ * NCR5380_exit - remove an NCR5380
+ * @instance: adapter to remove
+ */
+
+static void __devexit NCR5380_exit(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+
+ cancel_delayed_work(&hostdata->coroutine);
+ flush_scheduled_work();
+}
+
+/**
+ * NCR5380_queue_command - queue a command
+ * @cmd: SCSI command
+ * @done: completion handler
+ *
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
+ *
+ * Locks: host lock taken by caller
+ */
+
+static int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ Scsi_Cmnd *tmp;
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", instance->host_no);
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ return 0;
+ }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+#ifdef NCR5380_STATS
+ switch (cmd->cmnd[0]) {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_write[cmd->device->id] += cmd->request_bufflen;
+ hostdata->pendingw++;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_read[cmd->device->id] += cmd->request_bufflen;
+ hostdata->pendingr++;
+ break;
+ }
+#endif
+
+ /*
+ * We use the host_scribble field as a pointer to the next command
+ * in a queue
+ */
+
+ cmd->host_scribble = NULL;
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);
+ LIST(cmd, tmp);
+ tmp->host_scribble = (unsigned char *) cmd;
+ }
+ dprintk(NDEBUG_QUEUES, ("scsi%d : command added to %s of queue\n", instance->host_no, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
+
+ /* Run the coroutine if it isn't already running. */
+ /* Kick off command processing */
+ schedule_work(&hostdata->coroutine);
+ return 0;
+}
+
+
+/**
+ * NCR5380_main - NCR state machines
+ *
+ * NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
+ *
+ * Locks: called as its own thread with no locks held. Takes the
+ * host lock and called routines may take the isa dma lock.
+ */
+
+static void NCR5380_main(void *p)
+{
+ struct NCR5380_hostdata *hostdata = p;
+ struct Scsi_Host *instance = hostdata->host;
+ Scsi_Cmnd *tmp, *prev;
+ int done;
+
+ spin_lock_irq(instance->host_lock);
+ do {
+ /* Lock held here */
+ done = 1;
+ if (!hostdata->connected && !hostdata->selecting) {
+ dprintk(NDEBUG_MAIN, ("scsi%d : not connected\n", instance->host_no));
+ /*
+ * Search through the issue_queue for a command destined
+ * for a target that's not busy.
+ */
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ {
+ if (prev != tmp)
+ dprintk(NDEBUG_LISTS, ("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun));
+ /* When we find one, remove it from the issue queue. */
+ if (!(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))) {
+ if (prev) {
+ REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble);
+ prev->host_scribble = tmp->host_scribble;
+ } else {
+ REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
+ hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+ tmp->host_scribble = NULL;
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+ dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main() : command for target %d lun %d removed from issue_queue\n", instance->host_no, tmp->target, tmp->lun));
+
+ /*
+ * A successful selection is defined as one that
+ * leaves us with the command connected and
+ * in hostdata->connected, OR has terminated the
+ * command.
+ *
+ * With successful commands, we fall through
+ * and see if we can do an information transfer,
+ * with failures we will restart.
+ */
+ hostdata->selecting = NULL;
+ /* RvC: have to preset this to indicate a new command is being performed */
+
+ if (!NCR5380_select(instance, tmp,
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+ (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) {
+ break;
+ } else {
+ LIST(tmp, hostdata->issue_queue);
+ tmp->host_scribble = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+ done = 0;
+ dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main(): select() failed, returned to issue_queue\n", instance->host_no));
+ }
+ /* lock held here still */
+ } /* if target/lun is not busy */
+ } /* for */
+ /* exited locked */
+ } /* if (!hostdata->connected) */
+ if (hostdata->selecting) {
+ tmp = (Scsi_Cmnd *) hostdata->selecting;
+ /* Selection will drop and retake the lock */
+ if (!NCR5380_select(instance, tmp, (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) {
+ /* Ok ?? */
+ } else {
+ /* RvC: device failed, so we wait a long time
+ this is needed for Mustek scanners, that
+ do not respond to commands immediately
+ after a scan */
+ printk(KERN_DEBUG "scsi%d: device %d did not respond in time\n", instance->host_no, tmp->device->id);
+ LIST(tmp, hostdata->issue_queue);
+ tmp->host_scribble = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+ NCR5380_set_timer(hostdata, USLEEP_WAITLONG);
+ }
+ } /* if hostdata->selecting */
+ if (hostdata->connected
+#ifdef REAL_DMA
+ && !hostdata->dmalen
+#endif
+ && (!hostdata->time_expires || time_before_eq(hostdata->time_expires, jiffies))
+ ) {
+ dprintk(NDEBUG_MAIN, ("scsi%d : main() : performing information transfer\n", instance->host_no));
+ NCR5380_information_transfer(instance);
+ dprintk(NDEBUG_MAIN, ("scsi%d : main() : done set false\n", instance->host_no));
+ done = 0;
+ } else
+ break;
+ } while (!done);
+
+ spin_unlock_irq(instance->host_lock);
+}
+
+#ifndef DONT_USE_INTR
+
+/**
+ * NCR5380_intr - generic NCR5380 irq handler
+ * @irq: interrupt number
+ * @dev_id: device info
+ * @regs: registers (unused)
+ *
+ * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * Locks: takes the needed instance locks
+ */
+
+static irqreturn_t NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ NCR5380_local_declare();
+ struct Scsi_Host *instance = (struct Scsi_Host *)dev_id;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ int done;
+ unsigned char basr;
+ unsigned long flags;
+
+ dprintk(NDEBUG_INTR, ("scsi : NCR5380 irq %d triggered\n", irq));
+
+ do {
+ done = 1;
+ spin_lock_irqsave(instance->host_lock, flags);
+ /* Look for pending interrupts */
+ NCR5380_setup(instance);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ /* XXX dispatch to appropriate routine if found and done=0 */
+ if (basr & BASR_IRQ) {
+ NCR5380_dprint(NDEBUG_INTR, instance);
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
+ done = 0;
+ dprintk(NDEBUG_INTR, ("scsi%d : SEL interrupt\n", instance->host_no));
+ NCR5380_reselect(instance);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else if (basr & BASR_PARITY_ERROR) {
+ dprintk(NDEBUG_INTR, ("scsi%d : PARITY interrupt\n", instance->host_no));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+ dprintk(NDEBUG_INTR, ("scsi%d : RESET interrupt\n", instance->host_no));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else {
+#if defined(REAL_DMA)
+ /*
+ * We should only get PHASE MISMATCH and EOP interrupts
+ * if we have DMA enabled, so do a sanity check based on
+ * the current setting of the MODE register.
+ */
+
+ if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr & BASR_END_DMA_TRANSFER) || !(basr & BASR_PHASE_MATCH))) {
+ int transfered;
+
+ if (!hostdata->connected)
+ panic("scsi%d : received end of DMA interrupt with no connected cmd\n", instance->hostno);
+
+ transfered = (hostdata->dmalen - NCR5380_dma_residual(instance));
+ hostdata->connected->SCp.this_residual -= transferred;
+ hostdata->connected->SCp.ptr += transferred;
+ hostdata->dmalen = 0;
+
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+
+ /* FIXME: we need to poll briefly then defer a workqueue task ! */
+ NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG, BASR_ACK, 0, 2*HZ);
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+#else
+ dprintk(NDEBUG_INTR, ("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#endif
+ }
+ } /* if BASR_IRQ */
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ if(!done)
+ schedule_work(&hostdata->coroutine);
+ } while (!done);
+ return IRQ_HANDLED;
+}
+
+#endif
+
+/**
+ * collect_stats - collect stats on a scsi command
+ * @hostdata: adapter
+ * @cmd: command being issued
+ *
+ * Update the statistical data by parsing the command in question
+ */
+
+static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd)
+{
+#ifdef NCR5380_STATS
+ switch (cmd->cmnd[0]) {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase);
+ hostdata->pendingw--;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase);
+ hostdata->pendingr--;
+ break;
+ }
+#endif
+}
+
+
+/*
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ * int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ * including ARBITRATION, SELECTION, and initial message out for
+ * IDENTIFY and queue messages.
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this
+ * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
+ * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
+ * the command that is presently connected.
+ *
+ * Returns : -1 if selection could not execute for some reason,
+ * 0 if selection succeeded or failed because the target
+ * did not respond.
+ *
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
+ *
+ * If successful : I_T_L or I_T_L_Q nexus will be established,
+ * instance->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
+ *
+ * If failed (no target) : cmd->scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
+ *
+ * Locks: caller holds hostdata lock in IRQ mode
+ */
+
+static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag)
+{
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ unsigned char tmp[3], phase;
+ unsigned char *data;
+ int len;
+ unsigned long timeout;
+ unsigned char value;
+ int err;
+ NCR5380_setup(instance);
+
+ if (hostdata->selecting)
+ goto part2;
+
+ hostdata->restart_select = 0;
+
+ NCR5380_dprint(NDEBUG_ARBITRATION, instance);
+ dprintk(NDEBUG_ARBITRATION, ("scsi%d : starting arbitration, id = %d\n", instance->host_no, instance->this_id));
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION.
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /*
+ * Start arbitration.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+
+ /* We can be relaxed here, interrupts are on, we are
+ in workqueue context, the birds are singing in the trees */
+ spin_unlock_irq(instance->host_lock);
+ err = NCR5380_poll_politely(instance, INITIATOR_COMMAND_REG, ICR_ARBITRATION_PROGRESS, ICR_ARBITRATION_PROGRESS, 5*HZ);
+ spin_lock_irq(instance->host_lock);
+ if (err < 0) {
+ printk(KERN_DEBUG "scsi: arbitration timeout at %d\n", __LINE__);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ goto failed;
+ }
+
+ dprintk(NDEBUG_ARBITRATION, ("scsi%d : arbitration complete\n", instance->host_no));
+
+ /*
+ * The arbitration delay is 2.2us, but this is a minimum and there is
+ * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+ * the integral nature of udelay().
+ *
+ */
+
+ udelay(3);
+
+ /* Check for lost arbitration */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ dprintk(NDEBUG_ARBITRATION, ("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n", instance->host_no));
+ goto failed;
+ }
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
+
+ if (!(hostdata->flags & FLAG_DTC3181E) &&
+ /* RvC: DTC3181E has some trouble with this
+ * so we simply removed it. Seems to work with
+ * only Mustek scanner attached
+ */
+ (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ dprintk(NDEBUG_ARBITRATION, ("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n", instance->host_no));
+ goto failed;
+ }
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
+ * a minimum so we'll udelay ceil(1.2)
+ */
+
+ udelay(2);
+
+ dprintk(NDEBUG_ARBITRATION, ("scsi%d : won arbitration\n", instance->host_no));
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
+ * the host and target ID's on the SCSI bus.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id)));
+
+ /*
+ * Raise ATN while SEL is true before BSY goes false from arbitration,
+ * since this is the only way to guarantee that we'll get a MESSAGE OUT
+ * phase immediately after selection.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+ NCR5380_write(MODE_REG, MR_BASE);
+
+ /*
+ * Reselect interrupts must be turned off prior to the dropping of BSY,
+ * otherwise we will trigger an interrupt.
+ */
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /*
+ * The initiator shall then wait at least two deskew delays and release
+ * the BSY signal.
+ */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+
+ /* Reset BSY */
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+ /*
+ * Something weird happens when we cease to drive BSY - looks
+ * like the board/chip is letting us do another read before the
+ * appropriate propagation delay has expired, and we're confusing
+ * a BSY signal from ourselves as the target's response to SELECTION.
+ *
+ * A small delay (the 'C++' frontend breaks the pipeline with an
+ * unnecessary jump, making it work on my 386-33/Trantor T128, the
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
+ * work there.
+ *
+ * wingel suggests that this could be due to failing to wait
+ * one deskew delay.
+ */
+
+ udelay(1);
+
+ dprintk(NDEBUG_SELECTION, ("scsi%d : selecting target %d\n", instance->host_no, cmd->device->id));
+
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
+ * selection.
+ */
+
+ timeout = jiffies + (250 * HZ / 1000);
+
+ /*
+ * XXX very interesting - we're seeing a bounce where the BSY we
+ * asserted is being reflected / still asserted (propagation delay?)
+ * and it's detecting as true. Sigh.
+ */
+
+ hostdata->select_time = 0; /* we count the clock ticks at which we polled */
+ hostdata->selecting = cmd;
+
+part2:
+ /* RvC: here we enter after a sleeping period, or immediately after
+ execution of part 1
+ we poll only once ech clock tick */
+ value = NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO);
+
+ if (!value && (hostdata->select_time < HZ/4)) {
+ /* RvC: we still must wait for a device response */
+ hostdata->select_time++; /* after 25 ticks the device has failed */
+ NCR5380_set_timer(hostdata, 1);
+ return 0; /* RvC: we return here with hostdata->selecting set,
+ to go to sleep */
+ }
+
+ hostdata->selecting = NULL;/* clear this pointer, because we passed the
+ waiting period */
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_reselect(instance);
+ printk("scsi%d : reselection after won arbitration?\n", instance->host_no);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
+ * change the DATA BUS. -wingel
+ */
+
+ udelay(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ if (hostdata->targets_present & (1 << cmd->device->id)) {
+ printk(KERN_DEBUG "scsi%d : weirdness\n", instance->host_no);
+ if (hostdata->restart_select)
+ printk(KERN_DEBUG "\trestart select\n");
+ NCR5380_dprint(NDEBUG_SELECTION, instance);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ cmd->result = DID_BAD_TARGET << 16;
+ collect_stats(hostdata, cmd);
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ dprintk(NDEBUG_SELECTION, ("scsi%d : target did not respond within 250ms\n", instance->host_no));
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return 0;
+ }
+ hostdata->targets_present |= (1 << cmd->device->id);
+
+ /*
+ * Since we followed the SCSI spec, and raised ATN while SEL
+ * was true but before BSY was false during selection, the information
+ * transfer phase should be a MESSAGE OUT phase so that we can send the
+ * IDENTIFY message.
+ *
+ * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+ * message (2 bytes) with a tag ID that we increment with every command
+ * until it wraps back to 0.
+ *
+ * XXX - it turns out that there are some broken SCSI-II devices,
+ * which claim to support tagged queuing but fail when more than
+ * some number of commands are issued at once.
+ */
+
+ /* Wait for start of REQ/ACK handshake */
+
+ spin_unlock_irq(instance->host_lock);
+ err = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, HZ);
+ spin_lock_irq(instance->host_lock);
+
+ if(err) {
+ printk(KERN_ERR "scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ goto failed;
+ }
+
+ dprintk(NDEBUG_SELECTION, ("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id));
+ tmp[0] = IDENTIFY(((instance->irq == SCSI_IRQ_NONE) ? 0 : 1), cmd->device->lun);
+
+ len = 1;
+ cmd->tag = 0;
+
+ /* Send message(s) */
+ data = tmp;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ dprintk(NDEBUG_SELECTION, ("scsi%d : nexus established.\n", instance->host_no));
+ /* XXX need to handle errors here */
+ hostdata->connected = cmd;
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+
+ if (cmd->SCp.ptr != (char *)cmd->sense_buffer) {
+ initialize_SCp(cmd);
+ }
+
+ return 0;
+
+ /* Selection failed */
+failed:
+ return -1;
+
+}
+
+/*
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data) {
+ NCR5380_local_declare();
+ unsigned char p = *phase, tmp;
+ int c = *count;
+ unsigned char *d = *data;
+ /*
+ * RvC: some administrative data to process polling time
+ */
+ int break_allowed = 0;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ NCR5380_setup(instance);
+
+ if (!(p & SR_IO))
+ dprintk(NDEBUG_PIO, ("scsi%d : pio write %d bytes\n", instance->host_no, c));
+ else
+ dprintk(NDEBUG_PIO, ("scsi%d : pio read %d bytes\n", instance->host_no, c));
+
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
+ * phase specified in the appropriate bits of the TARGET COMMAND
+ * REGISTER match the STATUS REGISTER
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+ /* RvC: don't know if this is necessary, but other SCSI I/O is short
+ * so breaks are not necessary there
+ */
+ if ((p == PHASE_DATAIN) || (p == PHASE_DATAOUT)) {
+ break_allowed = 1;
+ }
+ do {
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
+ */
+
+ /* RvC: we simply poll once, after that we stop temporarily
+ * and let the device buffer fill up
+ * if breaking is not allowed, we keep polling as long as needed
+ */
+
+ /* FIXME */
+ while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) && !break_allowed);
+ if (!(tmp & SR_REQ)) {
+ /* timeout condition */
+ NCR5380_set_timer(hostdata, USLEEP_SLEEP);
+ break;
+ }
+
+ dprintk(NDEBUG_HANDSHAKE, ("scsi%d : REQ detected\n", instance->host_no));
+
+ /* Check for phase mismatch */
+ if ((tmp & PHASE_MASK) != p) {
+ dprintk(NDEBUG_HANDSHAKE, ("scsi%d : phase mismatch\n", instance->host_no));
+ NCR5380_dprint_phase(NDEBUG_HANDSHAKE, instance);
+ break;
+ }
+ /* Do actual transfer from SCSI bus to / from memory */
+ if (!(p & SR_IO))
+ NCR5380_write(OUTPUT_DATA_REG, *d);
+ else
+ *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+ ++d;
+
+ /*
+ * The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ */
+
+ if (!(p & SR_IO)) {
+ if (!((p & SR_MSG) && c > 1)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
+ NCR5380_dprint(NDEBUG_PIO, instance);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ } else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+ NCR5380_dprint(NDEBUG_PIO, instance);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ }
+ } else {
+ NCR5380_dprint(NDEBUG_PIO, instance);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ }
+
+ /* FIXME - if this fails bus reset ?? */
+ NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, 0, 5*HZ);
+ dprintk(NDEBUG_HANDSHAKE, ("scsi%d : req false, handshake complete\n", instance->host_no));
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking :
+ * 1. We were in MSGOUT phase, and we are on the last byte of the
+ * message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in a MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear and the target may proceed as normal.
+ */
+ if (!(p == PHASE_MSGIN && c == 1)) {
+ if (p == PHASE_MSGOUT && c > 1)
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ else
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+ } while (--c);
+
+ dprintk(NDEBUG_PIO, ("scsi%d : residual %d\n", instance->host_no, c));
+
+ *count = c;
+ *data = d;
+ tmp = NCR5380_read(STATUS_REG);
+ if (tmp & SR_REQ)
+ *phase = tmp & PHASE_MASK;
+ else
+ *phase = PHASE_UNKNOWN;
+
+ if (!c || (*phase == p))
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ * do_reset - issue a reset command
+ * @host: adapter to reset
+ *
+ * Issue a reset sequence to the NCR5380 and try and get the bus
+ * back into sane shape.
+ *
+ * Locks: caller holds queue lock
+ */
+
+static void do_reset(struct Scsi_Host *host) {
+ NCR5380_local_declare();
+ NCR5380_setup(host);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
+ udelay(25);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ *
+ * Purpose : abort the currently established nexus. Should only be
+ * called from a routine which can drop into a
+ *
+ * Returns : 0 on success, -1 on failure.
+ *
+ * Locks: queue lock held by caller
+ * FIXME: sort this out and get new_eh running
+ */
+
+static int do_abort(struct Scsi_Host *host) {
+ NCR5380_local_declare();
+ unsigned char *msgptr, phase, tmp;
+ int len;
+ int rc;
+ NCR5380_setup(host);
+
+
+ /* Request message out phase */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
+ * other phase and will have to source/sink data.
+ *
+ * We really don't care what value was on the bus or what value
+ * the target sees, so we just handshake.
+ */
+
+ rc = NCR5380_poll_politely(host, STATUS_REG, SR_REQ, SR_REQ, 60 * HZ);
+
+ if(rc < 0)
+ return -1;
+
+ tmp = (unsigned char)rc;
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ rc = NCR5380_poll_politely(host, STATUS_REG, SR_REQ, 0, 3*HZ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ if(rc == -1)
+ return -1;
+ }
+ tmp = ABORT;
+ msgptr = &tmp;
+ len = 1;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(host, &phase, &len, &msgptr);
+
+ /*
+ * If we got here, and the command completed successfully,
+ * we're about to go into bus free state.
+ */
+
+ return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ * or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * Locks: io_request lock held by caller
+ */
+
+
+static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data) {
+ NCR5380_local_declare();
+ register int c = *count;
+ register unsigned char p = *phase;
+ register unsigned char *d = *data;
+ unsigned char tmp;
+ int foo;
+#if defined(REAL_DMA_POLL)
+ int cnt, toPIO;
+ unsigned char saved_data = 0, overrun = 0, residue;
+#endif
+
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+
+ NCR5380_setup(instance);
+
+ if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
+ *phase = tmp;
+ return -1;
+ }
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+#ifdef READ_OVERRUNS
+ if (p & SR_IO) {
+ c -= 2;
+ }
+#endif
+ dprintk(NDEBUG_DMA, ("scsi%d : initializing DMA channel %d for %s, %d bytes %s %0x\n", instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" : "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d));
+ hostdata->dma_len = (p & SR_IO) ? NCR5380_dma_read_setup(instance, d, c) : NCR5380_dma_write_setup(instance, d, c);
+#endif
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+#ifdef REAL_DMA
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
+#elif defined(REAL_DMA_POLL)
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#else
+ /*
+ * Note : on my sample board, watch-dog timeouts occurred when interrupts
+ * were not disabled for the duration of a single DMA transfer, from
+ * before the setting of DMA mode to after transfer of the last byte.
+ */
+
+#if defined(PSEUDO_DMA) && defined(UNSAFE)
+ spin_unlock_irq(instance->host_lock);
+#endif
+ /* KLL May need eop and parity in 53c400 */
+ if (hostdata->flags & FLAG_NCR53C400)
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE | MR_MONITOR_BSY);
+ else
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#endif /* def REAL_DMA */
+
+ dprintk(NDEBUG_DMA, ("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG)));
+
+ /*
+ * On the PAS16 at least I/O recovery delays are not needed here.
+ * Everyone else seems to want them.
+ */
+
+ if (p & SR_IO) {
+ io_recovery_delay(1);
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ } else {
+ io_recovery_delay(1);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
+ io_recovery_delay(1);
+ NCR5380_write(START_DMA_SEND_REG, 0);
+ io_recovery_delay(1);
+ }
+
+#if defined(REAL_DMA_POLL)
+ do {
+ tmp = NCR5380_read(BUS_AND_STATUS_REG);
+ } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR | BASR_END_DMA_TRANSFER)));
+
+/*
+ At this point, either we've completed DMA, or we have a phase mismatch,
+ or we've unexpectedly lost BUSY (which is a real error).
+
+ For write DMAs, we want to wait until the last byte has been
+ transferred out over the bus before we turn off DMA mode. Alas, there
+ seems to be no terribly good way of doing this on a 5380 under all
+ conditions. For non-scatter-gather operations, we can wait until REQ
+ and ACK both go false, or until a phase mismatch occurs. Gather-writes
+ are nastier, since the device will be expecting more data than we
+ are prepared to send it, and REQ will remain asserted. On a 53C8[01] we
+ could test LAST BIT SENT to assure transfer (I imagine this is precisely
+ why this signal was added to the newer chips) but on the older 538[01]
+ this signal does not exist. The workaround for this lack is a watchdog;
+ we bail out of the wait-loop after a modest amount of wait-time if
+ the usual exit conditions are not met. Not a terribly clean or
+ correct solution :-%
+
+ Reads are equally tricky due to a nasty characteristic of the NCR5380.
+ If the chip is in DMA mode for an READ, it will respond to a target's
+ REQ by latching the SCSI data into the INPUT DATA register and asserting
+ ACK, even if it has _already_ been notified by the DMA controller that
+ the current DMA transfer has completed! If the NCR5380 is then taken
+ out of DMA mode, this already-acknowledged byte is lost.
+
+ This is not a problem for "one DMA transfer per command" reads, because
+ the situation will never arise... either all of the data is DMA'ed
+ properly, or the target switches to MESSAGE IN phase to signal a
+ disconnection (either operation bringing the DMA to a clean halt).
+ However, in order to handle scatter-reads, we must work around the
+ problem. The chosen fix is to DMA N-2 bytes, then check for the
+ condition before taking the NCR5380 out of DMA mode. One or two extra
+ bytes are transferred via PIO as necessary to fill out the original
+ request.
+ */
+
+ if (p & SR_IO) {
+#ifdef READ_OVERRUNS
+ udelay(10);
+ if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) == (BASR_PHASE_MATCH | BASR_ACK))) {
+ saved_data = NCR5380_read(INPUT_DATA_REGISTER);
+ overrun = 1;
+ }
+#endif
+ } else {
+ int limit = 100;
+ while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) || (NCR5380_read(STATUS_REG) & SR_REQ)) {
+ if (!(tmp & BASR_PHASE_MATCH))
+ break;
+ if (--limit < 0)
+ break;
+ }
+ }
+
+ dprintk(NDEBUG_DMA, ("scsi%d : polled DMA transfer complete, basr 0x%X, sr 0x%X\n", instance->host_no, tmp, NCR5380_read(STATUS_REG)));
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ residue = NCR5380_dma_residual(instance);
+ c -= residue;
+ *count -= c;
+ *data += c;
+ *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
+
+#ifdef READ_OVERRUNS
+ if (*phase == p && (p & SR_IO) && residue == 0) {
+ if (overrun) {
+ dprintk(NDEBUG_DMA, ("Got an input overrun, using saved byte\n"));
+ **data = saved_data;
+ *data += 1;
+ *count -= 1;
+ cnt = toPIO = 1;
+ } else {
+ printk("No overrun??\n");
+ cnt = toPIO = 2;
+ }
+ dprintk(NDEBUG_DMA, ("Doing %d-byte PIO to 0x%X\n", cnt, *data));
+ NCR5380_transfer_pio(instance, phase, &cnt, data);
+ *count -= toPIO - cnt;
+ }
+#endif
+
+ dprintk(NDEBUG_DMA, ("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n", *data, *count, *(*data + *count - 1), *(*data + *count)));
+ return 0;
+
+#elif defined(REAL_DMA)
+ return 0;
+#else /* defined(REAL_DMA_POLL) */
+ if (p & SR_IO) {
+#ifdef DMA_WORKS_RIGHT
+ foo = NCR5380_pread(instance, d, c);
+#else
+ int diff = 1;
+ if (hostdata->flags & FLAG_NCR53C400) {
+ diff = 0;
+ }
+ if (!(foo = NCR5380_pread(instance, d, c - diff))) {
+ /*
+ * We can't disable DMA mode after successfully transferring
+ * what we plan to be the last byte, since that would open up
+ * a race condition where if the target asserted REQ before
+ * we got the DMA mode reset, the NCR5380 would have latched
+ * an additional byte into the INPUT DATA register and we'd
+ * have dropped it.
+ *
+ * The workaround was to transfer one fewer bytes than we
+ * intended to with the pseudo-DMA read function, wait for
+ * the chip to latch the last byte, read it, and then disable
+ * pseudo-DMA mode.
+ *
+ * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
+ * REQ is deasserted when ACK is asserted, and not reasserted
+ * until ACK goes false. Since the NCR5380 won't lower ACK
+ * until DACK is asserted, which won't happen unless we twiddle
+ * the DMA port or we take the NCR5380 out of DMA mode, we
+ * can guarantee that we won't handshake another extra
+ * byte.
+ */
+
+ if (!(hostdata->flags & FLAG_NCR53C400)) {
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+ /* Wait for clean handshake */
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ d[c - 1] = NCR5380_read(INPUT_DATA_REG);
+ }
+ }
+#endif
+ } else {
+#ifdef DMA_WORKS_RIGHT
+ foo = NCR5380_pwrite(instance, d, c);
+#else
+ int timeout;
+ dprintk(NDEBUG_C400_PWRITE, ("About to pwrite %d bytes\n", c));
+ if (!(foo = NCR5380_pwrite(instance, d, c))) {
+ /*
+ * Wait for the last byte to be sent. If REQ is being asserted for
+ * the byte we're interested, we'll ACK it and it will go false.
+ */
+ if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
+ timeout = 20000;
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH));
+
+ if (!timeout)
+ dprintk(NDEBUG_LAST_BYTE_SENT, ("scsi%d : timed out on last byte\n", instance->host_no));
+
+ if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
+ hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
+ if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
+ hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
+ dprintk(NDEBUG_LAST_WRITE_SENT, ("scsi%d : last bit sent works\n", instance->host_no));
+ }
+ }
+ } else {
+ dprintk(NDEBUG_C400_PWRITE, ("Waiting for LASTBYTE\n"));
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
+ dprintk(NDEBUG_C400_PWRITE, ("Got LASTBYTE\n"));
+ }
+ }
+#endif
+ }
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) {
+ dprintk(NDEBUG_C400_PWRITE, ("53C400w: Checking for IRQ\n"));
+ if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) {
+ dprintk(NDEBUG_C400_PWRITE, ("53C400w: got it, reading reset interrupt reg\n"));
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else {
+ printk("53C400w: IRQ NOT THERE!\n");
+ }
+ }
+ *data = d + c;
+ *count = 0;
+ *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
+#if defined(PSEUDO_DMA) && defined(UNSAFE)
+ spin_lock_irq(instance->host_lock);
+#endif /* defined(REAL_DMA_POLL) */
+ return foo;
+#endif /* def REAL_DMA */
+}
+#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here
+ * to recover from an unexpected bus free condition.
+ *
+ * Locks: io_request_lock held by caller in IRQ mode
+ */
+
+static void NCR5380_information_transfer(struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+ unsigned char msgout = NOP;
+ int sink = 0;
+ int len;
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ int transfersize;
+#endif
+ unsigned char *data;
+ unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
+ Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+ /* RvC: we need to set the end of the polling time */
+ unsigned long poll_time = jiffies + USLEEP_POLL;
+
+ NCR5380_setup(instance);
+
+ while (1) {
+ tmp = NCR5380_read(STATUS_REG);
+ /* We only have a valid SCSI phase when REQ is asserted */
+ if (tmp & SR_REQ) {
+ phase = (tmp & PHASE_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+ NCR5380_dprint_phase(NDEBUG_INFORMATION, instance);
+ }
+ if (sink && (phase != PHASE_MSGOUT)) {
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ sink = 0;
+ continue;
+ }
+ switch (phase) {
+ case PHASE_DATAIN:
+ case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+ printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n", instance->host_no);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ return;
+#endif
+ /*
+ * If there is no room left in the current buffer in the
+ * scatter-gather list, move onto the next one.
+ */
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page)+
+ cmd->SCp.buffer->offset;
+ dprintk(NDEBUG_INFORMATION, ("scsi%d : %d bytes and %d buffers left\n", instance->host_no, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+ }
+ /*
+ * The preferred transfer method is going to be
+ * PSEUDO-DMA for systems that are strictly PIO,
+ * since we can let the hardware do the handshaking.
+ *
+ * For this to work, we need to know the transfersize
+ * ahead of time, since the pseudo-DMA code will sit
+ * in an unconditional loop.
+ */
+
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ /* KLL
+ * PSEUDO_DMA is defined here. If this is the g_NCR5380
+ * driver then it will always be defined, so the
+ * FLAG_NO_PSEUDO_DMA is used to inhibit PDMA in the base
+ * NCR5380 case. I think this is a fairly clean solution.
+ * We supplement these 2 if's with the flag.
+ */
+#ifdef NCR5380_dma_xfer_len
+ if (!cmd->device->borken && !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
+#else
+ transfersize = cmd->transfersize;
+
+#ifdef LIMIT_TRANSFERSIZE /* If we have problems with interrupt service */
+ if (transfersize > 512)
+ transfersize = 512;
+#endif /* LIMIT_TRANSFERSIZE */
+
+ if (!cmd->device->borken && transfersize && !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && cmd->SCp.this_residual && !(cmd->SCp.this_residual % transfersize)) {
+ /* Limit transfers to 32K, for xx400 & xx406
+ * pseudoDMA that transfers in 128 bytes blocks. */
+ if (transfersize > 32 * 1024)
+ transfersize = 32 * 1024;
+#endif
+ len = transfersize;
+ if (NCR5380_transfer_dma(instance, &phase, &len, (unsigned char **) &cmd->SCp.ptr)) {
+ /*
+ * If the watchdog timer fires, all future accesses to this
+ * device will use the polled-IO.
+ */
+ printk("scsi%d : switching target %d lun %d to slow handshake\n", instance->host_no, cmd->device->id, cmd->device->lun);
+ cmd->device->borken = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ /* XXX - need to source or sink data here, as appropriate */
+ } else
+ cmd->SCp.this_residual -= transfersize - len;
+ } else
+#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
+ NCR5380_transfer_pio(instance, &phase, (int *) &cmd->SCp.this_residual, (unsigned char **)
+ &cmd->SCp.ptr);
+ break;
+ case PHASE_MSGIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Message = tmp;
+
+ switch (tmp) {
+ /*
+ * Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ *
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands.
+ */
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ dprintk(NDEBUG_LINKED, ("scsi%d : target %d lun %d linked command complete.\n", instance->host_no, cmd->device->id, cmd->device->lun));
+ /*
+ * Sanity check : A linked command should only terminate with
+ * one of these messages if there are more linked commands
+ * available.
+ */
+ if (!cmd->next_link) {
+ printk("scsi%d : target %d lun %d linked command complete, no next_link\n" instance->host_no, cmd->device->id, cmd->device->lun);
+ sink = 1;
+ do_abort(instance);
+ return;
+ }
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ dprintk(NDEBUG_LINKED, ("scsi%d : target %d lun %d linked request done, calling scsi_done().\n", instance->host_no, cmd->device->id, cmd->device->lun));
+ collect_stats(hostdata, cmd);
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+#endif /* def LINKED */
+ case ABORT:
+ case COMMAND_COMPLETE:
+ /* Accept message by clearing ACK */
+ sink = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ hostdata->connected = NULL;
+ dprintk(NDEBUG_QUEUES, ("scsi%d : command for target %d, lun %d completed\n", instance->host_no, cmd->device->id, cmd->device->lun));
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+
+ /*
+ * I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ *
+ * If it was a REQUEST SENSE command, we need some way
+ * to differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the result
+ * code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed
+ */
+
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (status_byte(cmd->SCp.Status) != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+
+#ifdef AUTOSENSE
+ if ((cmd->cmnd[0] != REQUEST_SENSE) && (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
+ dprintk(NDEBUG_AUTOSENSE, ("scsi%d : performing request sense\n", instance->host_no));
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->sense_buffer;
+ cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+
+ LIST(cmd, hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)
+ hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ dprintk(NDEBUG_QUEUES, ("scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no));
+ } else
+#endif /* def AUTOSENSE */
+ {
+ collect_stats(hostdata, cmd);
+ cmd->scsi_done(cmd);
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+ return;
+ case MESSAGE_REJECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ switch (hostdata->last_message) {
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ cmd->device->simple_tags = 0;
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ break;
+ default:
+ break;
+ }
+ case DISCONNECT:{
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ cmd->device->disconnect = 1;
+ LIST(cmd, hostdata->disconnected_queue);
+ cmd->host_scribble = (unsigned char *)
+ hostdata->disconnected_queue;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = cmd;
+ dprintk(NDEBUG_QUEUES, ("scsi%d : command for target %d lun %d was moved from connected to" " the disconnected_queue\n", instance->host_no, cmd->device->id, cmd->device->lun));
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /* Wait for bus free to avoid nasty timeouts - FIXME timeout !*/
+ /* NCR538_poll_politely(instance, STATUS_REG, SR_BSY, 0, 30 * HZ); */
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+ return;
+ }
+ /*
+ * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible.
+ */
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ break;
+ case EXTENDED_MESSAGE:
+/*
+ * Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't
+ * include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.
+ */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ /* Accept first byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ dprintk(NDEBUG_EXTENDED, ("scsi%d : receiving extended message\n", instance->host_no));
+
+ len = 2;
+ data = extended_msg + 1;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+ dprintk(NDEBUG_EXTENDED, ("scsi%d : length=%d, code=0x%02x\n", instance->host_no, (int) extended_msg[1], (int) extended_msg[2]));
+
+ if (!len && extended_msg[1] <= (sizeof(extended_msg) - 1)) {
+ /* Accept third byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = extended_msg[1] - 1;
+ data = extended_msg + 3;
+ phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ dprintk(NDEBUG_EXTENDED, ("scsi%d : message received, residual %d\n", instance->host_no, len));
+
+ switch (extended_msg[2]) {
+ case EXTENDED_SDTR:
+ case EXTENDED_WDTR:
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ tmp = 0;
+ }
+ } else if (len) {
+ printk("scsi%d: error receiving extended message\n", instance->host_no);
+ tmp = 0;
+ } else {
+ printk("scsi%d: extended message code %02x length %d is too long\n", instance->host_no, extended_msg[2], extended_msg[1]);
+ tmp = 0;
+ }
+ /* Fall through to reject message */
+
+ /*
+ * If we get something weird that we aren't expecting,
+ * reject it.
+ */
+ default:
+ if (!tmp) {
+ printk("scsi%d: rejecting message ", instance->host_no);
+ print_msg(extended_msg);
+ printk("\n");
+ } else if (tmp != EXTENDED_MESSAGE)
+ printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n", instance->host_no, tmp, cmd->device->id, cmd->device->lun);
+ else
+ printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n", instance->host_no, extended_msg[1], extended_msg[0], cmd->device->id, cmd->device->lun);
+
+ msgout = MESSAGE_REJECT;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ break;
+ } /* switch (tmp) */
+ break;
+ case PHASE_MSGOUT:
+ len = 1;
+ data = &msgout;
+ hostdata->last_message = msgout;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (msgout == ABORT) {
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->connected = NULL;
+ cmd->result = DID_ERROR << 16;
+ collect_stats(hostdata, cmd);
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return;
+ }
+ msgout = NOP;
+ break;
+ case PHASE_CMDOUT:
+ len = cmd->cmd_len;
+ data = cmd->cmnd;
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
+ */
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (!cmd->device->disconnect && should_disconnect(cmd->cmnd[0])) {
+ NCR5380_set_timer(hostdata, USLEEP_SLEEP);
+ dprintk(NDEBUG_USLEEP, ("scsi%d : issued command, sleeping until %ul\n", instance->host_no, hostdata->time_expires));
+ return;
+ }
+ break;
+ case PHASE_STATIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Status = tmp;
+ break;
+ default:
+ printk("scsi%d : unknown phase\n", instance->host_no);
+ NCR5380_dprint(NDEBUG_ALL, instance);
+ } /* switch(phase) */
+ } /* if (tmp * SR_REQ) */
+ else {
+ /* RvC: go to sleep if polling time expired
+ */
+ if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) {
+ NCR5380_set_timer(hostdata, USLEEP_SLEEP);
+ dprintk(NDEBUG_USLEEP, ("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, hostdata->time_expires));
+ return;
+ }
+ }
+ } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ * Locks: io_request_lock held by caller if IRQ driven
+ */
+
+static void NCR5380_reselect(struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned char target_mask;
+ unsigned char lun, phase;
+ int len;
+ unsigned char msg[3];
+ unsigned char *data;
+ Scsi_Cmnd *tmp = NULL, *prev;
+ int abort = 0;
+ NCR5380_setup(instance);
+
+ /*
+ * Disable arbitration, etc. since the host adapter obviously
+ * lost, and tell an interrupted NCR5380_select() to restart.
+ */
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ hostdata->restart_select = 1;
+
+ target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+ dprintk(NDEBUG_SELECTION, ("scsi%d : reselect\n", instance->host_no));
+
+ /*
+ * At this point, we have detected that our SCSI ID is on the bus,
+ * SEL is true and BSY was false for at least one bus settle delay
+ * (400 ns).
+ *
+ * We must assert BSY ourselves, until the target drops the SEL
+ * signal.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+
+ /* FIXME: timeout too long, must fail to workqueue */
+ if(NCR5380_poll_politely(instance, STATUS_REG, SR_SEL, 0, 2*HZ)<0)
+ abort = 1;
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * Wait for target to go into MSGIN.
+ * FIXME: timeout needed and fail to work queeu
+ */
+
+ if(NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, 2*HZ))
+ abort = 1;
+
+ len = 1;
+ data = msg;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+ if (!(msg[0] & 0x80)) {
+ printk(KERN_ERR "scsi%d : expecting IDENTIFY message, got ", instance->host_no);
+ print_msg(msg);
+ abort = 1;
+ } else {
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ lun = (msg[0] & 0x07);
+
+ /*
+ * We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer.
+ */
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
+ ) {
+ if (prev) {
+ REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble);
+ prev->host_scribble = tmp->host_scribble;
+ } else {
+ REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
+ hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+ tmp->host_scribble = NULL;
+ break;
+ }
+ if (!tmp) {
+ printk(KERN_ERR "scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n", instance->host_no, target_mask, lun);
+ /*
+ * Since we have an established nexus that we can't do anything with,
+ * we must abort it.
+ */
+ abort = 1;
+ }
+ }
+
+ if (abort) {
+ do_abort(instance);
+ } else {
+ hostdata->connected = tmp;
+ dprintk(NDEBUG_RESELECTION, ("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n", instance->host_no, tmp->target, tmp->lun, tmp->tag));
+ }
+}
+
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : called by interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would finish the DMA transfer).
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
+ * nexus has been reestablished, on failure NULL is returned.
+ */
+
+#ifdef REAL_DMA
+static void NCR5380_dma_complete(NCR5380_instance * instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata * instance->hostdata);
+ int transferred;
+ NCR5380_setup(instance);
+
+ /*
+ * XXX this might not be right.
+ *
+ * Wait for final byte to transfer, ie wait for ACK to go false.
+ *
+ * We should use the Last Byte Sent bit, unfortunately this is
+ * not available on the 5380/5381 (only the various CMOS chips)
+ *
+ * FIXME: timeout, and need to handle long timeout/irq case
+ */
+
+ NCR5380_poll_politely(instance, BUS_AND_STATUS_REG, BASR_ACK, 0, 5*HZ);
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * The only places we should see a phase mismatch and have to send
+ * data from the same set of pointers will be the data transfer
+ * phases. So, residual, requested length are only important here.
+ */
+
+ if (!(hostdata->connected->SCp.phase & SR_CD)) {
+ transferred = instance->dmalen - NCR5380_dma_residual();
+ hostdata->connected->SCp.this_residual -= transferred;
+ hostdata->connected->SCp.ptr += transferred;
+ }
+}
+#endif /* def REAL_DMA */
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
+ * used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
+ * a problem, we could implement longjmp() / setjmp(), setjmp()
+ * called where the loop started in NCR5380_main().
+ *
+ * Locks: host lock taken by caller
+ */
+
+static int NCR5380_abort(Scsi_Cmnd * cmd) {
+ NCR5380_local_declare();
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ Scsi_Cmnd *tmp, **prev;
+
+ printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no);
+ print_Scsi_Cmnd(cmd);
+
+ NCR5380_print_status(instance);
+
+ NCR5380_setup(instance);
+
+ dprintk(NDEBUG_ABORT, ("scsi%d : abort called\n", instance->host_no));
+ dprintk(NDEBUG_ABORT, (" basr 0x%X, sr 0x%X\n", NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)));
+
+#if 0
+/*
+ * Case 1 : If the command is the currently executing command,
+ * we'll set the aborted flag and return control so that
+ * information transfer routine can exit cleanly.
+ */
+
+ if (hostdata->connected == cmd) {
+ dprintk(NDEBUG_ABORT, ("scsi%d : aborting connected command\n", instance->host_no));
+ hostdata->aborted = 1;
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
+/*
+ * Since we can't change phases until we've completed the current
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/*
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */
+
+ return 0;
+ }
+#endif
+
+/*
+ * Case 2 : If the command hasn't been issued yet, we simply remove it
+ * from the issue queue.
+ */
+
+ dprintk(NDEBUG_ABORT, ("scsi%d : abort going into loop.\n", instance->host_no));
+ for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, tmp->host_scribble);
+ (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ dprintk(NDEBUG_ABORT, ("scsi%d : abort removed command from issue queue.\n", instance->host_no));
+ tmp->done(tmp);
+ return SUCCESS;
+ }
+#if (NDEBUG & NDEBUG_ABORT)
+ /* KLL */
+ else if (prev == tmp)
+ printk(KERN_ERR "scsi%d : LOOP\n", instance->host_no);
+#endif
+
+/*
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected) {
+ dprintk(NDEBUG_ABORT, ("scsi%d : abort failed, command connected.\n", instance->host_no));
+ return FAILED;
+ }
+/*
+ * Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select(). The easiest way to implement this
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that
+ * device reselected.
+ *
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ dprintk(NDEBUG_ABORT, ("scsi%d : aborting disconnected command.\n", instance->host_no));
+
+ if (NCR5380_select(instance, cmd, (int) cmd->tag))
+ return FAILED;
+ dprintk(NDEBUG_ABORT, ("scsi%d : nexus reestablished.\n", instance->host_no));
+
+ do_abort(instance);
+
+ for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, tmp->host_scribble);
+ *prev = (Scsi_Cmnd *) tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ tmp->done(tmp);
+ return SUCCESS;
+ }
+ }
+/*
+ * Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+ printk(KERN_WARNING "scsi%d : warning : SCSI command probably completed successfully\n"
+ " before abortion\n", instance->host_no);
+ return FAILED;
+}
+
+
+/*
+ * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SUCCESS
+ *
+ * Locks: host lock taken by caller
+ */
+
+static int NCR5380_bus_reset(Scsi_Cmnd * cmd) {
+ NCR5380_local_declare();
+ NCR5380_setup(cmd->device->host);
+
+ NCR5380_print_status(cmd->device->host);
+ do_reset(cmd->device->host);
+ return SUCCESS;
+}
+
+/*
+ * Function : int NCR5380_device_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : reset a SCSI device
+ *
+ * Returns : FAILED
+ *
+ * Locks: io_request_lock held by caller
+ */
+
+static int NCR5380_device_reset(Scsi_Cmnd * cmd) {
+ return FAILED;
+}
+
+/*
+ * Function : int NCR5380_host_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : reset a SCSI device
+ *
+ * Returns : FAILED
+ *
+ * Locks: io_request_lock held by caller
+ */
+
+static int NCR5380_host_reset(Scsi_Cmnd * cmd) {
+ return FAILED;
+}
diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
new file mode 100644
index 000000000000..b5103f94d627
--- /dev/null
+++ b/drivers/scsi/NCR5380.h
@@ -0,0 +1,432 @@
+/*
+ * NCR 5380 defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 7
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.h,v $
+ */
+
+#ifndef NCR5380_H
+#define NCR5380_H
+
+#include <linux/interrupt.h>
+
+#define NCR5380_PUBLIC_RELEASE 7
+#define NCR53C400_PUBLIC_RELEASE 2
+
+#define NDEBUG_ARBITRATION 0x1
+#define NDEBUG_AUTOSENSE 0x2
+#define NDEBUG_DMA 0x4
+#define NDEBUG_HANDSHAKE 0x8
+#define NDEBUG_INFORMATION 0x10
+#define NDEBUG_INIT 0x20
+#define NDEBUG_INTR 0x40
+#define NDEBUG_LINKED 0x80
+#define NDEBUG_MAIN 0x100
+#define NDEBUG_NO_DATAOUT 0x200
+#define NDEBUG_NO_WRITE 0x400
+#define NDEBUG_PIO 0x800
+#define NDEBUG_PSEUDO_DMA 0x1000
+#define NDEBUG_QUEUES 0x2000
+#define NDEBUG_RESELECTION 0x4000
+#define NDEBUG_SELECTION 0x8000
+#define NDEBUG_USLEEP 0x10000
+#define NDEBUG_LAST_BYTE_SENT 0x20000
+#define NDEBUG_RESTART_SELECT 0x40000
+#define NDEBUG_EXTENDED 0x80000
+#define NDEBUG_C400_PREAD 0x100000
+#define NDEBUG_C400_PWRITE 0x200000
+#define NDEBUG_LISTS 0x400000
+
+#define NDEBUG_ANY 0xFFFFFFFFUL
+
+/*
+ * The contents of the OUTPUT DATA register are asserted on the bus when
+ * either arbitration is occurring or the phase-indicating signals (
+ * IO, CD, MSG) in the TARGET COMMAND register and the ASSERT DATA
+ * bit in the INITIATOR COMMAND register is set.
+ */
+
+#define OUTPUT_DATA_REG 0 /* wo DATA lines on SCSI bus */
+#define CURRENT_SCSI_DATA_REG 0 /* ro same */
+
+#define INITIATOR_COMMAND_REG 1 /* rw */
+#define ICR_ASSERT_RST 0x80 /* rw Set to assert RST */
+#define ICR_ARBITRATION_PROGRESS 0x40 /* ro Indicates arbitration complete */
+#define ICR_TRI_STATE 0x40 /* wo Set to tri-state drivers */
+#define ICR_ARBITRATION_LOST 0x20 /* ro Indicates arbitration lost */
+#define ICR_DIFF_ENABLE 0x20 /* wo Set to enable diff. drivers */
+#define ICR_ASSERT_ACK 0x10 /* rw ini Set to assert ACK */
+#define ICR_ASSERT_BSY 0x08 /* rw Set to assert BSY */
+#define ICR_ASSERT_SEL 0x04 /* rw Set to assert SEL */
+#define ICR_ASSERT_ATN 0x02 /* rw Set to assert ATN */
+#define ICR_ASSERT_DATA 0x01 /* rw SCSI_DATA_REG is asserted */
+
+#ifdef DIFFERENTIAL
+#define ICR_BASE ICR_DIFF_ENABLE
+#else
+#define ICR_BASE 0
+#endif
+
+#define MODE_REG 2
+/*
+ * Note : BLOCK_DMA code will keep DRQ asserted for the duration of the
+ * transfer, causing the chip to hog the bus. You probably don't want
+ * this.
+ */
+#define MR_BLOCK_DMA_MODE 0x80 /* rw block mode DMA */
+#define MR_TARGET 0x40 /* rw target mode */
+#define MR_ENABLE_PAR_CHECK 0x20 /* rw enable parity checking */
+#define MR_ENABLE_PAR_INTR 0x10 /* rw enable bad parity interrupt */
+#define MR_ENABLE_EOP_INTR 0x08 /* rw enable eop interrupt */
+#define MR_MONITOR_BSY 0x04 /* rw enable int on unexpected bsy fail */
+#define MR_DMA_MODE 0x02 /* rw DMA / pseudo DMA mode */
+#define MR_ARBITRATE 0x01 /* rw start arbitration */
+
+#ifdef PARITY
+#define MR_BASE MR_ENABLE_PAR_CHECK
+#else
+#define MR_BASE 0
+#endif
+
+#define TARGET_COMMAND_REG 3
+#define TCR_LAST_BYTE_SENT 0x80 /* ro DMA done */
+#define TCR_ASSERT_REQ 0x08 /* tgt rw assert REQ */
+#define TCR_ASSERT_MSG 0x04 /* tgt rw assert MSG */
+#define TCR_ASSERT_CD 0x02 /* tgt rw assert CD */
+#define TCR_ASSERT_IO 0x01 /* tgt rw assert IO */
+
+#define STATUS_REG 4 /* ro */
+/*
+ * Note : a set bit indicates an active signal, driven by us or another
+ * device.
+ */
+#define SR_RST 0x80
+#define SR_BSY 0x40
+#define SR_REQ 0x20
+#define SR_MSG 0x10
+#define SR_CD 0x08
+#define SR_IO 0x04
+#define SR_SEL 0x02
+#define SR_DBP 0x01
+
+/*
+ * Setting a bit in this register will cause an interrupt to be generated when
+ * BSY is false and SEL true and this bit is asserted on the bus.
+ */
+#define SELECT_ENABLE_REG 4 /* wo */
+
+#define BUS_AND_STATUS_REG 5 /* ro */
+#define BASR_END_DMA_TRANSFER 0x80 /* ro set on end of transfer */
+#define BASR_DRQ 0x40 /* ro mirror of DRQ pin */
+#define BASR_PARITY_ERROR 0x20 /* ro parity error detected */
+#define BASR_IRQ 0x10 /* ro mirror of IRQ pin */
+#define BASR_PHASE_MATCH 0x08 /* ro Set when MSG CD IO match TCR */
+#define BASR_BUSY_ERROR 0x04 /* ro Unexpected change to inactive state */
+#define BASR_ATN 0x02 /* ro BUS status */
+#define BASR_ACK 0x01 /* ro BUS status */
+
+/* Write any value to this register to start a DMA send */
+#define START_DMA_SEND_REG 5 /* wo */
+
+/*
+ * Used in DMA transfer mode, data is latched from the SCSI bus on
+ * the falling edge of REQ (ini) or ACK (tgt)
+ */
+#define INPUT_DATA_REG 6 /* ro */
+
+/* Write any value to this register to start a DMA receive */
+#define START_DMA_TARGET_RECEIVE_REG 6 /* wo */
+
+/* Read this register to clear interrupt conditions */
+#define RESET_PARITY_INTERRUPT_REG 7 /* ro */
+
+/* Write any value to this register to start an ini mode DMA receive */
+#define START_DMA_INITIATOR_RECEIVE_REG 7 /* wo */
+
+#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8 /* rw */
+
+#define CSR_RESET 0x80 /* wo Resets 53c400 */
+#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */
+#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
+#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
+#define CSR_53C80_INTR 0x10 /* rw Enable 53c80 interrupts */
+#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Is Host buffer ready */
+#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */
+#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */
+
+#if 0
+#define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
+#else
+#define CSR_BASE CSR_53C80_INTR
+#endif
+
+/* Number of 128-byte blocks to be transferred */
+#define C400_BLOCK_COUNTER_REG NCR53C400_register_offset-7 /* rw */
+
+/* Resume transfer after disconnect */
+#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6 /* wo */
+
+/* Access to host buffer stack */
+#define C400_HOST_BUFFER NCR53C400_register_offset-4 /* rw */
+
+
+/* Note : PHASE_* macros are based on the values of the STATUS register */
+#define PHASE_MASK (SR_MSG | SR_CD | SR_IO)
+
+#define PHASE_DATAOUT 0
+#define PHASE_DATAIN SR_IO
+#define PHASE_CMDOUT SR_CD
+#define PHASE_STATIN (SR_CD | SR_IO)
+#define PHASE_MSGOUT (SR_MSG | SR_CD)
+#define PHASE_MSGIN (SR_MSG | SR_CD | SR_IO)
+#define PHASE_UNKNOWN 0xff
+
+/*
+ * Convert status register phase to something we can use to set phase in
+ * the target register so we can get phase mismatch interrupts on DMA
+ * transfers.
+ */
+
+#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)
+
+/*
+ * The internal should_disconnect() function returns these based on the
+ * expected length of a disconnect if a device supports disconnect/
+ * reconnect.
+ */
+
+#define DISCONNECT_NONE 0
+#define DISCONNECT_TIME_TO_DATA 1
+#define DISCONNECT_LONG 2
+
+/*
+ * These are "special" values for the tag parameter passed to NCR5380_select.
+ */
+
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /*
+ * Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices.
+ */
+
+/*
+ * These are "special" values for the irq and dma_channel fields of the
+ * Scsi_Host structure
+ */
+
+#define SCSI_IRQ_NONE 255
+#define DMA_NONE 255
+#define IRQ_AUTO 254
+#define DMA_AUTO 254
+#define PORT_AUTO 0xffff /* autoprobe io port for 53c400a */
+
+#define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */
+#define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */
+#define FLAG_NCR53C400 4 /* NCR53c400 */
+#define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */
+#define FLAG_DTC3181E 16 /* DTC3181E */
+
+#ifndef ASM
+struct NCR5380_hostdata {
+ NCR5380_implementation_fields; /* implementation specific */
+ struct Scsi_Host *host; /* Host backpointer */
+ unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */
+ unsigned char targets_present; /* targets we have connected
+ to, so we can call a select
+ failure a retryable condition */
+ volatile unsigned char busy[8]; /* index = target, bit = lun */
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+ volatile int dma_len; /* requested length of DMA */
+#endif
+ volatile unsigned char last_message; /* last message OUT */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
+ volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
+ volatile int restart_select; /* we have disconnected,
+ used to restart
+ NCR5380_select() */
+ volatile unsigned aborted:1; /* flag, says aborted */
+ int flags;
+ unsigned long time_expires; /* in jiffies, set prior to sleeping */
+ int select_time; /* timer in select for target response */
+ volatile Scsi_Cmnd *selecting;
+ struct work_struct coroutine; /* our co-routine */
+#ifdef NCR5380_STATS
+ unsigned timebase; /* Base for time calcs */
+ long time_read[8]; /* time to do reads */
+ long time_write[8]; /* time to do writes */
+ unsigned long bytes_read[8]; /* bytes read */
+ unsigned long bytes_write[8]; /* bytes written */
+ unsigned pendingr;
+ unsigned pendingw;
+#endif
+};
+
+#ifdef __KERNEL__
+
+#define dprintk(a,b) do {} while(0)
+#define NCR5380_dprint(a,b) do {} while(0)
+#define NCR5380_dprint_phase(a,b) do {} while(0)
+
+#if defined(AUTOPROBE_IRQ)
+static int NCR5380_probe_irq(struct Scsi_Host *instance, int possible);
+#endif
+static int NCR5380_init(struct Scsi_Host *instance, int flags);
+static void NCR5380_exit(struct Scsi_Host *instance);
+static void NCR5380_information_transfer(struct Scsi_Host *instance);
+#ifndef DONT_USE_INTR
+static irqreturn_t NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+static void NCR5380_main(void *ptr);
+static void NCR5380_print_options(struct Scsi_Host *instance);
+#ifdef NDEBUG
+static void NCR5380_print_phase(struct Scsi_Host *instance);
+static void NCR5380_print(struct Scsi_Host *instance);
+#endif
+static int NCR5380_abort(Scsi_Cmnd * cmd);
+static int NCR5380_bus_reset(Scsi_Cmnd * cmd);
+static int NCR5380_host_reset(Scsi_Cmnd * cmd);
+static int NCR5380_device_reset(Scsi_Cmnd * cmd);
+static int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *));
+static int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer, char **start,
+off_t offset, int length, int inout);
+
+static void NCR5380_reselect(struct Scsi_Host *instance);
+static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag);
+#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
+static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
+#endif
+static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
+
+#if (defined(REAL_DMA) || defined(REAL_DMA_POLL))
+
+#if defined(i386) || defined(__alpha__)
+
+/**
+ * NCR5380_pc_dma_setup - setup ISA DMA
+ * @instance: adapter to set up
+ * @ptr: block to transfer (virtual address)
+ * @count: number of bytes to transfer
+ * @mode: DMA controller mode to use
+ *
+ * Program the DMA controller ready to perform an ISA DMA transfer
+ * on this chip.
+ *
+ * Locks: takes and releases the ISA DMA lock.
+ */
+
+static __inline__ int NCR5380_pc_dma_setup(struct Scsi_Host *instance, unsigned char *ptr, unsigned int count, unsigned char mode)
+{
+ unsigned limit;
+ unsigned long bus_addr = virt_to_bus(ptr);
+ unsigned long flags;
+
+ if (instance->dma_channel <= 3) {
+ if (count > 65536)
+ count = 65536;
+ limit = 65536 - (bus_addr & 0xFFFF);
+ } else {
+ if (count > 65536 * 2)
+ count = 65536 * 2;
+ limit = 65536 * 2 - (bus_addr & 0x1FFFF);
+ }
+
+ if (count > limit)
+ count = limit;
+
+ if ((count & 1) || (bus_addr & 1))
+ panic("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
+
+ flags=claim_dma_lock();
+ disable_dma(instance->dma_channel);
+ clear_dma_ff(instance->dma_channel);
+ set_dma_addr(instance->dma_channel, bus_addr);
+ set_dma_count(instance->dma_channel, count);
+ set_dma_mode(instance->dma_channel, mode);
+ enable_dma(instance->dma_channel);
+ release_dma_lock(flags);
+
+ return count;
+}
+
+/**
+ * NCR5380_pc_dma_write_setup - setup ISA DMA write
+ * @instance: adapter to set up
+ * @ptr: block to transfer (virtual address)
+ * @count: number of bytes to transfer
+ *
+ * Program the DMA controller ready to perform an ISA DMA write to the
+ * SCSI controller.
+ *
+ * Locks: called routines take and release the ISA DMA lock.
+ */
+
+static __inline__ int NCR5380_pc_dma_write_setup(struct Scsi_Host *instance, unsigned char *src, unsigned int count)
+{
+ return NCR5380_pc_dma_setup(instance, src, count, DMA_MODE_WRITE);
+}
+
+/**
+ * NCR5380_pc_dma_read_setup - setup ISA DMA read
+ * @instance: adapter to set up
+ * @ptr: block to transfer (virtual address)
+ * @count: number of bytes to transfer
+ *
+ * Program the DMA controller ready to perform an ISA DMA read from the
+ * SCSI controller.
+ *
+ * Locks: called routines take and release the ISA DMA lock.
+ */
+
+static __inline__ int NCR5380_pc_dma_read_setup(struct Scsi_Host *instance, unsigned char *src, unsigned int count)
+{
+ return NCR5380_pc_dma_setup(instance, src, count, DMA_MODE_READ);
+}
+
+/**
+ * NCR5380_pc_dma_residual - return bytes left
+ * @instance: adapter
+ *
+ * Reports the number of bytes left over after the DMA was terminated.
+ *
+ * Locks: takes and releases the ISA DMA lock.
+ */
+
+static __inline__ int NCR5380_pc_dma_residual(struct Scsi_Host *instance)
+{
+ unsigned long flags;
+ int tmp;
+
+ flags = claim_dma_lock();
+ clear_dma_ff(instance->dma_channel);
+ tmp = get_dma_residue(instance->dma_channel);
+ release_dma_lock(flags);
+
+ return tmp;
+}
+#endif /* defined(i386) || defined(__alpha__) */
+#endif /* defined(REAL_DMA) */
+#endif /* __KERNEL__ */
+#endif /* ndef ASM */
+#endif /* NCR5380_H */
diff --git a/drivers/scsi/NCR53C9x.c b/drivers/scsi/NCR53C9x.c
new file mode 100644
index 000000000000..3c86655a5f33
--- /dev/null
+++ b/drivers/scsi/NCR53C9x.c
@@ -0,0 +1,3649 @@
+/* NCR53C9x.c: Generic SCSI driver code for NCR53C9x chips.
+ *
+ * Originally esp.c : EnhancedScsiProcessor Sun SCSI driver code.
+ *
+ * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Most DMA dependencies put in driver specific files by
+ * Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * Set up to use esp_read/esp_write (preprocessor macros in NCR53c9x.h) by
+ * Tymm Twillman (tymm@coe.missouri.edu)
+ */
+
+/* TODO:
+ *
+ * 1) Maybe disable parity checking in config register one for SCSI1
+ * targets. (Gilmore says parity error on the SBus can lock up
+ * old sun4c's)
+ * 2) Add support for DMA2 pipelining.
+ * 3) Add tagged queueing.
+ * 4) Maybe change use of "esp" to something more "NCR"'ish.
+ */
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/* Command phase enumeration. */
+enum {
+ not_issued = 0x00, /* Still in the issue_SC queue. */
+
+ /* Various forms of selecting a target. */
+#define in_slct_mask 0x10
+ in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */
+ in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */
+ in_slct_msg = 0x12, /* select, then send a message */
+ in_slct_tag = 0x13, /* select and send tagged queue msg */
+ in_slct_sneg = 0x14, /* select and acquire sync capabilities */
+
+ /* Any post selection activity. */
+#define in_phases_mask 0x20
+ in_datain = 0x20, /* Data is transferring from the bus */
+ in_dataout = 0x21, /* Data is transferring to the bus */
+ in_data_done = 0x22, /* Last DMA data operation done (maybe) */
+ in_msgin = 0x23, /* Eating message from target */
+ in_msgincont = 0x24, /* Eating more msg bytes from target */
+ in_msgindone = 0x25, /* Decide what to do with what we got */
+ in_msgout = 0x26, /* Sending message to target */
+ in_msgoutdone = 0x27, /* Done sending msg out */
+ in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */
+ in_cmdend = 0x29, /* Done sending slow cmd */
+ in_status = 0x2a, /* Was in status phase, finishing cmd */
+ in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */
+ in_the_dark = 0x2c, /* Don't know what bus phase we are in */
+
+ /* Special states, ie. not normal bus transitions... */
+#define in_spec_mask 0x80
+ in_abortone = 0x80, /* Aborting one command currently */
+ in_abortall = 0x81, /* Blowing away all commands we have */
+ in_resetdev = 0x82, /* SCSI target reset in progress */
+ in_resetbus = 0x83, /* SCSI bus reset in progress */
+ in_tgterror = 0x84, /* Target did something stupid */
+};
+
+enum {
+ /* Zero has special meaning, see skipahead[12]. */
+/*0*/ do_never,
+
+/*1*/ do_phase_determine,
+/*2*/ do_reset_bus,
+/*3*/ do_reset_complete,
+/*4*/ do_work_bus,
+/*5*/ do_intr_end
+};
+
+/* The master ring of all esp hosts we are managing in this driver. */
+struct NCR_ESP *espchain;
+int nesps = 0, esps_in_use = 0, esps_running = 0;
+
+irqreturn_t esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+
+/* Debugging routines */
+static struct esp_cmdstrings {
+ unchar cmdchar;
+ char *text;
+} esp_cmd_strings[] = {
+ /* Miscellaneous */
+ { ESP_CMD_NULL, "ESP_NOP", },
+ { ESP_CMD_FLUSH, "FIFO_FLUSH", },
+ { ESP_CMD_RC, "RSTESP", },
+ { ESP_CMD_RS, "RSTSCSI", },
+ /* Disconnected State Group */
+ { ESP_CMD_RSEL, "RESLCTSEQ", },
+ { ESP_CMD_SEL, "SLCTNATN", },
+ { ESP_CMD_SELA, "SLCTATN", },
+ { ESP_CMD_SELAS, "SLCTATNSTOP", },
+ { ESP_CMD_ESEL, "ENSLCTRESEL", },
+ { ESP_CMD_DSEL, "DISSELRESEL", },
+ { ESP_CMD_SA3, "SLCTATN3", },
+ { ESP_CMD_RSEL3, "RESLCTSEQ", },
+ /* Target State Group */
+ { ESP_CMD_SMSG, "SNDMSG", },
+ { ESP_CMD_SSTAT, "SNDSTATUS", },
+ { ESP_CMD_SDATA, "SNDDATA", },
+ { ESP_CMD_DSEQ, "DISCSEQ", },
+ { ESP_CMD_TSEQ, "TERMSEQ", },
+ { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", },
+ { ESP_CMD_DCNCT, "DISC", },
+ { ESP_CMD_RMSG, "RCVMSG", },
+ { ESP_CMD_RCMD, "RCVCMD", },
+ { ESP_CMD_RDATA, "RCVDATA", },
+ { ESP_CMD_RCSEQ, "RCVCMDSEQ", },
+ /* Initiator State Group */
+ { ESP_CMD_TI, "TRANSINFO", },
+ { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", },
+ { ESP_CMD_MOK, "MSGACCEPTED", },
+ { ESP_CMD_TPAD, "TPAD", },
+ { ESP_CMD_SATN, "SATN", },
+ { ESP_CMD_RATN, "RATN", },
+};
+#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))
+
+/* Print textual representation of an ESP command */
+static inline void esp_print_cmd(unchar espcmd)
+{
+ unchar dma_bit = espcmd & ESP_CMD_DMA;
+ int i;
+
+ espcmd &= ~dma_bit;
+ for(i=0; i<NUM_ESP_COMMANDS; i++)
+ if(esp_cmd_strings[i].cmdchar == espcmd)
+ break;
+ if(i==NUM_ESP_COMMANDS)
+ printk("ESP_Unknown");
+ else
+ printk("%s%s", esp_cmd_strings[i].text,
+ ((dma_bit) ? "+DMA" : ""));
+}
+
+/* Print the status register's value */
+static inline void esp_print_statreg(unchar statreg)
+{
+ unchar phase;
+
+ printk("STATUS<");
+ phase = statreg & ESP_STAT_PMASK;
+ printk("%s,", (phase == ESP_DOP ? "DATA-OUT" :
+ (phase == ESP_DIP ? "DATA-IN" :
+ (phase == ESP_CMDP ? "COMMAND" :
+ (phase == ESP_STATP ? "STATUS" :
+ (phase == ESP_MOP ? "MSG-OUT" :
+ (phase == ESP_MIP ? "MSG_IN" :
+ "unknown")))))));
+ if(statreg & ESP_STAT_TDONE)
+ printk("TRANS_DONE,");
+ if(statreg & ESP_STAT_TCNT)
+ printk("TCOUNT_ZERO,");
+ if(statreg & ESP_STAT_PERR)
+ printk("P_ERROR,");
+ if(statreg & ESP_STAT_SPAM)
+ printk("SPAM,");
+ if(statreg & ESP_STAT_INTR)
+ printk("IRQ,");
+ printk(">");
+}
+
+/* Print the interrupt register's value */
+static inline void esp_print_ireg(unchar intreg)
+{
+ printk("INTREG< ");
+ if(intreg & ESP_INTR_S)
+ printk("SLCT_NATN ");
+ if(intreg & ESP_INTR_SATN)
+ printk("SLCT_ATN ");
+ if(intreg & ESP_INTR_RSEL)
+ printk("RSLCT ");
+ if(intreg & ESP_INTR_FDONE)
+ printk("FDONE ");
+ if(intreg & ESP_INTR_BSERV)
+ printk("BSERV ");
+ if(intreg & ESP_INTR_DC)
+ printk("DISCNCT ");
+ if(intreg & ESP_INTR_IC)
+ printk("ILL_CMD ");
+ if(intreg & ESP_INTR_SR)
+ printk("SCSI_BUS_RESET ");
+ printk(">");
+}
+
+/* Print the sequence step registers contents */
+static inline void esp_print_seqreg(unchar stepreg)
+{
+ stepreg &= ESP_STEP_VBITS;
+ printk("STEP<%s>",
+ (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" :
+ (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
+ (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
+ (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
+ (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
+ "UNKNOWN"))))));
+}
+
+static char *phase_string(int phase)
+{
+ switch(phase) {
+ case not_issued:
+ return "UNISSUED";
+ case in_slct_norm:
+ return "SLCTNORM";
+ case in_slct_stop:
+ return "SLCTSTOP";
+ case in_slct_msg:
+ return "SLCTMSG";
+ case in_slct_tag:
+ return "SLCTTAG";
+ case in_slct_sneg:
+ return "SLCTSNEG";
+ case in_datain:
+ return "DATAIN";
+ case in_dataout:
+ return "DATAOUT";
+ case in_data_done:
+ return "DATADONE";
+ case in_msgin:
+ return "MSGIN";
+ case in_msgincont:
+ return "MSGINCONT";
+ case in_msgindone:
+ return "MSGINDONE";
+ case in_msgout:
+ return "MSGOUT";
+ case in_msgoutdone:
+ return "MSGOUTDONE";
+ case in_cmdbegin:
+ return "CMDBEGIN";
+ case in_cmdend:
+ return "CMDEND";
+ case in_status:
+ return "STATUS";
+ case in_freeing:
+ return "FREEING";
+ case in_the_dark:
+ return "CLUELESS";
+ case in_abortone:
+ return "ABORTONE";
+ case in_abortall:
+ return "ABORTALL";
+ case in_resetdev:
+ return "RESETDEV";
+ case in_resetbus:
+ return "RESETBUS";
+ case in_tgterror:
+ return "TGTERROR";
+ default:
+ return "UNKNOWN";
+ };
+}
+
+#ifdef DEBUG_STATE_MACHINE
+static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase)
+{
+ ESPLOG(("<%s>", phase_string(newphase)));
+ s->SCp.sent_command = s->SCp.phase;
+ s->SCp.phase = newphase;
+}
+#else
+#define esp_advance_phase(__s, __newphase) \
+ (__s)->SCp.sent_command = (__s)->SCp.phase; \
+ (__s)->SCp.phase = (__newphase);
+#endif
+
+#ifdef DEBUG_ESP_CMDS
+static inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs,
+ unchar cmd)
+{
+ esp->espcmdlog[esp->espcmdent] = cmd;
+ esp->espcmdent = (esp->espcmdent + 1) & 31;
+ esp_write(eregs->esp_cmnd, cmd);
+}
+#else
+#define esp_cmd(__esp, __eregs, __cmd) esp_write((__eregs)->esp_cmnd, (__cmd))
+#endif
+
+/* How we use the various Linux SCSI data structures for operation.
+ *
+ * struct scsi_cmnd:
+ *
+ * We keep track of the syncronous capabilities of a target
+ * in the device member, using sync_min_period and
+ * sync_max_offset. These are the values we directly write
+ * into the ESP registers while running a command. If offset
+ * is zero the ESP will use asynchronous transfers.
+ * If the borken flag is set we assume we shouldn't even bother
+ * trying to negotiate for synchronous transfer as this target
+ * is really stupid. If we notice the target is dropping the
+ * bus, and we have been allowing it to disconnect, we clear
+ * the disconnect flag.
+ */
+
+/* Manipulation of the ESP command queues. Thanks to the aha152x driver
+ * and its author, Juergen E. Fischer, for the methods used here.
+ * Note that these are per-ESP queues, not global queues like
+ * the aha152x driver uses.
+ */
+static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+ Scsi_Cmnd *end;
+
+ new_SC->host_scribble = (unsigned char *) NULL;
+ if(!*SC)
+ *SC = new_SC;
+ else {
+ for(end=*SC;end->host_scribble;end=(Scsi_Cmnd *)end->host_scribble)
+ ;
+ end->host_scribble = (unsigned char *) new_SC;
+ }
+}
+
+static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+ new_SC->host_scribble = (unsigned char *) *SC;
+ *SC = new_SC;
+}
+
+static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
+{
+ Scsi_Cmnd *ptr;
+
+ ptr = *SC;
+ if(ptr)
+ *SC = (Scsi_Cmnd *) (*SC)->host_scribble;
+ return ptr;
+}
+
+static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
+{
+ Scsi_Cmnd *ptr, *prev;
+
+ for(ptr = *SC, prev = NULL;
+ ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
+ prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ ;
+ if(ptr) {
+ if(prev)
+ prev->host_scribble=ptr->host_scribble;
+ else
+ *SC=(Scsi_Cmnd *)ptr->host_scribble;
+ }
+ return ptr;
+}
+
+/* Resetting various pieces of the ESP scsi driver chipset */
+
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ int family_code, version, i;
+ volatile int trash;
+
+ /* Now reset the ESP chip */
+ esp_cmd(esp, eregs, ESP_CMD_RC);
+ esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
+ if(esp->erev == fast)
+ esp_write(eregs->esp_cfg2, ESP_CONFIG2_FENAB);
+ esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
+
+ /* This is the only point at which it is reliable to read
+ * the ID-code for a fast ESP chip variant.
+ */
+ esp->max_period = ((35 * esp->ccycle) / 1000);
+ if(esp->erev == fast) {
+ char *erev2string[] = {
+ "Emulex FAS236",
+ "Emulex FPESP100A",
+ "fast",
+ "QLogic FAS366",
+ "Emulex FAS216",
+ "Symbios Logic 53CF9x-2",
+ "unknown!"
+ };
+
+ version = esp_read(eregs->esp_uid);
+ family_code = (version & 0xf8) >> 3;
+ if(family_code == 0x02) {
+ if ((version & 7) == 2)
+ esp->erev = fas216;
+ else
+ esp->erev = fas236;
+ } else if(family_code == 0x0a)
+ esp->erev = fas366; /* Version is usually '5'. */
+ else if(family_code == 0x00) {
+ if ((version & 7) == 2)
+ esp->erev = fas100a; /* NCR53C9X */
+ else
+ esp->erev = espunknown;
+ } else if(family_code == 0x14) {
+ if ((version & 7) == 2)
+ esp->erev = fsc;
+ else
+ esp->erev = espunknown;
+ } else if(family_code == 0x00) {
+ if ((version & 7) == 2)
+ esp->erev = fas100a; /* NCR53C9X */
+ else
+ esp->erev = espunknown;
+ } else
+ esp->erev = espunknown;
+ ESPLOG(("esp%d: FAST chip is %s (family=%d, version=%d)\n",
+ esp->esp_id, erev2string[esp->erev - fas236],
+ family_code, (version & 7)));
+
+ esp->min_period = ((4 * esp->ccycle) / 1000);
+ } else {
+ esp->min_period = ((5 * esp->ccycle) / 1000);
+ }
+
+ /* Reload the configuration registers */
+ esp_write(eregs->esp_cfact, esp->cfact);
+ esp->prev_stp = 0;
+ esp_write(eregs->esp_stp, 0);
+ esp->prev_soff = 0;
+ esp_write(eregs->esp_soff, 0);
+ esp_write(eregs->esp_timeo, esp->neg_defp);
+ esp->max_period = (esp->max_period + 3)>>2;
+ esp->min_period = (esp->min_period + 3)>>2;
+
+ esp_write(eregs->esp_cfg1, esp->config1);
+ switch(esp->erev) {
+ case esp100:
+ /* nothing to do */
+ break;
+ case esp100a:
+ esp_write(eregs->esp_cfg2, esp->config2);
+ break;
+ case esp236:
+ /* Slow 236 */
+ esp_write(eregs->esp_cfg2, esp->config2);
+ esp->prev_cfg3 = esp->config3[0];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ break;
+ case fas366:
+ panic("esp: FAS366 support not present, please notify "
+ "jongk@cs.utwente.nl");
+ break;
+ case fas216:
+ case fas236:
+ case fsc:
+ /* Fast ESP variants */
+ esp_write(eregs->esp_cfg2, esp->config2);
+ for(i=0; i<8; i++)
+ esp->config3[i] |= ESP_CONFIG3_FCLK;
+ esp->prev_cfg3 = esp->config3[0];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ if(esp->diff)
+ esp->radelay = 0;
+ else
+ esp->radelay = 16;
+ /* Different timeout constant for these chips */
+ esp->neg_defp =
+ FSC_NEG_DEFP(esp->cfreq,
+ (esp->cfact == ESP_CCF_F0 ?
+ ESP_CCF_F7 + 1 : esp->cfact));
+ esp_write(eregs->esp_timeo, esp->neg_defp);
+ /* Enable Active Negotiation if possible */
+ if((esp->erev == fsc) && !esp->diff)
+ esp_write(eregs->esp_cfg4, ESP_CONFIG4_EAN);
+ break;
+ case fas100a:
+ /* Fast 100a */
+ esp_write(eregs->esp_cfg2, esp->config2);
+ for(i=0; i<8; i++)
+ esp->config3[i] |= ESP_CONFIG3_FCLOCK;
+ esp->prev_cfg3 = esp->config3[0];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ esp->radelay = 32;
+ break;
+ default:
+ panic("esp: what could it be... I wonder...");
+ break;
+ };
+
+ /* Eat any bitrot in the chip */
+ trash = esp_read(eregs->esp_intrpt);
+ udelay(100);
+}
+
+/* This places the ESP into a known state at boot time. */
+void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ volatile unchar trash;
+
+ /* Reset the DMA */
+ if(esp->dma_reset)
+ esp->dma_reset(esp);
+
+ /* Reset the ESP */
+ esp_reset_esp(esp, eregs);
+
+ /* Reset the SCSI bus, but tell ESP not to generate an irq */
+ esp_write(eregs->esp_cfg1, (esp_read(eregs->esp_cfg1) | ESP_CONFIG1_SRRDISAB));
+ esp_cmd(esp, eregs, ESP_CMD_RS);
+ udelay(400);
+ esp_write(eregs->esp_cfg1, esp->config1);
+
+ /* Eat any bitrot in the chip and we are done... */
+ trash = esp_read(eregs->esp_intrpt);
+}
+
+/* Allocate structure and insert basic data such as SCSI chip frequency
+ * data and a pointer to the device
+ */
+struct NCR_ESP* esp_allocate(Scsi_Host_Template *tpnt, void *esp_dev)
+{
+ struct NCR_ESP *esp, *elink;
+ struct Scsi_Host *esp_host;
+
+ esp_host = scsi_register(tpnt, sizeof(struct NCR_ESP));
+ if(!esp_host)
+ panic("Cannot register ESP SCSI host");
+ esp = (struct NCR_ESP *) esp_host->hostdata;
+ if(!esp)
+ panic("No esp in hostdata");
+ esp->ehost = esp_host;
+ esp->edev = esp_dev;
+ esp->esp_id = nesps++;
+
+ /* Set bitshift value (only used on Amiga with multiple ESPs) */
+ esp->shift = 2;
+
+ /* Put into the chain of esp chips detected */
+ if(espchain) {
+ elink = espchain;
+ while(elink->next) elink = elink->next;
+ elink->next = esp;
+ } else {
+ espchain = esp;
+ }
+ esp->next = NULL;
+
+ return esp;
+}
+
+void esp_deallocate(struct NCR_ESP *esp)
+{
+ struct NCR_ESP *elink;
+
+ if(espchain == esp) {
+ espchain = NULL;
+ } else {
+ for(elink = espchain; elink && (elink->next != esp); elink = elink->next);
+ if(elink)
+ elink->next = esp->next;
+ }
+ nesps--;
+}
+
+/* Complete initialization of ESP structure and device
+ * Caller must have initialized appropriate parts of the ESP structure
+ * between the call to esp_allocate and this function.
+ */
+void esp_initialize(struct NCR_ESP *esp)
+{
+ struct ESP_regs *eregs = esp->eregs;
+ unsigned int fmhz;
+ unchar ccf;
+ int i;
+
+ /* Check out the clock properties of the chip. */
+
+ /* This is getting messy but it has to be done
+ * correctly or else you get weird behavior all
+ * over the place. We are trying to basically
+ * figure out three pieces of information.
+ *
+ * a) Clock Conversion Factor
+ *
+ * This is a representation of the input
+ * crystal clock frequency going into the
+ * ESP on this machine. Any operation whose
+ * timing is longer than 400ns depends on this
+ * value being correct. For example, you'll
+ * get blips for arbitration/selection during
+ * high load or with multiple targets if this
+ * is not set correctly.
+ *
+ * b) Selection Time-Out
+ *
+ * The ESP isn't very bright and will arbitrate
+ * for the bus and try to select a target
+ * forever if you let it. This value tells
+ * the ESP when it has taken too long to
+ * negotiate and that it should interrupt
+ * the CPU so we can see what happened.
+ * The value is computed as follows (from
+ * NCR/Symbios chip docs).
+ *
+ * (Time Out Period) * (Input Clock)
+ * STO = ----------------------------------
+ * (8192) * (Clock Conversion Factor)
+ *
+ * You usually want the time out period to be
+ * around 250ms, I think we'll set it a little
+ * bit higher to account for fully loaded SCSI
+ * bus's and slow devices that don't respond so
+ * quickly to selection attempts. (yeah, I know
+ * this is out of spec. but there is a lot of
+ * buggy pieces of firmware out there so bite me)
+ *
+ * c) Imperical constants for synchronous offset
+ * and transfer period register values
+ *
+ * This entails the smallest and largest sync
+ * period we could ever handle on this ESP.
+ */
+
+ fmhz = esp->cfreq;
+
+ if(fmhz <= (5000000))
+ ccf = 0;
+ else
+ ccf = (((5000000 - 1) + (fmhz))/(5000000));
+ if(!ccf || ccf > 8) {
+ /* If we can't find anything reasonable,
+ * just assume 20MHZ. This is the clock
+ * frequency of the older sun4c's where I've
+ * been unable to find the clock-frequency
+ * PROM property. All other machines provide
+ * useful values it seems.
+ */
+ ccf = ESP_CCF_F4;
+ fmhz = (20000000);
+ }
+ if(ccf==(ESP_CCF_F7+1))
+ esp->cfact = ESP_CCF_F0;
+ else if(ccf == ESP_CCF_NEVER)
+ esp->cfact = ESP_CCF_F2;
+ else
+ esp->cfact = ccf;
+ esp->cfreq = fmhz;
+ esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+ esp->ctick = ESP_TICK(ccf, esp->ccycle);
+ esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+ esp->sync_defp = SYNC_DEFP_SLOW;
+
+ printk("SCSI ID %d Clk %dMHz CCF=%d TOut %d ",
+ esp->scsi_id, (esp->cfreq / 1000000),
+ ccf, (int) esp->neg_defp);
+
+ /* Fill in ehost data */
+ esp->ehost->base = (unsigned long)eregs;
+ esp->ehost->this_id = esp->scsi_id;
+ esp->ehost->irq = esp->irq;
+
+ /* SCSI id mask */
+ esp->scsi_id_mask = (1 << esp->scsi_id);
+
+ /* Probe the revision of this esp */
+ esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+ esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ esp_write(eregs->esp_cfg2, esp->config2);
+ if((esp_read(eregs->esp_cfg2) & ~(ESP_CONFIG2_MAGIC)) !=
+ (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+ printk("NCR53C90(esp100)\n");
+ esp->erev = esp100;
+ } else {
+ esp->config2 = 0;
+ esp_write(eregs->esp_cfg2, 0);
+ esp_write(eregs->esp_cfg3, 5);
+ if(esp_read(eregs->esp_cfg3) != 5) {
+ printk("NCR53C90A(esp100a)\n");
+ esp->erev = esp100a;
+ } else {
+ int target;
+
+ for(target=0; target<8; target++)
+ esp->config3[target] = 0;
+ esp->prev_cfg3 = 0;
+ esp_write(eregs->esp_cfg3, 0);
+ if(ccf > ESP_CCF_F5) {
+ printk("NCR53C9XF(espfast)\n");
+ esp->erev = fast;
+ esp->sync_defp = SYNC_DEFP_FAST;
+ } else {
+ printk("NCR53C9x(esp236)\n");
+ esp->erev = esp236;
+ }
+ }
+ }
+
+ /* Initialize the command queues */
+ esp->current_SC = NULL;
+ esp->disconnected_SC = NULL;
+ esp->issue_SC = NULL;
+
+ /* Clear the state machines. */
+ esp->targets_present = 0;
+ esp->resetting_bus = 0;
+ esp->snip = 0;
+
+ init_waitqueue_head(&esp->reset_queue);
+
+ esp->fas_premature_intr_workaround = 0;
+ for(i = 0; i < 32; i++)
+ esp->espcmdlog[i] = 0;
+ esp->espcmdent = 0;
+ for(i = 0; i < 16; i++) {
+ esp->cur_msgout[i] = 0;
+ esp->cur_msgin[i] = 0;
+ }
+ esp->prevmsgout = esp->prevmsgin = 0;
+ esp->msgout_len = esp->msgin_len = 0;
+
+ /* Clear the one behind caches to hold unmatchable values. */
+ esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff;
+
+ /* Reset the thing before we try anything... */
+ esp_bootup_reset(esp, eregs);
+
+ esps_in_use++;
+}
+
+/* The info function will return whatever useful
+ * information the developer sees fit. If not provided, then
+ * the name field will be used instead.
+ */
+const char *esp_info(struct Scsi_Host *host)
+{
+ struct NCR_ESP *esp;
+
+ esp = (struct NCR_ESP *) host->hostdata;
+ switch(esp->erev) {
+ case esp100:
+ return "ESP100 (NCR53C90)";
+ case esp100a:
+ return "ESP100A (NCR53C90A)";
+ case esp236:
+ return "ESP236 (NCR53C9x)";
+ case fas216:
+ return "Emulex FAS216";
+ case fas236:
+ return "Emulex FAS236";
+ case fas366:
+ return "QLogic FAS366";
+ case fas100a:
+ return "FPESP100A";
+ case fsc:
+ return "Symbios Logic 53CF9x-2";
+ default:
+ panic("Bogon ESP revision");
+ };
+}
+
+/* From Wolfgang Stanglmeier's NCR scsi driver. */
+struct info_str
+{
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len)
+{
+ struct scsi_device *sdev;
+ struct info_str info;
+ int i;
+
+ info.buffer = ptr;
+ info.length = len;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "ESP Host Adapter:\n");
+ copy_info(&info, "\tESP Model\t\t");
+ switch(esp->erev) {
+ case esp100:
+ copy_info(&info, "ESP100 (NCR53C90)\n");
+ break;
+ case esp100a:
+ copy_info(&info, "ESP100A (NCR53C90A)\n");
+ break;
+ case esp236:
+ copy_info(&info, "ESP236 (NCR53C9x)\n");
+ break;
+ case fas216:
+ copy_info(&info, "Emulex FAS216\n");
+ break;
+ case fas236:
+ copy_info(&info, "Emulex FAS236\n");
+ break;
+ case fas100a:
+ copy_info(&info, "FPESP100A\n");
+ break;
+ case fast:
+ copy_info(&info, "Generic FAST\n");
+ break;
+ case fas366:
+ copy_info(&info, "QLogic FAS366\n");
+ break;
+ case fsc:
+ copy_info(&info, "Symbios Logic 53C9x-2\n");
+ break;
+ case espunknown:
+ default:
+ copy_info(&info, "Unknown!\n");
+ break;
+ };
+ copy_info(&info, "\tLive Targets\t\t[ ");
+ for(i = 0; i < 15; i++) {
+ if(esp->targets_present & (1 << i))
+ copy_info(&info, "%d ", i);
+ }
+ copy_info(&info, "]\n\n");
+
+ /* Now describe the state of each existing target. */
+ copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\n");
+
+ shost_for_each_device(sdev, esp->ehost) {
+ struct esp_device *esp_dev = sdev->hostdata;
+ uint id = sdev->id;
+
+ if (!(esp->targets_present & (1 << id)))
+ continue;
+
+ copy_info(&info, "%d\t\t", id);
+ copy_info(&info, "%08lx\t", esp->config3[id]);
+ copy_info(&info, "[%02lx,%02lx]\t\t\t",
+ esp_dev->sync_max_offset,
+ esp_dev->sync_min_period);
+ copy_info(&info, "%s\n", esp_dev->disconnect ? "yes" : "no");
+ }
+
+ return info.pos > info.offset? info.pos - info.offset : 0;
+}
+
+/* ESP proc filesystem code. */
+int esp_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length,
+ int inout)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *)shost->hostdata;
+
+ if(inout)
+ return -EINVAL; /* not yet */
+ if(start)
+ *start = buffer;
+ return esp_host_info(esp, buffer, offset, length);
+}
+
+static void esp_get_dmabufs(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ if(sp->use_sg == 0) {
+ sp->SCp.this_residual = sp->request_bufflen;
+ sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
+ sp->SCp.buffers_residual = 0;
+ if (esp->dma_mmu_get_scsi_one)
+ esp->dma_mmu_get_scsi_one(esp, sp);
+ else
+ sp->SCp.ptr =
+ (char *) virt_to_phys(sp->request_buffer);
+ } else {
+ sp->SCp.buffer = (struct scatterlist *) sp->buffer;
+ sp->SCp.buffers_residual = sp->use_sg - 1;
+ sp->SCp.this_residual = sp->SCp.buffer->length;
+ if (esp->dma_mmu_get_scsi_sgl)
+ esp->dma_mmu_get_scsi_sgl(esp, sp);
+ else
+ sp->SCp.ptr =
+ (char *) virt_to_phys((page_address(sp->SCp.buffer->page) + sp->SCp.buffer->offset));
+ }
+}
+
+static void esp_release_dmabufs(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ if(sp->use_sg == 0) {
+ if (esp->dma_mmu_release_scsi_one)
+ esp->dma_mmu_release_scsi_one(esp, sp);
+ } else {
+ if (esp->dma_mmu_release_scsi_sgl)
+ esp->dma_mmu_release_scsi_sgl(esp, sp);
+ }
+}
+
+static void esp_restore_pointers(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
+
+ sp->SCp.ptr = ep->saved_ptr;
+ sp->SCp.buffer = ep->saved_buffer;
+ sp->SCp.this_residual = ep->saved_this_residual;
+ sp->SCp.buffers_residual = ep->saved_buffers_residual;
+}
+
+static void esp_save_pointers(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
+
+ ep->saved_ptr = sp->SCp.ptr;
+ ep->saved_buffer = sp->SCp.buffer;
+ ep->saved_this_residual = sp->SCp.this_residual;
+ ep->saved_buffers_residual = sp->SCp.buffers_residual;
+}
+
+/* Some rules:
+ *
+ * 1) Never ever panic while something is live on the bus.
+ * If there is to be any chance of syncing the disks this
+ * rule is to be obeyed.
+ *
+ * 2) Any target that causes a foul condition will no longer
+ * have synchronous transfers done to it, no questions
+ * asked.
+ *
+ * 3) Keep register accesses to a minimum. Think about some
+ * day when we have Xbus machines this is running on and
+ * the ESP chip is on the other end of the machine on a
+ * different board from the cpu where this is running.
+ */
+
+/* Fire off a command. We assume the bus is free and that the only
+ * case where we could see an interrupt is where we have disconnected
+ * commands active and they are trying to reselect us.
+ */
+static inline void esp_check_cmd(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ switch(sp->cmd_len) {
+ case 6:
+ case 10:
+ case 12:
+ esp->esp_slowcmd = 0;
+ break;
+
+ default:
+ esp->esp_slowcmd = 1;
+ esp->esp_scmdleft = sp->cmd_len;
+ esp->esp_scmdp = &sp->cmnd[0];
+ break;
+ };
+}
+
+static inline void build_sync_nego_msg(struct NCR_ESP *esp, int period, int offset)
+{
+ esp->cur_msgout[0] = EXTENDED_MESSAGE;
+ esp->cur_msgout[1] = 3;
+ esp->cur_msgout[2] = EXTENDED_SDTR;
+ esp->cur_msgout[3] = period;
+ esp->cur_msgout[4] = offset;
+ esp->msgout_len = 5;
+}
+
+static void esp_exec_cmd(struct NCR_ESP *esp)
+{
+ struct ESP_regs *eregs = esp->eregs;
+ struct esp_device *esp_dev;
+ Scsi_Cmnd *SCptr;
+ Scsi_Device *SDptr;
+ volatile unchar *cmdp = esp->esp_command;
+ unsigned char the_esp_command;
+ int lun, target;
+ int i;
+
+ /* Hold off if we have disconnected commands and
+ * an IRQ is showing...
+ */
+ if(esp->disconnected_SC && esp->dma_irq_p(esp))
+ return;
+
+ /* Grab first member of the issue queue. */
+ SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
+
+ /* Safe to panic here because current_SC is null. */
+ if(!SCptr)
+ panic("esp: esp_exec_cmd and issue queue is NULL");
+
+ SDptr = SCptr->device;
+ esp_dev = SDptr->hostdata;
+ lun = SCptr->device->lun;
+ target = SCptr->device->id;
+
+ esp->snip = 0;
+ esp->msgout_len = 0;
+
+ /* Send it out whole, or piece by piece? The ESP
+ * only knows how to automatically send out 6, 10,
+ * and 12 byte commands. I used to think that the
+ * Linux SCSI code would never throw anything other
+ * than that to us, but then again there is the
+ * SCSI generic driver which can send us anything.
+ */
+ esp_check_cmd(esp, SCptr);
+
+ /* If arbitration/selection is successful, the ESP will leave
+ * ATN asserted, causing the target to go into message out
+ * phase. The ESP will feed the target the identify and then
+ * the target can only legally go to one of command,
+ * datain/out, status, or message in phase, or stay in message
+ * out phase (should we be trying to send a sync negotiation
+ * message after the identify). It is not allowed to drop
+ * BSY, but some buggy targets do and we check for this
+ * condition in the selection complete code. Most of the time
+ * we'll make the command bytes available to the ESP and it
+ * will not interrupt us until it finishes command phase, we
+ * cannot do this for command sizes the ESP does not
+ * understand and in this case we'll get interrupted right
+ * when the target goes into command phase.
+ *
+ * It is absolutely _illegal_ in the presence of SCSI-2 devices
+ * to use the ESP select w/o ATN command. When SCSI-2 devices are
+ * present on the bus we _must_ always go straight to message out
+ * phase with an identify message for the target. Being that
+ * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
+ * selections should not confuse SCSI-1 we hope.
+ */
+
+ if(esp_dev->sync) {
+ /* this targets sync is known */
+#ifdef CONFIG_SCSI_MAC_ESP
+do_sync_known:
+#endif
+ if(esp_dev->disconnect)
+ *cmdp++ = IDENTIFY(1, lun);
+ else
+ *cmdp++ = IDENTIFY(0, lun);
+
+ if(esp->esp_slowcmd) {
+ the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+ esp_advance_phase(SCptr, in_slct_stop);
+ } else {
+ the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+ esp_advance_phase(SCptr, in_slct_norm);
+ }
+ } else if(!(esp->targets_present & (1<<target)) || !(esp_dev->disconnect)) {
+ /* After the bootup SCSI code sends both the
+ * TEST_UNIT_READY and INQUIRY commands we want
+ * to at least attempt allowing the device to
+ * disconnect.
+ */
+ ESPMISC(("esp: Selecting device for first time. target=%d "
+ "lun=%d\n", target, SCptr->device->lun));
+ if(!SDptr->borken && !esp_dev->disconnect)
+ esp_dev->disconnect = 1;
+
+ *cmdp++ = IDENTIFY(0, lun);
+ esp->prevmsgout = NOP;
+ esp_advance_phase(SCptr, in_slct_norm);
+ the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+
+ /* Take no chances... */
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ } else {
+ int toshiba_cdrom_hwbug_wkaround = 0;
+
+#ifdef CONFIG_SCSI_MAC_ESP
+ /* Never allow synchronous transfers (disconnect OK) on
+ * Macintosh. Well, maybe later when we figured out how to
+ * do DMA on the machines that support it ...
+ */
+ esp_dev->disconnect = 1;
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync = 1;
+ esp->snip = 0;
+ goto do_sync_known;
+#endif
+ /* We've talked to this guy before,
+ * but never negotiated. Let's try
+ * sync negotiation.
+ */
+ if(!SDptr->borken) {
+ if((SDptr->type == TYPE_ROM) &&
+ (!strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+ /* Nice try sucker... */
+ ESPMISC(("esp%d: Disabling sync for buggy "
+ "Toshiba CDROM.\n", esp->esp_id));
+ toshiba_cdrom_hwbug_wkaround = 1;
+ build_sync_nego_msg(esp, 0, 0);
+ } else {
+ build_sync_nego_msg(esp, esp->sync_defp, 15);
+ }
+ } else {
+ build_sync_nego_msg(esp, 0, 0);
+ }
+ esp_dev->sync = 1;
+ esp->snip = 1;
+
+ /* A fix for broken SCSI1 targets, when they disconnect
+ * they lock up the bus and confuse ESP. So disallow
+ * disconnects for SCSI1 targets for now until we
+ * find a better fix.
+ *
+ * Addendum: This is funny, I figured out what was going
+ * on. The blotzed SCSI1 target would disconnect,
+ * one of the other SCSI2 targets or both would be
+ * disconnected as well. The SCSI1 target would
+ * stay disconnected long enough that we start
+ * up a command on one of the SCSI2 targets. As
+ * the ESP is arbitrating for the bus the SCSI1
+ * target begins to arbitrate as well to reselect
+ * the ESP. The SCSI1 target refuses to drop it's
+ * ID bit on the data bus even though the ESP is
+ * at ID 7 and is the obvious winner for any
+ * arbitration. The ESP is a poor sport and refuses
+ * to lose arbitration, it will continue indefinitely
+ * trying to arbitrate for the bus and can only be
+ * stopped via a chip reset or SCSI bus reset.
+ * Therefore _no_ disconnects for SCSI1 targets
+ * thank you very much. ;-)
+ */
+ if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) ||
+ toshiba_cdrom_hwbug_wkaround || SDptr->borken) {
+ ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
+ "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
+ esp_dev->disconnect = 0;
+ *cmdp++ = IDENTIFY(0, lun);
+ } else {
+ *cmdp++ = IDENTIFY(1, lun);
+ }
+
+ /* ESP fifo is only so big...
+ * Make this look like a slow command.
+ */
+ esp->esp_slowcmd = 1;
+ esp->esp_scmdleft = SCptr->cmd_len;
+ esp->esp_scmdp = &SCptr->cmnd[0];
+
+ the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+ esp_advance_phase(SCptr, in_slct_msg);
+ }
+
+ if(!esp->esp_slowcmd)
+ for(i = 0; i < SCptr->cmd_len; i++)
+ *cmdp++ = SCptr->cmnd[i];
+
+ esp_write(eregs->esp_busid, (target & 7));
+ if (esp->prev_soff != esp_dev->sync_max_offset ||
+ esp->prev_stp != esp_dev->sync_min_period ||
+ (esp->erev > esp100a &&
+ esp->prev_cfg3 != esp->config3[target])) {
+ esp->prev_soff = esp_dev->sync_max_offset;
+ esp_write(eregs->esp_soff, esp->prev_soff);
+ esp->prev_stp = esp_dev->sync_min_period;
+ esp_write(eregs->esp_stp, esp->prev_stp);
+ if(esp->erev > esp100a) {
+ esp->prev_cfg3 = esp->config3[target];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ }
+ }
+ i = (cmdp - esp->esp_command);
+
+ /* Set up the DMA and ESP counters */
+ if(esp->do_pio_cmds){
+ int j = 0;
+
+ /*
+ * XXX MSch:
+ *
+ * It seems this is required, at least to clean up
+ * after failed commands when using PIO mode ...
+ */
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+ for(;j<i;j++)
+ esp_write(eregs->esp_fdata, esp->esp_command[j]);
+ the_esp_command &= ~ESP_CMD_DMA;
+
+ /* Tell ESP to "go". */
+ esp_cmd(esp, eregs, the_esp_command);
+ } else {
+ /* Set up the ESP counters */
+ esp_write(eregs->esp_tclow, i);
+ esp_write(eregs->esp_tcmed, 0);
+ esp->dma_init_write(esp, esp->esp_command_dvma, i);
+
+ /* Tell ESP to "go". */
+ esp_cmd(esp, eregs, the_esp_command);
+ }
+}
+
+/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
+int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ struct NCR_ESP *esp;
+
+ /* Set up func ptr and initial driver cmd-phase. */
+ SCpnt->scsi_done = done;
+ SCpnt->SCp.phase = not_issued;
+
+ esp = (struct NCR_ESP *) SCpnt->device->host->hostdata;
+
+ if(esp->dma_led_on)
+ esp->dma_led_on(esp);
+
+ /* We use the scratch area. */
+ ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->device->id, SCpnt->lun));
+ ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->lun));
+
+ esp_get_dmabufs(esp, SCpnt);
+ esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */
+
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0xff;
+ SCpnt->SCp.sent_command = 0;
+
+ /* Place into our queue. */
+ if(SCpnt->cmnd[0] == REQUEST_SENSE) {
+ ESPQUEUE(("RQSENSE\n"));
+ prepend_SC(&esp->issue_SC, SCpnt);
+ } else {
+ ESPQUEUE(("\n"));
+ append_SC(&esp->issue_SC, SCpnt);
+ }
+
+ /* Run it now if we can. */
+ if(!esp->current_SC && !esp->resetting_bus)
+ esp_exec_cmd(esp);
+
+ return 0;
+}
+
+/* Dump driver state. */
+static void esp_dump_cmd(Scsi_Cmnd *SCptr)
+{
+ ESPLOG(("[tgt<%02x> lun<%02x> "
+ "pphase<%s> cphase<%s>]",
+ SCptr->device->id, SCptr->device->lun,
+ phase_string(SCptr->SCp.sent_command),
+ phase_string(SCptr->SCp.phase)));
+}
+
+static void esp_dump_state(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+#ifdef DEBUG_ESP_CMDS
+ int i;
+#endif
+
+ ESPLOG(("esp%d: dumping state\n", esp->esp_id));
+
+ /* Print DMA status */
+ esp->dma_dump_state(esp);
+
+ ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+ esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
+ ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+ esp->esp_id, esp_read(eregs->esp_status), esp_read(eregs->esp_sstep),
+ esp_read(eregs->esp_intrpt)));
+#ifdef DEBUG_ESP_CMDS
+ printk("esp%d: last ESP cmds [", esp->esp_id);
+ i = (esp->espcmdent - 1) & 31;
+ printk("<");
+ esp_print_cmd(esp->espcmdlog[i]);
+ printk(">");
+ i = (i - 1) & 31;
+ printk("<");
+ esp_print_cmd(esp->espcmdlog[i]);
+ printk(">");
+ i = (i - 1) & 31;
+ printk("<");
+ esp_print_cmd(esp->espcmdlog[i]);
+ printk(">");
+ i = (i - 1) & 31;
+ printk("<");
+ esp_print_cmd(esp->espcmdlog[i]);
+ printk(">");
+ printk("]\n");
+#endif /* (DEBUG_ESP_CMDS) */
+
+ if(SCptr) {
+ ESPLOG(("esp%d: current command ", esp->esp_id));
+ esp_dump_cmd(SCptr);
+ }
+ ESPLOG(("\n"));
+ SCptr = esp->disconnected_SC;
+ ESPLOG(("esp%d: disconnected ", esp->esp_id));
+ while(SCptr) {
+ esp_dump_cmd(SCptr);
+ SCptr = (Scsi_Cmnd *) SCptr->host_scribble;
+ }
+ ESPLOG(("\n"));
+}
+
+/* Abort a command. The host_lock is acquired by caller. */
+int esp_abort(Scsi_Cmnd *SCptr)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->device->host->hostdata;
+ struct ESP_regs *eregs = esp->eregs;
+ int don;
+
+ ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
+ esp_dump_state(esp, eregs);
+
+ /* Wheee, if this is the current command on the bus, the
+ * best we can do is assert ATN and wait for msgout phase.
+ * This should even fix a hung SCSI bus when we lose state
+ * in the driver and timeout because the eventual phase change
+ * will cause the ESP to (eventually) give an interrupt.
+ */
+ if(esp->current_SC == SCptr) {
+ esp->cur_msgout[0] = ABORT;
+ esp->msgout_len = 1;
+ esp->msgout_ctr = 0;
+ esp_cmd(esp, eregs, ESP_CMD_SATN);
+ return SUCCESS;
+ }
+
+ /* If it is still in the issue queue then we can safely
+ * call the completion routine and report abort success.
+ */
+ don = esp->dma_ports_p(esp);
+ if(don) {
+ esp->dma_ints_off(esp);
+ synchronize_irq(esp->irq);
+ }
+ if(esp->issue_SC) {
+ Scsi_Cmnd **prev, *this;
+ for(prev = (&esp->issue_SC), this = esp->issue_SC;
+ this;
+ prev = (Scsi_Cmnd **) &(this->host_scribble),
+ this = (Scsi_Cmnd *) this->host_scribble) {
+ if(this == SCptr) {
+ *prev = (Scsi_Cmnd *) this->host_scribble;
+ this->host_scribble = NULL;
+ esp_release_dmabufs(esp, this);
+ this->result = DID_ABORT << 16;
+ this->done(this);
+ if(don)
+ esp->dma_ints_on(esp);
+ return SUCCESS;
+ }
+ }
+ }
+
+ /* Yuck, the command to abort is disconnected, it is not
+ * worth trying to abort it now if something else is live
+ * on the bus at this time. So, we let the SCSI code wait
+ * a little bit and try again later.
+ */
+ if(esp->current_SC) {
+ if(don)
+ esp->dma_ints_on(esp);
+ return FAILED;
+ }
+
+ /* It's disconnected, we have to reconnect to re-establish
+ * the nexus and tell the device to abort. However, we really
+ * cannot 'reconnect' per se. Don't try to be fancy, just
+ * indicate failure, which causes our caller to reset the whole
+ * bus.
+ */
+
+ if(don)
+ esp->dma_ints_on(esp);
+ return FAILED;
+}
+
+/* We've sent ESP_CMD_RS to the ESP, the interrupt had just
+ * arrived indicating the end of the SCSI bus reset. Our job
+ * is to clean out the command queues and begin re-execution
+ * of SCSI commands once more.
+ */
+static int esp_finish_reset(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *sp = esp->current_SC;
+
+ /* Clean up currently executing command, if any. */
+ if (sp != NULL) {
+ esp_release_dmabufs(esp, sp);
+ sp->result = (DID_RESET << 16);
+ sp->scsi_done(sp);
+ esp->current_SC = NULL;
+ }
+
+ /* Clean up disconnected queue, they have been invalidated
+ * by the bus reset.
+ */
+ if (esp->disconnected_SC) {
+ while((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) {
+ esp_release_dmabufs(esp, sp);
+ sp->result = (DID_RESET << 16);
+ sp->scsi_done(sp);
+ }
+ }
+
+ /* SCSI bus reset is complete. */
+ esp->resetting_bus = 0;
+ wake_up(&esp->reset_queue);
+
+ /* Ok, now it is safe to get commands going once more. */
+ if(esp->issue_SC)
+ esp_exec_cmd(esp);
+
+ return do_intr_end;
+}
+
+static int esp_do_resetbus(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
+ esp->resetting_bus = 1;
+ esp_cmd(esp, eregs, ESP_CMD_RS);
+
+ return do_intr_end;
+}
+
+/* Reset ESP chip, reset hanging bus, then kill active and
+ * disconnected commands for targets without soft reset.
+ *
+ * The host_lock is acquired by caller.
+ */
+int esp_reset(Scsi_Cmnd *SCptr)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->device->host->hostdata;
+
+ (void) esp_do_resetbus(esp, esp->eregs);
+
+ spin_unlock_irq(esp->ehost->host_lock);
+
+ wait_event(esp->reset_queue, (esp->resetting_bus == 0));
+
+ spin_lock_irq(esp->ehost->host_lock);
+
+ return SUCCESS;
+}
+
+/* Internal ESP done function. */
+static void esp_done(struct NCR_ESP *esp, int error)
+{
+ Scsi_Cmnd *done_SC;
+
+ if(esp->current_SC) {
+ done_SC = esp->current_SC;
+ esp->current_SC = NULL;
+ esp_release_dmabufs(esp, done_SC);
+ done_SC->result = error;
+ done_SC->scsi_done(done_SC);
+
+ /* Bus is free, issue any commands in the queue. */
+ if(esp->issue_SC && !esp->current_SC)
+ esp_exec_cmd(esp);
+ } else {
+ /* Panic is safe as current_SC is null so we may still
+ * be able to accept more commands to sync disk buffers.
+ */
+ ESPLOG(("panicing\n"));
+ panic("esp: done() called with NULL esp->current_SC");
+ }
+}
+
+/* Wheee, ESP interrupt engine. */
+
+/* Forward declarations. */
+static int esp_do_phase_determine(struct NCR_ESP *esp,
+ struct ESP_regs *eregs);
+static int esp_do_data_finale(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs);
+
+#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP)
+#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP)
+
+/* We try to avoid some interrupts by jumping ahead and see if the ESP
+ * has gotten far enough yet. Hence the following.
+ */
+static inline int skipahead1(struct NCR_ESP *esp, struct ESP_regs *eregs,
+ Scsi_Cmnd *scp, int prev_phase, int new_phase)
+{
+ if(scp->SCp.sent_command != prev_phase)
+ return 0;
+
+ if(esp->dma_irq_p(esp)) {
+ /* Yes, we are able to save an interrupt. */
+ esp->sreg = (esp_read(eregs->esp_status) & ~(ESP_STAT_INTR));
+ esp->ireg = esp_read(eregs->esp_intrpt);
+ if(!(esp->ireg & ESP_INTR_SR))
+ return 0;
+ else
+ return do_reset_complete;
+ }
+ /* Ho hum, target is taking forever... */
+ scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+ return do_intr_end;
+}
+
+static inline int skipahead2(struct NCR_ESP *esp,
+ struct ESP_regs *eregs,
+ Scsi_Cmnd *scp, int prev_phase1, int prev_phase2,
+ int new_phase)
+{
+ if(scp->SCp.sent_command != prev_phase1 &&
+ scp->SCp.sent_command != prev_phase2)
+ return 0;
+ if(esp->dma_irq_p(esp)) {
+ /* Yes, we are able to save an interrupt. */
+ esp->sreg = (esp_read(eregs->esp_status) & ~(ESP_STAT_INTR));
+ esp->ireg = esp_read(eregs->esp_intrpt);
+ if(!(esp->ireg & ESP_INTR_SR))
+ return 0;
+ else
+ return do_reset_complete;
+ }
+ /* Ho hum, target is taking forever... */
+ scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+ return do_intr_end;
+}
+
+/* Misc. esp helper macros. */
+#define esp_setcount(__eregs, __cnt) \
+ esp_write((__eregs)->esp_tclow, ((__cnt) & 0xff)); \
+ esp_write((__eregs)->esp_tcmed, (((__cnt) >> 8) & 0xff))
+
+#define esp_getcount(__eregs) \
+ ((esp_read((__eregs)->esp_tclow)&0xff) | \
+ ((esp_read((__eregs)->esp_tcmed)&0xff) << 8))
+
+#define fcount(__esp, __eregs) \
+ (esp_read((__eregs)->esp_fflags) & ESP_FF_FBYTES)
+
+#define fnzero(__esp, __eregs) \
+ (esp_read((__eregs)->esp_fflags) & ESP_FF_ONOTZERO)
+
+/* XXX speculative nops unnecessary when continuing amidst a data phase
+ * XXX even on esp100!!! another case of flooding the bus with I/O reg
+ * XXX writes...
+ */
+#define esp_maybe_nop(__esp, __eregs) \
+ if((__esp)->erev == esp100) \
+ esp_cmd((__esp), (__eregs), ESP_CMD_NULL)
+
+#define sreg_to_dataphase(__sreg) \
+ ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain)
+
+/* The ESP100 when in synchronous data phase, can mistake a long final
+ * REQ pulse from the target as an extra byte, it places whatever is on
+ * the data lines into the fifo. For now, we will assume when this
+ * happens that the target is a bit quirky and we don't want to
+ * be talking synchronously to it anyways. Regardless, we need to
+ * tell the ESP to eat the extraneous byte so that we can proceed
+ * to the next phase.
+ */
+static inline int esp100_sync_hwbug(struct NCR_ESP *esp, struct ESP_regs *eregs,
+ Scsi_Cmnd *sp, int fifocnt)
+{
+ /* Do not touch this piece of code. */
+ if((!(esp->erev == esp100)) ||
+ (!(sreg_datainp((esp->sreg = esp_read(eregs->esp_status))) && !fifocnt) &&
+ !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) {
+ if(sp->SCp.phase == in_dataout)
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ return 0;
+ } else {
+ /* Async mode for this guy. */
+ build_sync_nego_msg(esp, 0, 0);
+
+ /* Ack the bogus byte, but set ATN first. */
+ esp_cmd(esp, eregs, ESP_CMD_SATN);
+ esp_cmd(esp, eregs, ESP_CMD_MOK);
+ return 1;
+ }
+}
+
+/* This closes the window during a selection with a reselect pending, because
+ * we use DMA for the selection process the FIFO should hold the correct
+ * contents if we get reselected during this process. So we just need to
+ * ack the possible illegal cmd interrupt pending on the esp100.
+ */
+static inline int esp100_reconnect_hwbug(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ volatile unchar junk;
+
+ if(esp->erev != esp100)
+ return 0;
+ junk = esp_read(eregs->esp_intrpt);
+
+ if(junk & ESP_INTR_SR)
+ return 1;
+ return 0;
+}
+
+/* This verifies the BUSID bits during a reselection so that we know which
+ * target is talking to us.
+ */
+static inline int reconnect_target(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ int it, me = esp->scsi_id_mask, targ = 0;
+
+ if(2 != fcount(esp, eregs))
+ return -1;
+ it = esp_read(eregs->esp_fdata);
+ if(!(it & me))
+ return -1;
+ it &= ~me;
+ if(it & (it - 1))
+ return -1;
+ while(!(it & 1))
+ targ++, it >>= 1;
+ return targ;
+}
+
+/* This verifies the identify from the target so that we know which lun is
+ * being reconnected.
+ */
+static inline int reconnect_lun(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ int lun;
+
+ if((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
+ return -1;
+ lun = esp_read(eregs->esp_fdata);
+
+ /* Yes, you read this correctly. We report lun of zero
+ * if we see parity error. ESP reports parity error for
+ * the lun byte, and this is the only way to hope to recover
+ * because the target is connected.
+ */
+ if(esp->sreg & ESP_STAT_PERR)
+ return 0;
+
+ /* Check for illegal bits being set in the lun. */
+ if((lun & 0x40) || !(lun & 0x80))
+ return -1;
+
+ return lun & 7;
+}
+
+/* This puts the driver in a state where it can revitalize a command that
+ * is being continued due to reselection.
+ */
+static inline void esp_connect(struct NCR_ESP *esp, struct ESP_regs *eregs,
+ Scsi_Cmnd *sp)
+{
+ Scsi_Device *dp = sp->device;
+ struct esp_device *esp_dev = dp->hostdata;
+
+ if(esp->prev_soff != esp_dev->sync_max_offset ||
+ esp->prev_stp != esp_dev->sync_min_period ||
+ (esp->erev > esp100a &&
+ esp->prev_cfg3 != esp->config3[sp->device->id])) {
+ esp->prev_soff = esp_dev->sync_max_offset;
+ esp_write(eregs->esp_soff, esp->prev_soff);
+ esp->prev_stp = esp_dev->sync_min_period;
+ esp_write(eregs->esp_stp, esp->prev_stp);
+ if(esp->erev > esp100a) {
+ esp->prev_cfg3 = esp->config3[sp->device->id];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ }
+ }
+ esp->current_SC = sp;
+}
+
+/* This will place the current working command back into the issue queue
+ * if we are to receive a reselection amidst a selection attempt.
+ */
+static inline void esp_reconnect(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ if(!esp->disconnected_SC)
+ ESPLOG(("esp%d: Weird, being reselected but disconnected "
+ "command queue is empty.\n", esp->esp_id));
+ esp->snip = 0;
+ esp->current_SC = NULL;
+ sp->SCp.phase = not_issued;
+ append_SC(&esp->issue_SC, sp);
+}
+
+/* Begin message in phase. */
+static int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ esp_maybe_nop(esp, eregs);
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ esp->msgin_len = 1;
+ esp->msgin_ctr = 0;
+ esp_advance_phase(esp->current_SC, in_msgindone);
+ return do_work_bus;
+}
+
+static inline void advance_sg(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ ++sp->SCp.buffer;
+ --sp->SCp.buffers_residual;
+ sp->SCp.this_residual = sp->SCp.buffer->length;
+ if (esp->dma_advance_sg)
+ esp->dma_advance_sg (sp);
+ else
+ sp->SCp.ptr = (char *) virt_to_phys((page_address(sp->SCp.buffer->page) + sp->SCp.buffer->offset));
+
+}
+
+/* Please note that the way I've coded these routines is that I _always_
+ * check for a disconnect during any and all information transfer
+ * phases. The SCSI standard states that the target _can_ cause a BUS
+ * FREE condition by dropping all MSG/CD/IO/BSY signals. Also note
+ * that during information transfer phases the target controls every
+ * change in phase, the only thing the initiator can do is "ask" for
+ * a message out phase by driving ATN true. The target can, and sometimes
+ * will, completely ignore this request so we cannot assume anything when
+ * we try to force a message out phase to abort/reset a target. Most of
+ * the time the target will eventually be nice and go to message out, so
+ * we may have to hold on to our state about what we want to tell the target
+ * for some period of time.
+ */
+
+/* I think I have things working here correctly. Even partial transfers
+ * within a buffer or sub-buffer should not upset us at all no matter
+ * how bad the target and/or ESP fucks things up.
+ */
+static int esp_do_data(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ int thisphase, hmuch;
+
+ ESPDATA(("esp_do_data: "));
+ esp_maybe_nop(esp, eregs);
+ thisphase = sreg_to_dataphase(esp->sreg);
+ esp_advance_phase(SCptr, thisphase);
+ ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
+ hmuch = esp->dma_can_transfer(esp, SCptr);
+
+ /*
+ * XXX MSch: cater for PIO transfer here; PIO used if hmuch == 0
+ */
+ if (hmuch) { /* DMA */
+ /*
+ * DMA
+ */
+ ESPDATA(("hmuch<%d> ", hmuch));
+ esp->current_transfer_size = hmuch;
+ esp_setcount(eregs, (esp->fas_premature_intr_workaround ?
+ (hmuch + 0x40) : hmuch));
+ esp->dma_setup(esp, (__u32)((unsigned long)SCptr->SCp.ptr),
+ hmuch, (thisphase == in_datain));
+ ESPDATA(("DMA|TI --> do_intr_end\n"));
+ esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+ return do_intr_end;
+ /*
+ * end DMA
+ */
+ } else {
+ /*
+ * PIO
+ */
+ int oldphase, i = 0; /* or where we left off last time ?? esp->current_data ?? */
+ int fifocnt = 0;
+
+ oldphase = esp_read(eregs->esp_status) & ESP_STAT_PMASK;
+
+ /*
+ * polled transfer; ugly, can we make this happen in a DRQ
+ * interrupt handler ??
+ * requires keeping track of state information in host or
+ * command struct!
+ * Problem: I've never seen a DRQ happen on Mac, not even
+ * with ESP_CMD_DMA ...
+ */
+
+ /* figure out how much needs to be transferred */
+ hmuch = SCptr->SCp.this_residual;
+ ESPDATA(("hmuch<%d> pio ", hmuch));
+ esp->current_transfer_size = hmuch;
+
+ /* tell the ESP ... */
+ esp_setcount(eregs, hmuch);
+
+ /* loop */
+ while (hmuch) {
+ int j, fifo_stuck = 0, newphase;
+ unsigned long flags, timeout;
+#if 0
+ if ( i % 10 )
+ ESPDATA(("\r"));
+ else
+ ESPDATA(( /*"\n"*/ "\r"));
+#endif
+#if 0
+ local_irq_save(flags);
+#endif
+ if(thisphase == in_datain) {
+ /* 'go' ... */
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+
+ /* wait for data */
+ timeout = 1000000;
+ while (!((esp->sreg=esp_read(eregs->esp_status)) & ESP_STAT_INTR) && --timeout)
+ udelay(2);
+ if (timeout == 0)
+ printk("DRQ datain timeout! \n");
+
+ newphase = esp->sreg & ESP_STAT_PMASK;
+
+ /* see how much we got ... */
+ fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);
+
+ if (!fifocnt)
+ fifo_stuck++;
+ else
+ fifo_stuck = 0;
+
+ ESPDATA(("\rgot %d st %x ph %x", fifocnt, esp->sreg, newphase));
+
+ /* read fifo */
+ for(j=0;j<fifocnt;j++)
+ SCptr->SCp.ptr[i++] = esp_read(eregs->esp_fdata);
+
+ ESPDATA(("(%d) ", i));
+
+ /* how many to go ?? */
+ hmuch -= fifocnt;
+
+ /* break if status phase !! */
+ if(newphase == ESP_STATP) {
+ /* clear int. */
+ esp->ireg = esp_read(eregs->esp_intrpt);
+ break;
+ }
+ } else {
+#define MAX_FIFO 8
+ /* how much will fit ? */
+ int this_count = MAX_FIFO - fifocnt;
+ if (this_count > hmuch)
+ this_count = hmuch;
+
+ /* fill fifo */
+ for(j=0;j<this_count;j++)
+ esp_write(eregs->esp_fdata, SCptr->SCp.ptr[i++]);
+
+ /* how many left if this goes out ?? */
+ hmuch -= this_count;
+
+ /* 'go' ... */
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+
+ /* wait for 'got it' */
+ timeout = 1000000;
+ while (!((esp->sreg=esp_read(eregs->esp_status)) & ESP_STAT_INTR) && --timeout)
+ udelay(2);
+ if (timeout == 0)
+ printk("DRQ dataout timeout! \n");
+
+ newphase = esp->sreg & ESP_STAT_PMASK;
+
+ /* need to check how much was sent ?? */
+ fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);
+
+ ESPDATA(("\rsent %d st %x ph %x", this_count - fifocnt, esp->sreg, newphase));
+
+ ESPDATA(("(%d) ", i));
+
+ /* break if status phase !! */
+ if(newphase == ESP_STATP) {
+ /* clear int. */
+ esp->ireg = esp_read(eregs->esp_intrpt);
+ break;
+ }
+
+ }
+
+ /* clear int. */
+ esp->ireg = esp_read(eregs->esp_intrpt);
+
+ ESPDATA(("ir %x ... ", esp->ireg));
+
+ if (hmuch == 0)
+ ESPDATA(("done! \n"));
+
+#if 0
+ local_irq_restore(flags);
+#endif
+
+ /* check new bus phase */
+ if (newphase != oldphase && i < esp->current_transfer_size) {
+ /* something happened; disconnect ?? */
+ ESPDATA(("phase change, dropped out with %d done ... ", i));
+ break;
+ }
+
+ /* check int. status */
+ if (esp->ireg & ESP_INTR_DC) {
+ /* disconnect */
+ ESPDATA(("disconnect; %d transferred ... ", i));
+ break;
+ } else if (esp->ireg & ESP_INTR_FDONE) {
+ /* function done */
+ ESPDATA(("function done; %d transferred ... ", i));
+ break;
+ }
+
+ /* XXX fixme: bail out on stall */
+ if (fifo_stuck > 10) {
+ /* we're stuck */
+ ESPDATA(("fifo stall; %d transferred ... ", i));
+ break;
+ }
+ }
+
+ ESPDATA(("\n"));
+ /* check successful completion ?? */
+
+ if (thisphase == in_dataout)
+ hmuch += fifocnt; /* stuck?? adjust data pointer ...*/
+
+ /* tell do_data_finale how much was transferred */
+ esp->current_transfer_size -= hmuch;
+
+ /* still not completely sure on this one ... */
+ return /*do_intr_end*/ do_work_bus /*do_phase_determine*/ ;
+
+ /*
+ * end PIO
+ */
+ }
+ return do_intr_end;
+}
+
+/* See how successful the data transfer was. */
+static int esp_do_data_finale(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;
+
+ if(esp->dma_led_off)
+ esp->dma_led_off(esp);
+
+ ESPDATA(("esp_do_data_finale: "));
+
+ if(SCptr->SCp.phase == in_datain) {
+ if(esp->sreg & ESP_STAT_PERR) {
+ /* Yuck, parity error. The ESP asserts ATN
+ * so that we can go to message out phase
+ * immediately and inform the target that
+ * something bad happened.
+ */
+ ESPLOG(("esp%d: data bad parity detected.\n",
+ esp->esp_id));
+ esp->cur_msgout[0] = INITIATOR_ERROR;
+ esp->msgout_len = 1;
+ }
+ if(esp->dma_drain)
+ esp->dma_drain(esp);
+ }
+ if(esp->dma_invalidate)
+ esp->dma_invalidate(esp);
+
+ /* This could happen for the above parity error case. */
+ if(!(esp->ireg == ESP_INTR_BSERV)) {
+ /* Please go to msgout phase, please please please... */
+ ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
+ esp->esp_id));
+ return esp_do_phase_determine(esp, eregs);
+ }
+
+ /* Check for partial transfers and other horrible events. */
+ fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);
+ ecount = esp_getcount(eregs);
+ if(esp->fas_premature_intr_workaround)
+ ecount -= 0x40;
+ bytes_sent = esp->current_transfer_size;
+
+ ESPDATA(("trans_sz=%d, ", bytes_sent));
+ if(!(esp->sreg & ESP_STAT_TCNT))
+ bytes_sent -= ecount;
+ if(SCptr->SCp.phase == in_dataout)
+ bytes_sent -= fifocnt;
+
+ ESPDATA(("bytes_sent=%d (ecount=%d, fifocnt=%d), ", bytes_sent,
+ ecount, fifocnt));
+
+ /* If we were in synchronous mode, check for peculiarities. */
+ if(esp_dev->sync_max_offset)
+ bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt);
+ else
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+ /* Until we are sure of what has happened, we are certainly
+ * in the dark.
+ */
+ esp_advance_phase(SCptr, in_the_dark);
+
+ /* Check for premature interrupt condition. Can happen on FAS2x6
+ * chips. QLogic recommends a workaround by overprogramming the
+ * transfer counters, but this makes doing scatter-gather impossible.
+ * Until there is a way to disable scatter-gather for a single target,
+ * and not only for the entire host adapter as it is now, the workaround
+ * is way to expensive performance wise.
+ * Instead, it turns out that when this happens the target has disconnected
+ * already but it doesn't show in the interrupt register. Compensate for
+ * that here to try and avoid a SCSI bus reset.
+ */
+ if(!esp->fas_premature_intr_workaround && (fifocnt == 1) &&
+ sreg_dataoutp(esp->sreg)) {
+ ESPLOG(("esp%d: Premature interrupt, enabling workaround\n",
+ esp->esp_id));
+#if 0
+ /* Disable scatter-gather operations, they are not possible
+ * when using this workaround.
+ */
+ esp->ehost->sg_tablesize = 0;
+ esp->ehost->use_clustering = ENABLE_CLUSTERING;
+ esp->fas_premature_intr_workaround = 1;
+ bytes_sent = 0;
+ if(SCptr->use_sg) {
+ ESPLOG(("esp%d: Aborting scatter-gather operation\n",
+ esp->esp_id));
+ esp->cur_msgout[0] = ABORT;
+ esp->msgout_len = 1;
+ esp->msgout_ctr = 0;
+ esp_cmd(esp, eregs, ESP_CMD_SATN);
+ esp_setcount(eregs, 0xffff);
+ esp_cmd(esp, eregs, ESP_CMD_NULL);
+ esp_cmd(esp, eregs, ESP_CMD_TPAD | ESP_CMD_DMA);
+ return do_intr_end;
+ }
+#else
+ /* Just set the disconnected bit. That's what appears to
+ * happen anyway. The state machine will pick it up when
+ * we return.
+ */
+ esp->ireg |= ESP_INTR_DC;
+#endif
+ }
+
+ if(bytes_sent < 0) {
+ /* I've seen this happen due to lost state in this
+ * driver. No idea why it happened, but allowing
+ * this value to be negative caused things to
+ * lock up. This allows greater chance of recovery.
+ * In fact every time I've seen this, it has been
+ * a driver bug without question.
+ */
+ ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
+ ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
+ esp->esp_id,
+ esp->current_transfer_size, fifocnt, ecount));
+ ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
+ esp->esp_id,
+ SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
+ ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id,
+ SCptr->device->id));
+ SCptr->device->borken = 1;
+ esp_dev->sync = 0;
+ bytes_sent = 0;
+ }
+
+ /* Update the state of our transfer. */
+ SCptr->SCp.ptr += bytes_sent;
+ SCptr->SCp.this_residual -= bytes_sent;
+ if(SCptr->SCp.this_residual < 0) {
+ /* shit */
+ ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id));
+ SCptr->SCp.this_residual = 0;
+ }
+
+ /* Maybe continue. */
+ if(!bogus_data) {
+ ESPDATA(("!bogus_data, "));
+ /* NO MATTER WHAT, we advance the scatterlist,
+ * if the target should decide to disconnect
+ * in between scatter chunks (which is common)
+ * we could die horribly! I used to have the sg
+ * advance occur only if we are going back into
+ * (or are staying in) a data phase, you can
+ * imagine the hell I went through trying to
+ * figure this out.
+ */
+ if(!SCptr->SCp.this_residual && SCptr->SCp.buffers_residual)
+ advance_sg(esp, SCptr);
+#ifdef DEBUG_ESP_DATA
+ if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
+ ESPDATA(("to more data\n"));
+ } else {
+ ESPDATA(("to new phase\n"));
+ }
+#endif
+ return esp_do_phase_determine(esp, eregs);
+ }
+ /* Bogus data, just wait for next interrupt. */
+ ESPLOG(("esp%d: bogus_data during end of data phase\n",
+ esp->esp_id));
+ return do_intr_end;
+}
+
+/* We received a non-good status return at the end of
+ * running a SCSI command. This is used to decide if
+ * we should clear our synchronous transfer state for
+ * such a device when that happens.
+ *
+ * The idea is that when spinning up a disk or rewinding
+ * a tape, we don't want to go into a loop re-negotiating
+ * synchronous capabilities over and over.
+ */
+static int esp_should_clear_sync(Scsi_Cmnd *sp)
+{
+ unchar cmd1 = sp->cmnd[0];
+ unchar cmd2 = sp->data_cmnd[0];
+
+ /* These cases are for spinning up a disk and
+ * waiting for that spinup to complete.
+ */
+ if(cmd1 == START_STOP ||
+ cmd2 == START_STOP)
+ return 0;
+
+ if(cmd1 == TEST_UNIT_READY ||
+ cmd2 == TEST_UNIT_READY)
+ return 0;
+
+ /* One more special case for SCSI tape drives,
+ * this is what is used to probe the device for
+ * completion of a rewind or tape load operation.
+ */
+ if(sp->device->type == TYPE_TAPE) {
+ if(cmd1 == MODE_SENSE ||
+ cmd2 == MODE_SENSE)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Either a command is completing or a target is dropping off the bus
+ * to continue the command in the background so we can do other work.
+ */
+static int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ int rval;
+
+ rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing);
+ if(rval)
+ return rval;
+
+ if(esp->ireg != ESP_INTR_DC) {
+ ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
+ return do_reset_bus; /* target will not drop BSY... */
+ }
+ esp->msgout_len = 0;
+ esp->prevmsgout = NOP;
+ if(esp->prevmsgin == COMMAND_COMPLETE) {
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ /* Normal end of nexus. */
+ if(esp->disconnected_SC)
+ esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+ if(SCptr->SCp.Status != GOOD &&
+ SCptr->SCp.Status != CONDITION_GOOD &&
+ ((1<<SCptr->device->id) & esp->targets_present) &&
+ esp_dev->sync && esp_dev->sync_max_offset) {
+ /* SCSI standard says that the synchronous capabilities
+ * should be renegotiated at this point. Most likely
+ * we are about to request sense from this target
+ * in which case we want to avoid using sync
+ * transfers until we are sure of the current target
+ * state.
+ */
+ ESPMISC(("esp: Status <%d> for target %d lun %d\n",
+ SCptr->SCp.Status, SCptr->device->id, SCptr->device->lun));
+
+ /* But don't do this when spinning up a disk at
+ * boot time while we poll for completion as it
+ * fills up the console with messages. Also, tapes
+ * can report not ready many times right after
+ * loading up a tape.
+ */
+ if(esp_should_clear_sync(SCptr) != 0)
+ esp_dev->sync = 0;
+ }
+ ESPDISC(("F<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
+ esp_done(esp, ((SCptr->SCp.Status & 0xff) |
+ ((SCptr->SCp.Message & 0xff)<<8) |
+ (DID_OK << 16)));
+ } else if(esp->prevmsgin == DISCONNECT) {
+ /* Normal disconnect. */
+ esp_cmd(esp, eregs, ESP_CMD_ESEL);
+ ESPDISC(("D<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
+ append_SC(&esp->disconnected_SC, SCptr);
+ esp->current_SC = NULL;
+ if(esp->issue_SC)
+ esp_exec_cmd(esp);
+ } else {
+ /* Driver bug, we do not expect a disconnect here
+ * and should not have advanced the state engine
+ * to in_freeing.
+ */
+ ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
+ esp->esp_id));
+ return do_reset_bus;
+ }
+ return do_intr_end;
+}
+
+/* When a reselect occurs, and we cannot find the command to
+ * reconnect to in our queues, we do this.
+ */
+static int esp_bad_reconnect(struct NCR_ESP *esp)
+{
+ Scsi_Cmnd *sp;
+
+ ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
+ esp->esp_id));
+ ESPLOG(("QUEUE DUMP\n"));
+ sp = esp->issue_SC;
+ ESPLOG(("esp%d: issue_SC[", esp->esp_id));
+ while(sp) {
+ ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
+ sp = (Scsi_Cmnd *) sp->host_scribble;
+ }
+ ESPLOG(("]\n"));
+ sp = esp->current_SC;
+ ESPLOG(("esp%d: current_SC[", esp->esp_id));
+ while(sp) {
+ ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
+ sp = (Scsi_Cmnd *) sp->host_scribble;
+ }
+ ESPLOG(("]\n"));
+ sp = esp->disconnected_SC;
+ ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
+ while(sp) {
+ ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
+ sp = (Scsi_Cmnd *) sp->host_scribble;
+ }
+ ESPLOG(("]\n"));
+ return do_reset_bus;
+}
+
+/* Do the needy when a target tries to reconnect to us. */
+static int esp_do_reconnect(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ int lun, target;
+ Scsi_Cmnd *SCptr;
+
+ /* Check for all bogus conditions first. */
+ target = reconnect_target(esp, eregs);
+ if(target < 0) {
+ ESPDISC(("bad bus bits\n"));
+ return do_reset_bus;
+ }
+ lun = reconnect_lun(esp, eregs);
+ if(lun < 0) {
+ ESPDISC(("target=%2x, bad identify msg\n", target));
+ return do_reset_bus;
+ }
+
+ /* Things look ok... */
+ ESPDISC(("R<%02x,%02x>", target, lun));
+
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ if(esp100_reconnect_hwbug(esp, eregs))
+ return do_reset_bus;
+ esp_cmd(esp, eregs, ESP_CMD_NULL);
+
+ SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun);
+ if(!SCptr)
+ return esp_bad_reconnect(esp);
+
+ esp_connect(esp, eregs, SCptr);
+ esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+ /* Reconnect implies a restore pointers operation. */
+ esp_restore_pointers(esp, SCptr);
+
+ esp->snip = 0;
+ esp_advance_phase(SCptr, in_the_dark);
+ return do_intr_end;
+}
+
+/* End of NEXUS (hopefully), pick up status + message byte then leave if
+ * all goes well.
+ */
+static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ int intr, rval;
+
+ rval = skipahead1(esp, eregs, SCptr, in_the_dark, in_status);
+ if(rval)
+ return rval;
+
+ intr = esp->ireg;
+ ESPSTAT(("esp_do_status: "));
+ if(intr != ESP_INTR_DC) {
+ int message_out = 0; /* for parity problems */
+
+ /* Ack the message. */
+ ESPSTAT(("ack msg, "));
+ esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+ if(esp->dma_poll)
+ esp->dma_poll(esp, (unsigned char *) esp->esp_command);
+
+ ESPSTAT(("got something, "));
+ /* ESP chimes in with one of
+ *
+ * 1) function done interrupt:
+ * both status and message in bytes
+ * are available
+ *
+ * 2) bus service interrupt:
+ * only status byte was acquired
+ *
+ * 3) Anything else:
+ * can't happen, but we test for it
+ * anyways
+ *
+ * ALSO: If bad parity was detected on either
+ * the status _or_ the message byte then
+ * the ESP has asserted ATN on the bus
+ * and we must therefore wait for the
+ * next phase change.
+ */
+ if(intr & ESP_INTR_FDONE) {
+ /* We got it all, hallejulia. */
+ ESPSTAT(("got both, "));
+ SCptr->SCp.Status = esp->esp_command[0];
+ SCptr->SCp.Message = esp->esp_command[1];
+ esp->prevmsgin = SCptr->SCp.Message;
+ esp->cur_msgin[0] = SCptr->SCp.Message;
+ if(esp->sreg & ESP_STAT_PERR) {
+ /* There was bad parity for the
+ * message byte, the status byte
+ * was ok.
+ */
+ message_out = MSG_PARITY_ERROR;
+ }
+ } else if(intr == ESP_INTR_BSERV) {
+ /* Only got status byte. */
+ ESPLOG(("esp%d: got status only, ", esp->esp_id));
+ if(!(esp->sreg & ESP_STAT_PERR)) {
+ SCptr->SCp.Status = esp->esp_command[0];
+ SCptr->SCp.Message = 0xff;
+ } else {
+ /* The status byte had bad parity.
+ * we leave the scsi_pointer Status
+ * field alone as we set it to a default
+ * of CHECK_CONDITION in esp_queue.
+ */
+ message_out = INITIATOR_ERROR;
+ }
+ } else {
+ /* This shouldn't happen ever. */
+ ESPSTAT(("got bolixed\n"));
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp, eregs);
+ }
+
+ if(!message_out) {
+ ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
+ SCptr->SCp.Message));
+ if(SCptr->SCp.Message == COMMAND_COMPLETE) {
+ ESPSTAT(("and was COMMAND_COMPLETE\n"));
+ esp_advance_phase(SCptr, in_freeing);
+ return esp_do_freebus(esp, eregs);
+ } else {
+ ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
+ esp->esp_id));
+ esp->msgin_len = esp->msgin_ctr = 1;
+ esp_advance_phase(SCptr, in_msgindone);
+ return esp_do_msgindone(esp, eregs);
+ }
+ } else {
+ /* With luck we'll be able to let the target
+ * know that bad parity happened, it will know
+ * which byte caused the problems and send it
+ * again. For the case where the status byte
+ * receives bad parity, I do not believe most
+ * targets recover very well. We'll see.
+ */
+ ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
+ esp->esp_id, message_out));
+ esp->cur_msgout[0] = message_out;
+ esp->msgout_len = esp->msgout_ctr = 1;
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp, eregs);
+ }
+ } else {
+ /* If we disconnect now, all hell breaks loose. */
+ ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp, eregs);
+ }
+}
+
+static int esp_enter_status(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ unchar thecmd = ESP_CMD_ICCSEQ;
+
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+ if(esp->do_pio_cmds) {
+ esp_advance_phase(esp->current_SC, in_status);
+ esp_cmd(esp, eregs, thecmd);
+ while(!(esp_read(esp->eregs->esp_status) & ESP_STAT_INTR));
+ esp->esp_command[0] = esp_read(eregs->esp_fdata);
+ while(!(esp_read(esp->eregs->esp_status) & ESP_STAT_INTR));
+ esp->esp_command[1] = esp_read(eregs->esp_fdata);
+ } else {
+ esp->esp_command[0] = esp->esp_command[1] = 0xff;
+ esp_write(eregs->esp_tclow, 2);
+ esp_write(eregs->esp_tcmed, 0);
+ esp->dma_init_read(esp, esp->esp_command_dvma, 2);
+ thecmd |= ESP_CMD_DMA;
+ esp_cmd(esp, eregs, thecmd);
+ esp_advance_phase(esp->current_SC, in_status);
+ }
+
+ return esp_do_status(esp, eregs);
+}
+
+static int esp_disconnect_amidst_phases(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *sp = esp->current_SC;
+ struct esp_device *esp_dev = sp->device->hostdata;
+
+ /* This means real problems if we see this
+ * here. Unless we were actually trying
+ * to force the device to abort/reset.
+ */
+ ESPLOG(("esp%d: Disconnect amidst phases, ", esp->esp_id));
+ ESPLOG(("pphase<%s> cphase<%s>, ",
+ phase_string(sp->SCp.phase),
+ phase_string(sp->SCp.sent_command)));
+
+ if(esp->disconnected_SC)
+ esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+ switch(esp->cur_msgout[0]) {
+ default:
+ /* We didn't expect this to happen at all. */
+ ESPLOG(("device is bolixed\n"));
+ esp_advance_phase(sp, in_tgterror);
+ esp_done(esp, (DID_ERROR << 16));
+ break;
+
+ case BUS_DEVICE_RESET:
+ ESPLOG(("device reset successful\n"));
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync = 0;
+ esp_advance_phase(sp, in_resetdev);
+ esp_done(esp, (DID_RESET << 16));
+ break;
+
+ case ABORT:
+ ESPLOG(("device abort successful\n"));
+ esp_advance_phase(sp, in_abortone);
+ esp_done(esp, (DID_ABORT << 16));
+ break;
+
+ };
+ return do_intr_end;
+}
+
+static int esp_enter_msgout(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ esp_advance_phase(esp->current_SC, in_msgout);
+ return esp_do_msgout(esp, eregs);
+}
+
+static int esp_enter_msgin(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ esp_advance_phase(esp->current_SC, in_msgin);
+ return esp_do_msgin(esp, eregs);
+}
+
+static int esp_enter_cmd(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ esp_advance_phase(esp->current_SC, in_cmdbegin);
+ return esp_do_cmdbegin(esp, eregs);
+}
+
+static int esp_enter_badphase(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
+ esp->sreg & ESP_STAT_PMASK));
+ return do_reset_bus;
+}
+
+typedef int (*espfunc_t)(struct NCR_ESP *,
+ struct ESP_regs *);
+
+static espfunc_t phase_vector[] = {
+ esp_do_data, /* ESP_DOP */
+ esp_do_data, /* ESP_DIP */
+ esp_enter_cmd, /* ESP_CMDP */
+ esp_enter_status, /* ESP_STATP */
+ esp_enter_badphase, /* ESP_STAT_PMSG */
+ esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */
+ esp_enter_msgout, /* ESP_MOP */
+ esp_enter_msgin, /* ESP_MIP */
+};
+
+/* The target has control of the bus and we have to see where it has
+ * taken us.
+ */
+static int esp_do_phase_determine(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ if ((esp->ireg & ESP_INTR_DC) != 0)
+ return esp_disconnect_amidst_phases(esp, eregs);
+ return phase_vector[esp->sreg & ESP_STAT_PMASK](esp, eregs);
+}
+
+/* First interrupt after exec'ing a cmd comes here. */
+static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ int cmd_bytes_sent, fcnt;
+
+ fcnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);
+ cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt);
+ if(esp->dma_invalidate)
+ esp->dma_invalidate(esp);
+
+ /* Let's check to see if a reselect happened
+ * while we we're trying to select. This must
+ * be checked first.
+ */
+ if(esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+ esp_reconnect(esp, SCptr);
+ return esp_do_reconnect(esp, eregs);
+ }
+
+ /* Looks like things worked, we should see a bus service &
+ * a function complete interrupt at this point. Note we
+ * are doing a direct comparison because we don't want to
+ * be fooled into thinking selection was successful if
+ * ESP_INTR_DC is set, see below.
+ */
+ if(esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+ /* target speaks... */
+ esp->targets_present |= (1<<SCptr->device->id);
+
+ /* What if the target ignores the sdtr? */
+ if(esp->snip)
+ esp_dev->sync = 1;
+
+ /* See how far, if at all, we got in getting
+ * the information out to the target.
+ */
+ switch(esp->seqreg) {
+ default:
+
+ case ESP_STEP_ASEL:
+ /* Arbitration won, target selected, but
+ * we are in some phase which is not command
+ * phase nor is it message out phase.
+ *
+ * XXX We've confused the target, obviously.
+ * XXX So clear it's state, but we also end
+ * XXX up clearing everyone elses. That isn't
+ * XXX so nice. I'd like to just reset this
+ * XXX target, but if I cannot even get it's
+ * XXX attention and finish selection to talk
+ * XXX to it, there is not much more I can do.
+ * XXX If we have a loaded bus we're going to
+ * XXX spend the next second or so renegotiating
+ * XXX for synchronous transfers.
+ */
+ ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
+ esp->esp_id, SCptr->device->id));
+
+ case ESP_STEP_SID:
+ /* Arbitration won, target selected, went
+ * to message out phase, sent one message
+ * byte, then we stopped. ATN is asserted
+ * on the SCSI bus and the target is still
+ * there hanging on. This is a legal
+ * sequence step if we gave the ESP a select
+ * and stop command.
+ *
+ * XXX See above, I could set the borken flag
+ * XXX in the device struct and retry the
+ * XXX command. But would that help for
+ * XXX tagged capable targets?
+ */
+
+ case ESP_STEP_NCMD:
+ /* Arbitration won, target selected, maybe
+ * sent the one message byte in message out
+ * phase, but we did not go to command phase
+ * in the end. Actually, we could have sent
+ * only some of the message bytes if we tried
+ * to send out the entire identify and tag
+ * message using ESP_CMD_SA3.
+ */
+ cmd_bytes_sent = 0;
+ break;
+
+ case ESP_STEP_PPC:
+ /* No, not the powerPC pinhead. Arbitration
+ * won, all message bytes sent if we went to
+ * message out phase, went to command phase
+ * but only part of the command was sent.
+ *
+ * XXX I've seen this, but usually in conjunction
+ * XXX with a gross error which appears to have
+ * XXX occurred between the time I told the
+ * XXX ESP to arbitrate and when I got the
+ * XXX interrupt. Could I have misloaded the
+ * XXX command bytes into the fifo? Actually,
+ * XXX I most likely missed a phase, and therefore
+ * XXX went into never never land and didn't even
+ * XXX know it. That was the old driver though.
+ * XXX What is even more peculiar is that the ESP
+ * XXX showed the proper function complete and
+ * XXX bus service bits in the interrupt register.
+ */
+
+ case ESP_STEP_FINI4:
+ case ESP_STEP_FINI5:
+ case ESP_STEP_FINI6:
+ case ESP_STEP_FINI7:
+ /* Account for the identify message */
+ if(SCptr->SCp.phase == in_slct_norm)
+ cmd_bytes_sent -= 1;
+ };
+ esp_cmd(esp, eregs, ESP_CMD_NULL);
+
+ /* Be careful, we could really get fucked during synchronous
+ * data transfers if we try to flush the fifo now.
+ */
+ if(!fcnt && /* Fifo is empty and... */
+ /* either we are not doing synchronous transfers or... */
+ (!esp_dev->sync_max_offset ||
+ /* We are not going into data in phase. */
+ ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* flush is safe */
+
+ /* See how far we got if this is not a slow command. */
+ if(!esp->esp_slowcmd) {
+ if(cmd_bytes_sent < 0)
+ cmd_bytes_sent = 0;
+ if(cmd_bytes_sent != SCptr->cmd_len) {
+ /* Crapola, mark it as a slowcmd
+ * so that we have some chance of
+ * keeping the command alive with
+ * good luck.
+ *
+ * XXX Actually, if we didn't send it all
+ * XXX this means either we didn't set things
+ * XXX up properly (driver bug) or the target
+ * XXX or the ESP detected parity on one of
+ * XXX the command bytes. This makes much
+ * XXX more sense, and therefore this code
+ * XXX should be changed to send out a
+ * XXX parity error message or if the status
+ * XXX register shows no parity error then
+ * XXX just expect the target to bring the
+ * XXX bus into message in phase so that it
+ * XXX can send us the parity error message.
+ * XXX SCSI sucks...
+ */
+ esp->esp_slowcmd = 1;
+ esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
+ esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
+ }
+ }
+
+ /* Now figure out where we went. */
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp, eregs);
+ }
+
+ /* Did the target even make it? */
+ if(esp->ireg == ESP_INTR_DC) {
+ /* wheee... nobody there or they didn't like
+ * what we told it to do, clean up.
+ */
+
+ /* If anyone is off the bus, but working on
+ * a command in the background for us, tell
+ * the ESP to listen for them.
+ */
+ if(esp->disconnected_SC)
+ esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+ if(((1<<SCptr->device->id) & esp->targets_present) &&
+ esp->seqreg && esp->cur_msgout[0] == EXTENDED_MESSAGE &&
+ (SCptr->SCp.phase == in_slct_msg ||
+ SCptr->SCp.phase == in_slct_stop)) {
+ /* shit */
+ esp->snip = 0;
+ ESPLOG(("esp%d: Failed synchronous negotiation for target %d "
+ "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync = 1; /* so we don't negotiate again */
+
+ /* Run the command again, this time though we
+ * won't try to negotiate for synchronous transfers.
+ *
+ * XXX I'd like to do something like send an
+ * XXX INITIATOR_ERROR or ABORT message to the
+ * XXX target to tell it, "Sorry I confused you,
+ * XXX please come back and I will be nicer next
+ * XXX time". But that requires having the target
+ * XXX on the bus, and it has dropped BSY on us.
+ */
+ esp->current_SC = NULL;
+ esp_advance_phase(SCptr, not_issued);
+ prepend_SC(&esp->issue_SC, SCptr);
+ esp_exec_cmd(esp);
+ return do_intr_end;
+ }
+
+ /* Ok, this is normal, this is what we see during boot
+ * or whenever when we are scanning the bus for targets.
+ * But first make sure that is really what is happening.
+ */
+ if(((1<<SCptr->device->id) & esp->targets_present)) {
+ ESPLOG(("esp%d: Warning, live target %d not responding to "
+ "selection.\n", esp->esp_id, SCptr->device->id));
+
+ /* This _CAN_ happen. The SCSI standard states that
+ * the target is to _not_ respond to selection if
+ * _it_ detects bad parity on the bus for any reason.
+ * Therefore, we assume that if we've talked successfully
+ * to this target before, bad parity is the problem.
+ */
+ esp_done(esp, (DID_PARITY << 16));
+ } else {
+ /* Else, there really isn't anyone there. */
+ ESPMISC(("esp: selection failure, maybe nobody there?\n"));
+ ESPMISC(("esp: target %d lun %d\n",
+ SCptr->device->id, SCptr->device->lun));
+ esp_done(esp, (DID_BAD_TARGET << 16));
+ }
+ return do_intr_end;
+ }
+
+
+ ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
+ printk("esp%d: Currently -- ", esp->esp_id);
+ esp_print_ireg(esp->ireg);
+ printk(" ");
+ esp_print_statreg(esp->sreg);
+ printk(" ");
+ esp_print_seqreg(esp->seqreg);
+ printk("\n");
+ printk("esp%d: New -- ", esp->esp_id);
+ esp->sreg = esp_read(eregs->esp_status);
+ esp->seqreg = esp_read(eregs->esp_sstep);
+ esp->ireg = esp_read(eregs->esp_intrpt);
+ esp_print_ireg(esp->ireg);
+ printk(" ");
+ esp_print_statreg(esp->sreg);
+ printk(" ");
+ esp_print_seqreg(esp->seqreg);
+ printk("\n");
+ ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
+ return do_reset_bus; /* ugh... */
+}
+
+/* Continue reading bytes for msgin phase. */
+static int esp_do_msgincont(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ if(esp->ireg & ESP_INTR_BSERV) {
+ /* in the right phase too? */
+ if((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
+ /* phew... */
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ esp_advance_phase(esp->current_SC, in_msgindone);
+ return do_intr_end;
+ }
+
+ /* We changed phase but ESP shows bus service,
+ * in this case it is most likely that we, the
+ * hacker who has been up for 20hrs straight
+ * staring at the screen, drowned in coffee
+ * smelling like retched cigarette ashes
+ * have miscoded something..... so, try to
+ * recover as best we can.
+ */
+ ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id));
+ }
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return do_phase_determine;
+}
+
+static int check_singlebyte_msg(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ esp->prevmsgin = esp->cur_msgin[0];
+ if(esp->cur_msgin[0] & 0x80) {
+ /* wheee... */
+ ESPLOG(("esp%d: target sends identify amidst phases\n",
+ esp->esp_id));
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return 0;
+ } else if(((esp->cur_msgin[0] & 0xf0) == 0x20) ||
+ (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
+ esp->msgin_len = 2;
+ esp_advance_phase(esp->current_SC, in_msgincont);
+ return 0;
+ }
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ switch(esp->cur_msgin[0]) {
+ default:
+ /* We don't want to hear about it. */
+ ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id,
+ esp->cur_msgin[0]));
+ return MESSAGE_REJECT;
+
+ case NOP:
+ ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
+ esp->current_SC->device->id));
+ return 0;
+
+ case RESTORE_POINTERS:
+ /* In this case we might also have to backup the
+ * "slow command" pointer. It is rare to get such
+ * a save/restore pointer sequence so early in the
+ * bus transition sequences, but cover it.
+ */
+ if(esp->esp_slowcmd) {
+ esp->esp_scmdleft = esp->current_SC->cmd_len;
+ esp->esp_scmdp = &esp->current_SC->cmnd[0];
+ }
+ esp_restore_pointers(esp, esp->current_SC);
+ return 0;
+
+ case SAVE_POINTERS:
+ esp_save_pointers(esp, esp->current_SC);
+ return 0;
+
+ case COMMAND_COMPLETE:
+ case DISCONNECT:
+ /* Freeing the bus, let it go. */
+ esp->current_SC->SCp.phase = in_freeing;
+ return 0;
+
+ case MESSAGE_REJECT:
+ ESPMISC(("msg reject, "));
+ if(esp->prevmsgout == EXTENDED_MESSAGE) {
+ struct esp_device *esp_dev = esp->current_SC->device->hostdata;
+
+ /* Doesn't look like this target can
+ * do synchronous or WIDE transfers.
+ */
+ ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n"));
+ esp_dev->sync = 1;
+ esp_dev->wide = 1;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync_max_offset = 0;
+ return 0;
+ } else {
+ ESPMISC(("not sync nego, sending ABORT\n"));
+ return ABORT;
+ }
+ };
+}
+
+/* Target negotiates for synchronous transfers before we do, this
+ * is legal although very strange. What is even funnier is that
+ * the SCSI2 standard specifically recommends against targets doing
+ * this because so many initiators cannot cope with this occurring.
+ */
+static int target_with_ants_in_pants(struct NCR_ESP *esp,
+ Scsi_Cmnd *SCptr,
+ struct esp_device *esp_dev)
+{
+ if(esp_dev->sync || SCptr->device->borken) {
+ /* sorry, no can do */
+ ESPSDTR(("forcing to async, "));
+ build_sync_nego_msg(esp, 0, 0);
+ esp_dev->sync = 1;
+ esp->snip = 1;
+ ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
+ esp_advance_phase(SCptr, in_the_dark);
+ return EXTENDED_MESSAGE;
+ }
+
+ /* Ok, we'll check them out... */
+ return 0;
+}
+
+static void sync_report(struct NCR_ESP *esp)
+{
+ int msg3, msg4;
+ char *type;
+
+ msg3 = esp->cur_msgin[3];
+ msg4 = esp->cur_msgin[4];
+ if(msg4) {
+ int hz = 1000000000 / (msg3 * 4);
+ int integer = hz / 1000000;
+ int fraction = (hz - (integer * 1000000)) / 10000;
+ if((msg3 * 4) < 200) {
+ type = "FAST";
+ } else {
+ type = "synchronous";
+ }
+
+ /* Do not transform this back into one big printk
+ * again, it triggers a bug in our sparc64-gcc272
+ * sibling call optimization. -DaveM
+ */
+ ESPLOG((KERN_INFO "esp%d: target %d ",
+ esp->esp_id, esp->current_SC->device->id));
+ ESPLOG(("[period %dns offset %d %d.%02dMHz ",
+ (int) msg3 * 4, (int) msg4,
+ integer, fraction));
+ ESPLOG(("%s SCSI%s]\n", type,
+ (((msg3 * 4) < 200) ? "-II" : "")));
+ } else {
+ ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n",
+ esp->esp_id, esp->current_SC->device->id));
+ }
+}
+
+static int check_multibyte_msg(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ unchar regval = 0;
+ int message_out = 0;
+
+ ESPSDTR(("chk multibyte msg: "));
+ if(esp->cur_msgin[2] == EXTENDED_SDTR) {
+ int period = esp->cur_msgin[3];
+ int offset = esp->cur_msgin[4];
+
+ ESPSDTR(("is sync nego response, "));
+ if(!esp->snip) {
+ int rval;
+
+ /* Target negotiates first! */
+ ESPSDTR(("target jumps the gun, "));
+ message_out = EXTENDED_MESSAGE; /* we must respond */
+ rval = target_with_ants_in_pants(esp, SCptr, esp_dev);
+ if(rval)
+ return rval;
+ }
+
+ ESPSDTR(("examining sdtr, "));
+
+ /* Offset cannot be larger than ESP fifo size. */
+ if(offset > 15) {
+ ESPSDTR(("offset too big %2x, ", offset));
+ offset = 15;
+ ESPSDTR(("sending back new offset\n"));
+ build_sync_nego_msg(esp, period, offset);
+ return EXTENDED_MESSAGE;
+ }
+
+ if(offset && period > esp->max_period) {
+ /* Yeee, async for this slow device. */
+ ESPSDTR(("period too long %2x, ", period));
+ build_sync_nego_msg(esp, 0, 0);
+ ESPSDTR(("hoping for msgout\n"));
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return EXTENDED_MESSAGE;
+ } else if (offset && period < esp->min_period) {
+ ESPSDTR(("period too short %2x, ", period));
+ period = esp->min_period;
+ if(esp->erev > esp236)
+ regval = 4;
+ else
+ regval = 5;
+ } else if(offset) {
+ int tmp;
+
+ ESPSDTR(("period is ok, "));
+ tmp = esp->ccycle / 1000;
+ regval = (((period << 2) + tmp - 1) / tmp);
+ if(regval && (esp->erev > esp236)) {
+ if(period >= 50)
+ regval--;
+ }
+ }
+
+ if(offset) {
+ unchar bit;
+
+ esp_dev->sync_min_period = (regval & 0x1f);
+ esp_dev->sync_max_offset = (offset | esp->radelay);
+ if(esp->erev > esp236) {
+ if(esp->erev == fas100a)
+ bit = ESP_CONFIG3_FAST;
+ else
+ bit = ESP_CONFIG3_FSCSI;
+ if(period < 50)
+ esp->config3[SCptr->device->id] |= bit;
+ else
+ esp->config3[SCptr->device->id] &= ~bit;
+ esp->prev_cfg3 = esp->config3[SCptr->device->id];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ }
+ esp->prev_soff = esp_dev->sync_min_period;
+ esp_write(eregs->esp_soff, esp->prev_soff);
+ esp->prev_stp = esp_dev->sync_max_offset;
+ esp_write(eregs->esp_stp, esp->prev_stp);
+
+ ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
+ esp_dev->sync_max_offset,
+ esp_dev->sync_min_period,
+ esp->config3[SCptr->device->id]));
+
+ esp->snip = 0;
+ } else if(esp_dev->sync_max_offset) {
+ unchar bit;
+
+ /* back to async mode */
+ ESPSDTR(("unaccaptable sync nego, forcing async\n"));
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp->prev_soff = 0;
+ esp_write(eregs->esp_soff, 0);
+ esp->prev_stp = 0;
+ esp_write(eregs->esp_stp, 0);
+ if(esp->erev > esp236) {
+ if(esp->erev == fas100a)
+ bit = ESP_CONFIG3_FAST;
+ else
+ bit = ESP_CONFIG3_FSCSI;
+ esp->config3[SCptr->device->id] &= ~bit;
+ esp->prev_cfg3 = esp->config3[SCptr->device->id];
+ esp_write(eregs->esp_cfg3, esp->prev_cfg3);
+ }
+ }
+
+ sync_report(esp);
+
+ ESPSDTR(("chk multibyte msg: sync is known, "));
+ esp_dev->sync = 1;
+
+ if(message_out) {
+ ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
+ esp->esp_id));
+ build_sync_nego_msg(esp, period, offset);
+ esp_advance_phase(SCptr, in_the_dark);
+ return EXTENDED_MESSAGE;
+ }
+
+ ESPSDTR(("returning zero\n"));
+ esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
+ return 0;
+ } else if(esp->cur_msgin[2] == EXTENDED_WDTR) {
+ ESPLOG(("esp%d: AIEEE wide msg received\n", esp->esp_id));
+ message_out = MESSAGE_REJECT;
+ } else if(esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
+ ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
+ message_out = MESSAGE_REJECT;
+ }
+ esp_advance_phase(SCptr, in_the_dark);
+ return message_out;
+}
+
+static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ int message_out = 0, it = 0, rval;
+
+ rval = skipahead1(esp, eregs, SCptr, in_msgin, in_msgindone);
+ if(rval)
+ return rval;
+ if(SCptr->SCp.sent_command != in_status) {
+ if(!(esp->ireg & ESP_INTR_DC)) {
+ if(esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
+ message_out = MSG_PARITY_ERROR;
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ } else if((it = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES))!=1) {
+ /* We certainly dropped the ball somewhere. */
+ message_out = INITIATOR_ERROR;
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ } else if(!esp->msgin_len) {
+ it = esp_read(eregs->esp_fdata);
+ esp_advance_phase(SCptr, in_msgincont);
+ } else {
+ /* it is ok and we want it */
+ it = esp->cur_msgin[esp->msgin_ctr] =
+ esp_read(eregs->esp_fdata);
+ esp->msgin_ctr++;
+ }
+ } else {
+ esp_advance_phase(SCptr, in_the_dark);
+ return do_work_bus;
+ }
+ } else {
+ it = esp->cur_msgin[0];
+ }
+ if(!message_out && esp->msgin_len) {
+ if(esp->msgin_ctr < esp->msgin_len) {
+ esp_advance_phase(SCptr, in_msgincont);
+ } else if(esp->msgin_len == 1) {
+ message_out = check_singlebyte_msg(esp, eregs);
+ } else if(esp->msgin_len == 2) {
+ if(esp->cur_msgin[0] == EXTENDED_MESSAGE) {
+ if((it+2) >= 15) {
+ message_out = MESSAGE_REJECT;
+ } else {
+ esp->msgin_len = (it + 2);
+ esp_advance_phase(SCptr, in_msgincont);
+ }
+ } else {
+ message_out = MESSAGE_REJECT; /* foo on you */
+ }
+ } else {
+ message_out = check_multibyte_msg(esp, eregs);
+ }
+ }
+ if(message_out < 0) {
+ return -message_out;
+ } else if(message_out) {
+ if(((message_out != 1) &&
+ ((message_out < 0x20) || (message_out & 0x80))))
+ esp->msgout_len = 1;
+ esp->cur_msgout[0] = message_out;
+ esp_cmd(esp, eregs, ESP_CMD_SATN);
+ esp_advance_phase(SCptr, in_the_dark);
+ esp->msgin_len = 0;
+ }
+ esp->sreg = esp_read(eregs->esp_status);
+ esp->sreg &= ~(ESP_STAT_INTR);
+ if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
+ esp_cmd(esp, eregs, ESP_CMD_MOK);
+ if((SCptr->SCp.sent_command == in_msgindone) &&
+ (SCptr->SCp.phase == in_freeing))
+ return esp_do_freebus(esp, eregs);
+ return do_intr_end;
+}
+
+static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ unsigned char tmp;
+ Scsi_Cmnd *SCptr = esp->current_SC;
+
+ esp_advance_phase(SCptr, in_cmdend);
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ tmp = *esp->esp_scmdp++;
+ esp->esp_scmdleft--;
+ esp_write(eregs->esp_fdata, tmp);
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ return do_intr_end;
+}
+
+static int esp_do_cmddone(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ esp_cmd(esp, eregs, ESP_CMD_NULL);
+ if(esp->ireg & ESP_INTR_BSERV) {
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return esp_do_phase_determine(esp, eregs);
+ }
+ ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
+ esp->esp_id));
+ return do_reset_bus;
+}
+
+static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ switch(esp->msgout_len) {
+ case 1:
+ esp_write(eregs->esp_fdata, esp->cur_msgout[0]);
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ break;
+
+ case 2:
+ if(esp->do_pio_cmds){
+ esp_write(eregs->esp_fdata, esp->cur_msgout[0]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[1]);
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ } else {
+ esp->esp_command[0] = esp->cur_msgout[0];
+ esp->esp_command[1] = esp->cur_msgout[1];
+ esp->dma_setup(esp, esp->esp_command_dvma, 2, 0);
+ esp_setcount(eregs, 2);
+ esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ break;
+
+ case 4:
+ esp->snip = 1;
+ if(esp->do_pio_cmds){
+ esp_write(eregs->esp_fdata, esp->cur_msgout[0]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[1]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[2]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[3]);
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ } else {
+ esp->esp_command[0] = esp->cur_msgout[0];
+ esp->esp_command[1] = esp->cur_msgout[1];
+ esp->esp_command[2] = esp->cur_msgout[2];
+ esp->esp_command[3] = esp->cur_msgout[3];
+ esp->dma_setup(esp, esp->esp_command_dvma, 4, 0);
+ esp_setcount(eregs, 4);
+ esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ break;
+
+ case 5:
+ esp->snip = 1;
+ if(esp->do_pio_cmds){
+ esp_write(eregs->esp_fdata, esp->cur_msgout[0]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[1]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[2]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[3]);
+ esp_write(eregs->esp_fdata, esp->cur_msgout[4]);
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ } else {
+ esp->esp_command[0] = esp->cur_msgout[0];
+ esp->esp_command[1] = esp->cur_msgout[1];
+ esp->esp_command[2] = esp->cur_msgout[2];
+ esp->esp_command[3] = esp->cur_msgout[3];
+ esp->esp_command[4] = esp->cur_msgout[4];
+ esp->dma_setup(esp, esp->esp_command_dvma, 5, 0);
+ esp_setcount(eregs, 5);
+ esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ break;
+
+ default:
+ /* whoops */
+ ESPMISC(("bogus msgout sending NOP\n"));
+ esp->cur_msgout[0] = NOP;
+ esp_write(eregs->esp_fdata, esp->cur_msgout[0]);
+ esp->msgout_len = 1;
+ esp_cmd(esp, eregs, ESP_CMD_TI);
+ break;
+ }
+ esp_advance_phase(esp->current_SC, in_msgoutdone);
+ return do_intr_end;
+}
+
+static int esp_do_msgoutdone(struct NCR_ESP *esp,
+ struct ESP_regs *eregs)
+{
+ if((esp->msgout_len > 1) && esp->dma_barrier)
+ esp->dma_barrier(esp);
+
+ if(!(esp->ireg & ESP_INTR_DC)) {
+ esp_cmd(esp, eregs, ESP_CMD_NULL);
+ switch(esp->sreg & ESP_STAT_PMASK) {
+ case ESP_MOP:
+ /* whoops, parity error */
+ ESPLOG(("esp%d: still in msgout, parity error assumed\n",
+ esp->esp_id));
+ if(esp->msgout_len > 1)
+ esp_cmd(esp, eregs, ESP_CMD_SATN);
+ esp_advance_phase(esp->current_SC, in_msgout);
+ return do_work_bus;
+
+ case ESP_DIP:
+ break;
+
+ default:
+ if(!fcount(esp, eregs) &&
+ !(((struct esp_device *)esp->current_SC->device->hostdata)->sync_max_offset))
+ esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+ break;
+
+ };
+ }
+
+ /* If we sent out a synchronous negotiation message, update
+ * our state.
+ */
+ if(esp->cur_msgout[2] == EXTENDED_MESSAGE &&
+ esp->cur_msgout[4] == EXTENDED_SDTR) {
+ esp->snip = 1; /* anal retentiveness... */
+ }
+
+ esp->prevmsgout = esp->cur_msgout[0];
+ esp->msgout_len = 0;
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return esp_do_phase_determine(esp, eregs);
+}
+
+static int esp_bus_unexpected(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ ESPLOG(("esp%d: command in weird state %2x\n",
+ esp->esp_id, esp->current_SC->SCp.phase));
+ return do_reset_bus;
+}
+
+static espfunc_t bus_vector[] = {
+ esp_do_data_finale,
+ esp_do_data_finale,
+ esp_bus_unexpected,
+ esp_do_msgin,
+ esp_do_msgincont,
+ esp_do_msgindone,
+ esp_do_msgout,
+ esp_do_msgoutdone,
+ esp_do_cmdbegin,
+ esp_do_cmddone,
+ esp_do_status,
+ esp_do_freebus,
+ esp_do_phase_determine,
+ esp_bus_unexpected,
+ esp_bus_unexpected,
+ esp_bus_unexpected,
+};
+
+/* This is the second tier in our dual-level SCSI state machine. */
+static int esp_work_bus(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+ Scsi_Cmnd *SCptr = esp->current_SC;
+ unsigned int phase;
+
+ ESPBUS(("esp_work_bus: "));
+ if(!SCptr) {
+ ESPBUS(("reconnect\n"));
+ return esp_do_reconnect(esp, eregs);
+ }
+ phase = SCptr->SCp.phase;
+ if ((phase & 0xf0) == in_phases_mask)
+ return bus_vector[(phase & 0x0f)](esp, eregs);
+ else if((phase & 0xf0) == in_slct_mask)
+ return esp_select_complete(esp, eregs);
+ else
+ return esp_bus_unexpected(esp, eregs);
+}
+
+static espfunc_t isvc_vector[] = {
+ NULL,
+ esp_do_phase_determine,
+ esp_do_resetbus,
+ esp_finish_reset,
+ esp_work_bus
+};
+
+/* Main interrupt handler for an esp adapter. */
+void esp_handle(struct NCR_ESP *esp)
+{
+ struct ESP_regs *eregs;
+ Scsi_Cmnd *SCptr;
+ int what_next = do_intr_end;
+ eregs = esp->eregs;
+ SCptr = esp->current_SC;
+
+ if(esp->dma_irq_entry)
+ esp->dma_irq_entry(esp);
+
+ /* Check for errors. */
+ esp->sreg = esp_read(eregs->esp_status);
+ esp->sreg &= (~ESP_STAT_INTR);
+ esp->seqreg = (esp_read(eregs->esp_sstep) & ESP_STEP_VBITS);
+ esp->ireg = esp_read(eregs->esp_intrpt); /* Unlatch intr and stat regs */
+ ESPIRQ(("handle_irq: [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+ esp->sreg, esp->seqreg, esp->ireg));
+ if(esp->sreg & (ESP_STAT_SPAM)) {
+ /* Gross error, could be due to one of:
+ *
+ * - top of fifo overwritten, could be because
+ * we tried to do a synchronous transfer with
+ * an offset greater than ESP fifo size
+ *
+ * - top of command register overwritten
+ *
+ * - DMA setup to go in one direction, SCSI
+ * bus points in the other, whoops
+ *
+ * - weird phase change during asynchronous
+ * data phase while we are initiator
+ */
+ ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
+
+ /* If a command is live on the bus we cannot safely
+ * reset the bus, so we'll just let the pieces fall
+ * where they may. Here we are hoping that the
+ * target will be able to cleanly go away soon
+ * so we can safely reset things.
+ */
+ if(!SCptr) {
+ ESPLOG(("esp%d: No current cmd during gross error, "
+ "resetting bus\n", esp->esp_id));
+ what_next = do_reset_bus;
+ goto state_machine;
+ }
+ }
+
+ /* No current cmd is only valid at this point when there are
+ * commands off the bus or we are trying a reset.
+ */
+ if(!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
+ /* Panic is safe, since current_SC is null. */
+ ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
+ panic("esp_handle: current_SC == penguin within interrupt!");
+ }
+
+ if(esp->ireg & (ESP_INTR_IC)) {
+ /* Illegal command fed to ESP. Outside of obvious
+ * software bugs that could cause this, there is
+ * a condition with ESP100 where we can confuse the
+ * ESP into an erroneous illegal command interrupt
+ * because it does not scrape the FIFO properly
+ * for reselection. See esp100_reconnect_hwbug()
+ * to see how we try very hard to avoid this.
+ */
+ ESPLOG(("esp%d: invalid command\n", esp->esp_id));
+
+ esp_dump_state(esp, eregs);
+
+ if(SCptr) {
+ /* Devices with very buggy firmware can drop BSY
+ * during a scatter list interrupt when using sync
+ * mode transfers. We continue the transfer as
+ * expected, the target drops the bus, the ESP
+ * gets confused, and we get a illegal command
+ * interrupt because the bus is in the disconnected
+ * state now and ESP_CMD_TI is only allowed when
+ * a nexus is alive on the bus.
+ */
+ ESPLOG(("esp%d: Forcing async and disabling disconnect for "
+ "target %d\n", esp->esp_id, SCptr->device->id));
+ SCptr->device->borken = 1; /* foo on you */
+ }
+
+ what_next = do_reset_bus;
+ } else if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
+ int phase;
+
+ if(SCptr) {
+ phase = SCptr->SCp.phase;
+ if(phase & in_phases_mask) {
+ what_next = esp_work_bus(esp, eregs);
+ } else if(phase & in_slct_mask) {
+ what_next = esp_select_complete(esp, eregs);
+ } else {
+ ESPLOG(("esp%d: interrupt for no good reason...\n",
+ esp->esp_id));
+ what_next = do_intr_end;
+ }
+ } else {
+ ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ }
+ } else if(esp->ireg & ESP_INTR_SR) {
+ ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
+ what_next = do_reset_complete;
+ } else if(esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
+ ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ } else if(esp->ireg & ESP_INTR_RSEL) {
+ if(!SCptr) {
+ /* This is ok. */
+ what_next = esp_do_reconnect(esp, eregs);
+ } else if(SCptr->SCp.phase & in_slct_mask) {
+ /* Only selection code knows how to clean
+ * up properly.
+ */
+ ESPDISC(("Reselected during selection attempt\n"));
+ what_next = esp_select_complete(esp, eregs);
+ } else {
+ ESPLOG(("esp%d: Reselected while bus is busy\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ }
+ }
+
+ /* This is tier-one in our dual level SCSI state machine. */
+state_machine:
+ while(what_next != do_intr_end) {
+ if (what_next >= do_phase_determine &&
+ what_next < do_intr_end)
+ what_next = isvc_vector[what_next](esp, eregs);
+ else {
+ /* state is completely lost ;-( */
+ ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ }
+ }
+ if(esp->dma_irq_exit)
+ esp->dma_irq_exit(esp);
+}
+
+#ifndef CONFIG_SMP
+irqreturn_t esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ struct NCR_ESP *esp;
+ unsigned long flags;
+ int again;
+ struct Scsi_Host *dev = dev_id;
+
+ /* Handle all ESP interrupts showing at this IRQ level. */
+ spin_lock_irqsave(dev->host_lock, flags);
+repeat:
+ again = 0;
+ for_each_esp(esp) {
+#ifndef __mips__
+ if(((esp)->irq & 0xff) == irq) {
+#endif
+ if(esp->dma_irq_p(esp)) {
+ again = 1;
+
+ esp->dma_ints_off(esp);
+
+ ESPIRQ(("I%d(", esp->esp_id));
+ esp_handle(esp);
+ ESPIRQ((")"));
+
+ esp->dma_ints_on(esp);
+ }
+#ifndef __mips__
+ }
+#endif
+ }
+ if(again)
+ goto repeat;
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+#else
+/* For SMP we only service one ESP on the list list at our IRQ level! */
+irqreturn_t esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ struct NCR_ESP *esp;
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ /* Handle all ESP interrupts showing at this IRQ level. */
+ spin_lock_irqsave(dev->host_lock, flags);
+ for_each_esp(esp) {
+ if(((esp)->irq & 0xf) == irq) {
+ if(esp->dma_irq_p(esp)) {
+ esp->dma_ints_off(esp);
+
+ ESPIRQ(("I[%d:%d](",
+ smp_processor_id(), esp->esp_id));
+ esp_handle(esp);
+ ESPIRQ((")"));
+
+ esp->dma_ints_on(esp);
+ goto out;
+ }
+ }
+ }
+out:
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+#endif
+
+int esp_slave_alloc(Scsi_Device *SDptr)
+{
+ struct esp_device *esp_dev =
+ kmalloc(sizeof(struct esp_device), GFP_ATOMIC);
+
+ if (!esp_dev)
+ return -ENOMEM;
+ memset(esp_dev, 0, sizeof(struct esp_device));
+ SDptr->hostdata = esp_dev;
+ return 0;
+}
+
+void esp_slave_destroy(Scsi_Device *SDptr)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *) SDptr->host->hostdata;
+
+ esp->targets_present &= ~(1 << SDptr->id);
+ kfree(SDptr->hostdata);
+ SDptr->hostdata = NULL;
+}
+
+#ifdef MODULE
+int init_module(void) { return 0; }
+void cleanup_module(void) {}
+void esp_release(void)
+{
+ esps_in_use--;
+ esps_running = esps_in_use;
+}
+#endif
+
+EXPORT_SYMBOL(esp_abort);
+EXPORT_SYMBOL(esp_allocate);
+EXPORT_SYMBOL(esp_deallocate);
+EXPORT_SYMBOL(esp_initialize);
+EXPORT_SYMBOL(esp_intr);
+EXPORT_SYMBOL(esp_queue);
+EXPORT_SYMBOL(esp_reset);
+EXPORT_SYMBOL(esp_slave_alloc);
+EXPORT_SYMBOL(esp_slave_destroy);
+EXPORT_SYMBOL(esps_in_use);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/NCR53C9x.h b/drivers/scsi/NCR53C9x.h
new file mode 100644
index 000000000000..06e7edf23326
--- /dev/null
+++ b/drivers/scsi/NCR53C9x.h
@@ -0,0 +1,669 @@
+/* NCR53C9x.c: Defines and structures for the NCR53C9x generic driver.
+ *
+ * Originaly esp.h: Defines and structures for the Sparc ESP
+ * (Enhanced SCSI Processor) driver under Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Generalization by Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * More generalization (for i386 stuff) by Tymm Twillman (tymm@computer.org)
+ */
+
+#ifndef NCR53C9X_H
+#define NCR53C9X_H
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+
+/* djweis for mac driver */
+#if defined(CONFIG_MAC)
+#define PAD_SIZE 15
+#else
+#define PAD_SIZE 3
+#endif
+
+/* Handle multiple hostadapters on Amiga
+ * generally PAD_SIZE = 3
+ * but there is one exception: Oktagon (PAD_SIZE = 1) */
+#if defined(CONFIG_OKTAGON_SCSI) || defined(CONFIG_OKTAGON_SCSI_MODULE)
+#undef PAD_SIZE
+#if defined(CONFIG_BLZ1230_SCSI) || defined(CONFIG_BLZ1230_SCSI_MODULE) || \
+ defined(CONFIG_BLZ2060_SCSI) || defined(CONFIG_BLZ2060_SCSI_MODULE) || \
+ defined(CONFIG_CYBERSTORM_SCSI) || defined(CONFIG_CYBERSTORM_SCSI_MODULE) || \
+ defined(CONFIG_CYBERSTORMII_SCSI) || defined(CONFIG_CYBERSTORMII_SCSI_MODULE) || \
+ defined(CONFIG_FASTLANE_SCSI) || defined(CONFIG_FASTLANE_SCSI_MODULE)
+#define MULTIPLE_PAD_SIZES
+#else
+#define PAD_SIZE 1
+#endif
+#endif
+
+/* Macros for debugging messages */
+
+#define DEBUG_ESP
+/* #define DEBUG_ESP_DATA */
+/* #define DEBUG_ESP_QUEUE */
+/* #define DEBUG_ESP_DISCONNECT */
+/* #define DEBUG_ESP_STATUS */
+/* #define DEBUG_ESP_PHASES */
+/* #define DEBUG_ESP_WORKBUS */
+/* #define DEBUG_STATE_MACHINE */
+/* #define DEBUG_ESP_CMDS */
+/* #define DEBUG_ESP_IRQS */
+/* #define DEBUG_SDTR */
+/* #define DEBUG_ESP_SG */
+
+/* Use the following to sprinkle debugging messages in a way which
+ * suits you if combinations of the above become too verbose when
+ * trying to track down a specific problem.
+ */
+/* #define DEBUG_ESP_MISC */
+
+#if defined(DEBUG_ESP)
+#define ESPLOG(foo) printk foo
+#else
+#define ESPLOG(foo)
+#endif /* (DEBUG_ESP) */
+
+#if defined(DEBUG_ESP_DATA)
+#define ESPDATA(foo) printk foo
+#else
+#define ESPDATA(foo)
+#endif
+
+#if defined(DEBUG_ESP_QUEUE)
+#define ESPQUEUE(foo) printk foo
+#else
+#define ESPQUEUE(foo)
+#endif
+
+#if defined(DEBUG_ESP_DISCONNECT)
+#define ESPDISC(foo) printk foo
+#else
+#define ESPDISC(foo)
+#endif
+
+#if defined(DEBUG_ESP_STATUS)
+#define ESPSTAT(foo) printk foo
+#else
+#define ESPSTAT(foo)
+#endif
+
+#if defined(DEBUG_ESP_PHASES)
+#define ESPPHASE(foo) printk foo
+#else
+#define ESPPHASE(foo)
+#endif
+
+#if defined(DEBUG_ESP_WORKBUS)
+#define ESPBUS(foo) printk foo
+#else
+#define ESPBUS(foo)
+#endif
+
+#if defined(DEBUG_ESP_IRQS)
+#define ESPIRQ(foo) printk foo
+#else
+#define ESPIRQ(foo)
+#endif
+
+#if defined(DEBUG_SDTR)
+#define ESPSDTR(foo) printk foo
+#else
+#define ESPSDTR(foo)
+#endif
+
+#if defined(DEBUG_ESP_MISC)
+#define ESPMISC(foo) printk foo
+#else
+#define ESPMISC(foo)
+#endif
+
+/*
+ * padding for register structure
+ */
+#ifdef CONFIG_JAZZ_ESP
+#define EREGS_PAD(n)
+#else
+#ifndef MULTIPLE_PAD_SIZES
+#define EREGS_PAD(n) unchar n[PAD_SIZE];
+#endif
+#endif
+
+/* The ESP SCSI controllers have their register sets in three
+ * "classes":
+ *
+ * 1) Registers which are both read and write.
+ * 2) Registers which are read only.
+ * 3) Registers which are write only.
+ *
+ * Yet, they all live within the same IO space.
+ */
+
+#if !defined(__i386__) && !defined(__x86_64__)
+
+#ifndef MULTIPLE_PAD_SIZES
+
+#ifdef CONFIG_CPU_HAS_WB
+#include <asm/wbflush.h>
+#define esp_write(__reg, __val) do{(__reg) = (__val); wbflush();} while(0)
+#else
+#define esp_write(__reg, __val) ((__reg) = (__val))
+#endif
+#define esp_read(__reg) (__reg)
+
+struct ESP_regs {
+ /* Access Description Offset */
+ volatile unchar esp_tclow; /* rw Low bits of the transfer count 0x00 */
+ EREGS_PAD(tlpad1);
+ volatile unchar esp_tcmed; /* rw Mid bits of the transfer count 0x04 */
+ EREGS_PAD(fdpad);
+ volatile unchar esp_fdata; /* rw FIFO data bits 0x08 */
+ EREGS_PAD(cbpad);
+ volatile unchar esp_cmnd; /* rw SCSI command bits 0x0c */
+ EREGS_PAD(stpad);
+ volatile unchar esp_status; /* ro ESP status register 0x10 */
+#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */
+ EREGS_PAD(irqpd);
+ volatile unchar esp_intrpt; /* ro Kind of interrupt 0x14 */
+#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */
+ EREGS_PAD(sspad);
+ volatile unchar esp_sstep; /* ro Sequence step register 0x18 */
+#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */
+ EREGS_PAD(ffpad);
+ volatile unchar esp_fflags; /* ro Bits of current FIFO info 0x1c */
+#define esp_soff esp_fflags /* wo Sync offset 0x1c */
+ EREGS_PAD(cf1pd);
+ volatile unchar esp_cfg1; /* rw First configuration register 0x20 */
+ EREGS_PAD(cfpad);
+ volatile unchar esp_cfact; /* wo Clock conversion factor 0x24 */
+ EREGS_PAD(ctpad);
+ volatile unchar esp_ctest; /* wo Chip test register 0x28 */
+ EREGS_PAD(cf2pd);
+ volatile unchar esp_cfg2; /* rw Second configuration register 0x2c */
+ EREGS_PAD(cf3pd);
+
+ /* The following is only found on the 53C9X series SCSI chips */
+ volatile unchar esp_cfg3; /* rw Third configuration register 0x30 */
+ EREGS_PAD(cf4pd);
+ volatile unchar esp_cfg4; /* rw Fourth configuration register 0x34 */
+ EREGS_PAD(thpd);
+ /* The following is found on all chips except the NCR53C90 (ESP100) */
+ volatile unchar esp_tchi; /* rw High bits of transfer count 0x38 */
+#define esp_uid esp_tchi /* ro Unique ID code 0x38 */
+ EREGS_PAD(fgpad);
+ volatile unchar esp_fgrnd; /* rw Data base for fifo 0x3c */
+};
+
+#else /* MULTIPLE_PAD_SIZES */
+
+#define esp_write(__reg, __val) (*(__reg) = (__val))
+#define esp_read(__reg) (*(__reg))
+
+struct ESP_regs {
+ unsigned char io_addr[64]; /* dummy */
+ /* Access Description Offset */
+#define esp_tclow io_addr /* rw Low bits of the transfer count 0x00 */
+#define esp_tcmed io_addr + (1<<(esp->shift)) /* rw Mid bits of the transfer count 0x04 */
+#define esp_fdata io_addr + (2<<(esp->shift)) /* rw FIFO data bits 0x08 */
+#define esp_cmnd io_addr + (3<<(esp->shift)) /* rw SCSI command bits 0x0c */
+#define esp_status io_addr + (4<<(esp->shift)) /* ro ESP status register 0x10 */
+#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */
+#define esp_intrpt io_addr + (5<<(esp->shift)) /* ro Kind of interrupt 0x14 */
+#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */
+#define esp_sstep io_addr + (6<<(esp->shift)) /* ro Sequence step register 0x18 */
+#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */
+#define esp_fflags io_addr + (7<<(esp->shift)) /* ro Bits of current FIFO info 0x1c */
+#define esp_soff esp_fflags /* wo Sync offset 0x1c */
+#define esp_cfg1 io_addr + (8<<(esp->shift)) /* rw First configuration register 0x20 */
+#define esp_cfact io_addr + (9<<(esp->shift)) /* wo Clock conversion factor 0x24 */
+#define esp_ctest io_addr + (10<<(esp->shift)) /* wo Chip test register 0x28 */
+#define esp_cfg2 io_addr + (11<<(esp->shift)) /* rw Second configuration register 0x2c */
+
+ /* The following is only found on the 53C9X series SCSI chips */
+#define esp_cfg3 io_addr + (12<<(esp->shift)) /* rw Third configuration register 0x30 */
+#define esp_cfg4 io_addr + (13<<(esp->shift)) /* rw Fourth configuration register 0x34 */
+
+ /* The following is found on all chips except the NCR53C90 (ESP100) */
+#define esp_tchi io_addr + (14<<(esp->shift)) /* rw High bits of transfer count 0x38 */
+#define esp_uid esp_tchi /* ro Unique ID code 0x38 */
+#define esp_fgrnd io_addr + (15<<(esp->shift)) /* rw Data base for fifo 0x3c */
+};
+
+#endif
+
+#else /* !defined(__i386__) && !defined(__x86_64__) */
+
+#define esp_write(__reg, __val) outb((__val), (__reg))
+#define esp_read(__reg) inb((__reg))
+
+struct ESP_regs {
+ unsigned int io_addr;
+ /* Access Description Offset */
+#define esp_tclow io_addr /* rw Low bits of the transfer count 0x00 */
+#define esp_tcmed io_addr + 1 /* rw Mid bits of the transfer count 0x04 */
+#define esp_fdata io_addr + 2 /* rw FIFO data bits 0x08 */
+#define esp_cmnd io_addr + 3 /* rw SCSI command bits 0x0c */
+#define esp_status io_addr + 4 /* ro ESP status register 0x10 */
+#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */
+#define esp_intrpt io_addr + 5 /* ro Kind of interrupt 0x14 */
+#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */
+#define esp_sstep io_addr + 6 /* ro Sequence step register 0x18 */
+#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */
+#define esp_fflags io_addr + 7 /* ro Bits of current FIFO info 0x1c */
+#define esp_soff esp_fflags /* wo Sync offset 0x1c */
+#define esp_cfg1 io_addr + 8 /* rw First configuration register 0x20 */
+#define esp_cfact io_addr + 9 /* wo Clock conversion factor 0x24 */
+#define esp_ctest io_addr + 10 /* wo Chip test register 0x28 */
+#define esp_cfg2 io_addr + 11 /* rw Second configuration register 0x2c */
+
+ /* The following is only found on the 53C9X series SCSI chips */
+#define esp_cfg3 io_addr + 12 /* rw Third configuration register 0x30 */
+#define esp_cfg4 io_addr + 13 /* rw Fourth configuration register 0x34 */
+
+ /* The following is found on all chips except the NCR53C90 (ESP100) */
+#define esp_tchi io_addr + 14 /* rw High bits of transfer count 0x38 */
+#define esp_uid esp_tchi /* ro Unique ID code 0x38 */
+#define esp_fgrnd io_addr + 15 /* rw Data base for fifo 0x3c */
+};
+
+#endif /* !defined(__i386__) && !defined(__x86_64__) */
+
+/* Various revisions of the ESP board. */
+enum esp_rev {
+ esp100 = 0x00, /* NCR53C90 - very broken */
+ esp100a = 0x01, /* NCR53C90A */
+ esp236 = 0x02,
+ fas236 = 0x03,
+ fas100a = 0x04,
+ fast = 0x05,
+ fas366 = 0x06,
+ fas216 = 0x07,
+ fsc = 0x08, /* SYM53C94-2 */
+ espunknown = 0x09
+};
+
+/* We allocate one of these for each scsi device and attach it to
+ * SDptr->hostdata for use in the driver
+ */
+struct esp_device {
+ unsigned char sync_min_period;
+ unsigned char sync_max_offset;
+ unsigned sync:1;
+ unsigned wide:1;
+ unsigned disconnect:1;
+};
+
+/* We get one of these for each ESP probed. */
+struct NCR_ESP {
+ struct NCR_ESP *next; /* Next ESP on probed or NULL */
+ struct ESP_regs *eregs; /* All esp registers */
+ int dma; /* Who I do transfers with. */
+ void *dregs; /* And his registers. */
+ struct Scsi_Host *ehost; /* Backpointer to SCSI Host */
+
+ void *edev; /* Pointer to controller base/SBus */
+ int esp_id; /* Unique per-ESP ID number */
+
+ /* ESP Configuration Registers */
+ unsigned char config1; /* Copy of the 1st config register */
+ unsigned char config2; /* Copy of the 2nd config register */
+ unsigned char config3[16]; /* Copy of the 3rd config register */
+
+ /* The current command we are sending to the ESP chip. This esp_command
+ * ptr needs to be mapped in DVMA area so we can send commands and read
+ * from the ESP fifo without burning precious CPU cycles. Programmed I/O
+ * sucks when we have the DVMA to do it for us. The ESP is stupid and will
+ * only send out 6, 10, and 12 byte SCSI commands, others we need to send
+ * one byte at a time. esp_slowcmd being set says that we are doing one
+ * of the command types ESP doesn't understand, esp_scmdp keeps track of
+ * which byte we are sending, esp_scmdleft says how many bytes to go.
+ */
+ volatile unchar *esp_command; /* Location of command (CPU view) */
+ __u32 esp_command_dvma; /* Location of command (DVMA view) */
+ unsigned char esp_clen; /* Length of this command */
+ unsigned char esp_slowcmd;
+ unsigned char *esp_scmdp;
+ unsigned char esp_scmdleft;
+
+ /* The following are used to determine the cause of an IRQ. Upon every
+ * IRQ entry we synchronize these with the hardware registers.
+ */
+ unchar ireg; /* Copy of ESP interrupt register */
+ unchar sreg; /* Same for ESP status register */
+ unchar seqreg; /* The ESP sequence register */
+
+ /* The following is set when a premature interrupt condition is detected
+ * in some FAS revisions.
+ */
+ unchar fas_premature_intr_workaround;
+
+ /* To save register writes to the ESP, which can be expensive, we
+ * keep track of the previous value that various registers had for
+ * the last target we connected to. If they are the same for the
+ * current target, we skip the register writes as they are not needed.
+ */
+ unchar prev_soff, prev_stp, prev_cfg3;
+
+ /* For each target we keep track of save/restore data
+ * pointer information. This needs to be updated majorly
+ * when we add support for tagged queueing. -DaveM
+ */
+ struct esp_pointers {
+ char *saved_ptr;
+ struct scatterlist *saved_buffer;
+ int saved_this_residual;
+ int saved_buffers_residual;
+ } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/;
+
+ /* Clock periods, frequencies, synchronization, etc. */
+ unsigned int cfreq; /* Clock frequency in HZ */
+ unsigned int cfact; /* Clock conversion factor */
+ unsigned int ccycle; /* One ESP clock cycle */
+ unsigned int ctick; /* One ESP clock time */
+ unsigned int radelay; /* FAST chip req/ack delay */
+ unsigned int neg_defp; /* Default negotiation period */
+ unsigned int sync_defp; /* Default sync transfer period */
+ unsigned int max_period; /* longest our period can be */
+ unsigned int min_period; /* shortest period we can withstand */
+ /* For slow to medium speed input clock rates we shoot for 5mb/s,
+ * but for high input clock rates we try to do 10mb/s although I
+ * don't think a transfer can even run that fast with an ESP even
+ * with DMA2 scatter gather pipelining.
+ */
+#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */
+#define SYNC_DEFP_FAST 0x19 /* 10mb/s */
+
+ unsigned int snip; /* Sync. negotiation in progress */
+ unsigned int wnip; /* WIDE negotiation in progress */
+ unsigned int targets_present; /* targets spoken to before */
+
+ int current_transfer_size; /* Set at beginning of data dma */
+
+ unchar espcmdlog[32]; /* Log of current esp cmds sent. */
+ unchar espcmdent; /* Current entry in esp cmd log. */
+
+ /* Misc. info about this ESP */
+ enum esp_rev erev; /* ESP revision */
+ int irq; /* IRQ for this ESP */
+ int scsi_id; /* Who am I as initiator? */
+ int scsi_id_mask; /* Bitmask of 'me'. */
+ int diff; /* Differential SCSI bus? */
+ int slot; /* Slot the adapter occupies */
+
+ /* Our command queues, only one cmd lives in the current_SC queue. */
+ Scsi_Cmnd *issue_SC; /* Commands to be issued */
+ Scsi_Cmnd *current_SC; /* Who is currently working the bus */
+ Scsi_Cmnd *disconnected_SC; /* Commands disconnected from the bus */
+
+ /* Message goo */
+ unchar cur_msgout[16];
+ unchar cur_msgin[16];
+ unchar prevmsgout, prevmsgin;
+ unchar msgout_len, msgin_len;
+ unchar msgout_ctr, msgin_ctr;
+
+ /* States that we cannot keep in the per cmd structure because they
+ * cannot be assosciated with any specific command.
+ */
+ unchar resetting_bus;
+ wait_queue_head_t reset_queue;
+
+ unchar do_pio_cmds; /* Do command transfer with pio */
+
+ /* How much bits do we have to shift the registers */
+ unsigned char shift;
+
+ /* Functions handling DMA
+ */
+ /* Required functions */
+ int (*dma_bytes_sent)(struct NCR_ESP *, int);
+ int (*dma_can_transfer)(struct NCR_ESP *, Scsi_Cmnd *);
+ void (*dma_dump_state)(struct NCR_ESP *);
+ void (*dma_init_read)(struct NCR_ESP *, __u32, int);
+ void (*dma_init_write)(struct NCR_ESP *, __u32, int);
+ void (*dma_ints_off)(struct NCR_ESP *);
+ void (*dma_ints_on)(struct NCR_ESP *);
+ int (*dma_irq_p)(struct NCR_ESP *);
+ int (*dma_ports_p)(struct NCR_ESP *);
+ void (*dma_setup)(struct NCR_ESP *, __u32, int, int);
+
+ /* Optional functions (i.e. may be initialized to 0) */
+ void (*dma_barrier)(struct NCR_ESP *);
+ void (*dma_drain)(struct NCR_ESP *);
+ void (*dma_invalidate)(struct NCR_ESP *);
+ void (*dma_irq_entry)(struct NCR_ESP *);
+ void (*dma_irq_exit)(struct NCR_ESP *);
+ void (*dma_led_off)(struct NCR_ESP *);
+ void (*dma_led_on)(struct NCR_ESP *);
+ void (*dma_poll)(struct NCR_ESP *, unsigned char *);
+ void (*dma_reset)(struct NCR_ESP *);
+
+ /* Optional virtual DMA functions */
+ void (*dma_mmu_get_scsi_one)(struct NCR_ESP *, Scsi_Cmnd *);
+ void (*dma_mmu_get_scsi_sgl)(struct NCR_ESP *, Scsi_Cmnd *);
+ void (*dma_mmu_release_scsi_one)(struct NCR_ESP *, Scsi_Cmnd *);
+ void (*dma_mmu_release_scsi_sgl)(struct NCR_ESP *, Scsi_Cmnd *);
+ void (*dma_advance_sg)(Scsi_Cmnd *);
+};
+
+/* Bitfield meanings for the above registers. */
+
+/* ESP config reg 1, read-write, found on all ESP chips */
+#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */
+#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */
+#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */
+#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */
+#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */
+#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */
+
+/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236+fsc chips */
+#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236,fsc) */
+#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236,fsc) */
+#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */
+#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */
+#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */
+#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */
+#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236,fsc) */
+#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216,fsc) */
+#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */
+#define ESP_CONFIG2_RFB 0x80 /* Reserve FIFO byte (fsc) */
+#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */
+
+/* ESP config register 3 read-write, found only esp236+fas236+fas100a+fsc chips */
+#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/fas366) */
+#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236/fsc) */
+#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a) */
+#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236/fsc) */
+#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a) */
+#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236/fsc) */
+#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a) */
+#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236/fsc) */
+#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a) */
+#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236/fsc) */
+#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236/fsc) */
+#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236/fsc) */
+#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236/fsc) */
+
+/* ESP config register 4 read-write, found only on fsc chips */
+#define ESP_CONFIG4_BBTE 0x01 /* Back-to-Back transfer enable */
+#define ESP_CONFIG4_TEST 0x02 /* Transfer counter test mode */
+#define ESP_CONFIG4_EAN 0x04 /* Enable Active Negotiation */
+
+/* ESP command register read-write */
+/* Group 1 commands: These may be sent at any point in time to the ESP
+ * chip. None of them can generate interrupts 'cept
+ * the "SCSI bus reset" command if you have not disabled
+ * SCSI reset interrupts in the config1 ESP register.
+ */
+#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */
+#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */
+#define ESP_CMD_RC 0x02 /* Chip reset */
+#define ESP_CMD_RS 0x03 /* SCSI bus reset */
+
+/* Group 2 commands: ESP must be an initiator and connected to a target
+ * for these commands to work.
+ */
+#define ESP_CMD_TI 0x10 /* Transfer Information */
+#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */
+#define ESP_CMD_MOK 0x12 /* Message okie-dokie */
+#define ESP_CMD_TPAD 0x18 /* Transfer Pad */
+#define ESP_CMD_SATN 0x1a /* Set ATN */
+#define ESP_CMD_RATN 0x1b /* De-assert ATN */
+
+/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected
+ * to a target as the initiator for these commands to work.
+ */
+#define ESP_CMD_SMSG 0x20 /* Send message */
+#define ESP_CMD_SSTAT 0x21 /* Send status */
+#define ESP_CMD_SDATA 0x22 /* Send data */
+#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */
+#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */
+#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */
+#define ESP_CMD_DCNCT 0x27 /* Disconnect */
+#define ESP_CMD_RMSG 0x28 /* Receive Message */
+#define ESP_CMD_RCMD 0x29 /* Receive Command */
+#define ESP_CMD_RDATA 0x2a /* Receive Data */
+#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */
+
+/* Group 4 commands: The ESP must be in the disconnected state and must
+ * not be connected to any targets as initiator for
+ * these commands to work.
+ */
+#define ESP_CMD_RSEL 0x40 /* Reselect */
+#define ESP_CMD_SEL 0x41 /* Select w/o ATN */
+#define ESP_CMD_SELA 0x42 /* Select w/ATN */
+#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */
+#define ESP_CMD_ESEL 0x44 /* Enable selection */
+#define ESP_CMD_DSEL 0x45 /* Disable selections */
+#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */
+#define ESP_CMD_RSEL3 0x47 /* Reselect3 */
+
+/* This bit enables the ESP's DMA */
+#define ESP_CMD_DMA 0x80 /* Do DMA? */
+
+/* ESP status register read-only */
+#define ESP_STAT_PIO 0x01 /* IO phase bit */
+#define ESP_STAT_PCD 0x02 /* CD phase bit */
+#define ESP_STAT_PMSG 0x04 /* MSG phase bit */
+#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */
+#define ESP_STAT_TDONE 0x08 /* Transfer Completed */
+#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */
+#define ESP_STAT_PERR 0x20 /* Parity error */
+#define ESP_STAT_SPAM 0x40 /* Real bad error */
+/* This indicates the 'interrupt pending' condition, it is a reserved
+ * bit on old revs of the ESP (ESP100, ESP100A, FAS100A).
+ */
+#define ESP_STAT_INTR 0x80 /* Interrupt */
+
+/* The status register can be masked with ESP_STAT_PMASK and compared
+ * with the following values to determine the current phase the ESP
+ * (at least thinks it) is in. For our purposes we also add our own
+ * software 'done' bit for our phase management engine.
+ */
+#define ESP_DOP (0) /* Data Out */
+#define ESP_DIP (ESP_STAT_PIO) /* Data In */
+#define ESP_CMDP (ESP_STAT_PCD) /* Command */
+#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */
+#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */
+#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
+
+/* ESP interrupt register read-only */
+#define ESP_INTR_S 0x01 /* Select w/o ATN */
+#define ESP_INTR_SATN 0x02 /* Select w/ATN */
+#define ESP_INTR_RSEL 0x04 /* Reselected */
+#define ESP_INTR_FDONE 0x08 /* Function done */
+#define ESP_INTR_BSERV 0x10 /* Bus service */
+#define ESP_INTR_DC 0x20 /* Disconnect */
+#define ESP_INTR_IC 0x40 /* Illegal command given */
+#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */
+
+/* Interrupt status macros */
+#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR))
+#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC))
+#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN))
+#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S))
+#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \
+ (ESP_SELECT_WITHOUT_ATN_IRQ(esp)))
+#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL))
+
+/* ESP sequence step register read-only */
+#define ESP_STEP_VBITS 0x07 /* Valid bits */
+#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */
+#define ESP_STEP_SID 0x01 /* One msg byte sent */
+#define ESP_STEP_NCMD 0x02 /* Was not in command phase */
+#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd
+ * bytes to be lost
+ */
+#define ESP_STEP_FINI4 0x04 /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5 0x05
+#define ESP_STEP_FINI6 0x06
+#define ESP_STEP_FINI7 0x07
+#define ESP_STEP_SOM 0x08 /* Synchronous Offset Max */
+
+/* ESP chip-test register read-write */
+#define ESP_TEST_TARG 0x01 /* Target test mode */
+#define ESP_TEST_INI 0x02 /* Initiator test mode */
+#define ESP_TEST_TS 0x04 /* Tristate test mode */
+
+/* ESP unique ID register read-only, found on fas236+fas100a+fsc only */
+#define ESP_UID_F100A 0x00 /* FAS100A */
+#define ESP_UID_F236 0x02 /* FAS236 */
+#define ESP_UID_FSC 0xa2 /* NCR53CF9x-2 */
+#define ESP_UID_REV 0x07 /* ESP revision */
+#define ESP_UID_FAM 0xf8 /* ESP family */
+
+/* ESP fifo flags register read-only */
+/* Note that the following implies a 16 byte FIFO on the ESP. */
+#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100,fsc) */
+#define ESP_FF_SSTEP 0xe0 /* Sequence step */
+
+/* ESP clock conversion factor register write-only */
+#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */
+#define ESP_CCF_NEVER 0x01 /* Set it to this and die */
+#define ESP_CCF_F2 0x02 /* 10MHz */
+#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */
+#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */
+#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */
+#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */
+#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */
+
+#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */
+#define ESP_TIMEO_CONST 8192
+#define FSC_TIMEO_CONST 7668
+#define ESP_NEG_DEFP(mhz, cfact) \
+ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define FSC_NEG_DEFP(mhz, cfact) \
+ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (7668 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000))
+
+
+/* UGLY, UGLY, UGLY! */
+extern int nesps, esps_in_use, esps_running;
+
+/* For our interrupt engine. */
+#define for_each_esp(esp) \
+ for((esp) = espchain; (esp); (esp) = (esp)->next)
+
+
+/* External functions */
+extern void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs);
+extern struct NCR_ESP *esp_allocate(Scsi_Host_Template *, void *);
+extern void esp_deallocate(struct NCR_ESP *);
+extern void esp_release(void);
+extern void esp_initialize(struct NCR_ESP *);
+extern irqreturn_t esp_intr(int, void *, struct pt_regs *);
+extern const char *esp_info(struct Scsi_Host *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *);
+extern int esp_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length,
+ int inout);
+extern int esp_slave_alloc(Scsi_Device *);
+extern void esp_slave_destroy(Scsi_Device *);
+#endif /* !(NCR53C9X_H) */
diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c
new file mode 100644
index 000000000000..c685d546f838
--- /dev/null
+++ b/drivers/scsi/NCR53c406a.c
@@ -0,0 +1,1110 @@
+/*
+ * NCR53c406.c
+ * Low-level SCSI driver for NCR53c406a chip.
+ * Copyright (C) 1994, 1995, 1996 Normunds Saumanis (normunds@fi.ibm.com)
+ *
+ * LILO command line usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]
+ * Specify IRQ = 0 for non-interrupt driven mode.
+ * FASTPIO = 1 for fast pio mode, 0 for slow mode.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#define NCR53C406A_DEBUG 0
+#define VERBOSE_NCR53C406A_DEBUG 0
+
+/* Set this to 1 for PIO mode (recommended) or to 0 for DMA mode */
+#define USE_PIO 1
+
+#define USE_BIOS 0
+ /* #define BIOS_ADDR 0xD8000 *//* define this if autoprobe fails */
+ /* #define PORT_BASE 0x330 *//* define this if autoprobe fails */
+ /* #define IRQ_LEV 0 *//* define this if autoprobe fails */
+#define DMA_CHAN 5 /* this is ignored if DMA is disabled */
+
+/* Set this to 0 if you encounter kernel lockups while transferring
+ * data in PIO mode */
+#define USE_FAST_PIO 1
+
+/* ============= End of user configurable parameters ============= */
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+/* ============================================================= */
+
+#define WATCHDOG 5000000
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+#if DEBUG
+#undef NCR53C406A_DEBUG
+#define NCR53C406A_DEBUG 1
+#endif
+
+#if USE_PIO
+#define USE_DMA 0
+#else
+#define USE_DMA 1
+#endif
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#if USE_DMA
+#define C3_IMG 0x21 /* CDB TE */
+#else
+#define C3_IMG 0x20 /* CDB */
+#endif
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xb6 /* AA PI SIE POL */
+
+#define REG0 (outb(C4_IMG, CONFIG4))
+#define REG1 (outb(C5_IMG, CONFIG5))
+
+#if NCR53C406A_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_NCR53C406A_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(count) \
+ outb(count & 0xff, TC_LSB); \
+ outb((count >> 8) & 0xff, TC_MSB); \
+ outb((count >> 16) & 0xff, TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+
+#if NCR53C406A_DEBUG
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+/*----------------------------------------------------------------*/
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* Static function prototypes */
+static void NCR53c406a_intr(int, void *, struct pt_regs *);
+static irqreturn_t do_NCR53c406a_intr(int, void *, struct pt_regs *);
+static void chip_init(void);
+static void calc_port_addr(void);
+#ifndef IRQ_LEV
+static int irq_probe(void);
+#endif
+
+/* ================================================================= */
+
+#if USE_BIOS
+static void *bios_base;
+#endif
+
+#if PORT_BASE
+static int port_base = PORT_BASE;
+#else
+static int port_base;
+#endif
+
+#if IRQ_LEV
+static int irq_level = IRQ_LEV;
+#else
+static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized' */
+#endif
+
+#if USE_DMA
+static int dma_chan;
+#endif
+
+#if USE_PIO
+static int fast_pio = USE_FAST_PIO;
+#endif
+
+static Scsi_Cmnd *current_SC;
+static char info_msg[256];
+
+/* ================================================================= */
+
+/* possible BIOS locations */
+#if USE_BIOS
+static void *addresses[] = {
+ (void *) 0xd8000,
+ (void *) 0xc8000
+};
+#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
+#endif /* USE_BIOS */
+
+/* possible i/o port addresses */
+static unsigned short ports[] = { 0x230, 0x330, 0x280, 0x290, 0x330, 0x340, 0x300, 0x310, 0x348, 0x350 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+/* possible interrupt channels */
+static unsigned short intrs[] = { 10, 11, 12, 15 };
+#define INTR_COUNT (sizeof( intrs ) / sizeof( unsigned short ))
+
+/* signatures for NCR 53c406a based controllers */
+#if USE_BIOS
+struct signature {
+ char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] __initdata = {
+ /* 1 2 3 4 5 6 */
+ /* 123456789012345678901234567890123456789012345678901234567890 */
+ {
+"Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82},};
+
+#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+#endif /* USE_BIOS */
+
+/* ============================================================ */
+
+/* Control Register Set 0 */
+static int TC_LSB; /* transfer counter lsb */
+static int TC_MSB; /* transfer counter msb */
+static int SCSI_FIFO; /* scsi fifo register */
+static int CMD_REG; /* command register */
+static int STAT_REG; /* status register */
+static int DEST_ID; /* selection/reselection bus id */
+static int INT_REG; /* interrupt status register */
+static int SRTIMOUT; /* select/reselect timeout reg */
+static int SEQ_REG; /* sequence step register */
+static int SYNCPRD; /* synchronous transfer period */
+static int FIFO_FLAGS; /* indicates # of bytes in fifo */
+static int SYNCOFF; /* synchronous offset register */
+static int CONFIG1; /* configuration register */
+static int CLKCONV; /* clock conversion reg */
+ /*static int TESTREG;*//* test mode register */
+static int CONFIG2; /* Configuration 2 Register */
+static int CONFIG3; /* Configuration 3 Register */
+static int CONFIG4; /* Configuration 4 Register */
+static int TC_HIGH; /* Transfer Counter High */
+ /*static int FIFO_BOTTOM;*//* Reserve FIFO byte register */
+
+/* Control Register Set 1 */
+ /*static int JUMPER_SENSE;*//* Jumper sense port reg (r/w) */
+ /*static int SRAM_PTR;*//* SRAM address pointer reg (r/w) */
+ /*static int SRAM_DATA;*//* SRAM data register (r/w) */
+static int PIO_FIFO; /* PIO FIFO registers (r/w) */
+ /*static int PIO_FIFO1;*//* */
+ /*static int PIO_FIFO2;*//* */
+ /*static int PIO_FIFO3;*//* */
+static int PIO_STATUS; /* PIO status (r/w) */
+ /*static int ATA_CMD;*//* ATA command/status reg (r/w) */
+ /*static int ATA_ERR;*//* ATA features/error register (r/w) */
+static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
+static int CONFIG5; /* Configuration 5 register (r/w) */
+ /*static int SIGNATURE;*//* Signature Register (r) */
+ /*static int CONFIG6;*//* Configuration 6 register (r) */
+
+/* ============================================================== */
+
+#if USE_DMA
+static __inline__ int NCR53c406a_dma_setup(unsigned char *ptr, unsigned int count, unsigned char mode)
+{
+ unsigned limit;
+ unsigned long flags = 0;
+
+ VDEB(printk("dma: before count=%d ", count));
+ if (dma_chan <= 3) {
+ if (count > 65536)
+ count = 65536;
+ limit = 65536 - (((unsigned) ptr) & 0xFFFF);
+ } else {
+ if (count > (65536 << 1))
+ count = (65536 << 1);
+ limit = (65536 << 1) - (((unsigned) ptr) & 0x1FFFF);
+ }
+
+ if (count > limit)
+ count = limit;
+
+ VDEB(printk("after count=%d\n", count));
+ if ((count & 1) || (((unsigned) ptr) & 1))
+ panic("NCR53c406a: attempted unaligned DMA transfer\n");
+
+ flags = claim_dma_lock();
+ disable_dma(dma_chan);
+ clear_dma_ff(dma_chan);
+ set_dma_addr(dma_chan, (long) ptr);
+ set_dma_count(dma_chan, count);
+ set_dma_mode(dma_chan, mode);
+ enable_dma(dma_chan);
+ release_dma_lock(flags);
+
+ return count;
+}
+
+static __inline__ int NCR53c406a_dma_write(unsigned char *src, unsigned int count)
+{
+ return NCR53c406a_dma_setup(src, count, DMA_MODE_WRITE);
+}
+
+static __inline__ int NCR53c406a_dma_read(unsigned char *src, unsigned int count)
+{
+ return NCR53c406a_dma_setup(src, count, DMA_MODE_READ);
+}
+
+static __inline__ int NCR53c406a_dma_residual(void)
+{
+ register int tmp;
+ unsigned long flags;
+
+ flags = claim_dma_lock();
+ clear_dma_ff(dma_chan);
+ tmp = get_dma_residue(dma_chan);
+ release_dma_lock(flags);
+
+ return tmp;
+}
+#endif /* USE_DMA */
+
+#if USE_PIO
+static __inline__ int NCR53c406a_pio_read(unsigned char *request, unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+
+ REG1;
+ while (reqlen) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch (i & 0x1e) {
+ default:
+ case 0x10:
+ len = 0;
+ break;
+ case 0x0:
+ len = 1;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 84;
+ break;
+ case 0xe:
+ len = 128;
+ break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ insl(PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ *request++ = inb(PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static __inline__ int NCR53c406a_pio_write(unsigned char *request, unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+
+ REG1;
+ while (reqlen && !(i & 0x40)) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch (i & 0x1e) {
+ case 0x10:
+ len = 128;
+ break;
+ case 0x0:
+ len = 84;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 1;
+ break;
+ default:
+ case 0xe:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ outsl(PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ outb(*request++, PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+#endif /* USE_PIO */
+
+static int __init NCR53c406a_detect(Scsi_Host_Template * tpnt)
+{
+ int present = 0;
+ struct Scsi_Host *shpnt = NULL;
+#ifndef PORT_BASE
+ int i;
+#endif
+
+#if USE_BIOS
+ int ii, jj;
+ bios_base = 0;
+ /* look for a valid signature */
+ for (ii = 0; ii < ADDRESS_COUNT && !bios_base; ii++)
+ for (jj = 0; (jj < SIGNATURE_COUNT) && !bios_base; jj++)
+ if (!memcmp((void *) addresses[ii] + signatures[jj].sig_offset, (void *) signatures[jj].signature, (int) signatures[jj].sig_length))
+ bios_base = addresses[ii];
+
+ if (!bios_base) {
+ printk("NCR53c406a: BIOS signature not found\n");
+ return 0;
+ }
+
+ DEB(printk("NCR53c406a BIOS found at 0x%x\n", (unsigned int) bios_base);
+ );
+#endif /* USE_BIOS */
+
+#ifdef PORT_BASE
+ if (!request_region(port_base, 0x10, "NCR53c406a")) /* ports already snatched */
+ port_base = 0;
+
+#else /* autodetect */
+ if (port_base) { /* LILO override */
+ if (!request_region(port_base, 0x10, "NCR53c406a"))
+ port_base = 0;
+ } else {
+ for (i = 0; i < PORT_COUNT && !port_base; i++) {
+ if (!request_region(ports[i], 0x10, "NCR53c406a")) {
+ DEB(printk("NCR53c406a: port 0x%x in use\n", ports[i]));
+ } else {
+ VDEB(printk("NCR53c406a: port 0x%x available\n", ports[i]));
+ outb(C5_IMG, ports[i] + 0x0d); /* reg set 1 */
+ if ((inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) & 0xf8) == 0x58) {
+ port_base = ports[i];
+ VDEB(printk("NCR53c406a: Sig register valid\n"));
+ VDEB(printk("port_base=0x%x\n", port_base));
+ break;
+ }
+ release_region(ports[i], 0x10);
+ }
+ }
+ }
+#endif /* PORT_BASE */
+
+ if (!port_base) { /* no ports found */
+ printk("NCR53c406a: no available ports found\n");
+ return 0;
+ }
+
+ DEB(printk("NCR53c406a detected\n"));
+
+ calc_port_addr();
+ chip_init();
+
+#ifndef IRQ_LEV
+ if (irq_level < 0) { /* LILO override if >= 0 */
+ irq_level = irq_probe();
+ if (irq_level < 0) { /* Trouble */
+ printk("NCR53c406a: IRQ problem, irq_level=%d, giving up\n", irq_level);
+ goto err_release;
+ }
+ }
+#endif
+
+ DEB(printk("NCR53c406a: using port_base 0x%x\n", port_base));
+
+ present = 1;
+ tpnt->proc_name = "NCR53c406a";
+
+ shpnt = scsi_register(tpnt, 0);
+ if (!shpnt) {
+ printk("NCR53c406a: Unable to register host, giving up.\n");
+ goto err_release;
+ }
+
+ if (irq_level > 0) {
+ if (request_irq(irq_level, do_NCR53c406a_intr, 0, "NCR53c406a", shpnt)) {
+ printk("NCR53c406a: unable to allocate IRQ %d\n", irq_level);
+ goto err_free_scsi;
+ }
+ tpnt->can_queue = 1;
+ DEB(printk("NCR53c406a: allocated IRQ %d\n", irq_level));
+ } else if (irq_level == 0) {
+ tpnt->can_queue = 0;
+ DEB(printk("NCR53c406a: No interrupts detected\n"));
+ printk("NCR53c406a driver no longer supports polling interface\n");
+ printk("Please email linux-scsi@vger.kernel.org\n");
+
+#if USE_DMA
+ printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n");
+#endif /* USE_DMA */
+ goto err_free_scsi;
+ } else {
+ DEB(printk("NCR53c406a: Shouldn't get here!\n"));
+ goto err_free_scsi;
+ }
+
+#if USE_DMA
+ dma_chan = DMA_CHAN;
+ if (request_dma(dma_chan, "NCR53c406a") != 0) {
+ printk("NCR53c406a: unable to allocate DMA channel %d\n", dma_chan);
+ goto err_free_irq;
+ }
+
+ DEB(printk("Allocated DMA channel %d\n", dma_chan));
+#endif /* USE_DMA */
+
+ shpnt->irq = irq_level;
+ shpnt->io_port = port_base;
+ shpnt->n_io_port = 0x10;
+#if USE_DMA
+ shpnt->dma = dma_chan;
+#endif
+
+#if USE_DMA
+ sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, DMA channel %d.", port_base, irq_level, dma_chan);
+#else
+ sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, %s PIO mode.", port_base, irq_level, fast_pio ? "fast" : "slow");
+#endif
+
+ return (present);
+
+#if USE_DMA
+ err_free_irq:
+ if (irq_level)
+ free_irq(irq_level, shpnt);
+#endif
+ err_free_scsi:
+ scsi_unregister(shpnt);
+ err_release:
+ release_region(port_base, 0x10);
+ return 0;
+}
+
+static int NCR53c406a_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+#ifdef USE_DMA
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+#endif
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+
+ scsi_unregister(shost);
+ return 0;
+}
+
+/* called from init/main.c */
+static int __init NCR53c406a_setup(char *str)
+{
+ static size_t setup_idx = 0;
+ size_t i;
+ int ints[4];
+
+ DEB(printk("NCR53c406a: Setup called\n");
+ );
+
+ if (setup_idx >= PORT_COUNT - 1) {
+ printk("NCR53c406a: Setup called too many times. Bad LILO params?\n");
+ return 0;
+ }
+ get_options(str, 4, ints);
+ if (ints[0] < 1 || ints[0] > 3) {
+ printk("NCR53c406a: Malformed command line\n");
+ printk("NCR53c406a: Usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]\n");
+ return 0;
+ }
+ for (i = 0; i < PORT_COUNT && !port_base; i++)
+ if (ports[i] == ints[1]) {
+ port_base = ints[1];
+ DEB(printk("NCR53c406a: Specified port_base 0x%x\n", port_base);
+ )
+ }
+ if (!port_base) {
+ printk("NCR53c406a: Invalid PORTBASE 0x%x specified\n", ints[1]);
+ return 0;
+ }
+
+ if (ints[0] > 1) {
+ if (ints[2] == 0) {
+ irq_level = 0;
+ DEB(printk("NCR53c406a: Specified irq %d\n", irq_level);
+ )
+ } else
+ for (i = 0; i < INTR_COUNT && irq_level < 0; i++)
+ if (intrs[i] == ints[2]) {
+ irq_level = ints[2];
+ DEB(printk("NCR53c406a: Specified irq %d\n", port_base);
+ )
+ }
+ if (irq_level < 0)
+ printk("NCR53c406a: Invalid IRQ %d specified\n", ints[2]);
+ }
+
+ if (ints[0] > 2)
+ fast_pio = ints[3];
+
+ DEB(printk("NCR53c406a: port_base=0x%x, irq=%d, fast_pio=%d\n", port_base, irq_level, fast_pio);)
+ return 1;
+}
+
+__setup("ncr53c406a=", NCR53c406a_setup);
+
+static const char *NCR53c406a_info(struct Scsi_Host *SChost)
+{
+ DEB(printk("NCR53c406a_info called\n"));
+ return (info_msg);
+}
+
+#if 0
+static void wait_intr(void)
+{
+ unsigned long i = jiffies + WATCHDOG;
+
+ while (time_after(i, jiffies) && !(inb(STAT_REG) & 0xe0)) { /* wait for a pseudo-interrupt */
+ cpu_relax();
+ barrier();
+ }
+
+ if (time_before_eq(i, jiffies)) { /* Timed out */
+ rtrc(0);
+ current_SC->result = DID_TIME_OUT << 16;
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ NCR53c406a_intr(0, NULL, NULL);
+}
+#endif
+
+static int NCR53c406a_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ int i;
+
+ VDEB(printk("NCR53c406a_queue called\n"));
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->target, SCpnt->lun, SCpnt->request_bufflen));
+
+#if 0
+ VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+#endif
+
+ current_SC = SCpnt;
+ current_SC->scsi_done = done;
+ current_SC->SCp.phase = command_ph;
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+
+ /* We are locked here already by the mid layer */
+ REG0;
+ outb(SCpnt->device->id, DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
+
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ outb(SCpnt->cmnd[i], SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, CMD_REG);
+
+ rtrc(1);
+ return 0;
+}
+
+static int NCR53c406a_abort(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("NCR53c406a_abort called\n"));
+ return FAILED; /* Don't know how to abort */
+}
+
+static int NCR53c406a_host_reset(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("NCR53c406a_reset called\n"));
+ outb(C4_IMG, CONFIG4); /* Select reg set 0 */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG); /* required after reset */
+ outb(SCSI_RESET, CMD_REG);
+ chip_init();
+
+ rtrc(2);
+ return SUCCESS;
+}
+
+static int NCR53c406a_device_reset(Scsi_Cmnd * SCpnt)
+{
+ return FAILED;
+}
+
+static int NCR53c406a_bus_reset(Scsi_Cmnd * SCpnt)
+{
+ return FAILED;
+}
+
+static int NCR53c406a_biosparm(struct scsi_device *disk,
+ struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ int size;
+
+ DEB(printk("NCR53c406a_biosparm called\n"));
+
+ size = capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size >> 11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255 * 63);
+ }
+ return 0;
+}
+
+static irqreturn_t do_NCR53c406a_intr(int unused, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ NCR53c406a_intr(0, dev_id, regs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs)
+{
+ DEB(unsigned char fifo_size;
+ )
+ DEB(unsigned char seq_reg;
+ )
+ unsigned char status, int_reg;
+#if USE_PIO
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+#endif
+
+ VDEB(printk("NCR53c406a_intr called\n"));
+
+#if USE_PIO
+ REG1;
+ pio_status = inb(PIO_STATUS);
+#endif
+ REG0;
+ status = inb(STAT_REG);
+ DEB(seq_reg = inb(SEQ_REG));
+ int_reg = inb(INT_REG);
+ DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
+
+#if NCR53C406A_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x", status, seq_reg, int_reg, fifo_size);
+#if (USE_DMA)
+ printk("\n");
+#else
+ printk(", pio=%02x\n", pio_status);
+#endif /* USE_DMA */
+#endif /* NCR53C406A_DEBUG */
+
+ if (int_reg & 0x80) { /* SCSI reset intr */
+ rtrc(3);
+ DEB(printk("NCR53c406a: reset intr received\n"));
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_RESET << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+#if USE_PIO
+ if (pio_status & 0x80) {
+ printk("NCR53C406A: Warning: PIO error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+#endif /* USE_PIO */
+
+ if (status & 0x20) { /* Parity error */
+ printk("NCR53c406a: Warning: parity error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_PARITY << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (status & 0x40) { /* Gross error */
+ printk("NCR53c406a: Warning: gross error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (int_reg & 0x20) { /* Disconnect */
+ DEB(printk("NCR53c406a: disconnect intr received\n"));
+ if (current_SC->SCp.phase != message_in) { /* Unexpected disconnect */
+ current_SC->result = DID_NO_CONNECT << 16;
+ } else { /* Command complete, return status and message */
+ current_SC->result = (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+
+ rtrc(0);
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ switch (status & 0x07) { /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ rtrc(5);
+ current_SC->SCp.phase = data_out;
+ VDEB(printk("NCR53c406a: Data-Out phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+#if USE_DMA /* No s/g support for DMA */
+ NCR53c406a_dma_write(current_SC->request_buffer, current_SC->request_bufflen);
+#endif /* USE_DMA */
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+#if USE_PIO
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ NCR53c406a_pio_write(current_SC->request_buffer, current_SC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while (sgcount--) {
+ NCR53c406a_pio_write(page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+#endif /* USE_PIO */
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ rtrc(6);
+ current_SC->SCp.phase = data_in;
+ VDEB(printk("NCR53c406a: Data-In phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+#if USE_DMA /* No s/g support for DMA */
+ NCR53c406a_dma_read(current_SC->request_buffer, current_SC->request_bufflen);
+#endif /* USE_DMA */
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+#if USE_PIO
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ NCR53c406a_pio_read(current_SC->request_buffer, current_SC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while (sgcount--) {
+ NCR53c406a_pio_read(page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+#endif /* USE_PIO */
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ current_SC->SCp.phase = command_ph;
+ printk("NCR53c406a: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ rtrc(7);
+ current_SC->SCp.phase = status_ph;
+ VDEB(printk("NCR53c406a: Status phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ outb(INIT_CMD_COMPLETE, CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("NCR53c406a: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("NCR53c406a: Message-Out phase\n"));
+ current_SC->SCp.phase = message_out;
+ outb(SET_ATN, CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ rtrc(4);
+ VDEB(printk("NCR53c406a: Message-In phase\n"));
+ current_SC->SCp.phase = message_in;
+
+ current_SC->SCp.Status = inb(SCSI_FIFO);
+ current_SC->SCp.Message = inb(SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n", current_SC->SCp.Status, current_SC->SCp.Message));
+
+ if (current_SC->SCp.Message == SAVE_POINTERS || current_SC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+ }
+}
+
+#ifndef IRQ_LEV
+static int irq_probe(void)
+{
+ int irqs, irq;
+ unsigned long i;
+
+ inb(INT_REG); /* clear the interrupt register */
+ irqs = probe_irq_on();
+
+ /* Invalid command will cause an interrupt */
+ REG0;
+ outb(0xff, CMD_REG);
+
+ /* Wait for the interrupt to occur */
+ i = jiffies + WATCHDOG;
+ while (time_after(i, jiffies) && !(inb(STAT_REG) & 0x80))
+ barrier();
+ if (time_before_eq(i, jiffies)) { /* Timed out, must be hardware trouble */
+ probe_irq_off(irqs);
+ return -1;
+ }
+
+ irq = probe_irq_off(irqs);
+
+ /* Kick the chip */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG);
+ chip_init();
+
+ return irq;
+}
+#endif /* IRQ_LEV */
+
+static void chip_init(void)
+{
+ REG1;
+#if USE_DMA
+ outb(0x00, PIO_STATUS);
+#else /* USE_PIO */
+ outb(0x01, PIO_STATUS);
+#endif
+ outb(0x00, PIO_FLAG);
+
+ outb(C4_IMG, CONFIG4); /* REG0; */
+ outb(C3_IMG, CONFIG3);
+ outb(C2_IMG, CONFIG2);
+ outb(C1_IMG, CONFIG1);
+
+ outb(0x05, CLKCONV); /* clock conversion factor */
+ outb(0x9C, SRTIMOUT); /* Selection timeout */
+ outb(0x05, SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
+}
+
+static void __init calc_port_addr(void)
+{
+ /* Control Register Set 0 */
+ TC_LSB = (port_base + 0x00);
+ TC_MSB = (port_base + 0x01);
+ SCSI_FIFO = (port_base + 0x02);
+ CMD_REG = (port_base + 0x03);
+ STAT_REG = (port_base + 0x04);
+ DEST_ID = (port_base + 0x04);
+ INT_REG = (port_base + 0x05);
+ SRTIMOUT = (port_base + 0x05);
+ SEQ_REG = (port_base + 0x06);
+ SYNCPRD = (port_base + 0x06);
+ FIFO_FLAGS = (port_base + 0x07);
+ SYNCOFF = (port_base + 0x07);
+ CONFIG1 = (port_base + 0x08);
+ CLKCONV = (port_base + 0x09);
+ /* TESTREG = (port_base+0x0A); */
+ CONFIG2 = (port_base + 0x0B);
+ CONFIG3 = (port_base + 0x0C);
+ CONFIG4 = (port_base + 0x0D);
+ TC_HIGH = (port_base + 0x0E);
+ /* FIFO_BOTTOM = (port_base+0x0F); */
+
+ /* Control Register Set 1 */
+ /* JUMPER_SENSE = (port_base+0x00); */
+ /* SRAM_PTR = (port_base+0x01); */
+ /* SRAM_DATA = (port_base+0x02); */
+ PIO_FIFO = (port_base + 0x04);
+ /* PIO_FIFO1 = (port_base+0x05); */
+ /* PIO_FIFO2 = (port_base+0x06); */
+ /* PIO_FIFO3 = (port_base+0x07); */
+ PIO_STATUS = (port_base + 0x08);
+ /* ATA_CMD = (port_base+0x09); */
+ /* ATA_ERR = (port_base+0x0A); */
+ PIO_FLAG = (port_base + 0x0B);
+ CONFIG5 = (port_base + 0x0D);
+ /* SIGNATURE = (port_base+0x0E); */
+ /* CONFIG6 = (port_base+0x0F); */
+}
+
+MODULE_LICENSE("GPL");
+
+/* NOTE: scatter-gather support only works in PIO mode.
+ * Use SG_NONE if DMA mode is enabled!
+ */
+
+static Scsi_Host_Template driver_template =
+{
+ .proc_name = "NCR53c406a" /* proc_name */,
+ .name = "NCR53c406a" /* name */,
+ .detect = NCR53c406a_detect /* detect */,
+ .release = NCR53c406a_release,
+ .info = NCR53c406a_info /* info */,
+ .queuecommand = NCR53c406a_queue /* queuecommand */,
+ .eh_abort_handler = NCR53c406a_abort /* abort */,
+ .eh_bus_reset_handler = NCR53c406a_bus_reset /* reset */,
+ .eh_device_reset_handler = NCR53c406a_device_reset /* reset */,
+ .eh_host_reset_handler = NCR53c406a_host_reset /* reset */,
+ .bios_param = NCR53c406a_biosparm /* biosparm */,
+ .can_queue = 1 /* can_queue */,
+ .this_id = 7 /* SCSI ID of the chip */,
+ .sg_tablesize = 32 /*SG_ALL*/ /*SG_NONE*/,
+ .cmd_per_lun = 1 /* commands per lun */,
+ .unchecked_isa_dma = 1 /* unchecked_isa_dma */,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+#include "scsi_module.c"
+
+/*
+ * Overrides for Emacs so that we get a uniform tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c
new file mode 100644
index 000000000000..507751941f1e
--- /dev/null
+++ b/drivers/scsi/NCR_D700.c
@@ -0,0 +1,406 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* NCR Dual 700 MCA SCSI Driver
+ *
+ * Copyright (C) 2001 by James.Bottomley@HansenPartnership.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+ */
+
+/* Notes:
+ *
+ * Most of the work is done in the chip specific module, 53c700.o
+ *
+ * TODO List:
+ *
+ * 1. Extract the SCSI ID from the voyager CMOS table (necessary to
+ * support multi-host environments.
+ *
+ * */
+
+
+/* CHANGELOG
+ *
+ * Version 2.2
+ *
+ * Added mca_set_adapter_name().
+ *
+ * Version 2.1
+ *
+ * Modularise the driver into a Board piece (this file) and a chip
+ * piece 53c700.[ch] and 53c700.scr, added module options. You can
+ * now specify the scsi id by the parameters
+ *
+ * NCR_D700=slot:<n> [siop:<n>] id:<n> ....
+ *
+ * They need to be comma separated if compiled into the kernel
+ *
+ * Version 2.0
+ *
+ * Initial implementation of TCQ (Tag Command Queueing). TCQ is full
+ * featured and uses the clock algorithm to keep track of outstanding
+ * tags and guard against individual tag starvation. Also fixed a bug
+ * in all of the 1.x versions where the D700_data_residue() function
+ * was returning results off by 32 bytes (and thus causing the same 32
+ * bytes to be written twice corrupting the data block). It turns out
+ * the 53c700 only has a 6 bit DBC and DFIFO registers not 7 bit ones
+ * like the 53c710 (The 710 is the only data manual still available,
+ * which I'd been using to program the 700).
+ *
+ * Version 1.2
+ *
+ * Much improved message handling engine
+ *
+ * Version 1.1
+ *
+ * Add code to handle selection reasonably correctly. By the time we
+ * get the selection interrupt, we've already responded, but drop off the
+ * bus and hope the selector will go away.
+ *
+ * Version 1.0:
+ *
+ * Initial release. Fully functional except for procfs and tag
+ * command queueing. Has only been tested on cards with 53c700-66
+ * chips and only single ended. Features are
+ *
+ * 1. Synchronous data transfers to offset 8 (limit of 700-66) and
+ * 100ns (10MHz) limit of SCSI-2
+ *
+ * 2. Disconnection and reselection
+ *
+ * Testing:
+ *
+ * I've only really tested this with the 700-66 chip, but have done
+ * soak tests in multi-device environments to verify that
+ * disconnections and reselections are being processed correctly.
+ * */
+
+#define NCR_D700_VERSION "2.2"
+
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mca.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "53c700.h"
+#include "NCR_D700.h"
+
+static char *NCR_D700; /* command line from insmod */
+
+MODULE_AUTHOR("James Bottomley");
+MODULE_DESCRIPTION("NCR Dual700 SCSI Driver");
+MODULE_LICENSE("GPL");
+module_param(NCR_D700, charp, 0);
+
+static __u8 __initdata id_array[2*(MCA_MAX_SLOT_NR + 1)] =
+ { [0 ... 2*(MCA_MAX_SLOT_NR + 1)-1] = 7 };
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+static int __init
+param_setup(char *string)
+{
+ char *pos = string, *next;
+ int slot = -1, siop = -1;
+
+ while(pos != NULL && (next = strchr(pos, ':')) != NULL) {
+ int val = (int)simple_strtoul(++next, NULL, 0);
+
+ if(!strncmp(pos, "slot:", 5))
+ slot = val;
+ else if(!strncmp(pos, "siop:", 5))
+ siop = val;
+ else if(!strncmp(pos, "id:", 3)) {
+ if(slot == -1) {
+ printk(KERN_WARNING "NCR D700: Must specify slot for id parameter\n");
+ } else if(slot > MCA_MAX_SLOT_NR) {
+ printk(KERN_WARNING "NCR D700: Illegal slot %d for id %d\n", slot, val);
+ } else {
+ if(siop != 0 && siop != 1) {
+ id_array[slot*2] = val;
+ id_array[slot*2 + 1] =val;
+ } else {
+ id_array[slot*2 + siop] = val;
+ }
+ }
+ }
+ if((pos = strchr(pos, ARG_SEP)) != NULL)
+ pos++;
+ }
+ return 1;
+}
+
+/* Host template. The 53c700 routine NCR_700_detect will
+ * fill in all of the missing routines */
+static struct scsi_host_template NCR_D700_driver_template = {
+ .module = THIS_MODULE,
+ .name = "NCR Dual 700 MCA",
+ .proc_name = "NCR_D700",
+ .this_id = 7,
+};
+
+/* We needs this helper because we have two hosts per struct device */
+struct NCR_D700_private {
+ struct device *dev;
+ struct Scsi_Host *hosts[2];
+ char name[30];
+ char pad;
+};
+
+static int
+NCR_D700_probe_one(struct NCR_D700_private *p, int siop, int irq,
+ int slot, u32 region, int differential)
+{
+ struct NCR_700_Host_Parameters *hostdata;
+ struct Scsi_Host *host;
+ int ret;
+
+ hostdata = kmalloc(sizeof(*hostdata), GFP_KERNEL);
+ if (!hostdata) {
+ printk(KERN_ERR "NCR D700: SIOP%d: Failed to allocate host"
+ "data, detatching\n", siop);
+ return -ENOMEM;
+ }
+ memset(hostdata, 0, sizeof(*hostdata));
+
+ if (!request_region(region, 64, "NCR_D700")) {
+ printk(KERN_ERR "NCR D700: Failed to reserve IO region 0x%x\n",
+ region);
+ ret = -ENODEV;
+ goto region_failed;
+ }
+
+ /* Fill in the three required pieces of hostdata */
+ hostdata->base = region;
+ hostdata->differential = (((1<<siop) & differential) != 0);
+ hostdata->clock = NCR_D700_CLOCK_MHZ;
+
+ NCR_700_set_io_mapped(hostdata);
+
+ /* and register the siop */
+ host = NCR_700_detect(&NCR_D700_driver_template, hostdata, p->dev);
+ if (!host) {
+ ret = -ENOMEM;
+ goto detect_failed;
+ }
+
+ p->hosts[siop] = host;
+ /* FIXME: read this from SUS */
+ host->this_id = id_array[slot * 2 + siop];
+ host->irq = irq;
+ scsi_scan_host(host);
+
+ return 0;
+
+ detect_failed:
+ release_region(host->base, 64);
+ region_failed:
+ kfree(hostdata);
+
+ return ret;
+}
+
+static int
+NCR_D700_intr(int irq, void *data, struct pt_regs *regs)
+{
+ struct NCR_D700_private *p = (struct NCR_D700_private *)data;
+ int i, found = 0;
+
+ for (i = 0; i < 2; i++)
+ if (p->hosts[i] &&
+ NCR_700_intr(irq, p->hosts[i], regs) == IRQ_HANDLED)
+ found++;
+
+ return found ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* Detect a D700 card. Note, because of the setup --- the chips are
+ * essentially connectecd to the MCA bus independently, it is easier
+ * to set them up as two separate host adapters, rather than one
+ * adapter with two channels */
+static int
+NCR_D700_probe(struct device *dev)
+{
+ struct NCR_D700_private *p;
+ int differential;
+ static int banner = 1;
+ struct mca_device *mca_dev = to_mca_device(dev);
+ int slot = mca_dev->slot;
+ int found = 0;
+ int irq, i;
+ int pos3j, pos3k, pos3a, pos3b, pos4;
+ __u32 base_addr, offset_addr;
+
+ /* enable board interrupt */
+ pos4 = mca_device_read_pos(mca_dev, 4);
+ pos4 |= 0x4;
+ mca_device_write_pos(mca_dev, 4, pos4);
+
+ mca_device_write_pos(mca_dev, 6, 9);
+ pos3j = mca_device_read_pos(mca_dev, 3);
+ mca_device_write_pos(mca_dev, 6, 10);
+ pos3k = mca_device_read_pos(mca_dev, 3);
+ mca_device_write_pos(mca_dev, 6, 0);
+ pos3a = mca_device_read_pos(mca_dev, 3);
+ mca_device_write_pos(mca_dev, 6, 1);
+ pos3b = mca_device_read_pos(mca_dev, 3);
+
+ base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0;
+ offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70;
+
+ irq = (pos4 & 0x3) + 11;
+ if(irq >= 13)
+ irq++;
+ if(banner) {
+ printk(KERN_NOTICE "NCR D700: Driver Version " NCR_D700_VERSION "\n"
+ "NCR D700: Copyright (c) 2001 by James.Bottomley@HansenPartnership.com\n"
+ "NCR D700:\n");
+ banner = 0;
+ }
+ /* now do the bus related transforms */
+ irq = mca_device_transform_irq(mca_dev, irq);
+ base_addr = mca_device_transform_ioport(mca_dev, base_addr);
+ offset_addr = mca_device_transform_ioport(mca_dev, offset_addr);
+
+ printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = 0x%x\n", slot, irq, offset_addr);
+
+ /*outb(BOARD_RESET, base_addr);*/
+
+ /* clear any pending interrupts */
+ (void)inb(base_addr + 0x08);
+ /* get modctl, used later for setting diff bits */
+ switch(differential = (inb(base_addr + 0x08) >> 6)) {
+ case 0x00:
+ /* only SIOP1 differential */
+ differential = 0x02;
+ break;
+ case 0x01:
+ /* Both SIOPs differential */
+ differential = 0x03;
+ break;
+ case 0x03:
+ /* No SIOPs differential */
+ differential = 0x00;
+ break;
+ default:
+ printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT 0x%02x\n",
+ differential);
+ differential = 0x00;
+ break;
+ }
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ memset(p, '\0', sizeof(*p));
+ p->dev = dev;
+ snprintf(p->name, sizeof(p->name), "D700(%s)", dev->bus_id);
+ if (request_irq(irq, NCR_D700_intr, SA_SHIRQ, p->name, p)) {
+ printk(KERN_ERR "D700: request_irq failed\n");
+ kfree(p);
+ return -EBUSY;
+ }
+ /* plumb in both 700 chips */
+ for (i = 0; i < 2; i++) {
+ int err;
+
+ if ((err = NCR_D700_probe_one(p, i, slot, irq,
+ offset_addr + (0x80 * i),
+ differential)) != 0)
+ printk("D700: SIOP%d: probe failed, error = %d\n",
+ i, err);
+ else
+ found++;
+ }
+
+ if (!found) {
+ kfree(p);
+ return -ENODEV;
+ }
+
+ mca_device_set_claim(mca_dev, 1);
+ mca_device_set_name(mca_dev, "NCR_D700");
+ dev_set_drvdata(dev, p);
+ return 0;
+}
+
+static void
+NCR_D700_remove_one(struct Scsi_Host *host)
+{
+ scsi_remove_host(host);
+ NCR_700_release(host);
+ kfree((struct NCR_700_Host_Parameters *)host->hostdata[0]);
+ free_irq(host->irq, host);
+ release_region(host->base, 64);
+}
+
+static int
+NCR_D700_remove(struct device *dev)
+{
+ struct NCR_D700_private *p = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < 2; i++)
+ NCR_D700_remove_one(p->hosts[i]);
+
+ kfree(p);
+ return 0;
+}
+
+static short NCR_D700_id_table[] = { NCR_D700_MCA_ID, 0 };
+
+static struct mca_driver NCR_D700_driver = {
+ .id_table = NCR_D700_id_table,
+ .driver = {
+ .name = "NCR_D700",
+ .bus = &mca_bus_type,
+ .probe = NCR_D700_probe,
+ .remove = NCR_D700_remove,
+ },
+};
+
+static int __init NCR_D700_init(void)
+{
+#ifdef MODULE
+ if (NCR_D700)
+ param_setup(NCR_D700);
+#endif
+
+ return mca_register_driver(&NCR_D700_driver);
+}
+
+static void __exit NCR_D700_exit(void)
+{
+ mca_unregister_driver(&NCR_D700_driver);
+}
+
+module_init(NCR_D700_init);
+module_exit(NCR_D700_exit);
+
+__setup("NCR_D700=", param_setup);
diff --git a/drivers/scsi/NCR_D700.h b/drivers/scsi/NCR_D700.h
new file mode 100644
index 000000000000..f167af6bd2af
--- /dev/null
+++ b/drivers/scsi/NCR_D700.h
@@ -0,0 +1,29 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* NCR Dual 700 MCA SCSI Driver
+ *
+ * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com
+ */
+
+#ifndef _NCR_D700_H
+#define _NCR_D700_H
+
+/* Don't turn on debugging messages */
+#undef NCR_D700_DEBUG
+
+/* The MCA identifier */
+#define NCR_D700_MCA_ID 0x0092
+
+/* Defines for the Board registers */
+#define BOARD_RESET 0x80 /* board level reset */
+#define ADD_PARENB 0x04 /* Address Parity Enabled */
+#define DAT_PARENB 0x01 /* Data Parity Enabled */
+#define SFBK_ENB 0x10 /* SFDBK Interrupt Enabled */
+#define LED0GREEN 0x20 /* Led 0 (red 0; green 1) */
+#define LED1GREEN 0x40 /* Led 1 (red 0; green 1) */
+#define LED0RED 0xDF /* Led 0 (red 0; green 1) */
+#define LED1RED 0xBF /* Led 1 (red 0; green 1) */
+
+#define NCR_D700_CLOCK_MHZ 50
+
+#endif
diff --git a/drivers/scsi/NCR_Q720.c b/drivers/scsi/NCR_Q720.c
new file mode 100644
index 000000000000..9d18ec90510f
--- /dev/null
+++ b/drivers/scsi/NCR_Q720.c
@@ -0,0 +1,377 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* NCR Quad 720 MCA SCSI Driver
+ *
+ * Copyright (C) 2003 by James.Bottomley@HansenPartnership.com
+ */
+
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mca.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#include "ncr53c8xx.h"
+
+#include "NCR_Q720.h"
+
+static struct ncr_chip q720_chip __initdata = {
+ .revision_id = 0x0f,
+ .burst_max = 3,
+ .offset_max = 8,
+ .nr_divisor = 4,
+ .features = FE_WIDE | FE_DIFF | FE_VARCLK,
+};
+
+MODULE_AUTHOR("James Bottomley");
+MODULE_DESCRIPTION("NCR Quad 720 SCSI Driver");
+MODULE_LICENSE("GPL");
+
+#define NCR_Q720_VERSION "0.9"
+
+/* We needs this helper because we have up to four hosts per struct device */
+struct NCR_Q720_private {
+ struct device *dev;
+ void __iomem * mem_base;
+ __u32 phys_mem_base;
+ __u32 mem_size;
+ __u8 irq;
+ __u8 siops;
+ __u8 irq_enable;
+ struct Scsi_Host *hosts[4];
+};
+
+static struct scsi_host_template NCR_Q720_tpnt = {
+ .module = THIS_MODULE,
+ .proc_name = "NCR_Q720",
+};
+
+static irqreturn_t
+NCR_Q720_intr(int irq, void *data, struct pt_regs * regs)
+{
+ struct NCR_Q720_private *p = (struct NCR_Q720_private *)data;
+ __u8 sir = (readb(p->mem_base + 0x0d) & 0xf0) >> 4;
+ __u8 siop;
+
+ sir |= ~p->irq_enable;
+
+ if(sir == 0xff)
+ return IRQ_NONE;
+
+
+ while((siop = ffz(sir)) < p->siops) {
+ sir |= 1<<siop;
+ ncr53c8xx_intr(irq, p->hosts[siop], regs);
+ }
+ return IRQ_HANDLED;
+}
+
+static int __init
+NCR_Q720_probe_one(struct NCR_Q720_private *p, int siop,
+ int irq, int slot, __u32 paddr, void __iomem *vaddr)
+{
+ struct ncr_device device;
+ __u8 scsi_id;
+ static int unit = 0;
+ __u8 scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1);
+ __u8 differential = readb(vaddr + NCR_Q720_SCSR_OFFSET) & 0x20;
+ __u8 version;
+ int error;
+
+ scsi_id = scsr1 >> 4;
+ /* enable burst length 16 (FIXME: should allow this) */
+ scsr1 |= 0x02;
+ /* force a siop reset */
+ scsr1 |= 0x04;
+ writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1);
+ udelay(10);
+ version = readb(vaddr + 0x18) >> 4;
+
+ memset(&device, 0, sizeof(struct ncr_device));
+ /* Initialise ncr_device structure with items required by ncr_attach. */
+ device.chip = q720_chip;
+ device.chip.revision_id = version;
+ device.host_id = scsi_id;
+ device.dev = p->dev;
+ device.slot.base = paddr;
+ device.slot.base_c = paddr;
+ device.slot.base_v = vaddr;
+ device.slot.irq = irq;
+ device.differential = differential ? 2 : 0;
+ printk("Q720 probe unit %d (siop%d) at 0x%lx, diff = %d, vers = %d\n", unit, siop,
+ (unsigned long)paddr, differential, version);
+
+ p->hosts[siop] = ncr_attach(&NCR_Q720_tpnt, unit++, &device);
+
+ if (!p->hosts[siop])
+ goto fail;
+
+ p->irq_enable |= (1<<siop);
+ scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1);
+ /* clear the disable interrupt bit */
+ scsr1 &= ~0x01;
+ writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1);
+
+ error = scsi_add_host(p->hosts[siop], p->dev);
+ if (error)
+ ncr53c8xx_release(p->hosts[siop]);
+ else
+ scsi_scan_host(p->hosts[siop]);
+ return error;
+
+ fail:
+ return -ENODEV;
+}
+
+/* Detect a Q720 card. Note, because of the setup --- the chips are
+ * essentially connectecd to the MCA bus independently, it is easier
+ * to set them up as two separate host adapters, rather than one
+ * adapter with two channels */
+static int __init
+NCR_Q720_probe(struct device *dev)
+{
+ struct NCR_Q720_private *p;
+ static int banner = 1;
+ struct mca_device *mca_dev = to_mca_device(dev);
+ int slot = mca_dev->slot;
+ int found = 0;
+ int irq, i, siops;
+ __u8 pos2, pos4, asr2, asr9, asr10;
+ __u16 io_base;
+ __u32 base_addr, mem_size;
+ void __iomem *mem_base;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ memset(p, 0, sizeof(*p));
+ pos2 = mca_device_read_pos(mca_dev, 2);
+ /* enable device */
+ pos2 |= NCR_Q720_POS2_BOARD_ENABLE | NCR_Q720_POS2_INTERRUPT_ENABLE;
+ mca_device_write_pos(mca_dev, 2, pos2);
+
+ io_base = (pos2 & NCR_Q720_POS2_IO_MASK) << NCR_Q720_POS2_IO_SHIFT;
+
+
+ if(banner) {
+ printk(KERN_NOTICE "NCR Q720: Driver Version " NCR_Q720_VERSION "\n"
+ "NCR Q720: Copyright (c) 2003 by James.Bottomley@HansenPartnership.com\n"
+ "NCR Q720:\n");
+ banner = 0;
+ }
+ io_base = mca_device_transform_ioport(mca_dev, io_base);
+
+ /* OK, this is phase one of the bootstrap, we now know the
+ * I/O space base address. All the configuration registers
+ * are mapped here (including pos) */
+
+ /* sanity check I/O mapping */
+ i = inb(io_base) | (inb(io_base+1)<<8);
+ if(i != NCR_Q720_MCA_ID) {
+ printk(KERN_ERR "NCR_Q720, adapter failed to I/O map registers correctly at 0x%x(0x%x)\n", io_base, i);
+ kfree(p);
+ return -ENODEV;
+ }
+
+ /* Phase II, find the ram base and memory map the board register */
+ pos4 = inb(io_base + 4);
+ /* enable streaming data */
+ pos4 |= 0x01;
+ outb(pos4, io_base + 4);
+ base_addr = (pos4 & 0x7e) << 20;
+ base_addr += (pos4 & 0x80) << 23;
+ asr10 = inb(io_base + 0x12);
+ base_addr += (asr10 & 0x80) << 24;
+ base_addr += (asr10 & 0x70) << 23;
+
+ /* OK, got the base addr, now we need to find the ram size,
+ * enable and map it */
+ asr9 = inb(io_base + 0x11);
+ i = (asr9 & 0xc0) >> 6;
+ if(i == 0)
+ mem_size = 1024;
+ else
+ mem_size = 1 << (19 + i);
+
+ /* enable the sram mapping */
+ asr9 |= 0x20;
+
+ /* disable the rom mapping */
+ asr9 &= ~0x10;
+
+ outb(asr9, io_base + 0x11);
+
+ if(!request_mem_region(base_addr, mem_size, "NCR_Q720")) {
+ printk(KERN_ERR "NCR_Q720: Failed to claim memory region 0x%lx\n-0x%lx",
+ (unsigned long)base_addr,
+ (unsigned long)(base_addr + mem_size));
+ goto out_free;
+ }
+
+ if (dma_declare_coherent_memory(dev, base_addr, base_addr,
+ mem_size, DMA_MEMORY_MAP)
+ != DMA_MEMORY_MAP) {
+ printk(KERN_ERR "NCR_Q720: DMA declare memory failed\n");
+ goto out_release_region;
+ }
+
+ /* The first 1k of the memory buffer is a memory map of the registers
+ */
+ mem_base = dma_mark_declared_memory_occupied(dev, base_addr,
+ 1024);
+ if (IS_ERR(mem_base)) {
+ printk("NCR_Q720 failed to reserve memory mapped region\n");
+ goto out_release;
+ }
+
+ /* now also enable accesses in asr 2 */
+ asr2 = inb(io_base + 0x0a);
+
+ asr2 |= 0x01;
+
+ outb(asr2, io_base + 0x0a);
+
+ /* get the number of SIOPs (this should be 2 or 4) */
+ siops = ((asr2 & 0xe0) >> 5) + 1;
+
+ /* sanity check mapping (again) */
+ i = readw(mem_base);
+ if(i != NCR_Q720_MCA_ID) {
+ printk(KERN_ERR "NCR_Q720, adapter failed to memory map registers correctly at 0x%lx(0x%x)\n", (unsigned long)base_addr, i);
+ goto out_release;
+ }
+
+ irq = readb(mem_base + 5) & 0x0f;
+
+
+ /* now do the bus related transforms */
+ irq = mca_device_transform_irq(mca_dev, irq);
+
+ printk(KERN_NOTICE "NCR Q720: found in slot %d irq = %d mem base = 0x%lx siops = %d\n", slot, irq, (unsigned long)base_addr, siops);
+ printk(KERN_NOTICE "NCR Q720: On board ram %dk\n", mem_size/1024);
+
+ p->dev = dev;
+ p->mem_base = mem_base;
+ p->phys_mem_base = base_addr;
+ p->mem_size = mem_size;
+ p->irq = irq;
+ p->siops = siops;
+
+ if (request_irq(irq, NCR_Q720_intr, SA_SHIRQ, "NCR_Q720", p)) {
+ printk(KERN_ERR "NCR_Q720: request irq %d failed\n", irq);
+ goto out_release;
+ }
+ /* disable all the siop interrupts */
+ for(i = 0; i < siops; i++) {
+ void __iomem *reg_scsr1 = mem_base + NCR_Q720_CHIP_REGISTER_OFFSET
+ + i*NCR_Q720_SIOP_SHIFT + NCR_Q720_SCSR_OFFSET + 1;
+ __u8 scsr1 = readb(reg_scsr1);
+ scsr1 |= 0x01;
+ writeb(scsr1, reg_scsr1);
+ }
+
+ /* plumb in all 720 chips */
+ for (i = 0; i < siops; i++) {
+ void __iomem *siop_v_base = mem_base + NCR_Q720_CHIP_REGISTER_OFFSET
+ + i*NCR_Q720_SIOP_SHIFT;
+ __u32 siop_p_base = base_addr + NCR_Q720_CHIP_REGISTER_OFFSET
+ + i*NCR_Q720_SIOP_SHIFT;
+ __u16 port = io_base + NCR_Q720_CHIP_REGISTER_OFFSET
+ + i*NCR_Q720_SIOP_SHIFT;
+ int err;
+
+ outb(0xff, port + 0x40);
+ outb(0x07, port + 0x41);
+ if ((err = NCR_Q720_probe_one(p, i, irq, slot,
+ siop_p_base, siop_v_base)) != 0)
+ printk("Q720: SIOP%d: probe failed, error = %d\n",
+ i, err);
+ else
+ found++;
+ }
+
+ if (!found) {
+ kfree(p);
+ return -ENODEV;
+ }
+
+ mca_device_set_claim(mca_dev, 1);
+ mca_device_set_name(mca_dev, "NCR_Q720");
+ dev_set_drvdata(dev, p);
+
+ return 0;
+
+ out_release:
+ dma_release_declared_memory(dev);
+ out_release_region:
+ release_mem_region(base_addr, mem_size);
+ out_free:
+ kfree(p);
+
+ return -ENODEV;
+}
+
+static void __exit
+NCR_Q720_remove_one(struct Scsi_Host *host)
+{
+ scsi_remove_host(host);
+ ncr53c8xx_release(host);
+}
+
+static int __exit
+NCR_Q720_remove(struct device *dev)
+{
+ struct NCR_Q720_private *p = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < p->siops; i++)
+ if(p->hosts[i])
+ NCR_Q720_remove_one(p->hosts[i]);
+
+ dma_release_declared_memory(dev);
+ release_mem_region(p->phys_mem_base, p->mem_size);
+ free_irq(p->irq, p);
+ kfree(p);
+ return 0;
+}
+
+static short NCR_Q720_id_table[] = { NCR_Q720_MCA_ID, 0 };
+
+static struct mca_driver NCR_Q720_driver = {
+ .id_table = NCR_Q720_id_table,
+ .driver = {
+ .name = "NCR_Q720",
+ .bus = &mca_bus_type,
+ .probe = NCR_Q720_probe,
+ .remove = __devexit_p(NCR_Q720_remove),
+ },
+};
+
+static int __init
+NCR_Q720_init(void)
+{
+ int ret = ncr53c8xx_init();
+ if (!ret)
+ ret = mca_register_driver(&NCR_Q720_driver);
+ if (ret)
+ ncr53c8xx_exit();
+ return ret;
+}
+
+static void __exit
+NCR_Q720_exit(void)
+{
+ mca_unregister_driver(&NCR_Q720_driver);
+ ncr53c8xx_exit();
+}
+
+module_init(NCR_Q720_init);
+module_exit(NCR_Q720_exit);
diff --git a/drivers/scsi/NCR_Q720.h b/drivers/scsi/NCR_Q720.h
new file mode 100644
index 000000000000..7b9209008187
--- /dev/null
+++ b/drivers/scsi/NCR_Q720.h
@@ -0,0 +1,28 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* NCR Quad 720 MCA SCSI Driver
+ *
+ * Copyright (C) 2003 by James.Bottomley@HansenPartnership.com
+ */
+
+#ifndef _NCR_Q720_H
+#define _NCR_Q720_H
+
+/* The MCA identifier */
+#define NCR_Q720_MCA_ID 0x0720
+
+#define NCR_Q720_CLOCK_MHZ 30
+
+#define NCR_Q720_POS2_BOARD_ENABLE 0x01
+#define NCR_Q720_POS2_INTERRUPT_ENABLE 0x02
+#define NCR_Q720_POS2_PARITY_DISABLE 0x04
+#define NCR_Q720_POS2_IO_MASK 0xf8
+#define NCR_Q720_POS2_IO_SHIFT 8
+
+#define NCR_Q720_CHIP_REGISTER_OFFSET 0x200
+#define NCR_Q720_SCSR_OFFSET 0x070
+#define NCR_Q720_SIOP_SHIFT 0x080
+
+#endif
+
+
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
new file mode 100644
index 000000000000..c34403c30483
--- /dev/null
+++ b/drivers/scsi/a100u2w.c
@@ -0,0 +1,1202 @@
+/*
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 2003-2004 Christoph Hellwig
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Revision History:
+ * 07/02/98 hl - v.91n Initial drivers.
+ * 09/14/98 hl - v1.01 Support new Kernel.
+ * 09/22/98 hl - v1.01a Support reset.
+ * 09/24/98 hl - v1.01b Fixed reset.
+ * 10/05/98 hl - v1.02 split the source code and release.
+ * 12/19/98 bv - v1.02a Use spinlocks for 2.1.95 and up
+ * 01/31/99 bv - v1.02b Use mdelay instead of waitForPause
+ * 08/08/99 bv - v1.02c Use waitForPause again.
+ * 06/25/02 Doug Ledford <dledford@redhat.com> - v1.02d
+ * - Remove limit on number of controllers
+ * - Port to DMA mapping API
+ * - Clean up interrupt handler registration
+ * - Fix memory leaks
+ * - Fix allocation of scsi host structs and private data
+ * 11/18/03 Christoph Hellwig <hch@lst.de>
+ * - Port to new probing API
+ * - Fix some more leaks in init failure cases
+ * 9/28/04 Christoph Hellwig <hch@lst.de>
+ * - merge the two source files
+ * - remove internal queueing code
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "a100u2w.h"
+
+
+#define JIFFIES_TO_MS(t) ((t) * 1000 / HZ)
+#define MS_TO_JIFFIES(j) ((j * HZ) / 1000)
+
+static ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp);
+static void inia100SCBPost(BYTE * pHcb, BYTE * pScb);
+
+static NVRAM nvram, *nvramp = &nvram;
+static UCHAR dftNvRam[64] =
+{
+/*----------header -------------*/
+ 0x01, /* 0x00: Sub System Vendor ID 0 */
+ 0x11, /* 0x01: Sub System Vendor ID 1 */
+ 0x60, /* 0x02: Sub System ID 0 */
+ 0x10, /* 0x03: Sub System ID 1 */
+ 0x00, /* 0x04: SubClass */
+ 0x01, /* 0x05: Vendor ID 0 */
+ 0x11, /* 0x06: Vendor ID 1 */
+ 0x60, /* 0x07: Device ID 0 */
+ 0x10, /* 0x08: Device ID 1 */
+ 0x00, /* 0x09: Reserved */
+ 0x00, /* 0x0A: Reserved */
+ 0x01, /* 0x0B: Revision of Data Structure */
+ /* -- Host Adapter Structure --- */
+ 0x01, /* 0x0C: Number Of SCSI Channel */
+ 0x01, /* 0x0D: BIOS Configuration 1 */
+ 0x00, /* 0x0E: BIOS Configuration 2 */
+ 0x00, /* 0x0F: BIOS Configuration 3 */
+ /* --- SCSI Channel 0 Configuration --- */
+ 0x07, /* 0x10: H/A ID */
+ 0x83, /* 0x11: Channel Configuration */
+ 0x20, /* 0x12: MAX TAG per target */
+ 0x0A, /* 0x13: SCSI Reset Recovering time */
+ 0x00, /* 0x14: Channel Configuration4 */
+ 0x00, /* 0x15: Channel Configuration5 */
+ /* SCSI Channel 0 Target Configuration */
+ /* 0x16-0x25 */
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ /* --- SCSI Channel 1 Configuration --- */
+ 0x07, /* 0x26: H/A ID */
+ 0x83, /* 0x27: Channel Configuration */
+ 0x20, /* 0x28: MAX TAG per target */
+ 0x0A, /* 0x29: SCSI Reset Recovering time */
+ 0x00, /* 0x2A: Channel Configuration4 */
+ 0x00, /* 0x2B: Channel Configuration5 */
+ /* SCSI Channel 1 Target Configuration */
+ /* 0x2C-0x3B */
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ 0x00, /* 0x3C: Reserved */
+ 0x00, /* 0x3D: Reserved */
+ 0x00, /* 0x3E: Reserved */
+ 0x00 /* 0x3F: Checksum */
+};
+
+
+/***************************************************************************/
+static void waitForPause(unsigned amount)
+{
+ ULONG the_time = jiffies + MS_TO_JIFFIES(amount);
+ while (time_before_eq(jiffies, the_time))
+ cpu_relax();
+}
+
+/***************************************************************************/
+static UCHAR waitChipReady(ORC_HCS * hcsp)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
+ if (ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & HOSTSTOP) /* Wait HOSTSTOP set */
+ return 1;
+ waitForPause(100); /* wait 100ms before try again */
+ }
+ return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitFWReady(ORC_HCS * hcsp)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
+ if (ORC_RD(hcsp->HCS_Base, ORC_HSTUS) & RREADY) /* Wait READY set */
+ return 1;
+ waitForPause(100); /* wait 100ms before try again */
+ }
+ return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitSCSIRSTdone(ORC_HCS * hcsp)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
+ if (!(ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & SCSIRST)) /* Wait SCSIRST done */
+ return 1;
+ waitForPause(100); /* wait 100ms before try again */
+ }
+ return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitHDOoff(ORC_HCS * hcsp)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
+ if (!(ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & HDO)) /* Wait HDO off */
+ return 1;
+ waitForPause(100); /* wait 100ms before try again */
+ }
+ return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitHDIset(ORC_HCS * hcsp, UCHAR * pData)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) { /* Wait 1 second for report timeout */
+ if ((*pData = ORC_RD(hcsp->HCS_Base, ORC_HSTUS)) & HDI)
+ return 1; /* Wait HDI set */
+ waitForPause(100); /* wait 100ms before try again */
+ }
+ return 0;
+}
+
+/***************************************************************************/
+static unsigned short get_FW_version(ORC_HCS * hcsp)
+{
+ UCHAR bData;
+ union {
+ unsigned short sVersion;
+ unsigned char cVersion[2];
+ } Version;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_VERSION);
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ if (waitHDIset(hcsp, &bData) == 0) /* Wait HDI set */
+ return 0;
+ Version.cVersion[0] = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+ ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData); /* Clear HDI */
+
+ if (waitHDIset(hcsp, &bData) == 0) /* Wait HDI set */
+ return 0;
+ Version.cVersion[1] = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+ ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData); /* Clear HDI */
+
+ return (Version.sVersion);
+}
+
+/***************************************************************************/
+static UCHAR set_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char value)
+{
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_SET_NVM); /* Write command */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, address); /* Write address */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, value); /* Write value */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ return 1;
+}
+
+/***************************************************************************/
+static UCHAR get_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char *pDataIn)
+{
+ unsigned char bData;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_GET_NVM); /* Write command */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, address); /* Write address */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ if (waitHDIset(hcsp, &bData) == 0) /* Wait HDI set */
+ return 0;
+ *pDataIn = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+ ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData); /* Clear HDI */
+
+ return 1;
+}
+
+/***************************************************************************/
+static void orc_exec_scb(ORC_HCS * hcsp, ORC_SCB * scbp)
+{
+ scbp->SCB_Status = ORCSCB_POST;
+ ORC_WR(hcsp->HCS_Base + ORC_PQUEUE, scbp->SCB_ScbIdx);
+ return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+static int se2_rd_all(ORC_HCS * hcsp)
+{
+ int i;
+ UCHAR *np, chksum = 0;
+
+ np = (UCHAR *) nvramp;
+ for (i = 0; i < 64; i++, np++) { /* <01> */
+ if (get_NVRAM(hcsp, (unsigned char) i, np) == 0)
+ return -1;
+// *np++ = get_NVRAM(hcsp, (unsigned char ) i);
+ }
+
+/*------ Is ckecksum ok ? ------*/
+ np = (UCHAR *) nvramp;
+ for (i = 0; i < 63; i++)
+ chksum += *np++;
+
+ if (nvramp->CheckSum != (UCHAR) chksum)
+ return -1;
+ return 1;
+}
+
+/************************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+*************************************************************************/
+static void se2_update_all(ORC_HCS * hcsp)
+{ /* setup default pattern */
+ int i;
+ UCHAR *np, *np1, chksum = 0;
+
+ /* Calculate checksum first */
+ np = (UCHAR *) dftNvRam;
+ for (i = 0; i < 63; i++)
+ chksum += *np++;
+ *np = chksum;
+
+ np = (UCHAR *) dftNvRam;
+ np1 = (UCHAR *) nvramp;
+ for (i = 0; i < 64; i++, np++, np1++) {
+ if (*np != *np1) {
+ set_NVRAM(hcsp, (unsigned char) i, *np);
+ }
+ }
+ return;
+}
+
+/*************************************************************************
+ Function name : read_eeprom
+**************************************************************************/
+static void read_eeprom(ORC_HCS * hcsp)
+{
+ if (se2_rd_all(hcsp) != 1) {
+ se2_update_all(hcsp); /* setup default pattern */
+ se2_rd_all(hcsp); /* load again */
+ }
+}
+
+
+/***************************************************************************/
+static UCHAR load_FW(ORC_HCS * hcsp)
+{
+ U32 dData;
+ USHORT wBIOSAddress;
+ USHORT i;
+ UCHAR *pData, bData;
+
+
+ bData = ORC_RD(hcsp->HCS_Base, ORC_GCFG);
+ ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData | EEPRG); /* Enable EEPROM programming */
+ ORC_WR(hcsp->HCS_Base + ORC_EBIOSADR2, 0x00);
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x00);
+ if (ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA) != 0x55) {
+ ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData); /* Disable EEPROM programming */
+ return 0;
+ }
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x01);
+ if (ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA) != 0xAA) {
+ ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData); /* Disable EEPROM programming */
+ return 0;
+ }
+ ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST | DOWNLOAD); /* Enable SRAM programming */
+ pData = (UCHAR *) & dData;
+ dData = 0; /* Initial FW address to 0 */
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x10);
+ *pData = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA); /* Read from BIOS */
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x11);
+ *(pData + 1) = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA); /* Read from BIOS */
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x12);
+ *(pData + 2) = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA); /* Read from BIOS */
+ ORC_WR(hcsp->HCS_Base + ORC_EBIOSADR2, *(pData + 2));
+ ORC_WRLONG(hcsp->HCS_Base + ORC_FWBASEADR, dData); /* Write FW address */
+
+ wBIOSAddress = (USHORT) dData; /* FW code locate at BIOS address + ? */
+ for (i = 0, pData = (UCHAR *) & dData; /* Download the code */
+ i < 0x1000; /* Firmware code size = 4K */
+ i++, wBIOSAddress++) {
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, wBIOSAddress);
+ *pData++ = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA); /* Read from BIOS */
+ if ((i % 4) == 3) {
+ ORC_WRLONG(hcsp->HCS_Base + ORC_RISCRAM, dData); /* Write every 4 bytes */
+ pData = (UCHAR *) & dData;
+ }
+ }
+
+ ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST | DOWNLOAD); /* Reset program count 0 */
+ wBIOSAddress -= 0x1000; /* Reset the BIOS adddress */
+ for (i = 0, pData = (UCHAR *) & dData; /* Check the code */
+ i < 0x1000; /* Firmware code size = 4K */
+ i++, wBIOSAddress++) {
+ ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, wBIOSAddress);
+ *pData++ = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA); /* Read from BIOS */
+ if ((i % 4) == 3) {
+ if (ORC_RDLONG(hcsp->HCS_Base, ORC_RISCRAM) != dData) {
+ ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST); /* Reset program to 0 */
+ ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData); /*Disable EEPROM programming */
+ return 0;
+ }
+ pData = (UCHAR *) & dData;
+ }
+ }
+ ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST); /* Reset program to 0 */
+ ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData); /* Disable EEPROM programming */
+ return 1;
+}
+
+/***************************************************************************/
+static void setup_SCBs(ORC_HCS * hcsp)
+{
+ ORC_SCB *pVirScb;
+ int i;
+ ESCB *pVirEscb;
+ dma_addr_t pPhysEscb;
+
+ /* Setup SCB HCS_Base and SCB Size registers */
+ ORC_WR(hcsp->HCS_Base + ORC_SCBSIZE, ORC_MAXQUEUE); /* Total number of SCBs */
+ /* SCB HCS_Base address 0 */
+ ORC_WRLONG(hcsp->HCS_Base + ORC_SCBBASE0, hcsp->HCS_physScbArray);
+ /* SCB HCS_Base address 1 */
+ ORC_WRLONG(hcsp->HCS_Base + ORC_SCBBASE1, hcsp->HCS_physScbArray);
+
+ /* setup scatter list address with one buffer */
+ pVirScb = hcsp->HCS_virScbArray;
+ pVirEscb = hcsp->HCS_virEscbArray;
+
+ for (i = 0; i < ORC_MAXQUEUE; i++) {
+ pPhysEscb = (hcsp->HCS_physEscbArray + (sizeof(ESCB) * i));
+ pVirScb->SCB_SGPAddr = (U32) pPhysEscb;
+ pVirScb->SCB_SensePAddr = (U32) pPhysEscb;
+ pVirScb->SCB_EScb = pVirEscb;
+ pVirScb->SCB_ScbIdx = i;
+ pVirScb++;
+ pVirEscb++;
+ }
+
+ return;
+}
+
+/***************************************************************************/
+static void initAFlag(ORC_HCS * hcsp)
+{
+ UCHAR i, j;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ for (j = 0; j < 8; j++) {
+ hcsp->BitAllocFlag[i][j] = 0xffffffff;
+ }
+ }
+}
+
+/***************************************************************************/
+static int init_orchid(ORC_HCS * hcsp)
+{
+ UBYTE *readBytep;
+ USHORT revision;
+ UCHAR i;
+
+ initAFlag(hcsp);
+ ORC_WR(hcsp->HCS_Base + ORC_GIMSK, 0xFF); /* Disable all interrupt */
+ if (ORC_RD(hcsp->HCS_Base, ORC_HSTUS) & RREADY) { /* Orchid is ready */
+ revision = get_FW_version(hcsp);
+ if (revision == 0xFFFF) {
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, DEVRST); /* Reset Host Adapter */
+ if (waitChipReady(hcsp) == 0)
+ return (-1);
+ load_FW(hcsp); /* Download FW */
+ setup_SCBs(hcsp); /* Setup SCB HCS_Base and SCB Size registers */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, 0); /* clear HOSTSTOP */
+ if (waitFWReady(hcsp) == 0)
+ return (-1);
+ /* Wait for firmware ready */
+ } else {
+ setup_SCBs(hcsp); /* Setup SCB HCS_Base and SCB Size registers */
+ }
+ } else { /* Orchid is not Ready */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, DEVRST); /* Reset Host Adapter */
+ if (waitChipReady(hcsp) == 0)
+ return (-1);
+ load_FW(hcsp); /* Download FW */
+ setup_SCBs(hcsp); /* Setup SCB HCS_Base and SCB Size registers */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO); /* Do Hardware Reset & */
+
+ /* clear HOSTSTOP */
+ if (waitFWReady(hcsp) == 0) /* Wait for firmware ready */
+ return (-1);
+ }
+
+/*------------- get serial EEProm settting -------*/
+
+ read_eeprom(hcsp);
+
+ if (nvramp->Revision != 1)
+ return (-1);
+
+ hcsp->HCS_SCSI_ID = nvramp->SCSI0Id;
+ hcsp->HCS_BIOS = nvramp->BIOSConfig1;
+ hcsp->HCS_MaxTar = MAX_TARGETS;
+ readBytep = (UCHAR *) & (nvramp->Target00Config);
+ for (i = 0; i < 16; readBytep++, i++) {
+ hcsp->TargetFlag[i] = *readBytep;
+ hcsp->MaximumTags[i] = ORC_MAXTAGS;
+ } /* for */
+
+ if (nvramp->SCSI0Config & NCC_BUSRESET) { /* Reset SCSI bus */
+ hcsp->HCS_Flags |= HCF_SCSI_RESET;
+ }
+ ORC_WR(hcsp->HCS_Base + ORC_GIMSK, 0xFB); /* enable RP FIFO interrupt */
+ return (0);
+}
+
+/*****************************************************************************
+ Function name : orc_reset_scsi_bus
+ Description : Reset registers, reset a hanging bus and
+ kill active and disconnected commands for target w/o soft reset
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int orc_reset_scsi_bus(ORC_HCS * pHCB)
+{ /* I need Host Control Block Information */
+ ULONG flags;
+
+ spin_lock_irqsave(&(pHCB->BitAllocFlagLock), flags);
+
+ initAFlag(pHCB);
+ /* reset scsi bus */
+ ORC_WR(pHCB->HCS_Base + ORC_HCTRL, SCSIRST);
+ if (waitSCSIRSTdone(pHCB) == 0) {
+ spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+ return FAILED;
+ } else {
+ spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+ return SUCCESS;
+ }
+}
+
+/*****************************************************************************
+ Function name : orc_device_reset
+ Description : Reset registers, reset a hanging bus and
+ kill active and disconnected commands for target w/o soft reset
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int orc_device_reset(ORC_HCS * pHCB, struct scsi_cmnd *SCpnt, unsigned int target)
+{ /* I need Host Control Block Information */
+ ORC_SCB *pScb;
+ ESCB *pVirEscb;
+ ORC_SCB *pVirScb;
+ UCHAR i;
+ ULONG flags;
+
+ spin_lock_irqsave(&(pHCB->BitAllocFlagLock), flags);
+ pScb = (ORC_SCB *) NULL;
+ pVirEscb = (ESCB *) NULL;
+
+ /* setup scatter list address with one buffer */
+ pVirScb = pHCB->HCS_virScbArray;
+
+ initAFlag(pHCB);
+ /* device reset */
+ for (i = 0; i < ORC_MAXQUEUE; i++) {
+ pVirEscb = pVirScb->SCB_EScb;
+ if ((pVirScb->SCB_Status) && (pVirEscb->SCB_Srb == SCpnt))
+ break;
+ pVirScb++;
+ }
+
+ if (i == ORC_MAXQUEUE) {
+ printk("Unable to Reset - No SCB Found\n");
+ spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+ return FAILED;
+ }
+ if ((pScb = orc_alloc_scb(pHCB)) == NULL) {
+ spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+ return FAILED;
+ }
+ pScb->SCB_Opcode = ORC_BUSDEVRST;
+ pScb->SCB_Target = target;
+ pScb->SCB_HaStat = 0;
+ pScb->SCB_TaStat = 0;
+ pScb->SCB_Status = 0x0;
+ pScb->SCB_Link = 0xFF;
+ pScb->SCB_Reserved0 = 0;
+ pScb->SCB_Reserved1 = 0;
+ pScb->SCB_XferLen = 0;
+ pScb->SCB_SGLen = 0;
+
+ pVirEscb->SCB_Srb = NULL;
+ pVirEscb->SCB_Srb = SCpnt;
+ orc_exec_scb(pHCB, pScb); /* Start execute SCB */
+ spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+ return SUCCESS;
+}
+
+
+/***************************************************************************/
+static ORC_SCB *__orc_alloc_scb(ORC_HCS * hcsp)
+{
+ ORC_SCB *pTmpScb;
+ UCHAR Ch;
+ ULONG idx;
+ UCHAR index;
+ UCHAR i;
+
+ Ch = hcsp->HCS_Index;
+ for (i = 0; i < 8; i++) {
+ for (index = 0; index < 32; index++) {
+ if ((hcsp->BitAllocFlag[Ch][i] >> index) & 0x01) {
+ hcsp->BitAllocFlag[Ch][i] &= ~(1 << index);
+ break;
+ }
+ }
+ idx = index + 32 * i;
+ pTmpScb = (ORC_SCB *) ((ULONG) hcsp->HCS_virScbArray + (idx * sizeof(ORC_SCB)));
+ return (pTmpScb);
+ }
+ return (NULL);
+}
+
+static ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp)
+{
+ ORC_SCB *pTmpScb;
+ ULONG flags;
+
+ spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+ pTmpScb = __orc_alloc_scb(hcsp);
+ spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+ return (pTmpScb);
+}
+
+
+/***************************************************************************/
+static void orc_release_scb(ORC_HCS * hcsp, ORC_SCB * scbp)
+{
+ ULONG flags;
+ UCHAR Index;
+ UCHAR i;
+ UCHAR Ch;
+
+ spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+ Ch = hcsp->HCS_Index;
+ Index = scbp->SCB_ScbIdx;
+ i = Index / 32;
+ Index %= 32;
+ hcsp->BitAllocFlag[Ch][i] |= (1 << Index);
+ spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+}
+
+/*****************************************************************************
+ Function name : abort_SCB
+ Description : Abort a queued command.
+ (commands that are on the bus can't be aborted easily)
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int abort_SCB(ORC_HCS * hcsp, ORC_SCB * pScb)
+{
+ unsigned char bData, bStatus;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_ABORT_SCB); /* Write command */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ ORC_WR(hcsp->HCS_Base + ORC_HDATA, pScb->SCB_ScbIdx); /* Write address */
+ ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+ if (waitHDOoff(hcsp) == 0) /* Wait HDO off */
+ return 0;
+
+ if (waitHDIset(hcsp, &bData) == 0) /* Wait HDI set */
+ return 0;
+ bStatus = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+ ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData); /* Clear HDI */
+
+ if (bStatus == 1) /* 0 - Successfully */
+ return 0; /* 1 - Fail */
+ return 1;
+}
+
+/*****************************************************************************
+ Function name : inia100_abort
+ Description : Abort a queued command.
+ (commands that are on the bus can't be aborted easily)
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int orc_abort_srb(ORC_HCS * hcsp, struct scsi_cmnd *SCpnt)
+{
+ ESCB *pVirEscb;
+ ORC_SCB *pVirScb;
+ UCHAR i;
+ ULONG flags;
+
+ spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+
+ pVirScb = hcsp->HCS_virScbArray;
+
+ for (i = 0; i < ORC_MAXQUEUE; i++, pVirScb++) {
+ pVirEscb = pVirScb->SCB_EScb;
+ if ((pVirScb->SCB_Status) && (pVirEscb->SCB_Srb == SCpnt)) {
+ if (pVirScb->SCB_TagMsg == 0) {
+ spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+ return FAILED;
+ } else {
+ if (abort_SCB(hcsp, pVirScb)) {
+ pVirEscb->SCB_Srb = NULL;
+ spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+ return SUCCESS;
+ } else {
+ spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+ return FAILED;
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+ return FAILED;
+}
+
+/***********************************************************************
+ Routine Description:
+ This is the interrupt service routine for the Orchid SCSI adapter.
+ It reads the interrupt register to determine if the adapter is indeed
+ the source of the interrupt and clears the interrupt at the device.
+ Arguments:
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ Return Value:
+***********************************************************************/
+static void orc_interrupt(
+ ORC_HCS * hcsp
+)
+{
+ BYTE bScbIdx;
+ ORC_SCB *pScb;
+
+ if (ORC_RD(hcsp->HCS_Base, ORC_RQUEUECNT) == 0) {
+ return; // 0;
+
+ }
+ do {
+ bScbIdx = ORC_RD(hcsp->HCS_Base, ORC_RQUEUE);
+
+ pScb = (ORC_SCB *) ((ULONG) hcsp->HCS_virScbArray + (ULONG) (sizeof(ORC_SCB) * bScbIdx));
+ pScb->SCB_Status = 0x0;
+
+ inia100SCBPost((BYTE *) hcsp, (BYTE *) pScb);
+ } while (ORC_RD(hcsp->HCS_Base, ORC_RQUEUECNT));
+ return; //1;
+
+} /* End of I1060Interrupt() */
+
+/*****************************************************************************
+ Function name : inia100BuildSCB
+ Description :
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static void inia100BuildSCB(ORC_HCS * pHCB, ORC_SCB * pSCB, struct scsi_cmnd * SCpnt)
+{ /* Create corresponding SCB */
+ struct scatterlist *pSrbSG;
+ ORC_SG *pSG; /* Pointer to SG list */
+ int i, count_sg;
+ ESCB *pEScb;
+
+ pEScb = pSCB->SCB_EScb;
+ pEScb->SCB_Srb = SCpnt;
+ pSG = NULL;
+
+ pSCB->SCB_Opcode = ORC_EXECSCSI;
+ pSCB->SCB_Flags = SCF_NO_DCHK; /* Clear done bit */
+ pSCB->SCB_Target = SCpnt->device->id;
+ pSCB->SCB_Lun = SCpnt->device->lun;
+ pSCB->SCB_Reserved0 = 0;
+ pSCB->SCB_Reserved1 = 0;
+ pSCB->SCB_SGLen = 0;
+
+ if ((pSCB->SCB_XferLen = (U32) SCpnt->request_bufflen)) {
+ pSG = (ORC_SG *) & pEScb->ESCB_SGList[0];
+ if (SCpnt->use_sg) {
+ pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+ count_sg = pci_map_sg(pHCB->pdev, pSrbSG, SCpnt->use_sg,
+ SCpnt->sc_data_direction);
+ pSCB->SCB_SGLen = (U32) (count_sg * 8);
+ for (i = 0; i < count_sg; i++, pSG++, pSrbSG++) {
+ pSG->SG_Ptr = (U32) sg_dma_address(pSrbSG);
+ pSG->SG_Len = (U32) sg_dma_len(pSrbSG);
+ }
+ } else if (SCpnt->request_bufflen != 0) {/* Non SG */
+ pSCB->SCB_SGLen = 0x8;
+ SCpnt->SCp.dma_handle = pci_map_single(pHCB->pdev,
+ SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ SCpnt->sc_data_direction);
+ pSG->SG_Ptr = (U32) SCpnt->SCp.dma_handle;
+ pSG->SG_Len = (U32) SCpnt->request_bufflen;
+ } else {
+ pSCB->SCB_SGLen = 0;
+ pSG->SG_Ptr = 0;
+ pSG->SG_Len = 0;
+ }
+ }
+ pSCB->SCB_SGPAddr = (U32) pSCB->SCB_SensePAddr;
+ pSCB->SCB_HaStat = 0;
+ pSCB->SCB_TaStat = 0;
+ pSCB->SCB_Link = 0xFF;
+ pSCB->SCB_SenseLen = SENSE_SIZE;
+ pSCB->SCB_CDBLen = SCpnt->cmd_len;
+ if (pSCB->SCB_CDBLen >= IMAX_CDB) {
+ printk("max cdb length= %x\b", SCpnt->cmd_len);
+ pSCB->SCB_CDBLen = IMAX_CDB;
+ }
+ pSCB->SCB_Ident = SCpnt->device->lun | DISC_ALLOW;
+ if (SCpnt->device->tagged_supported) { /* Tag Support */
+ pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG; /* Do simple tag only */
+ } else {
+ pSCB->SCB_TagMsg = 0; /* No tag support */
+ }
+ memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, pSCB->SCB_CDBLen);
+ return;
+}
+
+/*****************************************************************************
+ Function name : inia100_queue
+ Description : Queue a command and setup interrupts for a free bus.
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_queue(struct scsi_cmnd * SCpnt, void (*done) (struct scsi_cmnd *))
+{
+ register ORC_SCB *pSCB;
+ ORC_HCS *pHCB; /* Point to Host adapter control block */
+
+ pHCB = (ORC_HCS *) SCpnt->device->host->hostdata;
+ SCpnt->scsi_done = done;
+ /* Get free SCSI control block */
+ if ((pSCB = orc_alloc_scb(pHCB)) == NULL)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ inia100BuildSCB(pHCB, pSCB, SCpnt);
+ orc_exec_scb(pHCB, pSCB); /* Start execute SCB */
+
+ return (0);
+}
+
+/*****************************************************************************
+ Function name : inia100_abort
+ Description : Abort a queued command.
+ (commands that are on the bus can't be aborted easily)
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_abort(struct scsi_cmnd * SCpnt)
+{
+ ORC_HCS *hcsp;
+
+ hcsp = (ORC_HCS *) SCpnt->device->host->hostdata;
+ return orc_abort_srb(hcsp, SCpnt);
+}
+
+/*****************************************************************************
+ Function name : inia100_reset
+ Description : Reset registers, reset a hanging bus and
+ kill active and disconnected commands for target w/o soft reset
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_bus_reset(struct scsi_cmnd * SCpnt)
+{ /* I need Host Control Block Information */
+ ORC_HCS *pHCB;
+ pHCB = (ORC_HCS *) SCpnt->device->host->hostdata;
+ return orc_reset_scsi_bus(pHCB);
+}
+
+/*****************************************************************************
+ Function name : inia100_device_reset
+ Description : Reset the device
+ Input : pHCB - Pointer to host adapter structure
+ Output : None.
+ Return : pSRB - Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_device_reset(struct scsi_cmnd * SCpnt)
+{ /* I need Host Control Block Information */
+ ORC_HCS *pHCB;
+ pHCB = (ORC_HCS *) SCpnt->device->host->hostdata;
+ return orc_device_reset(pHCB, SCpnt, SCpnt->device->id);
+
+}
+
+/*****************************************************************************
+ Function name : inia100SCBPost
+ Description : This is callback routine be called when orc finish one
+ SCSI command.
+ Input : pHCB - Pointer to host adapter control block.
+ pSCB - Pointer to SCSI control block.
+ Output : None.
+ Return : None.
+*****************************************************************************/
+static void inia100SCBPost(BYTE * pHcb, BYTE * pScb)
+{
+ struct scsi_cmnd *pSRB; /* Pointer to SCSI request block */
+ ORC_HCS *pHCB;
+ ORC_SCB *pSCB;
+ ESCB *pEScb;
+
+ pHCB = (ORC_HCS *) pHcb;
+ pSCB = (ORC_SCB *) pScb;
+ pEScb = pSCB->SCB_EScb;
+ if ((pSRB = (struct scsi_cmnd *) pEScb->SCB_Srb) == 0) {
+ printk("inia100SCBPost: SRB pointer is empty\n");
+ orc_release_scb(pHCB, pSCB); /* Release SCB for current channel */
+ return;
+ }
+ pEScb->SCB_Srb = NULL;
+
+ switch (pSCB->SCB_HaStat) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error interrupt generated */
+ pSCB->SCB_HaStat = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ pSCB->SCB_HaStat = DID_TIME_OUT;
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ pSCB->SCB_HaStat = DID_RESET;
+ break;
+
+ case 0x1a: /* SCB Aborted. 07/21/98 */
+ pSCB->SCB_HaStat = DID_ABORT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ than was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+ case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid. */
+
+ default:
+ printk("inia100: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+ pSCB->SCB_HaStat = DID_ERROR; /* Couldn't find any better */
+ break;
+ }
+
+ if (pSCB->SCB_TaStat == 2) { /* Check condition */
+ memcpy((unsigned char *) &pSRB->sense_buffer[0],
+ (unsigned char *) &pEScb->ESCB_SGList[0], SENSE_SIZE);
+ }
+ pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+
+ if (pSRB->use_sg) {
+ pci_unmap_sg(pHCB->pdev,
+ (struct scatterlist *)pSRB->request_buffer,
+ pSRB->use_sg, pSRB->sc_data_direction);
+ } else if (pSRB->request_bufflen != 0) {
+ pci_unmap_single(pHCB->pdev, pSRB->SCp.dma_handle,
+ pSRB->request_bufflen,
+ pSRB->sc_data_direction);
+ }
+
+ pSRB->scsi_done(pSRB); /* Notify system DONE */
+
+ orc_release_scb(pHCB, pSCB); /* Release SCB for current channel */
+}
+
+/*
+ * Interrupt handler (main routine of the driver)
+ */
+static irqreturn_t inia100_intr(int irqno, void *devid, struct pt_regs *regs)
+{
+ struct Scsi_Host *host = (struct Scsi_Host *)devid;
+ ORC_HCS *pHcb = (ORC_HCS *)host->hostdata;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ orc_interrupt(pHcb);
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static struct scsi_host_template inia100_template = {
+ .proc_name = "inia100",
+ .name = inia100_REVID,
+ .queuecommand = inia100_queue,
+ .eh_abort_handler = inia100_abort,
+ .eh_bus_reset_handler = inia100_bus_reset,
+ .eh_device_reset_handler = inia100_device_reset,
+ .can_queue = 1,
+ .this_id = 1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+static int __devinit inia100_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct Scsi_Host *shost;
+ ORC_HCS *pHCB;
+ unsigned long port, bios;
+ int error = -ENODEV;
+ u32 sz;
+ unsigned long dBiosAdr;
+ char *pbBiosAdr;
+
+ if (pci_enable_device(pdev))
+ goto out;
+ if (pci_set_dma_mask(pdev, 0xffffffffULL)) {
+ printk(KERN_WARNING "Unable to set 32bit DMA "
+ "on inia100 adapter, ignoring.\n");
+ goto out_disable_device;
+ }
+
+ pci_set_master(pdev);
+
+ port = pci_resource_start(pdev, 0);
+ if (!request_region(port, 256, "inia100")) {
+ printk(KERN_WARNING "inia100: io port 0x%lx, is busy.\n", port);
+ goto out_disable_device;
+ }
+
+ /* <02> read from base address + 0x50 offset to get the bios balue. */
+ bios = ORC_RDWORD(port, 0x50);
+
+
+ shost = scsi_host_alloc(&inia100_template, sizeof(ORC_HCS));
+ if (!shost)
+ goto out_release_region;
+
+ pHCB = (ORC_HCS *)shost->hostdata;
+ pHCB->pdev = pdev;
+ pHCB->HCS_Base = port;
+ pHCB->HCS_BIOS = bios;
+ spin_lock_init(&pHCB->BitAllocFlagLock);
+
+ /* Get total memory needed for SCB */
+ sz = ORC_MAXQUEUE * sizeof(ORC_SCB);
+ pHCB->HCS_virScbArray = pci_alloc_consistent(pdev, sz,
+ &pHCB->HCS_physScbArray);
+ if (!pHCB->HCS_virScbArray) {
+ printk("inia100: SCB memory allocation error\n");
+ goto out_host_put;
+ }
+ memset(pHCB->HCS_virScbArray, 0, sz);
+
+ /* Get total memory needed for ESCB */
+ sz = ORC_MAXQUEUE * sizeof(ESCB);
+ pHCB->HCS_virEscbArray = pci_alloc_consistent(pdev, sz,
+ &pHCB->HCS_physEscbArray);
+ if (!pHCB->HCS_virEscbArray) {
+ printk("inia100: ESCB memory allocation error\n");
+ goto out_free_scb_array;
+ }
+ memset(pHCB->HCS_virEscbArray, 0, sz);
+
+ dBiosAdr = pHCB->HCS_BIOS;
+ dBiosAdr = (dBiosAdr << 4);
+ pbBiosAdr = phys_to_virt(dBiosAdr);
+ if (init_orchid(pHCB)) { /* Initialize orchid chip */
+ printk("inia100: initial orchid fail!!\n");
+ goto out_free_escb_array;
+ }
+
+ shost->io_port = pHCB->HCS_Base;
+ shost->n_io_port = 0xff;
+ shost->can_queue = ORC_MAXQUEUE;
+ shost->unique_id = shost->io_port;
+ shost->max_id = pHCB->HCS_MaxTar;
+ shost->max_lun = 16;
+ shost->irq = pHCB->HCS_Intr = pdev->irq;
+ shost->this_id = pHCB->HCS_SCSI_ID; /* Assign HCS index */
+ shost->sg_tablesize = TOTAL_SG_ENTRY;
+
+ /* Initial orc chip */
+ error = request_irq(pdev->irq, inia100_intr, SA_SHIRQ,
+ "inia100", shost);
+ if (error < 0) {
+ printk(KERN_WARNING "inia100: unable to get irq %d\n",
+ pdev->irq);
+ goto out_free_escb_array;
+ }
+
+ pci_set_drvdata(pdev, shost);
+
+ error = scsi_add_host(shost, &pdev->dev);
+ if (error)
+ goto out_free_irq;
+
+ scsi_scan_host(shost);
+ return 0;
+
+ out_free_irq:
+ free_irq(shost->irq, shost);
+ out_free_escb_array:
+ pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ESCB),
+ pHCB->HCS_virEscbArray, pHCB->HCS_physEscbArray);
+ out_free_scb_array:
+ pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ORC_SCB),
+ pHCB->HCS_virScbArray, pHCB->HCS_physScbArray);
+ out_host_put:
+ scsi_host_put(shost);
+ out_release_region:
+ release_region(port, 256);
+ out_disable_device:
+ pci_disable_device(pdev);
+ out:
+ return error;
+}
+
+static void __devexit inia100_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ ORC_HCS *pHCB = (ORC_HCS *)shost->hostdata;
+
+ scsi_remove_host(shost);
+
+ free_irq(shost->irq, shost);
+ pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ESCB),
+ pHCB->HCS_virEscbArray, pHCB->HCS_physEscbArray);
+ pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ORC_SCB),
+ pHCB->HCS_virScbArray, pHCB->HCS_physScbArray);
+ release_region(shost->io_port, 256);
+
+ scsi_host_put(shost);
+}
+
+static struct pci_device_id inia100_pci_tbl[] = {
+ {PCI_VENDOR_ID_INIT, 0x1060, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, inia100_pci_tbl);
+
+static struct pci_driver inia100_pci_driver = {
+ .name = "inia100",
+ .id_table = inia100_pci_tbl,
+ .probe = inia100_probe_one,
+ .remove = __devexit_p(inia100_remove_one),
+};
+
+static int __init inia100_init(void)
+{
+ return pci_module_init(&inia100_pci_driver);
+}
+
+static void __exit inia100_exit(void)
+{
+ pci_unregister_driver(&inia100_pci_driver);
+}
+
+MODULE_DESCRIPTION("Initio A100U2W SCSI driver");
+MODULE_AUTHOR("Initio Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
+
+module_init(inia100_init);
+module_exit(inia100_exit);
diff --git a/drivers/scsi/a100u2w.h b/drivers/scsi/a100u2w.h
new file mode 100644
index 000000000000..6f542d2600ea
--- /dev/null
+++ b/drivers/scsi/a100u2w.h
@@ -0,0 +1,416 @@
+/*
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Revision History:
+ * 06/18/98 HL, Initial production Version 1.02
+ * 12/19/98 bv, Use spinlocks for 2.1.95 and up
+ * 06/25/02 Doug Ledford <dledford@redhat.com>
+ * - This and the i60uscsi.h file are almost identical,
+ * merged them into a single header used by both .c files.
+ */
+
+#define inia100_REVID "Initio INI-A100U2W SCSI device driver; Revision: 1.02d"
+
+#define ULONG unsigned long
+#define USHORT unsigned short
+#define UCHAR unsigned char
+#define BYTE unsigned char
+#define WORD unsigned short
+#define DWORD unsigned long
+#define UBYTE unsigned char
+#define UWORD unsigned short
+#define UDWORD unsigned long
+#define U32 u32
+
+#if 1
+#define ORC_MAXQUEUE 245
+#define ORC_MAXTAGS 64
+#else
+#define ORC_MAXQUEUE 25
+#define ORC_MAXTAGS 8
+#endif
+
+#define TOTAL_SG_ENTRY 32
+#define MAX_TARGETS 16
+#define IMAX_CDB 15
+#define SENSE_SIZE 14
+
+/************************************************************************/
+/* Scatter-Gather Element Structure */
+/************************************************************************/
+typedef struct ORC_SG_Struc {
+ U32 SG_Ptr; /* Data Pointer */
+ U32 SG_Len; /* Data Length */
+} ORC_SG;
+
+/* SCSI related definition */
+#define DISC_NOT_ALLOW 0x80 /* Disconnect is not allowed */
+#define DISC_ALLOW 0xC0 /* Disconnect is allowed */
+
+
+#define ORC_OFFSET_SCB 16
+#define ORC_MAX_SCBS 250
+#define MAX_CHANNELS 2
+#define MAX_ESCB_ELE 64
+#define TCF_DRV_255_63 0x0400
+
+/********************************************************/
+/* Orchid Host Command Set */
+/********************************************************/
+#define ORC_CMD_NOP 0x00 /* Host command - NOP */
+#define ORC_CMD_VERSION 0x01 /* Host command - Get F/W version */
+#define ORC_CMD_ECHO 0x02 /* Host command - ECHO */
+#define ORC_CMD_SET_NVM 0x03 /* Host command - Set NVRAM */
+#define ORC_CMD_GET_NVM 0x04 /* Host command - Get NVRAM */
+#define ORC_CMD_GET_BUS_STATUS 0x05 /* Host command - Get SCSI bus status */
+#define ORC_CMD_ABORT_SCB 0x06 /* Host command - Abort SCB */
+#define ORC_CMD_ISSUE_SCB 0x07 /* Host command - Issue SCB */
+
+/********************************************************/
+/* Orchid Register Set */
+/********************************************************/
+#define ORC_GINTS 0xA0 /* Global Interrupt Status */
+#define QINT 0x04 /* Reply Queue Interrupt */
+#define ORC_GIMSK 0xA1 /* Global Interrupt MASK */
+#define MQINT 0x04 /* Mask Reply Queue Interrupt */
+#define ORC_GCFG 0xA2 /* Global Configure */
+#define EEPRG 0x01 /* Enable EEPROM programming */
+#define ORC_GSTAT 0xA3 /* Global status */
+#define WIDEBUS 0x10 /* Wide SCSI Devices connected */
+#define ORC_HDATA 0xA4 /* Host Data */
+#define ORC_HCTRL 0xA5 /* Host Control */
+#define SCSIRST 0x80 /* SCSI bus reset */
+#define HDO 0x40 /* Host data out */
+#define HOSTSTOP 0x02 /* Host stop RISC engine */
+#define DEVRST 0x01 /* Device reset */
+#define ORC_HSTUS 0xA6 /* Host Status */
+#define HDI 0x02 /* Host data in */
+#define RREADY 0x01 /* RISC engine is ready to receive */
+#define ORC_NVRAM 0xA7 /* Nvram port address */
+#define SE2CS 0x008
+#define SE2CLK 0x004
+#define SE2DO 0x002
+#define SE2DI 0x001
+#define ORC_PQUEUE 0xA8 /* Posting queue FIFO */
+#define ORC_PQCNT 0xA9 /* Posting queue FIFO Cnt */
+#define ORC_RQUEUE 0xAA /* Reply queue FIFO */
+#define ORC_RQUEUECNT 0xAB /* Reply queue FIFO Cnt */
+#define ORC_FWBASEADR 0xAC /* Firmware base address */
+
+#define ORC_EBIOSADR0 0xB0 /* External Bios address */
+#define ORC_EBIOSADR1 0xB1 /* External Bios address */
+#define ORC_EBIOSADR2 0xB2 /* External Bios address */
+#define ORC_EBIOSDATA 0xB3 /* External Bios address */
+
+#define ORC_SCBSIZE 0xB7 /* SCB size register */
+#define ORC_SCBBASE0 0xB8 /* SCB base address 0 */
+#define ORC_SCBBASE1 0xBC /* SCB base address 1 */
+
+#define ORC_RISCCTL 0xE0 /* RISC Control */
+#define PRGMRST 0x002
+#define DOWNLOAD 0x001
+#define ORC_PRGMCTR0 0xE2 /* RISC program counter */
+#define ORC_PRGMCTR1 0xE3 /* RISC program counter */
+#define ORC_RISCRAM 0xEC /* RISC RAM data port 4 bytes */
+
+typedef struct orc_extended_scb { /* Extended SCB */
+ ORC_SG ESCB_SGList[TOTAL_SG_ENTRY]; /*0 Start of SG list */
+ struct scsi_cmnd *SCB_Srb; /*50 SRB Pointer */
+} ESCB;
+
+/***********************************************************************
+ SCSI Control Block
+************************************************************************/
+typedef struct orc_scb { /* Scsi_Ctrl_Blk */
+ UBYTE SCB_Opcode; /*00 SCB command code&residual */
+ UBYTE SCB_Flags; /*01 SCB Flags */
+ UBYTE SCB_Target; /*02 Target Id */
+ UBYTE SCB_Lun; /*03 Lun */
+ U32 SCB_Reserved0; /*04 Reserved for ORCHID must 0 */
+ U32 SCB_XferLen; /*08 Data Transfer Length */
+ U32 SCB_Reserved1; /*0C Reserved for ORCHID must 0 */
+ U32 SCB_SGLen; /*10 SG list # * 8 */
+ U32 SCB_SGPAddr; /*14 SG List Buf physical Addr */
+ U32 SCB_SGPAddrHigh; /*18 SG Buffer high physical Addr */
+ UBYTE SCB_HaStat; /*1C Host Status */
+ UBYTE SCB_TaStat; /*1D Target Status */
+ UBYTE SCB_Status; /*1E SCB status */
+ UBYTE SCB_Link; /*1F Link pointer, default 0xFF */
+ UBYTE SCB_SenseLen; /*20 Sense Allocation Length */
+ UBYTE SCB_CDBLen; /*21 CDB Length */
+ UBYTE SCB_Ident; /*22 Identify */
+ UBYTE SCB_TagMsg; /*23 Tag Message */
+ UBYTE SCB_CDB[IMAX_CDB]; /*24 SCSI CDBs */
+ UBYTE SCB_ScbIdx; /*3C Index for this ORCSCB */
+ U32 SCB_SensePAddr; /*34 Sense Buffer physical Addr */
+
+ ESCB *SCB_EScb; /*38 Extended SCB Pointer */
+#ifndef ALPHA
+ UBYTE SCB_Reserved2[4]; /*3E Reserved for Driver use */
+#endif
+} ORC_SCB;
+
+/* Opcodes of ORCSCB_Opcode */
+#define ORC_EXECSCSI 0x00 /* SCSI initiator command with residual */
+#define ORC_BUSDEVRST 0x01 /* SCSI Bus Device Reset */
+
+/* Status of ORCSCB_Status */
+#define ORCSCB_COMPLETE 0x00 /* SCB request completed */
+#define ORCSCB_POST 0x01 /* SCB is posted by the HOST */
+
+/* Bit Definition for ORCSCB_Flags */
+#define SCF_DISINT 0x01 /* Disable HOST interrupt */
+#define SCF_DIR 0x18 /* Direction bits */
+#define SCF_NO_DCHK 0x00 /* Direction determined by SCSI */
+#define SCF_DIN 0x08 /* From Target to Initiator */
+#define SCF_DOUT 0x10 /* From Initiator to Target */
+#define SCF_NO_XF 0x18 /* No data transfer */
+#define SCF_POLL 0x40
+
+/* Error Codes for ORCSCB_HaStat */
+#define HOST_SEL_TOUT 0x11
+#define HOST_DO_DU 0x12
+#define HOST_BUS_FREE 0x13
+#define HOST_BAD_PHAS 0x14
+#define HOST_INV_CMD 0x16
+#define HOST_SCSI_RST 0x1B
+#define HOST_DEV_RST 0x1C
+
+
+/* Error Codes for ORCSCB_TaStat */
+#define TARGET_CHK_COND 0x02
+#define TARGET_BUSY 0x08
+#define TARGET_TAG_FULL 0x28
+
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+
+typedef struct ORC_Tar_Ctrl_Struc {
+ UBYTE TCS_DrvDASD; /* 6 */
+ UBYTE TCS_DrvSCSI; /* 7 */
+ UBYTE TCS_DrvHead; /* 8 */
+ UWORD TCS_DrvFlags; /* 4 */
+ UBYTE TCS_DrvSector; /* 7 */
+} ORC_TCS;
+
+/* Bit Definition for TCF_DrvFlags */
+#define TCS_DF_NODASD_SUPT 0x20 /* Suppress OS/2 DASD Mgr support */
+#define TCS_DF_NOSCSI_SUPT 0x40 /* Suppress OS/2 SCSI Mgr support */
+
+
+/***********************************************************************
+ Host Adapter Control Structure
+************************************************************************/
+typedef struct ORC_Ha_Ctrl_Struc {
+ USHORT HCS_Base; /* 00 */
+ UBYTE HCS_Index; /* 02 */
+ UBYTE HCS_Intr; /* 04 */
+ UBYTE HCS_SCSI_ID; /* 06 H/A SCSI ID */
+ UBYTE HCS_BIOS; /* 07 BIOS configuration */
+
+ UBYTE HCS_Flags; /* 0B */
+ UBYTE HCS_HAConfig1; /* 1B SCSI0MAXTags */
+ UBYTE HCS_MaxTar; /* 1B SCSI0MAXTags */
+
+ USHORT HCS_Units; /* Number of units this adapter */
+ USHORT HCS_AFlags; /* Adapter info. defined flags */
+ ULONG HCS_Timeout; /* Adapter timeout value */
+ ORC_SCB *HCS_virScbArray; /* 28 Virtual Pointer to SCB array */
+ dma_addr_t HCS_physScbArray; /* Scb Physical address */
+ ESCB *HCS_virEscbArray; /* Virtual pointer to ESCB Scatter list */
+ dma_addr_t HCS_physEscbArray; /* scatter list Physical address */
+ UBYTE TargetFlag[16]; /* 30 target configuration, TCF_EN_TAG */
+ UBYTE MaximumTags[16]; /* 40 ORC_MAX_SCBS */
+ UBYTE ActiveTags[16][16]; /* 50 */
+ ORC_TCS HCS_Tcs[16]; /* 28 */
+ U32 BitAllocFlag[MAX_CHANNELS][8]; /* Max STB is 256, So 256/32 */
+ spinlock_t BitAllocFlagLock;
+ struct pci_dev *pdev;
+} ORC_HCS;
+
+/* Bit Definition for HCS_Flags */
+
+#define HCF_SCSI_RESET 0x01 /* SCSI BUS RESET */
+#define HCF_PARITY 0x02 /* parity card */
+#define HCF_LVDS 0x10 /* parity card */
+
+/* Bit Definition for TargetFlag */
+
+#define TCF_EN_255 0x08
+#define TCF_EN_TAG 0x10
+#define TCF_BUSY 0x20
+#define TCF_DISCONNECT 0x40
+#define TCF_SPIN_UP 0x80
+
+/* Bit Definition for HCS_AFlags */
+#define HCS_AF_IGNORE 0x01 /* Adapter ignore */
+#define HCS_AF_DISABLE_RESET 0x10 /* Adapter disable reset */
+#define HCS_AF_DISABLE_ADPT 0x80 /* Adapter disable */
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+ UCHAR SubVendorID0; /* 00 - Sub Vendor ID */
+ UCHAR SubVendorID1; /* 00 - Sub Vendor ID */
+ UCHAR SubSysID0; /* 02 - Sub System ID */
+ UCHAR SubSysID1; /* 02 - Sub System ID */
+ UCHAR SubClass; /* 04 - Sub Class */
+ UCHAR VendorID0; /* 05 - Vendor ID */
+ UCHAR VendorID1; /* 05 - Vendor ID */
+ UCHAR DeviceID0; /* 07 - Device ID */
+ UCHAR DeviceID1; /* 07 - Device ID */
+ UCHAR Reserved0[2]; /* 09 - Reserved */
+ UCHAR Revision; /* 0B - Revision of data structure */
+ /* ----Host Adapter Structure ---- */
+ UCHAR NumOfCh; /* 0C - Number of SCSI channel */
+ UCHAR BIOSConfig1; /* 0D - BIOS configuration 1 */
+ UCHAR BIOSConfig2; /* 0E - BIOS boot channel&target ID */
+ UCHAR BIOSConfig3; /* 0F - BIOS configuration 3 */
+ /* ----SCSI channel Structure ---- */
+ /* from "CTRL-I SCSI Host Adapter SetUp menu " */
+ UCHAR SCSI0Id; /* 10 - Channel 0 SCSI ID */
+ UCHAR SCSI0Config; /* 11 - Channel 0 SCSI configuration */
+ UCHAR SCSI0MaxTags; /* 12 - Channel 0 Maximum tags */
+ UCHAR SCSI0ResetTime; /* 13 - Channel 0 Reset recovering time */
+ UCHAR ReservedforChannel0[2]; /* 14 - Reserved */
+
+ /* ----SCSI target Structure ---- */
+ /* from "CTRL-I SCSI device SetUp menu " */
+ UCHAR Target00Config; /* 16 - Channel 0 Target 0 config */
+ UCHAR Target01Config; /* 17 - Channel 0 Target 1 config */
+ UCHAR Target02Config; /* 18 - Channel 0 Target 2 config */
+ UCHAR Target03Config; /* 19 - Channel 0 Target 3 config */
+ UCHAR Target04Config; /* 1A - Channel 0 Target 4 config */
+ UCHAR Target05Config; /* 1B - Channel 0 Target 5 config */
+ UCHAR Target06Config; /* 1C - Channel 0 Target 6 config */
+ UCHAR Target07Config; /* 1D - Channel 0 Target 7 config */
+ UCHAR Target08Config; /* 1E - Channel 0 Target 8 config */
+ UCHAR Target09Config; /* 1F - Channel 0 Target 9 config */
+ UCHAR Target0AConfig; /* 20 - Channel 0 Target A config */
+ UCHAR Target0BConfig; /* 21 - Channel 0 Target B config */
+ UCHAR Target0CConfig; /* 22 - Channel 0 Target C config */
+ UCHAR Target0DConfig; /* 23 - Channel 0 Target D config */
+ UCHAR Target0EConfig; /* 24 - Channel 0 Target E config */
+ UCHAR Target0FConfig; /* 25 - Channel 0 Target F config */
+
+ UCHAR SCSI1Id; /* 26 - Channel 1 SCSI ID */
+ UCHAR SCSI1Config; /* 27 - Channel 1 SCSI configuration */
+ UCHAR SCSI1MaxTags; /* 28 - Channel 1 Maximum tags */
+ UCHAR SCSI1ResetTime; /* 29 - Channel 1 Reset recovering time */
+ UCHAR ReservedforChannel1[2]; /* 2A - Reserved */
+
+ /* ----SCSI target Structure ---- */
+ /* from "CTRL-I SCSI device SetUp menu " */
+ UCHAR Target10Config; /* 2C - Channel 1 Target 0 config */
+ UCHAR Target11Config; /* 2D - Channel 1 Target 1 config */
+ UCHAR Target12Config; /* 2E - Channel 1 Target 2 config */
+ UCHAR Target13Config; /* 2F - Channel 1 Target 3 config */
+ UCHAR Target14Config; /* 30 - Channel 1 Target 4 config */
+ UCHAR Target15Config; /* 31 - Channel 1 Target 5 config */
+ UCHAR Target16Config; /* 32 - Channel 1 Target 6 config */
+ UCHAR Target17Config; /* 33 - Channel 1 Target 7 config */
+ UCHAR Target18Config; /* 34 - Channel 1 Target 8 config */
+ UCHAR Target19Config; /* 35 - Channel 1 Target 9 config */
+ UCHAR Target1AConfig; /* 36 - Channel 1 Target A config */
+ UCHAR Target1BConfig; /* 37 - Channel 1 Target B config */
+ UCHAR Target1CConfig; /* 38 - Channel 1 Target C config */
+ UCHAR Target1DConfig; /* 39 - Channel 1 Target D config */
+ UCHAR Target1EConfig; /* 3A - Channel 1 Target E config */
+ UCHAR Target1FConfig; /* 3B - Channel 1 Target F config */
+ UCHAR reserved[3]; /* 3C - Reserved */
+ /* ---------- CheckSum ---------- */
+ UCHAR CheckSum; /* 3F - Checksum of NVRam */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1 */
+#define NBC_BIOSENABLE 0x01 /* BIOS enable */
+#define NBC_CDROM 0x02 /* Support bootable CDROM */
+#define NBC_REMOVABLE 0x04 /* Support removable drive */
+
+/* Bios Configuration for nvram->BIOSConfig2 */
+#define NBB_TARGET_MASK 0x0F /* Boot SCSI target ID number */
+#define NBB_CHANL_MASK 0xF0 /* Boot SCSI channel number */
+
+/* Bit definition for nvram->SCSIConfig */
+#define NCC_BUSRESET 0x01 /* Reset SCSI bus at power up */
+#define NCC_PARITYCHK 0x02 /* SCSI parity enable */
+#define NCC_LVDS 0x10 /* Enable LVDS */
+#define NCC_ACTTERM1 0x20 /* Enable active terminator 1 */
+#define NCC_ACTTERM2 0x40 /* Enable active terminator 2 */
+#define NCC_AUTOTERM 0x80 /* Enable auto termination */
+
+/* Bit definition for nvram->TargetxConfig */
+#define NTC_PERIOD 0x07 /* Maximum Sync. Speed */
+#define NTC_1GIGA 0x08 /* 255 head / 63 sectors (64/32) */
+#define NTC_NO_SYNC 0x10 /* NO SYNC. NEGO */
+#define NTC_NO_WIDESYNC 0x20 /* NO WIDE SYNC. NEGO */
+#define NTC_DISC_ENABLE 0x40 /* Enable SCSI disconnect */
+#define NTC_SPINUP 0x80 /* Start disk drive */
+
+/* Default NVRam values */
+#define NBC_DEFAULT (NBC_ENABLE)
+#define NCC_DEFAULT (NCC_BUSRESET | NCC_AUTOTERM | NCC_PARITYCHK)
+#define NCC_MAX_TAGS 0x20 /* Maximum tags per target */
+#define NCC_RESET_TIME 0x0A /* SCSI RESET recovering time */
+#define NTC_DEFAULT (NTC_1GIGA | NTC_NO_WIDESYNC | NTC_DISC_ENABLE)
+
+#define ORC_RD(x,y) (UCHAR)(inb( (int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+#define ORC_RDWORD(x,y) (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+#define ORC_RDLONG(x,y) (long)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+#define ORC_WR( adr,data) outb( (UCHAR)(data), (int)(adr))
+#define ORC_WRSHORT(adr,data) outw( (UWORD)(data), (int)(adr))
+#define ORC_WRLONG( adr,data) outl( (ULONG)(data), (int)(adr))
diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c
new file mode 100644
index 000000000000..9928a2fbce0c
--- /dev/null
+++ b/drivers/scsi/a2091.c
@@ -0,0 +1,260 @@
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <linux/spinlock.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "wd33c93.h"
+#include "a2091.h"
+
+#include<linux/stat.h>
+
+#define DMA(ptr) ((a2091_scsiregs *)((ptr)->base))
+#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata))
+
+static irqreturn_t a2091_intr (int irq, void *_instance, struct pt_regs *fp)
+{
+ unsigned long flags;
+ unsigned int status;
+ struct Scsi_Host *instance = (struct Scsi_Host *)_instance;
+
+ status = DMA(instance)->ISTR;
+ if (!(status & (ISTR_INT_F|ISTR_INT_P)) || !(status & ISTR_INTS))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(instance->host_lock, flags);
+ wd33c93_intr(instance);
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
+{
+ unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
+ unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+ struct Scsi_Host *instance = cmd->device->host;
+
+ /* don't allow DMA if the physical address is bad */
+ if (addr & A2091_XFER_MASK ||
+ (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual)))
+ {
+ HDATA(instance)->dma_bounce_len = (cmd->SCp.this_residual + 511)
+ & ~0x1ff;
+ HDATA(instance)->dma_bounce_buffer =
+ kmalloc (HDATA(instance)->dma_bounce_len, GFP_KERNEL);
+
+ /* can't allocate memory; use PIO */
+ if (!HDATA(instance)->dma_bounce_buffer) {
+ HDATA(instance)->dma_bounce_len = 0;
+ return 1;
+ }
+
+ /* get the physical address of the bounce buffer */
+ addr = virt_to_bus(HDATA(instance)->dma_bounce_buffer);
+
+ /* the bounce buffer may not be in the first 16M of physmem */
+ if (addr & A2091_XFER_MASK) {
+ /* we could use chipmem... maybe later */
+ kfree (HDATA(instance)->dma_bounce_buffer);
+ HDATA(instance)->dma_bounce_buffer = NULL;
+ HDATA(instance)->dma_bounce_len = 0;
+ return 1;
+ }
+
+ if (!dir_in) {
+ /* copy to bounce buffer for a write */
+ if (cmd->use_sg)
+#if 0
+ panic ("scsi%ddma: incomplete s/g support",
+ instance->host_no);
+#else
+ memcpy (HDATA(instance)->dma_bounce_buffer,
+ cmd->SCp.ptr, cmd->SCp.this_residual);
+#endif
+ else
+ memcpy (HDATA(instance)->dma_bounce_buffer,
+ cmd->request_buffer, cmd->request_bufflen);
+ }
+ }
+
+ /* setup dma direction */
+ if (!dir_in)
+ cntr |= CNTR_DDIR;
+
+ /* remember direction */
+ HDATA(cmd->device->host)->dma_dir = dir_in;
+
+ DMA(cmd->device->host)->CNTR = cntr;
+
+ /* setup DMA *physical* address */
+ DMA(cmd->device->host)->ACR = addr;
+
+ if (dir_in){
+ /* invalidate any cache */
+ cache_clear (addr, cmd->SCp.this_residual);
+ }else{
+ /* push any dirty cache */
+ cache_push (addr, cmd->SCp.this_residual);
+ }
+ /* start DMA */
+ DMA(cmd->device->host)->ST_DMA = 1;
+
+ /* return success */
+ return 0;
+}
+
+static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt,
+ int status)
+{
+ /* disable SCSI interrupts */
+ unsigned short cntr = CNTR_PDMD;
+
+ if (!HDATA(instance)->dma_dir)
+ cntr |= CNTR_DDIR;
+
+ /* disable SCSI interrupts */
+ DMA(instance)->CNTR = cntr;
+
+ /* flush if we were reading */
+ if (HDATA(instance)->dma_dir) {
+ DMA(instance)->FLUSH = 1;
+ while (!(DMA(instance)->ISTR & ISTR_FE_FLG))
+ ;
+ }
+
+ /* clear a possible interrupt */
+ DMA(instance)->CINT = 1;
+
+ /* stop DMA */
+ DMA(instance)->SP_DMA = 1;
+
+ /* restore the CONTROL bits (minus the direction flag) */
+ DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN;
+
+ /* copy from a bounce buffer, if necessary */
+ if (status && HDATA(instance)->dma_bounce_buffer) {
+ if (SCpnt && SCpnt->use_sg) {
+#if 0
+ panic ("scsi%d: incomplete s/g support",
+ instance->host_no);
+#else
+ if( HDATA(instance)->dma_dir )
+ memcpy (SCpnt->SCp.ptr,
+ HDATA(instance)->dma_bounce_buffer,
+ SCpnt->SCp.this_residual);
+ kfree (HDATA(instance)->dma_bounce_buffer);
+ HDATA(instance)->dma_bounce_buffer = NULL;
+ HDATA(instance)->dma_bounce_len = 0;
+
+#endif
+ } else {
+ if (HDATA(instance)->dma_dir && SCpnt)
+ memcpy (SCpnt->request_buffer,
+ HDATA(instance)->dma_bounce_buffer,
+ SCpnt->request_bufflen);
+
+ kfree (HDATA(instance)->dma_bounce_buffer);
+ HDATA(instance)->dma_bounce_buffer = NULL;
+ HDATA(instance)->dma_bounce_len = 0;
+ }
+ }
+}
+
+int __init a2091_detect(Scsi_Host_Template *tpnt)
+{
+ static unsigned char called = 0;
+ struct Scsi_Host *instance;
+ unsigned long address;
+ struct zorro_dev *z = NULL;
+ wd33c93_regs regs;
+ int num_a2091 = 0;
+
+ if (!MACH_IS_AMIGA || called)
+ return 0;
+ called = 1;
+
+ tpnt->proc_name = "A2091";
+ tpnt->proc_info = &wd33c93_proc_info;
+
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ if (z->id != ZORRO_PROD_CBM_A590_A2091_1 &&
+ z->id != ZORRO_PROD_CBM_A590_A2091_2)
+ continue;
+ address = z->resource.start;
+ if (!request_mem_region(address, 256, "wd33c93"))
+ continue;
+
+ instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata));
+ if (instance == NULL) {
+ release_mem_region(address, 256);
+ continue;
+ }
+ instance->base = ZTWO_VADDR(address);
+ instance->irq = IRQ_AMIGA_PORTS;
+ instance->unique_id = z->slotaddr;
+ DMA(instance)->DAWR = DAWR_A2091;
+ regs.SASR = &(DMA(instance)->SASR);
+ regs.SCMD = &(DMA(instance)->SCMD);
+ wd33c93_init(instance, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
+ request_irq(IRQ_AMIGA_PORTS, a2091_intr, SA_SHIRQ, "A2091 SCSI",
+ instance);
+ DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN;
+ num_a2091++;
+ }
+
+ return num_a2091;
+}
+
+static int a2091_bus_reset(Scsi_Cmnd *cmd)
+{
+ /* FIXME perform bus-specific reset */
+ wd33c93_host_reset(cmd);
+ return SUCCESS;
+}
+
+#define HOSTS_C
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "A2901",
+ .name = "Commodore A2091/A590 SCSI",
+ .detect = a2091_detect,
+ .release = a2091_release,
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_bus_reset_handler = a2091_bus_reset,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+int a2091_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ DMA(instance)->CNTR = 0;
+ release_mem_region(ZTWO_PADDR(instance->base), 256);
+ free_irq(IRQ_AMIGA_PORTS, instance);
+ wd33c93_release();
+#endif
+ return 1;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/a2091.h b/drivers/scsi/a2091.h
new file mode 100644
index 000000000000..54993972dcc6
--- /dev/null
+++ b/drivers/scsi/a2091.h
@@ -0,0 +1,76 @@
+#ifndef A2091_H
+#define A2091_H
+
+/* $Id: a2091.h,v 1.4 1997/01/19 23:07:09 davem Exp $
+ *
+ * Header file for the Commodore A2091 Zorro II SCSI controller for Linux
+ *
+ * Written and (C) 1993, Hamish Macdonald, see a2091.c for more info
+ *
+ */
+
+#include <linux/types.h>
+
+int a2091_detect(Scsi_Host_Template *);
+int a2091_release(struct Scsi_Host *);
+const char *wd33c93_info(void);
+int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd33c93_abort(Scsi_Cmnd *);
+int wd33c93_reset(Scsi_Cmnd *, unsigned int);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+/*
+ * if the transfer address ANDed with this results in a non-zero
+ * result, then we can't use DMA.
+ */
+#define A2091_XFER_MASK (0xff000001)
+
+typedef struct {
+ unsigned char pad1[64];
+ volatile unsigned short ISTR;
+ volatile unsigned short CNTR;
+ unsigned char pad2[60];
+ volatile unsigned int WTC;
+ volatile unsigned long ACR;
+ unsigned char pad3[6];
+ volatile unsigned short DAWR;
+ unsigned char pad4;
+ volatile unsigned char SASR;
+ unsigned char pad5;
+ volatile unsigned char SCMD;
+ unsigned char pad6[76];
+ volatile unsigned short ST_DMA;
+ volatile unsigned short SP_DMA;
+ volatile unsigned short CINT;
+ unsigned char pad7[2];
+ volatile unsigned short FLUSH;
+} a2091_scsiregs;
+
+#define DAWR_A2091 (3)
+
+/* CNTR bits. */
+#define CNTR_TCEN (1<<7)
+#define CNTR_PREST (1<<6)
+#define CNTR_PDMD (1<<5)
+#define CNTR_INTEN (1<<4)
+#define CNTR_DDIR (1<<3)
+
+/* ISTR bits. */
+#define ISTR_INTX (1<<8)
+#define ISTR_INT_F (1<<7)
+#define ISTR_INTS (1<<6)
+#define ISTR_E_INT (1<<5)
+#define ISTR_INT_P (1<<4)
+#define ISTR_UE_INT (1<<3)
+#define ISTR_OE_INT (1<<2)
+#define ISTR_FF_FLG (1<<1)
+#define ISTR_FE_FLG (1<<0)
+
+#endif /* A2091_H */
diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c
new file mode 100644
index 000000000000..f8a89ec25042
--- /dev/null
+++ b/drivers/scsi/a3000.c
@@ -0,0 +1,245 @@
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "wd33c93.h"
+#include "a3000.h"
+
+#include<linux/stat.h>
+
+#define DMA(ptr) ((a3000_scsiregs *)((ptr)->base))
+#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata))
+
+static struct Scsi_Host *a3000_host = NULL;
+
+static irqreturn_t a3000_intr (int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned long flags;
+ unsigned int status = DMA(a3000_host)->ISTR;
+
+ if (!(status & ISTR_INT_P))
+ return IRQ_NONE;
+ if (status & ISTR_INTS)
+ {
+ spin_lock_irqsave(a3000_host->host_lock, flags);
+ wd33c93_intr (a3000_host);
+ spin_unlock_irqrestore(a3000_host->host_lock, flags);
+ return IRQ_HANDLED;
+ }
+ printk("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
+ return IRQ_NONE;
+}
+
+static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
+{
+ unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
+ unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+
+ /*
+ * if the physical address has the wrong alignment, or if
+ * physical address is bad, or if it is a write and at the
+ * end of a physical memory chunk, then allocate a bounce
+ * buffer
+ */
+ if (addr & A3000_XFER_MASK ||
+ (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual)))
+ {
+ HDATA(a3000_host)->dma_bounce_len = (cmd->SCp.this_residual + 511)
+ & ~0x1ff;
+ HDATA(a3000_host)->dma_bounce_buffer =
+ kmalloc (HDATA(a3000_host)->dma_bounce_len, GFP_KERNEL);
+
+ /* can't allocate memory; use PIO */
+ if (!HDATA(a3000_host)->dma_bounce_buffer) {
+ HDATA(a3000_host)->dma_bounce_len = 0;
+ return 1;
+ }
+
+ if (!dir_in) {
+ /* copy to bounce buffer for a write */
+ if (cmd->use_sg) {
+ memcpy (HDATA(a3000_host)->dma_bounce_buffer,
+ cmd->SCp.ptr, cmd->SCp.this_residual);
+ } else
+ memcpy (HDATA(a3000_host)->dma_bounce_buffer,
+ cmd->request_buffer, cmd->request_bufflen);
+ }
+
+ addr = virt_to_bus(HDATA(a3000_host)->dma_bounce_buffer);
+ }
+
+ /* setup dma direction */
+ if (!dir_in)
+ cntr |= CNTR_DDIR;
+
+ /* remember direction */
+ HDATA(a3000_host)->dma_dir = dir_in;
+
+ DMA(a3000_host)->CNTR = cntr;
+
+ /* setup DMA *physical* address */
+ DMA(a3000_host)->ACR = addr;
+
+ if (dir_in)
+ /* invalidate any cache */
+ cache_clear (addr, cmd->SCp.this_residual);
+ else
+ /* push any dirty cache */
+ cache_push (addr, cmd->SCp.this_residual);
+
+ /* start DMA */
+ mb(); /* make sure setup is completed */
+ DMA(a3000_host)->ST_DMA = 1;
+ mb(); /* make sure DMA has started before next IO */
+
+ /* return success */
+ return 0;
+}
+
+static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt,
+ int status)
+{
+ /* disable SCSI interrupts */
+ unsigned short cntr = CNTR_PDMD;
+
+ if (!HDATA(instance)->dma_dir)
+ cntr |= CNTR_DDIR;
+
+ DMA(instance)->CNTR = cntr;
+ mb(); /* make sure CNTR is updated before next IO */
+
+ /* flush if we were reading */
+ if (HDATA(instance)->dma_dir) {
+ DMA(instance)->FLUSH = 1;
+ mb(); /* don't allow prefetch */
+ while (!(DMA(instance)->ISTR & ISTR_FE_FLG))
+ barrier();
+ mb(); /* no IO until FLUSH is done */
+ }
+
+ /* clear a possible interrupt */
+ /* I think that this CINT is only necessary if you are
+ * using the terminal count features. HM 7 Mar 1994
+ */
+ DMA(instance)->CINT = 1;
+
+ /* stop DMA */
+ DMA(instance)->SP_DMA = 1;
+ mb(); /* make sure DMA is stopped before next IO */
+
+ /* restore the CONTROL bits (minus the direction flag) */
+ DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN;
+ mb(); /* make sure CNTR is updated before next IO */
+
+ /* copy from a bounce buffer, if necessary */
+ if (status && HDATA(instance)->dma_bounce_buffer) {
+ if (SCpnt && SCpnt->use_sg) {
+ if (HDATA(instance)->dma_dir && SCpnt)
+ memcpy (SCpnt->SCp.ptr,
+ HDATA(instance)->dma_bounce_buffer,
+ SCpnt->SCp.this_residual);
+ kfree (HDATA(instance)->dma_bounce_buffer);
+ HDATA(instance)->dma_bounce_buffer = NULL;
+ HDATA(instance)->dma_bounce_len = 0;
+ } else {
+ if (HDATA(instance)->dma_dir && SCpnt)
+ memcpy (SCpnt->request_buffer,
+ HDATA(instance)->dma_bounce_buffer,
+ SCpnt->request_bufflen);
+
+ kfree (HDATA(instance)->dma_bounce_buffer);
+ HDATA(instance)->dma_bounce_buffer = NULL;
+ HDATA(instance)->dma_bounce_len = 0;
+ }
+ }
+}
+
+int __init a3000_detect(Scsi_Host_Template *tpnt)
+{
+ wd33c93_regs regs;
+
+ if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(A3000_SCSI))
+ return 0;
+ if (!request_mem_region(0xDD0000, 256, "wd33c93"))
+ return 0;
+
+ tpnt->proc_name = "A3000";
+ tpnt->proc_info = &wd33c93_proc_info;
+
+ a3000_host = scsi_register (tpnt, sizeof(struct WD33C93_hostdata));
+ if (a3000_host == NULL)
+ goto fail_register;
+
+ a3000_host->base = ZTWO_VADDR(0xDD0000);
+ a3000_host->irq = IRQ_AMIGA_PORTS;
+ DMA(a3000_host)->DAWR = DAWR_A3000;
+ regs.SASR = &(DMA(a3000_host)->SASR);
+ regs.SCMD = &(DMA(a3000_host)->SCMD);
+ wd33c93_init(a3000_host, regs, dma_setup, dma_stop, WD33C93_FS_12_15);
+ if (request_irq(IRQ_AMIGA_PORTS, a3000_intr, SA_SHIRQ, "A3000 SCSI",
+ a3000_intr))
+ goto fail_irq;
+ DMA(a3000_host)->CNTR = CNTR_PDMD | CNTR_INTEN;
+
+ return 1;
+
+fail_irq:
+ wd33c93_release();
+ scsi_unregister(a3000_host);
+fail_register:
+ release_mem_region(0xDD0000, 256);
+ return 0;
+}
+
+static int a3000_bus_reset(Scsi_Cmnd *cmd)
+{
+ /* FIXME perform bus-specific reset */
+ wd33c93_host_reset(cmd);
+ return SUCCESS;
+}
+
+#define HOSTS_C
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "A3000",
+ .name = "Amiga 3000 built-in SCSI",
+ .detect = a3000_detect,
+ .release = a3000_release,
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_bus_reset_handler = a3000_bus_reset,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+int a3000_release(struct Scsi_Host *instance)
+{
+ wd33c93_release();
+ DMA(instance)->CNTR = 0;
+ release_mem_region(0xDD0000, 256);
+ free_irq(IRQ_AMIGA_PORTS, a3000_intr);
+ return 1;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/a3000.h b/drivers/scsi/a3000.h
new file mode 100644
index 000000000000..b1eda731877d
--- /dev/null
+++ b/drivers/scsi/a3000.h
@@ -0,0 +1,79 @@
+#ifndef A3000_H
+#define A3000_H
+
+/* $Id: a3000.h,v 1.4 1997/01/19 23:07:10 davem Exp $
+ *
+ * Header file for the Amiga 3000 built-in SCSI controller for Linux
+ *
+ * Written and (C) 1993, Hamish Macdonald, see a3000.c for more info
+ *
+ */
+
+#include <linux/types.h>
+
+int a3000_detect(Scsi_Host_Template *);
+int a3000_release(struct Scsi_Host *);
+const char *wd33c93_info(void);
+int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd33c93_abort(Scsi_Cmnd *);
+int wd33c93_reset(Scsi_Cmnd *, unsigned int);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+/*
+ * if the transfer address ANDed with this results in a non-zero
+ * result, then we can't use DMA.
+ */
+#define A3000_XFER_MASK (0x00000003)
+
+typedef struct {
+ unsigned char pad1[2];
+ volatile unsigned short DAWR;
+ volatile unsigned int WTC;
+ unsigned char pad2[2];
+ volatile unsigned short CNTR;
+ volatile unsigned long ACR;
+ unsigned char pad3[2];
+ volatile unsigned short ST_DMA;
+ unsigned char pad4[2];
+ volatile unsigned short FLUSH;
+ unsigned char pad5[2];
+ volatile unsigned short CINT;
+ unsigned char pad6[2];
+ volatile unsigned short ISTR;
+ unsigned char pad7[30];
+ volatile unsigned short SP_DMA;
+ unsigned char pad8;
+ volatile unsigned char SASR;
+ unsigned char pad9;
+ volatile unsigned char SCMD;
+} a3000_scsiregs;
+
+#define DAWR_A3000 (3)
+
+/* CNTR bits. */
+#define CNTR_TCEN (1<<5)
+#define CNTR_PREST (1<<4)
+#define CNTR_PDMD (1<<3)
+#define CNTR_INTEN (1<<2)
+#define CNTR_DDIR (1<<1)
+#define CNTR_IO_DX (1<<0)
+
+/* ISTR bits. */
+#define ISTR_INTX (1<<8)
+#define ISTR_INT_F (1<<7)
+#define ISTR_INTS (1<<6)
+#define ISTR_E_INT (1<<5)
+#define ISTR_INT_P (1<<4)
+#define ISTR_UE_INT (1<<3)
+#define ISTR_OE_INT (1<<2)
+#define ISTR_FF_FLG (1<<1)
+#define ISTR_FE_FLG (1<<0)
+
+#endif /* A3000_H */
diff --git a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile
new file mode 100644
index 000000000000..28d133a3094f
--- /dev/null
+++ b/drivers/scsi/aacraid/Makefile
@@ -0,0 +1,8 @@
+# Adaptec aacraid
+
+obj-$(CONFIG_SCSI_AACRAID) := aacraid.o
+
+aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \
+ dpcsup.o rx.o sa.o rkt.o
+
+EXTRA_CFLAGS := -Idrivers/scsi
diff --git a/drivers/scsi/aacraid/README b/drivers/scsi/aacraid/README
new file mode 100644
index 000000000000..fdb0f45f7336
--- /dev/null
+++ b/drivers/scsi/aacraid/README
@@ -0,0 +1,66 @@
+AACRAID Driver for Linux (take two)
+
+Introduction
+-------------------------
+The aacraid driver adds support for Adaptec (http://www.adaptec.com)
+RAID controllers. This is a major rewrite from the original
+Adaptec supplied driver. It has signficantly cleaned up both the code
+and the running binary size (the module is less than half the size of
+the original).
+
+Supported Cards/Chipsets
+-------------------------
+ Adaptec 2020S
+ Adaptec 2025S
+ Adaptec 2120S
+ Adaptec 2200S
+ Adaptec 2230S
+ Adaptec 2240S
+ Adaptec 2410SA
+ Adaptec 2610SA
+ Adaptec 2810SA
+ Adaptec 21610SA
+ Adaptec 3230S
+ Adaptec 3240S
+ Adaptec 4000SAS
+ Adaptec 4005SAS
+ Adaptec 4800SAS
+ Adaptec 4805SAS
+ Adaptec 5400S
+ Dell PERC 2 Quad Channel
+ Dell PERC 2/Si
+ Dell PERC 3/Si
+ Dell PERC 3/Di
+ Dell CERC 2
+ HP NetRAID-4M
+ Legend S220
+ Legend S230
+
+People
+-------------------------
+Alan Cox <alan@redhat.com>
+Christoph Hellwig <hch@infradead.org> (updates for new-style PCI probing and SCSI host registration,
+ small cleanups/fixes)
+Matt Domsch <matt_domsch@dell.com> (revision ioctl, adapter messages)
+Deanna Bonds (non-DASD support, PAE fibs and 64 bit, added new adaptec controllers
+ added new ioctls, changed scsi interface to use new error handler,
+ increased the number of fibs and outstanding commands to a container)
+
+ (fixed 64bit and 64G memory model, changed confusing naming convention
+ where fibs that go to the hardware are consistently called hw_fibs and
+ not just fibs like the name of the driver tracking structure)
+Mark Salyzyn <Mark_Salyzyn@adaptec.com> Fixed panic issues and added some new product ids for upcoming hbas.
+
+Original Driver
+-------------------------
+Adaptec Unix OEM Product Group
+
+Mailing List
+-------------------------
+linux-scsi@vger.kernel.org (Interested parties troll here)
+Also note this is very different to Brian's original driver
+so don't expect him to support it.
+Adaptec does support this driver. Contact either tech support or Mark Salyzyn.
+
+Original by Brian Boerner February 2001
+Rewritten by Alan Cox, November 2001
diff --git a/drivers/scsi/aacraid/TODO b/drivers/scsi/aacraid/TODO
new file mode 100644
index 000000000000..25856a21d982
--- /dev/null
+++ b/drivers/scsi/aacraid/TODO
@@ -0,0 +1,6 @@
+o Testing
+o More testing
+o Feature request: display the firmware/bios/etc revisions in the
+ /proc info
+o Drop irq_mask, basically unused
+o I/O size increase
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
new file mode 100644
index 000000000000..f3fc35386060
--- /dev/null
+++ b/drivers/scsi/aacraid/aachba.c
@@ -0,0 +1,2037 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blkdev.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "aacraid.h"
+
+/* values for inqd_pdt: Peripheral device type in plain English */
+#define INQD_PDT_DA 0x00 /* Direct-access (DISK) device */
+#define INQD_PDT_PROC 0x03 /* Processor device */
+#define INQD_PDT_CHNGR 0x08 /* Changer (jukebox, scsi2) */
+#define INQD_PDT_COMM 0x09 /* Communication device (scsi2) */
+#define INQD_PDT_NOLUN2 0x1f /* Unknown Device (scsi2) */
+#define INQD_PDT_NOLUN 0x7f /* Logical Unit Not Present */
+
+#define INQD_PDT_DMASK 0x1F /* Peripheral Device Type Mask */
+#define INQD_PDT_QMASK 0xE0 /* Peripheral Device Qualifer Mask */
+
+#define MAX_FIB_DATA (sizeof(struct hw_fib) - sizeof(FIB_HEADER))
+
+#define MAX_DRIVER_SG_SEGMENT_COUNT 17
+
+/*
+ * Sense codes
+ */
+
+#define SENCODE_NO_SENSE 0x00
+#define SENCODE_END_OF_DATA 0x00
+#define SENCODE_BECOMING_READY 0x04
+#define SENCODE_INIT_CMD_REQUIRED 0x04
+#define SENCODE_PARAM_LIST_LENGTH_ERROR 0x1A
+#define SENCODE_INVALID_COMMAND 0x20
+#define SENCODE_LBA_OUT_OF_RANGE 0x21
+#define SENCODE_INVALID_CDB_FIELD 0x24
+#define SENCODE_LUN_NOT_SUPPORTED 0x25
+#define SENCODE_INVALID_PARAM_FIELD 0x26
+#define SENCODE_PARAM_NOT_SUPPORTED 0x26
+#define SENCODE_PARAM_VALUE_INVALID 0x26
+#define SENCODE_RESET_OCCURRED 0x29
+#define SENCODE_LUN_NOT_SELF_CONFIGURED_YET 0x3E
+#define SENCODE_INQUIRY_DATA_CHANGED 0x3F
+#define SENCODE_SAVING_PARAMS_NOT_SUPPORTED 0x39
+#define SENCODE_DIAGNOSTIC_FAILURE 0x40
+#define SENCODE_INTERNAL_TARGET_FAILURE 0x44
+#define SENCODE_INVALID_MESSAGE_ERROR 0x49
+#define SENCODE_LUN_FAILED_SELF_CONFIG 0x4c
+#define SENCODE_OVERLAPPED_COMMAND 0x4E
+
+/*
+ * Additional sense codes
+ */
+
+#define ASENCODE_NO_SENSE 0x00
+#define ASENCODE_END_OF_DATA 0x05
+#define ASENCODE_BECOMING_READY 0x01
+#define ASENCODE_INIT_CMD_REQUIRED 0x02
+#define ASENCODE_PARAM_LIST_LENGTH_ERROR 0x00
+#define ASENCODE_INVALID_COMMAND 0x00
+#define ASENCODE_LBA_OUT_OF_RANGE 0x00
+#define ASENCODE_INVALID_CDB_FIELD 0x00
+#define ASENCODE_LUN_NOT_SUPPORTED 0x00
+#define ASENCODE_INVALID_PARAM_FIELD 0x00
+#define ASENCODE_PARAM_NOT_SUPPORTED 0x01
+#define ASENCODE_PARAM_VALUE_INVALID 0x02
+#define ASENCODE_RESET_OCCURRED 0x00
+#define ASENCODE_LUN_NOT_SELF_CONFIGURED_YET 0x00
+#define ASENCODE_INQUIRY_DATA_CHANGED 0x03
+#define ASENCODE_SAVING_PARAMS_NOT_SUPPORTED 0x00
+#define ASENCODE_DIAGNOSTIC_FAILURE 0x80
+#define ASENCODE_INTERNAL_TARGET_FAILURE 0x00
+#define ASENCODE_INVALID_MESSAGE_ERROR 0x00
+#define ASENCODE_LUN_FAILED_SELF_CONFIG 0x00
+#define ASENCODE_OVERLAPPED_COMMAND 0x00
+
+#define BYTE0(x) (unsigned char)(x)
+#define BYTE1(x) (unsigned char)((x) >> 8)
+#define BYTE2(x) (unsigned char)((x) >> 16)
+#define BYTE3(x) (unsigned char)((x) >> 24)
+
+/*------------------------------------------------------------------------------
+ * S T R U C T S / T Y P E D E F S
+ *----------------------------------------------------------------------------*/
+/* SCSI inquiry data */
+struct inquiry_data {
+ u8 inqd_pdt; /* Peripheral qualifier | Peripheral Device Type */
+ u8 inqd_dtq; /* RMB | Device Type Qualifier */
+ u8 inqd_ver; /* ISO version | ECMA version | ANSI-approved version */
+ u8 inqd_rdf; /* AENC | TrmIOP | Response data format */
+ u8 inqd_len; /* Additional length (n-4) */
+ u8 inqd_pad1[2];/* Reserved - must be zero */
+ u8 inqd_pad2; /* RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */
+ u8 inqd_vid[8]; /* Vendor ID */
+ u8 inqd_pid[16];/* Product ID */
+ u8 inqd_prl[4]; /* Product Revision Level */
+};
+
+/*
+ * M O D U L E G L O B A L S
+ */
+
+static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* sgmap);
+static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg);
+static int aac_send_srb_fib(struct scsi_cmnd* scsicmd);
+#ifdef AAC_DETAILED_STATUS_INFO
+static char *aac_get_status_string(u32 status);
+#endif
+
+/*
+ * Non dasd selection is handled entirely in aachba now
+ */
+
+static int nondasd = -1;
+static int dacmode = -1;
+
+static int commit = -1;
+
+module_param(nondasd, int, 0);
+MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices. 0=off, 1=on");
+module_param(dacmode, int, 0);
+MODULE_PARM_DESC(dacmode, "Control whether dma addressing is using 64 bit DAC. 0=off, 1=on");
+module_param(commit, int, 0);
+MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the adapter for foreign arrays.\nThis is typically needed in systems that do not have a BIOS. 0=off, 1=on");
+
+/**
+ * aac_get_config_status - check the adapter configuration
+ * @common: adapter to query
+ *
+ * Query config status, and commit the configuration if needed.
+ */
+int aac_get_config_status(struct aac_dev *dev)
+{
+ int status = 0;
+ struct fib * fibptr;
+
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(fibptr);
+ {
+ struct aac_get_config_status *dinfo;
+ dinfo = (struct aac_get_config_status *) fib_data(fibptr);
+
+ dinfo->command = cpu_to_le32(VM_ContainerConfig);
+ dinfo->type = cpu_to_le32(CT_GET_CONFIG_STATUS);
+ dinfo->count = cpu_to_le32(sizeof(((struct aac_get_config_status_resp *)NULL)->data));
+ }
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof (struct aac_get_config_status),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ if (status < 0 ) {
+ printk(KERN_WARNING "aac_get_config_status: SendFIB failed.\n");
+ } else {
+ struct aac_get_config_status_resp *reply
+ = (struct aac_get_config_status_resp *) fib_data(fibptr);
+ dprintk((KERN_WARNING
+ "aac_get_config_status: response=%d status=%d action=%d\n",
+ le32_to_cpu(reply->response),
+ le32_to_cpu(reply->status),
+ le32_to_cpu(reply->data.action)));
+ if ((le32_to_cpu(reply->response) != ST_OK) ||
+ (le32_to_cpu(reply->status) != CT_OK) ||
+ (le32_to_cpu(reply->data.action) > CFACT_PAUSE)) {
+ printk(KERN_WARNING "aac_get_config_status: Will not issue the Commit Configuration\n");
+ status = -EINVAL;
+ }
+ }
+ fib_complete(fibptr);
+ /* Send a CT_COMMIT_CONFIG to enable discovery of devices */
+ if (status >= 0) {
+ if (commit == 1) {
+ struct aac_commit_config * dinfo;
+ fib_init(fibptr);
+ dinfo = (struct aac_commit_config *) fib_data(fibptr);
+
+ dinfo->command = cpu_to_le32(VM_ContainerConfig);
+ dinfo->type = cpu_to_le32(CT_COMMIT_CONFIG);
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof (struct aac_commit_config),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ fib_complete(fibptr);
+ } else if (commit == 0) {
+ printk(KERN_WARNING
+ "aac_get_config_status: Foreign device configurations are being ignored\n");
+ }
+ }
+ fib_free(fibptr);
+ return status;
+}
+
+/**
+ * aac_get_containers - list containers
+ * @common: adapter to probe
+ *
+ * Make a list of all containers on this controller
+ */
+int aac_get_containers(struct aac_dev *dev)
+{
+ struct fsa_dev_info *fsa_dev_ptr;
+ u32 index;
+ int status = 0;
+ struct fib * fibptr;
+ unsigned instance;
+ struct aac_get_container_count *dinfo;
+ struct aac_get_container_count_resp *dresp;
+ int maximum_num_containers = MAXIMUM_NUM_CONTAINERS;
+
+ instance = dev->scsi_host_ptr->unique_id;
+
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(fibptr);
+ dinfo = (struct aac_get_container_count *) fib_data(fibptr);
+ dinfo->command = cpu_to_le32(VM_ContainerConfig);
+ dinfo->type = cpu_to_le32(CT_GET_CONTAINER_COUNT);
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof (struct aac_get_container_count),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ if (status >= 0) {
+ dresp = (struct aac_get_container_count_resp *)fib_data(fibptr);
+ maximum_num_containers = le32_to_cpu(dresp->ContainerSwitchEntries);
+ fib_complete(fibptr);
+ }
+
+ if (maximum_num_containers < MAXIMUM_NUM_CONTAINERS)
+ maximum_num_containers = MAXIMUM_NUM_CONTAINERS;
+
+ fsa_dev_ptr = (struct fsa_dev_info *) kmalloc(
+ sizeof(*fsa_dev_ptr) * maximum_num_containers, GFP_KERNEL);
+ if (!fsa_dev_ptr) {
+ fib_free(fibptr);
+ return -ENOMEM;
+ }
+ memset(fsa_dev_ptr, 0, sizeof(*fsa_dev_ptr) * maximum_num_containers);
+
+ dev->fsa_dev = fsa_dev_ptr;
+ dev->maximum_num_containers = maximum_num_containers;
+
+ for (index = 0; index < dev->maximum_num_containers; index++) {
+ struct aac_query_mount *dinfo;
+ struct aac_mount *dresp;
+
+ fsa_dev_ptr[index].devname[0] = '\0';
+
+ fib_init(fibptr);
+ dinfo = (struct aac_query_mount *) fib_data(fibptr);
+
+ dinfo->command = cpu_to_le32(VM_NameServe);
+ dinfo->count = cpu_to_le32(index);
+ dinfo->type = cpu_to_le32(FT_FILESYS);
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof (struct aac_query_mount),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ if (status < 0 ) {
+ printk(KERN_WARNING "aac_get_containers: SendFIB failed.\n");
+ break;
+ }
+ dresp = (struct aac_mount *)fib_data(fibptr);
+
+ dprintk ((KERN_DEBUG
+ "VM_NameServe cid=%d status=%d vol=%d state=%d cap=%u\n",
+ (int)index, (int)le32_to_cpu(dresp->status),
+ (int)le32_to_cpu(dresp->mnt[0].vol),
+ (int)le32_to_cpu(dresp->mnt[0].state),
+ (unsigned)le32_to_cpu(dresp->mnt[0].capacity)));
+ if ((le32_to_cpu(dresp->status) == ST_OK) &&
+ (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) &&
+ (le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) {
+ fsa_dev_ptr[index].valid = 1;
+ fsa_dev_ptr[index].type = le32_to_cpu(dresp->mnt[0].vol);
+ fsa_dev_ptr[index].size = le32_to_cpu(dresp->mnt[0].capacity);
+ if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY)
+ fsa_dev_ptr[index].ro = 1;
+ }
+ fib_complete(fibptr);
+ /*
+ * If there are no more containers, then stop asking.
+ */
+ if ((index + 1) >= le32_to_cpu(dresp->count)){
+ break;
+ }
+ }
+ fib_free(fibptr);
+ return status;
+}
+
+static void aac_io_done(struct scsi_cmnd * scsicmd)
+{
+ unsigned long cpu_flags;
+ struct Scsi_Host *host = scsicmd->device->host;
+ spin_lock_irqsave(host->host_lock, cpu_flags);
+ scsicmd->scsi_done(scsicmd);
+ spin_unlock_irqrestore(host->host_lock, cpu_flags);
+}
+
+static void get_container_name_callback(void *context, struct fib * fibptr)
+{
+ struct aac_get_name_resp * get_name_reply;
+ struct scsi_cmnd * scsicmd;
+
+ scsicmd = (struct scsi_cmnd *) context;
+
+ dprintk((KERN_DEBUG "get_container_name_callback[cpu %d]: t = %ld.\n", smp_processor_id(), jiffies));
+ if (fibptr == NULL)
+ BUG();
+
+ get_name_reply = (struct aac_get_name_resp *) fib_data(fibptr);
+ /* Failure is irrelevant, using default value instead */
+ if ((le32_to_cpu(get_name_reply->status) == CT_OK)
+ && (get_name_reply->data[0] != '\0')) {
+ int count;
+ char * dp;
+ char * sp = get_name_reply->data;
+ sp[sizeof(((struct aac_get_name_resp *)NULL)->data)-1] = '\0';
+ while (*sp == ' ')
+ ++sp;
+ count = sizeof(((struct inquiry_data *)NULL)->inqd_pid);
+ dp = ((struct inquiry_data *)scsicmd->request_buffer)->inqd_pid;
+ if (*sp) do {
+ *dp++ = (*sp) ? *sp++ : ' ';
+ } while (--count > 0);
+ }
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+ aac_io_done(scsicmd);
+}
+
+/**
+ * aac_get_container_name - get container name, none blocking.
+ */
+static int aac_get_container_name(struct scsi_cmnd * scsicmd, int cid)
+{
+ int status;
+ struct aac_get_name *dinfo;
+ struct fib * cmd_fibcontext;
+ struct aac_dev * dev;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+
+ if (!(cmd_fibcontext = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(cmd_fibcontext);
+ dinfo = (struct aac_get_name *) fib_data(cmd_fibcontext);
+
+ dinfo->command = cpu_to_le32(VM_ContainerConfig);
+ dinfo->type = cpu_to_le32(CT_READ_NAME);
+ dinfo->cid = cpu_to_le32(cid);
+ dinfo->count = cpu_to_le32(sizeof(((struct aac_get_name_resp *)NULL)->data));
+
+ status = fib_send(ContainerCommand,
+ cmd_fibcontext,
+ sizeof (struct aac_get_name),
+ FsaNormal,
+ 0, 1,
+ (fib_callback) get_container_name_callback,
+ (void *) scsicmd);
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ return 0;
+
+ printk(KERN_WARNING "aac_get_container_name: fib_send failed with status: %d.\n", status);
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+ return -1;
+}
+
+/**
+ * probe_container - query a logical volume
+ * @dev: device to query
+ * @cid: container identifier
+ *
+ * Queries the controller about the given volume. The volume information
+ * is updated in the struct fsa_dev_info structure rather than returned.
+ */
+
+static int probe_container(struct aac_dev *dev, int cid)
+{
+ struct fsa_dev_info *fsa_dev_ptr;
+ int status;
+ struct aac_query_mount *dinfo;
+ struct aac_mount *dresp;
+ struct fib * fibptr;
+ unsigned instance;
+
+ fsa_dev_ptr = dev->fsa_dev;
+ instance = dev->scsi_host_ptr->unique_id;
+
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(fibptr);
+
+ dinfo = (struct aac_query_mount *)fib_data(fibptr);
+
+ dinfo->command = cpu_to_le32(VM_NameServe);
+ dinfo->count = cpu_to_le32(cid);
+ dinfo->type = cpu_to_le32(FT_FILESYS);
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof(struct aac_query_mount),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ if (status < 0) {
+ printk(KERN_WARNING "aacraid: probe_containers query failed.\n");
+ goto error;
+ }
+
+ dresp = (struct aac_mount *) fib_data(fibptr);
+
+ if ((le32_to_cpu(dresp->status) == ST_OK) &&
+ (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) &&
+ (le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) {
+ fsa_dev_ptr[cid].valid = 1;
+ fsa_dev_ptr[cid].type = le32_to_cpu(dresp->mnt[0].vol);
+ fsa_dev_ptr[cid].size = le32_to_cpu(dresp->mnt[0].capacity);
+ if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY)
+ fsa_dev_ptr[cid].ro = 1;
+ }
+
+error:
+ fib_complete(fibptr);
+ fib_free(fibptr);
+
+ return status;
+}
+
+/* Local Structure to set SCSI inquiry data strings */
+struct scsi_inq {
+ char vid[8]; /* Vendor ID */
+ char pid[16]; /* Product ID */
+ char prl[4]; /* Product Revision Level */
+};
+
+/**
+ * InqStrCopy - string merge
+ * @a: string to copy from
+ * @b: string to copy to
+ *
+ * Copy a String from one location to another
+ * without copying \0
+ */
+
+static void inqstrcpy(char *a, char *b)
+{
+
+ while(*a != (char)0)
+ *b++ = *a++;
+}
+
+static char *container_types[] = {
+ "None",
+ "Volume",
+ "Mirror",
+ "Stripe",
+ "RAID5",
+ "SSRW",
+ "SSRO",
+ "Morph",
+ "Legacy",
+ "RAID4",
+ "RAID10",
+ "RAID00",
+ "V-MIRRORS",
+ "PSEUDO R4",
+ "RAID50",
+ "Unknown"
+};
+
+
+
+/* Function: setinqstr
+ *
+ * Arguments: [1] pointer to void [1] int
+ *
+ * Purpose: Sets SCSI inquiry data strings for vendor, product
+ * and revision level. Allows strings to be set in platform dependant
+ * files instead of in OS dependant driver source.
+ */
+
+static void setinqstr(int devtype, void *data, int tindex)
+{
+ struct scsi_inq *str;
+ struct aac_driver_ident *mp;
+
+ mp = aac_get_driver_ident(devtype);
+
+ str = (struct scsi_inq *)(data); /* cast data to scsi inq block */
+
+ inqstrcpy (mp->vname, str->vid);
+ inqstrcpy (mp->model, str->pid); /* last six chars reserved for vol type */
+
+ if (tindex < (sizeof(container_types)/sizeof(char *))){
+ char *findit = str->pid;
+
+ for ( ; *findit != ' '; findit++); /* walk till we find a space */
+ /* RAID is superfluous in the context of a RAID device */
+ if (memcmp(findit-4, "RAID", 4) == 0)
+ *(findit -= 4) = ' ';
+ inqstrcpy (container_types[tindex], findit + 1);
+ }
+ inqstrcpy ("V1.0", str->prl);
+}
+
+void set_sense(u8 *sense_buf, u8 sense_key, u8 sense_code,
+ u8 a_sense_code, u8 incorrect_length,
+ u8 bit_pointer, u16 field_pointer,
+ u32 residue)
+{
+ sense_buf[0] = 0xF0; /* Sense data valid, err code 70h (current error) */
+ sense_buf[1] = 0; /* Segment number, always zero */
+
+ if (incorrect_length) {
+ sense_buf[2] = sense_key | 0x20;/* Set ILI bit | sense key */
+ sense_buf[3] = BYTE3(residue);
+ sense_buf[4] = BYTE2(residue);
+ sense_buf[5] = BYTE1(residue);
+ sense_buf[6] = BYTE0(residue);
+ } else
+ sense_buf[2] = sense_key; /* Sense key */
+
+ if (sense_key == ILLEGAL_REQUEST)
+ sense_buf[7] = 10; /* Additional sense length */
+ else
+ sense_buf[7] = 6; /* Additional sense length */
+
+ sense_buf[12] = sense_code; /* Additional sense code */
+ sense_buf[13] = a_sense_code; /* Additional sense code qualifier */
+ if (sense_key == ILLEGAL_REQUEST) {
+ sense_buf[15] = 0;
+
+ if (sense_code == SENCODE_INVALID_PARAM_FIELD)
+ sense_buf[15] = 0x80;/* Std sense key specific field */
+ /* Illegal parameter is in the parameter block */
+
+ if (sense_code == SENCODE_INVALID_CDB_FIELD)
+ sense_buf[15] = 0xc0;/* Std sense key specific field */
+ /* Illegal parameter is in the CDB block */
+ sense_buf[15] |= bit_pointer;
+ sense_buf[16] = field_pointer >> 8; /* MSB */
+ sense_buf[17] = field_pointer; /* LSB */
+ }
+}
+
+int aac_get_adapter_info(struct aac_dev* dev)
+{
+ struct fib* fibptr;
+ struct aac_adapter_info* info;
+ int rcode;
+ u32 tmp;
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(fibptr);
+ info = (struct aac_adapter_info*) fib_data(fibptr);
+
+ memset(info,0,sizeof(struct aac_adapter_info));
+
+ rcode = fib_send(RequestAdapterInfo,
+ fibptr,
+ sizeof(struct aac_adapter_info),
+ FsaNormal,
+ 1, 1,
+ NULL,
+ NULL);
+
+ memcpy(&dev->adapter_info, info, sizeof(struct aac_adapter_info));
+
+ tmp = le32_to_cpu(dev->adapter_info.kernelrev);
+ printk(KERN_INFO "%s%d: kernel %d.%d-%d[%d]\n",
+ dev->name,
+ dev->id,
+ tmp>>24,
+ (tmp>>16)&0xff,
+ tmp&0xff,
+ le32_to_cpu(dev->adapter_info.kernelbuild));
+ tmp = le32_to_cpu(dev->adapter_info.monitorrev);
+ printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n",
+ dev->name, dev->id,
+ tmp>>24,(tmp>>16)&0xff,tmp&0xff,
+ le32_to_cpu(dev->adapter_info.monitorbuild));
+ tmp = le32_to_cpu(dev->adapter_info.biosrev);
+ printk(KERN_INFO "%s%d: bios %d.%d-%d[%d]\n",
+ dev->name, dev->id,
+ tmp>>24,(tmp>>16)&0xff,tmp&0xff,
+ le32_to_cpu(dev->adapter_info.biosbuild));
+ if (le32_to_cpu(dev->adapter_info.serial[0]) != 0xBAD0)
+ printk(KERN_INFO "%s%d: serial %x\n",
+ dev->name, dev->id,
+ le32_to_cpu(dev->adapter_info.serial[0]));
+
+ dev->nondasd_support = 0;
+ dev->raid_scsi_mode = 0;
+ if(dev->adapter_info.options & AAC_OPT_NONDASD){
+ dev->nondasd_support = 1;
+ }
+
+ /*
+ * If the firmware supports ROMB RAID/SCSI mode and we are currently
+ * in RAID/SCSI mode, set the flag. For now if in this mode we will
+ * force nondasd support on. If we decide to allow the non-dasd flag
+ * additional changes changes will have to be made to support
+ * RAID/SCSI. the function aac_scsi_cmd in this module will have to be
+ * changed to support the new dev->raid_scsi_mode flag instead of
+ * leaching off of the dev->nondasd_support flag. Also in linit.c the
+ * function aac_detect will have to be modified where it sets up the
+ * max number of channels based on the aac->nondasd_support flag only.
+ */
+ if ((dev->adapter_info.options & AAC_OPT_SCSI_MANAGED) &&
+ (dev->adapter_info.options & AAC_OPT_RAID_SCSI_MODE)) {
+ dev->nondasd_support = 1;
+ dev->raid_scsi_mode = 1;
+ }
+ if (dev->raid_scsi_mode != 0)
+ printk(KERN_INFO "%s%d: ROMB RAID/SCSI mode enabled\n",
+ dev->name, dev->id);
+
+ if(nondasd != -1) {
+ dev->nondasd_support = (nondasd!=0);
+ }
+ if(dev->nondasd_support != 0){
+ printk(KERN_INFO "%s%d: Non-DASD support enabled.\n",dev->name, dev->id);
+ }
+
+ dev->dac_support = 0;
+ if( (sizeof(dma_addr_t) > 4) && (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)){
+ printk(KERN_INFO "%s%d: 64bit support enabled.\n", dev->name, dev->id);
+ dev->dac_support = 1;
+ }
+
+ if(dacmode != -1) {
+ dev->dac_support = (dacmode!=0);
+ }
+ if(dev->dac_support != 0) {
+ if (!pci_set_dma_mask(dev->pdev, 0xFFFFFFFFFFFFFFFFULL) &&
+ !pci_set_consistent_dma_mask(dev->pdev, 0xFFFFFFFFFFFFFFFFULL)) {
+ printk(KERN_INFO"%s%d: 64 Bit DAC enabled\n",
+ dev->name, dev->id);
+ } else if (!pci_set_dma_mask(dev->pdev, 0xFFFFFFFFULL) &&
+ !pci_set_consistent_dma_mask(dev->pdev, 0xFFFFFFFFULL)) {
+ printk(KERN_INFO"%s%d: DMA mask set failed, 64 Bit DAC disabled\n",
+ dev->name, dev->id);
+ dev->dac_support = 0;
+ } else {
+ printk(KERN_WARNING"%s%d: No suitable DMA available.\n",
+ dev->name, dev->id);
+ rcode = -ENOMEM;
+ }
+ }
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+
+ return rcode;
+}
+
+
+static void read_callback(void *context, struct fib * fibptr)
+{
+ struct aac_dev *dev;
+ struct aac_read_reply *readreply;
+ struct scsi_cmnd *scsicmd;
+ u32 lba;
+ u32 cid;
+
+ scsicmd = (struct scsi_cmnd *) context;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ cid = ID_LUN_TO_CONTAINER(scsicmd->device->id, scsicmd->device->lun);
+
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ dprintk((KERN_DEBUG "read_callback[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies));
+
+ if (fibptr == NULL)
+ BUG();
+
+ if(scsicmd->use_sg)
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)scsicmd->buffer,
+ scsicmd->use_sg,
+ scsicmd->sc_data_direction);
+ else if(scsicmd->request_bufflen)
+ pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle,
+ scsicmd->request_bufflen,
+ scsicmd->sc_data_direction);
+ readreply = (struct aac_read_reply *)fib_data(fibptr);
+ if (le32_to_cpu(readreply->status) == ST_OK)
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ else {
+ printk(KERN_WARNING "read_callback: read failed, status = %d\n",
+ le32_to_cpu(readreply->status));
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ set_sense((u8 *) &dev->fsa_dev[cid].sense_data,
+ HARDWARE_ERROR,
+ SENCODE_INTERNAL_TARGET_FAILURE,
+ ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+ 0, 0);
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ (sizeof(dev->fsa_dev[cid].sense_data) > sizeof(scsicmd->sense_buffer))
+ ? sizeof(scsicmd->sense_buffer)
+ : sizeof(dev->fsa_dev[cid].sense_data));
+ }
+ fib_complete(fibptr);
+ fib_free(fibptr);
+
+ aac_io_done(scsicmd);
+}
+
+static void write_callback(void *context, struct fib * fibptr)
+{
+ struct aac_dev *dev;
+ struct aac_write_reply *writereply;
+ struct scsi_cmnd *scsicmd;
+ u32 lba;
+ u32 cid;
+
+ scsicmd = (struct scsi_cmnd *) context;
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ cid = ID_LUN_TO_CONTAINER(scsicmd->device->id, scsicmd->device->lun);
+
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ dprintk((KERN_DEBUG "write_callback[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies));
+ if (fibptr == NULL)
+ BUG();
+
+ if(scsicmd->use_sg)
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)scsicmd->buffer,
+ scsicmd->use_sg,
+ scsicmd->sc_data_direction);
+ else if(scsicmd->request_bufflen)
+ pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle,
+ scsicmd->request_bufflen,
+ scsicmd->sc_data_direction);
+
+ writereply = (struct aac_write_reply *) fib_data(fibptr);
+ if (le32_to_cpu(writereply->status) == ST_OK)
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ else {
+ printk(KERN_WARNING "write_callback: write failed, status = %d\n", writereply->status);
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ set_sense((u8 *) &dev->fsa_dev[cid].sense_data,
+ HARDWARE_ERROR,
+ SENCODE_INTERNAL_TARGET_FAILURE,
+ ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+ 0, 0);
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ sizeof(struct sense_data));
+ }
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+ aac_io_done(scsicmd);
+}
+
+int aac_read(struct scsi_cmnd * scsicmd, int cid)
+{
+ u32 lba;
+ u32 count;
+ int status;
+
+ u16 fibsize;
+ struct aac_dev *dev;
+ struct fib * cmd_fibcontext;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ /*
+ * Get block address and transfer length
+ */
+ if (scsicmd->cmnd[0] == READ_6) /* 6 byte command */
+ {
+ dprintk((KERN_DEBUG "aachba: received a read(6) command on id %d.\n", cid));
+
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ count = scsicmd->cmnd[4];
+
+ if (count == 0)
+ count = 256;
+ } else {
+ dprintk((KERN_DEBUG "aachba: received a read(10) command on id %d.\n", cid));
+
+ lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
+ count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
+ }
+ dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies));
+ /*
+ * Alocate and initialize a Fib
+ */
+ if (!(cmd_fibcontext = fib_alloc(dev))) {
+ return -1;
+ }
+
+ fib_init(cmd_fibcontext);
+
+ if(dev->dac_support == 1) {
+ struct aac_read64 *readcmd;
+ readcmd = (struct aac_read64 *) fib_data(cmd_fibcontext);
+ readcmd->command = cpu_to_le32(VM_CtHostRead64);
+ readcmd->cid = cpu_to_le16(cid);
+ readcmd->sector_count = cpu_to_le16(count);
+ readcmd->block = cpu_to_le32(lba);
+ readcmd->pad = 0;
+ readcmd->flags = 0;
+
+ aac_build_sg64(scsicmd, &readcmd->sg);
+ fibsize = sizeof(struct aac_read64) +
+ ((le32_to_cpu(readcmd->sg.count) - 1) *
+ sizeof (struct sgentry64));
+ BUG_ON (fibsize > (sizeof(struct hw_fib) -
+ sizeof(struct aac_fibhdr)));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand64,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) read_callback,
+ (void *) scsicmd);
+ } else {
+ struct aac_read *readcmd;
+ readcmd = (struct aac_read *) fib_data(cmd_fibcontext);
+ readcmd->command = cpu_to_le32(VM_CtBlockRead);
+ readcmd->cid = cpu_to_le32(cid);
+ readcmd->block = cpu_to_le32(lba);
+ readcmd->count = cpu_to_le32(count * 512);
+
+ if (count * 512 > (64 * 1024))
+ BUG();
+
+ aac_build_sg(scsicmd, &readcmd->sg);
+ fibsize = sizeof(struct aac_read) +
+ ((le32_to_cpu(readcmd->sg.count) - 1) *
+ sizeof (struct sgentry));
+ BUG_ON (fibsize > (sizeof(struct hw_fib) -
+ sizeof(struct aac_fibhdr)));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) read_callback,
+ (void *) scsicmd);
+ }
+
+
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ return 0;
+
+ printk(KERN_WARNING "aac_read: fib_send failed with status: %d.\n", status);
+ /*
+ * For some reason, the Fib didn't queue, return QUEUE_FULL
+ */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_TASK_SET_FULL;
+ aac_io_done(scsicmd);
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+ return 0;
+}
+
+static int aac_write(struct scsi_cmnd * scsicmd, int cid)
+{
+ u32 lba;
+ u32 count;
+ int status;
+ u16 fibsize;
+ struct aac_dev *dev;
+ struct fib * cmd_fibcontext;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ /*
+ * Get block address and transfer length
+ */
+ if (scsicmd->cmnd[0] == WRITE_6) /* 6 byte command */
+ {
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ count = scsicmd->cmnd[4];
+ if (count == 0)
+ count = 256;
+ } else {
+ dprintk((KERN_DEBUG "aachba: received a write(10) command on id %d.\n", cid));
+ lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
+ count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
+ }
+ dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %u, t = %ld.\n",
+ smp_processor_id(), (unsigned long long)lba, jiffies));
+ /*
+ * Allocate and initialize a Fib then setup a BlockWrite command
+ */
+ if (!(cmd_fibcontext = fib_alloc(dev))) {
+ scsicmd->result = DID_ERROR << 16;
+ aac_io_done(scsicmd);
+ return 0;
+ }
+ fib_init(cmd_fibcontext);
+
+ if(dev->dac_support == 1) {
+ struct aac_write64 *writecmd;
+ writecmd = (struct aac_write64 *) fib_data(cmd_fibcontext);
+ writecmd->command = cpu_to_le32(VM_CtHostWrite64);
+ writecmd->cid = cpu_to_le16(cid);
+ writecmd->sector_count = cpu_to_le16(count);
+ writecmd->block = cpu_to_le32(lba);
+ writecmd->pad = 0;
+ writecmd->flags = 0;
+
+ aac_build_sg64(scsicmd, &writecmd->sg);
+ fibsize = sizeof(struct aac_write64) +
+ ((le32_to_cpu(writecmd->sg.count) - 1) *
+ sizeof (struct sgentry64));
+ BUG_ON (fibsize > (sizeof(struct hw_fib) -
+ sizeof(struct aac_fibhdr)));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand64,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) write_callback,
+ (void *) scsicmd);
+ } else {
+ struct aac_write *writecmd;
+ writecmd = (struct aac_write *) fib_data(cmd_fibcontext);
+ writecmd->command = cpu_to_le32(VM_CtBlockWrite);
+ writecmd->cid = cpu_to_le32(cid);
+ writecmd->block = cpu_to_le32(lba);
+ writecmd->count = cpu_to_le32(count * 512);
+ writecmd->sg.count = cpu_to_le32(1);
+ /* ->stable is not used - it did mean which type of write */
+
+ if (count * 512 > (64 * 1024)) {
+ BUG();
+ }
+
+ aac_build_sg(scsicmd, &writecmd->sg);
+ fibsize = sizeof(struct aac_write) +
+ ((le32_to_cpu(writecmd->sg.count) - 1) *
+ sizeof (struct sgentry));
+ BUG_ON (fibsize > (sizeof(struct hw_fib) -
+ sizeof(struct aac_fibhdr)));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) write_callback,
+ (void *) scsicmd);
+ }
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ {
+ dprintk("write queued.\n");
+ return 0;
+ }
+
+ printk(KERN_WARNING "aac_write: fib_send failed with status: %d\n", status);
+ /*
+ * For some reason, the Fib didn't queue, return QUEUE_FULL
+ */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_TASK_SET_FULL;
+ aac_io_done(scsicmd);
+
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+ return 0;
+}
+
+static void synchronize_callback(void *context, struct fib *fibptr)
+{
+ struct aac_synchronize_reply *synchronizereply;
+ struct scsi_cmnd *cmd;
+
+ cmd = context;
+
+ dprintk((KERN_DEBUG "synchronize_callback[cpu %d]: t = %ld.\n",
+ smp_processor_id(), jiffies));
+ BUG_ON(fibptr == NULL);
+
+
+ synchronizereply = fib_data(fibptr);
+ if (le32_to_cpu(synchronizereply->status) == CT_OK)
+ cmd->result = DID_OK << 16 |
+ COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ else {
+ struct scsi_device *sdev = cmd->device;
+ struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
+ u32 cid = ID_LUN_TO_CONTAINER(sdev->id, sdev->lun);
+ printk(KERN_WARNING
+ "synchronize_callback: synchronize failed, status = %d\n",
+ le32_to_cpu(synchronizereply->status));
+ cmd->result = DID_OK << 16 |
+ COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ set_sense((u8 *)&dev->fsa_dev[cid].sense_data,
+ HARDWARE_ERROR,
+ SENCODE_INTERNAL_TARGET_FAILURE,
+ ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+ 0, 0);
+ memcpy(cmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ min(sizeof(dev->fsa_dev[cid].sense_data),
+ sizeof(cmd->sense_buffer)));
+ }
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+ aac_io_done(cmd);
+}
+
+static int aac_synchronize(struct scsi_cmnd *scsicmd, int cid)
+{
+ int status;
+ struct fib *cmd_fibcontext;
+ struct aac_synchronize *synchronizecmd;
+ struct scsi_cmnd *cmd;
+ struct scsi_device *sdev = scsicmd->device;
+ int active = 0;
+ unsigned long flags;
+
+ /*
+ * Wait for all commands to complete to this specific
+ * target (block).
+ */
+ spin_lock_irqsave(&sdev->list_lock, flags);
+ list_for_each_entry(cmd, &sdev->cmd_list, list)
+ if (cmd != scsicmd && cmd->serial_number != 0) {
+ ++active;
+ break;
+ }
+
+ spin_unlock_irqrestore(&sdev->list_lock, flags);
+
+ /*
+ * Yield the processor (requeue for later)
+ */
+ if (active)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ /*
+ * Alocate and initialize a Fib
+ */
+ if (!(cmd_fibcontext =
+ fib_alloc((struct aac_dev *)scsicmd->device->host->hostdata)))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ fib_init(cmd_fibcontext);
+
+ synchronizecmd = fib_data(cmd_fibcontext);
+ synchronizecmd->command = cpu_to_le32(VM_ContainerConfig);
+ synchronizecmd->type = cpu_to_le32(CT_FLUSH_CACHE);
+ synchronizecmd->cid = cpu_to_le32(cid);
+ synchronizecmd->count =
+ cpu_to_le32(sizeof(((struct aac_synchronize_reply *)NULL)->data));
+
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand,
+ cmd_fibcontext,
+ sizeof(struct aac_synchronize),
+ FsaNormal,
+ 0, 1,
+ (fib_callback)synchronize_callback,
+ (void *)scsicmd);
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ return 0;
+
+ printk(KERN_WARNING
+ "aac_synchronize: fib_send failed with status: %d.\n", status);
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+ return SCSI_MLQUEUE_HOST_BUSY;
+}
+
+/**
+ * aac_scsi_cmd() - Process SCSI command
+ * @scsicmd: SCSI command block
+ *
+ * Emulate a SCSI command and queue the required request for the
+ * aacraid firmware.
+ */
+
+int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
+{
+ u32 cid = 0;
+ struct Scsi_Host *host = scsicmd->device->host;
+ struct aac_dev *dev = (struct aac_dev *)host->hostdata;
+ struct fsa_dev_info *fsa_dev_ptr = dev->fsa_dev;
+ int cardtype = dev->cardtype;
+ int ret;
+
+ /*
+ * If the bus, id or lun is out of range, return fail
+ * Test does not apply to ID 16, the pseudo id for the controller
+ * itself.
+ */
+ if (scsicmd->device->id != host->this_id) {
+ if ((scsicmd->device->channel == 0) ){
+ if( (scsicmd->device->id >= dev->maximum_num_containers) || (scsicmd->device->lun != 0)){
+ scsicmd->result = DID_NO_CONNECT << 16;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+ cid = ID_LUN_TO_CONTAINER(scsicmd->device->id, scsicmd->device->lun);
+
+ /*
+ * If the target container doesn't exist, it may have
+ * been newly created
+ */
+ if ((fsa_dev_ptr[cid].valid & 1) == 0) {
+ switch (scsicmd->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ case TEST_UNIT_READY:
+ spin_unlock_irq(host->host_lock);
+ probe_container(dev, cid);
+ spin_lock_irq(host->host_lock);
+ if (fsa_dev_ptr[cid].valid == 0) {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+ default:
+ break;
+ }
+ }
+ /*
+ * If the target container still doesn't exist,
+ * return failure
+ */
+ if (fsa_dev_ptr[cid].valid == 0) {
+ scsicmd->result = DID_BAD_TARGET << 16;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+ } else { /* check for physical non-dasd devices */
+ if(dev->nondasd_support == 1){
+ return aac_send_srb_fib(scsicmd);
+ } else {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+ }
+ }
+ /*
+ * else Command for the controller itself
+ */
+ else if ((scsicmd->cmnd[0] != INQUIRY) && /* only INQUIRY & TUR cmnd supported for controller */
+ (scsicmd->cmnd[0] != TEST_UNIT_READY))
+ {
+ dprintk((KERN_WARNING "Only INQUIRY & TUR command supported for controller, rcvd = 0x%x.\n", scsicmd->cmnd[0]));
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ set_sense((u8 *) &dev->fsa_dev[cid].sense_data,
+ ILLEGAL_REQUEST,
+ SENCODE_INVALID_COMMAND,
+ ASENCODE_INVALID_COMMAND, 0, 0, 0, 0);
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ (sizeof(dev->fsa_dev[cid].sense_data) > sizeof(scsicmd->sense_buffer))
+ ? sizeof(scsicmd->sense_buffer)
+ : sizeof(dev->fsa_dev[cid].sense_data));
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+
+
+ /* Handle commands here that don't really require going out to the adapter */
+ switch (scsicmd->cmnd[0]) {
+ case INQUIRY:
+ {
+ struct inquiry_data *inq_data_ptr;
+
+ dprintk((KERN_DEBUG "INQUIRY command, ID: %d.\n", scsicmd->device->id));
+ inq_data_ptr = (struct inquiry_data *)scsicmd->request_buffer;
+ memset(inq_data_ptr, 0, sizeof (struct inquiry_data));
+
+ inq_data_ptr->inqd_ver = 2; /* claim compliance to SCSI-2 */
+ inq_data_ptr->inqd_dtq = 0x80; /* set RMB bit to one indicating that the medium is removable */
+ inq_data_ptr->inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */
+ inq_data_ptr->inqd_len = 31;
+ /*Format for "pad2" is RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */
+ inq_data_ptr->inqd_pad2= 0x32 ; /*WBus16|Sync|CmdQue */
+ /*
+ * Set the Vendor, Product, and Revision Level
+ * see: <vendor>.c i.e. aac.c
+ */
+ if (scsicmd->device->id == host->this_id) {
+ setinqstr(cardtype, (void *) (inq_data_ptr->inqd_vid), (sizeof(container_types)/sizeof(char *)));
+ inq_data_ptr->inqd_pdt = INQD_PDT_PROC; /* Processor device */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+ setinqstr(cardtype, (void *) (inq_data_ptr->inqd_vid), fsa_dev_ptr[cid].type);
+ inq_data_ptr->inqd_pdt = INQD_PDT_DA; /* Direct/random access device */
+ return aac_get_container_name(scsicmd, cid);
+ }
+ case READ_CAPACITY:
+ {
+ u32 capacity;
+ char *cp;
+
+ dprintk((KERN_DEBUG "READ CAPACITY command.\n"));
+ if (fsa_dev_ptr[cid].size <= 0x100000000LL)
+ capacity = fsa_dev_ptr[cid].size - 1;
+ else
+ capacity = (u32)-1;
+ cp = scsicmd->request_buffer;
+ cp[0] = (capacity >> 24) & 0xff;
+ cp[1] = (capacity >> 16) & 0xff;
+ cp[2] = (capacity >> 8) & 0xff;
+ cp[3] = (capacity >> 0) & 0xff;
+ cp[4] = 0;
+ cp[5] = 0;
+ cp[6] = 2;
+ cp[7] = 0;
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+
+ return 0;
+ }
+
+ case MODE_SENSE:
+ {
+ char *mode_buf;
+
+ dprintk((KERN_DEBUG "MODE SENSE command.\n"));
+ mode_buf = scsicmd->request_buffer;
+ mode_buf[0] = 3; /* Mode data length */
+ mode_buf[1] = 0; /* Medium type - default */
+ mode_buf[2] = 0; /* Device-specific param, bit 8: 0/1 = write enabled/protected */
+ mode_buf[3] = 0; /* Block descriptor length */
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+
+ return 0;
+ }
+ case MODE_SENSE_10:
+ {
+ char *mode_buf;
+
+ dprintk((KERN_DEBUG "MODE SENSE 10 byte command.\n"));
+ mode_buf = scsicmd->request_buffer;
+ mode_buf[0] = 0; /* Mode data length (MSB) */
+ mode_buf[1] = 6; /* Mode data length (LSB) */
+ mode_buf[2] = 0; /* Medium type - default */
+ mode_buf[3] = 0; /* Device-specific param, bit 8: 0/1 = write enabled/protected */
+ mode_buf[4] = 0; /* reserved */
+ mode_buf[5] = 0; /* reserved */
+ mode_buf[6] = 0; /* Block descriptor length (MSB) */
+ mode_buf[7] = 0; /* Block descriptor length (LSB) */
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+
+ return 0;
+ }
+ case REQUEST_SENSE:
+ dprintk((KERN_DEBUG "REQUEST SENSE command.\n"));
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, sizeof (struct sense_data));
+ memset(&dev->fsa_dev[cid].sense_data, 0, sizeof (struct sense_data));
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ dprintk((KERN_DEBUG "LOCK command.\n"));
+ if (scsicmd->cmnd[4])
+ fsa_dev_ptr[cid].locked = 1;
+ else
+ fsa_dev_ptr[cid].locked = 0;
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ /*
+ * These commands are all No-Ops
+ */
+ case TEST_UNIT_READY:
+ case RESERVE:
+ case RELEASE:
+ case REZERO_UNIT:
+ case REASSIGN_BLOCKS:
+ case SEEK_10:
+ case START_STOP:
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+
+ switch (scsicmd->cmnd[0])
+ {
+ case READ_6:
+ case READ_10:
+ /*
+ * Hack to keep track of ordinal number of the device that
+ * corresponds to a container. Needed to convert
+ * containers to /dev/sd device names
+ */
+
+ spin_unlock_irq(host->host_lock);
+ if (scsicmd->request->rq_disk)
+ memcpy(fsa_dev_ptr[cid].devname,
+ scsicmd->request->rq_disk->disk_name,
+ 8);
+
+ ret = aac_read(scsicmd, cid);
+ spin_lock_irq(host->host_lock);
+ return ret;
+
+ case WRITE_6:
+ case WRITE_10:
+ spin_unlock_irq(host->host_lock);
+ ret = aac_write(scsicmd, cid);
+ spin_lock_irq(host->host_lock);
+ return ret;
+
+ case SYNCHRONIZE_CACHE:
+ /* Issue FIB to tell Firmware to flush it's cache */
+ return aac_synchronize(scsicmd, cid);
+
+ default:
+ /*
+ * Unhandled commands
+ */
+ printk(KERN_WARNING "Unhandled SCSI Command: 0x%x.\n", scsicmd->cmnd[0]);
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ set_sense((u8 *) &dev->fsa_dev[cid].sense_data,
+ ILLEGAL_REQUEST, SENCODE_INVALID_COMMAND,
+ ASENCODE_INVALID_COMMAND, 0, 0, 0, 0);
+ memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
+ (sizeof(dev->fsa_dev[cid].sense_data) > sizeof(scsicmd->sense_buffer))
+ ? sizeof(scsicmd->sense_buffer)
+ : sizeof(dev->fsa_dev[cid].sense_data));
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+}
+
+static int query_disk(struct aac_dev *dev, void __user *arg)
+{
+ struct aac_query_disk qd;
+ struct fsa_dev_info *fsa_dev_ptr;
+
+ fsa_dev_ptr = dev->fsa_dev;
+ if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk)))
+ return -EFAULT;
+ if (qd.cnum == -1)
+ qd.cnum = ID_LUN_TO_CONTAINER(qd.id, qd.lun);
+ else if ((qd.bus == -1) && (qd.id == -1) && (qd.lun == -1))
+ {
+ if (qd.cnum < 0 || qd.cnum >= dev->maximum_num_containers)
+ return -EINVAL;
+ qd.instance = dev->scsi_host_ptr->host_no;
+ qd.bus = 0;
+ qd.id = CONTAINER_TO_ID(qd.cnum);
+ qd.lun = CONTAINER_TO_LUN(qd.cnum);
+ }
+ else return -EINVAL;
+
+ qd.valid = fsa_dev_ptr[qd.cnum].valid;
+ qd.locked = fsa_dev_ptr[qd.cnum].locked;
+ qd.deleted = fsa_dev_ptr[qd.cnum].deleted;
+
+ if (fsa_dev_ptr[qd.cnum].devname[0] == '\0')
+ qd.unmapped = 1;
+ else
+ qd.unmapped = 0;
+
+ strlcpy(qd.name, fsa_dev_ptr[qd.cnum].devname,
+ min(sizeof(qd.name), sizeof(fsa_dev_ptr[qd.cnum].devname) + 1));
+
+ if (copy_to_user(arg, &qd, sizeof (struct aac_query_disk)))
+ return -EFAULT;
+ return 0;
+}
+
+static int force_delete_disk(struct aac_dev *dev, void __user *arg)
+{
+ struct aac_delete_disk dd;
+ struct fsa_dev_info *fsa_dev_ptr;
+
+ fsa_dev_ptr = dev->fsa_dev;
+
+ if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
+ return -EFAULT;
+
+ if (dd.cnum >= dev->maximum_num_containers)
+ return -EINVAL;
+ /*
+ * Mark this container as being deleted.
+ */
+ fsa_dev_ptr[dd.cnum].deleted = 1;
+ /*
+ * Mark the container as no longer valid
+ */
+ fsa_dev_ptr[dd.cnum].valid = 0;
+ return 0;
+}
+
+static int delete_disk(struct aac_dev *dev, void __user *arg)
+{
+ struct aac_delete_disk dd;
+ struct fsa_dev_info *fsa_dev_ptr;
+
+ fsa_dev_ptr = dev->fsa_dev;
+
+ if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
+ return -EFAULT;
+
+ if (dd.cnum >= dev->maximum_num_containers)
+ return -EINVAL;
+ /*
+ * If the container is locked, it can not be deleted by the API.
+ */
+ if (fsa_dev_ptr[dd.cnum].locked)
+ return -EBUSY;
+ else {
+ /*
+ * Mark the container as no longer being valid.
+ */
+ fsa_dev_ptr[dd.cnum].valid = 0;
+ fsa_dev_ptr[dd.cnum].devname[0] = '\0';
+ return 0;
+ }
+}
+
+int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg)
+{
+ switch (cmd) {
+ case FSACTL_QUERY_DISK:
+ return query_disk(dev, arg);
+ case FSACTL_DELETE_DISK:
+ return delete_disk(dev, arg);
+ case FSACTL_FORCE_DELETE_DISK:
+ return force_delete_disk(dev, arg);
+ case FSACTL_GET_CONTAINERS:
+ return aac_get_containers(dev);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ *
+ * aac_srb_callback
+ * @context: the context set in the fib - here it is scsi cmd
+ * @fibptr: pointer to the fib
+ *
+ * Handles the completion of a scsi command to a non dasd device
+ *
+ */
+
+static void aac_srb_callback(void *context, struct fib * fibptr)
+{
+ struct aac_dev *dev;
+ struct aac_srb_reply *srbreply;
+ struct scsi_cmnd *scsicmd;
+
+ scsicmd = (struct scsi_cmnd *) context;
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+
+ if (fibptr == NULL)
+ BUG();
+
+ srbreply = (struct aac_srb_reply *) fib_data(fibptr);
+
+ scsicmd->sense_buffer[0] = '\0'; /* Initialize sense valid flag to false */
+ /*
+ * Calculate resid for sg
+ */
+
+ scsicmd->resid = scsicmd->request_bufflen -
+ le32_to_cpu(srbreply->data_xfer_length);
+
+ if(scsicmd->use_sg)
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)scsicmd->buffer,
+ scsicmd->use_sg,
+ scsicmd->sc_data_direction);
+ else if(scsicmd->request_bufflen)
+ pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle, scsicmd->request_bufflen,
+ scsicmd->sc_data_direction);
+
+ /*
+ * First check the fib status
+ */
+
+ if (le32_to_cpu(srbreply->status) != ST_OK){
+ int len;
+ printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status));
+ len = (le32_to_cpu(srbreply->sense_data_size) >
+ sizeof(scsicmd->sense_buffer)) ?
+ sizeof(scsicmd->sense_buffer) :
+ le32_to_cpu(srbreply->sense_data_size);
+ scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
+ memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+ }
+
+ /*
+ * Next check the srb status
+ */
+ switch( (le32_to_cpu(srbreply->srb_status))&0x3f){
+ case SRB_STATUS_ERROR_RECOVERY:
+ case SRB_STATUS_PENDING:
+ case SRB_STATUS_SUCCESS:
+ if(scsicmd->cmnd[0] == INQUIRY ){
+ u8 b;
+ u8 b1;
+ /* We can't expose disk devices because we can't tell whether they
+ * are the raw container drives or stand alone drives. If they have
+ * the removable bit set then we should expose them though.
+ */
+ b = (*(u8*)scsicmd->buffer)&0x1f;
+ b1 = ((u8*)scsicmd->buffer)[1];
+ if( b==TYPE_TAPE || b==TYPE_WORM || b==TYPE_ROM || b==TYPE_MOD|| b==TYPE_MEDIUM_CHANGER
+ || (b==TYPE_DISK && (b1&0x80)) ){
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ /*
+ * We will allow disk devices if in RAID/SCSI mode and
+ * the channel is 2
+ */
+ } else if ((dev->raid_scsi_mode) &&
+ (scsicmd->device->channel == 2)) {
+ scsicmd->result = DID_OK << 16 |
+ COMMAND_COMPLETE << 8;
+ } else {
+ scsicmd->result = DID_NO_CONNECT << 16 |
+ COMMAND_COMPLETE << 8;
+ }
+ } else {
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ }
+ break;
+ case SRB_STATUS_DATA_OVERRUN:
+ switch(scsicmd->cmnd[0]){
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+ if(le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow ) {
+ printk(KERN_WARNING"aacraid: SCSI CMD underflow\n");
+ } else {
+ printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n");
+ }
+ scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ case INQUIRY: {
+ u8 b;
+ u8 b1;
+ /* We can't expose disk devices because we can't tell whether they
+ * are the raw container drives or stand alone drives
+ */
+ b = (*(u8*)scsicmd->buffer)&0x0f;
+ b1 = ((u8*)scsicmd->buffer)[1];
+ if( b==TYPE_TAPE || b==TYPE_WORM || b==TYPE_ROM || b==TYPE_MOD|| b==TYPE_MEDIUM_CHANGER
+ || (b==TYPE_DISK && (b1&0x80)) ){
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ /*
+ * We will allow disk devices if in RAID/SCSI mode and
+ * the channel is 2
+ */
+ } else if ((dev->raid_scsi_mode) &&
+ (scsicmd->device->channel == 2)) {
+ scsicmd->result = DID_OK << 16 |
+ COMMAND_COMPLETE << 8;
+ } else {
+ scsicmd->result = DID_NO_CONNECT << 16 |
+ COMMAND_COMPLETE << 8;
+ }
+ break;
+ }
+ default:
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ break;
+ }
+ break;
+ case SRB_STATUS_ABORTED:
+ scsicmd->result = DID_ABORT << 16 | ABORT << 8;
+ break;
+ case SRB_STATUS_ABORT_FAILED:
+ // Not sure about this one - but assuming the hba was trying to abort for some reason
+ scsicmd->result = DID_ERROR << 16 | ABORT << 8;
+ break;
+ case SRB_STATUS_PARITY_ERROR:
+ scsicmd->result = DID_PARITY << 16 | MSG_PARITY_ERROR << 8;
+ break;
+ case SRB_STATUS_NO_DEVICE:
+ case SRB_STATUS_INVALID_PATH_ID:
+ case SRB_STATUS_INVALID_TARGET_ID:
+ case SRB_STATUS_INVALID_LUN:
+ case SRB_STATUS_SELECTION_TIMEOUT:
+ scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_COMMAND_TIMEOUT:
+ case SRB_STATUS_TIMEOUT:
+ scsicmd->result = DID_TIME_OUT << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_BUSY:
+ scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_BUS_RESET:
+ scsicmd->result = DID_RESET << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_MESSAGE_REJECTED:
+ scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8;
+ break;
+ case SRB_STATUS_REQUEST_FLUSHED:
+ case SRB_STATUS_ERROR:
+ case SRB_STATUS_INVALID_REQUEST:
+ case SRB_STATUS_REQUEST_SENSE_FAILED:
+ case SRB_STATUS_NO_HBA:
+ case SRB_STATUS_UNEXPECTED_BUS_FREE:
+ case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
+ case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
+ case SRB_STATUS_DELAYED_RETRY:
+ case SRB_STATUS_BAD_FUNCTION:
+ case SRB_STATUS_NOT_STARTED:
+ case SRB_STATUS_NOT_IN_USE:
+ case SRB_STATUS_FORCE_ABORT:
+ case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
+ default:
+#ifdef AAC_DETAILED_STATUS_INFO
+ printk("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n",
+ le32_to_cpu(srbreply->srb_status) & 0x3F,
+ aac_get_status_string(
+ le32_to_cpu(srbreply->srb_status) & 0x3F),
+ scsicmd->cmnd[0],
+ le32_to_cpu(srbreply->scsi_status));
+#endif
+ scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ }
+ if (le32_to_cpu(srbreply->scsi_status) == 0x02 ){ // Check Condition
+ int len;
+ scsicmd->result |= SAM_STAT_CHECK_CONDITION;
+ len = (le32_to_cpu(srbreply->sense_data_size) >
+ sizeof(scsicmd->sense_buffer)) ?
+ sizeof(scsicmd->sense_buffer) :
+ le32_to_cpu(srbreply->sense_data_size);
+#ifdef AAC_DETAILED_STATUS_INFO
+ dprintk((KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n",
+ le32_to_cpu(srbreply->status), len));
+#endif
+ memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+
+ }
+ /*
+ * OR in the scsi status (already shifted up a bit)
+ */
+ scsicmd->result |= le32_to_cpu(srbreply->scsi_status);
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+ aac_io_done(scsicmd);
+}
+
+/**
+ *
+ * aac_send_scb_fib
+ * @scsicmd: the scsi command block
+ *
+ * This routine will form a FIB and fill in the aac_srb from the
+ * scsicmd passed in.
+ */
+
+static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
+{
+ struct fib* cmd_fibcontext;
+ struct aac_dev* dev;
+ int status;
+ struct aac_srb *srbcmd;
+ u16 fibsize;
+ u32 flag;
+ u32 timeout;
+
+ if( scsicmd->device->id > 15 || scsicmd->device->lun > 7) {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ scsicmd->scsi_done(scsicmd);
+ return 0;
+ }
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ switch(scsicmd->sc_data_direction){
+ case DMA_TO_DEVICE:
+ flag = SRB_DataOut;
+ break;
+ case DMA_BIDIRECTIONAL:
+ flag = SRB_DataIn | SRB_DataOut;
+ break;
+ case DMA_FROM_DEVICE:
+ flag = SRB_DataIn;
+ break;
+ case DMA_NONE:
+ default: /* shuts up some versions of gcc */
+ flag = SRB_NoDataXfer;
+ break;
+ }
+
+
+ /*
+ * Allocate and initialize a Fib then setup a BlockWrite command
+ */
+ if (!(cmd_fibcontext = fib_alloc(dev))) {
+ return -1;
+ }
+ fib_init(cmd_fibcontext);
+
+ srbcmd = (struct aac_srb*) fib_data(cmd_fibcontext);
+ srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi);
+ srbcmd->channel = cpu_to_le32(aac_logical_to_phys(scsicmd->device->channel));
+ srbcmd->id = cpu_to_le32(scsicmd->device->id);
+ srbcmd->lun = cpu_to_le32(scsicmd->device->lun);
+ srbcmd->flags = cpu_to_le32(flag);
+ timeout = (scsicmd->timeout-jiffies)/HZ;
+ if(timeout == 0){
+ timeout = 1;
+ }
+ srbcmd->timeout = cpu_to_le32(timeout); // timeout in seconds
+ srbcmd->retry_limit = 0; /* Obsolete parameter */
+ srbcmd->cdb_size = cpu_to_le32(scsicmd->cmd_len);
+
+ if( dev->dac_support == 1 ) {
+ aac_build_sg64(scsicmd, (struct sgmap64*) &srbcmd->sg);
+ srbcmd->count = cpu_to_le32(scsicmd->request_bufflen);
+
+ memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+ memcpy(srbcmd->cdb, scsicmd->cmnd, scsicmd->cmd_len);
+ /*
+ * Build Scatter/Gather list
+ */
+ fibsize = sizeof (struct aac_srb) - sizeof (struct sgentry) +
+ ((le32_to_cpu(srbcmd->sg.count) & 0xff) *
+ sizeof (struct sgentry64));
+ BUG_ON (fibsize > (sizeof(struct hw_fib) -
+ sizeof(struct aac_fibhdr)));
+
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ScsiPortCommand64, cmd_fibcontext,
+ fibsize, FsaNormal, 0, 1,
+ (fib_callback) aac_srb_callback,
+ (void *) scsicmd);
+ } else {
+ aac_build_sg(scsicmd, (struct sgmap*)&srbcmd->sg);
+ srbcmd->count = cpu_to_le32(scsicmd->request_bufflen);
+
+ memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+ memcpy(srbcmd->cdb, scsicmd->cmnd, scsicmd->cmd_len);
+ /*
+ * Build Scatter/Gather list
+ */
+ fibsize = sizeof (struct aac_srb) +
+ (((le32_to_cpu(srbcmd->sg.count) & 0xff) - 1) *
+ sizeof (struct sgentry));
+ BUG_ON (fibsize > (sizeof(struct hw_fib) -
+ sizeof(struct aac_fibhdr)));
+
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ScsiPortCommand, cmd_fibcontext, fibsize, FsaNormal, 0, 1,
+ (fib_callback) aac_srb_callback, (void *) scsicmd);
+ }
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS){
+ return 0;
+ }
+
+ printk(KERN_WARNING "aac_srb: fib_send failed with status: %d\n", status);
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+
+ return -1;
+}
+
+static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* psg)
+{
+ struct aac_dev *dev;
+ unsigned long byte_count = 0;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ // Get rid of old data
+ psg->count = 0;
+ psg->sg[0].addr = 0;
+ psg->sg[0].count = 0;
+ if (scsicmd->use_sg) {
+ struct scatterlist *sg;
+ int i;
+ int sg_count;
+ sg = (struct scatterlist *) scsicmd->request_buffer;
+
+ sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg,
+ scsicmd->sc_data_direction);
+ psg->count = cpu_to_le32(sg_count);
+
+ byte_count = 0;
+
+ for (i = 0; i < sg_count; i++) {
+ psg->sg[i].addr = cpu_to_le32(sg_dma_address(sg));
+ psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+ byte_count += sg_dma_len(sg);
+ sg++;
+ }
+ /* hba wants the size to be exact */
+ if(byte_count > scsicmd->request_bufflen){
+ psg->sg[i-1].count -= (byte_count - scsicmd->request_bufflen);
+ byte_count = scsicmd->request_bufflen;
+ }
+ /* Check for command underflow */
+ if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+ }
+ else if(scsicmd->request_bufflen) {
+ dma_addr_t addr;
+ addr = pci_map_single(dev->pdev,
+ scsicmd->request_buffer,
+ scsicmd->request_bufflen,
+ scsicmd->sc_data_direction);
+ psg->count = cpu_to_le32(1);
+ psg->sg[0].addr = cpu_to_le32(addr);
+ psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen);
+ scsicmd->SCp.dma_handle = addr;
+ byte_count = scsicmd->request_bufflen;
+ }
+ return byte_count;
+}
+
+
+static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg)
+{
+ struct aac_dev *dev;
+ unsigned long byte_count = 0;
+ u64 le_addr;
+
+ dev = (struct aac_dev *)scsicmd->device->host->hostdata;
+ // Get rid of old data
+ psg->count = 0;
+ psg->sg[0].addr[0] = 0;
+ psg->sg[0].addr[1] = 0;
+ psg->sg[0].count = 0;
+ if (scsicmd->use_sg) {
+ struct scatterlist *sg;
+ int i;
+ int sg_count;
+ sg = (struct scatterlist *) scsicmd->request_buffer;
+
+ sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg,
+ scsicmd->sc_data_direction);
+ psg->count = cpu_to_le32(sg_count);
+
+ byte_count = 0;
+
+ for (i = 0; i < sg_count; i++) {
+ le_addr = cpu_to_le64(sg_dma_address(sg));
+ psg->sg[i].addr[1] = (u32)(le_addr>>32);
+ psg->sg[i].addr[0] = (u32)(le_addr & 0xffffffff);
+ psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+ byte_count += sg_dma_len(sg);
+ sg++;
+ }
+ /* hba wants the size to be exact */
+ if(byte_count > scsicmd->request_bufflen){
+ psg->sg[i-1].count -= (byte_count - scsicmd->request_bufflen);
+ byte_count = scsicmd->request_bufflen;
+ }
+ /* Check for command underflow */
+ if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+ }
+ else if(scsicmd->request_bufflen) {
+ dma_addr_t addr;
+ addr = pci_map_single(dev->pdev,
+ scsicmd->request_buffer,
+ scsicmd->request_bufflen,
+ scsicmd->sc_data_direction);
+ psg->count = cpu_to_le32(1);
+ le_addr = cpu_to_le64(addr);
+ psg->sg[0].addr[1] = (u32)(le_addr>>32);
+ psg->sg[0].addr[0] = (u32)(le_addr & 0xffffffff);
+ psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen);
+ scsicmd->SCp.dma_handle = addr;
+ byte_count = scsicmd->request_bufflen;
+ }
+ return byte_count;
+}
+
+#ifdef AAC_DETAILED_STATUS_INFO
+
+struct aac_srb_status_info {
+ u32 status;
+ char *str;
+};
+
+
+static struct aac_srb_status_info srb_status_info[] = {
+ { SRB_STATUS_PENDING, "Pending Status"},
+ { SRB_STATUS_SUCCESS, "Success"},
+ { SRB_STATUS_ABORTED, "Aborted Command"},
+ { SRB_STATUS_ABORT_FAILED, "Abort Failed"},
+ { SRB_STATUS_ERROR, "Error Event"},
+ { SRB_STATUS_BUSY, "Device Busy"},
+ { SRB_STATUS_INVALID_REQUEST, "Invalid Request"},
+ { SRB_STATUS_INVALID_PATH_ID, "Invalid Path ID"},
+ { SRB_STATUS_NO_DEVICE, "No Device"},
+ { SRB_STATUS_TIMEOUT, "Timeout"},
+ { SRB_STATUS_SELECTION_TIMEOUT, "Selection Timeout"},
+ { SRB_STATUS_COMMAND_TIMEOUT, "Command Timeout"},
+ { SRB_STATUS_MESSAGE_REJECTED, "Message Rejected"},
+ { SRB_STATUS_BUS_RESET, "Bus Reset"},
+ { SRB_STATUS_PARITY_ERROR, "Parity Error"},
+ { SRB_STATUS_REQUEST_SENSE_FAILED,"Request Sense Failed"},
+ { SRB_STATUS_NO_HBA, "No HBA"},
+ { SRB_STATUS_DATA_OVERRUN, "Data Overrun/Data Underrun"},
+ { SRB_STATUS_UNEXPECTED_BUS_FREE,"Unexpected Bus Free"},
+ { SRB_STATUS_PHASE_SEQUENCE_FAILURE,"Phase Error"},
+ { SRB_STATUS_BAD_SRB_BLOCK_LENGTH,"Bad Srb Block Length"},
+ { SRB_STATUS_REQUEST_FLUSHED, "Request Flushed"},
+ { SRB_STATUS_DELAYED_RETRY, "Delayed Retry"},
+ { SRB_STATUS_INVALID_LUN, "Invalid LUN"},
+ { SRB_STATUS_INVALID_TARGET_ID, "Invalid TARGET ID"},
+ { SRB_STATUS_BAD_FUNCTION, "Bad Function"},
+ { SRB_STATUS_ERROR_RECOVERY, "Error Recovery"},
+ { SRB_STATUS_NOT_STARTED, "Not Started"},
+ { SRB_STATUS_NOT_IN_USE, "Not In Use"},
+ { SRB_STATUS_FORCE_ABORT, "Force Abort"},
+ { SRB_STATUS_DOMAIN_VALIDATION_FAIL,"Domain Validation Failure"},
+ { 0xff, "Unknown Error"}
+};
+
+char *aac_get_status_string(u32 status)
+{
+ int i;
+
+ for(i=0; i < (sizeof(srb_status_info)/sizeof(struct aac_srb_status_info)); i++ ){
+ if(srb_status_info[i].status == status){
+ return srb_status_info[i].str;
+ }
+ }
+
+ return "Bad Status Code";
+}
+
+#endif
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
new file mode 100644
index 000000000000..700d90331c1c
--- /dev/null
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -0,0 +1,1623 @@
+#if (!defined(dprintk))
+# define dprintk(x)
+#endif
+
+/*------------------------------------------------------------------------------
+ * D E F I N E S
+ *----------------------------------------------------------------------------*/
+
+#define MAXIMUM_NUM_CONTAINERS 32
+
+#define AAC_NUM_FIB (256 + 64)
+#define AAC_NUM_IO_FIB 100
+
+#define AAC_MAX_LUN (8)
+
+#define AAC_MAX_HOSTPHYSMEMPAGES (0xfffff)
+
+/*
+ * These macros convert from physical channels to virtual channels
+ */
+#define CONTAINER_CHANNEL (0)
+#define ID_LUN_TO_CONTAINER(id, lun) (id)
+#define CONTAINER_TO_CHANNEL(cont) (CONTAINER_CHANNEL)
+#define CONTAINER_TO_ID(cont) (cont)
+#define CONTAINER_TO_LUN(cont) (0)
+
+#define aac_phys_to_logical(x) (x+1)
+#define aac_logical_to_phys(x) (x?x-1:0)
+
+/* #define AAC_DETAILED_STATUS_INFO */
+
+struct diskparm
+{
+ int heads;
+ int sectors;
+ int cylinders;
+};
+
+
+/*
+ * DON'T CHANGE THE ORDER, this is set by the firmware
+ */
+
+#define CT_NONE 0
+#define CT_VOLUME 1
+#define CT_MIRROR 2
+#define CT_STRIPE 3
+#define CT_RAID5 4
+#define CT_SSRW 5
+#define CT_SSRO 6
+#define CT_MORPH 7
+#define CT_PASSTHRU 8
+#define CT_RAID4 9
+#define CT_RAID10 10 /* stripe of mirror */
+#define CT_RAID00 11 /* stripe of stripe */
+#define CT_VOLUME_OF_MIRRORS 12 /* volume of mirror */
+#define CT_PSEUDO_RAID 13 /* really raid4 */
+#define CT_LAST_VOLUME_TYPE 14
+#define CT_OK 218
+
+/*
+ * Types of objects addressable in some fashion by the client.
+ * This is a superset of those objects handled just by the filesystem
+ * and includes "raw" objects that an administrator would use to
+ * configure containers and filesystems.
+ */
+
+#define FT_REG 1 /* regular file */
+#define FT_DIR 2 /* directory */
+#define FT_BLK 3 /* "block" device - reserved */
+#define FT_CHR 4 /* "character special" device - reserved */
+#define FT_LNK 5 /* symbolic link */
+#define FT_SOCK 6 /* socket */
+#define FT_FIFO 7 /* fifo */
+#define FT_FILESYS 8 /* ADAPTEC's "FSA"(tm) filesystem */
+#define FT_DRIVE 9 /* physical disk - addressable in scsi by bus/id/lun */
+#define FT_SLICE 10 /* virtual disk - raw volume - slice */
+#define FT_PARTITION 11 /* FSA partition - carved out of a slice - building block for containers */
+#define FT_VOLUME 12 /* Container - Volume Set */
+#define FT_STRIPE 13 /* Container - Stripe Set */
+#define FT_MIRROR 14 /* Container - Mirror Set */
+#define FT_RAID5 15 /* Container - Raid 5 Set */
+#define FT_DATABASE 16 /* Storage object with "foreign" content manager */
+
+/*
+ * Host side memory scatter gather list
+ * Used by the adapter for read, write, and readdirplus operations
+ * We have separate 32 and 64 bit version because even
+ * on 64 bit systems not all cards support the 64 bit version
+ */
+struct sgentry {
+ u32 addr; /* 32-bit address. */
+ u32 count; /* Length. */
+};
+
+struct sgentry64 {
+ u32 addr[2]; /* 64-bit addr. 2 pieces for data alignment */
+ u32 count; /* Length. */
+};
+
+/*
+ * SGMAP
+ *
+ * This is the SGMAP structure for all commands that use
+ * 32-bit addressing.
+ */
+
+struct sgmap {
+ u32 count;
+ struct sgentry sg[1];
+};
+
+struct sgmap64 {
+ u32 count;
+ struct sgentry64 sg[1];
+};
+
+struct creation_info
+{
+ u8 buildnum; /* e.g., 588 */
+ u8 usec; /* e.g., 588 */
+ u8 via; /* e.g., 1 = FSU,
+ * 2 = API
+ */
+ u8 year; /* e.g., 1997 = 97 */
+ u32 date; /*
+ * unsigned Month :4; // 1 - 12
+ * unsigned Day :6; // 1 - 32
+ * unsigned Hour :6; // 0 - 23
+ * unsigned Minute :6; // 0 - 60
+ * unsigned Second :6; // 0 - 60
+ */
+ u32 serial[2]; /* e.g., 0x1DEADB0BFAFAF001 */
+};
+
+
+/*
+ * Define all the constants needed for the communication interface
+ */
+
+/*
+ * Define how many queue entries each queue will have and the total
+ * number of entries for the entire communication interface. Also define
+ * how many queues we support.
+ *
+ * This has to match the controller
+ */
+
+#define NUMBER_OF_COMM_QUEUES 8 // 4 command; 4 response
+#define HOST_HIGH_CMD_ENTRIES 4
+#define HOST_NORM_CMD_ENTRIES 8
+#define ADAP_HIGH_CMD_ENTRIES 4
+#define ADAP_NORM_CMD_ENTRIES 512
+#define HOST_HIGH_RESP_ENTRIES 4
+#define HOST_NORM_RESP_ENTRIES 512
+#define ADAP_HIGH_RESP_ENTRIES 4
+#define ADAP_NORM_RESP_ENTRIES 8
+
+#define TOTAL_QUEUE_ENTRIES \
+ (HOST_NORM_CMD_ENTRIES + HOST_HIGH_CMD_ENTRIES + ADAP_NORM_CMD_ENTRIES + ADAP_HIGH_CMD_ENTRIES + \
+ HOST_NORM_RESP_ENTRIES + HOST_HIGH_RESP_ENTRIES + ADAP_NORM_RESP_ENTRIES + ADAP_HIGH_RESP_ENTRIES)
+
+
+/*
+ * Set the queues on a 16 byte alignment
+ */
+
+#define QUEUE_ALIGNMENT 16
+
+/*
+ * The queue headers define the Communication Region queues. These
+ * are physically contiguous and accessible by both the adapter and the
+ * host. Even though all queue headers are in the same contiguous block
+ * they will be represented as individual units in the data structures.
+ */
+
+struct aac_entry {
+ u32 size; /* Size in bytes of Fib which this QE points to */
+ u32 addr; /* Receiver address of the FIB */
+};
+
+/*
+ * The adapter assumes the ProducerIndex and ConsumerIndex are grouped
+ * adjacently and in that order.
+ */
+
+struct aac_qhdr {
+ u64 header_addr; /* Address to hand the adapter to access to this queue head */
+ u32 *producer; /* The producer index for this queue (host address) */
+ u32 *consumer; /* The consumer index for this queue (host address) */
+};
+
+/*
+ * Define all the events which the adapter would like to notify
+ * the host of.
+ */
+
+#define HostNormCmdQue 1 /* Change in host normal priority command queue */
+#define HostHighCmdQue 2 /* Change in host high priority command queue */
+#define HostNormRespQue 3 /* Change in host normal priority response queue */
+#define HostHighRespQue 4 /* Change in host high priority response queue */
+#define AdapNormRespNotFull 5
+#define AdapHighRespNotFull 6
+#define AdapNormCmdNotFull 7
+#define AdapHighCmdNotFull 8
+#define SynchCommandComplete 9
+#define AdapInternalError 0xfe /* The adapter detected an internal error shutting down */
+
+/*
+ * Define all the events the host wishes to notify the
+ * adapter of. The first four values much match the Qid the
+ * corresponding queue.
+ */
+
+#define AdapNormCmdQue 2
+#define AdapHighCmdQue 3
+#define AdapNormRespQue 6
+#define AdapHighRespQue 7
+#define HostShutdown 8
+#define HostPowerFail 9
+#define FatalCommError 10
+#define HostNormRespNotFull 11
+#define HostHighRespNotFull 12
+#define HostNormCmdNotFull 13
+#define HostHighCmdNotFull 14
+#define FastIo 15
+#define AdapPrintfDone 16
+
+/*
+ * Define all the queues that the adapter and host use to communicate
+ * Number them to match the physical queue layout.
+ */
+
+enum aac_queue_types {
+ HostNormCmdQueue = 0, /* Adapter to host normal priority command traffic */
+ HostHighCmdQueue, /* Adapter to host high priority command traffic */
+ AdapNormCmdQueue, /* Host to adapter normal priority command traffic */
+ AdapHighCmdQueue, /* Host to adapter high priority command traffic */
+ HostNormRespQueue, /* Adapter to host normal priority response traffic */
+ HostHighRespQueue, /* Adapter to host high priority response traffic */
+ AdapNormRespQueue, /* Host to adapter normal priority response traffic */
+ AdapHighRespQueue /* Host to adapter high priority response traffic */
+};
+
+/*
+ * Assign type values to the FSA communication data structures
+ */
+
+#define FIB_MAGIC 0x0001
+
+/*
+ * Define the priority levels the FSA communication routines support.
+ */
+
+#define FsaNormal 1
+#define FsaHigh 2
+
+/*
+ * Define the FIB. The FIB is the where all the requested data and
+ * command information are put to the application on the FSA adapter.
+ */
+
+struct aac_fibhdr {
+ u32 XferState; // Current transfer state for this CCB
+ u16 Command; // Routing information for the destination
+ u8 StructType; // Type FIB
+ u8 Flags; // Flags for FIB
+ u16 Size; // Size of this FIB in bytes
+ u16 SenderSize; // Size of the FIB in the sender (for response sizing)
+ u32 SenderFibAddress; // Host defined data in the FIB
+ u32 ReceiverFibAddress; // Logical address of this FIB for the adapter
+ u32 SenderData; // Place holder for the sender to store data
+ union {
+ struct {
+ u32 _ReceiverTimeStart; // Timestamp for receipt of fib
+ u32 _ReceiverTimeDone; // Timestamp for completion of fib
+ } _s;
+ } _u;
+};
+
+#define FIB_DATA_SIZE_IN_BYTES (512 - sizeof(struct aac_fibhdr))
+
+
+struct hw_fib {
+ struct aac_fibhdr header;
+ u8 data[FIB_DATA_SIZE_IN_BYTES]; // Command specific data
+};
+
+/*
+ * FIB commands
+ */
+
+#define TestCommandResponse 1
+#define TestAdapterCommand 2
+/*
+ * Lowlevel and comm commands
+ */
+#define LastTestCommand 100
+#define ReinitHostNormCommandQueue 101
+#define ReinitHostHighCommandQueue 102
+#define ReinitHostHighRespQueue 103
+#define ReinitHostNormRespQueue 104
+#define ReinitAdapNormCommandQueue 105
+#define ReinitAdapHighCommandQueue 107
+#define ReinitAdapHighRespQueue 108
+#define ReinitAdapNormRespQueue 109
+#define InterfaceShutdown 110
+#define DmaCommandFib 120
+#define StartProfile 121
+#define TermProfile 122
+#define SpeedTest 123
+#define TakeABreakPt 124
+#define RequestPerfData 125
+#define SetInterruptDefTimer 126
+#define SetInterruptDefCount 127
+#define GetInterruptDefStatus 128
+#define LastCommCommand 129
+/*
+ * Filesystem commands
+ */
+#define NuFileSystem 300
+#define UFS 301
+#define HostFileSystem 302
+#define LastFileSystemCommand 303
+/*
+ * Container Commands
+ */
+#define ContainerCommand 500
+#define ContainerCommand64 501
+/*
+ * Cluster Commands
+ */
+#define ClusterCommand 550
+/*
+ * Scsi Port commands (scsi passthrough)
+ */
+#define ScsiPortCommand 600
+#define ScsiPortCommand64 601
+/*
+ * Misc house keeping and generic adapter initiated commands
+ */
+#define AifRequest 700
+#define CheckRevision 701
+#define FsaHostShutdown 702
+#define RequestAdapterInfo 703
+#define IsAdapterPaused 704
+#define SendHostTime 705
+#define LastMiscCommand 706
+
+//
+// Commands that will target the failover level on the FSA adapter
+//
+
+enum fib_xfer_state {
+ HostOwned = (1<<0),
+ AdapterOwned = (1<<1),
+ FibInitialized = (1<<2),
+ FibEmpty = (1<<3),
+ AllocatedFromPool = (1<<4),
+ SentFromHost = (1<<5),
+ SentFromAdapter = (1<<6),
+ ResponseExpected = (1<<7),
+ NoResponseExpected = (1<<8),
+ AdapterProcessed = (1<<9),
+ HostProcessed = (1<<10),
+ HighPriority = (1<<11),
+ NormalPriority = (1<<12),
+ Async = (1<<13),
+ AsyncIo = (1<<13), // rpbfix: remove with new regime
+ PageFileIo = (1<<14), // rpbfix: remove with new regime
+ ShutdownRequest = (1<<15),
+ LazyWrite = (1<<16), // rpbfix: remove with new regime
+ AdapterMicroFib = (1<<17),
+ BIOSFibPath = (1<<18),
+ FastResponseCapable = (1<<19),
+ ApiFib = (1<<20) // Its an API Fib.
+};
+
+/*
+ * The following defines needs to be updated any time there is an
+ * incompatible change made to the aac_init structure.
+ */
+
+#define ADAPTER_INIT_STRUCT_REVISION 3
+
+struct aac_init
+{
+ u32 InitStructRevision;
+ u32 MiniPortRevision;
+ u32 fsrev;
+ u32 CommHeaderAddress;
+ u32 FastIoCommAreaAddress;
+ u32 AdapterFibsPhysicalAddress;
+ u32 AdapterFibsVirtualAddress;
+ u32 AdapterFibsSize;
+ u32 AdapterFibAlign;
+ u32 printfbuf;
+ u32 printfbufsiz;
+ u32 HostPhysMemPages; // number of 4k pages of host physical memory
+ u32 HostElapsedSeconds; // number of seconds since 1970.
+};
+
+enum aac_log_level {
+ LOG_AAC_INIT = 10,
+ LOG_AAC_INFORMATIONAL = 20,
+ LOG_AAC_WARNING = 30,
+ LOG_AAC_LOW_ERROR = 40,
+ LOG_AAC_MEDIUM_ERROR = 50,
+ LOG_AAC_HIGH_ERROR = 60,
+ LOG_AAC_PANIC = 70,
+ LOG_AAC_DEBUG = 80,
+ LOG_AAC_WINDBG_PRINT = 90
+};
+
+#define FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT 0x030b
+#define FSAFS_NTC_FIB_CONTEXT 0x030c
+
+struct aac_dev;
+
+struct adapter_ops
+{
+ void (*adapter_interrupt)(struct aac_dev *dev);
+ void (*adapter_notify)(struct aac_dev *dev, u32 event);
+ int (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 *status);
+ int (*adapter_check_health)(struct aac_dev *dev);
+};
+
+/*
+ * Define which interrupt handler needs to be installed
+ */
+
+struct aac_driver_ident
+{
+ int (*init)(struct aac_dev *dev);
+ char * name;
+ char * vname;
+ char * model;
+ u16 channels;
+ int quirks;
+};
+/*
+ * Some adapter firmware needs communication memory
+ * below 2gig. This tells the init function to set the
+ * dma mask such that fib memory will be allocated where the
+ * adapter firmware can get to it.
+ */
+#define AAC_QUIRK_31BIT 0x0001
+
+/*
+ * Some adapter firmware, when the raid card's cache is turned off, can not
+ * split up scatter gathers in order to deal with the limits of the
+ * underlying CHIM. This limit is 34 scatter gather elements.
+ */
+#define AAC_QUIRK_34SG 0x0002
+
+/*
+ * This adapter is a slave (no Firmware)
+ */
+#define AAC_QUIRK_SLAVE 0x0004
+
+/*
+ * This adapter is a master.
+ */
+#define AAC_QUIRK_MASTER 0x0008
+
+/*
+ * The adapter interface specs all queues to be located in the same
+ * physically contigous block. The host structure that defines the
+ * commuication queues will assume they are each a separate physically
+ * contigous memory region that will support them all being one big
+ * contigous block.
+ * There is a command and response queue for each level and direction of
+ * commuication. These regions are accessed by both the host and adapter.
+ */
+
+struct aac_queue {
+ u64 logical; /*address we give the adapter */
+ struct aac_entry *base; /*system virtual address */
+ struct aac_qhdr headers; /*producer,consumer q headers*/
+ u32 entries; /*Number of queue entries */
+ wait_queue_head_t qfull; /*Event to wait on if q full */
+ wait_queue_head_t cmdready; /*Cmd ready from the adapter */
+ /* This is only valid for adapter to host command queues. */
+ spinlock_t *lock; /* Spinlock for this queue must take this lock before accessing the lock */
+ spinlock_t lockdata; /* Actual lock (used only on one side of the lock) */
+ unsigned long SavedIrql; /* Previous IRQL when the spin lock is taken */
+ u32 padding; /* Padding - FIXME - can remove I believe */
+ struct list_head cmdq; /* A queue of FIBs which need to be prcessed by the FS thread. This is */
+ /* only valid for command queues which receive entries from the adapter. */
+ struct list_head pendingq; /* A queue of outstanding fib's to the adapter. */
+ u32 numpending; /* Number of entries on outstanding queue. */
+ struct aac_dev * dev; /* Back pointer to adapter structure */
+};
+
+/*
+ * Message queues. The order here is important, see also the
+ * queue type ordering
+ */
+
+struct aac_queue_block
+{
+ struct aac_queue queue[8];
+};
+
+/*
+ * SaP1 Message Unit Registers
+ */
+
+struct sa_drawbridge_CSR {
+ /* Offset | Name */
+ __le32 reserved[10]; /* 00h-27h | Reserved */
+ u8 LUT_Offset; /* 28h | Lookup Table Offset */
+ u8 reserved1[3]; /* 29h-2bh | Reserved */
+ __le32 LUT_Data; /* 2ch | Looup Table Data */
+ __le32 reserved2[26]; /* 30h-97h | Reserved */
+ __le16 PRICLEARIRQ; /* 98h | Primary Clear Irq */
+ __le16 SECCLEARIRQ; /* 9ah | Secondary Clear Irq */
+ __le16 PRISETIRQ; /* 9ch | Primary Set Irq */
+ __le16 SECSETIRQ; /* 9eh | Secondary Set Irq */
+ __le16 PRICLEARIRQMASK;/* a0h | Primary Clear Irq Mask */
+ __le16 SECCLEARIRQMASK;/* a2h | Secondary Clear Irq Mask */
+ __le16 PRISETIRQMASK; /* a4h | Primary Set Irq Mask */
+ __le16 SECSETIRQMASK; /* a6h | Secondary Set Irq Mask */
+ __le32 MAILBOX0; /* a8h | Scratchpad 0 */
+ __le32 MAILBOX1; /* ach | Scratchpad 1 */
+ __le32 MAILBOX2; /* b0h | Scratchpad 2 */
+ __le32 MAILBOX3; /* b4h | Scratchpad 3 */
+ __le32 MAILBOX4; /* b8h | Scratchpad 4 */
+ __le32 MAILBOX5; /* bch | Scratchpad 5 */
+ __le32 MAILBOX6; /* c0h | Scratchpad 6 */
+ __le32 MAILBOX7; /* c4h | Scratchpad 7 */
+ __le32 ROM_Setup_Data; /* c8h | Rom Setup and Data */
+ __le32 ROM_Control_Addr;/* cch | Rom Control and Address */
+ __le32 reserved3[12]; /* d0h-ffh | reserved */
+ __le32 LUT[64]; /* 100h-1ffh | Lookup Table Entries */
+};
+
+#define Mailbox0 SaDbCSR.MAILBOX0
+#define Mailbox1 SaDbCSR.MAILBOX1
+#define Mailbox2 SaDbCSR.MAILBOX2
+#define Mailbox3 SaDbCSR.MAILBOX3
+#define Mailbox4 SaDbCSR.MAILBOX4
+#define Mailbox5 SaDbCSR.MAILBOX5
+#define Mailbox7 SaDbCSR.MAILBOX7
+
+#define DoorbellReg_p SaDbCSR.PRISETIRQ
+#define DoorbellReg_s SaDbCSR.SECSETIRQ
+#define DoorbellClrReg_p SaDbCSR.PRICLEARIRQ
+
+
+#define DOORBELL_0 0x0001
+#define DOORBELL_1 0x0002
+#define DOORBELL_2 0x0004
+#define DOORBELL_3 0x0008
+#define DOORBELL_4 0x0010
+#define DOORBELL_5 0x0020
+#define DOORBELL_6 0x0040
+
+
+#define PrintfReady DOORBELL_5
+#define PrintfDone DOORBELL_5
+
+struct sa_registers {
+ struct sa_drawbridge_CSR SaDbCSR; /* 98h - c4h */
+};
+
+
+#define Sa_MINIPORT_REVISION 1
+
+#define sa_readw(AEP, CSR) readl(&((AEP)->regs.sa->CSR))
+#define sa_readl(AEP, CSR) readl(&((AEP)->regs.sa->CSR))
+#define sa_writew(AEP, CSR, value) writew(value, &((AEP)->regs.sa->CSR))
+#define sa_writel(AEP, CSR, value) writel(value, &((AEP)->regs.sa->CSR))
+
+/*
+ * Rx Message Unit Registers
+ */
+
+struct rx_mu_registers {
+ /* Local | PCI*| Name */
+ __le32 ARSR; /* 1300h | 00h | APIC Register Select Register */
+ __le32 reserved0; /* 1304h | 04h | Reserved */
+ __le32 AWR; /* 1308h | 08h | APIC Window Register */
+ __le32 reserved1; /* 130Ch | 0Ch | Reserved */
+ __le32 IMRx[2]; /* 1310h | 10h | Inbound Message Registers */
+ __le32 OMRx[2]; /* 1318h | 18h | Outbound Message Registers */
+ __le32 IDR; /* 1320h | 20h | Inbound Doorbell Register */
+ __le32 IISR; /* 1324h | 24h | Inbound Interrupt
+ Status Register */
+ __le32 IIMR; /* 1328h | 28h | Inbound Interrupt
+ Mask Register */
+ __le32 ODR; /* 132Ch | 2Ch | Outbound Doorbell Register */
+ __le32 OISR; /* 1330h | 30h | Outbound Interrupt
+ Status Register */
+ __le32 OIMR; /* 1334h | 34h | Outbound Interrupt
+ Mask Register */
+ /* * Must access through ATU Inbound
+ Translation Window */
+};
+
+struct rx_inbound {
+ __le32 Mailbox[8];
+};
+
+#define InboundMailbox0 IndexRegs.Mailbox[0]
+#define InboundMailbox1 IndexRegs.Mailbox[1]
+#define InboundMailbox2 IndexRegs.Mailbox[2]
+#define InboundMailbox3 IndexRegs.Mailbox[3]
+#define InboundMailbox4 IndexRegs.Mailbox[4]
+#define InboundMailbox5 IndexRegs.Mailbox[5]
+#define InboundMailbox6 IndexRegs.Mailbox[6]
+
+#define INBOUNDDOORBELL_0 0x00000001
+#define INBOUNDDOORBELL_1 0x00000002
+#define INBOUNDDOORBELL_2 0x00000004
+#define INBOUNDDOORBELL_3 0x00000008
+#define INBOUNDDOORBELL_4 0x00000010
+#define INBOUNDDOORBELL_5 0x00000020
+#define INBOUNDDOORBELL_6 0x00000040
+
+#define OUTBOUNDDOORBELL_0 0x00000001
+#define OUTBOUNDDOORBELL_1 0x00000002
+#define OUTBOUNDDOORBELL_2 0x00000004
+#define OUTBOUNDDOORBELL_3 0x00000008
+#define OUTBOUNDDOORBELL_4 0x00000010
+
+#define InboundDoorbellReg MUnit.IDR
+#define OutboundDoorbellReg MUnit.ODR
+
+struct rx_registers {
+ struct rx_mu_registers MUnit; /* 1300h - 1334h */
+ __le32 reserved1[6]; /* 1338h - 134ch */
+ struct rx_inbound IndexRegs;
+};
+
+#define rx_readb(AEP, CSR) readb(&((AEP)->regs.rx->CSR))
+#define rx_readl(AEP, CSR) readl(&((AEP)->regs.rx->CSR))
+#define rx_writeb(AEP, CSR, value) writeb(value, &((AEP)->regs.rx->CSR))
+#define rx_writel(AEP, CSR, value) writel(value, &((AEP)->regs.rx->CSR))
+
+/*
+ * Rkt Message Unit Registers (same as Rx, except a larger reserve region)
+ */
+
+#define rkt_mu_registers rx_mu_registers
+#define rkt_inbound rx_inbound
+
+struct rkt_registers {
+ struct rkt_mu_registers MUnit; /* 1300h - 1334h */
+ __le32 reserved1[1010]; /* 1338h - 22fch */
+ struct rkt_inbound IndexRegs; /* 2300h - */
+};
+
+#define rkt_readb(AEP, CSR) readb(&((AEP)->regs.rkt->CSR))
+#define rkt_readl(AEP, CSR) readl(&((AEP)->regs.rkt->CSR))
+#define rkt_writeb(AEP, CSR, value) writeb(value, &((AEP)->regs.rkt->CSR))
+#define rkt_writel(AEP, CSR, value) writel(value, &((AEP)->regs.rkt->CSR))
+
+struct fib;
+
+typedef void (*fib_callback)(void *ctxt, struct fib *fibctx);
+
+struct aac_fib_context {
+ s16 type; // used for verification of structure
+ s16 size;
+ u32 unique; // unique value representing this context
+ ulong jiffies; // used for cleanup - dmb changed to ulong
+ struct list_head next; // used to link context's into a linked list
+ struct semaphore wait_sem; // this is used to wait for the next fib to arrive.
+ int wait; // Set to true when thread is in WaitForSingleObject
+ unsigned long count; // total number of FIBs on FibList
+ struct list_head fib_list; // this holds fibs and their attachd hw_fibs
+};
+
+struct sense_data {
+ u8 error_code; /* 70h (current errors), 71h(deferred errors) */
+ u8 valid:1; /* A valid bit of one indicates that the information */
+ /* field contains valid information as defined in the
+ * SCSI-2 Standard.
+ */
+ u8 segment_number; /* Only used for COPY, COMPARE, or COPY AND VERIFY Commands */
+ u8 sense_key:4; /* Sense Key */
+ u8 reserved:1;
+ u8 ILI:1; /* Incorrect Length Indicator */
+ u8 EOM:1; /* End Of Medium - reserved for random access devices */
+ u8 filemark:1; /* Filemark - reserved for random access devices */
+
+ u8 information[4]; /* for direct-access devices, contains the unsigned
+ * logical block address or residue associated with
+ * the sense key
+ */
+ u8 add_sense_len; /* number of additional sense bytes to follow this field */
+ u8 cmnd_info[4]; /* not used */
+ u8 ASC; /* Additional Sense Code */
+ u8 ASCQ; /* Additional Sense Code Qualifier */
+ u8 FRUC; /* Field Replaceable Unit Code - not used */
+ u8 bit_ptr:3; /* indicates which byte of the CDB or parameter data
+ * was in error
+ */
+ u8 BPV:1; /* bit pointer valid (BPV): 1- indicates that
+ * the bit_ptr field has valid value
+ */
+ u8 reserved2:2;
+ u8 CD:1; /* command data bit: 1- illegal parameter in CDB.
+ * 0- illegal parameter in data.
+ */
+ u8 SKSV:1;
+ u8 field_ptr[2]; /* byte of the CDB or parameter data in error */
+};
+
+struct fsa_dev_info {
+ u64 last;
+ u64 size;
+ u32 type;
+ u16 queue_depth;
+ u8 valid;
+ u8 ro;
+ u8 locked;
+ u8 deleted;
+ char devname[8];
+ struct sense_data sense_data;
+};
+
+struct fib {
+ void *next; /* this is used by the allocator */
+ s16 type;
+ s16 size;
+ /*
+ * The Adapter that this I/O is destined for.
+ */
+ struct aac_dev *dev;
+ /*
+ * This is the event the sendfib routine will wait on if the
+ * caller did not pass one and this is synch io.
+ */
+ struct semaphore event_wait;
+ spinlock_t event_lock;
+
+ u32 done; /* gets set to 1 when fib is complete */
+ fib_callback callback;
+ void *callback_data;
+ u32 flags; // u32 dmb was ulong
+ /*
+ * The following is used to put this fib context onto the
+ * Outstanding I/O queue.
+ */
+ struct list_head queue;
+ /*
+ * And for the internal issue/reply queues (we may be able
+ * to merge these two)
+ */
+ struct list_head fiblink;
+ void *data;
+ struct hw_fib *hw_fib; /* Actual shared object */
+ dma_addr_t hw_fib_pa; /* physical address of hw_fib*/
+};
+
+/*
+ * Adapter Information Block
+ *
+ * This is returned by the RequestAdapterInfo block
+ */
+
+struct aac_adapter_info
+{
+ u32 platform;
+ u32 cpu;
+ u32 subcpu;
+ u32 clock;
+ u32 execmem;
+ u32 buffermem;
+ u32 totalmem;
+ u32 kernelrev;
+ u32 kernelbuild;
+ u32 monitorrev;
+ u32 monitorbuild;
+ u32 hwrev;
+ u32 hwbuild;
+ u32 biosrev;
+ u32 biosbuild;
+ u32 cluster;
+ u32 clusterchannelmask;
+ u32 serial[2];
+ u32 battery;
+ u32 options;
+ u32 OEM;
+};
+
+/*
+ * Battery platforms
+ */
+#define AAC_BAT_REQ_PRESENT (1)
+#define AAC_BAT_REQ_NOTPRESENT (2)
+#define AAC_BAT_OPT_PRESENT (3)
+#define AAC_BAT_OPT_NOTPRESENT (4)
+#define AAC_BAT_NOT_SUPPORTED (5)
+/*
+ * cpu types
+ */
+#define AAC_CPU_SIMULATOR (1)
+#define AAC_CPU_I960 (2)
+#define AAC_CPU_STRONGARM (3)
+
+/*
+ * Supported Options
+ */
+#define AAC_OPT_SNAPSHOT cpu_to_le32(1)
+#define AAC_OPT_CLUSTERS cpu_to_le32(1<<1)
+#define AAC_OPT_WRITE_CACHE cpu_to_le32(1<<2)
+#define AAC_OPT_64BIT_DATA cpu_to_le32(1<<3)
+#define AAC_OPT_HOST_TIME_FIB cpu_to_le32(1<<4)
+#define AAC_OPT_RAID50 cpu_to_le32(1<<5)
+#define AAC_OPT_4GB_WINDOW cpu_to_le32(1<<6)
+#define AAC_OPT_SCSI_UPGRADEABLE cpu_to_le32(1<<7)
+#define AAC_OPT_SOFT_ERR_REPORT cpu_to_le32(1<<8)
+#define AAC_OPT_SUPPORTED_RECONDITION cpu_to_le32(1<<9)
+#define AAC_OPT_SGMAP_HOST64 cpu_to_le32(1<<10)
+#define AAC_OPT_ALARM cpu_to_le32(1<<11)
+#define AAC_OPT_NONDASD cpu_to_le32(1<<12)
+#define AAC_OPT_SCSI_MANAGED cpu_to_le32(1<<13)
+#define AAC_OPT_RAID_SCSI_MODE cpu_to_le32(1<<14)
+#define AAC_OPT_SUPPLEMENT_ADAPTER_INFO cpu_to_le32(1<<16)
+#define AAC_OPT_NEW_COMM cpu_to_le32(1<<17)
+#define AAC_OPT_NEW_COMM_64 cpu_to_le32(1<<18)
+
+struct aac_dev
+{
+ struct list_head entry;
+ const char *name;
+ int id;
+
+ u16 irq_mask;
+ /*
+ * Map for 128 fib objects (64k)
+ */
+ dma_addr_t hw_fib_pa;
+ struct hw_fib *hw_fib_va;
+ struct hw_fib *aif_base_va;
+ /*
+ * Fib Headers
+ */
+ struct fib *fibs;
+
+ struct fib *free_fib;
+ struct fib *timeout_fib;
+ spinlock_t fib_lock;
+
+ struct aac_queue_block *queues;
+ /*
+ * The user API will use an IOCTL to register itself to receive
+ * FIBs from the adapter. The following list is used to keep
+ * track of all the threads that have requested these FIBs. The
+ * mutex is used to synchronize access to all data associated
+ * with the adapter fibs.
+ */
+ struct list_head fib_list;
+
+ struct adapter_ops a_ops;
+ unsigned long fsrev; /* Main driver's revision number */
+
+ struct aac_init *init; /* Holds initialization info to communicate with adapter */
+ dma_addr_t init_pa; /* Holds physical address of the init struct */
+
+ struct pci_dev *pdev; /* Our PCI interface */
+ void * printfbuf; /* pointer to buffer used for printf's from the adapter */
+ void * comm_addr; /* Base address of Comm area */
+ dma_addr_t comm_phys; /* Physical Address of Comm area */
+ size_t comm_size;
+
+ struct Scsi_Host *scsi_host_ptr;
+ int maximum_num_containers;
+ struct fsa_dev_info *fsa_dev;
+ pid_t thread_pid;
+ int cardtype;
+
+ /*
+ * The following is the device specific extension.
+ */
+ union
+ {
+ struct sa_registers __iomem *sa;
+ struct rx_registers __iomem *rx;
+ struct rkt_registers __iomem *rkt;
+ } regs;
+ u32 OIMR; /* Mask Register Cache */
+ /*
+ * AIF thread states
+ */
+ u32 aif_thread;
+ struct completion aif_completion;
+ struct aac_adapter_info adapter_info;
+ /* These are in adapter info but they are in the io flow so
+ * lets break them out so we don't have to do an AND to check them
+ */
+ u8 nondasd_support;
+ u8 dac_support;
+ u8 raid_scsi_mode;
+};
+
+#define aac_adapter_interrupt(dev) \
+ (dev)->a_ops.adapter_interrupt(dev)
+
+#define aac_adapter_notify(dev, event) \
+ (dev)->a_ops.adapter_notify(dev, event)
+
+
+#define aac_adapter_check_health(dev) \
+ (dev)->a_ops.adapter_check_health(dev)
+
+
+#define FIB_CONTEXT_FLAG_TIMED_OUT (0x00000001)
+
+/*
+ * Define the command values
+ */
+
+#define Null 0
+#define GetAttributes 1
+#define SetAttributes 2
+#define Lookup 3
+#define ReadLink 4
+#define Read 5
+#define Write 6
+#define Create 7
+#define MakeDirectory 8
+#define SymbolicLink 9
+#define MakeNode 10
+#define Removex 11
+#define RemoveDirectoryx 12
+#define Rename 13
+#define Link 14
+#define ReadDirectory 15
+#define ReadDirectoryPlus 16
+#define FileSystemStatus 17
+#define FileSystemInfo 18
+#define PathConfigure 19
+#define Commit 20
+#define Mount 21
+#define UnMount 22
+#define Newfs 23
+#define FsCheck 24
+#define FsSync 25
+#define SimReadWrite 26
+#define SetFileSystemStatus 27
+#define BlockRead 28
+#define BlockWrite 29
+#define NvramIoctl 30
+#define FsSyncWait 31
+#define ClearArchiveBit 32
+#define SetAcl 33
+#define GetAcl 34
+#define AssignAcl 35
+#define FaultInsertion 36 /* Fault Insertion Command */
+#define CrazyCache 37 /* Crazycache */
+
+#define MAX_FSACOMMAND_NUM 38
+
+
+/*
+ * Define the status returns. These are very unixlike although
+ * most are not in fact used
+ */
+
+#define ST_OK 0
+#define ST_PERM 1
+#define ST_NOENT 2
+#define ST_IO 5
+#define ST_NXIO 6
+#define ST_E2BIG 7
+#define ST_ACCES 13
+#define ST_EXIST 17
+#define ST_XDEV 18
+#define ST_NODEV 19
+#define ST_NOTDIR 20
+#define ST_ISDIR 21
+#define ST_INVAL 22
+#define ST_FBIG 27
+#define ST_NOSPC 28
+#define ST_ROFS 30
+#define ST_MLINK 31
+#define ST_WOULDBLOCK 35
+#define ST_NAMETOOLONG 63
+#define ST_NOTEMPTY 66
+#define ST_DQUOT 69
+#define ST_STALE 70
+#define ST_REMOTE 71
+#define ST_BADHANDLE 10001
+#define ST_NOT_SYNC 10002
+#define ST_BAD_COOKIE 10003
+#define ST_NOTSUPP 10004
+#define ST_TOOSMALL 10005
+#define ST_SERVERFAULT 10006
+#define ST_BADTYPE 10007
+#define ST_JUKEBOX 10008
+#define ST_NOTMOUNTED 10009
+#define ST_MAINTMODE 10010
+#define ST_STALEACL 10011
+
+/*
+ * On writes how does the client want the data written.
+ */
+
+#define CACHE_CSTABLE 1
+#define CACHE_UNSTABLE 2
+
+/*
+ * Lets the client know at which level the data was commited on
+ * a write request
+ */
+
+#define CMFILE_SYNCH_NVRAM 1
+#define CMDATA_SYNCH_NVRAM 2
+#define CMFILE_SYNCH 3
+#define CMDATA_SYNCH 4
+#define CMUNSTABLE 5
+
+struct aac_read
+{
+ u32 command;
+ u32 cid;
+ u32 block;
+ u32 count;
+ struct sgmap sg; // Must be last in struct because it is variable
+};
+
+struct aac_read64
+{
+ u32 command;
+ u16 cid;
+ u16 sector_count;
+ u32 block;
+ u16 pad;
+ u16 flags;
+ struct sgmap64 sg; // Must be last in struct because it is variable
+};
+
+struct aac_read_reply
+{
+ u32 status;
+ u32 count;
+};
+
+struct aac_write
+{
+ u32 command;
+ u32 cid;
+ u32 block;
+ u32 count;
+ u32 stable; // Not used
+ struct sgmap sg; // Must be last in struct because it is variable
+};
+
+struct aac_write64
+{
+ u32 command;
+ u16 cid;
+ u16 sector_count;
+ u32 block;
+ u16 pad;
+ u16 flags;
+ struct sgmap64 sg; // Must be last in struct because it is variable
+};
+struct aac_write_reply
+{
+ u32 status;
+ u32 count;
+ u32 committed;
+};
+
+#define CT_FLUSH_CACHE 129
+struct aac_synchronize {
+ u32 command; /* VM_ContainerConfig */
+ u32 type; /* CT_FLUSH_CACHE */
+ u32 cid;
+ u32 parm1;
+ u32 parm2;
+ u32 parm3;
+ u32 parm4;
+ u32 count; /* sizeof(((struct aac_synchronize_reply *)NULL)->data) */
+};
+
+struct aac_synchronize_reply {
+ u32 dummy0;
+ u32 dummy1;
+ u32 status; /* CT_OK */
+ u32 parm1;
+ u32 parm2;
+ u32 parm3;
+ u32 parm4;
+ u32 parm5;
+ u8 data[16];
+};
+
+struct aac_srb
+{
+ u32 function;
+ u32 channel;
+ u32 id;
+ u32 lun;
+ u32 timeout;
+ u32 flags;
+ u32 count; // Data xfer size
+ u32 retry_limit;
+ u32 cdb_size;
+ u8 cdb[16];
+ struct sgmap sg;
+};
+
+
+
+#define AAC_SENSE_BUFFERSIZE 30
+
+struct aac_srb_reply
+{
+ u32 status;
+ u32 srb_status;
+ u32 scsi_status;
+ u32 data_xfer_length;
+ u32 sense_data_size;
+ u8 sense_data[AAC_SENSE_BUFFERSIZE]; // Can this be SCSI_SENSE_BUFFERSIZE
+};
+/*
+ * SRB Flags
+ */
+#define SRB_NoDataXfer 0x0000
+#define SRB_DisableDisconnect 0x0004
+#define SRB_DisableSynchTransfer 0x0008
+#define SRB_BypassFrozenQueue 0x0010
+#define SRB_DisableAutosense 0x0020
+#define SRB_DataIn 0x0040
+#define SRB_DataOut 0x0080
+
+/*
+ * SRB Functions - set in aac_srb->function
+ */
+#define SRBF_ExecuteScsi 0x0000
+#define SRBF_ClaimDevice 0x0001
+#define SRBF_IO_Control 0x0002
+#define SRBF_ReceiveEvent 0x0003
+#define SRBF_ReleaseQueue 0x0004
+#define SRBF_AttachDevice 0x0005
+#define SRBF_ReleaseDevice 0x0006
+#define SRBF_Shutdown 0x0007
+#define SRBF_Flush 0x0008
+#define SRBF_AbortCommand 0x0010
+#define SRBF_ReleaseRecovery 0x0011
+#define SRBF_ResetBus 0x0012
+#define SRBF_ResetDevice 0x0013
+#define SRBF_TerminateIO 0x0014
+#define SRBF_FlushQueue 0x0015
+#define SRBF_RemoveDevice 0x0016
+#define SRBF_DomainValidation 0x0017
+
+/*
+ * SRB SCSI Status - set in aac_srb->scsi_status
+ */
+#define SRB_STATUS_PENDING 0x00
+#define SRB_STATUS_SUCCESS 0x01
+#define SRB_STATUS_ABORTED 0x02
+#define SRB_STATUS_ABORT_FAILED 0x03
+#define SRB_STATUS_ERROR 0x04
+#define SRB_STATUS_BUSY 0x05
+#define SRB_STATUS_INVALID_REQUEST 0x06
+#define SRB_STATUS_INVALID_PATH_ID 0x07
+#define SRB_STATUS_NO_DEVICE 0x08
+#define SRB_STATUS_TIMEOUT 0x09
+#define SRB_STATUS_SELECTION_TIMEOUT 0x0A
+#define SRB_STATUS_COMMAND_TIMEOUT 0x0B
+#define SRB_STATUS_MESSAGE_REJECTED 0x0D
+#define SRB_STATUS_BUS_RESET 0x0E
+#define SRB_STATUS_PARITY_ERROR 0x0F
+#define SRB_STATUS_REQUEST_SENSE_FAILED 0x10
+#define SRB_STATUS_NO_HBA 0x11
+#define SRB_STATUS_DATA_OVERRUN 0x12
+#define SRB_STATUS_UNEXPECTED_BUS_FREE 0x13
+#define SRB_STATUS_PHASE_SEQUENCE_FAILURE 0x14
+#define SRB_STATUS_BAD_SRB_BLOCK_LENGTH 0x15
+#define SRB_STATUS_REQUEST_FLUSHED 0x16
+#define SRB_STATUS_DELAYED_RETRY 0x17
+#define SRB_STATUS_INVALID_LUN 0x20
+#define SRB_STATUS_INVALID_TARGET_ID 0x21
+#define SRB_STATUS_BAD_FUNCTION 0x22
+#define SRB_STATUS_ERROR_RECOVERY 0x23
+#define SRB_STATUS_NOT_STARTED 0x24
+#define SRB_STATUS_NOT_IN_USE 0x30
+#define SRB_STATUS_FORCE_ABORT 0x31
+#define SRB_STATUS_DOMAIN_VALIDATION_FAIL 0x32
+
+/*
+ * Object-Server / Volume-Manager Dispatch Classes
+ */
+
+#define VM_Null 0
+#define VM_NameServe 1
+#define VM_ContainerConfig 2
+#define VM_Ioctl 3
+#define VM_FilesystemIoctl 4
+#define VM_CloseAll 5
+#define VM_CtBlockRead 6
+#define VM_CtBlockWrite 7
+#define VM_SliceBlockRead 8 /* raw access to configured "storage objects" */
+#define VM_SliceBlockWrite 9
+#define VM_DriveBlockRead 10 /* raw access to physical devices */
+#define VM_DriveBlockWrite 11
+#define VM_EnclosureMgt 12 /* enclosure management */
+#define VM_Unused 13 /* used to be diskset management */
+#define VM_CtBlockVerify 14
+#define VM_CtPerf 15 /* performance test */
+#define VM_CtBlockRead64 16
+#define VM_CtBlockWrite64 17
+#define VM_CtBlockVerify64 18
+#define VM_CtHostRead64 19
+#define VM_CtHostWrite64 20
+
+#define MAX_VMCOMMAND_NUM 21 /* used for sizing stats array - leave last */
+
+/*
+ * Descriptive information (eg, vital stats)
+ * that a content manager might report. The
+ * FileArray filesystem component is one example
+ * of a content manager. Raw mode might be
+ * another.
+ */
+
+struct aac_fsinfo {
+ u32 fsTotalSize; /* Consumed by fs, incl. metadata */
+ u32 fsBlockSize;
+ u32 fsFragSize;
+ u32 fsMaxExtendSize;
+ u32 fsSpaceUnits;
+ u32 fsMaxNumFiles;
+ u32 fsNumFreeFiles;
+ u32 fsInodeDensity;
+}; /* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+
+union aac_contentinfo {
+ struct aac_fsinfo filesys; /* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+};
+
+/*
+ * Query for Container Configuration Status
+ */
+
+#define CT_GET_CONFIG_STATUS 147
+struct aac_get_config_status {
+ u32 command; /* VM_ContainerConfig */
+ u32 type; /* CT_GET_CONFIG_STATUS */
+ u32 parm1;
+ u32 parm2;
+ u32 parm3;
+ u32 parm4;
+ u32 parm5;
+ u32 count; /* sizeof(((struct aac_get_config_status_resp *)NULL)->data) */
+};
+
+#define CFACT_CONTINUE 0
+#define CFACT_PAUSE 1
+#define CFACT_ABORT 2
+struct aac_get_config_status_resp {
+ u32 response; /* ST_OK */
+ u32 dummy0;
+ u32 status; /* CT_OK */
+ u32 parm1;
+ u32 parm2;
+ u32 parm3;
+ u32 parm4;
+ u32 parm5;
+ struct {
+ u32 action; /* CFACT_CONTINUE, CFACT_PAUSE or CFACT_ABORT */
+ u16 flags;
+ s16 count;
+ } data;
+};
+
+/*
+ * Accept the configuration as-is
+ */
+
+#define CT_COMMIT_CONFIG 152
+
+struct aac_commit_config {
+ u32 command; /* VM_ContainerConfig */
+ u32 type; /* CT_COMMIT_CONFIG */
+};
+
+/*
+ * Query for Container Configuration Count
+ */
+
+#define CT_GET_CONTAINER_COUNT 4
+struct aac_get_container_count {
+ u32 command; /* VM_ContainerConfig */
+ u32 type; /* CT_GET_CONTAINER_COUNT */
+};
+
+struct aac_get_container_count_resp {
+ u32 response; /* ST_OK */
+ u32 dummy0;
+ u32 MaxContainers;
+ u32 ContainerSwitchEntries;
+ u32 MaxPartitions;
+};
+
+
+/*
+ * Query for "mountable" objects, ie, objects that are typically
+ * associated with a drive letter on the client (host) side.
+ */
+
+struct aac_mntent {
+ u32 oid;
+ u8 name[16]; // if applicable
+ struct creation_info create_info; // if applicable
+ u32 capacity;
+ u32 vol; // substrate structure
+ u32 obj; // FT_FILESYS, FT_DATABASE, etc.
+ u32 state; // unready for mounting, readonly, etc.
+ union aac_contentinfo fileinfo; // Info specific to content manager (eg, filesystem)
+ u32 altoid; // != oid <==> snapshot or broken mirror exists
+};
+
+#define FSCS_NOTCLEAN 0x0001 /* fsck is neccessary before mounting */
+#define FSCS_READONLY 0x0002 /* possible result of broken mirror */
+#define FSCS_HIDDEN 0x0004 /* should be ignored - set during a clear */
+
+struct aac_query_mount {
+ u32 command;
+ u32 type;
+ u32 count;
+};
+
+struct aac_mount {
+ u32 status;
+ u32 type; /* should be same as that requested */
+ u32 count;
+ struct aac_mntent mnt[1];
+};
+
+#define CT_READ_NAME 130
+struct aac_get_name {
+ u32 command; /* VM_ContainerConfig */
+ u32 type; /* CT_READ_NAME */
+ u32 cid;
+ u32 parm1;
+ u32 parm2;
+ u32 parm3;
+ u32 parm4;
+ u32 count; /* sizeof(((struct aac_get_name_resp *)NULL)->data) */
+};
+
+#define CT_OK 218
+struct aac_get_name_resp {
+ u32 dummy0;
+ u32 dummy1;
+ u32 status; /* CT_OK */
+ u32 parm1;
+ u32 parm2;
+ u32 parm3;
+ u32 parm4;
+ u32 parm5;
+ u8 data[16];
+};
+
+/*
+ * The following command is sent to shut down each container.
+ */
+
+struct aac_close {
+ u32 command;
+ u32 cid;
+};
+
+struct aac_query_disk
+{
+ s32 cnum;
+ s32 bus;
+ s32 id;
+ s32 lun;
+ u32 valid;
+ u32 locked;
+ u32 deleted;
+ s32 instance;
+ s8 name[10];
+ u32 unmapped;
+};
+
+struct aac_delete_disk {
+ u32 disknum;
+ u32 cnum;
+};
+
+struct fib_ioctl
+{
+ u32 fibctx;
+ s32 wait;
+ char __user *fib;
+};
+
+struct revision
+{
+ u32 compat;
+ u32 version;
+ u32 build;
+};
+
+/*
+ * Ugly - non Linux like ioctl coding for back compat.
+ */
+
+#define CTL_CODE(function, method) ( \
+ (4<< 16) | ((function) << 2) | (method) \
+)
+
+/*
+ * Define the method codes for how buffers are passed for I/O and FS
+ * controls
+ */
+
+#define METHOD_BUFFERED 0
+#define METHOD_NEITHER 3
+
+/*
+ * Filesystem ioctls
+ */
+
+#define FSACTL_SENDFIB CTL_CODE(2050, METHOD_BUFFERED)
+#define FSACTL_SEND_RAW_SRB CTL_CODE(2067, METHOD_BUFFERED)
+#define FSACTL_DELETE_DISK 0x163
+#define FSACTL_QUERY_DISK 0x173
+#define FSACTL_OPEN_GET_ADAPTER_FIB CTL_CODE(2100, METHOD_BUFFERED)
+#define FSACTL_GET_NEXT_ADAPTER_FIB CTL_CODE(2101, METHOD_BUFFERED)
+#define FSACTL_CLOSE_GET_ADAPTER_FIB CTL_CODE(2102, METHOD_BUFFERED)
+#define FSACTL_MINIPORT_REV_CHECK CTL_CODE(2107, METHOD_BUFFERED)
+#define FSACTL_GET_PCI_INFO CTL_CODE(2119, METHOD_BUFFERED)
+#define FSACTL_FORCE_DELETE_DISK CTL_CODE(2120, METHOD_NEITHER)
+#define FSACTL_GET_CONTAINERS 2131
+
+
+struct aac_common
+{
+ /*
+ * If this value is set to 1 then interrupt moderation will occur
+ * in the base commuication support.
+ */
+ u32 irq_mod;
+ u32 peak_fibs;
+ u32 zero_fibs;
+ u32 fib_timeouts;
+ /*
+ * Statistical counters in debug mode
+ */
+#ifdef DBG
+ u32 FibsSent;
+ u32 FibRecved;
+ u32 NoResponseSent;
+ u32 NoResponseRecved;
+ u32 AsyncSent;
+ u32 AsyncRecved;
+ u32 NormalSent;
+ u32 NormalRecved;
+#endif
+};
+
+extern struct aac_common aac_config;
+
+
+/*
+ * The following macro is used when sending and receiving FIBs. It is
+ * only used for debugging.
+ */
+
+#ifdef DBG
+#define FIB_COUNTER_INCREMENT(counter) (counter)++
+#else
+#define FIB_COUNTER_INCREMENT(counter)
+#endif
+
+/*
+ * Adapter direct commands
+ * Monitor/Kernel API
+ */
+
+#define BREAKPOINT_REQUEST 0x00000004
+#define INIT_STRUCT_BASE_ADDRESS 0x00000005
+#define READ_PERMANENT_PARAMETERS 0x0000000a
+#define WRITE_PERMANENT_PARAMETERS 0x0000000b
+#define HOST_CRASHING 0x0000000d
+#define SEND_SYNCHRONOUS_FIB 0x0000000c
+#define COMMAND_POST_RESULTS 0x00000014
+#define GET_ADAPTER_PROPERTIES 0x00000019
+#define GET_DRIVER_BUFFER_PROPERTIES 0x00000023
+#define RCV_TEMP_READINGS 0x00000025
+#define GET_COMM_PREFERRED_SETTINGS 0x00000026
+#define IOP_RESET 0x00001000
+#define RE_INIT_ADAPTER 0x000000ee
+
+/*
+ * Adapter Status Register
+ *
+ * Phase Staus mailbox is 32bits:
+ * <31:16> = Phase Status
+ * <15:0> = Phase
+ *
+ * The adapter reports is present state through the phase. Only
+ * a single phase should be ever be set. Each phase can have multiple
+ * phase status bits to provide more detailed information about the
+ * state of the board. Care should be taken to ensure that any phase
+ * status bits that are set when changing the phase are also valid
+ * for the new phase or be cleared out. Adapter software (monitor,
+ * iflash, kernel) is responsible for properly maintining the phase
+ * status mailbox when it is running.
+ *
+ * MONKER_API Phases
+ *
+ * Phases are bit oriented. It is NOT valid to have multiple bits set
+ */
+
+#define SELF_TEST_FAILED 0x00000004
+#define MONITOR_PANIC 0x00000020
+#define KERNEL_UP_AND_RUNNING 0x00000080
+#define KERNEL_PANIC 0x00000100
+
+/*
+ * Doorbell bit defines
+ */
+
+#define DoorBellSyncCmdAvailable (1<<0) /* Host -> Adapter */
+#define DoorBellPrintfDone (1<<5) /* Host -> Adapter */
+#define DoorBellAdapterNormCmdReady (1<<1) /* Adapter -> Host */
+#define DoorBellAdapterNormRespReady (1<<2) /* Adapter -> Host */
+#define DoorBellAdapterNormCmdNotFull (1<<3) /* Adapter -> Host */
+#define DoorBellAdapterNormRespNotFull (1<<4) /* Adapter -> Host */
+#define DoorBellPrintfReady (1<<5) /* Adapter -> Host */
+
+/*
+ * For FIB communication, we need all of the following things
+ * to send back to the user.
+ */
+
+#define AifCmdEventNotify 1 /* Notify of event */
+#define AifEnConfigChange 3 /* Adapter configuration change */
+#define AifEnContainerChange 4 /* Container configuration change */
+#define AifEnDeviceFailure 5 /* SCSI device failed */
+#define AifEnAddContainer 15 /* A new array was created */
+#define AifEnDeleteContainer 16 /* A container was deleted */
+#define AifEnExpEvent 23 /* Firmware Event Log */
+#define AifExeFirmwarePanic 3 /* Firmware Event Panic */
+#define AifHighPriority 3 /* Highest Priority Event */
+
+#define AifCmdJobProgress 2 /* Progress report */
+#define AifJobCtrZero 101 /* Array Zero progress */
+#define AifJobStsSuccess 1 /* Job completes */
+#define AifCmdAPIReport 3 /* Report from other user of API */
+#define AifCmdDriverNotify 4 /* Notify host driver of event */
+#define AifDenMorphComplete 200 /* A morph operation completed */
+#define AifDenVolumeExtendComplete 201 /* A volume extend completed */
+#define AifReqJobList 100 /* Gets back complete job list */
+#define AifReqJobsForCtr 101 /* Gets back jobs for specific container */
+#define AifReqJobsForScsi 102 /* Gets back jobs for specific SCSI device */
+#define AifReqJobReport 103 /* Gets back a specific job report or list of them */
+#define AifReqTerminateJob 104 /* Terminates job */
+#define AifReqSuspendJob 105 /* Suspends a job */
+#define AifReqResumeJob 106 /* Resumes a job */
+#define AifReqSendAPIReport 107 /* API generic report requests */
+#define AifReqAPIJobStart 108 /* Start a job from the API */
+#define AifReqAPIJobUpdate 109 /* Update a job report from the API */
+#define AifReqAPIJobFinish 110 /* Finish a job from the API */
+
+/*
+ * Adapter Initiated FIB command structures. Start with the adapter
+ * initiated FIBs that really come from the adapter, and get responded
+ * to by the host.
+ */
+
+struct aac_aifcmd {
+ u32 command; /* Tell host what type of notify this is */
+ u32 seqnum; /* To allow ordering of reports (if necessary) */
+ u8 data[1]; /* Undefined length (from kernel viewpoint) */
+};
+
+/**
+ * Convert capacity to cylinders
+ * accounting for the fact capacity could be a 64 bit value
+ *
+ */
+static inline u32 cap_to_cyls(sector_t capacity, u32 divisor)
+{
+ sector_div(capacity, divisor);
+ return (u32)capacity;
+}
+
+struct scsi_cmnd;
+
+const char *aac_driverinfo(struct Scsi_Host *);
+struct fib *fib_alloc(struct aac_dev *dev);
+int fib_setup(struct aac_dev *dev);
+void fib_map_free(struct aac_dev *dev);
+void fib_free(struct fib * context);
+void fib_init(struct fib * context);
+void fib_dealloc(struct fib * context);
+void aac_printf(struct aac_dev *dev, u32 val);
+int fib_send(u16 command, struct fib * context, unsigned long size, int priority, int wait, int reply, fib_callback callback, void *ctxt);
+int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry);
+void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum);
+int fib_complete(struct fib * context);
+#define fib_data(fibctx) ((void *)(fibctx)->hw_fib->data)
+struct aac_dev *aac_init_adapter(struct aac_dev *dev);
+int aac_get_config_status(struct aac_dev *dev);
+int aac_get_containers(struct aac_dev *dev);
+int aac_scsi_cmd(struct scsi_cmnd *cmd);
+int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg);
+int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg);
+int aac_rx_init(struct aac_dev *dev);
+int aac_rkt_init(struct aac_dev *dev);
+int aac_sa_init(struct aac_dev *dev);
+unsigned int aac_response_normal(struct aac_queue * q);
+unsigned int aac_command_normal(struct aac_queue * q);
+int aac_command_thread(struct aac_dev * dev);
+int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx);
+int fib_adapter_complete(struct fib * fibptr, unsigned short size);
+struct aac_driver_ident* aac_get_driver_ident(int devtype);
+int aac_get_adapter_info(struct aac_dev* dev);
+int aac_send_shutdown(struct aac_dev *dev);
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
new file mode 100644
index 000000000000..30dd1f7120f4
--- /dev/null
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -0,0 +1,683 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * commctrl.c
+ *
+ * Abstract: Contains all routines for control of the AFA comm layer
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/blkdev.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include "aacraid.h"
+
+/**
+ * ioctl_send_fib - send a FIB from userspace
+ * @dev: adapter is being processed
+ * @arg: arguments to the ioctl call
+ *
+ * This routine sends a fib to the adapter on behalf of a user level
+ * program.
+ */
+
+static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
+{
+ struct hw_fib * kfib;
+ struct fib *fibptr;
+
+ fibptr = fib_alloc(dev);
+ if(fibptr == NULL)
+ return -ENOMEM;
+
+ kfib = fibptr->hw_fib;
+ /*
+ * First copy in the header so that we can check the size field.
+ */
+ if (copy_from_user((void *)kfib, arg, sizeof(struct aac_fibhdr))) {
+ fib_free(fibptr);
+ return -EFAULT;
+ }
+ /*
+ * Since we copy based on the fib header size, make sure that we
+ * will not overrun the buffer when we copy the memory. Return
+ * an error if we would.
+ */
+ if (le16_to_cpu(kfib->header.Size) >
+ sizeof(struct hw_fib) - sizeof(struct aac_fibhdr)) {
+ fib_free(fibptr);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(kfib, arg, le16_to_cpu(kfib->header.Size) +
+ sizeof(struct aac_fibhdr))) {
+ fib_free(fibptr);
+ return -EFAULT;
+ }
+
+ if (kfib->header.Command == cpu_to_le32(TakeABreakPt)) {
+ aac_adapter_interrupt(dev);
+ /*
+ * Since we didn't really send a fib, zero out the state to allow
+ * cleanup code not to assert.
+ */
+ kfib->header.XferState = 0;
+ } else {
+ int retval = fib_send(kfib->header.Command, fibptr,
+ le16_to_cpu(kfib->header.Size) , FsaNormal,
+ 1, 1, NULL, NULL);
+ if (retval) {
+ fib_free(fibptr);
+ return retval;
+ }
+ if (fib_complete(fibptr) != 0) {
+ fib_free(fibptr);
+ return -EINVAL;
+ }
+ }
+ /*
+ * Make sure that the size returned by the adapter (which includes
+ * the header) is less than or equal to the size of a fib, so we
+ * don't corrupt application data. Then copy that size to the user
+ * buffer. (Don't try to add the header information again, since it
+ * was already included by the adapter.)
+ */
+
+ if (copy_to_user(arg, (void *)kfib, kfib->header.Size)) {
+ fib_free(fibptr);
+ return -EFAULT;
+ }
+ fib_free(fibptr);
+ return 0;
+}
+
+/**
+ * open_getadapter_fib - Get the next fib
+ *
+ * This routine will get the next Fib, if available, from the AdapterFibContext
+ * passed in from the user.
+ */
+
+static int open_getadapter_fib(struct aac_dev * dev, void __user *arg)
+{
+ struct aac_fib_context * fibctx;
+ int status;
+
+ fibctx = kmalloc(sizeof(struct aac_fib_context), GFP_KERNEL);
+ if (fibctx == NULL) {
+ status = -ENOMEM;
+ } else {
+ unsigned long flags;
+ struct list_head * entry;
+ struct aac_fib_context * context;
+
+ fibctx->type = FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT;
+ fibctx->size = sizeof(struct aac_fib_context);
+ /*
+ * Yes yes, I know this could be an index, but we have a
+ * better guarantee of uniqueness for the locked loop below.
+ * Without the aid of a persistent history, this also helps
+ * reduce the chance that the opaque context would be reused.
+ */
+ fibctx->unique = (u32)((ulong)fibctx & 0xFFFFFFFF);
+ /*
+ * Initialize the mutex used to wait for the next AIF.
+ */
+ init_MUTEX_LOCKED(&fibctx->wait_sem);
+ fibctx->wait = 0;
+ /*
+ * Initialize the fibs and set the count of fibs on
+ * the list to 0.
+ */
+ fibctx->count = 0;
+ INIT_LIST_HEAD(&fibctx->fib_list);
+ fibctx->jiffies = jiffies/HZ;
+ /*
+ * Now add this context onto the adapter's
+ * AdapterFibContext list.
+ */
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ /* Ensure that we have a unique identifier */
+ entry = dev->fib_list.next;
+ while (entry != &dev->fib_list) {
+ context = list_entry(entry, struct aac_fib_context, next);
+ if (context->unique == fibctx->unique) {
+ /* Not unique (32 bits) */
+ fibctx->unique++;
+ entry = dev->fib_list.next;
+ } else {
+ entry = entry->next;
+ }
+ }
+ list_add_tail(&fibctx->next, &dev->fib_list);
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ if (copy_to_user(arg, &fibctx->unique,
+ sizeof(fibctx->unique))) {
+ status = -EFAULT;
+ } else {
+ status = 0;
+ }
+ }
+ return status;
+}
+
+/**
+ * next_getadapter_fib - get the next fib
+ * @dev: adapter to use
+ * @arg: ioctl argument
+ *
+ * This routine will get the next Fib, if available, from the AdapterFibContext
+ * passed in from the user.
+ */
+
+static int next_getadapter_fib(struct aac_dev * dev, void __user *arg)
+{
+ struct fib_ioctl f;
+ struct fib *fib;
+ struct aac_fib_context *fibctx;
+ int status;
+ struct list_head * entry;
+ unsigned long flags;
+
+ if(copy_from_user((void *)&f, arg, sizeof(struct fib_ioctl)))
+ return -EFAULT;
+ /*
+ * Verify that the HANDLE passed in was a valid AdapterFibContext
+ *
+ * Search the list of AdapterFibContext addresses on the adapter
+ * to be sure this is a valid address
+ */
+ entry = dev->fib_list.next;
+ fibctx = NULL;
+
+ while (entry != &dev->fib_list) {
+ fibctx = list_entry(entry, struct aac_fib_context, next);
+ /*
+ * Extract the AdapterFibContext from the Input parameters.
+ */
+ if (fibctx->unique == f.fibctx) { /* We found a winner */
+ break;
+ }
+ entry = entry->next;
+ fibctx = NULL;
+ }
+ if (!fibctx) {
+ dprintk ((KERN_INFO "Fib Context not found\n"));
+ return -EINVAL;
+ }
+
+ if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||
+ (fibctx->size != sizeof(struct aac_fib_context))) {
+ dprintk ((KERN_INFO "Fib Context corrupt?\n"));
+ return -EINVAL;
+ }
+ status = 0;
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ /*
+ * If there are no fibs to send back, then either wait or return
+ * -EAGAIN
+ */
+return_fib:
+ if (!list_empty(&fibctx->fib_list)) {
+ struct list_head * entry;
+ /*
+ * Pull the next fib from the fibs
+ */
+ entry = fibctx->fib_list.next;
+ list_del(entry);
+
+ fib = list_entry(entry, struct fib, fiblink);
+ fibctx->count--;
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ if (copy_to_user(f.fib, fib->hw_fib, sizeof(struct hw_fib))) {
+ kfree(fib->hw_fib);
+ kfree(fib);
+ return -EFAULT;
+ }
+ /*
+ * Free the space occupied by this copy of the fib.
+ */
+ kfree(fib->hw_fib);
+ kfree(fib);
+ status = 0;
+ fibctx->jiffies = jiffies/HZ;
+ } else {
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ if (f.wait) {
+ if(down_interruptible(&fibctx->wait_sem) < 0) {
+ status = -EINTR;
+ } else {
+ /* Lock again and retry */
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ goto return_fib;
+ }
+ } else {
+ status = -EAGAIN;
+ }
+ }
+ return status;
+}
+
+int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context * fibctx)
+{
+ struct fib *fib;
+
+ /*
+ * First free any FIBs that have not been consumed.
+ */
+ while (!list_empty(&fibctx->fib_list)) {
+ struct list_head * entry;
+ /*
+ * Pull the next fib from the fibs
+ */
+ entry = fibctx->fib_list.next;
+ list_del(entry);
+ fib = list_entry(entry, struct fib, fiblink);
+ fibctx->count--;
+ /*
+ * Free the space occupied by this copy of the fib.
+ */
+ kfree(fib->hw_fib);
+ kfree(fib);
+ }
+ /*
+ * Remove the Context from the AdapterFibContext List
+ */
+ list_del(&fibctx->next);
+ /*
+ * Invalidate context
+ */
+ fibctx->type = 0;
+ /*
+ * Free the space occupied by the Context
+ */
+ kfree(fibctx);
+ return 0;
+}
+
+/**
+ * close_getadapter_fib - close down user fib context
+ * @dev: adapter
+ * @arg: ioctl arguments
+ *
+ * This routine will close down the fibctx passed in from the user.
+ */
+
+static int close_getadapter_fib(struct aac_dev * dev, void __user *arg)
+{
+ struct aac_fib_context *fibctx;
+ int status;
+ unsigned long flags;
+ struct list_head * entry;
+
+ /*
+ * Verify that the HANDLE passed in was a valid AdapterFibContext
+ *
+ * Search the list of AdapterFibContext addresses on the adapter
+ * to be sure this is a valid address
+ */
+
+ entry = dev->fib_list.next;
+ fibctx = NULL;
+
+ while(entry != &dev->fib_list) {
+ fibctx = list_entry(entry, struct aac_fib_context, next);
+ /*
+ * Extract the fibctx from the input parameters
+ */
+ if (fibctx->unique == (u32)(unsigned long)arg) {
+ /* We found a winner */
+ break;
+ }
+ entry = entry->next;
+ fibctx = NULL;
+ }
+
+ if (!fibctx)
+ return 0; /* Already gone */
+
+ if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||
+ (fibctx->size != sizeof(struct aac_fib_context)))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ status = aac_close_fib_context(dev, fibctx);
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ return status;
+}
+
+/**
+ * check_revision - close down user fib context
+ * @dev: adapter
+ * @arg: ioctl arguments
+ *
+ * This routine returns the driver version.
+ * Under Linux, there have been no version incompatibilities, so this is
+ * simple!
+ */
+
+static int check_revision(struct aac_dev *dev, void __user *arg)
+{
+ struct revision response;
+
+ response.compat = 1;
+ response.version = dev->adapter_info.kernelrev;
+ response.build = dev->adapter_info.kernelbuild;
+
+ if (copy_to_user(arg, &response, sizeof(response)))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ *
+ * aac_send_raw_scb
+ *
+ */
+
+int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
+{
+ struct fib* srbfib;
+ int status;
+ struct aac_srb *srbcmd;
+ struct aac_srb __user *user_srb = arg;
+ struct aac_srb_reply __user *user_reply;
+ struct aac_srb_reply* reply;
+ u32 fibsize = 0;
+ u32 flags = 0;
+ s32 rcode = 0;
+ u32 data_dir;
+ void __user *sg_user[32];
+ void *sg_list[32];
+ u32 sg_indx = 0;
+ u32 byte_count = 0;
+ u32 actual_fibsize = 0;
+ int i;
+
+
+ if (!capable(CAP_SYS_ADMIN)){
+ printk(KERN_DEBUG"aacraid: No permission to send raw srb\n");
+ return -EPERM;
+ }
+ /*
+ * Allocate and initialize a Fib then setup a BlockWrite command
+ */
+ if (!(srbfib = fib_alloc(dev))) {
+ return -1;
+ }
+ fib_init(srbfib);
+
+ srbcmd = (struct aac_srb*) fib_data(srbfib);
+
+ if(copy_from_user(&fibsize, &user_srb->count,sizeof(u32))){
+ printk(KERN_DEBUG"aacraid: Could not copy data size from user\n");
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+
+ if (fibsize > FIB_DATA_SIZE_IN_BYTES) {
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ if(copy_from_user(srbcmd, user_srb,fibsize)){
+ printk(KERN_DEBUG"aacraid: Could not copy srb from user\n");
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+
+ user_reply = arg+fibsize;
+
+ flags = srbcmd->flags;
+ // Fix up srb for endian and force some values
+ srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi); // Force this
+ srbcmd->channel = cpu_to_le32(srbcmd->channel);
+ srbcmd->id = cpu_to_le32(srbcmd->id);
+ srbcmd->lun = cpu_to_le32(srbcmd->lun);
+ srbcmd->flags = cpu_to_le32(srbcmd->flags);
+ srbcmd->timeout = cpu_to_le32(srbcmd->timeout);
+ srbcmd->retry_limit =cpu_to_le32(0); // Obsolete parameter
+ srbcmd->cdb_size = cpu_to_le32(srbcmd->cdb_size);
+
+ switch (srbcmd->flags & (SRB_DataIn | SRB_DataOut)) {
+ case SRB_DataOut:
+ data_dir = DMA_TO_DEVICE;
+ break;
+ case (SRB_DataIn | SRB_DataOut):
+ data_dir = DMA_BIDIRECTIONAL;
+ break;
+ case SRB_DataIn:
+ data_dir = DMA_FROM_DEVICE;
+ break;
+ default:
+ data_dir = DMA_NONE;
+ }
+ if (dev->dac_support == 1) {
+ struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg;
+ byte_count = 0;
+
+ /*
+ * This should also catch if user used the 32 bit sgmap
+ */
+ actual_fibsize = sizeof(struct aac_srb) -
+ sizeof(struct sgentry) + ((srbcmd->sg.count & 0xff) *
+ sizeof(struct sgentry64));
+ if(actual_fibsize != fibsize){ // User made a mistake - should not continue
+ printk(KERN_DEBUG"aacraid: Bad Size specified in Raw SRB command\n");
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ if ((data_dir == DMA_NONE) && psg->count) {
+ printk(KERN_DEBUG"aacraid: SG with no direction specified in Raw SRB command\n");
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ for (i = 0; i < psg->count; i++) {
+ dma_addr_t addr;
+ u64 le_addr;
+ void* p;
+ p = kmalloc(psg->sg[i].count,GFP_KERNEL|__GFP_DMA);
+ if(p == 0) {
+ printk(KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ psg->sg[i].count,i,psg->count);
+ rcode = -ENOMEM;
+ goto cleanup;
+ }
+ sg_user[i] = (void __user *)psg->sg[i].addr;
+ sg_list[i] = p; // save so we can clean up later
+ sg_indx = i;
+
+ if( flags & SRB_DataOut ){
+ if(copy_from_user(p,sg_user[i],psg->sg[i].count)){
+ printk(KERN_DEBUG"aacraid: Could not copy sg data from user\n");
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ }
+ addr = pci_map_single(dev->pdev, p, psg->sg[i].count, data_dir);
+
+ le_addr = cpu_to_le64(addr);
+ psg->sg[i].addr[1] = (u32)(le_addr>>32);
+ psg->sg[i].addr[0] = (u32)(le_addr & 0xffffffff);
+ psg->sg[i].count = cpu_to_le32(psg->sg[i].count);
+ byte_count += psg->sg[i].count;
+ }
+
+ srbcmd->count = cpu_to_le32(byte_count);
+ status = fib_send(ScsiPortCommand64, srbfib, actual_fibsize, FsaNormal, 1, 1,NULL,NULL);
+ } else {
+ struct sgmap* psg = &srbcmd->sg;
+ byte_count = 0;
+
+ actual_fibsize = sizeof (struct aac_srb) +
+ (((le32_to_cpu(srbcmd->sg.count) & 0xff) - 1) *
+ sizeof (struct sgentry));
+ if(actual_fibsize != fibsize){ // User made a mistake - should not continue
+ printk(KERN_DEBUG"aacraid: Bad Size specified in Raw SRB command\n");
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ if ((data_dir == DMA_NONE) && psg->count) {
+ printk(KERN_DEBUG"aacraid: SG with no direction specified in Raw SRB command\n");
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ for (i = 0; i < psg->count; i++) {
+ dma_addr_t addr;
+ void* p;
+ p = kmalloc(psg->sg[i].count,GFP_KERNEL);
+ if(p == 0) {
+ printk(KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ psg->sg[i].count,i,psg->count);
+ rcode = -ENOMEM;
+ goto cleanup;
+ }
+ sg_user[i] = (void __user *)(psg->sg[i].addr);
+ sg_list[i] = p; // save so we can clean up later
+ sg_indx = i;
+
+ if( flags & SRB_DataOut ){
+ if(copy_from_user(p,sg_user[i],psg->sg[i].count)){
+ printk(KERN_DEBUG"aacraid: Could not copy sg data from user\n");
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ }
+ addr = pci_map_single(dev->pdev, p, psg->sg[i].count, data_dir);
+
+ psg->sg[i].addr = cpu_to_le32(addr);
+ psg->sg[i].count = cpu_to_le32(psg->sg[i].count);
+ byte_count += psg->sg[i].count;
+ }
+ srbcmd->count = cpu_to_le32(byte_count);
+ status = fib_send(ScsiPortCommand, srbfib, actual_fibsize, FsaNormal, 1, 1, NULL, NULL);
+ }
+
+ if (status != 0){
+ printk(KERN_DEBUG"aacraid: Could not send raw srb fib to hba\n");
+ rcode = -1;
+ goto cleanup;
+ }
+
+ if( flags & SRB_DataIn ) {
+ for(i = 0 ; i <= sg_indx; i++){
+ if(copy_to_user(sg_user[i],sg_list[i],le32_to_cpu(srbcmd->sg.sg[i].count))){
+ printk(KERN_DEBUG"aacraid: Could not copy sg data to user\n");
+ rcode = -EFAULT;
+ goto cleanup;
+
+ }
+ }
+ }
+
+ reply = (struct aac_srb_reply *) fib_data(srbfib);
+ if(copy_to_user(user_reply,reply,sizeof(struct aac_srb_reply))){
+ printk(KERN_DEBUG"aacraid: Could not copy reply to user\n");
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+
+cleanup:
+ for(i=0; i <= sg_indx; i++){
+ kfree(sg_list[i]);
+ }
+ fib_complete(srbfib);
+ fib_free(srbfib);
+
+ return rcode;
+}
+
+
+struct aac_pci_info {
+ u32 bus;
+ u32 slot;
+};
+
+
+int aac_get_pci_info(struct aac_dev* dev, void __user *arg)
+{
+ struct aac_pci_info pci_info;
+
+ pci_info.bus = dev->pdev->bus->number;
+ pci_info.slot = PCI_SLOT(dev->pdev->devfn);
+
+ if (copy_to_user(arg, &pci_info, sizeof(struct aac_pci_info))) {
+ printk(KERN_DEBUG "aacraid: Could not copy pci info\n");
+ return -EFAULT;
+ }
+ return 0;
+ }
+
+
+int aac_do_ioctl(struct aac_dev * dev, int cmd, void __user *arg)
+{
+ int status;
+
+ /*
+ * HBA gets first crack
+ */
+
+ status = aac_dev_ioctl(dev, cmd, arg);
+ if(status != -ENOTTY)
+ return status;
+
+ switch (cmd) {
+ case FSACTL_MINIPORT_REV_CHECK:
+ status = check_revision(dev, arg);
+ break;
+ case FSACTL_SENDFIB:
+ status = ioctl_send_fib(dev, arg);
+ break;
+ case FSACTL_OPEN_GET_ADAPTER_FIB:
+ status = open_getadapter_fib(dev, arg);
+ break;
+ case FSACTL_GET_NEXT_ADAPTER_FIB:
+ status = next_getadapter_fib(dev, arg);
+ break;
+ case FSACTL_CLOSE_GET_ADAPTER_FIB:
+ status = close_getadapter_fib(dev, arg);
+ break;
+ case FSACTL_SEND_RAW_SRB:
+ status = aac_send_raw_srb(dev,arg);
+ break;
+ case FSACTL_GET_PCI_INFO:
+ status = aac_get_pci_info(dev,arg);
+ break;
+ default:
+ status = -ENOTTY;
+ break;
+ }
+ return status;
+}
+
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
new file mode 100644
index 000000000000..6832a55ca907
--- /dev/null
+++ b/drivers/scsi/aacraid/comminit.c
@@ -0,0 +1,325 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * comminit.c
+ *
+ * Abstract: This supports the initialization of the host adapter commuication interface.
+ * This is a platform dependent module for the pci cyclone board.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/mm.h>
+#include <asm/semaphore.h>
+
+#include "aacraid.h"
+
+struct aac_common aac_config;
+
+static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign)
+{
+ unsigned char *base;
+ unsigned long size, align;
+ unsigned long fibsize = 4096;
+ unsigned long printfbufsiz = 256;
+ struct aac_init *init;
+ dma_addr_t phys;
+
+ size = fibsize + sizeof(struct aac_init) + commsize + commalign + printfbufsiz;
+
+
+ base = pci_alloc_consistent(dev->pdev, size, &phys);
+
+ if(base == NULL)
+ {
+ printk(KERN_ERR "aacraid: unable to create mapping.\n");
+ return 0;
+ }
+ dev->comm_addr = (void *)base;
+ dev->comm_phys = phys;
+ dev->comm_size = size;
+
+ dev->init = (struct aac_init *)(base + fibsize);
+ dev->init_pa = phys + fibsize;
+
+ init = dev->init;
+
+ init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
+ init->MiniPortRevision = cpu_to_le32(Sa_MINIPORT_REVISION);
+ init->fsrev = cpu_to_le32(dev->fsrev);
+
+ /*
+ * Adapter Fibs are the first thing allocated so that they
+ * start page aligned
+ */
+ dev->aif_base_va = (struct hw_fib *)base;
+
+ init->AdapterFibsVirtualAddress = 0;
+ init->AdapterFibsPhysicalAddress = cpu_to_le32((u32)phys);
+ init->AdapterFibsSize = cpu_to_le32(fibsize);
+ init->AdapterFibAlign = cpu_to_le32(sizeof(struct hw_fib));
+ /*
+ * number of 4k pages of host physical memory. The aacraid fw needs
+ * this number to be less than 4gb worth of pages. num_physpages is in
+ * system page units. New firmware doesn't have any issues with the
+ * mapping system, but older Firmware did, and had *troubles* dealing
+ * with the math overloading past 32 bits, thus we must limit this
+ * field.
+ *
+ * This assumes the memory is mapped zero->n, which isnt
+ * always true on real computers. It also has some slight problems
+ * with the GART on x86-64. I've btw never tried DMA from PCI space
+ * on this platform but don't be suprised if its problematic.
+ */
+#ifndef CONFIG_GART_IOMMU
+ if ((num_physpages << (PAGE_SHIFT - 12)) <= AAC_MAX_HOSTPHYSMEMPAGES) {
+ init->HostPhysMemPages =
+ cpu_to_le32(num_physpages << (PAGE_SHIFT-12));
+ } else
+#endif
+ {
+ init->HostPhysMemPages = cpu_to_le32(AAC_MAX_HOSTPHYSMEMPAGES);
+ }
+
+
+ /*
+ * Increment the base address by the amount already used
+ */
+ base = base + fibsize + sizeof(struct aac_init);
+ phys = (dma_addr_t)((ulong)phys + fibsize + sizeof(struct aac_init));
+ /*
+ * Align the beginning of Headers to commalign
+ */
+ align = (commalign - ((unsigned long)(base) & (commalign - 1)));
+ base = base + align;
+ phys = phys + align;
+ /*
+ * Fill in addresses of the Comm Area Headers and Queues
+ */
+ *commaddr = base;
+ init->CommHeaderAddress = cpu_to_le32((u32)phys);
+ /*
+ * Increment the base address by the size of the CommArea
+ */
+ base = base + commsize;
+ phys = phys + commsize;
+ /*
+ * Place the Printf buffer area after the Fast I/O comm area.
+ */
+ dev->printfbuf = (void *)base;
+ init->printfbuf = cpu_to_le32(phys);
+ init->printfbufsiz = cpu_to_le32(printfbufsiz);
+ memset(base, 0, printfbufsiz);
+ return 1;
+}
+
+static void aac_queue_init(struct aac_dev * dev, struct aac_queue * q, u32 *mem, int qsize)
+{
+ q->numpending = 0;
+ q->dev = dev;
+ INIT_LIST_HEAD(&q->pendingq);
+ init_waitqueue_head(&q->cmdready);
+ INIT_LIST_HEAD(&q->cmdq);
+ init_waitqueue_head(&q->qfull);
+ spin_lock_init(&q->lockdata);
+ q->lock = &q->lockdata;
+ q->headers.producer = mem;
+ q->headers.consumer = mem+1;
+ *(q->headers.producer) = cpu_to_le32(qsize);
+ *(q->headers.consumer) = cpu_to_le32(qsize);
+ q->entries = qsize;
+}
+
+/**
+ * aac_send_shutdown - shutdown an adapter
+ * @dev: Adapter to shutdown
+ *
+ * This routine will send a VM_CloseAll (shutdown) request to the adapter.
+ */
+
+int aac_send_shutdown(struct aac_dev * dev)
+{
+ struct fib * fibctx;
+ struct aac_close *cmd;
+ int status;
+
+ fibctx = fib_alloc(dev);
+ fib_init(fibctx);
+
+ cmd = (struct aac_close *) fib_data(fibctx);
+
+ cmd->command = cpu_to_le32(VM_CloseAll);
+ cmd->cid = cpu_to_le32(0xffffffff);
+
+ status = fib_send(ContainerCommand,
+ fibctx,
+ sizeof(struct aac_close),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+
+ if (status == 0)
+ fib_complete(fibctx);
+ fib_free(fibctx);
+ return status;
+}
+
+/**
+ * aac_comm_init - Initialise FSA data structures
+ * @dev: Adapter to initialise
+ *
+ * Initializes the data structures that are required for the FSA commuication
+ * interface to operate.
+ * Returns
+ * 1 - if we were able to init the commuication interface.
+ * 0 - If there were errors initing. This is a fatal error.
+ */
+
+int aac_comm_init(struct aac_dev * dev)
+{
+ unsigned long hdrsize = (sizeof(u32) * NUMBER_OF_COMM_QUEUES) * 2;
+ unsigned long queuesize = sizeof(struct aac_entry) * TOTAL_QUEUE_ENTRIES;
+ u32 *headers;
+ struct aac_entry * queues;
+ unsigned long size;
+ struct aac_queue_block * comm = dev->queues;
+ /*
+ * Now allocate and initialize the zone structures used as our
+ * pool of FIB context records. The size of the zone is based
+ * on the system memory size. We also initialize the mutex used
+ * to protect the zone.
+ */
+ spin_lock_init(&dev->fib_lock);
+
+ /*
+ * Allocate the physically contigous space for the commuication
+ * queue headers.
+ */
+
+ size = hdrsize + queuesize;
+
+ if (!aac_alloc_comm(dev, (void * *)&headers, size, QUEUE_ALIGNMENT))
+ return -ENOMEM;
+
+ queues = (struct aac_entry *)(((ulong)headers) + hdrsize);
+
+ /* Adapter to Host normal priority Command queue */
+ comm->queue[HostNormCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostNormCmdQueue], headers, HOST_NORM_CMD_ENTRIES);
+ queues += HOST_NORM_CMD_ENTRIES;
+ headers += 2;
+
+ /* Adapter to Host high priority command queue */
+ comm->queue[HostHighCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostHighCmdQueue], headers, HOST_HIGH_CMD_ENTRIES);
+
+ queues += HOST_HIGH_CMD_ENTRIES;
+ headers +=2;
+
+ /* Host to adapter normal priority command queue */
+ comm->queue[AdapNormCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapNormCmdQueue], headers, ADAP_NORM_CMD_ENTRIES);
+
+ queues += ADAP_NORM_CMD_ENTRIES;
+ headers += 2;
+
+ /* host to adapter high priority command queue */
+ comm->queue[AdapHighCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapHighCmdQueue], headers, ADAP_HIGH_CMD_ENTRIES);
+
+ queues += ADAP_HIGH_CMD_ENTRIES;
+ headers += 2;
+
+ /* adapter to host normal priority response queue */
+ comm->queue[HostNormRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostNormRespQueue], headers, HOST_NORM_RESP_ENTRIES);
+ queues += HOST_NORM_RESP_ENTRIES;
+ headers += 2;
+
+ /* adapter to host high priority response queue */
+ comm->queue[HostHighRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostHighRespQueue], headers, HOST_HIGH_RESP_ENTRIES);
+
+ queues += HOST_HIGH_RESP_ENTRIES;
+ headers += 2;
+
+ /* host to adapter normal priority response queue */
+ comm->queue[AdapNormRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapNormRespQueue], headers, ADAP_NORM_RESP_ENTRIES);
+
+ queues += ADAP_NORM_RESP_ENTRIES;
+ headers += 2;
+
+ /* host to adapter high priority response queue */
+ comm->queue[AdapHighRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapHighRespQueue], headers, ADAP_HIGH_RESP_ENTRIES);
+
+ comm->queue[AdapNormCmdQueue].lock = comm->queue[HostNormRespQueue].lock;
+ comm->queue[AdapHighCmdQueue].lock = comm->queue[HostHighRespQueue].lock;
+ comm->queue[AdapNormRespQueue].lock = comm->queue[HostNormCmdQueue].lock;
+ comm->queue[AdapHighRespQueue].lock = comm->queue[HostHighCmdQueue].lock;
+
+ return 0;
+}
+
+struct aac_dev *aac_init_adapter(struct aac_dev *dev)
+{
+ /*
+ * Ok now init the communication subsystem
+ */
+
+ dev->queues = (struct aac_queue_block *) kmalloc(sizeof(struct aac_queue_block), GFP_KERNEL);
+ if (dev->queues == NULL) {
+ printk(KERN_ERR "Error could not allocate comm region.\n");
+ return NULL;
+ }
+ memset(dev->queues, 0, sizeof(struct aac_queue_block));
+
+ if (aac_comm_init(dev)<0){
+ kfree(dev->queues);
+ return NULL;
+ }
+ /*
+ * Initialize the list of fibs
+ */
+ if(fib_setup(dev)<0){
+ kfree(dev->queues);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&dev->fib_list);
+ init_completion(&dev->aif_completion);
+
+ return dev;
+}
+
+
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
new file mode 100644
index 000000000000..3f36dbaa2bb3
--- /dev/null
+++ b/drivers/scsi/aacraid/commsup.c
@@ -0,0 +1,939 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * commsup.c
+ *
+ * Abstract: Contain all routines that are required for FSA host/adapter
+ * commuication.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blkdev.h>
+#include <asm/semaphore.h>
+
+#include "aacraid.h"
+
+/**
+ * fib_map_alloc - allocate the fib objects
+ * @dev: Adapter to allocate for
+ *
+ * Allocate and map the shared PCI space for the FIB blocks used to
+ * talk to the Adaptec firmware.
+ */
+
+static int fib_map_alloc(struct aac_dev *dev)
+{
+ if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, &dev->hw_fib_pa))==NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * fib_map_free - free the fib objects
+ * @dev: Adapter to free
+ *
+ * Free the PCI mappings and the memory allocated for FIB blocks
+ * on this adapter.
+ */
+
+void fib_map_free(struct aac_dev *dev)
+{
+ pci_free_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, dev->hw_fib_va, dev->hw_fib_pa);
+}
+
+/**
+ * fib_setup - setup the fibs
+ * @dev: Adapter to set up
+ *
+ * Allocate the PCI space for the fibs, map it and then intialise the
+ * fib area, the unmapped fib data and also the free list
+ */
+
+int fib_setup(struct aac_dev * dev)
+{
+ struct fib *fibptr;
+ struct hw_fib *hw_fib_va;
+ dma_addr_t hw_fib_pa;
+ int i;
+
+ if(fib_map_alloc(dev)<0)
+ return -ENOMEM;
+
+ hw_fib_va = dev->hw_fib_va;
+ hw_fib_pa = dev->hw_fib_pa;
+ memset(hw_fib_va, 0, sizeof(struct hw_fib) * AAC_NUM_FIB);
+ /*
+ * Initialise the fibs
+ */
+ for (i = 0, fibptr = &dev->fibs[i]; i < AAC_NUM_FIB; i++, fibptr++)
+ {
+ fibptr->dev = dev;
+ fibptr->hw_fib = hw_fib_va;
+ fibptr->data = (void *) fibptr->hw_fib->data;
+ fibptr->next = fibptr+1; /* Forward chain the fibs */
+ init_MUTEX_LOCKED(&fibptr->event_wait);
+ spin_lock_init(&fibptr->event_lock);
+ hw_fib_va->header.XferState = 0xffffffff;
+ hw_fib_va->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+ fibptr->hw_fib_pa = hw_fib_pa;
+ hw_fib_va = (struct hw_fib *)((unsigned char *)hw_fib_va + sizeof(struct hw_fib));
+ hw_fib_pa = hw_fib_pa + sizeof(struct hw_fib);
+ }
+ /*
+ * Add the fib chain to the free list
+ */
+ dev->fibs[AAC_NUM_FIB-1].next = NULL;
+ /*
+ * Enable this to debug out of queue space
+ */
+ dev->free_fib = &dev->fibs[0];
+ return 0;
+}
+
+/**
+ * fib_alloc - allocate a fib
+ * @dev: Adapter to allocate the fib for
+ *
+ * Allocate a fib from the adapter fib pool. If the pool is empty we
+ * wait for fibs to become free.
+ */
+
+struct fib * fib_alloc(struct aac_dev *dev)
+{
+ struct fib * fibptr;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ fibptr = dev->free_fib;
+ /* Cannot sleep here or you get hangs. Instead we did the
+ maths at compile time. */
+ if(!fibptr)
+ BUG();
+ dev->free_fib = fibptr->next;
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ /*
+ * Set the proper node type code and node byte size
+ */
+ fibptr->type = FSAFS_NTC_FIB_CONTEXT;
+ fibptr->size = sizeof(struct fib);
+ /*
+ * Null out fields that depend on being zero at the start of
+ * each I/O
+ */
+ fibptr->hw_fib->header.XferState = 0;
+ fibptr->callback = NULL;
+ fibptr->callback_data = NULL;
+
+ return fibptr;
+}
+
+/**
+ * fib_free - free a fib
+ * @fibptr: fib to free up
+ *
+ * Frees up a fib and places it on the appropriate queue
+ * (either free or timed out)
+ */
+
+void fib_free(struct fib * fibptr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fibptr->dev->fib_lock, flags);
+ if (fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT) {
+ aac_config.fib_timeouts++;
+ fibptr->next = fibptr->dev->timeout_fib;
+ fibptr->dev->timeout_fib = fibptr;
+ } else {
+ if (fibptr->hw_fib->header.XferState != 0) {
+ printk(KERN_WARNING "fib_free, XferState != 0, fibptr = 0x%p, XferState = 0x%x\n",
+ (void*)fibptr,
+ le32_to_cpu(fibptr->hw_fib->header.XferState));
+ }
+ fibptr->next = fibptr->dev->free_fib;
+ fibptr->dev->free_fib = fibptr;
+ }
+ spin_unlock_irqrestore(&fibptr->dev->fib_lock, flags);
+}
+
+/**
+ * fib_init - initialise a fib
+ * @fibptr: The fib to initialize
+ *
+ * Set up the generic fib fields ready for use
+ */
+
+void fib_init(struct fib *fibptr)
+{
+ struct hw_fib *hw_fib = fibptr->hw_fib;
+
+ hw_fib->header.StructType = FIB_MAGIC;
+ hw_fib->header.Size = cpu_to_le16(sizeof(struct hw_fib));
+ hw_fib->header.XferState = cpu_to_le32(HostOwned | FibInitialized | FibEmpty | FastResponseCapable);
+ hw_fib->header.SenderFibAddress = cpu_to_le32(fibptr->hw_fib_pa);
+ hw_fib->header.ReceiverFibAddress = cpu_to_le32(fibptr->hw_fib_pa);
+ hw_fib->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+}
+
+/**
+ * fib_deallocate - deallocate a fib
+ * @fibptr: fib to deallocate
+ *
+ * Will deallocate and return to the free pool the FIB pointed to by the
+ * caller.
+ */
+
+void fib_dealloc(struct fib * fibptr)
+{
+ struct hw_fib *hw_fib = fibptr->hw_fib;
+ if(hw_fib->header.StructType != FIB_MAGIC)
+ BUG();
+ hw_fib->header.XferState = 0;
+}
+
+/*
+ * Commuication primitives define and support the queuing method we use to
+ * support host to adapter commuication. All queue accesses happen through
+ * these routines and are the only routines which have a knowledge of the
+ * how these queues are implemented.
+ */
+
+/**
+ * aac_get_entry - get a queue entry
+ * @dev: Adapter
+ * @qid: Queue Number
+ * @entry: Entry return
+ * @index: Index return
+ * @nonotify: notification control
+ *
+ * With a priority the routine returns a queue entry if the queue has free entries. If the queue
+ * is full(no free entries) than no entry is returned and the function returns 0 otherwise 1 is
+ * returned.
+ */
+
+static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entry, u32 * index, unsigned long *nonotify)
+{
+ struct aac_queue * q;
+
+ /*
+ * All of the queues wrap when they reach the end, so we check
+ * to see if they have reached the end and if they have we just
+ * set the index back to zero. This is a wrap. You could or off
+ * the high bits in all updates but this is a bit faster I think.
+ */
+
+ q = &dev->queues->queue[qid];
+
+ *index = le32_to_cpu(*(q->headers.producer));
+ if ((*index - 2) == le32_to_cpu(*(q->headers.consumer)))
+ *nonotify = 1;
+
+ if (qid == AdapHighCmdQueue) {
+ if (*index >= ADAP_HIGH_CMD_ENTRIES)
+ *index = 0;
+ } else if (qid == AdapNormCmdQueue) {
+ if (*index >= ADAP_NORM_CMD_ENTRIES)
+ *index = 0; /* Wrap to front of the Producer Queue. */
+ }
+ else if (qid == AdapHighRespQueue)
+ {
+ if (*index >= ADAP_HIGH_RESP_ENTRIES)
+ *index = 0;
+ }
+ else if (qid == AdapNormRespQueue)
+ {
+ if (*index >= ADAP_NORM_RESP_ENTRIES)
+ *index = 0; /* Wrap to front of the Producer Queue. */
+ }
+ else {
+ printk("aacraid: invalid qid\n");
+ BUG();
+ }
+
+ if ((*index + 1) == le32_to_cpu(*(q->headers.consumer))) { /* Queue is full */
+ printk(KERN_WARNING "Queue %d full, %d outstanding.\n",
+ qid, q->numpending);
+ return 0;
+ } else {
+ *entry = q->base + *index;
+ return 1;
+ }
+}
+
+/**
+ * aac_queue_get - get the next free QE
+ * @dev: Adapter
+ * @index: Returned index
+ * @priority: Priority of fib
+ * @fib: Fib to associate with the queue entry
+ * @wait: Wait if queue full
+ * @fibptr: Driver fib object to go with fib
+ * @nonotify: Don't notify the adapter
+ *
+ * Gets the next free QE off the requested priorty adapter command
+ * queue and associates the Fib with the QE. The QE represented by
+ * index is ready to insert on the queue when this routine returns
+ * success.
+ */
+
+static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw_fib, int wait, struct fib * fibptr, unsigned long *nonotify)
+{
+ struct aac_entry * entry = NULL;
+ int map = 0;
+ struct aac_queue * q = &dev->queues->queue[qid];
+
+ spin_lock_irqsave(q->lock, q->SavedIrql);
+
+ if (qid == AdapHighCmdQueue || qid == AdapNormCmdQueue)
+ {
+ /* if no entries wait for some if caller wants to */
+ while (!aac_get_entry(dev, qid, &entry, index, nonotify))
+ {
+ printk(KERN_ERR "GetEntries failed\n");
+ }
+ /*
+ * Setup queue entry with a command, status and fib mapped
+ */
+ entry->size = cpu_to_le32(le16_to_cpu(hw_fib->header.Size));
+ map = 1;
+ }
+ else if (qid == AdapHighRespQueue || qid == AdapNormRespQueue)
+ {
+ while(!aac_get_entry(dev, qid, &entry, index, nonotify))
+ {
+ /* if no entries wait for some if caller wants to */
+ }
+ /*
+ * Setup queue entry with command, status and fib mapped
+ */
+ entry->size = cpu_to_le32(le16_to_cpu(hw_fib->header.Size));
+ entry->addr = hw_fib->header.SenderFibAddress;
+ /* Restore adapters pointer to the FIB */
+ hw_fib->header.ReceiverFibAddress = hw_fib->header.SenderFibAddress; /* Let the adapter now where to find its data */
+ map = 0;
+ }
+ /*
+ * If MapFib is true than we need to map the Fib and put pointers
+ * in the queue entry.
+ */
+ if (map)
+ entry->addr = cpu_to_le32(fibptr->hw_fib_pa);
+ return 0;
+}
+
+
+/**
+ * aac_insert_entry - insert a queue entry
+ * @dev: Adapter
+ * @index: Index of entry to insert
+ * @qid: Queue number
+ * @nonotify: Suppress adapter notification
+ *
+ * Gets the next free QE off the requested priorty adapter command
+ * queue and associates the Fib with the QE. The QE represented by
+ * index is ready to insert on the queue when this routine returns
+ * success.
+ */
+
+static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned long nonotify)
+{
+ struct aac_queue * q = &dev->queues->queue[qid];
+
+ if(q == NULL)
+ BUG();
+ *(q->headers.producer) = cpu_to_le32(index + 1);
+ spin_unlock_irqrestore(q->lock, q->SavedIrql);
+
+ if (qid == AdapHighCmdQueue ||
+ qid == AdapNormCmdQueue ||
+ qid == AdapHighRespQueue ||
+ qid == AdapNormRespQueue)
+ {
+ if (!nonotify)
+ aac_adapter_notify(dev, qid);
+ }
+ else
+ printk("Suprise insert!\n");
+ return 0;
+}
+
+/*
+ * Define the highest level of host to adapter communication routines.
+ * These routines will support host to adapter FS commuication. These
+ * routines have no knowledge of the commuication method used. This level
+ * sends and receives FIBs. This level has no knowledge of how these FIBs
+ * get passed back and forth.
+ */
+
+/**
+ * fib_send - send a fib to the adapter
+ * @command: Command to send
+ * @fibptr: The fib
+ * @size: Size of fib data area
+ * @priority: Priority of Fib
+ * @wait: Async/sync select
+ * @reply: True if a reply is wanted
+ * @callback: Called with reply
+ * @callback_data: Passed to callback
+ *
+ * Sends the requested FIB to the adapter and optionally will wait for a
+ * response FIB. If the caller does not wish to wait for a response than
+ * an event to wait on must be supplied. This event will be set when a
+ * response FIB is received from the adapter.
+ */
+
+int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority, int wait, int reply, fib_callback callback, void * callback_data)
+{
+ u32 index;
+ u32 qid;
+ struct aac_dev * dev = fibptr->dev;
+ unsigned long nointr = 0;
+ struct hw_fib * hw_fib = fibptr->hw_fib;
+ struct aac_queue * q;
+ unsigned long flags = 0;
+ if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned)))
+ return -EBUSY;
+ /*
+ * There are 5 cases with the wait and reponse requested flags.
+ * The only invalid cases are if the caller requests to wait and
+ * does not request a response and if the caller does not want a
+ * response and the Fib is not allocated from pool. If a response
+ * is not requesed the Fib will just be deallocaed by the DPC
+ * routine when the response comes back from the adapter. No
+ * further processing will be done besides deleting the Fib. We
+ * will have a debug mode where the adapter can notify the host
+ * it had a problem and the host can log that fact.
+ */
+ if (wait && !reply) {
+ return -EINVAL;
+ } else if (!wait && reply) {
+ hw_fib->header.XferState |= cpu_to_le32(Async | ResponseExpected);
+ FIB_COUNTER_INCREMENT(aac_config.AsyncSent);
+ } else if (!wait && !reply) {
+ hw_fib->header.XferState |= cpu_to_le32(NoResponseExpected);
+ FIB_COUNTER_INCREMENT(aac_config.NoResponseSent);
+ } else if (wait && reply) {
+ hw_fib->header.XferState |= cpu_to_le32(ResponseExpected);
+ FIB_COUNTER_INCREMENT(aac_config.NormalSent);
+ }
+ /*
+ * Map the fib into 32bits by using the fib number
+ */
+
+ hw_fib->header.SenderFibAddress = cpu_to_le32(((u32)(fibptr-dev->fibs)) << 1);
+ hw_fib->header.SenderData = (u32)(fibptr - dev->fibs);
+ /*
+ * Set FIB state to indicate where it came from and if we want a
+ * response from the adapter. Also load the command from the
+ * caller.
+ *
+ * Map the hw fib pointer as a 32bit value
+ */
+ hw_fib->header.Command = cpu_to_le16(command);
+ hw_fib->header.XferState |= cpu_to_le32(SentFromHost);
+ fibptr->hw_fib->header.Flags = 0; /* 0 the flags field - internal only*/
+ /*
+ * Set the size of the Fib we want to send to the adapter
+ */
+ hw_fib->header.Size = cpu_to_le16(sizeof(struct aac_fibhdr) + size);
+ if (le16_to_cpu(hw_fib->header.Size) > le16_to_cpu(hw_fib->header.SenderSize)) {
+ return -EMSGSIZE;
+ }
+ /*
+ * Get a queue entry connect the FIB to it and send an notify
+ * the adapter a command is ready.
+ */
+ if (priority == FsaHigh) {
+ hw_fib->header.XferState |= cpu_to_le32(HighPriority);
+ qid = AdapHighCmdQueue;
+ } else {
+ hw_fib->header.XferState |= cpu_to_le32(NormalPriority);
+ qid = AdapNormCmdQueue;
+ }
+ q = &dev->queues->queue[qid];
+
+ if(wait)
+ spin_lock_irqsave(&fibptr->event_lock, flags);
+ if(aac_queue_get( dev, &index, qid, hw_fib, 1, fibptr, &nointr)<0)
+ return -EWOULDBLOCK;
+ dprintk((KERN_DEBUG "fib_send: inserting a queue entry at index %d.\n",index));
+ dprintk((KERN_DEBUG "Fib contents:.\n"));
+ dprintk((KERN_DEBUG " Command = %d.\n", hw_fib->header.Command));
+ dprintk((KERN_DEBUG " XferState = %x.\n", hw_fib->header.XferState));
+ dprintk((KERN_DEBUG " hw_fib va being sent=%p\n",fibptr->hw_fib));
+ dprintk((KERN_DEBUG " hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa));
+ dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr));
+ /*
+ * Fill in the Callback and CallbackContext if we are not
+ * going to wait.
+ */
+ if (!wait) {
+ fibptr->callback = callback;
+ fibptr->callback_data = callback_data;
+ }
+ FIB_COUNTER_INCREMENT(aac_config.FibsSent);
+ list_add_tail(&fibptr->queue, &q->pendingq);
+ q->numpending++;
+
+ fibptr->done = 0;
+ fibptr->flags = 0;
+
+ if(aac_insert_entry(dev, index, qid, (nointr & aac_config.irq_mod)) < 0)
+ return -EWOULDBLOCK;
+ /*
+ * If the caller wanted us to wait for response wait now.
+ */
+
+ if (wait) {
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ down(&fibptr->event_wait);
+ if(fibptr->done == 0)
+ BUG();
+
+ if((fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT)){
+ return -ETIMEDOUT;
+ } else {
+ return 0;
+ }
+ }
+ /*
+ * If the user does not want a response than return success otherwise
+ * return pending
+ */
+ if (reply)
+ return -EINPROGRESS;
+ else
+ return 0;
+}
+
+/**
+ * aac_consumer_get - get the top of the queue
+ * @dev: Adapter
+ * @q: Queue
+ * @entry: Return entry
+ *
+ * Will return a pointer to the entry on the top of the queue requested that
+ * we are a consumer of, and return the address of the queue entry. It does
+ * not change the state of the queue.
+ */
+
+int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry)
+{
+ u32 index;
+ int status;
+ if (le32_to_cpu(*q->headers.producer) == le32_to_cpu(*q->headers.consumer)) {
+ status = 0;
+ } else {
+ /*
+ * The consumer index must be wrapped if we have reached
+ * the end of the queue, else we just use the entry
+ * pointed to by the header index
+ */
+ if (le32_to_cpu(*q->headers.consumer) >= q->entries)
+ index = 0;
+ else
+ index = le32_to_cpu(*q->headers.consumer);
+ *entry = q->base + index;
+ status = 1;
+ }
+ return(status);
+}
+
+/**
+ * aac_consumer_free - free consumer entry
+ * @dev: Adapter
+ * @q: Queue
+ * @qid: Queue ident
+ *
+ * Frees up the current top of the queue we are a consumer of. If the
+ * queue was full notify the producer that the queue is no longer full.
+ */
+
+void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid)
+{
+ int wasfull = 0;
+ u32 notify;
+
+ if ((le32_to_cpu(*q->headers.producer)+1) == le32_to_cpu(*q->headers.consumer))
+ wasfull = 1;
+
+ if (le32_to_cpu(*q->headers.consumer) >= q->entries)
+ *q->headers.consumer = cpu_to_le32(1);
+ else
+ *q->headers.consumer = cpu_to_le32(le32_to_cpu(*q->headers.consumer)+1);
+
+ if (wasfull) {
+ switch (qid) {
+
+ case HostNormCmdQueue:
+ notify = HostNormCmdNotFull;
+ break;
+ case HostHighCmdQueue:
+ notify = HostHighCmdNotFull;
+ break;
+ case HostNormRespQueue:
+ notify = HostNormRespNotFull;
+ break;
+ case HostHighRespQueue:
+ notify = HostHighRespNotFull;
+ break;
+ default:
+ BUG();
+ return;
+ }
+ aac_adapter_notify(dev, notify);
+ }
+}
+
+/**
+ * fib_adapter_complete - complete adapter issued fib
+ * @fibptr: fib to complete
+ * @size: size of fib
+ *
+ * Will do all necessary work to complete a FIB that was sent from
+ * the adapter.
+ */
+
+int fib_adapter_complete(struct fib * fibptr, unsigned short size)
+{
+ struct hw_fib * hw_fib = fibptr->hw_fib;
+ struct aac_dev * dev = fibptr->dev;
+ unsigned long nointr = 0;
+ if (hw_fib->header.XferState == 0)
+ return 0;
+ /*
+ * If we plan to do anything check the structure type first.
+ */
+ if ( hw_fib->header.StructType != FIB_MAGIC ) {
+ return -EINVAL;
+ }
+ /*
+ * This block handles the case where the adapter had sent us a
+ * command and we have finished processing the command. We
+ * call completeFib when we are done processing the command
+ * and want to send a response back to the adapter. This will
+ * send the completed cdb to the adapter.
+ */
+ if (hw_fib->header.XferState & cpu_to_le32(SentFromAdapter)) {
+ hw_fib->header.XferState |= cpu_to_le32(HostProcessed);
+ if (hw_fib->header.XferState & cpu_to_le32(HighPriority)) {
+ u32 index;
+ if (size)
+ {
+ size += sizeof(struct aac_fibhdr);
+ if (size > le16_to_cpu(hw_fib->header.SenderSize))
+ return -EMSGSIZE;
+ hw_fib->header.Size = cpu_to_le16(size);
+ }
+ if(aac_queue_get(dev, &index, AdapHighRespQueue, hw_fib, 1, NULL, &nointr) < 0) {
+ return -EWOULDBLOCK;
+ }
+ if (aac_insert_entry(dev, index, AdapHighRespQueue, (nointr & (int)aac_config.irq_mod)) != 0) {
+ }
+ }
+ else if (hw_fib->header.XferState & NormalPriority)
+ {
+ u32 index;
+
+ if (size) {
+ size += sizeof(struct aac_fibhdr);
+ if (size > le16_to_cpu(hw_fib->header.SenderSize))
+ return -EMSGSIZE;
+ hw_fib->header.Size = cpu_to_le16(size);
+ }
+ if (aac_queue_get(dev, &index, AdapNormRespQueue, hw_fib, 1, NULL, &nointr) < 0)
+ return -EWOULDBLOCK;
+ if (aac_insert_entry(dev, index, AdapNormRespQueue, (nointr & (int)aac_config.irq_mod)) != 0)
+ {
+ }
+ }
+ }
+ else
+ {
+ printk(KERN_WARNING "fib_adapter_complete: Unknown xferstate detected.\n");
+ BUG();
+ }
+ return 0;
+}
+
+/**
+ * fib_complete - fib completion handler
+ * @fib: FIB to complete
+ *
+ * Will do all necessary work to complete a FIB.
+ */
+
+int fib_complete(struct fib * fibptr)
+{
+ struct hw_fib * hw_fib = fibptr->hw_fib;
+
+ /*
+ * Check for a fib which has already been completed
+ */
+
+ if (hw_fib->header.XferState == 0)
+ return 0;
+ /*
+ * If we plan to do anything check the structure type first.
+ */
+
+ if (hw_fib->header.StructType != FIB_MAGIC)
+ return -EINVAL;
+ /*
+ * This block completes a cdb which orginated on the host and we
+ * just need to deallocate the cdb or reinit it. At this point the
+ * command is complete that we had sent to the adapter and this
+ * cdb could be reused.
+ */
+ if((hw_fib->header.XferState & cpu_to_le32(SentFromHost)) &&
+ (hw_fib->header.XferState & cpu_to_le32(AdapterProcessed)))
+ {
+ fib_dealloc(fibptr);
+ }
+ else if(hw_fib->header.XferState & cpu_to_le32(SentFromHost))
+ {
+ /*
+ * This handles the case when the host has aborted the I/O
+ * to the adapter because the adapter is not responding
+ */
+ fib_dealloc(fibptr);
+ } else if(hw_fib->header.XferState & cpu_to_le32(HostOwned)) {
+ fib_dealloc(fibptr);
+ } else {
+ BUG();
+ }
+ return 0;
+}
+
+/**
+ * aac_printf - handle printf from firmware
+ * @dev: Adapter
+ * @val: Message info
+ *
+ * Print a message passed to us by the controller firmware on the
+ * Adaptec board
+ */
+
+void aac_printf(struct aac_dev *dev, u32 val)
+{
+ int length = val & 0xffff;
+ int level = (val >> 16) & 0xffff;
+ char *cp = dev->printfbuf;
+
+ /*
+ * The size of the printfbuf is set in port.c
+ * There is no variable or define for it
+ */
+ if (length > 255)
+ length = 255;
+ if (cp[length] != 0)
+ cp[length] = 0;
+ if (level == LOG_AAC_HIGH_ERROR)
+ printk(KERN_WARNING "aacraid:%s", cp);
+ else
+ printk(KERN_INFO "aacraid:%s", cp);
+ memset(cp, 0, 256);
+}
+
+/**
+ * aac_command_thread - command processing thread
+ * @dev: Adapter to monitor
+ *
+ * Waits on the commandready event in it's queue. When the event gets set
+ * it will pull FIBs off it's queue. It will continue to pull FIBs off
+ * until the queue is empty. When the queue is empty it will wait for
+ * more FIBs.
+ */
+
+int aac_command_thread(struct aac_dev * dev)
+{
+ struct hw_fib *hw_fib, *hw_newfib;
+ struct fib *fib, *newfib;
+ struct aac_queue_block *queues = dev->queues;
+ struct aac_fib_context *fibctx;
+ unsigned long flags;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * We can only have one thread per adapter for AIF's.
+ */
+ if (dev->aif_thread)
+ return -EINVAL;
+ /*
+ * Set up the name that will appear in 'ps'
+ * stored in task_struct.comm[16].
+ */
+ daemonize("aacraid");
+ allow_signal(SIGKILL);
+ /*
+ * Let the DPC know it has a place to send the AIF's to.
+ */
+ dev->aif_thread = 1;
+ add_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while(1)
+ {
+ spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
+ while(!list_empty(&(queues->queue[HostNormCmdQueue].cmdq))) {
+ struct list_head *entry;
+ struct aac_aifcmd * aifcmd;
+
+ set_current_state(TASK_RUNNING);
+
+ entry = queues->queue[HostNormCmdQueue].cmdq.next;
+ list_del(entry);
+
+ spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+ fib = list_entry(entry, struct fib, fiblink);
+ /*
+ * We will process the FIB here or pass it to a
+ * worker thread that is TBD. We Really can't
+ * do anything at this point since we don't have
+ * anything defined for this thread to do.
+ */
+ hw_fib = fib->hw_fib;
+ memset(fib, 0, sizeof(struct fib));
+ fib->type = FSAFS_NTC_FIB_CONTEXT;
+ fib->size = sizeof( struct fib );
+ fib->hw_fib = hw_fib;
+ fib->data = hw_fib->data;
+ fib->dev = dev;
+ /*
+ * We only handle AifRequest fibs from the adapter.
+ */
+ aifcmd = (struct aac_aifcmd *) hw_fib->data;
+ if (aifcmd->command == cpu_to_le32(AifCmdDriverNotify)) {
+ /* Handle Driver Notify Events */
+ *(u32 *)hw_fib->data = cpu_to_le32(ST_OK);
+ fib_adapter_complete(fib, sizeof(u32));
+ } else {
+ struct list_head *entry;
+ /* The u32 here is important and intended. We are using
+ 32bit wrapping time to fit the adapter field */
+
+ u32 time_now, time_last;
+ unsigned long flagv;
+
+ time_now = jiffies/HZ;
+
+ spin_lock_irqsave(&dev->fib_lock, flagv);
+ entry = dev->fib_list.next;
+ /*
+ * For each Context that is on the
+ * fibctxList, make a copy of the
+ * fib, and then set the event to wake up the
+ * thread that is waiting for it.
+ */
+ while (entry != &dev->fib_list) {
+ /*
+ * Extract the fibctx
+ */
+ fibctx = list_entry(entry, struct aac_fib_context, next);
+ /*
+ * Check if the queue is getting
+ * backlogged
+ */
+ if (fibctx->count > 20)
+ {
+ /*
+ * It's *not* jiffies folks,
+ * but jiffies / HZ so do not
+ * panic ...
+ */
+ time_last = fibctx->jiffies;
+ /*
+ * Has it been > 2 minutes
+ * since the last read off
+ * the queue?
+ */
+ if ((time_now - time_last) > 120) {
+ entry = entry->next;
+ aac_close_fib_context(dev, fibctx);
+ continue;
+ }
+ }
+ /*
+ * Warning: no sleep allowed while
+ * holding spinlock
+ */
+ hw_newfib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC);
+ newfib = kmalloc(sizeof(struct fib), GFP_ATOMIC);
+ if (newfib && hw_newfib) {
+ /*
+ * Make the copy of the FIB
+ */
+ memcpy(hw_newfib, hw_fib, sizeof(struct hw_fib));
+ memcpy(newfib, fib, sizeof(struct fib));
+ newfib->hw_fib = hw_newfib;
+ /*
+ * Put the FIB onto the
+ * fibctx's fibs
+ */
+ list_add_tail(&newfib->fiblink, &fibctx->fib_list);
+ fibctx->count++;
+ /*
+ * Set the event to wake up the
+ * thread that will waiting.
+ */
+ up(&fibctx->wait_sem);
+ } else {
+ printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
+ if(newfib)
+ kfree(newfib);
+ if(hw_newfib)
+ kfree(hw_newfib);
+ }
+ entry = entry->next;
+ }
+ /*
+ * Set the status of this FIB
+ */
+ *(u32 *)hw_fib->data = cpu_to_le32(ST_OK);
+ fib_adapter_complete(fib, sizeof(u32));
+ spin_unlock_irqrestore(&dev->fib_lock, flagv);
+ }
+ spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
+ kfree(fib);
+ }
+ /*
+ * There are no more AIF's
+ */
+ spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+ schedule();
+
+ if(signal_pending(current))
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ remove_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+ dev->aif_thread = 0;
+ complete_and_exit(&dev->aif_completion, 0);
+}
diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c
new file mode 100644
index 000000000000..8480b427a6d9
--- /dev/null
+++ b/drivers/scsi/aacraid/dpcsup.c
@@ -0,0 +1,215 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * dpcsup.c
+ *
+ * Abstract: All DPC processing routines for the cyclone board occur here.
+ *
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blkdev.h>
+#include <asm/semaphore.h>
+
+#include "aacraid.h"
+
+/**
+ * aac_response_normal - Handle command replies
+ * @q: Queue to read from
+ *
+ * This DPC routine will be run when the adapter interrupts us to let us
+ * know there is a response on our normal priority queue. We will pull off
+ * all QE there are and wake up all the waiters before exiting. We will
+ * take a spinlock out on the queue before operating on it.
+ */
+
+unsigned int aac_response_normal(struct aac_queue * q)
+{
+ struct aac_dev * dev = q->dev;
+ struct aac_entry *entry;
+ struct hw_fib * hwfib;
+ struct fib * fib;
+ int consumed = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->lock, flags);
+ /*
+ * Keep pulling response QEs off the response queue and waking
+ * up the waiters until there are no more QEs. We then return
+ * back to the system. If no response was requesed we just
+ * deallocate the Fib here and continue.
+ */
+ while(aac_consumer_get(dev, q, &entry))
+ {
+ int fast;
+ u32 index = le32_to_cpu(entry->addr);
+ fast = index & 0x01;
+ fib = &dev->fibs[index >> 1];
+ hwfib = fib->hw_fib;
+
+ aac_consumer_free(dev, q, HostNormRespQueue);
+ /*
+ * Remove this fib from the Outstanding I/O queue.
+ * But only if it has not already been timed out.
+ *
+ * If the fib has been timed out already, then just
+ * continue. The caller has already been notified that
+ * the fib timed out.
+ */
+ if (!(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
+ list_del(&fib->queue);
+ dev->queues->queue[AdapNormCmdQueue].numpending--;
+ } else {
+ printk(KERN_WARNING "aacraid: FIB timeout (%x).\n", fib->flags);
+ printk(KERN_DEBUG"aacraid: hwfib=%p fib index=%i fib=%p\n",hwfib, hwfib->header.SenderData,fib);
+ continue;
+ }
+ spin_unlock_irqrestore(q->lock, flags);
+
+ if (fast) {
+ /*
+ * Doctor the fib
+ */
+ *(u32 *)hwfib->data = cpu_to_le32(ST_OK);
+ hwfib->header.XferState |= cpu_to_le32(AdapterProcessed);
+ }
+
+ FIB_COUNTER_INCREMENT(aac_config.FibRecved);
+
+ if (hwfib->header.Command == cpu_to_le16(NuFileSystem))
+ {
+ u32 *pstatus = (u32 *)hwfib->data;
+ if (*pstatus & cpu_to_le32(0xffff0000))
+ *pstatus = cpu_to_le32(ST_OK);
+ }
+ if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected | Async))
+ {
+ if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected))
+ FIB_COUNTER_INCREMENT(aac_config.NoResponseRecved);
+ else
+ FIB_COUNTER_INCREMENT(aac_config.AsyncRecved);
+ /*
+ * NOTE: we cannot touch the fib after this
+ * call, because it may have been deallocated.
+ */
+ fib->callback(fib->callback_data, fib);
+ } else {
+ unsigned long flagv;
+ spin_lock_irqsave(&fib->event_lock, flagv);
+ fib->done = 1;
+ up(&fib->event_wait);
+ spin_unlock_irqrestore(&fib->event_lock, flagv);
+ FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
+ }
+ consumed++;
+ spin_lock_irqsave(q->lock, flags);
+ }
+
+ if (consumed > aac_config.peak_fibs)
+ aac_config.peak_fibs = consumed;
+ if (consumed == 0)
+ aac_config.zero_fibs++;
+
+ spin_unlock_irqrestore(q->lock, flags);
+ return 0;
+}
+
+
+/**
+ * aac_command_normal - handle commands
+ * @q: queue to process
+ *
+ * This DPC routine will be queued when the adapter interrupts us to
+ * let us know there is a command on our normal priority queue. We will
+ * pull off all QE there are and wake up all the waiters before exiting.
+ * We will take a spinlock out on the queue before operating on it.
+ */
+
+unsigned int aac_command_normal(struct aac_queue *q)
+{
+ struct aac_dev * dev = q->dev;
+ struct aac_entry *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->lock, flags);
+
+ /*
+ * Keep pulling response QEs off the response queue and waking
+ * up the waiters until there are no more QEs. We then return
+ * back to the system.
+ */
+ while(aac_consumer_get(dev, q, &entry))
+ {
+ struct fib fibctx;
+ struct hw_fib * hw_fib;
+ u32 index;
+ struct fib *fib = &fibctx;
+
+ index = le32_to_cpu(entry->addr) / sizeof(struct hw_fib);
+ hw_fib = &dev->aif_base_va[index];
+
+ /*
+ * Allocate a FIB at all costs. For non queued stuff
+ * we can just use the stack so we are happy. We need
+ * a fib object in order to manage the linked lists
+ */
+ if (dev->aif_thread)
+ if((fib = kmalloc(sizeof(struct fib), GFP_ATOMIC)) == NULL)
+ fib = &fibctx;
+
+ memset(fib, 0, sizeof(struct fib));
+ INIT_LIST_HEAD(&fib->fiblink);
+ fib->type = FSAFS_NTC_FIB_CONTEXT;
+ fib->size = sizeof(struct fib);
+ fib->hw_fib = hw_fib;
+ fib->data = hw_fib->data;
+ fib->dev = dev;
+
+
+ if (dev->aif_thread && fib != &fibctx) {
+ list_add_tail(&fib->fiblink, &q->cmdq);
+ aac_consumer_free(dev, q, HostNormCmdQueue);
+ wake_up_interruptible(&q->cmdready);
+ } else {
+ aac_consumer_free(dev, q, HostNormCmdQueue);
+ spin_unlock_irqrestore(q->lock, flags);
+ /*
+ * Set the status of this FIB
+ */
+ *(u32 *)hw_fib->data = cpu_to_le32(ST_OK);
+ fib_adapter_complete(fib, sizeof(u32));
+ spin_lock_irqsave(q->lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(q->lock, flags);
+ return 0;
+}
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
new file mode 100644
index 000000000000..c9b82687ba1a
--- /dev/null
+++ b/drivers/scsi/aacraid/linit.c
@@ -0,0 +1,749 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * linit.c
+ *
+ * Abstract: Linux Driver entry module for Adaptec RAID Array Controller
+ */
+
+#define AAC_DRIVER_VERSION "1.1.2-lk2"
+#define AAC_DRIVER_BUILD_DATE __DATE__
+#define AAC_DRIVERNAME "aacraid"
+
+#include <linux/compat.h>
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/syscalls.h>
+#include <linux/ioctl32.h>
+#include <linux/delay.h>
+#include <linux/smp_lock.h>
+#include <asm/semaphore.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_eh.h>
+
+#include "aacraid.h"
+
+
+MODULE_AUTHOR("Red Hat Inc and Adaptec");
+MODULE_DESCRIPTION("Dell PERC2, 2/Si, 3/Si, 3/Di, "
+ "Adaptec Advanced Raid Products, "
+ "and HP NetRAID-4M SCSI driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(AAC_DRIVER_VERSION);
+
+static LIST_HEAD(aac_devices);
+static int aac_cfg_major = -1;
+
+/*
+ * Because of the way Linux names scsi devices, the order in this table has
+ * become important. Check for on-board Raid first, add-in cards second.
+ *
+ * Note: The last field is used to index into aac_drivers below.
+ */
+static struct pci_device_id aac_pci_tbl[] = {
+ { 0x1028, 0x0001, 0x1028, 0x0001, 0, 0, 0 }, /* PERC 2/Si (Iguana/PERC2Si) */
+ { 0x1028, 0x0002, 0x1028, 0x0002, 0, 0, 1 }, /* PERC 3/Di (Opal/PERC3Di) */
+ { 0x1028, 0x0003, 0x1028, 0x0003, 0, 0, 2 }, /* PERC 3/Si (SlimFast/PERC3Si */
+ { 0x1028, 0x0004, 0x1028, 0x00d0, 0, 0, 3 }, /* PERC 3/Di (Iguana FlipChip/PERC3DiF */
+ { 0x1028, 0x0002, 0x1028, 0x00d1, 0, 0, 4 }, /* PERC 3/Di (Viper/PERC3DiV) */
+ { 0x1028, 0x0002, 0x1028, 0x00d9, 0, 0, 5 }, /* PERC 3/Di (Lexus/PERC3DiL) */
+ { 0x1028, 0x000a, 0x1028, 0x0106, 0, 0, 6 }, /* PERC 3/Di (Jaguar/PERC3DiJ) */
+ { 0x1028, 0x000a, 0x1028, 0x011b, 0, 0, 7 }, /* PERC 3/Di (Dagger/PERC3DiD) */
+ { 0x1028, 0x000a, 0x1028, 0x0121, 0, 0, 8 }, /* PERC 3/Di (Boxster/PERC3DiB) */
+ { 0x9005, 0x0283, 0x9005, 0x0283, 0, 0, 9 }, /* catapult */
+ { 0x9005, 0x0284, 0x9005, 0x0284, 0, 0, 10 }, /* tomcat */
+ { 0x9005, 0x0285, 0x9005, 0x0286, 0, 0, 11 }, /* Adaptec 2120S (Crusader) */
+ { 0x9005, 0x0285, 0x9005, 0x0285, 0, 0, 12 }, /* Adaptec 2200S (Vulcan) */
+ { 0x9005, 0x0285, 0x9005, 0x0287, 0, 0, 13 }, /* Adaptec 2200S (Vulcan-2m) */
+ { 0x9005, 0x0285, 0x17aa, 0x0286, 0, 0, 14 }, /* Legend S220 (Legend Crusader) */
+ { 0x9005, 0x0285, 0x17aa, 0x0287, 0, 0, 15 }, /* Legend S230 (Legend Vulcan) */
+
+ { 0x9005, 0x0285, 0x9005, 0x0288, 0, 0, 16 }, /* Adaptec 3230S (Harrier) */
+ { 0x9005, 0x0285, 0x9005, 0x0289, 0, 0, 17 }, /* Adaptec 3240S (Tornado) */
+ { 0x9005, 0x0285, 0x9005, 0x028a, 0, 0, 18 }, /* ASR-2020ZCR SCSI PCI-X ZCR (Skyhawk) */
+ { 0x9005, 0x0285, 0x9005, 0x028b, 0, 0, 19 }, /* ASR-2025ZCR SCSI SO-DIMM PCI-X ZCR (Terminator) */
+ { 0x9005, 0x0286, 0x9005, 0x028c, 0, 0, 20 }, /* ASR-2230S + ASR-2230SLP PCI-X (Lancer) */
+ { 0x9005, 0x0286, 0x9005, 0x028d, 0, 0, 21 }, /* ASR-2130S (Lancer) */
+ { 0x9005, 0x0286, 0x9005, 0x029b, 0, 0, 22 }, /* AAR-2820SA (Intruder) */
+ { 0x9005, 0x0286, 0x9005, 0x029c, 0, 0, 23 }, /* AAR-2620SA (Intruder) */
+ { 0x9005, 0x0286, 0x9005, 0x029d, 0, 0, 24 }, /* AAR-2420SA (Intruder) */
+ { 0x9005, 0x0286, 0x9005, 0x0800, 0, 0, 25 }, /* Callisto Jupiter Platform */
+ { 0x9005, 0x0285, 0x9005, 0x028e, 0, 0, 26 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */
+ { 0x9005, 0x0285, 0x9005, 0x028f, 0, 0, 27 }, /* ASR-2025SA SATA SO-DIMM PCI-X ZCR (Terminator) */
+ { 0x9005, 0x0285, 0x9005, 0x0290, 0, 0, 28 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II) */
+ { 0x9005, 0x0285, 0x1028, 0x0291, 0, 0, 29 }, /* CERC SATA RAID 2 PCI SATA 6ch (DellCorsair) */
+ { 0x9005, 0x0285, 0x9005, 0x0292, 0, 0, 30 }, /* AAR-2810SA PCI SATA 8ch (Corsair-8) */
+ { 0x9005, 0x0285, 0x9005, 0x0293, 0, 0, 31 }, /* AAR-21610SA PCI SATA 16ch (Corsair-16) */
+ { 0x9005, 0x0285, 0x9005, 0x0294, 0, 0, 32 }, /* ESD SO-DIMM PCI-X SATA ZCR (Prowler) */
+ { 0x9005, 0x0285, 0x103C, 0x3227, 0, 0, 33 }, /* AAR-2610SA PCI SATA 6ch */
+ { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 34 }, /* ASR-2240S (SabreExpress) */
+ { 0x9005, 0x0285, 0x9005, 0x0297, 0, 0, 35 }, /* ASR-4005SAS */
+ { 0x9005, 0x0285, 0x1014, 0x02F2, 0, 0, 36 }, /* IBM 8i (AvonPark) */
+ { 0x9005, 0x0285, 0x9005, 0x0298, 0, 0, 37 }, /* ASR-4000SAS (BlackBird) */
+ { 0x9005, 0x0285, 0x9005, 0x0299, 0, 0, 38 }, /* ASR-4800SAS (Marauder-X) */
+ { 0x9005, 0x0285, 0x9005, 0x029A, 0, 0, 39 }, /* ASR-4805SAS (Marauder-E) */
+
+ { 0x9005, 0x0285, 0x1028, 0x0287, 0, 0, 40 }, /* Perc 320/DC*/
+ { 0x1011, 0x0046, 0x9005, 0x0365, 0, 0, 41 }, /* Adaptec 5400S (Mustang)*/
+ { 0x1011, 0x0046, 0x9005, 0x0364, 0, 0, 42 }, /* Adaptec 5400S (Mustang)*/
+ { 0x1011, 0x0046, 0x9005, 0x1364, 0, 0, 43 }, /* Dell PERC2/QC */
+ { 0x1011, 0x0046, 0x103c, 0x10c2, 0, 0, 44 }, /* HP NetRAID-4M */
+
+ { 0x9005, 0x0285, 0x1028, PCI_ANY_ID, 0, 0, 45 }, /* Dell Catchall */
+ { 0x9005, 0x0285, 0x17aa, PCI_ANY_ID, 0, 0, 46 }, /* Legend Catchall */
+ { 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 47 }, /* Adaptec Catch All */
+ { 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 48 }, /* Adaptec Rocket Catch All */
+ { 0,}
+};
+MODULE_DEVICE_TABLE(pci, aac_pci_tbl);
+
+/*
+ * dmb - For now we add the number of channels to this structure.
+ * In the future we should add a fib that reports the number of channels
+ * for the card. At that time we can remove the channels from here
+ */
+static struct aac_driver_ident aac_drivers[] = {
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 2/Si (Iguana/PERC2Si) */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Opal/PERC3Di) */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Si (SlimFast/PERC3Si */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Iguana FlipChip/PERC3DiF */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Viper/PERC3DiV) */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Lexus/PERC3DiL) */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 1, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Jaguar/PERC3DiJ) */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Dagger/PERC3DiD) */
+ { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* PERC 3/Di (Boxster/PERC3DiB) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "catapult ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* catapult */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "tomcat ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* tomcat */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2120S ", 1, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Adaptec 2120S (Crusader) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Adaptec 2200S (Vulcan) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Adaptec 2200S (Vulcan-2m) */
+ { aac_rx_init, "aacraid", "Legend ", "Legend S220 ", 1, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Legend S220 (Legend Crusader) */
+ { aac_rx_init, "aacraid", "Legend ", "Legend S230 ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Legend S230 (Legend Vulcan) */
+
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 3230S ", 2 }, /* Adaptec 3230S (Harrier) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 3240S ", 2 }, /* Adaptec 3240S (Tornado) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020ZCR ", 2 }, /* ASR-2020ZCR SCSI PCI-X ZCR (Skyhawk) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2025ZCR ", 2 }, /* ASR-2025ZCR SCSI SO-DIMM PCI-X ZCR (Terminator) */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "ASR-2230S PCI-X ", 2 }, /* ASR-2230S + ASR-2230SLP PCI-X (Lancer) */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "ASR-2130S PCI-X ", 1 }, /* ASR-2130S (Lancer) */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "AAR-2820SA ", 1 }, /* AAR-2820SA (Intruder) */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "AAR-2620SA ", 1 }, /* AAR-2620SA (Intruder) */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "AAR-2420SA ", 1 }, /* AAR-2420SA (Intruder) */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "Callisto ", 2, AAC_QUIRK_MASTER }, /* Jupiter Platform */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020SA ", 1 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2025SA ", 1 }, /* ASR-2025SA SATA SO-DIMM PCI-X ZCR (Terminator) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2410SA SATA ", 1 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II) */
+ { aac_rx_init, "aacraid", "DELL ", "CERC SR2 ", 1 }, /* CERC SATA RAID 2 PCI SATA 6ch (DellCorsair) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2810SA SATA ", 1 }, /* AAR-2810SA PCI SATA 8ch (Corsair-8) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-21610SA SATA", 1 }, /* AAR-21610SA PCI SATA 16ch (Corsair-16) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2026ZCR ", 1 }, /* ESD SO-DIMM PCI-X SATA ZCR (Prowler) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2610SA ", 1 }, /* SATA 6Ch (Bearcat) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2240S ", 1 }, /* ASR-2240S (SabreExpress) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4005SAS ", 1 }, /* ASR-4005SAS */
+ { aac_rx_init, "aacraid", "IBM ", "ServeRAID 8i ", 1 }, /* IBM 8i (AvonPark) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4000SAS ", 1 }, /* ASR-4000SAS (BlackBird & AvonPark) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4800SAS ", 1 }, /* ASR-4800SAS (Marauder-X) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4805SAS ", 1 }, /* ASR-4805SAS (Marauder-E) */
+
+ { aac_rx_init, "percraid", "DELL ", "PERC 320/DC ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Perc 320/DC*/
+ { aac_sa_init, "aacraid", "ADAPTEC ", "Adaptec 5400S ", 4, AAC_QUIRK_34SG }, /* Adaptec 5400S (Mustang)*/
+ { aac_sa_init, "aacraid", "ADAPTEC ", "AAC-364 ", 4, AAC_QUIRK_34SG }, /* Adaptec 5400S (Mustang)*/
+ { aac_sa_init, "percraid", "DELL ", "PERCRAID ", 4, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Dell PERC2/QC */
+ { aac_sa_init, "hpnraid", "HP ", "NetRAID ", 4, AAC_QUIRK_34SG }, /* HP NetRAID-4M */
+
+ { aac_rx_init, "aacraid", "DELL ", "RAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Dell Catchall */
+ { aac_rx_init, "aacraid", "Legend ", "RAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Legend Catchall */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Adaptec Catch All */
+ { aac_rkt_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec Rocket Catch All */
+};
+
+/**
+ * aac_queuecommand - queue a SCSI command
+ * @cmd: SCSI command to queue
+ * @done: Function to call on command completion
+ *
+ * Queues a command for execution by the associated Host Adapter.
+ *
+ * TODO: unify with aac_scsi_cmd().
+ */
+
+static int aac_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ cmd->scsi_done = done;
+ return (aac_scsi_cmd(cmd) ? FAILED : 0);
+}
+
+/**
+ * aac_info - Returns the host adapter name
+ * @shost: Scsi host to report on
+ *
+ * Returns a static string describing the device in question
+ */
+
+const char *aac_info(struct Scsi_Host *shost)
+{
+ struct aac_dev *dev = (struct aac_dev *)shost->hostdata;
+ return aac_drivers[dev->cardtype].name;
+}
+
+/**
+ * aac_get_driver_ident
+ * @devtype: index into lookup table
+ *
+ * Returns a pointer to the entry in the driver lookup table.
+ */
+
+struct aac_driver_ident* aac_get_driver_ident(int devtype)
+{
+ return &aac_drivers[devtype];
+}
+
+/**
+ * aac_biosparm - return BIOS parameters for disk
+ * @sdev: The scsi device corresponding to the disk
+ * @bdev: the block device corresponding to the disk
+ * @capacity: the sector capacity of the disk
+ * @geom: geometry block to fill in
+ *
+ * Return the Heads/Sectors/Cylinders BIOS Disk Parameters for Disk.
+ * The default disk geometry is 64 heads, 32 sectors, and the appropriate
+ * number of cylinders so as not to exceed drive capacity. In order for
+ * disks equal to or larger than 1 GB to be addressable by the BIOS
+ * without exceeding the BIOS limitation of 1024 cylinders, Extended
+ * Translation should be enabled. With Extended Translation enabled,
+ * drives between 1 GB inclusive and 2 GB exclusive are given a disk
+ * geometry of 128 heads and 32 sectors, and drives above 2 GB inclusive
+ * are given a disk geometry of 255 heads and 63 sectors. However, if
+ * the BIOS detects that the Extended Translation setting does not match
+ * the geometry in the partition table, then the translation inferred
+ * from the partition table will be used by the BIOS, and a warning may
+ * be displayed.
+ */
+
+static int aac_biosparm(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *geom)
+{
+ struct diskparm *param = (struct diskparm *)geom;
+ unsigned char *buf;
+
+ dprintk((KERN_DEBUG "aac_biosparm.\n"));
+
+ /*
+ * Assuming extended translation is enabled - #REVISIT#
+ */
+ if (capacity >= 2 * 1024 * 1024) { /* 1 GB in 512 byte sectors */
+ if(capacity >= 4 * 1024 * 1024) { /* 2 GB in 512 byte sectors */
+ param->heads = 255;
+ param->sectors = 63;
+ } else {
+ param->heads = 128;
+ param->sectors = 32;
+ }
+ } else {
+ param->heads = 64;
+ param->sectors = 32;
+ }
+
+ param->cylinders = cap_to_cyls(capacity, param->heads * param->sectors);
+
+ /*
+ * Read the first 1024 bytes from the disk device, if the boot
+ * sector partition table is valid, search for a partition table
+ * entry whose end_head matches one of the standard geometry
+ * translations ( 64/32, 128/32, 255/63 ).
+ */
+ buf = scsi_bios_ptable(bdev);
+ if(*(unsigned short *)(buf + 0x40) == cpu_to_le16(0xaa55)) {
+ struct partition *first = (struct partition * )buf;
+ struct partition *entry = first;
+ int saved_cylinders = param->cylinders;
+ int num;
+ unsigned char end_head, end_sec;
+
+ for(num = 0; num < 4; num++) {
+ end_head = entry->end_head;
+ end_sec = entry->end_sector & 0x3f;
+
+ if(end_head == 63) {
+ param->heads = 64;
+ param->sectors = 32;
+ break;
+ } else if(end_head == 127) {
+ param->heads = 128;
+ param->sectors = 32;
+ break;
+ } else if(end_head == 254) {
+ param->heads = 255;
+ param->sectors = 63;
+ break;
+ }
+ entry++;
+ }
+
+ if (num == 4) {
+ end_head = first->end_head;
+ end_sec = first->end_sector & 0x3f;
+ }
+
+ param->cylinders = cap_to_cyls(capacity, param->heads * param->sectors);
+ if (num < 4 && end_sec == param->sectors) {
+ if (param->cylinders != saved_cylinders)
+ dprintk((KERN_DEBUG "Adopting geometry: heads=%d, sectors=%d from partition table %d.\n",
+ param->heads, param->sectors, num));
+ } else if (end_head > 0 || end_sec > 0) {
+ dprintk((KERN_DEBUG "Strange geometry: heads=%d, sectors=%d in partition table %d.\n",
+ end_head + 1, end_sec, num));
+ dprintk((KERN_DEBUG "Using geometry: heads=%d, sectors=%d.\n",
+ param->heads, param->sectors));
+ }
+ }
+ kfree(buf);
+ return 0;
+}
+
+/**
+ * aac_slave_configure - compute queue depths
+ * @sdev: SCSI device we are considering
+ *
+ * Selects queue depths for each target device based on the host adapter's
+ * total capacity and the queue depth supported by the target device.
+ * A queue depth of one automatically disables tagged queueing.
+ */
+
+static int aac_slave_configure(struct scsi_device *sdev)
+{
+ if (sdev->tagged_supported)
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, 128);
+ else
+ scsi_adjust_queue_depth(sdev, 0, 1);
+ return 0;
+}
+
+static int aac_ioctl(struct scsi_device *sdev, int cmd, void __user * arg)
+{
+ struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
+ return aac_do_ioctl(dev, cmd, arg);
+}
+
+/*
+ * XXX: does aac really need no error handling??
+ */
+static int aac_eh_abort(struct scsi_cmnd *cmd)
+{
+ return FAILED;
+}
+
+/*
+ * aac_eh_reset - Reset command handling
+ * @scsi_cmd: SCSI command block causing the reset
+ *
+ */
+static int aac_eh_reset(struct scsi_cmnd* cmd)
+{
+ struct scsi_device * dev = cmd->device;
+ struct Scsi_Host * host = dev->host;
+ struct scsi_cmnd * command;
+ int count;
+ struct aac_dev * aac;
+ unsigned long flags;
+
+ printk(KERN_ERR "%s: Host adapter reset request. SCSI hang ?\n",
+ AAC_DRIVERNAME);
+
+
+ aac = (struct aac_dev *)host->hostdata;
+ if (aac_adapter_check_health(aac)) {
+ printk(KERN_ERR "%s: Host adapter appears dead\n",
+ AAC_DRIVERNAME);
+ return -ENODEV;
+ }
+ /*
+ * Wait for all commands to complete to this specific
+ * target (block maximum 60 seconds).
+ */
+ for (count = 60; count; --count) {
+ int active = 0;
+ __shost_for_each_device(dev, host) {
+ spin_lock_irqsave(&dev->list_lock, flags);
+ list_for_each_entry(command, &dev->cmd_list, list) {
+ if (command->serial_number) {
+ active++;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dev->list_lock, flags);
+ if (active)
+ break;
+
+ }
+ /*
+ * We can exit If all the commands are complete
+ */
+ if (active == 0)
+ return SUCCESS;
+ spin_unlock_irq(host->host_lock);
+ ssleep(1);
+ spin_lock_irq(host->host_lock);
+ }
+ printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME);
+ return -ETIMEDOUT;
+}
+
+/**
+ * aac_cfg_open - open a configuration file
+ * @inode: inode being opened
+ * @file: file handle attached
+ *
+ * Called when the configuration device is opened. Does the needed
+ * set up on the handle and then returns
+ *
+ * Bugs: This needs extending to check a given adapter is present
+ * so we can support hot plugging, and to ref count adapters.
+ */
+
+static int aac_cfg_open(struct inode *inode, struct file *file)
+{
+ struct aac_dev *aac;
+ unsigned minor = iminor(inode);
+ int err = -ENODEV;
+
+ list_for_each_entry(aac, &aac_devices, entry) {
+ if (aac->id == minor) {
+ file->private_data = aac;
+ err = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * aac_cfg_ioctl - AAC configuration request
+ * @inode: inode of device
+ * @file: file handle
+ * @cmd: ioctl command code
+ * @arg: argument
+ *
+ * Handles a configuration ioctl. Currently this involves wrapping it
+ * up and feeding it into the nasty windowsalike glue layer.
+ *
+ * Bugs: Needs locking against parallel ioctls lower down
+ * Bugs: Needs to handle hot plugging
+ */
+
+static int aac_cfg_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return aac_do_ioctl(file->private_data, cmd, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long aac_compat_do_ioctl(struct aac_dev *dev, unsigned cmd, unsigned long arg)
+{
+ long ret;
+ lock_kernel();
+ switch (cmd) {
+ case FSACTL_MINIPORT_REV_CHECK:
+ case FSACTL_SENDFIB:
+ case FSACTL_OPEN_GET_ADAPTER_FIB:
+ case FSACTL_CLOSE_GET_ADAPTER_FIB:
+ case FSACTL_SEND_RAW_SRB:
+ case FSACTL_GET_PCI_INFO:
+ case FSACTL_QUERY_DISK:
+ case FSACTL_DELETE_DISK:
+ case FSACTL_FORCE_DELETE_DISK:
+ case FSACTL_GET_CONTAINERS:
+ ret = aac_do_ioctl(dev, cmd, (void __user *)arg);
+ break;
+
+ case FSACTL_GET_NEXT_ADAPTER_FIB: {
+ struct fib_ioctl __user *f;
+
+ f = compat_alloc_user_space(sizeof(*f));
+ ret = 0;
+ if (clear_user(f, sizeof(*f) != sizeof(*f)))
+ ret = -EFAULT;
+ if (copy_in_user(f, (void __user *)arg, sizeof(struct fib_ioctl) - sizeof(u32)))
+ ret = -EFAULT;
+ if (!ret)
+ ret = aac_do_ioctl(dev, cmd, (void __user *)arg);
+ break;
+ }
+
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ unlock_kernel();
+ return ret;
+}
+
+static int aac_compat_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+{
+ struct aac_dev *dev = (struct aac_dev *)sdev->host->hostdata;
+ return aac_compat_do_ioctl(dev, cmd, (unsigned long)arg);
+}
+
+static long aac_compat_cfg_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ return aac_compat_do_ioctl((struct aac_dev *)file->private_data, cmd, arg);
+}
+#endif
+
+static struct file_operations aac_cfg_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = aac_cfg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = aac_compat_cfg_ioctl,
+#endif
+ .open = aac_cfg_open,
+};
+
+static struct scsi_host_template aac_driver_template = {
+ .module = THIS_MODULE,
+ .name = "AAC",
+ .proc_name = "aacraid",
+ .info = aac_info,
+ .ioctl = aac_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = aac_compat_ioctl,
+#endif
+ .queuecommand = aac_queuecommand,
+ .bios_param = aac_biosparm,
+ .slave_configure = aac_slave_configure,
+ .eh_abort_handler = aac_eh_abort,
+ .eh_host_reset_handler = aac_eh_reset,
+ .can_queue = AAC_NUM_IO_FIB,
+ .this_id = 16,
+ .sg_tablesize = 16,
+ .max_sectors = 128,
+#if (AAC_NUM_IO_FIB > 256)
+ .cmd_per_lun = 256,
+#else
+ .cmd_per_lun = AAC_NUM_IO_FIB,
+#endif
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+
+static int __devinit aac_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ unsigned index = id->driver_data;
+ struct Scsi_Host *shost;
+ struct aac_dev *aac;
+ struct list_head *insert = &aac_devices;
+ int error = -ENODEV;
+ int unique_id = 0;
+
+ list_for_each_entry(aac, &aac_devices, entry) {
+ if (aac->id > unique_id)
+ break;
+ insert = &aac->entry;
+ unique_id++;
+ }
+
+ if (pci_enable_device(pdev))
+ goto out;
+
+ if (pci_set_dma_mask(pdev, 0xFFFFFFFFULL) ||
+ pci_set_consistent_dma_mask(pdev, 0xFFFFFFFFULL))
+ goto out;
+ /*
+ * If the quirk31 bit is set, the adapter needs adapter
+ * to driver communication memory to be allocated below 2gig
+ */
+ if (aac_drivers[index].quirks & AAC_QUIRK_31BIT)
+ if (pci_set_dma_mask(pdev, 0x7FFFFFFFULL) ||
+ pci_set_consistent_dma_mask(pdev, 0x7FFFFFFFULL))
+ goto out;
+
+ pci_set_master(pdev);
+
+ shost = scsi_host_alloc(&aac_driver_template, sizeof(struct aac_dev));
+ if (!shost)
+ goto out_disable_pdev;
+
+ shost->irq = pdev->irq;
+ shost->base = pci_resource_start(pdev, 0);
+ shost->unique_id = unique_id;
+
+ aac = (struct aac_dev *)shost->hostdata;
+ aac->scsi_host_ptr = shost;
+ aac->pdev = pdev;
+ aac->name = aac_driver_template.name;
+ aac->id = shost->unique_id;
+ aac->cardtype = index;
+ INIT_LIST_HEAD(&aac->entry);
+
+ aac->fibs = kmalloc(sizeof(struct fib) * AAC_NUM_FIB, GFP_KERNEL);
+ if (!aac->fibs)
+ goto out_free_host;
+ spin_lock_init(&aac->fib_lock);
+
+ if ((*aac_drivers[index].init)(aac))
+ goto out_free_fibs;
+
+ /*
+ * If we had set a smaller DMA mask earlier, set it to 4gig
+ * now since the adapter can dma data to at least a 4gig
+ * address space.
+ */
+ if (aac_drivers[index].quirks & AAC_QUIRK_31BIT)
+ if (pci_set_dma_mask(pdev, 0xFFFFFFFFULL))
+ goto out_free_fibs;
+
+ aac_get_adapter_info(aac);
+
+ /*
+ * max channel will be the physical channels plus 1 virtual channel
+ * all containers are on the virtual channel 0
+ * physical channels are address by their actual physical number+1
+ */
+ if (aac->nondasd_support == 1)
+ shost->max_channel = aac_drivers[index].channels+1;
+ else
+ shost->max_channel = 1;
+
+ aac_get_config_status(aac);
+ aac_get_containers(aac);
+ list_add(&aac->entry, insert);
+
+ shost->max_id = aac->maximum_num_containers;
+ if (shost->max_id < MAXIMUM_NUM_CONTAINERS)
+ shost->max_id = MAXIMUM_NUM_CONTAINERS;
+ else
+ shost->this_id = shost->max_id;
+
+ /*
+ * dmb - we may need to move the setting of these parms somewhere else once
+ * we get a fib that can report the actual numbers
+ */
+ shost->max_lun = AAC_MAX_LUN;
+
+ pci_set_drvdata(pdev, shost);
+
+ error = scsi_add_host(shost, &pdev->dev);
+ if (error)
+ goto out_deinit;
+ scsi_scan_host(shost);
+
+ return 0;
+
+out_deinit:
+ kill_proc(aac->thread_pid, SIGKILL, 0);
+ wait_for_completion(&aac->aif_completion);
+
+ aac_send_shutdown(aac);
+ fib_map_free(aac);
+ pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys);
+ kfree(aac->queues);
+ free_irq(pdev->irq, aac);
+ iounmap(aac->regs.sa);
+ out_free_fibs:
+ kfree(aac->fibs);
+ kfree(aac->fsa_dev);
+ out_free_host:
+ scsi_host_put(shost);
+ out_disable_pdev:
+ pci_disable_device(pdev);
+ out:
+ return error;
+}
+
+static void __devexit aac_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct aac_dev *aac = (struct aac_dev *)shost->hostdata;
+
+ scsi_remove_host(shost);
+
+ kill_proc(aac->thread_pid, SIGKILL, 0);
+ wait_for_completion(&aac->aif_completion);
+
+ aac_send_shutdown(aac);
+ fib_map_free(aac);
+ pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr,
+ aac->comm_phys);
+ kfree(aac->queues);
+
+ free_irq(pdev->irq, aac);
+ iounmap(aac->regs.sa);
+
+ kfree(aac->fibs);
+
+ list_del(&aac->entry);
+ scsi_host_put(shost);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver aac_pci_driver = {
+ .name = AAC_DRIVERNAME,
+ .id_table = aac_pci_tbl,
+ .probe = aac_probe_one,
+ .remove = __devexit_p(aac_remove_one),
+};
+
+static int __init aac_init(void)
+{
+ int error;
+
+ printk(KERN_INFO "Red Hat/Adaptec aacraid driver (%s %s)\n",
+ AAC_DRIVER_VERSION, AAC_DRIVER_BUILD_DATE);
+
+ error = pci_module_init(&aac_pci_driver);
+ if (error)
+ return error;
+
+ aac_cfg_major = register_chrdev( 0, "aac", &aac_cfg_fops);
+ if (aac_cfg_major < 0) {
+ printk(KERN_WARNING
+ "aacraid: unable to register \"aac\" device.\n");
+ }
+ return 0;
+}
+
+static void __exit aac_exit(void)
+{
+ unregister_chrdev(aac_cfg_major, "aac");
+ pci_unregister_driver(&aac_pci_driver);
+}
+
+module_init(aac_init);
+module_exit(aac_exit);
diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c
new file mode 100644
index 000000000000..1b8ed47cfe30
--- /dev/null
+++ b/drivers/scsi/aacraid/rkt.c
@@ -0,0 +1,440 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * rkt.c
+ *
+ * Abstract: Hardware miniport for Drawbridge specific hardware functions.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <asm/semaphore.h>
+
+#include <scsi/scsi_host.h>
+
+#include "aacraid.h"
+
+static irqreturn_t aac_rkt_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aac_dev *dev = dev_id;
+ unsigned long bellbits;
+ u8 intstat, mask;
+ intstat = rkt_readb(dev, MUnit.OISR);
+ /*
+ * Read mask and invert because drawbridge is reversed.
+ * This allows us to only service interrupts that have
+ * been enabled.
+ */
+ mask = ~(dev->OIMR);
+ /* Check to see if this is our interrupt. If it isn't just return */
+ if (intstat & mask)
+ {
+ bellbits = rkt_readl(dev, OutboundDoorbellReg);
+ if (bellbits & DoorBellPrintfReady) {
+ aac_printf(dev, rkt_readl(dev, IndexRegs.Mailbox[5]));
+ rkt_writel(dev, MUnit.ODR,DoorBellPrintfReady);
+ rkt_writel(dev, InboundDoorbellReg,DoorBellPrintfDone);
+ }
+ else if (bellbits & DoorBellAdapterNormCmdReady) {
+ rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady);
+ aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+ }
+ else if (bellbits & DoorBellAdapterNormRespReady) {
+ aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+ rkt_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady);
+ }
+ else if (bellbits & DoorBellAdapterNormCmdNotFull) {
+ rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+ }
+ else if (bellbits & DoorBellAdapterNormRespNotFull) {
+ rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+ rkt_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/**
+ * rkt_sync_cmd - send a command and wait
+ * @dev: Adapter
+ * @command: Command to execute
+ * @p1: first parameter
+ * @ret: adapter status
+ *
+ * This routine will send a synchronous command to the adapter and wait
+ * for its completion.
+ */
+
+static int rkt_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *status)
+{
+ unsigned long start;
+ int ok;
+ /*
+ * Write the command into Mailbox 0
+ */
+ rkt_writel(dev, InboundMailbox0, command);
+ /*
+ * Write the parameters into Mailboxes 1 - 4
+ */
+ rkt_writel(dev, InboundMailbox1, p1);
+ rkt_writel(dev, InboundMailbox2, 0);
+ rkt_writel(dev, InboundMailbox3, 0);
+ rkt_writel(dev, InboundMailbox4, 0);
+ /*
+ * Clear the synch command doorbell to start on a clean slate.
+ */
+ rkt_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ /*
+ * Disable doorbell interrupts
+ */
+ rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff);
+ /*
+ * Force the completion of the mask register write before issuing
+ * the interrupt.
+ */
+ rkt_readb (dev, MUnit.OIMR);
+ /*
+ * Signal that there is a new synch command
+ */
+ rkt_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0);
+
+ ok = 0;
+ start = jiffies;
+
+ /*
+ * Wait up to 30 seconds
+ */
+ while (time_before(jiffies, start+30*HZ))
+ {
+ udelay(5); /* Delay 5 microseconds to let Mon960 get info. */
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the command.
+ */
+ if (rkt_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear the doorbell.
+ */
+ rkt_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ ok = 1;
+ break;
+ }
+ /*
+ * Yield the processor in case we are slow
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (ok != 1) {
+ /*
+ * Restore interrupt mask even though we timed out
+ */
+ rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb);
+ return -ETIMEDOUT;
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ if (status)
+ *status = rkt_readl(dev, IndexRegs.Mailbox[0]);
+ /*
+ * Clear the synch command doorbell.
+ */
+ rkt_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ /*
+ * Restore interrupt mask
+ */
+ rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb);
+ return 0;
+
+}
+
+/**
+ * aac_rkt_interrupt_adapter - interrupt adapter
+ * @dev: Adapter
+ *
+ * Send an interrupt to the i960 and breakpoint it.
+ */
+
+static void aac_rkt_interrupt_adapter(struct aac_dev *dev)
+{
+ u32 ret;
+ rkt_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ * aac_rkt_notify_adapter - send an event to the adapter
+ * @dev: Adapter
+ * @event: Event to send
+ *
+ * Notify the i960 that something it probably cares about has
+ * happened.
+ */
+
+static void aac_rkt_notify_adapter(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case AdapNormCmdQue:
+ rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1);
+ break;
+ case HostNormRespNotFull:
+ rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4);
+ break;
+ case AdapNormRespQue:
+ rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2);
+ break;
+ case HostNormCmdNotFull:
+ rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3);
+ break;
+ case HostShutdown:
+// rkt_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, &ret);
+ break;
+ case FastIo:
+ rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6);
+ break;
+ case AdapPrintfDone:
+ rkt_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+/**
+ * aac_rkt_start_adapter - activate adapter
+ * @dev: Adapter
+ *
+ * Start up processing on an i960 based AAC adapter
+ */
+
+static void aac_rkt_start_adapter(struct aac_dev *dev)
+{
+ u32 status;
+ struct aac_init *init;
+
+ init = dev->init;
+ init->HostElapsedSeconds = cpu_to_le32(get_seconds());
+ /*
+ * Tell the adapter we are back and up and running so it will scan
+ * its command queues and enable our interrupts
+ */
+ dev->irq_mask = (DoorBellPrintfReady | OUTBOUNDDOORBELL_1 | OUTBOUNDDOORBELL_2 | OUTBOUNDDOORBELL_3 | OUTBOUNDDOORBELL_4);
+ /*
+ * First clear out all interrupts. Then enable the one's that we
+ * can handle.
+ */
+ rkt_writeb(dev, MUnit.OIMR, 0xff);
+ rkt_writel(dev, MUnit.ODR, 0xffffffff);
+// rkt_writeb(dev, MUnit.OIMR, ~(u8)OUTBOUND_DOORBELL_INTERRUPT_MASK);
+ rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb);
+
+ // We can only use a 32 bit address here
+ rkt_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &status);
+}
+
+/**
+ * aac_rkt_check_health
+ * @dev: device to check if healthy
+ *
+ * Will attempt to determine if the specified adapter is alive and
+ * capable of handling requests, returning 0 if alive.
+ */
+static int aac_rkt_check_health(struct aac_dev *dev)
+{
+ u32 status = rkt_readl(dev, MUnit.OMRx[0]);
+
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (status & SELF_TEST_FAILED)
+ return -1;
+ /*
+ * Check to see if the board panic'd.
+ */
+ if (status & KERNEL_PANIC) {
+ char * buffer;
+ struct POSTSTATUS {
+ u32 Post_Command;
+ u32 Post_Address;
+ } * post;
+ dma_addr_t paddr, baddr;
+ int ret;
+
+ if ((status & 0xFF000000L) == 0xBC000000L)
+ return (status >> 16) & 0xFF;
+ buffer = pci_alloc_consistent(dev->pdev, 512, &baddr);
+ ret = -2;
+ if (buffer == NULL)
+ return ret;
+ post = pci_alloc_consistent(dev->pdev,
+ sizeof(struct POSTSTATUS), &paddr);
+ if (post == NULL) {
+ pci_free_consistent(dev->pdev, 512, buffer, baddr);
+ return ret;
+ }
+ memset(buffer, 0, 512);
+ post->Post_Command = cpu_to_le32(COMMAND_POST_RESULTS);
+ post->Post_Address = cpu_to_le32(baddr);
+ rkt_writel(dev, MUnit.IMRx[0], paddr);
+ rkt_sync_cmd(dev, COMMAND_POST_RESULTS, baddr, &status);
+ pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS),
+ post, paddr);
+ if ((buffer[0] == '0') && (buffer[1] == 'x')) {
+ ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10);
+ ret <<= 4;
+ ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10);
+ }
+ pci_free_consistent(dev->pdev, 512, buffer, baddr);
+ return ret;
+ }
+ /*
+ * Wait for the adapter to be up and running.
+ */
+ if (!(status & KERNEL_UP_AND_RUNNING))
+ return -3;
+ /*
+ * Everything is OK
+ */
+ return 0;
+}
+
+/**
+ * aac_rkt_init - initialize an i960 based AAC card
+ * @dev: device to configure
+ *
+ * Allocate and set up resources for the i960 based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
+ * to the comm region.
+ */
+
+int aac_rkt_init(struct aac_dev *dev)
+{
+ unsigned long start;
+ unsigned long status;
+ int instance;
+ const char * name;
+
+ instance = dev->id;
+ name = dev->name;
+
+ /*
+ * Map in the registers from the adapter.
+ */
+ if((dev->regs.rkt = ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+ {
+ printk(KERN_WARNING "aacraid: unable to map i960.\n" );
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (rkt_readl(dev, MUnit.OMRx[0]) & SELF_TEST_FAILED) {
+ printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the monitor panic'd while booting.
+ */
+ if (rkt_readl(dev, MUnit.OMRx[0]) & MONITOR_PANIC) {
+ printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) {
+ printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes
+ */
+ while (!(rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_UP_AND_RUNNING))
+ {
+ if(time_after(jiffies, start+180*HZ))
+ {
+ status = rkt_readl(dev, MUnit.OMRx[0]);
+ printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n",
+ dev->name, instance, status);
+ goto error_iounmap;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (request_irq(dev->scsi_host_ptr->irq, aac_rkt_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev)<0)
+ {
+ printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Fill in the function dispatch table.
+ */
+ dev->a_ops.adapter_interrupt = aac_rkt_interrupt_adapter;
+ dev->a_ops.adapter_notify = aac_rkt_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = rkt_sync_cmd;
+ dev->a_ops.adapter_check_health = aac_rkt_check_health;
+
+ if (aac_init_adapter(dev) == NULL)
+ goto error_irq;
+ /*
+ * Start any kernel threads needed
+ */
+ dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+ if(dev->thread_pid < 0)
+ {
+ printk(KERN_ERR "aacraid: Unable to create rkt thread.\n");
+ goto error_kfree;
+ }
+ /*
+ * Tell the adapter that all is configured, and it can start
+ * accepting requests
+ */
+ aac_rkt_start_adapter(dev);
+ return 0;
+
+error_kfree:
+ kfree(dev->queues);
+
+error_irq:
+ free_irq(dev->scsi_host_ptr->irq, (void *)dev);
+
+error_iounmap:
+ iounmap(dev->regs.rkt);
+
+ return -1;
+}
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
new file mode 100644
index 000000000000..630b99e1fe83
--- /dev/null
+++ b/drivers/scsi/aacraid/rx.c
@@ -0,0 +1,441 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * rx.c
+ *
+ * Abstract: Hardware miniport for Drawbridge specific hardware functions.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <asm/semaphore.h>
+
+#include <scsi/scsi_host.h>
+
+#include "aacraid.h"
+
+static irqreturn_t aac_rx_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aac_dev *dev = dev_id;
+ unsigned long bellbits;
+ u8 intstat, mask;
+ intstat = rx_readb(dev, MUnit.OISR);
+ /*
+ * Read mask and invert because drawbridge is reversed.
+ * This allows us to only service interrupts that have
+ * been enabled.
+ */
+ mask = ~(dev->OIMR);
+ /* Check to see if this is our interrupt. If it isn't just return */
+ if (intstat & mask)
+ {
+ bellbits = rx_readl(dev, OutboundDoorbellReg);
+ if (bellbits & DoorBellPrintfReady) {
+ aac_printf(dev, le32_to_cpu(rx_readl (dev, IndexRegs.Mailbox[5])));
+ rx_writel(dev, MUnit.ODR,DoorBellPrintfReady);
+ rx_writel(dev, InboundDoorbellReg,DoorBellPrintfDone);
+ }
+ else if (bellbits & DoorBellAdapterNormCmdReady) {
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady);
+ aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+ }
+ else if (bellbits & DoorBellAdapterNormRespReady) {
+ aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+ rx_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady);
+ }
+ else if (bellbits & DoorBellAdapterNormCmdNotFull) {
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+ }
+ else if (bellbits & DoorBellAdapterNormRespNotFull) {
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/**
+ * rx_sync_cmd - send a command and wait
+ * @dev: Adapter
+ * @command: Command to execute
+ * @p1: first parameter
+ * @ret: adapter status
+ *
+ * This routine will send a synchronous command to the adapter and wait
+ * for its completion.
+ */
+
+static int rx_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *status)
+{
+ unsigned long start;
+ int ok;
+ /*
+ * Write the command into Mailbox 0
+ */
+ rx_writel(dev, InboundMailbox0, command);
+ /*
+ * Write the parameters into Mailboxes 1 - 4
+ */
+ rx_writel(dev, InboundMailbox1, p1);
+ rx_writel(dev, InboundMailbox2, 0);
+ rx_writel(dev, InboundMailbox3, 0);
+ rx_writel(dev, InboundMailbox4, 0);
+ /*
+ * Clear the synch command doorbell to start on a clean slate.
+ */
+ rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ /*
+ * Disable doorbell interrupts
+ */
+ rx_writeb(dev, MUnit.OIMR, dev->OIMR |= 0x04);
+ /*
+ * Force the completion of the mask register write before issuing
+ * the interrupt.
+ */
+ rx_readb (dev, MUnit.OIMR);
+ /*
+ * Signal that there is a new synch command
+ */
+ rx_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0);
+
+ ok = 0;
+ start = jiffies;
+
+ /*
+ * Wait up to 30 seconds
+ */
+ while (time_before(jiffies, start+30*HZ))
+ {
+ udelay(5); /* Delay 5 microseconds to let Mon960 get info. */
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the command.
+ */
+ if (rx_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear the doorbell.
+ */
+ rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ ok = 1;
+ break;
+ }
+ /*
+ * Yield the processor in case we are slow
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (ok != 1) {
+ /*
+ * Restore interrupt mask even though we timed out
+ */
+ rx_writeb(dev, MUnit.OIMR, dev->OIMR &= 0xfb);
+ return -ETIMEDOUT;
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ if (status)
+ *status = rx_readl(dev, IndexRegs.Mailbox[0]);
+ /*
+ * Clear the synch command doorbell.
+ */
+ rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ /*
+ * Restore interrupt mask
+ */
+ rx_writeb(dev, MUnit.OIMR, dev->OIMR &= 0xfb);
+ return 0;
+
+}
+
+/**
+ * aac_rx_interrupt_adapter - interrupt adapter
+ * @dev: Adapter
+ *
+ * Send an interrupt to the i960 and breakpoint it.
+ */
+
+static void aac_rx_interrupt_adapter(struct aac_dev *dev)
+{
+ u32 ret;
+ rx_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ * aac_rx_notify_adapter - send an event to the adapter
+ * @dev: Adapter
+ * @event: Event to send
+ *
+ * Notify the i960 that something it probably cares about has
+ * happened.
+ */
+
+static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case AdapNormCmdQue:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1);
+ break;
+ case HostNormRespNotFull:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4);
+ break;
+ case AdapNormRespQue:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2);
+ break;
+ case HostNormCmdNotFull:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3);
+ break;
+ case HostShutdown:
+// rx_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, &ret);
+ break;
+ case FastIo:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6);
+ break;
+ case AdapPrintfDone:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+/**
+ * aac_rx_start_adapter - activate adapter
+ * @dev: Adapter
+ *
+ * Start up processing on an i960 based AAC adapter
+ */
+
+static void aac_rx_start_adapter(struct aac_dev *dev)
+{
+ u32 status;
+ struct aac_init *init;
+
+ init = dev->init;
+ init->HostElapsedSeconds = cpu_to_le32(get_seconds());
+ /*
+ * Tell the adapter we are back and up and running so it will scan
+ * its command queues and enable our interrupts
+ */
+ dev->irq_mask = (DoorBellPrintfReady | OUTBOUNDDOORBELL_1 | OUTBOUNDDOORBELL_2 | OUTBOUNDDOORBELL_3 | OUTBOUNDDOORBELL_4);
+ /*
+ * First clear out all interrupts. Then enable the one's that we
+ * can handle.
+ */
+ rx_writeb(dev, MUnit.OIMR, 0xff);
+ rx_writel(dev, MUnit.ODR, 0xffffffff);
+// rx_writeb(dev, MUnit.OIMR, ~(u8)OUTBOUND_DOORBELL_INTERRUPT_MASK);
+ rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb);
+
+ // We can only use a 32 bit address here
+ rx_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &status);
+}
+
+/**
+ * aac_rx_check_health
+ * @dev: device to check if healthy
+ *
+ * Will attempt to determine if the specified adapter is alive and
+ * capable of handling requests, returning 0 if alive.
+ */
+static int aac_rx_check_health(struct aac_dev *dev)
+{
+ u32 status = rx_readl(dev, MUnit.OMRx[0]);
+
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (status & SELF_TEST_FAILED)
+ return -1;
+ /*
+ * Check to see if the board panic'd.
+ */
+ if (status & KERNEL_PANIC) {
+ char * buffer;
+ struct POSTSTATUS {
+ u32 Post_Command;
+ u32 Post_Address;
+ } * post;
+ dma_addr_t paddr, baddr;
+ int ret;
+
+ if ((status & 0xFF000000L) == 0xBC000000L)
+ return (status >> 16) & 0xFF;
+ buffer = pci_alloc_consistent(dev->pdev, 512, &baddr);
+ ret = -2;
+ if (buffer == NULL)
+ return ret;
+ post = pci_alloc_consistent(dev->pdev,
+ sizeof(struct POSTSTATUS), &paddr);
+ if (post == NULL) {
+ pci_free_consistent(dev->pdev, 512, buffer, baddr);
+ return ret;
+ }
+ memset(buffer, 0, 512);
+ post->Post_Command = cpu_to_le32(COMMAND_POST_RESULTS);
+ post->Post_Address = cpu_to_le32(baddr);
+ rx_writel(dev, MUnit.IMRx[0], paddr);
+ rx_sync_cmd(dev, COMMAND_POST_RESULTS, baddr, &status);
+ pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS),
+ post, paddr);
+ if ((buffer[0] == '0') && (buffer[1] == 'x')) {
+ ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10);
+ ret <<= 4;
+ ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10);
+ }
+ pci_free_consistent(dev->pdev, 512, buffer, baddr);
+ return ret;
+ }
+ /*
+ * Wait for the adapter to be up and running.
+ */
+ if (!(status & KERNEL_UP_AND_RUNNING))
+ return -3;
+ /*
+ * Everything is OK
+ */
+ return 0;
+}
+
+/**
+ * aac_rx_init - initialize an i960 based AAC card
+ * @dev: device to configure
+ *
+ * Allocate and set up resources for the i960 based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
+ * to the comm region.
+ */
+
+int aac_rx_init(struct aac_dev *dev)
+{
+ unsigned long start;
+ unsigned long status;
+ int instance;
+ const char * name;
+
+ instance = dev->id;
+ name = dev->name;
+
+ /*
+ * Map in the registers from the adapter.
+ */
+ if((dev->regs.rx = ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+ {
+ printk(KERN_WARNING "aacraid: unable to map i960.\n" );
+ return -1;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (rx_readl(dev, MUnit.OMRx[0]) & SELF_TEST_FAILED) {
+ printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) {
+ printk(KERN_ERR "%s%d: adapter kernel panic.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the monitor panic'd while booting.
+ */
+ if (rx_readl(dev, MUnit.OMRx[0]) & MONITOR_PANIC) {
+ printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes
+ */
+ while ((!(rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_UP_AND_RUNNING))
+ || (!(rx_readl(dev, MUnit.OMRx[0]) & KERNEL_UP_AND_RUNNING)))
+ {
+ if(time_after(jiffies, start+180*HZ))
+ {
+ status = rx_readl(dev, IndexRegs.Mailbox[7]);
+ printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n",
+ dev->name, instance, status);
+ goto error_iounmap;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (request_irq(dev->scsi_host_ptr->irq, aac_rx_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev)<0)
+ {
+ printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Fill in the function dispatch table.
+ */
+ dev->a_ops.adapter_interrupt = aac_rx_interrupt_adapter;
+ dev->a_ops.adapter_notify = aac_rx_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
+ dev->a_ops.adapter_check_health = aac_rx_check_health;
+
+ if (aac_init_adapter(dev) == NULL)
+ goto error_irq;
+ /*
+ * Start any kernel threads needed
+ */
+ dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+ if(dev->thread_pid < 0)
+ {
+ printk(KERN_ERR "aacraid: Unable to create rx thread.\n");
+ goto error_kfree;
+ }
+ /*
+ * Tell the adapter that all is configured, and it can start
+ * accepting requests
+ */
+ aac_rx_start_adapter(dev);
+ return 0;
+
+error_kfree:
+ kfree(dev->queues);
+
+error_irq:
+ free_irq(dev->scsi_host_ptr->irq, (void *)dev);
+
+error_iounmap:
+ iounmap(dev->regs.rx);
+
+ return -1;
+}
diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
new file mode 100644
index 000000000000..bd6c30723fba
--- /dev/null
+++ b/drivers/scsi/aacraid/sa.c
@@ -0,0 +1,374 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * sa.c
+ *
+ * Abstract: Drawbridge specific support functions
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <asm/semaphore.h>
+
+#include <scsi/scsi_host.h>
+
+#include "aacraid.h"
+
+static irqreturn_t aac_sa_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aac_dev *dev = dev_id;
+ unsigned short intstat, mask;
+
+ intstat = sa_readw(dev, DoorbellReg_p);
+ /*
+ * Read mask and invert because drawbridge is reversed.
+ * This allows us to only service interrupts that have been enabled.
+ */
+ mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK));
+
+ /* Check to see if this is our interrupt. If it isn't just return */
+
+ if (intstat & mask) {
+ if (intstat & PrintfReady) {
+ aac_printf(dev, sa_readl(dev, Mailbox5));
+ sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */
+ sa_writew(dev, DoorbellReg_s, PrintfDone);
+ } else if (intstat & DOORBELL_1) { // dev -> Host Normal Command Ready
+ aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_1);
+ } else if (intstat & DOORBELL_2) { // dev -> Host Normal Response Ready
+ aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_2);
+ } else if (intstat & DOORBELL_3) { // dev -> Host Normal Command Not Full
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_3);
+ } else if (intstat & DOORBELL_4) { // dev -> Host Normal Response Not Full
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_4);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/**
+ * aac_sa_notify_adapter - handle adapter notification
+ * @dev: Adapter that notification is for
+ * @event: Event to notidy
+ *
+ * Notify the adapter of an event
+ */
+
+void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case AdapNormCmdQue:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_1);
+ break;
+ case HostNormRespNotFull:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_4);
+ break;
+ case AdapNormRespQue:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_2);
+ break;
+ case HostNormCmdNotFull:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_3);
+ break;
+ case HostShutdown:
+ //sa_sync_cmd(dev, HOST_CRASHING, 0, &ret);
+ break;
+ case FastIo:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_6);
+ break;
+ case AdapPrintfDone:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_5);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+
+/**
+ * sa_sync_cmd - send a command and wait
+ * @dev: Adapter
+ * @command: Command to execute
+ * @p1: first parameter
+ * @ret: adapter status
+ *
+ * This routine will send a synchronous command to the adapter and wait
+ * for its completion.
+ */
+
+static int sa_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *ret)
+{
+ unsigned long start;
+ int ok;
+ /*
+ * Write the Command into Mailbox 0
+ */
+ sa_writel(dev, Mailbox0, command);
+ /*
+ * Write the parameters into Mailboxes 1 - 4
+ */
+ sa_writel(dev, Mailbox1, p1);
+ sa_writel(dev, Mailbox2, 0);
+ sa_writel(dev, Mailbox3, 0);
+ sa_writel(dev, Mailbox4, 0);
+ /*
+ * Clear the synch command doorbell to start on a clean slate.
+ */
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
+ /*
+ * Signal that there is a new synch command
+ */
+ sa_writew(dev, DoorbellReg_s, DOORBELL_0);
+
+ ok = 0;
+ start = jiffies;
+
+ while(time_before(jiffies, start+30*HZ))
+ {
+ /*
+ * Delay 5uS so that the monitor gets access
+ */
+ udelay(5);
+ /*
+ * Mon110 will set doorbell0 bit when it has
+ * completed the command.
+ */
+ if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0) {
+ ok = 1;
+ break;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if (ok != 1)
+ return -ETIMEDOUT;
+ /*
+ * Clear the synch command doorbell.
+ */
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ if (ret)
+ *ret = sa_readl(dev, Mailbox0);
+ return 0;
+}
+
+/**
+ * aac_sa_interrupt_adapter - interrupt an adapter
+ * @dev: Which adapter to enable.
+ *
+ * Breakpoint an adapter.
+ */
+
+static void aac_sa_interrupt_adapter (struct aac_dev *dev)
+{
+ u32 ret;
+ sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ * aac_sa_start_adapter - activate adapter
+ * @dev: Adapter
+ *
+ * Start up processing on an ARM based AAC adapter
+ */
+
+static void aac_sa_start_adapter(struct aac_dev *dev)
+{
+ u32 ret;
+ struct aac_init *init;
+ /*
+ * Fill in the remaining pieces of the init.
+ */
+ init = dev->init;
+ init->HostElapsedSeconds = cpu_to_le32(get_seconds());
+
+ /*
+ * Tell the adapter we are back and up and running so it will scan its command
+ * queues and enable our interrupts
+ */
+ dev->irq_mask = (PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4);
+ /*
+ * First clear out all interrupts. Then enable the one's that
+ * we can handle.
+ */
+ sa_writew(dev, SaDbCSR.PRISETIRQMASK, cpu_to_le16(0xffff));
+ sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4));
+ /* We can only use a 32 bit address here */
+ sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &ret);
+}
+
+/**
+ * aac_sa_check_health
+ * @dev: device to check if healthy
+ *
+ * Will attempt to determine if the specified adapter is alive and
+ * capable of handling requests, returning 0 if alive.
+ */
+static int aac_sa_check_health(struct aac_dev *dev)
+{
+ long status = sa_readl(dev, Mailbox7);
+
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (status & SELF_TEST_FAILED)
+ return -1;
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ if (status & KERNEL_PANIC)
+ return -2;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes
+ */
+ if (!(status & KERNEL_UP_AND_RUNNING))
+ return -3;
+ /*
+ * Everything is OK
+ */
+ return 0;
+}
+
+/**
+ * aac_sa_init - initialize an ARM based AAC card
+ * @dev: device to configure
+ *
+ * Allocate and set up resources for the ARM based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
+ * to the comm region.
+ */
+
+int aac_sa_init(struct aac_dev *dev)
+{
+ unsigned long start;
+ unsigned long status;
+ int instance;
+ const char *name;
+
+ instance = dev->id;
+ name = dev->name;
+
+ /*
+ * Map in the registers from the adapter.
+ */
+
+ if((dev->regs.sa = ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+ {
+ printk(KERN_WARNING "aacraid: unable to map ARM.\n" );
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) {
+ printk(KERN_WARNING "%s%d: adapter self-test failed.\n", name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) {
+ printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n", name, instance);
+ goto error_iounmap;
+ }
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes.
+ */
+ while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) {
+ if (time_after(jiffies, start+180*HZ)) {
+ status = sa_readl(dev, Mailbox7);
+ printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %lx.\n",
+ name, instance, status);
+ goto error_iounmap;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if (request_irq(dev->scsi_host_ptr->irq, aac_sa_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev ) < 0) {
+ printk(KERN_WARNING "%s%d: Interrupt unavailable.\n", name, instance);
+ goto error_iounmap;
+ }
+
+ /*
+ * Fill in the function dispatch table.
+ */
+
+ dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
+ dev->a_ops.adapter_notify = aac_sa_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
+ dev->a_ops.adapter_check_health = aac_sa_check_health;
+
+
+ if(aac_init_adapter(dev) == NULL)
+ goto error_irq;
+
+ /*
+ * Start any kernel threads needed
+ */
+ dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+ if (dev->thread_pid < 0) {
+ printk(KERN_ERR "aacraid: Unable to create command thread.\n");
+ goto error_kfree;
+ }
+
+ /*
+ * Tell the adapter that all is configure, and it can start
+ * accepting requests
+ */
+ aac_sa_start_adapter(dev);
+ return 0;
+
+
+error_kfree:
+ kfree(dev->queues);
+
+error_irq:
+ free_irq(dev->scsi_host_ptr->irq, (void *)dev);
+
+error_iounmap:
+ iounmap(dev->regs.sa);
+
+ return -1;
+}
+
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
new file mode 100644
index 000000000000..9962c51dc2a7
--- /dev/null
+++ b/drivers/scsi/advansys.c
@@ -0,0 +1,18237 @@
+#define ASC_VERSION "3.3K" /* AdvanSys Driver Version */
+
+/*
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-2000 Advanced System Products, Inc.
+ * Copyright (c) 2000-2001 ConnectCom Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * As of March 8, 2000 Advanced System Products, Inc. (AdvanSys)
+ * changed its name to ConnectCom Solutions, Inc.
+ *
+ */
+
+/*
+
+ Documentation for the AdvanSys Driver
+
+ A. Linux Kernels Supported by this Driver
+ B. Adapters Supported by this Driver
+ C. Linux source files modified by AdvanSys Driver
+ D. Source Comments
+ E. Driver Compile Time Options and Debugging
+ F. Driver LILO Option
+ G. Tests to run before releasing new driver
+ H. Release History
+ I. Known Problems/Fix List
+ J. Credits (Chronological Order)
+
+ A. Linux Kernels Supported by this Driver
+
+ This driver has been tested in the following Linux kernels: v2.2.18
+ v2.4.0. The driver is supported on v2.2 and v2.4 kernels and on x86,
+ alpha, and PowerPC platforms.
+
+ B. Adapters Supported by this Driver
+
+ AdvanSys (Advanced System Products, Inc.) manufactures the following
+ RISC-based, Bus-Mastering, Fast (10 Mhz) and Ultra (20 Mhz) Narrow
+ (8-bit transfer) SCSI Host Adapters for the ISA, EISA, VL, and PCI
+ buses and RISC-based, Bus-Mastering, Ultra (20 Mhz) Wide (16-bit
+ transfer) SCSI Host Adapters for the PCI bus.
+
+ The CDB counts below indicate the number of SCSI CDB (Command
+ Descriptor Block) requests that can be stored in the RISC chip
+ cache and board LRAM. A CDB is a single SCSI command. The driver
+ detect routine will display the number of CDBs available for each
+ adapter detected. The number of CDBs used by the driver can be
+ lowered in the BIOS by changing the 'Host Queue Size' adapter setting.
+
+ Laptop Products:
+ ABP-480 - Bus-Master CardBus (16 CDB) (2.4 kernel and greater)
+
+ Connectivity Products:
+ ABP510/5150 - Bus-Master ISA (240 CDB)
+ ABP5140 - Bus-Master ISA PnP (16 CDB)
+ ABP5142 - Bus-Master ISA PnP with floppy (16 CDB)
+ ABP902/3902 - Bus-Master PCI (16 CDB)
+ ABP3905 - Bus-Master PCI (16 CDB)
+ ABP915 - Bus-Master PCI (16 CDB)
+ ABP920 - Bus-Master PCI (16 CDB)
+ ABP3922 - Bus-Master PCI (16 CDB)
+ ABP3925 - Bus-Master PCI (16 CDB)
+ ABP930 - Bus-Master PCI (16 CDB)
+ ABP930U - Bus-Master PCI Ultra (16 CDB)
+ ABP930UA - Bus-Master PCI Ultra (16 CDB)
+ ABP960 - Bus-Master PCI MAC/PC (16 CDB)
+ ABP960U - Bus-Master PCI MAC/PC Ultra (16 CDB)
+
+ Single Channel Products:
+ ABP542 - Bus-Master ISA with floppy (240 CDB)
+ ABP742 - Bus-Master EISA (240 CDB)
+ ABP842 - Bus-Master VL (240 CDB)
+ ABP940 - Bus-Master PCI (240 CDB)
+ ABP940U - Bus-Master PCI Ultra (240 CDB)
+ ABP940UA/3940UA - Bus-Master PCI Ultra (240 CDB)
+ ABP970 - Bus-Master PCI MAC/PC (240 CDB)
+ ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB)
+ ABP3960UA - Bus-Master PCI MAC/PC Ultra (240 CDB)
+ ABP940UW/3940UW - Bus-Master PCI Ultra-Wide (253 CDB)
+ ABP970UW - Bus-Master PCI MAC/PC Ultra-Wide (253 CDB)
+ ABP3940U2W - Bus-Master PCI LVD/Ultra2-Wide (253 CDB)
+
+ Multi-Channel Products:
+ ABP752 - Dual Channel Bus-Master EISA (240 CDB Per Channel)
+ ABP852 - Dual Channel Bus-Master VL (240 CDB Per Channel)
+ ABP950 - Dual Channel Bus-Master PCI (240 CDB Per Channel)
+ ABP950UW - Dual Channel Bus-Master PCI Ultra-Wide (253 CDB Per Channel)
+ ABP980 - Four Channel Bus-Master PCI (240 CDB Per Channel)
+ ABP980U - Four Channel Bus-Master PCI Ultra (240 CDB Per Channel)
+ ABP980UA/3980UA - Four Channel Bus-Master PCI Ultra (16 CDB Per Chan.)
+ ABP3950U2W - Bus-Master PCI LVD/Ultra2-Wide and Ultra-Wide (253 CDB)
+ ABP3950U3W - Bus-Master PCI Dual LVD2/Ultra3-Wide (253 CDB)
+
+ C. Linux source files modified by AdvanSys Driver
+
+ This section for historical purposes documents the changes
+ originally made to the Linux kernel source to add the advansys
+ driver. As Linux has changed some of these files have also
+ been modified.
+
+ 1. linux/arch/i386/config.in:
+
+ bool 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS y
+
+ 2. linux/drivers/scsi/hosts.c:
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ #include "advansys.h"
+ #endif
+
+ and after "static Scsi_Host_Template builtin_scsi_hosts[] =":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+ #endif
+
+ 3. linux/drivers/scsi/Makefile:
+
+ ifdef CONFIG_SCSI_ADVANSYS
+ SCSI_SRCS := $(SCSI_SRCS) advansys.c
+ SCSI_OBJS := $(SCSI_OBJS) advansys.o
+ else
+ SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) advansys.o
+ endif
+
+ 4. linux/init/main.c:
+
+ extern void advansys_setup(char *str, int *ints);
+
+ and add the following lines to the bootsetups[] array.
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ { "advansys=", advansys_setup },
+ #endif
+
+ D. Source Comments
+
+ 1. Use tab stops set to 4 for the source files. For vi use 'se tabstops=4'.
+
+ 2. This driver should be maintained in multiple files. But to make
+ it easier to include with Linux and to follow Linux conventions,
+ the whole driver is maintained in the source files advansys.h and
+ advansys.c. In this file logical sections of the driver begin with
+ a comment that contains '---'. The following are the logical sections
+ of the driver below.
+
+ --- Linux Version
+ --- Linux Include File
+ --- Driver Options
+ --- Debugging Header
+ --- Asc Library Constants and Macros
+ --- Adv Library Constants and Macros
+ --- Driver Constants and Macros
+ --- Driver Structures
+ --- Driver Data
+ --- Driver Function Prototypes
+ --- Linux 'Scsi_Host_Template' and advansys_setup() Functions
+ --- Loadable Driver Support
+ --- Miscellaneous Driver Functions
+ --- Functions Required by the Asc Library
+ --- Functions Required by the Adv Library
+ --- Tracing and Debugging Functions
+ --- Asc Library Functions
+ --- Adv Library Functions
+
+ 3. The string 'XXX' is used to flag code that needs to be re-written
+ or that contains a problem that needs to be addressed.
+
+ 4. I have stripped comments from and reformatted the source for the
+ Asc Library and Adv Library to reduce the size of this file. This
+ source can be found under the following headings. The Asc Library
+ is used to support Narrow Boards. The Adv Library is used to
+ support Wide Boards.
+
+ --- Asc Library Constants and Macros
+ --- Adv Library Constants and Macros
+ --- Asc Library Functions
+ --- Adv Library Functions
+
+ E. Driver Compile Time Options and Debugging
+
+ In this source file the following constants can be defined. They are
+ defined in the source below. Both of these options are enabled by
+ default.
+
+ 1. ADVANSYS_ASSERT - Enable driver assertions (Def: Enabled)
+
+ Enabling this option adds assertion logic statements to the
+ driver. If an assertion fails a message will be displayed to
+ the console, but the system will continue to operate. Any
+ assertions encountered should be reported to the person
+ responsible for the driver. Assertion statements may proactively
+ detect problems with the driver and facilitate fixing these
+ problems. Enabling assertions will add a small overhead to the
+ execution of the driver.
+
+ 2. ADVANSYS_DEBUG - Enable driver debugging (Def: Disabled)
+
+ Enabling this option adds tracing functions to the driver and
+ the ability to set a driver tracing level at boot time. This
+ option will also export symbols not required outside the driver to
+ the kernel name space. This option is very useful for debugging
+ the driver, but it will add to the size of the driver execution
+ image and add overhead to the execution of the driver.
+
+ The amount of debugging output can be controlled with the global
+ variable 'asc_dbglvl'. The higher the number the more output. By
+ default the debug level is 0.
+
+ If the driver is loaded at boot time and the LILO Driver Option
+ is included in the system, the debug level can be changed by
+ specifying a 5th (ASC_NUM_IOPORT_PROBE + 1) I/O Port. The
+ first three hex digits of the pseudo I/O Port must be set to
+ 'deb' and the fourth hex digit specifies the debug level: 0 - F.
+ The following command line will look for an adapter at 0x330
+ and set the debug level to 2.
+
+ linux advansys=0x330,0,0,0,0xdeb2
+
+ If the driver is built as a loadable module this variable can be
+ defined when the driver is loaded. The following insmod command
+ will set the debug level to one.
+
+ insmod advansys.o asc_dbglvl=1
+
+ Debugging Message Levels:
+ 0: Errors Only
+ 1: High-Level Tracing
+ 2-N: Verbose Tracing
+
+ To enable debug output to console, please make sure that:
+
+ a. System and kernel logging is enabled (syslogd, klogd running).
+ b. Kernel messages are routed to console output. Check
+ /etc/syslog.conf for an entry similar to this:
+
+ kern.* /dev/console
+
+ c. klogd is started with the appropriate -c parameter
+ (e.g. klogd -c 8)
+
+ This will cause printk() messages to be be displayed on the
+ current console. Refer to the klogd(8) and syslogd(8) man pages
+ for details.
+
+ Alternatively you can enable printk() to console with this
+ program. However, this is not the 'official' way to do this.
+ Debug output is logged in /var/log/messages.
+
+ main()
+ {
+ syscall(103, 7, 0, 0);
+ }
+
+ Increasing LOG_BUF_LEN in kernel/printk.c to something like
+ 40960 allows more debug messages to be buffered in the kernel
+ and written to the console or log file.
+
+ 3. ADVANSYS_STATS - Enable statistics (Def: Enabled >= v1.3.0)
+
+ Enabling this option adds statistics collection and display
+ through /proc to the driver. The information is useful for
+ monitoring driver and device performance. It will add to the
+ size of the driver execution image and add minor overhead to
+ the execution of the driver.
+
+ Statistics are maintained on a per adapter basis. Driver entry
+ point call counts and transfer size counts are maintained.
+ Statistics are only available for kernels greater than or equal
+ to v1.3.0 with the CONFIG_PROC_FS (/proc) file system configured.
+
+ AdvanSys SCSI adapter files have the following path name format:
+
+ /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)]
+
+ This information can be displayed with cat. For example:
+
+ cat /proc/scsi/advansys/0
+
+ When ADVANSYS_STATS is not defined the AdvanSys /proc files only
+ contain adapter and device configuration information.
+
+ F. Driver LILO Option
+
+ If init/main.c is modified as described in the 'Directions for Adding
+ the AdvanSys Driver to Linux' section (B.4.) above, the driver will
+ recognize the 'advansys' LILO command line and /etc/lilo.conf option.
+ This option can be used to either disable I/O port scanning or to limit
+ scanning to 1 - 4 I/O ports. Regardless of the option setting EISA and
+ PCI boards will still be searched for and detected. This option only
+ affects searching for ISA and VL boards.
+
+ Examples:
+ 1. Eliminate I/O port scanning:
+ boot: linux advansys=
+ or
+ boot: linux advansys=0x0
+ 2. Limit I/O port scanning to one I/O port:
+ boot: linux advansys=0x110
+ 3. Limit I/O port scanning to four I/O ports:
+ boot: linux advansys=0x110,0x210,0x230,0x330
+
+ For a loadable module the same effect can be achieved by setting
+ the 'asc_iopflag' variable and 'asc_ioport' array when loading
+ the driver, e.g.
+
+ insmod advansys.o asc_iopflag=1 asc_ioport=0x110,0x330
+
+ If ADVANSYS_DEBUG is defined a 5th (ASC_NUM_IOPORT_PROBE + 1)
+ I/O Port may be added to specify the driver debug level. Refer to
+ the 'Driver Compile Time Options and Debugging' section above for
+ more information.
+
+ G. Tests to run before releasing new driver
+
+ 1. In the supported kernels verify there are no warning or compile
+ errors when the kernel is built as both a driver and as a module
+ and with the following options:
+
+ ADVANSYS_DEBUG - enabled and disabled
+ CONFIG_SMP - enabled and disabled
+ CONFIG_PROC_FS - enabled and disabled
+
+ 2. Run tests on an x86, alpha, and PowerPC with at least one narrow
+ card and one wide card attached to a hard disk and CD-ROM drive:
+ fdisk, mkfs, fsck, bonnie, copy/compare test from the
+ CD-ROM to the hard drive.
+
+ H. Release History
+
+ BETA-1.0 (12/23/95):
+ First Release
+
+ BETA-1.1 (12/28/95):
+ 1. Prevent advansys_detect() from being called twice.
+ 2. Add LILO 0xdeb[0-f] option to set 'asc_dbglvl'.
+
+ 1.2 (1/12/96):
+ 1. Prevent re-entrancy in the interrupt handler which
+ resulted in the driver hanging Linux.
+ 2. Fix problem that prevented ABP-940 cards from being
+ recognized on some PCI motherboards.
+ 3. Add support for the ABP-5140 PnP ISA card.
+ 4. Fix check condition return status.
+ 5. Add conditionally compiled code for Linux v1.3.X.
+
+ 1.3 (2/23/96):
+ 1. Fix problem in advansys_biosparam() that resulted in the
+ wrong drive geometry being returned for drives > 1GB with
+ extended translation enabled.
+ 2. Add additional tracing during device initialization.
+ 3. Change code that only applies to ISA PnP adapter.
+ 4. Eliminate 'make dep' warning.
+ 5. Try to fix problem with handling resets by increasing their
+ timeout value.
+
+ 1.4 (5/8/96):
+ 1. Change definitions to eliminate conflicts with other subsystems.
+ 2. Add versioning code for the shared interrupt changes.
+ 3. Eliminate problem in asc_rmqueue() with iterating after removing
+ a request.
+ 4. Remove reset request loop problem from the "Known Problems or
+ Issues" section. This problem was isolated and fixed in the
+ mid-level SCSI driver.
+
+ 1.5 (8/8/96):
+ 1. Add support for ABP-940U (PCI Ultra) adapter.
+ 2. Add support for IRQ sharing by setting the SA_SHIRQ flag for
+ request_irq and supplying a dev_id pointer to both request_irq()
+ and free_irq().
+ 3. In AscSearchIOPortAddr11() restore a call to check_region() which
+ should be used before I/O port probing.
+ 4. Fix bug in asc_prt_hex() which resulted in the displaying
+ the wrong data.
+ 5. Incorporate miscellaneous Asc Library bug fixes and new microcode.
+ 6. Change driver versioning to be specific to each Linux sub-level.
+ 7. Change statistics gathering to be per adapter instead of global
+ to the driver.
+ 8. Add more information and statistics to the adapter /proc file:
+ /proc/scsi/advansys[0...].
+ 9. Remove 'cmd_per_lun' from the "Known Problems or Issues" list.
+ This problem has been addressed with the SCSI mid-level changes
+ made in v1.3.89. The advansys_select_queue_depths() function
+ was added for the v1.3.89 changes.
+
+ 1.6 (9/10/96):
+ 1. Incorporate miscellaneous Asc Library bug fixes and new microcode.
+
+ 1.7 (9/25/96):
+ 1. Enable clustering and optimize the setting of the maximum number
+ of scatter gather elements for any particular board. Clustering
+ increases CPU utilization, but results in a relatively larger
+ increase in I/O throughput.
+ 2. Improve the performance of the request queuing functions by
+ adding a last pointer to the queue structure.
+ 3. Correct problems with reset and abort request handling that
+ could have hung or crashed Linux.
+ 4. Add more information to the adapter /proc file:
+ /proc/scsi/advansys[0...].
+ 5. Remove the request timeout issue form the driver issues list.
+ 6. Miscellaneous documentation additions and changes.
+
+ 1.8 (10/4/96):
+ 1. Make changes to handle the new v2.1.0 kernel memory mapping
+ in which a kernel virtual address may not be equivalent to its
+ bus or DMA memory address.
+ 2. Change abort and reset request handling to make it yet even
+ more robust.
+ 3. Try to mitigate request starvation by sending ordered requests
+ to heavily loaded, tag queuing enabled devices.
+ 4. Maintain statistics on request response time.
+ 5. Add request response time statistics and other information to
+ the adapter /proc file: /proc/scsi/advansys[0...].
+
+ 1.9 (10/21/96):
+ 1. Add conditionally compiled code (ASC_QUEUE_FLOW_CONTROL) to
+ make use of mid-level SCSI driver device queue depth flow
+ control mechanism. This will eliminate aborts caused by a
+ device being unable to keep up with requests and eliminate
+ repeat busy or QUEUE FULL status returned by a device.
+ 2. Incorporate miscellaneous Asc Library bug fixes.
+ 3. To allow the driver to work in kernels with broken module
+ support set 'cmd_per_lun' if the driver is compiled as a
+ module. This change affects kernels v1.3.89 to present.
+ 4. Remove PCI BIOS address from the driver banner. The PCI BIOS
+ is relocated by the motherboard BIOS and its new address can
+ not be determined by the driver.
+ 5. Add mid-level SCSI queue depth information to the adapter
+ /proc file: /proc/scsi/advansys[0...].
+
+ 2.0 (11/14/96):
+ 1. Change allocation of global structures used for device
+ initialization to guarantee they are in DMA-able memory.
+ Previously when the driver was loaded as a module these
+ structures might not have been in DMA-able memory, causing
+ device initialization to fail.
+
+ 2.1 (12/30/96):
+ 1. In advansys_reset(), if the request is a synchronous reset
+ request, even if the request serial number has changed, then
+ complete the request.
+ 2. Add Asc Library bug fixes including new microcode.
+ 3. Clear inquiry buffer before using it.
+ 4. Correct ifdef typo.
+
+ 2.2 (1/15/97):
+ 1. Add Asc Library bug fixes including new microcode.
+ 2. Add synchronous data transfer rate information to the
+ adapter /proc file: /proc/scsi/advansys[0...].
+ 3. Change ADVANSYS_DEBUG to be disabled by default. This
+ will reduce the size of the driver image, eliminate execution
+ overhead, and remove unneeded symbols from the kernel symbol
+ space that were previously added by the driver.
+ 4. Add new compile-time option ADVANSYS_ASSERT for assertion
+ code that used to be defined within ADVANSYS_DEBUG. This
+ option is enabled by default.
+
+ 2.8 (5/26/97):
+ 1. Change version number to 2.8 to synchronize the Linux driver
+ version numbering with other AdvanSys drivers.
+ 2. Reformat source files without tabs to present the same view
+ of the file to everyone regardless of the editor tab setting
+ being used.
+ 3. Add Asc Library bug fixes.
+
+ 3.1A (1/8/98):
+ 1. Change version number to 3.1 to indicate that support for
+ Ultra-Wide adapters (ABP-940UW) is included in this release.
+ 2. Add Asc Library (Narrow Board) bug fixes.
+ 3. Report an underrun condition with the host status byte set
+ to DID_UNDERRUN. Currently DID_UNDERRUN is defined to 0 which
+ causes the underrun condition to be ignored. When Linux defines
+ its own DID_UNDERRUN the constant defined in this file can be
+ removed.
+ 4. Add patch to AscWaitTixISRDone().
+ 5. Add support for up to 16 different AdvanSys host adapter SCSI
+ channels in one system. This allows four cards with four channels
+ to be used in one system.
+
+ 3.1B (1/9/98):
+ 1. Handle that PCI register base addresses are not always page
+ aligned even though ioremap() requires that the address argument
+ be page aligned.
+
+ 3.1C (1/10/98):
+ 1. Update latest BIOS version checked for from the /proc file.
+ 2. Don't set microcode SDTR variable at initialization. Instead
+ wait until device capabilities have been detected from an Inquiry
+ command.
+
+ 3.1D (1/21/98):
+ 1. Improve performance when the driver is compiled as module by
+ allowing up to 64 scatter-gather elements instead of 8.
+
+ 3.1E (5/1/98):
+ 1. Set time delay in AscWaitTixISRDone() to 1000 ms.
+ 2. Include SMP locking changes.
+ 3. For v2.1.93 and newer kernels use CONFIG_PCI and new PCI BIOS
+ access functions.
+ 4. Update board serial number printing.
+ 5. Try allocating an IRQ both with and without the SA_INTERRUPT
+ flag set to allow IRQ sharing with drivers that do not set
+ the SA_INTERRUPT flag. Also display a more descriptive error
+ message if request_irq() fails.
+ 6. Update to latest Asc and Adv Libraries.
+
+ 3.2A (7/22/99):
+ 1. Update Adv Library to 4.16 which includes support for
+ the ASC38C0800 (Ultra2/LVD) IC.
+
+ 3.2B (8/23/99):
+ 1. Correct PCI compile time option for v2.1.93 and greater
+ kernels, advansys_info() string, and debug compile time
+ option.
+ 2. Correct DvcSleepMilliSecond() for v2.1.0 and greater
+ kernels. This caused an LVD detection/BIST problem problem
+ among other things.
+ 3. Sort PCI cards by PCI Bus, Slot, Function ascending order
+ to be consistent with the BIOS.
+ 4. Update to Asc Library S121 and Adv Library 5.2.
+
+ 3.2C (8/24/99):
+ 1. Correct PCI card detection bug introduced in 3.2B that
+ prevented PCI cards from being detected in kernels older
+ than v2.1.93.
+
+ 3.2D (8/26/99):
+ 1. Correct /proc device synchronous speed information display.
+ Also when re-negotiation is pending for a target device
+ note this condition with an * and footnote.
+ 2. Correct initialization problem with Ultra-Wide cards that
+ have a pre-3.2 BIOS. A microcode variable changed locations
+ in 3.2 and greater BIOSes which caused WDTR to be attempted
+ erroneously with drives that don't support WDTR.
+
+ 3.2E (8/30/99):
+ 1. Fix compile error caused by v2.3.13 PCI structure change.
+ 2. Remove field from ASCEEP_CONFIG that resulted in an EEPROM
+ checksum error for ISA cards.
+ 3. Remove ASC_QUEUE_FLOW_CONTROL conditional code. The mid-level
+ SCSI changes that it depended on were never included in Linux.
+
+ 3.2F (9/3/99):
+ 1. Handle new initial function code added in v2.3.16 for all
+ driver versions.
+
+ 3.2G (9/8/99):
+ 1. Fix PCI board detection in v2.3.13 and greater kernels.
+ 2. Fix comiple errors in v2.3.X with debugging enabled.
+
+ 3.2H (9/13/99):
+ 1. Add 64-bit address, long support for Alpha and UltraSPARC.
+ The driver has been verified to work on an Alpha system.
+ 2. Add partial byte order handling support for Power PC and
+ other big-endian platforms. This support has not yet been
+ completed or verified.
+ 3. For wide boards replace block zeroing of request and
+ scatter-gather structures with individual field initialization
+ to improve performance.
+ 4. Correct and clarify ROM BIOS version detection.
+
+ 3.2I (10/8/99):
+ 1. Update to Adv Library 5.4.
+ 2. Add v2.3.19 underrun reporting to asc_isr_callback() and
+ adv_isr_callback(). Remove DID_UNDERRUN constant and other
+ no longer needed code that previously documented the lack
+ of underrun handling.
+
+ 3.2J (10/14/99):
+ 1. Eliminate compile errors for v2.0 and earlier kernels.
+
+ 3.2K (11/15/99):
+ 1. Correct debug compile error in asc_prt_adv_scsi_req_q().
+ 2. Update Adv Library to 5.5.
+ 3. Add ifdef handling for /proc changes added in v2.3.28.
+ 4. Increase Wide board scatter-gather list maximum length to
+ 255 when the driver is compiled into the kernel.
+
+ 3.2L (11/18/99):
+ 1. Fix bug in adv_get_sglist() that caused an assertion failure
+ at line 7475. The reqp->sgblkp pointer must be initialized
+ to NULL in adv_get_sglist().
+
+ 3.2M (11/29/99):
+ 1. Really fix bug in adv_get_sglist().
+ 2. Incorporate v2.3.29 changes into driver.
+
+ 3.2N (4/1/00):
+ 1. Add CONFIG_ISA ifdef code.
+ 2. Include advansys_interrupts_enabled name change patch.
+ 3. For >= v2.3.28 use new SCSI error handling with new function
+ advansys_eh_bus_reset(). Don't include an abort function
+ because of base library limitations.
+ 4. For >= v2.3.28 use per board lock instead of io_request_lock.
+ 5. For >= v2.3.28 eliminate advansys_command() and
+ advansys_command_done().
+ 6. Add some changes for PowerPC (Big Endian) support, but it isn't
+ working yet.
+ 7. Fix "nonexistent resource free" problem that occurred on a module
+ unload for boards with an I/O space >= 255. The 'n_io_port' field
+ is only one byte and can not be used to hold an ioport length more
+ than 255.
+
+ 3.3A (4/4/00):
+ 1. Update to Adv Library 5.8.
+ 2. For wide cards add support for CDBs up to 16 bytes.
+ 3. Eliminate warnings when CONFIG_PROC_FS is not defined.
+
+ 3.3B (5/1/00):
+ 1. Support for PowerPC (Big Endian) wide cards. Narrow cards
+ still need work.
+ 2. Change bitfields to shift and mask access for endian
+ portability.
+
+ 3.3C (10/13/00):
+ 1. Update for latest 2.4 kernel.
+ 2. Test ABP-480 CardBus support in 2.4 kernel - works!
+ 3. Update to Asc Library S123.
+ 4. Update to Adv Library 5.12.
+
+ 3.3D (11/22/00):
+ 1. Update for latest 2.4 kernel.
+ 2. Create patches for 2.2 and 2.4 kernels.
+
+ 3.3E (1/9/01):
+ 1. Now that 2.4 is released remove ifdef code for kernel versions
+ less than 2.2. The driver is now only supported in kernels 2.2,
+ 2.4, and greater.
+ 2. Add code to release and acquire the io_request_lock in
+ the driver entrypoint functions: advansys_detect and
+ advansys_queuecommand. In kernel 2.4 the SCSI mid-level driver
+ still holds the io_request_lock on entry to SCSI low-level drivers.
+ This was supposed to be removed before 2.4 was released but never
+ happened. When the mid-level SCSI driver is changed all references
+ to the io_request_lock should be removed from the driver.
+ 3. Simplify error handling by removing advansys_abort(),
+ AscAbortSRB(), AscResetDevice(). SCSI bus reset requests are
+ now handled by resetting the SCSI bus and fully re-initializing
+ the chip. This simple method of error recovery has proven to work
+ most reliably after attempts at different methods. Also now only
+ support the "new" error handling method and remove the obsolete
+ error handling interface.
+ 4. Fix debug build errors.
+
+ 3.3F (1/24/01):
+ 1. Merge with ConnectCom version from Andy Kellner which
+ updates Adv Library to 5.14.
+ 2. Make PowerPC (Big Endian) work for narrow cards and
+ fix problems writing EEPROM for wide cards.
+ 3. Remove interrupts_enabled assertion function.
+
+ 3.3G (2/16/01):
+ 1. Return an error from narrow boards if passed a 16 byte
+ CDB. The wide board can already handle 16 byte CDBs.
+
+ 3.3GJ (4/15/02):
+ 1. hacks for lk 2.5 series (D. Gilbert)
+
+ 3.3GJD (10/14/02):
+ 1. change select_queue_depths to slave_configure
+ 2. make cmd_per_lun be sane again
+
+ 3.3K [2004/06/24]:
+ 1. continuing cleanup for lk 2.6 series
+ 2. Fix problem in lk 2.6.7-bk2 that broke PCI wide cards
+ 3. Fix problem that oopsed ISA cards
+
+ I. Known Problems/Fix List (XXX)
+
+ 1. Need to add memory mapping workaround. Test the memory mapping.
+ If it doesn't work revert to I/O port access. Can a test be done
+ safely?
+ 2. Handle an interrupt not working. Keep an interrupt counter in
+ the interrupt handler. In the timeout function if the interrupt
+ has not occurred then print a message and run in polled mode.
+ 3. Allow bus type scanning order to be changed.
+ 4. Need to add support for target mode commands, cf. CAM XPT.
+
+ J. Credits (Chronological Order)
+
+ Bob Frey <bfrey@turbolinux.com.cn> wrote the AdvanSys SCSI driver
+ and maintained it up to 3.3F. He continues to answer questions
+ and help maintain the driver.
+
+ Nathan Hartwell <mage@cdc3.cdc.net> provided the directions and
+ basis for the Linux v1.3.X changes which were included in the
+ 1.2 release.
+
+ Thomas E Zerucha <zerucha@shell.portal.com> pointed out a bug
+ in advansys_biosparam() which was fixed in the 1.3 release.
+
+ Erik Ratcliffe <erik@caldera.com> has done testing of the
+ AdvanSys driver in the Caldera releases.
+
+ Rik van Riel <H.H.vanRiel@fys.ruu.nl> provided a patch to
+ AscWaitTixISRDone() which he found necessary to make the
+ driver work with a SCSI-1 disk.
+
+ Mark Moran <mmoran@mmoran.com> has helped test Ultra-Wide
+ support in the 3.1A driver.
+
+ Doug Gilbert <dgilbert@interlog.com> has made changes and
+ suggestions to improve the driver and done a lot of testing.
+
+ Ken Mort <ken@mort.net> reported a DEBUG compile bug fixed
+ in 3.2K.
+
+ Tom Rini <trini@kernel.crashing.org> provided the CONFIG_ISA
+ patch and helped with PowerPC wide and narrow board support.
+
+ Philip Blundell <philb@gnu.org> provided an
+ advansys_interrupts_enabled patch.
+
+ Dave Jones <dave@denial.force9.co.uk> reported the compiler
+ warnings generated when CONFIG_PROC_FS was not defined in
+ the 3.2M driver.
+
+ Jerry Quinn <jlquinn@us.ibm.com> fixed PowerPC support (endian
+ problems) for wide cards.
+
+ Bryan Henderson <bryanh@giraffe-data.com> helped debug narrow
+ card error handling.
+
+ Manuel Veloso <veloso@pobox.com> worked hard on PowerPC narrow
+ board support and fixed a bug in AscGetEEPConfig().
+
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br> made
+ save_flags/restore_flags changes.
+
+ Andy Kellner <AKellner@connectcom.net> continues the Advansys SCSI
+ driver development for ConnectCom (Version > 3.3F).
+
+ K. ConnectCom (AdvanSys) Contact Information
+
+ Mail: ConnectCom Solutions, Inc.
+ 1150 Ringwood Court
+ San Jose, CA 95131
+ Operator/Sales: 1-408-383-9400
+ FAX: 1-408-383-9612
+ Tech Support: 1-408-467-2930
+ Tech Support E-Mail: linux@connectcom.net
+ FTP Site: ftp.connectcom.net (login: anonymous)
+ Web Site: http://www.connectcom.net
+
+*/
+
+/*
+ * --- Linux Include Files
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#if defined(CONFIG_X86) && !defined(CONFIG_ISA)
+#define CONFIG_ISA
+#endif /* CONFIG_X86 && !CONFIG_ISA */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+
+/* FIXME: (by jejb@steeleye.com) This warning is present for two
+ * reasons:
+ *
+ * 1) This driver badly needs converting to the correct driver model
+ * probing API
+ *
+ * 2) Although all of the necessary command mapping places have the
+ * appropriate dma_map.. APIs, the driver still processes its internal
+ * queue using bus_to_virt() and virt_to_bus() which are illegal under
+ * the API. The entire queue processing structure will need to be
+ * altered to fix this.
+ */
+#warning this driver is still not properly converted to the DMA API
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include "advansys.h"
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
+
+
+/*
+ * --- Driver Options
+ */
+
+/* Enable driver assertions. */
+#define ADVANSYS_ASSERT
+
+/* Enable driver /proc statistics. */
+#define ADVANSYS_STATS
+
+/* Enable driver tracing. */
+/* #define ADVANSYS_DEBUG */
+
+
+/*
+ * --- Debugging Header
+ */
+
+#ifdef ADVANSYS_DEBUG
+#define STATIC
+#else /* ADVANSYS_DEBUG */
+#define STATIC static
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Asc Library Constants and Macros
+ */
+
+#define ASC_LIB_VERSION_MAJOR 1
+#define ASC_LIB_VERSION_MINOR 24
+#define ASC_LIB_SERIAL_NUMBER 123
+
+/*
+ * Portable Data Types
+ *
+ * Any instance where a 32-bit long or pointer type is assumed
+ * for precision or HW defined structures, the following define
+ * types must be used. In Linux the char, short, and int types
+ * are all consistent at 8, 16, and 32 bits respectively. Pointers
+ * and long types are 64 bits on Alpha and UltraSPARC.
+ */
+#define ASC_PADDR __u32 /* Physical/Bus address data type. */
+#define ASC_VADDR __u32 /* Virtual address data type. */
+#define ASC_DCNT __u32 /* Unsigned Data count type. */
+#define ASC_SDCNT __s32 /* Signed Data count type. */
+
+/*
+ * These macros are used to convert a virtual address to a
+ * 32-bit value. This currently can be used on Linux Alpha
+ * which uses 64-bit virtual address but a 32-bit bus address.
+ * This is likely to break in the future, but doing this now
+ * will give us time to change the HW and FW to handle 64-bit
+ * addresses.
+ */
+#define ASC_VADDR_TO_U32 virt_to_bus
+#define ASC_U32_TO_VADDR bus_to_virt
+
+typedef unsigned char uchar;
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define EOF (-1)
+#define ERR (-1)
+#define UW_ERR (uint)(0xFFFF)
+#define isodd_word(val) ((((uint)val) & (uint)0x0001) != 0)
+#define AscPCIConfigVendorIDRegister 0x0000
+#define AscPCIConfigDeviceIDRegister 0x0002
+#define AscPCIConfigCommandRegister 0x0004
+#define AscPCIConfigStatusRegister 0x0006
+#define AscPCIConfigRevisionIDRegister 0x0008
+#define AscPCIConfigCacheSize 0x000C
+#define AscPCIConfigLatencyTimer 0x000D
+#define AscPCIIOBaseRegister 0x0010
+#define AscPCICmdRegBits_IOMemBusMaster 0x0007
+#define ASC_PCI_ID2BUS(id) ((id) & 0xFF)
+#define ASC_PCI_ID2DEV(id) (((id) >> 11) & 0x1F)
+#define ASC_PCI_ID2FUNC(id) (((id) >> 8) & 0x7)
+#define ASC_PCI_MKID(bus, dev, func) ((((dev) & 0x1F) << 11) | (((func) & 0x7) << 8) | ((bus) & 0xFF))
+#define ASC_PCI_VENDORID 0x10CD
+#define ASC_PCI_DEVICEID_1200A 0x1100
+#define ASC_PCI_DEVICEID_1200B 0x1200
+#define ASC_PCI_DEVICEID_ULTRA 0x1300
+#define ASC_PCI_REVISION_3150 0x02
+#define ASC_PCI_REVISION_3050 0x03
+
+#define ASC_DVCLIB_CALL_DONE (1)
+#define ASC_DVCLIB_CALL_FAILED (0)
+#define ASC_DVCLIB_CALL_ERROR (-1)
+
+/*
+ * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists.
+ * The SRB structure will have to be changed and the ASC_SRB2SCSIQ()
+ * macro re-defined to be able to obtain a ASC_SCSI_Q pointer from the
+ * SRB structure.
+ */
+#define CC_VERY_LONG_SG_LIST 0
+#define ASC_SRB2SCSIQ(srb_ptr) (srb_ptr)
+
+#define PortAddr unsigned short /* port address size */
+#define inp(port) inb(port)
+#define outp(port, byte) outb((byte), (port))
+
+#define inpw(port) inw(port)
+#define outpw(port, word) outw((word), (port))
+
+#define ASC_MAX_SG_QUEUE 7
+#define ASC_MAX_SG_LIST 255
+
+#define ASC_CS_TYPE unsigned short
+
+#define ASC_IS_ISA (0x0001)
+#define ASC_IS_ISAPNP (0x0081)
+#define ASC_IS_EISA (0x0002)
+#define ASC_IS_PCI (0x0004)
+#define ASC_IS_PCI_ULTRA (0x0104)
+#define ASC_IS_PCMCIA (0x0008)
+#define ASC_IS_MCA (0x0020)
+#define ASC_IS_VL (0x0040)
+#define ASC_ISA_PNP_PORT_ADDR (0x279)
+#define ASC_ISA_PNP_PORT_WRITE (ASC_ISA_PNP_PORT_ADDR+0x800)
+#define ASC_IS_WIDESCSI_16 (0x0100)
+#define ASC_IS_WIDESCSI_32 (0x0200)
+#define ASC_IS_BIG_ENDIAN (0x8000)
+#define ASC_CHIP_MIN_VER_VL (0x01)
+#define ASC_CHIP_MAX_VER_VL (0x07)
+#define ASC_CHIP_MIN_VER_PCI (0x09)
+#define ASC_CHIP_MAX_VER_PCI (0x0F)
+#define ASC_CHIP_VER_PCI_BIT (0x08)
+#define ASC_CHIP_MIN_VER_ISA (0x11)
+#define ASC_CHIP_MIN_VER_ISA_PNP (0x21)
+#define ASC_CHIP_MAX_VER_ISA (0x27)
+#define ASC_CHIP_VER_ISA_BIT (0x30)
+#define ASC_CHIP_VER_ISAPNP_BIT (0x20)
+#define ASC_CHIP_VER_ASYN_BUG (0x21)
+#define ASC_CHIP_VER_PCI 0x08
+#define ASC_CHIP_VER_PCI_ULTRA_3150 (ASC_CHIP_VER_PCI | 0x02)
+#define ASC_CHIP_VER_PCI_ULTRA_3050 (ASC_CHIP_VER_PCI | 0x03)
+#define ASC_CHIP_MIN_VER_EISA (0x41)
+#define ASC_CHIP_MAX_VER_EISA (0x47)
+#define ASC_CHIP_VER_EISA_BIT (0x40)
+#define ASC_CHIP_LATEST_VER_EISA ((ASC_CHIP_MIN_VER_EISA - 1) + 3)
+#define ASC_MAX_LIB_SUPPORTED_ISA_CHIP_VER 0x21
+#define ASC_MAX_LIB_SUPPORTED_PCI_CHIP_VER 0x0A
+#define ASC_MAX_VL_DMA_ADDR (0x07FFFFFFL)
+#define ASC_MAX_VL_DMA_COUNT (0x07FFFFFFL)
+#define ASC_MAX_PCI_DMA_ADDR (0xFFFFFFFFL)
+#define ASC_MAX_PCI_DMA_COUNT (0xFFFFFFFFL)
+#define ASC_MAX_ISA_DMA_ADDR (0x00FFFFFFL)
+#define ASC_MAX_ISA_DMA_COUNT (0x00FFFFFFL)
+#define ASC_MAX_EISA_DMA_ADDR (0x07FFFFFFL)
+#define ASC_MAX_EISA_DMA_COUNT (0x07FFFFFFL)
+
+#define ASC_SCSI_ID_BITS 3
+#define ASC_SCSI_TIX_TYPE uchar
+#define ASC_ALL_DEVICE_BIT_SET 0xFF
+#define ASC_SCSI_BIT_ID_TYPE uchar
+#define ASC_MAX_TID 7
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFF
+#define ASC_MAX_SENSE_LEN 32
+#define ASC_MIN_SENSE_LEN 14
+#define ASC_MAX_CDB_LEN 12
+#define ASC_SCSI_RESET_HOLD_TIME_US 60
+
+#define ADV_INQ_CLOCKING_ST_ONLY 0x0
+#define ADV_INQ_CLOCKING_DT_ONLY 0x1
+#define ADV_INQ_CLOCKING_ST_AND_DT 0x3
+
+/*
+ * Inquiry SPC-2 SPI Byte 1 EVPD (Enable Vital Product Data)
+ * and CmdDt (Command Support Data) field bit definitions.
+ */
+#define ADV_INQ_RTN_VPD_AND_CMDDT 0x3
+#define ADV_INQ_RTN_CMDDT_FOR_OP_CODE 0x2
+#define ADV_INQ_RTN_VPD_FOR_PG_CODE 0x1
+#define ADV_INQ_RTN_STD_INQUIRY_DATA 0x0
+
+#define ASC_SCSIDIR_NOCHK 0x00
+#define ASC_SCSIDIR_T2H 0x08
+#define ASC_SCSIDIR_H2T 0x10
+#define ASC_SCSIDIR_NODATA 0x18
+#define SCSI_ASC_NOMEDIA 0x3A
+#define ASC_SRB_HOST(x) ((uchar)((uchar)(x) >> 4))
+#define ASC_SRB_TID(x) ((uchar)((uchar)(x) & (uchar)0x0F))
+#define ASC_SRB_LUN(x) ((uchar)((uint)(x) >> 13))
+#define PUT_CDB1(x) ((uchar)((uint)(x) >> 8))
+#define MS_CMD_DONE 0x00
+#define MS_EXTEND 0x01
+#define MS_SDTR_LEN 0x03
+#define MS_SDTR_CODE 0x01
+#define MS_WDTR_LEN 0x02
+#define MS_WDTR_CODE 0x03
+#define MS_MDP_LEN 0x05
+#define MS_MDP_CODE 0x00
+
+/*
+ * Inquiry data structure and bitfield macros
+ *
+ * Only quantities of more than 1 bit are shifted, since the others are
+ * just tested for true or false. C bitfields aren't portable between big
+ * and little-endian platforms so they are not used.
+ */
+
+#define ASC_INQ_DVC_TYPE(inq) ((inq)->periph & 0x1f)
+#define ASC_INQ_QUALIFIER(inq) (((inq)->periph & 0xe0) >> 5)
+#define ASC_INQ_DVC_TYPE_MOD(inq) ((inq)->devtype & 0x7f)
+#define ASC_INQ_REMOVABLE(inq) ((inq)->devtype & 0x80)
+#define ASC_INQ_ANSI_VER(inq) ((inq)->ver & 0x07)
+#define ASC_INQ_ECMA_VER(inq) (((inq)->ver & 0x38) >> 3)
+#define ASC_INQ_ISO_VER(inq) (((inq)->ver & 0xc0) >> 6)
+#define ASC_INQ_RESPONSE_FMT(inq) ((inq)->byte3 & 0x0f)
+#define ASC_INQ_TERM_IO(inq) ((inq)->byte3 & 0x40)
+#define ASC_INQ_ASYNC_NOTIF(inq) ((inq)->byte3 & 0x80)
+#define ASC_INQ_SOFT_RESET(inq) ((inq)->flags & 0x01)
+#define ASC_INQ_CMD_QUEUE(inq) ((inq)->flags & 0x02)
+#define ASC_INQ_LINK_CMD(inq) ((inq)->flags & 0x08)
+#define ASC_INQ_SYNC(inq) ((inq)->flags & 0x10)
+#define ASC_INQ_WIDE16(inq) ((inq)->flags & 0x20)
+#define ASC_INQ_WIDE32(inq) ((inq)->flags & 0x40)
+#define ASC_INQ_REL_ADDR(inq) ((inq)->flags & 0x80)
+#define ASC_INQ_INFO_UNIT(inq) ((inq)->info & 0x01)
+#define ASC_INQ_QUICK_ARB(inq) ((inq)->info & 0x02)
+#define ASC_INQ_CLOCKING(inq) (((inq)->info & 0x0c) >> 2)
+
+typedef struct {
+ uchar periph;
+ uchar devtype;
+ uchar ver;
+ uchar byte3;
+ uchar add_len;
+ uchar res1;
+ uchar res2;
+ uchar flags;
+ uchar vendor_id[8];
+ uchar product_id[16];
+ uchar product_rev_level[4];
+} ASC_SCSI_INQUIRY;
+
+#define ASC_SG_LIST_PER_Q 7
+#define QS_FREE 0x00
+#define QS_READY 0x01
+#define QS_DISC1 0x02
+#define QS_DISC2 0x04
+#define QS_BUSY 0x08
+#define QS_ABORTED 0x40
+#define QS_DONE 0x80
+#define QC_NO_CALLBACK 0x01
+#define QC_SG_SWAP_QUEUE 0x02
+#define QC_SG_HEAD 0x04
+#define QC_DATA_IN 0x08
+#define QC_DATA_OUT 0x10
+#define QC_URGENT 0x20
+#define QC_MSG_OUT 0x40
+#define QC_REQ_SENSE 0x80
+#define QCSG_SG_XFER_LIST 0x02
+#define QCSG_SG_XFER_MORE 0x04
+#define QCSG_SG_XFER_END 0x08
+#define QD_IN_PROGRESS 0x00
+#define QD_NO_ERROR 0x01
+#define QD_ABORTED_BY_HOST 0x02
+#define QD_WITH_ERROR 0x04
+#define QD_INVALID_REQUEST 0x80
+#define QD_INVALID_HOST_NUM 0x81
+#define QD_INVALID_DEVICE 0x82
+#define QD_ERR_INTERNAL 0xFF
+#define QHSTA_NO_ERROR 0x00
+#define QHSTA_M_SEL_TIMEOUT 0x11
+#define QHSTA_M_DATA_OVER_RUN 0x12
+#define QHSTA_M_DATA_UNDER_RUN 0x12
+#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13
+#define QHSTA_M_BAD_BUS_PHASE_SEQ 0x14
+#define QHSTA_D_QDONE_SG_LIST_CORRUPTED 0x21
+#define QHSTA_D_ASC_DVC_ERROR_CODE_SET 0x22
+#define QHSTA_D_HOST_ABORT_FAILED 0x23
+#define QHSTA_D_EXE_SCSI_Q_FAILED 0x24
+#define QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT 0x25
+#define QHSTA_D_ASPI_NO_BUF_POOL 0x26
+#define QHSTA_M_WTM_TIMEOUT 0x41
+#define QHSTA_M_BAD_CMPL_STATUS_IN 0x42
+#define QHSTA_M_NO_AUTO_REQ_SENSE 0x43
+#define QHSTA_M_AUTO_REQ_SENSE_FAIL 0x44
+#define QHSTA_M_TARGET_STATUS_BUSY 0x45
+#define QHSTA_M_BAD_TAG_CODE 0x46
+#define QHSTA_M_BAD_QUEUE_FULL_OR_BUSY 0x47
+#define QHSTA_M_HUNG_REQ_SCSI_BUS_RESET 0x48
+#define QHSTA_D_LRAM_CMP_ERROR 0x81
+#define QHSTA_M_MICRO_CODE_ERROR_HALT 0xA1
+#define ASC_FLAG_SCSIQ_REQ 0x01
+#define ASC_FLAG_BIOS_SCSIQ_REQ 0x02
+#define ASC_FLAG_BIOS_ASYNC_IO 0x04
+#define ASC_FLAG_SRB_LINEAR_ADDR 0x08
+#define ASC_FLAG_WIN16 0x10
+#define ASC_FLAG_WIN32 0x20
+#define ASC_FLAG_ISA_OVER_16MB 0x40
+#define ASC_FLAG_DOS_VM_CALLBACK 0x80
+#define ASC_TAG_FLAG_EXTRA_BYTES 0x10
+#define ASC_TAG_FLAG_DISABLE_DISCONNECT 0x04
+#define ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX 0x08
+#define ASC_TAG_FLAG_DISABLE_CHK_COND_INT_HOST 0x40
+#define ASC_SCSIQ_CPY_BEG 4
+#define ASC_SCSIQ_SGHD_CPY_BEG 2
+#define ASC_SCSIQ_B_FWD 0
+#define ASC_SCSIQ_B_BWD 1
+#define ASC_SCSIQ_B_STATUS 2
+#define ASC_SCSIQ_B_QNO 3
+#define ASC_SCSIQ_B_CNTL 4
+#define ASC_SCSIQ_B_SG_QUEUE_CNT 5
+#define ASC_SCSIQ_D_DATA_ADDR 8
+#define ASC_SCSIQ_D_DATA_CNT 12
+#define ASC_SCSIQ_B_SENSE_LEN 20
+#define ASC_SCSIQ_DONE_INFO_BEG 22
+#define ASC_SCSIQ_D_SRBPTR 22
+#define ASC_SCSIQ_B_TARGET_IX 26
+#define ASC_SCSIQ_B_CDB_LEN 28
+#define ASC_SCSIQ_B_TAG_CODE 29
+#define ASC_SCSIQ_W_VM_ID 30
+#define ASC_SCSIQ_DONE_STATUS 32
+#define ASC_SCSIQ_HOST_STATUS 33
+#define ASC_SCSIQ_SCSI_STATUS 34
+#define ASC_SCSIQ_CDB_BEG 36
+#define ASC_SCSIQ_DW_REMAIN_XFER_ADDR 56
+#define ASC_SCSIQ_DW_REMAIN_XFER_CNT 60
+#define ASC_SCSIQ_B_FIRST_SG_WK_QP 48
+#define ASC_SCSIQ_B_SG_WK_QP 49
+#define ASC_SCSIQ_B_SG_WK_IX 50
+#define ASC_SCSIQ_W_ALT_DC1 52
+#define ASC_SCSIQ_B_LIST_CNT 6
+#define ASC_SCSIQ_B_CUR_LIST_CNT 7
+#define ASC_SGQ_B_SG_CNTL 4
+#define ASC_SGQ_B_SG_HEAD_QP 5
+#define ASC_SGQ_B_SG_LIST_CNT 6
+#define ASC_SGQ_B_SG_CUR_LIST_CNT 7
+#define ASC_SGQ_LIST_BEG 8
+#define ASC_DEF_SCSI1_QNG 4
+#define ASC_MAX_SCSI1_QNG 4
+#define ASC_DEF_SCSI2_QNG 16
+#define ASC_MAX_SCSI2_QNG 32
+#define ASC_TAG_CODE_MASK 0x23
+#define ASC_STOP_REQ_RISC_STOP 0x01
+#define ASC_STOP_ACK_RISC_STOP 0x03
+#define ASC_STOP_CLEAN_UP_BUSY_Q 0x10
+#define ASC_STOP_CLEAN_UP_DISC_Q 0x20
+#define ASC_STOP_HOST_REQ_RISC_HALT 0x40
+#define ASC_TIDLUN_TO_IX(tid, lun) (ASC_SCSI_TIX_TYPE)((tid) + ((lun)<<ASC_SCSI_ID_BITS))
+#define ASC_TID_TO_TARGET_ID(tid) (ASC_SCSI_BIT_ID_TYPE)(0x01 << (tid))
+#define ASC_TIX_TO_TARGET_ID(tix) (0x01 << ((tix) & ASC_MAX_TID))
+#define ASC_TIX_TO_TID(tix) ((tix) & ASC_MAX_TID)
+#define ASC_TID_TO_TIX(tid) ((tid) & ASC_MAX_TID)
+#define ASC_TIX_TO_LUN(tix) (((tix) >> ASC_SCSI_ID_BITS) & ASC_MAX_LUN)
+#define ASC_QNO_TO_QADDR(q_no) ((ASC_QADR_BEG)+((int)(q_no) << 6))
+
+typedef struct asc_scsiq_1 {
+ uchar status;
+ uchar q_no;
+ uchar cntl;
+ uchar sg_queue_cnt;
+ uchar target_id;
+ uchar target_lun;
+ ASC_PADDR data_addr;
+ ASC_DCNT data_cnt;
+ ASC_PADDR sense_addr;
+ uchar sense_len;
+ uchar extra_bytes;
+} ASC_SCSIQ_1;
+
+typedef struct asc_scsiq_2 {
+ ASC_VADDR srb_ptr;
+ uchar target_ix;
+ uchar flag;
+ uchar cdb_len;
+ uchar tag_code;
+ ushort vm_id;
+} ASC_SCSIQ_2;
+
+typedef struct asc_scsiq_3 {
+ uchar done_stat;
+ uchar host_stat;
+ uchar scsi_stat;
+ uchar scsi_msg;
+} ASC_SCSIQ_3;
+
+typedef struct asc_scsiq_4 {
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar y_first_sg_list_qp;
+ uchar y_working_sg_qp;
+ uchar y_working_sg_ix;
+ uchar y_res;
+ ushort x_req_count;
+ ushort x_reconnect_rtn;
+ ASC_PADDR x_saved_data_addr;
+ ASC_DCNT x_saved_data_cnt;
+} ASC_SCSIQ_4;
+
+typedef struct asc_q_done_info {
+ ASC_SCSIQ_2 d2;
+ ASC_SCSIQ_3 d3;
+ uchar q_status;
+ uchar q_no;
+ uchar cntl;
+ uchar sense_len;
+ uchar extra_bytes;
+ uchar res;
+ ASC_DCNT remain_bytes;
+} ASC_QDONE_INFO;
+
+typedef struct asc_sg_list {
+ ASC_PADDR addr;
+ ASC_DCNT bytes;
+} ASC_SG_LIST;
+
+typedef struct asc_sg_head {
+ ushort entry_cnt;
+ ushort queue_cnt;
+ ushort entry_to_copy;
+ ushort res;
+ ASC_SG_LIST sg_list[ASC_MAX_SG_LIST];
+} ASC_SG_HEAD;
+
+#define ASC_MIN_SG_LIST 2
+
+typedef struct asc_min_sg_head {
+ ushort entry_cnt;
+ ushort queue_cnt;
+ ushort entry_to_copy;
+ ushort res;
+ ASC_SG_LIST sg_list[ASC_MIN_SG_LIST];
+} ASC_MIN_SG_HEAD;
+
+#define QCX_SORT (0x0001)
+#define QCX_COALEASE (0x0002)
+
+typedef struct asc_scsi_q {
+ ASC_SCSIQ_1 q1;
+ ASC_SCSIQ_2 q2;
+ uchar *cdbptr;
+ ASC_SG_HEAD *sg_head;
+ ushort remain_sg_entry_cnt;
+ ushort next_sg_index;
+} ASC_SCSI_Q;
+
+typedef struct asc_scsi_req_q {
+ ASC_SCSIQ_1 r1;
+ ASC_SCSIQ_2 r2;
+ uchar *cdbptr;
+ ASC_SG_HEAD *sg_head;
+ uchar *sense_ptr;
+ ASC_SCSIQ_3 r3;
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar sense[ASC_MIN_SENSE_LEN];
+} ASC_SCSI_REQ_Q;
+
+typedef struct asc_scsi_bios_req_q {
+ ASC_SCSIQ_1 r1;
+ ASC_SCSIQ_2 r2;
+ uchar *cdbptr;
+ ASC_SG_HEAD *sg_head;
+ uchar *sense_ptr;
+ ASC_SCSIQ_3 r3;
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar sense[ASC_MIN_SENSE_LEN];
+} ASC_SCSI_BIOS_REQ_Q;
+
+typedef struct asc_risc_q {
+ uchar fwd;
+ uchar bwd;
+ ASC_SCSIQ_1 i1;
+ ASC_SCSIQ_2 i2;
+ ASC_SCSIQ_3 i3;
+ ASC_SCSIQ_4 i4;
+} ASC_RISC_Q;
+
+typedef struct asc_sg_list_q {
+ uchar seq_no;
+ uchar q_no;
+ uchar cntl;
+ uchar sg_head_qp;
+ uchar sg_list_cnt;
+ uchar sg_cur_list_cnt;
+} ASC_SG_LIST_Q;
+
+typedef struct asc_risc_sg_list_q {
+ uchar fwd;
+ uchar bwd;
+ ASC_SG_LIST_Q sg;
+ ASC_SG_LIST sg_list[7];
+} ASC_RISC_SG_LIST_Q;
+
+#define ASC_EXE_SCSI_IO_MAX_IDLE_LOOP 0x1000000UL
+#define ASC_EXE_SCSI_IO_MAX_WAIT_LOOP 1024
+#define ASCQ_ERR_NO_ERROR 0
+#define ASCQ_ERR_IO_NOT_FOUND 1
+#define ASCQ_ERR_LOCAL_MEM 2
+#define ASCQ_ERR_CHKSUM 3
+#define ASCQ_ERR_START_CHIP 4
+#define ASCQ_ERR_INT_TARGET_ID 5
+#define ASCQ_ERR_INT_LOCAL_MEM 6
+#define ASCQ_ERR_HALT_RISC 7
+#define ASCQ_ERR_GET_ASPI_ENTRY 8
+#define ASCQ_ERR_CLOSE_ASPI 9
+#define ASCQ_ERR_HOST_INQUIRY 0x0A
+#define ASCQ_ERR_SAVED_SRB_BAD 0x0B
+#define ASCQ_ERR_QCNTL_SG_LIST 0x0C
+#define ASCQ_ERR_Q_STATUS 0x0D
+#define ASCQ_ERR_WR_SCSIQ 0x0E
+#define ASCQ_ERR_PC_ADDR 0x0F
+#define ASCQ_ERR_SYN_OFFSET 0x10
+#define ASCQ_ERR_SYN_XFER_TIME 0x11
+#define ASCQ_ERR_LOCK_DMA 0x12
+#define ASCQ_ERR_UNLOCK_DMA 0x13
+#define ASCQ_ERR_VDS_CHK_INSTALL 0x14
+#define ASCQ_ERR_MICRO_CODE_HALT 0x15
+#define ASCQ_ERR_SET_LRAM_ADDR 0x16
+#define ASCQ_ERR_CUR_QNG 0x17
+#define ASCQ_ERR_SG_Q_LINKS 0x18
+#define ASCQ_ERR_SCSIQ_PTR 0x19
+#define ASCQ_ERR_ISR_RE_ENTRY 0x1A
+#define ASCQ_ERR_CRITICAL_RE_ENTRY 0x1B
+#define ASCQ_ERR_ISR_ON_CRITICAL 0x1C
+#define ASCQ_ERR_SG_LIST_ODD_ADDRESS 0x1D
+#define ASCQ_ERR_XFER_ADDRESS_TOO_BIG 0x1E
+#define ASCQ_ERR_SCSIQ_NULL_PTR 0x1F
+#define ASCQ_ERR_SCSIQ_BAD_NEXT_PTR 0x20
+#define ASCQ_ERR_GET_NUM_OF_FREE_Q 0x21
+#define ASCQ_ERR_SEND_SCSI_Q 0x22
+#define ASCQ_ERR_HOST_REQ_RISC_HALT 0x23
+#define ASCQ_ERR_RESET_SDTR 0x24
+
+/*
+ * Warning code values are set in ASC_DVC_VAR 'warn_code'.
+ */
+#define ASC_WARN_NO_ERROR 0x0000
+#define ASC_WARN_IO_PORT_ROTATE 0x0001
+#define ASC_WARN_EEPROM_CHKSUM 0x0002
+#define ASC_WARN_IRQ_MODIFIED 0x0004
+#define ASC_WARN_AUTO_CONFIG 0x0008
+#define ASC_WARN_CMD_QNG_CONFLICT 0x0010
+#define ASC_WARN_EEPROM_RECOVER 0x0020
+#define ASC_WARN_CFG_MSW_RECOVER 0x0040
+#define ASC_WARN_SET_PCI_CONFIG_SPACE 0x0080
+
+/*
+ * Error code values are set in ASC_DVC_VAR 'err_code'.
+ */
+#define ASC_IERR_WRITE_EEPROM 0x0001
+#define ASC_IERR_MCODE_CHKSUM 0x0002
+#define ASC_IERR_SET_PC_ADDR 0x0004
+#define ASC_IERR_START_STOP_CHIP 0x0008
+#define ASC_IERR_IRQ_NO 0x0010
+#define ASC_IERR_SET_IRQ_NO 0x0020
+#define ASC_IERR_CHIP_VERSION 0x0040
+#define ASC_IERR_SET_SCSI_ID 0x0080
+#define ASC_IERR_GET_PHY_ADDR 0x0100
+#define ASC_IERR_BAD_SIGNATURE 0x0200
+#define ASC_IERR_NO_BUS_TYPE 0x0400
+#define ASC_IERR_SCAM 0x0800
+#define ASC_IERR_SET_SDTR 0x1000
+#define ASC_IERR_RW_LRAM 0x8000
+
+#define ASC_DEF_IRQ_NO 10
+#define ASC_MAX_IRQ_NO 15
+#define ASC_MIN_IRQ_NO 10
+#define ASC_MIN_REMAIN_Q (0x02)
+#define ASC_DEF_MAX_TOTAL_QNG (0xF0)
+#define ASC_MIN_TAG_Q_PER_DVC (0x04)
+#define ASC_DEF_TAG_Q_PER_DVC (0x04)
+#define ASC_MIN_FREE_Q ASC_MIN_REMAIN_Q
+#define ASC_MIN_TOTAL_QNG ((ASC_MAX_SG_QUEUE)+(ASC_MIN_FREE_Q))
+#define ASC_MAX_TOTAL_QNG 240
+#define ASC_MAX_PCI_ULTRA_INRAM_TOTAL_QNG 16
+#define ASC_MAX_PCI_ULTRA_INRAM_TAG_QNG 8
+#define ASC_MAX_PCI_INRAM_TOTAL_QNG 20
+#define ASC_MAX_INRAM_TAG_QNG 16
+#define ASC_IOADR_TABLE_MAX_IX 11
+#define ASC_IOADR_GAP 0x10
+#define ASC_SEARCH_IOP_GAP 0x10
+#define ASC_MIN_IOP_ADDR (PortAddr)0x0100
+#define ASC_MAX_IOP_ADDR (PortAddr)0x3F0
+#define ASC_IOADR_1 (PortAddr)0x0110
+#define ASC_IOADR_2 (PortAddr)0x0130
+#define ASC_IOADR_3 (PortAddr)0x0150
+#define ASC_IOADR_4 (PortAddr)0x0190
+#define ASC_IOADR_5 (PortAddr)0x0210
+#define ASC_IOADR_6 (PortAddr)0x0230
+#define ASC_IOADR_7 (PortAddr)0x0250
+#define ASC_IOADR_8 (PortAddr)0x0330
+#define ASC_IOADR_DEF ASC_IOADR_8
+#define ASC_LIB_SCSIQ_WK_SP 256
+#define ASC_MAX_SYN_XFER_NO 16
+#define ASC_SYN_MAX_OFFSET 0x0F
+#define ASC_DEF_SDTR_OFFSET 0x0F
+#define ASC_DEF_SDTR_INDEX 0x00
+#define ASC_SDTR_ULTRA_PCI_10MB_INDEX 0x02
+#define SYN_XFER_NS_0 25
+#define SYN_XFER_NS_1 30
+#define SYN_XFER_NS_2 35
+#define SYN_XFER_NS_3 40
+#define SYN_XFER_NS_4 50
+#define SYN_XFER_NS_5 60
+#define SYN_XFER_NS_6 70
+#define SYN_XFER_NS_7 85
+#define SYN_ULTRA_XFER_NS_0 12
+#define SYN_ULTRA_XFER_NS_1 19
+#define SYN_ULTRA_XFER_NS_2 25
+#define SYN_ULTRA_XFER_NS_3 32
+#define SYN_ULTRA_XFER_NS_4 38
+#define SYN_ULTRA_XFER_NS_5 44
+#define SYN_ULTRA_XFER_NS_6 50
+#define SYN_ULTRA_XFER_NS_7 57
+#define SYN_ULTRA_XFER_NS_8 63
+#define SYN_ULTRA_XFER_NS_9 69
+#define SYN_ULTRA_XFER_NS_10 75
+#define SYN_ULTRA_XFER_NS_11 82
+#define SYN_ULTRA_XFER_NS_12 88
+#define SYN_ULTRA_XFER_NS_13 94
+#define SYN_ULTRA_XFER_NS_14 100
+#define SYN_ULTRA_XFER_NS_15 107
+
+typedef struct ext_msg {
+ uchar msg_type;
+ uchar msg_len;
+ uchar msg_req;
+ union {
+ struct {
+ uchar sdtr_xfer_period;
+ uchar sdtr_req_ack_offset;
+ } sdtr;
+ struct {
+ uchar wdtr_width;
+ } wdtr;
+ struct {
+ uchar mdp_b3;
+ uchar mdp_b2;
+ uchar mdp_b1;
+ uchar mdp_b0;
+ } mdp;
+ } u_ext_msg;
+ uchar res;
+} EXT_MSG;
+
+#define xfer_period u_ext_msg.sdtr.sdtr_xfer_period
+#define req_ack_offset u_ext_msg.sdtr.sdtr_req_ack_offset
+#define wdtr_width u_ext_msg.wdtr.wdtr_width
+#define mdp_b3 u_ext_msg.mdp_b3
+#define mdp_b2 u_ext_msg.mdp_b2
+#define mdp_b1 u_ext_msg.mdp_b1
+#define mdp_b0 u_ext_msg.mdp_b0
+
+typedef struct asc_dvc_cfg {
+ ASC_SCSI_BIT_ID_TYPE can_tagged_qng;
+ ASC_SCSI_BIT_ID_TYPE cmd_qng_enabled;
+ ASC_SCSI_BIT_ID_TYPE disc_enable;
+ ASC_SCSI_BIT_ID_TYPE sdtr_enable;
+ uchar chip_scsi_id;
+ uchar isa_dma_speed;
+ uchar isa_dma_channel;
+ uchar chip_version;
+ ushort lib_serial_no;
+ ushort lib_version;
+ ushort mcode_date;
+ ushort mcode_version;
+ uchar max_tag_qng[ASC_MAX_TID + 1];
+ uchar *overrun_buf;
+ uchar sdtr_period_offset[ASC_MAX_TID + 1];
+ ushort pci_slot_info;
+ uchar adapter_info[6];
+ struct device *dev;
+} ASC_DVC_CFG;
+
+#define ASC_DEF_DVC_CNTL 0xFFFF
+#define ASC_DEF_CHIP_SCSI_ID 7
+#define ASC_DEF_ISA_DMA_SPEED 4
+#define ASC_INIT_STATE_NULL 0x0000
+#define ASC_INIT_STATE_BEG_GET_CFG 0x0001
+#define ASC_INIT_STATE_END_GET_CFG 0x0002
+#define ASC_INIT_STATE_BEG_SET_CFG 0x0004
+#define ASC_INIT_STATE_END_SET_CFG 0x0008
+#define ASC_INIT_STATE_BEG_LOAD_MC 0x0010
+#define ASC_INIT_STATE_END_LOAD_MC 0x0020
+#define ASC_INIT_STATE_BEG_INQUIRY 0x0040
+#define ASC_INIT_STATE_END_INQUIRY 0x0080
+#define ASC_INIT_RESET_SCSI_DONE 0x0100
+#define ASC_INIT_STATE_WITHOUT_EEP 0x8000
+#define ASC_PCI_DEVICE_ID_REV_A 0x1100
+#define ASC_PCI_DEVICE_ID_REV_B 0x1200
+#define ASC_BUG_FIX_IF_NOT_DWB 0x0001
+#define ASC_BUG_FIX_ASYN_USE_SYN 0x0002
+#define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41
+#define ASC_MIN_TAGGED_CMD 7
+#define ASC_MAX_SCSI_RESET_WAIT 30
+
+struct asc_dvc_var; /* Forward Declaration. */
+
+typedef void (* ASC_ISR_CALLBACK)(struct asc_dvc_var *, ASC_QDONE_INFO *);
+typedef int (* ASC_EXE_CALLBACK)(struct asc_dvc_var *, ASC_SCSI_Q *);
+
+typedef struct asc_dvc_var {
+ PortAddr iop_base;
+ ushort err_code;
+ ushort dvc_cntl;
+ ushort bug_fix_cntl;
+ ushort bus_type;
+ ASC_ISR_CALLBACK isr_callback;
+ ASC_EXE_CALLBACK exe_callback;
+ ASC_SCSI_BIT_ID_TYPE init_sdtr;
+ ASC_SCSI_BIT_ID_TYPE sdtr_done;
+ ASC_SCSI_BIT_ID_TYPE use_tagged_qng;
+ ASC_SCSI_BIT_ID_TYPE unit_not_ready;
+ ASC_SCSI_BIT_ID_TYPE queue_full_or_busy;
+ ASC_SCSI_BIT_ID_TYPE start_motor;
+ uchar scsi_reset_wait;
+ uchar chip_no;
+ char is_in_int;
+ uchar max_total_qng;
+ uchar cur_total_qng;
+ uchar in_critical_cnt;
+ uchar irq_no;
+ uchar last_q_shortage;
+ ushort init_state;
+ uchar cur_dvc_qng[ASC_MAX_TID + 1];
+ uchar max_dvc_qng[ASC_MAX_TID + 1];
+ ASC_SCSI_Q *scsiq_busy_head[ASC_MAX_TID + 1];
+ ASC_SCSI_Q *scsiq_busy_tail[ASC_MAX_TID + 1];
+ uchar sdtr_period_tbl[ASC_MAX_SYN_XFER_NO];
+ ASC_DVC_CFG *cfg;
+ ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer_always;
+ char redo_scam;
+ ushort res2;
+ uchar dos_int13_table[ASC_MAX_TID + 1];
+ ASC_DCNT max_dma_count;
+ ASC_SCSI_BIT_ID_TYPE no_scam;
+ ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer;
+ uchar max_sdtr_index;
+ uchar host_init_sdtr_index;
+ struct asc_board *drv_ptr;
+ ASC_DCNT uc_break;
+} ASC_DVC_VAR;
+
+typedef struct asc_dvc_inq_info {
+ uchar type[ASC_MAX_TID + 1][ASC_MAX_LUN + 1];
+} ASC_DVC_INQ_INFO;
+
+typedef struct asc_cap_info {
+ ASC_DCNT lba;
+ ASC_DCNT blk_size;
+} ASC_CAP_INFO;
+
+typedef struct asc_cap_info_array {
+ ASC_CAP_INFO cap_info[ASC_MAX_TID + 1][ASC_MAX_LUN + 1];
+} ASC_CAP_INFO_ARRAY;
+
+#define ASC_MCNTL_NO_SEL_TIMEOUT (ushort)0x0001
+#define ASC_MCNTL_NULL_TARGET (ushort)0x0002
+#define ASC_CNTL_INITIATOR (ushort)0x0001
+#define ASC_CNTL_BIOS_GT_1GB (ushort)0x0002
+#define ASC_CNTL_BIOS_GT_2_DISK (ushort)0x0004
+#define ASC_CNTL_BIOS_REMOVABLE (ushort)0x0008
+#define ASC_CNTL_NO_SCAM (ushort)0x0010
+#define ASC_CNTL_INT_MULTI_Q (ushort)0x0080
+#define ASC_CNTL_NO_LUN_SUPPORT (ushort)0x0040
+#define ASC_CNTL_NO_VERIFY_COPY (ushort)0x0100
+#define ASC_CNTL_RESET_SCSI (ushort)0x0200
+#define ASC_CNTL_INIT_INQUIRY (ushort)0x0400
+#define ASC_CNTL_INIT_VERBOSE (ushort)0x0800
+#define ASC_CNTL_SCSI_PARITY (ushort)0x1000
+#define ASC_CNTL_BURST_MODE (ushort)0x2000
+#define ASC_CNTL_SDTR_ENABLE_ULTRA (ushort)0x4000
+#define ASC_EEP_DVC_CFG_BEG_VL 2
+#define ASC_EEP_MAX_DVC_ADDR_VL 15
+#define ASC_EEP_DVC_CFG_BEG 32
+#define ASC_EEP_MAX_DVC_ADDR 45
+#define ASC_EEP_DEFINED_WORDS 10
+#define ASC_EEP_MAX_ADDR 63
+#define ASC_EEP_RES_WORDS 0
+#define ASC_EEP_MAX_RETRY 20
+#define ASC_MAX_INIT_BUSY_RETRY 8
+#define ASC_EEP_ISA_PNP_WSIZE 16
+
+/*
+ * These macros keep the chip SCSI id and ISA DMA speed
+ * bitfields in board order. C bitfields aren't portable
+ * between big and little-endian platforms so they are
+ * not used.
+ */
+
+#define ASC_EEP_GET_CHIP_ID(cfg) ((cfg)->id_speed & 0x0f)
+#define ASC_EEP_GET_DMA_SPD(cfg) (((cfg)->id_speed & 0xf0) >> 4)
+#define ASC_EEP_SET_CHIP_ID(cfg, sid) \
+ ((cfg)->id_speed = ((cfg)->id_speed & 0xf0) | ((sid) & ASC_MAX_TID))
+#define ASC_EEP_SET_DMA_SPD(cfg, spd) \
+ ((cfg)->id_speed = ((cfg)->id_speed & 0x0f) | ((spd) & 0x0f) << 4)
+
+typedef struct asceep_config {
+ ushort cfg_lsw;
+ ushort cfg_msw;
+ uchar init_sdtr;
+ uchar disc_enable;
+ uchar use_cmd_qng;
+ uchar start_motor;
+ uchar max_total_qng;
+ uchar max_tag_qng;
+ uchar bios_scan;
+ uchar power_up_wait;
+ uchar no_scam;
+ uchar id_speed; /* low order 4 bits is chip scsi id */
+ /* high order 4 bits is isa dma speed */
+ uchar dos_int13_table[ASC_MAX_TID + 1];
+ uchar adapter_info[6];
+ ushort cntl;
+ ushort chksum;
+} ASCEEP_CONFIG;
+
+#define ASC_PCI_CFG_LSW_SCSI_PARITY 0x0800
+#define ASC_PCI_CFG_LSW_BURST_MODE 0x0080
+#define ASC_PCI_CFG_LSW_INTR_ABLE 0x0020
+
+#define ASC_EEP_CMD_READ 0x80
+#define ASC_EEP_CMD_WRITE 0x40
+#define ASC_EEP_CMD_WRITE_ABLE 0x30
+#define ASC_EEP_CMD_WRITE_DISABLE 0x00
+#define ASC_OVERRUN_BSIZE 0x00000048UL
+#define ASC_CTRL_BREAK_ONCE 0x0001
+#define ASC_CTRL_BREAK_STAY_IDLE 0x0002
+#define ASCV_MSGOUT_BEG 0x0000
+#define ASCV_MSGOUT_SDTR_PERIOD (ASCV_MSGOUT_BEG+3)
+#define ASCV_MSGOUT_SDTR_OFFSET (ASCV_MSGOUT_BEG+4)
+#define ASCV_BREAK_SAVED_CODE (ushort)0x0006
+#define ASCV_MSGIN_BEG (ASCV_MSGOUT_BEG+8)
+#define ASCV_MSGIN_SDTR_PERIOD (ASCV_MSGIN_BEG+3)
+#define ASCV_MSGIN_SDTR_OFFSET (ASCV_MSGIN_BEG+4)
+#define ASCV_SDTR_DATA_BEG (ASCV_MSGIN_BEG+8)
+#define ASCV_SDTR_DONE_BEG (ASCV_SDTR_DATA_BEG+8)
+#define ASCV_MAX_DVC_QNG_BEG (ushort)0x0020
+#define ASCV_BREAK_ADDR (ushort)0x0028
+#define ASCV_BREAK_NOTIFY_COUNT (ushort)0x002A
+#define ASCV_BREAK_CONTROL (ushort)0x002C
+#define ASCV_BREAK_HIT_COUNT (ushort)0x002E
+
+#define ASCV_ASCDVC_ERR_CODE_W (ushort)0x0030
+#define ASCV_MCODE_CHKSUM_W (ushort)0x0032
+#define ASCV_MCODE_SIZE_W (ushort)0x0034
+#define ASCV_STOP_CODE_B (ushort)0x0036
+#define ASCV_DVC_ERR_CODE_B (ushort)0x0037
+#define ASCV_OVERRUN_PADDR_D (ushort)0x0038
+#define ASCV_OVERRUN_BSIZE_D (ushort)0x003C
+#define ASCV_HALTCODE_W (ushort)0x0040
+#define ASCV_CHKSUM_W (ushort)0x0042
+#define ASCV_MC_DATE_W (ushort)0x0044
+#define ASCV_MC_VER_W (ushort)0x0046
+#define ASCV_NEXTRDY_B (ushort)0x0048
+#define ASCV_DONENEXT_B (ushort)0x0049
+#define ASCV_USE_TAGGED_QNG_B (ushort)0x004A
+#define ASCV_SCSIBUSY_B (ushort)0x004B
+#define ASCV_Q_DONE_IN_PROGRESS_B (ushort)0x004C
+#define ASCV_CURCDB_B (ushort)0x004D
+#define ASCV_RCLUN_B (ushort)0x004E
+#define ASCV_BUSY_QHEAD_B (ushort)0x004F
+#define ASCV_DISC1_QHEAD_B (ushort)0x0050
+#define ASCV_DISC_ENABLE_B (ushort)0x0052
+#define ASCV_CAN_TAGGED_QNG_B (ushort)0x0053
+#define ASCV_HOSTSCSI_ID_B (ushort)0x0055
+#define ASCV_MCODE_CNTL_B (ushort)0x0056
+#define ASCV_NULL_TARGET_B (ushort)0x0057
+#define ASCV_FREE_Q_HEAD_W (ushort)0x0058
+#define ASCV_DONE_Q_TAIL_W (ushort)0x005A
+#define ASCV_FREE_Q_HEAD_B (ushort)(ASCV_FREE_Q_HEAD_W+1)
+#define ASCV_DONE_Q_TAIL_B (ushort)(ASCV_DONE_Q_TAIL_W+1)
+#define ASCV_HOST_FLAG_B (ushort)0x005D
+#define ASCV_TOTAL_READY_Q_B (ushort)0x0064
+#define ASCV_VER_SERIAL_B (ushort)0x0065
+#define ASCV_HALTCODE_SAVED_W (ushort)0x0066
+#define ASCV_WTM_FLAG_B (ushort)0x0068
+#define ASCV_RISC_FLAG_B (ushort)0x006A
+#define ASCV_REQ_SG_LIST_QP (ushort)0x006B
+#define ASC_HOST_FLAG_IN_ISR 0x01
+#define ASC_HOST_FLAG_ACK_INT 0x02
+#define ASC_RISC_FLAG_GEN_INT 0x01
+#define ASC_RISC_FLAG_REQ_SG_LIST 0x02
+#define IOP_CTRL (0x0F)
+#define IOP_STATUS (0x0E)
+#define IOP_INT_ACK IOP_STATUS
+#define IOP_REG_IFC (0x0D)
+#define IOP_SYN_OFFSET (0x0B)
+#define IOP_EXTRA_CONTROL (0x0D)
+#define IOP_REG_PC (0x0C)
+#define IOP_RAM_ADDR (0x0A)
+#define IOP_RAM_DATA (0x08)
+#define IOP_EEP_DATA (0x06)
+#define IOP_EEP_CMD (0x07)
+#define IOP_VERSION (0x03)
+#define IOP_CONFIG_HIGH (0x04)
+#define IOP_CONFIG_LOW (0x02)
+#define IOP_SIG_BYTE (0x01)
+#define IOP_SIG_WORD (0x00)
+#define IOP_REG_DC1 (0x0E)
+#define IOP_REG_DC0 (0x0C)
+#define IOP_REG_SB (0x0B)
+#define IOP_REG_DA1 (0x0A)
+#define IOP_REG_DA0 (0x08)
+#define IOP_REG_SC (0x09)
+#define IOP_DMA_SPEED (0x07)
+#define IOP_REG_FLAG (0x07)
+#define IOP_FIFO_H (0x06)
+#define IOP_FIFO_L (0x04)
+#define IOP_REG_ID (0x05)
+#define IOP_REG_QP (0x03)
+#define IOP_REG_IH (0x02)
+#define IOP_REG_IX (0x01)
+#define IOP_REG_AX (0x00)
+#define IFC_REG_LOCK (0x00)
+#define IFC_REG_UNLOCK (0x09)
+#define IFC_WR_EN_FILTER (0x10)
+#define IFC_RD_NO_EEPROM (0x10)
+#define IFC_SLEW_RATE (0x20)
+#define IFC_ACT_NEG (0x40)
+#define IFC_INP_FILTER (0x80)
+#define IFC_INIT_DEFAULT (IFC_ACT_NEG | IFC_REG_UNLOCK)
+#define SC_SEL (uchar)(0x80)
+#define SC_BSY (uchar)(0x40)
+#define SC_ACK (uchar)(0x20)
+#define SC_REQ (uchar)(0x10)
+#define SC_ATN (uchar)(0x08)
+#define SC_IO (uchar)(0x04)
+#define SC_CD (uchar)(0x02)
+#define SC_MSG (uchar)(0x01)
+#define SEC_SCSI_CTL (uchar)(0x80)
+#define SEC_ACTIVE_NEGATE (uchar)(0x40)
+#define SEC_SLEW_RATE (uchar)(0x20)
+#define SEC_ENABLE_FILTER (uchar)(0x10)
+#define ASC_HALT_EXTMSG_IN (ushort)0x8000
+#define ASC_HALT_CHK_CONDITION (ushort)0x8100
+#define ASC_HALT_SS_QUEUE_FULL (ushort)0x8200
+#define ASC_HALT_DISABLE_ASYN_USE_SYN_FIX (ushort)0x8300
+#define ASC_HALT_ENABLE_ASYN_USE_SYN_FIX (ushort)0x8400
+#define ASC_HALT_SDTR_REJECTED (ushort)0x4000
+#define ASC_HALT_HOST_COPY_SG_LIST_TO_RISC ( ushort )0x2000
+#define ASC_MAX_QNO 0xF8
+#define ASC_DATA_SEC_BEG (ushort)0x0080
+#define ASC_DATA_SEC_END (ushort)0x0080
+#define ASC_CODE_SEC_BEG (ushort)0x0080
+#define ASC_CODE_SEC_END (ushort)0x0080
+#define ASC_QADR_BEG (0x4000)
+#define ASC_QADR_USED (ushort)(ASC_MAX_QNO * 64)
+#define ASC_QADR_END (ushort)0x7FFF
+#define ASC_QLAST_ADR (ushort)0x7FC0
+#define ASC_QBLK_SIZE 0x40
+#define ASC_BIOS_DATA_QBEG 0xF8
+#define ASC_MIN_ACTIVE_QNO 0x01
+#define ASC_QLINK_END 0xFF
+#define ASC_EEPROM_WORDS 0x10
+#define ASC_MAX_MGS_LEN 0x10
+#define ASC_BIOS_ADDR_DEF 0xDC00
+#define ASC_BIOS_SIZE 0x3800
+#define ASC_BIOS_RAM_OFF 0x3800
+#define ASC_BIOS_RAM_SIZE 0x800
+#define ASC_BIOS_MIN_ADDR 0xC000
+#define ASC_BIOS_MAX_ADDR 0xEC00
+#define ASC_BIOS_BANK_SIZE 0x0400
+#define ASC_MCODE_START_ADDR 0x0080
+#define ASC_CFG0_HOST_INT_ON 0x0020
+#define ASC_CFG0_BIOS_ON 0x0040
+#define ASC_CFG0_VERA_BURST_ON 0x0080
+#define ASC_CFG0_SCSI_PARITY_ON 0x0800
+#define ASC_CFG1_SCSI_TARGET_ON 0x0080
+#define ASC_CFG1_LRAM_8BITS_ON 0x0800
+#define ASC_CFG_MSW_CLR_MASK 0x3080
+#define CSW_TEST1 (ASC_CS_TYPE)0x8000
+#define CSW_AUTO_CONFIG (ASC_CS_TYPE)0x4000
+#define CSW_RESERVED1 (ASC_CS_TYPE)0x2000
+#define CSW_IRQ_WRITTEN (ASC_CS_TYPE)0x1000
+#define CSW_33MHZ_SELECTED (ASC_CS_TYPE)0x0800
+#define CSW_TEST2 (ASC_CS_TYPE)0x0400
+#define CSW_TEST3 (ASC_CS_TYPE)0x0200
+#define CSW_RESERVED2 (ASC_CS_TYPE)0x0100
+#define CSW_DMA_DONE (ASC_CS_TYPE)0x0080
+#define CSW_FIFO_RDY (ASC_CS_TYPE)0x0040
+#define CSW_EEP_READ_DONE (ASC_CS_TYPE)0x0020
+#define CSW_HALTED (ASC_CS_TYPE)0x0010
+#define CSW_SCSI_RESET_ACTIVE (ASC_CS_TYPE)0x0008
+#define CSW_PARITY_ERR (ASC_CS_TYPE)0x0004
+#define CSW_SCSI_RESET_LATCH (ASC_CS_TYPE)0x0002
+#define CSW_INT_PENDING (ASC_CS_TYPE)0x0001
+#define CIW_CLR_SCSI_RESET_INT (ASC_CS_TYPE)0x1000
+#define CIW_INT_ACK (ASC_CS_TYPE)0x0100
+#define CIW_TEST1 (ASC_CS_TYPE)0x0200
+#define CIW_TEST2 (ASC_CS_TYPE)0x0400
+#define CIW_SEL_33MHZ (ASC_CS_TYPE)0x0800
+#define CIW_IRQ_ACT (ASC_CS_TYPE)0x1000
+#define CC_CHIP_RESET (uchar)0x80
+#define CC_SCSI_RESET (uchar)0x40
+#define CC_HALT (uchar)0x20
+#define CC_SINGLE_STEP (uchar)0x10
+#define CC_DMA_ABLE (uchar)0x08
+#define CC_TEST (uchar)0x04
+#define CC_BANK_ONE (uchar)0x02
+#define CC_DIAG (uchar)0x01
+#define ASC_1000_ID0W 0x04C1
+#define ASC_1000_ID0W_FIX 0x00C1
+#define ASC_1000_ID1B 0x25
+#define ASC_EISA_BIG_IOP_GAP (0x1C30-0x0C50)
+#define ASC_EISA_SMALL_IOP_GAP (0x0020)
+#define ASC_EISA_MIN_IOP_ADDR (0x0C30)
+#define ASC_EISA_MAX_IOP_ADDR (0xFC50)
+#define ASC_EISA_REV_IOP_MASK (0x0C83)
+#define ASC_EISA_PID_IOP_MASK (0x0C80)
+#define ASC_EISA_CFG_IOP_MASK (0x0C86)
+#define ASC_GET_EISA_SLOT(iop) (PortAddr)((iop) & 0xF000)
+#define ASC_EISA_ID_740 0x01745004UL
+#define ASC_EISA_ID_750 0x01755004UL
+#define INS_HALTINT (ushort)0x6281
+#define INS_HALT (ushort)0x6280
+#define INS_SINT (ushort)0x6200
+#define INS_RFLAG_WTM (ushort)0x7380
+#define ASC_MC_SAVE_CODE_WSIZE 0x500
+#define ASC_MC_SAVE_DATA_WSIZE 0x40
+
+typedef struct asc_mc_saved {
+ ushort data[ASC_MC_SAVE_DATA_WSIZE];
+ ushort code[ASC_MC_SAVE_CODE_WSIZE];
+} ASC_MC_SAVED;
+
+#define AscGetQDoneInProgress(port) AscReadLramByte((port), ASCV_Q_DONE_IN_PROGRESS_B)
+#define AscPutQDoneInProgress(port, val) AscWriteLramByte((port), ASCV_Q_DONE_IN_PROGRESS_B, val)
+#define AscGetVarFreeQHead(port) AscReadLramWord((port), ASCV_FREE_Q_HEAD_W)
+#define AscGetVarDoneQTail(port) AscReadLramWord((port), ASCV_DONE_Q_TAIL_W)
+#define AscPutVarFreeQHead(port, val) AscWriteLramWord((port), ASCV_FREE_Q_HEAD_W, val)
+#define AscPutVarDoneQTail(port, val) AscWriteLramWord((port), ASCV_DONE_Q_TAIL_W, val)
+#define AscGetRiscVarFreeQHead(port) AscReadLramByte((port), ASCV_NEXTRDY_B)
+#define AscGetRiscVarDoneQTail(port) AscReadLramByte((port), ASCV_DONENEXT_B)
+#define AscPutRiscVarFreeQHead(port, val) AscWriteLramByte((port), ASCV_NEXTRDY_B, val)
+#define AscPutRiscVarDoneQTail(port, val) AscWriteLramByte((port), ASCV_DONENEXT_B, val)
+#define AscPutMCodeSDTRDoneAtID(port, id, data) AscWriteLramByte((port), (ushort)((ushort)ASCV_SDTR_DONE_BEG+(ushort)id), (data));
+#define AscGetMCodeSDTRDoneAtID(port, id) AscReadLramByte((port), (ushort)((ushort)ASCV_SDTR_DONE_BEG+(ushort)id));
+#define AscPutMCodeInitSDTRAtID(port, id, data) AscWriteLramByte((port), (ushort)((ushort)ASCV_SDTR_DATA_BEG+(ushort)id), data);
+#define AscGetMCodeInitSDTRAtID(port, id) AscReadLramByte((port), (ushort)((ushort)ASCV_SDTR_DATA_BEG+(ushort)id));
+#define AscSynIndexToPeriod(index) (uchar)(asc_dvc->sdtr_period_tbl[ (index) ])
+#define AscGetChipSignatureByte(port) (uchar)inp((port)+IOP_SIG_BYTE)
+#define AscGetChipSignatureWord(port) (ushort)inpw((port)+IOP_SIG_WORD)
+#define AscGetChipVerNo(port) (uchar)inp((port)+IOP_VERSION)
+#define AscGetChipCfgLsw(port) (ushort)inpw((port)+IOP_CONFIG_LOW)
+#define AscGetChipCfgMsw(port) (ushort)inpw((port)+IOP_CONFIG_HIGH)
+#define AscSetChipCfgLsw(port, data) outpw((port)+IOP_CONFIG_LOW, data)
+#define AscSetChipCfgMsw(port, data) outpw((port)+IOP_CONFIG_HIGH, data)
+#define AscGetChipEEPCmd(port) (uchar)inp((port)+IOP_EEP_CMD)
+#define AscSetChipEEPCmd(port, data) outp((port)+IOP_EEP_CMD, data)
+#define AscGetChipEEPData(port) (ushort)inpw((port)+IOP_EEP_DATA)
+#define AscSetChipEEPData(port, data) outpw((port)+IOP_EEP_DATA, data)
+#define AscGetChipLramAddr(port) (ushort)inpw((PortAddr)((port)+IOP_RAM_ADDR))
+#define AscSetChipLramAddr(port, addr) outpw((PortAddr)((port)+IOP_RAM_ADDR), addr)
+#define AscGetChipLramData(port) (ushort)inpw((port)+IOP_RAM_DATA)
+#define AscSetChipLramData(port, data) outpw((port)+IOP_RAM_DATA, data)
+#define AscGetChipIFC(port) (uchar)inp((port)+IOP_REG_IFC)
+#define AscSetChipIFC(port, data) outp((port)+IOP_REG_IFC, data)
+#define AscGetChipStatus(port) (ASC_CS_TYPE)inpw((port)+IOP_STATUS)
+#define AscSetChipStatus(port, cs_val) outpw((port)+IOP_STATUS, cs_val)
+#define AscGetChipControl(port) (uchar)inp((port)+IOP_CTRL)
+#define AscSetChipControl(port, cc_val) outp((port)+IOP_CTRL, cc_val)
+#define AscGetChipSyn(port) (uchar)inp((port)+IOP_SYN_OFFSET)
+#define AscSetChipSyn(port, data) outp((port)+IOP_SYN_OFFSET, data)
+#define AscSetPCAddr(port, data) outpw((port)+IOP_REG_PC, data)
+#define AscGetPCAddr(port) (ushort)inpw((port)+IOP_REG_PC)
+#define AscIsIntPending(port) (AscGetChipStatus(port) & (CSW_INT_PENDING | CSW_SCSI_RESET_LATCH))
+#define AscGetChipScsiID(port) ((AscGetChipCfgLsw(port) >> 8) & ASC_MAX_TID)
+#define AscGetExtraControl(port) (uchar)inp((port)+IOP_EXTRA_CONTROL)
+#define AscSetExtraControl(port, data) outp((port)+IOP_EXTRA_CONTROL, data)
+#define AscReadChipAX(port) (ushort)inpw((port)+IOP_REG_AX)
+#define AscWriteChipAX(port, data) outpw((port)+IOP_REG_AX, data)
+#define AscReadChipIX(port) (uchar)inp((port)+IOP_REG_IX)
+#define AscWriteChipIX(port, data) outp((port)+IOP_REG_IX, data)
+#define AscReadChipIH(port) (ushort)inpw((port)+IOP_REG_IH)
+#define AscWriteChipIH(port, data) outpw((port)+IOP_REG_IH, data)
+#define AscReadChipQP(port) (uchar)inp((port)+IOP_REG_QP)
+#define AscWriteChipQP(port, data) outp((port)+IOP_REG_QP, data)
+#define AscReadChipFIFO_L(port) (ushort)inpw((port)+IOP_REG_FIFO_L)
+#define AscWriteChipFIFO_L(port, data) outpw((port)+IOP_REG_FIFO_L, data)
+#define AscReadChipFIFO_H(port) (ushort)inpw((port)+IOP_REG_FIFO_H)
+#define AscWriteChipFIFO_H(port, data) outpw((port)+IOP_REG_FIFO_H, data)
+#define AscReadChipDmaSpeed(port) (uchar)inp((port)+IOP_DMA_SPEED)
+#define AscWriteChipDmaSpeed(port, data) outp((port)+IOP_DMA_SPEED, data)
+#define AscReadChipDA0(port) (ushort)inpw((port)+IOP_REG_DA0)
+#define AscWriteChipDA0(port) outpw((port)+IOP_REG_DA0, data)
+#define AscReadChipDA1(port) (ushort)inpw((port)+IOP_REG_DA1)
+#define AscWriteChipDA1(port) outpw((port)+IOP_REG_DA1, data)
+#define AscReadChipDC0(port) (ushort)inpw((port)+IOP_REG_DC0)
+#define AscWriteChipDC0(port) outpw((port)+IOP_REG_DC0, data)
+#define AscReadChipDC1(port) (ushort)inpw((port)+IOP_REG_DC1)
+#define AscWriteChipDC1(port) outpw((port)+IOP_REG_DC1, data)
+#define AscReadChipDvcID(port) (uchar)inp((port)+IOP_REG_ID)
+#define AscWriteChipDvcID(port, data) outp((port)+IOP_REG_ID, data)
+
+STATIC int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg);
+STATIC int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg);
+STATIC void AscWaitEEPRead(void);
+STATIC void AscWaitEEPWrite(void);
+STATIC ushort AscReadEEPWord(PortAddr, uchar);
+STATIC ushort AscWriteEEPWord(PortAddr, uchar, ushort);
+STATIC ushort AscGetEEPConfig(PortAddr, ASCEEP_CONFIG *, ushort);
+STATIC int AscSetEEPConfigOnce(PortAddr, ASCEEP_CONFIG *, ushort);
+STATIC int AscSetEEPConfig(PortAddr, ASCEEP_CONFIG *, ushort);
+STATIC int AscStartChip(PortAddr);
+STATIC int AscStopChip(PortAddr);
+STATIC void AscSetChipIH(PortAddr, ushort);
+STATIC int AscIsChipHalted(PortAddr);
+STATIC void AscAckInterrupt(PortAddr);
+STATIC void AscDisableInterrupt(PortAddr);
+STATIC void AscEnableInterrupt(PortAddr);
+STATIC void AscSetBank(PortAddr, uchar);
+STATIC int AscResetChipAndScsiBus(ASC_DVC_VAR *);
+#ifdef CONFIG_ISA
+STATIC ushort AscGetIsaDmaChannel(PortAddr);
+STATIC ushort AscSetIsaDmaChannel(PortAddr, ushort);
+STATIC uchar AscSetIsaDmaSpeed(PortAddr, uchar);
+STATIC uchar AscGetIsaDmaSpeed(PortAddr);
+#endif /* CONFIG_ISA */
+STATIC uchar AscReadLramByte(PortAddr, ushort);
+STATIC ushort AscReadLramWord(PortAddr, ushort);
+#if CC_VERY_LONG_SG_LIST
+STATIC ASC_DCNT AscReadLramDWord(PortAddr, ushort);
+#endif /* CC_VERY_LONG_SG_LIST */
+STATIC void AscWriteLramWord(PortAddr, ushort, ushort);
+STATIC void AscWriteLramByte(PortAddr, ushort, uchar);
+STATIC ASC_DCNT AscMemSumLramWord(PortAddr, ushort, int);
+STATIC void AscMemWordSetLram(PortAddr, ushort, ushort, int);
+STATIC void AscMemWordCopyPtrToLram(PortAddr, ushort, uchar *, int);
+STATIC void AscMemDWordCopyPtrToLram(PortAddr, ushort, uchar *, int);
+STATIC void AscMemWordCopyPtrFromLram(PortAddr, ushort, uchar *, int);
+STATIC ushort AscInitAscDvcVar(ASC_DVC_VAR *);
+STATIC ushort AscInitFromEEP(ASC_DVC_VAR *);
+STATIC ushort AscInitFromAscDvcVar(ASC_DVC_VAR *);
+STATIC ushort AscInitMicroCodeVar(ASC_DVC_VAR *);
+STATIC int AscTestExternalLram(ASC_DVC_VAR *);
+STATIC uchar AscMsgOutSDTR(ASC_DVC_VAR *, uchar, uchar);
+STATIC uchar AscCalSDTRData(ASC_DVC_VAR *, uchar, uchar);
+STATIC void AscSetChipSDTR(PortAddr, uchar, uchar);
+STATIC uchar AscGetSynPeriodIndex(ASC_DVC_VAR *, uchar);
+STATIC uchar AscAllocFreeQueue(PortAddr, uchar);
+STATIC uchar AscAllocMultipleFreeQueue(PortAddr, uchar, uchar);
+STATIC int AscHostReqRiscHalt(PortAddr);
+STATIC int AscStopQueueExe(PortAddr);
+STATIC int AscSendScsiQueue(ASC_DVC_VAR *,
+ ASC_SCSI_Q * scsiq,
+ uchar n_q_required);
+STATIC int AscPutReadyQueue(ASC_DVC_VAR *,
+ ASC_SCSI_Q *, uchar);
+STATIC int AscPutReadySgListQueue(ASC_DVC_VAR *,
+ ASC_SCSI_Q *, uchar);
+STATIC int AscSetChipSynRegAtID(PortAddr, uchar, uchar);
+STATIC int AscSetRunChipSynRegAtID(PortAddr, uchar, uchar);
+STATIC ushort AscInitLram(ASC_DVC_VAR *);
+STATIC ushort AscInitQLinkVar(ASC_DVC_VAR *);
+STATIC int AscSetLibErrorCode(ASC_DVC_VAR *, ushort);
+STATIC int AscIsrChipHalted(ASC_DVC_VAR *);
+STATIC uchar _AscCopyLramScsiDoneQ(PortAddr, ushort,
+ ASC_QDONE_INFO *, ASC_DCNT);
+STATIC int AscIsrQDone(ASC_DVC_VAR *);
+STATIC int AscCompareString(uchar *, uchar *, int);
+#ifdef CONFIG_ISA
+STATIC ushort AscGetEisaChipCfg(PortAddr);
+STATIC ASC_DCNT AscGetEisaProductID(PortAddr);
+STATIC PortAddr AscSearchIOPortAddrEISA(PortAddr);
+STATIC PortAddr AscSearchIOPortAddr11(PortAddr);
+STATIC PortAddr AscSearchIOPortAddr(PortAddr, ushort);
+STATIC void AscSetISAPNPWaitForKey(void);
+#endif /* CONFIG_ISA */
+STATIC uchar AscGetChipScsiCtrl(PortAddr);
+STATIC uchar AscSetChipScsiID(PortAddr, uchar);
+STATIC uchar AscGetChipVersion(PortAddr, ushort);
+STATIC ushort AscGetChipBusType(PortAddr);
+STATIC ASC_DCNT AscLoadMicroCode(PortAddr, ushort, uchar *, ushort);
+STATIC int AscFindSignature(PortAddr);
+STATIC void AscToggleIRQAct(PortAddr);
+STATIC uchar AscGetChipIRQ(PortAddr, ushort);
+STATIC uchar AscSetChipIRQ(PortAddr, uchar, ushort);
+STATIC ushort AscGetChipBiosAddress(PortAddr, ushort);
+STATIC inline ulong DvcEnterCritical(void);
+STATIC inline void DvcLeaveCritical(ulong);
+#ifdef CONFIG_PCI
+STATIC uchar DvcReadPCIConfigByte(ASC_DVC_VAR *, ushort);
+STATIC void DvcWritePCIConfigByte(ASC_DVC_VAR *,
+ ushort, uchar);
+#endif /* CONFIG_PCI */
+STATIC ushort AscGetChipBiosAddress(PortAddr, ushort);
+STATIC void DvcSleepMilliSecond(ASC_DCNT);
+STATIC void DvcDelayNanoSecond(ASC_DVC_VAR *, ASC_DCNT);
+STATIC void DvcPutScsiQ(PortAddr, ushort, uchar *, int);
+STATIC void DvcGetQinfo(PortAddr, ushort, uchar *, int);
+STATIC ushort AscInitGetConfig(ASC_DVC_VAR *);
+STATIC ushort AscInitSetConfig(ASC_DVC_VAR *);
+STATIC ushort AscInitAsc1000Driver(ASC_DVC_VAR *);
+STATIC void AscAsyncFix(ASC_DVC_VAR *, uchar,
+ ASC_SCSI_INQUIRY *);
+STATIC int AscTagQueuingSafe(ASC_SCSI_INQUIRY *);
+STATIC void AscInquiryHandling(ASC_DVC_VAR *,
+ uchar, ASC_SCSI_INQUIRY *);
+STATIC int AscExeScsiQueue(ASC_DVC_VAR *, ASC_SCSI_Q *);
+STATIC int AscISR(ASC_DVC_VAR *);
+STATIC uint AscGetNumOfFreeQueue(ASC_DVC_VAR *, uchar,
+ uchar);
+STATIC int AscSgListToQueue(int);
+#ifdef CONFIG_ISA
+STATIC void AscEnableIsaDma(uchar);
+#endif /* CONFIG_ISA */
+STATIC ASC_DCNT AscGetMaxDmaCount(ushort);
+
+
+/*
+ * --- Adv Library Constants and Macros
+ */
+
+#define ADV_LIB_VERSION_MAJOR 5
+#define ADV_LIB_VERSION_MINOR 14
+
+/*
+ * Define Adv Library required special types.
+ */
+
+/*
+ * Portable Data Types
+ *
+ * Any instance where a 32-bit long or pointer type is assumed
+ * for precision or HW defined structures, the following define
+ * types must be used. In Linux the char, short, and int types
+ * are all consistent at 8, 16, and 32 bits respectively. Pointers
+ * and long types are 64 bits on Alpha and UltraSPARC.
+ */
+#define ADV_PADDR __u32 /* Physical address data type. */
+#define ADV_VADDR __u32 /* Virtual address data type. */
+#define ADV_DCNT __u32 /* Unsigned Data count type. */
+#define ADV_SDCNT __s32 /* Signed Data count type. */
+
+/*
+ * These macros are used to convert a virtual address to a
+ * 32-bit value. This currently can be used on Linux Alpha
+ * which uses 64-bit virtual address but a 32-bit bus address.
+ * This is likely to break in the future, but doing this now
+ * will give us time to change the HW and FW to handle 64-bit
+ * addresses.
+ */
+#define ADV_VADDR_TO_U32 virt_to_bus
+#define ADV_U32_TO_VADDR bus_to_virt
+
+#define AdvPortAddr ulong /* Virtual memory address size */
+
+/*
+ * Define Adv Library required memory access macros.
+ */
+#define ADV_MEM_READB(addr) readb(addr)
+#define ADV_MEM_READW(addr) readw(addr)
+#define ADV_MEM_WRITEB(addr, byte) writeb(byte, addr)
+#define ADV_MEM_WRITEW(addr, word) writew(word, addr)
+#define ADV_MEM_WRITEDW(addr, dword) writel(dword, addr)
+
+#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 15)
+
+/*
+ * For wide boards a CDB length maximum of 16 bytes
+ * is supported.
+ */
+#define ADV_MAX_CDB_LEN 16
+
+/*
+ * Define total number of simultaneous maximum element scatter-gather
+ * request blocks per wide adapter. ASC_DEF_MAX_HOST_QNG (253) is the
+ * maximum number of outstanding commands per wide host adapter. Each
+ * command uses one or more ADV_SG_BLOCK each with 15 scatter-gather
+ * elements. Allow each command to have at least one ADV_SG_BLOCK structure.
+ * This allows about 15 commands to have the maximum 17 ADV_SG_BLOCK
+ * structures or 255 scatter-gather elements.
+ *
+ */
+#define ADV_TOT_SG_BLOCK ASC_DEF_MAX_HOST_QNG
+
+/*
+ * Define Adv Library required maximum number of scatter-gather
+ * elements per request.
+ */
+#define ADV_MAX_SG_LIST 255
+
+/* Number of SG blocks needed. */
+#define ADV_NUM_SG_BLOCK \
+ ((ADV_MAX_SG_LIST + (NO_OF_SG_PER_BLOCK - 1))/NO_OF_SG_PER_BLOCK)
+
+/* Total contiguous memory needed for SG blocks. */
+#define ADV_SG_TOTAL_MEM_SIZE \
+ (sizeof(ADV_SG_BLOCK) * ADV_NUM_SG_BLOCK)
+
+#define ADV_PAGE_SIZE PAGE_SIZE
+
+#define ADV_NUM_PAGE_CROSSING \
+ ((ADV_SG_TOTAL_MEM_SIZE + (ADV_PAGE_SIZE - 1))/ADV_PAGE_SIZE)
+
+/* a_condor.h */
+#define ADV_PCI_VENDOR_ID 0x10CD
+#define ADV_PCI_DEVICE_ID_REV_A 0x2300
+#define ADV_PCI_DEVID_38C0800_REV1 0x2500
+#define ADV_PCI_DEVID_38C1600_REV1 0x2700
+
+#define ADV_EEP_DVC_CFG_BEGIN (0x00)
+#define ADV_EEP_DVC_CFG_END (0x15)
+#define ADV_EEP_DVC_CTL_BEGIN (0x16) /* location of OEM name */
+#define ADV_EEP_MAX_WORD_ADDR (0x1E)
+
+#define ADV_EEP_DELAY_MS 100
+
+#define ADV_EEPROM_BIG_ENDIAN 0x8000 /* EEPROM Bit 15 */
+#define ADV_EEPROM_BIOS_ENABLE 0x4000 /* EEPROM Bit 14 */
+/*
+ * For the ASC3550 Bit 13 is Termination Polarity control bit.
+ * For later ICs Bit 13 controls whether the CIS (Card Information
+ * Service Section) is loaded from EEPROM.
+ */
+#define ADV_EEPROM_TERM_POL 0x2000 /* EEPROM Bit 13 */
+#define ADV_EEPROM_CIS_LD 0x2000 /* EEPROM Bit 13 */
+/*
+ * ASC38C1600 Bit 11
+ *
+ * If EEPROM Bit 11 is 0 for Function 0, then Function 0 will specify
+ * INT A in the PCI Configuration Space Int Pin field. If it is 1, then
+ * Function 0 will specify INT B.
+ *
+ * If EEPROM Bit 11 is 0 for Function 1, then Function 1 will specify
+ * INT B in the PCI Configuration Space Int Pin field. If it is 1, then
+ * Function 1 will specify INT A.
+ */
+#define ADV_EEPROM_INTAB 0x0800 /* EEPROM Bit 11 */
+
+typedef struct adveep_3550_config
+{
+ /* Word Offset, Description */
+
+ ushort cfg_lsw; /* 00 power up initialization */
+ /* bit 13 set - Term Polarity Control */
+ /* bit 14 set - BIOS Enable */
+ /* bit 15 set - Big Endian Mode */
+ ushort cfg_msw; /* 01 unused */
+ ushort disc_enable; /* 02 disconnect enable */
+ ushort wdtr_able; /* 03 Wide DTR able */
+ ushort sdtr_able; /* 04 Synchronous DTR able */
+ ushort start_motor; /* 05 send start up motor */
+ ushort tagqng_able; /* 06 tag queuing able */
+ ushort bios_scan; /* 07 BIOS device control */
+ ushort scam_tolerant; /* 08 no scam */
+
+ uchar adapter_scsi_id; /* 09 Host Adapter ID */
+ uchar bios_boot_delay; /* power up wait */
+
+ uchar scsi_reset_delay; /* 10 reset delay */
+ uchar bios_id_lun; /* first boot device scsi id & lun */
+ /* high nibble is lun */
+ /* low nibble is scsi id */
+
+ uchar termination; /* 11 0 - automatic */
+ /* 1 - low off / high off */
+ /* 2 - low off / high on */
+ /* 3 - low on / high on */
+ /* There is no low on / high off */
+
+ uchar reserved1; /* reserved byte (not used) */
+
+ ushort bios_ctrl; /* 12 BIOS control bits */
+ /* bit 0 BIOS don't act as initiator. */
+ /* bit 1 BIOS > 1 GB support */
+ /* bit 2 BIOS > 2 Disk Support */
+ /* bit 3 BIOS don't support removables */
+ /* bit 4 BIOS support bootable CD */
+ /* bit 5 BIOS scan enabled */
+ /* bit 6 BIOS support multiple LUNs */
+ /* bit 7 BIOS display of message */
+ /* bit 8 SCAM disabled */
+ /* bit 9 Reset SCSI bus during init. */
+ /* bit 10 */
+ /* bit 11 No verbose initialization. */
+ /* bit 12 SCSI parity enabled */
+ /* bit 13 */
+ /* bit 14 */
+ /* bit 15 */
+ ushort ultra_able; /* 13 ULTRA speed able */
+ ushort reserved2; /* 14 reserved */
+ uchar max_host_qng; /* 15 maximum host queuing */
+ uchar max_dvc_qng; /* maximum per device queuing */
+ ushort dvc_cntl; /* 16 control bit for driver */
+ ushort bug_fix; /* 17 control bit for bug fix */
+ ushort serial_number_word1; /* 18 Board serial number word 1 */
+ ushort serial_number_word2; /* 19 Board serial number word 2 */
+ ushort serial_number_word3; /* 20 Board serial number word 3 */
+ ushort check_sum; /* 21 EEP check sum */
+ uchar oem_name[16]; /* 22 OEM name */
+ ushort dvc_err_code; /* 30 last device driver error code */
+ ushort adv_err_code; /* 31 last uc and Adv Lib error code */
+ ushort adv_err_addr; /* 32 last uc error address */
+ ushort saved_dvc_err_code; /* 33 saved last dev. driver error code */
+ ushort saved_adv_err_code; /* 34 saved last uc and Adv Lib error code */
+ ushort saved_adv_err_addr; /* 35 saved last uc error address */
+ ushort num_of_err; /* 36 number of error */
+} ADVEEP_3550_CONFIG;
+
+typedef struct adveep_38C0800_config
+{
+ /* Word Offset, Description */
+
+ ushort cfg_lsw; /* 00 power up initialization */
+ /* bit 13 set - Load CIS */
+ /* bit 14 set - BIOS Enable */
+ /* bit 15 set - Big Endian Mode */
+ ushort cfg_msw; /* 01 unused */
+ ushort disc_enable; /* 02 disconnect enable */
+ ushort wdtr_able; /* 03 Wide DTR able */
+ ushort sdtr_speed1; /* 04 SDTR Speed TID 0-3 */
+ ushort start_motor; /* 05 send start up motor */
+ ushort tagqng_able; /* 06 tag queuing able */
+ ushort bios_scan; /* 07 BIOS device control */
+ ushort scam_tolerant; /* 08 no scam */
+
+ uchar adapter_scsi_id; /* 09 Host Adapter ID */
+ uchar bios_boot_delay; /* power up wait */
+
+ uchar scsi_reset_delay; /* 10 reset delay */
+ uchar bios_id_lun; /* first boot device scsi id & lun */
+ /* high nibble is lun */
+ /* low nibble is scsi id */
+
+ uchar termination_se; /* 11 0 - automatic */
+ /* 1 - low off / high off */
+ /* 2 - low off / high on */
+ /* 3 - low on / high on */
+ /* There is no low on / high off */
+
+ uchar termination_lvd; /* 11 0 - automatic */
+ /* 1 - low off / high off */
+ /* 2 - low off / high on */
+ /* 3 - low on / high on */
+ /* There is no low on / high off */
+
+ ushort bios_ctrl; /* 12 BIOS control bits */
+ /* bit 0 BIOS don't act as initiator. */
+ /* bit 1 BIOS > 1 GB support */
+ /* bit 2 BIOS > 2 Disk Support */
+ /* bit 3 BIOS don't support removables */
+ /* bit 4 BIOS support bootable CD */
+ /* bit 5 BIOS scan enabled */
+ /* bit 6 BIOS support multiple LUNs */
+ /* bit 7 BIOS display of message */
+ /* bit 8 SCAM disabled */
+ /* bit 9 Reset SCSI bus during init. */
+ /* bit 10 */
+ /* bit 11 No verbose initialization. */
+ /* bit 12 SCSI parity enabled */
+ /* bit 13 */
+ /* bit 14 */
+ /* bit 15 */
+ ushort sdtr_speed2; /* 13 SDTR speed TID 4-7 */
+ ushort sdtr_speed3; /* 14 SDTR speed TID 8-11 */
+ uchar max_host_qng; /* 15 maximum host queueing */
+ uchar max_dvc_qng; /* maximum per device queuing */
+ ushort dvc_cntl; /* 16 control bit for driver */
+ ushort sdtr_speed4; /* 17 SDTR speed 4 TID 12-15 */
+ ushort serial_number_word1; /* 18 Board serial number word 1 */
+ ushort serial_number_word2; /* 19 Board serial number word 2 */
+ ushort serial_number_word3; /* 20 Board serial number word 3 */
+ ushort check_sum; /* 21 EEP check sum */
+ uchar oem_name[16]; /* 22 OEM name */
+ ushort dvc_err_code; /* 30 last device driver error code */
+ ushort adv_err_code; /* 31 last uc and Adv Lib error code */
+ ushort adv_err_addr; /* 32 last uc error address */
+ ushort saved_dvc_err_code; /* 33 saved last dev. driver error code */
+ ushort saved_adv_err_code; /* 34 saved last uc and Adv Lib error code */
+ ushort saved_adv_err_addr; /* 35 saved last uc error address */
+ ushort reserved36; /* 36 reserved */
+ ushort reserved37; /* 37 reserved */
+ ushort reserved38; /* 38 reserved */
+ ushort reserved39; /* 39 reserved */
+ ushort reserved40; /* 40 reserved */
+ ushort reserved41; /* 41 reserved */
+ ushort reserved42; /* 42 reserved */
+ ushort reserved43; /* 43 reserved */
+ ushort reserved44; /* 44 reserved */
+ ushort reserved45; /* 45 reserved */
+ ushort reserved46; /* 46 reserved */
+ ushort reserved47; /* 47 reserved */
+ ushort reserved48; /* 48 reserved */
+ ushort reserved49; /* 49 reserved */
+ ushort reserved50; /* 50 reserved */
+ ushort reserved51; /* 51 reserved */
+ ushort reserved52; /* 52 reserved */
+ ushort reserved53; /* 53 reserved */
+ ushort reserved54; /* 54 reserved */
+ ushort reserved55; /* 55 reserved */
+ ushort cisptr_lsw; /* 56 CIS PTR LSW */
+ ushort cisprt_msw; /* 57 CIS PTR MSW */
+ ushort subsysvid; /* 58 SubSystem Vendor ID */
+ ushort subsysid; /* 59 SubSystem ID */
+ ushort reserved60; /* 60 reserved */
+ ushort reserved61; /* 61 reserved */
+ ushort reserved62; /* 62 reserved */
+ ushort reserved63; /* 63 reserved */
+} ADVEEP_38C0800_CONFIG;
+
+typedef struct adveep_38C1600_config
+{
+ /* Word Offset, Description */
+
+ ushort cfg_lsw; /* 00 power up initialization */
+ /* bit 11 set - Func. 0 INTB, Func. 1 INTA */
+ /* clear - Func. 0 INTA, Func. 1 INTB */
+ /* bit 13 set - Load CIS */
+ /* bit 14 set - BIOS Enable */
+ /* bit 15 set - Big Endian Mode */
+ ushort cfg_msw; /* 01 unused */
+ ushort disc_enable; /* 02 disconnect enable */
+ ushort wdtr_able; /* 03 Wide DTR able */
+ ushort sdtr_speed1; /* 04 SDTR Speed TID 0-3 */
+ ushort start_motor; /* 05 send start up motor */
+ ushort tagqng_able; /* 06 tag queuing able */
+ ushort bios_scan; /* 07 BIOS device control */
+ ushort scam_tolerant; /* 08 no scam */
+
+ uchar adapter_scsi_id; /* 09 Host Adapter ID */
+ uchar bios_boot_delay; /* power up wait */
+
+ uchar scsi_reset_delay; /* 10 reset delay */
+ uchar bios_id_lun; /* first boot device scsi id & lun */
+ /* high nibble is lun */
+ /* low nibble is scsi id */
+
+ uchar termination_se; /* 11 0 - automatic */
+ /* 1 - low off / high off */
+ /* 2 - low off / high on */
+ /* 3 - low on / high on */
+ /* There is no low on / high off */
+
+ uchar termination_lvd; /* 11 0 - automatic */
+ /* 1 - low off / high off */
+ /* 2 - low off / high on */
+ /* 3 - low on / high on */
+ /* There is no low on / high off */
+
+ ushort bios_ctrl; /* 12 BIOS control bits */
+ /* bit 0 BIOS don't act as initiator. */
+ /* bit 1 BIOS > 1 GB support */
+ /* bit 2 BIOS > 2 Disk Support */
+ /* bit 3 BIOS don't support removables */
+ /* bit 4 BIOS support bootable CD */
+ /* bit 5 BIOS scan enabled */
+ /* bit 6 BIOS support multiple LUNs */
+ /* bit 7 BIOS display of message */
+ /* bit 8 SCAM disabled */
+ /* bit 9 Reset SCSI bus during init. */
+ /* bit 10 Basic Integrity Checking disabled */
+ /* bit 11 No verbose initialization. */
+ /* bit 12 SCSI parity enabled */
+ /* bit 13 AIPP (Asyn. Info. Ph. Prot.) dis. */
+ /* bit 14 */
+ /* bit 15 */
+ ushort sdtr_speed2; /* 13 SDTR speed TID 4-7 */
+ ushort sdtr_speed3; /* 14 SDTR speed TID 8-11 */
+ uchar max_host_qng; /* 15 maximum host queueing */
+ uchar max_dvc_qng; /* maximum per device queuing */
+ ushort dvc_cntl; /* 16 control bit for driver */
+ ushort sdtr_speed4; /* 17 SDTR speed 4 TID 12-15 */
+ ushort serial_number_word1; /* 18 Board serial number word 1 */
+ ushort serial_number_word2; /* 19 Board serial number word 2 */
+ ushort serial_number_word3; /* 20 Board serial number word 3 */
+ ushort check_sum; /* 21 EEP check sum */
+ uchar oem_name[16]; /* 22 OEM name */
+ ushort dvc_err_code; /* 30 last device driver error code */
+ ushort adv_err_code; /* 31 last uc and Adv Lib error code */
+ ushort adv_err_addr; /* 32 last uc error address */
+ ushort saved_dvc_err_code; /* 33 saved last dev. driver error code */
+ ushort saved_adv_err_code; /* 34 saved last uc and Adv Lib error code */
+ ushort saved_adv_err_addr; /* 35 saved last uc error address */
+ ushort reserved36; /* 36 reserved */
+ ushort reserved37; /* 37 reserved */
+ ushort reserved38; /* 38 reserved */
+ ushort reserved39; /* 39 reserved */
+ ushort reserved40; /* 40 reserved */
+ ushort reserved41; /* 41 reserved */
+ ushort reserved42; /* 42 reserved */
+ ushort reserved43; /* 43 reserved */
+ ushort reserved44; /* 44 reserved */
+ ushort reserved45; /* 45 reserved */
+ ushort reserved46; /* 46 reserved */
+ ushort reserved47; /* 47 reserved */
+ ushort reserved48; /* 48 reserved */
+ ushort reserved49; /* 49 reserved */
+ ushort reserved50; /* 50 reserved */
+ ushort reserved51; /* 51 reserved */
+ ushort reserved52; /* 52 reserved */
+ ushort reserved53; /* 53 reserved */
+ ushort reserved54; /* 54 reserved */
+ ushort reserved55; /* 55 reserved */
+ ushort cisptr_lsw; /* 56 CIS PTR LSW */
+ ushort cisprt_msw; /* 57 CIS PTR MSW */
+ ushort subsysvid; /* 58 SubSystem Vendor ID */
+ ushort subsysid; /* 59 SubSystem ID */
+ ushort reserved60; /* 60 reserved */
+ ushort reserved61; /* 61 reserved */
+ ushort reserved62; /* 62 reserved */
+ ushort reserved63; /* 63 reserved */
+} ADVEEP_38C1600_CONFIG;
+
+/*
+ * EEPROM Commands
+ */
+#define ASC_EEP_CMD_DONE 0x0200
+#define ASC_EEP_CMD_DONE_ERR 0x0001
+
+/* cfg_word */
+#define EEP_CFG_WORD_BIG_ENDIAN 0x8000
+
+/* bios_ctrl */
+#define BIOS_CTRL_BIOS 0x0001
+#define BIOS_CTRL_EXTENDED_XLAT 0x0002
+#define BIOS_CTRL_GT_2_DISK 0x0004
+#define BIOS_CTRL_BIOS_REMOVABLE 0x0008
+#define BIOS_CTRL_BOOTABLE_CD 0x0010
+#define BIOS_CTRL_MULTIPLE_LUN 0x0040
+#define BIOS_CTRL_DISPLAY_MSG 0x0080
+#define BIOS_CTRL_NO_SCAM 0x0100
+#define BIOS_CTRL_RESET_SCSI_BUS 0x0200
+#define BIOS_CTRL_INIT_VERBOSE 0x0800
+#define BIOS_CTRL_SCSI_PARITY 0x1000
+#define BIOS_CTRL_AIPP_DIS 0x2000
+
+#define ADV_3550_MEMSIZE 0x2000 /* 8 KB Internal Memory */
+#define ADV_3550_IOLEN 0x40 /* I/O Port Range in bytes */
+
+#define ADV_38C0800_MEMSIZE 0x4000 /* 16 KB Internal Memory */
+#define ADV_38C0800_IOLEN 0x100 /* I/O Port Range in bytes */
+
+/*
+ * XXX - Since ASC38C1600 Rev.3 has a local RAM failure issue, there is
+ * a special 16K Adv Library and Microcode version. After the issue is
+ * resolved, should restore 32K support.
+ *
+ * #define ADV_38C1600_MEMSIZE 0x8000L * 32 KB Internal Memory *
+ */
+#define ADV_38C1600_MEMSIZE 0x4000 /* 16 KB Internal Memory */
+#define ADV_38C1600_IOLEN 0x100 /* I/O Port Range 256 bytes */
+#define ADV_38C1600_MEMLEN 0x1000 /* Memory Range 4KB bytes */
+
+/*
+ * Byte I/O register address from base of 'iop_base'.
+ */
+#define IOPB_INTR_STATUS_REG 0x00
+#define IOPB_CHIP_ID_1 0x01
+#define IOPB_INTR_ENABLES 0x02
+#define IOPB_CHIP_TYPE_REV 0x03
+#define IOPB_RES_ADDR_4 0x04
+#define IOPB_RES_ADDR_5 0x05
+#define IOPB_RAM_DATA 0x06
+#define IOPB_RES_ADDR_7 0x07
+#define IOPB_FLAG_REG 0x08
+#define IOPB_RES_ADDR_9 0x09
+#define IOPB_RISC_CSR 0x0A
+#define IOPB_RES_ADDR_B 0x0B
+#define IOPB_RES_ADDR_C 0x0C
+#define IOPB_RES_ADDR_D 0x0D
+#define IOPB_SOFT_OVER_WR 0x0E
+#define IOPB_RES_ADDR_F 0x0F
+#define IOPB_MEM_CFG 0x10
+#define IOPB_RES_ADDR_11 0x11
+#define IOPB_GPIO_DATA 0x12
+#define IOPB_RES_ADDR_13 0x13
+#define IOPB_FLASH_PAGE 0x14
+#define IOPB_RES_ADDR_15 0x15
+#define IOPB_GPIO_CNTL 0x16
+#define IOPB_RES_ADDR_17 0x17
+#define IOPB_FLASH_DATA 0x18
+#define IOPB_RES_ADDR_19 0x19
+#define IOPB_RES_ADDR_1A 0x1A
+#define IOPB_RES_ADDR_1B 0x1B
+#define IOPB_RES_ADDR_1C 0x1C
+#define IOPB_RES_ADDR_1D 0x1D
+#define IOPB_RES_ADDR_1E 0x1E
+#define IOPB_RES_ADDR_1F 0x1F
+#define IOPB_DMA_CFG0 0x20
+#define IOPB_DMA_CFG1 0x21
+#define IOPB_TICKLE 0x22
+#define IOPB_DMA_REG_WR 0x23
+#define IOPB_SDMA_STATUS 0x24
+#define IOPB_SCSI_BYTE_CNT 0x25
+#define IOPB_HOST_BYTE_CNT 0x26
+#define IOPB_BYTE_LEFT_TO_XFER 0x27
+#define IOPB_BYTE_TO_XFER_0 0x28
+#define IOPB_BYTE_TO_XFER_1 0x29
+#define IOPB_BYTE_TO_XFER_2 0x2A
+#define IOPB_BYTE_TO_XFER_3 0x2B
+#define IOPB_ACC_GRP 0x2C
+#define IOPB_RES_ADDR_2D 0x2D
+#define IOPB_DEV_ID 0x2E
+#define IOPB_RES_ADDR_2F 0x2F
+#define IOPB_SCSI_DATA 0x30
+#define IOPB_RES_ADDR_31 0x31
+#define IOPB_RES_ADDR_32 0x32
+#define IOPB_SCSI_DATA_HSHK 0x33
+#define IOPB_SCSI_CTRL 0x34
+#define IOPB_RES_ADDR_35 0x35
+#define IOPB_RES_ADDR_36 0x36
+#define IOPB_RES_ADDR_37 0x37
+#define IOPB_RAM_BIST 0x38
+#define IOPB_PLL_TEST 0x39
+#define IOPB_PCI_INT_CFG 0x3A
+#define IOPB_RES_ADDR_3B 0x3B
+#define IOPB_RFIFO_CNT 0x3C
+#define IOPB_RES_ADDR_3D 0x3D
+#define IOPB_RES_ADDR_3E 0x3E
+#define IOPB_RES_ADDR_3F 0x3F
+
+/*
+ * Word I/O register address from base of 'iop_base'.
+ */
+#define IOPW_CHIP_ID_0 0x00 /* CID0 */
+#define IOPW_CTRL_REG 0x02 /* CC */
+#define IOPW_RAM_ADDR 0x04 /* LA */
+#define IOPW_RAM_DATA 0x06 /* LD */
+#define IOPW_RES_ADDR_08 0x08
+#define IOPW_RISC_CSR 0x0A /* CSR */
+#define IOPW_SCSI_CFG0 0x0C /* CFG0 */
+#define IOPW_SCSI_CFG1 0x0E /* CFG1 */
+#define IOPW_RES_ADDR_10 0x10
+#define IOPW_SEL_MASK 0x12 /* SM */
+#define IOPW_RES_ADDR_14 0x14
+#define IOPW_FLASH_ADDR 0x16 /* FA */
+#define IOPW_RES_ADDR_18 0x18
+#define IOPW_EE_CMD 0x1A /* EC */
+#define IOPW_EE_DATA 0x1C /* ED */
+#define IOPW_SFIFO_CNT 0x1E /* SFC */
+#define IOPW_RES_ADDR_20 0x20
+#define IOPW_Q_BASE 0x22 /* QB */
+#define IOPW_QP 0x24 /* QP */
+#define IOPW_IX 0x26 /* IX */
+#define IOPW_SP 0x28 /* SP */
+#define IOPW_PC 0x2A /* PC */
+#define IOPW_RES_ADDR_2C 0x2C
+#define IOPW_RES_ADDR_2E 0x2E
+#define IOPW_SCSI_DATA 0x30 /* SD */
+#define IOPW_SCSI_DATA_HSHK 0x32 /* SDH */
+#define IOPW_SCSI_CTRL 0x34 /* SC */
+#define IOPW_HSHK_CFG 0x36 /* HCFG */
+#define IOPW_SXFR_STATUS 0x36 /* SXS */
+#define IOPW_SXFR_CNTL 0x38 /* SXL */
+#define IOPW_SXFR_CNTH 0x3A /* SXH */
+#define IOPW_RES_ADDR_3C 0x3C
+#define IOPW_RFIFO_DATA 0x3E /* RFD */
+
+/*
+ * Doubleword I/O register address from base of 'iop_base'.
+ */
+#define IOPDW_RES_ADDR_0 0x00
+#define IOPDW_RAM_DATA 0x04
+#define IOPDW_RES_ADDR_8 0x08
+#define IOPDW_RES_ADDR_C 0x0C
+#define IOPDW_RES_ADDR_10 0x10
+#define IOPDW_COMMA 0x14
+#define IOPDW_COMMB 0x18
+#define IOPDW_RES_ADDR_1C 0x1C
+#define IOPDW_SDMA_ADDR0 0x20
+#define IOPDW_SDMA_ADDR1 0x24
+#define IOPDW_SDMA_COUNT 0x28
+#define IOPDW_SDMA_ERROR 0x2C
+#define IOPDW_RDMA_ADDR0 0x30
+#define IOPDW_RDMA_ADDR1 0x34
+#define IOPDW_RDMA_COUNT 0x38
+#define IOPDW_RDMA_ERROR 0x3C
+
+#define ADV_CHIP_ID_BYTE 0x25
+#define ADV_CHIP_ID_WORD 0x04C1
+
+#define ADV_SC_SCSI_BUS_RESET 0x2000
+
+#define ADV_INTR_ENABLE_HOST_INTR 0x01
+#define ADV_INTR_ENABLE_SEL_INTR 0x02
+#define ADV_INTR_ENABLE_DPR_INTR 0x04
+#define ADV_INTR_ENABLE_RTA_INTR 0x08
+#define ADV_INTR_ENABLE_RMA_INTR 0x10
+#define ADV_INTR_ENABLE_RST_INTR 0x20
+#define ADV_INTR_ENABLE_DPE_INTR 0x40
+#define ADV_INTR_ENABLE_GLOBAL_INTR 0x80
+
+#define ADV_INTR_STATUS_INTRA 0x01
+#define ADV_INTR_STATUS_INTRB 0x02
+#define ADV_INTR_STATUS_INTRC 0x04
+
+#define ADV_RISC_CSR_STOP (0x0000)
+#define ADV_RISC_TEST_COND (0x2000)
+#define ADV_RISC_CSR_RUN (0x4000)
+#define ADV_RISC_CSR_SINGLE_STEP (0x8000)
+
+#define ADV_CTRL_REG_HOST_INTR 0x0100
+#define ADV_CTRL_REG_SEL_INTR 0x0200
+#define ADV_CTRL_REG_DPR_INTR 0x0400
+#define ADV_CTRL_REG_RTA_INTR 0x0800
+#define ADV_CTRL_REG_RMA_INTR 0x1000
+#define ADV_CTRL_REG_RES_BIT14 0x2000
+#define ADV_CTRL_REG_DPE_INTR 0x4000
+#define ADV_CTRL_REG_POWER_DONE 0x8000
+#define ADV_CTRL_REG_ANY_INTR 0xFF00
+
+#define ADV_CTRL_REG_CMD_RESET 0x00C6
+#define ADV_CTRL_REG_CMD_WR_IO_REG 0x00C5
+#define ADV_CTRL_REG_CMD_RD_IO_REG 0x00C4
+#define ADV_CTRL_REG_CMD_WR_PCI_CFG_SPACE 0x00C3
+#define ADV_CTRL_REG_CMD_RD_PCI_CFG_SPACE 0x00C2
+
+#define ADV_TICKLE_NOP 0x00
+#define ADV_TICKLE_A 0x01
+#define ADV_TICKLE_B 0x02
+#define ADV_TICKLE_C 0x03
+
+#define ADV_SCSI_CTRL_RSTOUT 0x2000
+
+#define AdvIsIntPending(port) \
+ (AdvReadWordRegister(port, IOPW_CTRL_REG) & ADV_CTRL_REG_HOST_INTR)
+
+/*
+ * SCSI_CFG0 Register bit definitions
+ */
+#define TIMER_MODEAB 0xC000 /* Watchdog, Second, and Select. Timer Ctrl. */
+#define PARITY_EN 0x2000 /* Enable SCSI Parity Error detection */
+#define EVEN_PARITY 0x1000 /* Select Even Parity */
+#define WD_LONG 0x0800 /* Watchdog Interval, 1: 57 min, 0: 13 sec */
+#define QUEUE_128 0x0400 /* Queue Size, 1: 128 byte, 0: 64 byte */
+#define PRIM_MODE 0x0100 /* Primitive SCSI mode */
+#define SCAM_EN 0x0080 /* Enable SCAM selection */
+#define SEL_TMO_LONG 0x0040 /* Sel/Resel Timeout, 1: 400 ms, 0: 1.6 ms */
+#define CFRM_ID 0x0020 /* SCAM id sel. confirm., 1: fast, 0: 6.4 ms */
+#define OUR_ID_EN 0x0010 /* Enable OUR_ID bits */
+#define OUR_ID 0x000F /* SCSI ID */
+
+/*
+ * SCSI_CFG1 Register bit definitions
+ */
+#define BIG_ENDIAN 0x8000 /* Enable Big Endian Mode MIO:15, EEP:15 */
+#define TERM_POL 0x2000 /* Terminator Polarity Ctrl. MIO:13, EEP:13 */
+#define SLEW_RATE 0x1000 /* SCSI output buffer slew rate */
+#define FILTER_SEL 0x0C00 /* Filter Period Selection */
+#define FLTR_DISABLE 0x0000 /* Input Filtering Disabled */
+#define FLTR_11_TO_20NS 0x0800 /* Input Filtering 11ns to 20ns */
+#define FLTR_21_TO_39NS 0x0C00 /* Input Filtering 21ns to 39ns */
+#define ACTIVE_DBL 0x0200 /* Disable Active Negation */
+#define DIFF_MODE 0x0100 /* SCSI differential Mode (Read-Only) */
+#define DIFF_SENSE 0x0080 /* 1: No SE cables, 0: SE cable (Read-Only) */
+#define TERM_CTL_SEL 0x0040 /* Enable TERM_CTL_H and TERM_CTL_L */
+#define TERM_CTL 0x0030 /* External SCSI Termination Bits */
+#define TERM_CTL_H 0x0020 /* Enable External SCSI Upper Termination */
+#define TERM_CTL_L 0x0010 /* Enable External SCSI Lower Termination */
+#define CABLE_DETECT 0x000F /* External SCSI Cable Connection Status */
+
+/*
+ * Addendum for ASC-38C0800 Chip
+ *
+ * The ASC-38C1600 Chip uses the same definitions except that the
+ * bus mode override bits [12:10] have been moved to byte register
+ * offset 0xE (IOPB_SOFT_OVER_WR) bits [12:10]. The [12:10] bits in
+ * SCSI_CFG1 are read-only and always available. Bit 14 (DIS_TERM_DRV)
+ * is not needed. The [12:10] bits in IOPB_SOFT_OVER_WR are write-only.
+ * Also each ASC-38C1600 function or channel uses only cable bits [5:4]
+ * and [1:0]. Bits [14], [7:6], [3:2] are unused.
+ */
+#define DIS_TERM_DRV 0x4000 /* 1: Read c_det[3:0], 0: cannot read */
+#define HVD_LVD_SE 0x1C00 /* Device Detect Bits */
+#define HVD 0x1000 /* HVD Device Detect */
+#define LVD 0x0800 /* LVD Device Detect */
+#define SE 0x0400 /* SE Device Detect */
+#define TERM_LVD 0x00C0 /* LVD Termination Bits */
+#define TERM_LVD_HI 0x0080 /* Enable LVD Upper Termination */
+#define TERM_LVD_LO 0x0040 /* Enable LVD Lower Termination */
+#define TERM_SE 0x0030 /* SE Termination Bits */
+#define TERM_SE_HI 0x0020 /* Enable SE Upper Termination */
+#define TERM_SE_LO 0x0010 /* Enable SE Lower Termination */
+#define C_DET_LVD 0x000C /* LVD Cable Detect Bits */
+#define C_DET3 0x0008 /* Cable Detect for LVD External Wide */
+#define C_DET2 0x0004 /* Cable Detect for LVD Internal Wide */
+#define C_DET_SE 0x0003 /* SE Cable Detect Bits */
+#define C_DET1 0x0002 /* Cable Detect for SE Internal Wide */
+#define C_DET0 0x0001 /* Cable Detect for SE Internal Narrow */
+
+
+#define CABLE_ILLEGAL_A 0x7
+ /* x 0 0 0 | on on | Illegal (all 3 connectors are used) */
+
+#define CABLE_ILLEGAL_B 0xB
+ /* 0 x 0 0 | on on | Illegal (all 3 connectors are used) */
+
+/*
+ * MEM_CFG Register bit definitions
+ */
+#define BIOS_EN 0x40 /* BIOS Enable MIO:14,EEP:14 */
+#define FAST_EE_CLK 0x20 /* Diagnostic Bit */
+#define RAM_SZ 0x1C /* Specify size of RAM to RISC */
+#define RAM_SZ_2KB 0x00 /* 2 KB */
+#define RAM_SZ_4KB 0x04 /* 4 KB */
+#define RAM_SZ_8KB 0x08 /* 8 KB */
+#define RAM_SZ_16KB 0x0C /* 16 KB */
+#define RAM_SZ_32KB 0x10 /* 32 KB */
+#define RAM_SZ_64KB 0x14 /* 64 KB */
+
+/*
+ * DMA_CFG0 Register bit definitions
+ *
+ * This register is only accessible to the host.
+ */
+#define BC_THRESH_ENB 0x80 /* PCI DMA Start Conditions */
+#define FIFO_THRESH 0x70 /* PCI DMA FIFO Threshold */
+#define FIFO_THRESH_16B 0x00 /* 16 bytes */
+#define FIFO_THRESH_32B 0x20 /* 32 bytes */
+#define FIFO_THRESH_48B 0x30 /* 48 bytes */
+#define FIFO_THRESH_64B 0x40 /* 64 bytes */
+#define FIFO_THRESH_80B 0x50 /* 80 bytes (default) */
+#define FIFO_THRESH_96B 0x60 /* 96 bytes */
+#define FIFO_THRESH_112B 0x70 /* 112 bytes */
+#define START_CTL 0x0C /* DMA start conditions */
+#define START_CTL_TH 0x00 /* Wait threshold level (default) */
+#define START_CTL_ID 0x04 /* Wait SDMA/SBUS idle */
+#define START_CTL_THID 0x08 /* Wait threshold and SDMA/SBUS idle */
+#define START_CTL_EMFU 0x0C /* Wait SDMA FIFO empty/full */
+#define READ_CMD 0x03 /* Memory Read Method */
+#define READ_CMD_MR 0x00 /* Memory Read */
+#define READ_CMD_MRL 0x02 /* Memory Read Long */
+#define READ_CMD_MRM 0x03 /* Memory Read Multiple (default) */
+
+/*
+ * ASC-38C0800 RAM BIST Register bit definitions
+ */
+#define RAM_TEST_MODE 0x80
+#define PRE_TEST_MODE 0x40
+#define NORMAL_MODE 0x00
+#define RAM_TEST_DONE 0x10
+#define RAM_TEST_STATUS 0x0F
+#define RAM_TEST_HOST_ERROR 0x08
+#define RAM_TEST_INTRAM_ERROR 0x04
+#define RAM_TEST_RISC_ERROR 0x02
+#define RAM_TEST_SCSI_ERROR 0x01
+#define RAM_TEST_SUCCESS 0x00
+#define PRE_TEST_VALUE 0x05
+#define NORMAL_VALUE 0x00
+
+/*
+ * ASC38C1600 Definitions
+ *
+ * IOPB_PCI_INT_CFG Bit Field Definitions
+ */
+
+#define INTAB_LD 0x80 /* Value loaded from EEPROM Bit 11. */
+
+/*
+ * Bit 1 can be set to change the interrupt for the Function to operate in
+ * Totem Pole mode. By default Bit 1 is 0 and the interrupt operates in
+ * Open Drain mode. Both functions of the ASC38C1600 must be set to the same
+ * mode, otherwise the operating mode is undefined.
+ */
+#define TOTEMPOLE 0x02
+
+/*
+ * Bit 0 can be used to change the Int Pin for the Function. The value is
+ * 0 by default for both Functions with Function 0 using INT A and Function
+ * B using INT B. For Function 0 if set, INT B is used. For Function 1 if set,
+ * INT A is used.
+ *
+ * EEPROM Word 0 Bit 11 for each Function may change the initial Int Pin
+ * value specified in the PCI Configuration Space.
+ */
+#define INTAB 0x01
+
+/* a_advlib.h */
+
+/*
+ * Adv Library Status Definitions
+ */
+#define ADV_TRUE 1
+#define ADV_FALSE 0
+#define ADV_NOERROR 1
+#define ADV_SUCCESS 1
+#define ADV_BUSY 0
+#define ADV_ERROR (-1)
+
+
+/*
+ * ADV_DVC_VAR 'warn_code' values
+ */
+#define ASC_WARN_BUSRESET_ERROR 0x0001 /* SCSI Bus Reset error */
+#define ASC_WARN_EEPROM_CHKSUM 0x0002 /* EEP check sum error */
+#define ASC_WARN_EEPROM_TERMINATION 0x0004 /* EEP termination bad field */
+#define ASC_WARN_SET_PCI_CONFIG_SPACE 0x0080 /* PCI config space set error */
+#define ASC_WARN_ERROR 0xFFFF /* ADV_ERROR return */
+
+#define ADV_MAX_TID 15 /* max. target identifier */
+#define ADV_MAX_LUN 7 /* max. logical unit number */
+
+/*
+ * Error code values are set in ADV_DVC_VAR 'err_code'.
+ */
+#define ASC_IERR_WRITE_EEPROM 0x0001 /* write EEPROM error */
+#define ASC_IERR_MCODE_CHKSUM 0x0002 /* micro code check sum error */
+#define ASC_IERR_NO_CARRIER 0x0004 /* No more carrier memory. */
+#define ASC_IERR_START_STOP_CHIP 0x0008 /* start/stop chip failed */
+#define ASC_IERR_CHIP_VERSION 0x0040 /* wrong chip version */
+#define ASC_IERR_SET_SCSI_ID 0x0080 /* set SCSI ID failed */
+#define ASC_IERR_HVD_DEVICE 0x0100 /* HVD attached to LVD connector. */
+#define ASC_IERR_BAD_SIGNATURE 0x0200 /* signature not found */
+#define ASC_IERR_ILLEGAL_CONNECTION 0x0400 /* Illegal cable connection */
+#define ASC_IERR_SINGLE_END_DEVICE 0x0800 /* Single-end used w/differential */
+#define ASC_IERR_REVERSED_CABLE 0x1000 /* Narrow flat cable reversed */
+#define ASC_IERR_BIST_PRE_TEST 0x2000 /* BIST pre-test error */
+#define ASC_IERR_BIST_RAM_TEST 0x4000 /* BIST RAM test error */
+#define ASC_IERR_BAD_CHIPTYPE 0x8000 /* Invalid 'chip_type' setting. */
+
+/*
+ * Fixed locations of microcode operating variables.
+ */
+#define ASC_MC_CODE_BEGIN_ADDR 0x0028 /* microcode start address */
+#define ASC_MC_CODE_END_ADDR 0x002A /* microcode end address */
+#define ASC_MC_CODE_CHK_SUM 0x002C /* microcode code checksum */
+#define ASC_MC_VERSION_DATE 0x0038 /* microcode version */
+#define ASC_MC_VERSION_NUM 0x003A /* microcode number */
+#define ASC_MC_BIOSMEM 0x0040 /* BIOS RISC Memory Start */
+#define ASC_MC_BIOSLEN 0x0050 /* BIOS RISC Memory Length */
+#define ASC_MC_BIOS_SIGNATURE 0x0058 /* BIOS Signature 0x55AA */
+#define ASC_MC_BIOS_VERSION 0x005A /* BIOS Version (2 bytes) */
+#define ASC_MC_SDTR_SPEED1 0x0090 /* SDTR Speed for TID 0-3 */
+#define ASC_MC_SDTR_SPEED2 0x0092 /* SDTR Speed for TID 4-7 */
+#define ASC_MC_SDTR_SPEED3 0x0094 /* SDTR Speed for TID 8-11 */
+#define ASC_MC_SDTR_SPEED4 0x0096 /* SDTR Speed for TID 12-15 */
+#define ASC_MC_CHIP_TYPE 0x009A
+#define ASC_MC_INTRB_CODE 0x009B
+#define ASC_MC_WDTR_ABLE 0x009C
+#define ASC_MC_SDTR_ABLE 0x009E
+#define ASC_MC_TAGQNG_ABLE 0x00A0
+#define ASC_MC_DISC_ENABLE 0x00A2
+#define ASC_MC_IDLE_CMD_STATUS 0x00A4
+#define ASC_MC_IDLE_CMD 0x00A6
+#define ASC_MC_IDLE_CMD_PARAMETER 0x00A8
+#define ASC_MC_DEFAULT_SCSI_CFG0 0x00AC
+#define ASC_MC_DEFAULT_SCSI_CFG1 0x00AE
+#define ASC_MC_DEFAULT_MEM_CFG 0x00B0
+#define ASC_MC_DEFAULT_SEL_MASK 0x00B2
+#define ASC_MC_SDTR_DONE 0x00B6
+#define ASC_MC_NUMBER_OF_QUEUED_CMD 0x00C0
+#define ASC_MC_NUMBER_OF_MAX_CMD 0x00D0
+#define ASC_MC_DEVICE_HSHK_CFG_TABLE 0x0100
+#define ASC_MC_CONTROL_FLAG 0x0122 /* Microcode control flag. */
+#define ASC_MC_WDTR_DONE 0x0124
+#define ASC_MC_CAM_MODE_MASK 0x015E /* CAM mode TID bitmask. */
+#define ASC_MC_ICQ 0x0160
+#define ASC_MC_IRQ 0x0164
+#define ASC_MC_PPR_ABLE 0x017A
+
+/*
+ * BIOS LRAM variable absolute offsets.
+ */
+#define BIOS_CODESEG 0x54
+#define BIOS_CODELEN 0x56
+#define BIOS_SIGNATURE 0x58
+#define BIOS_VERSION 0x5A
+
+/*
+ * Microcode Control Flags
+ *
+ * Flags set by the Adv Library in RISC variable 'control_flag' (0x122)
+ * and handled by the microcode.
+ */
+#define CONTROL_FLAG_IGNORE_PERR 0x0001 /* Ignore DMA Parity Errors */
+#define CONTROL_FLAG_ENABLE_AIPP 0x0002 /* Enabled AIPP checking. */
+
+/*
+ * ASC_MC_DEVICE_HSHK_CFG_TABLE microcode table or HSHK_CFG register format
+ */
+#define HSHK_CFG_WIDE_XFR 0x8000
+#define HSHK_CFG_RATE 0x0F00
+#define HSHK_CFG_OFFSET 0x001F
+
+#define ASC_DEF_MAX_HOST_QNG 0xFD /* Max. number of host commands (253) */
+#define ASC_DEF_MIN_HOST_QNG 0x10 /* Min. number of host commands (16) */
+#define ASC_DEF_MAX_DVC_QNG 0x3F /* Max. number commands per device (63) */
+#define ASC_DEF_MIN_DVC_QNG 0x04 /* Min. number commands per device (4) */
+
+#define ASC_QC_DATA_CHECK 0x01 /* Require ASC_QC_DATA_OUT set or clear. */
+#define ASC_QC_DATA_OUT 0x02 /* Data out DMA transfer. */
+#define ASC_QC_START_MOTOR 0x04 /* Send auto-start motor before request. */
+#define ASC_QC_NO_OVERRUN 0x08 /* Don't report overrun. */
+#define ASC_QC_FREEZE_TIDQ 0x10 /* Freeze TID queue after request. XXX TBD */
+
+#define ASC_QSC_NO_DISC 0x01 /* Don't allow disconnect for request. */
+#define ASC_QSC_NO_TAGMSG 0x02 /* Don't allow tag queuing for request. */
+#define ASC_QSC_NO_SYNC 0x04 /* Don't use Synch. transfer on request. */
+#define ASC_QSC_NO_WIDE 0x08 /* Don't use Wide transfer on request. */
+#define ASC_QSC_REDO_DTR 0x10 /* Renegotiate WDTR/SDTR before request. */
+/*
+ * Note: If a Tag Message is to be sent and neither ASC_QSC_HEAD_TAG or
+ * ASC_QSC_ORDERED_TAG is set, then a Simple Tag Message (0x20) is used.
+ */
+#define ASC_QSC_HEAD_TAG 0x40 /* Use Head Tag Message (0x21). */
+#define ASC_QSC_ORDERED_TAG 0x80 /* Use Ordered Tag Message (0x22). */
+
+/*
+ * All fields here are accessed by the board microcode and need to be
+ * little-endian.
+ */
+typedef struct adv_carr_t
+{
+ ADV_VADDR carr_va; /* Carrier Virtual Address */
+ ADV_PADDR carr_pa; /* Carrier Physical Address */
+ ADV_VADDR areq_vpa; /* ASC_SCSI_REQ_Q Virtual or Physical Address */
+ /*
+ * next_vpa [31:4] Carrier Virtual or Physical Next Pointer
+ *
+ * next_vpa [3:1] Reserved Bits
+ * next_vpa [0] Done Flag set in Response Queue.
+ */
+ ADV_VADDR next_vpa;
+} ADV_CARR_T;
+
+/*
+ * Mask used to eliminate low 4 bits of carrier 'next_vpa' field.
+ */
+#define ASC_NEXT_VPA_MASK 0xFFFFFFF0
+
+#define ASC_RQ_DONE 0x00000001
+#define ASC_RQ_GOOD 0x00000002
+#define ASC_CQ_STOPPER 0x00000000
+
+#define ASC_GET_CARRP(carrp) ((carrp) & ASC_NEXT_VPA_MASK)
+
+#define ADV_CARRIER_NUM_PAGE_CROSSING \
+ (((ADV_CARRIER_COUNT * sizeof(ADV_CARR_T)) + \
+ (ADV_PAGE_SIZE - 1))/ADV_PAGE_SIZE)
+
+#define ADV_CARRIER_BUFSIZE \
+ ((ADV_CARRIER_COUNT + ADV_CARRIER_NUM_PAGE_CROSSING) * sizeof(ADV_CARR_T))
+
+/*
+ * ASC_SCSI_REQ_Q 'a_flag' definitions
+ *
+ * The Adv Library should limit use to the lower nibble (4 bits) of
+ * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag.
+ */
+#define ADV_POLL_REQUEST 0x01 /* poll for request completion */
+#define ADV_SCSIQ_DONE 0x02 /* request done */
+#define ADV_DONT_RETRY 0x08 /* don't do retry */
+
+#define ADV_CHIP_ASC3550 0x01 /* Ultra-Wide IC */
+#define ADV_CHIP_ASC38C0800 0x02 /* Ultra2-Wide/LVD IC */
+#define ADV_CHIP_ASC38C1600 0x03 /* Ultra3-Wide/LVD2 IC */
+
+/*
+ * Adapter temporary configuration structure
+ *
+ * This structure can be discarded after initialization. Don't add
+ * fields here needed after initialization.
+ *
+ * Field naming convention:
+ *
+ * *_enable indicates the field enables or disables a feature. The
+ * value of the field is never reset.
+ */
+typedef struct adv_dvc_cfg {
+ ushort disc_enable; /* enable disconnection */
+ uchar chip_version; /* chip version */
+ uchar termination; /* Term. Ctrl. bits 6-5 of SCSI_CFG1 register */
+ ushort lib_version; /* Adv Library version number */
+ ushort control_flag; /* Microcode Control Flag */
+ ushort mcode_date; /* Microcode date */
+ ushort mcode_version; /* Microcode version */
+ ushort pci_slot_info; /* high byte device/function number */
+ /* bits 7-3 device num., bits 2-0 function num. */
+ /* low byte bus num. */
+ ushort serial1; /* EEPROM serial number word 1 */
+ ushort serial2; /* EEPROM serial number word 2 */
+ ushort serial3; /* EEPROM serial number word 3 */
+ struct device *dev; /* pointer to the pci dev structure for this board */
+} ADV_DVC_CFG;
+
+struct adv_dvc_var;
+struct adv_scsi_req_q;
+
+typedef void (* ADV_ISR_CALLBACK)
+ (struct adv_dvc_var *, struct adv_scsi_req_q *);
+
+typedef void (* ADV_ASYNC_CALLBACK)
+ (struct adv_dvc_var *, uchar);
+
+/*
+ * Adapter operation variable structure.
+ *
+ * One structure is required per host adapter.
+ *
+ * Field naming convention:
+ *
+ * *_able indicates both whether a feature should be enabled or disabled
+ * and whether a device isi capable of the feature. At initialization
+ * this field may be set, but later if a device is found to be incapable
+ * of the feature, the field is cleared.
+ */
+typedef struct adv_dvc_var {
+ AdvPortAddr iop_base; /* I/O port address */
+ ushort err_code; /* fatal error code */
+ ushort bios_ctrl; /* BIOS control word, EEPROM word 12 */
+ ADV_ISR_CALLBACK isr_callback;
+ ADV_ASYNC_CALLBACK async_callback;
+ ushort wdtr_able; /* try WDTR for a device */
+ ushort sdtr_able; /* try SDTR for a device */
+ ushort ultra_able; /* try SDTR Ultra speed for a device */
+ ushort sdtr_speed1; /* EEPROM SDTR Speed for TID 0-3 */
+ ushort sdtr_speed2; /* EEPROM SDTR Speed for TID 4-7 */
+ ushort sdtr_speed3; /* EEPROM SDTR Speed for TID 8-11 */
+ ushort sdtr_speed4; /* EEPROM SDTR Speed for TID 12-15 */
+ ushort tagqng_able; /* try tagged queuing with a device */
+ ushort ppr_able; /* PPR message capable per TID bitmask. */
+ uchar max_dvc_qng; /* maximum number of tagged commands per device */
+ ushort start_motor; /* start motor command allowed */
+ uchar scsi_reset_wait; /* delay in seconds after scsi bus reset */
+ uchar chip_no; /* should be assigned by caller */
+ uchar max_host_qng; /* maximum number of Q'ed command allowed */
+ uchar irq_no; /* IRQ number */
+ ushort no_scam; /* scam_tolerant of EEPROM */
+ struct asc_board *drv_ptr; /* driver pointer to private structure */
+ uchar chip_scsi_id; /* chip SCSI target ID */
+ uchar chip_type;
+ uchar bist_err_code;
+ ADV_CARR_T *carrier_buf;
+ ADV_CARR_T *carr_freelist; /* Carrier free list. */
+ ADV_CARR_T *icq_sp; /* Initiator command queue stopper pointer. */
+ ADV_CARR_T *irq_sp; /* Initiator response queue stopper pointer. */
+ ushort carr_pending_cnt; /* Count of pending carriers. */
+ /*
+ * Note: The following fields will not be used after initialization. The
+ * driver may discard the buffer after initialization is done.
+ */
+ ADV_DVC_CFG *cfg; /* temporary configuration structure */
+} ADV_DVC_VAR;
+
+#define NO_OF_SG_PER_BLOCK 15
+
+typedef struct asc_sg_block {
+ uchar reserved1;
+ uchar reserved2;
+ uchar reserved3;
+ uchar sg_cnt; /* Valid entries in block. */
+ ADV_PADDR sg_ptr; /* Pointer to next sg block. */
+ struct {
+ ADV_PADDR sg_addr; /* SG element address. */
+ ADV_DCNT sg_count; /* SG element count. */
+ } sg_list[NO_OF_SG_PER_BLOCK];
+} ADV_SG_BLOCK;
+
+/*
+ * ADV_SCSI_REQ_Q - microcode request structure
+ *
+ * All fields in this structure up to byte 60 are used by the microcode.
+ * The microcode makes assumptions about the size and ordering of fields
+ * in this structure. Do not change the structure definition here without
+ * coordinating the change with the microcode.
+ *
+ * All fields accessed by microcode must be maintained in little_endian
+ * order.
+ */
+typedef struct adv_scsi_req_q {
+ uchar cntl; /* Ucode flags and state (ASC_MC_QC_*). */
+ uchar target_cmd;
+ uchar target_id; /* Device target identifier. */
+ uchar target_lun; /* Device target logical unit number. */
+ ADV_PADDR data_addr; /* Data buffer physical address. */
+ ADV_DCNT data_cnt; /* Data count. Ucode sets to residual. */
+ ADV_PADDR sense_addr;
+ ADV_PADDR carr_pa;
+ uchar mflag;
+ uchar sense_len;
+ uchar cdb_len; /* SCSI CDB length. Must <= 16 bytes. */
+ uchar scsi_cntl;
+ uchar done_status; /* Completion status. */
+ uchar scsi_status; /* SCSI status byte. */
+ uchar host_status; /* Ucode host status. */
+ uchar sg_working_ix;
+ uchar cdb[12]; /* SCSI CDB bytes 0-11. */
+ ADV_PADDR sg_real_addr; /* SG list physical address. */
+ ADV_PADDR scsiq_rptr;
+ uchar cdb16[4]; /* SCSI CDB bytes 12-15. */
+ ADV_VADDR scsiq_ptr;
+ ADV_VADDR carr_va;
+ /*
+ * End of microcode structure - 60 bytes. The rest of the structure
+ * is used by the Adv Library and ignored by the microcode.
+ */
+ ADV_VADDR srb_ptr;
+ ADV_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */
+ char *vdata_addr; /* Data buffer virtual address. */
+ uchar a_flag;
+ uchar pad[2]; /* Pad out to a word boundary. */
+} ADV_SCSI_REQ_Q;
+
+/*
+ * Microcode idle loop commands
+ */
+#define IDLE_CMD_COMPLETED 0
+#define IDLE_CMD_STOP_CHIP 0x0001
+#define IDLE_CMD_STOP_CHIP_SEND_INT 0x0002
+#define IDLE_CMD_SEND_INT 0x0004
+#define IDLE_CMD_ABORT 0x0008
+#define IDLE_CMD_DEVICE_RESET 0x0010
+#define IDLE_CMD_SCSI_RESET_START 0x0020 /* Assert SCSI Bus Reset */
+#define IDLE_CMD_SCSI_RESET_END 0x0040 /* Deassert SCSI Bus Reset */
+#define IDLE_CMD_SCSIREQ 0x0080
+
+#define IDLE_CMD_STATUS_SUCCESS 0x0001
+#define IDLE_CMD_STATUS_FAILURE 0x0002
+
+/*
+ * AdvSendIdleCmd() flag definitions.
+ */
+#define ADV_NOWAIT 0x01
+
+/*
+ * Wait loop time out values.
+ */
+#define SCSI_WAIT_10_SEC 10UL /* 10 seconds */
+#define SCSI_WAIT_100_MSEC 100UL /* 100 milliseconds */
+#define SCSI_US_PER_MSEC 1000 /* microseconds per millisecond */
+#define SCSI_MS_PER_SEC 1000UL /* milliseconds per second */
+#define SCSI_MAX_RETRY 10 /* retry count */
+
+#define ADV_ASYNC_RDMA_FAILURE 0x01 /* Fatal RDMA failure. */
+#define ADV_ASYNC_SCSI_BUS_RESET_DET 0x02 /* Detected SCSI Bus Reset. */
+#define ADV_ASYNC_CARRIER_READY_FAILURE 0x03 /* Carrier Ready failure. */
+#define ADV_RDMA_IN_CARR_AND_Q_INVALID 0x04 /* RDMAed-in data invalid. */
+
+
+#define ADV_HOST_SCSI_BUS_RESET 0x80 /* Host Initiated SCSI Bus Reset. */
+
+/*
+ * Device drivers must define the following functions.
+ */
+STATIC inline ulong DvcEnterCritical(void);
+STATIC inline void DvcLeaveCritical(ulong);
+STATIC void DvcSleepMilliSecond(ADV_DCNT);
+STATIC uchar DvcAdvReadPCIConfigByte(ADV_DVC_VAR *, ushort);
+STATIC void DvcAdvWritePCIConfigByte(ADV_DVC_VAR *, ushort, uchar);
+STATIC ADV_PADDR DvcGetPhyAddr(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *,
+ uchar *, ASC_SDCNT *, int);
+STATIC void DvcDelayMicroSecond(ADV_DVC_VAR *, ushort);
+
+/*
+ * Adv Library functions available to drivers.
+ */
+STATIC int AdvExeScsiQueue(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+STATIC int AdvISR(ADV_DVC_VAR *);
+STATIC int AdvInitGetConfig(ADV_DVC_VAR *);
+STATIC int AdvInitAsc3550Driver(ADV_DVC_VAR *);
+STATIC int AdvInitAsc38C0800Driver(ADV_DVC_VAR *);
+STATIC int AdvInitAsc38C1600Driver(ADV_DVC_VAR *);
+STATIC int AdvResetChipAndSB(ADV_DVC_VAR *);
+STATIC int AdvResetSB(ADV_DVC_VAR *asc_dvc);
+
+/*
+ * Internal Adv Library functions.
+ */
+STATIC int AdvSendIdleCmd(ADV_DVC_VAR *, ushort, ADV_DCNT);
+STATIC void AdvInquiryHandling(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+STATIC int AdvInitFrom3550EEP(ADV_DVC_VAR *);
+STATIC int AdvInitFrom38C0800EEP(ADV_DVC_VAR *);
+STATIC int AdvInitFrom38C1600EEP(ADV_DVC_VAR *);
+STATIC ushort AdvGet3550EEPConfig(AdvPortAddr, ADVEEP_3550_CONFIG *);
+STATIC void AdvSet3550EEPConfig(AdvPortAddr, ADVEEP_3550_CONFIG *);
+STATIC ushort AdvGet38C0800EEPConfig(AdvPortAddr, ADVEEP_38C0800_CONFIG *);
+STATIC void AdvSet38C0800EEPConfig(AdvPortAddr, ADVEEP_38C0800_CONFIG *);
+STATIC ushort AdvGet38C1600EEPConfig(AdvPortAddr, ADVEEP_38C1600_CONFIG *);
+STATIC void AdvSet38C1600EEPConfig(AdvPortAddr, ADVEEP_38C1600_CONFIG *);
+STATIC void AdvWaitEEPCmd(AdvPortAddr);
+STATIC ushort AdvReadEEPWord(AdvPortAddr, int);
+
+/*
+ * PCI Bus Definitions
+ */
+#define AscPCICmdRegBits_BusMastering 0x0007
+#define AscPCICmdRegBits_ParErrRespCtrl 0x0040
+
+/* Read byte from a register. */
+#define AdvReadByteRegister(iop_base, reg_off) \
+ (ADV_MEM_READB((iop_base) + (reg_off)))
+
+/* Write byte to a register. */
+#define AdvWriteByteRegister(iop_base, reg_off, byte) \
+ (ADV_MEM_WRITEB((iop_base) + (reg_off), (byte)))
+
+/* Read word (2 bytes) from a register. */
+#define AdvReadWordRegister(iop_base, reg_off) \
+ (ADV_MEM_READW((iop_base) + (reg_off)))
+
+/* Write word (2 bytes) to a register. */
+#define AdvWriteWordRegister(iop_base, reg_off, word) \
+ (ADV_MEM_WRITEW((iop_base) + (reg_off), (word)))
+
+/* Write dword (4 bytes) to a register. */
+#define AdvWriteDWordRegister(iop_base, reg_off, dword) \
+ (ADV_MEM_WRITEDW((iop_base) + (reg_off), (dword)))
+
+/* Read byte from LRAM. */
+#define AdvReadByteLram(iop_base, addr, byte) \
+do { \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)); \
+ (byte) = ADV_MEM_READB((iop_base) + IOPB_RAM_DATA); \
+} while (0)
+
+/* Write byte to LRAM. */
+#define AdvWriteByteLram(iop_base, addr, byte) \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ ADV_MEM_WRITEB((iop_base) + IOPB_RAM_DATA, (byte)))
+
+/* Read word (2 bytes) from LRAM. */
+#define AdvReadWordLram(iop_base, addr, word) \
+do { \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)); \
+ (word) = (ADV_MEM_READW((iop_base) + IOPW_RAM_DATA)); \
+} while (0)
+
+/* Write word (2 bytes) to LRAM. */
+#define AdvWriteWordLram(iop_base, addr, word) \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, (word)))
+
+/* Write little-endian double word (4 bytes) to LRAM */
+/* Because of unspecified C language ordering don't use auto-increment. */
+#define AdvWriteDWordLramNoSwap(iop_base, addr, dword) \
+ ((ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, \
+ cpu_to_le16((ushort) ((dword) & 0xFFFF)))), \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr) + 2), \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, \
+ cpu_to_le16((ushort) ((dword >> 16) & 0xFFFF)))))
+
+/* Read word (2 bytes) from LRAM assuming that the address is already set. */
+#define AdvReadWordAutoIncLram(iop_base) \
+ (ADV_MEM_READW((iop_base) + IOPW_RAM_DATA))
+
+/* Write word (2 bytes) to LRAM assuming that the address is already set. */
+#define AdvWriteWordAutoIncLram(iop_base, word) \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, (word)))
+
+
+/*
+ * Define macro to check for Condor signature.
+ *
+ * Evaluate to ADV_TRUE if a Condor chip is found the specified port
+ * address 'iop_base'. Otherwise evalue to ADV_FALSE.
+ */
+#define AdvFindSignature(iop_base) \
+ (((AdvReadByteRegister((iop_base), IOPB_CHIP_ID_1) == \
+ ADV_CHIP_ID_BYTE) && \
+ (AdvReadWordRegister((iop_base), IOPW_CHIP_ID_0) == \
+ ADV_CHIP_ID_WORD)) ? ADV_TRUE : ADV_FALSE)
+
+/*
+ * Define macro to Return the version number of the chip at 'iop_base'.
+ *
+ * The second parameter 'bus_type' is currently unused.
+ */
+#define AdvGetChipVersion(iop_base, bus_type) \
+ AdvReadByteRegister((iop_base), IOPB_CHIP_TYPE_REV)
+
+/*
+ * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must
+ * match the ASC_SCSI_REQ_Q 'srb_ptr' field.
+ *
+ * If the request has not yet been sent to the device it will simply be
+ * aborted from RISC memory. If the request is disconnected it will be
+ * aborted on reselection by sending an Abort Message to the target ID.
+ *
+ * Return value:
+ * ADV_TRUE(1) - Queue was successfully aborted.
+ * ADV_FALSE(0) - Queue was not found on the active queue list.
+ */
+#define AdvAbortQueue(asc_dvc, scsiq) \
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
+ (ADV_DCNT) (scsiq))
+
+/*
+ * Send a Bus Device Reset Message to the specified target ID.
+ *
+ * All outstanding commands will be purged if sending the
+ * Bus Device Reset Message is successful.
+ *
+ * Return Value:
+ * ADV_TRUE(1) - All requests on the target are purged.
+ * ADV_FALSE(0) - Couldn't issue Bus Device Reset Message; Requests
+ * are not purged.
+ */
+#define AdvResetDevice(asc_dvc, target_id) \
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
+ (ADV_DCNT) (target_id))
+
+/*
+ * SCSI Wide Type definition.
+ */
+#define ADV_SCSI_BIT_ID_TYPE ushort
+
+/*
+ * AdvInitScsiTarget() 'cntl_flag' options.
+ */
+#define ADV_SCAN_LUN 0x01
+#define ADV_CAPINFO_NOLUN 0x02
+
+/*
+ * Convert target id to target id bit mask.
+ */
+#define ADV_TID_TO_TIDMASK(tid) (0x01 << ((tid) & ADV_MAX_TID))
+
+/*
+ * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values.
+ */
+
+#define QD_NO_STATUS 0x00 /* Request not completed yet. */
+#define QD_NO_ERROR 0x01
+#define QD_ABORTED_BY_HOST 0x02
+#define QD_WITH_ERROR 0x04
+
+#define QHSTA_NO_ERROR 0x00
+#define QHSTA_M_SEL_TIMEOUT 0x11
+#define QHSTA_M_DATA_OVER_RUN 0x12
+#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13
+#define QHSTA_M_QUEUE_ABORTED 0x15
+#define QHSTA_M_SXFR_SDMA_ERR 0x16 /* SXFR_STATUS SCSI DMA Error */
+#define QHSTA_M_SXFR_SXFR_PERR 0x17 /* SXFR_STATUS SCSI Bus Parity Error */
+#define QHSTA_M_RDMA_PERR 0x18 /* RISC PCI DMA parity error */
+#define QHSTA_M_SXFR_OFF_UFLW 0x19 /* SXFR_STATUS Offset Underflow */
+#define QHSTA_M_SXFR_OFF_OFLW 0x20 /* SXFR_STATUS Offset Overflow */
+#define QHSTA_M_SXFR_WD_TMO 0x21 /* SXFR_STATUS Watchdog Timeout */
+#define QHSTA_M_SXFR_DESELECTED 0x22 /* SXFR_STATUS Deselected */
+/* Note: QHSTA_M_SXFR_XFR_OFLW is identical to QHSTA_M_DATA_OVER_RUN. */
+#define QHSTA_M_SXFR_XFR_OFLW 0x12 /* SXFR_STATUS Transfer Overflow */
+#define QHSTA_M_SXFR_XFR_PH_ERR 0x24 /* SXFR_STATUS Transfer Phase Error */
+#define QHSTA_M_SXFR_UNKNOWN_ERROR 0x25 /* SXFR_STATUS Unknown Error */
+#define QHSTA_M_SCSI_BUS_RESET 0x30 /* Request aborted from SBR */
+#define QHSTA_M_SCSI_BUS_RESET_UNSOL 0x31 /* Request aborted from unsol. SBR */
+#define QHSTA_M_BUS_DEVICE_RESET 0x32 /* Request aborted from BDR */
+#define QHSTA_M_DIRECTION_ERR 0x35 /* Data Phase mismatch */
+#define QHSTA_M_DIRECTION_ERR_HUNG 0x36 /* Data Phase mismatch and bus hang */
+#define QHSTA_M_WTM_TIMEOUT 0x41
+#define QHSTA_M_BAD_CMPL_STATUS_IN 0x42
+#define QHSTA_M_NO_AUTO_REQ_SENSE 0x43
+#define QHSTA_M_AUTO_REQ_SENSE_FAIL 0x44
+#define QHSTA_M_INVALID_DEVICE 0x45 /* Bad target ID */
+#define QHSTA_M_FROZEN_TIDQ 0x46 /* TID Queue frozen. */
+#define QHSTA_M_SGBACKUP_ERROR 0x47 /* Scatter-Gather backup error */
+
+
+/*
+ * Default EEPROM Configuration structure defined in a_init.c.
+ */
+static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config;
+static ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config;
+static ADVEEP_38C1600_CONFIG Default_38C1600_EEPROM_Config;
+
+/*
+ * DvcGetPhyAddr() flag arguments
+ */
+#define ADV_IS_SCSIQ_FLAG 0x01 /* 'addr' is ASC_SCSI_REQ_Q pointer */
+#define ADV_ASCGETSGLIST_VADDR 0x02 /* 'addr' is AscGetSGList() virtual addr */
+#define ADV_IS_SENSE_FLAG 0x04 /* 'addr' is sense virtual pointer */
+#define ADV_IS_DATA_FLAG 0x08 /* 'addr' is data virtual pointer */
+#define ADV_IS_SGLIST_FLAG 0x10 /* 'addr' is sglist virtual pointer */
+#define ADV_IS_CARRIER_FLAG 0x20 /* 'addr' is ADV_CARR_T pointer */
+
+/* Return the address that is aligned at the next doubleword >= to 'addr'. */
+#define ADV_8BALIGN(addr) (((ulong) (addr) + 0x7) & ~0x7)
+#define ADV_16BALIGN(addr) (((ulong) (addr) + 0xF) & ~0xF)
+#define ADV_32BALIGN(addr) (((ulong) (addr) + 0x1F) & ~0x1F)
+
+/*
+ * Total contiguous memory needed for driver SG blocks.
+ *
+ * ADV_MAX_SG_LIST must be defined by a driver. It is the maximum
+ * number of scatter-gather elements the driver supports in a
+ * single request.
+ */
+
+#define ADV_SG_LIST_MAX_BYTE_SIZE \
+ (sizeof(ADV_SG_BLOCK) * \
+ ((ADV_MAX_SG_LIST + (NO_OF_SG_PER_BLOCK - 1))/NO_OF_SG_PER_BLOCK))
+
+/*
+ * Inquiry data structure and bitfield macros
+ *
+ * Using bitfields to access the subchar data isn't portable across
+ * endianness, so instead mask and shift. Only quantities of more
+ * than 1 bit are shifted, since the others are just tested for true
+ * or false.
+ */
+
+#define ADV_INQ_DVC_TYPE(inq) ((inq)->periph & 0x1f)
+#define ADV_INQ_QUALIFIER(inq) (((inq)->periph & 0xe0) >> 5)
+#define ADV_INQ_DVC_TYPE_MOD(inq) ((inq)->devtype & 0x7f)
+#define ADV_INQ_REMOVABLE(inq) ((inq)->devtype & 0x80)
+#define ADV_INQ_ANSI_VER(inq) ((inq)->ver & 0x07)
+#define ADV_INQ_ECMA_VER(inq) (((inq)->ver & 0x38) >> 3)
+#define ADV_INQ_ISO_VER(inq) (((inq)->ver & 0xc0) >> 6)
+#define ADV_INQ_RESPONSE_FMT(inq) ((inq)->byte3 & 0x0f)
+#define ADV_INQ_TERM_IO(inq) ((inq)->byte3 & 0x40)
+#define ADV_INQ_ASYNC_NOTIF(inq) ((inq)->byte3 & 0x80)
+#define ADV_INQ_SOFT_RESET(inq) ((inq)->flags & 0x01)
+#define ADV_INQ_CMD_QUEUE(inq) ((inq)->flags & 0x02)
+#define ADV_INQ_LINK_CMD(inq) ((inq)->flags & 0x08)
+#define ADV_INQ_SYNC(inq) ((inq)->flags & 0x10)
+#define ADV_INQ_WIDE16(inq) ((inq)->flags & 0x20)
+#define ADV_INQ_WIDE32(inq) ((inq)->flags & 0x40)
+#define ADV_INQ_REL_ADDR(inq) ((inq)->flags & 0x80)
+#define ADV_INQ_INFO_UNIT(inq) ((inq)->info & 0x01)
+#define ADV_INQ_QUICK_ARB(inq) ((inq)->info & 0x02)
+#define ADV_INQ_CLOCKING(inq) (((inq)->info & 0x0c) >> 2)
+
+typedef struct {
+ uchar periph; /* peripheral device type [0:4] */
+ /* peripheral qualifier [5:7] */
+ uchar devtype; /* device type modifier (for SCSI I) [0:6] */
+ /* RMB - removable medium bit [7] */
+ uchar ver; /* ANSI approved version [0:2] */
+ /* ECMA version [3:5] */
+ /* ISO version [6:7] */
+ uchar byte3; /* response data format [0:3] */
+ /* 0 SCSI 1 */
+ /* 1 CCS */
+ /* 2 SCSI-2 */
+ /* 3-F reserved */
+ /* reserved [4:5] */
+ /* terminate I/O process bit (see 5.6.22) [6] */
+ /* asynch. event notification (processor) [7] */
+ uchar add_len; /* additional length */
+ uchar res1; /* reserved */
+ uchar res2; /* reserved */
+ uchar flags; /* soft reset implemented [0] */
+ /* command queuing [1] */
+ /* reserved [2] */
+ /* linked command for this logical unit [3] */
+ /* synchronous data transfer [4] */
+ /* wide bus 16 bit data transfer [5] */
+ /* wide bus 32 bit data transfer [6] */
+ /* relative addressing mode [7] */
+ uchar vendor_id[8]; /* vendor identification */
+ uchar product_id[16]; /* product identification */
+ uchar product_rev_level[4]; /* product revision level */
+ uchar vendor_specific[20]; /* vendor specific */
+ uchar info; /* information unit supported [0] */
+ /* quick arbitrate supported [1] */
+ /* clocking field [2:3] */
+ /* reserved [4:7] */
+ uchar res3; /* reserved */
+} ADV_SCSI_INQUIRY; /* 58 bytes */
+
+
+/*
+ * --- Driver Constants and Macros
+ */
+
+#define ASC_NUM_BOARD_SUPPORTED 16
+#define ASC_NUM_IOPORT_PROBE 4
+#define ASC_NUM_BUS 4
+
+/* Reference Scsi_Host hostdata */
+#define ASC_BOARDP(host) ((asc_board_t *) &((host)->hostdata))
+
+/* asc_board_t flags */
+#define ASC_HOST_IN_RESET 0x01
+#define ASC_IS_WIDE_BOARD 0x04 /* AdvanSys Wide Board */
+#define ASC_SELECT_QUEUE_DEPTHS 0x08
+
+#define ASC_NARROW_BOARD(boardp) (((boardp)->flags & ASC_IS_WIDE_BOARD) == 0)
+#define ASC_WIDE_BOARD(boardp) ((boardp)->flags & ASC_IS_WIDE_BOARD)
+
+#define NO_ISA_DMA 0xff /* No ISA DMA Channel Used */
+
+#define ASC_INFO_SIZE 128 /* advansys_info() line size */
+
+#ifdef CONFIG_PROC_FS
+/* /proc/scsi/advansys/[0...] related definitions */
+#define ASC_PRTBUF_SIZE 2048
+#define ASC_PRTLINE_SIZE 160
+
+#define ASC_PRT_NEXT() \
+ if (cp) { \
+ totlen += len; \
+ leftlen -= len; \
+ if (leftlen == 0) { \
+ return totlen; \
+ } \
+ cp += len; \
+ }
+#endif /* CONFIG_PROC_FS */
+
+/* Asc Library return codes */
+#define ASC_TRUE 1
+#define ASC_FALSE 0
+#define ASC_NOERROR 1
+#define ASC_BUSY 0
+#define ASC_ERROR (-1)
+
+/* struct scsi_cmnd function return codes */
+#define STATUS_BYTE(byte) (byte)
+#define MSG_BYTE(byte) ((byte) << 8)
+#define HOST_BYTE(byte) ((byte) << 16)
+#define DRIVER_BYTE(byte) ((byte) << 24)
+
+/*
+ * The following definitions and macros are OS independent interfaces to
+ * the queue functions:
+ * REQ - SCSI request structure
+ * REQP - pointer to SCSI request structure
+ * REQPTID(reqp) - reqp's target id
+ * REQPNEXT(reqp) - reqp's next pointer
+ * REQPNEXTP(reqp) - pointer to reqp's next pointer
+ * REQPTIME(reqp) - reqp's time stamp value
+ * REQTIMESTAMP() - system time stamp value
+ */
+typedef struct scsi_cmnd REQ, *REQP;
+#define REQPNEXT(reqp) ((REQP) ((reqp)->host_scribble))
+#define REQPNEXTP(reqp) ((REQP *) &((reqp)->host_scribble))
+#define REQPTID(reqp) ((reqp)->device->id)
+#define REQPTIME(reqp) ((reqp)->SCp.this_residual)
+#define REQTIMESTAMP() (jiffies)
+
+#define REQTIMESTAT(function, ascq, reqp, tid) \
+{ \
+ /*
+ * If the request time stamp is less than the system time stamp, then \
+ * maybe the system time stamp wrapped. Set the request time to zero.\
+ */ \
+ if (REQPTIME(reqp) <= REQTIMESTAMP()) { \
+ REQPTIME(reqp) = REQTIMESTAMP() - REQPTIME(reqp); \
+ } else { \
+ /* Indicate an error occurred with the assertion. */ \
+ ASC_ASSERT(REQPTIME(reqp) <= REQTIMESTAMP()); \
+ REQPTIME(reqp) = 0; \
+ } \
+ /* Handle first minimum time case without external initialization. */ \
+ if (((ascq)->q_tot_cnt[tid] == 1) || \
+ (REQPTIME(reqp) < (ascq)->q_min_tim[tid])) { \
+ (ascq)->q_min_tim[tid] = REQPTIME(reqp); \
+ ASC_DBG3(1, "%s: new q_min_tim[%d] %u\n", \
+ (function), (tid), (ascq)->q_min_tim[tid]); \
+ } \
+ if (REQPTIME(reqp) > (ascq)->q_max_tim[tid]) { \
+ (ascq)->q_max_tim[tid] = REQPTIME(reqp); \
+ ASC_DBG3(1, "%s: new q_max_tim[%d] %u\n", \
+ (function), tid, (ascq)->q_max_tim[tid]); \
+ } \
+ (ascq)->q_tot_tim[tid] += REQPTIME(reqp); \
+ /* Reset the time stamp field. */ \
+ REQPTIME(reqp) = 0; \
+}
+
+/* asc_enqueue() flags */
+#define ASC_FRONT 1
+#define ASC_BACK 2
+
+/* asc_dequeue_list() argument */
+#define ASC_TID_ALL (-1)
+
+/* Return non-zero, if the queue is empty. */
+#define ASC_QUEUE_EMPTY(ascq) ((ascq)->q_tidmask == 0)
+
+#define PCI_MAX_SLOT 0x1F
+#define PCI_MAX_BUS 0xFF
+#define PCI_IOADDRESS_MASK 0xFFFE
+#define ASC_PCI_VENDORID 0x10CD
+#define ASC_PCI_DEVICE_ID_CNT 6 /* PCI Device ID count. */
+#define ASC_PCI_DEVICE_ID_1100 0x1100
+#define ASC_PCI_DEVICE_ID_1200 0x1200
+#define ASC_PCI_DEVICE_ID_1300 0x1300
+#define ASC_PCI_DEVICE_ID_2300 0x2300 /* ASC-3550 */
+#define ASC_PCI_DEVICE_ID_2500 0x2500 /* ASC-38C0800 */
+#define ASC_PCI_DEVICE_ID_2700 0x2700 /* ASC-38C1600 */
+
+#ifndef ADVANSYS_STATS
+#define ASC_STATS(shp, counter)
+#define ASC_STATS_ADD(shp, counter, count)
+#else /* ADVANSYS_STATS */
+#define ASC_STATS(shp, counter) \
+ (ASC_BOARDP(shp)->asc_stats.counter++)
+
+#define ASC_STATS_ADD(shp, counter, count) \
+ (ASC_BOARDP(shp)->asc_stats.counter += (count))
+#endif /* ADVANSYS_STATS */
+
+#define ASC_CEILING(val, unit) (((val) + ((unit) - 1))/(unit))
+
+/* If the result wraps when calculating tenths, return 0. */
+#define ASC_TENTHS(num, den) \
+ (((10 * ((num)/(den))) > (((num) * 10)/(den))) ? \
+ 0 : ((((num) * 10)/(den)) - (10 * ((num)/(den)))))
+
+/*
+ * Display a message to the console.
+ */
+#define ASC_PRINT(s) \
+ { \
+ printk("advansys: "); \
+ printk(s); \
+ }
+
+#define ASC_PRINT1(s, a1) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1)); \
+ }
+
+#define ASC_PRINT2(s, a1, a2) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1), (a2)); \
+ }
+
+#define ASC_PRINT3(s, a1, a2, a3) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1), (a2), (a3)); \
+ }
+
+#define ASC_PRINT4(s, a1, a2, a3, a4) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1), (a2), (a3), (a4)); \
+ }
+
+
+#ifndef ADVANSYS_DEBUG
+
+#define ASC_DBG(lvl, s)
+#define ASC_DBG1(lvl, s, a1)
+#define ASC_DBG2(lvl, s, a1, a2)
+#define ASC_DBG3(lvl, s, a1, a2, a3)
+#define ASC_DBG4(lvl, s, a1, a2, a3, a4)
+#define ASC_DBG_PRT_SCSI_HOST(lvl, s)
+#define ASC_DBG_PRT_SCSI_CMND(lvl, s)
+#define ASC_DBG_PRT_ASC_SCSI_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_ADV_SCSI_REQ_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_ASC_QDONE_INFO(lvl, qdone)
+#define ADV_DBG_PRT_ADV_SCSI_REQ_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_HEX(lvl, name, start, length)
+#define ASC_DBG_PRT_CDB(lvl, cdb, len)
+#define ASC_DBG_PRT_SENSE(lvl, sense, len)
+#define ASC_DBG_PRT_INQUIRY(lvl, inq, len)
+
+#else /* ADVANSYS_DEBUG */
+
+/*
+ * Debugging Message Levels:
+ * 0: Errors Only
+ * 1: High-Level Tracing
+ * 2-N: Verbose Tracing
+ */
+
+#define ASC_DBG(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk(s); \
+ } \
+ }
+
+#define ASC_DBG1(lvl, s, a1) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1)); \
+ } \
+ }
+
+#define ASC_DBG2(lvl, s, a1, a2) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2)); \
+ } \
+ }
+
+#define ASC_DBG3(lvl, s, a1, a2, a3) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2), (a3)); \
+ } \
+ }
+
+#define ASC_DBG4(lvl, s, a1, a2, a3, a4) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2), (a3), (a4)); \
+ } \
+ }
+
+#define ASC_DBG_PRT_SCSI_HOST(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_scsi_host(s); \
+ } \
+ }
+
+#define ASC_DBG_PRT_SCSI_CMND(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_scsi_cmnd(s); \
+ } \
+ }
+
+#define ASC_DBG_PRT_ASC_SCSI_Q(lvl, scsiqp) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_asc_scsi_q(scsiqp); \
+ } \
+ }
+
+#define ASC_DBG_PRT_ASC_QDONE_INFO(lvl, qdone) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_asc_qdone_info(qdone); \
+ } \
+ }
+
+#define ASC_DBG_PRT_ADV_SCSI_REQ_Q(lvl, scsiqp) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_adv_scsi_req_q(scsiqp); \
+ } \
+ }
+
+#define ASC_DBG_PRT_HEX(lvl, name, start, length) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_hex((name), (start), (length)); \
+ } \
+ }
+
+#define ASC_DBG_PRT_CDB(lvl, cdb, len) \
+ ASC_DBG_PRT_HEX((lvl), "CDB", (uchar *) (cdb), (len));
+
+#define ASC_DBG_PRT_SENSE(lvl, sense, len) \
+ ASC_DBG_PRT_HEX((lvl), "SENSE", (uchar *) (sense), (len));
+
+#define ASC_DBG_PRT_INQUIRY(lvl, inq, len) \
+ ASC_DBG_PRT_HEX((lvl), "INQUIRY", (uchar *) (inq), (len));
+#endif /* ADVANSYS_DEBUG */
+
+#ifndef ADVANSYS_ASSERT
+#define ASC_ASSERT(a)
+#else /* ADVANSYS_ASSERT */
+
+#define ASC_ASSERT(a) \
+ { \
+ if (!(a)) { \
+ printk("ASC_ASSERT() Failure: file %s, line %d\n", \
+ __FILE__, __LINE__); \
+ } \
+ }
+
+#endif /* ADVANSYS_ASSERT */
+
+
+/*
+ * --- Driver Structures
+ */
+
+#ifdef ADVANSYS_STATS
+
+/* Per board statistics structure */
+struct asc_stats {
+ /* Driver Entrypoint Statistics */
+ ADV_DCNT queuecommand; /* # calls to advansys_queuecommand() */
+ ADV_DCNT reset; /* # calls to advansys_eh_bus_reset() */
+ ADV_DCNT biosparam; /* # calls to advansys_biosparam() */
+ ADV_DCNT interrupt; /* # advansys_interrupt() calls */
+ ADV_DCNT callback; /* # calls to asc/adv_isr_callback() */
+ ADV_DCNT done; /* # calls to request's scsi_done function */
+ ADV_DCNT build_error; /* # asc/adv_build_req() ASC_ERROR returns. */
+ ADV_DCNT adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */
+ ADV_DCNT adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */
+ /* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */
+ ADV_DCNT exe_noerror; /* # ASC_NOERROR returns. */
+ ADV_DCNT exe_busy; /* # ASC_BUSY returns. */
+ ADV_DCNT exe_error; /* # ASC_ERROR returns. */
+ ADV_DCNT exe_unknown; /* # unknown returns. */
+ /* Data Transfer Statistics */
+ ADV_DCNT cont_cnt; /* # non-scatter-gather I/O requests received */
+ ADV_DCNT cont_xfer; /* # contiguous transfer 512-bytes */
+ ADV_DCNT sg_cnt; /* # scatter-gather I/O requests received */
+ ADV_DCNT sg_elem; /* # scatter-gather elements */
+ ADV_DCNT sg_xfer; /* # scatter-gather transfer 512-bytes */
+};
+#endif /* ADVANSYS_STATS */
+
+/*
+ * Request queuing structure
+ */
+typedef struct asc_queue {
+ ADV_SCSI_BIT_ID_TYPE q_tidmask; /* queue mask */
+ REQP q_first[ADV_MAX_TID+1]; /* first queued request */
+ REQP q_last[ADV_MAX_TID+1]; /* last queued request */
+#ifdef ADVANSYS_STATS
+ short q_cur_cnt[ADV_MAX_TID+1]; /* current queue count */
+ short q_max_cnt[ADV_MAX_TID+1]; /* maximum queue count */
+ ADV_DCNT q_tot_cnt[ADV_MAX_TID+1]; /* total enqueue count */
+ ADV_DCNT q_tot_tim[ADV_MAX_TID+1]; /* total time queued */
+ ushort q_max_tim[ADV_MAX_TID+1]; /* maximum time queued */
+ ushort q_min_tim[ADV_MAX_TID+1]; /* minimum time queued */
+#endif /* ADVANSYS_STATS */
+} asc_queue_t;
+
+/*
+ * Adv Library Request Structures
+ *
+ * The following two structures are used to process Wide Board requests.
+ *
+ * The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library
+ * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the
+ * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the
+ * Mid-Level SCSI request structure.
+ *
+ * Zero or more ADV_SG_BLOCK are used with each ADV_SCSI_REQ_Q. Each
+ * ADV_SG_BLOCK structure holds 15 scatter-gather elements. Under Linux
+ * up to 255 scatter-gather elements may be used per request or
+ * ADV_SCSI_REQ_Q.
+ *
+ * Both structures must be 32 byte aligned.
+ */
+typedef struct adv_sgblk {
+ ADV_SG_BLOCK sg_block; /* Sgblock structure. */
+ uchar align[32]; /* Sgblock structure padding. */
+ struct adv_sgblk *next_sgblkp; /* Next scatter-gather structure. */
+} adv_sgblk_t;
+
+typedef struct adv_req {
+ ADV_SCSI_REQ_Q scsi_req_q; /* Adv Library request structure. */
+ uchar align[32]; /* Request structure padding. */
+ struct scsi_cmnd *cmndp; /* Mid-Level SCSI command pointer. */
+ adv_sgblk_t *sgblkp; /* Adv Library scatter-gather pointer. */
+ struct adv_req *next_reqp; /* Next Request Structure. */
+} adv_req_t;
+
+/*
+ * Structure allocated for each board.
+ *
+ * This structure is allocated by scsi_register() at the end
+ * of the 'Scsi_Host' structure starting at the 'hostdata'
+ * field. It is guaranteed to be allocated from DMA-able memory.
+ */
+typedef struct asc_board {
+ int id; /* Board Id */
+ uint flags; /* Board flags */
+ union {
+ ASC_DVC_VAR asc_dvc_var; /* Narrow board */
+ ADV_DVC_VAR adv_dvc_var; /* Wide board */
+ } dvc_var;
+ union {
+ ASC_DVC_CFG asc_dvc_cfg; /* Narrow board */
+ ADV_DVC_CFG adv_dvc_cfg; /* Wide board */
+ } dvc_cfg;
+ ushort asc_n_io_port; /* Number I/O ports. */
+ asc_queue_t active; /* Active command queue */
+ asc_queue_t waiting; /* Waiting command queue */
+ asc_queue_t done; /* Done command queue */
+ ADV_SCSI_BIT_ID_TYPE init_tidmask; /* Target init./valid mask */
+ struct scsi_device *device[ADV_MAX_TID+1]; /* Mid-Level Scsi Device */
+ ushort reqcnt[ADV_MAX_TID+1]; /* Starvation request count */
+ ADV_SCSI_BIT_ID_TYPE queue_full; /* Queue full mask */
+ ushort queue_full_cnt[ADV_MAX_TID+1]; /* Queue full count */
+ union {
+ ASCEEP_CONFIG asc_eep; /* Narrow EEPROM config. */
+ ADVEEP_3550_CONFIG adv_3550_eep; /* 3550 EEPROM config. */
+ ADVEEP_38C0800_CONFIG adv_38C0800_eep; /* 38C0800 EEPROM config. */
+ ADVEEP_38C1600_CONFIG adv_38C1600_eep; /* 38C1600 EEPROM config. */
+ } eep_config;
+ ulong last_reset; /* Saved last reset time */
+ spinlock_t lock; /* Board spinlock */
+#ifdef CONFIG_PROC_FS
+ /* /proc/scsi/advansys/[0...] */
+ char *prtbuf; /* /proc print buffer */
+#endif /* CONFIG_PROC_FS */
+#ifdef ADVANSYS_STATS
+ struct asc_stats asc_stats; /* Board statistics */
+#endif /* ADVANSYS_STATS */
+ /*
+ * The following fields are used only for Narrow Boards.
+ */
+ /* The following three structures must be in DMA-able memory. */
+ ASC_SCSI_REQ_Q scsireqq;
+ ASC_CAP_INFO cap_info;
+ ASC_SCSI_INQUIRY inquiry;
+ uchar sdtr_data[ASC_MAX_TID+1]; /* SDTR information */
+ /*
+ * The following fields are used only for Wide Boards.
+ */
+ void *ioremap_addr; /* I/O Memory remap address. */
+ ushort ioport; /* I/O Port address. */
+ ADV_CARR_T *orig_carrp; /* ADV_CARR_T memory block. */
+ adv_req_t *orig_reqp; /* adv_req_t memory block. */
+ adv_req_t *adv_reqp; /* Request structures. */
+ adv_sgblk_t *adv_sgblkp; /* Scatter-gather structures. */
+ ushort bios_signature; /* BIOS Signature. */
+ ushort bios_version; /* BIOS Version. */
+ ushort bios_codeseg; /* BIOS Code Segment. */
+ ushort bios_codelen; /* BIOS Code Segment Length. */
+} asc_board_t;
+
+/*
+ * PCI configuration structures
+ */
+typedef struct _PCI_DATA_
+{
+ uchar type;
+ uchar bus;
+ uchar slot;
+ uchar func;
+ uchar offset;
+} PCI_DATA;
+
+typedef struct _PCI_DEVICE_
+{
+ ushort vendorID;
+ ushort deviceID;
+ ushort slotNumber;
+ ushort slotFound;
+ uchar busNumber;
+ uchar maxBusNumber;
+ uchar devFunc;
+ ushort startSlot;
+ ushort endSlot;
+ uchar bridge;
+ uchar type;
+} PCI_DEVICE;
+
+typedef struct _PCI_CONFIG_SPACE_
+{
+ ushort vendorID;
+ ushort deviceID;
+ ushort command;
+ ushort status;
+ uchar revision;
+ uchar classCode[3];
+ uchar cacheSize;
+ uchar latencyTimer;
+ uchar headerType;
+ uchar bist;
+ ADV_PADDR baseAddress[6];
+ ushort reserved[4];
+ ADV_PADDR optionRomAddr;
+ ushort reserved2[4];
+ uchar irqLine;
+ uchar irqPin;
+ uchar minGnt;
+ uchar maxLatency;
+} PCI_CONFIG_SPACE;
+
+
+/*
+ * --- Driver Data
+ */
+
+/* Note: All driver global data should be initialized. */
+
+/* Number of boards detected in system. */
+STATIC int asc_board_count = 0;
+STATIC struct Scsi_Host *asc_host[ASC_NUM_BOARD_SUPPORTED] = { 0 };
+
+/* Overrun buffer used by all narrow boards. */
+STATIC uchar overrun_buf[ASC_OVERRUN_BSIZE] = { 0 };
+
+/*
+ * Global structures required to issue a command.
+ */
+STATIC ASC_SCSI_Q asc_scsi_q = { { 0 } };
+STATIC ASC_SG_HEAD asc_sg_head = { 0 };
+
+/* List of supported bus types. */
+STATIC ushort asc_bus[ASC_NUM_BUS] __initdata = {
+ ASC_IS_ISA,
+ ASC_IS_VL,
+ ASC_IS_EISA,
+ ASC_IS_PCI,
+};
+
+/*
+ * Used with the LILO 'advansys' option to eliminate or
+ * limit I/O port probing at boot time, cf. advansys_setup().
+ */
+STATIC int asc_iopflag = ASC_FALSE;
+STATIC int asc_ioport[ASC_NUM_IOPORT_PROBE] = { 0, 0, 0, 0 };
+
+#ifdef ADVANSYS_DEBUG
+STATIC char *
+asc_bus_name[ASC_NUM_BUS] = {
+ "ASC_IS_ISA",
+ "ASC_IS_VL",
+ "ASC_IS_EISA",
+ "ASC_IS_PCI",
+};
+
+STATIC int asc_dbglvl = 3;
+#endif /* ADVANSYS_DEBUG */
+
+/* Declaration for Asc Library internal data referenced by driver. */
+STATIC PortAddr _asc_def_iop_base[];
+
+
+/*
+ * --- Driver Function Prototypes
+ *
+ * advansys.h contains function prototypes for functions global to Linux.
+ */
+
+STATIC irqreturn_t advansys_interrupt(int, void *, struct pt_regs *);
+STATIC int advansys_slave_configure(struct scsi_device *);
+STATIC void asc_scsi_done_list(struct scsi_cmnd *);
+STATIC int asc_execute_scsi_cmnd(struct scsi_cmnd *);
+STATIC int asc_build_req(asc_board_t *, struct scsi_cmnd *);
+STATIC int adv_build_req(asc_board_t *, struct scsi_cmnd *, ADV_SCSI_REQ_Q **);
+STATIC int adv_get_sglist(asc_board_t *, adv_req_t *, struct scsi_cmnd *, int);
+STATIC void asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *);
+STATIC void adv_isr_callback(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+STATIC void adv_async_callback(ADV_DVC_VAR *, uchar);
+STATIC void asc_enqueue(asc_queue_t *, REQP, int);
+STATIC REQP asc_dequeue(asc_queue_t *, int);
+STATIC REQP asc_dequeue_list(asc_queue_t *, REQP *, int);
+STATIC int asc_rmqueue(asc_queue_t *, REQP);
+STATIC void asc_execute_queue(asc_queue_t *);
+#ifdef CONFIG_PROC_FS
+STATIC int asc_proc_copy(off_t, off_t, char *, int , char *, int);
+STATIC int asc_prt_board_devices(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_adv_bios(struct Scsi_Host *, char *, int);
+STATIC int asc_get_eeprom_string(ushort *serialnum, uchar *cp);
+STATIC int asc_prt_asc_board_eeprom(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_adv_board_eeprom(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_driver_conf(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_asc_board_info(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_adv_board_info(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_line(char *, int, char *fmt, ...);
+#endif /* CONFIG_PROC_FS */
+
+/* Declaration for Asc Library internal functions referenced by driver. */
+STATIC int AscFindSignature(PortAddr);
+STATIC ushort AscGetEEPConfig(PortAddr, ASCEEP_CONFIG *, ushort);
+
+/* Statistics function prototypes. */
+#ifdef ADVANSYS_STATS
+#ifdef CONFIG_PROC_FS
+STATIC int asc_prt_board_stats(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_target_stats(struct Scsi_Host *, int, char *, int);
+#endif /* CONFIG_PROC_FS */
+#endif /* ADVANSYS_STATS */
+
+/* Debug function prototypes. */
+#ifdef ADVANSYS_DEBUG
+STATIC void asc_prt_scsi_host(struct Scsi_Host *);
+STATIC void asc_prt_scsi_cmnd(struct scsi_cmnd *);
+STATIC void asc_prt_asc_dvc_cfg(ASC_DVC_CFG *);
+STATIC void asc_prt_asc_dvc_var(ASC_DVC_VAR *);
+STATIC void asc_prt_asc_scsi_q(ASC_SCSI_Q *);
+STATIC void asc_prt_asc_qdone_info(ASC_QDONE_INFO *);
+STATIC void asc_prt_adv_dvc_cfg(ADV_DVC_CFG *);
+STATIC void asc_prt_adv_dvc_var(ADV_DVC_VAR *);
+STATIC void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *);
+STATIC void asc_prt_adv_sgblock(int, ADV_SG_BLOCK *);
+STATIC void asc_prt_hex(char *f, uchar *, int);
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Linux 'Scsi_Host_Template' and advansys_setup() Functions
+ */
+
+#ifdef CONFIG_PROC_FS
+/*
+ * advansys_proc_info() - /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset into a /proc/scsi/advansys/[0...] file
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written to a
+ * /proc/scsi/advansys/[0...] file.
+ *
+ * Note: This function uses the per board buffer 'prtbuf' which is
+ * allocated when the board is initialized in advansys_detect(). The
+ * buffer is ASC_PRTBUF_SIZE bytes. The function asc_proc_copy() is
+ * used to write to the buffer. The way asc_proc_copy() is written
+ * if 'prtbuf' is too small it will not be overwritten. Instead the
+ * user just won't get all the available statistics.
+ */
+int
+advansys_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
+ off_t offset, int length, int inout)
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ int i;
+ char *cp;
+ int cplen;
+ int cnt;
+ int totcnt;
+ int leftlen;
+ char *curbuf;
+ off_t advoffset;
+#ifdef ADVANSYS_STATS
+ int tgt_id;
+#endif /* ADVANSYS_STATS */
+
+ ASC_DBG(1, "advansys_proc_info: begin\n");
+
+ /*
+ * User write not supported.
+ */
+ if (inout == TRUE) {
+ return(-ENOSYS);
+ }
+
+ /*
+ * User read of /proc/scsi/advansys/[0...] file.
+ */
+
+ /* Find the specified board. */
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i]->host_no == shost->host_no) {
+ break;
+ }
+ }
+ if (i == asc_board_count) {
+ return(-ENOENT);
+ }
+
+ shp = asc_host[i];
+ boardp = ASC_BOARDP(shp);
+
+ /* Copy read data starting at the beginning of the buffer. */
+ *start = buffer;
+ curbuf = buffer;
+ advoffset = 0;
+ totcnt = 0;
+ leftlen = length;
+
+ /*
+ * Get board configuration information.
+ *
+ * advansys_info() returns the board string from its own static buffer.
+ */
+ cp = (char *) advansys_info(shp);
+ strcat(cp, "\n");
+ cplen = strlen(cp);
+ /* Copy board information. */
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display Wide Board BIOS Information.
+ */
+ if (ASC_WIDE_BOARD(boardp)) {
+ cp = boardp->prtbuf;
+ cplen = asc_prt_adv_bios(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+ }
+
+ /*
+ * Display driver information for each device attached to the board.
+ */
+ cp = boardp->prtbuf;
+ cplen = asc_prt_board_devices(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display EEPROM configuration for the board.
+ */
+ cp = boardp->prtbuf;
+ if (ASC_NARROW_BOARD(boardp)) {
+ cplen = asc_prt_asc_board_eeprom(shp, cp, ASC_PRTBUF_SIZE);
+ } else {
+ cplen = asc_prt_adv_board_eeprom(shp, cp, ASC_PRTBUF_SIZE);
+ }
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display driver configuration and information for the board.
+ */
+ cp = boardp->prtbuf;
+ cplen = asc_prt_driver_conf(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+#ifdef ADVANSYS_STATS
+ /*
+ * Display driver statistics for the board.
+ */
+ cp = boardp->prtbuf;
+ cplen = asc_prt_board_stats(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen <= ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display driver statistics for each target.
+ */
+ for (tgt_id = 0; tgt_id <= ADV_MAX_TID; tgt_id++) {
+ cp = boardp->prtbuf;
+ cplen = asc_prt_target_stats(shp, tgt_id, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen <= ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+ }
+#endif /* ADVANSYS_STATS */
+
+ /*
+ * Display Asc Library dynamic configuration information
+ * for the board.
+ */
+ cp = boardp->prtbuf;
+ if (ASC_NARROW_BOARD(boardp)) {
+ cplen = asc_prt_asc_board_info(shp, cp, ASC_PRTBUF_SIZE);
+ } else {
+ cplen = asc_prt_adv_board_info(shp, cp, ASC_PRTBUF_SIZE);
+ }
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+
+ return totcnt;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * advansys_detect()
+ *
+ * Detect function for AdvanSys adapters.
+ *
+ * Argument is a pointer to the host driver's scsi_hosts entry.
+ *
+ * Return number of adapters found.
+ *
+ * Note: Because this function is called during system initialization
+ * it must not call SCSI mid-level functions including scsi_malloc()
+ * and scsi_free().
+ */
+int __init
+advansys_detect(struct scsi_host_template *tpnt)
+{
+ static int detect_called = ASC_FALSE;
+ int iop;
+ int bus;
+ struct Scsi_Host *shp = NULL;
+ asc_board_t *boardp = NULL;
+ ASC_DVC_VAR *asc_dvc_varp = NULL;
+ ADV_DVC_VAR *adv_dvc_varp = NULL;
+ adv_sgblk_t *sgp = NULL;
+ int ioport = 0;
+ int share_irq = FALSE;
+ int iolen = 0;
+ struct device *dev = NULL;
+#ifdef CONFIG_PCI
+ int pci_init_search = 0;
+ struct pci_dev *pci_devicep[ASC_NUM_BOARD_SUPPORTED];
+ int pci_card_cnt_max = 0;
+ int pci_card_cnt = 0;
+ struct pci_dev *pci_devp = NULL;
+ int pci_device_id_cnt = 0;
+ unsigned int pci_device_id[ASC_PCI_DEVICE_ID_CNT] = {
+ ASC_PCI_DEVICE_ID_1100,
+ ASC_PCI_DEVICE_ID_1200,
+ ASC_PCI_DEVICE_ID_1300,
+ ASC_PCI_DEVICE_ID_2300,
+ ASC_PCI_DEVICE_ID_2500,
+ ASC_PCI_DEVICE_ID_2700
+ };
+ ADV_PADDR pci_memory_address;
+#endif /* CONFIG_PCI */
+ int warn_code, err_code;
+ int ret;
+
+ if (detect_called == ASC_FALSE) {
+ detect_called = ASC_TRUE;
+ } else {
+ printk("AdvanSys SCSI: advansys_detect() multiple calls ignored\n");
+ return 0;
+ }
+
+ ASC_DBG(1, "advansys_detect: begin\n");
+
+ asc_board_count = 0;
+
+ /*
+ * If I/O port probing has been modified, then verify and
+ * clean-up the 'asc_ioport' list.
+ */
+ if (asc_iopflag == ASC_TRUE) {
+ for (ioport = 0; ioport < ASC_NUM_IOPORT_PROBE; ioport++) {
+ ASC_DBG2(1, "advansys_detect: asc_ioport[%d] 0x%x\n",
+ ioport, asc_ioport[ioport]);
+ if (asc_ioport[ioport] != 0) {
+ for (iop = 0; iop < ASC_IOADR_TABLE_MAX_IX; iop++) {
+ if (_asc_def_iop_base[iop] == asc_ioport[ioport]) {
+ break;
+ }
+ }
+ if (iop == ASC_IOADR_TABLE_MAX_IX) {
+ printk(
+"AdvanSys SCSI: specified I/O Port 0x%X is invalid\n",
+ asc_ioport[ioport]);
+ asc_ioport[ioport] = 0;
+ }
+ }
+ }
+ ioport = 0;
+ }
+
+ for (bus = 0; bus < ASC_NUM_BUS; bus++) {
+
+ ASC_DBG2(1, "advansys_detect: bus search type %d (%s)\n",
+ bus, asc_bus_name[bus]);
+ iop = 0;
+
+ while (asc_board_count < ASC_NUM_BOARD_SUPPORTED) {
+
+ ASC_DBG1(2, "advansys_detect: asc_board_count %d\n",
+ asc_board_count);
+
+ switch (asc_bus[bus]) {
+ case ASC_IS_ISA:
+ case ASC_IS_VL:
+#ifdef CONFIG_ISA
+ if (asc_iopflag == ASC_FALSE) {
+ iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
+ } else {
+ /*
+ * ISA and VL I/O port scanning has either been
+ * eliminated or limited to selected ports on
+ * the LILO command line, /etc/lilo.conf, or
+ * by setting variables when the module was loaded.
+ */
+ ASC_DBG(1, "advansys_detect: I/O port scanning modified\n");
+ ioport_try_again:
+ iop = 0;
+ for (; ioport < ASC_NUM_IOPORT_PROBE; ioport++) {
+ if ((iop = asc_ioport[ioport]) != 0) {
+ break;
+ }
+ }
+ if (iop) {
+ ASC_DBG1(1,
+ "advansys_detect: probing I/O port 0x%x...\n",
+ iop);
+ if (check_region(iop, ASC_IOADR_GAP) != 0) {
+ printk(
+"AdvanSys SCSI: specified I/O Port 0x%X is busy\n", iop);
+ /* Don't try this I/O port twice. */
+ asc_ioport[ioport] = 0;
+ goto ioport_try_again;
+ } else if (AscFindSignature(iop) == ASC_FALSE) {
+ printk(
+"AdvanSys SCSI: specified I/O Port 0x%X has no adapter\n", iop);
+ /* Don't try this I/O port twice. */
+ asc_ioport[ioport] = 0;
+ goto ioport_try_again;
+ } else {
+ /*
+ * If this isn't an ISA board, then it must be
+ * a VL board. If currently looking an ISA
+ * board is being looked for then try for
+ * another ISA board in 'asc_ioport'.
+ */
+ if (asc_bus[bus] == ASC_IS_ISA &&
+ (AscGetChipVersion(iop, ASC_IS_ISA) &
+ ASC_CHIP_VER_ISA_BIT) == 0) {
+ /*
+ * Don't clear 'asc_ioport[ioport]'. Try
+ * this board again for VL. Increment
+ * 'ioport' past this board.
+ */
+ ioport++;
+ goto ioport_try_again;
+ }
+ }
+ /*
+ * This board appears good, don't try the I/O port
+ * again by clearing its value. Increment 'ioport'
+ * for the next iteration.
+ */
+ asc_ioport[ioport++] = 0;
+ }
+ }
+#endif /* CONFIG_ISA */
+ break;
+
+ case ASC_IS_EISA:
+#ifdef CONFIG_ISA
+ iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
+#endif /* CONFIG_ISA */
+ break;
+
+ case ASC_IS_PCI:
+#ifdef CONFIG_PCI
+ if (pci_init_search == 0) {
+ int i, j;
+
+ pci_init_search = 1;
+
+ /* Find all PCI cards. */
+ while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) {
+ if ((pci_devp = pci_find_device(ASC_PCI_VENDORID,
+ pci_device_id[pci_device_id_cnt], pci_devp)) ==
+ NULL) {
+ pci_device_id_cnt++;
+ } else {
+ if (pci_enable_device(pci_devp) == 0) {
+ pci_devicep[pci_card_cnt_max++] = pci_devp;
+ }
+ }
+ }
+
+ /*
+ * Sort PCI cards in ascending order by PCI Bus, Slot,
+ * and Device Number.
+ */
+ for (i = 0; i < pci_card_cnt_max - 1; i++)
+ {
+ for (j = i + 1; j < pci_card_cnt_max; j++) {
+ if ((pci_devicep[j]->bus->number <
+ pci_devicep[i]->bus->number) ||
+ ((pci_devicep[j]->bus->number ==
+ pci_devicep[i]->bus->number) &&
+ (pci_devicep[j]->devfn <
+ pci_devicep[i]->devfn))) {
+ pci_devp = pci_devicep[i];
+ pci_devicep[i] = pci_devicep[j];
+ pci_devicep[j] = pci_devp;
+ }
+ }
+ }
+
+ pci_card_cnt = 0;
+ } else {
+ pci_card_cnt++;
+ }
+
+ if (pci_card_cnt == pci_card_cnt_max) {
+ iop = 0;
+ } else {
+ pci_devp = pci_devicep[pci_card_cnt];
+
+ ASC_DBG2(2,
+ "advansys_detect: devfn %d, bus number %d\n",
+ pci_devp->devfn, pci_devp->bus->number);
+ iop = pci_resource_start(pci_devp, 0);
+ ASC_DBG2(1,
+ "advansys_detect: vendorID %X, deviceID %X\n",
+ pci_devp->vendor, pci_devp->device);
+ ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+ iop, pci_devp->irq);
+ }
+ if(pci_devp)
+ dev = &pci_devp->dev;
+
+#endif /* CONFIG_PCI */
+ break;
+
+ default:
+ ASC_PRINT1("advansys_detect: unknown bus type: %d\n",
+ asc_bus[bus]);
+ break;
+ }
+ ASC_DBG1(1, "advansys_detect: iop 0x%x\n", iop);
+
+ /*
+ * Adapter not found, try next bus type.
+ */
+ if (iop == 0) {
+ break;
+ }
+
+ /*
+ * Adapter found.
+ *
+ * Register the adapter, get its configuration, and
+ * initialize it.
+ */
+ ASC_DBG(2, "advansys_detect: scsi_register()\n");
+ shp = scsi_register(tpnt, sizeof(asc_board_t));
+
+ if (shp == NULL) {
+ continue;
+ }
+
+ scsi_set_device(shp, dev);
+
+ /* Save a pointer to the Scsi_Host of each board found. */
+ asc_host[asc_board_count++] = shp;
+
+ /* Initialize private per board data */
+ boardp = ASC_BOARDP(shp);
+ memset(boardp, 0, sizeof(asc_board_t));
+ boardp->id = asc_board_count - 1;
+
+ /* Initialize spinlock. */
+ spin_lock_init(&boardp->lock);
+
+ /*
+ * Handle both narrow and wide boards.
+ *
+ * If a Wide board was detected, set the board structure
+ * wide board flag. Set-up the board structure based on
+ * the board type.
+ */
+#ifdef CONFIG_PCI
+ if (asc_bus[bus] == ASC_IS_PCI &&
+ (pci_devp->device == ASC_PCI_DEVICE_ID_2300 ||
+ pci_devp->device == ASC_PCI_DEVICE_ID_2500 ||
+ pci_devp->device == ASC_PCI_DEVICE_ID_2700))
+ {
+ boardp->flags |= ASC_IS_WIDE_BOARD;
+ }
+#endif /* CONFIG_PCI */
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ ASC_DBG(1, "advansys_detect: narrow board\n");
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ asc_dvc_varp->bus_type = asc_bus[bus];
+ asc_dvc_varp->drv_ptr = boardp;
+ asc_dvc_varp->cfg = &boardp->dvc_cfg.asc_dvc_cfg;
+ asc_dvc_varp->cfg->overrun_buf = &overrun_buf[0];
+ asc_dvc_varp->iop_base = iop;
+ asc_dvc_varp->isr_callback = asc_isr_callback;
+ } else {
+ ASC_DBG(1, "advansys_detect: wide board\n");
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ adv_dvc_varp->drv_ptr = boardp;
+ adv_dvc_varp->cfg = &boardp->dvc_cfg.adv_dvc_cfg;
+ adv_dvc_varp->isr_callback = adv_isr_callback;
+ adv_dvc_varp->async_callback = adv_async_callback;
+#ifdef CONFIG_PCI
+ if (pci_devp->device == ASC_PCI_DEVICE_ID_2300)
+ {
+ ASC_DBG(1, "advansys_detect: ASC-3550\n");
+ adv_dvc_varp->chip_type = ADV_CHIP_ASC3550;
+ } else if (pci_devp->device == ASC_PCI_DEVICE_ID_2500)
+ {
+ ASC_DBG(1, "advansys_detect: ASC-38C0800\n");
+ adv_dvc_varp->chip_type = ADV_CHIP_ASC38C0800;
+ } else
+ {
+ ASC_DBG(1, "advansys_detect: ASC-38C1600\n");
+ adv_dvc_varp->chip_type = ADV_CHIP_ASC38C1600;
+ }
+#endif /* CONFIG_PCI */
+
+ /*
+ * Map the board's registers into virtual memory for
+ * PCI slave access. Only memory accesses are used to
+ * access the board's registers.
+ *
+ * Note: The PCI register base address is not always
+ * page aligned, but the address passed to ioremap()
+ * must be page aligned. It is guaranteed that the
+ * PCI register base address will not cross a page
+ * boundary.
+ */
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ iolen = ADV_3550_IOLEN;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ iolen = ADV_38C0800_IOLEN;
+ } else
+ {
+ iolen = ADV_38C1600_IOLEN;
+ }
+#ifdef CONFIG_PCI
+ pci_memory_address = pci_resource_start(pci_devp, 1);
+ ASC_DBG1(1, "advansys_detect: pci_memory_address: 0x%lx\n",
+ (ulong) pci_memory_address);
+ if ((boardp->ioremap_addr =
+ ioremap(pci_memory_address & PAGE_MASK,
+ PAGE_SIZE)) == 0) {
+ ASC_PRINT3(
+"advansys_detect: board %d: ioremap(%x, %d) returned NULL\n",
+ boardp->id, pci_memory_address, iolen);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ ASC_DBG1(1, "advansys_detect: ioremap_addr: 0x%lx\n",
+ (ulong) boardp->ioremap_addr);
+ adv_dvc_varp->iop_base = (AdvPortAddr)
+ (boardp->ioremap_addr +
+ (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+ ASC_DBG1(1, "advansys_detect: iop_base: 0x%lx\n",
+ adv_dvc_varp->iop_base);
+#endif /* CONFIG_PCI */
+
+ /*
+ * Even though it isn't used to access wide boards, other
+ * than for the debug line below, save I/O Port address so
+ * that it can be reported.
+ */
+ boardp->ioport = iop;
+
+ ASC_DBG2(1,
+"advansys_detect: iopb_chip_id_1 0x%x, iopw_chip_id_0 0x%x\n",
+ (ushort) inp(iop + 1), (ushort) inpw(iop));
+ }
+
+#ifdef CONFIG_PROC_FS
+ /*
+ * Allocate buffer for printing information from
+ * /proc/scsi/advansys/[0...].
+ */
+ if ((boardp->prtbuf =
+ kmalloc(ASC_PRTBUF_SIZE, GFP_ATOMIC)) == NULL) {
+ ASC_PRINT3(
+"advansys_detect: board %d: kmalloc(%d, %d) returned NULL\n",
+ boardp->id, ASC_PRTBUF_SIZE, GFP_ATOMIC);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+#endif /* CONFIG_PROC_FS */
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ asc_dvc_varp->cfg->dev = dev;
+ /*
+ * Set the board bus type and PCI IRQ before
+ * calling AscInitGetConfig().
+ */
+ switch (asc_dvc_varp->bus_type) {
+#ifdef CONFIG_ISA
+ case ASC_IS_ISA:
+ shp->unchecked_isa_dma = TRUE;
+ share_irq = FALSE;
+ break;
+ case ASC_IS_VL:
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = FALSE;
+ break;
+ case ASC_IS_EISA:
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+ break;
+#endif /* CONFIG_ISA */
+#ifdef CONFIG_PCI
+ case ASC_IS_PCI:
+ shp->irq = asc_dvc_varp->irq_no = pci_devp->irq;
+ asc_dvc_varp->cfg->pci_slot_info =
+ ASC_PCI_MKID(pci_devp->bus->number,
+ PCI_SLOT(pci_devp->devfn),
+ PCI_FUNC(pci_devp->devfn));
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+ break;
+#endif /* CONFIG_PCI */
+ default:
+ ASC_PRINT2(
+"advansys_detect: board %d: unknown adapter type: %d\n",
+ boardp->id, asc_dvc_varp->bus_type);
+ shp->unchecked_isa_dma = TRUE;
+ share_irq = FALSE;
+ break;
+ }
+ } else {
+ adv_dvc_varp->cfg->dev = dev;
+ /*
+ * For Wide boards set PCI information before calling
+ * AdvInitGetConfig().
+ */
+#ifdef CONFIG_PCI
+ shp->irq = adv_dvc_varp->irq_no = pci_devp->irq;
+ adv_dvc_varp->cfg->pci_slot_info =
+ ASC_PCI_MKID(pci_devp->bus->number,
+ PCI_SLOT(pci_devp->devfn),
+ PCI_FUNC(pci_devp->devfn));
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+#endif /* CONFIG_PCI */
+ }
+
+ /*
+ * Read the board configuration.
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * NOTE: AscInitGetConfig() may change the board's
+ * bus_type value. The asc_bus[bus] value should no
+ * longer be used. If the bus_type field must be
+ * referenced only use the bit-wise AND operator "&".
+ */
+ ASC_DBG(2, "advansys_detect: AscInitGetConfig()\n");
+ switch(ret = AscInitGetConfig(asc_dvc_varp)) {
+ case 0: /* No error */
+ break;
+ case ASC_WARN_IO_PORT_ROTATE:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: I/O port address modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_AUTO_CONFIG:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: I/O port increment switch enabled\n",
+ boardp->id);
+ break;
+ case ASC_WARN_EEPROM_CHKSUM:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: EEPROM checksum error\n",
+ boardp->id);
+ break;
+ case ASC_WARN_IRQ_MODIFIED:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: IRQ modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_CMD_QNG_CONFLICT:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: tag queuing enabled w/o disconnects\n",
+ boardp->id);
+ break;
+ default:
+ ASC_PRINT2(
+"AscInitGetConfig: board %d: unknown warning: 0x%x\n",
+ boardp->id, ret);
+ break;
+ }
+ if ((err_code = asc_dvc_varp->err_code) != 0) {
+ ASC_PRINT3(
+"AscInitGetConfig: board %d error: init_state 0x%x, err_code 0x%x\n",
+ boardp->id, asc_dvc_varp->init_state,
+ asc_dvc_varp->err_code);
+ }
+ } else {
+ ASC_DBG(2, "advansys_detect: AdvInitGetConfig()\n");
+ if ((ret = AdvInitGetConfig(adv_dvc_varp)) != 0) {
+ ASC_PRINT2("AdvInitGetConfig: board %d: warning: 0x%x\n",
+ boardp->id, ret);
+ }
+ if ((err_code = adv_dvc_varp->err_code) != 0) {
+ ASC_PRINT2(
+"AdvInitGetConfig: board %d error: err_code 0x%x\n",
+ boardp->id, adv_dvc_varp->err_code);
+ }
+ }
+
+ if (err_code != 0) {
+#ifdef CONFIG_PROC_FS
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Save the EEPROM configuration so that it can be displayed
+ * from /proc/scsi/advansys/[0...].
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+
+ ASCEEP_CONFIG *ep;
+
+ /*
+ * Set the adapter's target id bit in the 'init_tidmask' field.
+ */
+ boardp->init_tidmask |=
+ ADV_TID_TO_TIDMASK(asc_dvc_varp->cfg->chip_scsi_id);
+
+ /*
+ * Save EEPROM settings for the board.
+ */
+ ep = &boardp->eep_config.asc_eep;
+
+ ep->init_sdtr = asc_dvc_varp->cfg->sdtr_enable;
+ ep->disc_enable = asc_dvc_varp->cfg->disc_enable;
+ ep->use_cmd_qng = asc_dvc_varp->cfg->cmd_qng_enabled;
+ ASC_EEP_SET_DMA_SPD(ep, asc_dvc_varp->cfg->isa_dma_speed);
+ ep->start_motor = asc_dvc_varp->start_motor;
+ ep->cntl = asc_dvc_varp->dvc_cntl;
+ ep->no_scam = asc_dvc_varp->no_scam;
+ ep->max_total_qng = asc_dvc_varp->max_total_qng;
+ ASC_EEP_SET_CHIP_ID(ep, asc_dvc_varp->cfg->chip_scsi_id);
+ /* 'max_tag_qng' is set to the same value for every device. */
+ ep->max_tag_qng = asc_dvc_varp->cfg->max_tag_qng[0];
+ ep->adapter_info[0] = asc_dvc_varp->cfg->adapter_info[0];
+ ep->adapter_info[1] = asc_dvc_varp->cfg->adapter_info[1];
+ ep->adapter_info[2] = asc_dvc_varp->cfg->adapter_info[2];
+ ep->adapter_info[3] = asc_dvc_varp->cfg->adapter_info[3];
+ ep->adapter_info[4] = asc_dvc_varp->cfg->adapter_info[4];
+ ep->adapter_info[5] = asc_dvc_varp->cfg->adapter_info[5];
+
+ /*
+ * Modify board configuration.
+ */
+ ASC_DBG(2, "advansys_detect: AscInitSetConfig()\n");
+ switch (ret = AscInitSetConfig(asc_dvc_varp)) {
+ case 0: /* No error. */
+ break;
+ case ASC_WARN_IO_PORT_ROTATE:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: I/O port address modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_AUTO_CONFIG:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: I/O port increment switch enabled\n",
+ boardp->id);
+ break;
+ case ASC_WARN_EEPROM_CHKSUM:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: EEPROM checksum error\n",
+ boardp->id);
+ break;
+ case ASC_WARN_IRQ_MODIFIED:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: IRQ modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_CMD_QNG_CONFLICT:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: tag queuing w/o disconnects\n",
+ boardp->id);
+ break;
+ default:
+ ASC_PRINT2(
+"AscInitSetConfig: board %d: unknown warning: 0x%x\n",
+ boardp->id, ret);
+ break;
+ }
+ if (asc_dvc_varp->err_code != 0) {
+ ASC_PRINT3(
+"AscInitSetConfig: board %d error: init_state 0x%x, err_code 0x%x\n",
+ boardp->id, asc_dvc_varp->init_state,
+ asc_dvc_varp->err_code);
+#ifdef CONFIG_PROC_FS
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Finish initializing the 'Scsi_Host' structure.
+ */
+ /* AscInitSetConfig() will set the IRQ for non-PCI boards. */
+ if ((asc_dvc_varp->bus_type & ASC_IS_PCI) == 0) {
+ shp->irq = asc_dvc_varp->irq_no;
+ }
+ } else {
+ ADVEEP_3550_CONFIG *ep_3550;
+ ADVEEP_38C0800_CONFIG *ep_38C0800;
+ ADVEEP_38C1600_CONFIG *ep_38C1600;
+
+ /*
+ * Save Wide EEP Configuration Information.
+ */
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ ep_3550 = &boardp->eep_config.adv_3550_eep;
+
+ ep_3550->adapter_scsi_id = adv_dvc_varp->chip_scsi_id;
+ ep_3550->max_host_qng = adv_dvc_varp->max_host_qng;
+ ep_3550->max_dvc_qng = adv_dvc_varp->max_dvc_qng;
+ ep_3550->termination = adv_dvc_varp->cfg->termination;
+ ep_3550->disc_enable = adv_dvc_varp->cfg->disc_enable;
+ ep_3550->bios_ctrl = adv_dvc_varp->bios_ctrl;
+ ep_3550->wdtr_able = adv_dvc_varp->wdtr_able;
+ ep_3550->sdtr_able = adv_dvc_varp->sdtr_able;
+ ep_3550->ultra_able = adv_dvc_varp->ultra_able;
+ ep_3550->tagqng_able = adv_dvc_varp->tagqng_able;
+ ep_3550->start_motor = adv_dvc_varp->start_motor;
+ ep_3550->scsi_reset_delay = adv_dvc_varp->scsi_reset_wait;
+ ep_3550->serial_number_word1 =
+ adv_dvc_varp->cfg->serial1;
+ ep_3550->serial_number_word2 =
+ adv_dvc_varp->cfg->serial2;
+ ep_3550->serial_number_word3 =
+ adv_dvc_varp->cfg->serial3;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ ep_38C0800 = &boardp->eep_config.adv_38C0800_eep;
+
+ ep_38C0800->adapter_scsi_id = adv_dvc_varp->chip_scsi_id;
+ ep_38C0800->max_host_qng = adv_dvc_varp->max_host_qng;
+ ep_38C0800->max_dvc_qng = adv_dvc_varp->max_dvc_qng;
+ ep_38C0800->termination_lvd =
+ adv_dvc_varp->cfg->termination;
+ ep_38C0800->disc_enable = adv_dvc_varp->cfg->disc_enable;
+ ep_38C0800->bios_ctrl = adv_dvc_varp->bios_ctrl;
+ ep_38C0800->wdtr_able = adv_dvc_varp->wdtr_able;
+ ep_38C0800->tagqng_able = adv_dvc_varp->tagqng_able;
+ ep_38C0800->sdtr_speed1 = adv_dvc_varp->sdtr_speed1;
+ ep_38C0800->sdtr_speed2 = adv_dvc_varp->sdtr_speed2;
+ ep_38C0800->sdtr_speed3 = adv_dvc_varp->sdtr_speed3;
+ ep_38C0800->sdtr_speed4 = adv_dvc_varp->sdtr_speed4;
+ ep_38C0800->tagqng_able = adv_dvc_varp->tagqng_able;
+ ep_38C0800->start_motor = adv_dvc_varp->start_motor;
+ ep_38C0800->scsi_reset_delay =
+ adv_dvc_varp->scsi_reset_wait;
+ ep_38C0800->serial_number_word1 =
+ adv_dvc_varp->cfg->serial1;
+ ep_38C0800->serial_number_word2 =
+ adv_dvc_varp->cfg->serial2;
+ ep_38C0800->serial_number_word3 =
+ adv_dvc_varp->cfg->serial3;
+ } else
+ {
+ ep_38C1600 = &boardp->eep_config.adv_38C1600_eep;
+
+ ep_38C1600->adapter_scsi_id = adv_dvc_varp->chip_scsi_id;
+ ep_38C1600->max_host_qng = adv_dvc_varp->max_host_qng;
+ ep_38C1600->max_dvc_qng = adv_dvc_varp->max_dvc_qng;
+ ep_38C1600->termination_lvd =
+ adv_dvc_varp->cfg->termination;
+ ep_38C1600->disc_enable = adv_dvc_varp->cfg->disc_enable;
+ ep_38C1600->bios_ctrl = adv_dvc_varp->bios_ctrl;
+ ep_38C1600->wdtr_able = adv_dvc_varp->wdtr_able;
+ ep_38C1600->tagqng_able = adv_dvc_varp->tagqng_able;
+ ep_38C1600->sdtr_speed1 = adv_dvc_varp->sdtr_speed1;
+ ep_38C1600->sdtr_speed2 = adv_dvc_varp->sdtr_speed2;
+ ep_38C1600->sdtr_speed3 = adv_dvc_varp->sdtr_speed3;
+ ep_38C1600->sdtr_speed4 = adv_dvc_varp->sdtr_speed4;
+ ep_38C1600->tagqng_able = adv_dvc_varp->tagqng_able;
+ ep_38C1600->start_motor = adv_dvc_varp->start_motor;
+ ep_38C1600->scsi_reset_delay =
+ adv_dvc_varp->scsi_reset_wait;
+ ep_38C1600->serial_number_word1 =
+ adv_dvc_varp->cfg->serial1;
+ ep_38C1600->serial_number_word2 =
+ adv_dvc_varp->cfg->serial2;
+ ep_38C1600->serial_number_word3 =
+ adv_dvc_varp->cfg->serial3;
+ }
+
+ /*
+ * Set the adapter's target id bit in the 'init_tidmask' field.
+ */
+ boardp->init_tidmask |=
+ ADV_TID_TO_TIDMASK(adv_dvc_varp->chip_scsi_id);
+
+ /*
+ * Finish initializing the 'Scsi_Host' structure.
+ */
+ shp->irq = adv_dvc_varp->irq_no;
+ }
+
+ /*
+ * Channels are numbered beginning with 0. For AdvanSys one host
+ * structure supports one channel. Multi-channel boards have a
+ * separate host structure for each channel.
+ */
+ shp->max_channel = 0;
+ if (ASC_NARROW_BOARD(boardp)) {
+ shp->max_id = ASC_MAX_TID + 1;
+ shp->max_lun = ASC_MAX_LUN + 1;
+
+ shp->io_port = asc_dvc_varp->iop_base;
+ boardp->asc_n_io_port = ASC_IOADR_GAP;
+ shp->this_id = asc_dvc_varp->cfg->chip_scsi_id;
+
+ /* Set maximum number of queues the adapter can handle. */
+ shp->can_queue = asc_dvc_varp->max_total_qng;
+ } else {
+ shp->max_id = ADV_MAX_TID + 1;
+ shp->max_lun = ADV_MAX_LUN + 1;
+
+ /*
+ * Save the I/O Port address and length even though
+ * I/O ports are not used to access Wide boards.
+ * Instead the Wide boards are accessed with
+ * PCI Memory Mapped I/O.
+ */
+ shp->io_port = iop;
+ boardp->asc_n_io_port = iolen;
+
+ shp->this_id = adv_dvc_varp->chip_scsi_id;
+
+ /* Set maximum number of queues the adapter can handle. */
+ shp->can_queue = adv_dvc_varp->max_host_qng;
+ }
+
+ /*
+ * 'n_io_port' currently is one byte.
+ *
+ * Set a value to 'n_io_port', but never referenced it because
+ * it may be truncated.
+ */
+ shp->n_io_port = boardp->asc_n_io_port <= 255 ?
+ boardp->asc_n_io_port : 255;
+
+ /*
+ * Following v1.3.89, 'cmd_per_lun' is no longer needed
+ * and should be set to zero.
+ *
+ * But because of a bug introduced in v1.3.89 if the driver is
+ * compiled as a module and 'cmd_per_lun' is zero, the Mid-Level
+ * SCSI function 'allocate_device' will panic. To allow the driver
+ * to work as a module in these kernels set 'cmd_per_lun' to 1.
+ *
+ * Note: This is wrong. cmd_per_lun should be set to the depth
+ * you want on untagged devices always.
+#ifdef MODULE
+ */
+ shp->cmd_per_lun = 1;
+/* #else
+ shp->cmd_per_lun = 0;
+#endif */
+
+ /*
+ * Set the maximum number of scatter-gather elements the
+ * adapter can handle.
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Allow two commands with 'sg_tablesize' scatter-gather
+ * elements to be executed simultaneously. This value is
+ * the theoretical hardware limit. It may be decreased
+ * below.
+ */
+ shp->sg_tablesize =
+ (((asc_dvc_varp->max_total_qng - 2) / 2) *
+ ASC_SG_LIST_PER_Q) + 1;
+ } else {
+ shp->sg_tablesize = ADV_MAX_SG_LIST;
+ }
+
+ /*
+ * The value of 'sg_tablesize' can not exceed the SCSI
+ * mid-level driver definition of SG_ALL. SG_ALL also
+ * must not be exceeded, because it is used to define the
+ * size of the scatter-gather table in 'struct asc_sg_head'.
+ */
+ if (shp->sg_tablesize > SG_ALL) {
+ shp->sg_tablesize = SG_ALL;
+ }
+
+ ASC_DBG1(1, "advansys_detect: sg_tablesize: %d\n",
+ shp->sg_tablesize);
+
+ /* BIOS start address. */
+ if (ASC_NARROW_BOARD(boardp)) {
+ shp->base =
+ ((ulong) AscGetChipBiosAddress(
+ asc_dvc_varp->iop_base,
+ asc_dvc_varp->bus_type));
+ } else {
+ /*
+ * Fill-in BIOS board variables. The Wide BIOS saves
+ * information in LRAM that is used by the driver.
+ */
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_SIGNATURE,
+ boardp->bios_signature);
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_VERSION,
+ boardp->bios_version);
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_CODESEG,
+ boardp->bios_codeseg);
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_CODELEN,
+ boardp->bios_codelen);
+
+ ASC_DBG2(1,
+ "advansys_detect: bios_signature 0x%x, bios_version 0x%x\n",
+ boardp->bios_signature, boardp->bios_version);
+
+ ASC_DBG2(1,
+ "advansys_detect: bios_codeseg 0x%x, bios_codelen 0x%x\n",
+ boardp->bios_codeseg, boardp->bios_codelen);
+
+ /*
+ * If the BIOS saved a valid signature, then fill in
+ * the BIOS code segment base address.
+ */
+ if (boardp->bios_signature == 0x55AA) {
+ /*
+ * Convert x86 realmode code segment to a linear
+ * address by shifting left 4.
+ */
+ shp->base = ((ulong) boardp->bios_codeseg << 4);
+ } else {
+ shp->base = 0;
+ }
+ }
+
+ /*
+ * Register Board Resources - I/O Port, DMA, IRQ
+ */
+
+ /*
+ * Register I/O port range.
+ *
+ * For Wide boards the I/O ports are not used to access
+ * the board, but request the region anyway.
+ *
+ * 'shp->n_io_port' is not referenced, because it may be truncated.
+ */
+ ASC_DBG2(2,
+ "advansys_detect: request_region port 0x%lx, len 0x%x\n",
+ (ulong) shp->io_port, boardp->asc_n_io_port);
+ if (request_region(shp->io_port, boardp->asc_n_io_port,
+ "advansys") == NULL) {
+ ASC_PRINT3(
+"advansys_detect: board %d: request_region() failed, port 0x%lx, len 0x%x\n",
+ boardp->id, (ulong) shp->io_port, boardp->asc_n_io_port);
+#ifdef CONFIG_PROC_FS
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /* Register DMA Channel for Narrow boards. */
+ shp->dma_channel = NO_ISA_DMA; /* Default to no ISA DMA. */
+#ifdef CONFIG_ISA
+ if (ASC_NARROW_BOARD(boardp)) {
+ /* Register DMA channel for ISA bus. */
+ if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
+ shp->dma_channel = asc_dvc_varp->cfg->isa_dma_channel;
+ if ((ret =
+ request_dma(shp->dma_channel, "advansys")) != 0) {
+ ASC_PRINT3(
+"advansys_detect: board %d: request_dma() %d failed %d\n",
+ boardp->id, shp->dma_channel, ret);
+ release_region(shp->io_port, boardp->asc_n_io_port);
+#ifdef CONFIG_PROC_FS
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ AscEnableIsaDma(shp->dma_channel);
+ }
+ }
+#endif /* CONFIG_ISA */
+
+ /* Register IRQ Number. */
+ ASC_DBG1(2, "advansys_detect: request_irq() %d\n", shp->irq);
+ /*
+ * If request_irq() fails with the SA_INTERRUPT flag set,
+ * then try again without the SA_INTERRUPT flag set. This
+ * allows IRQ sharing to work even with other drivers that
+ * do not set the SA_INTERRUPT flag.
+ *
+ * If SA_INTERRUPT is not set, then interrupts are enabled
+ * before the driver interrupt function is called.
+ */
+ if (((ret = request_irq(shp->irq, advansys_interrupt,
+ SA_INTERRUPT | (share_irq == TRUE ? SA_SHIRQ : 0),
+ "advansys", boardp)) != 0) &&
+ ((ret = request_irq(shp->irq, advansys_interrupt,
+ (share_irq == TRUE ? SA_SHIRQ : 0),
+ "advansys", boardp)) != 0))
+ {
+ if (ret == -EBUSY) {
+ ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ 0x%x already in use.\n",
+ boardp->id, shp->irq);
+ } else if (ret == -EINVAL) {
+ ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ 0x%x not valid.\n",
+ boardp->id, shp->irq);
+ } else {
+ ASC_PRINT3(
+"advansys_detect: board %d: request_irq(): IRQ 0x%x failed with %d\n",
+ boardp->id, shp->irq, ret);
+ }
+ release_region(shp->io_port, boardp->asc_n_io_port);
+ iounmap(boardp->ioremap_addr);
+ if (shp->dma_channel != NO_ISA_DMA) {
+ free_dma(shp->dma_channel);
+ }
+#ifdef CONFIG_PROC_FS
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Initialize board RISC chip and enable interrupts.
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+ ASC_DBG(2, "advansys_detect: AscInitAsc1000Driver()\n");
+ warn_code = AscInitAsc1000Driver(asc_dvc_varp);
+ err_code = asc_dvc_varp->err_code;
+
+ if (warn_code || err_code) {
+ ASC_PRINT4(
+"advansys_detect: board %d error: init_state 0x%x, warn 0x%x, error 0x%x\n",
+ boardp->id, asc_dvc_varp->init_state,
+ warn_code, err_code);
+ }
+ } else {
+ ADV_CARR_T *carrp;
+ int req_cnt = 0;
+ adv_req_t *reqp = NULL;
+ int sg_cnt = 0;
+
+ /*
+ * Allocate buffer carrier structures. The total size
+ * is about 4 KB, so allocate all at once.
+ */
+ carrp =
+ (ADV_CARR_T *) kmalloc(ADV_CARRIER_BUFSIZE, GFP_ATOMIC);
+ ASC_DBG1(1, "advansys_detect: carrp 0x%lx\n", (ulong) carrp);
+
+ if (carrp == NULL) {
+ goto kmalloc_error;
+ }
+
+ /*
+ * Allocate up to 'max_host_qng' request structures for
+ * the Wide board. The total size is about 16 KB, so
+ * allocate all at once. If the allocation fails decrement
+ * and try again.
+ */
+ for (req_cnt = adv_dvc_varp->max_host_qng;
+ req_cnt > 0; req_cnt--) {
+
+ reqp = (adv_req_t *)
+ kmalloc(sizeof(adv_req_t) * req_cnt, GFP_ATOMIC);
+
+ ASC_DBG3(1,
+ "advansys_detect: reqp 0x%lx, req_cnt %d, bytes %lu\n",
+ (ulong) reqp, req_cnt,
+ (ulong) sizeof(adv_req_t) * req_cnt);
+
+ if (reqp != NULL) {
+ break;
+ }
+ }
+ if (reqp == NULL)
+ {
+ goto kmalloc_error;
+ }
+
+ /*
+ * Allocate up to ADV_TOT_SG_BLOCK request structures for
+ * the Wide board. Each structure is about 136 bytes.
+ */
+ boardp->adv_sgblkp = NULL;
+ for (sg_cnt = 0; sg_cnt < ADV_TOT_SG_BLOCK; sg_cnt++) {
+
+ sgp = (adv_sgblk_t *)
+ kmalloc(sizeof(adv_sgblk_t), GFP_ATOMIC);
+
+ if (sgp == NULL) {
+ break;
+ }
+
+ sgp->next_sgblkp = boardp->adv_sgblkp;
+ boardp->adv_sgblkp = sgp;
+
+ }
+ ASC_DBG3(1,
+ "advansys_detect: sg_cnt %d * %u = %u bytes\n",
+ sg_cnt, sizeof(adv_sgblk_t),
+ (unsigned) (sizeof(adv_sgblk_t) * sg_cnt));
+
+ /*
+ * If no request structures or scatter-gather structures could
+ * be allocated, then return an error. Otherwise continue with
+ * initialization.
+ */
+ kmalloc_error:
+ if (carrp == NULL)
+ {
+ ASC_PRINT1(
+"advansys_detect: board %d error: failed to kmalloc() carrier buffer.\n",
+ boardp->id);
+ err_code = ADV_ERROR;
+ } else if (reqp == NULL) {
+ kfree(carrp);
+ ASC_PRINT1(
+"advansys_detect: board %d error: failed to kmalloc() adv_req_t buffer.\n",
+ boardp->id);
+ err_code = ADV_ERROR;
+ } else if (boardp->adv_sgblkp == NULL) {
+ kfree(carrp);
+ kfree(reqp);
+ ASC_PRINT1(
+"advansys_detect: board %d error: failed to kmalloc() adv_sgblk_t buffers.\n",
+ boardp->id);
+ err_code = ADV_ERROR;
+ } else {
+
+ /* Save carrier buffer pointer. */
+ boardp->orig_carrp = carrp;
+
+ /*
+ * Save original pointer for kfree() in case the
+ * driver is built as a module and can be unloaded.
+ */
+ boardp->orig_reqp = reqp;
+
+ adv_dvc_varp->carrier_buf = carrp;
+
+ /*
+ * Point 'adv_reqp' to the request structures and
+ * link them together.
+ */
+ req_cnt--;
+ reqp[req_cnt].next_reqp = NULL;
+ for (; req_cnt > 0; req_cnt--) {
+ reqp[req_cnt - 1].next_reqp = &reqp[req_cnt];
+ }
+ boardp->adv_reqp = &reqp[0];
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ ASC_DBG(2,
+ "advansys_detect: AdvInitAsc3550Driver()\n");
+ warn_code = AdvInitAsc3550Driver(adv_dvc_varp);
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800) {
+ ASC_DBG(2,
+ "advansys_detect: AdvInitAsc38C0800Driver()\n");
+ warn_code = AdvInitAsc38C0800Driver(adv_dvc_varp);
+ } else {
+ ASC_DBG(2,
+ "advansys_detect: AdvInitAsc38C1600Driver()\n");
+ warn_code = AdvInitAsc38C1600Driver(adv_dvc_varp);
+ }
+ err_code = adv_dvc_varp->err_code;
+
+ if (warn_code || err_code) {
+ ASC_PRINT3(
+"advansys_detect: board %d error: warn 0x%x, error 0x%x\n",
+ boardp->id, warn_code, err_code);
+ }
+ }
+ }
+
+ if (err_code != 0) {
+ release_region(shp->io_port, boardp->asc_n_io_port);
+ if (ASC_WIDE_BOARD(boardp)) {
+ iounmap(boardp->ioremap_addr);
+ if (boardp->orig_carrp) {
+ kfree(boardp->orig_carrp);
+ boardp->orig_carrp = NULL;
+ }
+ if (boardp->orig_reqp) {
+ kfree(boardp->orig_reqp);
+ boardp->orig_reqp = boardp->adv_reqp = NULL;
+ }
+ while ((sgp = boardp->adv_sgblkp) != NULL)
+ {
+ boardp->adv_sgblkp = sgp->next_sgblkp;
+ kfree(sgp);
+ }
+ }
+ if (shp->dma_channel != NO_ISA_DMA) {
+ free_dma(shp->dma_channel);
+ }
+#ifdef CONFIG_PROC_FS
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ free_irq(shp->irq, boardp);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ ASC_DBG_PRT_SCSI_HOST(2, shp);
+ }
+ }
+
+ ASC_DBG1(1, "advansys_detect: done: asc_board_count %d\n", asc_board_count);
+ return asc_board_count;
+}
+
+/*
+ * advansys_release()
+ *
+ * Release resources allocated for a single AdvanSys adapter.
+ */
+int
+advansys_release(struct Scsi_Host *shp)
+{
+ asc_board_t *boardp;
+
+ ASC_DBG(1, "advansys_release: begin\n");
+ boardp = ASC_BOARDP(shp);
+ free_irq(shp->irq, boardp);
+ if (shp->dma_channel != NO_ISA_DMA) {
+ ASC_DBG(1, "advansys_release: free_dma()\n");
+ free_dma(shp->dma_channel);
+ }
+ release_region(shp->io_port, boardp->asc_n_io_port);
+ if (ASC_WIDE_BOARD(boardp)) {
+ adv_sgblk_t *sgp = NULL;
+
+ iounmap(boardp->ioremap_addr);
+ if (boardp->orig_carrp) {
+ kfree(boardp->orig_carrp);
+ boardp->orig_carrp = NULL;
+ }
+ if (boardp->orig_reqp) {
+ kfree(boardp->orig_reqp);
+ boardp->orig_reqp = boardp->adv_reqp = NULL;
+ }
+ while ((sgp = boardp->adv_sgblkp) != NULL)
+ {
+ boardp->adv_sgblkp = sgp->next_sgblkp;
+ kfree(sgp);
+ }
+ }
+#ifdef CONFIG_PROC_FS
+ ASC_ASSERT(boardp->prtbuf != NULL);
+ kfree(boardp->prtbuf);
+#endif /* CONFIG_PROC_FS */
+ scsi_unregister(shp);
+ ASC_DBG(1, "advansys_release: end\n");
+ return 0;
+}
+
+/*
+ * advansys_info()
+ *
+ * Return suitable for printing on the console with the argument
+ * adapter's configuration information.
+ *
+ * Note: The information line should not exceed ASC_INFO_SIZE bytes,
+ * otherwise the static 'info' array will be overrun.
+ */
+const char *
+advansys_info(struct Scsi_Host *shp)
+{
+ static char info[ASC_INFO_SIZE];
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ char *busname;
+ int iolen;
+ char *widename = NULL;
+
+ boardp = ASC_BOARDP(shp);
+ if (ASC_NARROW_BOARD(boardp)) {
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ ASC_DBG(1, "advansys_info: begin\n");
+ if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
+ if ((asc_dvc_varp->bus_type & ASC_IS_ISAPNP) == ASC_IS_ISAPNP) {
+ busname = "ISA PnP";
+ } else {
+ busname = "ISA";
+ }
+ /* Don't reference 'shp->n_io_port'; It may be truncated. */
+ sprintf(info,
+"AdvanSys SCSI %s: %s: IO 0x%lX-0x%lX, IRQ 0x%X, DMA 0x%X",
+ ASC_VERSION, busname,
+ (ulong) shp->io_port,
+ (ulong) shp->io_port + boardp->asc_n_io_port - 1,
+ shp->irq, shp->dma_channel);
+ } else {
+ if (asc_dvc_varp->bus_type & ASC_IS_VL) {
+ busname = "VL";
+ } else if (asc_dvc_varp->bus_type & ASC_IS_EISA) {
+ busname = "EISA";
+ } else if (asc_dvc_varp->bus_type & ASC_IS_PCI) {
+ if ((asc_dvc_varp->bus_type & ASC_IS_PCI_ULTRA)
+ == ASC_IS_PCI_ULTRA) {
+ busname = "PCI Ultra";
+ } else {
+ busname = "PCI";
+ }
+ } else {
+ busname = "?";
+ ASC_PRINT2( "advansys_info: board %d: unknown bus type %d\n",
+ boardp->id, asc_dvc_varp->bus_type);
+ }
+ /* Don't reference 'shp->n_io_port'; It may be truncated. */
+ sprintf(info,
+ "AdvanSys SCSI %s: %s: IO 0x%lX-0x%lX, IRQ 0x%X",
+ ASC_VERSION, busname,
+ (ulong) shp->io_port,
+ (ulong) shp->io_port + boardp->asc_n_io_port - 1,
+ shp->irq);
+ }
+ } else {
+ /*
+ * Wide Adapter Information
+ *
+ * Memory-mapped I/O is used instead of I/O space to access
+ * the adapter, but display the I/O Port range. The Memory
+ * I/O address is displayed through the driver /proc file.
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ iolen = ADV_3550_IOLEN;
+ widename = "Ultra-Wide";
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ iolen = ADV_38C0800_IOLEN;
+ widename = "Ultra2-Wide";
+ } else
+ {
+ iolen = ADV_38C1600_IOLEN;
+ widename = "Ultra3-Wide";
+ }
+ sprintf(info, "AdvanSys SCSI %s: PCI %s: PCIMEM 0x%lX-0x%lX, IRQ 0x%X",
+ ASC_VERSION,
+ widename,
+ (ulong) adv_dvc_varp->iop_base,
+ (ulong) adv_dvc_varp->iop_base + iolen - 1,
+ shp->irq);
+ }
+ ASC_ASSERT(strlen(info) < ASC_INFO_SIZE);
+ ASC_DBG(1, "advansys_info: end\n");
+ return info;
+}
+
+/*
+ * advansys_queuecommand() - interrupt-driven I/O entrypoint.
+ *
+ * This function always returns 0. Command return status is saved
+ * in the 'scp' result field.
+ */
+int
+advansys_queuecommand(struct scsi_cmnd *scp, void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ ulong flags;
+ struct scsi_cmnd *done_scp;
+
+ shp = scp->device->host;
+ boardp = ASC_BOARDP(shp);
+ ASC_STATS(shp, queuecommand);
+
+ /* host_lock taken by mid-level prior to call but need to protect */
+ /* against own ISR */
+ spin_lock_irqsave(&boardp->lock, flags);
+
+ /*
+ * Block new commands while handling a reset or abort request.
+ */
+ if (boardp->flags & ASC_HOST_IN_RESET) {
+ ASC_DBG1(1,
+ "advansys_queuecommand: scp 0x%lx blocked for reset request\n",
+ (ulong) scp);
+ scp->result = HOST_BYTE(DID_RESET);
+
+ /*
+ * Add blocked requests to the board's 'done' queue. The queued
+ * requests will be completed at the end of the abort or reset
+ * handling.
+ */
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ spin_unlock_irqrestore(&boardp->lock, flags);
+ return 0;
+ }
+
+ /*
+ * Attempt to execute any waiting commands for the board.
+ */
+ if (!ASC_QUEUE_EMPTY(&boardp->waiting)) {
+ ASC_DBG(1,
+ "advansys_queuecommand: before asc_execute_queue() waiting\n");
+ asc_execute_queue(&boardp->waiting);
+ }
+
+ /*
+ * Save the function pointer to Linux mid-level 'done' function
+ * and attempt to execute the command.
+ *
+ * If ASC_NOERROR is returned the request has been added to the
+ * board's 'active' queue and will be completed by the interrupt
+ * handler.
+ *
+ * If ASC_BUSY is returned add the request to the board's per
+ * target waiting list. This is the first time the request has
+ * been tried. Add it to the back of the waiting list. It will be
+ * retried later.
+ *
+ * If an error occurred, the request will have been placed on the
+ * board's 'done' queue and must be completed before returning.
+ */
+ scp->scsi_done = done;
+ switch (asc_execute_scsi_cmnd(scp)) {
+ case ASC_NOERROR:
+ break;
+ case ASC_BUSY:
+ asc_enqueue(&boardp->waiting, scp, ASC_BACK);
+ break;
+ case ASC_ERROR:
+ default:
+ done_scp = asc_dequeue_list(&boardp->done, NULL, ASC_TID_ALL);
+ /* Interrupts could be enabled here. */
+ asc_scsi_done_list(done_scp);
+ break;
+ }
+ spin_unlock_irqrestore(&boardp->lock, flags);
+
+ return 0;
+}
+
+/*
+ * advansys_reset()
+ *
+ * Reset the bus associated with the command 'scp'.
+ *
+ * This function runs its own thread. Interrupts must be blocked but
+ * sleeping is allowed and no locking other than for host structures is
+ * required. Returns SUCCESS or FAILED.
+ */
+int
+advansys_reset(struct scsi_cmnd *scp)
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ ulong flags;
+ struct scsi_cmnd *done_scp = NULL, *last_scp = NULL;
+ struct scsi_cmnd *tscp, *new_last_scp;
+ int status;
+ int ret = SUCCESS;
+
+ ASC_DBG1(1, "advansys_reset: 0x%lx\n", (ulong) scp);
+
+#ifdef ADVANSYS_STATS
+ if (scp->device->host != NULL) {
+ ASC_STATS(scp->device->host, reset);
+ }
+#endif /* ADVANSYS_STATS */
+
+ if ((shp = scp->device->host) == NULL) {
+ scp->result = HOST_BYTE(DID_ERROR);
+ return FAILED;
+ }
+
+ boardp = ASC_BOARDP(shp);
+
+ ASC_PRINT1("advansys_reset: board %d: SCSI bus reset started...\n",
+ boardp->id);
+ /*
+ * Check for re-entrancy.
+ */
+ spin_lock_irqsave(&boardp->lock, flags);
+ if (boardp->flags & ASC_HOST_IN_RESET) {
+ spin_unlock_irqrestore(&boardp->lock, flags);
+ return FAILED;
+ }
+ boardp->flags |= ASC_HOST_IN_RESET;
+ spin_unlock_irqrestore(&boardp->lock, flags);
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Narrow Board
+ */
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+
+ /*
+ * Reset the chip and SCSI bus.
+ */
+ ASC_DBG(1, "advansys_reset: before AscInitAsc1000Driver()\n");
+ status = AscInitAsc1000Driver(asc_dvc_varp);
+
+ /* Refer to ASC_IERR_* defintions for meaning of 'err_code'. */
+ if (asc_dvc_varp->err_code) {
+ ASC_PRINT2(
+ "advansys_reset: board %d: SCSI bus reset error: 0x%x\n",
+ boardp->id, asc_dvc_varp->err_code);
+ ret = FAILED;
+ } else if (status) {
+ ASC_PRINT2(
+ "advansys_reset: board %d: SCSI bus reset warning: 0x%x\n",
+ boardp->id, status);
+ } else {
+ ASC_PRINT1(
+ "advansys_reset: board %d: SCSI bus reset successful.\n",
+ boardp->id);
+ }
+
+ ASC_DBG(1, "advansys_reset: after AscInitAsc1000Driver()\n");
+ spin_lock_irqsave(&boardp->lock, flags);
+
+ } else {
+ /*
+ * Wide Board
+ *
+ * If the suggest reset bus flags are set, then reset the bus.
+ * Otherwise only reset the device.
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+
+ /*
+ * Reset the target's SCSI bus.
+ */
+ ASC_DBG(1, "advansys_reset: before AdvResetChipAndSB()\n");
+ switch (AdvResetChipAndSB(adv_dvc_varp)) {
+ case ASC_TRUE:
+ ASC_PRINT1("advansys_reset: board %d: SCSI bus reset successful.\n",
+ boardp->id);
+ break;
+ case ASC_FALSE:
+ default:
+ ASC_PRINT1("advansys_reset: board %d: SCSI bus reset error.\n",
+ boardp->id);
+ ret = FAILED;
+ break;
+ }
+ spin_lock_irqsave(&boardp->lock, flags);
+ (void) AdvISR(adv_dvc_varp);
+ }
+ /* Board lock is held. */
+
+ /*
+ * Dequeue all board 'done' requests. A pointer to the last request
+ * is returned in 'last_scp'.
+ */
+ done_scp = asc_dequeue_list(&boardp->done, &last_scp, ASC_TID_ALL);
+
+ /*
+ * Dequeue all board 'active' requests for all devices and set
+ * the request status to DID_RESET. A pointer to the last request
+ * is returned in 'last_scp'.
+ */
+ if (done_scp == NULL) {
+ done_scp = asc_dequeue_list(&boardp->active, &last_scp, ASC_TID_ALL);
+ for (tscp = done_scp; tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ } else {
+ /* Append to 'done_scp' at the end with 'last_scp'. */
+ ASC_ASSERT(last_scp != NULL);
+ last_scp->host_scribble = (unsigned char *)asc_dequeue_list(
+ &boardp->active, &new_last_scp, ASC_TID_ALL);
+ if (new_last_scp != NULL) {
+ ASC_ASSERT(REQPNEXT(last_scp) != NULL);
+ for (tscp = REQPNEXT(last_scp); tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ last_scp = new_last_scp;
+ }
+ }
+
+ /*
+ * Dequeue all 'waiting' requests and set the request status
+ * to DID_RESET.
+ */
+ if (done_scp == NULL) {
+ done_scp = asc_dequeue_list(&boardp->waiting, &last_scp, ASC_TID_ALL);
+ for (tscp = done_scp; tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ } else {
+ /* Append to 'done_scp' at the end with 'last_scp'. */
+ ASC_ASSERT(last_scp != NULL);
+ last_scp->host_scribble = (unsigned char *)asc_dequeue_list(
+ &boardp->waiting, &new_last_scp, ASC_TID_ALL);
+ if (new_last_scp != NULL) {
+ ASC_ASSERT(REQPNEXT(last_scp) != NULL);
+ for (tscp = REQPNEXT(last_scp); tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ last_scp = new_last_scp;
+ }
+ }
+
+ /* Save the time of the most recently completed reset. */
+ boardp->last_reset = jiffies;
+
+ /* Clear reset flag. */
+ boardp->flags &= ~ASC_HOST_IN_RESET;
+ spin_unlock_irqrestore(&boardp->lock, flags);
+
+ /*
+ * Complete all the 'done_scp' requests.
+ */
+ if (done_scp != NULL) {
+ asc_scsi_done_list(done_scp);
+ }
+
+ ASC_DBG1(1, "advansys_reset: ret %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * advansys_biosparam()
+ *
+ * Translate disk drive geometry if the "BIOS greater than 1 GB"
+ * support is enabled for a drive.
+ *
+ * ip (information pointer) is an int array with the following definition:
+ * ip[0]: heads
+ * ip[1]: sectors
+ * ip[2]: cylinders
+ */
+int
+advansys_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int ip[])
+{
+ asc_board_t *boardp;
+
+ ASC_DBG(1, "advansys_biosparam: begin\n");
+ ASC_STATS(sdev->host, biosparam);
+ boardp = ASC_BOARDP(sdev->host);
+ if (ASC_NARROW_BOARD(boardp)) {
+ if ((boardp->dvc_var.asc_dvc_var.dvc_cntl &
+ ASC_CNTL_BIOS_GT_1GB) && capacity > 0x200000) {
+ ip[0] = 255;
+ ip[1] = 63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ }
+ } else {
+ if ((boardp->dvc_var.adv_dvc_var.bios_ctrl &
+ BIOS_CTRL_EXTENDED_XLAT) && capacity > 0x200000) {
+ ip[0] = 255;
+ ip[1] = 63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ }
+ }
+ ip[2] = (unsigned long)capacity / (ip[0] * ip[1]);
+ ASC_DBG(1, "advansys_biosparam: end\n");
+ return 0;
+}
+
+/*
+ * advansys_setup()
+ *
+ * This function is called from init/main.c at boot time.
+ * It it passed LILO parameters that can be set from the
+ * LILO command line or in /etc/lilo.conf.
+ *
+ * It is used by the AdvanSys driver to either disable I/O
+ * port scanning or to limit scanning to 1 - 4 I/O ports.
+ * Regardless of the option setting EISA and PCI boards
+ * will still be searched for and detected. This option
+ * only affects searching for ISA and VL boards.
+ *
+ * If ADVANSYS_DEBUG is defined the driver debug level may
+ * be set using the 5th (ASC_NUM_IOPORT_PROBE + 1) I/O Port.
+ *
+ * Examples:
+ * 1. Eliminate I/O port scanning:
+ * boot: linux advansys=
+ * or
+ * boot: linux advansys=0x0
+ * 2. Limit I/O port scanning to one I/O port:
+ * boot: linux advansys=0x110
+ * 3. Limit I/O port scanning to four I/O ports:
+ * boot: linux advansys=0x110,0x210,0x230,0x330
+ * 4. If ADVANSYS_DEBUG, limit I/O port scanning to four I/O ports and
+ * set the driver debug level to 2.
+ * boot: linux advansys=0x110,0x210,0x230,0x330,0xdeb2
+ *
+ * ints[0] - number of arguments
+ * ints[1] - first argument
+ * ints[2] - second argument
+ * ...
+ */
+void __init
+advansys_setup(char *str, int *ints)
+{
+ int i;
+
+ if (asc_iopflag == ASC_TRUE) {
+ printk("AdvanSys SCSI: 'advansys' LILO option may appear only once\n");
+ return;
+ }
+
+ asc_iopflag = ASC_TRUE;
+
+ if (ints[0] > ASC_NUM_IOPORT_PROBE) {
+#ifdef ADVANSYS_DEBUG
+ if ((ints[0] == ASC_NUM_IOPORT_PROBE + 1) &&
+ (ints[ASC_NUM_IOPORT_PROBE + 1] >> 4 == 0xdeb)) {
+ asc_dbglvl = ints[ASC_NUM_IOPORT_PROBE + 1] & 0xf;
+ } else {
+#endif /* ADVANSYS_DEBUG */
+ printk("AdvanSys SCSI: only %d I/O ports accepted\n",
+ ASC_NUM_IOPORT_PROBE);
+#ifdef ADVANSYS_DEBUG
+ }
+#endif /* ADVANSYS_DEBUG */
+ }
+
+#ifdef ADVANSYS_DEBUG
+ ASC_DBG1(1, "advansys_setup: ints[0] %d\n", ints[0]);
+ for (i = 1; i < ints[0]; i++) {
+ ASC_DBG2(1, " ints[%d] 0x%x", i, ints[i]);
+ }
+ ASC_DBG(1, "\n");
+#endif /* ADVANSYS_DEBUG */
+
+ for (i = 1; i <= ints[0] && i <= ASC_NUM_IOPORT_PROBE; i++) {
+ asc_ioport[i-1] = ints[i];
+ ASC_DBG2(1, "advansys_setup: asc_ioport[%d] 0x%x\n",
+ i - 1, asc_ioport[i-1]);
+ }
+}
+
+
+/*
+ * --- Loadable Driver Support
+ */
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "advansys",
+#ifdef CONFIG_PROC_FS
+ .proc_info = advansys_proc_info,
+#endif
+ .name = "advansys",
+ .detect = advansys_detect,
+ .release = advansys_release,
+ .info = advansys_info,
+ .queuecommand = advansys_queuecommand,
+ .eh_bus_reset_handler = advansys_reset,
+ .bios_param = advansys_biosparam,
+ .slave_configure = advansys_slave_configure,
+ /*
+ * Because the driver may control an ISA adapter 'unchecked_isa_dma'
+ * must be set. The flag will be cleared in advansys_detect for non-ISA
+ * adapters. Refer to the comment in scsi_module.c for more information.
+ */
+ .unchecked_isa_dma = 1,
+ /*
+ * All adapters controlled by this driver are capable of large
+ * scatter-gather lists. According to the mid-level SCSI documentation
+ * this obviates any performance gain provided by setting
+ * 'use_clustering'. But empirically while CPU utilization is increased
+ * by enabling clustering, I/O throughput increases as well.
+ */
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
+
+
+/*
+ * --- Miscellaneous Driver Functions
+ */
+
+/*
+ * First-level interrupt handler.
+ *
+ * 'dev_id' is a pointer to the interrupting adapter's asc_board_t. Because
+ * all boards are currently checked for interrupts on each interrupt, 'dev_id'
+ * is not referenced. 'dev_id' could be used to identify an interrupt passed
+ * to the AdvanSys driver which is for a device sharing an interrupt with
+ * an AdvanSys adapter.
+ */
+STATIC irqreturn_t
+advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ulong flags;
+ int i;
+ asc_board_t *boardp;
+ struct scsi_cmnd *done_scp = NULL, *last_scp = NULL;
+ struct scsi_cmnd *new_last_scp;
+ struct Scsi_Host *shp;
+
+ ASC_DBG(1, "advansys_interrupt: begin\n");
+
+ /*
+ * Check for interrupts on all boards.
+ * AscISR() will call asc_isr_callback().
+ */
+ for (i = 0; i < asc_board_count; i++) {
+ shp = asc_host[i];
+ boardp = ASC_BOARDP(shp);
+ ASC_DBG2(2, "advansys_interrupt: i %d, boardp 0x%lx\n",
+ i, (ulong) boardp);
+ spin_lock_irqsave(&boardp->lock, flags);
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Narrow Board
+ */
+ if (AscIsIntPending(shp->io_port)) {
+ ASC_STATS(shp, interrupt);
+ ASC_DBG(1, "advansys_interrupt: before AscISR()\n");
+ AscISR(&boardp->dvc_var.asc_dvc_var);
+ }
+ } else {
+ /*
+ * Wide Board
+ */
+ ASC_DBG(1, "advansys_interrupt: before AdvISR()\n");
+ if (AdvISR(&boardp->dvc_var.adv_dvc_var)) {
+ ASC_STATS(shp, interrupt);
+ }
+ }
+
+ /*
+ * Start waiting requests and create a list of completed requests.
+ *
+ * If a reset request is being performed for the board, the reset
+ * handler will complete pending requests after it has completed.
+ */
+ if ((boardp->flags & ASC_HOST_IN_RESET) == 0) {
+ ASC_DBG2(1, "advansys_interrupt: done_scp 0x%lx, last_scp 0x%lx\n",
+ (ulong) done_scp, (ulong) last_scp);
+
+ /* Start any waiting commands for the board. */
+ if (!ASC_QUEUE_EMPTY(&boardp->waiting)) {
+ ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n");
+ asc_execute_queue(&boardp->waiting);
+ }
+
+ /*
+ * Add to the list of requests that must be completed.
+ *
+ * 'done_scp' will always be NULL on the first iteration
+ * of this loop. 'last_scp' is set at the same time as
+ * 'done_scp'.
+ */
+ if (done_scp == NULL) {
+ done_scp = asc_dequeue_list(&boardp->done, &last_scp,
+ ASC_TID_ALL);
+ } else {
+ ASC_ASSERT(last_scp != NULL);
+ last_scp->host_scribble = (unsigned char *)asc_dequeue_list(
+ &boardp->done, &new_last_scp, ASC_TID_ALL);
+ if (new_last_scp != NULL) {
+ ASC_ASSERT(REQPNEXT(last_scp) != NULL);
+ last_scp = new_last_scp;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&boardp->lock, flags);
+ }
+
+ /*
+ * If interrupts were enabled on entry, then they
+ * are now enabled here.
+ *
+ * Complete all requests on the done list.
+ */
+
+ asc_scsi_done_list(done_scp);
+
+ ASC_DBG(1, "advansys_interrupt: end\n");
+ return IRQ_HANDLED;
+}
+
+/*
+ * Set the number of commands to queue per device for the
+ * specified host adapter.
+ */
+STATIC int
+advansys_slave_configure(struct scsi_device *device)
+{
+ asc_board_t *boardp;
+
+ boardp = ASC_BOARDP(device->host);
+ boardp->flags |= ASC_SELECT_QUEUE_DEPTHS;
+ /*
+ * Save a pointer to the device and set its initial/maximum
+ * queue depth. Only save the pointer for a lun0 dev though.
+ */
+ if(device->lun == 0)
+ boardp->device[device->id] = device;
+ if(device->tagged_supported) {
+ if (ASC_NARROW_BOARD(boardp)) {
+ scsi_adjust_queue_depth(device, MSG_ORDERED_TAG,
+ boardp->dvc_var.asc_dvc_var.max_dvc_qng[device->id]);
+ } else {
+ scsi_adjust_queue_depth(device, MSG_ORDERED_TAG,
+ boardp->dvc_var.adv_dvc_var.max_dvc_qng);
+ }
+ } else {
+ scsi_adjust_queue_depth(device, 0, device->host->cmd_per_lun);
+ }
+ ASC_DBG4(1, "advansys_slave_configure: device 0x%lx, boardp 0x%lx, id %d, depth %d\n",
+ (ulong) device, (ulong) boardp, device->id, device->queue_depth);
+ return 0;
+}
+
+/*
+ * Complete all requests on the singly linked list pointed
+ * to by 'scp'.
+ *
+ * Interrupts can be enabled on entry.
+ */
+STATIC void
+asc_scsi_done_list(struct scsi_cmnd *scp)
+{
+ struct scsi_cmnd *tscp;
+
+ ASC_DBG(2, "asc_scsi_done_list: begin\n");
+ while (scp != NULL) {
+ asc_board_t *boardp;
+ struct device *dev;
+
+ ASC_DBG1(3, "asc_scsi_done_list: scp 0x%lx\n", (ulong) scp);
+ tscp = REQPNEXT(scp);
+ scp->host_scribble = NULL;
+
+ boardp = ASC_BOARDP(scp->device->host);
+
+ if (ASC_NARROW_BOARD(boardp))
+ dev = boardp->dvc_cfg.asc_dvc_cfg.dev;
+ else
+ dev = boardp->dvc_cfg.adv_dvc_cfg.dev;
+
+ if (scp->use_sg)
+ dma_unmap_sg(dev, (struct scatterlist *)scp->request_buffer,
+ scp->use_sg, scp->sc_data_direction);
+ else if (scp->request_bufflen)
+ dma_unmap_single(dev, scp->SCp.dma_handle,
+ scp->request_bufflen, scp->sc_data_direction);
+
+ ASC_STATS(scp->device->host, done);
+ ASC_ASSERT(scp->scsi_done != NULL);
+
+ scp->scsi_done(scp);
+
+ scp = tscp;
+ }
+ ASC_DBG(2, "asc_scsi_done_list: done\n");
+ return;
+}
+
+/*
+ * Execute a single 'Scsi_Cmnd'.
+ *
+ * The function 'done' is called when the request has been completed.
+ *
+ * Scsi_Cmnd:
+ *
+ * host - board controlling device
+ * device - device to send command
+ * target - target of device
+ * lun - lun of device
+ * cmd_len - length of SCSI CDB
+ * cmnd - buffer for SCSI 8, 10, or 12 byte CDB
+ * use_sg - if non-zero indicates scatter-gather request with use_sg elements
+ *
+ * if (use_sg == 0) {
+ * request_buffer - buffer address for request
+ * request_bufflen - length of request buffer
+ * } else {
+ * request_buffer - pointer to scatterlist structure
+ * }
+ *
+ * sense_buffer - sense command buffer
+ *
+ * result (4 bytes of an int):
+ * Byte Meaning
+ * 0 SCSI Status Byte Code
+ * 1 SCSI One Byte Message Code
+ * 2 Host Error Code
+ * 3 Mid-Level Error Code
+ *
+ * host driver fields:
+ * SCp - Scsi_Pointer used for command processing status
+ * scsi_done - used to save caller's done function
+ * host_scribble - used for pointer to another struct scsi_cmnd
+ *
+ * If this function returns ASC_NOERROR the request has been enqueued
+ * on the board's 'active' queue and will be completed from the
+ * interrupt handler.
+ *
+ * If this function returns ASC_NOERROR the request has been enqueued
+ * on the board's 'done' queue and must be completed by the caller.
+ *
+ * If ASC_BUSY is returned the request will be enqueued by the
+ * caller on the target's waiting queue and re-tried later.
+ */
+STATIC int
+asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
+{
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ ADV_SCSI_REQ_Q *adv_scsiqp;
+ struct scsi_device *device;
+ int ret;
+
+ ASC_DBG2(1, "asc_execute_scsi_cmnd: scp 0x%lx, done 0x%lx\n",
+ (ulong) scp, (ulong) scp->scsi_done);
+
+ boardp = ASC_BOARDP(scp->device->host);
+ device = boardp->device[scp->device->id];
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Build and execute Narrow Board request.
+ */
+
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+
+ /*
+ * Build Asc Library request structure using the
+ * global structures 'asc_scsi_req' and 'asc_sg_head'.
+ *
+ * If an error is returned, then the request has been
+ * queued on the board done queue. It will be completed
+ * by the caller.
+ *
+ * asc_build_req() can not return ASC_BUSY.
+ */
+ if (asc_build_req(boardp, scp) == ASC_ERROR) {
+ ASC_STATS(scp->device->host, build_error);
+ return ASC_ERROR;
+ }
+
+ /*
+ * Execute the command. If there is no error, add the command
+ * to the active queue.
+ */
+ switch (ret = AscExeScsiQueue(asc_dvc_varp, &asc_scsi_q)) {
+ case ASC_NOERROR:
+ ASC_STATS(scp->device->host, exe_noerror);
+ /*
+ * Increment monotonically increasing per device successful
+ * request counter. Wrapping doesn't matter.
+ */
+ boardp->reqcnt[scp->device->id]++;
+ asc_enqueue(&boardp->active, scp, ASC_BACK);
+ ASC_DBG(1,
+ "asc_execute_scsi_cmnd: AscExeScsiQueue(), ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ /*
+ * Caller will enqueue request on the target's waiting queue
+ * and retry later.
+ */
+ ASC_STATS(scp->device->host, exe_busy);
+ break;
+ case ASC_ERROR:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AscExeScsiQueue() ASC_ERROR, err_code 0x%x\n",
+ boardp->id, asc_dvc_varp->err_code);
+ ASC_STATS(scp->device->host, exe_error);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ default:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AscExeScsiQueue() unknown, err_code 0x%x\n",
+ boardp->id, asc_dvc_varp->err_code);
+ ASC_STATS(scp->device->host, exe_unknown);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ }
+ } else {
+ /*
+ * Build and execute Wide Board request.
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+
+ /*
+ * Build and get a pointer to an Adv Library request structure.
+ *
+ * If the request is successfully built then send it below,
+ * otherwise return with an error.
+ */
+ switch (adv_build_req(boardp, scp, &adv_scsiqp)) {
+ case ASC_NOERROR:
+ ASC_DBG(3, "asc_execute_scsi_cmnd: adv_build_req ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ ASC_DBG(1, "asc_execute_scsi_cmnd: adv_build_req ASC_BUSY\n");
+ /*
+ * If busy is returned the request has not been enqueued.
+ * It will be enqueued by the caller on the target's waiting
+ * queue and retried later.
+ *
+ * The asc_stats fields 'adv_build_noreq' and 'adv_build_nosg'
+ * count wide board busy conditions. They are updated in
+ * adv_build_req and adv_get_sglist, respectively.
+ */
+ return ASC_BUSY;
+ case ASC_ERROR:
+ /*
+ * If an error is returned, then the request has been
+ * queued on the board done queue. It will be completed
+ * by the caller.
+ */
+ default:
+ ASC_DBG(1, "asc_execute_scsi_cmnd: adv_build_req ASC_ERROR\n");
+ ASC_STATS(scp->device->host, build_error);
+ return ASC_ERROR;
+ }
+
+ /*
+ * Execute the command. If there is no error, add the command
+ * to the active queue.
+ */
+ switch (ret = AdvExeScsiQueue(adv_dvc_varp, adv_scsiqp)) {
+ case ASC_NOERROR:
+ ASC_STATS(scp->device->host, exe_noerror);
+ /*
+ * Increment monotonically increasing per device successful
+ * request counter. Wrapping doesn't matter.
+ */
+ boardp->reqcnt[scp->device->id]++;
+ asc_enqueue(&boardp->active, scp, ASC_BACK);
+ ASC_DBG(1,
+ "asc_execute_scsi_cmnd: AdvExeScsiQueue(), ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ /*
+ * Caller will enqueue request on the target's waiting queue
+ * and retry later.
+ */
+ ASC_STATS(scp->device->host, exe_busy);
+ break;
+ case ASC_ERROR:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AdvExeScsiQueue() ASC_ERROR, err_code 0x%x\n",
+ boardp->id, adv_dvc_varp->err_code);
+ ASC_STATS(scp->device->host, exe_error);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ default:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AdvExeScsiQueue() unknown, err_code 0x%x\n",
+ boardp->id, adv_dvc_varp->err_code);
+ ASC_STATS(scp->device->host, exe_unknown);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ }
+ }
+
+ ASC_DBG(1, "asc_execute_scsi_cmnd: end\n");
+ return ret;
+}
+
+/*
+ * Build a request structure for the Asc Library (Narrow Board).
+ *
+ * The global structures 'asc_scsi_q' and 'asc_sg_head' are
+ * used to build the request.
+ *
+ * If an error occurs, then queue the request on the board done
+ * queue and return ASC_ERROR.
+ */
+STATIC int
+asc_build_req(asc_board_t *boardp, struct scsi_cmnd *scp)
+{
+ struct device *dev = boardp->dvc_cfg.asc_dvc_cfg.dev;
+
+ /*
+ * Mutually exclusive access is required to 'asc_scsi_q' and
+ * 'asc_sg_head' until after the request is started.
+ */
+ memset(&asc_scsi_q, 0, sizeof(ASC_SCSI_Q));
+
+ /*
+ * Point the ASC_SCSI_Q to the 'struct scsi_cmnd'.
+ */
+ asc_scsi_q.q2.srb_ptr = ASC_VADDR_TO_U32(scp);
+
+ /*
+ * Build the ASC_SCSI_Q request.
+ *
+ * For narrow boards a CDB length maximum of 12 bytes
+ * is supported.
+ */
+ if (scp->cmd_len > ASC_MAX_CDB_LEN) {
+ ASC_PRINT3(
+"asc_build_req: board %d: cmd_len %d > ASC_MAX_CDB_LEN %d\n",
+ boardp->id, scp->cmd_len, ASC_MAX_CDB_LEN);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ return ASC_ERROR;
+ }
+ asc_scsi_q.cdbptr = &scp->cmnd[0];
+ asc_scsi_q.q2.cdb_len = scp->cmd_len;
+ asc_scsi_q.q1.target_id = ASC_TID_TO_TARGET_ID(scp->device->id);
+ asc_scsi_q.q1.target_lun = scp->device->lun;
+ asc_scsi_q.q2.target_ix = ASC_TIDLUN_TO_IX(scp->device->id, scp->device->lun);
+ asc_scsi_q.q1.sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0]));
+ asc_scsi_q.q1.sense_len = sizeof(scp->sense_buffer);
+
+ /*
+ * If there are any outstanding requests for the current target,
+ * then every 255th request send an ORDERED request. This heuristic
+ * tries to retain the benefit of request sorting while preventing
+ * request starvation. 255 is the max number of tags or pending commands
+ * a device may have outstanding.
+ *
+ * The request count is incremented below for every successfully
+ * started request.
+ *
+ */
+ if ((boardp->dvc_var.asc_dvc_var.cur_dvc_qng[scp->device->id] > 0) &&
+ (boardp->reqcnt[scp->device->id] % 255) == 0) {
+ asc_scsi_q.q2.tag_code = MSG_ORDERED_TAG;
+ } else {
+ asc_scsi_q.q2.tag_code = MSG_SIMPLE_TAG;
+ }
+
+ /*
+ * Build ASC_SCSI_Q for a contiguous buffer or a scatter-gather
+ * buffer command.
+ */
+ if (scp->use_sg == 0) {
+ /*
+ * CDB request of single contiguous buffer.
+ */
+ ASC_STATS(scp->device->host, cont_cnt);
+ scp->SCp.dma_handle = scp->request_bufflen ?
+ dma_map_single(dev, scp->request_buffer,
+ scp->request_bufflen, scp->sc_data_direction) : 0;
+ asc_scsi_q.q1.data_addr = cpu_to_le32(scp->SCp.dma_handle);
+ asc_scsi_q.q1.data_cnt = cpu_to_le32(scp->request_bufflen);
+ ASC_STATS_ADD(scp->device->host, cont_xfer,
+ ASC_CEILING(scp->request_bufflen, 512));
+ asc_scsi_q.q1.sg_queue_cnt = 0;
+ asc_scsi_q.sg_head = NULL;
+ } else {
+ /*
+ * CDB scatter-gather request list.
+ */
+ int sgcnt;
+ int use_sg;
+ struct scatterlist *slp;
+
+ slp = (struct scatterlist *)scp->request_buffer;
+ use_sg = dma_map_sg(dev, slp, scp->use_sg, scp->sc_data_direction);
+
+ if (use_sg > scp->device->host->sg_tablesize) {
+ ASC_PRINT3(
+"asc_build_req: board %d: use_sg %d > sg_tablesize %d\n",
+ boardp->id, use_sg, scp->device->host->sg_tablesize);
+ dma_unmap_sg(dev, slp, scp->use_sg, scp->sc_data_direction);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ return ASC_ERROR;
+ }
+
+ ASC_STATS(scp->device->host, sg_cnt);
+
+ /*
+ * Use global ASC_SG_HEAD structure and set the ASC_SCSI_Q
+ * structure to point to it.
+ */
+ memset(&asc_sg_head, 0, sizeof(ASC_SG_HEAD));
+
+ asc_scsi_q.q1.cntl |= QC_SG_HEAD;
+ asc_scsi_q.sg_head = &asc_sg_head;
+ asc_scsi_q.q1.data_cnt = 0;
+ asc_scsi_q.q1.data_addr = 0;
+ /* This is a byte value, otherwise it would need to be swapped. */
+ asc_sg_head.entry_cnt = asc_scsi_q.q1.sg_queue_cnt = use_sg;
+ ASC_STATS_ADD(scp->device->host, sg_elem, asc_sg_head.entry_cnt);
+
+ /*
+ * Convert scatter-gather list into ASC_SG_HEAD list.
+ */
+ for (sgcnt = 0; sgcnt < use_sg; sgcnt++, slp++) {
+ asc_sg_head.sg_list[sgcnt].addr = cpu_to_le32(sg_dma_address(slp));
+ asc_sg_head.sg_list[sgcnt].bytes = cpu_to_le32(sg_dma_len(slp));
+ ASC_STATS_ADD(scp->device->host, sg_xfer, ASC_CEILING(sg_dma_len(slp), 512));
+ }
+ }
+
+ ASC_DBG_PRT_ASC_SCSI_Q(2, &asc_scsi_q);
+ ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
+
+ return ASC_NOERROR;
+}
+
+/*
+ * Build a request structure for the Adv Library (Wide Board).
+ *
+ * If an adv_req_t can not be allocated to issue the request,
+ * then return ASC_BUSY. If an error occurs, then return ASC_ERROR.
+ *
+ * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the
+ * microcode for DMA addresses or math operations are byte swapped
+ * to little-endian order.
+ */
+STATIC int
+adv_build_req(asc_board_t *boardp, struct scsi_cmnd *scp,
+ ADV_SCSI_REQ_Q **adv_scsiqpp)
+{
+ adv_req_t *reqp;
+ ADV_SCSI_REQ_Q *scsiqp;
+ int i;
+ int ret;
+ struct device *dev = boardp->dvc_cfg.adv_dvc_cfg.dev;
+
+ /*
+ * Allocate an adv_req_t structure from the board to execute
+ * the command.
+ */
+ if (boardp->adv_reqp == NULL) {
+ ASC_DBG(1, "adv_build_req: no free adv_req_t\n");
+ ASC_STATS(scp->device->host, adv_build_noreq);
+ return ASC_BUSY;
+ } else {
+ reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp->next_reqp;
+ reqp->next_reqp = NULL;
+ }
+
+ /*
+ * Get 32-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers.
+ */
+ scsiqp = (ADV_SCSI_REQ_Q *) ADV_32BALIGN(&reqp->scsi_req_q);
+
+ /*
+ * Initialize the structure.
+ */
+ scsiqp->cntl = scsiqp->scsi_cntl = scsiqp->done_status = 0;
+
+ /*
+ * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure.
+ */
+ scsiqp->srb_ptr = ASC_VADDR_TO_U32(reqp);
+
+ /*
+ * Set the adv_req_t 'cmndp' to point to the struct scsi_cmnd structure.
+ */
+ reqp->cmndp = scp;
+
+ /*
+ * Build the ADV_SCSI_REQ_Q request.
+ */
+
+ /*
+ * Set CDB length and copy it to the request structure.
+ * For wide boards a CDB length maximum of 16 bytes
+ * is supported.
+ */
+ if (scp->cmd_len > ADV_MAX_CDB_LEN) {
+ ASC_PRINT3(
+"adv_build_req: board %d: cmd_len %d > ADV_MAX_CDB_LEN %d\n",
+ boardp->id, scp->cmd_len, ADV_MAX_CDB_LEN);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ return ASC_ERROR;
+ }
+ scsiqp->cdb_len = scp->cmd_len;
+ /* Copy first 12 CDB bytes to cdb[]. */
+ for (i = 0; i < scp->cmd_len && i < 12; i++) {
+ scsiqp->cdb[i] = scp->cmnd[i];
+ }
+ /* Copy last 4 CDB bytes, if present, to cdb16[]. */
+ for (; i < scp->cmd_len; i++) {
+ scsiqp->cdb16[i - 12] = scp->cmnd[i];
+ }
+
+ scsiqp->target_id = scp->device->id;
+ scsiqp->target_lun = scp->device->lun;
+
+ scsiqp->sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0]));
+ scsiqp->sense_len = sizeof(scp->sense_buffer);
+
+ /*
+ * Build ADV_SCSI_REQ_Q for a contiguous buffer or a scatter-gather
+ * buffer command.
+ */
+
+ scsiqp->data_cnt = cpu_to_le32(scp->request_bufflen);
+ scsiqp->vdata_addr = scp->request_buffer;
+ scsiqp->data_addr = cpu_to_le32(virt_to_bus(scp->request_buffer));
+
+ if (scp->use_sg == 0) {
+ /*
+ * CDB request of single contiguous buffer.
+ */
+ reqp->sgblkp = NULL;
+ scsiqp->data_cnt = cpu_to_le32(scp->request_bufflen);
+ if (scp->request_bufflen) {
+ scsiqp->vdata_addr = scp->request_buffer;
+ scp->SCp.dma_handle =
+ dma_map_single(dev, scp->request_buffer,
+ scp->request_bufflen, scp->sc_data_direction);
+ } else {
+ scsiqp->vdata_addr = 0;
+ scp->SCp.dma_handle = 0;
+ }
+ scsiqp->data_addr = cpu_to_le32(scp->SCp.dma_handle);
+ scsiqp->sg_list_ptr = NULL;
+ scsiqp->sg_real_addr = 0;
+ ASC_STATS(scp->device->host, cont_cnt);
+ ASC_STATS_ADD(scp->device->host, cont_xfer,
+ ASC_CEILING(scp->request_bufflen, 512));
+ } else {
+ /*
+ * CDB scatter-gather request list.
+ */
+ struct scatterlist *slp;
+ int use_sg;
+
+ slp = (struct scatterlist *)scp->request_buffer;
+ use_sg = dma_map_sg(dev, slp, scp->use_sg, scp->sc_data_direction);
+
+ if (use_sg > ADV_MAX_SG_LIST) {
+ ASC_PRINT3(
+"adv_build_req: board %d: use_sg %d > ADV_MAX_SG_LIST %d\n",
+ boardp->id, use_sg, scp->device->host->sg_tablesize);
+ dma_unmap_sg(dev, slp, scp->use_sg, scp->sc_data_direction);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+
+ /*
+ * Free the 'adv_req_t' structure by adding it back to the
+ * board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+
+ return ASC_ERROR;
+ }
+
+ if ((ret = adv_get_sglist(boardp, reqp, scp, use_sg)) != ADV_SUCCESS) {
+ /*
+ * Free the adv_req_t structure by adding it back to the
+ * board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+
+ return ret;
+ }
+
+ ASC_STATS(scp->device->host, sg_cnt);
+ ASC_STATS_ADD(scp->device->host, sg_elem, use_sg);
+ }
+
+ ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
+ ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
+
+ *adv_scsiqpp = scsiqp;
+
+ return ASC_NOERROR;
+}
+
+/*
+ * Build scatter-gather list for Adv Library (Wide Board).
+ *
+ * Additional ADV_SG_BLOCK structures will need to be allocated
+ * if the total number of scatter-gather elements exceeds
+ * NO_OF_SG_PER_BLOCK (15). The ADV_SG_BLOCK structures are
+ * assumed to be physically contiguous.
+ *
+ * Return:
+ * ADV_SUCCESS(1) - SG List successfully created
+ * ADV_ERROR(-1) - SG List creation failed
+ */
+STATIC int
+adv_get_sglist(asc_board_t *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, int use_sg)
+{
+ adv_sgblk_t *sgblkp;
+ ADV_SCSI_REQ_Q *scsiqp;
+ struct scatterlist *slp;
+ int sg_elem_cnt;
+ ADV_SG_BLOCK *sg_block, *prev_sg_block;
+ ADV_PADDR sg_block_paddr;
+ int i;
+
+ scsiqp = (ADV_SCSI_REQ_Q *) ADV_32BALIGN(&reqp->scsi_req_q);
+ slp = (struct scatterlist *) scp->request_buffer;
+ sg_elem_cnt = use_sg;
+ prev_sg_block = NULL;
+ reqp->sgblkp = NULL;
+
+ do
+ {
+ /*
+ * Allocate a 'adv_sgblk_t' structure from the board free
+ * list. One 'adv_sgblk_t' structure holds NO_OF_SG_PER_BLOCK
+ * (15) scatter-gather elements.
+ */
+ if ((sgblkp = boardp->adv_sgblkp) == NULL) {
+ ASC_DBG(1, "adv_get_sglist: no free adv_sgblk_t\n");
+ ASC_STATS(scp->device->host, adv_build_nosg);
+
+ /*
+ * Allocation failed. Free 'adv_sgblk_t' structures already
+ * allocated for the request.
+ */
+ while ((sgblkp = reqp->sgblkp) != NULL)
+ {
+ /* Remove 'sgblkp' from the request list. */
+ reqp->sgblkp = sgblkp->next_sgblkp;
+
+ /* Add 'sgblkp' to the board free list. */
+ sgblkp->next_sgblkp = boardp->adv_sgblkp;
+ boardp->adv_sgblkp = sgblkp;
+ }
+ return ASC_BUSY;
+ } else {
+ /* Complete 'adv_sgblk_t' board allocation. */
+ boardp->adv_sgblkp = sgblkp->next_sgblkp;
+ sgblkp->next_sgblkp = NULL;
+
+ /*
+ * Get 8 byte aligned virtual and physical addresses for
+ * the allocated ADV_SG_BLOCK structure.
+ */
+ sg_block = (ADV_SG_BLOCK *) ADV_8BALIGN(&sgblkp->sg_block);
+ sg_block_paddr = virt_to_bus(sg_block);
+
+ /*
+ * Check if this is the first 'adv_sgblk_t' for the request.
+ */
+ if (reqp->sgblkp == NULL)
+ {
+ /* Request's first scatter-gather block. */
+ reqp->sgblkp = sgblkp;
+
+ /*
+ * Set ADV_SCSI_REQ_T ADV_SG_BLOCK virtual and physical
+ * address pointers.
+ */
+ scsiqp->sg_list_ptr = sg_block;
+ scsiqp->sg_real_addr = cpu_to_le32(sg_block_paddr);
+ } else
+ {
+ /* Request's second or later scatter-gather block. */
+ sgblkp->next_sgblkp = reqp->sgblkp;
+ reqp->sgblkp = sgblkp;
+
+ /*
+ * Point the previous ADV_SG_BLOCK structure to
+ * the newly allocated ADV_SG_BLOCK structure.
+ */
+ ASC_ASSERT(prev_sg_block != NULL);
+ prev_sg_block->sg_ptr = cpu_to_le32(sg_block_paddr);
+ }
+ }
+
+ for (i = 0; i < NO_OF_SG_PER_BLOCK; i++)
+ {
+ sg_block->sg_list[i].sg_addr = cpu_to_le32(sg_dma_address(slp));
+ sg_block->sg_list[i].sg_count = cpu_to_le32(sg_dma_len(slp));
+ ASC_STATS_ADD(scp->device->host, sg_xfer, ASC_CEILING(sg_dma_len(slp), 512));
+
+ if (--sg_elem_cnt == 0)
+ { /* Last ADV_SG_BLOCK and scatter-gather entry. */
+ sg_block->sg_cnt = i + 1;
+ sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */
+ return ADV_SUCCESS;
+ }
+ slp++;
+ }
+ sg_block->sg_cnt = NO_OF_SG_PER_BLOCK;
+ prev_sg_block = sg_block;
+ }
+ while (1);
+ /* NOTREACHED */
+}
+
+/*
+ * asc_isr_callback() - Second Level Interrupt Handler called by AscISR().
+ *
+ * Interrupt callback function for the Narrow SCSI Asc Library.
+ */
+STATIC void
+asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
+{
+ asc_board_t *boardp;
+ struct scsi_cmnd *scp;
+ struct Scsi_Host *shp;
+ int i;
+
+ ASC_DBG2(1, "asc_isr_callback: asc_dvc_varp 0x%lx, qdonep 0x%lx\n",
+ (ulong) asc_dvc_varp, (ulong) qdonep);
+ ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep);
+
+ /*
+ * Get the struct scsi_cmnd structure and Scsi_Host structure for the
+ * command that has been completed.
+ */
+ scp = (struct scsi_cmnd *) ASC_U32_TO_VADDR(qdonep->d2.srb_ptr);
+ ASC_DBG1(1, "asc_isr_callback: scp 0x%lx\n", (ulong) scp);
+
+ if (scp == NULL) {
+ ASC_PRINT("asc_isr_callback: scp is NULL\n");
+ return;
+ }
+ ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
+
+ /*
+ * If the request's host pointer is not valid, display a
+ * message and return.
+ */
+ shp = scp->device->host;
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i] == shp) {
+ break;
+ }
+ }
+ if (i == asc_board_count) {
+ ASC_PRINT2(
+ "asc_isr_callback: scp 0x%lx has bad host pointer, host 0x%lx\n",
+ (ulong) scp, (ulong) shp);
+ return;
+ }
+
+ ASC_STATS(shp, callback);
+ ASC_DBG1(1, "asc_isr_callback: shp 0x%lx\n", (ulong) shp);
+
+ /*
+ * If the request isn't found on the active queue, it may
+ * have been removed to handle a reset request.
+ * Display a message and return.
+ */
+ boardp = ASC_BOARDP(shp);
+ ASC_ASSERT(asc_dvc_varp == &boardp->dvc_var.asc_dvc_var);
+ if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
+ ASC_PRINT2(
+ "asc_isr_callback: board %d: scp 0x%lx not on active queue\n",
+ boardp->id, (ulong) scp);
+ return;
+ }
+
+ /*
+ * 'qdonep' contains the command's ending status.
+ */
+ switch (qdonep->d3.done_stat) {
+ case QD_NO_ERROR:
+ ASC_DBG(2, "asc_isr_callback: QD_NO_ERROR\n");
+ scp->result = 0;
+
+ /*
+ * If an INQUIRY command completed successfully, then call
+ * the AscInquiryHandling() function to set-up the device.
+ */
+ if (scp->cmnd[0] == INQUIRY && scp->device->lun == 0 &&
+ (scp->request_bufflen - qdonep->remain_bytes) >= 8)
+ {
+ AscInquiryHandling(asc_dvc_varp, scp->device->id & 0x7,
+ (ASC_SCSI_INQUIRY *) scp->request_buffer);
+ }
+
+ /*
+ * Check for an underrun condition.
+ *
+ * If there was no error and an underrun condition, then
+ * then return the number of underrun bytes.
+ */
+ if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 &&
+ qdonep->remain_bytes <= scp->request_bufflen) {
+ ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n",
+ (unsigned) qdonep->remain_bytes);
+ scp->resid = qdonep->remain_bytes;
+ }
+ break;
+
+ case QD_WITH_ERROR:
+ ASC_DBG(2, "asc_isr_callback: QD_WITH_ERROR\n");
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ if (qdonep->d3.scsi_stat == SAM_STAT_CHECK_CONDITION) {
+ ASC_DBG(2, "asc_isr_callback: SAM_STAT_CHECK_CONDITION\n");
+ ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
+ sizeof(scp->sense_buffer));
+ /*
+ * Note: The 'status_byte()' macro used by target drivers
+ * defined in scsi.h shifts the status byte returned by
+ * host drivers right by 1 bit. This is why target drivers
+ * also use right shifted status byte definitions. For
+ * instance target drivers use CHECK_CONDITION, defined to
+ * 0x1, instead of the SCSI defined check condition value
+ * of 0x2. Host drivers are supposed to return the status
+ * byte as it is defined by SCSI.
+ */
+ scp->result = DRIVER_BYTE(DRIVER_SENSE) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ } else {
+ scp->result = STATUS_BYTE(qdonep->d3.scsi_stat);
+ }
+ break;
+
+ default:
+ /* QHSTA error occurred */
+ ASC_DBG1(1, "asc_isr_callback: host_stat 0x%x\n",
+ qdonep->d3.host_stat);
+ scp->result = HOST_BYTE(DID_BAD_TARGET);
+ break;
+ }
+ break;
+
+ case QD_ABORTED_BY_HOST:
+ ASC_DBG(1, "asc_isr_callback: QD_ABORTED_BY_HOST\n");
+ scp->result = HOST_BYTE(DID_ABORT) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+
+ default:
+ ASC_DBG1(1, "asc_isr_callback: done_stat 0x%x\n", qdonep->d3.done_stat);
+ scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+ }
+
+ /*
+ * If the 'init_tidmask' bit isn't already set for the target and the
+ * current request finished normally, then set the bit for the target
+ * to indicate that a device is present.
+ */
+ if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->device->id)) == 0 &&
+ qdonep->d3.done_stat == QD_NO_ERROR &&
+ qdonep->d3.host_stat == QHSTA_NO_ERROR) {
+ boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->device->id);
+ }
+
+ /*
+ * Because interrupts may be enabled by the 'struct scsi_cmnd' done
+ * function, add the command to the end of the board's done queue.
+ * The done function for the command will be called from
+ * advansys_interrupt().
+ */
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+
+ return;
+}
+
+/*
+ * adv_isr_callback() - Second Level Interrupt Handler called by AdvISR().
+ *
+ * Callback function for the Wide SCSI Adv Library.
+ */
+STATIC void
+adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
+{
+ asc_board_t *boardp;
+ adv_req_t *reqp;
+ adv_sgblk_t *sgblkp;
+ struct scsi_cmnd *scp;
+ struct Scsi_Host *shp;
+ int i;
+ ADV_DCNT resid_cnt;
+
+
+ ASC_DBG2(1, "adv_isr_callback: adv_dvc_varp 0x%lx, scsiqp 0x%lx\n",
+ (ulong) adv_dvc_varp, (ulong) scsiqp);
+ ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
+
+ /*
+ * Get the adv_req_t structure for the command that has been
+ * completed. The adv_req_t structure actually contains the
+ * completed ADV_SCSI_REQ_Q structure.
+ */
+ reqp = (adv_req_t *) ADV_U32_TO_VADDR(scsiqp->srb_ptr);
+ ASC_DBG1(1, "adv_isr_callback: reqp 0x%lx\n", (ulong) reqp);
+ if (reqp == NULL) {
+ ASC_PRINT("adv_isr_callback: reqp is NULL\n");
+ return;
+ }
+
+ /*
+ * Get the struct scsi_cmnd structure and Scsi_Host structure for the
+ * command that has been completed.
+ *
+ * Note: The adv_req_t request structure and adv_sgblk_t structure,
+ * if any, are dropped, because a board structure pointer can not be
+ * determined.
+ */
+ scp = reqp->cmndp;
+ ASC_DBG1(1, "adv_isr_callback: scp 0x%lx\n", (ulong) scp);
+ if (scp == NULL) {
+ ASC_PRINT("adv_isr_callback: scp is NULL; adv_req_t dropped.\n");
+ return;
+ }
+ ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
+
+ /*
+ * If the request's host pointer is not valid, display a message
+ * and return.
+ */
+ shp = scp->device->host;
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i] == shp) {
+ break;
+ }
+ }
+ /*
+ * Note: If the host structure is not found, the adv_req_t request
+ * structure and adv_sgblk_t structure, if any, is dropped.
+ */
+ if (i == asc_board_count) {
+ ASC_PRINT2(
+ "adv_isr_callback: scp 0x%lx has bad host pointer, host 0x%lx\n",
+ (ulong) scp, (ulong) shp);
+ return;
+ }
+
+ ASC_STATS(shp, callback);
+ ASC_DBG1(1, "adv_isr_callback: shp 0x%lx\n", (ulong) shp);
+
+ /*
+ * If the request isn't found on the active queue, it may have been
+ * removed to handle a reset request. Display a message and return.
+ *
+ * Note: Because the structure may still be in use don't attempt
+ * to free the adv_req_t and adv_sgblk_t, if any, structures.
+ */
+ boardp = ASC_BOARDP(shp);
+ ASC_ASSERT(adv_dvc_varp == &boardp->dvc_var.adv_dvc_var);
+ if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
+ ASC_PRINT2(
+ "adv_isr_callback: board %d: scp 0x%lx not on active queue\n",
+ boardp->id, (ulong) scp);
+ return;
+ }
+
+ /*
+ * 'done_status' contains the command's ending status.
+ */
+ switch (scsiqp->done_status) {
+ case QD_NO_ERROR:
+ ASC_DBG(2, "adv_isr_callback: QD_NO_ERROR\n");
+ scp->result = 0;
+
+ /*
+ * Check for an underrun condition.
+ *
+ * If there was no error and an underrun condition, then
+ * then return the number of underrun bytes.
+ */
+ resid_cnt = le32_to_cpu(scsiqp->data_cnt);
+ if (scp->request_bufflen != 0 && resid_cnt != 0 &&
+ resid_cnt <= scp->request_bufflen) {
+ ASC_DBG1(1, "adv_isr_callback: underrun condition %lu bytes\n",
+ (ulong) resid_cnt);
+ scp->resid = resid_cnt;
+ }
+ break;
+
+ case QD_WITH_ERROR:
+ ASC_DBG(2, "adv_isr_callback: QD_WITH_ERROR\n");
+ switch (scsiqp->host_status) {
+ case QHSTA_NO_ERROR:
+ if (scsiqp->scsi_status == SAM_STAT_CHECK_CONDITION) {
+ ASC_DBG(2, "adv_isr_callback: SAM_STAT_CHECK_CONDITION\n");
+ ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
+ sizeof(scp->sense_buffer));
+ /*
+ * Note: The 'status_byte()' macro used by target drivers
+ * defined in scsi.h shifts the status byte returned by
+ * host drivers right by 1 bit. This is why target drivers
+ * also use right shifted status byte definitions. For
+ * instance target drivers use CHECK_CONDITION, defined to
+ * 0x1, instead of the SCSI defined check condition value
+ * of 0x2. Host drivers are supposed to return the status
+ * byte as it is defined by SCSI.
+ */
+ scp->result = DRIVER_BYTE(DRIVER_SENSE) |
+ STATUS_BYTE(scsiqp->scsi_status);
+ } else {
+ scp->result = STATUS_BYTE(scsiqp->scsi_status);
+ }
+ break;
+
+ default:
+ /* Some other QHSTA error occurred. */
+ ASC_DBG1(1, "adv_isr_callback: host_status 0x%x\n",
+ scsiqp->host_status);
+ scp->result = HOST_BYTE(DID_BAD_TARGET);
+ break;
+ }
+ break;
+
+ case QD_ABORTED_BY_HOST:
+ ASC_DBG(1, "adv_isr_callback: QD_ABORTED_BY_HOST\n");
+ scp->result = HOST_BYTE(DID_ABORT) | STATUS_BYTE(scsiqp->scsi_status);
+ break;
+
+ default:
+ ASC_DBG1(1, "adv_isr_callback: done_status 0x%x\n", scsiqp->done_status);
+ scp->result = HOST_BYTE(DID_ERROR) | STATUS_BYTE(scsiqp->scsi_status);
+ break;
+ }
+
+ /*
+ * If the 'init_tidmask' bit isn't already set for the target and the
+ * current request finished normally, then set the bit for the target
+ * to indicate that a device is present.
+ */
+ if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->device->id)) == 0 &&
+ scsiqp->done_status == QD_NO_ERROR &&
+ scsiqp->host_status == QHSTA_NO_ERROR) {
+ boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->device->id);
+ }
+
+ /*
+ * Because interrupts may be enabled by the 'struct scsi_cmnd' done
+ * function, add the command to the end of the board's done queue.
+ * The done function for the command will be called from
+ * advansys_interrupt().
+ */
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+
+ /*
+ * Free all 'adv_sgblk_t' structures allocated for the request.
+ */
+ while ((sgblkp = reqp->sgblkp) != NULL)
+ {
+ /* Remove 'sgblkp' from the request list. */
+ reqp->sgblkp = sgblkp->next_sgblkp;
+
+ /* Add 'sgblkp' to the board free list. */
+ sgblkp->next_sgblkp = boardp->adv_sgblkp;
+ boardp->adv_sgblkp = sgblkp;
+ }
+
+ /*
+ * Free the adv_req_t structure used with the command by adding
+ * it back to the board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+
+ ASC_DBG(1, "adv_isr_callback: done\n");
+
+ return;
+}
+
+/*
+ * adv_async_callback() - Adv Library asynchronous event callback function.
+ */
+STATIC void
+adv_async_callback(ADV_DVC_VAR *adv_dvc_varp, uchar code)
+{
+ switch (code)
+ {
+ case ADV_ASYNC_SCSI_BUS_RESET_DET:
+ /*
+ * The firmware detected a SCSI Bus reset.
+ */
+ ASC_DBG(0, "adv_async_callback: ADV_ASYNC_SCSI_BUS_RESET_DET\n");
+ break;
+
+ case ADV_ASYNC_RDMA_FAILURE:
+ /*
+ * Handle RDMA failure by resetting the SCSI Bus and
+ * possibly the chip if it is unresponsive. Log the error
+ * with a unique code.
+ */
+ ASC_DBG(0, "adv_async_callback: ADV_ASYNC_RDMA_FAILURE\n");
+ AdvResetChipAndSB(adv_dvc_varp);
+ break;
+
+ case ADV_HOST_SCSI_BUS_RESET:
+ /*
+ * Host generated SCSI bus reset occurred.
+ */
+ ASC_DBG(0, "adv_async_callback: ADV_HOST_SCSI_BUS_RESET\n");
+ break;
+
+ default:
+ ASC_DBG1(0, "DvcAsyncCallBack: unknown code 0x%x\n", code);
+ break;
+ }
+}
+
+/*
+ * Add a 'REQP' to the end of specified queue. Set 'tidmask'
+ * to indicate a command is queued for the device.
+ *
+ * 'flag' may be either ASC_FRONT or ASC_BACK.
+ *
+ * 'REQPNEXT(reqp)' returns reqp's next pointer.
+ */
+STATIC void
+asc_enqueue(asc_queue_t *ascq, REQP reqp, int flag)
+{
+ int tid;
+
+ ASC_DBG3(3, "asc_enqueue: ascq 0x%lx, reqp 0x%lx, flag %d\n",
+ (ulong) ascq, (ulong) reqp, flag);
+ ASC_ASSERT(reqp != NULL);
+ ASC_ASSERT(flag == ASC_FRONT || flag == ASC_BACK);
+ tid = REQPTID(reqp);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+ if (flag == ASC_FRONT) {
+ reqp->host_scribble = (unsigned char *)ascq->q_first[tid];
+ ascq->q_first[tid] = reqp;
+ /* If the queue was empty, set the last pointer. */
+ if (ascq->q_last[tid] == NULL) {
+ ascq->q_last[tid] = reqp;
+ }
+ } else { /* ASC_BACK */
+ if (ascq->q_last[tid] != NULL) {
+ ascq->q_last[tid]->host_scribble = (unsigned char *)reqp;
+ }
+ ascq->q_last[tid] = reqp;
+ reqp->host_scribble = NULL;
+ /* If the queue was empty, set the first pointer. */
+ if (ascq->q_first[tid] == NULL) {
+ ascq->q_first[tid] = reqp;
+ }
+ }
+ /* The queue has at least one entry, set its bit. */
+ ascq->q_tidmask |= ADV_TID_TO_TIDMASK(tid);
+#ifdef ADVANSYS_STATS
+ /* Maintain request queue statistics. */
+ ascq->q_tot_cnt[tid]++;
+ ascq->q_cur_cnt[tid]++;
+ if (ascq->q_cur_cnt[tid] > ascq->q_max_cnt[tid]) {
+ ascq->q_max_cnt[tid] = ascq->q_cur_cnt[tid];
+ ASC_DBG2(2, "asc_enqueue: new q_max_cnt[%d] %d\n",
+ tid, ascq->q_max_cnt[tid]);
+ }
+ REQPTIME(reqp) = REQTIMESTAMP();
+#endif /* ADVANSYS_STATS */
+ ASC_DBG1(3, "asc_enqueue: reqp 0x%lx\n", (ulong) reqp);
+ return;
+}
+
+/*
+ * Return first queued 'REQP' on the specified queue for
+ * the specified target device. Clear the 'tidmask' bit for
+ * the device if no more commands are left queued for it.
+ *
+ * 'REQPNEXT(reqp)' returns reqp's next pointer.
+ */
+STATIC REQP
+asc_dequeue(asc_queue_t *ascq, int tid)
+{
+ REQP reqp;
+
+ ASC_DBG2(3, "asc_dequeue: ascq 0x%lx, tid %d\n", (ulong) ascq, tid);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+ if ((reqp = ascq->q_first[tid]) != NULL) {
+ ASC_ASSERT(ascq->q_tidmask & ADV_TID_TO_TIDMASK(tid));
+ ascq->q_first[tid] = REQPNEXT(reqp);
+ /* If the queue is empty, clear its bit and the last pointer. */
+ if (ascq->q_first[tid] == NULL) {
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(tid);
+ ASC_ASSERT(ascq->q_last[tid] == reqp);
+ ascq->q_last[tid] = NULL;
+ }
+#ifdef ADVANSYS_STATS
+ /* Maintain request queue statistics. */
+ ascq->q_cur_cnt[tid]--;
+ ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0);
+ REQTIMESTAT("asc_dequeue", ascq, reqp, tid);
+#endif /* ADVANSYS_STATS */
+ }
+ ASC_DBG1(3, "asc_dequeue: reqp 0x%lx\n", (ulong) reqp);
+ return reqp;
+}
+
+/*
+ * Return a pointer to a singly linked list of all the requests queued
+ * for 'tid' on the 'asc_queue_t' pointed to by 'ascq'.
+ *
+ * If 'lastpp' is not NULL, '*lastpp' will be set to point to the
+ * the last request returned in the singly linked list.
+ *
+ * 'tid' should either be a valid target id or if it is ASC_TID_ALL,
+ * then all queued requests are concatenated into one list and
+ * returned.
+ *
+ * Note: If 'lastpp' is used to append a new list to the end of
+ * an old list, only change the old list last pointer if '*lastpp'
+ * (or the function return value) is not NULL, i.e. use a temporary
+ * variable for 'lastpp' and check its value after the function return
+ * before assigning it to the list last pointer.
+ *
+ * Unfortunately collecting queuing time statistics adds overhead to
+ * the function that isn't inherent to the function's algorithm.
+ */
+STATIC REQP
+asc_dequeue_list(asc_queue_t *ascq, REQP *lastpp, int tid)
+{
+ REQP firstp, lastp;
+ int i;
+
+ ASC_DBG2(3, "asc_dequeue_list: ascq 0x%lx, tid %d\n", (ulong) ascq, tid);
+ ASC_ASSERT((tid == ASC_TID_ALL) || (tid >= 0 && tid <= ADV_MAX_TID));
+
+ /*
+ * If 'tid' is not ASC_TID_ALL, return requests only for
+ * the specified 'tid'. If 'tid' is ASC_TID_ALL, return all
+ * requests for all tids.
+ */
+ if (tid != ASC_TID_ALL) {
+ /* Return all requests for the specified 'tid'. */
+ if ((ascq->q_tidmask & ADV_TID_TO_TIDMASK(tid)) == 0) {
+ /* List is empty; Set first and last return pointers to NULL. */
+ firstp = lastp = NULL;
+ } else {
+ firstp = ascq->q_first[tid];
+ lastp = ascq->q_last[tid];
+ ascq->q_first[tid] = ascq->q_last[tid] = NULL;
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(tid);
+#ifdef ADVANSYS_STATS
+ {
+ REQP reqp;
+ ascq->q_cur_cnt[tid] = 0;
+ for (reqp = firstp; reqp; reqp = REQPNEXT(reqp)) {
+ REQTIMESTAT("asc_dequeue_list", ascq, reqp, tid);
+ }
+ }
+#endif /* ADVANSYS_STATS */
+ }
+ } else {
+ /* Return all requests for all tids. */
+ firstp = lastp = NULL;
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if (ascq->q_tidmask & ADV_TID_TO_TIDMASK(i)) {
+ if (firstp == NULL) {
+ firstp = ascq->q_first[i];
+ lastp = ascq->q_last[i];
+ } else {
+ ASC_ASSERT(lastp != NULL);
+ lastp->host_scribble = (unsigned char *)ascq->q_first[i];
+ lastp = ascq->q_last[i];
+ }
+ ascq->q_first[i] = ascq->q_last[i] = NULL;
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(i);
+#ifdef ADVANSYS_STATS
+ ascq->q_cur_cnt[i] = 0;
+#endif /* ADVANSYS_STATS */
+ }
+ }
+#ifdef ADVANSYS_STATS
+ {
+ REQP reqp;
+ for (reqp = firstp; reqp; reqp = REQPNEXT(reqp)) {
+ REQTIMESTAT("asc_dequeue_list", ascq, reqp, reqp->device->id);
+ }
+ }
+#endif /* ADVANSYS_STATS */
+ }
+ if (lastpp) {
+ *lastpp = lastp;
+ }
+ ASC_DBG1(3, "asc_dequeue_list: firstp 0x%lx\n", (ulong) firstp);
+ return firstp;
+}
+
+/*
+ * Remove the specified 'REQP' from the specified queue for
+ * the specified target device. Clear the 'tidmask' bit for the
+ * device if no more commands are left queued for it.
+ *
+ * 'REQPNEXT(reqp)' returns reqp's the next pointer.
+ *
+ * Return ASC_TRUE if the command was found and removed,
+ * otherwise return ASC_FALSE.
+ */
+STATIC int
+asc_rmqueue(asc_queue_t *ascq, REQP reqp)
+{
+ REQP currp, prevp;
+ int tid;
+ int ret = ASC_FALSE;
+
+ ASC_DBG2(3, "asc_rmqueue: ascq 0x%lx, reqp 0x%lx\n",
+ (ulong) ascq, (ulong) reqp);
+ ASC_ASSERT(reqp != NULL);
+
+ tid = REQPTID(reqp);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+
+ /*
+ * Handle the common case of 'reqp' being the first
+ * entry on the queue.
+ */
+ if (reqp == ascq->q_first[tid]) {
+ ret = ASC_TRUE;
+ ascq->q_first[tid] = REQPNEXT(reqp);
+ /* If the queue is now empty, clear its bit and the last pointer. */
+ if (ascq->q_first[tid] == NULL) {
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(tid);
+ ASC_ASSERT(ascq->q_last[tid] == reqp);
+ ascq->q_last[tid] = NULL;
+ }
+ } else if (ascq->q_first[tid] != NULL) {
+ ASC_ASSERT(ascq->q_last[tid] != NULL);
+ /*
+ * Because the case of 'reqp' being the first entry has been
+ * handled above and it is known the queue is not empty, if
+ * 'reqp' is found on the queue it is guaranteed the queue will
+ * not become empty and that 'q_first[tid]' will not be changed.
+ *
+ * Set 'prevp' to the first entry, 'currp' to the second entry,
+ * and search for 'reqp'.
+ */
+ for (prevp = ascq->q_first[tid], currp = REQPNEXT(prevp);
+ currp; prevp = currp, currp = REQPNEXT(currp)) {
+ if (currp == reqp) {
+ ret = ASC_TRUE;
+ prevp->host_scribble = (unsigned char *)REQPNEXT(currp);
+ reqp->host_scribble = NULL;
+ if (ascq->q_last[tid] == reqp) {
+ ascq->q_last[tid] = prevp;
+ }
+ break;
+ }
+ }
+ }
+#ifdef ADVANSYS_STATS
+ /* Maintain request queue statistics. */
+ if (ret == ASC_TRUE) {
+ ascq->q_cur_cnt[tid]--;
+ REQTIMESTAT("asc_rmqueue", ascq, reqp, tid);
+ }
+ ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0);
+#endif /* ADVANSYS_STATS */
+ ASC_DBG2(3, "asc_rmqueue: reqp 0x%lx, ret %d\n", (ulong) reqp, ret);
+ return ret;
+}
+
+/*
+ * Execute as many queued requests as possible for the specified queue.
+ *
+ * Calls asc_execute_scsi_cmnd() to execute a REQP/struct scsi_cmnd.
+ */
+STATIC void
+asc_execute_queue(asc_queue_t *ascq)
+{
+ ADV_SCSI_BIT_ID_TYPE scan_tidmask;
+ REQP reqp;
+ int i;
+
+ ASC_DBG1(1, "asc_execute_queue: ascq 0x%lx\n", (ulong) ascq);
+ /*
+ * Execute queued commands for devices attached to
+ * the current board in round-robin fashion.
+ */
+ scan_tidmask = ascq->q_tidmask;
+ do {
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if (scan_tidmask & ADV_TID_TO_TIDMASK(i)) {
+ if ((reqp = asc_dequeue(ascq, i)) == NULL) {
+ scan_tidmask &= ~ADV_TID_TO_TIDMASK(i);
+ } else if (asc_execute_scsi_cmnd((struct scsi_cmnd *) reqp)
+ == ASC_BUSY) {
+ scan_tidmask &= ~ADV_TID_TO_TIDMASK(i);
+ /*
+ * The request returned ASC_BUSY. Enqueue at the front of
+ * target's waiting list to maintain correct ordering.
+ */
+ asc_enqueue(ascq, reqp, ASC_FRONT);
+ }
+ }
+ }
+ } while (scan_tidmask);
+ return;
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * asc_prt_board_devices()
+ *
+ * Print driver information for devices attached to the board.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_board_devices(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int chip_scsi_id;
+ int i;
+
+ boardp = ASC_BOARDP(shp);
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nDevice Information for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
+ } else {
+ chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
+ }
+
+ len = asc_prt_line(cp, leftlen, "Target IDs Detected:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if (boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) {
+ len = asc_prt_line(cp, leftlen, " %X,", i);
+ ASC_PRT_NEXT();
+ }
+ }
+ len = asc_prt_line(cp, leftlen, " (%X=Host Adapter)\n", chip_scsi_id);
+ ASC_PRT_NEXT();
+
+ return totlen;
+}
+
+/*
+ * Display Wide Board BIOS Information.
+ */
+STATIC int
+asc_prt_adv_bios(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ ushort major, minor, letter;
+
+ boardp = ASC_BOARDP(shp);
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen, "\nROM BIOS Version: ");
+ ASC_PRT_NEXT();
+
+ /*
+ * If the BIOS saved a valid signature, then fill in
+ * the BIOS code segment base address.
+ */
+ if (boardp->bios_signature != 0x55AA) {
+ len = asc_prt_line(cp, leftlen, "Disabled or Pre-3.1\n");
+ ASC_PRT_NEXT();
+ len = asc_prt_line(cp, leftlen,
+"BIOS either disabled or Pre-3.1. If it is pre-3.1, then a newer version\n");
+ ASC_PRT_NEXT();
+ len = asc_prt_line(cp, leftlen,
+"can be found at the ConnectCom FTP site: ftp://ftp.connectcom.net/pub\n");
+ ASC_PRT_NEXT();
+ } else {
+ major = (boardp->bios_version >> 12) & 0xF;
+ minor = (boardp->bios_version >> 8) & 0xF;
+ letter = (boardp->bios_version & 0xFF);
+
+ len = asc_prt_line(cp, leftlen, "%d.%d%c\n",
+ major, minor, letter >= 26 ? '?' : letter + 'A');
+ ASC_PRT_NEXT();
+
+ /*
+ * Current available ROM BIOS release is 3.1I for UW
+ * and 3.2I for U2W. This code doesn't differentiate
+ * UW and U2W boards.
+ */
+ if (major < 3 || (major <= 3 && minor < 1) ||
+ (major <= 3 && minor <= 1 && letter < ('I'- 'A'))) {
+ len = asc_prt_line(cp, leftlen,
+"Newer version of ROM BIOS is available at the ConnectCom FTP site:\n");
+ ASC_PRT_NEXT();
+ len = asc_prt_line(cp, leftlen,
+"ftp://ftp.connectcom.net/pub\n");
+ ASC_PRT_NEXT();
+ }
+ }
+
+ return totlen;
+}
+
+/*
+ * Add serial number to information bar if signature AAh
+ * is found in at bit 15-9 (7 bits) of word 1.
+ *
+ * Serial Number consists fo 12 alpha-numeric digits.
+ *
+ * 1 - Product type (A,B,C,D..) Word0: 15-13 (3 bits)
+ * 2 - MFG Location (A,B,C,D..) Word0: 12-10 (3 bits)
+ * 3-4 - Product ID (0-99) Word0: 9-0 (10 bits)
+ * 5 - Product revision (A-J) Word0: " "
+ *
+ * Signature Word1: 15-9 (7 bits)
+ * 6 - Year (0-9) Word1: 8-6 (3 bits) & Word2: 15 (1 bit)
+ * 7-8 - Week of the year (1-52) Word1: 5-0 (6 bits)
+ *
+ * 9-12 - Serial Number (A001-Z999) Word2: 14-0 (15 bits)
+ *
+ * Note 1: Only production cards will have a serial number.
+ *
+ * Note 2: Signature is most significant 7 bits (0xFE).
+ *
+ * Returns ASC_TRUE if serial number found, otherwise returns ASC_FALSE.
+ */
+STATIC int
+asc_get_eeprom_string(ushort *serialnum, uchar *cp)
+{
+ ushort w, num;
+
+ if ((serialnum[1] & 0xFE00) != ((ushort) 0xAA << 8)) {
+ return ASC_FALSE;
+ } else {
+ /*
+ * First word - 6 digits.
+ */
+ w = serialnum[0];
+
+ /* Product type - 1st digit. */
+ if ((*cp = 'A' + ((w & 0xE000) >> 13)) == 'H') {
+ /* Product type is P=Prototype */
+ *cp += 0x8;
+ }
+ cp++;
+
+ /* Manufacturing location - 2nd digit. */
+ *cp++ = 'A' + ((w & 0x1C00) >> 10);
+
+ /* Product ID - 3rd, 4th digits. */
+ num = w & 0x3FF;
+ *cp++ = '0' + (num / 100);
+ num %= 100;
+ *cp++ = '0' + (num / 10);
+
+ /* Product revision - 5th digit. */
+ *cp++ = 'A' + (num % 10);
+
+ /*
+ * Second word
+ */
+ w = serialnum[1];
+
+ /*
+ * Year - 6th digit.
+ *
+ * If bit 15 of third word is set, then the
+ * last digit of the year is greater than 7.
+ */
+ if (serialnum[2] & 0x8000) {
+ *cp++ = '8' + ((w & 0x1C0) >> 6);
+ } else {
+ *cp++ = '0' + ((w & 0x1C0) >> 6);
+ }
+
+ /* Week of year - 7th, 8th digits. */
+ num = w & 0x003F;
+ *cp++ = '0' + num / 10;
+ num %= 10;
+ *cp++ = '0' + num;
+
+ /*
+ * Third word
+ */
+ w = serialnum[2] & 0x7FFF;
+
+ /* Serial number - 9th digit. */
+ *cp++ = 'A' + (w / 1000);
+
+ /* 10th, 11th, 12th digits. */
+ num = w % 1000;
+ *cp++ = '0' + num / 100;
+ num %= 100;
+ *cp++ = '0' + num / 10;
+ num %= 10;
+ *cp++ = '0' + num;
+
+ *cp = '\0'; /* Null Terminate the string. */
+ return ASC_TRUE;
+ }
+}
+
+/*
+ * asc_prt_asc_board_eeprom()
+ *
+ * Print board EEPROM configuration.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_asc_board_eeprom(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ int leftlen;
+ int totlen;
+ int len;
+ ASCEEP_CONFIG *ep;
+ int i;
+#ifdef CONFIG_ISA
+ int isa_dma_speed[] = { 10, 8, 7, 6, 5, 4, 3, 2 };
+#endif /* CONFIG_ISA */
+ uchar serialstr[13];
+
+ boardp = ASC_BOARDP(shp);
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ ep = &boardp->eep_config.asc_eep;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nEEPROM Settings for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ if (asc_get_eeprom_string((ushort *) &ep->adapter_info[0], serialstr) ==
+ ASC_TRUE) {
+ len = asc_prt_line(cp, leftlen, " Serial Number: %s\n", serialstr);
+ ASC_PRT_NEXT();
+ } else {
+ if (ep->adapter_info[5] == 0xBB) {
+ len = asc_prt_line(cp, leftlen,
+ " Default Settings Used for EEPROM-less Adapter.\n");
+ ASC_PRT_NEXT();
+ } else {
+ len = asc_prt_line(cp, leftlen,
+ " Serial Number Signature Not Present.\n");
+ ASC_PRT_NEXT();
+ }
+ }
+
+ len = asc_prt_line(cp, leftlen,
+" Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ASC_EEP_GET_CHIP_ID(ep), ep->max_total_qng, ep->max_tag_qng);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" cntl 0x%x, no_scam 0x%x\n",
+ ep->cntl, ep->no_scam);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Target ID: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %d", i);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Disconnects: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->disc_enable & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Queuing: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->use_cmd_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Start Motor: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->start_motor & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->init_sdtr & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+#ifdef CONFIG_ISA
+ if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
+ len = asc_prt_line(cp, leftlen,
+" Host ISA DMA speed: %d MB/S\n",
+ isa_dma_speed[ASC_EEP_GET_DMA_SPD(ep)]);
+ ASC_PRT_NEXT();
+ }
+#endif /* CONFIG_ISA */
+
+ return totlen;
+}
+
+/*
+ * asc_prt_adv_board_eeprom()
+ *
+ * Print board EEPROM configuration.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_adv_board_eeprom(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ int leftlen;
+ int totlen;
+ int len;
+ int i;
+ char *termstr;
+ uchar serialstr[13];
+ ADVEEP_3550_CONFIG *ep_3550 = NULL;
+ ADVEEP_38C0800_CONFIG *ep_38C0800 = NULL;
+ ADVEEP_38C1600_CONFIG *ep_38C1600 = NULL;
+ ushort word;
+ ushort *wordp;
+ ushort sdtr_speed = 0;
+
+ boardp = ASC_BOARDP(shp);
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ ep_3550 = &boardp->eep_config.adv_3550_eep;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ ep_38C0800 = &boardp->eep_config.adv_38C0800_eep;
+ } else
+ {
+ ep_38C1600 = &boardp->eep_config.adv_38C1600_eep;
+ }
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nEEPROM Settings for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ wordp = &ep_3550->serial_number_word1;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ wordp = &ep_38C0800->serial_number_word1;
+ } else
+ {
+ wordp = &ep_38C1600->serial_number_word1;
+ }
+
+ if (asc_get_eeprom_string(wordp, serialstr) == ASC_TRUE) {
+ len = asc_prt_line(cp, leftlen, " Serial Number: %s\n", serialstr);
+ ASC_PRT_NEXT();
+ } else {
+ len = asc_prt_line(cp, leftlen,
+ " Serial Number Signature Not Present.\n");
+ ASC_PRT_NEXT();
+ }
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ len = asc_prt_line(cp, leftlen,
+" Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep_3550->adapter_scsi_id, ep_3550->max_host_qng,
+ ep_3550->max_dvc_qng);
+ ASC_PRT_NEXT();
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ len = asc_prt_line(cp, leftlen,
+" Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep_38C0800->adapter_scsi_id, ep_38C0800->max_host_qng,
+ ep_38C0800->max_dvc_qng);
+ ASC_PRT_NEXT();
+ } else
+ {
+ len = asc_prt_line(cp, leftlen,
+" Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep_38C1600->adapter_scsi_id, ep_38C1600->max_host_qng,
+ ep_38C1600->max_dvc_qng);
+ ASC_PRT_NEXT();
+ }
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ word = ep_3550->termination;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ word = ep_38C0800->termination_lvd;
+ } else
+ {
+ word = ep_38C1600->termination_lvd;
+ }
+ switch (word) {
+ case 1:
+ termstr = "Low Off/High Off";
+ break;
+ case 2:
+ termstr = "Low Off/High On";
+ break;
+ case 3:
+ termstr = "Low On/High On";
+ break;
+ default:
+ case 0:
+ termstr = "Automatic";
+ break;
+ }
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ len = asc_prt_line(cp, leftlen,
+" termination: %u (%s), bios_ctrl: 0x%x\n",
+ ep_3550->termination, termstr, ep_3550->bios_ctrl);
+ ASC_PRT_NEXT();
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ len = asc_prt_line(cp, leftlen,
+" termination: %u (%s), bios_ctrl: 0x%x\n",
+ ep_38C0800->termination_lvd, termstr, ep_38C0800->bios_ctrl);
+ ASC_PRT_NEXT();
+ } else
+ {
+ len = asc_prt_line(cp, leftlen,
+" termination: %u (%s), bios_ctrl: 0x%x\n",
+ ep_38C1600->termination_lvd, termstr, ep_38C1600->bios_ctrl);
+ ASC_PRT_NEXT();
+ }
+
+ len = asc_prt_line(cp, leftlen,
+" Target ID: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %X", i);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ word = ep_3550->disc_enable;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ word = ep_38C0800->disc_enable;
+ } else
+ {
+ word = ep_38C1600->disc_enable;
+ }
+ len = asc_prt_line(cp, leftlen,
+" Disconnects: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ word = ep_3550->tagqng_able;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ word = ep_38C0800->tagqng_able;
+ } else
+ {
+ word = ep_38C1600->tagqng_able;
+ }
+ len = asc_prt_line(cp, leftlen,
+" Command Queuing: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ word = ep_3550->start_motor;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ word = ep_38C0800->start_motor;
+ } else
+ {
+ word = ep_38C1600->start_motor;
+ }
+ len = asc_prt_line(cp, leftlen,
+" Start Motor: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep_3550->sdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+ }
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ len = asc_prt_line(cp, leftlen,
+" Ultra Transfer: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep_3550->ultra_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+ }
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC3550)
+ {
+ word = ep_3550->wdtr_able;
+ } else if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ word = ep_38C0800->wdtr_able;
+ } else
+ {
+ word = ep_38C1600->wdtr_able;
+ }
+ len = asc_prt_line(cp, leftlen,
+" Wide Transfer: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (word & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ if (adv_dvc_varp->chip_type == ADV_CHIP_ASC38C0800 ||
+ adv_dvc_varp->chip_type == ADV_CHIP_ASC38C1600)
+ {
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer Speed (Mhz):\n ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ char *speed_str;
+
+ if (i == 0)
+ {
+ sdtr_speed = adv_dvc_varp->sdtr_speed1;
+ } else if (i == 4)
+ {
+ sdtr_speed = adv_dvc_varp->sdtr_speed2;
+ } else if (i == 8)
+ {
+ sdtr_speed = adv_dvc_varp->sdtr_speed3;
+ } else if (i == 12)
+ {
+ sdtr_speed = adv_dvc_varp->sdtr_speed4;
+ }
+ switch (sdtr_speed & ADV_MAX_TID)
+ {
+ case 0: speed_str = "Off"; break;
+ case 1: speed_str = " 5"; break;
+ case 2: speed_str = " 10"; break;
+ case 3: speed_str = " 20"; break;
+ case 4: speed_str = " 40"; break;
+ case 5: speed_str = " 80"; break;
+ default: speed_str = "Unk"; break;
+ }
+ len = asc_prt_line(cp, leftlen, "%X:%s ", i, speed_str);
+ ASC_PRT_NEXT();
+ if (i == 7)
+ {
+ len = asc_prt_line(cp, leftlen, "\n ");
+ ASC_PRT_NEXT();
+ }
+ sdtr_speed >>= 4;
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_prt_driver_conf()
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_driver_conf(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int chip_scsi_id;
+
+ boardp = ASC_BOARDP(shp);
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nLinux Driver Configuration and Information for AdvanSys SCSI Host %d:\n",
+ shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" host_busy %u, last_reset %u, max_id %u, max_lun %u, max_channel %u\n",
+ shp->host_busy, shp->last_reset, shp->max_id, shp->max_lun,
+ shp->max_channel);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" unique_id %d, can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n",
+ shp->unique_id, shp->can_queue, shp->this_id, shp->sg_tablesize,
+ shp->cmd_per_lun);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" unchecked_isa_dma %d, use_clustering %d\n",
+ shp->unchecked_isa_dma, shp->use_clustering);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" flags 0x%x, last_reset 0x%x, jiffies 0x%x, asc_n_io_port 0x%x\n",
+ boardp->flags, boardp->last_reset, jiffies, boardp->asc_n_io_port);
+ ASC_PRT_NEXT();
+
+ /* 'shp->n_io_port' may be truncated because it is only one byte. */
+ len = asc_prt_line(cp, leftlen,
+" io_port 0x%x, n_io_port 0x%x\n",
+ shp->io_port, shp->n_io_port);
+ ASC_PRT_NEXT();
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
+ } else {
+ chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_prt_asc_board_info()
+ *
+ * Print dynamic board configuration information.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_asc_board_info(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int chip_scsi_id;
+ int leftlen;
+ int totlen;
+ int len;
+ ASC_DVC_VAR *v;
+ ASC_DVC_CFG *c;
+ int i;
+ int renegotiate = 0;
+
+ boardp = ASC_BOARDP(shp);
+ v = &boardp->dvc_var.asc_dvc_var;
+ c = &boardp->dvc_cfg.asc_dvc_cfg;
+ chip_scsi_id = c->chip_scsi_id;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nAsc Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
+ shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" chip_version %u, lib_version 0x%x, lib_serial_no %u, mcode_date 0x%x\n",
+ c->chip_version, c->lib_version, c->lib_serial_no, c->mcode_date);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" mcode_version 0x%x, err_code %u\n",
+ c->mcode_version, v->err_code);
+ ASC_PRT_NEXT();
+
+ /* Current number of commands waiting for the host. */
+ len = asc_prt_line(cp, leftlen,
+" Total Command Pending: %d\n", v->cur_total_qng);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Queuing:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (v->use_tagged_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ /* Current number of commands waiting for a device. */
+ len = asc_prt_line(cp, leftlen,
+" Command Queue Pending:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %X:%u", i, v->cur_dvc_qng[i]);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ /* Current limit on number of commands that can be sent to a device. */
+ len = asc_prt_line(cp, leftlen,
+" Command Queue Limit:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %X:%u", i, v->max_dvc_qng[i]);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ /* Indicate whether the device has returned queue full status. */
+ len = asc_prt_line(cp, leftlen,
+" Command Queue Full:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ if (boardp->queue_full & ADV_TID_TO_TIDMASK(i)) {
+ len = asc_prt_line(cp, leftlen, " %X:Y-%d",
+ i, boardp->queue_full_cnt[i]);
+ } else {
+ len = asc_prt_line(cp, leftlen, " %X:N", i);
+ }
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (v->sdtr_done & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ uchar syn_period_ix;
+
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0) ||
+ ((v->init_sdtr & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:", i);
+ ASC_PRT_NEXT();
+
+ if ((boardp->sdtr_data[i] & ASC_SYN_MAX_OFFSET) == 0)
+ {
+ len = asc_prt_line(cp, leftlen, " Asynchronous");
+ ASC_PRT_NEXT();
+ } else
+ {
+ syn_period_ix =
+ (boardp->sdtr_data[i] >> 4) & (v->max_sdtr_index - 1);
+
+ len = asc_prt_line(cp, leftlen,
+ " Transfer Period Factor: %d (%d.%d Mhz),",
+ v->sdtr_period_tbl[syn_period_ix],
+ 250 / v->sdtr_period_tbl[syn_period_ix],
+ ASC_TENTHS(250, v->sdtr_period_tbl[syn_period_ix]));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, " REQ/ACK Offset: %d",
+ boardp->sdtr_data[i] & ASC_SYN_MAX_OFFSET);
+ ASC_PRT_NEXT();
+ }
+
+ if ((v->sdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
+ len = asc_prt_line(cp, leftlen, "*\n");
+ renegotiate = 1;
+ } else
+ {
+ len = asc_prt_line(cp, leftlen, "\n");
+ }
+ ASC_PRT_NEXT();
+ }
+
+ if (renegotiate)
+ {
+ len = asc_prt_line(cp, leftlen,
+ " * = Re-negotiation pending before next command.\n");
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_prt_adv_board_info()
+ *
+ * Print dynamic board configuration information.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_adv_board_info(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int i;
+ ADV_DVC_VAR *v;
+ ADV_DVC_CFG *c;
+ AdvPortAddr iop_base;
+ ushort chip_scsi_id;
+ ushort lramword;
+ uchar lrambyte;
+ ushort tagqng_able;
+ ushort sdtr_able, wdtr_able;
+ ushort wdtr_done, sdtr_done;
+ ushort period = 0;
+ int renegotiate = 0;
+
+ boardp = ASC_BOARDP(shp);
+ v = &boardp->dvc_var.adv_dvc_var;
+ c = &boardp->dvc_cfg.adv_dvc_cfg;
+ iop_base = v->iop_base;
+ chip_scsi_id = v->chip_scsi_id;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nAdv Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
+ shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" iop_base 0x%lx, cable_detect: %X, err_code %u\n",
+ v->iop_base,
+ AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1) & CABLE_DETECT,
+ v->err_code);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" chip_version %u, lib_version 0x%x, mcode_date 0x%x, mcode_version 0x%x\n",
+ c->chip_version, c->lib_version, c->mcode_date, c->mcode_version);
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ len = asc_prt_line(cp, leftlen,
+" Queuing Enabled:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (tagqng_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Queue Limit:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + i, lrambyte);
+
+ len = asc_prt_line(cp, leftlen, " %X:%d", i, lrambyte);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Pending:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_QUEUED_CMD + i, lrambyte);
+
+ len = asc_prt_line(cp, leftlen, " %X:%d", i, lrambyte);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ len = asc_prt_line(cp, leftlen,
+" Wide Enabled:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (wdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_DONE, wdtr_done);
+ len = asc_prt_line(cp, leftlen,
+" Transfer Bit Width:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ AdvReadWordLram(iop_base, ASC_MC_DEVICE_HSHK_CFG_TABLE + (2 * i),
+ lramword);
+
+ len = asc_prt_line(cp, leftlen, " %X:%d",
+ i, (lramword & 0x8000) ? 16 : 8);
+ ASC_PRT_NEXT();
+
+ if ((wdtr_able & ADV_TID_TO_TIDMASK(i)) &&
+ (wdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
+ len = asc_prt_line(cp, leftlen, "*");
+ ASC_PRT_NEXT();
+ renegotiate = 1;
+ }
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Enabled:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (sdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_DONE, sdtr_done);
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+
+ AdvReadWordLram(iop_base, ASC_MC_DEVICE_HSHK_CFG_TABLE + (2 * i),
+ lramword);
+ lramword &= ~0x8000;
+
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0) ||
+ ((sdtr_able & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:", i);
+ ASC_PRT_NEXT();
+
+ if ((lramword & 0x1F) == 0) /* Check for REQ/ACK Offset 0. */
+ {
+ len = asc_prt_line(cp, leftlen, " Asynchronous");
+ ASC_PRT_NEXT();
+ } else
+ {
+ len = asc_prt_line(cp, leftlen, " Transfer Period Factor: ");
+ ASC_PRT_NEXT();
+
+ if ((lramword & 0x1F00) == 0x1100) /* 80 Mhz */
+ {
+ len = asc_prt_line(cp, leftlen, "9 (80.0 Mhz),");
+ ASC_PRT_NEXT();
+ } else if ((lramword & 0x1F00) == 0x1000) /* 40 Mhz */
+ {
+ len = asc_prt_line(cp, leftlen, "10 (40.0 Mhz),");
+ ASC_PRT_NEXT();
+ } else /* 20 Mhz or below. */
+ {
+ period = (((lramword >> 8) * 25) + 50)/4;
+
+ if (period == 0) /* Should never happen. */
+ {
+ len = asc_prt_line(cp, leftlen, "%d (? Mhz), ");
+ ASC_PRT_NEXT();
+ } else
+ {
+ len = asc_prt_line(cp, leftlen,
+ "%d (%d.%d Mhz),",
+ period, 250/period, ASC_TENTHS(250, period));
+ ASC_PRT_NEXT();
+ }
+ }
+
+ len = asc_prt_line(cp, leftlen, " REQ/ACK Offset: %d",
+ lramword & 0x1F);
+ ASC_PRT_NEXT();
+ }
+
+ if ((sdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
+ len = asc_prt_line(cp, leftlen, "*\n");
+ renegotiate = 1;
+ } else
+ {
+ len = asc_prt_line(cp, leftlen, "\n");
+ }
+ ASC_PRT_NEXT();
+ }
+
+ if (renegotiate)
+ {
+ len = asc_prt_line(cp, leftlen,
+ " * = Re-negotiation pending before next command.\n");
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_proc_copy()
+ *
+ * Copy proc information to a read buffer taking into account the current
+ * read offset in the file and the remaining space in the read buffer.
+ */
+STATIC int
+asc_proc_copy(off_t advoffset, off_t offset, char *curbuf, int leftlen,
+ char *cp, int cplen)
+{
+ int cnt = 0;
+
+ ASC_DBG3(2, "asc_proc_copy: offset %d, advoffset %d, cplen %d\n",
+ (unsigned) offset, (unsigned) advoffset, cplen);
+ if (offset <= advoffset) {
+ /* Read offset below current offset, copy everything. */
+ cnt = min(cplen, leftlen);
+ ASC_DBG3(2, "asc_proc_copy: curbuf 0x%lx, cp 0x%lx, cnt %d\n",
+ (ulong) curbuf, (ulong) cp, cnt);
+ memcpy(curbuf, cp, cnt);
+ } else if (offset < advoffset + cplen) {
+ /* Read offset within current range, partial copy. */
+ cnt = (advoffset + cplen) - offset;
+ cp = (cp + cplen) - cnt;
+ cnt = min(cnt, leftlen);
+ ASC_DBG3(2, "asc_proc_copy: curbuf 0x%lx, cp 0x%lx, cnt %d\n",
+ (ulong) curbuf, (ulong) cp, cnt);
+ memcpy(curbuf, cp, cnt);
+ }
+ return cnt;
+}
+
+/*
+ * asc_prt_line()
+ *
+ * If 'cp' is NULL print to the console, otherwise print to a buffer.
+ *
+ * Return 0 if printing to the console, otherwise return the number of
+ * bytes written to the buffer.
+ *
+ * Note: If any single line is greater than ASC_PRTLINE_SIZE bytes the stack
+ * will be corrupted. 's[]' is defined to be ASC_PRTLINE_SIZE bytes.
+ */
+STATIC int
+asc_prt_line(char *buf, int buflen, char *fmt, ...)
+{
+ va_list args;
+ int ret;
+ char s[ASC_PRTLINE_SIZE];
+
+ va_start(args, fmt);
+ ret = vsprintf(s, fmt, args);
+ ASC_ASSERT(ret < ASC_PRTLINE_SIZE);
+ if (buf == NULL) {
+ (void) printk(s);
+ ret = 0;
+ } else {
+ ret = min(buflen, ret);
+ memcpy(buf, s, ret);
+ }
+ va_end(args);
+ return ret;
+}
+#endif /* CONFIG_PROC_FS */
+
+
+/*
+ * --- Functions Required by the Asc Library
+ */
+
+/*
+ * Delay for 'n' milliseconds. Don't use the 'jiffies'
+ * global variable which is incremented once every 5 ms
+ * from a timer interrupt, because this function may be
+ * called when interrupts are disabled.
+ */
+STATIC void
+DvcSleepMilliSecond(ADV_DCNT n)
+{
+ ASC_DBG1(4, "DvcSleepMilliSecond: %lu\n", (ulong) n);
+ mdelay(n);
+}
+
+/*
+ * Currently and inline noop but leave as a placeholder.
+ * Leave DvcEnterCritical() as a noop placeholder.
+ */
+STATIC inline ulong
+DvcEnterCritical(void)
+{
+ return 0;
+}
+
+/*
+ * Critical sections are all protected by the board spinlock.
+ * Leave DvcLeaveCritical() as a noop placeholder.
+ */
+STATIC inline void
+DvcLeaveCritical(ulong flags)
+{
+ return;
+}
+
+/*
+ * void
+ * DvcPutScsiQ(PortAddr iop_base, ushort s_addr, uchar *outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * Output an ASC_SCSI_Q structure to the chip
+ */
+STATIC void
+DvcPutScsiQ(PortAddr iop_base, ushort s_addr, uchar *outbuf, int words)
+{
+ int i;
+
+ ASC_DBG_PRT_HEX(2, "DvcPutScsiQ", outbuf, 2 * words);
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < 2 * words; i += 2) {
+ if (i == 4 || i == 20) {
+ continue;
+ }
+ outpw(iop_base + IOP_RAM_DATA,
+ ((ushort) outbuf[i + 1] << 8) | outbuf[i]);
+ }
+}
+
+/*
+ * void
+ * DvcGetQinfo(PortAddr iop_base, ushort s_addr, uchar *inbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * Input an ASC_QDONE_INFO structure from the chip
+ */
+STATIC void
+DvcGetQinfo(PortAddr iop_base, ushort s_addr, uchar *inbuf, int words)
+{
+ int i;
+ ushort word;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < 2 * words; i += 2) {
+ if (i == 10) {
+ continue;
+ }
+ word = inpw(iop_base + IOP_RAM_DATA);
+ inbuf[i] = word & 0xff;
+ inbuf[i + 1] = (word >> 8) & 0xff;
+ }
+ ASC_DBG_PRT_HEX(2, "DvcGetQinfo", inbuf, 2 * words);
+}
+
+/*
+ * Read a PCI configuration byte.
+ */
+STATIC uchar __init
+DvcReadPCIConfigByte(
+ ASC_DVC_VAR *asc_dvc,
+ ushort offset)
+{
+#ifdef CONFIG_PCI
+ uchar byte_data;
+ pci_read_config_byte(to_pci_dev(asc_dvc->cfg->dev), offset, &byte_data);
+ return byte_data;
+#else /* !defined(CONFIG_PCI) */
+ return 0;
+#endif /* !defined(CONFIG_PCI) */
+}
+
+/*
+ * Write a PCI configuration byte.
+ */
+STATIC void __init
+DvcWritePCIConfigByte(
+ ASC_DVC_VAR *asc_dvc,
+ ushort offset,
+ uchar byte_data)
+{
+#ifdef CONFIG_PCI
+ pci_write_config_byte(to_pci_dev(asc_dvc->cfg->dev), offset, byte_data);
+#endif /* CONFIG_PCI */
+}
+
+/*
+ * Return the BIOS address of the adapter at the specified
+ * I/O port and with the specified bus type.
+ */
+STATIC ushort __init
+AscGetChipBiosAddress(
+ PortAddr iop_base,
+ ushort bus_type)
+{
+ ushort cfg_lsw;
+ ushort bios_addr;
+
+ /*
+ * The PCI BIOS is re-located by the motherboard BIOS. Because
+ * of this the driver can not determine where a PCI BIOS is
+ * loaded and executes.
+ */
+ if (bus_type & ASC_IS_PCI)
+ {
+ return(0);
+ }
+
+#ifdef CONFIG_ISA
+ if((bus_type & ASC_IS_EISA) != 0)
+ {
+ cfg_lsw = AscGetEisaChipCfg(iop_base);
+ cfg_lsw &= 0x000F;
+ bios_addr = (ushort)(ASC_BIOS_MIN_ADDR +
+ (cfg_lsw * ASC_BIOS_BANK_SIZE));
+ return(bios_addr);
+ }/* if */
+#endif /* CONFIG_ISA */
+
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+
+ /*
+ * ISA PnP uses the top bit as the 32K BIOS flag
+ */
+ if (bus_type == ASC_IS_ISAPNP)
+ {
+ cfg_lsw &= 0x7FFF;
+ }/* if */
+
+ bios_addr = (ushort)(((cfg_lsw >> 12) * ASC_BIOS_BANK_SIZE) +
+ ASC_BIOS_MIN_ADDR);
+ return(bios_addr);
+}
+
+
+/*
+ * --- Functions Required by the Adv Library
+ */
+
+/*
+ * DvcGetPhyAddr()
+ *
+ * Return the physical address of 'vaddr' and set '*lenp' to the
+ * number of physically contiguous bytes that follow 'vaddr'.
+ * 'flag' indicates the type of structure whose physical address
+ * is being translated.
+ *
+ * Note: Because Linux currently doesn't page the kernel and all
+ * kernel buffers are physically contiguous, leave '*lenp' unchanged.
+ */
+ADV_PADDR
+DvcGetPhyAddr(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq,
+ uchar *vaddr, ADV_SDCNT *lenp, int flag)
+{
+ ADV_PADDR paddr;
+
+ paddr = virt_to_bus(vaddr);
+
+ ASC_DBG4(4,
+ "DvcGetPhyAddr: vaddr 0x%lx, lenp 0x%lx *lenp %lu, paddr 0x%lx\n",
+ (ulong) vaddr, (ulong) lenp, (ulong) *((ulong *) lenp), (ulong) paddr);
+
+ return paddr;
+}
+
+/*
+ * Read a PCI configuration byte.
+ */
+STATIC uchar __init
+DvcAdvReadPCIConfigByte(
+ ADV_DVC_VAR *asc_dvc,
+ ushort offset)
+{
+#ifdef CONFIG_PCI
+ uchar byte_data;
+ pci_read_config_byte(to_pci_dev(asc_dvc->cfg->dev), offset, &byte_data);
+ return byte_data;
+#else /* CONFIG_PCI */
+ return 0;
+#endif /* CONFIG_PCI */
+}
+
+/*
+ * Write a PCI configuration byte.
+ */
+STATIC void __init
+DvcAdvWritePCIConfigByte(
+ ADV_DVC_VAR *asc_dvc,
+ ushort offset,
+ uchar byte_data)
+{
+#ifdef CONFIG_PCI
+ pci_write_config_byte(to_pci_dev(asc_dvc->cfg->dev), offset, byte_data);
+#else /* CONFIG_PCI */
+ return;
+#endif /* CONFIG_PCI */
+}
+
+/*
+ * --- Tracing and Debugging Functions
+ */
+
+#ifdef ADVANSYS_STATS
+#ifdef CONFIG_PROC_FS
+/*
+ * asc_prt_board_stats()
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_board_stats(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ int leftlen;
+ int totlen;
+ int len;
+ struct asc_stats *s;
+ asc_board_t *boardp;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ boardp = ASC_BOARDP(shp);
+ s = &boardp->asc_stats;
+
+ len = asc_prt_line(cp, leftlen,
+"\nLinux Driver Statistics for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" queuecommand %lu, reset %lu, biosparam %lu, interrupt %lu\n",
+ s->queuecommand, s->reset, s->biosparam, s->interrupt);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" callback %lu, done %lu, build_error %lu, build_noreq %lu, build_nosg %lu\n",
+ s->callback, s->done, s->build_error, s->adv_build_noreq,
+ s->adv_build_nosg);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" exe_noerror %lu, exe_busy %lu, exe_error %lu, exe_unknown %lu\n",
+ s->exe_noerror, s->exe_busy, s->exe_error, s->exe_unknown);
+ ASC_PRT_NEXT();
+
+ /*
+ * Display data transfer statistics.
+ */
+ if (s->cont_cnt > 0) {
+ len = asc_prt_line(cp, leftlen, " cont_cnt %lu, ", s->cont_cnt);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "cont_xfer %lu.%01lu kb ",
+ s->cont_xfer/2,
+ ASC_TENTHS(s->cont_xfer, 2));
+ ASC_PRT_NEXT();
+
+ /* Contiguous transfer average size */
+ len = asc_prt_line(cp, leftlen, "avg_xfer %lu.%01lu kb\n",
+ (s->cont_xfer/2)/s->cont_cnt,
+ ASC_TENTHS((s->cont_xfer/2), s->cont_cnt));
+ ASC_PRT_NEXT();
+ }
+
+ if (s->sg_cnt > 0) {
+
+ len = asc_prt_line(cp, leftlen, " sg_cnt %lu, sg_elem %lu, ",
+ s->sg_cnt, s->sg_elem);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "sg_xfer %lu.%01lu kb\n",
+ s->sg_xfer/2,
+ ASC_TENTHS(s->sg_xfer, 2));
+ ASC_PRT_NEXT();
+
+ /* Scatter gather transfer statistics */
+ len = asc_prt_line(cp, leftlen, " avg_num_elem %lu.%01lu, ",
+ s->sg_elem/s->sg_cnt,
+ ASC_TENTHS(s->sg_elem, s->sg_cnt));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "avg_elem_size %lu.%01lu kb, ",
+ (s->sg_xfer/2)/s->sg_elem,
+ ASC_TENTHS((s->sg_xfer/2), s->sg_elem));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "avg_xfer_size %lu.%01lu kb\n",
+ (s->sg_xfer/2)/s->sg_cnt,
+ ASC_TENTHS((s->sg_xfer/2), s->sg_cnt));
+ ASC_PRT_NEXT();
+ }
+
+ /*
+ * Display request queuing statistics.
+ */
+ len = asc_prt_line(cp, leftlen,
+" Active and Waiting Request Queues (Time Unit: %d HZ):\n", HZ);
+ ASC_PRT_NEXT();
+
+
+ return totlen;
+}
+
+/*
+ * asc_prt_target_stats()
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * This is separated from asc_prt_board_stats because a full set
+ * of targets will overflow ASC_PRTBUF_SIZE.
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_target_stats(struct Scsi_Host *shp, int tgt_id, char *cp, int cplen)
+{
+ int leftlen;
+ int totlen;
+ int len;
+ struct asc_stats *s;
+ ushort chip_scsi_id;
+ asc_board_t *boardp;
+ asc_queue_t *active;
+ asc_queue_t *waiting;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ boardp = ASC_BOARDP(shp);
+ s = &boardp->asc_stats;
+
+ active = &ASC_BOARDP(shp)->active;
+ waiting = &ASC_BOARDP(shp)->waiting;
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
+ } else {
+ chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
+ }
+
+ if ((chip_scsi_id == tgt_id) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(tgt_id)) == 0)) {
+ return 0;
+ }
+
+ do {
+ if (active->q_tot_cnt[tgt_id] > 0 || waiting->q_tot_cnt[tgt_id] > 0) {
+ len = asc_prt_line(cp, leftlen, " target %d\n", tgt_id);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" active: cnt [cur %d, max %d, tot %u], time [min %d, max %d, avg %lu.%01lu]\n",
+ active->q_cur_cnt[tgt_id], active->q_max_cnt[tgt_id],
+ active->q_tot_cnt[tgt_id],
+ active->q_min_tim[tgt_id], active->q_max_tim[tgt_id],
+ (active->q_tot_cnt[tgt_id] == 0) ? 0 :
+ (active->q_tot_tim[tgt_id]/active->q_tot_cnt[tgt_id]),
+ (active->q_tot_cnt[tgt_id] == 0) ? 0 :
+ ASC_TENTHS(active->q_tot_tim[tgt_id],
+ active->q_tot_cnt[tgt_id]));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" waiting: cnt [cur %d, max %d, tot %u], time [min %u, max %u, avg %lu.%01lu]\n",
+ waiting->q_cur_cnt[tgt_id], waiting->q_max_cnt[tgt_id],
+ waiting->q_tot_cnt[tgt_id],
+ waiting->q_min_tim[tgt_id], waiting->q_max_tim[tgt_id],
+ (waiting->q_tot_cnt[tgt_id] == 0) ? 0 :
+ (waiting->q_tot_tim[tgt_id]/waiting->q_tot_cnt[tgt_id]),
+ (waiting->q_tot_cnt[tgt_id] == 0) ? 0 :
+ ASC_TENTHS(waiting->q_tot_tim[tgt_id],
+ waiting->q_tot_cnt[tgt_id]));
+ ASC_PRT_NEXT();
+ }
+ } while (0);
+
+ return totlen;
+}
+#endif /* CONFIG_PROC_FS */
+#endif /* ADVANSYS_STATS */
+
+#ifdef ADVANSYS_DEBUG
+/*
+ * asc_prt_scsi_host()
+ */
+STATIC void
+asc_prt_scsi_host(struct Scsi_Host *s)
+{
+ asc_board_t *boardp;
+
+ boardp = ASC_BOARDP(s);
+
+ printk("Scsi_Host at addr 0x%lx\n", (ulong) s);
+ printk(
+" host_busy %u, host_no %d, last_reset %d,\n",
+ s->host_busy, s->host_no,
+ (unsigned) s->last_reset);
+
+ printk(
+" base 0x%lx, io_port 0x%lx, n_io_port %u, irq 0x%x,\n",
+ (ulong) s->base, (ulong) s->io_port, s->n_io_port, s->irq);
+
+ printk(
+" dma_channel %d, this_id %d, can_queue %d,\n",
+ s->dma_channel, s->this_id, s->can_queue);
+
+ printk(
+" cmd_per_lun %d, sg_tablesize %d, unchecked_isa_dma %d\n",
+ s->cmd_per_lun, s->sg_tablesize, s->unchecked_isa_dma);
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ asc_prt_asc_dvc_var(&ASC_BOARDP(s)->dvc_var.asc_dvc_var);
+ asc_prt_asc_dvc_cfg(&ASC_BOARDP(s)->dvc_cfg.asc_dvc_cfg);
+ } else {
+ asc_prt_adv_dvc_var(&ASC_BOARDP(s)->dvc_var.adv_dvc_var);
+ asc_prt_adv_dvc_cfg(&ASC_BOARDP(s)->dvc_cfg.adv_dvc_cfg);
+ }
+}
+
+/*
+ * asc_prt_scsi_cmnd()
+ */
+STATIC void
+asc_prt_scsi_cmnd(struct scsi_cmnd *s)
+{
+ printk("struct scsi_cmnd at addr 0x%lx\n", (ulong) s);
+
+ printk(
+" host 0x%lx, device 0x%lx, target %u, lun %u, channel %u,\n",
+ (ulong) s->device->host, (ulong) s->device, s->device->id, s->device->lun,
+ s->device->channel);
+
+ asc_prt_hex(" CDB", s->cmnd, s->cmd_len);
+
+ printk (
+"sc_data_direction %u, resid %d\n",
+ s->sc_data_direction, s->resid);
+
+ printk(
+" use_sg %u, sglist_len %u, abort_reason 0x%x\n",
+ s->use_sg, s->sglist_len, s->abort_reason);
+
+ printk(
+" serial_number 0x%x, serial_number_at_timeout 0x%x, retries %d, allowed %d\n",
+ (unsigned) s->serial_number, (unsigned) s->serial_number_at_timeout,
+ s->retries, s->allowed);
+
+ printk(
+" timeout_per_command %d, timeout_total %d, timeout %d\n",
+ s->timeout_per_command, s->timeout_total, s->timeout);
+
+ printk(" internal_timeout %u\n", s->internal_timeout);
+
+ printk(
+" scsi_done 0x%lx, done 0x%lx, host_scribble 0x%lx, result 0x%x\n",
+ (ulong) s->scsi_done, (ulong) s->done,
+ (ulong) s->host_scribble, s->result);
+
+ printk(
+" tag %u, pid %u\n",
+ (unsigned) s->tag, (unsigned) s->pid);
+}
+
+/*
+ * asc_prt_asc_dvc_var()
+ */
+STATIC void
+asc_prt_asc_dvc_var(ASC_DVC_VAR *h)
+{
+ printk("ASC_DVC_VAR at addr 0x%lx\n", (ulong) h);
+
+ printk(
+" iop_base 0x%x, err_code 0x%x, dvc_cntl 0x%x, bug_fix_cntl %d,\n",
+ h->iop_base, h->err_code, h->dvc_cntl, h->bug_fix_cntl);
+
+ printk(
+" bus_type %d, isr_callback 0x%lx, exe_callback 0x%lx, init_sdtr 0x%x,\n",
+ h->bus_type, (ulong) h->isr_callback, (ulong) h->exe_callback,
+ (unsigned) h->init_sdtr);
+
+ printk(
+" sdtr_done 0x%x, use_tagged_qng 0x%x, unit_not_ready 0x%x, chip_no 0x%x,\n",
+ (unsigned) h->sdtr_done, (unsigned) h->use_tagged_qng,
+ (unsigned) h->unit_not_ready, (unsigned) h->chip_no);
+
+ printk(
+" queue_full_or_busy 0x%x, start_motor 0x%x, scsi_reset_wait %u,\n",
+ (unsigned) h->queue_full_or_busy, (unsigned) h->start_motor,
+ (unsigned) h->scsi_reset_wait);
+
+ printk(
+" is_in_int %u, max_total_qng %u, cur_total_qng %u, in_critical_cnt %u,\n",
+ (unsigned) h->is_in_int, (unsigned) h->max_total_qng,
+ (unsigned) h->cur_total_qng, (unsigned) h->in_critical_cnt);
+
+ printk(
+" last_q_shortage %u, init_state 0x%x, no_scam 0x%x, pci_fix_asyn_xfer 0x%x,\n",
+ (unsigned) h->last_q_shortage, (unsigned) h->init_state,
+ (unsigned) h->no_scam, (unsigned) h->pci_fix_asyn_xfer);
+
+ printk(
+" cfg 0x%lx, irq_no 0x%x\n",
+ (ulong) h->cfg, (unsigned) h->irq_no);
+}
+
+/*
+ * asc_prt_asc_dvc_cfg()
+ */
+STATIC void
+asc_prt_asc_dvc_cfg(ASC_DVC_CFG *h)
+{
+ printk("ASC_DVC_CFG at addr 0x%lx\n", (ulong) h);
+
+ printk(
+" can_tagged_qng 0x%x, cmd_qng_enabled 0x%x,\n",
+ h->can_tagged_qng, h->cmd_qng_enabled);
+ printk(
+" disc_enable 0x%x, sdtr_enable 0x%x,\n",
+ h->disc_enable, h->sdtr_enable);
+
+ printk(
+" chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, chip_version %d,\n",
+ h->chip_scsi_id, h->isa_dma_speed, h->isa_dma_channel,
+ h->chip_version);
+
+ printk(
+" pci_device_id %d, lib_serial_no %u, lib_version %u, mcode_date 0x%x,\n",
+ to_pci_dev(h->dev)->device, h->lib_serial_no, h->lib_version,
+ h->mcode_date);
+
+ printk(
+" mcode_version %d, overrun_buf 0x%lx\n",
+ h->mcode_version, (ulong) h->overrun_buf);
+}
+
+/*
+ * asc_prt_asc_scsi_q()
+ */
+STATIC void
+asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
+{
+ ASC_SG_HEAD *sgp;
+ int i;
+
+ printk("ASC_SCSI_Q at addr 0x%lx\n", (ulong) q);
+
+ printk(
+" target_ix 0x%x, target_lun %u, srb_ptr 0x%lx, tag_code 0x%x,\n",
+ q->q2.target_ix, q->q1.target_lun,
+ (ulong) q->q2.srb_ptr, q->q2.tag_code);
+
+ printk(
+" data_addr 0x%lx, data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n",
+ (ulong) le32_to_cpu(q->q1.data_addr),
+ (ulong) le32_to_cpu(q->q1.data_cnt),
+ (ulong) le32_to_cpu(q->q1.sense_addr), q->q1.sense_len);
+
+ printk(
+" cdbptr 0x%lx, cdb_len %u, sg_head 0x%lx, sg_queue_cnt %u\n",
+ (ulong) q->cdbptr, q->q2.cdb_len,
+ (ulong) q->sg_head, q->q1.sg_queue_cnt);
+
+ if (q->sg_head) {
+ sgp = q->sg_head;
+ printk("ASC_SG_HEAD at addr 0x%lx\n", (ulong) sgp);
+ printk(" entry_cnt %u, queue_cnt %u\n", sgp->entry_cnt, sgp->queue_cnt);
+ for (i = 0; i < sgp->entry_cnt; i++) {
+ printk(" [%u]: addr 0x%lx, bytes %lu\n",
+ i, (ulong) le32_to_cpu(sgp->sg_list[i].addr),
+ (ulong) le32_to_cpu(sgp->sg_list[i].bytes));
+ }
+
+ }
+}
+
+/*
+ * asc_prt_asc_qdone_info()
+ */
+STATIC void
+asc_prt_asc_qdone_info(ASC_QDONE_INFO *q)
+{
+ printk("ASC_QDONE_INFO at addr 0x%lx\n", (ulong) q);
+ printk(
+" srb_ptr 0x%lx, target_ix %u, cdb_len %u, tag_code %u,\n",
+ (ulong) q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len,
+ q->d2.tag_code);
+ printk(
+" done_stat 0x%x, host_stat 0x%x, scsi_stat 0x%x, scsi_msg 0x%x\n",
+ q->d3.done_stat, q->d3.host_stat, q->d3.scsi_stat, q->d3.scsi_msg);
+}
+
+/*
+ * asc_prt_adv_dvc_var()
+ *
+ * Display an ADV_DVC_VAR structure.
+ */
+STATIC void
+asc_prt_adv_dvc_var(ADV_DVC_VAR *h)
+{
+ printk(" ADV_DVC_VAR at addr 0x%lx\n", (ulong) h);
+
+ printk(
+" iop_base 0x%lx, err_code 0x%x, ultra_able 0x%x\n",
+ (ulong) h->iop_base, h->err_code, (unsigned) h->ultra_able);
+
+ printk(
+" isr_callback 0x%lx, sdtr_able 0x%x, wdtr_able 0x%x\n",
+ (ulong) h->isr_callback, (unsigned) h->sdtr_able,
+ (unsigned) h->wdtr_able);
+
+ printk(
+" start_motor 0x%x, scsi_reset_wait 0x%x, irq_no 0x%x,\n",
+ (unsigned) h->start_motor,
+ (unsigned) h->scsi_reset_wait, (unsigned) h->irq_no);
+
+ printk(
+" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%lxn\n",
+ (unsigned) h->max_host_qng, (unsigned) h->max_dvc_qng,
+ (ulong) h->carr_freelist);
+
+ printk(
+" icq_sp 0x%lx, irq_sp 0x%lx\n",
+ (ulong) h->icq_sp, (ulong) h->irq_sp);
+
+ printk(
+" no_scam 0x%x, tagqng_able 0x%x\n",
+ (unsigned) h->no_scam, (unsigned) h->tagqng_able);
+
+ printk(
+" chip_scsi_id 0x%x, cfg 0x%lx\n",
+ (unsigned) h->chip_scsi_id, (ulong) h->cfg);
+}
+
+/*
+ * asc_prt_adv_dvc_cfg()
+ *
+ * Display an ADV_DVC_CFG structure.
+ */
+STATIC void
+asc_prt_adv_dvc_cfg(ADV_DVC_CFG *h)
+{
+ printk(" ADV_DVC_CFG at addr 0x%lx\n", (ulong) h);
+
+ printk(
+" disc_enable 0x%x, termination 0x%x\n",
+ h->disc_enable, h->termination);
+
+ printk(
+" chip_version 0x%x, mcode_date 0x%x\n",
+ h->chip_version, h->mcode_date);
+
+ printk(
+" mcode_version 0x%x, pci_device_id 0x%x, lib_version %u\n",
+ h->mcode_version, to_pci_dev(h->dev)->device, h->lib_version);
+
+ printk(
+" control_flag 0x%x, pci_slot_info 0x%x\n",
+ h->control_flag, h->pci_slot_info);
+}
+
+/*
+ * asc_prt_adv_scsi_req_q()
+ *
+ * Display an ADV_SCSI_REQ_Q structure.
+ */
+STATIC void
+asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
+{
+ int sg_blk_cnt;
+ struct asc_sg_block *sg_ptr;
+
+ printk("ADV_SCSI_REQ_Q at addr 0x%lx\n", (ulong) q);
+
+ printk(
+" target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n",
+ q->target_id, q->target_lun, (ulong) q->srb_ptr, q->a_flag);
+
+ printk(" cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n",
+ q->cntl, (ulong) le32_to_cpu(q->data_addr), (ulong) q->vdata_addr);
+
+ printk(
+" data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n",
+ (ulong) le32_to_cpu(q->data_cnt),
+ (ulong) le32_to_cpu(q->sense_addr), q->sense_len);
+
+ printk(
+" cdb_len %u, done_status 0x%x, host_status 0x%x, scsi_status 0x%x\n",
+ q->cdb_len, q->done_status, q->host_status, q->scsi_status);
+
+ printk(
+" sg_working_ix 0x%x, target_cmd %u\n",
+ q->sg_working_ix, q->target_cmd);
+
+ printk(
+" scsiq_rptr 0x%lx, sg_real_addr 0x%lx, sg_list_ptr 0x%lx\n",
+ (ulong) le32_to_cpu(q->scsiq_rptr),
+ (ulong) le32_to_cpu(q->sg_real_addr), (ulong) q->sg_list_ptr);
+
+ /* Display the request's ADV_SG_BLOCK structures. */
+ if (q->sg_list_ptr != NULL)
+ {
+ sg_blk_cnt = 0;
+ while (1) {
+ /*
+ * 'sg_ptr' is a physical address. Convert it to a virtual
+ * address by indexing 'sg_blk_cnt' into the virtual address
+ * array 'sg_list_ptr'.
+ *
+ * XXX - Assumes all SG physical blocks are virtually contiguous.
+ */
+ sg_ptr = &(((ADV_SG_BLOCK *) (q->sg_list_ptr))[sg_blk_cnt]);
+ asc_prt_adv_sgblock(sg_blk_cnt, sg_ptr);
+ if (sg_ptr->sg_ptr == 0)
+ {
+ break;
+ }
+ sg_blk_cnt++;
+ }
+ }
+}
+
+/*
+ * asc_prt_adv_sgblock()
+ *
+ * Display an ADV_SG_BLOCK structure.
+ */
+STATIC void
+asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
+{
+ int i;
+
+ printk(" ASC_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
+ (ulong) b, sgblockno);
+ printk(" sg_cnt %u, sg_ptr 0x%lx\n",
+ b->sg_cnt, (ulong) le32_to_cpu(b->sg_ptr));
+ ASC_ASSERT(b->sg_cnt <= NO_OF_SG_PER_BLOCK);
+ if (b->sg_ptr != 0)
+ {
+ ASC_ASSERT(b->sg_cnt == NO_OF_SG_PER_BLOCK);
+ }
+ for (i = 0; i < b->sg_cnt; i++) {
+ printk(" [%u]: sg_addr 0x%lx, sg_count 0x%lx\n",
+ i, (ulong) b->sg_list[i].sg_addr, (ulong) b->sg_list[i].sg_count);
+ }
+}
+
+/*
+ * asc_prt_hex()
+ *
+ * Print hexadecimal output in 4 byte groupings 32 bytes
+ * or 8 double-words per line.
+ */
+STATIC void
+asc_prt_hex(char *f, uchar *s, int l)
+{
+ int i;
+ int j;
+ int k;
+ int m;
+
+ printk("%s: (%d bytes)\n", f, l);
+
+ for (i = 0; i < l; i += 32) {
+
+ /* Display a maximum of 8 double-words per line. */
+ if ((k = (l - i) / 4) >= 8) {
+ k = 8;
+ m = 0;
+ } else {
+ m = (l - i) % 4;
+ }
+
+ for (j = 0; j < k; j++) {
+ printk(" %2.2X%2.2X%2.2X%2.2X",
+ (unsigned) s[i+(j*4)], (unsigned) s[i+(j*4)+1],
+ (unsigned) s[i+(j*4)+2], (unsigned) s[i+(j*4)+3]);
+ }
+
+ switch (m) {
+ case 0:
+ default:
+ break;
+ case 1:
+ printk(" %2.2X",
+ (unsigned) s[i+(j*4)]);
+ break;
+ case 2:
+ printk(" %2.2X%2.2X",
+ (unsigned) s[i+(j*4)],
+ (unsigned) s[i+(j*4)+1]);
+ break;
+ case 3:
+ printk(" %2.2X%2.2X%2.2X",
+ (unsigned) s[i+(j*4)+1],
+ (unsigned) s[i+(j*4)+2],
+ (unsigned) s[i+(j*4)+3]);
+ break;
+ }
+
+ printk("\n");
+ }
+}
+#endif /* ADVANSYS_DEBUG */
+
+/*
+ * --- Asc Library Functions
+ */
+
+STATIC ushort __init
+AscGetEisaChipCfg(
+ PortAddr iop_base)
+{
+ PortAddr eisa_cfg_iop;
+
+ eisa_cfg_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
+ (PortAddr) (ASC_EISA_CFG_IOP_MASK);
+ return (inpw(eisa_cfg_iop));
+}
+
+STATIC uchar __init
+AscSetChipScsiID(
+ PortAddr iop_base,
+ uchar new_host_id
+)
+{
+ ushort cfg_lsw;
+
+ if (AscGetChipScsiID(iop_base) == new_host_id) {
+ return (new_host_id);
+ }
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ cfg_lsw &= 0xF8FF;
+ cfg_lsw |= (ushort) ((new_host_id & ASC_MAX_TID) << 8);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetChipScsiID(iop_base));
+}
+
+STATIC uchar __init
+AscGetChipScsiCtrl(
+ PortAddr iop_base)
+{
+ uchar sc;
+
+ AscSetBank(iop_base, 1);
+ sc = inp(iop_base + IOP_REG_SC);
+ AscSetBank(iop_base, 0);
+ return (sc);
+}
+
+STATIC uchar __init
+AscGetChipVersion(
+ PortAddr iop_base,
+ ushort bus_type
+)
+{
+ if ((bus_type & ASC_IS_EISA) != 0) {
+ PortAddr eisa_iop;
+ uchar revision;
+ eisa_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
+ (PortAddr) ASC_EISA_REV_IOP_MASK;
+ revision = inp(eisa_iop);
+ return ((uchar) ((ASC_CHIP_MIN_VER_EISA - 1) + revision));
+ }
+ return (AscGetChipVerNo(iop_base));
+}
+
+STATIC ushort __init
+AscGetChipBusType(
+ PortAddr iop_base)
+{
+ ushort chip_ver;
+
+ chip_ver = AscGetChipVerNo(iop_base);
+ if (
+ (chip_ver >= ASC_CHIP_MIN_VER_VL)
+ && (chip_ver <= ASC_CHIP_MAX_VER_VL)
+) {
+ if (
+ ((iop_base & 0x0C30) == 0x0C30)
+ || ((iop_base & 0x0C50) == 0x0C50)
+) {
+ return (ASC_IS_EISA);
+ }
+ return (ASC_IS_VL);
+ }
+ if ((chip_ver >= ASC_CHIP_MIN_VER_ISA) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_ISA)) {
+ if (chip_ver >= ASC_CHIP_MIN_VER_ISA_PNP) {
+ return (ASC_IS_ISAPNP);
+ }
+ return (ASC_IS_ISA);
+ } else if ((chip_ver >= ASC_CHIP_MIN_VER_PCI) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_PCI)) {
+ return (ASC_IS_PCI);
+ }
+ return (0);
+}
+
+STATIC ASC_DCNT
+AscLoadMicroCode(
+ PortAddr iop_base,
+ ushort s_addr,
+ uchar *mcode_buf,
+ ushort mcode_size
+)
+{
+ ASC_DCNT chksum;
+ ushort mcode_word_size;
+ ushort mcode_chksum;
+
+ /* Write the microcode buffer starting at LRAM address 0. */
+ mcode_word_size = (ushort) (mcode_size >> 1);
+ AscMemWordSetLram(iop_base, s_addr, 0, mcode_word_size);
+ AscMemWordCopyPtrToLram(iop_base, s_addr, mcode_buf, mcode_word_size);
+
+ chksum = AscMemSumLramWord(iop_base, s_addr, mcode_word_size);
+ ASC_DBG1(1, "AscLoadMicroCode: chksum 0x%lx\n", (ulong) chksum);
+ mcode_chksum = (ushort) AscMemSumLramWord(iop_base,
+ (ushort) ASC_CODE_SEC_BEG,
+ (ushort) ((mcode_size - s_addr - (ushort) ASC_CODE_SEC_BEG) / 2));
+ ASC_DBG1(1, "AscLoadMicroCode: mcode_chksum 0x%lx\n",
+ (ulong) mcode_chksum);
+ AscWriteLramWord(iop_base, ASCV_MCODE_CHKSUM_W, mcode_chksum);
+ AscWriteLramWord(iop_base, ASCV_MCODE_SIZE_W, mcode_size);
+ return (chksum);
+}
+
+STATIC int
+AscFindSignature(
+ PortAddr iop_base
+)
+{
+ ushort sig_word;
+
+ ASC_DBG2(1, "AscFindSignature: AscGetChipSignatureByte(0x%x) 0x%x\n",
+ iop_base, AscGetChipSignatureByte(iop_base));
+ if (AscGetChipSignatureByte(iop_base) == (uchar) ASC_1000_ID1B) {
+ ASC_DBG2(1, "AscFindSignature: AscGetChipSignatureWord(0x%x) 0x%x\n",
+ iop_base, AscGetChipSignatureWord(iop_base));
+ sig_word = AscGetChipSignatureWord(iop_base);
+ if ((sig_word == (ushort) ASC_1000_ID0W) ||
+ (sig_word == (ushort) ASC_1000_ID0W_FIX)) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+STATIC PortAddr _asc_def_iop_base[ASC_IOADR_TABLE_MAX_IX] __initdata =
+{
+ 0x100, ASC_IOADR_1, 0x120, ASC_IOADR_2, 0x140, ASC_IOADR_3, ASC_IOADR_4,
+ ASC_IOADR_5, ASC_IOADR_6, ASC_IOADR_7, ASC_IOADR_8
+};
+
+#ifdef CONFIG_ISA
+STATIC uchar _isa_pnp_inited __initdata = 0;
+
+STATIC PortAddr __init
+AscSearchIOPortAddr(
+ PortAddr iop_beg,
+ ushort bus_type)
+{
+ if (bus_type & ASC_IS_VL) {
+ while ((iop_beg = AscSearchIOPortAddr11(iop_beg)) != 0) {
+ if (AscGetChipVersion(iop_beg, bus_type) <= ASC_CHIP_MAX_VER_VL) {
+ return (iop_beg);
+ }
+ }
+ return (0);
+ }
+ if (bus_type & ASC_IS_ISA) {
+ if (_isa_pnp_inited == 0) {
+ AscSetISAPNPWaitForKey();
+ _isa_pnp_inited++;
+ }
+ while ((iop_beg = AscSearchIOPortAddr11(iop_beg)) != 0) {
+ if ((AscGetChipVersion(iop_beg, bus_type) & ASC_CHIP_VER_ISA_BIT) != 0) {
+ return (iop_beg);
+ }
+ }
+ return (0);
+ }
+ if (bus_type & ASC_IS_EISA) {
+ if ((iop_beg = AscSearchIOPortAddrEISA(iop_beg)) != 0) {
+ return (iop_beg);
+ }
+ return (0);
+ }
+ return (0);
+}
+
+STATIC PortAddr __init
+AscSearchIOPortAddr11(
+ PortAddr s_addr
+)
+{
+ int i;
+ PortAddr iop_base;
+
+ for (i = 0; i < ASC_IOADR_TABLE_MAX_IX; i++) {
+ if (_asc_def_iop_base[i] > s_addr) {
+ break;
+ }
+ }
+ for (; i < ASC_IOADR_TABLE_MAX_IX; i++) {
+ iop_base = _asc_def_iop_base[i];
+ if (check_region(iop_base, ASC_IOADR_GAP) != 0) {
+ ASC_DBG1(1,
+ "AscSearchIOPortAddr11: check_region() failed I/O port 0x%x\n",
+ iop_base);
+ continue;
+ }
+ ASC_DBG1(1, "AscSearchIOPortAddr11: probing I/O port 0x%x\n", iop_base);
+ if (AscFindSignature(iop_base)) {
+ return (iop_base);
+ }
+ }
+ return (0);
+}
+
+STATIC void __init
+AscSetISAPNPWaitForKey(void)
+{
+ outp(ASC_ISA_PNP_PORT_ADDR, 0x02);
+ outp(ASC_ISA_PNP_PORT_WRITE, 0x02);
+ return;
+}
+#endif /* CONFIG_ISA */
+
+STATIC void __init
+AscToggleIRQAct(
+ PortAddr iop_base
+)
+{
+ AscSetChipStatus(iop_base, CIW_IRQ_ACT);
+ AscSetChipStatus(iop_base, 0);
+ return;
+}
+
+STATIC uchar __init
+AscGetChipIRQ(
+ PortAddr iop_base,
+ ushort bus_type)
+{
+ ushort cfg_lsw;
+ uchar chip_irq;
+
+ if ((bus_type & ASC_IS_EISA) != 0) {
+ cfg_lsw = AscGetEisaChipCfg(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 8) & 0x07) + 10);
+ if ((chip_irq == 13) || (chip_irq > 15)) {
+ return (0);
+ }
+ return (chip_irq);
+ }
+ if ((bus_type & ASC_IS_VL) != 0) {
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 2) & 0x07));
+ if ((chip_irq == 0) ||
+ (chip_irq == 4) ||
+ (chip_irq == 7)) {
+ return (0);
+ }
+ return ((uchar) (chip_irq + (ASC_MIN_IRQ_NO - 1)));
+ }
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 2) & 0x03));
+ if (chip_irq == 3)
+ chip_irq += (uchar) 2;
+ return ((uchar) (chip_irq + ASC_MIN_IRQ_NO));
+}
+
+STATIC uchar __init
+AscSetChipIRQ(
+ PortAddr iop_base,
+ uchar irq_no,
+ ushort bus_type)
+{
+ ushort cfg_lsw;
+
+ if ((bus_type & ASC_IS_VL) != 0) {
+ if (irq_no != 0) {
+ if ((irq_no < ASC_MIN_IRQ_NO) || (irq_no > ASC_MAX_IRQ_NO)) {
+ irq_no = 0;
+ } else {
+ irq_no -= (uchar) ((ASC_MIN_IRQ_NO - 1));
+ }
+ }
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFE3);
+ cfg_lsw |= (ushort) 0x0010;
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ AscToggleIRQAct(iop_base);
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFE0);
+ cfg_lsw |= (ushort) ((irq_no & 0x07) << 2);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ AscToggleIRQAct(iop_base);
+ return (AscGetChipIRQ(iop_base, bus_type));
+ }
+ if ((bus_type & (ASC_IS_ISA)) != 0) {
+ if (irq_no == 15)
+ irq_no -= (uchar) 2;
+ irq_no -= (uchar) ASC_MIN_IRQ_NO;
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFF3);
+ cfg_lsw |= (ushort) ((irq_no & 0x03) << 2);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetChipIRQ(iop_base, bus_type));
+ }
+ return (0);
+}
+
+#ifdef CONFIG_ISA
+STATIC void __init
+AscEnableIsaDma(
+ uchar dma_channel)
+{
+ if (dma_channel < 4) {
+ outp(0x000B, (ushort) (0xC0 | dma_channel));
+ outp(0x000A, dma_channel);
+ } else if (dma_channel < 8) {
+ outp(0x00D6, (ushort) (0xC0 | (dma_channel - 4)));
+ outp(0x00D4, (ushort) (dma_channel - 4));
+ }
+ return;
+}
+#endif /* CONFIG_ISA */
+
+STATIC int
+AscIsrChipHalted(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ EXT_MSG ext_msg;
+ EXT_MSG out_msg;
+ ushort halt_q_addr;
+ int sdtr_accept;
+ ushort int_halt_code;
+ ASC_SCSI_BIT_ID_TYPE scsi_busy;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ PortAddr iop_base;
+ uchar tag_code;
+ uchar q_status;
+ uchar halt_qp;
+ uchar sdtr_data;
+ uchar target_ix;
+ uchar q_cntl, tid_no;
+ uchar cur_dvc_qng;
+ uchar asyn_sdtr;
+ uchar scsi_status;
+ asc_board_t *boardp;
+
+ ASC_ASSERT(asc_dvc->drv_ptr != NULL);
+ boardp = asc_dvc->drv_ptr;
+
+ iop_base = asc_dvc->iop_base;
+ int_halt_code = AscReadLramWord(iop_base, ASCV_HALTCODE_W);
+
+ halt_qp = AscReadLramByte(iop_base, ASCV_CURCDB_B);
+ halt_q_addr = ASC_QNO_TO_QADDR(halt_qp);
+ target_ix = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TARGET_IX));
+ q_cntl = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL));
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ target_id = (uchar) ASC_TID_TO_TARGET_ID(tid_no);
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+ asyn_sdtr = ASYN_SDTR_DATA_FIX_PCI_REV_AB;
+ } else {
+ asyn_sdtr = 0;
+ }
+ if (int_halt_code == ASC_HALT_DISABLE_ASYN_USE_SYN_FIX) {
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+ AscSetChipSDTR(iop_base, 0, tid_no);
+ boardp->sdtr_data[tid_no] = 0;
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_ENABLE_ASYN_USE_SYN_FIX) {
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ boardp->sdtr_data[tid_no] = asyn_sdtr;
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_EXTMSG_IN) {
+
+ AscMemWordCopyPtrFromLram(iop_base,
+ ASCV_MSGIN_BEG,
+ (uchar *) &ext_msg,
+ sizeof(EXT_MSG) >> 1);
+
+ if (ext_msg.msg_type == MS_EXTEND &&
+ ext_msg.msg_req == MS_SDTR_CODE &&
+ ext_msg.msg_len == MS_SDTR_LEN) {
+ sdtr_accept = TRUE;
+ if ((ext_msg.req_ack_offset > ASC_SYN_MAX_OFFSET)) {
+
+ sdtr_accept = FALSE;
+ ext_msg.req_ack_offset = ASC_SYN_MAX_OFFSET;
+ }
+ if ((ext_msg.xfer_period <
+ asc_dvc->sdtr_period_tbl[asc_dvc->host_init_sdtr_index]) ||
+ (ext_msg.xfer_period >
+ asc_dvc->sdtr_period_tbl[asc_dvc->max_sdtr_index])) {
+ sdtr_accept = FALSE;
+ ext_msg.xfer_period =
+ asc_dvc->sdtr_period_tbl[asc_dvc->host_init_sdtr_index];
+ }
+ if (sdtr_accept) {
+ sdtr_data = AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ if ((sdtr_data == 0xFF)) {
+
+ q_cntl |= QC_MSG_OUT;
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ boardp->sdtr_data[tid_no] = asyn_sdtr;
+ }
+ }
+ if (ext_msg.req_ack_offset == 0) {
+
+ q_cntl &= ~QC_MSG_OUT;
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ } else {
+ if (sdtr_accept && (q_cntl & QC_MSG_OUT)) {
+
+ q_cntl &= ~QC_MSG_OUT;
+ asc_dvc->sdtr_done |= target_id;
+ asc_dvc->init_sdtr |= target_id;
+ asc_dvc->pci_fix_asyn_xfer &= ~target_id;
+ sdtr_data = AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ AscSetChipSDTR(iop_base, sdtr_data, tid_no);
+ boardp->sdtr_data[tid_no] = sdtr_data;
+ } else {
+
+ q_cntl |= QC_MSG_OUT;
+ AscMsgOutSDTR(asc_dvc,
+ ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ asc_dvc->pci_fix_asyn_xfer &= ~target_id;
+ sdtr_data = AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ AscSetChipSDTR(iop_base, sdtr_data, tid_no);
+ boardp->sdtr_data[tid_no] = sdtr_data;
+ asc_dvc->sdtr_done |= target_id;
+ asc_dvc->init_sdtr |= target_id;
+ }
+ }
+
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (ext_msg.msg_type == MS_EXTEND &&
+ ext_msg.msg_req == MS_WDTR_CODE &&
+ ext_msg.msg_len == MS_WDTR_LEN) {
+
+ ext_msg.wdtr_width = 0;
+ AscMemWordCopyPtrToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (uchar *) &ext_msg,
+ sizeof(EXT_MSG) >> 1);
+ q_cntl |= QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else {
+
+ ext_msg.msg_type = MESSAGE_REJECT;
+ AscMemWordCopyPtrToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (uchar *) &ext_msg,
+ sizeof(EXT_MSG) >> 1);
+ q_cntl |= QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ }
+ } else if (int_halt_code == ASC_HALT_CHK_CONDITION) {
+
+ q_cntl |= QC_REQ_SENSE;
+
+ if ((asc_dvc->init_sdtr & target_id) != 0) {
+
+ asc_dvc->sdtr_done &= ~target_id;
+
+ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no);
+ q_cntl |= QC_MSG_OUT;
+ AscMsgOutSDTR(asc_dvc,
+ asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) &
+ (uchar) (asc_dvc->max_sdtr_index - 1)],
+ (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET));
+ }
+
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+
+ tag_code = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE));
+ tag_code &= 0xDC;
+ if (
+ (asc_dvc->pci_fix_asyn_xfer & target_id)
+ && !(asc_dvc->pci_fix_asyn_xfer_always & target_id)
+) {
+
+ tag_code |= (ASC_TAG_FLAG_DISABLE_DISCONNECT
+ | ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX);
+
+ }
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE),
+ tag_code);
+
+ q_status = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_STATUS));
+ q_status |= (QS_READY | QS_BUSY);
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ q_status);
+
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ AscWriteLramByte(iop_base, (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_SDTR_REJECTED) {
+
+ AscMemWordCopyPtrFromLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (uchar *) &out_msg,
+ sizeof(EXT_MSG) >> 1);
+
+ if ((out_msg.msg_type == MS_EXTEND) &&
+ (out_msg.msg_len == MS_SDTR_LEN) &&
+ (out_msg.msg_req == MS_SDTR_CODE)) {
+
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ boardp->sdtr_data[tid_no] = asyn_sdtr;
+ }
+ q_cntl &= ~QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) {
+
+ scsi_status = AscReadLramByte(iop_base,
+ (ushort) ((ushort) halt_q_addr + (ushort) ASC_SCSIQ_SCSI_STATUS));
+ cur_dvc_qng = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASC_QADR_BEG + (ushort) target_ix));
+ if ((cur_dvc_qng > 0) &&
+ (asc_dvc->cur_dvc_qng[tid_no] > 0)) {
+
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy |= target_id;
+ AscWriteLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy |= target_id;
+
+ if (scsi_status == SAM_STAT_TASK_SET_FULL) {
+ if (cur_dvc_qng > ASC_MIN_TAGGED_CMD) {
+ cur_dvc_qng -= 1;
+ asc_dvc->max_dvc_qng[tid_no] = cur_dvc_qng;
+
+ AscWriteLramByte(iop_base,
+ (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG +
+ (ushort) tid_no),
+ cur_dvc_qng);
+
+ /*
+ * Set the device queue depth to the number of
+ * active requests when the QUEUE FULL condition
+ * was encountered.
+ */
+ boardp->queue_full |= target_id;
+ boardp->queue_full_cnt[tid_no] = cur_dvc_qng;
+ }
+ }
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ }
+#if CC_VERY_LONG_SG_LIST
+ else if (int_halt_code == ASC_HALT_HOST_COPY_SG_LIST_TO_RISC)
+ {
+ uchar q_no;
+ ushort q_addr;
+ uchar sg_wk_q_no;
+ uchar first_sg_wk_q_no;
+ ASC_SCSI_Q *scsiq; /* Ptr to driver request. */
+ ASC_SG_HEAD *sg_head; /* Ptr to driver SG request. */
+ ASC_SG_LIST_Q scsi_sg_q; /* Structure written to queue. */
+ ushort sg_list_dwords;
+ ushort sg_entry_cnt;
+ uchar next_qp;
+ int i;
+
+ q_no = AscReadLramByte(iop_base, (ushort) ASCV_REQ_SG_LIST_QP);
+ if (q_no == ASC_QLINK_END)
+ {
+ return(0);
+ }
+
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+
+ /*
+ * Convert the request's SRB pointer to a host ASC_SCSI_REQ
+ * structure pointer using a macro provided by the driver.
+ * The ASC_SCSI_REQ pointer provides a pointer to the
+ * host ASC_SG_HEAD structure.
+ */
+ /* Read request's SRB pointer. */
+ scsiq = (ASC_SCSI_Q *)
+ ASC_SRB2SCSIQ(
+ ASC_U32_TO_VADDR(AscReadLramDWord(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_D_SRBPTR))));
+
+ /*
+ * Get request's first and working SG queue.
+ */
+ sg_wk_q_no = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_SG_WK_QP));
+
+ first_sg_wk_q_no = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FIRST_SG_WK_QP));
+
+ /*
+ * Reset request's working SG queue back to the
+ * first SG queue.
+ */
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_SG_WK_QP),
+ first_sg_wk_q_no);
+
+ sg_head = scsiq->sg_head;
+
+ /*
+ * Set sg_entry_cnt to the number of SG elements
+ * that will be completed on this interrupt.
+ *
+ * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1
+ * SG elements. The data_cnt and data_addr fields which
+ * add 1 to the SG element capacity are not used when
+ * restarting SG handling after a halt.
+ */
+ if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1))
+ {
+ sg_entry_cnt = ASC_MAX_SG_LIST - 1;
+
+ /*
+ * Keep track of remaining number of SG elements that will
+ * need to be handled on the next interrupt.
+ */
+ scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1);
+ } else
+ {
+ sg_entry_cnt = scsiq->remain_sg_entry_cnt;
+ scsiq->remain_sg_entry_cnt = 0;
+ }
+
+ /*
+ * Copy SG elements into the list of allocated SG queues.
+ *
+ * Last index completed is saved in scsiq->next_sg_index.
+ */
+ next_qp = first_sg_wk_q_no;
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+ scsi_sg_q.sg_head_qp = q_no;
+ scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
+ for( i = 0; i < sg_head->queue_cnt; i++)
+ {
+ scsi_sg_q.seq_no = i + 1;
+ if (sg_entry_cnt > ASC_SG_LIST_PER_Q)
+ {
+ sg_list_dwords = (uchar) (ASC_SG_LIST_PER_Q * 2);
+ sg_entry_cnt -= ASC_SG_LIST_PER_Q;
+ /*
+ * After very first SG queue RISC FW uses next
+ * SG queue first element then checks sg_list_cnt
+ * against zero and then decrements, so set
+ * sg_list_cnt 1 less than number of SG elements
+ * in each SG queue.
+ */
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ } else {
+ /*
+ * This is the last SG queue in the list of
+ * allocated SG queues. If there are more
+ * SG elements than will fit in the allocated
+ * queues, then set the QCSG_SG_XFER_MORE flag.
+ */
+ if (scsiq->remain_sg_entry_cnt != 0)
+ {
+ scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
+ } else
+ {
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
+ }
+ /* equals sg_entry_cnt * 2 */
+ sg_list_dwords = sg_entry_cnt << 1;
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
+ sg_entry_cnt = 0;
+ }
+
+ scsi_sg_q.q_no = next_qp;
+ AscMemWordCopyPtrToLram(iop_base,
+ q_addr + ASC_SCSIQ_SGHD_CPY_BEG,
+ (uchar *) &scsi_sg_q,
+ sizeof(ASC_SG_LIST_Q) >> 1);
+
+ AscMemDWordCopyPtrToLram(iop_base,
+ q_addr + ASC_SGQ_LIST_BEG,
+ (uchar *) &sg_head->sg_list[scsiq->next_sg_index],
+ sg_list_dwords);
+
+ scsiq->next_sg_index += ASC_SG_LIST_PER_Q;
+
+ /*
+ * If the just completed SG queue contained the
+ * last SG element, then no more SG queues need
+ * to be written.
+ */
+ if (scsi_sg_q.cntl & QCSG_SG_XFER_END)
+ {
+ break;
+ }
+
+ next_qp = AscReadLramByte( iop_base,
+ ( ushort )( q_addr+ASC_SCSIQ_B_FWD ) );
+ q_addr = ASC_QNO_TO_QADDR( next_qp );
+ }
+
+ /*
+ * Clear the halt condition so the RISC will be restarted
+ * after the return.
+ */
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return(0);
+ }
+#endif /* CC_VERY_LONG_SG_LIST */
+ return (0);
+}
+
+STATIC uchar
+_AscCopyLramScsiDoneQ(
+ PortAddr iop_base,
+ ushort q_addr,
+ ASC_QDONE_INFO * scsiq,
+ ASC_DCNT max_dma_count
+)
+{
+ ushort _val;
+ uchar sg_queue_cnt;
+
+ DvcGetQinfo(iop_base,
+ q_addr + ASC_SCSIQ_DONE_INFO_BEG,
+ (uchar *) scsiq,
+ (sizeof (ASC_SCSIQ_2) + sizeof (ASC_SCSIQ_3)) / 2);
+
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS));
+ scsiq->q_status = (uchar) _val;
+ scsiq->q_no = (uchar) (_val >> 8);
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_CNTL));
+ scsiq->cntl = (uchar) _val;
+ sg_queue_cnt = (uchar) (_val >> 8);
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_SENSE_LEN));
+ scsiq->sense_len = (uchar) _val;
+ scsiq->extra_bytes = (uchar) (_val >> 8);
+
+ /*
+ * Read high word of remain bytes from alternate location.
+ */
+ scsiq->remain_bytes = (((ADV_DCNT) AscReadLramWord( iop_base,
+ (ushort) (q_addr+ (ushort) ASC_SCSIQ_W_ALT_DC1))) << 16);
+ /*
+ * Read low word of remain bytes from original location.
+ */
+ scsiq->remain_bytes += AscReadLramWord(iop_base,
+ (ushort) (q_addr+ (ushort) ASC_SCSIQ_DW_REMAIN_XFER_CNT));
+
+ scsiq->remain_bytes &= max_dma_count;
+ return (sg_queue_cnt);
+}
+
+STATIC int
+AscIsrQDone(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ uchar next_qp;
+ uchar n_q_used;
+ uchar sg_list_qp;
+ uchar sg_queue_cnt;
+ uchar q_cnt;
+ uchar done_q_tail;
+ uchar tid_no;
+ ASC_SCSI_BIT_ID_TYPE scsi_busy;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ PortAddr iop_base;
+ ushort q_addr;
+ ushort sg_q_addr;
+ uchar cur_target_qng;
+ ASC_QDONE_INFO scsiq_buf;
+ ASC_QDONE_INFO *scsiq;
+ int false_overrun;
+ ASC_ISR_CALLBACK asc_isr_callback;
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = asc_dvc->isr_callback;
+ n_q_used = 1;
+ scsiq = (ASC_QDONE_INFO *) & scsiq_buf;
+ done_q_tail = (uchar) AscGetVarDoneQTail(iop_base);
+ q_addr = ASC_QNO_TO_QADDR(done_q_tail);
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_FWD));
+ if (next_qp != ASC_QLINK_END) {
+ AscPutVarDoneQTail(iop_base, next_qp);
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+ sg_queue_cnt = _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq,
+ asc_dvc->max_dma_count);
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ (uchar) (scsiq->q_status & (uchar) ~ (QS_READY | QS_ABORTED)));
+ tid_no = ASC_TIX_TO_TID(scsiq->d2.target_ix);
+ target_id = ASC_TIX_TO_TARGET_ID(scsiq->d2.target_ix);
+ if ((scsiq->cntl & QC_SG_HEAD) != 0) {
+ sg_q_addr = q_addr;
+ sg_list_qp = next_qp;
+ for (q_cnt = 0; q_cnt < sg_queue_cnt; q_cnt++) {
+ sg_list_qp = AscReadLramByte(iop_base,
+ (ushort) (sg_q_addr + (ushort) ASC_SCSIQ_B_FWD));
+ sg_q_addr = ASC_QNO_TO_QADDR(sg_list_qp);
+ if (sg_list_qp == ASC_QLINK_END) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SG_Q_LINKS);
+ scsiq->d3.done_stat = QD_WITH_ERROR;
+ scsiq->d3.host_stat = QHSTA_D_QDONE_SG_LIST_CORRUPTED;
+ goto FATAL_ERR_QDONE;
+ }
+ AscWriteLramByte(iop_base,
+ (ushort) (sg_q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ QS_FREE);
+ }
+ n_q_used = sg_queue_cnt + 1;
+ AscPutVarDoneQTail(iop_base, sg_list_qp);
+ }
+ if (asc_dvc->queue_full_or_busy & target_id) {
+ cur_target_qng = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASC_QADR_BEG + (ushort) scsiq->d2.target_ix));
+ if (cur_target_qng < asc_dvc->max_dvc_qng[tid_no]) {
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ AscWriteLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy &= ~target_id;
+ }
+ }
+ if (asc_dvc->cur_total_qng >= n_q_used) {
+ asc_dvc->cur_total_qng -= n_q_used;
+ if (asc_dvc->cur_dvc_qng[tid_no] != 0) {
+ asc_dvc->cur_dvc_qng[tid_no]--;
+ }
+ } else {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CUR_QNG);
+ scsiq->d3.done_stat = QD_WITH_ERROR;
+ goto FATAL_ERR_QDONE;
+ }
+ if ((scsiq->d2.srb_ptr == 0UL) ||
+ ((scsiq->q_status & QS_ABORTED) != 0)) {
+ return (0x11);
+ } else if (scsiq->q_status == QS_DONE) {
+ false_overrun = FALSE;
+ if (scsiq->extra_bytes != 0) {
+ scsiq->remain_bytes += (ADV_DCNT) scsiq->extra_bytes;
+ }
+ if (scsiq->d3.done_stat == QD_WITH_ERROR) {
+ if (scsiq->d3.host_stat == QHSTA_M_DATA_OVER_RUN) {
+ if ((scsiq->cntl & (QC_DATA_IN | QC_DATA_OUT)) == 0) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ } else if (false_overrun) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ }
+ } else if (scsiq->d3.host_stat ==
+ QHSTA_M_HUNG_REQ_SCSI_BUS_RESET) {
+ AscStopChip(iop_base);
+ AscSetChipControl(iop_base,
+ (uchar) (CC_SCSI_RESET | CC_HALT));
+ DvcDelayNanoSecond(asc_dvc, 60000);
+ AscSetChipControl(iop_base, CC_HALT);
+ AscSetChipStatus(iop_base, CIW_CLR_SCSI_RESET_INT);
+ AscSetChipStatus(iop_base, 0);
+ AscSetChipControl(iop_base, 0);
+ }
+ }
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ } else {
+ if ((AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CDB_BEG)) ==
+ START_STOP)) {
+ asc_dvc->unit_not_ready &= ~target_id;
+ if (scsiq->d3.done_stat != QD_NO_ERROR) {
+ asc_dvc->start_motor &= ~target_id;
+ }
+ }
+ }
+ return (1);
+ } else {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_Q_STATUS);
+ FATAL_ERR_QDONE:
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ }
+ return (0x80);
+ }
+ }
+ return (0);
+}
+
+STATIC int
+AscISR(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ ASC_CS_TYPE chipstat;
+ PortAddr iop_base;
+ ushort saved_ram_addr;
+ uchar ctrl_reg;
+ uchar saved_ctrl_reg;
+ int int_pending;
+ int status;
+ uchar host_flag;
+
+ iop_base = asc_dvc->iop_base;
+ int_pending = FALSE;
+
+ if (AscIsIntPending(iop_base) == 0)
+ {
+ return int_pending;
+ }
+
+ if (((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0)
+ || (asc_dvc->isr_callback == 0)
+) {
+ return (ERR);
+ }
+ if (asc_dvc->in_critical_cnt != 0) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL);
+ return (ERR);
+ }
+ if (asc_dvc->is_in_int) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY);
+ return (ERR);
+ }
+ asc_dvc->is_in_int = TRUE;
+ ctrl_reg = AscGetChipControl(iop_base);
+ saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET |
+ CC_SINGLE_STEP | CC_DIAG | CC_TEST));
+ chipstat = AscGetChipStatus(iop_base);
+ if (chipstat & CSW_SCSI_RESET_LATCH) {
+ if (!(asc_dvc->bus_type & (ASC_IS_VL | ASC_IS_EISA))) {
+ int i = 10;
+ int_pending = TRUE;
+ asc_dvc->sdtr_done = 0;
+ saved_ctrl_reg &= (uchar) (~CC_HALT);
+ while ((AscGetChipStatus(iop_base) & CSW_SCSI_RESET_ACTIVE) &&
+ (i-- > 0))
+ {
+ DvcSleepMilliSecond(100);
+ }
+ AscSetChipControl(iop_base, (CC_CHIP_RESET | CC_HALT));
+ AscSetChipControl(iop_base, CC_HALT);
+ AscSetChipStatus(iop_base, CIW_CLR_SCSI_RESET_INT);
+ AscSetChipStatus(iop_base, 0);
+ chipstat = AscGetChipStatus(iop_base);
+ }
+ }
+ saved_ram_addr = AscGetChipLramAddr(iop_base);
+ host_flag = AscReadLramByte(iop_base,
+ ASCV_HOST_FLAG_B) & (uchar) (~ASC_HOST_FLAG_IN_ISR);
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B,
+ (uchar) (host_flag | (uchar) ASC_HOST_FLAG_IN_ISR));
+ if ((chipstat & CSW_INT_PENDING)
+ || (int_pending)
+) {
+ AscAckInterrupt(iop_base);
+ int_pending = TRUE;
+ if ((chipstat & CSW_HALTED) &&
+ (ctrl_reg & CC_SINGLE_STEP)) {
+ if (AscIsrChipHalted(asc_dvc) == ERR) {
+ goto ISR_REPORT_QDONE_FATAL_ERROR;
+ } else {
+ saved_ctrl_reg &= (uchar) (~CC_HALT);
+ }
+ } else {
+ ISR_REPORT_QDONE_FATAL_ERROR:
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) {
+ while (((status = AscIsrQDone(asc_dvc)) & 0x01) != 0) {
+ }
+ } else {
+ do {
+ if ((status = AscIsrQDone(asc_dvc)) == 1) {
+ break;
+ }
+ } while (status == 0x11);
+ }
+ if ((status & 0x80) != 0)
+ int_pending = ERR;
+ }
+ }
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
+ AscSetChipLramAddr(iop_base, saved_ram_addr);
+ AscSetChipControl(iop_base, saved_ctrl_reg);
+ asc_dvc->is_in_int = FALSE;
+ return (int_pending);
+}
+
+/* Microcode buffer is kept after initialization for error recovery. */
+STATIC uchar _asc_mcode_buf[] =
+{
+ 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0x12, 0x0D, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC2, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40,
+ 0xB6, 0x00, 0x36, 0x00, 0x05, 0xD6, 0x0C, 0xD2, 0x12, 0xDA, 0x00, 0xA2, 0xC2, 0x00, 0x92, 0x80,
+ 0x1E, 0x98, 0x50, 0x00, 0xF5, 0x00, 0x48, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80,
+ 0x4F, 0x00, 0xF5, 0x00, 0x48, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, 0x80, 0x62,
+ 0x92, 0x80, 0x00, 0x46, 0x15, 0xEE, 0x13, 0xEA, 0x02, 0x01, 0x09, 0xD8, 0xCD, 0x04, 0x4D, 0x00,
+ 0x00, 0xA3, 0xD6, 0x00, 0xA6, 0x97, 0x7F, 0x23, 0x04, 0x61, 0x84, 0x01, 0xE6, 0x84, 0xD2, 0xC1,
+ 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xDA, 0x01, 0xA6, 0x97, 0xC6, 0x81, 0xC2, 0x88,
+ 0x80, 0x73, 0x80, 0x77, 0x00, 0x01, 0x01, 0xA1, 0xFE, 0x00, 0x4F, 0x00, 0x84, 0x97, 0x07, 0xA6,
+ 0x08, 0x01, 0x00, 0x33, 0x03, 0x00, 0xC2, 0x88, 0x03, 0x03, 0x01, 0xDE, 0xC2, 0x88, 0xCE, 0x00,
+ 0x69, 0x60, 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60, 0x00, 0xA2, 0x78, 0x01, 0x80, 0x63, 0x07, 0xA6,
+ 0x24, 0x01, 0x78, 0x81, 0x03, 0x03, 0x80, 0x63, 0xE2, 0x00, 0x07, 0xA6, 0x34, 0x01, 0x00, 0x33,
+ 0x04, 0x00, 0xC2, 0x88, 0x03, 0x07, 0x02, 0x01, 0x04, 0xCA, 0x0D, 0x23, 0x68, 0x98, 0x4D, 0x04,
+ 0x04, 0x85, 0x05, 0xD8, 0x0D, 0x23, 0x68, 0x98, 0xCD, 0x04, 0x15, 0x23, 0xF8, 0x88, 0xFB, 0x23,
+ 0x02, 0x61, 0x82, 0x01, 0x80, 0x63, 0x02, 0x03, 0x06, 0xA3, 0x62, 0x01, 0x00, 0x33, 0x0A, 0x00,
+ 0xC2, 0x88, 0x4E, 0x00, 0x07, 0xA3, 0x6E, 0x01, 0x00, 0x33, 0x0B, 0x00, 0xC2, 0x88, 0xCD, 0x04,
+ 0x36, 0x2D, 0x00, 0x33, 0x1A, 0x00, 0xC2, 0x88, 0x50, 0x04, 0x88, 0x81, 0x06, 0xAB, 0x82, 0x01,
+ 0x88, 0x81, 0x4E, 0x00, 0x07, 0xA3, 0x92, 0x01, 0x50, 0x00, 0x00, 0xA3, 0x3C, 0x01, 0x00, 0x05,
+ 0x7C, 0x81, 0x46, 0x97, 0x02, 0x01, 0x05, 0xC6, 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01,
+ 0xBE, 0x81, 0xFD, 0x23, 0x02, 0x61, 0x82, 0x01, 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0,
+ 0xB4, 0x01, 0x80, 0x63, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1B, 0x00, 0xC2, 0x88, 0x06, 0x23,
+ 0x68, 0x98, 0xCD, 0x04, 0xE6, 0x84, 0x06, 0x01, 0x00, 0xA2, 0xD4, 0x01, 0x57, 0x60, 0x00, 0xA0,
+ 0xDA, 0x01, 0xE6, 0x84, 0x80, 0x23, 0xA0, 0x01, 0xE6, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61,
+ 0x00, 0xA2, 0x00, 0x02, 0x04, 0x01, 0x0C, 0xDE, 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x84, 0x97,
+ 0xFC, 0x81, 0x08, 0x23, 0x02, 0x41, 0x82, 0x01, 0x4F, 0x00, 0x62, 0x97, 0x48, 0x04, 0x84, 0x80,
+ 0xF0, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29,
+ 0x03, 0x42, 0x06, 0xE2, 0x03, 0xEE, 0x6B, 0xEB, 0x11, 0x23, 0xF8, 0x88, 0x04, 0x98, 0xF0, 0x80,
+ 0x80, 0x73, 0x80, 0x77, 0x07, 0xA4, 0x2A, 0x02, 0x7C, 0x95, 0x06, 0xA6, 0x34, 0x02, 0x03, 0xA6,
+ 0x4C, 0x04, 0x46, 0x82, 0x04, 0x01, 0x03, 0xD8, 0xB4, 0x98, 0x6A, 0x96, 0x46, 0x82, 0xFE, 0x95,
+ 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D, 0x02, 0xA6, 0x6C, 0x02, 0x07, 0xA6, 0x5A, 0x02,
+ 0x06, 0xA6, 0x5E, 0x02, 0x03, 0xA6, 0x62, 0x02, 0xC2, 0x88, 0x7C, 0x95, 0x48, 0x82, 0x60, 0x96,
+ 0x48, 0x82, 0x04, 0x23, 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, 0x3C, 0x84, 0x04, 0x01, 0x0C, 0xDC,
+ 0xE0, 0x23, 0x25, 0x61, 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01,
+ 0x03, 0x23, 0xA4, 0x01, 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, 0x1C, 0x01, 0x02, 0xA6, 0xAA, 0x02,
+ 0x07, 0xA6, 0x5A, 0x02, 0x06, 0xA6, 0x5E, 0x02, 0x03, 0xA6, 0x20, 0x04, 0x01, 0xA6, 0xB4, 0x02,
+ 0x00, 0xA6, 0xB4, 0x02, 0x00, 0x33, 0x12, 0x00, 0xC2, 0x88, 0x00, 0x0E, 0x80, 0x63, 0x00, 0x43,
+ 0x00, 0xA0, 0x8C, 0x02, 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61, 0x84, 0x01,
+ 0x10, 0x31, 0x12, 0x35, 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0xEA, 0x82,
+ 0x18, 0x23, 0x04, 0x61, 0x18, 0xA0, 0xE2, 0x02, 0x04, 0x01, 0xA2, 0xC8, 0x00, 0x33, 0x1F, 0x00,
+ 0xC2, 0x88, 0x08, 0x31, 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, 0x7E, 0x98, 0xB6, 0x2D, 0x01, 0xA6,
+ 0x14, 0x03, 0x00, 0xA6, 0x14, 0x03, 0x07, 0xA6, 0x0C, 0x03, 0x06, 0xA6, 0x10, 0x03, 0x03, 0xA6,
+ 0x20, 0x04, 0x02, 0xA6, 0x6C, 0x02, 0x00, 0x33, 0x33, 0x00, 0xC2, 0x88, 0x7C, 0x95, 0xEE, 0x82,
+ 0x60, 0x96, 0xEE, 0x82, 0x82, 0x98, 0x80, 0x42, 0x7E, 0x98, 0x64, 0xE4, 0x04, 0x01, 0x2D, 0xC8,
+ 0x31, 0x05, 0x07, 0x01, 0x00, 0xA2, 0x54, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x86, 0x98,
+ 0x7E, 0x98, 0x00, 0xA6, 0x16, 0x03, 0x07, 0xA6, 0x4C, 0x03, 0x03, 0xA6, 0x3C, 0x04, 0x06, 0xA6,
+ 0x50, 0x03, 0x01, 0xA6, 0x16, 0x03, 0x00, 0x33, 0x25, 0x00, 0xC2, 0x88, 0x7C, 0x95, 0x32, 0x83,
+ 0x60, 0x96, 0x32, 0x83, 0x04, 0x01, 0x10, 0xCE, 0x07, 0xC8, 0x05, 0x05, 0xEB, 0x04, 0x00, 0x33,
+ 0x00, 0x20, 0xC0, 0x20, 0x81, 0x62, 0x72, 0x83, 0x00, 0x01, 0x05, 0x05, 0xFF, 0xA2, 0x7A, 0x03,
+ 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x2E, 0x83, 0x05, 0x05, 0x15, 0x01, 0x00, 0xA2, 0x9A, 0x03,
+ 0xEC, 0x00, 0x6E, 0x00, 0x95, 0x01, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0x01, 0xA6, 0x96, 0x03,
+ 0x00, 0xA6, 0x96, 0x03, 0x10, 0x84, 0x80, 0x42, 0x7E, 0x98, 0x01, 0xA6, 0xA4, 0x03, 0x00, 0xA6,
+ 0xBC, 0x03, 0x10, 0x84, 0xA8, 0x98, 0x80, 0x42, 0x01, 0xA6, 0xA4, 0x03, 0x07, 0xA6, 0xB2, 0x03,
+ 0xD4, 0x83, 0x7C, 0x95, 0xA8, 0x83, 0x00, 0x33, 0x2F, 0x00, 0xC2, 0x88, 0xA8, 0x98, 0x80, 0x42,
+ 0x00, 0xA6, 0xBC, 0x03, 0x07, 0xA6, 0xCA, 0x03, 0xD4, 0x83, 0x7C, 0x95, 0xC0, 0x83, 0x00, 0x33,
+ 0x26, 0x00, 0xC2, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36, 0x04, 0x23, 0xA0, 0x01, 0x12, 0x23,
+ 0xA1, 0x01, 0x10, 0x84, 0x07, 0xF0, 0x06, 0xA4, 0xF4, 0x03, 0x80, 0x6B, 0x80, 0x67, 0x05, 0x23,
+ 0x83, 0x03, 0x80, 0x63, 0x03, 0xA6, 0x0E, 0x04, 0x07, 0xA6, 0x06, 0x04, 0x06, 0xA6, 0x0A, 0x04,
+ 0x00, 0x33, 0x17, 0x00, 0xC2, 0x88, 0x7C, 0x95, 0xF4, 0x83, 0x60, 0x96, 0xF4, 0x83, 0x20, 0x84,
+ 0x07, 0xF0, 0x06, 0xA4, 0x20, 0x04, 0x80, 0x6B, 0x80, 0x67, 0x05, 0x23, 0x83, 0x03, 0x80, 0x63,
+ 0xB6, 0x2D, 0x03, 0xA6, 0x3C, 0x04, 0x07, 0xA6, 0x34, 0x04, 0x06, 0xA6, 0x38, 0x04, 0x00, 0x33,
+ 0x30, 0x00, 0xC2, 0x88, 0x7C, 0x95, 0x20, 0x84, 0x60, 0x96, 0x20, 0x84, 0x1D, 0x01, 0x06, 0xCC,
+ 0x00, 0x33, 0x00, 0x84, 0xC0, 0x20, 0x00, 0x23, 0xEA, 0x00, 0x81, 0x62, 0xA2, 0x0D, 0x80, 0x63,
+ 0x07, 0xA6, 0x5A, 0x04, 0x00, 0x33, 0x18, 0x00, 0xC2, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01,
+ 0x07, 0xA4, 0x64, 0x04, 0x23, 0x01, 0x00, 0xA2, 0x86, 0x04, 0x0A, 0xA0, 0x76, 0x04, 0xE0, 0x00,
+ 0x00, 0x33, 0x1D, 0x00, 0xC2, 0x88, 0x0B, 0xA0, 0x82, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00,
+ 0xC2, 0x88, 0x42, 0x23, 0xF8, 0x88, 0x00, 0x23, 0x22, 0xA3, 0xE6, 0x04, 0x08, 0x23, 0x22, 0xA3,
+ 0xA2, 0x04, 0x28, 0x23, 0x22, 0xA3, 0xAE, 0x04, 0x02, 0x23, 0x22, 0xA3, 0xC4, 0x04, 0x42, 0x23,
+ 0xF8, 0x88, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xAE, 0x04, 0x45, 0x23, 0xF8, 0x88, 0x04, 0x98,
+ 0x00, 0xA2, 0xC0, 0x04, 0xB4, 0x98, 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xE8, 0x81,
+ 0x47, 0x23, 0xF8, 0x88, 0x04, 0x01, 0x0B, 0xDE, 0x04, 0x98, 0xB4, 0x98, 0x00, 0x33, 0x00, 0x81,
+ 0xC0, 0x20, 0x81, 0x62, 0x14, 0x01, 0x00, 0xA0, 0x00, 0x02, 0x43, 0x23, 0xF8, 0x88, 0x04, 0x23,
+ 0xA0, 0x01, 0x44, 0x23, 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xF4, 0x04, 0x00, 0x33,
+ 0x27, 0x00, 0xC2, 0x88, 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01,
+ 0x04, 0x98, 0x26, 0x95, 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0x22, 0x05,
+ 0x00, 0x05, 0x76, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x1C, 0x05, 0x0A, 0x85, 0x46, 0x97, 0xCD, 0x04,
+ 0x24, 0x85, 0x48, 0x04, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x34, 0x85,
+ 0x02, 0x23, 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x40, 0x05, 0x1D, 0x01, 0x04, 0xD6,
+ 0xFF, 0x23, 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01,
+ 0x04, 0x01, 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01,
+ 0xC9, 0x00, 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x60, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00,
+ 0x5D, 0x00, 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xF8, 0x05,
+ 0x03, 0x03, 0x02, 0xA0, 0x8E, 0x05, 0xF4, 0x85, 0x00, 0x33, 0x2D, 0x00, 0xC2, 0x88, 0x04, 0xA0,
+ 0xB8, 0x05, 0x80, 0x63, 0x00, 0x23, 0xDF, 0x00, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xA4, 0x05,
+ 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23, 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x62, 0x97, 0x04, 0x85,
+ 0x04, 0x23, 0x02, 0x41, 0x82, 0x01, 0x04, 0x85, 0x08, 0xA0, 0xBE, 0x05, 0xF4, 0x85, 0x03, 0xA0,
+ 0xC4, 0x05, 0xF4, 0x85, 0x01, 0xA0, 0xCE, 0x05, 0x88, 0x00, 0x80, 0x63, 0xCC, 0x86, 0x07, 0xA0,
+ 0xEE, 0x05, 0x5F, 0x00, 0x00, 0x2B, 0xDF, 0x08, 0x00, 0xA2, 0xE6, 0x05, 0x80, 0x67, 0x80, 0x63,
+ 0x01, 0xA2, 0x7A, 0x06, 0x7C, 0x85, 0x06, 0x23, 0x68, 0x98, 0x48, 0x23, 0xF8, 0x88, 0x07, 0x23,
+ 0x80, 0x00, 0x06, 0x87, 0x80, 0x63, 0x7C, 0x85, 0x00, 0x23, 0xDF, 0x00, 0x00, 0x63, 0x4A, 0x00,
+ 0x06, 0x61, 0x00, 0xA2, 0x36, 0x06, 0x1D, 0x01, 0x16, 0xD4, 0xC0, 0x23, 0x07, 0x41, 0x83, 0x03,
+ 0x80, 0x63, 0x06, 0xA6, 0x1C, 0x06, 0x00, 0x33, 0x37, 0x00, 0xC2, 0x88, 0x1D, 0x01, 0x01, 0xD6,
+ 0x20, 0x23, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x02, 0x23, 0xDF, 0x00, 0x07, 0xA6, 0x7C, 0x05,
+ 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00, 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, 0x06, 0x61,
+ 0x00, 0xA2, 0x4E, 0x06, 0x1D, 0x01, 0x03, 0xCA, 0xC0, 0x23, 0x07, 0x41, 0x00, 0x63, 0x1D, 0x01,
+ 0x04, 0xCC, 0x00, 0x33, 0x00, 0x83, 0xC0, 0x20, 0x81, 0x62, 0x80, 0x23, 0x07, 0x41, 0x00, 0x63,
+ 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x01, 0x23, 0xDF, 0x00, 0x06, 0xA6,
+ 0x84, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x80, 0x67, 0x80, 0x63, 0x00, 0x33, 0x00, 0x40, 0xC0, 0x20,
+ 0x81, 0x62, 0x00, 0x63, 0x00, 0x00, 0xFE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x94, 0x06,
+ 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x00, 0x01, 0xA0, 0x14, 0x07, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63,
+ 0x01, 0x00, 0x06, 0xA6, 0xAA, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43,
+ 0x00, 0xA0, 0xA2, 0x06, 0x06, 0xA6, 0xBC, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x80, 0x67, 0x40, 0x0E,
+ 0x80, 0x63, 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x23, 0xDF, 0x00, 0x00, 0x63, 0x07, 0xA6, 0xD6, 0x06,
+ 0x00, 0x33, 0x2A, 0x00, 0xC2, 0x88, 0x03, 0x03, 0x80, 0x63, 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6,
+ 0xE8, 0x06, 0x00, 0x33, 0x29, 0x00, 0xC2, 0x88, 0x00, 0x43, 0x00, 0xA2, 0xF4, 0x06, 0xC0, 0x0E,
+ 0x80, 0x63, 0xDE, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80, 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01,
+ 0x02, 0xDA, 0x80, 0x63, 0x7C, 0x85, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x8C, 0x06, 0x00, 0x33,
+ 0x2C, 0x00, 0xC2, 0x88, 0x0C, 0xA2, 0x2E, 0x07, 0xFE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6,
+ 0x2C, 0x07, 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x33, 0x3D, 0x00, 0xC2, 0x88, 0x00, 0x00, 0x80, 0x67,
+ 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x44, 0x07, 0x07, 0xA6, 0x7C, 0x05, 0xBF, 0x23, 0x04, 0x61,
+ 0x84, 0x01, 0xE6, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01, 0xF2, 0x00,
+ 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05, 0x81, 0x05,
+ 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00, 0x81, 0x01,
+ 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00, 0x80, 0x01,
+ 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00, 0x70, 0x00,
+ 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04, 0x70, 0x00,
+ 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01, 0xA2, 0x01,
+ 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0xC4, 0x07, 0x00, 0x33,
+ 0x07, 0x00, 0xC2, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00, 0xB0, 0x01,
+ 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2, 0xE4, 0x07,
+ 0x00, 0x05, 0xDA, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05, 0x00, 0x63,
+ 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08, 0x80, 0x02,
+ 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x00, 0xA0,
+ 0x14, 0x08, 0x16, 0x88, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF3, 0x04,
+ 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2, 0x44, 0x08,
+ 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0x24, 0x08, 0x04, 0x98,
+ 0x26, 0x95, 0x24, 0x88, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x5A, 0x88, 0x02, 0x01,
+ 0x04, 0xD8, 0x46, 0x97, 0x04, 0x98, 0x26, 0x95, 0x4A, 0x88, 0x75, 0x00, 0x00, 0xA3, 0x64, 0x08,
+ 0x00, 0x05, 0x4E, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x76, 0x08,
+ 0x00, 0x33, 0x3E, 0x00, 0xC2, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x38, 0x2B,
+ 0x9C, 0x88, 0x38, 0x2B, 0x92, 0x88, 0x32, 0x09, 0x31, 0x05, 0x92, 0x98, 0x05, 0x05, 0xB2, 0x09,
+ 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32, 0x80, 0x36,
+ 0x80, 0x3A, 0x80, 0x3E, 0xB4, 0x3D, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A,
+ 0x40, 0x3E, 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0xB4, 0x08, 0x5D, 0x00, 0xFE, 0xC3,
+ 0x00, 0x63, 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73,
+ 0x13, 0x23, 0xF8, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01,
+ 0x81, 0x62, 0xE2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2,
+ 0xF1, 0xC7, 0x41, 0x23, 0xF8, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xE6, 0x84,
+};
+
+STATIC ushort _asc_mcode_size = sizeof(_asc_mcode_buf);
+STATIC ADV_DCNT _asc_mcode_chksum = 0x012C453FUL;
+
+#define ASC_SYN_OFFSET_ONE_DISABLE_LIST 16
+STATIC uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] =
+{
+ INQUIRY,
+ REQUEST_SENSE,
+ READ_CAPACITY,
+ READ_TOC,
+ MODE_SELECT,
+ MODE_SENSE,
+ MODE_SELECT_10,
+ MODE_SENSE_10,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF
+};
+
+STATIC int
+AscExeScsiQueue(
+ ASC_DVC_VAR *asc_dvc,
+ ASC_SCSI_Q *scsiq
+)
+{
+ PortAddr iop_base;
+ ulong last_int_level;
+ int sta;
+ int n_q_required;
+ int disable_syn_offset_one_fix;
+ int i;
+ ASC_PADDR addr;
+ ASC_EXE_CALLBACK asc_exe_callback;
+ ushort sg_entry_cnt = 0;
+ ushort sg_entry_cnt_minus_one = 0;
+ uchar target_ix;
+ uchar tid_no;
+ uchar sdtr_data;
+ uchar extra_bytes;
+ uchar scsi_cmd;
+ uchar disable_cmd;
+ ASC_SG_HEAD *sg_head;
+ ASC_DCNT data_cnt;
+
+ iop_base = asc_dvc->iop_base;
+ sg_head = scsiq->sg_head;
+ asc_exe_callback = asc_dvc->exe_callback;
+ if (asc_dvc->err_code != 0)
+ return (ERR);
+ if (scsiq == (ASC_SCSI_Q *) 0L) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SCSIQ_NULL_PTR);
+ return (ERR);
+ }
+ scsiq->q1.q_no = 0;
+ if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) {
+ scsiq->q1.extra_bytes = 0;
+ }
+ sta = 0;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ n_q_required = 1;
+ if (scsiq->cdbptr[0] == REQUEST_SENSE) {
+ if ((asc_dvc->init_sdtr & scsiq->q1.target_id) != 0) {
+ asc_dvc->sdtr_done &= ~scsiq->q1.target_id;
+ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no);
+ AscMsgOutSDTR(asc_dvc,
+ asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) &
+ (uchar) (asc_dvc->max_sdtr_index - 1)],
+ (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET));
+ scsiq->q1.cntl |= (QC_MSG_OUT | QC_URGENT);
+ }
+ }
+ last_int_level = DvcEnterCritical();
+ if (asc_dvc->in_critical_cnt != 0) {
+ DvcLeaveCritical(last_int_level);
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY);
+ return (ERR);
+ }
+ asc_dvc->in_critical_cnt++;
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
+ if ((sg_entry_cnt = sg_head->entry_cnt) == 0) {
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return (ERR);
+ }
+#if !CC_VERY_LONG_SG_LIST
+ if (sg_entry_cnt > ASC_MAX_SG_LIST)
+ {
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return(ERR);
+ }
+#endif /* !CC_VERY_LONG_SG_LIST */
+ if (sg_entry_cnt == 1) {
+ scsiq->q1.data_addr = (ADV_PADDR) sg_head->sg_list[0].addr;
+ scsiq->q1.data_cnt = (ADV_DCNT) sg_head->sg_list[0].bytes;
+ scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE);
+ }
+ sg_entry_cnt_minus_one = sg_entry_cnt - 1;
+ }
+ scsi_cmd = scsiq->cdbptr[0];
+ disable_syn_offset_one_fix = FALSE;
+ if ((asc_dvc->pci_fix_asyn_xfer & scsiq->q1.target_id) &&
+ !(asc_dvc->pci_fix_asyn_xfer_always & scsiq->q1.target_id)) {
+ if (scsiq->q1.cntl & QC_SG_HEAD) {
+ data_cnt = 0;
+ for (i = 0; i < sg_entry_cnt; i++) {
+ data_cnt += (ADV_DCNT) le32_to_cpu(sg_head->sg_list[i].bytes);
+ }
+ } else {
+ data_cnt = le32_to_cpu(scsiq->q1.data_cnt);
+ }
+ if (data_cnt != 0UL) {
+ if (data_cnt < 512UL) {
+ disable_syn_offset_one_fix = TRUE;
+ } else {
+ for (i = 0; i < ASC_SYN_OFFSET_ONE_DISABLE_LIST; i++) {
+ disable_cmd = _syn_offset_one_disable_cmd[i];
+ if (disable_cmd == 0xFF) {
+ break;
+ }
+ if (scsi_cmd == disable_cmd) {
+ disable_syn_offset_one_fix = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (disable_syn_offset_one_fix) {
+ scsiq->q2.tag_code &= ~MSG_SIMPLE_TAG;
+ scsiq->q2.tag_code |= (ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX |
+ ASC_TAG_FLAG_DISABLE_DISCONNECT);
+ } else {
+ scsiq->q2.tag_code &= 0x27;
+ }
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
+ if ((scsi_cmd == READ_6) ||
+ (scsi_cmd == READ_10)) {
+ addr =
+ (ADV_PADDR) le32_to_cpu(
+ sg_head->sg_list[sg_entry_cnt_minus_one].addr) +
+ (ADV_DCNT) le32_to_cpu(
+ sg_head->sg_list[sg_entry_cnt_minus_one].bytes);
+ extra_bytes = (uchar) ((ushort) addr & 0x0003);
+ if ((extra_bytes != 0) &&
+ ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES)
+ == 0)) {
+ scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES;
+ scsiq->q1.extra_bytes = extra_bytes;
+ data_cnt = le32_to_cpu(
+ sg_head->sg_list[sg_entry_cnt_minus_one].bytes);
+ data_cnt -= (ASC_DCNT) extra_bytes;
+ sg_head->sg_list[sg_entry_cnt_minus_one].bytes =
+ cpu_to_le32(data_cnt);
+ }
+ }
+ }
+ }
+ sg_head->entry_to_copy = sg_head->entry_cnt;
+#if CC_VERY_LONG_SG_LIST
+ /*
+ * Set the sg_entry_cnt to the maximum possible. The rest of
+ * the SG elements will be copied when the RISC completes the
+ * SG elements that fit and halts.
+ */
+ if (sg_entry_cnt > ASC_MAX_SG_LIST)
+ {
+ sg_entry_cnt = ASC_MAX_SG_LIST;
+ }
+#endif /* CC_VERY_LONG_SG_LIST */
+ n_q_required = AscSgListToQueue(sg_entry_cnt);
+ if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required) >=
+ (uint) n_q_required) || ((scsiq->q1.cntl & QC_URGENT) != 0)) {
+ if ((sta = AscSendScsiQueue(asc_dvc, scsiq,
+ n_q_required)) == 1) {
+ asc_dvc->in_critical_cnt--;
+ if (asc_exe_callback != 0) {
+ (*asc_exe_callback) (asc_dvc, scsiq);
+ }
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+ }
+ }
+ } else {
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
+ if ((scsi_cmd == READ_6) ||
+ (scsi_cmd == READ_10)) {
+ addr = le32_to_cpu(scsiq->q1.data_addr) +
+ le32_to_cpu(scsiq->q1.data_cnt);
+ extra_bytes = (uchar) ((ushort) addr & 0x0003);
+ if ((extra_bytes != 0) &&
+ ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES)
+ == 0)) {
+ data_cnt = le32_to_cpu(scsiq->q1.data_cnt);
+ if (((ushort) data_cnt & 0x01FF) == 0) {
+ scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES;
+ data_cnt -= (ASC_DCNT) extra_bytes;
+ scsiq->q1.data_cnt = cpu_to_le32(data_cnt);
+ scsiq->q1.extra_bytes = extra_bytes;
+ }
+ }
+ }
+ }
+ }
+ n_q_required = 1;
+ if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, 1) >= 1) ||
+ ((scsiq->q1.cntl & QC_URGENT) != 0)) {
+ if ((sta = AscSendScsiQueue(asc_dvc, scsiq,
+ n_q_required)) == 1) {
+ asc_dvc->in_critical_cnt--;
+ if (asc_exe_callback != 0) {
+ (*asc_exe_callback) (asc_dvc, scsiq);
+ }
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+ }
+ }
+ }
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+}
+
+STATIC int
+AscSendScsiQueue(
+ ASC_DVC_VAR *asc_dvc,
+ ASC_SCSI_Q *scsiq,
+ uchar n_q_required
+)
+{
+ PortAddr iop_base;
+ uchar free_q_head;
+ uchar next_qp;
+ uchar tid_no;
+ uchar target_ix;
+ int sta;
+
+ iop_base = asc_dvc->iop_base;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ sta = 0;
+ free_q_head = (uchar) AscGetVarFreeQHead(iop_base);
+ if (n_q_required > 1) {
+ if ((next_qp = AscAllocMultipleFreeQueue(iop_base,
+ free_q_head, (uchar) (n_q_required)))
+ != (uchar) ASC_QLINK_END) {
+ asc_dvc->last_q_shortage = 0;
+ scsiq->sg_head->queue_cnt = n_q_required - 1;
+ scsiq->q1.q_no = free_q_head;
+ if ((sta = AscPutReadySgListQueue(asc_dvc, scsiq,
+ free_q_head)) == 1) {
+ AscPutVarFreeQHead(iop_base, next_qp);
+ asc_dvc->cur_total_qng += (uchar) (n_q_required);
+ asc_dvc->cur_dvc_qng[tid_no]++;
+ }
+ return (sta);
+ }
+ } else if (n_q_required == 1) {
+ if ((next_qp = AscAllocFreeQueue(iop_base,
+ free_q_head)) != ASC_QLINK_END) {
+ scsiq->q1.q_no = free_q_head;
+ if ((sta = AscPutReadyQueue(asc_dvc, scsiq,
+ free_q_head)) == 1) {
+ AscPutVarFreeQHead(iop_base, next_qp);
+ asc_dvc->cur_total_qng++;
+ asc_dvc->cur_dvc_qng[tid_no]++;
+ }
+ return (sta);
+ }
+ }
+ return (sta);
+}
+
+STATIC int
+AscSgListToQueue(
+ int sg_list
+)
+{
+ int n_sg_list_qs;
+
+ n_sg_list_qs = ((sg_list - 1) / ASC_SG_LIST_PER_Q);
+ if (((sg_list - 1) % ASC_SG_LIST_PER_Q) != 0)
+ n_sg_list_qs++;
+ return (n_sg_list_qs + 1);
+}
+
+
+STATIC uint
+AscGetNumOfFreeQueue(
+ ASC_DVC_VAR *asc_dvc,
+ uchar target_ix,
+ uchar n_qs
+)
+{
+ uint cur_used_qs;
+ uint cur_free_qs;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ uchar tid_no;
+
+ target_id = ASC_TIX_TO_TARGET_ID(target_ix);
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ if ((asc_dvc->unit_not_ready & target_id) ||
+ (asc_dvc->queue_full_or_busy & target_id)) {
+ return (0);
+ }
+ if (n_qs == 1) {
+ cur_used_qs = (uint) asc_dvc->cur_total_qng +
+ (uint) asc_dvc->last_q_shortage +
+ (uint) ASC_MIN_FREE_Q;
+ } else {
+ cur_used_qs = (uint) asc_dvc->cur_total_qng +
+ (uint) ASC_MIN_FREE_Q;
+ }
+ if ((uint) (cur_used_qs + n_qs) <= (uint) asc_dvc->max_total_qng) {
+ cur_free_qs = (uint) asc_dvc->max_total_qng - cur_used_qs;
+ if (asc_dvc->cur_dvc_qng[tid_no] >=
+ asc_dvc->max_dvc_qng[tid_no]) {
+ return (0);
+ }
+ return (cur_free_qs);
+ }
+ if (n_qs > 1) {
+ if ((n_qs > asc_dvc->last_q_shortage) && (n_qs <= (asc_dvc->max_total_qng - ASC_MIN_FREE_Q))) {
+ asc_dvc->last_q_shortage = n_qs;
+ }
+ }
+ return (0);
+}
+
+STATIC int
+AscPutReadyQueue(
+ ASC_DVC_VAR *asc_dvc,
+ ASC_SCSI_Q *scsiq,
+ uchar q_no
+)
+{
+ ushort q_addr;
+ uchar tid_no;
+ uchar sdtr_data;
+ uchar syn_period_ix;
+ uchar syn_offset;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ if (((asc_dvc->init_sdtr & scsiq->q1.target_id) != 0) &&
+ ((asc_dvc->sdtr_done & scsiq->q1.target_id) == 0)) {
+ tid_no = ASC_TIX_TO_TID(scsiq->q2.target_ix);
+ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no);
+ syn_period_ix = (sdtr_data >> 4) & (asc_dvc->max_sdtr_index - 1);
+ syn_offset = sdtr_data & ASC_SYN_MAX_OFFSET;
+ AscMsgOutSDTR(asc_dvc,
+ asc_dvc->sdtr_period_tbl[syn_period_ix],
+ syn_offset);
+ scsiq->q1.cntl |= QC_MSG_OUT;
+ }
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ if ((scsiq->q1.target_id & asc_dvc->use_tagged_qng) == 0) {
+ scsiq->q2.tag_code &= ~MSG_SIMPLE_TAG ;
+ }
+ scsiq->q1.status = QS_FREE;
+ AscMemWordCopyPtrToLram(iop_base,
+ q_addr + ASC_SCSIQ_CDB_BEG,
+ (uchar *) scsiq->cdbptr,
+ scsiq->q2.cdb_len >> 1);
+
+ DvcPutScsiQ(iop_base,
+ q_addr + ASC_SCSIQ_CPY_BEG,
+ (uchar *) &scsiq->q1.cntl,
+ ((sizeof(ASC_SCSIQ_1) + sizeof(ASC_SCSIQ_2)) / 2) - 1);
+ AscWriteLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ (ushort) (((ushort) scsiq->q1.q_no << 8) | (ushort) QS_READY));
+ return (1);
+}
+
+STATIC int
+AscPutReadySgListQueue(
+ ASC_DVC_VAR *asc_dvc,
+ ASC_SCSI_Q *scsiq,
+ uchar q_no
+)
+{
+ int sta;
+ int i;
+ ASC_SG_HEAD *sg_head;
+ ASC_SG_LIST_Q scsi_sg_q;
+ ASC_DCNT saved_data_addr;
+ ASC_DCNT saved_data_cnt;
+ PortAddr iop_base;
+ ushort sg_list_dwords;
+ ushort sg_index;
+ ushort sg_entry_cnt;
+ ushort q_addr;
+ uchar next_qp;
+
+ iop_base = asc_dvc->iop_base;
+ sg_head = scsiq->sg_head;
+ saved_data_addr = scsiq->q1.data_addr;
+ saved_data_cnt = scsiq->q1.data_cnt;
+ scsiq->q1.data_addr = (ASC_PADDR) sg_head->sg_list[0].addr;
+ scsiq->q1.data_cnt = (ASC_DCNT) sg_head->sg_list[0].bytes;
+#if CC_VERY_LONG_SG_LIST
+ /*
+ * If sg_head->entry_cnt is greater than ASC_MAX_SG_LIST
+ * then not all SG elements will fit in the allocated queues.
+ * The rest of the SG elements will be copied when the RISC
+ * completes the SG elements that fit and halts.
+ */
+ if (sg_head->entry_cnt > ASC_MAX_SG_LIST)
+ {
+ /*
+ * Set sg_entry_cnt to be the number of SG elements that
+ * will fit in the allocated SG queues. It is minus 1, because
+ * the first SG element is handled above. ASC_MAX_SG_LIST is
+ * already inflated by 1 to account for this. For example it
+ * may be 50 which is 1 + 7 queues * 7 SG elements.
+ */
+ sg_entry_cnt = ASC_MAX_SG_LIST - 1;
+
+ /*
+ * Keep track of remaining number of SG elements that will
+ * need to be handled from a_isr.c.
+ */
+ scsiq->remain_sg_entry_cnt = sg_head->entry_cnt - ASC_MAX_SG_LIST;
+ } else
+ {
+#endif /* CC_VERY_LONG_SG_LIST */
+ /*
+ * Set sg_entry_cnt to be the number of SG elements that
+ * will fit in the allocated SG queues. It is minus 1, because
+ * the first SG element is handled above.
+ */
+ sg_entry_cnt = sg_head->entry_cnt - 1;
+#if CC_VERY_LONG_SG_LIST
+ }
+#endif /* CC_VERY_LONG_SG_LIST */
+ if (sg_entry_cnt != 0) {
+ scsiq->q1.cntl |= QC_SG_HEAD;
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ sg_index = 1;
+ scsiq->q1.sg_queue_cnt = sg_head->queue_cnt;
+ scsi_sg_q.sg_head_qp = q_no;
+ scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
+ for (i = 0; i < sg_head->queue_cnt; i++) {
+ scsi_sg_q.seq_no = i + 1;
+ if (sg_entry_cnt > ASC_SG_LIST_PER_Q) {
+ sg_list_dwords = (uchar) (ASC_SG_LIST_PER_Q * 2);
+ sg_entry_cnt -= ASC_SG_LIST_PER_Q;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q;
+ } else {
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ }
+ } else {
+#if CC_VERY_LONG_SG_LIST
+ /*
+ * This is the last SG queue in the list of
+ * allocated SG queues. If there are more
+ * SG elements than will fit in the allocated
+ * queues, then set the QCSG_SG_XFER_MORE flag.
+ */
+ if (sg_head->entry_cnt > ASC_MAX_SG_LIST)
+ {
+ scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
+ } else
+ {
+#endif /* CC_VERY_LONG_SG_LIST */
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
+#if CC_VERY_LONG_SG_LIST
+ }
+#endif /* CC_VERY_LONG_SG_LIST */
+ sg_list_dwords = sg_entry_cnt << 1;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt;
+ } else {
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
+ }
+ sg_entry_cnt = 0;
+ }
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FWD));
+ scsi_sg_q.q_no = next_qp;
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+ AscMemWordCopyPtrToLram(iop_base,
+ q_addr + ASC_SCSIQ_SGHD_CPY_BEG,
+ (uchar *) &scsi_sg_q,
+ sizeof(ASC_SG_LIST_Q) >> 1);
+ AscMemDWordCopyPtrToLram(iop_base,
+ q_addr + ASC_SGQ_LIST_BEG,
+ (uchar *) &sg_head->sg_list[sg_index],
+ sg_list_dwords);
+ sg_index += ASC_SG_LIST_PER_Q;
+ scsiq->next_sg_index = sg_index;
+ }
+ } else {
+ scsiq->q1.cntl &= ~QC_SG_HEAD;
+ }
+ sta = AscPutReadyQueue(asc_dvc, scsiq, q_no);
+ scsiq->q1.data_addr = saved_data_addr;
+ scsiq->q1.data_cnt = saved_data_cnt;
+ return (sta);
+}
+
+STATIC int
+AscSetRunChipSynRegAtID(
+ PortAddr iop_base,
+ uchar tid_no,
+ uchar sdtr_data
+)
+{
+ int sta = FALSE;
+
+ if (AscHostReqRiscHalt(iop_base)) {
+ sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
+ AscStartChip(iop_base);
+ return (sta);
+ }
+ return (sta);
+}
+
+STATIC int
+AscSetChipSynRegAtID(
+ PortAddr iop_base,
+ uchar id,
+ uchar sdtr_data
+)
+{
+ ASC_SCSI_BIT_ID_TYPE org_id;
+ int i;
+ int sta = TRUE;
+
+ AscSetBank(iop_base, 1);
+ org_id = AscReadChipDvcID(iop_base);
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if (org_id == (0x01 << i))
+ break;
+ }
+ org_id = (ASC_SCSI_BIT_ID_TYPE) i;
+ AscWriteChipDvcID(iop_base, id);
+ if (AscReadChipDvcID(iop_base) == (0x01 << id)) {
+ AscSetBank(iop_base, 0);
+ AscSetChipSyn(iop_base, sdtr_data);
+ if (AscGetChipSyn(iop_base) != sdtr_data) {
+ sta = FALSE;
+ }
+ } else {
+ sta = FALSE;
+ }
+ AscSetBank(iop_base, 1);
+ AscWriteChipDvcID(iop_base, org_id);
+ AscSetBank(iop_base, 0);
+ return (sta);
+}
+
+STATIC ushort
+AscInitLram(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ uchar i;
+ ushort s_addr;
+ PortAddr iop_base;
+ ushort warn_code;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0,
+ (ushort) (((int) (asc_dvc->max_total_qng + 2 + 1) * 64) >> 1)
+);
+ i = ASC_MIN_ACTIVE_QNO;
+ s_addr = ASC_QADR_BEG + ASC_QBLK_SIZE;
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) (i + 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (asc_dvc->max_total_qng));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) i);
+ i++;
+ s_addr += ASC_QBLK_SIZE;
+ for (; i < asc_dvc->max_total_qng; i++, s_addr += ASC_QBLK_SIZE) {
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) (i + 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (i - 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) i);
+ }
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) ASC_QLINK_END);
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (asc_dvc->max_total_qng - 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) asc_dvc->max_total_qng);
+ i++;
+ s_addr += ASC_QBLK_SIZE;
+ for (; i <= (uchar) (asc_dvc->max_total_qng + 3);
+ i++, s_addr += ASC_QBLK_SIZE) {
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_FWD), i);
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_BWD), i);
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_QNO), i);
+ }
+ return (warn_code);
+}
+
+STATIC ushort
+AscInitQLinkVar(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ PortAddr iop_base;
+ int i;
+ ushort lram_addr;
+
+ iop_base = asc_dvc->iop_base;
+ AscPutRiscVarFreeQHead(iop_base, 1);
+ AscPutRiscVarDoneQTail(iop_base, asc_dvc->max_total_qng);
+ AscPutVarFreeQHead(iop_base, 1);
+ AscPutVarDoneQTail(iop_base, asc_dvc->max_total_qng);
+ AscWriteLramByte(iop_base, ASCV_BUSY_QHEAD_B,
+ (uchar) ((int) asc_dvc->max_total_qng + 1));
+ AscWriteLramByte(iop_base, ASCV_DISC1_QHEAD_B,
+ (uchar) ((int) asc_dvc->max_total_qng + 2));
+ AscWriteLramByte(iop_base, (ushort) ASCV_TOTAL_READY_Q_B,
+ asc_dvc->max_total_qng);
+ AscWriteLramWord(iop_base, ASCV_ASCDVC_ERR_CODE_W, 0);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, 0);
+ AscWriteLramByte(iop_base, ASCV_SCSIBUSY_B, 0);
+ AscWriteLramByte(iop_base, ASCV_WTM_FLAG_B, 0);
+ AscPutQDoneInProgress(iop_base, 0);
+ lram_addr = ASC_QADR_BEG;
+ for (i = 0; i < 32; i++, lram_addr += 2) {
+ AscWriteLramWord(iop_base, lram_addr, 0);
+ }
+ return (0);
+}
+
+STATIC int
+AscSetLibErrorCode(
+ ASC_DVC_VAR *asc_dvc,
+ ushort err_code
+)
+{
+ if (asc_dvc->err_code == 0) {
+ asc_dvc->err_code = err_code;
+ AscWriteLramWord(asc_dvc->iop_base, ASCV_ASCDVC_ERR_CODE_W,
+ err_code);
+ }
+ return (err_code);
+}
+
+
+STATIC uchar
+AscMsgOutSDTR(
+ ASC_DVC_VAR *asc_dvc,
+ uchar sdtr_period,
+ uchar sdtr_offset
+)
+{
+ EXT_MSG sdtr_buf;
+ uchar sdtr_period_index;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ sdtr_buf.msg_type = MS_EXTEND;
+ sdtr_buf.msg_len = MS_SDTR_LEN;
+ sdtr_buf.msg_req = MS_SDTR_CODE;
+ sdtr_buf.xfer_period = sdtr_period;
+ sdtr_offset &= ASC_SYN_MAX_OFFSET;
+ sdtr_buf.req_ack_offset = sdtr_offset;
+ if ((sdtr_period_index =
+ AscGetSynPeriodIndex(asc_dvc, sdtr_period)) <=
+ asc_dvc->max_sdtr_index) {
+ AscMemWordCopyPtrToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (uchar *) &sdtr_buf,
+ sizeof (EXT_MSG) >> 1);
+ return ((sdtr_period_index << 4) | sdtr_offset);
+ } else {
+
+ sdtr_buf.req_ack_offset = 0;
+ AscMemWordCopyPtrToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (uchar *) &sdtr_buf,
+ sizeof (EXT_MSG) >> 1);
+ return (0);
+ }
+}
+
+STATIC uchar
+AscCalSDTRData(
+ ASC_DVC_VAR *asc_dvc,
+ uchar sdtr_period,
+ uchar syn_offset
+)
+{
+ uchar byte;
+ uchar sdtr_period_ix;
+
+ sdtr_period_ix = AscGetSynPeriodIndex(asc_dvc, sdtr_period);
+ if (
+ (sdtr_period_ix > asc_dvc->max_sdtr_index)
+) {
+ return (0xFF);
+ }
+ byte = (sdtr_period_ix << 4) | (syn_offset & ASC_SYN_MAX_OFFSET);
+ return (byte);
+}
+
+STATIC void
+AscSetChipSDTR(
+ PortAddr iop_base,
+ uchar sdtr_data,
+ uchar tid_no
+)
+{
+ AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
+ AscPutMCodeSDTRDoneAtID(iop_base, tid_no, sdtr_data);
+ return;
+}
+
+STATIC uchar
+AscGetSynPeriodIndex(
+ ASC_DVC_VAR *asc_dvc,
+ uchar syn_time
+)
+{
+ uchar *period_table;
+ int max_index;
+ int min_index;
+ int i;
+
+ period_table = asc_dvc->sdtr_period_tbl;
+ max_index = (int) asc_dvc->max_sdtr_index;
+ min_index = (int)asc_dvc->host_init_sdtr_index;
+ if ((syn_time <= period_table[max_index])) {
+ for (i = min_index; i < (max_index - 1); i++) {
+ if (syn_time <= period_table[i]) {
+ return ((uchar) i);
+ }
+ }
+ return ((uchar) max_index);
+ } else {
+ return ((uchar) (max_index + 1));
+ }
+}
+
+STATIC uchar
+AscAllocFreeQueue(
+ PortAddr iop_base,
+ uchar free_q_head
+)
+{
+ ushort q_addr;
+ uchar next_qp;
+ uchar q_status;
+
+ q_addr = ASC_QNO_TO_QADDR(free_q_head);
+ q_status = (uchar) AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_STATUS));
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FWD));
+ if (((q_status & QS_READY) == 0) && (next_qp != ASC_QLINK_END)) {
+ return (next_qp);
+ }
+ return (ASC_QLINK_END);
+}
+
+STATIC uchar
+AscAllocMultipleFreeQueue(
+ PortAddr iop_base,
+ uchar free_q_head,
+ uchar n_free_q
+)
+{
+ uchar i;
+
+ for (i = 0; i < n_free_q; i++) {
+ if ((free_q_head = AscAllocFreeQueue(iop_base, free_q_head))
+ == ASC_QLINK_END) {
+ return (ASC_QLINK_END);
+ }
+ }
+ return (free_q_head);
+}
+
+STATIC int
+AscHostReqRiscHalt(
+ PortAddr iop_base
+)
+{
+ int count = 0;
+ int sta = 0;
+ uchar saved_stop_code;
+
+ if (AscIsChipHalted(iop_base))
+ return (1);
+ saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP
+);
+ do {
+ if (AscIsChipHalted(iop_base)) {
+ sta = 1;
+ break;
+ }
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code);
+ return (sta);
+}
+
+STATIC int
+AscStopQueueExe(
+ PortAddr iop_base
+)
+{
+ int count = 0;
+
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) == 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_REQ_RISC_STOP);
+ do {
+ if (
+ AscReadLramByte(iop_base, ASCV_STOP_CODE_B) &
+ ASC_STOP_ACK_RISC_STOP) {
+ return (1);
+ }
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (0);
+}
+
+STATIC void
+DvcDelayMicroSecond(ADV_DVC_VAR *asc_dvc, ushort micro_sec)
+{
+ udelay(micro_sec);
+}
+
+STATIC void
+DvcDelayNanoSecond(ASC_DVC_VAR *asc_dvc, ASC_DCNT nano_sec)
+{
+ udelay((nano_sec + 999)/1000);
+}
+
+#ifdef CONFIG_ISA
+STATIC ASC_DCNT __init
+AscGetEisaProductID(
+ PortAddr iop_base)
+{
+ PortAddr eisa_iop;
+ ushort product_id_high, product_id_low;
+ ASC_DCNT product_id;
+
+ eisa_iop = ASC_GET_EISA_SLOT(iop_base) | ASC_EISA_PID_IOP_MASK;
+ product_id_low = inpw(eisa_iop);
+ product_id_high = inpw(eisa_iop + 2);
+ product_id = ((ASC_DCNT) product_id_high << 16) |
+ (ASC_DCNT) product_id_low;
+ return (product_id);
+}
+
+STATIC PortAddr __init
+AscSearchIOPortAddrEISA(
+ PortAddr iop_base)
+{
+ ASC_DCNT eisa_product_id;
+
+ if (iop_base == 0) {
+ iop_base = ASC_EISA_MIN_IOP_ADDR;
+ } else {
+ if (iop_base == ASC_EISA_MAX_IOP_ADDR)
+ return (0);
+ if ((iop_base & 0x0050) == 0x0050) {
+ iop_base += ASC_EISA_BIG_IOP_GAP;
+ } else {
+ iop_base += ASC_EISA_SMALL_IOP_GAP;
+ }
+ }
+ while (iop_base <= ASC_EISA_MAX_IOP_ADDR) {
+ eisa_product_id = AscGetEisaProductID(iop_base);
+ if ((eisa_product_id == ASC_EISA_ID_740) ||
+ (eisa_product_id == ASC_EISA_ID_750)) {
+ if (AscFindSignature(iop_base)) {
+ inpw(iop_base + 4);
+ return (iop_base);
+ }
+ }
+ if (iop_base == ASC_EISA_MAX_IOP_ADDR)
+ return (0);
+ if ((iop_base & 0x0050) == 0x0050) {
+ iop_base += ASC_EISA_BIG_IOP_GAP;
+ } else {
+ iop_base += ASC_EISA_SMALL_IOP_GAP;
+ }
+ }
+ return (0);
+}
+#endif /* CONFIG_ISA */
+
+STATIC int
+AscStartChip(
+ PortAddr iop_base
+)
+{
+ AscSetChipControl(iop_base, 0);
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
+ return (0);
+ }
+ return (1);
+}
+
+STATIC int
+AscStopChip(
+ PortAddr iop_base
+)
+{
+ uchar cc_val;
+
+ cc_val = AscGetChipControl(iop_base) & (~(CC_SINGLE_STEP | CC_TEST | CC_DIAG));
+ AscSetChipControl(iop_base, (uchar) (cc_val | CC_HALT));
+ AscSetChipIH(iop_base, INS_HALT);
+ AscSetChipIH(iop_base, INS_RFLAG_WTM);
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) {
+ return (0);
+ }
+ return (1);
+}
+
+STATIC int
+AscIsChipHalted(
+ PortAddr iop_base
+)
+{
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
+ if ((AscGetChipControl(iop_base) & CC_HALT) != 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+STATIC void
+AscSetChipIH(
+ PortAddr iop_base,
+ ushort ins_code
+)
+{
+ AscSetBank(iop_base, 1);
+ AscWriteChipIH(iop_base, ins_code);
+ AscSetBank(iop_base, 0);
+ return;
+}
+
+STATIC void
+AscAckInterrupt(
+ PortAddr iop_base
+)
+{
+ uchar host_flag;
+ uchar risc_flag;
+ ushort loop;
+
+ loop = 0;
+ do {
+ risc_flag = AscReadLramByte(iop_base, ASCV_RISC_FLAG_B);
+ if (loop++ > 0x7FFF) {
+ break;
+ }
+ } while ((risc_flag & ASC_RISC_FLAG_GEN_INT) != 0);
+ host_flag = AscReadLramByte(iop_base, ASCV_HOST_FLAG_B) & (~ASC_HOST_FLAG_ACK_INT);
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B,
+ (uchar) (host_flag | ASC_HOST_FLAG_ACK_INT));
+ AscSetChipStatus(iop_base, CIW_INT_ACK);
+ loop = 0;
+ while (AscGetChipStatus(iop_base) & CSW_INT_PENDING) {
+ AscSetChipStatus(iop_base, CIW_INT_ACK);
+ if (loop++ > 3) {
+ break;
+ }
+ }
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
+ return;
+}
+
+STATIC void
+AscDisableInterrupt(
+ PortAddr iop_base
+)
+{
+ ushort cfg;
+
+ cfg = AscGetChipCfgLsw(iop_base);
+ AscSetChipCfgLsw(iop_base, cfg & (~ASC_CFG0_HOST_INT_ON));
+ return;
+}
+
+STATIC void
+AscEnableInterrupt(
+ PortAddr iop_base
+)
+{
+ ushort cfg;
+
+ cfg = AscGetChipCfgLsw(iop_base);
+ AscSetChipCfgLsw(iop_base, cfg | ASC_CFG0_HOST_INT_ON);
+ return;
+}
+
+
+
+STATIC void
+AscSetBank(
+ PortAddr iop_base,
+ uchar bank
+)
+{
+ uchar val;
+
+ val = AscGetChipControl(iop_base) &
+ (~(CC_SINGLE_STEP | CC_TEST | CC_DIAG | CC_SCSI_RESET | CC_CHIP_RESET));
+ if (bank == 1) {
+ val |= CC_BANK_ONE;
+ } else if (bank == 2) {
+ val |= CC_DIAG | CC_BANK_ONE;
+ } else {
+ val &= ~CC_BANK_ONE;
+ }
+ AscSetChipControl(iop_base, val);
+ return;
+}
+
+STATIC int
+AscResetChipAndScsiBus(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ PortAddr iop_base;
+ int i = 10;
+
+ iop_base = asc_dvc->iop_base;
+ while ((AscGetChipStatus(iop_base) & CSW_SCSI_RESET_ACTIVE) && (i-- > 0))
+ {
+ DvcSleepMilliSecond(100);
+ }
+ AscStopChip(iop_base);
+ AscSetChipControl(iop_base, CC_CHIP_RESET | CC_SCSI_RESET | CC_HALT);
+ DvcDelayNanoSecond(asc_dvc, 60000);
+ AscSetChipIH(iop_base, INS_RFLAG_WTM);
+ AscSetChipIH(iop_base, INS_HALT);
+ AscSetChipControl(iop_base, CC_CHIP_RESET | CC_HALT);
+ AscSetChipControl(iop_base, CC_HALT);
+ DvcSleepMilliSecond(200);
+ AscSetChipStatus(iop_base, CIW_CLR_SCSI_RESET_INT);
+ AscSetChipStatus(iop_base, 0);
+ return (AscIsChipHalted(iop_base));
+}
+
+STATIC ASC_DCNT __init
+AscGetMaxDmaCount(
+ ushort bus_type)
+{
+ if (bus_type & ASC_IS_ISA)
+ return (ASC_MAX_ISA_DMA_COUNT);
+ else if (bus_type & (ASC_IS_EISA | ASC_IS_VL))
+ return (ASC_MAX_VL_DMA_COUNT);
+ return (ASC_MAX_PCI_DMA_COUNT);
+}
+
+#ifdef CONFIG_ISA
+STATIC ushort __init
+AscGetIsaDmaChannel(
+ PortAddr iop_base)
+{
+ ushort channel;
+
+ channel = AscGetChipCfgLsw(iop_base) & 0x0003;
+ if (channel == 0x03)
+ return (0);
+ else if (channel == 0x00)
+ return (7);
+ return (channel + 4);
+}
+
+STATIC ushort __init
+AscSetIsaDmaChannel(
+ PortAddr iop_base,
+ ushort dma_channel)
+{
+ ushort cfg_lsw;
+ uchar value;
+
+ if ((dma_channel >= 5) && (dma_channel <= 7)) {
+ if (dma_channel == 7)
+ value = 0x00;
+ else
+ value = dma_channel - 4;
+ cfg_lsw = AscGetChipCfgLsw(iop_base) & 0xFFFC;
+ cfg_lsw |= value;
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetIsaDmaChannel(iop_base));
+ }
+ return (0);
+}
+
+STATIC uchar __init
+AscSetIsaDmaSpeed(
+ PortAddr iop_base,
+ uchar speed_value)
+{
+ speed_value &= 0x07;
+ AscSetBank(iop_base, 1);
+ AscWriteChipDmaSpeed(iop_base, speed_value);
+ AscSetBank(iop_base, 0);
+ return (AscGetIsaDmaSpeed(iop_base));
+}
+
+STATIC uchar __init
+AscGetIsaDmaSpeed(
+ PortAddr iop_base
+)
+{
+ uchar speed_value;
+
+ AscSetBank(iop_base, 1);
+ speed_value = AscReadChipDmaSpeed(iop_base);
+ speed_value &= 0x07;
+ AscSetBank(iop_base, 0);
+ return (speed_value);
+}
+#endif /* CONFIG_ISA */
+
+STATIC ushort __init
+AscReadPCIConfigWord(
+ ASC_DVC_VAR *asc_dvc,
+ ushort pci_config_offset)
+{
+ uchar lsb, msb;
+
+ lsb = DvcReadPCIConfigByte(asc_dvc, pci_config_offset);
+ msb = DvcReadPCIConfigByte(asc_dvc, pci_config_offset + 1);
+ return ((ushort) ((msb << 8) | lsb));
+}
+
+STATIC ushort __init
+AscInitGetConfig(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ ushort warn_code;
+ PortAddr iop_base;
+ ushort PCIDeviceID;
+ ushort PCIVendorID;
+ uchar PCIRevisionID;
+ uchar prevCmdRegBits;
+
+ warn_code = 0;
+ iop_base = asc_dvc->iop_base;
+ asc_dvc->init_state = ASC_INIT_STATE_BEG_GET_CFG;
+ if (asc_dvc->err_code != 0) {
+ return (UW_ERR);
+ }
+ if (asc_dvc->bus_type == ASC_IS_PCI) {
+ PCIVendorID = AscReadPCIConfigWord(asc_dvc,
+ AscPCIConfigVendorIDRegister);
+
+ PCIDeviceID = AscReadPCIConfigWord(asc_dvc,
+ AscPCIConfigDeviceIDRegister);
+
+ PCIRevisionID = DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigRevisionIDRegister);
+
+ if (PCIVendorID != ASC_PCI_VENDORID) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ prevCmdRegBits = DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister);
+
+ if ((prevCmdRegBits & AscPCICmdRegBits_IOMemBusMaster) !=
+ AscPCICmdRegBits_IOMemBusMaster) {
+ DvcWritePCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister,
+ (prevCmdRegBits |
+ AscPCICmdRegBits_IOMemBusMaster));
+
+ if ((DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister)
+ & AscPCICmdRegBits_IOMemBusMaster)
+ != AscPCICmdRegBits_IOMemBusMaster) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+ if ((PCIDeviceID == ASC_PCI_DEVICEID_1200A) ||
+ (PCIDeviceID == ASC_PCI_DEVICEID_1200B)) {
+ DvcWritePCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer, 0x00);
+ if (DvcReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer)
+ != 0x00) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ } else if (PCIDeviceID == ASC_PCI_DEVICEID_ULTRA) {
+ if (DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer) < 0x20) {
+ DvcWritePCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer, 0x20);
+
+ if (DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer) < 0x20) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+ }
+ }
+
+ if (AscFindSignature(iop_base)) {
+ warn_code |= AscInitAscDvcVar(asc_dvc);
+ warn_code |= AscInitFromEEP(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG;
+ if (asc_dvc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT) {
+ asc_dvc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT;
+ }
+ } else {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ }
+ return(warn_code);
+}
+
+STATIC ushort __init
+AscInitSetConfig(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ ushort warn_code = 0;
+
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_SET_CFG;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (AscFindSignature(asc_dvc->iop_base)) {
+ warn_code |= AscInitFromAscDvcVar(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_SET_CFG;
+ } else {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ }
+ return (warn_code);
+}
+
+STATIC ushort __init
+AscInitFromAscDvcVar(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ PortAddr iop_base;
+ ushort cfg_msw;
+ ushort warn_code;
+ ushort pci_device_id = 0;
+
+ iop_base = asc_dvc->iop_base;
+#ifdef CONFIG_PCI
+ if (asc_dvc->cfg->dev)
+ pci_device_id = to_pci_dev(asc_dvc->cfg->dev)->device;
+#endif
+ warn_code = 0;
+ cfg_msw = AscGetChipCfgMsw(iop_base);
+ if ((cfg_msw & ASC_CFG_MSW_CLR_MASK) != 0) {
+ cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+ warn_code |= ASC_WARN_CFG_MSW_RECOVER;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ }
+ if ((asc_dvc->cfg->cmd_qng_enabled & asc_dvc->cfg->disc_enable) !=
+ asc_dvc->cfg->cmd_qng_enabled) {
+ asc_dvc->cfg->disc_enable = asc_dvc->cfg->cmd_qng_enabled;
+ warn_code |= ASC_WARN_CMD_QNG_CONFLICT;
+ }
+ if (AscGetChipStatus(iop_base) & CSW_AUTO_CONFIG) {
+ warn_code |= ASC_WARN_AUTO_CONFIG;
+ }
+ if ((asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL)) != 0) {
+ if (AscSetChipIRQ(iop_base, asc_dvc->irq_no, asc_dvc->bus_type)
+ != asc_dvc->irq_no) {
+ asc_dvc->err_code |= ASC_IERR_SET_IRQ_NO;
+ }
+ }
+ if (asc_dvc->bus_type & ASC_IS_PCI) {
+ cfg_msw &= 0xFFC0;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ if ((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA) {
+ } else {
+ if ((pci_device_id == ASC_PCI_DEVICE_ID_REV_A) ||
+ (pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_IF_NOT_DWB;
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN;
+ }
+ }
+ } else if (asc_dvc->bus_type == ASC_IS_ISAPNP) {
+ if (AscGetChipVersion(iop_base, asc_dvc->bus_type)
+ == ASC_CHIP_VER_ASYN_BUG) {
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN;
+ }
+ }
+ if (AscSetChipScsiID(iop_base, asc_dvc->cfg->chip_scsi_id) !=
+ asc_dvc->cfg->chip_scsi_id) {
+ asc_dvc->err_code |= ASC_IERR_SET_SCSI_ID;
+ }
+#ifdef CONFIG_ISA
+ if (asc_dvc->bus_type & ASC_IS_ISA) {
+ AscSetIsaDmaChannel(iop_base, asc_dvc->cfg->isa_dma_channel);
+ AscSetIsaDmaSpeed(iop_base, asc_dvc->cfg->isa_dma_speed);
+ }
+#endif /* CONFIG_ISA */
+ return (warn_code);
+}
+
+STATIC ushort
+AscInitAsc1000Driver(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ ushort warn_code;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_RESET_SCSI) &&
+ !(asc_dvc->init_state & ASC_INIT_RESET_SCSI_DONE)) {
+ AscResetChipAndScsiBus(asc_dvc);
+ DvcSleepMilliSecond((ASC_DCNT)
+ ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ }
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (!AscFindSignature(asc_dvc->iop_base)) {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ return (warn_code);
+ }
+ AscDisableInterrupt(iop_base);
+ warn_code |= AscInitLram(asc_dvc);
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ ASC_DBG1(1, "AscInitAsc1000Driver: _asc_mcode_chksum 0x%lx\n",
+ (ulong) _asc_mcode_chksum);
+ if (AscLoadMicroCode(iop_base, 0, _asc_mcode_buf,
+ _asc_mcode_size) != _asc_mcode_chksum) {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return (warn_code);
+ }
+ warn_code |= AscInitMicroCodeVar(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_LOAD_MC;
+ AscEnableInterrupt(iop_base);
+ return (warn_code);
+}
+
+STATIC ushort __init
+AscInitAscDvcVar(
+ ASC_DVC_VAR *asc_dvc)
+{
+ int i;
+ PortAddr iop_base;
+ ushort warn_code;
+ uchar chip_version;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ asc_dvc->err_code = 0;
+ if ((asc_dvc->bus_type &
+ (ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
+ asc_dvc->err_code |= ASC_IERR_NO_BUS_TYPE;
+ }
+ AscSetChipControl(iop_base, CC_HALT);
+ AscSetChipStatus(iop_base, 0);
+ asc_dvc->bug_fix_cntl = 0;
+ asc_dvc->pci_fix_asyn_xfer = 0;
+ asc_dvc->pci_fix_asyn_xfer_always = 0;
+ /* asc_dvc->init_state initalized in AscInitGetConfig(). */
+ asc_dvc->sdtr_done = 0;
+ asc_dvc->cur_total_qng = 0;
+ asc_dvc->is_in_int = 0;
+ asc_dvc->in_critical_cnt = 0;
+ asc_dvc->last_q_shortage = 0;
+ asc_dvc->use_tagged_qng = 0;
+ asc_dvc->no_scam = 0;
+ asc_dvc->unit_not_ready = 0;
+ asc_dvc->queue_full_or_busy = 0;
+ asc_dvc->redo_scam = 0;
+ asc_dvc->res2 = 0;
+ asc_dvc->host_init_sdtr_index = 0;
+ asc_dvc->cfg->can_tagged_qng = 0;
+ asc_dvc->cfg->cmd_qng_enabled = 0;
+ asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL;
+ asc_dvc->init_sdtr = 0;
+ asc_dvc->max_total_qng = ASC_DEF_MAX_TOTAL_QNG;
+ asc_dvc->scsi_reset_wait = 3;
+ asc_dvc->start_motor = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type);
+ asc_dvc->cfg->sdtr_enable = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->chip_scsi_id = ASC_DEF_CHIP_SCSI_ID;
+ asc_dvc->cfg->lib_serial_no = ASC_LIB_SERIAL_NUMBER;
+ asc_dvc->cfg->lib_version = (ASC_LIB_VERSION_MAJOR << 8) |
+ ASC_LIB_VERSION_MINOR;
+ chip_version = AscGetChipVersion(iop_base, asc_dvc->bus_type);
+ asc_dvc->cfg->chip_version = chip_version;
+ asc_dvc->sdtr_period_tbl[0] = SYN_XFER_NS_0;
+ asc_dvc->sdtr_period_tbl[1] = SYN_XFER_NS_1;
+ asc_dvc->sdtr_period_tbl[2] = SYN_XFER_NS_2;
+ asc_dvc->sdtr_period_tbl[3] = SYN_XFER_NS_3;
+ asc_dvc->sdtr_period_tbl[4] = SYN_XFER_NS_4;
+ asc_dvc->sdtr_period_tbl[5] = SYN_XFER_NS_5;
+ asc_dvc->sdtr_period_tbl[6] = SYN_XFER_NS_6;
+ asc_dvc->sdtr_period_tbl[7] = SYN_XFER_NS_7;
+ asc_dvc->max_sdtr_index = 7;
+ if ((asc_dvc->bus_type & ASC_IS_PCI) &&
+ (chip_version >= ASC_CHIP_VER_PCI_ULTRA_3150)) {
+ asc_dvc->bus_type = ASC_IS_PCI_ULTRA;
+ asc_dvc->sdtr_period_tbl[0] = SYN_ULTRA_XFER_NS_0;
+ asc_dvc->sdtr_period_tbl[1] = SYN_ULTRA_XFER_NS_1;
+ asc_dvc->sdtr_period_tbl[2] = SYN_ULTRA_XFER_NS_2;
+ asc_dvc->sdtr_period_tbl[3] = SYN_ULTRA_XFER_NS_3;
+ asc_dvc->sdtr_period_tbl[4] = SYN_ULTRA_XFER_NS_4;
+ asc_dvc->sdtr_period_tbl[5] = SYN_ULTRA_XFER_NS_5;
+ asc_dvc->sdtr_period_tbl[6] = SYN_ULTRA_XFER_NS_6;
+ asc_dvc->sdtr_period_tbl[7] = SYN_ULTRA_XFER_NS_7;
+ asc_dvc->sdtr_period_tbl[8] = SYN_ULTRA_XFER_NS_8;
+ asc_dvc->sdtr_period_tbl[9] = SYN_ULTRA_XFER_NS_9;
+ asc_dvc->sdtr_period_tbl[10] = SYN_ULTRA_XFER_NS_10;
+ asc_dvc->sdtr_period_tbl[11] = SYN_ULTRA_XFER_NS_11;
+ asc_dvc->sdtr_period_tbl[12] = SYN_ULTRA_XFER_NS_12;
+ asc_dvc->sdtr_period_tbl[13] = SYN_ULTRA_XFER_NS_13;
+ asc_dvc->sdtr_period_tbl[14] = SYN_ULTRA_XFER_NS_14;
+ asc_dvc->sdtr_period_tbl[15] = SYN_ULTRA_XFER_NS_15;
+ asc_dvc->max_sdtr_index = 15;
+ if (chip_version == ASC_CHIP_VER_PCI_ULTRA_3150)
+ {
+ AscSetExtraControl(iop_base,
+ (SEC_ACTIVE_NEGATE | SEC_SLEW_RATE));
+ } else if (chip_version >= ASC_CHIP_VER_PCI_ULTRA_3050) {
+ AscSetExtraControl(iop_base,
+ (SEC_ACTIVE_NEGATE | SEC_ENABLE_FILTER));
+ }
+ }
+ if (asc_dvc->bus_type == ASC_IS_PCI) {
+ AscSetExtraControl(iop_base, (SEC_ACTIVE_NEGATE | SEC_SLEW_RATE));
+ }
+
+ asc_dvc->cfg->isa_dma_speed = ASC_DEF_ISA_DMA_SPEED;
+ if (AscGetChipBusType(iop_base) == ASC_IS_ISAPNP) {
+ AscSetChipIFC(iop_base, IFC_INIT_DEFAULT);
+ asc_dvc->bus_type = ASC_IS_ISAPNP;
+ }
+#ifdef CONFIG_ISA
+ if ((asc_dvc->bus_type & ASC_IS_ISA) != 0) {
+ asc_dvc->cfg->isa_dma_channel = (uchar) AscGetIsaDmaChannel(iop_base);
+ }
+#endif /* CONFIG_ISA */
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->cur_dvc_qng[i] = 0;
+ asc_dvc->max_dvc_qng[i] = ASC_MAX_SCSI1_QNG;
+ asc_dvc->scsiq_busy_head[i] = (ASC_SCSI_Q *) 0L;
+ asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q *) 0L;
+ asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG;
+ }
+ return (warn_code);
+}
+
+STATIC ushort __init
+AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
+{
+ ASCEEP_CONFIG eep_config_buf;
+ ASCEEP_CONFIG *eep_config;
+ PortAddr iop_base;
+ ushort chksum;
+ ushort warn_code;
+ ushort cfg_msw, cfg_lsw;
+ int i;
+ int write_eep = 0;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE);
+ AscStopQueueExe(iop_base);
+ if ((AscStopChip(iop_base) == FALSE) ||
+ (AscGetChipScsiCtrl(iop_base) != 0)) {
+ asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE;
+ AscResetChipAndScsiBus(asc_dvc);
+ DvcSleepMilliSecond((ASC_DCNT)
+ ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ }
+ if (AscIsChipHalted(iop_base) == FALSE) {
+ asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
+ return (warn_code);
+ }
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
+ return (warn_code);
+ }
+ eep_config = (ASCEEP_CONFIG *) &eep_config_buf;
+ cfg_msw = AscGetChipCfgMsw(iop_base);
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ if ((cfg_msw & ASC_CFG_MSW_CLR_MASK) != 0) {
+ cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+ warn_code |= ASC_WARN_CFG_MSW_RECOVER;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ }
+ chksum = AscGetEEPConfig(iop_base, eep_config, asc_dvc->bus_type);
+ ASC_DBG1(1, "AscInitFromEEP: chksum 0x%x\n", chksum);
+ if (chksum == 0) {
+ chksum = 0xaa55;
+ }
+ if (AscGetChipStatus(iop_base) & CSW_AUTO_CONFIG) {
+ warn_code |= ASC_WARN_AUTO_CONFIG;
+ if (asc_dvc->cfg->chip_version == 3) {
+ if (eep_config->cfg_lsw != cfg_lsw) {
+ warn_code |= ASC_WARN_EEPROM_RECOVER;
+ eep_config->cfg_lsw = AscGetChipCfgLsw(iop_base);
+ }
+ if (eep_config->cfg_msw != cfg_msw) {
+ warn_code |= ASC_WARN_EEPROM_RECOVER;
+ eep_config->cfg_msw = AscGetChipCfgMsw(iop_base);
+ }
+ }
+ }
+ eep_config->cfg_msw &= ~ASC_CFG_MSW_CLR_MASK;
+ eep_config->cfg_lsw |= ASC_CFG0_HOST_INT_ON;
+ ASC_DBG1(1, "AscInitFromEEP: eep_config->chksum 0x%x\n",
+ eep_config->chksum);
+ if (chksum != eep_config->chksum) {
+ if (AscGetChipVersion(iop_base, asc_dvc->bus_type) ==
+ ASC_CHIP_VER_PCI_ULTRA_3050 )
+ {
+ ASC_DBG(1,
+"AscInitFromEEP: chksum error ignored; EEPROM-less board\n");
+ eep_config->init_sdtr = 0xFF;
+ eep_config->disc_enable = 0xFF;
+ eep_config->start_motor = 0xFF;
+ eep_config->use_cmd_qng = 0;
+ eep_config->max_total_qng = 0xF0;
+ eep_config->max_tag_qng = 0x20;
+ eep_config->cntl = 0xBFFF;
+ ASC_EEP_SET_CHIP_ID(eep_config, 7);
+ eep_config->no_scam = 0;
+ eep_config->adapter_info[0] = 0;
+ eep_config->adapter_info[1] = 0;
+ eep_config->adapter_info[2] = 0;
+ eep_config->adapter_info[3] = 0;
+ eep_config->adapter_info[4] = 0;
+ /* Indicate EEPROM-less board. */
+ eep_config->adapter_info[5] = 0xBB;
+ } else {
+ ASC_PRINT(
+"AscInitFromEEP: EEPROM checksum error; Will try to re-write EEPROM.\n");
+ write_eep = 1;
+ warn_code |= ASC_WARN_EEPROM_CHKSUM;
+ }
+ }
+ asc_dvc->cfg->sdtr_enable = eep_config->init_sdtr;
+ asc_dvc->cfg->disc_enable = eep_config->disc_enable;
+ asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng;
+ asc_dvc->cfg->isa_dma_speed = ASC_EEP_GET_DMA_SPD(eep_config);
+ asc_dvc->start_motor = eep_config->start_motor;
+ asc_dvc->dvc_cntl = eep_config->cntl;
+ asc_dvc->no_scam = eep_config->no_scam;
+ asc_dvc->cfg->adapter_info[0] = eep_config->adapter_info[0];
+ asc_dvc->cfg->adapter_info[1] = eep_config->adapter_info[1];
+ asc_dvc->cfg->adapter_info[2] = eep_config->adapter_info[2];
+ asc_dvc->cfg->adapter_info[3] = eep_config->adapter_info[3];
+ asc_dvc->cfg->adapter_info[4] = eep_config->adapter_info[4];
+ asc_dvc->cfg->adapter_info[5] = eep_config->adapter_info[5];
+ if (!AscTestExternalLram(asc_dvc)) {
+ if (((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA)) {
+ eep_config->max_total_qng = ASC_MAX_PCI_ULTRA_INRAM_TOTAL_QNG;
+ eep_config->max_tag_qng = ASC_MAX_PCI_ULTRA_INRAM_TAG_QNG;
+ } else {
+ eep_config->cfg_msw |= 0x0800;
+ cfg_msw |= 0x0800;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ eep_config->max_total_qng = ASC_MAX_PCI_INRAM_TOTAL_QNG;
+ eep_config->max_tag_qng = ASC_MAX_INRAM_TAG_QNG;
+ }
+ } else {
+ }
+ if (eep_config->max_total_qng < ASC_MIN_TOTAL_QNG) {
+ eep_config->max_total_qng = ASC_MIN_TOTAL_QNG;
+ }
+ if (eep_config->max_total_qng > ASC_MAX_TOTAL_QNG) {
+ eep_config->max_total_qng = ASC_MAX_TOTAL_QNG;
+ }
+ if (eep_config->max_tag_qng > eep_config->max_total_qng) {
+ eep_config->max_tag_qng = eep_config->max_total_qng;
+ }
+ if (eep_config->max_tag_qng < ASC_MIN_TAG_Q_PER_DVC) {
+ eep_config->max_tag_qng = ASC_MIN_TAG_Q_PER_DVC;
+ }
+ asc_dvc->max_total_qng = eep_config->max_total_qng;
+ if ((eep_config->use_cmd_qng & eep_config->disc_enable) !=
+ eep_config->use_cmd_qng) {
+ eep_config->disc_enable = eep_config->use_cmd_qng;
+ warn_code |= ASC_WARN_CMD_QNG_CONFLICT;
+ }
+ if (asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL | ASC_IS_EISA)) {
+ asc_dvc->irq_no = AscGetChipIRQ(iop_base, asc_dvc->bus_type);
+ }
+ ASC_EEP_SET_CHIP_ID(eep_config, ASC_EEP_GET_CHIP_ID(eep_config) & ASC_MAX_TID);
+ asc_dvc->cfg->chip_scsi_id = ASC_EEP_GET_CHIP_ID(eep_config);
+ if (((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA) &&
+ !(asc_dvc->dvc_cntl & ASC_CNTL_SDTR_ENABLE_ULTRA)) {
+ asc_dvc->host_init_sdtr_index = ASC_SDTR_ULTRA_PCI_10MB_INDEX;
+ }
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->dos_int13_table[i] = eep_config->dos_int13_table[i];
+ asc_dvc->cfg->max_tag_qng[i] = eep_config->max_tag_qng;
+ asc_dvc->cfg->sdtr_period_offset[i] =
+ (uchar) (ASC_DEF_SDTR_OFFSET |
+ (asc_dvc->host_init_sdtr_index << 4));
+ }
+ eep_config->cfg_msw = AscGetChipCfgMsw(iop_base);
+ if (write_eep) {
+ if ((i = AscSetEEPConfig(iop_base, eep_config, asc_dvc->bus_type)) !=
+ 0) {
+ ASC_PRINT1(
+"AscInitFromEEP: Failed to re-write EEPROM with %d errors.\n", i);
+ } else {
+ ASC_PRINT("AscInitFromEEP: Succesfully re-wrote EEPROM.");
+ }
+ }
+ return (warn_code);
+}
+
+STATIC ushort
+AscInitMicroCodeVar(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ int i;
+ ushort warn_code;
+ PortAddr iop_base;
+ ASC_PADDR phy_addr;
+ ASC_DCNT phy_size;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ AscPutMCodeInitSDTRAtID(iop_base, i,
+ asc_dvc->cfg->sdtr_period_offset[i]
+);
+ }
+
+ AscInitQLinkVar(asc_dvc);
+ AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B,
+ asc_dvc->cfg->disc_enable);
+ AscWriteLramByte(iop_base, ASCV_HOSTSCSI_ID_B,
+ ASC_TID_TO_TARGET_ID(asc_dvc->cfg->chip_scsi_id));
+
+ /* Align overrun buffer on an 8 byte boundary. */
+ phy_addr = virt_to_bus(asc_dvc->cfg->overrun_buf);
+ phy_addr = cpu_to_le32((phy_addr + 7) & ~0x7);
+ AscMemDWordCopyPtrToLram(iop_base, ASCV_OVERRUN_PADDR_D,
+ (uchar *) &phy_addr, 1);
+ phy_size = cpu_to_le32(ASC_OVERRUN_BSIZE - 8);
+ AscMemDWordCopyPtrToLram(iop_base, ASCV_OVERRUN_BSIZE_D,
+ (uchar *) &phy_size, 1);
+
+ asc_dvc->cfg->mcode_date =
+ AscReadLramWord(iop_base, (ushort) ASCV_MC_DATE_W);
+ asc_dvc->cfg->mcode_version =
+ AscReadLramWord(iop_base, (ushort) ASCV_MC_VER_W);
+
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
+ return (warn_code);
+ }
+ if (AscStartChip(iop_base) != 1) {
+ asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
+ return (warn_code);
+ }
+
+ return (warn_code);
+}
+
+STATIC int __init
+AscTestExternalLram(
+ ASC_DVC_VAR *asc_dvc)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ ushort saved_word;
+ int sta;
+
+ iop_base = asc_dvc->iop_base;
+ sta = 0;
+ q_addr = ASC_QNO_TO_QADDR(241);
+ saved_word = AscReadLramWord(iop_base, q_addr);
+ AscSetChipLramAddr(iop_base, q_addr);
+ AscSetChipLramData(iop_base, 0x55AA);
+ DvcSleepMilliSecond(10);
+ AscSetChipLramAddr(iop_base, q_addr);
+ if (AscGetChipLramData(iop_base) == 0x55AA) {
+ sta = 1;
+ AscWriteLramWord(iop_base, q_addr, saved_word);
+ }
+ return (sta);
+}
+
+STATIC int __init
+AscWriteEEPCmdReg(
+ PortAddr iop_base,
+ uchar cmd_reg
+)
+{
+ uchar read_back;
+ int retry;
+
+ retry = 0;
+ while (TRUE) {
+ AscSetChipEEPCmd(iop_base, cmd_reg);
+ DvcSleepMilliSecond(1);
+ read_back = AscGetChipEEPCmd(iop_base);
+ if (read_back == cmd_reg) {
+ return (1);
+ }
+ if (retry++ > ASC_EEP_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+STATIC int __init
+AscWriteEEPDataReg(
+ PortAddr iop_base,
+ ushort data_reg
+)
+{
+ ushort read_back;
+ int retry;
+
+ retry = 0;
+ while (TRUE) {
+ AscSetChipEEPData(iop_base, data_reg);
+ DvcSleepMilliSecond(1);
+ read_back = AscGetChipEEPData(iop_base);
+ if (read_back == data_reg) {
+ return (1);
+ }
+ if (retry++ > ASC_EEP_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+STATIC void __init
+AscWaitEEPRead(void)
+{
+ DvcSleepMilliSecond(1);
+ return;
+}
+
+STATIC void __init
+AscWaitEEPWrite(void)
+{
+ DvcSleepMilliSecond(20);
+ return;
+}
+
+STATIC ushort __init
+AscReadEEPWord(
+ PortAddr iop_base,
+ uchar addr)
+{
+ ushort read_wval;
+ uchar cmd_reg;
+
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_DISABLE);
+ AscWaitEEPRead();
+ cmd_reg = addr | ASC_EEP_CMD_READ;
+ AscWriteEEPCmdReg(iop_base, cmd_reg);
+ AscWaitEEPRead();
+ read_wval = AscGetChipEEPData(iop_base);
+ AscWaitEEPRead();
+ return (read_wval);
+}
+
+STATIC ushort __init
+AscWriteEEPWord(
+ PortAddr iop_base,
+ uchar addr,
+ ushort word_val)
+{
+ ushort read_wval;
+
+ read_wval = AscReadEEPWord(iop_base, addr);
+ if (read_wval != word_val) {
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_ABLE);
+ AscWaitEEPRead();
+ AscWriteEEPDataReg(iop_base, word_val);
+ AscWaitEEPRead();
+ AscWriteEEPCmdReg(iop_base,
+ (uchar) ((uchar) ASC_EEP_CMD_WRITE | addr));
+ AscWaitEEPWrite();
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_DISABLE);
+ AscWaitEEPRead();
+ return (AscReadEEPWord(iop_base, addr));
+ }
+ return (read_wval);
+}
+
+STATIC ushort __init
+AscGetEEPConfig(
+ PortAddr iop_base,
+ ASCEEP_CONFIG * cfg_buf, ushort bus_type)
+{
+ ushort wval;
+ ushort sum;
+ ushort *wbuf;
+ int cfg_beg;
+ int cfg_end;
+ int uchar_end_in_config = ASC_EEP_MAX_DVC_ADDR - 2;
+ int s_addr;
+
+ wbuf = (ushort *) cfg_buf;
+ sum = 0;
+ /* Read two config words; Byte-swapping done by AscReadEEPWord(). */
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ *wbuf = AscReadEEPWord(iop_base, (uchar) s_addr);
+ sum += *wbuf;
+ }
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
+ wval = AscReadEEPWord( iop_base, ( uchar )s_addr ) ;
+ if (s_addr <= uchar_end_in_config) {
+ /*
+ * Swap all char fields - must unswap bytes already swapped
+ * by AscReadEEPWord().
+ */
+ *wbuf = le16_to_cpu(wval);
+ } else {
+ /* Don't swap word field at the end - cntl field. */
+ *wbuf = wval;
+ }
+ sum += wval; /* Checksum treats all EEPROM data as words. */
+ }
+ /*
+ * Read the checksum word which will be compared against 'sum'
+ * by the caller. Word field already swapped.
+ */
+ *wbuf = AscReadEEPWord(iop_base, (uchar) s_addr);
+ return (sum);
+}
+
+STATIC int __init
+AscSetEEPConfigOnce(
+ PortAddr iop_base,
+ ASCEEP_CONFIG * cfg_buf, ushort bus_type)
+{
+ int n_error;
+ ushort *wbuf;
+ ushort word;
+ ushort sum;
+ int s_addr;
+ int cfg_beg;
+ int cfg_end;
+ int uchar_end_in_config = ASC_EEP_MAX_DVC_ADDR - 2;
+
+
+ wbuf = (ushort *) cfg_buf;
+ n_error = 0;
+ sum = 0;
+ /* Write two config words; AscWriteEEPWord() will swap bytes. */
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != AscWriteEEPWord(iop_base, (uchar) s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
+ if (s_addr <= uchar_end_in_config) {
+ /*
+ * This is a char field. Swap char fields before they are
+ * swapped again by AscWriteEEPWord().
+ */
+ word = cpu_to_le16(*wbuf);
+ if (word != AscWriteEEPWord( iop_base, (uchar) s_addr, word)) {
+ n_error++;
+ }
+ } else {
+ /* Don't swap word field at the end - cntl field. */
+ if (*wbuf != AscWriteEEPWord(iop_base, (uchar) s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ sum += *wbuf; /* Checksum calculated from word values. */
+ }
+ /* Write checksum word. It will be swapped by AscWriteEEPWord(). */
+ *wbuf = sum;
+ if (sum != AscWriteEEPWord(iop_base, (uchar) s_addr, sum)) {
+ n_error++;
+ }
+
+ /* Read EEPROM back again. */
+ wbuf = (ushort *) cfg_buf;
+ /*
+ * Read two config words; Byte-swapping done by AscReadEEPWord().
+ */
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ if (*wbuf != AscReadEEPWord(iop_base, (uchar) s_addr)) {
+ n_error++;
+ }
+ }
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
+ if (s_addr <= uchar_end_in_config) {
+ /*
+ * Swap all char fields. Must unswap bytes already swapped
+ * by AscReadEEPWord().
+ */
+ word = le16_to_cpu(AscReadEEPWord(iop_base, (uchar) s_addr));
+ } else {
+ /* Don't swap word field at the end - cntl field. */
+ word = AscReadEEPWord(iop_base, (uchar) s_addr);
+ }
+ if (*wbuf != word) {
+ n_error++;
+ }
+ }
+ /* Read checksum; Byte swapping not needed. */
+ if (AscReadEEPWord(iop_base, (uchar) s_addr) != sum) {
+ n_error++;
+ }
+ return (n_error);
+}
+
+STATIC int __init
+AscSetEEPConfig(
+ PortAddr iop_base,
+ ASCEEP_CONFIG * cfg_buf, ushort bus_type
+)
+{
+ int retry;
+ int n_error;
+
+ retry = 0;
+ while (TRUE) {
+ if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf,
+ bus_type)) == 0) {
+ break;
+ }
+ if (++retry > ASC_EEP_MAX_RETRY) {
+ break;
+ }
+ }
+ return (n_error);
+}
+
+STATIC void
+AscAsyncFix(
+ ASC_DVC_VAR *asc_dvc,
+ uchar tid_no,
+ ASC_SCSI_INQUIRY *inq)
+{
+ uchar dvc_type;
+ ASC_SCSI_BIT_ID_TYPE tid_bits;
+
+ dvc_type = ASC_INQ_DVC_TYPE(inq);
+ tid_bits = ASC_TIX_TO_TARGET_ID(tid_no);
+
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN)
+ {
+ if (!(asc_dvc->init_sdtr & tid_bits))
+ {
+ if ((dvc_type == TYPE_ROM) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "HP ", 3) == 0))
+ {
+ asc_dvc->pci_fix_asyn_xfer_always |= tid_bits;
+ }
+ asc_dvc->pci_fix_asyn_xfer |= tid_bits;
+ if ((dvc_type == TYPE_PROCESSOR) ||
+ (dvc_type == TYPE_SCANNER) ||
+ (dvc_type == TYPE_ROM) ||
+ (dvc_type == TYPE_TAPE))
+ {
+ asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+ }
+
+ if (asc_dvc->pci_fix_asyn_xfer & tid_bits)
+ {
+ AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
+ ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ }
+ }
+ return;
+}
+
+STATIC int
+AscTagQueuingSafe(ASC_SCSI_INQUIRY *inq)
+{
+ if ((inq->add_len >= 32) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "QUANTUM XP34301", 15) == 0) &&
+ (AscCompareString((uchar *) inq->product_rev_level,
+ (uchar *) "1071", 4) == 0))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+STATIC void
+AscInquiryHandling(ASC_DVC_VAR *asc_dvc,
+ uchar tid_no, ASC_SCSI_INQUIRY *inq)
+{
+ ASC_SCSI_BIT_ID_TYPE tid_bit = ASC_TIX_TO_TARGET_ID(tid_no);
+ ASC_SCSI_BIT_ID_TYPE orig_init_sdtr, orig_use_tagged_qng;
+
+ orig_init_sdtr = asc_dvc->init_sdtr;
+ orig_use_tagged_qng = asc_dvc->use_tagged_qng;
+
+ asc_dvc->init_sdtr &= ~tid_bit;
+ asc_dvc->cfg->can_tagged_qng &= ~tid_bit;
+ asc_dvc->use_tagged_qng &= ~tid_bit;
+
+ if (ASC_INQ_RESPONSE_FMT(inq) >= 2 || ASC_INQ_ANSI_VER(inq) >= 2) {
+ if ((asc_dvc->cfg->sdtr_enable & tid_bit) && ASC_INQ_SYNC(inq)) {
+ asc_dvc->init_sdtr |= tid_bit;
+ }
+ if ((asc_dvc->cfg->cmd_qng_enabled & tid_bit) &&
+ ASC_INQ_CMD_QUEUE(inq)) {
+ if (AscTagQueuingSafe(inq)) {
+ asc_dvc->use_tagged_qng |= tid_bit;
+ asc_dvc->cfg->can_tagged_qng |= tid_bit;
+ }
+ }
+ }
+ if (orig_use_tagged_qng != asc_dvc->use_tagged_qng) {
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_DISC_ENABLE_B,
+ asc_dvc->cfg->disc_enable);
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_USE_TAGGED_QNG_B,
+ asc_dvc->use_tagged_qng);
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_CAN_TAGGED_QNG_B,
+ asc_dvc->cfg->can_tagged_qng);
+
+ asc_dvc->max_dvc_qng[tid_no] =
+ asc_dvc->cfg->max_tag_qng[tid_no];
+ AscWriteLramByte(asc_dvc->iop_base,
+ (ushort) (ASCV_MAX_DVC_QNG_BEG + tid_no),
+ asc_dvc->max_dvc_qng[tid_no]);
+ }
+ if (orig_init_sdtr != asc_dvc->init_sdtr) {
+ AscAsyncFix(asc_dvc, tid_no, inq);
+ }
+ return;
+}
+
+STATIC int
+AscCompareString(
+ uchar *str1,
+ uchar *str2,
+ int len
+)
+{
+ int i;
+ int diff;
+
+ for (i = 0; i < len; i++) {
+ diff = (int) (str1[i] - str2[i]);
+ if (diff != 0)
+ return (diff);
+ }
+ return (0);
+}
+
+STATIC uchar
+AscReadLramByte(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ uchar byte_data;
+ ushort word_data;
+
+ if (isodd_word(addr)) {
+ AscSetChipLramAddr(iop_base, addr - 1);
+ word_data = AscGetChipLramData(iop_base);
+ byte_data = (uchar) ((word_data >> 8) & 0xFF);
+ } else {
+ AscSetChipLramAddr(iop_base, addr);
+ word_data = AscGetChipLramData(iop_base);
+ byte_data = (uchar) (word_data & 0xFF);
+ }
+ return (byte_data);
+}
+STATIC ushort
+AscReadLramWord(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ ushort word_data;
+
+ AscSetChipLramAddr(iop_base, addr);
+ word_data = AscGetChipLramData(iop_base);
+ return (word_data);
+}
+
+#if CC_VERY_LONG_SG_LIST
+STATIC ASC_DCNT
+AscReadLramDWord(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ ushort val_low, val_high;
+ ASC_DCNT dword_data;
+
+ AscSetChipLramAddr(iop_base, addr);
+ val_low = AscGetChipLramData(iop_base);
+ val_high = AscGetChipLramData(iop_base);
+ dword_data = ((ASC_DCNT) val_high << 16) | (ASC_DCNT) val_low;
+ return (dword_data);
+}
+#endif /* CC_VERY_LONG_SG_LIST */
+
+STATIC void
+AscWriteLramWord(
+ PortAddr iop_base,
+ ushort addr,
+ ushort word_val
+)
+{
+ AscSetChipLramAddr(iop_base, addr);
+ AscSetChipLramData(iop_base, word_val);
+ return;
+}
+
+STATIC void
+AscWriteLramByte(
+ PortAddr iop_base,
+ ushort addr,
+ uchar byte_val
+)
+{
+ ushort word_data;
+
+ if (isodd_word(addr)) {
+ addr--;
+ word_data = AscReadLramWord(iop_base, addr);
+ word_data &= 0x00FF;
+ word_data |= (((ushort) byte_val << 8) & 0xFF00);
+ } else {
+ word_data = AscReadLramWord(iop_base, addr);
+ word_data &= 0xFF00;
+ word_data |= ((ushort) byte_val & 0x00FF);
+ }
+ AscWriteLramWord(iop_base, addr, word_data);
+ return;
+}
+
+/*
+ * Copy 2 bytes to LRAM.
+ *
+ * The source data is assumed to be in little-endian order in memory
+ * and is maintained in little-endian order when written to LRAM.
+ */
+STATIC void
+AscMemWordCopyPtrToLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ uchar *s_buffer,
+ int words
+)
+{
+ int i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < 2 * words; i += 2) {
+ /*
+ * On a little-endian system the second argument below
+ * produces a little-endian ushort which is written to
+ * LRAM in little-endian order. On a big-endian system
+ * the second argument produces a big-endian ushort which
+ * is "transparently" byte-swapped by outpw() and written
+ * in little-endian order to LRAM.
+ */
+ outpw(iop_base + IOP_RAM_DATA,
+ ((ushort) s_buffer[i + 1] << 8) | s_buffer[i]);
+ }
+ return;
+}
+
+/*
+ * Copy 4 bytes to LRAM.
+ *
+ * The source data is assumed to be in little-endian order in memory
+ * and is maintained in little-endian order when writen to LRAM.
+ */
+STATIC void
+AscMemDWordCopyPtrToLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ uchar *s_buffer,
+ int dwords
+)
+{
+ int i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < 4 * dwords; i += 4) {
+ outpw(iop_base + IOP_RAM_DATA,
+ ((ushort) s_buffer[i + 1] << 8) | s_buffer[i]); /* LSW */
+ outpw(iop_base + IOP_RAM_DATA,
+ ((ushort) s_buffer[i + 3] << 8) | s_buffer[i + 2]); /* MSW */
+ }
+ return;
+}
+
+/*
+ * Copy 2 bytes from LRAM.
+ *
+ * The source data is assumed to be in little-endian order in LRAM
+ * and is maintained in little-endian order when written to memory.
+ */
+STATIC void
+AscMemWordCopyPtrFromLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ uchar *d_buffer,
+ int words
+)
+{
+ int i;
+ ushort word;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < 2 * words; i += 2) {
+ word = inpw(iop_base + IOP_RAM_DATA);
+ d_buffer[i] = word & 0xff;
+ d_buffer[i + 1] = (word >> 8) & 0xff;
+ }
+ return;
+}
+
+STATIC ASC_DCNT
+AscMemSumLramWord(
+ PortAddr iop_base,
+ ushort s_addr,
+ int words
+)
+{
+ ASC_DCNT sum;
+ int i;
+
+ sum = 0L;
+ for (i = 0; i < words; i++, s_addr += 2) {
+ sum += AscReadLramWord(iop_base, s_addr);
+ }
+ return (sum);
+}
+
+STATIC void
+AscMemWordSetLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort set_wval,
+ int words
+)
+{
+ int i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++) {
+ AscSetChipLramData(iop_base, set_wval);
+ }
+ return;
+}
+
+
+/*
+ * --- Adv Library Functions
+ */
+
+/* a_mcode.h */
+
+/* Microcode buffer is kept after initialization for error recovery. */
+STATIC unsigned char _adv_asc3550_buf[] = {
+ 0x00, 0x00, 0x00, 0xf2, 0x00, 0xf0, 0x00, 0x16, 0x18, 0xe4, 0x00, 0xfc, 0x01, 0x00, 0x48, 0xe4,
+ 0xbe, 0x18, 0x18, 0x80, 0x03, 0xf6, 0x02, 0x00, 0x00, 0xfa, 0xff, 0xff, 0x28, 0x0e, 0x9e, 0xe7,
+ 0xff, 0x00, 0x82, 0xe7, 0x00, 0xea, 0x00, 0xf6, 0x01, 0xe6, 0x09, 0xe7, 0x55, 0xf0, 0x01, 0xf6,
+ 0x01, 0xfa, 0x08, 0x00, 0x03, 0x00, 0x04, 0x00, 0x18, 0xf4, 0x10, 0x00, 0x00, 0xec, 0x85, 0xf0,
+ 0xbc, 0x00, 0xd5, 0xf0, 0x8e, 0x0c, 0x38, 0x54, 0x00, 0xe6, 0x1e, 0xf0, 0x86, 0xf0, 0xb4, 0x00,
+ 0x98, 0x57, 0xd0, 0x01, 0x0c, 0x1c, 0x3e, 0x1c, 0x0c, 0x00, 0xbb, 0x00, 0xaa, 0x18, 0x02, 0x80,
+ 0x32, 0xf0, 0x01, 0xfc, 0x88, 0x0c, 0xc6, 0x12, 0x02, 0x13, 0x18, 0x40, 0x00, 0x57, 0x01, 0xea,
+ 0x3c, 0x00, 0x6c, 0x01, 0x6e, 0x01, 0x04, 0x12, 0x3e, 0x57, 0x00, 0x80, 0x03, 0xe6, 0xb6, 0x00,
+ 0xc0, 0x00, 0x01, 0x01, 0x3e, 0x01, 0xda, 0x0f, 0x22, 0x10, 0x08, 0x12, 0x02, 0x4a, 0xb9, 0x54,
+ 0x03, 0x58, 0x1b, 0x80, 0x30, 0xe4, 0x4b, 0xe4, 0x20, 0x00, 0x32, 0x00, 0x3e, 0x00, 0x80, 0x00,
+ 0x24, 0x01, 0x3c, 0x01, 0x68, 0x01, 0x6a, 0x01, 0x70, 0x01, 0x72, 0x01, 0x74, 0x01, 0x76, 0x01,
+ 0x78, 0x01, 0x62, 0x0a, 0x92, 0x0c, 0x2c, 0x10, 0x2e, 0x10, 0x06, 0x13, 0x4c, 0x1c, 0xbb, 0x55,
+ 0x3c, 0x56, 0x04, 0x80, 0x4a, 0xe4, 0x02, 0xee, 0x5b, 0xf0, 0xb1, 0xf0, 0x03, 0xf7, 0x06, 0xf7,
+ 0x03, 0xfc, 0x0f, 0x00, 0x40, 0x00, 0xbe, 0x00, 0x00, 0x01, 0xb0, 0x08, 0x30, 0x13, 0x64, 0x15,
+ 0x32, 0x1c, 0x38, 0x1c, 0x4e, 0x1c, 0x10, 0x44, 0x02, 0x48, 0x00, 0x4c, 0x04, 0xea, 0x5d, 0xf0,
+ 0x04, 0xf6, 0x02, 0xfc, 0x05, 0x00, 0x34, 0x00, 0x36, 0x00, 0x98, 0x00, 0xcc, 0x00, 0x20, 0x01,
+ 0x4e, 0x01, 0x4e, 0x0b, 0x1e, 0x0e, 0x0c, 0x10, 0x0a, 0x12, 0x04, 0x13, 0x40, 0x13, 0x30, 0x1c,
+ 0x00, 0x4e, 0xbd, 0x56, 0x06, 0x83, 0x00, 0xdc, 0x05, 0xf0, 0x09, 0xf0, 0x59, 0xf0, 0xa7, 0xf0,
+ 0xb8, 0xf0, 0x0e, 0xf7, 0x06, 0x00, 0x19, 0x00, 0x33, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xb5, 0x00,
+ 0xba, 0x00, 0xd0, 0x00, 0xe1, 0x00, 0xe7, 0x00, 0xde, 0x03, 0x56, 0x0a, 0x14, 0x0e, 0x02, 0x10,
+ 0x04, 0x10, 0x0a, 0x10, 0x36, 0x10, 0x0a, 0x13, 0x12, 0x13, 0x52, 0x13, 0x10, 0x15, 0x14, 0x15,
+ 0xac, 0x16, 0x20, 0x1c, 0x34, 0x1c, 0x36, 0x1c, 0x08, 0x44, 0x38, 0x44, 0x91, 0x44, 0x0a, 0x45,
+ 0x48, 0x46, 0x01, 0x48, 0x68, 0x54, 0x83, 0x55, 0xb0, 0x57, 0x01, 0x58, 0x83, 0x59, 0x05, 0xe6,
+ 0x0b, 0xf0, 0x0c, 0xf0, 0x5c, 0xf0, 0x4b, 0xf4, 0x04, 0xf8, 0x05, 0xf8, 0x02, 0xfa, 0x03, 0xfa,
+ 0x04, 0xfc, 0x05, 0xfc, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x9e, 0x00, 0xa8, 0x00,
+ 0xaa, 0x00, 0xb9, 0x00, 0xe0, 0x00, 0x22, 0x01, 0x26, 0x01, 0x79, 0x01, 0x7a, 0x01, 0xc0, 0x01,
+ 0xc2, 0x01, 0x7c, 0x02, 0x5a, 0x03, 0xea, 0x04, 0xe8, 0x07, 0x68, 0x08, 0x69, 0x08, 0xba, 0x08,
+ 0xe9, 0x09, 0x06, 0x0b, 0x3a, 0x0e, 0x00, 0x10, 0x1a, 0x10, 0xed, 0x10, 0xf1, 0x10, 0x06, 0x12,
+ 0x0c, 0x13, 0x16, 0x13, 0x1e, 0x13, 0x82, 0x13, 0x42, 0x14, 0xd6, 0x14, 0x8a, 0x15, 0xc6, 0x17,
+ 0xd2, 0x17, 0x6b, 0x18, 0x12, 0x1c, 0x46, 0x1c, 0x9c, 0x32, 0x00, 0x40, 0x0e, 0x47, 0x48, 0x47,
+ 0x41, 0x48, 0x89, 0x48, 0x80, 0x4c, 0x00, 0x54, 0x44, 0x55, 0xe5, 0x55, 0x14, 0x56, 0x77, 0x57,
+ 0xbf, 0x57, 0x40, 0x5c, 0x06, 0x80, 0x08, 0x90, 0x03, 0xa1, 0xfe, 0x9c, 0xf0, 0x29, 0x02, 0xfe,
+ 0xb8, 0x0c, 0xff, 0x10, 0x00, 0x00, 0xd0, 0xfe, 0xcc, 0x18, 0x00, 0xcf, 0xfe, 0x80, 0x01, 0xff,
+ 0x03, 0x00, 0x00, 0xfe, 0x93, 0x15, 0xfe, 0x0f, 0x05, 0xff, 0x38, 0x00, 0x00, 0xfe, 0x57, 0x24,
+ 0x00, 0xfe, 0x48, 0x00, 0x4f, 0xff, 0x04, 0x00, 0x00, 0x10, 0xff, 0x09, 0x00, 0x00, 0xff, 0x08,
+ 0x01, 0x01, 0xff, 0x08, 0xff, 0xff, 0xff, 0x27, 0x00, 0x00, 0xff, 0x10, 0xff, 0xff, 0xff, 0x0f,
+ 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, 0xff, 0x21, 0x00, 0x00, 0xfe, 0x04, 0xf7, 0xcf,
+ 0x2a, 0x67, 0x0b, 0x01, 0xfe, 0xce, 0x0e, 0xfe, 0x04, 0xf7, 0xcf, 0x67, 0x0b, 0x3c, 0x2a, 0xfe,
+ 0x3d, 0xf0, 0xfe, 0x02, 0x02, 0xfe, 0x20, 0xf0, 0x9c, 0xfe, 0x91, 0xf0, 0xfe, 0xf0, 0x01, 0xfe,
+ 0x90, 0xf0, 0xfe, 0xf0, 0x01, 0xfe, 0x8f, 0xf0, 0x9c, 0x05, 0x51, 0x3b, 0x02, 0xfe, 0xd4, 0x0c,
+ 0x01, 0xfe, 0x44, 0x0d, 0xfe, 0xdd, 0x12, 0xfe, 0xfc, 0x10, 0xfe, 0x28, 0x1c, 0x05, 0xfe, 0xa6,
+ 0x00, 0xfe, 0xd3, 0x12, 0x47, 0x18, 0xfe, 0xa6, 0x00, 0xb5, 0xfe, 0x48, 0xf0, 0xfe, 0x86, 0x02,
+ 0xfe, 0x49, 0xf0, 0xfe, 0xa0, 0x02, 0xfe, 0x4a, 0xf0, 0xfe, 0xbe, 0x02, 0xfe, 0x46, 0xf0, 0xfe,
+ 0x50, 0x02, 0xfe, 0x47, 0xf0, 0xfe, 0x56, 0x02, 0xfe, 0x43, 0xf0, 0xfe, 0x44, 0x02, 0xfe, 0x44,
+ 0xf0, 0xfe, 0x48, 0x02, 0xfe, 0x45, 0xf0, 0xfe, 0x4c, 0x02, 0x17, 0x0b, 0xa0, 0x17, 0x06, 0x18,
+ 0x96, 0x02, 0x29, 0xfe, 0x00, 0x1c, 0xde, 0xfe, 0x02, 0x1c, 0xdd, 0xfe, 0x1e, 0x1c, 0xfe, 0xe9,
+ 0x10, 0x01, 0xfe, 0x20, 0x17, 0xfe, 0xe7, 0x10, 0xfe, 0x06, 0xfc, 0xc7, 0x0a, 0x6b, 0x01, 0x9e,
+ 0x02, 0x29, 0x14, 0x4d, 0x37, 0x97, 0x01, 0xfe, 0x64, 0x0f, 0x0a, 0x6b, 0x01, 0x82, 0xfe, 0xbd,
+ 0x10, 0x0a, 0x6b, 0x01, 0x82, 0xfe, 0xad, 0x10, 0xfe, 0x16, 0x1c, 0xfe, 0x58, 0x1c, 0x17, 0x06,
+ 0x18, 0x96, 0x2a, 0x25, 0x29, 0xfe, 0x3d, 0xf0, 0xfe, 0x02, 0x02, 0x21, 0xfe, 0x94, 0x02, 0xfe,
+ 0x5a, 0x1c, 0xea, 0xfe, 0x14, 0x1c, 0x14, 0xfe, 0x30, 0x00, 0x37, 0x97, 0x01, 0xfe, 0x54, 0x0f,
+ 0x17, 0x06, 0x18, 0x96, 0x02, 0xd0, 0x1e, 0x20, 0x07, 0x10, 0x34, 0xfe, 0x69, 0x10, 0x17, 0x06,
+ 0x18, 0x96, 0xfe, 0x04, 0xec, 0x20, 0x46, 0x3d, 0x12, 0x20, 0xfe, 0x05, 0xf6, 0xc7, 0x01, 0xfe,
+ 0x52, 0x16, 0x09, 0x4a, 0x4c, 0x35, 0x11, 0x2d, 0x3c, 0x8a, 0x01, 0xe6, 0x02, 0x29, 0x0a, 0x40,
+ 0x01, 0x0e, 0x07, 0x00, 0x5d, 0x01, 0x6f, 0xfe, 0x18, 0x10, 0xfe, 0x41, 0x58, 0x0a, 0x99, 0x01,
+ 0x0e, 0xfe, 0xc8, 0x54, 0x64, 0xfe, 0x0c, 0x03, 0x01, 0xe6, 0x02, 0x29, 0x2a, 0x46, 0xfe, 0x02,
+ 0xe8, 0x27, 0xf8, 0xfe, 0x9e, 0x43, 0xf7, 0xfe, 0x27, 0xf0, 0xfe, 0xdc, 0x01, 0xfe, 0x07, 0x4b,
+ 0xfe, 0x20, 0xf0, 0x9c, 0xfe, 0x40, 0x1c, 0x25, 0xd2, 0xfe, 0x26, 0xf0, 0xfe, 0x56, 0x03, 0xfe,
+ 0xa0, 0xf0, 0xfe, 0x44, 0x03, 0xfe, 0x11, 0xf0, 0x9c, 0xfe, 0xef, 0x10, 0xfe, 0x9f, 0xf0, 0xfe,
+ 0x64, 0x03, 0xeb, 0x0f, 0xfe, 0x11, 0x00, 0x02, 0x5a, 0x2a, 0xfe, 0x48, 0x1c, 0xeb, 0x09, 0x04,
+ 0x1d, 0xfe, 0x18, 0x13, 0x23, 0x1e, 0x98, 0xac, 0x12, 0x98, 0x0a, 0x40, 0x01, 0x0e, 0xac, 0x75,
+ 0x01, 0xfe, 0xbc, 0x15, 0x11, 0xca, 0x25, 0xd2, 0xfe, 0x01, 0xf0, 0xd2, 0xfe, 0x82, 0xf0, 0xfe,
+ 0x92, 0x03, 0xec, 0x11, 0xfe, 0xe4, 0x00, 0x65, 0xfe, 0xa4, 0x03, 0x25, 0x32, 0x1f, 0xfe, 0xb4,
+ 0x03, 0x01, 0x43, 0xfe, 0x06, 0xf0, 0xfe, 0xc4, 0x03, 0x8d, 0x81, 0xfe, 0x0a, 0xf0, 0xfe, 0x7a,
+ 0x06, 0x02, 0x22, 0x05, 0x6b, 0x28, 0x16, 0xfe, 0xf6, 0x04, 0x14, 0x2c, 0x01, 0x33, 0x8f, 0xfe,
+ 0x66, 0x02, 0x02, 0xd1, 0xeb, 0x2a, 0x67, 0x1a, 0xfe, 0x67, 0x1b, 0xf8, 0xf7, 0xfe, 0x48, 0x1c,
+ 0x70, 0x01, 0x6e, 0x87, 0x0a, 0x40, 0x01, 0x0e, 0x07, 0x00, 0x16, 0xd3, 0x0a, 0xca, 0x01, 0x0e,
+ 0x74, 0x60, 0x59, 0x76, 0x27, 0x05, 0x6b, 0x28, 0xfe, 0x10, 0x12, 0x14, 0x2c, 0x01, 0x33, 0x8f,
+ 0xfe, 0x66, 0x02, 0x02, 0xd1, 0xbc, 0x7d, 0xbd, 0x7f, 0x25, 0x22, 0x65, 0xfe, 0x3c, 0x04, 0x1f,
+ 0xfe, 0x38, 0x04, 0x68, 0xfe, 0xa0, 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x4e, 0x12, 0x2b, 0xff, 0x02,
+ 0x00, 0x10, 0x01, 0x08, 0x1f, 0xfe, 0xe0, 0x04, 0x2b, 0x01, 0x08, 0x1f, 0x22, 0x30, 0x2e, 0xd5,
+ 0xfe, 0x4c, 0x44, 0xfe, 0x4c, 0x12, 0x60, 0xfe, 0x44, 0x48, 0x13, 0x2c, 0xfe, 0x4c, 0x54, 0x64,
+ 0xd3, 0x46, 0x76, 0x27, 0xfa, 0xef, 0xfe, 0x62, 0x13, 0x09, 0x04, 0x1d, 0xfe, 0x2a, 0x13, 0x2f,
+ 0x07, 0x7e, 0xa5, 0xfe, 0x20, 0x10, 0x13, 0x2c, 0xfe, 0x4c, 0x54, 0x64, 0xd3, 0xfa, 0xef, 0x86,
+ 0x09, 0x04, 0x1d, 0xfe, 0x08, 0x13, 0x2f, 0x07, 0x7e, 0x6e, 0x09, 0x04, 0x1d, 0xfe, 0x1c, 0x12,
+ 0x14, 0x92, 0x09, 0x04, 0x06, 0x3b, 0x14, 0xc4, 0x01, 0x33, 0x8f, 0xfe, 0x70, 0x0c, 0x02, 0x22,
+ 0x2b, 0x11, 0xfe, 0xe6, 0x00, 0xfe, 0x1c, 0x90, 0xf9, 0x03, 0x14, 0x92, 0x01, 0x33, 0x02, 0x29,
+ 0xfe, 0x42, 0x5b, 0x67, 0x1a, 0xfe, 0x46, 0x59, 0xf8, 0xf7, 0xfe, 0x87, 0x80, 0xfe, 0x31, 0xe4,
+ 0x4f, 0x09, 0x04, 0x0b, 0xfe, 0x78, 0x13, 0xfe, 0x20, 0x80, 0x07, 0x1a, 0xfe, 0x70, 0x12, 0x49,
+ 0x04, 0x06, 0xfe, 0x60, 0x13, 0x05, 0xfe, 0xa2, 0x00, 0x28, 0x16, 0xfe, 0x80, 0x05, 0xfe, 0x31,
+ 0xe4, 0x6a, 0x49, 0x04, 0x0b, 0xfe, 0x4a, 0x13, 0x05, 0xfe, 0xa0, 0x00, 0x28, 0xfe, 0x42, 0x12,
+ 0x5e, 0x01, 0x08, 0x25, 0x32, 0xf1, 0x01, 0x08, 0x26, 0xfe, 0x98, 0x05, 0x11, 0xfe, 0xe3, 0x00,
+ 0x23, 0x49, 0xfe, 0x4a, 0xf0, 0xfe, 0x6a, 0x05, 0xfe, 0x49, 0xf0, 0xfe, 0x64, 0x05, 0x83, 0x24,
+ 0xfe, 0x21, 0x00, 0xa1, 0x24, 0xfe, 0x22, 0x00, 0xa0, 0x24, 0x4c, 0xfe, 0x09, 0x48, 0x01, 0x08,
+ 0x26, 0xfe, 0x98, 0x05, 0xfe, 0xe2, 0x08, 0x49, 0x04, 0xc5, 0x3b, 0x01, 0x86, 0x24, 0x06, 0x12,
+ 0xcc, 0x37, 0xfe, 0x27, 0x01, 0x09, 0x04, 0x1d, 0xfe, 0x22, 0x12, 0x47, 0x01, 0xa7, 0x14, 0x92,
+ 0x09, 0x04, 0x06, 0x3b, 0x14, 0xc4, 0x01, 0x33, 0x8f, 0xfe, 0x70, 0x0c, 0x02, 0x22, 0x05, 0xfe,
+ 0x9c, 0x00, 0x28, 0xfe, 0x3e, 0x12, 0x05, 0x50, 0x28, 0xfe, 0x36, 0x13, 0x47, 0x01, 0xa7, 0x26,
+ 0xfe, 0x08, 0x06, 0x0a, 0x06, 0x49, 0x04, 0x19, 0xfe, 0x02, 0x12, 0x5f, 0x01, 0xfe, 0xaa, 0x14,
+ 0x1f, 0xfe, 0xfe, 0x05, 0x11, 0x9a, 0x01, 0x43, 0x11, 0xfe, 0xe5, 0x00, 0x05, 0x50, 0xb4, 0x0c,
+ 0x50, 0x05, 0xc6, 0x28, 0xfe, 0x62, 0x12, 0x05, 0x3f, 0x28, 0xfe, 0x5a, 0x13, 0x01, 0xfe, 0x14,
+ 0x18, 0x01, 0xfe, 0x66, 0x18, 0xfe, 0x43, 0x48, 0xb7, 0x19, 0x13, 0x6c, 0xff, 0x02, 0x00, 0x57,
+ 0x48, 0x8b, 0x1c, 0x3d, 0x85, 0xb7, 0x69, 0x47, 0x01, 0xa7, 0x26, 0xfe, 0x72, 0x06, 0x49, 0x04,
+ 0x1b, 0xdf, 0x89, 0x0a, 0x4d, 0x01, 0xfe, 0xd8, 0x14, 0x1f, 0xfe, 0x68, 0x06, 0x11, 0x9a, 0x01,
+ 0x43, 0x11, 0xfe, 0xe5, 0x00, 0x05, 0x3f, 0xb4, 0x0c, 0x3f, 0x17, 0x06, 0x01, 0xa7, 0xec, 0x72,
+ 0x70, 0x01, 0x6e, 0x87, 0x11, 0xfe, 0xe2, 0x00, 0x01, 0x08, 0x25, 0x32, 0xfe, 0x0a, 0xf0, 0xfe,
+ 0xa6, 0x06, 0x8c, 0xfe, 0x5c, 0x07, 0xfe, 0x06, 0xf0, 0xfe, 0x64, 0x07, 0x8d, 0x81, 0x02, 0x22,
+ 0x09, 0x04, 0x0b, 0xfe, 0x2e, 0x12, 0x15, 0x1a, 0x01, 0x08, 0x15, 0x00, 0x01, 0x08, 0x15, 0x00,
+ 0x01, 0x08, 0x15, 0x00, 0x01, 0x08, 0xfe, 0x99, 0xa4, 0x01, 0x08, 0x15, 0x00, 0x02, 0xfe, 0x32,
+ 0x08, 0x61, 0x04, 0x1b, 0xfe, 0x38, 0x12, 0x09, 0x04, 0x1b, 0x6e, 0x15, 0xfe, 0x1b, 0x00, 0x01,
+ 0x08, 0x15, 0x00, 0x01, 0x08, 0x15, 0x00, 0x01, 0x08, 0x15, 0x00, 0x01, 0x08, 0x15, 0x06, 0x01,
+ 0x08, 0x15, 0x00, 0x02, 0xd9, 0x66, 0x4c, 0xfe, 0x3a, 0x55, 0x5f, 0xfe, 0x9a, 0x81, 0x4b, 0x1d,
+ 0xba, 0xfe, 0x32, 0x07, 0x0a, 0x1d, 0xfe, 0x09, 0x6f, 0xaf, 0xfe, 0xca, 0x45, 0xfe, 0x32, 0x12,
+ 0x62, 0x2c, 0x85, 0x66, 0x7b, 0x01, 0x08, 0x25, 0x32, 0xfe, 0x0a, 0xf0, 0xfe, 0x32, 0x07, 0x8d,
+ 0x81, 0x8c, 0xfe, 0x5c, 0x07, 0x02, 0x22, 0x01, 0x43, 0x02, 0xfe, 0x8a, 0x06, 0x15, 0x19, 0x02,
+ 0xfe, 0x8a, 0x06, 0xfe, 0x9c, 0xf7, 0xd4, 0xfe, 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x77, 0xfe, 0xca,
+ 0x07, 0x0c, 0x54, 0x18, 0x55, 0x09, 0x4a, 0x6a, 0x35, 0x1e, 0x20, 0x07, 0x10, 0xfe, 0x0e, 0x12,
+ 0x74, 0xfe, 0x80, 0x80, 0x37, 0x20, 0x63, 0x27, 0xfe, 0x06, 0x10, 0xfe, 0x83, 0xe7, 0xc4, 0xa1,
+ 0xfe, 0x03, 0x40, 0x09, 0x4a, 0x4f, 0x35, 0x01, 0xa8, 0xad, 0xfe, 0x1f, 0x40, 0x12, 0x58, 0x01,
+ 0xa5, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0xfe, 0x44, 0x51, 0xfe, 0xc6, 0x51, 0x83, 0xfb, 0xfe,
+ 0x8a, 0x90, 0x0c, 0x52, 0x18, 0x53, 0xfe, 0x0c, 0x90, 0xfe, 0x8e, 0x90, 0xfe, 0x40, 0x50, 0xfe,
+ 0xc2, 0x50, 0x0c, 0x39, 0x18, 0x3a, 0xfe, 0x4a, 0x10, 0x09, 0x04, 0x6a, 0xfe, 0x2a, 0x12, 0xfe,
+ 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x0c, 0x54, 0x18, 0x55, 0x09, 0x04, 0x4f, 0x85, 0x01, 0xa8, 0xfe,
+ 0x1f, 0x80, 0x12, 0x58, 0xfe, 0x44, 0x90, 0xfe, 0xc6, 0x90, 0x0c, 0x56, 0x18, 0x57, 0xfb, 0xfe,
+ 0x8a, 0x90, 0x0c, 0x52, 0x18, 0x53, 0xfe, 0x40, 0x90, 0xfe, 0xc2, 0x90, 0x0c, 0x39, 0x18, 0x3a,
+ 0x0c, 0x38, 0x18, 0x4e, 0x09, 0x4a, 0x19, 0x35, 0x2a, 0x13, 0xfe, 0x4e, 0x11, 0x65, 0xfe, 0x48,
+ 0x08, 0xfe, 0x9e, 0xf0, 0xfe, 0x5c, 0x08, 0xb1, 0x16, 0x32, 0x2a, 0x73, 0xdd, 0xb8, 0xfe, 0x80,
+ 0x08, 0xb9, 0xfe, 0x9e, 0x08, 0x8c, 0xfe, 0x74, 0x08, 0xfe, 0x06, 0xf0, 0xfe, 0x7a, 0x08, 0x8d,
+ 0x81, 0x02, 0x22, 0x01, 0x43, 0xfe, 0xc9, 0x10, 0x15, 0x19, 0xfe, 0xc9, 0x10, 0x61, 0x04, 0x06,
+ 0xfe, 0x10, 0x12, 0x61, 0x04, 0x0b, 0x45, 0x09, 0x04, 0x0b, 0xfe, 0x68, 0x12, 0xfe, 0x2e, 0x1c,
+ 0x02, 0xfe, 0x24, 0x0a, 0x61, 0x04, 0x06, 0x45, 0x61, 0x04, 0x0b, 0xfe, 0x52, 0x12, 0xfe, 0x2c,
+ 0x1c, 0xfe, 0xaa, 0xf0, 0xfe, 0x1e, 0x09, 0xfe, 0xac, 0xf0, 0xfe, 0xbe, 0x08, 0xfe, 0x8a, 0x10,
+ 0xaa, 0xfe, 0xf3, 0x10, 0xfe, 0xad, 0xf0, 0xfe, 0xca, 0x08, 0x02, 0xfe, 0x24, 0x0a, 0xab, 0xfe,
+ 0xe7, 0x10, 0xfe, 0x2b, 0xf0, 0x9d, 0xe9, 0x1c, 0xfe, 0x00, 0xfe, 0xfe, 0x1c, 0x12, 0xb5, 0xfe,
+ 0xd2, 0xf0, 0x9d, 0xfe, 0x76, 0x18, 0x1c, 0x1a, 0x16, 0x9d, 0x05, 0xcb, 0x1c, 0x06, 0x16, 0x9d,
+ 0xb8, 0x6d, 0xb9, 0x6d, 0xaa, 0xab, 0xfe, 0xb1, 0x10, 0x70, 0x5e, 0x2b, 0x14, 0x92, 0x01, 0x33,
+ 0x0f, 0xfe, 0x35, 0x00, 0xfe, 0x01, 0xf0, 0x5a, 0x0f, 0x7c, 0x02, 0x5a, 0xfe, 0x74, 0x18, 0x1c,
+ 0xfe, 0x00, 0xf8, 0x16, 0x6d, 0x67, 0x1b, 0x01, 0xfe, 0x44, 0x0d, 0x3b, 0x01, 0xe6, 0x1e, 0x27,
+ 0x74, 0x67, 0x1a, 0x02, 0x6d, 0x09, 0x04, 0x0b, 0x21, 0xfe, 0x06, 0x0a, 0x09, 0x04, 0x6a, 0xfe,
+ 0x82, 0x12, 0x09, 0x04, 0x19, 0xfe, 0x66, 0x13, 0x1e, 0x58, 0xac, 0xfc, 0xfe, 0x83, 0x80, 0xfe,
+ 0xc8, 0x44, 0xfe, 0x2e, 0x13, 0xfe, 0x04, 0x91, 0xfe, 0x86, 0x91, 0x63, 0x27, 0xfe, 0x40, 0x59,
+ 0xfe, 0xc1, 0x59, 0x77, 0xd7, 0x05, 0x54, 0x31, 0x55, 0x0c, 0x7b, 0x18, 0x7c, 0xbe, 0x54, 0xbf,
+ 0x55, 0x01, 0xa8, 0xad, 0x63, 0x27, 0x12, 0x58, 0xc0, 0x38, 0xc1, 0x4e, 0x79, 0x56, 0x68, 0x57,
+ 0xf4, 0xf5, 0xfe, 0x04, 0xfa, 0x38, 0xfe, 0x05, 0xfa, 0x4e, 0x01, 0xa5, 0xa2, 0x23, 0x0c, 0x7b,
+ 0x0c, 0x7c, 0x79, 0x56, 0x68, 0x57, 0xfe, 0x12, 0x10, 0x09, 0x04, 0x19, 0x16, 0xd7, 0x79, 0x39,
+ 0x68, 0x3a, 0x09, 0x04, 0xfe, 0xf7, 0x00, 0x35, 0x05, 0x52, 0x31, 0x53, 0xfe, 0x10, 0x58, 0xfe,
+ 0x91, 0x58, 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0x02, 0x6d, 0x09, 0x04, 0x19, 0x16, 0xd7, 0x09,
+ 0x04, 0xfe, 0xf7, 0x00, 0x35, 0xfe, 0x3a, 0x55, 0xfe, 0x19, 0x81, 0x5f, 0xfe, 0x10, 0x90, 0xfe,
+ 0x92, 0x90, 0xfe, 0xd7, 0x10, 0x2f, 0x07, 0x9b, 0x16, 0xfe, 0xc6, 0x08, 0x11, 0x9b, 0x09, 0x04,
+ 0x0b, 0xfe, 0x14, 0x13, 0x05, 0x39, 0x31, 0x3a, 0x77, 0xfe, 0xc6, 0x08, 0xfe, 0x0c, 0x58, 0xfe,
+ 0x8d, 0x58, 0x02, 0x6d, 0x23, 0x47, 0xfe, 0x19, 0x80, 0xde, 0x09, 0x04, 0x0b, 0xfe, 0x1a, 0x12,
+ 0xfe, 0x6c, 0x19, 0xfe, 0x19, 0x41, 0xe9, 0xb5, 0xfe, 0xd1, 0xf0, 0xd9, 0x14, 0x7a, 0x01, 0x33,
+ 0x0f, 0xfe, 0x44, 0x00, 0xfe, 0x8e, 0x10, 0xfe, 0x6c, 0x19, 0xbe, 0x39, 0xfe, 0xed, 0x19, 0xbf,
+ 0x3a, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0xe9, 0x1c, 0xfe, 0x00, 0xff, 0x34, 0xfe, 0x74, 0x10,
+ 0xb5, 0xfe, 0xd2, 0xf0, 0xfe, 0xb2, 0x0a, 0xfe, 0x76, 0x18, 0x1c, 0x1a, 0x84, 0x05, 0xcb, 0x1c,
+ 0x06, 0xfe, 0x08, 0x13, 0x0f, 0xfe, 0x16, 0x00, 0x02, 0x5a, 0xfe, 0xd1, 0xf0, 0xfe, 0xc4, 0x0a,
+ 0x14, 0x7a, 0x01, 0x33, 0x0f, 0xfe, 0x17, 0x00, 0xfe, 0x42, 0x10, 0xfe, 0xce, 0xf0, 0xfe, 0xca,
+ 0x0a, 0xfe, 0x3c, 0x10, 0xfe, 0xcd, 0xf0, 0xfe, 0xd6, 0x0a, 0x0f, 0xfe, 0x22, 0x00, 0x02, 0x5a,
+ 0xfe, 0xcb, 0xf0, 0xfe, 0xe2, 0x0a, 0x0f, 0xfe, 0x24, 0x00, 0x02, 0x5a, 0xfe, 0xd0, 0xf0, 0xfe,
+ 0xec, 0x0a, 0x0f, 0x93, 0xdc, 0xfe, 0xcf, 0xf0, 0xfe, 0xf6, 0x0a, 0x0f, 0x4c, 0xfe, 0x10, 0x10,
+ 0xfe, 0xcc, 0xf0, 0xd9, 0x61, 0x04, 0x19, 0x3b, 0x0f, 0xfe, 0x12, 0x00, 0x2a, 0x13, 0xfe, 0x4e,
+ 0x11, 0x65, 0xfe, 0x0c, 0x0b, 0xfe, 0x9e, 0xf0, 0xfe, 0x20, 0x0b, 0xb1, 0x16, 0x32, 0x2a, 0x73,
+ 0xdd, 0xb8, 0x22, 0xb9, 0x22, 0x2a, 0xec, 0x65, 0xfe, 0x2c, 0x0b, 0x25, 0x32, 0x8c, 0xfe, 0x48,
+ 0x0b, 0x8d, 0x81, 0xb8, 0xd4, 0xb9, 0xd4, 0x02, 0x22, 0x01, 0x43, 0xfe, 0xdb, 0x10, 0x11, 0xfe,
+ 0xe8, 0x00, 0xaa, 0xab, 0x70, 0xbc, 0x7d, 0xbd, 0x7f, 0xfe, 0x89, 0xf0, 0x22, 0x30, 0x2e, 0xd8,
+ 0xbc, 0x7d, 0xbd, 0x7f, 0x01, 0x08, 0x1f, 0x22, 0x30, 0x2e, 0xd6, 0xb1, 0x45, 0x0f, 0xfe, 0x42,
+ 0x00, 0x02, 0x5a, 0x78, 0x06, 0xfe, 0x81, 0x49, 0x16, 0xfe, 0x38, 0x0c, 0x09, 0x04, 0x0b, 0xfe,
+ 0x44, 0x13, 0x0f, 0x00, 0x4b, 0x0b, 0xfe, 0x54, 0x12, 0x4b, 0xfe, 0x28, 0x00, 0x21, 0xfe, 0xa6,
+ 0x0c, 0x0a, 0x40, 0x01, 0x0e, 0x07, 0x00, 0x5d, 0x3e, 0xfe, 0x28, 0x00, 0xfe, 0xe2, 0x10, 0x01,
+ 0xe7, 0x01, 0xe8, 0x0a, 0x99, 0x01, 0xfe, 0x32, 0x0e, 0x59, 0x11, 0x2d, 0x01, 0x6f, 0x02, 0x29,
+ 0x0f, 0xfe, 0x44, 0x00, 0x4b, 0x0b, 0xdf, 0x3e, 0x0b, 0xfe, 0xb4, 0x10, 0x01, 0x86, 0x3e, 0x0b,
+ 0xfe, 0xaa, 0x10, 0x01, 0x86, 0xfe, 0x19, 0x82, 0xfe, 0x34, 0x46, 0xa3, 0x3e, 0x0b, 0x0f, 0xfe,
+ 0x43, 0x00, 0xfe, 0x96, 0x10, 0x09, 0x4a, 0x0b, 0x35, 0x01, 0xe7, 0x01, 0xe8, 0x59, 0x11, 0x2d,
+ 0x01, 0x6f, 0x67, 0x0b, 0x59, 0x3c, 0x8a, 0x02, 0xfe, 0x2a, 0x03, 0x09, 0x04, 0x0b, 0x84, 0x3e,
+ 0x0b, 0x0f, 0x00, 0xfe, 0x5c, 0x10, 0x61, 0x04, 0x1b, 0xfe, 0x58, 0x12, 0x09, 0x04, 0x1b, 0xfe,
+ 0x50, 0x13, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x5c, 0x0c, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d,
+ 0xf0, 0xfe, 0x62, 0x0c, 0x09, 0x4a, 0x1b, 0x35, 0xfe, 0xa9, 0x10, 0x0f, 0xfe, 0x15, 0x00, 0xfe,
+ 0x04, 0xe6, 0x0b, 0x5f, 0x5c, 0x0f, 0xfe, 0x13, 0x00, 0xfe, 0x10, 0x10, 0x0f, 0xfe, 0x47, 0x00,
+ 0xa1, 0x0f, 0xfe, 0x41, 0x00, 0xa0, 0x0f, 0xfe, 0x24, 0x00, 0x87, 0xaa, 0xab, 0x70, 0x05, 0x6b,
+ 0x28, 0x21, 0xd1, 0x5f, 0xfe, 0x04, 0xe6, 0x1b, 0xfe, 0x9d, 0x41, 0xfe, 0x1c, 0x42, 0x59, 0x01,
+ 0xda, 0x02, 0x29, 0xea, 0x14, 0x0b, 0x37, 0x95, 0xa9, 0x14, 0xfe, 0x31, 0x00, 0x37, 0x97, 0x01,
+ 0xfe, 0x54, 0x0f, 0x02, 0xd0, 0x3c, 0xfe, 0x06, 0xec, 0xc9, 0xee, 0x3e, 0x1d, 0xfe, 0xce, 0x45,
+ 0x34, 0x3c, 0xfe, 0x06, 0xea, 0xc9, 0xfe, 0x47, 0x4b, 0x89, 0xfe, 0x75, 0x57, 0x05, 0x51, 0xfe,
+ 0x98, 0x56, 0xfe, 0x38, 0x12, 0x0a, 0x42, 0x01, 0x0e, 0xfe, 0x44, 0x48, 0x46, 0x09, 0x04, 0x1d,
+ 0xfe, 0x1a, 0x13, 0x0a, 0x40, 0x01, 0x0e, 0x47, 0xfe, 0x41, 0x58, 0x0a, 0x99, 0x01, 0x0e, 0xfe,
+ 0x49, 0x54, 0x8e, 0xfe, 0x2a, 0x0d, 0x02, 0xfe, 0x2a, 0x03, 0x0a, 0x51, 0xfe, 0xee, 0x14, 0xee,
+ 0x3e, 0x1d, 0xfe, 0xce, 0x45, 0x34, 0x3c, 0xfe, 0xce, 0x47, 0xfe, 0xad, 0x13, 0x02, 0x29, 0x1e,
+ 0x20, 0x07, 0x10, 0xfe, 0x9e, 0x12, 0x23, 0x12, 0x4d, 0x12, 0x94, 0x12, 0xce, 0x1e, 0x2d, 0x47,
+ 0x37, 0x2d, 0xb1, 0xe0, 0xfe, 0xbc, 0xf0, 0xfe, 0xec, 0x0d, 0x13, 0x06, 0x12, 0x4d, 0x01, 0xfe,
+ 0xe2, 0x15, 0x05, 0xfe, 0x38, 0x01, 0x31, 0xfe, 0x3a, 0x01, 0x77, 0xfe, 0xf0, 0x0d, 0xfe, 0x02,
+ 0xec, 0xce, 0x62, 0x00, 0x5d, 0xfe, 0x04, 0xec, 0x20, 0x46, 0xfe, 0x05, 0xf6, 0xfe, 0x34, 0x01,
+ 0x01, 0xfe, 0x52, 0x16, 0xfb, 0xfe, 0x48, 0xf4, 0x0d, 0xfe, 0x18, 0x13, 0xaf, 0xfe, 0x02, 0xea,
+ 0xce, 0x62, 0x7a, 0xfe, 0xc5, 0x13, 0x14, 0x1b, 0x37, 0x95, 0xa9, 0x5c, 0x05, 0xfe, 0x38, 0x01,
+ 0x1c, 0xfe, 0xf0, 0xff, 0x0c, 0xfe, 0x60, 0x01, 0x05, 0xfe, 0x3a, 0x01, 0x0c, 0xfe, 0x62, 0x01,
+ 0x3d, 0x12, 0x20, 0x24, 0x06, 0x12, 0x2d, 0x11, 0x2d, 0x8a, 0x13, 0x06, 0x03, 0x23, 0x03, 0x1e,
+ 0x4d, 0xfe, 0xf7, 0x12, 0x1e, 0x94, 0xac, 0x12, 0x94, 0x07, 0x7a, 0xfe, 0x71, 0x13, 0xfe, 0x24,
+ 0x1c, 0x14, 0x1a, 0x37, 0x95, 0xa9, 0xfe, 0xd9, 0x10, 0xb6, 0xfe, 0x03, 0xdc, 0xfe, 0x73, 0x57,
+ 0xfe, 0x80, 0x5d, 0x03, 0xb6, 0xfe, 0x03, 0xdc, 0xfe, 0x5b, 0x57, 0xfe, 0x80, 0x5d, 0x03, 0xfe,
+ 0x03, 0x57, 0xb6, 0x23, 0xfe, 0x00, 0xcc, 0x03, 0xfe, 0x03, 0x57, 0xb6, 0x75, 0x03, 0x09, 0x04,
+ 0x4c, 0xfe, 0x22, 0x13, 0xfe, 0x1c, 0x80, 0x07, 0x06, 0xfe, 0x1a, 0x13, 0xfe, 0x1e, 0x80, 0xe1,
+ 0xfe, 0x1d, 0x80, 0xa4, 0xfe, 0x0c, 0x90, 0xfe, 0x0e, 0x13, 0xfe, 0x0e, 0x90, 0xa3, 0xfe, 0x3c,
+ 0x90, 0xfe, 0x30, 0xf4, 0x0b, 0xfe, 0x3c, 0x50, 0xa0, 0x01, 0xfe, 0x82, 0x16, 0x2f, 0x07, 0x2d,
+ 0xe0, 0x01, 0xfe, 0xbc, 0x15, 0x09, 0x04, 0x1d, 0x45, 0x01, 0xe7, 0x01, 0xe8, 0x11, 0xfe, 0xe9,
+ 0x00, 0x09, 0x04, 0x4c, 0xfe, 0x2c, 0x13, 0x01, 0xfe, 0x14, 0x16, 0xfe, 0x1e, 0x1c, 0xfe, 0x14,
+ 0x90, 0xfe, 0x96, 0x90, 0x0c, 0xfe, 0x64, 0x01, 0x18, 0xfe, 0x66, 0x01, 0x09, 0x04, 0x4f, 0xfe,
+ 0x12, 0x12, 0xfe, 0x03, 0x80, 0x74, 0xfe, 0x01, 0xec, 0x20, 0xfe, 0x80, 0x40, 0x12, 0x20, 0x63,
+ 0x27, 0x11, 0xc8, 0x59, 0x1e, 0x20, 0xed, 0x76, 0x20, 0x03, 0xfe, 0x08, 0x1c, 0x05, 0xfe, 0xac,
+ 0x00, 0xfe, 0x06, 0x58, 0x05, 0xfe, 0xae, 0x00, 0xfe, 0x07, 0x58, 0x05, 0xfe, 0xb0, 0x00, 0xfe,
+ 0x08, 0x58, 0x05, 0xfe, 0xb2, 0x00, 0xfe, 0x09, 0x58, 0xfe, 0x0a, 0x1c, 0x24, 0x69, 0x12, 0xc9,
+ 0x23, 0x0c, 0x50, 0x0c, 0x3f, 0x13, 0x40, 0x48, 0x5f, 0x17, 0x1d, 0xfe, 0x90, 0x4d, 0xfe, 0x91,
+ 0x54, 0x21, 0xfe, 0x08, 0x0f, 0x3e, 0x10, 0x13, 0x42, 0x48, 0x17, 0x4c, 0xfe, 0x90, 0x4d, 0xfe,
+ 0x91, 0x54, 0x21, 0xfe, 0x1e, 0x0f, 0x24, 0x10, 0x12, 0x20, 0x78, 0x2c, 0x46, 0x1e, 0x20, 0xed,
+ 0x76, 0x20, 0x11, 0xc8, 0xf6, 0xfe, 0xd6, 0xf0, 0xfe, 0x32, 0x0f, 0xea, 0x70, 0xfe, 0x14, 0x1c,
+ 0xfe, 0x10, 0x1c, 0xfe, 0x18, 0x1c, 0x03, 0x3c, 0xfe, 0x0c, 0x14, 0xee, 0xfe, 0x07, 0xe6, 0x1d,
+ 0xfe, 0xce, 0x47, 0xfe, 0xf5, 0x13, 0x03, 0x01, 0x86, 0x78, 0x2c, 0x46, 0xfa, 0xef, 0xfe, 0x42,
+ 0x13, 0x2f, 0x07, 0x2d, 0xfe, 0x34, 0x13, 0x0a, 0x42, 0x01, 0x0e, 0xb0, 0xfe, 0x36, 0x12, 0xf0,
+ 0xfe, 0x45, 0x48, 0x01, 0xe3, 0xfe, 0x00, 0xcc, 0xb0, 0xfe, 0xf3, 0x13, 0x3d, 0x75, 0x07, 0x10,
+ 0xa3, 0x0a, 0x80, 0x01, 0x0e, 0xfe, 0x80, 0x5c, 0x01, 0x6f, 0xfe, 0x0e, 0x10, 0x07, 0x7e, 0x45,
+ 0xf6, 0xfe, 0xd6, 0xf0, 0xfe, 0x6c, 0x0f, 0x03, 0xfe, 0x44, 0x58, 0x74, 0xfe, 0x01, 0xec, 0x97,
+ 0xfe, 0x9e, 0x40, 0xfe, 0x9d, 0xe7, 0x00, 0xfe, 0x9c, 0xe7, 0x1b, 0x76, 0x27, 0x01, 0xda, 0xfe,
+ 0xdd, 0x10, 0x2a, 0xbc, 0x7d, 0xbd, 0x7f, 0x30, 0x2e, 0xd5, 0x07, 0x1b, 0xfe, 0x48, 0x12, 0x07,
+ 0x0b, 0xfe, 0x56, 0x12, 0x07, 0x1a, 0xfe, 0x30, 0x12, 0x07, 0xc2, 0x16, 0xfe, 0x3e, 0x11, 0x07,
+ 0xfe, 0x23, 0x00, 0x16, 0xfe, 0x4a, 0x11, 0x07, 0x06, 0x16, 0xfe, 0xa8, 0x11, 0x07, 0x19, 0xfe,
+ 0x12, 0x12, 0x07, 0x00, 0x16, 0x22, 0x14, 0xc2, 0x01, 0x33, 0x9f, 0x2b, 0x01, 0x08, 0x8c, 0x43,
+ 0x03, 0x2b, 0xfe, 0x62, 0x08, 0x0a, 0xca, 0x01, 0xfe, 0x32, 0x0e, 0x11, 0x7e, 0x02, 0x29, 0x2b,
+ 0x2f, 0x07, 0x9b, 0xfe, 0xd9, 0x13, 0x79, 0x39, 0x68, 0x3a, 0x77, 0xfe, 0xfc, 0x10, 0x09, 0x04,
+ 0x6a, 0xfe, 0x72, 0x12, 0xc0, 0x38, 0xc1, 0x4e, 0xf4, 0xf5, 0x8e, 0xfe, 0xc6, 0x10, 0x1e, 0x58,
+ 0xfe, 0x26, 0x13, 0x05, 0x7b, 0x31, 0x7c, 0x77, 0xfe, 0x82, 0x0c, 0x0c, 0x54, 0x18, 0x55, 0x23,
+ 0x0c, 0x7b, 0x0c, 0x7c, 0x01, 0xa8, 0x24, 0x69, 0x73, 0x12, 0x58, 0x01, 0xa5, 0xc0, 0x38, 0xc1,
+ 0x4e, 0xfe, 0x04, 0x55, 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa, 0x38, 0xfe, 0x05, 0xfa, 0x4e, 0xfe,
+ 0x91, 0x10, 0x05, 0x56, 0x31, 0x57, 0xfe, 0x40, 0x56, 0xfe, 0xe1, 0x56, 0x0c, 0x56, 0x18, 0x57,
+ 0x83, 0xc0, 0x38, 0xc1, 0x4e, 0xf4, 0xf5, 0x05, 0x52, 0x31, 0x53, 0xfe, 0x00, 0x56, 0xfe, 0xa1,
+ 0x56, 0x0c, 0x52, 0x18, 0x53, 0x09, 0x04, 0x6a, 0xfe, 0x1e, 0x12, 0x1e, 0x58, 0xfe, 0x1f, 0x40,
+ 0x05, 0x54, 0x31, 0x55, 0xfe, 0x2c, 0x50, 0xfe, 0xae, 0x50, 0x05, 0x56, 0x31, 0x57, 0xfe, 0x44,
+ 0x50, 0xfe, 0xc6, 0x50, 0x05, 0x52, 0x31, 0x53, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0x05, 0x39,
+ 0x31, 0x3a, 0xfe, 0x40, 0x50, 0xfe, 0xc2, 0x50, 0x02, 0x5c, 0x24, 0x06, 0x12, 0xcd, 0x02, 0x5b,
+ 0x2b, 0x01, 0x08, 0x1f, 0x44, 0x30, 0x2e, 0xd5, 0x07, 0x06, 0x21, 0x44, 0x2f, 0x07, 0x9b, 0x21,
+ 0x5b, 0x01, 0x6e, 0x1c, 0x3d, 0x16, 0x44, 0x09, 0x04, 0x0b, 0xe2, 0x79, 0x39, 0x68, 0x3a, 0xfe,
+ 0x0a, 0x55, 0x34, 0xfe, 0x8b, 0x55, 0xbe, 0x39, 0xbf, 0x3a, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51,
+ 0x02, 0x5b, 0xfe, 0x19, 0x81, 0xaf, 0xfe, 0x19, 0x41, 0x02, 0x5b, 0x2b, 0x01, 0x08, 0x25, 0x32,
+ 0x1f, 0xa2, 0x30, 0x2e, 0xd8, 0x4b, 0x1a, 0xfe, 0xa6, 0x12, 0x4b, 0x0b, 0x3b, 0x02, 0x44, 0x01,
+ 0x08, 0x25, 0x32, 0x1f, 0xa2, 0x30, 0x2e, 0xd6, 0x07, 0x1a, 0x21, 0x44, 0x01, 0x08, 0x1f, 0xa2,
+ 0x30, 0x2e, 0xfe, 0xe8, 0x09, 0xfe, 0xc2, 0x49, 0x60, 0x05, 0xfe, 0x9c, 0x00, 0x28, 0x84, 0x49,
+ 0x04, 0x19, 0x34, 0x9f, 0xfe, 0xbb, 0x45, 0x4b, 0x00, 0x45, 0x3e, 0x06, 0x78, 0x3d, 0xfe, 0xda,
+ 0x14, 0x01, 0x6e, 0x87, 0xfe, 0x4b, 0x45, 0xe2, 0x2f, 0x07, 0x9a, 0xe1, 0x05, 0xc6, 0x28, 0x84,
+ 0x05, 0x3f, 0x28, 0x34, 0x5e, 0x02, 0x5b, 0xfe, 0xc0, 0x5d, 0xfe, 0xf8, 0x14, 0xfe, 0x03, 0x17,
+ 0x05, 0x50, 0xb4, 0x0c, 0x50, 0x5e, 0x2b, 0x01, 0x08, 0x26, 0x5c, 0x01, 0xfe, 0xaa, 0x14, 0x02,
+ 0x5c, 0x01, 0x08, 0x25, 0x32, 0x1f, 0x44, 0x30, 0x2e, 0xd6, 0x07, 0x06, 0x21, 0x44, 0x01, 0xfe,
+ 0x8e, 0x13, 0xfe, 0x42, 0x58, 0xfe, 0x82, 0x14, 0xfe, 0xa4, 0x14, 0x87, 0xfe, 0x4a, 0xf4, 0x0b,
+ 0x16, 0x44, 0xfe, 0x4a, 0xf4, 0x06, 0xfe, 0x0c, 0x12, 0x2f, 0x07, 0x9a, 0x85, 0x02, 0x5b, 0x05,
+ 0x3f, 0xb4, 0x0c, 0x3f, 0x5e, 0x2b, 0x01, 0x08, 0x26, 0x5c, 0x01, 0xfe, 0xd8, 0x14, 0x02, 0x5c,
+ 0x13, 0x06, 0x65, 0xfe, 0xca, 0x12, 0x26, 0xfe, 0xe0, 0x12, 0x72, 0xf1, 0x01, 0x08, 0x23, 0x72,
+ 0x03, 0x8f, 0xfe, 0xdc, 0x12, 0x25, 0xfe, 0xdc, 0x12, 0x1f, 0xfe, 0xca, 0x12, 0x5e, 0x2b, 0x01,
+ 0x08, 0xfe, 0xd5, 0x10, 0x13, 0x6c, 0xff, 0x02, 0x00, 0x57, 0x48, 0x8b, 0x1c, 0xfe, 0xff, 0x7f,
+ 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x13, 0x6c, 0xff, 0x02, 0x00, 0x57, 0x48, 0x8b, 0x1c,
+ 0x3d, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x03, 0x13, 0x6c, 0xff, 0x02, 0x00, 0x57, 0x48, 0x8b,
+ 0x03, 0x13, 0x6c, 0xff, 0x02, 0x00, 0x57, 0x48, 0x8b, 0xfe, 0x0b, 0x58, 0x03, 0x0a, 0x50, 0x01,
+ 0x82, 0x0a, 0x3f, 0x01, 0x82, 0x03, 0xfc, 0x1c, 0x10, 0xff, 0x03, 0x00, 0x54, 0xfe, 0x00, 0xf4,
+ 0x19, 0x48, 0xfe, 0x00, 0x7d, 0xfe, 0x01, 0x7d, 0xfe, 0x02, 0x7d, 0xfe, 0x03, 0x7c, 0x63, 0x27,
+ 0x0c, 0x52, 0x18, 0x53, 0xbe, 0x56, 0xbf, 0x57, 0x03, 0xfe, 0x62, 0x08, 0xfe, 0x82, 0x4a, 0xfe,
+ 0xe1, 0x1a, 0xfe, 0x83, 0x5a, 0x74, 0x03, 0x01, 0xfe, 0x14, 0x18, 0xfe, 0x42, 0x48, 0x5f, 0x60,
+ 0x89, 0x01, 0x08, 0x1f, 0xfe, 0xa2, 0x14, 0x30, 0x2e, 0xd8, 0x01, 0x08, 0x1f, 0xfe, 0xa2, 0x14,
+ 0x30, 0x2e, 0xfe, 0xe8, 0x0a, 0xfe, 0xc1, 0x59, 0x05, 0xc6, 0x28, 0xfe, 0xcc, 0x12, 0x49, 0x04,
+ 0x1b, 0xfe, 0xc4, 0x13, 0x23, 0x62, 0x1b, 0xe2, 0x4b, 0xc3, 0x64, 0xfe, 0xe8, 0x13, 0x3b, 0x13,
+ 0x06, 0x17, 0xc3, 0x78, 0xdb, 0xfe, 0x78, 0x10, 0xff, 0x02, 0x83, 0x55, 0xa1, 0xff, 0x02, 0x83,
+ 0x55, 0x62, 0x1a, 0xa4, 0xbb, 0xfe, 0x30, 0x00, 0x8e, 0xe4, 0x17, 0x2c, 0x13, 0x06, 0xfe, 0x56,
+ 0x10, 0x62, 0x0b, 0xe1, 0xbb, 0xfe, 0x64, 0x00, 0x8e, 0xe4, 0x0a, 0xfe, 0x64, 0x00, 0x17, 0x93,
+ 0x13, 0x06, 0xfe, 0x28, 0x10, 0x62, 0x06, 0xfe, 0x60, 0x13, 0xbb, 0xfe, 0xc8, 0x00, 0x8e, 0xe4,
+ 0x0a, 0xfe, 0xc8, 0x00, 0x17, 0x4d, 0x13, 0x06, 0x83, 0xbb, 0xfe, 0x90, 0x01, 0xba, 0xfe, 0x4e,
+ 0x14, 0x89, 0xfe, 0x12, 0x10, 0xfe, 0x43, 0xf4, 0x94, 0xfe, 0x56, 0xf0, 0xfe, 0x60, 0x14, 0xfe,
+ 0x04, 0xf4, 0x6c, 0xfe, 0x43, 0xf4, 0x93, 0xfe, 0xf3, 0x10, 0xf9, 0x01, 0xfe, 0x22, 0x13, 0x1c,
+ 0x3d, 0xfe, 0x10, 0x13, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x69, 0xba, 0xfe, 0x9c, 0x14, 0xb7,
+ 0x69, 0xfe, 0x1c, 0x10, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4, 0x19, 0xba, 0xfe, 0x9c, 0x14, 0xb7,
+ 0x19, 0x83, 0x60, 0x23, 0xfe, 0x4d, 0xf4, 0x00, 0xdf, 0x89, 0x13, 0x06, 0xfe, 0xb4, 0x56, 0xfe,
+ 0xc3, 0x58, 0x03, 0x60, 0x13, 0x0b, 0x03, 0x15, 0x06, 0x01, 0x08, 0x26, 0xe5, 0x15, 0x0b, 0x01,
+ 0x08, 0x26, 0xe5, 0x15, 0x1a, 0x01, 0x08, 0x26, 0xe5, 0x72, 0xfe, 0x89, 0x49, 0x01, 0x08, 0x03,
+ 0x15, 0x06, 0x01, 0x08, 0x26, 0xa6, 0x15, 0x1a, 0x01, 0x08, 0x26, 0xa6, 0x15, 0x06, 0x01, 0x08,
+ 0x26, 0xa6, 0xfe, 0x89, 0x49, 0x01, 0x08, 0x26, 0xa6, 0x72, 0xfe, 0x89, 0x4a, 0x01, 0x08, 0x03,
+ 0x60, 0x03, 0x1e, 0xcc, 0x07, 0x06, 0xfe, 0x44, 0x13, 0xad, 0x12, 0xcc, 0xfe, 0x49, 0xf4, 0x00,
+ 0x3b, 0x72, 0x9f, 0x5e, 0xfe, 0x01, 0xec, 0xfe, 0x27, 0x01, 0xf1, 0x01, 0x08, 0x2f, 0x07, 0xfe,
+ 0xe3, 0x00, 0xfe, 0x20, 0x13, 0x1f, 0xfe, 0x5a, 0x15, 0x23, 0x12, 0xcd, 0x01, 0x43, 0x1e, 0xcd,
+ 0x07, 0x06, 0x45, 0x09, 0x4a, 0x06, 0x35, 0x03, 0x0a, 0x42, 0x01, 0x0e, 0xed, 0x88, 0x07, 0x10,
+ 0xa4, 0x0a, 0x80, 0x01, 0x0e, 0x88, 0x0a, 0x51, 0x01, 0x9e, 0x03, 0x0a, 0x80, 0x01, 0x0e, 0x88,
+ 0xfe, 0x80, 0xe7, 0x10, 0x07, 0x10, 0x84, 0xfe, 0x45, 0x58, 0x01, 0xe3, 0x88, 0x03, 0x0a, 0x42,
+ 0x01, 0x0e, 0x88, 0x0a, 0x51, 0x01, 0x9e, 0x03, 0x0a, 0x42, 0x01, 0x0e, 0xfe, 0x80, 0x80, 0xf2,
+ 0xfe, 0x49, 0xe4, 0x10, 0xa4, 0x0a, 0x80, 0x01, 0x0e, 0xf2, 0x0a, 0x51, 0x01, 0x82, 0x03, 0x17,
+ 0x10, 0x71, 0x66, 0xfe, 0x60, 0x01, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x24, 0x1c, 0xfe,
+ 0x1d, 0xf7, 0x1d, 0x90, 0xfe, 0xf6, 0x15, 0x01, 0xfe, 0xfc, 0x16, 0xe0, 0x91, 0x1d, 0x66, 0xfe,
+ 0x2c, 0x01, 0xfe, 0x2f, 0x19, 0x03, 0xae, 0x21, 0xfe, 0xe6, 0x15, 0xfe, 0xda, 0x10, 0x17, 0x10,
+ 0x71, 0x05, 0xfe, 0x64, 0x01, 0xfe, 0x00, 0xf4, 0x19, 0xfe, 0x18, 0x58, 0x05, 0xfe, 0x66, 0x01,
+ 0xfe, 0x19, 0x58, 0x91, 0x19, 0xfe, 0x3c, 0x90, 0xfe, 0x30, 0xf4, 0x06, 0xfe, 0x3c, 0x50, 0x66,
+ 0xfe, 0x38, 0x00, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0x19, 0x90, 0xfe, 0x40, 0x16, 0xfe, 0xb6,
+ 0x14, 0x34, 0x03, 0xae, 0x21, 0xfe, 0x18, 0x16, 0xfe, 0x9c, 0x10, 0x17, 0x10, 0x71, 0xfe, 0x83,
+ 0x5a, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x1d, 0xf7, 0x38, 0x90, 0xfe, 0x62, 0x16, 0xfe,
+ 0x94, 0x14, 0xfe, 0x10, 0x13, 0x91, 0x38, 0x66, 0x1b, 0xfe, 0xaf, 0x19, 0xfe, 0x98, 0xe7, 0x00,
+ 0x03, 0xae, 0x21, 0xfe, 0x56, 0x16, 0xfe, 0x6c, 0x10, 0x17, 0x10, 0x71, 0xfe, 0x30, 0xbc, 0xfe,
+ 0xb2, 0xbc, 0x91, 0xc5, 0x66, 0x1b, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0xc5, 0x90, 0xfe, 0x9a,
+ 0x16, 0xfe, 0x5c, 0x14, 0x34, 0x03, 0xae, 0x21, 0xfe, 0x86, 0x16, 0xfe, 0x42, 0x10, 0xfe, 0x02,
+ 0xf6, 0x10, 0x71, 0xfe, 0x18, 0xfe, 0x54, 0xfe, 0x19, 0xfe, 0x55, 0xfc, 0xfe, 0x1d, 0xf7, 0x4f,
+ 0x90, 0xfe, 0xc0, 0x16, 0xfe, 0x36, 0x14, 0xfe, 0x1c, 0x13, 0x91, 0x4f, 0x47, 0xfe, 0x83, 0x58,
+ 0xfe, 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x10, 0xfe, 0x81, 0xe7, 0x10, 0x11, 0xfe, 0xdd, 0x00, 0x63,
+ 0x27, 0x03, 0x63, 0x27, 0xfe, 0x12, 0x45, 0x21, 0xfe, 0xb0, 0x16, 0x14, 0x06, 0x37, 0x95, 0xa9,
+ 0x02, 0x29, 0xfe, 0x39, 0xf0, 0xfe, 0x04, 0x17, 0x23, 0x03, 0xfe, 0x7e, 0x18, 0x1c, 0x1a, 0x5d,
+ 0x13, 0x0d, 0x03, 0x71, 0x05, 0xcb, 0x1c, 0x06, 0xfe, 0xef, 0x12, 0xfe, 0xe1, 0x10, 0x78, 0x2c,
+ 0x46, 0x2f, 0x07, 0x2d, 0xfe, 0x3c, 0x13, 0xfe, 0x82, 0x14, 0xfe, 0x42, 0x13, 0x3c, 0x8a, 0x0a,
+ 0x42, 0x01, 0x0e, 0xb0, 0xfe, 0x3e, 0x12, 0xf0, 0xfe, 0x45, 0x48, 0x01, 0xe3, 0xfe, 0x00, 0xcc,
+ 0xb0, 0xfe, 0xf3, 0x13, 0x3d, 0x75, 0x07, 0x10, 0xa3, 0x0a, 0x80, 0x01, 0x0e, 0xf2, 0x01, 0x6f,
+ 0xfe, 0x16, 0x10, 0x07, 0x7e, 0x85, 0xfe, 0x40, 0x14, 0xfe, 0x24, 0x12, 0xf6, 0xfe, 0xd6, 0xf0,
+ 0xfe, 0x24, 0x17, 0x17, 0x0b, 0x03, 0xfe, 0x9c, 0xe7, 0x0b, 0x0f, 0xfe, 0x15, 0x00, 0x59, 0x76,
+ 0x27, 0x01, 0xda, 0x17, 0x06, 0x03, 0x3c, 0x8a, 0x09, 0x4a, 0x1d, 0x35, 0x11, 0x2d, 0x01, 0x6f,
+ 0x17, 0x06, 0x03, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90, 0x79, 0xc7, 0x68, 0xc8, 0xfe, 0x48, 0x55,
+ 0x34, 0xfe, 0xc9, 0x55, 0x03, 0x1e, 0x98, 0x73, 0x12, 0x98, 0x03, 0x0a, 0x99, 0x01, 0x0e, 0xf0,
+ 0x0a, 0x40, 0x01, 0x0e, 0xfe, 0x49, 0x44, 0x16, 0xfe, 0xf0, 0x17, 0x73, 0x75, 0x03, 0x0a, 0x42,
+ 0x01, 0x0e, 0x07, 0x10, 0x45, 0x0a, 0x51, 0x01, 0x9e, 0x0a, 0x40, 0x01, 0x0e, 0x73, 0x75, 0x03,
+ 0xfe, 0x4e, 0xe4, 0x1a, 0x64, 0xfe, 0x24, 0x18, 0x05, 0xfe, 0x90, 0x00, 0xfe, 0x3a, 0x45, 0x5b,
+ 0xfe, 0x4e, 0xe4, 0xc2, 0x64, 0xfe, 0x36, 0x18, 0x05, 0xfe, 0x92, 0x00, 0xfe, 0x02, 0xe6, 0x1b,
+ 0xdc, 0xfe, 0x4e, 0xe4, 0xfe, 0x0b, 0x00, 0x64, 0xfe, 0x48, 0x18, 0x05, 0xfe, 0x94, 0x00, 0xfe,
+ 0x02, 0xe6, 0x19, 0xfe, 0x08, 0x10, 0x05, 0xfe, 0x96, 0x00, 0xfe, 0x02, 0xe6, 0x2c, 0xfe, 0x4e,
+ 0x45, 0xfe, 0x0c, 0x12, 0xaf, 0xff, 0x04, 0x68, 0x54, 0xde, 0x1c, 0x69, 0x03, 0x07, 0x7a, 0xfe,
+ 0x5a, 0xf0, 0xfe, 0x74, 0x18, 0x24, 0xfe, 0x09, 0x00, 0xfe, 0x34, 0x10, 0x07, 0x1b, 0xfe, 0x5a,
+ 0xf0, 0xfe, 0x82, 0x18, 0x24, 0xc3, 0xfe, 0x26, 0x10, 0x07, 0x1a, 0x5d, 0x24, 0x2c, 0xdc, 0x07,
+ 0x0b, 0x5d, 0x24, 0x93, 0xfe, 0x0e, 0x10, 0x07, 0x06, 0x5d, 0x24, 0x4d, 0x9f, 0xad, 0x03, 0x14,
+ 0xfe, 0x09, 0x00, 0x01, 0x33, 0xfe, 0x04, 0xfe, 0x7d, 0x05, 0x7f, 0xf9, 0x03, 0x25, 0xfe, 0xca,
+ 0x18, 0xfe, 0x14, 0xf0, 0x08, 0x65, 0xfe, 0xc6, 0x18, 0x03, 0xff, 0x1a, 0x00, 0x00,
+};
+
+STATIC unsigned short _adv_asc3550_size =
+ sizeof(_adv_asc3550_buf); /* 0x13AD */
+STATIC ADV_DCNT _adv_asc3550_chksum =
+ 0x04D52DDDUL; /* Expanded little-endian checksum. */
+
+/* Microcode buffer is kept after initialization for error recovery. */
+STATIC unsigned char _adv_asc38C0800_buf[] = {
+ 0x00, 0x00, 0x00, 0xf2, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0x16, 0x18, 0xe4, 0x01, 0x00, 0x48, 0xe4,
+ 0x18, 0x80, 0x03, 0xf6, 0x02, 0x00, 0xce, 0x19, 0x00, 0xfa, 0xff, 0xff, 0x1c, 0x0f, 0x00, 0xf6,
+ 0x9e, 0xe7, 0xff, 0x00, 0x82, 0xe7, 0x00, 0xea, 0x01, 0xfa, 0x01, 0xe6, 0x09, 0xe7, 0x55, 0xf0,
+ 0x01, 0xf6, 0x03, 0x00, 0x04, 0x00, 0x10, 0x00, 0x1e, 0xf0, 0x85, 0xf0, 0x18, 0xf4, 0x08, 0x00,
+ 0xbc, 0x00, 0x38, 0x54, 0x00, 0xec, 0xd5, 0xf0, 0x82, 0x0d, 0x00, 0xe6, 0x86, 0xf0, 0xb1, 0xf0,
+ 0x98, 0x57, 0x01, 0xfc, 0xb4, 0x00, 0xd4, 0x01, 0x0c, 0x1c, 0x3e, 0x1c, 0x3c, 0x00, 0xbb, 0x00,
+ 0x00, 0x10, 0xba, 0x19, 0x02, 0x80, 0x32, 0xf0, 0x7c, 0x0d, 0x02, 0x13, 0xba, 0x13, 0x18, 0x40,
+ 0x00, 0x57, 0x01, 0xea, 0x02, 0xfc, 0x03, 0xfc, 0x3e, 0x00, 0x6c, 0x01, 0x6e, 0x01, 0x74, 0x01,
+ 0x76, 0x01, 0xb9, 0x54, 0x3e, 0x57, 0x00, 0x80, 0x03, 0xe6, 0xb6, 0x00, 0xc0, 0x00, 0x01, 0x01,
+ 0x3e, 0x01, 0x7a, 0x01, 0xca, 0x08, 0xce, 0x10, 0x16, 0x11, 0x04, 0x12, 0x08, 0x12, 0x02, 0x4a,
+ 0xbb, 0x55, 0x3c, 0x56, 0x03, 0x58, 0x1b, 0x80, 0x30, 0xe4, 0x4b, 0xe4, 0x5d, 0xf0, 0x02, 0xfa,
+ 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x80, 0x00, 0x24, 0x01, 0x3c, 0x01, 0x68, 0x01, 0x6a, 0x01,
+ 0x70, 0x01, 0x72, 0x01, 0x78, 0x01, 0x7c, 0x01, 0x62, 0x0a, 0x86, 0x0d, 0x06, 0x13, 0x4c, 0x1c,
+ 0x04, 0x80, 0x4a, 0xe4, 0x02, 0xee, 0x5b, 0xf0, 0x03, 0xf7, 0x0c, 0x00, 0x0f, 0x00, 0x47, 0x00,
+ 0xbe, 0x00, 0x00, 0x01, 0x20, 0x11, 0x5c, 0x16, 0x32, 0x1c, 0x38, 0x1c, 0x4e, 0x1c, 0x10, 0x44,
+ 0x00, 0x4c, 0x04, 0xea, 0x5c, 0xf0, 0xa7, 0xf0, 0x04, 0xf6, 0x03, 0xfa, 0x05, 0x00, 0x34, 0x00,
+ 0x36, 0x00, 0x98, 0x00, 0xcc, 0x00, 0x20, 0x01, 0x4e, 0x01, 0x4a, 0x0b, 0x42, 0x0c, 0x12, 0x0f,
+ 0x0c, 0x10, 0x22, 0x11, 0x0a, 0x12, 0x04, 0x13, 0x30, 0x1c, 0x02, 0x48, 0x00, 0x4e, 0x42, 0x54,
+ 0x44, 0x55, 0xbd, 0x56, 0x06, 0x83, 0x00, 0xdc, 0x05, 0xf0, 0x09, 0xf0, 0x59, 0xf0, 0xb8, 0xf0,
+ 0x4b, 0xf4, 0x06, 0xf7, 0x0e, 0xf7, 0x04, 0xfc, 0x05, 0xfc, 0x06, 0x00, 0x19, 0x00, 0x33, 0x00,
+ 0x9b, 0x00, 0xa4, 0x00, 0xb5, 0x00, 0xba, 0x00, 0xd0, 0x00, 0xe1, 0x00, 0xe7, 0x00, 0xe2, 0x03,
+ 0x08, 0x0f, 0x02, 0x10, 0x04, 0x10, 0x0a, 0x10, 0x0a, 0x13, 0x0c, 0x13, 0x12, 0x13, 0x24, 0x14,
+ 0x34, 0x14, 0x04, 0x16, 0x08, 0x16, 0xa4, 0x17, 0x20, 0x1c, 0x34, 0x1c, 0x36, 0x1c, 0x08, 0x44,
+ 0x38, 0x44, 0x91, 0x44, 0x0a, 0x45, 0x48, 0x46, 0x01, 0x48, 0x68, 0x54, 0x3a, 0x55, 0x83, 0x55,
+ 0xe5, 0x55, 0xb0, 0x57, 0x01, 0x58, 0x83, 0x59, 0x05, 0xe6, 0x0b, 0xf0, 0x0c, 0xf0, 0x04, 0xf8,
+ 0x05, 0xf8, 0x07, 0x00, 0x0a, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x9e, 0x00, 0xa8, 0x00, 0xaa, 0x00,
+ 0xb9, 0x00, 0xe0, 0x00, 0x22, 0x01, 0x26, 0x01, 0x79, 0x01, 0x7e, 0x01, 0xc4, 0x01, 0xc6, 0x01,
+ 0x80, 0x02, 0x5e, 0x03, 0xee, 0x04, 0x9a, 0x06, 0xf8, 0x07, 0x62, 0x08, 0x68, 0x08, 0x69, 0x08,
+ 0xd6, 0x08, 0xe9, 0x09, 0xfa, 0x0b, 0x2e, 0x0f, 0x12, 0x10, 0x1a, 0x10, 0xed, 0x10, 0xf1, 0x10,
+ 0x2a, 0x11, 0x06, 0x12, 0x0c, 0x12, 0x3e, 0x12, 0x10, 0x13, 0x16, 0x13, 0x1e, 0x13, 0x46, 0x14,
+ 0x76, 0x14, 0x82, 0x14, 0x36, 0x15, 0xca, 0x15, 0x6b, 0x18, 0xbe, 0x18, 0xca, 0x18, 0xe6, 0x19,
+ 0x12, 0x1c, 0x46, 0x1c, 0x9c, 0x32, 0x00, 0x40, 0x0e, 0x47, 0xfe, 0x9c, 0xf0, 0x2b, 0x02, 0xfe,
+ 0xac, 0x0d, 0xff, 0x10, 0x00, 0x00, 0xd7, 0xfe, 0xe8, 0x19, 0x00, 0xd6, 0xfe, 0x84, 0x01, 0xff,
+ 0x03, 0x00, 0x00, 0xfe, 0x93, 0x15, 0xfe, 0x0f, 0x05, 0xff, 0x38, 0x00, 0x00, 0xfe, 0x57, 0x24,
+ 0x00, 0xfe, 0x4c, 0x00, 0x5b, 0xff, 0x04, 0x00, 0x00, 0x11, 0xff, 0x09, 0x00, 0x00, 0xff, 0x08,
+ 0x01, 0x01, 0xff, 0x08, 0xff, 0xff, 0xff, 0x27, 0x00, 0x00, 0xff, 0x10, 0xff, 0xff, 0xff, 0x11,
+ 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, 0xff, 0x21, 0x00, 0x00, 0xfe, 0x04, 0xf7, 0xd6,
+ 0x2c, 0x99, 0x0a, 0x01, 0xfe, 0xc2, 0x0f, 0xfe, 0x04, 0xf7, 0xd6, 0x99, 0x0a, 0x42, 0x2c, 0xfe,
+ 0x3d, 0xf0, 0xfe, 0x06, 0x02, 0xfe, 0x20, 0xf0, 0xa7, 0xfe, 0x91, 0xf0, 0xfe, 0xf4, 0x01, 0xfe,
+ 0x90, 0xf0, 0xfe, 0xf4, 0x01, 0xfe, 0x8f, 0xf0, 0xa7, 0x03, 0x5d, 0x4d, 0x02, 0xfe, 0xc8, 0x0d,
+ 0x01, 0xfe, 0x38, 0x0e, 0xfe, 0xdd, 0x12, 0xfe, 0xfc, 0x10, 0xfe, 0x28, 0x1c, 0x03, 0xfe, 0xa6,
+ 0x00, 0xfe, 0xd3, 0x12, 0x41, 0x14, 0xfe, 0xa6, 0x00, 0xc2, 0xfe, 0x48, 0xf0, 0xfe, 0x8a, 0x02,
+ 0xfe, 0x49, 0xf0, 0xfe, 0xa4, 0x02, 0xfe, 0x4a, 0xf0, 0xfe, 0xc2, 0x02, 0xfe, 0x46, 0xf0, 0xfe,
+ 0x54, 0x02, 0xfe, 0x47, 0xf0, 0xfe, 0x5a, 0x02, 0xfe, 0x43, 0xf0, 0xfe, 0x48, 0x02, 0xfe, 0x44,
+ 0xf0, 0xfe, 0x4c, 0x02, 0xfe, 0x45, 0xf0, 0xfe, 0x50, 0x02, 0x18, 0x0a, 0xaa, 0x18, 0x06, 0x14,
+ 0xa1, 0x02, 0x2b, 0xfe, 0x00, 0x1c, 0xe7, 0xfe, 0x02, 0x1c, 0xe6, 0xfe, 0x1e, 0x1c, 0xfe, 0xe9,
+ 0x10, 0x01, 0xfe, 0x18, 0x18, 0xfe, 0xe7, 0x10, 0xfe, 0x06, 0xfc, 0xce, 0x09, 0x70, 0x01, 0xa8,
+ 0x02, 0x2b, 0x15, 0x59, 0x39, 0xa2, 0x01, 0xfe, 0x58, 0x10, 0x09, 0x70, 0x01, 0x87, 0xfe, 0xbd,
+ 0x10, 0x09, 0x70, 0x01, 0x87, 0xfe, 0xad, 0x10, 0xfe, 0x16, 0x1c, 0xfe, 0x58, 0x1c, 0x18, 0x06,
+ 0x14, 0xa1, 0x2c, 0x1c, 0x2b, 0xfe, 0x3d, 0xf0, 0xfe, 0x06, 0x02, 0x23, 0xfe, 0x98, 0x02, 0xfe,
+ 0x5a, 0x1c, 0xf8, 0xfe, 0x14, 0x1c, 0x15, 0xfe, 0x30, 0x00, 0x39, 0xa2, 0x01, 0xfe, 0x48, 0x10,
+ 0x18, 0x06, 0x14, 0xa1, 0x02, 0xd7, 0x22, 0x20, 0x07, 0x11, 0x35, 0xfe, 0x69, 0x10, 0x18, 0x06,
+ 0x14, 0xa1, 0xfe, 0x04, 0xec, 0x20, 0x4f, 0x43, 0x13, 0x20, 0xfe, 0x05, 0xf6, 0xce, 0x01, 0xfe,
+ 0x4a, 0x17, 0x08, 0x54, 0x58, 0x37, 0x12, 0x2f, 0x42, 0x92, 0x01, 0xfe, 0x82, 0x16, 0x02, 0x2b,
+ 0x09, 0x46, 0x01, 0x0e, 0x07, 0x00, 0x66, 0x01, 0x73, 0xfe, 0x18, 0x10, 0xfe, 0x41, 0x58, 0x09,
+ 0xa4, 0x01, 0x0e, 0xfe, 0xc8, 0x54, 0x6b, 0xfe, 0x10, 0x03, 0x01, 0xfe, 0x82, 0x16, 0x02, 0x2b,
+ 0x2c, 0x4f, 0xfe, 0x02, 0xe8, 0x2a, 0xfe, 0xbf, 0x57, 0xfe, 0x9e, 0x43, 0xfe, 0x77, 0x57, 0xfe,
+ 0x27, 0xf0, 0xfe, 0xe0, 0x01, 0xfe, 0x07, 0x4b, 0xfe, 0x20, 0xf0, 0xa7, 0xfe, 0x40, 0x1c, 0x1c,
+ 0xd9, 0xfe, 0x26, 0xf0, 0xfe, 0x5a, 0x03, 0xfe, 0xa0, 0xf0, 0xfe, 0x48, 0x03, 0xfe, 0x11, 0xf0,
+ 0xa7, 0xfe, 0xef, 0x10, 0xfe, 0x9f, 0xf0, 0xfe, 0x68, 0x03, 0xf9, 0x10, 0xfe, 0x11, 0x00, 0x02,
+ 0x65, 0x2c, 0xfe, 0x48, 0x1c, 0xf9, 0x08, 0x05, 0x1b, 0xfe, 0x18, 0x13, 0x21, 0x22, 0xa3, 0xb7,
+ 0x13, 0xa3, 0x09, 0x46, 0x01, 0x0e, 0xb7, 0x78, 0x01, 0xfe, 0xb4, 0x16, 0x12, 0xd1, 0x1c, 0xd9,
+ 0xfe, 0x01, 0xf0, 0xd9, 0xfe, 0x82, 0xf0, 0xfe, 0x96, 0x03, 0xfa, 0x12, 0xfe, 0xe4, 0x00, 0x27,
+ 0xfe, 0xa8, 0x03, 0x1c, 0x34, 0x1d, 0xfe, 0xb8, 0x03, 0x01, 0x4b, 0xfe, 0x06, 0xf0, 0xfe, 0xc8,
+ 0x03, 0x95, 0x86, 0xfe, 0x0a, 0xf0, 0xfe, 0x8a, 0x06, 0x02, 0x24, 0x03, 0x70, 0x28, 0x17, 0xfe,
+ 0xfa, 0x04, 0x15, 0x6d, 0x01, 0x36, 0x7b, 0xfe, 0x6a, 0x02, 0x02, 0xd8, 0xf9, 0x2c, 0x99, 0x19,
+ 0xfe, 0x67, 0x1b, 0xfe, 0xbf, 0x57, 0xfe, 0x77, 0x57, 0xfe, 0x48, 0x1c, 0x74, 0x01, 0xaf, 0x8c,
+ 0x09, 0x46, 0x01, 0x0e, 0x07, 0x00, 0x17, 0xda, 0x09, 0xd1, 0x01, 0x0e, 0x8d, 0x51, 0x64, 0x79,
+ 0x2a, 0x03, 0x70, 0x28, 0xfe, 0x10, 0x12, 0x15, 0x6d, 0x01, 0x36, 0x7b, 0xfe, 0x6a, 0x02, 0x02,
+ 0xd8, 0xc7, 0x81, 0xc8, 0x83, 0x1c, 0x24, 0x27, 0xfe, 0x40, 0x04, 0x1d, 0xfe, 0x3c, 0x04, 0x3b,
+ 0xfe, 0xa0, 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x4e, 0x12, 0x2d, 0xff, 0x02, 0x00, 0x10, 0x01, 0x0b,
+ 0x1d, 0xfe, 0xe4, 0x04, 0x2d, 0x01, 0x0b, 0x1d, 0x24, 0x33, 0x31, 0xde, 0xfe, 0x4c, 0x44, 0xfe,
+ 0x4c, 0x12, 0x51, 0xfe, 0x44, 0x48, 0x0f, 0x6f, 0xfe, 0x4c, 0x54, 0x6b, 0xda, 0x4f, 0x79, 0x2a,
+ 0xfe, 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x62, 0x13, 0x08, 0x05, 0x1b, 0xfe, 0x2a, 0x13, 0x32,
+ 0x07, 0x82, 0xfe, 0x52, 0x13, 0xfe, 0x20, 0x10, 0x0f, 0x6f, 0xfe, 0x4c, 0x54, 0x6b, 0xda, 0xfe,
+ 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x40, 0x13, 0x08, 0x05, 0x1b, 0xfe, 0x08, 0x13, 0x32, 0x07,
+ 0x82, 0xfe, 0x30, 0x13, 0x08, 0x05, 0x1b, 0xfe, 0x1c, 0x12, 0x15, 0x9d, 0x08, 0x05, 0x06, 0x4d,
+ 0x15, 0xfe, 0x0d, 0x00, 0x01, 0x36, 0x7b, 0xfe, 0x64, 0x0d, 0x02, 0x24, 0x2d, 0x12, 0xfe, 0xe6,
+ 0x00, 0xfe, 0x1c, 0x90, 0xfe, 0x40, 0x5c, 0x04, 0x15, 0x9d, 0x01, 0x36, 0x02, 0x2b, 0xfe, 0x42,
+ 0x5b, 0x99, 0x19, 0xfe, 0x46, 0x59, 0xfe, 0xbf, 0x57, 0xfe, 0x77, 0x57, 0xfe, 0x87, 0x80, 0xfe,
+ 0x31, 0xe4, 0x5b, 0x08, 0x05, 0x0a, 0xfe, 0x84, 0x13, 0xfe, 0x20, 0x80, 0x07, 0x19, 0xfe, 0x7c,
+ 0x12, 0x53, 0x05, 0x06, 0xfe, 0x6c, 0x13, 0x03, 0xfe, 0xa2, 0x00, 0x28, 0x17, 0xfe, 0x90, 0x05,
+ 0xfe, 0x31, 0xe4, 0x5a, 0x53, 0x05, 0x0a, 0xfe, 0x56, 0x13, 0x03, 0xfe, 0xa0, 0x00, 0x28, 0xfe,
+ 0x4e, 0x12, 0x67, 0xff, 0x02, 0x00, 0x10, 0x27, 0xfe, 0x48, 0x05, 0x1c, 0x34, 0xfe, 0x89, 0x48,
+ 0xff, 0x02, 0x00, 0x10, 0x27, 0xfe, 0x56, 0x05, 0x26, 0xfe, 0xa8, 0x05, 0x12, 0xfe, 0xe3, 0x00,
+ 0x21, 0x53, 0xfe, 0x4a, 0xf0, 0xfe, 0x76, 0x05, 0xfe, 0x49, 0xf0, 0xfe, 0x70, 0x05, 0x88, 0x25,
+ 0xfe, 0x21, 0x00, 0xab, 0x25, 0xfe, 0x22, 0x00, 0xaa, 0x25, 0x58, 0xfe, 0x09, 0x48, 0xff, 0x02,
+ 0x00, 0x10, 0x27, 0xfe, 0x86, 0x05, 0x26, 0xfe, 0xa8, 0x05, 0xfe, 0xe2, 0x08, 0x53, 0x05, 0xcb,
+ 0x4d, 0x01, 0xb0, 0x25, 0x06, 0x13, 0xd3, 0x39, 0xfe, 0x27, 0x01, 0x08, 0x05, 0x1b, 0xfe, 0x22,
+ 0x12, 0x41, 0x01, 0xb2, 0x15, 0x9d, 0x08, 0x05, 0x06, 0x4d, 0x15, 0xfe, 0x0d, 0x00, 0x01, 0x36,
+ 0x7b, 0xfe, 0x64, 0x0d, 0x02, 0x24, 0x03, 0xfe, 0x9c, 0x00, 0x28, 0xeb, 0x03, 0x5c, 0x28, 0xfe,
+ 0x36, 0x13, 0x41, 0x01, 0xb2, 0x26, 0xfe, 0x18, 0x06, 0x09, 0x06, 0x53, 0x05, 0x1f, 0xfe, 0x02,
+ 0x12, 0x50, 0x01, 0xfe, 0x9e, 0x15, 0x1d, 0xfe, 0x0e, 0x06, 0x12, 0xa5, 0x01, 0x4b, 0x12, 0xfe,
+ 0xe5, 0x00, 0x03, 0x5c, 0xc1, 0x0c, 0x5c, 0x03, 0xcd, 0x28, 0xfe, 0x62, 0x12, 0x03, 0x45, 0x28,
+ 0xfe, 0x5a, 0x13, 0x01, 0xfe, 0x0c, 0x19, 0x01, 0xfe, 0x76, 0x19, 0xfe, 0x43, 0x48, 0xc4, 0xcc,
+ 0x0f, 0x71, 0xff, 0x02, 0x00, 0x57, 0x52, 0x93, 0x1e, 0x43, 0x8b, 0xc4, 0x6e, 0x41, 0x01, 0xb2,
+ 0x26, 0xfe, 0x82, 0x06, 0x53, 0x05, 0x1a, 0xe9, 0x91, 0x09, 0x59, 0x01, 0xfe, 0xcc, 0x15, 0x1d,
+ 0xfe, 0x78, 0x06, 0x12, 0xa5, 0x01, 0x4b, 0x12, 0xfe, 0xe5, 0x00, 0x03, 0x45, 0xc1, 0x0c, 0x45,
+ 0x18, 0x06, 0x01, 0xb2, 0xfa, 0x76, 0x74, 0x01, 0xaf, 0x8c, 0x12, 0xfe, 0xe2, 0x00, 0x27, 0xdb,
+ 0x1c, 0x34, 0xfe, 0x0a, 0xf0, 0xfe, 0xb6, 0x06, 0x94, 0xfe, 0x6c, 0x07, 0xfe, 0x06, 0xf0, 0xfe,
+ 0x74, 0x07, 0x95, 0x86, 0x02, 0x24, 0x08, 0x05, 0x0a, 0xfe, 0x2e, 0x12, 0x16, 0x19, 0x01, 0x0b,
+ 0x16, 0x00, 0x01, 0x0b, 0x16, 0x00, 0x01, 0x0b, 0x16, 0x00, 0x01, 0x0b, 0xfe, 0x99, 0xa4, 0x01,
+ 0x0b, 0x16, 0x00, 0x02, 0xfe, 0x42, 0x08, 0x68, 0x05, 0x1a, 0xfe, 0x38, 0x12, 0x08, 0x05, 0x1a,
+ 0xfe, 0x30, 0x13, 0x16, 0xfe, 0x1b, 0x00, 0x01, 0x0b, 0x16, 0x00, 0x01, 0x0b, 0x16, 0x00, 0x01,
+ 0x0b, 0x16, 0x00, 0x01, 0x0b, 0x16, 0x06, 0x01, 0x0b, 0x16, 0x00, 0x02, 0xe2, 0x6c, 0x58, 0xbe,
+ 0x50, 0xfe, 0x9a, 0x81, 0x55, 0x1b, 0x7a, 0xfe, 0x42, 0x07, 0x09, 0x1b, 0xfe, 0x09, 0x6f, 0xba,
+ 0xfe, 0xca, 0x45, 0xfe, 0x32, 0x12, 0x69, 0x6d, 0x8b, 0x6c, 0x7f, 0x27, 0xfe, 0x54, 0x07, 0x1c,
+ 0x34, 0xfe, 0x0a, 0xf0, 0xfe, 0x42, 0x07, 0x95, 0x86, 0x94, 0xfe, 0x6c, 0x07, 0x02, 0x24, 0x01,
+ 0x4b, 0x02, 0xdb, 0x16, 0x1f, 0x02, 0xdb, 0xfe, 0x9c, 0xf7, 0xdc, 0xfe, 0x2c, 0x90, 0xfe, 0xae,
+ 0x90, 0x56, 0xfe, 0xda, 0x07, 0x0c, 0x60, 0x14, 0x61, 0x08, 0x54, 0x5a, 0x37, 0x22, 0x20, 0x07,
+ 0x11, 0xfe, 0x0e, 0x12, 0x8d, 0xfe, 0x80, 0x80, 0x39, 0x20, 0x6a, 0x2a, 0xfe, 0x06, 0x10, 0xfe,
+ 0x83, 0xe7, 0xfe, 0x48, 0x00, 0xab, 0xfe, 0x03, 0x40, 0x08, 0x54, 0x5b, 0x37, 0x01, 0xb3, 0xb8,
+ 0xfe, 0x1f, 0x40, 0x13, 0x62, 0x01, 0xef, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0xfe, 0x44, 0x51,
+ 0xfe, 0xc6, 0x51, 0x88, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, 0x0c, 0x5e, 0x14, 0x5f, 0xfe, 0x0c,
+ 0x90, 0xfe, 0x8e, 0x90, 0xfe, 0x40, 0x50, 0xfe, 0xc2, 0x50, 0x0c, 0x3d, 0x14, 0x3e, 0xfe, 0x4a,
+ 0x10, 0x08, 0x05, 0x5a, 0xfe, 0x2a, 0x12, 0xfe, 0x2c, 0x90, 0xfe, 0xae, 0x90, 0x0c, 0x60, 0x14,
+ 0x61, 0x08, 0x05, 0x5b, 0x8b, 0x01, 0xb3, 0xfe, 0x1f, 0x80, 0x13, 0x62, 0xfe, 0x44, 0x90, 0xfe,
+ 0xc6, 0x90, 0x0c, 0x3f, 0x14, 0x40, 0xfe, 0x08, 0x90, 0xfe, 0x8a, 0x90, 0x0c, 0x5e, 0x14, 0x5f,
+ 0xfe, 0x40, 0x90, 0xfe, 0xc2, 0x90, 0x0c, 0x3d, 0x14, 0x3e, 0x0c, 0x2e, 0x14, 0x3c, 0x21, 0x0c,
+ 0x49, 0x0c, 0x63, 0x08, 0x54, 0x1f, 0x37, 0x2c, 0x0f, 0xfe, 0x4e, 0x11, 0x27, 0xdd, 0xfe, 0x9e,
+ 0xf0, 0xfe, 0x76, 0x08, 0xbc, 0x17, 0x34, 0x2c, 0x77, 0xe6, 0xc5, 0xfe, 0x9a, 0x08, 0xc6, 0xfe,
+ 0xb8, 0x08, 0x94, 0xfe, 0x8e, 0x08, 0xfe, 0x06, 0xf0, 0xfe, 0x94, 0x08, 0x95, 0x86, 0x02, 0x24,
+ 0x01, 0x4b, 0xfe, 0xc9, 0x10, 0x16, 0x1f, 0xfe, 0xc9, 0x10, 0x68, 0x05, 0x06, 0xfe, 0x10, 0x12,
+ 0x68, 0x05, 0x0a, 0x4e, 0x08, 0x05, 0x0a, 0xfe, 0x90, 0x12, 0xfe, 0x2e, 0x1c, 0x02, 0xfe, 0x18,
+ 0x0b, 0x68, 0x05, 0x06, 0x4e, 0x68, 0x05, 0x0a, 0xfe, 0x7a, 0x12, 0xfe, 0x2c, 0x1c, 0xfe, 0xaa,
+ 0xf0, 0xfe, 0xd2, 0x09, 0xfe, 0xac, 0xf0, 0xfe, 0x00, 0x09, 0x02, 0xfe, 0xde, 0x09, 0xfe, 0xb7,
+ 0xf0, 0xfe, 0xfc, 0x08, 0xfe, 0x02, 0xf6, 0x1a, 0x50, 0xfe, 0x70, 0x18, 0xfe, 0xf1, 0x18, 0xfe,
+ 0x40, 0x55, 0xfe, 0xe1, 0x55, 0xfe, 0x10, 0x58, 0xfe, 0x91, 0x58, 0xfe, 0x14, 0x59, 0xfe, 0x95,
+ 0x59, 0x1c, 0x85, 0xfe, 0x8c, 0xf0, 0xfe, 0xfc, 0x08, 0xfe, 0xac, 0xf0, 0xfe, 0xf0, 0x08, 0xb5,
+ 0xfe, 0xcb, 0x10, 0xfe, 0xad, 0xf0, 0xfe, 0x0c, 0x09, 0x02, 0xfe, 0x18, 0x0b, 0xb6, 0xfe, 0xbf,
+ 0x10, 0xfe, 0x2b, 0xf0, 0x85, 0xf4, 0x1e, 0xfe, 0x00, 0xfe, 0xfe, 0x1c, 0x12, 0xc2, 0xfe, 0xd2,
+ 0xf0, 0x85, 0xfe, 0x76, 0x18, 0x1e, 0x19, 0x17, 0x85, 0x03, 0xd2, 0x1e, 0x06, 0x17, 0x85, 0xc5,
+ 0x4a, 0xc6, 0x4a, 0xb5, 0xb6, 0xfe, 0x89, 0x10, 0x74, 0x67, 0x2d, 0x15, 0x9d, 0x01, 0x36, 0x10,
+ 0xfe, 0x35, 0x00, 0xfe, 0x01, 0xf0, 0x65, 0x10, 0x80, 0x02, 0x65, 0xfe, 0x98, 0x80, 0xfe, 0x19,
+ 0xe4, 0x0a, 0xfe, 0x1a, 0x12, 0x51, 0xfe, 0x19, 0x82, 0xfe, 0x6c, 0x18, 0xfe, 0x44, 0x54, 0xbe,
+ 0xfe, 0x19, 0x81, 0xfe, 0x74, 0x18, 0x8f, 0x90, 0x17, 0xfe, 0xce, 0x08, 0x02, 0x4a, 0x08, 0x05,
+ 0x5a, 0xec, 0x03, 0x2e, 0x29, 0x3c, 0x0c, 0x3f, 0x14, 0x40, 0x9b, 0x2e, 0x9c, 0x3c, 0xfe, 0x6c,
+ 0x18, 0xfe, 0xed, 0x18, 0xfe, 0x44, 0x54, 0xfe, 0xe5, 0x54, 0x3a, 0x3f, 0x3b, 0x40, 0x03, 0x49,
+ 0x29, 0x63, 0x8f, 0xfe, 0xe3, 0x54, 0xfe, 0x74, 0x18, 0xfe, 0xf5, 0x18, 0x8f, 0xfe, 0xe3, 0x54,
+ 0x90, 0xc0, 0x56, 0xfe, 0xce, 0x08, 0x02, 0x4a, 0xfe, 0x37, 0xf0, 0xfe, 0xda, 0x09, 0xfe, 0x8b,
+ 0xf0, 0xfe, 0x60, 0x09, 0x02, 0x4a, 0x08, 0x05, 0x0a, 0x23, 0xfe, 0xfa, 0x0a, 0x3a, 0x49, 0x3b,
+ 0x63, 0x56, 0xfe, 0x3e, 0x0a, 0x0f, 0xfe, 0xc0, 0x07, 0x41, 0x98, 0x00, 0xad, 0xfe, 0x01, 0x59,
+ 0xfe, 0x52, 0xf0, 0xfe, 0x0c, 0x0a, 0x8f, 0x7a, 0xfe, 0x24, 0x0a, 0x3a, 0x49, 0x8f, 0xfe, 0xe3,
+ 0x54, 0x57, 0x49, 0x7d, 0x63, 0xfe, 0x14, 0x58, 0xfe, 0x95, 0x58, 0x02, 0x4a, 0x3a, 0x49, 0x3b,
+ 0x63, 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0xbe, 0x57, 0x49, 0x57, 0x63, 0x02, 0x4a, 0x08, 0x05,
+ 0x5a, 0xfe, 0x82, 0x12, 0x08, 0x05, 0x1f, 0xfe, 0x66, 0x13, 0x22, 0x62, 0xb7, 0xfe, 0x03, 0xa1,
+ 0xfe, 0x83, 0x80, 0xfe, 0xc8, 0x44, 0xfe, 0x2e, 0x13, 0xfe, 0x04, 0x91, 0xfe, 0x86, 0x91, 0x6a,
+ 0x2a, 0xfe, 0x40, 0x59, 0xfe, 0xc1, 0x59, 0x56, 0xe0, 0x03, 0x60, 0x29, 0x61, 0x0c, 0x7f, 0x14,
+ 0x80, 0x57, 0x60, 0x7d, 0x61, 0x01, 0xb3, 0xb8, 0x6a, 0x2a, 0x13, 0x62, 0x9b, 0x2e, 0x9c, 0x3c,
+ 0x3a, 0x3f, 0x3b, 0x40, 0x90, 0xc0, 0xfe, 0x04, 0xfa, 0x2e, 0xfe, 0x05, 0xfa, 0x3c, 0x01, 0xef,
+ 0xfe, 0x36, 0x10, 0x21, 0x0c, 0x7f, 0x0c, 0x80, 0x3a, 0x3f, 0x3b, 0x40, 0xe4, 0x08, 0x05, 0x1f,
+ 0x17, 0xe0, 0x3a, 0x3d, 0x3b, 0x3e, 0x08, 0x05, 0xfe, 0xf7, 0x00, 0x37, 0x03, 0x5e, 0x29, 0x5f,
+ 0xfe, 0x10, 0x58, 0xfe, 0x91, 0x58, 0x57, 0x49, 0x7d, 0x63, 0x02, 0xfe, 0xf4, 0x09, 0x08, 0x05,
+ 0x1f, 0x17, 0xe0, 0x08, 0x05, 0xfe, 0xf7, 0x00, 0x37, 0xbe, 0xfe, 0x19, 0x81, 0x50, 0xfe, 0x10,
+ 0x90, 0xfe, 0x92, 0x90, 0xfe, 0xd3, 0x10, 0x32, 0x07, 0xa6, 0x17, 0xfe, 0x08, 0x09, 0x12, 0xa6,
+ 0x08, 0x05, 0x0a, 0xfe, 0x14, 0x13, 0x03, 0x3d, 0x29, 0x3e, 0x56, 0xfe, 0x08, 0x09, 0xfe, 0x0c,
+ 0x58, 0xfe, 0x8d, 0x58, 0x02, 0x4a, 0x21, 0x41, 0xfe, 0x19, 0x80, 0xe7, 0x08, 0x05, 0x0a, 0xfe,
+ 0x1a, 0x12, 0xfe, 0x6c, 0x19, 0xfe, 0x19, 0x41, 0xf4, 0xc2, 0xfe, 0xd1, 0xf0, 0xe2, 0x15, 0x7e,
+ 0x01, 0x36, 0x10, 0xfe, 0x44, 0x00, 0xfe, 0x8e, 0x10, 0xfe, 0x6c, 0x19, 0x57, 0x3d, 0xfe, 0xed,
+ 0x19, 0x7d, 0x3e, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0xf4, 0x1e, 0xfe, 0x00, 0xff, 0x35, 0xfe,
+ 0x74, 0x10, 0xc2, 0xfe, 0xd2, 0xf0, 0xfe, 0xa6, 0x0b, 0xfe, 0x76, 0x18, 0x1e, 0x19, 0x8a, 0x03,
+ 0xd2, 0x1e, 0x06, 0xfe, 0x08, 0x13, 0x10, 0xfe, 0x16, 0x00, 0x02, 0x65, 0xfe, 0xd1, 0xf0, 0xfe,
+ 0xb8, 0x0b, 0x15, 0x7e, 0x01, 0x36, 0x10, 0xfe, 0x17, 0x00, 0xfe, 0x42, 0x10, 0xfe, 0xce, 0xf0,
+ 0xfe, 0xbe, 0x0b, 0xfe, 0x3c, 0x10, 0xfe, 0xcd, 0xf0, 0xfe, 0xca, 0x0b, 0x10, 0xfe, 0x22, 0x00,
+ 0x02, 0x65, 0xfe, 0xcb, 0xf0, 0xfe, 0xd6, 0x0b, 0x10, 0xfe, 0x24, 0x00, 0x02, 0x65, 0xfe, 0xd0,
+ 0xf0, 0xfe, 0xe0, 0x0b, 0x10, 0x9e, 0xe5, 0xfe, 0xcf, 0xf0, 0xfe, 0xea, 0x0b, 0x10, 0x58, 0xfe,
+ 0x10, 0x10, 0xfe, 0xcc, 0xf0, 0xe2, 0x68, 0x05, 0x1f, 0x4d, 0x10, 0xfe, 0x12, 0x00, 0x2c, 0x0f,
+ 0xfe, 0x4e, 0x11, 0x27, 0xfe, 0x00, 0x0c, 0xfe, 0x9e, 0xf0, 0xfe, 0x14, 0x0c, 0xbc, 0x17, 0x34,
+ 0x2c, 0x77, 0xe6, 0xc5, 0x24, 0xc6, 0x24, 0x2c, 0xfa, 0x27, 0xfe, 0x20, 0x0c, 0x1c, 0x34, 0x94,
+ 0xfe, 0x3c, 0x0c, 0x95, 0x86, 0xc5, 0xdc, 0xc6, 0xdc, 0x02, 0x24, 0x01, 0x4b, 0xfe, 0xdb, 0x10,
+ 0x12, 0xfe, 0xe8, 0x00, 0xb5, 0xb6, 0x74, 0xc7, 0x81, 0xc8, 0x83, 0xfe, 0x89, 0xf0, 0x24, 0x33,
+ 0x31, 0xe1, 0xc7, 0x81, 0xc8, 0x83, 0x27, 0xfe, 0x66, 0x0c, 0x1d, 0x24, 0x33, 0x31, 0xdf, 0xbc,
+ 0x4e, 0x10, 0xfe, 0x42, 0x00, 0x02, 0x65, 0x7c, 0x06, 0xfe, 0x81, 0x49, 0x17, 0xfe, 0x2c, 0x0d,
+ 0x08, 0x05, 0x0a, 0xfe, 0x44, 0x13, 0x10, 0x00, 0x55, 0x0a, 0xfe, 0x54, 0x12, 0x55, 0xfe, 0x28,
+ 0x00, 0x23, 0xfe, 0x9a, 0x0d, 0x09, 0x46, 0x01, 0x0e, 0x07, 0x00, 0x66, 0x44, 0xfe, 0x28, 0x00,
+ 0xfe, 0xe2, 0x10, 0x01, 0xf5, 0x01, 0xf6, 0x09, 0xa4, 0x01, 0xfe, 0x26, 0x0f, 0x64, 0x12, 0x2f,
+ 0x01, 0x73, 0x02, 0x2b, 0x10, 0xfe, 0x44, 0x00, 0x55, 0x0a, 0xe9, 0x44, 0x0a, 0xfe, 0xb4, 0x10,
+ 0x01, 0xb0, 0x44, 0x0a, 0xfe, 0xaa, 0x10, 0x01, 0xb0, 0xfe, 0x19, 0x82, 0xfe, 0x34, 0x46, 0xac,
+ 0x44, 0x0a, 0x10, 0xfe, 0x43, 0x00, 0xfe, 0x96, 0x10, 0x08, 0x54, 0x0a, 0x37, 0x01, 0xf5, 0x01,
+ 0xf6, 0x64, 0x12, 0x2f, 0x01, 0x73, 0x99, 0x0a, 0x64, 0x42, 0x92, 0x02, 0xfe, 0x2e, 0x03, 0x08,
+ 0x05, 0x0a, 0x8a, 0x44, 0x0a, 0x10, 0x00, 0xfe, 0x5c, 0x10, 0x68, 0x05, 0x1a, 0xfe, 0x58, 0x12,
+ 0x08, 0x05, 0x1a, 0xfe, 0x50, 0x13, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x50, 0x0d, 0xfe,
+ 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x56, 0x0d, 0x08, 0x54, 0x1a, 0x37, 0xfe, 0xa9, 0x10, 0x10,
+ 0xfe, 0x15, 0x00, 0xfe, 0x04, 0xe6, 0x0a, 0x50, 0xfe, 0x2e, 0x10, 0x10, 0xfe, 0x13, 0x00, 0xfe,
+ 0x10, 0x10, 0x10, 0x6f, 0xab, 0x10, 0xfe, 0x41, 0x00, 0xaa, 0x10, 0xfe, 0x24, 0x00, 0x8c, 0xb5,
+ 0xb6, 0x74, 0x03, 0x70, 0x28, 0x23, 0xd8, 0x50, 0xfe, 0x04, 0xe6, 0x1a, 0xfe, 0x9d, 0x41, 0xfe,
+ 0x1c, 0x42, 0x64, 0x01, 0xe3, 0x02, 0x2b, 0xf8, 0x15, 0x0a, 0x39, 0xa0, 0xb4, 0x15, 0xfe, 0x31,
+ 0x00, 0x39, 0xa2, 0x01, 0xfe, 0x48, 0x10, 0x02, 0xd7, 0x42, 0xfe, 0x06, 0xec, 0xd0, 0xfc, 0x44,
+ 0x1b, 0xfe, 0xce, 0x45, 0x35, 0x42, 0xfe, 0x06, 0xea, 0xd0, 0xfe, 0x47, 0x4b, 0x91, 0xfe, 0x75,
+ 0x57, 0x03, 0x5d, 0xfe, 0x98, 0x56, 0xfe, 0x38, 0x12, 0x09, 0x48, 0x01, 0x0e, 0xfe, 0x44, 0x48,
+ 0x4f, 0x08, 0x05, 0x1b, 0xfe, 0x1a, 0x13, 0x09, 0x46, 0x01, 0x0e, 0x41, 0xfe, 0x41, 0x58, 0x09,
+ 0xa4, 0x01, 0x0e, 0xfe, 0x49, 0x54, 0x96, 0xfe, 0x1e, 0x0e, 0x02, 0xfe, 0x2e, 0x03, 0x09, 0x5d,
+ 0xfe, 0xee, 0x14, 0xfc, 0x44, 0x1b, 0xfe, 0xce, 0x45, 0x35, 0x42, 0xfe, 0xce, 0x47, 0xfe, 0xad,
+ 0x13, 0x02, 0x2b, 0x22, 0x20, 0x07, 0x11, 0xfe, 0x9e, 0x12, 0x21, 0x13, 0x59, 0x13, 0x9f, 0x13,
+ 0xd5, 0x22, 0x2f, 0x41, 0x39, 0x2f, 0xbc, 0xad, 0xfe, 0xbc, 0xf0, 0xfe, 0xe0, 0x0e, 0x0f, 0x06,
+ 0x13, 0x59, 0x01, 0xfe, 0xda, 0x16, 0x03, 0xfe, 0x38, 0x01, 0x29, 0xfe, 0x3a, 0x01, 0x56, 0xfe,
+ 0xe4, 0x0e, 0xfe, 0x02, 0xec, 0xd5, 0x69, 0x00, 0x66, 0xfe, 0x04, 0xec, 0x20, 0x4f, 0xfe, 0x05,
+ 0xf6, 0xfe, 0x34, 0x01, 0x01, 0xfe, 0x4a, 0x17, 0xfe, 0x08, 0x90, 0xfe, 0x48, 0xf4, 0x0d, 0xfe,
+ 0x18, 0x13, 0xba, 0xfe, 0x02, 0xea, 0xd5, 0x69, 0x7e, 0xfe, 0xc5, 0x13, 0x15, 0x1a, 0x39, 0xa0,
+ 0xb4, 0xfe, 0x2e, 0x10, 0x03, 0xfe, 0x38, 0x01, 0x1e, 0xfe, 0xf0, 0xff, 0x0c, 0xfe, 0x60, 0x01,
+ 0x03, 0xfe, 0x3a, 0x01, 0x0c, 0xfe, 0x62, 0x01, 0x43, 0x13, 0x20, 0x25, 0x06, 0x13, 0x2f, 0x12,
+ 0x2f, 0x92, 0x0f, 0x06, 0x04, 0x21, 0x04, 0x22, 0x59, 0xfe, 0xf7, 0x12, 0x22, 0x9f, 0xb7, 0x13,
+ 0x9f, 0x07, 0x7e, 0xfe, 0x71, 0x13, 0xfe, 0x24, 0x1c, 0x15, 0x19, 0x39, 0xa0, 0xb4, 0xfe, 0xd9,
+ 0x10, 0xc3, 0xfe, 0x03, 0xdc, 0xfe, 0x73, 0x57, 0xfe, 0x80, 0x5d, 0x04, 0xc3, 0xfe, 0x03, 0xdc,
+ 0xfe, 0x5b, 0x57, 0xfe, 0x80, 0x5d, 0x04, 0xfe, 0x03, 0x57, 0xc3, 0x21, 0xfe, 0x00, 0xcc, 0x04,
+ 0xfe, 0x03, 0x57, 0xc3, 0x78, 0x04, 0x08, 0x05, 0x58, 0xfe, 0x22, 0x13, 0xfe, 0x1c, 0x80, 0x07,
+ 0x06, 0xfe, 0x1a, 0x13, 0xfe, 0x1e, 0x80, 0xed, 0xfe, 0x1d, 0x80, 0xae, 0xfe, 0x0c, 0x90, 0xfe,
+ 0x0e, 0x13, 0xfe, 0x0e, 0x90, 0xac, 0xfe, 0x3c, 0x90, 0xfe, 0x30, 0xf4, 0x0a, 0xfe, 0x3c, 0x50,
+ 0xaa, 0x01, 0xfe, 0x7a, 0x17, 0x32, 0x07, 0x2f, 0xad, 0x01, 0xfe, 0xb4, 0x16, 0x08, 0x05, 0x1b,
+ 0x4e, 0x01, 0xf5, 0x01, 0xf6, 0x12, 0xfe, 0xe9, 0x00, 0x08, 0x05, 0x58, 0xfe, 0x2c, 0x13, 0x01,
+ 0xfe, 0x0c, 0x17, 0xfe, 0x1e, 0x1c, 0xfe, 0x14, 0x90, 0xfe, 0x96, 0x90, 0x0c, 0xfe, 0x64, 0x01,
+ 0x14, 0xfe, 0x66, 0x01, 0x08, 0x05, 0x5b, 0xfe, 0x12, 0x12, 0xfe, 0x03, 0x80, 0x8d, 0xfe, 0x01,
+ 0xec, 0x20, 0xfe, 0x80, 0x40, 0x13, 0x20, 0x6a, 0x2a, 0x12, 0xcf, 0x64, 0x22, 0x20, 0xfb, 0x79,
+ 0x20, 0x04, 0xfe, 0x08, 0x1c, 0x03, 0xfe, 0xac, 0x00, 0xfe, 0x06, 0x58, 0x03, 0xfe, 0xae, 0x00,
+
+ 0xfe, 0x07, 0x58, 0x03, 0xfe, 0xb0, 0x00, 0xfe, 0x08, 0x58, 0x03, 0xfe, 0xb2, 0x00, 0xfe, 0x09,
+ 0x58, 0xfe, 0x0a, 0x1c, 0x25, 0x6e, 0x13, 0xd0, 0x21, 0x0c, 0x5c, 0x0c, 0x45, 0x0f, 0x46, 0x52,
+ 0x50, 0x18, 0x1b, 0xfe, 0x90, 0x4d, 0xfe, 0x91, 0x54, 0x23, 0xfe, 0xfc, 0x0f, 0x44, 0x11, 0x0f,
+ 0x48, 0x52, 0x18, 0x58, 0xfe, 0x90, 0x4d, 0xfe, 0x91, 0x54, 0x23, 0xe4, 0x25, 0x11, 0x13, 0x20,
+ 0x7c, 0x6f, 0x4f, 0x22, 0x20, 0xfb, 0x79, 0x20, 0x12, 0xcf, 0xfe, 0x14, 0x56, 0xfe, 0xd6, 0xf0,
+ 0xfe, 0x26, 0x10, 0xf8, 0x74, 0xfe, 0x14, 0x1c, 0xfe, 0x10, 0x1c, 0xfe, 0x18, 0x1c, 0x04, 0x42,
+ 0xfe, 0x0c, 0x14, 0xfc, 0xfe, 0x07, 0xe6, 0x1b, 0xfe, 0xce, 0x47, 0xfe, 0xf5, 0x13, 0x04, 0x01,
+ 0xb0, 0x7c, 0x6f, 0x4f, 0xfe, 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x42, 0x13, 0x32, 0x07, 0x2f,
+ 0xfe, 0x34, 0x13, 0x09, 0x48, 0x01, 0x0e, 0xbb, 0xfe, 0x36, 0x12, 0xfe, 0x41, 0x48, 0xfe, 0x45,
+ 0x48, 0x01, 0xf0, 0xfe, 0x00, 0xcc, 0xbb, 0xfe, 0xf3, 0x13, 0x43, 0x78, 0x07, 0x11, 0xac, 0x09,
+ 0x84, 0x01, 0x0e, 0xfe, 0x80, 0x5c, 0x01, 0x73, 0xfe, 0x0e, 0x10, 0x07, 0x82, 0x4e, 0xfe, 0x14,
+ 0x56, 0xfe, 0xd6, 0xf0, 0xfe, 0x60, 0x10, 0x04, 0xfe, 0x44, 0x58, 0x8d, 0xfe, 0x01, 0xec, 0xa2,
+ 0xfe, 0x9e, 0x40, 0xfe, 0x9d, 0xe7, 0x00, 0xfe, 0x9c, 0xe7, 0x1a, 0x79, 0x2a, 0x01, 0xe3, 0xfe,
+ 0xdd, 0x10, 0x2c, 0xc7, 0x81, 0xc8, 0x83, 0x33, 0x31, 0xde, 0x07, 0x1a, 0xfe, 0x48, 0x12, 0x07,
+ 0x0a, 0xfe, 0x56, 0x12, 0x07, 0x19, 0xfe, 0x30, 0x12, 0x07, 0xc9, 0x17, 0xfe, 0x32, 0x12, 0x07,
+ 0xfe, 0x23, 0x00, 0x17, 0xeb, 0x07, 0x06, 0x17, 0xfe, 0x9c, 0x12, 0x07, 0x1f, 0xfe, 0x12, 0x12,
+ 0x07, 0x00, 0x17, 0x24, 0x15, 0xc9, 0x01, 0x36, 0xa9, 0x2d, 0x01, 0x0b, 0x94, 0x4b, 0x04, 0x2d,
+ 0xdd, 0x09, 0xd1, 0x01, 0xfe, 0x26, 0x0f, 0x12, 0x82, 0x02, 0x2b, 0x2d, 0x32, 0x07, 0xa6, 0xfe,
+ 0xd9, 0x13, 0x3a, 0x3d, 0x3b, 0x3e, 0x56, 0xfe, 0xf0, 0x11, 0x08, 0x05, 0x5a, 0xfe, 0x72, 0x12,
+ 0x9b, 0x2e, 0x9c, 0x3c, 0x90, 0xc0, 0x96, 0xfe, 0xba, 0x11, 0x22, 0x62, 0xfe, 0x26, 0x13, 0x03,
+ 0x7f, 0x29, 0x80, 0x56, 0xfe, 0x76, 0x0d, 0x0c, 0x60, 0x14, 0x61, 0x21, 0x0c, 0x7f, 0x0c, 0x80,
+ 0x01, 0xb3, 0x25, 0x6e, 0x77, 0x13, 0x62, 0x01, 0xef, 0x9b, 0x2e, 0x9c, 0x3c, 0xfe, 0x04, 0x55,
+ 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa, 0x2e, 0xfe, 0x05, 0xfa, 0x3c, 0xfe, 0x91, 0x10, 0x03, 0x3f,
+ 0x29, 0x40, 0xfe, 0x40, 0x56, 0xfe, 0xe1, 0x56, 0x0c, 0x3f, 0x14, 0x40, 0x88, 0x9b, 0x2e, 0x9c,
+ 0x3c, 0x90, 0xc0, 0x03, 0x5e, 0x29, 0x5f, 0xfe, 0x00, 0x56, 0xfe, 0xa1, 0x56, 0x0c, 0x5e, 0x14,
+ 0x5f, 0x08, 0x05, 0x5a, 0xfe, 0x1e, 0x12, 0x22, 0x62, 0xfe, 0x1f, 0x40, 0x03, 0x60, 0x29, 0x61,
+ 0xfe, 0x2c, 0x50, 0xfe, 0xae, 0x50, 0x03, 0x3f, 0x29, 0x40, 0xfe, 0x44, 0x50, 0xfe, 0xc6, 0x50,
+ 0x03, 0x5e, 0x29, 0x5f, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0x03, 0x3d, 0x29, 0x3e, 0xfe, 0x40,
+ 0x50, 0xfe, 0xc2, 0x50, 0x02, 0x89, 0x25, 0x06, 0x13, 0xd4, 0x02, 0x72, 0x2d, 0x01, 0x0b, 0x1d,
+ 0x4c, 0x33, 0x31, 0xde, 0x07, 0x06, 0x23, 0x4c, 0x32, 0x07, 0xa6, 0x23, 0x72, 0x01, 0xaf, 0x1e,
+ 0x43, 0x17, 0x4c, 0x08, 0x05, 0x0a, 0xee, 0x3a, 0x3d, 0x3b, 0x3e, 0xfe, 0x0a, 0x55, 0x35, 0xfe,
+ 0x8b, 0x55, 0x57, 0x3d, 0x7d, 0x3e, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0x02, 0x72, 0xfe, 0x19,
+ 0x81, 0xba, 0xfe, 0x19, 0x41, 0x02, 0x72, 0x2d, 0x01, 0x0b, 0x1c, 0x34, 0x1d, 0xe8, 0x33, 0x31,
+ 0xe1, 0x55, 0x19, 0xfe, 0xa6, 0x12, 0x55, 0x0a, 0x4d, 0x02, 0x4c, 0x01, 0x0b, 0x1c, 0x34, 0x1d,
+ 0xe8, 0x33, 0x31, 0xdf, 0x07, 0x19, 0x23, 0x4c, 0x01, 0x0b, 0x1d, 0xe8, 0x33, 0x31, 0xfe, 0xe8,
+ 0x09, 0xfe, 0xc2, 0x49, 0x51, 0x03, 0xfe, 0x9c, 0x00, 0x28, 0x8a, 0x53, 0x05, 0x1f, 0x35, 0xa9,
+ 0xfe, 0xbb, 0x45, 0x55, 0x00, 0x4e, 0x44, 0x06, 0x7c, 0x43, 0xfe, 0xda, 0x14, 0x01, 0xaf, 0x8c,
+ 0xfe, 0x4b, 0x45, 0xee, 0x32, 0x07, 0xa5, 0xed, 0x03, 0xcd, 0x28, 0x8a, 0x03, 0x45, 0x28, 0x35,
+ 0x67, 0x02, 0x72, 0xfe, 0xc0, 0x5d, 0xfe, 0xf8, 0x14, 0xfe, 0x03, 0x17, 0x03, 0x5c, 0xc1, 0x0c,
+ 0x5c, 0x67, 0x2d, 0x01, 0x0b, 0x26, 0x89, 0x01, 0xfe, 0x9e, 0x15, 0x02, 0x89, 0x01, 0x0b, 0x1c,
+ 0x34, 0x1d, 0x4c, 0x33, 0x31, 0xdf, 0x07, 0x06, 0x23, 0x4c, 0x01, 0xf1, 0xfe, 0x42, 0x58, 0xf1,
+ 0xfe, 0xa4, 0x14, 0x8c, 0xfe, 0x4a, 0xf4, 0x0a, 0x17, 0x4c, 0xfe, 0x4a, 0xf4, 0x06, 0xea, 0x32,
+ 0x07, 0xa5, 0x8b, 0x02, 0x72, 0x03, 0x45, 0xc1, 0x0c, 0x45, 0x67, 0x2d, 0x01, 0x0b, 0x26, 0x89,
+ 0x01, 0xfe, 0xcc, 0x15, 0x02, 0x89, 0x0f, 0x06, 0x27, 0xfe, 0xbe, 0x13, 0x26, 0xfe, 0xd4, 0x13,
+ 0x76, 0xfe, 0x89, 0x48, 0x01, 0x0b, 0x21, 0x76, 0x04, 0x7b, 0xfe, 0xd0, 0x13, 0x1c, 0xfe, 0xd0,
+ 0x13, 0x1d, 0xfe, 0xbe, 0x13, 0x67, 0x2d, 0x01, 0x0b, 0xfe, 0xd5, 0x10, 0x0f, 0x71, 0xff, 0x02,
+ 0x00, 0x57, 0x52, 0x93, 0x1e, 0xfe, 0xff, 0x7f, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x04, 0x0f,
+ 0x71, 0xff, 0x02, 0x00, 0x57, 0x52, 0x93, 0x1e, 0x43, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x04,
+ 0x0f, 0x71, 0xff, 0x02, 0x00, 0x57, 0x52, 0x93, 0x04, 0x0f, 0x71, 0xff, 0x02, 0x00, 0x57, 0x52,
+ 0x93, 0xfe, 0x0b, 0x58, 0x04, 0x09, 0x5c, 0x01, 0x87, 0x09, 0x45, 0x01, 0x87, 0x04, 0xfe, 0x03,
+ 0xa1, 0x1e, 0x11, 0xff, 0x03, 0x00, 0x54, 0xfe, 0x00, 0xf4, 0x1f, 0x52, 0xfe, 0x00, 0x7d, 0xfe,
+ 0x01, 0x7d, 0xfe, 0x02, 0x7d, 0xfe, 0x03, 0x7c, 0x6a, 0x2a, 0x0c, 0x5e, 0x14, 0x5f, 0x57, 0x3f,
+ 0x7d, 0x40, 0x04, 0xdd, 0xfe, 0x82, 0x4a, 0xfe, 0xe1, 0x1a, 0xfe, 0x83, 0x5a, 0x8d, 0x04, 0x01,
+ 0xfe, 0x0c, 0x19, 0xfe, 0x42, 0x48, 0x50, 0x51, 0x91, 0x01, 0x0b, 0x1d, 0xfe, 0x96, 0x15, 0x33,
+ 0x31, 0xe1, 0x01, 0x0b, 0x1d, 0xfe, 0x96, 0x15, 0x33, 0x31, 0xfe, 0xe8, 0x0a, 0xfe, 0xc1, 0x59,
+ 0x03, 0xcd, 0x28, 0xfe, 0xcc, 0x12, 0x53, 0x05, 0x1a, 0xfe, 0xc4, 0x13, 0x21, 0x69, 0x1a, 0xee,
+ 0x55, 0xca, 0x6b, 0xfe, 0xdc, 0x14, 0x4d, 0x0f, 0x06, 0x18, 0xca, 0x7c, 0x30, 0xfe, 0x78, 0x10,
+ 0xff, 0x02, 0x83, 0x55, 0xab, 0xff, 0x02, 0x83, 0x55, 0x69, 0x19, 0xae, 0x98, 0xfe, 0x30, 0x00,
+ 0x96, 0xf2, 0x18, 0x6d, 0x0f, 0x06, 0xfe, 0x56, 0x10, 0x69, 0x0a, 0xed, 0x98, 0xfe, 0x64, 0x00,
+ 0x96, 0xf2, 0x09, 0xfe, 0x64, 0x00, 0x18, 0x9e, 0x0f, 0x06, 0xfe, 0x28, 0x10, 0x69, 0x06, 0xfe,
+ 0x60, 0x13, 0x98, 0xfe, 0xc8, 0x00, 0x96, 0xf2, 0x09, 0xfe, 0xc8, 0x00, 0x18, 0x59, 0x0f, 0x06,
+ 0x88, 0x98, 0xfe, 0x90, 0x01, 0x7a, 0xfe, 0x42, 0x15, 0x91, 0xe4, 0xfe, 0x43, 0xf4, 0x9f, 0xfe,
+ 0x56, 0xf0, 0xfe, 0x54, 0x15, 0xfe, 0x04, 0xf4, 0x71, 0xfe, 0x43, 0xf4, 0x9e, 0xfe, 0xf3, 0x10,
+ 0xfe, 0x40, 0x5c, 0x01, 0xfe, 0x16, 0x14, 0x1e, 0x43, 0xec, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4,
+ 0x6e, 0x7a, 0xfe, 0x90, 0x15, 0xc4, 0x6e, 0xfe, 0x1c, 0x10, 0xfe, 0x00, 0x17, 0xfe, 0x4d, 0xe4,
+ 0xcc, 0x7a, 0xfe, 0x90, 0x15, 0xc4, 0xcc, 0x88, 0x51, 0x21, 0xfe, 0x4d, 0xf4, 0x00, 0xe9, 0x91,
+ 0x0f, 0x06, 0xfe, 0xb4, 0x56, 0xfe, 0xc3, 0x58, 0x04, 0x51, 0x0f, 0x0a, 0x04, 0x16, 0x06, 0x01,
+ 0x0b, 0x26, 0xf3, 0x16, 0x0a, 0x01, 0x0b, 0x26, 0xf3, 0x16, 0x19, 0x01, 0x0b, 0x26, 0xf3, 0x76,
+ 0xfe, 0x89, 0x49, 0x01, 0x0b, 0x04, 0x16, 0x06, 0x01, 0x0b, 0x26, 0xb1, 0x16, 0x19, 0x01, 0x0b,
+ 0x26, 0xb1, 0x16, 0x06, 0x01, 0x0b, 0x26, 0xb1, 0xfe, 0x89, 0x49, 0x01, 0x0b, 0x26, 0xb1, 0x76,
+ 0xfe, 0x89, 0x4a, 0x01, 0x0b, 0x04, 0x51, 0x04, 0x22, 0xd3, 0x07, 0x06, 0xfe, 0x48, 0x13, 0xb8,
+ 0x13, 0xd3, 0xfe, 0x49, 0xf4, 0x00, 0x4d, 0x76, 0xa9, 0x67, 0xfe, 0x01, 0xec, 0xfe, 0x27, 0x01,
+ 0xfe, 0x89, 0x48, 0xff, 0x02, 0x00, 0x10, 0x27, 0xfe, 0x2e, 0x16, 0x32, 0x07, 0xfe, 0xe3, 0x00,
+ 0xfe, 0x20, 0x13, 0x1d, 0xfe, 0x52, 0x16, 0x21, 0x13, 0xd4, 0x01, 0x4b, 0x22, 0xd4, 0x07, 0x06,
+ 0x4e, 0x08, 0x54, 0x06, 0x37, 0x04, 0x09, 0x48, 0x01, 0x0e, 0xfb, 0x8e, 0x07, 0x11, 0xae, 0x09,
+ 0x84, 0x01, 0x0e, 0x8e, 0x09, 0x5d, 0x01, 0xa8, 0x04, 0x09, 0x84, 0x01, 0x0e, 0x8e, 0xfe, 0x80,
+ 0xe7, 0x11, 0x07, 0x11, 0x8a, 0xfe, 0x45, 0x58, 0x01, 0xf0, 0x8e, 0x04, 0x09, 0x48, 0x01, 0x0e,
+ 0x8e, 0x09, 0x5d, 0x01, 0xa8, 0x04, 0x09, 0x48, 0x01, 0x0e, 0xfe, 0x80, 0x80, 0xfe, 0x80, 0x4c,
+ 0xfe, 0x49, 0xe4, 0x11, 0xae, 0x09, 0x84, 0x01, 0x0e, 0xfe, 0x80, 0x4c, 0x09, 0x5d, 0x01, 0x87,
+ 0x04, 0x18, 0x11, 0x75, 0x6c, 0xfe, 0x60, 0x01, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x24,
+ 0x1c, 0xfe, 0x1d, 0xf7, 0x1b, 0x97, 0xfe, 0xee, 0x16, 0x01, 0xfe, 0xf4, 0x17, 0xad, 0x9a, 0x1b,
+ 0x6c, 0xfe, 0x2c, 0x01, 0xfe, 0x2f, 0x19, 0x04, 0xb9, 0x23, 0xfe, 0xde, 0x16, 0xfe, 0xda, 0x10,
+ 0x18, 0x11, 0x75, 0x03, 0xfe, 0x64, 0x01, 0xfe, 0x00, 0xf4, 0x1f, 0xfe, 0x18, 0x58, 0x03, 0xfe,
+ 0x66, 0x01, 0xfe, 0x19, 0x58, 0x9a, 0x1f, 0xfe, 0x3c, 0x90, 0xfe, 0x30, 0xf4, 0x06, 0xfe, 0x3c,
+ 0x50, 0x6c, 0xfe, 0x38, 0x00, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0x1f, 0x97, 0xfe, 0x38, 0x17,
+ 0xfe, 0xb6, 0x14, 0x35, 0x04, 0xb9, 0x23, 0xfe, 0x10, 0x17, 0xfe, 0x9c, 0x10, 0x18, 0x11, 0x75,
+ 0xfe, 0x83, 0x5a, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x1d, 0xf7, 0x2e, 0x97, 0xfe, 0x5a,
+ 0x17, 0xfe, 0x94, 0x14, 0xec, 0x9a, 0x2e, 0x6c, 0x1a, 0xfe, 0xaf, 0x19, 0xfe, 0x98, 0xe7, 0x00,
+ 0x04, 0xb9, 0x23, 0xfe, 0x4e, 0x17, 0xfe, 0x6c, 0x10, 0x18, 0x11, 0x75, 0xfe, 0x30, 0xbc, 0xfe,
+ 0xb2, 0xbc, 0x9a, 0xcb, 0x6c, 0x1a, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0xcb, 0x97, 0xfe, 0x92,
+ 0x17, 0xfe, 0x5c, 0x14, 0x35, 0x04, 0xb9, 0x23, 0xfe, 0x7e, 0x17, 0xfe, 0x42, 0x10, 0xfe, 0x02,
+ 0xf6, 0x11, 0x75, 0xfe, 0x18, 0xfe, 0x60, 0xfe, 0x19, 0xfe, 0x61, 0xfe, 0x03, 0xa1, 0xfe, 0x1d,
+ 0xf7, 0x5b, 0x97, 0xfe, 0xb8, 0x17, 0xfe, 0x36, 0x14, 0xfe, 0x1c, 0x13, 0x9a, 0x5b, 0x41, 0xfe,
+ 0x83, 0x58, 0xfe, 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x11, 0xfe, 0x81, 0xe7, 0x11, 0x12, 0xfe, 0xdd,
+ 0x00, 0x6a, 0x2a, 0x04, 0x6a, 0x2a, 0xfe, 0x12, 0x45, 0x23, 0xfe, 0xa8, 0x17, 0x15, 0x06, 0x39,
+ 0xa0, 0xb4, 0x02, 0x2b, 0xfe, 0x39, 0xf0, 0xfe, 0xfc, 0x17, 0x21, 0x04, 0xfe, 0x7e, 0x18, 0x1e,
+ 0x19, 0x66, 0x0f, 0x0d, 0x04, 0x75, 0x03, 0xd2, 0x1e, 0x06, 0xfe, 0xef, 0x12, 0xfe, 0xe1, 0x10,
+ 0x7c, 0x6f, 0x4f, 0x32, 0x07, 0x2f, 0xfe, 0x3c, 0x13, 0xf1, 0xfe, 0x42, 0x13, 0x42, 0x92, 0x09,
+ 0x48, 0x01, 0x0e, 0xbb, 0xeb, 0xfe, 0x41, 0x48, 0xfe, 0x45, 0x48, 0x01, 0xf0, 0xfe, 0x00, 0xcc,
+ 0xbb, 0xfe, 0xf3, 0x13, 0x43, 0x78, 0x07, 0x11, 0xac, 0x09, 0x84, 0x01, 0x0e, 0xfe, 0x80, 0x4c,
+ 0x01, 0x73, 0xfe, 0x16, 0x10, 0x07, 0x82, 0x8b, 0xfe, 0x40, 0x14, 0xfe, 0x24, 0x12, 0xfe, 0x14,
+ 0x56, 0xfe, 0xd6, 0xf0, 0xfe, 0x1c, 0x18, 0x18, 0x0a, 0x04, 0xfe, 0x9c, 0xe7, 0x0a, 0x10, 0xfe,
+ 0x15, 0x00, 0x64, 0x79, 0x2a, 0x01, 0xe3, 0x18, 0x06, 0x04, 0x42, 0x92, 0x08, 0x54, 0x1b, 0x37,
+ 0x12, 0x2f, 0x01, 0x73, 0x18, 0x06, 0x04, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90, 0x3a, 0xce, 0x3b,
+ 0xcf, 0xfe, 0x48, 0x55, 0x35, 0xfe, 0xc9, 0x55, 0x04, 0x22, 0xa3, 0x77, 0x13, 0xa3, 0x04, 0x09,
+ 0xa4, 0x01, 0x0e, 0xfe, 0x41, 0x48, 0x09, 0x46, 0x01, 0x0e, 0xfe, 0x49, 0x44, 0x17, 0xfe, 0xe8,
+ 0x18, 0x77, 0x78, 0x04, 0x09, 0x48, 0x01, 0x0e, 0x07, 0x11, 0x4e, 0x09, 0x5d, 0x01, 0xa8, 0x09,
+ 0x46, 0x01, 0x0e, 0x77, 0x78, 0x04, 0xfe, 0x4e, 0xe4, 0x19, 0x6b, 0xfe, 0x1c, 0x19, 0x03, 0xfe,
+ 0x90, 0x00, 0xfe, 0x3a, 0x45, 0xfe, 0x2c, 0x10, 0xfe, 0x4e, 0xe4, 0xc9, 0x6b, 0xfe, 0x2e, 0x19,
+ 0x03, 0xfe, 0x92, 0x00, 0xfe, 0x02, 0xe6, 0x1a, 0xe5, 0xfe, 0x4e, 0xe4, 0xfe, 0x0b, 0x00, 0x6b,
+ 0xfe, 0x40, 0x19, 0x03, 0xfe, 0x94, 0x00, 0xfe, 0x02, 0xe6, 0x1f, 0xfe, 0x08, 0x10, 0x03, 0xfe,
+ 0x96, 0x00, 0xfe, 0x02, 0xe6, 0x6d, 0xfe, 0x4e, 0x45, 0xea, 0xba, 0xff, 0x04, 0x68, 0x54, 0xe7,
+ 0x1e, 0x6e, 0xfe, 0x08, 0x1c, 0xfe, 0x67, 0x19, 0xfe, 0x0a, 0x1c, 0xfe, 0x1a, 0xf4, 0xfe, 0x00,
+ 0x04, 0xea, 0xfe, 0x48, 0xf4, 0x19, 0x7a, 0xfe, 0x74, 0x19, 0x0f, 0x19, 0x04, 0x07, 0x7e, 0xfe,
+ 0x5a, 0xf0, 0xfe, 0x84, 0x19, 0x25, 0xfe, 0x09, 0x00, 0xfe, 0x34, 0x10, 0x07, 0x1a, 0xfe, 0x5a,
+ 0xf0, 0xfe, 0x92, 0x19, 0x25, 0xca, 0xfe, 0x26, 0x10, 0x07, 0x19, 0x66, 0x25, 0x6d, 0xe5, 0x07,
+ 0x0a, 0x66, 0x25, 0x9e, 0xfe, 0x0e, 0x10, 0x07, 0x06, 0x66, 0x25, 0x59, 0xa9, 0xb8, 0x04, 0x15,
+ 0xfe, 0x09, 0x00, 0x01, 0x36, 0xfe, 0x04, 0xfe, 0x81, 0x03, 0x83, 0xfe, 0x40, 0x5c, 0x04, 0x1c,
+ 0xf7, 0xfe, 0x14, 0xf0, 0x0b, 0x27, 0xfe, 0xd6, 0x19, 0x1c, 0xf7, 0x7b, 0xf7, 0xfe, 0x82, 0xf0,
+ 0xfe, 0xda, 0x19, 0x04, 0xff, 0xcc, 0x00, 0x00,
+};
+
+STATIC unsigned short _adv_asc38C0800_size =
+ sizeof(_adv_asc38C0800_buf); /* 0x14E1 */
+STATIC ADV_DCNT _adv_asc38C0800_chksum =
+ 0x050D3FD8UL; /* Expanded little-endian checksum. */
+
+/* Microcode buffer is kept after initialization for error recovery. */
+STATIC unsigned char _adv_asc38C1600_buf[] = {
+ 0x00, 0x00, 0x00, 0xf2, 0x00, 0x16, 0x00, 0xfc, 0x00, 0x10, 0x00, 0xf0, 0x18, 0xe4, 0x01, 0x00,
+ 0x04, 0x1e, 0x48, 0xe4, 0x03, 0xf6, 0xf7, 0x13, 0x2e, 0x1e, 0x02, 0x00, 0x07, 0x17, 0xc0, 0x5f,
+ 0x00, 0xfa, 0xff, 0xff, 0x04, 0x00, 0x00, 0xf6, 0x09, 0xe7, 0x82, 0xe7, 0x85, 0xf0, 0x86, 0xf0,
+ 0x4e, 0x10, 0x9e, 0xe7, 0xff, 0x00, 0x55, 0xf0, 0x01, 0xf6, 0x03, 0x00, 0x98, 0x57, 0x01, 0xe6,
+ 0x00, 0xea, 0x00, 0xec, 0x01, 0xfa, 0x18, 0xf4, 0x08, 0x00, 0xf0, 0x1d, 0x38, 0x54, 0x32, 0xf0,
+ 0x10, 0x00, 0xc2, 0x0e, 0x1e, 0xf0, 0xd5, 0xf0, 0xbc, 0x00, 0x4b, 0xe4, 0x00, 0xe6, 0xb1, 0xf0,
+ 0xb4, 0x00, 0x02, 0x13, 0x3e, 0x1c, 0xc8, 0x47, 0x3e, 0x00, 0xd8, 0x01, 0x06, 0x13, 0x0c, 0x1c,
+ 0x5e, 0x1e, 0x00, 0x57, 0xc8, 0x57, 0x01, 0xfc, 0xbc, 0x0e, 0xa2, 0x12, 0xb9, 0x54, 0x00, 0x80,
+ 0x62, 0x0a, 0x5a, 0x12, 0xc8, 0x15, 0x3e, 0x1e, 0x18, 0x40, 0xbd, 0x56, 0x03, 0xe6, 0x01, 0xea,
+ 0x5c, 0xf0, 0x0f, 0x00, 0x20, 0x00, 0x6c, 0x01, 0x6e, 0x01, 0x04, 0x12, 0x04, 0x13, 0xbb, 0x55,
+ 0x3c, 0x56, 0x3e, 0x57, 0x03, 0x58, 0x4a, 0xe4, 0x40, 0x00, 0xb6, 0x00, 0xbb, 0x00, 0xc0, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x3e, 0x01, 0x58, 0x0a, 0x44, 0x10, 0x0a, 0x12, 0x4c, 0x1c, 0x4e, 0x1c,
+ 0x02, 0x4a, 0x30, 0xe4, 0x05, 0xe6, 0x0c, 0x00, 0x3c, 0x00, 0x80, 0x00, 0x24, 0x01, 0x3c, 0x01,
+ 0x68, 0x01, 0x6a, 0x01, 0x70, 0x01, 0x72, 0x01, 0x74, 0x01, 0x76, 0x01, 0x78, 0x01, 0x7c, 0x01,
+ 0xc6, 0x0e, 0x0c, 0x10, 0xac, 0x12, 0xae, 0x12, 0x16, 0x1a, 0x32, 0x1c, 0x6e, 0x1e, 0x02, 0x48,
+ 0x3a, 0x55, 0xc9, 0x57, 0x02, 0xee, 0x5b, 0xf0, 0x03, 0xf7, 0x06, 0xf7, 0x03, 0xfc, 0x06, 0x00,
+ 0x1e, 0x00, 0xbe, 0x00, 0xe1, 0x00, 0x0c, 0x12, 0x18, 0x1a, 0x70, 0x1a, 0x30, 0x1c, 0x38, 0x1c,
+ 0x10, 0x44, 0x00, 0x4c, 0xb0, 0x57, 0x40, 0x5c, 0x4d, 0xe4, 0x04, 0xea, 0x5d, 0xf0, 0xa7, 0xf0,
+ 0x04, 0xf6, 0x02, 0xfc, 0x05, 0x00, 0x09, 0x00, 0x19, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00,
+ 0x36, 0x00, 0x98, 0x00, 0x9e, 0x00, 0xcc, 0x00, 0x20, 0x01, 0x4e, 0x01, 0x79, 0x01, 0x3c, 0x09,
+ 0x68, 0x0d, 0x02, 0x10, 0x04, 0x10, 0x3a, 0x10, 0x08, 0x12, 0x0a, 0x13, 0x40, 0x16, 0x50, 0x16,
+ 0x00, 0x17, 0x4a, 0x19, 0x00, 0x4e, 0x00, 0x54, 0x01, 0x58, 0x00, 0xdc, 0x05, 0xf0, 0x09, 0xf0,
+ 0x59, 0xf0, 0xb8, 0xf0, 0x48, 0xf4, 0x0e, 0xf7, 0x0a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0xa4, 0x00,
+ 0xb5, 0x00, 0xba, 0x00, 0xd0, 0x00, 0xe7, 0x00, 0xf0, 0x03, 0x69, 0x08, 0xe9, 0x09, 0x5c, 0x0c,
+ 0xb6, 0x12, 0xbc, 0x19, 0xd8, 0x1b, 0x20, 0x1c, 0x34, 0x1c, 0x36, 0x1c, 0x42, 0x1d, 0x08, 0x44,
+ 0x38, 0x44, 0x91, 0x44, 0x0a, 0x45, 0x48, 0x46, 0x89, 0x48, 0x68, 0x54, 0x83, 0x55, 0x83, 0x59,
+ 0x31, 0xe4, 0x02, 0xe6, 0x07, 0xf0, 0x08, 0xf0, 0x0b, 0xf0, 0x0c, 0xf0, 0x4b, 0xf4, 0x04, 0xf8,
+ 0x05, 0xf8, 0x02, 0xfa, 0x03, 0xfa, 0x04, 0xfc, 0x05, 0xfc, 0x07, 0x00, 0xa8, 0x00, 0xaa, 0x00,
+ 0xb9, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0x22, 0x01, 0x26, 0x01, 0x60, 0x01, 0x7a, 0x01, 0x82, 0x01,
+ 0xc8, 0x01, 0xca, 0x01, 0x86, 0x02, 0x6a, 0x03, 0x18, 0x05, 0xb2, 0x07, 0x68, 0x08, 0x10, 0x0d,
+ 0x06, 0x10, 0x0a, 0x10, 0x0e, 0x10, 0x12, 0x10, 0x60, 0x10, 0xed, 0x10, 0xf3, 0x10, 0x06, 0x12,
+ 0x10, 0x12, 0x1e, 0x12, 0x0c, 0x13, 0x0e, 0x13, 0x10, 0x13, 0xfe, 0x9c, 0xf0, 0x35, 0x05, 0xfe,
+ 0xec, 0x0e, 0xff, 0x10, 0x00, 0x00, 0xe9, 0xfe, 0x34, 0x1f, 0x00, 0xe8, 0xfe, 0x88, 0x01, 0xff,
+ 0x03, 0x00, 0x00, 0xfe, 0x93, 0x15, 0xfe, 0x0f, 0x05, 0xff, 0x38, 0x00, 0x00, 0xfe, 0x57, 0x24,
+ 0x00, 0xfe, 0x4c, 0x00, 0x65, 0xff, 0x04, 0x00, 0x00, 0x1a, 0xff, 0x09, 0x00, 0x00, 0xff, 0x08,
+ 0x01, 0x01, 0xff, 0x08, 0xff, 0xff, 0xff, 0x27, 0x00, 0x00, 0xff, 0x10, 0xff, 0xff, 0xff, 0x13,
+ 0x00, 0x00, 0xfe, 0x78, 0x56, 0xfe, 0x34, 0x12, 0xff, 0x21, 0x00, 0x00, 0xfe, 0x04, 0xf7, 0xe8,
+ 0x37, 0x7d, 0x0d, 0x01, 0xfe, 0x4a, 0x11, 0xfe, 0x04, 0xf7, 0xe8, 0x7d, 0x0d, 0x51, 0x37, 0xfe,
+ 0x3d, 0xf0, 0xfe, 0x0c, 0x02, 0xfe, 0x20, 0xf0, 0xbc, 0xfe, 0x91, 0xf0, 0xfe, 0xf8, 0x01, 0xfe,
+ 0x90, 0xf0, 0xfe, 0xf8, 0x01, 0xfe, 0x8f, 0xf0, 0xbc, 0x03, 0x67, 0x4d, 0x05, 0xfe, 0x08, 0x0f,
+ 0x01, 0xfe, 0x78, 0x0f, 0xfe, 0xdd, 0x12, 0x05, 0xfe, 0x0e, 0x03, 0xfe, 0x28, 0x1c, 0x03, 0xfe,
+ 0xa6, 0x00, 0xfe, 0xd1, 0x12, 0x3e, 0x22, 0xfe, 0xa6, 0x00, 0xac, 0xfe, 0x48, 0xf0, 0xfe, 0x90,
+ 0x02, 0xfe, 0x49, 0xf0, 0xfe, 0xaa, 0x02, 0xfe, 0x4a, 0xf0, 0xfe, 0xc8, 0x02, 0xfe, 0x46, 0xf0,
+ 0xfe, 0x5a, 0x02, 0xfe, 0x47, 0xf0, 0xfe, 0x60, 0x02, 0xfe, 0x43, 0xf0, 0xfe, 0x4e, 0x02, 0xfe,
+ 0x44, 0xf0, 0xfe, 0x52, 0x02, 0xfe, 0x45, 0xf0, 0xfe, 0x56, 0x02, 0x1c, 0x0d, 0xa2, 0x1c, 0x07,
+ 0x22, 0xb7, 0x05, 0x35, 0xfe, 0x00, 0x1c, 0xfe, 0xf1, 0x10, 0xfe, 0x02, 0x1c, 0xf5, 0xfe, 0x1e,
+ 0x1c, 0xfe, 0xe9, 0x10, 0x01, 0x5f, 0xfe, 0xe7, 0x10, 0xfe, 0x06, 0xfc, 0xde, 0x0a, 0x81, 0x01,
+ 0xa3, 0x05, 0x35, 0x1f, 0x95, 0x47, 0xb8, 0x01, 0xfe, 0xe4, 0x11, 0x0a, 0x81, 0x01, 0x5c, 0xfe,
+ 0xbd, 0x10, 0x0a, 0x81, 0x01, 0x5c, 0xfe, 0xad, 0x10, 0xfe, 0x16, 0x1c, 0xfe, 0x58, 0x1c, 0x1c,
+ 0x07, 0x22, 0xb7, 0x37, 0x2a, 0x35, 0xfe, 0x3d, 0xf0, 0xfe, 0x0c, 0x02, 0x2b, 0xfe, 0x9e, 0x02,
+ 0xfe, 0x5a, 0x1c, 0xfe, 0x12, 0x1c, 0xfe, 0x14, 0x1c, 0x1f, 0xfe, 0x30, 0x00, 0x47, 0xb8, 0x01,
+ 0xfe, 0xd4, 0x11, 0x1c, 0x07, 0x22, 0xb7, 0x05, 0xe9, 0x21, 0x2c, 0x09, 0x1a, 0x31, 0xfe, 0x69,
+ 0x10, 0x1c, 0x07, 0x22, 0xb7, 0xfe, 0x04, 0xec, 0x2c, 0x60, 0x01, 0xfe, 0x1e, 0x1e, 0x20, 0x2c,
+ 0xfe, 0x05, 0xf6, 0xde, 0x01, 0xfe, 0x62, 0x1b, 0x01, 0x0c, 0x61, 0x4a, 0x44, 0x15, 0x56, 0x51,
+ 0x01, 0xfe, 0x9e, 0x1e, 0x01, 0xfe, 0x96, 0x1a, 0x05, 0x35, 0x0a, 0x57, 0x01, 0x18, 0x09, 0x00,
+ 0x36, 0x01, 0x85, 0xfe, 0x18, 0x10, 0xfe, 0x41, 0x58, 0x0a, 0xba, 0x01, 0x18, 0xfe, 0xc8, 0x54,
+ 0x7b, 0xfe, 0x1c, 0x03, 0x01, 0xfe, 0x96, 0x1a, 0x05, 0x35, 0x37, 0x60, 0xfe, 0x02, 0xe8, 0x30,
+ 0xfe, 0xbf, 0x57, 0xfe, 0x9e, 0x43, 0xfe, 0x77, 0x57, 0xfe, 0x27, 0xf0, 0xfe, 0xe4, 0x01, 0xfe,
+ 0x07, 0x4b, 0xfe, 0x20, 0xf0, 0xbc, 0xfe, 0x40, 0x1c, 0x2a, 0xeb, 0xfe, 0x26, 0xf0, 0xfe, 0x66,
+ 0x03, 0xfe, 0xa0, 0xf0, 0xfe, 0x54, 0x03, 0xfe, 0x11, 0xf0, 0xbc, 0xfe, 0xef, 0x10, 0xfe, 0x9f,
+ 0xf0, 0xfe, 0x74, 0x03, 0xfe, 0x46, 0x1c, 0x19, 0xfe, 0x11, 0x00, 0x05, 0x70, 0x37, 0xfe, 0x48,
+ 0x1c, 0xfe, 0x46, 0x1c, 0x01, 0x0c, 0x06, 0x28, 0xfe, 0x18, 0x13, 0x26, 0x21, 0xb9, 0xc7, 0x20,
+ 0xb9, 0x0a, 0x57, 0x01, 0x18, 0xc7, 0x89, 0x01, 0xfe, 0xc8, 0x1a, 0x15, 0xe1, 0x2a, 0xeb, 0xfe,
+ 0x01, 0xf0, 0xeb, 0xfe, 0x82, 0xf0, 0xfe, 0xa4, 0x03, 0xfe, 0x9c, 0x32, 0x15, 0xfe, 0xe4, 0x00,
+ 0x2f, 0xfe, 0xb6, 0x03, 0x2a, 0x3c, 0x16, 0xfe, 0xc6, 0x03, 0x01, 0x41, 0xfe, 0x06, 0xf0, 0xfe,
+ 0xd6, 0x03, 0xaf, 0xa0, 0xfe, 0x0a, 0xf0, 0xfe, 0xa2, 0x07, 0x05, 0x29, 0x03, 0x81, 0x1e, 0x1b,
+ 0xfe, 0x24, 0x05, 0x1f, 0x63, 0x01, 0x42, 0x8f, 0xfe, 0x70, 0x02, 0x05, 0xea, 0xfe, 0x46, 0x1c,
+ 0x37, 0x7d, 0x1d, 0xfe, 0x67, 0x1b, 0xfe, 0xbf, 0x57, 0xfe, 0x77, 0x57, 0xfe, 0x48, 0x1c, 0x75,
+ 0x01, 0xa6, 0x86, 0x0a, 0x57, 0x01, 0x18, 0x09, 0x00, 0x1b, 0xec, 0x0a, 0xe1, 0x01, 0x18, 0x77,
+ 0x50, 0x40, 0x8d, 0x30, 0x03, 0x81, 0x1e, 0xf8, 0x1f, 0x63, 0x01, 0x42, 0x8f, 0xfe, 0x70, 0x02,
+ 0x05, 0xea, 0xd7, 0x99, 0xd8, 0x9c, 0x2a, 0x29, 0x2f, 0xfe, 0x4e, 0x04, 0x16, 0xfe, 0x4a, 0x04,
+ 0x7e, 0xfe, 0xa0, 0x00, 0xfe, 0x9b, 0x57, 0xfe, 0x54, 0x12, 0x32, 0xff, 0x02, 0x00, 0x10, 0x01,
+ 0x08, 0x16, 0xfe, 0x02, 0x05, 0x32, 0x01, 0x08, 0x16, 0x29, 0x27, 0x25, 0xee, 0xfe, 0x4c, 0x44,
+ 0xfe, 0x58, 0x12, 0x50, 0xfe, 0x44, 0x48, 0x13, 0x34, 0xfe, 0x4c, 0x54, 0x7b, 0xec, 0x60, 0x8d,
+ 0x30, 0x01, 0xfe, 0x4e, 0x1e, 0xfe, 0x48, 0x47, 0xfe, 0x7c, 0x13, 0x01, 0x0c, 0x06, 0x28, 0xfe,
+ 0x32, 0x13, 0x01, 0x43, 0x09, 0x9b, 0xfe, 0x68, 0x13, 0xfe, 0x26, 0x10, 0x13, 0x34, 0xfe, 0x4c,
+ 0x54, 0x7b, 0xec, 0x01, 0xfe, 0x4e, 0x1e, 0xfe, 0x48, 0x47, 0xfe, 0x54, 0x13, 0x01, 0x0c, 0x06,
+ 0x28, 0xa5, 0x01, 0x43, 0x09, 0x9b, 0xfe, 0x40, 0x13, 0x01, 0x0c, 0x06, 0x28, 0xf9, 0x1f, 0x7f,
+ 0x01, 0x0c, 0x06, 0x07, 0x4d, 0x1f, 0xfe, 0x0d, 0x00, 0x01, 0x42, 0x8f, 0xfe, 0xa4, 0x0e, 0x05,
+ 0x29, 0x32, 0x15, 0xfe, 0xe6, 0x00, 0x0f, 0xfe, 0x1c, 0x90, 0x04, 0xfe, 0x9c, 0x93, 0x3a, 0x0b,
+ 0x0e, 0x8b, 0x02, 0x1f, 0x7f, 0x01, 0x42, 0x05, 0x35, 0xfe, 0x42, 0x5b, 0x7d, 0x1d, 0xfe, 0x46,
+ 0x59, 0xfe, 0xbf, 0x57, 0xfe, 0x77, 0x57, 0x0f, 0xfe, 0x87, 0x80, 0x04, 0xfe, 0x87, 0x83, 0xfe,
+ 0xc9, 0x47, 0x0b, 0x0e, 0xd0, 0x65, 0x01, 0x0c, 0x06, 0x0d, 0xfe, 0x98, 0x13, 0x0f, 0xfe, 0x20,
+ 0x80, 0x04, 0xfe, 0xa0, 0x83, 0x33, 0x0b, 0x0e, 0x09, 0x1d, 0xfe, 0x84, 0x12, 0x01, 0x38, 0x06,
+ 0x07, 0xfe, 0x70, 0x13, 0x03, 0xfe, 0xa2, 0x00, 0x1e, 0x1b, 0xfe, 0xda, 0x05, 0xd0, 0x54, 0x01,
+ 0x38, 0x06, 0x0d, 0xfe, 0x58, 0x13, 0x03, 0xfe, 0xa0, 0x00, 0x1e, 0xfe, 0x50, 0x12, 0x5e, 0xff,
+ 0x02, 0x00, 0x10, 0x2f, 0xfe, 0x90, 0x05, 0x2a, 0x3c, 0xcc, 0xff, 0x02, 0x00, 0x10, 0x2f, 0xfe,
+ 0x9e, 0x05, 0x17, 0xfe, 0xf4, 0x05, 0x15, 0xfe, 0xe3, 0x00, 0x26, 0x01, 0x38, 0xfe, 0x4a, 0xf0,
+ 0xfe, 0xc0, 0x05, 0xfe, 0x49, 0xf0, 0xfe, 0xba, 0x05, 0x71, 0x2e, 0xfe, 0x21, 0x00, 0xf1, 0x2e,
+ 0xfe, 0x22, 0x00, 0xa2, 0x2e, 0x4a, 0xfe, 0x09, 0x48, 0xff, 0x02, 0x00, 0x10, 0x2f, 0xfe, 0xd0,
+ 0x05, 0x17, 0xfe, 0xf4, 0x05, 0xfe, 0xe2, 0x08, 0x01, 0x38, 0x06, 0xfe, 0x1c, 0x00, 0x4d, 0x01,
+ 0xa7, 0x2e, 0x07, 0x20, 0xe4, 0x47, 0xfe, 0x27, 0x01, 0x01, 0x0c, 0x06, 0x28, 0xfe, 0x24, 0x12,
+ 0x3e, 0x01, 0x84, 0x1f, 0x7f, 0x01, 0x0c, 0x06, 0x07, 0x4d, 0x1f, 0xfe, 0x0d, 0x00, 0x01, 0x42,
+ 0x8f, 0xfe, 0xa4, 0x0e, 0x05, 0x29, 0x03, 0xe6, 0x1e, 0xfe, 0xca, 0x13, 0x03, 0xb6, 0x1e, 0xfe,
+ 0x40, 0x12, 0x03, 0x66, 0x1e, 0xfe, 0x38, 0x13, 0x3e, 0x01, 0x84, 0x17, 0xfe, 0x72, 0x06, 0x0a,
+ 0x07, 0x01, 0x38, 0x06, 0x24, 0xfe, 0x02, 0x12, 0x4f, 0x01, 0xfe, 0x56, 0x19, 0x16, 0xfe, 0x68,
+ 0x06, 0x15, 0x82, 0x01, 0x41, 0x15, 0xe2, 0x03, 0x66, 0x8a, 0x10, 0x66, 0x03, 0x9a, 0x1e, 0xfe,
+ 0x70, 0x12, 0x03, 0x55, 0x1e, 0xfe, 0x68, 0x13, 0x01, 0xc6, 0x09, 0x12, 0x48, 0xfe, 0x92, 0x06,
+ 0x2e, 0x12, 0x01, 0xfe, 0xac, 0x1d, 0xfe, 0x43, 0x48, 0x62, 0x80, 0x13, 0x58, 0xff, 0x02, 0x00,
+ 0x57, 0x52, 0xad, 0x23, 0x3f, 0x4e, 0x62, 0x49, 0x3e, 0x01, 0x84, 0x17, 0xfe, 0xea, 0x06, 0x01,
+ 0x38, 0x06, 0x12, 0xf7, 0x45, 0x0a, 0x95, 0x01, 0xfe, 0x84, 0x19, 0x16, 0xfe, 0xe0, 0x06, 0x15,
+ 0x82, 0x01, 0x41, 0x15, 0xe2, 0x03, 0x55, 0x8a, 0x10, 0x55, 0x1c, 0x07, 0x01, 0x84, 0xfe, 0xae,
+ 0x10, 0x03, 0x6f, 0x1e, 0xfe, 0x9e, 0x13, 0x3e, 0x01, 0x84, 0x03, 0x9a, 0x1e, 0xfe, 0x1a, 0x12,
+ 0x01, 0x38, 0x06, 0x12, 0xfc, 0x01, 0xc6, 0x01, 0xfe, 0xac, 0x1d, 0xfe, 0x43, 0x48, 0x62, 0x80,
+ 0xf0, 0x45, 0x0a, 0x95, 0x03, 0xb6, 0x1e, 0xf8, 0x01, 0x38, 0x06, 0x24, 0x36, 0xfe, 0x02, 0xf6,
+ 0x07, 0x71, 0x78, 0x8c, 0x00, 0x4d, 0x62, 0x49, 0x3e, 0x2d, 0x93, 0x4e, 0xd0, 0x0d, 0x17, 0xfe,
+ 0x9a, 0x07, 0x01, 0xfe, 0xc0, 0x19, 0x16, 0xfe, 0x90, 0x07, 0x26, 0x20, 0x9e, 0x15, 0x82, 0x01,
+ 0x41, 0x15, 0xe2, 0x21, 0x9e, 0x09, 0x07, 0xfb, 0x03, 0xe6, 0xfe, 0x58, 0x57, 0x10, 0xe6, 0x05,
+ 0xfe, 0x2a, 0x06, 0x03, 0x6f, 0x8a, 0x10, 0x6f, 0x1c, 0x07, 0x01, 0x84, 0xfe, 0x9c, 0x32, 0x5f,
+ 0x75, 0x01, 0xa6, 0x86, 0x15, 0xfe, 0xe2, 0x00, 0x2f, 0xed, 0x2a, 0x3c, 0xfe, 0x0a, 0xf0, 0xfe,
+ 0xce, 0x07, 0xae, 0xfe, 0x96, 0x08, 0xfe, 0x06, 0xf0, 0xfe, 0x9e, 0x08, 0xaf, 0xa0, 0x05, 0x29,
+ 0x01, 0x0c, 0x06, 0x0d, 0xfe, 0x2e, 0x12, 0x14, 0x1d, 0x01, 0x08, 0x14, 0x00, 0x01, 0x08, 0x14,
+ 0x00, 0x01, 0x08, 0x14, 0x00, 0x01, 0x08, 0xfe, 0x99, 0xa4, 0x01, 0x08, 0x14, 0x00, 0x05, 0xfe,
+ 0xc6, 0x09, 0x01, 0x76, 0x06, 0x12, 0xfe, 0x3a, 0x12, 0x01, 0x0c, 0x06, 0x12, 0xfe, 0x30, 0x13,
+ 0x14, 0xfe, 0x1b, 0x00, 0x01, 0x08, 0x14, 0x00, 0x01, 0x08, 0x14, 0x00, 0x01, 0x08, 0x14, 0x00,
+ 0x01, 0x08, 0x14, 0x07, 0x01, 0x08, 0x14, 0x00, 0x05, 0xef, 0x7c, 0x4a, 0x78, 0x4f, 0x0f, 0xfe,
+ 0x9a, 0x81, 0x04, 0xfe, 0x9a, 0x83, 0xfe, 0xcb, 0x47, 0x0b, 0x0e, 0x2d, 0x28, 0x48, 0xfe, 0x6c,
+ 0x08, 0x0a, 0x28, 0xfe, 0x09, 0x6f, 0xca, 0xfe, 0xca, 0x45, 0xfe, 0x32, 0x12, 0x53, 0x63, 0x4e,
+ 0x7c, 0x97, 0x2f, 0xfe, 0x7e, 0x08, 0x2a, 0x3c, 0xfe, 0x0a, 0xf0, 0xfe, 0x6c, 0x08, 0xaf, 0xa0,
+ 0xae, 0xfe, 0x96, 0x08, 0x05, 0x29, 0x01, 0x41, 0x05, 0xed, 0x14, 0x24, 0x05, 0xed, 0xfe, 0x9c,
+ 0xf7, 0x9f, 0x01, 0xfe, 0xae, 0x1e, 0xfe, 0x18, 0x58, 0x01, 0xfe, 0xbe, 0x1e, 0xfe, 0x99, 0x58,
+ 0xfe, 0x78, 0x18, 0xfe, 0xf9, 0x18, 0x8e, 0xfe, 0x16, 0x09, 0x10, 0x6a, 0x22, 0x6b, 0x01, 0x0c,
+ 0x61, 0x54, 0x44, 0x21, 0x2c, 0x09, 0x1a, 0xf8, 0x77, 0x01, 0xfe, 0x7e, 0x1e, 0x47, 0x2c, 0x7a,
+ 0x30, 0xf0, 0xfe, 0x83, 0xe7, 0xfe, 0x3f, 0x00, 0x71, 0xfe, 0x03, 0x40, 0x01, 0x0c, 0x61, 0x65,
+ 0x44, 0x01, 0xc2, 0xc8, 0xfe, 0x1f, 0x40, 0x20, 0x6e, 0x01, 0xfe, 0x6a, 0x16, 0xfe, 0x08, 0x50,
+ 0xfe, 0x8a, 0x50, 0xfe, 0x44, 0x51, 0xfe, 0xc6, 0x51, 0xfe, 0x10, 0x10, 0x01, 0xfe, 0xce, 0x1e,
+ 0x01, 0xfe, 0xde, 0x1e, 0x10, 0x68, 0x22, 0x69, 0x01, 0xfe, 0xee, 0x1e, 0x01, 0xfe, 0xfe, 0x1e,
+ 0xfe, 0x40, 0x50, 0xfe, 0xc2, 0x50, 0x10, 0x4b, 0x22, 0x4c, 0xfe, 0x8a, 0x10, 0x01, 0x0c, 0x06,
+ 0x54, 0xfe, 0x50, 0x12, 0x01, 0xfe, 0xae, 0x1e, 0x01, 0xfe, 0xbe, 0x1e, 0x10, 0x6a, 0x22, 0x6b,
+ 0x01, 0x0c, 0x06, 0x65, 0x4e, 0x01, 0xc2, 0x0f, 0xfe, 0x1f, 0x80, 0x04, 0xfe, 0x9f, 0x83, 0x33,
+ 0x0b, 0x0e, 0x20, 0x6e, 0x0f, 0xfe, 0x44, 0x90, 0x04, 0xfe, 0xc4, 0x93, 0x3a, 0x0b, 0xfe, 0xc6,
+ 0x90, 0x04, 0xfe, 0xc6, 0x93, 0x79, 0x0b, 0x0e, 0x10, 0x6c, 0x22, 0x6d, 0x01, 0xfe, 0xce, 0x1e,
+ 0x01, 0xfe, 0xde, 0x1e, 0x10, 0x68, 0x22, 0x69, 0x0f, 0xfe, 0x40, 0x90, 0x04, 0xfe, 0xc0, 0x93,
+ 0x3a, 0x0b, 0xfe, 0xc2, 0x90, 0x04, 0xfe, 0xc2, 0x93, 0x79, 0x0b, 0x0e, 0x10, 0x4b, 0x22, 0x4c,
+ 0x10, 0x64, 0x22, 0x34, 0x01, 0x0c, 0x61, 0x24, 0x44, 0x37, 0x13, 0xfe, 0x4e, 0x11, 0x2f, 0xfe,
+ 0xde, 0x09, 0xfe, 0x9e, 0xf0, 0xfe, 0xf2, 0x09, 0xfe, 0x01, 0x48, 0x1b, 0x3c, 0x37, 0x88, 0xf5,
+ 0xd4, 0xfe, 0x1e, 0x0a, 0xd5, 0xfe, 0x42, 0x0a, 0xd2, 0xfe, 0x1e, 0x0a, 0xd3, 0xfe, 0x42, 0x0a,
+ 0xae, 0xfe, 0x12, 0x0a, 0xfe, 0x06, 0xf0, 0xfe, 0x18, 0x0a, 0xaf, 0xa0, 0x05, 0x29, 0x01, 0x41,
+ 0xfe, 0xc1, 0x10, 0x14, 0x24, 0xfe, 0xc1, 0x10, 0x01, 0x76, 0x06, 0x07, 0xfe, 0x14, 0x12, 0x01,
+ 0x76, 0x06, 0x0d, 0x5d, 0x01, 0x0c, 0x06, 0x0d, 0xfe, 0x74, 0x12, 0xfe, 0x2e, 0x1c, 0x05, 0xfe,
+ 0x1a, 0x0c, 0x01, 0x76, 0x06, 0x07, 0x5d, 0x01, 0x76, 0x06, 0x0d, 0x41, 0xfe, 0x2c, 0x1c, 0xfe,
+ 0xaa, 0xf0, 0xfe, 0xce, 0x0a, 0xfe, 0xac, 0xf0, 0xfe, 0x66, 0x0a, 0xfe, 0x92, 0x10, 0xc4, 0xf6,
+ 0xfe, 0xad, 0xf0, 0xfe, 0x72, 0x0a, 0x05, 0xfe, 0x1a, 0x0c, 0xc5, 0xfe, 0xe7, 0x10, 0xfe, 0x2b,
+ 0xf0, 0xbf, 0xfe, 0x6b, 0x18, 0x23, 0xfe, 0x00, 0xfe, 0xfe, 0x1c, 0x12, 0xac, 0xfe, 0xd2, 0xf0,
+ 0xbf, 0xfe, 0x76, 0x18, 0x23, 0x1d, 0x1b, 0xbf, 0x03, 0xe3, 0x23, 0x07, 0x1b, 0xbf, 0xd4, 0x5b,
+ 0xd5, 0x5b, 0xd2, 0x5b, 0xd3, 0x5b, 0xc4, 0xc5, 0xfe, 0xa9, 0x10, 0x75, 0x5e, 0x32, 0x1f, 0x7f,
+ 0x01, 0x42, 0x19, 0xfe, 0x35, 0x00, 0xfe, 0x01, 0xf0, 0x70, 0x19, 0x98, 0x05, 0x70, 0xfe, 0x74,
+ 0x18, 0x23, 0xfe, 0x00, 0xf8, 0x1b, 0x5b, 0x7d, 0x12, 0x01, 0xfe, 0x78, 0x0f, 0x4d, 0x01, 0xfe,
+ 0x96, 0x1a, 0x21, 0x30, 0x77, 0x7d, 0x1d, 0x05, 0x5b, 0x01, 0x0c, 0x06, 0x0d, 0x2b, 0xfe, 0xe2,
+ 0x0b, 0x01, 0x0c, 0x06, 0x54, 0xfe, 0xa6, 0x12, 0x01, 0x0c, 0x06, 0x24, 0xfe, 0x88, 0x13, 0x21,
+ 0x6e, 0xc7, 0x01, 0xfe, 0x1e, 0x1f, 0x0f, 0xfe, 0x83, 0x80, 0x04, 0xfe, 0x83, 0x83, 0xfe, 0xc9,
+ 0x47, 0x0b, 0x0e, 0xfe, 0xc8, 0x44, 0xfe, 0x42, 0x13, 0x0f, 0xfe, 0x04, 0x91, 0x04, 0xfe, 0x84,
+ 0x93, 0xfe, 0xca, 0x57, 0x0b, 0xfe, 0x86, 0x91, 0x04, 0xfe, 0x86, 0x93, 0xfe, 0xcb, 0x57, 0x0b,
+ 0x0e, 0x7a, 0x30, 0xfe, 0x40, 0x59, 0xfe, 0xc1, 0x59, 0x8e, 0x40, 0x03, 0x6a, 0x3b, 0x6b, 0x10,
+ 0x97, 0x22, 0x98, 0xd9, 0x6a, 0xda, 0x6b, 0x01, 0xc2, 0xc8, 0x7a, 0x30, 0x20, 0x6e, 0xdb, 0x64,
+ 0xdc, 0x34, 0x91, 0x6c, 0x7e, 0x6d, 0xfe, 0x44, 0x55, 0xfe, 0xe5, 0x55, 0xfe, 0x04, 0xfa, 0x64,
+ 0xfe, 0x05, 0xfa, 0x34, 0x01, 0xfe, 0x6a, 0x16, 0xa3, 0x26, 0x10, 0x97, 0x10, 0x98, 0x91, 0x6c,
+ 0x7e, 0x6d, 0xfe, 0x14, 0x10, 0x01, 0x0c, 0x06, 0x24, 0x1b, 0x40, 0x91, 0x4b, 0x7e, 0x4c, 0x01,
+ 0x0c, 0x06, 0xfe, 0xf7, 0x00, 0x44, 0x03, 0x68, 0x3b, 0x69, 0xfe, 0x10, 0x58, 0xfe, 0x91, 0x58,
+ 0xfe, 0x14, 0x59, 0xfe, 0x95, 0x59, 0x05, 0x5b, 0x01, 0x0c, 0x06, 0x24, 0x1b, 0x40, 0x01, 0x0c,
+ 0x06, 0xfe, 0xf7, 0x00, 0x44, 0x78, 0x01, 0xfe, 0x8e, 0x1e, 0x4f, 0x0f, 0xfe, 0x10, 0x90, 0x04,
+ 0xfe, 0x90, 0x93, 0x3a, 0x0b, 0xfe, 0x92, 0x90, 0x04, 0xfe, 0x92, 0x93, 0x79, 0x0b, 0x0e, 0xfe,
+ 0xbd, 0x10, 0x01, 0x43, 0x09, 0xbb, 0x1b, 0xfe, 0x6e, 0x0a, 0x15, 0xbb, 0x01, 0x0c, 0x06, 0x0d,
+ 0xfe, 0x14, 0x13, 0x03, 0x4b, 0x3b, 0x4c, 0x8e, 0xfe, 0x6e, 0x0a, 0xfe, 0x0c, 0x58, 0xfe, 0x8d,
+ 0x58, 0x05, 0x5b, 0x26, 0x3e, 0x0f, 0xfe, 0x19, 0x80, 0x04, 0xfe, 0x99, 0x83, 0x33, 0x0b, 0x0e,
+ 0xfe, 0xe5, 0x10, 0x01, 0x0c, 0x06, 0x0d, 0xfe, 0x1a, 0x12, 0xfe, 0x6c, 0x19, 0xfe, 0x19, 0x41,
+ 0xfe, 0x6b, 0x18, 0xac, 0xfe, 0xd1, 0xf0, 0xef, 0x1f, 0x92, 0x01, 0x42, 0x19, 0xfe, 0x44, 0x00,
+ 0xfe, 0x90, 0x10, 0xfe, 0x6c, 0x19, 0xd9, 0x4b, 0xfe, 0xed, 0x19, 0xda, 0x4c, 0xfe, 0x0c, 0x51,
+ 0xfe, 0x8e, 0x51, 0xfe, 0x6b, 0x18, 0x23, 0xfe, 0x00, 0xff, 0x31, 0xfe, 0x76, 0x10, 0xac, 0xfe,
+ 0xd2, 0xf0, 0xfe, 0xba, 0x0c, 0xfe, 0x76, 0x18, 0x23, 0x1d, 0x5d, 0x03, 0xe3, 0x23, 0x07, 0xfe,
+ 0x08, 0x13, 0x19, 0xfe, 0x16, 0x00, 0x05, 0x70, 0xfe, 0xd1, 0xf0, 0xfe, 0xcc, 0x0c, 0x1f, 0x92,
+ 0x01, 0x42, 0x19, 0xfe, 0x17, 0x00, 0x5c, 0xfe, 0xce, 0xf0, 0xfe, 0xd2, 0x0c, 0xfe, 0x3e, 0x10,
+ 0xfe, 0xcd, 0xf0, 0xfe, 0xde, 0x0c, 0x19, 0xfe, 0x22, 0x00, 0x05, 0x70, 0xfe, 0xcb, 0xf0, 0xfe,
+ 0xea, 0x0c, 0x19, 0xfe, 0x24, 0x00, 0x05, 0x70, 0xfe, 0xd0, 0xf0, 0xfe, 0xf4, 0x0c, 0x19, 0x94,
+ 0xfe, 0x1c, 0x10, 0xfe, 0xcf, 0xf0, 0xfe, 0xfe, 0x0c, 0x19, 0x4a, 0xf3, 0xfe, 0xcc, 0xf0, 0xef,
+ 0x01, 0x76, 0x06, 0x24, 0x4d, 0x19, 0xfe, 0x12, 0x00, 0x37, 0x13, 0xfe, 0x4e, 0x11, 0x2f, 0xfe,
+ 0x16, 0x0d, 0xfe, 0x9e, 0xf0, 0xfe, 0x2a, 0x0d, 0xfe, 0x01, 0x48, 0x1b, 0x3c, 0x37, 0x88, 0xf5,
+ 0xd4, 0x29, 0xd5, 0x29, 0xd2, 0x29, 0xd3, 0x29, 0x37, 0xfe, 0x9c, 0x32, 0x2f, 0xfe, 0x3e, 0x0d,
+ 0x2a, 0x3c, 0xae, 0xfe, 0x62, 0x0d, 0xaf, 0xa0, 0xd4, 0x9f, 0xd5, 0x9f, 0xd2, 0x9f, 0xd3, 0x9f,
+ 0x05, 0x29, 0x01, 0x41, 0xfe, 0xd3, 0x10, 0x15, 0xfe, 0xe8, 0x00, 0xc4, 0xc5, 0x75, 0xd7, 0x99,
+ 0xd8, 0x9c, 0xfe, 0x89, 0xf0, 0x29, 0x27, 0x25, 0xbe, 0xd7, 0x99, 0xd8, 0x9c, 0x2f, 0xfe, 0x8c,
+ 0x0d, 0x16, 0x29, 0x27, 0x25, 0xbd, 0xfe, 0x01, 0x48, 0xa4, 0x19, 0xfe, 0x42, 0x00, 0x05, 0x70,
+ 0x90, 0x07, 0xfe, 0x81, 0x49, 0x1b, 0xfe, 0x64, 0x0e, 0x01, 0x0c, 0x06, 0x0d, 0xfe, 0x44, 0x13,
+ 0x19, 0x00, 0x2d, 0x0d, 0xfe, 0x54, 0x12, 0x2d, 0xfe, 0x28, 0x00, 0x2b, 0xfe, 0xda, 0x0e, 0x0a,
+ 0x57, 0x01, 0x18, 0x09, 0x00, 0x36, 0x46, 0xfe, 0x28, 0x00, 0xfe, 0xfa, 0x10, 0x01, 0xfe, 0xf4,
+ 0x1c, 0x01, 0xfe, 0x00, 0x1d, 0x0a, 0xba, 0x01, 0xfe, 0x58, 0x10, 0x40, 0x15, 0x56, 0x01, 0x85,
+ 0x05, 0x35, 0x19, 0xfe, 0x44, 0x00, 0x2d, 0x0d, 0xf7, 0x46, 0x0d, 0xfe, 0xcc, 0x10, 0x01, 0xa7,
+ 0x46, 0x0d, 0xfe, 0xc2, 0x10, 0x01, 0xa7, 0x0f, 0xfe, 0x19, 0x82, 0x04, 0xfe, 0x99, 0x83, 0xfe,
+ 0xcc, 0x47, 0x0b, 0x0e, 0xfe, 0x34, 0x46, 0xa5, 0x46, 0x0d, 0x19, 0xfe, 0x43, 0x00, 0xfe, 0xa2,
+ 0x10, 0x01, 0x0c, 0x61, 0x0d, 0x44, 0x01, 0xfe, 0xf4, 0x1c, 0x01, 0xfe, 0x00, 0x1d, 0x40, 0x15,
+ 0x56, 0x01, 0x85, 0x7d, 0x0d, 0x40, 0x51, 0x01, 0xfe, 0x9e, 0x1e, 0x05, 0xfe, 0x3a, 0x03, 0x01,
+ 0x0c, 0x06, 0x0d, 0x5d, 0x46, 0x0d, 0x19, 0x00, 0xfe, 0x62, 0x10, 0x01, 0x76, 0x06, 0x12, 0xfe,
+ 0x5c, 0x12, 0x01, 0x0c, 0x06, 0x12, 0xfe, 0x52, 0x13, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe,
+ 0x8e, 0x0e, 0xfe, 0x1c, 0x1c, 0xfe, 0x9d, 0xf0, 0xfe, 0x94, 0x0e, 0x01, 0x0c, 0x61, 0x12, 0x44,
+ 0xfe, 0x9f, 0x10, 0x19, 0xfe, 0x15, 0x00, 0xfe, 0x04, 0xe6, 0x0d, 0x4f, 0xfe, 0x2e, 0x10, 0x19,
+ 0xfe, 0x13, 0x00, 0xfe, 0x10, 0x10, 0x19, 0xfe, 0x47, 0x00, 0xf1, 0x19, 0xfe, 0x41, 0x00, 0xa2,
+ 0x19, 0xfe, 0x24, 0x00, 0x86, 0xc4, 0xc5, 0x75, 0x03, 0x81, 0x1e, 0x2b, 0xea, 0x4f, 0xfe, 0x04,
+ 0xe6, 0x12, 0xfe, 0x9d, 0x41, 0xfe, 0x1c, 0x42, 0x40, 0x01, 0xf4, 0x05, 0x35, 0xfe, 0x12, 0x1c,
+ 0x1f, 0x0d, 0x47, 0xb5, 0xc3, 0x1f, 0xfe, 0x31, 0x00, 0x47, 0xb8, 0x01, 0xfe, 0xd4, 0x11, 0x05,
+ 0xe9, 0x51, 0xfe, 0x06, 0xec, 0xe0, 0xfe, 0x0e, 0x47, 0x46, 0x28, 0xfe, 0xce, 0x45, 0x31, 0x51,
+ 0xfe, 0x06, 0xea, 0xe0, 0xfe, 0x47, 0x4b, 0x45, 0xfe, 0x75, 0x57, 0x03, 0x67, 0xfe, 0x98, 0x56,
+ 0xfe, 0x38, 0x12, 0x0a, 0x5a, 0x01, 0x18, 0xfe, 0x44, 0x48, 0x60, 0x01, 0x0c, 0x06, 0x28, 0xfe,
+ 0x18, 0x13, 0x0a, 0x57, 0x01, 0x18, 0x3e, 0xfe, 0x41, 0x58, 0x0a, 0xba, 0xfe, 0xfa, 0x14, 0xfe,
+ 0x49, 0x54, 0xb0, 0xfe, 0x5e, 0x0f, 0x05, 0xfe, 0x3a, 0x03, 0x0a, 0x67, 0xfe, 0xe0, 0x14, 0xfe,
+ 0x0e, 0x47, 0x46, 0x28, 0xfe, 0xce, 0x45, 0x31, 0x51, 0xfe, 0xce, 0x47, 0xfe, 0xad, 0x13, 0x05,
+ 0x35, 0x21, 0x2c, 0x09, 0x1a, 0xfe, 0x98, 0x12, 0x26, 0x20, 0x96, 0x20, 0xe7, 0xfe, 0x08, 0x1c,
+ 0xfe, 0x7c, 0x19, 0xfe, 0xfd, 0x19, 0xfe, 0x0a, 0x1c, 0x03, 0xe5, 0xfe, 0x48, 0x55, 0xa5, 0x3b,
+ 0xfe, 0x62, 0x01, 0xfe, 0xc9, 0x55, 0x31, 0xfe, 0x74, 0x10, 0x01, 0xfe, 0xf0, 0x1a, 0x03, 0xfe,
+ 0x38, 0x01, 0x3b, 0xfe, 0x3a, 0x01, 0x8e, 0xfe, 0x1e, 0x10, 0xfe, 0x02, 0xec, 0xe7, 0x53, 0x00,
+ 0x36, 0xfe, 0x04, 0xec, 0x2c, 0x60, 0xfe, 0x05, 0xf6, 0xfe, 0x34, 0x01, 0x01, 0xfe, 0x62, 0x1b,
+ 0x01, 0xfe, 0xce, 0x1e, 0xb2, 0x11, 0xfe, 0x18, 0x13, 0xca, 0xfe, 0x02, 0xea, 0xe7, 0x53, 0x92,
+ 0xfe, 0xc3, 0x13, 0x1f, 0x12, 0x47, 0xb5, 0xc3, 0xfe, 0x2a, 0x10, 0x03, 0xfe, 0x38, 0x01, 0x23,
+ 0xfe, 0xf0, 0xff, 0x10, 0xe5, 0x03, 0xfe, 0x3a, 0x01, 0x10, 0xfe, 0x62, 0x01, 0x01, 0xfe, 0x1e,
+ 0x1e, 0x20, 0x2c, 0x15, 0x56, 0x01, 0xfe, 0x9e, 0x1e, 0x13, 0x07, 0x02, 0x26, 0x02, 0x21, 0x96,
+ 0xc7, 0x20, 0x96, 0x09, 0x92, 0xfe, 0x79, 0x13, 0x1f, 0x1d, 0x47, 0xb5, 0xc3, 0xfe, 0xe1, 0x10,
+ 0xcf, 0xfe, 0x03, 0xdc, 0xfe, 0x73, 0x57, 0xfe, 0x80, 0x5d, 0x02, 0xcf, 0xfe, 0x03, 0xdc, 0xfe,
+ 0x5b, 0x57, 0xfe, 0x80, 0x5d, 0x02, 0xfe, 0x03, 0x57, 0xcf, 0x26, 0xfe, 0x00, 0xcc, 0x02, 0xfe,
+ 0x03, 0x57, 0xcf, 0x89, 0x02, 0x01, 0x0c, 0x06, 0x4a, 0xfe, 0x4e, 0x13, 0x0f, 0xfe, 0x1c, 0x80,
+ 0x04, 0xfe, 0x9c, 0x83, 0x33, 0x0b, 0x0e, 0x09, 0x07, 0xfe, 0x3a, 0x13, 0x0f, 0xfe, 0x1e, 0x80,
+ 0x04, 0xfe, 0x9e, 0x83, 0x33, 0x0b, 0x0e, 0xfe, 0x2a, 0x13, 0x0f, 0xfe, 0x1d, 0x80, 0x04, 0xfe,
+ 0x9d, 0x83, 0xfe, 0xf9, 0x13, 0x0e, 0xfe, 0x1c, 0x13, 0x01, 0xfe, 0xee, 0x1e, 0xac, 0xfe, 0x14,
+ 0x13, 0x01, 0xfe, 0xfe, 0x1e, 0xfe, 0x81, 0x58, 0xfa, 0x01, 0xfe, 0x0e, 0x1f, 0xfe, 0x30, 0xf4,
+ 0x0d, 0xfe, 0x3c, 0x50, 0xa2, 0x01, 0xfe, 0x92, 0x1b, 0x01, 0x43, 0x09, 0x56, 0xfb, 0x01, 0xfe,
+ 0xc8, 0x1a, 0x01, 0x0c, 0x06, 0x28, 0xa4, 0x01, 0xfe, 0xf4, 0x1c, 0x01, 0xfe, 0x00, 0x1d, 0x15,
+ 0xfe, 0xe9, 0x00, 0x01, 0x0c, 0x06, 0x4a, 0xfe, 0x4e, 0x13, 0x01, 0xfe, 0x22, 0x1b, 0xfe, 0x1e,
+ 0x1c, 0x0f, 0xfe, 0x14, 0x90, 0x04, 0xfe, 0x94, 0x93, 0x3a, 0x0b, 0xfe, 0x96, 0x90, 0x04, 0xfe,
+ 0x96, 0x93, 0x79, 0x0b, 0x0e, 0x10, 0xfe, 0x64, 0x01, 0x22, 0xfe, 0x66, 0x01, 0x01, 0x0c, 0x06,
+ 0x65, 0xf9, 0x0f, 0xfe, 0x03, 0x80, 0x04, 0xfe, 0x83, 0x83, 0x33, 0x0b, 0x0e, 0x77, 0xfe, 0x01,
+ 0xec, 0x2c, 0xfe, 0x80, 0x40, 0x20, 0x2c, 0x7a, 0x30, 0x15, 0xdf, 0x40, 0x21, 0x2c, 0xfe, 0x00,
+ 0x40, 0x8d, 0x2c, 0x02, 0xfe, 0x08, 0x1c, 0x03, 0xfe, 0xac, 0x00, 0xfe, 0x06, 0x58, 0x03, 0xfe,
+ 0xae, 0x00, 0xfe, 0x07, 0x58, 0x03, 0xfe, 0xb0, 0x00, 0xfe, 0x08, 0x58, 0x03, 0xfe, 0xb2, 0x00,
+ 0xfe, 0x09, 0x58, 0xfe, 0x0a, 0x1c, 0x2e, 0x49, 0x20, 0xe0, 0x26, 0x10, 0x66, 0x10, 0x55, 0x10,
+ 0x6f, 0x13, 0x57, 0x52, 0x4f, 0x1c, 0x28, 0xfe, 0x90, 0x4d, 0xfe, 0x91, 0x54, 0x2b, 0xfe, 0x88,
+ 0x11, 0x46, 0x1a, 0x13, 0x5a, 0x52, 0x1c, 0x4a, 0xfe, 0x90, 0x4d, 0xfe, 0x91, 0x54, 0x2b, 0xfe,
+ 0x9e, 0x11, 0x2e, 0x1a, 0x20, 0x2c, 0x90, 0x34, 0x60, 0x21, 0x2c, 0xfe, 0x00, 0x40, 0x8d, 0x2c,
+ 0x15, 0xdf, 0xfe, 0x14, 0x56, 0xfe, 0xd6, 0xf0, 0xfe, 0xb2, 0x11, 0xfe, 0x12, 0x1c, 0x75, 0xfe,
+ 0x14, 0x1c, 0xfe, 0x10, 0x1c, 0xfe, 0x18, 0x1c, 0x02, 0x51, 0xfe, 0x0c, 0x14, 0xfe, 0x0e, 0x47,
+ 0xfe, 0x07, 0xe6, 0x28, 0xfe, 0xce, 0x47, 0xfe, 0xf5, 0x13, 0x02, 0x01, 0xa7, 0x90, 0x34, 0x60,
+ 0xfe, 0x06, 0x80, 0xfe, 0x48, 0x47, 0xfe, 0x42, 0x13, 0xfe, 0x02, 0x80, 0x09, 0x56, 0xfe, 0x34,
+ 0x13, 0x0a, 0x5a, 0x01, 0x18, 0xcb, 0xfe, 0x36, 0x12, 0xfe, 0x41, 0x48, 0xfe, 0x45, 0x48, 0x01,
+ 0xfe, 0xb2, 0x16, 0xfe, 0x00, 0xcc, 0xcb, 0xfe, 0xf3, 0x13, 0x3f, 0x89, 0x09, 0x1a, 0xa5, 0x0a,
+ 0x9d, 0x01, 0x18, 0xfe, 0x80, 0x5c, 0x01, 0x85, 0xf2, 0x09, 0x9b, 0xa4, 0xfe, 0x14, 0x56, 0xfe,
+ 0xd6, 0xf0, 0xfe, 0xec, 0x11, 0x02, 0xfe, 0x44, 0x58, 0x77, 0xfe, 0x01, 0xec, 0xb8, 0xfe, 0x9e,
+ 0x40, 0xfe, 0x9d, 0xe7, 0x00, 0xfe, 0x9c, 0xe7, 0x12, 0x8d, 0x30, 0x01, 0xf4, 0xfe, 0xdd, 0x10,
+ 0x37, 0xd7, 0x99, 0xd8, 0x9c, 0x27, 0x25, 0xee, 0x09, 0x12, 0xfe, 0x48, 0x12, 0x09, 0x0d, 0xfe,
+ 0x56, 0x12, 0x09, 0x1d, 0xfe, 0x30, 0x12, 0x09, 0xdd, 0x1b, 0xfe, 0xc4, 0x13, 0x09, 0xfe, 0x23,
+ 0x00, 0x1b, 0xfe, 0xd0, 0x13, 0x09, 0x07, 0x1b, 0xfe, 0x34, 0x14, 0x09, 0x24, 0xfe, 0x12, 0x12,
+ 0x09, 0x00, 0x1b, 0x29, 0x1f, 0xdd, 0x01, 0x42, 0xa1, 0x32, 0x01, 0x08, 0xae, 0x41, 0x02, 0x32,
+ 0xfe, 0x62, 0x08, 0x0a, 0xe1, 0x01, 0xfe, 0x58, 0x10, 0x15, 0x9b, 0x05, 0x35, 0x32, 0x01, 0x43,
+ 0x09, 0xbb, 0xfe, 0xd7, 0x13, 0x91, 0x4b, 0x7e, 0x4c, 0x8e, 0xfe, 0x80, 0x13, 0x01, 0x0c, 0x06,
+ 0x54, 0xfe, 0x72, 0x12, 0xdb, 0x64, 0xdc, 0x34, 0xfe, 0x44, 0x55, 0xfe, 0xe5, 0x55, 0xb0, 0xfe,
+ 0x4a, 0x13, 0x21, 0x6e, 0xfe, 0x26, 0x13, 0x03, 0x97, 0x3b, 0x98, 0x8e, 0xfe, 0xb6, 0x0e, 0x10,
+ 0x6a, 0x22, 0x6b, 0x26, 0x10, 0x97, 0x10, 0x98, 0x01, 0xc2, 0x2e, 0x49, 0x88, 0x20, 0x6e, 0x01,
+ 0xfe, 0x6a, 0x16, 0xdb, 0x64, 0xdc, 0x34, 0xfe, 0x04, 0x55, 0xfe, 0xa5, 0x55, 0xfe, 0x04, 0xfa,
+ 0x64, 0xfe, 0x05, 0xfa, 0x34, 0xfe, 0x8f, 0x10, 0x03, 0x6c, 0x3b, 0x6d, 0xfe, 0x40, 0x56, 0xfe,
+ 0xe1, 0x56, 0x10, 0x6c, 0x22, 0x6d, 0x71, 0xdb, 0x64, 0xdc, 0x34, 0xfe, 0x44, 0x55, 0xfe, 0xe5,
+ 0x55, 0x03, 0x68, 0x3b, 0x69, 0xfe, 0x00, 0x56, 0xfe, 0xa1, 0x56, 0x10, 0x68, 0x22, 0x69, 0x01,
+ 0x0c, 0x06, 0x54, 0xf9, 0x21, 0x6e, 0xfe, 0x1f, 0x40, 0x03, 0x6a, 0x3b, 0x6b, 0xfe, 0x2c, 0x50,
+ 0xfe, 0xae, 0x50, 0x03, 0x6c, 0x3b, 0x6d, 0xfe, 0x44, 0x50, 0xfe, 0xc6, 0x50, 0x03, 0x68, 0x3b,
+ 0x69, 0xfe, 0x08, 0x50, 0xfe, 0x8a, 0x50, 0x03, 0x4b, 0x3b, 0x4c, 0xfe, 0x40, 0x50, 0xfe, 0xc2,
+ 0x50, 0x05, 0x73, 0x2e, 0x07, 0x20, 0x9e, 0x05, 0x72, 0x32, 0x01, 0x08, 0x16, 0x3d, 0x27, 0x25,
+ 0xee, 0x09, 0x07, 0x2b, 0x3d, 0x01, 0x43, 0x09, 0xbb, 0x2b, 0x72, 0x01, 0xa6, 0x23, 0x3f, 0x1b,
+ 0x3d, 0x01, 0x0c, 0x06, 0x0d, 0xfe, 0x1e, 0x13, 0x91, 0x4b, 0x7e, 0x4c, 0xfe, 0x0a, 0x55, 0x31,
+ 0xfe, 0x8b, 0x55, 0xd9, 0x4b, 0xda, 0x4c, 0xfe, 0x0c, 0x51, 0xfe, 0x8e, 0x51, 0x05, 0x72, 0x01,
+ 0xfe, 0x8e, 0x1e, 0xca, 0xfe, 0x19, 0x41, 0x05, 0x72, 0x32, 0x01, 0x08, 0x2a, 0x3c, 0x16, 0xc0,
+ 0x27, 0x25, 0xbe, 0x2d, 0x1d, 0xc0, 0x2d, 0x0d, 0x83, 0x2d, 0x7f, 0x1b, 0xfe, 0x66, 0x15, 0x05,
+ 0x3d, 0x01, 0x08, 0x2a, 0x3c, 0x16, 0xc0, 0x27, 0x25, 0xbd, 0x09, 0x1d, 0x2b, 0x3d, 0x01, 0x08,
+ 0x16, 0xc0, 0x27, 0x25, 0xfe, 0xe8, 0x09, 0xfe, 0xc2, 0x49, 0x50, 0x03, 0xb6, 0x1e, 0x83, 0x01,
+ 0x38, 0x06, 0x24, 0x31, 0xa1, 0xfe, 0xbb, 0x45, 0x2d, 0x00, 0xa4, 0x46, 0x07, 0x90, 0x3f, 0x01,
+ 0xfe, 0xf8, 0x15, 0x01, 0xa6, 0x86, 0xfe, 0x4b, 0x45, 0xfe, 0x20, 0x13, 0x01, 0x43, 0x09, 0x82,
+ 0xfe, 0x16, 0x13, 0x03, 0x9a, 0x1e, 0x5d, 0x03, 0x55, 0x1e, 0x31, 0x5e, 0x05, 0x72, 0xfe, 0xc0,
+ 0x5d, 0x01, 0xa7, 0xfe, 0x03, 0x17, 0x03, 0x66, 0x8a, 0x10, 0x66, 0x5e, 0x32, 0x01, 0x08, 0x17,
+ 0x73, 0x01, 0xfe, 0x56, 0x19, 0x05, 0x73, 0x01, 0x08, 0x2a, 0x3c, 0x16, 0x3d, 0x27, 0x25, 0xbd,
+ 0x09, 0x07, 0x2b, 0x3d, 0x01, 0xfe, 0xbe, 0x16, 0xfe, 0x42, 0x58, 0xfe, 0xe8, 0x14, 0x01, 0xa6,
+ 0x86, 0xfe, 0x4a, 0xf4, 0x0d, 0x1b, 0x3d, 0xfe, 0x4a, 0xf4, 0x07, 0xfe, 0x0e, 0x12, 0x01, 0x43,
+ 0x09, 0x82, 0x4e, 0x05, 0x72, 0x03, 0x55, 0x8a, 0x10, 0x55, 0x5e, 0x32, 0x01, 0x08, 0x17, 0x73,
+ 0x01, 0xfe, 0x84, 0x19, 0x05, 0x73, 0x01, 0x08, 0x2a, 0x3c, 0x16, 0x3d, 0x27, 0x25, 0xbd, 0x09,
+ 0x12, 0x2b, 0x3d, 0x01, 0xfe, 0xe8, 0x17, 0x8b, 0xfe, 0xaa, 0x14, 0xfe, 0xb6, 0x14, 0x86, 0xa8,
+ 0xb2, 0x0d, 0x1b, 0x3d, 0xb2, 0x07, 0xfe, 0x0e, 0x12, 0x01, 0x43, 0x09, 0x82, 0x4e, 0x05, 0x72,
+ 0x03, 0x6f, 0x8a, 0x10, 0x6f, 0x5e, 0x32, 0x01, 0x08, 0x17, 0x73, 0x01, 0xfe, 0xc0, 0x19, 0x05,
+ 0x73, 0x13, 0x07, 0x2f, 0xfe, 0xcc, 0x15, 0x17, 0xfe, 0xe2, 0x15, 0x5f, 0xcc, 0x01, 0x08, 0x26,
+ 0x5f, 0x02, 0x8f, 0xfe, 0xde, 0x15, 0x2a, 0xfe, 0xde, 0x15, 0x16, 0xfe, 0xcc, 0x15, 0x5e, 0x32,
+ 0x01, 0x08, 0xfe, 0xd5, 0x10, 0x13, 0x58, 0xff, 0x02, 0x00, 0x57, 0x52, 0xad, 0x23, 0xfe, 0xff,
+ 0x7f, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x02, 0x13, 0x58, 0xff, 0x02, 0x00, 0x57, 0x52, 0xad,
+ 0x23, 0x3f, 0xfe, 0x30, 0x56, 0xfe, 0x00, 0x5c, 0x02, 0x13, 0x58, 0xff, 0x02, 0x00, 0x57, 0x52,
+ 0xad, 0x02, 0x13, 0x58, 0xff, 0x02, 0x00, 0x57, 0x52, 0xfe, 0x00, 0x5e, 0x02, 0x13, 0x58, 0xff,
+ 0x02, 0x00, 0x57, 0x52, 0xad, 0xfe, 0x0b, 0x58, 0x02, 0x0a, 0x66, 0x01, 0x5c, 0x0a, 0x55, 0x01,
+ 0x5c, 0x0a, 0x6f, 0x01, 0x5c, 0x02, 0x01, 0xfe, 0x1e, 0x1f, 0x23, 0x1a, 0xff, 0x03, 0x00, 0x54,
+ 0xfe, 0x00, 0xf4, 0x24, 0x52, 0x0f, 0xfe, 0x00, 0x7c, 0x04, 0xfe, 0x07, 0x7c, 0x3a, 0x0b, 0x0e,
+ 0xfe, 0x00, 0x71, 0xfe, 0xf9, 0x18, 0xfe, 0x7a, 0x19, 0xfe, 0xfb, 0x19, 0xfe, 0x1a, 0xf7, 0x00,
+ 0xfe, 0x1b, 0xf7, 0x00, 0x7a, 0x30, 0x10, 0x68, 0x22, 0x69, 0xd9, 0x6c, 0xda, 0x6d, 0x02, 0xfe,
+ 0x62, 0x08, 0xfe, 0x82, 0x4a, 0xfe, 0xe1, 0x1a, 0xfe, 0x83, 0x5a, 0x77, 0x02, 0x01, 0xc6, 0xfe,
+ 0x42, 0x48, 0x4f, 0x50, 0x45, 0x01, 0x08, 0x16, 0xfe, 0xe0, 0x17, 0x27, 0x25, 0xbe, 0x01, 0x08,
+ 0x16, 0xfe, 0xe0, 0x17, 0x27, 0x25, 0xfe, 0xe8, 0x0a, 0xfe, 0xc1, 0x59, 0x03, 0x9a, 0x1e, 0xfe,
+ 0xda, 0x12, 0x01, 0x38, 0x06, 0x12, 0xfe, 0xd0, 0x13, 0x26, 0x53, 0x12, 0x48, 0xfe, 0x08, 0x17,
+ 0xd1, 0x12, 0x53, 0x12, 0xfe, 0x1e, 0x13, 0x2d, 0xb4, 0x7b, 0xfe, 0x26, 0x17, 0x4d, 0x13, 0x07,
+ 0x1c, 0xb4, 0x90, 0x04, 0xfe, 0x78, 0x10, 0xff, 0x02, 0x83, 0x55, 0xf1, 0xff, 0x02, 0x83, 0x55,
+ 0x53, 0x1d, 0xfe, 0x12, 0x13, 0xd6, 0xfe, 0x30, 0x00, 0xb0, 0xfe, 0x80, 0x17, 0x1c, 0x63, 0x13,
+ 0x07, 0xfe, 0x56, 0x10, 0x53, 0x0d, 0xfe, 0x16, 0x13, 0xd6, 0xfe, 0x64, 0x00, 0xb0, 0xfe, 0x80,
+ 0x17, 0x0a, 0xfe, 0x64, 0x00, 0x1c, 0x94, 0x13, 0x07, 0xfe, 0x28, 0x10, 0x53, 0x07, 0xfe, 0x60,
+ 0x13, 0xd6, 0xfe, 0xc8, 0x00, 0xb0, 0xfe, 0x80, 0x17, 0x0a, 0xfe, 0xc8, 0x00, 0x1c, 0x95, 0x13,
+ 0x07, 0x71, 0xd6, 0xfe, 0x90, 0x01, 0x48, 0xfe, 0x8c, 0x17, 0x45, 0xf3, 0xfe, 0x43, 0xf4, 0x96,
+ 0xfe, 0x56, 0xf0, 0xfe, 0x9e, 0x17, 0xfe, 0x04, 0xf4, 0x58, 0xfe, 0x43, 0xf4, 0x94, 0xf6, 0x8b,
+ 0x01, 0xfe, 0x24, 0x16, 0x23, 0x3f, 0xfc, 0xa8, 0x8c, 0x49, 0x48, 0xfe, 0xda, 0x17, 0x62, 0x49,
+ 0xfe, 0x1c, 0x10, 0xa8, 0x8c, 0x80, 0x48, 0xfe, 0xda, 0x17, 0x62, 0x80, 0x71, 0x50, 0x26, 0xfe,
+ 0x4d, 0xf4, 0x00, 0xf7, 0x45, 0x13, 0x07, 0xfe, 0xb4, 0x56, 0xfe, 0xc3, 0x58, 0x02, 0x50, 0x13,
+ 0x0d, 0x02, 0x50, 0x3e, 0x78, 0x4f, 0x45, 0x01, 0x08, 0x16, 0xa9, 0x27, 0x25, 0xbe, 0xfe, 0x03,
+ 0xea, 0xfe, 0x7e, 0x01, 0x01, 0x08, 0x16, 0xa9, 0x27, 0x25, 0xfe, 0xe9, 0x0a, 0x01, 0x08, 0x16,
+ 0xa9, 0x27, 0x25, 0xfe, 0xe9, 0x0a, 0xfe, 0x05, 0xea, 0xfe, 0x7f, 0x01, 0x01, 0x08, 0x16, 0xa9,
+ 0x27, 0x25, 0xfe, 0x69, 0x09, 0xfe, 0x02, 0xea, 0xfe, 0x80, 0x01, 0x01, 0x08, 0x16, 0xa9, 0x27,
+ 0x25, 0xfe, 0xe8, 0x08, 0x47, 0xfe, 0x81, 0x01, 0x03, 0xb6, 0x1e, 0x83, 0x01, 0x38, 0x06, 0x24,
+ 0x31, 0xa2, 0x78, 0xf2, 0x53, 0x07, 0x36, 0xfe, 0x34, 0xf4, 0x3f, 0xa1, 0x78, 0x03, 0x9a, 0x1e,
+ 0x83, 0x01, 0x38, 0x06, 0x12, 0x31, 0xf0, 0x4f, 0x45, 0xfe, 0x90, 0x10, 0xfe, 0x40, 0x5a, 0x23,
+ 0x3f, 0xfb, 0x8c, 0x49, 0x48, 0xfe, 0xaa, 0x18, 0x62, 0x49, 0x71, 0x8c, 0x80, 0x48, 0xfe, 0xaa,
+ 0x18, 0x62, 0x80, 0xfe, 0xb4, 0x56, 0xfe, 0x40, 0x5d, 0x01, 0xc6, 0x01, 0xfe, 0xac, 0x1d, 0xfe,
+ 0x02, 0x17, 0xfe, 0xc8, 0x45, 0xfe, 0x5a, 0xf0, 0xfe, 0xc0, 0x18, 0xfe, 0x43, 0x48, 0x2d, 0x93,
+ 0x36, 0xfe, 0x34, 0xf4, 0xfe, 0x00, 0x11, 0xfe, 0x40, 0x10, 0x2d, 0xb4, 0x36, 0xfe, 0x34, 0xf4,
+ 0x04, 0xfe, 0x34, 0x10, 0x2d, 0xfe, 0x0b, 0x00, 0x36, 0x46, 0x63, 0xfe, 0x28, 0x10, 0xfe, 0xc0,
+ 0x49, 0xff, 0x02, 0x00, 0x54, 0xb2, 0xfe, 0x90, 0x01, 0x48, 0xfe, 0xfa, 0x18, 0x45, 0xfe, 0x1c,
+ 0xf4, 0x3f, 0xf3, 0xfe, 0x40, 0xf4, 0x96, 0xfe, 0x56, 0xf0, 0xfe, 0x0c, 0x19, 0xfe, 0x04, 0xf4,
+ 0x58, 0xfe, 0x40, 0xf4, 0x94, 0xf6, 0x3e, 0x2d, 0x93, 0x4e, 0xd0, 0x0d, 0x21, 0xfe, 0x7f, 0x01,
+ 0xfe, 0xc8, 0x46, 0xfe, 0x24, 0x13, 0x8c, 0x00, 0x5d, 0x26, 0x21, 0xfe, 0x7e, 0x01, 0xfe, 0xc8,
+ 0x45, 0xfe, 0x14, 0x13, 0x21, 0xfe, 0x80, 0x01, 0xfe, 0x48, 0x45, 0xfa, 0x21, 0xfe, 0x81, 0x01,
+ 0xfe, 0xc8, 0x44, 0x4e, 0x26, 0x02, 0x13, 0x07, 0x02, 0x78, 0x45, 0x50, 0x13, 0x0d, 0x02, 0x14,
+ 0x07, 0x01, 0x08, 0x17, 0xfe, 0x82, 0x19, 0x14, 0x0d, 0x01, 0x08, 0x17, 0xfe, 0x82, 0x19, 0x14,
+ 0x1d, 0x01, 0x08, 0x17, 0xfe, 0x82, 0x19, 0x5f, 0xfe, 0x89, 0x49, 0x01, 0x08, 0x02, 0x14, 0x07,
+ 0x01, 0x08, 0x17, 0xc1, 0x14, 0x1d, 0x01, 0x08, 0x17, 0xc1, 0x14, 0x07, 0x01, 0x08, 0x17, 0xc1,
+ 0xfe, 0x89, 0x49, 0x01, 0x08, 0x17, 0xc1, 0x5f, 0xfe, 0x89, 0x4a, 0x01, 0x08, 0x02, 0x50, 0x02,
+ 0x14, 0x07, 0x01, 0x08, 0x17, 0x74, 0x14, 0x7f, 0x01, 0x08, 0x17, 0x74, 0x14, 0x12, 0x01, 0x08,
+ 0x17, 0x74, 0xfe, 0x89, 0x49, 0x01, 0x08, 0x17, 0x74, 0x14, 0x00, 0x01, 0x08, 0x17, 0x74, 0xfe,
+ 0x89, 0x4a, 0x01, 0x08, 0x17, 0x74, 0xfe, 0x09, 0x49, 0x01, 0x08, 0x17, 0x74, 0x5f, 0xcc, 0x01,
+ 0x08, 0x02, 0x21, 0xe4, 0x09, 0x07, 0xfe, 0x4c, 0x13, 0xc8, 0x20, 0xe4, 0xfe, 0x49, 0xf4, 0x00,
+ 0x4d, 0x5f, 0xa1, 0x5e, 0xfe, 0x01, 0xec, 0xfe, 0x27, 0x01, 0xcc, 0xff, 0x02, 0x00, 0x10, 0x2f,
+ 0xfe, 0x3e, 0x1a, 0x01, 0x43, 0x09, 0xfe, 0xe3, 0x00, 0xfe, 0x22, 0x13, 0x16, 0xfe, 0x64, 0x1a,
+ 0x26, 0x20, 0x9e, 0x01, 0x41, 0x21, 0x9e, 0x09, 0x07, 0x5d, 0x01, 0x0c, 0x61, 0x07, 0x44, 0x02,
+ 0x0a, 0x5a, 0x01, 0x18, 0xfe, 0x00, 0x40, 0xaa, 0x09, 0x1a, 0xfe, 0x12, 0x13, 0x0a, 0x9d, 0x01,
+ 0x18, 0xaa, 0x0a, 0x67, 0x01, 0xa3, 0x02, 0x0a, 0x9d, 0x01, 0x18, 0xaa, 0xfe, 0x80, 0xe7, 0x1a,
+ 0x09, 0x1a, 0x5d, 0xfe, 0x45, 0x58, 0x01, 0xfe, 0xb2, 0x16, 0xaa, 0x02, 0x0a, 0x5a, 0x01, 0x18,
+ 0xaa, 0x0a, 0x67, 0x01, 0xa3, 0x02, 0x0a, 0x5a, 0x01, 0x18, 0x01, 0xfe, 0x7e, 0x1e, 0xfe, 0x80,
+ 0x4c, 0xfe, 0x49, 0xe4, 0x1a, 0xfe, 0x12, 0x13, 0x0a, 0x9d, 0x01, 0x18, 0xfe, 0x80, 0x4c, 0x0a,
+ 0x67, 0x01, 0x5c, 0x02, 0x1c, 0x1a, 0x87, 0x7c, 0xe5, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe,
+ 0x24, 0x1c, 0xfe, 0x1d, 0xf7, 0x28, 0xb1, 0xfe, 0x04, 0x1b, 0x01, 0xfe, 0x2a, 0x1c, 0xfa, 0xb3,
+ 0x28, 0x7c, 0xfe, 0x2c, 0x01, 0xfe, 0x2f, 0x19, 0x02, 0xc9, 0x2b, 0xfe, 0xf4, 0x1a, 0xfe, 0xfa,
+ 0x10, 0x1c, 0x1a, 0x87, 0x03, 0xfe, 0x64, 0x01, 0xfe, 0x00, 0xf4, 0x24, 0xfe, 0x18, 0x58, 0x03,
+ 0xfe, 0x66, 0x01, 0xfe, 0x19, 0x58, 0xb3, 0x24, 0x01, 0xfe, 0x0e, 0x1f, 0xfe, 0x30, 0xf4, 0x07,
+ 0xfe, 0x3c, 0x50, 0x7c, 0xfe, 0x38, 0x00, 0xfe, 0x0f, 0x79, 0xfe, 0x1c, 0xf7, 0x24, 0xb1, 0xfe,
+ 0x50, 0x1b, 0xfe, 0xd4, 0x14, 0x31, 0x02, 0xc9, 0x2b, 0xfe, 0x26, 0x1b, 0xfe, 0xba, 0x10, 0x1c,
+ 0x1a, 0x87, 0xfe, 0x83, 0x5a, 0xfe, 0x18, 0xdf, 0xfe, 0x19, 0xde, 0xfe, 0x1d, 0xf7, 0x54, 0xb1,
+ 0xfe, 0x72, 0x1b, 0xfe, 0xb2, 0x14, 0xfc, 0xb3, 0x54, 0x7c, 0x12, 0xfe, 0xaf, 0x19, 0xfe, 0x98,
+ 0xe7, 0x00, 0x02, 0xc9, 0x2b, 0xfe, 0x66, 0x1b, 0xfe, 0x8a, 0x10, 0x1c, 0x1a, 0x87, 0x8b, 0x0f,
+ 0xfe, 0x30, 0x90, 0x04, 0xfe, 0xb0, 0x93, 0x3a, 0x0b, 0xfe, 0x18, 0x58, 0xfe, 0x32, 0x90, 0x04,
+ 0xfe, 0xb2, 0x93, 0x3a, 0x0b, 0xfe, 0x19, 0x58, 0x0e, 0xa8, 0xb3, 0x4a, 0x7c, 0x12, 0xfe, 0x0f,
+ 0x79, 0xfe, 0x1c, 0xf7, 0x4a, 0xb1, 0xfe, 0xc6, 0x1b, 0xfe, 0x5e, 0x14, 0x31, 0x02, 0xc9, 0x2b,
+ 0xfe, 0x96, 0x1b, 0x5c, 0xfe, 0x02, 0xf6, 0x1a, 0x87, 0xfe, 0x18, 0xfe, 0x6a, 0xfe, 0x19, 0xfe,
+ 0x6b, 0x01, 0xfe, 0x1e, 0x1f, 0xfe, 0x1d, 0xf7, 0x65, 0xb1, 0xfe, 0xee, 0x1b, 0xfe, 0x36, 0x14,
+ 0xfe, 0x1c, 0x13, 0xb3, 0x65, 0x3e, 0xfe, 0x83, 0x58, 0xfe, 0xaf, 0x19, 0xfe, 0x80, 0xe7, 0x1a,
+ 0xfe, 0x81, 0xe7, 0x1a, 0x15, 0xfe, 0xdd, 0x00, 0x7a, 0x30, 0x02, 0x7a, 0x30, 0xfe, 0x12, 0x45,
+ 0x2b, 0xfe, 0xdc, 0x1b, 0x1f, 0x07, 0x47, 0xb5, 0xc3, 0x05, 0x35, 0xfe, 0x39, 0xf0, 0x75, 0x26,
+ 0x02, 0xfe, 0x7e, 0x18, 0x23, 0x1d, 0x36, 0x13, 0x11, 0x02, 0x87, 0x03, 0xe3, 0x23, 0x07, 0xfe,
+ 0xef, 0x12, 0xfe, 0xe1, 0x10, 0x90, 0x34, 0x60, 0xfe, 0x02, 0x80, 0x09, 0x56, 0xfe, 0x3c, 0x13,
+ 0xfe, 0x82, 0x14, 0xfe, 0x42, 0x13, 0x51, 0xfe, 0x06, 0x83, 0x0a, 0x5a, 0x01, 0x18, 0xcb, 0xfe,
+ 0x3e, 0x12, 0xfe, 0x41, 0x48, 0xfe, 0x45, 0x48, 0x01, 0xfe, 0xb2, 0x16, 0xfe, 0x00, 0xcc, 0xcb,
+ 0xfe, 0xf3, 0x13, 0x3f, 0x89, 0x09, 0x1a, 0xa5, 0x0a, 0x9d, 0x01, 0x18, 0xfe, 0x80, 0x4c, 0x01,
+ 0x85, 0xfe, 0x16, 0x10, 0x09, 0x9b, 0x4e, 0xfe, 0x40, 0x14, 0xfe, 0x24, 0x12, 0xfe, 0x14, 0x56,
+ 0xfe, 0xd6, 0xf0, 0xfe, 0x52, 0x1c, 0x1c, 0x0d, 0x02, 0xfe, 0x9c, 0xe7, 0x0d, 0x19, 0xfe, 0x15,
+ 0x00, 0x40, 0x8d, 0x30, 0x01, 0xf4, 0x1c, 0x07, 0x02, 0x51, 0xfe, 0x06, 0x83, 0xfe, 0x18, 0x80,
+ 0x61, 0x28, 0x44, 0x15, 0x56, 0x01, 0x85, 0x1c, 0x07, 0x02, 0xfe, 0x38, 0x90, 0xfe, 0xba, 0x90,
+ 0x91, 0xde, 0x7e, 0xdf, 0xfe, 0x48, 0x55, 0x31, 0xfe, 0xc9, 0x55, 0x02, 0x21, 0xb9, 0x88, 0x20,
+ 0xb9, 0x02, 0x0a, 0xba, 0x01, 0x18, 0xfe, 0x41, 0x48, 0x0a, 0x57, 0x01, 0x18, 0xfe, 0x49, 0x44,
+ 0x1b, 0xfe, 0x1e, 0x1d, 0x88, 0x89, 0x02, 0x0a, 0x5a, 0x01, 0x18, 0x09, 0x1a, 0xa4, 0x0a, 0x67,
+ 0x01, 0xa3, 0x0a, 0x57, 0x01, 0x18, 0x88, 0x89, 0x02, 0xfe, 0x4e, 0xe4, 0x1d, 0x7b, 0xfe, 0x52,
+ 0x1d, 0x03, 0xfe, 0x90, 0x00, 0xfe, 0x3a, 0x45, 0xfe, 0x2c, 0x10, 0xfe, 0x4e, 0xe4, 0xdd, 0x7b,
+ 0xfe, 0x64, 0x1d, 0x03, 0xfe, 0x92, 0x00, 0xd1, 0x12, 0xfe, 0x1a, 0x10, 0xfe, 0x4e, 0xe4, 0xfe,
+ 0x0b, 0x00, 0x7b, 0xfe, 0x76, 0x1d, 0x03, 0xfe, 0x94, 0x00, 0xd1, 0x24, 0xfe, 0x08, 0x10, 0x03,
+ 0xfe, 0x96, 0x00, 0xd1, 0x63, 0xfe, 0x4e, 0x45, 0x83, 0xca, 0xff, 0x04, 0x68, 0x54, 0xfe, 0xf1,
+ 0x10, 0x23, 0x49, 0xfe, 0x08, 0x1c, 0xfe, 0x67, 0x19, 0xfe, 0x0a, 0x1c, 0xfe, 0x1a, 0xf4, 0xfe,
+ 0x00, 0x04, 0x83, 0xb2, 0x1d, 0x48, 0xfe, 0xaa, 0x1d, 0x13, 0x1d, 0x02, 0x09, 0x92, 0xfe, 0x5a,
+ 0xf0, 0xfe, 0xba, 0x1d, 0x2e, 0x93, 0xfe, 0x34, 0x10, 0x09, 0x12, 0xfe, 0x5a, 0xf0, 0xfe, 0xc8,
+ 0x1d, 0x2e, 0xb4, 0xfe, 0x26, 0x10, 0x09, 0x1d, 0x36, 0x2e, 0x63, 0xfe, 0x1a, 0x10, 0x09, 0x0d,
+ 0x36, 0x2e, 0x94, 0xf2, 0x09, 0x07, 0x36, 0x2e, 0x95, 0xa1, 0xc8, 0x02, 0x1f, 0x93, 0x01, 0x42,
+ 0xfe, 0x04, 0xfe, 0x99, 0x03, 0x9c, 0x8b, 0x02, 0x2a, 0xfe, 0x1c, 0x1e, 0xfe, 0x14, 0xf0, 0x08,
+ 0x2f, 0xfe, 0x0c, 0x1e, 0x2a, 0xfe, 0x1c, 0x1e, 0x8f, 0xfe, 0x1c, 0x1e, 0xfe, 0x82, 0xf0, 0xfe,
+ 0x10, 0x1e, 0x02, 0x0f, 0x3f, 0x04, 0xfe, 0x80, 0x83, 0x33, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x18,
+ 0x80, 0x04, 0xfe, 0x98, 0x83, 0x33, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x02, 0x80, 0x04, 0xfe, 0x82,
+ 0x83, 0x33, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x06, 0x80, 0x04, 0xfe, 0x86, 0x83, 0x33, 0x0b, 0x0e,
+ 0x02, 0x0f, 0xfe, 0x1b, 0x80, 0x04, 0xfe, 0x9b, 0x83, 0x33, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x04,
+ 0x80, 0x04, 0xfe, 0x84, 0x83, 0x33, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x80, 0x80, 0x04, 0xfe, 0x80,
+ 0x83, 0xfe, 0xc9, 0x47, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x19, 0x81, 0x04, 0xfe, 0x99, 0x83, 0xfe,
+ 0xca, 0x47, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x06, 0x83, 0x04, 0xfe, 0x86, 0x83, 0xfe, 0xce, 0x47,
+ 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x2c, 0x90, 0x04, 0xfe, 0xac, 0x93, 0x3a, 0x0b, 0x0e, 0x02, 0x0f,
+ 0xfe, 0xae, 0x90, 0x04, 0xfe, 0xae, 0x93, 0x79, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x08, 0x90, 0x04,
+ 0xfe, 0x88, 0x93, 0x3a, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x8a, 0x90, 0x04, 0xfe, 0x8a, 0x93, 0x79,
+ 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x0c, 0x90, 0x04, 0xfe, 0x8c, 0x93, 0x3a, 0x0b, 0x0e, 0x02, 0x0f,
+ 0xfe, 0x8e, 0x90, 0x04, 0xfe, 0x8e, 0x93, 0x79, 0x0b, 0x0e, 0x02, 0x0f, 0xfe, 0x3c, 0x90, 0x04,
+ 0xfe, 0xbc, 0x93, 0x3a, 0x0b, 0x0e, 0x02, 0x8b, 0x0f, 0xfe, 0x03, 0x80, 0x04, 0xfe, 0x83, 0x83,
+ 0x33, 0x0b, 0x77, 0x0e, 0xa8, 0x02, 0xff, 0x66, 0x00, 0x00,
+};
+
+STATIC unsigned short _adv_asc38C1600_size =
+ sizeof(_adv_asc38C1600_buf); /* 0x1673 */
+STATIC ADV_DCNT _adv_asc38C1600_chksum =
+ 0x0604EF77UL; /* Expanded little-endian checksum. */
+
+/* a_init.c */
+/*
+ * EEPROM Configuration.
+ *
+ * All drivers should use this structure to set the default EEPROM
+ * configuration. The BIOS now uses this structure when it is built.
+ * Additional structure information can be found in a_condor.h where
+ * the structure is defined.
+ *
+ * The *_Field_IsChar structs are needed to correct for endianness.
+ * These values are read from the board 16 bits at a time directly
+ * into the structs. Because some fields are char, the values will be
+ * in the wrong order. The *_Field_IsChar tells when to flip the
+ * bytes. Data read and written to PCI memory is automatically swapped
+ * on big-endian platforms so char fields read as words are actually being
+ * unswapped on big-endian platforms.
+ */
+STATIC ADVEEP_3550_CONFIG
+Default_3550_EEPROM_Config __initdata = {
+ ADV_EEPROM_BIOS_ENABLE, /* cfg_lsw */
+ 0x0000, /* cfg_msw */
+ 0xFFFF, /* disc_enable */
+ 0xFFFF, /* wdtr_able */
+ 0xFFFF, /* sdtr_able */
+ 0xFFFF, /* start_motor */
+ 0xFFFF, /* tagqng_able */
+ 0xFFFF, /* bios_scan */
+ 0, /* scam_tolerant */
+ 7, /* adapter_scsi_id */
+ 0, /* bios_boot_delay */
+ 3, /* scsi_reset_delay */
+ 0, /* bios_id_lun */
+ 0, /* termination */
+ 0, /* reserved1 */
+ 0xFFE7, /* bios_ctrl */
+ 0xFFFF, /* ultra_able */
+ 0, /* reserved2 */
+ ASC_DEF_MAX_HOST_QNG, /* max_host_qng */
+ ASC_DEF_MAX_DVC_QNG, /* max_dvc_qng */
+ 0, /* dvc_cntl */
+ 0, /* bug_fix */
+ 0, /* serial_number_word1 */
+ 0, /* serial_number_word2 */
+ 0, /* serial_number_word3 */
+ 0, /* check_sum */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* oem_name[16] */
+ 0, /* dvc_err_code */
+ 0, /* adv_err_code */
+ 0, /* adv_err_addr */
+ 0, /* saved_dvc_err_code */
+ 0, /* saved_adv_err_code */
+ 0, /* saved_adv_err_addr */
+ 0 /* num_of_err */
+};
+
+STATIC ADVEEP_3550_CONFIG
+ADVEEP_3550_Config_Field_IsChar __initdata = {
+ 0, /* cfg_lsw */
+ 0, /* cfg_msw */
+ 0, /* -disc_enable */
+ 0, /* wdtr_able */
+ 0, /* sdtr_able */
+ 0, /* start_motor */
+ 0, /* tagqng_able */
+ 0, /* bios_scan */
+ 0, /* scam_tolerant */
+ 1, /* adapter_scsi_id */
+ 1, /* bios_boot_delay */
+ 1, /* scsi_reset_delay */
+ 1, /* bios_id_lun */
+ 1, /* termination */
+ 1, /* reserved1 */
+ 0, /* bios_ctrl */
+ 0, /* ultra_able */
+ 0, /* reserved2 */
+ 1, /* max_host_qng */
+ 1, /* max_dvc_qng */
+ 0, /* dvc_cntl */
+ 0, /* bug_fix */
+ 0, /* serial_number_word1 */
+ 0, /* serial_number_word2 */
+ 0, /* serial_number_word3 */
+ 0, /* check_sum */
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, /* oem_name[16] */
+ 0, /* dvc_err_code */
+ 0, /* adv_err_code */
+ 0, /* adv_err_addr */
+ 0, /* saved_dvc_err_code */
+ 0, /* saved_adv_err_code */
+ 0, /* saved_adv_err_addr */
+ 0 /* num_of_err */
+};
+
+STATIC ADVEEP_38C0800_CONFIG
+Default_38C0800_EEPROM_Config __initdata = {
+ ADV_EEPROM_BIOS_ENABLE, /* 00 cfg_lsw */
+ 0x0000, /* 01 cfg_msw */
+ 0xFFFF, /* 02 disc_enable */
+ 0xFFFF, /* 03 wdtr_able */
+ 0x4444, /* 04 sdtr_speed1 */
+ 0xFFFF, /* 05 start_motor */
+ 0xFFFF, /* 06 tagqng_able */
+ 0xFFFF, /* 07 bios_scan */
+ 0, /* 08 scam_tolerant */
+ 7, /* 09 adapter_scsi_id */
+ 0, /* bios_boot_delay */
+ 3, /* 10 scsi_reset_delay */
+ 0, /* bios_id_lun */
+ 0, /* 11 termination_se */
+ 0, /* termination_lvd */
+ 0xFFE7, /* 12 bios_ctrl */
+ 0x4444, /* 13 sdtr_speed2 */
+ 0x4444, /* 14 sdtr_speed3 */
+ ASC_DEF_MAX_HOST_QNG, /* 15 max_host_qng */
+ ASC_DEF_MAX_DVC_QNG, /* max_dvc_qng */
+ 0, /* 16 dvc_cntl */
+ 0x4444, /* 17 sdtr_speed4 */
+ 0, /* 18 serial_number_word1 */
+ 0, /* 19 serial_number_word2 */
+ 0, /* 20 serial_number_word3 */
+ 0, /* 21 check_sum */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* 22-29 oem_name[16] */
+ 0, /* 30 dvc_err_code */
+ 0, /* 31 adv_err_code */
+ 0, /* 32 adv_err_addr */
+ 0, /* 33 saved_dvc_err_code */
+ 0, /* 34 saved_adv_err_code */
+ 0, /* 35 saved_adv_err_addr */
+ 0, /* 36 reserved */
+ 0, /* 37 reserved */
+ 0, /* 38 reserved */
+ 0, /* 39 reserved */
+ 0, /* 40 reserved */
+ 0, /* 41 reserved */
+ 0, /* 42 reserved */
+ 0, /* 43 reserved */
+ 0, /* 44 reserved */
+ 0, /* 45 reserved */
+ 0, /* 46 reserved */
+ 0, /* 47 reserved */
+ 0, /* 48 reserved */
+ 0, /* 49 reserved */
+ 0, /* 50 reserved */
+ 0, /* 51 reserved */
+ 0, /* 52 reserved */
+ 0, /* 53 reserved */
+ 0, /* 54 reserved */
+ 0, /* 55 reserved */
+ 0, /* 56 cisptr_lsw */
+ 0, /* 57 cisprt_msw */
+ ADV_PCI_VENDOR_ID, /* 58 subsysvid */
+ ADV_PCI_DEVID_38C0800_REV1, /* 59 subsysid */
+ 0, /* 60 reserved */
+ 0, /* 61 reserved */
+ 0, /* 62 reserved */
+ 0 /* 63 reserved */
+};
+
+STATIC ADVEEP_38C0800_CONFIG
+ADVEEP_38C0800_Config_Field_IsChar __initdata = {
+ 0, /* 00 cfg_lsw */
+ 0, /* 01 cfg_msw */
+ 0, /* 02 disc_enable */
+ 0, /* 03 wdtr_able */
+ 0, /* 04 sdtr_speed1 */
+ 0, /* 05 start_motor */
+ 0, /* 06 tagqng_able */
+ 0, /* 07 bios_scan */
+ 0, /* 08 scam_tolerant */
+ 1, /* 09 adapter_scsi_id */
+ 1, /* bios_boot_delay */
+ 1, /* 10 scsi_reset_delay */
+ 1, /* bios_id_lun */
+ 1, /* 11 termination_se */
+ 1, /* termination_lvd */
+ 0, /* 12 bios_ctrl */
+ 0, /* 13 sdtr_speed2 */
+ 0, /* 14 sdtr_speed3 */
+ 1, /* 15 max_host_qng */
+ 1, /* max_dvc_qng */
+ 0, /* 16 dvc_cntl */
+ 0, /* 17 sdtr_speed4 */
+ 0, /* 18 serial_number_word1 */
+ 0, /* 19 serial_number_word2 */
+ 0, /* 20 serial_number_word3 */
+ 0, /* 21 check_sum */
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, /* 22-29 oem_name[16] */
+ 0, /* 30 dvc_err_code */
+ 0, /* 31 adv_err_code */
+ 0, /* 32 adv_err_addr */
+ 0, /* 33 saved_dvc_err_code */
+ 0, /* 34 saved_adv_err_code */
+ 0, /* 35 saved_adv_err_addr */
+ 0, /* 36 reserved */
+ 0, /* 37 reserved */
+ 0, /* 38 reserved */
+ 0, /* 39 reserved */
+ 0, /* 40 reserved */
+ 0, /* 41 reserved */
+ 0, /* 42 reserved */
+ 0, /* 43 reserved */
+ 0, /* 44 reserved */
+ 0, /* 45 reserved */
+ 0, /* 46 reserved */
+ 0, /* 47 reserved */
+ 0, /* 48 reserved */
+ 0, /* 49 reserved */
+ 0, /* 50 reserved */
+ 0, /* 51 reserved */
+ 0, /* 52 reserved */
+ 0, /* 53 reserved */
+ 0, /* 54 reserved */
+ 0, /* 55 reserved */
+ 0, /* 56 cisptr_lsw */
+ 0, /* 57 cisprt_msw */
+ 0, /* 58 subsysvid */
+ 0, /* 59 subsysid */
+ 0, /* 60 reserved */
+ 0, /* 61 reserved */
+ 0, /* 62 reserved */
+ 0 /* 63 reserved */
+};
+
+STATIC ADVEEP_38C1600_CONFIG
+Default_38C1600_EEPROM_Config __initdata = {
+ ADV_EEPROM_BIOS_ENABLE, /* 00 cfg_lsw */
+ 0x0000, /* 01 cfg_msw */
+ 0xFFFF, /* 02 disc_enable */
+ 0xFFFF, /* 03 wdtr_able */
+ 0x5555, /* 04 sdtr_speed1 */
+ 0xFFFF, /* 05 start_motor */
+ 0xFFFF, /* 06 tagqng_able */
+ 0xFFFF, /* 07 bios_scan */
+ 0, /* 08 scam_tolerant */
+ 7, /* 09 adapter_scsi_id */
+ 0, /* bios_boot_delay */
+ 3, /* 10 scsi_reset_delay */
+ 0, /* bios_id_lun */
+ 0, /* 11 termination_se */
+ 0, /* termination_lvd */
+ 0xFFE7, /* 12 bios_ctrl */
+ 0x5555, /* 13 sdtr_speed2 */
+ 0x5555, /* 14 sdtr_speed3 */
+ ASC_DEF_MAX_HOST_QNG, /* 15 max_host_qng */
+ ASC_DEF_MAX_DVC_QNG, /* max_dvc_qng */
+ 0, /* 16 dvc_cntl */
+ 0x5555, /* 17 sdtr_speed4 */
+ 0, /* 18 serial_number_word1 */
+ 0, /* 19 serial_number_word2 */
+ 0, /* 20 serial_number_word3 */
+ 0, /* 21 check_sum */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* 22-29 oem_name[16] */
+ 0, /* 30 dvc_err_code */
+ 0, /* 31 adv_err_code */
+ 0, /* 32 adv_err_addr */
+ 0, /* 33 saved_dvc_err_code */
+ 0, /* 34 saved_adv_err_code */
+ 0, /* 35 saved_adv_err_addr */
+ 0, /* 36 reserved */
+ 0, /* 37 reserved */
+ 0, /* 38 reserved */
+ 0, /* 39 reserved */
+ 0, /* 40 reserved */
+ 0, /* 41 reserved */
+ 0, /* 42 reserved */
+ 0, /* 43 reserved */
+ 0, /* 44 reserved */
+ 0, /* 45 reserved */
+ 0, /* 46 reserved */
+ 0, /* 47 reserved */
+ 0, /* 48 reserved */
+ 0, /* 49 reserved */
+ 0, /* 50 reserved */
+ 0, /* 51 reserved */
+ 0, /* 52 reserved */
+ 0, /* 53 reserved */
+ 0, /* 54 reserved */
+ 0, /* 55 reserved */
+ 0, /* 56 cisptr_lsw */
+ 0, /* 57 cisprt_msw */
+ ADV_PCI_VENDOR_ID, /* 58 subsysvid */
+ ADV_PCI_DEVID_38C1600_REV1, /* 59 subsysid */
+ 0, /* 60 reserved */
+ 0, /* 61 reserved */
+ 0, /* 62 reserved */
+ 0 /* 63 reserved */
+};
+
+STATIC ADVEEP_38C1600_CONFIG
+ADVEEP_38C1600_Config_Field_IsChar __initdata = {
+ 0, /* 00 cfg_lsw */
+ 0, /* 01 cfg_msw */
+ 0, /* 02 disc_enable */
+ 0, /* 03 wdtr_able */
+ 0, /* 04 sdtr_speed1 */
+ 0, /* 05 start_motor */
+ 0, /* 06 tagqng_able */
+ 0, /* 07 bios_scan */
+ 0, /* 08 scam_tolerant */
+ 1, /* 09 adapter_scsi_id */
+ 1, /* bios_boot_delay */
+ 1, /* 10 scsi_reset_delay */
+ 1, /* bios_id_lun */
+ 1, /* 11 termination_se */
+ 1, /* termination_lvd */
+ 0, /* 12 bios_ctrl */
+ 0, /* 13 sdtr_speed2 */
+ 0, /* 14 sdtr_speed3 */
+ 1, /* 15 max_host_qng */
+ 1, /* max_dvc_qng */
+ 0, /* 16 dvc_cntl */
+ 0, /* 17 sdtr_speed4 */
+ 0, /* 18 serial_number_word1 */
+ 0, /* 19 serial_number_word2 */
+ 0, /* 20 serial_number_word3 */
+ 0, /* 21 check_sum */
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, /* 22-29 oem_name[16] */
+ 0, /* 30 dvc_err_code */
+ 0, /* 31 adv_err_code */
+ 0, /* 32 adv_err_addr */
+ 0, /* 33 saved_dvc_err_code */
+ 0, /* 34 saved_adv_err_code */
+ 0, /* 35 saved_adv_err_addr */
+ 0, /* 36 reserved */
+ 0, /* 37 reserved */
+ 0, /* 38 reserved */
+ 0, /* 39 reserved */
+ 0, /* 40 reserved */
+ 0, /* 41 reserved */
+ 0, /* 42 reserved */
+ 0, /* 43 reserved */
+ 0, /* 44 reserved */
+ 0, /* 45 reserved */
+ 0, /* 46 reserved */
+ 0, /* 47 reserved */
+ 0, /* 48 reserved */
+ 0, /* 49 reserved */
+ 0, /* 50 reserved */
+ 0, /* 51 reserved */
+ 0, /* 52 reserved */
+ 0, /* 53 reserved */
+ 0, /* 54 reserved */
+ 0, /* 55 reserved */
+ 0, /* 56 cisptr_lsw */
+ 0, /* 57 cisprt_msw */
+ 0, /* 58 subsysvid */
+ 0, /* 59 subsysid */
+ 0, /* 60 reserved */
+ 0, /* 61 reserved */
+ 0, /* 62 reserved */
+ 0 /* 63 reserved */
+};
+
+/*
+ * Initialize the ADV_DVC_VAR structure.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ */
+STATIC int __init
+AdvInitGetConfig(ADV_DVC_VAR *asc_dvc)
+{
+ ushort warn_code;
+ AdvPortAddr iop_base;
+ uchar pci_cmd_reg;
+ int status;
+
+ warn_code = 0;
+ asc_dvc->err_code = 0;
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * PCI Command Register
+ *
+ * Note: AscPCICmdRegBits_BusMastering definition (0x0007) includes
+ * I/O Space Control, Memory Space Control and Bus Master Control bits.
+ */
+
+ if (((pci_cmd_reg = DvcAdvReadPCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister))
+ & AscPCICmdRegBits_BusMastering)
+ != AscPCICmdRegBits_BusMastering)
+ {
+ pci_cmd_reg |= AscPCICmdRegBits_BusMastering;
+
+ DvcAdvWritePCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister, pci_cmd_reg);
+
+ if (((DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigCommandRegister))
+ & AscPCICmdRegBits_BusMastering)
+ != AscPCICmdRegBits_BusMastering)
+ {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+
+ /*
+ * PCI Latency Timer
+ *
+ * If the "latency timer" register is 0x20 or above, then we don't need
+ * to change it. Otherwise, set it to 0x20 (i.e. set it to 0x20 if it
+ * comes up less than 0x20).
+ */
+ if (DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer) < 0x20) {
+ DvcAdvWritePCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer, 0x20);
+ if (DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer) < 0x20)
+ {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+
+ /*
+ * Save the state of the PCI Configuration Command Register
+ * "Parity Error Response Control" Bit. If the bit is clear (0),
+ * in AdvInitAsc3550/38C0800Driver() tell the microcode to ignore
+ * DMA parity errors.
+ */
+ asc_dvc->cfg->control_flag = 0;
+ if (((DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigCommandRegister)
+ & AscPCICmdRegBits_ParErrRespCtrl)) == 0)
+ {
+ asc_dvc->cfg->control_flag |= CONTROL_FLAG_IGNORE_PERR;
+ }
+
+ asc_dvc->cfg->lib_version = (ADV_LIB_VERSION_MAJOR << 8) |
+ ADV_LIB_VERSION_MINOR;
+ asc_dvc->cfg->chip_version =
+ AdvGetChipVersion(iop_base, asc_dvc->bus_type);
+
+ ASC_DBG2(1, "AdvInitGetConfig: iopb_chip_id_1: 0x%x 0x%x\n",
+ (ushort) AdvReadByteRegister(iop_base, IOPB_CHIP_ID_1),
+ (ushort) ADV_CHIP_ID_BYTE);
+
+ ASC_DBG2(1, "AdvInitGetConfig: iopw_chip_id_0: 0x%x 0x%x\n",
+ (ushort) AdvReadWordRegister(iop_base, IOPW_CHIP_ID_0),
+ (ushort) ADV_CHIP_ID_WORD);
+
+ /*
+ * Reset the chip to start and allow register writes.
+ */
+ if (AdvFindSignature(iop_base) == 0)
+ {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ return ADV_ERROR;
+ }
+ else {
+ /*
+ * The caller must set 'chip_type' to a valid setting.
+ */
+ if (asc_dvc->chip_type != ADV_CHIP_ASC3550 &&
+ asc_dvc->chip_type != ADV_CHIP_ASC38C0800 &&
+ asc_dvc->chip_type != ADV_CHIP_ASC38C1600)
+ {
+ asc_dvc->err_code |= ASC_IERR_BAD_CHIPTYPE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Reset Chip.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_CTRL_REG,
+ ADV_CTRL_REG_CMD_RESET);
+ DvcSleepMilliSecond(100);
+ AdvWriteWordRegister(iop_base, IOPW_CTRL_REG,
+ ADV_CTRL_REG_CMD_WR_IO_REG);
+
+ if (asc_dvc->chip_type == ADV_CHIP_ASC38C1600)
+ {
+ if ((status = AdvInitFrom38C1600EEP(asc_dvc)) == ADV_ERROR)
+ {
+ return ADV_ERROR;
+ }
+ } else if (asc_dvc->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ if ((status = AdvInitFrom38C0800EEP(asc_dvc)) == ADV_ERROR)
+ {
+ return ADV_ERROR;
+ }
+ } else
+ {
+ if ((status = AdvInitFrom3550EEP(asc_dvc)) == ADV_ERROR)
+ {
+ return ADV_ERROR;
+ }
+ }
+ warn_code |= status;
+ }
+
+ return warn_code;
+}
+
+/*
+ * Initialize the ASC-3550.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Needed after initialization for error recovery.
+ */
+STATIC int
+AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADV_DCNT sum;
+ int begin_addr;
+ int end_addr;
+ ushort code_sum;
+ int word;
+ int j;
+ int adv_asc3550_expanded_size;
+ ADV_CARR_T *carrp;
+ ADV_DCNT contig_len;
+ ADV_SDCNT buf_size;
+ ADV_PADDR carr_paddr;
+ int i;
+ ushort scsi_cfg1;
+ uchar tid;
+ ushort bios_mem[ASC_MC_BIOSLEN/2]; /* BIOS RISC Memory 0x40-0x8F. */
+ ushort wdtr_able = 0, sdtr_able, tagqng_able;
+ uchar max_cmd[ADV_MAX_TID + 1];
+
+ /* If there is already an error, don't continue. */
+ if (asc_dvc->err_code != 0)
+ {
+ return ADV_ERROR;
+ }
+
+ /*
+ * The caller must set 'chip_type' to ADV_CHIP_ASC3550.
+ */
+ if (asc_dvc->chip_type != ADV_CHIP_ASC3550)
+ {
+ asc_dvc->err_code |= ASC_IERR_BAD_CHIPTYPE;
+ return ADV_ERROR;
+ }
+
+ warn_code = 0;
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Save the RISC memory BIOS region before writing the microcode.
+ * The BIOS may already be loaded and using its RISC LRAM region
+ * so its region must be saved and restored.
+ *
+ * Note: This code makes the assumption, which is currently true,
+ * that a chip reset does not clear RISC LRAM.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN/2; i++)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_BIOSMEM + (2 * i), bios_mem[i]);
+ }
+
+ /*
+ * Save current per TID negotiated values.
+ */
+ if (bios_mem[(ASC_MC_BIOS_SIGNATURE - ASC_MC_BIOSMEM)/2] == 0x55AA)
+ {
+ ushort bios_version, major, minor;
+
+ bios_version = bios_mem[(ASC_MC_BIOS_VERSION - ASC_MC_BIOSMEM)/2];
+ major = (bios_version >> 12) & 0xF;
+ minor = (bios_version >> 8) & 0xF;
+ if (major < 3 || (major == 3 && minor == 1))
+ {
+ /* BIOS 3.1 and earlier location of 'wdtr_able' variable. */
+ AdvReadWordLram(iop_base, 0x120, wdtr_able);
+ } else
+ {
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ }
+ }
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+
+ /*
+ * Load the Microcode
+ *
+ * Write the microcode image to RISC memory starting at address 0.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+ /* Assume the following compressed format of the microcode buffer:
+ *
+ * 254 word (508 byte) table indexed by byte code followed
+ * by the following byte codes:
+ *
+ * 1-Byte Code:
+ * 00: Emit word 0 in table.
+ * 01: Emit word 1 in table.
+ * .
+ * FD: Emit word 253 in table.
+ *
+ * Multi-Byte Code:
+ * FE WW WW: (3 byte code) Word to emit is the next word WW WW.
+ * FF BB WW WW: (4 byte code) Emit BB count times next word WW WW.
+ */
+ word = 0;
+ for (i = 253 * 2; i < _adv_asc3550_size; i++)
+ {
+ if (_adv_asc3550_buf[i] == 0xff)
+ {
+ for (j = 0; j < _adv_asc3550_buf[i + 1]; j++)
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc3550_buf[i + 3] << 8) |
+ _adv_asc3550_buf[i + 2]));
+ word++;
+ }
+ i += 3;
+ } else if (_adv_asc3550_buf[i] == 0xfe)
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc3550_buf[i + 2] << 8) |
+ _adv_asc3550_buf[i + 1]));
+ i += 2;
+ word++;
+ } else
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc3550_buf[(_adv_asc3550_buf[i] * 2) + 1] << 8) |
+ _adv_asc3550_buf[_adv_asc3550_buf[i] * 2]));
+ word++;
+ }
+ }
+
+ /*
+ * Set 'word' for later use to clear the rest of memory and save
+ * the expanded mcode size.
+ */
+ word *= 2;
+ adv_asc3550_expanded_size = word;
+
+ /*
+ * Clear the rest of ASC-3550 Internal RAM (8KB).
+ */
+ for (; word < ADV_3550_MEMSIZE; word += 2)
+ {
+ AdvWriteWordAutoIncLram(iop_base, 0);
+ }
+
+ /*
+ * Verify the microcode checksum.
+ */
+ sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+
+ for (word = 0; word < adv_asc3550_expanded_size; word += 2)
+ {
+ sum += AdvReadWordAutoIncLram(iop_base);
+ }
+
+ if (sum != _adv_asc3550_chksum)
+ {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Restore the RISC memory BIOS region.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN/2; i++)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_BIOSMEM + (2 * i), bios_mem[i]);
+ }
+
+ /*
+ * Calculate and write the microcode code checksum to the microcode
+ * code checksum location ASC_MC_CODE_CHK_SUM (0x2C).
+ */
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, begin_addr);
+ AdvReadWordLram(iop_base, ASC_MC_CODE_END_ADDR, end_addr);
+ code_sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, begin_addr);
+ for (word = begin_addr; word < end_addr; word += 2)
+ {
+ code_sum += AdvReadWordAutoIncLram(iop_base);
+ }
+ AdvWriteWordLram(iop_base, ASC_MC_CODE_CHK_SUM, code_sum);
+
+ /*
+ * Read and save microcode version and date.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_DATE, asc_dvc->cfg->mcode_date);
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_NUM, asc_dvc->cfg->mcode_version);
+
+ /*
+ * Set the chip type to indicate the ASC3550.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_CHIP_TYPE, ADV_CHIP_ASC3550);
+
+ /*
+ * If the PCI Configuration Command Register "Parity Error Response
+ * Control" Bit was clear (0), then set the microcode variable
+ * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode
+ * to ignore DMA parity errors.
+ */
+ if (asc_dvc->cfg->control_flag & CONTROL_FLAG_IGNORE_PERR)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ word |= CONTROL_FLAG_IGNORE_PERR;
+ AdvWriteWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ }
+
+ /*
+ * For ASC-3550, setting the START_CTL_EMFU [3:2] bits sets a FIFO
+ * threshold of 128 bytes. This register is only accessible to the host.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_DMA_CFG0,
+ START_CTL_EMFU | READ_CMD_MRM);
+
+ /*
+ * Microcode operating variables for WDTR, SDTR, and command tag
+ * queuing will be set in AdvInquiryHandling() based on what a
+ * device reports it is capable of in Inquiry byte 7.
+ *
+ * If SCSI Bus Resets have been disabled, then directly set
+ * SDTR and WDTR from the EEPROM configuration. This will allow
+ * the BIOS and warm boot to work without a SCSI bus hang on
+ * the Inquiry caused by host and target mismatched DTR values.
+ * Without the SCSI Bus Reset, before an Inquiry a device can't
+ * be assumed to be in Asynchronous, Narrow mode.
+ */
+ if ((asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS) == 0)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, asc_dvc->wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, asc_dvc->sdtr_able);
+ }
+
+ /*
+ * Set microcode operating variables for SDTR_SPEED1, SDTR_SPEED2,
+ * SDTR_SPEED3, and SDTR_SPEED4 based on the ULTRA EEPROM per TID
+ * bitmask. These values determine the maximum SDTR speed negotiated
+ * with a device.
+ *
+ * The SDTR per TID bitmask overrides the SDTR_SPEED1, SDTR_SPEED2,
+ * SDTR_SPEED3, and SDTR_SPEED4 values so it is safe to set them
+ * without determining here whether the device supports SDTR.
+ *
+ * 4-bit speed SDTR speed name
+ * =========== ===============
+ * 0000b (0x0) SDTR disabled
+ * 0001b (0x1) 5 Mhz
+ * 0010b (0x2) 10 Mhz
+ * 0011b (0x3) 20 Mhz (Ultra)
+ * 0100b (0x4) 40 Mhz (LVD/Ultra2)
+ * 0101b (0x5) 80 Mhz (LVD2/Ultra3)
+ * 0110b (0x6) Undefined
+ * .
+ * 1111b (0xF) Undefined
+ */
+ word = 0;
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ if (ADV_TID_TO_TIDMASK(tid) & asc_dvc->ultra_able)
+ {
+ /* Set Ultra speed for TID 'tid'. */
+ word |= (0x3 << (4 * (tid % 4)));
+ } else
+ {
+ /* Set Fast speed for TID 'tid'. */
+ word |= (0x2 << (4 * (tid % 4)));
+ }
+ if (tid == 3) /* Check if done with sdtr_speed1. */
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED1, word);
+ word = 0;
+ } else if (tid == 7) /* Check if done with sdtr_speed2. */
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED2, word);
+ word = 0;
+ } else if (tid == 11) /* Check if done with sdtr_speed3. */
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED3, word);
+ word = 0;
+ } else if (tid == 15) /* Check if done with sdtr_speed4. */
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED4, word);
+ /* End of loop. */
+ }
+ }
+
+ /*
+ * Set microcode operating variable for the disconnect per TID bitmask.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DISC_ENABLE, asc_dvc->cfg->disc_enable);
+
+ /*
+ * Set SCSI_CFG0 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG0 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG0,
+ PARITY_EN | QUEUE_128 | SEL_TMO_LONG | OUR_ID_EN |
+ asc_dvc->chip_scsi_id);
+
+ /*
+ * Determine SCSI_CFG1 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+
+ /* Read current SCSI_CFG1 Register value. */
+ scsi_cfg1 = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+
+ /*
+ * If all three connectors are in use, return an error.
+ */
+ if ((scsi_cfg1 & CABLE_ILLEGAL_A) == 0 ||
+ (scsi_cfg1 & CABLE_ILLEGAL_B) == 0)
+ {
+ asc_dvc->err_code |= ASC_IERR_ILLEGAL_CONNECTION;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If the internal narrow cable is reversed all of the SCSI_CTRL
+ * register signals will be set. Check for and return an error if
+ * this condition is found.
+ */
+ if ((AdvReadWordRegister(iop_base, IOPW_SCSI_CTRL) & 0x3F07) == 0x3F07)
+ {
+ asc_dvc->err_code |= ASC_IERR_REVERSED_CABLE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If this is a differential board and a single-ended device
+ * is attached to one of the connectors, return an error.
+ */
+ if ((scsi_cfg1 & DIFF_MODE) && (scsi_cfg1 & DIFF_SENSE) == 0)
+ {
+ asc_dvc->err_code |= ASC_IERR_SINGLE_END_DEVICE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If automatic termination control is enabled, then set the
+ * termination value based on a table listed in a_condor.h.
+ *
+ * If manual termination was specified with an EEPROM setting
+ * then 'termination' was set-up in AdvInitFrom3550EEPROM() and
+ * is ready to be 'ored' into SCSI_CFG1.
+ */
+ if (asc_dvc->cfg->termination == 0)
+ {
+ /*
+ * The software always controls termination by setting TERM_CTL_SEL.
+ * If TERM_CTL_SEL were set to 0, the hardware would set termination.
+ */
+ asc_dvc->cfg->termination |= TERM_CTL_SEL;
+
+ switch(scsi_cfg1 & CABLE_DETECT)
+ {
+ /* TERM_CTL_H: on, TERM_CTL_L: on */
+ case 0x3: case 0x7: case 0xB: case 0xD: case 0xE: case 0xF:
+ asc_dvc->cfg->termination |= (TERM_CTL_H | TERM_CTL_L);
+ break;
+
+ /* TERM_CTL_H: on, TERM_CTL_L: off */
+ case 0x1: case 0x5: case 0x9: case 0xA: case 0xC:
+ asc_dvc->cfg->termination |= TERM_CTL_H;
+ break;
+
+ /* TERM_CTL_H: off, TERM_CTL_L: off */
+ case 0x2: case 0x6:
+ break;
+ }
+ }
+
+ /*
+ * Clear any set TERM_CTL_H and TERM_CTL_L bits.
+ */
+ scsi_cfg1 &= ~TERM_CTL;
+
+ /*
+ * Invert the TERM_CTL_H and TERM_CTL_L bits and then
+ * set 'scsi_cfg1'. The TERM_POL bit does not need to be
+ * referenced, because the hardware internally inverts
+ * the Termination High and Low bits if TERM_POL is set.
+ */
+ scsi_cfg1 |= (TERM_CTL_SEL | (~asc_dvc->cfg->termination & TERM_CTL));
+
+ /*
+ * Set SCSI_CFG1 Microcode Default Value
+ *
+ * Set filter value and possibly modified termination control
+ * bits in the Microcode SCSI_CFG1 Register Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG1,
+ FLTR_DISABLE | scsi_cfg1);
+
+ /*
+ * Set MEM_CFG Microcode Default Value
+ *
+ * The microcode will set the MEM_CFG register using this value
+ * after it is started below.
+ *
+ * MEM_CFG may be accessed as a word or byte, but only bits 0-7
+ * are defined.
+ *
+ * ASC-3550 has 8KB internal memory.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_MEM_CFG,
+ BIOS_EN | RAM_SZ_8KB);
+
+ /*
+ * Set SEL_MASK Microcode Default Value
+ *
+ * The microcode will set the SEL_MASK register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SEL_MASK,
+ ADV_TID_TO_TIDMASK(asc_dvc->chip_scsi_id));
+
+ /*
+ * Build carrier freelist.
+ *
+ * Driver must have already allocated memory and set 'carrier_buf'.
+ */
+ ASC_ASSERT(asc_dvc->carrier_buf != NULL);
+
+ carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf);
+ asc_dvc->carr_freelist = NULL;
+ if (carrp == (ADV_CARR_T *) asc_dvc->carrier_buf)
+ {
+ buf_size = ADV_CARRIER_BUFSIZE;
+ } else
+ {
+ buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T);
+ }
+
+ do {
+ /*
+ * Get physical address of the carrier 'carrp'.
+ */
+ contig_len = sizeof(ADV_CARR_T);
+ carr_paddr = cpu_to_le32(DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp,
+ (ADV_SDCNT *) &contig_len, ADV_IS_CARRIER_FLAG));
+
+ buf_size -= sizeof(ADV_CARR_T);
+
+ /*
+ * If the current carrier is not physically contiguous, then
+ * maybe there was a page crossing. Try the next carrier aligned
+ * start address.
+ */
+ if (contig_len < sizeof(ADV_CARR_T))
+ {
+ carrp++;
+ continue;
+ }
+
+ carrp->carr_pa = carr_paddr;
+ carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp));
+
+ /*
+ * Insert the carrier at the beginning of the freelist.
+ */
+ carrp->next_vpa = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+ asc_dvc->carr_freelist = carrp;
+
+ carrp++;
+ }
+ while (buf_size > 0);
+
+ /*
+ * Set-up the Host->RISC Initiator Command Queue (ICQ).
+ */
+
+ if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL)
+ {
+ asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
+ return ADV_ERROR;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
+
+ /*
+ * The first command issued will be placed in the stopper carrier.
+ */
+ asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Set RISC ICQ physical address start value.
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_ICQ, asc_dvc->icq_sp->carr_pa);
+
+ /*
+ * Set-up the RISC->Host Initiator Response Queue (IRQ).
+ */
+ if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL)
+ {
+ asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
+ return ADV_ERROR;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
+
+ /*
+ * The first command completed by the RISC will be placed in
+ * the stopper.
+ *
+ * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
+ * completed the RISC will set the ASC_RQ_STOPPER bit.
+ */
+ asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Set RISC IRQ physical address start value.
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_IRQ, asc_dvc->irq_sp->carr_pa);
+ asc_dvc->carr_pending_cnt = 0;
+
+ AdvWriteByteRegister(iop_base, IOPB_INTR_ENABLES,
+ (ADV_INTR_ENABLE_HOST_INTR | ADV_INTR_ENABLE_GLOBAL_INTR));
+
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, word);
+ AdvWriteWordRegister(iop_base, IOPW_PC, word);
+
+ /* finally, finally, gentlemen, start your engine */
+ AdvWriteWordRegister(iop_base, IOPW_RISC_CSR, ADV_RISC_CSR_RUN);
+
+ /*
+ * Reset the SCSI Bus if the EEPROM indicates that SCSI Bus
+ * Resets should be performed. The RISC has to be running
+ * to issue a SCSI Bus Reset.
+ */
+ if (asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS)
+ {
+ /*
+ * If the BIOS Signature is present in memory, restore the
+ * BIOS Handshake Configuration Table and do not perform
+ * a SCSI Bus Reset.
+ */
+ if (bios_mem[(ASC_MC_BIOS_SIGNATURE - ASC_MC_BIOSMEM)/2] == 0x55AA)
+ {
+ /*
+ * Restore per TID negotiated values.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ AdvWriteByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+ } else
+ {
+ if (AdvResetSB(asc_dvc) != ADV_TRUE)
+ {
+ warn_code = ASC_WARN_BUSRESET_ERROR;
+ }
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Initialize the ASC-38C0800.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Needed after initialization for error recovery.
+ */
+STATIC int
+AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADV_DCNT sum;
+ int begin_addr;
+ int end_addr;
+ ushort code_sum;
+ int word;
+ int j;
+ int adv_asc38C0800_expanded_size;
+ ADV_CARR_T *carrp;
+ ADV_DCNT contig_len;
+ ADV_SDCNT buf_size;
+ ADV_PADDR carr_paddr;
+ int i;
+ ushort scsi_cfg1;
+ uchar byte;
+ uchar tid;
+ ushort bios_mem[ASC_MC_BIOSLEN/2]; /* BIOS RISC Memory 0x40-0x8F. */
+ ushort wdtr_able, sdtr_able, tagqng_able;
+ uchar max_cmd[ADV_MAX_TID + 1];
+
+ /* If there is already an error, don't continue. */
+ if (asc_dvc->err_code != 0)
+ {
+ return ADV_ERROR;
+ }
+
+ /*
+ * The caller must set 'chip_type' to ADV_CHIP_ASC38C0800.
+ */
+ if (asc_dvc->chip_type != ADV_CHIP_ASC38C0800)
+ {
+ asc_dvc->err_code = ASC_IERR_BAD_CHIPTYPE;
+ return ADV_ERROR;
+ }
+
+ warn_code = 0;
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Save the RISC memory BIOS region before writing the microcode.
+ * The BIOS may already be loaded and using its RISC LRAM region
+ * so its region must be saved and restored.
+ *
+ * Note: This code makes the assumption, which is currently true,
+ * that a chip reset does not clear RISC LRAM.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN/2; i++)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_BIOSMEM + (2 * i), bios_mem[i]);
+ }
+
+ /*
+ * Save current per TID negotiated values.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+
+ /*
+ * RAM BIST (RAM Built-In Self Test)
+ *
+ * Address : I/O base + offset 0x38h register (byte).
+ * Function: Bit 7-6(RW) : RAM mode
+ * Normal Mode : 0x00
+ * Pre-test Mode : 0x40
+ * RAM Test Mode : 0x80
+ * Bit 5 : unused
+ * Bit 4(RO) : Done bit
+ * Bit 3-0(RO) : Status
+ * Host Error : 0x08
+ * Int_RAM Error : 0x04
+ * RISC Error : 0x02
+ * SCSI Error : 0x01
+ * No Error : 0x00
+ *
+ * Note: RAM BIST code should be put right here, before loading the
+ * microcode and after saving the RISC memory BIOS region.
+ */
+
+ /*
+ * LRAM Pre-test
+ *
+ * Write PRE_TEST_MODE (0x40) to register and wait for 10 milliseconds.
+ * If Done bit not set or low nibble not PRE_TEST_VALUE (0x05), return
+ * an error. Reset to NORMAL_MODE (0x00) and do again. If cannot reset
+ * to NORMAL_MODE, return an error too.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, PRE_TEST_MODE);
+ DvcSleepMilliSecond(10); /* Wait for 10ms before reading back. */
+ byte = AdvReadByteRegister(iop_base, IOPB_RAM_BIST);
+ if ((byte & RAM_TEST_DONE) == 0 || (byte & 0x0F) != PRE_TEST_VALUE)
+ {
+ asc_dvc->err_code |= ASC_IERR_BIST_PRE_TEST;
+ return ADV_ERROR;
+ }
+
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, NORMAL_MODE);
+ DvcSleepMilliSecond(10); /* Wait for 10ms before reading back. */
+ if (AdvReadByteRegister(iop_base, IOPB_RAM_BIST)
+ != NORMAL_VALUE)
+ {
+ asc_dvc->err_code |= ASC_IERR_BIST_PRE_TEST;
+ return ADV_ERROR;
+ }
+ }
+
+ /*
+ * LRAM Test - It takes about 1.5 ms to run through the test.
+ *
+ * Write RAM_TEST_MODE (0x80) to register and wait for 10 milliseconds.
+ * If Done bit not set or Status not 0, save register byte, set the
+ * err_code, and return an error.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, RAM_TEST_MODE);
+ DvcSleepMilliSecond(10); /* Wait for 10ms before checking status. */
+
+ byte = AdvReadByteRegister(iop_base, IOPB_RAM_BIST);
+ if ((byte & RAM_TEST_DONE) == 0 || (byte & RAM_TEST_STATUS) != 0)
+ {
+ /* Get here if Done bit not set or Status not 0. */
+ asc_dvc->bist_err_code = byte; /* for BIOS display message */
+ asc_dvc->err_code |= ASC_IERR_BIST_RAM_TEST;
+ return ADV_ERROR;
+ }
+
+ /* We need to reset back to normal mode after LRAM test passes. */
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, NORMAL_MODE);
+
+ /*
+ * Load the Microcode
+ *
+ * Write the microcode image to RISC memory starting at address 0.
+ *
+ */
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+
+ /* Assume the following compressed format of the microcode buffer:
+ *
+ * 254 word (508 byte) table indexed by byte code followed
+ * by the following byte codes:
+ *
+ * 1-Byte Code:
+ * 00: Emit word 0 in table.
+ * 01: Emit word 1 in table.
+ * .
+ * FD: Emit word 253 in table.
+ *
+ * Multi-Byte Code:
+ * FE WW WW: (3 byte code) Word to emit is the next word WW WW.
+ * FF BB WW WW: (4 byte code) Emit BB count times next word WW WW.
+ */
+ word = 0;
+ for (i = 253 * 2; i < _adv_asc38C0800_size; i++)
+ {
+ if (_adv_asc38C0800_buf[i] == 0xff)
+ {
+ for (j = 0; j < _adv_asc38C0800_buf[i + 1]; j++)
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc38C0800_buf[i + 3] << 8) |
+ _adv_asc38C0800_buf[i + 2]));
+ word++;
+ }
+ i += 3;
+ } else if (_adv_asc38C0800_buf[i] == 0xfe)
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc38C0800_buf[i + 2] << 8) |
+ _adv_asc38C0800_buf[i + 1]));
+ i += 2;
+ word++;
+ } else
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc38C0800_buf[(_adv_asc38C0800_buf[i] * 2) + 1] << 8) |
+ _adv_asc38C0800_buf[_adv_asc38C0800_buf[i] * 2]));
+ word++;
+ }
+ }
+
+ /*
+ * Set 'word' for later use to clear the rest of memory and save
+ * the expanded mcode size.
+ */
+ word *= 2;
+ adv_asc38C0800_expanded_size = word;
+
+ /*
+ * Clear the rest of ASC-38C0800 Internal RAM (16KB).
+ */
+ for (; word < ADV_38C0800_MEMSIZE; word += 2)
+ {
+ AdvWriteWordAutoIncLram(iop_base, 0);
+ }
+
+ /*
+ * Verify the microcode checksum.
+ */
+ sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+
+ for (word = 0; word < adv_asc38C0800_expanded_size; word += 2)
+ {
+ sum += AdvReadWordAutoIncLram(iop_base);
+ }
+ ASC_DBG2(1, "AdvInitAsc38C0800Driver: word %d, i %d\n", word, i);
+
+ ASC_DBG2(1,
+ "AdvInitAsc38C0800Driver: sum 0x%lx, _adv_asc38C0800_chksum 0x%lx\n",
+ (ulong) sum, (ulong) _adv_asc38C0800_chksum);
+
+ if (sum != _adv_asc38C0800_chksum)
+ {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Restore the RISC memory BIOS region.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN/2; i++)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_BIOSMEM + (2 * i), bios_mem[i]);
+ }
+
+ /*
+ * Calculate and write the microcode code checksum to the microcode
+ * code checksum location ASC_MC_CODE_CHK_SUM (0x2C).
+ */
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, begin_addr);
+ AdvReadWordLram(iop_base, ASC_MC_CODE_END_ADDR, end_addr);
+ code_sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, begin_addr);
+ for (word = begin_addr; word < end_addr; word += 2)
+ {
+ code_sum += AdvReadWordAutoIncLram(iop_base);
+ }
+ AdvWriteWordLram(iop_base, ASC_MC_CODE_CHK_SUM, code_sum);
+
+ /*
+ * Read microcode version and date.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_DATE, asc_dvc->cfg->mcode_date);
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_NUM, asc_dvc->cfg->mcode_version);
+
+ /*
+ * Set the chip type to indicate the ASC38C0800.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_CHIP_TYPE, ADV_CHIP_ASC38C0800);
+
+ /*
+ * Write 1 to bit 14 'DIS_TERM_DRV' in the SCSI_CFG1 register.
+ * When DIS_TERM_DRV set to 1, C_DET[3:0] will reflect current
+ * cable detection and then we are able to read C_DET[3:0].
+ *
+ * Note: We will reset DIS_TERM_DRV to 0 in the 'Set SCSI_CFG1
+ * Microcode Default Value' section below.
+ */
+ scsi_cfg1 = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+ AdvWriteWordRegister(iop_base, IOPW_SCSI_CFG1, scsi_cfg1 | DIS_TERM_DRV);
+
+ /*
+ * If the PCI Configuration Command Register "Parity Error Response
+ * Control" Bit was clear (0), then set the microcode variable
+ * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode
+ * to ignore DMA parity errors.
+ */
+ if (asc_dvc->cfg->control_flag & CONTROL_FLAG_IGNORE_PERR)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ word |= CONTROL_FLAG_IGNORE_PERR;
+ AdvWriteWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ }
+
+ /*
+ * For ASC-38C0800, set FIFO_THRESH_80B [6:4] bits and START_CTL_TH [3:2]
+ * bits for the default FIFO threshold.
+ *
+ * Note: ASC-38C0800 FIFO threshold has been changed to 256 bytes.
+ *
+ * For DMA Errata #4 set the BC_THRESH_ENB bit.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_DMA_CFG0,
+ BC_THRESH_ENB | FIFO_THRESH_80B | START_CTL_TH | READ_CMD_MRM);
+
+ /*
+ * Microcode operating variables for WDTR, SDTR, and command tag
+ * queuing will be set in AdvInquiryHandling() based on what a
+ * device reports it is capable of in Inquiry byte 7.
+ *
+ * If SCSI Bus Resets have been disabled, then directly set
+ * SDTR and WDTR from the EEPROM configuration. This will allow
+ * the BIOS and warm boot to work without a SCSI bus hang on
+ * the Inquiry caused by host and target mismatched DTR values.
+ * Without the SCSI Bus Reset, before an Inquiry a device can't
+ * be assumed to be in Asynchronous, Narrow mode.
+ */
+ if ((asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS) == 0)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, asc_dvc->wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, asc_dvc->sdtr_able);
+ }
+
+ /*
+ * Set microcode operating variables for DISC and SDTR_SPEED1,
+ * SDTR_SPEED2, SDTR_SPEED3, and SDTR_SPEED4 based on the EEPROM
+ * configuration values.
+ *
+ * The SDTR per TID bitmask overrides the SDTR_SPEED1, SDTR_SPEED2,
+ * SDTR_SPEED3, and SDTR_SPEED4 values so it is safe to set them
+ * without determining here whether the device supports SDTR.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DISC_ENABLE, asc_dvc->cfg->disc_enable);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED1, asc_dvc->sdtr_speed1);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED2, asc_dvc->sdtr_speed2);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED3, asc_dvc->sdtr_speed3);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED4, asc_dvc->sdtr_speed4);
+
+ /*
+ * Set SCSI_CFG0 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG0 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG0,
+ PARITY_EN | QUEUE_128 | SEL_TMO_LONG | OUR_ID_EN |
+ asc_dvc->chip_scsi_id);
+
+ /*
+ * Determine SCSI_CFG1 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+
+ /* Read current SCSI_CFG1 Register value. */
+ scsi_cfg1 = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+
+ /*
+ * If the internal narrow cable is reversed all of the SCSI_CTRL
+ * register signals will be set. Check for and return an error if
+ * this condition is found.
+ */
+ if ((AdvReadWordRegister(iop_base, IOPW_SCSI_CTRL) & 0x3F07) == 0x3F07)
+ {
+ asc_dvc->err_code |= ASC_IERR_REVERSED_CABLE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * All kind of combinations of devices attached to one of four connectors
+ * are acceptable except HVD device attached. For example, LVD device can
+ * be attached to SE connector while SE device attached to LVD connector.
+ * If LVD device attached to SE connector, it only runs up to Ultra speed.
+ *
+ * If an HVD device is attached to one of LVD connectors, return an error.
+ * However, there is no way to detect HVD device attached to SE connectors.
+ */
+ if (scsi_cfg1 & HVD)
+ {
+ asc_dvc->err_code |= ASC_IERR_HVD_DEVICE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If either SE or LVD automatic termination control is enabled, then
+ * set the termination value based on a table listed in a_condor.h.
+ *
+ * If manual termination was specified with an EEPROM setting then
+ * 'termination' was set-up in AdvInitFrom38C0800EEPROM() and is ready to
+ * be 'ored' into SCSI_CFG1.
+ */
+ if ((asc_dvc->cfg->termination & TERM_SE) == 0)
+ {
+ /* SE automatic termination control is enabled. */
+ switch(scsi_cfg1 & C_DET_SE)
+ {
+ /* TERM_SE_HI: on, TERM_SE_LO: on */
+ case 0x1: case 0x2: case 0x3:
+ asc_dvc->cfg->termination |= TERM_SE;
+ break;
+
+ /* TERM_SE_HI: on, TERM_SE_LO: off */
+ case 0x0:
+ asc_dvc->cfg->termination |= TERM_SE_HI;
+ break;
+ }
+ }
+
+ if ((asc_dvc->cfg->termination & TERM_LVD) == 0)
+ {
+ /* LVD automatic termination control is enabled. */
+ switch(scsi_cfg1 & C_DET_LVD)
+ {
+ /* TERM_LVD_HI: on, TERM_LVD_LO: on */
+ case 0x4: case 0x8: case 0xC:
+ asc_dvc->cfg->termination |= TERM_LVD;
+ break;
+
+ /* TERM_LVD_HI: off, TERM_LVD_LO: off */
+ case 0x0:
+ break;
+ }
+ }
+
+ /*
+ * Clear any set TERM_SE and TERM_LVD bits.
+ */
+ scsi_cfg1 &= (~TERM_SE & ~TERM_LVD);
+
+ /*
+ * Invert the TERM_SE and TERM_LVD bits and then set 'scsi_cfg1'.
+ */
+ scsi_cfg1 |= (~asc_dvc->cfg->termination & 0xF0);
+
+ /*
+ * Clear BIG_ENDIAN, DIS_TERM_DRV, Terminator Polarity and HVD/LVD/SE bits
+ * and set possibly modified termination control bits in the Microcode
+ * SCSI_CFG1 Register Value.
+ */
+ scsi_cfg1 &= (~BIG_ENDIAN & ~DIS_TERM_DRV & ~TERM_POL & ~HVD_LVD_SE);
+
+ /*
+ * Set SCSI_CFG1 Microcode Default Value
+ *
+ * Set possibly modified termination control and reset DIS_TERM_DRV
+ * bits in the Microcode SCSI_CFG1 Register Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG1, scsi_cfg1);
+
+ /*
+ * Set MEM_CFG Microcode Default Value
+ *
+ * The microcode will set the MEM_CFG register using this value
+ * after it is started below.
+ *
+ * MEM_CFG may be accessed as a word or byte, but only bits 0-7
+ * are defined.
+ *
+ * ASC-38C0800 has 16KB internal memory.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_MEM_CFG,
+ BIOS_EN | RAM_SZ_16KB);
+
+ /*
+ * Set SEL_MASK Microcode Default Value
+ *
+ * The microcode will set the SEL_MASK register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SEL_MASK,
+ ADV_TID_TO_TIDMASK(asc_dvc->chip_scsi_id));
+
+ /*
+ * Build the carrier freelist.
+ *
+ * Driver must have already allocated memory and set 'carrier_buf'.
+ */
+ ASC_ASSERT(asc_dvc->carrier_buf != NULL);
+
+ carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf);
+ asc_dvc->carr_freelist = NULL;
+ if (carrp == (ADV_CARR_T *) asc_dvc->carrier_buf)
+ {
+ buf_size = ADV_CARRIER_BUFSIZE;
+ } else
+ {
+ buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T);
+ }
+
+ do {
+ /*
+ * Get physical address for the carrier 'carrp'.
+ */
+ contig_len = sizeof(ADV_CARR_T);
+ carr_paddr = cpu_to_le32(DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp,
+ (ADV_SDCNT *) &contig_len, ADV_IS_CARRIER_FLAG));
+
+ buf_size -= sizeof(ADV_CARR_T);
+
+ /*
+ * If the current carrier is not physically contiguous, then
+ * maybe there was a page crossing. Try the next carrier aligned
+ * start address.
+ */
+ if (contig_len < sizeof(ADV_CARR_T))
+ {
+ carrp++;
+ continue;
+ }
+
+ carrp->carr_pa = carr_paddr;
+ carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp));
+
+ /*
+ * Insert the carrier at the beginning of the freelist.
+ */
+ carrp->next_vpa = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+ asc_dvc->carr_freelist = carrp;
+
+ carrp++;
+ }
+ while (buf_size > 0);
+
+ /*
+ * Set-up the Host->RISC Initiator Command Queue (ICQ).
+ */
+
+ if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL)
+ {
+ asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
+ return ADV_ERROR;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
+
+ /*
+ * The first command issued will be placed in the stopper carrier.
+ */
+ asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Set RISC ICQ physical address start value.
+ * carr_pa is LE, must be native before write
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_ICQ, asc_dvc->icq_sp->carr_pa);
+
+ /*
+ * Set-up the RISC->Host Initiator Response Queue (IRQ).
+ */
+ if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL)
+ {
+ asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
+ return ADV_ERROR;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
+
+ /*
+ * The first command completed by the RISC will be placed in
+ * the stopper.
+ *
+ * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
+ * completed the RISC will set the ASC_RQ_STOPPER bit.
+ */
+ asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Set RISC IRQ physical address start value.
+ *
+ * carr_pa is LE, must be native before write *
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_IRQ, asc_dvc->irq_sp->carr_pa);
+ asc_dvc->carr_pending_cnt = 0;
+
+ AdvWriteByteRegister(iop_base, IOPB_INTR_ENABLES,
+ (ADV_INTR_ENABLE_HOST_INTR | ADV_INTR_ENABLE_GLOBAL_INTR));
+
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, word);
+ AdvWriteWordRegister(iop_base, IOPW_PC, word);
+
+ /* finally, finally, gentlemen, start your engine */
+ AdvWriteWordRegister(iop_base, IOPW_RISC_CSR, ADV_RISC_CSR_RUN);
+
+ /*
+ * Reset the SCSI Bus if the EEPROM indicates that SCSI Bus
+ * Resets should be performed. The RISC has to be running
+ * to issue a SCSI Bus Reset.
+ */
+ if (asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS)
+ {
+ /*
+ * If the BIOS Signature is present in memory, restore the
+ * BIOS Handshake Configuration Table and do not perform
+ * a SCSI Bus Reset.
+ */
+ if (bios_mem[(ASC_MC_BIOS_SIGNATURE - ASC_MC_BIOSMEM)/2] == 0x55AA)
+ {
+ /*
+ * Restore per TID negotiated values.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ AdvWriteByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+ } else
+ {
+ if (AdvResetSB(asc_dvc) != ADV_TRUE)
+ {
+ warn_code = ASC_WARN_BUSRESET_ERROR;
+ }
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Initialize the ASC-38C1600.
+ *
+ * On failure set the ASC_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Needed after initialization for error recovery.
+ */
+STATIC int
+AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADV_DCNT sum;
+ int begin_addr;
+ int end_addr;
+ ushort code_sum;
+ long word;
+ int j;
+ int adv_asc38C1600_expanded_size;
+ ADV_CARR_T *carrp;
+ ADV_DCNT contig_len;
+ ADV_SDCNT buf_size;
+ ADV_PADDR carr_paddr;
+ int i;
+ ushort scsi_cfg1;
+ uchar byte;
+ uchar tid;
+ ushort bios_mem[ASC_MC_BIOSLEN/2]; /* BIOS RISC Memory 0x40-0x8F. */
+ ushort wdtr_able, sdtr_able, ppr_able, tagqng_able;
+ uchar max_cmd[ASC_MAX_TID + 1];
+
+ /* If there is already an error, don't continue. */
+ if (asc_dvc->err_code != 0)
+ {
+ return ADV_ERROR;
+ }
+
+ /*
+ * The caller must set 'chip_type' to ADV_CHIP_ASC38C1600.
+ */
+ if (asc_dvc->chip_type != ADV_CHIP_ASC38C1600)
+ {
+ asc_dvc->err_code = ASC_IERR_BAD_CHIPTYPE;
+ return ADV_ERROR;
+ }
+
+ warn_code = 0;
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Save the RISC memory BIOS region before writing the microcode.
+ * The BIOS may already be loaded and using its RISC LRAM region
+ * so its region must be saved and restored.
+ *
+ * Note: This code makes the assumption, which is currently true,
+ * that a chip reset does not clear RISC LRAM.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN/2; i++)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_BIOSMEM + (2 * i), bios_mem[i]);
+ }
+
+ /*
+ * Save current per TID negotiated values.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ AdvReadWordLram(iop_base, ASC_MC_PPR_ABLE, ppr_able);
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ASC_MAX_TID; tid++)
+ {
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+
+ /*
+ * RAM BIST (Built-In Self Test)
+ *
+ * Address : I/O base + offset 0x38h register (byte).
+ * Function: Bit 7-6(RW) : RAM mode
+ * Normal Mode : 0x00
+ * Pre-test Mode : 0x40
+ * RAM Test Mode : 0x80
+ * Bit 5 : unused
+ * Bit 4(RO) : Done bit
+ * Bit 3-0(RO) : Status
+ * Host Error : 0x08
+ * Int_RAM Error : 0x04
+ * RISC Error : 0x02
+ * SCSI Error : 0x01
+ * No Error : 0x00
+ *
+ * Note: RAM BIST code should be put right here, before loading the
+ * microcode and after saving the RISC memory BIOS region.
+ */
+
+ /*
+ * LRAM Pre-test
+ *
+ * Write PRE_TEST_MODE (0x40) to register and wait for 10 milliseconds.
+ * If Done bit not set or low nibble not PRE_TEST_VALUE (0x05), return
+ * an error. Reset to NORMAL_MODE (0x00) and do again. If cannot reset
+ * to NORMAL_MODE, return an error too.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, PRE_TEST_MODE);
+ DvcSleepMilliSecond(10); /* Wait for 10ms before reading back. */
+ byte = AdvReadByteRegister(iop_base, IOPB_RAM_BIST);
+ if ((byte & RAM_TEST_DONE) == 0 || (byte & 0x0F) != PRE_TEST_VALUE)
+ {
+ asc_dvc->err_code |= ASC_IERR_BIST_PRE_TEST;
+ return ADV_ERROR;
+ }
+
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, NORMAL_MODE);
+ DvcSleepMilliSecond(10); /* Wait for 10ms before reading back. */
+ if (AdvReadByteRegister(iop_base, IOPB_RAM_BIST)
+ != NORMAL_VALUE)
+ {
+ asc_dvc->err_code |= ASC_IERR_BIST_PRE_TEST;
+ return ADV_ERROR;
+ }
+ }
+
+ /*
+ * LRAM Test - It takes about 1.5 ms to run through the test.
+ *
+ * Write RAM_TEST_MODE (0x80) to register and wait for 10 milliseconds.
+ * If Done bit not set or Status not 0, save register byte, set the
+ * err_code, and return an error.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, RAM_TEST_MODE);
+ DvcSleepMilliSecond(10); /* Wait for 10ms before checking status. */
+
+ byte = AdvReadByteRegister(iop_base, IOPB_RAM_BIST);
+ if ((byte & RAM_TEST_DONE) == 0 || (byte & RAM_TEST_STATUS) != 0)
+ {
+ /* Get here if Done bit not set or Status not 0. */
+ asc_dvc->bist_err_code = byte; /* for BIOS display message */
+ asc_dvc->err_code |= ASC_IERR_BIST_RAM_TEST;
+ return ADV_ERROR;
+ }
+
+ /* We need to reset back to normal mode after LRAM test passes. */
+ AdvWriteByteRegister(iop_base, IOPB_RAM_BIST, NORMAL_MODE);
+
+ /*
+ * Load the Microcode
+ *
+ * Write the microcode image to RISC memory starting at address 0.
+ *
+ */
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+
+ /*
+ * Assume the following compressed format of the microcode buffer:
+ *
+ * 254 word (508 byte) table indexed by byte code followed
+ * by the following byte codes:
+ *
+ * 1-Byte Code:
+ * 00: Emit word 0 in table.
+ * 01: Emit word 1 in table.
+ * .
+ * FD: Emit word 253 in table.
+ *
+ * Multi-Byte Code:
+ * FE WW WW: (3 byte code) Word to emit is the next word WW WW.
+ * FF BB WW WW: (4 byte code) Emit BB count times next word WW WW.
+ */
+ word = 0;
+ for (i = 253 * 2; i < _adv_asc38C1600_size; i++)
+ {
+ if (_adv_asc38C1600_buf[i] == 0xff)
+ {
+ for (j = 0; j < _adv_asc38C1600_buf[i + 1]; j++)
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc38C1600_buf[i + 3] << 8) |
+ _adv_asc38C1600_buf[i + 2]));
+ word++;
+ }
+ i += 3;
+ } else if (_adv_asc38C1600_buf[i] == 0xfe)
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc38C1600_buf[i + 2] << 8) |
+ _adv_asc38C1600_buf[i + 1]));
+ i += 2;
+ word++;
+ } else
+ {
+ AdvWriteWordAutoIncLram(iop_base, (((ushort)
+ _adv_asc38C1600_buf[(_adv_asc38C1600_buf[i] * 2) + 1] << 8) |
+ _adv_asc38C1600_buf[_adv_asc38C1600_buf[i] * 2]));
+ word++;
+ }
+ }
+
+ /*
+ * Set 'word' for later use to clear the rest of memory and save
+ * the expanded mcode size.
+ */
+ word *= 2;
+ adv_asc38C1600_expanded_size = word;
+
+ /*
+ * Clear the rest of ASC-38C1600 Internal RAM (32KB).
+ */
+ for (; word < ADV_38C1600_MEMSIZE; word += 2)
+ {
+ AdvWriteWordAutoIncLram(iop_base, 0);
+ }
+
+ /*
+ * Verify the microcode checksum.
+ */
+ sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+
+ for (word = 0; word < adv_asc38C1600_expanded_size; word += 2)
+ {
+ sum += AdvReadWordAutoIncLram(iop_base);
+ }
+
+ if (sum != _adv_asc38C1600_chksum)
+ {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Restore the RISC memory BIOS region.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN/2; i++)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_BIOSMEM + (2 * i), bios_mem[i]);
+ }
+
+ /*
+ * Calculate and write the microcode code checksum to the microcode
+ * code checksum location ASC_MC_CODE_CHK_SUM (0x2C).
+ */
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, begin_addr);
+ AdvReadWordLram(iop_base, ASC_MC_CODE_END_ADDR, end_addr);
+ code_sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, begin_addr);
+ for (word = begin_addr; word < end_addr; word += 2)
+ {
+ code_sum += AdvReadWordAutoIncLram(iop_base);
+ }
+ AdvWriteWordLram(iop_base, ASC_MC_CODE_CHK_SUM, code_sum);
+
+ /*
+ * Read microcode version and date.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_DATE, asc_dvc->cfg->mcode_date);
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_NUM, asc_dvc->cfg->mcode_version);
+
+ /*
+ * Set the chip type to indicate the ASC38C1600.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_CHIP_TYPE, ADV_CHIP_ASC38C1600);
+
+ /*
+ * Write 1 to bit 14 'DIS_TERM_DRV' in the SCSI_CFG1 register.
+ * When DIS_TERM_DRV set to 1, C_DET[3:0] will reflect current
+ * cable detection and then we are able to read C_DET[3:0].
+ *
+ * Note: We will reset DIS_TERM_DRV to 0 in the 'Set SCSI_CFG1
+ * Microcode Default Value' section below.
+ */
+ scsi_cfg1 = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+ AdvWriteWordRegister(iop_base, IOPW_SCSI_CFG1, scsi_cfg1 | DIS_TERM_DRV);
+
+ /*
+ * If the PCI Configuration Command Register "Parity Error Response
+ * Control" Bit was clear (0), then set the microcode variable
+ * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode
+ * to ignore DMA parity errors.
+ */
+ if (asc_dvc->cfg->control_flag & CONTROL_FLAG_IGNORE_PERR)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ word |= CONTROL_FLAG_IGNORE_PERR;
+ AdvWriteWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ }
+
+ /*
+ * If the BIOS control flag AIPP (Asynchronous Information
+ * Phase Protection) disable bit is not set, then set the firmware
+ * 'control_flag' CONTROL_FLAG_ENABLE_AIPP bit to enable
+ * AIPP checking and encoding.
+ */
+ if ((asc_dvc->bios_ctrl & BIOS_CTRL_AIPP_DIS) == 0)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ word |= CONTROL_FLAG_ENABLE_AIPP;
+ AdvWriteWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ }
+
+ /*
+ * For ASC-38C1600 use DMA_CFG0 default values: FIFO_THRESH_80B [6:4],
+ * and START_CTL_TH [3:2].
+ */
+ AdvWriteByteRegister(iop_base, IOPB_DMA_CFG0,
+ FIFO_THRESH_80B | START_CTL_TH | READ_CMD_MRM);
+
+ /*
+ * Microcode operating variables for WDTR, SDTR, and command tag
+ * queuing will be set in AdvInquiryHandling() based on what a
+ * device reports it is capable of in Inquiry byte 7.
+ *
+ * If SCSI Bus Resets have been disabled, then directly set
+ * SDTR and WDTR from the EEPROM configuration. This will allow
+ * the BIOS and warm boot to work without a SCSI bus hang on
+ * the Inquiry caused by host and target mismatched DTR values.
+ * Without the SCSI Bus Reset, before an Inquiry a device can't
+ * be assumed to be in Asynchronous, Narrow mode.
+ */
+ if ((asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS) == 0)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, asc_dvc->wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, asc_dvc->sdtr_able);
+ }
+
+ /*
+ * Set microcode operating variables for DISC and SDTR_SPEED1,
+ * SDTR_SPEED2, SDTR_SPEED3, and SDTR_SPEED4 based on the EEPROM
+ * configuration values.
+ *
+ * The SDTR per TID bitmask overrides the SDTR_SPEED1, SDTR_SPEED2,
+ * SDTR_SPEED3, and SDTR_SPEED4 values so it is safe to set them
+ * without determining here whether the device supports SDTR.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DISC_ENABLE, asc_dvc->cfg->disc_enable);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED1, asc_dvc->sdtr_speed1);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED2, asc_dvc->sdtr_speed2);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED3, asc_dvc->sdtr_speed3);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_SPEED4, asc_dvc->sdtr_speed4);
+
+ /*
+ * Set SCSI_CFG0 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG0 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG0,
+ PARITY_EN | QUEUE_128 | SEL_TMO_LONG | OUR_ID_EN |
+ asc_dvc->chip_scsi_id);
+
+ /*
+ * Calculate SCSI_CFG1 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ *
+ * Each ASC-38C1600 function has only two cable detect bits.
+ * The bus mode override bits are in IOPB_SOFT_OVER_WR.
+ */
+ scsi_cfg1 = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+
+ /*
+ * If the cable is reversed all of the SCSI_CTRL register signals
+ * will be set. Check for and return an error if this condition is
+ * found.
+ */
+ if ((AdvReadWordRegister(iop_base, IOPW_SCSI_CTRL) & 0x3F07) == 0x3F07)
+ {
+ asc_dvc->err_code |= ASC_IERR_REVERSED_CABLE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Each ASC-38C1600 function has two connectors. Only an HVD device
+ * can not be connected to either connector. An LVD device or SE device
+ * may be connected to either connecor. If an SE device is connected,
+ * then at most Ultra speed (20 Mhz) can be used on both connectors.
+ *
+ * If an HVD device is attached, return an error.
+ */
+ if (scsi_cfg1 & HVD)
+ {
+ asc_dvc->err_code |= ASC_IERR_HVD_DEVICE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Each function in the ASC-38C1600 uses only the SE cable detect and
+ * termination because there are two connectors for each function. Each
+ * function may use either LVD or SE mode. Corresponding the SE automatic
+ * termination control EEPROM bits are used for each function. Each
+ * function has its own EEPROM. If SE automatic control is enabled for
+ * the function, then set the termination value based on a table listed
+ * in a_condor.h.
+ *
+ * If manual termination is specified in the EEPROM for the function,
+ * then 'termination' was set-up in AscInitFrom38C1600EEPROM() and is
+ * ready to be 'ored' into SCSI_CFG1.
+ */
+ if ((asc_dvc->cfg->termination & TERM_SE) == 0)
+ {
+ /* SE automatic termination control is enabled. */
+ switch(scsi_cfg1 & C_DET_SE)
+ {
+ /* TERM_SE_HI: on, TERM_SE_LO: on */
+ case 0x1: case 0x2: case 0x3:
+ asc_dvc->cfg->termination |= TERM_SE;
+ break;
+
+ case 0x0:
+ if (ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info) == 0)
+ {
+ /* Function 0 - TERM_SE_HI: off, TERM_SE_LO: off */
+ }
+ else
+ {
+ /* Function 1 - TERM_SE_HI: on, TERM_SE_LO: off */
+ asc_dvc->cfg->termination |= TERM_SE_HI;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Clear any set TERM_SE bits.
+ */
+ scsi_cfg1 &= ~TERM_SE;
+
+ /*
+ * Invert the TERM_SE bits and then set 'scsi_cfg1'.
+ */
+ scsi_cfg1 |= (~asc_dvc->cfg->termination & TERM_SE);
+
+ /*
+ * Clear Big Endian and Terminator Polarity bits and set possibly
+ * modified termination control bits in the Microcode SCSI_CFG1
+ * Register Value.
+ *
+ * Big Endian bit is not used even on big endian machines.
+ */
+ scsi_cfg1 &= (~BIG_ENDIAN & ~DIS_TERM_DRV & ~TERM_POL);
+
+ /*
+ * Set SCSI_CFG1 Microcode Default Value
+ *
+ * Set possibly modified termination control bits in the Microcode
+ * SCSI_CFG1 Register Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG1, scsi_cfg1);
+
+ /*
+ * Set MEM_CFG Microcode Default Value
+ *
+ * The microcode will set the MEM_CFG register using this value
+ * after it is started below.
+ *
+ * MEM_CFG may be accessed as a word or byte, but only bits 0-7
+ * are defined.
+ *
+ * ASC-38C1600 has 32KB internal memory.
+ *
+ * XXX - Since ASC38C1600 Rev.3 has a Local RAM failure issue, we come
+ * out a special 16K Adv Library and Microcode version. After the issue
+ * resolved, we should turn back to the 32K support. Both a_condor.h and
+ * mcode.sas files also need to be updated.
+ *
+ * AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_MEM_CFG,
+ * BIOS_EN | RAM_SZ_32KB);
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_MEM_CFG, BIOS_EN | RAM_SZ_16KB);
+
+ /*
+ * Set SEL_MASK Microcode Default Value
+ *
+ * The microcode will set the SEL_MASK register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SEL_MASK,
+ ADV_TID_TO_TIDMASK(asc_dvc->chip_scsi_id));
+
+ /*
+ * Build the carrier freelist.
+ *
+ * Driver must have already allocated memory and set 'carrier_buf'.
+ */
+
+ ASC_ASSERT(asc_dvc->carrier_buf != NULL);
+
+ carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf);
+ asc_dvc->carr_freelist = NULL;
+ if (carrp == (ADV_CARR_T *) asc_dvc->carrier_buf)
+ {
+ buf_size = ADV_CARRIER_BUFSIZE;
+ } else
+ {
+ buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T);
+ }
+
+ do {
+ /*
+ * Get physical address for the carrier 'carrp'.
+ */
+ contig_len = sizeof(ADV_CARR_T);
+ carr_paddr = cpu_to_le32(DvcGetPhyAddr(asc_dvc, NULL, (uchar *) carrp,
+ (ADV_SDCNT *) &contig_len, ADV_IS_CARRIER_FLAG));
+
+ buf_size -= sizeof(ADV_CARR_T);
+
+ /*
+ * If the current carrier is not physically contiguous, then
+ * maybe there was a page crossing. Try the next carrier aligned
+ * start address.
+ */
+ if (contig_len < sizeof(ADV_CARR_T))
+ {
+ carrp++;
+ continue;
+ }
+
+ carrp->carr_pa = carr_paddr;
+ carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp));
+
+ /*
+ * Insert the carrier at the beginning of the freelist.
+ */
+ carrp->next_vpa = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+ asc_dvc->carr_freelist = carrp;
+
+ carrp++;
+ }
+ while (buf_size > 0);
+
+ /*
+ * Set-up the Host->RISC Initiator Command Queue (ICQ).
+ */
+ if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL)
+ {
+ asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
+ return ADV_ERROR;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
+
+ /*
+ * The first command issued will be placed in the stopper carrier.
+ */
+ asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Set RISC ICQ physical address start value. Initialize the
+ * COMMA register to the same value otherwise the RISC will
+ * prematurely detect a command is available.
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_ICQ, asc_dvc->icq_sp->carr_pa);
+ AdvWriteDWordRegister(iop_base, IOPDW_COMMA,
+ le32_to_cpu(asc_dvc->icq_sp->carr_pa));
+
+ /*
+ * Set-up the RISC->Host Initiator Response Queue (IRQ).
+ */
+ if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL)
+ {
+ asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
+ return ADV_ERROR;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
+
+ /*
+ * The first command completed by the RISC will be placed in
+ * the stopper.
+ *
+ * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
+ * completed the RISC will set the ASC_RQ_STOPPER bit.
+ */
+ asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Set RISC IRQ physical address start value.
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_IRQ, asc_dvc->irq_sp->carr_pa);
+ asc_dvc->carr_pending_cnt = 0;
+
+ AdvWriteByteRegister(iop_base, IOPB_INTR_ENABLES,
+ (ADV_INTR_ENABLE_HOST_INTR | ADV_INTR_ENABLE_GLOBAL_INTR));
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, word);
+ AdvWriteWordRegister(iop_base, IOPW_PC, word);
+
+ /* finally, finally, gentlemen, start your engine */
+ AdvWriteWordRegister(iop_base, IOPW_RISC_CSR, ADV_RISC_CSR_RUN);
+
+ /*
+ * Reset the SCSI Bus if the EEPROM indicates that SCSI Bus
+ * Resets should be performed. The RISC has to be running
+ * to issue a SCSI Bus Reset.
+ */
+ if (asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS)
+ {
+ /*
+ * If the BIOS Signature is present in memory, restore the
+ * per TID microcode operating variables.
+ */
+ if (bios_mem[(ASC_MC_BIOS_SIGNATURE - ASC_MC_BIOSMEM)/2] == 0x55AA)
+ {
+ /*
+ * Restore per TID negotiated values.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_PPR_ABLE, ppr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ASC_MAX_TID; tid++)
+ {
+ AdvWriteByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+ } else
+ {
+ if (AdvResetSB(asc_dvc) != ADV_TRUE)
+ {
+ warn_code = ASC_WARN_BUSRESET_ERROR;
+ }
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Read the board's EEPROM configuration. Set fields in ADV_DVC_VAR and
+ * ADV_DVC_CFG based on the EEPROM settings. The chip is stopped while
+ * all of this is done.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Note: Chip is stopped on entry.
+ */
+STATIC int __init
+AdvInitFrom3550EEP(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADVEEP_3550_CONFIG eep_config;
+ int i;
+
+ iop_base = asc_dvc->iop_base;
+
+ warn_code = 0;
+
+ /*
+ * Read the board's EEPROM configuration.
+ *
+ * Set default values if a bad checksum is found.
+ */
+ if (AdvGet3550EEPConfig(iop_base, &eep_config) != eep_config.check_sum)
+ {
+ warn_code |= ASC_WARN_EEPROM_CHKSUM;
+
+ /*
+ * Set EEPROM default values.
+ */
+ for (i = 0; i < sizeof(ADVEEP_3550_CONFIG); i++)
+ {
+ *((uchar *) &eep_config + i) =
+ *((uchar *) &Default_3550_EEPROM_Config + i);
+ }
+
+ /*
+ * Assume the 6 byte board serial number that was read
+ * from EEPROM is correct even if the EEPROM checksum
+ * failed.
+ */
+ eep_config.serial_number_word3 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 1);
+
+ eep_config.serial_number_word2 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 2);
+
+ eep_config.serial_number_word1 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 3);
+
+ AdvSet3550EEPConfig(iop_base, &eep_config);
+ }
+ /*
+ * Set ASC_DVC_VAR and ASC_DVC_CFG variables from the
+ * EEPROM configuration that was read.
+ *
+ * This is the mapping of EEPROM fields to Adv Library fields.
+ */
+ asc_dvc->wdtr_able = eep_config.wdtr_able;
+ asc_dvc->sdtr_able = eep_config.sdtr_able;
+ asc_dvc->ultra_able = eep_config.ultra_able;
+ asc_dvc->tagqng_able = eep_config.tagqng_able;
+ asc_dvc->cfg->disc_enable = eep_config.disc_enable;
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+ asc_dvc->chip_scsi_id = (eep_config.adapter_scsi_id & ADV_MAX_TID);
+ asc_dvc->start_motor = eep_config.start_motor;
+ asc_dvc->scsi_reset_wait = eep_config.scsi_reset_delay;
+ asc_dvc->bios_ctrl = eep_config.bios_ctrl;
+ asc_dvc->no_scam = eep_config.scam_tolerant;
+ asc_dvc->cfg->serial1 = eep_config.serial_number_word1;
+ asc_dvc->cfg->serial2 = eep_config.serial_number_word2;
+ asc_dvc->cfg->serial3 = eep_config.serial_number_word3;
+
+ /*
+ * Set the host maximum queuing (max. 253, min. 16) and the per device
+ * maximum queuing (max. 63, min. 4).
+ */
+ if (eep_config.max_host_qng > ASC_DEF_MAX_HOST_QNG)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else if (eep_config.max_host_qng < ASC_DEF_MIN_HOST_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_host_qng == 0)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else
+ {
+ eep_config.max_host_qng = ASC_DEF_MIN_HOST_QNG;
+ }
+ }
+
+ if (eep_config.max_dvc_qng > ASC_DEF_MAX_DVC_QNG)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else if (eep_config.max_dvc_qng < ASC_DEF_MIN_DVC_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_dvc_qng == 0)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MIN_DVC_QNG;
+ }
+ }
+
+ /*
+ * If 'max_dvc_qng' is greater than 'max_host_qng', then
+ * set 'max_dvc_qng' to 'max_host_qng'.
+ */
+ if (eep_config.max_dvc_qng > eep_config.max_host_qng)
+ {
+ eep_config.max_dvc_qng = eep_config.max_host_qng;
+ }
+
+ /*
+ * Set ADV_DVC_VAR 'max_host_qng' and ADV_DVC_VAR 'max_dvc_qng'
+ * values based on possibly adjusted EEPROM values.
+ */
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+
+
+ /*
+ * If the EEPROM 'termination' field is set to automatic (0), then set
+ * the ADV_DVC_CFG 'termination' field to automatic also.
+ *
+ * If the termination is specified with a non-zero 'termination'
+ * value check that a legal value is set and set the ADV_DVC_CFG
+ * 'termination' field appropriately.
+ */
+ if (eep_config.termination == 0)
+ {
+ asc_dvc->cfg->termination = 0; /* auto termination */
+ } else
+ {
+ /* Enable manual control with low off / high off. */
+ if (eep_config.termination == 1)
+ {
+ asc_dvc->cfg->termination = TERM_CTL_SEL;
+
+ /* Enable manual control with low off / high on. */
+ } else if (eep_config.termination == 2)
+ {
+ asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H;
+
+ /* Enable manual control with low on / high on. */
+ } else if (eep_config.termination == 3)
+ {
+ asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H | TERM_CTL_L;
+ } else
+ {
+ /*
+ * The EEPROM 'termination' field contains a bad value. Use
+ * automatic termination instead.
+ */
+ asc_dvc->cfg->termination = 0;
+ warn_code |= ASC_WARN_EEPROM_TERMINATION;
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Read the board's EEPROM configuration. Set fields in ADV_DVC_VAR and
+ * ADV_DVC_CFG based on the EEPROM settings. The chip is stopped while
+ * all of this is done.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Note: Chip is stopped on entry.
+ */
+STATIC int __init
+AdvInitFrom38C0800EEP(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADVEEP_38C0800_CONFIG eep_config;
+ int i;
+ uchar tid, termination;
+ ushort sdtr_speed = 0;
+
+ iop_base = asc_dvc->iop_base;
+
+ warn_code = 0;
+
+ /*
+ * Read the board's EEPROM configuration.
+ *
+ * Set default values if a bad checksum is found.
+ */
+ if (AdvGet38C0800EEPConfig(iop_base, &eep_config) != eep_config.check_sum)
+ {
+ warn_code |= ASC_WARN_EEPROM_CHKSUM;
+
+ /*
+ * Set EEPROM default values.
+ */
+ for (i = 0; i < sizeof(ADVEEP_38C0800_CONFIG); i++)
+ {
+ *((uchar *) &eep_config + i) =
+ *((uchar *) &Default_38C0800_EEPROM_Config + i);
+ }
+
+ /*
+ * Assume the 6 byte board serial number that was read
+ * from EEPROM is correct even if the EEPROM checksum
+ * failed.
+ */
+ eep_config.serial_number_word3 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 1);
+
+ eep_config.serial_number_word2 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 2);
+
+ eep_config.serial_number_word1 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 3);
+
+ AdvSet38C0800EEPConfig(iop_base, &eep_config);
+ }
+ /*
+ * Set ADV_DVC_VAR and ADV_DVC_CFG variables from the
+ * EEPROM configuration that was read.
+ *
+ * This is the mapping of EEPROM fields to Adv Library fields.
+ */
+ asc_dvc->wdtr_able = eep_config.wdtr_able;
+ asc_dvc->sdtr_speed1 = eep_config.sdtr_speed1;
+ asc_dvc->sdtr_speed2 = eep_config.sdtr_speed2;
+ asc_dvc->sdtr_speed3 = eep_config.sdtr_speed3;
+ asc_dvc->sdtr_speed4 = eep_config.sdtr_speed4;
+ asc_dvc->tagqng_able = eep_config.tagqng_able;
+ asc_dvc->cfg->disc_enable = eep_config.disc_enable;
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+ asc_dvc->chip_scsi_id = (eep_config.adapter_scsi_id & ADV_MAX_TID);
+ asc_dvc->start_motor = eep_config.start_motor;
+ asc_dvc->scsi_reset_wait = eep_config.scsi_reset_delay;
+ asc_dvc->bios_ctrl = eep_config.bios_ctrl;
+ asc_dvc->no_scam = eep_config.scam_tolerant;
+ asc_dvc->cfg->serial1 = eep_config.serial_number_word1;
+ asc_dvc->cfg->serial2 = eep_config.serial_number_word2;
+ asc_dvc->cfg->serial3 = eep_config.serial_number_word3;
+
+ /*
+ * For every Target ID if any of its 'sdtr_speed[1234]' bits
+ * are set, then set an 'sdtr_able' bit for it.
+ */
+ asc_dvc->sdtr_able = 0;
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ if (tid == 0)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed1;
+ } else if (tid == 4)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed2;
+ } else if (tid == 8)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed3;
+ } else if (tid == 12)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed4;
+ }
+ if (sdtr_speed & ADV_MAX_TID)
+ {
+ asc_dvc->sdtr_able |= (1 << tid);
+ }
+ sdtr_speed >>= 4;
+ }
+
+ /*
+ * Set the host maximum queuing (max. 253, min. 16) and the per device
+ * maximum queuing (max. 63, min. 4).
+ */
+ if (eep_config.max_host_qng > ASC_DEF_MAX_HOST_QNG)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else if (eep_config.max_host_qng < ASC_DEF_MIN_HOST_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_host_qng == 0)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else
+ {
+ eep_config.max_host_qng = ASC_DEF_MIN_HOST_QNG;
+ }
+ }
+
+ if (eep_config.max_dvc_qng > ASC_DEF_MAX_DVC_QNG)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else if (eep_config.max_dvc_qng < ASC_DEF_MIN_DVC_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_dvc_qng == 0)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MIN_DVC_QNG;
+ }
+ }
+
+ /*
+ * If 'max_dvc_qng' is greater than 'max_host_qng', then
+ * set 'max_dvc_qng' to 'max_host_qng'.
+ */
+ if (eep_config.max_dvc_qng > eep_config.max_host_qng)
+ {
+ eep_config.max_dvc_qng = eep_config.max_host_qng;
+ }
+
+ /*
+ * Set ADV_DVC_VAR 'max_host_qng' and ADV_DVC_VAR 'max_dvc_qng'
+ * values based on possibly adjusted EEPROM values.
+ */
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+
+ /*
+ * If the EEPROM 'termination' field is set to automatic (0), then set
+ * the ADV_DVC_CFG 'termination' field to automatic also.
+ *
+ * If the termination is specified with a non-zero 'termination'
+ * value check that a legal value is set and set the ADV_DVC_CFG
+ * 'termination' field appropriately.
+ */
+ if (eep_config.termination_se == 0)
+ {
+ termination = 0; /* auto termination for SE */
+ } else
+ {
+ /* Enable manual control with low off / high off. */
+ if (eep_config.termination_se == 1)
+ {
+ termination = 0;
+
+ /* Enable manual control with low off / high on. */
+ } else if (eep_config.termination_se == 2)
+ {
+ termination = TERM_SE_HI;
+
+ /* Enable manual control with low on / high on. */
+ } else if (eep_config.termination_se == 3)
+ {
+ termination = TERM_SE;
+ } else
+ {
+ /*
+ * The EEPROM 'termination_se' field contains a bad value.
+ * Use automatic termination instead.
+ */
+ termination = 0;
+ warn_code |= ASC_WARN_EEPROM_TERMINATION;
+ }
+ }
+
+ if (eep_config.termination_lvd == 0)
+ {
+ asc_dvc->cfg->termination = termination; /* auto termination for LVD */
+ } else
+ {
+ /* Enable manual control with low off / high off. */
+ if (eep_config.termination_lvd == 1)
+ {
+ asc_dvc->cfg->termination = termination;
+
+ /* Enable manual control with low off / high on. */
+ } else if (eep_config.termination_lvd == 2)
+ {
+ asc_dvc->cfg->termination = termination | TERM_LVD_HI;
+
+ /* Enable manual control with low on / high on. */
+ } else if (eep_config.termination_lvd == 3)
+ {
+ asc_dvc->cfg->termination =
+ termination | TERM_LVD;
+ } else
+ {
+ /*
+ * The EEPROM 'termination_lvd' field contains a bad value.
+ * Use automatic termination instead.
+ */
+ asc_dvc->cfg->termination = termination;
+ warn_code |= ASC_WARN_EEPROM_TERMINATION;
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Read the board's EEPROM configuration. Set fields in ASC_DVC_VAR and
+ * ASC_DVC_CFG based on the EEPROM settings. The chip is stopped while
+ * all of this is done.
+ *
+ * On failure set the ASC_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Note: Chip is stopped on entry.
+ */
+STATIC int __init
+AdvInitFrom38C1600EEP(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADVEEP_38C1600_CONFIG eep_config;
+ int i;
+ uchar tid, termination;
+ ushort sdtr_speed = 0;
+
+ iop_base = asc_dvc->iop_base;
+
+ warn_code = 0;
+
+ /*
+ * Read the board's EEPROM configuration.
+ *
+ * Set default values if a bad checksum is found.
+ */
+ if (AdvGet38C1600EEPConfig(iop_base, &eep_config) != eep_config.check_sum)
+ {
+ warn_code |= ASC_WARN_EEPROM_CHKSUM;
+
+ /*
+ * Set EEPROM default values.
+ */
+ for (i = 0; i < sizeof(ADVEEP_38C1600_CONFIG); i++)
+ {
+ if (i == 1 && ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info) != 0)
+ {
+ /*
+ * Set Function 1 EEPROM Word 0 MSB
+ *
+ * Clear the BIOS_ENABLE (bit 14) and INTAB (bit 11)
+ * EEPROM bits.
+ *
+ * Disable Bit 14 (BIOS_ENABLE) to fix SPARC Ultra 60 and
+ * old Mac system booting problem. The Expansion ROM must
+ * be disabled in Function 1 for these systems.
+ *
+ */
+ *((uchar *) &eep_config + i) =
+ ((*((uchar *) &Default_38C1600_EEPROM_Config + i)) &
+ (~(((ADV_EEPROM_BIOS_ENABLE | ADV_EEPROM_INTAB) >> 8) &
+ 0xFF)));
+
+ /*
+ * Set the INTAB (bit 11) if the GPIO 0 input indicates
+ * the Function 1 interrupt line is wired to INTA.
+ *
+ * Set/Clear Bit 11 (INTAB) from the GPIO bit 0 input:
+ * 1 - Function 1 interrupt line wired to INT A.
+ * 0 - Function 1 interrupt line wired to INT B.
+ *
+ * Note: Adapter boards always have Function 0 wired to INTA.
+ * Put all 5 GPIO bits in input mode and then read
+ * their input values.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_GPIO_CNTL, 0);
+ if (AdvReadByteRegister(iop_base, IOPB_GPIO_DATA) & 0x01)
+ {
+ /* Function 1 interrupt wired to INTA; Set EEPROM bit. */
+ *((uchar *) &eep_config + i) |=
+ ((ADV_EEPROM_INTAB >> 8) & 0xFF);
+ }
+ }
+ else
+ {
+ *((uchar *) &eep_config + i) =
+ *((uchar *) &Default_38C1600_EEPROM_Config + i);
+ }
+ }
+
+ /*
+ * Assume the 6 byte board serial number that was read
+ * from EEPROM is correct even if the EEPROM checksum
+ * failed.
+ */
+ eep_config.serial_number_word3 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 1);
+
+ eep_config.serial_number_word2 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 2);
+
+ eep_config.serial_number_word1 =
+ AdvReadEEPWord(iop_base, ADV_EEP_DVC_CFG_END - 3);
+
+ AdvSet38C1600EEPConfig(iop_base, &eep_config);
+ }
+
+ /*
+ * Set ASC_DVC_VAR and ASC_DVC_CFG variables from the
+ * EEPROM configuration that was read.
+ *
+ * This is the mapping of EEPROM fields to Adv Library fields.
+ */
+ asc_dvc->wdtr_able = eep_config.wdtr_able;
+ asc_dvc->sdtr_speed1 = eep_config.sdtr_speed1;
+ asc_dvc->sdtr_speed2 = eep_config.sdtr_speed2;
+ asc_dvc->sdtr_speed3 = eep_config.sdtr_speed3;
+ asc_dvc->sdtr_speed4 = eep_config.sdtr_speed4;
+ asc_dvc->ppr_able = 0;
+ asc_dvc->tagqng_able = eep_config.tagqng_able;
+ asc_dvc->cfg->disc_enable = eep_config.disc_enable;
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+ asc_dvc->chip_scsi_id = (eep_config.adapter_scsi_id & ASC_MAX_TID);
+ asc_dvc->start_motor = eep_config.start_motor;
+ asc_dvc->scsi_reset_wait = eep_config.scsi_reset_delay;
+ asc_dvc->bios_ctrl = eep_config.bios_ctrl;
+ asc_dvc->no_scam = eep_config.scam_tolerant;
+
+ /*
+ * For every Target ID if any of its 'sdtr_speed[1234]' bits
+ * are set, then set an 'sdtr_able' bit for it.
+ */
+ asc_dvc->sdtr_able = 0;
+ for (tid = 0; tid <= ASC_MAX_TID; tid++)
+ {
+ if (tid == 0)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed1;
+ } else if (tid == 4)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed2;
+ } else if (tid == 8)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed3;
+ } else if (tid == 12)
+ {
+ sdtr_speed = asc_dvc->sdtr_speed4;
+ }
+ if (sdtr_speed & ASC_MAX_TID)
+ {
+ asc_dvc->sdtr_able |= (1 << tid);
+ }
+ sdtr_speed >>= 4;
+ }
+
+ /*
+ * Set the host maximum queuing (max. 253, min. 16) and the per device
+ * maximum queuing (max. 63, min. 4).
+ */
+ if (eep_config.max_host_qng > ASC_DEF_MAX_HOST_QNG)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else if (eep_config.max_host_qng < ASC_DEF_MIN_HOST_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_host_qng == 0)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else
+ {
+ eep_config.max_host_qng = ASC_DEF_MIN_HOST_QNG;
+ }
+ }
+
+ if (eep_config.max_dvc_qng > ASC_DEF_MAX_DVC_QNG)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else if (eep_config.max_dvc_qng < ASC_DEF_MIN_DVC_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_dvc_qng == 0)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MIN_DVC_QNG;
+ }
+ }
+
+ /*
+ * If 'max_dvc_qng' is greater than 'max_host_qng', then
+ * set 'max_dvc_qng' to 'max_host_qng'.
+ */
+ if (eep_config.max_dvc_qng > eep_config.max_host_qng)
+ {
+ eep_config.max_dvc_qng = eep_config.max_host_qng;
+ }
+
+ /*
+ * Set ASC_DVC_VAR 'max_host_qng' and ASC_DVC_VAR 'max_dvc_qng'
+ * values based on possibly adjusted EEPROM values.
+ */
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+
+ /*
+ * If the EEPROM 'termination' field is set to automatic (0), then set
+ * the ASC_DVC_CFG 'termination' field to automatic also.
+ *
+ * If the termination is specified with a non-zero 'termination'
+ * value check that a legal value is set and set the ASC_DVC_CFG
+ * 'termination' field appropriately.
+ */
+ if (eep_config.termination_se == 0)
+ {
+ termination = 0; /* auto termination for SE */
+ } else
+ {
+ /* Enable manual control with low off / high off. */
+ if (eep_config.termination_se == 1)
+ {
+ termination = 0;
+
+ /* Enable manual control with low off / high on. */
+ } else if (eep_config.termination_se == 2)
+ {
+ termination = TERM_SE_HI;
+
+ /* Enable manual control with low on / high on. */
+ } else if (eep_config.termination_se == 3)
+ {
+ termination = TERM_SE;
+ } else
+ {
+ /*
+ * The EEPROM 'termination_se' field contains a bad value.
+ * Use automatic termination instead.
+ */
+ termination = 0;
+ warn_code |= ASC_WARN_EEPROM_TERMINATION;
+ }
+ }
+
+ if (eep_config.termination_lvd == 0)
+ {
+ asc_dvc->cfg->termination = termination; /* auto termination for LVD */
+ } else
+ {
+ /* Enable manual control with low off / high off. */
+ if (eep_config.termination_lvd == 1)
+ {
+ asc_dvc->cfg->termination = termination;
+
+ /* Enable manual control with low off / high on. */
+ } else if (eep_config.termination_lvd == 2)
+ {
+ asc_dvc->cfg->termination = termination | TERM_LVD_HI;
+
+ /* Enable manual control with low on / high on. */
+ } else if (eep_config.termination_lvd == 3)
+ {
+ asc_dvc->cfg->termination =
+ termination | TERM_LVD;
+ } else
+ {
+ /*
+ * The EEPROM 'termination_lvd' field contains a bad value.
+ * Use automatic termination instead.
+ */
+ asc_dvc->cfg->termination = termination;
+ warn_code |= ASC_WARN_EEPROM_TERMINATION;
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Read EEPROM configuration into the specified buffer.
+ *
+ * Return a checksum based on the EEPROM configuration read.
+ */
+STATIC ushort __init
+AdvGet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf)
+{
+ ushort wval, chksum;
+ ushort *wbuf;
+ int eep_addr;
+ ushort *charfields;
+
+ charfields = (ushort *) &ADVEEP_3550_Config_Field_IsChar;
+ wbuf = (ushort *) cfg_buf;
+ chksum = 0;
+
+ for (eep_addr = ADV_EEP_DVC_CFG_BEGIN;
+ eep_addr < ADV_EEP_DVC_CFG_END;
+ eep_addr++, wbuf++)
+ {
+ wval = AdvReadEEPWord(iop_base, eep_addr);
+ chksum += wval; /* Checksum is calculated from word values. */
+ if (*charfields++) {
+ *wbuf = le16_to_cpu(wval);
+ } else {
+ *wbuf = wval;
+ }
+ }
+ /* Read checksum word. */
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ wbuf++; charfields++;
+
+ /* Read rest of EEPROM not covered by the checksum. */
+ for (eep_addr = ADV_EEP_DVC_CTL_BEGIN;
+ eep_addr < ADV_EEP_MAX_WORD_ADDR;
+ eep_addr++, wbuf++)
+ {
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ if (*charfields++) {
+ *wbuf = le16_to_cpu(*wbuf);
+ }
+ }
+ return chksum;
+}
+
+/*
+ * Read EEPROM configuration into the specified buffer.
+ *
+ * Return a checksum based on the EEPROM configuration read.
+ */
+STATIC ushort __init
+AdvGet38C0800EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C0800_CONFIG *cfg_buf)
+{
+ ushort wval, chksum;
+ ushort *wbuf;
+ int eep_addr;
+ ushort *charfields;
+
+ charfields = (ushort *) &ADVEEP_38C0800_Config_Field_IsChar;
+ wbuf = (ushort *) cfg_buf;
+ chksum = 0;
+
+ for (eep_addr = ADV_EEP_DVC_CFG_BEGIN;
+ eep_addr < ADV_EEP_DVC_CFG_END;
+ eep_addr++, wbuf++)
+ {
+ wval = AdvReadEEPWord(iop_base, eep_addr);
+ chksum += wval; /* Checksum is calculated from word values. */
+ if (*charfields++) {
+ *wbuf = le16_to_cpu(wval);
+ } else {
+ *wbuf = wval;
+ }
+ }
+ /* Read checksum word. */
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ wbuf++; charfields++;
+
+ /* Read rest of EEPROM not covered by the checksum. */
+ for (eep_addr = ADV_EEP_DVC_CTL_BEGIN;
+ eep_addr < ADV_EEP_MAX_WORD_ADDR;
+ eep_addr++, wbuf++)
+ {
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ if (*charfields++) {
+ *wbuf = le16_to_cpu(*wbuf);
+ }
+ }
+ return chksum;
+}
+
+/*
+ * Read EEPROM configuration into the specified buffer.
+ *
+ * Return a checksum based on the EEPROM configuration read.
+ */
+STATIC ushort __init
+AdvGet38C1600EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C1600_CONFIG *cfg_buf)
+{
+ ushort wval, chksum;
+ ushort *wbuf;
+ int eep_addr;
+ ushort *charfields;
+
+ charfields = (ushort*) &ADVEEP_38C1600_Config_Field_IsChar;
+ wbuf = (ushort *) cfg_buf;
+ chksum = 0;
+
+ for (eep_addr = ADV_EEP_DVC_CFG_BEGIN;
+ eep_addr < ADV_EEP_DVC_CFG_END;
+ eep_addr++, wbuf++)
+ {
+ wval = AdvReadEEPWord(iop_base, eep_addr);
+ chksum += wval; /* Checksum is calculated from word values. */
+ if (*charfields++) {
+ *wbuf = le16_to_cpu(wval);
+ } else {
+ *wbuf = wval;
+ }
+ }
+ /* Read checksum word. */
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ wbuf++; charfields++;
+
+ /* Read rest of EEPROM not covered by the checksum. */
+ for (eep_addr = ADV_EEP_DVC_CTL_BEGIN;
+ eep_addr < ADV_EEP_MAX_WORD_ADDR;
+ eep_addr++, wbuf++)
+ {
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ if (*charfields++) {
+ *wbuf = le16_to_cpu(*wbuf);
+ }
+ }
+ return chksum;
+}
+
+/*
+ * Read the EEPROM from specified location
+ */
+STATIC ushort __init
+AdvReadEEPWord(AdvPortAddr iop_base, int eep_word_addr)
+{
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD,
+ ASC_EEP_CMD_READ | eep_word_addr);
+ AdvWaitEEPCmd(iop_base);
+ return AdvReadWordRegister(iop_base, IOPW_EE_DATA);
+}
+
+/*
+ * Wait for EEPROM command to complete
+ */
+STATIC void __init
+AdvWaitEEPCmd(AdvPortAddr iop_base)
+{
+ int eep_delay_ms;
+
+ for (eep_delay_ms = 0; eep_delay_ms < ADV_EEP_DELAY_MS; eep_delay_ms++)
+ {
+ if (AdvReadWordRegister(iop_base, IOPW_EE_CMD) & ASC_EEP_CMD_DONE)
+ {
+ break;
+ }
+ DvcSleepMilliSecond(1);
+ }
+ if ((AdvReadWordRegister(iop_base, IOPW_EE_CMD) & ASC_EEP_CMD_DONE) == 0)
+ {
+ ASC_ASSERT(0);
+ }
+ return;
+}
+
+/*
+ * Write the EEPROM from 'cfg_buf'.
+ */
+void
+AdvSet3550EEPConfig(AdvPortAddr iop_base, ADVEEP_3550_CONFIG *cfg_buf)
+{
+ ushort *wbuf;
+ ushort addr, chksum;
+ ushort *charfields;
+
+ wbuf = (ushort *) cfg_buf;
+ charfields = (ushort *) &ADVEEP_3550_Config_Field_IsChar;
+ chksum = 0;
+
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_ABLE);
+ AdvWaitEEPCmd(iop_base);
+
+ /*
+ * Write EEPROM from word 0 to word 20.
+ */
+ for (addr = ADV_EEP_DVC_CFG_BEGIN;
+ addr < ADV_EEP_DVC_CFG_END; addr++, wbuf++)
+ {
+ ushort word;
+
+ if (*charfields++) {
+ word = cpu_to_le16(*wbuf);
+ } else {
+ word = *wbuf;
+ }
+ chksum += *wbuf; /* Checksum is calculated from word values. */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, word);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ DvcSleepMilliSecond(ADV_EEP_DELAY_MS);
+ }
+
+ /*
+ * Write EEPROM checksum at word 21.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, chksum);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ wbuf++; charfields++;
+
+ /*
+ * Write EEPROM OEM name at words 22 to 29.
+ */
+ for (addr = ADV_EEP_DVC_CTL_BEGIN;
+ addr < ADV_EEP_MAX_WORD_ADDR; addr++, wbuf++)
+ {
+ ushort word;
+
+ if (*charfields++) {
+ word = cpu_to_le16(*wbuf);
+ } else {
+ word = *wbuf;
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, word);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_DISABLE);
+ AdvWaitEEPCmd(iop_base);
+ return;
+}
+
+/*
+ * Write the EEPROM from 'cfg_buf'.
+ */
+void
+AdvSet38C0800EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C0800_CONFIG *cfg_buf)
+{
+ ushort *wbuf;
+ ushort *charfields;
+ ushort addr, chksum;
+
+ wbuf = (ushort *) cfg_buf;
+ charfields = (ushort *) &ADVEEP_38C0800_Config_Field_IsChar;
+ chksum = 0;
+
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_ABLE);
+ AdvWaitEEPCmd(iop_base);
+
+ /*
+ * Write EEPROM from word 0 to word 20.
+ */
+ for (addr = ADV_EEP_DVC_CFG_BEGIN;
+ addr < ADV_EEP_DVC_CFG_END; addr++, wbuf++)
+ {
+ ushort word;
+
+ if (*charfields++) {
+ word = cpu_to_le16(*wbuf);
+ } else {
+ word = *wbuf;
+ }
+ chksum += *wbuf; /* Checksum is calculated from word values. */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, word);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ DvcSleepMilliSecond(ADV_EEP_DELAY_MS);
+ }
+
+ /*
+ * Write EEPROM checksum at word 21.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, chksum);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ wbuf++; charfields++;
+
+ /*
+ * Write EEPROM OEM name at words 22 to 29.
+ */
+ for (addr = ADV_EEP_DVC_CTL_BEGIN;
+ addr < ADV_EEP_MAX_WORD_ADDR; addr++, wbuf++)
+ {
+ ushort word;
+
+ if (*charfields++) {
+ word = cpu_to_le16(*wbuf);
+ } else {
+ word = *wbuf;
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, word);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_DISABLE);
+ AdvWaitEEPCmd(iop_base);
+ return;
+}
+
+/*
+ * Write the EEPROM from 'cfg_buf'.
+ */
+void
+AdvSet38C1600EEPConfig(AdvPortAddr iop_base,
+ ADVEEP_38C1600_CONFIG *cfg_buf)
+{
+ ushort *wbuf;
+ ushort *charfields;
+ ushort addr, chksum;
+
+ wbuf = (ushort *) cfg_buf;
+ charfields = (ushort *) &ADVEEP_38C1600_Config_Field_IsChar;
+ chksum = 0;
+
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_ABLE);
+ AdvWaitEEPCmd(iop_base);
+
+ /*
+ * Write EEPROM from word 0 to word 20.
+ */
+ for (addr = ADV_EEP_DVC_CFG_BEGIN;
+ addr < ADV_EEP_DVC_CFG_END; addr++, wbuf++)
+ {
+ ushort word;
+
+ if (*charfields++) {
+ word = cpu_to_le16(*wbuf);
+ } else {
+ word = *wbuf;
+ }
+ chksum += *wbuf; /* Checksum is calculated from word values. */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, word);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ DvcSleepMilliSecond(ADV_EEP_DELAY_MS);
+ }
+
+ /*
+ * Write EEPROM checksum at word 21.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, chksum);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ wbuf++; charfields++;
+
+ /*
+ * Write EEPROM OEM name at words 22 to 29.
+ */
+ for (addr = ADV_EEP_DVC_CTL_BEGIN;
+ addr < ADV_EEP_MAX_WORD_ADDR; addr++, wbuf++)
+ {
+ ushort word;
+
+ if (*charfields++) {
+ word = cpu_to_le16(*wbuf);
+ } else {
+ word = *wbuf;
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, word);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_DISABLE);
+ AdvWaitEEPCmd(iop_base);
+ return;
+}
+
+/* a_advlib.c */
+/*
+ * AdvExeScsiQueue() - Send a request to the RISC microcode program.
+ *
+ * Allocate a carrier structure, point the carrier to the ADV_SCSI_REQ_Q,
+ * add the carrier to the ICQ (Initiator Command Queue), and tickle the
+ * RISC to notify it a new command is ready to be executed.
+ *
+ * If 'done_status' is not set to QD_DO_RETRY, then 'error_retry' will be
+ * set to SCSI_MAX_RETRY.
+ *
+ * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the microcode
+ * for DMA addresses or math operations are byte swapped to little-endian
+ * order.
+ *
+ * Return:
+ * ADV_SUCCESS(1) - The request was successfully queued.
+ * ADV_BUSY(0) - Resource unavailable; Retry again after pending
+ * request completes.
+ * ADV_ERROR(-1) - Invalid ADV_SCSI_REQ_Q request structure
+ * host IC error.
+ */
+STATIC int
+AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc,
+ ADV_SCSI_REQ_Q *scsiq)
+{
+ ulong last_int_level;
+ AdvPortAddr iop_base;
+ ADV_DCNT req_size;
+ ADV_PADDR req_paddr;
+ ADV_CARR_T *new_carrp;
+
+ ASC_ASSERT(scsiq != NULL); /* 'scsiq' should never be NULL. */
+
+ /*
+ * The ADV_SCSI_REQ_Q 'target_id' field should never exceed ADV_MAX_TID.
+ */
+ if (scsiq->target_id > ADV_MAX_TID)
+ {
+ scsiq->host_status = QHSTA_M_INVALID_DEVICE;
+ scsiq->done_status = QD_WITH_ERROR;
+ return ADV_ERROR;
+ }
+
+ iop_base = asc_dvc->iop_base;
+
+ last_int_level = DvcEnterCritical();
+
+ /*
+ * Allocate a carrier ensuring at least one carrier always
+ * remains on the freelist and initialize fields.
+ */
+ if ((new_carrp = asc_dvc->carr_freelist) == NULL)
+ {
+ DvcLeaveCritical(last_int_level);
+ return ADV_BUSY;
+ }
+ asc_dvc->carr_freelist = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(le32_to_cpu(new_carrp->next_vpa));
+ asc_dvc->carr_pending_cnt++;
+
+ /*
+ * Set the carrier to be a stopper by setting 'next_vpa'
+ * to the stopper value. The current stopper will be changed
+ * below to point to the new stopper.
+ */
+ new_carrp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
+
+ /*
+ * Clear the ADV_SCSI_REQ_Q done flag.
+ */
+ scsiq->a_flag &= ~ADV_SCSIQ_DONE;
+
+ req_size = sizeof(ADV_SCSI_REQ_Q);
+ req_paddr = DvcGetPhyAddr(asc_dvc, scsiq, (uchar *) scsiq,
+ (ADV_SDCNT *) &req_size, ADV_IS_SCSIQ_FLAG);
+
+ ASC_ASSERT(ADV_32BALIGN(req_paddr) == req_paddr);
+ ASC_ASSERT(req_size >= sizeof(ADV_SCSI_REQ_Q));
+
+ /* Wait for assertion before making little-endian */
+ req_paddr = cpu_to_le32(req_paddr);
+
+ /* Save virtual and physical address of ADV_SCSI_REQ_Q and carrier. */
+ scsiq->scsiq_ptr = cpu_to_le32(ADV_VADDR_TO_U32(scsiq));
+ scsiq->scsiq_rptr = req_paddr;
+
+ scsiq->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->icq_sp));
+ /*
+ * Every ADV_CARR_T.carr_pa is byte swapped to little-endian
+ * order during initialization.
+ */
+ scsiq->carr_pa = asc_dvc->icq_sp->carr_pa;
+
+ /*
+ * Use the current stopper to send the ADV_SCSI_REQ_Q command to
+ * the microcode. The newly allocated stopper will become the new
+ * stopper.
+ */
+ asc_dvc->icq_sp->areq_vpa = req_paddr;
+
+ /*
+ * Set the 'next_vpa' pointer for the old stopper to be the
+ * physical address of the new stopper. The RISC can only
+ * follow physical addresses.
+ */
+ asc_dvc->icq_sp->next_vpa = new_carrp->carr_pa;
+
+ /*
+ * Set the host adapter stopper pointer to point to the new carrier.
+ */
+ asc_dvc->icq_sp = new_carrp;
+
+ if (asc_dvc->chip_type == ADV_CHIP_ASC3550 ||
+ asc_dvc->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ /*
+ * Tickle the RISC to tell it to read its Command Queue Head pointer.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_TICKLE, ADV_TICKLE_A);
+ if (asc_dvc->chip_type == ADV_CHIP_ASC3550)
+ {
+ /*
+ * Clear the tickle value. In the ASC-3550 the RISC flag
+ * command 'clr_tickle_a' does not work unless the host
+ * value is cleared.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_TICKLE, ADV_TICKLE_NOP);
+ }
+ } else if (asc_dvc->chip_type == ADV_CHIP_ASC38C1600)
+ {
+ /*
+ * Notify the RISC a carrier is ready by writing the physical
+ * address of the new carrier stopper to the COMMA register.
+ */
+ AdvWriteDWordRegister(iop_base, IOPDW_COMMA,
+ le32_to_cpu(new_carrp->carr_pa));
+ }
+
+ DvcLeaveCritical(last_int_level);
+
+ return ADV_SUCCESS;
+}
+
+/*
+ * Reset SCSI Bus and purge all outstanding requests.
+ *
+ * Return Value:
+ * ADV_TRUE(1) - All requests are purged and SCSI Bus is reset.
+ * ADV_FALSE(0) - Microcode command failed.
+ * ADV_ERROR(-1) - Microcode command timed-out. Microcode or IC
+ * may be hung which requires driver recovery.
+ */
+STATIC int
+AdvResetSB(ADV_DVC_VAR *asc_dvc)
+{
+ int status;
+
+ /*
+ * Send the SCSI Bus Reset idle start idle command which asserts
+ * the SCSI Bus Reset signal.
+ */
+ status = AdvSendIdleCmd(asc_dvc, (ushort) IDLE_CMD_SCSI_RESET_START, 0L);
+ if (status != ADV_TRUE)
+ {
+ return status;
+ }
+
+ /*
+ * Delay for the specified SCSI Bus Reset hold time.
+ *
+ * The hold time delay is done on the host because the RISC has no
+ * microsecond accurate timer.
+ */
+ DvcDelayMicroSecond(asc_dvc, (ushort) ASC_SCSI_RESET_HOLD_TIME_US);
+
+ /*
+ * Send the SCSI Bus Reset end idle command which de-asserts
+ * the SCSI Bus Reset signal and purges any pending requests.
+ */
+ status = AdvSendIdleCmd(asc_dvc, (ushort) IDLE_CMD_SCSI_RESET_END, 0L);
+ if (status != ADV_TRUE)
+ {
+ return status;
+ }
+
+ DvcSleepMilliSecond((ADV_DCNT) asc_dvc->scsi_reset_wait * 1000);
+
+ return status;
+}
+
+/*
+ * Reset chip and SCSI Bus.
+ *
+ * Return Value:
+ * ADV_TRUE(1) - Chip re-initialization and SCSI Bus Reset successful.
+ * ADV_FALSE(0) - Chip re-initialization and SCSI Bus Reset failure.
+ */
+STATIC int
+AdvResetChipAndSB(ADV_DVC_VAR *asc_dvc)
+{
+ int status;
+ ushort wdtr_able, sdtr_able, tagqng_able;
+ ushort ppr_able = 0;
+ uchar tid, max_cmd[ADV_MAX_TID + 1];
+ AdvPortAddr iop_base;
+ ushort bios_sig;
+
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Save current per TID negotiated values.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ if (asc_dvc->chip_type == ADV_CHIP_ASC38C1600)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_PPR_ABLE, ppr_able);
+ }
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+
+ /*
+ * Force the AdvInitAsc3550/38C0800Driver() function to
+ * perform a SCSI Bus Reset by clearing the BIOS signature word.
+ * The initialization functions assumes a SCSI Bus Reset is not
+ * needed if the BIOS signature word is present.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_BIOS_SIGNATURE, bios_sig);
+ AdvWriteWordLram(iop_base, ASC_MC_BIOS_SIGNATURE, 0);
+
+ /*
+ * Stop chip and reset it.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_RISC_CSR, ADV_RISC_CSR_STOP);
+ AdvWriteWordRegister(iop_base, IOPW_CTRL_REG, ADV_CTRL_REG_CMD_RESET);
+ DvcSleepMilliSecond(100);
+ AdvWriteWordRegister(iop_base, IOPW_CTRL_REG, ADV_CTRL_REG_CMD_WR_IO_REG);
+
+ /*
+ * Reset Adv Library error code, if any, and try
+ * re-initializing the chip.
+ */
+ asc_dvc->err_code = 0;
+ if (asc_dvc->chip_type == ADV_CHIP_ASC38C1600)
+ {
+ status = AdvInitAsc38C1600Driver(asc_dvc);
+ }
+ else if (asc_dvc->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ status = AdvInitAsc38C0800Driver(asc_dvc);
+ } else
+ {
+ status = AdvInitAsc3550Driver(asc_dvc);
+ }
+
+ /* Translate initialization return value to status value. */
+ if (status == 0)
+ {
+ status = ADV_TRUE;
+ } else
+ {
+ status = ADV_FALSE;
+ }
+
+ /*
+ * Restore the BIOS signature word.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_BIOS_SIGNATURE, bios_sig);
+
+ /*
+ * Restore per TID negotiated values.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, wdtr_able);
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ if (asc_dvc->chip_type == ADV_CHIP_ASC38C1600)
+ {
+ AdvWriteWordLram(iop_base, ASC_MC_PPR_ABLE, ppr_able);
+ }
+ AdvWriteWordLram(iop_base, ASC_MC_TAGQNG_ABLE, tagqng_able);
+ for (tid = 0; tid <= ADV_MAX_TID; tid++)
+ {
+ AdvWriteByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ max_cmd[tid]);
+ }
+
+ return status;
+}
+
+/*
+ * Adv Library Interrupt Service Routine
+ *
+ * This function is called by a driver's interrupt service routine.
+ * The function disables and re-enables interrupts.
+ *
+ * When a microcode idle command is completed, the ADV_DVC_VAR
+ * 'idle_cmd_done' field is set to ADV_TRUE.
+ *
+ * Note: AdvISR() can be called when interrupts are disabled or even
+ * when there is no hardware interrupt condition present. It will
+ * always check for completed idle commands and microcode requests.
+ * This is an important feature that shouldn't be changed because it
+ * allows commands to be completed from polling mode loops.
+ *
+ * Return:
+ * ADV_TRUE(1) - interrupt was pending
+ * ADV_FALSE(0) - no interrupt was pending
+ */
+STATIC int
+AdvISR(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ uchar int_stat;
+ ushort target_bit;
+ ADV_CARR_T *free_carrp;
+ ADV_VADDR irq_next_vpa;
+ int flags;
+ ADV_SCSI_REQ_Q *scsiq;
+
+ flags = DvcEnterCritical();
+
+ iop_base = asc_dvc->iop_base;
+
+ /* Reading the register clears the interrupt. */
+ int_stat = AdvReadByteRegister(iop_base, IOPB_INTR_STATUS_REG);
+
+ if ((int_stat & (ADV_INTR_STATUS_INTRA | ADV_INTR_STATUS_INTRB |
+ ADV_INTR_STATUS_INTRC)) == 0)
+ {
+ DvcLeaveCritical(flags);
+ return ADV_FALSE;
+ }
+
+ /*
+ * Notify the driver of an asynchronous microcode condition by
+ * calling the ADV_DVC_VAR.async_callback function. The function
+ * is passed the microcode ASC_MC_INTRB_CODE byte value.
+ */
+ if (int_stat & ADV_INTR_STATUS_INTRB)
+ {
+ uchar intrb_code;
+
+ AdvReadByteLram(iop_base, ASC_MC_INTRB_CODE, intrb_code);
+
+ if (asc_dvc->chip_type == ADV_CHIP_ASC3550 ||
+ asc_dvc->chip_type == ADV_CHIP_ASC38C0800)
+ {
+ if (intrb_code == ADV_ASYNC_CARRIER_READY_FAILURE &&
+ asc_dvc->carr_pending_cnt != 0)
+ {
+ AdvWriteByteRegister(iop_base, IOPB_TICKLE, ADV_TICKLE_A);
+ if (asc_dvc->chip_type == ADV_CHIP_ASC3550)
+ {
+ AdvWriteByteRegister(iop_base, IOPB_TICKLE, ADV_TICKLE_NOP);
+ }
+ }
+ }
+
+ if (asc_dvc->async_callback != 0)
+ {
+ (*asc_dvc->async_callback)(asc_dvc, intrb_code);
+ }
+ }
+
+ /*
+ * Check if the IRQ stopper carrier contains a completed request.
+ */
+ while (((irq_next_vpa =
+ le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ASC_RQ_DONE) != 0)
+ {
+ /*
+ * Get a pointer to the newly completed ADV_SCSI_REQ_Q structure.
+ * The RISC will have set 'areq_vpa' to a virtual address.
+ *
+ * The firmware will have copied the ASC_SCSI_REQ_Q.scsiq_ptr
+ * field to the carrier ADV_CARR_T.areq_vpa field. The conversion
+ * below complements the conversion of ASC_SCSI_REQ_Q.scsiq_ptr'
+ * in AdvExeScsiQueue().
+ */
+ scsiq = (ADV_SCSI_REQ_Q *)
+ ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->areq_vpa));
+
+ /*
+ * Request finished with good status and the queue was not
+ * DMAed to host memory by the firmware. Set all status fields
+ * to indicate good status.
+ */
+ if ((irq_next_vpa & ASC_RQ_GOOD) != 0)
+ {
+ scsiq->done_status = QD_NO_ERROR;
+ scsiq->host_status = scsiq->scsi_status = 0;
+ scsiq->data_cnt = 0L;
+ }
+
+ /*
+ * Advance the stopper pointer to the next carrier
+ * ignoring the lower four bits. Free the previous
+ * stopper carrier.
+ */
+ free_carrp = asc_dvc->irq_sp;
+ asc_dvc->irq_sp = (ADV_CARR_T *)
+ ADV_U32_TO_VADDR(ASC_GET_CARRP(irq_next_vpa));
+
+ free_carrp->next_vpa =
+ cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+ asc_dvc->carr_freelist = free_carrp;
+ asc_dvc->carr_pending_cnt--;
+
+ ASC_ASSERT(scsiq != NULL);
+ target_bit = ADV_TID_TO_TIDMASK(scsiq->target_id);
+
+ /*
+ * Clear request microcode control flag.
+ */
+ scsiq->cntl = 0;
+
+ /*
+ * If the command that completed was a SCSI INQUIRY and
+ * LUN 0 was sent the command, then process the INQUIRY
+ * command information for the device.
+ *
+ * Note: If data returned were either VPD or CmdDt data,
+ * don't process the INQUIRY command information for
+ * the device, otherwise may erroneously set *_able bits.
+ */
+ if (scsiq->done_status == QD_NO_ERROR &&
+ scsiq->cdb[0] == INQUIRY &&
+ scsiq->target_lun == 0 &&
+ (scsiq->cdb[1] & ADV_INQ_RTN_VPD_AND_CMDDT)
+ == ADV_INQ_RTN_STD_INQUIRY_DATA)
+ {
+ AdvInquiryHandling(asc_dvc, scsiq);
+ }
+
+ /*
+ * Notify the driver of the completed request by passing
+ * the ADV_SCSI_REQ_Q pointer to its callback function.
+ */
+ scsiq->a_flag |= ADV_SCSIQ_DONE;
+ (*asc_dvc->isr_callback)(asc_dvc, scsiq);
+ /*
+ * Note: After the driver callback function is called, 'scsiq'
+ * can no longer be referenced.
+ *
+ * Fall through and continue processing other completed
+ * requests...
+ */
+
+ /*
+ * Disable interrupts again in case the driver inadvertently
+ * enabled interrupts in its callback function.
+ *
+ * The DvcEnterCritical() return value is ignored, because
+ * the 'flags' saved when AdvISR() was first entered will be
+ * used to restore the interrupt flag on exit.
+ */
+ (void) DvcEnterCritical();
+ }
+ DvcLeaveCritical(flags);
+ return ADV_TRUE;
+}
+
+/*
+ * Send an idle command to the chip and wait for completion.
+ *
+ * Command completion is polled for once per microsecond.
+ *
+ * The function can be called from anywhere including an interrupt handler.
+ * But the function is not re-entrant, so it uses the DvcEnter/LeaveCritical()
+ * functions to prevent reentrancy.
+ *
+ * Return Values:
+ * ADV_TRUE - command completed successfully
+ * ADV_FALSE - command failed
+ * ADV_ERROR - command timed out
+ */
+STATIC int
+AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc,
+ ushort idle_cmd,
+ ADV_DCNT idle_cmd_parameter)
+{
+ ulong last_int_level;
+ int result;
+ ADV_DCNT i, j;
+ AdvPortAddr iop_base;
+
+ last_int_level = DvcEnterCritical();
+
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Clear the idle command status which is set by the microcode
+ * to a non-zero value to indicate when the command is completed.
+ * The non-zero result is one of the IDLE_CMD_STATUS_* values
+ * defined in a_advlib.h.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_IDLE_CMD_STATUS, (ushort) 0);
+
+ /*
+ * Write the idle command value after the idle command parameter
+ * has been written to avoid a race condition. If the order is not
+ * followed, the microcode may process the idle command before the
+ * parameters have been written to LRAM.
+ */
+ AdvWriteDWordLramNoSwap(iop_base, ASC_MC_IDLE_CMD_PARAMETER,
+ cpu_to_le32(idle_cmd_parameter));
+ AdvWriteWordLram(iop_base, ASC_MC_IDLE_CMD, idle_cmd);
+
+ /*
+ * Tickle the RISC to tell it to process the idle command.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_TICKLE, ADV_TICKLE_B);
+ if (asc_dvc->chip_type == ADV_CHIP_ASC3550)
+ {
+ /*
+ * Clear the tickle value. In the ASC-3550 the RISC flag
+ * command 'clr_tickle_b' does not work unless the host
+ * value is cleared.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_TICKLE, ADV_TICKLE_NOP);
+ }
+
+ /* Wait for up to 100 millisecond for the idle command to timeout. */
+ for (i = 0; i < SCSI_WAIT_100_MSEC; i++)
+ {
+ /* Poll once each microsecond for command completion. */
+ for (j = 0; j < SCSI_US_PER_MSEC; j++)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_IDLE_CMD_STATUS, result);
+ if (result != 0)
+ {
+ DvcLeaveCritical(last_int_level);
+ return result;
+ }
+ DvcDelayMicroSecond(asc_dvc, (ushort) 1);
+ }
+ }
+
+ ASC_ASSERT(0); /* The idle command should never timeout. */
+ DvcLeaveCritical(last_int_level);
+ return ADV_ERROR;
+}
+
+/*
+ * Inquiry Information Byte 7 Handling
+ *
+ * Handle SCSI Inquiry Command information for a device by setting
+ * microcode operating variables that affect WDTR, SDTR, and Tag
+ * Queuing.
+ */
+STATIC void
+AdvInquiryHandling(
+ ADV_DVC_VAR *asc_dvc,
+ ADV_SCSI_REQ_Q *scsiq)
+{
+ AdvPortAddr iop_base;
+ uchar tid;
+ ADV_SCSI_INQUIRY *inq;
+ ushort tidmask;
+ ushort cfg_word;
+
+ /*
+ * AdvInquiryHandling() requires up to INQUIRY information Byte 7
+ * to be available.
+ *
+ * If less than 8 bytes of INQUIRY information were requested or less
+ * than 8 bytes were transferred, then return. cdb[4] is the request
+ * length and the ADV_SCSI_REQ_Q 'data_cnt' field is set by the
+ * microcode to the transfer residual count.
+ */
+
+ if (scsiq->cdb[4] < 8 ||
+ (scsiq->cdb[4] - le32_to_cpu(scsiq->data_cnt)) < 8)
+ {
+ return;
+ }
+
+ iop_base = asc_dvc->iop_base;
+ tid = scsiq->target_id;
+
+ inq = (ADV_SCSI_INQUIRY *) scsiq->vdata_addr;
+
+ /*
+ * WDTR, SDTR, and Tag Queuing cannot be enabled for old devices.
+ */
+ if (ADV_INQ_RESPONSE_FMT(inq) < 2 && ADV_INQ_ANSI_VER(inq) < 2)
+ {
+ return;
+ } else
+ {
+ /*
+ * INQUIRY Byte 7 Handling
+ *
+ * Use a device's INQUIRY byte 7 to determine whether it
+ * supports WDTR, SDTR, and Tag Queuing. If the feature
+ * is enabled in the EEPROM and the device supports the
+ * feature, then enable it in the microcode.
+ */
+
+ tidmask = ADV_TID_TO_TIDMASK(tid);
+
+ /*
+ * Wide Transfers
+ *
+ * If the EEPROM enabled WDTR for the device and the device
+ * supports wide bus (16 bit) transfers, then turn on the
+ * device's 'wdtr_able' bit and write the new value to the
+ * microcode.
+ */
+ if ((asc_dvc->wdtr_able & tidmask) && ADV_INQ_WIDE16(inq))
+ {
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, cfg_word);
+ if ((cfg_word & tidmask) == 0)
+ {
+ cfg_word |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, cfg_word);
+
+ /*
+ * Clear the microcode "SDTR negotiation" and "WDTR
+ * negotiation" done indicators for the target to cause
+ * it to negotiate with the new setting set above.
+ * WDTR when accepted causes the target to enter
+ * asynchronous mode, so SDTR must be negotiated.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_DONE, cfg_word);
+ cfg_word &= ~tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_DONE, cfg_word);
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_DONE, cfg_word);
+ cfg_word &= ~tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_DONE, cfg_word);
+ }
+ }
+
+ /*
+ * Synchronous Transfers
+ *
+ * If the EEPROM enabled SDTR for the device and the device
+ * supports synchronous transfers, then turn on the device's
+ * 'sdtr_able' bit. Write the new value to the microcode.
+ */
+ if ((asc_dvc->sdtr_able & tidmask) && ADV_INQ_SYNC(inq))
+ {
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, cfg_word);
+ if ((cfg_word & tidmask) == 0)
+ {
+ cfg_word |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, cfg_word);
+
+ /*
+ * Clear the microcode "SDTR negotiation" done indicator
+ * for the target to cause it to negotiate with the new
+ * setting set above.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_DONE, cfg_word);
+ cfg_word &= ~tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_DONE, cfg_word);
+ }
+ }
+ /*
+ * If the Inquiry data included enough space for the SPI-3
+ * Clocking field, then check if DT mode is supported.
+ */
+ if (asc_dvc->chip_type == ADV_CHIP_ASC38C1600 &&
+ (scsiq->cdb[4] >= 57 ||
+ (scsiq->cdb[4] - le32_to_cpu(scsiq->data_cnt)) >= 57))
+ {
+ /*
+ * PPR (Parallel Protocol Request) Capable
+ *
+ * If the device supports DT mode, then it must be PPR capable.
+ * The PPR message will be used in place of the SDTR and WDTR
+ * messages to negotiate synchronous speed and offset, transfer
+ * width, and protocol options.
+ */
+ if (ADV_INQ_CLOCKING(inq) & ADV_INQ_CLOCKING_DT_ONLY)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_PPR_ABLE, asc_dvc->ppr_able);
+ asc_dvc->ppr_able |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_PPR_ABLE, asc_dvc->ppr_able);
+ }
+ }
+
+ /*
+ * If the EEPROM enabled Tag Queuing for the device and the
+ * device supports Tag Queueing, then turn on the device's
+ * 'tagqng_enable' bit in the microcode and set the microcode
+ * maximum command count to the ADV_DVC_VAR 'max_dvc_qng'
+ * value.
+ *
+ * Tag Queuing is disabled for the BIOS which runs in polled
+ * mode and would see no benefit from Tag Queuing. Also by
+ * disabling Tag Queuing in the BIOS devices with Tag Queuing
+ * bugs will at least work with the BIOS.
+ */
+ if ((asc_dvc->tagqng_able & tidmask) && ADV_INQ_CMD_QUEUE(inq))
+ {
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, cfg_word);
+ cfg_word |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_TAGQNG_ABLE, cfg_word);
+
+ AdvWriteByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ asc_dvc->max_dvc_qng);
+ }
+ }
+}
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/scsi/advansys.h b/drivers/scsi/advansys.h
new file mode 100644
index 000000000000..3f4bde02302e
--- /dev/null
+++ b/drivers/scsi/advansys.h
@@ -0,0 +1,36 @@
+/*
+ * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-2000 Advanced System Products, Inc.
+ * Copyright (c) 2000-2001 ConnectCom Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * As of March 8, 2000 Advanced System Products, Inc. (AdvanSys)
+ * changed its name to ConnectCom Solutions, Inc.
+ *
+ */
+
+#ifndef _ADVANSYS_H
+#define _ADVANSYS_H
+
+/*
+ * Scsi_Host_Template function prototypes.
+ */
+int advansys_detect(struct scsi_host_template *);
+int advansys_release(struct Scsi_Host *);
+const char *advansys_info(struct Scsi_Host *);
+int advansys_queuecommand(struct scsi_cmnd *, void (* done)(struct scsi_cmnd *));
+int advansys_reset(struct scsi_cmnd *);
+int advansys_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int[]);
+static int advansys_slave_configure(struct scsi_device *);
+
+/* init/main.c setup function */
+void advansys_setup(char *, int *);
+
+#endif /* _ADVANSYS_H */
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
new file mode 100644
index 000000000000..d7b8efe86401
--- /dev/null
+++ b/drivers/scsi/aha152x.c
@@ -0,0 +1,3982 @@
+/* aha152x.c -- Adaptec AHA-152x driver
+ * Author: Jürgen E. Fischer, fischer@norbit.de
+ * Copyright 1993-2004 Jürgen E. Fischer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *
+ * $Id: aha152x.c,v 2.7 2004/01/24 11:42:59 fischer Exp $
+ *
+ * $Log: aha152x.c,v $
+ * Revision 2.7 2004/01/24 11:42:59 fischer
+ * - gather code that is not used by PCMCIA at the end
+ * - move request_region for !PCMCIA case to detection
+ * - migration to new scsi host api (remove legacy code)
+ * - free host scribble before scsi_done
+ * - fix error handling
+ * - one isapnp device added to id_table
+ *
+ * Revision 2.6 2003/10/30 20:52:47 fischer
+ * - interfaces changes for kernel 2.6
+ * - aha152x_probe_one introduced for pcmcia stub
+ * - fixed pnpdev handling
+ * - instead of allocation a new one, reuse command for request sense after check condition and reset
+ * - fixes race in is_complete
+ *
+ * Revision 2.5 2002/04/14 11:24:53 fischer
+ * - isapnp support
+ * - abort fixed
+ * - 2.5 support
+ *
+ * Revision 2.4 2000/12/16 12:53:56 fischer
+ * - allow REQUEST SENSE to be queued
+ * - handle shared PCI interrupts
+ *
+ * Revision 2.3 2000/11/04 16:40:26 fischer
+ * - handle data overruns
+ * - extend timeout for data phases
+ *
+ * Revision 2.2 2000/08/08 19:54:53 fischer
+ * - minor changes
+ *
+ * Revision 2.1 2000/05/17 16:23:17 fischer
+ * - signature update
+ * - fix for data out w/o scatter gather
+ *
+ * Revision 2.0 1999/12/25 15:07:32 fischer
+ * - interrupt routine completly reworked
+ * - basic support for new eh code
+ *
+ * Revision 1.21 1999/11/10 23:46:36 fischer
+ * - default to synchronous operation
+ * - synchronous negotiation fixed
+ * - added timeout to loops
+ * - debugging output can be controlled through procfs
+ *
+ * Revision 1.20 1999/11/07 18:37:31 fischer
+ * - synchronous operation works
+ * - resid support for sg driver
+ *
+ * Revision 1.19 1999/11/02 22:39:59 fischer
+ * - moved leading comments to README.aha152x
+ * - new additional module parameters
+ * - updates for 2.3
+ * - support for the Tripace TC1550 controller
+ * - interrupt handling changed
+ *
+ * Revision 1.18 1996/09/07 20:10:40 fischer
+ * - fixed can_queue handling (multiple outstanding commands working again)
+ *
+ * Revision 1.17 1996/08/17 16:05:14 fischer
+ * - biosparam improved
+ * - interrupt verification
+ * - updated documentation
+ * - cleanups
+ *
+ * Revision 1.16 1996/06/09 00:04:56 root
+ * - added configuration symbols for insmod (aha152x/aha152x1)
+ *
+ * Revision 1.15 1996/04/30 14:52:06 fischer
+ * - proc info fixed
+ * - support for extended translation for >1GB disks
+ *
+ * Revision 1.14 1996/01/17 15:11:20 fischer
+ * - fixed lockup in MESSAGE IN phase after reconnection
+ *
+ * Revision 1.13 1996/01/09 02:15:53 fischer
+ * - some cleanups
+ * - moved request_irq behind controller initialization
+ * (to avoid spurious interrupts)
+ *
+ * Revision 1.12 1995/12/16 12:26:07 fischer
+ * - barrier()s added
+ * - configurable RESET delay added
+ *
+ * Revision 1.11 1995/12/06 21:18:35 fischer
+ * - some minor updates
+ *
+ * Revision 1.10 1995/07/22 19:18:45 fischer
+ * - support for 2 controllers
+ * - started synchronous data transfers (not working yet)
+ *
+ * Revision 1.9 1995/03/18 09:20:24 root
+ * - patches for PCMCIA and modules
+ *
+ * Revision 1.8 1995/01/21 22:07:19 root
+ * - snarf_region => request_region
+ * - aha152x_intr interface change
+ *
+ * Revision 1.7 1995/01/02 23:19:36 root
+ * - updated COMMAND_SIZE to cmd_len
+ * - changed sti() to restore_flags()
+ * - fixed some #ifdef which generated warnings
+ *
+ * Revision 1.6 1994/11/24 20:35:27 root
+ * - problem with odd number of bytes in fifo fixed
+ *
+ * Revision 1.5 1994/10/30 14:39:56 root
+ * - abort code fixed
+ * - debugging improved
+ *
+ * Revision 1.4 1994/09/12 11:33:01 root
+ * - irqaction to request_irq
+ * - abortion updated
+ *
+ * Revision 1.3 1994/08/04 13:53:05 root
+ * - updates for mid-level-driver changes
+ * - accept unexpected BUSFREE phase as error condition
+ * - parity check now configurable
+ *
+ * Revision 1.2 1994/07/03 12:56:36 root
+ * - cleaned up debugging code
+ * - more tweaking on reset delays
+ * - updated abort/reset code (pretty untested...)
+ *
+ * Revision 1.1 1994/05/28 21:18:49 root
+ * - update for mid-level interface change (abort-reset)
+ * - delays after resets adjusted for some slow devices
+ *
+ * Revision 1.0 1994/03/25 12:52:00 root
+ * - Fixed "more data than expected" problem
+ * - added new BIOS signatures
+ *
+ * Revision 0.102 1994/01/31 20:44:12 root
+ * - minor changes in insw/outsw handling
+ *
+ * Revision 0.101 1993/12/13 01:16:27 root
+ * - fixed STATUS phase (non-GOOD stati were dropped sometimes;
+ * fixes problems with CD-ROM sector size detection & media change)
+ *
+ * Revision 0.100 1993/12/10 16:58:47 root
+ * - fix for unsuccessful selections in case of non-continuous id assignments
+ * on the scsi bus.
+ *
+ * Revision 0.99 1993/10/24 16:19:59 root
+ * - fixed DATA IN (rare read errors gone)
+ *
+ * Revision 0.98 1993/10/17 12:54:44 root
+ * - fixed some recent fixes (shame on me)
+ * - moved initialization of scratch area to aha152x_queue
+ *
+ * Revision 0.97 1993/10/09 18:53:53 root
+ * - DATA IN fixed. Rarely left data in the fifo.
+ *
+ * Revision 0.96 1993/10/03 00:53:59 root
+ * - minor changes on DATA IN
+ *
+ * Revision 0.95 1993/09/24 10:36:01 root
+ * - change handling of MSGI after reselection
+ * - fixed sti/cli
+ * - minor changes
+ *
+ * Revision 0.94 1993/09/18 14:08:22 root
+ * - fixed bug in multiple outstanding command code
+ * - changed detection
+ * - support for kernel command line configuration
+ * - reset corrected
+ * - changed message handling
+ *
+ * Revision 0.93 1993/09/15 20:41:19 root
+ * - fixed bugs with multiple outstanding commands
+ *
+ * Revision 0.92 1993/09/13 02:46:33 root
+ * - multiple outstanding commands work (no problems with IBM drive)
+ *
+ * Revision 0.91 1993/09/12 20:51:46 root
+ * added multiple outstanding commands
+ * (some problem with this $%&? IBM device remain)
+ *
+ * Revision 0.9 1993/09/12 11:11:22 root
+ * - corrected auto-configuration
+ * - changed the auto-configuration (added some '#define's)
+ * - added support for dis-/reconnection
+ *
+ * Revision 0.8 1993/09/06 23:09:39 root
+ * - added support for the drive activity light
+ * - minor changes
+ *
+ * Revision 0.7 1993/09/05 14:30:15 root
+ * - improved phase detection
+ * - now using the new snarf_region code of 0.99pl13
+ *
+ * Revision 0.6 1993/09/02 11:01:38 root
+ * first public release; added some signatures and biosparam()
+ *
+ * Revision 0.5 1993/08/30 10:23:30 root
+ * fixed timing problems with my IBM drive
+ *
+ * Revision 0.4 1993/08/29 14:06:52 root
+ * fixed some problems with timeouts due incomplete commands
+ *
+ * Revision 0.3 1993/08/28 15:55:03 root
+ * writing data works too. mounted and worked on a dos partition
+ *
+ * Revision 0.2 1993/08/27 22:42:07 root
+ * reading data works. Mounted a msdos partition.
+ *
+ * Revision 0.1 1993/08/25 13:38:30 root
+ * first "damn thing doesn't work" version
+ *
+ * Revision 0.0 1993/08/14 19:54:25 root
+ * empty function bodies; detect() works.
+ *
+ *
+ **************************************************************************
+
+ see Documentation/scsi/aha152x.txt for configuration details
+
+ **************************************************************************/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <linux/blkdev.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/isapnp.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <asm/semaphore.h>
+#include <scsi/scsicam.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aha152x.h"
+
+
+/* DEFINES */
+
+/* For PCMCIA cards, always use AUTOCONF */
+#if defined(PCMCIA) || defined(MODULE)
+#if !defined(AUTOCONF)
+#define AUTOCONF
+#endif
+#endif
+
+#if !defined(AUTOCONF) && !defined(SETUP0)
+#error define AUTOCONF or SETUP0
+#endif
+
+#if defined(AHA152X_DEBUG)
+#define DEBUG_DEFAULT debug_eh
+
+#define DPRINTK(when,msgs...) \
+ do { if(HOSTDATA(shpnt)->debug & (when)) printk(msgs); } while(0)
+
+#define DO_LOCK(flags) \
+ do { \
+ if(spin_is_locked(&QLOCK)) { \
+ DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \
+ } \
+ DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \
+ spin_lock_irqsave(&QLOCK,flags); \
+ DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \
+ QLOCKER=__FUNCTION__; \
+ QLOCKERL=__LINE__; \
+ } while(0)
+
+#define DO_UNLOCK(flags) \
+ do { \
+ DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \
+ spin_unlock_irqrestore(&QLOCK,flags); \
+ DPRINTK(debug_locks, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \
+ QLOCKER="(not locked)"; \
+ QLOCKERL=0; \
+ } while(0)
+
+#else
+#define DPRINTK(when,msgs...)
+#define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags)
+#define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags)
+#endif
+
+#define LEAD "(scsi%d:%d:%d) "
+#define WARN_LEAD KERN_WARNING LEAD
+#define INFO_LEAD KERN_INFO LEAD
+#define NOTE_LEAD KERN_NOTICE LEAD
+#define ERR_LEAD KERN_ERR LEAD
+#define DEBUG_LEAD KERN_DEBUG LEAD
+#define CMDINFO(cmd) \
+ (cmd) ? ((cmd)->device->host->host_no) : -1, \
+ (cmd) ? ((cmd)->device->id & 0x0f) : -1, \
+ (cmd) ? ((cmd)->device->lun & 0x07) : -1
+
+#define DELAY_DEFAULT 1000
+
+#if defined(PCMCIA)
+#define IRQ_MIN 0
+#define IRQ_MAX 16
+#else
+#define IRQ_MIN 9
+#if defined(__PPC)
+#define IRQ_MAX (NR_IRQS-1)
+#else
+#define IRQ_MAX 12
+#endif
+#endif
+
+enum {
+ not_issued = 0x0001, /* command not yet issued */
+ selecting = 0x0002, /* target is beeing selected */
+ identified = 0x0004, /* IDENTIFY was sent */
+ disconnected = 0x0008, /* target disconnected */
+ completed = 0x0010, /* target sent COMMAND COMPLETE */
+ aborted = 0x0020, /* ABORT was sent */
+ resetted = 0x0040, /* BUS DEVICE RESET was sent */
+ spiordy = 0x0080, /* waiting for SPIORDY to raise */
+ syncneg = 0x0100, /* synchronous negotiation in progress */
+ aborting = 0x0200, /* ABORT is pending */
+ resetting = 0x0400, /* BUS DEVICE RESET is pending */
+ check_condition = 0x0800, /* requesting sense after CHECK CONDITION */
+};
+
+MODULE_AUTHOR("Jürgen Fischer");
+MODULE_DESCRIPTION(AHA152X_REVID);
+MODULE_LICENSE("GPL");
+
+#if !defined(PCMCIA)
+#if defined(MODULE)
+static int io[] = {0, 0};
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io,"base io address of controller");
+
+static int irq[] = {0, 0};
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq,"interrupt for controller");
+
+static int scsiid[] = {7, 7};
+module_param_array(scsiid, int, NULL, 0);
+MODULE_PARM_DESC(scsiid,"scsi id of controller");
+
+static int reconnect[] = {1, 1};
+module_param_array(reconnect, int, NULL, 0);
+MODULE_PARM_DESC(reconnect,"allow targets to disconnect");
+
+static int parity[] = {1, 1};
+module_param_array(parity, int, NULL, 0);
+MODULE_PARM_DESC(parity,"use scsi parity");
+
+static int sync[] = {1, 1};
+module_param_array(sync, int, NULL, 0);
+MODULE_PARM_DESC(sync,"use synchronous transfers");
+
+static int delay[] = {DELAY_DEFAULT, DELAY_DEFAULT};
+module_param_array(delay, int, NULL, 0);
+MODULE_PARM_DESC(delay,"scsi reset delay");
+
+static int exttrans[] = {0, 0};
+module_param_array(exttrans, int, NULL, 0);
+MODULE_PARM_DESC(exttrans,"use extended translation");
+
+#if !defined(AHA152X_DEBUG)
+static int aha152x[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
+module_param_array(aha152x, int, NULL, 0);
+MODULE_PARM_DESC(aha152x, "parameters for first controller");
+
+static int aha152x1[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
+module_param_array(aha152x1, int, NULL, 0);
+MODULE_PARM_DESC(aha152x1, "parameters for second controller");
+#else
+static int debug[] = {DEBUG_DEFAULT, DEBUG_DEFAULT};
+module_param_array(debug, int, NULL, 0);
+MODULE_PARM_DESC(debug, "flags for driver debugging");
+
+static int aha152x[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
+module_param_array(aha152x, int, NULL, 0);
+MODULE_PARM_DESC(aha152x, "parameters for first controller");
+
+static int aha152x1[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
+module_param_array(aha152x1, int, NULL, 0);
+MODULE_PARM_DESC(aha152x1, "parameters for second controller");
+#endif /* !defined(AHA152X_DEBUG) */
+#endif /* MODULE */
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id id_table[] __devinitdata = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1505), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('A','D','P'), ISAPNP_FUNCTION(0x1530), 0 },
+ { ISAPNP_DEVICE_SINGLE_END, }
+};
+MODULE_DEVICE_TABLE(isapnp, id_table);
+#endif /* ISAPNP */
+
+#endif /* !PCMCIA */
+
+static int registered_count=0;
+static struct Scsi_Host *aha152x_host[2];
+static Scsi_Host_Template aha152x_driver_template;
+
+/*
+ * internal states of the host
+ *
+ */
+enum aha152x_state {
+ idle=0,
+ unknown,
+ seldo,
+ seldi,
+ selto,
+ busfree,
+ msgo,
+ cmd,
+ msgi,
+ status,
+ datai,
+ datao,
+ parerr,
+ rsti,
+ maxstate
+};
+
+/*
+ * current state information of the host
+ *
+ */
+struct aha152x_hostdata {
+ Scsi_Cmnd *issue_SC;
+ /* pending commands to issue */
+
+ Scsi_Cmnd *current_SC;
+ /* current command on the bus */
+
+ Scsi_Cmnd *disconnected_SC;
+ /* commands that disconnected */
+
+ Scsi_Cmnd *done_SC;
+ /* command that was completed */
+
+ spinlock_t lock;
+ /* host lock */
+
+#if defined(AHA152X_DEBUG)
+ const char *locker;
+ /* which function has the lock */
+ int lockerl; /* where did it get it */
+
+ int debug; /* current debugging setting */
+#endif
+
+#if defined(AHA152X_STAT)
+ int total_commands;
+ int disconnections;
+ int busfree_without_any_action;
+ int busfree_without_old_command;
+ int busfree_without_new_command;
+ int busfree_without_done_command;
+ int busfree_with_check_condition;
+ int count[maxstate];
+ int count_trans[maxstate];
+ unsigned long time[maxstate];
+#endif
+
+ int commands; /* current number of commands */
+
+ int reconnect; /* disconnection allowed */
+ int parity; /* parity checking enabled */
+ int synchronous; /* synchronous transferes enabled */
+ int delay; /* reset out delay */
+ int ext_trans; /* extended translation enabled */
+
+ int swint; /* software-interrupt was fired during detect() */
+ int service; /* bh needs to be run */
+ int in_intr; /* bh is running */
+
+ /* current state,
+ previous state,
+ last state different from current state */
+ enum aha152x_state state, prevstate, laststate;
+
+ int target;
+ /* reconnecting target */
+
+ unsigned char syncrate[8];
+ /* current synchronous transfer agreements */
+
+ unsigned char syncneg[8];
+ /* 0: no negotiation;
+ * 1: negotiation in progress;
+ * 2: negotiation completed
+ */
+
+ int cmd_i;
+ /* number of sent bytes of current command */
+
+ int msgi_len;
+ /* number of received message bytes */
+ unsigned char msgi[256];
+ /* received message bytes */
+
+ int msgo_i, msgo_len;
+ /* number of sent bytes and length of current messages */
+ unsigned char msgo[256];
+ /* pending messages */
+
+ int data_len;
+ /* number of sent/received bytes in dataphase */
+
+ unsigned long io_port0;
+ unsigned long io_port1;
+
+#ifdef __ISAPNP__
+ struct pnp_dev *pnpdev;
+#endif
+};
+
+
+/*
+ * host specific command extension
+ *
+ */
+struct aha152x_scdata {
+ Scsi_Cmnd *next; /* next sc in queue */
+ struct semaphore *sem; /* semaphore to block on */
+};
+
+
+/* access macros for hostdata */
+
+#define HOSTDATA(shpnt) ((struct aha152x_hostdata *) &shpnt->hostdata)
+
+#define HOSTNO ((shpnt)->host_no)
+
+#define CURRENT_SC (HOSTDATA(shpnt)->current_SC)
+#define DONE_SC (HOSTDATA(shpnt)->done_SC)
+#define ISSUE_SC (HOSTDATA(shpnt)->issue_SC)
+#define DISCONNECTED_SC (HOSTDATA(shpnt)->disconnected_SC)
+#define QLOCK (HOSTDATA(shpnt)->lock)
+#define QLOCKER (HOSTDATA(shpnt)->locker)
+#define QLOCKERL (HOSTDATA(shpnt)->lockerl)
+
+#define STATE (HOSTDATA(shpnt)->state)
+#define PREVSTATE (HOSTDATA(shpnt)->prevstate)
+#define LASTSTATE (HOSTDATA(shpnt)->laststate)
+
+#define RECONN_TARGET (HOSTDATA(shpnt)->target)
+
+#define CMD_I (HOSTDATA(shpnt)->cmd_i)
+
+#define MSGO(i) (HOSTDATA(shpnt)->msgo[i])
+#define MSGO_I (HOSTDATA(shpnt)->msgo_i)
+#define MSGOLEN (HOSTDATA(shpnt)->msgo_len)
+#define ADDMSGO(x) (MSGOLEN<256 ? (void)(MSGO(MSGOLEN++)=x) : aha152x_error(shpnt,"MSGO overflow"))
+
+#define MSGI(i) (HOSTDATA(shpnt)->msgi[i])
+#define MSGILEN (HOSTDATA(shpnt)->msgi_len)
+#define ADDMSGI(x) (MSGILEN<256 ? (void)(MSGI(MSGILEN++)=x) : aha152x_error(shpnt,"MSGI overflow"))
+
+#define DATA_LEN (HOSTDATA(shpnt)->data_len)
+
+#define SYNCRATE (HOSTDATA(shpnt)->syncrate[CURRENT_SC->device->id])
+#define SYNCNEG (HOSTDATA(shpnt)->syncneg[CURRENT_SC->device->id])
+
+#define DELAY (HOSTDATA(shpnt)->delay)
+#define EXT_TRANS (HOSTDATA(shpnt)->ext_trans)
+#define TC1550 (HOSTDATA(shpnt)->tc1550)
+#define RECONNECT (HOSTDATA(shpnt)->reconnect)
+#define PARITY (HOSTDATA(shpnt)->parity)
+#define SYNCHRONOUS (HOSTDATA(shpnt)->synchronous)
+
+#define HOSTIOPORT0 (HOSTDATA(shpnt)->io_port0)
+#define HOSTIOPORT1 (HOSTDATA(shpnt)->io_port1)
+
+#define SCDATA(SCpnt) ((struct aha152x_scdata *) (SCpnt)->host_scribble)
+#define SCNEXT(SCpnt) SCDATA(SCpnt)->next
+#define SCSEM(SCpnt) SCDATA(SCpnt)->sem
+
+#define SG_ADDRESS(buffer) ((char *) (page_address((buffer)->page)+(buffer)->offset))
+
+/* state handling */
+static void seldi_run(struct Scsi_Host *shpnt);
+static void seldo_run(struct Scsi_Host *shpnt);
+static void selto_run(struct Scsi_Host *shpnt);
+static void busfree_run(struct Scsi_Host *shpnt);
+
+static void msgo_init(struct Scsi_Host *shpnt);
+static void msgo_run(struct Scsi_Host *shpnt);
+static void msgo_end(struct Scsi_Host *shpnt);
+
+static void cmd_init(struct Scsi_Host *shpnt);
+static void cmd_run(struct Scsi_Host *shpnt);
+static void cmd_end(struct Scsi_Host *shpnt);
+
+static void datai_init(struct Scsi_Host *shpnt);
+static void datai_run(struct Scsi_Host *shpnt);
+static void datai_end(struct Scsi_Host *shpnt);
+
+static void datao_init(struct Scsi_Host *shpnt);
+static void datao_run(struct Scsi_Host *shpnt);
+static void datao_end(struct Scsi_Host *shpnt);
+
+static void status_run(struct Scsi_Host *shpnt);
+
+static void msgi_run(struct Scsi_Host *shpnt);
+static void msgi_end(struct Scsi_Host *shpnt);
+
+static void parerr_run(struct Scsi_Host *shpnt);
+static void rsti_run(struct Scsi_Host *shpnt);
+
+static void is_complete(struct Scsi_Host *shpnt);
+
+/*
+ * driver states
+ *
+ */
+static struct {
+ char *name;
+ void (*init)(struct Scsi_Host *);
+ void (*run)(struct Scsi_Host *);
+ void (*end)(struct Scsi_Host *);
+ int spio;
+} states[] = {
+ { "idle", NULL, NULL, NULL, 0},
+ { "unknown", NULL, NULL, NULL, 0},
+ { "seldo", NULL, seldo_run, NULL, 0},
+ { "seldi", NULL, seldi_run, NULL, 0},
+ { "selto", NULL, selto_run, NULL, 0},
+ { "busfree", NULL, busfree_run, NULL, 0},
+ { "msgo", msgo_init, msgo_run, msgo_end, 1},
+ { "cmd", cmd_init, cmd_run, cmd_end, 1},
+ { "msgi", NULL, msgi_run, msgi_end, 1},
+ { "status", NULL, status_run, NULL, 1},
+ { "datai", datai_init, datai_run, datai_end, 0},
+ { "datao", datao_init, datao_run, datao_end, 0},
+ { "parerr", NULL, parerr_run, NULL, 0},
+ { "rsti", NULL, rsti_run, NULL, 0},
+};
+
+/* setup & interrupt */
+static irqreturn_t intr(int irq, void *dev_id, struct pt_regs *);
+static void reset_ports(struct Scsi_Host *shpnt);
+static void aha152x_error(struct Scsi_Host *shpnt, char *msg);
+static void done(struct Scsi_Host *shpnt, int error);
+
+/* diagnostics */
+static void disp_ports(struct Scsi_Host *shpnt);
+static void show_command(Scsi_Cmnd * ptr);
+static void show_queues(struct Scsi_Host *shpnt);
+static void disp_enintr(struct Scsi_Host *shpnt);
+
+
+/*
+ * queue services:
+ *
+ */
+static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+ Scsi_Cmnd *end;
+
+ SCNEXT(new_SC) = NULL;
+ if (!*SC)
+ *SC = new_SC;
+ else {
+ for (end = *SC; SCNEXT(end); end = SCNEXT(end))
+ ;
+ SCNEXT(end) = new_SC;
+ }
+}
+
+static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd ** SC)
+{
+ Scsi_Cmnd *ptr;
+
+ ptr = *SC;
+ if (ptr) {
+ *SC = SCNEXT(*SC);
+ SCNEXT(ptr)=NULL;
+ }
+ return ptr;
+}
+
+static inline Scsi_Cmnd *remove_lun_SC(Scsi_Cmnd ** SC, int target, int lun)
+{
+ Scsi_Cmnd *ptr, *prev;
+
+ for (ptr = *SC, prev = NULL;
+ ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
+ prev = ptr, ptr = SCNEXT(ptr))
+ ;
+
+ if (ptr) {
+ if (prev)
+ SCNEXT(prev) = SCNEXT(ptr);
+ else
+ *SC = SCNEXT(ptr);
+
+ SCNEXT(ptr)=NULL;
+ }
+
+ return ptr;
+}
+
+static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, Scsi_Cmnd *SCp)
+{
+ Scsi_Cmnd *ptr, *prev;
+
+ for (ptr = *SC, prev = NULL;
+ ptr && SCp!=ptr;
+ prev = ptr, ptr = SCNEXT(ptr))
+ ;
+
+ if (ptr) {
+ if (prev)
+ SCNEXT(prev) = SCNEXT(ptr);
+ else
+ *SC = SCNEXT(ptr);
+
+ SCNEXT(ptr)=NULL;
+ }
+
+ return ptr;
+}
+
+static inline struct Scsi_Host *lookup_irq(int irqno)
+{
+ int i;
+
+ for(i=0; i<ARRAY_SIZE(aha152x_host); i++)
+ if(aha152x_host[i] && aha152x_host[i]->irq==irqno)
+ return aha152x_host[i];
+
+ return NULL;
+}
+
+static irqreturn_t swintr(int irqno, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *shpnt = lookup_irq(irqno);
+
+ if (!shpnt) {
+ printk(KERN_ERR "aha152x: catched software interrupt %d for unknown controller.\n", irqno);
+ return IRQ_NONE;
+ }
+
+ HOSTDATA(shpnt)->swint++;
+
+ SETPORT(DMACNTRL0, INTEN);
+ return IRQ_HANDLED;
+}
+
+struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
+{
+ struct Scsi_Host *shpnt;
+
+ shpnt = scsi_host_alloc(&aha152x_driver_template, sizeof(struct aha152x_hostdata));
+ if (!shpnt) {
+ printk(KERN_ERR "aha152x: scsi_host_alloc failed\n");
+ return NULL;
+ }
+
+ /* need to have host registered before triggering any interrupt */
+ aha152x_host[registered_count] = shpnt;
+
+ memset(HOSTDATA(shpnt), 0, sizeof *HOSTDATA(shpnt));
+
+ shpnt->io_port = setup->io_port;
+ shpnt->n_io_port = IO_RANGE;
+ shpnt->irq = setup->irq;
+
+ if (!setup->tc1550) {
+ HOSTIOPORT0 = setup->io_port;
+ HOSTIOPORT1 = setup->io_port;
+ } else {
+ HOSTIOPORT0 = setup->io_port+0x10;
+ HOSTIOPORT1 = setup->io_port-0x10;
+ }
+
+ spin_lock_init(&QLOCK);
+ RECONNECT = setup->reconnect;
+ SYNCHRONOUS = setup->synchronous;
+ PARITY = setup->parity;
+ DELAY = setup->delay;
+ EXT_TRANS = setup->ext_trans;
+
+#if defined(AHA152X_DEBUG)
+ HOSTDATA(shpnt)->debug = setup->debug;
+#endif
+
+ SETPORT(SCSIID, setup->scsiid << 4);
+ shpnt->this_id = setup->scsiid;
+
+ if (setup->reconnect)
+ shpnt->can_queue = AHA152X_MAXQUEUE;
+
+ /* RESET OUT */
+ printk("aha152x: resetting bus...\n");
+ SETPORT(SCSISEQ, SCSIRSTO);
+ mdelay(256);
+ SETPORT(SCSISEQ, 0);
+ mdelay(DELAY);
+
+ reset_ports(shpnt);
+
+ printk(KERN_INFO
+ "aha152x%d%s: "
+ "vital data: rev=%x, "
+ "io=0x%03lx (0x%03lx/0x%03lx), "
+ "irq=%d, "
+ "scsiid=%d, "
+ "reconnect=%s, "
+ "parity=%s, "
+ "synchronous=%s, "
+ "delay=%d, "
+ "extended translation=%s\n",
+ shpnt->host_no, setup->tc1550 ? " (tc1550 mode)" : "",
+ GETPORT(REV) & 0x7,
+ shpnt->io_port, HOSTIOPORT0, HOSTIOPORT1,
+ shpnt->irq,
+ shpnt->this_id,
+ RECONNECT ? "enabled" : "disabled",
+ PARITY ? "enabled" : "disabled",
+ SYNCHRONOUS ? "enabled" : "disabled",
+ DELAY,
+ EXT_TRANS ? "enabled" : "disabled");
+
+ /* not expecting any interrupts */
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, 0);
+
+ if( request_irq(shpnt->irq, swintr, SA_INTERRUPT|SA_SHIRQ, "aha152x", shpnt) ) {
+ printk(KERN_ERR "aha152x%d: irq %d busy.\n", shpnt->host_no, shpnt->irq);
+ goto out_host_put;
+ }
+
+ HOSTDATA(shpnt)->swint = 0;
+
+ printk(KERN_INFO "aha152x%d: trying software interrupt, ", shpnt->host_no);
+
+ mb();
+ SETPORT(DMACNTRL0, SWINT|INTEN);
+ mdelay(1000);
+ free_irq(shpnt->irq, shpnt);
+
+ if (!HOSTDATA(shpnt)->swint) {
+ if (TESTHI(DMASTAT, INTSTAT)) {
+ printk("lost.\n");
+ } else {
+ printk("failed.\n");
+ }
+
+ SETPORT(DMACNTRL0, INTEN);
+
+ printk(KERN_ERR "aha152x%d: irq %d possibly wrong. "
+ "Please verify.\n", shpnt->host_no, shpnt->irq);
+ goto out_host_put;
+ }
+ printk("ok.\n");
+
+
+ /* clear interrupts */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ if ( request_irq(shpnt->irq, intr, SA_INTERRUPT|SA_SHIRQ, "aha152x", shpnt) ) {
+ printk(KERN_ERR "aha152x%d: failed to reassign irq %d.\n", shpnt->host_no, shpnt->irq);
+ goto out_host_put;
+ }
+
+ if( scsi_add_host(shpnt, NULL) ) {
+ free_irq(shpnt->irq, shpnt);
+ printk(KERN_ERR "aha152x%d: failed to add host.\n", shpnt->host_no);
+ goto out_host_put;
+ }
+
+ scsi_scan_host(shpnt);
+
+ registered_count++;
+
+ return shpnt;
+
+out_host_put:
+ aha152x_host[registered_count]=NULL;
+ scsi_host_put(shpnt);
+
+ return NULL;
+}
+
+void aha152x_release(struct Scsi_Host *shpnt)
+{
+ if(!shpnt)
+ return;
+
+ if (shpnt->irq)
+ free_irq(shpnt->irq, shpnt);
+
+#if !defined(PCMCIA)
+ if (shpnt->io_port)
+ release_region(shpnt->io_port, IO_RANGE);
+#endif
+
+#ifdef __ISAPNP__
+ if (HOSTDATA(shpnt)->pnpdev)
+ pnp_device_detach(HOSTDATA(shpnt)->pnpdev);
+#endif
+
+ scsi_remove_host(shpnt);
+ scsi_host_put(shpnt);
+}
+
+
+/*
+ * setup controller to generate interrupts depending
+ * on current state (lock has to be acquired)
+ *
+ */
+static int setup_expected_interrupts(struct Scsi_Host *shpnt)
+{
+ if(CURRENT_SC) {
+ CURRENT_SC->SCp.phase |= 1 << 16;
+
+ if(CURRENT_SC->SCp.phase & selecting) {
+ DPRINTK(debug_intr, DEBUG_LEAD "expecting: (seldo) (seltimo) (seldi)\n", CMDINFO(CURRENT_SC));
+ SETPORT(SSTAT1, SELTO);
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+ } else {
+ DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (busfree) %s\n", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.phase & spiordy ? "(spiordy)" : "");
+ SETPORT(SIMODE0, (CURRENT_SC->SCp.phase & spiordy) ? ENSPIORDY : 0);
+ SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
+ }
+ } else if(STATE==seldi) {
+ DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (identify)\n", CMDINFO(CURRENT_SC));
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
+ } else {
+ DPRINTK(debug_intr, DEBUG_LEAD "expecting: %s %s\n",
+ CMDINFO(CURRENT_SC),
+ DISCONNECTED_SC ? "(reselection)" : "",
+ ISSUE_SC ? "(busfree)" : "");
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ENSCSIRST | ( (ISSUE_SC||DONE_SC) ? ENBUSFREE : 0));
+ }
+
+ if(!HOSTDATA(shpnt)->in_intr)
+ SETBITS(DMACNTRL0, INTEN);
+
+ return TESTHI(DMASTAT, INTSTAT);
+}
+
+
+/*
+ * Queue a command and setup interrupts for a free bus.
+ */
+static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct semaphore *sem, int phase, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+ unsigned long flags;
+
+#if defined(AHA152X_DEBUG)
+ if (HOSTDATA(shpnt)->debug & debug_queue) {
+ printk(INFO_LEAD "queue: %p; cmd_len=%d pieces=%d size=%u cmnd=",
+ CMDINFO(SCpnt), SCpnt, SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen);
+ print_command(SCpnt->cmnd);
+ }
+#endif
+
+ SCpnt->scsi_done = done;
+ SCpnt->resid = SCpnt->request_bufflen;
+ SCpnt->SCp.phase = not_issued | phase;
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->SCp.have_data_in = 0;
+ SCpnt->SCp.sent_command = 0;
+
+ if(SCpnt->SCp.phase & (resetting|check_condition)) {
+ if(SCpnt->host_scribble==0 || SCSEM(SCpnt) || SCNEXT(SCpnt)) {
+ printk(ERR_LEAD "cannot reuse command\n", CMDINFO(SCpnt));
+ return FAILED;
+ }
+ } else {
+ SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC);
+ if(SCpnt->host_scribble==0) {
+ printk(ERR_LEAD "allocation failed\n", CMDINFO(SCpnt));
+ return FAILED;
+ }
+ }
+
+ SCNEXT(SCpnt) = NULL;
+ SCSEM(SCpnt) = sem;
+
+ /* setup scratch area
+ SCp.ptr : buffer pointer
+ SCp.this_residual : buffer length
+ SCp.buffer : next buffer
+ SCp.buffers_residual : left buffers in list
+ SCp.phase : current state of the command */
+ if (SCpnt->use_sg) {
+ SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer;
+ SCpnt->SCp.ptr = SG_ADDRESS(SCpnt->SCp.buffer);
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+ } else {
+ SCpnt->SCp.ptr = (char *) SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+ }
+
+ DO_LOCK(flags);
+
+#if defined(AHA152X_STAT)
+ HOSTDATA(shpnt)->total_commands++;
+#endif
+
+ /* Turn led on, when this is the first command. */
+ HOSTDATA(shpnt)->commands++;
+ if (HOSTDATA(shpnt)->commands==1)
+ SETPORT(PORTA, 1);
+
+ append_SC(&ISSUE_SC, SCpnt);
+
+ if(!HOSTDATA(shpnt)->in_intr)
+ setup_expected_interrupts(shpnt);
+
+ DO_UNLOCK(flags);
+
+ return 0;
+}
+
+/*
+ * queue a command
+ *
+ */
+static int aha152x_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+#if 0
+ if(*SCpnt->cmnd == REQUEST_SENSE) {
+ SCpnt->result = 0;
+ done(SCpnt);
+
+ return 0;
+ }
+#endif
+
+ return aha152x_internal_queue(SCpnt, NULL, 0, done);
+}
+
+
+/*
+ *
+ *
+ */
+static void reset_done(Scsi_Cmnd *SCpnt)
+{
+#if 0
+ struct Scsi_Host *shpnt = SCpnt->host;
+ DPRINTK(debug_eh, INFO_LEAD "reset_done called\n", CMDINFO(SCpnt));
+#endif
+ if(SCSEM(SCpnt)) {
+ up(SCSEM(SCpnt));
+ } else {
+ printk(KERN_ERR "aha152x: reset_done w/o semaphore\n");
+ }
+}
+
+/*
+ * Abort a command
+ *
+ */
+static int aha152x_abort(Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+ Scsi_Cmnd *ptr;
+ unsigned long flags;
+
+#if defined(AHA152X_DEBUG)
+ if(HOSTDATA(shpnt)->debug & debug_eh) {
+ printk(DEBUG_LEAD "abort(%p)", CMDINFO(SCpnt), SCpnt);
+ show_queues(shpnt);
+ }
+#endif
+
+ DO_LOCK(flags);
+
+ ptr=remove_SC(&ISSUE_SC, SCpnt);
+
+ if(ptr) {
+ DPRINTK(debug_eh, DEBUG_LEAD "not yet issued - SUCCESS\n", CMDINFO(SCpnt));
+
+ HOSTDATA(shpnt)->commands--;
+ if (!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0);
+ DO_UNLOCK(flags);
+
+ kfree(SCpnt->host_scribble);
+ SCpnt->host_scribble=NULL;
+
+ return SUCCESS;
+ }
+
+ DO_UNLOCK(flags);
+
+ /*
+ * FIXME:
+ * for current command: queue ABORT for message out and raise ATN
+ * for disconnected command: pseudo SC with ABORT message or ABORT on reselection?
+ *
+ */
+
+ printk(ERR_LEAD "cannot abort running or disconnected command\n", CMDINFO(SCpnt));
+
+ return FAILED;
+}
+
+static void timer_expired(unsigned long p)
+{
+ Scsi_Cmnd *SCp = (Scsi_Cmnd *)p;
+ struct semaphore *sem = SCSEM(SCp);
+ struct Scsi_Host *shpnt = SCp->device->host;
+ unsigned long flags;
+
+ /* remove command from issue queue */
+ DO_LOCK(flags);
+ remove_SC(&ISSUE_SC, SCp);
+ DO_UNLOCK(flags);
+
+ up(sem);
+}
+
+/*
+ * Reset a device
+ *
+ */
+static int aha152x_device_reset(Scsi_Cmnd * SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+ DECLARE_MUTEX_LOCKED(sem);
+ struct timer_list timer;
+ int ret, issued, disconnected;
+ unsigned long flags;
+
+#if defined(AHA152X_DEBUG)
+ if(HOSTDATA(shpnt)->debug & debug_eh) {
+ printk(INFO_LEAD "aha152x_device_reset(%p)", CMDINFO(SCpnt), SCpnt);
+ show_queues(shpnt);
+ }
+#endif
+
+ if(CURRENT_SC==SCpnt) {
+ printk(ERR_LEAD "cannot reset current device\n", CMDINFO(SCpnt));
+ return FAILED;
+ }
+
+ DO_LOCK(flags);
+ issued = remove_SC(&ISSUE_SC, SCpnt)==0;
+ disconnected = issued && remove_SC(&DISCONNECTED_SC, SCpnt);
+ DO_UNLOCK(flags);
+
+ SCpnt->cmd_len = 0;
+ SCpnt->use_sg = 0;
+ SCpnt->request_buffer = NULL;
+ SCpnt->request_bufflen = 0;
+
+ init_timer(&timer);
+ timer.data = (unsigned long) SCpnt;
+ timer.expires = jiffies + 100*HZ; /* 10s */
+ timer.function = (void (*)(unsigned long)) timer_expired;
+
+ aha152x_internal_queue(SCpnt, &sem, resetting, reset_done);
+ add_timer(&timer);
+ down(&sem);
+ del_timer(&timer);
+
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->request_buffer = SCpnt->buffer;
+ SCpnt->request_bufflen = SCpnt->bufflen;
+
+ DO_LOCK(flags);
+
+ if(SCpnt->SCp.phase & resetted) {
+ HOSTDATA(shpnt)->commands--;
+ if (!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0);
+ kfree(SCpnt->host_scribble);
+ SCpnt->host_scribble=NULL;
+
+ ret = SUCCESS;
+ } else {
+ /* requeue */
+ if(!issued) {
+ append_SC(&ISSUE_SC, SCpnt);
+ } else if(disconnected) {
+ append_SC(&DISCONNECTED_SC, SCpnt);
+ }
+
+ ret = FAILED;
+ }
+
+ DO_UNLOCK(flags);
+
+ spin_lock_irq(shpnt->host_lock);
+ return ret;
+}
+
+static void free_hard_reset_SCs(struct Scsi_Host *shpnt, Scsi_Cmnd **SCs)
+{
+ Scsi_Cmnd *ptr;
+
+ ptr=*SCs;
+ while(ptr) {
+ Scsi_Cmnd *next;
+
+ if(SCDATA(ptr)) {
+ next = SCNEXT(ptr);
+ } else {
+ printk(DEBUG_LEAD "queue corrupted at %p\n", CMDINFO(ptr), ptr);
+ next = NULL;
+ }
+
+ if (!ptr->device->soft_reset) {
+ DPRINTK(debug_eh, DEBUG_LEAD "disconnected command %p removed\n", CMDINFO(ptr), ptr);
+ remove_SC(SCs, ptr);
+ HOSTDATA(shpnt)->commands--;
+ kfree(ptr->host_scribble);
+ ptr->host_scribble=NULL;
+ }
+
+ ptr = next;
+ }
+}
+
+/*
+ * Reset the bus
+ *
+ */
+static int aha152x_bus_reset(Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+ unsigned long flags;
+
+ DO_LOCK(flags);
+
+#if defined(AHA152X_DEBUG)
+ if(HOSTDATA(shpnt)->debug & debug_eh) {
+ printk(DEBUG_LEAD "aha152x_bus_reset(%p)", CMDINFO(SCpnt), SCpnt);
+ show_queues(shpnt);
+ }
+#endif
+
+ free_hard_reset_SCs(shpnt, &ISSUE_SC);
+ free_hard_reset_SCs(shpnt, &DISCONNECTED_SC);
+
+ DPRINTK(debug_eh, DEBUG_LEAD "resetting bus\n", CMDINFO(SCpnt));
+
+ SETPORT(SCSISEQ, SCSIRSTO);
+ mdelay(256);
+ SETPORT(SCSISEQ, 0);
+ mdelay(DELAY);
+
+ DPRINTK(debug_eh, DEBUG_LEAD "bus resetted\n", CMDINFO(SCpnt));
+
+ setup_expected_interrupts(shpnt);
+ if(HOSTDATA(shpnt)->commands==0)
+ SETPORT(PORTA, 0);
+
+ DO_UNLOCK(flags);
+
+ return SUCCESS;
+}
+
+
+/*
+ * Restore default values to the AIC-6260 registers and reset the fifos
+ *
+ */
+static void reset_ports(struct Scsi_Host *shpnt)
+{
+ unsigned long flags;
+
+ /* disable interrupts */
+ SETPORT(DMACNTRL0, RSTFIFO);
+
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL1, 0);
+ SETPORT(SCSISIG, 0);
+ SETRATE(0);
+
+ /* clear all interrupt conditions */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ SETPORT(SSTAT4, SYNCERR | FWERR | FRERR);
+
+ SETPORT(DMACNTRL0, 0);
+ SETPORT(DMACNTRL1, 0);
+
+ SETPORT(BRSTCNTRL, 0xf1);
+
+ /* clear SCSI fifos and transfer count */
+ SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1);
+
+ DO_LOCK(flags);
+ setup_expected_interrupts(shpnt);
+ DO_UNLOCK(flags);
+}
+
+/*
+ * Reset the host (bus and controller)
+ *
+ */
+int aha152x_host_reset(Scsi_Cmnd * SCpnt)
+{
+#if defined(AHA152X_DEBUG)
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+#endif
+
+ DPRINTK(debug_eh, DEBUG_LEAD "aha152x_host_reset(%p)\n", CMDINFO(SCpnt), SCpnt);
+
+ aha152x_bus_reset(SCpnt);
+
+ DPRINTK(debug_eh, DEBUG_LEAD "resetting ports\n", CMDINFO(SCpnt));
+ reset_ports(SCpnt->device->host);
+
+ return SUCCESS;
+}
+
+/*
+ * Return the "logical geometry"
+ *
+ */
+static int aha152x_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *info_array)
+{
+ struct Scsi_Host *shpnt = sdev->host;
+
+ /* try default translation */
+ info_array[0] = 64;
+ info_array[1] = 32;
+ info_array[2] = (unsigned long)capacity / (64 * 32);
+
+ /* for disks >1GB do some guessing */
+ if (info_array[2] >= 1024) {
+ int info[3];
+
+ /* try to figure out the geometry from the partition table */
+ if (scsicam_bios_param(bdev, capacity, info) < 0 ||
+ !((info[0] == 64 && info[1] == 32) || (info[0] == 255 && info[1] == 63))) {
+ if (EXT_TRANS) {
+ printk(KERN_NOTICE
+ "aha152x: unable to verify geometry for disk with >1GB.\n"
+ " using extended translation.\n");
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = (unsigned long)capacity / (255 * 63);
+ } else {
+ printk(KERN_NOTICE
+ "aha152x: unable to verify geometry for disk with >1GB.\n"
+ " Using default translation. Please verify yourself.\n"
+ " Perhaps you need to enable extended translation in the driver.\n"
+ " See Documentation/scsi/aha152x.txt for details.\n");
+ }
+ } else {
+ info_array[0] = info[0];
+ info_array[1] = info[1];
+ info_array[2] = info[2];
+
+ if (info[0] == 255 && !EXT_TRANS) {
+ printk(KERN_NOTICE
+ "aha152x: current partition table is using extended translation.\n"
+ " using it also, although it's not explicitly enabled.\n");
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Internal done function
+ *
+ */
+static void done(struct Scsi_Host *shpnt, int error)
+{
+ if (CURRENT_SC) {
+ if(DONE_SC)
+ printk(ERR_LEAD "there's already a completed command %p - will cause abort\n", CMDINFO(CURRENT_SC), DONE_SC);
+
+ DONE_SC = CURRENT_SC;
+ CURRENT_SC = NULL;
+ DONE_SC->result = error;
+ } else
+ printk(KERN_ERR "aha152x: done() called outside of command\n");
+}
+
+static struct work_struct aha152x_tq;
+
+/*
+ * Run service completions on the card with interrupts enabled.
+ *
+ */
+static void run(void)
+{
+ int i;
+ for (i = 0; i<ARRAY_SIZE(aha152x_host); i++) {
+ struct Scsi_Host *shpnt = aha152x_host[i];
+ if (shpnt && HOSTDATA(shpnt)->service) {
+ HOSTDATA(shpnt)->service=0;
+ is_complete(shpnt);
+ }
+ }
+}
+
+/*
+ * Interrupts handler
+ *
+ */
+
+static irqreturn_t intr(int irqno, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *shpnt = lookup_irq(irqno);
+ unsigned char rev, dmacntrl0;
+
+ if (!shpnt) {
+ printk(KERN_ERR "aha152x: catched interrupt %d for unknown controller.\n", irqno);
+ return IRQ_NONE;
+ }
+
+ /*
+ * Read a couple of registers that are known to not be all 1's. If
+ * we read all 1's (-1), that means that either:
+ *
+ * a. The host adapter chip has gone bad, and we cannot control it,
+ * OR
+ * b. The host adapter is a PCMCIA card that has been ejected
+ *
+ * In either case, we cannot do anything with the host adapter at
+ * this point in time. So just ignore the interrupt and return.
+ * In the latter case, the interrupt might actually be meant for
+ * someone else sharing this IRQ, and that driver will handle it.
+ */
+ rev = GETPORT(REV);
+ dmacntrl0 = GETPORT(DMACNTRL0);
+ if ((rev == 0xFF) && (dmacntrl0 == 0xFF))
+ return IRQ_NONE;
+
+ /* no more interrupts from the controller, while we're busy.
+ INTEN is restored by the BH handler */
+ CLRBITS(DMACNTRL0, INTEN);
+
+#if 0
+ /* check if there is already something to be
+ serviced; should not happen */
+ if(HOSTDATA(shpnt)->service) {
+ printk(KERN_ERR "aha152x%d: lost interrupt (%d)\n", HOSTNO, HOSTDATA(shpnt)->service);
+ show_queues(shpnt);
+ }
+#endif
+
+ /* Poke the BH handler */
+ HOSTDATA(shpnt)->service++;
+ INIT_WORK(&aha152x_tq, (void *) run, NULL);
+ schedule_work(&aha152x_tq);
+ return IRQ_HANDLED;
+}
+
+/*
+ * busfree phase
+ * - handle completition/disconnection/error of current command
+ * - start selection for next command (if any)
+ */
+static void busfree_run(struct Scsi_Host *shpnt)
+{
+ unsigned long flags;
+#if defined(AHA152X_STAT)
+ int action=0;
+#endif
+
+ SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+ if(CURRENT_SC) {
+#if defined(AHA152X_STAT)
+ action++;
+#endif
+ CURRENT_SC->SCp.phase &= ~syncneg;
+
+ if(CURRENT_SC->SCp.phase & completed) {
+ /* target sent COMMAND COMPLETE */
+ done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
+
+ } else if(CURRENT_SC->SCp.phase & aborted) {
+ DPRINTK(debug_eh, DEBUG_LEAD "ABORT sent\n", CMDINFO(CURRENT_SC));
+ done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_ABORT << 16));
+
+ } else if(CURRENT_SC->SCp.phase & resetted) {
+ DPRINTK(debug_eh, DEBUG_LEAD "BUS DEVICE RESET sent\n", CMDINFO(CURRENT_SC));
+ done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_RESET << 16));
+
+ } else if(CURRENT_SC->SCp.phase & disconnected) {
+ /* target sent DISCONNECT */
+ DPRINTK(debug_selection, DEBUG_LEAD "target disconnected at %d/%d\n",
+ CMDINFO(CURRENT_SC),
+ CURRENT_SC->resid,
+ CURRENT_SC->request_bufflen);
+#if defined(AHA152X_STAT)
+ HOSTDATA(shpnt)->disconnections++;
+#endif
+ append_SC(&DISCONNECTED_SC, CURRENT_SC);
+ CURRENT_SC->SCp.phase |= 1 << 16;
+ CURRENT_SC = NULL;
+
+ } else {
+ done(shpnt, DID_ERROR << 16);
+ }
+#if defined(AHA152X_STAT)
+ } else {
+ HOSTDATA(shpnt)->busfree_without_old_command++;
+#endif
+ }
+
+ DO_LOCK(flags);
+
+ if(DONE_SC) {
+#if defined(AHA152X_STAT)
+ action++;
+#endif
+
+ if(DONE_SC->SCp.phase & check_condition) {
+#if 0
+ if(HOSTDATA(shpnt)->debug & debug_eh) {
+ printk(ERR_LEAD "received sense: ", CMDINFO(DONE_SC));
+ print_sense("bh", DONE_SC);
+ }
+#endif
+
+ /* restore old command */
+ memcpy((void *) DONE_SC->cmnd, (void *) DONE_SC->data_cmnd, sizeof(DONE_SC->data_cmnd));
+ DONE_SC->request_buffer = DONE_SC->buffer;
+ DONE_SC->request_bufflen = DONE_SC->bufflen;
+ DONE_SC->use_sg = DONE_SC->old_use_sg;
+ DONE_SC->cmd_len = DONE_SC->old_cmd_len;
+
+ DONE_SC->SCp.Status = 0x02;
+
+ HOSTDATA(shpnt)->commands--;
+ if (!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0); /* turn led off */
+ } else if(DONE_SC->SCp.Status==0x02) {
+#if defined(AHA152X_STAT)
+ HOSTDATA(shpnt)->busfree_with_check_condition++;
+#endif
+#if 0
+ DPRINTK(debug_eh, ERR_LEAD "CHECK CONDITION found\n", CMDINFO(DONE_SC));
+#endif
+
+ if(!(DONE_SC->SCp.Status & not_issued)) {
+ Scsi_Cmnd *ptr = DONE_SC;
+ DONE_SC=NULL;
+#if 0
+ DPRINTK(debug_eh, ERR_LEAD "requesting sense\n", CMDINFO(ptr));
+#endif
+
+ ptr->cmnd[0] = REQUEST_SENSE;
+ ptr->cmnd[1] = 0;
+ ptr->cmnd[2] = 0;
+ ptr->cmnd[3] = 0;
+ ptr->cmnd[4] = sizeof(ptr->sense_buffer);
+ ptr->cmnd[5] = 0;
+ ptr->cmd_len = 6;
+ ptr->use_sg = 0;
+ ptr->request_buffer = ptr->sense_buffer;
+ ptr->request_bufflen = sizeof(ptr->sense_buffer);
+
+ DO_UNLOCK(flags);
+ aha152x_internal_queue(ptr, NULL, check_condition, ptr->scsi_done);
+ DO_LOCK(flags);
+#if 0
+ } else {
+ DPRINTK(debug_eh, ERR_LEAD "command not issued - CHECK CONDITION ignored\n", CMDINFO(DONE_SC));
+#endif
+ }
+ }
+
+ if(DONE_SC && DONE_SC->scsi_done) {
+#if defined(AHA152X_DEBUG)
+ int hostno=DONE_SC->device->host->host_no;
+ int id=DONE_SC->device->id & 0xf;
+ int lun=DONE_SC->device->lun & 0x7;
+#endif
+ Scsi_Cmnd *ptr = DONE_SC;
+ DONE_SC=NULL;
+
+ /* turn led off, when no commands are in the driver */
+ HOSTDATA(shpnt)->commands--;
+ if (!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0); /* turn led off */
+
+ if(ptr->scsi_done != reset_done) {
+ kfree(ptr->host_scribble);
+ ptr->host_scribble=NULL;
+ }
+
+ DO_UNLOCK(flags);
+ DPRINTK(debug_done, DEBUG_LEAD "calling scsi_done(%p)\n", hostno, id, lun, ptr);
+ ptr->scsi_done(ptr);
+ DPRINTK(debug_done, DEBUG_LEAD "scsi_done(%p) returned\n", hostno, id, lun, ptr);
+ DO_LOCK(flags);
+ }
+
+ DONE_SC=NULL;
+#if defined(AHA152X_STAT)
+ } else {
+ HOSTDATA(shpnt)->busfree_without_done_command++;
+#endif
+ }
+
+ if(ISSUE_SC)
+ CURRENT_SC = remove_first_SC(&ISSUE_SC);
+
+ DO_UNLOCK(flags);
+
+ if(CURRENT_SC) {
+#if defined(AHA152X_STAT)
+ action++;
+#endif
+ CURRENT_SC->SCp.phase |= selecting;
+
+ DPRINTK(debug_selection, DEBUG_LEAD "selecting target\n", CMDINFO(CURRENT_SC));
+
+ /* clear selection timeout */
+ SETPORT(SSTAT1, SELTO);
+
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->device->id);
+ SETPORT(SXFRCTL1, (PARITY ? ENSPCHK : 0 ) | ENSTIMER);
+ SETPORT(SCSISEQ, ENSELO | ENAUTOATNO | (DISCONNECTED_SC ? ENRESELI : 0));
+ } else {
+#if defined(AHA152X_STAT)
+ HOSTDATA(shpnt)->busfree_without_new_command++;
+#endif
+ SETPORT(SCSISEQ, DISCONNECTED_SC ? ENRESELI : 0);
+ }
+
+#if defined(AHA152X_STAT)
+ if(!action)
+ HOSTDATA(shpnt)->busfree_without_any_action++;
+#endif
+}
+
+/*
+ * Selection done (OUT)
+ * - queue IDENTIFY message and SDTR to selected target for message out
+ * (ATN asserted automagically via ENAUTOATNO in busfree())
+ */
+static void seldo_run(struct Scsi_Host *shpnt)
+{
+ SETPORT(SCSISIG, 0);
+ SETPORT(SSTAT1, CLRBUSFREE);
+ SETPORT(SSTAT1, CLRPHASECHG);
+
+ CURRENT_SC->SCp.phase &= ~(selecting|not_issued);
+
+ SETPORT(SCSISEQ, 0);
+
+ if (TESTLO(SSTAT0, SELDO)) {
+ printk(ERR_LEAD "aha152x: passing bus free condition\n", CMDINFO(CURRENT_SC));
+ done(shpnt, DID_NO_CONNECT << 16);
+ return;
+ }
+
+ SETPORT(SSTAT0, CLRSELDO);
+
+ ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
+
+ if (CURRENT_SC->SCp.phase & aborting) {
+ ADDMSGO(ABORT);
+ } else if (CURRENT_SC->SCp.phase & resetting) {
+ ADDMSGO(BUS_DEVICE_RESET);
+ } else if (SYNCNEG==0 && SYNCHRONOUS) {
+ CURRENT_SC->SCp.phase |= syncneg;
+ ADDMSGO(EXTENDED_MESSAGE);
+ ADDMSGO(3);
+ ADDMSGO(EXTENDED_SDTR);
+ ADDMSGO(50); /* 200ns */
+ ADDMSGO(8); /* 8 byte req/ack offset */
+
+ SYNCNEG=1; /* negotiation in progress */
+ }
+
+ SETRATE(SYNCRATE);
+}
+
+/*
+ * Selection timeout
+ * - return command to mid-level with failure cause
+ *
+ */
+static void selto_run(struct Scsi_Host *shpnt)
+{
+ SETPORT(SCSISEQ, 0);
+ SETPORT(SSTAT1, CLRSELTIMO);
+
+ DPRINTK(debug_selection, DEBUG_LEAD "selection timeout\n", CMDINFO(CURRENT_SC));
+
+ if(!CURRENT_SC) {
+ DPRINTK(debug_selection, DEBUG_LEAD "!CURRENT_SC\n", CMDINFO(CURRENT_SC));
+ return;
+ }
+
+ CURRENT_SC->SCp.phase &= ~selecting;
+
+ if (CURRENT_SC->SCp.phase & aborted) {
+ DPRINTK(debug_selection, DEBUG_LEAD "aborted\n", CMDINFO(CURRENT_SC));
+ done(shpnt, DID_ABORT << 16);
+ } else if (TESTLO(SSTAT0, SELINGO)) {
+ DPRINTK(debug_selection, DEBUG_LEAD "arbitration not won\n", CMDINFO(CURRENT_SC));
+ done(shpnt, DID_BUS_BUSY << 16);
+ } else {
+ /* ARBITRATION won, but SELECTION failed */
+ DPRINTK(debug_selection, DEBUG_LEAD "selection failed\n", CMDINFO(CURRENT_SC));
+ done(shpnt, DID_NO_CONNECT << 16);
+ }
+}
+
+/*
+ * Selection in done
+ * - put current command back to issue queue
+ * (reconnection of a disconnected nexus instead
+ * of successful selection out)
+ *
+ */
+static void seldi_run(struct Scsi_Host *shpnt)
+{
+ int selid;
+ int target;
+ unsigned long flags;
+
+ SETPORT(SCSISIG, 0);
+ SETPORT(SSTAT0, CLRSELDI);
+ SETPORT(SSTAT1, CLRBUSFREE);
+ SETPORT(SSTAT1, CLRPHASECHG);
+
+ if(CURRENT_SC) {
+ if(!(CURRENT_SC->SCp.phase & not_issued))
+ printk(ERR_LEAD "command should not have been issued yet\n", CMDINFO(CURRENT_SC));
+
+ DPRINTK(debug_selection, ERR_LEAD "command requeued - reselection\n", CMDINFO(CURRENT_SC));
+
+ DO_LOCK(flags);
+ append_SC(&ISSUE_SC, CURRENT_SC);
+ DO_UNLOCK(flags);
+
+ CURRENT_SC = NULL;
+ }
+
+ if(!DISCONNECTED_SC) {
+ DPRINTK(debug_selection, DEBUG_LEAD "unexpected SELDI ", CMDINFO(CURRENT_SC));
+ return;
+ }
+
+ RECONN_TARGET=-1;
+
+ selid = GETPORT(SELID) & ~(1 << shpnt->this_id);
+
+ if (selid==0) {
+ printk("aha152x%d: target id unknown (%02x)\n", HOSTNO, selid);
+ return;
+ }
+
+ for(target=7; !(selid & (1 << target)); target--)
+ ;
+
+ if(selid & ~(1 << target)) {
+ printk("aha152x%d: multiple targets reconnected (%02x)\n",
+ HOSTNO, selid);
+ }
+
+
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
+ SETPORT(SCSISEQ, 0);
+
+ SETRATE(HOSTDATA(shpnt)->syncrate[target]);
+
+ RECONN_TARGET=target;
+ DPRINTK(debug_selection, DEBUG_LEAD "target %d reselected (%02x).\n", CMDINFO(CURRENT_SC), target, selid);
+}
+
+/*
+ * message in phase
+ * - handle initial message after reconnection to identify
+ * reconnecting nexus
+ * - queue command on DISCONNECTED_SC on DISCONNECT message
+ * - set completed flag on COMMAND COMPLETE
+ * (other completition code moved to busfree_run)
+ * - handle response to SDTR
+ * - clear synchronous transfer agreements on BUS RESET
+ *
+ * FIXME: what about SAVE POINTERS, RESTORE POINTERS?
+ *
+ */
+static void msgi_run(struct Scsi_Host *shpnt)
+{
+ for(;;) {
+ int sstat1 = GETPORT(SSTAT1);
+
+ if(sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT))
+ return;
+
+ if(TESTLO(SSTAT0,SPIORDY)) {
+ DPRINTK(debug_msgi, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ return;
+ }
+
+ ADDMSGI(GETPORT(SCSIDAT));
+
+#if defined(AHA152X_DEBUG)
+ if (HOSTDATA(shpnt)->debug & debug_msgi) {
+ printk(INFO_LEAD "inbound message %02x ", CMDINFO(CURRENT_SC), MSGI(0));
+ print_msg(&MSGI(0));
+ printk("\n");
+ }
+#endif
+
+ if(!CURRENT_SC) {
+ if(LASTSTATE!=seldi) {
+ printk(KERN_ERR "aha152x%d: message in w/o current command not after reselection\n", HOSTNO);
+ }
+
+ /*
+ * Handle reselection
+ */
+ if(!(MSGI(0) & IDENTIFY_BASE)) {
+ printk(KERN_ERR "aha152x%d: target didn't identify after reselection\n", HOSTNO);
+ continue;
+ }
+
+ CURRENT_SC = remove_lun_SC(&DISCONNECTED_SC, RECONN_TARGET, MSGI(0) & 0x3f);
+
+ if (!CURRENT_SC) {
+ show_queues(shpnt);
+ printk(KERN_ERR "aha152x%d: no disconnected command for target %d/%d\n", HOSTNO, RECONN_TARGET, MSGI(0) & 0x3f);
+ continue;
+ }
+
+ DPRINTK(debug_msgi, DEBUG_LEAD "target reconnected\n", CMDINFO(CURRENT_SC));
+
+ CURRENT_SC->SCp.Message = MSGI(0);
+ CURRENT_SC->SCp.phase &= ~disconnected;
+
+ MSGILEN=0;
+
+ /* next message if any */
+ continue;
+ }
+
+ CURRENT_SC->SCp.Message = MSGI(0);
+
+ switch (MSGI(0)) {
+ case DISCONNECT:
+ if (!RECONNECT)
+ printk(WARN_LEAD "target was not allowed to disconnect\n", CMDINFO(CURRENT_SC));
+
+ CURRENT_SC->SCp.phase |= disconnected;
+ break;
+
+ case COMMAND_COMPLETE:
+ if(CURRENT_SC->SCp.phase & completed)
+ DPRINTK(debug_msgi, DEBUG_LEAD "again COMMAND COMPLETE\n", CMDINFO(CURRENT_SC));
+
+ CURRENT_SC->SCp.phase |= completed;
+ break;
+
+ case MESSAGE_REJECT:
+ if (SYNCNEG==1) {
+ printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC));
+ SYNCNEG=2; /* negotiation completed */
+ } else
+ printk(INFO_LEAD "inbound message (MESSAGE REJECT)\n", CMDINFO(CURRENT_SC));
+ break;
+
+ case SAVE_POINTERS:
+ break;
+
+ case RESTORE_POINTERS:
+ break;
+
+ case EXTENDED_MESSAGE:
+ if(MSGILEN<2 || MSGILEN<MSGI(1)+2) {
+ /* not yet completed */
+ continue;
+ }
+
+ switch (MSGI(2)) {
+ case EXTENDED_SDTR:
+ {
+ long ticks;
+
+ if (MSGI(1) != 3) {
+ printk(ERR_LEAD "SDTR message length!=3\n", CMDINFO(CURRENT_SC));
+ break;
+ }
+
+ if (!HOSTDATA(shpnt)->synchronous)
+ break;
+
+ printk(INFO_LEAD, CMDINFO(CURRENT_SC));
+ print_msg(&MSGI(0));
+ printk("\n");
+
+ ticks = (MSGI(3) * 4 + 49) / 50;
+
+ if (syncneg) {
+ /* negotiation in progress */
+ if (ticks > 9 || MSGI(4) < 1 || MSGI(4) > 8) {
+ ADDMSGO(MESSAGE_REJECT);
+ printk(INFO_LEAD "received Synchronous Data Transfer Request invalid - rejected\n", CMDINFO(CURRENT_SC));
+ break;
+ }
+
+ SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
+ } else if (ticks <= 9 && MSGI(4) >= 1) {
+ ADDMSGO(EXTENDED_MESSAGE);
+ ADDMSGO(3);
+ ADDMSGO(EXTENDED_SDTR);
+ if (ticks < 4) {
+ ticks = 4;
+ ADDMSGO(50);
+ } else
+ ADDMSGO(MSGI(3));
+
+ if (MSGI(4) > 8)
+ MSGI(4) = 8;
+
+ ADDMSGO(MSGI(4));
+
+ SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
+ } else {
+ /* requested SDTR is too slow, do it asynchronously */
+ printk(INFO_LEAD "Synchronous Data Transfer Request too slow - Rejecting\n", CMDINFO(CURRENT_SC));
+ ADDMSGO(MESSAGE_REJECT);
+ }
+
+ SYNCNEG=2; /* negotiation completed */
+ SETRATE(SYNCRATE);
+ }
+ break;
+
+ case BUS_DEVICE_RESET:
+ {
+ int i;
+
+ for(i=0; i<8; i++) {
+ HOSTDATA(shpnt)->syncrate[i]=0;
+ HOSTDATA(shpnt)->syncneg[i]=0;
+ }
+
+ }
+ break;
+
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ case EXTENDED_WDTR:
+ default:
+ ADDMSGO(MESSAGE_REJECT);
+ break;
+ }
+ break;
+ }
+
+ MSGILEN=0;
+ }
+}
+
+static void msgi_end(struct Scsi_Host *shpnt)
+{
+ if(MSGILEN>0)
+ printk(WARN_LEAD "target left before message completed (%d)\n", CMDINFO(CURRENT_SC), MSGILEN);
+
+ if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE)) {
+ DPRINTK(debug_msgi, DEBUG_LEAD "msgo pending\n", CMDINFO(CURRENT_SC));
+ SETPORT(SCSISIG, P_MSGI | SIG_ATNO);
+ }
+}
+
+/*
+ * message out phase
+ *
+ */
+static void msgo_init(struct Scsi_Host *shpnt)
+{
+ if(MSGOLEN==0) {
+ if((CURRENT_SC->SCp.phase & syncneg) && SYNCNEG==2 && SYNCRATE==0) {
+ ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
+ } else {
+ printk(INFO_LEAD "unexpected MESSAGE OUT phase; rejecting\n", CMDINFO(CURRENT_SC));
+ ADDMSGO(MESSAGE_REJECT);
+ }
+ }
+
+#if defined(AHA152X_DEBUG)
+ if(HOSTDATA(shpnt)->debug & debug_msgo) {
+ int i;
+
+ printk(DEBUG_LEAD "messages( ", CMDINFO(CURRENT_SC));
+ for (i=0; i<MSGOLEN; i+=print_msg(&MSGO(i)), printk(" "))
+ ;
+ printk(")\n");
+ }
+#endif
+}
+
+/*
+ * message out phase
+ *
+ */
+static void msgo_run(struct Scsi_Host *shpnt)
+{
+ if(MSGO_I==MSGOLEN)
+ DPRINTK(debug_msgo, DEBUG_LEAD "messages all sent (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN);
+
+ while(MSGO_I<MSGOLEN) {
+ DPRINTK(debug_msgo, DEBUG_LEAD "message byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO(MSGO_I), MSGO_I, MSGOLEN);
+
+ if(TESTLO(SSTAT0, SPIORDY)) {
+ DPRINTK(debug_msgo, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ return;
+ }
+
+ if (MSGO_I==MSGOLEN-1) {
+ /* Leave MESSAGE OUT after transfer */
+ SETPORT(SSTAT1, CLRATNO);
+ }
+
+
+ if (MSGO(MSGO_I) & IDENTIFY_BASE)
+ CURRENT_SC->SCp.phase |= identified;
+
+ if (MSGO(MSGO_I)==ABORT)
+ CURRENT_SC->SCp.phase |= aborted;
+
+ if (MSGO(MSGO_I)==BUS_DEVICE_RESET)
+ CURRENT_SC->SCp.phase |= resetted;
+
+ SETPORT(SCSIDAT, MSGO(MSGO_I++));
+ }
+}
+
+static void msgo_end(struct Scsi_Host *shpnt)
+{
+ if(MSGO_I<MSGOLEN) {
+ printk(ERR_LEAD "message sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN);
+ if(SYNCNEG==1) {
+ printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC));
+ SYNCNEG=2;
+ }
+ }
+
+ MSGO_I = 0;
+ MSGOLEN = 0;
+}
+
+/*
+ * command phase
+ *
+ */
+static void cmd_init(struct Scsi_Host *shpnt)
+{
+ if (CURRENT_SC->SCp.sent_command) {
+ printk(ERR_LEAD "command already sent\n", CMDINFO(CURRENT_SC));
+ done(shpnt, DID_ERROR << 16);
+ return;
+ }
+
+#if defined(AHA152X_DEBUG)
+ if (HOSTDATA(shpnt)->debug & debug_cmd) {
+ printk(DEBUG_LEAD "cmd_init: ", CMDINFO(CURRENT_SC));
+ print_command(CURRENT_SC->cmnd);
+ }
+#endif
+
+ CMD_I=0;
+}
+
+/*
+ * command phase
+ *
+ */
+static void cmd_run(struct Scsi_Host *shpnt)
+{
+ if(CMD_I==CURRENT_SC->cmd_len) {
+ DPRINTK(debug_cmd, DEBUG_LEAD "command already completely sent (%d/%d)", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len);
+ disp_ports(shpnt);
+ }
+
+ while(CMD_I<CURRENT_SC->cmd_len) {
+ DPRINTK(debug_cmd, DEBUG_LEAD "command byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), CURRENT_SC->cmnd[CMD_I], CMD_I, CURRENT_SC->cmd_len);
+
+ if(TESTLO(SSTAT0, SPIORDY)) {
+ DPRINTK(debug_cmd, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ return;
+ }
+
+ SETPORT(SCSIDAT, CURRENT_SC->cmnd[CMD_I++]);
+ }
+}
+
+static void cmd_end(struct Scsi_Host *shpnt)
+{
+ if(CMD_I<CURRENT_SC->cmd_len)
+ printk(ERR_LEAD "command sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len);
+ else
+ CURRENT_SC->SCp.sent_command++;
+}
+
+/*
+ * status phase
+ *
+ */
+static void status_run(struct Scsi_Host *shpnt)
+{
+ if(TESTLO(SSTAT0,SPIORDY)) {
+ DPRINTK(debug_status, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ return;
+ }
+
+ CURRENT_SC->SCp.Status = GETPORT(SCSIDAT);
+
+#if defined(AHA152X_DEBUG)
+ if (HOSTDATA(shpnt)->debug & debug_status) {
+ printk(DEBUG_LEAD "inbound status %02x ", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.Status);
+ print_status(CURRENT_SC->SCp.Status);
+ printk("\n");
+ }
+#endif
+}
+
+/*
+ * data in phase
+ *
+ */
+static void datai_init(struct Scsi_Host *shpnt)
+{
+ SETPORT(DMACNTRL0, RSTFIFO);
+ SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
+
+ SETPORT(SXFRCTL0, CH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE);
+
+ DATA_LEN=0;
+ DPRINTK(debug_datai,
+ DEBUG_LEAD "datai_init: request_bufflen=%d resid=%d\n",
+ CMDINFO(CURRENT_SC), CURRENT_SC->request_bufflen, CURRENT_SC->resid);
+}
+
+static void datai_run(struct Scsi_Host *shpnt)
+{
+ unsigned long the_time;
+ int fifodata, data_count;
+
+ /*
+ * loop while the phase persists or the fifos are not empty
+ *
+ */
+ while(TESTLO(DMASTAT, INTSTAT) || TESTLO(DMASTAT, DFIFOEMP) || TESTLO(SSTAT2, SEMPTY)) {
+ /* FIXME: maybe this should be done by setting up
+ * STCNT to trigger ENSWRAP interrupt, instead of
+ * polling for DFIFOFULL
+ */
+ the_time=jiffies + 100*HZ;
+ while(TESTLO(DMASTAT, DFIFOFULL|INTSTAT) && time_before(jiffies,the_time))
+ barrier();
+
+ if(TESTLO(DMASTAT, DFIFOFULL|INTSTAT)) {
+ printk(ERR_LEAD "datai timeout", CMDINFO(CURRENT_SC));
+ disp_ports(shpnt);
+ break;
+ }
+
+ if(TESTHI(DMASTAT, DFIFOFULL)) {
+ fifodata = 128;
+ } else {
+ the_time=jiffies + 100*HZ;
+ while(TESTLO(SSTAT2, SEMPTY) && time_before(jiffies,the_time))
+ barrier();
+
+ if(TESTLO(SSTAT2, SEMPTY)) {
+ printk(ERR_LEAD "datai sempty timeout", CMDINFO(CURRENT_SC));
+ disp_ports(shpnt);
+ break;
+ }
+
+ fifodata = GETPORT(FIFOSTAT);
+ }
+
+ if(CURRENT_SC->SCp.this_residual>0) {
+ while(fifodata>0 && CURRENT_SC->SCp.this_residual>0) {
+ data_count = fifodata>CURRENT_SC->SCp.this_residual ?
+ CURRENT_SC->SCp.this_residual :
+ fifodata;
+ fifodata -= data_count;
+
+ if(data_count & 1) {
+ DPRINTK(debug_datai, DEBUG_LEAD "8bit\n", CMDINFO(CURRENT_SC));
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
+ CURRENT_SC->SCp.this_residual--;
+ DATA_LEN++;
+ SETPORT(DMACNTRL0, ENDMA);
+ }
+
+ if(data_count > 1) {
+ DPRINTK(debug_datai, DEBUG_LEAD "16bit(%d)\n", CMDINFO(CURRENT_SC), data_count);
+ data_count >>= 1;
+ insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ DATA_LEN += 2 * data_count;
+ }
+
+ if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer);
+ CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
+ }
+ }
+ } else if(fifodata>0) {
+ printk(ERR_LEAD "no buffers left for %d(%d) bytes (data overrun!?)\n", CMDINFO(CURRENT_SC), fifodata, GETPORT(FIFOSTAT));
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ while(fifodata>0) {
+ int data;
+ data=GETPORT(DATAPORT);
+ DPRINTK(debug_datai, DEBUG_LEAD "data=%02x\n", CMDINFO(CURRENT_SC), data);
+ fifodata--;
+ DATA_LEN++;
+ }
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ }
+ }
+
+ if(TESTLO(DMASTAT, INTSTAT) ||
+ TESTLO(DMASTAT, DFIFOEMP) ||
+ TESTLO(SSTAT2, SEMPTY) ||
+ GETPORT(FIFOSTAT)>0) {
+ /*
+ * something went wrong, if there's something left in the fifos
+ * or the phase didn't change
+ */
+ printk(ERR_LEAD "fifos should be empty and phase should have changed\n", CMDINFO(CURRENT_SC));
+ disp_ports(shpnt);
+ }
+
+ if(DATA_LEN!=GETSTCNT()) {
+ printk(ERR_LEAD
+ "manual transfer count differs from automatic (count=%d;stcnt=%d;diff=%d;fifostat=%d)",
+ CMDINFO(CURRENT_SC), DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN, GETPORT(FIFOSTAT));
+ disp_ports(shpnt);
+ mdelay(10000);
+ }
+}
+
+static void datai_end(struct Scsi_Host *shpnt)
+{
+ CURRENT_SC->resid -= GETSTCNT();
+
+ DPRINTK(debug_datai,
+ DEBUG_LEAD "datai_end: request_bufflen=%d resid=%d stcnt=%d\n",
+ CMDINFO(CURRENT_SC), CURRENT_SC->request_bufflen, CURRENT_SC->resid, GETSTCNT());
+
+ SETPORT(SXFRCTL0, CH1|CLRSTCNT);
+ SETPORT(DMACNTRL0, 0);
+}
+
+/*
+ * data out phase
+ *
+ */
+static void datao_init(struct Scsi_Host *shpnt)
+{
+ SETPORT(DMACNTRL0, WRITE_READ | RSTFIFO);
+ SETPORT(DMACNTRL0, WRITE_READ | ENDMA);
+
+ SETPORT(SXFRCTL0, CH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE );
+
+ DATA_LEN = CURRENT_SC->resid;
+
+ DPRINTK(debug_datao,
+ DEBUG_LEAD "datao_init: request_bufflen=%d; resid=%d\n",
+ CMDINFO(CURRENT_SC), CURRENT_SC->request_bufflen, CURRENT_SC->resid);
+}
+
+static void datao_run(struct Scsi_Host *shpnt)
+{
+ unsigned long the_time;
+ int data_count;
+
+ /* until phase changes or all data sent */
+ while(TESTLO(DMASTAT, INTSTAT) && CURRENT_SC->SCp.this_residual>0) {
+ data_count = 128;
+ if(data_count > CURRENT_SC->SCp.this_residual)
+ data_count=CURRENT_SC->SCp.this_residual;
+
+ if(TESTLO(DMASTAT, DFIFOEMP)) {
+ printk(ERR_LEAD "datao fifo not empty (%d)", CMDINFO(CURRENT_SC), GETPORT(FIFOSTAT));
+ disp_ports(shpnt);
+ break;
+ }
+
+ if(data_count & 1) {
+ SETPORT(DMACNTRL0,WRITE_READ|ENDMA|_8BIT);
+ SETPORT(DATAPORT, *CURRENT_SC->SCp.ptr++);
+ CURRENT_SC->SCp.this_residual--;
+ CURRENT_SC->resid--;
+ SETPORT(DMACNTRL0,WRITE_READ|ENDMA);
+ }
+
+ if(data_count > 1) {
+ data_count >>= 1;
+ outsw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ CURRENT_SC->resid -= 2 * data_count;
+ }
+
+ if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer);
+ CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
+ }
+
+ the_time=jiffies + 100*HZ;
+ while(TESTLO(DMASTAT, DFIFOEMP|INTSTAT) && time_before(jiffies,the_time))
+ barrier();
+
+ if(TESTLO(DMASTAT, DFIFOEMP|INTSTAT)) {
+ printk(ERR_LEAD "dataout timeout", CMDINFO(CURRENT_SC));
+ disp_ports(shpnt);
+ break;
+ }
+ }
+}
+
+static void datao_end(struct Scsi_Host *shpnt)
+{
+ if(TESTLO(DMASTAT, DFIFOEMP)) {
+ int data_count = (DATA_LEN - CURRENT_SC->resid) - GETSTCNT();
+
+ DPRINTK(debug_datao, DEBUG_LEAD "datao: %d bytes to resend (%d written, %d transferred)\n",
+ CMDINFO(CURRENT_SC),
+ data_count,
+ DATA_LEN-CURRENT_SC->resid,
+ GETSTCNT());
+
+ CURRENT_SC->resid += data_count;
+
+ if(CURRENT_SC->use_sg) {
+ data_count -= CURRENT_SC->SCp.ptr - SG_ADDRESS(CURRENT_SC->SCp.buffer);
+ while(data_count>0) {
+ CURRENT_SC->SCp.buffer--;
+ CURRENT_SC->SCp.buffers_residual++;
+ data_count -= CURRENT_SC->SCp.buffer->length;
+ }
+ CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer) - data_count;
+ CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length + data_count;
+ } else {
+ CURRENT_SC->SCp.ptr -= data_count;
+ CURRENT_SC->SCp.this_residual += data_count;
+ }
+ }
+
+ DPRINTK(debug_datao, DEBUG_LEAD "datao_end: request_bufflen=%d; resid=%d; stcnt=%d\n",
+ CMDINFO(CURRENT_SC),
+ CURRENT_SC->request_bufflen,
+ CURRENT_SC->resid,
+ GETSTCNT());
+
+ SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(DMACNTRL0, 0);
+}
+
+/*
+ * figure out what state we're in
+ *
+ */
+static int update_state(struct Scsi_Host *shpnt)
+{
+ int dataphase=0;
+ unsigned int stat0 = GETPORT(SSTAT0);
+ unsigned int stat1 = GETPORT(SSTAT1);
+
+ PREVSTATE = STATE;
+ STATE=unknown;
+
+ if(stat1 & SCSIRSTI) {
+ STATE=rsti;
+ SETPORT(SCSISEQ,0);
+ SETPORT(SSTAT1,SCSIRSTI);
+ } else if(stat0 & SELDI && PREVSTATE==busfree) {
+ STATE=seldi;
+ } else if(stat0 & SELDO && CURRENT_SC && (CURRENT_SC->SCp.phase & selecting)) {
+ STATE=seldo;
+ } else if(stat1 & SELTO) {
+ STATE=selto;
+ } else if(stat1 & BUSFREE) {
+ STATE=busfree;
+ SETPORT(SSTAT1,BUSFREE);
+ } else if(stat1 & SCSIPERR) {
+ STATE=parerr;
+ SETPORT(SSTAT1,SCSIPERR);
+ } else if(stat1 & REQINIT) {
+ switch(GETPORT(SCSISIG) & P_MASK) {
+ case P_MSGI: STATE=msgi; break;
+ case P_MSGO: STATE=msgo; break;
+ case P_DATAO: STATE=datao; break;
+ case P_DATAI: STATE=datai; break;
+ case P_STATUS: STATE=status; break;
+ case P_CMD: STATE=cmd; break;
+ }
+ dataphase=1;
+ }
+
+ if((stat0 & SELDI) && STATE!=seldi && !dataphase) {
+ printk(INFO_LEAD "reselection missed?", CMDINFO(CURRENT_SC));
+ disp_ports(shpnt);
+ }
+
+ if(STATE!=PREVSTATE) {
+ LASTSTATE=PREVSTATE;
+ }
+
+ return dataphase;
+}
+
+/*
+ * handle parity error
+ *
+ * FIXME: in which phase?
+ *
+ */
+static void parerr_run(struct Scsi_Host *shpnt)
+{
+ printk(ERR_LEAD "parity error\n", CMDINFO(CURRENT_SC));
+ done(shpnt, DID_PARITY << 16);
+}
+
+/*
+ * handle reset in
+ *
+ */
+static void rsti_run(struct Scsi_Host *shpnt)
+{
+ Scsi_Cmnd *ptr;
+
+ printk(KERN_NOTICE "aha152x%d: scsi reset in\n", HOSTNO);
+
+ ptr=DISCONNECTED_SC;
+ while(ptr) {
+ Scsi_Cmnd *next = SCNEXT(ptr);
+
+ if (!ptr->device->soft_reset) {
+ remove_SC(&DISCONNECTED_SC, ptr);
+
+ kfree(ptr->host_scribble);
+ ptr->host_scribble=NULL;
+
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(ptr);
+ }
+
+ ptr = next;
+ }
+
+ if(CURRENT_SC && !CURRENT_SC->device->soft_reset)
+ done(shpnt, DID_RESET << 16 );
+}
+
+
+/*
+ * bottom-half handler
+ *
+ */
+static void is_complete(struct Scsi_Host *shpnt)
+{
+ int dataphase;
+ unsigned long flags;
+ int pending;
+
+ DO_LOCK(flags);
+ if(HOSTDATA(shpnt)->in_intr) {
+ DO_UNLOCK(flags);
+ /* aha152x_error never returns.. */
+ aha152x_error(shpnt, "bottom-half already running!?");
+ }
+ HOSTDATA(shpnt)->in_intr++;
+
+ /*
+ * loop while there are interrupt conditions pending
+ *
+ */
+ do {
+ unsigned long start = jiffies;
+ DO_UNLOCK(flags);
+
+ dataphase=update_state(shpnt);
+
+ DPRINTK(debug_phases, LEAD "start %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name);
+
+ /*
+ * end previous state
+ *
+ */
+ if(PREVSTATE!=STATE && states[PREVSTATE].end)
+ states[PREVSTATE].end(shpnt);
+
+ /*
+ * disable SPIO mode if previous phase used it
+ * and this one doesn't
+ *
+ */
+ if(states[PREVSTATE].spio && !states[STATE].spio) {
+ SETPORT(SXFRCTL0, CH1);
+ SETPORT(DMACNTRL0, 0);
+ if(CURRENT_SC)
+ CURRENT_SC->SCp.phase &= ~spiordy;
+ }
+
+ /*
+ * accept current dataphase phase
+ *
+ */
+ if(dataphase) {
+ SETPORT(SSTAT0, REQINIT);
+ SETPORT(SCSISIG, GETPORT(SCSISIG) & P_MASK);
+ SETPORT(SSTAT1, PHASECHG);
+ }
+
+ /*
+ * enable SPIO mode if previous didn't use it
+ * and this one does
+ *
+ */
+ if(!states[PREVSTATE].spio && states[STATE].spio) {
+ SETPORT(DMACNTRL0, 0);
+ SETPORT(SXFRCTL0, CH1|SPIOEN);
+ if(CURRENT_SC)
+ CURRENT_SC->SCp.phase |= spiordy;
+ }
+
+ /*
+ * initialize for new state
+ *
+ */
+ if(PREVSTATE!=STATE && states[STATE].init)
+ states[STATE].init(shpnt);
+
+ /*
+ * handle current state
+ *
+ */
+ if(states[STATE].run)
+ states[STATE].run(shpnt);
+ else
+ printk(ERR_LEAD "unexpected state (%x)\n", CMDINFO(CURRENT_SC), STATE);
+
+ /*
+ * setup controller to interrupt on
+ * the next expected condition and
+ * loop if it's already there
+ *
+ */
+ DO_LOCK(flags);
+ pending=setup_expected_interrupts(shpnt);
+#if defined(AHA152X_STAT)
+ HOSTDATA(shpnt)->count[STATE]++;
+ if(PREVSTATE!=STATE)
+ HOSTDATA(shpnt)->count_trans[STATE]++;
+ HOSTDATA(shpnt)->time[STATE] += jiffies-start;
+#endif
+
+ DPRINTK(debug_phases, LEAD "end %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name);
+ } while(pending);
+
+ /*
+ * enable interrupts and leave bottom-half
+ *
+ */
+ HOSTDATA(shpnt)->in_intr--;
+ SETBITS(DMACNTRL0, INTEN);
+ DO_UNLOCK(flags);
+}
+
+
+/*
+ * Dump the current driver status and panic
+ */
+static void aha152x_error(struct Scsi_Host *shpnt, char *msg)
+{
+ printk(KERN_EMERG "\naha152x%d: %s\n", HOSTNO, msg);
+ show_queues(shpnt);
+ panic("aha152x panic\n");
+}
+
+/*
+ * Display registers of AIC-6260
+ */
+static void disp_ports(struct Scsi_Host *shpnt)
+{
+#if defined(AHA152X_DEBUG)
+ int s;
+
+ printk("\n%s: %s(%s) ",
+ CURRENT_SC ? "busy" : "waiting",
+ states[STATE].name,
+ states[PREVSTATE].name);
+
+ s = GETPORT(SCSISEQ);
+ printk("SCSISEQ( ");
+ if (s & TEMODEO)
+ printk("TARGET MODE ");
+ if (s & ENSELO)
+ printk("SELO ");
+ if (s & ENSELI)
+ printk("SELI ");
+ if (s & ENRESELI)
+ printk("RESELI ");
+ if (s & ENAUTOATNO)
+ printk("AUTOATNO ");
+ if (s & ENAUTOATNI)
+ printk("AUTOATNI ");
+ if (s & ENAUTOATNP)
+ printk("AUTOATNP ");
+ if (s & SCSIRSTO)
+ printk("SCSIRSTO ");
+ printk(");");
+
+ printk(" SCSISIG(");
+ s = GETPORT(SCSISIG);
+ switch (s & P_MASK) {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*invalid*");
+ break;
+ }
+
+ printk("); ");
+
+ printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ printk("SSTAT( ");
+ s = GETPORT(SSTAT0);
+ if (s & TARGET)
+ printk("TARGET ");
+ if (s & SELDO)
+ printk("SELDO ");
+ if (s & SELDI)
+ printk("SELDI ");
+ if (s & SELINGO)
+ printk("SELINGO ");
+ if (s & SWRAP)
+ printk("SWRAP ");
+ if (s & SDONE)
+ printk("SDONE ");
+ if (s & SPIORDY)
+ printk("SPIORDY ");
+ if (s & DMADONE)
+ printk("DMADONE ");
+
+ s = GETPORT(SSTAT1);
+ if (s & SELTO)
+ printk("SELTO ");
+ if (s & ATNTARG)
+ printk("ATNTARG ");
+ if (s & SCSIRSTI)
+ printk("SCSIRSTI ");
+ if (s & PHASEMIS)
+ printk("PHASEMIS ");
+ if (s & BUSFREE)
+ printk("BUSFREE ");
+ if (s & SCSIPERR)
+ printk("SCSIPERR ");
+ if (s & PHASECHG)
+ printk("PHASECHG ");
+ if (s & REQINIT)
+ printk("REQINIT ");
+ printk("); ");
+
+
+ printk("SSTAT( ");
+
+ s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if (s & TARGET)
+ printk("TARGET ");
+ if (s & SELDO)
+ printk("SELDO ");
+ if (s & SELDI)
+ printk("SELDI ");
+ if (s & SELINGO)
+ printk("SELINGO ");
+ if (s & SWRAP)
+ printk("SWRAP ");
+ if (s & SDONE)
+ printk("SDONE ");
+ if (s & SPIORDY)
+ printk("SPIORDY ");
+ if (s & DMADONE)
+ printk("DMADONE ");
+
+ s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if (s & SELTO)
+ printk("SELTO ");
+ if (s & ATNTARG)
+ printk("ATNTARG ");
+ if (s & SCSIRSTI)
+ printk("SCSIRSTI ");
+ if (s & PHASEMIS)
+ printk("PHASEMIS ");
+ if (s & BUSFREE)
+ printk("BUSFREE ");
+ if (s & SCSIPERR)
+ printk("SCSIPERR ");
+ if (s & PHASECHG)
+ printk("PHASECHG ");
+ if (s & REQINIT)
+ printk("REQINIT ");
+ printk("); ");
+
+ printk("SXFRCTL0( ");
+
+ s = GETPORT(SXFRCTL0);
+ if (s & SCSIEN)
+ printk("SCSIEN ");
+ if (s & DMAEN)
+ printk("DMAEN ");
+ if (s & CH1)
+ printk("CH1 ");
+ if (s & CLRSTCNT)
+ printk("CLRSTCNT ");
+ if (s & SPIOEN)
+ printk("SPIOEN ");
+ if (s & CLRCH1)
+ printk("CLRCH1 ");
+ printk("); ");
+
+ printk("SIGNAL( ");
+
+ s = GETPORT(SCSISIG);
+ if (s & SIG_ATNI)
+ printk("ATNI ");
+ if (s & SIG_SELI)
+ printk("SELI ");
+ if (s & SIG_BSYI)
+ printk("BSYI ");
+ if (s & SIG_REQI)
+ printk("REQI ");
+ if (s & SIG_ACKI)
+ printk("ACKI ");
+ printk("); ");
+
+ printk("SELID (%02x), ", GETPORT(SELID));
+
+ printk("STCNT (%d), ", GETSTCNT());
+
+ printk("SSTAT2( ");
+
+ s = GETPORT(SSTAT2);
+ if (s & SOFFSET)
+ printk("SOFFSET ");
+ if (s & SEMPTY)
+ printk("SEMPTY ");
+ if (s & SFULL)
+ printk("SFULL ");
+ printk("); SFCNT (%d); ", s & (SFULL | SFCNT));
+
+ s = GETPORT(SSTAT3);
+ printk("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
+
+ printk("SSTAT4( ");
+ s = GETPORT(SSTAT4);
+ if (s & SYNCERR)
+ printk("SYNCERR ");
+ if (s & FWERR)
+ printk("FWERR ");
+ if (s & FRERR)
+ printk("FRERR ");
+ printk("); ");
+
+ printk("DMACNTRL0( ");
+ s = GETPORT(DMACNTRL0);
+ printk("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ printk("%s ", s & DMA ? "DMA" : "PIO");
+ printk("%s ", s & WRITE_READ ? "WRITE" : "READ");
+ if (s & ENDMA)
+ printk("ENDMA ");
+ if (s & INTEN)
+ printk("INTEN ");
+ if (s & RSTFIFO)
+ printk("RSTFIFO ");
+ if (s & SWINT)
+ printk("SWINT ");
+ printk("); ");
+
+ printk("DMASTAT( ");
+ s = GETPORT(DMASTAT);
+ if (s & ATDONE)
+ printk("ATDONE ");
+ if (s & WORDRDY)
+ printk("WORDRDY ");
+ if (s & DFIFOFULL)
+ printk("DFIFOFULL ");
+ if (s & DFIFOEMP)
+ printk("DFIFOEMP ");
+ printk(")\n");
+#endif
+}
+
+/*
+ * display enabled interrupts
+ */
+static void disp_enintr(struct Scsi_Host *shpnt)
+{
+ int s;
+
+ printk(KERN_DEBUG "enabled interrupts ( ");
+
+ s = GETPORT(SIMODE0);
+ if (s & ENSELDO)
+ printk("ENSELDO ");
+ if (s & ENSELDI)
+ printk("ENSELDI ");
+ if (s & ENSELINGO)
+ printk("ENSELINGO ");
+ if (s & ENSWRAP)
+ printk("ENSWRAP ");
+ if (s & ENSDONE)
+ printk("ENSDONE ");
+ if (s & ENSPIORDY)
+ printk("ENSPIORDY ");
+ if (s & ENDMADONE)
+ printk("ENDMADONE ");
+
+ s = GETPORT(SIMODE1);
+ if (s & ENSELTIMO)
+ printk("ENSELTIMO ");
+ if (s & ENATNTARG)
+ printk("ENATNTARG ");
+ if (s & ENPHASEMIS)
+ printk("ENPHASEMIS ");
+ if (s & ENBUSFREE)
+ printk("ENBUSFREE ");
+ if (s & ENSCSIPERR)
+ printk("ENSCSIPERR ");
+ if (s & ENPHASECHG)
+ printk("ENPHASECHG ");
+ if (s & ENREQINIT)
+ printk("ENREQINIT ");
+ printk(")\n");
+}
+
+/*
+ * Show the command data of a command
+ */
+static void show_command(Scsi_Cmnd *ptr)
+{
+ printk(KERN_DEBUG "0x%08x: target=%d; lun=%d; cmnd=(",
+ (unsigned int) ptr, ptr->device->id, ptr->device->lun);
+
+ print_command(ptr->cmnd);
+
+ printk(KERN_DEBUG "); request_bufflen=%d; resid=%d; phase |",
+ ptr->request_bufflen, ptr->resid);
+
+ if (ptr->SCp.phase & not_issued)
+ printk("not issued|");
+ if (ptr->SCp.phase & selecting)
+ printk("selecting|");
+ if (ptr->SCp.phase & identified)
+ printk("identified|");
+ if (ptr->SCp.phase & disconnected)
+ printk("disconnected|");
+ if (ptr->SCp.phase & completed)
+ printk("completed|");
+ if (ptr->SCp.phase & spiordy)
+ printk("spiordy|");
+ if (ptr->SCp.phase & syncneg)
+ printk("syncneg|");
+ if (ptr->SCp.phase & aborted)
+ printk("aborted|");
+ if (ptr->SCp.phase & resetted)
+ printk("resetted|");
+ if( SCDATA(ptr) ) {
+ printk("; next=0x%p\n", SCNEXT(ptr));
+ } else {
+ printk("; next=(host scribble NULL)\n");
+ }
+}
+
+/*
+ * Dump the queued data
+ */
+static void show_queues(struct Scsi_Host *shpnt)
+{
+ Scsi_Cmnd *ptr;
+ unsigned long flags;
+
+ DO_LOCK(flags);
+ printk(KERN_DEBUG "\nqueue status:\nissue_SC:\n");
+ for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
+ show_command(ptr);
+ DO_UNLOCK(flags);
+
+ printk(KERN_DEBUG "current_SC:\n");
+ if (CURRENT_SC)
+ show_command(CURRENT_SC);
+ else
+ printk(KERN_DEBUG "none\n");
+
+ printk(KERN_DEBUG "disconnected_SC:\n");
+ for (ptr = DISCONNECTED_SC; ptr; ptr = SCDATA(ptr) ? SCNEXT(ptr) : NULL)
+ show_command(ptr);
+
+ disp_ports(shpnt);
+ disp_enintr(shpnt);
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+static int get_command(char *pos, Scsi_Cmnd * ptr)
+{
+ char *start = pos;
+ int i;
+
+ SPRINTF("0x%08x: target=%d; lun=%d; cmnd=( ",
+ (unsigned int) ptr, ptr->device->id, ptr->device->lun);
+
+ for (i = 0; i < COMMAND_SIZE(ptr->cmnd[0]); i++)
+ SPRINTF("0x%02x ", ptr->cmnd[i]);
+
+ SPRINTF("); resid=%d; residual=%d; buffers=%d; phase |",
+ ptr->resid, ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if (ptr->SCp.phase & not_issued)
+ SPRINTF("not issued|");
+ if (ptr->SCp.phase & selecting)
+ SPRINTF("selecting|");
+ if (ptr->SCp.phase & disconnected)
+ SPRINTF("disconnected|");
+ if (ptr->SCp.phase & aborted)
+ SPRINTF("aborted|");
+ if (ptr->SCp.phase & identified)
+ SPRINTF("identified|");
+ if (ptr->SCp.phase & completed)
+ SPRINTF("completed|");
+ if (ptr->SCp.phase & spiordy)
+ SPRINTF("spiordy|");
+ if (ptr->SCp.phase & syncneg)
+ SPRINTF("syncneg|");
+ SPRINTF("; next=0x%p\n", SCNEXT(ptr));
+
+ return (pos - start);
+}
+
+static int get_ports(struct Scsi_Host *shpnt, char *pos)
+{
+ char *start = pos;
+ int s;
+
+ SPRINTF("\n%s: %s(%s) ", CURRENT_SC ? "on bus" : "waiting", states[STATE].name, states[PREVSTATE].name);
+
+ s = GETPORT(SCSISEQ);
+ SPRINTF("SCSISEQ( ");
+ if (s & TEMODEO)
+ SPRINTF("TARGET MODE ");
+ if (s & ENSELO)
+ SPRINTF("SELO ");
+ if (s & ENSELI)
+ SPRINTF("SELI ");
+ if (s & ENRESELI)
+ SPRINTF("RESELI ");
+ if (s & ENAUTOATNO)
+ SPRINTF("AUTOATNO ");
+ if (s & ENAUTOATNI)
+ SPRINTF("AUTOATNI ");
+ if (s & ENAUTOATNP)
+ SPRINTF("AUTOATNP ");
+ if (s & SCSIRSTO)
+ SPRINTF("SCSIRSTO ");
+ SPRINTF(");");
+
+ SPRINTF(" SCSISIG(");
+ s = GETPORT(SCSISIG);
+ switch (s & P_MASK) {
+ case P_DATAO:
+ SPRINTF("DATA OUT");
+ break;
+ case P_DATAI:
+ SPRINTF("DATA IN");
+ break;
+ case P_CMD:
+ SPRINTF("COMMAND");
+ break;
+ case P_STATUS:
+ SPRINTF("STATUS");
+ break;
+ case P_MSGO:
+ SPRINTF("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ SPRINTF("MESSAGE IN");
+ break;
+ default:
+ SPRINTF("*invalid*");
+ break;
+ }
+
+ SPRINTF("); ");
+
+ SPRINTF("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ SPRINTF("SSTAT( ");
+ s = GETPORT(SSTAT0);
+ if (s & TARGET)
+ SPRINTF("TARGET ");
+ if (s & SELDO)
+ SPRINTF("SELDO ");
+ if (s & SELDI)
+ SPRINTF("SELDI ");
+ if (s & SELINGO)
+ SPRINTF("SELINGO ");
+ if (s & SWRAP)
+ SPRINTF("SWRAP ");
+ if (s & SDONE)
+ SPRINTF("SDONE ");
+ if (s & SPIORDY)
+ SPRINTF("SPIORDY ");
+ if (s & DMADONE)
+ SPRINTF("DMADONE ");
+
+ s = GETPORT(SSTAT1);
+ if (s & SELTO)
+ SPRINTF("SELTO ");
+ if (s & ATNTARG)
+ SPRINTF("ATNTARG ");
+ if (s & SCSIRSTI)
+ SPRINTF("SCSIRSTI ");
+ if (s & PHASEMIS)
+ SPRINTF("PHASEMIS ");
+ if (s & BUSFREE)
+ SPRINTF("BUSFREE ");
+ if (s & SCSIPERR)
+ SPRINTF("SCSIPERR ");
+ if (s & PHASECHG)
+ SPRINTF("PHASECHG ");
+ if (s & REQINIT)
+ SPRINTF("REQINIT ");
+ SPRINTF("); ");
+
+
+ SPRINTF("SSTAT( ");
+
+ s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if (s & TARGET)
+ SPRINTF("TARGET ");
+ if (s & SELDO)
+ SPRINTF("SELDO ");
+ if (s & SELDI)
+ SPRINTF("SELDI ");
+ if (s & SELINGO)
+ SPRINTF("SELINGO ");
+ if (s & SWRAP)
+ SPRINTF("SWRAP ");
+ if (s & SDONE)
+ SPRINTF("SDONE ");
+ if (s & SPIORDY)
+ SPRINTF("SPIORDY ");
+ if (s & DMADONE)
+ SPRINTF("DMADONE ");
+
+ s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if (s & SELTO)
+ SPRINTF("SELTO ");
+ if (s & ATNTARG)
+ SPRINTF("ATNTARG ");
+ if (s & SCSIRSTI)
+ SPRINTF("SCSIRSTI ");
+ if (s & PHASEMIS)
+ SPRINTF("PHASEMIS ");
+ if (s & BUSFREE)
+ SPRINTF("BUSFREE ");
+ if (s & SCSIPERR)
+ SPRINTF("SCSIPERR ");
+ if (s & PHASECHG)
+ SPRINTF("PHASECHG ");
+ if (s & REQINIT)
+ SPRINTF("REQINIT ");
+ SPRINTF("); ");
+
+ SPRINTF("SXFRCTL0( ");
+
+ s = GETPORT(SXFRCTL0);
+ if (s & SCSIEN)
+ SPRINTF("SCSIEN ");
+ if (s & DMAEN)
+ SPRINTF("DMAEN ");
+ if (s & CH1)
+ SPRINTF("CH1 ");
+ if (s & CLRSTCNT)
+ SPRINTF("CLRSTCNT ");
+ if (s & SPIOEN)
+ SPRINTF("SPIOEN ");
+ if (s & CLRCH1)
+ SPRINTF("CLRCH1 ");
+ SPRINTF("); ");
+
+ SPRINTF("SIGNAL( ");
+
+ s = GETPORT(SCSISIG);
+ if (s & SIG_ATNI)
+ SPRINTF("ATNI ");
+ if (s & SIG_SELI)
+ SPRINTF("SELI ");
+ if (s & SIG_BSYI)
+ SPRINTF("BSYI ");
+ if (s & SIG_REQI)
+ SPRINTF("REQI ");
+ if (s & SIG_ACKI)
+ SPRINTF("ACKI ");
+ SPRINTF("); ");
+
+ SPRINTF("SELID(%02x), ", GETPORT(SELID));
+
+ SPRINTF("STCNT(%d), ", GETSTCNT());
+
+ SPRINTF("SSTAT2( ");
+
+ s = GETPORT(SSTAT2);
+ if (s & SOFFSET)
+ SPRINTF("SOFFSET ");
+ if (s & SEMPTY)
+ SPRINTF("SEMPTY ");
+ if (s & SFULL)
+ SPRINTF("SFULL ");
+ SPRINTF("); SFCNT (%d); ", s & (SFULL | SFCNT));
+
+ s = GETPORT(SSTAT3);
+ SPRINTF("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
+
+ SPRINTF("SSTAT4( ");
+ s = GETPORT(SSTAT4);
+ if (s & SYNCERR)
+ SPRINTF("SYNCERR ");
+ if (s & FWERR)
+ SPRINTF("FWERR ");
+ if (s & FRERR)
+ SPRINTF("FRERR ");
+ SPRINTF("); ");
+
+ SPRINTF("DMACNTRL0( ");
+ s = GETPORT(DMACNTRL0);
+ SPRINTF("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ SPRINTF("%s ", s & DMA ? "DMA" : "PIO");
+ SPRINTF("%s ", s & WRITE_READ ? "WRITE" : "READ");
+ if (s & ENDMA)
+ SPRINTF("ENDMA ");
+ if (s & INTEN)
+ SPRINTF("INTEN ");
+ if (s & RSTFIFO)
+ SPRINTF("RSTFIFO ");
+ if (s & SWINT)
+ SPRINTF("SWINT ");
+ SPRINTF("); ");
+
+ SPRINTF("DMASTAT( ");
+ s = GETPORT(DMASTAT);
+ if (s & ATDONE)
+ SPRINTF("ATDONE ");
+ if (s & WORDRDY)
+ SPRINTF("WORDRDY ");
+ if (s & DFIFOFULL)
+ SPRINTF("DFIFOFULL ");
+ if (s & DFIFOEMP)
+ SPRINTF("DFIFOEMP ");
+ SPRINTF(")\n");
+
+ SPRINTF("enabled interrupts( ");
+
+ s = GETPORT(SIMODE0);
+ if (s & ENSELDO)
+ SPRINTF("ENSELDO ");
+ if (s & ENSELDI)
+ SPRINTF("ENSELDI ");
+ if (s & ENSELINGO)
+ SPRINTF("ENSELINGO ");
+ if (s & ENSWRAP)
+ SPRINTF("ENSWRAP ");
+ if (s & ENSDONE)
+ SPRINTF("ENSDONE ");
+ if (s & ENSPIORDY)
+ SPRINTF("ENSPIORDY ");
+ if (s & ENDMADONE)
+ SPRINTF("ENDMADONE ");
+
+ s = GETPORT(SIMODE1);
+ if (s & ENSELTIMO)
+ SPRINTF("ENSELTIMO ");
+ if (s & ENATNTARG)
+ SPRINTF("ENATNTARG ");
+ if (s & ENPHASEMIS)
+ SPRINTF("ENPHASEMIS ");
+ if (s & ENBUSFREE)
+ SPRINTF("ENBUSFREE ");
+ if (s & ENSCSIPERR)
+ SPRINTF("ENSCSIPERR ");
+ if (s & ENPHASECHG)
+ SPRINTF("ENPHASECHG ");
+ if (s & ENREQINIT)
+ SPRINTF("ENREQINIT ");
+ SPRINTF(")\n");
+
+ return (pos - start);
+}
+
+static int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ if(!shpnt || !buffer || length<8 || strncmp("aha152x ", buffer, 8)!=0)
+ return -EINVAL;
+
+#if defined(AHA152X_DEBUG)
+ if(length>14 && strncmp("debug ", buffer+8, 6)==0) {
+ int debug = HOSTDATA(shpnt)->debug;
+
+ HOSTDATA(shpnt)->debug = simple_strtoul(buffer+14, NULL, 0);
+
+ printk(KERN_INFO "aha152x%d: debugging options set to 0x%04x (were 0x%04x)\n", HOSTNO, HOSTDATA(shpnt)->debug, debug);
+ } else
+#endif
+#if defined(AHA152X_STAT)
+ if(length>13 && strncmp("reset", buffer+8, 5)==0) {
+ int i;
+
+ HOSTDATA(shpnt)->total_commands=0;
+ HOSTDATA(shpnt)->disconnections=0;
+ HOSTDATA(shpnt)->busfree_without_any_action=0;
+ HOSTDATA(shpnt)->busfree_without_old_command=0;
+ HOSTDATA(shpnt)->busfree_without_new_command=0;
+ HOSTDATA(shpnt)->busfree_without_done_command=0;
+ HOSTDATA(shpnt)->busfree_with_check_condition=0;
+ for (i = idle; i<maxstate; i++) {
+ HOSTDATA(shpnt)->count[i]=0;
+ HOSTDATA(shpnt)->count_trans[i]=0;
+ HOSTDATA(shpnt)->time[i]=0;
+ }
+
+ printk(KERN_INFO "aha152x%d: stats reseted.\n", HOSTNO);
+
+ } else
+#endif
+ {
+ return -EINVAL;
+ }
+
+
+ return length;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+
+static int aha152x_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start,
+ off_t offset, int length, int inout)
+{
+ int i;
+ char *pos = buffer;
+ Scsi_Cmnd *ptr;
+ unsigned long flags;
+ int thislength;
+
+ DPRINTK(debug_procinfo,
+ KERN_DEBUG "aha152x_proc_info: buffer=%p offset=%ld length=%d hostno=%d inout=%d\n",
+ buffer, offset, length, shpnt->host_no, inout);
+
+
+ if (inout)
+ return aha152x_set_info(buffer, length, shpnt);
+
+ SPRINTF(AHA152X_REVID "\n");
+
+ SPRINTF("ioports 0x%04lx to 0x%04lx\n",
+ shpnt->io_port, shpnt->io_port + shpnt->n_io_port - 1);
+ SPRINTF("interrupt 0x%02x\n", shpnt->irq);
+ SPRINTF("disconnection/reconnection %s\n",
+ RECONNECT ? "enabled" : "disabled");
+ SPRINTF("parity checking %s\n",
+ PARITY ? "enabled" : "disabled");
+ SPRINTF("synchronous transfers %s\n",
+ SYNCHRONOUS ? "enabled" : "disabled");
+ SPRINTF("%d commands currently queued\n", HOSTDATA(shpnt)->commands);
+
+ if(SYNCHRONOUS) {
+ SPRINTF("synchronously operating targets (tick=50 ns):\n");
+ for (i = 0; i < 8; i++)
+ if (HOSTDATA(shpnt)->syncrate[i] & 0x7f)
+ SPRINTF("target %d: period %dT/%dns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2),
+ (((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2) * 50,
+ HOSTDATA(shpnt)->syncrate[i] & 0x0f);
+ }
+#if defined(AHA152X_DEBUG)
+#define PDEBUG(flags,txt) \
+ if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);
+
+ SPRINTF("enabled debugging options: ");
+
+ PDEBUG(debug_procinfo, "procinfo");
+ PDEBUG(debug_queue, "queue");
+ PDEBUG(debug_intr, "interrupt");
+ PDEBUG(debug_selection, "selection");
+ PDEBUG(debug_msgo, "message out");
+ PDEBUG(debug_msgi, "message in");
+ PDEBUG(debug_status, "status");
+ PDEBUG(debug_cmd, "command");
+ PDEBUG(debug_datai, "data in");
+ PDEBUG(debug_datao, "data out");
+ PDEBUG(debug_eh, "eh");
+ PDEBUG(debug_locks, "locks");
+ PDEBUG(debug_phases, "phases");
+
+ SPRINTF("\n");
+#endif
+
+ SPRINTF("\nqueue status:\n");
+ DO_LOCK(flags);
+ if (ISSUE_SC) {
+ SPRINTF("not yet issued commands:\n");
+ for (ptr = ISSUE_SC; ptr; ptr = SCNEXT(ptr))
+ pos += get_command(pos, ptr);
+ } else
+ SPRINTF("no not yet issued commands\n");
+ DO_UNLOCK(flags);
+
+ if (CURRENT_SC) {
+ SPRINTF("current command:\n");
+ pos += get_command(pos, CURRENT_SC);
+ } else
+ SPRINTF("no current command\n");
+
+ if (DISCONNECTED_SC) {
+ SPRINTF("disconnected commands:\n");
+ for (ptr = DISCONNECTED_SC; ptr; ptr = SCNEXT(ptr))
+ pos += get_command(pos, ptr);
+ } else
+ SPRINTF("no disconnected commands\n");
+
+ pos += get_ports(shpnt, pos);
+
+#if defined(AHA152X_STAT)
+ SPRINTF("statistics:\n"
+ "total commands: %d\n"
+ "disconnections: %d\n"
+ "busfree with check condition: %d\n"
+ "busfree without old command: %d\n"
+ "busfree without new command: %d\n"
+ "busfree without done command: %d\n"
+ "busfree without any action: %d\n"
+ "state "
+ "transitions "
+ "count "
+ "time\n",
+ HOSTDATA(shpnt)->total_commands,
+ HOSTDATA(shpnt)->disconnections,
+ HOSTDATA(shpnt)->busfree_with_check_condition,
+ HOSTDATA(shpnt)->busfree_without_old_command,
+ HOSTDATA(shpnt)->busfree_without_new_command,
+ HOSTDATA(shpnt)->busfree_without_done_command,
+ HOSTDATA(shpnt)->busfree_without_any_action);
+ for(i=0; i<maxstate; i++) {
+ SPRINTF("%-10s %-12d %-12d %-12ld\n",
+ states[i].name,
+ HOSTDATA(shpnt)->count_trans[i],
+ HOSTDATA(shpnt)->count[i],
+ HOSTDATA(shpnt)->time[i]);
+ }
+#endif
+
+ DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: pos=%p\n", pos);
+
+ thislength = pos - (buffer + offset);
+ DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: length=%d thislength=%d\n", length, thislength);
+
+ if(thislength<0) {
+ DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: output too short\n");
+ *start = NULL;
+ return 0;
+ }
+
+ thislength = thislength<length ? thislength : length;
+
+ DPRINTK(debug_procinfo, KERN_DEBUG "aha152x_proc_info: return %d\n", thislength);
+
+ *start = buffer + offset;
+ return thislength < length ? thislength : length;
+}
+
+static Scsi_Host_Template aha152x_driver_template = {
+ .module = THIS_MODULE,
+ .name = AHA152X_REVID,
+ .proc_name = "aha152x",
+ .proc_info = aha152x_proc_info,
+ .queuecommand = aha152x_queue,
+ .eh_abort_handler = aha152x_abort,
+ .eh_device_reset_handler = aha152x_device_reset,
+ .eh_bus_reset_handler = aha152x_bus_reset,
+ .eh_host_reset_handler = aha152x_host_reset,
+ .bios_param = aha152x_biosparam,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+#if !defined(PCMCIA)
+static int setup_count;
+static struct aha152x_setup setup[2];
+
+/* possible i/o addresses for the AIC-6260; default first */
+static unsigned short ports[] = { 0x340, 0x140 };
+
+#if !defined(SKIP_BIOSTEST)
+/* possible locations for the Adaptec BIOS; defaults first */
+static unsigned int addresses[] =
+{
+ 0xdc000, /* default first */
+ 0xc8000,
+ 0xcc000,
+ 0xd0000,
+ 0xd4000,
+ 0xd8000,
+ 0xe0000,
+ 0xeb800, /* VTech Platinum SMP */
+ 0xf0000,
+};
+
+/* signatures for various AIC-6[23]60 based controllers.
+ The point in detecting signatures is to avoid useless and maybe
+ harmful probes on ports. I'm not sure that all listed boards pass
+ auto-configuration. For those which fail the BIOS signature is
+ obsolete, because user intervention to supply the configuration is
+ needed anyway. May be an information whether or not the BIOS supports
+ extended translation could be also useful here. */
+static struct signature {
+ unsigned char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] =
+{
+ { "Adaptec AHA-1520 BIOS", 0x102e, 21 },
+ /* Adaptec 152x */
+ { "Adaptec AHA-1520B", 0x000b, 17 },
+ /* Adaptec 152x rev B */
+ { "Adaptec AHA-1520B", 0x0026, 17 },
+ /* Iomega Jaz Jet ISA (AIC6370Q) */
+ { "Adaptec ASW-B626 BIOS", 0x1029, 21 },
+ /* on-board controller */
+ { "Adaptec BIOS: ASW-B626", 0x000f, 22 },
+ /* on-board controller */
+ { "Adaptec ASW-B626 S2", 0x2e6c, 19 },
+ /* on-board controller */
+ { "Adaptec BIOS:AIC-6360", 0x000c, 21 },
+ /* on-board controller */
+ { "ScsiPro SP-360 BIOS", 0x2873, 19 },
+ /* ScsiPro-Controller */
+ { "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 },
+ /* Gigabyte Local-Bus-SCSI */
+ { "Adaptec BIOS:AVA-282X", 0x000c, 21 },
+ /* Adaptec 282x */
+ { "Adaptec IBM Dock II SCSI", 0x2edd, 24 },
+ /* IBM Thinkpad Dock II */
+ { "Adaptec BIOS:AHA-1532P", 0x001c, 22 },
+ /* IBM Thinkpad Dock II SCSI */
+ { "DTC3520A Host Adapter BIOS", 0x318a, 26 },
+ /* DTC 3520A ISA SCSI */
+};
+#endif /* !SKIP_BIOSTEST */
+
+/*
+ * Test, if port_base is valid.
+ *
+ */
+static int aha152x_porttest(int io_port)
+{
+ int i;
+
+ SETPORT(io_port + O_DMACNTRL1, 0); /* reset stack pointer */
+ for (i = 0; i < 16; i++)
+ SETPORT(io_port + O_STACK, i);
+
+ SETPORT(io_port + O_DMACNTRL1, 0); /* reset stack pointer */
+ for (i = 0; i < 16 && GETPORT(io_port + O_STACK) == i; i++)
+ ;
+
+ return (i == 16);
+}
+
+static int tc1550_porttest(int io_port)
+{
+ int i;
+
+ SETPORT(io_port + O_TC_DMACNTRL1, 0); /* reset stack pointer */
+ for (i = 0; i < 16; i++)
+ SETPORT(io_port + O_STACK, i);
+
+ SETPORT(io_port + O_TC_DMACNTRL1, 0); /* reset stack pointer */
+ for (i = 0; i < 16 && GETPORT(io_port + O_TC_STACK) == i; i++)
+ ;
+
+ return (i == 16);
+}
+
+
+static int checksetup(struct aha152x_setup *setup)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ports) && (setup->io_port != ports[i]); i++)
+ ;
+
+ if (i == ARRAY_SIZE(ports))
+ return 0;
+
+ if ( request_region(setup->io_port, IO_RANGE, "aha152x")==0 ) {
+ printk(KERN_ERR "aha152x: io port 0x%x busy.\n", setup->io_port);
+ return 0;
+ }
+
+ if( aha152x_porttest(setup->io_port) ) {
+ setup->tc1550=0;
+ } else if( tc1550_porttest(setup->io_port) ) {
+ setup->tc1550=1;
+ } else {
+ release_region(setup->io_port, IO_RANGE);
+ return 0;
+ }
+
+ release_region(setup->io_port, IO_RANGE);
+
+ if ((setup->irq < IRQ_MIN) || (setup->irq > IRQ_MAX))
+ return 0;
+
+ if ((setup->scsiid < 0) || (setup->scsiid > 7))
+ return 0;
+
+ if ((setup->reconnect < 0) || (setup->reconnect > 1))
+ return 0;
+
+ if ((setup->parity < 0) || (setup->parity > 1))
+ return 0;
+
+ if ((setup->synchronous < 0) || (setup->synchronous > 1))
+ return 0;
+
+ if ((setup->ext_trans < 0) || (setup->ext_trans > 1))
+ return 0;
+
+
+ return 1;
+}
+
+
+static int __init aha152x_init(void)
+{
+ int i, j, ok;
+#if defined(AUTOCONF)
+ aha152x_config conf;
+#endif
+#ifdef __ISAPNP__
+ struct pnp_dev *dev=NULL, *pnpdev[2] = {NULL, NULL};
+#endif
+
+ if ( setup_count ) {
+ printk(KERN_INFO "aha152x: processing commandline: ");
+
+ for (i = 0; i<setup_count; i++) {
+ if (!checksetup(&setup[i])) {
+ printk(KERN_ERR "\naha152x: %s\n", setup[i].conf);
+ printk(KERN_ERR "aha152x: invalid line\n");
+ }
+ }
+ printk("ok\n");
+ }
+
+#if defined(SETUP0)
+ if (setup_count < ARRAY_SIZE(setup)) {
+ struct aha152x_setup override = SETUP0;
+
+ if (setup_count == 0 || (override.io_port != setup[0].io_port)) {
+ if (!checksetup(&override)) {
+ printk(KERN_ERR "\naha152x: invalid override SETUP0={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay,
+ override.ext_trans);
+ } else
+ setup[setup_count++] = override;
+ }
+ }
+#endif
+
+#if defined(SETUP1)
+ if (setup_count < ARRAY_SIZE(setup)) {
+ struct aha152x_setup override = SETUP1;
+
+ if (setup_count == 0 || (override.io_port != setup[0].io_port)) {
+ if (!checksetup(&override)) {
+ printk(KERN_ERR "\naha152x: invalid override SETUP1={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay,
+ override.ext_trans);
+ } else
+ setup[setup_count++] = override;
+ }
+ }
+#endif
+
+#if defined(MODULE)
+ if (setup_count<ARRAY_SIZE(setup) && (aha152x[0]!=0 || io[0]!=0 || irq[0]!=0)) {
+ if(aha152x[0]!=0) {
+ setup[setup_count].conf = "";
+ setup[setup_count].io_port = aha152x[0];
+ setup[setup_count].irq = aha152x[1];
+ setup[setup_count].scsiid = aha152x[2];
+ setup[setup_count].reconnect = aha152x[3];
+ setup[setup_count].parity = aha152x[4];
+ setup[setup_count].synchronous = aha152x[5];
+ setup[setup_count].delay = aha152x[6];
+ setup[setup_count].ext_trans = aha152x[7];
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = aha152x[8];
+#endif
+ } else if(io[0]!=0 || irq[0]!=0) {
+ if(io[0]!=0) setup[setup_count].io_port = io[0];
+ if(irq[0]!=0) setup[setup_count].irq = irq[0];
+
+ setup[setup_count].scsiid = scsiid[0];
+ setup[setup_count].reconnect = reconnect[0];
+ setup[setup_count].parity = parity[0];
+ setup[setup_count].synchronous = sync[0];
+ setup[setup_count].delay = delay[0];
+ setup[setup_count].ext_trans = exttrans[0];
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = debug[0];
+#endif
+ }
+
+ if (checksetup(&setup[setup_count]))
+ setup_count++;
+ else
+ printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n",
+ setup[setup_count].io_port,
+ setup[setup_count].irq,
+ setup[setup_count].scsiid,
+ setup[setup_count].reconnect,
+ setup[setup_count].parity,
+ setup[setup_count].synchronous,
+ setup[setup_count].delay,
+ setup[setup_count].ext_trans);
+ }
+
+ if (setup_count<ARRAY_SIZE(setup) && (aha152x1[0]!=0 || io[1]!=0 || irq[1]!=0)) {
+ if(aha152x1[0]!=0) {
+ setup[setup_count].conf = "";
+ setup[setup_count].io_port = aha152x1[0];
+ setup[setup_count].irq = aha152x1[1];
+ setup[setup_count].scsiid = aha152x1[2];
+ setup[setup_count].reconnect = aha152x1[3];
+ setup[setup_count].parity = aha152x1[4];
+ setup[setup_count].synchronous = aha152x1[5];
+ setup[setup_count].delay = aha152x1[6];
+ setup[setup_count].ext_trans = aha152x1[7];
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = aha152x1[8];
+#endif
+ } else if(io[1]!=0 || irq[1]!=0) {
+ if(io[1]!=0) setup[setup_count].io_port = io[1];
+ if(irq[1]!=0) setup[setup_count].irq = irq[1];
+
+ setup[setup_count].scsiid = scsiid[1];
+ setup[setup_count].reconnect = reconnect[1];
+ setup[setup_count].parity = parity[1];
+ setup[setup_count].synchronous = sync[1];
+ setup[setup_count].delay = delay[1];
+ setup[setup_count].ext_trans = exttrans[1];
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = debug[1];
+#endif
+ }
+ if (checksetup(&setup[setup_count]))
+ setup_count++;
+ else
+ printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n",
+ setup[setup_count].io_port,
+ setup[setup_count].irq,
+ setup[setup_count].scsiid,
+ setup[setup_count].reconnect,
+ setup[setup_count].parity,
+ setup[setup_count].synchronous,
+ setup[setup_count].delay,
+ setup[setup_count].ext_trans);
+ }
+#endif
+
+#ifdef __ISAPNP__
+ for(i=0; setup_count<ARRAY_SIZE(setup) && id_table[i].vendor; i++) {
+ while ( setup_count<ARRAY_SIZE(setup) &&
+ (dev=pnp_find_dev(NULL, id_table[i].vendor, id_table[i].function, dev)) ) {
+ if (pnp_device_attach(dev) < 0)
+ continue;
+
+ if (pnp_activate_dev(dev) < 0) {
+ pnp_device_detach(dev);
+ continue;
+ }
+
+ if (!pnp_port_valid(dev, 0)) {
+ pnp_device_detach(dev);
+ continue;
+ }
+
+ if (setup_count==1 && pnp_port_start(dev, 0)==setup[0].io_port) {
+ pnp_device_detach(dev);
+ continue;
+ }
+
+ setup[setup_count].io_port = pnp_port_start(dev, 0);
+ setup[setup_count].irq = pnp_irq(dev, 0);
+ setup[setup_count].scsiid = 7;
+ setup[setup_count].reconnect = 1;
+ setup[setup_count].parity = 1;
+ setup[setup_count].synchronous = 1;
+ setup[setup_count].delay = DELAY_DEFAULT;
+ setup[setup_count].ext_trans = 0;
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = DEBUG_DEFAULT;
+#endif
+#if defined(__ISAPNP__)
+ pnpdev[setup_count] = dev;
+#endif
+ printk (KERN_INFO
+ "aha152x: found ISAPnP adapter at io=0x%03x, irq=%d\n",
+ setup[setup_count].io_port, setup[setup_count].irq);
+ setup_count++;
+ }
+ }
+#endif
+
+#if defined(AUTOCONF)
+ if (setup_count<ARRAY_SIZE(setup)) {
+#if !defined(SKIP_BIOSTEST)
+ ok = 0;
+ for (i = 0; i < ARRAY_SIZE(addresses) && !ok; i++) {
+ void __iomem *p = ioremap(addresses[i], 0x4000);
+ if (!p)
+ continue;
+ for (j = 0; j<ARRAY_SIZE(signatures) && !ok; j++)
+ ok = check_signature(p + signatures[j].sig_offset,
+ signatures[j].signature, signatures[j].sig_length);
+ iounmap(p);
+ }
+ if (!ok && setup_count == 0)
+ return 0;
+
+ printk(KERN_INFO "aha152x: BIOS test: passed, ");
+#else
+ printk(KERN_INFO "aha152x: ");
+#endif /* !SKIP_BIOSTEST */
+
+ ok = 0;
+ for (i = 0; i < ARRAY_SIZE(ports) && setup_count < 2; i++) {
+ if ((setup_count == 1) && (setup[0].io_port == ports[i]))
+ continue;
+
+ if ( request_region(ports[i], IO_RANGE, "aha152x")==0 ) {
+ printk(KERN_ERR "aha152x: io port 0x%x busy.\n", ports[i]);
+ continue;
+ }
+
+ if (aha152x_porttest(ports[i])) {
+ setup[setup_count].tc1550 = 0;
+
+ conf.cf_port =
+ (GETPORT(ports[i] + O_PORTA) << 8) + GETPORT(ports[i] + O_PORTB);
+ } else if (tc1550_porttest(ports[i])) {
+ setup[setup_count].tc1550 = 1;
+
+ conf.cf_port =
+ (GETPORT(ports[i] + O_TC_PORTA) << 8) + GETPORT(ports[i] + O_TC_PORTB);
+ } else {
+ release_region(ports[i], IO_RANGE);
+ continue;
+ }
+
+ release_region(ports[i], IO_RANGE);
+
+ ok++;
+ setup[setup_count].io_port = ports[i];
+ setup[setup_count].irq = IRQ_MIN + conf.cf_irq;
+ setup[setup_count].scsiid = conf.cf_id;
+ setup[setup_count].reconnect = conf.cf_tardisc;
+ setup[setup_count].parity = !conf.cf_parity;
+ setup[setup_count].synchronous = conf.cf_syncneg;
+ setup[setup_count].delay = DELAY_DEFAULT;
+ setup[setup_count].ext_trans = 0;
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = DEBUG_DEFAULT;
+#endif
+ setup_count++;
+
+ }
+
+ if (ok)
+ printk("auto configuration: ok, ");
+ }
+#endif
+
+ printk("%d controller(s) configured\n", setup_count);
+
+ for (i=0; i<setup_count; i++) {
+ if ( request_region(setup[i].io_port, IO_RANGE, "aha152x") ) {
+ struct Scsi_Host *shpnt = aha152x_probe_one(&setup[i]);
+
+ if( !shpnt ) {
+ release_region(setup[i].io_port, IO_RANGE);
+#if defined(__ISAPNP__)
+ } else if( pnpdev[i] ) {
+ HOSTDATA(shpnt)->pnpdev=pnpdev[i];
+ pnpdev[i]=NULL;
+#endif
+ }
+ } else {
+ printk(KERN_ERR "aha152x: io port 0x%x busy.\n", setup[i].io_port);
+ }
+
+#if defined(__ISAPNP__)
+ if( pnpdev[i] )
+ pnp_device_detach(pnpdev[i]);
+#endif
+ }
+
+ return registered_count>0;
+}
+
+static void __exit aha152x_exit(void)
+{
+ int i;
+
+ for(i=0; i<ARRAY_SIZE(setup); i++) {
+ aha152x_release(aha152x_host[i]);
+ aha152x_host[i]=NULL;
+ }
+}
+
+module_init(aha152x_init);
+module_exit(aha152x_exit);
+
+#if !defined(MODULE)
+static int __init aha152x_setup(char *str)
+{
+#if defined(AHA152X_DEBUG)
+ int ints[11];
+#else
+ int ints[10];
+#endif
+ get_options(str, ARRAY_SIZE(ints), ints);
+
+ if(setup_count>=ARRAY_SIZE(setup)) {
+ printk(KERN_ERR "aha152x: you can only configure up to two controllers\n");
+ return 1;
+ }
+
+ setup[setup_count].conf = str;
+ setup[setup_count].io_port = ints[0] >= 1 ? ints[1] : 0x340;
+ setup[setup_count].irq = ints[0] >= 2 ? ints[2] : 11;
+ setup[setup_count].scsiid = ints[0] >= 3 ? ints[3] : 7;
+ setup[setup_count].reconnect = ints[0] >= 4 ? ints[4] : 1;
+ setup[setup_count].parity = ints[0] >= 5 ? ints[5] : 1;
+ setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 1;
+ setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
+ setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0;
+#if defined(AHA152X_DEBUG)
+ setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT;
+ if (ints[0] > 9) {
+ printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n");
+#else
+ if (ints[0] > 8) { /*}*/
+ printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
+#endif
+ } else {
+ setup_count++;
+ return 0;
+ }
+
+ return 1;
+}
+__setup("aha152x=", aha152x_setup);
+#endif
+
+#endif /* !PCMCIA */
diff --git a/drivers/scsi/aha152x.h b/drivers/scsi/aha152x.h
new file mode 100644
index 000000000000..d277613af29b
--- /dev/null
+++ b/drivers/scsi/aha152x.h
@@ -0,0 +1,337 @@
+#ifndef _AHA152X_H
+#define _AHA152X_H
+
+/*
+ * $Id: aha152x.h,v 2.7 2004/01/24 11:39:03 fischer Exp $
+ */
+
+/* number of queueable commands
+ (unless we support more than 1 cmd_per_lun this should do) */
+#define AHA152X_MAXQUEUE 7
+
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 2.7 $"
+
+/* port addresses */
+#define SCSISEQ (HOSTIOPORT0+0x00) /* SCSI sequence control */
+#define SXFRCTL0 (HOSTIOPORT0+0x01) /* SCSI transfer control 0 */
+#define SXFRCTL1 (HOSTIOPORT0+0x02) /* SCSI transfer control 1 */
+#define SCSISIG (HOSTIOPORT0+0x03) /* SCSI signal in/out */
+#define SCSIRATE (HOSTIOPORT0+0x04) /* SCSI rate control */
+#define SELID (HOSTIOPORT0+0x05) /* selection/reselection ID */
+#define SCSIID SELID /* SCSI ID */
+#define SCSIDAT (HOSTIOPORT0+0x06) /* SCSI latched data */
+#define SCSIBUS (HOSTIOPORT0+0x07) /* SCSI data bus */
+#define STCNT0 (HOSTIOPORT0+0x08) /* SCSI transfer count 0 */
+#define STCNT1 (HOSTIOPORT0+0x09) /* SCSI transfer count 1 */
+#define STCNT2 (HOSTIOPORT0+0x0a) /* SCSI transfer count 2 */
+#define SSTAT0 (HOSTIOPORT0+0x0b) /* SCSI interrupt status 0 */
+#define SSTAT1 (HOSTIOPORT0+0x0c) /* SCSI interrupt status 1 */
+#define SSTAT2 (HOSTIOPORT0+0x0d) /* SCSI interrupt status 2 */
+#define SCSITEST (HOSTIOPORT0+0x0e) /* SCSI test control */
+#define SSTAT3 SCSITEST /* SCSI interrupt status 3 */
+#define SSTAT4 (HOSTIOPORT0+0x0f) /* SCSI status 4 */
+#define SIMODE0 (HOSTIOPORT1+0x10) /* SCSI interrupt mode 0 */
+#define SIMODE1 (HOSTIOPORT1+0x11) /* SCSI interrupt mode 1 */
+#define DMACNTRL0 (HOSTIOPORT1+0x12) /* DMA control 0 */
+#define DMACNTRL1 (HOSTIOPORT1+0x13) /* DMA control 1 */
+#define DMASTAT (HOSTIOPORT1+0x14) /* DMA status */
+#define FIFOSTAT (HOSTIOPORT1+0x15) /* FIFO status */
+#define DATAPORT (HOSTIOPORT1+0x16) /* DATA port */
+#define BRSTCNTRL (HOSTIOPORT1+0x18) /* burst control */
+#define PORTA (HOSTIOPORT1+0x1a) /* PORT A */
+#define PORTB (HOSTIOPORT1+0x1b) /* PORT B */
+#define REV (HOSTIOPORT1+0x1c) /* revision */
+#define STACK (HOSTIOPORT1+0x1d) /* stack */
+#define TEST (HOSTIOPORT1+0x1e) /* test register */
+
+#define IO_RANGE 0x20
+
+/* used in aha152x_porttest */
+#define O_PORTA 0x1a /* PORT A */
+#define O_PORTB 0x1b /* PORT B */
+#define O_DMACNTRL1 0x13 /* DMA control 1 */
+#define O_STACK 0x1d /* stack */
+
+/* used in tc1550_porttest */
+#define O_TC_PORTA 0x0a /* PORT A */
+#define O_TC_PORTB 0x0b /* PORT B */
+#define O_TC_DMACNTRL1 0x03 /* DMA control 1 */
+#define O_TC_STACK 0x0d /* stack */
+
+/* bits and bitmasks to ports */
+
+/* SCSI sequence control */
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRESELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/* SCSI transfer control 0 */
+#define SCSIEN 0x80
+#define DMAEN 0x40
+#define CH1 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define CLRCH1 0x02
+
+/* SCSI transfer control 1 */
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18 /* mask */
+#define STIMESEL_ 3
+#define ENSTIMER 0x04
+#define BYTEALIGN 0x02
+
+/* SCSI signal IN */
+#define SIG_CDI 0x80
+#define SIG_IOI 0x40
+#define SIG_MSGI 0x20
+#define SIG_ATNI 0x10
+#define SIG_SELI 0x08
+#define SIG_BSYI 0x04
+#define SIG_REQI 0x02
+#define SIG_ACKI 0x01
+
+/* SCSI Phases */
+#define P_MASK (SIG_MSGI|SIG_CDI|SIG_IOI)
+#define P_DATAO (0)
+#define P_DATAI (SIG_IOI)
+#define P_CMD (SIG_CDI)
+#define P_STATUS (SIG_CDI|SIG_IOI)
+#define P_MSGO (SIG_MSGI|SIG_CDI)
+#define P_MSGI (SIG_MSGI|SIG_CDI|SIG_IOI)
+
+/* SCSI signal OUT */
+#define SIG_CDO 0x80
+#define SIG_IOO 0x40
+#define SIG_MSGO 0x20
+#define SIG_ATNO 0x10
+#define SIG_SELO 0x08
+#define SIG_BSYO 0x04
+#define SIG_REQO 0x02
+#define SIG_ACKO 0x01
+
+/* SCSI rate control */
+#define SXFR 0x70 /* mask */
+#define SXFR_ 4
+#define SOFS 0x0f /* mask */
+
+/* SCSI ID */
+#define OID 0x70
+#define OID_ 4
+#define TID 0x07
+
+/* SCSI transfer count */
+#define GETSTCNT() ( (GETPORT(STCNT2)<<16) \
+ + (GETPORT(STCNT1)<< 8) \
+ + GETPORT(STCNT0) )
+
+#define SETSTCNT(X) { SETPORT(STCNT2, ((X) & 0xFF0000) >> 16); \
+ SETPORT(STCNT1, ((X) & 0x00FF00) >> 8); \
+ SETPORT(STCNT0, ((X) & 0x0000FF) ); }
+
+/* SCSI interrupt status */
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define SWRAP 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define SETSDONE 0x80
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+#define CLRSDONE 0x04
+#define CLRSPIORDY 0x02
+#define CLRDMADONE 0x01
+
+/* SCSI status 1 */
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define CLRSELTIMO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+/* SCSI status 2 */
+#define SOFFSET 0x20
+#define SEMPTY 0x10
+#define SFULL 0x08
+#define SFCNT 0x07 /* mask */
+
+/* SCSI status 3 */
+#define SCSICNT 0xf0 /* mask */
+#define SCSICNT_ 4
+#define OFFCNT 0x0f /* mask */
+
+/* SCSI TEST control */
+#define SCTESTU 0x08
+#define SCTESTD 0x04
+#define STCTEST 0x01
+
+/* SCSI status 4 */
+#define SYNCERR 0x04
+#define FWERR 0x02
+#define FRERR 0x01
+
+#define CLRSYNCERR 0x04
+#define CLRFWERR 0x02
+#define CLRFRERR 0x01
+
+/* SCSI interrupt mode 0 */
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+/* SCSI interrupt mode 1 */
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/* DMA control 0 */
+#define ENDMA 0x80
+#define _8BIT 0x40
+#define DMA 0x20
+#define WRITE_READ 0x08
+#define INTEN 0x04
+#define RSTFIFO 0x02
+#define SWINT 0x01
+
+/* DMA control 1 */
+#define PWRDWN 0x80
+#define STK 0x07 /* mask */
+
+/* DMA status */
+#define ATDONE 0x80
+#define WORDRDY 0x40
+#define INTSTAT 0x20
+#define DFIFOFULL 0x10
+#define DFIFOEMP 0x08
+
+/* BURST control */
+#define BON 0xf0
+#define BOFF 0x0f
+
+/* TEST REGISTER */
+#define BOFFTMR 0x40
+#define BONTMR 0x20
+#define STCNTH 0x10
+#define STCNTM 0x08
+#define STCNTL 0x04
+#define SCSIBLK 0x02
+#define DMABLK 0x01
+
+/* On the AHA-152x board PORTA and PORTB contain
+ some information about the board's configuration. */
+typedef union {
+ struct {
+ unsigned reserved:2; /* reserved */
+ unsigned tardisc:1; /* Target disconnect: 0=disabled, 1=enabled */
+ unsigned syncneg:1; /* Initial sync neg: 0=disabled, 1=enabled */
+ unsigned msgclasses:2; /* Message classes
+ 0=#4
+ 1=#0, #1, #2, #3, #4
+ 2=#0, #3, #4
+ 3=#0, #4
+ */
+ unsigned boot:1; /* boot: 0=disabled, 1=enabled */
+ unsigned dma:1; /* Transfer mode: 0=PIO; 1=DMA */
+ unsigned id:3; /* SCSI-id */
+ unsigned irq:2; /* IRQ-Channel: 0,3=12, 1=10, 2=11 */
+ unsigned dmachan:2; /* DMA-Channel: 0=0, 1=5, 2=6, 3=7 */
+ unsigned parity:1; /* SCSI-parity: 1=enabled 0=disabled */
+ } fields;
+ unsigned short port;
+} aha152x_config ;
+
+#define cf_parity fields.parity
+#define cf_dmachan fields.dmachan
+#define cf_irq fields.irq
+#define cf_id fields.id
+#define cf_dma fields.dma
+#define cf_boot fields.boot
+#define cf_msgclasses fields.msgclasses
+#define cf_syncneg fields.syncneg
+#define cf_tardisc fields.tardisc
+#define cf_port port
+
+/* Some macros to manipulate ports and their bits */
+
+#define SETPORT(PORT, VAL) outb( (VAL), (PORT) )
+#define GETPORT(PORT) inb( PORT )
+#define SETBITS(PORT, BITS) outb( (inb(PORT) | (BITS)), (PORT) )
+#define CLRBITS(PORT, BITS) outb( (inb(PORT) & ~(BITS)), (PORT) )
+#define TESTHI(PORT, BITS) ((inb(PORT) & (BITS)) == (BITS))
+#define TESTLO(PORT, BITS) ((inb(PORT) & (BITS)) == 0)
+
+#define SETRATE(RATE) SETPORT(SCSIRATE,(RATE) & 0x7f)
+
+#if defined(AHA152X_DEBUG)
+enum {
+ debug_procinfo = 0x0001,
+ debug_queue = 0x0002,
+ debug_locks = 0x0004,
+ debug_intr = 0x0008,
+ debug_selection = 0x0010,
+ debug_msgo = 0x0020,
+ debug_msgi = 0x0040,
+ debug_status = 0x0080,
+ debug_cmd = 0x0100,
+ debug_datai = 0x0200,
+ debug_datao = 0x0400,
+ debug_eh = 0x0800,
+ debug_done = 0x1000,
+ debug_phases = 0x2000,
+};
+#endif
+
+/* for the pcmcia stub */
+struct aha152x_setup {
+ int io_port;
+ int irq;
+ int scsiid;
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+ int ext_trans;
+ int tc1550;
+#if defined(AHA152X_DEBUG)
+ int debug;
+#endif
+ char *conf;
+};
+
+struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *);
+void aha152x_release(struct Scsi_Host *);
+int aha152x_host_reset(Scsi_Cmnd *);
+
+#endif /* _AHA152X_H */
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
new file mode 100644
index 000000000000..e9920a009593
--- /dev/null
+++ b/drivers/scsi/aha1542.c
@@ -0,0 +1,1832 @@
+/* $Id: aha1542.c,v 1.1 1992/07/24 06:27:38 root Exp root $
+ * linux/kernel/aha1542.c
+ *
+ * Copyright (C) 1992 Tommy Thorn
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Modified by Eric Youngdale
+ * Use request_irq and request_dma to help prevent unexpected conflicts
+ * Set up on-board DMA controller, such that we do not have to
+ * have the bios enabled to use the aha1542.
+ * Modified by David Gentzel
+ * Don't call request_dma if dma mask is 0 (for BusLogic BT-445S VL-Bus
+ * controller).
+ * Modified by Matti Aarnio
+ * Accept parameters from LILO cmd-line. -- 1-Oct-94
+ * Modified by Mike McLagan <mike.mclagan@linux.org>
+ * Recognise extended mode on AHA1542CP, different bit than 1542CF
+ * 1-Jan-97
+ * Modified by Bjorn L. Thordarson and Einar Thor Einarsson
+ * Recognize that DMA0 is valid DMA channel -- 13-Jul-98
+ * Modified by Chris Faulhaber <jedgar@fxp.org>
+ * Added module command-line options
+ * 19-Jul-99
+ * Modified by Adam Fritzler <mid@auk.cx>
+ * Added proper detection of the AHA-1640 (MCA version of AHA-1540)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/blkdev.h>
+#include <linux/mca.h>
+#include <linux/mca-legacy.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aha1542.h"
+
+#define SCSI_BUF_PA(address) isa_virt_to_bus(address)
+#define SCSI_SG_PA(sgent) (isa_page_to_bus((sgent)->page) + (sgent)->offset)
+
+static void BAD_DMA(void *address, unsigned int length)
+{
+ printk(KERN_CRIT "buf vaddress %p paddress 0x%lx length %d\n",
+ address,
+ SCSI_BUF_PA(address),
+ length);
+ panic("Buffer at physical address > 16Mb used for aha1542");
+}
+
+static void BAD_SG_DMA(Scsi_Cmnd * SCpnt,
+ struct scatterlist *sgpnt,
+ int nseg,
+ int badseg)
+{
+ printk(KERN_CRIT "sgpnt[%d:%d] page %p/0x%llx length %u\n",
+ badseg, nseg,
+ page_address(sgpnt[badseg].page) + sgpnt[badseg].offset,
+ (unsigned long long)SCSI_SG_PA(&sgpnt[badseg]),
+ sgpnt[badseg].length);
+
+ /*
+ * Not safe to continue.
+ */
+ panic("Buffer at physical address > 16Mb used for aha1542");
+}
+
+#include<linux/stat.h>
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+/*
+ static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1542.c,v 1.1 1992/07/24 06:27:38 root Exp root $";
+ */
+
+/* The adaptec can be configured for quite a number of addresses, but
+ I generally do not want the card poking around at random. We allow
+ two addresses - this allows people to use the Adaptec with a Midi
+ card, which also used 0x330 -- can be overridden with LILO! */
+
+#define MAXBOARDS 4 /* Increase this and the sizes of the
+ arrays below, if you need more.. */
+
+/* Boards 3,4 slots are reserved for ISAPnP/MCA scans */
+
+static unsigned int bases[MAXBOARDS] __initdata = {0x330, 0x334, 0, 0};
+
+/* set by aha1542_setup according to the command line; they also may
+ be marked __initdata, but require zero initializers then */
+
+static int setup_called[MAXBOARDS];
+static int setup_buson[MAXBOARDS];
+static int setup_busoff[MAXBOARDS];
+static int setup_dmaspeed[MAXBOARDS] __initdata = { -1, -1, -1, -1 };
+
+/*
+ * LILO/Module params: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]
+ *
+ * Where: <PORTBASE> is any of the valid AHA addresses:
+ * 0x130, 0x134, 0x230, 0x234, 0x330, 0x334
+ * <BUSON> is the time (in microsecs) that AHA spends on the AT-bus
+ * when transferring data. 1542A power-on default is 11us,
+ * valid values are in range: 2..15 (decimal)
+ * <BUSOFF> is the time that AHA spends OFF THE BUS after while
+ * it is transferring data (not to monopolize the bus).
+ * Power-on default is 4us, valid range: 1..64 microseconds.
+ * <DMASPEED> Default is jumper selected (1542A: on the J1),
+ * but experimenter can alter it with this.
+ * Valid values: 5, 6, 7, 8, 10 (MB/s)
+ * Factory default is 5 MB/s.
+ */
+
+#if defined(MODULE)
+static int isapnp = 0;
+static int aha1542[] = {0x330, 11, 4, -1};
+module_param_array(aha1542, int, NULL, 0);
+module_param(isapnp, bool, 0);
+
+static struct isapnp_device_id id_table[] __initdata = {
+ {
+ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('A', 'D', 'P'), ISAPNP_FUNCTION(0x1542),
+ 0
+ },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+#else
+static int isapnp = 1;
+#endif
+
+#define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */
+#define BIOS_TRANSLATION_6432 1 /* Default case these days */
+#define BIOS_TRANSLATION_25563 2 /* Big disk case */
+
+struct aha1542_hostdata {
+ /* This will effectively start both of them at the first mailbox */
+ int bios_translation; /* Mapping bios uses - for compatibility */
+ int aha1542_last_mbi_used;
+ int aha1542_last_mbo_used;
+ Scsi_Cmnd *SCint[AHA1542_MAILBOXES];
+ struct mailbox mb[2 * AHA1542_MAILBOXES];
+ struct ccb ccb[AHA1542_MAILBOXES];
+};
+
+#define HOSTDATA(host) ((struct aha1542_hostdata *) &host->hostdata)
+
+static struct Scsi_Host *aha_host[7]; /* One for each IRQ level (9-15) */
+
+static DEFINE_SPINLOCK(aha1542_lock);
+
+
+
+#define WAITnexttimeout 3000000
+
+static void setup_mailboxes(int base_io, struct Scsi_Host *shpnt);
+static int aha1542_restart(struct Scsi_Host *shost);
+static void aha1542_intr_handle(struct Scsi_Host *shost, void *dev_id, struct pt_regs *regs);
+static irqreturn_t do_aha1542_intr_handle(int irq, void *dev_id,
+ struct pt_regs *regs);
+
+#define aha1542_intr_reset(base) outb(IRST, CONTROL(base))
+
+#define WAIT(port, mask, allof, noneof) \
+ { register int WAITbits; \
+ register int WAITtimeout = WAITnexttimeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+/* Similar to WAIT, except we use the udelay call to regulate the
+ amount of time we wait. */
+#define WAITd(port, mask, allof, noneof, timeout) \
+ { register int WAITbits; \
+ register int WAITtimeout = timeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ mdelay(1); \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+static void aha1542_stat(void)
+{
+/* int s = inb(STATUS), i = inb(INTRFLAGS);
+ printk("status=%x intrflags=%x\n", s, i, WAITnexttimeout-WAITtimeout); */
+}
+
+/* This is a bit complicated, but we need to make sure that an interrupt
+ routine does not send something out while we are in the middle of this.
+ Fortunately, it is only at boot time that multi-byte messages
+ are ever sent. */
+static int aha1542_out(unsigned int base, unchar * cmdp, int len)
+{
+ unsigned long flags = 0;
+ int got_lock;
+
+ if (len == 1) {
+ got_lock = 0;
+ while (1 == 1) {
+ WAIT(STATUS(base), CDF, 0, CDF);
+ spin_lock_irqsave(&aha1542_lock, flags);
+ if (inb(STATUS(base)) & CDF) {
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ continue;
+ }
+ outb(*cmdp, DATA(base));
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ return 0;
+ }
+ } else {
+ spin_lock_irqsave(&aha1542_lock, flags);
+ got_lock = 1;
+ while (len--) {
+ WAIT(STATUS(base), CDF, 0, CDF);
+ outb(*cmdp++, DATA(base));
+ }
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ }
+ return 0;
+fail:
+ if (got_lock)
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ printk(KERN_ERR "aha1542_out failed(%d): ", len + 1);
+ aha1542_stat();
+ return 1;
+}
+
+/* Only used at boot time, so we do not need to worry about latency as much
+ here */
+
+static int __init aha1542_in(unsigned int base, unchar * cmdp, int len)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&aha1542_lock, flags);
+ while (len--) {
+ WAIT(STATUS(base), DF, DF, 0);
+ *cmdp++ = inb(DATA(base));
+ }
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ return 0;
+fail:
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ printk(KERN_ERR "aha1542_in failed(%d): ", len + 1);
+ aha1542_stat();
+ return 1;
+}
+
+/* Similar to aha1542_in, except that we wait a very short period of time.
+ We use this if we know the board is alive and awake, but we are not sure
+ if the board will respond to the command we are about to send or not */
+static int __init aha1542_in1(unsigned int base, unchar * cmdp, int len)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&aha1542_lock, flags);
+ while (len--) {
+ WAITd(STATUS(base), DF, DF, 0, 100);
+ *cmdp++ = inb(DATA(base));
+ }
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ return 0;
+fail:
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ return 1;
+}
+
+static int makecode(unsigned hosterr, unsigned scsierr)
+{
+ switch (hosterr) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error, interrupt generated */
+ hosterr = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ hosterr = DID_TIME_OUT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ than was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+
+ case 0x15: /* MBO command was not 00, 01 or 02-The first byte of the CB was
+ invalid. This usually indicates a software failure. */
+
+ case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid.
+ This usually indicates a software failure. */
+
+ case 0x17: /* Linked CCB does not have the same LUN-A subsequent CCB of a set
+ of linked CCB's does not specify the same logical unit number as
+ the first. */
+ case 0x18: /* Invalid Target Direction received from Host-The direction of a
+ Target Mode CCB was invalid. */
+
+ case 0x19: /* Duplicate CCB Received in Target Mode-More than once CCB was
+ received to service data transfer between the same target LUN
+ and initiator SCSI ID in the same direction. */
+
+ case 0x1a: /* Invalid CCB or Segment List Parameter-A segment list with a zero
+ length segment or invalid segment list boundaries was received.
+ A CCB parameter was invalid. */
+ DEB(printk("Aha1542: %x %x\n", hosterr, scsierr));
+ hosterr = DID_ERROR; /* Couldn't find any better */
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ hosterr = DID_RESET;
+ break;
+ default:
+ printk(KERN_ERR "aha1542: makecode: unknown hoststatus %x\n", hosterr);
+ break;
+ }
+ return scsierr | (hosterr << 16);
+}
+
+static int __init aha1542_test_port(int bse, struct Scsi_Host *shpnt)
+{
+ unchar inquiry_cmd[] = {CMD_INQUIRY};
+ unchar inquiry_result[4];
+ unchar *cmdp;
+ int len;
+ volatile int debug = 0;
+
+ /* Quick and dirty test for presence of the card. */
+ if (inb(STATUS(bse)) == 0xff)
+ return 0;
+
+ /* Reset the adapter. I ought to make a hard reset, but it's not really necessary */
+
+ /* DEB(printk("aha1542_test_port called \n")); */
+
+ /* In case some other card was probing here, reset interrupts */
+ aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+
+ outb(SRST | IRST /*|SCRST */ , CONTROL(bse));
+
+ mdelay(20); /* Wait a little bit for things to settle down. */
+
+ debug = 1;
+ /* Expect INIT and IDLE, any of the others are bad */
+ WAIT(STATUS(bse), STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+
+ debug = 2;
+ /* Shouldn't have generated any interrupts during reset */
+ if (inb(INTRFLAGS(bse)) & INTRMASK)
+ goto fail;
+
+
+ /* Perform a host adapter inquiry instead so we do not need to set
+ up the mailboxes ahead of time */
+
+ aha1542_out(bse, inquiry_cmd, 1);
+
+ debug = 3;
+ len = 4;
+ cmdp = &inquiry_result[0];
+
+ while (len--) {
+ WAIT(STATUS(bse), DF, DF, 0);
+ *cmdp++ = inb(DATA(bse));
+ }
+
+ debug = 8;
+ /* Reading port should reset DF */
+ if (inb(STATUS(bse)) & DF)
+ goto fail;
+
+ debug = 9;
+ /* When HACC, command is completed, and we're though testing */
+ WAIT(INTRFLAGS(bse), HACC, HACC, 0);
+ /* now initialize adapter */
+
+ debug = 10;
+ /* Clear interrupts */
+ outb(IRST, CONTROL(bse));
+
+ debug = 11;
+
+ return debug; /* 1 = ok */
+fail:
+ return 0; /* 0 = not ok */
+}
+
+/* A quick wrapper for do_aha1542_intr_handle to grab the spin lock */
+static irqreturn_t do_aha1542_intr_handle(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *shost;
+
+ shost = aha_host[irq - 9];
+ if (!shost)
+ panic("Splunge!");
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ aha1542_intr_handle(shost, dev_id, regs);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/* A "high" level interrupt handler */
+static void aha1542_intr_handle(struct Scsi_Host *shost, void *dev_id, struct pt_regs *regs)
+{
+ void (*my_done) (Scsi_Cmnd *) = NULL;
+ int errstatus, mbi, mbo, mbistatus;
+ int number_serviced;
+ unsigned long flags;
+ Scsi_Cmnd *SCtmp;
+ int flag;
+ int needs_restart;
+ struct mailbox *mb;
+ struct ccb *ccb;
+
+ mb = HOSTDATA(shost)->mb;
+ ccb = HOSTDATA(shost)->ccb;
+
+#ifdef DEBUG
+ {
+ flag = inb(INTRFLAGS(shost->io_port));
+ printk(KERN_DEBUG "aha1542_intr_handle: ");
+ if (!(flag & ANYINTR))
+ printk("no interrupt?");
+ if (flag & MBIF)
+ printk("MBIF ");
+ if (flag & MBOA)
+ printk("MBOF ");
+ if (flag & HACC)
+ printk("HACC ");
+ if (flag & SCRD)
+ printk("SCRD ");
+ printk("status %02x\n", inb(STATUS(shost->io_port)));
+ };
+#endif
+ number_serviced = 0;
+ needs_restart = 0;
+
+ while (1 == 1) {
+ flag = inb(INTRFLAGS(shost->io_port));
+
+ /* Check for unusual interrupts. If any of these happen, we should
+ probably do something special, but for now just printing a message
+ is sufficient. A SCSI reset detected is something that we really
+ need to deal with in some way. */
+ if (flag & ~MBIF) {
+ if (flag & MBOA)
+ printk("MBOF ");
+ if (flag & HACC)
+ printk("HACC ");
+ if (flag & SCRD) {
+ needs_restart = 1;
+ printk("SCRD ");
+ }
+ }
+ aha1542_intr_reset(shost->io_port);
+
+ spin_lock_irqsave(&aha1542_lock, flags);
+ mbi = HOSTDATA(shost)->aha1542_last_mbi_used + 1;
+ if (mbi >= 2 * AHA1542_MAILBOXES)
+ mbi = AHA1542_MAILBOXES;
+
+ do {
+ if (mb[mbi].status != 0)
+ break;
+ mbi++;
+ if (mbi >= 2 * AHA1542_MAILBOXES)
+ mbi = AHA1542_MAILBOXES;
+ } while (mbi != HOSTDATA(shost)->aha1542_last_mbi_used);
+
+ if (mb[mbi].status == 0) {
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ /* Hmm, no mail. Must have read it the last time around */
+ if (!number_serviced && !needs_restart)
+ printk(KERN_WARNING "aha1542.c: interrupt received, but no mail.\n");
+ /* We detected a reset. Restart all pending commands for
+ devices that use the hard reset option */
+ if (needs_restart)
+ aha1542_restart(shost);
+ return;
+ };
+
+ mbo = (scsi2int(mb[mbi].ccbptr) - (SCSI_BUF_PA(&ccb[0]))) / sizeof(struct ccb);
+ mbistatus = mb[mbi].status;
+ mb[mbi].status = 0;
+ HOSTDATA(shost)->aha1542_last_mbi_used = mbi;
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+
+#ifdef DEBUG
+ {
+ if (ccb[mbo].tarstat | ccb[mbo].hastat)
+ printk(KERN_DEBUG "aha1542_command: returning %x (status %d)\n",
+ ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
+ };
+#endif
+
+ if (mbistatus == 3)
+ continue; /* Aborted command not found */
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "...done %d %d\n", mbo, mbi);
+#endif
+
+ SCtmp = HOSTDATA(shost)->SCint[mbo];
+
+ if (!SCtmp || !SCtmp->scsi_done) {
+ printk(KERN_WARNING "aha1542_intr_handle: Unexpected interrupt\n");
+ printk(KERN_WARNING "tarstat=%x, hastat=%x idlun=%x ccb#=%d \n", ccb[mbo].tarstat,
+ ccb[mbo].hastat, ccb[mbo].idlun, mbo);
+ return;
+ }
+ my_done = SCtmp->scsi_done;
+ if (SCtmp->host_scribble) {
+ kfree(SCtmp->host_scribble);
+ SCtmp->host_scribble = NULL;
+ }
+ /* Fetch the sense data, and tuck it away, in the required slot. The
+ Adaptec automatically fetches it, and there is no guarantee that
+ we will still have it in the cdb when we come back */
+ if (ccb[mbo].tarstat == 2)
+ memcpy(SCtmp->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
+ sizeof(SCtmp->sense_buffer));
+
+
+ /* is there mail :-) */
+
+ /* more error checking left out here */
+ if (mbistatus != 1)
+ /* This is surely wrong, but I don't know what's right */
+ errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
+ else
+ errstatus = 0;
+
+#ifdef DEBUG
+ if (errstatus)
+ printk(KERN_DEBUG "(aha1542 error:%x %x %x) ", errstatus,
+ ccb[mbo].hastat, ccb[mbo].tarstat);
+#endif
+
+ if (ccb[mbo].tarstat == 2) {
+#ifdef DEBUG
+ int i;
+#endif
+ DEB(printk("aha1542_intr_handle: sense:"));
+#ifdef DEBUG
+ for (i = 0; i < 12; i++)
+ printk("%02x ", ccb[mbo].cdb[ccb[mbo].cdblen + i]);
+ printk("\n");
+#endif
+ /*
+ DEB(printk("aha1542_intr_handle: buf:"));
+ for (i = 0; i < bufflen; i++)
+ printk("%02x ", ((unchar *)buff)[i]);
+ printk("\n");
+ */
+ }
+ DEB(if (errstatus) printk("aha1542_intr_handle: returning %6x\n", errstatus));
+ SCtmp->result = errstatus;
+ HOSTDATA(shost)->SCint[mbo] = NULL; /* This effectively frees up the mailbox slot, as
+ far as queuecommand is concerned */
+ my_done(SCtmp);
+ number_serviced++;
+ };
+}
+
+static int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ unchar ahacmd = CMD_START_SCSI;
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->device->id;
+ unchar lun = SCpnt->device->lun;
+ unsigned long flags;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ int mbo;
+ struct mailbox *mb;
+ struct ccb *ccb;
+
+ DEB(int i);
+
+ mb = HOSTDATA(SCpnt->device->host)->mb;
+ ccb = HOSTDATA(SCpnt->device->host)->ccb;
+
+ DEB(if (target > 1) {
+ SCpnt->result = DID_TIME_OUT << 16;
+ done(SCpnt); return 0;
+ }
+ );
+
+ if (*cmd == REQUEST_SENSE) {
+ /* Don't do the command - we have the sense data already */
+#if 0
+ /* scsi_request_sense() provides a buffer of size 256,
+ so there is no reason to expect equality */
+ if (bufflen != sizeof(SCpnt->sense_buffer))
+ printk(KERN_CRIT "aha1542: Wrong buffer length supplied "
+ "for request sense (%d)\n", bufflen);
+#endif
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ }
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd + 2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd + 2);
+ else
+ i = -1;
+ if (done)
+ printk(KERN_DEBUG "aha1542_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ else
+ printk(KERN_DEBUG "aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ aha1542_stat();
+ printk(KERN_DEBUG "aha1542_queuecommand: dumping scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("%02x ", cmd[i]);
+ printk("\n");
+ if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ return 0; /* we are still testing, so *don't* write */
+#endif
+ /* Use the outgoing mailboxes in a round-robin fashion, because this
+ is how the host adapter will scan for them */
+
+ spin_lock_irqsave(&aha1542_lock, flags);
+ mbo = HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used + 1;
+ if (mbo >= AHA1542_MAILBOXES)
+ mbo = 0;
+
+ do {
+ if (mb[mbo].status == 0 && HOSTDATA(SCpnt->device->host)->SCint[mbo] == NULL)
+ break;
+ mbo++;
+ if (mbo >= AHA1542_MAILBOXES)
+ mbo = 0;
+ } while (mbo != HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used);
+
+ if (mb[mbo].status || HOSTDATA(SCpnt->device->host)->SCint[mbo])
+ panic("Unable to find empty mailbox for aha1542.\n");
+
+ HOSTDATA(SCpnt->device->host)->SCint[mbo] = SCpnt; /* This will effectively prevent someone else from
+ screwing with this cdb. */
+
+ HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used = mbo;
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Sending command (%d %x)...", mbo, done);
+#endif
+
+ any2scsi(mb[mbo].ccbptr, SCSI_BUF_PA(&ccb[mbo])); /* This gets trashed for some reason */
+
+ memset(&ccb[mbo], 0, sizeof(struct ccb));
+
+ ccb[mbo].cdblen = SCpnt->cmd_len;
+
+ direction = 0;
+ if (*cmd == READ_10 || *cmd == READ_6)
+ direction = 8;
+ else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 16;
+
+ memcpy(ccb[mbo].cdb, cmd, ccb[mbo].cdblen);
+
+ if (SCpnt->use_sg) {
+ struct scatterlist *sgpnt;
+ struct chain *cptr;
+#ifdef DEBUG
+ unsigned char *ptr;
+#endif
+ int i;
+ ccb[mbo].op = 2; /* SCSI Initiator Command w/scatter-gather */
+ SCpnt->host_scribble = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ cptr = (struct chain *) SCpnt->host_scribble;
+ if (cptr == NULL) {
+ /* free the claimed mailbox slot */
+ HOSTDATA(SCpnt->device->host)->SCint[mbo] = NULL;
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ for (i = 0; i < SCpnt->use_sg; i++) {
+ if (sgpnt[i].length == 0 || SCpnt->use_sg > 16 ||
+ (((int) sgpnt[i].offset) & 1) || (sgpnt[i].length & 1)) {
+ unsigned char *ptr;
+ printk(KERN_CRIT "Bad segment list supplied to aha1542.c (%d, %d)\n", SCpnt->use_sg, i);
+ for (i = 0; i < SCpnt->use_sg; i++) {
+ printk(KERN_CRIT "%d: %p %d\n", i,
+ (page_address(sgpnt[i].page) +
+ sgpnt[i].offset),
+ sgpnt[i].length);
+ };
+ printk(KERN_CRIT "cptr %x: ", (unsigned int) cptr);
+ ptr = (unsigned char *) &cptr[i];
+ for (i = 0; i < 18; i++)
+ printk("%02x ", ptr[i]);
+ panic("Foooooooood fight!");
+ };
+ any2scsi(cptr[i].dataptr, SCSI_SG_PA(&sgpnt[i]));
+ if (SCSI_SG_PA(&sgpnt[i]) + sgpnt[i].length - 1 > ISA_DMA_THRESHOLD)
+ BAD_SG_DMA(SCpnt, sgpnt, SCpnt->use_sg, i);
+ any2scsi(cptr[i].datalen, sgpnt[i].length);
+ };
+ any2scsi(ccb[mbo].datalen, SCpnt->use_sg * sizeof(struct chain));
+ any2scsi(ccb[mbo].dataptr, SCSI_BUF_PA(cptr));
+#ifdef DEBUG
+ printk("cptr %x: ", cptr);
+ ptr = (unsigned char *) cptr;
+ for (i = 0; i < 18; i++)
+ printk("%02x ", ptr[i]);
+#endif
+ } else {
+ ccb[mbo].op = 0; /* SCSI Initiator Command */
+ SCpnt->host_scribble = NULL;
+ any2scsi(ccb[mbo].datalen, bufflen);
+ if (buff && SCSI_BUF_PA(buff + bufflen - 1) > ISA_DMA_THRESHOLD)
+ BAD_DMA(buff, bufflen);
+ any2scsi(ccb[mbo].dataptr, SCSI_BUF_PA(buff));
+ };
+ ccb[mbo].idlun = (target & 7) << 5 | direction | (lun & 7); /*SCSI Target Id */
+ ccb[mbo].rsalen = 16;
+ ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
+ ccb[mbo].commlinkid = 0;
+
+#ifdef DEBUG
+ {
+ int i;
+ printk(KERN_DEBUG "aha1542_command: sending.. ");
+ for (i = 0; i < sizeof(ccb[mbo]) - 10; i++)
+ printk("%02x ", ((unchar *) & ccb[mbo])[i]);
+ };
+#endif
+
+ if (done) {
+ DEB(printk("aha1542_queuecommand: now waiting for interrupt ");
+ aha1542_stat());
+ SCpnt->scsi_done = done;
+ mb[mbo].status = 1;
+ aha1542_out(SCpnt->device->host->io_port, &ahacmd, 1); /* start scsi command */
+ DEB(aha1542_stat());
+ } else
+ printk("aha1542_queuecommand: done can't be NULL\n");
+
+ return 0;
+}
+
+/* Initialize mailboxes */
+static void setup_mailboxes(int bse, struct Scsi_Host *shpnt)
+{
+ int i;
+ struct mailbox *mb;
+ struct ccb *ccb;
+
+ unchar cmd[5] = { CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
+
+ mb = HOSTDATA(shpnt)->mb;
+ ccb = HOSTDATA(shpnt)->ccb;
+
+ for (i = 0; i < AHA1542_MAILBOXES; i++) {
+ mb[i].status = mb[AHA1542_MAILBOXES + i].status = 0;
+ any2scsi(mb[i].ccbptr, SCSI_BUF_PA(&ccb[i]));
+ };
+ aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+ any2scsi((cmd + 2), SCSI_BUF_PA(mb));
+ aha1542_out(bse, cmd, 5);
+ WAIT(INTRFLAGS(bse), INTRMASK, HACC, 0);
+ while (0) {
+fail:
+ printk(KERN_ERR "aha1542_detect: failed setting up mailboxes\n");
+ }
+ aha1542_intr_reset(bse);
+}
+
+static int __init aha1542_getconfig(int base_io, unsigned char *irq_level, unsigned char *dma_chan, unsigned char *scsi_id)
+{
+ unchar inquiry_cmd[] = {CMD_RETCONF};
+ unchar inquiry_result[3];
+ int i;
+ i = inb(STATUS(base_io));
+ if (i & DF) {
+ i = inb(DATA(base_io));
+ };
+ aha1542_out(base_io, inquiry_cmd, 1);
+ aha1542_in(base_io, inquiry_result, 3);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ while (0) {
+fail:
+ printk(KERN_ERR "aha1542_detect: query board settings\n");
+ }
+ aha1542_intr_reset(base_io);
+ switch (inquiry_result[0]) {
+ case 0x80:
+ *dma_chan = 7;
+ break;
+ case 0x40:
+ *dma_chan = 6;
+ break;
+ case 0x20:
+ *dma_chan = 5;
+ break;
+ case 0x01:
+ *dma_chan = 0;
+ break;
+ case 0:
+ /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
+ Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
+ *dma_chan = 0xFF;
+ break;
+ default:
+ printk(KERN_ERR "Unable to determine Adaptec DMA priority. Disabling board\n");
+ return -1;
+ };
+ switch (inquiry_result[1]) {
+ case 0x40:
+ *irq_level = 15;
+ break;
+ case 0x20:
+ *irq_level = 14;
+ break;
+ case 0x8:
+ *irq_level = 12;
+ break;
+ case 0x4:
+ *irq_level = 11;
+ break;
+ case 0x2:
+ *irq_level = 10;
+ break;
+ case 0x1:
+ *irq_level = 9;
+ break;
+ default:
+ printk(KERN_ERR "Unable to determine Adaptec IRQ level. Disabling board\n");
+ return -1;
+ };
+ *scsi_id = inquiry_result[2] & 7;
+ return 0;
+}
+
+/* This function should only be called for 1542C boards - we can detect
+ the special firmware settings and unlock the board */
+
+static int __init aha1542_mbenable(int base)
+{
+ static unchar mbenable_cmd[3];
+ static unchar mbenable_result[2];
+ int retval;
+
+ retval = BIOS_TRANSLATION_6432;
+
+ mbenable_cmd[0] = CMD_EXTBIOS;
+ aha1542_out(base, mbenable_cmd, 1);
+ if (aha1542_in1(base, mbenable_result, 2))
+ return retval;
+ WAITd(INTRFLAGS(base), INTRMASK, HACC, 0, 100);
+ aha1542_intr_reset(base);
+
+ if ((mbenable_result[0] & 0x08) || mbenable_result[1]) {
+ mbenable_cmd[0] = CMD_MBENABLE;
+ mbenable_cmd[1] = 0;
+ mbenable_cmd[2] = mbenable_result[1];
+
+ if ((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03))
+ retval = BIOS_TRANSLATION_25563;
+
+ aha1542_out(base, mbenable_cmd, 3);
+ WAIT(INTRFLAGS(base), INTRMASK, HACC, 0);
+ };
+ while (0) {
+fail:
+ printk(KERN_ERR "aha1542_mbenable: Mailbox init failed\n");
+ }
+ aha1542_intr_reset(base);
+ return retval;
+}
+
+/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
+static int __init aha1542_query(int base_io, int *transl)
+{
+ unchar inquiry_cmd[] = {CMD_INQUIRY};
+ unchar inquiry_result[4];
+ int i;
+ i = inb(STATUS(base_io));
+ if (i & DF) {
+ i = inb(DATA(base_io));
+ };
+ aha1542_out(base_io, inquiry_cmd, 1);
+ aha1542_in(base_io, inquiry_result, 4);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ while (0) {
+fail:
+ printk(KERN_ERR "aha1542_detect: query card type\n");
+ }
+ aha1542_intr_reset(base_io);
+
+ *transl = BIOS_TRANSLATION_6432; /* Default case */
+
+ /* For an AHA1740 series board, we ignore the board since there is a
+ hardware bug which can lead to wrong blocks being returned if the board
+ is operating in the 1542 emulation mode. Since there is an extended mode
+ driver, we simply ignore the board and let the 1740 driver pick it up.
+ */
+
+ if (inquiry_result[0] == 0x43) {
+ printk(KERN_INFO "aha1542.c: Emulation mode not supported for AHA 174N hardware.\n");
+ return 1;
+ };
+
+ /* Always call this - boards that do not support extended bios translation
+ will ignore the command, and we will set the proper default */
+
+ *transl = aha1542_mbenable(base_io);
+
+ return 0;
+}
+
+#ifndef MODULE
+static char *setup_str[MAXBOARDS] __initdata;
+static int setup_idx = 0;
+
+static void __init aha1542_setup(char *str, int *ints)
+{
+ const char *ahausage = "aha1542: usage: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]\n";
+ int setup_portbase;
+
+ if (setup_idx >= MAXBOARDS) {
+ printk(KERN_ERR "aha1542: aha1542_setup called too many times! Bad LILO params ?\n");
+ printk(KERN_ERR " Entryline 1: %s\n", setup_str[0]);
+ printk(KERN_ERR " Entryline 2: %s\n", setup_str[1]);
+ printk(KERN_ERR " This line: %s\n", str);
+ return;
+ }
+ if (ints[0] < 1 || ints[0] > 4) {
+ printk(KERN_ERR "aha1542: %s\n", str);
+ printk(ahausage);
+ printk(KERN_ERR "aha1542: Wrong parameters may cause system malfunction.. We try anyway..\n");
+ }
+ setup_called[setup_idx] = ints[0];
+ setup_str[setup_idx] = str;
+
+ setup_portbase = ints[0] >= 1 ? ints[1] : 0; /* Preserve the default value.. */
+ setup_buson[setup_idx] = ints[0] >= 2 ? ints[2] : 7;
+ setup_busoff[setup_idx] = ints[0] >= 3 ? ints[3] : 5;
+ if (ints[0] >= 4)
+ {
+ int atbt = -1;
+ switch (ints[4]) {
+ case 5:
+ atbt = 0x00;
+ break;
+ case 6:
+ atbt = 0x04;
+ break;
+ case 7:
+ atbt = 0x01;
+ break;
+ case 8:
+ atbt = 0x02;
+ break;
+ case 10:
+ atbt = 0x03;
+ break;
+ default:
+ printk(KERN_ERR "aha1542: %s\n", str);
+ printk(ahausage);
+ printk(KERN_ERR "aha1542: Valid values for DMASPEED are 5-8, 10 MB/s. Using jumper defaults.\n");
+ break;
+ }
+ setup_dmaspeed[setup_idx] = atbt;
+ }
+ if (setup_portbase != 0)
+ bases[setup_idx] = setup_portbase;
+
+ ++setup_idx;
+}
+
+static int __init do_setup(char *str)
+{
+ int ints[5];
+
+ int count=setup_idx;
+
+ get_options(str, sizeof(ints)/sizeof(int), ints);
+ aha1542_setup(str,ints);
+
+ return count<setup_idx;
+}
+
+__setup("aha1542=",do_setup);
+#endif
+
+/* return non-zero on detection */
+static int __init aha1542_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned char dma_chan;
+ unsigned char irq_level;
+ unsigned char scsi_id;
+ unsigned long flags;
+ unsigned int base_io;
+ int trans;
+ struct Scsi_Host *shpnt = NULL;
+ int count = 0;
+ int indx;
+
+ DEB(printk("aha1542_detect: \n"));
+
+ tpnt->proc_name = "aha1542";
+
+#ifdef MODULE
+ bases[0] = aha1542[0];
+ setup_buson[0] = aha1542[1];
+ setup_busoff[0] = aha1542[2];
+ {
+ int atbt = -1;
+ switch (aha1542[3]) {
+ case 5:
+ atbt = 0x00;
+ break;
+ case 6:
+ atbt = 0x04;
+ break;
+ case 7:
+ atbt = 0x01;
+ break;
+ case 8:
+ atbt = 0x02;
+ break;
+ case 10:
+ atbt = 0x03;
+ break;
+ };
+ setup_dmaspeed[0] = atbt;
+ }
+#endif
+
+ /*
+ * Find MicroChannel cards (AHA1640)
+ */
+#ifdef CONFIG_MCA_LEGACY
+ if(MCA_bus) {
+ int slot = 0;
+ int pos = 0;
+
+ for (indx = 0; (slot != MCA_NOTFOUND) &&
+ (indx < sizeof(bases)/sizeof(bases[0])); indx++) {
+
+ if (bases[indx])
+ continue;
+
+ /* Detect only AHA-1640 cards -- MCA ID 0F1F */
+ slot = mca_find_unused_adapter(0x0f1f, slot);
+ if (slot == MCA_NOTFOUND)
+ break;
+
+
+ /* Found one */
+ pos = mca_read_stored_pos(slot, 3);
+
+ /* Decode address */
+ if (pos & 0x80) {
+ if (pos & 0x02) {
+ if (pos & 0x01)
+ bases[indx] = 0x334;
+ else
+ bases[indx] = 0x234;
+ } else {
+ if (pos & 0x01)
+ bases[indx] = 0x134;
+ }
+ } else {
+ if (pos & 0x02) {
+ if (pos & 0x01)
+ bases[indx] = 0x330;
+ else
+ bases[indx] = 0x230;
+ } else {
+ if (pos & 0x01)
+ bases[indx] = 0x130;
+ }
+ }
+
+ /* No need to decode IRQ and Arb level -- those are
+ * read off the card later.
+ */
+ printk(KERN_INFO "Found an AHA-1640 in MCA slot %d, I/O 0x%04x\n", slot, bases[indx]);
+
+ mca_set_adapter_name(slot, "Adapter AHA-1640");
+ mca_set_adapter_procfn(slot, NULL, NULL);
+ mca_mark_as_used(slot);
+
+ /* Go on */
+ slot++;
+ }
+
+ }
+#endif
+
+ /*
+ * Hunt for ISA Plug'n'Pray Adaptecs (AHA1535)
+ */
+
+ if(isapnp)
+ {
+ struct pnp_dev *pdev = NULL;
+ for(indx = 0; indx <sizeof(bases)/sizeof(bases[0]);indx++)
+ {
+ if(bases[indx])
+ continue;
+ pdev = pnp_find_dev(NULL, ISAPNP_VENDOR('A', 'D', 'P'),
+ ISAPNP_FUNCTION(0x1542), pdev);
+ if(pdev==NULL)
+ break;
+ /*
+ * Activate the PnP card
+ */
+
+ if(pnp_device_attach(pdev)<0)
+ continue;
+
+ if(pnp_activate_dev(pdev)<0) {
+ pnp_device_detach(pdev);
+ continue;
+ }
+
+ if(!pnp_port_valid(pdev, 0)) {
+ pnp_device_detach(pdev);
+ continue;
+ }
+
+ bases[indx] = pnp_port_start(pdev, 0);
+
+ /* The card can be queried for its DMA, we have
+ the DMA set up that is enough */
+
+ printk(KERN_INFO "ISAPnP found an AHA1535 at I/O 0x%03X\n", bases[indx]);
+ }
+ }
+ for (indx = 0; indx < sizeof(bases) / sizeof(bases[0]); indx++)
+ if (bases[indx] != 0 && request_region(bases[indx], 4, "aha1542")) {
+ shpnt = scsi_register(tpnt,
+ sizeof(struct aha1542_hostdata));
+
+ if(shpnt==NULL) {
+ release_region(bases[indx], 4);
+ continue;
+ }
+ /* For now we do this - until kmalloc is more intelligent
+ we are resigned to stupid hacks like this */
+ if (SCSI_BUF_PA(shpnt) >= ISA_DMA_THRESHOLD) {
+ printk(KERN_ERR "Invalid address for shpnt with 1542.\n");
+ goto unregister;
+ }
+ if (!aha1542_test_port(bases[indx], shpnt))
+ goto unregister;
+
+
+ base_io = bases[indx];
+
+ /* Set the Bus on/off-times as not to ruin floppy performance */
+ {
+ unchar oncmd[] = {CMD_BUSON_TIME, 7};
+ unchar offcmd[] = {CMD_BUSOFF_TIME, 5};
+
+ if (setup_called[indx]) {
+ oncmd[1] = setup_buson[indx];
+ offcmd[1] = setup_busoff[indx];
+ }
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, oncmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, offcmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ if (setup_dmaspeed[indx] >= 0) {
+ unchar dmacmd[] = {CMD_DMASPEED, 0};
+ dmacmd[1] = setup_dmaspeed[indx];
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, dmacmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ }
+ while (0) {
+fail:
+ printk(KERN_ERR "aha1542_detect: setting bus on/off-time failed\n");
+ }
+ aha1542_intr_reset(base_io);
+ }
+ if (aha1542_query(base_io, &trans))
+ goto unregister;
+
+ if (aha1542_getconfig(base_io, &irq_level, &dma_chan, &scsi_id) == -1)
+ goto unregister;
+
+ printk(KERN_INFO "Configuring Adaptec (SCSI-ID %d) at IO:%x, IRQ %d", scsi_id, base_io, irq_level);
+ if (dma_chan != 0xFF)
+ printk(", DMA priority %d", dma_chan);
+ printk("\n");
+
+ DEB(aha1542_stat());
+ setup_mailboxes(base_io, shpnt);
+
+ DEB(aha1542_stat());
+
+ DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
+ spin_lock_irqsave(&aha1542_lock, flags);
+ if (request_irq(irq_level, do_aha1542_intr_handle, 0, "aha1542", NULL)) {
+ printk(KERN_ERR "Unable to allocate IRQ for adaptec controller.\n");
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ goto unregister;
+ }
+ if (dma_chan != 0xFF) {
+ if (request_dma(dma_chan, "aha1542")) {
+ printk(KERN_ERR "Unable to allocate DMA channel for Adaptec.\n");
+ free_irq(irq_level, NULL);
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ goto unregister;
+ }
+ if (dma_chan == 0 || dma_chan >= 5) {
+ set_dma_mode(dma_chan, DMA_MODE_CASCADE);
+ enable_dma(dma_chan);
+ }
+ }
+ aha_host[irq_level - 9] = shpnt;
+ shpnt->this_id = scsi_id;
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 4; /* Number of bytes of I/O space used */
+ shpnt->dma_channel = dma_chan;
+ shpnt->irq = irq_level;
+ HOSTDATA(shpnt)->bios_translation = trans;
+ if (trans == BIOS_TRANSLATION_25563)
+ printk(KERN_INFO "aha1542.c: Using extended bios translation\n");
+ HOSTDATA(shpnt)->aha1542_last_mbi_used = (2 * AHA1542_MAILBOXES - 1);
+ HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1);
+ memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+#if 0
+ DEB(printk(" *** READ CAPACITY ***\n"));
+
+ {
+ unchar buf[8];
+ static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int i;
+
+ for (i = 0; i < sizeof(buf); ++i)
+ buf[i] = 0x87;
+ for (i = 0; i < 2; ++i)
+ if (!aha1542_command(i, cmd, buf, sizeof(buf))) {
+ printk(KERN_DEBUG "aha_detect: LU %d sector_size %d device_size %d\n",
+ i, xscsi2int(buf + 4), xscsi2int(buf));
+ }
+ }
+
+ DEB(printk(" *** NOW RUNNING MY OWN TEST *** \n"));
+
+ for (i = 0; i < 4; ++i) {
+ unsigned char cmd[10];
+ static buffer[512];
+
+ cmd[0] = READ_10;
+ cmd[1] = 0;
+ xany2scsi(cmd + 2, i);
+ cmd[6] = 0;
+ cmd[7] = 0;
+ cmd[8] = 1;
+ cmd[9] = 0;
+ aha1542_command(0, cmd, buffer, 512);
+ }
+#endif
+ count++;
+ continue;
+unregister:
+ release_region(bases[indx], 4);
+ scsi_unregister(shpnt);
+ continue;
+
+ };
+
+ return count;
+}
+
+static int aha1542_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static int aha1542_restart(struct Scsi_Host *shost)
+{
+ int i;
+ int count = 0;
+#if 0
+ unchar ahacmd = CMD_START_SCSI;
+#endif
+
+ for (i = 0; i < AHA1542_MAILBOXES; i++)
+ if (HOSTDATA(shost)->SCint[i] &&
+ !(HOSTDATA(shost)->SCint[i]->device->soft_reset)) {
+#if 0
+ HOSTDATA(shost)->mb[i].status = 1; /* Indicate ready to restart... */
+#endif
+ count++;
+ }
+ printk(KERN_DEBUG "Potential to restart %d stalled commands...\n", count);
+#if 0
+ /* start scsi command */
+ if (count)
+ aha1542_out(shost->io_port, &ahacmd, 1);
+#endif
+ return 0;
+}
+
+static int aha1542_abort(Scsi_Cmnd * SCpnt)
+{
+
+ /*
+ * The abort command does not leave the device in a clean state where
+ * it is available to be used again. Until this gets worked out, we
+ * will leave it commented out.
+ */
+
+ printk(KERN_ERR "aha1542.c: Unable to abort command for target %d\n",
+ SCpnt->device->id);
+ return FAILED;
+}
+
+/*
+ * This is a device reset. This is handled by sending a special command
+ * to the device.
+ */
+static int aha1542_dev_reset(Scsi_Cmnd * SCpnt)
+{
+ unsigned long flags;
+ struct mailbox *mb;
+ unchar target = SCpnt->device->id;
+ unchar lun = SCpnt->device->lun;
+ int mbo;
+ struct ccb *ccb;
+ unchar ahacmd = CMD_START_SCSI;
+
+ ccb = HOSTDATA(SCpnt->device->host)->ccb;
+ mb = HOSTDATA(SCpnt->device->host)->mb;
+
+ spin_lock_irqsave(&aha1542_lock, flags);
+ mbo = HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used + 1;
+ if (mbo >= AHA1542_MAILBOXES)
+ mbo = 0;
+
+ do {
+ if (mb[mbo].status == 0 && HOSTDATA(SCpnt->device->host)->SCint[mbo] == NULL)
+ break;
+ mbo++;
+ if (mbo >= AHA1542_MAILBOXES)
+ mbo = 0;
+ } while (mbo != HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used);
+
+ if (mb[mbo].status || HOSTDATA(SCpnt->device->host)->SCint[mbo])
+ panic("Unable to find empty mailbox for aha1542.\n");
+
+ HOSTDATA(SCpnt->device->host)->SCint[mbo] = SCpnt; /* This will effectively
+ prevent someone else from
+ screwing with this cdb. */
+
+ HOSTDATA(SCpnt->device->host)->aha1542_last_mbo_used = mbo;
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+
+ any2scsi(mb[mbo].ccbptr, SCSI_BUF_PA(&ccb[mbo])); /* This gets trashed for some reason */
+
+ memset(&ccb[mbo], 0, sizeof(struct ccb));
+
+ ccb[mbo].op = 0x81; /* BUS DEVICE RESET */
+
+ ccb[mbo].idlun = (target & 7) << 5 | (lun & 7); /*SCSI Target Id */
+
+ ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
+ ccb[mbo].commlinkid = 0;
+
+ /*
+ * Now tell the 1542 to flush all pending commands for this
+ * target
+ */
+ aha1542_out(SCpnt->device->host->io_port, &ahacmd, 1);
+
+ printk(KERN_WARNING "aha1542.c: Trying device reset for target %d\n", SCpnt->device->id);
+
+ return SUCCESS;
+
+
+#ifdef ERIC_neverdef
+ /*
+ * With the 1542 we apparently never get an interrupt to
+ * acknowledge a device reset being sent. Then again, Leonard
+ * says we are doing this wrong in the first place...
+ *
+ * Take a wait and see attitude. If we get spurious interrupts,
+ * then the device reset is doing something sane and useful, and
+ * we will wait for the interrupt to post completion.
+ */
+ printk(KERN_WARNING "Sent BUS DEVICE RESET to target %d\n", SCpnt->target);
+
+ /*
+ * Free the command block for all commands running on this
+ * target...
+ */
+ for (i = 0; i < AHA1542_MAILBOXES; i++) {
+ if (HOSTDATA(SCpnt->host)->SCint[i] &&
+ HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target) {
+ Scsi_Cmnd *SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ if (SCtmp->host_scribble) {
+ kfree(SCtmp->host_scribble);
+ SCtmp->host_scribble = NULL;
+ }
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ }
+ return SUCCESS;
+
+ return FAILED;
+#endif /* ERIC_neverdef */
+}
+
+static int aha1542_bus_reset(Scsi_Cmnd * SCpnt)
+{
+ int i;
+
+ /*
+ * This does a scsi reset for all devices on the bus.
+ * In principle, we could also reset the 1542 - should
+ * we do this? Try this first, and we can add that later
+ * if it turns out to be useful.
+ */
+ outb(SCRST, CONTROL(SCpnt->device->host->io_port));
+
+ /*
+ * Wait for the thing to settle down a bit. Unfortunately
+ * this is going to basically lock up the machine while we
+ * wait for this to complete. To be 100% correct, we need to
+ * check for timeout, and if we are doing something like this
+ * we are pretty desperate anyways.
+ */
+ spin_unlock_irq(SCpnt->device->host->host_lock);
+ ssleep(4);
+ spin_lock_irq(SCpnt->device->host->host_lock);
+
+ WAIT(STATUS(SCpnt->device->host->io_port),
+ STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+
+ /*
+ * Now try to pick up the pieces. For all pending commands,
+ * free any internal data structures, and basically clear things
+ * out. We do not try and restart any commands or anything -
+ * the strategy handler takes care of that crap.
+ */
+ printk(KERN_WARNING "Sent BUS RESET to scsi host %d\n", SCpnt->device->host->host_no);
+
+ for (i = 0; i < AHA1542_MAILBOXES; i++) {
+ if (HOSTDATA(SCpnt->device->host)->SCint[i] != NULL) {
+ Scsi_Cmnd *SCtmp;
+ SCtmp = HOSTDATA(SCpnt->device->host)->SCint[i];
+
+
+ if (SCtmp->device->soft_reset) {
+ /*
+ * If this device implements the soft reset option,
+ * then it is still holding onto the command, and
+ * may yet complete it. In this case, we don't
+ * flush the data.
+ */
+ continue;
+ }
+ if (SCtmp->host_scribble) {
+ kfree(SCtmp->host_scribble);
+ SCtmp->host_scribble = NULL;
+ }
+ HOSTDATA(SCpnt->device->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->device->host)->mb[i].status = 0;
+ }
+ }
+
+ return SUCCESS;
+
+fail:
+ return FAILED;
+}
+
+static int aha1542_host_reset(Scsi_Cmnd * SCpnt)
+{
+ int i;
+
+ /*
+ * This does a scsi reset for all devices on the bus.
+ * In principle, we could also reset the 1542 - should
+ * we do this? Try this first, and we can add that later
+ * if it turns out to be useful.
+ */
+ outb(HRST | SCRST, CONTROL(SCpnt->device->host->io_port));
+
+ /*
+ * Wait for the thing to settle down a bit. Unfortunately
+ * this is going to basically lock up the machine while we
+ * wait for this to complete. To be 100% correct, we need to
+ * check for timeout, and if we are doing something like this
+ * we are pretty desperate anyways.
+ */
+ spin_unlock_irq(SCpnt->device->host->host_lock);
+ ssleep(4);
+ spin_lock_irq(SCpnt->device->host->host_lock);
+
+ WAIT(STATUS(SCpnt->device->host->io_port),
+ STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+
+ /*
+ * We need to do this too before the 1542 can interact with
+ * us again.
+ */
+ setup_mailboxes(SCpnt->device->host->io_port, SCpnt->device->host);
+
+ /*
+ * Now try to pick up the pieces. For all pending commands,
+ * free any internal data structures, and basically clear things
+ * out. We do not try and restart any commands or anything -
+ * the strategy handler takes care of that crap.
+ */
+ printk(KERN_WARNING "Sent BUS RESET to scsi host %d\n", SCpnt->device->host->host_no);
+
+ for (i = 0; i < AHA1542_MAILBOXES; i++) {
+ if (HOSTDATA(SCpnt->device->host)->SCint[i] != NULL) {
+ Scsi_Cmnd *SCtmp;
+ SCtmp = HOSTDATA(SCpnt->device->host)->SCint[i];
+
+ if (SCtmp->device->soft_reset) {
+ /*
+ * If this device implements the soft reset option,
+ * then it is still holding onto the command, and
+ * may yet complete it. In this case, we don't
+ * flush the data.
+ */
+ continue;
+ }
+ if (SCtmp->host_scribble) {
+ kfree(SCtmp->host_scribble);
+ SCtmp->host_scribble = NULL;
+ }
+ HOSTDATA(SCpnt->device->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->device->host)->mb[i].status = 0;
+ }
+ }
+
+ return SUCCESS;
+
+fail:
+ return FAILED;
+}
+
+#if 0
+/*
+ * These are the old error handling routines. They are only temporarily
+ * here while we play with the new error handling code.
+ */
+static int aha1542_old_abort(Scsi_Cmnd * SCpnt)
+{
+#if 0
+ unchar ahacmd = CMD_START_SCSI;
+ unsigned long flags;
+ struct mailbox *mb;
+ int mbi, mbo, i;
+
+ printk(KERN_DEBUG "In aha1542_abort: %x %x\n",
+ inb(STATUS(SCpnt->host->io_port)),
+ inb(INTRFLAGS(SCpnt->host->io_port)));
+
+ spin_lock_irqsave(&aha1542_lock, flags);
+ mb = HOSTDATA(SCpnt->host)->mb;
+ mbi = HOSTDATA(SCpnt->host)->aha1542_last_mbi_used + 1;
+ if (mbi >= 2 * AHA1542_MAILBOXES)
+ mbi = AHA1542_MAILBOXES;
+
+ do {
+ if (mb[mbi].status != 0)
+ break;
+ mbi++;
+ if (mbi >= 2 * AHA1542_MAILBOXES)
+ mbi = AHA1542_MAILBOXES;
+ } while (mbi != HOSTDATA(SCpnt->host)->aha1542_last_mbi_used);
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+
+ if (mb[mbi].status) {
+ printk(KERN_ERR "Lost interrupt discovered on irq %d - attempting to recover\n",
+ SCpnt->host->irq);
+ aha1542_intr_handle(SCpnt->host, NULL);
+ return 0;
+ }
+ /* OK, no lost interrupt. Try looking to see how many pending commands
+ we think we have. */
+
+ for (i = 0; i < AHA1542_MAILBOXES; i++)
+ if (HOSTDATA(SCpnt->host)->SCint[i]) {
+ if (HOSTDATA(SCpnt->host)->SCint[i] == SCpnt) {
+ printk(KERN_ERR "Timed out command pending for %s\n",
+ SCpnt->request->rq_disk ?
+ SCpnt->request->rq_disk->disk_name : "?"
+ );
+ if (HOSTDATA(SCpnt->host)->mb[i].status) {
+ printk(KERN_ERR "OGMB still full - restarting\n");
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
+ };
+ } else
+ printk(KERN_ERR "Other pending command %s\n",
+ SCpnt->request->rq_disk ?
+ SCpnt->request->rq_disk->disk_name : "?"
+ );
+ }
+#endif
+
+ DEB(printk("aha1542_abort\n"));
+#if 0
+ spin_lock_irqsave(&aha1542_lock, flags);
+ for (mbo = 0; mbo < AHA1542_MAILBOXES; mbo++) {
+ if (SCpnt == HOSTDATA(SCpnt->host)->SCint[mbo]) {
+ mb[mbo].status = 2; /* Abort command */
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+ break;
+ }
+ }
+ if (AHA1542_MAILBOXES == mbo)
+ spin_unlock_irqrestore(&aha1542_lock, flags);
+#endif
+ return SCSI_ABORT_SNOOZE;
+}
+
+/* We do not implement a reset function here, but the upper level code
+ assumes that it will get some kind of response for the command in
+ SCpnt. We must oblige, or the command will hang the scsi system.
+ For a first go, we assume that the 1542 notifies us with all of the
+ pending commands (it does implement soft reset, after all). */
+
+static int aha1542_old_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ unchar ahacmd = CMD_START_SCSI;
+ int i;
+
+ /*
+ * See if a bus reset was suggested.
+ */
+ if (reset_flags & SCSI_RESET_SUGGEST_BUS_RESET) {
+ /*
+ * This does a scsi reset for all devices on the bus.
+ * In principle, we could also reset the 1542 - should
+ * we do this? Try this first, and we can add that later
+ * if it turns out to be useful.
+ */
+ outb(HRST | SCRST, CONTROL(SCpnt->host->io_port));
+
+ /*
+ * Wait for the thing to settle down a bit. Unfortunately
+ * this is going to basically lock up the machine while we
+ * wait for this to complete. To be 100% correct, we need to
+ * check for timeout, and if we are doing something like this
+ * we are pretty desperate anyways.
+ */
+ WAIT(STATUS(SCpnt->host->io_port),
+ STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF);
+
+ /*
+ * We need to do this too before the 1542 can interact with
+ * us again.
+ */
+ setup_mailboxes(SCpnt->host->io_port, SCpnt->host);
+
+ /*
+ * Now try to pick up the pieces. Restart all commands
+ * that are currently active on the bus, and reset all of
+ * the datastructures. We have some time to kill while
+ * things settle down, so print a nice message.
+ */
+ printk(KERN_WARNING "Sent BUS RESET to scsi host %d\n", SCpnt->host->host_no);
+
+ for (i = 0; i < AHA1542_MAILBOXES; i++)
+ if (HOSTDATA(SCpnt->host)->SCint[i] != NULL) {
+ Scsi_Cmnd *SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ SCtmp->result = DID_RESET << 16;
+ if (SCtmp->host_scribble) {
+ kfree(SCtmp->host_scribble);
+ SCtmp->host_scribble = NULL;
+ }
+ printk(KERN_WARNING "Sending DID_RESET for target %d\n", SCpnt->target);
+ SCtmp->scsi_done(SCpnt);
+
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ /*
+ * Now tell the mid-level code what we did here. Since
+ * we have restarted all of the outstanding commands,
+ * then report SUCCESS.
+ */
+ return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET);
+fail:
+ printk(KERN_CRIT "aha1542.c: Unable to perform hard reset.\n");
+ printk(KERN_CRIT "Power cycle machine to reset\n");
+ return (SCSI_RESET_ERROR | SCSI_RESET_BUS_RESET);
+
+
+ } else {
+ /* This does a selective reset of just the one device */
+ /* First locate the ccb for this command */
+ for (i = 0; i < AHA1542_MAILBOXES; i++)
+ if (HOSTDATA(SCpnt->host)->SCint[i] == SCpnt) {
+ HOSTDATA(SCpnt->host)->ccb[i].op = 0x81; /* BUS DEVICE RESET */
+ /* Now tell the 1542 to flush all pending commands for this target */
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
+
+ /* Here is the tricky part. What to do next. Do we get an interrupt
+ for the commands that we aborted with the specified target, or
+ do we generate this on our own? Try it without first and see
+ what happens */
+ printk(KERN_WARNING "Sent BUS DEVICE RESET to target %d\n", SCpnt->target);
+
+ /* If the first does not work, then try the second. I think the
+ first option is more likely to be correct. Free the command
+ block for all commands running on this target... */
+ for (i = 0; i < AHA1542_MAILBOXES; i++)
+ if (HOSTDATA(SCpnt->host)->SCint[i] &&
+ HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target) {
+ Scsi_Cmnd *SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ SCtmp->result = DID_RESET << 16;
+ if (SCtmp->host_scribble) {
+ kfree(SCtmp->host_scribble);
+ SCtmp->host_scribble = NULL;
+ }
+ printk(KERN_WARNING "Sending DID_RESET for target %d\n", SCpnt->target);
+ SCtmp->scsi_done(SCpnt);
+
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ return SCSI_RESET_SUCCESS;
+ }
+ }
+ /* No active command at this time, so this means that each time we got
+ some kind of response the last time through. Tell the mid-level code
+ to request sense information in order to decide what to do next. */
+ return SCSI_RESET_PUNT;
+}
+#endif /* end of big comment block around old_abort + old_reset */
+
+static int aha1542_biosparam(struct scsi_device *sdev,
+ struct block_device *bdev, sector_t capacity, int *ip)
+{
+ int translation_algorithm;
+ int size = capacity;
+
+ translation_algorithm = HOSTDATA(sdev->host)->bios_translation;
+
+ if ((size >> 11) > 1024 && translation_algorithm == BIOS_TRANSLATION_25563) {
+ /* Please verify that this is the same as what DOS returns */
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / 255 / 63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ }
+
+ return 0;
+}
+MODULE_LICENSE("GPL");
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "aha1542",
+ .name = "Adaptec 1542",
+ .detect = aha1542_detect,
+ .release = aha1542_release,
+ .queuecommand = aha1542_queuecommand,
+ .eh_abort_handler = aha1542_abort,
+ .eh_device_reset_handler= aha1542_dev_reset,
+ .eh_bus_reset_handler = aha1542_bus_reset,
+ .eh_host_reset_handler = aha1542_host_reset,
+ .bios_param = aha1542_biosparam,
+ .can_queue = AHA1542_MAILBOXES,
+ .this_id = 7,
+ .sg_tablesize = AHA1542_SCATTER,
+ .cmd_per_lun = AHA1542_CMDLUN,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/aha1542.h b/drivers/scsi/aha1542.h
new file mode 100644
index 000000000000..c402351dc79a
--- /dev/null
+++ b/drivers/scsi/aha1542.h
@@ -0,0 +1,151 @@
+#ifndef _AHA1542_H
+
+/* $Id: aha1542.h,v 1.1 1992/07/24 06:27:38 root Exp root $
+ *
+ * Header file for the adaptec 1542 driver for Linux
+ *
+ * $Log: aha1542.h,v $
+ * Revision 1.1 1992/07/24 06:27:38 root
+ * Initial revision
+ *
+ * Revision 1.2 1992/07/04 18:41:49 root
+ * Replaced distribution with current drivers
+ *
+ * Revision 1.3 1992/06/23 23:58:20 root
+ * Fixes.
+ *
+ * Revision 1.2 1992/05/26 22:13:23 root
+ * Changed bug that prevented DMA above first 2 mbytes.
+ *
+ * Revision 1.1 1992/05/22 21:00:29 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/24 18:01:50 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/02 03:23:13 drew
+ * Initial revision
+ *
+ * Revision 1.3 1992/01/27 14:46:29 tthorn
+ * *** empty log message ***
+ *
+ */
+
+#include <linux/types.h>
+
+/* I/O Port interface 4.2 */
+/* READ */
+#define STATUS(base) base
+#define STST 0x80 /* Self Test in Progress */
+#define DIAGF 0x40 /* Internal Diagnostic Failure */
+#define INIT 0x20 /* Mailbox Initialization Required */
+#define IDLE 0x10 /* SCSI Host Adapter Idle */
+#define CDF 0x08 /* Command/Data Out Port Full */
+#define DF 0x04 /* Data In Port Full */
+#define INVDCMD 0x01 /* Invalid H A Command */
+#define STATMASK 0xfd /* 0x02 is reserved */
+
+#define INTRFLAGS(base) (STATUS(base)+2)
+#define ANYINTR 0x80 /* Any Interrupt */
+#define SCRD 0x08 /* SCSI Reset Detected */
+#define HACC 0x04 /* HA Command Complete */
+#define MBOA 0x02 /* MBO Empty */
+#define MBIF 0x01 /* MBI Full */
+#define INTRMASK 0x8f
+
+/* WRITE */
+#define CONTROL(base) STATUS(base)
+#define HRST 0x80 /* Hard Reset */
+#define SRST 0x40 /* Soft Reset */
+#define IRST 0x20 /* Interrupt Reset */
+#define SCRST 0x10 /* SCSI Bus Reset */
+
+/* READ/WRITE */
+#define DATA(base) (STATUS(base)+1)
+#define CMD_NOP 0x00 /* No Operation */
+#define CMD_MBINIT 0x01 /* Mailbox Initialization */
+#define CMD_START_SCSI 0x02 /* Start SCSI Command */
+#define CMD_INQUIRY 0x04 /* Adapter Inquiry */
+#define CMD_EMBOI 0x05 /* Enable MailBox Out Interrupt */
+#define CMD_BUSON_TIME 0x07 /* Set Bus-On Time */
+#define CMD_BUSOFF_TIME 0x08 /* Set Bus-Off Time */
+#define CMD_DMASPEED 0x09 /* Set AT Bus Transfer Speed */
+#define CMD_RETDEVS 0x0a /* Return Installed Devices */
+#define CMD_RETCONF 0x0b /* Return Configuration Data */
+#define CMD_RETSETUP 0x0d /* Return Setup Data */
+#define CMD_ECHO 0x1f /* ECHO Command Data */
+
+#define CMD_EXTBIOS 0x28 /* Return extend bios information only 1542C */
+#define CMD_MBENABLE 0x29 /* Set Mailbox Interface enable only 1542C */
+
+/* Mailbox Definition 5.2.1 and 5.2.2 */
+struct mailbox {
+ unchar status; /* Command/Status */
+ unchar ccbptr[3]; /* msb, .., lsb */
+};
+
+/* This is used with scatter-gather */
+struct chain {
+ unchar datalen[3]; /* Size of this part of chain */
+ unchar dataptr[3]; /* Location of data */
+};
+
+/* These belong in scsi.h also */
+static inline void any2scsi(u8 *p, u32 v)
+{
+ p[0] = v >> 16;
+ p[1] = v >> 8;
+ p[2] = v;
+}
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+
+struct ccb { /* Command Control Block 5.3 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked*/
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdblen; /* SCSI Command Length */
+ unchar rsalen; /* Request Sense Allocation Length/Disable */
+ unchar datalen[3]; /* Data Length (msb, .., lsb) */
+ unchar dataptr[3]; /* Data Pointer */
+ unchar linkptr[3]; /* Link Pointer */
+ unchar commlinkid; /* Command Linking Identifier */
+ unchar hastat; /* Host Adapter Status (HASTAT) */
+ unchar tarstat; /* Target Device Status */
+ unchar reserved[2];
+ unchar cdb[MAX_CDB+MAX_SENSE];/* SCSI Command Descriptor Block */
+ /* REQUEST SENSE */
+};
+
+static int aha1542_detect(Scsi_Host_Template *);
+static int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int aha1542_abort(Scsi_Cmnd * SCpnt);
+static int aha1542_bus_reset(Scsi_Cmnd * SCpnt);
+static int aha1542_dev_reset(Scsi_Cmnd * SCpnt);
+static int aha1542_host_reset(Scsi_Cmnd * SCpnt);
+#if 0
+static int aha1542_old_abort(Scsi_Cmnd * SCpnt);
+static int aha1542_old_reset(Scsi_Cmnd *, unsigned int);
+#endif
+static int aha1542_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int *);
+
+#define AHA1542_MAILBOXES 8
+#define AHA1542_SCATTER 16
+#define AHA1542_CMDLUN 1
+
+#endif
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
new file mode 100644
index 000000000000..73f33e716a0c
--- /dev/null
+++ b/drivers/scsi/aha1740.c
@@ -0,0 +1,707 @@
+/* $Id$
+ * 1993/03/31
+ * linux/kernel/aha1740.c
+ *
+ * Based loosely on aha1542.c which is
+ * Copyright (C) 1992 Tommy Thorn and
+ * Modified by Eric Youngdale
+ *
+ * This file is aha1740.c, written and
+ * Copyright (C) 1992,1993 Brad McLean
+ * brad@saturn.gaylord.com or brad@bradpc.gaylord.com.
+ *
+ * Modifications to makecode and queuecommand
+ * for proper handling of multiple devices courteously
+ * provided by Michael Weller, March, 1993
+ *
+ * Multiple adapter support, extended translation detection,
+ * update to current scsi subsystem changes, proc fs support,
+ * working (!) module support based on patches from Andreas Arens,
+ * by Andreas Degert <ad@papyrus.hamburg.com>, 2/1997
+ *
+ * aha1740_makecode may still need even more work
+ * if it doesn't work for your devices, take a look.
+ *
+ * Reworked for new_eh and new locking by Alan Cox <alan@redhat.com>
+ *
+ * Converted to EISA and generic DMA APIs by Marc Zyngier
+ * <maz@wild-wind.fr.eu.org>, 4/2003.
+ *
+ * Shared interrupt support added by Rask Ingemann Lambertsen
+ * <rask@sygehus.dk>, 10/2003
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/eisa.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aha1740.h"
+
+/* IF YOU ARE HAVING PROBLEMS WITH THIS DRIVER, AND WANT TO WATCH
+ IT WORK, THEN:
+#define DEBUG
+*/
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+struct aha1740_hostdata {
+ struct eisa_device *edev;
+ unsigned int translation;
+ unsigned int last_ecb_used;
+ dma_addr_t ecb_dma_addr;
+ struct ecb ecb[AHA1740_ECBS];
+};
+
+struct aha1740_sg {
+ struct aha1740_chain sg_chain[AHA1740_SCATTER];
+ dma_addr_t sg_dma_addr;
+ dma_addr_t buf_dma_addr;
+};
+
+#define HOSTDATA(host) ((struct aha1740_hostdata *) &host->hostdata)
+
+static inline struct ecb *ecb_dma_to_cpu (struct Scsi_Host *host,
+ dma_addr_t dma)
+{
+ struct aha1740_hostdata *hdata = HOSTDATA (host);
+ dma_addr_t offset;
+
+ offset = dma - hdata->ecb_dma_addr;
+
+ return (struct ecb *)(((char *) hdata->ecb) + (unsigned int) offset);
+}
+
+static inline dma_addr_t ecb_cpu_to_dma (struct Scsi_Host *host, void *cpu)
+{
+ struct aha1740_hostdata *hdata = HOSTDATA (host);
+ dma_addr_t offset;
+
+ offset = (char *) cpu - (char *) hdata->ecb;
+
+ return hdata->ecb_dma_addr + offset;
+}
+
+static int aha1740_proc_info(struct Scsi_Host *shpnt, char *buffer,
+ char **start, off_t offset,
+ int length, int inout)
+{
+ int len;
+ struct aha1740_hostdata *host;
+
+ if (inout)
+ return-ENOSYS;
+
+ host = HOSTDATA(shpnt);
+
+ len = sprintf(buffer, "aha174x at IO:%lx, IRQ %d, SLOT %d.\n"
+ "Extended translation %sabled.\n",
+ shpnt->io_port, shpnt->irq, host->edev->slot,
+ host->translation ? "en" : "dis");
+
+ if (offset > len) {
+ *start = buffer;
+ return 0;
+ }
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+static int aha1740_makecode(unchar *sense, unchar *status)
+{
+ struct statusword
+ {
+ ushort don:1, /* Command Done - No Error */
+ du:1, /* Data underrun */
+ :1, qf:1, /* Queue full */
+ sc:1, /* Specification Check */
+ dor:1, /* Data overrun */
+ ch:1, /* Chaining Halted */
+ intr:1, /* Interrupt issued */
+ asa:1, /* Additional Status Available */
+ sns:1, /* Sense information Stored */
+ :1, ini:1, /* Initialization Required */
+ me:1, /* Major error or exception */
+ :1, eca:1, /* Extended Contingent alliance */
+ :1;
+ } status_word;
+ int retval = DID_OK;
+
+ status_word = * (struct statusword *) status;
+#ifdef DEBUG
+ printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",
+ status[0], status[1], status[2], status[3],
+ sense[0], sense[1], sense[2], sense[3]);
+#endif
+ if (!status_word.don) { /* Anything abnormal was detected */
+ if ( (status[1]&0x18) || status_word.sc ) {
+ /*Additional info available*/
+ /* Use the supplied info for further diagnostics */
+ switch ( status[2] ) {
+ case 0x12:
+ if ( status_word.dor )
+ retval=DID_ERROR; /* It's an Overrun */
+ /* If not overrun, assume underrun and
+ * ignore it! */
+ case 0x00: /* No info, assume no error, should
+ * not occur */
+ break;
+ case 0x11:
+ case 0x21:
+ retval=DID_TIME_OUT;
+ break;
+ case 0x0a:
+ retval=DID_BAD_TARGET;
+ break;
+ case 0x04:
+ case 0x05:
+ retval=DID_ABORT;
+ /* Either by this driver or the
+ * AHA1740 itself */
+ break;
+ default:
+ retval=DID_ERROR; /* No further
+ * diagnostics
+ * possible */
+ }
+ } else {
+ /* Michael suggests, and Brad concurs: */
+ if ( status_word.qf ) {
+ retval = DID_TIME_OUT; /* forces a redo */
+ /* I think this specific one should
+ * not happen -Brad */
+ printk("aha1740.c: WARNING: AHA1740 queue overflow!\n");
+ } else
+ if ( status[0]&0x60 ) {
+ /* Didn't find a better error */
+ retval = DID_ERROR;
+ }
+ /* In any other case return DID_OK so for example
+ CONDITION_CHECKS make it through to the appropriate
+ device driver */
+ }
+ }
+ /* Under all circumstances supply the target status -Michael */
+ return status[3] | retval << 16;
+}
+
+static int aha1740_test_port(unsigned int base)
+{
+ if ( inb(PORTADR(base)) & PORTADDR_ENH )
+ return 1; /* Okay, we're all set */
+
+ printk("aha174x: Board detected, but not in enhanced mode, so disabled it.\n");
+ return 0;
+}
+
+/* A "high" level interrupt handler */
+static irqreturn_t aha1740_intr_handle(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct Scsi_Host *host = (struct Scsi_Host *) dev_id;
+ void (*my_done)(Scsi_Cmnd *);
+ int errstatus, adapstat;
+ int number_serviced;
+ struct ecb *ecbptr;
+ Scsi_Cmnd *SCtmp;
+ unsigned int base;
+ unsigned long flags;
+ int handled = 0;
+ struct aha1740_sg *sgptr;
+ struct eisa_device *edev;
+
+ if (!host)
+ panic("aha1740.c: Irq from unknown host!\n");
+ spin_lock_irqsave(host->host_lock, flags);
+ base = host->io_port;
+ number_serviced = 0;
+ edev = HOSTDATA(host)->edev;
+
+ while(inb(G2STAT(base)) & G2STAT_INTPEND) {
+ handled = 1;
+ DEB(printk("aha1740_intr top of loop.\n"));
+ adapstat = inb(G2INTST(base));
+ ecbptr = ecb_dma_to_cpu (host, inl(MBOXIN0(base)));
+ outb(G2CNTRL_IRST,G2CNTRL(base)); /* interrupt reset */
+
+ switch ( adapstat & G2INTST_MASK ) {
+ case G2INTST_CCBRETRY:
+ case G2INTST_CCBERROR:
+ case G2INTST_CCBGOOD:
+ /* Host Ready -> Mailbox in complete */
+ outb(G2CNTRL_HRDY,G2CNTRL(base));
+ if (!ecbptr) {
+ printk("Aha1740 null ecbptr in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT(base)),adapstat,
+ inb(G2INTST(base)), number_serviced++);
+ continue;
+ }
+ SCtmp = ecbptr->SCpnt;
+ if (!SCtmp) {
+ printk("Aha1740 null SCtmp in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT(base)),adapstat,
+ inb(G2INTST(base)), number_serviced++);
+ continue;
+ }
+ sgptr = (struct aha1740_sg *) SCtmp->host_scribble;
+ if (SCtmp->use_sg) {
+ /* We used scatter-gather.
+ Do the unmapping dance. */
+ dma_unmap_sg (&edev->dev,
+ (struct scatterlist *) SCtmp->request_buffer,
+ SCtmp->use_sg,
+ SCtmp->sc_data_direction);
+ } else {
+ dma_unmap_single (&edev->dev,
+ sgptr->buf_dma_addr,
+ SCtmp->request_bufflen,
+ DMA_BIDIRECTIONAL);
+ }
+
+ /* Free the sg block */
+ dma_free_coherent (&edev->dev,
+ sizeof (struct aha1740_sg),
+ SCtmp->host_scribble,
+ sgptr->sg_dma_addr);
+
+ /* Fetch the sense data, and tuck it away, in
+ the required slot. The Adaptec
+ automatically fetches it, and there is no
+ guarantee that we will still have it in the
+ cdb when we come back */
+ if ( (adapstat & G2INTST_MASK) == G2INTST_CCBERROR ) {
+ memcpy(SCtmp->sense_buffer, ecbptr->sense,
+ sizeof(SCtmp->sense_buffer));
+ errstatus = aha1740_makecode(ecbptr->sense,ecbptr->status);
+ } else
+ errstatus = 0;
+ DEB(if (errstatus)
+ printk("aha1740_intr_handle: returning %6x\n",
+ errstatus));
+ SCtmp->result = errstatus;
+ my_done = ecbptr->done;
+ memset(ecbptr,0,sizeof(struct ecb));
+ if ( my_done )
+ my_done(SCtmp);
+ break;
+
+ case G2INTST_HARDFAIL:
+ printk(KERN_ALERT "aha1740 hardware failure!\n");
+ panic("aha1740.c"); /* Goodbye */
+
+ case G2INTST_ASNEVENT:
+ printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",
+ adapstat,
+ inb(MBOXIN0(base)),
+ inb(MBOXIN1(base)),
+ inb(MBOXIN2(base)),
+ inb(MBOXIN3(base))); /* Say What? */
+ /* Host Ready -> Mailbox in complete */
+ outb(G2CNTRL_HRDY,G2CNTRL(base));
+ break;
+
+ case G2INTST_CMDGOOD:
+ /* set immediate command success flag here: */
+ break;
+
+ case G2INTST_CMDERROR:
+ /* Set immediate command failure flag here: */
+ break;
+ }
+ number_serviced++;
+ }
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return IRQ_RETVAL(handled);
+}
+
+static int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->device->id;
+ struct aha1740_hostdata *host = HOSTDATA(SCpnt->device->host);
+ unsigned long flags;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ dma_addr_t sg_dma;
+ struct aha1740_sg *sgptr;
+ int ecbno;
+ DEB(int i);
+
+ if(*cmd == REQUEST_SENSE) {
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ }
+
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd+2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd+2);
+ else
+ i = -1;
+ printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ",
+ target, *cmd, i, bufflen);
+ printk("scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
+ printk("\n");
+#endif
+
+ /* locate an available ecb */
+ spin_lock_irqsave(SCpnt->device->host->host_lock, flags);
+ ecbno = host->last_ecb_used + 1; /* An optimization */
+ if (ecbno >= AHA1740_ECBS)
+ ecbno = 0;
+ do {
+ if (!host->ecb[ecbno].cmdw)
+ break;
+ ecbno++;
+ if (ecbno >= AHA1740_ECBS)
+ ecbno = 0;
+ } while (ecbno != host->last_ecb_used);
+
+ if (host->ecb[ecbno].cmdw)
+ panic("Unable to find empty ecb for aha1740.\n");
+
+ host->ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command
+ doubles as reserved flag */
+
+ host->last_ecb_used = ecbno;
+ spin_unlock_irqrestore(SCpnt->device->host->host_lock, flags);
+
+#ifdef DEBUG
+ printk("Sending command (%d %x)...", ecbno, done);
+#endif
+
+ host->ecb[ecbno].cdblen = SCpnt->cmd_len; /* SCSI Command
+ * Descriptor Block
+ * Length */
+
+ direction = 0;
+ if (*cmd == READ_10 || *cmd == READ_6)
+ direction = 1;
+ else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 0;
+
+ memcpy(host->ecb[ecbno].cdb, cmd, SCpnt->cmd_len);
+
+ SCpnt->host_scribble = dma_alloc_coherent (&host->edev->dev,
+ sizeof (struct aha1740_sg),
+ &sg_dma, GFP_ATOMIC);
+ if(SCpnt->host_scribble == NULL) {
+ printk(KERN_WARNING "aha1740: out of memory in queuecommand!\n");
+ return 1;
+ }
+ sgptr = (struct aha1740_sg *) SCpnt->host_scribble;
+ sgptr->sg_dma_addr = sg_dma;
+
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ struct aha1740_chain * cptr;
+ int i, count;
+ DEB(unsigned char * ptr);
+
+ host->ecb[ecbno].sg = 1; /* SCSI Initiator Command
+ * w/scatter-gather*/
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ cptr = sgptr->sg_chain;
+ count = dma_map_sg (&host->edev->dev, sgpnt, SCpnt->use_sg,
+ SCpnt->sc_data_direction);
+ for(i=0; i < count; i++) {
+ cptr[i].datalen = sg_dma_len (sgpnt + i);
+ cptr[i].dataptr = sg_dma_address (sgpnt + i);
+ }
+ host->ecb[ecbno].datalen = count*sizeof(struct aha1740_chain);
+ host->ecb[ecbno].dataptr = sg_dma;
+#ifdef DEBUG
+ printk("cptr %x: ",cptr);
+ ptr = (unsigned char *) cptr;
+ for(i=0;i<24;i++) printk("%02x ", ptr[i]);
+#endif
+ } else {
+ host->ecb[ecbno].datalen = bufflen;
+ sgptr->buf_dma_addr = dma_map_single (&host->edev->dev,
+ buff, bufflen,
+ DMA_BIDIRECTIONAL);
+ host->ecb[ecbno].dataptr = sgptr->buf_dma_addr;
+ }
+ host->ecb[ecbno].lun = SCpnt->device->lun;
+ host->ecb[ecbno].ses = 1; /* Suppress underrun errors */
+ host->ecb[ecbno].dir = direction;
+ host->ecb[ecbno].ars = 1; /* Yes, get the sense on an error */
+ host->ecb[ecbno].senselen = 12;
+ host->ecb[ecbno].senseptr = ecb_cpu_to_dma (SCpnt->device->host,
+ host->ecb[ecbno].sense);
+ host->ecb[ecbno].statusptr = ecb_cpu_to_dma (SCpnt->device->host,
+ host->ecb[ecbno].status);
+ host->ecb[ecbno].done = done;
+ host->ecb[ecbno].SCpnt = SCpnt;
+#ifdef DEBUG
+ {
+ int i;
+ printk("aha1740_command: sending.. ");
+ for (i = 0; i < sizeof(host->ecb[ecbno]) - 10; i++)
+ printk("%02x ", ((unchar *)&host->ecb[ecbno])[i]);
+ }
+ printk("\n");
+#endif
+ if (done) {
+ /* The Adaptec Spec says the card is so fast that the loops
+ will only be executed once in the code below. Even if this
+ was true with the fastest processors when the spec was
+ written, it doesn't seem to be true with todays fast
+ processors. We print a warning if the code is executed more
+ often than LOOPCNT_WARN. If this happens, it should be
+ investigated. If the count reaches LOOPCNT_MAX, we assume
+ something is broken; since there is no way to return an
+ error (the return value is ignored by the mid-level scsi
+ layer) we have to panic (and maybe that's the best thing we
+ can do then anyhow). */
+
+#define LOOPCNT_WARN 10 /* excessive mbxout wait -> syslog-msg */
+#define LOOPCNT_MAX 1000000 /* mbxout deadlock -> panic() after ~ 2 sec. */
+ int loopcnt;
+ unsigned int base = SCpnt->device->host->io_port;
+ DEB(printk("aha1740[%d] critical section\n",ecbno));
+
+ spin_lock_irqsave(SCpnt->device->host->host_lock, flags);
+ for (loopcnt = 0; ; loopcnt++) {
+ if (inb(G2STAT(base)) & G2STAT_MBXOUT) break;
+ if (loopcnt == LOOPCNT_WARN) {
+ printk("aha1740[%d]_mbxout wait!\n",ecbno);
+ }
+ if (loopcnt == LOOPCNT_MAX)
+ panic("aha1740.c: mbxout busy!\n");
+ }
+ outl (ecb_cpu_to_dma (SCpnt->device->host, host->ecb + ecbno),
+ MBOXOUT0(base));
+ for (loopcnt = 0; ; loopcnt++) {
+ if (! (inb(G2STAT(base)) & G2STAT_BUSY)) break;
+ if (loopcnt == LOOPCNT_WARN) {
+ printk("aha1740[%d]_attn wait!\n",ecbno);
+ }
+ if (loopcnt == LOOPCNT_MAX)
+ panic("aha1740.c: attn wait failed!\n");
+ }
+ outb(ATTN_START | (target & 7), ATTN(base)); /* Start it up */
+ spin_unlock_irqrestore(SCpnt->device->host->host_lock, flags);
+ DEB(printk("aha1740[%d] request queued.\n",ecbno));
+ } else
+ printk(KERN_ALERT "aha1740_queuecommand: done can't be NULL\n");
+ return 0;
+}
+
+/* Query the board for its irq_level and irq_type. Nothing else matters
+ in enhanced mode on an EISA bus. */
+
+static void aha1740_getconfig(unsigned int base, unsigned int *irq_level,
+ unsigned int *irq_type,
+ unsigned int *translation)
+{
+ static int intab[] = { 9, 10, 11, 12, 0, 14, 15, 0 };
+
+ *irq_level = intab[inb(INTDEF(base)) & 0x7];
+ *irq_type = (inb(INTDEF(base)) & 0x8) >> 3;
+ *translation = inb(RESV1(base)) & 0x1;
+ outb(inb(INTDEF(base)) | 0x10, INTDEF(base));
+}
+
+static int aha1740_biosparam(struct scsi_device *sdev,
+ struct block_device *dev,
+ sector_t capacity, int* ip)
+{
+ int size = capacity;
+ int extended = HOSTDATA(sdev->host)->translation;
+
+ DEB(printk("aha1740_biosparam\n"));
+ if (extended && (ip[2] > 1024)) {
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / (255 * 63);
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ }
+ return 0;
+}
+
+static int aha1740_eh_abort_handler (Scsi_Cmnd *dummy)
+{
+/*
+ * From Alan Cox :
+ * The AHA1740 has firmware handled abort/reset handling. The "head in
+ * sand" kernel code is correct for once 8)
+ *
+ * So we define a dummy handler just to keep the kernel SCSI code as
+ * quiet as possible...
+ */
+
+ return 0;
+}
+
+static Scsi_Host_Template aha1740_template = {
+ .module = THIS_MODULE,
+ .proc_name = "aha1740",
+ .proc_info = aha1740_proc_info,
+ .name = "Adaptec 174x (EISA)",
+ .queuecommand = aha1740_queuecommand,
+ .bios_param = aha1740_biosparam,
+ .can_queue = AHA1740_ECBS,
+ .this_id = 7,
+ .sg_tablesize = AHA1740_SCATTER,
+ .cmd_per_lun = AHA1740_CMDLUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_abort_handler = aha1740_eh_abort_handler,
+};
+
+static int aha1740_probe (struct device *dev)
+{
+ int slotbase;
+ unsigned int irq_level, irq_type, translation;
+ struct Scsi_Host *shpnt;
+ struct aha1740_hostdata *host;
+ struct eisa_device *edev = to_eisa_device (dev);
+
+ DEB(printk("aha1740_probe: \n"));
+
+ slotbase = edev->base_addr + EISA_VENDOR_ID_OFFSET;
+ if (!request_region(slotbase, SLOTSIZE, "aha1740")) /* See if in use */
+ return -EBUSY;
+ if (!aha1740_test_port(slotbase))
+ goto err_release_region;
+ aha1740_getconfig(slotbase,&irq_level,&irq_type,&translation);
+ if ((inb(G2STAT(slotbase)) &
+ (G2STAT_MBXOUT|G2STAT_BUSY)) != G2STAT_MBXOUT) {
+ /* If the card isn't ready, hard reset it */
+ outb(G2CNTRL_HRST, G2CNTRL(slotbase));
+ outb(0, G2CNTRL(slotbase));
+ }
+ printk(KERN_INFO "Configuring slot %d at IO:%x, IRQ %u (%s)\n",
+ edev->slot, slotbase, irq_level, irq_type ? "edge" : "level");
+ printk(KERN_INFO "aha174x: Extended translation %sabled.\n",
+ translation ? "en" : "dis");
+ shpnt = scsi_host_alloc(&aha1740_template,
+ sizeof(struct aha1740_hostdata));
+ if(shpnt == NULL)
+ goto err_release_region;
+
+ shpnt->base = 0;
+ shpnt->io_port = slotbase;
+ shpnt->n_io_port = SLOTSIZE;
+ shpnt->irq = irq_level;
+ shpnt->dma_channel = 0xff;
+ host = HOSTDATA(shpnt);
+ host->edev = edev;
+ host->translation = translation;
+ host->ecb_dma_addr = dma_map_single (&edev->dev, host->ecb,
+ sizeof (host->ecb),
+ DMA_BIDIRECTIONAL);
+ if (!host->ecb_dma_addr) {
+ printk (KERN_ERR "aha1740_probe: Couldn't map ECB, giving up\n");
+ scsi_unregister (shpnt);
+ goto err_host_put;
+ }
+
+ DEB(printk("aha1740_probe: enable interrupt channel %d\n",irq_level));
+ if (request_irq(irq_level,aha1740_intr_handle,irq_type ? 0 : SA_SHIRQ,
+ "aha1740",shpnt)) {
+ printk(KERN_ERR "aha1740_probe: Unable to allocate IRQ %d.\n",
+ irq_level);
+ goto err_unmap;
+ }
+
+ eisa_set_drvdata (edev, shpnt);
+ scsi_add_host (shpnt, dev); /* XXX handle failure */
+ scsi_scan_host (shpnt);
+ return 0;
+
+ err_unmap:
+ dma_unmap_single (&edev->dev, host->ecb_dma_addr,
+ sizeof (host->ecb), DMA_BIDIRECTIONAL);
+ err_host_put:
+ scsi_host_put (shpnt);
+ err_release_region:
+ release_region(slotbase, SLOTSIZE);
+
+ return -ENODEV;
+}
+
+static __devexit int aha1740_remove (struct device *dev)
+{
+ struct Scsi_Host *shpnt = dev->driver_data;
+ struct aha1740_hostdata *host = HOSTDATA (shpnt);
+
+ scsi_remove_host(shpnt);
+
+ free_irq (shpnt->irq, shpnt);
+ dma_unmap_single (dev, host->ecb_dma_addr,
+ sizeof (host->ecb), DMA_BIDIRECTIONAL);
+ release_region (shpnt->io_port, SLOTSIZE);
+
+ scsi_host_put (shpnt);
+
+ return 0;
+}
+
+static struct eisa_device_id aha1740_ids[] = {
+ { "ADP0000" }, /* 1740 */
+ { "ADP0001" }, /* 1740A */
+ { "ADP0002" }, /* 1742A */
+ { "ADP0400" }, /* 1744 */
+ { "" }
+};
+
+static struct eisa_driver aha1740_driver = {
+ .id_table = aha1740_ids,
+ .driver = {
+ .name = "aha1740",
+ .probe = aha1740_probe,
+ .remove = __devexit_p (aha1740_remove),
+ },
+};
+
+static __init int aha1740_init (void)
+{
+ return eisa_driver_register (&aha1740_driver);
+}
+
+static __exit void aha1740_exit (void)
+{
+ eisa_driver_unregister (&aha1740_driver);
+}
+
+module_init (aha1740_init);
+module_exit (aha1740_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/aha1740.h b/drivers/scsi/aha1740.h
new file mode 100644
index 000000000000..af23fd6bd795
--- /dev/null
+++ b/drivers/scsi/aha1740.h
@@ -0,0 +1,154 @@
+#ifndef _AHA1740_H
+
+/* $Id$
+ *
+ * Header file for the adaptec 1740 driver for Linux
+ *
+ * With minor revisions 3/31/93
+ * Written and (C) 1992,1993 Brad McLean. See aha1740.c
+ * for more info
+ *
+ */
+
+#include <linux/types.h>
+
+#define SLOTSIZE 0x5c
+
+/* EISA configuration registers & values */
+#define HID0(base) (base + 0x0)
+#define HID1(base) (base + 0x1)
+#define HID2(base) (base + 0x2)
+#define HID3(base) (base + 0x3)
+#define EBCNTRL(base) (base + 0x4)
+#define PORTADR(base) (base + 0x40)
+#define BIOSADR(base) (base + 0x41)
+#define INTDEF(base) (base + 0x42)
+#define SCSIDEF(base) (base + 0x43)
+#define BUSDEF(base) (base + 0x44)
+#define RESV0(base) (base + 0x45)
+#define RESV1(base) (base + 0x46)
+#define RESV2(base) (base + 0x47)
+
+#define HID_MFG "ADP"
+#define HID_PRD 0
+#define HID_REV 2
+#define EBCNTRL_VALUE 1
+#define PORTADDR_ENH 0x80
+/* READ */
+#define G2INTST(base) (base + 0x56)
+#define G2STAT(base) (base + 0x57)
+#define MBOXIN0(base) (base + 0x58)
+#define MBOXIN1(base) (base + 0x59)
+#define MBOXIN2(base) (base + 0x5a)
+#define MBOXIN3(base) (base + 0x5b)
+#define G2STAT2(base) (base + 0x5c)
+
+#define G2INTST_MASK 0xf0 /* isolate the status */
+#define G2INTST_CCBGOOD 0x10 /* CCB Completed */
+#define G2INTST_CCBRETRY 0x50 /* CCB Completed with a retry */
+#define G2INTST_HARDFAIL 0x70 /* Adapter Hardware Failure */
+#define G2INTST_CMDGOOD 0xa0 /* Immediate command success */
+#define G2INTST_CCBERROR 0xc0 /* CCB Completed with error */
+#define G2INTST_ASNEVENT 0xd0 /* Asynchronous Event Notification */
+#define G2INTST_CMDERROR 0xe0 /* Immediate command error */
+
+#define G2STAT_MBXOUT 4 /* Mailbox Out Empty Bit */
+#define G2STAT_INTPEND 2 /* Interrupt Pending Bit */
+#define G2STAT_BUSY 1 /* Busy Bit (attention pending) */
+
+#define G2STAT2_READY 0 /* Host Ready Bit */
+
+/* WRITE (and ReadBack) */
+#define MBOXOUT0(base) (base + 0x50)
+#define MBOXOUT1(base) (base + 0x51)
+#define MBOXOUT2(base) (base + 0x52)
+#define MBOXOUT3(base) (base + 0x53)
+#define ATTN(base) (base + 0x54)
+#define G2CNTRL(base) (base + 0x55)
+
+#define ATTN_IMMED 0x10 /* Immediate Command */
+#define ATTN_START 0x40 /* Start CCB */
+#define ATTN_ABORT 0x50 /* Abort CCB */
+
+#define G2CNTRL_HRST 0x80 /* Hard Reset */
+#define G2CNTRL_IRST 0x40 /* Clear EISA Interrupt */
+#define G2CNTRL_HRDY 0x20 /* Sets HOST ready */
+
+/* This is used with scatter-gather */
+struct aha1740_chain {
+ u32 dataptr; /* Location of data */
+ u32 datalen; /* Size of this part of chain */
+};
+
+/* These belong in scsi.h */
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16) ; \
+(up)[1] = (((unsigned long)(p)) >> 8); \
+(up)[2] = ((unsigned long)(p));
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+#define MAX_STATUS 32
+
+struct ecb { /* Enhanced Control Block 6.1 */
+ u16 cmdw; /* Command Word */
+ /* Flag Word 1 */
+ u16 cne:1, /* Control Block Chaining */
+ :6, di:1, /* Disable Interrupt */
+ :2, ses:1, /* Suppress Underrun error */
+ :1, sg:1, /* Scatter/Gather */
+ :1, dsb:1, /* Disable Status Block */
+ ars:1; /* Automatic Request Sense */
+ /* Flag Word 2 */
+ u16 lun:3, /* Logical Unit */
+ tag:1, /* Tagged Queuing */
+ tt:2, /* Tag Type */
+ nd:1, /* No Disconnect */
+ :1, dat:1, /* Data transfer - check direction */
+ dir:1, /* Direction of transfer 1 = datain */
+ st:1, /* Suppress Transfer */
+ chk:1, /* Calculate Checksum */
+ :2, rec:1,:1; /* Error Recovery */
+ u16 nil0; /* nothing */
+ u32 dataptr; /* Data or Scatter List ptr */
+ u32 datalen; /* Data or Scatter List len */
+ u32 statusptr; /* Status Block ptr */
+ u32 linkptr; /* Chain Address */
+ u32 nil1; /* nothing */
+ u32 senseptr; /* Sense Info Pointer */
+ u8 senselen; /* Sense Length */
+ u8 cdblen; /* CDB Length */
+ u16 datacheck; /* Data checksum */
+ u8 cdb[MAX_CDB]; /* CDB area */
+/* Hardware defined portion ends here, rest is driver defined */
+ u8 sense[MAX_SENSE]; /* Sense area */
+ u8 status[MAX_STATUS]; /* Status area */
+ Scsi_Cmnd *SCpnt; /* Link to the SCSI Command Block */
+ void (*done) (Scsi_Cmnd *); /* Completion Function */
+};
+
+#define AHA1740CMD_NOP 0x00 /* No OP */
+#define AHA1740CMD_INIT 0x01 /* Initiator SCSI Command */
+#define AHA1740CMD_DIAG 0x05 /* Run Diagnostic Command */
+#define AHA1740CMD_SCSI 0x06 /* Initialize SCSI */
+#define AHA1740CMD_SENSE 0x08 /* Read Sense Information */
+#define AHA1740CMD_DOWN 0x09 /* Download Firmware (yeah, I bet!) */
+#define AHA1740CMD_RINQ 0x0a /* Read Host Adapter Inquiry Data */
+#define AHA1740CMD_TARG 0x10 /* Target SCSI Command */
+
+#define AHA1740_ECBS 32
+#define AHA1740_SCATTER 16
+#define AHA1740_CMDLUN 1
+
+#endif
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
new file mode 100644
index 000000000000..3a15b13e747a
--- /dev/null
+++ b/drivers/scsi/ahci.c
@@ -0,0 +1,1065 @@
+/*
+ * ahci.c - AHCI SATA support
+ *
+ * Copyright 2004 Red Hat, Inc.
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ * Version 1.0 of the AHCI specification:
+ * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME "ahci"
+#define DRV_VERSION "1.00"
+
+
+enum {
+ AHCI_PCI_BAR = 5,
+ AHCI_MAX_SG = 168, /* hardware max is 64K */
+ AHCI_DMA_BOUNDARY = 0xffffffff,
+ AHCI_USE_CLUSTERING = 0,
+ AHCI_CMD_SLOT_SZ = 32 * 32,
+ AHCI_RX_FIS_SZ = 256,
+ AHCI_CMD_TBL_HDR = 0x80,
+ AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16),
+ AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ +
+ AHCI_RX_FIS_SZ,
+ AHCI_IRQ_ON_SG = (1 << 31),
+ AHCI_CMD_ATAPI = (1 << 5),
+ AHCI_CMD_WRITE = (1 << 6),
+
+ RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
+
+ board_ahci = 0,
+
+ /* global controller registers */
+ HOST_CAP = 0x00, /* host capabilities */
+ HOST_CTL = 0x04, /* global host control */
+ HOST_IRQ_STAT = 0x08, /* interrupt status */
+ HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */
+ HOST_VERSION = 0x10, /* AHCI spec. version compliancy */
+
+ /* HOST_CTL bits */
+ HOST_RESET = (1 << 0), /* reset controller; self-clear */
+ HOST_IRQ_EN = (1 << 1), /* global IRQ enable */
+ HOST_AHCI_EN = (1 << 31), /* AHCI enabled */
+
+ /* HOST_CAP bits */
+ HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */
+
+ /* registers for each SATA port */
+ PORT_LST_ADDR = 0x00, /* command list DMA addr */
+ PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */
+ PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */
+ PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */
+ PORT_IRQ_STAT = 0x10, /* interrupt status */
+ PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */
+ PORT_CMD = 0x18, /* port command */
+ PORT_TFDATA = 0x20, /* taskfile data */
+ PORT_SIG = 0x24, /* device TF signature */
+ PORT_CMD_ISSUE = 0x38, /* command issue */
+ PORT_SCR = 0x28, /* SATA phy register block */
+ PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */
+ PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */
+ PORT_SCR_ERR = 0x30, /* SATA phy register: SError */
+ PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */
+
+ /* PORT_IRQ_{STAT,MASK} bits */
+ PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */
+ PORT_IRQ_TF_ERR = (1 << 30), /* task file error */
+ PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */
+ PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */
+ PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */
+ PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */
+ PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */
+ PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */
+
+ PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */
+ PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */
+ PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */
+ PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */
+ PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */
+ PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */
+ PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */
+ PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */
+ PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */
+
+ PORT_IRQ_FATAL = PORT_IRQ_TF_ERR |
+ PORT_IRQ_HBUS_ERR |
+ PORT_IRQ_HBUS_DATA_ERR |
+ PORT_IRQ_IF_ERR,
+ DEF_PORT_IRQ = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY |
+ PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE |
+ PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS |
+ PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS |
+ PORT_IRQ_D2H_REG_FIS,
+
+ /* PORT_CMD bits */
+ PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */
+ PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */
+ PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */
+ PORT_CMD_POWER_ON = (1 << 2), /* Power up device */
+ PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */
+ PORT_CMD_START = (1 << 0), /* Enable port DMA engine */
+
+ PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */
+ PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */
+ PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */
+};
+
+struct ahci_cmd_hdr {
+ u32 opts;
+ u32 status;
+ u32 tbl_addr;
+ u32 tbl_addr_hi;
+ u32 reserved[4];
+};
+
+struct ahci_sg {
+ u32 addr;
+ u32 addr_hi;
+ u32 reserved;
+ u32 flags_size;
+};
+
+struct ahci_host_priv {
+ unsigned long flags;
+ u32 cap; /* cache of HOST_CAP register */
+ u32 port_map; /* cache of HOST_PORTS_IMPL reg */
+};
+
+struct ahci_port_priv {
+ struct ahci_cmd_hdr *cmd_slot;
+ dma_addr_t cmd_slot_dma;
+ void *cmd_tbl;
+ dma_addr_t cmd_tbl_dma;
+ struct ahci_sg *cmd_tbl_sg;
+ void *rx_fis;
+ dma_addr_t rx_fis_dma;
+};
+
+static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static int ahci_qc_issue(struct ata_queued_cmd *qc);
+static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
+static void ahci_phy_reset(struct ata_port *ap);
+static void ahci_irq_clear(struct ata_port *ap);
+static void ahci_eng_timeout(struct ata_port *ap);
+static int ahci_port_start(struct ata_port *ap);
+static void ahci_port_stop(struct ata_port *ap);
+static void ahci_host_stop(struct ata_host_set *host_set);
+static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
+static void ahci_qc_prep(struct ata_queued_cmd *qc);
+static u8 ahci_check_status(struct ata_port *ap);
+static u8 ahci_check_err(struct ata_port *ap);
+static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+
+static Scsi_Host_Template ahci_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = AHCI_MAX_SG,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = AHCI_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = AHCI_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations ahci_ops = {
+ .port_disable = ata_port_disable,
+
+ .check_status = ahci_check_status,
+ .check_altstatus = ahci_check_status,
+ .check_err = ahci_check_err,
+ .dev_select = ata_noop_dev_select,
+
+ .tf_read = ahci_tf_read,
+
+ .phy_reset = ahci_phy_reset,
+
+ .qc_prep = ahci_qc_prep,
+ .qc_issue = ahci_qc_issue,
+
+ .eng_timeout = ahci_eng_timeout,
+
+ .irq_handler = ahci_interrupt,
+ .irq_clear = ahci_irq_clear,
+
+ .scr_read = ahci_scr_read,
+ .scr_write = ahci_scr_write,
+
+ .port_start = ahci_port_start,
+ .port_stop = ahci_port_stop,
+ .host_stop = ahci_host_stop,
+};
+
+static struct ata_port_info ahci_port_info[] = {
+ /* board_ahci */
+ {
+ .sht = &ahci_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+ ATA_FLAG_PIO_DMA,
+ .pio_mask = 0x03, /* pio3-4 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &ahci_ops,
+ },
+};
+
+static struct pci_device_id ahci_pci_tbl[] = {
+ { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ICH6 */
+ { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ICH6M */
+ { PCI_VENDOR_ID_INTEL, 0x27c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ICH7 */
+ { PCI_VENDOR_ID_INTEL, 0x27c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ICH7M */
+ { PCI_VENDOR_ID_INTEL, 0x27c3, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ICH7R */
+ { PCI_VENDOR_ID_AL, 0x5288, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ULi M5288 */
+ { } /* terminate list */
+};
+
+
+static struct pci_driver ahci_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ahci_pci_tbl,
+ .probe = ahci_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+
+static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port)
+{
+ return base + 0x100 + (port * 0x80);
+}
+
+static inline void *ahci_port_base (void *base, unsigned int port)
+{
+ return (void *) ahci_port_base_ul((unsigned long)base, port);
+}
+
+static void ahci_host_stop(struct ata_host_set *host_set)
+{
+ struct ahci_host_priv *hpriv = host_set->private_data;
+ kfree(hpriv);
+}
+
+static int ahci_port_start(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct ahci_host_priv *hpriv = ap->host_set->private_data;
+ struct ahci_port_priv *pp;
+ int rc;
+ void *mem, *mmio = ap->host_set->mmio_base;
+ void *port_mmio = ahci_port_base(mmio, ap->port_no);
+ dma_addr_t mem_dma;
+
+ rc = ata_port_start(ap);
+ if (rc)
+ return rc;
+
+ pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ mem = dma_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL);
+ if (!mem) {
+ rc = -ENOMEM;
+ goto err_out_kfree;
+ }
+ memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+
+ /*
+ * First item in chunk of DMA memory: 32-slot command table,
+ * 32 bytes each in size
+ */
+ pp->cmd_slot = mem;
+ pp->cmd_slot_dma = mem_dma;
+
+ mem += AHCI_CMD_SLOT_SZ;
+ mem_dma += AHCI_CMD_SLOT_SZ;
+
+ /*
+ * Second item: Received-FIS area
+ */
+ pp->rx_fis = mem;
+ pp->rx_fis_dma = mem_dma;
+
+ mem += AHCI_RX_FIS_SZ;
+ mem_dma += AHCI_RX_FIS_SZ;
+
+ /*
+ * Third item: data area for storing a single command
+ * and its scatter-gather table
+ */
+ pp->cmd_tbl = mem;
+ pp->cmd_tbl_dma = mem_dma;
+
+ pp->cmd_tbl_sg = mem + AHCI_CMD_TBL_HDR;
+
+ ap->private_data = pp;
+
+ if (hpriv->cap & HOST_CAP_64)
+ writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
+ writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+ readl(port_mmio + PORT_LST_ADDR); /* flush */
+
+ if (hpriv->cap & HOST_CAP_64)
+ writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
+ writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+ readl(port_mmio + PORT_FIS_ADDR); /* flush */
+
+ writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
+ PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
+ PORT_CMD_START, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+
+ return 0;
+
+err_out_kfree:
+ kfree(pp);
+err_out:
+ ata_port_stop(ap);
+ return rc;
+}
+
+
+static void ahci_port_stop(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct ahci_port_priv *pp = ap->private_data;
+ void *mmio = ap->host_set->mmio_base;
+ void *port_mmio = ahci_port_base(mmio, ap->port_no);
+ u32 tmp;
+
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+
+ /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
+ * this is slightly incorrect.
+ */
+ msleep(500);
+
+ ap->private_data = NULL;
+ dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
+ pp->cmd_slot, pp->cmd_slot_dma);
+ kfree(pp);
+ ata_port_stop(ap);
+}
+
+static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
+{
+ unsigned int sc_reg;
+
+ switch (sc_reg_in) {
+ case SCR_STATUS: sc_reg = 0; break;
+ case SCR_CONTROL: sc_reg = 1; break;
+ case SCR_ERROR: sc_reg = 2; break;
+ case SCR_ACTIVE: sc_reg = 3; break;
+ default:
+ return 0xffffffffU;
+ }
+
+ return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
+ u32 val)
+{
+ unsigned int sc_reg;
+
+ switch (sc_reg_in) {
+ case SCR_STATUS: sc_reg = 0; break;
+ case SCR_CONTROL: sc_reg = 1; break;
+ case SCR_ERROR: sc_reg = 2; break;
+ case SCR_ACTIVE: sc_reg = 3; break;
+ default:
+ return;
+ }
+
+ writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void ahci_phy_reset(struct ata_port *ap)
+{
+ void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+ struct ata_taskfile tf;
+ struct ata_device *dev = &ap->device[0];
+ u32 tmp;
+
+ __sata_phy_reset(ap);
+
+ if (ap->flags & ATA_FLAG_PORT_DISABLED)
+ return;
+
+ tmp = readl(port_mmio + PORT_SIG);
+ tf.lbah = (tmp >> 24) & 0xff;
+ tf.lbam = (tmp >> 16) & 0xff;
+ tf.lbal = (tmp >> 8) & 0xff;
+ tf.nsect = (tmp) & 0xff;
+
+ dev->class = ata_dev_classify(&tf);
+ if (!ata_dev_present(dev))
+ ata_port_disable(ap);
+}
+
+static u8 ahci_check_status(struct ata_port *ap)
+{
+ void *mmio = (void *) ap->ioaddr.cmd_addr;
+
+ return readl(mmio + PORT_TFDATA) & 0xFF;
+}
+
+static u8 ahci_check_err(struct ata_port *ap)
+{
+ void *mmio = (void *) ap->ioaddr.cmd_addr;
+
+ return (readl(mmio + PORT_TFDATA) >> 8) & 0xFF;
+}
+
+static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ahci_port_priv *pp = ap->private_data;
+ u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
+
+ ata_tf_from_fis(d2h_fis, tf);
+}
+
+static void ahci_fill_sg(struct ata_queued_cmd *qc)
+{
+ struct ahci_port_priv *pp = qc->ap->private_data;
+ unsigned int i;
+
+ VPRINTK("ENTER\n");
+
+ /*
+ * Next, the S/G list.
+ */
+ for (i = 0; i < qc->n_elem; i++) {
+ u32 sg_len;
+ dma_addr_t addr;
+
+ addr = sg_dma_address(&qc->sg[i]);
+ sg_len = sg_dma_len(&qc->sg[i]);
+
+ pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff);
+ pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
+ pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len - 1);
+ }
+}
+
+static void ahci_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct ahci_port_priv *pp = qc->ap->private_data;
+ u32 opts;
+ const u32 cmd_fis_len = 5; /* five dwords */
+
+ /*
+ * Fill in command slot information (currently only one slot,
+ * slot 0, is currently since we don't do queueing)
+ */
+
+ opts = (qc->n_elem << 16) | cmd_fis_len;
+ if (qc->tf.flags & ATA_TFLAG_WRITE)
+ opts |= AHCI_CMD_WRITE;
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_ATAPI:
+ case ATA_PROT_ATAPI_NODATA:
+ case ATA_PROT_ATAPI_DMA:
+ opts |= AHCI_CMD_ATAPI;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ pp->cmd_slot[0].opts = cpu_to_le32(opts);
+ pp->cmd_slot[0].status = 0;
+ pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
+ pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
+
+ /*
+ * Fill in command table information. First, the header,
+ * a SATA Register - Host to Device command FIS.
+ */
+ ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0);
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ ahci_fill_sg(qc);
+}
+
+static void ahci_intr_error(struct ata_port *ap, u32 irq_stat)
+{
+ void *mmio = ap->host_set->mmio_base;
+ void *port_mmio = ahci_port_base(mmio, ap->port_no);
+ u32 tmp;
+ int work;
+
+ /* stop DMA */
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp &= ~PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+
+ /* wait for engine to stop. TODO: this could be
+ * as long as 500 msec
+ */
+ work = 1000;
+ while (work-- > 0) {
+ tmp = readl(port_mmio + PORT_CMD);
+ if ((tmp & PORT_CMD_LIST_ON) == 0)
+ break;
+ udelay(10);
+ }
+
+ /* clear SATA phy error, if any */
+ tmp = readl(port_mmio + PORT_SCR_ERR);
+ writel(tmp, port_mmio + PORT_SCR_ERR);
+
+ /* if DRQ/BSY is set, device needs to be reset.
+ * if so, issue COMRESET
+ */
+ tmp = readl(port_mmio + PORT_TFDATA);
+ if (tmp & (ATA_BUSY | ATA_DRQ)) {
+ writel(0x301, port_mmio + PORT_SCR_CTL);
+ readl(port_mmio + PORT_SCR_CTL); /* flush */
+ udelay(10);
+ writel(0x300, port_mmio + PORT_SCR_CTL);
+ readl(port_mmio + PORT_SCR_CTL); /* flush */
+ }
+
+ /* re-start DMA */
+ tmp = readl(port_mmio + PORT_CMD);
+ tmp |= PORT_CMD_START;
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+
+ printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->id);
+}
+
+static void ahci_eng_timeout(struct ata_port *ap)
+{
+ void *mmio = ap->host_set->mmio_base;
+ void *port_mmio = ahci_port_base(mmio, ap->port_no);
+ struct ata_queued_cmd *qc;
+
+ DPRINTK("ENTER\n");
+
+ ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT));
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (!qc) {
+ printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+ ap->id);
+ } else {
+ /* hack alert! We cannot use the supplied completion
+ * function from inside the ->eh_strategy_handler() thread.
+ * libata is the only user of ->eh_strategy_handler() in
+ * any kernel, so the default scsi_done() assumes it is
+ * not being called from the SCSI EH.
+ */
+ qc->scsidone = scsi_finish_command;
+ ata_qc_complete(qc, ATA_ERR);
+ }
+
+}
+
+static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+ void *mmio = ap->host_set->mmio_base;
+ void *port_mmio = ahci_port_base(mmio, ap->port_no);
+ u32 status, serr, ci;
+
+ serr = readl(port_mmio + PORT_SCR_ERR);
+ writel(serr, port_mmio + PORT_SCR_ERR);
+
+ status = readl(port_mmio + PORT_IRQ_STAT);
+ writel(status, port_mmio + PORT_IRQ_STAT);
+
+ ci = readl(port_mmio + PORT_CMD_ISSUE);
+ if (likely((ci & 0x1) == 0)) {
+ if (qc) {
+ ata_qc_complete(qc, 0);
+ qc = NULL;
+ }
+ }
+
+ if (status & PORT_IRQ_FATAL) {
+ ahci_intr_error(ap, status);
+ if (qc)
+ ata_qc_complete(qc, ATA_ERR);
+ }
+
+ return 1;
+}
+
+static void ahci_irq_clear(struct ata_port *ap)
+{
+ /* TODO */
+}
+
+static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ struct ahci_host_priv *hpriv;
+ unsigned int i, handled = 0;
+ void *mmio;
+ u32 irq_stat, irq_ack = 0;
+
+ VPRINTK("ENTER\n");
+
+ hpriv = host_set->private_data;
+ mmio = host_set->mmio_base;
+
+ /* sigh. 0xffffffff is a valid return from h/w */
+ irq_stat = readl(mmio + HOST_IRQ_STAT);
+ irq_stat &= hpriv->port_map;
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ spin_lock(&host_set->lock);
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap;
+ u32 tmp;
+
+ VPRINTK("port %u\n", i);
+ ap = host_set->ports[i];
+ tmp = irq_stat & (1 << i);
+ if (tmp && ap) {
+ struct ata_queued_cmd *qc;
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (ahci_host_intr(ap, qc))
+ irq_ack |= (1 << i);
+ }
+ }
+
+ if (irq_ack) {
+ writel(irq_ack, mmio + HOST_IRQ_STAT);
+ handled = 1;
+ }
+
+ spin_unlock(&host_set->lock);
+
+ VPRINTK("EXIT\n");
+
+ return IRQ_RETVAL(handled);
+}
+
+static int ahci_qc_issue(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void *port_mmio = (void *) ap->ioaddr.cmd_addr;
+
+ writel(1, port_mmio + PORT_SCR_ACT);
+ readl(port_mmio + PORT_SCR_ACT); /* flush */
+
+ writel(1, port_mmio + PORT_CMD_ISSUE);
+ readl(port_mmio + PORT_CMD_ISSUE); /* flush */
+
+ return 0;
+}
+
+static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
+ unsigned int port_idx)
+{
+ VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx);
+ base = ahci_port_base_ul(base, port_idx);
+ VPRINTK("base now==0x%lx\n", base);
+
+ port->cmd_addr = base;
+ port->scr_addr = base + PORT_SCR;
+
+ VPRINTK("EXIT\n");
+}
+
+static int ahci_host_init(struct ata_probe_ent *probe_ent)
+{
+ struct ahci_host_priv *hpriv = probe_ent->private_data;
+ struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ void __iomem *mmio = probe_ent->mmio_base;
+ u32 tmp, cap_save;
+ u16 tmp16;
+ unsigned int i, j, using_dac;
+ int rc;
+ void __iomem *port_mmio;
+
+ cap_save = readl(mmio + HOST_CAP);
+ cap_save &= ( (1<<28) | (1<<17) );
+ cap_save |= (1 << 27);
+
+ /* global controller reset */
+ tmp = readl(mmio + HOST_CTL);
+ if ((tmp & HOST_RESET) == 0) {
+ writel(tmp | HOST_RESET, mmio + HOST_CTL);
+ readl(mmio + HOST_CTL); /* flush */
+ }
+
+ /* reset must complete within 1 second, or
+ * the hardware should be considered fried.
+ */
+ ssleep(1);
+
+ tmp = readl(mmio + HOST_CTL);
+ if (tmp & HOST_RESET) {
+ printk(KERN_ERR DRV_NAME "(%s): controller reset failed (0x%x)\n",
+ pci_name(pdev), tmp);
+ return -EIO;
+ }
+
+ writel(HOST_AHCI_EN, mmio + HOST_CTL);
+ (void) readl(mmio + HOST_CTL); /* flush */
+ writel(cap_save, mmio + HOST_CAP);
+ writel(0xf, mmio + HOST_PORTS_IMPL);
+ (void) readl(mmio + HOST_PORTS_IMPL); /* flush */
+
+ pci_read_config_word(pdev, 0x92, &tmp16);
+ tmp16 |= 0xf;
+ pci_write_config_word(pdev, 0x92, tmp16);
+
+ hpriv->cap = readl(mmio + HOST_CAP);
+ hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
+ probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
+
+ VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
+ hpriv->cap, hpriv->port_map, probe_ent->n_ports);
+
+ using_dac = hpriv->cap & HOST_CAP_64;
+ if (using_dac &&
+ !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+ if (rc) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): 64-bit DMA enable failed\n",
+ pci_name(pdev));
+ return rc;
+ }
+ }
+
+ hpriv->flags |= HOST_CAP_64;
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n",
+ pci_name(pdev));
+ return rc;
+ }
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n",
+ pci_name(pdev));
+ return rc;
+ }
+ }
+
+ for (i = 0; i < probe_ent->n_ports; i++) {
+#if 0 /* BIOSen initialize this incorrectly */
+ if (!(hpriv->port_map & (1 << i)))
+ continue;
+#endif
+
+ port_mmio = ahci_port_base(mmio, i);
+ VPRINTK("mmio %p port_mmio %p\n", mmio, port_mmio);
+
+ ahci_setup_port(&probe_ent->port[i],
+ (unsigned long) mmio, i);
+
+ /* make sure port is not active */
+ tmp = readl(port_mmio + PORT_CMD);
+ VPRINTK("PORT_CMD 0x%x\n", tmp);
+ if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
+ PORT_CMD_FIS_RX | PORT_CMD_START)) {
+ tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
+ PORT_CMD_FIS_RX | PORT_CMD_START);
+ writel(tmp, port_mmio + PORT_CMD);
+ readl(port_mmio + PORT_CMD); /* flush */
+
+ /* spec says 500 msecs for each bit, so
+ * this is slightly incorrect.
+ */
+ msleep(500);
+ }
+
+ writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
+
+ j = 0;
+ while (j < 100) {
+ msleep(10);
+ tmp = readl(port_mmio + PORT_SCR_STAT);
+ if ((tmp & 0xf) == 0x3)
+ break;
+ j++;
+ }
+
+ tmp = readl(port_mmio + PORT_SCR_ERR);
+ VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
+ writel(tmp, port_mmio + PORT_SCR_ERR);
+
+ /* ack any pending irq events for this port */
+ tmp = readl(port_mmio + PORT_IRQ_STAT);
+ VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
+ if (tmp)
+ writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+ writel(1 << i, mmio + HOST_IRQ_STAT);
+
+ /* set irq mask (enables interrupts) */
+ writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
+ }
+
+ tmp = readl(mmio + HOST_CTL);
+ VPRINTK("HOST_CTL 0x%x\n", tmp);
+ writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
+ tmp = readl(mmio + HOST_CTL);
+ VPRINTK("HOST_CTL 0x%x\n", tmp);
+
+ pci_set_master(pdev);
+
+ return 0;
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+ u16 pci_command;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+ if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+ pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+ }
+}
+
+static void ahci_print_info(struct ata_probe_ent *probe_ent)
+{
+ struct ahci_host_priv *hpriv = probe_ent->private_data;
+ struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ void *mmio = probe_ent->mmio_base;
+ u32 vers, cap, impl, speed;
+ const char *speed_s;
+ u16 cc;
+ const char *scc_s;
+
+ vers = readl(mmio + HOST_VERSION);
+ cap = hpriv->cap;
+ impl = hpriv->port_map;
+
+ speed = (cap >> 20) & 0xf;
+ if (speed == 1)
+ speed_s = "1.5";
+ else if (speed == 2)
+ speed_s = "3";
+ else
+ speed_s = "?";
+
+ pci_read_config_word(pdev, 0x0a, &cc);
+ if (cc == 0x0101)
+ scc_s = "IDE";
+ else if (cc == 0x0106)
+ scc_s = "SATA";
+ else if (cc == 0x0104)
+ scc_s = "RAID";
+ else
+ scc_s = "unknown";
+
+ printk(KERN_INFO DRV_NAME "(%s) AHCI %02x%02x.%02x%02x "
+ "%u slots %u ports %s Gbps 0x%x impl %s mode\n"
+ ,
+ pci_name(pdev),
+
+ (vers >> 24) & 0xff,
+ (vers >> 16) & 0xff,
+ (vers >> 8) & 0xff,
+ vers & 0xff,
+
+ ((cap >> 8) & 0x1f) + 1,
+ (cap & 0x1f) + 1,
+ speed_s,
+ impl,
+ scc_s);
+
+ printk(KERN_INFO DRV_NAME "(%s) flags: "
+ "%s%s%s%s%s%s"
+ "%s%s%s%s%s%s%s\n"
+ ,
+ pci_name(pdev),
+
+ cap & (1 << 31) ? "64bit " : "",
+ cap & (1 << 30) ? "ncq " : "",
+ cap & (1 << 28) ? "ilck " : "",
+ cap & (1 << 27) ? "stag " : "",
+ cap & (1 << 26) ? "pm " : "",
+ cap & (1 << 25) ? "led " : "",
+
+ cap & (1 << 24) ? "clo " : "",
+ cap & (1 << 19) ? "nz " : "",
+ cap & (1 << 18) ? "only " : "",
+ cap & (1 << 17) ? "pmp " : "",
+ cap & (1 << 15) ? "pio " : "",
+ cap & (1 << 14) ? "slum " : "",
+ cap & (1 << 13) ? "part " : ""
+ );
+}
+
+static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ struct ahci_host_priv *hpriv;
+ unsigned long base;
+ void *mmio_base;
+ unsigned int board_idx = (unsigned int) ent->driver_data;
+ int pci_dev_busy = 0;
+ int rc;
+
+ VPRINTK("ENTER\n");
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ pci_enable_intx(pdev);
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ mmio_base = ioremap(pci_resource_start(pdev, AHCI_PCI_BAR),
+ pci_resource_len(pdev, AHCI_PCI_BAR));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+ base = (unsigned long) mmio_base;
+
+ hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+ if (!hpriv) {
+ rc = -ENOMEM;
+ goto err_out_iounmap;
+ }
+ memset(hpriv, 0, sizeof(*hpriv));
+
+ probe_ent->sht = ahci_port_info[board_idx].sht;
+ probe_ent->host_flags = ahci_port_info[board_idx].host_flags;
+ probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask;
+ probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask;
+ probe_ent->port_ops = ahci_port_info[board_idx].port_ops;
+
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+ probe_ent->private_data = hpriv;
+
+ /* initialize adapter */
+ rc = ahci_host_init(probe_ent);
+ if (rc)
+ goto err_out_hpriv;
+
+ ahci_print_info(probe_ent);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_hpriv:
+ kfree(hpriv);
+err_out_iounmap:
+ iounmap(mmio_base);
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+
+static int __init ahci_init(void)
+{
+ return pci_module_init(&ahci_pci_driver);
+}
+
+
+static void __exit ahci_exit(void)
+{
+ pci_unregister_driver(&ahci_pci_driver);
+}
+
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("AHCI SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, ahci_pci_tbl);
+
+module_init(ahci_init);
+module_exit(ahci_exit);
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx
new file mode 100644
index 000000000000..c2523a30a7f5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx
@@ -0,0 +1,97 @@
+#
+# AIC79XX 2.5.X Kernel configuration File.
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic79xx#4 $
+#
+config SCSI_AIC79XX
+ tristate "Adaptec AIC79xx U320 support"
+ depends on PCI && SCSI
+ help
+ This driver supports all of Adaptec's Ultra 320 PCI-X
+ based SCSI controllers.
+
+config AIC79XX_CMDS_PER_DEVICE
+ int "Maximum number of TCQ commands per device"
+ depends on SCSI_AIC79XX
+ default "32"
+ ---help---
+ Specify the number of commands you would like to allocate per SCSI
+ device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+ This is an upper bound value for the number of tagged transactions
+ to be used for any device. The aic7xxx driver will automatically
+ vary this number based on device behavior. For devices with a
+ fixed maximum, the driver will eventually lock to this maximum
+ and display a console message inidicating this value.
+
+ Due to resource allocation issues in the Linux SCSI mid-layer, using
+ a high number of commands per device may result in memory allocation
+ failures when many devices are attached to the system. For this reason,
+ the default is set to 32. Higher values may result in higer performance
+ on some devices. The upper bound is 253. 0 disables tagged queueing.
+
+ Per device tag depth can be controlled via the kernel command line
+ "tag_info" option. See drivers/scsi/aic7xxx/README.aic79xx
+ for details.
+
+config AIC79XX_RESET_DELAY_MS
+ int "Initial bus reset delay in milli-seconds"
+ depends on SCSI_AIC79XX
+ default "15000"
+ ---help---
+ The number of milliseconds to delay after an initial bus reset.
+ The bus settle delay following all error recovery actions is
+ dictated by the SCSI layer and is not affected by this value.
+
+ Default: 15000 (15 seconds)
+
+config AIC79XX_BUILD_FIRMWARE
+ bool "Build Adapter Firmware with Kernel Build"
+ depends on SCSI_AIC79XX && !PREVENT_FIRMWARE_BUILD
+ help
+ This option should only be enabled if you are modifying the firmware
+ source to the aic79xx driver and wish to have the generated firmware
+ include files updated during a normal kernel build. The assembler
+ for the firmware requires lex and yacc or their equivalents, as well
+ as the db v1 library. You may have to install additional packages
+ or modify the assembler Makefile or the files it includes if your
+ build environment is different than that of the author.
+
+config AIC79XX_ENABLE_RD_STRM
+ bool "Enable Read Streaming for All Targets"
+ depends on SCSI_AIC79XX
+ default n
+ help
+ Read Streaming is a U320 protocol option that should enhance
+ performance. Early U320 drive firmware actually performs slower
+ with read streaming enabled so it is disabled by default. Read
+ Streaming can be configured in much the same way as tagged queueing
+ using the "rd_strm" command line option. See
+ drivers/scsi/aic7xxx/README.aic79xx for details.
+
+config AIC79XX_DEBUG_ENABLE
+ bool "Compile in Debugging Code"
+ depends on SCSI_AIC79XX
+ default y
+ help
+ Compile in aic79xx debugging code that can be useful in diagnosing
+ driver errors.
+
+config AIC79XX_DEBUG_MASK
+ int "Debug code enable mask (16383 for all debugging)"
+ depends on SCSI_AIC79XX
+ default "0"
+ help
+ Bit mask of debug options that is only valid if the
+ CONFIG_AIC79XX_DEBUG_ENBLE option is enabled. The bits in this mask
+ are defined in the drivers/scsi/aic7xxx/aic79xx.h - search for the
+ variable ahd_debug in that file to find them.
+
+config AIC79XX_REG_PRETTY_PRINT
+ bool "Decode registers during diagnostics"
+ depends on SCSI_AIC79XX
+ default y
+ help
+ Compile in register value tables for the output of expanded register
+ contents in diagnostics. This make it much easier to understand debug
+ output without having to refer to a data book and/or the aic7xxx.reg
+ file.
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
new file mode 100644
index 000000000000..8398e0dd4810
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
@@ -0,0 +1,100 @@
+#
+# AIC7XXX and AIC79XX 2.5.X Kernel configuration File.
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic7xxx#7 $
+#
+config SCSI_AIC7XXX
+ tristate "Adaptec AIC7xxx Fast -> U160 support (New Driver)"
+ depends on (PCI || EISA) && SCSI
+ ---help---
+ This driver supports all of Adaptec's Fast through Ultra 160 PCI
+ based SCSI controllers as well as the aic7770 based EISA and VLB
+ SCSI controllers (the 274x and 284x series). For AAA and ARO based
+ configurations, only SCSI functionality is provided.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aic7xxx.
+
+config AIC7XXX_CMDS_PER_DEVICE
+ int "Maximum number of TCQ commands per device"
+ depends on SCSI_AIC7XXX
+ default "32"
+ ---help---
+ Specify the number of commands you would like to allocate per SCSI
+ device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+ This is an upper bound value for the number of tagged transactions
+ to be used for any device. The aic7xxx driver will automatically
+ vary this number based on device behavior. For devices with a
+ fixed maximum, the driver will eventually lock to this maximum
+ and display a console message inidicating this value.
+
+ Due to resource allocation issues in the Linux SCSI mid-layer, using
+ a high number of commands per device may result in memory allocation
+ failures when many devices are attached to the system. For this reason,
+ the default is set to 32. Higher values may result in higer performance
+ on some devices. The upper bound is 253. 0 disables tagged queueing.
+
+ Per device tag depth can be controlled via the kernel command line
+ "tag_info" option. See drivers/scsi/aic7xxx/README.aic7xxx
+ for details.
+
+config AIC7XXX_RESET_DELAY_MS
+ int "Initial bus reset delay in milli-seconds"
+ depends on SCSI_AIC7XXX
+ default "15000"
+ ---help---
+ The number of milliseconds to delay after an initial bus reset.
+ The bus settle delay following all error recovery actions is
+ dictated by the SCSI layer and is not affected by this value.
+
+ Default: 15000 (15 seconds)
+
+config AIC7XXX_PROBE_EISA_VL
+ bool "Probe for EISA and VL AIC7XXX Adapters"
+ depends on SCSI_AIC7XXX && EISA
+ help
+ Probe for EISA and VLB Aic7xxx controllers. In many newer systems,
+ the invasive probes necessary to detect these controllers can cause
+ other devices to fail. For this reason, the non-PCI probe code is
+ disabled by default. The current value of this option can be "toggled"
+ via the no_probe kernel command line option.
+
+config AIC7XXX_BUILD_FIRMWARE
+ bool "Build Adapter Firmware with Kernel Build"
+ depends on SCSI_AIC7XXX && !PREVENT_FIRMWARE_BUILD
+ help
+ This option should only be enabled if you are modifying the firmware
+ source to the aic7xxx driver and wish to have the generated firmware
+ include files updated during a normal kernel build. The assembler
+ for the firmware requires lex and yacc or their equivalents, as well
+ as the db v1 library. You may have to install additional packages
+ or modify the assembler Makefile or the files it includes if your
+ build environment is different than that of the author.
+
+config AIC7XXX_DEBUG_ENABLE
+ bool "Compile in Debugging Code"
+ depends on SCSI_AIC7XXX
+ default y
+ help
+ Compile in aic7xxx debugging code that can be useful in diagnosing
+ driver errors.
+
+config AIC7XXX_DEBUG_MASK
+ int "Debug code enable mask (2047 for all debugging)"
+ depends on SCSI_AIC7XXX
+ default "0"
+ help
+ Bit mask of debug options that is only valid if the
+ CONFIG_AIC7XXX_DEBUG_ENBLE option is enabled. The bits in this mask
+ are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the
+ variable ahc_debug in that file to find them.
+
+config AIC7XXX_REG_PRETTY_PRINT
+ bool "Decode registers during diagnostics"
+ depends on SCSI_AIC7XXX
+ default y
+ help
+ Compile in register value tables for the output of expanded register
+ contents in diagnostics. This make it much easier to understand debug
+ output without having to refer to a data book and/or the aic7xxx.reg
+ file.
diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
new file mode 100644
index 000000000000..9a6ce19a4030
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Makefile
@@ -0,0 +1,99 @@
+#
+# Makefile for the Linux aic7xxx SCSI driver.
+#
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Makefile#8 $
+#
+
+# Let kbuild descend into aicasm when cleaning
+subdir- += aicasm
+
+obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx.o
+obj-$(CONFIG_SCSI_AIC79XX) += aic79xx.o
+
+# Core Fast -> U160 files
+aic7xxx-y += aic7xxx_core.o \
+ aic7xxx_93cx6.o
+aic7xxx-$(CONFIG_EISA) += aic7770.o
+aic7xxx-$(CONFIG_PCI) += aic7xxx_pci.o
+aic7xxx-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) += aic7xxx_reg_print.o
+
+# Platform Specific Fast -> U160 Files
+aic7xxx-y += aic7xxx_osm.o \
+ aic7xxx_proc.o
+aic7xxx-$(CONFIG_EISA) += aic7770_osm.o
+aic7xxx-$(CONFIG_PCI) += aic7xxx_osm_pci.o
+
+# Core U320 files
+aic79xx-y += aic79xx_core.o \
+ aic79xx_pci.o
+aic79xx-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) += aic79xx_reg_print.o
+
+# Platform Specific U320 Files
+aic79xx-y += aic79xx_osm.o \
+ aic79xx_proc.o \
+ aic79xx_osm_pci.o
+
+EXTRA_CFLAGS += -Idrivers/scsi
+ifdef WARNINGS_BECOME_ERRORS
+EXTRA_CFLAGS += -Werror
+endif
+#EXTRA_CFLAGS += -g
+
+# Files generated that shall be removed upon make clean
+clean-files := aic7xxx_seq.h aic7xxx_reg.h aic7xxx_reg_print.c
+clean-files += aic79xx_seq.h aic79xx_reg.h aic79xx_reg_print.c
+
+# Dependencies for generated files need to be listed explicitly
+
+$(obj)/aic7xxx_core.o: $(obj)/aic7xxx_seq.h
+$(obj)/aic79xx_core.o: $(obj)/aic79xx_seq.h
+$(obj)/aic79xx_reg_print.c: $(src)/aic79xx_reg_print.c_shipped
+$(obj)/aic7xxx_reg_print.c: $(src)/aic7xxx_reg_print.c_shipped
+
+$(addprefix $(obj)/,$(aic7xxx-y)): $(obj)/aic7xxx_reg.h
+$(addprefix $(obj)/,$(aic79xx-y)): $(obj)/aic79xx_reg.h
+
+aic7xxx-gen-$(CONFIG_AIC7XXX_BUILD_FIRMWARE) := $(obj)/aic7xxx_seq.h \
+ $(obj)/aic7xxx_reg.h
+aic7xxx-gen-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) += $(obj)/aic7xxx_reg_print.c
+
+aicasm-7xxx-opts-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) := \
+ -p $(obj)/aic7xxx_reg_print.c -i aic7xxx_osm.h
+
+ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+aic7xxx_seq.h: aic7xxx_reg.h
+ifeq ($(CONFIG_AIC7XXX_REG_PRETTY_PRINT),y)
+aic7xxx_reg.h: aic7xxx_reg_print.c
+endif
+$(aic7xxx-gen-y): $(src)/aic7xxx.seq $(src)/aic7xxx.reg $(obj)/aicasm/aicasm
+ $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic7xxx_reg.h \
+ $(aicasm-7xxx-opts-y) -o $(obj)/aic7xxx_seq.h \
+ $(src)/aic7xxx.seq
+endif
+
+aic79xx-gen-$(CONFIG_AIC79XX_BUILD_FIRMWARE) := $(obj)/aic79xx_seq.h \
+ $(obj)/aic79xx_reg.h
+aic79xx-gen-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) += $(obj)/aic79xx_reg_print.c
+
+aicasm-79xx-opts-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) := \
+ -p $(obj)/aic79xx_reg_print.c -i aic79xx_osm.h
+
+ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y)
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+aic79xx_seq.h: aic79xx_reg.h
+ifeq ($(CONFIG_AIC79XX_REG_PRETTY_PRINT),y)
+aic79xx_reg.h: aic79xx_reg_print.c
+endif
+$(aic79xx-gen-y): $(src)/aic79xx.seq $(src)/aic79xx.reg $(obj)/aicasm/aicasm
+ $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic79xx_reg.h \
+ $(aicasm-79xx-opts-y) -o $(obj)/aic79xx_seq.h \
+ $(src)/aic79xx.seq
+endif
+
+$(obj)/aicasm/aicasm: $(src)/aicasm/*.[chyl]
+ $(MAKE) -C $(src)/aicasm
diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c
new file mode 100644
index 000000000000..92703bb35982
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7770.c
@@ -0,0 +1,415 @@
+/*
+ * Product specific probe and attach routines for:
+ * 27/284X and aic7770 motherboard SCSI controllers
+ *
+ * Copyright (c) 1994-1998, 2000, 2001 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7770.c#32 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define ID_AIC7770 0x04907770
+#define ID_AHA_274x 0x04907771
+#define ID_AHA_284xB 0x04907756 /* BIOS enabled */
+#define ID_AHA_284x 0x04907757 /* BIOS disabled*/
+#define ID_OLV_274x 0x04907782 /* Olivetti OEM */
+#define ID_OLV_274xD 0x04907783 /* Olivetti OEM (Differential) */
+
+static int aic7770_chip_init(struct ahc_softc *ahc);
+static int aic7770_suspend(struct ahc_softc *ahc);
+static int aic7770_resume(struct ahc_softc *ahc);
+static int aha2840_load_seeprom(struct ahc_softc *ahc);
+static ahc_device_setup_t ahc_aic7770_VL_setup;
+static ahc_device_setup_t ahc_aic7770_EISA_setup;
+static ahc_device_setup_t ahc_aic7770_setup;
+
+struct aic7770_identity aic7770_ident_table[] =
+{
+ {
+ ID_AHA_274x,
+ 0xFFFFFFFF,
+ "Adaptec 274X SCSI adapter",
+ ahc_aic7770_EISA_setup
+ },
+ {
+ ID_AHA_284xB,
+ 0xFFFFFFFE,
+ "Adaptec 284X SCSI adapter",
+ ahc_aic7770_VL_setup
+ },
+ {
+ ID_AHA_284x,
+ 0xFFFFFFFE,
+ "Adaptec 284X SCSI adapter (BIOS Disabled)",
+ ahc_aic7770_VL_setup
+ },
+ {
+ ID_OLV_274x,
+ 0xFFFFFFFF,
+ "Adaptec (Olivetti OEM) 274X SCSI adapter",
+ ahc_aic7770_EISA_setup
+ },
+ {
+ ID_OLV_274xD,
+ 0xFFFFFFFF,
+ "Adaptec (Olivetti OEM) 274X Differential SCSI adapter",
+ ahc_aic7770_EISA_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7770,
+ 0xFFFFFFFF,
+ "Adaptec aic7770 SCSI adapter",
+ ahc_aic7770_EISA_setup
+ }
+};
+const int ahc_num_aic7770_devs = NUM_ELEMENTS(aic7770_ident_table);
+
+struct aic7770_identity *
+aic7770_find_device(uint32_t id)
+{
+ struct aic7770_identity *entry;
+ int i;
+
+ for (i = 0; i < ahc_num_aic7770_devs; i++) {
+ entry = &aic7770_ident_table[i];
+ if (entry->full_id == (id & entry->id_mask))
+ return (entry);
+ }
+ return (NULL);
+}
+
+int
+aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io)
+{
+ u_long l;
+ int error;
+ int have_seeprom;
+ u_int hostconf;
+ u_int irq;
+ u_int intdef;
+
+ error = entry->setup(ahc);
+ have_seeprom = 0;
+ if (error != 0)
+ return (error);
+
+ error = aic7770_map_registers(ahc, io);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Before we continue probing the card, ensure that
+ * its interrupts are *disabled*. We don't want
+ * a misstep to hang the machine in an interrupt
+ * storm.
+ */
+ ahc_intr_enable(ahc, FALSE);
+
+ ahc->description = entry->name;
+ error = ahc_softc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ ahc->bus_chip_init = aic7770_chip_init;
+ ahc->bus_suspend = aic7770_suspend;
+ ahc->bus_resume = aic7770_resume;
+
+ error = ahc_reset(ahc, /*reinit*/FALSE);
+ if (error != 0)
+ return (error);
+
+ /* Make sure we have a valid interrupt vector */
+ intdef = ahc_inb(ahc, INTDEF);
+ irq = intdef & VECTOR;
+ switch (irq) {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
+ default:
+ printf("aic7770_config: invalid irq setting %d\n", intdef);
+ return (ENXIO);
+ }
+
+ if ((intdef & EDGE_TRIG) != 0)
+ ahc->flags |= AHC_EDGE_INTERRUPT;
+
+ switch (ahc->chip & (AHC_EISA|AHC_VL)) {
+ case AHC_EISA:
+ {
+ u_int biosctrl;
+ u_int scsiconf;
+ u_int scsiconf1;
+
+ biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
+ scsiconf = ahc_inb(ahc, SCSICONF);
+ scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
+
+ /* Get the primary channel information */
+ if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
+ ahc->flags |= 1;
+
+ if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
+ ahc->flags |= AHC_USEDEFAULTS;
+ } else {
+ if ((ahc->features & AHC_WIDE) != 0) {
+ ahc->our_id = scsiconf1 & HWSCSIID;
+ if (scsiconf & TERM_ENB)
+ ahc->flags |= AHC_TERM_ENB_A;
+ } else {
+ ahc->our_id = scsiconf & HSCSIID;
+ ahc->our_id_b = scsiconf1 & HSCSIID;
+ if (scsiconf & TERM_ENB)
+ ahc->flags |= AHC_TERM_ENB_A;
+ if (scsiconf1 & TERM_ENB)
+ ahc->flags |= AHC_TERM_ENB_B;
+ }
+ }
+ if ((ahc_inb(ahc, HA_274_BIOSGLOBAL) & HA_274_EXTENDED_TRANS))
+ ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
+ break;
+ }
+ case AHC_VL:
+ {
+ have_seeprom = aha2840_load_seeprom(ahc);
+ break;
+ }
+ default:
+ break;
+ }
+ if (have_seeprom == 0) {
+ free(ahc->seep_config, M_DEVBUF);
+ ahc->seep_config = NULL;
+ }
+
+ /*
+ * Ensure autoflush is enabled
+ */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+
+ /* Setup the FIFO threshold and the bus off time */
+ hostconf = ahc_inb(ahc, HOSTCONF);
+ ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
+ ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
+
+ ahc->bus_softc.aic7770_softc.busspd = hostconf & DFTHRSH;
+ ahc->bus_softc.aic7770_softc.bustime = (hostconf << 2) & BOFF;
+
+ /*
+ * Generic aic7xxx initialization.
+ */
+ error = ahc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ error = aic7770_map_int(ahc, irq);
+ if (error != 0)
+ return (error);
+
+ ahc_list_lock(&l);
+ /*
+ * Link this softc in with all other ahc instances.
+ */
+ ahc_softc_insert(ahc);
+
+ /*
+ * Enable the board's BUS drivers
+ */
+ ahc_outb(ahc, BCTL, ENABLE);
+
+ ahc_list_unlock(&l);
+
+ return (0);
+}
+
+static int
+aic7770_chip_init(struct ahc_softc *ahc)
+{
+ ahc_outb(ahc, BUSSPD, ahc->bus_softc.aic7770_softc.busspd);
+ ahc_outb(ahc, BUSTIME, ahc->bus_softc.aic7770_softc.bustime);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+ ahc_outb(ahc, BCTL, ENABLE);
+ return (ahc_chip_init(ahc));
+}
+
+static int
+aic7770_suspend(struct ahc_softc *ahc)
+{
+ return (ahc_suspend(ahc));
+}
+
+static int
+aic7770_resume(struct ahc_softc *ahc)
+{
+ return (ahc_resume(ahc));
+}
+
+/*
+ * Read the 284x SEEPROM.
+ */
+static int
+aha2840_load_seeprom(struct ahc_softc *ahc)
+{
+ struct seeprom_descriptor sd;
+ struct seeprom_config *sc;
+ int have_seeprom;
+ uint8_t scsi_conf;
+
+ sd.sd_ahc = ahc;
+ sd.sd_control_offset = SEECTL_2840;
+ sd.sd_status_offset = STATUS_2840;
+ sd.sd_dataout_offset = STATUS_2840;
+ sd.sd_chip = C46;
+ sd.sd_MS = 0;
+ sd.sd_RDY = EEPROM_TF;
+ sd.sd_CS = CS_2840;
+ sd.sd_CK = CK_2840;
+ sd.sd_DO = DO_2840;
+ sd.sd_DI = DI_2840;
+ sc = ahc->seep_config;
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahc_name(ahc));
+ have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+ /*start_addr*/0, sizeof(*sc)/2);
+
+ if (have_seeprom) {
+
+ if (ahc_verify_cksum(sc) == 0) {
+ if(bootverbose)
+ printf ("checksum error\n");
+ have_seeprom = 0;
+ } else if (bootverbose) {
+ printf("done.\n");
+ }
+ }
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available\n", ahc_name(ahc));
+ ahc->flags |= AHC_USEDEFAULTS;
+ } else {
+ /*
+ * Put the data we've collected down into SRAM
+ * where ahc_init will find it.
+ */
+ int i;
+ int max_targ;
+ uint16_t discenable;
+
+ max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
+ discenable = 0;
+ for (i = 0; i < max_targ; i++){
+ uint8_t target_settings;
+
+ target_settings = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFSYNCH)
+ target_settings |= SOFS;
+ if (sc->device_flags[i] & CFWIDEB)
+ target_settings |= WIDEXFER;
+ if (sc->device_flags[i] & CFDISC)
+ discenable |= (0x01 << i);
+ ahc_outb(ahc, TARG_SCSIRATE + i, target_settings);
+ }
+ ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+ ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+
+ ahc->our_id = sc->brtime_id & CFSCSIID;
+
+ scsi_conf = (ahc->our_id & 0x7);
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc->adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ if (sc->bios_control & CF284XEXTEND)
+ ahc->flags |= AHC_EXTENDED_TRANS_A;
+ /* Set SCSICONF info */
+ ahc_outb(ahc, SCSICONF, scsi_conf);
+
+ if (sc->adapter_control & CF284XSTERM)
+ ahc->flags |= AHC_TERM_ENB_A;
+ }
+ return (have_seeprom);
+}
+
+static int
+ahc_aic7770_VL_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7770_setup(ahc);
+ ahc->chip |= AHC_VL;
+ return (error);
+}
+
+static int
+ahc_aic7770_EISA_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7770_setup(ahc);
+ ahc->chip |= AHC_EISA;
+ return (error);
+}
+
+static int
+ahc_aic7770_setup(struct ahc_softc *ahc)
+{
+ ahc->channel = 'A';
+ ahc->channel_b = 'B';
+ ahc->chip = AHC_AIC7770;
+ ahc->features = AHC_AIC7770_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+ ahc->flags |= AHC_PAGESCBS;
+ ahc->instruction_ram_size = 448;
+ return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c
new file mode 100644
index 000000000000..c2b47f2bdffd
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -0,0 +1,264 @@
+/*
+ * Linux driver attachment glue for aic7770 based controllers.
+ *
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7770_osm.c#14 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/device.h>
+#include <linux/eisa.h>
+
+#define EISA_MFCTR_CHAR0(ID) (char)(((ID>>26) & 0x1F) | '@') /* Bits 26-30 */
+#define EISA_MFCTR_CHAR1(ID) (char)(((ID>>21) & 0x1F) | '@') /* Bits 21-25 */
+#define EISA_MFCTR_CHAR2(ID) (char)(((ID>>16) & 0x1F) | '@') /* Bits 16-20 */
+#define EISA_PRODUCT_ID(ID) (short)((ID>>4) & 0xFFF) /* Bits 4-15 */
+#define EISA_REVISION_ID(ID) (uint8_t)(ID & 0x0F) /* Bits 0-3 */
+
+static int aic7770_eisa_dev_probe(struct device *dev);
+static int aic7770_eisa_dev_remove(struct device *dev);
+static struct eisa_driver aic7770_driver = {
+ .driver = {
+ .name = "aic7xxx",
+ .probe = aic7770_eisa_dev_probe,
+ .remove = aic7770_eisa_dev_remove,
+ }
+};
+
+typedef struct device *aic7770_dev_t;
+#else
+#define MINSLOT 1
+#define NUMSLOTS 16
+#define IDOFFSET 0x80
+
+typedef void *aic7770_dev_t;
+#endif
+
+static int aic7770_linux_config(struct aic7770_identity *entry,
+ aic7770_dev_t dev, u_int eisaBase);
+
+int
+ahc_linux_eisa_init(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ struct eisa_device_id *eid;
+ struct aic7770_identity *id;
+ int i;
+
+ if (aic7xxx_probe_eisa_vl == 0)
+ return -ENODEV;
+
+ /*
+ * Linux requires the EISA IDs to be specified in
+ * the EISA ID string format. Perform the conversion
+ * and setup a table with a NUL terminal entry.
+ */
+ aic7770_driver.id_table = malloc(sizeof(struct eisa_device_id) *
+ (ahc_num_aic7770_devs + 1),
+ M_DEVBUF, M_NOWAIT);
+ if (aic7770_driver.id_table == NULL)
+ return -ENOMEM;
+
+ for (eid = (struct eisa_device_id *)aic7770_driver.id_table,
+ id = aic7770_ident_table, i = 0;
+ i < ahc_num_aic7770_devs; eid++, id++, i++) {
+
+ sprintf(eid->sig, "%c%c%c%03X%01X",
+ EISA_MFCTR_CHAR0(id->full_id),
+ EISA_MFCTR_CHAR1(id->full_id),
+ EISA_MFCTR_CHAR2(id->full_id),
+ EISA_PRODUCT_ID(id->full_id),
+ EISA_REVISION_ID(id->full_id));
+ eid->driver_data = i;
+ }
+ eid->sig[0] = 0;
+
+ return eisa_driver_register(&aic7770_driver);
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) */
+ struct aic7770_identity *entry;
+ u_int slot;
+ u_int eisaBase;
+ u_int i;
+ int ret = -ENODEV;
+
+ if (aic7xxx_probe_eisa_vl == 0)
+ return ret;
+
+ eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
+ for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
+ uint32_t eisa_id;
+ size_t id_size;
+
+ if (request_region(eisaBase, AHC_EISA_IOSIZE, "aic7xxx") == 0)
+ continue;
+
+ eisa_id = 0;
+ id_size = sizeof(eisa_id);
+ for (i = 0; i < 4; i++) {
+ /* VLcards require priming*/
+ outb(0x80 + i, eisaBase + IDOFFSET);
+ eisa_id |= inb(eisaBase + IDOFFSET + i)
+ << ((id_size-i-1) * 8);
+ }
+ release_region(eisaBase, AHC_EISA_IOSIZE);
+ if (eisa_id & 0x80000000)
+ continue; /* no EISA card in slot */
+
+ entry = aic7770_find_device(eisa_id);
+ if (entry != NULL) {
+ aic7770_linux_config(entry, NULL, eisaBase);
+ ret = 0;
+ }
+ }
+ return ret;
+#endif
+}
+
+void
+ahc_linux_eisa_exit(void)
+{
+ if(aic7xxx_probe_eisa_vl != 0 && aic7770_driver.id_table != NULL) {
+ eisa_driver_unregister(&aic7770_driver);
+ free(aic7770_driver.id_table, M_DEVBUF);
+ }
+}
+
+static int
+aic7770_linux_config(struct aic7770_identity *entry, aic7770_dev_t dev,
+ u_int eisaBase)
+{
+ struct ahc_softc *ahc;
+ char buf[80];
+ char *name;
+ int error;
+
+ /*
+ * Allocate a softc for this card and
+ * set it up for attachment by our
+ * common detect routine.
+ */
+ sprintf(buf, "ahc_eisa:%d", eisaBase >> 12);
+ name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (name == NULL)
+ return (ENOMEM);
+ strcpy(name, buf);
+ ahc = ahc_alloc(&aic7xxx_driver_template, name);
+ if (ahc == NULL)
+ return (ENOMEM);
+ error = aic7770_config(ahc, entry, eisaBase);
+ if (error != 0) {
+ ahc->bsh.ioport = 0;
+ ahc_free(ahc);
+ return (error);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ dev->driver_data = (void *)ahc;
+ if (aic7xxx_detect_complete)
+ error = ahc_linux_register_host(ahc, &aic7xxx_driver_template);
+#endif
+ return (error);
+}
+
+int
+aic7770_map_registers(struct ahc_softc *ahc, u_int port)
+{
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ if (request_region(port, AHC_EISA_IOSIZE, "aic7xxx") == 0)
+ return (ENOMEM);
+ ahc->tag = BUS_SPACE_PIO;
+ ahc->bsh.ioport = port;
+ return (0);
+}
+
+int
+aic7770_map_int(struct ahc_softc *ahc, u_int irq)
+{
+ int error;
+ int shared;
+
+ shared = 0;
+ if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0)
+ shared = SA_SHIRQ;
+
+ error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc);
+ if (error == 0)
+ ahc->platform_data->irq = irq;
+
+ return (-error);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int
+aic7770_eisa_dev_probe(struct device *dev)
+{
+ struct eisa_device *edev;
+
+ edev = to_eisa_device(dev);
+ return (aic7770_linux_config(aic7770_ident_table + edev->id.driver_data,
+ dev, edev->base_addr+AHC_EISA_SLOT_OFFSET));
+}
+
+static int
+aic7770_eisa_dev_remove(struct device *dev)
+{
+ struct ahc_softc *ahc;
+ u_long l;
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahc_list_lock(&l);
+ ahc = ahc_find_softc((struct ahc_softc *)dev->driver_data);
+ if (ahc != NULL) {
+ u_long s;
+
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, FALSE);
+ ahc_unlock(ahc, &s);
+ ahc_free(ahc);
+ }
+ ahc_list_unlock(&l);
+
+ return (0);
+}
+#endif
diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h
new file mode 100644
index 000000000000..fd4b2f3eb0c2
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.h
@@ -0,0 +1,1537 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#95 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_H_
+#define _AIC79XX_H_
+
+/* Register Definitions */
+#include "aic79xx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahd_platform_data;
+struct scb_platform_data;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD (~0)
+#define SCB_LIST_NULL 0xFF00
+#define SCB_LIST_NULL_LE (ahd_htole16(SCB_LIST_NULL))
+#define QOUTFIFO_ENTRY_VALID 0x8000
+#define QOUTFIFO_ENTRY_VALID_LE (ahd_htole16(0x8000))
+#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
+
+#define SCSIID_TARGET(ahd, scsiid) \
+ (((scsiid) & TID) >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+ ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahd, scsiid) ('A')
+#define SCB_IS_SCSIBUS_B(ahd, scb) (0)
+#define SCB_GET_OUR_ID(scb) \
+ SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define SCB_GET_TARGET(ahd, scb) \
+ SCSIID_TARGET((ahd), (scb)->hscb->scsiid)
+#define SCB_GET_CHANNEL(ahd, scb) \
+ SCSIID_CHANNEL(ahd, (scb)->hscb->scsiid)
+#define SCB_GET_LUN(scb) \
+ ((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahd, scb) \
+ SCB_GET_TARGET(ahd, scb)
+#define SCB_GET_TARGET_MASK(ahd, scb) \
+ (0x01 << (SCB_GET_TARGET_OFFSET(ahd, scb)))
+#ifdef AHD_DEBUG
+#define SCB_IS_SILENT(scb) \
+ ((ahd_debug & AHD_SHOW_MASKED_ERRORS) == 0 \
+ && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb) \
+ (((scb)->flags & SCB_SILENT) != 0)
+#endif
+/*
+ * TCLs have the following format: TTTTLLLLLLLL
+ */
+#define TCL_TARGET_OFFSET(tcl) \
+ ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+ (tcl & (AHD_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+ ((lun) | (((scsiid) & TID) << 4))
+#define BUILD_TCL_RAW(target, channel, lun) \
+ ((lun) | ((target) << 8))
+
+#define SCB_GET_TAG(scb) \
+ ahd_le16toh(scb->hscb->tag)
+
+#ifndef AHD_TARGET_MODE
+#undef AHD_TMODE_ENABLE
+#define AHD_TMODE_ENABLE 0
+#endif
+
+#define AHD_BUILD_COL_IDX(target, lun) \
+ (((lun) << 4) | target)
+
+#define AHD_GET_SCB_COL_IDX(ahd, scb) \
+ ((SCB_GET_LUN(scb) << 4) | SCB_GET_TARGET(ahd, scb))
+
+#define AHD_SET_SCB_COL_IDX(scb, col_idx) \
+do { \
+ (scb)->hscb->scsiid = ((col_idx) << TID_SHIFT) & TID; \
+ (scb)->hscb->lun = ((col_idx) >> 4) & (AHD_NUM_LUNS_NONPKT-1); \
+} while (0)
+
+#define AHD_COPY_SCB_COL_IDX(dst, src) \
+do { \
+ dst->hscb->scsiid = src->hscb->scsiid; \
+ dst->hscb->lun = src->hscb->lun; \
+} while (0)
+
+#define AHD_NEVER_COL_IDX 0xFFFF
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHD_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in non-packetized transfers.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but until we see a need to support that many, we support 256.
+ */
+#define AHD_NUM_LUNS_NONPKT 64
+#define AHD_NUM_LUNS 256
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHD_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound. Due to software design,
+ * we may not be able to use this number.
+ */
+#define AHD_SCB_MAX 512
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.
+ */
+#define AHD_MAX_QUEUE AHD_SCB_MAX
+
+/*
+ * Define the size of our QIN and QOUT FIFOs. They must be a power of 2
+ * in size and accommodate as many transactions as can be queued concurrently.
+ */
+#define AHD_QIN_SIZE AHD_MAX_QUEUE
+#define AHD_QOUT_SIZE AHD_MAX_QUEUE
+
+#define AHD_QIN_WRAP(x) ((x) & (AHD_QIN_SIZE-1))
+/*
+ * The maximum amount of SCB storage we allocate in host memory.
+ */
+#define AHD_SCB_MAX_ALLOC AHD_MAX_QUEUE
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHD_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHD_BUSRESET_DELAY 25
+
+/******************* Chip Characteristics/Operating Settings *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+ AHD_NONE = 0x0000,
+ AHD_CHIPID_MASK = 0x00FF,
+ AHD_AIC7901 = 0x0001,
+ AHD_AIC7902 = 0x0002,
+ AHD_AIC7901A = 0x0003,
+ AHD_PCI = 0x0100, /* Bus type PCI */
+ AHD_PCIX = 0x0200, /* Bus type PCIX */
+ AHD_BUS_MASK = 0x0F00
+} ahd_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+ AHD_FENONE = 0x00000,
+ AHD_WIDE = 0x00001,/* Wide Channel */
+ AHD_MULTI_FUNC = 0x00100,/* Multi-Function/Channel Device */
+ AHD_TARGETMODE = 0x01000,/* Has tested target mode support */
+ AHD_MULTIROLE = 0x02000,/* Space for two roles at a time */
+ AHD_RTI = 0x04000,/* Retained Training Support */
+ AHD_NEW_IOCELL_OPTS = 0x08000,/* More Signal knobs in the IOCELL */
+ AHD_NEW_DFCNTRL_OPTS = 0x10000,/* SCSIENWRDIS bit */
+ AHD_FAST_CDB_DELIVERY = 0x20000,/* CDB acks released to Output Sync */
+ AHD_REMOVABLE = 0x00000,/* Hot-Swap supported - None so far*/
+ AHD_AIC7901_FE = AHD_FENONE,
+ AHD_AIC7901A_FE = AHD_FENONE,
+ AHD_AIC7902_FE = AHD_MULTI_FUNC
+} ahd_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+ AHD_BUGNONE = 0x0000,
+ /*
+ * Rev A hardware fails to update LAST/CURR/NEXTSCB
+ * correctly in certain packetized selection cases.
+ */
+ AHD_SENT_SCB_UPDATE_BUG = 0x0001,
+ /* The wrong SCB is accessed to check the abort pending bit. */
+ AHD_ABORT_LQI_BUG = 0x0002,
+ /* Packetized bitbucket crosses packet boundaries. */
+ AHD_PKT_BITBUCKET_BUG = 0x0004,
+ /* The selection timer runs twice as long as its setting. */
+ AHD_LONG_SETIMO_BUG = 0x0008,
+ /* The Non-LQ CRC error status is delayed until phase change. */
+ AHD_NLQICRC_DELAYED_BUG = 0x0010,
+ /* The chip must be reset for all outgoing bus resets. */
+ AHD_SCSIRST_BUG = 0x0020,
+ /* Some PCIX fields must be saved and restored across chip reset. */
+ AHD_PCIX_CHIPRST_BUG = 0x0040,
+ /* MMAPIO is not functional in PCI-X mode. */
+ AHD_PCIX_MMAPIO_BUG = 0x0080,
+ /* Reads to SCBRAM fail to reset the discard timer. */
+ AHD_PCIX_SCBRAM_RD_BUG = 0x0100,
+ /* Bug workarounds that can be disabled on non-PCIX busses. */
+ AHD_PCIX_BUG_MASK = AHD_PCIX_CHIPRST_BUG
+ | AHD_PCIX_MMAPIO_BUG
+ | AHD_PCIX_SCBRAM_RD_BUG,
+ /*
+ * LQOSTOP0 status set even for forced selections with ATN
+ * to perform non-packetized message delivery.
+ */
+ AHD_LQO_ATNO_BUG = 0x0200,
+ /* FIFO auto-flush does not always trigger. */
+ AHD_AUTOFLUSH_BUG = 0x0400,
+ /* The CLRLQO registers are not self-clearing. */
+ AHD_CLRLQO_AUTOCLR_BUG = 0x0800,
+ /* The PACKETIZED status bit refers to the previous connection. */
+ AHD_PKTIZED_STATUS_BUG = 0x1000,
+ /* "Short Luns" are not placed into outgoing LQ packets correctly. */
+ AHD_PKT_LUN_BUG = 0x2000,
+ /*
+ * Only the FIFO allocated to the non-packetized connection may
+ * be in use during a non-packetzied connection.
+ */
+ AHD_NONPACKFIFO_BUG = 0x4000,
+ /*
+ * Writing to a DFF SCBPTR register may fail if concurent with
+ * a hardware write to the other DFF SCBPTR register. This is
+ * not currently a concern in our sequencer since all chips with
+ * this bug have the AHD_NONPACKFIFO_BUG and all writes of concern
+ * occur in non-packetized connections.
+ */
+ AHD_MDFF_WSCBPTR_BUG = 0x8000,
+ /* SGHADDR updates are slow. */
+ AHD_REG_SLOW_SETTLE_BUG = 0x10000,
+ /*
+ * Changing the MODE_PTR coincident with an interrupt that
+ * switches to a different mode will cause the interrupt to
+ * be in the mode written outside of interrupt context.
+ */
+ AHD_SET_MODE_BUG = 0x20000,
+ /* Non-packetized busfree revision does not work. */
+ AHD_BUSFREEREV_BUG = 0x40000,
+ /*
+ * Paced transfers are indicated with a non-standard PPR
+ * option bit in the neg table, 160MHz is indicated by
+ * sync factor 0x7, and the offset if off by a factor of 2.
+ */
+ AHD_PACED_NEGTABLE_BUG = 0x80000,
+ /* LQOOVERRUN false positives. */
+ AHD_LQOOVERRUN_BUG = 0x100000,
+ /*
+ * Controller write to INTSTAT will lose to a host
+ * write to CLRINT.
+ */
+ AHD_INTCOLLISION_BUG = 0x200000,
+ /*
+ * The GEM318 violates the SCSI spec by not waiting
+ * the mandated bus settle delay between phase changes
+ * in some situations. Some aic79xx chip revs. are more
+ * strict in this regard and will treat REQ assertions
+ * that fall within the bus settle delay window as
+ * glitches. This flag tells the firmware to tolerate
+ * early REQ assertions.
+ */
+ AHD_EARLY_REQ_BUG = 0x400000,
+ /*
+ * The LED does not stay on long enough in packetized modes.
+ */
+ AHD_FAINT_LED_BUG = 0x800000
+} ahd_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+ AHD_FNONE = 0x00000,
+ AHD_BOOT_CHANNEL = 0x00001,/* We were set as the boot channel. */
+ AHD_USEDEFAULTS = 0x00004,/*
+ * For cards without an seeprom
+ * or a BIOS to initialize the chip's
+ * SRAM, we use the default target
+ * settings.
+ */
+ AHD_SEQUENCER_DEBUG = 0x00008,
+ AHD_RESET_BUS_A = 0x00010,
+ AHD_EXTENDED_TRANS_A = 0x00020,
+ AHD_TERM_ENB_A = 0x00040,
+ AHD_SPCHK_ENB_A = 0x00080,
+ AHD_STPWLEVEL_A = 0x00100,
+ AHD_INITIATORROLE = 0x00200,/*
+ * Allow initiator operations on
+ * this controller.
+ */
+ AHD_TARGETROLE = 0x00400,/*
+ * Allow target operations on this
+ * controller.
+ */
+ AHD_RESOURCE_SHORTAGE = 0x00800,
+ AHD_TQINFIFO_BLOCKED = 0x01000,/* Blocked waiting for ATIOs */
+ AHD_INT50_SPEEDFLEX = 0x02000,/*
+ * Internal 50pin connector
+ * sits behind an aic3860
+ */
+ AHD_BIOS_ENABLED = 0x04000,
+ AHD_ALL_INTERRUPTS = 0x08000,
+ AHD_39BIT_ADDRESSING = 0x10000,/* Use 39 bit addressing scheme. */
+ AHD_64BIT_ADDRESSING = 0x20000,/* Use 64 bit addressing scheme. */
+ AHD_CURRENT_SENSING = 0x40000,
+ AHD_SCB_CONFIG_USED = 0x80000,/* No SEEPROM but SCB had info. */
+ AHD_HP_BOARD = 0x100000,
+ AHD_RESET_POLL_ACTIVE = 0x200000,
+ AHD_UPDATE_PEND_CMDS = 0x400000,
+ AHD_RUNNING_QOUTFIFO = 0x800000,
+ AHD_HAD_FIRST_SEL = 0x1000000
+} ahd_flag;
+
+/************************* Hardware SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
+ * consists of a "hardware SCB" mirroring the fields available on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the cdb) to be executed. After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role. See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target. The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally.
+ */
+struct initiator_status {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sgptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* Standard SCSI status byte */
+};
+
+struct target_status {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sgptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+/*
+ * Initiator mode SCB shared data area.
+ * If the embedded CDB is 12 bytes or less, we embed
+ * the sense buffer address in the SCB. This allows
+ * us to retrieve sense information without interrupting
+ * the host in packetized mode.
+ */
+typedef uint32_t sense_addr_t;
+#define MAX_CDB_LEN 16
+#define MAX_CDB_LEN_WITH_SENSE_ADDR (MAX_CDB_LEN - sizeof(sense_addr_t))
+union initiator_data {
+ struct {
+ uint64_t cdbptr;
+ uint8_t cdblen;
+ } cdb_from_host;
+ uint8_t cdb[MAX_CDB_LEN];
+ struct {
+ uint8_t cdb[MAX_CDB_LEN_WITH_SENSE_ADDR];
+ sense_addr_t sense_addr;
+ } cdb_plus_saddr;
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+ uint32_t spare[2];
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/ union {
+ union initiator_data idata;
+ struct target_data tdata;
+ struct initiator_status istatus;
+ struct target_status tstatus;
+ } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer. The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag. Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields). If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur. Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields. After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced. After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set. If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete. If the
+ * sequencer determines that there is a residual in the tranfer, or
+ * there is non-zero status, it will set the SG_STATUS_VALID flag in
+ * sgptr and dma the scb back into host memory. To sumarize:
+ *
+ * Sequencer:
+ * o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ * or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ * o We are transfering the last segment if residual_datacnt has
+ * the SG_LAST_SEG flag set.
+ *
+ * Host:
+ * o A residual can only have occurred if a completed scb has the
+ * SG_STATUS_VALID flag set. Inspection of the SCSI status field,
+ * the residual_datacnt, and the residual_sgptr field will tell
+ * for sure.
+ *
+ * o residual_sgptr and sgptr refer to the "next" sg entry
+ * and so may point beyond the last valid sg entry for the
+ * transfer.
+ */
+#define SG_PTR_MASK 0xFFFFFFF8
+/*16*/ uint16_t tag; /* Reused by Sequencer. */
+/*18*/ uint8_t control; /* See SCB_CONTROL in aic79xx.reg for details */
+/*19*/ uint8_t scsiid; /*
+ * Selection out Id
+ * Our Id (bits 0-3) Their ID (bits 4-7)
+ */
+/*20*/ uint8_t lun;
+/*21*/ uint8_t task_attribute;
+/*22*/ uint8_t cdb_len;
+/*23*/ uint8_t task_management;
+/*24*/ uint64_t dataptr;
+/*32*/ uint32_t datacnt; /* Byte 3 is spare. */
+/*36*/ uint32_t sgptr;
+/*40*/ uint32_t hscb_busaddr;
+/*44*/ uint32_t next_hscb_busaddr;
+/********** Long lun field only downloaded for full 8 byte lun support ********/
+/*48*/ uint8_t pkt_long_lun[8];
+/******* Fields below are not Downloaded (Sequencer may use for scratch) ******/
+/*56*/ uint8_t spare[8];
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent. Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length. We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer. This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahd_dma_seg {
+ uint32_t addr;
+ uint32_t len;
+#define AHD_DMA_LAST_SEG 0x80000000
+#define AHD_SG_HIGH_ADDR_MASK 0x7F000000
+#define AHD_SG_LEN_MASK 0x00FFFFFF
+};
+
+struct ahd_dma64_seg {
+ uint64_t addr;
+ uint32_t len;
+ uint32_t pad;
+};
+
+struct map_node {
+ bus_dmamap_t dmamap;
+ dma_addr_t physaddr;
+ uint8_t *vaddr;
+ SLIST_ENTRY(map_node) links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+ SCB_FLAG_NONE = 0x00000,
+ SCB_TRANSMISSION_ERROR = 0x00001,/*
+ * We detected a parity or CRC
+ * error that has effected the
+ * payload of the command. This
+ * flag is checked when normal
+ * status is returned to catch
+ * the case of a target not
+ * responding to our attempt
+ * to report the error.
+ */
+ SCB_OTHERTCL_TIMEOUT = 0x00002,/*
+ * Another device was active
+ * during the first timeout for
+ * this SCB so we gave ourselves
+ * an additional timeout period
+ * in case it was hogging the
+ * bus.
+ */
+ SCB_DEVICE_RESET = 0x00004,
+ SCB_SENSE = 0x00008,
+ SCB_CDB32_PTR = 0x00010,
+ SCB_RECOVERY_SCB = 0x00020,
+ SCB_AUTO_NEGOTIATE = 0x00040,/* Negotiate to achieve goal. */
+ SCB_NEGOTIATE = 0x00080,/* Negotiation forced for command. */
+ SCB_ABORT = 0x00100,
+ SCB_ACTIVE = 0x00200,
+ SCB_TARGET_IMMEDIATE = 0x00400,
+ SCB_PACKETIZED = 0x00800,
+ SCB_EXPECT_PPR_BUSFREE = 0x01000,
+ SCB_PKT_SENSE = 0x02000,
+ SCB_CMDPHASE_ABORT = 0x04000,
+ SCB_ON_COL_LIST = 0x08000,
+ SCB_SILENT = 0x10000 /*
+ * Be quiet about transmission type
+ * errors. They are expected and we
+ * don't want to upset the user. This
+ * flag is typically used during DV.
+ */
+} scb_flag;
+
+struct scb {
+ struct hardware_scb *hscb;
+ union {
+ SLIST_ENTRY(scb) sle;
+ LIST_ENTRY(scb) le;
+ TAILQ_ENTRY(scb) tqe;
+ } links;
+ union {
+ SLIST_ENTRY(scb) sle;
+ LIST_ENTRY(scb) le;
+ TAILQ_ENTRY(scb) tqe;
+ } links2;
+#define pending_links links2.le
+#define collision_links links2.le
+ struct scb *col_scb;
+ ahd_io_ctx_t io_ctx;
+ struct ahd_softc *ahd_softc;
+ scb_flag flags;
+#ifndef __linux__
+ bus_dmamap_t dmamap;
+#endif
+ struct scb_platform_data *platform_data;
+ struct map_node *hscb_map;
+ struct map_node *sg_map;
+ struct map_node *sense_map;
+ void *sg_list;
+ uint8_t *sense_data;
+ dma_addr_t sg_list_busaddr;
+ dma_addr_t sense_busaddr;
+ u_int sg_count;/* How full ahd_dma_seg is */
+#define AHD_MAX_LQ_CRC_ERRORS 5
+ u_int crc_retry_count;
+};
+
+TAILQ_HEAD(scb_tailq, scb);
+LIST_HEAD(scb_list, scb);
+
+struct scb_data {
+ /*
+ * TAILQ of lists of free SCBs grouped by device
+ * collision domains.
+ */
+ struct scb_tailq free_scbs;
+
+ /*
+ * Per-device lists of SCBs whose tag ID would collide
+ * with an already active tag on the device.
+ */
+ struct scb_list free_scb_lists[AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT];
+
+ /*
+ * SCBs that will not collide with any active device.
+ */
+ struct scb_list any_dev_free_scb_list;
+
+ /*
+ * Mapping from tag to SCB.
+ */
+ struct scb *scbindex[AHD_SCB_MAX];
+
+ /*
+ * "Bus" addresses of our data structures.
+ */
+ bus_dma_tag_t hscb_dmat; /* dmat for our hardware SCB array */
+ bus_dma_tag_t sg_dmat; /* dmat for our sg segments */
+ bus_dma_tag_t sense_dmat; /* dmat for our sense buffers */
+ SLIST_HEAD(, map_node) hscb_maps;
+ SLIST_HEAD(, map_node) sg_maps;
+ SLIST_HEAD(, map_node) sense_maps;
+ int scbs_left; /* unallocated scbs in head map_node */
+ int sgs_left; /* unallocated sgs in head map_node */
+ int sense_left; /* unallocated sense in head map_node */
+ uint16_t numscbs;
+ uint16_t maxhscbs; /* Number of SCBs on the card */
+ uint8_t init_level; /*
+ * How far we've initialized
+ * this structure.
+ */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+ uint8_t scsiid; /* Our ID and the initiator's ID */
+ uint8_t identify; /* Identify message */
+ uint8_t bytes[22]; /*
+ * Bytes contains any additional message
+ * bytes terminated by 0xFF. The remainder
+ * is the cdb to execute.
+ */
+ uint8_t cmd_valid; /*
+ * When a command is complete, the firmware
+ * will set cmd_valid to all bits set.
+ * After the host has seen the command,
+ * the bits are cleared. This allows us
+ * to just peek at host memory to determine
+ * if more work is complete. cmd_valid is on
+ * an 8 byte boundary to simplify setting
+ * it on aic7880 hardware which only has
+ * limited direct access to the DMA FIFO.
+ */
+ uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHD_TMODE_EVENT_BUFFER_SIZE 8
+struct ahd_tmode_event {
+ uint8_t initiator_id;
+ uint8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */
+#define EVENT_TYPE_BUS_RESET 0xFF
+ uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it. Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHD_TARGET_MODE
+struct ahd_tmode_lstate {
+ struct cam_path *path;
+ struct ccb_hdr_slist accept_tios;
+ struct ccb_hdr_slist immed_notifies;
+ struct ahd_tmode_event event_buffer[AHD_TMODE_EVENT_BUFFER_SIZE];
+ uint8_t event_r_idx;
+ uint8_t event_w_idx;
+};
+#else
+struct ahd_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHD_TRANS_CUR 0x01 /* Modify current neogtiation status */
+#define AHD_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */
+#define AHD_TRANS_GOAL 0x04 /* Modify negotiation goal */
+#define AHD_TRANS_USER 0x08 /* Modify user negotiation settings */
+#define AHD_PERIOD_10MHz 0x19
+
+#define AHD_WIDTH_UNKNOWN 0xFF
+#define AHD_PERIOD_UNKNOWN 0xFF
+#define AHD_OFFSET_UNKNOWN 0xFF
+#define AHD_PPR_OPTS_UNKNOWN 0xFF
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahd_transinfo {
+ uint8_t protocol_version; /* SCSI Revision level */
+ uint8_t transport_version; /* SPI Revision level */
+ uint8_t width; /* Bus width */
+ uint8_t period; /* Sync rate factor */
+ uint8_t offset; /* Sync offset */
+ uint8_t ppr_options; /* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahd_initiator_tinfo {
+ struct ahd_transinfo curr;
+ struct ahd_transinfo goal;
+ struct ahd_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping. For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahd_tmode_tstate {
+ struct ahd_tmode_lstate* enabled_luns[AHD_NUM_LUNS];
+ struct ahd_initiator_tinfo transinfo[AHD_NUM_TARGETS];
+
+ /*
+ * Per initiator state bitmasks.
+ */
+ uint16_t auto_negotiate;/* Auto Negotiation Required */
+ uint16_t discenable; /* Disconnection allowed */
+ uint16_t tagenable; /* Tagged Queuing allowed */
+};
+
+/*
+ * Points of interest along the negotiated transfer scale.
+ */
+#define AHD_SYNCRATE_160 0x8
+#define AHD_SYNCRATE_PACED 0x8
+#define AHD_SYNCRATE_DT 0x9
+#define AHD_SYNCRATE_ULTRA2 0xa
+#define AHD_SYNCRATE_ULTRA 0xc
+#define AHD_SYNCRATE_FAST 0x19
+#define AHD_SYNCRATE_MIN_DT AHD_SYNCRATE_FAST
+#define AHD_SYNCRATE_SYNC 0x32
+#define AHD_SYNCRATE_MIN 0x60
+#define AHD_SYNCRATE_ASYNC 0xFF
+#define AHD_SYNCRATE_MAX AHD_SYNCRATE_160
+
+/* Safe and valid period for async negotiations. */
+#define AHD_ASYNC_XFER_PERIOD 0x44
+
+/*
+ * In RevA, the synctable uses a 120MHz rate for the period
+ * factor 8 and 160MHz for the period factor 7. The 120MHz
+ * rate never made it into the official SCSI spec, so we must
+ * compensate when setting the negotiation table for Rev A
+ * parts.
+ */
+#define AHD_SYNCRATE_REVA_120 0x8
+#define AHD_SYNCRATE_REVA_160 0x7
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table.
+ */
+struct ahd_phase_table_entry {
+ uint8_t phase;
+ uint8_t mesg_out; /* Message response to parity errors */
+ char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+ uint16_t device_flags[16]; /* words 0-15 */
+#define CFXFER 0x003F /* synchronous transfer rate */
+#define CFXFER_ASYNC 0x3F
+#define CFQAS 0x0040 /* Negotiate QAS */
+#define CFPACKETIZED 0x0080 /* Negotiate Packetized Transfers */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFDISC 0x0400 /* enable disconnection */
+#define CFMULTILUNDEV 0x0800 /* Probe multiple luns in BIOS scan */
+#define CFWIDEB 0x1000 /* wide bus device */
+#define CFHOSTMANAGED 0x8000 /* Managed by a RAID controller */
+
+/*
+ * BIOS Control Bits
+ */
+ uint16_t bios_control; /* word 16 */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable boot drives */
+#define CFBIOSSTATE 0x000C /* BIOS Action State */
+#define CFBS_DISABLED 0x00
+#define CFBS_ENABLED 0x04
+#define CFBS_DISABLED_SCAN 0x08
+#define CFENABLEDV 0x0010 /* Perform Domain Validation */
+#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
+#define CFSPARITY 0x0040 /* SCSI parity */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+#define CFBOOTCD 0x0100 /* Support Bootable CD-ROM */
+#define CFMSG_LEVEL 0x0600 /* BIOS Message Level */
+#define CFMSG_VERBOSE 0x0000
+#define CFMSG_SILENT 0x0200
+#define CFMSG_DIAG 0x0400
+#define CFRESETB 0x0800 /* reset SCSI bus at boot */
+/* UNUSED 0xf000 */
+
+/*
+ * Host Adapter Control Bits
+ */
+ uint16_t adapter_control; /* word 17 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFSTERM 0x0002 /* SCSI low byte termination */
+#define CFWSTERM 0x0004 /* SCSI high byte termination */
+#define CFSEAUTOTERM 0x0008 /* Ultra2 Perform secondary Auto Term*/
+#define CFSELOWTERM 0x0010 /* Ultra2 secondary low term */
+#define CFSEHIGHTERM 0x0020 /* Ultra2 secondary high term */
+#define CFSTPWLEVEL 0x0040 /* Termination level control */
+#define CFBIOSAUTOTERM 0x0080 /* Perform Auto termination */
+#define CFTERM_MENU 0x0100 /* BIOS displays termination menu */
+#define CFCLUSTERENB 0x8000 /* Cluster Enable */
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+ uint16_t brtime_id; /* word 18 */
+#define CFSCSIID 0x000f /* host adapter SCSI ID */
+/* UNUSED 0x00f0 */
+#define CFBRTIME 0xff00 /* bus release time/PCI Latency Time */
+
+/*
+ * Maximum targets
+ */
+ uint16_t max_targets; /* word 19 */
+#define CFMAXTARG 0x00ff /* maximum targets */
+#define CFBOOTLUN 0x0f00 /* Lun to boot from */
+#define CFBOOTID 0xf000 /* Target to boot from */
+ uint16_t res_1[10]; /* words 20-29 */
+ uint16_t signature; /* BIOS Signature */
+#define CFSIGNATURE 0x400
+ uint16_t checksum; /* word 31 */
+};
+
+/*
+ * Vital Product Data used during POST and by the BIOS.
+ */
+struct vpd_config {
+ uint8_t bios_flags;
+#define VPDMASTERBIOS 0x0001
+#define VPDBOOTHOST 0x0002
+ uint8_t reserved_1[21];
+ uint8_t resource_type;
+ uint8_t resource_len[2];
+ uint8_t resource_data[8];
+ uint8_t vpd_tag;
+ uint16_t vpd_len;
+ uint8_t vpd_keyword[2];
+ uint8_t length;
+ uint8_t revision;
+ uint8_t device_flags;
+ uint8_t termnation_menus[2];
+ uint8_t fifo_threshold;
+ uint8_t end_tag;
+ uint8_t vpd_checksum;
+ uint16_t default_target_flags;
+ uint16_t default_bios_flags;
+ uint16_t default_ctrl_flags;
+ uint8_t default_irq;
+ uint8_t pci_lattime;
+ uint8_t max_target;
+ uint8_t boot_lun;
+ uint16_t signature;
+ uint8_t reserved_2;
+ uint8_t checksum;
+ uint8_t reserved_3[4];
+};
+
+/****************************** Flexport Logic ********************************/
+#define FLXADDR_TERMCTL 0x0
+#define FLX_TERMCTL_ENSECHIGH 0x8
+#define FLX_TERMCTL_ENSECLOW 0x4
+#define FLX_TERMCTL_ENPRIHIGH 0x2
+#define FLX_TERMCTL_ENPRILOW 0x1
+#define FLXADDR_ROMSTAT_CURSENSECTL 0x1
+#define FLX_ROMSTAT_SEECFG 0xF0
+#define FLX_ROMSTAT_EECFG 0x0F
+#define FLX_ROMSTAT_SEE_93C66 0x00
+#define FLX_ROMSTAT_SEE_NONE 0xF0
+#define FLX_ROMSTAT_EE_512x8 0x0
+#define FLX_ROMSTAT_EE_1MBx8 0x1
+#define FLX_ROMSTAT_EE_2MBx8 0x2
+#define FLX_ROMSTAT_EE_4MBx8 0x3
+#define FLX_ROMSTAT_EE_16MBx8 0x4
+#define CURSENSE_ENB 0x1
+#define FLXADDR_FLEXSTAT 0x2
+#define FLX_FSTAT_BUSY 0x1
+#define FLXADDR_CURRENT_STAT 0x4
+#define FLX_CSTAT_SEC_HIGH 0xC0
+#define FLX_CSTAT_SEC_LOW 0x30
+#define FLX_CSTAT_PRI_HIGH 0x0C
+#define FLX_CSTAT_PRI_LOW 0x03
+#define FLX_CSTAT_MASK 0x03
+#define FLX_CSTAT_SHIFT 2
+#define FLX_CSTAT_OKAY 0x0
+#define FLX_CSTAT_OVER 0x1
+#define FLX_CSTAT_UNDER 0x2
+#define FLX_CSTAT_INVALID 0x3
+
+int ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count, int bstream);
+
+int ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int ahd_wait_seeprom(struct ahd_softc *ahd);
+int ahd_verify_vpd_cksum(struct vpd_config *vpd);
+int ahd_verify_cksum(struct seeprom_config *sc);
+int ahd_acquire_seeprom(struct ahd_softc *ahd);
+void ahd_release_seeprom(struct ahd_softc *ahd);
+
+/**************************** Message Buffer *********************************/
+typedef enum {
+ MSG_FLAG_NONE = 0x00,
+ MSG_FLAG_EXPECT_PPR_BUSFREE = 0x01,
+ MSG_FLAG_IU_REQ_CHANGED = 0x02,
+ MSG_FLAG_EXPECT_IDE_BUSFREE = 0x04,
+ MSG_FLAG_EXPECT_QASREJ_BUSFREE = 0x08,
+ MSG_FLAG_PACKETIZED = 0x10
+} ahd_msg_flags;
+
+typedef enum {
+ MSG_TYPE_NONE = 0x00,
+ MSG_TYPE_INITIATOR_MSGOUT = 0x01,
+ MSG_TYPE_INITIATOR_MSGIN = 0x02,
+ MSG_TYPE_TARGET_MSGOUT = 0x03,
+ MSG_TYPE_TARGET_MSGIN = 0x04
+} ahd_msg_type;
+
+typedef enum {
+ MSGLOOP_IN_PROG,
+ MSGLOOP_MSGCOMPLETE,
+ MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+struct ahd_suspend_channel_state {
+ uint8_t scsiseq;
+ uint8_t sxfrctl0;
+ uint8_t sxfrctl1;
+ uint8_t simode0;
+ uint8_t simode1;
+ uint8_t seltimer;
+ uint8_t seqctl;
+};
+
+struct ahd_suspend_state {
+ struct ahd_suspend_channel_state channel[2];
+ uint8_t optionmode;
+ uint8_t dscommand0;
+ uint8_t dspcistatus;
+ /* hsmailbox */
+ uint8_t crccontrol1;
+ uint8_t scbbaddr;
+ /* Host and sequencer SCB counts */
+ uint8_t dff_thrsh;
+ uint8_t *scratch_ram;
+ uint8_t *btt;
+};
+
+typedef void (*ahd_bus_intr_t)(struct ahd_softc *);
+
+typedef enum {
+ AHD_MODE_DFF0,
+ AHD_MODE_DFF1,
+ AHD_MODE_CCHAN,
+ AHD_MODE_SCSI,
+ AHD_MODE_CFG,
+ AHD_MODE_UNKNOWN
+} ahd_mode;
+
+#define AHD_MK_MSK(x) (0x01 << (x))
+#define AHD_MODE_DFF0_MSK AHD_MK_MSK(AHD_MODE_DFF0)
+#define AHD_MODE_DFF1_MSK AHD_MK_MSK(AHD_MODE_DFF1)
+#define AHD_MODE_CCHAN_MSK AHD_MK_MSK(AHD_MODE_CCHAN)
+#define AHD_MODE_SCSI_MSK AHD_MK_MSK(AHD_MODE_SCSI)
+#define AHD_MODE_CFG_MSK AHD_MK_MSK(AHD_MODE_CFG)
+#define AHD_MODE_UNKNOWN_MSK AHD_MK_MSK(AHD_MODE_UNKNOWN)
+#define AHD_MODE_ANY_MSK (~0)
+
+typedef uint8_t ahd_mode_state;
+
+typedef void ahd_callback_t (void *);
+
+struct ahd_softc {
+ bus_space_tag_t tags[2];
+ bus_space_handle_t bshs[2];
+#ifndef __linux__
+ bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
+#endif
+ struct scb_data scb_data;
+
+ struct hardware_scb *next_queued_hscb;
+
+ /*
+ * SCBs that have been sent to the controller
+ */
+ LIST_HEAD(, scb) pending_scbs;
+
+ /*
+ * Current register window mode information.
+ */
+ ahd_mode dst_mode;
+ ahd_mode src_mode;
+
+ /*
+ * Saved register window mode information
+ * used for restore on next unpause.
+ */
+ ahd_mode saved_dst_mode;
+ ahd_mode saved_src_mode;
+
+ /*
+ * Platform specific data.
+ */
+ struct ahd_platform_data *platform_data;
+
+ /*
+ * Platform specific device information.
+ */
+ ahd_dev_softc_t dev_softc;
+
+ /*
+ * Bus specific device information.
+ */
+ ahd_bus_intr_t bus_intr;
+
+ /*
+ * Target mode related state kept on a per enabled lun basis.
+ * Targets that are not enabled will have null entries.
+ * As an initiator, we keep one target entry for our initiator
+ * ID to store our sync/wide transfer settings.
+ */
+ struct ahd_tmode_tstate *enabled_targets[AHD_NUM_TARGETS];
+
+ /*
+ * The black hole device responsible for handling requests for
+ * disabled luns on enabled targets.
+ */
+ struct ahd_tmode_lstate *black_hole;
+
+ /*
+ * Device instance currently on the bus awaiting a continue TIO
+ * for a command that was not given the disconnect priveledge.
+ */
+ struct ahd_tmode_lstate *pending_device;
+
+ /*
+ * Timer handles for timer driven callbacks.
+ */
+ ahd_timer_t reset_timer;
+ ahd_timer_t stat_timer;
+
+ /*
+ * Statistics.
+ */
+#define AHD_STAT_UPDATE_US 250000 /* 250ms */
+#define AHD_STAT_BUCKETS 4
+ u_int cmdcmplt_bucket;
+ uint32_t cmdcmplt_counts[AHD_STAT_BUCKETS];
+ uint32_t cmdcmplt_total;
+
+ /*
+ * Card characteristics
+ */
+ ahd_chip chip;
+ ahd_feature features;
+ ahd_bug bugs;
+ ahd_flag flags;
+ struct seeprom_config *seep_config;
+
+ /* Values to store in the SEQCTL register for pause and unpause */
+ uint8_t unpause;
+ uint8_t pause;
+
+ /* Command Queues */
+ uint16_t qoutfifonext;
+ uint16_t qoutfifonext_valid_tag;
+ uint16_t qinfifonext;
+ uint16_t qinfifo[AHD_SCB_MAX];
+ uint16_t *qoutfifo;
+
+ /* Critical Section Data */
+ struct cs *critical_sections;
+ u_int num_critical_sections;
+
+ /* Buffer for handling packetized bitbucket. */
+ uint8_t *overrun_buf;
+
+ /* Links for chaining softcs */
+ TAILQ_ENTRY(ahd_softc) links;
+
+ /* Channel Names ('A', 'B', etc.) */
+ char channel;
+
+ /* Initiator Bus ID */
+ uint8_t our_id;
+
+ /*
+ * Target incoming command FIFO.
+ */
+ struct target_cmd *targetcmds;
+ uint8_t tqinfifonext;
+
+ /*
+ * Cached verson of the hs_mailbox so we can avoid
+ * pausing the sequencer during mailbox updates.
+ */
+ uint8_t hs_mailbox;
+
+ /*
+ * Incoming and outgoing message handling.
+ */
+ uint8_t send_msg_perror;
+ ahd_msg_flags msg_flags;
+ ahd_msg_type msg_type;
+ uint8_t msgout_buf[12];/* Message we are sending */
+ uint8_t msgin_buf[12];/* Message we are receiving */
+ u_int msgout_len; /* Length of message to send */
+ u_int msgout_index; /* Current index in msgout */
+ u_int msgin_index; /* Current index in msgin */
+
+ /*
+ * Mapping information for data structures shared
+ * between the sequencer and kernel.
+ */
+ bus_dma_tag_t parent_dmat;
+ bus_dma_tag_t shared_data_dmat;
+ bus_dmamap_t shared_data_dmamap;
+ dma_addr_t shared_data_busaddr;
+
+ /* Information saved through suspend/resume cycles */
+ struct ahd_suspend_state suspend_state;
+
+ /* Number of enabled target mode device on this card */
+ u_int enabled_luns;
+
+ /* Initialization level of this data structure */
+ u_int init_level;
+
+ /* PCI cacheline size. */
+ u_int pci_cachesize;
+
+ /* IO Cell Parameters */
+ uint8_t iocell_opts[AHD_NUM_PER_DEV_ANNEXCOLS];
+
+ u_int stack_size;
+ uint16_t *saved_stack;
+
+ /* Per-Unit descriptive information */
+ const char *description;
+ const char *bus_description;
+ char *name;
+ int unit;
+
+ /* Selection Timer settings */
+ int seltime;
+
+ /*
+ * Interrupt coalescing settings.
+ */
+#define AHD_INT_COALESCING_TIMER_DEFAULT 250 /*us*/
+#define AHD_INT_COALESCING_MAXCMDS_DEFAULT 10
+#define AHD_INT_COALESCING_MAXCMDS_MAX 127
+#define AHD_INT_COALESCING_MINCMDS_DEFAULT 5
+#define AHD_INT_COALESCING_MINCMDS_MAX 127
+#define AHD_INT_COALESCING_THRESHOLD_DEFAULT 2000
+#define AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT 1000
+ u_int int_coalescing_timer;
+ u_int int_coalescing_maxcmds;
+ u_int int_coalescing_mincmds;
+ u_int int_coalescing_threshold;
+ u_int int_coalescing_stop_threshold;
+
+ uint16_t user_discenable;/* Disconnection allowed */
+ uint16_t user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahd_softc_tailq, ahd_softc);
+extern struct ahd_softc_tailq ahd_tailq;
+
+/*************************** IO Cell Configuration ****************************/
+#define AHD_PRECOMP_SLEW_INDEX \
+ (AHD_ANNEXCOL_PRECOMP_SLEW - AHD_ANNEXCOL_PER_DEV0)
+
+#define AHD_AMPLITUDE_INDEX \
+ (AHD_ANNEXCOL_AMPLITUDE - AHD_ANNEXCOL_PER_DEV0)
+
+#define AHD_SET_SLEWRATE(ahd, new_slew) \
+do { \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_SLEWRATE_MASK; \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |= \
+ (((new_slew) << AHD_SLEWRATE_SHIFT) & AHD_SLEWRATE_MASK); \
+} while (0)
+
+#define AHD_SET_PRECOMP(ahd, new_pcomp) \
+do { \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK; \
+ (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |= \
+ (((new_pcomp) << AHD_PRECOMP_SHIFT) & AHD_PRECOMP_MASK); \
+} while (0)
+
+#define AHD_SET_AMPLITUDE(ahd, new_amp) \
+do { \
+ (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] &= ~AHD_AMPLITUDE_MASK; \
+ (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] |= \
+ (((new_amp) << AHD_AMPLITUDE_SHIFT) & AHD_AMPLITUDE_MASK); \
+} while (0)
+
+/************************ Active Device Information ***************************/
+typedef enum {
+ ROLE_UNKNOWN,
+ ROLE_INITIATOR,
+ ROLE_TARGET
+} role_t;
+
+struct ahd_devinfo {
+ int our_scsiid;
+ int target_offset;
+ uint16_t target_mask;
+ u_int target;
+ u_int lun;
+ char channel;
+ role_t role; /*
+ * Only guaranteed to be correct if not
+ * in the busfree state.
+ */
+};
+
+/****************************** PCI Structures ********************************/
+#define AHD_PCI_IOADDR0 PCIR_MAPS /* I/O BAR*/
+#define AHD_PCI_MEMADDR (PCIR_MAPS + 4) /* Memory BAR */
+#define AHD_PCI_IOADDR1 (PCIR_MAPS + 12)/* Second I/O BAR */
+
+typedef int (ahd_device_setup_t)(struct ahd_softc *);
+
+struct ahd_pci_identity {
+ uint64_t full_id;
+ uint64_t id_mask;
+ char *name;
+ ahd_device_setup_t *setup;
+};
+extern struct ahd_pci_identity ahd_pci_ident_table [];
+extern const u_int ahd_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+ uint32_t full_id;
+ uint32_t id_mask;
+ char *name;
+ ahd_device_setup_t *setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahd_num_aic7770_devs;
+
+#define AHD_EISA_SLOT_OFFSET 0xc00
+#define AHD_EISA_IOSIZE 0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+void ahd_reset_cmds_pending(struct ahd_softc *ahd);
+u_int ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl);
+void ahd_busy_tcl(struct ahd_softc *ahd,
+ u_int tcl, u_int busyid);
+static __inline void ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl);
+static __inline void
+ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+ ahd_busy_tcl(ahd, tcl, SCB_LIST_NULL);
+}
+
+/***************************** PCI Front End *********************************/
+struct ahd_pci_identity *ahd_find_pci_device(ahd_dev_softc_t);
+int ahd_pci_config(struct ahd_softc *,
+ struct ahd_pci_identity *);
+int ahd_pci_test_register_access(struct ahd_softc *);
+
+/************************** SCB and SCB queue management **********************/
+int ahd_probe_scbs(struct ahd_softc *);
+void ahd_qinfifo_requeue_tail(struct ahd_softc *ahd,
+ struct scb *scb);
+int ahd_match_scb(struct ahd_softc *ahd, struct scb *scb,
+ int target, char channel, int lun,
+ u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahd_softc *ahd_alloc(void *platform_arg, char *name);
+int ahd_softc_init(struct ahd_softc *);
+void ahd_controller_info(struct ahd_softc *ahd, char *buf);
+int ahd_init(struct ahd_softc *ahd);
+int ahd_default_config(struct ahd_softc *ahd);
+int ahd_parse_vpddata(struct ahd_softc *ahd,
+ struct vpd_config *vpd);
+int ahd_parse_cfgdata(struct ahd_softc *ahd,
+ struct seeprom_config *sc);
+void ahd_intr_enable(struct ahd_softc *ahd, int enable);
+void ahd_update_coalescing_values(struct ahd_softc *ahd,
+ u_int timer,
+ u_int maxcmds,
+ u_int mincmds);
+void ahd_enable_coalescing(struct ahd_softc *ahd,
+ int enable);
+void ahd_pause_and_flushwork(struct ahd_softc *ahd);
+int ahd_suspend(struct ahd_softc *ahd);
+int ahd_resume(struct ahd_softc *ahd);
+void ahd_softc_insert(struct ahd_softc *);
+struct ahd_softc *ahd_find_softc(struct ahd_softc *ahd);
+void ahd_set_unit(struct ahd_softc *, int);
+void ahd_set_name(struct ahd_softc *, char *);
+struct scb *ahd_get_scb(struct ahd_softc *ahd, u_int col_idx);
+void ahd_free_scb(struct ahd_softc *ahd, struct scb *scb);
+void ahd_alloc_scbs(struct ahd_softc *ahd);
+void ahd_free(struct ahd_softc *ahd);
+int ahd_reset(struct ahd_softc *ahd, int reinit);
+void ahd_shutdown(void *arg);
+int ahd_write_flexport(struct ahd_softc *ahd,
+ u_int addr, u_int value);
+int ahd_read_flexport(struct ahd_softc *ahd, u_int addr,
+ uint8_t *value);
+int ahd_wait_flexport(struct ahd_softc *ahd);
+
+/*************************** Interrupt Services *******************************/
+void ahd_pci_intr(struct ahd_softc *ahd);
+void ahd_clear_intstat(struct ahd_softc *ahd);
+void ahd_flush_qoutfifo(struct ahd_softc *ahd);
+void ahd_run_qoutfifo(struct ahd_softc *ahd);
+#ifdef AHD_TARGET_MODE
+void ahd_run_tqinfifo(struct ahd_softc *ahd, int paused);
+#endif
+void ahd_handle_hwerrint(struct ahd_softc *ahd);
+void ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat);
+void ahd_handle_scsiint(struct ahd_softc *ahd,
+ u_int intstat);
+void ahd_clear_critical_section(struct ahd_softc *ahd);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+ SEARCH_COMPLETE,
+ SEARCH_COUNT,
+ SEARCH_REMOVE,
+ SEARCH_PRINT
+} ahd_search_action;
+int ahd_search_qinfifo(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahd_search_action action);
+int ahd_search_disc_list(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ int stop_on_first, int remove,
+ int save_state);
+void ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+int ahd_reset_channel(struct ahd_softc *ahd, char channel,
+ int initiate_reset);
+int ahd_abort_scbs(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahd_restart(struct ahd_softc *ahd);
+void ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo);
+void ahd_handle_scb_status(struct ahd_softc *ahd,
+ struct scb *scb);
+void ahd_handle_scsi_status(struct ahd_softc *ahd,
+ struct scb *scb);
+void ahd_calc_residual(struct ahd_softc *ahd,
+ struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahd_phase_table_entry*
+ ahd_lookup_phase_entry(int phase);
+void ahd_compile_devinfo(struct ahd_devinfo *devinfo,
+ u_int our_id, u_int target,
+ u_int lun, char channel,
+ role_t role);
+/************************** Transfer Negotiation ******************************/
+void ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+ u_int *ppr_options, u_int maxsync);
+void ahd_validate_offset(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int period, u_int *offset,
+ int wide, role_t role);
+void ahd_validate_width(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int *bus_width,
+ role_t role);
+/*
+ * Negotiation types. These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+ AHD_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */
+ AHD_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */
+ AHD_NEG_ALWAYS /* Renegotiat even if goal is async. */
+} ahd_neg_type;
+int ahd_update_neg_request(struct ahd_softc*,
+ struct ahd_devinfo*,
+ struct ahd_tmode_tstate*,
+ struct ahd_initiator_tinfo*,
+ ahd_neg_type);
+void ahd_set_width(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int width, u_int type, int paused);
+void ahd_set_syncrate(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int ppr_options,
+ u_int type, int paused);
+typedef enum {
+ AHD_QUEUE_NONE,
+ AHD_QUEUE_BASIC,
+ AHD_QUEUE_TAGGED
+} ahd_queue_alg;
+
+void ahd_set_tags(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ ahd_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHD_TARGET_MODE
+void ahd_send_lstate_events(struct ahd_softc *,
+ struct ahd_tmode_lstate *);
+void ahd_handle_en_lun(struct ahd_softc *ahd,
+ struct cam_sim *sim, union ccb *ccb);
+cam_status ahd_find_tmode_devs(struct ahd_softc *ahd,
+ struct cam_sim *sim, union ccb *ccb,
+ struct ahd_tmode_tstate **tstate,
+ struct ahd_tmode_lstate **lstate,
+ int notfound_failure);
+#ifndef AHD_TMODE_ENABLE
+#define AHD_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHD_DEBUG
+extern uint32_t ahd_debug;
+#define AHD_SHOW_MISC 0x00001
+#define AHD_SHOW_SENSE 0x00002
+#define AHD_SHOW_RECOVERY 0x00004
+#define AHD_DUMP_SEEPROM 0x00008
+#define AHD_SHOW_TERMCTL 0x00010
+#define AHD_SHOW_MEMORY 0x00020
+#define AHD_SHOW_MESSAGES 0x00040
+#define AHD_SHOW_MODEPTR 0x00080
+#define AHD_SHOW_SELTO 0x00100
+#define AHD_SHOW_FIFOS 0x00200
+#define AHD_SHOW_QFULL 0x00400
+#define AHD_SHOW_DV 0x00800
+#define AHD_SHOW_MASKED_ERRORS 0x01000
+#define AHD_SHOW_QUEUE 0x02000
+#define AHD_SHOW_TQIN 0x04000
+#define AHD_SHOW_SG 0x08000
+#define AHD_SHOW_INT_COALESCING 0x10000
+#define AHD_DEBUG_SEQUENCER 0x20000
+#endif
+void ahd_print_scb(struct scb *scb);
+void ahd_print_devinfo(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+void ahd_dump_sglist(struct scb *scb);
+void ahd_dump_all_cards_state(void);
+void ahd_dump_card_state(struct ahd_softc *ahd);
+int ahd_print_register(ahd_reg_parse_entry_t *table,
+ u_int num_entries,
+ const char *name,
+ u_int address,
+ u_int value,
+ u_int *cur_column,
+ u_int wrap_point);
+void ahd_dump_scbs(struct ahd_softc *ahd);
+#endif /* _AIC79XX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx.reg b/drivers/scsi/aic7xxx/aic79xx.reg
new file mode 100644
index 000000000000..cca58edc8648
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.reg
@@ -0,0 +1,3958 @@
+/*
+ * Aic79xx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic79xx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic79xx driver.
+ */
+
+/* Register window Modes */
+#define M_DFF0 0
+#define M_DFF1 1
+#define M_CCHAN 2
+#define M_SCSI 3
+#define M_CFG 4
+#define M_DST_SHIFT 4
+
+#define MK_MODE(src, dst) ((src) | ((dst) << M_DST_SHIFT))
+#define SET_MODE(src, dst) \
+ SET_SRC_MODE src; \
+ SET_DST_MODE dst; \
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
+ mvi MK_MODE(src, dst) call set_mode_work_around; \
+ } else { \
+ mvi MODE_PTR, MK_MODE(src, dst); \
+ }
+
+#define TOGGLE_DFF_MODE \
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
+ call toggle_dff_mode_work_around; \
+ } else { \
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); \
+ }
+
+#define RESTORE_MODE(mode) \
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
+ mov mode call set_mode_work_around; \
+ } else { \
+ mov MODE_PTR, mode; \
+ }
+
+#define SET_SEQINTCODE(code) \
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) { \
+ mvi code call set_seqint_work_around; \
+ } else { \
+ mvi SEQINTCODE, code; \
+ }
+
+/*
+ * Mode Pointer
+ * Controls which of the 5, 512byte, address spaces should be used
+ * as the source and destination of any register accesses in our
+ * register window.
+ */
+register MODE_PTR {
+ address 0x000
+ access_mode RW
+ field DST_MODE 0x70
+ field SRC_MODE 0x07
+ mode_pointer
+}
+
+const SRC_MODE_SHIFT 0
+const DST_MODE_SHIFT 4
+
+/*
+ * Host Interrupt Status
+ */
+register INTSTAT {
+ address 0x001
+ access_mode RW
+ field HWERRINT 0x80
+ field BRKADRINT 0x40
+ field SWTMINT 0x20
+ field PCIINT 0x10
+ field SCSIINT 0x08
+ field SEQINT 0x04
+ field CMDCMPLT 0x02
+ field SPLTINT 0x01
+ mask INT_PEND 0xFF
+}
+
+/*
+ * Sequencer Interrupt Code
+ */
+register SEQINTCODE {
+ address 0x002
+ access_mode RW
+ field {
+ NO_SEQINT, /* No seqint pending. */
+ BAD_PHASE, /* unknown scsi bus phase */
+ SEND_REJECT, /* sending a message reject */
+ PROTO_VIOLATION, /* Protocol Violation */
+ NO_MATCH, /* no cmd match for reconnect */
+ IGN_WIDE_RES, /* Complex IGN Wide Res Msg */
+ PDATA_REINIT, /*
+ * Returned to data phase
+ * that requires data
+ * transfer pointers to be
+ * recalculated from the
+ * transfer residual.
+ */
+ HOST_MSG_LOOP, /*
+ * The bus is ready for the
+ * host to perform another
+ * message transaction. This
+ * mechanism is used for things
+ * like sync/wide negotiation
+ * that require a kernel based
+ * message state engine.
+ */
+ BAD_STATUS, /* Bad status from target */
+ DATA_OVERRUN, /*
+ * Target attempted to write
+ * beyond the bounds of its
+ * command.
+ */
+ MKMSG_FAILED, /*
+ * Target completed command
+ * without honoring our ATN
+ * request to issue a message.
+ */
+ MISSED_BUSFREE, /*
+ * The sequencer never saw
+ * the bus go free after
+ * either a command complete
+ * or disconnect message.
+ */
+ DUMP_CARD_STATE,
+ ILLEGAL_PHASE,
+ INVALID_SEQINT,
+ CFG4ISTAT_INTR,
+ STATUS_OVERRUN,
+ CFG4OVERRUN,
+ ENTERING_NONPACK,
+ TASKMGMT_FUNC_COMPLETE, /*
+ * Task management function
+ * request completed with
+ * an expected busfree.
+ */
+ TASKMGMT_CMD_CMPLT_OKAY, /*
+ * A command with a non-zero
+ * task management function
+ * has completed via the normal
+ * command completion method
+ * for commands with a zero
+ * task management function.
+ * This happens when an attempt
+ * to abort a command loses
+ * the race for the command to
+ * complete normally.
+ */
+ TRACEPOINT0,
+ TRACEPOINT1,
+ TRACEPOINT2,
+ TRACEPOINT3,
+ SAW_HWERR,
+ BAD_SCB_STATUS
+ }
+}
+
+/*
+ * Clear Host Interrupt
+ */
+register CLRINT {
+ address 0x003
+ access_mode WO
+ field CLRHWERRINT 0x80 /* Rev B or greater */
+ field CLRBRKADRINT 0x40
+ field CLRSWTMINT 0x20
+ field CLRPCIINT 0x10
+ field CLRSCSIINT 0x08
+ field CLRSEQINT 0x04
+ field CLRCMDINT 0x02
+ field CLRSPLTINT 0x01
+}
+
+/*
+ * Error Register
+ */
+register ERROR {
+ address 0x004
+ access_mode RO
+ field CIOPARERR 0x80
+ field CIOACCESFAIL 0x40 /* Rev B or greater */
+ field MPARERR 0x20
+ field DPARERR 0x10
+ field SQPARERR 0x08
+ field ILLOPCODE 0x04
+ field DSCTMOUT 0x02
+}
+
+/*
+ * Clear Error
+ */
+register CLRERR {
+ address 0x004
+ access_mode WO
+ field CLRCIOPARERR 0x80
+ field CLRCIOACCESFAIL 0x40 /* Rev B or greater */
+ field CLRMPARERR 0x20
+ field CLRDPARERR 0x10
+ field CLRSQPARERR 0x08
+ field CLRILLOPCODE 0x04
+ field CLRDSCTMOUT 0x02
+}
+
+/*
+ * Host Control Register
+ * Overall host control of the device.
+ */
+register HCNTRL {
+ address 0x005
+ access_mode RW
+ field SEQ_RESET 0x80 /* Rev B or greater */
+ field POWRDN 0x40
+ field SWINT 0x10
+ field SWTIMER_START_B 0x08 /* Rev B or greater */
+ field PAUSE 0x04
+ field INTEN 0x02
+ field CHIPRST 0x01
+ field CHIPRSTACK 0x01
+}
+
+/*
+ * Host New SCB Queue Offset
+ */
+register HNSCB_QOFF {
+ address 0x006
+ access_mode RW
+ size 2
+}
+
+/*
+ * Host Empty SCB Queue Offset
+ */
+register HESCB_QOFF {
+ address 0x008
+ access_mode RW
+}
+
+/*
+ * Host Mailbox
+ */
+register HS_MAILBOX {
+ address 0x00B
+ access_mode RW
+ mask HOST_TQINPOS 0x80 /* Boundary at either 0 or 128 */
+ mask ENINT_COALESCE 0x40 /* Perform interrupt coalescing */
+}
+
+/*
+ * Sequencer Interupt Status
+ */
+register SEQINTSTAT {
+ address 0x00C
+ access_mode RO
+ field SEQ_SWTMRTO 0x10
+ field SEQ_SEQINT 0x08
+ field SEQ_SCSIINT 0x04
+ field SEQ_PCIINT 0x02
+ field SEQ_SPLTINT 0x01
+}
+
+/*
+ * Clear SEQ Interrupt
+ */
+register CLRSEQINTSTAT {
+ address 0x00C
+ access_mode WO
+ field CLRSEQ_SWTMRTO 0x10
+ field CLRSEQ_SEQINT 0x08
+ field CLRSEQ_SCSIINT 0x04
+ field CLRSEQ_PCIINT 0x02
+ field CLRSEQ_SPLTINT 0x01
+}
+
+/*
+ * Software Timer
+ */
+register SWTIMER {
+ address 0x00E
+ access_mode RW
+ size 2
+}
+
+/*
+ * SEQ New SCB Queue Offset
+ */
+register SNSCB_QOFF {
+ address 0x010
+ access_mode RW
+ size 2
+ modes M_CCHAN
+}
+
+/*
+ * SEQ Empty SCB Queue Offset
+ */
+register SESCB_QOFF {
+ address 0x012
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * SEQ Done SCB Queue Offset
+ */
+register SDSCB_QOFF {
+ address 0x014
+ access_mode RW
+ modes M_CCHAN
+ size 2
+}
+
+/*
+ * Queue Offset Control & Status
+ */
+register QOFF_CTLSTA {
+ address 0x016
+ access_mode RW
+ modes M_CCHAN
+ field EMPTY_SCB_AVAIL 0x80
+ field NEW_SCB_AVAIL 0x40
+ field SDSCB_ROLLOVR 0x20
+ field HS_MAILBOX_ACT 0x10
+ field SCB_QSIZE 0x0F {
+ SCB_QSIZE_4,
+ SCB_QSIZE_8,
+ SCB_QSIZE_16,
+ SCB_QSIZE_32,
+ SCB_QSIZE_64,
+ SCB_QSIZE_128,
+ SCB_QSIZE_256,
+ SCB_QSIZE_512,
+ SCB_QSIZE_1024,
+ SCB_QSIZE_2048,
+ SCB_QSIZE_4096,
+ SCB_QSIZE_8192,
+ SCB_QSIZE_16384
+ }
+}
+
+/*
+ * Interrupt Control
+ */
+register INTCTL {
+ address 0x018
+ access_mode RW
+ field SWTMINTMASK 0x80
+ field SWTMINTEN 0x40
+ field SWTIMER_START 0x20
+ field AUTOCLRCMDINT 0x10
+ field PCIINTEN 0x08
+ field SCSIINTEN 0x04
+ field SEQINTEN 0x02
+ field SPLTINTEN 0x01
+}
+
+/*
+ * Data FIFO Control
+ */
+register DFCNTRL {
+ address 0x019
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field PRELOADEN 0x80
+ field SCSIENWRDIS 0x40 /* Rev B only. */
+ field SCSIEN 0x20
+ field SCSIENACK 0x20
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04
+ field DIRECTIONACK 0x04
+ field FIFOFLUSH 0x02
+ field FIFOFLUSHACK 0x02
+ field DIRECTIONEN 0x01
+}
+
+/*
+ * Device Space Command 0
+ */
+register DSCOMMAND0 {
+ address 0x019
+ access_mode RW
+ modes M_CFG
+ field CACHETHEN 0x80 /* Cache Threshold enable */
+ field DPARCKEN 0x40 /* Data Parity Check Enable */
+ field MPARCKEN 0x20 /* Memory Parity Check Enable */
+ field EXTREQLCK 0x10 /* External Request Lock */
+ field DISABLE_TWATE 0x02 /* Rev B or greater */
+ field CIOPARCKEN 0x01 /* Internal bus parity error enable */
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFSTATUS {
+ address 0x01A
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field PRELOAD_AVAIL 0x80
+ field PKT_PRELOAD_AVAIL 0x40
+ field MREQPEND 0x10
+ field HDONE 0x08
+ field DFTHRESH 0x04
+ field FIFOFULL 0x02
+ field FIFOEMP 0x01
+}
+
+/*
+ * S/G Cache Pointer
+ */
+register SG_CACHE_PRE {
+ address 0x01B
+ access_mode WO
+ modes M_DFF0, M_DFF1
+ field SG_ADDR_MASK 0xf8
+ field ODD_SEG 0x04
+ field LAST_SEG 0x02
+}
+
+register SG_CACHE_SHADOW {
+ address 0x01B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field SG_ADDR_MASK 0xf8
+ field ODD_SEG 0x04
+ field LAST_SEG 0x02
+ field LAST_SEG_DONE 0x01
+}
+
+/*
+ * Arbiter Control
+ */
+register ARBCTL {
+ address 0x01B
+ access_mode RW
+ modes M_CFG
+ field RESET_HARB 0x80
+ field RETRY_SWEN 0x08
+ field USE_TIME 0x07
+}
+
+/*
+ * Data Channel Host Address
+ */
+register HADDR {
+ address 0x070
+ access_mode RW
+ size 8
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Host Overlay DMA Address
+ */
+register HODMAADR {
+ address 0x070
+ access_mode RW
+ size 8
+ modes M_SCSI
+}
+
+/*
+ * PCI PLL Delay.
+ */
+register PLLDELAY {
+ address 0x070
+ access_mode RW
+ size 1
+ modes M_CFG
+ field SPLIT_DROP_REQ 0x80
+}
+
+/*
+ * Data Channel Host Count
+ */
+register HCNT {
+ address 0x078
+ access_mode RW
+ size 3
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Host Overlay DMA Count
+ */
+register HODMACNT {
+ address 0x078
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Host Overlay DMA Enable
+ */
+register HODMAEN {
+ address 0x07A
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Scatter/Gather Host Address
+ */
+register SGHADDR {
+ address 0x07C
+ access_mode RW
+ size 8
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * SCB Host Address
+ */
+register SCBHADDR {
+ address 0x07C
+ access_mode RW
+ size 8
+ modes M_CCHAN
+}
+
+/*
+ * Scatter/Gather Host Count
+ */
+register SGHCNT {
+ address 0x084
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * SCB Host Count
+ */
+register SCBHCNT {
+ address 0x084
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * Data FIFO Threshold
+ */
+register DFF_THRSH {
+ address 0x088
+ access_mode RW
+ modes M_CFG
+ field WR_DFTHRSH 0x70 {
+ WR_DFTHRSH_MIN,
+ WR_DFTHRSH_25,
+ WR_DFTHRSH_50,
+ WR_DFTHRSH_63,
+ WR_DFTHRSH_75,
+ WR_DFTHRSH_85,
+ WR_DFTHRSH_90,
+ WR_DFTHRSH_MAX
+ }
+ field RD_DFTHRSH 0x07 {
+ RD_DFTHRSH_MIN,
+ RD_DFTHRSH_25,
+ RD_DFTHRSH_50,
+ RD_DFTHRSH_63,
+ RD_DFTHRSH_75,
+ RD_DFTHRSH_85,
+ RD_DFTHRSH_90,
+ RD_DFTHRSH_MAX
+ }
+}
+
+/*
+ * ROM Address
+ */
+register ROMADDR {
+ address 0x08A
+ access_mode RW
+ size 3
+}
+
+/*
+ * ROM Control
+ */
+register ROMCNTRL {
+ address 0x08D
+ access_mode RW
+ field ROMOP 0xE0
+ field ROMSPD 0x18
+ field REPEAT 0x02
+ field RDY 0x01
+}
+
+/*
+ * ROM Data
+ */
+register ROMDATA {
+ address 0x08E
+ access_mode RW
+}
+
+/*
+ * Data Channel Receive Message 0
+ */
+register DCHRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * CMC Recieve Message 0
+ */
+register CMCRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_CCHAN
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * Overlay Recieve Message 0
+ */
+register OVLYRXMSG0 {
+ address 0x090
+ access_mode RO
+ modes M_SCSI
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * Relaxed Order Enable
+ */
+register ROENABLE {
+ address 0x090
+ access_mode RW
+ modes M_CFG
+ field MSIROEN 0x20
+ field OVLYROEN 0x10
+ field CMCROEN 0x08
+ field SGROEN 0x04
+ field DCH1ROEN 0x02
+ field DCH0ROEN 0x01
+}
+
+/*
+ * Data Channel Receive Message 1
+ */
+register DCHRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CBNUM 0xFF
+}
+
+/*
+ * CMC Recieve Message 1
+ */
+register CMCRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_CCHAN
+ field CBNUM 0xFF
+}
+
+/*
+ * Overlay Recieve Message 1
+ */
+register OVLYRXMSG1 {
+ address 0x091
+ access_mode RO
+ modes M_SCSI
+ field CBNUM 0xFF
+}
+
+/*
+ * No Snoop Enable
+ */
+register NSENABLE {
+ address 0x091
+ access_mode RW
+ modes M_CFG
+ field MSINSEN 0x20
+ field OVLYNSEN 0x10
+ field CMCNSEN 0x08
+ field SGNSEN 0x04
+ field DCH1NSEN 0x02
+ field DCH0NSEN 0x01
+}
+
+/*
+ * Data Channel Receive Message 2
+ */
+register DCHRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MINDEX 0xFF
+}
+
+/*
+ * CMC Recieve Message 2
+ */
+register CMCRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_CCHAN
+ field MINDEX 0xFF
+}
+
+/*
+ * Overlay Recieve Message 2
+ */
+register OVLYRXMSG2 {
+ address 0x092
+ access_mode RO
+ modes M_SCSI
+ field MINDEX 0xFF
+}
+
+/*
+ * Outstanding Split Transactions
+ */
+register OST {
+ address 0x092
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Data Channel Receive Message 3
+ */
+register DCHRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MCLASS 0x0F
+}
+
+/*
+ * CMC Recieve Message 3
+ */
+register CMCRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_CCHAN
+ field MCLASS 0x0F
+}
+
+/*
+ * Overlay Recieve Message 3
+ */
+register OVLYRXMSG3 {
+ address 0x093
+ access_mode RO
+ modes M_SCSI
+ field MCLASS 0x0F
+}
+
+/*
+ * PCI-X Control
+ */
+register PCIXCTL {
+ address 0x093
+ access_mode RW
+ modes M_CFG
+ field SERRPULSE 0x80
+ field UNEXPSCIEN 0x20
+ field SPLTSMADIS 0x10
+ field SPLTSTADIS 0x08
+ field SRSPDPEEN 0x04
+ field TSCSERREN 0x02
+ field CMPABCDIS 0x01
+}
+
+/*
+ * CMC Sequencer Byte Count
+ */
+register CMCSEQBCNT {
+ address 0x094
+ access_mode RO
+ modes M_CCHAN
+}
+
+/*
+ * Overlay Sequencer Byte Count
+ */
+register OVLYSEQBCNT {
+ address 0x094
+ access_mode RO
+ modes M_SCSI
+}
+
+/*
+ * Data Channel Sequencer Byte Count
+ */
+register DCHSEQBCNT {
+ address 0x094
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Data Channel Split Status 0
+ */
+register DCHSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * CMC Split Status 0
+ */
+register CMCSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_CCHAN
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * Overlay Split Status 0
+ */
+register OVLYSPLTSTAT0 {
+ address 0x096
+ access_mode RW
+ modes M_SCSI
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * Data Channel Split Status 1
+ */
+register DCHSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * CMC Split Status 1
+ */
+register CMCSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_CCHAN
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * Overlay Split Status 1
+ */
+register OVLYSPLTSTAT1 {
+ address 0x097
+ access_mode RW
+ modes M_SCSI
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * S/G Receive Message 0
+ */
+register SGRXMSG0 {
+ address 0x098
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CDNUM 0xF8
+ field CFNUM 0x07
+}
+
+/*
+ * S/G Receive Message 1
+ */
+register SGRXMSG1 {
+ address 0x099
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CBNUM 0xFF
+}
+
+/*
+ * S/G Receive Message 2
+ */
+register SGRXMSG2 {
+ address 0x09A
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MINDEX 0xFF
+}
+
+/*
+ * S/G Receive Message 3
+ */
+register SGRXMSG3 {
+ address 0x09B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field MCLASS 0x0F
+}
+
+/*
+ * Slave Split Out Address 0
+ */
+register SLVSPLTOUTADR0 {
+ address 0x098
+ access_mode RO
+ modes M_SCSI
+ field LOWER_ADDR 0x7F
+}
+
+/*
+ * Slave Split Out Address 1
+ */
+register SLVSPLTOUTADR1 {
+ address 0x099
+ access_mode RO
+ modes M_SCSI
+ field REQ_DNUM 0xF8
+ field REQ_FNUM 0x07
+}
+
+/*
+ * Slave Split Out Address 2
+ */
+register SLVSPLTOUTADR2 {
+ address 0x09A
+ access_mode RO
+ modes M_SCSI
+ field REQ_BNUM 0xFF
+}
+
+/*
+ * Slave Split Out Address 3
+ */
+register SLVSPLTOUTADR3 {
+ address 0x09B
+ access_mode RO
+ modes M_SCSI
+ field RLXORD 020
+ field TAG_NUM 0x1F
+}
+
+/*
+ * SG Sequencer Byte Count
+ */
+register SGSEQBCNT {
+ address 0x09C
+ access_mode RO
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Slave Split Out Attribute 0
+ */
+register SLVSPLTOUTATTR0 {
+ address 0x09C
+ access_mode RO
+ modes M_SCSI
+ field LOWER_BCNT 0xFF
+}
+
+/*
+ * Slave Split Out Attribute 1
+ */
+register SLVSPLTOUTATTR1 {
+ address 0x09D
+ access_mode RO
+ modes M_SCSI
+ field CMPLT_DNUM 0xF8
+ field CMPLT_FNUM 0x07
+}
+
+/*
+ * Slave Split Out Attribute 2
+ */
+register SLVSPLTOUTATTR2 {
+ address 0x09E
+ access_mode RO
+ size 2
+ modes M_SCSI
+ field CMPLT_BNUM 0xFF
+}
+/*
+ * S/G Split Status 0
+ */
+register SGSPLTSTAT0 {
+ address 0x09E
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field STAETERM 0x80
+ field SCBCERR 0x40
+ field SCADERR 0x20
+ field SCDATBUCKET 0x10
+ field CNTNOTCMPLT 0x08
+ field RXOVRUN 0x04
+ field RXSCEMSG 0x02
+ field RXSPLTRSP 0x01
+}
+
+/*
+ * S/G Split Status 1
+ */
+register SGSPLTSTAT1 {
+ address 0x09F
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field RXDATABUCKET 0x01
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+ modes M_CFG
+ field TEST_GROUP 0xF0
+ field TEST_NUM 0x0F
+}
+
+/*
+ * Data FIFO 0 PCI Status
+ */
+register DF0PCISTAT {
+ address 0x0A0
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * Data FIFO 1 PCI Status
+ */
+register DF1PCISTAT {
+ address 0x0A1
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * S/G PCI Status
+ */
+register SGPCISTAT {
+ address 0x0A2
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field DPR 0x01
+}
+
+/*
+ * CMC PCI Status
+ */
+register CMCPCISTAT {
+ address 0x0A3
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * Overlay PCI Status
+ */
+register OVLYPCISTAT {
+ address 0x0A4
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field SCAAPERR 0x08
+ field RDPERR 0x04
+ field DPR 0x01
+}
+
+/*
+ * PCI Status for MSI Master DMA Transfer
+ */
+register MSIPCISTAT {
+ address 0x0A6
+ access_mode RW
+ modes M_CFG
+ field SSE 0x40
+ field RMA 0x20
+ field RTA 0x10
+ field CLRPENDMSI 0x08
+ field TWATERR 0x02
+ field DPR 0x01
+}
+
+/*
+ * PCI Status for Target
+ */
+register TARGPCISTAT {
+ address 0x0A7
+ access_mode RW
+ modes M_CFG
+ field DPE 0x80
+ field SSE 0x40
+ field STA 0x08
+ field TWATERR 0x02
+}
+
+/*
+ * LQ Packet In
+ * The last LQ Packet received
+ */
+register LQIN {
+ address 0x020
+ access_mode RW
+ size 20
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCB Type Pointer
+ * SCB offset for Target Mode SCB type information
+ */
+register TYPEPTR {
+ address 0x020
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Queue Tag Pointer
+ * SCB offset to the Two Byte tag identifier used for target mode.
+ */
+register TAGPTR {
+ address 0x021
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Logical Unit Number Pointer
+ * SCB offset to the LSB (little endian) of the lun field.
+ */
+register LUNPTR {
+ address 0x022
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Data Length Pointer
+ * SCB offset for the 4 byte data length field in target mode.
+ */
+register DATALENPTR {
+ address 0x023
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Status Length Pointer
+ * SCB offset to the two byte status field in target SCBs.
+ */
+register STATLENPTR {
+ address 0x024
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Length Pointer
+ * Scb offset for the CDB length field in initiator SCBs.
+ */
+register CMDLENPTR {
+ address 0x025
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Task Attribute Pointer
+ * Scb offset for the byte field specifying the attribute byte
+ * to be used in command packets.
+ */
+register ATTRPTR {
+ address 0x026
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Task Management Flags Pointer
+ * Scb offset for the byte field specifying the attribute flags
+ * byte to be used in command packets.
+ */
+register FLAGPTR {
+ address 0x027
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Pointer
+ * Scb offset for the first byte in the CDB for initiator SCBs.
+ */
+register CMDPTR {
+ address 0x028
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Queue Next Pointer
+ * Scb offset for the 2 byte "next scb link".
+ */
+register QNEXTPTR {
+ address 0x029
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * SCSI ID Pointer
+ * Scb offset to the value to place in the SCSIID register
+ * during target mode connections.
+ */
+register IDPTR {
+ address 0x02A
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Aborted Byte Pointer
+ * Offset to the SCB flags field that includes the
+ * "SCB aborted" status bit.
+ */
+register ABRTBYTEPTR {
+ address 0x02B
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Command Aborted Bit Pointer
+ * Bit offset in the SCB flags field for "SCB aborted" status.
+ */
+register ABRTBITPTR {
+ address 0x02C
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register MAXCMDBYTES {
+ address 0x02D
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register MAXCMD2RCV {
+ address 0x02E
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register SHORTTHRESH {
+ address 0x02F
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Logical Unit Number Length
+ * The length, in bytes, of the SCB lun field.
+ */
+register LUNLEN {
+ address 0x030
+ access_mode RW
+ modes M_CFG
+ mask ILUNLEN 0x0F
+ mask TLUNLEN 0xF0
+}
+const LUNLEN_SINGLE_LEVEL_LUN 0xF
+
+/*
+ * CDB Limit
+ * The size, in bytes, of the embedded CDB field in initator SCBs.
+ */
+register CDBLIMIT {
+ address 0x031
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Maximum Commands
+ * The maximum number of commands to issue during a
+ * single packetized connection.
+ */
+register MAXCMD {
+ address 0x032
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * Maximum Command Counter
+ * The number of commands already sent during this connection
+ */
+register MAXCMDCNT {
+ address 0x033
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * LQ Packet Reserved Bytes
+ * The bytes to be sent in the currently reserved fileds
+ * of all LQ packets.
+ */
+register LQRSVD01 {
+ address 0x034
+ access_mode RW
+ modes M_SCSI
+}
+register LQRSVD16 {
+ address 0x035
+ access_mode RW
+ modes M_SCSI
+}
+register LQRSVD17 {
+ address 0x036
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Command Reserved 0
+ * The byte to be sent for the reserved byte 0 of
+ * outgoing command packets.
+ */
+register CMDRSVD0 {
+ address 0x037
+ access_mode RW
+ modes M_CFG
+}
+
+/*
+ * LQ Manager Control 0
+ */
+register LQCTL0 {
+ address 0x038
+ access_mode RW
+ modes M_CFG
+ field LQITARGCLT 0xC0
+ field LQIINITGCLT 0x30
+ field LQ0TARGCLT 0x0C
+ field LQ0INITGCLT 0x03
+}
+
+/*
+ * LQ Manager Control 1
+ */
+register LQCTL1 {
+ address 0x038
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field PCI2PCI 0x04
+ field SINGLECMD 0x02
+ field ABORTPENDING 0x01
+}
+
+/*
+ * LQ Manager Control 2
+ */
+register LQCTL2 {
+ address 0x039
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQIRETRY 0x80
+ field LQICONTINUE 0x40
+ field LQITOIDLE 0x20
+ field LQIPAUSE 0x10
+ field LQORETRY 0x08
+ field LQOCONTINUE 0x04
+ field LQOTOIDLE 0x02
+ field LQOPAUSE 0x01
+}
+
+/*
+ * SCSI RAM BIST0
+ */
+register SCSBIST0 {
+ address 0x039
+ access_mode RW
+ modes M_CFG
+ field GSBISTERR 0x40
+ field GSBISTDONE 0x20
+ field GSBISTRUN 0x10
+ field OSBISTERR 0x04
+ field OSBISTDONE 0x02
+ field OSBISTRUN 0x01
+}
+
+/*
+ * SCSI Sequence Control0
+ */
+register SCSISEQ0 {
+ address 0x03A
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field TEMODEO 0x80
+ field ENSELO 0x40
+ field ENARBO 0x20
+ field FORCEBUSFREE 0x10
+ field SCSIRSTO 0x01
+}
+
+/*
+ * SCSI RAM BIST 1
+ */
+register SCSBIST1 {
+ address 0x03A
+ access_mode RW
+ modes M_CFG
+ field NTBISTERR 0x04
+ field NTBISTDONE 0x02
+ field NTBISTRUN 0x01
+}
+
+/*
+ * SCSI Sequence Control 1
+ */
+register SCSISEQ1 {
+ address 0x03B
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field MANUALCTL 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field MANUALP 0x0C
+ field ENAUTOATNP 0x02
+ field ALTSTIM 0x01
+}
+
+/*
+ * SCSI Transfer Control 0
+ */
+register SXFRCTL0 {
+ address 0x03C
+ access_mode RW
+ modes M_SCSI
+ field DFON 0x80
+ field DFPEXP 0x40
+ field BIOSCANCELEN 0x10
+ field SPIOEN 0x08
+}
+
+/*
+ * SCSI Transfer Control 1
+ */
+register SXFRCTL1 {
+ address 0x03D
+ access_mode RW
+ modes M_SCSI
+ field BITBUCKET 0x80
+ field ENSACHK 0x40
+ field ENSPCHK 0x20
+ field STIMESEL 0x18
+ field ENSTIMER 0x04
+ field ACTNEGEN 0x02
+ field STPWEN 0x01
+}
+
+/*
+ * SCSI Transfer Control 2
+ */
+register SXFRCTL2 {
+ address 0x03E
+ access_mode RW
+ modes M_SCSI
+ field AUTORSTDIS 0x10
+ field CMDDMAEN 0x08
+ field ASU 0x07
+}
+
+/*
+ * SCSI Bus Initiator IDs
+ * Bitmask of observed initiators on the bus.
+ */
+register BUSINITID {
+ address 0x03C
+ access_mode RW
+ modes M_CFG
+ size 2
+}
+
+/*
+ * Data Length Counters
+ * Packet byte counter.
+ */
+register DLCOUNT {
+ address 0x03C
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ size 3
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFFSTAT {
+ address 0x03F
+ access_mode RW
+ modes M_SCSI
+ field FIFO1FREE 0x20
+ field FIFO0FREE 0x10
+ /*
+ * On the B, this enum only works
+ * in the read direction. For writes,
+ * you must use the B version of the
+ * CURRFIFO_0 definition which is defined
+ * as a constant outside of this register
+ * definition to avoid confusing the
+ * register pretty printing code.
+ */
+ enum CURRFIFO 0x03 {
+ CURRFIFO_0,
+ CURRFIFO_1,
+ CURRFIFO_NONE 0x3
+ }
+}
+
+const B_CURRFIFO_0 0x2
+
+/*
+ * SCSI Bus Target IDs
+ * Bitmask of observed targets on the bus.
+ */
+register BUSTARGID {
+ address 0x03E
+ access_mode RW
+ modes M_CFG
+ size 2
+}
+
+/*
+ * SCSI Control Signal Out
+ */
+register SCSISIGO {
+ address 0x040
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CDO 0x80
+ field IOO 0x40
+ field MSGO 0x20
+ field ATNO 0x10
+ field SELO 0x08
+ field BSYO 0x04
+ field REQO 0x02
+ field ACKO 0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+ enum PHASE_MASK CDO|IOO|MSGO {
+ P_DATAOUT 0x0,
+ P_DATAIN IOO,
+ P_DATAOUT_DT P_DATAOUT|MSGO,
+ P_DATAIN_DT P_DATAIN|MSGO,
+ P_COMMAND CDO,
+ P_MESGOUT CDO|MSGO,
+ P_STATUS CDO|IOO,
+ P_MESGIN CDO|IOO|MSGO
+ }
+}
+
+register SCSISIGI {
+ address 0x041
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ field ATNI 0x10
+ field SELI 0x08
+ field BSYI 0x04
+ field REQI 0x02
+ field ACKI 0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+ enum PHASE_MASK CDO|IOO|MSGO {
+ P_DATAOUT 0x0,
+ P_DATAIN IOO,
+ P_DATAOUT_DT P_DATAOUT|MSGO,
+ P_DATAIN_DT P_DATAIN|MSGO,
+ P_COMMAND CDO,
+ P_MESGOUT CDO|MSGO,
+ P_STATUS CDO|IOO,
+ P_MESGIN CDO|IOO|MSGO
+ }
+}
+
+/*
+ * Multiple Target IDs
+ * Bitmask of ids to respond as a target.
+ */
+register MULTARGID {
+ address 0x040
+ access_mode RW
+ modes M_CFG
+ size 2
+}
+
+/*
+ * SCSI Phase
+ */
+register SCSIPHASE {
+ address 0x042
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field STATUS_PHASE 0x20
+ field COMMAND_PHASE 0x10
+ field MSG_IN_PHASE 0x08
+ field MSG_OUT_PHASE 0x04
+ field DATA_PHASE_MASK 0x03 {
+ DATA_OUT_PHASE 0x01,
+ DATA_IN_PHASE 0x02
+ }
+}
+
+/*
+ * SCSI Data 0 Image
+ */
+register SCSIDAT0_IMG {
+ address 0x043
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCSI Latched Data
+ */
+register SCSIDAT {
+ address 0x044
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ size 2
+}
+
+/*
+ * SCSI Data Bus
+ */
+register SCSIBUS {
+ address 0x046
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ size 2
+}
+
+/*
+ * Target ID In
+ */
+register TARGIDIN {
+ address 0x048
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLKOUT 0x80
+ field TARGID 0x0F
+}
+
+/*
+ * Selection/Reselection ID
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+ address 0x049
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field SELID_MASK 0xf0
+ field ONEBIT 0x08
+}
+
+/*
+ * SCSI Block Control
+ * Controls Bus type and channel selection. SELWIDE allows for the
+ * coexistence of 8bit and 16bit devices on a wide bus.
+ */
+register SBLKCTL {
+ address 0x04A
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field DIAGLEDEN 0x80
+ field DIAGLEDON 0x40
+ field ENAB40 0x08 /* LVD transceiver active */
+ field ENAB20 0x04 /* SE/HVD transceiver active */
+ field SELWIDE 0x02
+}
+
+/*
+ * Option Mode
+ */
+register OPTIONMODE {
+ address 0x04A
+ access_mode RW
+ modes M_CFG
+ field BIOSCANCTL 0x80
+ field AUTOACKEN 0x40
+ field BIASCANCTL 0x20
+ field BUSFREEREV 0x10
+ field ENDGFORMCHK 0x04
+ field AUTO_MSGOUT_DE 0x02
+ mask OPTIONMODE_DEFAULTS AUTO_MSGOUT_DE
+}
+
+/*
+ * SCSI Status 0
+ */
+register SSTAT0 {
+ address 0x04B
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field TARGET 0x80 /* Board acting as target */
+ field SELDO 0x40 /* Selection Done */
+ field SELDI 0x20 /* Board has been selected */
+ field SELINGO 0x10 /* Selection In Progress */
+ field IOERR 0x08 /* LVD Tranceiver mode changed */
+ field OVERRUN 0x04 /* SCSI Offset overrun detected */
+ field SPIORDY 0x02 /* SCSI PIO Ready */
+ field ARBDO 0x01 /* Arbitration Done Out */
+}
+
+/*
+ * Clear SCSI Interrupt 0
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+ address 0x04B
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRSELDO 0x40
+ field CLRSELDI 0x20
+ field CLRSELINGO 0x10
+ field CLRIOERR 0x08
+ field CLROVERRUN 0x04
+ field CLRSPIORDY 0x02
+ field CLRARBDO 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 0
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+ address 0x04B
+ access_mode RW
+ modes M_CFG
+ field ENSELDO 0x40
+ field ENSELDI 0x20
+ field ENSELINGO 0x10
+ field ENIOERR 0x08
+ field ENOVERRUN 0x04
+ field ENSPIORDY 0x02
+ field ENARBDO 0x01
+}
+
+/*
+ * SCSI Status 1
+ */
+register SSTAT1 {
+ address 0x04C
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field SELTO 0x80
+ field ATNTARG 0x40
+ field SCSIRSTI 0x20
+ field PHASEMIS 0x10
+ field BUSFREE 0x08
+ field SCSIPERR 0x04
+ field STRB2FAST 0x02
+ field REQINIT 0x01
+}
+
+/*
+ * Clear SCSI Interrupt 1
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+ address 0x04C
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRSELTIMEO 0x80
+ field CLRATNO 0x40
+ field CLRSCSIRSTI 0x20
+ field CLRBUSFREE 0x08
+ field CLRSCSIPERR 0x04
+ field CLRSTRB2FAST 0x02
+ field CLRREQINIT 0x01
+}
+
+/*
+ * SCSI Status 2
+ */
+register SSTAT2 {
+ address 0x04d
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field BUSFREETIME 0xc0 {
+ BUSFREE_LQO 0x40,
+ BUSFREE_DFF0 0x80,
+ BUSFREE_DFF1 0xC0
+ }
+ field NONPACKREQ 0x20
+ field EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ field BSYX 0x08 /* Busy Expander */
+ field WIDE_RES 0x04 /* Modes 0 and 1 only */
+ field SDONE 0x02 /* Modes 0 and 1 only */
+ field DMADONE 0x01 /* Modes 0 and 1 only */
+}
+
+/*
+ * Clear SCSI Interrupt 2
+ */
+register CLRSINT2 {
+ address 0x04D
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRNONPACKREQ 0x20
+ field CLRWIDE_RES 0x04 /* Modes 0 and 1 only */
+ field CLRSDONE 0x02 /* Modes 0 and 1 only */
+ field CLRDMADONE 0x01 /* Modes 0 and 1 only */
+}
+
+/*
+ * SCSI Interrupt Mode 2
+ */
+register SIMODE2 {
+ address 0x04D
+ access_mode RW
+ modes M_CFG
+ field ENWIDE_RES 0x04
+ field ENSDONE 0x02
+ field ENDMADONE 0x01
+}
+
+/*
+ * Physical Error Diagnosis
+ */
+register PERRDIAG {
+ address 0x04E
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field HIZERO 0x80
+ field HIPERR 0x40
+ field PREVPHASE 0x20
+ field PARITYERR 0x10
+ field AIPERR 0x08
+ field CRCERR 0x04
+ field DGFORMERR 0x02
+ field DTERR 0x01
+}
+
+/*
+ * LQI Manager Current State
+ */
+register LQISTATE {
+ address 0x04E
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * SCSI Offset Count
+ */
+register SOFFCNT {
+ address 0x04F
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * LQO Manager Current State
+ */
+register LQOSTATE {
+ address 0x04F
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * LQI Manager Status
+ */
+register LQISTAT0 {
+ address 0x050
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQIATNQAS 0x20
+ field LQICRCT1 0x10
+ field LQICRCT2 0x08
+ field LQIBADLQT 0x04
+ field LQIATNLQ 0x02
+ field LQIATNCMD 0x01
+}
+
+/*
+ * Clear LQI Interrupts 0
+ */
+register CLRLQIINT0 {
+ address 0x050
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQIATNQAS 0x20
+ field CLRLQICRCT1 0x10
+ field CLRLQICRCT2 0x08
+ field CLRLQIBADLQT 0x04
+ field CLRLQIATNLQ 0x02
+ field CLRLQIATNCMD 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 0
+ */
+register LQIMODE0 {
+ address 0x050
+ access_mode RW
+ modes M_CFG
+ field ENLQIATNQASK 0x20
+ field ENLQICRCT1 0x10
+ field ENLQICRCT2 0x08
+ field ENLQIBADLQT 0x04
+ field ENLQIATNLQ 0x02
+ field ENLQIATNCMD 0x01
+}
+
+/*
+ * LQI Manager Status 1
+ */
+register LQISTAT1 {
+ address 0x051
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQIPHASE_LQ 0x80
+ field LQIPHASE_NLQ 0x40
+ field LQIABORT 0x20
+ field LQICRCI_LQ 0x10
+ field LQICRCI_NLQ 0x08
+ field LQIBADLQI 0x04
+ field LQIOVERI_LQ 0x02
+ field LQIOVERI_NLQ 0x01
+}
+
+/*
+ * Clear LQI Manager Interrupts1
+ */
+register CLRLQIINT1 {
+ address 0x051
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQIPHASE_LQ 0x80
+ field CLRLQIPHASE_NLQ 0x40
+ field CLRLIQABORT 0x20
+ field CLRLQICRCI_LQ 0x10
+ field CLRLQICRCI_NLQ 0x08
+ field CLRLQIBADLQI 0x04
+ field CLRLQIOVERI_LQ 0x02
+ field CLRLQIOVERI_NLQ 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 1
+ */
+register LQIMODE1 {
+ address 0x051
+ access_mode RW
+ modes M_CFG
+ field ENLQIPHASE_LQ 0x80 /* LQIPHASE1 */
+ field ENLQIPHASE_NLQ 0x40 /* LQIPHASE2 */
+ field ENLIQABORT 0x20
+ field ENLQICRCI_LQ 0x10 /* LQICRCI1 */
+ field ENLQICRCI_NLQ 0x08 /* LQICRCI2 */
+ field ENLQIBADLQI 0x04
+ field ENLQIOVERI_LQ 0x02 /* LQIOVERI1 */
+ field ENLQIOVERI_NLQ 0x01 /* LQIOVERI2 */
+}
+
+/*
+ * LQI Manager Status 2
+ */
+register LQISTAT2 {
+ address 0x052
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field PACKETIZED 0x80
+ field LQIPHASE_OUTPKT 0x40
+ field LQIWORKONLQ 0x20
+ field LQIWAITFIFO 0x10
+ field LQISTOPPKT 0x08
+ field LQISTOPLQ 0x04
+ field LQISTOPCMD 0x02
+ field LQIGSAVAIL 0x01
+}
+
+/*
+ * SCSI Status 3
+ */
+register SSTAT3 {
+ address 0x053
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field NTRAMPERR 0x02
+ field OSRAMPERR 0x01
+}
+
+/*
+ * Clear SCSI Status 3
+ */
+register CLRSINT3 {
+ address 0x053
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRNTRAMPERR 0x02
+ field CLROSRAMPERR 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 3
+ */
+register SIMODE3 {
+ address 0x053
+ access_mode RW
+ modes M_CFG
+ field ENNTRAMPERR 0x02
+ field ENOSRAMPERR 0x01
+}
+
+/*
+ * LQO Manager Status 0
+ */
+register LQOSTAT0 {
+ address 0x054
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQOTARGSCBPERR 0x10
+ field LQOSTOPT2 0x08
+ field LQOATNLQ 0x04
+ field LQOATNPKT 0x02
+ field LQOTCRC 0x01
+}
+
+/*
+ * Clear LQO Manager interrupt 0
+ */
+register CLRLQOINT0 {
+ address 0x054
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQOTARGSCBPERR 0x10
+ field CLRLQOSTOPT2 0x08
+ field CLRLQOATNLQ 0x04
+ field CLRLQOATNPKT 0x02
+ field CLRLQOTCRC 0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 0
+ */
+register LQOMODE0 {
+ address 0x054
+ access_mode RW
+ modes M_CFG
+ field ENLQOTARGSCBPERR 0x10
+ field ENLQOSTOPT2 0x08
+ field ENLQOATNLQ 0x04
+ field ENLQOATNPKT 0x02
+ field ENLQOTCRC 0x01
+}
+
+/*
+ * LQO Manager Status 1
+ */
+register LQOSTAT1 {
+ address 0x055
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQOINITSCBPERR 0x10
+ field LQOSTOPI2 0x08
+ field LQOBADQAS 0x04
+ field LQOBUSFREE 0x02
+ field LQOPHACHGINPKT 0x01
+}
+
+/*
+ * Clear LOQ Interrupt 1
+ */
+register CLRLQOINT1 {
+ address 0x055
+ access_mode WO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field CLRLQOINITSCBPERR 0x10
+ field CLRLQOSTOPI2 0x08
+ field CLRLQOBADQAS 0x04
+ field CLRLQOBUSFREE 0x02
+ field CLRLQOPHACHGINPKT 0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 1
+ */
+register LQOMODE1 {
+ address 0x055
+ access_mode RW
+ modes M_CFG
+ field ENLQOINITSCBPERR 0x10
+ field ENLQOSTOPI2 0x08
+ field ENLQOBADQAS 0x04
+ field ENLQOBUSFREE 0x02
+ field ENLQOPHACHGINPKT 0x01
+}
+
+/*
+ * LQO Manager Status 2
+ */
+register LQOSTAT2 {
+ address 0x056
+ access_mode RO
+ modes M_DFF0, M_DFF1, M_SCSI
+ field LQOPKT 0xE0
+ field LQOWAITFIFO 0x10
+ field LQOPHACHGOUTPKT 0x02 /* outside of packet boundaries. */
+ field LQOSTOP0 0x01 /* Stopped after sending all packets */
+}
+
+/*
+ * Output Synchronizer Space Count
+ */
+register OS_SPACE_CNT {
+ address 0x056
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * SCSI Interrupt Mode 1
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+ address 0x057
+ access_mode RW
+ modes M_DFF0, M_DFF1, M_SCSI
+ field ENSELTIMO 0x80
+ field ENATNTARG 0x40
+ field ENSCSIRST 0x20
+ field ENPHASEMIS 0x10
+ field ENBUSFREE 0x08
+ field ENSCSIPERR 0x04
+ field ENSTRB2FAST 0x02
+ field ENREQINIT 0x01
+}
+
+/*
+ * Good Status FIFO
+ */
+register GSFIFO {
+ address 0x058
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * Data FIFO SCSI Transfer Control
+ */
+register DFFSXFRCTL {
+ address 0x05A
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field DFFBITBUCKET 0x08
+ field CLRSHCNT 0x04
+ field CLRCHN 0x02
+ field RSTCHN 0x01
+}
+
+/*
+ * Next SCSI Control Block
+ */
+register NEXTSCB {
+ address 0x05A
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/* Rev B only. */
+register LQOSCSCTL {
+ address 0x05A
+ access_mode RW
+ size 1
+ modes M_CFG
+ field LQOH2A_VERSION 0x80
+ field LQONOCHKOVER 0x01
+}
+
+/*
+ * SEQ Interrupts
+ */
+register SEQINTSRC {
+ address 0x05B
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field CTXTDONE 0x40
+ field SAVEPTRS 0x20
+ field CFG4DATA 0x10
+ field CFG4ISTAT 0x08
+ field CFG4TSTAT 0x04
+ field CFG4ICMD 0x02
+ field CFG4TCMD 0x01
+}
+
+/*
+ * Clear Arp Interrupts
+ */
+register CLRSEQINTSRC {
+ address 0x05B
+ access_mode WO
+ modes M_DFF0, M_DFF1
+ field CLRCTXTDONE 0x40
+ field CLRSAVEPTRS 0x20
+ field CLRCFG4DATA 0x10
+ field CLRCFG4ISTAT 0x08
+ field CLRCFG4TSTAT 0x04
+ field CLRCFG4ICMD 0x02
+ field CLRCFG4TCMD 0x01
+}
+
+/*
+ * SEQ Interrupt Enabled (Shared)
+ */
+register SEQIMODE {
+ address 0x05C
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field ENCTXTDONE 0x40
+ field ENSAVEPTRS 0x20
+ field ENCFG4DATA 0x10
+ field ENCFG4ISTAT 0x08
+ field ENCFG4TSTAT 0x04
+ field ENCFG4ICMD 0x02
+ field ENCFG4TCMD 0x01
+}
+
+/*
+ * Current SCSI Control Block
+ */
+register CURRSCB {
+ address 0x05C
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Data FIFO Status
+ */
+register MDFFSTAT {
+ address 0x05D
+ access_mode RO
+ modes M_DFF0, M_DFF1
+ field SHCNTNEGATIVE 0x40 /* Rev B or higher */
+ field SHCNTMINUS1 0x20 /* Rev B or higher */
+ field LASTSDONE 0x10
+ field SHVALID 0x08
+ field DLZERO 0x04 /* FIFO data ends on packet boundary. */
+ field DATAINFIFO 0x02
+ field FIFOFREE 0x01
+}
+
+/*
+ * CRC Control
+ */
+register CRCCONTROL {
+ address 0x05d
+ access_mode RW
+ modes M_CFG
+ field CRCVALCHKEN 0x40
+}
+
+/*
+ * SCSI Test Control
+ */
+register SCSITEST {
+ address 0x05E
+ access_mode RW
+ modes M_CFG
+ field CNTRTEST 0x08
+ field SEL_TXPLL_DEBUG 0x04
+}
+
+/*
+ * Data FIFO Queue Tag
+ */
+register DFFTAG {
+ address 0x05E
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Last SCSI Control Block
+ */
+register LASTSCB {
+ address 0x05E
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * SCSI I/O Cell Power-down Control
+ */
+register IOPDNCTL {
+ address 0x05F
+ access_mode RW
+ modes M_CFG
+ field DISABLE_OE 0x80
+ field PDN_IDIST 0x04
+ field PDN_DIFFSENSE 0x01
+}
+
+/*
+ * Shaddow Host Address.
+ */
+register SHADDR {
+ address 0x060
+ access_mode RO
+ size 8
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Data Group CRC Interval.
+ */
+register DGRPCRCI {
+ address 0x060
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Address
+ */
+register NEGOADDR {
+ address 0x060
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Data Transfer Negotiation Data - Period Byte
+ */
+register NEGPERIOD {
+ address 0x061
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Packetized CRC Interval
+ */
+register PACKCRCI {
+ address 0x062
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Data - Offset Byte
+ */
+register NEGOFFSET {
+ address 0x062
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Data Transfer Negotiation Data - PPR Options
+ */
+register NEGPPROPTS {
+ address 0x063
+ access_mode RW
+ modes M_SCSI
+ field PPROPT_PACE 0x08
+ field PPROPT_QAS 0x04
+ field PPROPT_DT 0x02
+ field PPROPT_IUT 0x01
+}
+
+/*
+ * Data Transfer Negotiation Data - Connection Options
+ */
+register NEGCONOPTS {
+ address 0x064
+ access_mode RW
+ modes M_SCSI
+ field ENSNAPSHOT 0x40
+ field RTI_WRTDIS 0x20
+ field RTI_OVRDTRN 0x10
+ field ENSLOWCRC 0x08
+ field ENAUTOATNI 0x04
+ field ENAUTOATNO 0x02
+ field WIDEXFER 0x01
+}
+
+/*
+ * Negotiation Table Annex Column Index.
+ */
+register ANNEXCOL {
+ address 0x065
+ access_mode RW
+ modes M_SCSI
+}
+
+register SCSCHKN {
+ address 0x066
+ access_mode RW
+ modes M_CFG
+ field STSELSKIDDIS 0x40
+ field CURRFIFODEF 0x20
+ field WIDERESEN 0x10
+ field SDONEMSKDIS 0x08
+ field DFFACTCLR 0x04
+ field SHVALIDSTDIS 0x02
+ field LSTSGCLRDIS 0x01
+}
+
+const AHD_ANNEXCOL_PER_DEV0 4
+const AHD_NUM_PER_DEV_ANNEXCOLS 4
+const AHD_ANNEXCOL_PRECOMP_SLEW 4
+const AHD_PRECOMP_MASK 0x07
+const AHD_PRECOMP_SHIFT 0
+const AHD_PRECOMP_CUTBACK_17 0x04
+const AHD_PRECOMP_CUTBACK_29 0x06
+const AHD_PRECOMP_CUTBACK_37 0x07
+const AHD_SLEWRATE_MASK 0x78
+const AHD_SLEWRATE_SHIFT 3
+/*
+ * Rev A has only a single bit (high bit of field) of slew adjustment.
+ * Rev B has 4 bits. The current default happens to be the same for both.
+ */
+const AHD_SLEWRATE_DEF_REVA 0x08
+const AHD_SLEWRATE_DEF_REVB 0x08
+
+/* Rev A does not have any amplitude setting. */
+const AHD_ANNEXCOL_AMPLITUDE 6
+const AHD_AMPLITUDE_MASK 0x7
+const AHD_AMPLITUDE_SHIFT 0
+const AHD_AMPLITUDE_DEF 0x7
+
+/*
+ * Negotiation Table Annex Data Port.
+ */
+register ANNEXDAT {
+ address 0x066
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Initiator's Own Id.
+ * The SCSI ID to use for Selection Out and seen during a reselection..
+ */
+register IOWNID {
+ address 0x067
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 0
+ */
+register PLL960CTL0 {
+ address 0x068
+ access_mode RW
+ modes M_CFG
+ field PLL_VCOSEL 0x80
+ field PLL_PWDN 0x40
+ field PLL_NS 0x30
+ field PLL_ENLUD 0x08
+ field PLL_ENLPF 0x04
+ field PLL_DLPF 0x02
+ field PLL_ENFBM 0x01
+}
+
+/*
+ * Target Own Id
+ */
+register TOWNID {
+ address 0x069
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 1
+ */
+register PLL960CTL1 {
+ address 0x069
+ access_mode RW
+ modes M_CFG
+ field PLL_CNTEN 0x80
+ field PLL_CNTCLR 0x40
+ field PLL_RST 0x01
+}
+
+/*
+ * Expander Signature
+ */
+register XSIG {
+ address 0x06A
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Shadow Byte Count
+ */
+register SHCNT {
+ address 0x068
+ access_mode RW
+ size 3
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Selection Out ID
+ */
+register SELOID {
+ address 0x06B
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * 960-MHz Phase-Locked Loop Test Count
+ */
+register PLL960CNT0 {
+ address 0x06A
+ access_mode RO
+ size 2
+ modes M_CFG
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 0
+ */
+register PLL400CTL0 {
+ address 0x06C
+ access_mode RW
+ modes M_CFG
+ field PLL_VCOSEL 0x80
+ field PLL_PWDN 0x40
+ field PLL_NS 0x30
+ field PLL_ENLUD 0x08
+ field PLL_ENLPF 0x04
+ field PLL_DLPF 0x02
+ field PLL_ENFBM 0x01
+}
+
+/*
+ * Arbitration Fairness
+ */
+register FAIRNESS {
+ address 0x06C
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 1
+ */
+register PLL400CTL1 {
+ address 0x06D
+ access_mode RW
+ modes M_CFG
+ field PLL_CNTEN 0x80
+ field PLL_CNTCLR 0x40
+ field PLL_RST 0x01
+}
+
+/*
+ * Arbitration Unfairness
+ */
+register UNFAIRNESS {
+ address 0x06E
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Test Count
+ */
+register PLL400CNT0 {
+ address 0x06E
+ access_mode RO
+ size 2
+ modes M_CFG
+}
+
+/*
+ * SCB Page Pointer
+ */
+register SCBPTR {
+ address 0x0A8
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1, M_CCHAN, M_SCSI
+}
+
+/*
+ * CMC SCB Array Count
+ * Number of bytes to transfer between CMC SCB memory and SCBRAM.
+ * Transfers must be 8byte aligned and sized.
+ */
+register CCSCBACNT {
+ address 0x0AB
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * SCB Autopointer
+ * SCB-Next Address Snooping logic. When an SCB is transferred to
+ * the card, the next SCB address to be used by the CMC array can
+ * be autoloaded from that transfer.
+ */
+register SCBAUTOPTR {
+ address 0x0AB
+ access_mode RW
+ modes M_CFG
+ field AUSCBPTR_EN 0x80
+ field SCBPTR_ADDR 0x38
+ field SCBPTR_OFF 0x07
+}
+
+/*
+ * CMC SG Ram Address Pointer
+ */
+register CCSGADDR {
+ address 0x0AC
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * CMC SCB RAM Address Pointer
+ */
+register CCSCBADDR {
+ address 0x0AC
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * CMC SCB Ram Back-up Address Pointer
+ * Indicates the true stop location of transfers halted prior
+ * to SCBHCNT going to 0.
+ */
+register CCSCBADR_BK {
+ address 0x0AC
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * CMC SG Control
+ */
+register CCSGCTL {
+ address 0x0AD
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field CCSGDONE 0x80
+ field SG_CACHE_AVAIL 0x10
+ field CCSGENACK 0x08
+ mask CCSGEN 0x0C
+ field SG_FETCH_REQ 0x02
+ field CCSGRESET 0x01
+}
+
+/*
+ * CMD SCB Control
+ */
+register CCSCBCTL {
+ address 0x0AD
+ access_mode RW
+ modes M_CCHAN
+ field CCSCBDONE 0x80
+ field ARRDONE 0x40
+ field CCARREN 0x10
+ field CCSCBEN 0x08
+ field CCSCBDIR 0x04
+ field CCSCBRESET 0x01
+}
+
+/*
+ * CMC Ram BIST
+ */
+register CMC_RAMBIST {
+ address 0x0AD
+ access_mode RW
+ modes M_CFG
+ field SG_ELEMENT_SIZE 0x80
+ field SCBRAMBIST_FAIL 0x40
+ field SG_BIST_FAIL 0x20
+ field SG_BIST_EN 0x10
+ field CMC_BUFFER_BIST_FAIL 0x02
+ field CMC_BUFFER_BIST_EN 0x01
+}
+
+/*
+ * CMC SG RAM Data Port
+ */
+register CCSGRAM {
+ address 0x0B0
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * CMC SCB RAM Data Port
+ */
+register CCSCBRAM {
+ address 0x0B0
+ access_mode RW
+ modes M_CCHAN
+}
+
+/*
+ * Flex DMA Address.
+ */
+register FLEXADR {
+ address 0x0B0
+ access_mode RW
+ size 3
+ modes M_SCSI
+}
+
+/*
+ * Flex DMA Byte Count
+ */
+register FLEXCNT {
+ address 0x0B3
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Flex DMA Status
+ */
+register FLEXDMASTAT {
+ address 0x0B5
+ access_mode RW
+ modes M_SCSI
+ field FLEXDMAERR 0x02
+ field FLEXDMADONE 0x01
+}
+
+/*
+ * Flex DMA Data Port
+ */
+register FLEXDATA {
+ address 0x0B6
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Board Data
+ */
+register BRDDAT {
+ address 0x0B8
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Board Control
+ */
+register BRDCTL {
+ address 0x0B9
+ access_mode RW
+ modes M_SCSI
+ field FLXARBACK 0x80
+ field FLXARBREQ 0x40
+ field BRDADDR 0x38
+ field BRDEN 0x04
+ field BRDRW 0x02
+ field BRDSTB 0x01
+}
+
+/*
+ * Serial EEPROM Address
+ */
+register SEEADR {
+ address 0x0BA
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Serial EEPROM Data
+ */
+register SEEDAT {
+ address 0x0BC
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Serial EEPROM Status
+ */
+register SEESTAT {
+ address 0x0BE
+ access_mode RO
+ modes M_SCSI
+ field INIT_DONE 0x80
+ field SEEOPCODE 0x70
+ field LDALTID_L 0x08
+ field SEEARBACK 0x04
+ field SEEBUSY 0x02
+ field SEESTART 0x01
+}
+
+/*
+ * Serial EEPROM Control
+ */
+register SEECTL {
+ address 0x0BE
+ access_mode RW
+ modes M_SCSI
+ field SEEOPCODE 0x70 {
+ SEEOP_ERASE 0x70,
+ SEEOP_READ 0x60,
+ SEEOP_WRITE 0x50,
+ /*
+ * The following four commands use special
+ * addresses for differentiation.
+ */
+ SEEOP_ERAL 0x40
+ }
+ mask SEEOP_EWEN 0x40
+ mask SEEOP_WALL 0x40
+ mask SEEOP_EWDS 0x40
+ field SEERST 0x02
+ field SEESTART 0x01
+}
+
+const SEEOP_ERAL_ADDR 0x80
+const SEEOP_EWEN_ADDR 0xC0
+const SEEOP_WRAL_ADDR 0x40
+const SEEOP_EWDS_ADDR 0x00
+
+/*
+ * SCB Counter
+ */
+register SCBCNT {
+ address 0x0BF
+ access_mode RW
+ modes M_SCSI
+}
+
+/*
+ * Data FIFO Write Address
+ * Pointer to the next QWD location to be written to the data FIFO.
+ */
+register DFWADDR {
+ address 0x0C0
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * DSP Filter Control
+ */
+register DSPFLTRCTL {
+ address 0x0C0
+ access_mode RW
+ modes M_CFG
+ field FLTRDISABLE 0x20
+ field EDGESENSE 0x10
+ field DSPFCNTSEL 0x0F
+}
+
+/*
+ * DSP Data Channel Control
+ */
+register DSPDATACTL {
+ address 0x0C1
+ access_mode RW
+ modes M_CFG
+ field BYPASSENAB 0x80
+ field DESQDIS 0x10
+ field RCVROFFSTDIS 0x04
+ field XMITOFFSTDIS 0x02
+}
+
+/*
+ * Data FIFO Read Address
+ * Pointer to the next QWD location to be read from the data FIFO.
+ */
+register DFRADDR {
+ address 0x0C2
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * DSP REQ Control
+ */
+register DSPREQCTL {
+ address 0x0C2
+ access_mode RW
+ modes M_CFG
+ field MANREQCTL 0xC0
+ field MANREQDLY 0x3F
+}
+
+/*
+ * DSP ACK Control
+ */
+register DSPACKCTL {
+ address 0x0C3
+ access_mode RW
+ modes M_CFG
+ field MANACKCTL 0xC0
+ field MANACKDLY 0x3F
+}
+
+/*
+ * Data FIFO Data
+ * Read/Write byte port into the data FIFO. The read and write
+ * FIFO pointers increment with each read and write respectively
+ * to this port.
+ */
+register DFDAT {
+ address 0x0C4
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * DSP Channel Select
+ */
+register DSPSELECT {
+ address 0x0C4
+ access_mode RW
+ modes M_CFG
+ field AUTOINCEN 0x80
+ field DSPSEL 0x1F
+}
+
+const NUMDSPS 0x14
+
+/*
+ * Write Bias Control
+ */
+register WRTBIASCTL {
+ address 0x0C5
+ access_mode WO
+ modes M_CFG
+ field AUTOXBCDIS 0x80
+ field XMITMANVAL 0x3F
+}
+
+/*
+ * Currently the WRTBIASCTL is the same as the default.
+ */
+const WRTBIASCTL_HP_DEFAULT 0x0
+
+/*
+ * Receiver Bias Control
+ */
+register RCVRBIOSCTL {
+ address 0x0C6
+ access_mode WO
+ modes M_CFG
+ field AUTORBCDIS 0x80
+ field RCVRMANVAL 0x3F
+}
+
+/*
+ * Write Bias Calculator
+ */
+register WRTBIASCALC {
+ address 0x0C7
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * Data FIFO Pointers
+ * Contains the byte offset from DFWADDR and DWRADDR to the current
+ * FIFO write/read locations.
+ */
+register DFPTRS {
+ address 0x0C8
+ access_mode RW
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Receiver Bias Calculator
+ */
+register RCVRBIASCALC {
+ address 0x0C8
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * Data FIFO Backup Read Pointer
+ * Contains the data FIFO address to be restored if the last
+ * data accessed from the data FIFO was not transferred successfully.
+ */
+register DFBKPTR {
+ address 0x0C9
+ access_mode RW
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Skew Calculator
+ */
+register SKEWCALC {
+ address 0x0C9
+ access_mode RO
+ modes M_CFG
+}
+
+/*
+ * Data FIFO Debug Control
+ */
+register DFDBCTL {
+ address 0x0CB
+ access_mode RW
+ modes M_DFF0, M_DFF1
+ field DFF_CIO_WR_RDY 0x20
+ field DFF_CIO_RD_RDY 0x10
+ field DFF_DIR_ERR 0x08
+ field DFF_RAMBIST_FAIL 0x04
+ field DFF_RAMBIST_DONE 0x02
+ field DFF_RAMBIST_EN 0x01
+}
+
+/*
+ * Data FIFO Space Count
+ * Number of FIFO locations that are free.
+ */
+register DFSCNT {
+ address 0x0CC
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Data FIFO Byte Count
+ * Number of filled FIFO locations.
+ */
+register DFBCNT {
+ address 0x0CE
+ access_mode RO
+ size 2
+ modes M_DFF0, M_DFF1
+}
+
+/*
+ * Sequencer Program Overlay Address.
+ * Low address must be written prior to high address.
+ */
+register OVLYADDR {
+ address 0x0D4
+ modes M_SCSI
+ size 2
+ access_mode RW
+}
+
+/*
+ * Sequencer Control 0
+ * Error detection mode, speed configuration,
+ * single step, breakpoints and program load.
+ */
+register SEQCTL0 {
+ address 0x0D6
+ access_mode RW
+ field PERRORDIS 0x80
+ field PAUSEDIS 0x40
+ field FAILDIS 0x20
+ field FASTMODE 0x10
+ field BRKADRINTEN 0x08
+ field STEP 0x04
+ field SEQRESET 0x02
+ field LOADRAM 0x01
+}
+
+/*
+ * Sequencer Control 1
+ * Instruction RAM Diagnostics
+ */
+register SEQCTL1 {
+ address 0x0D7
+ access_mode RW
+ field OVRLAY_DATA_CHK 0x08
+ field RAMBIST_DONE 0x04
+ field RAMBIST_FAIL 0x02
+ field RAMBIST_EN 0x01
+}
+
+/*
+ * Sequencer Flags
+ * Zero and Carry state of the ALU.
+ */
+register FLAGS {
+ address 0x0D8
+ access_mode RO
+ field ZERO 0x02
+ field CARRY 0x01
+}
+
+/*
+ * Sequencer Interrupt Control
+ */
+register SEQINTCTL {
+ address 0x0D9
+ access_mode RW
+ field INTVEC1DSL 0x80
+ field INT1_CONTEXT 0x20
+ field SCS_SEQ_INT1M1 0x10
+ field SCS_SEQ_INT1M0 0x08
+ field INTMASK2 0x04
+ field INTMASK1 0x02
+ field IRET 0x01
+}
+
+/*
+ * Sequencer RAM Data Port
+ * Single byte window into the Sequencer Instruction Ram area starting
+ * at the address specified by OVLYADDR. To write a full instruction word,
+ * simply write four bytes in succession. OVLYADDR will increment after the
+ * most significant instrution byte (the byte with the parity bit) is written.
+ */
+register SEQRAM {
+ address 0x0DA
+ access_mode RW
+}
+
+/*
+ * Sequencer Program Counter
+ * Low byte must be written prior to high byte.
+ */
+register PRGMCNT {
+ address 0x0DE
+ access_mode RW
+ size 2
+}
+
+/*
+ * Accumulator
+ */
+register ACCUM {
+ address 0x0E0
+ access_mode RW
+ accumulator
+}
+
+/*
+ * Source Index Register
+ * Incrementing index for reads of SINDIR and the destination (low byte only)
+ * for any immediate operands passed in jmp, jc, jnc, call instructions.
+ * Example:
+ * mvi 0xFF call some_routine;
+ *
+ * Will set SINDEX[0] to 0xFF and call the routine "some_routine.
+ */
+register SINDEX {
+ address 0x0E2
+ access_mode RW
+ size 2
+ sindex
+}
+
+/*
+ * Destination Index Register
+ * Incrementing index for writes to DINDIR. Can be used as a scratch register.
+ */
+register DINDEX {
+ address 0x0E4
+ access_mode RW
+ size 2
+}
+
+/*
+ * Break Address
+ * Sequencer instruction breakpoint address address.
+ */
+register BRKADDR0 {
+ address 0x0E6
+ access_mode RW
+}
+
+register BRKADDR1 {
+ address 0x0E6
+ access_mode RW
+ field BRKDIS 0x80 /* Disable Breakpoint */
+}
+
+/*
+ * All Ones
+ * All reads to this register return the value 0xFF.
+ */
+register ALLONES {
+ address 0x0E8
+ access_mode RO
+ allones
+}
+
+/*
+ * All Zeros
+ * All reads to this register return the value 0.
+ */
+register ALLZEROS {
+ address 0x0EA
+ access_mode RO
+ allzeros
+}
+
+/*
+ * No Destination
+ * Writes to this register have no effect.
+ */
+register NONE {
+ address 0x0EA
+ access_mode WO
+ none
+}
+
+/*
+ * Source Index Indirect
+ * Reading this register is equivalent to reading (register_base + SINDEX) and
+ * incrementing SINDEX by 1.
+ */
+register SINDIR {
+ address 0x0EC
+ access_mode RO
+}
+
+/*
+ * Destination Index Indirect
+ * Writing this register is equivalent to writing to (register_base + DINDEX)
+ * and incrementing DINDEX by 1.
+ */
+register DINDIR {
+ address 0x0ED
+ access_mode WO
+}
+
+/*
+ * Function One
+ * 2's complement to bit value conversion. Write the 2's complement value
+ * (0-7 only) to the top nibble and retrieve the bit indexed by that value
+ * on the next read of this register.
+ * Example:
+ * Write 0x60
+ * Read 0x40
+ */
+register FUNCTION1 {
+ address 0x0F0
+ access_mode RW
+}
+
+/*
+ * Stack
+ * Window into the stack. Each stack location is 10 bits wide reported
+ * low byte followed by high byte. There are 8 stack locations.
+ */
+register STACK {
+ address 0x0F2
+ access_mode RW
+}
+
+/*
+ * Interrupt Vector 1 Address
+ * Interrupt branch address for SCS SEQ_INT1 mode 0 and 1 interrupts.
+ */
+register INTVEC1_ADDR {
+ address 0x0F4
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Current Address
+ * Address of the SEQRAM instruction currently executing instruction.
+ */
+register CURADDR {
+ address 0x0F4
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+/*
+ * Interrupt Vector 2 Address
+ * Interrupt branch address for HST_SEQ_INT2 interrupts.
+ */
+register INTVEC2_ADDR {
+ address 0x0F6
+ access_mode RW
+ size 2
+ modes M_CFG
+}
+
+/*
+ * Last Address
+ * Address of the SEQRAM instruction executed prior to the current instruction.
+ */
+register LASTADDR {
+ address 0x0F6
+ access_mode RW
+ size 2
+ modes M_SCSI
+}
+
+register AHD_PCI_CONFIG_BASE {
+ address 0x100
+ access_mode RW
+ size 256
+ modes M_CFG
+}
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+scratch_ram {
+ /* Mode Specific */
+ address 0x0A0
+ size 8
+ modes 0, 1, 2, 3
+ REG0 {
+ size 2
+ }
+ REG1 {
+ size 2
+ }
+ REG_ISR {
+ size 2
+ }
+ SG_STATE {
+ size 1
+ field SEGS_AVAIL 0x01
+ field LOADING_NEEDED 0x02
+ field FETCH_INPROG 0x04
+ }
+ /*
+ * Track whether the transfer byte count for
+ * the current data phase is odd.
+ */
+ DATA_COUNT_ODD {
+ size 1
+ }
+}
+
+scratch_ram {
+ /* Mode Specific */
+ address 0x0F8
+ size 8
+ modes 0, 1, 2, 3
+ LONGJMP_ADDR {
+ size 2
+ }
+ ACCUM_SAVE {
+ size 1
+ }
+}
+
+
+scratch_ram {
+ address 0x100
+ size 128
+ modes 0, 1, 2, 3
+ /*
+ * Per "other-id" execution queues. We use an array of
+ * tail pointers into lists of SCBs sorted by "other-id".
+ * The execution head pointer threads the head SCBs for
+ * each list.
+ */
+ WAITING_SCB_TAILS {
+ size 32
+ }
+ WAITING_TID_HEAD {
+ size 2
+ }
+ WAITING_TID_TAIL {
+ size 2
+ }
+ /*
+ * SCBID of the next SCB in the new SCB queue.
+ */
+ NEXT_QUEUED_SCB_ADDR {
+ size 4
+ }
+ /*
+ * head of list of SCBs that have
+ * completed but have not been
+ * put into the qoutfifo.
+ */
+ COMPLETE_SCB_HEAD {
+ size 2
+ }
+ /*
+ * The list of completed SCBs in
+ * the active DMA.
+ */
+ COMPLETE_SCB_DMAINPROG_HEAD {
+ size 2
+ }
+ /*
+ * head of list of SCBs that have
+ * completed but need to be uploaded
+ * to the host prior to being completed.
+ */
+ COMPLETE_DMA_SCB_HEAD {
+ size 2
+ }
+ /* Counting semaphore to prevent new select-outs */
+ QFREEZE_COUNT {
+ size 2
+ }
+ /*
+ * Mode to restore on legacy idle loop exit.
+ */
+ SAVED_MODE {
+ size 1
+ }
+ /*
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
+ */
+ MSG_OUT {
+ size 1
+ }
+ /* Parameters for DMA Logic */
+ DMAPARAMS {
+ size 1
+ field PRELOADEN 0x80
+ field WIDEODD 0x40
+ field SCSIEN 0x20
+ field SDMAEN 0x10
+ field SDMAENACK 0x10
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04 /* Set indicates PCI->SCSI */
+ field FIFOFLUSH 0x02
+ field FIFORESET 0x01
+ }
+ SEQ_FLAGS {
+ size 1
+ field NOT_IDENTIFIED 0x80
+ field NO_CDB_SENT 0x40
+ field TARGET_CMD_IS_TAGGED 0x40
+ field DPHASE 0x20
+ /* Target flags */
+ field TARG_CMD_PENDING 0x10
+ field CMDPHASE_PENDING 0x08
+ field DPHASE_PENDING 0x04
+ field SPHASE_PENDING 0x02
+ field NO_DISCONNECT 0x01
+ }
+ /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+ SAVED_SCSIID {
+ size 1
+ }
+ SAVED_LUN {
+ size 1
+ }
+ /*
+ * The last bus phase as seen by the sequencer.
+ */
+ LASTPHASE {
+ size 1
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ field P_BUSFREE 0x01
+ enum PHASE_MASK CDO|IOO|MSGO {
+ P_DATAOUT 0x0,
+ P_DATAIN IOO,
+ P_DATAOUT_DT P_DATAOUT|MSGO,
+ P_DATAIN_DT P_DATAIN|MSGO,
+ P_COMMAND CDO,
+ P_MESGOUT CDO|MSGO,
+ P_STATUS CDO|IOO,
+ P_MESGIN CDO|IOO|MSGO
+ }
+ }
+ /*
+ * Value to "or" into the SCBPTR[1] value to
+ * indicate that an entry in the QINFIFO is valid.
+ */
+ QOUTFIFO_ENTRY_VALID_TAG {
+ size 1
+ }
+ /*
+ * Base address of our shared data with the kernel driver in host
+ * memory. This includes the qoutfifo and target mode
+ * incoming command queue.
+ */
+ SHARED_DATA_ADDR {
+ size 4
+ }
+ /*
+ * Pointer to location in host memory for next
+ * position in the qoutfifo.
+ */
+ QOUTFIFO_NEXT_ADDR {
+ size 4
+ }
+ /*
+ * Kernel and sequencer offsets into the queue of
+ * incoming target mode command descriptors. The
+ * queue is full when the KERNEL_TQINPOS == TQINPOS.
+ */
+ KERNEL_TQINPOS {
+ size 1
+ }
+ TQINPOS {
+ size 1
+ }
+ ARG_1 {
+ size 1
+ mask SEND_MSG 0x80
+ mask SEND_SENSE 0x40
+ mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
+ mask EXIT_MSG_LOOP 0x08
+ mask CONT_MSG_LOOP_WRITE 0x04
+ mask CONT_MSG_LOOP_READ 0x03
+ mask CONT_MSG_LOOP_TARG 0x02
+ alias RETURN_1
+ }
+ ARG_2 {
+ size 1
+ alias RETURN_2
+ }
+
+ /*
+ * Snapshot of MSG_OUT taken after each message is sent.
+ */
+ LAST_MSG {
+ size 1
+ }
+
+ /*
+ * Sequences the kernel driver has okayed for us. This allows
+ * the driver to do things like prevent initiator or target
+ * operations.
+ */
+ SCSISEQ_TEMPLATE {
+ size 1
+ field MANUALCTL 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field MANUALP 0x0C
+ field ENAUTOATNP 0x02
+ field ALTSTIM 0x01
+ }
+
+ /*
+ * The initiator specified tag for this target mode transaction.
+ */
+ INITIATOR_TAG {
+ size 1
+ }
+
+ SEQ_FLAGS2 {
+ size 1
+ field TARGET_MSG_PENDING 0x02
+ field SELECTOUT_QFROZEN 0x04
+ }
+
+ ALLOCFIFO_SCBPTR {
+ size 2
+ }
+
+ /*
+ * The maximum amount of time to wait, when interrupt coalescing
+ * is enabled, before issueing a CMDCMPLT interrupt for a completed
+ * command.
+ */
+ INT_COALESCING_TIMER {
+ size 2
+ }
+
+ /*
+ * The maximum number of commands to coalesce into a single interrupt.
+ * Actually the 2's complement of that value to simplify sequencer
+ * code.
+ */
+ INT_COALESCING_MAXCMDS {
+ size 1
+ }
+
+ /*
+ * The minimum number of commands still outstanding required
+ * to continue coalescing (2's complement of value).
+ */
+ INT_COALESCING_MINCMDS {
+ size 1
+ }
+
+ /*
+ * Number of commands "in-flight".
+ */
+ CMDS_PENDING {
+ size 2
+ }
+
+ /*
+ * The count of commands that have been coalesced.
+ */
+ INT_COALESCING_CMDCOUNT {
+ size 1
+ }
+
+ /*
+ * Since the HS_MAIBOX is self clearing, copy its contents to
+ * this position in scratch ram every time it changes.
+ */
+ LOCAL_HS_MAILBOX {
+ size 1
+ }
+ /*
+ * Target-mode CDB type to CDB length table used
+ * in non-packetized operation.
+ */
+ CMDSIZE_TABLE {
+ size 8
+ }
+}
+
+/************************* Hardware SCB Definition ****************************/
+scb {
+ address 0x180
+ size 64
+ modes 0, 1, 2, 3
+ SCB_RESIDUAL_DATACNT {
+ size 4
+ alias SCB_CDB_STORE
+ alias SCB_HOST_CDB_PTR
+ }
+ SCB_RESIDUAL_SGPTR {
+ size 4
+ field SG_ADDR_MASK 0xf8 /* In the last byte */
+ field SG_OVERRUN_RESID 0x02 /* In the first byte */
+ field SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_SCSI_STATUS {
+ size 1
+ alias SCB_HOST_CDB_LEN
+ }
+ SCB_TARGET_PHASES {
+ size 1
+ }
+ SCB_TARGET_DATA_DIR {
+ size 1
+ }
+ SCB_TARGET_ITAG {
+ size 1
+ }
+ SCB_SENSE_BUSADDR {
+ /*
+ * Only valid if CDB length is less than 13 bytes or
+ * we are using a CDB pointer. Otherwise contains
+ * the last 4 bytes of embedded cdb information.
+ */
+ size 4
+ alias SCB_NEXT_COMPLETE
+ }
+ SCB_TAG {
+ alias SCB_FIFO_USE_COUNT
+ size 2
+ }
+ SCB_CONTROL {
+ size 1
+ field TARGET_SCB 0x80
+ field DISCENB 0x40
+ field TAG_ENB 0x20
+ field MK_MESSAGE 0x10
+ field STATUS_RCVD 0x08
+ field DISCONNECTED 0x04
+ field SCB_TAG_TYPE 0x03
+ }
+ SCB_SCSIID {
+ size 1
+ field TID 0xF0
+ field OID 0x0F
+ }
+ SCB_LUN {
+ size 1
+ field LID 0xff
+ }
+ SCB_TASK_ATTRIBUTE {
+ size 1
+ /*
+ * Overloaded field for non-packetized
+ * ignore wide residue message handling.
+ */
+ field SCB_XFERLEN_ODD 0x01
+ }
+ SCB_CDB_LEN {
+ size 1
+ field SCB_CDB_LEN_PTR 0x80 /* CDB in host memory */
+ }
+ SCB_TASK_MANAGEMENT {
+ size 1
+ }
+ SCB_DATAPTR {
+ size 8
+ }
+ SCB_DATACNT {
+ /*
+ * The last byte is really the high address bits for
+ * the data address.
+ */
+ size 4
+ field SG_LAST_SEG 0x80 /* In the fourth byte */
+ field SG_HIGH_ADDR_BITS 0x7F /* In the fourth byte */
+ }
+ SCB_SGPTR {
+ size 4
+ field SG_STATUS_VALID 0x04 /* In the first byte */
+ field SG_FULL_RESID 0x02 /* In the first byte */
+ field SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_BUSADDR {
+ size 4
+ }
+ SCB_NEXT {
+ alias SCB_NEXT_SCB_BUSADDR
+ size 2
+ }
+ SCB_NEXT2 {
+ size 2
+ }
+ SCB_SPARE {
+ size 8
+ alias SCB_PKT_LUN
+ }
+ SCB_DISCONNECTED_LISTS {
+ size 8
+ }
+}
+
+/*********************************** Constants ********************************/
+const MK_MESSAGE_BIT_OFFSET 4
+const TID_SHIFT 4
+const TARGET_CMD_CMPLT 0xfe
+const INVALID_ADDR 0x80
+#define SCB_LIST_NULL 0xff
+#define QOUTFIFO_ENTRY_VALID_TOGGLE 0x80
+
+const CCSGADDR_MAX 0x80
+const CCSCBADDR_MAX 0x80
+const CCSGRAM_MAXSEGS 16
+
+/* Selection Timeout Timer Constants */
+const STIMESEL_SHIFT 3
+const STIMESEL_MIN 0x18
+const STIMESEL_BUG_ADJ 0x8
+
+/* WDTR Message values */
+const BUS_8_BIT 0x00
+const BUS_16_BIT 0x01
+const BUS_32_BIT 0x02
+
+/* Offset maximums */
+const MAX_OFFSET 0xfe
+const MAX_OFFSET_PACED 0xfe
+const MAX_OFFSET_PACED_BUG 0x7f
+/*
+ * Some 160 devices incorrectly accept 0xfe as a
+ * sync offset, but will overrun this value. Limit
+ * to 0x7f for speed lower than U320 which will
+ * avoid the persistent sync offset overruns.
+ */
+const MAX_OFFSET_NON_PACED 0x7f
+const HOST_MSG 0xff
+
+/*
+ * The size of our sense buffers.
+ * Sense buffer mapping can be handled in either of two ways.
+ * The first is to allocate a dmamap for each transaction.
+ * Depending on the architecture, dmamaps can be costly. The
+ * alternative is to statically map the buffers in much the same
+ * way we handle our scatter gather lists. The driver implements
+ * the later.
+ */
+const AHD_SENSE_BUFSIZE 256
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+
+const STATUS_BUSY 0x08
+const STATUS_QUEUE_FULL 0x28
+const STATUS_PKT_SENSE 0xFF
+const TARGET_DATA_IN 1
+
+const SCB_TRANSFER_SIZE_FULL_LUN 56
+const SCB_TRANSFER_SIZE_1BYTE_LUN 48
+/* PKT_OVERRUN_BUFSIZE must be a multiple of 256 less than 64K */
+const PKT_OVERRUN_BUFSIZE 512
+
+/*
+ * Timer parameters.
+ */
+const AHD_TIMER_US_PER_TICK 25
+const AHD_TIMER_MAX_TICKS 0xFFFF
+const AHD_TIMER_MAX_US (AHD_TIMER_MAX_TICKS * AHD_TIMER_US_PER_TICK)
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_CNT_LIMIT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
+const SG_SIZEOF download
+const PKT_OVERRUN_BUFOFFSET download
+const SCB_TRANSFER_SIZE download
+
+/*
+ * BIOS SCB offsets
+ */
+const NVRAM_SCB_OFFSET 0x2C
diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq
new file mode 100644
index 000000000000..65339bc1ca99
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.seq
@@ -0,0 +1,2058 @@
+/*
+ * Adaptec U320 device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#99 $"
+PATCH_ARG_LIST = "struct ahd_softc *ahd"
+PREFIX = "ahd_"
+
+#include "aic79xx.reg"
+#include "scsi_message.h"
+
+restart:
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ test SEQINTCODE, 0xFF jz idle_loop;
+ SET_SEQINTCODE(NO_SEQINT)
+}
+
+idle_loop:
+
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ /*
+ * Convert ERROR status into a sequencer
+ * interrupt to handle the case of an
+ * interrupt collision on the hardware
+ * setting of HWERR.
+ */
+ test ERROR, 0xFF jz no_error_set;
+ SET_SEQINTCODE(SAW_HWERR)
+no_error_set:
+ }
+ SET_MODE(M_SCSI, M_SCSI)
+ test SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus;
+ test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz idle_loop_checkbus;
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus;
+ /*
+ * ENSELO is cleared by a SELDO, so we must test for SELDO
+ * one last time.
+ */
+BEGIN_CRITICAL;
+ test SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+ call start_selection;
+idle_loop_checkbus:
+BEGIN_CRITICAL;
+ test SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+ test SSTAT0, SELDI jnz select_in;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz idle_loop_check_nonpackreq;
+ test SCSISIGO, ATNO jz idle_loop_check_nonpackreq;
+ call unexpected_nonpkt_phase_find_ctxt;
+idle_loop_check_nonpackreq:
+ test SSTAT2, NONPACKREQ jz . + 2;
+ call unexpected_nonpkt_phase_find_ctxt;
+ if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+ and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+ cmp A, FIFO0FREE|FIFO1FREE jne . + 3;
+ and SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
+ jmp . + 2;
+ or SBLKCTL, DIAGLEDEN|DIAGLEDON;
+ }
+ call idle_loop_gsfifo_in_scsi_mode;
+ call idle_loop_service_fifos;
+ call idle_loop_cchan;
+ jmp idle_loop;
+
+BEGIN_CRITICAL;
+idle_loop_gsfifo:
+ SET_MODE(M_SCSI, M_SCSI)
+idle_loop_gsfifo_in_scsi_mode:
+ test LQISTAT2, LQIGSAVAIL jz return;
+ /*
+ * We have received good status for this transaction. There may
+ * still be data in our FIFOs draining to the host. Complete
+ * the SCB only if all data has transferred to the host.
+ */
+good_status_IU_done:
+ bmov SCBPTR, GSFIFO, 2;
+ clr SCB_SCSI_STATUS;
+ /*
+ * If a command completed before an attempted task management
+ * function completed, notify the host after disabling any
+ * pending select-outs.
+ */
+ test SCB_TASK_MANAGEMENT, 0xFF jz gsfifo_complete_normally;
+ test SSTAT0, SELDO|SELINGO jnz . + 2;
+ and SCSISEQ0, ~ENSELO;
+ SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
+gsfifo_complete_normally:
+ or SCB_CONTROL, STATUS_RCVD;
+
+ /*
+ * Since this status did not consume a FIFO, we have to
+ * be a bit more dilligent in how we check for FIFOs pertaining
+ * to this transaction. There are two states that a FIFO still
+ * transferring data may be in.
+ *
+ * 1) Configured and draining to the host, with a FIFO handler.
+ * 2) Pending cfg4data, fifo not empty.
+ *
+ * Case 1 can be detected by noticing a non-zero FIFO active
+ * count in the SCB. In this case, we allow the routine servicing
+ * the FIFO to complete the SCB.
+ *
+ * Case 2 implies either a pending or yet to occur save data
+ * pointers for this same context in the other FIFO. So, if
+ * we detect case 1, we will properly defer the post of the SCB
+ * and achieve the desired result. The pending cfg4data will
+ * notice that status has been received and complete the SCB.
+ */
+ test SCB_FIFO_USE_COUNT, 0xFF jnz idle_loop_gsfifo_in_scsi_mode;
+ call complete;
+END_CRITICAL;
+ jmp idle_loop_gsfifo_in_scsi_mode;
+
+idle_loop_service_fifos:
+ SET_MODE(M_DFF0, M_DFF0)
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
+ call longjmp;
+idle_loop_next_fifo:
+ SET_MODE(M_DFF1, M_DFF1)
+ test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
+return:
+ ret;
+
+idle_loop_cchan:
+ SET_MODE(M_CCHAN, M_CCHAN)
+ test QOFF_CTLSTA, HS_MAILBOX_ACT jz hs_mailbox_empty;
+ or QOFF_CTLSTA, HS_MAILBOX_ACT;
+ mov LOCAL_HS_MAILBOX, HS_MAILBOX;
+hs_mailbox_empty:
+BEGIN_CRITICAL;
+ test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
+ test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
+ test CCSCBCTL, CCSCBDONE jz return;
+END_CRITICAL;
+ /* FALLTHROUGH */
+scbdma_tohost_done:
+ test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
+ /*
+ * An SCB has been succesfully uploaded to the host.
+ * If the SCB was uploaded for some reason other than
+ * bad SCSI status (currently only for underruns), we
+ * queue the SCB for normal completion. Otherwise, we
+ * wait until any select-out activity has halted, and
+ * then notify the host so that the transaction can be
+ * dealt with.
+ */
+ test SCB_SCSI_STATUS, 0xff jnz scbdma_notify_host;
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+ bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+scbdma_notify_host:
+ SET_MODE(M_SCSI, M_SCSI)
+ test SCSISEQ0, ENSELO jnz return;
+ test SSTAT0, (SELDO|SELINGO) jnz return;
+ SET_MODE(M_CCHAN, M_CCHAN)
+ /*
+ * Remove SCB and notify host.
+ */
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ SET_SEQINTCODE(BAD_SCB_STATUS)
+ ret;
+fill_qoutfifo_dmadone:
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ call qoutfifo_updated;
+ mvi COMPLETE_SCB_DMAINPROG_HEAD[1], SCB_LIST_NULL;
+ bmov QOUTFIFO_NEXT_ADDR, SCBHADDR, 4;
+ test QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
+ bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4;
+ xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret;
+
+qoutfifo_updated:
+ /*
+ * If there are more commands waiting to be dma'ed
+ * to the host, always coalesce. Otherwise honor the
+ * host's wishes.
+ */
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne coalesce_by_count;
+ cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL jne coalesce_by_count;
+ test LOCAL_HS_MAILBOX, ENINT_COALESCE jz issue_cmdcmplt;
+
+ /*
+ * If we have relatively few commands outstanding, don't
+ * bother waiting for another command to complete.
+ */
+ test CMDS_PENDING[1], 0xFF jnz coalesce_by_count;
+ /* Add -1 so that jnc means <= not just < */
+ add A, -1, INT_COALESCING_MINCMDS;
+ add NONE, A, CMDS_PENDING;
+ jnc issue_cmdcmplt;
+
+ /*
+ * If coalescing, only coalesce up to the limit
+ * provided by the host driver.
+ */
+coalesce_by_count:
+ mov A, INT_COALESCING_MAXCMDS;
+ add NONE, A, INT_COALESCING_CMDCOUNT;
+ jc issue_cmdcmplt;
+ /*
+ * If the timer is not currently active,
+ * fire it up.
+ */
+ test INTCTL, SWTMINTMASK jz return;
+ bmov SWTIMER, INT_COALESCING_TIMER, 2;
+ mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+ or INTCTL, SWTMINTEN|SWTIMER_START;
+ and INTCTL, ~SWTMINTMASK ret;
+
+issue_cmdcmplt:
+ mvi INTSTAT, CMDCMPLT;
+ clr INT_COALESCING_CMDCOUNT;
+ or INTCTL, SWTMINTMASK ret;
+
+BEGIN_CRITICAL;
+fetch_new_scb_inprog:
+ test CCSCBCTL, ARRDONE jz return;
+fetch_new_scb_done:
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov REG0, SCBPTR, 2;
+ clr A;
+ add CMDS_PENDING, 1;
+ adc CMDS_PENDING[1], A;
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+ /*
+ * "Short Luns" are not placed into outgoing LQ
+ * packets in the correct byte order. Use a full
+ * sized lun field instead and fill it with the
+ * one byte of lun information we support.
+ */
+ mov SCB_PKT_LUN[6], SCB_LUN;
+ }
+ /*
+ * The FIFO use count field is shared with the
+ * tag set by the host so that our SCB dma engine
+ * knows the correct location to store the SCB.
+ * Set it to zero before processing the SCB.
+ */
+ clr SCB_FIFO_USE_COUNT;
+ /* Update the next SCB address to download. */
+ bmov NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4;
+ mvi SCB_NEXT[1], SCB_LIST_NULL;
+ mvi SCB_NEXT2[1], SCB_LIST_NULL;
+ /* Increment our position in the QINFIFO. */
+ mov NONE, SNSCB_QOFF;
+ /*
+ * SCBs that want to send messages are always
+ * queued independently. This ensures that they
+ * are at the head of the SCB list to select out
+ * to a target and we will see the MK_MESSAGE flag.
+ */
+ test SCB_CONTROL, MK_MESSAGE jnz first_new_target_scb;
+ shr SINDEX, 3, SCB_SCSIID;
+ and SINDEX, ~0x1;
+ mvi SINDEX[1], (WAITING_SCB_TAILS >> 8);
+ bmov DINDEX, SINDEX, 2;
+ bmov SCBPTR, SINDIR, 2;
+ bmov DINDIR, REG0, 2;
+ cmp SCBPTR[1], SCB_LIST_NULL je first_new_target_scb;
+ bmov SCB_NEXT, REG0, 2 ret;
+first_new_target_scb:
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je first_new_scb;
+ bmov SCBPTR, WAITING_TID_TAIL, 2;
+ bmov SCB_NEXT2, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2 ret;
+first_new_scb:
+ bmov WAITING_TID_HEAD, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2 ret;
+END_CRITICAL;
+
+scbdma_idle:
+ /*
+ * Give precedence to downloading new SCBs to execute
+ * unless select-outs are currently frozen.
+ */
+ test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz . + 2;
+BEGIN_CRITICAL;
+ test QOFF_CTLSTA, NEW_SCB_AVAIL jnz fetch_new_scb;
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne dma_complete_scb;
+ cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL je return;
+ /* FALLTHROUGH */
+fill_qoutfifo:
+ /*
+ * Keep track of the SCBs we are dmaing just
+ * in case the DMA fails or is aborted.
+ */
+ mov A, QOUTFIFO_ENTRY_VALID_TAG;
+ bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
+ bmov SCBPTR, COMPLETE_SCB_HEAD, 2;
+fill_qoutfifo_loop:
+ mov CCSCBRAM, SCBPTR;
+ or CCSCBRAM, A, SCBPTR[1];
+ mov NONE, SDSCB_QOFF;
+ inc INT_COALESCING_CMDCOUNT;
+ add CMDS_PENDING, -1;
+ adc CMDS_PENDING[1], -1;
+ cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
+ cmp CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done;
+ test QOFF_CTLSTA, SDSCB_ROLLOVR jnz fill_qoutfifo_done;
+ bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
+ jmp fill_qoutfifo_loop;
+fill_qoutfifo_done:
+ mov SCBHCNT, CCSCBADDR;
+ mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
+ bmov COMPLETE_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ mvi SCB_NEXT_COMPLETE[1], SCB_LIST_NULL ret;
+
+fetch_new_scb:
+ bmov SCBHADDR, NEXT_QUEUED_SCB_ADDR, 4;
+ mvi CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET jmp dma_scb;
+dma_complete_scb:
+ bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
+ bmov SCBHADDR, SCB_BUSADDR, 4;
+ mvi CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb;
+END_CRITICAL;
+
+/*
+ * Either post or fetch an SCB from host memory. The caller
+ * is responsible for polling for transfer completion.
+ *
+ * Prerequisits: Mode == M_CCHAN
+ * SINDEX contains CCSCBCTL flags
+ * SCBHADDR set to Host SCB address
+ * SCBPTR set to SCB src location on "push" operations
+ */
+SET_SRC_MODE M_CCHAN;
+SET_DST_MODE M_CCHAN;
+dma_scb:
+ mvi SCBHCNT, SCB_TRANSFER_SIZE;
+ mov CCSCBCTL, SINDEX ret;
+
+BEGIN_CRITICAL;
+setjmp:
+ bmov LONGJMP_ADDR, STACK, 2 ret;
+setjmp_inline:
+ bmov LONGJMP_ADDR, STACK, 2;
+longjmp:
+ bmov STACK, LONGJMP_ADDR, 2 ret;
+END_CRITICAL;
+
+/*************************** Chip Bug Work Arounds ****************************/
+/*
+ * Must disable interrupts when setting the mode pointer
+ * register as an interrupt occurring mid update will
+ * fail to store the new mode value for restoration on
+ * an iret.
+ */
+if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+set_mode_work_around:
+ mvi SEQINTCTL, INTVEC1DSL;
+ mov MODE_PTR, SINDEX;
+ clr SEQINTCTL ret;
+
+toggle_dff_mode_work_around:
+ mvi SEQINTCTL, INTVEC1DSL;
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+ clr SEQINTCTL ret;
+}
+
+
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+set_seqint_work_around:
+ mov SEQINTCODE, SINDEX;
+ mvi SEQINTCODE, NO_SEQINT ret;
+}
+
+/************************ Packetized LongJmp Routines *************************/
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+start_selection:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+ /*
+ * Razor #494
+ * Rev A hardware fails to update LAST/CURR/NEXTSCB
+ * correctly after a packetized selection in several
+ * situations:
+ *
+ * 1) If only one command existed in the queue, the
+ * LAST/CURR/NEXTSCB are unchanged.
+ *
+ * 2) In a non QAS, protocol allowed phase change,
+ * the queue is shifted 1 too far. LASTSCB is
+ * the last SCB that was correctly processed.
+ *
+ * 3) In the QAS case, if the full list of commands
+ * was successfully sent, NEXTSCB is NULL and neither
+ * CURRSCB nor LASTSCB can be trusted. We must
+ * manually walk the list counting MAXCMDCNT elements
+ * to find the last SCB that was sent correctly.
+ *
+ * To simplify the workaround for this bug in SELDO
+ * handling, we initialize LASTSCB prior to enabling
+ * selection so we can rely on it even for case #1 above.
+ */
+ bmov LASTSCB, WAITING_TID_HEAD, 2;
+ }
+ bmov CURRSCB, WAITING_TID_HEAD, 2;
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ shr SELOID, 4, SCB_SCSIID;
+ /*
+ * If we want to send a message to the device, ensure
+ * we are selecting with atn irregardless of our packetized
+ * agreement. Since SPI4 only allows target reset or PPR
+ * messages if this is a packetized connection, the change
+ * to our negotiation table entry for this selection will
+ * be cleared when the message is acted on.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 3;
+ mov NEGOADDR, SELOID;
+ or NEGCONOPTS, ENAUTOATNO;
+ or SCSISEQ0, ENSELO ret;
+END_CRITICAL;
+
+/*
+ * Allocate a FIFO for a non-packetized transaction.
+ * In RevA hardware, both FIFOs must be free before we
+ * can allocate a FIFO for a non-packetized transaction.
+ */
+allocate_fifo_loop:
+ /*
+ * Do whatever work is required to free a FIFO.
+ */
+ call idle_loop_service_fifos;
+ SET_MODE(M_SCSI, M_SCSI)
+allocate_fifo:
+ if ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0) {
+ and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+ cmp A, FIFO0FREE|FIFO1FREE jne allocate_fifo_loop;
+ } else {
+ test DFFSTAT, FIFO1FREE jnz allocate_fifo1;
+ test DFFSTAT, FIFO0FREE jz allocate_fifo_loop;
+ mvi DFFSTAT, B_CURRFIFO_0;
+ SET_MODE(M_DFF0, M_DFF0)
+ bmov SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+ }
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+allocate_fifo1:
+ mvi DFFSTAT, CURRFIFO_1;
+ SET_MODE(M_DFF1, M_DFF1)
+ bmov SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+
+/*
+ * We have been reselected as an initiator
+ * or selected as a target.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+select_in:
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ /*
+ * Test to ensure that the bus has not
+ * already gone free prior to clearing
+ * any stale busfree status. This avoids
+ * a window whereby a busfree just after
+ * a selection could be missed.
+ */
+ test SCSISIGI, BSYI jz . + 2;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE;
+ }
+ or SXFRCTL0, SPIOEN;
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ and A, OID, IOWNID;
+ or SAVED_SCSIID, A;
+ mvi CLRSINT0, CLRSELDI;
+ jmp ITloop;
+
+/*
+ * We have successfully selected out.
+ *
+ * Clear SELDO.
+ * Dequeue all SCBs sent from the waiting queue
+ * Requeue all SCBs *not* sent to the tail of the waiting queue
+ * Take Razor #494 into account for above.
+ *
+ * In Packetized Mode:
+ * Return to the idle loop. Our interrupt handler will take
+ * care of any incoming L_Qs.
+ *
+ * In Non-Packetize Mode:
+ * Continue to our normal state machine.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+select_out:
+BEGIN_CRITICAL;
+ /* Clear out all SCBs that have been successfully sent. */
+ if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+ /*
+ * For packetized, the LQO manager clears ENSELO on
+ * the assertion of SELDO. If we are non-packetized,
+ * LASTSCB and CURRSCB are accurate.
+ */
+ test SCSISEQ0, ENSELO jnz use_lastscb;
+
+ /*
+ * The update is correct for LQOSTAT1 errors. All
+ * but LQOBUSFREE are handled by kernel interrupts.
+ * If we see LQOBUSFREE, return to the idle loop.
+ * Once we are out of the select_out critical section,
+ * the kernel will cleanup the LQOBUSFREE and we will
+ * eventually restart the selection if appropriate.
+ */
+ test LQOSTAT1, LQOBUSFREE jnz idle_loop;
+
+ /*
+ * On a phase change oustside of packet boundaries,
+ * LASTSCB points to the currently active SCB context
+ * on the bus.
+ */
+ test LQOSTAT2, LQOPHACHGOUTPKT jnz use_lastscb;
+
+ /*
+ * If the hardware has traversed the whole list, NEXTSCB
+ * will be NULL, CURRSCB and LASTSCB cannot be trusted,
+ * but MAXCMDCNT is accurate. If we stop part way through
+ * the list or only had one command to issue, NEXTSCB[1] is
+ * not NULL and LASTSCB is the last command to go out.
+ */
+ cmp NEXTSCB[1], SCB_LIST_NULL jne use_lastscb;
+
+ /*
+ * Brute force walk.
+ */
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ mvi SEQINTCTL, INTVEC1DSL;
+ mvi MODE_PTR, MK_MODE(M_CFG, M_CFG);
+ mov A, MAXCMDCNT;
+ mvi MODE_PTR, MK_MODE(M_SCSI, M_SCSI);
+ clr SEQINTCTL;
+find_lastscb_loop:
+ dec A;
+ test A, 0xFF jz found_last_sent_scb;
+ bmov SCBPTR, SCB_NEXT, 2;
+ jmp find_lastscb_loop;
+use_lastscb:
+ bmov SCBPTR, LASTSCB, 2;
+found_last_sent_scb:
+ bmov CURRSCB, SCBPTR, 2;
+curscb_ww_done:
+ } else {
+ bmov SCBPTR, CURRSCB, 2;
+ }
+
+ /*
+ * Requeue any SCBs not sent, to the tail of the waiting Q.
+ */
+ cmp SCB_NEXT[1], SCB_LIST_NULL je select_out_list_done;
+
+ /*
+ * We know that neither the per-TID list nor the list of
+ * TIDs is empty. Use this knowledge to our advantage.
+ */
+ bmov REG0, SCB_NEXT, 2;
+ bmov SCBPTR, WAITING_TID_TAIL, 2;
+ bmov SCB_NEXT2, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2;
+ jmp select_out_inc_tid_q;
+
+select_out_list_done:
+ /*
+ * The whole list made it. Just clear our TID's tail pointer
+ * unless we were queued independently due to our need to
+ * send a message.
+ */
+ test SCB_CONTROL, MK_MESSAGE jnz select_out_inc_tid_q;
+ shr DINDEX, 3, SCB_SCSIID;
+ or DINDEX, 1; /* Want only the second byte */
+ mvi DINDEX[1], ((WAITING_SCB_TAILS) >> 8);
+ mvi DINDIR, SCB_LIST_NULL;
+select_out_inc_tid_q:
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ bmov WAITING_TID_HEAD, SCB_NEXT2, 2;
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2;
+ mvi WAITING_TID_TAIL[1], SCB_LIST_NULL;
+ bmov SCBPTR, CURRSCB, 2;
+ mvi CLRSINT0, CLRSELDO;
+ test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase;
+ test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase;
+
+ /*
+ * If this is a packetized connection, return to our
+ * idle_loop and let our interrupt handler deal with
+ * any connection setup/teardown issues. The only
+ * exceptions are the case of MK_MESSAGE and task management
+ * SCBs.
+ */
+ if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
+ /*
+ * In the A, the LQO manager transitions to LQOSTOP0 even if
+ * we have selected out with ATN asserted and the target
+ * REQs in a non-packet phase.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz select_out_no_message;
+ test SCSISIGO, ATNO jnz select_out_non_packetized;
+select_out_no_message:
+ }
+ test LQOSTAT2, LQOSTOP0 jz select_out_non_packetized;
+ test SCB_TASK_MANAGEMENT, 0xFF jz idle_loop;
+ SET_SEQINTCODE(TASKMGMT_FUNC_COMPLETE)
+ jmp idle_loop;
+
+select_out_non_packetized:
+ /* Non packetized request. */
+ and SCSISEQ0, ~ENSELO;
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ /*
+ * Test to ensure that the bus has not
+ * already gone free prior to clearing
+ * any stale busfree status. This avoids
+ * a window whereby a busfree just after
+ * a selection could be missed.
+ */
+ test SCSISIGI, BSYI jz . + 2;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE;
+ }
+ mov SAVED_SCSIID, SCB_SCSIID;
+ mov SAVED_LUN, SCB_LUN;
+ mvi SEQ_FLAGS, NO_CDB_SENT;
+END_CRITICAL;
+ or SXFRCTL0, SPIOEN;
+
+ /*
+ * As soon as we get a successful selection, the target
+ * should go into the message out phase since we have ATN
+ * asserted.
+ */
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+
+ /*
+ * Main loop for information transfer phases. Wait for the
+ * target to assert REQ before checking MSG, C/D and I/O for
+ * the bus phase.
+ */
+mesgin_phasemis:
+ITloop:
+ call phase_lock;
+
+ mov A, LASTPHASE;
+
+ test A, ~P_DATAIN_DT jz p_data;
+ cmp A,P_COMMAND je p_command;
+ cmp A,P_MESGOUT je p_mesgout;
+ cmp A,P_STATUS je p_status;
+ cmp A,P_MESGIN je p_mesgin;
+
+ SET_SEQINTCODE(BAD_PHASE)
+ jmp ITloop; /* Try reading the bus again. */
+
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+ test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+ SET_SEQINTCODE(PROTO_VIOLATION)
+p_command_okay:
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz p_command_allocate_fifo;
+ /*
+ * Command retry. Free our current FIFO and
+ * re-allocate a FIFO so transfer state is
+ * reset.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+ SET_MODE(M_SCSI, M_SCSI)
+p_command_allocate_fifo:
+ bmov ALLOCFIFO_SCBPTR, SCBPTR, 2;
+ call allocate_fifo;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ add NONE, -17, SCB_CDB_LEN;
+ jnc p_command_embedded;
+p_command_from_host:
+ bmov HADDR[0], SCB_HOST_CDB_PTR, 9;
+ mvi SG_CACHE_PRE, LAST_SEG;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+ jmp p_command_xfer;
+p_command_embedded:
+ bmov SHCNT[0], SCB_CDB_LEN, 1;
+ bmov DFDAT, SCB_CDB_STORE, 16;
+ mvi DFCNTRL, SCSIEN;
+p_command_xfer:
+ and SEQ_FLAGS, ~NO_CDB_SENT;
+ if ((ahd->features & AHD_FAST_CDB_DELIVERY) != 0) {
+ /*
+ * To speed up CDB delivery in Rev B, all CDB acks
+ * are "released" to the output sync as soon as the
+ * command phase starts. There is only one problem
+ * with this approach. If the target changes phase
+ * before all data are sent, we have left over acks
+ * that can go out on the bus in a data phase. Due
+ * to other chip contraints, this only happens if
+ * the target goes to data-in, but if the acks go
+ * out before we can test SDONE, we'll think that
+ * the transfer has completed successfully. Work
+ * around this by taking advantage of the 400ns or
+ * 800ns dead time between command phase and the REQ
+ * of the new phase. If the transfer has completed
+ * successfully, SCSIEN should fall *long* before we
+ * see a phase change. We thus treat any phasemiss
+ * that occurs before SCSIEN falls as an incomplete
+ * transfer.
+ */
+ test SSTAT1, PHASEMIS jnz p_command_xfer_failed;
+ test DFCNTRL, SCSIEN jnz . - 1;
+ } else {
+ test DFCNTRL, SCSIEN jnz .;
+ }
+ /*
+ * DMA Channel automatically disabled.
+ * Don't allow a data phase if the command
+ * was not fully transferred.
+ */
+ test SSTAT2, SDONE jnz ITloop;
+p_command_xfer_failed:
+ or SEQ_FLAGS, NO_CDB_SENT;
+ jmp ITloop;
+
+
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+p_status:
+ test SEQ_FLAGS,NOT_IDENTIFIED jnz mesgin_proto_violation;
+p_status_okay:
+ mov SCB_SCSI_STATUS, SCSIDAT;
+ or SCB_CONTROL, STATUS_RCVD;
+ jmp ITloop;
+
+/*
+ * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target. The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte. This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own. If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+ /* Turn on ATN for the retry */
+ mvi SCSISIGO, ATNO;
+p_mesgout:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+ test SCB_CONTROL,MK_MESSAGE jnz host_message_loop;
+p_mesgout_identify:
+ or SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+ test SCB_CONTROL, DISCENB jnz . + 2;
+ and SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_NONPACKET_TAG as the tag value.
+ */
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDAT, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDAT,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCBPTR jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ jmp host_message_loop;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDAT, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
+
+p_mesgout_done:
+ mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
+ mov LAST_MSG, MSG_OUT;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
+ jmp ITloop;
+
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+ /* read the 1st message byte */
+ mvi ACCUM call inb_first;
+
+ test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
+ cmp A,MSG_DISCONNECT je mesgin_disconnect;
+ cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
+ cmp ALLZEROS,A je mesgin_complete;
+ cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
+ cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_ign_wide_residue;
+ cmp A,MSG_NOOP je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine. To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop. Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+ call phase_lock; /* Benign the first time through. */
+ SET_SEQINTCODE(HOST_MSG_LOOP)
+ cmp RETURN_1, EXIT_MSG_LOOP je ITloop;
+ cmp RETURN_1, CONT_MSG_LOOP_WRITE jne . + 3;
+ mov SCSIDAT, RETURN_2;
+ jmp host_message_loop;
+ /* Must be CONT_MSG_LOOP_READ */
+ mov NONE, SCSIDAT; /* ACK Byte */
+ jmp host_message_loop;
+
+mesgin_ign_wide_residue:
+ mov SAVED_MODE, MODE_PTR;
+ SET_MODE(M_SCSI, M_SCSI)
+ shr NEGOADDR, 4, SAVED_SCSIID;
+ mov A, NEGCONOPTS;
+ RESTORE_MODE(SAVED_MODE)
+ test A, WIDEXFER jz mesgin_reject;
+ /* Pull the residue byte */
+ mvi REG0 call inb_next;
+ cmp REG0, 0x01 jne mesgin_reject;
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+ test SCB_TASK_ATTRIBUTE, SCB_XFERLEN_ODD jnz mesgin_done;
+ SET_SEQINTCODE(IGN_WIDE_RES)
+ jmp mesgin_done;
+
+mesgin_proto_violation:
+ SET_SEQINTCODE(PROTO_VIOLATION)
+ jmp mesgin_done;
+mesgin_reject:
+ mvi MSG_MESSAGE_REJECT call mk_mesg;
+mesgin_done:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+ jmp ITloop;
+
+#define INDEX_DISC_LIST(scsiid, lun) \
+ and A, 0xC0, scsiid; \
+ or SCBPTR, A, lun; \
+ clr SCBPTR[1]; \
+ and SINDEX, 0x30, scsiid; \
+ shr SINDEX, 3; /* Multiply by 2 */ \
+ add SINDEX, (SCB_DISCONNECTED_LISTS & 0xFF); \
+ mvi SINDEX[1], ((SCB_DISCONNECTED_LISTS >> 8) & 0xFF)
+
+mesgin_identify:
+ /*
+ * Determine whether a target is using tagged or non-tagged
+ * transactions by first looking at the transaction stored in
+ * the per-device, disconnected array. If there is no untagged
+ * transaction for this target, this must be a tagged transaction.
+ */
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+ INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov REG0, SINDIR, 2;
+ cmp REG0[1], SCB_LIST_NULL je snoop_tag;
+ /* Untagged. Clear the busy table entry and setup the SCB. */
+ bmov DINDIR, ALLONES, 2;
+ bmov SCBPTR, REG0, 2;
+ jmp setup_SCB;
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB. After receiving the tag, look for the SCB at SCB locations tag and
+ * tag + 256.
+ */
+snoop_tag:
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x80;
+ }
+ mov NONE, SCSIDAT; /* ACK Identify MSG */
+ call phase_lock;
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x1;
+ }
+ cmp LASTPHASE, P_MESGIN jne not_found_ITloop;
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x2;
+ }
+ cmp SCSIBUS, MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+ clr SCBPTR[1];
+ mvi SCBPTR call inb_next; /* tag value */
+verify_scb:
+ test SCB_CONTROL,DISCONNECTED jz verify_other_scb;
+ mov A, SAVED_SCSIID;
+ cmp SCB_SCSIID, A jne verify_other_scb;
+ mov A, SAVED_LUN;
+ cmp SCB_LUN, A je setup_SCB_disconnected;
+verify_other_scb:
+ xor SCBPTR[1], 1;
+ test SCBPTR[1], 0xFF jnz verify_scb;
+ jmp not_found;
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x10;
+ }
+ test SCB_CONTROL,DISCONNECTED jz not_found;
+setup_SCB_disconnected:
+ and SCB_CONTROL,~DISCONNECTED;
+ clr SEQ_FLAGS; /* make note of IDENTIFY */
+ test SCB_SGPTR, SG_LIST_NULL jnz . + 3;
+ bmov ALLOCFIFO_SCBPTR, SCBPTR, 2;
+ call allocate_fifo;
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ mvi HOST_MSG call mk_mesg;
+ jmp mesgin_done;
+
+not_found:
+ SET_SEQINTCODE(NO_MATCH)
+ jmp mesgin_done;
+
+not_found_ITloop:
+ SET_SEQINTCODE(NO_MATCH)
+ jmp ITloop;
+
+/*
+ * We received a "command complete" message. Put the SCB on the complete
+ * queue and trigger a completion interrupt via the idle loop. Before doing
+ * so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information. In the case of a non zero status byte, we
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved. If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
+ * RETURN_1 to SEND_SENSE.
+ */
+mesgin_complete:
+
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again. We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+ /*
+ * If we are identified and have successfully sent the CDB,
+ * any status will do. Optimize this fast path.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+ /*
+ * If the target never sent an identify message but instead went
+ * to mesgin to give an invalid message, let the host abort us.
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+
+ /*
+ * If we recevied good status but never successfully sent the
+ * cdb, abort the command.
+ */
+ test SCB_SCSI_STATUS,0xff jnz complete_accepted;
+ test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+complete_accepted:
+
+ /*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz complete_nomsg;
+ SET_SEQINTCODE(MKMSG_FAILED)
+complete_nomsg:
+ call queue_scb_completion;
+ jmp await_busfree;
+
+freeze_queue:
+ /* Cancel any pending select-out. */
+ test SSTAT0, SELDO|SELINGO jnz . + 2;
+ and SCSISEQ0, ~ENSELO;
+ mov ACCUM_SAVE, A;
+ clr A;
+ add QFREEZE_COUNT, 1;
+ adc QFREEZE_COUNT[1], A;
+ or SEQ_FLAGS2, SELECTOUT_QFROZEN;
+ mov A, ACCUM_SAVE ret;
+
+/*
+ * Complete the current FIFO's SCB if data for this same
+ * SCB is not transferring in the other FIFO.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+pkt_complete_scb_if_fifos_idle:
+ bmov ARG_1, SCBPTR, 2;
+ mvi DFFSXFRCTL, CLRCHN;
+ SET_MODE(M_SCSI, M_SCSI)
+ bmov SCBPTR, ARG_1, 2;
+ test SCB_FIFO_USE_COUNT, 0xFF jnz return;
+queue_scb_completion:
+ test SCB_SCSI_STATUS,0xff jnz bad_status;
+ /*
+ * Check for residuals
+ */
+ test SCB_SGPTR, SG_LIST_NULL jnz complete; /* No xfer */
+ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+complete:
+ bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+ bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+bad_status:
+ cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
+ call freeze_queue;
+upload_scb:
+ /*
+ * Restore SCB TAG since we reuse this field
+ * in the sequencer. We don't want to corrupt
+ * it on the host.
+ */
+ bmov SCB_TAG, SCBPTR, 2;
+ bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2;
+ bmov COMPLETE_DMA_SCB_HEAD, SCBPTR, 2;
+ or SCB_SGPTR, SG_STATUS_VALID ret;
+
+/*
+ * Is it a disconnect message? Set a flag in the SCB to remind us
+ * and await the bus going free. If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
+ */
+mesgin_disconnect:
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte
+ * or we want to abort this command. Either way, the target
+ * should take us to message out phase and then attempt to
+ * disconnect again.
+ * XXX - Wait for more testing.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
+ jnz mesgin_proto_violation;
+ or SCB_CONTROL,DISCONNECTED;
+ test SCB_CONTROL, TAG_ENB jnz await_busfree;
+queue_disc_scb:
+ bmov REG0, SCBPTR, 2;
+ INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov DINDIR, REG0, 2;
+ bmov SCBPTR, REG0, 2;
+ /* FALLTHROUGH */
+await_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0) {
+ /*
+ * In the BUSFREEREV_BUG case, the
+ * busfree status was cleared at the
+ * beginning of the connection.
+ */
+ mvi CLRSINT1,CLRBUSFREE;
+ }
+ mov NONE, SCSIDAT; /* Ack the last byte */
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz await_busfree_not_m_dff;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+await_busfree_clrchn:
+ mvi DFFSXFRCTL, CLRCHN;
+await_busfree_not_m_dff:
+ call clear_target_state;
+ test SSTAT1,REQINIT|BUSFREE jz .;
+ test SSTAT1, BUSFREE jnz idle_loop;
+ SET_SEQINTCODE(MISSED_BUSFREE)
+
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them. This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+mesgin_sdptrs:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+ test SEQ_FLAGS, DPHASE jz ITloop;
+ call save_pointers;
+ jmp ITloop;
+
+save_pointers:
+ /*
+ * If we are asked to save our position at the end of the
+ * transfer, just mark us at the end rather than perform a
+ * full save.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz save_pointers_full;
+ or SCB_SGPTR, SG_LIST_NULL ret;
+
+save_pointers_full:
+ /*
+ * The SCB_DATAPTR becomes the current SHADDR.
+ * All other information comes directly from our residual
+ * state.
+ */
+ bmov SCB_DATAPTR, SHADDR, 8;
+ bmov SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8 ret;
+
+/*
+ * Restore pointers message? Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest. We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data or command phase.
+ */
+mesgin_rdptrs:
+ and SEQ_FLAGS, ~DPHASE;
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz msgin_rdptrs_get_fifo;
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+ SET_MODE(M_SCSI, M_SCSI)
+msgin_rdptrs_get_fifo:
+ call allocate_fifo;
+ jmp mesgin_done;
+
+clear_target_state:
+ mvi LASTPHASE, P_BUSFREE;
+ /* clear target specific flags */
+ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
+
+phase_lock:
+ if ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0) {
+ /*
+ * Don't ignore persistent REQ assertions just because
+ * they were asserted within the bus settle delay window.
+ * This allows us to tolerate devices like the GEM318
+ * that violate the SCSI spec. We are careful not to
+ * count REQ while we are waiting for it to fall during
+ * an async phase due to our asserted ACK. Each
+ * sequencer instruction takes ~25ns, so the REQ must
+ * last at least 100ns in order to be counted as a true
+ * REQ.
+ */
+ test SCSIPHASE, 0xFF jnz phase_locked;
+ test SCSISIGI, ACKI jnz phase_lock;
+ test SCSISIGI, REQI jz phase_lock;
+ test SCSIPHASE, 0xFF jnz phase_locked;
+ test SCSISIGI, ACKI jnz phase_lock;
+ test SCSISIGI, REQI jz phase_lock;
+phase_locked:
+ } else {
+ test SCSIPHASE, 0xFF jz .;
+ }
+ test SSTAT1, SCSIPERR jnz phase_lock;
+phase_lock_latch_phase:
+ and LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * An ACK is not sent on input from the target until SCSIDATL is read from.
+ * So we wait until SCSIDATL is latched (the usual way), then read the data
+ * byte directly off the bus using SCSIBUSL. When we have pulled the ATN
+ * line, or we just want to acknowledge the byte, then we do a dummy read
+ * from SCISDATL. The SCSI spec guarantees that the target will hold the
+ * data byte on the bus until we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called. inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+inb_next_wait:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SCSIPHASE, 0xFF jz .;
+ test SSTAT1, SCSIPERR jnz inb_next_wait;
+inb_next_check_phase:
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+ clr DINDEX[1];
+ mov DINDEX,SINDEX;
+ mov DINDIR,SCSIBUS ret; /*read byte directly from bus*/
+inb_last:
+ mov NONE,SCSIDAT ret; /*dummy read from latch to ACK*/
+
+mk_mesg:
+ mvi SCSISIGO, ATNO;
+ mov MSG_OUT,SINDEX ret;
+
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+disable_ccsgen:
+ test SG_STATE, FETCH_INPROG jz disable_ccsgen_fetch_done;
+ clr CCSGCTL;
+disable_ccsgen_fetch_done:
+ clr SG_STATE ret;
+
+service_fifo:
+ /*
+ * Do we have any prefetch left???
+ */
+ test SG_STATE, SEGS_AVAIL jnz idle_sg_avail;
+
+ /*
+ * Can this FIFO have access to the S/G cache yet?
+ */
+ test CCSGCTL, SG_CACHE_AVAIL jz return;
+
+ /* Did we just finish fetching segs? */
+ test CCSGCTL, CCSGDONE jnz idle_sgfetch_complete;
+
+ /* Are we actively fetching segments? */
+ test CCSGCTL, CCSGENACK jnz return;
+
+ /*
+ * We fetch a "cacheline aligned" and sized amount of data
+ * so we don't end up referencing a non-existant page.
+ * Cacheline aligned is in quotes because the kernel will
+ * set the prefetch amount to a reasonable level if the
+ * cacheline size is unknown.
+ */
+ bmov SGHADDR, SCB_RESIDUAL_SGPTR, 4;
+ mvi SGHCNT, SG_PREFETCH_CNT;
+ if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) {
+ /*
+ * Need two instruction between "touches" of SGHADDR.
+ */
+ nop;
+ }
+ and SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+ mvi CCSGCTL, CCSGEN|CCSGRESET;
+ or SG_STATE, FETCH_INPROG ret;
+idle_sgfetch_complete:
+ /*
+ * Guard against SG_CACHE_AVAIL activating during sg fetch
+ * request in the other FIFO.
+ */
+ test SG_STATE, FETCH_INPROG jz return;
+ clr CCSGCTL;
+ and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+ mvi SG_STATE, SEGS_AVAIL|LOADING_NEEDED;
+idle_sg_avail:
+ /* Does the hardware have space for another SG entry? */
+ test DFSTATUS, PRELOAD_AVAIL jz return;
+ /*
+ * On the A, preloading a segment before HDMAENACK
+ * comes true can clobber the shaddow address of the
+ * first segment in the S/G FIFO. Wait until it is
+ * safe to proceed.
+ */
+ if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) == 0) {
+ test DFCNTRL, HDMAENACK jz return;
+ }
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ bmov HADDR, CCSGRAM, 8;
+ } else {
+ bmov HADDR, CCSGRAM, 4;
+ }
+ bmov HCNT, CCSGRAM, 3;
+ bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+ if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+ and HADDR[4], SG_HIGH_ADDR_BITS, SCB_RESIDUAL_DATACNT[3];
+ }
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ /* Skip 4 bytes of pad. */
+ add CCSGADDR, 4;
+ }
+sg_advance:
+ clr A; /* add sizeof(struct scatter) */
+ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+ adc SCB_RESIDUAL_SGPTR[1],A;
+ adc SCB_RESIDUAL_SGPTR[2],A;
+ adc SCB_RESIDUAL_SGPTR[3],A;
+ mov SINDEX, SCB_RESIDUAL_SGPTR[0];
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 3;
+ or SINDEX, LAST_SEG;
+ clr SG_STATE;
+ mov SG_CACHE_PRE, SINDEX;
+ if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) {
+ /*
+ * Use SCSIENWRDIS so that SCSIEN is never
+ * modified by this operation.
+ */
+ or DFCNTRL, PRELOADEN|HDMAEN|SCSIENWRDIS;
+ } else {
+ or DFCNTRL, PRELOADEN|HDMAEN;
+ }
+ /*
+ * Do we have another segment in the cache?
+ */
+ add NONE, SG_PREFETCH_CNT_LIMIT, CCSGADDR;
+ jnc return;
+ and SG_STATE, ~SEGS_AVAIL ret;
+
+/*
+ * Initialize the DMA address and counter from the SCB.
+ */
+load_first_seg:
+ bmov HADDR, SCB_DATAPTR, 11;
+ and REG_ISR, ~SG_FULL_RESID, SCB_SGPTR[0];
+ test SCB_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or REG_ISR, LAST_SEG;
+ mov SG_CACHE_PRE, REG_ISR;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+ /*
+ * Since we've are entering a data phase, we will
+ * rely on the SCB_RESID* fields. Initialize the
+ * residual and clear the full residual flag.
+ */
+ and SCB_SGPTR[0], ~SG_FULL_RESID;
+ bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+ /* If we need more S/G elements, tell the idle loop */
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz . + 2;
+ mvi SG_STATE, LOADING_NEEDED ret;
+ clr SG_STATE ret;
+
+p_data_handle_xfer:
+ call setjmp;
+ test SG_STATE, LOADING_NEEDED jnz service_fifo;
+p_data_clear_handler:
+ or LONGJMP_ADDR[1], INVALID_ADDR ret;
+
+p_data:
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+ SET_SEQINTCODE(PROTO_VIOLATION)
+p_data_allowed:
+
+ test SEQ_FLAGS, DPHASE jz data_phase_initialize;
+
+ /*
+ * If we re-enter the data phase after going through another
+ * phase, our transfer location has almost certainly been
+ * corrupted by the interveining, non-data, transfers. Ask
+ * the host driver to fix us up based on the transfer residual
+ * unless we already know that we should be bitbucketing.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+ SET_SEQINTCODE(PDATA_REINIT)
+ jmp data_phase_inbounds;
+
+p_data_bitbucket:
+ /*
+ * Turn on `Bit Bucket' mode, wait until the target takes
+ * us to another phase, and then notify the host.
+ */
+ mov SAVED_MODE, MODE_PTR;
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz bitbucket_not_m_dff;
+ /*
+ * Ensure that any FIFO contents are cleared out and the
+ * FIFO free'd prior to starting the BITBUCKET. BITBUCKET
+ * doesn't discard data already in the FIFO.
+ */
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+ SET_MODE(M_SCSI, M_SCSI)
+bitbucket_not_m_dff:
+ or SXFRCTL1,BITBUCKET;
+ /* Wait for non-data phase. */
+ test SCSIPHASE, ~DATA_PHASE_MASK jz .;
+ and SXFRCTL1, ~BITBUCKET;
+ RESTORE_MODE(SAVED_MODE)
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ SET_SEQINTCODE(DATA_OVERRUN)
+ jmp ITloop;
+
+data_phase_initialize:
+ test SCB_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+ call load_first_seg;
+data_phase_inbounds:
+ /* We have seen a data phase at least once. */
+ or SEQ_FLAGS, DPHASE;
+ mov SAVED_MODE, MODE_PTR;
+ test SG_STATE, LOADING_NEEDED jz data_group_dma_loop;
+ call p_data_handle_xfer;
+data_group_dma_loop:
+ /*
+ * The transfer is complete if either the last segment
+ * completes or the target changes phase. Both conditions
+ * will clear SCSIEN.
+ */
+ call idle_loop_service_fifos;
+ call idle_loop_cchan;
+ call idle_loop_gsfifo;
+ RESTORE_MODE(SAVED_MODE)
+ test DFCNTRL, SCSIEN jnz data_group_dma_loop;
+
+data_group_dmafinish:
+ /*
+ * The transfer has terminated either due to a phase
+ * change, and/or the completion of the last segment.
+ * We have two goals here. Do as much other work
+ * as possible while the data fifo drains on a read
+ * and respond as quickly as possible to the standard
+ * messages (save data pointers/disconnect and command
+ * complete) that usually follow a data phase.
+ */
+ call calc_residual;
+
+ /*
+ * Go ahead and shut down the DMA engine now.
+ */
+ test DFCNTRL, DIRECTION jnz data_phase_finish;
+data_group_fifoflush:
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ /*
+ * We have enabled the auto-ack feature. This means
+ * that the controller may have already transferred
+ * some overrun bytes into the data FIFO and acked them
+ * on the bus. The only way to detect this situation is
+ * to wait for LAST_SEG_DONE to come true on a completed
+ * transfer and then test to see if the data FIFO is
+ * non-empty. We know there is more data yet to transfer
+ * if SG_LIST_NULL is not yet set, thus there cannot be
+ * an overrun.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_finish;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+ test DFSTATUS, FIFOEMP jnz data_phase_finish;
+ /* Overrun */
+ jmp p_data;
+data_phase_finish:
+ /*
+ * If the target has left us in data phase, loop through
+ * the dma code again. We will only loop if there is a
+ * data overrun.
+ */
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jnz data_phase_done;
+ }
+ if ((ahd->flags & AHD_INITIATORROLE) != 0) {
+ test SSTAT1, REQINIT jz .;
+ test SCSIPHASE, DATA_PHASE_MASK jnz p_data;
+ }
+
+data_phase_done:
+ /* Kill off any pending prefetch */
+ call disable_ccsgen;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ test SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+ /*
+ and SEQ_FLAGS, ~DPHASE_PENDING;
+ * For data-in phases, wait for any pending acks from the
+ * initiator before changing phase. We only need to
+ * send Ignore Wide Residue messages for data-in phases.
+ test DFCNTRL, DIRECTION jz target_ITloop;
+ test SSTAT1, REQINIT jnz .;
+ test SCB_TASK_ATTRIBUTE, SCB_XFERLEN_ODD jz target_ITloop;
+ SET_MODE(M_SCSI, M_SCSI)
+ test NEGCONOPTS, WIDEXFER jz target_ITloop;
+ */
+ /*
+ * Issue an Ignore Wide Residue Message.
+ mvi P_MESGIN|BSYO call change_phase;
+ mvi MSG_IGN_WIDE_RESIDUE call target_outb;
+ mvi 1 call target_outb;
+ jmp target_ITloop;
+ */
+ } else {
+ jmp ITloop;
+ }
+
+/*
+ * We assume that, even though data may still be
+ * transferring to the host, that the SCSI side of
+ * the DMA engine is now in a static state. This
+ * allows us to update our notion of where we are
+ * in this transfer.
+ *
+ * If, by chance, we stopped before being able
+ * to fetch additional segments for this transfer,
+ * yet the last S/G was completely exhausted,
+ * call our idle loop until it is able to load
+ * another segment. This will allow us to immediately
+ * pickup on the next segment on the next data phase.
+ *
+ * If we happened to stop on the last segment, then
+ * our residual information is still correct from
+ * the idle loop and there is no need to perform
+ * any fixups.
+ */
+residual_before_last_seg:
+ test MDFFSTAT, SHVALID jnz sgptr_fixup;
+ /*
+ * Can never happen from an interrupt as the packetized
+ * hardware will only interrupt us once SHVALID or
+ * LAST_SEG_DONE.
+ */
+ call idle_loop_service_fifos;
+ RESTORE_MODE(SAVED_MODE)
+ /* FALLTHROUGH */
+calc_residual:
+ test SG_CACHE_SHADOW, LAST_SEG jz residual_before_last_seg;
+ /* Record if we've consumed all S/G entries */
+ test MDFFSTAT, SHVALID jz . + 2;
+ bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+ or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL ret;
+
+sgptr_fixup:
+ /*
+ * Fixup the residual next S/G pointer. The S/G preload
+ * feature of the chip allows us to load two elements
+ * in addition to the currently active element. We
+ * store the bottom byte of the next S/G pointer in
+ * the SG_CACHE_PTR register so we can restore the
+ * correct value when the DMA completes. If the next
+ * sg ptr value has advanced to the point where higher
+ * bytes in the address have been affected, fix them
+ * too.
+ */
+ test SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+ test SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+ add SCB_RESIDUAL_SGPTR[1], -1;
+ adc SCB_RESIDUAL_SGPTR[2], -1;
+ adc SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+ and SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+ clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+ bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+
+export timer_isr:
+ call issue_cmdcmplt;
+ mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+ /*
+ * In H2A4, the mode pointer is not saved
+ * for intvec2, but is restored on iret.
+ * This can lead to the restoration of a
+ * bogus mode ptr. Manually clear the
+ * intmask bits and do a normal return
+ * to compensate.
+ */
+ and SEQINTCTL, ~(INTMASK2|INTMASK1) ret;
+ } else {
+ or SEQINTCTL, IRET ret;
+ }
+
+export seq_isr:
+ if ((ahd->features & AHD_RTI) == 0) {
+ /*
+ * On RevA Silicon, if the target returns us to data-out
+ * after we have already trained for data-out, it is
+ * possible for us to transition the free running clock to
+ * data-valid before the required 100ns P1 setup time (8 P1
+ * assertions in fast-160 mode). This will only happen if
+ * this L-Q is a continuation of a data transfer for which
+ * we have already prefetched data into our FIFO (LQ/Data
+ * followed by LQ/Data for the same write transaction).
+ * This can cause some target implementations to miss the
+ * first few data transfers on the bus. We detect this
+ * situation by noticing that this is the first data transfer
+ * after an LQ (LQIWORKONLQ true), that the data transfer is
+ * a continuation of a transfer already setup in our FIFO
+ * (SAVEPTRS interrupt), and that the transaction is a write
+ * (DIRECTION set in DFCNTRL). The delay is performed by
+ * disabling SCSIEN until we see the first REQ from the
+ * target.
+ *
+ * First instruction in an ISR cannot be a branch on
+ * Rev A. Snapshot LQISTAT2 so the status is not missed
+ * and deffer the test by one instruction.
+ */
+ mov REG_ISR, LQISTAT2;
+ test REG_ISR, LQIWORKONLQ jz main_isr;
+ test SEQINTSRC, SAVEPTRS jz main_isr;
+ test LONGJMP_ADDR[1], INVALID_ADDR jz saveptr_active_fifo;
+ /*
+ * Switch to the active FIFO after clearing the snapshot
+ * savepointer in the current FIFO. We do this so that
+ * a pending CTXTDONE or SAVEPTR is visible in the active
+ * FIFO. This status is the only way we can detect if we
+ * have lost the race (e.g. host paused us) and our attepts
+ * to disable the channel occurred after all REQs were
+ * already seen and acked (REQINIT never comes true).
+ */
+ mvi DFFSXFRCTL, CLRCHN;
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+ test DFCNTRL, DIRECTION jz interrupt_return;
+ and DFCNTRL, ~SCSIEN;
+snapshot_wait_data_valid:
+ test SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz snapshot_data_valid;
+ test SSTAT1, REQINIT jz snapshot_wait_data_valid;
+snapshot_data_valid:
+ or DFCNTRL, SCSIEN;
+ or SEQINTCTL, IRET ret;
+snapshot_saveptr:
+ mvi DFFSXFRCTL, CLRCHN;
+ or SEQINTCTL, IRET ret;
+main_isr:
+ }
+ test SEQINTSRC, CFG4DATA jnz cfg4data_intr;
+ test SEQINTSRC, CFG4ISTAT jnz cfg4istat_intr;
+ test SEQINTSRC, SAVEPTRS jnz saveptr_intr;
+ test SEQINTSRC, CFG4ICMD jnz cfg4icmd_intr;
+ SET_SEQINTCODE(INVALID_SEQINT)
+
+/*
+ * There are two types of save pointers interrupts:
+ * The first is a snapshot save pointers where the current FIFO is not
+ * active and contains a snapshot of the current poniter information.
+ * This happens between packets in a stream for a single L_Q. Since we
+ * are not performing a pointer save, we can safely clear the channel
+ * so it can be used for other transactions. On RTI capable controllers,
+ * where snapshots can, and are, disabled, the code to handle this type
+ * of snapshot is not active.
+ *
+ * The second case is a save pointers on an active FIFO which occurs
+ * if the target changes to a new L_Q or busfrees/QASes and the transfer
+ * has a residual. This should occur coincident with a ctxtdone. We
+ * disable the interrupt and allow our active routine to handle the
+ * save.
+ */
+saveptr_intr:
+ if ((ahd->features & AHD_RTI) == 0) {
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz snapshot_saveptr;
+ }
+saveptr_active_fifo:
+ and SEQIMODE, ~ENSAVEPTRS;
+ or SEQINTCTL, IRET ret;
+
+cfg4data_intr:
+ test SCB_SGPTR[0], SG_LIST_NULL jnz pkt_handle_overrun_inc_use_count;
+ call load_first_seg;
+ call pkt_handle_xfer;
+ inc SCB_FIFO_USE_COUNT;
+interrupt_return:
+ or SEQINTCTL, IRET ret;
+
+cfg4istat_intr:
+ call freeze_queue;
+ add NONE, -13, SCB_CDB_LEN;
+ jnc cfg4istat_have_sense_addr;
+ test SCB_CDB_LEN, SCB_CDB_LEN_PTR jnz cfg4istat_have_sense_addr;
+ /*
+ * Host sets up address/count and enables transfer.
+ */
+ SET_SEQINTCODE(CFG4ISTAT_INTR)
+ jmp cfg4istat_setup_handler;
+cfg4istat_have_sense_addr:
+ bmov HADDR, SCB_SENSE_BUSADDR, 4;
+ mvi HCNT[1], (AHD_SENSE_BUFSIZE >> 8);
+ mvi SG_CACHE_PRE, LAST_SEG;
+ mvi DFCNTRL, PRELOADEN|SCSIEN|HDMAEN;
+cfg4istat_setup_handler:
+ /*
+ * Status pkt is transferring to host.
+ * Wait in idle loop for transfer to complete.
+ * If a command completed before an attempted
+ * task management function completed, notify the host.
+ */
+ test SCB_TASK_MANAGEMENT, 0xFF jz cfg4istat_no_taskmgmt_func;
+ SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
+cfg4istat_no_taskmgmt_func:
+ call pkt_handle_status;
+ or SEQINTCTL, IRET ret;
+
+cfg4icmd_intr:
+ /*
+ * In the case of DMAing a CDB from the host, the normal
+ * CDB buffer is formatted with an 8 byte address followed
+ * by a 1 byte count.
+ */
+ bmov HADDR[0], SCB_HOST_CDB_PTR, 9;
+ mvi SG_CACHE_PRE, LAST_SEG;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+ call pkt_handle_cdb;
+ or SEQINTCTL, IRET ret;
+
+/*
+ * See if the target has gone on in this context creating an
+ * overrun condition. For the write case, the hardware cannot
+ * ack bytes until data are provided. So, if the target begins
+ * another packet without changing contexts, implying we are
+ * not sitting on a packet boundary, we are in an overrun
+ * situation. For the read case, the hardware will continue to
+ * ack bytes into the FIFO, and may even ack the last overrun packet
+ * into the FIFO. If the FIFO should become non-empty, we are in
+ * a read overrun case.
+ */
+#define check_overrun \
+ /* Not on a packet boundary. */ \
+ test MDFFSTAT, DLZERO jz pkt_handle_overrun; \
+ test DFSTATUS, FIFOEMP jz pkt_handle_overrun
+
+pkt_handle_xfer:
+ test SG_STATE, LOADING_NEEDED jz pkt_last_seg;
+ call setjmp;
+ test SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+ test SCSISIGO, ATNO jnz . + 2;
+ test SSTAT2, NONPACKREQ jz pkt_service_fifo;
+ /*
+ * Defer handling of this NONPACKREQ until we
+ * can be sure it pertains to this FIFO. SAVEPTRS
+ * will not be asserted if the NONPACKREQ is for us,
+ * so we must simulate it if shaddow is valid. If
+ * shaddow is not valid, keep running this FIFO until we
+ * have satisfied the transfer by loading segments and
+ * waiting for either shaddow valid or last_seg_done.
+ */
+ test MDFFSTAT, SHVALID jnz pkt_saveptrs;
+pkt_service_fifo:
+ test SG_STATE, LOADING_NEEDED jnz service_fifo;
+pkt_last_seg:
+ call setjmp;
+ test SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_last_seg_done;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+ test SCSISIGO, ATNO jnz . + 2;
+ test SSTAT2, NONPACKREQ jz return;
+ test MDFFSTAT, SHVALID jz return;
+ /* FALLTHROUGH */
+
+/*
+ * Either a SAVEPTRS interrupt condition is pending for this FIFO
+ * or we have a pending NONPACKREQ for this FIFO. We differentiate
+ * between the two by capturing the state of the SAVEPTRS interrupt
+ * prior to clearing this status and executing the common code for
+ * these two cases.
+ */
+pkt_saveptrs:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ mov REG0, SEQINTSRC;
+ call calc_residual;
+ call save_pointers;
+ mvi CLRSEQINTSRC, CLRSAVEPTRS;
+ call disable_ccsgen;
+ or SEQIMODE, ENSAVEPTRS;
+ test DFCNTRL, DIRECTION jnz pkt_saveptrs_check_status;
+ test DFSTATUS, FIFOEMP jnz pkt_saveptrs_check_status;
+ /*
+ * Keep a handler around for this FIFO until it drains
+ * to the host to guarantee that we don't complete the
+ * command to the host before the data arrives.
+ */
+pkt_saveptrs_wait_fifoemp:
+ call setjmp;
+ test DFSTATUS, FIFOEMP jz return;
+pkt_saveptrs_check_status:
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ test REG0, SAVEPTRS jz unexpected_nonpkt_phase;
+ dec SCB_FIFO_USE_COUNT;
+ test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
+ mvi DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+/*
+ * LAST_SEG_DONE status has been seen in the current FIFO.
+ * This indicates that all of the allowed data for this
+ * command has transferred across the SCSI and host buses.
+ * Check for overrun and see if we can complete this command.
+ */
+pkt_last_seg_done:
+BEGIN_CRITICAL;
+ /*
+ * Mark transfer as completed.
+ */
+ or SCB_SGPTR, SG_LIST_NULL;
+
+ /*
+ * Wait for the current context to finish to verify that
+ * no overrun condition has occurred.
+ */
+ test SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
+ call setjmp;
+pkt_wait_ctxt_done_loop:
+ test SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
+ /*
+ * A sufficiently large overrun or a NONPACKREQ may
+ * prevent CTXTDONE from ever asserting, so we must
+ * poll for these statuses too.
+ */
+ check_overrun;
+ test SSTAT2, NONPACKREQ jz return;
+ test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+ /* FALLTHROUGH */
+
+pkt_ctxt_done:
+ check_overrun;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ /*
+ * If status has been received, it is safe to skip
+ * the check to see if another FIFO is active because
+ * LAST_SEG_DONE has been observed. However, we check
+ * the FIFO anyway since it costs us only one extra
+ * instruction to leverage common code to perform the
+ * SCB completion.
+ */
+ dec SCB_FIFO_USE_COUNT;
+ test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
+ mvi DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+/*
+ * Must wait until CDB xfer is over before issuing the
+ * clear channel.
+ */
+pkt_handle_cdb:
+ call setjmp;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jz return;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ mvi DFFSXFRCTL, CLRCHN ret;
+
+/*
+ * Watch over the status transfer. Our host sense buffer is
+ * large enough to take the maximum allowed status packet.
+ * None-the-less, we must still catch and report overruns to
+ * the host. Additionally, properly catch unexpected non-packet
+ * phases that are typically caused by CRC errors in status packet
+ * transmission.
+ */
+pkt_handle_status:
+ call setjmp;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_status_check_overrun;
+ test SEQINTSRC, CTXTDONE jz pkt_status_check_nonpackreq;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_status_check_overrun;
+pkt_status_IU_done:
+ if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ test DFSTATUS, FIFOEMP jz return;
+BEGIN_CRITICAL;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ mvi SCB_SCSI_STATUS, STATUS_PKT_SENSE;
+ or SCB_CONTROL, STATUS_RCVD;
+ jmp pkt_complete_scb_if_fifos_idle;
+END_CRITICAL;
+pkt_status_check_overrun:
+ /*
+ * Status PKT overruns are uncerimoniously recovered with a
+ * bus reset. If we've overrun, let the host know so that
+ * recovery can be performed.
+ *
+ * LAST_SEG_DONE has been observed. If either CTXTDONE or
+ * a NONPACKREQ phase change have occurred and the FIFO is
+ * empty, there is no overrun.
+ */
+ test DFSTATUS, FIFOEMP jz pkt_status_report_overrun;
+ test SEQINTSRC, CTXTDONE jz . + 2;
+ test DFSTATUS, FIFOEMP jnz pkt_status_IU_done;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz return;
+ test DFSTATUS, FIFOEMP jnz pkt_status_check_nonpackreq;
+pkt_status_report_overrun:
+ SET_SEQINTCODE(STATUS_OVERRUN)
+ /* SEQUENCER RESTARTED */
+pkt_status_check_nonpackreq:
+ /*
+ * CTXTDONE may be held off if a NONPACKREQ is associated with
+ * the current context. If a NONPACKREQ is observed, decide
+ * if it is for the current context. If it is for the current
+ * context, we must defer NONPACKREQ processing until all data
+ * has transferred to the host.
+ */
+ test SCSIPHASE, ~DATA_PHASE_MASK jz return;
+ test SCSISIGO, ATNO jnz . + 2;
+ test SSTAT2, NONPACKREQ jz return;
+ test SEQINTSRC, CTXTDONE jnz pkt_status_IU_done;
+ test DFSTATUS, FIFOEMP jz return;
+ /*
+ * The unexpected nonpkt phase handler assumes that any
+ * data channel use will have a FIFO reference count. It
+ * turns out that the status handler doesn't need a refernce
+ * count since the status received flag, and thus completion
+ * processing, cannot be set until the handler is finished.
+ * We increment the count here to make the nonpkt handler
+ * happy.
+ */
+ inc SCB_FIFO_USE_COUNT;
+ /* FALLTHROUGH */
+
+/*
+ * Nonpackreq is a polled status. It can come true in three situations:
+ * we have received an L_Q, we have sent one or more L_Qs, or there is no
+ * L_Q context associated with this REQ (REQ occurs immediately after a
+ * (re)selection). Routines that know that the context responsible for this
+ * nonpackreq call directly into unexpected_nonpkt_phase. In the case of the
+ * top level idle loop, we exhaust all active contexts prior to determining that
+ * we simply do not have the full I_T_L_Q for this phase.
+ */
+unexpected_nonpkt_phase_find_ctxt:
+ /*
+ * This nonpackreq is most likely associated with one of the tags
+ * in a FIFO or an outgoing LQ. Only treat it as an I_T only
+ * nonpackreq if we've cleared out the FIFOs and handled any
+ * pending SELDO.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+ and A, FIFO1FREE|FIFO0FREE, DFFSTAT;
+ cmp A, FIFO1FREE|FIFO0FREE jne return;
+ test SSTAT0, SELDO jnz return;
+ mvi SCBPTR[1], SCB_LIST_NULL;
+unexpected_nonpkt_phase:
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz unexpected_nonpkt_mode_cleared;
+SET_SRC_MODE M_DFF0;
+SET_DST_MODE M_DFF0;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ dec SCB_FIFO_USE_COUNT;
+ mvi DFFSXFRCTL, CLRCHN;
+unexpected_nonpkt_mode_cleared:
+ mvi CLRSINT2, CLRNONPACKREQ;
+ test SCSIPHASE, ~(MSG_IN_PHASE|MSG_OUT_PHASE) jnz illegal_phase;
+ SET_SEQINTCODE(ENTERING_NONPACK)
+ jmp ITloop;
+
+illegal_phase:
+ SET_SEQINTCODE(ILLEGAL_PHASE)
+ jmp ITloop;
+
+/*
+ * We have entered an overrun situation. If we have working
+ * BITBUCKET, flip that on and let the hardware eat any overrun
+ * data. Otherwise use an overrun buffer in the host to simulate
+ * BITBUCKET.
+ */
+pkt_handle_overrun_inc_use_count:
+ inc SCB_FIFO_USE_COUNT;
+pkt_handle_overrun:
+ SET_SEQINTCODE(CFG4OVERRUN)
+ call freeze_queue;
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0) {
+ or DFFSXFRCTL, DFFBITBUCKET;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ } else {
+ call load_overrun_buf;
+ mvi DFCNTRL, (HDMAEN|SCSIEN|PRELOADEN);
+ }
+ call setjmp;
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+ test DFSTATUS, PRELOAD_AVAIL jz overrun_load_done;
+ call load_overrun_buf;
+ or DFCNTRL, PRELOADEN;
+overrun_load_done:
+ test SEQINTSRC, CTXTDONE jnz pkt_overrun_end;
+ } else {
+ test DFFSXFRCTL, DFFBITBUCKET jz pkt_overrun_end;
+ }
+ test SSTAT2, NONPACKREQ jz return;
+pkt_overrun_end:
+ or SCB_RESIDUAL_SGPTR, SG_OVERRUN_RESID;
+ test SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+ dec SCB_FIFO_USE_COUNT;
+ or LONGJMP_ADDR[1], INVALID_ADDR;
+ test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
+ mvi DFFSXFRCTL, CLRCHN ret;
+
+if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+load_overrun_buf:
+ /*
+ * Load a dummy segment if preload space is available.
+ */
+ mov HADDR[0], SHARED_DATA_ADDR;
+ add HADDR[1], PKT_OVERRUN_BUFOFFSET, SHARED_DATA_ADDR[1];
+ mov ACCUM_SAVE, A;
+ clr A;
+ adc HADDR[2], A, SHARED_DATA_ADDR[2];
+ adc HADDR[3], A, SHARED_DATA_ADDR[3];
+ mov A, ACCUM_SAVE;
+ bmov HADDR[4], ALLZEROS, 4;
+ /* PKT_OVERRUN_BUFSIZE is a multiple of 256 */
+ clr HCNT[0];
+ mvi HCNT[1], ((PKT_OVERRUN_BUFSIZE >> 8) & 0xFF);
+ clr HCNT[2] ret;
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
new file mode 100644
index 000000000000..137fb1a37dd1
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -0,0 +1,9888 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#202 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/******************************** Globals *************************************/
+struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahd_chip_names[] =
+{
+ "NONE",
+ "aic7901",
+ "aic7902",
+ "aic7901A"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahd_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahd_hard_error_entry {
+ uint8_t errno;
+ char *errmesg;
+};
+
+static struct ahd_hard_error_entry ahd_hard_errors[] = {
+ { DSCTMOUT, "Discard Timer has timed out" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Parity Error" },
+ { DPARERR, "Data-path Parity Error" },
+ { MPARERR, "Scratch or SCB Memory Parity Error" },
+ { CIOPARERR, "CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahd_hard_errors);
+
+static struct ahd_phase_table_entry ahd_phase_table[] =
+{
+ { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
+ { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
+ { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
+ { P_COMMAND, MSG_NOOP, "in Command phase" },
+ { P_MESGOUT, MSG_NOOP, "in Message-out phase" },
+ { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { P_BUSFREE, MSG_NOOP, "while idle" },
+ { 0, MSG_NOOP, "in unknown phase" }
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahd_phase_table) - 1;
+
+/* Our Sequencer Program */
+#include "aic79xx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void ahd_handle_transmission_error(struct ahd_softc *ahd);
+static void ahd_handle_lqiphase_error(struct ahd_softc *ahd,
+ u_int lqistat1);
+static int ahd_handle_pkt_busfree(struct ahd_softc *ahd,
+ u_int busfreetime);
+static int ahd_handle_nonpkt_busfree(struct ahd_softc *ahd);
+static void ahd_handle_proto_violation(struct ahd_softc *ahd);
+static void ahd_force_renegotiation(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+
+static struct ahd_tmode_tstate*
+ ahd_alloc_tstate(struct ahd_softc *ahd,
+ u_int scsi_id, char channel);
+#ifdef AHD_TARGET_MODE
+static void ahd_free_tstate(struct ahd_softc *ahd,
+ u_int scsi_id, char channel, int force);
+#endif
+static void ahd_devlimited_syncrate(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *,
+ u_int *period,
+ u_int *ppr_options,
+ role_t role);
+static void ahd_update_neg_table(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_transinfo *tinfo);
+static void ahd_update_pending_scbs(struct ahd_softc *ahd);
+static void ahd_fetch_devinfo(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_scb_devinfo(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct scb *scb);
+static void ahd_setup_initiator_msgout(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct scb *scb);
+static void ahd_build_transfer_msg(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_construct_sdtr(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int period, u_int offset);
+static void ahd_construct_wdtr(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int bus_width);
+static void ahd_construct_ppr(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int bus_width, u_int ppr_options);
+static void ahd_clear_msg_state(struct ahd_softc *ahd);
+static void ahd_handle_message_phase(struct ahd_softc *ahd);
+typedef enum {
+ AHDMSG_1B,
+ AHDMSG_2B,
+ AHDMSG_EXT
+} ahd_msgtype;
+static int ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type,
+ u_int msgval, int full);
+static int ahd_parse_msg(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static int ahd_handle_msg_reject(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_handle_ign_wide_residue(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_reinitialize_dataptrs(struct ahd_softc *ahd);
+static void ahd_handle_devreset(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ u_int lun, cam_status status,
+ char *message, int verbose_level);
+#ifdef AHD_TARGET_MODE
+static void ahd_setup_target_msgin(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo,
+ struct scb *scb);
+#endif
+
+static u_int ahd_sglist_size(struct ahd_softc *ahd);
+static u_int ahd_sglist_allocsize(struct ahd_softc *ahd);
+static bus_dmamap_callback_t
+ ahd_dmamap_cb;
+static void ahd_initialize_hscbs(struct ahd_softc *ahd);
+static int ahd_init_scbdata(struct ahd_softc *ahd);
+static void ahd_fini_scbdata(struct ahd_softc *ahd);
+static void ahd_setup_iocell_workaround(struct ahd_softc *ahd);
+static void ahd_iocell_first_selection(struct ahd_softc *ahd);
+static void ahd_add_col_list(struct ahd_softc *ahd,
+ struct scb *scb, u_int col_idx);
+static void ahd_rem_col_list(struct ahd_softc *ahd,
+ struct scb *scb);
+static void ahd_chip_init(struct ahd_softc *ahd);
+static void ahd_qinfifo_requeue(struct ahd_softc *ahd,
+ struct scb *prev_scb,
+ struct scb *scb);
+static int ahd_qinfifo_count(struct ahd_softc *ahd);
+static int ahd_search_scb_list(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahd_search_action action,
+ u_int *list_head, u_int tid);
+static void ahd_stitch_tid_list(struct ahd_softc *ahd,
+ u_int tid_prev, u_int tid_cur,
+ u_int tid_next);
+static void ahd_add_scb_to_free_list(struct ahd_softc *ahd,
+ u_int scbid);
+static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+ u_int prev, u_int next, u_int tid);
+static void ahd_reset_current_bus(struct ahd_softc *ahd);
+static ahd_callback_t ahd_reset_poll;
+static ahd_callback_t ahd_stat_timer;
+#ifdef AHD_DUMP_SEQ
+static void ahd_dumpseq(struct ahd_softc *ahd);
+#endif
+static void ahd_loadseq(struct ahd_softc *ahd);
+static int ahd_check_patch(struct ahd_softc *ahd,
+ struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr);
+static u_int ahd_resolve_seqaddr(struct ahd_softc *ahd,
+ u_int address);
+static void ahd_download_instr(struct ahd_softc *ahd,
+ u_int instrptr, uint8_t *dconsts);
+static int ahd_probe_stack_size(struct ahd_softc *ahd);
+static int ahd_scb_active_in_fifo(struct ahd_softc *ahd,
+ struct scb *scb);
+static void ahd_run_data_fifo(struct ahd_softc *ahd,
+ struct scb *scb);
+
+#ifdef AHD_TARGET_MODE
+static void ahd_queue_lstate_event(struct ahd_softc *ahd,
+ struct ahd_tmode_lstate *lstate,
+ u_int initiator_id,
+ u_int event_type,
+ u_int event_arg);
+static void ahd_update_scsiid(struct ahd_softc *ahd,
+ u_int targid_mask);
+static int ahd_handle_target_cmd(struct ahd_softc *ahd,
+ struct target_cmd *cmd);
+#endif
+
+/******************************** Private Inlines *****************************/
+static __inline void ahd_assert_atn(struct ahd_softc *ahd);
+static __inline int ahd_currently_packetized(struct ahd_softc *ahd);
+static __inline int ahd_set_active_fifo(struct ahd_softc *ahd);
+
+static __inline void
+ahd_assert_atn(struct ahd_softc *ahd)
+{
+ ahd_outb(ahd, SCSISIGO, ATNO);
+}
+
+/*
+ * Determine if the current connection has a packetized
+ * agreement. This does not necessarily mean that we
+ * are currently in a packetized transfer. We could
+ * just as easily be sending or receiving a message.
+ */
+static __inline int
+ahd_currently_packetized(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int packetized;
+
+ saved_modes = ahd_save_modes(ahd);
+ if ((ahd->bugs & AHD_PKTIZED_STATUS_BUG) != 0) {
+ /*
+ * The packetized bit refers to the last
+ * connection, not the current one. Check
+ * for non-zero LQISTATE instead.
+ */
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ packetized = ahd_inb(ahd, LQISTATE) != 0;
+ } else {
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ packetized = ahd_inb(ahd, LQISTAT2) & PACKETIZED;
+ }
+ ahd_restore_modes(ahd, saved_modes);
+ return (packetized);
+}
+
+static __inline int
+ahd_set_active_fifo(struct ahd_softc *ahd)
+{
+ u_int active_fifo;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+ switch (active_fifo) {
+ case 0:
+ case 1:
+ ahd_set_modes(ahd, active_fifo, active_fifo);
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahd_restart(struct ahd_softc *ahd)
+{
+
+ ahd_pause(ahd);
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ /* No more pending messages */
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, SCSISIGO, 0); /* De-assert BSY */
+ ahd_outb(ahd, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET);
+ ahd_outb(ahd, SEQINTCTL, 0);
+ ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+ ahd_outb(ahd, SEQ_FLAGS, 0);
+ ahd_outb(ahd, SAVED_SCSIID, 0xFF);
+ ahd_outb(ahd, SAVED_LUN, 0xFF);
+
+ /*
+ * Ensure that the sequencer's idea of TQINPOS
+ * matches our own. The sequencer increments TQINPOS
+ * only after it sees a DMA complete and a reset could
+ * occur before the increment leaving the kernel to believe
+ * the command arrived but the sequencer to not.
+ */
+ ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+
+ /* Always allow reselection */
+ ahd_outb(ahd, SCSISEQ1,
+ ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+ ahd_unpause(ahd);
+}
+
+void
+ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo)
+{
+ ahd_mode_state saved_modes;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_FIFOS) != 0)
+ printf("%s: Clearing FIFO %d\n", ahd_name(ahd), fifo);
+#endif
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, fifo, fifo);
+ ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+ if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+ ahd_outb(ahd, CCSGCTL, CCSGRESET);
+ ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+ ahd_outb(ahd, SG_STATE, 0);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/************************* Input/Output Queues ********************************/
+/*
+ * Flush and completed commands that are sitting in the command
+ * complete queues down on the chip but have yet to be dma'ed back up.
+ */
+void
+ahd_flush_qoutfifo(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int saved_scbptr;
+ u_int ccscbctl;
+ u_int scbid;
+ u_int next_scbid;
+
+ saved_modes = ahd_save_modes(ahd);
+
+ /*
+ * Complete any SCBs that just finished being
+ * DMA'ed into the qoutfifo.
+ */
+ ahd_run_qoutfifo(ahd);
+
+ /*
+ * Flush the good status FIFO for compelted packetized commands.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ saved_scbptr = ahd_get_scbptr(ahd);
+ while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) {
+ u_int fifo_mode;
+ u_int i;
+
+ scbid = (ahd_inb(ahd, GSFIFO+1) << 8)
+ | ahd_inb(ahd, GSFIFO);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Warning - GSFIFO SCB %d invalid\n",
+ ahd_name(ahd), scbid);
+ continue;
+ }
+ /*
+ * Determine if this transaction is still active in
+ * any FIFO. If it is, we must flush that FIFO to
+ * the host before completing the command.
+ */
+ fifo_mode = 0;
+ for (i = 0; i < 2; i++) {
+ /* Toggle to the other mode. */
+ fifo_mode ^= 1;
+ ahd_set_modes(ahd, fifo_mode, fifo_mode);
+ if (ahd_scb_active_in_fifo(ahd, scb) == 0)
+ continue;
+
+ ahd_run_data_fifo(ahd, scb);
+
+ /*
+ * Clearing this transaction in this FIFO may
+ * cause a CFG4DATA for this same transaction
+ * to assert in the other FIFO. Make sure we
+ * loop one more time and check the other FIFO.
+ */
+ i = 0;
+ }
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_set_scbptr(ahd, scbid);
+ if ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_LIST_NULL) == 0
+ && ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_FULL_RESID) != 0
+ || (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR)
+ & SG_LIST_NULL) != 0)) {
+ u_int comp_head;
+
+ /*
+ * The transfer completed with a residual.
+ * Place this SCB on the complete DMA list
+ * so that we Update our in-core copy of the
+ * SCB before completing the command.
+ */
+ ahd_outb(ahd, SCB_SCSI_STATUS, 0);
+ ahd_outb(ahd, SCB_SGPTR,
+ ahd_inb_scbram(ahd, SCB_SGPTR)
+ | SG_STATUS_VALID);
+ ahd_outw(ahd, SCB_TAG, SCB_GET_TAG(scb));
+ comp_head = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+ ahd_outw(ahd, SCB_NEXT_COMPLETE, comp_head);
+ if (SCBID_IS_NULL(comp_head))
+ ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD,
+ SCB_GET_TAG(scb));
+ } else
+ ahd_complete_scb(ahd, scb);
+ }
+ ahd_set_scbptr(ahd, saved_scbptr);
+
+ /*
+ * Setup for command channel portion of flush.
+ */
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+ /*
+ * Wait for any inprogress DMA to complete and clear DMA state
+ * if this if for an SCB in the qinfifo.
+ */
+ while (((ccscbctl = ahd_inb(ahd, CCSCBCTL)) & (CCARREN|CCSCBEN)) != 0) {
+
+ if ((ccscbctl & (CCSCBDIR|CCARREN)) == (CCSCBDIR|CCARREN)) {
+ if ((ccscbctl & ARRDONE) != 0)
+ break;
+ } else if ((ccscbctl & CCSCBDONE) != 0)
+ break;
+ ahd_delay(200);
+ }
+ if ((ccscbctl & CCSCBDIR) != 0)
+ ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));
+
+ saved_scbptr = ahd_get_scbptr(ahd);
+ /*
+ * Manually update/complete any completed SCBs that are waiting to be
+ * DMA'ed back up to the host.
+ */
+ scbid = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+ while (!SCBID_IS_NULL(scbid)) {
+ uint8_t *hscb_ptr;
+ u_int i;
+
+ ahd_set_scbptr(ahd, scbid);
+ next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Warning - DMA-up and complete "
+ "SCB %d invalid\n", ahd_name(ahd), scbid);
+ continue;
+ }
+ hscb_ptr = (uint8_t *)scb->hscb;
+ for (i = 0; i < sizeof(struct hardware_scb); i++)
+ *hscb_ptr++ = ahd_inb_scbram(ahd, SCB_BASE + i);
+
+ ahd_complete_scb(ahd, scb);
+ scbid = next_scbid;
+ }
+ ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+
+ scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+ while (!SCBID_IS_NULL(scbid)) {
+
+ ahd_set_scbptr(ahd, scbid);
+ next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Warning - Complete SCB %d invalid\n",
+ ahd_name(ahd), scbid);
+ continue;
+ }
+
+ ahd_complete_scb(ahd, scb);
+ scbid = next_scbid;
+ }
+ ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+
+ /*
+ * Restore state.
+ */
+ ahd_set_scbptr(ahd, saved_scbptr);
+ ahd_restore_modes(ahd, saved_modes);
+ ahd->flags |= AHD_UPDATE_PEND_CMDS;
+}
+
+/*
+ * Determine if an SCB for a packetized transaction
+ * is active in a FIFO.
+ */
+static int
+ahd_scb_active_in_fifo(struct ahd_softc *ahd, struct scb *scb)
+{
+
+ /*
+ * The FIFO is only active for our transaction if
+ * the SCBPTR matches the SCB's ID and the firmware
+ * has installed a handler for the FIFO or we have
+ * a pending SAVEPTRS or CFG4DATA interrupt.
+ */
+ if (ahd_get_scbptr(ahd) != SCB_GET_TAG(scb)
+ || ((ahd_inb(ahd, LONGJMP_ADDR+1) & INVALID_ADDR) != 0
+ && (ahd_inb(ahd, SEQINTSRC) & (CFG4DATA|SAVEPTRS)) == 0))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Run a data fifo to completion for a transaction we know
+ * has completed across the SCSI bus (good status has been
+ * received). We are already set to the correct FIFO mode
+ * on entry to this routine.
+ *
+ * This function attempts to operate exactly as the firmware
+ * would when running this FIFO. Care must be taken to update
+ * this routine any time the firmware's FIFO algorithm is
+ * changed.
+ */
+static void
+ahd_run_data_fifo(struct ahd_softc *ahd, struct scb *scb)
+{
+ u_int seqintsrc;
+
+ while (1) {
+ seqintsrc = ahd_inb(ahd, SEQINTSRC);
+ if ((seqintsrc & CFG4DATA) != 0) {
+ uint32_t datacnt;
+ uint32_t sgptr;
+
+ /*
+ * Clear full residual flag.
+ */
+ sgptr = ahd_inl_scbram(ahd, SCB_SGPTR) & ~SG_FULL_RESID;
+ ahd_outb(ahd, SCB_SGPTR, sgptr);
+
+ /*
+ * Load datacnt and address.
+ */
+ datacnt = ahd_inl_scbram(ahd, SCB_DATACNT);
+ if ((datacnt & AHD_DMA_LAST_SEG) != 0) {
+ sgptr |= LAST_SEG;
+ ahd_outb(ahd, SG_STATE, 0);
+ } else
+ ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
+ ahd_outq(ahd, HADDR, ahd_inq_scbram(ahd, SCB_DATAPTR));
+ ahd_outl(ahd, HCNT, datacnt & AHD_SG_LEN_MASK);
+ ahd_outb(ahd, SG_CACHE_PRE, sgptr);
+ ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+
+ /*
+ * Initialize Residual Fields.
+ */
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, datacnt >> 24);
+ ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr & SG_PTR_MASK);
+
+ /*
+ * Mark the SCB as having a FIFO in use.
+ */
+ ahd_outb(ahd, SCB_FIFO_USE_COUNT,
+ ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) + 1);
+
+ /*
+ * Install a "fake" handler for this FIFO.
+ */
+ ahd_outw(ahd, LONGJMP_ADDR, 0);
+
+ /*
+ * Notify the hardware that we have satisfied
+ * this sequencer interrupt.
+ */
+ ahd_outb(ahd, CLRSEQINTSRC, CLRCFG4DATA);
+ } else if ((seqintsrc & SAVEPTRS) != 0) {
+ uint32_t sgptr;
+ uint32_t resid;
+
+ if ((ahd_inb(ahd, LONGJMP_ADDR+1)&INVALID_ADDR) != 0) {
+ /*
+ * Snapshot Save Pointers. Clear
+ * the snapshot and continue.
+ */
+ ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+ continue;
+ }
+
+ /*
+ * Disable S/G fetch so the DMA engine
+ * is available to future users.
+ */
+ if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+ ahd_outb(ahd, CCSGCTL, 0);
+ ahd_outb(ahd, SG_STATE, 0);
+
+ /*
+ * Flush the data FIFO. Strickly only
+ * necessary for Rev A parts.
+ */
+ ahd_outb(ahd, DFCNTRL,
+ ahd_inb(ahd, DFCNTRL) | FIFOFLUSH);
+
+ /*
+ * Calculate residual.
+ */
+ sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ resid = ahd_inl(ahd, SHCNT);
+ resid |=
+ ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24;
+ ahd_outl(ahd, SCB_RESIDUAL_DATACNT, resid);
+ if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG) == 0) {
+ /*
+ * Must back up to the correct S/G element.
+ * Typically this just means resetting our
+ * low byte to the offset in the SG_CACHE,
+ * but if we wrapped, we have to correct
+ * the other bytes of the sgptr too.
+ */
+ if ((ahd_inb(ahd, SG_CACHE_SHADOW) & 0x80) != 0
+ && (sgptr & 0x80) == 0)
+ sgptr -= 0x100;
+ sgptr &= ~0xFF;
+ sgptr |= ahd_inb(ahd, SG_CACHE_SHADOW)
+ & SG_ADDR_MASK;
+ ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, 0);
+ } else if ((resid & AHD_SG_LEN_MASK) == 0) {
+ ahd_outb(ahd, SCB_RESIDUAL_SGPTR,
+ sgptr | SG_LIST_NULL);
+ }
+ /*
+ * Save Pointers.
+ */
+ ahd_outq(ahd, SCB_DATAPTR, ahd_inq(ahd, SHADDR));
+ ahd_outl(ahd, SCB_DATACNT, resid);
+ ahd_outl(ahd, SCB_SGPTR, sgptr);
+ ahd_outb(ahd, CLRSEQINTSRC, CLRSAVEPTRS);
+ ahd_outb(ahd, SEQIMODE,
+ ahd_inb(ahd, SEQIMODE) | ENSAVEPTRS);
+ /*
+ * If the data is to the SCSI bus, we are
+ * done, otherwise wait for FIFOEMP.
+ */
+ if ((ahd_inb(ahd, DFCNTRL) & DIRECTION) != 0)
+ break;
+ } else if ((ahd_inb(ahd, SG_STATE) & LOADING_NEEDED) != 0) {
+ uint32_t sgptr;
+ uint64_t data_addr;
+ uint32_t data_len;
+ u_int dfcntrl;
+
+ /*
+ * Disable S/G fetch so the DMA engine
+ * is available to future users.
+ */
+ if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0) {
+ ahd_outb(ahd, CCSGCTL, 0);
+ ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
+ }
+
+ /*
+ * Wait for the DMA engine to notice that the
+ * host transfer is enabled and that there is
+ * space in the S/G FIFO for new segments before
+ * loading more segments.
+ */
+ if ((ahd_inb(ahd, DFSTATUS) & PRELOAD_AVAIL) == 0)
+ continue;
+ if ((ahd_inb(ahd, DFCNTRL) & HDMAENACK) == 0)
+ continue;
+
+ /*
+ * Determine the offset of the next S/G
+ * element to load.
+ */
+ sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ sgptr &= SG_PTR_MASK;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+ data_addr = sg->addr;
+ data_len = sg->len;
+ sgptr += sizeof(*sg);
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+ data_addr = sg->len & AHD_SG_HIGH_ADDR_MASK;
+ data_addr <<= 8;
+ data_addr |= sg->addr;
+ data_len = sg->len;
+ sgptr += sizeof(*sg);
+ }
+
+ /*
+ * Update residual information.
+ */
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, data_len >> 24);
+ ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+
+ /*
+ * Load the S/G.
+ */
+ if (data_len & AHD_DMA_LAST_SEG) {
+ sgptr |= LAST_SEG;
+ ahd_outb(ahd, SG_STATE, 0);
+ }
+ ahd_outq(ahd, HADDR, data_addr);
+ ahd_outl(ahd, HCNT, data_len & AHD_SG_LEN_MASK);
+ ahd_outb(ahd, SG_CACHE_PRE, sgptr & 0xFF);
+
+ /*
+ * Advertise the segment to the hardware.
+ */
+ dfcntrl = ahd_inb(ahd, DFCNTRL)|PRELOADEN|HDMAEN;
+ if ((ahd->features & AHD_NEW_DFCNTRL_OPTS)!=0) {
+ /*
+ * Use SCSIENWRDIS so that SCSIEN
+ * is never modified by this
+ * operation.
+ */
+ dfcntrl |= SCSIENWRDIS;
+ }
+ ahd_outb(ahd, DFCNTRL, dfcntrl);
+ } else if ((ahd_inb(ahd, SG_CACHE_SHADOW)
+ & LAST_SEG_DONE) != 0) {
+
+ /*
+ * Transfer completed to the end of SG list
+ * and has flushed to the host.
+ */
+ ahd_outb(ahd, SCB_SGPTR,
+ ahd_inb_scbram(ahd, SCB_SGPTR) | SG_LIST_NULL);
+ break;
+ } else if ((ahd_inb(ahd, DFSTATUS) & FIFOEMP) != 0) {
+ break;
+ }
+ ahd_delay(200);
+ }
+ /*
+ * Clear any handler for this FIFO, decrement
+ * the FIFO use count for the SCB, and release
+ * the FIFO.
+ */
+ ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+ ahd_outb(ahd, SCB_FIFO_USE_COUNT,
+ ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) - 1);
+ ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+}
+
+void
+ahd_run_qoutfifo(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ u_int scb_index;
+
+ if ((ahd->flags & AHD_RUNNING_QOUTFIFO) != 0)
+ panic("ahd_run_qoutfifo recursion");
+ ahd->flags |= AHD_RUNNING_QOUTFIFO;
+ ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);
+ while ((ahd->qoutfifo[ahd->qoutfifonext]
+ & QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag) {
+
+ scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]
+ & ~QOUTFIFO_ENTRY_VALID_LE);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ if (scb == NULL) {
+ printf("%s: WARNING no command for scb %d "
+ "(cmdcmplt)\nQOUTPOS = %d\n",
+ ahd_name(ahd), scb_index,
+ ahd->qoutfifonext);
+ ahd_dump_card_state(ahd);
+ } else
+ ahd_complete_scb(ahd, scb);
+
+ ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1);
+ if (ahd->qoutfifonext == 0)
+ ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID_LE;
+ }
+ ahd->flags &= ~AHD_RUNNING_QOUTFIFO;
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahd_handle_hwerrint(struct ahd_softc *ahd)
+{
+ /*
+ * Some catastrophic hardware error has occurred.
+ * Print it for the user and disable the controller.
+ */
+ int i;
+ int error;
+
+ error = ahd_inb(ahd, ERROR);
+ for (i = 0; i < num_errors; i++) {
+ if ((error & ahd_hard_errors[i].errno) != 0)
+ printf("%s: hwerrint, %s\n",
+ ahd_name(ahd), ahd_hard_errors[i].errmesg);
+ }
+
+ ahd_dump_card_state(ahd);
+ panic("BRKADRINT");
+
+ /* Tell everyone that this HBA is no longer available */
+ ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_NO_HBA);
+
+ /* Tell the system that this controller has gone away. */
+ ahd_free(ahd);
+}
+
+void
+ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
+{
+ u_int seqintcode;
+
+ /*
+ * Save the sequencer interrupt code and clear the SEQINT
+ * bit. We will unpause the sequencer, if appropriate,
+ * after servicing the request.
+ */
+ seqintcode = ahd_inb(ahd, SEQINTCODE);
+ ahd_outb(ahd, CLRINT, CLRSEQINT);
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ /*
+ * Unpause the sequencer and let it clear
+ * SEQINT by writing NO_SEQINT to it. This
+ * will cause the sequencer to be paused again,
+ * which is the expected state of this routine.
+ */
+ ahd_unpause(ahd);
+ while (!ahd_is_paused(ahd))
+ ;
+ ahd_outb(ahd, CLRINT, CLRSEQINT);
+ }
+ ahd_update_modes(ahd);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: Handle Seqint Called for code %d\n",
+ ahd_name(ahd), seqintcode);
+#endif
+ switch (seqintcode) {
+ case BAD_SCB_STATUS:
+ {
+ struct scb *scb;
+ u_int scbid;
+ int cmds_pending;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL) {
+ ahd_complete_scb(ahd, scb);
+ } else {
+ printf("%s: WARNING no command for scb %d "
+ "(bad status)\n", ahd_name(ahd), scbid);
+ ahd_dump_card_state(ahd);
+ }
+ cmds_pending = ahd_inw(ahd, CMDS_PENDING);
+ if (cmds_pending > 0)
+ ahd_outw(ahd, CMDS_PENDING, cmds_pending - 1);
+ break;
+ }
+ case ENTERING_NONPACK:
+ {
+ struct scb *scb;
+ u_int scbid;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ /*
+ * Somehow need to know if this
+ * is from a selection or reselection.
+ * From that, we can determine target
+ * ID so we at least have an I_T nexus.
+ */
+ } else {
+ ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+ ahd_outb(ahd, SAVED_LUN, scb->hscb->lun);
+ ahd_outb(ahd, SEQ_FLAGS, 0x0);
+ }
+ if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0
+ && (ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+ /*
+ * Phase change after read stream with
+ * CRC error with P0 asserted on last
+ * packet.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Assuming LQIPHASE_NLQ with "
+ "P0 assertion\n", ahd_name(ahd));
+#endif
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Entering NONPACK\n", ahd_name(ahd));
+#endif
+ break;
+ }
+ case INVALID_SEQINT:
+ printf("%s: Invalid Sequencer interrupt occurred.\n",
+ ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ break;
+ case STATUS_OVERRUN:
+ {
+ struct scb *scb;
+ u_int scbid;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL)
+ ahd_print_path(ahd, scb);
+ else
+ printf("%s: ", ahd_name(ahd));
+ printf("SCB %d Packetized Status Overrun", scbid);
+ ahd_dump_card_state(ahd);
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ break;
+ }
+ case CFG4ISTAT_INTR:
+ {
+ struct scb *scb;
+ u_int scbid;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ ahd_dump_card_state(ahd);
+ printf("CFG4ISTAT: Free SCB %d referenced", scbid);
+ panic("For safety");
+ }
+ ahd_outq(ahd, HADDR, scb->sense_busaddr);
+ ahd_outw(ahd, HCNT, AHD_SENSE_BUFSIZE);
+ ahd_outb(ahd, HCNT + 2, 0);
+ ahd_outb(ahd, SG_CACHE_PRE, SG_LAST_SEG);
+ ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+ break;
+ }
+ case ILLEGAL_PHASE:
+ {
+ u_int bus_phase;
+
+ bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ printf("%s: ILLEGAL_PHASE 0x%x\n",
+ ahd_name(ahd), bus_phase);
+
+ switch (bus_phase) {
+ case P_DATAOUT:
+ case P_DATAIN:
+ case P_DATAOUT_DT:
+ case P_DATAIN_DT:
+ case P_MESGOUT:
+ case P_STATUS:
+ case P_MESGIN:
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ printf("%s: Issued Bus Reset.\n", ahd_name(ahd));
+ break;
+ case P_COMMAND:
+ {
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ struct ahd_initiator_tinfo *targ_info;
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_transinfo *tinfo;
+ u_int scbid;
+
+ /*
+ * If a target takes us into the command phase
+ * assume that it has been externally reset and
+ * has thus lost our previous packetized negotiation
+ * agreement. Since we have not sent an identify
+ * message and may not have fully qualified the
+ * connection, we change our command to TUR, assert
+ * ATN and ABORT the task when we go to message in
+ * phase. The OSM will see the REQUEUE_REQUEST
+ * status and retry the command.
+ */
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("Invalid phase with no valid SCB. "
+ "Resetting bus.\n");
+ ahd_reset_channel(ahd, 'A',
+ /*Initiate Reset*/TRUE);
+ break;
+ }
+ ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahd, scb),
+ SCB_GET_LUN(scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ ROLE_INITIATOR);
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_ACTIVE, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_ACTIVE, /*paused*/TRUE);
+ ahd_outb(ahd, SCB_CDB_STORE, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+1, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+2, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+3, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+4, 0);
+ ahd_outb(ahd, SCB_CDB_STORE+5, 0);
+ ahd_outb(ahd, SCB_CDB_LEN, 6);
+ scb->hscb->control &= ~(TAG_ENB|SCB_TAG_TYPE);
+ scb->hscb->control |= MK_MESSAGE;
+ ahd_outb(ahd, SCB_CONTROL, scb->hscb->control);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+ /*
+ * The lun is 0, regardless of the SCB's lun
+ * as we have not sent an identify message.
+ */
+ ahd_outb(ahd, SAVED_LUN, 0);
+ ahd_outb(ahd, SEQ_FLAGS, 0);
+ ahd_assert_atn(ahd);
+ scb->flags &= ~(SCB_PACKETIZED);
+ scb->flags |= SCB_ABORT|SCB_CMDPHASE_ABORT;
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahd_freeze_scb(scb);
+
+ /*
+ * Allow the sequencer to continue with
+ * non-pack processing.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRLQOINT1, CLRLQOPHACHGINPKT);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Unexpected command phase from "
+ "packetized target\n");
+ }
+#endif
+ break;
+ }
+ }
+ break;
+ }
+ case CFG4OVERRUN:
+ {
+ struct scb *scb;
+ u_int scb_index;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ printf("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
+ ahd_inb(ahd, MODE_PTR));
+ }
+#endif
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ if (scb == NULL) {
+ /*
+ * Attempt to transfer to an SCB that is
+ * not outstanding.
+ */
+ ahd_assert_atn(ahd);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd->msgout_buf[0] = MSG_ABORT_TASK;
+ ahd->msgout_len = 1;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ /*
+ * Clear status received flag to prevent any
+ * attempt to complete this bogus SCB.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb_scbram(ahd, SCB_CONTROL)
+ & ~STATUS_RCVD);
+ }
+ break;
+ }
+ case DUMP_CARD_STATE:
+ {
+ ahd_dump_card_state(ahd);
+ break;
+ }
+ case PDATA_REINIT:
+ {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ printf("%s: PDATA_REINIT - DFCNTRL = 0x%x "
+ "SG_CACHE_SHADOW = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
+ ahd_inb(ahd, SG_CACHE_SHADOW));
+ }
+#endif
+ ahd_reinitialize_dataptrs(ahd);
+ break;
+ }
+ case HOST_MSG_LOOP:
+ {
+ struct ahd_devinfo devinfo;
+
+ /*
+ * The sequencer has encountered a message phase
+ * that requires host assistance for completion.
+ * While handling the message phase(s), we will be
+ * notified by the sequencer after each byte is
+ * transfered so we can track bus phase changes.
+ *
+ * If this is the first time we've seen a HOST_MSG_LOOP
+ * interrupt, initialize the state of the host message
+ * loop.
+ */
+ ahd_fetch_devinfo(ahd, &devinfo);
+ if (ahd->msg_type == MSG_TYPE_NONE) {
+ struct scb *scb;
+ u_int scb_index;
+ u_int bus_phase;
+
+ bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ if (bus_phase != P_MESGIN
+ && bus_phase != P_MESGOUT) {
+ printf("ahd_intr: HOST_MSG_LOOP bad "
+ "phase 0x%x\n", bus_phase);
+ /*
+ * Probably transitioned to bus free before
+ * we got here. Just punt the message.
+ */
+ ahd_dump_card_state(ahd);
+ ahd_clear_intstat(ahd);
+ ahd_restart(ahd);
+ return;
+ }
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ if (devinfo.role == ROLE_INITIATOR) {
+ if (bus_phase == P_MESGOUT)
+ ahd_setup_initiator_msgout(ahd,
+ &devinfo,
+ scb);
+ else {
+ ahd->msg_type =
+ MSG_TYPE_INITIATOR_MSGIN;
+ ahd->msgin_index = 0;
+ }
+ }
+#ifdef AHD_TARGET_MODE
+ else {
+ if (bus_phase == P_MESGOUT) {
+ ahd->msg_type =
+ MSG_TYPE_TARGET_MSGOUT;
+ ahd->msgin_index = 0;
+ }
+ else
+ ahd_setup_target_msgin(ahd,
+ &devinfo,
+ scb);
+ }
+#endif
+ }
+
+ ahd_handle_message_phase(ahd);
+ break;
+ }
+ case NO_MATCH:
+ {
+ /* Ensure we don't leave the selection hardware on */
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+
+ printf("%s:%c:%d: no active SCB for reconnecting "
+ "target - issuing BUS DEVICE RESET\n",
+ ahd_name(ahd), 'A', ahd_inb(ahd, SELID) >> 4);
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "REG0 == 0x%x ACCUM = 0x%x\n",
+ ahd_inb(ahd, SAVED_SCSIID), ahd_inb(ahd, SAVED_LUN),
+ ahd_inw(ahd, REG0), ahd_inb(ahd, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n",
+ ahd_inb(ahd, SEQ_FLAGS), ahd_get_scbptr(ahd),
+ ahd_find_busy_tcl(ahd,
+ BUILD_TCL(ahd_inb(ahd, SAVED_SCSIID),
+ ahd_inb(ahd, SAVED_LUN))),
+ ahd_inw(ahd, SINDEX));
+ printf("SELID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_CONTROL == 0x%x\n",
+ ahd_inb(ahd, SELID), ahd_inb_scbram(ahd, SCB_SCSIID),
+ ahd_inb_scbram(ahd, SCB_LUN),
+ ahd_inb_scbram(ahd, SCB_CONTROL));
+ printf("SCSIBUS[0] == 0x%x, SCSISIGI == 0x%x\n",
+ ahd_inb(ahd, SCSIBUS), ahd_inb(ahd, SCSISIGI));
+ printf("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
+ printf("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
+ ahd_dump_card_state(ahd);
+ ahd->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahd->msgout_len = 1;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_assert_atn(ahd);
+ break;
+ }
+ case PROTO_VIOLATION:
+ {
+ ahd_handle_proto_violation(ahd);
+ break;
+ }
+ case IGN_WIDE_RES:
+ {
+ struct ahd_devinfo devinfo;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ ahd_handle_ign_wide_residue(ahd, &devinfo);
+ break;
+ }
+ case BAD_PHASE:
+ {
+ u_int lastphase;
+
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ printf("%s:%c:%d: unknown scsi bus phase %x, "
+ "lastphase = 0x%x. Attempting to continue\n",
+ ahd_name(ahd), 'A',
+ SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+ lastphase, ahd_inb(ahd, SCSISIGI));
+ break;
+ }
+ case MISSED_BUSFREE:
+ {
+ u_int lastphase;
+
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ printf("%s:%c:%d: Missed busfree. "
+ "Lastphase = 0x%x, Curphase = 0x%x\n",
+ ahd_name(ahd), 'A',
+ SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+ lastphase, ahd_inb(ahd, SCSISIGI));
+ ahd_restart(ahd);
+ return;
+ }
+ case DATA_OVERRUN:
+ {
+ /*
+ * When the sequencer detects an overrun, it
+ * places the controller in "BITBUCKET" mode
+ * and allows the target to complete its transfer.
+ * Unfortunately, none of the counters get updated
+ * when the controller is in this mode, so we have
+ * no way of knowing how large the overrun was.
+ */
+ struct scb *scb;
+ u_int scbindex;
+#ifdef AHD_DEBUG
+ u_int lastphase;
+#endif
+
+ scbindex = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbindex);
+#ifdef AHD_DEBUG
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("data overrun detected %s. Tag == 0x%x.\n",
+ ahd_lookup_phase_entry(lastphase)->phasemsg,
+ SCB_GET_TAG(scb));
+ ahd_print_path(ahd, scb);
+ printf("%s seen Data Phase. Length = %ld. "
+ "NumSGs = %d.\n",
+ ahd_inb(ahd, SEQ_FLAGS) & DPHASE
+ ? "Have" : "Haven't",
+ ahd_get_transfer_length(scb), scb->sg_count);
+ ahd_dump_sglist(scb);
+ }
+#endif
+
+ /*
+ * Set this and it will take effect when the
+ * target does a command complete.
+ */
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ ahd_freeze_scb(scb);
+ break;
+ }
+ case MKMSG_FAILED:
+ {
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+ ahd_name(ahd), devinfo.channel, devinfo.target,
+ devinfo.lun);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL
+ && (scb->flags & SCB_RECOVERY_SCB) != 0)
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+ break;
+ }
+ case TASKMGMT_FUNC_COMPLETE:
+ {
+ u_int scbid;
+ struct scb *scb;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL) {
+ u_int lun;
+ u_int tag;
+ cam_status error;
+
+ ahd_print_path(ahd, scb);
+ printf("Task Management Func 0x%x Complete\n",
+ scb->hscb->task_management);
+ lun = CAM_LUN_WILDCARD;
+ tag = SCB_LIST_NULL;
+
+ switch (scb->hscb->task_management) {
+ case SIU_TASKMGMT_ABORT_TASK:
+ tag = SCB_GET_TAG(scb);
+ case SIU_TASKMGMT_ABORT_TASK_SET:
+ case SIU_TASKMGMT_CLEAR_TASK_SET:
+ lun = scb->hscb->lun;
+ error = CAM_REQ_ABORTED;
+ ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+ 'A', lun, tag, ROLE_INITIATOR,
+ error);
+ break;
+ case SIU_TASKMGMT_LUN_RESET:
+ lun = scb->hscb->lun;
+ case SIU_TASKMGMT_TARGET_RESET:
+ {
+ struct ahd_devinfo devinfo;
+
+ ahd_scb_devinfo(ahd, &devinfo, scb);
+ error = CAM_BDR_SENT;
+ ahd_handle_devreset(ahd, &devinfo, lun,
+ CAM_BDR_SENT,
+ lun != CAM_LUN_WILDCARD
+ ? "Lun Reset"
+ : "Target Reset",
+ /*verbose_level*/0);
+ break;
+ }
+ default:
+ panic("Unexpected TaskMgmt Func\n");
+ break;
+ }
+ }
+ break;
+ }
+ case TASKMGMT_CMD_CMPLT_OKAY:
+ {
+ u_int scbid;
+ struct scb *scb;
+
+ /*
+ * An ABORT TASK TMF failed to be delivered before
+ * the targeted command completed normally.
+ */
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL) {
+ /*
+ * Remove the second instance of this SCB from
+ * the QINFIFO if it is still there.
+ */
+ ahd_print_path(ahd, scb);
+ printf("SCB completes before TMF\n");
+ /*
+ * Handle losing the race. Wait until any
+ * current selection completes. We will then
+ * set the TMF back to zero in this SCB so that
+ * the sequencer doesn't bother to issue another
+ * sequencer interrupt for its completion.
+ */
+ while ((ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
+ && (ahd_inb(ahd, SSTAT0) & SELDO) == 0
+ && (ahd_inb(ahd, SSTAT1) & SELTO) == 0)
+ ;
+ ahd_outb(ahd, SCB_TASK_MANAGEMENT, 0);
+ ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ }
+ break;
+ }
+ case TRACEPOINT0:
+ case TRACEPOINT1:
+ case TRACEPOINT2:
+ case TRACEPOINT3:
+ printf("%s: Tracepoint %d\n", ahd_name(ahd),
+ seqintcode - TRACEPOINT0);
+ break;
+ case NO_SEQINT:
+ break;
+ case SAW_HWERR:
+ ahd_handle_hwerrint(ahd);
+ break;
+ default:
+ printf("%s: Unexpected SEQINTCODE %d\n", ahd_name(ahd),
+ seqintcode);
+ break;
+ }
+ /*
+ * The sequencer is paused immediately on
+ * a SEQINT, so we should restart it when
+ * we're done.
+ */
+ ahd_unpause(ahd);
+}
+
+void
+ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
+{
+ struct scb *scb;
+ u_int status0;
+ u_int status3;
+ u_int status;
+ u_int lqistat1;
+ u_int lqostat0;
+ u_int scbid;
+ u_int busfreetime;
+
+ ahd_update_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ status3 = ahd_inb(ahd, SSTAT3) & (NTRAMPERR|OSRAMPERR);
+ status0 = ahd_inb(ahd, SSTAT0) & (IOERR|OVERRUN|SELDI|SELDO);
+ status = ahd_inb(ahd, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ lqistat1 = ahd_inb(ahd, LQISTAT1);
+ lqostat0 = ahd_inb(ahd, LQOSTAT0);
+ busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+ if ((status0 & (SELDI|SELDO)) != 0) {
+ u_int simode0;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ simode0 = ahd_inb(ahd, SIMODE0);
+ status0 &= simode0 & (IOERR|OVERRUN|SELDI|SELDO);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ }
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL
+ && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+ scb = NULL;
+
+ /* Make sure the sequencer is in a safe location. */
+ ahd_clear_critical_section(ahd);
+
+ if ((status0 & IOERR) != 0) {
+ u_int now_lvd;
+
+ now_lvd = ahd_inb(ahd, SBLKCTL) & ENAB40;
+ printf("%s: Transceiver State Has Changed to %s mode\n",
+ ahd_name(ahd), now_lvd ? "LVD" : "SE");
+ ahd_outb(ahd, CLRSINT0, CLRIOERR);
+ /*
+ * A change in I/O mode is equivalent to a bus reset.
+ */
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ ahd_pause(ahd);
+ ahd_setup_iocell_workaround(ahd);
+ ahd_unpause(ahd);
+ } else if ((status0 & OVERRUN) != 0) {
+ printf("%s: SCSI offset overrun detected. Resetting bus.\n",
+ ahd_name(ahd));
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ } else if ((status & SCSIRSTI) != 0) {
+ printf("%s: Someone reset channel A\n", ahd_name(ahd));
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
+ } else if ((status & SCSIPERR) != 0) {
+ ahd_handle_transmission_error(ahd);
+ } else if (lqostat0 != 0) {
+ printf("%s: lqostat0 == 0x%x!\n", ahd_name(ahd), lqostat0);
+ ahd_outb(ahd, CLRLQOINT0, lqostat0);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+ } else if ((status & SELTO) != 0) {
+ u_int scbid;
+
+ /* Stop the selection */
+ ahd_outb(ahd, SCSISEQ0, 0);
+
+ /* No more pending messages */
+ ahd_clear_msg_state(ahd);
+
+ /* Clear interrupt state */
+ ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Although the driver does not care about the
+ * 'Selection in Progress' status bit, the busy
+ * LED does. SELINGO is only cleared by a sucessfull
+ * selection, so we must manually clear it to insure
+ * the LED turns off just incase no future successful
+ * selections occur (e.g. no devices on the bus).
+ */
+ ahd_outb(ahd, CLRSINT0, CLRSELINGO);
+
+ scbid = ahd_inw(ahd, WAITING_TID_HEAD);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: ahd_intr - referenced scb not "
+ "valid during SELTO scb(0x%x)\n",
+ ahd_name(ahd), scbid);
+ ahd_dump_card_state(ahd);
+ } else {
+ struct ahd_devinfo devinfo;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SELTO) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Saw Selection Timeout for SCB 0x%x\n",
+ scbid);
+ }
+#endif
+ /*
+ * Force a renegotiation with this target just in
+ * case the cable was pulled and will later be
+ * re-attached. The target may forget its negotiation
+ * settings with us should it attempt to reselect
+ * during the interruption. The target will not issue
+ * a unit attention in this case, so we must always
+ * renegotiate.
+ */
+ ahd_scb_devinfo(ahd, &devinfo, scb);
+ ahd_force_renegotiation(ahd, &devinfo);
+ ahd_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+ ahd_freeze_devq(ahd, scb);
+ }
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_iocell_first_selection(ahd);
+ ahd_unpause(ahd);
+ } else if ((status0 & (SELDI|SELDO)) != 0) {
+ ahd_iocell_first_selection(ahd);
+ ahd_unpause(ahd);
+ } else if (status3 != 0) {
+ printf("%s: SCSI Cell parity error SSTAT3 == 0x%x\n",
+ ahd_name(ahd), status3);
+ ahd_outb(ahd, CLRSINT3, status3);
+ } else if ((lqistat1 & (LQIPHASE_LQ|LQIPHASE_NLQ)) != 0) {
+ ahd_handle_lqiphase_error(ahd, lqistat1);
+ } else if ((lqistat1 & LQICRCI_NLQ) != 0) {
+ /*
+ * This status can be delayed during some
+ * streaming operations. The SCSIPHASE
+ * handler has already dealt with this case
+ * so just clear the error.
+ */
+ ahd_outb(ahd, CLRLQIINT1, CLRLQICRCI_NLQ);
+ } else if ((status & BUSFREE) != 0) {
+ u_int lqostat1;
+ int restart;
+ int clear_fifo;
+ int packetized;
+ u_int mode;
+
+ /*
+ * Clear our selection hardware as soon as possible.
+ * We may have an entry in the waiting Q for this target,
+ * that is affected by this busfree and we don't want to
+ * go about selecting the target while we handle the event.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+
+ /*
+ * Determine what we were up to at the time of
+ * the busfree.
+ */
+ mode = AHD_MODE_SCSI;
+ busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+ lqostat1 = ahd_inb(ahd, LQOSTAT1);
+ switch (busfreetime) {
+ case BUSFREE_DFF0:
+ case BUSFREE_DFF1:
+ {
+ u_int scbid;
+ struct scb *scb;
+
+ mode = busfreetime == BUSFREE_DFF0
+ ? AHD_MODE_DFF0 : AHD_MODE_DFF1;
+ ahd_set_modes(ahd, mode, mode);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Invalid SCB %d in DFF%d "
+ "during unexpected busfree\n",
+ ahd_name(ahd), scbid, mode);
+ packetized = 0;
+ } else
+ packetized = (scb->flags & SCB_PACKETIZED) != 0;
+ clear_fifo = 1;
+ break;
+ }
+ case BUSFREE_LQO:
+ clear_fifo = 0;
+ packetized = 1;
+ break;
+ default:
+ clear_fifo = 0;
+ packetized = (lqostat1 & LQOBUSFREE) != 0;
+ if (!packetized
+ && ahd_inb(ahd, LASTPHASE) == P_BUSFREE)
+ packetized = 1;
+ break;
+ }
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("Saw Busfree. Busfreetime = 0x%x.\n",
+ busfreetime);
+#endif
+ /*
+ * Busfrees that occur in non-packetized phases are
+ * handled by the nonpkt_busfree handler.
+ */
+ if (packetized && ahd_inb(ahd, LASTPHASE) == P_BUSFREE) {
+ restart = ahd_handle_pkt_busfree(ahd, busfreetime);
+ } else {
+ packetized = 0;
+ restart = ahd_handle_nonpkt_busfree(ahd);
+ }
+ /*
+ * Clear the busfree interrupt status. The setting of
+ * the interrupt is a pulse, so in a perfect world, we
+ * would not need to muck with the ENBUSFREE logic. This
+ * would ensure that if the bus moves on to another
+ * connection, busfree protection is still in force. If
+ * BUSFREEREV is broken, however, we must manually clear
+ * the ENBUSFREE if the busfree occurred during a non-pack
+ * connection so that we don't get false positives during
+ * future, packetized, connections.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
+ if (packetized == 0
+ && (ahd->bugs & AHD_BUSFREEREV_BUG) != 0)
+ ahd_outb(ahd, SIMODE1,
+ ahd_inb(ahd, SIMODE1) & ~ENBUSFREE);
+
+ if (clear_fifo)
+ ahd_clear_fifo(ahd, mode);
+
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ if (restart) {
+ ahd_restart(ahd);
+ } else {
+ ahd_unpause(ahd);
+ }
+ } else {
+ printf("%s: Missing case in ahd_handle_scsiint. status = %x\n",
+ ahd_name(ahd), status);
+ ahd_dump_card_state(ahd);
+ ahd_clear_intstat(ahd);
+ ahd_unpause(ahd);
+ }
+}
+
+static void
+ahd_handle_transmission_error(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ u_int scbid;
+ u_int lqistat1;
+ u_int lqistat2;
+ u_int msg_out;
+ u_int curphase;
+ u_int lastphase;
+ u_int perrdiag;
+ u_int cur_col;
+ int silent;
+
+ scb = NULL;
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ lqistat1 = ahd_inb(ahd, LQISTAT1) & ~(LQIPHASE_LQ|LQIPHASE_NLQ);
+ lqistat2 = ahd_inb(ahd, LQISTAT2);
+ if ((lqistat1 & (LQICRCI_NLQ|LQICRCI_LQ)) == 0
+ && (ahd->bugs & AHD_NLQICRC_DELAYED_BUG) != 0) {
+ u_int lqistate;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ lqistate = ahd_inb(ahd, LQISTATE);
+ if ((lqistate >= 0x1E && lqistate <= 0x24)
+ || (lqistate == 0x29)) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+ printf("%s: NLQCRC found via LQISTATE\n",
+ ahd_name(ahd));
+ }
+#endif
+ lqistat1 |= LQICRCI_NLQ;
+ }
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ }
+
+ ahd_outb(ahd, CLRLQIINT1, lqistat1);
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ perrdiag = ahd_inb(ahd, PERRDIAG);
+ msg_out = MSG_INITIATOR_DET_ERR;
+ ahd_outb(ahd, CLRSINT1, CLRSCSIPERR);
+
+ /*
+ * Try to find the SCB associated with this error.
+ */
+ silent = FALSE;
+ if (lqistat1 == 0
+ || (lqistat1 & LQICRCI_NLQ) != 0) {
+ if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0)
+ ahd_set_active_fifo(ahd);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL && SCB_IS_SILENT(scb))
+ silent = TRUE;
+ }
+
+ cur_col = 0;
+ if (silent == FALSE) {
+ printf("%s: Transmission error detected\n", ahd_name(ahd));
+ ahd_lqistat1_print(lqistat1, &cur_col, 50);
+ ahd_lastphase_print(lastphase, &cur_col, 50);
+ ahd_scsisigi_print(curphase, &cur_col, 50);
+ ahd_perrdiag_print(perrdiag, &cur_col, 50);
+ printf("\n");
+ ahd_dump_card_state(ahd);
+ }
+
+ if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) {
+ if (silent == FALSE) {
+ printf("%s: Gross protocol error during incoming "
+ "packet. lqistat1 == 0x%x. Resetting bus.\n",
+ ahd_name(ahd), lqistat1);
+ }
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ } else if ((lqistat1 & LQICRCI_LQ) != 0) {
+ /*
+ * A CRC error has been detected on an incoming LQ.
+ * The bus is currently hung on the last ACK.
+ * Hit LQIRETRY to release the last ack, and
+ * wait for the sequencer to determine that ATNO
+ * is asserted while in message out to take us
+ * to our host message loop. No NONPACKREQ or
+ * LQIPHASE type errors will occur in this
+ * scenario. After this first LQIRETRY, the LQI
+ * manager will be in ISELO where it will
+ * happily sit until another packet phase begins.
+ * Unexpected bus free detection is enabled
+ * through any phases that occur after we release
+ * this last ack until the LQI manager sees a
+ * packet phase. This implies we may have to
+ * ignore a perfectly valid "unexected busfree"
+ * after our "initiator detected error" message is
+ * sent. A busfree is the expected response after
+ * we tell the target that it's L_Q was corrupted.
+ * (SPI4R09 10.7.3.3.3)
+ */
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ printf("LQIRetry for LQICRCI_LQ to release ACK\n");
+ } else if ((lqistat1 & LQICRCI_NLQ) != 0) {
+ /*
+ * We detected a CRC error in a NON-LQ packet.
+ * The hardware has varying behavior in this situation
+ * depending on whether this packet was part of a
+ * stream or not.
+ *
+ * PKT by PKT mode:
+ * The hardware has already acked the complete packet.
+ * If the target honors our outstanding ATN condition,
+ * we should be (or soon will be) in MSGOUT phase.
+ * This will trigger the LQIPHASE_LQ status bit as the
+ * hardware was expecting another LQ. Unexpected
+ * busfree detection is enabled. Once LQIPHASE_LQ is
+ * true (first entry into host message loop is much
+ * the same), we must clear LQIPHASE_LQ and hit
+ * LQIRETRY so the hardware is ready to handle
+ * a future LQ. NONPACKREQ will not be asserted again
+ * once we hit LQIRETRY until another packet is
+ * processed. The target may either go busfree
+ * or start another packet in response to our message.
+ *
+ * Read Streaming P0 asserted:
+ * If we raise ATN and the target completes the entire
+ * stream (P0 asserted during the last packet), the
+ * hardware will ack all data and return to the ISTART
+ * state. When the target reponds to our ATN condition,
+ * LQIPHASE_LQ will be asserted. We should respond to
+ * this with an LQIRETRY to prepare for any future
+ * packets. NONPACKREQ will not be asserted again
+ * once we hit LQIRETRY until another packet is
+ * processed. The target may either go busfree or
+ * start another packet in response to our message.
+ * Busfree detection is enabled.
+ *
+ * Read Streaming P0 not asserted:
+ * If we raise ATN and the target transitions to
+ * MSGOUT in or after a packet where P0 is not
+ * asserted, the hardware will assert LQIPHASE_NLQ.
+ * We should respond to the LQIPHASE_NLQ with an
+ * LQIRETRY. Should the target stay in a non-pkt
+ * phase after we send our message, the hardware
+ * will assert LQIPHASE_LQ. Recovery is then just as
+ * listed above for the read streaming with P0 asserted.
+ * Busfree detection is enabled.
+ */
+ if (silent == FALSE)
+ printf("LQICRC_NLQ\n");
+ if (scb == NULL) {
+ printf("%s: No SCB valid for LQICRC_NLQ. "
+ "Resetting bus\n", ahd_name(ahd));
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ }
+ } else if ((lqistat1 & LQIBADLQI) != 0) {
+ printf("Need to handle BADLQI!\n");
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ return;
+ } else if ((perrdiag & (PARITYERR|PREVPHASE)) == PARITYERR) {
+ if ((curphase & ~P_DATAIN_DT) != 0) {
+ /* Ack the byte. So we can continue. */
+ if (silent == FALSE)
+ printf("Acking %s to clear perror\n",
+ ahd_lookup_phase_entry(curphase)->phasemsg);
+ ahd_inb(ahd, SCSIDAT);
+ }
+
+ if (curphase == P_MESGIN)
+ msg_out = MSG_PARITY_ERROR;
+ }
+
+ /*
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
+ * need to do is stuff the message buffer with
+ * the appropriate message. "In" phases have set
+ * mesg_out to something other than MSG_NOP.
+ */
+ ahd->send_msg_perror = msg_out;
+ if (scb != NULL && msg_out == MSG_INITIATOR_DET_ERR)
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_unpause(ahd);
+}
+
+static void
+ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
+{
+ /*
+ * Clear the sources of the interrupts.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRLQIINT1, lqistat1);
+
+ /*
+ * If the "illegal" phase changes were in response
+ * to our ATN to flag a CRC error, AND we ended up
+ * on packet boundaries, clear the error, restart the
+ * LQI manager as appropriate, and go on our merry
+ * way toward sending the message. Otherwise, reset
+ * the bus to clear the error.
+ */
+ ahd_set_active_fifo(ahd);
+ if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0
+ && (ahd_inb(ahd, MDFFSTAT) & DLZERO) != 0) {
+ if ((lqistat1 & LQIPHASE_LQ) != 0) {
+ printf("LQIRETRY for LQIPHASE_LQ\n");
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ } else if ((lqistat1 & LQIPHASE_NLQ) != 0) {
+ printf("LQIRETRY for LQIPHASE_NLQ\n");
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ } else
+ panic("ahd_handle_lqiphase_error: No phase errors\n");
+ ahd_dump_card_state(ahd);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_unpause(ahd);
+ } else {
+ printf("Reseting Channel for LQI Phase error\n");
+ ahd_dump_card_state(ahd);
+ ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+ }
+}
+
+/*
+ * Packetized unexpected or expected busfree.
+ * Entered in mode based on busfreetime.
+ */
+static int
+ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime)
+{
+ u_int lqostat1;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ lqostat1 = ahd_inb(ahd, LQOSTAT1);
+ if ((lqostat1 & LQOBUSFREE) != 0) {
+ struct scb *scb;
+ u_int scbid;
+ u_int saved_scbptr;
+ u_int waiting_h;
+ u_int waiting_t;
+ u_int next;
+
+ if ((busfreetime & BUSFREE_LQO) == 0)
+ printf("%s: Warning, BUSFREE time is 0x%x. "
+ "Expected BUSFREE_LQO.\n",
+ ahd_name(ahd), busfreetime);
+ /*
+ * The LQO manager detected an unexpected busfree
+ * either:
+ *
+ * 1) During an outgoing LQ.
+ * 2) After an outgoing LQ but before the first
+ * REQ of the command packet.
+ * 3) During an outgoing command packet.
+ *
+ * In all cases, CURRSCB is pointing to the
+ * SCB that encountered the failure. Clean
+ * up the queue, clear SELDO and LQOBUSFREE,
+ * and allow the sequencer to restart the select
+ * out at its lesure.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ scbid = ahd_inw(ahd, CURRSCB);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL)
+ panic("SCB not valid during LQOBUSFREE");
+ /*
+ * Clear the status.
+ */
+ ahd_outb(ahd, CLRLQOINT1, CLRLQOBUSFREE);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0)
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, CLRSINT0, CLRSELDO);
+
+ /*
+ * Return the LQO manager to its idle loop. It will
+ * not do this automatically if the busfree occurs
+ * after the first REQ of either the LQ or command
+ * packet or between the LQ and command packet.
+ */
+ ahd_outb(ahd, LQCTL2, ahd_inb(ahd, LQCTL2) | LQOTOIDLE);
+
+ /*
+ * Update the waiting for selection queue so
+ * we restart on the correct SCB.
+ */
+ waiting_h = ahd_inw(ahd, WAITING_TID_HEAD);
+ saved_scbptr = ahd_get_scbptr(ahd);
+ if (waiting_h != scbid) {
+
+ ahd_outw(ahd, WAITING_TID_HEAD, scbid);
+ waiting_t = ahd_inw(ahd, WAITING_TID_TAIL);
+ if (waiting_t == waiting_h) {
+ ahd_outw(ahd, WAITING_TID_TAIL, scbid);
+ next = SCB_LIST_NULL;
+ } else {
+ ahd_set_scbptr(ahd, waiting_h);
+ next = ahd_inw_scbram(ahd, SCB_NEXT2);
+ }
+ ahd_set_scbptr(ahd, scbid);
+ ahd_outw(ahd, SCB_NEXT2, next);
+ }
+ ahd_set_scbptr(ahd, saved_scbptr);
+ if (scb->crc_retry_count < AHD_MAX_LQ_CRC_ERRORS) {
+ if (SCB_IS_SILENT(scb) == FALSE) {
+ ahd_print_path(ahd, scb);
+ printf("Probable outgoing LQ CRC error. "
+ "Retrying command\n");
+ }
+ scb->crc_retry_count++;
+ } else {
+ ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
+ ahd_freeze_scb(scb);
+ ahd_freeze_devq(ahd, scb);
+ }
+ /* Return unpausing the sequencer. */
+ return (0);
+ } else if ((ahd_inb(ahd, PERRDIAG) & PARITYERR) != 0) {
+ /*
+ * Ignore what are really parity errors that
+ * occur on the last REQ of a free running
+ * clock prior to going busfree. Some drives
+ * do not properly active negate just before
+ * going busfree resulting in a parity glitch.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRSCSIPERR|CLRBUSFREE);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MASKED_ERRORS) != 0)
+ printf("%s: Parity on last REQ detected "
+ "during busfree phase.\n",
+ ahd_name(ahd));
+#endif
+ /* Return unpausing the sequencer. */
+ return (0);
+ }
+ if (ahd->src_mode != AHD_MODE_SCSI) {
+ u_int scbid;
+ struct scb *scb;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ ahd_print_path(ahd, scb);
+ printf("Unexpected PKT busfree condition\n");
+ ahd_dump_card_state(ahd);
+ ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), 'A',
+ SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+ ROLE_INITIATOR, CAM_UNEXP_BUSFREE);
+
+ /* Return restarting the sequencer. */
+ return (1);
+ }
+ printf("%s: Unexpected PKT busfree condition\n", ahd_name(ahd));
+ ahd_dump_card_state(ahd);
+ /* Restart the sequencer. */
+ return (1);
+}
+
+/*
+ * Non-packetized unexpected or expected busfree.
+ */
+static int
+ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
+{
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ u_int lastphase;
+ u_int saved_scsiid;
+ u_int saved_lun;
+ u_int target;
+ u_int initiator_role_id;
+ u_int scbid;
+ u_int ppr_busfree;
+ int printerror;
+
+ /*
+ * Look at what phase we were last in. If its message out,
+ * chances are pretty good that the busfree was in response
+ * to one of our abort requests.
+ */
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+ saved_lun = ahd_inb(ahd, SAVED_LUN);
+ target = SCSIID_TARGET(ahd, saved_scsiid);
+ initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+ ahd_compile_devinfo(&devinfo, initiator_role_id,
+ target, saved_lun, 'A', ROLE_INITIATOR);
+ printerror = 1;
+
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb != NULL
+ && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+ scb = NULL;
+
+ ppr_busfree = (ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0;
+ if (lastphase == P_MESGOUT) {
+ u_int tag;
+
+ tag = SCB_LIST_NULL;
+ if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT_TAG, TRUE)
+ || ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT, TRUE)) {
+ int found;
+ int sent_msg;
+
+ if (scb == NULL) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Abort for unidentified "
+ "connection completed.\n");
+ /* restart the sequencer. */
+ return (1);
+ }
+ sent_msg = ahd->msgout_buf[ahd->msgout_index - 1];
+ ahd_print_path(ahd, scb);
+ printf("SCB %d - Abort%s Completed.\n",
+ SCB_GET_TAG(scb),
+ sent_msg == MSG_ABORT_TAG ? "" : " Tag");
+
+ if (sent_msg == MSG_ABORT_TAG)
+ tag = SCB_GET_TAG(scb);
+
+ if ((scb->flags & SCB_CMDPHASE_ABORT) != 0) {
+ /*
+ * This abort is in response to an
+ * unexpected switch to command phase
+ * for a packetized connection. Since
+ * the identify message was never sent,
+ * "saved lun" is 0. We really want to
+ * abort only the SCB that encountered
+ * this error, which could have a different
+ * lun. The SCB will be retried so the OS
+ * will see the UA after renegotiating to
+ * packetized.
+ */
+ tag = SCB_GET_TAG(scb);
+ saved_lun = scb->hscb->lun;
+ }
+ found = ahd_abort_scbs(ahd, target, 'A', saved_lun,
+ tag, ROLE_INITIATOR,
+ CAM_REQ_ABORTED);
+ printf("found == 0x%x\n", found);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_1B,
+ MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+ /*
+ * Don't mark the user's request for this BDR
+ * as completing with CAM_BDR_SENT. CAM3
+ * specifies CAM_REQ_CMP.
+ */
+ if (scb != NULL
+ && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+ && ahd_match_scb(ahd, scb, target, 'A',
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_INITIATOR))
+ ahd_set_transaction_status(scb, CAM_REQ_CMP);
+#endif
+ ahd_handle_devreset(ahd, &devinfo, CAM_LUN_WILDCARD,
+ CAM_BDR_SENT, "Bus Device Reset",
+ /*verbose_level*/0);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE)
+ && ppr_busfree == 0) {
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ /*
+ * PPR Rejected. Try non-ppr negotiation
+ * and retry command.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("PPR negotiation rejected busfree.\n");
+#endif
+ tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->goal.ppr_options = 0;
+ ahd_qinfifo_requeue_tail(ahd, scb);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE)
+ && ppr_busfree == 0) {
+ /*
+ * Negotiation Rejected. Go-narrow and
+ * retry command.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("WDTR negotiation rejected busfree.\n");
+#endif
+ ahd_set_width(ahd, &devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahd_qinfifo_requeue_tail(ahd, scb);
+ printerror = 0;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE)
+ && ppr_busfree == 0) {
+ /*
+ * Negotiation Rejected. Go-async and
+ * retry command.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("SDTR negotiation rejected busfree.\n");
+#endif
+ ahd_set_syncrate(ahd, &devinfo,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahd_qinfifo_requeue_tail(ahd, scb);
+ printerror = 0;
+ } else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0
+ && ahd_sent_msg(ahd, AHDMSG_1B,
+ MSG_INITIATOR_DET_ERR, TRUE)) {
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Expected IDE Busfree\n");
+#endif
+ printerror = 0;
+ } else if ((ahd->msg_flags & MSG_FLAG_EXPECT_QASREJ_BUSFREE)
+ && ahd_sent_msg(ahd, AHDMSG_1B,
+ MSG_MESSAGE_REJECT, TRUE)) {
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Expected QAS Reject Busfree\n");
+#endif
+ printerror = 0;
+ }
+ }
+
+ /*
+ * The busfree required flag is honored at the end of
+ * the message phases. We check it last in case we
+ * had to send some other message that caused a busfree.
+ */
+ if (printerror != 0
+ && (lastphase == P_MESGIN || lastphase == P_MESGOUT)
+ && ((ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0)) {
+
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahd_freeze_scb(scb);
+ if ((ahd->msg_flags & MSG_FLAG_IU_REQ_CHANGED) != 0) {
+ ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQ_ABORTED);
+ } else {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("PPR Negotiation Busfree.\n");
+#endif
+ ahd_done(ahd, scb);
+ }
+ printerror = 0;
+ }
+ if (printerror != 0) {
+ int aborted;
+
+ aborted = 0;
+ if (scb != NULL) {
+ u_int tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = SCB_GET_TAG(scb);
+ else
+ tag = SCB_LIST_NULL;
+ ahd_print_path(ahd, scb);
+ aborted = ahd_abort_scbs(ahd, target, 'A',
+ SCB_GET_LUN(scb), tag,
+ ROLE_INITIATOR,
+ CAM_UNEXP_BUSFREE);
+ } else {
+ /*
+ * We had not fully identified this connection,
+ * so we cannot abort anything.
+ */
+ printf("%s: ", ahd_name(ahd));
+ }
+ if (lastphase != P_BUSFREE)
+ ahd_force_renegotiation(ahd, &devinfo);
+ printf("Unexpected busfree %s, %d SCBs aborted, "
+ "PRGMCNT == 0x%x\n",
+ ahd_lookup_phase_entry(lastphase)->phasemsg,
+ aborted,
+ ahd_inb(ahd, PRGMCNT)
+ | (ahd_inb(ahd, PRGMCNT+1) << 8));
+ ahd_dump_card_state(ahd);
+ }
+ /* Always restart the sequencer. */
+ return (1);
+}
+
+static void
+ahd_handle_proto_violation(struct ahd_softc *ahd)
+{
+ struct ahd_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+ u_int seq_flags;
+ u_int curphase;
+ u_int lastphase;
+ int found;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ scbid = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scbid);
+ seq_flags = ahd_inb(ahd, SEQ_FLAGS);
+ curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+ lastphase = ahd_inb(ahd, LASTPHASE);
+ if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+ /*
+ * The reconnecting target either did not send an
+ * identify message, or did, but we didn't find an SCB
+ * to match.
+ */
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x.\n", lastphase);
+ scb = NULL;
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("No SCB found during protocol violation\n");
+ goto proto_violation_reset;
+ } else {
+ ahd_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+ if ((seq_flags & NO_CDB_SENT) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("No or incomplete CDB sent to device.\n");
+ } else if ((ahd_inb_scbram(ahd, SCB_CONTROL)
+ & STATUS_RCVD) == 0) {
+ /*
+ * The target never bothered to provide status to
+ * us prior to completing the command. Since we don't
+ * know the disposition of this command, we must attempt
+ * to abort it. Assert ATN and prepare to send an abort
+ * message.
+ */
+ ahd_print_path(ahd, scb);
+ printf("Completed command without status.\n");
+ } else {
+ ahd_print_path(ahd, scb);
+ printf("Unknown protocol violation.\n");
+ ahd_dump_card_state(ahd);
+ }
+ }
+ if ((lastphase & ~P_DATAIN_DT) == 0
+ || lastphase == P_COMMAND) {
+proto_violation_reset:
+ /*
+ * Target either went directly to data
+ * phase or didn't respond to our ATN.
+ * The only safe thing to do is to blow
+ * it away with a bus reset.
+ */
+ found = ahd_reset_channel(ahd, 'A', TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahd_name(ahd), 'A', found);
+ } else {
+ /*
+ * Leave the selection hardware off in case
+ * this abort attempt will affect yet to
+ * be sent commands.
+ */
+ ahd_outb(ahd, SCSISEQ0,
+ ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ ahd_assert_atn(ahd);
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ if (scb == NULL) {
+ ahd_print_devinfo(ahd, &devinfo);
+ ahd->msgout_buf[0] = MSG_ABORT_TASK;
+ ahd->msgout_len = 1;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ } else {
+ ahd_print_path(ahd, scb);
+ scb->flags |= SCB_ABORT;
+ }
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahd_lookup_phase_entry(curphase)->phasemsg);
+ }
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahd_force_renegotiation(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct ahd_initiator_tinfo *targ_info;
+ struct ahd_tmode_tstate *tstate;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Forcing renegotiation\n");
+ }
+#endif
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target,
+ &tstate);
+ ahd_update_neg_request(ahd, devinfo, tstate,
+ targ_info, AHD_NEG_IF_NON_ASYNC);
+}
+
+#define AHD_MAX_STEPS 2000
+void
+ahd_clear_critical_section(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ int stepping;
+ int steps;
+ int first_instr;
+ u_int simode0;
+ u_int simode1;
+ u_int simode3;
+ u_int lqimode0;
+ u_int lqimode1;
+ u_int lqomode0;
+ u_int lqomode1;
+
+ if (ahd->num_critical_sections == 0)
+ return;
+
+ stepping = FALSE;
+ steps = 0;
+ first_instr = 0;
+ simode0 = 0;
+ simode1 = 0;
+ simode3 = 0;
+ lqimode0 = 0;
+ lqimode1 = 0;
+ lqomode0 = 0;
+ lqomode1 = 0;
+ saved_modes = ahd_save_modes(ahd);
+ for (;;) {
+ struct cs *cs;
+ u_int seqaddr;
+ u_int i;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ seqaddr = ahd_inb(ahd, CURADDR)
+ | (ahd_inb(ahd, CURADDR+1) << 8);
+
+ cs = ahd->critical_sections;
+ for (i = 0; i < ahd->num_critical_sections; i++, cs++) {
+
+ if (cs->begin < seqaddr && cs->end >= seqaddr)
+ break;
+ }
+
+ if (i == ahd->num_critical_sections)
+ break;
+
+ if (steps > AHD_MAX_STEPS) {
+ printf("%s: Infinite loop in critical section\n"
+ "%s: First Instruction 0x%x now 0x%x\n",
+ ahd_name(ahd), ahd_name(ahd), first_instr,
+ seqaddr);
+ ahd_dump_card_state(ahd);
+ panic("critical section loop");
+ }
+
+ steps++;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: Single stepping at 0x%x\n", ahd_name(ahd),
+ seqaddr);
+#endif
+ if (stepping == FALSE) {
+
+ first_instr = seqaddr;
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ simode0 = ahd_inb(ahd, SIMODE0);
+ simode3 = ahd_inb(ahd, SIMODE3);
+ lqimode0 = ahd_inb(ahd, LQIMODE0);
+ lqimode1 = ahd_inb(ahd, LQIMODE1);
+ lqomode0 = ahd_inb(ahd, LQOMODE0);
+ lqomode1 = ahd_inb(ahd, LQOMODE1);
+ ahd_outb(ahd, SIMODE0, 0);
+ ahd_outb(ahd, SIMODE3, 0);
+ ahd_outb(ahd, LQIMODE0, 0);
+ ahd_outb(ahd, LQIMODE1, 0);
+ ahd_outb(ahd, LQOMODE0, 0);
+ ahd_outb(ahd, LQOMODE1, 0);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ simode1 = ahd_inb(ahd, SIMODE1);
+ /*
+ * We don't clear ENBUSFREE. Unfortunately
+ * we cannot re-enable busfree detection within
+ * the current connection, so we must leave it
+ * on while single stepping.
+ */
+ ahd_outb(ahd, SIMODE1, simode1 & ENBUSFREE);
+ ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) | STEP);
+ stepping = TRUE;
+ }
+ ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+ ahd_outb(ahd, HCNTRL, ahd->unpause);
+ while (!ahd_is_paused(ahd))
+ ahd_delay(200);
+ ahd_update_modes(ahd);
+ }
+ if (stepping) {
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ ahd_outb(ahd, SIMODE0, simode0);
+ ahd_outb(ahd, SIMODE3, simode3);
+ ahd_outb(ahd, LQIMODE0, lqimode0);
+ ahd_outb(ahd, LQIMODE1, lqimode1);
+ ahd_outb(ahd, LQOMODE0, lqomode0);
+ ahd_outb(ahd, LQOMODE1, lqomode1);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
+ ahd_outb(ahd, SIMODE1, simode1);
+ /*
+ * SCSIINT seems to glitch occassionally when
+ * the interrupt masks are restored. Clear SCSIINT
+ * one more time so that only persistent errors
+ * are seen as a real interrupt.
+ */
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ }
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahd_clear_intstat(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ /* Clear any interrupt conditions this may have caused */
+ ahd_outb(ahd, CLRLQIINT0, CLRLQIATNQAS|CLRLQICRCT1|CLRLQICRCT2
+ |CLRLQIBADLQT|CLRLQIATNLQ|CLRLQIATNCMD);
+ ahd_outb(ahd, CLRLQIINT1, CLRLQIPHASE_LQ|CLRLQIPHASE_NLQ|CLRLIQABORT
+ |CLRLQICRCI_LQ|CLRLQICRCI_NLQ|CLRLQIBADLQI
+ |CLRLQIOVERI_LQ|CLRLQIOVERI_NLQ|CLRNONPACKREQ);
+ ahd_outb(ahd, CLRLQOINT0, CLRLQOTARGSCBPERR|CLRLQOSTOPT2|CLRLQOATNLQ
+ |CLRLQOATNPKT|CLRLQOTCRC);
+ ahd_outb(ahd, CLRLQOINT1, CLRLQOINITSCBPERR|CLRLQOSTOPI2|CLRLQOBADQAS
+ |CLRLQOBUSFREE|CLRLQOPHACHGINPKT);
+ if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+ ahd_outb(ahd, CLRLQOINT0, 0);
+ ahd_outb(ahd, CLRLQOINT1, 0);
+ }
+ ahd_outb(ahd, CLRSINT3, CLRNTRAMPERR|CLROSRAMPERR);
+ ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
+ ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO
+ |CLRIOERR|CLROVERRUN);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHD_DEBUG
+uint32_t ahd_debug = AHD_DEBUG_OPTS;
+#endif
+void
+ahd_print_scb(struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ int i;
+
+ hscb = scb->hscb;
+ printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+ (void *)scb,
+ hscb->control,
+ hscb->scsiid,
+ hscb->lun,
+ hscb->cdb_len);
+ printf("Shared Data: ");
+ for (i = 0; i < sizeof(hscb->shared_data.idata.cdb); i++)
+ printf("%#02x", hscb->shared_data.idata.cdb[i]);
+ printf(" dataptr:%#x%x datacnt:%#x sgptr:%#x tag:%#x\n",
+ (uint32_t)((ahd_le64toh(hscb->dataptr) >> 32) & 0xFFFFFFFF),
+ (uint32_t)(ahd_le64toh(hscb->dataptr) & 0xFFFFFFFF),
+ ahd_le32toh(hscb->datacnt),
+ ahd_le32toh(hscb->sgptr),
+ SCB_GET_TAG(scb));
+ ahd_dump_sglist(scb);
+}
+
+void
+ahd_dump_sglist(struct scb *scb)
+{
+ int i;
+
+ if (scb->sg_count > 0) {
+ if ((scb->ahd_softc->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg_list;
+
+ sg_list = (struct ahd_dma64_seg*)scb->sg_list;
+ for (i = 0; i < scb->sg_count; i++) {
+ uint64_t addr;
+ uint32_t len;
+
+ addr = ahd_le64toh(sg_list[i].addr);
+ len = ahd_le32toh(sg_list[i].len);
+ printf("sg[%d] - Addr 0x%x%x : Length %d%s\n",
+ i,
+ (uint32_t)((addr >> 32) & 0xFFFFFFFF),
+ (uint32_t)(addr & 0xFFFFFFFF),
+ sg_list[i].len & AHD_SG_LEN_MASK,
+ (sg_list[i].len & AHD_DMA_LAST_SEG)
+ ? " Last" : "");
+ }
+ } else {
+ struct ahd_dma_seg *sg_list;
+
+ sg_list = (struct ahd_dma_seg*)scb->sg_list;
+ for (i = 0; i < scb->sg_count; i++) {
+ uint32_t len;
+
+ len = ahd_le32toh(sg_list[i].len);
+ printf("sg[%d] - Addr 0x%x%x : Length %d%s\n",
+ i,
+ (len & AHD_SG_HIGH_ADDR_MASK) >> 24,
+ ahd_le32toh(sg_list[i].addr),
+ len & AHD_SG_LEN_MASK,
+ len & AHD_DMA_LAST_SEG ? " Last" : "");
+ }
+ }
+ }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahd_tmode_tstate *
+ahd_alloc_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel)
+{
+ struct ahd_tmode_tstate *master_tstate;
+ struct ahd_tmode_tstate *tstate;
+ int i;
+
+ master_tstate = ahd->enabled_targets[ahd->our_id];
+ if (ahd->enabled_targets[scsi_id] != NULL
+ && ahd->enabled_targets[scsi_id] != master_tstate)
+ panic("%s: ahd_alloc_tstate - Target already allocated",
+ ahd_name(ahd));
+ tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL)
+ return (NULL);
+
+ /*
+ * If we have allocated a master tstate, copy user settings from
+ * the master tstate (taken from SRAM or the EEPROM) for this
+ * channel, but reset our current and goal settings to async/narrow
+ * until an initiator talks to us.
+ */
+ if (master_tstate != NULL) {
+ memcpy(tstate, master_tstate, sizeof(*tstate));
+ memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+ for (i = 0; i < 16; i++) {
+ memset(&tstate->transinfo[i].curr, 0,
+ sizeof(tstate->transinfo[i].curr));
+ memset(&tstate->transinfo[i].goal, 0,
+ sizeof(tstate->transinfo[i].goal));
+ }
+ } else
+ memset(tstate, 0, sizeof(*tstate));
+ ahd->enabled_targets[scsi_id] = tstate;
+ return (tstate);
+}
+
+#ifdef AHD_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force)
+{
+ struct ahd_tmode_tstate *tstate;
+
+ /*
+ * Don't clean up our "master" tstate.
+ * It has our default user settings.
+ */
+ if (scsi_id == ahd->our_id
+ && force == FALSE)
+ return;
+
+ tstate = ahd->enabled_targets[scsi_id];
+ if (tstate != NULL)
+ free(tstate, M_DEVBUF);
+ ahd->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest period to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+void
+ahd_devlimited_syncrate(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int *period, u_int *ppr_options, role_t role)
+{
+ struct ahd_transinfo *transinfo;
+ u_int maxsync;
+
+ if ((ahd_inb(ahd, SBLKCTL) & ENAB40) != 0
+ && (ahd_inb(ahd, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHD_SYNCRATE_PACED;
+ } else {
+ maxsync = AHD_SYNCRATE_ULTRA;
+ /* Can't do DT related options on an SE bus */
+ *ppr_options &= MSG_EXT_PPR_QAS_REQ;
+ }
+ /*
+ * Never allow a value higher than our current goal
+ * period otherwise we may allow a target initiated
+ * negotiation to go above the limit as set by the
+ * user. In the case of an initiator initiated
+ * sync negotiation, we limit based on the user
+ * setting. This allows the system to still accept
+ * incoming negotiations even if target initiated
+ * negotiation is not performed.
+ */
+ if (role == ROLE_TARGET)
+ transinfo = &tinfo->user;
+ else
+ transinfo = &tinfo->goal;
+ *ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN);
+ if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
+ maxsync = MAX(maxsync, AHD_SYNCRATE_ULTRA2);
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ if (transinfo->period == 0) {
+ *period = 0;
+ *ppr_options = 0;
+ } else {
+ *period = MAX(*period, transinfo->period);
+ ahd_find_syncrate(ahd, period, ppr_options, maxsync);
+ }
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+void
+ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+ u_int *ppr_options, u_int maxsync)
+{
+ if (*period < maxsync)
+ *period = maxsync;
+
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+ && *period > AHD_SYNCRATE_MIN_DT)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ if (*period > AHD_SYNCRATE_MIN)
+ *period = 0;
+
+ /* Honor PPR option conformance rules. */
+ if (*period > AHD_SYNCRATE_PACED)
+ *ppr_options &= ~MSG_EXT_PPR_RTI;
+
+ if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+ *ppr_options &= (MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_QAS_REQ);
+
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0)
+ *ppr_options &= MSG_EXT_PPR_QAS_REQ;
+
+ /* Skip all PACED only entries if IU is not available */
+ if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0
+ && *period < AHD_SYNCRATE_DT)
+ *period = AHD_SYNCRATE_DT;
+
+ /* Skip all DT only entries if DT is not available */
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && *period < AHD_SYNCRATE_ULTRA2)
+ *period = AHD_SYNCRATE_ULTRA2;
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahd_validate_offset(struct ahd_softc *ahd,
+ struct ahd_initiator_tinfo *tinfo,
+ u_int period, u_int *offset, int wide,
+ role_t role)
+{
+ u_int maxoffset;
+
+ /* Limit offset to what we can do */
+ if (period == 0)
+ maxoffset = 0;
+ else if (period <= AHD_SYNCRATE_PACED) {
+ if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0)
+ maxoffset = MAX_OFFSET_PACED_BUG;
+ else
+ maxoffset = MAX_OFFSET_PACED;
+ } else
+ maxoffset = MAX_OFFSET_NON_PACED;
+ *offset = MIN(*offset, maxoffset);
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *offset = MIN(*offset, tinfo->user.offset);
+ else
+ *offset = MIN(*offset, tinfo->goal.offset);
+ }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo,
+ u_int *bus_width, role_t role)
+{
+ switch (*bus_width) {
+ default:
+ if (ahd->features & AHD_WIDE) {
+ /* Respond Wide */
+ *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *bus_width = MIN(tinfo->user.width, *bus_width);
+ else
+ *bus_width = MIN(tinfo->goal.width, *bus_width);
+ }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity. This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct ahd_tmode_tstate *tstate,
+ struct ahd_initiator_tinfo *tinfo, ahd_neg_type neg_type)
+{
+ u_int auto_negotiate_orig;
+
+ auto_negotiate_orig = tstate->auto_negotiate;
+ if (neg_type == AHD_NEG_ALWAYS) {
+ /*
+ * Force our "current" settings to be
+ * unknown so that unless a bus reset
+ * occurs the need to renegotiate is
+ * recorded persistently.
+ */
+ if ((ahd->features & AHD_WIDE) != 0)
+ tinfo->curr.width = AHD_WIDTH_UNKNOWN;
+ tinfo->curr.period = AHD_PERIOD_UNKNOWN;
+ tinfo->curr.offset = AHD_OFFSET_UNKNOWN;
+ }
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+ || (neg_type == AHD_NEG_IF_NON_ASYNC
+ && (tinfo->goal.offset != 0
+ || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+ || tinfo->goal.ppr_options != 0)))
+ tstate->auto_negotiate |= devinfo->target_mask;
+ else
+ tstate->auto_negotiate &= ~devinfo->target_mask;
+
+ return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int period, u_int offset, u_int ppr_options,
+ u_int type, int paused)
+{
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int old_period;
+ u_int old_offset;
+ u_int old_ppr;
+ int active;
+ int update_needed;
+
+ active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+ update_needed = 0;
+
+ if (period == 0 || offset == 0) {
+ period = 0;
+ offset = 0;
+ }
+
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHD_TRANS_USER) != 0) {
+ tinfo->user.period = period;
+ tinfo->user.offset = offset;
+ tinfo->user.ppr_options = ppr_options;
+ }
+
+ if ((type & AHD_TRANS_GOAL) != 0) {
+ tinfo->goal.period = period;
+ tinfo->goal.offset = offset;
+ tinfo->goal.ppr_options = ppr_options;
+ }
+
+ old_period = tinfo->curr.period;
+ old_offset = tinfo->curr.offset;
+ old_ppr = tinfo->curr.ppr_options;
+
+ if ((type & AHD_TRANS_CUR) != 0
+ && (old_period != period
+ || old_offset != offset
+ || old_ppr != ppr_options)) {
+
+ update_needed++;
+
+ tinfo->curr.period = period;
+ tinfo->curr.offset = offset;
+ tinfo->curr.ppr_options = ppr_options;
+
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ if (offset != 0) {
+ int options;
+
+ printf("%s: target %d synchronous with "
+ "period = 0x%x, offset = 0x%x",
+ ahd_name(ahd), devinfo->target,
+ period, offset);
+ options = 0;
+ if ((ppr_options & MSG_EXT_PPR_RD_STRM) != 0) {
+ printf("(RDSTRM");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ printf("%s", options ? "|DT" : "(DT");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ printf("%s", options ? "|IU" : "(IU");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_RTI) != 0) {
+ printf("%s", options ? "|RTI" : "(RTI");
+ options++;
+ }
+ if ((ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
+ printf("%s", options ? "|QAS" : "(QAS");
+ options++;
+ }
+ if (options != 0)
+ printf(")\n");
+ else
+ printf("\n");
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers%s\n",
+ ahd_name(ahd), devinfo->target,
+ (ppr_options & MSG_EXT_PPR_QAS_REQ) != 0
+ ? "(QAS)" : "");
+ }
+ }
+ }
+ /*
+ * Always refresh the neg-table to handle the case of the
+ * sequencer setting the ENATNO bit for a MK_MESSAGE request.
+ * We will always renegotiate in that case if this is a
+ * packetized request. Also manage the busfree expected flag
+ * from this common routine so that we catch changes due to
+ * WDTR or SDTR messages.
+ */
+ if ((type & AHD_TRANS_CUR) != 0) {
+ if (!paused)
+ ahd_pause(ahd);
+ ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+ if (!paused)
+ ahd_unpause(ahd);
+ if (ahd->msg_type != MSG_TYPE_NONE) {
+ if ((old_ppr & MSG_EXT_PPR_IU_REQ)
+ != (ppr_options & MSG_EXT_PPR_IU_REQ)) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Expecting IU Change busfree\n");
+ }
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+ | MSG_FLAG_IU_REQ_CHANGED;
+ }
+ if ((old_ppr & MSG_EXT_PPR_IU_REQ) != 0) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("PPR with IU_REQ outstanding\n");
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE;
+ }
+ }
+ }
+
+ update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, AHD_NEG_TO_GOAL);
+
+ if (update_needed && active)
+ ahd_update_pending_scbs(ahd);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_width(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int width, u_int type, int paused)
+{
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int oldwidth;
+ int active;
+ int update_needed;
+
+ active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+ update_needed = 0;
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHD_TRANS_USER) != 0)
+ tinfo->user.width = width;
+
+ if ((type & AHD_TRANS_GOAL) != 0)
+ tinfo->goal.width = width;
+
+ oldwidth = tinfo->curr.width;
+ if ((type & AHD_TRANS_CUR) != 0 && oldwidth != width) {
+
+ update_needed++;
+
+ tinfo->curr.width = width;
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahd_name(ahd), devinfo->target,
+ 8 * (0x01 << width));
+ }
+ }
+
+ if ((type & AHD_TRANS_CUR) != 0) {
+ if (!paused)
+ ahd_pause(ahd);
+ ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+ if (!paused)
+ ahd_unpause(ahd);
+ }
+
+ update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, AHD_NEG_TO_GOAL);
+ if (update_needed && active)
+ ahd_update_pending_scbs(ahd);
+
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahd_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ ahd_queue_alg alg)
+{
+ ahd_platform_set_tags(ahd, devinfo, alg);
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+static void
+ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct ahd_transinfo *tinfo)
+{
+ ahd_mode_state saved_modes;
+ u_int period;
+ u_int ppr_opts;
+ u_int con_opts;
+ u_int offset;
+ u_int saved_negoaddr;
+ uint8_t iocell_opts[sizeof(ahd->iocell_opts)];
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ saved_negoaddr = ahd_inb(ahd, NEGOADDR);
+ ahd_outb(ahd, NEGOADDR, devinfo->target);
+ period = tinfo->period;
+ offset = tinfo->offset;
+ memcpy(iocell_opts, ahd->iocell_opts, sizeof(ahd->iocell_opts));
+ ppr_opts = tinfo->ppr_options & (MSG_EXT_PPR_QAS_REQ|MSG_EXT_PPR_DT_REQ
+ |MSG_EXT_PPR_IU_REQ|MSG_EXT_PPR_RTI);
+ con_opts = 0;
+ if (period == 0)
+ period = AHD_SYNCRATE_ASYNC;
+ if (period == AHD_SYNCRATE_160) {
+
+ if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0) {
+ /*
+ * When the SPI4 spec was finalized, PACE transfers
+ * was not made a configurable option in the PPR
+ * message. Instead it is assumed to be enabled for
+ * any syncrate faster than 80MHz. Nevertheless,
+ * Harpoon2A4 allows this to be configurable.
+ *
+ * Harpoon2A4 also assumes at most 2 data bytes per
+ * negotiated REQ/ACK offset. Paced transfers take
+ * 4, so we must adjust our offset.
+ */
+ ppr_opts |= PPROPT_PACE;
+ offset *= 2;
+
+ /*
+ * Harpoon2A assumed that there would be a
+ * fallback rate between 160MHz and 80Mhz,
+ * so 7 is used as the period factor rather
+ * than 8 for 160MHz.
+ */
+ period = AHD_SYNCRATE_REVA_160;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_PCOMP_EN) == 0)
+ iocell_opts[AHD_PRECOMP_SLEW_INDEX] &=
+ ~AHD_PRECOMP_MASK;
+ } else {
+ /*
+ * Precomp should be disabled for non-paced transfers.
+ */
+ iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK;
+
+ if ((ahd->features & AHD_NEW_IOCELL_OPTS) != 0
+ && (ppr_opts & MSG_EXT_PPR_DT_REQ) != 0) {
+ /*
+ * Slow down our CRC interval to be
+ * compatible with devices that can't
+ * handle a CRC at full speed.
+ */
+ con_opts |= ENSLOWCRC;
+ }
+ }
+
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP_SLEW);
+ ahd_outb(ahd, ANNEXDAT, iocell_opts[AHD_PRECOMP_SLEW_INDEX]);
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_AMPLITUDE);
+ ahd_outb(ahd, ANNEXDAT, iocell_opts[AHD_AMPLITUDE_INDEX]);
+
+ ahd_outb(ahd, NEGPERIOD, period);
+ ahd_outb(ahd, NEGPPROPTS, ppr_opts);
+ ahd_outb(ahd, NEGOFFSET, offset);
+
+ if (tinfo->width == MSG_EXT_WDTR_BUS_16_BIT)
+ con_opts |= WIDEXFER;
+
+ /*
+ * During packetized transfers, the target will
+ * give us the oportunity to send command packets
+ * without us asserting attention.
+ */
+ if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+ con_opts |= ENAUTOATNO;
+ ahd_outb(ahd, NEGCONOPTS, con_opts);
+ ahd_outb(ahd, NEGOADDR, saved_negoaddr);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * When the transfer settings for a connection change, setup for
+ * negotiation in pending SCBs to effect the change as quickly as
+ * possible. We also cancel any negotiations that are scheduled
+ * for inflight SCBs that have not been started yet.
+ */
+static void
+ahd_update_pending_scbs(struct ahd_softc *ahd)
+{
+ struct scb *pending_scb;
+ int pending_scb_count;
+ u_int scb_tag;
+ int paused;
+ u_int saved_scbptr;
+ ahd_mode_state saved_modes;
+
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings. We can only safely
+ * clear the negotiation required flag (setting requires the
+ * execution queue to be modified) and this is only possible
+ * if we are not already attempting to select out for this
+ * SCB. For this reason, all callers only call this routine
+ * if we are changing the negotiation settings for the currently
+ * active transaction on the bus.
+ */
+ pending_scb_count = 0;
+ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+ struct ahd_devinfo devinfo;
+ struct hardware_scb *pending_hscb;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ ahd_scb_devinfo(ahd, &devinfo, pending_scb);
+ tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ pending_hscb = pending_scb->hscb;
+ if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+ && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+ pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+ pending_hscb->control &= ~MK_MESSAGE;
+ }
+ ahd_sync_scb(ahd, pending_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ pending_scb_count++;
+ }
+
+ if (pending_scb_count == 0)
+ return;
+
+ if (ahd_is_paused(ahd)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahd_pause(ahd);
+ }
+
+ /*
+ * Force the sequencer to reinitialize the selection for
+ * the command at the head of the execution queue if it
+ * has already been setup. The negotiation changes may
+ * effect whether we select-out with ATN.
+ */
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ saved_scbptr = ahd_get_scbptr(ahd);
+ /* Ensure that the hscbs down on the card match the new information */
+ for (scb_tag = 0; scb_tag < ahd->scb_data.maxhscbs; scb_tag++) {
+ struct hardware_scb *pending_hscb;
+ u_int control;
+
+ pending_scb = ahd_lookup_scb(ahd, scb_tag);
+ if (pending_scb == NULL)
+ continue;
+ ahd_set_scbptr(ahd, scb_tag);
+ pending_hscb = pending_scb->hscb;
+ control = ahd_inb_scbram(ahd, SCB_CONTROL);
+ control &= ~MK_MESSAGE;
+ control |= pending_hscb->control & MK_MESSAGE;
+ ahd_outb(ahd, SCB_CONTROL, control);
+ }
+ ahd_set_scbptr(ahd, saved_scbptr);
+ ahd_restore_modes(ahd, saved_modes);
+
+ if (paused == 0)
+ ahd_unpause(ahd);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahd_fetch_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ ahd_mode_state saved_modes;
+ u_int saved_scsiid;
+ role_t role;
+ int our_id;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ if (ahd_inb(ahd, SSTAT0) & TARGET)
+ role = ROLE_TARGET;
+ else
+ role = ROLE_INITIATOR;
+
+ if (role == ROLE_TARGET
+ && (ahd_inb(ahd, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+ /* We were selected, so pull our id from TARGIDIN */
+ our_id = ahd_inb(ahd, TARGIDIN) & OID;
+ } else if (role == ROLE_TARGET)
+ our_id = ahd_inb(ahd, TOWNID);
+ else
+ our_id = ahd_inb(ahd, IOWNID);
+
+ saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+ ahd_compile_devinfo(devinfo,
+ our_id,
+ SCSIID_TARGET(ahd, saved_scsiid),
+ ahd_inb(ahd, SAVED_LUN),
+ SCSIID_CHANNEL(ahd, saved_scsiid),
+ role);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+void
+ahd_print_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ printf("%s:%c:%d:%d: ", ahd_name(ahd), 'A',
+ devinfo->target, devinfo->lun);
+}
+
+struct ahd_phase_table_entry*
+ahd_lookup_phase_entry(int phase)
+{
+ struct ahd_phase_table_entry *entry;
+ struct ahd_phase_table_entry *last_entry;
+
+ /*
+ * num_phases doesn't include the default entry which
+ * will be returned if the phase doesn't match.
+ */
+ last_entry = &ahd_phase_table[num_phases];
+ for (entry = ahd_phase_table; entry < last_entry; entry++) {
+ if (phase == entry->phase)
+ break;
+ }
+ return (entry);
+}
+
+void
+ahd_compile_devinfo(struct ahd_devinfo *devinfo, u_int our_id, u_int target,
+ u_int lun, char channel, role_t role)
+{
+ devinfo->our_scsiid = our_id;
+ devinfo->target = target;
+ devinfo->lun = lun;
+ devinfo->target_offset = target;
+ devinfo->channel = channel;
+ devinfo->role = role;
+ if (channel == 'B')
+ devinfo->target_offset += 8;
+ devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahd_scb_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct scb *scb)
+{
+ role_t role;
+ int our_id;
+
+ our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+ role = ROLE_INITIATOR;
+ if ((scb->hscb->control & TARGET_SCB) != 0)
+ role = ROLE_TARGET;
+ ahd_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahd, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted. Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct scb *scb)
+{
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+
+ if (ahd_currently_packetized(ahd))
+ ahd->msg_flags |= MSG_FLAG_PACKETIZED;
+
+ if (ahd->send_msg_perror
+ && ahd_inb(ahd, MSG_OUT) == HOST_MSG) {
+ ahd->msgout_buf[ahd->msgout_index++] = ahd->send_msg_perror;
+ ahd->msgout_len++;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("Setting up for Parity Error delivery\n");
+#endif
+ return;
+ } else if (scb == NULL) {
+ printf("%s: WARNING. No pending message for "
+ "I_T msgin. Issuing NO-OP\n", ahd_name(ahd));
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_NOOP;
+ ahd->msgout_len++;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ return;
+ }
+
+ if ((scb->flags & SCB_DEVICE_RESET) == 0
+ && (scb->flags & SCB_PACKETIZED) == 0
+ && ahd_inb(ahd, MSG_OUT) == MSG_IDENTIFYFLAG) {
+ u_int identify_msg;
+
+ identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+ if ((scb->hscb->control & DISCENB) != 0)
+ identify_msg |= MSG_IDENTIFY_DISCFLAG;
+ ahd->msgout_buf[ahd->msgout_index++] = identify_msg;
+ ahd->msgout_len++;
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ ahd->msgout_buf[ahd->msgout_index++] =
+ scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+ ahd->msgout_buf[ahd->msgout_index++] = SCB_GET_TAG(scb);
+ ahd->msgout_len += 2;
+ }
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahd->msgout_len++;
+ ahd_print_path(ahd, scb);
+ printf("Bus Device Reset Message Sent\n");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ } else if ((scb->flags & SCB_ABORT) != 0) {
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT_TAG;
+ } else {
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT;
+ }
+ ahd->msgout_len++;
+ ahd_print_path(ahd, scb);
+ printf("Abort%s Message Sent\n",
+ (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+ ahd_build_transfer_msg(ahd, devinfo);
+ /*
+ * Clear our selection hardware in advance of potential
+ * PPR IU status change busfree. We may have an entry in
+ * the waiting Q for this target, and we don't want to go
+ * about selecting while we handle the busfree and blow
+ * it away.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ } else {
+ printf("ahd_intr: AWAITING_MSG for an SCB that "
+ "does not have a waiting message\n");
+ printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+ devinfo->target_mask);
+ panic("SCB = %d, SCB Control = %x:%x, MSG_OUT = %x "
+ "SCB flags = %x", SCB_GET_TAG(scb), scb->hscb->control,
+ ahd_inb_scbram(ahd, SCB_CONTROL), ahd_inb(ahd, MSG_OUT),
+ scb->flags);
+ }
+
+ /*
+ * Clear the MK_MESSAGE flag from the SCB so we aren't
+ * asked to send this message again.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+ scb->hscb->control &= ~MK_MESSAGE;
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahd_build_transfer_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ /*
+ * We need to initiate transfer negotiations.
+ * If our current and goal settings are identical,
+ * we want to renegotiate due to a check condition.
+ */
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ int dowide;
+ int dosync;
+ int doppr;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /*
+ * Filter our period based on the current connection.
+ * If we can't perform DT transfers on this segment (not in LVD
+ * mode for instance), then our decision to issue a PPR message
+ * may change.
+ */
+ period = tinfo->goal.period;
+ offset = tinfo->goal.offset;
+ ppr_options = tinfo->goal.ppr_options;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ ppr_options = 0;
+ ahd_devlimited_syncrate(ahd, tinfo, &period,
+ &ppr_options, devinfo->role);
+ dowide = tinfo->curr.width != tinfo->goal.width;
+ dosync = tinfo->curr.offset != offset || tinfo->curr.period != period;
+ /*
+ * Only use PPR if we have options that need it, even if the device
+ * claims to support it. There might be an expander in the way
+ * that doesn't.
+ */
+ doppr = ppr_options != 0;
+
+ if (!dowide && !dosync && !doppr) {
+ dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+ dosync = tinfo->goal.offset != 0;
+ }
+
+ if (!dowide && !dosync && !doppr) {
+ /*
+ * Force async with a WDTR message if we have a wide bus,
+ * or just issue an SDTR with a 0 offset.
+ */
+ if ((ahd->features & AHD_WIDE) != 0)
+ dowide = 1;
+ else
+ dosync = 1;
+
+ if (bootverbose) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Ensuring async\n");
+ }
+ }
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ doppr = 0;
+
+ /*
+ * Both the PPR message and SDTR message require the
+ * goal syncrate to be limited to what the target device
+ * is capable of handling (based on whether an LVD->SE
+ * expander is on the bus), so combine these two cases.
+ * Regardless, guarantee that if we are using WDTR and SDTR
+ * messages that WDTR comes first.
+ */
+ if (doppr || (dosync && !dowide)) {
+
+ offset = tinfo->goal.offset;
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ doppr ? tinfo->goal.width
+ : tinfo->curr.width,
+ devinfo->role);
+ if (doppr) {
+ ahd_construct_ppr(ahd, devinfo, period, offset,
+ tinfo->goal.width, ppr_options);
+ } else {
+ ahd_construct_sdtr(ahd, devinfo, period, offset);
+ }
+ } else {
+ ahd_construct_wdtr(ahd, devinfo, tinfo->goal.width);
+ }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_sdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int period, u_int offset)
+{
+ if (offset == 0)
+ period = AHD_ASYNC_XFER_PERIOD;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_SDTR_LEN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_SDTR;
+ ahd->msgout_buf[ahd->msgout_index++] = period;
+ ahd->msgout_buf[ahd->msgout_index++] = offset;
+ ahd->msgout_len += 5;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ devinfo->lun, period, offset);
+ }
+}
+
+/*
+ * Build a wide negotiateion message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_wdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int bus_width)
+{
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_WDTR_LEN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_WDTR;
+ ahd->msgout_buf[ahd->msgout_index++] = bus_width;
+ ahd->msgout_len += 4;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ devinfo->lun, bus_width);
+ }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_ppr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int period, u_int offset, u_int bus_width,
+ u_int ppr_options)
+{
+ /*
+ * Always request precompensation from
+ * the other target if we are running
+ * at paced syncrates.
+ */
+ if (period <= AHD_SYNCRATE_PACED)
+ ppr_options |= MSG_EXT_PPR_PCOMP_EN;
+ if (offset == 0)
+ period = AHD_ASYNC_XFER_PERIOD;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_PPR_LEN;
+ ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_PPR;
+ ahd->msgout_buf[ahd->msgout_index++] = period;
+ ahd->msgout_buf[ahd->msgout_index++] = 0;
+ ahd->msgout_buf[ahd->msgout_index++] = offset;
+ ahd->msgout_buf[ahd->msgout_index++] = bus_width;
+ ahd->msgout_buf[ahd->msgout_index++] = ppr_options;
+ ahd->msgout_len += 8;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+ "offset %x, ppr_options %x\n", ahd_name(ahd),
+ devinfo->channel, devinfo->target, devinfo->lun,
+ bus_width, period, offset, ppr_options);
+ }
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahd_clear_msg_state(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd->send_msg_perror = 0;
+ ahd->msg_flags = MSG_FLAG_NONE;
+ ahd->msgout_len = 0;
+ ahd->msgin_index = 0;
+ ahd->msg_type = MSG_TYPE_NONE;
+ if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+ /*
+ * The target didn't care to respond to our
+ * message request, so clear ATN.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ }
+ ahd_outb(ahd, MSG_OUT, MSG_NOOP);
+ ahd_outb(ahd, SEQ_FLAGS2,
+ ahd_inb(ahd, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahd_handle_message_phase(struct ahd_softc *ahd)
+{
+ struct ahd_devinfo devinfo;
+ u_int bus_phase;
+ int end_session;
+
+ ahd_fetch_devinfo(ahd, &devinfo);
+ end_session = FALSE;
+ bus_phase = ahd_inb(ahd, LASTPHASE);
+
+ if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0) {
+ printf("LQIRETRY for LQIPHASE_OUTPKT\n");
+ ahd_outb(ahd, LQCTL2, LQIRETRY);
+ }
+reswitch:
+ switch (ahd->msg_type) {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ int lastbyte;
+ int phasemis;
+ int msgdone;
+
+ if (ahd->msgout_len == 0 && ahd->send_msg_perror == 0)
+ panic("HOST_MSG_LOOP interrupt with no active message");
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("INITIATOR_MSG_OUT");
+ }
+#endif
+ phasemis = bus_phase != P_MESGOUT;
+ if (phasemis) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahd_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
+ if (bus_phase == P_MESGIN) {
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ ahd->send_msg_perror = 0;
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ ahd->msgin_index = 0;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ if (ahd->send_msg_perror) {
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n", ahd->send_msg_perror);
+#endif
+ /*
+ * If we are notifying the target of a CRC error
+ * during packetized operations, the target is
+ * within its rights to acknowledge our message
+ * with a busfree.
+ */
+ if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0
+ && ahd->send_msg_perror == MSG_INITIATOR_DET_ERR)
+ ahd->msg_flags |= MSG_FLAG_EXPECT_IDE_BUSFREE;
+
+ ahd_outb(ahd, RETURN_2, ahd->send_msg_perror);
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+ break;
+ }
+
+ msgdone = ahd->msgout_index == ahd->msgout_len;
+ if (msgdone) {
+ /*
+ * The target has requested a retry.
+ * Re-assert ATN, reset our message index to
+ * 0, and try again.
+ */
+ ahd->msgout_index = 0;
+ ahd_assert_atn(ahd);
+ }
+
+ lastbyte = ahd->msgout_index == (ahd->msgout_len - 1);
+ if (lastbyte) {
+ /* Last byte is signified by dropping ATN */
+ ahd_outb(ahd, CLRSINT1, CLRATNO);
+ }
+
+ /*
+ * Clear our interrupt status and present
+ * the next byte on the bus.
+ */
+ ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahd->msgout_buf[ahd->msgout_index]);
+#endif
+ ahd_outb(ahd, RETURN_2, ahd->msgout_buf[ahd->msgout_index++]);
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ int phasemis;
+ int message_done;
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("INITIATOR_MSG_IN");
+ }
+#endif
+ phasemis = bus_phase != P_MESGIN;
+ if (phasemis) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahd_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
+ ahd->msgin_index = 0;
+ if (bus_phase == P_MESGOUT
+ && (ahd->send_msg_perror != 0
+ || (ahd->msgout_len != 0
+ && ahd->msgout_index == 0))) {
+ ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ /* Pull the byte in without acking it */
+ ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIBUS);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahd->msgin_buf[ahd->msgin_index]);
+#endif
+
+ message_done = ahd_parse_msg(ahd, &devinfo);
+
+ if (message_done) {
+ /*
+ * Clear our incoming message buffer in case there
+ * is another message following this one.
+ */
+ ahd->msgin_index = 0;
+
+ /*
+ * If this message illicited a response,
+ * assert ATN so the target takes us to the
+ * message out phase.
+ */
+ if (ahd->msgout_len != 0) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Asserting ATN for response\n");
+ }
+#endif
+ ahd_assert_atn(ahd);
+ }
+ } else
+ ahd->msgin_index++;
+
+ if (message_done == MSGLOOP_TERMINATED) {
+ end_session = TRUE;
+ } else {
+ /* Ack the byte */
+ ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_READ);
+ }
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGIN:
+ {
+ int msgdone;
+ int msgout_request;
+
+ /*
+ * By default, the message loop will continue.
+ */
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+ if (ahd->msgout_len == 0)
+ panic("Target MSGIN with no active message");
+
+ /*
+ * If we interrupted a mesgout session, the initiator
+ * will not know this until our first REQ. So, we
+ * only honor mesgout requests after we've sent our
+ * first byte.
+ */
+ if ((ahd_inb(ahd, SCSISIGI) & ATNI) != 0
+ && ahd->msgout_index > 0)
+ msgout_request = TRUE;
+ else
+ msgout_request = FALSE;
+
+ if (msgout_request) {
+
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahd->msg_type = MSG_TYPE_TARGET_MSGOUT;
+ ahd_outb(ahd, SCSISIGO, P_MESGOUT | BSYO);
+ ahd->msgin_index = 0;
+ /* Dummy read to REQ for first byte */
+ ahd_inb(ahd, SCSIDAT);
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ break;
+ }
+
+ msgdone = ahd->msgout_index == ahd->msgout_len;
+ if (msgdone) {
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+ end_session = TRUE;
+ break;
+ }
+
+ /*
+ * Present the next byte on the bus.
+ */
+ ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ ahd_outb(ahd, SCSIDAT, ahd->msgout_buf[ahd->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGOUT:
+ {
+ int lastbyte;
+ int msgdone;
+
+ /*
+ * By default, the message loop will continue.
+ */
+ ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+ /*
+ * The initiator signals that this is
+ * the last byte by dropping ATN.
+ */
+ lastbyte = (ahd_inb(ahd, SCSISIGI) & ATNI) == 0;
+
+ /*
+ * Read the latched byte, but turn off SPIOEN first
+ * so that we don't inadvertently cause a REQ for the
+ * next byte.
+ */
+ ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+ ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIDAT);
+ msgdone = ahd_parse_msg(ahd, &devinfo);
+ if (msgdone == MSGLOOP_TERMINATED) {
+ /*
+ * The message is *really* done in that it caused
+ * us to go to bus free. The sequencer has already
+ * been reset at this point, so pull the ejection
+ * handle.
+ */
+ return;
+ }
+
+ ahd->msgin_index++;
+
+ /*
+ * XXX Read spec about initiator dropping ATN too soon
+ * and use msgdone to detect it.
+ */
+ if (msgdone == MSGLOOP_MSGCOMPLETE) {
+ ahd->msgin_index = 0;
+
+ /*
+ * If this message illicited a response, transition
+ * to the Message in phase and send it.
+ */
+ if (ahd->msgout_len != 0) {
+ ahd_outb(ahd, SCSISIGO, P_MESGIN | BSYO);
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+ ahd->msgin_index = 0;
+ break;
+ }
+ }
+
+ if (lastbyte)
+ end_session = TRUE;
+ else {
+ /* Ask for the next byte. */
+ ahd_outb(ahd, SXFRCTL0,
+ ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+ }
+
+ break;
+ }
+ default:
+ panic("Unknown REQINIT message type");
+ }
+
+ if (end_session) {
+ if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0) {
+ printf("%s: Returning to Idle Loop\n",
+ ahd_name(ahd));
+ ahd_clear_msg_state(ahd);
+
+ /*
+ * Perform the equivalent of a clear_target_state.
+ */
+ ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+ ahd_outb(ahd, SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT);
+ ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+ } else {
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, RETURN_1, EXIT_MSG_LOOP);
+ }
+ }
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message. If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full)
+{
+ int found;
+ u_int index;
+
+ found = FALSE;
+ index = 0;
+
+ while (index < ahd->msgout_len) {
+ if (ahd->msgout_buf[index] == MSG_EXTENDED) {
+ u_int end_index;
+
+ end_index = index + 1 + ahd->msgout_buf[index + 1];
+ if (ahd->msgout_buf[index+2] == msgval
+ && type == AHDMSG_EXT) {
+
+ if (full) {
+ if (ahd->msgout_index > end_index)
+ found = TRUE;
+ } else if (ahd->msgout_index > index)
+ found = TRUE;
+ }
+ index = end_index;
+ } else if (ahd->msgout_buf[index] >= MSG_SIMPLE_TASK
+ && ahd->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+ /* Skip tag type and tag id or residue param*/
+ index += 2;
+ } else {
+ /* Single byte message */
+ if (type == AHDMSG_1B
+ && ahd->msgout_index > index
+ && (ahd->msgout_buf[index] == msgval
+ || ((ahd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0
+ && msgval == MSG_IDENTIFYFLAG)))
+ found = TRUE;
+ index++;
+ }
+
+ if (found)
+ break;
+ }
+ return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ int reject;
+ int done;
+ int response;
+
+ done = MSGLOOP_IN_PROG;
+ response = FALSE;
+ reject = FALSE;
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ /*
+ * Parse as much of the message as is available,
+ * rejecting it if we don't support it. When
+ * the entire message is available and has been
+ * handled, return MSGLOOP_MSGCOMPLETE, indicating
+ * that we have parsed an entire message.
+ *
+ * In the case of extended messages, we accept the length
+ * byte outright and perform more checking once we know the
+ * extended message type.
+ */
+ switch (ahd->msgin_buf[0]) {
+ case MSG_DISCONNECT:
+ case MSG_SAVEDATAPOINTER:
+ case MSG_CMDCOMPLETE:
+ case MSG_RESTOREPOINTERS:
+ case MSG_IGN_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_MESSAGE_REJECT:
+ response = ahd_handle_msg_reject(ahd, devinfo);
+ /* FALLTHROUGH */
+ case MSG_NOOP:
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ case MSG_EXTENDED:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahd->msgin_index < 2)
+ break;
+ switch (ahd->msgin_buf[2]) {
+ case MSG_EXT_SDTR:
+ {
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+ u_int saved_offset;
+
+ if (ahd->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have both args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_SDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahd->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+ break;
+
+ period = ahd->msgin_buf[3];
+ ppr_options = 0;
+ saved_offset = offset = ahd->msgin_buf[4];
+ ahd_devlimited_syncrate(ahd, tinfo, &period,
+ &ppr_options, devinfo->role);
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ tinfo->curr.width, devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received "
+ "SDTR period %x, offset %x\n\t"
+ "Filtered to period %x, offset %x\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ ahd->msgin_buf[3], saved_offset,
+ period, offset);
+ }
+ ahd_set_syncrate(ahd, devinfo, period,
+ offset, ppr_options,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+
+ /*
+ * See if we initiated Sync Negotiation
+ * and didn't have to fall down to async
+ * transfers.
+ */
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+ /* We started it */
+ if (saved_offset != offset) {
+ /* Went too low - force async */
+ reject = TRUE;
+ }
+ } else {
+ /*
+ * Send our own SDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated SDTR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_construct_sdtr(ahd, devinfo,
+ period, offset);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ u_int bus_width;
+ u_int saved_width;
+ u_int sending_reply;
+
+ sending_reply = FALSE;
+ if (ahd->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have our arg before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_WDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahd->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+ break;
+
+ bus_width = ahd->msgin_buf[3];
+ saved_width = bus_width;
+ ahd_validate_width(ahd, tinfo, &bus_width,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received WDTR "
+ "%x filtered to %x\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, bus_width);
+ }
+
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+ /*
+ * Don't send a WDTR back to the
+ * target, since we asked first.
+ * If the width went higher than our
+ * request, reject it.
+ */
+ if (saved_width > bus_width) {
+ reject = TRUE;
+ printf("(%s:%c:%d:%d): requested %dBit "
+ "transfers. Rejecting...\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ 8 * (0x01 << bus_width));
+ bus_width = 0;
+ }
+ } else {
+ /*
+ * Send our own WDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated WDTR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_construct_wdtr(ahd, devinfo, bus_width);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ sending_reply = TRUE;
+ }
+ /*
+ * After a wide message, we are async, but
+ * some devices don't seem to honor this portion
+ * of the spec. Force a renegotiation of the
+ * sync component of our transfer agreement even
+ * if our goal is async. By updating our width
+ * after forcing the negotiation, we avoid
+ * renegotiating for width.
+ */
+ ahd_update_neg_request(ahd, devinfo, tstate,
+ tinfo, AHD_NEG_ALWAYS);
+ ahd_set_width(ahd, devinfo, bus_width,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ if (sending_reply == FALSE && reject == FALSE) {
+
+ /*
+ * We will always have an SDTR to send.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_PPR:
+ {
+ u_int period;
+ u_int offset;
+ u_int bus_width;
+ u_int ppr_options;
+ u_int saved_width;
+ u_int saved_offset;
+ u_int saved_ppr_options;
+
+ if (ahd->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have all args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_PPR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahd->msgin_index < (MSG_EXT_PPR_LEN + 1))
+ break;
+
+ period = ahd->msgin_buf[3];
+ offset = ahd->msgin_buf[5];
+ bus_width = ahd->msgin_buf[6];
+ saved_width = bus_width;
+ ppr_options = ahd->msgin_buf[7];
+ /*
+ * According to the spec, a DT only
+ * period factor with no DT option
+ * set implies async.
+ */
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && period <= 9)
+ offset = 0;
+ saved_ppr_options = ppr_options;
+ saved_offset = offset;
+
+ /*
+ * Transfer options are only available if we
+ * are negotiating wide.
+ */
+ if (bus_width == 0)
+ ppr_options &= MSG_EXT_PPR_QAS_REQ;
+
+ ahd_validate_width(ahd, tinfo, &bus_width,
+ devinfo->role);
+ ahd_devlimited_syncrate(ahd, tinfo, &period,
+ &ppr_options, devinfo->role);
+ ahd_validate_offset(ahd, tinfo, period, &offset,
+ bus_width, devinfo->role);
+
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ /*
+ * If we are unable to do any of the
+ * requested options (we went too low),
+ * then we'll have to reject the message.
+ */
+ if (saved_width > bus_width
+ || saved_offset != offset
+ || saved_ppr_options != ppr_options) {
+ reject = TRUE;
+ period = 0;
+ offset = 0;
+ bus_width = 0;
+ ppr_options = 0;
+ }
+ } else {
+ if (devinfo->role != ROLE_TARGET)
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated PPR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ else
+ printf("(%s:%c:%d:%d): Initiator "
+ "Initiated PPR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_construct_ppr(ahd, devinfo, period, offset,
+ bus_width, ppr_options);
+ ahd->msgout_index = 0;
+ response = TRUE;
+ }
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received PPR width %x, "
+ "period %x, offset %x,options %x\n"
+ "\tFiltered to width %x, period %x, "
+ "offset %x, options %x\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, ahd->msgin_buf[3],
+ saved_offset, saved_ppr_options,
+ bus_width, period, offset, ppr_options);
+ }
+ ahd_set_width(ahd, devinfo, bus_width,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahd_set_syncrate(ahd, devinfo, period,
+ offset, ppr_options,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ default:
+ /* Unknown extended message. Reject it. */
+ reject = TRUE;
+ break;
+ }
+ break;
+ }
+#ifdef AHD_TARGET_MODE
+ case MSG_BUS_DEV_RESET:
+ ahd_handle_devreset(ahd, devinfo, CAM_LUN_WILDCARD,
+ CAM_BDR_SENT,
+ "Bus Device Reset Received",
+ /*verbose_level*/0);
+ ahd_restart(ahd);
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_ABORT_TAG:
+ case MSG_ABORT:
+ case MSG_CLEAR_QUEUE:
+ {
+ int tag;
+
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
+ break;
+ }
+ tag = SCB_LIST_NULL;
+ if (ahd->msgin_buf[0] == MSG_ABORT_TAG)
+ tag = ahd_inb(ahd, INITIATOR_TAG);
+ ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+ devinfo->lun, tag, ROLE_TARGET,
+ CAM_REQ_ABORTED);
+
+ tstate = ahd->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ struct ahd_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[devinfo->lun];
+ if (lstate != NULL) {
+ ahd_queue_lstate_event(ahd, lstate,
+ devinfo->our_scsiid,
+ ahd->msgin_buf[0],
+ /*arg*/tag);
+ ahd_send_lstate_events(ahd, lstate);
+ }
+ }
+ ahd_restart(ahd);
+ done = MSGLOOP_TERMINATED;
+ break;
+ }
+#endif
+ case MSG_QAS_REQUEST:
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ printf("%s: QAS request. SCSISIGI == 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, SCSISIGI));
+#endif
+ ahd->msg_flags |= MSG_FLAG_EXPECT_QASREJ_BUSFREE;
+ /* FALLTHROUGH */
+ case MSG_TERM_IO_PROC:
+ default:
+ reject = TRUE;
+ break;
+ }
+
+ if (reject) {
+ /*
+ * Setup to reject the message.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 1;
+ ahd->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ done = MSGLOOP_MSGCOMPLETE;
+ response = TRUE;
+ }
+
+ if (done != MSGLOOP_IN_PROG && !response)
+ /* Clear the outgoing message buffer */
+ ahd->msgout_len = 0;
+
+ return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+ struct scb *scb;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int scb_index;
+ u_int last_msg;
+ int response = 0;
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /* Might be necessary */
+ last_msg = ahd_inb(ahd, LAST_MSG);
+
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/TRUE)
+ && tinfo->goal.period <= AHD_SYNCRATE_PACED) {
+ /*
+ * Target may not like our SPI-4 PPR Options.
+ * Attempt to negotiate 80MHz which will turn
+ * off these options.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying simple U160 PPR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.period = AHD_SYNCRATE_DT;
+ tinfo->goal.ppr_options &= MSG_EXT_PPR_IU_REQ
+ | MSG_EXT_PPR_QAS_REQ
+ | MSG_EXT_PPR_DT_REQ;
+ } else {
+ /*
+ * Target does not support the PPR message.
+ * Attempt to negotiate SPI-2 style.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying WDTR/SDTR\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.ppr_options = 0;
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ }
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = 1;
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+ /* note 8bit xfers */
+ printf("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahd_name(ahd),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * No need to clear the sync rate. If the target
+ * did not accept the command, our syncrate is
+ * unaffected. If the target started the negotiation,
+ * but rejected our response, we already cleared the
+ * sync rate before sending our WDTR.
+ */
+ if (tinfo->goal.offset != tinfo->curr.offset) {
+
+ /* Start the sync negotiation */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = 1;
+ }
+ } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ /* note asynch xfers and clear flag */
+ ahd_set_syncrate(ahd, devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+ "Using asynchronous transfers\n",
+ ahd_name(ahd), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+ if (tag_type == MSG_SIMPLE_TASK) {
+ printf("(%s:%c:%d:%d): refuses tagged commands. "
+ "Performing non-tagged I/O\n", ahd_name(ahd),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahd_set_tags(ahd, devinfo, AHD_QUEUE_NONE);
+ mask = ~0x23;
+ } else {
+ printf("(%s:%c:%d:%d): refuses %s tagged commands. "
+ "Performing simple queue tagged I/O only\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ devinfo->lun, tag_type == MSG_ORDERED_TASK
+ ? "ordered" : "head of queue");
+ ahd_set_tags(ahd, devinfo, AHD_QUEUE_BASIC);
+ mask = ~0x03;
+ }
+
+ /*
+ * Resend the identify for this CCB as the target
+ * may believe that the selection is invalid otherwise.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb_scbram(ahd, SCB_CONTROL) & mask);
+ scb->hscb->control &= mask;
+ ahd_set_transaction_tag(scb, /*enabled*/FALSE,
+ /*type*/MSG_SIMPLE_TASK);
+ ahd_outb(ahd, MSG_OUT, MSG_IDENTIFYFLAG);
+ ahd_assert_atn(ahd);
+ ahd_busy_tcl(ahd, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+ SCB_GET_TAG(scb));
+
+ /*
+ * Requeue all tagged commands for this target
+ * currently in our posession so they can be
+ * converted to untagged commands.
+ */
+ ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ } else if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_IDENTIFYFLAG, TRUE)) {
+ /*
+ * Most likely the device believes that we had
+ * previously negotiated packetized.
+ */
+ ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+ | MSG_FLAG_IU_REQ_CHANGED;
+
+ ahd_force_renegotiation(ahd, devinfo);
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+ ahd_build_transfer_msg(ahd, devinfo);
+ ahd->msgout_index = 0;
+ response = 1;
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahd_name(ahd), devinfo->channel, devinfo->target,
+ last_msg);
+ }
+ return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahd_handle_ign_wide_residue(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ u_int scb_index;
+ struct scb *scb;
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+ /*
+ * XXX Actually check data direction in the sequencer?
+ * Perhaps add datadir to some spare bits in the hscb?
+ */
+ if ((ahd_inb(ahd, SEQ_FLAGS) & DPHASE) == 0
+ || ahd_get_transfer_dir(scb) != CAM_DIR_IN) {
+ /*
+ * Ignore the message if we haven't
+ * seen an appropriate data phase yet.
+ */
+ } else {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing. Otherwise, subtract a byte
+ * and update the residual count accordingly.
+ */
+ uint32_t sgptr;
+
+ sgptr = ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ if ((sgptr & SG_LIST_NULL) != 0
+ && (ahd_inb_scbram(ahd, SCB_TASK_ATTRIBUTE)
+ & SCB_XFERLEN_ODD) != 0) {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing.
+ */
+ } else {
+ uint32_t data_cnt;
+ uint64_t data_addr;
+ uint32_t sglen;
+
+ /* Pull in the rest of the sgptr */
+ sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ data_cnt = ahd_inl_scbram(ahd, SCB_RESIDUAL_DATACNT);
+ if ((sgptr & SG_LIST_NULL) != 0) {
+ /*
+ * The residual data count is not updated
+ * for the command run to completion case.
+ * Explicitly zero the count.
+ */
+ data_cnt &= ~AHD_SG_LEN_MASK;
+ }
+ data_addr = ahd_inq(ahd, SHADDR);
+ data_cnt += 1;
+ data_addr -= 1;
+ sgptr &= SG_PTR_MASK;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+ sg--;
+ sglen = ahd_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST
+ * bits while setting the count to 1.
+ */
+ data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+ data_addr = ahd_le64toh(sg->addr)
+ + (sglen & AHD_SG_LEN_MASK)
+ - 1;
+
+ /*
+ * Increment sg so it points to the
+ * "next" sg.
+ */
+ sg++;
+ sgptr = ahd_sg_virt_to_bus(ahd, scb,
+ sg);
+ }
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+ sg--;
+ sglen = ahd_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST
+ * bits while setting the count to 1.
+ */
+ data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+ data_addr = ahd_le32toh(sg->addr)
+ + (sglen & AHD_SG_LEN_MASK)
+ - 1;
+
+ /*
+ * Increment sg so it points to the
+ * "next" sg.
+ */
+ sg++;
+ sgptr = ahd_sg_virt_to_bus(ahd, scb,
+ sg);
+ }
+ }
+ /*
+ * Toggle the "oddness" of the transfer length
+ * to handle this mid-transfer ignore wide
+ * residue. This ensures that the oddness is
+ * correct for subsequent data transfers.
+ */
+ ahd_outb(ahd, SCB_TASK_ATTRIBUTE,
+ ahd_inb_scbram(ahd, SCB_TASK_ATTRIBUTE)
+ ^ SCB_XFERLEN_ODD);
+
+ ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+ ahd_outl(ahd, SCB_RESIDUAL_DATACNT, data_cnt);
+ /*
+ * The FIFO's pointers will be updated if/when the
+ * sequencer re-enters a data phase.
+ */
+ }
+ }
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahd_reinitialize_dataptrs(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int scb_index;
+ u_int wait;
+ uint32_t sgptr;
+ uint32_t resid;
+ uint64_t dataptr;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK,
+ AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK);
+
+ scb_index = ahd_get_scbptr(ahd);
+ scb = ahd_lookup_scb(ahd, scb_index);
+
+ /*
+ * Release and reacquire the FIFO so we
+ * have a clean slate.
+ */
+ ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+ wait = 1000;
+ while (--wait && !(ahd_inb(ahd, MDFFSTAT) & FIFOFREE))
+ ahd_delay(100);
+ if (wait == 0) {
+ ahd_print_path(ahd, scb);
+ printf("ahd_reinitialize_dataptrs: Forcing FIFO free.\n");
+ ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+ }
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, DFFSTAT,
+ ahd_inb(ahd, DFFSTAT)
+ | (saved_modes == 0x11 ? CURRFIFO_1 : CURRFIFO_0));
+
+ /*
+ * Determine initial values for data_addr and data_cnt
+ * for resuming the data phase.
+ */
+ sgptr = (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 1) << 8)
+ | ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ sgptr &= SG_PTR_MASK;
+
+ resid = (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 2) << 16)
+ | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 1) << 8)
+ | ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT);
+
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ dataptr = ahd_le64toh(sg->addr)
+ + (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+ - resid;
+ ahd_outb(ahd, HADDR + 7, dataptr >> 56);
+ ahd_outb(ahd, HADDR + 6, dataptr >> 48);
+ ahd_outb(ahd, HADDR + 5, dataptr >> 40);
+ ahd_outb(ahd, HADDR + 4, dataptr >> 32);
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ dataptr = ahd_le32toh(sg->addr)
+ + (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+ - resid;
+ ahd_outb(ahd, HADDR + 4,
+ (ahd_le32toh(sg->len) & ~AHD_SG_LEN_MASK) >> 24);
+ }
+ ahd_outb(ahd, HADDR + 3, dataptr >> 24);
+ ahd_outb(ahd, HADDR + 2, dataptr >> 16);
+ ahd_outb(ahd, HADDR + 1, dataptr >> 8);
+ ahd_outb(ahd, HADDR, dataptr);
+ ahd_outb(ahd, HCNT + 2, resid >> 16);
+ ahd_outb(ahd, HCNT + 1, resid >> 8);
+ ahd_outb(ahd, HCNT, resid);
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ u_int lun, cam_status status, char *message,
+ int verbose_level)
+{
+#ifdef AHD_TARGET_MODE
+ struct ahd_tmode_tstate* tstate;
+#endif
+ int found;
+
+ found = ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+ lun, SCB_LIST_NULL, devinfo->role,
+ status);
+
+#ifdef AHD_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target mord peripheral
+ * drivers affected by this action.
+ */
+ tstate = ahd->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ u_int cur_lun;
+ u_int max_lun;
+
+ if (lun != CAM_LUN_WILDCARD) {
+ cur_lun = 0;
+ max_lun = AHD_NUM_LUNS - 1;
+ } else {
+ cur_lun = lun;
+ max_lun = lun;
+ }
+ for (cur_lun <= max_lun; cur_lun++) {
+ struct ahd_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[cur_lun];
+ if (lstate == NULL)
+ continue;
+
+ ahd_queue_lstate_event(ahd, lstate, devinfo->our_scsiid,
+ MSG_BUS_DEV_RESET, /*arg*/0);
+ ahd_send_lstate_events(ahd, lstate);
+ }
+ }
+#endif
+
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, devinfo, /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHD_TRANS_CUR, /*paused*/TRUE);
+
+ ahd_send_async(ahd, devinfo->channel, devinfo->target,
+ lun, AC_SENT_BDR, NULL);
+
+ if (message != NULL
+ && (verbose_level <= bootverbose))
+ printf("%s: %s on %c:%d. %d SCBs aborted\n", ahd_name(ahd),
+ message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHD_TARGET_MODE
+static void
+ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ struct scb *scb)
+{
+
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahd->msgout_index = 0;
+ ahd->msgout_len = 0;
+
+ if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+ ahd_build_transfer_msg(ahd, devinfo);
+ else
+ panic("ahd_intr: AWAITING target message with no message");
+
+ ahd->msgout_index = 0;
+ ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+static u_int
+ahd_sglist_size(struct ahd_softc *ahd)
+{
+ bus_size_t list_size;
+
+ list_size = sizeof(struct ahd_dma_seg) * AHD_NSEG;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ list_size = sizeof(struct ahd_dma64_seg) * AHD_NSEG;
+ return (list_size);
+}
+
+/*
+ * Calculate the optimum S/G List allocation size. S/G elements used
+ * for a given transaction must be physically contiguous. Assume the
+ * OS will allocate full pages to us, so it doesn't make sense to request
+ * less than a page.
+ */
+static u_int
+ahd_sglist_allocsize(struct ahd_softc *ahd)
+{
+ bus_size_t sg_list_increment;
+ bus_size_t sg_list_size;
+ bus_size_t max_list_size;
+ bus_size_t best_list_size;
+
+ /* Start out with the minimum required for AHD_NSEG. */
+ sg_list_increment = ahd_sglist_size(ahd);
+ sg_list_size = sg_list_increment;
+
+ /* Get us as close as possible to a page in size. */
+ while ((sg_list_size + sg_list_increment) <= PAGE_SIZE)
+ sg_list_size += sg_list_increment;
+
+ /*
+ * Try to reduce the amount of wastage by allocating
+ * multiple pages.
+ */
+ best_list_size = sg_list_size;
+ max_list_size = roundup(sg_list_increment, PAGE_SIZE);
+ if (max_list_size < 4 * PAGE_SIZE)
+ max_list_size = 4 * PAGE_SIZE;
+ if (max_list_size > (AHD_SCB_MAX_ALLOC * sg_list_increment))
+ max_list_size = (AHD_SCB_MAX_ALLOC * sg_list_increment);
+ while ((sg_list_size + sg_list_increment) <= max_list_size
+ && (sg_list_size % PAGE_SIZE) != 0) {
+ bus_size_t new_mod;
+ bus_size_t best_mod;
+
+ sg_list_size += sg_list_increment;
+ new_mod = sg_list_size % PAGE_SIZE;
+ best_mod = best_list_size % PAGE_SIZE;
+ if (new_mod > best_mod || new_mod == 0) {
+ best_list_size = sg_list_size;
+ }
+ }
+ return (best_list_size);
+}
+
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahd_softc *
+ahd_alloc(void *platform_arg, char *name)
+{
+ struct ahd_softc *ahd;
+
+#ifndef __FreeBSD__
+ ahd = malloc(sizeof(*ahd), M_DEVBUF, M_NOWAIT);
+ if (!ahd) {
+ printf("aic7xxx: cannot malloc softc!\n");
+ free(name, M_DEVBUF);
+ return NULL;
+ }
+#else
+ ahd = device_get_softc((device_t)platform_arg);
+#endif
+ memset(ahd, 0, sizeof(*ahd));
+ ahd->seep_config = malloc(sizeof(*ahd->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahd->seep_config == NULL) {
+#ifndef __FreeBSD__
+ free(ahd, M_DEVBUF);
+#endif
+ free(name, M_DEVBUF);
+ return (NULL);
+ }
+ LIST_INIT(&ahd->pending_scbs);
+ /* We don't know our unit number until the OSM sets it */
+ ahd->name = name;
+ ahd->unit = -1;
+ ahd->description = NULL;
+ ahd->bus_description = NULL;
+ ahd->channel = 'A';
+ ahd->chip = AHD_NONE;
+ ahd->features = AHD_FENONE;
+ ahd->bugs = AHD_BUGNONE;
+ ahd->flags = AHD_SPCHK_ENB_A|AHD_RESET_BUS_A|AHD_TERM_ENB_A
+ | AHD_EXTENDED_TRANS_A|AHD_STPWLEVEL_A;
+ ahd_timer_init(&ahd->reset_timer);
+ ahd_timer_init(&ahd->stat_timer);
+ ahd->int_coalescing_timer = AHD_INT_COALESCING_TIMER_DEFAULT;
+ ahd->int_coalescing_maxcmds = AHD_INT_COALESCING_MAXCMDS_DEFAULT;
+ ahd->int_coalescing_mincmds = AHD_INT_COALESCING_MINCMDS_DEFAULT;
+ ahd->int_coalescing_threshold = AHD_INT_COALESCING_THRESHOLD_DEFAULT;
+ ahd->int_coalescing_stop_threshold =
+ AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT;
+
+ if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+ ahd_free(ahd);
+ ahd = NULL;
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MEMORY) != 0) {
+ printf("%s: scb size = 0x%x, hscb size = 0x%x\n",
+ ahd_name(ahd), (u_int)sizeof(struct scb),
+ (u_int)sizeof(struct hardware_scb));
+ }
+#endif
+ return (ahd);
+}
+
+int
+ahd_softc_init(struct ahd_softc *ahd)
+{
+
+ ahd->unpause = 0;
+ ahd->pause = PAUSE;
+ return (0);
+}
+
+void
+ahd_softc_insert(struct ahd_softc *ahd)
+{
+ struct ahd_softc *list_ahd;
+
+#if AHD_PCI_CONFIG > 0
+ /*
+ * Second Function PCI devices need to inherit some
+ * settings from function 0.
+ */
+ if ((ahd->features & AHD_MULTI_FUNC) != 0) {
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ ahd_dev_softc_t list_pci;
+ ahd_dev_softc_t pci;
+
+ list_pci = list_ahd->dev_softc;
+ pci = ahd->dev_softc;
+ if (ahd_get_pci_slot(list_pci) == ahd_get_pci_slot(pci)
+ && ahd_get_pci_bus(list_pci) == ahd_get_pci_bus(pci)) {
+ struct ahd_softc *master;
+ struct ahd_softc *slave;
+
+ if (ahd_get_pci_function(list_pci) == 0) {
+ master = list_ahd;
+ slave = ahd;
+ } else {
+ master = ahd;
+ slave = list_ahd;
+ }
+ slave->flags &= ~AHD_BIOS_ENABLED;
+ slave->flags |=
+ master->flags & AHD_BIOS_ENABLED;
+ break;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Insertion sort into our list of softcs.
+ */
+ list_ahd = TAILQ_FIRST(&ahd_tailq);
+ while (list_ahd != NULL
+ && ahd_softc_comp(ahd, list_ahd) <= 0)
+ list_ahd = TAILQ_NEXT(list_ahd, links);
+ if (list_ahd != NULL)
+ TAILQ_INSERT_BEFORE(list_ahd, ahd, links);
+ else
+ TAILQ_INSERT_TAIL(&ahd_tailq, ahd, links);
+ ahd->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahd_softc *
+ahd_find_softc(struct ahd_softc *ahd)
+{
+ struct ahd_softc *list_ahd;
+
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ if (list_ahd == ahd)
+ return (ahd);
+ }
+ return (NULL);
+}
+
+void
+ahd_set_unit(struct ahd_softc *ahd, int unit)
+{
+ ahd->unit = unit;
+}
+
+void
+ahd_set_name(struct ahd_softc *ahd, char *name)
+{
+ if (ahd->name != NULL)
+ free(ahd->name, M_DEVBUF);
+ ahd->name = name;
+}
+
+void
+ahd_free(struct ahd_softc *ahd)
+{
+ int i;
+
+ switch (ahd->init_level) {
+ default:
+ case 5:
+ ahd_shutdown(ahd);
+ /* FALLTHROUGH */
+ case 4:
+ ahd_dmamap_unload(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 3:
+ ahd_dmamem_free(ahd, ahd->shared_data_dmat, ahd->qoutfifo,
+ ahd->shared_data_dmamap);
+ ahd_dmamap_destroy(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 2:
+ ahd_dma_tag_destroy(ahd, ahd->shared_data_dmat);
+ case 1:
+#ifndef __linux__
+ ahd_dma_tag_destroy(ahd, ahd->buffer_dmat);
+#endif
+ break;
+ case 0:
+ break;
+ }
+
+#ifndef __linux__
+ ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
+#endif
+ ahd_platform_free(ahd);
+ ahd_fini_scbdata(ahd);
+ for (i = 0; i < AHD_NUM_TARGETS; i++) {
+ struct ahd_tmode_tstate *tstate;
+
+ tstate = ahd->enabled_targets[i];
+ if (tstate != NULL) {
+#ifdef AHD_TARGET_MODE
+ int j;
+
+ for (j = 0; j < AHD_NUM_LUNS; j++) {
+ struct ahd_tmode_lstate *lstate;
+
+ lstate = tstate->enabled_luns[j];
+ if (lstate != NULL) {
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+ }
+ }
+#endif
+ free(tstate, M_DEVBUF);
+ }
+ }
+#ifdef AHD_TARGET_MODE
+ if (ahd->black_hole != NULL) {
+ xpt_free_path(ahd->black_hole->path);
+ free(ahd->black_hole, M_DEVBUF);
+ }
+#endif
+ if (ahd->name != NULL)
+ free(ahd->name, M_DEVBUF);
+ if (ahd->seep_config != NULL)
+ free(ahd->seep_config, M_DEVBUF);
+ if (ahd->saved_stack != NULL)
+ free(ahd->saved_stack, M_DEVBUF);
+#ifndef __FreeBSD__
+ free(ahd, M_DEVBUF);
+#endif
+ return;
+}
+
+void
+ahd_shutdown(void *arg)
+{
+ struct ahd_softc *ahd;
+
+ ahd = (struct ahd_softc *)arg;
+
+ /*
+ * Stop periodic timer callbacks.
+ */
+ ahd_timer_stop(&ahd->reset_timer);
+ ahd_timer_stop(&ahd->stat_timer);
+
+ /* This will reset most registers to 0, but not all */
+ ahd_reset(ahd, /*reinit*/FALSE);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset. If "reinit" is
+ * non-zero, this reset occured after initial configuration
+ * and the caller requests that the chip be fully reinitialized
+ * to a runable state. Chip interrupts are *not* enabled after
+ * a reinitialization. The caller must enable interrupts via
+ * ahd_intr_enable().
+ */
+int
+ahd_reset(struct ahd_softc *ahd, int reinit)
+{
+ u_int sxfrctl1;
+ int wait;
+ uint32_t cmd;
+
+ /*
+ * Preserve the value of the SXFRCTL1 register for all channels.
+ * It contains settings that affect termination and we don't want
+ * to disturb the integrity of the bus.
+ */
+ ahd_pause(ahd);
+ ahd_update_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ sxfrctl1 = ahd_inb(ahd, SXFRCTL1);
+
+ cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+ if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+ uint32_t mod_cmd;
+
+ /*
+ * A4 Razor #632
+ * During the assertion of CHIPRST, the chip
+ * does not disable its parity logic prior to
+ * the start of the reset. This may cause a
+ * parity error to be detected and thus a
+ * spurious SERR or PERR assertion. Disble
+ * PERR and SERR responses during the CHIPRST.
+ */
+ mod_cmd = cmd & ~(PCIM_CMD_PERRESPEN|PCIM_CMD_SERRESPEN);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ mod_cmd, /*bytes*/2);
+ }
+ ahd_outb(ahd, HCNTRL, CHIPRST | ahd->pause);
+
+ /*
+ * Ensure that the reset has finished. We delay 1000us
+ * prior to reading the register to make sure the chip
+ * has sufficiently completed its reset to handle register
+ * accesses.
+ */
+ wait = 1000;
+ do {
+ ahd_delay(1000);
+ } while (--wait && !(ahd_inb(ahd, HCNTRL) & CHIPRSTACK));
+
+ if (wait == 0) {
+ printf("%s: WARNING - Failed chip reset! "
+ "Trying to initialize anyway.\n", ahd_name(ahd));
+ }
+ ahd_outb(ahd, HCNTRL, ahd->pause);
+
+ if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+ /*
+ * Clear any latched PCI error status and restore
+ * previous SERR and PERR response enables.
+ */
+ ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+ 0xFF, /*bytes*/1);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ cmd, /*bytes*/2);
+ }
+
+ /*
+ * Mode should be SCSI after a chip reset, but lets
+ * set it just to be safe. We touch the MODE_PTR
+ * register directly so as to bypass the lazy update
+ * code in ahd_set_modes().
+ */
+ ahd_known_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, MODE_PTR,
+ ahd_build_mode_state(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI));
+
+ /*
+ * Restore SXFRCTL1.
+ *
+ * We must always initialize STPWEN to 1 before we
+ * restore the saved values. STPWEN is initialized
+ * to a tri-state condition which can only be cleared
+ * by turning it on.
+ */
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+ /* Determine chip configuration */
+ ahd->features &= ~AHD_WIDE;
+ if ((ahd_inb(ahd, SBLKCTL) & SELWIDE) != 0)
+ ahd->features |= AHD_WIDE;
+
+ /*
+ * If a recovery action has forced a chip reset,
+ * re-initialize the chip to our liking.
+ */
+ if (reinit != 0)
+ ahd_chip_init(ahd);
+
+ return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahd_probe_scbs(struct ahd_softc *ahd) {
+ int i;
+
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ for (i = 0; i < AHD_SCB_MAX; i++) {
+ int j;
+
+ ahd_set_scbptr(ahd, i);
+ ahd_outw(ahd, SCB_BASE, i);
+ for (j = 2; j < 64; j++)
+ ahd_outb(ahd, SCB_BASE+j, 0);
+ /* Start out life as unallocated (needing an abort) */
+ ahd_outb(ahd, SCB_CONTROL, MK_MESSAGE);
+ if (ahd_inw_scbram(ahd, SCB_BASE) != i)
+ break;
+ ahd_set_scbptr(ahd, 0);
+ if (ahd_inw_scbram(ahd, SCB_BASE) != 0)
+ break;
+ }
+ return (i);
+}
+
+static void
+ahd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ dma_addr_t *baddr;
+
+ baddr = (dma_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+
+static void
+ahd_initialize_hscbs(struct ahd_softc *ahd)
+{
+ int i;
+
+ for (i = 0; i < ahd->scb_data.maxhscbs; i++) {
+ ahd_set_scbptr(ahd, i);
+
+ /* Clear the control byte. */
+ ahd_outb(ahd, SCB_CONTROL, 0);
+
+ /* Set the next pointer */
+ ahd_outw(ahd, SCB_NEXT, SCB_LIST_NULL);
+ }
+}
+
+static int
+ahd_init_scbdata(struct ahd_softc *ahd)
+{
+ struct scb_data *scb_data;
+ int i;
+
+ scb_data = &ahd->scb_data;
+ TAILQ_INIT(&scb_data->free_scbs);
+ for (i = 0; i < AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT; i++)
+ LIST_INIT(&scb_data->free_scb_lists[i]);
+ LIST_INIT(&scb_data->any_dev_free_scb_list);
+ SLIST_INIT(&scb_data->hscb_maps);
+ SLIST_INIT(&scb_data->sg_maps);
+ SLIST_INIT(&scb_data->sense_maps);
+
+ /* Determine the number of hardware SCBs and initialize them */
+ scb_data->maxhscbs = ahd_probe_scbs(ahd);
+ if (scb_data->maxhscbs == 0) {
+ printf("%s: No SCB space found\n", ahd_name(ahd));
+ return (ENXIO);
+ }
+
+ ahd_initialize_hscbs(ahd);
+
+ /*
+ * Create our DMA tags. These tags define the kinds of device
+ * accessible memory allocations and memory mappings we will
+ * need to perform during normal operation.
+ *
+ * Unless we need to further restrict the allocation, we rely
+ * on the restrictions of the parent dmat, hence the common
+ * use of MAXADDR and MAXSIZE.
+ */
+
+ /* DMA tag for our hardware scb structures */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ PAGE_SIZE, /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->hscb_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* DMA tag for our S/G structures. */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/8,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ ahd_sglist_allocsize(ahd), /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sg_dmat) != 0) {
+ goto error_exit;
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MEMORY) != 0)
+ printf("%s: ahd_sglist_allocsize = 0x%x\n", ahd_name(ahd),
+ ahd_sglist_allocsize(ahd));
+#endif
+
+ scb_data->init_level++;
+
+ /* DMA tag for our sense buffers. We allocate in page sized chunks */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ PAGE_SIZE, /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sense_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Perform initial CCB allocation */
+ ahd_alloc_scbs(ahd);
+
+ if (scb_data->numscbs == 0) {
+ printf("%s: ahd_init_scbdata - "
+ "Unable to allocate initial scbs\n",
+ ahd_name(ahd));
+ goto error_exit;
+ }
+
+ /*
+ * Note that we were successfull
+ */
+ return (0);
+
+error_exit:
+
+ return (ENOMEM);
+}
+
+static struct scb *
+ahd_find_scb_by_tag(struct ahd_softc *ahd, u_int tag)
+{
+ struct scb *scb;
+
+ /*
+ * Look on the pending list.
+ */
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ if (SCB_GET_TAG(scb) == tag)
+ return (scb);
+ }
+
+ /*
+ * Then on all of the collision free lists.
+ */
+ TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+ struct scb *list_scb;
+
+ list_scb = scb;
+ do {
+ if (SCB_GET_TAG(list_scb) == tag)
+ return (list_scb);
+ list_scb = LIST_NEXT(list_scb, collision_links);
+ } while (list_scb);
+ }
+
+ /*
+ * And finally on the generic free list.
+ */
+ LIST_FOREACH(scb, &ahd->scb_data.any_dev_free_scb_list, links.le) {
+ if (SCB_GET_TAG(scb) == tag)
+ return (scb);
+ }
+
+ return (NULL);
+}
+
+static void
+ahd_fini_scbdata(struct ahd_softc *ahd)
+{
+ struct scb_data *scb_data;
+
+ scb_data = &ahd->scb_data;
+ if (scb_data == NULL)
+ return;
+
+ switch (scb_data->init_level) {
+ default:
+ case 7:
+ {
+ struct map_node *sns_map;
+
+ while ((sns_map = SLIST_FIRST(&scb_data->sense_maps)) != NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sense_maps, links);
+ ahd_dmamap_unload(ahd, scb_data->sense_dmat,
+ sns_map->dmamap);
+ ahd_dmamem_free(ahd, scb_data->sense_dmat,
+ sns_map->vaddr, sns_map->dmamap);
+ free(sns_map, M_DEVBUF);
+ }
+ ahd_dma_tag_destroy(ahd, scb_data->sense_dmat);
+ /* FALLTHROUGH */
+ }
+ case 6:
+ {
+ struct map_node *sg_map;
+
+ while ((sg_map = SLIST_FIRST(&scb_data->sg_maps)) != NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+ ahd_dmamap_unload(ahd, scb_data->sg_dmat,
+ sg_map->dmamap);
+ ahd_dmamem_free(ahd, scb_data->sg_dmat,
+ sg_map->vaddr, sg_map->dmamap);
+ free(sg_map, M_DEVBUF);
+ }
+ ahd_dma_tag_destroy(ahd, scb_data->sg_dmat);
+ /* FALLTHROUGH */
+ }
+ case 5:
+ {
+ struct map_node *hscb_map;
+
+ while ((hscb_map = SLIST_FIRST(&scb_data->hscb_maps)) != NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->hscb_maps, links);
+ ahd_dmamap_unload(ahd, scb_data->hscb_dmat,
+ hscb_map->dmamap);
+ ahd_dmamem_free(ahd, scb_data->hscb_dmat,
+ hscb_map->vaddr, hscb_map->dmamap);
+ free(hscb_map, M_DEVBUF);
+ }
+ ahd_dma_tag_destroy(ahd, scb_data->hscb_dmat);
+ /* FALLTHROUGH */
+ }
+ case 4:
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ break;
+ }
+}
+
+/*
+ * DSP filter Bypass must be enabled until the first selection
+ * after a change in bus mode (Razor #491 and #493).
+ */
+static void
+ahd_setup_iocell_workaround(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ ahd_outb(ahd, DSPDATACTL, ahd_inb(ahd, DSPDATACTL)
+ | BYPASSENAB | RCVROFFSTDIS | XMITOFFSTDIS);
+ ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) | (ENSELDO|ENSELDI));
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: Setting up iocell workaround\n", ahd_name(ahd));
+#endif
+ ahd_restore_modes(ahd, saved_modes);
+ ahd->flags &= ~AHD_HAD_FIRST_SEL;
+}
+
+static void
+ahd_iocell_first_selection(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ u_int sblkctl;
+
+ if ((ahd->flags & AHD_HAD_FIRST_SEL) != 0)
+ return;
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ sblkctl = ahd_inb(ahd, SBLKCTL);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: iocell first selection\n", ahd_name(ahd));
+#endif
+ if ((sblkctl & ENAB40) != 0) {
+ ahd_outb(ahd, DSPDATACTL,
+ ahd_inb(ahd, DSPDATACTL) & ~BYPASSENAB);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: BYPASS now disabled\n", ahd_name(ahd));
+#endif
+ }
+ ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) & ~(ENSELDO|ENSELDI));
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+ ahd_restore_modes(ahd, saved_modes);
+ ahd->flags |= AHD_HAD_FIRST_SEL;
+}
+
+/*************************** SCB Management ***********************************/
+static void
+ahd_add_col_list(struct ahd_softc *ahd, struct scb *scb, u_int col_idx)
+{
+ struct scb_list *free_list;
+ struct scb_tailq *free_tailq;
+ struct scb *first_scb;
+
+ scb->flags |= SCB_ON_COL_LIST;
+ AHD_SET_SCB_COL_IDX(scb, col_idx);
+ free_list = &ahd->scb_data.free_scb_lists[col_idx];
+ free_tailq = &ahd->scb_data.free_scbs;
+ first_scb = LIST_FIRST(free_list);
+ if (first_scb != NULL) {
+ LIST_INSERT_AFTER(first_scb, scb, collision_links);
+ } else {
+ LIST_INSERT_HEAD(free_list, scb, collision_links);
+ TAILQ_INSERT_TAIL(free_tailq, scb, links.tqe);
+ }
+}
+
+static void
+ahd_rem_col_list(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct scb_list *free_list;
+ struct scb_tailq *free_tailq;
+ struct scb *first_scb;
+ u_int col_idx;
+
+ scb->flags &= ~SCB_ON_COL_LIST;
+ col_idx = AHD_GET_SCB_COL_IDX(ahd, scb);
+ free_list = &ahd->scb_data.free_scb_lists[col_idx];
+ free_tailq = &ahd->scb_data.free_scbs;
+ first_scb = LIST_FIRST(free_list);
+ if (first_scb == scb) {
+ struct scb *next_scb;
+
+ /*
+ * Maintain order in the collision free
+ * lists for fairness if this device has
+ * other colliding tags active.
+ */
+ next_scb = LIST_NEXT(scb, collision_links);
+ if (next_scb != NULL) {
+ TAILQ_INSERT_AFTER(free_tailq, scb,
+ next_scb, links.tqe);
+ }
+ TAILQ_REMOVE(free_tailq, scb, links.tqe);
+ }
+ LIST_REMOVE(scb, collision_links);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+struct scb *
+ahd_get_scb(struct ahd_softc *ahd, u_int col_idx)
+{
+ struct scb *scb;
+ int tries;
+
+ tries = 0;
+look_again:
+ TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+ if (AHD_GET_SCB_COL_IDX(ahd, scb) != col_idx) {
+ ahd_rem_col_list(ahd, scb);
+ goto found;
+ }
+ }
+ if ((scb = LIST_FIRST(&ahd->scb_data.any_dev_free_scb_list)) == NULL) {
+
+ if (tries++ != 0)
+ return (NULL);
+ ahd_alloc_scbs(ahd);
+ goto look_again;
+ }
+ LIST_REMOVE(scb, links.le);
+ if (col_idx != AHD_NEVER_COL_IDX
+ && (scb->col_scb != NULL)
+ && (scb->col_scb->flags & SCB_ACTIVE) == 0) {
+ LIST_REMOVE(scb->col_scb, links.le);
+ ahd_add_col_list(ahd, scb->col_scb, col_idx);
+ }
+found:
+ scb->flags |= SCB_ACTIVE;
+ return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+void
+ahd_free_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+
+ /* Clean up for the next user */
+ scb->flags = SCB_FLAG_NONE;
+ scb->hscb->control = 0;
+ ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = NULL;
+
+ if (scb->col_scb == NULL) {
+
+ /*
+ * No collision possible. Just free normally.
+ */
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb, links.le);
+ } else if ((scb->col_scb->flags & SCB_ON_COL_LIST) != 0) {
+
+ /*
+ * The SCB we might have collided with is on
+ * a free collision list. Put both SCBs on
+ * the generic list.
+ */
+ ahd_rem_col_list(ahd, scb->col_scb);
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb, links.le);
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb->col_scb, links.le);
+ } else if ((scb->col_scb->flags
+ & (SCB_PACKETIZED|SCB_ACTIVE)) == SCB_ACTIVE
+ && (scb->col_scb->hscb->control & TAG_ENB) != 0) {
+
+ /*
+ * The SCB we might collide with on the next allocation
+ * is still active in a non-packetized, tagged, context.
+ * Put us on the SCB collision list.
+ */
+ ahd_add_col_list(ahd, scb,
+ AHD_GET_SCB_COL_IDX(ahd, scb->col_scb));
+ } else {
+ /*
+ * The SCB we might collide with on the next allocation
+ * is either active in a packetized context, or free.
+ * Since we can't collide, put this SCB on the generic
+ * free list.
+ */
+ LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+ scb, links.le);
+ }
+
+ ahd_platform_scb_free(ahd, scb);
+}
+
+void
+ahd_alloc_scbs(struct ahd_softc *ahd)
+{
+ struct scb_data *scb_data;
+ struct scb *next_scb;
+ struct hardware_scb *hscb;
+ struct map_node *hscb_map;
+ struct map_node *sg_map;
+ struct map_node *sense_map;
+ uint8_t *segs;
+ uint8_t *sense_data;
+ dma_addr_t hscb_busaddr;
+ dma_addr_t sg_busaddr;
+ dma_addr_t sense_busaddr;
+ int newcount;
+ int i;
+
+ scb_data = &ahd->scb_data;
+ if (scb_data->numscbs >= AHD_SCB_MAX_ALLOC)
+ /* Can't allocate any more */
+ return;
+
+ if (scb_data->scbs_left != 0) {
+ int offset;
+
+ offset = (PAGE_SIZE / sizeof(*hscb)) - scb_data->scbs_left;
+ hscb_map = SLIST_FIRST(&scb_data->hscb_maps);
+ hscb = &((struct hardware_scb *)hscb_map->vaddr)[offset];
+ hscb_busaddr = hscb_map->physaddr + (offset * sizeof(*hscb));
+ } else {
+ hscb_map = malloc(sizeof(*hscb_map), M_DEVBUF, M_NOWAIT);
+
+ if (hscb_map == NULL)
+ return;
+
+ /* Allocate the next batch of hardware SCBs */
+ if (ahd_dmamem_alloc(ahd, scb_data->hscb_dmat,
+ (void **)&hscb_map->vaddr,
+ BUS_DMA_NOWAIT, &hscb_map->dmamap) != 0) {
+ free(hscb_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->hscb_maps, hscb_map, links);
+
+ ahd_dmamap_load(ahd, scb_data->hscb_dmat, hscb_map->dmamap,
+ hscb_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+ &hscb_map->physaddr, /*flags*/0);
+
+ hscb = (struct hardware_scb *)hscb_map->vaddr;
+ hscb_busaddr = hscb_map->physaddr;
+ scb_data->scbs_left = PAGE_SIZE / sizeof(*hscb);
+ }
+
+ if (scb_data->sgs_left != 0) {
+ int offset;
+
+ offset = ((ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd))
+ - scb_data->sgs_left) * ahd_sglist_size(ahd);
+ sg_map = SLIST_FIRST(&scb_data->sg_maps);
+ segs = sg_map->vaddr + offset;
+ sg_busaddr = sg_map->physaddr + offset;
+ } else {
+ sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+ if (sg_map == NULL)
+ return;
+
+ /* Allocate the next batch of S/G lists */
+ if (ahd_dmamem_alloc(ahd, scb_data->sg_dmat,
+ (void **)&sg_map->vaddr,
+ BUS_DMA_NOWAIT, &sg_map->dmamap) != 0) {
+ free(sg_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+ ahd_dmamap_load(ahd, scb_data->sg_dmat, sg_map->dmamap,
+ sg_map->vaddr, ahd_sglist_allocsize(ahd),
+ ahd_dmamap_cb, &sg_map->physaddr, /*flags*/0);
+
+ segs = sg_map->vaddr;
+ sg_busaddr = sg_map->physaddr;
+ scb_data->sgs_left =
+ ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_MEMORY)
+ printf("Mapped SG data\n");
+#endif
+ }
+
+ if (scb_data->sense_left != 0) {
+ int offset;
+
+ offset = PAGE_SIZE - (AHD_SENSE_BUFSIZE * scb_data->sense_left);
+ sense_map = SLIST_FIRST(&scb_data->sense_maps);
+ sense_data = sense_map->vaddr + offset;
+ sense_busaddr = sense_map->physaddr + offset;
+ } else {
+ sense_map = malloc(sizeof(*sense_map), M_DEVBUF, M_NOWAIT);
+
+ if (sense_map == NULL)
+ return;
+
+ /* Allocate the next batch of sense buffers */
+ if (ahd_dmamem_alloc(ahd, scb_data->sense_dmat,
+ (void **)&sense_map->vaddr,
+ BUS_DMA_NOWAIT, &sense_map->dmamap) != 0) {
+ free(sense_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sense_maps, sense_map, links);
+
+ ahd_dmamap_load(ahd, scb_data->sense_dmat, sense_map->dmamap,
+ sense_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+ &sense_map->physaddr, /*flags*/0);
+
+ sense_data = sense_map->vaddr;
+ sense_busaddr = sense_map->physaddr;
+ scb_data->sense_left = PAGE_SIZE / AHD_SENSE_BUFSIZE;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_MEMORY)
+ printf("Mapped sense data\n");
+#endif
+ }
+
+ newcount = MIN(scb_data->sense_left, scb_data->scbs_left);
+ newcount = MIN(newcount, scb_data->sgs_left);
+ newcount = MIN(newcount, (AHD_SCB_MAX_ALLOC - scb_data->numscbs));
+ scb_data->sense_left -= newcount;
+ scb_data->scbs_left -= newcount;
+ scb_data->sgs_left -= newcount;
+ for (i = 0; i < newcount; i++) {
+ u_int col_tag;
+
+ struct scb_platform_data *pdata;
+#ifndef __linux__
+ int error;
+#endif
+ next_scb = (struct scb *)malloc(sizeof(*next_scb),
+ M_DEVBUF, M_NOWAIT);
+ if (next_scb == NULL)
+ break;
+
+ pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+ M_DEVBUF, M_NOWAIT);
+ if (pdata == NULL) {
+ free(next_scb, M_DEVBUF);
+ break;
+ }
+ next_scb->platform_data = pdata;
+ next_scb->hscb_map = hscb_map;
+ next_scb->sg_map = sg_map;
+ next_scb->sense_map = sense_map;
+ next_scb->sg_list = segs;
+ next_scb->sense_data = sense_data;
+ next_scb->sense_busaddr = sense_busaddr;
+ memset(hscb, 0, sizeof(*hscb));
+ next_scb->hscb = hscb;
+ hscb->hscb_busaddr = ahd_htole32(hscb_busaddr);
+
+ /*
+ * The sequencer always starts with the second entry.
+ * The first entry is embedded in the scb.
+ */
+ next_scb->sg_list_busaddr = sg_busaddr;
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ next_scb->sg_list_busaddr
+ += sizeof(struct ahd_dma64_seg);
+ else
+ next_scb->sg_list_busaddr += sizeof(struct ahd_dma_seg);
+ next_scb->ahd_softc = ahd;
+ next_scb->flags = SCB_FLAG_NONE;
+#ifndef __linux__
+ error = ahd_dmamap_create(ahd, ahd->buffer_dmat, /*flags*/0,
+ &next_scb->dmamap);
+ if (error != 0) {
+ free(next_scb, M_DEVBUF);
+ free(pdata, M_DEVBUF);
+ break;
+ }
+#endif
+ next_scb->hscb->tag = ahd_htole16(scb_data->numscbs);
+ col_tag = scb_data->numscbs ^ 0x100;
+ next_scb->col_scb = ahd_find_scb_by_tag(ahd, col_tag);
+ if (next_scb->col_scb != NULL)
+ next_scb->col_scb->col_scb = next_scb;
+ ahd_free_scb(ahd, next_scb);
+ hscb++;
+ hscb_busaddr += sizeof(*hscb);
+ segs += ahd_sglist_size(ahd);
+ sg_busaddr += ahd_sglist_size(ahd);
+ sense_data += AHD_SENSE_BUFSIZE;
+ sense_busaddr += AHD_SENSE_BUFSIZE;
+ scb_data->numscbs++;
+ }
+}
+
+void
+ahd_controller_info(struct ahd_softc *ahd, char *buf)
+{
+ const char *speed;
+ const char *type;
+ int len;
+
+ len = sprintf(buf, "%s: ", ahd_chip_names[ahd->chip & AHD_CHIPID_MASK]);
+ buf += len;
+
+ speed = "Ultra320 ";
+ if ((ahd->features & AHD_WIDE) != 0) {
+ type = "Wide ";
+ } else {
+ type = "Single ";
+ }
+ len = sprintf(buf, "%s%sChannel %c, SCSI Id=%d, ",
+ speed, type, ahd->channel, ahd->our_id);
+ buf += len;
+
+ sprintf(buf, "%s, %d SCBs", ahd->bus_description,
+ ahd->scb_data.maxhscbs);
+}
+
+static const char *channel_strings[] = {
+ "Primary Low",
+ "Primary High",
+ "Secondary Low",
+ "Secondary High"
+};
+
+static const char *termstat_strings[] = {
+ "Terminated Correctly",
+ "Over Terminated",
+ "Under Terminated",
+ "Not Configured"
+};
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahd_init(struct ahd_softc *ahd)
+{
+ uint8_t *base_vaddr;
+ uint8_t *next_vaddr;
+ dma_addr_t next_baddr;
+ size_t driver_data_size;
+ int i;
+ int error;
+ u_int warn_user;
+ uint8_t current_sensing;
+ uint8_t fstat;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+ ahd->stack_size = ahd_probe_stack_size(ahd);
+ ahd->saved_stack = malloc(ahd->stack_size * sizeof(uint16_t),
+ M_DEVBUF, M_NOWAIT);
+ if (ahd->saved_stack == NULL)
+ return (ENOMEM);
+
+ /*
+ * Verify that the compiler hasn't over-agressively
+ * padded important structures.
+ */
+ if (sizeof(struct hardware_scb) != 64)
+ panic("Hardware SCB size is incorrect");
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_DEBUG_SEQUENCER) != 0)
+ ahd->flags |= AHD_SEQUENCER_DEBUG;
+#endif
+
+ /*
+ * Default to allowing initiator operations.
+ */
+ ahd->flags |= AHD_INITIATORROLE;
+
+ /*
+ * Only allow target mode features if this unit has them enabled.
+ */
+ if ((AHD_TMODE_ENABLE & (0x1 << ahd->unit)) == 0)
+ ahd->features &= ~AHD_TARGETMODE;
+
+#ifndef __linux__
+ /* DMA tag for mapping buffers into device visible space. */
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/ahd->flags & AHD_39BIT_ADDRESSING
+ ? (dma_addr_t)0x7FFFFFFFFFULL
+ : BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/(AHD_NSEG - 1) * PAGE_SIZE,
+ /*nsegments*/AHD_NSEG,
+ /*maxsegsz*/AHD_MAXTRANSFER_SIZE,
+ /*flags*/BUS_DMA_ALLOCNOW,
+ &ahd->buffer_dmat) != 0) {
+ return (ENOMEM);
+ }
+#endif
+
+ ahd->init_level++;
+
+ /*
+ * DMA tag for our command fifos and other data in system memory
+ * the card's sequencer must be able to access. For initiator
+ * roles, we need to allocate space for the qoutfifo. When providing
+ * for the target mode role, we must additionally provide space for
+ * the incoming target command fifo.
+ */
+ driver_data_size = AHD_SCB_MAX * sizeof(uint16_t)
+ + sizeof(struct hardware_scb);
+ if ((ahd->features & AHD_TARGETMODE) != 0)
+ driver_data_size += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0)
+ driver_data_size += PKT_OVERRUN_BUFSIZE;
+ if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ driver_data_size,
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &ahd->shared_data_dmat) != 0) {
+ return (ENOMEM);
+ }
+
+ ahd->init_level++;
+
+ /* Allocation of driver data */
+ if (ahd_dmamem_alloc(ahd, ahd->shared_data_dmat,
+ (void **)&base_vaddr,
+ BUS_DMA_NOWAIT, &ahd->shared_data_dmamap) != 0) {
+ return (ENOMEM);
+ }
+
+ ahd->init_level++;
+
+ /* And permanently map it in */
+ ahd_dmamap_load(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+ base_vaddr, driver_data_size, ahd_dmamap_cb,
+ &ahd->shared_data_busaddr, /*flags*/0);
+ ahd->qoutfifo = (uint16_t *)base_vaddr;
+ next_vaddr = (uint8_t *)&ahd->qoutfifo[AHD_QOUT_SIZE];
+ next_baddr = ahd->shared_data_busaddr + AHD_QOUT_SIZE*sizeof(uint16_t);
+ if ((ahd->features & AHD_TARGETMODE) != 0) {
+ ahd->targetcmds = (struct target_cmd *)next_vaddr;
+ next_vaddr += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+ next_baddr += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+ }
+
+ if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+ ahd->overrun_buf = next_vaddr;
+ next_vaddr += PKT_OVERRUN_BUFSIZE;
+ next_baddr += PKT_OVERRUN_BUFSIZE;
+ }
+
+ /*
+ * We need one SCB to serve as the "next SCB". Since the
+ * tag identifier in this SCB will never be used, there is
+ * no point in using a valid HSCB tag from an SCB pulled from
+ * the standard free pool. So, we allocate this "sentinel"
+ * specially from the DMA safe memory chunk used for the QOUTFIFO.
+ */
+ ahd->next_queued_hscb = (struct hardware_scb *)next_vaddr;
+ ahd->next_queued_hscb->hscb_busaddr = ahd_htole32(next_baddr);
+
+ ahd->init_level++;
+
+ /* Allocate SCB data now that buffer_dmat is initialized */
+ if (ahd_init_scbdata(ahd) != 0)
+ return (ENOMEM);
+
+ if ((ahd->flags & AHD_INITIATORROLE) == 0)
+ ahd->flags &= ~AHD_RESET_BUS_A;
+
+ /*
+ * Before committing these settings to the chip, give
+ * the OSM one last chance to modify our configuration.
+ */
+ ahd_platform_init(ahd);
+
+ /* Bring up the chip. */
+ ahd_chip_init(ahd);
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+ if ((ahd->flags & AHD_CURRENT_SENSING) == 0)
+ goto init_done;
+
+ /*
+ * Verify termination based on current draw and
+ * warn user if the bus is over/under terminated.
+ */
+ error = ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL,
+ CURSENSE_ENB);
+ if (error != 0) {
+ printf("%s: current sensing timeout 1\n", ahd_name(ahd));
+ goto init_done;
+ }
+ for (i = 20, fstat = FLX_FSTAT_BUSY;
+ (fstat & FLX_FSTAT_BUSY) != 0 && i; i--) {
+ error = ahd_read_flexport(ahd, FLXADDR_FLEXSTAT, &fstat);
+ if (error != 0) {
+ printf("%s: current sensing timeout 2\n",
+ ahd_name(ahd));
+ goto init_done;
+ }
+ }
+ if (i == 0) {
+ printf("%s: Timedout during current-sensing test\n",
+ ahd_name(ahd));
+ goto init_done;
+ }
+
+ /* Latch Current Sensing status. */
+ error = ahd_read_flexport(ahd, FLXADDR_CURRENT_STAT, &current_sensing);
+ if (error != 0) {
+ printf("%s: current sensing timeout 3\n", ahd_name(ahd));
+ goto init_done;
+ }
+
+ /* Diable current sensing. */
+ ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_TERMCTL) != 0) {
+ printf("%s: current_sensing == 0x%x\n",
+ ahd_name(ahd), current_sensing);
+ }
+#endif
+ warn_user = 0;
+ for (i = 0; i < 4; i++, current_sensing >>= FLX_CSTAT_SHIFT) {
+ u_int term_stat;
+
+ term_stat = (current_sensing & FLX_CSTAT_MASK);
+ switch (term_stat) {
+ case FLX_CSTAT_OVER:
+ case FLX_CSTAT_UNDER:
+ warn_user++;
+ case FLX_CSTAT_INVALID:
+ case FLX_CSTAT_OKAY:
+ if (warn_user == 0 && bootverbose == 0)
+ break;
+ printf("%s: %s Channel %s\n", ahd_name(ahd),
+ channel_strings[i], termstat_strings[term_stat]);
+ break;
+ }
+ }
+ if (warn_user) {
+ printf("%s: WARNING. Termination is not configured correctly.\n"
+ "%s: WARNING. SCSI bus operations may FAIL.\n",
+ ahd_name(ahd), ahd_name(ahd));
+ }
+init_done:
+ ahd_restart(ahd);
+ ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
+ ahd_stat_timer, ahd);
+ return (0);
+}
+
+/*
+ * (Re)initialize chip state after a chip reset.
+ */
+static void
+ahd_chip_init(struct ahd_softc *ahd)
+{
+ uint32_t busaddr;
+ u_int sxfrctl1;
+ u_int scsiseq_template;
+ u_int wait;
+ u_int i;
+ u_int target;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ ahd_outb(ahd, SBLKCTL, ahd_inb(ahd, SBLKCTL) & ~(DIAGLEDEN|DIAGLEDON));
+
+ /*
+ * Return HS_MAILBOX to its default value.
+ */
+ ahd->hs_mailbox = 0;
+ ahd_outb(ahd, HS_MAILBOX, 0);
+
+ /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1. */
+ ahd_outb(ahd, IOWNID, ahd->our_id);
+ ahd_outb(ahd, TOWNID, ahd->our_id);
+ sxfrctl1 = (ahd->flags & AHD_TERM_ENB_A) != 0 ? STPWEN : 0;
+ sxfrctl1 |= (ahd->flags & AHD_SPCHK_ENB_A) != 0 ? ENSPCHK : 0;
+ if ((ahd->bugs & AHD_LONG_SETIMO_BUG)
+ && (ahd->seltime != STIMESEL_MIN)) {
+ /*
+ * The selection timer duration is twice as long
+ * as it should be. Halve it by adding "1" to
+ * the user specified setting.
+ */
+ sxfrctl1 |= ahd->seltime + STIMESEL_BUG_ADJ;
+ } else {
+ sxfrctl1 |= ahd->seltime;
+ }
+
+ ahd_outb(ahd, SXFRCTL0, DFON);
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1|ahd->seltime|ENSTIMER|ACTNEGEN);
+ ahd_outb(ahd, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+
+ /*
+ * Now that termination is set, wait for up
+ * to 500ms for our transceivers to settle. If
+ * the adapter does not have a cable attached,
+ * the transceivers may never settle, so don't
+ * complain if we fail here.
+ */
+ for (wait = 10000;
+ (ahd_inb(ahd, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+ wait--)
+ ahd_delay(100);
+
+ /* Clear any false bus resets due to the transceivers settling */
+ ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+
+ /* Initialize mode specific S/G state. */
+ for (i = 0; i < 2; i++) {
+ ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+ ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+ ahd_outb(ahd, SG_STATE, 0);
+ ahd_outb(ahd, CLRSEQINTSRC, 0xFF);
+ ahd_outb(ahd, SEQIMODE,
+ ENSAVEPTRS|ENCFG4DATA|ENCFG4ISTAT
+ |ENCFG4TSTAT|ENCFG4ICMD|ENCFG4TCMD);
+ }
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ ahd_outb(ahd, DSCOMMAND0, ahd_inb(ahd, DSCOMMAND0)|MPARCKEN|CACHETHEN);
+ ahd_outb(ahd, DFF_THRSH, RD_DFTHRSH_75|WR_DFTHRSH_75);
+ ahd_outb(ahd, SIMODE0, ENIOERR|ENOVERRUN);
+ ahd_outb(ahd, SIMODE3, ENNTRAMPERR|ENOSRAMPERR);
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ ahd_outb(ahd, OPTIONMODE, AUTOACKEN|AUTO_MSGOUT_DE);
+ } else {
+ ahd_outb(ahd, OPTIONMODE, AUTOACKEN|BUSFREEREV|AUTO_MSGOUT_DE);
+ }
+ ahd_outb(ahd, SCSCHKN, CURRFIFODEF|WIDERESEN|SHVALIDSTDIS);
+ if ((ahd->chip & AHD_BUS_MASK) == AHD_PCIX)
+ /*
+ * Do not issue a target abort when a split completion
+ * error occurs. Let our PCIX interrupt handler deal
+ * with it instead. H2A4 Razor #625
+ */
+ ahd_outb(ahd, PCIXCTL, ahd_inb(ahd, PCIXCTL) | SPLTSTADIS);
+
+ if ((ahd->bugs & AHD_LQOOVERRUN_BUG) != 0)
+ ahd_outb(ahd, LQOSCSCTL, LQONOCHKOVER);
+
+ /*
+ * Tweak IOCELL settings.
+ */
+ if ((ahd->flags & AHD_HP_BOARD) != 0) {
+ for (i = 0; i < NUMDSPS; i++) {
+ ahd_outb(ahd, DSPSELECT, i);
+ ahd_outb(ahd, WRTBIASCTL, WRTBIASCTL_HP_DEFAULT);
+ }
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("%s: WRTBIASCTL now 0x%x\n", ahd_name(ahd),
+ WRTBIASCTL_HP_DEFAULT);
+#endif
+ }
+ ahd_setup_iocell_workaround(ahd);
+
+ /*
+ * Enable LQI Manager interrupts.
+ */
+ ahd_outb(ahd, LQIMODE1, ENLQIPHASE_LQ|ENLQIPHASE_NLQ|ENLIQABORT
+ | ENLQICRCI_LQ|ENLQICRCI_NLQ|ENLQIBADLQI
+ | ENLQIOVERI_LQ|ENLQIOVERI_NLQ);
+ ahd_outb(ahd, LQOMODE0, ENLQOATNLQ|ENLQOATNPKT|ENLQOTCRC);
+ /*
+ * An interrupt from LQOBUSFREE is made redundant by the
+ * BUSFREE interrupt. We choose to have the sequencer catch
+ * LQOPHCHGINPKT errors manually for the command phase at the
+ * start of a packetized selection case.
+ ahd_outb(ahd, LQOMODE1, ENLQOBUSFREE|ENLQOPHACHGINPKT);
+ */
+ ahd_outb(ahd, LQOMODE1, 0);
+
+ /*
+ * Setup sequencer interrupt handlers.
+ */
+ ahd_outw(ahd, INTVEC1_ADDR, ahd_resolve_seqaddr(ahd, LABEL_seq_isr));
+ ahd_outw(ahd, INTVEC2_ADDR, ahd_resolve_seqaddr(ahd, LABEL_timer_isr));
+
+ /*
+ * Setup SCB Offset registers.
+ */
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+ ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb,
+ pkt_long_lun));
+ } else {
+ ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb, lun));
+ }
+ ahd_outb(ahd, CMDLENPTR, offsetof(struct hardware_scb, cdb_len));
+ ahd_outb(ahd, ATTRPTR, offsetof(struct hardware_scb, task_attribute));
+ ahd_outb(ahd, FLAGPTR, offsetof(struct hardware_scb, task_management));
+ ahd_outb(ahd, CMDPTR, offsetof(struct hardware_scb,
+ shared_data.idata.cdb));
+ ahd_outb(ahd, QNEXTPTR,
+ offsetof(struct hardware_scb, next_hscb_busaddr));
+ ahd_outb(ahd, ABRTBITPTR, MK_MESSAGE_BIT_OFFSET);
+ ahd_outb(ahd, ABRTBYTEPTR, offsetof(struct hardware_scb, control));
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+ ahd_outb(ahd, LUNLEN,
+ sizeof(ahd->next_queued_hscb->pkt_long_lun) - 1);
+ } else {
+ ahd_outb(ahd, LUNLEN, LUNLEN_SINGLE_LEVEL_LUN);
+ }
+ ahd_outb(ahd, CDBLIMIT, SCB_CDB_LEN_PTR - 1);
+ ahd_outb(ahd, MAXCMD, 0xFF);
+ ahd_outb(ahd, SCBAUTOPTR,
+ AUSCBPTR_EN | offsetof(struct hardware_scb, tag));
+
+ /* We haven't been enabled for target mode yet. */
+ ahd_outb(ahd, MULTARGID, 0);
+ ahd_outb(ahd, MULTARGID + 1, 0);
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ /* Initialize the negotiation table. */
+ if ((ahd->features & AHD_NEW_IOCELL_OPTS) == 0) {
+ /*
+ * Clear the spare bytes in the neg table to avoid
+ * spurious parity errors.
+ */
+ for (target = 0; target < AHD_NUM_TARGETS; target++) {
+ ahd_outb(ahd, NEGOADDR, target);
+ ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PER_DEV0);
+ for (i = 0; i < AHD_NUM_PER_DEV_ANNEXCOLS; i++)
+ ahd_outb(ahd, ANNEXDAT, 0);
+ }
+ }
+ for (target = 0; target < AHD_NUM_TARGETS; target++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ target, &tstate);
+ ahd_compile_devinfo(&devinfo, ahd->our_id,
+ target, CAM_LUN_WILDCARD,
+ 'A', ROLE_INITIATOR);
+ ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+ }
+
+ ahd_outb(ahd, CLRSINT3, NTRAMPERR|OSRAMPERR);
+ ahd_outb(ahd, CLRINT, CLRSCSIINT);
+
+#ifdef NEEDS_MORE_TESTING
+ /*
+ * Always enable abort on incoming L_Qs if this feature is
+ * supported. We use this to catch invalid SCB references.
+ */
+ if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0)
+ ahd_outb(ahd, LQCTL1, ABORTPENDING);
+ else
+#endif
+ ahd_outb(ahd, LQCTL1, 0);
+
+ /* All of our queues are empty */
+ ahd->qoutfifonext = 0;
+ ahd->qoutfifonext_valid_tag = QOUTFIFO_ENTRY_VALID_LE;
+ ahd_outb(ahd, QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID >> 8);
+ for (i = 0; i < AHD_QOUT_SIZE; i++)
+ ahd->qoutfifo[i] = 0;
+ ahd_sync_qoutfifo(ahd, BUS_DMASYNC_PREREAD);
+
+ ahd->qinfifonext = 0;
+ for (i = 0; i < AHD_QIN_SIZE; i++)
+ ahd->qinfifo[i] = SCB_LIST_NULL;
+
+ if ((ahd->features & AHD_TARGETMODE) != 0) {
+ /* All target command blocks start out invalid. */
+ for (i = 0; i < AHD_TMODE_CMDS; i++)
+ ahd->targetcmds[i].cmd_valid = 0;
+ ahd_sync_tqinfifo(ahd, BUS_DMASYNC_PREREAD);
+ ahd->tqinfifonext = 1;
+ ahd_outb(ahd, KERNEL_TQINPOS, ahd->tqinfifonext - 1);
+ ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+ }
+
+ /* Initialize Scratch Ram. */
+ ahd_outb(ahd, SEQ_FLAGS, 0);
+ ahd_outb(ahd, SEQ_FLAGS2, 0);
+
+ /* We don't have any waiting selections */
+ ahd_outw(ahd, WAITING_TID_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, WAITING_TID_TAIL, SCB_LIST_NULL);
+ for (i = 0; i < AHD_NUM_TARGETS; i++)
+ ahd_outw(ahd, WAITING_SCB_TAILS + (2 * i), SCB_LIST_NULL);
+
+ /*
+ * Nobody is waiting to be DMAed into the QOUTFIFO.
+ */
+ ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, COMPLETE_SCB_DMAINPROG_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+
+ /*
+ * The Freeze Count is 0.
+ */
+ ahd_outw(ahd, QFREEZE_COUNT, 0);
+
+ /*
+ * Tell the sequencer where it can find our arrays in memory.
+ */
+ busaddr = ahd->shared_data_busaddr;
+ ahd_outb(ahd, SHARED_DATA_ADDR, busaddr & 0xFF);
+ ahd_outb(ahd, SHARED_DATA_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, SHARED_DATA_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, SHARED_DATA_ADDR + 3, (busaddr >> 24) & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR, busaddr & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+ /*
+ * Setup the allowed SCSI Sequences based on operational mode.
+ * If we are a target, we'll enable select in operations once
+ * we've had a lun enabled.
+ */
+ scsiseq_template = ENAUTOATNP;
+ if ((ahd->flags & AHD_INITIATORROLE) != 0)
+ scsiseq_template |= ENRSELI;
+ ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq_template);
+
+ /* There are no busy SCBs yet. */
+ for (target = 0; target < AHD_NUM_TARGETS; target++) {
+ int lun;
+
+ for (lun = 0; lun < AHD_NUM_LUNS_NONPKT; lun++)
+ ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(target, 'A', lun));
+ }
+
+ /*
+ * Initialize the group code to command length table.
+ * Vendor Unique codes are set to 0 so we only capture
+ * the first byte of the cdb. These can be overridden
+ * when target mode is enabled.
+ */
+ ahd_outb(ahd, CMDSIZE_TABLE, 5);
+ ahd_outb(ahd, CMDSIZE_TABLE + 1, 9);
+ ahd_outb(ahd, CMDSIZE_TABLE + 2, 9);
+ ahd_outb(ahd, CMDSIZE_TABLE + 3, 0);
+ ahd_outb(ahd, CMDSIZE_TABLE + 4, 15);
+ ahd_outb(ahd, CMDSIZE_TABLE + 5, 11);
+ ahd_outb(ahd, CMDSIZE_TABLE + 6, 0);
+ ahd_outb(ahd, CMDSIZE_TABLE + 7, 0);
+
+ /* Tell the sequencer of our initial queue positions */
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ ahd_outb(ahd, QOFF_CTLSTA, SCB_QSIZE_512);
+ ahd->qinfifonext = 0;
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+ ahd_set_hescb_qoff(ahd, 0);
+ ahd_set_snscb_qoff(ahd, 0);
+ ahd_set_sescb_qoff(ahd, 0);
+ ahd_set_sdscb_qoff(ahd, 0);
+
+ /*
+ * Tell the sequencer which SCB will be the next one it receives.
+ */
+ busaddr = ahd_le32toh(ahd->next_queued_hscb->hscb_busaddr);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+ /*
+ * Default to coalescing disabled.
+ */
+ ahd_outw(ahd, INT_COALESCING_CMDCOUNT, 0);
+ ahd_outw(ahd, CMDS_PENDING, 0);
+ ahd_update_coalescing_values(ahd, ahd->int_coalescing_timer,
+ ahd->int_coalescing_maxcmds,
+ ahd->int_coalescing_mincmds);
+ ahd_enable_coalescing(ahd, FALSE);
+
+ ahd_loadseq(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+}
+
+/*
+ * Setup default device and controller settings.
+ * This should only be called if our probe has
+ * determined that no configuration data is available.
+ */
+int
+ahd_default_config(struct ahd_softc *ahd)
+{
+ int targ;
+
+ ahd->our_id = 7;
+
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate ahd_tmode_tstate. "
+ "Failing attach\n", ahd_name(ahd));
+ return (ENOMEM);
+ }
+
+ for (targ = 0; targ < AHD_NUM_TARGETS; targ++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ uint16_t target_mask;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ targ, &tstate);
+ /*
+ * We support SPC2 and SPI4.
+ */
+ tinfo->user.protocol_version = 4;
+ tinfo->user.transport_version = 4;
+
+ target_mask = 0x01 << targ;
+ ahd->user_discenable |= target_mask;
+ tstate->discenable |= target_mask;
+ ahd->user_tagenable |= target_mask;
+#ifdef AHD_FORCE_160
+ tinfo->user.period = AHD_SYNCRATE_DT;
+#else
+ tinfo->user.period = AHD_SYNCRATE_160;
+#endif
+ tinfo->user.offset = MAX_OFFSET;
+ tinfo->user.ppr_options = MSG_EXT_PPR_RD_STRM
+ | MSG_EXT_PPR_WR_FLOW
+ | MSG_EXT_PPR_HOLD_MCS
+ | MSG_EXT_PPR_IU_REQ
+ | MSG_EXT_PPR_QAS_REQ
+ | MSG_EXT_PPR_DT_REQ;
+ if ((ahd->features & AHD_RTI) != 0)
+ tinfo->user.ppr_options |= MSG_EXT_PPR_RTI;
+
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+ /*
+ * Start out Async/Narrow/Untagged and with
+ * conservative protocol support.
+ */
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
+ ahd_compile_devinfo(&devinfo, ahd->our_id,
+ targ, CAM_LUN_WILDCARD,
+ 'A', ROLE_INITIATOR);
+ tstate->tagenable &= ~target_mask;
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ }
+ return (0);
+}
+
+/*
+ * Parse device configuration information.
+ */
+int
+ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc)
+{
+ int targ;
+ int max_targ;
+
+ max_targ = sc->max_targets & CFMAXTARG;
+ ahd->our_id = sc->brtime_id & CFSCSIID;
+
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate ahd_tmode_tstate. "
+ "Failing attach\n", ahd_name(ahd));
+ return (ENOMEM);
+ }
+
+ for (targ = 0; targ < max_targ; targ++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_transinfo *user_tinfo;
+ struct ahd_tmode_tstate *tstate;
+ uint16_t target_mask;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ targ, &tstate);
+ user_tinfo = &tinfo->user;
+
+ /*
+ * We support SPC2 and SPI4.
+ */
+ tinfo->user.protocol_version = 4;
+ tinfo->user.transport_version = 4;
+
+ target_mask = 0x01 << targ;
+ ahd->user_discenable &= ~target_mask;
+ tstate->discenable &= ~target_mask;
+ ahd->user_tagenable &= ~target_mask;
+ if (sc->device_flags[targ] & CFDISC) {
+ tstate->discenable |= target_mask;
+ ahd->user_discenable |= target_mask;
+ ahd->user_tagenable |= target_mask;
+ } else {
+ /*
+ * Cannot be packetized without disconnection.
+ */
+ sc->device_flags[targ] &= ~CFPACKETIZED;
+ }
+
+ user_tinfo->ppr_options = 0;
+ user_tinfo->period = (sc->device_flags[targ] & CFXFER);
+ if (user_tinfo->period < CFXFER_ASYNC) {
+ if (user_tinfo->period <= AHD_PERIOD_10MHz)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_DT_REQ;
+ user_tinfo->offset = MAX_OFFSET;
+ } else {
+ user_tinfo->offset = 0;
+ user_tinfo->period = AHD_ASYNC_XFER_PERIOD;
+ }
+#ifdef AHD_FORCE_160
+ if (user_tinfo->period <= AHD_SYNCRATE_160)
+ user_tinfo->period = AHD_SYNCRATE_DT;
+#endif
+
+ if ((sc->device_flags[targ] & CFPACKETIZED) != 0) {
+ user_tinfo->ppr_options |= MSG_EXT_PPR_RD_STRM
+ | MSG_EXT_PPR_WR_FLOW
+ | MSG_EXT_PPR_HOLD_MCS
+ | MSG_EXT_PPR_IU_REQ;
+ if ((ahd->features & AHD_RTI) != 0)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_RTI;
+ }
+
+ if ((sc->device_flags[targ] & CFQAS) != 0)
+ user_tinfo->ppr_options |= MSG_EXT_PPR_QAS_REQ;
+
+ if ((sc->device_flags[targ] & CFWIDEB) != 0)
+ user_tinfo->width = MSG_EXT_WDTR_BUS_16_BIT;
+ else
+ user_tinfo->width = MSG_EXT_WDTR_BUS_8_BIT;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0)
+ printf("(%d): %x:%x:%x:%x\n", targ, user_tinfo->width,
+ user_tinfo->period, user_tinfo->offset,
+ user_tinfo->ppr_options);
+#endif
+ /*
+ * Start out Async/Narrow/Untagged and with
+ * conservative protocol support.
+ */
+ tstate->tagenable &= ~target_mask;
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
+ ahd_compile_devinfo(&devinfo, ahd->our_id,
+ targ, CAM_LUN_WILDCARD,
+ 'A', ROLE_INITIATOR);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+ /*paused*/TRUE);
+ }
+
+ ahd->flags &= ~AHD_SPCHK_ENB_A;
+ if (sc->bios_control & CFSPARITY)
+ ahd->flags |= AHD_SPCHK_ENB_A;
+
+ ahd->flags &= ~AHD_RESET_BUS_A;
+ if (sc->bios_control & CFRESETB)
+ ahd->flags |= AHD_RESET_BUS_A;
+
+ ahd->flags &= ~AHD_EXTENDED_TRANS_A;
+ if (sc->bios_control & CFEXTEND)
+ ahd->flags |= AHD_EXTENDED_TRANS_A;
+
+ ahd->flags &= ~AHD_BIOS_ENABLED;
+ if ((sc->bios_control & CFBIOSSTATE) == CFBS_ENABLED)
+ ahd->flags |= AHD_BIOS_ENABLED;
+
+ ahd->flags &= ~AHD_STPWLEVEL_A;
+ if ((sc->adapter_control & CFSTPWLEVEL) != 0)
+ ahd->flags |= AHD_STPWLEVEL_A;
+
+ return (0);
+}
+
+/*
+ * Parse device configuration information.
+ */
+int
+ahd_parse_vpddata(struct ahd_softc *ahd, struct vpd_config *vpd)
+{
+ int error;
+
+ error = ahd_verify_vpd_cksum(vpd);
+ if (error == 0)
+ return (EINVAL);
+ if ((vpd->bios_flags & VPDBOOTHOST) != 0)
+ ahd->flags |= AHD_BOOT_CHANNEL;
+ return (0);
+}
+
+void
+ahd_intr_enable(struct ahd_softc *ahd, int enable)
+{
+ u_int hcntrl;
+
+ hcntrl = ahd_inb(ahd, HCNTRL);
+ hcntrl &= ~INTEN;
+ ahd->pause &= ~INTEN;
+ ahd->unpause &= ~INTEN;
+ if (enable) {
+ hcntrl |= INTEN;
+ ahd->pause |= INTEN;
+ ahd->unpause |= INTEN;
+ }
+ ahd_outb(ahd, HCNTRL, hcntrl);
+}
+
+void
+ahd_update_coalescing_values(struct ahd_softc *ahd, u_int timer, u_int maxcmds,
+ u_int mincmds)
+{
+ if (timer > AHD_TIMER_MAX_US)
+ timer = AHD_TIMER_MAX_US;
+ ahd->int_coalescing_timer = timer;
+
+ if (maxcmds > AHD_INT_COALESCING_MAXCMDS_MAX)
+ maxcmds = AHD_INT_COALESCING_MAXCMDS_MAX;
+ if (mincmds > AHD_INT_COALESCING_MINCMDS_MAX)
+ mincmds = AHD_INT_COALESCING_MINCMDS_MAX;
+ ahd->int_coalescing_maxcmds = maxcmds;
+ ahd_outw(ahd, INT_COALESCING_TIMER, timer / AHD_TIMER_US_PER_TICK);
+ ahd_outb(ahd, INT_COALESCING_MAXCMDS, -maxcmds);
+ ahd_outb(ahd, INT_COALESCING_MINCMDS, -mincmds);
+}
+
+void
+ahd_enable_coalescing(struct ahd_softc *ahd, int enable)
+{
+
+ ahd->hs_mailbox &= ~ENINT_COALESCE;
+ if (enable)
+ ahd->hs_mailbox |= ENINT_COALESCE;
+ ahd_outb(ahd, HS_MAILBOX, ahd->hs_mailbox);
+ ahd_flush_device_writes(ahd);
+ ahd_run_qoutfifo(ahd);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahd_pause_and_flushwork(struct ahd_softc *ahd)
+{
+ u_int intstat;
+ u_int maxloops;
+ u_int qfreeze_cnt;
+
+ maxloops = 1000;
+ ahd->flags |= AHD_ALL_INTERRUPTS;
+ ahd_pause(ahd);
+ /*
+ * Increment the QFreeze Count so that the sequencer
+ * will not start new selections. We do this only
+ * until we are safely paused without further selections
+ * pending.
+ */
+ ahd_outw(ahd, QFREEZE_COUNT, ahd_inw(ahd, QFREEZE_COUNT) + 1);
+ ahd_outb(ahd, SEQ_FLAGS2, ahd_inb(ahd, SEQ_FLAGS2) | SELECTOUT_QFROZEN);
+ do {
+ struct scb *waiting_scb;
+
+ ahd_unpause(ahd);
+ ahd_intr(ahd);
+ ahd_pause(ahd);
+ ahd_clear_critical_section(ahd);
+ intstat = ahd_inb(ahd, INTSTAT);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ if ((ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0)
+ ahd_outb(ahd, SCSISEQ0,
+ ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+ /*
+ * In the non-packetized case, the sequencer (for Rev A),
+ * relies on ENSELO remaining set after SELDO. The hardware
+ * auto-clears ENSELO in the packetized case.
+ */
+ waiting_scb = ahd_lookup_scb(ahd,
+ ahd_inw(ahd, WAITING_TID_HEAD));
+ if (waiting_scb != NULL
+ && (waiting_scb->flags & SCB_PACKETIZED) == 0
+ && (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0)
+ ahd_outb(ahd, SCSISEQ0,
+ ahd_inb(ahd, SCSISEQ0) | ENSELO);
+ } while (--maxloops
+ && (intstat != 0xFF || (ahd->features & AHD_REMOVABLE) == 0)
+ && ((intstat & INT_PEND) != 0
+ || (ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
+ || (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0));
+
+ if (maxloops == 0) {
+ printf("Infinite interrupt loop, INTSTAT = %x",
+ ahd_inb(ahd, INTSTAT));
+ }
+ qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
+ if (qfreeze_cnt == 0) {
+ printf("%s: ahd_pause_and_flushwork with 0 qfreeze count!\n",
+ ahd_name(ahd));
+ } else {
+ qfreeze_cnt--;
+ }
+ ahd_outw(ahd, QFREEZE_COUNT, qfreeze_cnt);
+ if (qfreeze_cnt == 0)
+ ahd_outb(ahd, SEQ_FLAGS2,
+ ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
+
+ ahd_flush_qoutfifo(ahd);
+
+ ahd_platform_flushwork(ahd);
+ ahd->flags &= ~AHD_ALL_INTERRUPTS;
+}
+
+int
+ahd_suspend(struct ahd_softc *ahd)
+{
+
+ ahd_pause_and_flushwork(ahd);
+
+ if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+ ahd_unpause(ahd);
+ return (EBUSY);
+ }
+ ahd_shutdown(ahd);
+ return (0);
+}
+
+int
+ahd_resume(struct ahd_softc *ahd)
+{
+
+ ahd_reset(ahd, /*reinit*/TRUE);
+ ahd_intr_enable(ahd, TRUE);
+ ahd_restart(ahd);
+ return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Set SCBPTR to the SCB that contains the busy
+ * table entry for TCL. Return the offset into
+ * the SCB that contains the entry for TCL.
+ * saved_scbid is dereferenced and set to the
+ * scbid that should be restored once manipualtion
+ * of the TCL entry is complete.
+ */
+static __inline u_int
+ahd_index_busy_tcl(struct ahd_softc *ahd, u_int *saved_scbid, u_int tcl)
+{
+ /*
+ * Index to the SCB that contains the busy entry.
+ */
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ *saved_scbid = ahd_get_scbptr(ahd);
+ ahd_set_scbptr(ahd, TCL_LUN(tcl)
+ | ((TCL_TARGET_OFFSET(tcl) & 0xC) << 4));
+
+ /*
+ * And now calculate the SCB offset to the entry.
+ * Each entry is 2 bytes wide, hence the
+ * multiplication by 2.
+ */
+ return (((TCL_TARGET_OFFSET(tcl) & 0x3) << 1) + SCB_DISCONNECTED_LISTS);
+}
+
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ */
+u_int
+ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+ u_int scbid;
+ u_int scb_offset;
+ u_int saved_scbptr;
+
+ scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+ scbid = ahd_inw_scbram(ahd, scb_offset);
+ ahd_set_scbptr(ahd, saved_scbptr);
+ return (scbid);
+}
+
+void
+ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int scbid)
+{
+ u_int scb_offset;
+ u_int saved_scbptr;
+
+ scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+ ahd_outw(ahd, scb_offset, scbid);
+ ahd_set_scbptr(ahd, saved_scbptr);
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, int target,
+ char channel, int lun, u_int tag, role_t role)
+{
+ int targ = SCB_GET_TARGET(ahd, scb);
+ char chan = SCB_GET_CHANNEL(ahd, scb);
+ int slun = SCB_GET_LUN(scb);
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+ if (match != 0)
+ match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+ if (match != 0) {
+#ifdef AHD_TARGET_MODE
+ int group;
+
+ group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+ if (role == ROLE_INITIATOR) {
+ match = (group != XPT_FC_GROUP_TMODE)
+ && ((tag == SCB_GET_TAG(scb))
+ || (tag == SCB_LIST_NULL));
+ } else if (role == ROLE_TARGET) {
+ match = (group == XPT_FC_GROUP_TMODE)
+ && ((tag == scb->io_ctx->csio.tag_id)
+ || (tag == SCB_LIST_NULL));
+ }
+#else /* !AHD_TARGET_MODE */
+ match = ((tag == SCB_GET_TAG(scb)) || (tag == SCB_LIST_NULL));
+#endif /* AHD_TARGET_MODE */
+ }
+
+ return match;
+}
+
+void
+ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+ int target;
+ char channel;
+ int lun;
+
+ target = SCB_GET_TARGET(ahd, scb);
+ lun = SCB_GET_LUN(scb);
+ channel = SCB_GET_CHANNEL(ahd, scb);
+
+ ahd_search_qinfifo(ahd, target, channel, lun,
+ /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ ahd_platform_freeze_devq(ahd, scb);
+}
+
+void
+ahd_qinfifo_requeue_tail(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct scb *prev_scb;
+ ahd_mode_state saved_modes;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ prev_scb = NULL;
+ if (ahd_qinfifo_count(ahd) != 0) {
+ u_int prev_tag;
+ u_int prev_pos;
+
+ prev_pos = AHD_QIN_WRAP(ahd->qinfifonext - 1);
+ prev_tag = ahd->qinfifo[prev_pos];
+ prev_scb = ahd_lookup_scb(ahd, prev_tag);
+ }
+ ahd_qinfifo_requeue(ahd, prev_scb, scb);
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+static void
+ahd_qinfifo_requeue(struct ahd_softc *ahd, struct scb *prev_scb,
+ struct scb *scb)
+{
+ if (prev_scb == NULL) {
+ uint32_t busaddr;
+
+ busaddr = ahd_le32toh(scb->hscb->hscb_busaddr);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+ } else {
+ prev_scb->hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+ ahd_sync_scb(ahd, prev_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ }
+ ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+ ahd->qinfifonext++;
+ scb->hscb->next_hscb_busaddr = ahd->next_queued_hscb->hscb_busaddr;
+ ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahd_qinfifo_count(struct ahd_softc *ahd)
+{
+ u_int qinpos;
+ u_int wrap_qinpos;
+ u_int wrap_qinfifonext;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ qinpos = ahd_get_snscb_qoff(ahd);
+ wrap_qinpos = AHD_QIN_WRAP(qinpos);
+ wrap_qinfifonext = AHD_QIN_WRAP(ahd->qinfifonext);
+ if (wrap_qinfifonext >= wrap_qinpos)
+ return (wrap_qinfifonext - wrap_qinpos);
+ else
+ return (wrap_qinfifonext
+ + NUM_ELEMENTS(ahd->qinfifo) - wrap_qinpos);
+}
+
+void
+ahd_reset_cmds_pending(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int pending_cmds;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+ /*
+ * Don't count any commands as outstanding that the
+ * sequencer has already marked for completion.
+ */
+ ahd_flush_qoutfifo(ahd);
+
+ pending_cmds = 0;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ pending_cmds++;
+ }
+ ahd_outw(ahd, CMDS_PENDING, pending_cmds - ahd_qinfifo_count(ahd));
+ ahd_restore_modes(ahd, saved_modes);
+ ahd->flags &= ~AHD_UPDATE_PEND_CMDS;
+}
+
+int
+ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahd_search_action action)
+{
+ struct scb *scb;
+ struct scb *prev_scb;
+ ahd_mode_state saved_modes;
+ u_int qinstart;
+ u_int qinpos;
+ u_int qintail;
+ u_int tid_next;
+ u_int tid_prev;
+ u_int scbid;
+ u_int savedscbptr;
+ uint32_t busaddr;
+ int found;
+ int targets;
+
+ /* Must be in CCHAN mode */
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+ /*
+ * Halt any pending SCB DMA. The sequencer will reinitiate
+ * this dma if the qinfifo is not empty once we unpause.
+ */
+ if ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN|CCSCBDIR))
+ == (CCARREN|CCSCBEN|CCSCBDIR)) {
+ ahd_outb(ahd, CCSCBCTL,
+ ahd_inb(ahd, CCSCBCTL) & ~(CCARREN|CCSCBEN));
+ while ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN)) != 0)
+ ;
+ }
+ /* Determine sequencer's position in the qinfifo. */
+ qintail = AHD_QIN_WRAP(ahd->qinfifonext);
+ qinstart = ahd_get_snscb_qoff(ahd);
+ qinpos = AHD_QIN_WRAP(qinstart);
+ found = 0;
+ prev_scb = NULL;
+
+ if (action == SEARCH_PRINT) {
+ printf("qinstart = %d qinfifonext = %d\nQINFIFO:",
+ qinstart, ahd->qinfifonext);
+ }
+
+ /*
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
+ */
+ ahd->qinfifonext = qinstart;
+ busaddr = ahd_le32toh(ahd->next_queued_hscb->hscb_busaddr);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+ ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+ while (qinpos != qintail) {
+ scb = ahd_lookup_scb(ahd, ahd->qinfifo[qinpos]);
+ if (scb == NULL) {
+ printf("qinpos = %d, SCB index = %d\n",
+ qinpos, ahd->qinfifo[qinpos]);
+ panic("Loop 1\n");
+ }
+
+ if (ahd_match_scb(ahd, scb, target, channel, lun, tag, role)) {
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahd_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahd_set_transaction_status(scb,
+ status);
+ cstat = ahd_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahd_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in qinfifo\n");
+ ahd_done(ahd, scb);
+
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ break;
+ case SEARCH_PRINT:
+ printf(" 0x%x", ahd->qinfifo[qinpos]);
+ /* FALLTHROUGH */
+ case SEARCH_COUNT:
+ ahd_qinfifo_requeue(ahd, prev_scb, scb);
+ prev_scb = scb;
+ break;
+ }
+ } else {
+ ahd_qinfifo_requeue(ahd, prev_scb, scb);
+ prev_scb = scb;
+ }
+ qinpos = AHD_QIN_WRAP(qinpos+1);
+ }
+
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+
+ if (action == SEARCH_PRINT)
+ printf("\nWAITING_TID_QUEUES:\n");
+
+ /*
+ * Search waiting for selection lists. We traverse the
+ * list of "their ids" waiting for selection and, if
+ * appropriate, traverse the SCBs of each "their id"
+ * looking for matches.
+ */
+ savedscbptr = ahd_get_scbptr(ahd);
+ tid_next = ahd_inw(ahd, WAITING_TID_HEAD);
+ tid_prev = SCB_LIST_NULL;
+ targets = 0;
+ for (scbid = tid_next; !SCBID_IS_NULL(scbid); scbid = tid_next) {
+ u_int tid_head;
+
+ /*
+ * We limit based on the number of SCBs since
+ * MK_MESSAGE SCBs are not in the per-tid lists.
+ */
+ targets++;
+ if (targets > AHD_SCB_MAX) {
+ panic("TID LIST LOOP");
+ }
+ if (scbid >= ahd->scb_data.numscbs) {
+ printf("%s: Waiting TID List inconsistency. "
+ "SCB index == 0x%x, yet numscbs == 0x%x.",
+ ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+ ahd_dump_card_state(ahd);
+ panic("for safety");
+ }
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: SCB = 0x%x Not Active!\n",
+ ahd_name(ahd), scbid);
+ panic("Waiting TID List traversal\n");
+ }
+ ahd_set_scbptr(ahd, scbid);
+ tid_next = ahd_inw_scbram(ahd, SCB_NEXT2);
+ if (ahd_match_scb(ahd, scb, target, channel, CAM_LUN_WILDCARD,
+ SCB_LIST_NULL, ROLE_UNKNOWN) == 0) {
+ tid_prev = scbid;
+ continue;
+ }
+
+ /*
+ * We found a list of scbs that needs to be searched.
+ */
+ if (action == SEARCH_PRINT)
+ printf(" %d ( ", SCB_GET_TARGET(ahd, scb));
+ tid_head = scbid;
+ found += ahd_search_scb_list(ahd, target, channel,
+ lun, tag, role, status,
+ action, &tid_head,
+ SCB_GET_TARGET(ahd, scb));
+ if (tid_head != scbid)
+ ahd_stitch_tid_list(ahd, tid_prev, tid_head, tid_next);
+ if (!SCBID_IS_NULL(tid_head))
+ tid_prev = tid_head;
+ if (action == SEARCH_PRINT)
+ printf(")\n");
+ }
+ ahd_set_scbptr(ahd, savedscbptr);
+ ahd_restore_modes(ahd, saved_modes);
+ return (found);
+}
+
+static int
+ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahd_search_action action, u_int *list_head, u_int tid)
+{
+ struct scb *scb;
+ u_int scbid;
+ u_int next;
+ u_int prev;
+ int found;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ found = 0;
+ prev = SCB_LIST_NULL;
+ next = *list_head;
+ for (scbid = next; !SCBID_IS_NULL(scbid); scbid = next) {
+ if (scbid >= ahd->scb_data.numscbs) {
+ printf("%s:SCB List inconsistency. "
+ "SCB == 0x%x, yet numscbs == 0x%x.",
+ ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+ ahd_dump_card_state(ahd);
+ panic("for safety");
+ }
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: SCB = %d Not Active!\n",
+ ahd_name(ahd), scbid);
+ panic("Waiting List traversal\n");
+ }
+ ahd_set_scbptr(ahd, scbid);
+ next = ahd_inw_scbram(ahd, SCB_NEXT);
+ if (ahd_match_scb(ahd, scb, target, channel,
+ lun, SCB_LIST_NULL, role) == 0) {
+ prev = scbid;
+ continue;
+ }
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahd_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahd_set_transaction_status(scb, status);
+ cstat = ahd_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahd_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in Waiting List\n");
+ ahd_done(ahd, scb);
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ ahd_rem_wscb(ahd, scbid, prev, next, tid);
+ if (prev == SCB_LIST_NULL)
+ *list_head = next;
+ break;
+ case SEARCH_PRINT:
+ printf("0x%x ", scbid);
+ case SEARCH_COUNT:
+ prev = scbid;
+ break;
+ }
+ if (found > AHD_SCB_MAX)
+ panic("SCB LIST LOOP");
+ }
+ if (action == SEARCH_COMPLETE
+ || action == SEARCH_REMOVE)
+ ahd_outw(ahd, CMDS_PENDING, ahd_inw(ahd, CMDS_PENDING) - found);
+ return (found);
+}
+
+static void
+ahd_stitch_tid_list(struct ahd_softc *ahd, u_int tid_prev,
+ u_int tid_cur, u_int tid_next)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+
+ if (SCBID_IS_NULL(tid_cur)) {
+
+ /* Bypass current TID list */
+ if (SCBID_IS_NULL(tid_prev)) {
+ ahd_outw(ahd, WAITING_TID_HEAD, tid_next);
+ } else {
+ ahd_set_scbptr(ahd, tid_prev);
+ ahd_outw(ahd, SCB_NEXT2, tid_next);
+ }
+ if (SCBID_IS_NULL(tid_next))
+ ahd_outw(ahd, WAITING_TID_TAIL, tid_prev);
+ } else {
+
+ /* Stitch through tid_cur */
+ if (SCBID_IS_NULL(tid_prev)) {
+ ahd_outw(ahd, WAITING_TID_HEAD, tid_cur);
+ } else {
+ ahd_set_scbptr(ahd, tid_prev);
+ ahd_outw(ahd, SCB_NEXT2, tid_cur);
+ }
+ ahd_set_scbptr(ahd, tid_cur);
+ ahd_outw(ahd, SCB_NEXT2, tid_next);
+
+ if (SCBID_IS_NULL(tid_next))
+ ahd_outw(ahd, WAITING_TID_TAIL, tid_cur);
+ }
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+ u_int prev, u_int next, u_int tid)
+{
+ u_int tail_offset;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ if (!SCBID_IS_NULL(prev)) {
+ ahd_set_scbptr(ahd, prev);
+ ahd_outw(ahd, SCB_NEXT, next);
+ }
+
+ /*
+ * SCBs that had MK_MESSAGE set in them will not
+ * be queued to the per-target lists, so don't
+ * blindly clear the tail pointer.
+ */
+ tail_offset = WAITING_SCB_TAILS + (2 * tid);
+ if (SCBID_IS_NULL(next)
+ && ahd_inw(ahd, tail_offset) == scbid)
+ ahd_outw(ahd, tail_offset, prev);
+ ahd_add_scb_to_free_list(ahd, scbid);
+ return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs. This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahd_add_scb_to_free_list(struct ahd_softc *ahd, u_int scbid)
+{
+/* XXX Need some other mechanism to designate "free". */
+ /*
+ * Invalidate the tag so that our abort
+ * routines don't think it's active.
+ ahd_outb(ahd, SCB_TAG, SCB_LIST_NULL);
+ */
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ struct scb *scbp;
+ struct scb *scbp_next;
+ u_int i, j;
+ u_int maxtarget;
+ u_int minlun;
+ u_int maxlun;
+ int found;
+ ahd_mode_state saved_modes;
+
+ /* restore this when we're done */
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ found = ahd_search_qinfifo(ahd, target, channel, lun, SCB_LIST_NULL,
+ role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ /*
+ * Clean out the busy target table for any untagged commands.
+ */
+ i = 0;
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+
+ if (lun == CAM_LUN_WILDCARD) {
+ minlun = 0;
+ maxlun = AHD_NUM_LUNS_NONPKT;
+ } else if (lun >= AHD_NUM_LUNS_NONPKT) {
+ minlun = maxlun = 0;
+ } else {
+ minlun = lun;
+ maxlun = lun + 1;
+ }
+
+ if (role != ROLE_TARGET) {
+ for (;i < maxtarget; i++) {
+ for (j = minlun;j < maxlun; j++) {
+ u_int scbid;
+ u_int tcl;
+
+ tcl = BUILD_TCL_RAW(i, 'A', j);
+ scbid = ahd_find_busy_tcl(ahd, tcl);
+ scbp = ahd_lookup_scb(ahd, scbid);
+ if (scbp == NULL
+ || ahd_match_scb(ahd, scbp, target, channel,
+ lun, tag, role) == 0)
+ continue;
+ ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(i, 'A', j));
+ }
+ }
+ }
+
+ /*
+ * Don't abort commands that have already completed,
+ * but haven't quite made it up to the host yet.
+ */
+ ahd_flush_qoutfifo(ahd);
+
+ /*
+ * Go through the pending CCB list and look for
+ * commands for this target that are still active.
+ * These are other tagged commands that were
+ * disconnected when the reset occurred.
+ */
+ scbp_next = LIST_FIRST(&ahd->pending_scbs);
+ while (scbp_next != NULL) {
+ scbp = scbp_next;
+ scbp_next = LIST_NEXT(scbp, pending_links);
+ if (ahd_match_scb(ahd, scbp, target, channel, lun, tag, role)) {
+ cam_status ostat;
+
+ ostat = ahd_get_transaction_status(scbp);
+ if (ostat == CAM_REQ_INPROG)
+ ahd_set_transaction_status(scbp, status);
+ if (ahd_get_transaction_status(scbp) != CAM_REQ_CMP)
+ ahd_freeze_scb(scbp);
+ if ((scbp->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB on pending list\n");
+ ahd_done(ahd, scbp);
+ found++;
+ }
+ }
+ ahd_restore_modes(ahd, saved_modes);
+ ahd_platform_abort_scbs(ahd, target, channel, lun, tag, role, status);
+ ahd->flags |= AHD_UPDATE_PEND_CMDS;
+ return found;
+}
+
+static void
+ahd_reset_current_bus(struct ahd_softc *ahd)
+{
+ uint8_t scsiseq;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~ENSCSIRST);
+ scsiseq = ahd_inb(ahd, SCSISEQ0) & ~(ENSELO|ENARBO|SCSIRSTO);
+ ahd_outb(ahd, SCSISEQ0, scsiseq | SCSIRSTO);
+ ahd_flush_device_writes(ahd);
+ ahd_delay(AHD_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahd_outb(ahd, SCSISEQ0, scsiseq);
+ ahd_flush_device_writes(ahd);
+ ahd_delay(AHD_BUSRESET_DELAY);
+ if ((ahd->bugs & AHD_SCSIRST_BUG) != 0) {
+ /*
+ * 2A Razor #474
+ * Certain chip state is not cleared for
+ * SCSI bus resets that we initiate, so
+ * we must reset the chip.
+ */
+ ahd_reset(ahd, /*reinit*/TRUE);
+ ahd_intr_enable(ahd, /*enable*/TRUE);
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ }
+
+ ahd_clear_intstat(ahd);
+}
+
+int
+ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
+{
+ struct ahd_devinfo devinfo;
+ u_int initiator;
+ u_int target;
+ u_int max_scsiid;
+ int found;
+ u_int fifo;
+ u_int next_fifo;
+
+ ahd->pending_device = NULL;
+
+ ahd_compile_devinfo(&devinfo,
+ CAM_TARGET_WILDCARD,
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahd_pause(ahd);
+
+ /* Make sure the sequencer is in a safe location. */
+ ahd_clear_critical_section(ahd);
+
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ ahd_run_tqinfifo(ahd, /*paused*/TRUE);
+ }
+#endif
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+ /*
+ * Disable selections so no automatic hardware
+ * functions will modify chip state.
+ */
+ ahd_outb(ahd, SCSISEQ0, 0);
+ ahd_outb(ahd, SCSISEQ1, 0);
+
+ /*
+ * Safely shut down our DMA engines. Always start with
+ * the FIFO that is not currently active (if any are
+ * actively connected).
+ */
+ next_fifo = fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+ if (next_fifo > CURRFIFO_1)
+ /* If disconneced, arbitrarily start with FIFO1. */
+ next_fifo = fifo = 0;
+ do {
+ next_fifo ^= CURRFIFO_1;
+ ahd_set_modes(ahd, next_fifo, next_fifo);
+ ahd_outb(ahd, DFCNTRL,
+ ahd_inb(ahd, DFCNTRL) & ~(SCSIEN|HDMAEN));
+ while ((ahd_inb(ahd, DFCNTRL) & HDMAENACK) != 0)
+ ahd_delay(10);
+ /*
+ * Set CURRFIFO to the now inactive channel.
+ */
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, DFFSTAT, next_fifo);
+ } while (next_fifo != fifo);
+
+ /*
+ * Reset the bus if we are initiating this reset
+ */
+ ahd_clear_msg_state(ahd);
+ ahd_outb(ahd, SIMODE1,
+ ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST|ENBUSFREE));
+
+ if (initiate_reset)
+ ahd_reset_current_bus(ahd);
+
+ ahd_clear_intstat(ahd);
+
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+ /*
+ * Cleanup anything left in the FIFOs.
+ */
+ ahd_clear_fifo(ahd, 0);
+ ahd_clear_fifo(ahd, 1);
+
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+ for (target = 0; target <= max_scsiid; target++) {
+
+ if (ahd->enabled_targets[target] == NULL)
+ continue;
+ for (initiator = 0; initiator <= max_scsiid; initiator++) {
+ struct ahd_devinfo devinfo;
+
+ ahd_compile_devinfo(&devinfo, target, initiator,
+ CAM_LUN_WILDCARD,
+ 'A', ROLE_UNKNOWN);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_CUR, /*paused*/TRUE);
+ ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHD_TRANS_CUR, /*paused*/TRUE);
+ }
+ }
+
+#ifdef AHD_TARGET_MODE
+ max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+
+ /*
+ * Send an immediate notify ccb to all target more peripheral
+ * drivers affected by this action.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+ struct ahd_tmode_tstate* tstate;
+ u_int lun;
+
+ tstate = ahd->enabled_targets[target];
+ if (tstate == NULL)
+ continue;
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahd_queue_lstate_event(ahd, lstate, CAM_TARGET_WILDCARD,
+ EVENT_TYPE_BUS_RESET, /*arg*/0);
+ ahd_send_lstate_events(ahd, lstate);
+ }
+ }
+#endif
+ /* Notify the XPT that a bus reset occurred */
+ ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+ ahd_restart(ahd);
+ /*
+ * Freeze the SIMQ until our poller can determine that
+ * the bus reset has really gone away. We set the initial
+ * timer to 0 to have the check performed as soon as possible
+ * from the timer context.
+ */
+ if ((ahd->flags & AHD_RESET_POLL_ACTIVE) == 0) {
+ ahd->flags |= AHD_RESET_POLL_ACTIVE;
+ ahd_freeze_simq(ahd);
+ ahd_timer_reset(&ahd->reset_timer, 0, ahd_reset_poll, ahd);
+ }
+ return (found);
+}
+
+
+#define AHD_RESET_POLL_US 1000
+static void
+ahd_reset_poll(void *arg)
+{
+ struct ahd_softc *ahd;
+ u_int scsiseq1;
+ u_long l;
+ u_long s;
+
+ ahd_list_lock(&l);
+ ahd = ahd_find_softc((struct ahd_softc *)arg);
+ if (ahd == NULL) {
+ printf("ahd_reset_poll: Instance %p no longer exists\n", arg);
+ ahd_list_unlock(&l);
+ return;
+ }
+ ahd_lock(ahd, &s);
+ ahd_pause(ahd);
+ ahd_update_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+ if ((ahd_inb(ahd, SSTAT1) & SCSIRSTI) != 0) {
+ ahd_timer_reset(&ahd->reset_timer, AHD_RESET_POLL_US,
+ ahd_reset_poll, ahd);
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ ahd_list_unlock(&l);
+ return;
+ }
+
+ /* Reset is now low. Complete chip reinitialization. */
+ ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
+ scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+ ahd_outb(ahd, SCSISEQ1, scsiseq1 & (ENSELI|ENRSELI|ENAUTOATNP));
+ ahd_unpause(ahd);
+ ahd->flags &= ~AHD_RESET_POLL_ACTIVE;
+ ahd_unlock(ahd, &s);
+ ahd_release_simq(ahd);
+ ahd_list_unlock(&l);
+}
+
+/**************************** Statistics Processing ***************************/
+static void
+ahd_stat_timer(void *arg)
+{
+ struct ahd_softc *ahd;
+ u_long l;
+ u_long s;
+ int enint_coal;
+
+ ahd_list_lock(&l);
+ ahd = ahd_find_softc((struct ahd_softc *)arg);
+ if (ahd == NULL) {
+ printf("ahd_stat_timer: Instance %p no longer exists\n", arg);
+ ahd_list_unlock(&l);
+ return;
+ }
+ ahd_lock(ahd, &s);
+
+ enint_coal = ahd->hs_mailbox & ENINT_COALESCE;
+ if (ahd->cmdcmplt_total > ahd->int_coalescing_threshold)
+ enint_coal |= ENINT_COALESCE;
+ else if (ahd->cmdcmplt_total < ahd->int_coalescing_stop_threshold)
+ enint_coal &= ~ENINT_COALESCE;
+
+ if (enint_coal != (ahd->hs_mailbox & ENINT_COALESCE)) {
+ ahd_enable_coalescing(ahd, enint_coal);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_INT_COALESCING) != 0)
+ printf("%s: Interrupt coalescing "
+ "now %sabled. Cmds %d\n",
+ ahd_name(ahd),
+ (enint_coal & ENINT_COALESCE) ? "en" : "dis",
+ ahd->cmdcmplt_total);
+#endif
+ }
+
+ ahd->cmdcmplt_bucket = (ahd->cmdcmplt_bucket+1) & (AHD_STAT_BUCKETS-1);
+ ahd->cmdcmplt_total -= ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket];
+ ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket] = 0;
+ ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
+ ahd_stat_timer, ahd);
+ ahd_unlock(ahd, &s);
+ ahd_list_unlock(&l);
+}
+
+/****************************** Status Processing *****************************/
+void
+ahd_handle_scb_status(struct ahd_softc *ahd, struct scb *scb)
+{
+ if (scb->hscb->shared_data.istatus.scsi_status != 0) {
+ ahd_handle_scsi_status(ahd, scb);
+ } else {
+ ahd_calc_residual(ahd, scb);
+ ahd_done(ahd, scb);
+ }
+}
+
+void
+ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ u_int qfreeze_cnt;
+
+ /*
+ * The sequencer freezes its select-out queue
+ * anytime a SCSI status error occurs. We must
+ * handle the error and decrement the QFREEZE count
+ * to allow the sequencer to continue.
+ */
+ hscb = scb->hscb;
+
+ /* Freeze the queue until the client sees the error. */
+ ahd_freeze_devq(ahd, scb);
+ ahd_freeze_scb(scb);
+ qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
+ if (qfreeze_cnt == 0) {
+ printf("%s: Bad status with 0 qfreeze count!\n", ahd_name(ahd));
+ } else {
+ qfreeze_cnt--;
+ ahd_outw(ahd, QFREEZE_COUNT, qfreeze_cnt);
+ }
+ if (qfreeze_cnt == 0)
+ ahd_outb(ahd, SEQ_FLAGS2,
+ ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
+
+ /* Don't want to clobber the original sense code */
+ if ((scb->flags & SCB_SENSE) != 0) {
+ /*
+ * Clear the SCB_SENSE Flag and perform
+ * a normal command completion.
+ */
+ scb->flags &= ~SCB_SENSE;
+ ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ ahd_done(ahd, scb);
+ return;
+ }
+ ahd_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+ ahd_set_scsi_status(scb, hscb->shared_data.istatus.scsi_status);
+ switch (hscb->shared_data.istatus.scsi_status) {
+ case STATUS_PKT_SENSE:
+ {
+ struct scsi_status_iu_header *siu;
+
+ ahd_sync_sense(ahd, scb, BUS_DMASYNC_POSTREAD);
+ siu = (struct scsi_status_iu_header *)scb->sense_data;
+ ahd_set_scsi_status(scb, siu->status);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SENSE) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("SCB 0x%x Received PKT Status of 0x%x\n",
+ SCB_GET_TAG(scb), siu->status);
+ printf("\tflags = 0x%x, sense len = 0x%x, "
+ "pktfail = 0x%x\n",
+ siu->flags, scsi_4btoul(siu->sense_length),
+ scsi_4btoul(siu->pkt_failures_length));
+ }
+#endif
+ if ((siu->flags & SIU_RSPVALID) != 0) {
+ ahd_print_path(ahd, scb);
+ if (scsi_4btoul(siu->pkt_failures_length) < 4) {
+ printf("Unable to parse pkt_failures\n");
+ } else {
+
+ switch (SIU_PKTFAIL_CODE(siu)) {
+ case SIU_PFC_NONE:
+ printf("No packet failure found\n");
+ break;
+ case SIU_PFC_CIU_FIELDS_INVALID:
+ printf("Invalid Command IU Field\n");
+ break;
+ case SIU_PFC_TMF_NOT_SUPPORTED:
+ printf("TMF not supportd\n");
+ break;
+ case SIU_PFC_TMF_FAILED:
+ printf("TMF failed\n");
+ break;
+ case SIU_PFC_INVALID_TYPE_CODE:
+ printf("Invalid L_Q Type code\n");
+ break;
+ case SIU_PFC_ILLEGAL_REQUEST:
+ printf("Illegal request\n");
+ default:
+ break;
+ }
+ }
+ if (siu->status == SCSI_STATUS_OK)
+ ahd_set_transaction_status(scb,
+ CAM_REQ_CMP_ERR);
+ }
+ if ((siu->flags & SIU_SNSVALID) != 0) {
+ scb->flags |= SCB_PKT_SENSE;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SENSE) != 0)
+ printf("Sense data available\n");
+#endif
+ }
+ ahd_done(ahd, scb);
+ break;
+ }
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ {
+ struct ahd_devinfo devinfo;
+ struct ahd_dma_seg *sg;
+ struct scsi_sense *sc;
+ struct ahd_initiator_tinfo *targ_info;
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_transinfo *tinfo;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ ahd_print_path(ahd, scb);
+ printf("SCB %d: requests Check Status\n",
+ SCB_GET_TAG(scb));
+ }
+#endif
+
+ if (ahd_perform_autosense(scb) == 0)
+ break;
+
+ ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahd, scb),
+ SCB_GET_LUN(scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ ROLE_INITIATOR);
+ targ_info = ahd_fetch_transinfo(ahd,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ sg = scb->sg_list;
+ sc = (struct scsi_sense *)hscb->shared_data.idata.cdb;
+ /*
+ * Save off the residual if there is one.
+ */
+ ahd_update_residual(ahd, scb);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ ahd_print_path(ahd, scb);
+ printf("Sending Sense\n");
+ }
+#endif
+ scb->sg_count = 0;
+ sg = ahd_sg_setup(ahd, scb, sg, ahd_get_sense_bufaddr(ahd, scb),
+ ahd_get_sense_bufsize(ahd, scb),
+ /*last*/TRUE);
+ sc->opcode = REQUEST_SENSE;
+ sc->byte2 = 0;
+ if (tinfo->protocol_version <= SCSI_REV_2
+ && SCB_GET_LUN(scb) < 8)
+ sc->byte2 = SCB_GET_LUN(scb) << 5;
+ sc->unused[0] = 0;
+ sc->unused[1] = 0;
+ sc->length = ahd_get_sense_bufsize(ahd, scb);
+ sc->control = 0;
+
+ /*
+ * We can't allow the target to disconnect.
+ * This will be an untagged transaction and
+ * having the target disconnect will make this
+ * transaction indestinguishable from outstanding
+ * tagged transactions.
+ */
+ hscb->control = 0;
+
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate. Unit attention
+ * errors will be reported before any data
+ * phases occur.
+ */
+ if (ahd_get_residual(scb) == ahd_get_transfer_length(scb)) {
+ ahd_update_neg_request(ahd, &devinfo,
+ tstate, targ_info,
+ AHD_NEG_IF_NON_ASYNC);
+ }
+ if (tstate->auto_negotiate & devinfo.target_mask) {
+ hscb->control |= MK_MESSAGE;
+ scb->flags &=
+ ~(SCB_NEGOTIATE|SCB_ABORT|SCB_DEVICE_RESET);
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ }
+ hscb->cdb_len = sizeof(*sc);
+ ahd_setup_data_scb(ahd, scb);
+ scb->flags |= SCB_SENSE;
+ ahd_queue_scb(ahd, scb);
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ ahd_scb_timer_reset(scb, 5 * 1000000);
+ break;
+ }
+ case SCSI_STATUS_OK:
+ printf("%s: Interrupted for staus of 0???\n",
+ ahd_name(ahd));
+ /* FALLTHROUGH */
+ default:
+ ahd_done(ahd, scb);
+ break;
+ }
+}
+
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ struct initiator_status *spkt;
+ uint32_t sgptr;
+ uint32_t resid_sgptr;
+ uint32_t resid;
+
+ /*
+ * 5 cases.
+ * 1) No residual.
+ * SG_STATUS_VALID clear in sgptr.
+ * 2) Transferless command
+ * 3) Never performed any transfers.
+ * sgptr has SG_FULL_RESID set.
+ * 4) No residual but target did not
+ * save data pointers after the
+ * last transfer, so sgptr was
+ * never updated.
+ * 5) We have a partial residual.
+ * Use residual_sgptr to determine
+ * where we are.
+ */
+
+ hscb = scb->hscb;
+ sgptr = ahd_le32toh(hscb->sgptr);
+ if ((sgptr & SG_STATUS_VALID) == 0)
+ /* Case 1 */
+ return;
+ sgptr &= ~SG_STATUS_VALID;
+
+ if ((sgptr & SG_LIST_NULL) != 0)
+ /* Case 2 */
+ return;
+
+ /*
+ * Residual fields are the same in both
+ * target and initiator status packets,
+ * so we can always use the initiator fields
+ * regardless of the role for this SCB.
+ */
+ spkt = &hscb->shared_data.istatus;
+ resid_sgptr = ahd_le32toh(spkt->residual_sgptr);
+ if ((sgptr & SG_FULL_RESID) != 0) {
+ /* Case 3 */
+ resid = ahd_get_transfer_length(scb);
+ } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+ /* Case 4 */
+ return;
+ } else if ((resid_sgptr & SG_OVERRUN_RESID) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("data overrun detected Tag == 0x%x.\n",
+ SCB_GET_TAG(scb));
+ ahd_freeze_devq(ahd, scb);
+ ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ ahd_freeze_scb(scb);
+ return;
+ } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+ panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+ /* NOTREACHED */
+ } else {
+ struct ahd_dma_seg *sg;
+
+ /*
+ * Remainder of the SG where the transfer
+ * stopped.
+ */
+ resid = ahd_le32toh(spkt->residual_datacnt) & AHD_SG_LEN_MASK;
+ sg = ahd_sg_bus_to_virt(ahd, scb, resid_sgptr & SG_PTR_MASK);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ while ((ahd_le32toh(sg->len) & AHD_DMA_LAST_SEG) == 0) {
+ sg++;
+ resid += ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+ }
+ }
+ if ((scb->flags & SCB_SENSE) == 0)
+ ahd_set_residual(scb, resid);
+ else
+ ahd_set_sense_residual(scb, resid);
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Handled %sResidual of %d bytes\n",
+ (scb->flags & SCB_SENSE) ? "Sense " : "", resid);
+ }
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHD_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahd_queue_lstate_event(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate,
+ u_int initiator_id, u_int event_type, u_int event_arg)
+{
+ struct ahd_tmode_event *event;
+ int pending;
+
+ xpt_freeze_devq(lstate->path, /*count*/1);
+ if (lstate->event_w_idx >= lstate->event_r_idx)
+ pending = lstate->event_w_idx - lstate->event_r_idx;
+ else
+ pending = AHD_TMODE_EVENT_BUFFER_SIZE + 1
+ - (lstate->event_r_idx - lstate->event_w_idx);
+
+ if (event_type == EVENT_TYPE_BUS_RESET
+ || event_type == MSG_BUS_DEV_RESET) {
+ /*
+ * Any earlier events are irrelevant, so reset our buffer.
+ * This has the effect of allowing us to deal with reset
+ * floods (an external device holding down the reset line)
+ * without losing the event that is really interesting.
+ */
+ lstate->event_r_idx = 0;
+ lstate->event_w_idx = 0;
+ xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+ }
+
+ if (pending == AHD_TMODE_EVENT_BUFFER_SIZE) {
+ xpt_print_path(lstate->path);
+ printf("immediate event %x:%x lost\n",
+ lstate->event_buffer[lstate->event_r_idx].event_type,
+ lstate->event_buffer[lstate->event_r_idx].event_arg);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+ }
+
+ event = &lstate->event_buffer[lstate->event_w_idx];
+ event->initiator_id = initiator_id;
+ event->event_type = event_type;
+ event->event_arg = event_arg;
+ lstate->event_w_idx++;
+ if (lstate->event_w_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahd_send_lstate_events(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate)
+{
+ struct ccb_hdr *ccbh;
+ struct ccb_immed_notify *inot;
+
+ while (lstate->event_r_idx != lstate->event_w_idx
+ && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+ struct ahd_tmode_event *event;
+
+ event = &lstate->event_buffer[lstate->event_r_idx];
+ SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+ inot = (struct ccb_immed_notify *)ccbh;
+ switch (event->event_type) {
+ case EVENT_TYPE_BUS_RESET:
+ ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+ break;
+ default:
+ ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+ inot->message_args[0] = event->event_type;
+ inot->message_args[1] = event->event_arg;
+ break;
+ }
+ inot->initiator_id = event->initiator_id;
+ inot->sense_len = 0;
+ xpt_done((union ccb *)inot);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ }
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHD_DUMP_SEQ
+void
+ahd_dumpseq(struct ahd_softc* ahd)
+{
+ int i;
+ int max_prog;
+
+ max_prog = 2048;
+
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahd_outb(ahd, PRGMCNT, 0);
+ ahd_outb(ahd, PRGMCNT+1, 0);
+ for (i = 0; i < max_prog; i++) {
+ uint8_t ins_bytes[4];
+
+ ahd_insb(ahd, SEQRAM, ins_bytes, 4);
+ printf("0x%08x\n", ins_bytes[0] << 24
+ | ins_bytes[1] << 16
+ | ins_bytes[2] << 8
+ | ins_bytes[3]);
+ }
+}
+#endif
+
+static void
+ahd_loadseq(struct ahd_softc *ahd)
+{
+ struct cs cs_table[num_critical_sections];
+ u_int begin_set[num_critical_sections];
+ u_int end_set[num_critical_sections];
+ struct patch *cur_patch;
+ u_int cs_count;
+ u_int cur_cs;
+ u_int i;
+ int downloaded;
+ u_int skip_addr;
+ u_int sg_prefetch_cnt;
+ u_int sg_prefetch_cnt_limit;
+ u_int sg_prefetch_align;
+ u_int sg_size;
+ uint8_t download_consts[DOWNLOAD_CONST_COUNT];
+
+ if (bootverbose)
+ printf("%s: Downloading Sequencer Program...",
+ ahd_name(ahd));
+
+#if DOWNLOAD_CONST_COUNT != 7
+#error "Download Const Mismatch"
+#endif
+ /*
+ * Start out with 0 critical sections
+ * that apply to this firmware load.
+ */
+ cs_count = 0;
+ cur_cs = 0;
+ memset(begin_set, 0, sizeof(begin_set));
+ memset(end_set, 0, sizeof(end_set));
+
+ /*
+ * Setup downloadable constant table.
+ *
+ * The computation for the S/G prefetch variables is
+ * a bit complicated. We would like to always fetch
+ * in terms of cachelined sized increments. However,
+ * if the cacheline is not an even multiple of the
+ * SG element size or is larger than our SG RAM, using
+ * just the cache size might leave us with only a portion
+ * of an SG element at the tail of a prefetch. If the
+ * cacheline is larger than our S/G prefetch buffer less
+ * the size of an SG element, we may round down to a cacheline
+ * that doesn't contain any or all of the S/G of interest
+ * within the bounds of our S/G ram. Provide variables to
+ * the sequencer that will allow it to handle these edge
+ * cases.
+ */
+ /* Start by aligning to the nearest cacheline. */
+ sg_prefetch_align = ahd->pci_cachesize;
+ if (sg_prefetch_align == 0)
+ sg_prefetch_align = 8;
+ /* Round down to the nearest power of 2. */
+ while (powerof2(sg_prefetch_align) == 0)
+ sg_prefetch_align--;
+ /*
+ * If the cacheline boundary is greater than half our prefetch RAM
+ * we risk not being able to fetch even a single complete S/G
+ * segment if we align to that boundary.
+ */
+ if (sg_prefetch_align > CCSGADDR_MAX/2)
+ sg_prefetch_align = CCSGADDR_MAX/2;
+ /* Start by fetching a single cacheline. */
+ sg_prefetch_cnt = sg_prefetch_align;
+ /*
+ * Increment the prefetch count by cachelines until
+ * at least one S/G element will fit.
+ */
+ sg_size = sizeof(struct ahd_dma_seg);
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ sg_size = sizeof(struct ahd_dma64_seg);
+ while (sg_prefetch_cnt < sg_size)
+ sg_prefetch_cnt += sg_prefetch_align;
+ /*
+ * If the cacheline is not an even multiple of
+ * the S/G size, we may only get a partial S/G when
+ * we align. Add a cacheline if this is the case.
+ */
+ if ((sg_prefetch_align % sg_size) != 0
+ && (sg_prefetch_cnt < CCSGADDR_MAX))
+ sg_prefetch_cnt += sg_prefetch_align;
+ /*
+ * Lastly, compute a value that the sequencer can use
+ * to determine if the remainder of the CCSGRAM buffer
+ * has a full S/G element in it.
+ */
+ sg_prefetch_cnt_limit = -(sg_prefetch_cnt - sg_size + 1);
+ download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+ download_consts[SG_PREFETCH_CNT_LIMIT] = sg_prefetch_cnt_limit;
+ download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_align - 1);
+ download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_align - 1);
+ download_consts[SG_SIZEOF] = sg_size;
+ download_consts[PKT_OVERRUN_BUFOFFSET] =
+ (ahd->overrun_buf - (uint8_t *)ahd->qoutfifo) / 256;
+ download_consts[SCB_TRANSFER_SIZE] = SCB_TRANSFER_SIZE_1BYTE_LUN;
+ cur_patch = patches;
+ downloaded = 0;
+ skip_addr = 0;
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahd_outb(ahd, PRGMCNT, 0);
+ ahd_outb(ahd, PRGMCNT+1, 0);
+
+ for (i = 0; i < sizeof(seqprog)/4; i++) {
+ if (ahd_check_patch(ahd, &cur_patch, i, &skip_addr) == 0) {
+ /*
+ * Don't download this instruction as it
+ * is in a patch that was removed.
+ */
+ continue;
+ }
+ /*
+ * Move through the CS table until we find a CS
+ * that might apply to this instruction.
+ */
+ for (; cur_cs < num_critical_sections; cur_cs++) {
+ if (critical_sections[cur_cs].end <= i) {
+ if (begin_set[cs_count] == TRUE
+ && end_set[cs_count] == FALSE) {
+ cs_table[cs_count].end = downloaded;
+ end_set[cs_count] = TRUE;
+ cs_count++;
+ }
+ continue;
+ }
+ if (critical_sections[cur_cs].begin <= i
+ && begin_set[cs_count] == FALSE) {
+ cs_table[cs_count].begin = downloaded;
+ begin_set[cs_count] = TRUE;
+ }
+ break;
+ }
+ ahd_download_instr(ahd, i, download_consts);
+ downloaded++;
+ }
+
+ ahd->num_critical_sections = cs_count;
+ if (cs_count != 0) {
+
+ cs_count *= sizeof(struct cs);
+ ahd->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+ if (ahd->critical_sections == NULL)
+ panic("ahd_loadseq: Could not malloc");
+ memcpy(ahd->critical_sections, cs_table, cs_count);
+ }
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE);
+
+ if (bootverbose) {
+ printf(" %d instructions downloaded\n", downloaded);
+ printf("%s: Features 0x%x, Bugs 0x%x, Flags 0x%x\n",
+ ahd_name(ahd), ahd->features, ahd->bugs, ahd->flags);
+ }
+}
+
+static int
+ahd_check_patch(struct ahd_softc *ahd, struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr)
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ u_int num_patches;
+
+ num_patches = sizeof(patches)/sizeof(struct patch);
+ last_patch = &patches[num_patches];
+ cur_patch = *start_patch;
+
+ while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+ if (cur_patch->patch_func(ahd) == 0) {
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+static u_int
+ahd_resolve_seqaddr(struct ahd_softc *ahd, u_int address)
+{
+ struct patch *cur_patch;
+ int address_offset;
+ u_int skip_addr;
+ u_int i;
+
+ address_offset = 0;
+ cur_patch = patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;) {
+
+ ahd_check_patch(ahd, &cur_patch, i, &skip_addr);
+
+ if (skip_addr > i) {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ } else {
+ i++;
+ }
+ }
+ return (address - address_offset);
+}
+
+static void
+ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ u_int opcode;
+
+ /*
+ * The firmware is always compiled into a little endian format.
+ */
+ instr.integer = ahd_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode) {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ fmt3_ins = &instr.format3;
+ fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address);
+ /* FALLTHROUGH */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0) {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ /* FALLTHROUGH */
+ case AIC_OP_ROL:
+ {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for (i = 0, count = 0; i < 31; i++) {
+ uint32_t mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if ((count & 0x01) == 0)
+ instr.format1.parity = 1;
+
+ /* The sequencer is a little endian cpu */
+ instr.integer = ahd_htole32(instr.integer);
+ ahd_outsb(ahd, SEQRAM, instr.bytes, 4);
+ break;
+ }
+ default:
+ panic("Unknown opcode encountered in seq program");
+ break;
+ }
+}
+
+static int
+ahd_probe_stack_size(struct ahd_softc *ahd)
+{
+ int last_probe;
+
+ last_probe = 0;
+ while (1) {
+ int i;
+
+ /*
+ * We avoid using 0 as a pattern to avoid
+ * confusion if the stack implementation
+ * "back-fills" with zeros when "poping'
+ * entries.
+ */
+ for (i = 1; i <= last_probe+1; i++) {
+ ahd_outb(ahd, STACK, i & 0xFF);
+ ahd_outb(ahd, STACK, (i >> 8) & 0xFF);
+ }
+
+ /* Verify */
+ for (i = last_probe+1; i > 0; i--) {
+ u_int stack_entry;
+
+ stack_entry = ahd_inb(ahd, STACK)
+ |(ahd_inb(ahd, STACK) << 8);
+ if (stack_entry != i)
+ goto sized;
+ }
+ last_probe++;
+ }
+sized:
+ return (last_probe);
+}
+
+void
+ahd_dump_all_cards_state(void)
+{
+ struct ahd_softc *list_ahd;
+
+ TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+ ahd_dump_card_state(list_ahd);
+ }
+}
+
+int
+ahd_print_register(ahd_reg_parse_entry_t *table, u_int num_entries,
+ const char *name, u_int address, u_int value,
+ u_int *cur_column, u_int wrap_point)
+{
+ int printed;
+ u_int printed_mask;
+
+ if (cur_column != NULL && *cur_column >= wrap_point) {
+ printf("\n");
+ *cur_column = 0;
+ }
+ printed = printf("%s[0x%x]", name, value);
+ if (table == NULL) {
+ printed += printf(" ");
+ *cur_column += printed;
+ return (printed);
+ }
+ printed_mask = 0;
+ while (printed_mask != 0xFF) {
+ int entry;
+
+ for (entry = 0; entry < num_entries; entry++) {
+ if (((value & table[entry].mask)
+ != table[entry].value)
+ || ((printed_mask & table[entry].mask)
+ == table[entry].mask))
+ continue;
+
+ printed += printf("%s%s",
+ printed_mask == 0 ? ":(" : "|",
+ table[entry].name);
+ printed_mask |= table[entry].mask;
+
+ break;
+ }
+ if (entry >= num_entries)
+ break;
+ }
+ if (printed_mask != 0)
+ printed += printf(") ");
+ else
+ printed += printf(" ");
+ if (cur_column != NULL)
+ *cur_column += printed;
+ return (printed);
+}
+
+void
+ahd_dump_card_state(struct ahd_softc *ahd)
+{
+ struct scb *scb;
+ ahd_mode_state saved_modes;
+ u_int dffstat;
+ int paused;
+ u_int scb_index;
+ u_int saved_scb_index;
+ u_int cur_col;
+ int i;
+
+ if (ahd_is_paused(ahd)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahd_pause(ahd);
+ }
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ printf(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
+ "%s: Dumping Card State at program address 0x%x Mode 0x%x\n",
+ ahd_name(ahd),
+ ahd_inb(ahd, CURADDR) | (ahd_inb(ahd, CURADDR+1) << 8),
+ ahd_build_mode_state(ahd, ahd->saved_src_mode,
+ ahd->saved_dst_mode));
+ if (paused)
+ printf("Card was paused\n");
+
+ if (ahd_check_cmdcmpltqueues(ahd))
+ printf("Completions are pending\n");
+
+ /*
+ * Mode independent registers.
+ */
+ cur_col = 0;
+ ahd_hs_mailbox_print(ahd_inb(ahd, LOCAL_HS_MAILBOX), &cur_col, 50);
+ ahd_intctl_print(ahd_inb(ahd, INTCTL), &cur_col, 50);
+ ahd_seqintstat_print(ahd_inb(ahd, SEQINTSTAT), &cur_col, 50);
+ ahd_saved_mode_print(ahd_inb(ahd, SAVED_MODE), &cur_col, 50);
+ ahd_dffstat_print(ahd_inb(ahd, DFFSTAT), &cur_col, 50);
+ ahd_scsisigi_print(ahd_inb(ahd, SCSISIGI), &cur_col, 50);
+ ahd_scsiphase_print(ahd_inb(ahd, SCSIPHASE), &cur_col, 50);
+ ahd_scsibus_print(ahd_inb(ahd, SCSIBUS), &cur_col, 50);
+ ahd_lastphase_print(ahd_inb(ahd, LASTPHASE), &cur_col, 50);
+ ahd_scsiseq0_print(ahd_inb(ahd, SCSISEQ0), &cur_col, 50);
+ ahd_scsiseq1_print(ahd_inb(ahd, SCSISEQ1), &cur_col, 50);
+ ahd_seqctl0_print(ahd_inb(ahd, SEQCTL0), &cur_col, 50);
+ ahd_seqintctl_print(ahd_inb(ahd, SEQINTCTL), &cur_col, 50);
+ ahd_seq_flags_print(ahd_inb(ahd, SEQ_FLAGS), &cur_col, 50);
+ ahd_seq_flags2_print(ahd_inb(ahd, SEQ_FLAGS2), &cur_col, 50);
+ ahd_sstat0_print(ahd_inb(ahd, SSTAT0), &cur_col, 50);
+ ahd_sstat1_print(ahd_inb(ahd, SSTAT1), &cur_col, 50);
+ ahd_sstat2_print(ahd_inb(ahd, SSTAT2), &cur_col, 50);
+ ahd_sstat3_print(ahd_inb(ahd, SSTAT3), &cur_col, 50);
+ ahd_perrdiag_print(ahd_inb(ahd, PERRDIAG), &cur_col, 50);
+ ahd_simode1_print(ahd_inb(ahd, SIMODE1), &cur_col, 50);
+ ahd_lqistat0_print(ahd_inb(ahd, LQISTAT0), &cur_col, 50);
+ ahd_lqistat1_print(ahd_inb(ahd, LQISTAT1), &cur_col, 50);
+ ahd_lqistat2_print(ahd_inb(ahd, LQISTAT2), &cur_col, 50);
+ ahd_lqostat0_print(ahd_inb(ahd, LQOSTAT0), &cur_col, 50);
+ ahd_lqostat1_print(ahd_inb(ahd, LQOSTAT1), &cur_col, 50);
+ ahd_lqostat2_print(ahd_inb(ahd, LQOSTAT2), &cur_col, 50);
+ printf("\n");
+ printf("\nSCB Count = %d CMDS_PENDING = %d LASTSCB 0x%x "
+ "CURRSCB 0x%x NEXTSCB 0x%x\n",
+ ahd->scb_data.numscbs, ahd_inw(ahd, CMDS_PENDING),
+ ahd_inw(ahd, LASTSCB), ahd_inw(ahd, CURRSCB),
+ ahd_inw(ahd, NEXTSCB));
+ cur_col = 0;
+ /* QINFIFO */
+ ahd_search_qinfifo(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, /*status*/0, SEARCH_PRINT);
+ saved_scb_index = ahd_get_scbptr(ahd);
+ printf("Pending list:");
+ i = 0;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ cur_col = printf("\n%3d FIFO_USE[0x%x] ", SCB_GET_TAG(scb),
+ ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT));
+ ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
+ ahd_scb_control_print(ahd_inb_scbram(ahd, SCB_CONTROL),
+ &cur_col, 60);
+ ahd_scb_scsiid_print(ahd_inb_scbram(ahd, SCB_SCSIID),
+ &cur_col, 60);
+ }
+ printf("\nTotal %d\n", i);
+
+ printf("Kernel Free SCB list: ");
+ i = 0;
+ TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+ struct scb *list_scb;
+
+ list_scb = scb;
+ do {
+ printf("%d ", SCB_GET_TAG(list_scb));
+ list_scb = LIST_NEXT(list_scb, collision_links);
+ } while (list_scb && i++ < AHD_SCB_MAX);
+ }
+
+ LIST_FOREACH(scb, &ahd->scb_data.any_dev_free_scb_list, links.le) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ printf("%d ", SCB_GET_TAG(scb));
+ }
+ printf("\n");
+
+ printf("Sequencer Complete DMA-inprog list: ");
+ scb_index = ahd_inw(ahd, COMPLETE_SCB_DMAINPROG_HEAD);
+ i = 0;
+ while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+ ahd_set_scbptr(ahd, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+ }
+ printf("\n");
+
+ printf("Sequencer Complete list: ");
+ scb_index = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+ i = 0;
+ while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+ ahd_set_scbptr(ahd, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+ }
+ printf("\n");
+
+
+ printf("Sequencer DMA-Up and Complete list: ");
+ scb_index = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+ i = 0;
+ while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+ ahd_set_scbptr(ahd, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+ }
+ printf("\n");
+ ahd_set_scbptr(ahd, saved_scb_index);
+ dffstat = ahd_inb(ahd, DFFSTAT);
+ for (i = 0; i < 2; i++) {
+#ifdef AHD_DEBUG
+ struct scb *fifo_scb;
+#endif
+ u_int fifo_scbptr;
+
+ ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+ fifo_scbptr = ahd_get_scbptr(ahd);
+ printf("\n%s: FIFO%d %s, LONGJMP == 0x%x, SCB 0x%x\n",
+ ahd_name(ahd), i,
+ (dffstat & (FIFO0FREE << i)) ? "Free" : "Active",
+ ahd_inw(ahd, LONGJMP_ADDR), fifo_scbptr);
+ cur_col = 0;
+ ahd_seqimode_print(ahd_inb(ahd, SEQIMODE), &cur_col, 50);
+ ahd_seqintsrc_print(ahd_inb(ahd, SEQINTSRC), &cur_col, 50);
+ ahd_dfcntrl_print(ahd_inb(ahd, DFCNTRL), &cur_col, 50);
+ ahd_dfstatus_print(ahd_inb(ahd, DFSTATUS), &cur_col, 50);
+ ahd_sg_cache_shadow_print(ahd_inb(ahd, SG_CACHE_SHADOW),
+ &cur_col, 50);
+ ahd_sg_state_print(ahd_inb(ahd, SG_STATE), &cur_col, 50);
+ ahd_dffsxfrctl_print(ahd_inb(ahd, DFFSXFRCTL), &cur_col, 50);
+ ahd_soffcnt_print(ahd_inb(ahd, SOFFCNT), &cur_col, 50);
+ ahd_mdffstat_print(ahd_inb(ahd, MDFFSTAT), &cur_col, 50);
+ if (cur_col > 50) {
+ printf("\n");
+ cur_col = 0;
+ }
+ cur_col += printf("SHADDR = 0x%x%x, SHCNT = 0x%x ",
+ ahd_inl(ahd, SHADDR+4),
+ ahd_inl(ahd, SHADDR),
+ (ahd_inb(ahd, SHCNT)
+ | (ahd_inb(ahd, SHCNT + 1) << 8)
+ | (ahd_inb(ahd, SHCNT + 2) << 16)));
+ if (cur_col > 50) {
+ printf("\n");
+ cur_col = 0;
+ }
+ cur_col += printf("HADDR = 0x%x%x, HCNT = 0x%x ",
+ ahd_inl(ahd, HADDR+4),
+ ahd_inl(ahd, HADDR),
+ (ahd_inb(ahd, HCNT)
+ | (ahd_inb(ahd, HCNT + 1) << 8)
+ | (ahd_inb(ahd, HCNT + 2) << 16)));
+ ahd_ccsgctl_print(ahd_inb(ahd, CCSGCTL), &cur_col, 50);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_SG) != 0) {
+ fifo_scb = ahd_lookup_scb(ahd, fifo_scbptr);
+ if (fifo_scb != NULL)
+ ahd_dump_sglist(fifo_scb);
+ }
+#endif
+ }
+ printf("\nLQIN: ");
+ for (i = 0; i < 20; i++)
+ printf("0x%x ", ahd_inb(ahd, LQIN + i));
+ printf("\n");
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ printf("%s: LQISTATE = 0x%x, LQOSTATE = 0x%x, OPTIONMODE = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, LQISTATE), ahd_inb(ahd, LQOSTATE),
+ ahd_inb(ahd, OPTIONMODE));
+ printf("%s: OS_SPACE_CNT = 0x%x MAXCMDCNT = 0x%x\n",
+ ahd_name(ahd), ahd_inb(ahd, OS_SPACE_CNT),
+ ahd_inb(ahd, MAXCMDCNT));
+ ahd_simode0_print(ahd_inb(ahd, SIMODE0), &cur_col, 50);
+ printf("\n");
+ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+ cur_col = 0;
+ ahd_ccscbctl_print(ahd_inb(ahd, CCSCBCTL), &cur_col, 50);
+ printf("\n");
+ ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+ printf("%s: REG0 == 0x%x, SINDEX = 0x%x, DINDEX = 0x%x\n",
+ ahd_name(ahd), ahd_inw(ahd, REG0), ahd_inw(ahd, SINDEX),
+ ahd_inw(ahd, DINDEX));
+ printf("%s: SCBPTR == 0x%x, SCB_NEXT == 0x%x, SCB_NEXT2 == 0x%x\n",
+ ahd_name(ahd), ahd_get_scbptr(ahd),
+ ahd_inw_scbram(ahd, SCB_NEXT),
+ ahd_inw_scbram(ahd, SCB_NEXT2));
+ printf("CDB %x %x %x %x %x %x\n",
+ ahd_inb_scbram(ahd, SCB_CDB_STORE),
+ ahd_inb_scbram(ahd, SCB_CDB_STORE+1),
+ ahd_inb_scbram(ahd, SCB_CDB_STORE+2),
+ ahd_inb_scbram(ahd, SCB_CDB_STORE+3),
+ ahd_inb_scbram(ahd, SCB_CDB_STORE+4),
+ ahd_inb_scbram(ahd, SCB_CDB_STORE+5));
+ printf("STACK:");
+ for (i = 0; i < ahd->stack_size; i++) {
+ ahd->saved_stack[i] =
+ ahd_inb(ahd, STACK)|(ahd_inb(ahd, STACK) << 8);
+ printf(" 0x%x", ahd->saved_stack[i]);
+ }
+ for (i = ahd->stack_size-1; i >= 0; i--) {
+ ahd_outb(ahd, STACK, ahd->saved_stack[i] & 0xFF);
+ ahd_outb(ahd, STACK, (ahd->saved_stack[i] >> 8) & 0xFF);
+ }
+ printf("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n");
+ ahd_platform_dump_card_state(ahd);
+ ahd_restore_modes(ahd, saved_modes);
+ if (paused == 0)
+ ahd_unpause(ahd);
+}
+
+void
+ahd_dump_scbs(struct ahd_softc *ahd)
+{
+ ahd_mode_state saved_modes;
+ u_int saved_scb_index;
+ int i;
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ saved_scb_index = ahd_get_scbptr(ahd);
+ for (i = 0; i < AHD_SCB_MAX; i++) {
+ ahd_set_scbptr(ahd, i);
+ printf("%3d", i);
+ printf("(CTRL 0x%x ID 0x%x N 0x%x N2 0x%x SG 0x%x, RSG 0x%x)\n",
+ ahd_inb_scbram(ahd, SCB_CONTROL),
+ ahd_inb_scbram(ahd, SCB_SCSIID),
+ ahd_inw_scbram(ahd, SCB_NEXT),
+ ahd_inw_scbram(ahd, SCB_NEXT2),
+ ahd_inl_scbram(ahd, SCB_SGPTR),
+ ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR));
+ }
+ printf("\n");
+ ahd_set_scbptr(ahd, saved_scb_index);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+/**************************** Flexport Logic **********************************/
+/*
+ * Read count 16bit words from 16bit word address start_addr from the
+ * SEEPROM attached to the controller, into buf, using the controller's
+ * SEEPROM reading state machine. Optionally treat the data as a byte
+ * stream in terms of byte order.
+ */
+int
+ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count, int bytestream)
+{
+ u_int cur_addr;
+ u_int end_addr;
+ int error;
+
+ /*
+ * If we never make it through the loop even once,
+ * we were passed invalid arguments.
+ */
+ error = EINVAL;
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ end_addr = start_addr + count;
+ for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+
+ ahd_outb(ahd, SEEADR, cur_addr);
+ ahd_outb(ahd, SEECTL, SEEOP_READ | SEESTART);
+
+ error = ahd_wait_seeprom(ahd);
+ if (error)
+ break;
+ if (bytestream != 0) {
+ uint8_t *bytestream_ptr;
+
+ bytestream_ptr = (uint8_t *)buf;
+ *bytestream_ptr++ = ahd_inb(ahd, SEEDAT);
+ *bytestream_ptr = ahd_inb(ahd, SEEDAT+1);
+ } else {
+ /*
+ * ahd_inw() already handles machine byte order.
+ */
+ *buf = ahd_inw(ahd, SEEDAT);
+ }
+ buf++;
+ }
+ return (error);
+}
+
+/*
+ * Write count 16bit words from buf, into SEEPROM attache to the
+ * controller starting at 16bit word address start_addr, using the
+ * controller's SEEPROM writing state machine.
+ */
+int
+ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ u_int cur_addr;
+ u_int end_addr;
+ int error;
+ int retval;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ error = ENOENT;
+
+ /* Place the chip into write-enable mode */
+ ahd_outb(ahd, SEEADR, SEEOP_EWEN_ADDR);
+ ahd_outb(ahd, SEECTL, SEEOP_EWEN | SEESTART);
+ error = ahd_wait_seeprom(ahd);
+ if (error)
+ return (error);
+
+ /*
+ * Write the data. If we don't get throught the loop at
+ * least once, the arguments were invalid.
+ */
+ retval = EINVAL;
+ end_addr = start_addr + count;
+ for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+ ahd_outw(ahd, SEEDAT, *buf++);
+ ahd_outb(ahd, SEEADR, cur_addr);
+ ahd_outb(ahd, SEECTL, SEEOP_WRITE | SEESTART);
+
+ retval = ahd_wait_seeprom(ahd);
+ if (retval)
+ break;
+ }
+
+ /*
+ * Disable writes.
+ */
+ ahd_outb(ahd, SEEADR, SEEOP_EWDS_ADDR);
+ ahd_outb(ahd, SEECTL, SEEOP_EWDS | SEESTART);
+ error = ahd_wait_seeprom(ahd);
+ if (error)
+ return (error);
+ return (retval);
+}
+
+/*
+ * Wait ~100us for the serial eeprom to satisfy our request.
+ */
+int
+ahd_wait_seeprom(struct ahd_softc *ahd)
+{
+ int cnt;
+
+ cnt = 20;
+ while ((ahd_inb(ahd, SEESTAT) & (SEEARBACK|SEEBUSY)) != 0 && --cnt)
+ ahd_delay(5);
+
+ if (cnt == 0)
+ return (ETIMEDOUT);
+ return (0);
+}
+
+/*
+ * Validate the two checksums in the per_channel
+ * vital product data struct.
+ */
+int
+ahd_verify_vpd_cksum(struct vpd_config *vpd)
+{
+ int i;
+ int maxaddr;
+ uint32_t checksum;
+ uint8_t *vpdarray;
+
+ vpdarray = (uint8_t *)vpd;
+ maxaddr = offsetof(struct vpd_config, vpd_checksum);
+ checksum = 0;
+ for (i = offsetof(struct vpd_config, resource_type); i < maxaddr; i++)
+ checksum = checksum + vpdarray[i];
+ if (checksum == 0
+ || (-checksum & 0xFF) != vpd->vpd_checksum)
+ return (0);
+
+ checksum = 0;
+ maxaddr = offsetof(struct vpd_config, checksum);
+ for (i = offsetof(struct vpd_config, default_target_flags);
+ i < maxaddr; i++)
+ checksum = checksum + vpdarray[i];
+ if (checksum == 0
+ || (-checksum & 0xFF) != vpd->checksum)
+ return (0);
+ return (1);
+}
+
+int
+ahd_verify_cksum(struct seeprom_config *sc)
+{
+ int i;
+ int maxaddr;
+ uint32_t checksum;
+ uint16_t *scarray;
+
+ maxaddr = (sizeof(*sc)/2) - 1;
+ checksum = 0;
+ scarray = (uint16_t *)sc;
+
+ for (i = 0; i < maxaddr; i++)
+ checksum = checksum + scarray[i];
+ if (checksum == 0
+ || (checksum & 0xFFFF) != sc->checksum) {
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+int
+ahd_acquire_seeprom(struct ahd_softc *ahd)
+{
+ /*
+ * We should be able to determine the SEEPROM type
+ * from the flexport logic, but unfortunately not
+ * all implementations have this logic and there is
+ * no programatic method for determining if the logic
+ * is present.
+ */
+ return (1);
+#if 0
+ uint8_t seetype;
+ int error;
+
+ error = ahd_read_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, &seetype);
+ if (error != 0
+ || ((seetype & FLX_ROMSTAT_SEECFG) == FLX_ROMSTAT_SEE_NONE))
+ return (0);
+ return (1);
+#endif
+}
+
+void
+ahd_release_seeprom(struct ahd_softc *ahd)
+{
+ /* Currently a no-op */
+}
+
+int
+ahd_write_flexport(struct ahd_softc *ahd, u_int addr, u_int value)
+{
+ int error;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ if (addr > 7)
+ panic("ahd_write_flexport: address out of range");
+ ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+ error = ahd_wait_flexport(ahd);
+ if (error != 0)
+ return (error);
+ ahd_outb(ahd, BRDDAT, value);
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, BRDCTL, BRDSTB|BRDEN|(addr << 3));
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+ ahd_flush_device_writes(ahd);
+ ahd_outb(ahd, BRDCTL, 0);
+ ahd_flush_device_writes(ahd);
+ return (0);
+}
+
+int
+ahd_read_flexport(struct ahd_softc *ahd, u_int addr, uint8_t *value)
+{
+ int error;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ if (addr > 7)
+ panic("ahd_read_flexport: address out of range");
+ ahd_outb(ahd, BRDCTL, BRDRW|BRDEN|(addr << 3));
+ error = ahd_wait_flexport(ahd);
+ if (error != 0)
+ return (error);
+ *value = ahd_inb(ahd, BRDDAT);
+ ahd_outb(ahd, BRDCTL, 0);
+ ahd_flush_device_writes(ahd);
+ return (0);
+}
+
+/*
+ * Wait at most 2 seconds for flexport arbitration to succeed.
+ */
+int
+ahd_wait_flexport(struct ahd_softc *ahd)
+{
+ int cnt;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+ cnt = 1000000 * 2 / 5;
+ while ((ahd_inb(ahd, BRDCTL) & FLXARBACK) == 0 && --cnt)
+ ahd_delay(5);
+
+ if (cnt == 0)
+ return (ETIMEDOUT);
+ return (0);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHD_TARGET_MODE
+cam_status
+ahd_find_tmode_devs(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb,
+ struct ahd_tmode_tstate **tstate,
+ struct ahd_tmode_lstate **lstate,
+ int notfound_failure)
+{
+
+ if ((ahd->features & AHD_TARGETMODE) == 0)
+ return (CAM_REQ_INVALID);
+
+ /*
+ * Handle the 'black hole' device that sucks up
+ * requests to unattached luns on enabled targets.
+ */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+ && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *tstate = NULL;
+ *lstate = ahd->black_hole;
+ } else {
+ u_int max_id;
+
+ max_id = (ahd->features & AHD_WIDE) ? 15 : 7;
+ if (ccb->ccb_h.target_id > max_id)
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun >= AHD_NUM_LUNS)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahd->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate =
+ (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ }
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+void
+ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
+{
+#if NOT_YET
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ u_int target;
+ u_int lun;
+ u_int target_mask;
+ u_long s;
+ char channel;
+
+ status = ahd_find_tmode_devs(ahd, sim, ccb, &tstate, &lstate,
+ /*notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ if ((ahd->features & AHD_MULTIROLE) != 0) {
+ u_int our_id;
+
+ our_id = ahd->our_id;
+ if (ccb->ccb_h.target_id != our_id) {
+ if ((ahd->features & AHD_MULTI_TID) != 0
+ && (ahd->flags & AHD_INITIATORROLE) != 0) {
+ /*
+ * Only allow additional targets if
+ * the initiator role is disabled.
+ * The hardware cannot handle a re-select-in
+ * on the initiator id during a re-select-out
+ * on a different target id.
+ */
+ status = CAM_TID_INVALID;
+ } else if ((ahd->flags & AHD_INITIATORROLE) != 0
+ || ahd->enabled_luns > 0) {
+ /*
+ * Only allow our target id to change
+ * if the initiator role is not configured
+ * and there are no enabled luns which
+ * are attached to the currently registered
+ * scsi id.
+ */
+ status = CAM_TID_INVALID;
+ }
+ }
+ }
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ /*
+ * We now have an id that is valid.
+ * If we aren't in target mode, switch modes.
+ */
+ if ((ahd->flags & AHD_TARGETROLE) == 0
+ && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ u_long s;
+
+ printf("Configuring Target Mode\n");
+ ahd_lock(ahd, &s);
+ if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+ ccb->ccb_h.status = CAM_BUSY;
+ ahd_unlock(ahd, &s);
+ return;
+ }
+ ahd->flags |= AHD_TARGETROLE;
+ if ((ahd->features & AHD_MULTIROLE) == 0)
+ ahd->flags &= ~AHD_INITIATORROLE;
+ ahd_pause(ahd);
+ ahd_loadseq(ahd);
+ ahd_restart(ahd);
+ ahd_unlock(ahd, &s);
+ }
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ channel = SIM_CHANNEL(ahd, sim);
+ target_mask = 0x01 << target;
+ if (channel == 'B')
+ target_mask <<= 8;
+
+ if (cel->enable != 0) {
+ u_int scsiseq1;
+
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun already enabled\n");
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ printf("Non-zero Group Codes\n");
+ return;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+ tstate = ahd_alloc_tstate(ahd, target, channel);
+ if (tstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate tstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate lstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ memset(lstate, 0, sizeof(*lstate));
+ status = xpt_create_path(&lstate->path, /*periph*/NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (status != CAM_REQ_CMP) {
+ free(lstate, M_DEVBUF);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate path\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ ahd_lock(ahd, &s);
+ ahd_pause(ahd);
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = lstate;
+ ahd->enabled_luns++;
+
+ if ((ahd->features & AHD_MULTI_TID) != 0) {
+ u_int targid_mask;
+
+ targid_mask = ahd_inb(ahd, TARGID)
+ | (ahd_inb(ahd, TARGID + 1) << 8);
+
+ targid_mask |= target_mask;
+ ahd_outb(ahd, TARGID, targid_mask);
+ ahd_outb(ahd, TARGID+1, (targid_mask >> 8));
+
+ ahd_update_scsiid(ahd, targid_mask);
+ } else {
+ u_int our_id;
+ char channel;
+
+ channel = SIM_CHANNEL(ahd, sim);
+ our_id = SIM_SCSI_ID(ahd, sim);
+
+ /*
+ * This can only happen if selections
+ * are not enabled
+ */
+ if (target != our_id) {
+ u_int sblkctl;
+ char cur_channel;
+ int swap;
+
+ sblkctl = ahd_inb(ahd, SBLKCTL);
+ cur_channel = (sblkctl & SELBUSB)
+ ? 'B' : 'A';
+ if ((ahd->features & AHD_TWIN) == 0)
+ cur_channel = 'A';
+ swap = cur_channel != channel;
+ ahd->our_id = target;
+
+ if (swap)
+ ahd_outb(ahd, SBLKCTL,
+ sblkctl ^ SELBUSB);
+
+ ahd_outb(ahd, SCSIID, target);
+
+ if (swap)
+ ahd_outb(ahd, SBLKCTL, sblkctl);
+ }
+ }
+ } else
+ ahd->black_hole = lstate;
+ /* Allow select-in operations */
+ if (ahd->black_hole != NULL && ahd->enabled_luns > 0) {
+ scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+ scsiseq1 |= ENSELI;
+ ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+ scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+ scsiseq1 |= ENSELI;
+ ahd_outb(ahd, SCSISEQ1, scsiseq1);
+ }
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ } else {
+ struct scb *scb;
+ int i, empty;
+
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+
+ ahd_lock(ahd, &s);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+ struct ccb_hdr *ccbh;
+
+ ccbh = &scb->io_ctx->ccb_h;
+ if (ccbh->func_code == XPT_CONT_TARGET_IO
+ && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+ printf("CTIO pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ ahd_unlock(ahd, &s);
+ return;
+ }
+ }
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ahd_unlock(ahd, &s);
+ return;
+ }
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+
+ ahd_pause(ahd);
+ /* Can we clean up the target too? */
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = NULL;
+ ahd->enabled_luns--;
+ for (empty = 1, i = 0; i < 8; i++)
+ if (tstate->enabled_luns[i] != NULL) {
+ empty = 0;
+ break;
+ }
+
+ if (empty) {
+ ahd_free_tstate(ahd, target, channel,
+ /*force*/FALSE);
+ if (ahd->features & AHD_MULTI_TID) {
+ u_int targid_mask;
+
+ targid_mask = ahd_inb(ahd, TARGID)
+ | (ahd_inb(ahd, TARGID + 1)
+ << 8);
+
+ targid_mask &= ~target_mask;
+ ahd_outb(ahd, TARGID, targid_mask);
+ ahd_outb(ahd, TARGID+1,
+ (targid_mask >> 8));
+ ahd_update_scsiid(ahd, targid_mask);
+ }
+ }
+ } else {
+
+ ahd->black_hole = NULL;
+
+ /*
+ * We can't allow selections without
+ * our black hole device.
+ */
+ empty = TRUE;
+ }
+ if (ahd->enabled_luns == 0) {
+ /* Disallow select-in */
+ u_int scsiseq1;
+
+ scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+ scsiseq1 &= ~ENSELI;
+ ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+ scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+ scsiseq1 &= ~ENSELI;
+ ahd_outb(ahd, SCSISEQ1, scsiseq1);
+
+ if ((ahd->features & AHD_MULTIROLE) == 0) {
+ printf("Configuring Initiator Mode\n");
+ ahd->flags &= ~AHD_TARGETROLE;
+ ahd->flags |= AHD_INITIATORROLE;
+ ahd_pause(ahd);
+ ahd_loadseq(ahd);
+ ahd_restart(ahd);
+ /*
+ * Unpaused. The extra unpause
+ * that follows is harmless.
+ */
+ }
+ }
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ }
+#endif
+}
+
+static void
+ahd_update_scsiid(struct ahd_softc *ahd, u_int targid_mask)
+{
+#if NOT_YET
+ u_int scsiid_mask;
+ u_int scsiid;
+
+ if ((ahd->features & AHD_MULTI_TID) == 0)
+ panic("ahd_update_scsiid called on non-multitid unit\n");
+
+ /*
+ * Since we will rely on the TARGID mask
+ * for selection enables, ensure that OID
+ * in SCSIID is not set to some other ID
+ * that we don't want to allow selections on.
+ */
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ scsiid = ahd_inb(ahd, SCSIID_ULTRA2);
+ else
+ scsiid = ahd_inb(ahd, SCSIID);
+ scsiid_mask = 0x1 << (scsiid & OID);
+ if ((targid_mask & scsiid_mask) == 0) {
+ u_int our_id;
+
+ /* ffs counts from 1 */
+ our_id = ffs(targid_mask);
+ if (our_id == 0)
+ our_id = ahd->our_id;
+ else
+ our_id--;
+ scsiid &= TID;
+ scsiid |= our_id;
+ }
+ if ((ahd->features & AHD_ULTRA2) != 0)
+ ahd_outb(ahd, SCSIID_ULTRA2, scsiid);
+ else
+ ahd_outb(ahd, SCSIID, scsiid);
+#endif
+}
+
+void
+ahd_run_tqinfifo(struct ahd_softc *ahd, int paused)
+{
+ struct target_cmd *cmd;
+
+ ahd_sync_tqinfifo(ahd, BUS_DMASYNC_POSTREAD);
+ while ((cmd = &ahd->targetcmds[ahd->tqinfifonext])->cmd_valid != 0) {
+
+ /*
+ * Only advance through the queue if we
+ * have the resources to process the command.
+ */
+ if (ahd_handle_target_cmd(ahd, cmd) != 0)
+ break;
+
+ cmd->cmd_valid = 0;
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ ahd_targetcmd_offset(ahd, ahd->tqinfifonext),
+ sizeof(struct target_cmd),
+ BUS_DMASYNC_PREREAD);
+ ahd->tqinfifonext++;
+
+ /*
+ * Lazily update our position in the target mode incoming
+ * command queue as seen by the sequencer.
+ */
+ if ((ahd->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+ u_int hs_mailbox;
+
+ hs_mailbox = ahd_inb(ahd, HS_MAILBOX);
+ hs_mailbox &= ~HOST_TQINPOS;
+ hs_mailbox |= ahd->tqinfifonext & HOST_TQINPOS;
+ ahd_outb(ahd, HS_MAILBOX, hs_mailbox);
+ }
+ }
+}
+
+static int
+ahd_handle_target_cmd(struct ahd_softc *ahd, struct target_cmd *cmd)
+{
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_tmode_lstate *lstate;
+ struct ccb_accept_tio *atio;
+ uint8_t *byte;
+ int initiator;
+ int target;
+ int lun;
+
+ initiator = SCSIID_TARGET(ahd, cmd->scsiid);
+ target = SCSIID_OUR_ID(cmd->scsiid);
+ lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+ byte = cmd->bytes;
+ tstate = ahd->enabled_targets[target];
+ lstate = NULL;
+ if (tstate != NULL)
+ lstate = tstate->enabled_luns[lun];
+
+ /*
+ * Commands for disabled luns go to the black hole driver.
+ */
+ if (lstate == NULL)
+ lstate = ahd->black_hole;
+
+ atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ if (atio == NULL) {
+ ahd->flags |= AHD_TQINFIFO_BLOCKED;
+ /*
+ * Wait for more ATIOs from the peripheral driver for this lun.
+ */
+ return (1);
+ } else
+ ahd->flags &= ~AHD_TQINFIFO_BLOCKED;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+ printf("Incoming command from %d for %d:%d%s\n",
+ initiator, target, lun,
+ lstate == ahd->black_hole ? "(Black Holed)" : "");
+#endif
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+ if (lstate == ahd->black_hole) {
+ /* Fill in the wildcards */
+ atio->ccb_h.target_id = target;
+ atio->ccb_h.target_lun = lun;
+ }
+
+ /*
+ * Package it up and send it off to
+ * whomever has this lun enabled.
+ */
+ atio->sense_len = 0;
+ atio->init_id = initiator;
+ if (byte[0] != 0xFF) {
+ /* Tag was included */
+ atio->tag_action = *byte++;
+ atio->tag_id = *byte++;
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ } else {
+ atio->ccb_h.flags = 0;
+ }
+ byte++;
+
+ /* Okay. Now determine the cdb size based on the command code */
+ switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+
+ memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+ /*
+ * We weren't allowed to disconnect.
+ * We're hanging on the bus until a
+ * continue target I/O comes in response
+ * to this accept tio.
+ */
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+ printf("Received Immediate Command %d:%d:%d - %p\n",
+ initiator, target, lun, ahd->pending_device);
+#endif
+ ahd->pending_device = lstate;
+ ahd_freeze_ccb((union ccb *)atio);
+ atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+ }
+ xpt_done((union ccb*)atio);
+ return (0);
+}
+
+#endif
diff --git a/drivers/scsi/aic7xxx/aic79xx_inline.h b/drivers/scsi/aic7xxx/aic79xx_inline.h
new file mode 100644
index 000000000000..d80bc5161fb1
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_inline.h
@@ -0,0 +1,965 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_inline.h#51 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_INLINE_H_
+#define _AIC79XX_INLINE_H_
+
+/******************************** Debugging ***********************************/
+static __inline char *ahd_name(struct ahd_softc *ahd);
+
+static __inline char *
+ahd_name(struct ahd_softc *ahd)
+{
+ return (ahd->name);
+}
+
+/************************ Sequencer Execution Control *************************/
+static __inline void ahd_known_modes(struct ahd_softc *ahd,
+ ahd_mode src, ahd_mode dst);
+static __inline ahd_mode_state ahd_build_mode_state(struct ahd_softc *ahd,
+ ahd_mode src,
+ ahd_mode dst);
+static __inline void ahd_extract_mode_state(struct ahd_softc *ahd,
+ ahd_mode_state state,
+ ahd_mode *src, ahd_mode *dst);
+static __inline void ahd_set_modes(struct ahd_softc *ahd, ahd_mode src,
+ ahd_mode dst);
+static __inline void ahd_update_modes(struct ahd_softc *ahd);
+static __inline void ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+ ahd_mode dstmode, const char *file,
+ int line);
+static __inline ahd_mode_state ahd_save_modes(struct ahd_softc *ahd);
+static __inline void ahd_restore_modes(struct ahd_softc *ahd,
+ ahd_mode_state state);
+static __inline int ahd_is_paused(struct ahd_softc *ahd);
+static __inline void ahd_pause(struct ahd_softc *ahd);
+static __inline void ahd_unpause(struct ahd_softc *ahd);
+
+static __inline void
+ahd_known_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+ ahd->src_mode = src;
+ ahd->dst_mode = dst;
+ ahd->saved_src_mode = src;
+ ahd->saved_dst_mode = dst;
+}
+
+static __inline ahd_mode_state
+ahd_build_mode_state(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+ return ((src << SRC_MODE_SHIFT) | (dst << DST_MODE_SHIFT));
+}
+
+static __inline void
+ahd_extract_mode_state(struct ahd_softc *ahd, ahd_mode_state state,
+ ahd_mode *src, ahd_mode *dst)
+{
+ *src = (state & SRC_MODE) >> SRC_MODE_SHIFT;
+ *dst = (state & DST_MODE) >> DST_MODE_SHIFT;
+}
+
+static __inline void
+ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+ if (ahd->src_mode == src && ahd->dst_mode == dst)
+ return;
+#ifdef AHD_DEBUG
+ if (ahd->src_mode == AHD_MODE_UNKNOWN
+ || ahd->dst_mode == AHD_MODE_UNKNOWN)
+ panic("Setting mode prior to saving it.\n");
+ if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+ printf("%s: Setting mode 0x%x\n", ahd_name(ahd),
+ ahd_build_mode_state(ahd, src, dst));
+#endif
+ ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst));
+ ahd->src_mode = src;
+ ahd->dst_mode = dst;
+}
+
+static __inline void
+ahd_update_modes(struct ahd_softc *ahd)
+{
+ ahd_mode_state mode_ptr;
+ ahd_mode src;
+ ahd_mode dst;
+
+ mode_ptr = ahd_inb(ahd, MODE_PTR);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+ printf("Reading mode 0x%x\n", mode_ptr);
+#endif
+ ahd_extract_mode_state(ahd, mode_ptr, &src, &dst);
+ ahd_known_modes(ahd, src, dst);
+}
+
+static __inline void
+ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+ ahd_mode dstmode, const char *file, int line)
+{
+#ifdef AHD_DEBUG
+ if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0
+ || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) {
+ panic("%s:%s:%d: Mode assertion failed.\n",
+ ahd_name(ahd), file, line);
+ }
+#endif
+}
+
+static __inline ahd_mode_state
+ahd_save_modes(struct ahd_softc *ahd)
+{
+ if (ahd->src_mode == AHD_MODE_UNKNOWN
+ || ahd->dst_mode == AHD_MODE_UNKNOWN)
+ ahd_update_modes(ahd);
+
+ return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode));
+}
+
+static __inline void
+ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state)
+{
+ ahd_mode src;
+ ahd_mode dst;
+
+ ahd_extract_mode_state(ahd, state, &src, &dst);
+ ahd_set_modes(ahd, src, dst);
+}
+
+#define AHD_ASSERT_MODES(ahd, source, dest) \
+ ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__);
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+ahd_is_paused(struct ahd_softc *ahd)
+{
+ return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop. The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register. The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+ahd_pause(struct ahd_softc *ahd)
+{
+ ahd_outb(ahd, HCNTRL, ahd->pause);
+
+ /*
+ * Since the sequencer can disable pausing in a critical section, we
+ * must loop until it actually stops.
+ */
+ while (ahd_is_paused(ahd) == 0)
+ ;
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted. If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+ahd_unpause(struct ahd_softc *ahd)
+{
+ /*
+ * Automatically restore our modes to those saved
+ * prior to the first change of the mode.
+ */
+ if (ahd->saved_src_mode != AHD_MODE_UNKNOWN
+ && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) {
+ if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0)
+ ahd_reset_cmds_pending(ahd);
+ ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+ }
+
+ if ((ahd_inb(ahd, INTSTAT) & ~CMDCMPLT) == 0)
+ ahd_outb(ahd, HCNTRL, ahd->unpause);
+
+ ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN);
+}
+
+/*********************** Scatter Gather List Handling *************************/
+static __inline void *ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+ void *sgptr, dma_addr_t addr,
+ bus_size_t len, int last);
+static __inline void ahd_setup_scb_common(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_setup_data_scb(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_setup_noxfer_scb(struct ahd_softc *ahd,
+ struct scb *scb);
+
+static __inline void *
+ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+ void *sgptr, dma_addr_t addr, bus_size_t len, int last)
+{
+ scb->sg_count++;
+ if (sizeof(dma_addr_t) > 4
+ && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = (struct ahd_dma64_seg *)sgptr;
+ sg->addr = ahd_htole64(addr);
+ sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0));
+ return (sg + 1);
+ } else {
+ struct ahd_dma_seg *sg;
+
+ sg = (struct ahd_dma_seg *)sgptr;
+ sg->addr = ahd_htole32(addr & 0xFFFFFFFF);
+ sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000)
+ | (last ? AHD_DMA_LAST_SEG : 0));
+ return (sg + 1);
+ }
+}
+
+static __inline void
+ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb)
+{
+ /* XXX Handle target mode SCBs. */
+ scb->crc_retry_count = 0;
+ if ((scb->flags & SCB_PACKETIZED) != 0) {
+ /* XXX what about ACA?? It is type 4, but TAG_TYPE == 0x3. */
+ scb->hscb->task_attribute = scb->hscb->control & SCB_TAG_TYPE;
+ } else {
+ if (ahd_get_transfer_length(scb) & 0x01)
+ scb->hscb->task_attribute = SCB_XFERLEN_ODD;
+ else
+ scb->hscb->task_attribute = 0;
+ }
+
+ if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR
+ || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0)
+ scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr =
+ ahd_htole32(scb->sense_busaddr);
+}
+
+static __inline void
+ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ /*
+ * Copy the first SG into the "current" data ponter area.
+ */
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+ struct ahd_dma64_seg *sg;
+
+ sg = (struct ahd_dma64_seg *)scb->sg_list;
+ scb->hscb->dataptr = sg->addr;
+ scb->hscb->datacnt = sg->len;
+ } else {
+ struct ahd_dma_seg *sg;
+ uint32_t *dataptr_words;
+
+ sg = (struct ahd_dma_seg *)scb->sg_list;
+ dataptr_words = (uint32_t*)&scb->hscb->dataptr;
+ dataptr_words[0] = sg->addr;
+ dataptr_words[1] = 0;
+ if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+ uint64_t high_addr;
+
+ high_addr = ahd_le32toh(sg->len) & 0x7F000000;
+ scb->hscb->dataptr |= ahd_htole64(high_addr << 8);
+ }
+ scb->hscb->datacnt = sg->len;
+ }
+ /*
+ * Note where to find the SG entries in bus space.
+ * We also set the full residual flag which the
+ * sequencer will clear as soon as a data transfer
+ * occurs.
+ */
+ scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID);
+}
+
+static __inline void
+ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL);
+ scb->hscb->dataptr = 0;
+ scb->hscb->datacnt = 0;
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline size_t ahd_sg_size(struct ahd_softc *ahd);
+static __inline void *
+ ahd_sg_bus_to_virt(struct ahd_softc *ahd,
+ struct scb *scb,
+ uint32_t sg_busaddr);
+static __inline uint32_t
+ ahd_sg_virt_to_bus(struct ahd_softc *ahd,
+ struct scb *scb,
+ void *sg);
+static __inline void ahd_sync_scb(struct ahd_softc *ahd,
+ struct scb *scb, int op);
+static __inline void ahd_sync_sglist(struct ahd_softc *ahd,
+ struct scb *scb, int op);
+static __inline void ahd_sync_sense(struct ahd_softc *ahd,
+ struct scb *scb, int op);
+static __inline uint32_t
+ ahd_targetcmd_offset(struct ahd_softc *ahd,
+ u_int index);
+
+static __inline size_t
+ahd_sg_size(struct ahd_softc *ahd)
+{
+ if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+ return (sizeof(struct ahd_dma64_seg));
+ return (sizeof(struct ahd_dma_seg));
+}
+
+static __inline void *
+ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr)
+{
+ dma_addr_t sg_offset;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd));
+ return ((uint8_t *)scb->sg_list + sg_offset);
+}
+
+static __inline uint32_t
+ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg)
+{
+ dma_addr_t sg_offset;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list)
+ - ahd_sg_size(ahd);
+
+ return (scb->sg_list_busaddr + sg_offset);
+}
+
+static __inline void
+ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+ ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat,
+ scb->hscb_map->dmamap,
+ /*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr,
+ /*len*/sizeof(*scb->hscb), op);
+}
+
+static __inline void
+ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+ if (scb->sg_count == 0)
+ return;
+
+ ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat,
+ scb->sg_map->dmamap,
+ /*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd),
+ /*len*/ahd_sg_size(ahd) * scb->sg_count, op);
+}
+
+static __inline void
+ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+ ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat,
+ scb->sense_map->dmamap,
+ /*offset*/scb->sense_busaddr,
+ /*len*/AHD_SENSE_BUFSIZE, op);
+}
+
+static __inline uint32_t
+ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index)
+{
+ return (((uint8_t *)&ahd->targetcmds[index])
+ - (uint8_t *)ahd->qoutfifo);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+static __inline void ahd_complete_scb(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_update_residual(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline struct ahd_initiator_tinfo *
+ ahd_fetch_transinfo(struct ahd_softc *ahd,
+ char channel, u_int our_id,
+ u_int remote_id,
+ struct ahd_tmode_tstate **tstate);
+static __inline uint16_t
+ ahd_inw(struct ahd_softc *ahd, u_int port);
+static __inline void ahd_outw(struct ahd_softc *ahd, u_int port,
+ u_int value);
+static __inline uint32_t
+ ahd_inl(struct ahd_softc *ahd, u_int port);
+static __inline void ahd_outl(struct ahd_softc *ahd, u_int port,
+ uint32_t value);
+static __inline uint64_t
+ ahd_inq(struct ahd_softc *ahd, u_int port);
+static __inline void ahd_outq(struct ahd_softc *ahd, u_int port,
+ uint64_t value);
+static __inline u_int ahd_get_scbptr(struct ahd_softc *ahd);
+static __inline void ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr);
+static __inline u_int ahd_get_hnscb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_hescb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_snscb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_sescb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_get_sdscb_qoff(struct ahd_softc *ahd);
+static __inline void ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int ahd_inb_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline u_int ahd_inw_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline uint32_t
+ ahd_inl_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline uint64_t
+ ahd_inq_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline void ahd_swap_with_next_hscb(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb);
+static __inline uint8_t *
+ ahd_get_sense_buf(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline uint32_t
+ ahd_get_sense_bufaddr(struct ahd_softc *ahd,
+ struct scb *scb);
+
+static __inline void
+ahd_complete_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahd_le32toh(scb->hscb->sgptr);
+ if ((sgptr & SG_STATUS_VALID) != 0)
+ ahd_handle_scb_status(ahd, scb);
+ else
+ ahd_done(ahd, scb);
+}
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahd_update_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahd_le32toh(scb->hscb->sgptr);
+ if ((sgptr & SG_STATUS_VALID) != 0)
+ ahd_calc_residual(ahd, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahd_initiator_tinfo *
+ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id,
+ u_int remote_id, struct ahd_tmode_tstate **tstate)
+{
+ /*
+ * Transfer data structures are stored from the perspective
+ * of the target role. Since the parameters for a connection
+ * in the initiator role to a given target are the same as
+ * when the roles are reversed, we pretend we are the target.
+ */
+ if (channel == 'B')
+ our_id += 8;
+ *tstate = ahd->enabled_targets[our_id];
+ return (&(*tstate)->transinfo[remote_id]);
+}
+
+#define AHD_COPY_COL_IDX(dst, src) \
+do { \
+ dst->hscb->scsiid = src->hscb->scsiid; \
+ dst->hscb->lun = src->hscb->lun; \
+} while (0)
+
+static __inline uint16_t
+ahd_inw(struct ahd_softc *ahd, u_int port)
+{
+ return ((ahd_inb(ahd, port+1) << 8) | ahd_inb(ahd, port));
+}
+
+static __inline void
+ahd_outw(struct ahd_softc *ahd, u_int port, u_int value)
+{
+ ahd_outb(ahd, port, value & 0xFF);
+ ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+}
+
+static __inline uint32_t
+ahd_inl(struct ahd_softc *ahd, u_int port)
+{
+ return ((ahd_inb(ahd, port))
+ | (ahd_inb(ahd, port+1) << 8)
+ | (ahd_inb(ahd, port+2) << 16)
+ | (ahd_inb(ahd, port+3) << 24));
+}
+
+static __inline void
+ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value)
+{
+ ahd_outb(ahd, port, (value) & 0xFF);
+ ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF);
+ ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF);
+ ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF);
+}
+
+static __inline uint64_t
+ahd_inq(struct ahd_softc *ahd, u_int port)
+{
+ return ((ahd_inb(ahd, port))
+ | (ahd_inb(ahd, port+1) << 8)
+ | (ahd_inb(ahd, port+2) << 16)
+ | (ahd_inb(ahd, port+3) << 24)
+ | (((uint64_t)ahd_inb(ahd, port+4)) << 32)
+ | (((uint64_t)ahd_inb(ahd, port+5)) << 40)
+ | (((uint64_t)ahd_inb(ahd, port+6)) << 48)
+ | (((uint64_t)ahd_inb(ahd, port+7)) << 56));
+}
+
+static __inline void
+ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value)
+{
+ ahd_outb(ahd, port, value & 0xFF);
+ ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+ ahd_outb(ahd, port+2, (value >> 16) & 0xFF);
+ ahd_outb(ahd, port+3, (value >> 24) & 0xFF);
+ ahd_outb(ahd, port+4, (value >> 32) & 0xFF);
+ ahd_outb(ahd, port+5, (value >> 40) & 0xFF);
+ ahd_outb(ahd, port+6, (value >> 48) & 0xFF);
+ ahd_outb(ahd, port+7, (value >> 56) & 0xFF);
+}
+
+static __inline u_int
+ahd_get_scbptr(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8));
+}
+
+static __inline void
+ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr)
+{
+ AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+ ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+ ahd_outb(ahd, SCBPTR, scbptr & 0xFF);
+ ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF);
+}
+
+static __inline u_int
+ahd_get_hnscb_qoff(struct ahd_softc *ahd)
+{
+ return (ahd_inw_atomic(ahd, HNSCB_QOFF));
+}
+
+static __inline void
+ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ ahd_outw_atomic(ahd, HNSCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_hescb_qoff(struct ahd_softc *ahd)
+{
+ return (ahd_inb(ahd, HESCB_QOFF));
+}
+
+static __inline void
+ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ ahd_outb(ahd, HESCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_snscb_qoff(struct ahd_softc *ahd)
+{
+ u_int oldvalue;
+
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ oldvalue = ahd_inw(ahd, SNSCB_QOFF);
+ ahd_outw(ahd, SNSCB_QOFF, oldvalue);
+ return (oldvalue);
+}
+
+static __inline void
+ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ ahd_outw(ahd, SNSCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_sescb_qoff(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ return (ahd_inb(ahd, SESCB_QOFF));
+}
+
+static __inline void
+ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ ahd_outb(ahd, SESCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_sdscb_qoff(struct ahd_softc *ahd)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8));
+}
+
+static __inline void
+ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+ AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+ ahd_outb(ahd, SDSCB_QOFF, value & 0xFF);
+ ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF);
+}
+
+static __inline u_int
+ahd_inb_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ u_int value;
+
+ /*
+ * Workaround PCI-X Rev A. hardware bug.
+ * After a host read of SCB memory, the chip
+ * may become confused into thinking prefetch
+ * was required. This starts the discard timer
+ * running and can cause an unexpected discard
+ * timer interrupt. The work around is to read
+ * a normal register prior to the exhaustion of
+ * the discard timer. The mode pointer register
+ * has no side effects and so serves well for
+ * this purpose.
+ *
+ * Razor #528
+ */
+ value = ahd_inb(ahd, offset);
+ if ((ahd->flags & AHD_PCIX_SCBRAM_RD_BUG) != 0)
+ ahd_inb(ahd, MODE_PTR);
+ return (value);
+}
+
+static __inline u_int
+ahd_inw_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ return (ahd_inb_scbram(ahd, offset)
+ | (ahd_inb_scbram(ahd, offset+1) << 8));
+}
+
+static __inline uint32_t
+ahd_inl_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ return (ahd_inw_scbram(ahd, offset)
+ | (ahd_inw_scbram(ahd, offset+2) << 16));
+}
+
+static __inline uint64_t
+ahd_inq_scbram(struct ahd_softc *ahd, u_int offset)
+{
+ return (ahd_inl_scbram(ahd, offset)
+ | ((uint64_t)ahd_inl_scbram(ahd, offset+4)) << 32);
+}
+
+static __inline struct scb *
+ahd_lookup_scb(struct ahd_softc *ahd, u_int tag)
+{
+ struct scb* scb;
+
+ if (tag >= AHD_SCB_MAX)
+ return (NULL);
+ scb = ahd->scb_data.scbindex[tag];
+ if (scb != NULL)
+ ahd_sync_scb(ahd, scb,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ return (scb);
+}
+
+static __inline void
+ahd_swap_with_next_hscb(struct ahd_softc *ahd, struct scb *scb)
+{
+ struct hardware_scb *q_hscb;
+ uint32_t saved_hscb_busaddr;
+
+ /*
+ * Our queuing method is a bit tricky. The card
+ * knows in advance which HSCB (by address) to download,
+ * and we can't disappoint it. To achieve this, the next
+ * HSCB to download is saved off in ahd->next_queued_hscb.
+ * When we are called to queue "an arbitrary scb",
+ * we copy the contents of the incoming HSCB to the one
+ * the sequencer knows about, swap HSCB pointers and
+ * finally assign the SCB to the tag indexed location
+ * in the scb_array. This makes sure that we can still
+ * locate the correct SCB by SCB_TAG.
+ */
+ q_hscb = ahd->next_queued_hscb;
+ saved_hscb_busaddr = q_hscb->hscb_busaddr;
+ memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+ q_hscb->hscb_busaddr = saved_hscb_busaddr;
+ q_hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+
+ /* Now swap HSCB pointers. */
+ ahd->next_queued_hscb = scb->hscb;
+ scb->hscb = q_hscb;
+
+ /* Now define the mapping from tag to SCB in the scbindex */
+ ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ ahd_swap_with_next_hscb(ahd, scb);
+
+ if (SCBID_IS_NULL(SCB_GET_TAG(scb)))
+ panic("Attempt to queue invalid SCB tag %x\n",
+ SCB_GET_TAG(scb));
+
+ /*
+ * Keep a history of SCBs we've downloaded in the qinfifo.
+ */
+ ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+ ahd->qinfifonext++;
+
+ if (scb->sg_count != 0)
+ ahd_setup_data_scb(ahd, scb);
+ else
+ ahd_setup_noxfer_scb(ahd, scb);
+ ahd_setup_scb_common(ahd, scb);
+
+ /*
+ * Make sure our data is consistent from the
+ * perspective of the adapter.
+ */
+ ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_QUEUE) != 0) {
+ uint64_t host_dataptr;
+
+ host_dataptr = ahd_le64toh(scb->hscb->dataptr);
+ printf("%s: Queueing SCB 0x%x bus addr 0x%x - 0x%x%x/0x%x\n",
+ ahd_name(ahd),
+ SCB_GET_TAG(scb), ahd_le32toh(scb->hscb->hscb_busaddr),
+ (u_int)((host_dataptr >> 32) & 0xFFFFFFFF),
+ (u_int)(host_dataptr & 0xFFFFFFFF),
+ ahd_le32toh(scb->hscb->datacnt));
+ }
+#endif
+ /* Tell the adapter about the newly queued SCB */
+ ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+}
+
+static __inline uint8_t *
+ahd_get_sense_buf(struct ahd_softc *ahd, struct scb *scb)
+{
+ return (scb->sense_data);
+}
+
+static __inline uint32_t
+ahd_get_sense_bufaddr(struct ahd_softc *ahd, struct scb *scb)
+{
+ return (scb->sense_busaddr);
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void ahd_sync_qoutfifo(struct ahd_softc *ahd, int op);
+static __inline void ahd_sync_tqinfifo(struct ahd_softc *ahd, int op);
+static __inline u_int ahd_check_cmdcmpltqueues(struct ahd_softc *ahd);
+static __inline int ahd_intr(struct ahd_softc *ahd);
+
+static __inline void
+ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
+{
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+ /*offset*/0, /*len*/AHC_SCB_MAX * sizeof(uint16_t), op);
+}
+
+static __inline void
+ahd_sync_tqinfifo(struct ahd_softc *ahd, int op)
+{
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0) {
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ ahd_targetcmd_offset(ahd, 0),
+ sizeof(struct target_cmd) * AHD_TMODE_CMDS,
+ op);
+ }
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHD_RUN_QOUTFIFO 0x1
+#define AHD_RUN_TQINFIFO 0x2
+static __inline u_int
+ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
+{
+ u_int retval;
+
+ retval = 0;
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+ /*offset*/ahd->qoutfifonext, /*len*/2,
+ BUS_DMASYNC_POSTREAD);
+ if ((ahd->qoutfifo[ahd->qoutfifonext]
+ & QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag)
+ retval |= AHD_RUN_QOUTFIFO;
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0
+ && (ahd->flags & AHD_TQINFIFO_BLOCKED) == 0) {
+ ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+ ahd->shared_data_dmamap,
+ ahd_targetcmd_offset(ahd, ahd->tqinfifofnext),
+ /*len*/sizeof(struct target_cmd),
+ BUS_DMASYNC_POSTREAD);
+ if (ahd->targetcmds[ahd->tqinfifonext].cmd_valid != 0)
+ retval |= AHD_RUN_TQINFIFO;
+ }
+#endif
+ return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline int
+ahd_intr(struct ahd_softc *ahd)
+{
+ u_int intstat;
+
+ if ((ahd->pause & INTEN) == 0) {
+ /*
+ * Our interrupt is not enabled on the chip
+ * and may be disabled for re-entrancy reasons,
+ * so just return. This is likely just a shared
+ * interrupt.
+ */
+ return (0);
+ }
+
+ /*
+ * Instead of directly reading the interrupt status register,
+ * infer the cause of the interrupt by checking our in-core
+ * completion queues. This avoids a costly PCI bus read in
+ * most cases.
+ */
+ if ((ahd->flags & AHD_ALL_INTERRUPTS) == 0
+ && (ahd_check_cmdcmpltqueues(ahd) != 0))
+ intstat = CMDCMPLT;
+ else
+ intstat = ahd_inb(ahd, INTSTAT);
+
+ if ((intstat & INT_PEND) == 0)
+ return (0);
+
+ if (intstat & CMDCMPLT) {
+ ahd_outb(ahd, CLRINT, CLRCMDINT);
+
+ /*
+ * Ensure that the chip sees that we've cleared
+ * this interrupt before we walk the output fifo.
+ * Otherwise, we may, due to posted bus writes,
+ * clear the interrupt after we finish the scan,
+ * and after the sequencer has added new entries
+ * and asserted the interrupt again.
+ */
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ if (ahd_is_paused(ahd)) {
+ /*
+ * Potentially lost SEQINT.
+ * If SEQINTCODE is non-zero,
+ * simulate the SEQINT.
+ */
+ if (ahd_inb(ahd, SEQINTCODE) != NO_SEQINT)
+ intstat |= SEQINT;
+ }
+ } else {
+ ahd_flush_device_writes(ahd);
+ }
+ ahd_run_qoutfifo(ahd);
+ ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]++;
+ ahd->cmdcmplt_total++;
+#ifdef AHD_TARGET_MODE
+ if ((ahd->flags & AHD_TARGETROLE) != 0)
+ ahd_run_tqinfifo(ahd, /*paused*/FALSE);
+#endif
+ }
+
+ /*
+ * Handle statuses that may invalidate our cached
+ * copy of INTSTAT separately.
+ */
+ if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0) {
+ /* Hot eject. Do nothing */
+ } else if (intstat & HWERRINT) {
+ ahd_handle_hwerrint(ahd);
+ } else if ((intstat & (PCIINT|SPLTINT)) != 0) {
+ ahd->bus_intr(ahd);
+ } else {
+
+ if ((intstat & SEQINT) != 0)
+ ahd_handle_seqint(ahd, intstat);
+
+ if ((intstat & SCSIINT) != 0)
+ ahd_handle_scsiint(ahd, intstat);
+ }
+ return (1);
+}
+
+#endif /* _AIC79XX_INLINE_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
new file mode 100644
index 000000000000..fb2877c303f0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -0,0 +1,5017 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#171 $
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 1997-1999 Doug Ledford
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include <scsi/scsicam.h>
+
+/*
+ * Include aiclib.c as part of our
+ * "module dependencies are hard" work around.
+ */
+#include "aiclib.c"
+
+#include <linux/init.h> /* __setup */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#include "sd.h" /* For geometry detection */
+#endif
+
+#include <linux/mm.h> /* For fetching system memory size */
+#include <linux/delay.h> /* For ssleep/msleep */
+
+/*
+ * Lock protecting manipulation of the ahd softc list.
+ */
+spinlock_t ahd_list_spinlock;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/* For dynamic sglist size calculation. */
+u_int ahd_linux_nseg;
+#endif
+
+/*
+ * Bucket size for counting good commands in between bad ones.
+ */
+#define AHD_LINUX_ERR_THRESH 1000
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC79XX_RESET_DELAY_MS
+#define AIC79XX_RESET_DELAY CONFIG_AIC79XX_RESET_DELAY_MS
+#else
+#define AIC79XX_RESET_DELAY 5000
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic79xx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+ uint16_t tag_commands[16]; /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0 tagged queuing disabled
+ * 1 <= n <= 253 n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full. For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred). On some devices, queue full is returned for a temporary
+ * resource shortage. These devices will return queue full at varying
+ * depths. The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic79xx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ * to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic79xx_tag_info[] =
+{
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC79XX_CMDS_PER_DEVICE
+#define AIC79XX_CMDS_PER_DEVICE CONFIG_AIC79XX_CMDS_PER_DEVICE
+#else
+#define AIC79XX_CMDS_PER_DEVICE AHD_MAX_QUEUE
+#endif
+
+#define AIC79XX_CONFIGED_TAG_COMMANDS { \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \
+ AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE \
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic79xx_tag_info[] =
+{
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS},
+ {AIC79XX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * By default, read streaming is disabled. In theory,
+ * read streaming should enhance performance, but early
+ * U320 drive firmware actually performs slower with
+ * read streaming enabled.
+ */
+#ifdef CONFIG_AIC79XX_ENABLE_RD_STRM
+#define AIC79XX_CONFIGED_RD_STRM 0xFFFF
+#else
+#define AIC79XX_CONFIGED_RD_STRM 0
+#endif
+
+static uint16_t aic79xx_rd_strm_info[] =
+{
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM,
+ AIC79XX_CONFIGED_RD_STRM
+};
+
+/*
+ * DV option:
+ *
+ * positive value = DV Enabled
+ * zero = DV Disabled
+ * negative value = DV Default for adapter type/seeprom
+ */
+#ifdef CONFIG_AIC79XX_DV_SETTING
+#define AIC79XX_CONFIGED_DV CONFIG_AIC79XX_DV_SETTING
+#else
+#define AIC79XX_CONFIGED_DV -1
+#endif
+
+static int8_t aic79xx_dv_settings[] =
+{
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV,
+ AIC79XX_CONFIGED_DV
+};
+
+/*
+ * The I/O cell on the chip is very configurable in respect to its analog
+ * characteristics. Set the defaults here; they can be overriden with
+ * the proper insmod parameters.
+ */
+struct ahd_linux_iocell_opts
+{
+ uint8_t precomp;
+ uint8_t slewrate;
+ uint8_t amplitude;
+};
+#define AIC79XX_DEFAULT_PRECOMP 0xFF
+#define AIC79XX_DEFAULT_SLEWRATE 0xFF
+#define AIC79XX_DEFAULT_AMPLITUDE 0xFF
+#define AIC79XX_DEFAULT_IOOPTS \
+{ \
+ AIC79XX_DEFAULT_PRECOMP, \
+ AIC79XX_DEFAULT_SLEWRATE, \
+ AIC79XX_DEFAULT_AMPLITUDE \
+}
+#define AIC79XX_PRECOMP_INDEX 0
+#define AIC79XX_SLEWRATE_INDEX 1
+#define AIC79XX_AMPLITUDE_INDEX 2
+static struct ahd_linux_iocell_opts aic79xx_iocell_info[] =
+{
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS,
+ AIC79XX_DEFAULT_IOOPTS
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+void
+ahd_print_path(struct ahd_softc *ahd, struct scb *scb)
+{
+ printk("(scsi%d:%c:%d:%d): ",
+ ahd->platform_data->host->host_no,
+ scb != NULL ? SCB_GET_CHANNEL(ahd, scb) : 'X',
+ scb != NULL ? SCB_GET_TARGET(ahd, scb) : -1,
+ scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ adapters
+ * cards in the system. This should be fixed. Exceptions to this
+ * rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic79xx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips. The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect. Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from. The only exceptions to this are when a controller
+ * has its BIOS disabled. So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number. We also force
+ * all controllers with their BIOS disabled to the end of the list. This
+ * works on *almost* all computers. Where it doesn't work, we have this
+ * option. Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end. That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static uint32_t aic79xx_reverse_scan;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ * 0 == Use whatever is in the SEEPROM or default to off
+ * 1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic79xx_extended;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers. This is somewhat
+ * dubious at best. To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ * 0 = Shut off PCI parity check
+ * non-0 = Enable PCI parity check
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number. That will invert the 0 which will result in
+ * -1.
+ */
+static uint32_t aic79xx_pci_parity = ~0;
+
+/*
+ * There are lots of broken chipsets in the world. Some of them will
+ * violate the PCI spec when we issue byte sized memory writes to our
+ * controller. I/O mapped register access, if allowed by the given
+ * platform, will work in almost all cases.
+ */
+uint32_t aic79xx_allow_memio = ~0;
+
+/*
+ * aic79xx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic79xx_detect().
+ */
+int aic79xx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ * 0 - 256ms
+ * 1 - 128ms
+ * 2 - 64ms
+ * 3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static uint32_t aic79xx_seltime;
+
+/*
+ * Certain devices do not perform any aging on commands. Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+uint32_t aic79xx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+static char *aic79xx = NULL;
+
+MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>");
+MODULE_DESCRIPTION("Adaptec Aic790X U320 SCSI Host Bus Adapter driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(AIC79XX_DRIVER_VERSION);
+module_param(aic79xx, charp, 0);
+MODULE_PARM_DESC(aic79xx,
+"period delimited, options string.\n"
+" verbose Enable verbose/diagnostic logging\n"
+" allow_memio Allow device registers to be memory mapped\n"
+" debug Bitmask of debug values to enable\n"
+" no_reset Supress initial bus resets\n"
+" extended Enable extended geometry on all controllers\n"
+" periodic_otag Send an ordered tagged transaction\n"
+" periodically to prevent tag starvation.\n"
+" This may be required by some older disk\n"
+" or drives/RAID arrays.\n"
+" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n"
+" tag_info:<tag_str> Set per-target tag depth\n"
+" global_tag_depth:<int> Global tag depth for all targets on all buses\n"
+" rd_strm:<rd_strm_masks> Set per-target read streaming setting.\n"
+" dv:<dv_settings> Set per-controller Domain Validation Setting.\n"
+" slewrate:<slewrate_list>Set the signal slew rate (0-15).\n"
+" precomp:<pcomp_list> Set the signal precompensation (0-7).\n"
+" amplitude:<int> Set the signal amplitude (0-7).\n"
+" seltime:<int> Selection Timeout:\n"
+" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"\n"
+" Sample /etc/modprobe.conf line:\n"
+" Enable verbose logging\n"
+" Set tag depth on Controller 2/Target 2 to 10 tags\n"
+" Shorten the selection timeout to 128ms\n"
+"\n"
+" options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n"
+"\n"
+" Sample /etc/modprobe.conf line:\n"
+" Change Read Streaming for Controller's 2 and 3\n"
+"\n"
+" options aic79xx 'aic79xx=rd_strm:{..0xFFF0.0xC0F0}'");
+
+static void ahd_linux_handle_scsi_status(struct ahd_softc *,
+ struct ahd_linux_device *,
+ struct scb *);
+static void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd,
+ Scsi_Cmnd *cmd);
+static void ahd_linux_filter_inquiry(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dev_timed_unfreeze(u_long arg);
+static void ahd_linux_sem_timeout(u_long arg);
+static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd);
+static void ahd_linux_size_nseg(void);
+static void ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd);
+static void ahd_linux_start_dv(struct ahd_softc *ahd);
+static void ahd_linux_dv_timeout(struct scsi_cmnd *cmd);
+static int ahd_linux_dv_thread(void *data);
+static void ahd_linux_kill_dv_thread(struct ahd_softc *ahd);
+static void ahd_linux_dv_target(struct ahd_softc *ahd, u_int target);
+static void ahd_linux_dv_transition(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_fill_cmd(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dv_inq(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ,
+ u_int request_length);
+static void ahd_linux_dv_tur(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dv_rebd(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_web(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_reb(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static void ahd_linux_dv_su(struct ahd_softc *ahd,
+ struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ);
+static int ahd_linux_fallback(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static __inline int ahd_linux_dv_fallback(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static void ahd_linux_dv_complete(Scsi_Cmnd *cmd);
+static void ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ);
+static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo);
+static u_int ahd_linux_user_dv_setting(struct ahd_softc *ahd);
+static void ahd_linux_setup_user_rd_strm_settings(struct ahd_softc *ahd);
+static void ahd_linux_device_queue_depth(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev);
+static struct ahd_linux_target* ahd_linux_alloc_target(struct ahd_softc*,
+ u_int, u_int);
+static void ahd_linux_free_target(struct ahd_softc*,
+ struct ahd_linux_target*);
+static struct ahd_linux_device* ahd_linux_alloc_device(struct ahd_softc*,
+ struct ahd_linux_target*,
+ u_int);
+static void ahd_linux_free_device(struct ahd_softc*,
+ struct ahd_linux_device*);
+static void ahd_linux_run_device_queue(struct ahd_softc*,
+ struct ahd_linux_device*);
+static void ahd_linux_setup_tag_info_global(char *p);
+static aic_option_callback_t ahd_linux_setup_tag_info;
+static aic_option_callback_t ahd_linux_setup_rd_strm_info;
+static aic_option_callback_t ahd_linux_setup_dv;
+static aic_option_callback_t ahd_linux_setup_iocell_info;
+static int ahd_linux_next_unit(void);
+static void ahd_runq_tasklet(unsigned long data);
+static int aic79xx_setup(char *c);
+
+/****************************** Inlines ***************************************/
+static __inline void ahd_schedule_completeq(struct ahd_softc *ahd);
+static __inline void ahd_schedule_runq(struct ahd_softc *ahd);
+static __inline void ahd_setup_runq_tasklet(struct ahd_softc *ahd);
+static __inline void ahd_teardown_runq_tasklet(struct ahd_softc *ahd);
+static __inline struct ahd_linux_device*
+ ahd_linux_get_device(struct ahd_softc *ahd, u_int channel,
+ u_int target, u_int lun, int alloc);
+static struct ahd_cmd *ahd_linux_run_complete_queue(struct ahd_softc *ahd);
+static __inline void ahd_linux_check_device_queue(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev);
+static __inline struct ahd_linux_device *
+ ahd_linux_next_device_to_run(struct ahd_softc *ahd);
+static __inline void ahd_linux_run_device_queues(struct ahd_softc *ahd);
+static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);
+
+static __inline void
+ahd_schedule_completeq(struct ahd_softc *ahd)
+{
+ if ((ahd->platform_data->flags & AHD_RUN_CMPLT_Q_TIMER) == 0) {
+ ahd->platform_data->flags |= AHD_RUN_CMPLT_Q_TIMER;
+ ahd->platform_data->completeq_timer.expires = jiffies;
+ add_timer(&ahd->platform_data->completeq_timer);
+ }
+}
+
+/*
+ * Must be called with our lock held.
+ */
+static __inline void
+ahd_schedule_runq(struct ahd_softc *ahd)
+{
+ tasklet_schedule(&ahd->platform_data->runq_tasklet);
+}
+
+static __inline
+void ahd_setup_runq_tasklet(struct ahd_softc *ahd)
+{
+ tasklet_init(&ahd->platform_data->runq_tasklet, ahd_runq_tasklet,
+ (unsigned long)ahd);
+}
+
+static __inline void
+ahd_teardown_runq_tasklet(struct ahd_softc *ahd)
+{
+ tasklet_kill(&ahd->platform_data->runq_tasklet);
+}
+
+static __inline struct ahd_linux_device*
+ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, u_int target,
+ u_int lun, int alloc)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_linux_device *dev;
+ u_int target_offset;
+
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ targ = ahd->platform_data->targets[target_offset];
+ if (targ == NULL) {
+ if (alloc != 0) {
+ targ = ahd_linux_alloc_target(ahd, channel, target);
+ if (targ == NULL)
+ return (NULL);
+ } else
+ return (NULL);
+ }
+ dev = targ->devices[lun];
+ if (dev == NULL && alloc != 0)
+ dev = ahd_linux_alloc_device(ahd, targ, lun);
+ return (dev);
+}
+
+#define AHD_LINUX_MAX_RETURNED_ERRORS 4
+static struct ahd_cmd *
+ahd_linux_run_complete_queue(struct ahd_softc *ahd)
+{
+ struct ahd_cmd *acmd;
+ u_long done_flags;
+ int with_errors;
+
+ with_errors = 0;
+ ahd_done_lock(ahd, &done_flags);
+ while ((acmd = TAILQ_FIRST(&ahd->platform_data->completeq)) != NULL) {
+ Scsi_Cmnd *cmd;
+
+ if (with_errors > AHD_LINUX_MAX_RETURNED_ERRORS) {
+ /*
+ * Linux uses stack recursion to requeue
+ * commands that need to be retried. Avoid
+ * blowing out the stack by "spoon feeding"
+ * commands that completed with error back
+ * the operating system in case they are going
+ * to be retried. "ick"
+ */
+ ahd_schedule_completeq(ahd);
+ break;
+ }
+ TAILQ_REMOVE(&ahd->platform_data->completeq,
+ acmd, acmd_links.tqe);
+ cmd = &acmd_scsi_cmd(acmd);
+ cmd->host_scribble = NULL;
+ if (ahd_cmd_get_transaction_status(cmd) != DID_OK
+ || (cmd->result & 0xFF) != SCSI_STATUS_OK)
+ with_errors++;
+
+ cmd->scsi_done(cmd);
+ }
+ ahd_done_unlock(ahd, &done_flags);
+ return (acmd);
+}
+
+static __inline void
+ahd_linux_check_device_queue(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev)
+{
+ if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) != 0
+ && dev->active == 0) {
+ dev->flags &= ~AHD_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen--;
+ }
+
+ if (TAILQ_FIRST(&dev->busyq) == NULL
+ || dev->openings == 0 || dev->qfrozen != 0)
+ return;
+
+ ahd_linux_run_device_queue(ahd, dev);
+}
+
+static __inline struct ahd_linux_device *
+ahd_linux_next_device_to_run(struct ahd_softc *ahd)
+{
+
+ if ((ahd->flags & AHD_RESOURCE_SHORTAGE) != 0
+ || (ahd->platform_data->qfrozen != 0
+ && AHD_DV_SIMQ_FROZEN(ahd) == 0))
+ return (NULL);
+ return (TAILQ_FIRST(&ahd->platform_data->device_runq));
+}
+
+static __inline void
+ahd_linux_run_device_queues(struct ahd_softc *ahd)
+{
+ struct ahd_linux_device *dev;
+
+ while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) {
+ TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHD_DEV_ON_RUN_LIST;
+ ahd_linux_check_device_queue(ahd, dev);
+ }
+}
+
+static __inline void
+ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+ Scsi_Cmnd *cmd;
+ int direction;
+
+ cmd = scb->io_ctx;
+ direction = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+ ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE);
+ if (cmd->use_sg != 0) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(ahd->dev_softc, sg, cmd->use_sg, direction);
+ } else if (cmd->request_bufflen != 0) {
+ pci_unmap_single(ahd->dev_softc,
+ scb->platform_data->buf_busaddr,
+ cmd->request_bufflen, direction);
+ }
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahd, cmd) \
+ ((((cmd)->device->id << TID_SHIFT) & TID) | (ahd)->our_id)
+
+/************************ Host template entry points *************************/
+static int ahd_linux_detect(Scsi_Host_Template *);
+static const char *ahd_linux_info(struct Scsi_Host *);
+static int ahd_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int ahd_linux_slave_alloc(Scsi_Device *);
+static int ahd_linux_slave_configure(Scsi_Device *);
+static void ahd_linux_slave_destroy(Scsi_Device *);
+#if defined(__i386__)
+static int ahd_linux_biosparam(struct scsi_device*,
+ struct block_device*, sector_t, int[]);
+#endif
+#else
+static int ahd_linux_release(struct Scsi_Host *);
+static void ahd_linux_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs);
+#if defined(__i386__)
+static int ahd_linux_biosparam(Disk *, kdev_t, int[]);
+#endif
+#endif
+static int ahd_linux_bus_reset(Scsi_Cmnd *);
+static int ahd_linux_dev_reset(Scsi_Cmnd *);
+static int ahd_linux_abort(Scsi_Cmnd *);
+
+/*
+ * Calculate a safe value for AHD_NSEG (as expressed through ahd_linux_nseg).
+ *
+ * In pre-2.5.X...
+ * The midlayer allocates an S/G array dynamically when a command is issued
+ * using SCSI malloc. This array, which is in an OS dependent format that
+ * must later be copied to our private S/G list, is sized to house just the
+ * number of segments needed for the current transfer. Since the code that
+ * sizes the SCSI malloc pool does not take into consideration fragmentation
+ * of the pool, executing transactions numbering just a fraction of our
+ * concurrent transaction limit with SG list lengths aproaching AHC_NSEG will
+ * quickly depleat the SCSI malloc pool of usable space. Unfortunately, the
+ * mid-layer does not properly handle this scsi malloc failures for the S/G
+ * array and the result can be a lockup of the I/O subsystem. We try to size
+ * our S/G list so that it satisfies our drivers allocation requirements in
+ * addition to avoiding fragmentation of the SCSI malloc pool.
+ */
+static void
+ahd_linux_size_nseg(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ u_int cur_size;
+ u_int best_size;
+
+ /*
+ * The SCSI allocator rounds to the nearest 512 bytes
+ * an cannot allocate across a page boundary. Our algorithm
+ * is to start at 1K of scsi malloc space per-command and
+ * loop through all factors of the PAGE_SIZE and pick the best.
+ */
+ best_size = 0;
+ for (cur_size = 1024; cur_size <= PAGE_SIZE; cur_size *= 2) {
+ u_int nseg;
+
+ nseg = cur_size / sizeof(struct scatterlist);
+ if (nseg < AHD_LINUX_MIN_NSEG)
+ continue;
+
+ if (best_size == 0) {
+ best_size = cur_size;
+ ahd_linux_nseg = nseg;
+ } else {
+ u_int best_rem;
+ u_int cur_rem;
+
+ /*
+ * Compare the traits of the current "best_size"
+ * with the current size to determine if the
+ * current size is a better size.
+ */
+ best_rem = best_size % sizeof(struct scatterlist);
+ cur_rem = cur_size % sizeof(struct scatterlist);
+ if (cur_rem < best_rem) {
+ best_size = cur_size;
+ ahd_linux_nseg = nseg;
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * Try to detect an Adaptec 79XX controller.
+ */
+static int
+ahd_linux_detect(Scsi_Host_Template *template)
+{
+ struct ahd_softc *ahd;
+ int found;
+ int error = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+#endif
+
+ /*
+ * Sanity checking of Linux SCSI data structures so
+ * that some of our hacks^H^H^H^H^Hassumptions aren't
+ * violated.
+ */
+ if (offsetof(struct ahd_cmd_internal, end)
+ > offsetof(struct scsi_cmnd, host_scribble)) {
+ printf("ahd_linux_detect: SCSI data structures changed.\n");
+ printf("ahd_linux_detect: Unable to attach\n");
+ return (0);
+ }
+ /*
+ * Determine an appropriate size for our Scatter Gatther lists.
+ */
+ ahd_linux_size_nseg();
+#ifdef MODULE
+ /*
+ * If we've been passed any parameters, process them now.
+ */
+ if (aic79xx)
+ aic79xx_setup(aic79xx);
+#endif
+
+ template->proc_name = "aic79xx";
+
+ /*
+ * Initialize our softc list lock prior to
+ * probing for any adapters.
+ */
+ ahd_list_lockinit();
+
+#ifdef CONFIG_PCI
+ error = ahd_linux_pci_init();
+ if (error)
+ return error;
+#endif
+
+ /*
+ * Register with the SCSI layer all
+ * controllers we've found.
+ */
+ found = 0;
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+
+ if (ahd_linux_register_host(ahd, template) == 0)
+ found++;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ aic79xx_detect_complete++;
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+static int
+ahd_linux_release(struct Scsi_Host * host)
+{
+ struct ahd_softc *ahd;
+ u_long l;
+
+ ahd_list_lock(&l);
+ if (host != NULL) {
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahd = ahd_find_softc(*(struct ahd_softc **)host->hostdata);
+ if (ahd != NULL) {
+ u_long s;
+
+ ahd_lock(ahd, &s);
+ ahd_intr_enable(ahd, FALSE);
+ ahd_unlock(ahd, &s);
+ ahd_free(ahd);
+ }
+ }
+ ahd_list_unlock(&l);
+ return (0);
+}
+#endif
+
+/*
+ * Return a string describing the driver.
+ */
+static const char *
+ahd_linux_info(struct Scsi_Host *host)
+{
+ static char buffer[512];
+ char ahd_info[256];
+ char *bp;
+ struct ahd_softc *ahd;
+
+ bp = &buffer[0];
+ ahd = *(struct ahd_softc **)host->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AIC79XX PCI-X SCSI HBA DRIVER, Rev ");
+ strcat(bp, AIC79XX_DRIVER_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, ahd->description);
+ strcat(bp, ">\n");
+ strcat(bp, " ");
+ ahd_controller_info(ahd, ahd_info);
+ strcat(bp, ahd_info);
+ strcat(bp, "\n");
+
+ return (bp);
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+static int
+ahd_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+ struct ahd_softc *ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+
+ /*
+ * Save the callback on completion function.
+ */
+ cmd->scsi_done = scsi_done;
+
+ ahd_midlayer_entrypoint_lock(ahd, &flags);
+
+ /*
+ * Close the race of a command that was in the process of
+ * being queued to us just as our simq was frozen. Let
+ * DV commands through so long as we are only frozen to
+ * perform DV.
+ */
+ if (ahd->platform_data->qfrozen != 0
+ && AHD_DV_CMD(cmd) == 0) {
+
+ ahd_cmd_set_transaction_status(cmd, CAM_REQUEUE_REQ);
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ ahd_schedule_completeq(ahd);
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+ return (0);
+ }
+ dev = ahd_linux_get_device(ahd, cmd->device->channel,
+ cmd->device->id, cmd->device->lun,
+ /*alloc*/TRUE);
+ if (dev == NULL) {
+ ahd_cmd_set_transaction_status(cmd, CAM_RESRC_UNAVAIL);
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ ahd_schedule_completeq(ahd);
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+ printf("%s: aic79xx_linux_queue - Unable to allocate device!\n",
+ ahd_name(ahd));
+ return (0);
+ }
+ if (cmd->cmd_len > MAX_CDB_LEN)
+ return (-EINVAL);
+ cmd->result = CAM_REQ_INPROG << 16;
+ TAILQ_INSERT_TAIL(&dev->busyq, (struct ahd_cmd *)cmd, acmd_links.tqe);
+ if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ ahd_linux_run_device_queues(ahd);
+ }
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+ return (0);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int
+ahd_linux_slave_alloc(Scsi_Device *device)
+{
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Alloc %d\n", ahd_name(ahd), device->id);
+ return (0);
+}
+
+static int
+ahd_linux_slave_configure(Scsi_Device *device)
+{
+ struct ahd_softc *ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Configure %d\n", ahd_name(ahd), device->id);
+ ahd_midlayer_entrypoint_lock(ahd, &flags);
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahd_linux_get_device(ahd, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ dev->flags |= AHD_DEV_SLAVE_CONFIGURED;
+ dev->scsi_device = device;
+ ahd_linux_device_queue_depth(ahd, dev);
+ }
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+ return (0);
+}
+
+static void
+ahd_linux_slave_destroy(Scsi_Device *device)
+{
+ struct ahd_softc *ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Destroy %d\n", ahd_name(ahd), device->id);
+ ahd_midlayer_entrypoint_lock(ahd, &flags);
+ dev = ahd_linux_get_device(ahd, device->channel,
+ device->id, device->lun,
+ /*alloc*/FALSE);
+
+ /*
+ * Filter out "silly" deletions of real devices by only
+ * deleting devices that have had slave_configure()
+ * called on them. All other devices that have not
+ * been configured will automatically be deleted by
+ * the refcounting process.
+ */
+ if (dev != NULL
+ && (dev->flags & AHD_DEV_SLAVE_CONFIGURED) != 0) {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ if (TAILQ_EMPTY(&dev->busyq)
+ && dev->active == 0
+ && (dev->flags & AHD_DEV_TIMER_ACTIVE) == 0)
+ ahd_linux_free_device(ahd, dev);
+ }
+ ahd_midlayer_entrypoint_unlock(ahd, &flags);
+}
+#else
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+ahd_linux_select_queue_depth(struct Scsi_Host * host,
+ Scsi_Device * scsi_devs)
+{
+ Scsi_Device *device;
+ Scsi_Device *ldev;
+ struct ahd_softc *ahd;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)host->hostdata);
+ ahd_lock(ahd, &flags);
+ for (device = scsi_devs; device != NULL; device = device->next) {
+
+ /*
+ * Watch out for duplicate devices. This works around
+ * some quirks in how the SCSI scanning code does its
+ * device management.
+ */
+ for (ldev = scsi_devs; ldev != device; ldev = ldev->next) {
+ if (ldev->host == device->host
+ && ldev->channel == device->channel
+ && ldev->id == device->id
+ && ldev->lun == device->lun)
+ break;
+ }
+ /* Skip duplicate. */
+ if (ldev != device)
+ continue;
+
+ if (device->host == host) {
+ struct ahd_linux_device *dev;
+
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahd_linux_get_device(ahd, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ dev->scsi_device = device;
+ ahd_linux_device_queue_depth(ahd, dev);
+ device->queue_depth = dev->openings
+ + dev->active;
+ if ((dev->flags & (AHD_DEV_Q_BASIC
+ | AHD_DEV_Q_TAGGED)) == 0) {
+ /*
+ * We allow the OS to queue 2 untagged
+ * transactions to us at any time even
+ * though we can only execute them
+ * serially on the controller/device.
+ * This should remove some latency.
+ */
+ device->queue_depth = 2;
+ }
+ }
+ }
+ }
+ ahd_unlock(ahd, &flags);
+}
+#endif
+
+#if defined(__i386__)
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ uint8_t *bh;
+#else
+ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ struct scsi_device *sdev = disk->device;
+ u_long capacity = disk->capacity;
+ struct buffer_head *bh;
+#endif
+ int heads;
+ int sectors;
+ int cylinders;
+ int ret;
+ int extended;
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)sdev->host->hostdata);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ bh = scsi_bios_ptable(bdev);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev));
+#else
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+#endif
+
+ if (bh) {
+ ret = scsi_partsize(bh, capacity,
+ &geom[2], &geom[0], &geom[1]);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ kfree(bh);
+#else
+ brelse(bh);
+#endif
+ if (ret != -1)
+ return (ret);
+ }
+ heads = 64;
+ sectors = 32;
+ cylinders = aic_sector_div(capacity, heads, sectors);
+
+ if (aic79xx_extended != 0)
+ extended = 1;
+ else
+ extended = (ahd->flags & AHD_EXTENDED_TRANS_A) != 0;
+ if (extended && cylinders >= 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = aic_sector_div(capacity, heads, sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return (0);
+}
+#endif
+
+/*
+ * Abort the current SCSI command(s).
+ */
+static int
+ahd_linux_abort(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ struct ahd_cmd *acmd;
+ struct ahd_cmd *list_acmd;
+ struct ahd_linux_device *dev;
+ struct scb *pending_scb;
+ u_long s;
+ u_int saved_scbptr;
+ u_int active_scbptr;
+ u_int last_phase;
+ u_int cdb_byte;
+ int retval;
+ int was_paused;
+ int paused;
+ int wait;
+ int disconnected;
+ ahd_mode_state saved_modes;
+
+ pending_scb = NULL;
+ paused = FALSE;
+ wait = FALSE;
+ ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+ acmd = (struct ahd_cmd *)cmd;
+
+ printf("%s:%d:%d:%d: Attempting to abort cmd %p:",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun, cmd);
+ for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
+ printf(" 0x%x", cmd->cmnd[cdb_byte]);
+ printf("\n");
+
+ /*
+ * In all versions of Linux, we have to work around
+ * a major flaw in how the mid-layer is locked down
+ * if we are to sleep successfully in our error handler
+ * while allowing our interrupt handler to run. Since
+ * the midlayer acquires either the io_request_lock or
+ * our lock prior to calling us, we must use the
+ * spin_unlock_irq() method for unlocking our lock.
+ * This will force interrupts to be enabled on the
+ * current CPU. Since the EH thread should not have
+ * been running with CPU interrupts disabled other than
+ * by acquiring either the io_request_lock or our own
+ * lock, this *should* be safe.
+ */
+ ahd_midlayer_entrypoint_lock(ahd, &s);
+
+ /*
+ * First determine if we currently own this command.
+ * Start by searching the device queue. If not found
+ * there, check the pending_scb list. If not found
+ * at all, and the system wanted us to just abort the
+ * command, return success.
+ */
+ dev = ahd_linux_get_device(ahd, cmd->device->channel,
+ cmd->device->id, cmd->device->lun,
+ /*alloc*/FALSE);
+
+ if (dev == NULL) {
+ /*
+ * No target device for this command exists,
+ * so we must not still own the command.
+ */
+ printf("%s:%d:%d:%d: Is not an active device\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ retval = SUCCESS;
+ goto no_cmd;
+ }
+
+ TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+ if (list_acmd == acmd)
+ break;
+ }
+
+ if (list_acmd != NULL) {
+ printf("%s:%d:%d:%d: Command found on device queue\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+ cmd->result = DID_ABORT << 16;
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ retval = SUCCESS;
+ goto done;
+ }
+
+ /*
+ * See if we can find a matching cmd in the pending list.
+ */
+ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+ if (pending_scb->io_ctx == cmd)
+ break;
+ }
+
+ if (pending_scb == NULL) {
+ printf("%s:%d:%d:%d: Command not found\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ goto no_cmd;
+ }
+
+ if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+ /*
+ * We can't queue two recovery actions using the same SCB
+ */
+ retval = FAILED;
+ goto done;
+ }
+
+ /*
+ * Ensure that the card doesn't do anything
+ * behind our back. Also make sure that we
+ * didn't "just" miss an interrupt that would
+ * affect this cmd.
+ */
+ was_paused = ahd_is_paused(ahd);
+ ahd_pause_and_flushwork(ahd);
+ paused = TRUE;
+
+ if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+ printf("%s:%d:%d:%d: Command already completed\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ goto no_cmd;
+ }
+
+ printf("%s: At time of recovery, card was %spaused\n",
+ ahd_name(ahd), was_paused ? "" : "not ");
+ ahd_dump_card_state(ahd);
+
+ disconnected = TRUE;
+ if (ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A',
+ cmd->device->lun, SCB_GET_TAG(pending_scb),
+ ROLE_INITIATOR, CAM_REQ_ABORTED,
+ SEARCH_COMPLETE) > 0) {
+ printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ retval = SUCCESS;
+ goto done;
+ }
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ last_phase = ahd_inb(ahd, LASTPHASE);
+ saved_scbptr = ahd_get_scbptr(ahd);
+ active_scbptr = saved_scbptr;
+ if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {
+ struct scb *bus_scb;
+
+ bus_scb = ahd_lookup_scb(ahd, active_scbptr);
+ if (bus_scb == pending_scb)
+ disconnected = FALSE;
+ }
+
+ /*
+ * At this point, pending_scb is the scb associated with the
+ * passed in command. That command is currently active on the
+ * bus or is in the disconnected state.
+ */
+ if (last_phase != P_BUSFREE
+ && SCB_GET_TAG(pending_scb) == active_scbptr) {
+
+ /*
+ * We're active on the bus, so assert ATN
+ * and hope that the target responds.
+ */
+ pending_scb = ahd_lookup_scb(ahd, active_scbptr);
+ pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT;
+ ahd_outb(ahd, MSG_OUT, HOST_MSG);
+ ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
+ printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+ ahd_name(ahd), cmd->device->channel,
+ cmd->device->id, cmd->device->lun);
+ wait = TRUE;
+ } else if (disconnected) {
+
+ /*
+ * Actually re-queue this SCB in an attempt
+ * to select the device before it reconnects.
+ */
+ pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT;
+ ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb));
+ pending_scb->hscb->cdb_len = 0;
+ pending_scb->hscb->task_attribute = 0;
+ pending_scb->hscb->task_management = SIU_TASKMGMT_ABORT_TASK;
+
+ if ((pending_scb->flags & SCB_PACKETIZED) != 0) {
+ /*
+ * Mark the SCB has having an outstanding
+ * task management function. Should the command
+ * complete normally before the task management
+ * function can be sent, the host will be notified
+ * to abort our requeued SCB.
+ */
+ ahd_outb(ahd, SCB_TASK_MANAGEMENT,
+ pending_scb->hscb->task_management);
+ } else {
+ /*
+ * If non-packetized, set the MK_MESSAGE control
+ * bit indicating that we desire to send a message.
+ * We also set the disconnected flag since there is
+ * no guarantee that our SCB control byte matches
+ * the version on the card. We don't want the
+ * sequencer to abort the command thinking an
+ * unsolicited reselection occurred.
+ */
+ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+
+ /*
+ * The sequencer will never re-reference the
+ * in-core SCB. To make sure we are notified
+ * during reslection, set the MK_MESSAGE flag in
+ * the card's copy of the SCB.
+ */
+ ahd_outb(ahd, SCB_CONTROL,
+ ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE);
+ }
+
+ /*
+ * Clear out any entries in the QINFIFO first
+ * so we are the next SCB for this target
+ * to run.
+ */
+ ahd_search_qinfifo(ahd, cmd->device->id,
+ cmd->device->channel + 'A', cmd->device->lun,
+ SCB_LIST_NULL, ROLE_INITIATOR,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+ ahd_qinfifo_requeue_tail(ahd, pending_scb);
+ ahd_set_scbptr(ahd, saved_scbptr);
+ ahd_print_path(ahd, pending_scb);
+ printf("Device is disconnected, re-queuing SCB\n");
+ wait = TRUE;
+ } else {
+ printf("%s:%d:%d:%d: Unable to deliver message\n",
+ ahd_name(ahd), cmd->device->channel,
+ cmd->device->id, cmd->device->lun);
+ retval = FAILED;
+ goto done;
+ }
+
+no_cmd:
+ /*
+ * Our assumption is that if we don't have the command, no
+ * recovery action was required, so we return success. Again,
+ * the semantics of the mid-layer recovery engine are not
+ * well defined, so this may change in time.
+ */
+ retval = SUCCESS;
+done:
+ if (paused)
+ ahd_unpause(ahd);
+ if (wait) {
+ struct timer_list timer;
+ int ret;
+
+ pending_scb->platform_data->flags |= AHD_SCB_UP_EH_SEM;
+ spin_unlock_irq(&ahd->platform_data->spin_lock);
+ init_timer(&timer);
+ timer.data = (u_long)pending_scb;
+ timer.expires = jiffies + (5 * HZ);
+ timer.function = ahd_linux_sem_timeout;
+ add_timer(&timer);
+ printf("Recovery code sleeping\n");
+ down(&ahd->platform_data->eh_sem);
+ printf("Recovery code awake\n");
+ ret = del_timer_sync(&timer);
+ if (ret == 0) {
+ printf("Timer Expired\n");
+ retval = FAILED;
+ }
+ spin_lock_irq(&ahd->platform_data->spin_lock);
+ }
+ ahd_schedule_runq(ahd);
+ ahd_linux_run_complete_queue(ahd);
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ return (retval);
+}
+
+
+static void
+ahd_linux_dev_reset_complete(Scsi_Cmnd *cmd)
+{
+ free(cmd, M_DEVBUF);
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+static int
+ahd_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ struct scsi_cmnd *recovery_cmd;
+ struct ahd_linux_device *dev;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ u_long s;
+ struct timer_list timer;
+ int retval;
+
+ ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+ recovery_cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK);
+ if (!recovery_cmd)
+ return (FAILED);
+ memset(recovery_cmd, 0, sizeof(struct scsi_cmnd));
+ recovery_cmd->device = cmd->device;
+ recovery_cmd->scsi_done = ahd_linux_dev_reset_complete;
+#if AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s:%d:%d:%d: Device reset called for cmd %p\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->device->lun, cmd);
+#endif
+ ahd_midlayer_entrypoint_lock(ahd, &s);
+
+ dev = ahd_linux_get_device(ahd, cmd->device->channel, cmd->device->id,
+ cmd->device->lun, /*alloc*/FALSE);
+ if (dev == NULL) {
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ kfree(recovery_cmd);
+ return (FAILED);
+ }
+ if ((scb = ahd_get_scb(ahd, AHD_NEVER_COL_IDX)) == NULL) {
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ kfree(recovery_cmd);
+ return (FAILED);
+ }
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ cmd->device->id, &tstate);
+ recovery_cmd->result = CAM_REQ_INPROG << 16;
+ recovery_cmd->host_scribble = (char *)scb;
+ scb->io_ctx = recovery_cmd;
+ scb->platform_data->dev = dev;
+ scb->sg_count = 0;
+ ahd_set_residual(scb, 0);
+ ahd_set_sense_residual(scb, 0);
+ hscb = scb->hscb;
+ hscb->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahd, cmd);
+ hscb->lun = cmd->device->lun;
+ hscb->cdb_len = 0;
+ hscb->task_management = SIU_TASKMGMT_LUN_RESET;
+ scb->flags |= SCB_DEVICE_RESET|SCB_RECOVERY_SCB|SCB_ACTIVE;
+ if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ scb->flags |= SCB_PACKETIZED;
+ } else {
+ hscb->control |= MK_MESSAGE;
+ }
+ dev->openings--;
+ dev->active++;
+ dev->commands_issued++;
+ LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links);
+ ahd_queue_scb(ahd, scb);
+
+ scb->platform_data->flags |= AHD_SCB_UP_EH_SEM;
+ spin_unlock_irq(&ahd->platform_data->spin_lock);
+ init_timer(&timer);
+ timer.data = (u_long)scb;
+ timer.expires = jiffies + (5 * HZ);
+ timer.function = ahd_linux_sem_timeout;
+ add_timer(&timer);
+ printf("Recovery code sleeping\n");
+ down(&ahd->platform_data->eh_sem);
+ printf("Recovery code awake\n");
+ retval = SUCCESS;
+ if (del_timer_sync(&timer) == 0) {
+ printf("Timer Expired\n");
+ retval = FAILED;
+ }
+ spin_lock_irq(&ahd->platform_data->spin_lock);
+ ahd_schedule_runq(ahd);
+ ahd_linux_run_complete_queue(ahd);
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+ printf("%s: Device reset returning 0x%x\n", ahd_name(ahd), retval);
+ return (retval);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+static int
+ahd_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ u_long s;
+ int found;
+
+ ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+ printf("%s: Bus reset called for cmd %p\n",
+ ahd_name(ahd), cmd);
+#endif
+ ahd_midlayer_entrypoint_lock(ahd, &s);
+ found = ahd_reset_channel(ahd, cmd->device->channel + 'A',
+ /*initiate reset*/TRUE);
+ ahd_linux_run_complete_queue(ahd);
+ ahd_midlayer_entrypoint_unlock(ahd, &s);
+
+ if (bootverbose)
+ printf("%s: SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", ahd_name(ahd), found);
+
+ return (SUCCESS);
+}
+
+Scsi_Host_Template aic79xx_driver_template = {
+ .module = THIS_MODULE,
+ .name = "aic79xx",
+ .proc_info = ahd_linux_proc_info,
+ .info = ahd_linux_info,
+ .queuecommand = ahd_linux_queue,
+ .eh_abort_handler = ahd_linux_abort,
+ .eh_device_reset_handler = ahd_linux_dev_reset,
+ .eh_bus_reset_handler = ahd_linux_bus_reset,
+#if defined(__i386__)
+ .bios_param = ahd_linux_biosparam,
+#endif
+ .can_queue = AHD_MAX_QUEUE,
+ .this_id = -1,
+ .cmd_per_lun = 2,
+ .use_clustering = ENABLE_CLUSTERING,
+ .slave_alloc = ahd_linux_slave_alloc,
+ .slave_configure = ahd_linux_slave_configure,
+ .slave_destroy = ahd_linux_slave_destroy,
+};
+
+/**************************** Tasklet Handler *********************************/
+
+/*
+ * In 2.4.X and above, this routine is called from a tasklet,
+ * so we must re-acquire our lock prior to executing this code.
+ * In all prior kernels, ahd_schedule_runq() calls this routine
+ * directly and ahd_schedule_runq() is called with our lock held.
+ */
+static void
+ahd_runq_tasklet(unsigned long data)
+{
+ struct ahd_softc* ahd;
+ struct ahd_linux_device *dev;
+ u_long flags;
+
+ ahd = (struct ahd_softc *)data;
+ ahd_lock(ahd, &flags);
+ while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) {
+
+ TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHD_DEV_ON_RUN_LIST;
+ ahd_linux_check_device_queue(ahd, dev);
+ /* Yeild to our interrupt handler */
+ ahd_unlock(ahd, &flags);
+ ahd_lock(ahd, &flags);
+ }
+ ahd_unlock(ahd, &flags);
+}
+
+/******************************** Bus DMA *************************************/
+int
+ahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent,
+ bus_size_t alignment, bus_size_t boundary,
+ dma_addr_t lowaddr, dma_addr_t highaddr,
+ bus_dma_filter_t *filter, void *filterarg,
+ bus_size_t maxsize, int nsegments,
+ bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+ bus_dma_tag_t dmat;
+
+ dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+ if (dmat == NULL)
+ return (ENOMEM);
+
+ /*
+ * Linux is very simplistic about DMA memory. For now don't
+ * maintain all specification information. Once Linux supplies
+ * better facilities for doing these operations, or the
+ * needs of this particular driver change, we might need to do
+ * more here.
+ */
+ dmat->alignment = alignment;
+ dmat->boundary = boundary;
+ dmat->maxsize = maxsize;
+ *ret_tag = dmat;
+ return (0);
+}
+
+void
+ahd_dma_tag_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat)
+{
+ free(dmat, M_DEVBUF);
+}
+
+int
+ahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr,
+ int flags, bus_dmamap_t *mapp)
+{
+ bus_dmamap_t map;
+
+ map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+ if (map == NULL)
+ return (ENOMEM);
+ /*
+ * Although we can dma data above 4GB, our
+ * "consistent" memory is below 4GB for
+ * space efficiency reasons (only need a 4byte
+ * address). For this reason, we have to reset
+ * our dma mask when doing allocations.
+ */
+ if (ahd->dev_softc != NULL)
+ if (pci_set_dma_mask(ahd->dev_softc, 0xFFFFFFFF)) {
+ printk(KERN_WARNING "aic79xx: No suitable DMA available.\n");
+ kfree(map);
+ return (ENODEV);
+ }
+ *vaddr = pci_alloc_consistent(ahd->dev_softc,
+ dmat->maxsize, &map->bus_addr);
+ if (ahd->dev_softc != NULL)
+ if (pci_set_dma_mask(ahd->dev_softc,
+ ahd->platform_data->hw_dma_mask)) {
+ printk(KERN_WARNING "aic79xx: No suitable DMA available.\n");
+ kfree(map);
+ return (ENODEV);
+ }
+ if (*vaddr == NULL)
+ return (ENOMEM);
+ *mapp = map;
+ return(0);
+}
+
+void
+ahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat,
+ void* vaddr, bus_dmamap_t map)
+{
+ pci_free_consistent(ahd->dev_softc, dmat->maxsize,
+ vaddr, map->bus_addr);
+}
+
+int
+ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map,
+ void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+ void *cb_arg, int flags)
+{
+ /*
+ * Assume for now that this will only be used during
+ * initialization and not for per-transaction buffer mapping.
+ */
+ bus_dma_segment_t stack_sg;
+
+ stack_sg.ds_addr = map->bus_addr;
+ stack_sg.ds_len = dmat->maxsize;
+ cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+ return (0);
+}
+
+void
+ahd_dmamap_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /*
+ * The map may is NULL in our < 2.3.X implementation.
+ */
+ if (map != NULL)
+ free(map, M_DEVBUF);
+}
+
+int
+ahd_dmamap_unload(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /* Nothing to do */
+ return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+/*
+ * Compare "left hand" softc with "right hand" softc, returning:
+ * < 0 - lahd has a lower priority than rahd
+ * 0 - Softcs are equal
+ * > 0 - lahd has a higher priority than rahd
+ */
+int
+ahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd)
+{
+ int value;
+
+ /*
+ * Under Linux, cards are ordered as follows:
+ * 1) PCI devices that are marked as the boot controller.
+ * 2) PCI devices with BIOS enabled sorted by bus/slot/func.
+ * 3) All remaining PCI devices sorted by bus/slot/func.
+ */
+#if 0
+ value = (lahd->flags & AHD_BOOT_CHANNEL)
+ - (rahd->flags & AHD_BOOT_CHANNEL);
+ if (value != 0)
+ /* Controllers set for boot have a *higher* priority */
+ return (value);
+#endif
+
+ value = (lahd->flags & AHD_BIOS_ENABLED)
+ - (rahd->flags & AHD_BIOS_ENABLED);
+ if (value != 0)
+ /* Controllers with BIOS enabled have a *higher* priority */
+ return (value);
+
+ /* Still equal. Sort by bus/slot/func. */
+ if (aic79xx_reverse_scan != 0)
+ value = ahd_get_pci_bus(lahd->dev_softc)
+ - ahd_get_pci_bus(rahd->dev_softc);
+ else
+ value = ahd_get_pci_bus(rahd->dev_softc)
+ - ahd_get_pci_bus(lahd->dev_softc);
+ if (value != 0)
+ return (value);
+ if (aic79xx_reverse_scan != 0)
+ value = ahd_get_pci_slot(lahd->dev_softc)
+ - ahd_get_pci_slot(rahd->dev_softc);
+ else
+ value = ahd_get_pci_slot(rahd->dev_softc)
+ - ahd_get_pci_slot(lahd->dev_softc);
+ if (value != 0)
+ return (value);
+
+ value = rahd->channel - lahd->channel;
+ return (value);
+}
+
+static void
+ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
+{
+
+ if ((instance >= 0) && (targ >= 0)
+ && (instance < NUM_ELEMENTS(aic79xx_tag_info))
+ && (targ < AHD_NUM_TARGETS)) {
+ aic79xx_tag_info[instance].tag_commands[targ] = value & 0x1FF;
+ if (bootverbose)
+ printf("tag_info[%d:%d] = %d\n", instance, targ, value);
+ }
+}
+
+static void
+ahd_linux_setup_rd_strm_info(u_long arg, int instance, int targ, int32_t value)
+{
+ if ((instance >= 0)
+ && (instance < NUM_ELEMENTS(aic79xx_rd_strm_info))) {
+ aic79xx_rd_strm_info[instance] = value & 0xFFFF;
+ if (bootverbose)
+ printf("rd_strm[%d] = 0x%x\n", instance, value);
+ }
+}
+
+static void
+ahd_linux_setup_dv(u_long arg, int instance, int targ, int32_t value)
+{
+ if ((instance >= 0)
+ && (instance < NUM_ELEMENTS(aic79xx_dv_settings))) {
+ aic79xx_dv_settings[instance] = value;
+ if (bootverbose)
+ printf("dv[%d] = %d\n", instance, value);
+ }
+}
+
+static void
+ahd_linux_setup_iocell_info(u_long index, int instance, int targ, int32_t value)
+{
+
+ if ((instance >= 0)
+ && (instance < NUM_ELEMENTS(aic79xx_iocell_info))) {
+ uint8_t *iocell_info;
+
+ iocell_info = (uint8_t*)&aic79xx_iocell_info[instance];
+ iocell_info[index] = value & 0xFFFF;
+ if (bootverbose)
+ printf("iocell[%d:%ld] = %d\n", instance, index, value);
+ }
+}
+
+static void
+ahd_linux_setup_tag_info_global(char *p)
+{
+ int tags, i, j;
+
+ tags = simple_strtoul(p + 1, NULL, 0) & 0xff;
+ printf("Setting Global Tags= %d\n", tags);
+
+ for (i = 0; i < NUM_ELEMENTS(aic79xx_tag_info); i++) {
+ for (j = 0; j < AHD_NUM_TARGETS; j++) {
+ aic79xx_tag_info[i].tag_commands[j] = tags;
+ }
+ }
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic79xx=stpwlev:1,extended
+ */
+static int
+aic79xx_setup(char *s)
+{
+ int i, n;
+ char *p;
+ char *end;
+
+ static struct {
+ const char *name;
+ uint32_t *flag;
+ } options[] = {
+ { "extended", &aic79xx_extended },
+ { "no_reset", &aic79xx_no_reset },
+ { "verbose", &aic79xx_verbose },
+ { "allow_memio", &aic79xx_allow_memio},
+#ifdef AHD_DEBUG
+ { "debug", &ahd_debug },
+#endif
+ { "reverse_scan", &aic79xx_reverse_scan },
+ { "periodic_otag", &aic79xx_periodic_otag },
+ { "pci_parity", &aic79xx_pci_parity },
+ { "seltime", &aic79xx_seltime },
+ { "tag_info", NULL },
+ { "global_tag_depth", NULL},
+ { "rd_strm", NULL },
+ { "dv", NULL },
+ { "slewrate", NULL },
+ { "precomp", NULL },
+ { "amplitude", NULL },
+ };
+
+ end = strchr(s, '\0');
+
+ /*
+ * XXX ia64 gcc isn't smart enough to know that NUM_ELEMENTS
+ * will never be 0 in this case.
+ */
+ n = 0;
+
+ while ((p = strsep(&s, ",.")) != NULL) {
+ if (*p == '\0')
+ continue;
+ for (i = 0; i < NUM_ELEMENTS(options); i++) {
+
+ n = strlen(options[i].name);
+ if (strncmp(options[i].name, p, n) == 0)
+ break;
+ }
+ if (i == NUM_ELEMENTS(options))
+ continue;
+
+ if (strncmp(p, "global_tag_depth", n) == 0) {
+ ahd_linux_setup_tag_info_global(p + n);
+ } else if (strncmp(p, "tag_info", n) == 0) {
+ s = aic_parse_brace_option("tag_info", p + n, end,
+ 2, ahd_linux_setup_tag_info, 0);
+ } else if (strncmp(p, "rd_strm", n) == 0) {
+ s = aic_parse_brace_option("rd_strm", p + n, end,
+ 1, ahd_linux_setup_rd_strm_info, 0);
+ } else if (strncmp(p, "dv", n) == 0) {
+ s = aic_parse_brace_option("dv", p + n, end, 1,
+ ahd_linux_setup_dv, 0);
+ } else if (strncmp(p, "slewrate", n) == 0) {
+ s = aic_parse_brace_option("slewrate",
+ p + n, end, 1, ahd_linux_setup_iocell_info,
+ AIC79XX_SLEWRATE_INDEX);
+ } else if (strncmp(p, "precomp", n) == 0) {
+ s = aic_parse_brace_option("precomp",
+ p + n, end, 1, ahd_linux_setup_iocell_info,
+ AIC79XX_PRECOMP_INDEX);
+ } else if (strncmp(p, "amplitude", n) == 0) {
+ s = aic_parse_brace_option("amplitude",
+ p + n, end, 1, ahd_linux_setup_iocell_info,
+ AIC79XX_AMPLITUDE_INDEX);
+ } else if (p[n] == ':') {
+ *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+ } else if (!strncmp(p, "verbose", n)) {
+ *(options[i].flag) = 1;
+ } else {
+ *(options[i].flag) ^= 0xFFFFFFFF;
+ }
+ }
+ return 1;
+}
+
+__setup("aic79xx=", aic79xx_setup);
+
+uint32_t aic79xx_verbose;
+
+int
+ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template)
+{
+ char buf[80];
+ struct Scsi_Host *host;
+ char *new_name;
+ u_long s;
+ u_long target;
+
+ template->name = ahd->description;
+ host = scsi_host_alloc(template, sizeof(struct ahd_softc *));
+ if (host == NULL)
+ return (ENOMEM);
+
+ *((struct ahd_softc **)host->hostdata) = ahd;
+ ahd_lock(ahd, &s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_assign_lock(host, &ahd->platform_data->spin_lock);
+#elif AHD_SCSI_HAS_HOST_LOCK != 0
+ host->lock = &ahd->platform_data->spin_lock;
+#endif
+ ahd->platform_data->host = host;
+ host->can_queue = AHD_MAX_QUEUE;
+ host->cmd_per_lun = 2;
+ host->sg_tablesize = AHD_NSEG;
+ host->this_id = ahd->our_id;
+ host->irq = ahd->platform_data->irq;
+ host->max_id = (ahd->features & AHD_WIDE) ? 16 : 8;
+ host->max_lun = AHD_NUM_LUNS;
+ host->max_channel = 0;
+ host->sg_tablesize = AHD_NSEG;
+ ahd_set_unit(ahd, ahd_linux_next_unit());
+ sprintf(buf, "scsi%d", host->host_no);
+ new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (new_name != NULL) {
+ strcpy(new_name, buf);
+ ahd_set_name(ahd, new_name);
+ }
+ host->unique_id = ahd->unit;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ scsi_set_pci_device(host, ahd->dev_softc);
+#endif
+ ahd_linux_setup_user_rd_strm_settings(ahd);
+ ahd_linux_initialize_scsi_bus(ahd);
+ ahd_unlock(ahd, &s);
+ ahd->platform_data->dv_pid = kernel_thread(ahd_linux_dv_thread, ahd, 0);
+ ahd_lock(ahd, &s);
+ if (ahd->platform_data->dv_pid < 0) {
+ printf("%s: Failed to create DV thread, error= %d\n",
+ ahd_name(ahd), ahd->platform_data->dv_pid);
+ return (-ahd->platform_data->dv_pid);
+ }
+ /*
+ * Initially allocate *all* of our linux target objects
+ * so that the DV thread will scan them all in parallel
+ * just after driver initialization. Any device that
+ * does not exist will have its target object destroyed
+ * by the selection timeout handler. In the case of a
+ * device that appears after the initial DV scan, async
+ * negotiation will occur for the first command, and DV
+ * will comence should that first command be successful.
+ */
+ for (target = 0; target < host->max_id; target++) {
+
+ /*
+ * Skip our own ID. Some Compaq/HP storage devices
+ * have enclosure management devices that respond to
+ * single bit selection (i.e. selecting ourselves).
+ * It is expected that either an external application
+ * or a modified kernel will be used to probe this
+ * ID if it is appropriate. To accommodate these
+ * installations, ahc_linux_alloc_target() will allocate
+ * for our ID if asked to do so.
+ */
+ if (target == ahd->our_id)
+ continue;
+
+ ahd_linux_alloc_target(ahd, 0, target);
+ }
+ ahd_intr_enable(ahd, TRUE);
+ ahd_linux_start_dv(ahd);
+ ahd_unlock(ahd, &s);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_add_host(host, &ahd->dev_softc->dev); /* XXX handle failure */
+ scsi_scan_host(host);
+#endif
+ return (0);
+}
+
+uint64_t
+ahd_linux_get_memsize(void)
+{
+ struct sysinfo si;
+
+ si_meminfo(&si);
+ return ((uint64_t)si.totalram << PAGE_SHIFT);
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device. We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahd_linux_next_unit(void)
+{
+ struct ahd_softc *ahd;
+ int unit;
+
+ unit = 0;
+retry:
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ if (ahd->unit == unit) {
+ unit++;
+ goto retry;
+ }
+ }
+ return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+static void
+ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd)
+{
+ u_int target_id;
+ u_int numtarg;
+
+ target_id = 0;
+ numtarg = 0;
+
+ if (aic79xx_no_reset != 0)
+ ahd->flags &= ~AHD_RESET_BUS_A;
+
+ if ((ahd->flags & AHD_RESET_BUS_A) != 0)
+ ahd_reset_channel(ahd, 'A', /*initiate_reset*/TRUE);
+ else
+ numtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+
+ /*
+ * Force negotiation to async for all targets that
+ * will not see an initial bus reset.
+ */
+ for (; target_id < numtarg; target_id++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ target_id, &tstate);
+ ahd_compile_devinfo(&devinfo, ahd->our_id, target_id,
+ CAM_LUN_WILDCARD, 'A', ROLE_INITIATOR);
+ ahd_update_neg_request(ahd, &devinfo, tstate,
+ tinfo, AHD_NEG_ALWAYS);
+ }
+ /* Give the bus some time to recover */
+ if ((ahd->flags & AHD_RESET_BUS_A) != 0) {
+ ahd_freeze_simq(ahd);
+ init_timer(&ahd->platform_data->reset_timer);
+ ahd->platform_data->reset_timer.data = (u_long)ahd;
+ ahd->platform_data->reset_timer.expires =
+ jiffies + (AIC79XX_RESET_DELAY * HZ)/1000;
+ ahd->platform_data->reset_timer.function =
+ (ahd_linux_callback_t *)ahd_release_simq;
+ add_timer(&ahd->platform_data->reset_timer);
+ }
+}
+
+int
+ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
+{
+ ahd->platform_data =
+ malloc(sizeof(struct ahd_platform_data), M_DEVBUF, M_NOWAIT);
+ if (ahd->platform_data == NULL)
+ return (ENOMEM);
+ memset(ahd->platform_data, 0, sizeof(struct ahd_platform_data));
+ TAILQ_INIT(&ahd->platform_data->completeq);
+ TAILQ_INIT(&ahd->platform_data->device_runq);
+ ahd->platform_data->irq = AHD_LINUX_NOIRQ;
+ ahd->platform_data->hw_dma_mask = 0xFFFFFFFF;
+ ahd_lockinit(ahd);
+ ahd_done_lockinit(ahd);
+ init_timer(&ahd->platform_data->completeq_timer);
+ ahd->platform_data->completeq_timer.data = (u_long)ahd;
+ ahd->platform_data->completeq_timer.function =
+ (ahd_linux_callback_t *)ahd_linux_thread_run_complete_queue;
+ init_MUTEX_LOCKED(&ahd->platform_data->eh_sem);
+ init_MUTEX_LOCKED(&ahd->platform_data->dv_sem);
+ init_MUTEX_LOCKED(&ahd->platform_data->dv_cmd_sem);
+ ahd_setup_runq_tasklet(ahd);
+ ahd->seltime = (aic79xx_seltime & 0x3) << 4;
+ return (0);
+}
+
+void
+ahd_platform_free(struct ahd_softc *ahd)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_linux_device *dev;
+ int i, j;
+
+ if (ahd->platform_data != NULL) {
+ del_timer_sync(&ahd->platform_data->completeq_timer);
+ ahd_linux_kill_dv_thread(ahd);
+ ahd_teardown_runq_tasklet(ahd);
+ if (ahd->platform_data->host != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_remove_host(ahd->platform_data->host);
+#endif
+ scsi_host_put(ahd->platform_data->host);
+ }
+
+ /* destroy all of the device and target objects */
+ for (i = 0; i < AHD_NUM_TARGETS; i++) {
+ targ = ahd->platform_data->targets[i];
+ if (targ != NULL) {
+ /* Keep target around through the loop. */
+ targ->refcount++;
+ for (j = 0; j < AHD_NUM_LUNS; j++) {
+
+ if (targ->devices[j] == NULL)
+ continue;
+ dev = targ->devices[j];
+ ahd_linux_free_device(ahd, dev);
+ }
+ /*
+ * Forcibly free the target now that
+ * all devices are gone.
+ */
+ ahd_linux_free_target(ahd, targ);
+ }
+ }
+
+ if (ahd->platform_data->irq != AHD_LINUX_NOIRQ)
+ free_irq(ahd->platform_data->irq, ahd);
+ if (ahd->tags[0] == BUS_SPACE_PIO
+ && ahd->bshs[0].ioport != 0)
+ release_region(ahd->bshs[0].ioport, 256);
+ if (ahd->tags[1] == BUS_SPACE_PIO
+ && ahd->bshs[1].ioport != 0)
+ release_region(ahd->bshs[1].ioport, 256);
+ if (ahd->tags[0] == BUS_SPACE_MEMIO
+ && ahd->bshs[0].maddr != NULL) {
+ iounmap(ahd->bshs[0].maddr);
+ release_mem_region(ahd->platform_data->mem_busaddr,
+ 0x1000);
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * In 2.4 we detach from the scsi midlayer before the PCI
+ * layer invokes our remove callback. No per-instance
+ * detach is provided, so we must reach inside the PCI
+ * subsystem's internals and detach our driver manually.
+ */
+ if (ahd->dev_softc != NULL)
+ ahd->dev_softc->driver = NULL;
+#endif
+ free(ahd->platform_data, M_DEVBUF);
+ }
+}
+
+void
+ahd_platform_init(struct ahd_softc *ahd)
+{
+ /*
+ * Lookup and commit any modified IO Cell options.
+ */
+ if (ahd->unit < NUM_ELEMENTS(aic79xx_iocell_info)) {
+ struct ahd_linux_iocell_opts *iocell_opts;
+
+ iocell_opts = &aic79xx_iocell_info[ahd->unit];
+ if (iocell_opts->precomp != AIC79XX_DEFAULT_PRECOMP)
+ AHD_SET_PRECOMP(ahd, iocell_opts->precomp);
+ if (iocell_opts->slewrate != AIC79XX_DEFAULT_SLEWRATE)
+ AHD_SET_SLEWRATE(ahd, iocell_opts->slewrate);
+ if (iocell_opts->amplitude != AIC79XX_DEFAULT_AMPLITUDE)
+ AHD_SET_AMPLITUDE(ahd, iocell_opts->amplitude);
+ }
+
+}
+
+void
+ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+ ahd_platform_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+ SCB_GET_CHANNEL(ahd, scb),
+ SCB_GET_LUN(scb), SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+ ahd_queue_alg alg)
+{
+ struct ahd_linux_device *dev;
+ int was_queuing;
+ int now_queuing;
+
+ dev = ahd_linux_get_device(ahd, devinfo->channel - 'A',
+ devinfo->target,
+ devinfo->lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ return;
+ was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED);
+ switch (alg) {
+ default:
+ case AHD_QUEUE_NONE:
+ now_queuing = 0;
+ break;
+ case AHD_QUEUE_BASIC:
+ now_queuing = AHD_DEV_Q_BASIC;
+ break;
+ case AHD_QUEUE_TAGGED:
+ now_queuing = AHD_DEV_Q_TAGGED;
+ break;
+ }
+ if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) == 0
+ && (was_queuing != now_queuing)
+ && (dev->active != 0)) {
+ dev->flags |= AHD_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen++;
+ }
+
+ dev->flags &= ~(AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED|AHD_DEV_PERIODIC_OTAG);
+ if (now_queuing) {
+ u_int usertags;
+
+ usertags = ahd_linux_user_tagdepth(ahd, devinfo);
+ if (!was_queuing) {
+ /*
+ * Start out agressively and allow our
+ * dynamic queue depth algorithm to take
+ * care of the rest.
+ */
+ dev->maxtags = usertags;
+ dev->openings = dev->maxtags - dev->active;
+ }
+ if (dev->maxtags == 0) {
+ /*
+ * Queueing is disabled by the user.
+ */
+ dev->openings = 1;
+ } else if (alg == AHD_QUEUE_TAGGED) {
+ dev->flags |= AHD_DEV_Q_TAGGED;
+ if (aic79xx_periodic_otag != 0)
+ dev->flags |= AHD_DEV_PERIODIC_OTAG;
+ } else
+ dev->flags |= AHD_DEV_Q_BASIC;
+ } else {
+ /* We can only have one opening. */
+ dev->maxtags = 0;
+ dev->openings = 1 - dev->active;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ if (dev->scsi_device != NULL) {
+ switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) {
+ case AHD_DEV_Q_BASIC:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_SIMPLE_TASK,
+ dev->openings + dev->active);
+ break;
+ case AHD_DEV_Q_TAGGED:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_ORDERED_TASK,
+ dev->openings + dev->active);
+ break;
+ default:
+ /*
+ * We allow the OS to queue 2 untagged transactions to
+ * us at any time even though we can only execute them
+ * serially on the controller/device. This should
+ * remove some latency.
+ */
+ scsi_adjust_queue_depth(dev->scsi_device,
+ /*NON-TAGGED*/0,
+ /*queue depth*/2);
+ break;
+ }
+ }
+#endif
+}
+
+int
+ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ int targ;
+ int maxtarg;
+ int maxlun;
+ int clun;
+ int count;
+
+ if (tag != SCB_LIST_NULL)
+ return (0);
+
+ targ = 0;
+ if (target != CAM_TARGET_WILDCARD) {
+ targ = target;
+ maxtarg = targ + 1;
+ } else {
+ maxtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+ }
+ clun = 0;
+ if (lun != CAM_LUN_WILDCARD) {
+ clun = lun;
+ maxlun = clun + 1;
+ } else {
+ maxlun = AHD_NUM_LUNS;
+ }
+
+ count = 0;
+ for (; targ < maxtarg; targ++) {
+
+ for (; clun < maxlun; clun++) {
+ struct ahd_linux_device *dev;
+ struct ahd_busyq *busyq;
+ struct ahd_cmd *acmd;
+
+ dev = ahd_linux_get_device(ahd, /*chan*/0, targ,
+ clun, /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ busyq = &dev->busyq;
+ while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ TAILQ_REMOVE(busyq, acmd,
+ acmd_links.tqe);
+ count++;
+ cmd->result = status << 16;
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+ }
+ }
+ }
+
+ return (count);
+}
+
+static void
+ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd)
+{
+ u_long flags;
+
+ ahd_lock(ahd, &flags);
+ del_timer(&ahd->platform_data->completeq_timer);
+ ahd->platform_data->flags &= ~AHD_RUN_CMPLT_Q_TIMER;
+ ahd_linux_run_complete_queue(ahd);
+ ahd_unlock(ahd, &flags);
+}
+
+static void
+ahd_linux_start_dv(struct ahd_softc *ahd)
+{
+
+ /*
+ * Freeze the simq and signal ahd_linux_queue to not let any
+ * more commands through
+ */
+ if ((ahd->platform_data->flags & AHD_DV_ACTIVE) == 0) {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s: Starting DV\n", ahd_name(ahd));
+#endif
+
+ ahd->platform_data->flags |= AHD_DV_ACTIVE;
+ ahd_freeze_simq(ahd);
+
+ /* Wake up the DV kthread */
+ up(&ahd->platform_data->dv_sem);
+ }
+}
+
+static int
+ahd_linux_dv_thread(void *data)
+{
+ struct ahd_softc *ahd;
+ int target;
+ u_long s;
+
+ ahd = (struct ahd_softc *)data;
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("In DV Thread\n");
+#endif
+
+ /*
+ * Complete thread creation.
+ */
+ lock_kernel();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60)
+ /*
+ * Don't care about any signals.
+ */
+ siginitsetinv(&current->blocked, 0);
+
+ daemonize();
+ sprintf(current->comm, "ahd_dv_%d", ahd->unit);
+#else
+ daemonize("ahd_dv_%d", ahd->unit);
+ current->flags |= PF_FREEZE;
+#endif
+ unlock_kernel();
+
+ while (1) {
+ /*
+ * Use down_interruptible() rather than down() to
+ * avoid inclusion in the load average.
+ */
+ down_interruptible(&ahd->platform_data->dv_sem);
+
+ /* Check to see if we've been signaled to exit */
+ ahd_lock(ahd, &s);
+ if ((ahd->platform_data->flags & AHD_DV_SHUTDOWN) != 0) {
+ ahd_unlock(ahd, &s);
+ break;
+ }
+ ahd_unlock(ahd, &s);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s: Beginning Domain Validation\n",
+ ahd_name(ahd));
+#endif
+
+ /*
+ * Wait for any pending commands to drain before proceeding.
+ */
+ ahd_lock(ahd, &s);
+ while (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+ ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_EMPTY;
+ ahd_unlock(ahd, &s);
+ down_interruptible(&ahd->platform_data->dv_sem);
+ ahd_lock(ahd, &s);
+ }
+
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ while (AHD_DV_SIMQ_FROZEN(ahd) == 0) {
+ ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE;
+ ahd_unlock(ahd, &s);
+ down_interruptible(&ahd->platform_data->dv_sem);
+ ahd_lock(ahd, &s);
+ }
+ ahd_unlock(ahd, &s);
+
+ for (target = 0; target < AHD_NUM_TARGETS; target++)
+ ahd_linux_dv_target(ahd, target);
+
+ ahd_lock(ahd, &s);
+ ahd->platform_data->flags &= ~AHD_DV_ACTIVE;
+ ahd_unlock(ahd, &s);
+
+ /*
+ * Release the SIMQ so that normal commands are
+ * allowed to continue on the bus.
+ */
+ ahd_release_simq(ahd);
+ }
+ up(&ahd->platform_data->eh_sem);
+ return (0);
+}
+
+static void
+ahd_linux_kill_dv_thread(struct ahd_softc *ahd)
+{
+ u_long s;
+
+ ahd_lock(ahd, &s);
+ if (ahd->platform_data->dv_pid != 0) {
+ ahd->platform_data->flags |= AHD_DV_SHUTDOWN;
+ ahd_unlock(ahd, &s);
+ up(&ahd->platform_data->dv_sem);
+
+ /*
+ * Use the eh_sem as an indicator that the
+ * dv thread is exiting. Note that the dv
+ * thread must still return after performing
+ * the up on our semaphore before it has
+ * completely exited this module. Unfortunately,
+ * there seems to be no easy way to wait for the
+ * exit of a thread for which you are not the
+ * parent (dv threads are parented by init).
+ * Cross your fingers...
+ */
+ down(&ahd->platform_data->eh_sem);
+
+ /*
+ * Mark the dv thread as already dead. This
+ * avoids attempting to kill it a second time.
+ * This is necessary because we must kill the
+ * DV thread before calling ahd_free() in the
+ * module shutdown case to avoid bogus locking
+ * in the SCSI mid-layer, but we ahd_free() is
+ * called without killing the DV thread in the
+ * instance detach case, so ahd_platform_free()
+ * calls us again to verify that the DV thread
+ * is dead.
+ */
+ ahd->platform_data->dv_pid = 0;
+ } else {
+ ahd_unlock(ahd, &s);
+ }
+}
+
+#define AHD_LINUX_DV_INQ_SHORT_LEN 36
+#define AHD_LINUX_DV_INQ_LEN 256
+#define AHD_LINUX_DV_TIMEOUT (HZ / 4)
+
+#define AHD_SET_DV_STATE(ahd, targ, newstate) \
+ ahd_set_dv_state(ahd, targ, newstate, __LINE__)
+
+static __inline void
+ahd_set_dv_state(struct ahd_softc *ahd, struct ahd_linux_target *targ,
+ ahd_dv_state newstate, u_int line)
+{
+ ahd_dv_state oldstate;
+
+ oldstate = targ->dv_state;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s:%d: Going from state %d to state %d\n",
+ ahd_name(ahd), line, oldstate, newstate);
+#endif
+
+ if (oldstate == newstate)
+ targ->dv_state_retry++;
+ else
+ targ->dv_state_retry = 0;
+ targ->dv_state = newstate;
+}
+
+static void
+ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset)
+{
+ struct ahd_devinfo devinfo;
+ struct ahd_linux_target *targ;
+ struct scsi_cmnd *cmd;
+ struct scsi_device *scsi_dev;
+ struct scsi_sense_data *sense;
+ uint8_t *buffer;
+ u_long s;
+ u_int timeout;
+ int echo_size;
+
+ sense = NULL;
+ buffer = NULL;
+ echo_size = 0;
+ ahd_lock(ahd, &s);
+ targ = ahd->platform_data->targets[target_offset];
+ if (targ == NULL || (targ->flags & AHD_DV_REQUIRED) == 0) {
+ ahd_unlock(ahd, &s);
+ return;
+ }
+ ahd_compile_devinfo(&devinfo, ahd->our_id, targ->target, /*lun*/0,
+ targ->channel + 'A', ROLE_INITIATOR);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Performing DV\n");
+ }
+#endif
+
+ ahd_unlock(ahd, &s);
+
+ cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK);
+ scsi_dev = malloc(sizeof(struct scsi_device), M_DEVBUF, M_WAITOK);
+ scsi_dev->host = ahd->platform_data->host;
+ scsi_dev->id = devinfo.target;
+ scsi_dev->lun = devinfo.lun;
+ scsi_dev->channel = devinfo.channel - 'A';
+ ahd->platform_data->dv_scsi_dev = scsi_dev;
+
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_SHORT_ASYNC);
+
+ while (targ->dv_state != AHD_DV_STATE_EXIT) {
+ timeout = AHD_LINUX_DV_TIMEOUT;
+ switch (targ->dv_state) {
+ case AHD_DV_STATE_INQ_SHORT_ASYNC:
+ case AHD_DV_STATE_INQ_ASYNC:
+ case AHD_DV_STATE_INQ_ASYNC_VERIFY:
+ /*
+ * Set things to async narrow to reduce the
+ * chance that the INQ will fail.
+ */
+ ahd_lock(ahd, &s);
+ ahd_set_syncrate(ahd, &devinfo, 0, 0, 0,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_unlock(ahd, &s);
+ timeout = 10 * HZ;
+ targ->flags &= ~AHD_INQ_VALID;
+ /* FALLTHROUGH */
+ case AHD_DV_STATE_INQ_VERIFY:
+ {
+ u_int inq_len;
+
+ if (targ->dv_state == AHD_DV_STATE_INQ_SHORT_ASYNC)
+ inq_len = AHD_LINUX_DV_INQ_SHORT_LEN;
+ else
+ inq_len = targ->inq_data->additional_length + 5;
+ ahd_linux_dv_inq(ahd, cmd, &devinfo, targ, inq_len);
+ break;
+ }
+ case AHD_DV_STATE_TUR:
+ case AHD_DV_STATE_BUSY:
+ timeout = 5 * HZ;
+ ahd_linux_dv_tur(ahd, cmd, &devinfo);
+ break;
+ case AHD_DV_STATE_REBD:
+ ahd_linux_dv_rebd(ahd, cmd, &devinfo, targ);
+ break;
+ case AHD_DV_STATE_WEB:
+ ahd_linux_dv_web(ahd, cmd, &devinfo, targ);
+ break;
+
+ case AHD_DV_STATE_REB:
+ ahd_linux_dv_reb(ahd, cmd, &devinfo, targ);
+ break;
+
+ case AHD_DV_STATE_SU:
+ ahd_linux_dv_su(ahd, cmd, &devinfo, targ);
+ timeout = 50 * HZ;
+ break;
+
+ default:
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Unknown DV state %d\n", targ->dv_state);
+ goto out;
+ }
+
+ /* Queue the command and wait for it to complete */
+ /* Abuse eh_timeout in the scsi_cmnd struct for our purposes */
+ init_timer(&cmd->eh_timeout);
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+ /*
+ * All of the printfs during negotiation
+ * really slow down the negotiation.
+ * Add a bit of time just to be safe.
+ */
+ timeout += HZ;
+#endif
+ scsi_add_timer(cmd, timeout, ahd_linux_dv_timeout);
+ /*
+ * In 2.5.X, it is assumed that all calls from the
+ * "midlayer" (which we are emulating) will have the
+ * ahd host lock held. For other kernels, the
+ * io_request_lock must be held.
+ */
+#if AHD_SCSI_HAS_HOST_LOCK != 0
+ ahd_lock(ahd, &s);
+#else
+ spin_lock_irqsave(&io_request_lock, s);
+#endif
+ ahd_linux_queue(cmd, ahd_linux_dv_complete);
+#if AHD_SCSI_HAS_HOST_LOCK != 0
+ ahd_unlock(ahd, &s);
+#else
+ spin_unlock_irqrestore(&io_request_lock, s);
+#endif
+ down_interruptible(&ahd->platform_data->dv_cmd_sem);
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ ahd_lock(ahd, &s);
+ while (AHD_DV_SIMQ_FROZEN(ahd) == 0) {
+ ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE;
+ ahd_unlock(ahd, &s);
+ down_interruptible(&ahd->platform_data->dv_sem);
+ ahd_lock(ahd, &s);
+ }
+ ahd_unlock(ahd, &s);
+
+ ahd_linux_dv_transition(ahd, cmd, &devinfo, targ);
+ }
+
+out:
+ if ((targ->flags & AHD_INQ_VALID) != 0
+ && ahd_linux_get_device(ahd, devinfo.channel - 'A',
+ devinfo.target, devinfo.lun,
+ /*alloc*/FALSE) == NULL) {
+ /*
+ * The DV state machine failed to configure this device.
+ * This is normal if DV is disabled. Since we have inquiry
+ * data, filter it and use the "optimistic" negotiation
+ * parameters found in the inquiry string.
+ */
+ ahd_linux_filter_inquiry(ahd, &devinfo);
+ if ((targ->flags & (AHD_BASIC_DV|AHD_ENHANCED_DV)) != 0) {
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("DV failed to configure device. "
+ "Please file a bug report against "
+ "this driver.\n");
+ }
+ }
+
+ if (cmd != NULL)
+ free(cmd, M_DEVBUF);
+
+ if (ahd->platform_data->dv_scsi_dev != NULL) {
+ free(ahd->platform_data->dv_scsi_dev, M_DEVBUF);
+ ahd->platform_data->dv_scsi_dev = NULL;
+ }
+
+ ahd_lock(ahd, &s);
+ if (targ->dv_buffer != NULL) {
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = NULL;
+ }
+ if (targ->dv_buffer1 != NULL) {
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = NULL;
+ }
+ targ->flags &= ~AHD_DV_REQUIRED;
+ if (targ->refcount == 0)
+ ahd_linux_free_target(ahd, targ);
+ ahd_unlock(ahd, &s);
+}
+
+static __inline int
+ahd_linux_dv_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ u_long s;
+ int retval;
+
+ ahd_lock(ahd, &s);
+ retval = ahd_linux_fallback(ahd, devinfo);
+ ahd_unlock(ahd, &s);
+
+ return (retval);
+}
+
+static void
+ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ)
+{
+ u_int32_t status;
+
+ status = aic_error_action(cmd, targ->inq_data,
+ ahd_cmd_get_transaction_status(cmd),
+ ahd_cmd_get_scsi_status(cmd));
+
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Entering ahd_linux_dv_transition, state= %d, "
+ "status= 0x%x, cmd->result= 0x%x\n", targ->dv_state,
+ status, cmd->result);
+ }
+#endif
+
+ switch (targ->dv_state) {
+ case AHD_DV_STATE_INQ_SHORT_ASYNC:
+ case AHD_DV_STATE_INQ_ASYNC:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHD_DV_STATE_INQ_ASYNC_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ u_int xportflags;
+ u_int spi3data;
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHD_LINUX_DV_INQ_LEN) != 0) {
+ /*
+ * Inquiry data must have changed.
+ * Try from the top again.
+ */
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ }
+
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1);
+ targ->flags |= AHD_INQ_VALID;
+ if (ahd_linux_user_dv_setting(ahd) == 0)
+ break;
+
+ xportflags = targ->inq_data->flags;
+ if ((xportflags & (SID_Sync|SID_WBus16)) == 0)
+ break;
+
+ spi3data = targ->inq_data->spi3data;
+ switch (spi3data & SID_SPI_CLOCK_DT_ST) {
+ default:
+ case SID_SPI_CLOCK_ST:
+ /* Assume only basic DV is supported. */
+ targ->flags |= AHD_BASIC_DV;
+ break;
+ case SID_SPI_CLOCK_DT:
+ case SID_SPI_CLOCK_DT_ST:
+ targ->flags |= AHD_ENHANCED_DV;
+ break;
+ }
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHD_DV_STATE_INQ_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHD_LINUX_DV_INQ_LEN) == 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ int i;
+
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Inquiry buffer mismatch:");
+ for (i = 0; i < AHD_LINUX_DV_INQ_LEN; i++) {
+ if ((i & 0xF) == 0)
+ printf("\n ");
+ printf("0x%x:0x0%x ",
+ ((uint8_t *)targ->inq_data)[i],
+ targ->dv_buffer[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ } else if ((status & SS_ERRMASK) == EBUSY)
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_TUR:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ if ((targ->flags & AHD_BASIC_DV) != 0) {
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_VERIFY);
+ } else if ((targ->flags & AHD_ENHANCED_DV) != 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REBD);
+ } else {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ }
+ break;
+ case SS_RETRY:
+ case SS_TUR:
+ if ((status & SS_ERRMASK) == EBUSY) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY);
+ break;
+ }
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry >= 10) {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV TUR reties exhausted\n");
+ }
+#endif
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ if (status & SSQ_DELAY)
+ ssleep(1);
+
+ break;
+ case SS_START:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_SU);
+ break;
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_REBD:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ uint32_t echo_size;
+
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_WEB);
+ echo_size = scsi_3btoul(&targ->dv_buffer[1]);
+ echo_size &= 0x1FFF;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Echo buffer size= %d\n", echo_size);
+ }
+#endif
+ if (echo_size == 0) {
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+
+ /* Generate the buffer pattern */
+ targ->dv_echo_size = echo_size;
+ ahd_linux_generate_dv_pattern(targ);
+ /*
+ * Setup initial negotiation values.
+ */
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if (targ->dv_state_retry <= 10)
+ break;
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV REBD reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ case SS_FATAL:
+ default:
+ /*
+ * Setup initial negotiation values
+ * and try level 1 DV.
+ */
+ ahd_linux_filter_inquiry(ahd, devinfo);
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_VERIFY);
+ targ->dv_echo_size = 0;
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_WEB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REB);
+ break;
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry <= 10)
+ break;
+ /* FALLTHROUGH */
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV WEB reties exhausted\n");
+ }
+#endif
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_REB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ if (memcmp(targ->dv_buffer, targ->dv_buffer1,
+ targ->dv_echo_size) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0)
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ else
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_WEB);
+ break;
+ }
+
+ if (targ->dv_buffer != NULL) {
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = NULL;
+ }
+ if (targ->dv_buffer1 != NULL) {
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = NULL;
+ }
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahd_linux_dv_fallback(ahd, devinfo) != 0) {
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_EXIT);
+ break;
+ }
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_WEB);
+ }
+ if (targ->dv_state_retry <= 10) {
+ if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0)
+ msleep(ahd->our_id*1000/10);
+ break;
+ }
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV REB reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_SU:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHD_DV_STATE_BUSY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHD_SET_DV_STATE(ahd, targ,
+ AHD_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHD_SET_DV_STATE(ahd, targ, targ->dv_state);
+ if (ahd_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if (targ->dv_state_retry < 60) {
+ if ((status & SSQ_DELAY) != 0)
+ ssleep(1);
+ } else {
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("DV BUSY reties exhausted\n");
+ }
+#endif
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ }
+ break;
+ default:
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ default:
+ printf("%s: Invalid DV completion state %d\n", ahd_name(ahd),
+ targ->dv_state);
+ AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT);
+ break;
+ }
+}
+
+static void
+ahd_linux_dv_fill_cmd(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo)
+{
+ memset(cmd, 0, sizeof(struct scsi_cmnd));
+ cmd->device = ahd->platform_data->dv_scsi_dev;
+ cmd->scsi_done = ahd_linux_dv_complete;
+}
+
+/*
+ * Synthesize an inquiry command. On the return trip, it'll be
+ * sniffed and the device transfer settings set for us.
+ */
+static void
+ahd_linux_dv_inq(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ,
+ u_int request_length)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending INQ\n");
+ }
+#endif
+ if (targ->inq_data == NULL)
+ targ->inq_data = malloc(AHD_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ if (targ->dv_state > AHD_DV_STATE_INQ_ASYNC) {
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHD_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ }
+
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = INQUIRY;
+ cmd->cmnd[4] = request_length;
+ cmd->request_bufflen = request_length;
+ if (targ->dv_state > AHD_DV_STATE_INQ_ASYNC)
+ cmd->request_buffer = targ->dv_buffer;
+ else
+ cmd->request_buffer = targ->inq_data;
+ memset(cmd->request_buffer, 0, AHD_LINUX_DV_INQ_LEN);
+}
+
+static void
+ahd_linux_dv_tur(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending TUR\n");
+ }
+#endif
+ /* Do a TUR to clear out any non-fatal transitional state */
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = TEST_UNIT_READY;
+}
+
+#define AHD_REBD_LEN 4
+
+static void
+ahd_linux_dv_rebd(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending REBD\n");
+ }
+#endif
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHD_REBD_LEN, M_DEVBUF, M_WAITOK);
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0b;
+ scsi_ulto3b(AHD_REBD_LEN, &cmd->cmnd[6]);
+ cmd->request_bufflen = AHD_REBD_LEN;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahd_linux_dv_web(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending WEB\n");
+ }
+#endif
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_WRITE;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = WRITE_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahd_linux_dv_reb(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo, struct ahd_linux_target *targ)
+{
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending REB\n");
+ }
+#endif
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer1;
+}
+
+static void
+ahd_linux_dv_su(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+ struct ahd_devinfo *devinfo,
+ struct ahd_linux_target *targ)
+{
+ u_int le;
+
+ le = SID_IS_REMOVABLE(targ->inq_data) ? SSS_LOEJ : 0;
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Sending SU\n");
+ }
+#endif
+ ahd_linux_dv_fill_cmd(ahd, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = START_STOP_UNIT;
+ cmd->cmnd[4] = le | SSS_START;
+}
+
+static int
+ahd_linux_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_transinfo *goal;
+ struct ahd_tmode_tstate *tstate;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int cur_speed;
+ u_int wide_speed;
+ u_int narrow_speed;
+ u_int fallback_speed;
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ ahd_print_devinfo(ahd, devinfo);
+ printf("Trying to fallback\n");
+ }
+#endif
+ targ = ahd->platform_data->targets[devinfo->target_offset];
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ goal = &tinfo->goal;
+ width = goal->width;
+ period = goal->period;
+ offset = goal->offset;
+ ppr_options = goal->ppr_options;
+ if (offset == 0)
+ period = AHD_ASYNC_XFER_PERIOD;
+ if (targ->dv_next_narrow_period == 0)
+ targ->dv_next_narrow_period = MAX(period, AHD_SYNCRATE_ULTRA2);
+ if (targ->dv_next_wide_period == 0)
+ targ->dv_next_wide_period = period;
+ if (targ->dv_max_width == 0)
+ targ->dv_max_width = width;
+ if (targ->dv_max_ppr_options == 0)
+ targ->dv_max_ppr_options = ppr_options;
+ if (targ->dv_last_ppr_options == 0)
+ targ->dv_last_ppr_options = ppr_options;
+
+ cur_speed = aic_calc_speed(width, period, offset, AHD_SYNCRATE_MIN);
+ wide_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_16_BIT,
+ targ->dv_next_wide_period,
+ MAX_OFFSET, AHD_SYNCRATE_MIN);
+ narrow_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_8_BIT,
+ targ->dv_next_narrow_period,
+ MAX_OFFSET, AHD_SYNCRATE_MIN);
+ fallback_speed = aic_calc_speed(width, period+1, offset,
+ AHD_SYNCRATE_MIN);
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ printf("cur_speed= %d, wide_speed= %d, narrow_speed= %d, "
+ "fallback_speed= %d\n", cur_speed, wide_speed,
+ narrow_speed, fallback_speed);
+ }
+#endif
+
+ if (cur_speed > 160000) {
+ /*
+ * Paced/DT/IU_REQ only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ } else if (cur_speed > 80000) {
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ /*
+ * Try without IU_REQ as it may be confusing
+ * an expander.
+ */
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else {
+ /*
+ * Paced/DT only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else if (cur_speed > 3300) {
+
+ /*
+ * In this range we the following
+ * options ordered from highest to
+ * lowest desireability:
+ *
+ * o Wide/DT
+ * o Wide/non-DT
+ * o Narrow at a potentally higher sync rate.
+ *
+ * All modes are tested with and without IU_REQ
+ * set since using IUs may confuse an expander.
+ */
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ /*
+ * Try going non-DT.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ } else if (targ->dv_last_ppr_options != 0) {
+ /*
+ * Try without QAS or any other PPR options.
+ * We may need a non-PPR message to work with
+ * an expander. We look at the "last PPR options"
+ * so we will perform this fallback even if the
+ * target responded to our PPR negotiation with
+ * no option bits set.
+ */
+ ppr_options = 0;
+ } else if (width == MSG_EXT_WDTR_BUS_16_BIT) {
+ /*
+ * If the next narrow speed is greater than
+ * the next wide speed, fallback to narrow.
+ * Otherwise fallback to the next DT/Wide setting.
+ * The narrow async speed will always be smaller
+ * than the wide async speed, so handle this case
+ * specifically.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ if (narrow_speed > fallback_speed
+ || period >= AHD_ASYNC_XFER_PERIOD) {
+ targ->dv_next_wide_period = period+1;
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ period = targ->dv_next_narrow_period;
+ } else {
+ period++;
+ }
+ } else if ((ahd->features & AHD_WIDE) != 0
+ && targ->dv_max_width != 0
+ && wide_speed >= fallback_speed
+ && (targ->dv_next_wide_period <= AHD_ASYNC_XFER_PERIOD
+ || period >= AHD_ASYNC_XFER_PERIOD)) {
+
+ /*
+ * We are narrow. Try falling back
+ * to the next wide speed with
+ * all supported ppr options set.
+ */
+ targ->dv_next_narrow_period = period+1;
+ width = MSG_EXT_WDTR_BUS_16_BIT;
+ period = targ->dv_next_wide_period;
+ ppr_options = targ->dv_max_ppr_options;
+ } else {
+ /* Only narrow fallback is allowed. */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else {
+ return (-1);
+ }
+ offset = MAX_OFFSET;
+ ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_PACED);
+ ahd_set_width(ahd, devinfo, width, AHD_TRANS_GOAL, FALSE);
+ if (period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ if (width == MSG_EXT_WDTR_BUS_8_BIT)
+ targ->dv_next_narrow_period = AHD_ASYNC_XFER_PERIOD;
+ else
+ targ->dv_next_wide_period = AHD_ASYNC_XFER_PERIOD;
+ }
+ ahd_set_syncrate(ahd, devinfo, period, offset,
+ ppr_options, AHD_TRANS_GOAL, FALSE);
+ targ->dv_last_ppr_options = ppr_options;
+ return (0);
+}
+
+static void
+ahd_linux_dv_timeout(struct scsi_cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+ struct scb *scb;
+ u_long flags;
+
+ ahd = *((struct ahd_softc **)cmd->device->host->hostdata);
+ ahd_lock(ahd, &flags);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV) {
+ printf("%s: Timeout while doing DV command %x.\n",
+ ahd_name(ahd), cmd->cmnd[0]);
+ ahd_dump_card_state(ahd);
+ }
+#endif
+
+ /*
+ * Guard against "done race". No action is
+ * required if we just completed.
+ */
+ if ((scb = (struct scb *)cmd->host_scribble) == NULL) {
+ ahd_unlock(ahd, &flags);
+ return;
+ }
+
+ /*
+ * Command has not completed. Mark this
+ * SCB as having failing status prior to
+ * resetting the bus, so we get the correct
+ * error code.
+ */
+ if ((scb->flags & SCB_SENSE) != 0)
+ ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ else
+ ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ ahd_reset_channel(ahd, cmd->device->channel + 'A', /*initiate*/TRUE);
+
+ /*
+ * Add a minimal bus settle delay for devices that are slow to
+ * respond after bus resets.
+ */
+ ahd_freeze_simq(ahd);
+ init_timer(&ahd->platform_data->reset_timer);
+ ahd->platform_data->reset_timer.data = (u_long)ahd;
+ ahd->platform_data->reset_timer.expires = jiffies + HZ / 2;
+ ahd->platform_data->reset_timer.function =
+ (ahd_linux_callback_t *)ahd_release_simq;
+ add_timer(&ahd->platform_data->reset_timer);
+ if (ahd_linux_next_device_to_run(ahd) != NULL)
+ ahd_schedule_runq(ahd);
+ ahd_linux_run_complete_queue(ahd);
+ ahd_unlock(ahd, &flags);
+}
+
+static void
+ahd_linux_dv_complete(struct scsi_cmnd *cmd)
+{
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)cmd->device->host->hostdata);
+
+ /* Delete the DV timer before it goes off! */
+ scsi_delete_timer(cmd);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_DV)
+ printf("%s:%c:%d: Command completed, status= 0x%x\n",
+ ahd_name(ahd), cmd->device->channel, cmd->device->id,
+ cmd->result);
+#endif
+
+ /* Wake up the state machine */
+ up(&ahd->platform_data->dv_cmd_sem);
+}
+
+static void
+ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ)
+{
+ uint16_t b;
+ u_int i;
+ u_int j;
+
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+
+ i = 0;
+
+ b = 0x0001;
+ for (j = 0 ; i < targ->dv_echo_size; j++) {
+ if (j < 32) {
+ /*
+ * 32bytes of sequential numbers.
+ */
+ targ->dv_buffer[i++] = j & 0xff;
+ } else if (j < 48) {
+ /*
+ * 32bytes of repeating 0x0000, 0xffff.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xff : 0x00;
+ } else if (j < 64) {
+ /*
+ * 32bytes of repeating 0x5555, 0xaaaa.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xaa : 0x55;
+ } else {
+ /*
+ * Remaining buffer is filled with a repeating
+ * patter of:
+ *
+ * 0xffff
+ * ~0x0001 << shifted once in each loop.
+ */
+ if (j & 0x02) {
+ if (j & 0x01) {
+ targ->dv_buffer[i++] = ~(b >> 8) & 0xff;
+ b <<= 1;
+ if (b == 0x0000)
+ b = 0x0001;
+ } else {
+ targ->dv_buffer[i++] = (~b & 0xff);
+ }
+ } else {
+ targ->dv_buffer[i++] = 0xff;
+ }
+ }
+ }
+}
+
+static u_int
+ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ static int warned_user;
+ u_int tags;
+
+ tags = 0;
+ if ((ahd->user_discenable & devinfo->target_mask) != 0) {
+ if (ahd->unit >= NUM_ELEMENTS(aic79xx_tag_info)) {
+
+ if (warned_user == 0) {
+ printf(KERN_WARNING
+"aic79xx: WARNING: Insufficient tag_info instances\n"
+"aic79xx: for installed controllers. Using defaults\n"
+"aic79xx: Please update the aic79xx_tag_info array in\n"
+"aic79xx: the aic79xx_osm.c source file.\n");
+ warned_user++;
+ }
+ tags = AHD_MAX_QUEUE;
+ } else {
+ adapter_tag_info_t *tag_info;
+
+ tag_info = &aic79xx_tag_info[ahd->unit];
+ tags = tag_info->tag_commands[devinfo->target_offset];
+ if (tags > AHD_MAX_QUEUE)
+ tags = AHD_MAX_QUEUE;
+ }
+ }
+ return (tags);
+}
+
+static u_int
+ahd_linux_user_dv_setting(struct ahd_softc *ahd)
+{
+ static int warned_user;
+ int dv;
+
+ if (ahd->unit >= NUM_ELEMENTS(aic79xx_dv_settings)) {
+
+ if (warned_user == 0) {
+ printf(KERN_WARNING
+"aic79xx: WARNING: Insufficient dv settings instances\n"
+"aic79xx: for installed controllers. Using defaults\n"
+"aic79xx: Please update the aic79xx_dv_settings array in"
+"aic79xx: the aic79xx_osm.c source file.\n");
+ warned_user++;
+ }
+ dv = -1;
+ } else {
+
+ dv = aic79xx_dv_settings[ahd->unit];
+ }
+
+ if (dv < 0) {
+ /*
+ * Apply the default.
+ */
+ dv = 1;
+ if (ahd->seep_config != 0)
+ dv = (ahd->seep_config->bios_control & CFENABLEDV);
+ }
+ return (dv);
+}
+
+static void
+ahd_linux_setup_user_rd_strm_settings(struct ahd_softc *ahd)
+{
+ static int warned_user;
+ u_int rd_strm_mask;
+ u_int target_id;
+
+ /*
+ * If we have specific read streaming info for this controller,
+ * apply it. Otherwise use the defaults.
+ */
+ if (ahd->unit >= NUM_ELEMENTS(aic79xx_rd_strm_info)) {
+
+ if (warned_user == 0) {
+
+ printf(KERN_WARNING
+"aic79xx: WARNING: Insufficient rd_strm instances\n"
+"aic79xx: for installed controllers. Using defaults\n"
+"aic79xx: Please update the aic79xx_rd_strm_info array\n"
+"aic79xx: in the aic79xx_osm.c source file.\n");
+ warned_user++;
+ }
+ rd_strm_mask = AIC79XX_CONFIGED_RD_STRM;
+ } else {
+
+ rd_strm_mask = aic79xx_rd_strm_info[ahd->unit];
+ }
+ for (target_id = 0; target_id < 16; target_id++) {
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ target_id, &tstate);
+ ahd_compile_devinfo(&devinfo, ahd->our_id, target_id,
+ CAM_LUN_WILDCARD, 'A', ROLE_INITIATOR);
+ tinfo->user.ppr_options &= ~MSG_EXT_PPR_RD_STRM;
+ if ((rd_strm_mask & devinfo.target_mask) != 0)
+ tinfo->user.ppr_options |= MSG_EXT_PPR_RD_STRM;
+ }
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahd_linux_device_queue_depth(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev)
+{
+ struct ahd_devinfo devinfo;
+ u_int tags;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+ tags = ahd_linux_user_tagdepth(ahd, &devinfo);
+ if (tags != 0
+ && dev->scsi_device != NULL
+ && dev->scsi_device->tagged_supported != 0) {
+
+ ahd_set_tags(ahd, &devinfo, AHD_QUEUE_TAGGED);
+ ahd_print_devinfo(ahd, &devinfo);
+ printf("Tagged Queuing enabled. Depth %d\n", tags);
+ } else {
+ ahd_set_tags(ahd, &devinfo, AHD_QUEUE_NONE);
+ }
+}
+
+static void
+ahd_linux_run_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev)
+{
+ struct ahd_cmd *acmd;
+ struct scsi_cmnd *cmd;
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int col_idx;
+ uint16_t mask;
+
+ if ((dev->flags & AHD_DEV_ON_RUN_LIST) != 0)
+ panic("running device on run list");
+
+ while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+ && dev->openings > 0 && dev->qfrozen == 0) {
+
+ /*
+ * Schedule us to run later. The only reason we are not
+ * running is because the whole controller Q is frozen.
+ */
+ if (ahd->platform_data->qfrozen != 0
+ && AHD_DV_SIMQ_FROZEN(ahd) == 0) {
+
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ return;
+ }
+
+ cmd = &acmd_scsi_cmd(acmd);
+
+ /*
+ * Get an scb to use.
+ */
+ tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+ cmd->device->id, &tstate);
+ if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) == 0
+ || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ col_idx = AHD_NEVER_COL_IDX;
+ } else {
+ col_idx = AHD_BUILD_COL_IDX(cmd->device->id,
+ cmd->device->lun);
+ }
+ if ((scb = ahd_get_scb(ahd, col_idx)) == NULL) {
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ ahd->flags |= AHD_RESOURCE_SHORTAGE;
+ return;
+ }
+ TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+ scb->io_ctx = cmd;
+ scb->platform_data->dev = dev;
+ hscb = scb->hscb;
+ cmd->host_scribble = (char *)scb;
+
+ /*
+ * Fill out basics of the HSCB.
+ */
+ hscb->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahd, cmd);
+ hscb->lun = cmd->device->lun;
+ scb->hscb->task_management = 0;
+ mask = SCB_GET_TARGET_MASK(ahd, scb);
+
+ if ((ahd->user_discenable & mask) != 0)
+ hscb->control |= DISCENB;
+
+ if (AHD_DV_CMD(cmd) != 0)
+ scb->flags |= SCB_SILENT;
+
+ if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0)
+ scb->flags |= SCB_PACKETIZED;
+
+ if ((tstate->auto_negotiate & mask) != 0) {
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
+ }
+
+ if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ int msg_bytes;
+ uint8_t tag_msgs[2];
+
+ msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
+ if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
+ hscb->control |= tag_msgs[0];
+ if (tag_msgs[0] == MSG_ORDERED_TASK)
+ dev->commands_since_idle_or_otag = 0;
+ } else
+#endif
+ if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH
+ && (dev->flags & AHD_DEV_Q_TAGGED) != 0) {
+ hscb->control |= MSG_ORDERED_TASK;
+ dev->commands_since_idle_or_otag = 0;
+ } else {
+ hscb->control |= MSG_SIMPLE_TASK;
+ }
+ }
+
+ hscb->cdb_len = cmd->cmd_len;
+ memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len);
+
+ scb->sg_count = 0;
+ ahd_set_residual(scb, 0);
+ ahd_set_sense_residual(scb, 0);
+ if (cmd->use_sg != 0) {
+ void *sg;
+ struct scatterlist *cur_seg;
+ u_int nseg;
+ int dir;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+ nseg = pci_map_sg(ahd->dev_softc, cur_seg,
+ cmd->use_sg, dir);
+ scb->platform_data->xfer_len = 0;
+ for (sg = scb->sg_list; nseg > 0; nseg--, cur_seg++) {
+ dma_addr_t addr;
+ bus_size_t len;
+
+ addr = sg_dma_address(cur_seg);
+ len = sg_dma_len(cur_seg);
+ scb->platform_data->xfer_len += len;
+ sg = ahd_sg_setup(ahd, scb, sg, addr, len,
+ /*last*/nseg == 1);
+ }
+ } else if (cmd->request_bufflen != 0) {
+ void *sg;
+ dma_addr_t addr;
+ int dir;
+
+ sg = scb->sg_list;
+ dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+ addr = pci_map_single(ahd->dev_softc,
+ cmd->request_buffer,
+ cmd->request_bufflen, dir);
+ scb->platform_data->xfer_len = cmd->request_bufflen;
+ scb->platform_data->buf_busaddr = addr;
+ sg = ahd_sg_setup(ahd, scb, sg, addr,
+ cmd->request_bufflen, /*last*/TRUE);
+ }
+
+ LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links);
+ dev->openings--;
+ dev->active++;
+ dev->commands_issued++;
+
+ /* Update the error counting bucket and dump if needed */
+ if (dev->target->cmds_since_error) {
+ dev->target->cmds_since_error++;
+ if (dev->target->cmds_since_error >
+ AHD_LINUX_ERR_THRESH)
+ dev->target->cmds_since_error = 0;
+ }
+
+ if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0)
+ dev->commands_since_idle_or_otag++;
+ scb->flags |= SCB_ACTIVE;
+ ahd_queue_scb(ahd, scb);
+ }
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+irqreturn_t
+ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct ahd_softc *ahd;
+ u_long flags;
+ int ours;
+
+ ahd = (struct ahd_softc *) dev_id;
+ ahd_lock(ahd, &flags);
+ ours = ahd_intr(ahd);
+ if (ahd_linux_next_device_to_run(ahd) != NULL)
+ ahd_schedule_runq(ahd);
+ ahd_linux_run_complete_queue(ahd);
+ ahd_unlock(ahd, &flags);
+ return IRQ_RETVAL(ours);
+}
+
+void
+ahd_platform_flushwork(struct ahd_softc *ahd)
+{
+
+ while (ahd_linux_run_complete_queue(ahd) != NULL)
+ ;
+}
+
+static struct ahd_linux_target*
+ahd_linux_alloc_target(struct ahd_softc *ahd, u_int channel, u_int target)
+{
+ struct ahd_linux_target *targ;
+
+ targ = malloc(sizeof(*targ), M_DEVBUF, M_NOWAIT);
+ if (targ == NULL)
+ return (NULL);
+ memset(targ, 0, sizeof(*targ));
+ targ->channel = channel;
+ targ->target = target;
+ targ->ahd = ahd;
+ targ->flags = AHD_DV_REQUIRED;
+ ahd->platform_data->targets[target] = targ;
+ return (targ);
+}
+
+static void
+ahd_linux_free_target(struct ahd_softc *ahd, struct ahd_linux_target *targ)
+{
+ struct ahd_devinfo devinfo;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_offset;
+ char channel;
+
+ /*
+ * Force a negotiation to async/narrow on any
+ * future command to this device unless a bus
+ * reset occurs between now and that command.
+ */
+ channel = 'A' + targ->channel;
+ our_id = ahd->our_id;
+ target_offset = targ->target;
+ tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+ targ->target, &tstate);
+ ahd_compile_devinfo(&devinfo, our_id, targ->target, CAM_LUN_WILDCARD,
+ channel, ROLE_INITIATOR);
+ ahd_set_syncrate(ahd, &devinfo, 0, 0, 0,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_update_neg_request(ahd, &devinfo, tstate, tinfo, AHD_NEG_ALWAYS);
+ ahd->platform_data->targets[target_offset] = NULL;
+ if (targ->inq_data != NULL)
+ free(targ->inq_data, M_DEVBUF);
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ free(targ, M_DEVBUF);
+}
+
+static struct ahd_linux_device*
+ahd_linux_alloc_device(struct ahd_softc *ahd,
+ struct ahd_linux_target *targ, u_int lun)
+{
+ struct ahd_linux_device *dev;
+
+ dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+ if (dev == NULL)
+ return (NULL);
+ memset(dev, 0, sizeof(*dev));
+ init_timer(&dev->timer);
+ TAILQ_INIT(&dev->busyq);
+ dev->flags = AHD_DEV_UNCONFIGURED;
+ dev->lun = lun;
+ dev->target = targ;
+
+ /*
+ * We start out life using untagged
+ * transactions of which we allow one.
+ */
+ dev->openings = 1;
+
+ /*
+ * Set maxtags to 0. This will be changed if we
+ * later determine that we are dealing with
+ * a tagged queuing capable device.
+ */
+ dev->maxtags = 0;
+
+ targ->refcount++;
+ targ->devices[lun] = dev;
+ return (dev);
+}
+
+static void
+ahd_linux_free_device(struct ahd_softc *ahd, struct ahd_linux_device *dev)
+{
+ struct ahd_linux_target *targ;
+
+ del_timer(&dev->timer);
+ targ = dev->target;
+ targ->devices[dev->lun] = NULL;
+ free(dev, M_DEVBUF);
+ targ->refcount--;
+ if (targ->refcount == 0
+ && (targ->flags & AHD_DV_REQUIRED) == 0)
+ ahd_linux_free_target(ahd, targ);
+}
+
+void
+ahd_send_async(struct ahd_softc *ahd, char channel,
+ u_int target, u_int lun, ac_code code, void *arg)
+{
+ switch (code) {
+ case AC_TRANSFER_NEG:
+ {
+ char buf[80];
+ struct ahd_linux_target *targ;
+ struct info_str info;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+
+ info.buffer = buf;
+ info.length = sizeof(buf);
+ info.offset = 0;
+ info.pos = 0;
+ tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id,
+ target, &tstate);
+
+ /*
+ * Don't bother reporting results while
+ * negotiations are still pending.
+ */
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ /*
+ * Don't bother reporting results that
+ * are identical to those last reported.
+ */
+ targ = ahd->platform_data->targets[target];
+ if (targ == NULL)
+ break;
+ if (tinfo->curr.period == targ->last_tinfo.period
+ && tinfo->curr.width == targ->last_tinfo.width
+ && tinfo->curr.offset == targ->last_tinfo.offset
+ && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ targ->last_tinfo.period = tinfo->curr.period;
+ targ->last_tinfo.width = tinfo->curr.width;
+ targ->last_tinfo.offset = tinfo->curr.offset;
+ targ->last_tinfo.ppr_options = tinfo->curr.ppr_options;
+
+ printf("(%s:%c:", ahd_name(ahd), channel);
+ if (target == CAM_TARGET_WILDCARD)
+ printf("*): ");
+ else
+ printf("%d): ", target);
+ ahd_format_transinfo(&info, &tinfo->curr);
+ if (info.pos < info.length)
+ *info.buffer = '\0';
+ else
+ buf[info.length - 1] = '\0';
+ printf("%s", buf);
+ break;
+ }
+ case AC_SENT_BDR:
+ {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ WARN_ON(lun != CAM_LUN_WILDCARD);
+ scsi_report_device_reset(ahd->platform_data->host,
+ channel - 'A', target);
+#else
+ Scsi_Device *scsi_dev;
+
+ /*
+ * Find the SCSI device associated with this
+ * request and indicate that a UA is expected.
+ */
+ for (scsi_dev = ahd->platform_data->host->host_queue;
+ scsi_dev != NULL; scsi_dev = scsi_dev->next) {
+ if (channel - 'A' == scsi_dev->channel
+ && target == scsi_dev->id
+ && (lun == CAM_LUN_WILDCARD
+ || lun == scsi_dev->lun)) {
+ scsi_dev->was_reset = 1;
+ scsi_dev->expecting_cc_ua = 1;
+ }
+ }
+#endif
+ break;
+ }
+ case AC_BUS_RESET:
+ if (ahd->platform_data->host != NULL) {
+ scsi_report_bus_reset(ahd->platform_data->host,
+ channel - 'A');
+ }
+ break;
+ default:
+ panic("ahd_send_async: Unexpected async event");
+ }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahd_done(struct ahd_softc *ahd, struct scb *scb)
+{
+ Scsi_Cmnd *cmd;
+ struct ahd_linux_device *dev;
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ printf("SCB %d done'd twice\n", SCB_GET_TAG(scb));
+ ahd_dump_card_state(ahd);
+ panic("Stopping for safety");
+ }
+ LIST_REMOVE(scb, pending_links);
+ cmd = scb->io_ctx;
+ dev = scb->platform_data->dev;
+ dev->active--;
+ dev->openings++;
+ if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+ cmd->result &= ~(CAM_DEV_QFRZN << 16);
+ dev->qfrozen--;
+ }
+ ahd_linux_unmap_scb(ahd, scb);
+
+ /*
+ * Guard against stale sense data.
+ * The Linux mid-layer assumes that sense
+ * was retrieved anytime the first byte of
+ * the sense buffer looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) {
+ uint32_t amount_xferred;
+
+ amount_xferred =
+ ahd_get_transfer_length(scb) - ahd_get_residual(scb);
+ if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Set CAM_UNCOR_PARITY\n");
+ }
+#endif
+ ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
+#ifdef AHD_REPORT_UNDERFLOWS
+ /*
+ * This code is disabled by default as some
+ * clients of the SCSI system do not properly
+ * initialize the underflow parameter. This
+ * results in spurious termination of commands
+ * that complete as expected (e.g. underflow is
+ * allowed as command can return variable amounts
+ * of data.
+ */
+ } else if (amount_xferred < scb->io_ctx->underflow) {
+ u_int i;
+
+ ahd_print_path(ahd, scb);
+ printf("CDB:");
+ for (i = 0; i < scb->io_ctx->cmd_len; i++)
+ printf(" 0x%x", scb->io_ctx->cmnd[i]);
+ printf("\n");
+ ahd_print_path(ahd, scb);
+ printf("Saw underflow (%ld of %ld bytes). "
+ "Treated as error\n",
+ ahd_get_residual(scb),
+ ahd_get_transfer_length(scb));
+ ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+#endif
+ } else {
+ ahd_set_transaction_status(scb, CAM_REQ_CMP);
+ }
+ } else if (ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+ ahd_linux_handle_scsi_status(ahd, dev, scb);
+ } else if (ahd_get_transaction_status(scb) == CAM_SEL_TIMEOUT) {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ if (AHD_DV_CMD(cmd) == FALSE)
+ dev->target->flags &= ~AHD_DV_REQUIRED;
+ }
+ /*
+ * Start DV for devices that require it assuming the first command
+ * sent does not result in a selection timeout.
+ */
+ if (ahd_get_transaction_status(scb) != CAM_SEL_TIMEOUT
+ && (dev->target->flags & AHD_DV_REQUIRED) != 0)
+ ahd_linux_start_dv(ahd);
+
+ if (dev->openings == 1
+ && ahd_get_transaction_status(scb) == CAM_REQ_CMP
+ && ahd_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+ dev->tag_success_count++;
+ /*
+ * Some devices deal with temporary internal resource
+ * shortages by returning queue full. When the queue
+ * full occurrs, we throttle back. Slowly try to get
+ * back to our previous queue depth.
+ */
+ if ((dev->openings + dev->active) < dev->maxtags
+ && dev->tag_success_count > AHD_TAG_SUCCESS_INTERVAL) {
+ dev->tag_success_count = 0;
+ dev->openings++;
+ }
+
+ if (dev->active == 0)
+ dev->commands_since_idle_or_otag = 0;
+
+ if (TAILQ_EMPTY(&dev->busyq)) {
+ if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0
+ && dev->active == 0
+ && (dev->flags & AHD_DEV_TIMER_ACTIVE) == 0)
+ ahd_linux_free_device(ahd, dev);
+ } else if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
+ dev->flags |= AHD_DEV_ON_RUN_LIST;
+ }
+
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ printf("Recovery SCB completes\n");
+ if (ahd_get_transaction_status(scb) == CAM_BDR_SENT
+ || ahd_get_transaction_status(scb) == CAM_REQ_ABORTED)
+ ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ if ((scb->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) {
+ scb->platform_data->flags &= ~AHD_SCB_UP_EH_SEM;
+ up(&ahd->platform_data->eh_sem);
+ }
+ }
+
+ ahd_free_scb(ahd, scb);
+ ahd_linux_queue_cmd_complete(ahd, cmd);
+
+ if ((ahd->platform_data->flags & AHD_DV_WAIT_SIMQ_EMPTY) != 0
+ && LIST_FIRST(&ahd->pending_scbs) == NULL) {
+ ahd->platform_data->flags &= ~AHD_DV_WAIT_SIMQ_EMPTY;
+ up(&ahd->platform_data->dv_sem);
+ }
+}
+
+static void
+ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
+ struct ahd_linux_device *dev, struct scb *scb)
+{
+ struct ahd_devinfo devinfo;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+
+ /*
+ * We don't currently trust the mid-layer to
+ * properly deal with queue full or busy. So,
+ * when one occurs, we tell the mid-layer to
+ * unconditionally requeue the command to us
+ * so that we can retry it ourselves. We also
+ * implement our own throttling mechanism so
+ * we don't clobber the device with too many
+ * commands.
+ */
+ switch (ahd_get_scsi_status(scb)) {
+ default:
+ break;
+ case SCSI_STATUS_CHECK_COND:
+ case SCSI_STATUS_CMD_TERMINATED:
+ {
+ Scsi_Cmnd *cmd;
+
+ /*
+ * Copy sense information to the OS's cmd
+ * structure if it is available.
+ */
+ cmd = scb->io_ctx;
+ if ((scb->flags & (SCB_SENSE|SCB_PKT_SENSE)) != 0) {
+ struct scsi_status_iu_header *siu;
+ u_int sense_size;
+ u_int sense_offset;
+
+ if (scb->flags & SCB_SENSE) {
+ sense_size = MIN(sizeof(struct scsi_sense_data)
+ - ahd_get_sense_residual(scb),
+ sizeof(cmd->sense_buffer));
+ sense_offset = 0;
+ } else {
+ /*
+ * Copy only the sense data into the provided
+ * buffer.
+ */
+ siu = (struct scsi_status_iu_header *)
+ scb->sense_data;
+ sense_size = MIN(scsi_4btoul(siu->sense_length),
+ sizeof(cmd->sense_buffer));
+ sense_offset = SIU_SENSE_OFFSET(siu);
+ }
+
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer,
+ ahd_get_sense_buf(ahd, scb)
+ + sense_offset, sense_size);
+ cmd->result |= (DRIVER_SENSE << 24);
+
+#ifdef AHD_DEBUG
+ if (ahd_debug & AHD_SHOW_SENSE) {
+ int i;
+
+ printf("Copied %d bytes of sense data at %d:",
+ sense_size, sense_offset);
+ for (i = 0; i < sense_size; i++) {
+ if ((i & 0xF) == 0)
+ printf("\n");
+ printf("0x%x ", cmd->sense_buffer[i]);
+ }
+ printf("\n");
+ }
+#endif
+ }
+ break;
+ }
+ case SCSI_STATUS_QUEUE_FULL:
+ {
+ /*
+ * By the time the core driver has returned this
+ * command, all other commands that were queued
+ * to us but not the device have been returned.
+ * This ensures that dev->active is equal to
+ * the number of commands actually queued to
+ * the device.
+ */
+ dev->tag_success_count = 0;
+ if (dev->active != 0) {
+ /*
+ * Drop our opening count to the number
+ * of commands currently outstanding.
+ */
+ dev->openings = 0;
+#ifdef AHD_DEBUG
+ if ((ahd_debug & AHD_SHOW_QFULL) != 0) {
+ ahd_print_path(ahd, scb);
+ printf("Dropping tag count to %d\n",
+ dev->active);
+ }
+#endif
+ if (dev->active == dev->tags_on_last_queuefull) {
+
+ dev->last_queuefull_same_count++;
+ /*
+ * If we repeatedly see a queue full
+ * at the same queue depth, this
+ * device has a fixed number of tag
+ * slots. Lock in this tag depth
+ * so we stop seeing queue fulls from
+ * this device.
+ */
+ if (dev->last_queuefull_same_count
+ == AHD_LOCK_TAGS_COUNT) {
+ dev->maxtags = dev->active;
+ ahd_print_path(ahd, scb);
+ printf("Locking max tag count at %d\n",
+ dev->active);
+ }
+ } else {
+ dev->tags_on_last_queuefull = dev->active;
+ dev->last_queuefull_same_count = 0;
+ }
+ ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahd_set_scsi_status(scb, SCSI_STATUS_OK);
+ ahd_platform_set_tags(ahd, &devinfo,
+ (dev->flags & AHD_DEV_Q_BASIC)
+ ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
+ break;
+ }
+ /*
+ * Drop down to a single opening, and treat this
+ * as if the target returned BUSY SCSI status.
+ */
+ dev->openings = 1;
+ ahd_platform_set_tags(ahd, &devinfo,
+ (dev->flags & AHD_DEV_Q_BASIC)
+ ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
+ ahd_set_scsi_status(scb, SCSI_STATUS_BUSY);
+ /* FALLTHROUGH */
+ }
+ case SCSI_STATUS_BUSY:
+ /*
+ * Set a short timer to defer sending commands for
+ * a bit since Linux will not delay in this case.
+ */
+ if ((dev->flags & AHD_DEV_TIMER_ACTIVE) != 0) {
+ printf("%s:%c:%d: Device Timer still active during "
+ "busy processing\n", ahd_name(ahd),
+ dev->target->channel, dev->target->target);
+ break;
+ }
+ dev->flags |= AHD_DEV_TIMER_ACTIVE;
+ dev->qfrozen++;
+ init_timer(&dev->timer);
+ dev->timer.data = (u_long)dev;
+ dev->timer.expires = jiffies + (HZ/2);
+ dev->timer.function = ahd_linux_dev_timed_unfreeze;
+ add_timer(&dev->timer);
+ break;
+ }
+}
+
+static void
+ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, Scsi_Cmnd *cmd)
+{
+ /*
+ * Typically, the complete queue has very few entries
+ * queued to it before the queue is emptied by
+ * ahd_linux_run_complete_queue, so sorting the entries
+ * by generation number should be inexpensive.
+ * We perform the sort so that commands that complete
+ * with an error are retuned in the order origionally
+ * queued to the controller so that any subsequent retries
+ * are performed in order. The underlying ahd routines do
+ * not guarantee the order that aborted commands will be
+ * returned to us.
+ */
+ struct ahd_completeq *completeq;
+ struct ahd_cmd *list_cmd;
+ struct ahd_cmd *acmd;
+
+ /*
+ * Map CAM error codes into Linux Error codes. We
+ * avoid the conversion so that the DV code has the
+ * full error information available when making
+ * state change decisions.
+ */
+ if (AHD_DV_CMD(cmd) == FALSE) {
+ uint32_t status;
+ u_int new_status;
+
+ status = ahd_cmd_get_transaction_status(cmd);
+ if (status != CAM_REQ_CMP) {
+ struct ahd_linux_device *dev;
+ struct ahd_devinfo devinfo;
+ cam_status cam_status;
+ uint32_t action;
+ u_int scsi_status;
+
+ dev = ahd_linux_get_device(ahd, cmd->device->channel,
+ cmd->device->id,
+ cmd->device->lun,
+ /*alloc*/FALSE);
+
+ if (dev == NULL)
+ goto no_fallback;
+
+ ahd_compile_devinfo(&devinfo,
+ ahd->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A':'B',
+ ROLE_INITIATOR);
+
+ scsi_status = ahd_cmd_get_scsi_status(cmd);
+ cam_status = ahd_cmd_get_transaction_status(cmd);
+ action = aic_error_action(cmd, dev->target->inq_data,
+ cam_status, scsi_status);
+ if ((action & SSQ_FALLBACK) != 0) {
+
+ /* Update stats */
+ dev->target->errors_detected++;
+ if (dev->target->cmds_since_error == 0)
+ dev->target->cmds_since_error++;
+ else {
+ dev->target->cmds_since_error = 0;
+ ahd_linux_fallback(ahd, &devinfo);
+ }
+ }
+ }
+no_fallback:
+ switch (status) {
+ case CAM_REQ_INPROG:
+ case CAM_REQ_CMP:
+ case CAM_SCSI_STATUS_ERROR:
+ new_status = DID_OK;
+ break;
+ case CAM_REQ_ABORTED:
+ new_status = DID_ABORT;
+ break;
+ case CAM_BUSY:
+ new_status = DID_BUS_BUSY;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ new_status = DID_BAD_TARGET;
+ break;
+ case CAM_SEL_TIMEOUT:
+ new_status = DID_NO_CONNECT;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ new_status = DID_RESET;
+ break;
+ case CAM_UNCOR_PARITY:
+ new_status = DID_PARITY;
+ break;
+ case CAM_CMD_TIMEOUT:
+ new_status = DID_TIME_OUT;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_REQ_CMP_ERR:
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_NO_HBA:
+ case CAM_DATA_RUN_ERR:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_SEQUENCE_FAIL:
+ case CAM_CCB_LEN_ERR:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TERMIO:
+ case CAM_UNREC_HBA_ERROR:
+ case CAM_REQ_TOO_BIG:
+ new_status = DID_ERROR;
+ break;
+ case CAM_REQUEUE_REQ:
+ /*
+ * If we want the request requeued, make sure there
+ * are sufficent retries. In the old scsi error code,
+ * we used to be able to specify a result code that
+ * bypassed the retry count. Now we must use this
+ * hack. We also "fake" a check condition with
+ * a sense code of ABORTED COMMAND. This seems to
+ * evoke a retry even if this command is being sent
+ * via the eh thread. Ick! Ick! Ick!
+ */
+ if (cmd->retries > 0)
+ cmd->retries--;
+ new_status = DID_OK;
+ ahd_cmd_set_scsi_status(cmd, SCSI_STATUS_CHECK_COND);
+ cmd->result |= (DRIVER_SENSE << 24);
+ memset(cmd->sense_buffer, 0,
+ sizeof(cmd->sense_buffer));
+ cmd->sense_buffer[0] = SSD_ERRCODE_VALID
+ | SSD_CURRENT_ERROR;
+ cmd->sense_buffer[2] = SSD_KEY_ABORTED_COMMAND;
+ break;
+ default:
+ /* We should never get here */
+ new_status = DID_ERROR;
+ break;
+ }
+
+ ahd_cmd_set_transaction_status(cmd, new_status);
+ }
+
+ completeq = &ahd->platform_data->completeq;
+ list_cmd = TAILQ_FIRST(completeq);
+ acmd = (struct ahd_cmd *)cmd;
+ while (list_cmd != NULL
+ && acmd_scsi_cmd(list_cmd).serial_number
+ < acmd_scsi_cmd(acmd).serial_number)
+ list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+ if (list_cmd != NULL)
+ TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+ else
+ TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static void
+ahd_linux_filter_inquiry(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+ struct scsi_inquiry_data *sid;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_transinfo *user;
+ struct ahd_transinfo *goal;
+ struct ahd_transinfo *curr;
+ struct ahd_tmode_tstate *tstate;
+ struct ahd_linux_device *dev;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int trans_version;
+ u_int prot_version;
+
+ /*
+ * Determine if this lun actually exists. If so,
+ * hold on to its corresponding device structure.
+ * If not, make sure we release the device and
+ * don't bother processing the rest of this inquiry
+ * command.
+ */
+ dev = ahd_linux_get_device(ahd, devinfo->channel - 'A',
+ devinfo->target, devinfo->lun,
+ /*alloc*/TRUE);
+
+ sid = (struct scsi_inquiry_data *)dev->target->inq_data;
+ if (SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+ dev->flags &= ~AHD_DEV_UNCONFIGURED;
+ } else {
+ dev->flags |= AHD_DEV_UNCONFIGURED;
+ return;
+ }
+
+ /*
+ * Update our notion of this device's transfer
+ * negotiation capabilities.
+ */
+ tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ user = &tinfo->user;
+ goal = &tinfo->goal;
+ curr = &tinfo->curr;
+ width = user->width;
+ period = user->period;
+ offset = user->offset;
+ ppr_options = user->ppr_options;
+ trans_version = user->transport_version;
+ prot_version = MIN(user->protocol_version, SID_ANSI_REV(sid));
+
+ /*
+ * Only attempt SPI3/4 once we've verified that
+ * the device claims to support SPI3/4 features.
+ */
+ if (prot_version < SCSI_REV_2)
+ trans_version = SID_ANSI_REV(sid);
+ else
+ trans_version = SCSI_REV_2;
+
+ if ((sid->flags & SID_WBus16) == 0)
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ if ((sid->flags & SID_Sync) == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ if ((sid->spi3data & SID_SPI_QAS) == 0)
+ ppr_options &= ~MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+ ppr_options &= MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_IUS) == 0)
+ ppr_options &= (MSG_EXT_PPR_DT_REQ
+ | MSG_EXT_PPR_QAS_REQ);
+
+ if (prot_version > SCSI_REV_2
+ && ppr_options != 0)
+ trans_version = user->transport_version;
+
+ ahd_validate_width(ahd, /*tinfo limit*/NULL, &width, ROLE_UNKNOWN);
+ ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_MAX);
+ ahd_validate_offset(ahd, /*tinfo limit*/NULL, period,
+ &offset, width, ROLE_UNKNOWN);
+ if (offset == 0 || period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ /* Apply our filtered user settings. */
+ curr->transport_version = trans_version;
+ curr->protocol_version = prot_version;
+ ahd_set_width(ahd, devinfo, width, AHD_TRANS_GOAL, /*paused*/FALSE);
+ ahd_set_syncrate(ahd, devinfo, period, offset, ppr_options,
+ AHD_TRANS_GOAL, /*paused*/FALSE);
+}
+
+void
+ahd_freeze_simq(struct ahd_softc *ahd)
+{
+ ahd->platform_data->qfrozen++;
+ if (ahd->platform_data->qfrozen == 1) {
+ scsi_block_requests(ahd->platform_data->host);
+ ahd_platform_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ);
+ }
+}
+
+void
+ahd_release_simq(struct ahd_softc *ahd)
+{
+ u_long s;
+ int unblock_reqs;
+
+ unblock_reqs = 0;
+ ahd_lock(ahd, &s);
+ if (ahd->platform_data->qfrozen > 0)
+ ahd->platform_data->qfrozen--;
+ if (ahd->platform_data->qfrozen == 0) {
+ unblock_reqs = 1;
+ }
+ if (AHD_DV_SIMQ_FROZEN(ahd)
+ && ((ahd->platform_data->flags & AHD_DV_WAIT_SIMQ_RELEASE) != 0)) {
+ ahd->platform_data->flags &= ~AHD_DV_WAIT_SIMQ_RELEASE;
+ up(&ahd->platform_data->dv_sem);
+ }
+ ahd_schedule_runq(ahd);
+ ahd_unlock(ahd, &s);
+ /*
+ * There is still a race here. The mid-layer
+ * should keep its own freeze count and use
+ * a bottom half handler to run the queues
+ * so we can unblock with our own lock held.
+ */
+ if (unblock_reqs)
+ scsi_unblock_requests(ahd->platform_data->host);
+}
+
+static void
+ahd_linux_sem_timeout(u_long arg)
+{
+ struct scb *scb;
+ struct ahd_softc *ahd;
+ u_long s;
+
+ scb = (struct scb *)arg;
+ ahd = scb->ahd_softc;
+ ahd_lock(ahd, &s);
+ if ((scb->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) {
+ scb->platform_data->flags &= ~AHD_SCB_UP_EH_SEM;
+ up(&ahd->platform_data->eh_sem);
+ }
+ ahd_unlock(ahd, &s);
+}
+
+static void
+ahd_linux_dev_timed_unfreeze(u_long arg)
+{
+ struct ahd_linux_device *dev;
+ struct ahd_softc *ahd;
+ u_long s;
+
+ dev = (struct ahd_linux_device *)arg;
+ ahd = dev->target->ahd;
+ ahd_lock(ahd, &s);
+ dev->flags &= ~AHD_DEV_TIMER_ACTIVE;
+ if (dev->qfrozen > 0)
+ dev->qfrozen--;
+ if (dev->qfrozen == 0
+ && (dev->flags & AHD_DEV_ON_RUN_LIST) == 0)
+ ahd_linux_run_device_queue(ahd, dev);
+ if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0
+ && dev->active == 0)
+ ahd_linux_free_device(ahd, dev);
+ ahd_unlock(ahd, &s);
+}
+
+void
+ahd_platform_dump_card_state(struct ahd_softc *ahd)
+{
+ struct ahd_linux_device *dev;
+ int target;
+ int maxtarget;
+ int lun;
+ int i;
+
+ maxtarget = (ahd->features & AHD_WIDE) ? 15 : 7;
+ for (target = 0; target <=maxtarget; target++) {
+
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_cmd *acmd;
+
+ dev = ahd_linux_get_device(ahd, 0, target,
+ lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ printf("DevQ(%d:%d:%d): ", 0, target, lun);
+ i = 0;
+ TAILQ_FOREACH(acmd, &dev->busyq, acmd_links.tqe) {
+ if (i++ > AHD_SCB_MAX)
+ break;
+ }
+ printf("%d waiting\n", i);
+ }
+ }
+}
+
+static int __init
+ahd_linux_init(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ return ahd_linux_detect(&aic79xx_driver_template);
+#else
+ scsi_register_module(MODULE_SCSI_HA, &aic79xx_driver_template);
+ if (aic79xx_driver_template.present == 0) {
+ scsi_unregister_module(MODULE_SCSI_HA,
+ &aic79xx_driver_template);
+ return (-ENODEV);
+ }
+
+ return (0);
+#endif
+}
+
+static void __exit
+ahd_linux_exit(void)
+{
+ struct ahd_softc *ahd;
+
+ /*
+ * Shutdown DV threads before going into the SCSI mid-layer.
+ * This avoids situations where the mid-layer locks the entire
+ * kernel so that waiting for our DV threads to exit leads
+ * to deadlock.
+ */
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+
+ ahd_linux_kill_dv_thread(ahd);
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * In 2.4 we have to unregister from the PCI core _after_
+ * unregistering from the scsi midlayer to avoid dangling
+ * references.
+ */
+ scsi_unregister_module(MODULE_SCSI_HA, &aic79xx_driver_template);
+#endif
+ ahd_linux_pci_exit();
+}
+
+module_init(ahd_linux_init);
+module_exit(ahd_linux_exit);
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h
new file mode 100644
index 000000000000..605f92b6c5ca
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -0,0 +1,1147 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#137 $
+ *
+ */
+#ifndef _AIC79XX_LINUX_H_
+#define _AIC79XX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include <linux/interrupt.h> /* For tasklet support. */
+#include <linux/config.h>
+#include <linux/slab.h>
+
+/* Core SCSI definitions */
+#define AIC_LIB_PREFIX ahd
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+#include "scsi_iu.h"
+#include "aiclib.h"
+
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC79XX_DEBUG_ENABLE
+#ifdef CONFIG_AIC79XX_DEBUG_MASK
+#define AHD_DEBUG 1
+#define AHD_DEBUG_OPTS CONFIG_AIC79XX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHD_DEBUG 1
+#define AHD_DEBUG_OPTS 0
+#endif
+/* No debugging code. */
+#endif
+
+/********************************** Misc Macros *******************************/
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define powerof2(x) ((((x)-1)&(x))==0)
+
+/************************* Forward Declarations *******************************/
+struct ahd_softc;
+typedef struct pci_dev *ahd_dev_softc_t;
+typedef Scsi_Cmnd *ahd_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahd_htobe16(x) cpu_to_be16(x)
+#define ahd_htobe32(x) cpu_to_be32(x)
+#define ahd_htobe64(x) cpu_to_be64(x)
+#define ahd_htole16(x) cpu_to_le16(x)
+#define ahd_htole32(x) cpu_to_le32(x)
+#define ahd_htole64(x) cpu_to_le64(x)
+
+#define ahd_be16toh(x) be16_to_cpu(x)
+#define ahd_be32toh(x) be32_to_cpu(x)
+#define ahd_be64toh(x) be64_to_cpu(x)
+#define ahd_le16toh(x) le16_to_cpu(x)
+#define ahd_le32toh(x) le32_to_cpu(x)
+#define ahd_le64toh(x) le64_to_cpu(x)
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#if defined(__BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#if defined(__LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+/************************* Configuration Data *********************************/
+extern uint32_t aic79xx_allow_memio;
+extern int aic79xx_detect_complete;
+extern Scsi_Host_Template aic79xx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+typedef uint32_t bus_size_t;
+
+typedef enum {
+ BUS_SPACE_MEMIO,
+ BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+ u_long ioport;
+ volatile uint8_t __iomem *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+ dma_addr_t ds_addr;
+ bus_size_t ds_len;
+} bus_dma_segment_t;
+
+struct ahd_linux_dma_tag
+{
+ bus_size_t alignment;
+ bus_size_t boundary;
+ bus_size_t maxsize;
+};
+typedef struct ahd_linux_dma_tag* bus_dma_tag_t;
+
+struct ahd_linux_dmamap
+{
+ dma_addr_t bus_addr;
+};
+typedef struct ahd_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, dma_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK 0x0
+#define BUS_DMA_NOWAIT 0x1
+#define BUS_DMA_ALLOCNOW 0x2
+#define BUS_DMA_LOAD_SEGS 0x4 /*
+ * Argument is an S/G list not
+ * a single buffer.
+ */
+
+#define BUS_SPACE_MAXADDR 0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
+
+int ahd_dma_tag_create(struct ahd_softc *, bus_dma_tag_t /*parent*/,
+ bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+ dma_addr_t /*lowaddr*/, dma_addr_t /*highaddr*/,
+ bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+ bus_size_t /*maxsize*/, int /*nsegments*/,
+ bus_size_t /*maxsegsz*/, int /*flags*/,
+ bus_dma_tag_t */*dma_tagp*/);
+
+void ahd_dma_tag_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/);
+
+int ahd_dmamem_alloc(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+ void** /*vaddr*/, int /*flags*/,
+ bus_dmamap_t* /*mapp*/);
+
+void ahd_dmamem_free(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+ void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void ahd_dmamap_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/,
+ bus_dmamap_t /*map*/);
+
+int ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t /*dmat*/,
+ bus_dmamap_t /*map*/, void * /*buf*/,
+ bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+ void */*callback_arg*/, int /*flags*/);
+
+int ahd_dmamap_unload(struct ahd_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahd_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD 0x01 /* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD 0x02 /* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE 0x04 /* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE 0x08 /* post-write synchronization */
+
+/*
+ * XXX
+ * ahd_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API. Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory. Perhaps we need
+ * to do an mb()?
+ */
+#define ahd_dmamap_sync(ahd, dma_tag, dmamap, offset, len, op)
+
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahd_timer_t;
+
+/********************************** Includes **********************************/
+#ifdef CONFIG_AIC79XX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#include "aic79xx.h"
+
+/***************************** Timer Facilities *******************************/
+#define ahd_timer_init init_timer
+#define ahd_timer_stop del_timer_sync
+typedef void ahd_linux_callback_t (u_long);
+static __inline void ahd_timer_reset(ahd_timer_t *timer, u_int usec,
+ ahd_callback_t *func, void *arg);
+static __inline void ahd_scb_timer_reset(struct scb *scb, u_int usec);
+
+static __inline void
+ahd_timer_reset(ahd_timer_t *timer, u_int usec, ahd_callback_t *func, void *arg)
+{
+ struct ahd_softc *ahd;
+
+ ahd = (struct ahd_softc *)arg;
+ del_timer(timer);
+ timer->data = (u_long)arg;
+ timer->expires = jiffies + (usec * HZ)/1000000;
+ timer->function = (ahd_linux_callback_t*)func;
+ add_timer(timer);
+}
+
+static __inline void
+ahd_scb_timer_reset(struct scb *scb, u_int usec)
+{
+ mod_timer(&scb->io_ctx->eh_timeout, jiffies + (usec * HZ)/1000000);
+}
+
+/***************************** SMP support ************************************/
+#include <linux/spinlock.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) || defined(SCSI_HAS_HOST_LOCK))
+#define AHD_SCSI_HAS_HOST_LOCK 1
+#else
+#define AHD_SCSI_HAS_HOST_LOCK 0
+#endif
+
+#define AIC79XX_DRIVER_VERSION "1.3.11"
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros. The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahd_cmd_internal {
+ /* Area owned by the Linux scsi layer. */
+ uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)];
+ union {
+ STAILQ_ENTRY(ahd_cmd) ste;
+ LIST_ENTRY(ahd_cmd) le;
+ TAILQ_ENTRY(ahd_cmd) tqe;
+ } links;
+ uint32_t end;
+};
+
+struct ahd_cmd {
+ union {
+ struct ahd_cmd_internal icmd;
+ struct scsi_cmnd scsi_cmd;
+ } un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle. The structure allocated for a device only becomes persistent
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahd_busyq, ahd_cmd);
+typedef enum {
+ AHD_DEV_UNCONFIGURED = 0x01,
+ AHD_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+ AHD_DEV_TIMER_ACTIVE = 0x04, /* Our timer is active */
+ AHD_DEV_ON_RUN_LIST = 0x08, /* Queued to be run later */
+ AHD_DEV_Q_BASIC = 0x10, /* Allow basic device queuing */
+ AHD_DEV_Q_TAGGED = 0x20, /* Allow full SCSI2 command queueing */
+ AHD_DEV_PERIODIC_OTAG = 0x40, /* Send OTAG to prevent starvation */
+ AHD_DEV_SLAVE_CONFIGURED = 0x80 /* slave_configure() has been called */
+} ahd_linux_dev_flags;
+
+struct ahd_linux_target;
+struct ahd_linux_device {
+ TAILQ_ENTRY(ahd_linux_device) links;
+ struct ahd_busyq busyq;
+
+ /*
+ * The number of transactions currently
+ * queued to the device.
+ */
+ int active;
+
+ /*
+ * The currently allowed number of
+ * transactions that can be queued to
+ * the device. Must be signed for
+ * conversion from tagged to untagged
+ * mode where the device may have more
+ * than one outstanding active transaction.
+ */
+ int openings;
+
+ /*
+ * A positive count indicates that this
+ * device's queue is halted.
+ */
+ u_int qfrozen;
+
+ /*
+ * Cumulative command counter.
+ */
+ u_long commands_issued;
+
+ /*
+ * The number of tagged transactions when
+ * running at our current opening level
+ * that have been successfully received by
+ * this device since the last QUEUE FULL.
+ */
+ u_int tag_success_count;
+#define AHD_TAG_SUCCESS_INTERVAL 50
+
+ ahd_linux_dev_flags flags;
+
+ /*
+ * Per device timer.
+ */
+ struct timer_list timer;
+
+ /*
+ * The high limit for the tags variable.
+ */
+ u_int maxtags;
+
+ /*
+ * The computed number of tags outstanding
+ * at the time of the last QUEUE FULL event.
+ */
+ u_int tags_on_last_queuefull;
+
+ /*
+ * How many times we have seen a queue full
+ * with the same number of tags. This is used
+ * to stop our adaptive queue depth algorithm
+ * on devices with a fixed number of tags.
+ */
+ u_int last_queuefull_same_count;
+#define AHD_LOCK_TAGS_COUNT 50
+
+ /*
+ * How many transactions have been queued
+ * without the device going idle. We use
+ * this statistic to determine when to issue
+ * an ordered tag to prevent transaction
+ * starvation. This statistic is only updated
+ * if the AHD_DEV_PERIODIC_OTAG flag is set
+ * on this device.
+ */
+ u_int commands_since_idle_or_otag;
+#define AHD_OTAG_THRESH 500
+
+ int lun;
+ Scsi_Device *scsi_device;
+ struct ahd_linux_target *target;
+};
+
+typedef enum {
+ AHD_DV_REQUIRED = 0x01,
+ AHD_INQ_VALID = 0x02,
+ AHD_BASIC_DV = 0x04,
+ AHD_ENHANCED_DV = 0x08
+} ahd_linux_targ_flags;
+
+/* DV States */
+typedef enum {
+ AHD_DV_STATE_EXIT = 0,
+ AHD_DV_STATE_INQ_SHORT_ASYNC,
+ AHD_DV_STATE_INQ_ASYNC,
+ AHD_DV_STATE_INQ_ASYNC_VERIFY,
+ AHD_DV_STATE_TUR,
+ AHD_DV_STATE_REBD,
+ AHD_DV_STATE_INQ_VERIFY,
+ AHD_DV_STATE_WEB,
+ AHD_DV_STATE_REB,
+ AHD_DV_STATE_SU,
+ AHD_DV_STATE_BUSY
+} ahd_dv_state;
+
+struct ahd_linux_target {
+ struct ahd_linux_device *devices[AHD_NUM_LUNS];
+ int channel;
+ int target;
+ int refcount;
+ struct ahd_transinfo last_tinfo;
+ struct ahd_softc *ahd;
+ ahd_linux_targ_flags flags;
+ struct scsi_inquiry_data *inq_data;
+ /*
+ * The next "fallback" period to use for narrow/wide transfers.
+ */
+ uint8_t dv_next_narrow_period;
+ uint8_t dv_next_wide_period;
+ uint8_t dv_max_width;
+ uint8_t dv_max_ppr_options;
+ uint8_t dv_last_ppr_options;
+ u_int dv_echo_size;
+ ahd_dv_state dv_state;
+ u_int dv_state_retry;
+ uint8_t *dv_buffer;
+ uint8_t *dv_buffer1;
+
+ /*
+ * Cumulative counter of errors.
+ */
+ u_long errors_detected;
+ u_long cmds_since_error;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require. So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/*
+ * We dynamically adjust the number of segments in pre-2.5 kernels to
+ * avoid fragmentation issues in the SCSI mid-layer's private memory
+ * allocator. See aic79xx_osm.c ahd_linux_size_nseg() for details.
+ */
+extern u_int ahd_linux_nseg;
+#define AHD_NSEG ahd_linux_nseg
+#define AHD_LINUX_MIN_NSEG 64
+#else
+#define AHD_NSEG 128
+#endif
+
+/*
+ * Per-SCB OSM storage.
+ */
+typedef enum {
+ AHD_SCB_UP_EH_SEM = 0x1
+} ahd_linux_scb_flags;
+
+struct scb_platform_data {
+ struct ahd_linux_device *dev;
+ dma_addr_t buf_busaddr;
+ uint32_t xfer_len;
+ uint32_t sense_resid; /* Auto-Sense residual */
+ ahd_linux_scb_flags flags;
+};
+
+/*
+ * Define a structure used for each host adapter. All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+typedef enum {
+ AHD_DV_WAIT_SIMQ_EMPTY = 0x01,
+ AHD_DV_WAIT_SIMQ_RELEASE = 0x02,
+ AHD_DV_ACTIVE = 0x04,
+ AHD_DV_SHUTDOWN = 0x08,
+ AHD_RUN_CMPLT_Q_TIMER = 0x10
+} ahd_linux_softc_flags;
+
+TAILQ_HEAD(ahd_completeq, ahd_cmd);
+
+struct ahd_platform_data {
+ /*
+ * Fields accessed from interrupt context.
+ */
+ struct ahd_linux_target *targets[AHD_NUM_TARGETS];
+ TAILQ_HEAD(, ahd_linux_device) device_runq;
+ struct ahd_completeq completeq;
+
+ spinlock_t spin_lock;
+ struct tasklet_struct runq_tasklet;
+ u_int qfrozen;
+ pid_t dv_pid;
+ struct timer_list completeq_timer;
+ struct timer_list reset_timer;
+ struct timer_list stats_timer;
+ struct semaphore eh_sem;
+ struct semaphore dv_sem;
+ struct semaphore dv_cmd_sem; /* XXX This needs to be in
+ * the target struct
+ */
+ struct scsi_device *dv_scsi_dev;
+ struct Scsi_Host *host; /* pointer to scsi host */
+#define AHD_LINUX_NOIRQ ((uint32_t)~0)
+ uint32_t irq; /* IRQ for this adapter */
+ uint32_t bios_address;
+ uint32_t mem_busaddr; /* Mem Base Addr */
+ uint64_t hw_dma_mask;
+ ahd_linux_softc_flags flags;
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahd_delay(long);
+static __inline void
+ahd_delay(long usec)
+{
+ /*
+ * udelay on Linux can have problems for
+ * multi-millisecond waits. Wait at most
+ * 1024us per call.
+ */
+ while (usec > 0) {
+ udelay(usec % 1024);
+ usec -= 1024;
+ }
+}
+
+
+/***************************** Low Level I/O **********************************/
+static __inline uint8_t ahd_inb(struct ahd_softc * ahd, long port);
+static __inline uint16_t ahd_inw_atomic(struct ahd_softc * ahd, long port);
+static __inline void ahd_outb(struct ahd_softc * ahd, long port, uint8_t val);
+static __inline void ahd_outw_atomic(struct ahd_softc * ahd,
+ long port, uint16_t val);
+static __inline void ahd_outsb(struct ahd_softc * ahd, long port,
+ uint8_t *, int count);
+static __inline void ahd_insb(struct ahd_softc * ahd, long port,
+ uint8_t *, int count);
+
+static __inline uint8_t
+ahd_inb(struct ahd_softc * ahd, long port)
+{
+ uint8_t x;
+
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ x = readb(ahd->bshs[0].maddr + port);
+ } else {
+ x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+ }
+ mb();
+ return (x);
+}
+
+static __inline uint16_t
+ahd_inw_atomic(struct ahd_softc * ahd, long port)
+{
+ uint8_t x;
+
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ x = readw(ahd->bshs[0].maddr + port);
+ } else {
+ x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+ }
+ mb();
+ return (x);
+}
+
+static __inline void
+ahd_outb(struct ahd_softc * ahd, long port, uint8_t val)
+{
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ writeb(val, ahd->bshs[0].maddr + port);
+ } else {
+ outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+ }
+ mb();
+}
+
+static __inline void
+ahd_outw_atomic(struct ahd_softc * ahd, long port, uint16_t val)
+{
+ if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+ writew(val, ahd->bshs[0].maddr + port);
+ } else {
+ outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+ }
+ mb();
+}
+
+static __inline void
+ahd_outsb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ ahd_outb(ahd, port, *array++);
+}
+
+static __inline void
+ahd_insb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ *array++ = ahd_inb(ahd, port);
+}
+
+/**************************** Initialization **********************************/
+int ahd_linux_register_host(struct ahd_softc *,
+ Scsi_Host_Template *);
+
+uint64_t ahd_linux_get_memsize(void);
+
+/*************************** Pretty Printing **********************************/
+struct info_str {
+ char *buffer;
+ int length;
+ off_t offset;
+ int pos;
+};
+
+void ahd_format_transinfo(struct info_str *info,
+ struct ahd_transinfo *tinfo);
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahd_lockinit(struct ahd_softc *);
+static __inline void ahd_lock(struct ahd_softc *, unsigned long *flags);
+static __inline void ahd_unlock(struct ahd_softc *, unsigned long *flags);
+
+/* Lock acquisition and release of the above lock in midlayer entry points. */
+static __inline void ahd_midlayer_entrypoint_lock(struct ahd_softc *,
+ unsigned long *flags);
+static __inline void ahd_midlayer_entrypoint_unlock(struct ahd_softc *,
+ unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahd_done_lockinit(struct ahd_softc *);
+static __inline void ahd_done_lock(struct ahd_softc *, unsigned long *flags);
+static __inline void ahd_done_unlock(struct ahd_softc *, unsigned long *flags);
+
+/* Lock held during ahd_list manipulation and ahd softc frees */
+extern spinlock_t ahd_list_spinlock;
+static __inline void ahd_list_lockinit(void);
+static __inline void ahd_list_lock(unsigned long *flags);
+static __inline void ahd_list_unlock(unsigned long *flags);
+
+static __inline void
+ahd_lockinit(struct ahd_softc *ahd)
+{
+ spin_lock_init(&ahd->platform_data->spin_lock);
+}
+
+static __inline void
+ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ spin_lock_irqsave(&ahd->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahd->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahd_midlayer_entrypoint_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+ /*
+ * In 2.5.X and some 2.4.X versions, the midlayer takes our
+ * lock just before calling us, so we avoid locking again.
+ * For other kernel versions, the io_request_lock is taken
+ * just before our entry point is called. In this case, we
+ * trade the io_request_lock for our per-softc lock.
+ */
+#if AHD_SCSI_HAS_HOST_LOCK == 0
+ spin_unlock(&io_request_lock);
+ spin_lock(&ahd->platform_data->spin_lock);
+#endif
+}
+
+static __inline void
+ahd_midlayer_entrypoint_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+#if AHD_SCSI_HAS_HOST_LOCK == 0
+ spin_unlock(&ahd->platform_data->spin_lock);
+ spin_lock(&io_request_lock);
+#endif
+}
+
+static __inline void
+ahd_done_lockinit(struct ahd_softc *ahd)
+{
+ /*
+ * In 2.5.X, our own lock is held during completions.
+ * In previous versions, the io_request_lock is used.
+ * In either case, we can't initialize this lock again.
+ */
+}
+
+static __inline void
+ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+#if AHD_SCSI_HAS_HOST_LOCK == 0
+ spin_lock(&io_request_lock);
+#endif
+}
+
+static __inline void
+ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+#if AHD_SCSI_HAS_HOST_LOCK == 0
+ spin_unlock(&io_request_lock);
+#endif
+}
+
+static __inline void
+ahd_list_lockinit(void)
+{
+ spin_lock_init(&ahd_list_spinlock);
+}
+
+static __inline void
+ahd_list_lock(unsigned long *flags)
+{
+ spin_lock_irqsave(&ahd_list_spinlock, *flags);
+}
+
+static __inline void
+ahd_list_unlock(unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahd_list_spinlock, *flags);
+}
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR 0x00
+#define PCIR_VENDOR 0x00
+#define PCIR_DEVICE 0x02
+#define PCIR_COMMAND 0x04
+#define PCIM_CMD_PORTEN 0x0001
+#define PCIM_CMD_MEMEN 0x0002
+#define PCIM_CMD_BUSMASTEREN 0x0004
+#define PCIM_CMD_MWRICEN 0x0010
+#define PCIM_CMD_PERRESPEN 0x0040
+#define PCIM_CMD_SERRESPEN 0x0100
+#define PCIR_STATUS 0x06
+#define PCIR_REVID 0x08
+#define PCIR_PROGIF 0x09
+#define PCIR_SUBCLASS 0x0a
+#define PCIR_CLASS 0x0b
+#define PCIR_CACHELNSZ 0x0c
+#define PCIR_LATTIMER 0x0d
+#define PCIR_HEADERTYPE 0x0e
+#define PCIM_MFDEV 0x80
+#define PCIR_BIST 0x0f
+#define PCIR_CAP_PTR 0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS 0x10
+#define PCIR_SUBVEND_0 0x2c
+#define PCIR_SUBDEV_0 0x2e
+
+/****************************** PCI-X definitions *****************************/
+#define PCIXR_COMMAND 0x96
+#define PCIXR_DEVADDR 0x98
+#define PCIXM_DEVADDR_FNUM 0x0003 /* Function Number */
+#define PCIXM_DEVADDR_DNUM 0x00F8 /* Device Number */
+#define PCIXM_DEVADDR_BNUM 0xFF00 /* Bus Number */
+#define PCIXR_STATUS 0x9A
+#define PCIXM_STATUS_64BIT 0x0001 /* Active 64bit connection to device. */
+#define PCIXM_STATUS_133CAP 0x0002 /* Device is 133MHz capable */
+#define PCIXM_STATUS_SCDISC 0x0004 /* Split Completion Discarded */
+#define PCIXM_STATUS_UNEXPSC 0x0008 /* Unexpected Split Completion */
+#define PCIXM_STATUS_CMPLEXDEV 0x0010 /* Device Complexity (set == bridge) */
+#define PCIXM_STATUS_MAXMRDBC 0x0060 /* Maximum Burst Read Count */
+#define PCIXM_STATUS_MAXSPLITS 0x0380 /* Maximum Split Transactions */
+#define PCIXM_STATUS_MAXCRDS 0x1C00 /* Maximum Cumulative Read Size */
+#define PCIXM_STATUS_RCVDSCEM 0x2000 /* Received a Split Comp w/Error msg */
+
+extern struct pci_driver aic79xx_pci_driver;
+
+typedef enum
+{
+ AHD_POWER_STATE_D0,
+ AHD_POWER_STATE_D1,
+ AHD_POWER_STATE_D2,
+ AHD_POWER_STATE_D3
+} ahd_power_state;
+
+void ahd_power_state_change(struct ahd_softc *ahd,
+ ahd_power_state new_state);
+
+/******************************* PCI Routines *********************************/
+int ahd_linux_pci_init(void);
+void ahd_linux_pci_exit(void);
+int ahd_pci_map_registers(struct ahd_softc *ahd);
+int ahd_pci_map_int(struct ahd_softc *ahd);
+
+static __inline uint32_t ahd_pci_read_config(ahd_dev_softc_t pci,
+ int reg, int width);
+
+static __inline uint32_t
+ahd_pci_read_config(ahd_dev_softc_t pci, int reg, int width)
+{
+ switch (width) {
+ case 1:
+ {
+ uint8_t retval;
+
+ pci_read_config_byte(pci, reg, &retval);
+ return (retval);
+ }
+ case 2:
+ {
+ uint16_t retval;
+ pci_read_config_word(pci, reg, &retval);
+ return (retval);
+ }
+ case 4:
+ {
+ uint32_t retval;
+ pci_read_config_dword(pci, reg, &retval);
+ return (retval);
+ }
+ default:
+ panic("ahd_pci_read_config: Read size too big");
+ /* NOTREACHED */
+ return (0);
+ }
+}
+
+static __inline void ahd_pci_write_config(ahd_dev_softc_t pci,
+ int reg, uint32_t value,
+ int width);
+
+static __inline void
+ahd_pci_write_config(ahd_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+ switch (width) {
+ case 1:
+ pci_write_config_byte(pci, reg, value);
+ break;
+ case 2:
+ pci_write_config_word(pci, reg, value);
+ break;
+ case 4:
+ pci_write_config_dword(pci, reg, value);
+ break;
+ default:
+ panic("ahd_pci_write_config: Write size too big");
+ /* NOTREACHED */
+ }
+}
+
+static __inline int ahd_get_pci_function(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_function(ahd_dev_softc_t pci)
+{
+ return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahd_get_pci_slot(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_slot(ahd_dev_softc_t pci)
+{
+ return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahd_get_pci_bus(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_bus(ahd_dev_softc_t pci)
+{
+ return (pci->bus->number);
+}
+
+static __inline void ahd_flush_device_writes(struct ahd_softc *);
+static __inline void
+ahd_flush_device_writes(struct ahd_softc *ahd)
+{
+ /* XXX Is this sufficient for all architectures??? */
+ ahd_inb(ahd, INTSTAT);
+}
+
+/**************************** Proc FS Support *********************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+int ahd_linux_proc_info(char *, char **, off_t, int, int, int);
+#else
+int ahd_linux_proc_info(struct Scsi_Host *, char *, char **,
+ off_t, int, int);
+#endif
+
+/*************************** Domain Validation ********************************/
+#define AHD_DV_CMD(cmd) ((cmd)->scsi_done == ahd_linux_dv_complete)
+#define AHD_DV_SIMQ_FROZEN(ahd) \
+ ((((ahd)->platform_data->flags & AHD_DV_ACTIVE) != 0) \
+ && (ahd)->platform_data->qfrozen == 1)
+
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahd_cmd_set_transaction_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahd_set_transaction_status(struct scb *, uint32_t);
+static __inline void ahd_cmd_set_scsi_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahd_set_scsi_status(struct scb *, uint32_t);
+static __inline uint32_t ahd_cmd_get_transaction_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahd_get_transaction_status(struct scb *);
+static __inline uint32_t ahd_cmd_get_scsi_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahd_get_scsi_status(struct scb *);
+static __inline void ahd_set_transaction_tag(struct scb *, int, u_int);
+static __inline u_long ahd_get_transfer_length(struct scb *);
+static __inline int ahd_get_transfer_dir(struct scb *);
+static __inline void ahd_set_residual(struct scb *, u_long);
+static __inline void ahd_set_sense_residual(struct scb *scb, u_long resid);
+static __inline u_long ahd_get_residual(struct scb *);
+static __inline u_long ahd_get_sense_residual(struct scb *);
+static __inline int ahd_perform_autosense(struct scb *);
+static __inline uint32_t ahd_get_sense_bufsize(struct ahd_softc *,
+ struct scb *);
+static __inline void ahd_notify_xfer_settings_change(struct ahd_softc *,
+ struct ahd_devinfo *);
+static __inline void ahd_platform_scb_free(struct ahd_softc *ahd,
+ struct scb *scb);
+static __inline void ahd_freeze_scb(struct scb *scb);
+
+static __inline
+void ahd_cmd_set_transaction_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~(CAM_STATUS_MASK << 16);
+ cmd->result |= status << 16;
+}
+
+static __inline
+void ahd_set_transaction_status(struct scb *scb, uint32_t status)
+{
+ ahd_cmd_set_transaction_status(scb->io_ctx,status);
+}
+
+static __inline
+void ahd_cmd_set_scsi_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~0xFFFF;
+ cmd->result |= status;
+}
+
+static __inline
+void ahd_set_scsi_status(struct scb *scb, uint32_t status)
+{
+ ahd_cmd_set_scsi_status(scb->io_ctx, status);
+}
+
+static __inline
+uint32_t ahd_cmd_get_transaction_status(Scsi_Cmnd *cmd)
+{
+ return ((cmd->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline
+uint32_t ahd_get_transaction_status(struct scb *scb)
+{
+ return (ahd_cmd_get_transaction_status(scb->io_ctx));
+}
+
+static __inline
+uint32_t ahd_cmd_get_scsi_status(Scsi_Cmnd *cmd)
+{
+ return (cmd->result & 0xFFFF);
+}
+
+static __inline
+uint32_t ahd_get_scsi_status(struct scb *scb)
+{
+ return (ahd_cmd_get_scsi_status(scb->io_ctx));
+}
+
+static __inline
+void ahd_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+ /*
+ * Nothing to do for linux as the incoming transaction
+ * has no concept of tag/non tagged, etc.
+ */
+}
+
+static __inline
+u_long ahd_get_transfer_length(struct scb *scb)
+{
+ return (scb->platform_data->xfer_len);
+}
+
+static __inline
+int ahd_get_transfer_dir(struct scb *scb)
+{
+ return (scb->io_ctx->sc_data_direction);
+}
+
+static __inline
+void ahd_set_residual(struct scb *scb, u_long resid)
+{
+ scb->io_ctx->resid = resid;
+}
+
+static __inline
+void ahd_set_sense_residual(struct scb *scb, u_long resid)
+{
+ scb->platform_data->sense_resid = resid;
+}
+
+static __inline
+u_long ahd_get_residual(struct scb *scb)
+{
+ return (scb->io_ctx->resid);
+}
+
+static __inline
+u_long ahd_get_sense_residual(struct scb *scb)
+{
+ return (scb->platform_data->sense_resid);
+}
+
+static __inline
+int ahd_perform_autosense(struct scb *scb)
+{
+ /*
+ * We always perform autosense in Linux.
+ * On other platforms this is set on a
+ * per-transaction basis.
+ */
+ return (1);
+}
+
+static __inline uint32_t
+ahd_get_sense_bufsize(struct ahd_softc *ahd, struct scb *scb)
+{
+ return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void
+ahd_notify_xfer_settings_change(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo)
+{
+ /* Nothing to do here for linux */
+}
+
+static __inline void
+ahd_platform_scb_free(struct ahd_softc *ahd, struct scb *scb)
+{
+ ahd->flags &= ~AHD_RESOURCE_SHORTAGE;
+}
+
+int ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg);
+void ahd_platform_free(struct ahd_softc *ahd);
+void ahd_platform_init(struct ahd_softc *ahd);
+void ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+void ahd_freeze_simq(struct ahd_softc *ahd);
+void ahd_release_simq(struct ahd_softc *ahd);
+
+static __inline void
+ahd_freeze_scb(struct scb *scb)
+{
+ if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
+ scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+ scb->platform_data->dev->qfrozen++;
+ }
+}
+
+void ahd_platform_set_tags(struct ahd_softc *ahd,
+ struct ahd_devinfo *devinfo, ahd_queue_alg);
+int ahd_platform_abort_scbs(struct ahd_softc *ahd, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+irqreturn_t
+ ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs);
+void ahd_platform_flushwork(struct ahd_softc *ahd);
+int ahd_softc_comp(struct ahd_softc *, struct ahd_softc *);
+void ahd_done(struct ahd_softc*, struct scb*);
+void ahd_send_async(struct ahd_softc *, char channel,
+ u_int target, u_int lun, ac_code, void *);
+void ahd_print_path(struct ahd_softc *, struct scb *);
+void ahd_platform_dump_card_state(struct ahd_softc *ahd);
+
+#ifdef CONFIG_PCI
+#define AHD_PCI_CONFIG 1
+#else
+#define AHD_PCI_CONFIG 0
+#endif
+#define bootverbose aic79xx_verbose
+extern uint32_t aic79xx_verbose;
+
+#endif /* _AIC79XX_LINUX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
new file mode 100644
index 000000000000..91daf0c7fb10
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -0,0 +1,368 @@
+/*
+ * Linux driver attachment glue for PCI based U320 controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm_pci.c#25 $
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aic79xx_pci.h"
+
+static int ahd_linux_pci_dev_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static int ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd,
+ u_long *base, u_long *base2);
+static int ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+ u_long *bus_addr,
+ uint8_t __iomem **maddr);
+static void ahd_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* Define the macro locally since it's different for different class of chips.
+ */
+#define ID(x) \
+ ID2C(x), \
+ ID2C(IDIROC(x))
+
+static struct pci_device_id ahd_linux_pci_id_table[] = {
+ /* aic7901 based controllers */
+ ID(ID_AHA_29320A),
+ ID(ID_AHA_29320ALP),
+ /* aic7902 based controllers */
+ ID(ID_AHA_29320),
+ ID(ID_AHA_29320B),
+ ID(ID_AHA_29320LP),
+ ID(ID_AHA_39320),
+ ID(ID_AHA_39320_B),
+ ID(ID_AHA_39320A),
+ ID(ID_AHA_39320D),
+ ID(ID_AHA_39320D_HP),
+ ID(ID_AHA_39320D_B),
+ ID(ID_AHA_39320D_B_HP),
+ /* Generic chip probes for devices we don't know exactly. */
+ ID16(ID_AIC7901 & ID_9005_GENERIC_MASK),
+ ID(ID_AIC7901A & ID_DEV_VENDOR_MASK),
+ ID16(ID_AIC7902 & ID_9005_GENERIC_MASK),
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
+
+struct pci_driver aic79xx_pci_driver = {
+ .name = "aic79xx",
+ .probe = ahd_linux_pci_dev_probe,
+ .remove = ahd_linux_pci_dev_remove,
+ .id_table = ahd_linux_pci_id_table
+};
+
+static void
+ahd_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+ struct ahd_softc *ahd;
+ u_long l;
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahd_list_lock(&l);
+ ahd = ahd_find_softc((struct ahd_softc *)pci_get_drvdata(pdev));
+ if (ahd != NULL) {
+ u_long s;
+
+ TAILQ_REMOVE(&ahd_tailq, ahd, links);
+ ahd_list_unlock(&l);
+ ahd_lock(ahd, &s);
+ ahd_intr_enable(ahd, FALSE);
+ ahd_unlock(ahd, &s);
+ ahd_free(ahd);
+ } else
+ ahd_list_unlock(&l);
+}
+
+static int
+ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ char buf[80];
+ struct ahd_softc *ahd;
+ ahd_dev_softc_t pci;
+ struct ahd_pci_identity *entry;
+ char *name;
+ int error;
+
+ /*
+ * Some BIOSen report the same device multiple times.
+ */
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ struct pci_dev *probed_pdev;
+
+ probed_pdev = ahd->dev_softc;
+ if (probed_pdev->bus->number == pdev->bus->number
+ && probed_pdev->devfn == pdev->devfn)
+ break;
+ }
+ if (ahd != NULL) {
+ /* Skip duplicate. */
+ return (-ENODEV);
+ }
+
+ pci = pdev;
+ entry = ahd_find_pci_device(pci);
+ if (entry == NULL)
+ return (-ENODEV);
+
+ /*
+ * Allocate a softc for this card and
+ * set it up for attachment by our
+ * common detect routine.
+ */
+ sprintf(buf, "ahd_pci:%d:%d:%d",
+ ahd_get_pci_bus(pci),
+ ahd_get_pci_slot(pci),
+ ahd_get_pci_function(pci));
+ name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (name == NULL)
+ return (-ENOMEM);
+ strcpy(name, buf);
+ ahd = ahd_alloc(NULL, name);
+ if (ahd == NULL)
+ return (-ENOMEM);
+ if (pci_enable_device(pdev)) {
+ ahd_free(ahd);
+ return (-ENODEV);
+ }
+ pci_set_master(pdev);
+
+ if (sizeof(dma_addr_t) > 4) {
+ uint64_t memsize;
+ const uint64_t mask_39bit = 0x7FFFFFFFFFULL;
+
+ memsize = ahd_linux_get_memsize();
+
+ if (memsize >= 0x8000000000ULL
+ && pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) {
+ ahd->flags |= AHD_64BIT_ADDRESSING;
+ ahd->platform_data->hw_dma_mask = DMA_64BIT_MASK;
+ } else if (memsize > 0x80000000
+ && pci_set_dma_mask(pdev, mask_39bit) == 0) {
+ ahd->flags |= AHD_39BIT_ADDRESSING;
+ ahd->platform_data->hw_dma_mask = mask_39bit;
+ }
+ } else {
+ pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ ahd->platform_data->hw_dma_mask = DMA_32BIT_MASK;
+ }
+ ahd->dev_softc = pci;
+ error = ahd_pci_config(ahd, entry);
+ if (error != 0) {
+ ahd_free(ahd);
+ return (-error);
+ }
+ pci_set_drvdata(pdev, ahd);
+ if (aic79xx_detect_complete) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahd_linux_register_host(ahd, &aic79xx_driver_template);
+#else
+ printf("aic79xx: ignoring PCI device found after "
+ "initialization\n");
+ return (-ENODEV);
+#endif
+ }
+ return (0);
+}
+
+int
+ahd_linux_pci_init(void)
+{
+ return (pci_module_init(&aic79xx_pci_driver));
+}
+
+void
+ahd_linux_pci_exit(void)
+{
+ pci_unregister_driver(&aic79xx_pci_driver);
+}
+
+static int
+ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base,
+ u_long *base2)
+{
+ *base = pci_resource_start(ahd->dev_softc, 0);
+ /*
+ * This is really the 3rd bar and should be at index 2,
+ * but the Linux PCI code doesn't know how to "count" 64bit
+ * bars.
+ */
+ *base2 = pci_resource_start(ahd->dev_softc, 3);
+ if (*base == 0 || *base2 == 0)
+ return (ENOMEM);
+ if (request_region(*base, 256, "aic79xx") == 0)
+ return (ENOMEM);
+ if (request_region(*base2, 256, "aic79xx") == 0) {
+ release_region(*base2, 256);
+ return (ENOMEM);
+ }
+ return (0);
+}
+
+static int
+ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+ u_long *bus_addr,
+ uint8_t __iomem **maddr)
+{
+ u_long start;
+ u_long base_page;
+ u_long base_offset;
+ int error;
+
+ if (aic79xx_allow_memio == 0)
+ return (ENOMEM);
+
+ if ((ahd->bugs & AHD_PCIX_MMAPIO_BUG) != 0)
+ return (ENOMEM);
+
+ error = 0;
+ start = pci_resource_start(ahd->dev_softc, 1);
+ base_page = start & PAGE_MASK;
+ base_offset = start - base_page;
+ if (start != 0) {
+ *bus_addr = start;
+ if (request_mem_region(start, 0x1000, "aic79xx") == 0)
+ error = ENOMEM;
+ if (error == 0) {
+ *maddr = ioremap_nocache(base_page, base_offset + 256);
+ if (*maddr == NULL) {
+ error = ENOMEM;
+ release_mem_region(start, 0x1000);
+ } else
+ *maddr += base_offset;
+ }
+ } else
+ error = ENOMEM;
+ return (error);
+}
+
+int
+ahd_pci_map_registers(struct ahd_softc *ahd)
+{
+ uint32_t command;
+ u_long base;
+ uint8_t __iomem *maddr;
+ int error;
+
+ /*
+ * If its allowed, we prefer memory mapped access.
+ */
+ command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, 4);
+ command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+ base = 0;
+ maddr = NULL;
+ error = ahd_linux_pci_reserve_mem_region(ahd, &base, &maddr);
+ if (error == 0) {
+ ahd->platform_data->mem_busaddr = base;
+ ahd->tags[0] = BUS_SPACE_MEMIO;
+ ahd->bshs[0].maddr = maddr;
+ ahd->tags[1] = BUS_SPACE_MEMIO;
+ ahd->bshs[1].maddr = maddr + 0x100;
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ command | PCIM_CMD_MEMEN, 4);
+
+ if (ahd_pci_test_register_access(ahd) != 0) {
+
+ printf("aic79xx: PCI Device %d:%d:%d "
+ "failed memory mapped test. Using PIO.\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc));
+ iounmap(maddr);
+ release_mem_region(ahd->platform_data->mem_busaddr,
+ 0x1000);
+ ahd->bshs[0].maddr = NULL;
+ maddr = NULL;
+ } else
+ command |= PCIM_CMD_MEMEN;
+ } else if (bootverbose) {
+ printf("aic79xx: PCI%d:%d:%d MEM region 0x%lx "
+ "unavailable. Cannot memory map device.\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc),
+ base);
+ }
+
+ if (maddr == NULL) {
+ u_long base2;
+
+ error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2);
+ if (error == 0) {
+ ahd->tags[0] = BUS_SPACE_PIO;
+ ahd->tags[1] = BUS_SPACE_PIO;
+ ahd->bshs[0].ioport = base;
+ ahd->bshs[1].ioport = base2;
+ command |= PCIM_CMD_PORTEN;
+ } else {
+ printf("aic79xx: PCI%d:%d:%d IO regions 0x%lx and 0x%lx"
+ "unavailable. Cannot map device.\n",
+ ahd_get_pci_bus(ahd->dev_softc),
+ ahd_get_pci_slot(ahd->dev_softc),
+ ahd_get_pci_function(ahd->dev_softc),
+ base, base2);
+ }
+ }
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4);
+ return (error);
+}
+
+int
+ahd_pci_map_int(struct ahd_softc *ahd)
+{
+ int error;
+
+ error = request_irq(ahd->dev_softc->irq, ahd_linux_isr,
+ SA_SHIRQ, "aic79xx", ahd);
+ if (error == 0)
+ ahd->platform_data->irq = ahd->dev_softc->irq;
+
+ return (-error);
+}
+
+void
+ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
+{
+ pci_set_power_state(ahd->dev_softc, new_state);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c
new file mode 100644
index 000000000000..4c3bb7bb8420
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.c
@@ -0,0 +1,987 @@
+/*
+ * Product specific probe and attach routines for:
+ * aic7901 and aic7902 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_pci.c#77 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#endif
+
+#include "aic79xx_pci.h"
+
+static __inline uint64_t
+ahd_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+ uint64_t id;
+
+ id = subvendor
+ | (subdevice << 16)
+ | ((uint64_t)vendor << 32)
+ | ((uint64_t)device << 48);
+
+ return (id);
+}
+
+#define ID_AIC7902_PCI_REV_A4 0x3
+#define ID_AIC7902_PCI_REV_B0 0x10
+#define SUBID_HP 0x0E11
+
+#define DEVID_9005_HOSTRAID(id) ((id) & 0x80)
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define DEVID_9005_TYPE_HBA 0x0 /* Standard Card */
+#define DEVID_9005_TYPE_HBA_2EXT 0x1 /* 2 External Ports */
+#define DEVID_9005_TYPE_IROC 0x8 /* Raid(0,1,10) Card */
+#define DEVID_9005_TYPE_MB 0xF /* On Motherboard */
+
+#define DEVID_9005_MFUNC(id) ((id) & 0x10)
+
+#define DEVID_9005_PACKETIZED(id) ((id) & 0x8000)
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define SUBID_9005_TYPE_HBA 0x0 /* Standard Card */
+#define SUBID_9005_TYPE_MB 0xF /* On Motherboard */
+
+#define SUBID_9005_AUTOTERM(id) (((id) & 0x10) == 0)
+
+#define SUBID_9005_LEGACYCONN_FUNC(id) ((id) & 0x20)
+
+#define SUBID_9005_SEEPTYPE(id) ((id) & 0x0C0) >> 6)
+#define SUBID_9005_SEEPTYPE_NONE 0x0
+#define SUBID_9005_SEEPTYPE_4K 0x1
+
+static ahd_device_setup_t ahd_aic7901_setup;
+static ahd_device_setup_t ahd_aic7901A_setup;
+static ahd_device_setup_t ahd_aic7902_setup;
+static ahd_device_setup_t ahd_aic790X_setup;
+
+struct ahd_pci_identity ahd_pci_ident_table [] =
+{
+ /* aic7901 based controllers */
+ {
+ ID_AHA_29320A,
+ ID_ALL_MASK,
+ "Adaptec 29320A Ultra320 SCSI adapter",
+ ahd_aic7901_setup
+ },
+ {
+ ID_AHA_29320ALP,
+ ID_ALL_MASK,
+ "Adaptec 29320ALP Ultra320 SCSI adapter",
+ ahd_aic7901_setup
+ },
+ /* aic7902 based controllers */
+ {
+ ID_AHA_29320,
+ ID_ALL_MASK,
+ "Adaptec 29320 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_29320B,
+ ID_ALL_MASK,
+ "Adaptec 29320B Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_29320LP,
+ ID_ALL_MASK,
+ "Adaptec 29320LP Ultra320 SCSI adapter",
+ ahd_aic7901A_setup
+ },
+ {
+ ID_AHA_39320,
+ ID_ALL_MASK,
+ "Adaptec 39320 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320_B,
+ ID_ALL_MASK,
+ "Adaptec 39320 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320A,
+ ID_ALL_MASK,
+ "Adaptec 39320A Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D,
+ ID_ALL_MASK,
+ "Adaptec 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D_HP,
+ ID_ALL_MASK,
+ "Adaptec (HP OEM) 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D_B,
+ ID_ALL_MASK,
+ "Adaptec 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ {
+ ID_AHA_39320D_B_HP,
+ ID_ALL_MASK,
+ "Adaptec (HP OEM) 39320D Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7901 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec AIC7901 Ultra320 SCSI adapter",
+ ahd_aic7901_setup
+ },
+ {
+ ID_AIC7901A & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec AIC7901A Ultra320 SCSI adapter",
+ ahd_aic7901A_setup
+ },
+ {
+ ID_AIC7902 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec AIC7902 Ultra320 SCSI adapter",
+ ahd_aic7902_setup
+ }
+};
+
+const u_int ahd_num_pci_devs = NUM_ELEMENTS(ahd_pci_ident_table);
+
+#define DEVCONFIG 0x40
+#define PCIXINITPAT 0x0000E000ul
+#define PCIXINIT_PCI33_66 0x0000E000ul
+#define PCIXINIT_PCIX50_66 0x0000C000ul
+#define PCIXINIT_PCIX66_100 0x0000A000ul
+#define PCIXINIT_PCIX100_133 0x00008000ul
+#define PCI_BUS_MODES_INDEX(devconfig) \
+ (((devconfig) & PCIXINITPAT) >> 13)
+static const char *pci_bus_modes[] =
+{
+ "PCI bus mode unknown",
+ "PCI bus mode unknown",
+ "PCI bus mode unknown",
+ "PCI bus mode unknown",
+ "PCI-X 101-133Mhz",
+ "PCI-X 67-100Mhz",
+ "PCI-X 50-66Mhz",
+ "PCI 33 or 66Mhz"
+};
+
+#define TESTMODE 0x00000800ul
+#define IRDY_RST 0x00000200ul
+#define FRAME_RST 0x00000100ul
+#define PCI64BIT 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define ENDIANSEL 0x00000020ul
+#define MIXQWENDIANEN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define QWENDIANSEL 0x00000001ul
+
+#define DEVCONFIG1 0x44
+#define PREQDIS 0x01
+
+#define CSIZE_LATTIME 0x0c
+#define CACHESIZE 0x000000fful
+#define LATTIME 0x0000ff00ul
+
+static int ahd_check_extport(struct ahd_softc *ahd);
+static void ahd_configure_termination(struct ahd_softc *ahd,
+ u_int adapter_control);
+static void ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat);
+
+struct ahd_pci_identity *
+ahd_find_pci_device(ahd_dev_softc_t pci)
+{
+ uint64_t full_id;
+ uint16_t device;
+ uint16_t vendor;
+ uint16_t subdevice;
+ uint16_t subvendor;
+ struct ahd_pci_identity *entry;
+ u_int i;
+
+ vendor = ahd_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+ device = ahd_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+ subvendor = ahd_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+ subdevice = ahd_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ full_id = ahd_compose_id(device,
+ vendor,
+ subdevice,
+ subvendor);
+
+ /*
+ * Controllers, mask out the IROC/HostRAID bit
+ */
+
+ full_id &= ID_ALL_IROC_MASK;
+
+ for (i = 0; i < ahd_num_pci_devs; i++) {
+ entry = &ahd_pci_ident_table[i];
+ if (entry->full_id == (full_id & entry->id_mask)) {
+ /* Honor exclusion entries. */
+ if (entry->name == NULL)
+ return (NULL);
+ return (entry);
+ }
+ }
+ return (NULL);
+}
+
+int
+ahd_pci_config(struct ahd_softc *ahd, struct ahd_pci_identity *entry)
+{
+ struct scb_data *shared_scb_data;
+ u_long l;
+ u_int command;
+ uint32_t devconfig;
+ uint16_t subvendor;
+ int error;
+
+ shared_scb_data = NULL;
+ ahd->description = entry->name;
+ /*
+ * Record if this is an HP board.
+ */
+ subvendor = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_SUBVEND_0, /*bytes*/2);
+ if (subvendor == SUBID_HP)
+ ahd->flags |= AHD_HP_BOARD;
+
+ error = entry->setup(ahd);
+ if (error != 0)
+ return (error);
+
+ devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+ if ((devconfig & PCIXINITPAT) == PCIXINIT_PCI33_66) {
+ ahd->chip |= AHD_PCI;
+ /* Disable PCIX workarounds when running in PCI mode. */
+ ahd->bugs &= ~AHD_PCIX_BUG_MASK;
+ } else {
+ ahd->chip |= AHD_PCIX;
+ }
+ ahd->bus_description = pci_bus_modes[PCI_BUS_MODES_INDEX(devconfig)];
+
+ ahd_power_state_change(ahd, AHD_POWER_STATE_D0);
+
+ error = ahd_pci_map_registers(ahd);
+ if (error != 0)
+ return (error);
+
+ /*
+ * If we need to support high memory, enable dual
+ * address cycles. This bit must be set to enable
+ * high address bit generation even if we are on a
+ * 64bit bus (PCI64BIT set in devconfig).
+ */
+ if ((ahd->flags & (AHD_39BIT_ADDRESSING|AHD_64BIT_ADDRESSING)) != 0) {
+ uint32_t devconfig;
+
+ if (bootverbose)
+ printf("%s: Enabling 39Bit Addressing\n",
+ ahd_name(ahd));
+ devconfig = ahd_pci_read_config(ahd->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ devconfig |= DACEN;
+ ahd_pci_write_config(ahd->dev_softc, DEVCONFIG,
+ devconfig, /*bytes*/4);
+ }
+
+ /* Ensure busmastering is enabled */
+ command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+ command |= PCIM_CMD_BUSMASTEREN;
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, /*bytes*/2);
+
+ error = ahd_softc_init(ahd);
+ if (error != 0)
+ return (error);
+
+ ahd->bus_intr = ahd_pci_intr;
+
+ error = ahd_reset(ahd, /*reinit*/FALSE);
+ if (error != 0)
+ return (ENXIO);
+
+ ahd->pci_cachesize =
+ ahd_pci_read_config(ahd->dev_softc, CSIZE_LATTIME,
+ /*bytes*/1) & CACHESIZE;
+ ahd->pci_cachesize *= 4;
+
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ /* See if we have a SEEPROM and perform auto-term */
+ error = ahd_check_extport(ahd);
+ if (error != 0)
+ return (error);
+
+ /* Core initialization */
+ error = ahd_init(ahd);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Allow interrupts now that we are completely setup.
+ */
+ error = ahd_pci_map_int(ahd);
+ if (error != 0)
+ return (error);
+
+ ahd_list_lock(&l);
+ /*
+ * Link this softc in with all other ahd instances.
+ */
+ ahd_softc_insert(ahd);
+ ahd_list_unlock(&l);
+ return (0);
+}
+
+/*
+ * Perform some simple tests that should catch situations where
+ * our registers are invalidly mapped.
+ */
+int
+ahd_pci_test_register_access(struct ahd_softc *ahd)
+{
+ uint32_t cmd;
+ u_int targpcistat;
+ u_int pci_status1;
+ int error;
+ uint8_t hcntrl;
+
+ error = EIO;
+
+ /*
+ * Enable PCI error interrupt status, but suppress NMIs
+ * generated by SERR raised due to target aborts.
+ */
+ cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+ cmd & ~PCIM_CMD_SERRESPEN, /*bytes*/2);
+
+ /*
+ * First a simple test to see if any
+ * registers can be read. Reading
+ * HCNTRL has no side effects and has
+ * at least one bit that is guaranteed to
+ * be zero so it is a good register to
+ * use for this test.
+ */
+ hcntrl = ahd_inb(ahd, HCNTRL);
+ if (hcntrl == 0xFF)
+ goto fail;
+
+ /*
+ * Next create a situation where write combining
+ * or read prefetching could be initiated by the
+ * CPU or host bridge. Our device does not support
+ * either, so look for data corruption and/or flaged
+ * PCI errors. First pause without causing another
+ * chip reset.
+ */
+ hcntrl &= ~CHIPRST;
+ ahd_outb(ahd, HCNTRL, hcntrl|PAUSE);
+ while (ahd_is_paused(ahd) == 0)
+ ;
+
+ /* Clear any PCI errors that occurred before our driver attached. */
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ targpcistat = ahd_inb(ahd, TARGPCISTAT);
+ ahd_outb(ahd, TARGPCISTAT, targpcistat);
+ pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+ pci_status1, /*bytes*/1);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ ahd_outb(ahd, CLRINT, CLRPCIINT);
+
+ ahd_outb(ahd, SEQCTL0, PERRORDIS);
+ ahd_outl(ahd, SRAM_BASE, 0x5aa555aa);
+ if (ahd_inl(ahd, SRAM_BASE) != 0x5aa555aa)
+ goto fail;
+
+ if ((ahd_inb(ahd, INTSTAT) & PCIINT) != 0) {
+ u_int targpcistat;
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ targpcistat = ahd_inb(ahd, TARGPCISTAT);
+ if ((targpcistat & STA) != 0)
+ goto fail;
+ }
+
+ error = 0;
+
+fail:
+ if ((ahd_inb(ahd, INTSTAT) & PCIINT) != 0) {
+
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ targpcistat = ahd_inb(ahd, TARGPCISTAT);
+
+ /* Silently clear any latched errors. */
+ ahd_outb(ahd, TARGPCISTAT, targpcistat);
+ pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+ pci_status1, /*bytes*/1);
+ ahd_outb(ahd, CLRINT, CLRPCIINT);
+ }
+ ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, cmd, /*bytes*/2);
+ return (error);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static int
+ahd_check_extport(struct ahd_softc *ahd)
+{
+ struct vpd_config vpd;
+ struct seeprom_config *sc;
+ u_int adapter_control;
+ int have_seeprom;
+ int error;
+
+ sc = ahd->seep_config;
+ have_seeprom = ahd_acquire_seeprom(ahd);
+ if (have_seeprom) {
+ u_int start_addr;
+
+ /*
+ * Fetch VPD for this function and parse it.
+ */
+ if (bootverbose)
+ printf("%s: Reading VPD from SEEPROM...",
+ ahd_name(ahd));
+
+ /* Address is always in units of 16bit words */
+ start_addr = ((2 * sizeof(*sc))
+ + (sizeof(vpd) * (ahd->channel - 'A'))) / 2;
+
+ error = ahd_read_seeprom(ahd, (uint16_t *)&vpd,
+ start_addr, sizeof(vpd)/2,
+ /*bytestream*/TRUE);
+ if (error == 0)
+ error = ahd_parse_vpddata(ahd, &vpd);
+ if (bootverbose)
+ printf("%s: VPD parsing %s\n",
+ ahd_name(ahd),
+ error == 0 ? "successful" : "failed");
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahd_name(ahd));
+
+ /* Address is always in units of 16bit words */
+ start_addr = (sizeof(*sc) / 2) * (ahd->channel - 'A');
+
+ error = ahd_read_seeprom(ahd, (uint16_t *)sc,
+ start_addr, sizeof(*sc)/2,
+ /*bytestream*/FALSE);
+
+ if (error != 0) {
+ printf("Unable to read SEEPROM\n");
+ have_seeprom = 0;
+ } else {
+ have_seeprom = ahd_verify_cksum(sc);
+
+ if (bootverbose) {
+ if (have_seeprom == 0)
+ printf ("checksum error\n");
+ else
+ printf ("done.\n");
+ }
+ }
+ ahd_release_seeprom(ahd);
+ }
+
+ if (!have_seeprom) {
+ u_int nvram_scb;
+
+ /*
+ * Pull scratch ram settings and treat them as
+ * if they are the contents of an seeprom if
+ * the 'ADPT', 'BIOS', or 'ASPI' signature is found
+ * in SCB 0xFF. We manually compose the data as 16bit
+ * values to avoid endian issues.
+ */
+ ahd_set_scbptr(ahd, 0xFF);
+ nvram_scb = ahd_inb_scbram(ahd, SCB_BASE + NVRAM_SCB_OFFSET);
+ if (nvram_scb != 0xFF
+ && ((ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+ && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'D'
+ && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+ && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'T')
+ || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'B'
+ && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'I'
+ && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'O'
+ && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'S')
+ || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+ && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'S'
+ && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+ && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'I'))) {
+ uint16_t *sc_data;
+ int i;
+
+ ahd_set_scbptr(ahd, nvram_scb);
+ sc_data = (uint16_t *)sc;
+ for (i = 0; i < 64; i += 2)
+ *sc_data++ = ahd_inw_scbram(ahd, SCB_BASE+i);
+ have_seeprom = ahd_verify_cksum(sc);
+ if (have_seeprom)
+ ahd->flags |= AHD_SCB_CONFIG_USED;
+ }
+ }
+
+#if AHD_DEBUG
+ if (have_seeprom != 0
+ && (ahd_debug & AHD_DUMP_SEEPROM) != 0) {
+ uint16_t *sc_data;
+ int i;
+
+ printf("%s: Seeprom Contents:", ahd_name(ahd));
+ sc_data = (uint16_t *)sc;
+ for (i = 0; i < (sizeof(*sc)); i += 2)
+ printf("\n\t0x%.4x", sc_data[i]);
+ printf("\n");
+ }
+#endif
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available.\n", ahd_name(ahd));
+ ahd->flags |= AHD_USEDEFAULTS;
+ error = ahd_default_config(ahd);
+ adapter_control = CFAUTOTERM|CFSEAUTOTERM;
+ free(ahd->seep_config, M_DEVBUF);
+ ahd->seep_config = NULL;
+ } else {
+ error = ahd_parse_cfgdata(ahd, sc);
+ adapter_control = sc->adapter_control;
+ }
+ if (error != 0)
+ return (error);
+
+ ahd_configure_termination(ahd, adapter_control);
+
+ return (0);
+}
+
+static void
+ahd_configure_termination(struct ahd_softc *ahd, u_int adapter_control)
+{
+ int error;
+ u_int sxfrctl1;
+ uint8_t termctl;
+ uint32_t devconfig;
+
+ devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+ devconfig &= ~STPWLEVEL;
+ if ((ahd->flags & AHD_STPWLEVEL_A) != 0)
+ devconfig |= STPWLEVEL;
+ if (bootverbose)
+ printf("%s: STPWLEVEL is %s\n",
+ ahd_name(ahd), (devconfig & STPWLEVEL) ? "on" : "off");
+ ahd_pci_write_config(ahd->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+
+ /* Make sure current sensing is off. */
+ if ((ahd->flags & AHD_CURRENT_SENSING) != 0) {
+ (void)ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+ }
+
+ /*
+ * Read to sense. Write to set.
+ */
+ error = ahd_read_flexport(ahd, FLXADDR_TERMCTL, &termctl);
+ if ((adapter_control & CFAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual Primary Termination\n",
+ ahd_name(ahd));
+ termctl &= ~(FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH);
+ if ((adapter_control & CFSTERM) != 0)
+ termctl |= FLX_TERMCTL_ENPRILOW;
+ if ((adapter_control & CFWSTERM) != 0)
+ termctl |= FLX_TERMCTL_ENPRIHIGH;
+ } else if (error != 0) {
+ printf("%s: Primary Auto-Term Sensing failed! "
+ "Using Defaults.\n", ahd_name(ahd));
+ termctl = FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH;
+ }
+
+ if ((adapter_control & CFSEAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual Secondary Termination\n",
+ ahd_name(ahd));
+ termctl &= ~(FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH);
+ if ((adapter_control & CFSELOWTERM) != 0)
+ termctl |= FLX_TERMCTL_ENSECLOW;
+ if ((adapter_control & CFSEHIGHTERM) != 0)
+ termctl |= FLX_TERMCTL_ENSECHIGH;
+ } else if (error != 0) {
+ printf("%s: Secondary Auto-Term Sensing failed! "
+ "Using Defaults.\n", ahd_name(ahd));
+ termctl |= FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH;
+ }
+
+ /*
+ * Now set the termination based on what we found.
+ */
+ sxfrctl1 = ahd_inb(ahd, SXFRCTL1) & ~STPWEN;
+ if ((termctl & FLX_TERMCTL_ENPRILOW) != 0) {
+ ahd->flags |= AHD_TERM_ENB_A;
+ sxfrctl1 |= STPWEN;
+ }
+ /* Must set the latch once in order to be effective. */
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+ ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+ error = ahd_write_flexport(ahd, FLXADDR_TERMCTL, termctl);
+ if (error != 0) {
+ printf("%s: Unable to set termination settings!\n",
+ ahd_name(ahd));
+ } else if (bootverbose) {
+ printf("%s: Primary High byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENPRIHIGH) ? "En" : "Dis");
+
+ printf("%s: Primary Low byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENPRILOW) ? "En" : "Dis");
+
+ printf("%s: Secondary High byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENSECHIGH) ? "En" : "Dis");
+
+ printf("%s: Secondary Low byte termination %sabled\n",
+ ahd_name(ahd),
+ (termctl & FLX_TERMCTL_ENSECLOW) ? "En" : "Dis");
+ }
+ return;
+}
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+static const char *split_status_source[] =
+{
+ "DFF0",
+ "DFF1",
+ "OVLY",
+ "CMC",
+};
+
+static const char *pci_status_source[] =
+{
+ "DFF0",
+ "DFF1",
+ "SG",
+ "CMC",
+ "OVLY",
+ "NONE",
+ "MSI",
+ "TARG"
+};
+
+static const char *split_status_strings[] =
+{
+ "%s: Received split response in %s.\n",
+ "%s: Received split completion error message in %s\n",
+ "%s: Receive overrun in %s\n",
+ "%s: Count not complete in %s\n",
+ "%s: Split completion data bucket in %s\n",
+ "%s: Split completion address error in %s\n",
+ "%s: Split completion byte count error in %s\n",
+ "%s: Signaled Target-abort to early terminate a split in %s\n"
+};
+
+static const char *pci_status_strings[] =
+{
+ "%s: Data Parity Error has been reported via PERR# in %s\n",
+ "%s: Target initial wait state error in %s\n",
+ "%s: Split completion read data parity error in %s\n",
+ "%s: Split completion address attribute parity error in %s\n",
+ "%s: Received a Target Abort in %s\n",
+ "%s: Received a Master Abort in %s\n",
+ "%s: Signal System Error Detected in %s\n",
+ "%s: Address or Write Phase Parity Error Detected in %s.\n"
+};
+
+void
+ahd_pci_intr(struct ahd_softc *ahd)
+{
+ uint8_t pci_status[8];
+ ahd_mode_state saved_modes;
+ u_int pci_status1;
+ u_int intstat;
+ u_int i;
+ u_int reg;
+
+ intstat = ahd_inb(ahd, INTSTAT);
+
+ if ((intstat & SPLTINT) != 0)
+ ahd_pci_split_intr(ahd, intstat);
+
+ if ((intstat & PCIINT) == 0)
+ return;
+
+ printf("%s: PCI error Interrupt\n", ahd_name(ahd));
+ saved_modes = ahd_save_modes(ahd);
+ ahd_dump_card_state(ahd);
+ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+ for (i = 0, reg = DF0PCISTAT; i < 8; i++, reg++) {
+
+ if (i == 5)
+ continue;
+ pci_status[i] = ahd_inb(ahd, reg);
+ /* Clear latched errors. So our interrupt deasserts. */
+ ahd_outb(ahd, reg, pci_status[i]);
+ }
+
+ for (i = 0; i < 8; i++) {
+ u_int bit;
+
+ if (i == 5)
+ continue;
+
+ for (bit = 0; bit < 8; bit++) {
+
+ if ((pci_status[i] & (0x1 << bit)) != 0) {
+ static const char *s;
+
+ s = pci_status_strings[bit];
+ if (i == 7/*TARG*/ && bit == 3)
+ s = "%s: Signaled Target Abort\n";
+ printf(s, ahd_name(ahd), pci_status_source[i]);
+ }
+ }
+ }
+ pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+ pci_status1, /*bytes*/1);
+ ahd_restore_modes(ahd, saved_modes);
+ ahd_outb(ahd, CLRINT, CLRPCIINT);
+ ahd_unpause(ahd);
+}
+
+static void
+ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
+{
+ uint8_t split_status[4];
+ uint8_t split_status1[4];
+ uint8_t sg_split_status[2];
+ uint8_t sg_split_status1[2];
+ ahd_mode_state saved_modes;
+ u_int i;
+ uint16_t pcix_status;
+
+ /*
+ * Check for splits in all modes. Modes 0 and 1
+ * additionally have SG engine splits to look at.
+ */
+ pcix_status = ahd_pci_read_config(ahd->dev_softc, PCIXR_STATUS,
+ /*bytes*/2);
+ printf("%s: PCI Split Interrupt - PCI-X status = 0x%x\n",
+ ahd_name(ahd), pcix_status);
+ saved_modes = ahd_save_modes(ahd);
+ for (i = 0; i < 4; i++) {
+ ahd_set_modes(ahd, i, i);
+
+ split_status[i] = ahd_inb(ahd, DCHSPLTSTAT0);
+ split_status1[i] = ahd_inb(ahd, DCHSPLTSTAT1);
+ /* Clear latched errors. So our interrupt deasserts. */
+ ahd_outb(ahd, DCHSPLTSTAT0, split_status[i]);
+ ahd_outb(ahd, DCHSPLTSTAT1, split_status1[i]);
+ if (i > 1)
+ continue;
+ sg_split_status[i] = ahd_inb(ahd, SGSPLTSTAT0);
+ sg_split_status1[i] = ahd_inb(ahd, SGSPLTSTAT1);
+ /* Clear latched errors. So our interrupt deasserts. */
+ ahd_outb(ahd, SGSPLTSTAT0, sg_split_status[i]);
+ ahd_outb(ahd, SGSPLTSTAT1, sg_split_status1[i]);
+ }
+
+ for (i = 0; i < 4; i++) {
+ u_int bit;
+
+ for (bit = 0; bit < 8; bit++) {
+
+ if ((split_status[i] & (0x1 << bit)) != 0) {
+ static const char *s;
+
+ s = split_status_strings[bit];
+ printf(s, ahd_name(ahd),
+ split_status_source[i]);
+ }
+
+ if (i > 1)
+ continue;
+
+ if ((sg_split_status[i] & (0x1 << bit)) != 0) {
+ static const char *s;
+
+ s = split_status_strings[bit];
+ printf(s, ahd_name(ahd), "SG");
+ }
+ }
+ }
+ /*
+ * Clear PCI-X status bits.
+ */
+ ahd_pci_write_config(ahd->dev_softc, PCIXR_STATUS,
+ pcix_status, /*bytes*/2);
+ ahd_outb(ahd, CLRINT, CLRSPLTINT);
+ ahd_restore_modes(ahd, saved_modes);
+}
+
+static int
+ahd_aic7901_setup(struct ahd_softc *ahd)
+{
+
+ ahd->chip = AHD_AIC7901;
+ ahd->features = AHD_AIC7901_FE;
+ return (ahd_aic790X_setup(ahd));
+}
+
+static int
+ahd_aic7901A_setup(struct ahd_softc *ahd)
+{
+
+ ahd->chip = AHD_AIC7901A;
+ ahd->features = AHD_AIC7901A_FE;
+ return (ahd_aic790X_setup(ahd));
+}
+
+static int
+ahd_aic7902_setup(struct ahd_softc *ahd)
+{
+ ahd->chip = AHD_AIC7902;
+ ahd->features = AHD_AIC7902_FE;
+ return (ahd_aic790X_setup(ahd));
+}
+
+static int
+ahd_aic790X_setup(struct ahd_softc *ahd)
+{
+ ahd_dev_softc_t pci;
+ u_int rev;
+
+ pci = ahd->dev_softc;
+ rev = ahd_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev < ID_AIC7902_PCI_REV_A4) {
+ printf("%s: Unable to attach to unsupported chip revision %d\n",
+ ahd_name(ahd), rev);
+ ahd_pci_write_config(pci, PCIR_COMMAND, 0, /*bytes*/2);
+ return (ENXIO);
+ }
+ ahd->channel = ahd_get_pci_function(pci) + 'A';
+ if (rev < ID_AIC7902_PCI_REV_B0) {
+ /*
+ * Enable A series workarounds.
+ */
+ ahd->bugs |= AHD_SENT_SCB_UPDATE_BUG|AHD_ABORT_LQI_BUG
+ | AHD_PKT_BITBUCKET_BUG|AHD_LONG_SETIMO_BUG
+ | AHD_NLQICRC_DELAYED_BUG|AHD_SCSIRST_BUG
+ | AHD_LQO_ATNO_BUG|AHD_AUTOFLUSH_BUG
+ | AHD_CLRLQO_AUTOCLR_BUG|AHD_PCIX_MMAPIO_BUG
+ | AHD_PCIX_CHIPRST_BUG|AHD_PCIX_SCBRAM_RD_BUG
+ | AHD_PKTIZED_STATUS_BUG|AHD_PKT_LUN_BUG
+ | AHD_MDFF_WSCBPTR_BUG|AHD_REG_SLOW_SETTLE_BUG
+ | AHD_SET_MODE_BUG|AHD_BUSFREEREV_BUG
+ | AHD_NONPACKFIFO_BUG|AHD_PACED_NEGTABLE_BUG
+ | AHD_FAINT_LED_BUG;
+
+ /*
+ * IO Cell paramter setup.
+ */
+ AHD_SET_PRECOMP(ahd, AHD_PRECOMP_CUTBACK_29);
+
+ if ((ahd->flags & AHD_HP_BOARD) == 0)
+ AHD_SET_SLEWRATE(ahd, AHD_SLEWRATE_DEF_REVA);
+ } else {
+ u_int devconfig1;
+
+ ahd->features |= AHD_RTI|AHD_NEW_IOCELL_OPTS
+ | AHD_NEW_DFCNTRL_OPTS|AHD_FAST_CDB_DELIVERY;
+ ahd->bugs |= AHD_LQOOVERRUN_BUG|AHD_EARLY_REQ_BUG;
+
+ /*
+ * Some issues have been resolved in the 7901B.
+ */
+ if ((ahd->features & AHD_MULTI_FUNC) != 0)
+ ahd->bugs |= AHD_INTCOLLISION_BUG|AHD_ABORT_LQI_BUG;
+
+ /*
+ * IO Cell paramter setup.
+ */
+ AHD_SET_PRECOMP(ahd, AHD_PRECOMP_CUTBACK_29);
+ AHD_SET_SLEWRATE(ahd, AHD_SLEWRATE_DEF_REVB);
+ AHD_SET_AMPLITUDE(ahd, AHD_AMPLITUDE_DEF);
+
+ /*
+ * Set the PREQDIS bit for H2B which disables some workaround
+ * that doesn't work on regular PCI busses.
+ * XXX - Find out exactly what this does from the hardware
+ * folks!
+ */
+ devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+ ahd_pci_write_config(pci, DEVCONFIG1,
+ devconfig1|PREQDIS, /*bytes*/1);
+ devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+ }
+
+ return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.h b/drivers/scsi/aic7xxx/aic79xx_pci.h
new file mode 100644
index 000000000000..b5cfeabdfecf
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.h
@@ -0,0 +1,70 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ *
+ */
+#ifndef _AIC79XX_PCI_H_
+#define _AIC79XX_PCI_H_
+
+#define ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull
+#define ID_ALL_IROC_MASK 0xFF7FFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK 0xFFF0FFFF00000000ull
+#define ID_9005_GENERIC_IROC_MASK 0xFF70FFFF00000000ull
+
+#define ID_AIC7901 0x800F9005FFFF9005ull
+#define ID_AHA_29320A 0x8000900500609005ull
+#define ID_AHA_29320ALP 0x8017900500449005ull
+
+#define ID_AIC7901A 0x801E9005FFFF9005ull
+#define ID_AHA_29320 0x8012900500429005ull
+#define ID_AHA_29320B 0x8013900500439005ull
+#define ID_AHA_29320LP 0x8014900500449005ull
+
+#define ID_AIC7902 0x801F9005FFFF9005ull
+#define ID_AIC7902_B 0x801D9005FFFF9005ull
+#define ID_AHA_39320 0x8010900500409005ull
+#define ID_AHA_39320_B 0x8015900500409005ull
+#define ID_AHA_39320A 0x8016900500409005ull
+#define ID_AHA_39320D 0x8011900500419005ull
+#define ID_AHA_39320D_B 0x801C900500419005ull
+#define ID_AHA_39320D_HP 0x8011900500AC0E11ull
+#define ID_AHA_39320D_B_HP 0x801C900500AC0E11ull
+
+#endif /* _AIC79XX_PCI_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c
new file mode 100644
index 000000000000..e01cd6175e34
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_proc.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_proc.c#19 $
+ */
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+static void copy_mem_info(struct info_str *info, char *data, int len);
+static int copy_info(struct info_str *info, char *fmt, ...);
+static void ahd_dump_target_state(struct ahd_softc *ahd,
+ struct info_str *info,
+ u_int our_id, char channel,
+ u_int target_id, u_int target_offset);
+static void ahd_dump_device_state(struct info_str *info,
+ struct ahd_linux_device *dev);
+static int ahd_proc_write_seeprom(struct ahd_softc *ahd,
+ char *buffer, int length);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->offset + info->length)
+ len = info->offset + info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+
+ if (info->pos < info->offset) {
+ off_t partial;
+
+ partial = info->offset - info->pos;
+ data += partial;
+ info->pos += partial;
+ len -= partial;
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer, data, len);
+ info->pos += len;
+ info->buffer += len;
+ }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return (len);
+}
+
+void
+ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
+{
+ u_int speed;
+ u_int freq;
+ u_int mb;
+
+ if (tinfo->period == AHD_PERIOD_UNKNOWN) {
+ copy_info(info, "Renegotiation Pending\n");
+ return;
+ }
+ speed = 3300;
+ freq = 0;
+ if (tinfo->offset != 0) {
+ freq = aic_calc_syncsrate(tinfo->period);
+ speed = freq;
+ }
+ speed *= (0x01 << tinfo->width);
+ mb = speed / 1000;
+ if (mb > 0)
+ copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+ else
+ copy_info(info, "%dKB/s transfers", speed);
+
+ if (freq != 0) {
+ int printed_options;
+
+ printed_options = 0;
+ copy_info(info, " (%d.%03dMHz", freq / 1000, freq % 1000);
+ if ((tinfo->ppr_options & MSG_EXT_PPR_RD_STRM) != 0) {
+ copy_info(info, " RDSTRM");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ copy_info(info, "%s", printed_options ? "|DT" : " DT");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ copy_info(info, "%s", printed_options ? "|IU" : " IU");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) {
+ copy_info(info, "%s",
+ printed_options ? "|RTI" : " RTI");
+ printed_options++;
+ }
+ if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
+ copy_info(info, "%s",
+ printed_options ? "|QAS" : " QAS");
+ printed_options++;
+ }
+ }
+
+ if (tinfo->width > 0) {
+ if (freq != 0) {
+ copy_info(info, ", ");
+ } else {
+ copy_info(info, " (");
+ }
+ copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+ } else if (freq != 0) {
+ copy_info(info, ")");
+ }
+ copy_info(info, "\n");
+}
+
+static void
+ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
+ u_int our_id, char channel, u_int target_id,
+ u_int target_offset)
+{
+ struct ahd_linux_target *targ;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ int lun;
+
+ tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+ target_id, &tstate);
+ copy_info(info, "Target %d Negotiation Settings\n", target_id);
+ copy_info(info, "\tUser: ");
+ ahd_format_transinfo(info, &tinfo->user);
+ targ = ahd->platform_data->targets[target_offset];
+ if (targ == NULL)
+ return;
+
+ copy_info(info, "\tGoal: ");
+ ahd_format_transinfo(info, &tinfo->goal);
+ copy_info(info, "\tCurr: ");
+ ahd_format_transinfo(info, &tinfo->curr);
+ copy_info(info, "\tTransmission Errors %ld\n", targ->errors_detected);
+
+ for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+ struct ahd_linux_device *dev;
+
+ dev = targ->devices[lun];
+
+ if (dev == NULL)
+ continue;
+
+ ahd_dump_device_state(info, dev);
+ }
+}
+
+static void
+ahd_dump_device_state(struct info_str *info, struct ahd_linux_device *dev)
+{
+ copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+ dev->target->channel + 'A', dev->target->target, dev->lun);
+
+ copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
+ copy_info(info, "\t\tCommands Active %d\n", dev->active);
+ copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+ copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+ copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+static int
+ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
+{
+ ahd_mode_state saved_modes;
+ int have_seeprom;
+ u_long s;
+ int paused;
+ int written;
+
+ /* Default to failure. */
+ written = -EINVAL;
+ ahd_lock(ahd, &s);
+ paused = ahd_is_paused(ahd);
+ if (!paused)
+ ahd_pause(ahd);
+
+ saved_modes = ahd_save_modes(ahd);
+ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+ if (length != sizeof(struct seeprom_config)) {
+ printf("ahd_proc_write_seeprom: incorrect buffer size\n");
+ goto done;
+ }
+
+ have_seeprom = ahd_verify_cksum((struct seeprom_config*)buffer);
+ if (have_seeprom == 0) {
+ printf("ahd_proc_write_seeprom: cksum verification failed\n");
+ goto done;
+ }
+
+ have_seeprom = ahd_acquire_seeprom(ahd);
+ if (!have_seeprom) {
+ printf("ahd_proc_write_seeprom: No Serial EEPROM\n");
+ goto done;
+ } else {
+ u_int start_addr;
+
+ if (ahd->seep_config == NULL) {
+ ahd->seep_config = malloc(sizeof(*ahd->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahd->seep_config == NULL) {
+ printf("aic79xx: Unable to allocate serial "
+ "eeprom buffer. Write failing\n");
+ goto done;
+ }
+ }
+ printf("aic79xx: Writing Serial EEPROM\n");
+ start_addr = 32 * (ahd->channel - 'A');
+ ahd_write_seeprom(ahd, (u_int16_t *)buffer, start_addr,
+ sizeof(struct seeprom_config)/2);
+ ahd_read_seeprom(ahd, (uint16_t *)ahd->seep_config,
+ start_addr, sizeof(struct seeprom_config)/2,
+ /*ByteStream*/FALSE);
+ ahd_release_seeprom(ahd);
+ written = length;
+ }
+
+done:
+ ahd_restore_modes(ahd, saved_modes);
+ if (!paused)
+ ahd_unpause(ahd);
+ ahd_unlock(ahd, &s);
+ return (written);
+}
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ahd_linux_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+#else
+ahd_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
+ off_t offset, int length, int inout)
+#endif
+{
+ struct ahd_softc *ahd;
+ struct info_str info;
+ char ahd_info[256];
+ u_long l;
+ u_int max_targ;
+ u_int i;
+ int retval;
+
+ retval = -EINVAL;
+ ahd_list_lock(&l);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+ if (ahd->platform_data->host->host_no == hostno)
+ break;
+ }
+#else
+ ahd = ahd_find_softc(*(struct ahd_softc **)shost->hostdata);
+#endif
+
+ if (ahd == NULL)
+ goto done;
+
+ /* Has data been written to the file? */
+ if (inout == TRUE) {
+ retval = ahd_proc_write_seeprom(ahd, buffer, length);
+ goto done;
+ }
+
+ if (start)
+ *start = buffer;
+
+ info.buffer = buffer;
+ info.length = length;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "Adaptec AIC79xx driver version: %s\n",
+ AIC79XX_DRIVER_VERSION);
+ copy_info(&info, "%s\n", ahd->description);
+ ahd_controller_info(ahd, ahd_info);
+ copy_info(&info, "%s\n", ahd_info);
+ copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n",
+ ahd->scb_data.numscbs, AHD_NSEG);
+
+ max_targ = 15;
+
+ if (ahd->seep_config == NULL)
+ copy_info(&info, "No Serial EEPROM\n");
+ else {
+ copy_info(&info, "Serial EEPROM:\n");
+ for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "0x%.4x ",
+ ((uint16_t*)ahd->seep_config)[i]);
+ }
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "\n");
+
+ if ((ahd->features & AHD_WIDE) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+
+ ahd_dump_target_state(ahd, &info, ahd->our_id, 'A',
+ /*target_id*/i, /*target_offset*/i);
+ }
+ retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+ ahd_list_unlock(&l);
+ return (retval);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
new file mode 100644
index 000000000000..c01ac39090d9
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
@@ -0,0 +1,3776 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#94 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $
+ */
+typedef int (ahd_reg_print_t)(u_int, u_int *, u_int);
+typedef struct ahd_reg_parse_entry {
+ char *name;
+ uint8_t value;
+ uint8_t mask;
+} ahd_reg_parse_entry_t;
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mode_ptr_print;
+#else
+#define ahd_mode_ptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MODE_PTR", 0x00, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intstat_print;
+#else
+#define ahd_intstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTSTAT", 0x01, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintcode_print;
+#else
+#define ahd_seqintcode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTCODE", 0x02, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrint_print;
+#else
+#define ahd_clrint_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRINT", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_error_print;
+#else
+#define ahd_error_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ERROR", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrerr_print;
+#else
+#define ahd_clrerr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRERR", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hcntrl_print;
+#else
+#define ahd_hcntrl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HCNTRL", 0x05, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hnscb_qoff_print;
+#else
+#define ahd_hnscb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HNSCB_QOFF", 0x06, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hescb_qoff_print;
+#else
+#define ahd_hescb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HESCB_QOFF", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hs_mailbox_print;
+#else
+#define ahd_hs_mailbox_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HS_MAILBOX", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrseqintstat_print;
+#else
+#define ahd_clrseqintstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintstat_print;
+#else
+#define ahd_seqintstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_swtimer_print;
+#else
+#define ahd_swtimer_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SWTIMER", 0x0e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_snscb_qoff_print;
+#else
+#define ahd_snscb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SNSCB_QOFF", 0x10, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sescb_qoff_print;
+#else
+#define ahd_sescb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SESCB_QOFF", 0x12, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sdscb_qoff_print;
+#else
+#define ahd_sdscb_qoff_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SDSCB_QOFF", 0x14, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoff_ctlsta_print;
+#else
+#define ahd_qoff_ctlsta_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QOFF_CTLSTA", 0x16, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intctl_print;
+#else
+#define ahd_intctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTCTL", 0x18, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfcntrl_print;
+#else
+#define ahd_dfcntrl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFCNTRL", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dscommand0_print;
+#else
+#define ahd_dscommand0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSCOMMAND0", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfstatus_print;
+#else
+#define ahd_dfstatus_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFSTATUS", 0x1a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_cache_shadow_print;
+#else
+#define ahd_sg_cache_shadow_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SG_CACHE_SHADOW", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_arbctl_print;
+#else
+#define ahd_arbctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ARBCTL", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_cache_pre_print;
+#else
+#define ahd_sg_cache_pre_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SG_CACHE_PRE", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqin_print;
+#else
+#define ahd_lqin_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQIN", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_typeptr_print;
+#else
+#define ahd_typeptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TYPEPTR", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_tagptr_print;
+#else
+#define ahd_tagptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TAGPTR", 0x21, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lunptr_print;
+#else
+#define ahd_lunptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LUNPTR", 0x22, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_datalenptr_print;
+#else
+#define ahd_datalenptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DATALENPTR", 0x23, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_statlenptr_print;
+#else
+#define ahd_statlenptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "STATLENPTR", 0x24, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdlenptr_print;
+#else
+#define ahd_cmdlenptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDLENPTR", 0x25, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_attrptr_print;
+#else
+#define ahd_attrptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ATTRPTR", 0x26, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flagptr_print;
+#else
+#define ahd_flagptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLAGPTR", 0x27, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdptr_print;
+#else
+#define ahd_cmdptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDPTR", 0x28, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qnextptr_print;
+#else
+#define ahd_qnextptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QNEXTPTR", 0x29, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_idptr_print;
+#else
+#define ahd_idptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "IDPTR", 0x2a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_abrtbyteptr_print;
+#else
+#define ahd_abrtbyteptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ABRTBYTEPTR", 0x2b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_abrtbitptr_print;
+#else
+#define ahd_abrtbitptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ABRTBITPTR", 0x2c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmdbytes_print;
+#else
+#define ahd_maxcmdbytes_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMDBYTES", 0x2d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmd2rcv_print;
+#else
+#define ahd_maxcmd2rcv_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMD2RCV", 0x2e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shortthresh_print;
+#else
+#define ahd_shortthresh_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHORTTHRESH", 0x2f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lunlen_print;
+#else
+#define ahd_lunlen_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LUNLEN", 0x30, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cdblimit_print;
+#else
+#define ahd_cdblimit_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CDBLIMIT", 0x31, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmd_print;
+#else
+#define ahd_maxcmd_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMD", 0x32, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmdcnt_print;
+#else
+#define ahd_maxcmdcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MAXCMDCNT", 0x33, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqrsvd01_print;
+#else
+#define ahd_lqrsvd01_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQRSVD01", 0x34, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqrsvd16_print;
+#else
+#define ahd_lqrsvd16_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQRSVD16", 0x35, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqrsvd17_print;
+#else
+#define ahd_lqrsvd17_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQRSVD17", 0x36, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdrsvd0_print;
+#else
+#define ahd_cmdrsvd0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDRSVD0", 0x37, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl0_print;
+#else
+#define ahd_lqctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQCTL0", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl1_print;
+#else
+#define ahd_lqctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQCTL1", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsbist0_print;
+#else
+#define ahd_scsbist0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSBIST0", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl2_print;
+#else
+#define ahd_lqctl2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQCTL2", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsbist1_print;
+#else
+#define ahd_scsbist1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSBIST1", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq0_print;
+#else
+#define ahd_scsiseq0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISEQ0", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq1_print;
+#else
+#define ahd_scsiseq1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISEQ1", 0x3b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl0_print;
+#else
+#define ahd_sxfrctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SXFRCTL0", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_businitid_print;
+#else
+#define ahd_businitid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BUSINITID", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dlcount_print;
+#else
+#define ahd_dlcount_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DLCOUNT", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl1_print;
+#else
+#define ahd_sxfrctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SXFRCTL1", 0x3d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_bustargid_print;
+#else
+#define ahd_bustargid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BUSTARGID", 0x3e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl2_print;
+#else
+#define ahd_sxfrctl2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SXFRCTL2", 0x3e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dffstat_print;
+#else
+#define ahd_dffstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFFSTAT", 0x3f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsisigo_print;
+#else
+#define ahd_scsisigo_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISIGO", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_multargid_print;
+#else
+#define ahd_multargid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MULTARGID", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsisigi_print;
+#else
+#define ahd_scsisigi_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISIGI", 0x41, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiphase_print;
+#else
+#define ahd_scsiphase_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIPHASE", 0x42, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsidat0_img_print;
+#else
+#define ahd_scsidat0_img_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIDAT0_IMG", 0x43, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsidat_print;
+#else
+#define ahd_scsidat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIDAT", 0x44, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsibus_print;
+#else
+#define ahd_scsibus_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSIBUS", 0x46, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_targidin_print;
+#else
+#define ahd_targidin_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TARGIDIN", 0x48, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_selid_print;
+#else
+#define ahd_selid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SELID", 0x49, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sblkctl_print;
+#else
+#define ahd_sblkctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SBLKCTL", 0x4a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_optionmode_print;
+#else
+#define ahd_optionmode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OPTIONMODE", 0x4a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat0_print;
+#else
+#define ahd_sstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint0_print;
+#else
+#define ahd_clrsint0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode0_print;
+#else
+#define ahd_simode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint1_print;
+#else
+#define ahd_clrsint1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT1", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat1_print;
+#else
+#define ahd_sstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT1", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat2_print;
+#else
+#define ahd_sstat2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint2_print;
+#else
+#define ahd_clrsint2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode2_print;
+#else
+#define ahd_simode2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_perrdiag_print;
+#else
+#define ahd_perrdiag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PERRDIAG", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistate_print;
+#else
+#define ahd_lqistate_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTATE", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_soffcnt_print;
+#else
+#define ahd_soffcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SOFFCNT", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostate_print;
+#else
+#define ahd_lqostate_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTATE", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat0_print;
+#else
+#define ahd_lqistat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTAT0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqiint0_print;
+#else
+#define ahd_clrlqiint0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQIINT0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqimode0_print;
+#else
+#define ahd_lqimode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQIMODE0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqimode1_print;
+#else
+#define ahd_lqimode1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQIMODE1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat1_print;
+#else
+#define ahd_lqistat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTAT1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqiint1_print;
+#else
+#define ahd_clrlqiint1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQIINT1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat2_print;
+#else
+#define ahd_lqistat2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQISTAT2", 0x52, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat3_print;
+#else
+#define ahd_sstat3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SSTAT3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode3_print;
+#else
+#define ahd_simode3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint3_print;
+#else
+#define ahd_clrsint3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSINT3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqomode0_print;
+#else
+#define ahd_lqomode0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOMODE0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat0_print;
+#else
+#define ahd_lqostat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTAT0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqoint0_print;
+#else
+#define ahd_clrlqoint0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQOINT0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat1_print;
+#else
+#define ahd_lqostat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTAT1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqoint1_print;
+#else
+#define ahd_clrlqoint1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRLQOINT1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqomode1_print;
+#else
+#define ahd_lqomode1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOMODE1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat2_print;
+#else
+#define ahd_lqostat2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSTAT2", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_os_space_cnt_print;
+#else
+#define ahd_os_space_cnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OS_SPACE_CNT", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode1_print;
+#else
+#define ahd_simode1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SIMODE1", 0x57, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_gsfifo_print;
+#else
+#define ahd_gsfifo_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "GSFIFO", 0x58, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dffsxfrctl_print;
+#else
+#define ahd_dffsxfrctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFFSXFRCTL", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqoscsctl_print;
+#else
+#define ahd_lqoscsctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LQOSCSCTL", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_nextscb_print;
+#else
+#define ahd_nextscb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEXTSCB", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrseqintsrc_print;
+#else
+#define ahd_clrseqintsrc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CLRSEQINTSRC", 0x5b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintsrc_print;
+#else
+#define ahd_seqintsrc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTSRC", 0x5b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_currscb_print;
+#else
+#define ahd_currscb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CURRSCB", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqimode_print;
+#else
+#define ahd_seqimode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQIMODE", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mdffstat_print;
+#else
+#define ahd_mdffstat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MDFFSTAT", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_crccontrol_print;
+#else
+#define ahd_crccontrol_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CRCCONTROL", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfftag_print;
+#else
+#define ahd_dfftag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFFTAG", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastscb_print;
+#else
+#define ahd_lastscb_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LASTSCB", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsitest_print;
+#else
+#define ahd_scsitest_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSITEST", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_iopdnctl_print;
+#else
+#define ahd_iopdnctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "IOPDNCTL", 0x5f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shaddr_print;
+#else
+#define ahd_shaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHADDR", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negoaddr_print;
+#else
+#define ahd_negoaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGOADDR", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dgrpcrci_print;
+#else
+#define ahd_dgrpcrci_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DGRPCRCI", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negperiod_print;
+#else
+#define ahd_negperiod_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGPERIOD", 0x61, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_packcrci_print;
+#else
+#define ahd_packcrci_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PACKCRCI", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negoffset_print;
+#else
+#define ahd_negoffset_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGOFFSET", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negppropts_print;
+#else
+#define ahd_negppropts_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGPPROPTS", 0x63, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negconopts_print;
+#else
+#define ahd_negconopts_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEGCONOPTS", 0x64, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_annexcol_print;
+#else
+#define ahd_annexcol_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ANNEXCOL", 0x65, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scschkn_print;
+#else
+#define ahd_scschkn_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSCHKN", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_annexdat_print;
+#else
+#define ahd_annexdat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ANNEXDAT", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_iownid_print;
+#else
+#define ahd_iownid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "IOWNID", 0x67, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll960ctl0_print;
+#else
+#define ahd_pll960ctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL960CTL0", 0x68, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shcnt_print;
+#else
+#define ahd_shcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHCNT", 0x68, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_townid_print;
+#else
+#define ahd_townid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TOWNID", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll960ctl1_print;
+#else
+#define ahd_pll960ctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL960CTL1", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll960cnt0_print;
+#else
+#define ahd_pll960cnt0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL960CNT0", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_xsig_print;
+#else
+#define ahd_xsig_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "XSIG", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seloid_print;
+#else
+#define ahd_seloid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SELOID", 0x6b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll400ctl0_print;
+#else
+#define ahd_pll400ctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL400CTL0", 0x6c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_fairness_print;
+#else
+#define ahd_fairness_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FAIRNESS", 0x6c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll400ctl1_print;
+#else
+#define ahd_pll400ctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL400CTL1", 0x6d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pll400cnt0_print;
+#else
+#define ahd_pll400cnt0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLL400CNT0", 0x6e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_unfairness_print;
+#else
+#define ahd_unfairness_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "UNFAIRNESS", 0x6e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_haddr_print;
+#else
+#define ahd_haddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HADDR", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_plldelay_print;
+#else
+#define ahd_plldelay_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PLLDELAY", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hodmaadr_print;
+#else
+#define ahd_hodmaadr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HODMAADR", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hodmacnt_print;
+#else
+#define ahd_hodmacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HODMACNT", 0x78, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hcnt_print;
+#else
+#define ahd_hcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HCNT", 0x78, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hodmaen_print;
+#else
+#define ahd_hodmaen_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "HODMAEN", 0x7a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sghaddr_print;
+#else
+#define ahd_sghaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGHADDR", 0x7c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbhaddr_print;
+#else
+#define ahd_scbhaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBHADDR", 0x7c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sghcnt_print;
+#else
+#define ahd_sghcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGHCNT", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbhcnt_print;
+#else
+#define ahd_scbhcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBHCNT", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dff_thrsh_print;
+#else
+#define ahd_dff_thrsh_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFF_THRSH", 0x88, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_romaddr_print;
+#else
+#define ahd_romaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROMADDR", 0x8a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_romcntrl_print;
+#else
+#define ahd_romcntrl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROMCNTRL", 0x8d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_romdata_print;
+#else
+#define ahd_romdata_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROMDATA", 0x8e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg0_print;
+#else
+#define ahd_cmcrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG0", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_roenable_print;
+#else
+#define ahd_roenable_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ROENABLE", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg0_print;
+#else
+#define ahd_ovlyrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG0", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg0_print;
+#else
+#define ahd_dchrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG0", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg1_print;
+#else
+#define ahd_ovlyrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG1", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_nsenable_print;
+#else
+#define ahd_nsenable_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NSENABLE", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg1_print;
+#else
+#define ahd_dchrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG1", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg1_print;
+#else
+#define ahd_cmcrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG1", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg2_print;
+#else
+#define ahd_dchrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG2", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg2_print;
+#else
+#define ahd_ovlyrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG2", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg2_print;
+#else
+#define ahd_cmcrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG2", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ost_print;
+#else
+#define ahd_ost_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OST", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchrxmsg3_print;
+#else
+#define ahd_dchrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHRXMSG3", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcrxmsg3_print;
+#else
+#define ahd_cmcrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCRXMSG3", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pcixctl_print;
+#else
+#define ahd_pcixctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PCIXCTL", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyrxmsg3_print;
+#else
+#define ahd_ovlyrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYRXMSG3", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyseqbcnt_print;
+#else
+#define ahd_ovlyseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYSEQBCNT", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcseqbcnt_print;
+#else
+#define ahd_cmcseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCSEQBCNT", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchseqbcnt_print;
+#else
+#define ahd_dchseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHSEQBCNT", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcspltstat0_print;
+#else
+#define ahd_cmcspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyspltstat0_print;
+#else
+#define ahd_ovlyspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchspltstat0_print;
+#else
+#define ahd_dchspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchspltstat1_print;
+#else
+#define ahd_dchspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DCHSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcspltstat1_print;
+#else
+#define ahd_cmcspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyspltstat1_print;
+#else
+#define ahd_ovlyspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg0_print;
+#else
+#define ahd_sgrxmsg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG0", 0x98, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr0_print;
+#else
+#define ahd_slvspltoutadr0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR0", 0x98, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg1_print;
+#else
+#define ahd_sgrxmsg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG1", 0x99, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr1_print;
+#else
+#define ahd_slvspltoutadr1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR1", 0x99, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg2_print;
+#else
+#define ahd_sgrxmsg2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG2", 0x9a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr2_print;
+#else
+#define ahd_slvspltoutadr2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR2", 0x9a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgrxmsg3_print;
+#else
+#define ahd_sgrxmsg3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGRXMSG3", 0x9b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutadr3_print;
+#else
+#define ahd_slvspltoutadr3_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTADR3", 0x9b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgseqbcnt_print;
+#else
+#define ahd_sgseqbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGSEQBCNT", 0x9c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutattr0_print;
+#else
+#define ahd_slvspltoutattr0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTATTR0", 0x9c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutattr1_print;
+#else
+#define ahd_slvspltoutattr1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTATTR1", 0x9d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_slvspltoutattr2_print;
+#else
+#define ahd_slvspltoutattr2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SLVSPLTOUTATTR2", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgspltstat0_print;
+#else
+#define ahd_sgspltstat0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGSPLTSTAT0", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sfunct_print;
+#else
+#define ahd_sfunct_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SFUNCT", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgspltstat1_print;
+#else
+#define ahd_sgspltstat1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGSPLTSTAT1", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_df0pcistat_print;
+#else
+#define ahd_df0pcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DF0PCISTAT", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg0_print;
+#else
+#define ahd_reg0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "REG0", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_df1pcistat_print;
+#else
+#define ahd_df1pcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DF1PCISTAT", 0xa1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgpcistat_print;
+#else
+#define ahd_sgpcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SGPCISTAT", 0xa2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg1_print;
+#else
+#define ahd_reg1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "REG1", 0xa2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmcpcistat_print;
+#else
+#define ahd_cmcpcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMCPCISTAT", 0xa3, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlypcistat_print;
+#else
+#define ahd_ovlypcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYPCISTAT", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg_isr_print;
+#else
+#define ahd_reg_isr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "REG_ISR", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_state_print;
+#else
+#define ahd_sg_state_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SG_STATE", 0xa6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_msipcistat_print;
+#else
+#define ahd_msipcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MSIPCISTAT", 0xa6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_targpcistat_print;
+#else
+#define ahd_targpcistat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TARGPCISTAT", 0xa7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_data_count_odd_print;
+#else
+#define ahd_data_count_odd_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DATA_COUNT_ODD", 0xa7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbptr_print;
+#else
+#define ahd_scbptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBPTR", 0xa8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbacnt_print;
+#else
+#define ahd_ccscbacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBACNT", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbautoptr_print;
+#else
+#define ahd_scbautoptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBAUTOPTR", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgaddr_print;
+#else
+#define ahd_ccsgaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSGADDR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbaddr_print;
+#else
+#define ahd_ccscbaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBADDR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbadr_bk_print;
+#else
+#define ahd_ccscbadr_bk_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBADR_BK", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmc_rambist_print;
+#else
+#define ahd_cmc_rambist_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMC_RAMBIST", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgctl_print;
+#else
+#define ahd_ccsgctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSGCTL", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbctl_print;
+#else
+#define ahd_ccscbctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBCTL", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgram_print;
+#else
+#define ahd_ccsgram_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSGRAM", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexadr_print;
+#else
+#define ahd_flexadr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXADR", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbram_print;
+#else
+#define ahd_ccscbram_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CCSCBRAM", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexcnt_print;
+#else
+#define ahd_flexcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXCNT", 0xb3, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexdmastat_print;
+#else
+#define ahd_flexdmastat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXDMASTAT", 0xb5, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flexdata_print;
+#else
+#define ahd_flexdata_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLEXDATA", 0xb6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brddat_print;
+#else
+#define ahd_brddat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRDDAT", 0xb8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brdctl_print;
+#else
+#define ahd_brdctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRDCTL", 0xb9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seeadr_print;
+#else
+#define ahd_seeadr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEEADR", 0xba, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seedat_print;
+#else
+#define ahd_seedat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEEDAT", 0xbc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seectl_print;
+#else
+#define ahd_seectl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEECTL", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seestat_print;
+#else
+#define ahd_seestat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEESTAT", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbcnt_print;
+#else
+#define ahd_scbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCBCNT", 0xbf, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfwaddr_print;
+#else
+#define ahd_dfwaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFWADDR", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspfltrctl_print;
+#else
+#define ahd_dspfltrctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPFLTRCTL", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspdatactl_print;
+#else
+#define ahd_dspdatactl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPDATACTL", 0xc1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfraddr_print;
+#else
+#define ahd_dfraddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFRADDR", 0xc2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspreqctl_print;
+#else
+#define ahd_dspreqctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPREQCTL", 0xc2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspackctl_print;
+#else
+#define ahd_dspackctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPACKCTL", 0xc3, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfdat_print;
+#else
+#define ahd_dfdat_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFDAT", 0xc4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspselect_print;
+#else
+#define ahd_dspselect_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DSPSELECT", 0xc4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_wrtbiasctl_print;
+#else
+#define ahd_wrtbiasctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WRTBIASCTL", 0xc5, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_rcvrbiosctl_print;
+#else
+#define ahd_rcvrbiosctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "RCVRBIOSCTL", 0xc6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_wrtbiascalc_print;
+#else
+#define ahd_wrtbiascalc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WRTBIASCALC", 0xc7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfptrs_print;
+#else
+#define ahd_dfptrs_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFPTRS", 0xc8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_rcvrbiascalc_print;
+#else
+#define ahd_rcvrbiascalc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "RCVRBIASCALC", 0xc8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfbkptr_print;
+#else
+#define ahd_dfbkptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFBKPTR", 0xc9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_skewcalc_print;
+#else
+#define ahd_skewcalc_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SKEWCALC", 0xc9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfdbctl_print;
+#else
+#define ahd_dfdbctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFDBCTL", 0xcb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfscnt_print;
+#else
+#define ahd_dfscnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFSCNT", 0xcc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfbcnt_print;
+#else
+#define ahd_dfbcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DFBCNT", 0xce, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ovlyaddr_print;
+#else
+#define ahd_ovlyaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "OVLYADDR", 0xd4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqctl0_print;
+#else
+#define ahd_seqctl0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQCTL0", 0xd6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqctl1_print;
+#else
+#define ahd_seqctl1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQCTL1", 0xd7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flags_print;
+#else
+#define ahd_flags_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FLAGS", 0xd8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintctl_print;
+#else
+#define ahd_seqintctl_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQINTCTL", 0xd9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqram_print;
+#else
+#define ahd_seqram_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQRAM", 0xda, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_prgmcnt_print;
+#else
+#define ahd_prgmcnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "PRGMCNT", 0xde, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_accum_print;
+#else
+#define ahd_accum_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ACCUM", 0xe0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sindex_print;
+#else
+#define ahd_sindex_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SINDEX", 0xe2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dindex_print;
+#else
+#define ahd_dindex_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DINDEX", 0xe4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brkaddr1_print;
+#else
+#define ahd_brkaddr1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRKADDR1", 0xe6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brkaddr0_print;
+#else
+#define ahd_brkaddr0_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "BRKADDR0", 0xe6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allones_print;
+#else
+#define ahd_allones_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ALLONES", 0xe8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allzeros_print;
+#else
+#define ahd_allzeros_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ALLZEROS", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_none_print;
+#else
+#define ahd_none_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NONE", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sindir_print;
+#else
+#define ahd_sindir_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SINDIR", 0xec, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dindir_print;
+#else
+#define ahd_dindir_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DINDIR", 0xed, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_function1_print;
+#else
+#define ahd_function1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "FUNCTION1", 0xf0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_stack_print;
+#else
+#define ahd_stack_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "STACK", 0xf2, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_curaddr_print;
+#else
+#define ahd_curaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CURADDR", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intvec1_addr_print;
+#else
+#define ahd_intvec1_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTVEC1_ADDR", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intvec2_addr_print;
+#else
+#define ahd_intvec2_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INTVEC2_ADDR", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastaddr_print;
+#else
+#define ahd_lastaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LASTADDR", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_longjmp_addr_print;
+#else
+#define ahd_longjmp_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LONGJMP_ADDR", 0xf8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_accum_save_print;
+#else
+#define ahd_accum_save_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ACCUM_SAVE", 0xfa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_scb_tails_print;
+#else
+#define ahd_waiting_scb_tails_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WAITING_SCB_TAILS", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ahd_pci_config_base_print;
+#else
+#define ahd_ahd_pci_config_base_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "AHD_PCI_CONFIG_BASE", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sram_base_print;
+#else
+#define ahd_sram_base_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SRAM_BASE", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_tid_head_print;
+#else
+#define ahd_waiting_tid_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WAITING_TID_HEAD", 0x120, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_tid_tail_print;
+#else
+#define ahd_waiting_tid_tail_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "WAITING_TID_TAIL", 0x122, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_next_queued_scb_addr_print;
+#else
+#define ahd_next_queued_scb_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "NEXT_QUEUED_SCB_ADDR", 0x124, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_scb_head_print;
+#else
+#define ahd_complete_scb_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "COMPLETE_SCB_HEAD", 0x128, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_scb_dmainprog_head_print;
+#else
+#define ahd_complete_scb_dmainprog_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "COMPLETE_SCB_DMAINPROG_HEAD", 0x12a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_dma_scb_head_print;
+#else
+#define ahd_complete_dma_scb_head_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_HEAD", 0x12c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qfreeze_count_print;
+#else
+#define ahd_qfreeze_count_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QFREEZE_COUNT", 0x12e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_mode_print;
+#else
+#define ahd_saved_mode_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SAVED_MODE", 0x130, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_msg_out_print;
+#else
+#define ahd_msg_out_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "MSG_OUT", 0x131, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dmaparams_print;
+#else
+#define ahd_dmaparams_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "DMAPARAMS", 0x132, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seq_flags_print;
+#else
+#define ahd_seq_flags_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQ_FLAGS", 0x133, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_scsiid_print;
+#else
+#define ahd_saved_scsiid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SAVED_SCSIID", 0x134, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_lun_print;
+#else
+#define ahd_saved_lun_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SAVED_LUN", 0x135, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastphase_print;
+#else
+#define ahd_lastphase_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LASTPHASE", 0x136, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoutfifo_entry_valid_tag_print;
+#else
+#define ahd_qoutfifo_entry_valid_tag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QOUTFIFO_ENTRY_VALID_TAG", 0x137, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shared_data_addr_print;
+#else
+#define ahd_shared_data_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SHARED_DATA_ADDR", 0x138, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoutfifo_next_addr_print;
+#else
+#define ahd_qoutfifo_next_addr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "QOUTFIFO_NEXT_ADDR", 0x13c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_kernel_tqinpos_print;
+#else
+#define ahd_kernel_tqinpos_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "KERNEL_TQINPOS", 0x140, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_tqinpos_print;
+#else
+#define ahd_tqinpos_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "TQINPOS", 0x141, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_arg_1_print;
+#else
+#define ahd_arg_1_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ARG_1", 0x142, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_arg_2_print;
+#else
+#define ahd_arg_2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ARG_2", 0x143, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_last_msg_print;
+#else
+#define ahd_last_msg_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LAST_MSG", 0x144, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq_template_print;
+#else
+#define ahd_scsiseq_template_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCSISEQ_TEMPLATE", 0x145, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_initiator_tag_print;
+#else
+#define ahd_initiator_tag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INITIATOR_TAG", 0x146, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seq_flags2_print;
+#else
+#define ahd_seq_flags2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SEQ_FLAGS2", 0x147, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allocfifo_scbptr_print;
+#else
+#define ahd_allocfifo_scbptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "ALLOCFIFO_SCBPTR", 0x148, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_timer_print;
+#else
+#define ahd_int_coalescing_timer_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESCING_TIMER", 0x14a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_maxcmds_print;
+#else
+#define ahd_int_coalescing_maxcmds_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESCING_MAXCMDS", 0x14c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_mincmds_print;
+#else
+#define ahd_int_coalescing_mincmds_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESCING_MINCMDS", 0x14d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmds_pending_print;
+#else
+#define ahd_cmds_pending_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDS_PENDING", 0x14e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_cmdcount_print;
+#else
+#define ahd_int_coalescing_cmdcount_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "INT_COALESCING_CMDCOUNT", 0x150, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_local_hs_mailbox_print;
+#else
+#define ahd_local_hs_mailbox_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "LOCAL_HS_MAILBOX", 0x151, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdsize_table_print;
+#else
+#define ahd_cmdsize_table_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "CMDSIZE_TABLE", 0x152, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_base_print;
+#else
+#define ahd_scb_base_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_BASE", 0x180, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_residual_datacnt_print;
+#else
+#define ahd_scb_residual_datacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_RESIDUAL_DATACNT", 0x180, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_residual_sgptr_print;
+#else
+#define ahd_scb_residual_sgptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_RESIDUAL_SGPTR", 0x184, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_scsi_status_print;
+#else
+#define ahd_scb_scsi_status_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SCSI_STATUS", 0x188, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_target_phases_print;
+#else
+#define ahd_scb_target_phases_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TARGET_PHASES", 0x189, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_target_data_dir_print;
+#else
+#define ahd_scb_target_data_dir_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TARGET_DATA_DIR", 0x18a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_target_itag_print;
+#else
+#define ahd_scb_target_itag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TARGET_ITAG", 0x18b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_sense_busaddr_print;
+#else
+#define ahd_scb_sense_busaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SENSE_BUSADDR", 0x18c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_tag_print;
+#else
+#define ahd_scb_tag_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TAG", 0x190, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_control_print;
+#else
+#define ahd_scb_control_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_CONTROL", 0x192, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_scsiid_print;
+#else
+#define ahd_scb_scsiid_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SCSIID", 0x193, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_lun_print;
+#else
+#define ahd_scb_lun_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_LUN", 0x194, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_task_attribute_print;
+#else
+#define ahd_scb_task_attribute_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TASK_ATTRIBUTE", 0x195, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_cdb_len_print;
+#else
+#define ahd_scb_cdb_len_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_CDB_LEN", 0x196, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_task_management_print;
+#else
+#define ahd_scb_task_management_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_TASK_MANAGEMENT", 0x197, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_dataptr_print;
+#else
+#define ahd_scb_dataptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_DATAPTR", 0x198, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_datacnt_print;
+#else
+#define ahd_scb_datacnt_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_DATACNT", 0x1a0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_sgptr_print;
+#else
+#define ahd_scb_sgptr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SGPTR", 0x1a4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_busaddr_print;
+#else
+#define ahd_scb_busaddr_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_BUSADDR", 0x1a8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_next_print;
+#else
+#define ahd_scb_next_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_NEXT", 0x1ac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_next2_print;
+#else
+#define ahd_scb_next2_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_NEXT2", 0x1ae, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_spare_print;
+#else
+#define ahd_scb_spare_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_SPARE", 0x1b0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_disconnected_lists_print;
+#else
+#define ahd_scb_disconnected_lists_print(regvalue, cur_col, wrap) \
+ ahd_print_register(NULL, 0, "SCB_DISCONNECTED_LISTS", 0x1b8, regvalue, cur_col, wrap)
+#endif
+
+
+#define MODE_PTR 0x00
+#define DST_MODE 0x70
+#define SRC_MODE 0x07
+
+#define INTSTAT 0x01
+#define INT_PEND 0xff
+#define HWERRINT 0x80
+#define BRKADRINT 0x40
+#define SWTMINT 0x20
+#define PCIINT 0x10
+#define SCSIINT 0x08
+#define SEQINT 0x04
+#define CMDCMPLT 0x02
+#define SPLTINT 0x01
+
+#define SEQINTCODE 0x02
+#define BAD_SCB_STATUS 0x1a
+#define SAW_HWERR 0x19
+#define TRACEPOINT3 0x18
+#define TRACEPOINT2 0x17
+#define TRACEPOINT1 0x16
+#define TRACEPOINT0 0x15
+#define TASKMGMT_CMD_CMPLT_OKAY 0x14
+#define TASKMGMT_FUNC_COMPLETE 0x13
+#define ENTERING_NONPACK 0x12
+#define CFG4OVERRUN 0x11
+#define STATUS_OVERRUN 0x10
+#define CFG4ISTAT_INTR 0x0f
+#define INVALID_SEQINT 0x0e
+#define ILLEGAL_PHASE 0x0d
+#define DUMP_CARD_STATE 0x0c
+#define MISSED_BUSFREE 0x0b
+#define MKMSG_FAILED 0x0a
+#define DATA_OVERRUN 0x09
+#define BAD_STATUS 0x08
+#define HOST_MSG_LOOP 0x07
+#define PDATA_REINIT 0x06
+#define IGN_WIDE_RES 0x05
+#define NO_MATCH 0x04
+#define PROTO_VIOLATION 0x03
+#define SEND_REJECT 0x02
+#define BAD_PHASE 0x01
+#define NO_SEQINT 0x00
+
+#define CLRINT 0x03
+#define CLRHWERRINT 0x80
+#define CLRBRKADRINT 0x40
+#define CLRSWTMINT 0x20
+#define CLRPCIINT 0x10
+#define CLRSCSIINT 0x08
+#define CLRSEQINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSPLTINT 0x01
+
+#define ERROR 0x04
+#define CIOPARERR 0x80
+#define CIOACCESFAIL 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define DSCTMOUT 0x02
+
+#define CLRERR 0x04
+#define CLRCIOPARERR 0x80
+#define CLRCIOACCESFAIL 0x40
+#define CLRMPARERR 0x20
+#define CLRDPARERR 0x10
+#define CLRSQPARERR 0x08
+#define CLRILLOPCODE 0x04
+#define CLRDSCTMOUT 0x02
+
+#define HCNTRL 0x05
+#define SEQ_RESET 0x80
+#define POWRDN 0x40
+#define SWINT 0x10
+#define SWTIMER_START_B 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HNSCB_QOFF 0x06
+
+#define HESCB_QOFF 0x08
+
+#define HS_MAILBOX 0x0b
+#define HOST_TQINPOS 0x80
+#define ENINT_COALESCE 0x40
+
+#define CLRSEQINTSTAT 0x0c
+#define CLRSEQ_SWTMRTO 0x10
+#define CLRSEQ_SEQINT 0x08
+#define CLRSEQ_SCSIINT 0x04
+#define CLRSEQ_PCIINT 0x02
+#define CLRSEQ_SPLTINT 0x01
+
+#define SEQINTSTAT 0x0c
+#define SEQ_SWTMRTO 0x10
+#define SEQ_SEQINT 0x08
+#define SEQ_SCSIINT 0x04
+#define SEQ_PCIINT 0x02
+#define SEQ_SPLTINT 0x01
+
+#define SWTIMER 0x0e
+
+#define SNSCB_QOFF 0x10
+
+#define SESCB_QOFF 0x12
+
+#define SDSCB_QOFF 0x14
+
+#define QOFF_CTLSTA 0x16
+#define EMPTY_SCB_AVAIL 0x80
+#define NEW_SCB_AVAIL 0x40
+#define SDSCB_ROLLOVR 0x20
+#define HS_MAILBOX_ACT 0x10
+#define SCB_QSIZE 0x0f
+#define SCB_QSIZE_16384 0x0c
+#define SCB_QSIZE_8192 0x0b
+#define SCB_QSIZE_4096 0x0a
+#define SCB_QSIZE_2048 0x09
+#define SCB_QSIZE_1024 0x08
+#define SCB_QSIZE_512 0x07
+#define SCB_QSIZE_256 0x06
+#define SCB_QSIZE_128 0x05
+#define SCB_QSIZE_64 0x04
+#define SCB_QSIZE_32 0x03
+#define SCB_QSIZE_16 0x02
+#define SCB_QSIZE_8 0x01
+#define SCB_QSIZE_4 0x00
+
+#define INTCTL 0x18
+#define SWTMINTMASK 0x80
+#define SWTMINTEN 0x40
+#define SWTIMER_START 0x20
+#define AUTOCLRCMDINT 0x10
+#define PCIINTEN 0x08
+#define SCSIINTEN 0x04
+#define SEQINTEN 0x02
+#define SPLTINTEN 0x01
+
+#define DFCNTRL 0x19
+#define SCSIENWRDIS 0x40
+#define SCSIENACK 0x20
+#define DIRECTIONACK 0x04
+#define FIFOFLUSHACK 0x02
+#define DIRECTIONEN 0x01
+
+#define DSCOMMAND0 0x19
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
+#define DISABLE_TWATE 0x02
+#define CIOPARCKEN 0x01
+
+#define DFSTATUS 0x1a
+#define PRELOAD_AVAIL 0x80
+#define PKT_PRELOAD_AVAIL 0x40
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define SG_CACHE_SHADOW 0x1b
+#define ODD_SEG 0x04
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+#define ARBCTL 0x1b
+#define RESET_HARB 0x80
+#define RETRY_SWEN 0x08
+#define USE_TIME 0x07
+
+#define SG_CACHE_PRE 0x1b
+
+#define LQIN 0x20
+
+#define TYPEPTR 0x20
+
+#define TAGPTR 0x21
+
+#define LUNPTR 0x22
+
+#define DATALENPTR 0x23
+
+#define STATLENPTR 0x24
+
+#define CMDLENPTR 0x25
+
+#define ATTRPTR 0x26
+
+#define FLAGPTR 0x27
+
+#define CMDPTR 0x28
+
+#define QNEXTPTR 0x29
+
+#define IDPTR 0x2a
+
+#define ABRTBYTEPTR 0x2b
+
+#define ABRTBITPTR 0x2c
+
+#define MAXCMDBYTES 0x2d
+
+#define MAXCMD2RCV 0x2e
+
+#define SHORTTHRESH 0x2f
+
+#define LUNLEN 0x30
+#define TLUNLEN 0xf0
+#define ILUNLEN 0x0f
+
+#define CDBLIMIT 0x31
+
+#define MAXCMD 0x32
+
+#define MAXCMDCNT 0x33
+
+#define LQRSVD01 0x34
+
+#define LQRSVD16 0x35
+
+#define LQRSVD17 0x36
+
+#define CMDRSVD0 0x37
+
+#define LQCTL0 0x38
+#define LQITARGCLT 0xc0
+#define LQIINITGCLT 0x30
+#define LQ0TARGCLT 0x0c
+#define LQ0INITGCLT 0x03
+
+#define LQCTL1 0x38
+#define PCI2PCI 0x04
+#define SINGLECMD 0x02
+#define ABORTPENDING 0x01
+
+#define SCSBIST0 0x39
+#define GSBISTERR 0x40
+#define GSBISTDONE 0x20
+#define GSBISTRUN 0x10
+#define OSBISTERR 0x04
+#define OSBISTDONE 0x02
+#define OSBISTRUN 0x01
+
+#define LQCTL2 0x39
+#define LQIRETRY 0x80
+#define LQICONTINUE 0x40
+#define LQITOIDLE 0x20
+#define LQIPAUSE 0x10
+#define LQORETRY 0x08
+#define LQOCONTINUE 0x04
+#define LQOTOIDLE 0x02
+#define LQOPAUSE 0x01
+
+#define SCSBIST1 0x3a
+#define NTBISTERR 0x04
+#define NTBISTDONE 0x02
+#define NTBISTRUN 0x01
+
+#define SCSISEQ0 0x3a
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENARBO 0x20
+#define FORCEBUSFREE 0x10
+#define SCSIRSTO 0x01
+
+#define SCSISEQ1 0x3b
+
+#define SXFRCTL0 0x3c
+#define DFON 0x80
+#define DFPEXP 0x40
+#define BIOSCANCELEN 0x10
+#define SPIOEN 0x08
+
+#define BUSINITID 0x3c
+
+#define DLCOUNT 0x3c
+
+#define SXFRCTL1 0x3d
+#define BITBUCKET 0x80
+#define ENSACHK 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define BUSTARGID 0x3e
+
+#define SXFRCTL2 0x3e
+#define AUTORSTDIS 0x10
+#define CMDDMAEN 0x08
+#define ASU 0x07
+
+#define DFFSTAT 0x3f
+#define CURRFIFO 0x03
+#define FIFO1FREE 0x20
+#define FIFO0FREE 0x10
+#define CURRFIFO_NONE 0x03
+#define CURRFIFO_1 0x01
+#define CURRFIFO_0 0x00
+
+#define SCSISIGO 0x40
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define MULTARGID 0x40
+
+#define SCSISIGI 0x41
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIPHASE 0x42
+#define STATUS_PHASE 0x20
+#define COMMAND_PHASE 0x10
+#define MSG_IN_PHASE 0x08
+#define MSG_OUT_PHASE 0x04
+#define DATA_PHASE_MASK 0x03
+#define DATA_IN_PHASE 0x02
+#define DATA_OUT_PHASE 0x01
+
+#define SCSIDAT0_IMG 0x43
+
+#define SCSIDAT 0x44
+
+#define SCSIBUS 0x46
+
+#define TARGIDIN 0x48
+#define CLKOUT 0x80
+#define TARGID 0x0f
+
+#define SELID 0x49
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define SBLKCTL 0x4a
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define ENAB40 0x08
+#define ENAB20 0x04
+#define SELWIDE 0x02
+
+#define OPTIONMODE 0x4a
+#define OPTIONMODE_DEFAULTS 0x02
+#define BIOSCANCTL 0x80
+#define AUTOACKEN 0x40
+#define BIASCANCTL 0x20
+#define BUSFREEREV 0x10
+#define ENDGFORMCHK 0x04
+#define AUTO_MSGOUT_DE 0x02
+
+#define SSTAT0 0x4b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define IOERR 0x08
+#define OVERRUN 0x04
+#define SPIORDY 0x02
+#define ARBDO 0x01
+
+#define CLRSINT0 0x4b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRIOERR 0x08
+#define CLROVERRUN 0x04
+#define CLRSPIORDY 0x02
+#define CLRARBDO 0x01
+
+#define SIMODE0 0x4b
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENOVERRUN 0x04
+#define ENSPIORDY 0x02
+#define ENARBDO 0x01
+
+#define CLRSINT1 0x4c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRSTRB2FAST 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x4c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define STRB2FAST 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x4d
+#define BUSFREETIME 0xc0
+#define NONPACKREQ 0x20
+#define EXP_ACTIVE 0x10
+#define BSYX 0x08
+#define WIDE_RES 0x04
+#define SDONE 0x02
+#define DMADONE 0x01
+#define BUSFREE_DFF1 0xc0
+#define BUSFREE_DFF0 0x80
+#define BUSFREE_LQO 0x40
+
+#define CLRSINT2 0x4d
+#define CLRNONPACKREQ 0x20
+#define CLRWIDE_RES 0x04
+#define CLRSDONE 0x02
+#define CLRDMADONE 0x01
+
+#define SIMODE2 0x4d
+#define ENWIDE_RES 0x04
+#define ENSDONE 0x02
+#define ENDMADONE 0x01
+
+#define PERRDIAG 0x4e
+#define HIZERO 0x80
+#define HIPERR 0x40
+#define PREVPHASE 0x20
+#define PARITYERR 0x10
+#define AIPERR 0x08
+#define CRCERR 0x04
+#define DGFORMERR 0x02
+#define DTERR 0x01
+
+#define LQISTATE 0x4e
+
+#define SOFFCNT 0x4f
+
+#define LQOSTATE 0x4f
+
+#define LQISTAT0 0x50
+#define LQIATNQAS 0x20
+#define LQICRCT1 0x10
+#define LQICRCT2 0x08
+#define LQIBADLQT 0x04
+#define LQIATNLQ 0x02
+#define LQIATNCMD 0x01
+
+#define CLRLQIINT0 0x50
+#define CLRLQIATNQAS 0x20
+#define CLRLQICRCT1 0x10
+#define CLRLQICRCT2 0x08
+#define CLRLQIBADLQT 0x04
+#define CLRLQIATNLQ 0x02
+#define CLRLQIATNCMD 0x01
+
+#define LQIMODE0 0x50
+#define ENLQIATNQASK 0x20
+#define ENLQICRCT1 0x10
+#define ENLQICRCT2 0x08
+#define ENLQIBADLQT 0x04
+#define ENLQIATNLQ 0x02
+#define ENLQIATNCMD 0x01
+
+#define LQIMODE1 0x51
+#define ENLQIPHASE_LQ 0x80
+#define ENLQIPHASE_NLQ 0x40
+#define ENLIQABORT 0x20
+#define ENLQICRCI_LQ 0x10
+#define ENLQICRCI_NLQ 0x08
+#define ENLQIBADLQI 0x04
+#define ENLQIOVERI_LQ 0x02
+#define ENLQIOVERI_NLQ 0x01
+
+#define LQISTAT1 0x51
+#define LQIPHASE_LQ 0x80
+#define LQIPHASE_NLQ 0x40
+#define LQIABORT 0x20
+#define LQICRCI_LQ 0x10
+#define LQICRCI_NLQ 0x08
+#define LQIBADLQI 0x04
+#define LQIOVERI_LQ 0x02
+#define LQIOVERI_NLQ 0x01
+
+#define CLRLQIINT1 0x51
+#define CLRLQIPHASE_LQ 0x80
+#define CLRLQIPHASE_NLQ 0x40
+#define CLRLIQABORT 0x20
+#define CLRLQICRCI_LQ 0x10
+#define CLRLQICRCI_NLQ 0x08
+#define CLRLQIBADLQI 0x04
+#define CLRLQIOVERI_LQ 0x02
+#define CLRLQIOVERI_NLQ 0x01
+
+#define LQISTAT2 0x52
+#define PACKETIZED 0x80
+#define LQIPHASE_OUTPKT 0x40
+#define LQIWORKONLQ 0x20
+#define LQIWAITFIFO 0x10
+#define LQISTOPPKT 0x08
+#define LQISTOPLQ 0x04
+#define LQISTOPCMD 0x02
+#define LQIGSAVAIL 0x01
+
+#define SSTAT3 0x53
+#define NTRAMPERR 0x02
+#define OSRAMPERR 0x01
+
+#define SIMODE3 0x53
+#define ENNTRAMPERR 0x02
+#define ENOSRAMPERR 0x01
+
+#define CLRSINT3 0x53
+#define CLRNTRAMPERR 0x02
+#define CLROSRAMPERR 0x01
+
+#define LQOMODE0 0x54
+#define ENLQOTARGSCBPERR 0x10
+#define ENLQOSTOPT2 0x08
+#define ENLQOATNLQ 0x04
+#define ENLQOATNPKT 0x02
+#define ENLQOTCRC 0x01
+
+#define LQOSTAT0 0x54
+#define LQOTARGSCBPERR 0x10
+#define LQOSTOPT2 0x08
+#define LQOATNLQ 0x04
+#define LQOATNPKT 0x02
+#define LQOTCRC 0x01
+
+#define CLRLQOINT0 0x54
+#define CLRLQOTARGSCBPERR 0x10
+#define CLRLQOSTOPT2 0x08
+#define CLRLQOATNLQ 0x04
+#define CLRLQOATNPKT 0x02
+#define CLRLQOTCRC 0x01
+
+#define LQOSTAT1 0x55
+#define LQOINITSCBPERR 0x10
+#define LQOSTOPI2 0x08
+#define LQOBADQAS 0x04
+#define LQOBUSFREE 0x02
+#define LQOPHACHGINPKT 0x01
+
+#define CLRLQOINT1 0x55
+#define CLRLQOINITSCBPERR 0x10
+#define CLRLQOSTOPI2 0x08
+#define CLRLQOBADQAS 0x04
+#define CLRLQOBUSFREE 0x02
+#define CLRLQOPHACHGINPKT 0x01
+
+#define LQOMODE1 0x55
+#define ENLQOINITSCBPERR 0x10
+#define ENLQOSTOPI2 0x08
+#define ENLQOBADQAS 0x04
+#define ENLQOBUSFREE 0x02
+#define ENLQOPHACHGINPKT 0x01
+
+#define LQOSTAT2 0x56
+#define LQOPKT 0xe0
+#define LQOWAITFIFO 0x10
+#define LQOPHACHGOUTPKT 0x02
+#define LQOSTOP0 0x01
+
+#define OS_SPACE_CNT 0x56
+
+#define SIMODE1 0x57
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENSTRB2FAST 0x02
+#define ENREQINIT 0x01
+
+#define GSFIFO 0x58
+
+#define DFFSXFRCTL 0x5a
+#define DFFBITBUCKET 0x08
+#define CLRSHCNT 0x04
+#define CLRCHN 0x02
+#define RSTCHN 0x01
+
+#define LQOSCSCTL 0x5a
+#define LQOH2A_VERSION 0x80
+#define LQONOCHKOVER 0x01
+
+#define NEXTSCB 0x5a
+
+#define CLRSEQINTSRC 0x5b
+#define CLRCTXTDONE 0x40
+#define CLRSAVEPTRS 0x20
+#define CLRCFG4DATA 0x10
+#define CLRCFG4ISTAT 0x08
+#define CLRCFG4TSTAT 0x04
+#define CLRCFG4ICMD 0x02
+#define CLRCFG4TCMD 0x01
+
+#define SEQINTSRC 0x5b
+#define CTXTDONE 0x40
+#define SAVEPTRS 0x20
+#define CFG4DATA 0x10
+#define CFG4ISTAT 0x08
+#define CFG4TSTAT 0x04
+#define CFG4ICMD 0x02
+#define CFG4TCMD 0x01
+
+#define CURRSCB 0x5c
+
+#define SEQIMODE 0x5c
+#define ENCTXTDONE 0x40
+#define ENSAVEPTRS 0x20
+#define ENCFG4DATA 0x10
+#define ENCFG4ISTAT 0x08
+#define ENCFG4TSTAT 0x04
+#define ENCFG4ICMD 0x02
+#define ENCFG4TCMD 0x01
+
+#define MDFFSTAT 0x5d
+#define SHCNTNEGATIVE 0x40
+#define SHCNTMINUS1 0x20
+#define LASTSDONE 0x10
+#define SHVALID 0x08
+#define DLZERO 0x04
+#define DATAINFIFO 0x02
+#define FIFOFREE 0x01
+
+#define CRCCONTROL 0x5d
+#define CRCVALCHKEN 0x40
+
+#define DFFTAG 0x5e
+
+#define LASTSCB 0x5e
+
+#define SCSITEST 0x5e
+#define CNTRTEST 0x08
+#define SEL_TXPLL_DEBUG 0x04
+
+#define IOPDNCTL 0x5f
+#define DISABLE_OE 0x80
+#define PDN_IDIST 0x04
+#define PDN_DIFFSENSE 0x01
+
+#define SHADDR 0x60
+
+#define NEGOADDR 0x60
+
+#define DGRPCRCI 0x60
+
+#define NEGPERIOD 0x61
+
+#define PACKCRCI 0x62
+
+#define NEGOFFSET 0x62
+
+#define NEGPPROPTS 0x63
+#define PPROPT_PACE 0x08
+#define PPROPT_QAS 0x04
+#define PPROPT_DT 0x02
+#define PPROPT_IUT 0x01
+
+#define NEGCONOPTS 0x64
+#define ENSNAPSHOT 0x40
+#define RTI_WRTDIS 0x20
+#define RTI_OVRDTRN 0x10
+#define ENSLOWCRC 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNO 0x02
+#define WIDEXFER 0x01
+
+#define ANNEXCOL 0x65
+
+#define SCSCHKN 0x66
+#define STSELSKIDDIS 0x40
+#define CURRFIFODEF 0x20
+#define WIDERESEN 0x10
+#define SDONEMSKDIS 0x08
+#define DFFACTCLR 0x04
+#define SHVALIDSTDIS 0x02
+#define LSTSGCLRDIS 0x01
+
+#define ANNEXDAT 0x66
+
+#define IOWNID 0x67
+
+#define PLL960CTL0 0x68
+
+#define SHCNT 0x68
+
+#define TOWNID 0x69
+
+#define PLL960CTL1 0x69
+
+#define PLL960CNT0 0x6a
+
+#define XSIG 0x6a
+
+#define SELOID 0x6b
+
+#define PLL400CTL0 0x6c
+#define PLL_VCOSEL 0x80
+#define PLL_PWDN 0x40
+#define PLL_NS 0x30
+#define PLL_ENLUD 0x08
+#define PLL_ENLPF 0x04
+#define PLL_DLPF 0x02
+#define PLL_ENFBM 0x01
+
+#define FAIRNESS 0x6c
+
+#define PLL400CTL1 0x6d
+#define PLL_CNTEN 0x80
+#define PLL_CNTCLR 0x40
+#define PLL_RST 0x01
+
+#define PLL400CNT0 0x6e
+
+#define UNFAIRNESS 0x6e
+
+#define HADDR 0x70
+
+#define PLLDELAY 0x70
+#define SPLIT_DROP_REQ 0x80
+
+#define HODMAADR 0x70
+
+#define HODMACNT 0x78
+
+#define HCNT 0x78
+
+#define HODMAEN 0x7a
+
+#define SGHADDR 0x7c
+
+#define SCBHADDR 0x7c
+
+#define SGHCNT 0x84
+
+#define SCBHCNT 0x84
+
+#define DFF_THRSH 0x88
+#define WR_DFTHRSH 0x70
+#define RD_DFTHRSH 0x07
+#define WR_DFTHRSH_MAX 0x70
+#define WR_DFTHRSH_90 0x60
+#define WR_DFTHRSH_85 0x50
+#define WR_DFTHRSH_75 0x40
+#define WR_DFTHRSH_63 0x30
+#define WR_DFTHRSH_50 0x20
+#define WR_DFTHRSH_25 0x10
+#define RD_DFTHRSH_MAX 0x07
+#define RD_DFTHRSH_90 0x06
+#define RD_DFTHRSH_85 0x05
+#define RD_DFTHRSH_75 0x04
+#define RD_DFTHRSH_63 0x03
+#define RD_DFTHRSH_50 0x02
+#define RD_DFTHRSH_25 0x01
+#define WR_DFTHRSH_MIN 0x00
+#define RD_DFTHRSH_MIN 0x00
+
+#define ROMADDR 0x8a
+
+#define ROMCNTRL 0x8d
+#define ROMOP 0xe0
+#define ROMSPD 0x18
+#define REPEAT 0x02
+#define RDY 0x01
+
+#define ROMDATA 0x8e
+
+#define CMCRXMSG0 0x90
+
+#define ROENABLE 0x90
+#define MSIROEN 0x20
+#define OVLYROEN 0x10
+#define CMCROEN 0x08
+#define SGROEN 0x04
+#define DCH1ROEN 0x02
+#define DCH0ROEN 0x01
+
+#define OVLYRXMSG0 0x90
+
+#define DCHRXMSG0 0x90
+
+#define OVLYRXMSG1 0x91
+
+#define NSENABLE 0x91
+#define MSINSEN 0x20
+#define OVLYNSEN 0x10
+#define CMCNSEN 0x08
+#define SGNSEN 0x04
+#define DCH1NSEN 0x02
+#define DCH0NSEN 0x01
+
+#define DCHRXMSG1 0x91
+
+#define CMCRXMSG1 0x91
+
+#define DCHRXMSG2 0x92
+
+#define OVLYRXMSG2 0x92
+
+#define CMCRXMSG2 0x92
+
+#define OST 0x92
+
+#define DCHRXMSG3 0x93
+
+#define CMCRXMSG3 0x93
+
+#define PCIXCTL 0x93
+#define SERRPULSE 0x80
+#define UNEXPSCIEN 0x20
+#define SPLTSMADIS 0x10
+#define SPLTSTADIS 0x08
+#define SRSPDPEEN 0x04
+#define TSCSERREN 0x02
+#define CMPABCDIS 0x01
+
+#define OVLYRXMSG3 0x93
+
+#define OVLYSEQBCNT 0x94
+
+#define CMCSEQBCNT 0x94
+
+#define DCHSEQBCNT 0x94
+
+#define CMCSPLTSTAT0 0x96
+
+#define OVLYSPLTSTAT0 0x96
+
+#define DCHSPLTSTAT0 0x96
+
+#define DCHSPLTSTAT1 0x97
+
+#define CMCSPLTSTAT1 0x97
+
+#define OVLYSPLTSTAT1 0x97
+
+#define SGRXMSG0 0x98
+#define CDNUM 0xf8
+#define CFNUM 0x07
+
+#define SLVSPLTOUTADR0 0x98
+#define LOWER_ADDR 0x7f
+
+#define SGRXMSG1 0x99
+#define CBNUM 0xff
+
+#define SLVSPLTOUTADR1 0x99
+#define REQ_DNUM 0xf8
+#define REQ_FNUM 0x07
+
+#define SGRXMSG2 0x9a
+#define MINDEX 0xff
+
+#define SLVSPLTOUTADR2 0x9a
+#define REQ_BNUM 0xff
+
+#define SGRXMSG3 0x9b
+#define MCLASS 0x0f
+
+#define SLVSPLTOUTADR3 0x9b
+#define TAG_NUM 0x1f
+#define RLXORD 0x10
+
+#define SGSEQBCNT 0x9c
+
+#define SLVSPLTOUTATTR0 0x9c
+#define LOWER_BCNT 0xff
+
+#define SLVSPLTOUTATTR1 0x9d
+#define CMPLT_DNUM 0xf8
+#define CMPLT_FNUM 0x07
+
+#define SLVSPLTOUTATTR2 0x9e
+#define CMPLT_BNUM 0xff
+
+#define SGSPLTSTAT0 0x9e
+#define STAETERM 0x80
+#define SCBCERR 0x40
+#define SCADERR 0x20
+#define SCDATBUCKET 0x10
+#define CNTNOTCMPLT 0x08
+#define RXOVRUN 0x04
+#define RXSCEMSG 0x02
+#define RXSPLTRSP 0x01
+
+#define SFUNCT 0x9f
+#define TEST_GROUP 0xf0
+#define TEST_NUM 0x0f
+
+#define SGSPLTSTAT1 0x9f
+#define RXDATABUCKET 0x01
+
+#define DF0PCISTAT 0xa0
+
+#define REG0 0xa0
+
+#define DF1PCISTAT 0xa1
+
+#define SGPCISTAT 0xa2
+
+#define REG1 0xa2
+
+#define CMCPCISTAT 0xa3
+
+#define OVLYPCISTAT 0xa4
+#define SCAAPERR 0x08
+#define RDPERR 0x04
+
+#define REG_ISR 0xa4
+
+#define SG_STATE 0xa6
+#define FETCH_INPROG 0x04
+#define LOADING_NEEDED 0x02
+#define SEGS_AVAIL 0x01
+
+#define MSIPCISTAT 0xa6
+#define RMA 0x20
+#define RTA 0x10
+#define CLRPENDMSI 0x08
+#define DPR 0x01
+
+#define TARGPCISTAT 0xa7
+#define DPE 0x80
+#define SSE 0x40
+#define STA 0x08
+#define TWATERR 0x02
+
+#define DATA_COUNT_ODD 0xa7
+
+#define SCBPTR 0xa8
+
+#define CCSCBACNT 0xab
+
+#define SCBAUTOPTR 0xab
+#define AUSCBPTR_EN 0x80
+#define SCBPTR_ADDR 0x38
+#define SCBPTR_OFF 0x07
+
+#define CCSGADDR 0xac
+
+#define CCSCBADDR 0xac
+
+#define CCSCBADR_BK 0xac
+
+#define CMC_RAMBIST 0xad
+#define SG_ELEMENT_SIZE 0x80
+#define SCBRAMBIST_FAIL 0x40
+#define SG_BIST_FAIL 0x20
+#define SG_BIST_EN 0x10
+#define CMC_BUFFER_BIST_FAIL 0x02
+#define CMC_BUFFER_BIST_EN 0x01
+
+#define CCSGCTL 0xad
+#define CCSGEN 0x0c
+#define CCSGDONE 0x80
+#define SG_CACHE_AVAIL 0x10
+#define CCSGENACK 0x08
+#define SG_FETCH_REQ 0x02
+#define CCSGRESET 0x01
+
+#define CCSCBCTL 0xad
+#define CCSCBDONE 0x80
+#define ARRDONE 0x40
+#define CCARREN 0x10
+#define CCSCBEN 0x08
+#define CCSCBDIR 0x04
+#define CCSCBRESET 0x01
+
+#define CCSGRAM 0xb0
+
+#define FLEXADR 0xb0
+
+#define CCSCBRAM 0xb0
+
+#define FLEXCNT 0xb3
+
+#define FLEXDMASTAT 0xb5
+#define FLEXDMAERR 0x02
+#define FLEXDMADONE 0x01
+
+#define FLEXDATA 0xb6
+
+#define BRDDAT 0xb8
+
+#define BRDCTL 0xb9
+#define FLXARBACK 0x80
+#define FLXARBREQ 0x40
+#define BRDADDR 0x38
+#define BRDEN 0x04
+#define BRDRW 0x02
+#define BRDSTB 0x01
+
+#define SEEADR 0xba
+
+#define SEEDAT 0xbc
+
+#define SEECTL 0xbe
+#define SEEOP_EWEN 0x40
+#define SEEOP_WALL 0x40
+#define SEEOP_EWDS 0x40
+#define SEEOPCODE 0x70
+#define SEERST 0x02
+#define SEESTART 0x01
+#define SEEOP_ERASE 0x70
+#define SEEOP_READ 0x60
+#define SEEOP_WRITE 0x50
+#define SEEOP_ERAL 0x40
+
+#define SEESTAT 0xbe
+#define INIT_DONE 0x80
+#define LDALTID_L 0x08
+#define SEEARBACK 0x04
+#define SEEBUSY 0x02
+
+#define SCBCNT 0xbf
+
+#define DFWADDR 0xc0
+
+#define DSPFLTRCTL 0xc0
+#define FLTRDISABLE 0x20
+#define EDGESENSE 0x10
+#define DSPFCNTSEL 0x0f
+
+#define DSPDATACTL 0xc1
+#define BYPASSENAB 0x80
+#define DESQDIS 0x10
+#define RCVROFFSTDIS 0x04
+#define XMITOFFSTDIS 0x02
+
+#define DFRADDR 0xc2
+
+#define DSPREQCTL 0xc2
+#define MANREQCTL 0xc0
+#define MANREQDLY 0x3f
+
+#define DSPACKCTL 0xc3
+#define MANACKCTL 0xc0
+#define MANACKDLY 0x3f
+
+#define DFDAT 0xc4
+
+#define DSPSELECT 0xc4
+#define AUTOINCEN 0x80
+#define DSPSEL 0x1f
+
+#define WRTBIASCTL 0xc5
+#define AUTOXBCDIS 0x80
+#define XMITMANVAL 0x3f
+
+#define RCVRBIOSCTL 0xc6
+#define AUTORBCDIS 0x80
+#define RCVRMANVAL 0x3f
+
+#define WRTBIASCALC 0xc7
+
+#define DFPTRS 0xc8
+
+#define RCVRBIASCALC 0xc8
+
+#define DFBKPTR 0xc9
+
+#define SKEWCALC 0xc9
+
+#define DFDBCTL 0xcb
+#define DFF_CIO_WR_RDY 0x20
+#define DFF_CIO_RD_RDY 0x10
+#define DFF_DIR_ERR 0x08
+#define DFF_RAMBIST_FAIL 0x04
+#define DFF_RAMBIST_DONE 0x02
+#define DFF_RAMBIST_EN 0x01
+
+#define DFSCNT 0xcc
+
+#define DFBCNT 0xce
+
+#define OVLYADDR 0xd4
+
+#define SEQCTL0 0xd6
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+#define SEQCTL1 0xd7
+#define OVRLAY_DATA_CHK 0x08
+#define RAMBIST_DONE 0x04
+#define RAMBIST_FAIL 0x02
+#define RAMBIST_EN 0x01
+
+#define FLAGS 0xd8
+#define ZERO 0x02
+#define CARRY 0x01
+
+#define SEQINTCTL 0xd9
+#define INTVEC1DSL 0x80
+#define INT1_CONTEXT 0x20
+#define SCS_SEQ_INT1M1 0x10
+#define SCS_SEQ_INT1M0 0x08
+#define INTMASK2 0x04
+#define INTMASK1 0x02
+#define IRET 0x01
+
+#define SEQRAM 0xda
+
+#define PRGMCNT 0xde
+
+#define ACCUM 0xe0
+
+#define SINDEX 0xe2
+
+#define DINDEX 0xe4
+
+#define BRKADDR1 0xe6
+#define BRKDIS 0x80
+
+#define BRKADDR0 0xe6
+
+#define ALLONES 0xe8
+
+#define ALLZEROS 0xea
+
+#define NONE 0xea
+
+#define SINDIR 0xec
+
+#define DINDIR 0xed
+
+#define FUNCTION1 0xf0
+
+#define STACK 0xf2
+
+#define CURADDR 0xf4
+
+#define INTVEC1_ADDR 0xf4
+
+#define INTVEC2_ADDR 0xf6
+
+#define LASTADDR 0xf6
+
+#define LONGJMP_ADDR 0xf8
+
+#define ACCUM_SAVE 0xfa
+
+#define WAITING_SCB_TAILS 0x100
+
+#define AHD_PCI_CONFIG_BASE 0x100
+
+#define SRAM_BASE 0x100
+
+#define WAITING_TID_HEAD 0x120
+
+#define WAITING_TID_TAIL 0x122
+
+#define NEXT_QUEUED_SCB_ADDR 0x124
+
+#define COMPLETE_SCB_HEAD 0x128
+
+#define COMPLETE_SCB_DMAINPROG_HEAD 0x12a
+
+#define COMPLETE_DMA_SCB_HEAD 0x12c
+
+#define QFREEZE_COUNT 0x12e
+
+#define SAVED_MODE 0x130
+
+#define MSG_OUT 0x131
+
+#define DMAPARAMS 0x132
+#define PRELOADEN 0x80
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAEN 0x10
+#define SDMAENACK 0x10
+#define HDMAENACK 0x08
+#define HDMAEN 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define SEQ_FLAGS 0x133
+#define NOT_IDENTIFIED 0x80
+#define NO_CDB_SENT 0x40
+#define TARGET_CMD_IS_TAGGED 0x40
+#define DPHASE 0x20
+#define TARG_CMD_PENDING 0x10
+#define CMDPHASE_PENDING 0x08
+#define DPHASE_PENDING 0x04
+#define SPHASE_PENDING 0x02
+#define NO_DISCONNECT 0x01
+
+#define SAVED_SCSIID 0x134
+
+#define SAVED_LUN 0x135
+
+#define LASTPHASE 0x136
+#define PHASE_MASK 0xe0
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define P_BUSFREE 0x01
+#define P_MESGIN 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define P_DATAIN_DT 0x60
+#define P_DATAIN 0x40
+#define P_DATAOUT_DT 0x20
+#define P_DATAOUT 0x00
+
+#define QOUTFIFO_ENTRY_VALID_TAG 0x137
+
+#define SHARED_DATA_ADDR 0x138
+
+#define QOUTFIFO_NEXT_ADDR 0x13c
+
+#define KERNEL_TQINPOS 0x140
+
+#define TQINPOS 0x141
+
+#define ARG_1 0x142
+#define RETURN_1 0x142
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
+#define EXIT_MSG_LOOP 0x08
+#define CONT_MSG_LOOP_WRITE 0x04
+#define CONT_MSG_LOOP_READ 0x03
+#define CONT_MSG_LOOP_TARG 0x02
+
+#define ARG_2 0x143
+#define RETURN_2 0x143
+
+#define LAST_MSG 0x144
+
+#define SCSISEQ_TEMPLATE 0x145
+#define MANUALCTL 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define MANUALP 0x0c
+#define ENAUTOATNP 0x02
+#define ALTSTIM 0x01
+
+#define INITIATOR_TAG 0x146
+
+#define SEQ_FLAGS2 0x147
+#define SELECTOUT_QFROZEN 0x04
+#define TARGET_MSG_PENDING 0x02
+
+#define ALLOCFIFO_SCBPTR 0x148
+
+#define INT_COALESCING_TIMER 0x14a
+
+#define INT_COALESCING_MAXCMDS 0x14c
+
+#define INT_COALESCING_MINCMDS 0x14d
+
+#define CMDS_PENDING 0x14e
+
+#define INT_COALESCING_CMDCOUNT 0x150
+
+#define LOCAL_HS_MAILBOX 0x151
+
+#define CMDSIZE_TABLE 0x152
+
+#define SCB_BASE 0x180
+
+#define SCB_RESIDUAL_DATACNT 0x180
+#define SCB_CDB_STORE 0x180
+#define SCB_HOST_CDB_PTR 0x180
+
+#define SCB_RESIDUAL_SGPTR 0x184
+#define SG_ADDR_MASK 0xf8
+#define SG_OVERRUN_RESID 0x02
+
+#define SCB_SCSI_STATUS 0x188
+#define SCB_HOST_CDB_LEN 0x188
+
+#define SCB_TARGET_PHASES 0x189
+
+#define SCB_TARGET_DATA_DIR 0x18a
+
+#define SCB_TARGET_ITAG 0x18b
+
+#define SCB_SENSE_BUSADDR 0x18c
+#define SCB_NEXT_COMPLETE 0x18c
+
+#define SCB_TAG 0x190
+#define SCB_FIFO_USE_COUNT 0x190
+
+#define SCB_CONTROL 0x192
+#define TARGET_SCB 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define MK_MESSAGE 0x10
+#define STATUS_RCVD 0x08
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+
+#define SCB_SCSIID 0x193
+#define TID 0xf0
+#define OID 0x0f
+
+#define SCB_LUN 0x194
+#define LID 0xff
+
+#define SCB_TASK_ATTRIBUTE 0x195
+#define SCB_XFERLEN_ODD 0x01
+
+#define SCB_CDB_LEN 0x196
+#define SCB_CDB_LEN_PTR 0x80
+
+#define SCB_TASK_MANAGEMENT 0x197
+
+#define SCB_DATAPTR 0x198
+
+#define SCB_DATACNT 0x1a0
+#define SG_LAST_SEG 0x80
+#define SG_HIGH_ADDR_BITS 0x7f
+
+#define SCB_SGPTR 0x1a4
+#define SG_STATUS_VALID 0x04
+#define SG_FULL_RESID 0x02
+#define SG_LIST_NULL 0x01
+
+#define SCB_BUSADDR 0x1a8
+
+#define SCB_NEXT 0x1ac
+#define SCB_NEXT_SCB_BUSADDR 0x1ac
+
+#define SCB_NEXT2 0x1ae
+
+#define SCB_SPARE 0x1b0
+#define SCB_PKT_LUN 0x1b0
+
+#define SCB_DISCONNECTED_LISTS 0x1b8
+
+
+#define AHD_TIMER_US_PER_TICK 0x19
+#define SCB_TRANSFER_SIZE_FULL_LUN 0x38
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define MAX_OFFSET_NON_PACED 0x7f
+#define MAX_OFFSET_PACED 0xfe
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+#define TID_SHIFT 0x04
+#define MK_MESSAGE_BIT_OFFSET 0x04
+#define WRTBIASCTL_HP_DEFAULT 0x00
+#define SEEOP_EWDS_ADDR 0x00
+#define AHD_AMPLITUDE_SHIFT 0x00
+#define AHD_AMPLITUDE_MASK 0x07
+#define AHD_ANNEXCOL_AMPLITUDE 0x06
+#define AHD_SLEWRATE_DEF_REVA 0x08
+#define AHD_SLEWRATE_SHIFT 0x03
+#define AHD_SLEWRATE_MASK 0x78
+#define AHD_PRECOMP_CUTBACK_29 0x06
+#define AHD_NUM_PER_DEV_ANNEXCOLS 0x04
+#define B_CURRFIFO_0 0x02
+#define LUNLEN_SINGLE_LEVEL_LUN 0x0f
+#define NVRAM_SCB_OFFSET 0x2c
+#define AHD_TIMER_MAX_US 0x18ffe7
+#define AHD_TIMER_MAX_TICKS 0xffff
+#define STATUS_PKT_SENSE 0xff
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define AHD_SENSE_BUFSIZE 0x100
+#define MAX_OFFSET_PACED_BUG 0x7f
+#define BUS_8_BIT 0x00
+#define STIMESEL_BUG_ADJ 0x08
+#define STIMESEL_MIN 0x18
+#define STIMESEL_SHIFT 0x03
+#define CCSGRAM_MAXSEGS 0x10
+#define INVALID_ADDR 0x80
+#define TARGET_CMD_CMPLT 0xfe
+#define SEEOP_WRAL_ADDR 0x40
+#define SEEOP_ERAL_ADDR 0x80
+#define AHD_AMPLITUDE_DEF 0x07
+#define AHD_SLEWRATE_DEF_REVB 0x08
+#define AHD_PRECOMP_CUTBACK_37 0x07
+#define AHD_PRECOMP_CUTBACK_17 0x04
+#define AHD_PRECOMP_SHIFT 0x00
+#define AHD_PRECOMP_MASK 0x07
+#define AHD_ANNEXCOL_PRECOMP_SLEW 0x04
+#define SRC_MODE_SHIFT 0x00
+#define PKT_OVERRUN_BUFSIZE 0x200
+#define SCB_TRANSFER_SIZE_1BYTE_LUN 0x30
+#define TARGET_DATA_IN 0x01
+#define HOST_MSG 0xff
+#define MAX_OFFSET 0xfe
+#define BUS_16_BIT 0x01
+#define CCSCBADDR_MAX 0x80
+#define NUMDSPS 0x14
+#define SEEOP_EWEN_ADDR 0xc0
+#define AHD_ANNEXCOL_PER_DEV0 0x04
+#define DST_MODE_SHIFT 0x04
+
+
+/* Downloaded Constant Definitions */
+#define SCB_TRANSFER_SIZE 0x06
+#define PKT_OVERRUN_BUFOFFSET 0x05
+#define SG_SIZEOF 0x04
+#define SG_PREFETCH_ADDR_MASK 0x03
+#define SG_PREFETCH_ALIGN_MASK 0x02
+#define SG_PREFETCH_CNT_LIMIT 0x01
+#define SG_PREFETCH_CNT 0x00
+#define DOWNLOAD_CONST_COUNT 0x07
+
+
+/* Exported Labels */
+#define LABEL_seq_isr 0x269
+#define LABEL_timer_isr 0x265
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
new file mode 100644
index 000000000000..3098a757e3d7
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
@@ -0,0 +1,3635 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#94 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $
+ */
+
+#include "aic79xx_osm.h"
+
+static ahd_reg_parse_entry_t MODE_PTR_parse_table[] = {
+ { "SRC_MODE", 0x07, 0x07 },
+ { "DST_MODE", 0x70, 0x70 }
+};
+
+int
+ahd_mode_ptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(MODE_PTR_parse_table, 2, "MODE_PTR",
+ 0x00, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t INTSTAT_parse_table[] = {
+ { "SPLTINT", 0x01, 0x01 },
+ { "CMDCMPLT", 0x02, 0x02 },
+ { "SEQINT", 0x04, 0x04 },
+ { "SCSIINT", 0x08, 0x08 },
+ { "PCIINT", 0x10, 0x10 },
+ { "SWTMINT", 0x20, 0x20 },
+ { "BRKADRINT", 0x40, 0x40 },
+ { "HWERRINT", 0x80, 0x80 },
+ { "INT_PEND", 0xff, 0xff }
+};
+
+int
+ahd_intstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(INTSTAT_parse_table, 9, "INTSTAT",
+ 0x01, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTCODE_parse_table[] = {
+ { "NO_SEQINT", 0x00, 0xff },
+ { "BAD_PHASE", 0x01, 0xff },
+ { "SEND_REJECT", 0x02, 0xff },
+ { "PROTO_VIOLATION", 0x03, 0xff },
+ { "NO_MATCH", 0x04, 0xff },
+ { "IGN_WIDE_RES", 0x05, 0xff },
+ { "PDATA_REINIT", 0x06, 0xff },
+ { "HOST_MSG_LOOP", 0x07, 0xff },
+ { "BAD_STATUS", 0x08, 0xff },
+ { "DATA_OVERRUN", 0x09, 0xff },
+ { "MKMSG_FAILED", 0x0a, 0xff },
+ { "MISSED_BUSFREE", 0x0b, 0xff },
+ { "DUMP_CARD_STATE", 0x0c, 0xff },
+ { "ILLEGAL_PHASE", 0x0d, 0xff },
+ { "INVALID_SEQINT", 0x0e, 0xff },
+ { "CFG4ISTAT_INTR", 0x0f, 0xff },
+ { "STATUS_OVERRUN", 0x10, 0xff },
+ { "CFG4OVERRUN", 0x11, 0xff },
+ { "ENTERING_NONPACK", 0x12, 0xff },
+ { "TASKMGMT_FUNC_COMPLETE",0x13, 0xff },
+ { "TASKMGMT_CMD_CMPLT_OKAY",0x14, 0xff },
+ { "TRACEPOINT0", 0x15, 0xff },
+ { "TRACEPOINT1", 0x16, 0xff },
+ { "TRACEPOINT2", 0x17, 0xff },
+ { "TRACEPOINT3", 0x18, 0xff },
+ { "SAW_HWERR", 0x19, 0xff },
+ { "BAD_SCB_STATUS", 0x1a, 0xff }
+};
+
+int
+ahd_seqintcode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTCODE_parse_table, 27, "SEQINTCODE",
+ 0x02, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRINT_parse_table[] = {
+ { "CLRSPLTINT", 0x01, 0x01 },
+ { "CLRCMDINT", 0x02, 0x02 },
+ { "CLRSEQINT", 0x04, 0x04 },
+ { "CLRSCSIINT", 0x08, 0x08 },
+ { "CLRPCIINT", 0x10, 0x10 },
+ { "CLRSWTMINT", 0x20, 0x20 },
+ { "CLRBRKADRINT", 0x40, 0x40 },
+ { "CLRHWERRINT", 0x80, 0x80 }
+};
+
+int
+ahd_clrint_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRINT_parse_table, 8, "CLRINT",
+ 0x03, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ERROR_parse_table[] = {
+ { "DSCTMOUT", 0x02, 0x02 },
+ { "ILLOPCODE", 0x04, 0x04 },
+ { "SQPARERR", 0x08, 0x08 },
+ { "DPARERR", 0x10, 0x10 },
+ { "MPARERR", 0x20, 0x20 },
+ { "CIOACCESFAIL", 0x40, 0x40 },
+ { "CIOPARERR", 0x80, 0x80 }
+};
+
+int
+ahd_error_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ERROR_parse_table, 7, "ERROR",
+ 0x04, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRERR_parse_table[] = {
+ { "CLRDSCTMOUT", 0x02, 0x02 },
+ { "CLRILLOPCODE", 0x04, 0x04 },
+ { "CLRSQPARERR", 0x08, 0x08 },
+ { "CLRDPARERR", 0x10, 0x10 },
+ { "CLRMPARERR", 0x20, 0x20 },
+ { "CLRCIOACCESFAIL", 0x40, 0x40 },
+ { "CLRCIOPARERR", 0x80, 0x80 }
+};
+
+int
+ahd_clrerr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRERR_parse_table, 7, "CLRERR",
+ 0x04, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t HCNTRL_parse_table[] = {
+ { "CHIPRST", 0x01, 0x01 },
+ { "CHIPRSTACK", 0x01, 0x01 },
+ { "INTEN", 0x02, 0x02 },
+ { "PAUSE", 0x04, 0x04 },
+ { "SWTIMER_START_B", 0x08, 0x08 },
+ { "SWINT", 0x10, 0x10 },
+ { "POWRDN", 0x40, 0x40 },
+ { "SEQ_RESET", 0x80, 0x80 }
+};
+
+int
+ahd_hcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(HCNTRL_parse_table, 8, "HCNTRL",
+ 0x05, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hnscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HNSCB_QOFF",
+ 0x06, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hescb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HESCB_QOFF",
+ 0x08, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t HS_MAILBOX_parse_table[] = {
+ { "ENINT_COALESCE", 0x40, 0x40 },
+ { "HOST_TQINPOS", 0x80, 0x80 }
+};
+
+int
+ahd_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(HS_MAILBOX_parse_table, 2, "HS_MAILBOX",
+ 0x0b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSEQINTSTAT_parse_table[] = {
+ { "CLRSEQ_SPLTINT", 0x01, 0x01 },
+ { "CLRSEQ_PCIINT", 0x02, 0x02 },
+ { "CLRSEQ_SCSIINT", 0x04, 0x04 },
+ { "CLRSEQ_SEQINT", 0x08, 0x08 },
+ { "CLRSEQ_SWTMRTO", 0x10, 0x10 }
+};
+
+int
+ahd_clrseqintstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSEQINTSTAT_parse_table, 5, "CLRSEQINTSTAT",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTSTAT_parse_table[] = {
+ { "SEQ_SPLTINT", 0x01, 0x01 },
+ { "SEQ_PCIINT", 0x02, 0x02 },
+ { "SEQ_SCSIINT", 0x04, 0x04 },
+ { "SEQ_SEQINT", 0x08, 0x08 },
+ { "SEQ_SWTMRTO", 0x10, 0x10 }
+};
+
+int
+ahd_seqintstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTSTAT_parse_table, 5, "SEQINTSTAT",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_swtimer_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SWTIMER",
+ 0x0e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_snscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SNSCB_QOFF",
+ 0x10, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sescb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SESCB_QOFF",
+ 0x12, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sdscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SDSCB_QOFF",
+ 0x14, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t QOFF_CTLSTA_parse_table[] = {
+ { "SCB_QSIZE_4", 0x00, 0x0f },
+ { "SCB_QSIZE_8", 0x01, 0x0f },
+ { "SCB_QSIZE_16", 0x02, 0x0f },
+ { "SCB_QSIZE_32", 0x03, 0x0f },
+ { "SCB_QSIZE_64", 0x04, 0x0f },
+ { "SCB_QSIZE_128", 0x05, 0x0f },
+ { "SCB_QSIZE_256", 0x06, 0x0f },
+ { "SCB_QSIZE_512", 0x07, 0x0f },
+ { "SCB_QSIZE_1024", 0x08, 0x0f },
+ { "SCB_QSIZE_2048", 0x09, 0x0f },
+ { "SCB_QSIZE_4096", 0x0a, 0x0f },
+ { "SCB_QSIZE_8192", 0x0b, 0x0f },
+ { "SCB_QSIZE_16384", 0x0c, 0x0f },
+ { "SCB_QSIZE", 0x0f, 0x0f },
+ { "HS_MAILBOX_ACT", 0x10, 0x10 },
+ { "SDSCB_ROLLOVR", 0x20, 0x20 },
+ { "NEW_SCB_AVAIL", 0x40, 0x40 },
+ { "EMPTY_SCB_AVAIL", 0x80, 0x80 }
+};
+
+int
+ahd_qoff_ctlsta_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(QOFF_CTLSTA_parse_table, 18, "QOFF_CTLSTA",
+ 0x16, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t INTCTL_parse_table[] = {
+ { "SPLTINTEN", 0x01, 0x01 },
+ { "SEQINTEN", 0x02, 0x02 },
+ { "SCSIINTEN", 0x04, 0x04 },
+ { "PCIINTEN", 0x08, 0x08 },
+ { "AUTOCLRCMDINT", 0x10, 0x10 },
+ { "SWTIMER_START", 0x20, 0x20 },
+ { "SWTMINTEN", 0x40, 0x40 },
+ { "SWTMINTMASK", 0x80, 0x80 }
+};
+
+int
+ahd_intctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(INTCTL_parse_table, 8, "INTCTL",
+ 0x18, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFCNTRL_parse_table[] = {
+ { "DIRECTIONEN", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "FIFOFLUSHACK", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "DIRECTIONACK", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "SCSIENACK", 0x20, 0x20 },
+ { "SCSIENWRDIS", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahd_dfcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFCNTRL_parse_table, 11, "DFCNTRL",
+ 0x19, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSCOMMAND0_parse_table[] = {
+ { "CIOPARCKEN", 0x01, 0x01 },
+ { "DISABLE_TWATE", 0x02, 0x02 },
+ { "EXTREQLCK", 0x10, 0x10 },
+ { "MPARCKEN", 0x20, 0x20 },
+ { "DPARCKEN", 0x40, 0x40 },
+ { "CACHETHEN", 0x80, 0x80 }
+};
+
+int
+ahd_dscommand0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSCOMMAND0_parse_table, 6, "DSCOMMAND0",
+ 0x19, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFSTATUS_parse_table[] = {
+ { "FIFOEMP", 0x01, 0x01 },
+ { "FIFOFULL", 0x02, 0x02 },
+ { "DFTHRESH", 0x04, 0x04 },
+ { "HDONE", 0x08, 0x08 },
+ { "MREQPEND", 0x10, 0x10 },
+ { "PKT_PRELOAD_AVAIL", 0x40, 0x40 },
+ { "PRELOAD_AVAIL", 0x80, 0x80 }
+};
+
+int
+ahd_dfstatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFSTATUS_parse_table, 7, "DFSTATUS",
+ 0x1a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SG_CACHE_SHADOW_parse_table[] = {
+ { "LAST_SEG_DONE", 0x01, 0x01 },
+ { "LAST_SEG", 0x02, 0x02 },
+ { "ODD_SEG", 0x04, 0x04 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahd_sg_cache_shadow_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SG_CACHE_SHADOW_parse_table, 4, "SG_CACHE_SHADOW",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ARBCTL_parse_table[] = {
+ { "USE_TIME", 0x07, 0x07 },
+ { "RETRY_SWEN", 0x08, 0x08 },
+ { "RESET_HARB", 0x80, 0x80 }
+};
+
+int
+ahd_arbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ARBCTL_parse_table, 3, "ARBCTL",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SG_CACHE_PRE_parse_table[] = {
+ { "LAST_SEG", 0x02, 0x02 },
+ { "ODD_SEG", 0x04, 0x04 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahd_sg_cache_pre_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SG_CACHE_PRE_parse_table, 3, "SG_CACHE_PRE",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqin_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQIN",
+ 0x20, regvalue, cur_col, wrap));
+}
+
+int
+ahd_typeptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TYPEPTR",
+ 0x20, regvalue, cur_col, wrap));
+}
+
+int
+ahd_tagptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TAGPTR",
+ 0x21, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lunptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LUNPTR",
+ 0x22, regvalue, cur_col, wrap));
+}
+
+int
+ahd_datalenptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DATALENPTR",
+ 0x23, regvalue, cur_col, wrap));
+}
+
+int
+ahd_statlenptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "STATLENPTR",
+ 0x24, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdlenptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDLENPTR",
+ 0x25, regvalue, cur_col, wrap));
+}
+
+int
+ahd_attrptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ATTRPTR",
+ 0x26, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flagptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLAGPTR",
+ 0x27, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDPTR",
+ 0x28, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qnextptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QNEXTPTR",
+ 0x29, regvalue, cur_col, wrap));
+}
+
+int
+ahd_idptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "IDPTR",
+ 0x2a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_abrtbyteptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ABRTBYTEPTR",
+ 0x2b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_abrtbitptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ABRTBITPTR",
+ 0x2c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmdbytes_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMDBYTES",
+ 0x2d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmd2rcv_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMD2RCV",
+ 0x2e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shortthresh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHORTTHRESH",
+ 0x2f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LUNLEN_parse_table[] = {
+ { "ILUNLEN", 0x0f, 0x0f },
+ { "TLUNLEN", 0xf0, 0xf0 }
+};
+
+int
+ahd_lunlen_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LUNLEN_parse_table, 2, "LUNLEN",
+ 0x30, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cdblimit_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CDBLIMIT",
+ 0x31, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMD",
+ 0x32, regvalue, cur_col, wrap));
+}
+
+int
+ahd_maxcmdcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MAXCMDCNT",
+ 0x33, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqrsvd01_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQRSVD01",
+ 0x34, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqrsvd16_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQRSVD16",
+ 0x35, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqrsvd17_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQRSVD17",
+ 0x36, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdrsvd0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDRSVD0",
+ 0x37, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQCTL0_parse_table[] = {
+ { "LQ0INITGCLT", 0x03, 0x03 },
+ { "LQ0TARGCLT", 0x0c, 0x0c },
+ { "LQIINITGCLT", 0x30, 0x30 },
+ { "LQITARGCLT", 0xc0, 0xc0 }
+};
+
+int
+ahd_lqctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQCTL0_parse_table, 4, "LQCTL0",
+ 0x38, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQCTL1_parse_table[] = {
+ { "ABORTPENDING", 0x01, 0x01 },
+ { "SINGLECMD", 0x02, 0x02 },
+ { "PCI2PCI", 0x04, 0x04 }
+};
+
+int
+ahd_lqctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQCTL1_parse_table, 3, "LQCTL1",
+ 0x38, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSBIST0_parse_table[] = {
+ { "OSBISTRUN", 0x01, 0x01 },
+ { "OSBISTDONE", 0x02, 0x02 },
+ { "OSBISTERR", 0x04, 0x04 },
+ { "GSBISTRUN", 0x10, 0x10 },
+ { "GSBISTDONE", 0x20, 0x20 },
+ { "GSBISTERR", 0x40, 0x40 }
+};
+
+int
+ahd_scsbist0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSBIST0_parse_table, 6, "SCSBIST0",
+ 0x39, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQCTL2_parse_table[] = {
+ { "LQOPAUSE", 0x01, 0x01 },
+ { "LQOTOIDLE", 0x02, 0x02 },
+ { "LQOCONTINUE", 0x04, 0x04 },
+ { "LQORETRY", 0x08, 0x08 },
+ { "LQIPAUSE", 0x10, 0x10 },
+ { "LQITOIDLE", 0x20, 0x20 },
+ { "LQICONTINUE", 0x40, 0x40 },
+ { "LQIRETRY", 0x80, 0x80 }
+};
+
+int
+ahd_lqctl2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQCTL2_parse_table, 8, "LQCTL2",
+ 0x39, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSBIST1_parse_table[] = {
+ { "NTBISTRUN", 0x01, 0x01 },
+ { "NTBISTDONE", 0x02, 0x02 },
+ { "NTBISTERR", 0x04, 0x04 }
+};
+
+int
+ahd_scsbist1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSBIST1_parse_table, 3, "SCSBIST1",
+ 0x3a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISEQ0_parse_table[] = {
+ { "SCSIRSTO", 0x01, 0x01 },
+ { "FORCEBUSFREE", 0x10, 0x10 },
+ { "ENARBO", 0x20, 0x20 },
+ { "ENSELO", 0x40, 0x40 },
+ { "TEMODEO", 0x80, 0x80 }
+};
+
+int
+ahd_scsiseq0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISEQ0_parse_table, 5, "SCSISEQ0",
+ 0x3a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISEQ1_parse_table[] = {
+ { "ALTSTIM", 0x01, 0x01 },
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "MANUALP", 0x0c, 0x0c },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "MANUALCTL", 0x40, 0x40 }
+};
+
+int
+ahd_scsiseq1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISEQ1_parse_table, 6, "SCSISEQ1",
+ 0x3b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SXFRCTL0_parse_table[] = {
+ { "SPIOEN", 0x08, 0x08 },
+ { "BIOSCANCELEN", 0x10, 0x10 },
+ { "DFPEXP", 0x40, 0x40 },
+ { "DFON", 0x80, 0x80 }
+};
+
+int
+ahd_sxfrctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SXFRCTL0_parse_table, 4, "SXFRCTL0",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_businitid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BUSINITID",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dlcount_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DLCOUNT",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SXFRCTL1_parse_table[] = {
+ { "STPWEN", 0x01, 0x01 },
+ { "ACTNEGEN", 0x02, 0x02 },
+ { "ENSTIMER", 0x04, 0x04 },
+ { "STIMESEL", 0x18, 0x18 },
+ { "ENSPCHK", 0x20, 0x20 },
+ { "ENSACHK", 0x40, 0x40 },
+ { "BITBUCKET", 0x80, 0x80 }
+};
+
+int
+ahd_sxfrctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SXFRCTL1_parse_table, 7, "SXFRCTL1",
+ 0x3d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_bustargid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BUSTARGID",
+ 0x3e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SXFRCTL2_parse_table[] = {
+ { "ASU", 0x07, 0x07 },
+ { "CMDDMAEN", 0x08, 0x08 },
+ { "AUTORSTDIS", 0x10, 0x10 }
+};
+
+int
+ahd_sxfrctl2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SXFRCTL2_parse_table, 3, "SXFRCTL2",
+ 0x3e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFFSTAT_parse_table[] = {
+ { "CURRFIFO_0", 0x00, 0x03 },
+ { "CURRFIFO_1", 0x01, 0x03 },
+ { "CURRFIFO_NONE", 0x03, 0x03 },
+ { "FIFO0FREE", 0x10, 0x10 },
+ { "FIFO1FREE", 0x20, 0x20 },
+ { "CURRFIFO", 0x03, 0x03 }
+};
+
+int
+ahd_dffstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFFSTAT_parse_table, 6, "DFFSTAT",
+ 0x3f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISIGO_parse_table[] = {
+ { "P_DATAOUT", 0x00, 0xe0 },
+ { "P_DATAOUT_DT", 0x20, 0xe0 },
+ { "P_DATAIN", 0x40, 0xe0 },
+ { "P_DATAIN_DT", 0x60, 0xe0 },
+ { "P_COMMAND", 0x80, 0xe0 },
+ { "P_MESGOUT", 0xa0, 0xe0 },
+ { "P_STATUS", 0xc0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 },
+ { "ACKO", 0x01, 0x01 },
+ { "REQO", 0x02, 0x02 },
+ { "BSYO", 0x04, 0x04 },
+ { "SELO", 0x08, 0x08 },
+ { "ATNO", 0x10, 0x10 },
+ { "MSGO", 0x20, 0x20 },
+ { "IOO", 0x40, 0x40 },
+ { "CDO", 0x80, 0x80 },
+ { "PHASE_MASK", 0xe0, 0xe0 }
+};
+
+int
+ahd_scsisigo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISIGO_parse_table, 17, "SCSISIGO",
+ 0x40, regvalue, cur_col, wrap));
+}
+
+int
+ahd_multargid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MULTARGID",
+ 0x40, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISIGI_parse_table[] = {
+ { "P_DATAOUT", 0x00, 0xe0 },
+ { "P_DATAOUT_DT", 0x20, 0xe0 },
+ { "P_DATAIN", 0x40, 0xe0 },
+ { "P_DATAIN_DT", 0x60, 0xe0 },
+ { "P_COMMAND", 0x80, 0xe0 },
+ { "P_MESGOUT", 0xa0, 0xe0 },
+ { "P_STATUS", 0xc0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 },
+ { "ACKI", 0x01, 0x01 },
+ { "REQI", 0x02, 0x02 },
+ { "BSYI", 0x04, 0x04 },
+ { "SELI", 0x08, 0x08 },
+ { "ATNI", 0x10, 0x10 },
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "PHASE_MASK", 0xe0, 0xe0 }
+};
+
+int
+ahd_scsisigi_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISIGI_parse_table, 17, "SCSISIGI",
+ 0x41, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSIPHASE_parse_table[] = {
+ { "DATA_OUT_PHASE", 0x01, 0x03 },
+ { "DATA_IN_PHASE", 0x02, 0x03 },
+ { "DATA_PHASE_MASK", 0x03, 0x03 },
+ { "MSG_OUT_PHASE", 0x04, 0x04 },
+ { "MSG_IN_PHASE", 0x08, 0x08 },
+ { "COMMAND_PHASE", 0x10, 0x10 },
+ { "STATUS_PHASE", 0x20, 0x20 }
+};
+
+int
+ahd_scsiphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSIPHASE_parse_table, 7, "SCSIPHASE",
+ 0x42, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsidat0_img_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCSIDAT0_IMG",
+ 0x43, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsidat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCSIDAT",
+ 0x44, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsibus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCSIBUS",
+ 0x46, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t TARGIDIN_parse_table[] = {
+ { "TARGID", 0x0f, 0x0f },
+ { "CLKOUT", 0x80, 0x80 }
+};
+
+int
+ahd_targidin_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(TARGIDIN_parse_table, 2, "TARGIDIN",
+ 0x48, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SELID_parse_table[] = {
+ { "ONEBIT", 0x08, 0x08 },
+ { "SELID_MASK", 0xf0, 0xf0 }
+};
+
+int
+ahd_selid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SELID_parse_table, 2, "SELID",
+ 0x49, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SBLKCTL_parse_table[] = {
+ { "SELWIDE", 0x02, 0x02 },
+ { "ENAB20", 0x04, 0x04 },
+ { "ENAB40", 0x08, 0x08 },
+ { "DIAGLEDON", 0x40, 0x40 },
+ { "DIAGLEDEN", 0x80, 0x80 }
+};
+
+int
+ahd_sblkctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SBLKCTL_parse_table, 5, "SBLKCTL",
+ 0x4a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OPTIONMODE_parse_table[] = {
+ { "AUTO_MSGOUT_DE", 0x02, 0x02 },
+ { "ENDGFORMCHK", 0x04, 0x04 },
+ { "BUSFREEREV", 0x10, 0x10 },
+ { "BIASCANCTL", 0x20, 0x20 },
+ { "AUTOACKEN", 0x40, 0x40 },
+ { "BIOSCANCTL", 0x80, 0x80 },
+ { "OPTIONMODE_DEFAULTS",0x02, 0x02 }
+};
+
+int
+ahd_optionmode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OPTIONMODE_parse_table, 7, "OPTIONMODE",
+ 0x4a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT0_parse_table[] = {
+ { "ARBDO", 0x01, 0x01 },
+ { "SPIORDY", 0x02, 0x02 },
+ { "OVERRUN", 0x04, 0x04 },
+ { "IOERR", 0x08, 0x08 },
+ { "SELINGO", 0x10, 0x10 },
+ { "SELDI", 0x20, 0x20 },
+ { "SELDO", 0x40, 0x40 },
+ { "TARGET", 0x80, 0x80 }
+};
+
+int
+ahd_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT0_parse_table, 8, "SSTAT0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT0_parse_table[] = {
+ { "CLRARBDO", 0x01, 0x01 },
+ { "CLRSPIORDY", 0x02, 0x02 },
+ { "CLROVERRUN", 0x04, 0x04 },
+ { "CLRIOERR", 0x08, 0x08 },
+ { "CLRSELINGO", 0x10, 0x10 },
+ { "CLRSELDI", 0x20, 0x20 },
+ { "CLRSELDO", 0x40, 0x40 }
+};
+
+int
+ahd_clrsint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT0_parse_table, 7, "CLRSINT0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE0_parse_table[] = {
+ { "ENARBDO", 0x01, 0x01 },
+ { "ENSPIORDY", 0x02, 0x02 },
+ { "ENOVERRUN", 0x04, 0x04 },
+ { "ENIOERR", 0x08, 0x08 },
+ { "ENSELINGO", 0x10, 0x10 },
+ { "ENSELDI", 0x20, 0x20 },
+ { "ENSELDO", 0x40, 0x40 }
+};
+
+int
+ahd_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE0_parse_table, 7, "SIMODE0",
+ 0x4b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT1_parse_table[] = {
+ { "CLRREQINIT", 0x01, 0x01 },
+ { "CLRSTRB2FAST", 0x02, 0x02 },
+ { "CLRSCSIPERR", 0x04, 0x04 },
+ { "CLRBUSFREE", 0x08, 0x08 },
+ { "CLRSCSIRSTI", 0x20, 0x20 },
+ { "CLRATNO", 0x40, 0x40 },
+ { "CLRSELTIMEO", 0x80, 0x80 }
+};
+
+int
+ahd_clrsint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT1_parse_table, 7, "CLRSINT1",
+ 0x4c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT1_parse_table[] = {
+ { "REQINIT", 0x01, 0x01 },
+ { "STRB2FAST", 0x02, 0x02 },
+ { "SCSIPERR", 0x04, 0x04 },
+ { "BUSFREE", 0x08, 0x08 },
+ { "PHASEMIS", 0x10, 0x10 },
+ { "SCSIRSTI", 0x20, 0x20 },
+ { "ATNTARG", 0x40, 0x40 },
+ { "SELTO", 0x80, 0x80 }
+};
+
+int
+ahd_sstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT1_parse_table, 8, "SSTAT1",
+ 0x4c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT2_parse_table[] = {
+ { "BUSFREE_LQO", 0x40, 0xc0 },
+ { "BUSFREE_DFF0", 0x80, 0xc0 },
+ { "BUSFREE_DFF1", 0xc0, 0xc0 },
+ { "DMADONE", 0x01, 0x01 },
+ { "SDONE", 0x02, 0x02 },
+ { "WIDE_RES", 0x04, 0x04 },
+ { "BSYX", 0x08, 0x08 },
+ { "EXP_ACTIVE", 0x10, 0x10 },
+ { "NONPACKREQ", 0x20, 0x20 },
+ { "BUSFREETIME", 0xc0, 0xc0 }
+};
+
+int
+ahd_sstat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT2_parse_table, 10, "SSTAT2",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT2_parse_table[] = {
+ { "CLRDMADONE", 0x01, 0x01 },
+ { "CLRSDONE", 0x02, 0x02 },
+ { "CLRWIDE_RES", 0x04, 0x04 },
+ { "CLRNONPACKREQ", 0x20, 0x20 }
+};
+
+int
+ahd_clrsint2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT2_parse_table, 4, "CLRSINT2",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE2_parse_table[] = {
+ { "ENDMADONE", 0x01, 0x01 },
+ { "ENSDONE", 0x02, 0x02 },
+ { "ENWIDE_RES", 0x04, 0x04 }
+};
+
+int
+ahd_simode2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE2_parse_table, 3, "SIMODE2",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PERRDIAG_parse_table[] = {
+ { "DTERR", 0x01, 0x01 },
+ { "DGFORMERR", 0x02, 0x02 },
+ { "CRCERR", 0x04, 0x04 },
+ { "AIPERR", 0x08, 0x08 },
+ { "PARITYERR", 0x10, 0x10 },
+ { "PREVPHASE", 0x20, 0x20 },
+ { "HIPERR", 0x40, 0x40 },
+ { "HIZERO", 0x80, 0x80 }
+};
+
+int
+ahd_perrdiag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PERRDIAG_parse_table, 8, "PERRDIAG",
+ 0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqistate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQISTATE",
+ 0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_soffcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SOFFCNT",
+ 0x4f, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lqostate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LQOSTATE",
+ 0x4f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQISTAT0_parse_table[] = {
+ { "LQIATNCMD", 0x01, 0x01 },
+ { "LQIATNLQ", 0x02, 0x02 },
+ { "LQIBADLQT", 0x04, 0x04 },
+ { "LQICRCT2", 0x08, 0x08 },
+ { "LQICRCT1", 0x10, 0x10 },
+ { "LQIATNQAS", 0x20, 0x20 }
+};
+
+int
+ahd_lqistat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQISTAT0_parse_table, 6, "LQISTAT0",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQIINT0_parse_table[] = {
+ { "CLRLQIATNCMD", 0x01, 0x01 },
+ { "CLRLQIATNLQ", 0x02, 0x02 },
+ { "CLRLQIBADLQT", 0x04, 0x04 },
+ { "CLRLQICRCT2", 0x08, 0x08 },
+ { "CLRLQICRCT1", 0x10, 0x10 },
+ { "CLRLQIATNQAS", 0x20, 0x20 }
+};
+
+int
+ahd_clrlqiint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQIINT0_parse_table, 6, "CLRLQIINT0",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQIMODE0_parse_table[] = {
+ { "ENLQIATNCMD", 0x01, 0x01 },
+ { "ENLQIATNLQ", 0x02, 0x02 },
+ { "ENLQIBADLQT", 0x04, 0x04 },
+ { "ENLQICRCT2", 0x08, 0x08 },
+ { "ENLQICRCT1", 0x10, 0x10 },
+ { "ENLQIATNQASK", 0x20, 0x20 }
+};
+
+int
+ahd_lqimode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQIMODE0_parse_table, 6, "LQIMODE0",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQIMODE1_parse_table[] = {
+ { "ENLQIOVERI_NLQ", 0x01, 0x01 },
+ { "ENLQIOVERI_LQ", 0x02, 0x02 },
+ { "ENLQIBADLQI", 0x04, 0x04 },
+ { "ENLQICRCI_NLQ", 0x08, 0x08 },
+ { "ENLQICRCI_LQ", 0x10, 0x10 },
+ { "ENLIQABORT", 0x20, 0x20 },
+ { "ENLQIPHASE_NLQ", 0x40, 0x40 },
+ { "ENLQIPHASE_LQ", 0x80, 0x80 }
+};
+
+int
+ahd_lqimode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQIMODE1_parse_table, 8, "LQIMODE1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQISTAT1_parse_table[] = {
+ { "LQIOVERI_NLQ", 0x01, 0x01 },
+ { "LQIOVERI_LQ", 0x02, 0x02 },
+ { "LQIBADLQI", 0x04, 0x04 },
+ { "LQICRCI_NLQ", 0x08, 0x08 },
+ { "LQICRCI_LQ", 0x10, 0x10 },
+ { "LQIABORT", 0x20, 0x20 },
+ { "LQIPHASE_NLQ", 0x40, 0x40 },
+ { "LQIPHASE_LQ", 0x80, 0x80 }
+};
+
+int
+ahd_lqistat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQISTAT1_parse_table, 8, "LQISTAT1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQIINT1_parse_table[] = {
+ { "CLRLQIOVERI_NLQ", 0x01, 0x01 },
+ { "CLRLQIOVERI_LQ", 0x02, 0x02 },
+ { "CLRLQIBADLQI", 0x04, 0x04 },
+ { "CLRLQICRCI_NLQ", 0x08, 0x08 },
+ { "CLRLQICRCI_LQ", 0x10, 0x10 },
+ { "CLRLIQABORT", 0x20, 0x20 },
+ { "CLRLQIPHASE_NLQ", 0x40, 0x40 },
+ { "CLRLQIPHASE_LQ", 0x80, 0x80 }
+};
+
+int
+ahd_clrlqiint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQIINT1_parse_table, 8, "CLRLQIINT1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQISTAT2_parse_table[] = {
+ { "LQIGSAVAIL", 0x01, 0x01 },
+ { "LQISTOPCMD", 0x02, 0x02 },
+ { "LQISTOPLQ", 0x04, 0x04 },
+ { "LQISTOPPKT", 0x08, 0x08 },
+ { "LQIWAITFIFO", 0x10, 0x10 },
+ { "LQIWORKONLQ", 0x20, 0x20 },
+ { "LQIPHASE_OUTPKT", 0x40, 0x40 },
+ { "PACKETIZED", 0x80, 0x80 }
+};
+
+int
+ahd_lqistat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQISTAT2_parse_table, 8, "LQISTAT2",
+ 0x52, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SSTAT3_parse_table[] = {
+ { "OSRAMPERR", 0x01, 0x01 },
+ { "NTRAMPERR", 0x02, 0x02 }
+};
+
+int
+ahd_sstat3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SSTAT3_parse_table, 2, "SSTAT3",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE3_parse_table[] = {
+ { "ENOSRAMPERR", 0x01, 0x01 },
+ { "ENNTRAMPERR", 0x02, 0x02 }
+};
+
+int
+ahd_simode3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE3_parse_table, 2, "SIMODE3",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSINT3_parse_table[] = {
+ { "CLROSRAMPERR", 0x01, 0x01 },
+ { "CLRNTRAMPERR", 0x02, 0x02 }
+};
+
+int
+ahd_clrsint3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSINT3_parse_table, 2, "CLRSINT3",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOMODE0_parse_table[] = {
+ { "ENLQOTCRC", 0x01, 0x01 },
+ { "ENLQOATNPKT", 0x02, 0x02 },
+ { "ENLQOATNLQ", 0x04, 0x04 },
+ { "ENLQOSTOPT2", 0x08, 0x08 },
+ { "ENLQOTARGSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqomode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOMODE0_parse_table, 5, "LQOMODE0",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSTAT0_parse_table[] = {
+ { "LQOTCRC", 0x01, 0x01 },
+ { "LQOATNPKT", 0x02, 0x02 },
+ { "LQOATNLQ", 0x04, 0x04 },
+ { "LQOSTOPT2", 0x08, 0x08 },
+ { "LQOTARGSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqostat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSTAT0_parse_table, 5, "LQOSTAT0",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQOINT0_parse_table[] = {
+ { "CLRLQOTCRC", 0x01, 0x01 },
+ { "CLRLQOATNPKT", 0x02, 0x02 },
+ { "CLRLQOATNLQ", 0x04, 0x04 },
+ { "CLRLQOSTOPT2", 0x08, 0x08 },
+ { "CLRLQOTARGSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_clrlqoint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQOINT0_parse_table, 5, "CLRLQOINT0",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSTAT1_parse_table[] = {
+ { "LQOPHACHGINPKT", 0x01, 0x01 },
+ { "LQOBUSFREE", 0x02, 0x02 },
+ { "LQOBADQAS", 0x04, 0x04 },
+ { "LQOSTOPI2", 0x08, 0x08 },
+ { "LQOINITSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqostat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSTAT1_parse_table, 5, "LQOSTAT1",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRLQOINT1_parse_table[] = {
+ { "CLRLQOPHACHGINPKT", 0x01, 0x01 },
+ { "CLRLQOBUSFREE", 0x02, 0x02 },
+ { "CLRLQOBADQAS", 0x04, 0x04 },
+ { "CLRLQOSTOPI2", 0x08, 0x08 },
+ { "CLRLQOINITSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_clrlqoint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRLQOINT1_parse_table, 5, "CLRLQOINT1",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOMODE1_parse_table[] = {
+ { "ENLQOPHACHGINPKT", 0x01, 0x01 },
+ { "ENLQOBUSFREE", 0x02, 0x02 },
+ { "ENLQOBADQAS", 0x04, 0x04 },
+ { "ENLQOSTOPI2", 0x08, 0x08 },
+ { "ENLQOINITSCBPERR", 0x10, 0x10 }
+};
+
+int
+ahd_lqomode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOMODE1_parse_table, 5, "LQOMODE1",
+ 0x55, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSTAT2_parse_table[] = {
+ { "LQOSTOP0", 0x01, 0x01 },
+ { "LQOPHACHGOUTPKT", 0x02, 0x02 },
+ { "LQOWAITFIFO", 0x10, 0x10 },
+ { "LQOPKT", 0xe0, 0xe0 }
+};
+
+int
+ahd_lqostat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSTAT2_parse_table, 4, "LQOSTAT2",
+ 0x56, regvalue, cur_col, wrap));
+}
+
+int
+ahd_os_space_cnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OS_SPACE_CNT",
+ 0x56, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SIMODE1_parse_table[] = {
+ { "ENREQINIT", 0x01, 0x01 },
+ { "ENSTRB2FAST", 0x02, 0x02 },
+ { "ENSCSIPERR", 0x04, 0x04 },
+ { "ENBUSFREE", 0x08, 0x08 },
+ { "ENPHASEMIS", 0x10, 0x10 },
+ { "ENSCSIRST", 0x20, 0x20 },
+ { "ENATNTARG", 0x40, 0x40 },
+ { "ENSELTIMO", 0x80, 0x80 }
+};
+
+int
+ahd_simode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SIMODE1_parse_table, 8, "SIMODE1",
+ 0x57, regvalue, cur_col, wrap));
+}
+
+int
+ahd_gsfifo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "GSFIFO",
+ 0x58, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFFSXFRCTL_parse_table[] = {
+ { "RSTCHN", 0x01, 0x01 },
+ { "CLRCHN", 0x02, 0x02 },
+ { "CLRSHCNT", 0x04, 0x04 },
+ { "DFFBITBUCKET", 0x08, 0x08 }
+};
+
+int
+ahd_dffsxfrctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFFSXFRCTL_parse_table, 4, "DFFSXFRCTL",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LQOSCSCTL_parse_table[] = {
+ { "LQONOCHKOVER", 0x01, 0x01 },
+ { "LQOH2A_VERSION", 0x80, 0x80 }
+};
+
+int
+ahd_lqoscsctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LQOSCSCTL_parse_table, 2, "LQOSCSCTL",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_nextscb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEXTSCB",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CLRSEQINTSRC_parse_table[] = {
+ { "CLRCFG4TCMD", 0x01, 0x01 },
+ { "CLRCFG4ICMD", 0x02, 0x02 },
+ { "CLRCFG4TSTAT", 0x04, 0x04 },
+ { "CLRCFG4ISTAT", 0x08, 0x08 },
+ { "CLRCFG4DATA", 0x10, 0x10 },
+ { "CLRSAVEPTRS", 0x20, 0x20 },
+ { "CLRCTXTDONE", 0x40, 0x40 }
+};
+
+int
+ahd_clrseqintsrc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CLRSEQINTSRC_parse_table, 7, "CLRSEQINTSRC",
+ 0x5b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTSRC_parse_table[] = {
+ { "CFG4TCMD", 0x01, 0x01 },
+ { "CFG4ICMD", 0x02, 0x02 },
+ { "CFG4TSTAT", 0x04, 0x04 },
+ { "CFG4ISTAT", 0x08, 0x08 },
+ { "CFG4DATA", 0x10, 0x10 },
+ { "SAVEPTRS", 0x20, 0x20 },
+ { "CTXTDONE", 0x40, 0x40 }
+};
+
+int
+ahd_seqintsrc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTSRC_parse_table, 7, "SEQINTSRC",
+ 0x5b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_currscb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CURRSCB",
+ 0x5c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQIMODE_parse_table[] = {
+ { "ENCFG4TCMD", 0x01, 0x01 },
+ { "ENCFG4ICMD", 0x02, 0x02 },
+ { "ENCFG4TSTAT", 0x04, 0x04 },
+ { "ENCFG4ISTAT", 0x08, 0x08 },
+ { "ENCFG4DATA", 0x10, 0x10 },
+ { "ENSAVEPTRS", 0x20, 0x20 },
+ { "ENCTXTDONE", 0x40, 0x40 }
+};
+
+int
+ahd_seqimode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQIMODE_parse_table, 7, "SEQIMODE",
+ 0x5c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t MDFFSTAT_parse_table[] = {
+ { "FIFOFREE", 0x01, 0x01 },
+ { "DATAINFIFO", 0x02, 0x02 },
+ { "DLZERO", 0x04, 0x04 },
+ { "SHVALID", 0x08, 0x08 },
+ { "LASTSDONE", 0x10, 0x10 },
+ { "SHCNTMINUS1", 0x20, 0x20 },
+ { "SHCNTNEGATIVE", 0x40, 0x40 }
+};
+
+int
+ahd_mdffstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(MDFFSTAT_parse_table, 7, "MDFFSTAT",
+ 0x5d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CRCCONTROL_parse_table[] = {
+ { "CRCVALCHKEN", 0x40, 0x40 }
+};
+
+int
+ahd_crccontrol_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CRCCONTROL_parse_table, 1, "CRCCONTROL",
+ 0x5d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfftag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFFTAG",
+ 0x5e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lastscb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LASTSCB",
+ 0x5e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSITEST_parse_table[] = {
+ { "SEL_TXPLL_DEBUG", 0x04, 0x04 },
+ { "CNTRTEST", 0x08, 0x08 }
+};
+
+int
+ahd_scsitest_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSITEST_parse_table, 2, "SCSITEST",
+ 0x5e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t IOPDNCTL_parse_table[] = {
+ { "PDN_DIFFSENSE", 0x01, 0x01 },
+ { "PDN_IDIST", 0x04, 0x04 },
+ { "DISABLE_OE", 0x80, 0x80 }
+};
+
+int
+ahd_iopdnctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(IOPDNCTL_parse_table, 3, "IOPDNCTL",
+ 0x5f, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHADDR",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahd_negoaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEGOADDR",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dgrpcrci_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DGRPCRCI",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahd_negperiod_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEGPERIOD",
+ 0x61, regvalue, cur_col, wrap));
+}
+
+int
+ahd_packcrci_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PACKCRCI",
+ 0x62, regvalue, cur_col, wrap));
+}
+
+int
+ahd_negoffset_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEGOFFSET",
+ 0x62, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t NEGPPROPTS_parse_table[] = {
+ { "PPROPT_IUT", 0x01, 0x01 },
+ { "PPROPT_DT", 0x02, 0x02 },
+ { "PPROPT_QAS", 0x04, 0x04 },
+ { "PPROPT_PACE", 0x08, 0x08 }
+};
+
+int
+ahd_negppropts_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NEGPPROPTS_parse_table, 4, "NEGPPROPTS",
+ 0x63, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t NEGCONOPTS_parse_table[] = {
+ { "WIDEXFER", 0x01, 0x01 },
+ { "ENAUTOATNO", 0x02, 0x02 },
+ { "ENAUTOATNI", 0x04, 0x04 },
+ { "ENSLOWCRC", 0x08, 0x08 },
+ { "RTI_OVRDTRN", 0x10, 0x10 },
+ { "RTI_WRTDIS", 0x20, 0x20 },
+ { "ENSNAPSHOT", 0x40, 0x40 }
+};
+
+int
+ahd_negconopts_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NEGCONOPTS_parse_table, 7, "NEGCONOPTS",
+ 0x64, regvalue, cur_col, wrap));
+}
+
+int
+ahd_annexcol_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ANNEXCOL",
+ 0x65, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSCHKN_parse_table[] = {
+ { "LSTSGCLRDIS", 0x01, 0x01 },
+ { "SHVALIDSTDIS", 0x02, 0x02 },
+ { "DFFACTCLR", 0x04, 0x04 },
+ { "SDONEMSKDIS", 0x08, 0x08 },
+ { "WIDERESEN", 0x10, 0x10 },
+ { "CURRFIFODEF", 0x20, 0x20 },
+ { "STSELSKIDDIS", 0x40, 0x40 }
+};
+
+int
+ahd_scschkn_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSCHKN_parse_table, 7, "SCSCHKN",
+ 0x66, regvalue, cur_col, wrap));
+}
+
+int
+ahd_annexdat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ANNEXDAT",
+ 0x66, regvalue, cur_col, wrap));
+}
+
+int
+ahd_iownid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "IOWNID",
+ 0x67, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL960CTL0_parse_table[] = {
+ { "PLL_ENFBM", 0x01, 0x01 },
+ { "PLL_DLPF", 0x02, 0x02 },
+ { "PLL_ENLPF", 0x04, 0x04 },
+ { "PLL_ENLUD", 0x08, 0x08 },
+ { "PLL_NS", 0x30, 0x30 },
+ { "PLL_PWDN", 0x40, 0x40 },
+ { "PLL_VCOSEL", 0x80, 0x80 }
+};
+
+int
+ahd_pll960ctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL960CTL0_parse_table, 7, "PLL960CTL0",
+ 0x68, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHCNT",
+ 0x68, regvalue, cur_col, wrap));
+}
+
+int
+ahd_townid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TOWNID",
+ 0x69, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL960CTL1_parse_table[] = {
+ { "PLL_RST", 0x01, 0x01 },
+ { "PLL_CNTCLR", 0x40, 0x40 },
+ { "PLL_CNTEN", 0x80, 0x80 }
+};
+
+int
+ahd_pll960ctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL960CTL1_parse_table, 3, "PLL960CTL1",
+ 0x69, regvalue, cur_col, wrap));
+}
+
+int
+ahd_pll960cnt0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PLL960CNT0",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_xsig_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "XSIG",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seloid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SELOID",
+ 0x6b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL400CTL0_parse_table[] = {
+ { "PLL_ENFBM", 0x01, 0x01 },
+ { "PLL_DLPF", 0x02, 0x02 },
+ { "PLL_ENLPF", 0x04, 0x04 },
+ { "PLL_ENLUD", 0x08, 0x08 },
+ { "PLL_NS", 0x30, 0x30 },
+ { "PLL_PWDN", 0x40, 0x40 },
+ { "PLL_VCOSEL", 0x80, 0x80 }
+};
+
+int
+ahd_pll400ctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL400CTL0_parse_table, 7, "PLL400CTL0",
+ 0x6c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_fairness_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FAIRNESS",
+ 0x6c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLL400CTL1_parse_table[] = {
+ { "PLL_RST", 0x01, 0x01 },
+ { "PLL_CNTCLR", 0x40, 0x40 },
+ { "PLL_CNTEN", 0x80, 0x80 }
+};
+
+int
+ahd_pll400ctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLL400CTL1_parse_table, 3, "PLL400CTL1",
+ 0x6d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_pll400cnt0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PLL400CNT0",
+ 0x6e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_unfairness_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "UNFAIRNESS",
+ 0x6e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_haddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HADDR",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PLLDELAY_parse_table[] = {
+ { "SPLIT_DROP_REQ", 0x80, 0x80 }
+};
+
+int
+ahd_plldelay_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PLLDELAY_parse_table, 1, "PLLDELAY",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hodmaadr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HODMAADR",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hodmacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HODMACNT",
+ 0x78, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HCNT",
+ 0x78, regvalue, cur_col, wrap));
+}
+
+int
+ahd_hodmaen_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "HODMAEN",
+ 0x7a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sghaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SGHADDR",
+ 0x7c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbhaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBHADDR",
+ 0x7c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sghcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SGHCNT",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbhcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBHCNT",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFF_THRSH_parse_table[] = {
+ { "WR_DFTHRSH_MIN", 0x00, 0x70 },
+ { "RD_DFTHRSH_MIN", 0x00, 0x07 },
+ { "RD_DFTHRSH_25", 0x01, 0x07 },
+ { "RD_DFTHRSH_50", 0x02, 0x07 },
+ { "RD_DFTHRSH_63", 0x03, 0x07 },
+ { "RD_DFTHRSH_75", 0x04, 0x07 },
+ { "RD_DFTHRSH_85", 0x05, 0x07 },
+ { "RD_DFTHRSH_90", 0x06, 0x07 },
+ { "RD_DFTHRSH_MAX", 0x07, 0x07 },
+ { "WR_DFTHRSH_25", 0x10, 0x70 },
+ { "WR_DFTHRSH_50", 0x20, 0x70 },
+ { "WR_DFTHRSH_63", 0x30, 0x70 },
+ { "WR_DFTHRSH_75", 0x40, 0x70 },
+ { "WR_DFTHRSH_85", 0x50, 0x70 },
+ { "WR_DFTHRSH_90", 0x60, 0x70 },
+ { "WR_DFTHRSH_MAX", 0x70, 0x70 },
+ { "RD_DFTHRSH", 0x07, 0x07 },
+ { "WR_DFTHRSH", 0x70, 0x70 }
+};
+
+int
+ahd_dff_thrsh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFF_THRSH_parse_table, 18, "DFF_THRSH",
+ 0x88, regvalue, cur_col, wrap));
+}
+
+int
+ahd_romaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ROMADDR",
+ 0x8a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ROMCNTRL_parse_table[] = {
+ { "RDY", 0x01, 0x01 },
+ { "REPEAT", 0x02, 0x02 },
+ { "ROMSPD", 0x18, 0x18 },
+ { "ROMOP", 0xe0, 0xe0 }
+};
+
+int
+ahd_romcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ROMCNTRL_parse_table, 4, "ROMCNTRL",
+ 0x8d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_romdata_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ROMDATA",
+ 0x8e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_cmcrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG0_parse_table, 2, "CMCRXMSG0",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ROENABLE_parse_table[] = {
+ { "DCH0ROEN", 0x01, 0x01 },
+ { "DCH1ROEN", 0x02, 0x02 },
+ { "SGROEN", 0x04, 0x04 },
+ { "CMCROEN", 0x08, 0x08 },
+ { "OVLYROEN", 0x10, 0x10 },
+ { "MSIROEN", 0x20, 0x20 }
+};
+
+int
+ahd_roenable_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ROENABLE_parse_table, 6, "ROENABLE",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_ovlyrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG0_parse_table, 2, "OVLYRXMSG0",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_dchrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG0_parse_table, 2, "DCHRXMSG0",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_ovlyrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG1_parse_table, 1, "OVLYRXMSG1",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t NSENABLE_parse_table[] = {
+ { "DCH0NSEN", 0x01, 0x01 },
+ { "DCH1NSEN", 0x02, 0x02 },
+ { "SGNSEN", 0x04, 0x04 },
+ { "CMCNSEN", 0x08, 0x08 },
+ { "OVLYNSEN", 0x10, 0x10 },
+ { "MSINSEN", 0x20, 0x20 }
+};
+
+int
+ahd_nsenable_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NSENABLE_parse_table, 6, "NSENABLE",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_dchrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG1_parse_table, 1, "DCHRXMSG1",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_cmcrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG1_parse_table, 1, "CMCRXMSG1",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_dchrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG2_parse_table, 1, "DCHRXMSG2",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_ovlyrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG2_parse_table, 1, "OVLYRXMSG2",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_cmcrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG2_parse_table, 1, "CMCRXMSG2",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ost_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OST",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_dchrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHRXMSG3_parse_table, 1, "DCHRXMSG3",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_cmcrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCRXMSG3_parse_table, 1, "CMCRXMSG3",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t PCIXCTL_parse_table[] = {
+ { "CMPABCDIS", 0x01, 0x01 },
+ { "TSCSERREN", 0x02, 0x02 },
+ { "SRSPDPEEN", 0x04, 0x04 },
+ { "SPLTSTADIS", 0x08, 0x08 },
+ { "SPLTSMADIS", 0x10, 0x10 },
+ { "UNEXPSCIEN", 0x20, 0x20 },
+ { "SERRPULSE", 0x80, 0x80 }
+};
+
+int
+ahd_pcixctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(PCIXCTL_parse_table, 7, "PCIXCTL",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_ovlyrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYRXMSG3_parse_table, 1, "OVLYRXMSG3",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ovlyseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OVLYSEQBCNT",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmcseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMCSEQBCNT",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dchseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DCHSEQBCNT",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_cmcspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCSPLTSTAT0_parse_table, 8, "CMCSPLTSTAT0",
+ 0x96, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_ovlyspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYSPLTSTAT0_parse_table, 8, "OVLYSPLTSTAT0",
+ 0x96, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_dchspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHSPLTSTAT0_parse_table, 8, "DCHSPLTSTAT0",
+ 0x96, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DCHSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_dchspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DCHSPLTSTAT1_parse_table, 1, "DCHSPLTSTAT1",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_cmcspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCSPLTSTAT1_parse_table, 1, "CMCSPLTSTAT1",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_ovlyspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYSPLTSTAT1_parse_table, 1, "OVLYSPLTSTAT1",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG0_parse_table[] = {
+ { "CFNUM", 0x07, 0x07 },
+ { "CDNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_sgrxmsg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG0_parse_table, 2, "SGRXMSG0",
+ 0x98, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR0_parse_table[] = {
+ { "LOWER_ADDR", 0x7f, 0x7f }
+};
+
+int
+ahd_slvspltoutadr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR0_parse_table, 1, "SLVSPLTOUTADR0",
+ 0x98, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG1_parse_table[] = {
+ { "CBNUM", 0xff, 0xff }
+};
+
+int
+ahd_sgrxmsg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG1_parse_table, 1, "SGRXMSG1",
+ 0x99, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR1_parse_table[] = {
+ { "REQ_FNUM", 0x07, 0x07 },
+ { "REQ_DNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_slvspltoutadr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR1_parse_table, 2, "SLVSPLTOUTADR1",
+ 0x99, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG2_parse_table[] = {
+ { "MINDEX", 0xff, 0xff }
+};
+
+int
+ahd_sgrxmsg2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG2_parse_table, 1, "SGRXMSG2",
+ 0x9a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR2_parse_table[] = {
+ { "REQ_BNUM", 0xff, 0xff }
+};
+
+int
+ahd_slvspltoutadr2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR2_parse_table, 1, "SLVSPLTOUTADR2",
+ 0x9a, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGRXMSG3_parse_table[] = {
+ { "MCLASS", 0x0f, 0x0f }
+};
+
+int
+ahd_sgrxmsg3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGRXMSG3_parse_table, 1, "SGRXMSG3",
+ 0x9b, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTADR3_parse_table[] = {
+ { "RLXORD", 0x10, 0x10 },
+ { "TAG_NUM", 0x1f, 0x1f }
+};
+
+int
+ahd_slvspltoutadr3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTADR3_parse_table, 2, "SLVSPLTOUTADR3",
+ 0x9b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sgseqbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SGSEQBCNT",
+ 0x9c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTATTR0_parse_table[] = {
+ { "LOWER_BCNT", 0xff, 0xff }
+};
+
+int
+ahd_slvspltoutattr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTATTR0_parse_table, 1, "SLVSPLTOUTATTR0",
+ 0x9c, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTATTR1_parse_table[] = {
+ { "CMPLT_FNUM", 0x07, 0x07 },
+ { "CMPLT_DNUM", 0xf8, 0xf8 }
+};
+
+int
+ahd_slvspltoutattr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTATTR1_parse_table, 2, "SLVSPLTOUTATTR1",
+ 0x9d, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SLVSPLTOUTATTR2_parse_table[] = {
+ { "CMPLT_BNUM", 0xff, 0xff }
+};
+
+int
+ahd_slvspltoutattr2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SLVSPLTOUTATTR2_parse_table, 1, "SLVSPLTOUTATTR2",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGSPLTSTAT0_parse_table[] = {
+ { "RXSPLTRSP", 0x01, 0x01 },
+ { "RXSCEMSG", 0x02, 0x02 },
+ { "RXOVRUN", 0x04, 0x04 },
+ { "CNTNOTCMPLT", 0x08, 0x08 },
+ { "SCDATBUCKET", 0x10, 0x10 },
+ { "SCADERR", 0x20, 0x20 },
+ { "SCBCERR", 0x40, 0x40 },
+ { "STAETERM", 0x80, 0x80 }
+};
+
+int
+ahd_sgspltstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGSPLTSTAT0_parse_table, 8, "SGSPLTSTAT0",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SFUNCT_parse_table[] = {
+ { "TEST_NUM", 0x0f, 0x0f },
+ { "TEST_GROUP", 0xf0, 0xf0 }
+};
+
+int
+ahd_sfunct_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SFUNCT_parse_table, 2, "SFUNCT",
+ 0x9f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGSPLTSTAT1_parse_table[] = {
+ { "RXDATABUCKET", 0x01, 0x01 }
+};
+
+int
+ahd_sgspltstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGSPLTSTAT1_parse_table, 1, "SGSPLTSTAT1",
+ 0x9f, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DF0PCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_df0pcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DF0PCISTAT_parse_table, 8, "DF0PCISTAT",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_reg0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "REG0",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DF1PCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_df1pcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DF1PCISTAT_parse_table, 8, "DF1PCISTAT",
+ 0xa1, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SGPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_sgpcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SGPCISTAT_parse_table, 7, "SGPCISTAT",
+ 0xa2, regvalue, cur_col, wrap));
+}
+
+int
+ahd_reg1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "REG1",
+ 0xa2, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMCPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_cmcpcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMCPCISTAT_parse_table, 8, "CMCPCISTAT",
+ 0xa3, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t OVLYPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "RDPERR", 0x04, 0x04 },
+ { "SCAAPERR", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_ovlypcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(OVLYPCISTAT_parse_table, 7, "OVLYPCISTAT",
+ 0xa4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_reg_isr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "REG_ISR",
+ 0xa4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SG_STATE_parse_table[] = {
+ { "SEGS_AVAIL", 0x01, 0x01 },
+ { "LOADING_NEEDED", 0x02, 0x02 },
+ { "FETCH_INPROG", 0x04, 0x04 }
+};
+
+int
+ahd_sg_state_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SG_STATE_parse_table, 3, "SG_STATE",
+ 0xa6, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t MSIPCISTAT_parse_table[] = {
+ { "DPR", 0x01, 0x01 },
+ { "TWATERR", 0x02, 0x02 },
+ { "CLRPENDMSI", 0x08, 0x08 },
+ { "RTA", 0x10, 0x10 },
+ { "RMA", 0x20, 0x20 },
+ { "SSE", 0x40, 0x40 }
+};
+
+int
+ahd_msipcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(MSIPCISTAT_parse_table, 6, "MSIPCISTAT",
+ 0xa6, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t TARGPCISTAT_parse_table[] = {
+ { "TWATERR", 0x02, 0x02 },
+ { "STA", 0x08, 0x08 },
+ { "SSE", 0x40, 0x40 },
+ { "DPE", 0x80, 0x80 }
+};
+
+int
+ahd_targpcistat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(TARGPCISTAT_parse_table, 4, "TARGPCISTAT",
+ 0xa7, regvalue, cur_col, wrap));
+}
+
+int
+ahd_data_count_odd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DATA_COUNT_ODD",
+ 0xa7, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBPTR",
+ 0xa8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBACNT",
+ 0xab, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCBAUTOPTR_parse_table[] = {
+ { "SCBPTR_OFF", 0x07, 0x07 },
+ { "SCBPTR_ADDR", 0x38, 0x38 },
+ { "AUSCBPTR_EN", 0x80, 0x80 }
+};
+
+int
+ahd_scbautoptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCBAUTOPTR_parse_table, 3, "SCBAUTOPTR",
+ 0xab, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccsgaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSGADDR",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBADDR",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbadr_bk_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBADR_BK",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CMC_RAMBIST_parse_table[] = {
+ { "CMC_BUFFER_BIST_EN", 0x01, 0x01 },
+ { "CMC_BUFFER_BIST_FAIL",0x02, 0x02 },
+ { "SG_BIST_EN", 0x10, 0x10 },
+ { "SG_BIST_FAIL", 0x20, 0x20 },
+ { "SCBRAMBIST_FAIL", 0x40, 0x40 },
+ { "SG_ELEMENT_SIZE", 0x80, 0x80 }
+};
+
+int
+ahd_cmc_rambist_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CMC_RAMBIST_parse_table, 6, "CMC_RAMBIST",
+ 0xad, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CCSGCTL_parse_table[] = {
+ { "CCSGRESET", 0x01, 0x01 },
+ { "SG_FETCH_REQ", 0x02, 0x02 },
+ { "CCSGENACK", 0x08, 0x08 },
+ { "SG_CACHE_AVAIL", 0x10, 0x10 },
+ { "CCSGDONE", 0x80, 0x80 },
+ { "CCSGEN", 0x0c, 0x0c }
+};
+
+int
+ahd_ccsgctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CCSGCTL_parse_table, 6, "CCSGCTL",
+ 0xad, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t CCSCBCTL_parse_table[] = {
+ { "CCSCBRESET", 0x01, 0x01 },
+ { "CCSCBDIR", 0x04, 0x04 },
+ { "CCSCBEN", 0x08, 0x08 },
+ { "CCARREN", 0x10, 0x10 },
+ { "ARRDONE", 0x40, 0x40 },
+ { "CCSCBDONE", 0x80, 0x80 }
+};
+
+int
+ahd_ccscbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(CCSCBCTL_parse_table, 6, "CCSCBCTL",
+ 0xad, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccsgram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSGRAM",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flexadr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLEXADR",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ccscbram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CCSCBRAM",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flexcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLEXCNT",
+ 0xb3, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t FLEXDMASTAT_parse_table[] = {
+ { "FLEXDMADONE", 0x01, 0x01 },
+ { "FLEXDMAERR", 0x02, 0x02 }
+};
+
+int
+ahd_flexdmastat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(FLEXDMASTAT_parse_table, 2, "FLEXDMASTAT",
+ 0xb5, regvalue, cur_col, wrap));
+}
+
+int
+ahd_flexdata_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FLEXDATA",
+ 0xb6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_brddat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BRDDAT",
+ 0xb8, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t BRDCTL_parse_table[] = {
+ { "BRDSTB", 0x01, 0x01 },
+ { "BRDRW", 0x02, 0x02 },
+ { "BRDEN", 0x04, 0x04 },
+ { "BRDADDR", 0x38, 0x38 },
+ { "FLXARBREQ", 0x40, 0x40 },
+ { "FLXARBACK", 0x80, 0x80 }
+};
+
+int
+ahd_brdctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(BRDCTL_parse_table, 6, "BRDCTL",
+ 0xb9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seeadr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SEEADR",
+ 0xba, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seedat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SEEDAT",
+ 0xbc, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEECTL_parse_table[] = {
+ { "SEEOP_ERAL", 0x40, 0x70 },
+ { "SEEOP_WRITE", 0x50, 0x70 },
+ { "SEEOP_READ", 0x60, 0x70 },
+ { "SEEOP_ERASE", 0x70, 0x70 },
+ { "SEESTART", 0x01, 0x01 },
+ { "SEERST", 0x02, 0x02 },
+ { "SEEOPCODE", 0x70, 0x70 },
+ { "SEEOP_EWEN", 0x40, 0x40 },
+ { "SEEOP_WALL", 0x40, 0x40 },
+ { "SEEOP_EWDS", 0x40, 0x40 }
+};
+
+int
+ahd_seectl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEECTL_parse_table, 10, "SEECTL",
+ 0xbe, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEESTAT_parse_table[] = {
+ { "SEESTART", 0x01, 0x01 },
+ { "SEEBUSY", 0x02, 0x02 },
+ { "SEEARBACK", 0x04, 0x04 },
+ { "LDALTID_L", 0x08, 0x08 },
+ { "SEEOPCODE", 0x70, 0x70 },
+ { "INIT_DONE", 0x80, 0x80 }
+};
+
+int
+ahd_seestat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEESTAT_parse_table, 6, "SEESTAT",
+ 0xbe, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCBCNT",
+ 0xbf, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfwaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFWADDR",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPFLTRCTL_parse_table[] = {
+ { "DSPFCNTSEL", 0x0f, 0x0f },
+ { "EDGESENSE", 0x10, 0x10 },
+ { "FLTRDISABLE", 0x20, 0x20 }
+};
+
+int
+ahd_dspfltrctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPFLTRCTL_parse_table, 3, "DSPFLTRCTL",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPDATACTL_parse_table[] = {
+ { "XMITOFFSTDIS", 0x02, 0x02 },
+ { "RCVROFFSTDIS", 0x04, 0x04 },
+ { "DESQDIS", 0x10, 0x10 },
+ { "BYPASSENAB", 0x80, 0x80 }
+};
+
+int
+ahd_dspdatactl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPDATACTL_parse_table, 4, "DSPDATACTL",
+ 0xc1, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfraddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFRADDR",
+ 0xc2, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPREQCTL_parse_table[] = {
+ { "MANREQDLY", 0x3f, 0x3f },
+ { "MANREQCTL", 0xc0, 0xc0 }
+};
+
+int
+ahd_dspreqctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPREQCTL_parse_table, 2, "DSPREQCTL",
+ 0xc2, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPACKCTL_parse_table[] = {
+ { "MANACKDLY", 0x3f, 0x3f },
+ { "MANACKCTL", 0xc0, 0xc0 }
+};
+
+int
+ahd_dspackctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPACKCTL_parse_table, 2, "DSPACKCTL",
+ 0xc3, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfdat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFDAT",
+ 0xc4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DSPSELECT_parse_table[] = {
+ { "DSPSEL", 0x1f, 0x1f },
+ { "AUTOINCEN", 0x80, 0x80 }
+};
+
+int
+ahd_dspselect_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DSPSELECT_parse_table, 2, "DSPSELECT",
+ 0xc4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t WRTBIASCTL_parse_table[] = {
+ { "XMITMANVAL", 0x3f, 0x3f },
+ { "AUTOXBCDIS", 0x80, 0x80 }
+};
+
+int
+ahd_wrtbiasctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(WRTBIASCTL_parse_table, 2, "WRTBIASCTL",
+ 0xc5, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t RCVRBIOSCTL_parse_table[] = {
+ { "RCVRMANVAL", 0x3f, 0x3f },
+ { "AUTORBCDIS", 0x80, 0x80 }
+};
+
+int
+ahd_rcvrbiosctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(RCVRBIOSCTL_parse_table, 2, "RCVRBIOSCTL",
+ 0xc6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_wrtbiascalc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WRTBIASCALC",
+ 0xc7, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfptrs_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFPTRS",
+ 0xc8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_rcvrbiascalc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "RCVRBIASCALC",
+ 0xc8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfbkptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFBKPTR",
+ 0xc9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_skewcalc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SKEWCALC",
+ 0xc9, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DFDBCTL_parse_table[] = {
+ { "DFF_RAMBIST_EN", 0x01, 0x01 },
+ { "DFF_RAMBIST_DONE", 0x02, 0x02 },
+ { "DFF_RAMBIST_FAIL", 0x04, 0x04 },
+ { "DFF_DIR_ERR", 0x08, 0x08 },
+ { "DFF_CIO_RD_RDY", 0x10, 0x10 },
+ { "DFF_CIO_WR_RDY", 0x20, 0x20 }
+};
+
+int
+ahd_dfdbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DFDBCTL_parse_table, 6, "DFDBCTL",
+ 0xcb, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfscnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFSCNT",
+ 0xcc, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dfbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DFBCNT",
+ 0xce, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ovlyaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "OVLYADDR",
+ 0xd4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQCTL0_parse_table[] = {
+ { "LOADRAM", 0x01, 0x01 },
+ { "SEQRESET", 0x02, 0x02 },
+ { "STEP", 0x04, 0x04 },
+ { "BRKADRINTEN", 0x08, 0x08 },
+ { "FASTMODE", 0x10, 0x10 },
+ { "FAILDIS", 0x20, 0x20 },
+ { "PAUSEDIS", 0x40, 0x40 },
+ { "PERRORDIS", 0x80, 0x80 }
+};
+
+int
+ahd_seqctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQCTL0_parse_table, 8, "SEQCTL0",
+ 0xd6, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQCTL1_parse_table[] = {
+ { "RAMBIST_EN", 0x01, 0x01 },
+ { "RAMBIST_FAIL", 0x02, 0x02 },
+ { "RAMBIST_DONE", 0x04, 0x04 },
+ { "OVRLAY_DATA_CHK", 0x08, 0x08 }
+};
+
+int
+ahd_seqctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQCTL1_parse_table, 4, "SEQCTL1",
+ 0xd7, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t FLAGS_parse_table[] = {
+ { "CARRY", 0x01, 0x01 },
+ { "ZERO", 0x02, 0x02 }
+};
+
+int
+ahd_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(FLAGS_parse_table, 2, "FLAGS",
+ 0xd8, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQINTCTL_parse_table[] = {
+ { "IRET", 0x01, 0x01 },
+ { "INTMASK1", 0x02, 0x02 },
+ { "INTMASK2", 0x04, 0x04 },
+ { "SCS_SEQ_INT1M0", 0x08, 0x08 },
+ { "SCS_SEQ_INT1M1", 0x10, 0x10 },
+ { "INT1_CONTEXT", 0x20, 0x20 },
+ { "INTVEC1DSL", 0x80, 0x80 }
+};
+
+int
+ahd_seqintctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQINTCTL_parse_table, 7, "SEQINTCTL",
+ 0xd9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seqram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SEQRAM",
+ 0xda, regvalue, cur_col, wrap));
+}
+
+int
+ahd_prgmcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "PRGMCNT",
+ 0xde, regvalue, cur_col, wrap));
+}
+
+int
+ahd_accum_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ACCUM",
+ 0xe0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SINDEX",
+ 0xe2, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DINDEX",
+ 0xe4, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t BRKADDR1_parse_table[] = {
+ { "BRKDIS", 0x80, 0x80 }
+};
+
+int
+ahd_brkaddr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(BRKADDR1_parse_table, 1, "BRKADDR1",
+ 0xe6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_brkaddr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "BRKADDR0",
+ 0xe6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_allones_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ALLONES",
+ 0xe8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_allzeros_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ALLZEROS",
+ 0xea, regvalue, cur_col, wrap));
+}
+
+int
+ahd_none_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NONE",
+ 0xea, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SINDIR",
+ 0xec, regvalue, cur_col, wrap));
+}
+
+int
+ahd_dindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "DINDIR",
+ 0xed, regvalue, cur_col, wrap));
+}
+
+int
+ahd_function1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "FUNCTION1",
+ 0xf0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_stack_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "STACK",
+ 0xf2, regvalue, cur_col, wrap));
+}
+
+int
+ahd_curaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CURADDR",
+ 0xf4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_intvec1_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INTVEC1_ADDR",
+ 0xf4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_intvec2_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INTVEC2_ADDR",
+ 0xf6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_lastaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LASTADDR",
+ 0xf6, regvalue, cur_col, wrap));
+}
+
+int
+ahd_longjmp_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LONGJMP_ADDR",
+ 0xf8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_accum_save_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ACCUM_SAVE",
+ 0xfa, regvalue, cur_col, wrap));
+}
+
+int
+ahd_waiting_scb_tails_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WAITING_SCB_TAILS",
+ 0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_ahd_pci_config_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "AHD_PCI_CONFIG_BASE",
+ 0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sram_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SRAM_BASE",
+ 0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_waiting_tid_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WAITING_TID_HEAD",
+ 0x120, regvalue, cur_col, wrap));
+}
+
+int
+ahd_waiting_tid_tail_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "WAITING_TID_TAIL",
+ 0x122, regvalue, cur_col, wrap));
+}
+
+int
+ahd_next_queued_scb_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "NEXT_QUEUED_SCB_ADDR",
+ 0x124, regvalue, cur_col, wrap));
+}
+
+int
+ahd_complete_scb_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "COMPLETE_SCB_HEAD",
+ 0x128, regvalue, cur_col, wrap));
+}
+
+int
+ahd_complete_scb_dmainprog_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "COMPLETE_SCB_DMAINPROG_HEAD",
+ 0x12a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_complete_dma_scb_head_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_HEAD",
+ 0x12c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qfreeze_count_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QFREEZE_COUNT",
+ 0x12e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_mode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SAVED_MODE",
+ 0x130, regvalue, cur_col, wrap));
+}
+
+int
+ahd_msg_out_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "MSG_OUT",
+ 0x131, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t DMAPARAMS_parse_table[] = {
+ { "FIFORESET", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SDMAEN", 0x10, 0x10 },
+ { "SDMAENACK", 0x10, 0x10 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "WIDEODD", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahd_dmaparams_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(DMAPARAMS_parse_table, 10, "DMAPARAMS",
+ 0x132, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQ_FLAGS_parse_table[] = {
+ { "NO_DISCONNECT", 0x01, 0x01 },
+ { "SPHASE_PENDING", 0x02, 0x02 },
+ { "DPHASE_PENDING", 0x04, 0x04 },
+ { "CMDPHASE_PENDING", 0x08, 0x08 },
+ { "TARG_CMD_PENDING", 0x10, 0x10 },
+ { "DPHASE", 0x20, 0x20 },
+ { "NO_CDB_SENT", 0x40, 0x40 },
+ { "TARGET_CMD_IS_TAGGED",0x40, 0x40 },
+ { "NOT_IDENTIFIED", 0x80, 0x80 }
+};
+
+int
+ahd_seq_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQ_FLAGS_parse_table, 9, "SEQ_FLAGS",
+ 0x133, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SAVED_SCSIID",
+ 0x134, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SAVED_LUN",
+ 0x135, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t LASTPHASE_parse_table[] = {
+ { "P_DATAOUT", 0x00, 0xe0 },
+ { "P_DATAOUT_DT", 0x20, 0xe0 },
+ { "P_DATAIN", 0x40, 0xe0 },
+ { "P_DATAIN_DT", 0x60, 0xe0 },
+ { "P_COMMAND", 0x80, 0xe0 },
+ { "P_MESGOUT", 0xa0, 0xe0 },
+ { "P_STATUS", 0xc0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 },
+ { "P_BUSFREE", 0x01, 0x01 },
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "PHASE_MASK", 0xe0, 0xe0 }
+};
+
+int
+ahd_lastphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(LASTPHASE_parse_table, 13, "LASTPHASE",
+ 0x136, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qoutfifo_entry_valid_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QOUTFIFO_ENTRY_VALID_TAG",
+ 0x137, regvalue, cur_col, wrap));
+}
+
+int
+ahd_shared_data_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SHARED_DATA_ADDR",
+ 0x138, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qoutfifo_next_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "QOUTFIFO_NEXT_ADDR",
+ 0x13c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_kernel_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "KERNEL_TQINPOS",
+ 0x140, regvalue, cur_col, wrap));
+}
+
+int
+ahd_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "TQINPOS",
+ 0x141, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t ARG_1_parse_table[] = {
+ { "CONT_MSG_LOOP_TARG", 0x02, 0x02 },
+ { "CONT_MSG_LOOP_READ", 0x03, 0x03 },
+ { "CONT_MSG_LOOP_WRITE",0x04, 0x04 },
+ { "EXIT_MSG_LOOP", 0x08, 0x08 },
+ { "MSGOUT_PHASEMIS", 0x10, 0x10 },
+ { "SEND_REJ", 0x20, 0x20 },
+ { "SEND_SENSE", 0x40, 0x40 },
+ { "SEND_MSG", 0x80, 0x80 }
+};
+
+int
+ahd_arg_1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(ARG_1_parse_table, 8, "ARG_1",
+ 0x142, regvalue, cur_col, wrap));
+}
+
+int
+ahd_arg_2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ARG_2",
+ 0x143, regvalue, cur_col, wrap));
+}
+
+int
+ahd_last_msg_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LAST_MSG",
+ 0x144, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCSISEQ_TEMPLATE_parse_table[] = {
+ { "ALTSTIM", 0x01, 0x01 },
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "MANUALP", 0x0c, 0x0c },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "MANUALCTL", 0x40, 0x40 }
+};
+
+int
+ahd_scsiseq_template_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCSISEQ_TEMPLATE_parse_table, 6, "SCSISEQ_TEMPLATE",
+ 0x145, regvalue, cur_col, wrap));
+}
+
+int
+ahd_initiator_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INITIATOR_TAG",
+ 0x146, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SEQ_FLAGS2_parse_table[] = {
+ { "TARGET_MSG_PENDING", 0x02, 0x02 },
+ { "SELECTOUT_QFROZEN", 0x04, 0x04 }
+};
+
+int
+ahd_seq_flags2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SEQ_FLAGS2_parse_table, 2, "SEQ_FLAGS2",
+ 0x147, regvalue, cur_col, wrap));
+}
+
+int
+ahd_allocfifo_scbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "ALLOCFIFO_SCBPTR",
+ 0x148, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalescing_timer_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESCING_TIMER",
+ 0x14a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalescing_maxcmds_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESCING_MAXCMDS",
+ 0x14c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalescing_mincmds_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESCING_MINCMDS",
+ 0x14d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmds_pending_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDS_PENDING",
+ 0x14e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_int_coalescing_cmdcount_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "INT_COALESCING_CMDCOUNT",
+ 0x150, regvalue, cur_col, wrap));
+}
+
+int
+ahd_local_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "LOCAL_HS_MAILBOX",
+ 0x151, regvalue, cur_col, wrap));
+}
+
+int
+ahd_cmdsize_table_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "CMDSIZE_TABLE",
+ 0x152, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_BASE",
+ 0x180, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_residual_datacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_RESIDUAL_DATACNT",
+ 0x180, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_RESIDUAL_SGPTR_parse_table[] = {
+ { "SG_LIST_NULL", 0x01, 0x01 },
+ { "SG_OVERRUN_RESID", 0x02, 0x02 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahd_scb_residual_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_RESIDUAL_SGPTR_parse_table, 3, "SCB_RESIDUAL_SGPTR",
+ 0x184, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_scsi_status_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_SCSI_STATUS",
+ 0x188, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_target_phases_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TARGET_PHASES",
+ 0x189, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_target_data_dir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TARGET_DATA_DIR",
+ 0x18a, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_target_itag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TARGET_ITAG",
+ 0x18b, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_sense_busaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_SENSE_BUSADDR",
+ 0x18c, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TAG",
+ 0x190, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_CONTROL_parse_table[] = {
+ { "SCB_TAG_TYPE", 0x03, 0x03 },
+ { "DISCONNECTED", 0x04, 0x04 },
+ { "STATUS_RCVD", 0x08, 0x08 },
+ { "MK_MESSAGE", 0x10, 0x10 },
+ { "TAG_ENB", 0x20, 0x20 },
+ { "DISCENB", 0x40, 0x40 },
+ { "TARGET_SCB", 0x80, 0x80 }
+};
+
+int
+ahd_scb_control_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_CONTROL_parse_table, 7, "SCB_CONTROL",
+ 0x192, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_SCSIID_parse_table[] = {
+ { "OID", 0x0f, 0x0f },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahd_scb_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_SCSIID_parse_table, 2, "SCB_SCSIID",
+ 0x193, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_LUN_parse_table[] = {
+ { "LID", 0xff, 0xff }
+};
+
+int
+ahd_scb_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_LUN_parse_table, 1, "SCB_LUN",
+ 0x194, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_TASK_ATTRIBUTE_parse_table[] = {
+ { "SCB_XFERLEN_ODD", 0x01, 0x01 }
+};
+
+int
+ahd_scb_task_attribute_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_TASK_ATTRIBUTE_parse_table, 1, "SCB_TASK_ATTRIBUTE",
+ 0x195, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_CDB_LEN_parse_table[] = {
+ { "SCB_CDB_LEN_PTR", 0x80, 0x80 }
+};
+
+int
+ahd_scb_cdb_len_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_CDB_LEN_parse_table, 1, "SCB_CDB_LEN",
+ 0x196, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_task_management_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_TASK_MANAGEMENT",
+ 0x197, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_dataptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_DATAPTR",
+ 0x198, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_DATACNT_parse_table[] = {
+ { "SG_HIGH_ADDR_BITS", 0x7f, 0x7f },
+ { "SG_LAST_SEG", 0x80, 0x80 }
+};
+
+int
+ahd_scb_datacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_DATACNT_parse_table, 2, "SCB_DATACNT",
+ 0x1a0, regvalue, cur_col, wrap));
+}
+
+static ahd_reg_parse_entry_t SCB_SGPTR_parse_table[] = {
+ { "SG_LIST_NULL", 0x01, 0x01 },
+ { "SG_FULL_RESID", 0x02, 0x02 },
+ { "SG_STATUS_VALID", 0x04, 0x04 }
+};
+
+int
+ahd_scb_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(SCB_SGPTR_parse_table, 3, "SCB_SGPTR",
+ 0x1a4, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_busaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_BUSADDR",
+ 0x1a8, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_next_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_NEXT",
+ 0x1ac, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_next2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_NEXT2",
+ 0x1ae, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_spare_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_SPARE",
+ 0x1b0, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_disconnected_lists_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahd_print_register(NULL, 0, "SCB_DISCONNECTED_LISTS",
+ 0x1b8, regvalue, cur_col, wrap));
+}
+
diff --git a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
new file mode 100644
index 000000000000..77c471f934e0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
@@ -0,0 +1,1139 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#94 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $
+ */
+static uint8_t seqprog[] = {
+ 0xff, 0x02, 0x06, 0x78,
+ 0x00, 0xea, 0x50, 0x59,
+ 0x01, 0xea, 0x04, 0x30,
+ 0xff, 0x04, 0x0c, 0x78,
+ 0x19, 0xea, 0x50, 0x59,
+ 0x19, 0xea, 0x04, 0x00,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x60, 0x3a, 0x1a, 0x68,
+ 0x04, 0x47, 0x1b, 0x68,
+ 0xff, 0x21, 0x1b, 0x70,
+ 0x40, 0x4b, 0x92, 0x69,
+ 0x00, 0xe2, 0x54, 0x59,
+ 0x40, 0x4b, 0x92, 0x69,
+ 0x20, 0x4b, 0x82, 0x69,
+ 0xfc, 0x42, 0x24, 0x78,
+ 0x10, 0x40, 0x24, 0x78,
+ 0x00, 0xe2, 0xc4, 0x5d,
+ 0x20, 0x4d, 0x28, 0x78,
+ 0x00, 0xe2, 0xc4, 0x5d,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0x30, 0x60,
+ 0x7f, 0x4a, 0x94, 0x08,
+ 0x00, 0xe2, 0x32, 0x40,
+ 0xc0, 0x4a, 0x94, 0x00,
+ 0x00, 0xe2, 0x3e, 0x58,
+ 0x00, 0xe2, 0x56, 0x58,
+ 0x00, 0xe2, 0x66, 0x58,
+ 0x00, 0xe2, 0x06, 0x40,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x01, 0x52, 0x64, 0x78,
+ 0x02, 0x58, 0x50, 0x31,
+ 0xff, 0xea, 0x10, 0x0b,
+ 0xff, 0x97, 0x4f, 0x78,
+ 0x50, 0x4b, 0x4a, 0x68,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x14, 0xea, 0x50, 0x59,
+ 0x14, 0xea, 0x04, 0x00,
+ 0x08, 0x92, 0x25, 0x03,
+ 0xff, 0x90, 0x3f, 0x68,
+ 0x00, 0xe2, 0x56, 0x5b,
+ 0x00, 0xe2, 0x3e, 0x40,
+ 0x00, 0xea, 0x44, 0x59,
+ 0x01, 0xea, 0x00, 0x30,
+ 0x80, 0xf9, 0x5e, 0x68,
+ 0x00, 0xe2, 0x42, 0x59,
+ 0x11, 0xea, 0x44, 0x59,
+ 0x11, 0xea, 0x00, 0x00,
+ 0x80, 0xf9, 0x42, 0x79,
+ 0xff, 0xea, 0xd4, 0x0d,
+ 0x22, 0xea, 0x44, 0x59,
+ 0x22, 0xea, 0x00, 0x00,
+ 0x10, 0x16, 0x70, 0x78,
+ 0x01, 0x0b, 0xa2, 0x32,
+ 0x10, 0x16, 0x2c, 0x00,
+ 0x18, 0xad, 0x00, 0x79,
+ 0x04, 0xad, 0xca, 0x68,
+ 0x80, 0xad, 0x64, 0x78,
+ 0x10, 0xad, 0x98, 0x78,
+ 0xff, 0x88, 0x83, 0x68,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x02, 0x8c, 0x59, 0x32,
+ 0x02, 0x28, 0x19, 0x33,
+ 0x02, 0xa8, 0x50, 0x36,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x40, 0x3a, 0x64, 0x68,
+ 0x50, 0x4b, 0x64, 0x68,
+ 0x22, 0xea, 0x44, 0x59,
+ 0x22, 0xea, 0x00, 0x00,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x02, 0x8c, 0x59, 0x32,
+ 0x1a, 0xea, 0x50, 0x59,
+ 0x1a, 0xea, 0x04, 0x00,
+ 0xff, 0xea, 0xd4, 0x0d,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x00, 0xe2, 0xa6, 0x58,
+ 0xff, 0xea, 0x56, 0x02,
+ 0x04, 0x7c, 0x78, 0x32,
+ 0x20, 0x16, 0x64, 0x78,
+ 0x04, 0x38, 0x79, 0x32,
+ 0x80, 0x37, 0x6f, 0x16,
+ 0xff, 0x2d, 0xb5, 0x60,
+ 0xff, 0x29, 0xb5, 0x60,
+ 0x40, 0x51, 0xc5, 0x78,
+ 0xff, 0x4f, 0xb5, 0x68,
+ 0xff, 0x4d, 0xc1, 0x19,
+ 0x00, 0x4e, 0xd5, 0x19,
+ 0x00, 0xe2, 0xc4, 0x50,
+ 0x01, 0x4c, 0xc1, 0x31,
+ 0x00, 0x50, 0xd5, 0x19,
+ 0x00, 0xe2, 0xc4, 0x48,
+ 0x80, 0x18, 0x64, 0x78,
+ 0x02, 0x4a, 0x1d, 0x30,
+ 0x10, 0xea, 0x18, 0x00,
+ 0x60, 0x18, 0x30, 0x00,
+ 0x7f, 0x18, 0x30, 0x0c,
+ 0x02, 0xea, 0x02, 0x00,
+ 0xff, 0xea, 0xa0, 0x0a,
+ 0x80, 0x18, 0x30, 0x04,
+ 0x40, 0xad, 0x64, 0x78,
+ 0xe7, 0xad, 0x5a, 0x09,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x01, 0x4e, 0x9d, 0x1a,
+ 0x00, 0x4f, 0x9f, 0x22,
+ 0x01, 0x94, 0x6d, 0x33,
+ 0x01, 0xea, 0x20, 0x33,
+ 0x04, 0xac, 0x49, 0x32,
+ 0xff, 0xea, 0x5a, 0x03,
+ 0xff, 0xea, 0x5e, 0x03,
+ 0x01, 0x10, 0xd4, 0x31,
+ 0x10, 0x92, 0xf5, 0x68,
+ 0x3d, 0x93, 0xc5, 0x29,
+ 0xfe, 0xe2, 0xc4, 0x09,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xec, 0x50, 0x31,
+ 0x02, 0xa0, 0xda, 0x31,
+ 0xff, 0xa9, 0xf4, 0x70,
+ 0x02, 0xa0, 0x58, 0x37,
+ 0xff, 0x21, 0xfd, 0x70,
+ 0x02, 0x22, 0x51, 0x31,
+ 0x02, 0xa0, 0x5c, 0x33,
+ 0x02, 0xa0, 0x44, 0x36,
+ 0x02, 0xa0, 0x40, 0x32,
+ 0x02, 0xa0, 0x44, 0x36,
+ 0x04, 0x47, 0x05, 0x69,
+ 0x40, 0x16, 0x30, 0x69,
+ 0xff, 0x2d, 0x35, 0x61,
+ 0xff, 0x29, 0x65, 0x70,
+ 0x01, 0x37, 0xc1, 0x31,
+ 0x02, 0x28, 0x55, 0x32,
+ 0x01, 0xea, 0x5a, 0x01,
+ 0x04, 0x3c, 0xf9, 0x30,
+ 0x02, 0x28, 0x51, 0x31,
+ 0x01, 0xa8, 0x60, 0x31,
+ 0x00, 0xa9, 0x60, 0x01,
+ 0x01, 0x14, 0xd4, 0x31,
+ 0x01, 0x50, 0xa1, 0x1a,
+ 0xff, 0x4e, 0x9d, 0x1a,
+ 0xff, 0x4f, 0x9f, 0x22,
+ 0xff, 0x8d, 0x29, 0x71,
+ 0x80, 0xac, 0x28, 0x71,
+ 0x20, 0x16, 0x28, 0x69,
+ 0x02, 0x8c, 0x51, 0x31,
+ 0x00, 0xe2, 0x12, 0x41,
+ 0x01, 0xac, 0x08, 0x31,
+ 0x09, 0xea, 0x5a, 0x01,
+ 0x02, 0x8c, 0x51, 0x32,
+ 0xff, 0xea, 0x1a, 0x07,
+ 0x04, 0x24, 0xf9, 0x30,
+ 0x1d, 0xea, 0x3a, 0x41,
+ 0x02, 0x2c, 0x51, 0x31,
+ 0x04, 0xa8, 0xf9, 0x30,
+ 0x19, 0xea, 0x3a, 0x41,
+ 0x06, 0xea, 0x08, 0x81,
+ 0x01, 0xe2, 0x5a, 0x35,
+ 0x02, 0xf2, 0xf0, 0x35,
+ 0x02, 0xf2, 0xf0, 0x31,
+ 0x02, 0xf8, 0xe4, 0x35,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x01, 0xe2, 0x00, 0x30,
+ 0xff, 0xea, 0xb2, 0x0d,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x11, 0x00, 0x00, 0x10,
+ 0xff, 0xea, 0xb2, 0x0d,
+ 0x01, 0xe2, 0x04, 0x30,
+ 0x01, 0xea, 0x04, 0x34,
+ 0x02, 0x20, 0xbd, 0x30,
+ 0x02, 0x20, 0xb9, 0x30,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x4c, 0x93, 0xd7, 0x28,
+ 0x10, 0x92, 0x63, 0x79,
+ 0x01, 0x6b, 0xc0, 0x30,
+ 0x02, 0x64, 0xc8, 0x00,
+ 0x40, 0x3a, 0x74, 0x04,
+ 0x00, 0xe2, 0x56, 0x58,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0x64, 0x61,
+ 0x20, 0x3f, 0x7a, 0x69,
+ 0x10, 0x3f, 0x64, 0x79,
+ 0x02, 0xea, 0x7e, 0x00,
+ 0x00, 0xea, 0x44, 0x59,
+ 0x01, 0xea, 0x00, 0x30,
+ 0x02, 0x48, 0x51, 0x35,
+ 0x01, 0xea, 0x7e, 0x00,
+ 0x11, 0xea, 0x44, 0x59,
+ 0x11, 0xea, 0x00, 0x00,
+ 0x02, 0x48, 0x51, 0x35,
+ 0x08, 0xea, 0x98, 0x00,
+ 0x08, 0x57, 0xae, 0x00,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0xf0, 0x49, 0x68, 0x0a,
+ 0x0f, 0x67, 0xc0, 0x09,
+ 0x00, 0x34, 0x69, 0x02,
+ 0x20, 0xea, 0x96, 0x00,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x40, 0x3a, 0xae, 0x69,
+ 0x02, 0x55, 0x06, 0x68,
+ 0x02, 0x56, 0xae, 0x69,
+ 0xff, 0x5b, 0xae, 0x61,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x80, 0xea, 0xb2, 0x01,
+ 0x44, 0xea, 0x00, 0x00,
+ 0x01, 0x33, 0xc0, 0x31,
+ 0x33, 0xea, 0x00, 0x00,
+ 0xff, 0xea, 0xb2, 0x09,
+ 0xff, 0xe0, 0xc0, 0x19,
+ 0xff, 0xe0, 0xb0, 0x79,
+ 0x02, 0xac, 0x51, 0x31,
+ 0x00, 0xe2, 0xa6, 0x41,
+ 0x02, 0x5e, 0x50, 0x31,
+ 0x02, 0xa8, 0xb8, 0x30,
+ 0x02, 0x5c, 0x50, 0x31,
+ 0xff, 0xad, 0xc1, 0x71,
+ 0x02, 0xac, 0x41, 0x31,
+ 0x02, 0x22, 0x51, 0x31,
+ 0x02, 0xa0, 0x5c, 0x33,
+ 0x02, 0xa0, 0x44, 0x32,
+ 0x00, 0xe2, 0xca, 0x41,
+ 0x10, 0x92, 0xcb, 0x69,
+ 0x3d, 0x93, 0xc9, 0x29,
+ 0x01, 0xe4, 0xc8, 0x01,
+ 0x01, 0xea, 0xca, 0x01,
+ 0xff, 0xea, 0xda, 0x01,
+ 0x02, 0x20, 0x51, 0x31,
+ 0x02, 0xae, 0x41, 0x32,
+ 0xff, 0x21, 0xd3, 0x61,
+ 0xff, 0xea, 0x46, 0x02,
+ 0x02, 0x5c, 0x50, 0x31,
+ 0x40, 0xea, 0x96, 0x00,
+ 0x02, 0x56, 0xcc, 0x6d,
+ 0x01, 0x55, 0xcc, 0x6d,
+ 0x10, 0x92, 0xdf, 0x79,
+ 0x10, 0x40, 0xe8, 0x69,
+ 0x01, 0x56, 0xe8, 0x79,
+ 0xff, 0x97, 0x07, 0x78,
+ 0x13, 0xea, 0x50, 0x59,
+ 0x13, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x06, 0x40,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x08, 0xea, 0x98, 0x00,
+ 0x08, 0x57, 0xae, 0x00,
+ 0x01, 0x93, 0x69, 0x32,
+ 0x01, 0x94, 0x6b, 0x32,
+ 0x40, 0xea, 0x66, 0x02,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0x80, 0xea, 0x62, 0x02,
+ 0x00, 0xe2, 0xb8, 0x5b,
+ 0x01, 0x36, 0xc1, 0x31,
+ 0x9f, 0xe0, 0x4c, 0x7c,
+ 0x80, 0xe0, 0x0c, 0x72,
+ 0xa0, 0xe0, 0x44, 0x72,
+ 0xc0, 0xe0, 0x3a, 0x72,
+ 0xe0, 0xe0, 0x74, 0x72,
+ 0x01, 0xea, 0x50, 0x59,
+ 0x01, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x80, 0x33, 0x13, 0x7a,
+ 0x03, 0xea, 0x50, 0x59,
+ 0x03, 0xea, 0x04, 0x00,
+ 0xee, 0x00, 0x1a, 0x6a,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x02, 0xa8, 0x90, 0x32,
+ 0x00, 0xe2, 0x6a, 0x59,
+ 0xef, 0x96, 0xd5, 0x19,
+ 0x00, 0xe2, 0x2a, 0x52,
+ 0x09, 0x80, 0xe1, 0x30,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x30, 0x42,
+ 0x01, 0x96, 0xd1, 0x30,
+ 0x10, 0x80, 0x89, 0x31,
+ 0x20, 0xea, 0x32, 0x00,
+ 0xbf, 0x33, 0x67, 0x0a,
+ 0x20, 0x19, 0x32, 0x6a,
+ 0x02, 0x4d, 0xf8, 0x69,
+ 0x40, 0x33, 0x67, 0x02,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x80, 0x33, 0xb5, 0x6a,
+ 0x01, 0x44, 0x10, 0x33,
+ 0x08, 0x92, 0x25, 0x03,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x10, 0xea, 0x80, 0x00,
+ 0x01, 0x31, 0xc5, 0x31,
+ 0x80, 0xe2, 0x60, 0x62,
+ 0x10, 0x92, 0x85, 0x6a,
+ 0xc0, 0x94, 0xc5, 0x01,
+ 0x40, 0x92, 0x51, 0x6a,
+ 0xbf, 0xe2, 0xc4, 0x09,
+ 0x20, 0x92, 0x65, 0x7a,
+ 0x01, 0xe2, 0x88, 0x30,
+ 0x00, 0xe2, 0xb8, 0x5b,
+ 0xa0, 0x36, 0x6d, 0x62,
+ 0x23, 0x92, 0x89, 0x08,
+ 0x00, 0xe2, 0xb8, 0x5b,
+ 0xa0, 0x36, 0x6d, 0x62,
+ 0x00, 0xa8, 0x64, 0x42,
+ 0xff, 0xe2, 0x64, 0x62,
+ 0x00, 0xe2, 0x84, 0x42,
+ 0x40, 0xea, 0x98, 0x00,
+ 0x01, 0xe2, 0x88, 0x30,
+ 0x00, 0xe2, 0xb8, 0x5b,
+ 0xa0, 0x36, 0x43, 0x72,
+ 0x40, 0xea, 0x98, 0x00,
+ 0x01, 0x31, 0x89, 0x32,
+ 0x08, 0xea, 0x62, 0x02,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0xe0, 0xea, 0xd4, 0x5b,
+ 0x80, 0xe0, 0xc0, 0x6a,
+ 0x04, 0xe0, 0x66, 0x73,
+ 0x02, 0xe0, 0x96, 0x73,
+ 0x00, 0xea, 0x1e, 0x73,
+ 0x03, 0xe0, 0xa6, 0x73,
+ 0x23, 0xe0, 0x96, 0x72,
+ 0x08, 0xe0, 0xbc, 0x72,
+ 0x00, 0xe2, 0xb8, 0x5b,
+ 0x07, 0xea, 0x50, 0x59,
+ 0x07, 0xea, 0x04, 0x00,
+ 0x08, 0x42, 0xf9, 0x71,
+ 0x04, 0x42, 0x93, 0x62,
+ 0x01, 0x43, 0x89, 0x30,
+ 0x00, 0xe2, 0x84, 0x42,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0x84, 0x42,
+ 0x01, 0x00, 0x60, 0x32,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x4c, 0x34, 0xc1, 0x28,
+ 0x01, 0x64, 0xc0, 0x31,
+ 0x00, 0x30, 0x45, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x01, 0xe0, 0xba, 0x7a,
+ 0xa0, 0xea, 0xca, 0x5b,
+ 0x01, 0xa0, 0xba, 0x62,
+ 0x01, 0x84, 0xaf, 0x7a,
+ 0x01, 0x95, 0xbd, 0x6a,
+ 0x05, 0xea, 0x50, 0x59,
+ 0x05, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xbc, 0x42,
+ 0x03, 0xea, 0x50, 0x59,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xbc, 0x42,
+ 0x07, 0xea, 0xdc, 0x5b,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x3f, 0xe0, 0x6a, 0x0a,
+ 0xc0, 0x34, 0xc1, 0x09,
+ 0x00, 0x35, 0x51, 0x01,
+ 0xff, 0xea, 0x52, 0x09,
+ 0x30, 0x34, 0xc5, 0x09,
+ 0x3d, 0xe2, 0xc4, 0x29,
+ 0xb8, 0xe2, 0xc4, 0x19,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xec, 0x40, 0x31,
+ 0xff, 0xa1, 0xdc, 0x72,
+ 0x02, 0xe8, 0xda, 0x31,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0x00, 0xe2, 0xfe, 0x42,
+ 0x80, 0x33, 0x67, 0x02,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x00, 0xe2, 0xb8, 0x5b,
+ 0x01, 0x33, 0x67, 0x02,
+ 0xe0, 0x36, 0x19, 0x63,
+ 0x02, 0x33, 0x67, 0x02,
+ 0x20, 0x46, 0x12, 0x63,
+ 0xff, 0xea, 0x52, 0x09,
+ 0xa8, 0xea, 0xca, 0x5b,
+ 0x04, 0x92, 0xf9, 0x7a,
+ 0x01, 0x34, 0xc1, 0x31,
+ 0x00, 0x93, 0xf9, 0x62,
+ 0x01, 0x35, 0xc1, 0x31,
+ 0x00, 0x94, 0x03, 0x73,
+ 0x01, 0xa9, 0x52, 0x11,
+ 0xff, 0xa9, 0xee, 0x6a,
+ 0x00, 0xe2, 0x12, 0x43,
+ 0x10, 0x33, 0x67, 0x02,
+ 0x04, 0x92, 0x13, 0x7b,
+ 0xfb, 0x92, 0x25, 0x0b,
+ 0xff, 0xea, 0x66, 0x0a,
+ 0x01, 0xa4, 0x0d, 0x6b,
+ 0x02, 0xa8, 0x90, 0x32,
+ 0x00, 0xe2, 0x6a, 0x59,
+ 0x10, 0x92, 0xbd, 0x7a,
+ 0xff, 0xea, 0xdc, 0x5b,
+ 0x00, 0xe2, 0xbc, 0x42,
+ 0x04, 0xea, 0x50, 0x59,
+ 0x04, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xbc, 0x42,
+ 0x04, 0xea, 0x50, 0x59,
+ 0x04, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x08, 0x92, 0xb5, 0x7a,
+ 0xc0, 0x33, 0x29, 0x7b,
+ 0x80, 0x33, 0xb5, 0x6a,
+ 0xff, 0x88, 0x29, 0x6b,
+ 0x40, 0x33, 0xb5, 0x6a,
+ 0x10, 0x92, 0x2f, 0x7b,
+ 0x0a, 0xea, 0x50, 0x59,
+ 0x0a, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x4e, 0x5b,
+ 0x00, 0xe2, 0x82, 0x43,
+ 0x50, 0x4b, 0x36, 0x6b,
+ 0xbf, 0x3a, 0x74, 0x08,
+ 0x01, 0xe0, 0xf4, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x01, 0x2e, 0x5d, 0x1a,
+ 0x00, 0x2f, 0x5f, 0x22,
+ 0x04, 0x47, 0x8f, 0x02,
+ 0x01, 0xfa, 0xc0, 0x35,
+ 0x02, 0xa8, 0x84, 0x32,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x02, 0x42, 0x51, 0x31,
+ 0xff, 0x90, 0x65, 0x68,
+ 0xff, 0x88, 0x5b, 0x6b,
+ 0x01, 0xa4, 0x57, 0x6b,
+ 0x02, 0xa4, 0x5f, 0x6b,
+ 0x01, 0x84, 0x5f, 0x7b,
+ 0x02, 0x28, 0x19, 0x33,
+ 0x02, 0xa8, 0x50, 0x36,
+ 0xff, 0x88, 0x5f, 0x73,
+ 0x00, 0xe2, 0x32, 0x5b,
+ 0x02, 0xa8, 0x20, 0x33,
+ 0x02, 0x2c, 0x19, 0x33,
+ 0x02, 0xa8, 0x58, 0x32,
+ 0x04, 0xa4, 0x49, 0x07,
+ 0xc0, 0x33, 0xb5, 0x6a,
+ 0x04, 0x92, 0x25, 0x03,
+ 0x20, 0x92, 0x83, 0x6b,
+ 0x02, 0xa8, 0x40, 0x31,
+ 0xc0, 0x34, 0xc1, 0x09,
+ 0x00, 0x35, 0x51, 0x01,
+ 0xff, 0xea, 0x52, 0x09,
+ 0x30, 0x34, 0xc5, 0x09,
+ 0x3d, 0xe2, 0xc4, 0x29,
+ 0xb8, 0xe2, 0xc4, 0x19,
+ 0x01, 0xea, 0xc6, 0x01,
+ 0x02, 0xe2, 0xc8, 0x31,
+ 0x02, 0xa0, 0xda, 0x31,
+ 0x02, 0xa0, 0x50, 0x31,
+ 0xf7, 0x57, 0xae, 0x08,
+ 0x08, 0xea, 0x98, 0x00,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0xee, 0x00, 0x8c, 0x6b,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x00, 0xe2, 0xb4, 0x5b,
+ 0x09, 0x4c, 0x8e, 0x7b,
+ 0x08, 0x4c, 0x06, 0x68,
+ 0x0b, 0xea, 0x50, 0x59,
+ 0x0b, 0xea, 0x04, 0x00,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0x20, 0x33, 0xf9, 0x79,
+ 0x00, 0xe2, 0x9e, 0x5b,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x01, 0x84, 0xa3, 0x7b,
+ 0x01, 0xa4, 0x49, 0x07,
+ 0x08, 0x60, 0x30, 0x33,
+ 0x08, 0x80, 0x41, 0x37,
+ 0xdf, 0x33, 0x67, 0x0a,
+ 0xee, 0x00, 0xb0, 0x6b,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x00, 0xe2, 0x6a, 0x59,
+ 0x00, 0xe2, 0xbc, 0x42,
+ 0x01, 0xea, 0x6c, 0x02,
+ 0xc0, 0xea, 0x66, 0x06,
+ 0xff, 0x42, 0xc4, 0x6b,
+ 0x01, 0x41, 0xb8, 0x6b,
+ 0x02, 0x41, 0xb8, 0x7b,
+ 0xff, 0x42, 0xc4, 0x6b,
+ 0x01, 0x41, 0xb8, 0x6b,
+ 0x02, 0x41, 0xb8, 0x7b,
+ 0xff, 0x42, 0xc4, 0x7b,
+ 0x04, 0x4c, 0xb8, 0x6b,
+ 0xe0, 0x41, 0x6c, 0x0e,
+ 0x01, 0x44, 0xd4, 0x31,
+ 0xff, 0x42, 0xcc, 0x7b,
+ 0x04, 0x4c, 0xcc, 0x6b,
+ 0xe0, 0x41, 0x6c, 0x0a,
+ 0xe0, 0x36, 0xf9, 0x61,
+ 0xff, 0xea, 0xca, 0x09,
+ 0x01, 0xe2, 0xc8, 0x31,
+ 0x01, 0x46, 0xda, 0x35,
+ 0x01, 0x44, 0xd4, 0x35,
+ 0x10, 0xea, 0x80, 0x00,
+ 0x01, 0xe2, 0x62, 0x36,
+ 0x04, 0xa6, 0xe4, 0x7b,
+ 0xff, 0xea, 0x5a, 0x09,
+ 0xff, 0xea, 0x4c, 0x0d,
+ 0x01, 0xa6, 0x02, 0x6c,
+ 0x10, 0xad, 0x64, 0x78,
+ 0x80, 0xad, 0xfa, 0x6b,
+ 0x08, 0xad, 0x64, 0x68,
+ 0x04, 0x84, 0xf9, 0x30,
+ 0x00, 0xea, 0x08, 0x81,
+ 0xff, 0xea, 0xd4, 0x09,
+ 0x02, 0x84, 0xf9, 0x88,
+ 0x0d, 0xea, 0x5a, 0x01,
+ 0x04, 0xa6, 0x4c, 0x05,
+ 0x04, 0xa6, 0x64, 0x78,
+ 0xff, 0xea, 0x5a, 0x09,
+ 0x03, 0x84, 0x59, 0x89,
+ 0x03, 0xea, 0x4c, 0x01,
+ 0x80, 0x1a, 0x64, 0x78,
+ 0x08, 0x19, 0x64, 0x78,
+ 0x08, 0xb0, 0xe0, 0x30,
+ 0x04, 0xb0, 0xe0, 0x30,
+ 0x03, 0xb0, 0xf0, 0x30,
+ 0x01, 0xb0, 0x06, 0x33,
+ 0x7f, 0x83, 0xe9, 0x08,
+ 0x04, 0xac, 0x58, 0x19,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x04, 0x84, 0x09, 0x9b,
+ 0x00, 0x85, 0x0b, 0x23,
+ 0x00, 0x86, 0x0d, 0x23,
+ 0x00, 0x87, 0x0f, 0x23,
+ 0x01, 0x84, 0xc5, 0x31,
+ 0x80, 0x83, 0x25, 0x7c,
+ 0x02, 0xe2, 0xc4, 0x01,
+ 0xff, 0xea, 0x4c, 0x09,
+ 0x01, 0xe2, 0x36, 0x30,
+ 0xc8, 0x19, 0x32, 0x00,
+ 0x88, 0x19, 0x32, 0x00,
+ 0x01, 0xac, 0xd4, 0x99,
+ 0x00, 0xe2, 0x64, 0x50,
+ 0xfe, 0xa6, 0x4c, 0x0d,
+ 0x0b, 0x98, 0xe1, 0x30,
+ 0xfd, 0xa4, 0x49, 0x09,
+ 0x80, 0xa3, 0x39, 0x7c,
+ 0x02, 0xa4, 0x48, 0x01,
+ 0x01, 0xa4, 0x36, 0x30,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0xfd, 0xa4, 0x49, 0x0b,
+ 0x05, 0xa3, 0x07, 0x33,
+ 0x80, 0x83, 0x45, 0x6c,
+ 0x02, 0xea, 0x4c, 0x05,
+ 0xff, 0xea, 0x4c, 0x0d,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x02, 0xa6, 0xe6, 0x6b,
+ 0x80, 0xf9, 0xf2, 0x05,
+ 0xc0, 0x33, 0x53, 0x7c,
+ 0x03, 0xea, 0x50, 0x59,
+ 0x03, 0xea, 0x04, 0x00,
+ 0x20, 0x33, 0x77, 0x7c,
+ 0x01, 0x84, 0x5d, 0x6c,
+ 0x06, 0xea, 0x50, 0x59,
+ 0x06, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x7a, 0x44,
+ 0x01, 0x00, 0x60, 0x32,
+ 0xee, 0x00, 0x66, 0x6c,
+ 0x05, 0xea, 0xb4, 0x00,
+ 0x33, 0xea, 0x44, 0x59,
+ 0x33, 0xea, 0x00, 0x00,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0xfc, 0x42, 0x68, 0x7c,
+ 0x7f, 0x3d, 0x7a, 0x08,
+ 0x00, 0x30, 0x45, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x09, 0xea, 0x50, 0x59,
+ 0x09, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x01, 0xa4, 0x5d, 0x6c,
+ 0x00, 0xe2, 0x30, 0x5c,
+ 0x20, 0x33, 0x67, 0x02,
+ 0x01, 0x00, 0x60, 0x32,
+ 0x02, 0xa6, 0x82, 0x7c,
+ 0x00, 0xe2, 0x46, 0x5c,
+ 0x00, 0xe2, 0x56, 0x58,
+ 0x00, 0xe2, 0x66, 0x58,
+ 0x00, 0xe2, 0x3a, 0x58,
+ 0x00, 0x30, 0x45, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x20, 0x19, 0x82, 0x6c,
+ 0x00, 0xe2, 0xb2, 0x5c,
+ 0x04, 0x19, 0x9c, 0x6c,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x84, 0x9d, 0x7c,
+ 0x01, 0x1b, 0x96, 0x7c,
+ 0x01, 0x1a, 0x9c, 0x6c,
+ 0x00, 0xe2, 0x4c, 0x44,
+ 0x80, 0x4b, 0xa2, 0x6c,
+ 0x01, 0x4c, 0x9e, 0x7c,
+ 0x03, 0x42, 0x4c, 0x6c,
+ 0x00, 0xe2, 0xe0, 0x5b,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x04, 0x33, 0xf9, 0x79,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x08, 0x5d, 0xba, 0x6c,
+ 0x00, 0xe2, 0x56, 0x58,
+ 0x00, 0x30, 0x45, 0x59,
+ 0x01, 0x30, 0x01, 0x30,
+ 0x02, 0x1b, 0xaa, 0x7c,
+ 0x08, 0x5d, 0xb8, 0x7c,
+ 0x03, 0x68, 0x00, 0x37,
+ 0x01, 0x84, 0x09, 0x07,
+ 0x80, 0x1b, 0xc4, 0x7c,
+ 0x80, 0x84, 0xc5, 0x6c,
+ 0xff, 0x85, 0x0b, 0x1b,
+ 0xff, 0x86, 0x0d, 0x23,
+ 0xff, 0x87, 0x0f, 0x23,
+ 0xf8, 0x1b, 0x08, 0x0b,
+ 0xff, 0xea, 0x06, 0x0b,
+ 0x03, 0x68, 0x00, 0x37,
+ 0x00, 0xe2, 0xc4, 0x58,
+ 0x10, 0xea, 0x18, 0x00,
+ 0xf9, 0xd9, 0xb2, 0x0d,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x01, 0x52, 0x48, 0x31,
+ 0x20, 0xa4, 0xee, 0x7c,
+ 0x20, 0x5b, 0xee, 0x7c,
+ 0x80, 0xf9, 0xfc, 0x7c,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x11, 0x00, 0x00, 0x10,
+ 0x04, 0x19, 0x08, 0x7d,
+ 0xdf, 0x19, 0x32, 0x08,
+ 0x60, 0x5b, 0xe6, 0x6c,
+ 0x01, 0x4c, 0xe2, 0x7c,
+ 0x20, 0x19, 0x32, 0x00,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x10, 0x5b, 0x00, 0x6d,
+ 0x08, 0x5b, 0x0a, 0x6d,
+ 0x20, 0x5b, 0xfa, 0x6c,
+ 0x02, 0x5b, 0x2a, 0x6d,
+ 0x0e, 0xea, 0x50, 0x59,
+ 0x0e, 0xea, 0x04, 0x00,
+ 0x80, 0xf9, 0xea, 0x6c,
+ 0xdf, 0x5c, 0xb8, 0x08,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x01, 0xa4, 0xe5, 0x6d,
+ 0x00, 0xe2, 0x30, 0x5c,
+ 0x00, 0xe2, 0x34, 0x5d,
+ 0x01, 0x90, 0x21, 0x1b,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x00, 0xe2, 0x32, 0x5b,
+ 0xf3, 0x96, 0xd5, 0x19,
+ 0x00, 0xe2, 0x18, 0x55,
+ 0x80, 0x96, 0x19, 0x6d,
+ 0x0f, 0xea, 0x50, 0x59,
+ 0x0f, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x20, 0x45,
+ 0x04, 0x8c, 0xe1, 0x30,
+ 0x01, 0xea, 0xf2, 0x00,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0xff, 0x97, 0x27, 0x7d,
+ 0x14, 0xea, 0x50, 0x59,
+ 0x14, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x96, 0x5d,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x09, 0x80, 0xe1, 0x30,
+ 0x02, 0xea, 0x36, 0x00,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x8e, 0x5d,
+ 0x01, 0xd9, 0xb2, 0x05,
+ 0x02, 0xa6, 0x44, 0x7d,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x20, 0x5b, 0x52, 0x6d,
+ 0xfc, 0x42, 0x3e, 0x7d,
+ 0x10, 0x40, 0x40, 0x6d,
+ 0x20, 0x4d, 0x42, 0x7d,
+ 0x08, 0x5d, 0x52, 0x6d,
+ 0x02, 0xa6, 0xe6, 0x6b,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x20, 0x5b, 0x52, 0x6d,
+ 0x01, 0x1b, 0x72, 0x6d,
+ 0xfc, 0x42, 0x4e, 0x7d,
+ 0x10, 0x40, 0x50, 0x6d,
+ 0x20, 0x4d, 0x64, 0x78,
+ 0x08, 0x5d, 0x64, 0x78,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x5b, 0x40, 0x31,
+ 0x00, 0xe2, 0xb2, 0x5c,
+ 0x00, 0xe2, 0x9e, 0x5b,
+ 0x20, 0xea, 0xb6, 0x00,
+ 0x00, 0xe2, 0xe0, 0x5b,
+ 0x20, 0x5c, 0xb8, 0x00,
+ 0x04, 0x19, 0x68, 0x6d,
+ 0x01, 0x1a, 0x68, 0x6d,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x01, 0x1a, 0x64, 0x78,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x20, 0xa0, 0xcc, 0x7d,
+ 0xff, 0x90, 0x21, 0x1b,
+ 0x08, 0x92, 0x43, 0x6b,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x01, 0xa4, 0x49, 0x03,
+ 0x40, 0x5b, 0x82, 0x6d,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x40, 0x5b, 0x82, 0x6d,
+ 0x04, 0x5d, 0xe6, 0x7d,
+ 0x01, 0x1a, 0xe6, 0x7d,
+ 0x20, 0x4d, 0x64, 0x78,
+ 0x40, 0x5b, 0xcc, 0x7d,
+ 0x04, 0x5d, 0xe6, 0x7d,
+ 0x01, 0x1a, 0xe6, 0x7d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0xff, 0x90, 0x21, 0x1b,
+ 0x08, 0x92, 0x43, 0x6b,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x01, 0x1b, 0x64, 0x78,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x01, 0x1b, 0xaa, 0x6d,
+ 0x40, 0x5b, 0xb8, 0x7d,
+ 0x01, 0x1b, 0xaa, 0x6d,
+ 0x02, 0x19, 0x32, 0x00,
+ 0x01, 0x1a, 0x64, 0x78,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0xff, 0xea, 0x10, 0x03,
+ 0x08, 0x92, 0x25, 0x03,
+ 0x00, 0xe2, 0x42, 0x43,
+ 0x01, 0x1a, 0xb4, 0x7d,
+ 0x40, 0x5b, 0xb0, 0x7d,
+ 0x01, 0x1a, 0x9e, 0x6d,
+ 0xfc, 0x42, 0x64, 0x78,
+ 0x01, 0x1a, 0xb8, 0x6d,
+ 0x10, 0xea, 0x50, 0x59,
+ 0x10, 0xea, 0x04, 0x00,
+ 0xfc, 0x42, 0x64, 0x78,
+ 0x10, 0x40, 0xbe, 0x6d,
+ 0x20, 0x4d, 0x64, 0x78,
+ 0x40, 0x5b, 0x9e, 0x6d,
+ 0x01, 0x1a, 0x64, 0x78,
+ 0x01, 0x90, 0x21, 0x1b,
+ 0x30, 0x3f, 0xc0, 0x09,
+ 0x30, 0xe0, 0x64, 0x60,
+ 0x40, 0x4b, 0x64, 0x68,
+ 0xff, 0xea, 0x52, 0x01,
+ 0xee, 0x00, 0xd2, 0x6d,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0xff, 0x90, 0x21, 0x1b,
+ 0x02, 0xea, 0xb4, 0x00,
+ 0x20, 0xea, 0x9a, 0x00,
+ 0xf3, 0x42, 0xde, 0x6d,
+ 0x12, 0xea, 0x50, 0x59,
+ 0x12, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x0d, 0xea, 0x50, 0x59,
+ 0x0d, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0xf8, 0x41,
+ 0x01, 0x90, 0x21, 0x1b,
+ 0x11, 0xea, 0x50, 0x59,
+ 0x11, 0xea, 0x04, 0x00,
+ 0x00, 0xe2, 0x32, 0x5b,
+ 0x08, 0x5a, 0xb4, 0x00,
+ 0x00, 0xe2, 0x0c, 0x5e,
+ 0xa8, 0xea, 0x32, 0x00,
+ 0x00, 0xe2, 0x3e, 0x59,
+ 0x80, 0x1a, 0xfa, 0x7d,
+ 0x00, 0xe2, 0x0c, 0x5e,
+ 0x80, 0x19, 0x32, 0x00,
+ 0x40, 0x5b, 0x00, 0x6e,
+ 0x08, 0x5a, 0x00, 0x7e,
+ 0x20, 0x4d, 0x64, 0x78,
+ 0x02, 0x84, 0x09, 0x03,
+ 0x40, 0x5b, 0xcc, 0x7d,
+ 0xff, 0x90, 0x21, 0x1b,
+ 0x80, 0xf9, 0xf2, 0x01,
+ 0x08, 0x92, 0x43, 0x6b,
+ 0x02, 0xea, 0xb4, 0x04,
+ 0x01, 0x38, 0xe1, 0x30,
+ 0x05, 0x39, 0xe3, 0x98,
+ 0x01, 0xe0, 0xf4, 0x31,
+ 0xff, 0xea, 0xc0, 0x09,
+ 0x00, 0x3a, 0xe5, 0x20,
+ 0x00, 0x3b, 0xe7, 0x20,
+ 0x01, 0xfa, 0xc0, 0x31,
+ 0x04, 0xea, 0xe8, 0x30,
+ 0xff, 0xea, 0xf0, 0x08,
+ 0x02, 0xea, 0xf2, 0x00,
+ 0xff, 0xea, 0xf4, 0x0c
+};
+
+typedef int ahd_patch_func_t (struct ahd_softc *ahd);
+static ahd_patch_func_t ahd_patch22_func;
+
+static int
+ahd_patch22_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch21_func;
+
+static int
+ahd_patch21_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch20_func;
+
+static int
+ahd_patch20_func(struct ahd_softc *ahd)
+{
+ return ((ahd->features & AHD_RTI) == 0);
+}
+
+static ahd_patch_func_t ahd_patch19_func;
+
+static int
+ahd_patch19_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_INITIATORROLE) != 0);
+}
+
+static ahd_patch_func_t ahd_patch18_func;
+
+static int
+ahd_patch18_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_TARGETROLE) != 0);
+}
+
+static ahd_patch_func_t ahd_patch17_func;
+
+static int
+ahd_patch17_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch16_func;
+
+static int
+ahd_patch16_func(struct ahd_softc *ahd)
+{
+ return ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0);
+}
+
+static ahd_patch_func_t ahd_patch15_func;
+
+static int
+ahd_patch15_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_39BIT_ADDRESSING) != 0);
+}
+
+static ahd_patch_func_t ahd_patch14_func;
+
+static int
+ahd_patch14_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_64BIT_ADDRESSING) != 0);
+}
+
+static ahd_patch_func_t ahd_patch13_func;
+
+static int
+ahd_patch13_func(struct ahd_softc *ahd)
+{
+ return ((ahd->features & AHD_NEW_DFCNTRL_OPTS) == 0);
+}
+
+static ahd_patch_func_t ahd_patch12_func;
+
+static int
+ahd_patch12_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch11_func;
+
+static int
+ahd_patch11_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch10_func;
+
+static int
+ahd_patch10_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch9_func;
+
+static int
+ahd_patch9_func(struct ahd_softc *ahd)
+{
+ return ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch8_func;
+
+static int
+ahd_patch8_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch7_func;
+
+static int
+ahd_patch7_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch6_func;
+
+static int
+ahd_patch6_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch5_func;
+
+static int
+ahd_patch5_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch4_func;
+
+static int
+ahd_patch4_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_PKT_LUN_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch3_func;
+
+static int
+ahd_patch3_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_FAINT_LED_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch2_func;
+
+static int
+ahd_patch2_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_SET_MODE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch1_func;
+
+static int
+ahd_patch1_func(struct ahd_softc *ahd)
+{
+ return ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch0_func;
+
+static int
+ahd_patch0_func(struct ahd_softc *ahd)
+{
+ return (0);
+}
+
+static struct patch {
+ ahd_patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {
+ { ahd_patch1_func, 0, 3, 3 },
+ { ahd_patch1_func, 1, 1, 2 },
+ { ahd_patch0_func, 2, 1, 1 },
+ { ahd_patch1_func, 3, 3, 3 },
+ { ahd_patch1_func, 4, 1, 2 },
+ { ahd_patch0_func, 5, 1, 1 },
+ { ahd_patch2_func, 6, 1, 2 },
+ { ahd_patch0_func, 7, 1, 1 },
+ { ahd_patch3_func, 20, 5, 1 },
+ { ahd_patch2_func, 29, 1, 2 },
+ { ahd_patch0_func, 30, 1, 1 },
+ { ahd_patch1_func, 37, 1, 2 },
+ { ahd_patch0_func, 38, 1, 1 },
+ { ahd_patch2_func, 43, 1, 2 },
+ { ahd_patch0_func, 44, 1, 1 },
+ { ahd_patch2_func, 47, 1, 2 },
+ { ahd_patch0_func, 48, 1, 1 },
+ { ahd_patch2_func, 51, 1, 2 },
+ { ahd_patch0_func, 52, 1, 1 },
+ { ahd_patch2_func, 65, 1, 2 },
+ { ahd_patch0_func, 66, 1, 1 },
+ { ahd_patch2_func, 69, 1, 2 },
+ { ahd_patch0_func, 70, 1, 1 },
+ { ahd_patch1_func, 73, 1, 2 },
+ { ahd_patch0_func, 74, 1, 1 },
+ { ahd_patch4_func, 107, 1, 1 },
+ { ahd_patch2_func, 162, 6, 1 },
+ { ahd_patch1_func, 168, 2, 1 },
+ { ahd_patch5_func, 170, 1, 1 },
+ { ahd_patch2_func, 179, 1, 2 },
+ { ahd_patch0_func, 180, 1, 1 },
+ { ahd_patch6_func, 181, 2, 2 },
+ { ahd_patch0_func, 183, 6, 3 },
+ { ahd_patch2_func, 186, 1, 2 },
+ { ahd_patch0_func, 187, 1, 1 },
+ { ahd_patch2_func, 190, 1, 2 },
+ { ahd_patch0_func, 191, 1, 1 },
+ { ahd_patch7_func, 193, 2, 1 },
+ { ahd_patch5_func, 201, 16, 2 },
+ { ahd_patch0_func, 217, 1, 1 },
+ { ahd_patch8_func, 237, 2, 1 },
+ { ahd_patch1_func, 241, 1, 2 },
+ { ahd_patch0_func, 242, 1, 1 },
+ { ahd_patch7_func, 245, 2, 1 },
+ { ahd_patch1_func, 259, 1, 2 },
+ { ahd_patch0_func, 260, 1, 1 },
+ { ahd_patch1_func, 263, 1, 2 },
+ { ahd_patch0_func, 264, 1, 1 },
+ { ahd_patch2_func, 267, 1, 2 },
+ { ahd_patch0_func, 268, 1, 1 },
+ { ahd_patch1_func, 323, 1, 2 },
+ { ahd_patch0_func, 324, 1, 1 },
+ { ahd_patch2_func, 332, 1, 2 },
+ { ahd_patch0_func, 333, 1, 1 },
+ { ahd_patch2_func, 336, 1, 2 },
+ { ahd_patch0_func, 337, 1, 1 },
+ { ahd_patch1_func, 343, 1, 2 },
+ { ahd_patch0_func, 344, 1, 1 },
+ { ahd_patch1_func, 346, 1, 2 },
+ { ahd_patch0_func, 347, 1, 1 },
+ { ahd_patch9_func, 366, 1, 1 },
+ { ahd_patch9_func, 369, 1, 1 },
+ { ahd_patch9_func, 371, 1, 1 },
+ { ahd_patch9_func, 383, 1, 1 },
+ { ahd_patch1_func, 393, 1, 2 },
+ { ahd_patch0_func, 394, 1, 1 },
+ { ahd_patch1_func, 396, 1, 2 },
+ { ahd_patch0_func, 397, 1, 1 },
+ { ahd_patch1_func, 405, 1, 2 },
+ { ahd_patch0_func, 406, 1, 1 },
+ { ahd_patch2_func, 419, 1, 2 },
+ { ahd_patch0_func, 420, 1, 1 },
+ { ahd_patch10_func, 450, 1, 1 },
+ { ahd_patch1_func, 457, 1, 2 },
+ { ahd_patch0_func, 458, 1, 1 },
+ { ahd_patch2_func, 470, 1, 2 },
+ { ahd_patch0_func, 471, 1, 1 },
+ { ahd_patch11_func, 476, 6, 2 },
+ { ahd_patch0_func, 482, 1, 1 },
+ { ahd_patch12_func, 505, 1, 1 },
+ { ahd_patch13_func, 514, 1, 1 },
+ { ahd_patch14_func, 515, 1, 2 },
+ { ahd_patch0_func, 516, 1, 1 },
+ { ahd_patch15_func, 519, 1, 1 },
+ { ahd_patch14_func, 520, 1, 1 },
+ { ahd_patch16_func, 531, 1, 2 },
+ { ahd_patch0_func, 532, 1, 1 },
+ { ahd_patch1_func, 551, 1, 2 },
+ { ahd_patch0_func, 552, 1, 1 },
+ { ahd_patch1_func, 555, 1, 2 },
+ { ahd_patch0_func, 556, 1, 1 },
+ { ahd_patch2_func, 561, 1, 2 },
+ { ahd_patch0_func, 562, 1, 1 },
+ { ahd_patch2_func, 566, 1, 2 },
+ { ahd_patch0_func, 567, 1, 1 },
+ { ahd_patch1_func, 568, 1, 2 },
+ { ahd_patch0_func, 569, 1, 1 },
+ { ahd_patch2_func, 580, 1, 2 },
+ { ahd_patch0_func, 581, 1, 1 },
+ { ahd_patch17_func, 585, 1, 1 },
+ { ahd_patch18_func, 590, 1, 1 },
+ { ahd_patch19_func, 591, 2, 1 },
+ { ahd_patch18_func, 595, 1, 2 },
+ { ahd_patch0_func, 596, 1, 1 },
+ { ahd_patch2_func, 599, 1, 2 },
+ { ahd_patch0_func, 600, 1, 1 },
+ { ahd_patch2_func, 615, 1, 2 },
+ { ahd_patch0_func, 616, 1, 1 },
+ { ahd_patch20_func, 617, 14, 1 },
+ { ahd_patch1_func, 635, 1, 2 },
+ { ahd_patch0_func, 636, 1, 1 },
+ { ahd_patch20_func, 637, 1, 1 },
+ { ahd_patch1_func, 649, 1, 2 },
+ { ahd_patch0_func, 650, 1, 1 },
+ { ahd_patch1_func, 657, 1, 2 },
+ { ahd_patch0_func, 658, 1, 1 },
+ { ahd_patch17_func, 681, 1, 1 },
+ { ahd_patch17_func, 719, 1, 1 },
+ { ahd_patch1_func, 730, 1, 2 },
+ { ahd_patch0_func, 731, 1, 1 },
+ { ahd_patch1_func, 748, 1, 2 },
+ { ahd_patch0_func, 749, 1, 1 },
+ { ahd_patch1_func, 751, 1, 2 },
+ { ahd_patch0_func, 752, 1, 1 },
+ { ahd_patch1_func, 755, 1, 2 },
+ { ahd_patch0_func, 756, 1, 1 },
+ { ahd_patch21_func, 758, 1, 2 },
+ { ahd_patch0_func, 759, 2, 1 },
+ { ahd_patch22_func, 762, 4, 2 },
+ { ahd_patch0_func, 766, 1, 1 },
+ { ahd_patch22_func, 774, 11, 1 }
+};
+
+static struct cs {
+ uint16_t begin;
+ uint16_t end;
+} critical_sections[] = {
+ { 11, 12 },
+ { 13, 14 },
+ { 29, 42 },
+ { 56, 59 },
+ { 101, 128 },
+ { 129, 157 },
+ { 159, 162 },
+ { 170, 178 },
+ { 201, 250 },
+ { 681, 697 },
+ { 697, 711 },
+ { 721, 725 }
+};
+
+static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);
diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h
new file mode 100644
index 000000000000..8ff16fd8ed49
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.h
@@ -0,0 +1,1352 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#79 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC7XXX_H_
+#define _AIC7XXX_H_
+
+/* Register Definitions */
+#include "aic7xxx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_platform_data;
+struct scb_platform_data;
+struct seeprom_descriptor;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD (~0)
+
+#define SCSIID_TARGET(ahc, scsiid) \
+ (((scsiid) & ((((ahc)->features & AHC_TWIN) != 0) ? TWIN_TID : TID)) \
+ >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+ ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahc, scsiid) \
+ ((((ahc)->features & AHC_TWIN) != 0) \
+ ? ((((scsiid) & TWIN_CHNLB) != 0) ? 'B' : 'A') \
+ : 'A')
+#define SCB_IS_SCSIBUS_B(ahc, scb) \
+ (SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid) == 'B')
+#define SCB_GET_OUR_ID(scb) \
+ SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define SCB_GET_TARGET(ahc, scb) \
+ SCSIID_TARGET((ahc), (scb)->hscb->scsiid)
+#define SCB_GET_CHANNEL(ahc, scb) \
+ SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid)
+#define SCB_GET_LUN(scb) \
+ ((scb)->hscb->lun & LID)
+#define SCB_GET_TARGET_OFFSET(ahc, scb) \
+ (SCB_GET_TARGET(ahc, scb) + (SCB_IS_SCSIBUS_B(ahc, scb) ? 8 : 0))
+#define SCB_GET_TARGET_MASK(ahc, scb) \
+ (0x01 << (SCB_GET_TARGET_OFFSET(ahc, scb)))
+#ifdef AHC_DEBUG
+#define SCB_IS_SILENT(scb) \
+ ((ahc_debug & AHC_SHOW_MASKED_ERRORS) == 0 \
+ && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb) \
+ (((scb)->flags & SCB_SILENT) != 0)
+#endif
+#define TCL_TARGET_OFFSET(tcl) \
+ ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+ (tcl & (AHC_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+ ((lun) | (((scsiid) & TID) << 4))
+
+#ifndef AHC_TARGET_MODE
+#undef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHC_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in SPI3.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but it is doubtful this driver will ever support IUTs.
+ */
+#define AHC_NUM_LUNS 64
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHC_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound. Controllers vary in the number
+ * they actually support.
+ */
+#define AHC_SCB_MAX 255
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information. Although
+ * the space for SCBs on the host adapter varies by model, the driver will
+ * page the SCBs between host and controller memory as needed. We are limited
+ * to 253 because:
+ * 1) The 8bit nature of the RISC engine holds us to an 8bit value.
+ * 2) We reserve one value, 255, to represent the invalid element.
+ * 3) Our input queue scheme requires one SCB to always be reserved
+ * in advance of queuing any SCBs. This takes us down to 254.
+ * 4) To handle our output queue correctly on machines that only
+ * support 32bit stores, we must clear the array 4 bytes at a
+ * time. To avoid colliding with a DMA write from the sequencer,
+ * we must be sure that 4 slots are empty when we write to clear
+ * the queue. This reduces us to 253 SCBs: 1 that just completed
+ * and the known three additional empty slots in the queue that
+ * precede it.
+ */
+#define AHC_MAX_QUEUE 253
+
+/*
+ * The maximum amount of SCB storage we allocate in host memory. This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHC_SCB_MAX_ALLOC (AHC_MAX_QUEUE+1)
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHC_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHC_BUSRESET_DELAY 25
+
+/******************* Chip Characteristics/Operating Settings *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+ AHC_NONE = 0x0000,
+ AHC_CHIPID_MASK = 0x00FF,
+ AHC_AIC7770 = 0x0001,
+ AHC_AIC7850 = 0x0002,
+ AHC_AIC7855 = 0x0003,
+ AHC_AIC7859 = 0x0004,
+ AHC_AIC7860 = 0x0005,
+ AHC_AIC7870 = 0x0006,
+ AHC_AIC7880 = 0x0007,
+ AHC_AIC7895 = 0x0008,
+ AHC_AIC7895C = 0x0009,
+ AHC_AIC7890 = 0x000a,
+ AHC_AIC7896 = 0x000b,
+ AHC_AIC7892 = 0x000c,
+ AHC_AIC7899 = 0x000d,
+ AHC_VL = 0x0100, /* Bus type VL */
+ AHC_EISA = 0x0200, /* Bus type EISA */
+ AHC_PCI = 0x0400, /* Bus type PCI */
+ AHC_BUS_MASK = 0x0F00
+} ahc_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+ AHC_FENONE = 0x00000,
+ AHC_ULTRA = 0x00001, /* Supports 20MHz Transfers */
+ AHC_ULTRA2 = 0x00002, /* Supports 40MHz Transfers */
+ AHC_WIDE = 0x00004, /* Wide Channel */
+ AHC_TWIN = 0x00008, /* Twin Channel */
+ AHC_MORE_SRAM = 0x00010, /* 80 bytes instead of 64 */
+ AHC_CMD_CHAN = 0x00020, /* Has a Command DMA Channel */
+ AHC_QUEUE_REGS = 0x00040, /* Has Queue management registers */
+ AHC_SG_PRELOAD = 0x00080, /* Can perform auto-SG preload */
+ AHC_SPIOCAP = 0x00100, /* Has a Serial Port I/O Cap Register */
+ AHC_MULTI_TID = 0x00200, /* Has bitmask of TIDs for select-in */
+ AHC_HS_MAILBOX = 0x00400, /* Has HS_MAILBOX register */
+ AHC_DT = 0x00800, /* Double Transition transfers */
+ AHC_NEW_TERMCTL = 0x01000, /* Newer termination scheme */
+ AHC_MULTI_FUNC = 0x02000, /* Multi-Function Twin Channel Device */
+ AHC_LARGE_SCBS = 0x04000, /* 64byte SCBs */
+ AHC_AUTORATE = 0x08000, /* Automatic update of SCSIRATE/OFFSET*/
+ AHC_AUTOPAUSE = 0x10000, /* Automatic pause on register access */
+ AHC_TARGETMODE = 0x20000, /* Has tested target mode support */
+ AHC_MULTIROLE = 0x40000, /* Space for two roles at a time */
+ AHC_REMOVABLE = 0x80000, /* Hot-Swap supported */
+ AHC_AIC7770_FE = AHC_FENONE,
+ /*
+ * The real 7850 does not support Ultra modes, but there are
+ * several cards that use the generic 7850 PCI ID even though
+ * they are using an Ultra capable chip (7859/7860). We start
+ * out with the AHC_ULTRA feature set and then check the DEVSTATUS
+ * register to determine if the capability is really present.
+ */
+ AHC_AIC7850_FE = AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE|AHC_ULTRA,
+ AHC_AIC7860_FE = AHC_AIC7850_FE,
+ AHC_AIC7870_FE = AHC_TARGETMODE,
+ AHC_AIC7880_FE = AHC_AIC7870_FE|AHC_ULTRA,
+ /*
+ * Although we have space for both the initiator and
+ * target roles on ULTRA2 chips, we currently disable
+ * the initiator role to allow multi-scsi-id target mode
+ * configurations. We can only respond on the same SCSI
+ * ID as our initiator role if we allow initiator operation.
+ * At some point, we should add a configuration knob to
+ * allow both roles to be loaded.
+ */
+ AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2
+ |AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID
+ |AHC_HS_MAILBOX|AHC_NEW_TERMCTL|AHC_LARGE_SCBS
+ |AHC_TARGETMODE,
+ AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_DT|AHC_AUTORATE|AHC_AUTOPAUSE,
+ AHC_AIC7895_FE = AHC_AIC7880_FE|AHC_MORE_SRAM|AHC_AUTOPAUSE
+ |AHC_CMD_CHAN|AHC_MULTI_FUNC|AHC_LARGE_SCBS,
+ AHC_AIC7895C_FE = AHC_AIC7895_FE|AHC_MULTI_TID,
+ AHC_AIC7896_FE = AHC_AIC7890_FE|AHC_MULTI_FUNC,
+ AHC_AIC7899_FE = AHC_AIC7892_FE|AHC_MULTI_FUNC
+} ahc_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+ AHC_BUGNONE = 0x00,
+ /*
+ * On all chips prior to the U2 product line,
+ * the WIDEODD S/G segment feature does not
+ * work during scsi->HostBus transfers.
+ */
+ AHC_TMODE_WIDEODD_BUG = 0x01,
+ /*
+ * On the aic7890/91 Rev 0 chips, the autoflush
+ * feature does not work. A manual flush of
+ * the DMA FIFO is required.
+ */
+ AHC_AUTOFLUSH_BUG = 0x02,
+ /*
+ * On many chips, cacheline streaming does not work.
+ */
+ AHC_CACHETHEN_BUG = 0x04,
+ /*
+ * On the aic7896/97 chips, cacheline
+ * streaming must be enabled.
+ */
+ AHC_CACHETHEN_DIS_BUG = 0x08,
+ /*
+ * PCI 2.1 Retry failure on non-empty data fifo.
+ */
+ AHC_PCI_2_1_RETRY_BUG = 0x10,
+ /*
+ * Controller does not handle cacheline residuals
+ * properly on S/G segments if PCI MWI instructions
+ * are allowed.
+ */
+ AHC_PCI_MWI_BUG = 0x20,
+ /*
+ * An SCB upload using the SCB channel's
+ * auto array entry copy feature may
+ * corrupt data. This appears to only
+ * occur on 66MHz systems.
+ */
+ AHC_SCBCHAN_UPLOAD_BUG = 0x40
+} ahc_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+ AHC_FNONE = 0x000,
+ AHC_PRIMARY_CHANNEL = 0x003, /*
+ * The channel that should
+ * be probed first.
+ */
+ AHC_USEDEFAULTS = 0x004, /*
+ * For cards without an seeprom
+ * or a BIOS to initialize the chip's
+ * SRAM, we use the default target
+ * settings.
+ */
+ AHC_SEQUENCER_DEBUG = 0x008,
+ AHC_SHARED_SRAM = 0x010,
+ AHC_LARGE_SEEPROM = 0x020, /* Uses C56_66 not C46 */
+ AHC_RESET_BUS_A = 0x040,
+ AHC_RESET_BUS_B = 0x080,
+ AHC_EXTENDED_TRANS_A = 0x100,
+ AHC_EXTENDED_TRANS_B = 0x200,
+ AHC_TERM_ENB_A = 0x400,
+ AHC_TERM_ENB_B = 0x800,
+ AHC_INITIATORROLE = 0x1000, /*
+ * Allow initiator operations on
+ * this controller.
+ */
+ AHC_TARGETROLE = 0x2000, /*
+ * Allow target operations on this
+ * controller.
+ */
+ AHC_NEWEEPROM_FMT = 0x4000,
+ AHC_RESOURCE_SHORTAGE = 0x8000,
+ AHC_TQINFIFO_BLOCKED = 0x10000, /* Blocked waiting for ATIOs */
+ AHC_INT50_SPEEDFLEX = 0x20000, /*
+ * Internal 50pin connector
+ * sits behind an aic3860
+ */
+ AHC_SCB_BTT = 0x40000, /*
+ * The busy targets table is
+ * stored in SCB space rather
+ * than SRAM.
+ */
+ AHC_BIOS_ENABLED = 0x80000,
+ AHC_ALL_INTERRUPTS = 0x100000,
+ AHC_PAGESCBS = 0x400000, /* Enable SCB paging */
+ AHC_EDGE_INTERRUPT = 0x800000, /* Device uses edge triggered ints */
+ AHC_39BIT_ADDRESSING = 0x1000000, /* Use 39 bit addressing scheme. */
+ AHC_LSCBS_ENABLED = 0x2000000, /* 64Byte SCBs enabled */
+ AHC_SCB_CONFIG_USED = 0x4000000, /* No SEEPROM but SCB2 had info. */
+ AHC_NO_BIOS_INIT = 0x8000000, /* No BIOS left over settings. */
+ AHC_DISABLE_PCI_PERR = 0x10000000,
+ AHC_HAS_TERM_LOGIC = 0x20000000
+} ahc_flag;
+
+/************************* Hardware SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
+ * consists of a "hardware SCB" mirroring the fields available on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the cdb) to be executed. After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role. See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target. The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally (non-zero status byte).
+ */
+struct status_pkt {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sg_ptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* Standard SCSI status byte */
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sg_ptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/ union {
+ /*
+ * If the cdb is 12 bytes or less, we embed it directly
+ * in the SCB. For longer cdbs, we embed the address
+ * of the cdb payload as seen by the chip and a DMA
+ * is used to pull it in.
+ */
+ uint8_t cdb[12];
+ uint32_t cdb_ptr;
+ struct status_pkt status;
+ struct target_data tdata;
+ } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer. The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag. Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields). If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur. Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields. After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced. After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set. If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete. If the
+ * sequencer determines that there is a residual in the tranfer, it
+ * will set the SG_RESID_VALID flag in sgptr and dma the scb back into
+ * host memory. To sumarize:
+ *
+ * Sequencer:
+ * o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ * or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ * o We are transfering the last segment if residual_datacnt has
+ * the SG_LAST_SEG flag set.
+ *
+ * Host:
+ * o A residual has occurred if a completed scb has the
+ * SG_RESID_VALID flag set.
+ *
+ * o residual_sgptr and sgptr refer to the "next" sg entry
+ * and so may point beyond the last valid sg entry for the
+ * transfer.
+ */
+/*12*/ uint32_t dataptr;
+/*16*/ uint32_t datacnt; /*
+ * Byte 3 (numbered from 0) of
+ * the datacnt is really the
+ * 4th byte in that data address.
+ */
+/*20*/ uint32_t sgptr;
+#define SG_PTR_MASK 0xFFFFFFF8
+/*24*/ uint8_t control; /* See SCB_CONTROL in aic7xxx.reg for details */
+/*25*/ uint8_t scsiid; /* what to load in the SCSIID register */
+/*26*/ uint8_t lun;
+/*27*/ uint8_t tag; /*
+ * Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+/*28*/ uint8_t cdb_len;
+/*29*/ uint8_t scsirate; /* Value for SCSIRATE register */
+/*30*/ uint8_t scsioffset; /* Value for SCSIOFFSET register */
+/*31*/ uint8_t next; /*
+ * Used for threading SCBs in the
+ * "Waiting for Selection" and
+ * "Disconnected SCB" lists down
+ * in the sequencer.
+ */
+/*32*/ uint8_t cdb32[32]; /*
+ * CDB storage for cdbs of size
+ * 13->32. We store them here
+ * because hardware scbs are
+ * allocated from DMA safe
+ * memory so we are guaranteed
+ * the controller can access
+ * this data.
+ */
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent. Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length. We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer. This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahc_dma_seg {
+ uint32_t addr;
+ uint32_t len;
+#define AHC_DMA_LAST_SEG 0x80000000
+#define AHC_SG_HIGH_ADDR_MASK 0x7F000000
+#define AHC_SG_LEN_MASK 0x00FFFFFF
+};
+
+struct sg_map_node {
+ bus_dmamap_t sg_dmamap;
+ dma_addr_t sg_physaddr;
+ struct ahc_dma_seg* sg_vaddr;
+ SLIST_ENTRY(sg_map_node) links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_OTHERTCL_TIMEOUT = 0x0002,/*
+ * Another device was active
+ * during the first timeout for
+ * this SCB so we gave ourselves
+ * an additional timeout period
+ * in case it was hogging the
+ * bus.
+ */
+ SCB_DEVICE_RESET = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_CDB32_PTR = 0x0010,
+ SCB_RECOVERY_SCB = 0x0020,
+ SCB_AUTO_NEGOTIATE = 0x0040,/* Negotiate to achieve goal. */
+ SCB_NEGOTIATE = 0x0080,/* Negotiation forced for command. */
+ SCB_ABORT = 0x0100,
+ SCB_UNTAGGEDQ = 0x0200,
+ SCB_ACTIVE = 0x0400,
+ SCB_TARGET_IMMEDIATE = 0x0800,
+ SCB_TRANSMISSION_ERROR = 0x1000,/*
+ * We detected a parity or CRC
+ * error that has effected the
+ * payload of the command. This
+ * flag is checked when normal
+ * status is returned to catch
+ * the case of a target not
+ * responding to our attempt
+ * to report the error.
+ */
+ SCB_TARGET_SCB = 0x2000,
+ SCB_SILENT = 0x4000 /*
+ * Be quiet about transmission type
+ * errors. They are expected and we
+ * don't want to upset the user. This
+ * flag is typically used during DV.
+ */
+} scb_flag;
+
+struct scb {
+ struct hardware_scb *hscb;
+ union {
+ SLIST_ENTRY(scb) sle;
+ TAILQ_ENTRY(scb) tqe;
+ } links;
+ LIST_ENTRY(scb) pending_links;
+ ahc_io_ctx_t io_ctx;
+ struct ahc_softc *ahc_softc;
+ scb_flag flags;
+#ifndef __linux__
+ bus_dmamap_t dmamap;
+#endif
+ struct scb_platform_data *platform_data;
+ struct sg_map_node *sg_map;
+ struct ahc_dma_seg *sg_list;
+ dma_addr_t sg_list_phys;
+ u_int sg_count;/* How full ahc_dma_seg is */
+};
+
+struct scb_data {
+ SLIST_HEAD(, scb) free_scbs; /*
+ * Pool of SCBs ready to be assigned
+ * commands to execute.
+ */
+ struct scb *scbindex[256]; /*
+ * Mapping from tag to SCB.
+ * As tag identifiers are an
+ * 8bit value, we provide space
+ * for all possible tag values.
+ * Any lookups to entries at or
+ * above AHC_SCB_MAX_ALLOC will
+ * always fail.
+ */
+ struct hardware_scb *hscbs; /* Array of hardware SCBs */
+ struct scb *scbarray; /* Array of kernel SCBs */
+ struct scsi_sense_data *sense; /* Per SCB sense data */
+
+ /*
+ * "Bus" addresses of our data structures.
+ */
+ bus_dma_tag_t hscb_dmat; /* dmat for our hardware SCB array */
+ bus_dmamap_t hscb_dmamap;
+ dma_addr_t hscb_busaddr;
+ bus_dma_tag_t sense_dmat;
+ bus_dmamap_t sense_dmamap;
+ dma_addr_t sense_busaddr;
+ bus_dma_tag_t sg_dmat; /* dmat for our sg segments */
+ SLIST_HEAD(, sg_map_node) sg_maps;
+ uint8_t numscbs;
+ uint8_t maxhscbs; /* Number of SCBs on the card */
+ uint8_t init_level; /*
+ * How far we've initialized
+ * this structure.
+ */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+ uint8_t scsiid; /* Our ID and the initiator's ID */
+ uint8_t identify; /* Identify message */
+ uint8_t bytes[22]; /*
+ * Bytes contains any additional message
+ * bytes terminated by 0xFF. The remainder
+ * is the cdb to execute.
+ */
+ uint8_t cmd_valid; /*
+ * When a command is complete, the firmware
+ * will set cmd_valid to all bits set.
+ * After the host has seen the command,
+ * the bits are cleared. This allows us
+ * to just peek at host memory to determine
+ * if more work is complete. cmd_valid is on
+ * an 8 byte boundary to simplify setting
+ * it on aic7880 hardware which only has
+ * limited direct access to the DMA FIFO.
+ */
+ uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHC_TMODE_EVENT_BUFFER_SIZE 8
+struct ahc_tmode_event {
+ uint8_t initiator_id;
+ uint8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */
+#define EVENT_TYPE_BUS_RESET 0xFF
+ uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it. Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHC_TARGET_MODE
+struct ahc_tmode_lstate {
+ struct cam_path *path;
+ struct ccb_hdr_slist accept_tios;
+ struct ccb_hdr_slist immed_notifies;
+ struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE];
+ uint8_t event_r_idx;
+ uint8_t event_w_idx;
+};
+#else
+struct ahc_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHC_TRANS_CUR 0x01 /* Modify current neogtiation status */
+#define AHC_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */
+#define AHC_TRANS_GOAL 0x04 /* Modify negotiation goal */
+#define AHC_TRANS_USER 0x08 /* Modify user negotiation settings */
+
+#define AHC_WIDTH_UNKNOWN 0xFF
+#define AHC_PERIOD_UNKNOWN 0xFF
+#define AHC_OFFSET_UNKNOWN 0xFF
+#define AHC_PPR_OPTS_UNKNOWN 0xFF
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahc_transinfo {
+ uint8_t protocol_version; /* SCSI Revision level */
+ uint8_t transport_version; /* SPI Revision level */
+ uint8_t width; /* Bus width */
+ uint8_t period; /* Sync rate factor */
+ uint8_t offset; /* Sync offset */
+ uint8_t ppr_options; /* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahc_initiator_tinfo {
+ uint8_t scsirate; /* Computed value for SCSIRATE reg */
+ struct ahc_transinfo curr;
+ struct ahc_transinfo goal;
+ struct ahc_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping. For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahc_tmode_tstate {
+ struct ahc_tmode_lstate* enabled_luns[AHC_NUM_LUNS];
+ struct ahc_initiator_tinfo transinfo[AHC_NUM_TARGETS];
+
+ /*
+ * Per initiator state bitmasks.
+ */
+ uint16_t auto_negotiate;/* Auto Negotiation Required */
+ uint16_t ultraenb; /* Using ultra sync rate */
+ uint16_t discenable; /* Disconnection allowed */
+ uint16_t tagenable; /* Tagged Queuing allowed */
+};
+
+/*
+ * Data structure for our table of allowed synchronous transfer rates.
+ */
+struct ahc_syncrate {
+ u_int sxfr_u2; /* Value of the SXFR parameter for Ultra2+ Chips */
+ u_int sxfr; /* Value of the SXFR parameter for <= Ultra Chips */
+#define ULTRA_SXFR 0x100 /* Rate Requires Ultra Mode set */
+#define ST_SXFR 0x010 /* Rate Single Transition Only */
+#define DT_SXFR 0x040 /* Rate Double Transition Only */
+ uint8_t period; /* Period to send to SCSI target */
+ char *rate;
+};
+
+/* Safe and valid period for async negotiations. */
+#define AHC_ASYNC_XFER_PERIOD 0x45
+#define AHC_ULTRA2_XFER_PERIOD 0x0a
+
+/*
+ * Indexes into our table of syncronous transfer rates.
+ */
+#define AHC_SYNCRATE_DT 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA 3
+#define AHC_SYNCRATE_FAST 6
+#define AHC_SYNCRATE_MAX AHC_SYNCRATE_DT
+#define AHC_SYNCRATE_MIN 13
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table.
+ */
+struct ahc_phase_table_entry {
+ uint8_t phase;
+ uint8_t mesg_out; /* Message response to parity errors */
+ char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+ uint16_t device_flags[16]; /* words 0-15 */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device */
+#define CFSYNCHISULTRA 0x0040 /* CFSYNCH is an ultra offset (2940AU)*/
+#define CFSYNCSINGLE 0x0080 /* Single-Transition signalling */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+#define CFMULTILUNDEV 0x0800 /* Probe multiple luns in BIOS scan */
+#define CFWBCACHEENB 0x4000 /* Enable W-Behind Cache on disks */
+#define CFWBCACHENOP 0xc000 /* Don't touch W-Behind Cache */
+
+/*
+ * BIOS Control Bits
+ */
+ uint16_t bios_control; /* word 16 */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable boot drives */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+#define CFBIOS_BUSSCAN 0x0008 /* Have the BIOS Scan the Bus */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CFSTPWLEVEL 0x0010 /* Termination level control */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
+#define CFTERM_MENU 0x0040 /* BIOS displays termination menu */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+#define CFSCAMEN 0x0100 /* SCAM enable */
+#define CFMSG_LEVEL 0x0600 /* BIOS Message Level */
+#define CFMSG_VERBOSE 0x0000
+#define CFMSG_SILENT 0x0200
+#define CFMSG_DIAG 0x0400
+#define CFBOOTCD 0x0800 /* Support Bootable CD-ROM */
+/* UNUSED 0xff00 */
+
+/*
+ * Host Adapter Control Bits
+ */
+ uint16_t adapter_control; /* word 17 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
+#define CFWSTERM 0x0008 /* SCSI high byte termination */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte term (284x cards) */
+#define CFMULTILUN 0x0020
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
+#define CFCLUSTERENB 0x0080 /* Cluster Enable */
+#define CFBOOTCHAN 0x0300 /* probe this channel first */
+#define CFBOOTCHANSHIFT 8
+#define CFSEAUTOTERM 0x0400 /* Ultra2 Perform secondary Auto Term*/
+#define CFSELOWTERM 0x0800 /* Ultra2 secondary low term */
+#define CFSEHIGHTERM 0x1000 /* Ultra2 secondary high term */
+#define CFENABLEDV 0x4000 /* Perform Domain Validation*/
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+ uint16_t brtime_id; /* word 18 */
+#define CFSCSIID 0x000f /* host adapter SCSI ID */
+/* UNUSED 0x00f0 */
+#define CFBRTIME 0xff00 /* bus release time */
+
+/*
+ * Maximum targets
+ */
+ uint16_t max_targets; /* word 19 */
+#define CFMAXTARG 0x00ff /* maximum targets */
+#define CFBOOTLUN 0x0f00 /* Lun to boot from */
+#define CFBOOTID 0xf000 /* Target to boot from */
+ uint16_t res_1[10]; /* words 20-29 */
+ uint16_t signature; /* Signature == 0x250 */
+#define CFSIGNATURE 0x250
+#define CFSIGNATURE2 0x300
+ uint16_t checksum; /* word 31 */
+};
+
+/**************************** Message Buffer *********************************/
+typedef enum {
+ MSG_TYPE_NONE = 0x00,
+ MSG_TYPE_INITIATOR_MSGOUT = 0x01,
+ MSG_TYPE_INITIATOR_MSGIN = 0x02,
+ MSG_TYPE_TARGET_MSGOUT = 0x03,
+ MSG_TYPE_TARGET_MSGIN = 0x04
+} ahc_msg_type;
+
+typedef enum {
+ MSGLOOP_IN_PROG,
+ MSGLOOP_MSGCOMPLETE,
+ MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahc_aic7770_softc {
+ /*
+ * Saved register state used for chip_init().
+ */
+ uint8_t busspd;
+ uint8_t bustime;
+};
+
+struct ahc_pci_softc {
+ /*
+ * Saved register state used for chip_init().
+ */
+ uint32_t devconfig;
+ uint16_t targcrccnt;
+ uint8_t command;
+ uint8_t csize_lattime;
+ uint8_t optionmode;
+ uint8_t crccontrol1;
+ uint8_t dscommand0;
+ uint8_t dspcistatus;
+ uint8_t scbbaddr;
+ uint8_t dff_thrsh;
+};
+
+union ahc_bus_softc {
+ struct ahc_aic7770_softc aic7770_softc;
+ struct ahc_pci_softc pci_softc;
+};
+
+typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+typedef int (*ahc_bus_chip_init_t)(struct ahc_softc *);
+typedef int (*ahc_bus_suspend_t)(struct ahc_softc *);
+typedef int (*ahc_bus_resume_t)(struct ahc_softc *);
+typedef void ahc_callback_t (void *);
+
+struct ahc_softc {
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
+#ifndef __linux__
+ bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
+#endif
+ struct scb_data *scb_data;
+
+ struct scb *next_queued_scb;
+
+ /*
+ * SCBs that have been sent to the controller
+ */
+ LIST_HEAD(, scb) pending_scbs;
+
+ /*
+ * Counting lock for deferring the release of additional
+ * untagged transactions from the untagged_queues. When
+ * the lock is decremented to 0, all queues in the
+ * untagged_queues array are run.
+ */
+ u_int untagged_queue_lock;
+
+ /*
+ * Per-target queue of untagged-transactions. The
+ * transaction at the head of the queue is the
+ * currently pending untagged transaction for the
+ * target. The driver only allows a single untagged
+ * transaction per target.
+ */
+ struct scb_tailq untagged_queues[AHC_NUM_TARGETS];
+
+ /*
+ * Bus attachment specific data.
+ */
+ union ahc_bus_softc bus_softc;
+
+ /*
+ * Platform specific data.
+ */
+ struct ahc_platform_data *platform_data;
+
+ /*
+ * Platform specific device information.
+ */
+ ahc_dev_softc_t dev_softc;
+
+ /*
+ * Bus specific device information.
+ */
+ ahc_bus_intr_t bus_intr;
+
+ /*
+ * Bus specific initialization required
+ * after a chip reset.
+ */
+ ahc_bus_chip_init_t bus_chip_init;
+
+ /*
+ * Bus specific suspend routine.
+ */
+ ahc_bus_suspend_t bus_suspend;
+
+ /*
+ * Bus specific resume routine.
+ */
+ ahc_bus_resume_t bus_resume;
+
+ /*
+ * Target mode related state kept on a per enabled lun basis.
+ * Targets that are not enabled will have null entries.
+ * As an initiator, we keep one target entry for our initiator
+ * ID to store our sync/wide transfer settings.
+ */
+ struct ahc_tmode_tstate *enabled_targets[AHC_NUM_TARGETS];
+
+ /*
+ * The black hole device responsible for handling requests for
+ * disabled luns on enabled targets.
+ */
+ struct ahc_tmode_lstate *black_hole;
+
+ /*
+ * Device instance currently on the bus awaiting a continue TIO
+ * for a command that was not given the disconnect priveledge.
+ */
+ struct ahc_tmode_lstate *pending_device;
+
+ /*
+ * Card characteristics
+ */
+ ahc_chip chip;
+ ahc_feature features;
+ ahc_bug bugs;
+ ahc_flag flags;
+ struct seeprom_config *seep_config;
+
+ /* Values to store in the SEQCTL register for pause and unpause */
+ uint8_t unpause;
+ uint8_t pause;
+
+ /* Command Queues */
+ uint8_t qoutfifonext;
+ uint8_t qinfifonext;
+ uint8_t *qoutfifo;
+ uint8_t *qinfifo;
+
+ /* Critical Section Data */
+ struct cs *critical_sections;
+ u_int num_critical_sections;
+
+ /* Links for chaining softcs */
+ TAILQ_ENTRY(ahc_softc) links;
+
+ /* Channel Names ('A', 'B', etc.) */
+ char channel;
+ char channel_b;
+
+ /* Initiator Bus ID */
+ uint8_t our_id;
+ uint8_t our_id_b;
+
+ /*
+ * PCI error detection.
+ */
+ int unsolicited_ints;
+
+ /*
+ * Target incoming command FIFO.
+ */
+ struct target_cmd *targetcmds;
+ uint8_t tqinfifonext;
+
+ /*
+ * Cached copy of the sequencer control register.
+ */
+ uint8_t seqctl;
+
+ /*
+ * Incoming and outgoing message handling.
+ */
+ uint8_t send_msg_perror;
+ ahc_msg_type msg_type;
+ uint8_t msgout_buf[12];/* Message we are sending */
+ uint8_t msgin_buf[12];/* Message we are receiving */
+ u_int msgout_len; /* Length of message to send */
+ u_int msgout_index; /* Current index in msgout */
+ u_int msgin_index; /* Current index in msgin */
+
+ /*
+ * Mapping information for data structures shared
+ * between the sequencer and kernel.
+ */
+ bus_dma_tag_t parent_dmat;
+ bus_dma_tag_t shared_data_dmat;
+ bus_dmamap_t shared_data_dmamap;
+ dma_addr_t shared_data_busaddr;
+
+ /*
+ * Bus address of the one byte buffer used to
+ * work-around a DMA bug for chips <= aic7880
+ * in target mode.
+ */
+ dma_addr_t dma_bug_buf;
+
+ /* Number of enabled target mode device on this card */
+ u_int enabled_luns;
+
+ /* Initialization level of this data structure */
+ u_int init_level;
+
+ /* PCI cacheline size. */
+ u_int pci_cachesize;
+
+ /*
+ * Count of parity errors we have seen as a target.
+ * We auto-disable parity error checking after seeing
+ * AHC_PCI_TARGET_PERR_THRESH number of errors.
+ */
+ u_int pci_target_perr_count;
+#define AHC_PCI_TARGET_PERR_THRESH 10
+
+ /* Maximum number of sequencer instructions supported. */
+ u_int instruction_ram_size;
+
+ /* Per-Unit descriptive information */
+ const char *description;
+ char *name;
+ int unit;
+
+ /* Selection Timer settings */
+ int seltime;
+ int seltime_b;
+
+ uint16_t user_discenable;/* Disconnection allowed */
+ uint16_t user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahc_softc_tailq, ahc_softc);
+extern struct ahc_softc_tailq ahc_tailq;
+
+/************************ Active Device Information ***************************/
+typedef enum {
+ ROLE_UNKNOWN,
+ ROLE_INITIATOR,
+ ROLE_TARGET
+} role_t;
+
+struct ahc_devinfo {
+ int our_scsiid;
+ int target_offset;
+ uint16_t target_mask;
+ u_int target;
+ u_int lun;
+ char channel;
+ role_t role; /*
+ * Only guaranteed to be correct if not
+ * in the busfree state.
+ */
+};
+
+/****************************** PCI Structures ********************************/
+typedef int (ahc_device_setup_t)(struct ahc_softc *);
+
+struct ahc_pci_identity {
+ uint64_t full_id;
+ uint64_t id_mask;
+ char *name;
+ ahc_device_setup_t *setup;
+};
+extern struct ahc_pci_identity ahc_pci_ident_table[];
+extern const u_int ahc_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+ uint32_t full_id;
+ uint32_t id_mask;
+ const char *name;
+ ahc_device_setup_t *setup;
+};
+extern struct aic7770_identity aic7770_ident_table[];
+extern const int ahc_num_aic7770_devs;
+
+#define AHC_EISA_SLOT_OFFSET 0xc00
+#define AHC_EISA_IOSIZE 0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
+void ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
+void ahc_busy_tcl(struct ahc_softc *ahc,
+ u_int tcl, u_int busyid);
+
+/***************************** PCI Front End *********************************/
+struct ahc_pci_identity *ahc_find_pci_device(ahc_dev_softc_t);
+int ahc_pci_config(struct ahc_softc *,
+ struct ahc_pci_identity *);
+int ahc_pci_test_register_access(struct ahc_softc *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int aic7770_config(struct ahc_softc *ahc,
+ struct aic7770_identity *,
+ u_int port);
+
+/************************** SCB and SCB queue management **********************/
+int ahc_probe_scbs(struct ahc_softc *);
+void ahc_run_untagged_queues(struct ahc_softc *ahc);
+void ahc_run_untagged_queue(struct ahc_softc *ahc,
+ struct scb_tailq *queue);
+void ahc_qinfifo_requeue_tail(struct ahc_softc *ahc,
+ struct scb *scb);
+int ahc_match_scb(struct ahc_softc *ahc, struct scb *scb,
+ int target, char channel, int lun,
+ u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahc_softc *ahc_alloc(void *platform_arg, char *name);
+int ahc_softc_init(struct ahc_softc *);
+void ahc_controller_info(struct ahc_softc *ahc, char *buf);
+int ahc_chip_init(struct ahc_softc *ahc);
+int ahc_init(struct ahc_softc *ahc);
+void ahc_intr_enable(struct ahc_softc *ahc, int enable);
+void ahc_pause_and_flushwork(struct ahc_softc *ahc);
+int ahc_suspend(struct ahc_softc *ahc);
+int ahc_resume(struct ahc_softc *ahc);
+void ahc_softc_insert(struct ahc_softc *);
+struct ahc_softc *ahc_find_softc(struct ahc_softc *ahc);
+void ahc_set_unit(struct ahc_softc *, int);
+void ahc_set_name(struct ahc_softc *, char *);
+void ahc_alloc_scbs(struct ahc_softc *ahc);
+void ahc_free(struct ahc_softc *ahc);
+int ahc_reset(struct ahc_softc *ahc, int reinit);
+void ahc_shutdown(void *arg);
+
+/*************************** Interrupt Services *******************************/
+void ahc_clear_intstat(struct ahc_softc *ahc);
+void ahc_run_qoutfifo(struct ahc_softc *ahc);
+#ifdef AHC_TARGET_MODE
+void ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
+#endif
+void ahc_handle_brkadrint(struct ahc_softc *ahc);
+void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
+void ahc_handle_scsiint(struct ahc_softc *ahc,
+ u_int intstat);
+void ahc_clear_critical_section(struct ahc_softc *ahc);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+ SEARCH_COMPLETE,
+ SEARCH_COUNT,
+ SEARCH_REMOVE
+} ahc_search_action;
+int ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahc_search_action action);
+int ahc_search_untagged_queues(struct ahc_softc *ahc,
+ ahc_io_ctx_t ctx,
+ int target, char channel,
+ int lun, uint32_t status,
+ ahc_search_action action);
+int ahc_search_disc_list(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ int stop_on_first, int remove,
+ int save_state);
+void ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+int ahc_reset_channel(struct ahc_softc *ahc, char channel,
+ int initiate_reset);
+int ahc_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahc_restart(struct ahc_softc *ahc);
+void ahc_calc_residual(struct ahc_softc *ahc,
+ struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahc_phase_table_entry*
+ ahc_lookup_phase_entry(int phase);
+void ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+ u_int our_id, u_int target,
+ u_int lun, char channel,
+ role_t role);
+/************************** Transfer Negotiation ******************************/
+struct ahc_syncrate* ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+ u_int *ppr_options, u_int maxsync);
+u_int ahc_find_period(struct ahc_softc *ahc,
+ u_int scsirate, u_int maxsync);
+void ahc_validate_offset(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide,
+ role_t role);
+void ahc_validate_width(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ u_int *bus_width,
+ role_t role);
+/*
+ * Negotiation types. These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+ AHC_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */
+ AHC_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */
+ AHC_NEG_ALWAYS /* Renegotiat even if goal is async. */
+} ahc_neg_type;
+int ahc_update_neg_request(struct ahc_softc*,
+ struct ahc_devinfo*,
+ struct ahc_tmode_tstate*,
+ struct ahc_initiator_tinfo*,
+ ahc_neg_type);
+void ahc_set_width(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused);
+void ahc_set_syncrate(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate,
+ u_int period, u_int offset,
+ u_int ppr_options,
+ u_int type, int paused);
+typedef enum {
+ AHC_QUEUE_NONE,
+ AHC_QUEUE_BASIC,
+ AHC_QUEUE_TAGGED
+} ahc_queue_alg;
+
+void ahc_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHC_TARGET_MODE
+void ahc_send_lstate_events(struct ahc_softc *,
+ struct ahc_tmode_lstate *);
+void ahc_handle_en_lun(struct ahc_softc *ahc,
+ struct cam_sim *sim, union ccb *ccb);
+cam_status ahc_find_tmode_devs(struct ahc_softc *ahc,
+ struct cam_sim *sim, union ccb *ccb,
+ struct ahc_tmode_tstate **tstate,
+ struct ahc_tmode_lstate **lstate,
+ int notfound_failure);
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHC_DEBUG
+extern uint32_t ahc_debug;
+#define AHC_SHOW_MISC 0x0001
+#define AHC_SHOW_SENSE 0x0002
+#define AHC_DUMP_SEEPROM 0x0004
+#define AHC_SHOW_TERMCTL 0x0008
+#define AHC_SHOW_MEMORY 0x0010
+#define AHC_SHOW_MESSAGES 0x0020
+#define AHC_SHOW_DV 0x0040
+#define AHC_SHOW_SELTO 0x0080
+#define AHC_SHOW_QFULL 0x0200
+#define AHC_SHOW_QUEUE 0x0400
+#define AHC_SHOW_TQIN 0x0800
+#define AHC_SHOW_MASKED_ERRORS 0x1000
+#define AHC_DEBUG_SEQUENCER 0x2000
+#endif
+void ahc_print_scb(struct scb *scb);
+void ahc_print_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *dev);
+void ahc_dump_card_state(struct ahc_softc *ahc);
+int ahc_print_register(ahc_reg_parse_entry_t *table,
+ u_int num_entries,
+ const char *name,
+ u_int address,
+ u_int value,
+ u_int *cur_column,
+ u_int wrap_point);
+/******************************* SEEPROM *************************************/
+int ahc_acquire_seeprom(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd);
+void ahc_release_seeprom(struct seeprom_descriptor *sd);
+#endif /* _AIC7XXX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg
new file mode 100644
index 000000000000..810ec700d9fc
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.reg
@@ -0,0 +1,1594 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+ address 0x000
+ access_mode RW
+ field TEMODE 0x80
+ field ENSELO 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field ENAUTOATNO 0x08
+ field ENAUTOATNI 0x04
+ field ENAUTOATNP 0x02
+ field SCSIRSTO 0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+ address 0x001
+ access_mode RW
+ field DFON 0x80
+ field DFPEXP 0x40
+ field FAST20 0x20
+ field CLRSTCNT 0x10
+ field SPIOEN 0x08
+ field SCAMEN 0x04
+ field CLRCHN 0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+ address 0x002
+ access_mode RW
+ field BITBUCKET 0x80
+ field SWRAPEN 0x40
+ field ENSPCHK 0x20
+ mask STIMESEL 0x18
+ field ENSTIMER 0x04
+ field ACTNEGEN 0x02
+ field STPWEN 0x01 /* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+ address 0x003
+ access_mode RO
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ field ATNI 0x10
+ field SELI 0x08
+ field BSYI 0x04
+ field REQI 0x02
+ field ACKI 0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_DATAOUT_DT P_DATAOUT|MSGI
+ mask P_DATAIN_DT P_DATAIN|MSGI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus. Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+ address 0x003
+ access_mode WO
+ field CDO 0x80
+ field IOO 0x40
+ field MSGO 0x20
+ field ATNO 0x10
+ field SELO 0x08
+ field BSYO 0x04
+ field REQO 0x02
+ field ACKO 0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers. Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+ address 0x004
+ access_mode RW
+ field WIDEXFER 0x80 /* Wide transfer control */
+ field ENABLE_CRC 0x40 /* CRC for D-Phases */
+ field SINGLE_EDGE 0x10 /* Disable DT Transfers */
+ mask SXFR 0x70 /* Sync transfer rate */
+ mask SXFR_ULTRA2 0x0f /* Sync transfer rate */
+ mask SOFS 0x0f /* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID {
+ address 0x005
+ access_mode RW
+ mask TID 0xf0 /* Target ID mask */
+ mask TWIN_TID 0x70
+ field TWIN_CHNLB 0x80
+ mask OID 0x0f /* Our ID mask */
+ /*
+ * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+ * The aic7890/91 allow an offset of up to 127 transfers in both wide
+ * and narrow mode.
+ */
+ alias SCSIOFFSET
+ mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode. SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+ address 0x006
+ access_mode RW
+}
+
+register SCSIDATH {
+ address 0x007
+ access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus. The counter is decremented only once
+ * the data has been safely transferred. SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+register STCNT {
+ address 0x008
+ size 3
+ access_mode RW
+}
+
+/* ALT_MODE registers (Ultra2 and Ultra160 chips) */
+register SXFRCTL2 {
+ address 0x013
+ access_mode RW
+ field AUTORSTDIS 0x10
+ field CMDDMAEN 0x08
+ mask ASYNC_SETUP 0x07
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register OPTIONMODE {
+ address 0x008
+ access_mode RW
+ field AUTORATEEN 0x80
+ field AUTOACKEN 0x40
+ field ATNMGMNTEN 0x20
+ field BUSFREEREV 0x10
+ field EXPPHASEDIS 0x08
+ field SCSIDATL_IMGEN 0x04
+ field AUTO_MSGOUT_DE 0x02
+ field DIS_MSGIN_DUALEDGE 0x01
+ mask OPTIONMODE_DEFAULTS AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register TARGCRCCNT {
+ address 0x00a
+ size 2
+ access_mode RW
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+ address 0x00b
+ access_mode WO
+ field CLRSELDO 0x40
+ field CLRSELDI 0x20
+ field CLRSELINGO 0x10
+ field CLRSWRAP 0x08
+ field CLRIOERR 0x08 /* Ultra2 Only */
+ field CLRSPIORDY 0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0 {
+ address 0x00b
+ access_mode RO
+ field TARGET 0x80 /* Board acting as target */
+ field SELDO 0x40 /* Selection Done */
+ field SELDI 0x20 /* Board has been selected */
+ field SELINGO 0x10 /* Selection In Progress */
+ field SWRAP 0x08 /* 24bit counter wrap */
+ field IOERR 0x08 /* LVD Tranceiver mode changed */
+ field SDONE 0x04 /* STCNT = 0x000000 */
+ field SPIORDY 0x02 /* SCSI PIO Ready */
+ field DMADONE 0x01 /* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+ address 0x00c
+ access_mode WO
+ field CLRSELTIMEO 0x80
+ field CLRATNO 0x40
+ field CLRSCSIRSTI 0x20
+ field CLRBUSFREE 0x08
+ field CLRSCSIPERR 0x04
+ field CLRPHASECHG 0x02
+ field CLRREQINIT 0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1 {
+ address 0x00c
+ access_mode RO
+ field SELTO 0x80
+ field ATNTARG 0x40
+ field SCSIRSTI 0x20
+ field PHASEMIS 0x10
+ field BUSFREE 0x08
+ field SCSIPERR 0x04
+ field PHASECHG 0x02
+ field REQINIT 0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+ address 0x00d
+ access_mode RO
+ field OVERRUN 0x80
+ field SHVALID 0x40 /* Shaddow Layer non-zero */
+ field EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ field CRCVALERR 0x08 /* CRC doesn't match (U3 only) */
+ field CRCENDERR 0x04 /* No terminal CRC packet (U3 only) */
+ field CRCREQERR 0x02 /* Illegal CRC packet req (U3 only) */
+ field DUAL_EDGE_ERR 0x01 /* Incorrect data phase (U3 only) */
+ mask SFCNT 0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+ address 0x00e
+ access_mode RO
+ mask SCSICNT 0xf0
+ mask OFFCNT 0x0f
+ mask U2OFFCNT 0x7f
+}
+
+/*
+ * SCSI ID for the aic7890/91 chips
+ */
+register SCSIID_ULTRA2 {
+ address 0x00f
+ access_mode RW
+ mask TID 0xf0 /* Target ID mask */
+ mask OID 0x0f /* Our ID mask */
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+ address 0x010
+ access_mode RW
+ field ENSELDO 0x40
+ field ENSELDI 0x20
+ field ENSELINGO 0x10
+ field ENSWRAP 0x08
+ field ENIOERR 0x08 /* LVD Tranceiver mode changes */
+ field ENSDONE 0x04
+ field ENSPIORDY 0x02
+ field ENDMADONE 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+ address 0x011
+ access_mode RW
+ field ENSELTIMO 0x80
+ field ENATNTARG 0x40
+ field ENSCSIRST 0x20
+ field ENPHASEMIS 0x10
+ field ENBUSFREE 0x08
+ field ENSCSIPERR 0x04
+ field ENPHASECHG 0x02
+ field ENREQINIT 0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+ address 0x012
+ access_mode RW
+}
+
+register SCSIBUSH {
+ address 0x013
+ access_mode RW
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus. They are counted up in the same
+ * manner as STCNT is counted down. SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+ address 0x014
+ size 4
+ access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+ address 0x018
+ access_mode RW
+ field STAGE6 0x20
+ field STAGE5 0x10
+ field STAGE4 0x08
+ field STAGE3 0x04
+ field STAGE2 0x02
+ field STAGE1 0x01
+ alias TARGIDIN
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+ address 0x019
+ access_mode RW
+ mask SELID_MASK 0xf0
+ field ONEBIT 0x08
+}
+
+register SCAMCTL {
+ address 0x01a
+ access_mode RW
+ field ENSCAMSELO 0x80
+ field CLRSCAMSELID 0x40
+ field ALTSTIM 0x20
+ field DFLTTID 0x10
+ mask SCAMLVL 0x03
+}
+
+/*
+ * Target Mode Selecting in ID bitmask (aic7890/91/96/97)
+ */
+register TARGID {
+ address 0x01b
+ size 2
+ access_mode RW
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection. On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+ address 0x01b
+ access_mode RW
+ field SOFT1 0x80
+ field SOFT0 0x40
+ field SOFTCMDEN 0x20
+ field EXT_BRDCTL 0x10 /* External Board control */
+ field SEEPROM 0x08 /* External serial eeprom logic */
+ field EEPROM 0x04 /* Writable external BIOS ROM */
+ field ROM 0x02 /* Logic for accessing external ROM */
+ field SSPIOCPS 0x01 /* Termination and cable detection */
+}
+
+register BRDCTL {
+ address 0x01d
+ field BRDDAT7 0x80
+ field BRDDAT6 0x40
+ field BRDDAT5 0x20
+ field BRDSTB 0x10
+ field BRDCS 0x08
+ field BRDRW 0x04
+ field BRDCTL1 0x02
+ field BRDCTL0 0x01
+ /* 7890 Definitions */
+ field BRDDAT4 0x10
+ field BRDDAT3 0x08
+ field BRDDAT2 0x04
+ field BRDRW_ULTRA2 0x02
+ field BRDSTB_ULTRA2 0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device. In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device. When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM. When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.
+ *
+ * After successful arbitration for the memory port, the SEECS bit of
+ * the SEECTL register is connected to the chip select. The SEECK,
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in
+ * lines respectively. The SEERDY bit of SEECTL is useful in that it
+ * gives us an 800 nsec timer. After a write to the SEECTL register,
+ * the SEERDY goes high 800 nsec later. The one exception to this is
+ * when we first request access to the memory port. The SEERDY goes
+ * high to signify that access has been granted and, for this case, has
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to
+ * read the serial EEPROM.
+ */
+register SEECTL {
+ address 0x01e
+ field EXTARBACK 0x80
+ field EXTARBREQ 0x40
+ field SEEMS 0x20
+ field SEERDY 0x10
+ field SEECS 0x08
+ field SEECK 0x04
+ field SEEDO 0x02
+ field SEEDI 0x01
+}
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection. In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+ address 0x01f
+ access_mode RW
+ field DIAGLEDEN 0x80 /* Aic78X0 only */
+ field DIAGLEDON 0x40 /* Aic78X0 only */
+ field AUTOFLUSHDIS 0x20
+ field SELBUSB 0x08
+ field ENAB40 0x08 /* LVD transceiver active */
+ field ENAB20 0x04 /* SE/HVD transceiver active */
+ field SELWIDE 0x02
+ field XCVR 0x01 /* External transceiver active */
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+ address 0x060
+ access_mode RW
+ field PERRORDIS 0x80
+ field PAUSEDIS 0x40
+ field FAILDIS 0x20
+ field FASTMODE 0x10
+ field BRKADRINTEN 0x08
+ field STEP 0x04
+ field SEQRESET 0x02
+ field LOADRAM 0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
+ * four bytes in succession. The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+ address 0x061
+ access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+ address 0x062
+ access_mode RW
+}
+
+register SEQADDR1 {
+ address 0x063
+ access_mode RW
+ mask SEQADDR1_MASK 0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+ address 0x064
+ access_mode RW
+ accumulator
+}
+
+register SINDEX {
+ address 0x065
+ access_mode RW
+ sindex
+}
+
+register DINDEX {
+ address 0x066
+ access_mode RW
+}
+
+register ALLONES {
+ address 0x069
+ access_mode RO
+ allones
+}
+
+register ALLZEROS {
+ address 0x06a
+ access_mode RO
+ allzeros
+}
+
+register NONE {
+ address 0x06a
+ access_mode WO
+ none
+}
+
+register FLAGS {
+ address 0x06b
+ access_mode RO
+ field ZERO 0x02
+ field CARRY 0x01
+}
+
+register SINDIR {
+ address 0x06c
+ access_mode RO
+}
+
+register DINDIR {
+ address 0x06d
+ access_mode WO
+}
+
+register FUNCTION1 {
+ address 0x06e
+ access_mode RW
+}
+
+register STACK {
+ address 0x06f
+ access_mode RO
+}
+
+const STACK_SIZE 4
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+ address 0x084
+ access_mode RW
+ field ACE 0x08
+ field ENABLE 0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND0 {
+ address 0x084
+ access_mode RW
+ field CACHETHEN 0x80 /* Cache Threshold enable */
+ field DPARCKEN 0x40 /* Data Parity Check Enable */
+ field MPARCKEN 0x20 /* Memory Parity Check Enable */
+ field EXTREQLCK 0x10 /* External Request Lock */
+ /* aic7890/91/96/97 only */
+ field INTSCBRAMSEL 0x08 /* Internal SCB RAM Select */
+ field RAMPS 0x04 /* External SCB RAM Present */
+ field USCBSIZE32 0x02 /* Use 32byte SCB Page Size */
+ field CIOPARCKEN 0x01 /* Internal bus parity error enable */
+}
+
+register DSCOMMAND1 {
+ address 0x085
+ access_mode RW
+ mask DSLATT 0xfc /* PCI latency timer (non-ultra2) */
+ field HADDLDSEL1 0x02 /* Host Address Load Select Bits */
+ field HADDLDSEL0 0x01
+}
+
+/*
+ * Bus On/Off Time (p. 3-44) aic7770 only
+ */
+register BUSTIME {
+ address 0x085
+ access_mode RW
+ mask BOFF 0xf0
+ mask BON 0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45) aic7770 only
+ */
+register BUSSPD {
+ address 0x086
+ access_mode RW
+ mask DFTHRSH 0xc0
+ mask STBOFF 0x38
+ mask STBON 0x07
+ mask DFTHRSH_100 0xc0
+ mask DFTHRSH_75 0x80
+}
+
+/* aic7850/55/60/70/80/95 only */
+register DSPCISTATUS {
+ address 0x086
+ mask DFTHRSH_100 0xc0
+}
+
+/* aic7890/91/96/97 only */
+register HS_MAILBOX {
+ address 0x086
+ mask HOST_MAILBOX 0xF0
+ mask SEQ_MAILBOX 0x0F
+ mask HOST_TQINPOS 0x80 /* Boundary at either 0 or 128 */
+}
+
+const HOST_MAILBOX_SHIFT 4
+const SEQ_MAILBOX_SHIFT 0
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+ address 0x087
+ access_mode RW
+ field POWRDN 0x40
+ field SWINT 0x10
+ field IRQMS 0x08
+ field PAUSE 0x04
+ field INTEN 0x02
+ field CHIPRST 0x01
+ field CHIPRSTACK 0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+ address 0x088
+ size 4
+ access_mode RW
+}
+
+register HCNT {
+ address 0x08c
+ size 3
+ access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+ address 0x090
+ access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+ address 0x091
+ access_mode RW
+ field BRKADRINT 0x08
+ field SCSIINT 0x04
+ field CMDCMPLT 0x02
+ field SEQINT 0x01
+ mask BAD_PHASE SEQINT /* unknown scsi bus phase */
+ mask SEND_REJECT 0x10|SEQINT /* sending a message reject */
+ mask PROTO_VIOLATION 0x20|SEQINT /* SCSI protocol violation */
+ mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
+ mask IGN_WIDE_RES 0x40|SEQINT /* Complex IGN Wide Res Msg */
+ mask PDATA_REINIT 0x50|SEQINT /*
+ * Returned to data phase
+ * that requires data
+ * transfer pointers to be
+ * recalculated from the
+ * transfer residual.
+ */
+ mask HOST_MSG_LOOP 0x60|SEQINT /*
+ * The bus is ready for the
+ * host to perform another
+ * message transaction. This
+ * mechanism is used for things
+ * like sync/wide negotiation
+ * that require a kernel based
+ * message state engine.
+ */
+ mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
+ mask PERR_DETECTED 0x80|SEQINT /*
+ * Either the phase_lock
+ * or inb_next routine has
+ * noticed a parity error.
+ */
+ mask DATA_OVERRUN 0x90|SEQINT /*
+ * Target attempted to write
+ * beyond the bounds of its
+ * command.
+ */
+ mask MKMSG_FAILED 0xa0|SEQINT /*
+ * Target completed command
+ * without honoring our ATN
+ * request to issue a message.
+ */
+ mask MISSED_BUSFREE 0xb0|SEQINT /*
+ * The sequencer never saw
+ * the bus go free after
+ * either a command complete
+ * or disconnect message.
+ */
+ mask SCB_MISMATCH 0xc0|SEQINT /*
+ * Downloaded SCB's tag does
+ * not match the entry we
+ * intended to download.
+ */
+ mask NO_FREE_SCB 0xd0|SEQINT /*
+ * get_free_or_disc_scb failed.
+ */
+ mask OUT_OF_RANGE 0xe0|SEQINT
+
+ mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */
+ mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors. You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+ address 0x092
+ access_mode RO
+ field CIOPARERR 0x80 /* Ultra2 only */
+ field PCIERRSTAT 0x40 /* PCI only */
+ field MPARERR 0x20 /* PCI only */
+ field DPARERR 0x10 /* PCI only */
+ field SQPARERR 0x08
+ field ILLOPCODE 0x04
+ field ILLSADDR 0x02
+ field ILLHADDR 0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+ address 0x092
+ access_mode WO
+ field CLRPARERR 0x10 /* PCI only */
+ field CLRBRKADRINT 0x08
+ field CLRSCSIINT 0x04
+ field CLRCMDINT 0x02
+ field CLRSEQINT 0x01
+}
+
+register DFCNTRL {
+ address 0x093
+ access_mode RW
+ field PRELOADEN 0x80 /* aic7890 only */
+ field WIDEODD 0x40
+ field SCSIEN 0x20
+ field SDMAEN 0x10
+ field SDMAENACK 0x10
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04
+ field FIFOFLUSH 0x02
+ field FIFORESET 0x01
+}
+
+register DFSTATUS {
+ address 0x094
+ access_mode RO
+ field PRELOAD_AVAIL 0x80
+ field DFCACHETH 0x40
+ field FIFOQWDEMP 0x20
+ field MREQPEND 0x10
+ field HDONE 0x08
+ field DFTHRESH 0x04
+ field FIFOFULL 0x02
+ field FIFOEMP 0x01
+}
+
+register DFWADDR {
+ address 0x95
+ access_mode RW
+}
+
+register DFRADDR {
+ address 0x97
+ access_mode RW
+}
+
+register DFDAT {
+ address 0x099
+ access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+ address 0x09a
+ access_mode RW
+ field SCBAUTO 0x80
+ mask SCBCNT_MASK 0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+ address 0x09b
+ access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT {
+ address 0x09c
+ access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+ address 0x09d
+ access_mode WO
+}
+
+register CRCCONTROL1 {
+ address 0x09d
+ access_mode RW
+ field CRCONSEEN 0x80
+ field CRCVALCHKEN 0x40
+ field CRCENDCHKEN 0x20
+ field CRCREQCHKEN 0x10
+ field TARGCRCENDEN 0x08
+ field TARGCRCCNTEN 0x04
+}
+
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+ address 0x09e
+ access_mode RO
+}
+
+register SCSIPHASE {
+ address 0x09e
+ access_mode RO
+ field STATUS_PHASE 0x20
+ field COMMAND_PHASE 0x10
+ field MSG_IN_PHASE 0x08
+ field MSG_OUT_PHASE 0x04
+ field DATA_IN_PHASE 0x02
+ field DATA_OUT_PHASE 0x01
+ mask DATA_PHASE_MASK 0x03
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+ field ALT_MODE 0x80
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+ address 0x0a0
+ size 64
+
+ SCB_CDB_PTR {
+ size 4
+ alias SCB_RESIDUAL_DATACNT
+ alias SCB_CDB_STORE
+ }
+ SCB_RESIDUAL_SGPTR {
+ size 4
+ }
+ SCB_SCSI_STATUS {
+ size 1
+ }
+ SCB_TARGET_PHASES {
+ size 1
+ }
+ SCB_TARGET_DATA_DIR {
+ size 1
+ }
+ SCB_TARGET_ITAG {
+ size 1
+ }
+ SCB_DATAPTR {
+ size 4
+ }
+ SCB_DATACNT {
+ /*
+ * The last byte is really the high address bits for
+ * the data address.
+ */
+ size 4
+ field SG_LAST_SEG 0x80 /* In the fourth byte */
+ mask SG_HIGH_ADDR_BITS 0x7F /* In the fourth byte */
+ }
+ SCB_SGPTR {
+ size 4
+ field SG_RESID_VALID 0x04 /* In the first byte */
+ field SG_FULL_RESID 0x02 /* In the first byte */
+ field SG_LIST_NULL 0x01 /* In the first byte */
+ }
+ SCB_CONTROL {
+ size 1
+ field TARGET_SCB 0x80
+ field STATUS_RCVD 0x80
+ field DISCENB 0x40
+ field TAG_ENB 0x20
+ field MK_MESSAGE 0x10
+ field ULTRAENB 0x08
+ field DISCONNECTED 0x04
+ mask SCB_TAG_TYPE 0x03
+ }
+ SCB_SCSIID {
+ size 1
+ field TWIN_CHNLB 0x80
+ mask TWIN_TID 0x70
+ mask TID 0xf0
+ mask OID 0x0f
+ }
+ SCB_LUN {
+ field SCB_XFERLEN_ODD 0x80
+ mask LID 0x3f
+ size 1
+ }
+ SCB_TAG {
+ size 1
+ }
+ SCB_CDB_LEN {
+ size 1
+ }
+ SCB_SCSIRATE {
+ size 1
+ }
+ SCB_SCSIOFFSET {
+ size 1
+ }
+ SCB_NEXT {
+ size 1
+ }
+ SCB_64_SPARE {
+ size 16
+ }
+ SCB_64_BTT {
+ size 16
+ }
+}
+
+const SCB_UPLOAD_SIZE 32
+const SCB_DOWNLOAD_SIZE 32
+const SCB_DOWNLOAD_SIZE_64 48
+
+const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+ address 0x0c0
+ access_mode RW
+ field CS_2840 0x04
+ field CK_2840 0x02
+ field DO_2840 0x01
+}
+
+register STATUS_2840 {
+ address 0x0c1
+ access_mode RW
+ field EEPROM_TF 0x80
+ mask BIOS_SEL 0x60
+ mask ADSEL 0x1e
+ field DI_2840 0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register CCHADDR {
+ address 0x0E0
+ size 8
+}
+
+register CCHCNT {
+ address 0x0E8
+}
+
+register CCSGRAM {
+ address 0x0E9
+}
+
+register CCSGADDR {
+ address 0x0EA
+}
+
+register CCSGCTL {
+ address 0x0EB
+ field CCSGDONE 0x80
+ field CCSGEN 0x08
+ field SG_FETCH_NEEDED 0x02 /* Bit used for software state */
+ field CCSGRESET 0x01
+}
+
+register CCSCBCNT {
+ address 0xEF
+}
+
+register CCSCBCTL {
+ address 0x0EE
+ field CCSCBDONE 0x80
+ field ARRDONE 0x40 /* SCB Array prefetch done */
+ field CCARREN 0x10
+ field CCSCBEN 0x08
+ field CCSCBDIR 0x04
+ field CCSCBRESET 0x01
+}
+
+register CCSCBADDR {
+ address 0x0ED
+}
+
+register CCSCBRAM {
+ address 0xEC
+}
+
+/*
+ * SCB bank address (7895/7896/97 only)
+ */
+register SCBBADDR {
+ address 0x0F0
+ access_mode RW
+}
+
+register CCSCBPTR {
+ address 0x0F1
+}
+
+register HNSCB_QOFF {
+ address 0x0F4
+}
+
+register SNSCB_QOFF {
+ address 0x0F6
+}
+
+register SDSCB_QOFF {
+ address 0x0F8
+}
+
+register QOFF_CTLSTA {
+ address 0x0FA
+ field SCB_AVAIL 0x40
+ field SNSCB_ROLLOVER 0x20
+ field SDSCB_ROLLOVER 0x10
+ mask SCB_QSIZE 0x07
+ mask SCB_QSIZE_256 0x06
+}
+
+register DFF_THRSH {
+ address 0x0FB
+ mask WR_DFTHRSH 0x70
+ mask RD_DFTHRSH 0x07
+ mask RD_DFTHRSH_MIN 0x00
+ mask RD_DFTHRSH_25 0x01
+ mask RD_DFTHRSH_50 0x02
+ mask RD_DFTHRSH_63 0x03
+ mask RD_DFTHRSH_75 0x04
+ mask RD_DFTHRSH_85 0x05
+ mask RD_DFTHRSH_90 0x06
+ mask RD_DFTHRSH_MAX 0x07
+ mask WR_DFTHRSH_MIN 0x00
+ mask WR_DFTHRSH_25 0x10
+ mask WR_DFTHRSH_50 0x20
+ mask WR_DFTHRSH_63 0x30
+ mask WR_DFTHRSH_75 0x40
+ mask WR_DFTHRSH_85 0x50
+ mask WR_DFTHRSH_90 0x60
+ mask WR_DFTHRSH_MAX 0x70
+}
+
+register SG_CACHE_PRE {
+ access_mode WO
+ address 0x0fc
+ mask SG_ADDR_MASK 0xf8
+ field LAST_SEG 0x02
+ field LAST_SEG_DONE 0x01
+}
+
+register SG_CACHE_SHADOW {
+ access_mode RO
+ address 0x0fc
+ mask SG_ADDR_MASK 0xf8
+ field LAST_SEG 0x02
+ field LAST_SEG_DONE 0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE). The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space. This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+ address 0x020
+ size 58
+
+ /*
+ * 1 byte per target starting at this address for configuration values
+ */
+ BUSY_TARGETS {
+ alias TARG_SCSIRATE
+ size 16
+ }
+ /*
+ * Bit vector of targets that have ULTRA enabled as set by
+ * the BIOS. The Sequencer relies on a per-SCB field to
+ * control whether to enable Ultra transfers or not. During
+ * initialization, we read this field and reuse it for 2
+ * entries in the busy target table.
+ */
+ ULTRA_ENB {
+ alias CMDSIZE_TABLE
+ size 2
+ }
+ /*
+ * Bit vector of targets that have disconnection disabled as set by
+ * the BIOS. The Sequencer relies in a per-SCB field to control the
+ * disconnect priveldge. During initialization, we read this field
+ * and reuse it for 2 entries in the busy target table.
+ */
+ DISC_DSB {
+ size 2
+ }
+ CMDSIZE_TABLE_TAIL {
+ size 4
+ }
+ /*
+ * Partial transfer past cacheline end to be
+ * transferred using an extra S/G.
+ */
+ MWI_RESIDUAL {
+ size 1
+ alias TARG_IMMEDIATE_SCB
+ }
+ /*
+ * SCBID of the next SCB to be started by the controller.
+ */
+ NEXT_QUEUED_SCB {
+ size 1
+ }
+ /*
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
+ */
+ MSG_OUT {
+ size 1
+ }
+ /* Parameters for DMA Logic */
+ DMAPARAMS {
+ size 1
+ field PRELOADEN 0x80
+ field WIDEODD 0x40
+ field SCSIEN 0x20
+ field SDMAEN 0x10
+ field SDMAENACK 0x10
+ field HDMAEN 0x08
+ field HDMAENACK 0x08
+ field DIRECTION 0x04 /* Set indicates PCI->SCSI */
+ field FIFOFLUSH 0x02
+ field FIFORESET 0x01
+ }
+ SEQ_FLAGS {
+ size 1
+ field NOT_IDENTIFIED 0x80
+ field NO_CDB_SENT 0x40
+ field TARGET_CMD_IS_TAGGED 0x40
+ field DPHASE 0x20
+ /* Target flags */
+ field TARG_CMD_PENDING 0x10
+ field CMDPHASE_PENDING 0x08
+ field DPHASE_PENDING 0x04
+ field SPHASE_PENDING 0x02
+ field NO_DISCONNECT 0x01
+ }
+ /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+ SAVED_SCSIID {
+ size 1
+ }
+ SAVED_LUN {
+ size 1
+ }
+ /*
+ * The last bus phase as seen by the sequencer.
+ */
+ LASTPHASE {
+ size 1
+ field CDI 0x80
+ field IOI 0x40
+ field MSGI 0x20
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+ mask P_BUSFREE 0x01
+ }
+ /*
+ * head of list of SCBs awaiting
+ * selection
+ */
+ WAITING_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that are
+ * disconnected. Used for SCB
+ * paging.
+ */
+ DISCONNECTED_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that are
+ * not in use. Used for SCB paging.
+ */
+ FREE_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that have
+ * completed but have not been
+ * put into the qoutfifo.
+ */
+ COMPLETE_SCBH {
+ size 1
+ }
+ /*
+ * Address of the hardware scb array in the host.
+ */
+ HSCB_ADDR {
+ size 4
+ }
+ /*
+ * Base address of our shared data with the kernel driver in host
+ * memory. This includes the qoutfifo and target mode
+ * incoming command queue.
+ */
+ SHARED_DATA_ADDR {
+ size 4
+ }
+ KERNEL_QINPOS {
+ size 1
+ }
+ QINPOS {
+ size 1
+ }
+ QOUTPOS {
+ size 1
+ }
+ /*
+ * Kernel and sequencer offsets into the queue of
+ * incoming target mode command descriptors. The
+ * queue is full when the KERNEL_TQINPOS == TQINPOS.
+ */
+ KERNEL_TQINPOS {
+ size 1
+ }
+ TQINPOS {
+ size 1
+ }
+ ARG_1 {
+ size 1
+ mask SEND_MSG 0x80
+ mask SEND_SENSE 0x40
+ mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
+ mask EXIT_MSG_LOOP 0x08
+ mask CONT_MSG_LOOP 0x04
+ mask CONT_TARG_SESSION 0x02
+ alias RETURN_1
+ }
+ ARG_2 {
+ size 1
+ alias RETURN_2
+ }
+
+ /*
+ * Snapshot of MSG_OUT taken after each message is sent.
+ */
+ LAST_MSG {
+ size 1
+ }
+
+ /*
+ * Sequences the kernel driver has okayed for us. This allows
+ * the driver to do things like prevent initiator or target
+ * operations.
+ */
+ SCSISEQ_TEMPLATE {
+ size 1
+ field ENSELO 0x40
+ field ENSELI 0x20
+ field ENRSELI 0x10
+ field ENAUTOATNO 0x08
+ field ENAUTOATNI 0x04
+ field ENAUTOATNP 0x02
+ }
+}
+
+scratch_ram {
+ address 0x056
+ size 4
+ /*
+ * These scratch ram locations are initialized by the 274X BIOS.
+ * We reuse them after capturing the BIOS settings during
+ * initialization.
+ */
+
+ /*
+ * The initiator specified tag for this target mode transaction.
+ */
+ HA_274_BIOSGLOBAL {
+ size 1
+ field HA_274_EXTENDED_TRANS 0x01
+ alias INITIATOR_TAG
+ }
+
+ SEQ_FLAGS2 {
+ size 1
+ field SCB_DMA 0x01
+ field TARGET_MSG_PENDING 0x02
+ }
+}
+
+scratch_ram {
+ address 0x05a
+ size 6
+ /*
+ * These are reserved registers in the card's scratch ram on the 2742.
+ * The EISA configuraiton chip is mapped here. On Rev E. of the
+ * aic7770, the sequencer can use this area for scratch, but the
+ * host cannot directly access these registers. On later chips, this
+ * area can be read and written by both the host and the sequencer.
+ * Even on later chips, many of these locations are initialized by
+ * the BIOS.
+ */
+ SCSICONF {
+ size 1
+ field TERM_ENB 0x80
+ field RESET_SCSI 0x40
+ field ENSPCHK 0x20
+ mask HSCSIID 0x07 /* our SCSI ID */
+ mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
+ }
+ INTDEF {
+ address 0x05c
+ size 1
+ field EDGE_TRIG 0x80
+ mask VECTOR 0x0f
+ }
+ HOSTCONF {
+ address 0x05d
+ size 1
+ }
+ HA_274_BIOSCTRL {
+ address 0x05f
+ size 1
+ mask BIOSMODE 0x30
+ mask BIOSDISABLED 0x30
+ field CHANNEL_B_PRIMARY 0x08
+ }
+}
+
+scratch_ram {
+ address 0x070
+ size 16
+
+ /*
+ * Per target SCSI offset values for Ultra2 controllers.
+ */
+ TARG_OFFSET {
+ size 16
+ }
+}
+
+const TID_SHIFT 4
+const SCB_LIST_NULL 0xff
+const TARGET_CMD_CMPLT 0xfe
+
+const CCSGADDR_MAX 0x80
+const CCSGRAM_MAXSEGS 16
+
+/* WDTR Message values */
+const BUS_8_BIT 0x00
+const BUS_16_BIT 0x01
+const BUS_32_BIT 0x02
+
+/* Offset maximums */
+const MAX_OFFSET_8BIT 0x0f
+const MAX_OFFSET_16BIT 0x08
+const MAX_OFFSET_ULTRA2 0x7f
+const MAX_OFFSET 0x7f
+const HOST_MSG 0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+
+const STATUS_BUSY 0x08
+const STATUS_QUEUE_FULL 0x28
+const TARGET_DATA_IN 1
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+/* Offsets into the SCBID array where different data is stored */
+const QOUTFIFO_OFFSET download
+const QINFIFO_OFFSET download
+const CACHESIZE_MASK download
+const INVERTED_CACHESIZE_MASK download
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq
new file mode 100644
index 000000000000..d84b741fbab5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.seq
@@ -0,0 +1,2398 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $"
+PATCH_ARG_LIST = "struct ahc_softc *ahc"
+PREFIX = "ahc_"
+
+#include "aic7xxx.reg"
+#include "scsi_message.h"
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration. The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time. This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection. The solution used here is to
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes,
+ * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued. The sequencer will
+ * automatically consume the entries.
+ */
+
+bus_free_sel:
+ /*
+ * Turn off the selection hardware. We need to reset the
+ * selection request in order to perform a new selection.
+ */
+ and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP;
+ and SIMODE1, ~ENBUSFREE;
+poll_for_work:
+ call clear_target_state;
+ and SXFRCTL0, ~SPIOEN;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ clr SCSIBUSL;
+ }
+ test SCSISEQ, ENSELO jnz poll_for_selection;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
+ test SCSISEQ, ENSELO jnz poll_for_selection;
+ }
+ cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+poll_for_work_loop:
+ if ((ahc->features & AHC_TWIN) != 0) {
+ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
+ }
+ test SSTAT0, SELDO|SELDI jnz selection;
+test_queue:
+ /* Has the driver posted any work for us? */
+BEGIN_CRITICAL;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+ } else {
+ mov A, QINPOS;
+ cmp KERNEL_QINPOS, A je poll_for_work_loop;
+ }
+ mov ARG_1, NEXT_QUEUED_SCB;
+
+ /*
+ * We have at least one queued SCB now and we don't have any
+ * SCBs in the list of SCBs awaiting selection. Allocate a
+ * card SCB for the host's SCB and get to work on it.
+ */
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ } else {
+ /* In the non-paging case, the SCBID == hardware SCB index */
+ mov SCBPTR, ARG_1;
+ }
+ or SEQ_FLAGS2, SCB_DMA;
+END_CRITICAL;
+dma_queued_scb:
+ /*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov ARG_1 call dma_scb;
+ /*
+ * Check one last time to see if this SCB was canceled
+ * before we completed the DMA operation. If it was,
+ * the QINFIFO next pointer will not match our saved
+ * value.
+ */
+ mov A, ARG_1;
+BEGIN_CRITICAL;
+ cmp NEXT_QUEUED_SCB, A jne abort_qinscb;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ cmp SCB_TAG, A je . + 2;
+ mvi SCB_MISMATCH call set_seqint;
+ }
+ mov NEXT_QUEUED_SCB, SCB_NEXT;
+ mov SCB_NEXT,WAITING_SCBH;
+ mov WAITING_SCBH, SCBPTR;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ mov NONE, SNSCB_QOFF;
+ } else {
+ inc QINPOS;
+ }
+ and SEQ_FLAGS2, ~SCB_DMA;
+END_CRITICAL;
+start_waiting:
+ /*
+ * Start the first entry on the waiting SCB list.
+ */
+ mov SCBPTR, WAITING_SCBH;
+ call start_selection;
+
+poll_for_selection:
+ /*
+ * Twin channel devices cannot handle things like SELTO
+ * interrupts on the "background" channel. So, while
+ * selecting, keep polling the current channel until
+ * either a selection or reselection occurs.
+ */
+ test SSTAT0, SELDO|SELDI jz poll_for_selection;
+
+selection:
+ /*
+ * We aren't expecting a bus free, so interrupt
+ * the kernel driver if it happens.
+ */
+ mvi CLRSINT1,CLRBUSFREE;
+ if ((ahc->features & AHC_DT) == 0) {
+ or SIMODE1, ENBUSFREE;
+ }
+
+ /*
+ * Guard against a bus free after (re)selection
+ * but prior to enabling the busfree interrupt. SELDI
+ * and SELDO will be cleared in that case.
+ */
+ test SSTAT0, SELDI|SELDO jz bus_free_sel;
+ test SSTAT0,SELDO jnz select_out;
+select_in:
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+ test SSTAT0, TARGET jz initiator_reselect;
+ }
+ mvi CLRSINT0, CLRSELDI;
+
+ /*
+ * We've just been selected. Assert BSY and
+ * setup the phase for receiving messages
+ * from the target.
+ */
+ mvi SCSISIGO, P_MESGOUT|BSYO;
+
+ /*
+ * Setup the DMA for sending the identify and
+ * command information.
+ */
+ mvi SEQ_FLAGS, CMDPHASE_PENDING;
+
+ mov A, TQINPOS;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SHARED_DATA_ADDR call set_32byte_addr;
+ mvi CCSCBCTL, CCSCBRESET;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SHARED_DATA_ADDR call set_32byte_addr;
+ mvi DFCNTRL, FIFORESET;
+ }
+
+ /* Initiator that selected us */
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ /* The Target ID we were selected at */
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ and A, OID, TARGIDIN;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ and A, OID, SCSIID_ULTRA2;
+ } else {
+ and A, OID, SCSIID;
+ }
+ or SAVED_SCSIID, A;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ test SBLKCTL, SELBUSB jz . + 2;
+ or SAVED_SCSIID, TWIN_CHNLB;
+ }
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, SAVED_SCSIID;
+ } else {
+ mov DFDAT, SAVED_SCSIID;
+ }
+
+ /*
+ * If ATN isn't asserted, the target isn't interested
+ * in talking to us. Go directly to bus free.
+ * XXX SCSI-1 may require us to assume lun 0 if
+ * ATN is false.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+
+ /*
+ * Watch ATN closely now as we pull in messages from the
+ * initiator. We follow the guidlines from section 6.5
+ * of the SCSI-2 spec for what messages are allowed when.
+ */
+ call target_inb;
+
+ /*
+ * Our first message must be one of IDENTIFY, ABORT, or
+ * BUS_DEVICE_RESET.
+ */
+ test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, DINDEX;
+
+ /* Remember for disconnection decision */
+ test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
+ /* XXX Honor per target settings too */
+ or SEQ_FLAGS, NO_DISCONNECT;
+
+ test SCSISIGI, ATNI jz ident_messages_done;
+ call target_inb;
+ /*
+ * If this is a tagged request, the tagged message must
+ * immediately follow the identify. We test for a valid
+ * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
+ * < MSG_IGN_WIDE_RESIDUE.
+ */
+ add A, -MSG_SIMPLE_Q_TAG, DINDEX;
+ jnc ident_messages_done_msg_pending;
+ add A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
+ jc ident_messages_done_msg_pending;
+
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+
+ /*
+ * If the initiator doesn't feel like providing a tag number,
+ * we've got a failed selection and must transition to bus
+ * free.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+
+ /*
+ * Store the tag for the host.
+ */
+ call target_inb;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+ mov INITIATOR_TAG, DINDEX;
+ or SEQ_FLAGS, TARGET_CMD_IS_TAGGED;
+
+ident_messages_done:
+ /* Terminate the ident list */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi CCSCBRAM, SCB_LIST_NULL;
+ } else {
+ mvi DFDAT, SCB_LIST_NULL;
+ }
+ or SEQ_FLAGS, TARG_CMD_PENDING;
+ test SEQ_FLAGS2, TARGET_MSG_PENDING
+ jnz target_mesgout_pending;
+ test SCSISIGI, ATNI jnz target_mesgout_continue;
+ jmp target_ITloop;
+
+
+ident_messages_done_msg_pending:
+ or SEQ_FLAGS2, TARGET_MSG_PENDING;
+ jmp ident_messages_done;
+
+ /*
+ * Pushed message loop to allow the kernel to
+ * run it's own target mode message state engine.
+ */
+host_target_message_loop:
+ mvi HOST_MSG_LOOP call set_seqint;
+ cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop;
+ test SSTAT0, SPIORDY jz .;
+ jmp host_target_message_loop;
+ }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+initiator_reselect:
+ /* XXX test for and handle ONE BIT condition */
+ or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ and A, OID, SCSIID_ULTRA2;
+ } else {
+ and A, OID, SCSIID;
+ }
+ or SAVED_SCSIID, A;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ test SBLKCTL, SELBUSB jz . + 2;
+ or SAVED_SCSIID, TWIN_CHNLB;
+ }
+ mvi CLRSINT0, CLRSELDI;
+ jmp ITloop;
+}
+
+abort_qinscb:
+ call add_scb_to_free_list;
+ jmp poll_for_work_loop;
+
+start_selection:
+ /*
+ * If bus reset interrupts have been disabled (from a previous
+ * reset), re-enable them now. Resets are only of interest
+ * when we have outstanding transactions, so we can safely
+ * defer re-enabling the interrupt until, as an initiator,
+ * we start sending out transactions again.
+ */
+ test SIMODE1, ENSCSIRST jnz . + 3;
+ mvi CLRSINT1, CLRSCSIRSTI;
+ or SIMODE1, ENSCSIRST;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+ test SCB_SCSIID, TWIN_CHNLB jz . + 2;
+ or SINDEX, SELBUSB;
+ mov SBLKCTL,SINDEX; /* select channel */
+ }
+initialize_scsiid:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov SCSIID_ULTRA2, SCB_SCSIID;
+ } else if ((ahc->features & AHC_TWIN) != 0) {
+ and SCSIID, TWIN_TID|OID, SCB_SCSIID;
+ } else {
+ mov SCSIID, SCB_SCSIID;
+ }
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ mov SINDEX, SCSISEQ_TEMPLATE;
+ test SCB_CONTROL, TARGET_SCB jz . + 2;
+ or SINDEX, TEMODE;
+ mov SCSISEQ, SINDEX ret;
+ } else {
+ mov SCSISEQ, SCSISEQ_TEMPLATE ret;
+ }
+
+/*
+ * Initialize transfer settings with SCB provided settings.
+ */
+set_transfer_settings:
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ test SCB_CONTROL, ULTRAENB jz . + 2;
+ or SXFRCTL0, FAST20;
+ }
+ /*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov SCSIRATE, SCB_SCSIRATE, 2 ret;
+ } else {
+ mov SCSIRATE, SCB_SCSIRATE ret;
+ }
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * We carefully toggle SPIOEN to allow us to return the
+ * message byte we receive so it can be checked prior to
+ * driving REQ on the bus for the next byte.
+ */
+target_inb:
+ /*
+ * Drive REQ on the bus by enabling SCSI PIO.
+ */
+ or SXFRCTL0, SPIOEN;
+ /* Wait for the byte */
+ test SSTAT0, SPIORDY jz .;
+ /* Prevent our read from triggering another REQ */
+ and SXFRCTL0, ~SPIOEN;
+ /* Save latched contents */
+ mov DINDEX, SCSIDATL ret;
+}
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list. This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH. Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select_out:
+ /* Turn off the selection hardware */
+ and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+ mov SCBPTR, WAITING_SCBH;
+ mov WAITING_SCBH,SCB_NEXT;
+ mov SAVED_SCSIID, SCB_SCSIID;
+ and SAVED_LUN, LID, SCB_LUN;
+ call set_transfer_settings;
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jz initiator_select;
+
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+
+ /*
+ * Put tag in connonical location since not
+ * all connections have an SCB.
+ */
+ mov INITIATOR_TAG, SCB_TARGET_ITAG;
+
+ /*
+ * We've just re-selected an initiator.
+ * Assert BSY and setup the phase for
+ * sending our identify messages.
+ */
+ mvi P_MESGIN|BSYO call change_phase;
+ mvi CLRSINT0, CLRSELDO;
+
+ /*
+ * Start out with a simple identify message.
+ */
+ or SAVED_LUN, MSG_IDENTIFYFLAG call target_outb;
+
+ /*
+ * If we are the result of a tagged command, send
+ * a simple Q tag and the tag id.
+ */
+ test SCB_CONTROL, TAG_ENB jz . + 3;
+ mvi MSG_SIMPLE_Q_TAG call target_outb;
+ mov SCB_TARGET_ITAG call target_outb;
+target_synccmd:
+ /*
+ * Now determine what phases the host wants us
+ * to go through.
+ */
+ mov SEQ_FLAGS, SCB_TARGET_PHASES;
+
+ test SCB_CONTROL, MK_MESSAGE jz target_ITloop;
+ mvi P_MESGIN|BSYO call change_phase;
+ jmp host_target_message_loop;
+target_ITloop:
+ /*
+ * Start honoring ATN signals now that
+ * we properly identified ourselves.
+ */
+ test SCSISIGI, ATNI jnz target_mesgout;
+ test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase;
+ test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase;
+ test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase;
+
+ /*
+ * No more work to do. Either disconnect or not depending
+ * on the state of NO_DISCONNECT.
+ */
+ test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
+ mvi TARG_IMMEDIATE_SCB, SCB_LIST_NULL;
+ call complete_target_cmd;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ }
+ cmp TARG_IMMEDIATE_SCB, SCB_LIST_NULL je .;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov TARG_IMMEDIATE_SCB call dma_scb;
+ call set_transfer_settings;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ jmp target_synccmd;
+
+target_mesgout:
+ mvi SCSISIGO, P_MESGOUT|BSYO;
+target_mesgout_continue:
+ call target_inb;
+target_mesgout_pending:
+ and SEQ_FLAGS2, ~TARGET_MSG_PENDING;
+ /* Local Processing goes here... */
+ jmp host_target_message_loop;
+
+target_disconnect:
+ mvi P_MESGIN|BSYO call change_phase;
+ test SEQ_FLAGS, DPHASE jz . + 2;
+ mvi MSG_SAVEDATAPOINTER call target_outb;
+ mvi MSG_DISCONNECT call target_outb;
+
+target_busfree_wait:
+ /* Wait for preceding I/O session to complete. */
+ test SCSISIGI, ACKI jnz .;
+target_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ clr SCSIBUSL;
+ }
+ clr SCSISIGO;
+ mvi LASTPHASE, P_BUSFREE;
+ call complete_target_cmd;
+ jmp poll_for_work;
+
+target_cmdphase:
+ /*
+ * The target has dropped ATN (doesn't want to abort or BDR)
+ * and we believe this selection to be valid. If the ring
+ * buffer for new commands is full, return busy or queue full.
+ */
+ if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+ and A, HOST_TQINPOS, HS_MAILBOX;
+ } else {
+ mov A, KERNEL_TQINPOS;
+ }
+ cmp TQINPOS, A jne tqinfifo_has_space;
+ mvi P_STATUS|BSYO call change_phase;
+ test SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 3;
+ mvi STATUS_QUEUE_FULL call target_outb;
+ jmp target_busfree_wait;
+ mvi STATUS_BUSY call target_outb;
+ jmp target_busfree_wait;
+tqinfifo_has_space:
+ mvi P_COMMAND|BSYO call change_phase;
+ call target_inb;
+ mov A, DINDEX;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, A;
+ } else {
+ mov DFDAT, A;
+ }
+
+ /*
+ * Determine the number of bytes to read
+ * based on the command group code via table lookup.
+ * We reuse the first 8 bytes of the TARG_SCSIRATE
+ * BIOS array for this table. Count is one less than
+ * the total for the command since we've already fetched
+ * the first byte.
+ */
+ shr A, CMD_GROUP_CODE_SHIFT;
+ add SINDEX, CMDSIZE_TABLE, A;
+ mov A, SINDIR;
+
+ test A, 0xFF jz command_phase_done;
+ or SXFRCTL0, SPIOEN;
+command_loop:
+ test SSTAT0, SPIORDY jz .;
+ cmp A, 1 jne . + 2;
+ and SXFRCTL0, ~SPIOEN; /* Last Byte */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, SCSIDATL;
+ } else {
+ mov DFDAT, SCSIDATL;
+ }
+ dec A;
+ test A, 0xFF jnz command_loop;
+
+command_phase_done:
+ and SEQ_FLAGS, ~CMDPHASE_PENDING;
+ jmp target_ITloop;
+
+target_dphase:
+ /*
+ * Data phases on the bus are from the
+ * perspective of the initiator. The dma
+ * code looks at LASTPHASE to determine the
+ * data direction of the DMA. Toggle it for
+ * target transfers.
+ */
+ xor LASTPHASE, IOI, SCB_TARGET_DATA_DIR;
+ or SCB_TARGET_DATA_DIR, BSYO call change_phase;
+ jmp p_data;
+
+target_sphase:
+ mvi P_STATUS|BSYO call change_phase;
+ mvi LASTPHASE, P_STATUS;
+ mov SCB_SCSI_STATUS call target_outb;
+ /* XXX Watch for ATN or parity errors??? */
+ mvi SCSISIGO, P_MESGIN|BSYO;
+ /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
+ mov ALLZEROS call target_outb;
+ jmp target_busfree_wait;
+
+complete_target_cmd:
+ test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2;
+ mov SCB_TAG jmp complete_post;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Set the valid byte */
+ mvi CCSCBADDR, 24;
+ mov CCSCBRAM, ALLONES;
+ mvi CCHCNT, 28;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL;
+ } else {
+ /* Set the valid byte */
+ or DFCNTRL, FIFORESET;
+ mvi DFWADDR, 3; /* Third 64bit word or byte 24 */
+ mov DFDAT, ALLONES;
+ mvi 28 call set_hcnt;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ call dma_finish;
+ }
+ inc TQINPOS;
+ mvi INTSTAT,CMDCMPLT ret;
+ }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+initiator_select:
+ or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+ /*
+ * As soon as we get a successful selection, the target
+ * should go into the message out phase since we have ATN
+ * asserted.
+ */
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+ mvi SEQ_FLAGS, NO_CDB_SENT;
+ mvi CLRSINT0, CLRSELDO;
+
+ /*
+ * Main loop for information transfer phases. Wait for the
+ * target to assert REQ before checking MSG, C/D and I/O for
+ * the bus phase.
+ */
+mesgin_phasemis:
+ITloop:
+ call phase_lock;
+
+ mov A, LASTPHASE;
+
+ test A, ~P_DATAIN jz p_data;
+ cmp A,P_COMMAND je p_command;
+ cmp A,P_MESGOUT je p_mesgout;
+ cmp A,P_STATUS je p_status;
+ cmp A,P_MESGIN je p_mesgin;
+
+ mvi BAD_PHASE call set_seqint;
+ jmp ITloop; /* Try reading the bus again. */
+
+await_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ mov NONE, SCSIDATL; /* Ack the last byte */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ clr SCSIBUSL; /* Prevent bit leakage durint SELTO */
+ }
+ and SXFRCTL0, ~SPIOEN;
+ test SSTAT1,REQINIT|BUSFREE jz .;
+ test SSTAT1, BUSFREE jnz poll_for_work;
+ mvi MISSED_BUSFREE call set_seqint;
+}
+
+clear_target_state:
+ /*
+ * We assume that the kernel driver may reset us
+ * at any time, even in the middle of a DMA, so
+ * clear DFCNTRL too.
+ */
+ clr DFCNTRL;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+
+ /*
+ * We don't know the target we will connect to,
+ * so default to narrow transfers to avoid
+ * parity problems.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov SCSIRATE, ALLZEROS, 2;
+ } else {
+ clr SCSIRATE;
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ and SXFRCTL0, ~(FAST20);
+ }
+ }
+ mvi LASTPHASE, P_BUSFREE;
+ /* clear target specific flags */
+ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
+
+sg_advance:
+ clr A; /* add sizeof(struct scatter) */
+ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+ adc SCB_RESIDUAL_SGPTR[1],A;
+ adc SCB_RESIDUAL_SGPTR[2],A;
+ adc SCB_RESIDUAL_SGPTR[3],A ret;
+
+if ((ahc->features & AHC_CMD_CHAN) != 0) {
+disable_ccsgen:
+ test CCSGCTL, CCSGEN jz return;
+ test CCSGCTL, CCSGDONE jz .;
+disable_ccsgen_fetch_done:
+ clr CCSGCTL;
+ test CCSGCTL, CCSGEN jnz .;
+ ret;
+idle_loop:
+ /*
+ * Do we need any more segments for this transfer?
+ */
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return;
+
+ /* Did we just finish fetching segs? */
+ cmp CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete;
+
+ /* Are we actively fetching segments? */
+ test CCSGCTL, CCSGEN jnz return;
+
+ /*
+ * Do we have any prefetch left???
+ */
+ cmp CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail;
+
+ /*
+ * Need to fetch segments, but we can only do that
+ * if the command channel is completely idle. Make
+ * sure we don't have an SCB prefetch going on.
+ */
+ test CCSCBCTL, CCSCBEN jnz return;
+
+ /*
+ * We fetch a "cacheline aligned" and sized amount of data
+ * so we don't end up referencing a non-existant page.
+ * Cacheline aligned is in quotes because the kernel will
+ * set the prefetch amount to a reasonable level if the
+ * cacheline size is unknown.
+ */
+ mvi CCHCNT, SG_PREFETCH_CNT;
+ and CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+ bmov CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+ mvi CCSGCTL, CCSGEN|CCSGRESET ret;
+idle_sgfetch_complete:
+ call disable_ccsgen_fetch_done;
+ and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+idle_sg_avail:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /* Does the hardware have space for another SG entry? */
+ test DFSTATUS, PRELOAD_AVAIL jz return;
+ bmov HADDR, CCSGRAM, 7;
+ bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+ }
+ call sg_advance;
+ mov SINDEX, SCB_RESIDUAL_SGPTR[0];
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or SINDEX, LAST_SEG;
+ mov SG_CACHE_PRE, SINDEX;
+ /* Load the segment */
+ or DFCNTRL, PRELOADEN;
+ }
+ ret;
+}
+
+if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+/*
+ * Calculate the trailing portion of this S/G segment that cannot
+ * be transferred using memory write and invalidate PCI transactions.
+ * XXX Can we optimize this for PCI writes only???
+ */
+calc_mwi_residual:
+ /*
+ * If the ending address is on a cacheline boundary,
+ * there is no need for an extra segment.
+ */
+ mov A, HCNT[0];
+ add A, A, HADDR[0];
+ and A, CACHESIZE_MASK;
+ test A, 0xFF jz return;
+
+ /*
+ * If the transfer is less than a cachline,
+ * there is no need for an extra segment.
+ */
+ test HCNT[1], 0xFF jnz calc_mwi_residual_final;
+ test HCNT[2], 0xFF jnz calc_mwi_residual_final;
+ add NONE, INVERTED_CACHESIZE_MASK, HCNT[0];
+ jnc return;
+
+calc_mwi_residual_final:
+ mov MWI_RESIDUAL, A;
+ not A;
+ inc A;
+ add HCNT[0], A;
+ adc HCNT[1], -1;
+ adc HCNT[2], -1 ret;
+}
+
+p_data:
+ test SEQ_FLAGS,NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+ mvi PROTO_VIOLATION call set_seqint;
+p_data_allowed:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+ } else {
+ mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+ }
+ test LASTPHASE, IOI jnz . + 2;
+ or DMAPARAMS, DIRECTION;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* We don't have any valid S/G elements */
+ mvi CCSGADDR, SG_PREFETCH_CNT;
+ }
+ test SEQ_FLAGS, DPHASE jz data_phase_initialize;
+
+ /*
+ * If we re-enter the data phase after going through another
+ * phase, our transfer location has almost certainly been
+ * corrupted by the interveining, non-data, transfers. Ask
+ * the host driver to fix us up based on the transfer residual.
+ */
+ mvi PDATA_REINIT call set_seqint;
+ jmp data_phase_loop;
+
+data_phase_initialize:
+ /* We have seen a data phase for the first time */
+ or SEQ_FLAGS, DPHASE;
+
+ /*
+ * Initialize the DMA address and counter from the SCB.
+ * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG
+ * flag in the highest byte of the data count. We cannot
+ * modify the saved values in the SCB until we see a save
+ * data pointers message.
+ */
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ /* The lowest address byte must be loaded last. */
+ mov SCB_DATACNT[3] call set_hhaddr;
+ }
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SCB_DATAPTR, 7;
+ bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_DATAPTR call bcopy_7;
+ mvi DINDEX, SCB_RESIDUAL_DATACNT + 3;
+ mvi SCB_DATACNT + 3 call bcopy_5;
+ }
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+ call calc_mwi_residual;
+ }
+ and SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID;
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ call set_stcnt_from_hcnt;
+ }
+ }
+
+data_phase_loop:
+ /* Guard against overruns */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds;
+
+ /*
+ * Turn on `Bit Bucket' mode, wait until the target takes
+ * us to another phase, and then notify the host.
+ */
+ and DMAPARAMS, DIRECTION;
+ mov DFCNTRL, DMAPARAMS;
+ or SXFRCTL1,BITBUCKET;
+ if ((ahc->features & AHC_DT) == 0) {
+ test SSTAT1,PHASEMIS jz .;
+ } else {
+ test SCSIPHASE, DATA_PHASE_MASK jnz .;
+ }
+ and SXFRCTL1, ~BITBUCKET;
+ mvi DATA_OVERRUN call set_seqint;
+ jmp ITloop;
+
+data_phase_inbounds:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov SINDEX, SCB_RESIDUAL_SGPTR[0];
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+ or SINDEX, LAST_SEG;
+ mov SG_CACHE_PRE, SINDEX;
+ mov DFCNTRL, DMAPARAMS;
+ultra2_dma_loop:
+ call idle_loop;
+ /*
+ * The transfer is complete if either the last segment
+ * completes or the target changes phase.
+ */
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish;
+ if ((ahc->features & AHC_DT) == 0) {
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ /*
+ * As a target, we control the phases,
+ * so ignore PHASEMIS.
+ */
+ test SSTAT0, TARGET jnz ultra2_dma_loop;
+ }
+ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+ test SSTAT1,PHASEMIS jz ultra2_dma_loop;
+ }
+ } else {
+ test DFCNTRL, SCSIEN jnz ultra2_dma_loop;
+ }
+
+ultra2_dmafinish:
+ /*
+ * The transfer has terminated either due to a phase
+ * change, and/or the completion of the last segment.
+ * We have two goals here. Do as much other work
+ * as possible while the data fifo drains on a read
+ * and respond as quickly as possible to the standard
+ * messages (save data pointers/disconnect and command
+ * complete) that usually follow a data phase.
+ */
+ if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+ /*
+ * On chips with broken auto-flush, start
+ * the flushing process now. We'll poke
+ * the chip from time to time to keep the
+ * flush process going as we complete the
+ * data phase.
+ */
+ or DFCNTRL, FIFOFLUSH;
+ }
+ /*
+ * We assume that, even though data may still be
+ * transferring to the host, that the SCSI side of
+ * the DMA engine is now in a static state. This
+ * allows us to update our notion of where we are
+ * in this transfer.
+ *
+ * If, by chance, we stopped before being able
+ * to fetch additional segments for this transfer,
+ * yet the last S/G was completely exhausted,
+ * call our idle loop until it is able to load
+ * another segment. This will allow us to immediately
+ * pickup on the next segment on the next data phase.
+ *
+ * If we happened to stop on the last segment, then
+ * our residual information is still correct from
+ * the idle loop and there is no need to perform
+ * any fixups.
+ */
+ultra2_ensure_sg:
+ test SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
+ /* Record if we've consumed all S/G entries */
+ test SSTAT2, SHVALID jnz residuals_correct;
+ or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+ jmp residuals_correct;
+
+ultra2_shvalid:
+ test SSTAT2, SHVALID jnz sgptr_fixup;
+ call idle_loop;
+ jmp ultra2_ensure_sg;
+
+sgptr_fixup:
+ /*
+ * Fixup the residual next S/G pointer. The S/G preload
+ * feature of the chip allows us to load two elements
+ * in addition to the currently active element. We
+ * store the bottom byte of the next S/G pointer in
+ * the SG_CACEPTR register so we can restore the
+ * correct value when the DMA completes. If the next
+ * sg ptr value has advanced to the point where higher
+ * bytes in the address have been affected, fix them
+ * too.
+ */
+ test SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+ test SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+ add SCB_RESIDUAL_SGPTR[1], -1;
+ adc SCB_RESIDUAL_SGPTR[2], -1;
+ adc SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+ and SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+ /* We are not the last seg */
+ and SCB_RESIDUAL_DATACNT[3], ~SG_LAST_SEG;
+residuals_correct:
+ /*
+ * Go ahead and shut down the DMA engine now.
+ * In the future, we'll want to handle end of
+ * transfer messages prior to doing this, but this
+ * requires similar restructuring for pre-ULTRA2
+ * controllers.
+ */
+ test DMAPARAMS, DIRECTION jnz ultra2_fifoempty;
+ultra2_fifoflush:
+ if ((ahc->features & AHC_DT) == 0) {
+ if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+ /*
+ * On Rev A of the aic7890, the autoflush
+ * feature doesn't function correctly.
+ * Perform an explicit manual flush. During
+ * a manual flush, the FIFOEMP bit becomes
+ * true every time the PCI FIFO empties
+ * regardless of the state of the SCSI FIFO.
+ * It can take up to 4 clock cycles for the
+ * SCSI FIFO to get data into the PCI FIFO
+ * and for FIFOEMP to de-assert. Here we
+ * guard against this condition by making
+ * sure the FIFOEMP bit stays on for 5 full
+ * clock cycles.
+ */
+ or DFCNTRL, FIFOFLUSH;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ }
+ test DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+ } else {
+ /*
+ * We enable the auto-ack feature on DT capable
+ * controllers. This means that the controller may
+ * have already transferred some overrun bytes into
+ * the data FIFO and acked them on the bus. The only
+ * way to detect this situation is to wait for
+ * LAST_SEG_DONE to come true on a completed transfer
+ * and then test to see if the data FIFO is non-empty.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL
+ jz ultra2_wait_fifoemp;
+ test SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+ /*
+ * FIFOEMP can lag LAST_SEG_DONE. Wait a few
+ * clocks before calling this an overrun.
+ */
+ test DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+ test DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+ test DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+ /* Overrun */
+ jmp data_phase_loop;
+ultra2_wait_fifoemp:
+ test DFSTATUS, FIFOEMP jz .;
+ }
+ultra2_fifoempty:
+ /* Don't clobber an inprogress host data transfer */
+ test DFSTATUS, MREQPEND jnz ultra2_fifoempty;
+ultra2_dmahalt:
+ and DFCNTRL, ~(SCSIEN|HDMAEN);
+ test DFCNTRL, SCSIEN|HDMAEN jnz .;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ /*
+ * Keep HHADDR cleared for future, 32bit addressed
+ * only, DMA operations.
+ *
+ * Due to bayonette style S/G handling, our residual
+ * data must be "fixed up" once the transfer is halted.
+ * Here we fixup the HSHADDR stored in the high byte
+ * of the residual data cnt. By postponing the fixup,
+ * we can batch the clearing of HADDR with the fixup.
+ * If we halted on the last segment, the residual is
+ * already correct. If we are not on the last
+ * segment, copy the high address directly from HSHADDR.
+ * We don't need to worry about maintaining the
+ * SG_LAST_SEG flag as it will always be false in the
+ * case where an update is required.
+ */
+ or DSCOMMAND1, HADDLDSEL0;
+ test SG_CACHE_SHADOW, LAST_SEG jnz . + 2;
+ mov SCB_RESIDUAL_DATACNT[3], SHADDR;
+ clr HADDR;
+ and DSCOMMAND1, ~HADDLDSEL0;
+ }
+ } else {
+ /* If we are the last SG block, tell the hardware. */
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ test MWI_RESIDUAL, 0xFF jnz dma_mid_sg;
+ }
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jz dma_last_sg;
+ if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) {
+ test DMAPARAMS, DIRECTION jz dma_mid_sg;
+ }
+ }
+dma_last_sg:
+ and DMAPARAMS, ~WIDEODD;
+dma_mid_sg:
+ /* Start DMA data transfer. */
+ mov DFCNTRL, DMAPARAMS;
+dma_loop:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ call idle_loop;
+ }
+ test SSTAT0,DMADONE jnz dma_dmadone;
+ test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */
+dma_phasemis:
+ /*
+ * We will be "done" DMAing when the transfer count goes to
+ * zero, or the target changes the phase (in light of this,
+ * it makes sense that the DMA circuitry doesn't ACK when
+ * PHASEMIS is active). If we are doing a SCSI->Host transfer,
+ * the data FIFO should be flushed auto-magically on STCNT=0
+ * or a phase change, so just wait for FIFO empty status.
+ */
+dma_checkfifo:
+ test DFCNTRL,DIRECTION jnz dma_fifoempty;
+dma_fifoflush:
+ test DFSTATUS,FIFOEMP jz dma_fifoflush;
+dma_fifoempty:
+ /* Don't clobber an inprogress host data transfer */
+ test DFSTATUS, MREQPEND jnz dma_fifoempty;
+
+ /*
+ * Now shut off the DMA and make sure that the DMA
+ * hardware has actually stopped. Touching the DMA
+ * counters, etc. while a DMA is active will result
+ * in an ILLSADDR exception.
+ */
+dma_dmadone:
+ and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+ /*
+ * Some revisions of the aic78XX have a problem where, if the
+ * data fifo is full, but the PCI input latch is not empty,
+ * HDMAEN cannot be cleared. The fix used here is to drain
+ * the prefetched but unused data from the data fifo until
+ * there is space for the input latch to drain.
+ */
+ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+ mov NONE, DFDAT;
+ }
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+
+ /* See if we have completed this last segment */
+ test STCNT[0], 0xff jnz data_phase_finish;
+ test STCNT[1], 0xff jnz data_phase_finish;
+ test STCNT[2], 0xff jnz data_phase_finish;
+
+ /*
+ * Advance the scatter-gather pointers if needed
+ */
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ test MWI_RESIDUAL, 0xFF jz no_mwi_resid;
+ /*
+ * Reload HADDR from SHADDR and setup the
+ * count to be the size of our residual.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SHADDR, 4;
+ mov HCNT, MWI_RESIDUAL;
+ bmov HCNT[1], ALLZEROS, 2;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SHADDR call bcopy_4;
+ mov MWI_RESIDUAL call set_hcnt;
+ }
+ clr MWI_RESIDUAL;
+ jmp sg_load_done;
+no_mwi_resid:
+ }
+ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load;
+ or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+ jmp data_phase_finish;
+sg_load:
+ /*
+ * Load the next SG element's data address and length
+ * into the DMA engine. If we don't have hardware
+ * to perform a prefetch, we'll have to fetch the
+ * segment from host memory first.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Wait for the idle loop to complete */
+ test CCSGCTL, CCSGEN jz . + 3;
+ call idle_loop;
+ test CCSGCTL, CCSGEN jnz . - 1;
+ bmov HADDR, CCSGRAM, 7;
+ /*
+ * Workaround for flaky external SCB RAM
+ * on certain aic7895 setups. It seems
+ * unable to handle direct transfers from
+ * S/G ram to certain SCB locations.
+ */
+ mov SINDEX, CCSGRAM;
+ mov SCB_RESIDUAL_DATACNT[3], SINDEX;
+ } else {
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov ALLZEROS call set_hhaddr;
+ }
+ mvi DINDEX, HADDR;
+ mvi SCB_RESIDUAL_SGPTR call bcopy_4;
+
+ mvi SG_SIZEOF call set_hcnt;
+
+ or DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+ call dma_finish;
+
+ mvi DINDEX, HADDR;
+ call dfdat_in_7;
+ mov SCB_RESIDUAL_DATACNT[3], DFDAT;
+ }
+
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+
+ /*
+ * The lowest address byte must be loaded
+ * last as it triggers the computation of
+ * some items in the PCI block. The ULTRA2
+ * chips do this on PRELOAD.
+ */
+ mov HADDR, HADDR;
+ }
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ call calc_mwi_residual;
+ }
+
+ /* Point to the new next sg in memory */
+ call sg_advance;
+
+sg_load_done:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ call set_stcnt_from_hcnt;
+ }
+
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jnz data_phase_loop;
+ }
+ }
+data_phase_finish:
+ /*
+ * If the target has left us in data phase, loop through
+ * the dma code again. In the case of ULTRA2 adapters,
+ * we should only loop if there is a data overrun. For
+ * all other adapters, we'll loop after each S/G element
+ * is loaded as well as if there is an overrun.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SSTAT0, TARGET jnz data_phase_done;
+ }
+ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+ test SSTAT1, REQINIT jz .;
+ if ((ahc->features & AHC_DT) == 0) {
+ test SSTAT1,PHASEMIS jz data_phase_loop;
+ } else {
+ test SCSIPHASE, DATA_PHASE_MASK jnz data_phase_loop;
+ }
+ }
+
+data_phase_done:
+ /*
+ * After a DMA finishes, save the SG and STCNT residuals back into
+ * the SCB. We use STCNT instead of HCNT, since it's a reflection
+ * of how many bytes were transferred on the SCSI (as opposed to the
+ * host) bus.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Kill off any pending prefetch */
+ call disable_ccsgen;
+ }
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ /*
+ * Clear the high address byte so that all other DMA
+ * operations, which use 32bit addressing, can assume
+ * HHADDR is 0.
+ */
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ mov ALLZEROS call set_hhaddr;
+ }
+ }
+
+ /*
+ * Update our residual information before the information is
+ * lost by some other type of SCSI I/O (e.g. PIO). If we have
+ * transferred all data, no update is needed.
+ *
+ */
+ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jnz residual_update_done;
+ if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+ && ahc->pci_cachesize != 0) {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ test MWI_RESIDUAL, 0xFF jz bmov_resid;
+ }
+ mov A, MWI_RESIDUAL;
+ add SCB_RESIDUAL_DATACNT[0], A, STCNT[0];
+ clr A;
+ adc SCB_RESIDUAL_DATACNT[1], A, STCNT[1];
+ adc SCB_RESIDUAL_DATACNT[2], A, STCNT[2];
+ clr MWI_RESIDUAL;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ jmp . + 2;
+bmov_resid:
+ bmov SCB_RESIDUAL_DATACNT, STCNT, 3;
+ }
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_RESIDUAL_DATACNT, STCNT, 3;
+ } else {
+ mov SCB_RESIDUAL_DATACNT[0], STCNT[0];
+ mov SCB_RESIDUAL_DATACNT[1], STCNT[1];
+ mov SCB_RESIDUAL_DATACNT[2], STCNT[2];
+ }
+residual_update_done:
+ /*
+ * Since we've been through a data phase, the SCB_RESID* fields
+ * are now initialized. Clear the full residual flag.
+ */
+ and SCB_SGPTR[0], ~SG_FULL_RESID;
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /* Clear the channel in case we return to data phase later */
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ }
+
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ test SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+ and SEQ_FLAGS, ~DPHASE_PENDING;
+ /*
+ * For data-in phases, wait for any pending acks from the
+ * initiator before changing phase. We only need to
+ * send Ignore Wide Residue messages for data-in phases.
+ */
+ test DFCNTRL, DIRECTION jz target_ITloop;
+ test SSTAT1, REQINIT jnz .;
+ test SCB_LUN, SCB_XFERLEN_ODD jz target_ITloop;
+ test SCSIRATE, WIDEXFER jz target_ITloop;
+ /*
+ * Issue an Ignore Wide Residue Message.
+ */
+ mvi P_MESGIN|BSYO call change_phase;
+ mvi MSG_IGN_WIDE_RESIDUE call target_outb;
+ mvi 1 call target_outb;
+ jmp target_ITloop;
+ } else {
+ jmp ITloop;
+ }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+ test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+ mvi PROTO_VIOLATION call set_seqint;
+p_command_okay:
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov HCNT[0], SCB_CDB_LEN, 1;
+ bmov HCNT[1], ALLZEROS, 2;
+ mvi SG_CACHE_PRE, LAST_SEG;
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT[0], SCB_CDB_LEN, 1;
+ bmov STCNT[1], ALLZEROS, 2;
+ } else {
+ mov STCNT[0], SCB_CDB_LEN;
+ clr STCNT[1];
+ clr STCNT[2];
+ }
+ add NONE, -13, SCB_CDB_LEN;
+ mvi SCB_CDB_STORE jnc p_command_embedded;
+p_command_from_host:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov HADDR[0], SCB_CDB_PTR, 4;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+ } else {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR[0], SCB_CDB_PTR, 4;
+ bmov HCNT, STCNT, 3;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_CDB_PTR call bcopy_4;
+ mov SCB_CDB_LEN call set_hcnt;
+ }
+ mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
+ }
+ jmp p_command_xfer;
+p_command_embedded:
+ /*
+ * The data fifo seems to require 4 byte aligned
+ * transfers from the sequencer. Force this to
+ * be the case by clearing HADDR[0] even though
+ * we aren't going to touch host memory.
+ */
+ clr HADDR[0];
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
+ bmov DFDAT, SCB_CDB_STORE, 12;
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ /*
+ * On the 7895 the data FIFO will
+ * get corrupted if you try to dump
+ * data from external SCB memory into
+ * the FIFO while it is enabled. So,
+ * fill the fifo and then enable SCSI
+ * transfers.
+ */
+ mvi DFCNTRL, (DIRECTION|FIFORESET);
+ } else {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+ }
+ bmov DFDAT, SCB_CDB_STORE, 12;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH);
+ } else {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ } else {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+ call copy_to_fifo_6;
+ call copy_to_fifo_6;
+ or DFCNTRL, FIFOFLUSH;
+ }
+p_command_xfer:
+ and SEQ_FLAGS, ~NO_CDB_SENT;
+ if ((ahc->features & AHC_DT) == 0) {
+ test SSTAT0, SDONE jnz . + 2;
+ test SSTAT1, PHASEMIS jz . - 1;
+ /*
+ * Wait for our ACK to go-away on it's own
+ * instead of being killed by SCSIEN getting cleared.
+ */
+ test SCSISIGI, ACKI jnz .;
+ } else {
+ test DFCNTRL, SCSIEN jnz .;
+ }
+ test SSTAT0, SDONE jnz p_command_successful;
+ /*
+ * Don't allow a data phase if the command
+ * was not fully transferred.
+ */
+ or SEQ_FLAGS, NO_CDB_SENT;
+p_command_successful:
+ and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
+ jmp ITloop;
+
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+p_status_okay:
+ mov SCB_SCSI_STATUS, SCSIDATL;
+ or SCB_CONTROL, STATUS_RCVD;
+ jmp ITloop;
+
+/*
+ * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target. The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte. This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own. If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+ /* Turn on ATN for the retry */
+ if ((ahc->features & AHC_DT) == 0) {
+ or SCSISIGO, ATNO, LASTPHASE;
+ } else {
+ mvi SCSISIGO, ATNO;
+ }
+p_mesgout:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+ test SCB_CONTROL,MK_MESSAGE jnz host_message_loop;
+p_mesgout_identify:
+ or SINDEX, MSG_IDENTIFYFLAG|DISCENB, SAVED_LUN;
+ test SCB_CONTROL, DISCENB jnz . + 2;
+ and SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDATL, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCB_TAG jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ jmp host_message_loop;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDATL, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
+
+p_mesgout_done:
+ mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
+ mov LAST_MSG, MSG_OUT;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
+ jmp ITloop;
+
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+ mvi ACCUM call inb_first; /* read the 1st message byte */
+
+ test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
+ cmp A,MSG_DISCONNECT je mesgin_disconnect;
+ cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
+ cmp ALLZEROS,A je mesgin_complete;
+ cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
+ cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_ign_wide_residue;
+ cmp A,MSG_NOOP je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine. To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop. Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+ mvi HOST_MSG_LOOP call set_seqint;
+ call phase_lock;
+ cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1;
+ jmp host_message_loop;
+
+mesgin_ign_wide_residue:
+if ((ahc->features & AHC_WIDE) != 0) {
+ test SCSIRATE, WIDEXFER jz mesgin_reject;
+ /* Pull the residue byte */
+ mvi ARG_1 call inb_next;
+ cmp ARG_1, 0x01 jne mesgin_reject;
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+ test SCB_LUN, SCB_XFERLEN_ODD jnz mesgin_done;
+ mvi IGN_WIDE_RES call set_seqint;
+ jmp mesgin_done;
+}
+
+mesgin_proto_violation:
+ mvi PROTO_VIOLATION call set_seqint;
+ jmp mesgin_done;
+mesgin_reject:
+ mvi MSG_MESSAGE_REJECT call mk_mesg;
+mesgin_done:
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ jmp ITloop;
+
+/*
+ * We received a "command complete" message. Put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt. Before doing so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information. In the case of a non zero status byte, we
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved. If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
+ * RETURN_1 to SEND_SENSE.
+ */
+mesgin_complete:
+
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again. We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+ /*
+ * If we are identified and have successfully sent the CDB,
+ * any status will do. Optimize this fast path.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+ /*
+ * If the target never sent an identify message but instead went
+ * to mesgin to give an invalid message, let the host abort us.
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+
+ /*
+ * If we recevied good status but never successfully sent the
+ * cdb, abort the command.
+ */
+ test SCB_SCSI_STATUS,0xff jnz complete_accepted;
+ test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+
+complete_accepted:
+ /*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 2;
+ mvi MKMSG_FAILED call set_seqint;
+
+ /*
+ * Check for residuals
+ */
+ test SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
+ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+check_status:
+ test SCB_SCSI_STATUS,0xff jz complete; /* Good Status? */
+upload_scb:
+ or SCB_SGPTR, SG_RESID_VALID;
+ mvi DMAPARAMS, FIFORESET;
+ mov SCB_TAG call dma_scb;
+ test SCB_SCSI_STATUS, 0xff jz complete; /* Just a residual? */
+ mvi BAD_STATUS call set_seqint; /* let driver know */
+ cmp RETURN_1, SEND_SENSE jne complete;
+ call add_scb_to_free_list;
+ jmp await_busfree;
+complete:
+ mov SCB_TAG call complete_post;
+ jmp await_busfree;
+}
+
+complete_post:
+ /* Post the SCBID in SINDEX and issue an interrupt */
+ call add_scb_to_free_list;
+ mov ARG_1, SINDEX;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ mov A, SDSCB_QOFF;
+ } else {
+ mov A, QOUTPOS;
+ }
+ mvi QOUTFIFO_OFFSET call post_byte_setup;
+ mov ARG_1 call post_byte;
+ if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+ inc QOUTPOS;
+ }
+ mvi INTSTAT,CMDCMPLT ret;
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Is it a disconnect message? Set a flag in the SCB to remind us
+ * and await the bus going free. If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
+ */
+mesgin_disconnect:
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte
+ * or we want to abort this command. Either way, the target
+ * should take us to message out phase and then attempt to
+ * disconnect again.
+ * XXX - Wait for more testing.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
+ jnz mesgin_proto_violation;
+ or SCB_CONTROL,DISCONNECTED;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ call add_scb_to_disc_list;
+ }
+ test SCB_CONTROL, TAG_ENB jnz await_busfree;
+ mov ARG_1, SCB_TAG;
+ and SAVED_LUN, LID, SCB_LUN;
+ mov SCB_SCSIID call set_busy_target;
+ jmp await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them. This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible. For chips without S/G pipelining,
+ * we can only ack the message after SHADDR has been saved. On these
+ * chips, SHADDR increments with every bus transaction, even PIO.
+ */
+mesgin_sdptrs:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ test SEQ_FLAGS, DPHASE jz ITloop;
+ } else {
+ test SEQ_FLAGS, DPHASE jz mesgin_done;
+ }
+
+ /*
+ * If we are asked to save our position at the end of the
+ * transfer, just mark us at the end rather than perform a
+ * full save.
+ */
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz mesgin_sdptrs_full;
+ or SCB_SGPTR, SG_LIST_NULL;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ jmp ITloop;
+ } else {
+ jmp mesgin_done;
+ }
+
+mesgin_sdptrs_full:
+
+ /*
+ * The SCB_SGPTR becomes the next one we'll download,
+ * and the SCB_DATAPTR becomes the current SHADDR.
+ * Use the residual number since STCNT is corrupted by
+ * any message transfer.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_DATAPTR, SHADDR, 4;
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ }
+ bmov SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8;
+ } else {
+ mvi DINDEX, SCB_DATAPTR;
+ mvi SHADDR call bcopy_4;
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ mvi SCB_RESIDUAL_DATACNT call bcopy_8;
+ }
+ jmp ITloop;
+
+/*
+ * Restore pointers message? Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest. We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data or command phase.
+ */
+mesgin_rdptrs:
+ and SEQ_FLAGS, ~DPHASE; /*
+ * We'll reload them
+ * the next time through
+ * the dataphase.
+ */
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ jmp mesgin_done;
+
+/*
+ * Index into our Busy Target table. SINDEX and DINDEX are modified
+ * upon return. SCBPTR may be modified by this action.
+ */
+set_busy_target:
+ shr DINDEX, 4, SINDEX;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mov SCBPTR, SAVED_LUN;
+ add DINDEX, SCB_64_BTT;
+ } else {
+ add DINDEX, BUSY_TARGETS;
+ }
+ mov DINDIR, ARG_1 ret;
+
+/*
+ * Identify message? For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+ /*
+ * Determine whether a target is using tagged or non-tagged
+ * transactions by first looking at the transaction stored in
+ * the busy target array. If there is no untagged transaction
+ * for this target or the transaction is for a different lun, then
+ * this must be a tagged transaction.
+ */
+ shr SINDEX, 4, SAVED_SCSIID;
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ add SINDEX, SCB_64_BTT;
+ mov SCBPTR, SAVED_LUN;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ add NONE, -SCB_64_BTT, SINDEX;
+ jc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ add NONE, -(SCB_64_BTT + 16), SINDEX;
+ jnc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ }
+ } else {
+ add SINDEX, BUSY_TARGETS;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ add NONE, -BUSY_TARGETS, SINDEX;
+ jc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ add NONE, -(BUSY_TARGETS + 16), SINDEX;
+ jnc . + 2;
+ mvi INTSTAT, OUT_OF_RANGE;
+ nop;
+ }
+ }
+ mov ARG_1, SINDIR;
+ cmp ARG_1, SCB_LIST_NULL je snoop_tag;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ARG_1 call findSCB;
+ } else {
+ mov SCBPTR, ARG_1;
+ }
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ jmp setup_SCB_id_lun_okay;
+ } else {
+ /*
+ * We only allow one untagged command per-target
+ * at a time. So, if the lun doesn't match, look
+ * for a tag message.
+ */
+ and A, LID, SCB_LUN;
+ cmp SAVED_LUN, A je setup_SCB_id_lun_okay;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ /*
+ * findSCB removes the SCB from the
+ * disconnected list, so we must replace
+ * it there should this SCB be for another
+ * lun.
+ */
+ call cleanup_scb;
+ }
+ }
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB. With SCB paging, we must search for non-tagged
+ * transactions since the SCB may exist in any slot. If we're not
+ * using SCB paging, we can use the tag as the direct index to the
+ * SCB.
+ */
+snoop_tag:
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x80;
+ }
+ mov NONE,SCSIDATL; /* ACK Identify MSG */
+ call phase_lock;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x1;
+ }
+ cmp LASTPHASE, P_MESGIN jne not_found;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x2;
+ }
+ cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mvi ARG_1 call inb_next; /* tag value */
+ mov ARG_1 call findSCB;
+ } else {
+ mvi ARG_1 call inb_next; /* tag value */
+ mov SCBPTR, ARG_1;
+ }
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x4;
+ }
+ mov A, SCB_SCSIID;
+ cmp SAVED_SCSIID, A jne not_found_cleanup_scb;
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x8;
+ }
+setup_SCB_id_okay:
+ and A, LID, SCB_LUN;
+ cmp SAVED_LUN, A jne not_found_cleanup_scb;
+setup_SCB_id_lun_okay:
+ if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x10;
+ }
+ test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
+ and SCB_CONTROL,~DISCONNECTED;
+ test SCB_CONTROL, TAG_ENB jnz setup_SCB_tagged;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mov A, SCBPTR;
+ }
+ mvi ARG_1, SCB_LIST_NULL;
+ mov SAVED_SCSIID call set_busy_target;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mov SCBPTR, A;
+ }
+setup_SCB_tagged:
+ clr SEQ_FLAGS; /* make note of IDENTIFY */
+ call set_transfer_settings;
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ mvi HOST_MSG call mk_mesg;
+ jmp mesgin_done;
+
+not_found_cleanup_scb:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ call cleanup_scb;
+ }
+not_found:
+ mvi NO_MATCH call set_seqint;
+ jmp mesgin_done;
+
+mk_mesg:
+ if ((ahc->features & AHC_DT) == 0) {
+ or SCSISIGO, ATNO, LASTPHASE;
+ } else {
+ mvi SCSISIGO, ATNO;
+ }
+ mov MSG_OUT,SINDEX ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from. So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL. When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called. inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next_wait_perr:
+ mvi PERR_DETECTED call set_seqint;
+ jmp inb_next_wait;
+inb_next:
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+inb_next_wait:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SSTAT1, REQINIT jz inb_next_wait;
+ test SSTAT1, SCSIPERR jnz inb_next_wait_perr;
+inb_next_check_phase:
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+ mov DINDEX,SINDEX;
+ mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/
+inb_last:
+ mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Change to a new phase. If we are changing the state of the I/O signal,
+ * from out to in, wait an additional data release delay before continuing.
+ */
+change_phase:
+ /* Wait for preceeding I/O session to complete. */
+ test SCSISIGI, ACKI jnz .;
+
+ /* Change the phase */
+ and DINDEX, IOI, SCSISIGI;
+ mov SCSISIGO, SINDEX;
+ and A, IOI, SINDEX;
+
+ /*
+ * If the data direction has changed, from
+ * out (initiator driving) to in (target driving),
+ * we must wait at least a data release delay plus
+ * the normal bus settle delay. [SCSI III SPI 10.11.0]
+ */
+ cmp DINDEX, A je change_phase_wait;
+ test SINDEX, IOI jz change_phase_wait;
+ call change_phase_wait;
+change_phase_wait:
+ nop;
+ nop;
+ nop;
+ nop ret;
+
+/*
+ * Send a byte to an initiator in Automatic PIO mode.
+ */
+target_outb:
+ or SXFRCTL0, SPIOEN;
+ test SSTAT0, SPIORDY jz .;
+ mov SCSIDATL, SINDEX;
+ test SSTAT0, SPIORDY jz .;
+ and SXFRCTL0, ~SPIOEN ret;
+}
+
+/*
+ * Locate a disconnected SCB by SCBID. Upon return, SCBPTR and SINDEX will
+ * be set to the position of the SCB. If the SCB cannot be found locally,
+ * it will be paged in from host memory. RETURN_2 stores the address of the
+ * preceding SCB in the disconnected list which can be used to speed up
+ * removal of the found SCB from the disconnected list.
+ */
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+findSCB:
+ mov A, SINDEX; /* Tag passed in SINDEX */
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
+ mov SCBPTR, DISCONNECTED_SCBH; /* Initialize SCBPTR */
+ mvi ARG_2, SCB_LIST_NULL; /* Head of list */
+ jmp findSCB_loop;
+findSCB_next:
+ cmp SCB_NEXT, SCB_LIST_NULL je findSCB_notFound;
+ mov ARG_2, SCBPTR;
+ mov SCBPTR,SCB_NEXT;
+findSCB_loop:
+ cmp SCB_TAG, A jne findSCB_next;
+rem_scb_from_disc_list:
+ cmp ARG_2, SCB_LIST_NULL je rHead;
+ mov DINDEX, SCB_NEXT;
+ mov SINDEX, SCBPTR;
+ mov SCBPTR, ARG_2;
+ mov SCB_NEXT, DINDEX;
+ mov SCBPTR, SINDEX ret;
+rHead:
+ mov DISCONNECTED_SCBH,SCB_NEXT ret;
+END_CRITICAL;
+findSCB_notFound:
+ /*
+ * We didn't find it. Page in the SCB.
+ */
+ mov ARG_1, A; /* Save tag */
+ mov ALLZEROS call get_free_or_disc_scb;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov ARG_1 jmp dma_scb;
+}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR.
+ */
+post_byte_setup:
+ mov ARG_2, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SHARED_DATA_ADDR call set_1byte_addr;
+ mvi CCHCNT, 1;
+ mvi CCSCBCTL, CCSCBRESET ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SHARED_DATA_ADDR call set_1byte_addr;
+ mvi 1 call set_hcnt;
+ mvi DFCNTRL, FIFORESET ret;
+ }
+
+post_byte:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov CCSCBRAM, SINDEX, 1;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL ret;
+ } else {
+ mov DFDAT, SINDEX;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+ }
+
+phase_lock_perr:
+ mvi PERR_DETECTED call set_seqint;
+phase_lock:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SSTAT1, REQINIT jz phase_lock;
+ test SSTAT1, SCSIPERR jnz phase_lock_perr;
+phase_lock_latch_phase:
+ if ((ahc->features & AHC_DT) == 0) {
+ and SCSISIGO, PHASE_MASK, SCSISIGI;
+ }
+ and LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+if ((ahc->features & AHC_CMD_CHAN) == 0) {
+set_hcnt:
+ mov HCNT[0], SINDEX;
+clear_hcnt:
+ clr HCNT[1];
+ clr HCNT[2] ret;
+
+set_stcnt_from_hcnt:
+ mov STCNT[0], HCNT[0];
+ mov STCNT[1], HCNT[1];
+ mov STCNT[2], HCNT[2] ret;
+
+bcopy_8:
+ mov DINDIR, SINDIR;
+bcopy_7:
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR;
+bcopy_5:
+ mov DINDIR, SINDIR;
+bcopy_4:
+ mov DINDIR, SINDIR;
+bcopy_3:
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR ret;
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+ shr ARG_2, 3, A;
+ shl A, 5;
+ jmp set_1byte_addr;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 64byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_64byte_addr:
+ shr ARG_2, 2, A;
+ shl A, 6;
+
+/*
+ * Setup addr assuming that A + (ARG_2 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+ add DINDIR, A, SINDIR;
+ mov A, ARG_2;
+ adc DINDIR, A, SINDIR;
+ clr A;
+ adc DINDIR, A, SINDIR;
+ adc DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch an SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+ mov A, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi HSCB_ADDR call set_64byte_addr;
+ mov CCSCBPTR, SCBPTR;
+ test DMAPARAMS, DIRECTION jz dma_scb_tohost;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ mvi CCHCNT, SCB_DOWNLOAD_SIZE_64;
+ } else {
+ mvi CCHCNT, SCB_DOWNLOAD_SIZE;
+ }
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+ jmp dma_scb_finish;
+dma_scb_tohost:
+ mvi CCHCNT, SCB_UPLOAD_SIZE;
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ } else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
+ mvi CCSCBCTL, CCARREN|CCSCBRESET;
+ cmp CCSCBCTL, ARRDONE|CCARREN jne .;
+ mvi CCHCNT, SCB_UPLOAD_SIZE;
+ mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
+ } else {
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+ }
+dma_scb_finish:
+ clr CCSCBCTL;
+ test CCSCBCTL, CCARREN|CCSCBEN jnz .;
+ ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi HSCB_ADDR call set_64byte_addr;
+ mvi SCB_DOWNLOAD_SIZE call set_hcnt;
+ mov DFCNTRL, DMAPARAMS;
+ test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
+ /* Fill it with the SCB data */
+copy_scb_tofifo:
+ mvi SINDEX, SCB_BASE;
+ add A, SCB_DOWNLOAD_SIZE, SINDEX;
+copy_scb_tofifo_loop:
+ call copy_to_fifo_8;
+ cmp SINDEX, A jne copy_scb_tofifo_loop;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+dma_scb_fromhost:
+ mvi DINDEX, SCB_BASE;
+ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+ /*
+ * The PCI module will only issue a PCI
+ * retry if the data FIFO is empty. If the
+ * host disconnects in the middle of a
+ * transfer, we must empty the fifo of all
+ * available data to force the chip to
+ * continue the transfer. This does not
+ * happen for SCSI transfers as the SCSI module
+ * will drain the FIFO as data are made available.
+ * When the hang occurs, we know that a multiple
+ * of 8 bytes is in the FIFO because the PCI
+ * module has an 8 byte input latch that only
+ * dumps to the FIFO when HCNT == 0 or the
+ * latch is full.
+ */
+ clr A;
+ /* Wait for at least 8 bytes of data to arrive. */
+dma_scb_hang_fifo:
+ test DFSTATUS, FIFOQWDEMP jnz dma_scb_hang_fifo;
+dma_scb_hang_wait:
+ test DFSTATUS, MREQPEND jnz dma_scb_hang_wait;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ /*
+ * The PCI module no longer intends to perform
+ * a PCI transaction. Drain the fifo.
+ */
+dma_scb_hang_dma_drain_fifo:
+ not A, HCNT;
+ add A, SCB_DOWNLOAD_SIZE+SCB_BASE+1;
+ and A, ~0x7;
+ mov DINDIR,DFDAT;
+ cmp DINDEX, A jne . - 1;
+ cmp DINDEX, SCB_DOWNLOAD_SIZE+SCB_BASE
+ je dma_finish_nowait;
+ /* Restore A as the lines left to transfer. */
+ add A, -SCB_BASE, DINDEX;
+ shr A, 3;
+ jmp dma_scb_hang_fifo;
+dma_scb_hang_dma_done:
+ and DFCNTRL, ~HDMAEN;
+ test DFCNTRL, HDMAEN jnz .;
+ add SEQADDR0, A;
+ } else {
+ call dma_finish;
+ }
+ call dfdat_in_8;
+ call dfdat_in_8;
+ call dfdat_in_8;
+dfdat_in_8:
+ mov DINDIR,DFDAT;
+dfdat_in_7:
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+dfdat_in_2:
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT ret;
+ }
+
+copy_to_fifo_8:
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+copy_to_fifo_6:
+ mov DFDAT,SINDIR;
+copy_to_fifo_5:
+ mov DFDAT,SINDIR;
+copy_to_fifo_4:
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+ test DFSTATUS,HDONE jz dma_finish;
+dma_finish_nowait:
+ /* Turn off DMA */
+ and DFCNTRL, ~HDMAEN;
+ test DFCNTRL, HDMAEN jnz .;
+ ret;
+
+/*
+ * Restore an SCB that failed to match an incoming reselection
+ * to the correct/safe state. If the SCB is for a disconnected
+ * transaction, it must be returned to the disconnected list.
+ * If it is not in the disconnected state, it must be free.
+ */
+cleanup_scb:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ test SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list;
+ }
+add_scb_to_free_list:
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+ mov SCB_NEXT, FREE_SCBH;
+ mvi SCB_TAG, SCB_LIST_NULL;
+ mov FREE_SCBH, SCBPTR ret;
+END_CRITICAL;
+ } else {
+ mvi SCB_TAG, SCB_LIST_NULL ret;
+ }
+
+if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+set_hhaddr:
+ or DSCOMMAND1, HADDLDSEL0;
+ and HADDR, SG_HIGH_ADDR_BITS, SINDEX;
+ and DSCOMMAND1, ~HADDLDSEL0 ret;
+}
+
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+get_free_or_disc_scb:
+BEGIN_CRITICAL;
+ cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+ mvi NO_FREE_SCB call set_seqint;
+ mvi SINDEX, SCB_LIST_NULL ret;
+dequeue_disc_scb:
+ mov SCBPTR, DISCONNECTED_SCBH;
+ mov DISCONNECTED_SCBH, SCB_NEXT;
+END_CRITICAL;
+ mvi DMAPARAMS, FIFORESET;
+ mov SCB_TAG jmp dma_scb;
+BEGIN_CRITICAL;
+dequeue_free_scb:
+ mov SCBPTR, FREE_SCBH;
+ mov FREE_SCBH, SCB_NEXT ret;
+END_CRITICAL;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list. This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+BEGIN_CRITICAL;
+ mov SCB_NEXT, DISCONNECTED_SCBH;
+ mov DISCONNECTED_SCBH, SCBPTR ret;
+END_CRITICAL;
+}
+set_seqint:
+ mov INTSTAT, SINDEX;
+ nop;
+return:
+ ret;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
new file mode 100644
index 000000000000..468d612a44f6
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
@@ -0,0 +1,306 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#17 $
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * The instruction set of the 93C66/56/46/26/06 chips are as follows:
+ *
+ * Start OP *
+ * Function Bit Code Address** Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must precede
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ * **Note: There are 8 address bits for the 93C56/66 chips unlike
+ * the 93C46/26/06 chips which have 6 address bits.
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively). While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+/*
+ * Right now, we only have to read the SEEPROM. But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+ uint8_t len;
+ uint8_t bits[9];
+} seeprom_read = {3, {1, 1, 0}};
+
+static struct seeprom_cmd seeprom_ewen = {9, {1, 0, 0, 1, 1, 0, 0, 0, 0}};
+static struct seeprom_cmd seeprom_ewds = {9, {1, 0, 0, 0, 0, 0, 0, 0, 0}};
+static struct seeprom_cmd seeprom_write = {3, {1, 0, 1}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy) \
+ while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) { \
+ ; /* Do nothing */ \
+ } \
+ (void)SEEPROM_INB(sd); /* Clear clock */
+
+/*
+ * Send a START condition and the given command
+ */
+static void
+send_seeprom_cmd(struct seeprom_descriptor *sd, struct seeprom_cmd *cmd)
+{
+ uint8_t temp;
+ int i = 0;
+
+ /* Send chip select for one clock cycle. */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+
+ for (i = 0; i < cmd->len; i++) {
+ if (cmd->bits[i] != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if (cmd->bits[i] != 0)
+ temp ^= sd->sd_DO;
+ }
+}
+
+/*
+ * Clear CS put the chip in the reset state, where it can wait for new commands.
+ */
+static void
+reset_seeprom(struct seeprom_descriptor *sd)
+{
+ uint8_t temp;
+
+ temp = sd->sd_MS;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+}
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ int i = 0;
+ u_int k = 0;
+ uint16_t v;
+ uint8_t temp;
+
+ /*
+ * Read the requested registers of the seeprom. The loop
+ * will range from 0 to count-1.
+ */
+ for (k = start_addr; k < count + start_addr; k++) {
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ send_seeprom_cmd(sd, &seeprom_read);
+
+ /* Send the 6 or 8 bit address (MSB first, LSB last). */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ for (i = (sd->sd_chip - 1); i >= 0; i--) {
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ v = 0;
+ for (i = 16; i >= 0; i--) {
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ v <<= 1;
+ if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+ v |= 1;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ }
+
+ buf[k - start_addr] = v;
+
+ /* Reset the chip select for the next command cycle. */
+ reset_seeprom(sd);
+ }
+#ifdef AHC_DUMP_EEPROM
+ printf("\nSerial EEPROM:\n\t");
+ for (k = 0; k < count; k = k + 1) {
+ if (((k % 8) == 0) && (k != 0)) {
+ printf ("\n\t");
+ }
+ printf (" 0x%x", buf[k]);
+ }
+ printf ("\n");
+#endif
+ return (1);
+}
+
+/*
+ * Write the serial EEPROM and return 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count)
+{
+ uint16_t v;
+ uint8_t temp;
+ int i, k;
+
+ /* Place the chip into write-enable mode */
+ send_seeprom_cmd(sd, &seeprom_ewen);
+ reset_seeprom(sd);
+
+ /* Write all requested data out to the seeprom. */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ for (k = start_addr; k < count + start_addr; k++) {
+ /* Send the write command */
+ send_seeprom_cmd(sd, &seeprom_write);
+
+ /* Send the 6 or 8 bit address (MSB first). */
+ for (i = (sd->sd_chip - 1); i >= 0; i--) {
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /* Write the 16 bit value, MSB first */
+ v = buf[k - start_addr];
+ for (i = 15; i >= 0; i--) {
+ if ((v & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((v & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /* Wait for the chip to complete the write */
+ temp = sd->sd_MS;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ temp = sd->sd_MS ^ sd->sd_CS;
+ do {
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ } while ((SEEPROM_DATA_INB(sd) & sd->sd_DI) == 0);
+
+ reset_seeprom(sd);
+ }
+
+ /* Put the chip back into write-protect mode */
+ send_seeprom_cmd(sd, &seeprom_ewds);
+ reset_seeprom(sd);
+
+ return (1);
+}
+
+int
+ahc_verify_cksum(struct seeprom_config *sc)
+{
+ int i;
+ int maxaddr;
+ uint32_t checksum;
+ uint16_t *scarray;
+
+ maxaddr = (sizeof(*sc)/2) - 1;
+ checksum = 0;
+ scarray = (uint16_t *)sc;
+
+ for (i = 0; i < maxaddr; i++)
+ checksum = checksum + scarray[i];
+ if (checksum == 0
+ || (checksum & 0xFFFF) != sc->checksum) {
+ return (0);
+ } else {
+ return(1);
+ }
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.h b/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
new file mode 100644
index 000000000000..859c43ccdd46
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
@@ -0,0 +1,102 @@
+/*
+ * Interface to the 93C46/56 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers. It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995, 2000 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.h#12 $
+ *
+ * $FreeBSD$
+ */
+#ifndef _AIC7XXX_93CX6_H_
+#define _AIC7XXX_93CX6_H_
+
+typedef enum {
+ C46 = 6,
+ C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+ struct ahc_softc *sd_ahc;
+ u_int sd_control_offset;
+ u_int sd_status_offset;
+ u_int sd_dataout_offset;
+ seeprom_chip_t sd_chip;
+ uint16_t sd_MS;
+ uint16_t sd_RDY;
+ uint16_t sd_CS;
+ uint16_t sd_CK;
+ uint16_t sd_DO;
+ uint16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf. The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset. The following parameters are
+ * also passed in:
+ *
+ * CS - Chip select
+ * CK - Clock
+ * DO - Data out
+ * DI - Data in
+ * RDY - SEEPROM ready
+ * MS - Memory port mode select
+ *
+ * A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define SEEPROM_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_control_offset)
+#define SEEPROM_OUTB(sd, value) \
+do { \
+ ahc_outb(sd->sd_ahc, sd->sd_control_offset, value); \
+ ahc_flush_device_writes(sd->sd_ahc); \
+} while(0)
+
+#define SEEPROM_STATUS_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_status_offset)
+#define SEEPROM_DATA_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
+
+int ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int ahc_verify_cksum(struct seeprom_config *sc);
+
+#endif /* _AIC7XXX_93CX6_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
new file mode 100644
index 000000000000..9a6b4a570aa7
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -0,0 +1,7451 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#134 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/****************************** Softc Data ************************************/
+struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahc_chip_names[] =
+{
+ "NONE",
+ "aic7770",
+ "aic7850",
+ "aic7855",
+ "aic7859",
+ "aic7860",
+ "aic7870",
+ "aic7880",
+ "aic7895",
+ "aic7895C",
+ "aic7890/91",
+ "aic7896/97",
+ "aic7892",
+ "aic7899"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahc_hard_error_entry {
+ uint8_t errno;
+ char *errmesg;
+};
+
+static struct ahc_hard_error_entry ahc_hard_errors[] = {
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referrenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Parity Error" },
+ { DPARERR, "Data-path Parity Error" },
+ { MPARERR, "Scratch or SCB Memory Parity Error" },
+ { PCIERRSTAT, "PCI Error detected" },
+ { CIOPARERR, "CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahc_hard_errors);
+
+static struct ahc_phase_table_entry ahc_phase_table[] =
+{
+ { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
+ { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
+ { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
+ { P_COMMAND, MSG_NOOP, "in Command phase" },
+ { P_MESGOUT, MSG_NOOP, "in Message-out phase" },
+ { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { P_BUSFREE, MSG_NOOP, "while idle" },
+ { 0, MSG_NOOP, "in unknown phase" }
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahc_phase_table) - 1;
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
+ */
+static struct ahc_syncrate ahc_syncrates[] =
+{
+ /* ultra2 fast/ultra period rate */
+ { 0x42, 0x000, 9, "80.0" },
+ { 0x03, 0x000, 10, "40.0" },
+ { 0x04, 0x000, 11, "33.0" },
+ { 0x05, 0x100, 12, "20.0" },
+ { 0x06, 0x110, 15, "16.0" },
+ { 0x07, 0x120, 18, "13.4" },
+ { 0x08, 0x000, 25, "10.0" },
+ { 0x19, 0x010, 31, "8.0" },
+ { 0x1a, 0x020, 37, "6.67" },
+ { 0x1b, 0x030, 43, "5.7" },
+ { 0x1c, 0x040, 50, "5.0" },
+ { 0x00, 0x050, 56, "4.4" },
+ { 0x00, 0x060, 62, "4.0" },
+ { 0x00, 0x070, 68, "3.6" },
+ { 0x00, 0x000, 0, NULL }
+};
+
+/* Our Sequencer Program */
+#include "aic7xxx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void ahc_force_renegotiation(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static struct ahc_tmode_tstate*
+ ahc_alloc_tstate(struct ahc_softc *ahc,
+ u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void ahc_free_tstate(struct ahc_softc *ahc,
+ u_int scsi_id, char channel, int force);
+#endif
+static struct ahc_syncrate*
+ ahc_devlimited_syncrate(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *,
+ u_int *period,
+ u_int *ppr_options,
+ role_t role);
+static void ahc_update_pending_scbs(struct ahc_softc *ahc);
+static void ahc_fetch_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_scb_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+static void ahc_assert_atn(struct ahc_softc *ahc);
+static void ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+static void ahc_build_transfer_msg(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_construct_sdtr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int period, u_int offset);
+static void ahc_construct_wdtr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int bus_width);
+static void ahc_construct_ppr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int bus_width, u_int ppr_options);
+static void ahc_clear_msg_state(struct ahc_softc *ahc);
+static void ahc_handle_proto_violation(struct ahc_softc *ahc);
+static void ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+ AHCMSG_1B,
+ AHCMSG_2B,
+ AHCMSG_EXT
+} ahc_msgtype;
+static int ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+ u_int msgval, int full);
+static int ahc_parse_msg(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static int ahc_handle_msg_reject(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
+static void ahc_handle_devreset(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ cam_status status, char *message,
+ int verbose_level);
+#ifdef AHC_TARGET_MODE
+static void ahc_setup_target_msgin(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+#endif
+
+static bus_dmamap_callback_t ahc_dmamap_cb;
+static void ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int ahc_init_scbdata(struct ahc_softc *ahc);
+static void ahc_fini_scbdata(struct ahc_softc *ahc);
+static void ahc_qinfifo_requeue(struct ahc_softc *ahc,
+ struct scb *prev_scb,
+ struct scb *scb);
+static int ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+ u_int prev, u_int scbptr);
+static void ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int ahc_rem_wscb(struct ahc_softc *ahc,
+ u_int scbpos, u_int prev);
+static void ahc_reset_current_bus(struct ahc_softc *ahc);
+#ifdef AHC_DUMP_SEQ
+static void ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static int ahc_loadseq(struct ahc_softc *ahc);
+static int ahc_check_patch(struct ahc_softc *ahc,
+ struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr);
+static void ahc_download_instr(struct ahc_softc *ahc,
+ u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void ahc_queue_lstate_event(struct ahc_softc *ahc,
+ struct ahc_tmode_lstate *lstate,
+ u_int initiator_id,
+ u_int event_type,
+ u_int event_arg);
+static void ahc_update_scsiid(struct ahc_softc *ahc,
+ u_int targid_mask);
+static int ahc_handle_target_cmd(struct ahc_softc *ahc,
+ struct target_cmd *cmd);
+#endif
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahc_restart(struct ahc_softc *ahc)
+{
+
+ ahc_pause(ahc);
+
+ /* No more pending messages. */
+ ahc_clear_msg_state(ahc);
+
+ ahc_outb(ahc, SCSISIGO, 0); /* De-assert BSY */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ ahc_outb(ahc, LASTPHASE, P_BUSFREE);
+ ahc_outb(ahc, SAVED_SCSIID, 0xFF);
+ ahc_outb(ahc, SAVED_LUN, 0xFF);
+
+ /*
+ * Ensure that the sequencer's idea of TQINPOS
+ * matches our own. The sequencer increments TQINPOS
+ * only after it sees a DMA complete and a reset could
+ * occur before the increment leaving the kernel to believe
+ * the command arrived but the sequencer to not.
+ */
+ ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+ /* Always allow reselection */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Ensure that no DMA operations are in progress */
+ ahc_outb(ahc, CCSCBCNT, 0);
+ ahc_outb(ahc, CCSGCTL, 0);
+ ahc_outb(ahc, CCSCBCTL, 0);
+ }
+ /*
+ * If we were in the process of DMA'ing SCB data into
+ * an SCB, replace that SCB on the free list. This prevents
+ * an SCB leak.
+ */
+ if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+ ahc_add_curscb_to_free_list(ahc);
+ ahc_outb(ahc, SEQ_FLAGS2,
+ ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
+ }
+ ahc_outb(ahc, MWI_RESIDUAL, 0);
+ ahc_outb(ahc, SEQCTL, ahc->seqctl);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+ ahc_unpause(ahc);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ u_int scb_index;
+
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+ while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+
+ scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+ if ((ahc->qoutfifonext & 0x03) == 0x03) {
+ u_int modnext;
+
+ /*
+ * Clear 32bits of QOUTFIFO at a time
+ * so that we don't clobber an incoming
+ * byte DMA to the array on architectures
+ * that only support 32bit load and store
+ * operations.
+ */
+ modnext = ahc->qoutfifonext & ~0x3;
+ *((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ /*offset*/modnext, /*len*/4,
+ BUS_DMASYNC_PREREAD);
+ }
+ ahc->qoutfifonext++;
+
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s: WARNING no command for scb %d "
+ "(cmdcmplt)\nQOUTPOS = %d\n",
+ ahc_name(ahc), scb_index,
+ (ahc->qoutfifonext - 1) & 0xFF);
+ continue;
+ }
+
+ /*
+ * Save off the residual
+ * if there is one.
+ */
+ ahc_update_residual(ahc, scb);
+ ahc_done(ahc, scb);
+ }
+}
+
+void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
+}
+
+void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
+{
+ struct scb *scb;
+
+ if (ahc->untagged_queue_lock != 0)
+ return;
+
+ if ((scb = TAILQ_FIRST(queue)) != NULL
+ && (scb->flags & SCB_ACTIVE) == 0) {
+ scb->flags |= SCB_ACTIVE;
+ ahc_queue_scb(ahc, scb);
+ }
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
+{
+ /*
+ * We upset the sequencer :-(
+ * Lookup the error message
+ */
+ int i;
+ int error;
+
+ error = ahc_inb(ahc, ERROR);
+ for (i = 0; error != 1 && i < num_errors; i++)
+ error >>= 1;
+ printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
+ ahc_name(ahc), ahc_hard_errors[i].errmesg,
+ ahc_inb(ahc, SEQADDR0) |
+ (ahc_inb(ahc, SEQADDR1) << 8));
+
+ ahc_dump_card_state(ahc);
+
+ /* Tell everyone that this HBA is no longer available */
+ ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_NO_HBA);
+
+ /* Disable all interrupt sources by resetting the controller */
+ ahc_shutdown(ahc);
+}
+
+void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
+{
+ struct scb *scb;
+ struct ahc_devinfo devinfo;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+
+ /*
+ * Clear the upper byte that holds SEQINT status
+ * codes and clear the SEQINT bit. We will unpause
+ * the sequencer, if appropriate, after servicing
+ * the request.
+ */
+ ahc_outb(ahc, CLRINT, CLRSEQINT);
+ switch (intstat & SEQINT_MASK) {
+ case BAD_STATUS:
+ {
+ u_int scb_index;
+ struct hardware_scb *hscb;
+
+ /*
+ * Set the default return value to 0 (don't
+ * send sense). The sense code will change
+ * this if needed.
+ */
+ ahc_outb(ahc, RETURN_1, 0);
+
+ /*
+ * The sequencer will notify us when a command
+ * has an error that would be of interest to
+ * the kernel. This allows us to leave the sequencer
+ * running in the common case of command completes
+ * without error. The sequencer will already have
+ * dma'd the SCB back up to us, so we can reference
+ * the in kernel copy directly.
+ */
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("ahc_intr - referenced scb "
+ "not valid during seqint 0x%x scb(%d)\n",
+ intstat, scb_index);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ goto unpause;
+ }
+
+ hscb = scb->hscb;
+
+ /* Don't want to clobber the original sense code */
+ if ((scb->flags & SCB_SENSE) != 0) {
+ /*
+ * Clear the SCB_SENSE Flag and have
+ * the sequencer do a normal command
+ * complete.
+ */
+ scb->flags &= ~SCB_SENSE;
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ break;
+ }
+ ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+ /* Freeze the queue until the client sees the error. */
+ ahc_freeze_devq(ahc, scb);
+ ahc_freeze_scb(scb);
+ ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+ switch (hscb->shared_data.status.scsi_status) {
+ case SCSI_STATUS_OK:
+ printf("%s: Interrupted for staus of 0???\n",
+ ahc_name(ahc));
+ break;
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ {
+ struct ahc_dma_seg *sg;
+ struct scsi_sense *sc;
+ struct ahc_initiator_tinfo *targ_info;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_transinfo *tinfo;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_SENSE) {
+ ahc_print_path(ahc, scb);
+ printf("SCB %d: requests Check Status\n",
+ scb->hscb->tag);
+ }
+#endif
+
+ if (ahc_perform_autosense(scb) == 0)
+ break;
+
+ targ_info = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ sg = scb->sg_list;
+ sc = (struct scsi_sense *)(&hscb->shared_data.cdb);
+ /*
+ * Save off the residual if there is one.
+ */
+ ahc_update_residual(ahc, scb);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_SENSE) {
+ ahc_print_path(ahc, scb);
+ printf("Sending Sense\n");
+ }
+#endif
+ sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+ sg->len = ahc_get_sense_bufsize(ahc, scb);
+ sg->len |= AHC_DMA_LAST_SEG;
+
+ /* Fixup byte order */
+ sg->addr = ahc_htole32(sg->addr);
+ sg->len = ahc_htole32(sg->len);
+
+ sc->opcode = REQUEST_SENSE;
+ sc->byte2 = 0;
+ if (tinfo->protocol_version <= SCSI_REV_2
+ && SCB_GET_LUN(scb) < 8)
+ sc->byte2 = SCB_GET_LUN(scb) << 5;
+ sc->unused[0] = 0;
+ sc->unused[1] = 0;
+ sc->length = sg->len;
+ sc->control = 0;
+
+ /*
+ * We can't allow the target to disconnect.
+ * This will be an untagged transaction and
+ * having the target disconnect will make this
+ * transaction indestinguishable from outstanding
+ * tagged transactions.
+ */
+ hscb->control = 0;
+
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate. Unit attention
+ * errors will be reported before any data
+ * phases occur.
+ */
+ if (ahc_get_residual(scb)
+ == ahc_get_transfer_length(scb)) {
+ ahc_update_neg_request(ahc, &devinfo,
+ tstate, targ_info,
+ AHC_NEG_IF_NON_ASYNC);
+ }
+ if (tstate->auto_negotiate & devinfo.target_mask) {
+ hscb->control |= MK_MESSAGE;
+ scb->flags &= ~SCB_NEGOTIATE;
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ }
+ hscb->cdb_len = sizeof(*sc);
+ hscb->dataptr = sg->addr;
+ hscb->datacnt = sg->len;
+ hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+ hscb->sgptr = ahc_htole32(hscb->sgptr);
+ scb->sg_count = 1;
+ scb->flags |= SCB_SENSE;
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ ahc_outb(ahc, RETURN_1, SEND_SENSE);
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ ahc_scb_timer_reset(scb, 5 * 1000000);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case NO_MATCH:
+ {
+ /* Ensure we don't leave the selection hardware on */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ printf("%s:%c:%d: no active SCB for reconnecting "
+ "target - issuing BUS DEVICE RESET\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target);
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+ ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n",
+ ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+ ahc_index_busy_tcl(ahc,
+ BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+ ahc_inb(ahc, SAVED_LUN))),
+ ahc_inb(ahc, SINDEX));
+ printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+ ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+ ahc_inb(ahc, SCB_CONTROL));
+ printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+ ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+ printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+ printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+ ahc_dump_card_state(ahc);
+ ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_assert_atn(ahc);
+ break;
+ }
+ case SEND_REJECT:
+ {
+ u_int rejbyte = ahc_inb(ahc, ACCUM);
+ printf("%s:%c:%d: Warning - unknown message received from "
+ "target (0x%x). Rejecting\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+ break;
+ }
+ case PROTO_VIOLATION:
+ {
+ ahc_handle_proto_violation(ahc);
+ break;
+ }
+ case IGN_WIDE_RES:
+ ahc_handle_ign_wide_residue(ahc, &devinfo);
+ break;
+ case PDATA_REINIT:
+ ahc_reinitialize_dataptrs(ahc);
+ break;
+ case BAD_PHASE:
+ {
+ u_int lastphase;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ printf("%s:%c:%d: unknown scsi bus phase %x, "
+ "lastphase = 0x%x. Attempting to continue\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ lastphase, ahc_inb(ahc, SCSISIGI));
+ break;
+ }
+ case MISSED_BUSFREE:
+ {
+ u_int lastphase;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ printf("%s:%c:%d: Missed busfree. "
+ "Lastphase = 0x%x, Curphase = 0x%x\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ lastphase, ahc_inb(ahc, SCSISIGI));
+ ahc_restart(ahc);
+ return;
+ }
+ case HOST_MSG_LOOP:
+ {
+ /*
+ * The sequencer has encountered a message phase
+ * that requires host assistance for completion.
+ * While handling the message phase(s), we will be
+ * notified by the sequencer after each byte is
+ * transfered so we can track bus phase changes.
+ *
+ * If this is the first time we've seen a HOST_MSG_LOOP
+ * interrupt, initialize the state of the host message
+ * loop.
+ */
+ if (ahc->msg_type == MSG_TYPE_NONE) {
+ struct scb *scb;
+ u_int scb_index;
+ u_int bus_phase;
+
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ if (bus_phase != P_MESGIN
+ && bus_phase != P_MESGOUT) {
+ printf("ahc_intr: HOST_MSG_LOOP bad "
+ "phase 0x%x\n",
+ bus_phase);
+ /*
+ * Probably transitioned to bus free before
+ * we got here. Just punt the message.
+ */
+ ahc_clear_intstat(ahc);
+ ahc_restart(ahc);
+ return;
+ }
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (devinfo.role == ROLE_INITIATOR) {
+ if (scb == NULL)
+ panic("HOST_MSG_LOOP with "
+ "invalid SCB %x\n", scb_index);
+
+ if (bus_phase == P_MESGOUT)
+ ahc_setup_initiator_msgout(ahc,
+ &devinfo,
+ scb);
+ else {
+ ahc->msg_type =
+ MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msgin_index = 0;
+ }
+ }
+#ifdef AHC_TARGET_MODE
+ else {
+ if (bus_phase == P_MESGOUT) {
+ ahc->msg_type =
+ MSG_TYPE_TARGET_MSGOUT;
+ ahc->msgin_index = 0;
+ }
+ else
+ ahc_setup_target_msgin(ahc,
+ &devinfo,
+ scb);
+ }
+#endif
+ }
+
+ ahc_handle_message_phase(ahc);
+ break;
+ }
+ case PERR_DETECTED:
+ {
+ /*
+ * If we've cleared the parity error interrupt
+ * but the sequencer still believes that SCSIPERR
+ * is true, it must be that the parity error is
+ * for the currently presented byte on the bus,
+ * and we are not in a phase (data-in) where we will
+ * eventually ack this byte. Ack the byte and
+ * throw it away in the hope that the target will
+ * take us to message out to deliver the appropriate
+ * error message.
+ */
+ if ((intstat & SCSIINT) == 0
+ && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
+
+ if ((ahc->features & AHC_DT) == 0) {
+ u_int curphase;
+
+ /*
+ * The hardware will only let you ack bytes
+ * if the expected phase in SCSISIGO matches
+ * the current phase. Make sure this is
+ * currently the case.
+ */
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ ahc_outb(ahc, LASTPHASE, curphase);
+ ahc_outb(ahc, SCSISIGO, curphase);
+ }
+ if ((ahc_inb(ahc, SCSISIGI) & (CDI|MSGI)) == 0) {
+ int wait;
+
+ /*
+ * In a data phase. Faster to bitbucket
+ * the data than to individually ack each
+ * byte. This is also the only strategy
+ * that will work with AUTOACK enabled.
+ */
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) | BITBUCKET);
+ wait = 5000;
+ while (--wait != 0) {
+ if ((ahc_inb(ahc, SCSISIGI)
+ & (CDI|MSGI)) != 0)
+ break;
+ ahc_delay(100);
+ }
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ if (wait == 0) {
+ struct scb *scb;
+ u_int scb_index;
+
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Unable to clear parity error. "
+ "Resetting bus.\n");
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL)
+ ahc_set_transaction_status(scb,
+ CAM_UNCOR_PARITY);
+ ahc_reset_channel(ahc, devinfo.channel,
+ /*init reset*/TRUE);
+ }
+ } else {
+ ahc_inb(ahc, SCSIDATL);
+ }
+ }
+ break;
+ }
+ case DATA_OVERRUN:
+ {
+ /*
+ * When the sequencer detects an overrun, it
+ * places the controller in "BITBUCKET" mode
+ * and allows the target to complete its transfer.
+ * Unfortunately, none of the counters get updated
+ * when the controller is in this mode, so we have
+ * no way of knowing how large the overrun was.
+ */
+ u_int scbindex = ahc_inb(ahc, SCB_TAG);
+ u_int lastphase = ahc_inb(ahc, LASTPHASE);
+ u_int i;
+
+ scb = ahc_lookup_scb(ahc, scbindex);
+ for (i = 0; i < num_phases; i++) {
+ if (lastphase == ahc_phase_table[i].phase)
+ break;
+ }
+ ahc_print_path(ahc, scb);
+ printf("data overrun detected %s."
+ " Tag == 0x%x.\n",
+ ahc_phase_table[i].phasemsg,
+ scb->hscb->tag);
+ ahc_print_path(ahc, scb);
+ printf("%s seen Data Phase. Length = %ld. NumSGs = %d.\n",
+ ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ ahc_get_transfer_length(scb), scb->sg_count);
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (ahc_le32toh(scb->sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahc_le32toh(scb->sg_list[i].addr),
+ ahc_le32toh(scb->sg_list[i].len)
+ & AHC_SG_LEN_MASK);
+ }
+ }
+ /*
+ * Set this and it will take effect when the
+ * target does a command complete.
+ */
+ ahc_freeze_devq(ahc, scb);
+ if ((scb->flags & SCB_SENSE) == 0) {
+ ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ } else {
+ scb->flags &= ~SCB_SENSE;
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ }
+ ahc_freeze_scb(scb);
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /*
+ * Clear the channel in case we return
+ * to data phase later.
+ */
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+ }
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ u_int dscommand1;
+
+ /* Ensure HHADDR is 0 for future DMA operations. */
+ dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+ ahc_outb(ahc, HADDR, 0);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1);
+ }
+ break;
+ }
+ case MKMSG_FAILED:
+ {
+ u_int scbindex;
+
+ printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ devinfo.lun);
+ scbindex = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbindex);
+ if (scb != NULL
+ && (scb->flags & SCB_RECOVERY_SCB) != 0)
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ break;
+ }
+ case NO_FREE_SCB:
+ {
+ printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ break;
+ }
+ case SCB_MISMATCH:
+ {
+ u_int scbptr;
+
+ scbptr = ahc_inb(ahc, SCBPTR);
+ printf("Bogus TAG after DMA. SCBPTR %d, tag %d, our tag %d\n",
+ scbptr, ahc_inb(ahc, ARG_1),
+ ahc->scb_data->hscbs[scbptr].tag);
+ ahc_dump_card_state(ahc);
+ panic("for saftey");
+ break;
+ }
+ case OUT_OF_RANGE:
+ {
+ printf("%s: BTT calculation out of range\n", ahc_name(ahc));
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+ ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n, A == 0x%x\n",
+ ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+ ahc_index_busy_tcl(ahc,
+ BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+ ahc_inb(ahc, SAVED_LUN))),
+ ahc_inb(ahc, SINDEX),
+ ahc_inb(ahc, ACCUM));
+ printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+ ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+ ahc_inb(ahc, SCB_CONTROL));
+ printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+ ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ break;
+ }
+ default:
+ printf("ahc_intr: seqint, "
+ "intstat == 0x%x, scsisigi = 0x%x\n",
+ intstat, ahc_inb(ahc, SCSISIGI));
+ break;
+ }
+unpause:
+ /*
+ * The sequencer is paused immediately on
+ * a SEQINT, so we should restart it when
+ * we're done.
+ */
+ ahc_unpause(ahc);
+}
+
+void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
+{
+ u_int scb_index;
+ u_int status0;
+ u_int status;
+ struct scb *scb;
+ char cur_channel;
+ char intr_channel;
+
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+ cur_channel = 'B';
+ else
+ cur_channel = 'A';
+ intr_channel = cur_channel;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+ else
+ status0 = 0;
+ status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ if (status == 0 && status0 == 0) {
+ if ((ahc->features & AHC_TWIN) != 0) {
+ /* Try the other channel */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ status = ahc_inb(ahc, SSTAT1)
+ & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+ }
+ if (status == 0) {
+ printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_unpause(ahc);
+ return;
+ }
+ }
+
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL
+ && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+ scb = NULL;
+
+ if ((ahc->features & AHC_ULTRA2) != 0
+ && (status0 & IOERR) != 0) {
+ int now_lvd;
+
+ now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+ printf("%s: Transceiver State Has Changed to %s mode\n",
+ ahc_name(ahc), now_lvd ? "LVD" : "SE");
+ ahc_outb(ahc, CLRSINT0, CLRIOERR);
+ /*
+ * When transitioning to SE mode, the reset line
+ * glitches, triggering an arbitration bug in some
+ * Ultra2 controllers. This bug is cleared when we
+ * assert the reset line. Since a reset glitch has
+ * already occurred with this transition and a
+ * transceiver state change is handled just like
+ * a bus reset anyway, asserting the reset line
+ * ourselves is safe.
+ */
+ ahc_reset_channel(ahc, intr_channel,
+ /*Initiate Reset*/now_lvd == 0);
+ } else if ((status & SCSIRSTI) != 0) {
+ printf("%s: Someone reset channel %c\n",
+ ahc_name(ahc), intr_channel);
+ if (intr_channel != cur_channel)
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
+ } else if ((status & SCSIPERR) != 0) {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ * SCSIPERR is latched true as soon as a parity error
+ * occurs. If the sequencer acked the transfer that
+ * caused the parity error and the currently presented
+ * transfer on the bus has correct parity, SCSIPERR will
+ * be cleared by CLRSCSIPERR. Use this to determine if
+ * we should look at the last phase the sequencer recorded,
+ * or the current phase presented on the bus.
+ */
+ struct ahc_devinfo devinfo;
+ u_int mesg_out;
+ u_int curphase;
+ u_int errorphase;
+ u_int lastphase;
+ u_int scsirate;
+ u_int i;
+ u_int sstat2;
+ int silent;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ sstat2 = ahc_inb(ahc, SSTAT2);
+ ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+ /*
+ * For all phases save DATA, the sequencer won't
+ * automatically ack a byte that has a parity error
+ * in it. So the only way that the current phase
+ * could be 'data-in' is if the parity error is for
+ * an already acked byte in the data phase. During
+ * synchronous data-in transfers, we may actually
+ * ack bytes before latching the current phase in
+ * LASTPHASE, leading to the discrepancy between
+ * curphase and lastphase.
+ */
+ if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+ || curphase == P_DATAIN || curphase == P_DATAIN_DT)
+ errorphase = curphase;
+ else
+ errorphase = lastphase;
+
+ for (i = 0; i < num_phases; i++) {
+ if (errorphase == ahc_phase_table[i].phase)
+ break;
+ }
+ mesg_out = ahc_phase_table[i].mesg_out;
+ silent = FALSE;
+ if (scb != NULL) {
+ if (SCB_IS_SILENT(scb))
+ silent = TRUE;
+ else
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ } else
+ printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+ SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+ scsirate = ahc_inb(ahc, SCSIRATE);
+ if (silent == FALSE) {
+ printf("parity error detected %s. "
+ "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+ ahc_phase_table[i].phasemsg,
+ ahc_inw(ahc, SEQADDR0),
+ scsirate);
+ if ((ahc->features & AHC_DT) != 0) {
+ if ((sstat2 & CRCVALERR) != 0)
+ printf("\tCRC Value Mismatch\n");
+ if ((sstat2 & CRCENDERR) != 0)
+ printf("\tNo terminal CRC packet "
+ "recevied\n");
+ if ((sstat2 & CRCREQERR) != 0)
+ printf("\tIllegal CRC packet "
+ "request\n");
+ if ((sstat2 & DUAL_EDGE_ERR) != 0)
+ printf("\tUnexpected %sDT Data Phase\n",
+ (scsirate & SINGLE_EDGE)
+ ? "" : "non-");
+ }
+ }
+
+ if ((ahc->features & AHC_DT) != 0
+ && (sstat2 & DUAL_EDGE_ERR) != 0) {
+ /*
+ * This error applies regardless of
+ * data direction, so ignore the value
+ * in the phase table.
+ */
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ }
+
+ /*
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
+ * need to do is stuff the message buffer with
+ * the appropriate message. "In" phases have set
+ * mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP) {
+ if (ahc->msg_type != MSG_TYPE_NONE)
+ ahc->send_msg_perror = TRUE;
+ else
+ ahc_outb(ahc, MSG_OUT, mesg_out);
+ }
+ /*
+ * Force a renegotiation with this target just in
+ * case we are out of sync for some external reason
+ * unknown (or unreported) by the target.
+ */
+ ahc_fetch_devinfo(ahc, &devinfo);
+ ahc_force_renegotiation(ahc, &devinfo);
+
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_unpause(ahc);
+ } else if ((status & SELTO) != 0) {
+ u_int scbptr;
+
+ /* Stop the selection */
+ ahc_outb(ahc, SCSISEQ, 0);
+
+ /* No more pending messages */
+ ahc_clear_msg_state(ahc);
+
+ /* Clear interrupt state */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Although the driver does not care about the
+ * 'Selection in Progress' status bit, the busy
+ * LED does. SELINGO is only cleared by a sucessfull
+ * selection, so we must manually clear it to insure
+ * the LED turns off just incase no future successful
+ * selections occur (e.g. no devices on the bus).
+ */
+ ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+ scbptr = ahc_inb(ahc, WAITING_SCBH);
+ ahc_outb(ahc, SCBPTR, scbptr);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s: ahc_intr - referenced scb not "
+ "valid during SELTO scb(%d, %d)\n",
+ ahc_name(ahc), scbptr, scb_index);
+ ahc_dump_card_state(ahc);
+ } else {
+ struct ahc_devinfo devinfo;
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_SELTO) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Saw Selection Timeout for SCB 0x%x\n",
+ scb_index);
+ }
+#endif
+ /*
+ * Force a renegotiation with this target just in
+ * case the cable was pulled and will later be
+ * re-attached. The target may forget its negotiation
+ * settings with us should it attempt to reselect
+ * during the interruption. The target will not issue
+ * a unit attention in this case, so we must always
+ * renegotiate.
+ */
+ ahc_scb_devinfo(ahc, &devinfo, scb);
+ ahc_force_renegotiation(ahc, &devinfo);
+ ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+ ahc_freeze_devq(ahc, scb);
+ }
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_restart(ahc);
+ } else if ((status & BUSFREE) != 0
+ && (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+ struct ahc_devinfo devinfo;
+ u_int lastphase;
+ u_int saved_scsiid;
+ u_int saved_lun;
+ u_int target;
+ u_int initiator_role_id;
+ char channel;
+ int printerror;
+
+ /*
+ * Clear our selection hardware as soon as possible.
+ * We may have an entry in the waiting Q for this target,
+ * that is affected by this busfree and we don't want to
+ * go about selecting the target while we handle the event.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ /*
+ * Disable busfree interrupts and clear the busfree
+ * interrupt status. We do this here so that several
+ * bus transactions occur prior to clearing the SCSIINT
+ * latch. It can take a bit for the clearing to take effect.
+ */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Look at what phase we were last in.
+ * If its message out, chances are pretty good
+ * that the busfree was in response to one of
+ * our abort requests.
+ */
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ saved_lun = ahc_inb(ahc, SAVED_LUN);
+ target = SCSIID_TARGET(ahc, saved_scsiid);
+ initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+ channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+ ahc_compile_devinfo(&devinfo, initiator_role_id,
+ target, saved_lun, channel, ROLE_INITIATOR);
+ printerror = 1;
+
+ if (lastphase == P_MESGOUT) {
+ u_int tag;
+
+ tag = SCB_LIST_NULL;
+ if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+ || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+ if (ahc->msgout_buf[ahc->msgout_index - 1]
+ == MSG_ABORT_TAG)
+ tag = scb->hscb->tag;
+ ahc_print_path(ahc, scb);
+ printf("SCB %d - Abort%s Completed.\n",
+ scb->hscb->tag, tag == SCB_LIST_NULL ?
+ "" : " Tag");
+ ahc_abort_scbs(ahc, target, channel,
+ saved_lun, tag,
+ ROLE_INITIATOR,
+ CAM_REQ_ABORTED);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_1B,
+ MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+ /*
+ * Don't mark the user's request for this BDR
+ * as completing with CAM_BDR_SENT. CAM3
+ * specifies CAM_REQ_CMP.
+ */
+ if (scb != NULL
+ && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+ && ahc_match_scb(ahc, scb, target, channel,
+ CAM_LUN_WILDCARD,
+ SCB_LIST_NULL,
+ ROLE_INITIATOR)) {
+ ahc_set_transaction_status(scb, CAM_REQ_CMP);
+ }
+#endif
+ ahc_compile_devinfo(&devinfo,
+ initiator_role_id,
+ target,
+ CAM_LUN_WILDCARD,
+ channel,
+ ROLE_INITIATOR);
+ ahc_handle_devreset(ahc, &devinfo,
+ CAM_BDR_SENT,
+ "Bus Device Reset",
+ /*verbose_level*/0);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_PPR, FALSE)) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ /*
+ * PPR Rejected. Try non-ppr negotiation
+ * and retry command.
+ */
+ tinfo = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->goal.ppr_options = 0;
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_WDTR, FALSE)) {
+ /*
+ * Negotiation Rejected. Go-narrow and
+ * retry command.
+ */
+ ahc_set_width(ahc, &devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_SDTR, FALSE)) {
+ /*
+ * Negotiation Rejected. Go-async and
+ * retry command.
+ */
+ ahc_set_syncrate(ahc, &devinfo,
+ /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0,
+ AHC_TRANS_CUR|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
+ }
+ }
+ if (printerror != 0) {
+ u_int i;
+
+ if (scb != NULL) {
+ u_int tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = scb->hscb->tag;
+ else
+ tag = SCB_LIST_NULL;
+ ahc_print_path(ahc, scb);
+ ahc_abort_scbs(ahc, target, channel,
+ SCB_GET_LUN(scb), tag,
+ ROLE_INITIATOR,
+ CAM_UNEXP_BUSFREE);
+ } else {
+ /*
+ * We had not fully identified this connection,
+ * so we cannot abort anything.
+ */
+ printf("%s: ", ahc_name(ahc));
+ }
+ for (i = 0; i < num_phases; i++) {
+ if (lastphase == ahc_phase_table[i].phase)
+ break;
+ }
+ if (lastphase != P_BUSFREE) {
+ /*
+ * Renegotiate with this device at the
+ * next oportunity just in case this busfree
+ * is due to a negotiation mismatch with the
+ * device.
+ */
+ ahc_force_renegotiation(ahc, &devinfo);
+ }
+ printf("Unexpected busfree %s\n"
+ "SEQADDR == 0x%x\n",
+ ahc_phase_table[i].phasemsg,
+ ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8));
+ }
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_restart(ahc);
+ } else {
+ printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+ ahc_name(ahc), status);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahc_force_renegotiation(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ struct ahc_initiator_tinfo *targ_info;
+ struct ahc_tmode_tstate *tstate;
+
+ targ_info = ahc_fetch_transinfo(ahc,
+ devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target,
+ &tstate);
+ ahc_update_neg_request(ahc, devinfo, tstate,
+ targ_info, AHC_NEG_IF_NON_ASYNC);
+}
+
+#define AHC_MAX_STEPS 2000
+void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+ int stepping;
+ int steps;
+ u_int simode0;
+ u_int simode1;
+
+ if (ahc->num_critical_sections == 0)
+ return;
+
+ stepping = FALSE;
+ steps = 0;
+ simode0 = 0;
+ simode1 = 0;
+ for (;;) {
+ struct cs *cs;
+ u_int seqaddr;
+ u_int i;
+
+ seqaddr = ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8);
+
+ /*
+ * Seqaddr represents the next instruction to execute,
+ * so we are really executing the instruction just
+ * before it.
+ */
+ if (seqaddr != 0)
+ seqaddr -= 1;
+ cs = ahc->critical_sections;
+ for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+
+ if (cs->begin < seqaddr && cs->end >= seqaddr)
+ break;
+ }
+
+ if (i == ahc->num_critical_sections)
+ break;
+
+ if (steps > AHC_MAX_STEPS) {
+ printf("%s: Infinite loop in critical section\n",
+ ahc_name(ahc));
+ ahc_dump_card_state(ahc);
+ panic("critical section loop");
+ }
+
+ steps++;
+ if (stepping == FALSE) {
+
+ /*
+ * Disable all interrupt sources so that the
+ * sequencer will not be stuck by a pausing
+ * interrupt condition while we attempt to
+ * leave a critical section.
+ */
+ simode0 = ahc_inb(ahc, SIMODE0);
+ ahc_outb(ahc, SIMODE0, 0);
+ simode1 = ahc_inb(ahc, SIMODE1);
+ if ((ahc->features & AHC_DT) != 0)
+ /*
+ * On DT class controllers, we
+ * use the enhanced busfree logic.
+ * Unfortunately we cannot re-enable
+ * busfree detection within the
+ * current connection, so we must
+ * leave it on while single stepping.
+ */
+ ahc_outb(ahc, SIMODE1, simode1 & ENBUSFREE);
+ else
+ ahc_outb(ahc, SIMODE1, 0);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_outb(ahc, SEQCTL, ahc->seqctl | STEP);
+ stepping = TRUE;
+ }
+ if ((ahc->features & AHC_DT) != 0) {
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+ while (!ahc_is_paused(ahc))
+ ahc_delay(200);
+ }
+ if (stepping) {
+ ahc_outb(ahc, SIMODE0, simode0);
+ ahc_outb(ahc, SIMODE1, simode1);
+ ahc_outb(ahc, SEQCTL, ahc->seqctl);
+ }
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+ /* Clear any interrupt conditions this may have caused */
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+ CLRREQINIT);
+ ahc_flush_device_writes(ahc);
+ ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+ ahc_flush_device_writes(ahc);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_flush_device_writes(ahc);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHC_DEBUG
+uint32_t ahc_debug = AHC_DEBUG_OPTS;
+#endif
+
+void
+ahc_print_scb(struct scb *scb)
+{
+ int i;
+
+ struct hardware_scb *hscb = scb->hscb;
+
+ printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+ (void *)scb,
+ hscb->control,
+ hscb->scsiid,
+ hscb->lun,
+ hscb->cdb_len);
+ printf("Shared Data: ");
+ for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
+ printf("%#02x", hscb->shared_data.cdb[i]);
+ printf(" dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+ ahc_le32toh(hscb->dataptr),
+ ahc_le32toh(hscb->datacnt),
+ ahc_le32toh(hscb->sgptr),
+ hscb->tag);
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (ahc_le32toh(scb->sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahc_le32toh(scb->sg_list[i].addr),
+ ahc_le32toh(scb->sg_list[i].len));
+ }
+ }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahc_tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+ struct ahc_tmode_tstate *master_tstate;
+ struct ahc_tmode_tstate *tstate;
+ int i;
+
+ master_tstate = ahc->enabled_targets[ahc->our_id];
+ if (channel == 'B') {
+ scsi_id += 8;
+ master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+ }
+ if (ahc->enabled_targets[scsi_id] != NULL
+ && ahc->enabled_targets[scsi_id] != master_tstate)
+ panic("%s: ahc_alloc_tstate - Target already allocated",
+ ahc_name(ahc));
+ tstate = (struct ahc_tmode_tstate*)malloc(sizeof(*tstate),
+ M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL)
+ return (NULL);
+
+ /*
+ * If we have allocated a master tstate, copy user settings from
+ * the master tstate (taken from SRAM or the EEPROM) for this
+ * channel, but reset our current and goal settings to async/narrow
+ * until an initiator talks to us.
+ */
+ if (master_tstate != NULL) {
+ memcpy(tstate, master_tstate, sizeof(*tstate));
+ memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+ tstate->ultraenb = 0;
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ memset(&tstate->transinfo[i].curr, 0,
+ sizeof(tstate->transinfo[i].curr));
+ memset(&tstate->transinfo[i].goal, 0,
+ sizeof(tstate->transinfo[i].goal));
+ }
+ } else
+ memset(tstate, 0, sizeof(*tstate));
+ ahc->enabled_targets[scsi_id] = tstate;
+ return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
+{
+ struct ahc_tmode_tstate *tstate;
+
+ /*
+ * Don't clean up our "master" tstate.
+ * It has our default user settings.
+ */
+ if (((channel == 'B' && scsi_id == ahc->our_id_b)
+ || (channel == 'A' && scsi_id == ahc->our_id))
+ && force == FALSE)
+ return;
+
+ if (channel == 'B')
+ scsi_id += 8;
+ tstate = ahc->enabled_targets[scsi_id];
+ if (tstate != NULL)
+ free(tstate, M_DEVBUF);
+ ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ u_int *period, u_int *ppr_options, role_t role)
+{
+ struct ahc_transinfo *transinfo;
+ u_int maxsync;
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+ && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHC_SYNCRATE_DT;
+ } else {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ /* Can't do DT on an SE bus */
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ } else if ((ahc->features & AHC_ULTRA) != 0) {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ } else {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+ /*
+ * Never allow a value higher than our current goal
+ * period otherwise we may allow a target initiated
+ * negotiation to go above the limit as set by the
+ * user. In the case of an initiator initiated
+ * sync negotiation, we limit based on the user
+ * setting. This allows the system to still accept
+ * incoming negotiations even if target initiated
+ * negotiation is not performed.
+ */
+ if (role == ROLE_TARGET)
+ transinfo = &tinfo->user;
+ else
+ transinfo = &tinfo->goal;
+ *ppr_options &= transinfo->ppr_options;
+ if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
+ maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ if (transinfo->period == 0) {
+ *period = 0;
+ *ppr_options = 0;
+ return (NULL);
+ }
+ *period = MAX(*period, transinfo->period);
+ return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+ u_int *ppr_options, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_DT) == 0)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ /* Skip all DT only entries if DT is not available */
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && maxsync < AHC_SYNCRATE_ULTRA2)
+ maxsync = AHC_SYNCRATE_ULTRA2;
+
+ for (syncrate = &ahc_syncrates[maxsync];
+ syncrate->rate != NULL;
+ syncrate++) {
+
+ /*
+ * The Ultra2 table doesn't go as low
+ * as for the Fast/Ultra cards.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))
+ break;
+
+ if (*period <= syncrate->period) {
+ /*
+ * When responding to a target that requests
+ * sync, the requested rate may fall between
+ * two rates that we can output, but still be
+ * a rate that we can receive. Because of this,
+ * we want to respond to the target with
+ * the same rate that it sent to us even
+ * if the period we use to send data to it
+ * is lower. Only lower the response period
+ * if we must.
+ */
+ if (syncrate == &ahc_syncrates[maxsync])
+ *period = syncrate->period;
+
+ /*
+ * At some speeds, we only support
+ * ST transfers.
+ */
+ if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ break;
+ }
+ }
+
+ if ((*period == 0)
+ || (syncrate->rate == NULL)
+ || ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))) {
+ /* Use asynchronous transfers. */
+ *period = 0;
+ syncrate = NULL;
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ scsirate &= SXFR_ULTRA2;
+ else
+ scsirate &= SXFR;
+
+ syncrate = &ahc_syncrates[maxsync];
+ while (syncrate->rate != NULL) {
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if (syncrate->sxfr_u2 == 0)
+ break;
+ else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+ return (syncrate->period);
+ } else if (scsirate == (syncrate->sxfr & SXFR)) {
+ return (syncrate->period);
+ }
+ syncrate++;
+ }
+ return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahc_validate_offset(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide, role_t role)
+{
+ u_int maxoffset;
+
+ /* Limit offset to what we can do */
+ if (syncrate == NULL) {
+ maxoffset = 0;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ maxoffset = MAX_OFFSET_ULTRA2;
+ } else {
+ if (wide)
+ maxoffset = MAX_OFFSET_16BIT;
+ else
+ maxoffset = MAX_OFFSET_8BIT;
+ }
+ *offset = MIN(*offset, maxoffset);
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *offset = MIN(*offset, tinfo->user.offset);
+ else
+ *offset = MIN(*offset, tinfo->goal.offset);
+ }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+ u_int *bus_width, role_t role)
+{
+ switch (*bus_width) {
+ default:
+ if (ahc->features & AHC_WIDE) {
+ /* Respond Wide */
+ *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *bus_width = MIN(tinfo->user.width, *bus_width);
+ else
+ *bus_width = MIN(tinfo->goal.width, *bus_width);
+ }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity. This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct ahc_tmode_tstate *tstate,
+ struct ahc_initiator_tinfo *tinfo, ahc_neg_type neg_type)
+{
+ u_int auto_negotiate_orig;
+
+ auto_negotiate_orig = tstate->auto_negotiate;
+ if (neg_type == AHC_NEG_ALWAYS) {
+ /*
+ * Force our "current" settings to be
+ * unknown so that unless a bus reset
+ * occurs the need to renegotiate is
+ * recorded persistently.
+ */
+ if ((ahc->features & AHC_WIDE) != 0)
+ tinfo->curr.width = AHC_WIDTH_UNKNOWN;
+ tinfo->curr.period = AHC_PERIOD_UNKNOWN;
+ tinfo->curr.offset = AHC_OFFSET_UNKNOWN;
+ }
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+ || (neg_type == AHC_NEG_IF_NON_ASYNC
+ && (tinfo->goal.offset != 0
+ || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+ || tinfo->goal.ppr_options != 0)))
+ tstate->auto_negotiate |= devinfo->target_mask;
+ else
+ tstate->auto_negotiate &= ~devinfo->target_mask;
+
+ return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate, u_int period,
+ u_int offset, u_int ppr_options, u_int type, int paused)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int old_period;
+ u_int old_offset;
+ u_int old_ppr;
+ int active;
+ int update_needed;
+
+ active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+ update_needed = 0;
+
+ if (syncrate == NULL) {
+ period = 0;
+ offset = 0;
+ }
+
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHC_TRANS_USER) != 0) {
+ tinfo->user.period = period;
+ tinfo->user.offset = offset;
+ tinfo->user.ppr_options = ppr_options;
+ }
+
+ if ((type & AHC_TRANS_GOAL) != 0) {
+ tinfo->goal.period = period;
+ tinfo->goal.offset = offset;
+ tinfo->goal.ppr_options = ppr_options;
+ }
+
+ old_period = tinfo->curr.period;
+ old_offset = tinfo->curr.offset;
+ old_ppr = tinfo->curr.ppr_options;
+
+ if ((type & AHC_TRANS_CUR) != 0
+ && (old_period != period
+ || old_offset != offset
+ || old_ppr != ppr_options)) {
+ u_int scsirate;
+
+ update_needed++;
+ scsirate = tinfo->scsirate;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+ if (syncrate != NULL) {
+ scsirate |= syncrate->sxfr_u2;
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+ scsirate |= ENABLE_CRC;
+ else
+ scsirate |= SINGLE_EDGE;
+ }
+ } else {
+
+ scsirate &= ~(SXFR|SOFS);
+ /*
+ * Ensure Ultra mode is set properly for
+ * this target.
+ */
+ tstate->ultraenb &= ~devinfo->target_mask;
+ if (syncrate != NULL) {
+ if (syncrate->sxfr & ULTRA_SXFR) {
+ tstate->ultraenb |=
+ devinfo->target_mask;
+ }
+ scsirate |= syncrate->sxfr & SXFR;
+ scsirate |= offset & SOFS;
+ }
+ if (active) {
+ u_int sxfrctl0;
+
+ sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ sxfrctl0 &= ~FAST20;
+ if (tstate->ultraenb & devinfo->target_mask)
+ sxfrctl0 |= FAST20;
+ ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+ }
+ }
+ if (active) {
+ ahc_outb(ahc, SCSIRATE, scsirate);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIOFFSET, offset);
+ }
+
+ tinfo->scsirate = scsirate;
+ tinfo->curr.period = period;
+ tinfo->curr.offset = offset;
+ tinfo->curr.ppr_options = ppr_options;
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ if (offset != 0) {
+ printf("%s: target %d synchronous at %sMHz%s, "
+ "offset = 0x%x\n", ahc_name(ahc),
+ devinfo->target, syncrate->rate,
+ (ppr_options & MSG_EXT_PPR_DT_REQ)
+ ? " DT" : "", offset);
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers\n",
+ ahc_name(ahc), devinfo->target);
+ }
+ }
+ }
+
+ update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, AHC_NEG_TO_GOAL);
+
+ if (update_needed)
+ ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int oldwidth;
+ int active;
+ int update_needed;
+
+ active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+ update_needed = 0;
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHC_TRANS_USER) != 0)
+ tinfo->user.width = width;
+
+ if ((type & AHC_TRANS_GOAL) != 0)
+ tinfo->goal.width = width;
+
+ oldwidth = tinfo->curr.width;
+ if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+ u_int scsirate;
+
+ update_needed++;
+ scsirate = tinfo->scsirate;
+ scsirate &= ~WIDEXFER;
+ if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ scsirate |= WIDEXFER;
+
+ tinfo->scsirate = scsirate;
+
+ if (active)
+ ahc_outb(ahc, SCSIRATE, scsirate);
+
+ tinfo->curr.width = width;
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << width));
+ }
+ }
+
+ update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, AHC_NEG_TO_GOAL);
+ if (update_needed)
+ ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg)
+{
+ ahc_platform_set_tags(ahc, devinfo, alg);
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_scbs(struct ahc_softc *ahc)
+{
+ struct scb *pending_scb;
+ int pending_scb_count;
+ int i;
+ int paused;
+ u_int saved_scbptr;
+
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings.
+ */
+ pending_scb_count = 0;
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ struct ahc_devinfo devinfo;
+ struct hardware_scb *pending_hscb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ pending_hscb = pending_scb->hscb;
+ pending_hscb->control &= ~ULTRAENB;
+ if ((tstate->ultraenb & devinfo.target_mask) != 0)
+ pending_hscb->control |= ULTRAENB;
+ pending_hscb->scsirate = tinfo->scsirate;
+ pending_hscb->scsioffset = tinfo->curr.offset;
+ if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+ && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+ pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+ pending_hscb->control &= ~MK_MESSAGE;
+ }
+ ahc_sync_scb(ahc, pending_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ pending_scb_count++;
+ }
+
+ if (pending_scb_count == 0)
+ return;
+
+ if (ahc_is_paused(ahc)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahc_pause(ahc);
+ }
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ /* Ensure that the hscbs down on the card match the new information */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ struct hardware_scb *pending_hscb;
+ u_int control;
+ u_int scb_tag;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scb_tag = ahc_inb(ahc, SCB_TAG);
+ pending_scb = ahc_lookup_scb(ahc, scb_tag);
+ if (pending_scb == NULL)
+ continue;
+
+ pending_hscb = pending_scb->hscb;
+ control = ahc_inb(ahc, SCB_CONTROL);
+ control &= ~(ULTRAENB|MK_MESSAGE);
+ control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
+ ahc_outb(ahc, SCB_CONTROL, control);
+ ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+ ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+ }
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+
+ if (paused == 0)
+ ahc_unpause(ahc);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int saved_scsiid;
+ role_t role;
+ int our_id;
+
+ if (ahc_inb(ahc, SSTAT0) & TARGET)
+ role = ROLE_TARGET;
+ else
+ role = ROLE_INITIATOR;
+
+ if (role == ROLE_TARGET
+ && (ahc->features & AHC_MULTI_TID) != 0
+ && (ahc_inb(ahc, SEQ_FLAGS)
+ & (CMDPHASE_PENDING|TARG_CMD_PENDING|NO_DISCONNECT)) != 0) {
+ /* We were selected, so pull our id from TARGIDIN */
+ our_id = ahc_inb(ahc, TARGIDIN) & OID;
+ } else if ((ahc->features & AHC_ULTRA2) != 0)
+ our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+ else
+ our_id = ahc_inb(ahc, SCSIID) & OID;
+
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ ahc_compile_devinfo(devinfo,
+ our_id,
+ SCSIID_TARGET(ahc, saved_scsiid),
+ ahc_inb(ahc, SAVED_LUN),
+ SCSIID_CHANNEL(ahc, saved_scsiid),
+ role);
+}
+
+struct ahc_phase_table_entry*
+ahc_lookup_phase_entry(int phase)
+{
+ struct ahc_phase_table_entry *entry;
+ struct ahc_phase_table_entry *last_entry;
+
+ /*
+ * num_phases doesn't include the default entry which
+ * will be returned if the phase doesn't match.
+ */
+ last_entry = &ahc_phase_table[num_phases];
+ for (entry = ahc_phase_table; entry < last_entry; entry++) {
+ if (phase == entry->phase)
+ break;
+ }
+ return (entry);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+ u_int lun, char channel, role_t role)
+{
+ devinfo->our_scsiid = our_id;
+ devinfo->target = target;
+ devinfo->lun = lun;
+ devinfo->target_offset = target;
+ devinfo->channel = channel;
+ devinfo->role = role;
+ if (channel == 'B')
+ devinfo->target_offset += 8;
+ devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+void
+ahc_print_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ printf("%s:%c:%d:%d: ", ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+}
+
+static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+ role_t role;
+ int our_id;
+
+ our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+ role = ROLE_INITIATOR;
+ if ((scb->flags & SCB_TARGET_SCB) != 0)
+ role = ROLE_TARGET;
+ ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+static void
+ahc_assert_atn(struct ahc_softc *ahc)
+{
+ u_int scsisigo;
+
+ scsisigo = ATNO;
+ if ((ahc->features & AHC_DT) == 0)
+ scsisigo |= ahc_inb(ahc, SCSISIGI);
+ ahc_outb(ahc, SCSISIGO, scsisigo);
+}
+
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted. Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+
+ if ((scb->flags & SCB_DEVICE_RESET) == 0
+ && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+ u_int identify_msg;
+
+ identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+ if ((scb->hscb->control & DISCENB) != 0)
+ identify_msg |= MSG_IDENTIFY_DISCFLAG;
+ ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+ ahc->msgout_len++;
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ ahc->msgout_buf[ahc->msgout_index++] =
+ scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+ ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+ ahc->msgout_len += 2;
+ }
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahc->msgout_len++;
+ ahc_print_path(ahc, scb);
+ printf("Bus Device Reset Message Sent\n");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else if ((scb->flags & SCB_ABORT) != 0) {
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+ else
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+ ahc->msgout_len++;
+ ahc_print_path(ahc, scb);
+ printf("Abort%s Message Sent\n",
+ (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+ ahc_build_transfer_msg(ahc, devinfo);
+ } else {
+ printf("ahc_intr: AWAITING_MSG for an SCB that "
+ "does not have a waiting message\n");
+ printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+ devinfo->target_mask);
+ panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+ "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
+ ahc_inb(ahc, MSG_OUT), scb->flags);
+ }
+
+ /*
+ * Clear the MK_MESSAGE flag from the SCB so we aren't
+ * asked to send this message again.
+ */
+ ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+ scb->hscb->control &= ~MK_MESSAGE;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ /*
+ * We need to initiate transfer negotiations.
+ * If our current and goal settings are identical,
+ * we want to renegotiate due to a check condition.
+ */
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *rate;
+ int dowide;
+ int dosync;
+ int doppr;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /*
+ * Filter our period based on the current connection.
+ * If we can't perform DT transfers on this segment (not in LVD
+ * mode for instance), then our decision to issue a PPR message
+ * may change.
+ */
+ period = tinfo->goal.period;
+ offset = tinfo->goal.offset;
+ ppr_options = tinfo->goal.ppr_options;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ ppr_options = 0;
+ rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options, devinfo->role);
+ dowide = tinfo->curr.width != tinfo->goal.width;
+ dosync = tinfo->curr.offset != offset || tinfo->curr.period != period;
+ /*
+ * Only use PPR if we have options that need it, even if the device
+ * claims to support it. There might be an expander in the way
+ * that doesn't.
+ */
+ doppr = ppr_options != 0;
+
+ if (!dowide && !dosync && !doppr) {
+ dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+ dosync = tinfo->goal.offset != 0;
+ }
+
+ if (!dowide && !dosync && !doppr) {
+ /*
+ * Force async with a WDTR message if we have a wide bus,
+ * or just issue an SDTR with a 0 offset.
+ */
+ if ((ahc->features & AHC_WIDE) != 0)
+ dowide = 1;
+ else
+ dosync = 1;
+
+ if (bootverbose) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Ensuring async\n");
+ }
+ }
+
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ doppr = 0;
+
+ /*
+ * Both the PPR message and SDTR message require the
+ * goal syncrate to be limited to what the target device
+ * is capable of handling (based on whether an LVD->SE
+ * expander is on the bus), so combine these two cases.
+ * Regardless, guarantee that if we are using WDTR and SDTR
+ * messages that WDTR comes first.
+ */
+ if (doppr || (dosync && !dowide)) {
+
+ offset = tinfo->goal.offset;
+ ahc_validate_offset(ahc, tinfo, rate, &offset,
+ doppr ? tinfo->goal.width
+ : tinfo->curr.width,
+ devinfo->role);
+ if (doppr) {
+ ahc_construct_ppr(ahc, devinfo, period, offset,
+ tinfo->goal.width, ppr_options);
+ } else {
+ ahc_construct_sdtr(ahc, devinfo, period, offset);
+ }
+ } else {
+ ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+ }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int period, u_int offset)
+{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
+ ahc->msgout_buf[ahc->msgout_index++] = period;
+ ahc->msgout_buf[ahc->msgout_index++] = offset;
+ ahc->msgout_len += 5;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, period, offset);
+ }
+}
+
+/*
+ * Build a wide negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int bus_width)
+{
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
+ ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+ ahc->msgout_len += 4;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, bus_width);
+ }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int period, u_int offset, u_int bus_width,
+ u_int ppr_options)
+{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
+ ahc->msgout_buf[ahc->msgout_index++] = period;
+ ahc->msgout_buf[ahc->msgout_index++] = 0;
+ ahc->msgout_buf[ahc->msgout_index++] = offset;
+ ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+ ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
+ ahc->msgout_len += 8;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+ "offset %x, ppr_options %x\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun,
+ bus_width, period, offset, ppr_options);
+ }
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahc_clear_msg_state(struct ahc_softc *ahc)
+{
+ ahc->msgout_len = 0;
+ ahc->msgin_index = 0;
+ ahc->msg_type = MSG_TYPE_NONE;
+ if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
+ /*
+ * The target didn't care to respond to our
+ * message request, so clear ATN.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ }
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_outb(ahc, SEQ_FLAGS2,
+ ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+}
+
+static void
+ahc_handle_proto_violation(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+ u_int seq_flags;
+ u_int curphase;
+ u_int lastphase;
+ int found;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbid);
+ seq_flags = ahc_inb(ahc, SEQ_FLAGS);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+ /*
+ * The reconnecting target either did not send an
+ * identify message, or did, but we didn't find an SCB
+ * to match.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x.\n", lastphase);
+ scb = NULL;
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("No SCB found during protocol violation\n");
+ goto proto_violation_reset;
+ } else {
+ ahc_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+ if ((seq_flags & NO_CDB_SENT) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("No or incomplete CDB sent to device.\n");
+ } else if ((ahc_inb(ahc, SCB_CONTROL) & STATUS_RCVD) == 0) {
+ /*
+ * The target never bothered to provide status to
+ * us prior to completing the command. Since we don't
+ * know the disposition of this command, we must attempt
+ * to abort it. Assert ATN and prepare to send an abort
+ * message.
+ */
+ ahc_print_path(ahc, scb);
+ printf("Completed command without status.\n");
+ } else {
+ ahc_print_path(ahc, scb);
+ printf("Unknown protocol violation.\n");
+ ahc_dump_card_state(ahc);
+ }
+ }
+ if ((lastphase & ~P_DATAIN_DT) == 0
+ || lastphase == P_COMMAND) {
+proto_violation_reset:
+ /*
+ * Target either went directly to data/command
+ * phase or didn't respond to our ATN.
+ * The only safe thing to do is to blow
+ * it away with a bus reset.
+ */
+ found = ahc_reset_channel(ahc, 'A', TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), 'A', found);
+ } else {
+ /*
+ * Leave the selection hardware off in case
+ * this abort attempt will affect yet to
+ * be sent commands.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_assert_atn(ahc);
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ if (scb == NULL) {
+ ahc_print_devinfo(ahc, &devinfo);
+ ahc->msgout_buf[0] = MSG_ABORT_TASK;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ } else {
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_ABORT;
+ }
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahc_lookup_phase_entry(curphase)->phasemsg);
+ }
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ u_int bus_phase;
+ int end_session;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ end_session = FALSE;
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+ switch (ahc->msg_type) {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ int lastbyte;
+ int phasemis;
+ int msgdone;
+
+ if (ahc->msgout_len == 0)
+ panic("HOST_MSG_LOOP interrupt with no active message");
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("INITIATOR_MSG_OUT");
+ }
+#endif
+ phasemis = bus_phase != P_MESGOUT;
+ if (phasemis) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahc_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
+ if (bus_phase == P_MESGIN) {
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ ahc->send_msg_perror = FALSE;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msgin_index = 0;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ if (ahc->send_msg_perror) {
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n", ahc->send_msg_perror);
+#endif
+ ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+ break;
+ }
+
+ msgdone = ahc->msgout_index == ahc->msgout_len;
+ if (msgdone) {
+ /*
+ * The target has requested a retry.
+ * Re-assert ATN, reset our message index to
+ * 0, and try again.
+ */
+ ahc->msgout_index = 0;
+ ahc_assert_atn(ahc);
+ }
+
+ lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+ if (lastbyte) {
+ /* Last byte is signified by dropping ATN */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ }
+
+ /*
+ * Clear our interrupt status and present
+ * the next byte on the bus.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahc->msgout_buf[ahc->msgout_index]);
+#endif
+ ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ int phasemis;
+ int message_done;
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("INITIATOR_MSG_IN");
+ }
+#endif
+ phasemis = bus_phase != P_MESGIN;
+ if (phasemis) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahc_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
+ ahc->msgin_index = 0;
+ if (bus_phase == P_MESGOUT
+ && (ahc->send_msg_perror == TRUE
+ || (ahc->msgout_len != 0
+ && ahc->msgout_index == 0))) {
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
+ }
+
+ /* Pull the byte in without acking it */
+ ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahc->msgin_buf[ahc->msgin_index]);
+#endif
+
+ message_done = ahc_parse_msg(ahc, &devinfo);
+
+ if (message_done) {
+ /*
+ * Clear our incoming message buffer in case there
+ * is another message following this one.
+ */
+ ahc->msgin_index = 0;
+
+ /*
+ * If this message illicited a response,
+ * assert ATN so the target takes us to the
+ * message out phase.
+ */
+ if (ahc->msgout_len != 0) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Asserting ATN for response\n");
+ }
+#endif
+ ahc_assert_atn(ahc);
+ }
+ } else
+ ahc->msgin_index++;
+
+ if (message_done == MSGLOOP_TERMINATED) {
+ end_session = TRUE;
+ } else {
+ /* Ack the byte */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_inb(ahc, SCSIDATL);
+ }
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGIN:
+ {
+ int msgdone;
+ int msgout_request;
+
+ if (ahc->msgout_len == 0)
+ panic("Target MSGIN with no active message");
+
+ /*
+ * If we interrupted a mesgout session, the initiator
+ * will not know this until our first REQ. So, we
+ * only honor mesgout requests after we've sent our
+ * first byte.
+ */
+ if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+ && ahc->msgout_index > 0)
+ msgout_request = TRUE;
+ else
+ msgout_request = FALSE;
+
+ if (msgout_request) {
+
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+ ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+ ahc->msgin_index = 0;
+ /* Dummy read to REQ for first byte */
+ ahc_inb(ahc, SCSIDATL);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ break;
+ }
+
+ msgdone = ahc->msgout_index == ahc->msgout_len;
+ if (msgdone) {
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+ end_session = TRUE;
+ break;
+ }
+
+ /*
+ * Present the next byte on the bus.
+ */
+ ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGOUT:
+ {
+ int lastbyte;
+ int msgdone;
+
+ /*
+ * The initiator signals that this is
+ * the last byte by dropping ATN.
+ */
+ lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+
+ /*
+ * Read the latched byte, but turn off SPIOEN first
+ * so that we don't inadvertently cause a REQ for the
+ * next byte.
+ */
+ ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+ ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
+ msgdone = ahc_parse_msg(ahc, &devinfo);
+ if (msgdone == MSGLOOP_TERMINATED) {
+ /*
+ * The message is *really* done in that it caused
+ * us to go to bus free. The sequencer has already
+ * been reset at this point, so pull the ejection
+ * handle.
+ */
+ return;
+ }
+
+ ahc->msgin_index++;
+
+ /*
+ * XXX Read spec about initiator dropping ATN too soon
+ * and use msgdone to detect it.
+ */
+ if (msgdone == MSGLOOP_MSGCOMPLETE) {
+ ahc->msgin_index = 0;
+
+ /*
+ * If this message illicited a response, transition
+ * to the Message in phase and send it.
+ */
+ if (ahc->msgout_len != 0) {
+ ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+ ahc->msgin_index = 0;
+ break;
+ }
+ }
+
+ if (lastbyte)
+ end_session = TRUE;
+ else {
+ /* Ask for the next byte. */
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ }
+
+ break;
+ }
+ default:
+ panic("Unknown REQINIT message type");
+ }
+
+ if (end_session) {
+ ahc_clear_msg_state(ahc);
+ ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+ } else
+ ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message. If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
+{
+ int found;
+ u_int index;
+
+ found = FALSE;
+ index = 0;
+
+ while (index < ahc->msgout_len) {
+ if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+ u_int end_index;
+
+ end_index = index + 1 + ahc->msgout_buf[index + 1];
+ if (ahc->msgout_buf[index+2] == msgval
+ && type == AHCMSG_EXT) {
+
+ if (full) {
+ if (ahc->msgout_index > end_index)
+ found = TRUE;
+ } else if (ahc->msgout_index > index)
+ found = TRUE;
+ }
+ index = end_index;
+ } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
+ && ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+ /* Skip tag type and tag id or residue param*/
+ index += 2;
+ } else {
+ /* Single byte message */
+ if (type == AHCMSG_1B
+ && ahc->msgout_buf[index] == msgval
+ && ahc->msgout_index > index)
+ found = TRUE;
+ index++;
+ }
+
+ if (found)
+ break;
+ }
+ return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ int reject;
+ int done;
+ int response;
+ u_int targ_scsirate;
+
+ done = MSGLOOP_IN_PROG;
+ response = FALSE;
+ reject = FALSE;
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ targ_scsirate = tinfo->scsirate;
+
+ /*
+ * Parse as much of the message as is available,
+ * rejecting it if we don't support it. When
+ * the entire message is available and has been
+ * handled, return MSGLOOP_MSGCOMPLETE, indicating
+ * that we have parsed an entire message.
+ *
+ * In the case of extended messages, we accept the length
+ * byte outright and perform more checking once we know the
+ * extended message type.
+ */
+ switch (ahc->msgin_buf[0]) {
+ case MSG_DISCONNECT:
+ case MSG_SAVEDATAPOINTER:
+ case MSG_CMDCOMPLETE:
+ case MSG_RESTOREPOINTERS:
+ case MSG_IGN_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_MESSAGE_REJECT:
+ response = ahc_handle_msg_reject(ahc, devinfo);
+ /* FALLTHROUGH */
+ case MSG_NOOP:
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ case MSG_EXTENDED:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahc->msgin_index < 2)
+ break;
+ switch (ahc->msgin_buf[2]) {
+ case MSG_EXT_SDTR:
+ {
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
+ u_int saved_offset;
+
+ if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have both args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_SDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+ break;
+
+ period = ahc->msgin_buf[3];
+ ppr_options = 0;
+ saved_offset = offset = ahc->msgin_buf[4];
+ syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options,
+ devinfo->role);
+ ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+ targ_scsirate & WIDEXFER,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received "
+ "SDTR period %x, offset %x\n\t"
+ "Filtered to period %x, offset %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ ahc->msgin_buf[3], saved_offset,
+ period, offset);
+ }
+ ahc_set_syncrate(ahc, devinfo,
+ syncrate, period,
+ offset, ppr_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+
+ /*
+ * See if we initiated Sync Negotiation
+ * and didn't have to fall down to async
+ * transfers.
+ */
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+ /* We started it */
+ if (saved_offset != offset) {
+ /* Went too low - force async */
+ reject = TRUE;
+ }
+ } else {
+ /*
+ * Send our own SDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated SDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_sdtr(ahc, devinfo,
+ period, offset);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ u_int bus_width;
+ u_int saved_width;
+ u_int sending_reply;
+
+ sending_reply = FALSE;
+ if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have our arg before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_WDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+ break;
+
+ bus_width = ahc->msgin_buf[3];
+ saved_width = bus_width;
+ ahc_validate_width(ahc, tinfo, &bus_width,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received WDTR "
+ "%x filtered to %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, bus_width);
+ }
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+ /*
+ * Don't send a WDTR back to the
+ * target, since we asked first.
+ * If the width went higher than our
+ * request, reject it.
+ */
+ if (saved_width > bus_width) {
+ reject = TRUE;
+ printf("(%s:%c:%d:%d): requested %dBit "
+ "transfers. Rejecting...\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ 8 * (0x01 << bus_width));
+ bus_width = 0;
+ }
+ } else {
+ /*
+ * Send our own WDTR in reply
+ */
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated WDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_wdtr(ahc, devinfo, bus_width);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ sending_reply = TRUE;
+ }
+ /*
+ * After a wide message, we are async, but
+ * some devices don't seem to honor this portion
+ * of the spec. Force a renegotiation of the
+ * sync component of our transfer agreement even
+ * if our goal is async. By updating our width
+ * after forcing the negotiation, we avoid
+ * renegotiating for width.
+ */
+ ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, AHC_NEG_ALWAYS);
+ ahc_set_width(ahc, devinfo, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ if (sending_reply == FALSE && reject == FALSE) {
+
+ /*
+ * We will always have an SDTR to send.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ case MSG_EXT_PPR:
+ {
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int offset;
+ u_int bus_width;
+ u_int ppr_options;
+ u_int saved_width;
+ u_int saved_offset;
+ u_int saved_ppr_options;
+
+ if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have all args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_PPR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+ break;
+
+ period = ahc->msgin_buf[3];
+ offset = ahc->msgin_buf[5];
+ bus_width = ahc->msgin_buf[6];
+ saved_width = bus_width;
+ ppr_options = ahc->msgin_buf[7];
+ /*
+ * According to the spec, a DT only
+ * period factor with no DT option
+ * set implies async.
+ */
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && period == 9)
+ offset = 0;
+ saved_ppr_options = ppr_options;
+ saved_offset = offset;
+
+ /*
+ * Mask out any options we don't support
+ * on any controller. Transfer options are
+ * only available if we are negotiating wide.
+ */
+ ppr_options &= MSG_EXT_PPR_DT_REQ;
+ if (bus_width == 0)
+ ppr_options = 0;
+
+ ahc_validate_width(ahc, tinfo, &bus_width,
+ devinfo->role);
+ syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options,
+ devinfo->role);
+ ahc_validate_offset(ahc, tinfo, syncrate,
+ &offset, bus_width,
+ devinfo->role);
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ /*
+ * If we are unable to do any of the
+ * requested options (we went too low),
+ * then we'll have to reject the message.
+ */
+ if (saved_width > bus_width
+ || saved_offset != offset
+ || saved_ppr_options != ppr_options) {
+ reject = TRUE;
+ period = 0;
+ offset = 0;
+ bus_width = 0;
+ ppr_options = 0;
+ syncrate = NULL;
+ }
+ } else {
+ if (devinfo->role != ROLE_TARGET)
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated PPR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ else
+ printf("(%s:%c:%d:%d): Initiator "
+ "Initiated PPR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_ppr(ahc, devinfo, period, offset,
+ bus_width, ppr_options);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received PPR width %x, "
+ "period %x, offset %x,options %x\n"
+ "\tFiltered to width %x, period %x, "
+ "offset %x, options %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, ahc->msgin_buf[3],
+ saved_offset, saved_ppr_options,
+ bus_width, period, offset, ppr_options);
+ }
+ ahc_set_width(ahc, devinfo, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_set_syncrate(ahc, devinfo,
+ syncrate, period,
+ offset, ppr_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
+ default:
+ /* Unknown extended message. Reject it. */
+ reject = TRUE;
+ break;
+ }
+ break;
+ }
+#ifdef AHC_TARGET_MODE
+ case MSG_BUS_DEV_RESET:
+ ahc_handle_devreset(ahc, devinfo,
+ CAM_BDR_SENT,
+ "Bus Device Reset Received",
+ /*verbose_level*/0);
+ ahc_restart(ahc);
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_ABORT_TAG:
+ case MSG_ABORT:
+ case MSG_CLEAR_QUEUE:
+ {
+ int tag;
+
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
+ break;
+ }
+ tag = SCB_LIST_NULL;
+ if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+ tag = ahc_inb(ahc, INITIATOR_TAG);
+ ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+ devinfo->lun, tag, ROLE_TARGET,
+ CAM_REQ_ABORTED);
+
+ tstate = ahc->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[devinfo->lun];
+ if (lstate != NULL) {
+ ahc_queue_lstate_event(ahc, lstate,
+ devinfo->our_scsiid,
+ ahc->msgin_buf[0],
+ /*arg*/tag);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+ ahc_restart(ahc);
+ done = MSGLOOP_TERMINATED;
+ break;
+ }
+#endif
+ case MSG_TERM_IO_PROC:
+ default:
+ reject = TRUE;
+ break;
+ }
+
+ if (reject) {
+ /*
+ * Setup to reject the message.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 1;
+ ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ done = MSGLOOP_MSGCOMPLETE;
+ response = TRUE;
+ }
+
+ if (done != MSGLOOP_IN_PROG && !response)
+ /* Clear the outgoing message buffer */
+ ahc->msgout_len = 0;
+
+ return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+ struct scb *scb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int scb_index;
+ u_int last_msg;
+ int response = 0;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /* Might be necessary */
+ last_msg = ahc_inb(ahc, LAST_MSG);
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+ /*
+ * Target does not support the PPR message.
+ * Attempt to negotiate SPI-2 style.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying WDTR/SDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.ppr_options = 0;
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = 1;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+ /* note 8bit xfers */
+ printf("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * No need to clear the sync rate. If the target
+ * did not accept the command, our syncrate is
+ * unaffected. If the target started the negotiation,
+ * but rejected our response, we already cleared the
+ * sync rate before sending our WDTR.
+ */
+ if (tinfo->goal.offset != tinfo->curr.offset) {
+
+ /* Start the sync negotiation */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = 1;
+ }
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ /* note asynch xfers and clear flag */
+ ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+ "Using asynchronous transfers\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+ if (tag_type == MSG_SIMPLE_TASK) {
+ printf("(%s:%c:%d:%d): refuses tagged commands. "
+ "Performing non-tagged I/O\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahc_set_tags(ahc, devinfo, AHC_QUEUE_NONE);
+ mask = ~0x23;
+ } else {
+ printf("(%s:%c:%d:%d): refuses %s tagged commands. "
+ "Performing simple queue tagged I/O only\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, tag_type == MSG_ORDERED_TASK
+ ? "ordered" : "head of queue");
+ ahc_set_tags(ahc, devinfo, AHC_QUEUE_BASIC);
+ mask = ~0x03;
+ }
+
+ /*
+ * Resend the identify for this CCB as the target
+ * may believe that the selection is invalid otherwise.
+ */
+ ahc_outb(ahc, SCB_CONTROL,
+ ahc_inb(ahc, SCB_CONTROL) & mask);
+ scb->hscb->control &= mask;
+ ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+ /*type*/MSG_SIMPLE_TASK);
+ ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+ ahc_assert_atn(ahc);
+
+ /*
+ * This transaction is now at the head of
+ * the untagged queue for this target.
+ */
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+
+ untagged_q =
+ &(ahc->untagged_queues[devinfo->target_offset]);
+ TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+ scb->flags |= SCB_UNTAGGEDQ;
+ }
+ ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+ scb->hscb->tag);
+
+ /*
+ * Requeue all tagged commands for this target
+ * currently in our posession so they can be
+ * converted to untagged commands.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ last_msg);
+ }
+ return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int scb_index;
+ struct scb *scb;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ /*
+ * XXX Actually check data direction in the sequencer?
+ * Perhaps add datadir to some spare bits in the hscb?
+ */
+ if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+ || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
+ /*
+ * Ignore the message if we haven't
+ * seen an appropriate data phase yet.
+ */
+ } else {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing. Otherwise, subtract a byte
+ * and update the residual count accordingly.
+ */
+ uint32_t sgptr;
+
+ sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+ if ((sgptr & SG_LIST_NULL) != 0
+ && (ahc_inb(ahc, SCB_LUN) & SCB_XFERLEN_ODD) != 0) {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing.
+ */
+ } else {
+ struct ahc_dma_seg *sg;
+ uint32_t data_cnt;
+ uint32_t data_addr;
+ uint32_t sglen;
+
+ /* Pull in all of the sgptr */
+ sgptr = ahc_inl(ahc, SCB_RESIDUAL_SGPTR);
+ data_cnt = ahc_inl(ahc, SCB_RESIDUAL_DATACNT);
+
+ if ((sgptr & SG_LIST_NULL) != 0) {
+ /*
+ * The residual data count is not updated
+ * for the command run to completion case.
+ * Explicitly zero the count.
+ */
+ data_cnt &= ~AHC_SG_LEN_MASK;
+ }
+
+ data_addr = ahc_inl(ahc, SHADDR);
+
+ data_cnt += 1;
+ data_addr -= 1;
+ sgptr &= SG_PTR_MASK;
+
+ sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
+
+ sg--;
+ sglen = ahc_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST bits
+ * while setting the count to 1.
+ */
+ data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
+ data_addr = ahc_le32toh(sg->addr)
+ + (sglen & AHC_SG_LEN_MASK) - 1;
+
+ /*
+ * Increment sg so it points to the
+ * "next" sg.
+ */
+ sg++;
+ sgptr = ahc_sg_virt_to_bus(scb, sg);
+ }
+ ahc_outl(ahc, SCB_RESIDUAL_SGPTR, sgptr);
+ ahc_outl(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
+ /*
+ * Toggle the "oddness" of the transfer length
+ * to handle this mid-transfer ignore wide
+ * residue. This ensures that the oddness is
+ * correct for subsequent data transfers.
+ */
+ ahc_outb(ahc, SCB_LUN,
+ ahc_inb(ahc, SCB_LUN) ^ SCB_XFERLEN_ODD);
+ }
+ }
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ struct ahc_dma_seg *sg;
+ u_int scb_index;
+ uint32_t sgptr;
+ uint32_t resid;
+ uint32_t dataptr;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
+ | ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+
+ sgptr &= SG_PTR_MASK;
+ sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
+ | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
+
+ dataptr = ahc_le32toh(sg->addr)
+ + (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
+ - resid;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ u_int dscommand1;
+
+ dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+ ahc_outb(ahc, HADDR,
+ (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1);
+ }
+ ahc_outb(ahc, HADDR + 3, dataptr >> 24);
+ ahc_outb(ahc, HADDR + 2, dataptr >> 16);
+ ahc_outb(ahc, HADDR + 1, dataptr >> 8);
+ ahc_outb(ahc, HADDR, dataptr);
+ ahc_outb(ahc, HCNT + 2, resid >> 16);
+ ahc_outb(ahc, HCNT + 1, resid >> 8);
+ ahc_outb(ahc, HCNT, resid);
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ ahc_outb(ahc, STCNT + 2, resid >> 16);
+ ahc_outb(ahc, STCNT + 1, resid >> 8);
+ ahc_outb(ahc, STCNT, resid);
+ }
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ cam_status status, char *message, int verbose_level)
+{
+#ifdef AHC_TARGET_MODE
+ struct ahc_tmode_tstate* tstate;
+ u_int lun;
+#endif
+ int found;
+
+ found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+ status);
+
+#ifdef AHC_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target mord peripheral
+ * drivers affected by this action.
+ */
+ tstate = ahc->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+ MSG_BUS_DEV_RESET, /*arg*/0);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+#endif
+
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+ ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+
+ if (message != NULL
+ && (verbose_level <= bootverbose))
+ printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+ message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHC_TARGET_MODE
+static void
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+
+ if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+ ahc_build_transfer_msg(ahc, devinfo);
+ else
+ panic("ahc_intr: AWAITING target message with no message");
+
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahc_softc *
+ahc_alloc(void *platform_arg, char *name)
+{
+ struct ahc_softc *ahc;
+ int i;
+
+#ifndef __FreeBSD__
+ ahc = malloc(sizeof(*ahc), M_DEVBUF, M_NOWAIT);
+ if (!ahc) {
+ printf("aic7xxx: cannot malloc softc!\n");
+ free(name, M_DEVBUF);
+ return NULL;
+ }
+#else
+ ahc = device_get_softc((device_t)platform_arg);
+#endif
+ memset(ahc, 0, sizeof(*ahc));
+ ahc->seep_config = malloc(sizeof(*ahc->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->seep_config == NULL) {
+#ifndef __FreeBSD__
+ free(ahc, M_DEVBUF);
+#endif
+ free(name, M_DEVBUF);
+ return (NULL);
+ }
+ LIST_INIT(&ahc->pending_scbs);
+ /* We don't know our unit number until the OSM sets it */
+ ahc->name = name;
+ ahc->unit = -1;
+ ahc->description = NULL;
+ ahc->channel = 'A';
+ ahc->channel_b = 'B';
+ ahc->chip = AHC_NONE;
+ ahc->features = AHC_FENONE;
+ ahc->bugs = AHC_BUGNONE;
+ ahc->flags = AHC_FNONE;
+ /*
+ * Default to all error reporting enabled with the
+ * sequencer operating at its fastest speed.
+ * The bus attach code may modify this.
+ */
+ ahc->seqctl = FASTMODE;
+
+ for (i = 0; i < AHC_NUM_TARGETS; i++)
+ TAILQ_INIT(&ahc->untagged_queues[i]);
+ if (ahc_platform_alloc(ahc, platform_arg) != 0) {
+ ahc_free(ahc);
+ ahc = NULL;
+ }
+ return (ahc);
+}
+
+int
+ahc_softc_init(struct ahc_softc *ahc)
+{
+
+ /* The IRQMS bit is only valid on VL and EISA chips */
+ if ((ahc->chip & AHC_PCI) == 0)
+ ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
+ else
+ ahc->unpause = 0;
+ ahc->pause = ahc->unpause | PAUSE;
+ /* XXX The shared scb data stuff should be deprecated */
+ if (ahc->scb_data == NULL) {
+ ahc->scb_data = malloc(sizeof(*ahc->scb_data),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->scb_data == NULL)
+ return (ENOMEM);
+ memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
+ }
+
+ return (0);
+}
+
+void
+ahc_softc_insert(struct ahc_softc *ahc)
+{
+ struct ahc_softc *list_ahc;
+
+#if AHC_PCI_CONFIG > 0
+ /*
+ * Second Function PCI devices need to inherit some
+ * settings from function 0.
+ */
+ if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
+ && (ahc->features & AHC_MULTI_FUNC) != 0) {
+ TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+ ahc_dev_softc_t list_pci;
+ ahc_dev_softc_t pci;
+
+ list_pci = list_ahc->dev_softc;
+ pci = ahc->dev_softc;
+ if (ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
+ && ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)) {
+ struct ahc_softc *master;
+ struct ahc_softc *slave;
+
+ if (ahc_get_pci_function(list_pci) == 0) {
+ master = list_ahc;
+ slave = ahc;
+ } else {
+ master = ahc;
+ slave = list_ahc;
+ }
+ slave->flags &= ~AHC_BIOS_ENABLED;
+ slave->flags |=
+ master->flags & AHC_BIOS_ENABLED;
+ slave->flags &= ~AHC_PRIMARY_CHANNEL;
+ slave->flags |=
+ master->flags & AHC_PRIMARY_CHANNEL;
+ break;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Insertion sort into our list of softcs.
+ */
+ list_ahc = TAILQ_FIRST(&ahc_tailq);
+ while (list_ahc != NULL
+ && ahc_softc_comp(ahc, list_ahc) <= 0)
+ list_ahc = TAILQ_NEXT(list_ahc, links);
+ if (list_ahc != NULL)
+ TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
+ else
+ TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
+ ahc->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahc_softc *
+ahc_find_softc(struct ahc_softc *ahc)
+{
+ struct ahc_softc *list_ahc;
+
+ TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+ if (list_ahc == ahc)
+ return (ahc);
+ }
+ return (NULL);
+}
+
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+ ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+ if (ahc->name != NULL)
+ free(ahc->name, M_DEVBUF);
+ ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+ int i;
+
+ switch (ahc->init_level) {
+ default:
+ case 5:
+ ahc_shutdown(ahc);
+ /* FALLTHROUGH */
+ case 4:
+ ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 3:
+ ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
+ ahc->shared_data_dmamap);
+ ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap);
+ /* FALLTHROUGH */
+ case 2:
+ ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+ case 1:
+#ifndef __linux__
+ ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
+#endif
+ break;
+ case 0:
+ break;
+ }
+
+#ifndef __linux__
+ ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
+#endif
+ ahc_platform_free(ahc);
+ ahc_fini_scbdata(ahc);
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ struct ahc_tmode_tstate *tstate;
+
+ tstate = ahc->enabled_targets[i];
+ if (tstate != NULL) {
+#ifdef AHC_TARGET_MODE
+ int j;
+
+ for (j = 0; j < AHC_NUM_LUNS; j++) {
+ struct ahc_tmode_lstate *lstate;
+
+ lstate = tstate->enabled_luns[j];
+ if (lstate != NULL) {
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+ }
+ }
+#endif
+ free(tstate, M_DEVBUF);
+ }
+ }
+#ifdef AHC_TARGET_MODE
+ if (ahc->black_hole != NULL) {
+ xpt_free_path(ahc->black_hole->path);
+ free(ahc->black_hole, M_DEVBUF);
+ }
+#endif
+ if (ahc->name != NULL)
+ free(ahc->name, M_DEVBUF);
+ if (ahc->seep_config != NULL)
+ free(ahc->seep_config, M_DEVBUF);
+#ifndef __FreeBSD__
+ free(ahc, M_DEVBUF);
+#endif
+ return;
+}
+
+void
+ahc_shutdown(void *arg)
+{
+ struct ahc_softc *ahc;
+ int i;
+
+ ahc = (struct ahc_softc *)arg;
+
+ /* This will reset most registers to 0, but not all */
+ ahc_reset(ahc, /*reinit*/FALSE);
+ ahc_outb(ahc, SCSISEQ, 0);
+ ahc_outb(ahc, SXFRCTL0, 0);
+ ahc_outb(ahc, DSPCISTATUS, 0);
+
+ for (i = TARG_SCSIRATE; i < SCSICONF; i++)
+ ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset. If "reinit" is
+ * non-zero, this reset occured after initial configuration
+ * and the caller requests that the chip be fully reinitialized
+ * to a runable state. Chip interrupts are *not* enabled after
+ * a reinitialization. The caller must enable interrupts via
+ * ahc_intr_enable().
+ */
+int
+ahc_reset(struct ahc_softc *ahc, int reinit)
+{
+ u_int sblkctl;
+ u_int sxfrctl1_a, sxfrctl1_b;
+ int error;
+ int wait;
+
+ /*
+ * Preserve the value of the SXFRCTL1 register for all channels.
+ * It contains settings that affect termination and we don't want
+ * to disturb the integrity of the bus.
+ */
+ ahc_pause(ahc);
+ if ((ahc_inb(ahc, HCNTRL) & CHIPRST) != 0) {
+ /*
+ * The chip has not been initialized since
+ * PCI/EISA/VLB bus reset. Don't trust
+ * "left over BIOS data".
+ */
+ ahc->flags |= AHC_NO_BIOS_INIT;
+ }
+ sxfrctl1_b = 0;
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+ u_int sblkctl;
+
+ /*
+ * Save channel B's settings in case this chip
+ * is setup for TWIN channel operation.
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+ sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+ ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+ }
+ sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+ ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+ /*
+ * Ensure that the reset has finished. We delay 1000us
+ * prior to reading the register to make sure the chip
+ * has sufficiently completed its reset to handle register
+ * accesses.
+ */
+ wait = 1000;
+ do {
+ ahc_delay(1000);
+ } while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+ if (wait == 0) {
+ printf("%s: WARNING - Failed chip reset! "
+ "Trying to initialize anyway.\n", ahc_name(ahc));
+ }
+ ahc_outb(ahc, HCNTRL, ahc->pause);
+
+ /* Determine channel configuration */
+ sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+ /* No Twin Channel PCI cards */
+ if ((ahc->chip & AHC_PCI) != 0)
+ sblkctl &= ~SELBUSB;
+ switch (sblkctl) {
+ case 0:
+ /* Single Narrow Channel */
+ break;
+ case 2:
+ /* Wide Channel */
+ ahc->features |= AHC_WIDE;
+ break;
+ case 8:
+ /* Twin Channel */
+ ahc->features |= AHC_TWIN;
+ break;
+ default:
+ printf(" Unsupported adapter type. Ignoring\n");
+ return(-1);
+ }
+
+ /*
+ * Reload sxfrctl1.
+ *
+ * We must always initialize STPWEN to 1 before we
+ * restore the saved values. STPWEN is initialized
+ * to a tri-state condition which can only be cleared
+ * by turning it on.
+ */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ u_int sblkctl;
+
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+ ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+ ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+ }
+ ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+ error = 0;
+ if (reinit != 0)
+ /*
+ * If a recovery action has forced a chip reset,
+ * re-initialize the chip to our liking.
+ */
+ error = ahc->bus_chip_init(ahc);
+#ifdef AHC_DUMP_SEQ
+ else
+ ahc_dumpseq(ahc);
+#endif
+
+ return (error);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+ int i;
+
+ for (i = 0; i < AHC_SCB_MAX; i++) {
+
+ ahc_outb(ahc, SCBPTR, i);
+ ahc_outb(ahc, SCB_BASE, i);
+ if (ahc_inb(ahc, SCB_BASE) != i)
+ break;
+ ahc_outb(ahc, SCBPTR, 0);
+ if (ahc_inb(ahc, SCB_BASE) != 0)
+ break;
+ }
+ return (i);
+}
+
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ dma_addr_t *baddr;
+
+ baddr = (dma_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+ int scbsize;
+ int i;
+
+ scbsize = 32;
+ if ((ahc->flags & AHC_LSCBS_ENABLED) != 0)
+ scbsize = 64;
+
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ int j;
+
+ ahc_outb(ahc, SCBPTR, i);
+
+ /*
+ * Touch all SCB bytes to avoid parity errors
+ * should one of our debugging routines read
+ * an otherwise uninitiatlized byte.
+ */
+ for (j = 0; j < scbsize; j++)
+ ahc_outb(ahc, SCB_BASE+j, 0xFF);
+
+ /* Clear the control byte. */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ /* Set the next pointer */
+ if ((ahc->flags & AHC_PAGESCBS) != 0)
+ ahc_outb(ahc, SCB_NEXT, i+1);
+ else
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+ /* Make the tag number, SCSIID, and lun invalid */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+ ahc_outb(ahc, SCB_SCSIID, 0xFF);
+ ahc_outb(ahc, SCB_LUN, 0xFF);
+ }
+
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ /* SCB 0 heads the free list. */
+ ahc_outb(ahc, FREE_SCBH, 0);
+ } else {
+ /* No free list. */
+ ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+ }
+
+ /* Make sure that the last SCB terminates the free list */
+ ahc_outb(ahc, SCBPTR, i-1);
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+
+ scb_data = ahc->scb_data;
+ SLIST_INIT(&scb_data->free_scbs);
+ SLIST_INIT(&scb_data->sg_maps);
+
+ /* Allocate SCB resources */
+ scb_data->scbarray =
+ (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
+ M_DEVBUF, M_NOWAIT);
+ if (scb_data->scbarray == NULL)
+ return (ENOMEM);
+ memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
+
+ /* Determine the number of hardware SCBs and initialize them */
+
+ scb_data->maxhscbs = ahc_probe_scbs(ahc);
+ if (ahc->scb_data->maxhscbs == 0) {
+ printf("%s: No SCB space found\n", ahc_name(ahc));
+ return (ENXIO);
+ }
+
+ /*
+ * Create our DMA tags. These tags define the kinds of device
+ * accessible memory allocations and memory mappings we will
+ * need to perform during normal operation.
+ *
+ * Unless we need to further restrict the allocation, we rely
+ * on the restrictions of the parent dmat, hence the common
+ * use of MAXADDR and MAXSIZE.
+ */
+
+ /* DMA tag for our hardware scb structures */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->hscb_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Allocation for our hscbs */
+ if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
+ (void **)&scb_data->hscbs,
+ BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* And permanently map them */
+ ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
+ scb_data->hscbs,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+ ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
+
+ scb_data->init_level++;
+
+ /* DMA tag for our sense buffers */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sense_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Allocate them */
+ if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
+ (void **)&scb_data->sense,
+ BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* And permanently map them */
+ ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
+ scb_data->sense,
+ AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+ ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
+
+ scb_data->init_level++;
+
+ /* DMA tag for our S/G structures. We allocate in page sized chunks */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/8,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ PAGE_SIZE, /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &scb_data->sg_dmat) != 0) {
+ goto error_exit;
+ }
+
+ scb_data->init_level++;
+
+ /* Perform initial CCB allocation */
+ memset(scb_data->hscbs, 0,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
+ ahc_alloc_scbs(ahc);
+
+ if (scb_data->numscbs == 0) {
+ printf("%s: ahc_init_scbdata - "
+ "Unable to allocate initial scbs\n",
+ ahc_name(ahc));
+ goto error_exit;
+ }
+
+ /*
+ * Reserve the next queued SCB.
+ */
+ ahc->next_queued_scb = ahc_get_scb(ahc);
+
+ /*
+ * Note that we were successfull
+ */
+ return (0);
+
+error_exit:
+
+ return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+
+ scb_data = ahc->scb_data;
+ if (scb_data == NULL)
+ return;
+
+ switch (scb_data->init_level) {
+ default:
+ case 7:
+ {
+ struct sg_map_node *sg_map;
+
+ while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+ ahc_dmamap_unload(ahc, scb_data->sg_dmat,
+ sg_map->sg_dmamap);
+ ahc_dmamem_free(ahc, scb_data->sg_dmat,
+ sg_map->sg_vaddr,
+ sg_map->sg_dmamap);
+ free(sg_map, M_DEVBUF);
+ }
+ ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
+ }
+ case 6:
+ ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+ scb_data->sense_dmamap);
+ case 5:
+ ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
+ scb_data->sense_dmamap);
+ ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
+ scb_data->sense_dmamap);
+ case 4:
+ ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+ case 3:
+ ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+ scb_data->hscb_dmamap);
+ case 2:
+ ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
+ scb_data->hscb_dmamap);
+ ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
+ scb_data->hscb_dmamap);
+ case 1:
+ ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+ break;
+ case 0:
+ break;
+ }
+ if (scb_data->scbarray != NULL)
+ free(scb_data->scbarray, M_DEVBUF);
+}
+
+void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+ struct scb *next_scb;
+ struct sg_map_node *sg_map;
+ dma_addr_t physaddr;
+ struct ahc_dma_seg *segs;
+ int newcount;
+ int i;
+
+ scb_data = ahc->scb_data;
+ if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
+ /* Can't allocate any more */
+ return;
+
+ next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+ sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+ if (sg_map == NULL)
+ return;
+
+ /* Allocate S/G space for the next batch of SCBS */
+ if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
+ (void **)&sg_map->sg_vaddr,
+ BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
+ free(sg_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+ ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
+ sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
+ &sg_map->sg_physaddr, /*flags*/0);
+
+ segs = sg_map->sg_vaddr;
+ physaddr = sg_map->sg_physaddr;
+
+ newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+ newcount = MIN(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
+ for (i = 0; i < newcount; i++) {
+ struct scb_platform_data *pdata;
+#ifndef __linux__
+ int error;
+#endif
+ pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+ M_DEVBUF, M_NOWAIT);
+ if (pdata == NULL)
+ break;
+ next_scb->platform_data = pdata;
+ next_scb->sg_map = sg_map;
+ next_scb->sg_list = segs;
+ /*
+ * The sequencer always starts with the second entry.
+ * The first entry is embedded in the scb.
+ */
+ next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+ next_scb->ahc_softc = ahc;
+ next_scb->flags = SCB_FREE;
+#ifndef __linux__
+ error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
+ &next_scb->dmamap);
+ if (error != 0)
+ break;
+#endif
+ next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+ next_scb->hscb->tag = ahc->scb_data->numscbs;
+ SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+ next_scb, links.sle);
+ segs += AHC_NSEG;
+ physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+ next_scb++;
+ ahc->scb_data->numscbs++;
+ }
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+ int len;
+
+ len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+ buf += len;
+ if ((ahc->features & AHC_TWIN) != 0)
+ len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+ "B SCSI Id=%d, primary %c, ",
+ ahc->our_id, ahc->our_id_b,
+ (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
+ else {
+ const char *speed;
+ const char *type;
+
+ speed = "";
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ speed = "Ultra ";
+ } else if ((ahc->features & AHC_DT) != 0) {
+ speed = "Ultra160 ";
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ speed = "Ultra2 ";
+ }
+ if ((ahc->features & AHC_WIDE) != 0) {
+ type = "Wide";
+ } else {
+ type = "Single";
+ }
+ len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
+ speed, type, ahc->channel, ahc->our_id);
+ }
+ buf += len;
+
+ if ((ahc->flags & AHC_PAGESCBS) != 0)
+ sprintf(buf, "%d/%d SCBs",
+ ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
+ else
+ sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
+int
+ahc_chip_init(struct ahc_softc *ahc)
+{
+ int term;
+ int error;
+ u_int i;
+ u_int scsi_conf;
+ u_int scsiseq_template;
+ uint32_t physaddr;
+
+ ahc_outb(ahc, SEQ_FLAGS, 0);
+ ahc_outb(ahc, SEQ_FLAGS2, 0);
+
+ /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+ if (ahc->features & AHC_TWIN) {
+
+ /*
+ * Setup Channel B first.
+ */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+ term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+ ahc_outb(ahc, SCSIID, ahc->our_id_b);
+ scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+ /* Select Channel A */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+ }
+ term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+ scsi_conf = ahc_inb(ahc, SCSICONF);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term|ahc->seltime
+ |ENSTIMER|ACTNEGEN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+ /* There are no untagged SCBs active yet. */
+ for (i = 0; i < 16; i++) {
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ int lun;
+
+ /*
+ * The SCB based BTT allows an entry per
+ * target and lun pair.
+ */
+ for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+ }
+ }
+
+ /* All of our queues are empty */
+ for (i = 0; i < 256; i++)
+ ahc->qoutfifo[i] = SCB_LIST_NULL;
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
+
+ for (i = 0; i < 256; i++)
+ ahc->qinfifo[i] = SCB_LIST_NULL;
+
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ ahc_outb(ahc, TARGID, 0);
+ ahc_outb(ahc, TARGID + 1, 0);
+ }
+
+ /*
+ * Tell the sequencer where it can find our arrays in memory.
+ */
+ physaddr = ahc->scb_data->hscb_busaddr;
+ ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+ physaddr = ahc->shared_data_busaddr;
+ ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+ /*
+ * Initialize the group code to command length table.
+ * This overrides the values in TARG_SCSIRATE, so only
+ * setup the table after we have processed that information.
+ */
+ ahc_outb(ahc, CMDSIZE_TABLE, 5);
+ ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+ ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+ ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+
+ if ((ahc->features & AHC_HS_MAILBOX) != 0)
+ ahc_outb(ahc, HS_MAILBOX, 0);
+
+ /* Tell the sequencer of our initial queue positions */
+ if ((ahc->features & AHC_TARGETMODE) != 0) {
+ ahc->tqinfifonext = 1;
+ ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+ ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+ }
+ ahc->qinfifonext = 0;
+ ahc->qoutfifonext = 0;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ ahc_outb(ahc, SNSCB_QOFF, ahc->qinfifonext);
+ ahc_outb(ahc, SDSCB_QOFF, 0);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ ahc_outb(ahc, QINPOS, ahc->qinfifonext);
+ ahc_outb(ahc, QOUTPOS, ahc->qoutfifonext);
+ }
+
+ /* We don't have any waiting selections */
+ ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+ /* Our disconnection list is empty too */
+ ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+ /* Message out buffer starts empty */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+ /*
+ * Setup the allowed SCSI Sequences based on operational mode.
+ * If we are a target, we'll enalbe select in operations once
+ * we've had a lun enabled.
+ */
+ scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
+ if ((ahc->flags & AHC_INITIATORROLE) != 0)
+ scsiseq_template |= ENRSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
+
+ /* Initialize our list of free SCBs. */
+ ahc_build_free_scb_list(ahc);
+
+ /*
+ * Tell the sequencer which SCB will be the next one it receives.
+ */
+ ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+ /*
+ * Load the Sequencer program and Enable the adapter
+ * in "fast" mode.
+ */
+ if (bootverbose)
+ printf("%s: Downloading Sequencer Program...",
+ ahc_name(ahc));
+
+ error = ahc_loadseq(ahc);
+ if (error != 0)
+ return (error);
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int wait;
+
+ /*
+ * Wait for up to 500ms for our transceivers
+ * to settle. If the adapter does not have
+ * a cable attached, the transceivers may
+ * never settle, so don't complain if we
+ * fail here.
+ */
+ for (wait = 5000;
+ (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+ wait--)
+ ahc_delay(100);
+ }
+ ahc_restart(ahc);
+ return (0);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+ int max_targ;
+ u_int i;
+ u_int scsi_conf;
+ u_int ultraenb;
+ u_int discenable;
+ u_int tagenable;
+ size_t driver_data_size;
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_DEBUG_SEQUENCER) != 0)
+ ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
+
+#ifdef AHC_PRINT_SRAM
+ printf("Scratch Ram:");
+ for (i = 0x20; i < 0x5f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0x70; i < 0x7f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ }
+ printf ("\n");
+ /*
+ * Reading uninitialized scratch ram may
+ * generate parity errors.
+ */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+#endif
+ max_targ = 15;
+
+ /*
+ * Assume we have a board at this stage and it has been reset.
+ */
+ if ((ahc->flags & AHC_USEDEFAULTS) != 0)
+ ahc->our_id = ahc->our_id_b = 7;
+
+ /*
+ * Default to allowing initiator operations.
+ */
+ ahc->flags |= AHC_INITIATORROLE;
+
+ /*
+ * Only allow target mode features if this unit has them enabled.
+ */
+ if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+ ahc->features &= ~AHC_TARGETMODE;
+
+#ifndef __linux__
+ /* DMA tag for mapping buffers into device visible space. */
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/ahc->flags & AHC_39BIT_ADDRESSING
+ ? (dma_addr_t)0x7FFFFFFFFFULL
+ : BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/(AHC_NSEG - 1) * PAGE_SIZE,
+ /*nsegments*/AHC_NSEG,
+ /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
+ /*flags*/BUS_DMA_ALLOCNOW,
+ &ahc->buffer_dmat) != 0) {
+ return (ENOMEM);
+ }
+#endif
+
+ ahc->init_level++;
+
+ /*
+ * DMA tag for our command fifos and other data in system memory
+ * the card's sequencer must be able to access. For initiator
+ * roles, we need to allocate space for the qinfifo and qoutfifo.
+ * The qinfifo and qoutfifo are composed of 256 1 byte elements.
+ * When providing for the target mode role, we must additionally
+ * provide space for the incoming target command fifo and an extra
+ * byte to deal with a dma bug in some chip versions.
+ */
+ driver_data_size = 2 * 256 * sizeof(uint8_t);
+ if ((ahc->features & AHC_TARGETMODE) != 0)
+ driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+ + /*DMA WideOdd Bug Buffer*/1;
+ if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+ /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ driver_data_size,
+ /*nsegments*/1,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &ahc->shared_data_dmat) != 0) {
+ return (ENOMEM);
+ }
+
+ ahc->init_level++;
+
+ /* Allocation of driver data */
+ if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
+ (void **)&ahc->qoutfifo,
+ BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
+ return (ENOMEM);
+ }
+
+ ahc->init_level++;
+
+ /* And permanently map it in */
+ ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+ ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
+ &ahc->shared_data_busaddr, /*flags*/0);
+
+ if ((ahc->features & AHC_TARGETMODE) != 0) {
+ ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+ ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+ ahc->dma_bug_buf = ahc->shared_data_busaddr
+ + driver_data_size - 1;
+ /* All target command blocks start out invalid. */
+ for (i = 0; i < AHC_TMODE_CMDS; i++)
+ ahc->targetcmds[i].cmd_valid = 0;
+ ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
+ ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+ }
+ ahc->qinfifo = &ahc->qoutfifo[256];
+
+ ahc->init_level++;
+
+ /* Allocate SCB data now that buffer_dmat is initialized */
+ if (ahc->scb_data->maxhscbs == 0)
+ if (ahc_init_scbdata(ahc) != 0)
+ return (ENOMEM);
+
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate ahc_tmode_tstate. "
+ "Failing attach\n", ahc_name(ahc));
+ return (ENOMEM);
+ }
+
+ if ((ahc->features & AHC_TWIN) != 0) {
+ if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+ printf("%s: unable to allocate ahc_tmode_tstate. "
+ "Failing attach\n", ahc_name(ahc));
+ return (ENOMEM);
+ }
+ }
+
+ if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
+ ahc->flags |= AHC_PAGESCBS;
+ } else {
+ ahc->flags &= ~AHC_PAGESCBS;
+ }
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_MISC) {
+ printf("%s: hardware scb %u bytes; kernel scb %u bytes; "
+ "ahc_dma %u bytes\n",
+ ahc_name(ahc),
+ (u_int)sizeof(struct hardware_scb),
+ (u_int)sizeof(struct scb),
+ (u_int)sizeof(struct ahc_dma_seg));
+ }
+#endif /* AHC_DEBUG */
+
+ /*
+ * Look at the information that board initialization or
+ * the board bios has left us.
+ */
+ if (ahc->features & AHC_TWIN) {
+ scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+ if ((scsi_conf & RESET_SCSI) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0)
+ ahc->flags |= AHC_RESET_BUS_B;
+ }
+
+ scsi_conf = ahc_inb(ahc, SCSICONF);
+ if ((scsi_conf & RESET_SCSI) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0)
+ ahc->flags |= AHC_RESET_BUS_A;
+
+ ultraenb = 0;
+ tagenable = ALL_TARGETS_MASK;
+
+ /* Grab the disconnection disable table and invert it for our needs */
+ if ((ahc->flags & AHC_USEDEFAULTS) != 0) {
+ printf("%s: Host Adapter Bios disabled. Using default SCSI "
+ "device parameters\n", ahc_name(ahc));
+ ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+ AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+ discenable = ALL_TARGETS_MASK;
+ if ((ahc->features & AHC_ULTRA) != 0)
+ ultraenb = ALL_TARGETS_MASK;
+ } else {
+ discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+ | ahc_inb(ahc, DISC_DSB));
+ if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+ ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+ | ahc_inb(ahc, ULTRA_ENB);
+ }
+
+ if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
+ }
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ /* Default to async narrow across the board */
+ memset(tinfo, 0, sizeof(*tinfo));
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ if ((ahc->features & AHC_WIDE) != 0)
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+ /*
+ * These will be truncated when we determine the
+ * connection type we have with the target.
+ */
+ tinfo->user.period = ahc_syncrates->period;
+ tinfo->user.offset = MAX_OFFSET;
+ } else {
+ u_int scsirate;
+ uint16_t mask;
+
+ /* Take the settings leftover in scratch RAM. */
+ scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+ mask = (0x01 << i);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+ u_int maxsync;
+
+ if ((scsirate & SOFS) == 0x0F) {
+ /*
+ * Haven't negotiated yet,
+ * so the format is different.
+ */
+ scsirate = (scsirate & SXFR) >> 4
+ | (ultraenb & mask)
+ ? 0x08 : 0x0
+ | (scsirate & WIDEXFER);
+ offset = MAX_OFFSET_ULTRA2;
+ } else
+ offset = ahc_inb(ahc, TARG_OFFSET + i);
+ if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+ /* Set to the lowest sync rate, 5MHz */
+ scsirate |= 0x1c;
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ if ((ahc->features & AHC_DT) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ tinfo->user.period =
+ ahc_find_period(ahc, scsirate, maxsync);
+ if (offset == 0)
+ tinfo->user.period = 0;
+ else
+ tinfo->user.offset = MAX_OFFSET;
+ if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+ && (ahc->features & AHC_DT) != 0)
+ tinfo->user.ppr_options =
+ MSG_EXT_PPR_DT_REQ;
+ } else if ((scsirate & SOFS) != 0) {
+ if ((scsirate & SXFR) == 0x40
+ && (ultraenb & mask) != 0) {
+ /* Treat 10MHz as a non-ultra speed */
+ scsirate &= ~SXFR;
+ ultraenb &= ~mask;
+ }
+ tinfo->user.period =
+ ahc_find_period(ahc, scsirate,
+ (ultraenb & mask)
+ ? AHC_SYNCRATE_ULTRA
+ : AHC_SYNCRATE_FAST);
+ if (tinfo->user.period != 0)
+ tinfo->user.offset = MAX_OFFSET;
+ }
+ if (tinfo->user.period == 0)
+ tinfo->user.offset = 0;
+ if ((scsirate & WIDEXFER) != 0
+ && (ahc->features & AHC_WIDE) != 0)
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+ tinfo->user.protocol_version = 4;
+ if ((ahc->features & AHC_DT) != 0)
+ tinfo->user.transport_version = 3;
+ else
+ tinfo->user.transport_version = 2;
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
+ }
+ tstate->ultraenb = 0;
+ }
+ ahc->user_discenable = discenable;
+ ahc->user_tagenable = tagenable;
+
+ return (ahc->bus_chip_init(ahc));
+}
+
+void
+ahc_intr_enable(struct ahc_softc *ahc, int enable)
+{
+ u_int hcntrl;
+
+ hcntrl = ahc_inb(ahc, HCNTRL);
+ hcntrl &= ~INTEN;
+ ahc->pause &= ~INTEN;
+ ahc->unpause &= ~INTEN;
+ if (enable) {
+ hcntrl |= INTEN;
+ ahc->pause |= INTEN;
+ ahc->unpause |= INTEN;
+ }
+ ahc_outb(ahc, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
+{
+ int intstat;
+ int maxloops;
+ int paused;
+
+ maxloops = 1000;
+ ahc->flags |= AHC_ALL_INTERRUPTS;
+ paused = FALSE;
+ do {
+ if (paused)
+ ahc_unpause(ahc);
+ ahc_intr(ahc);
+ ahc_pause(ahc);
+ paused = TRUE;
+ ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_clear_critical_section(ahc);
+ intstat = ahc_inb(ahc, INTSTAT);
+ } while (--maxloops
+ && (intstat != 0xFF || (ahc->features & AHC_REMOVABLE) == 0)
+ && ((intstat & INT_PEND) != 0
+ || (ahc_inb(ahc, SSTAT0) & (SELDO|SELINGO)) != 0));
+ if (maxloops == 0) {
+ printf("Infinite interrupt loop, INTSTAT = %x",
+ ahc_inb(ahc, INTSTAT));
+ }
+ ahc_platform_flushwork(ahc);
+ ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
+
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+
+ ahc_pause_and_flushwork(ahc);
+
+ if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+ ahc_unpause(ahc);
+ return (EBUSY);
+ }
+
+#ifdef AHC_TARGET_MODE
+ /*
+ * XXX What about ATIOs that have not yet been serviced?
+ * Perhaps we should just refuse to be suspended if we
+ * are acting in a target role.
+ */
+ if (ahc->pending_device != NULL) {
+ ahc_unpause(ahc);
+ return (EBUSY);
+ }
+#endif
+ ahc_shutdown(ahc);
+ return (0);
+}
+
+int
+ahc_resume(struct ahc_softc *ahc)
+{
+
+ ahc_reset(ahc, /*reinit*/TRUE);
+ ahc_intr_enable(ahc, TRUE);
+ ahc_restart(ahc);
+ return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+ u_int scbid;
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
+ }
+
+ return (scbid);
+}
+
+void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
+ }
+}
+
+void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
+ }
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+ char channel, int lun, u_int tag, role_t role)
+{
+ int targ = SCB_GET_TARGET(ahc, scb);
+ char chan = SCB_GET_CHANNEL(ahc, scb);
+ int slun = SCB_GET_LUN(scb);
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+ if (match != 0)
+ match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+ if (match != 0) {
+#ifdef AHC_TARGET_MODE
+ int group;
+
+ group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+ if (role == ROLE_INITIATOR) {
+ match = (group != XPT_FC_GROUP_TMODE)
+ && ((tag == scb->hscb->tag)
+ || (tag == SCB_LIST_NULL));
+ } else if (role == ROLE_TARGET) {
+ match = (group == XPT_FC_GROUP_TMODE)
+ && ((tag == scb->io_ctx->csio.tag_id)
+ || (tag == SCB_LIST_NULL));
+ }
+#else /* !AHC_TARGET_MODE */
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+ }
+
+ return match;
+}
+
+void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+ int target;
+ char channel;
+ int lun;
+
+ target = SCB_GET_TARGET(ahc, scb);
+ lun = SCB_GET_LUN(scb);
+ channel = SCB_GET_CHANNEL(ahc, scb);
+
+ ahc_search_qinfifo(ahc, target, channel, lun,
+ /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ ahc_platform_freeze_devq(ahc, scb);
+}
+
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct scb *prev_scb;
+
+ prev_scb = NULL;
+ if (ahc_qinfifo_count(ahc) != 0) {
+ u_int prev_tag;
+ uint8_t prev_pos;
+
+ prev_pos = ahc->qinfifonext - 1;
+ prev_tag = ahc->qinfifo[prev_pos];
+ prev_scb = ahc_lookup_scb(ahc, prev_tag);
+ }
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ }
+}
+
+static void
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+ struct scb *scb)
+{
+ if (prev_scb == NULL) {
+ ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+ } else {
+ prev_scb->hscb->next = scb->hscb->tag;
+ ahc_sync_scb(ahc, prev_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ }
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+ scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+ ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahc_qinfifo_count(struct ahc_softc *ahc)
+{
+ uint8_t qinpos;
+ uint8_t diff;
+
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ qinpos = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinpos);
+ } else
+ qinpos = ahc_inb(ahc, QINPOS);
+ diff = ahc->qinfifonext - qinpos;
+ return (diff);
+}
+
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahc_search_action action)
+{
+ struct scb *scb;
+ struct scb *prev_scb;
+ uint8_t qinstart;
+ uint8_t qinpos;
+ uint8_t qintail;
+ uint8_t next;
+ uint8_t prev;
+ uint8_t curscbptr;
+ int found;
+ int have_qregs;
+
+ qintail = ahc->qinfifonext;
+ have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+ if (have_qregs) {
+ qinstart = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinstart);
+ } else
+ qinstart = ahc_inb(ahc, QINPOS);
+ qinpos = qinstart;
+ found = 0;
+ prev_scb = NULL;
+
+ if (action == SEARCH_COMPLETE) {
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+ }
+
+ /*
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
+ */
+ ahc->qinfifonext = qinpos;
+ ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+ while (qinpos != qintail) {
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+ if (scb == NULL) {
+ printf("qinpos = %d, SCB index = %d\n",
+ qinpos, ahc->qinfifo[qinpos]);
+ panic("Loop 1\n");
+ }
+
+ if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in qinfifo\n");
+ ahc_done(ahc, scb);
+
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ break;
+ case SEARCH_COUNT:
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ prev_scb = scb;
+ break;
+ }
+ } else {
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ prev_scb = scb;
+ }
+ qinpos++;
+ }
+
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ }
+
+ if (action != SEARCH_COUNT
+ && (found != 0)
+ && (qinstart != ahc->qinfifonext)) {
+ /*
+ * The sequencer may be in the process of dmaing
+ * down the SCB at the beginning of the queue.
+ * This could be problematic if either the first,
+ * or the second SCB is removed from the queue
+ * (the first SCB includes a pointer to the "next"
+ * SCB to dma). If we have removed any entries, swap
+ * the first element in the queue with the next HSCB
+ * so the sequencer will notice that NEXT_QUEUED_SCB
+ * has changed during its dma attempt and will retry
+ * the DMA.
+ */
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
+
+ if (scb == NULL) {
+ printf("found = %d, qinstart = %d, qinfifionext = %d\n",
+ found, qinstart, ahc->qinfifonext);
+ panic("First/Second Qinfifo fixup\n");
+ }
+ /*
+ * ahc_swap_with_next_hscb forces our next pointer to
+ * point to the reserved SCB for future commands. Save
+ * and restore our original next pointer to maintain
+ * queue integrity.
+ */
+ next = scb->hscb->next;
+ ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+ ahc_swap_with_next_hscb(ahc, scb);
+ scb->hscb->next = next;
+ ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+ /* Tell the card about the new head of the qinfifo. */
+ ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+ /* Fixup the tail "next" pointer. */
+ qintail = ahc->qinfifonext - 1;
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+ scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+ }
+
+ /*
+ * Search waiting for selection list.
+ */
+ curscbptr = ahc_inb(ahc, SCBPTR);
+ next = ahc_inb(ahc, WAITING_SCBH); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ uint8_t scb_index;
+
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ printf("Waiting List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ }
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("scb_index = %d, next = %d\n",
+ scb_index, next);
+ panic("Waiting List traversal\n");
+ }
+ if (ahc_match_scb(ahc, scb, target, channel,
+ lun, SCB_LIST_NULL, role)) {
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb,
+ status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in Waiting List\n");
+ ahc_done(ahc, scb);
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ next = ahc_rem_wscb(ahc, next, prev);
+ break;
+ case SEARCH_COUNT:
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ break;
+ }
+ } else {
+
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ ahc_outb(ahc, SCBPTR, curscbptr);
+
+ found += ahc_search_untagged_queues(ahc, /*ahc_io_ctx_t*/NULL, target,
+ channel, lun, status, action);
+
+ if (action == SEARCH_COMPLETE)
+ ahc_release_untagged_queues(ahc);
+ return (found);
+}
+
+int
+ahc_search_untagged_queues(struct ahc_softc *ahc, ahc_io_ctx_t ctx,
+ int target, char channel, int lun, uint32_t status,
+ ahc_search_action action)
+{
+ struct scb *scb;
+ int maxtarget;
+ int found;
+ int i;
+
+ if (action == SEARCH_COMPLETE) {
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+ }
+
+ found = 0;
+ i = 0;
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+ } else {
+ maxtarget = 0;
+ }
+
+ for (; i < maxtarget; i++) {
+ struct scb_tailq *untagged_q;
+ struct scb *next_scb;
+
+ untagged_q = &(ahc->untagged_queues[i]);
+ next_scb = TAILQ_FIRST(untagged_q);
+ while (next_scb != NULL) {
+
+ scb = next_scb;
+ next_scb = TAILQ_NEXT(scb, links.tqe);
+
+ /*
+ * The head of the list may be the currently
+ * active untagged command for a device.
+ * We're only searching for commands that
+ * have not been started. A transaction
+ * marked active but still in the qinfifo
+ * is removed by the qinfifo scanning code
+ * above.
+ */
+ if ((scb->flags & SCB_ACTIVE) != 0)
+ continue;
+
+ if (ahc_match_scb(ahc, scb, target, channel, lun,
+ SCB_LIST_NULL, ROLE_INITIATOR) == 0
+ || (ctx != NULL && ctx != scb->io_ctx))
+ continue;
+
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in untaggedQ\n");
+ ahc_done(ahc, scb);
+ break;
+ }
+ case SEARCH_REMOVE:
+ scb->flags &= ~SCB_UNTAGGEDQ;
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ break;
+ case SEARCH_COUNT:
+ break;
+ }
+ }
+ }
+
+ if (action == SEARCH_COMPLETE)
+ ahc_release_untagged_queues(ahc);
+ return (found);
+}
+
+int
+ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, int stop_on_first, int remove,
+ int save_state)
+{
+ struct scb *scbp;
+ u_int next;
+ u_int prev;
+ u_int count;
+ u_int active_scb;
+
+ count = 0;
+ next = ahc_inb(ahc, DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+
+ if (save_state) {
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+ } else
+ /* Silence compiler */
+ active_scb = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ u_int scb_index;
+
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ printf("Disconnected List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ }
+
+ if (next == prev) {
+ panic("Disconnected List Loop. "
+ "cur SCBPTR == %x, prev SCBPTR == %x.",
+ next, prev);
+ }
+ scbp = ahc_lookup_scb(ahc, scb_index);
+ if (ahc_match_scb(ahc, scbp, target, channel, lun,
+ tag, ROLE_INITIATOR)) {
+ count++;
+ if (remove) {
+ next =
+ ahc_rem_scb_from_disc_list(ahc, prev, next);
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ if (stop_on_first)
+ break;
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ if (save_state)
+ ahc_outb(ahc, SCBPTR, active_scb);
+ return (count);
+}
+
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
+static u_int
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
+{
+ u_int next;
+
+ ahc_outb(ahc, SCBPTR, scbptr);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ if (prev != SCB_LIST_NULL) {
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ } else
+ ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+ return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs. This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+ /*
+ * Invalidate the tag so that our abort
+ * routines don't think it's active.
+ */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+ ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+ }
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{
+ u_int curscb, next;
+
+ /*
+ * Select the SCB we want to abort and
+ * pull the next pointer out of it.
+ */
+ curscb = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, scbpos);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ /* Clear the necessary fields */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ /* update the waiting list */
+ if (prev == SCB_LIST_NULL) {
+ /* First in the list */
+ ahc_outb(ahc, WAITING_SCBH, next);
+
+ /*
+ * Ensure we aren't attempting to perform
+ * selection for this entry.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else {
+ /*
+ * Select the scb that pointed to us
+ * and update its next pointer.
+ */
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ }
+
+ /*
+ * Point us back at the original scb position.
+ */
+ ahc_outb(ahc, SCBPTR, curscb);
+ return next;
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ struct scb *scbp;
+ struct scb *scbp_next;
+ u_int active_scb;
+ int i, j;
+ int maxtarget;
+ int minlun;
+ int maxlun;
+
+ int found;
+
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+
+ found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+ role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ /*
+ * Clean out the busy target table for any untagged commands.
+ */
+ i = 0;
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+
+ if (lun == CAM_LUN_WILDCARD) {
+
+ /*
+ * Unless we are using an SCB based
+ * busy targets table, there is only
+ * one table entry for all luns of
+ * a target.
+ */
+ minlun = 0;
+ maxlun = 1;
+ if ((ahc->flags & AHC_SCB_BTT) != 0)
+ maxlun = AHC_NUM_LUNS;
+ } else {
+ minlun = lun;
+ maxlun = lun + 1;
+ }
+
+ if (role != ROLE_TARGET) {
+ for (;i < maxtarget; i++) {
+ for (j = minlun;j < maxlun; j++) {
+ u_int scbid;
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ scbid = ahc_index_busy_tcl(ahc, tcl);
+ scbp = ahc_lookup_scb(ahc, scbid);
+ if (scbp == NULL
+ || ahc_match_scb(ahc, scbp, target, channel,
+ lun, tag, role) == 0)
+ continue;
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+ }
+ }
+
+ /*
+ * Go through the disconnected list and remove any entries we
+ * have queued for completion, 0'ing their control byte too.
+ * We save the active SCB and restore it ourselves, so there
+ * is no reason for this search to restore it too.
+ */
+ ahc_search_disc_list(ahc, target, channel, lun, tag,
+ /*stop_on_first*/FALSE, /*remove*/TRUE,
+ /*save_state*/FALSE);
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list. In some cases, these remnants
+ * might not still have mappings in the scbindex array (e.g. unexpected
+ * bus free with the same scb queued for an abort). Don't hold this
+ * against them.
+ */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ u_int scbid;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scbp = ahc_lookup_scb(ahc, scbid);
+ if ((scbp == NULL && scbid != SCB_LIST_NULL)
+ || (scbp != NULL
+ && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
+ ahc_add_curscb_to_free_list(ahc);
+ }
+
+ /*
+ * Go through the pending CCB list and look for
+ * commands for this target that are still active.
+ * These are other tagged commands that were
+ * disconnected when the reset occurred.
+ */
+ scbp_next = LIST_FIRST(&ahc->pending_scbs);
+ while (scbp_next != NULL) {
+ scbp = scbp_next;
+ scbp_next = LIST_NEXT(scbp, pending_links);
+ if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+ cam_status ostat;
+
+ ostat = ahc_get_transaction_status(scbp);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scbp, status);
+ if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+ ahc_freeze_scb(scbp);
+ if ((scbp->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB on pending list\n");
+ ahc_done(ahc, scbp);
+ found++;
+ }
+ }
+ ahc_outb(ahc, SCBPTR, active_scb);
+ ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+ ahc_release_untagged_queues(ahc);
+ return found;
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+ uint8_t scsiseq;
+
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+ ahc_flush_device_writes(ahc);
+ ahc_delay(AHC_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+ ahc_clear_intstat(ahc);
+
+ /* Re-enable reset interrupts */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+ struct ahc_devinfo devinfo;
+ u_int initiator, target, max_scsiid;
+ u_int sblkctl;
+ u_int scsiseq;
+ u_int simode1;
+ int found;
+ int restart_needed;
+ char cur_channel;
+
+ ahc->pending_device = NULL;
+
+ ahc_compile_devinfo(&devinfo,
+ CAM_TARGET_WILDCARD,
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahc_pause(ahc);
+
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
+ /*
+ * Run our command complete fifos to ensure that we perform
+ * completion processing on any commands that 'completed'
+ * before the reset occurred.
+ */
+ ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+ /*
+ * XXX - In Twin mode, the tqinfifo may have commands
+ * for an unaffected channel in it. However, if
+ * we have run out of ATIO resources to drain that
+ * queue, we may not get them all out here. Further,
+ * the blocked transactions for the reset channel
+ * should just be killed off, irrespecitve of whether
+ * we are blocked on ATIO resources. Write a routine
+ * to compact the tqinfifo appropriately.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+ }
+#endif
+
+ /*
+ * Reset the bus if we are initiating this reset
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = 'A';
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((sblkctl & SELBUSB) != 0))
+ cur_channel = 'B';
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ if (cur_channel != channel) {
+ /* Case 1: Command for another bus is active
+ * Stealthily reset the other bus without
+ * upsetting the current bus.
+ */
+ ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+ simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#ifdef AHC_TARGET_MODE
+ /*
+ * Bus resets clear ENSELI, so we cannot
+ * defer re-enabling bus reset interrupts
+ * if we are in target mode.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ simode1 |= ENSCSIRST;
+#endif
+ ahc_outb(ahc, SIMODE1, simode1);
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+ ahc_outb(ahc, SBLKCTL, sblkctl);
+ restart_needed = FALSE;
+ } else {
+ /* Case 2: A command from this bus is active or we're idle */
+ simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#ifdef AHC_TARGET_MODE
+ /*
+ * Bus resets clear ENSELI, so we cannot
+ * defer re-enabling bus reset interrupts
+ * if we are in target mode.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ simode1 |= ENSCSIRST;
+#endif
+ ahc_outb(ahc, SIMODE1, simode1);
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+ restart_needed = TRUE;
+ }
+
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+ max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
+#ifdef AHC_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target more peripheral
+ * drivers affected by this action.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+ struct ahc_tmode_tstate* tstate;
+ u_int lun;
+
+ tstate = ahc->enabled_targets[target];
+ if (tstate == NULL)
+ continue;
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+ EVENT_TYPE_BUS_RESET, /*arg*/0);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+#endif
+ /* Notify the XPT that a bus reset occurred */
+ ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+
+ if (ahc->enabled_targets[target] == NULL)
+ continue;
+ for (initiator = 0; initiator <= max_scsiid; initiator++) {
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo, target, initiator,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+ ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHC_TRANS_CUR,
+ /*paused*/TRUE);
+ }
+ }
+
+ if (restart_needed)
+ ahc_restart(ahc);
+ else
+ ahc_unpause(ahc);
+ return found;
+}
+
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ struct status_pkt *spkt;
+ uint32_t sgptr;
+ uint32_t resid_sgptr;
+ uint32_t resid;
+
+ /*
+ * 5 cases.
+ * 1) No residual.
+ * SG_RESID_VALID clear in sgptr.
+ * 2) Transferless command
+ * 3) Never performed any transfers.
+ * sgptr has SG_FULL_RESID set.
+ * 4) No residual but target did not
+ * save data pointers after the
+ * last transfer, so sgptr was
+ * never updated.
+ * 5) We have a partial residual.
+ * Use residual_sgptr to determine
+ * where we are.
+ */
+
+ hscb = scb->hscb;
+ sgptr = ahc_le32toh(hscb->sgptr);
+ if ((sgptr & SG_RESID_VALID) == 0)
+ /* Case 1 */
+ return;
+ sgptr &= ~SG_RESID_VALID;
+
+ if ((sgptr & SG_LIST_NULL) != 0)
+ /* Case 2 */
+ return;
+
+ spkt = &hscb->shared_data.status;
+ resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+ if ((sgptr & SG_FULL_RESID) != 0) {
+ /* Case 3 */
+ resid = ahc_get_transfer_length(scb);
+ } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+ /* Case 4 */
+ return;
+ } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+ panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+ } else {
+ struct ahc_dma_seg *sg;
+
+ /*
+ * Remainder of the SG where the transfer
+ * stopped.
+ */
+ resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+ sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+ sg++;
+ resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+ }
+ }
+ if ((scb->flags & SCB_SENSE) == 0)
+ ahc_set_residual(scb, resid);
+ else
+ ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MISC) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Handled %sResidual of %d bytes\n",
+ (scb->flags & SCB_SENSE) ? "Sense " : "", resid);
+ }
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
+ u_int initiator_id, u_int event_type, u_int event_arg)
+{
+ struct ahc_tmode_event *event;
+ int pending;
+
+ xpt_freeze_devq(lstate->path, /*count*/1);
+ if (lstate->event_w_idx >= lstate->event_r_idx)
+ pending = lstate->event_w_idx - lstate->event_r_idx;
+ else
+ pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+ - (lstate->event_r_idx - lstate->event_w_idx);
+
+ if (event_type == EVENT_TYPE_BUS_RESET
+ || event_type == MSG_BUS_DEV_RESET) {
+ /*
+ * Any earlier events are irrelevant, so reset our buffer.
+ * This has the effect of allowing us to deal with reset
+ * floods (an external device holding down the reset line)
+ * without losing the event that is really interesting.
+ */
+ lstate->event_r_idx = 0;
+ lstate->event_w_idx = 0;
+ xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+ }
+
+ if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+ xpt_print_path(lstate->path);
+ printf("immediate event %x:%x lost\n",
+ lstate->event_buffer[lstate->event_r_idx].event_type,
+ lstate->event_buffer[lstate->event_r_idx].event_arg);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+ }
+
+ event = &lstate->event_buffer[lstate->event_w_idx];
+ event->initiator_id = initiator_id;
+ event->event_type = event_type;
+ event->event_arg = event_arg;
+ lstate->event_w_idx++;
+ if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
+{
+ struct ccb_hdr *ccbh;
+ struct ccb_immed_notify *inot;
+
+ while (lstate->event_r_idx != lstate->event_w_idx
+ && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+ struct ahc_tmode_event *event;
+
+ event = &lstate->event_buffer[lstate->event_r_idx];
+ SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+ inot = (struct ccb_immed_notify *)ccbh;
+ switch (event->event_type) {
+ case EVENT_TYPE_BUS_RESET:
+ ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+ break;
+ default:
+ ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+ inot->message_args[0] = event->event_type;
+ inot->message_args[1] = event->event_arg;
+ break;
+ }
+ inot->initiator_id = event->initiator_id;
+ inot->sense_len = 0;
+ xpt_done((union ccb *)inot);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ }
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
+{
+ int i;
+
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+ for (i = 0; i < ahc->instruction_ram_size; i++) {
+ uint8_t ins_bytes[4];
+
+ ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+ printf("0x%08x\n", ins_bytes[0] << 24
+ | ins_bytes[1] << 16
+ | ins_bytes[2] << 8
+ | ins_bytes[3]);
+ }
+}
+#endif
+
+static int
+ahc_loadseq(struct ahc_softc *ahc)
+{
+ struct cs cs_table[num_critical_sections];
+ u_int begin_set[num_critical_sections];
+ u_int end_set[num_critical_sections];
+ struct patch *cur_patch;
+ u_int cs_count;
+ u_int cur_cs;
+ u_int i;
+ u_int skip_addr;
+ u_int sg_prefetch_cnt;
+ int downloaded;
+ uint8_t download_consts[7];
+
+ /*
+ * Start out with 0 critical sections
+ * that apply to this firmware load.
+ */
+ cs_count = 0;
+ cur_cs = 0;
+ memset(begin_set, 0, sizeof(begin_set));
+ memset(end_set, 0, sizeof(end_set));
+
+ /* Setup downloadable constant table */
+ download_consts[QOUTFIFO_OFFSET] = 0;
+ if (ahc->targetcmds != NULL)
+ download_consts[QOUTFIFO_OFFSET] += 32;
+ download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+ download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+ download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+ sg_prefetch_cnt = ahc->pci_cachesize;
+ if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+ sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+ download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+ download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+ download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+
+ cur_patch = patches;
+ downloaded = 0;
+ skip_addr = 0;
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+
+ for (i = 0; i < sizeof(seqprog)/4; i++) {
+ if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+ /*
+ * Don't download this instruction as it
+ * is in a patch that was removed.
+ */
+ continue;
+ }
+
+ if (downloaded == ahc->instruction_ram_size) {
+ /*
+ * We're about to exceed the instruction
+ * storage capacity for this chip. Fail
+ * the load.
+ */
+ printf("\n%s: Program too large for instruction memory "
+ "size of %d!\n", ahc_name(ahc),
+ ahc->instruction_ram_size);
+ return (ENOMEM);
+ }
+
+ /*
+ * Move through the CS table until we find a CS
+ * that might apply to this instruction.
+ */
+ for (; cur_cs < num_critical_sections; cur_cs++) {
+ if (critical_sections[cur_cs].end <= i) {
+ if (begin_set[cs_count] == TRUE
+ && end_set[cs_count] == FALSE) {
+ cs_table[cs_count].end = downloaded;
+ end_set[cs_count] = TRUE;
+ cs_count++;
+ }
+ continue;
+ }
+ if (critical_sections[cur_cs].begin <= i
+ && begin_set[cs_count] == FALSE) {
+ cs_table[cs_count].begin = downloaded;
+ begin_set[cs_count] = TRUE;
+ }
+ break;
+ }
+ ahc_download_instr(ahc, i, download_consts);
+ downloaded++;
+ }
+
+ ahc->num_critical_sections = cs_count;
+ if (cs_count != 0) {
+
+ cs_count *= sizeof(struct cs);
+ ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+ if (ahc->critical_sections == NULL)
+ panic("ahc_loadseq: Could not malloc");
+ memcpy(ahc->critical_sections, cs_table, cs_count);
+ }
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+
+ if (bootverbose) {
+ printf(" %d instructions downloaded\n", downloaded);
+ printf("%s: Features 0x%x, Bugs 0x%x, Flags 0x%x\n",
+ ahc_name(ahc), ahc->features, ahc->bugs, ahc->flags);
+ }
+ return (0);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr)
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ u_int num_patches;
+
+ num_patches = sizeof(patches)/sizeof(struct patch);
+ last_patch = &patches[num_patches];
+ cur_patch = *start_patch;
+
+ while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+ if (cur_patch->patch_func(ahc) == 0) {
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ u_int opcode;
+
+ /*
+ * The firmware is always compiled into a little endian format.
+ */
+ instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode) {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ struct patch *cur_patch;
+ int address_offset;
+ u_int address;
+ u_int skip_addr;
+ u_int i;
+
+ fmt3_ins = &instr.format3;
+ address_offset = 0;
+ address = fmt3_ins->address;
+ cur_patch = patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;) {
+
+ ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+ if (skip_addr > i) {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ } else {
+ i++;
+ }
+ }
+ address -= address_offset;
+ fmt3_ins->address = address;
+ /* FALLTHROUGH */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0) {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ if ((ahc->features & AHC_CMD_CHAN) == 0
+ && opcode == AIC_OP_BMOV) {
+ /*
+ * Block move was added at the same time
+ * as the command channel. Verify that
+ * this is only a move of a single element
+ * and convert the BMOV to a MOV
+ * (AND with an immediate of FF).
+ */
+ if (fmt1_ins->immediate != 1)
+ panic("%s: BMOV not supported\n",
+ ahc_name(ahc));
+ fmt1_ins->opcode = AIC_OP_AND;
+ fmt1_ins->immediate = 0xff;
+ }
+ /* FALLTHROUGH */
+ case AIC_OP_ROL:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for (i = 0, count = 0; i < 31; i++) {
+ uint32_t mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if ((count & 0x01) == 0)
+ instr.format1.parity = 1;
+ } else {
+ /* Compress the instruction for older sequencers */
+ if (fmt3_ins != NULL) {
+ instr.integer =
+ fmt3_ins->immediate
+ | (fmt3_ins->source << 8)
+ | (fmt3_ins->address << 16)
+ | (fmt3_ins->opcode << 25);
+ } else {
+ instr.integer =
+ fmt1_ins->immediate
+ | (fmt1_ins->source << 8)
+ | (fmt1_ins->destination << 16)
+ | (fmt1_ins->ret << 24)
+ | (fmt1_ins->opcode << 25);
+ }
+ }
+ /* The sequencer is a little endian cpu */
+ instr.integer = ahc_htole32(instr.integer);
+ ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+ break;
+ default:
+ panic("Unknown opcode encountered in seq program");
+ break;
+ }
+}
+
+int
+ahc_print_register(ahc_reg_parse_entry_t *table, u_int num_entries,
+ const char *name, u_int address, u_int value,
+ u_int *cur_column, u_int wrap_point)
+{
+ int printed;
+ u_int printed_mask;
+
+ if (cur_column != NULL && *cur_column >= wrap_point) {
+ printf("\n");
+ *cur_column = 0;
+ }
+ printed = printf("%s[0x%x]", name, value);
+ if (table == NULL) {
+ printed += printf(" ");
+ *cur_column += printed;
+ return (printed);
+ }
+ printed_mask = 0;
+ while (printed_mask != 0xFF) {
+ int entry;
+
+ for (entry = 0; entry < num_entries; entry++) {
+ if (((value & table[entry].mask)
+ != table[entry].value)
+ || ((printed_mask & table[entry].mask)
+ == table[entry].mask))
+ continue;
+
+ printed += printf("%s%s",
+ printed_mask == 0 ? ":(" : "|",
+ table[entry].name);
+ printed_mask |= table[entry].mask;
+
+ break;
+ }
+ if (entry >= num_entries)
+ break;
+ }
+ if (printed_mask != 0)
+ printed += printf(") ");
+ else
+ printed += printf(" ");
+ if (cur_column != NULL)
+ *cur_column += printed;
+ return (printed);
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ struct scb_tailq *untagged_q;
+ u_int cur_col;
+ int paused;
+ int target;
+ int maxtarget;
+ int i;
+ uint8_t last_phase;
+ uint8_t qinpos;
+ uint8_t qintail;
+ uint8_t qoutpos;
+ uint8_t scb_index;
+ uint8_t saved_scbptr;
+
+ if (ahc_is_paused(ahc)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahc_pause(ahc);
+ }
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ last_phase = ahc_inb(ahc, LASTPHASE);
+ printf(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
+ "%s: Dumping Card State %s, at SEQADDR 0x%x\n",
+ ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+ if (paused)
+ printf("Card was paused\n");
+ printf("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
+ ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
+ ahc_inb(ahc, ARG_2));
+ printf("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
+ ahc_inb(ahc, SCBPTR));
+ cur_col = 0;
+ if ((ahc->features & AHC_DT) != 0)
+ ahc_scsiphase_print(ahc_inb(ahc, SCSIPHASE), &cur_col, 50);
+ ahc_scsisigi_print(ahc_inb(ahc, SCSISIGI), &cur_col, 50);
+ ahc_error_print(ahc_inb(ahc, ERROR), &cur_col, 50);
+ ahc_scsibusl_print(ahc_inb(ahc, SCSIBUSL), &cur_col, 50);
+ ahc_lastphase_print(ahc_inb(ahc, LASTPHASE), &cur_col, 50);
+ ahc_scsiseq_print(ahc_inb(ahc, SCSISEQ), &cur_col, 50);
+ ahc_sblkctl_print(ahc_inb(ahc, SBLKCTL), &cur_col, 50);
+ ahc_scsirate_print(ahc_inb(ahc, SCSIRATE), &cur_col, 50);
+ ahc_seqctl_print(ahc_inb(ahc, SEQCTL), &cur_col, 50);
+ ahc_seq_flags_print(ahc_inb(ahc, SEQ_FLAGS), &cur_col, 50);
+ ahc_sstat0_print(ahc_inb(ahc, SSTAT0), &cur_col, 50);
+ ahc_sstat1_print(ahc_inb(ahc, SSTAT1), &cur_col, 50);
+ ahc_sstat2_print(ahc_inb(ahc, SSTAT2), &cur_col, 50);
+ ahc_sstat3_print(ahc_inb(ahc, SSTAT3), &cur_col, 50);
+ ahc_simode0_print(ahc_inb(ahc, SIMODE0), &cur_col, 50);
+ ahc_simode1_print(ahc_inb(ahc, SIMODE1), &cur_col, 50);
+ ahc_sxfrctl0_print(ahc_inb(ahc, SXFRCTL0), &cur_col, 50);
+ ahc_dfcntrl_print(ahc_inb(ahc, DFCNTRL), &cur_col, 50);
+ ahc_dfstatus_print(ahc_inb(ahc, DFSTATUS), &cur_col, 50);
+ if (cur_col != 0)
+ printf("\n");
+ printf("STACK:");
+ for (i = 0; i < STACK_SIZE; i++)
+ printf(" 0x%x", ahc_inb(ahc, STACK)|(ahc_inb(ahc, STACK) << 8));
+ printf("\nSCB count = %d\n", ahc->scb_data->numscbs);
+ printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+ printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+ /* QINFIFO */
+ printf("QINFIFO entries: ");
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ qinpos = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinpos);
+ } else
+ qinpos = ahc_inb(ahc, QINPOS);
+ qintail = ahc->qinfifonext;
+ while (qinpos != qintail) {
+ printf("%d ", ahc->qinfifo[qinpos]);
+ qinpos++;
+ }
+ printf("\n");
+
+ printf("Waiting Queue entries: ");
+ scb_index = ahc_inb(ahc, WAITING_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ printf("Disconnected Queue entries: ");
+ scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+ printf("QOUTFIFO entries: ");
+ qoutpos = ahc->qoutfifonext;
+ i = 0;
+ while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+ printf("%d ", ahc->qoutfifo[qoutpos]);
+ qoutpos++;
+ }
+ printf("\n");
+
+ printf("Sequencer Free SCB List: ");
+ scb_index = ahc_inb(ahc, FREE_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ printf("Sequencer SCB Info: ");
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ ahc_outb(ahc, SCBPTR, i);
+ cur_col = printf("\n%3d ", i);
+
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL), &cur_col, 60);
+ ahc_scb_scsiid_print(ahc_inb(ahc, SCB_SCSIID), &cur_col, 60);
+ ahc_scb_lun_print(ahc_inb(ahc, SCB_LUN), &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ }
+ printf("\n");
+
+ printf("Pending list: ");
+ i = 0;
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ if (i++ > 256)
+ break;
+ cur_col = printf("\n%3d ", scb->hscb->tag);
+ ahc_scb_control_print(scb->hscb->control, &cur_col, 60);
+ ahc_scb_scsiid_print(scb->hscb->scsiid, &cur_col, 60);
+ ahc_scb_lun_print(scb->hscb->lun, &cur_col, 60);
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+ printf("(");
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL),
+ &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ printf(")");
+ }
+ }
+ printf("\n");
+
+ printf("Kernel Free SCB list: ");
+ i = 0;
+ SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+ if (i++ > 256)
+ break;
+ printf("%d ", scb->hscb->tag);
+ }
+ printf("\n");
+
+ maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+ for (target = 0; target <= maxtarget; target++) {
+ untagged_q = &ahc->untagged_queues[target];
+ if (TAILQ_FIRST(untagged_q) == NULL)
+ continue;
+ printf("Untagged Q(%d): ", target);
+ i = 0;
+ TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+ if (i++ > 256)
+ break;
+ printf("%d ", scb->hscb->tag);
+ }
+ printf("\n");
+ }
+
+ ahc_platform_dump_card_state(ahc);
+ printf("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n");
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ if (paused == 0)
+ ahc_unpause(ahc);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+ struct ahc_tmode_tstate **tstate,
+ struct ahc_tmode_lstate **lstate,
+ int notfound_failure)
+{
+
+ if ((ahc->features & AHC_TARGETMODE) == 0)
+ return (CAM_REQ_INVALID);
+
+ /*
+ * Handle the 'black hole' device that sucks up
+ * requests to unattached luns on enabled targets.
+ */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+ && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *tstate = NULL;
+ *lstate = ahc->black_hole;
+ } else {
+ u_int max_id;
+
+ max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
+ if (ccb->ccb_h.target_id > max_id)
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate =
+ (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ }
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ u_long s;
+ u_int target;
+ u_int lun;
+ u_int target_mask;
+ u_int our_id;
+ int error;
+ char channel;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+ /*notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ if (cam_sim_bus(sim) == 0)
+ our_id = ahc->our_id;
+ else
+ our_id = ahc->our_id_b;
+
+ if (ccb->ccb_h.target_id != our_id) {
+ /*
+ * our_id represents our initiator ID, or
+ * the ID of the first target to have an
+ * enabled lun in target mode. There are
+ * two cases that may preclude enabling a
+ * target id other than our_id.
+ *
+ * o our_id is for an active initiator role.
+ * Since the hardware does not support
+ * reselections to the initiator role at
+ * anything other than our_id, and our_id
+ * is used by the hardware to indicate the
+ * ID to use for both select-out and
+ * reselect-out operations, the only target
+ * ID we can support in this mode is our_id.
+ *
+ * o The MULTARGID feature is not available and
+ * a previous target mode ID has been enabled.
+ */
+ if ((ahc->features & AHC_MULTIROLE) != 0) {
+
+ if ((ahc->features & AHC_MULTI_TID) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0) {
+ /*
+ * Only allow additional targets if
+ * the initiator role is disabled.
+ * The hardware cannot handle a re-select-in
+ * on the initiator id during a re-select-out
+ * on a different target id.
+ */
+ status = CAM_TID_INVALID;
+ } else if ((ahc->flags & AHC_INITIATORROLE) != 0
+ || ahc->enabled_luns > 0) {
+ /*
+ * Only allow our target id to change
+ * if the initiator role is not configured
+ * and there are no enabled luns which
+ * are attached to the currently registered
+ * scsi id.
+ */
+ status = CAM_TID_INVALID;
+ }
+ } else if ((ahc->features & AHC_MULTI_TID) == 0
+ && ahc->enabled_luns > 0) {
+
+ status = CAM_TID_INVALID;
+ }
+ }
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ /*
+ * We now have an id that is valid.
+ * If we aren't in target mode, switch modes.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) == 0
+ && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ u_long s;
+ ahc_flag saved_flags;
+
+ printf("Configuring Target Mode\n");
+ ahc_lock(ahc, &s);
+ if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+ ccb->ccb_h.status = CAM_BUSY;
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ saved_flags = ahc->flags;
+ ahc->flags |= AHC_TARGETROLE;
+ if ((ahc->features & AHC_MULTIROLE) == 0)
+ ahc->flags &= ~AHC_INITIATORROLE;
+ ahc_pause(ahc);
+ error = ahc_loadseq(ahc);
+ if (error != 0) {
+ /*
+ * Restore original configuration and notify
+ * the caller that we cannot support target mode.
+ * Since the adapter started out in this
+ * configuration, the firmware load will succeed,
+ * so there is no point in checking ahc_loadseq's
+ * return value.
+ */
+ ahc->flags = saved_flags;
+ (void)ahc_loadseq(ahc);
+ ahc_restart(ahc);
+ ahc_unlock(ahc, &s);
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ return;
+ }
+ ahc_restart(ahc);
+ ahc_unlock(ahc, &s);
+ }
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ channel = SIM_CHANNEL(ahc, sim);
+ target_mask = 0x01 << target;
+ if (channel == 'B')
+ target_mask <<= 8;
+
+ if (cel->enable != 0) {
+ u_int scsiseq;
+
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun already enabled\n");
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ printf("Non-zero Group Codes\n");
+ return;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+ tstate = ahc_alloc_tstate(ahc, target, channel);
+ if (tstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate tstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate lstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ memset(lstate, 0, sizeof(*lstate));
+ status = xpt_create_path(&lstate->path, /*periph*/NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (status != CAM_REQ_CMP) {
+ free(lstate, M_DEVBUF);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate path\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ ahc_lock(ahc, &s);
+ ahc_pause(ahc);
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = lstate;
+ ahc->enabled_luns++;
+
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ u_int targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1) << 8);
+
+ targid_mask |= target_mask;
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+
+ ahc_update_scsiid(ahc, targid_mask);
+ } else {
+ u_int our_id;
+ char channel;
+
+ channel = SIM_CHANNEL(ahc, sim);
+ our_id = SIM_SCSI_ID(ahc, sim);
+
+ /*
+ * This can only happen if selections
+ * are not enabled
+ */
+ if (target != our_id) {
+ u_int sblkctl;
+ char cur_channel;
+ int swap;
+
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = (sblkctl & SELBUSB)
+ ? 'B' : 'A';
+ if ((ahc->features & AHC_TWIN) == 0)
+ cur_channel = 'A';
+ swap = cur_channel != channel;
+ if (channel == 'A')
+ ahc->our_id = target;
+ else
+ ahc->our_id_b = target;
+
+ if (swap)
+ ahc_outb(ahc, SBLKCTL,
+ sblkctl ^ SELBUSB);
+
+ ahc_outb(ahc, SCSIID, target);
+
+ if (swap)
+ ahc_outb(ahc, SBLKCTL, sblkctl);
+ }
+ }
+ } else
+ ahc->black_hole = lstate;
+ /* Allow select-in operations */
+ if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+ }
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ } else {
+ struct scb *scb;
+ int i, empty;
+
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+
+ ahc_lock(ahc, &s);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ struct ccb_hdr *ccbh;
+
+ ccbh = &scb->io_ctx->ccb_h;
+ if (ccbh->func_code == XPT_CONT_TARGET_IO
+ && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+ printf("CTIO pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ }
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+
+ ahc_pause(ahc);
+ /* Can we clean up the target too? */
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = NULL;
+ ahc->enabled_luns--;
+ for (empty = 1, i = 0; i < 8; i++)
+ if (tstate->enabled_luns[i] != NULL) {
+ empty = 0;
+ break;
+ }
+
+ if (empty) {
+ ahc_free_tstate(ahc, target, channel,
+ /*force*/FALSE);
+ if (ahc->features & AHC_MULTI_TID) {
+ u_int targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1)
+ << 8);
+
+ targid_mask &= ~target_mask;
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1,
+ (targid_mask >> 8));
+ ahc_update_scsiid(ahc, targid_mask);
+ }
+ }
+ } else {
+
+ ahc->black_hole = NULL;
+
+ /*
+ * We can't allow selections without
+ * our black hole device.
+ */
+ empty = TRUE;
+ }
+ if (ahc->enabled_luns == 0) {
+ /* Disallow select-in */
+ u_int scsiseq;
+
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+
+ if ((ahc->features & AHC_MULTIROLE) == 0) {
+ printf("Configuring Initiator Mode\n");
+ ahc->flags &= ~AHC_TARGETROLE;
+ ahc->flags |= AHC_INITIATORROLE;
+ /*
+ * Returning to a configuration that
+ * fit previously will always succeed.
+ */
+ (void)ahc_loadseq(ahc);
+ ahc_restart(ahc);
+ /*
+ * Unpaused. The extra unpause
+ * that follows is harmless.
+ */
+ }
+ }
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ }
+}
+
+static void
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
+{
+ u_int scsiid_mask;
+ u_int scsiid;
+
+ if ((ahc->features & AHC_MULTI_TID) == 0)
+ panic("ahc_update_scsiid called on non-multitid unit\n");
+
+ /*
+ * Since we will rely on the TARGID mask
+ * for selection enables, ensure that OID
+ * in SCSIID is not set to some other ID
+ * that we don't want to allow selections on.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+ else
+ scsiid = ahc_inb(ahc, SCSIID);
+ scsiid_mask = 0x1 << (scsiid & OID);
+ if ((targid_mask & scsiid_mask) == 0) {
+ u_int our_id;
+
+ /* ffs counts from 1 */
+ our_id = ffs(targid_mask);
+ if (our_id == 0)
+ our_id = ahc->our_id;
+ else
+ our_id--;
+ scsiid &= TID;
+ scsiid |= our_id;
+ }
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+ else
+ ahc_outb(ahc, SCSIID, scsiid);
+}
+
+void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+ struct target_cmd *cmd;
+
+ /*
+ * If the card supports auto-access pause,
+ * we can access the card directly regardless
+ * of whether it is paused or not.
+ */
+ if ((ahc->features & AHC_AUTOPAUSE) != 0)
+ paused = TRUE;
+
+ ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
+ while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
+
+ /*
+ * Only advance through the queue if we
+ * have the resources to process the command.
+ */
+ if (ahc_handle_target_cmd(ahc, cmd) != 0)
+ break;
+
+ cmd->cmd_valid = 0;
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
+ sizeof(struct target_cmd),
+ BUS_DMASYNC_PREREAD);
+ ahc->tqinfifonext++;
+
+ /*
+ * Lazily update our position in the target mode incoming
+ * command queue as seen by the sequencer.
+ */
+ if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+ if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+ u_int hs_mailbox;
+
+ hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+ hs_mailbox &= ~HOST_TQINPOS;
+ hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+ ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+ } else {
+ if (!paused)
+ ahc_pause(ahc);
+ ahc_outb(ahc, KERNEL_TQINPOS,
+ ahc->tqinfifonext & HOST_TQINPOS);
+ if (!paused)
+ ahc_unpause(ahc);
+ }
+ }
+ }
+}
+
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
+{
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_tmode_lstate *lstate;
+ struct ccb_accept_tio *atio;
+ uint8_t *byte;
+ int initiator;
+ int target;
+ int lun;
+
+ initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+ target = SCSIID_OUR_ID(cmd->scsiid);
+ lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+ byte = cmd->bytes;
+ tstate = ahc->enabled_targets[target];
+ lstate = NULL;
+ if (tstate != NULL)
+ lstate = tstate->enabled_luns[lun];
+
+ /*
+ * Commands for disabled luns go to the black hole driver.
+ */
+ if (lstate == NULL)
+ lstate = ahc->black_hole;
+
+ atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ if (atio == NULL) {
+ ahc->flags |= AHC_TQINFIFO_BLOCKED;
+ /*
+ * Wait for more ATIOs from the peripheral driver for this lun.
+ */
+ if (bootverbose)
+ printf("%s: ATIOs exhausted\n", ahc_name(ahc));
+ return (1);
+ } else
+ ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+ printf("Incoming command from %d for %d:%d%s\n",
+ initiator, target, lun,
+ lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+ if (lstate == ahc->black_hole) {
+ /* Fill in the wildcards */
+ atio->ccb_h.target_id = target;
+ atio->ccb_h.target_lun = lun;
+ }
+
+ /*
+ * Package it up and send it off to
+ * whomever has this lun enabled.
+ */
+ atio->sense_len = 0;
+ atio->init_id = initiator;
+ if (byte[0] != 0xFF) {
+ /* Tag was included */
+ atio->tag_action = *byte++;
+ atio->tag_id = *byte++;
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ } else {
+ atio->ccb_h.flags = 0;
+ }
+ byte++;
+
+ /* Okay. Now determine the cdb size based on the command code */
+ switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+
+ memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+ /*
+ * We weren't allowed to disconnect.
+ * We're hanging on the bus until a
+ * continue target I/O comes in response
+ * to this accept tio.
+ */
+#if 0
+ printf("Received Immediate Command %d:%d:%d - %p\n",
+ initiator, target, lun, ahc->pending_device);
+#endif
+ ahc->pending_device = lstate;
+ ahc_freeze_ccb((union ccb *)atio);
+ atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+ }
+ xpt_done((union ccb*)atio);
+ return (0);
+}
+
+#endif
diff --git a/drivers/scsi/aic7xxx/aic7xxx_inline.h b/drivers/scsi/aic7xxx/aic7xxx_inline.h
new file mode 100644
index 000000000000..2cc8a17ed8b4
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_inline.h
@@ -0,0 +1,649 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_inline.h#43 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC7XXX_INLINE_H_
+#define _AIC7XXX_INLINE_H_
+
+/************************* Sequencer Execution Control ************************/
+static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc);
+static __inline int ahc_is_paused(struct ahc_softc *ahc);
+static __inline void ahc_pause(struct ahc_softc *ahc);
+static __inline void ahc_unpause(struct ahc_softc *ahc);
+
+/*
+ * Work around any chip bugs related to halting sequencer execution.
+ * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
+ * reading a register that will set this signal and deassert it.
+ * Without this workaround, if the chip is paused, by an interrupt or
+ * manual pause while accessing scb ram, accesses to certain registers
+ * will hang the system (infinite pci retries).
+ */
+static __inline void
+ahc_pause_bug_fix(struct ahc_softc *ahc)
+{
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ (void)ahc_inb(ahc, CCSCBCTL);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+ahc_is_paused(struct ahc_softc *ahc)
+{
+ return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop. The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register. The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+ahc_pause(struct ahc_softc *ahc)
+{
+ ahc_outb(ahc, HCNTRL, ahc->pause);
+
+ /*
+ * Since the sequencer can disable pausing in a critical section, we
+ * must loop until it actually stops.
+ */
+ while (ahc_is_paused(ahc) == 0)
+ ;
+
+ ahc_pause_bug_fix(ahc);
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted. If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+ahc_unpause(struct ahc_softc *ahc)
+{
+ if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static __inline void ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahc_freeze_untagged_queues(struct ahc_softc *ahc)
+{
+ if ((ahc->flags & AHC_SCB_BTT) == 0)
+ ahc->untagged_queue_lock++;
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed. We use a counting semaphore to allow the lock
+ * to be acquired recursively. Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahc_release_untagged_queues(struct ahc_softc *ahc)
+{
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+ ahc->untagged_queue_lock--;
+ if (ahc->untagged_queue_lock == 0)
+ ahc_run_untagged_queues(ahc);
+ }
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline struct ahc_dma_seg *
+ ahc_sg_bus_to_virt(struct scb *scb,
+ uint32_t sg_busaddr);
+static __inline uint32_t
+ ahc_sg_virt_to_bus(struct scb *scb,
+ struct ahc_dma_seg *sg);
+static __inline uint32_t
+ ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
+static __inline void ahc_sync_scb(struct ahc_softc *ahc,
+ struct scb *scb, int op);
+static __inline void ahc_sync_sglist(struct ahc_softc *ahc,
+ struct scb *scb, int op);
+static __inline uint32_t
+ ahc_targetcmd_offset(struct ahc_softc *ahc,
+ u_int index);
+
+static __inline struct ahc_dma_seg *
+ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
+{
+ int sg_index;
+
+ sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_index++;
+
+ return (&scb->sg_list[sg_index]);
+}
+
+static __inline uint32_t
+ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
+{
+ int sg_index;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_index = sg - &scb->sg_list[1];
+
+ return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
+}
+
+static __inline uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+ return (ahc->scb_data->hscb_busaddr
+ + (sizeof(struct hardware_scb) * index));
+}
+
+static __inline void
+ahc_sync_scb(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+ ahc_dmamap_sync(ahc, ahc->scb_data->hscb_dmat,
+ ahc->scb_data->hscb_dmamap,
+ /*offset*/(scb->hscb - ahc->hscbs) * sizeof(*scb->hscb),
+ /*len*/sizeof(*scb->hscb), op);
+}
+
+static __inline void
+ahc_sync_sglist(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+ if (scb->sg_count == 0)
+ return;
+
+ ahc_dmamap_sync(ahc, ahc->scb_data->sg_dmat, scb->sg_map->sg_dmamap,
+ /*offset*/(scb->sg_list - scb->sg_map->sg_vaddr)
+ * sizeof(struct ahc_dma_seg),
+ /*len*/sizeof(struct ahc_dma_seg) * scb->sg_count, op);
+}
+
+static __inline uint32_t
+ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
+{
+ return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
+}
+
+/******************************** Debugging ***********************************/
+static __inline char *ahc_name(struct ahc_softc *ahc);
+
+static __inline char *
+ahc_name(struct ahc_softc *ahc)
+{
+ return (ahc->name);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+
+static __inline void ahc_update_residual(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline struct ahc_initiator_tinfo *
+ ahc_fetch_transinfo(struct ahc_softc *ahc,
+ char channel, u_int our_id,
+ u_int remote_id,
+ struct ahc_tmode_tstate **tstate);
+static __inline uint16_t
+ ahc_inw(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outw(struct ahc_softc *ahc, u_int port,
+ u_int value);
+static __inline uint32_t
+ ahc_inl(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outl(struct ahc_softc *ahc, u_int port,
+ uint32_t value);
+static __inline uint64_t
+ ahc_inq(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outq(struct ahc_softc *ahc, u_int port,
+ uint64_t value);
+static __inline struct scb*
+ ahc_get_scb(struct ahc_softc *ahc);
+static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void ahc_swap_with_next_hscb(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline struct scsi_sense_data *
+ ahc_get_sense_buf(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline uint32_t
+ ahc_get_sense_bufaddr(struct ahc_softc *ahc,
+ struct scb *scb);
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahc_le32toh(scb->hscb->sgptr);
+ if ((sgptr & SG_RESID_VALID) != 0)
+ ahc_calc_residual(ahc, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
+ u_int remote_id, struct ahc_tmode_tstate **tstate)
+{
+ /*
+ * Transfer data structures are stored from the perspective
+ * of the target role. Since the parameters for a connection
+ * in the initiator role to a given target are the same as
+ * when the roles are reversed, we pretend we are the target.
+ */
+ if (channel == 'B')
+ our_id += 8;
+ *tstate = ahc->enabled_targets[our_id];
+ return (&(*tstate)->transinfo[remote_id]);
+}
+
+static __inline uint16_t
+ahc_inw(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port+1) << 8) | ahc_inb(ahc, port));
+}
+
+static __inline void
+ahc_outw(struct ahc_softc *ahc, u_int port, u_int value)
+{
+ ahc_outb(ahc, port, value & 0xFF);
+ ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
+}
+
+static __inline uint32_t
+ahc_inl(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port))
+ | (ahc_inb(ahc, port+1) << 8)
+ | (ahc_inb(ahc, port+2) << 16)
+ | (ahc_inb(ahc, port+3) << 24));
+}
+
+static __inline void
+ahc_outl(struct ahc_softc *ahc, u_int port, uint32_t value)
+{
+ ahc_outb(ahc, port, (value) & 0xFF);
+ ahc_outb(ahc, port+1, ((value) >> 8) & 0xFF);
+ ahc_outb(ahc, port+2, ((value) >> 16) & 0xFF);
+ ahc_outb(ahc, port+3, ((value) >> 24) & 0xFF);
+}
+
+static __inline uint64_t
+ahc_inq(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port))
+ | (ahc_inb(ahc, port+1) << 8)
+ | (ahc_inb(ahc, port+2) << 16)
+ | (ahc_inb(ahc, port+3) << 24)
+ | (((uint64_t)ahc_inb(ahc, port+4)) << 32)
+ | (((uint64_t)ahc_inb(ahc, port+5)) << 40)
+ | (((uint64_t)ahc_inb(ahc, port+6)) << 48)
+ | (((uint64_t)ahc_inb(ahc, port+7)) << 56));
+}
+
+static __inline void
+ahc_outq(struct ahc_softc *ahc, u_int port, uint64_t value)
+{
+ ahc_outb(ahc, port, value & 0xFF);
+ ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
+ ahc_outb(ahc, port+2, (value >> 16) & 0xFF);
+ ahc_outb(ahc, port+3, (value >> 24) & 0xFF);
+ ahc_outb(ahc, port+4, (value >> 32) & 0xFF);
+ ahc_outb(ahc, port+5, (value >> 40) & 0xFF);
+ ahc_outb(ahc, port+6, (value >> 48) & 0xFF);
+ ahc_outb(ahc, port+7, (value >> 56) & 0xFF);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+
+ if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) {
+ ahc_alloc_scbs(ahc);
+ scb = SLIST_FIRST(&ahc->scb_data->free_scbs);
+ if (scb == NULL)
+ return (NULL);
+ }
+ SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+ return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+ /* Clean up for the next user */
+ ahc->scb_data->scbindex[hscb->tag] = NULL;
+ scb->flags = SCB_FREE;
+ hscb->control = 0;
+
+ SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);
+
+ /* Notify the OSM that a resource is now available. */
+ ahc_platform_scb_free(ahc, scb);
+}
+
+static __inline struct scb *
+ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
+{
+ struct scb* scb;
+
+ scb = ahc->scb_data->scbindex[tag];
+ if (scb != NULL)
+ ahc_sync_scb(ahc, scb,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ return (scb);
+}
+
+static __inline void
+ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *q_hscb;
+ u_int saved_tag;
+
+ /*
+ * Our queuing method is a bit tricky. The card
+ * knows in advance which HSCB to download, and we
+ * can't disappoint it. To achieve this, the next
+ * SCB to download is saved off in ahc->next_queued_scb.
+ * When we are called to queue "an arbitrary scb",
+ * we copy the contents of the incoming HSCB to the one
+ * the sequencer knows about, swap HSCB pointers and
+ * finally assign the SCB to the tag indexed location
+ * in the scb_array. This makes sure that we can still
+ * locate the correct SCB by SCB_TAG.
+ */
+ q_hscb = ahc->next_queued_scb->hscb;
+ saved_tag = q_hscb->tag;
+ memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+ if ((scb->flags & SCB_CDB32_PTR) != 0) {
+ q_hscb->shared_data.cdb_ptr =
+ ahc_htole32(ahc_hscb_busaddr(ahc, q_hscb->tag)
+ + offsetof(struct hardware_scb, cdb32));
+ }
+ q_hscb->tag = saved_tag;
+ q_hscb->next = scb->hscb->tag;
+
+ /* Now swap HSCB pointers. */
+ ahc->next_queued_scb->hscb = scb->hscb;
+ scb->hscb = q_hscb;
+
+ /* Now define the mapping from tag to SCB in the scbindex */
+ ahc->scb_data->scbindex[scb->hscb->tag] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc_swap_with_next_hscb(ahc, scb);
+
+ if (scb->hscb->tag == SCB_LIST_NULL
+ || scb->hscb->next == SCB_LIST_NULL)
+ panic("Attempt to queue invalid SCB tag %x:%x\n",
+ scb->hscb->tag, scb->hscb->next);
+
+ /*
+ * Setup data "oddness".
+ */
+ scb->hscb->lun &= LID;
+ if (ahc_get_transfer_length(scb) & 0x1)
+ scb->hscb->lun |= SCB_XFERLEN_ODD;
+
+ /*
+ * Keep a history of SCBs we've downloaded in the qinfifo.
+ */
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+ /*
+ * Make sure our data is consistent from the
+ * perspective of the adapter.
+ */
+ ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ /* Tell the adapter about the newly queued SCB */
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ if ((ahc->features & AHC_AUTOPAUSE) == 0)
+ ahc_pause(ahc);
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ if ((ahc->features & AHC_AUTOPAUSE) == 0)
+ ahc_unpause(ahc);
+ }
+}
+
+static __inline struct scsi_sense_data *
+ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
+{
+ int offset;
+
+ offset = scb - ahc->scb_data->scbarray;
+ return (&ahc->scb_data->sense[offset]);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
+{
+ int offset;
+
+ offset = scb - ahc->scb_data->scbarray;
+ return (ahc->scb_data->sense_busaddr
+ + (offset * sizeof(struct scsi_sense_data)));
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void ahc_sync_qoutfifo(struct ahc_softc *ahc, int op);
+static __inline void ahc_sync_tqinfifo(struct ahc_softc *ahc, int op);
+static __inline u_int ahc_check_cmdcmpltqueues(struct ahc_softc *ahc);
+static __inline int ahc_intr(struct ahc_softc *ahc);
+
+static __inline void
+ahc_sync_qoutfifo(struct ahc_softc *ahc, int op)
+{
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+ /*offset*/0, /*len*/256, op);
+}
+
+static __inline void
+ahc_sync_tqinfifo(struct ahc_softc *ahc, int op)
+{
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, 0),
+ sizeof(struct target_cmd) * AHC_TMODE_CMDS,
+ op);
+ }
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHC_RUN_QOUTFIFO 0x1
+#define AHC_RUN_TQINFIFO 0x2
+static __inline u_int
+ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
+{
+ u_int retval;
+
+ retval = 0;
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+ /*offset*/ahc->qoutfifonext, /*len*/1,
+ BUS_DMASYNC_POSTREAD);
+ if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
+ retval |= AHC_RUN_QOUTFIFO;
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETROLE) != 0
+ && (ahc->flags & AHC_TQINFIFO_BLOCKED) == 0) {
+ ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, ahc->tqinfifofnext),
+ /*len*/sizeof(struct target_cmd),
+ BUS_DMASYNC_POSTREAD);
+ if (ahc->targetcmds[ahc->tqinfifonext].cmd_valid != 0)
+ retval |= AHC_RUN_TQINFIFO;
+ }
+#endif
+ return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline int
+ahc_intr(struct ahc_softc *ahc)
+{
+ u_int intstat;
+
+ if ((ahc->pause & INTEN) == 0) {
+ /*
+ * Our interrupt is not enabled on the chip
+ * and may be disabled for re-entrancy reasons,
+ * so just return. This is likely just a shared
+ * interrupt.
+ */
+ return (0);
+ }
+ /*
+ * Instead of directly reading the interrupt status register,
+ * infer the cause of the interrupt by checking our in-core
+ * completion queues. This avoids a costly PCI bus read in
+ * most cases.
+ */
+ if ((ahc->flags & (AHC_ALL_INTERRUPTS|AHC_EDGE_INTERRUPT)) == 0
+ && (ahc_check_cmdcmpltqueues(ahc) != 0))
+ intstat = CMDCMPLT;
+ else {
+ intstat = ahc_inb(ahc, INTSTAT);
+ }
+
+ if ((intstat & INT_PEND) == 0) {
+#if AHC_PCI_CONFIG > 0
+ if (ahc->unsolicited_ints > 500) {
+ ahc->unsolicited_ints = 0;
+ if ((ahc->chip & AHC_PCI) != 0
+ && (ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
+ ahc->bus_intr(ahc);
+ }
+#endif
+ ahc->unsolicited_ints++;
+ return (0);
+ }
+ ahc->unsolicited_ints = 0;
+
+ if (intstat & CMDCMPLT) {
+ ahc_outb(ahc, CLRINT, CLRCMDINT);
+
+ /*
+ * Ensure that the chip sees that we've cleared
+ * this interrupt before we walk the output fifo.
+ * Otherwise, we may, due to posted bus writes,
+ * clear the interrupt after we finish the scan,
+ * and after the sequencer has added new entries
+ * and asserted the interrupt again.
+ */
+ ahc_flush_device_writes(ahc);
+ ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+#endif
+ }
+
+ /*
+ * Handle statuses that may invalidate our cached
+ * copy of INTSTAT separately.
+ */
+ if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0) {
+ /* Hot eject. Do nothing */
+ } else if (intstat & BRKADRINT) {
+ ahc_handle_brkadrint(ahc);
+ } else if ((intstat & (SEQINT|SCSIINT)) != 0) {
+
+ ahc_pause_bug_fix(ahc);
+
+ if ((intstat & SEQINT) != 0)
+ ahc_handle_seqint(ahc, intstat);
+
+ if ((intstat & SCSIINT) != 0)
+ ahc_handle_scsiint(ahc, intstat);
+ }
+ return (1);
+}
+
+#endif /* _AIC7XXX_INLINE_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
new file mode 100644
index 000000000000..031c6aaa5ca5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -0,0 +1,5043 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#235 $
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ * Substantially modified to include support for wide and twin bus
+ * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * SCB paging, and other rework of the code.
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * Thanks also go to (in alphabetical order) the following:
+ *
+ * Rory Bolt - Sequencer bug fixes
+ * Jay Estabrook - Initial DEC Alpha support
+ * Doug Ledford - Much needed abort/reset bug fixes
+ * Kai Makisara - DMAing of SCBs
+ *
+ * A Boot time option was also added for not resetting the scsi bus.
+ *
+ * Form: aic7xxx=extended
+ * aic7xxx=no_reset
+ * aic7xxx=verbose
+ *
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ * Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp
+ */
+
+/*
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs. Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ * 1: Import of the latest FreeBSD sequencer code for this driver
+ * 2: Modification of kernel code to accommodate different sequencer semantics
+ * 3: Extensive changes throughout kernel portion of driver to improve
+ * abort/reset processing and error hanndling
+ * 4: Other work contributed by various people on the Internet
+ * 5: Changes to printk information and verbosity selection code
+ * 6: General reliability related changes, especially in IRQ management
+ * 7: Modifications to the default probe/attach order for supported cards
+ * 8: SMP friendliness has been improved
+ *
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include <scsi/scsicam.h>
+
+/*
+ * Include aiclib.c as part of our
+ * "module dependencies are hard" work around.
+ */
+#include "aiclib.c"
+
+#include <linux/init.h> /* __setup */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#include "sd.h" /* For geometry detection */
+#endif
+
+#include <linux/mm.h> /* For fetching system memory size */
+#include <linux/blkdev.h> /* For block_size() */
+#include <linux/delay.h> /* For ssleep/msleep */
+
+/*
+ * Lock protecting manipulation of the ahc softc list.
+ */
+spinlock_t ahc_list_spinlock;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/* For dynamic sglist size calculation. */
+u_int ahc_linux_nseg;
+#endif
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY_MS
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY_MS
+#else
+#define AIC7XXX_RESET_DELAY 5000
+#endif
+
+/*
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
+ *
+ * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+ uint8_t tag_commands[16]; /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0 tagged queuing disabled
+ * 1 <= n <= 253 n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full. For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred). On some devices, queue full is returned for a temporary
+ * resource shortage. These devices will return queue full at varying
+ * depths. The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ * to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE AHC_MAX_QUEUE
+#endif
+
+#define AIC7XXX_CONFIGED_TAG_COMMANDS { \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE, \
+ AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE \
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS},
+ {AIC7XXX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * DV option:
+ *
+ * positive value = DV Enabled
+ * zero = DV Disabled
+ * negative value = DV Default for adapter type/seeprom
+ */
+#ifdef CONFIG_AIC7XXX_DV_SETTING
+#define AIC7XXX_CONFIGED_DV CONFIG_AIC7XXX_DV_SETTING
+#else
+#define AIC7XXX_CONFIGED_DV -1
+#endif
+
+static int8_t aic7xxx_dv_settings[] =
+{
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV,
+ AIC7XXX_CONFIGED_DV
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+ printk("(scsi%d:%c:%d:%d): ",
+ ahc->platform_data->host->host_no,
+ scb != NULL ? SCB_GET_CHANNEL(ahc, scb) : 'X',
+ scb != NULL ? SCB_GET_TARGET(ahc, scb) : -1,
+ scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ * cards in the system. This should be fixed. Exceptions to this
+ * rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic7xxx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips. The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect. Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from. The only exceptions to this are when a controller
+ * has its BIOS disabled. So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number. We also force
+ * all controllers with their BIOS disabled to the end of the list. This
+ * works on *almost* all computers. Where it doesn't work, we have this
+ * option. Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end. That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static uint32_t aic7xxx_reverse_scan;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ * 0 == Use whatever is in the SEEPROM or default to off
+ * 1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic7xxx_extended;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers. This is somewhat
+ * dubious at best. To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations where stray PCI transactions with bad parity are
+ * the norm rather than the exception, the error messages can be overwelming.
+ * It's included in the driver for completeness.
+ * 0 = Shut off PCI parity check
+ * non-0 = reverse polarity pci parity checking
+ */
+static uint32_t aic7xxx_pci_parity = ~0;
+
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices. Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers. NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+#ifndef CONFIG_AIC7XXX_PROBE_EISA_VL
+uint32_t aic7xxx_probe_eisa_vl;
+#else
+uint32_t aic7xxx_probe_eisa_vl = ~0;
+#endif
+
+/*
+ * There are lots of broken chipsets in the world. Some of them will
+ * violate the PCI spec when we issue byte sized memory writes to our
+ * controller. I/O mapped register access, if allowed by the given
+ * platform, will work in almost all cases.
+ */
+uint32_t aic7xxx_allow_memio = ~0;
+
+/*
+ * aic7xxx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic7xxx_detect().
+ */
+int aic7xxx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ * 0 - 256ms
+ * 1 - 128ms
+ * 2 - 64ms
+ * 3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static uint32_t aic7xxx_seltime;
+
+/*
+ * Certain devices do not perform any aging on commands. Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+uint32_t aic7xxx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+static char *aic7xxx = NULL;
+
+MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>");
+MODULE_DESCRIPTION("Adaptec Aic77XX/78XX SCSI Host Bus Adapter driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(AIC7XXX_DRIVER_VERSION);
+module_param(aic7xxx, charp, 0444);
+MODULE_PARM_DESC(aic7xxx,
+"period delimited, options string.\n"
+" verbose Enable verbose/diagnostic logging\n"
+" allow_memio Allow device registers to be memory mapped\n"
+" debug Bitmask of debug values to enable\n"
+" no_probe Toggle EISA/VLB controller probing\n"
+" probe_eisa_vl Toggle EISA/VLB controller probing\n"
+" no_reset Supress initial bus resets\n"
+" extended Enable extended geometry on all controllers\n"
+" periodic_otag Send an ordered tagged transaction\n"
+" periodically to prevent tag starvation.\n"
+" This may be required by some older disk\n"
+" drives or RAID arrays.\n"
+" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n"
+" tag_info:<tag_str> Set per-target tag depth\n"
+" global_tag_depth:<int> Global tag depth for every target\n"
+" on every bus\n"
+" dv:<dv_settings> Set per-controller Domain Validation Setting.\n"
+" seltime:<int> Selection Timeout\n"
+" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"\n"
+" Sample /etc/modprobe.conf line:\n"
+" Toggle EISA/VLB probing\n"
+" Set tag depth on Controller 1/Target 1 to 10 tags\n"
+" Shorten the selection timeout to 128ms\n"
+"\n"
+" options aic7xxx 'aic7xxx=probe_eisa_vl.tag_info:{{}.{.10}}.seltime:1'\n"
+);
+
+static void ahc_linux_handle_scsi_status(struct ahc_softc *,
+ struct ahc_linux_device *,
+ struct scb *);
+static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
+ Scsi_Cmnd *cmd);
+static void ahc_linux_filter_inquiry(struct ahc_softc*, struct ahc_devinfo*);
+static void ahc_linux_sem_timeout(u_long arg);
+static void ahc_linux_freeze_simq(struct ahc_softc *ahc);
+static void ahc_linux_release_simq(u_long arg);
+static void ahc_linux_dev_timed_unfreeze(u_long arg);
+static int ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
+static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
+static void ahc_linux_size_nseg(void);
+static void ahc_linux_thread_run_complete_queue(struct ahc_softc *ahc);
+static void ahc_linux_start_dv(struct ahc_softc *ahc);
+static void ahc_linux_dv_timeout(struct scsi_cmnd *cmd);
+static int ahc_linux_dv_thread(void *data);
+static void ahc_linux_kill_dv_thread(struct ahc_softc *ahc);
+static void ahc_linux_dv_target(struct ahc_softc *ahc, u_int target);
+static void ahc_linux_dv_transition(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_fill_cmd(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_dv_inq(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ,
+ u_int request_length);
+static void ahc_linux_dv_tur(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_dv_rebd(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_web(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_reb(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static void ahc_linux_dv_su(struct ahc_softc *ahc,
+ struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ);
+static int ahc_linux_fallback(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_linux_dv_complete(Scsi_Cmnd *cmd);
+static void ahc_linux_generate_dv_pattern(struct ahc_linux_target *targ);
+static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static u_int ahc_linux_user_dv_setting(struct ahc_softc *ahc);
+static void ahc_linux_device_queue_depth(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev);
+static struct ahc_linux_target* ahc_linux_alloc_target(struct ahc_softc*,
+ u_int, u_int);
+static void ahc_linux_free_target(struct ahc_softc*,
+ struct ahc_linux_target*);
+static struct ahc_linux_device* ahc_linux_alloc_device(struct ahc_softc*,
+ struct ahc_linux_target*,
+ u_int);
+static void ahc_linux_free_device(struct ahc_softc*,
+ struct ahc_linux_device*);
+static void ahc_linux_run_device_queue(struct ahc_softc*,
+ struct ahc_linux_device*);
+static void ahc_linux_setup_tag_info_global(char *p);
+static aic_option_callback_t ahc_linux_setup_tag_info;
+static aic_option_callback_t ahc_linux_setup_dv;
+static int aic7xxx_setup(char *s);
+static int ahc_linux_next_unit(void);
+static void ahc_runq_tasklet(unsigned long data);
+static struct ahc_cmd *ahc_linux_run_complete_queue(struct ahc_softc *ahc);
+
+/********************************* Inlines ************************************/
+static __inline void ahc_schedule_runq(struct ahc_softc *ahc);
+static __inline struct ahc_linux_device*
+ ahc_linux_get_device(struct ahc_softc *ahc, u_int channel,
+ u_int target, u_int lun, int alloc);
+static __inline void ahc_schedule_completeq(struct ahc_softc *ahc);
+static __inline void ahc_linux_check_device_queue(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev);
+static __inline struct ahc_linux_device *
+ ahc_linux_next_device_to_run(struct ahc_softc *ahc);
+static __inline void ahc_linux_run_device_queues(struct ahc_softc *ahc);
+static __inline void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
+
+static __inline int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+ struct ahc_dma_seg *sg,
+ dma_addr_t addr, bus_size_t len);
+
+static __inline void
+ahc_schedule_completeq(struct ahc_softc *ahc)
+{
+ if ((ahc->platform_data->flags & AHC_RUN_CMPLT_Q_TIMER) == 0) {
+ ahc->platform_data->flags |= AHC_RUN_CMPLT_Q_TIMER;
+ ahc->platform_data->completeq_timer.expires = jiffies;
+ add_timer(&ahc->platform_data->completeq_timer);
+ }
+}
+
+/*
+ * Must be called with our lock held.
+ */
+static __inline void
+ahc_schedule_runq(struct ahc_softc *ahc)
+{
+ tasklet_schedule(&ahc->platform_data->runq_tasklet);
+}
+
+static __inline struct ahc_linux_device*
+ahc_linux_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
+ u_int lun, int alloc)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_linux_device *dev;
+ u_int target_offset;
+
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL) {
+ if (alloc != 0) {
+ targ = ahc_linux_alloc_target(ahc, channel, target);
+ if (targ == NULL)
+ return (NULL);
+ } else
+ return (NULL);
+ }
+ dev = targ->devices[lun];
+ if (dev == NULL && alloc != 0)
+ dev = ahc_linux_alloc_device(ahc, targ, lun);
+ return (dev);
+}
+
+#define AHC_LINUX_MAX_RETURNED_ERRORS 4
+static struct ahc_cmd *
+ahc_linux_run_complete_queue(struct ahc_softc *ahc)
+{
+ struct ahc_cmd *acmd;
+ u_long done_flags;
+ int with_errors;
+
+ with_errors = 0;
+ ahc_done_lock(ahc, &done_flags);
+ while ((acmd = TAILQ_FIRST(&ahc->platform_data->completeq)) != NULL) {
+ Scsi_Cmnd *cmd;
+
+ if (with_errors > AHC_LINUX_MAX_RETURNED_ERRORS) {
+ /*
+ * Linux uses stack recursion to requeue
+ * commands that need to be retried. Avoid
+ * blowing out the stack by "spoon feeding"
+ * commands that completed with error back
+ * the operating system in case they are going
+ * to be retried. "ick"
+ */
+ ahc_schedule_completeq(ahc);
+ break;
+ }
+ TAILQ_REMOVE(&ahc->platform_data->completeq,
+ acmd, acmd_links.tqe);
+ cmd = &acmd_scsi_cmd(acmd);
+ cmd->host_scribble = NULL;
+ if (ahc_cmd_get_transaction_status(cmd) != DID_OK
+ || (cmd->result & 0xFF) != SCSI_STATUS_OK)
+ with_errors++;
+
+ cmd->scsi_done(cmd);
+ }
+ ahc_done_unlock(ahc, &done_flags);
+ return (acmd);
+}
+
+static __inline void
+ahc_linux_check_device_queue(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev)
+{
+ if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) != 0
+ && dev->active == 0) {
+ dev->flags &= ~AHC_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen--;
+ }
+
+ if (TAILQ_FIRST(&dev->busyq) == NULL
+ || dev->openings == 0 || dev->qfrozen != 0)
+ return;
+
+ ahc_linux_run_device_queue(ahc, dev);
+}
+
+static __inline struct ahc_linux_device *
+ahc_linux_next_device_to_run(struct ahc_softc *ahc)
+{
+
+ if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0
+ || (ahc->platform_data->qfrozen != 0
+ && AHC_DV_SIMQ_FROZEN(ahc) == 0))
+ return (NULL);
+ return (TAILQ_FIRST(&ahc->platform_data->device_runq));
+}
+
+static __inline void
+ahc_linux_run_device_queues(struct ahc_softc *ahc)
+{
+ struct ahc_linux_device *dev;
+
+ while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+ TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+ ahc_linux_check_device_queue(ahc, dev);
+ }
+}
+
+static __inline void
+ahc_linux_unmap_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ Scsi_Cmnd *cmd;
+
+ cmd = scb->io_ctx;
+ ahc_sync_sglist(ahc, scb, BUS_DMASYNC_POSTWRITE);
+ if (cmd->use_sg != 0) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(ahc->dev_softc, sg, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ } else if (cmd->request_bufflen != 0) {
+ pci_unmap_single(ahc->dev_softc,
+ scb->platform_data->buf_busaddr,
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ }
+}
+
+static __inline int
+ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+ struct ahc_dma_seg *sg, dma_addr_t addr, bus_size_t len)
+{
+ int consumed;
+
+ if ((scb->sg_count + 1) > AHC_NSEG)
+ panic("Too few segs for dma mapping. "
+ "Increase AHC_NSEG\n");
+
+ consumed = 1;
+ sg->addr = ahc_htole32(addr & 0xFFFFFFFF);
+ scb->platform_data->xfer_len += len;
+
+ if (sizeof(dma_addr_t) > 4
+ && (ahc->flags & AHC_39BIT_ADDRESSING) != 0)
+ len |= (addr >> 8) & AHC_SG_HIGH_ADDR_MASK;
+
+ sg->len = ahc_htole32(len);
+ return (consumed);
+}
+
+/************************ Host template entry points *************************/
+static int ahc_linux_detect(Scsi_Host_Template *);
+static int ahc_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+static const char *ahc_linux_info(struct Scsi_Host *);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int ahc_linux_slave_alloc(Scsi_Device *);
+static int ahc_linux_slave_configure(Scsi_Device *);
+static void ahc_linux_slave_destroy(Scsi_Device *);
+#if defined(__i386__)
+static int ahc_linux_biosparam(struct scsi_device*,
+ struct block_device*,
+ sector_t, int[]);
+#endif
+#else
+static int ahc_linux_release(struct Scsi_Host *);
+static void ahc_linux_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs);
+#if defined(__i386__)
+static int ahc_linux_biosparam(Disk *, kdev_t, int[]);
+#endif
+#endif
+static int ahc_linux_bus_reset(Scsi_Cmnd *);
+static int ahc_linux_dev_reset(Scsi_Cmnd *);
+static int ahc_linux_abort(Scsi_Cmnd *);
+
+/*
+ * Calculate a safe value for AHC_NSEG (as expressed through ahc_linux_nseg).
+ *
+ * In pre-2.5.X...
+ * The midlayer allocates an S/G array dynamically when a command is issued
+ * using SCSI malloc. This array, which is in an OS dependent format that
+ * must later be copied to our private S/G list, is sized to house just the
+ * number of segments needed for the current transfer. Since the code that
+ * sizes the SCSI malloc pool does not take into consideration fragmentation
+ * of the pool, executing transactions numbering just a fraction of our
+ * concurrent transaction limit with list lengths aproaching AHC_NSEG will
+ * quickly depleat the SCSI malloc pool of usable space. Unfortunately, the
+ * mid-layer does not properly handle this scsi malloc failures for the S/G
+ * array and the result can be a lockup of the I/O subsystem. We try to size
+ * our S/G list so that it satisfies our drivers allocation requirements in
+ * addition to avoiding fragmentation of the SCSI malloc pool.
+ */
+static void
+ahc_linux_size_nseg(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ u_int cur_size;
+ u_int best_size;
+
+ /*
+ * The SCSI allocator rounds to the nearest 512 bytes
+ * an cannot allocate across a page boundary. Our algorithm
+ * is to start at 1K of scsi malloc space per-command and
+ * loop through all factors of the PAGE_SIZE and pick the best.
+ */
+ best_size = 0;
+ for (cur_size = 1024; cur_size <= PAGE_SIZE; cur_size *= 2) {
+ u_int nseg;
+
+ nseg = cur_size / sizeof(struct scatterlist);
+ if (nseg < AHC_LINUX_MIN_NSEG)
+ continue;
+
+ if (best_size == 0) {
+ best_size = cur_size;
+ ahc_linux_nseg = nseg;
+ } else {
+ u_int best_rem;
+ u_int cur_rem;
+
+ /*
+ * Compare the traits of the current "best_size"
+ * with the current size to determine if the
+ * current size is a better size.
+ */
+ best_rem = best_size % sizeof(struct scatterlist);
+ cur_rem = cur_size % sizeof(struct scatterlist);
+ if (cur_rem < best_rem) {
+ best_size = cur_size;
+ ahc_linux_nseg = nseg;
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * Try to detect an Adaptec 7XXX controller.
+ */
+static int
+ahc_linux_detect(Scsi_Host_Template *template)
+{
+ struct ahc_softc *ahc;
+ int found = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * It is a bug that the upper layer takes
+ * this lock just prior to calling us.
+ */
+ spin_unlock_irq(&io_request_lock);
+#endif
+
+ /*
+ * Sanity checking of Linux SCSI data structures so
+ * that some of our hacks^H^H^H^H^Hassumptions aren't
+ * violated.
+ */
+ if (offsetof(struct ahc_cmd_internal, end)
+ > offsetof(struct scsi_cmnd, host_scribble)) {
+ printf("ahc_linux_detect: SCSI data structures changed.\n");
+ printf("ahc_linux_detect: Unable to attach\n");
+ return (0);
+ }
+ ahc_linux_size_nseg();
+ /*
+ * If we've been passed any parameters, process them now.
+ */
+ if (aic7xxx)
+ aic7xxx_setup(aic7xxx);
+
+ template->proc_name = "aic7xxx";
+
+ /*
+ * Initialize our softc list lock prior to
+ * probing for any adapters.
+ */
+ ahc_list_lockinit();
+
+ found = ahc_linux_pci_init();
+ if (!ahc_linux_eisa_init())
+ found++;
+
+ /*
+ * Register with the SCSI layer all
+ * controllers we've found.
+ */
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+
+ if (ahc_linux_register_host(ahc, template) == 0)
+ found++;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ spin_lock_irq(&io_request_lock);
+#endif
+ aic7xxx_detect_complete++;
+
+ return (found);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+int
+ahc_linux_release(struct Scsi_Host * host)
+{
+ struct ahc_softc *ahc;
+ u_long l;
+
+ ahc_list_lock(&l);
+ if (host != NULL) {
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahc = ahc_find_softc(*(struct ahc_softc **)host->hostdata);
+ if (ahc != NULL) {
+ u_long s;
+
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, FALSE);
+ ahc_unlock(ahc, &s);
+ ahc_free(ahc);
+ }
+ }
+ ahc_list_unlock(&l);
+ return (0);
+}
+#endif
+
+/*
+ * Return a string describing the driver.
+ */
+static const char *
+ahc_linux_info(struct Scsi_Host *host)
+{
+ static char buffer[512];
+ char ahc_info[256];
+ char *bp;
+ struct ahc_softc *ahc;
+
+ bp = &buffer[0];
+ ahc = *(struct ahc_softc **)host->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev ");
+ strcat(bp, AIC7XXX_DRIVER_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, ahc->description);
+ strcat(bp, ">\n");
+ strcat(bp, " ");
+ ahc_controller_info(ahc, ahc_info);
+ strcat(bp, ahc_info);
+ strcat(bp, "\n");
+
+ return (bp);
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+static int
+ahc_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+ struct ahc_softc *ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+
+ /*
+ * Save the callback on completion function.
+ */
+ cmd->scsi_done = scsi_done;
+
+ ahc_midlayer_entrypoint_lock(ahc, &flags);
+
+ /*
+ * Close the race of a command that was in the process of
+ * being queued to us just as our simq was frozen. Let
+ * DV commands through so long as we are only frozen to
+ * perform DV.
+ */
+ if (ahc->platform_data->qfrozen != 0
+ && AHC_DV_CMD(cmd) == 0) {
+
+ ahc_cmd_set_transaction_status(cmd, CAM_REQUEUE_REQ);
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ ahc_schedule_completeq(ahc);
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+ return (0);
+ }
+ dev = ahc_linux_get_device(ahc, cmd->device->channel, cmd->device->id,
+ cmd->device->lun, /*alloc*/TRUE);
+ if (dev == NULL) {
+ ahc_cmd_set_transaction_status(cmd, CAM_RESRC_UNAVAIL);
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ ahc_schedule_completeq(ahc);
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+ printf("%s: aic7xxx_linux_queue - Unable to allocate device!\n",
+ ahc_name(ahc));
+ return (0);
+ }
+ cmd->result = CAM_REQ_INPROG << 16;
+ TAILQ_INSERT_TAIL(&dev->busyq, (struct ahc_cmd *)cmd, acmd_links.tqe);
+ if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ ahc_linux_run_device_queues(ahc);
+ }
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+ return (0);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int
+ahc_linux_slave_alloc(Scsi_Device *device)
+{
+ struct ahc_softc *ahc;
+
+ ahc = *((struct ahc_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Alloc %d\n", ahc_name(ahc), device->id);
+ return (0);
+}
+
+static int
+ahc_linux_slave_configure(Scsi_Device *device)
+{
+ struct ahc_softc *ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Configure %d\n", ahc_name(ahc), device->id);
+ ahc_midlayer_entrypoint_lock(ahc, &flags);
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahc_linux_get_device(ahc, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ dev->scsi_device = device;
+ ahc_linux_device_queue_depth(ahc, dev);
+ }
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+ return (0);
+}
+
+static void
+ahc_linux_slave_destroy(Scsi_Device *device)
+{
+ struct ahc_softc *ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)device->host->hostdata);
+ if (bootverbose)
+ printf("%s: Slave Destroy %d\n", ahc_name(ahc), device->id);
+ ahc_midlayer_entrypoint_lock(ahc, &flags);
+ dev = ahc_linux_get_device(ahc, device->channel,
+ device->id, device->lun,
+ /*alloc*/FALSE);
+ /*
+ * Filter out "silly" deletions of real devices by only
+ * deleting devices that have had slave_configure()
+ * called on them. All other devices that have not
+ * been configured will automatically be deleted by
+ * the refcounting process.
+ */
+ if (dev != NULL
+ && (dev->flags & AHC_DEV_SLAVE_CONFIGURED) != 0) {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ if (TAILQ_EMPTY(&dev->busyq)
+ && dev->active == 0
+ && (dev->flags & AHC_DEV_TIMER_ACTIVE) == 0)
+ ahc_linux_free_device(ahc, dev);
+ }
+ ahc_midlayer_entrypoint_unlock(ahc, &flags);
+}
+#else
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+ahc_linux_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs)
+{
+ Scsi_Device *device;
+ Scsi_Device *ldev;
+ struct ahc_softc *ahc;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)host->hostdata);
+ ahc_lock(ahc, &flags);
+ for (device = scsi_devs; device != NULL; device = device->next) {
+
+ /*
+ * Watch out for duplicate devices. This works around
+ * some quirks in how the SCSI scanning code does its
+ * device management.
+ */
+ for (ldev = scsi_devs; ldev != device; ldev = ldev->next) {
+ if (ldev->host == device->host
+ && ldev->channel == device->channel
+ && ldev->id == device->id
+ && ldev->lun == device->lun)
+ break;
+ }
+ /* Skip duplicate. */
+ if (ldev != device)
+ continue;
+
+ if (device->host == host) {
+ struct ahc_linux_device *dev;
+
+ /*
+ * Since Linux has attached to the device, configure
+ * it so we don't free and allocate the device
+ * structure on every command.
+ */
+ dev = ahc_linux_get_device(ahc, device->channel,
+ device->id, device->lun,
+ /*alloc*/TRUE);
+ if (dev != NULL) {
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ dev->scsi_device = device;
+ ahc_linux_device_queue_depth(ahc, dev);
+ device->queue_depth = dev->openings
+ + dev->active;
+ if ((dev->flags & (AHC_DEV_Q_BASIC
+ | AHC_DEV_Q_TAGGED)) == 0) {
+ /*
+ * We allow the OS to queue 2 untagged
+ * transactions to us at any time even
+ * though we can only execute them
+ * serially on the controller/device.
+ * This should remove some latency.
+ */
+ device->queue_depth = 2;
+ }
+ }
+ }
+ }
+ ahc_unlock(ahc, &flags);
+}
+#endif
+
+#if defined(__i386__)
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ahc_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ uint8_t *bh;
+#else
+ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ struct scsi_device *sdev = disk->device;
+ u_long capacity = disk->capacity;
+ struct buffer_head *bh;
+#endif
+ int heads;
+ int sectors;
+ int cylinders;
+ int ret;
+ int extended;
+ struct ahc_softc *ahc;
+ u_int channel;
+
+ ahc = *((struct ahc_softc **)sdev->host->hostdata);
+ channel = sdev->channel;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ bh = scsi_bios_ptable(bdev);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev));
+#else
+ bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+#endif
+
+ if (bh) {
+ ret = scsi_partsize(bh, capacity,
+ &geom[2], &geom[0], &geom[1]);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ kfree(bh);
+#else
+ brelse(bh);
+#endif
+ if (ret != -1)
+ return (ret);
+ }
+ heads = 64;
+ sectors = 32;
+ cylinders = aic_sector_div(capacity, heads, sectors);
+
+ if (aic7xxx_extended != 0)
+ extended = 1;
+ else if (channel == 0)
+ extended = (ahc->flags & AHC_EXTENDED_TRANS_A) != 0;
+ else
+ extended = (ahc->flags & AHC_EXTENDED_TRANS_B) != 0;
+ if (extended && cylinders >= 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = aic_sector_div(capacity, heads, sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return (0);
+}
+#endif
+
+/*
+ * Abort the current SCSI command(s).
+ */
+static int
+ahc_linux_abort(Scsi_Cmnd *cmd)
+{
+ int error;
+
+ error = ahc_linux_queue_recovery_cmd(cmd, SCB_ABORT);
+ if (error != 0)
+ printf("aic7xxx_abort returns 0x%x\n", error);
+ return (error);
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+static int
+ahc_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+ int error;
+
+ error = ahc_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+ if (error != 0)
+ printf("aic7xxx_dev_reset returns 0x%x\n", error);
+ return (error);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+static int
+ahc_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+ u_long s;
+ int found;
+
+ ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+ ahc_midlayer_entrypoint_lock(ahc, &s);
+ found = ahc_reset_channel(ahc, cmd->device->channel + 'A',
+ /*initiate reset*/TRUE);
+ ahc_linux_run_complete_queue(ahc);
+ ahc_midlayer_entrypoint_unlock(ahc, &s);
+
+ if (bootverbose)
+ printf("%s: SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", ahc_name(ahc), found);
+
+ return SUCCESS;
+}
+
+Scsi_Host_Template aic7xxx_driver_template = {
+ .module = THIS_MODULE,
+ .name = "aic7xxx",
+ .proc_info = ahc_linux_proc_info,
+ .info = ahc_linux_info,
+ .queuecommand = ahc_linux_queue,
+ .eh_abort_handler = ahc_linux_abort,
+ .eh_device_reset_handler = ahc_linux_dev_reset,
+ .eh_bus_reset_handler = ahc_linux_bus_reset,
+#if defined(__i386__)
+ .bios_param = ahc_linux_biosparam,
+#endif
+ .can_queue = AHC_MAX_QUEUE,
+ .this_id = -1,
+ .cmd_per_lun = 2,
+ .use_clustering = ENABLE_CLUSTERING,
+ .slave_alloc = ahc_linux_slave_alloc,
+ .slave_configure = ahc_linux_slave_configure,
+ .slave_destroy = ahc_linux_slave_destroy,
+};
+
+/**************************** Tasklet Handler *********************************/
+
+/*
+ * In 2.4.X and above, this routine is called from a tasklet,
+ * so we must re-acquire our lock prior to executing this code.
+ * In all prior kernels, ahc_schedule_runq() calls this routine
+ * directly and ahc_schedule_runq() is called with our lock held.
+ */
+static void
+ahc_runq_tasklet(unsigned long data)
+{
+ struct ahc_softc* ahc;
+ struct ahc_linux_device *dev;
+ u_long flags;
+
+ ahc = (struct ahc_softc *)data;
+ ahc_lock(ahc, &flags);
+ while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+
+ TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+ dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+ ahc_linux_check_device_queue(ahc, dev);
+ /* Yeild to our interrupt handler */
+ ahc_unlock(ahc, &flags);
+ ahc_lock(ahc, &flags);
+ }
+ ahc_unlock(ahc, &flags);
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahc, cmd) \
+ ((((cmd)->device->id << TID_SHIFT) & TID) \
+ | (((cmd)->device->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b) \
+ | (((cmd)->device->channel == 0) ? 0 : TWIN_CHNLB))
+
+/******************************** Bus DMA *************************************/
+int
+ahc_dma_tag_create(struct ahc_softc *ahc, bus_dma_tag_t parent,
+ bus_size_t alignment, bus_size_t boundary,
+ dma_addr_t lowaddr, dma_addr_t highaddr,
+ bus_dma_filter_t *filter, void *filterarg,
+ bus_size_t maxsize, int nsegments,
+ bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+ bus_dma_tag_t dmat;
+
+ dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+ if (dmat == NULL)
+ return (ENOMEM);
+
+ /*
+ * Linux is very simplistic about DMA memory. For now don't
+ * maintain all specification information. Once Linux supplies
+ * better facilities for doing these operations, or the
+ * needs of this particular driver change, we might need to do
+ * more here.
+ */
+ dmat->alignment = alignment;
+ dmat->boundary = boundary;
+ dmat->maxsize = maxsize;
+ *ret_tag = dmat;
+ return (0);
+}
+
+void
+ahc_dma_tag_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat)
+{
+ free(dmat, M_DEVBUF);
+}
+
+int
+ahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr,
+ int flags, bus_dmamap_t *mapp)
+{
+ bus_dmamap_t map;
+
+ map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+ if (map == NULL)
+ return (ENOMEM);
+ /*
+ * Although we can dma data above 4GB, our
+ * "consistent" memory is below 4GB for
+ * space efficiency reasons (only need a 4byte
+ * address). For this reason, we have to reset
+ * our dma mask when doing allocations.
+ */
+ if (ahc->dev_softc != NULL)
+ if (pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF)) {
+ printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n");
+ kfree(map);
+ return (ENODEV);
+ }
+ *vaddr = pci_alloc_consistent(ahc->dev_softc,
+ dmat->maxsize, &map->bus_addr);
+ if (ahc->dev_softc != NULL)
+ if (pci_set_dma_mask(ahc->dev_softc,
+ ahc->platform_data->hw_dma_mask)) {
+ printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n");
+ kfree(map);
+ return (ENODEV);
+ }
+ if (*vaddr == NULL)
+ return (ENOMEM);
+ *mapp = map;
+ return(0);
+}
+
+void
+ahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat,
+ void* vaddr, bus_dmamap_t map)
+{
+ pci_free_consistent(ahc->dev_softc, dmat->maxsize,
+ vaddr, map->bus_addr);
+}
+
+int
+ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map,
+ void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+ void *cb_arg, int flags)
+{
+ /*
+ * Assume for now that this will only be used during
+ * initialization and not for per-transaction buffer mapping.
+ */
+ bus_dma_segment_t stack_sg;
+
+ stack_sg.ds_addr = map->bus_addr;
+ stack_sg.ds_len = dmat->maxsize;
+ cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+ return (0);
+}
+
+void
+ahc_dmamap_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /*
+ * The map may is NULL in our < 2.3.X implementation.
+ * Now it's 2.6.5, but just in case...
+ */
+ BUG_ON(map == NULL);
+ free(map, M_DEVBUF);
+}
+
+int
+ahc_dmamap_unload(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+ /* Nothing to do */
+ return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+/*
+ * Compare "left hand" softc with "right hand" softc, returning:
+ * < 0 - lahc has a lower priority than rahc
+ * 0 - Softcs are equal
+ * > 0 - lahc has a higher priority than rahc
+ */
+int
+ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
+{
+ int value;
+ int rvalue;
+ int lvalue;
+
+ /*
+ * Under Linux, cards are ordered as follows:
+ * 1) VLB/EISA BIOS enabled devices sorted by BIOS address.
+ * 2) PCI devices with BIOS enabled sorted by bus/slot/func.
+ * 3) All remaining VLB/EISA devices sorted by ioport.
+ * 4) All remaining PCI devices sorted by bus/slot/func.
+ */
+ value = (lahc->flags & AHC_BIOS_ENABLED)
+ - (rahc->flags & AHC_BIOS_ENABLED);
+ if (value != 0)
+ /* Controllers with BIOS enabled have a *higher* priority */
+ return (value);
+
+ /*
+ * Same BIOS setting, now sort based on bus type.
+ * EISA and VL controllers sort together. EISA/VL
+ * have higher priority than PCI.
+ */
+ rvalue = (rahc->chip & AHC_BUS_MASK);
+ if (rvalue == AHC_VL)
+ rvalue = AHC_EISA;
+ lvalue = (lahc->chip & AHC_BUS_MASK);
+ if (lvalue == AHC_VL)
+ lvalue = AHC_EISA;
+ value = rvalue - lvalue;
+ if (value != 0)
+ return (value);
+
+ /* Still equal. Sort by BIOS address, ioport, or bus/slot/func. */
+ switch (rvalue) {
+#ifdef CONFIG_PCI
+ case AHC_PCI:
+ {
+ char primary_channel;
+
+ if (aic7xxx_reverse_scan != 0)
+ value = ahc_get_pci_bus(lahc->dev_softc)
+ - ahc_get_pci_bus(rahc->dev_softc);
+ else
+ value = ahc_get_pci_bus(rahc->dev_softc)
+ - ahc_get_pci_bus(lahc->dev_softc);
+ if (value != 0)
+ break;
+ if (aic7xxx_reverse_scan != 0)
+ value = ahc_get_pci_slot(lahc->dev_softc)
+ - ahc_get_pci_slot(rahc->dev_softc);
+ else
+ value = ahc_get_pci_slot(rahc->dev_softc)
+ - ahc_get_pci_slot(lahc->dev_softc);
+ if (value != 0)
+ break;
+ /*
+ * On multi-function devices, the user can choose
+ * to have function 1 probed before function 0.
+ * Give whichever channel is the primary channel
+ * the highest priority.
+ */
+ primary_channel = (lahc->flags & AHC_PRIMARY_CHANNEL) + 'A';
+ value = -1;
+ if (lahc->channel == primary_channel)
+ value = 1;
+ break;
+ }
+#endif
+ case AHC_EISA:
+ if ((rahc->flags & AHC_BIOS_ENABLED) != 0) {
+ value = rahc->platform_data->bios_address
+ - lahc->platform_data->bios_address;
+ } else {
+ value = rahc->bsh.ioport
+ - lahc->bsh.ioport;
+ }
+ break;
+ default:
+ panic("ahc_softc_sort: invalid bus type");
+ }
+ return (value);
+}
+
+static void
+ahc_linux_setup_tag_info_global(char *p)
+{
+ int tags, i, j;
+
+ tags = simple_strtoul(p + 1, NULL, 0) & 0xff;
+ printf("Setting Global Tags= %d\n", tags);
+
+ for (i = 0; i < NUM_ELEMENTS(aic7xxx_tag_info); i++) {
+ for (j = 0; j < AHC_NUM_TARGETS; j++) {
+ aic7xxx_tag_info[i].tag_commands[j] = tags;
+ }
+ }
+}
+
+static void
+ahc_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
+{
+
+ if ((instance >= 0) && (targ >= 0)
+ && (instance < NUM_ELEMENTS(aic7xxx_tag_info))
+ && (targ < AHC_NUM_TARGETS)) {
+ aic7xxx_tag_info[instance].tag_commands[targ] = value & 0xff;
+ if (bootverbose)
+ printf("tag_info[%d:%d] = %d\n", instance, targ, value);
+ }
+}
+
+static void
+ahc_linux_setup_dv(u_long arg, int instance, int targ, int32_t value)
+{
+
+ if ((instance >= 0)
+ && (instance < NUM_ELEMENTS(aic7xxx_dv_settings))) {
+ aic7xxx_dv_settings[instance] = value;
+ if (bootverbose)
+ printf("dv[%d] = %d\n", instance, value);
+ }
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=stpwlev:1,extended
+ */
+static int
+aic7xxx_setup(char *s)
+{
+ int i, n;
+ char *p;
+ char *end;
+
+ static struct {
+ const char *name;
+ uint32_t *flag;
+ } options[] = {
+ { "extended", &aic7xxx_extended },
+ { "no_reset", &aic7xxx_no_reset },
+ { "verbose", &aic7xxx_verbose },
+ { "allow_memio", &aic7xxx_allow_memio},
+#ifdef AHC_DEBUG
+ { "debug", &ahc_debug },
+#endif
+ { "reverse_scan", &aic7xxx_reverse_scan },
+ { "no_probe", &aic7xxx_probe_eisa_vl },
+ { "probe_eisa_vl", &aic7xxx_probe_eisa_vl },
+ { "periodic_otag", &aic7xxx_periodic_otag },
+ { "pci_parity", &aic7xxx_pci_parity },
+ { "seltime", &aic7xxx_seltime },
+ { "tag_info", NULL },
+ { "global_tag_depth", NULL },
+ { "dv", NULL }
+ };
+
+ end = strchr(s, '\0');
+
+ /*
+ * XXX ia64 gcc isn't smart enough to know that NUM_ELEMENTS
+ * will never be 0 in this case.
+ */
+ n = 0;
+
+ while ((p = strsep(&s, ",.")) != NULL) {
+ if (*p == '\0')
+ continue;
+ for (i = 0; i < NUM_ELEMENTS(options); i++) {
+
+ n = strlen(options[i].name);
+ if (strncmp(options[i].name, p, n) == 0)
+ break;
+ }
+ if (i == NUM_ELEMENTS(options))
+ continue;
+
+ if (strncmp(p, "global_tag_depth", n) == 0) {
+ ahc_linux_setup_tag_info_global(p + n);
+ } else if (strncmp(p, "tag_info", n) == 0) {
+ s = aic_parse_brace_option("tag_info", p + n, end,
+ 2, ahc_linux_setup_tag_info, 0);
+ } else if (strncmp(p, "dv", n) == 0) {
+ s = aic_parse_brace_option("dv", p + n, end, 1,
+ ahc_linux_setup_dv, 0);
+ } else if (p[n] == ':') {
+ *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+ } else if (strncmp(p, "verbose", n) == 0) {
+ *(options[i].flag) = 1;
+ } else {
+ *(options[i].flag) ^= 0xFFFFFFFF;
+ }
+ }
+ return 1;
+}
+
+__setup("aic7xxx=", aic7xxx_setup);
+
+uint32_t aic7xxx_verbose;
+
+int
+ahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
+{
+ char buf[80];
+ struct Scsi_Host *host;
+ char *new_name;
+ u_long s;
+ u_int targ_offset;
+
+ template->name = ahc->description;
+ host = scsi_host_alloc(template, sizeof(struct ahc_softc *));
+ if (host == NULL)
+ return (ENOMEM);
+
+ *((struct ahc_softc **)host->hostdata) = ahc;
+ ahc_lock(ahc, &s);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_assign_lock(host, &ahc->platform_data->spin_lock);
+#elif AHC_SCSI_HAS_HOST_LOCK != 0
+ host->lock = &ahc->platform_data->spin_lock;
+#endif
+ ahc->platform_data->host = host;
+ host->can_queue = AHC_MAX_QUEUE;
+ host->cmd_per_lun = 2;
+ /* XXX No way to communicate the ID for multiple channels */
+ host->this_id = ahc->our_id;
+ host->irq = ahc->platform_data->irq;
+ host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
+ host->max_lun = AHC_NUM_LUNS;
+ host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;
+ host->sg_tablesize = AHC_NSEG;
+ ahc_set_unit(ahc, ahc_linux_next_unit());
+ sprintf(buf, "scsi%d", host->host_no);
+ new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (new_name != NULL) {
+ strcpy(new_name, buf);
+ ahc_set_name(ahc, new_name);
+ }
+ host->unique_id = ahc->unit;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ scsi_set_pci_device(host, ahc->dev_softc);
+#endif
+ ahc_linux_initialize_scsi_bus(ahc);
+ ahc_unlock(ahc, &s);
+ ahc->platform_data->dv_pid = kernel_thread(ahc_linux_dv_thread, ahc, 0);
+ ahc_lock(ahc, &s);
+ if (ahc->platform_data->dv_pid < 0) {
+ printf("%s: Failed to create DV thread, error= %d\n",
+ ahc_name(ahc), ahc->platform_data->dv_pid);
+ return (-ahc->platform_data->dv_pid);
+ }
+ /*
+ * Initially allocate *all* of our linux target objects
+ * so that the DV thread will scan them all in parallel
+ * just after driver initialization. Any device that
+ * does not exist will have its target object destroyed
+ * by the selection timeout handler. In the case of a
+ * device that appears after the initial DV scan, async
+ * negotiation will occur for the first command, and DV
+ * will comence should that first command be successful.
+ */
+ for (targ_offset = 0;
+ targ_offset < host->max_id * (host->max_channel + 1);
+ targ_offset++) {
+ u_int channel;
+ u_int target;
+
+ channel = 0;
+ target = targ_offset;
+ if (target > 7
+ && (ahc->features & AHC_TWIN) != 0) {
+ channel = 1;
+ target &= 0x7;
+ }
+ /*
+ * Skip our own ID. Some Compaq/HP storage devices
+ * have enclosure management devices that respond to
+ * single bit selection (i.e. selecting ourselves).
+ * It is expected that either an external application
+ * or a modified kernel will be used to probe this
+ * ID if it is appropriate. To accommodate these
+ * installations, ahc_linux_alloc_target() will allocate
+ * for our ID if asked to do so.
+ */
+ if ((channel == 0 && target == ahc->our_id)
+ || (channel == 1 && target == ahc->our_id_b))
+ continue;
+
+ ahc_linux_alloc_target(ahc, channel, target);
+ }
+ ahc_intr_enable(ahc, TRUE);
+ ahc_linux_start_dv(ahc);
+ ahc_unlock(ahc, &s);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_add_host(host, (ahc->dev_softc ? &ahc->dev_softc->dev : NULL)); /* XXX handle failure */
+ scsi_scan_host(host);
+#endif
+ return (0);
+}
+
+uint64_t
+ahc_linux_get_memsize(void)
+{
+ struct sysinfo si;
+
+ si_meminfo(&si);
+ return ((uint64_t)si.totalram << PAGE_SHIFT);
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device. We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahc_linux_next_unit(void)
+{
+ struct ahc_softc *ahc;
+ int unit;
+
+ unit = 0;
+retry:
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ if (ahc->unit == unit) {
+ unit++;
+ goto retry;
+ }
+ }
+ return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc)
+{
+ int i;
+ int numtarg;
+
+ i = 0;
+ numtarg = 0;
+
+ if (aic7xxx_no_reset != 0)
+ ahc->flags &= ~(AHC_RESET_BUS_A|AHC_RESET_BUS_B);
+
+ if ((ahc->flags & AHC_RESET_BUS_A) != 0)
+ ahc_reset_channel(ahc, 'A', /*initiate_reset*/TRUE);
+ else
+ numtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+
+ if ((ahc->features & AHC_TWIN) != 0) {
+
+ if ((ahc->flags & AHC_RESET_BUS_B) != 0) {
+ ahc_reset_channel(ahc, 'B', /*initiate_reset*/TRUE);
+ } else {
+ if (numtarg == 0)
+ i = 8;
+ numtarg += 8;
+ }
+ }
+
+ /*
+ * Force negotiation to async for all targets that
+ * will not see an initial bus reset.
+ */
+ for (; i < numtarg; i++) {
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
+ }
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ ahc_compile_devinfo(&devinfo, our_id, target_id,
+ CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+ ahc_update_neg_request(ahc, &devinfo, tstate,
+ tinfo, AHC_NEG_ALWAYS);
+ }
+ /* Give the bus some time to recover */
+ if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {
+ ahc_linux_freeze_simq(ahc);
+ init_timer(&ahc->platform_data->reset_timer);
+ ahc->platform_data->reset_timer.data = (u_long)ahc;
+ ahc->platform_data->reset_timer.expires =
+ jiffies + (AIC7XXX_RESET_DELAY * HZ)/1000;
+ ahc->platform_data->reset_timer.function =
+ ahc_linux_release_simq;
+ add_timer(&ahc->platform_data->reset_timer);
+ }
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+
+ ahc->platform_data =
+ malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT);
+ if (ahc->platform_data == NULL)
+ return (ENOMEM);
+ memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
+ TAILQ_INIT(&ahc->platform_data->completeq);
+ TAILQ_INIT(&ahc->platform_data->device_runq);
+ ahc->platform_data->irq = AHC_LINUX_NOIRQ;
+ ahc->platform_data->hw_dma_mask = 0xFFFFFFFF;
+ ahc_lockinit(ahc);
+ ahc_done_lockinit(ahc);
+ init_timer(&ahc->platform_data->completeq_timer);
+ ahc->platform_data->completeq_timer.data = (u_long)ahc;
+ ahc->platform_data->completeq_timer.function =
+ (ahc_linux_callback_t *)ahc_linux_thread_run_complete_queue;
+ init_MUTEX_LOCKED(&ahc->platform_data->eh_sem);
+ init_MUTEX_LOCKED(&ahc->platform_data->dv_sem);
+ init_MUTEX_LOCKED(&ahc->platform_data->dv_cmd_sem);
+ tasklet_init(&ahc->platform_data->runq_tasklet, ahc_runq_tasklet,
+ (unsigned long)ahc);
+ ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
+ ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;
+ if (aic7xxx_pci_parity == 0)
+ ahc->flags |= AHC_DISABLE_PCI_PERR;
+
+ return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_linux_device *dev;
+ int i, j;
+
+ if (ahc->platform_data != NULL) {
+ del_timer_sync(&ahc->platform_data->completeq_timer);
+ ahc_linux_kill_dv_thread(ahc);
+ tasklet_kill(&ahc->platform_data->runq_tasklet);
+ if (ahc->platform_data->host != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ scsi_remove_host(ahc->platform_data->host);
+#endif
+ scsi_host_put(ahc->platform_data->host);
+ }
+
+ /* destroy all of the device and target objects */
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ targ = ahc->platform_data->targets[i];
+ if (targ != NULL) {
+ /* Keep target around through the loop. */
+ targ->refcount++;
+ for (j = 0; j < AHC_NUM_LUNS; j++) {
+
+ if (targ->devices[j] == NULL)
+ continue;
+ dev = targ->devices[j];
+ ahc_linux_free_device(ahc, dev);
+ }
+ /*
+ * Forcibly free the target now that
+ * all devices are gone.
+ */
+ ahc_linux_free_target(ahc, targ);
+ }
+ }
+
+ if (ahc->platform_data->irq != AHC_LINUX_NOIRQ)
+ free_irq(ahc->platform_data->irq, ahc);
+ if (ahc->tag == BUS_SPACE_PIO
+ && ahc->bsh.ioport != 0)
+ release_region(ahc->bsh.ioport, 256);
+ if (ahc->tag == BUS_SPACE_MEMIO
+ && ahc->bsh.maddr != NULL) {
+ iounmap(ahc->bsh.maddr);
+ release_mem_region(ahc->platform_data->mem_busaddr,
+ 0x1000);
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * In 2.4 we detach from the scsi midlayer before the PCI
+ * layer invokes our remove callback. No per-instance
+ * detach is provided, so we must reach inside the PCI
+ * subsystem's internals and detach our driver manually.
+ */
+ if (ahc->dev_softc != NULL)
+ ahc->dev_softc->driver = NULL;
+#endif
+ free(ahc->platform_data, M_DEVBUF);
+ }
+}
+
+void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc_platform_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg)
+{
+ struct ahc_linux_device *dev;
+ int was_queuing;
+ int now_queuing;
+
+ dev = ahc_linux_get_device(ahc, devinfo->channel - 'A',
+ devinfo->target,
+ devinfo->lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ return;
+ was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);
+ switch (alg) {
+ default:
+ case AHC_QUEUE_NONE:
+ now_queuing = 0;
+ break;
+ case AHC_QUEUE_BASIC:
+ now_queuing = AHC_DEV_Q_BASIC;
+ break;
+ case AHC_QUEUE_TAGGED:
+ now_queuing = AHC_DEV_Q_TAGGED;
+ break;
+ }
+ if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0
+ && (was_queuing != now_queuing)
+ && (dev->active != 0)) {
+ dev->flags |= AHC_DEV_FREEZE_TIL_EMPTY;
+ dev->qfrozen++;
+ }
+
+ dev->flags &= ~(AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED|AHC_DEV_PERIODIC_OTAG);
+ if (now_queuing) {
+ u_int usertags;
+
+ usertags = ahc_linux_user_tagdepth(ahc, devinfo);
+ if (!was_queuing) {
+ /*
+ * Start out agressively and allow our
+ * dynamic queue depth algorithm to take
+ * care of the rest.
+ */
+ dev->maxtags = usertags;
+ dev->openings = dev->maxtags - dev->active;
+ }
+ if (dev->maxtags == 0) {
+ /*
+ * Queueing is disabled by the user.
+ */
+ dev->openings = 1;
+ } else if (alg == AHC_QUEUE_TAGGED) {
+ dev->flags |= AHC_DEV_Q_TAGGED;
+ if (aic7xxx_periodic_otag != 0)
+ dev->flags |= AHC_DEV_PERIODIC_OTAG;
+ } else
+ dev->flags |= AHC_DEV_Q_BASIC;
+ } else {
+ /* We can only have one opening. */
+ dev->maxtags = 0;
+ dev->openings = 1 - dev->active;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ if (dev->scsi_device != NULL) {
+ switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) {
+ case AHC_DEV_Q_BASIC:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_SIMPLE_TASK,
+ dev->openings + dev->active);
+ break;
+ case AHC_DEV_Q_TAGGED:
+ scsi_adjust_queue_depth(dev->scsi_device,
+ MSG_ORDERED_TASK,
+ dev->openings + dev->active);
+ break;
+ default:
+ /*
+ * We allow the OS to queue 2 untagged transactions to
+ * us at any time even though we can only execute them
+ * serially on the controller/device. This should
+ * remove some latency.
+ */
+ scsi_adjust_queue_depth(dev->scsi_device,
+ /*NON-TAGGED*/0,
+ /*queue depth*/2);
+ break;
+ }
+ }
+#endif
+}
+
+int
+ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
+{
+ int chan;
+ int maxchan;
+ int targ;
+ int maxtarg;
+ int clun;
+ int maxlun;
+ int count;
+
+ if (tag != SCB_LIST_NULL)
+ return (0);
+
+ chan = 0;
+ if (channel != ALL_CHANNELS) {
+ chan = channel - 'A';
+ maxchan = chan + 1;
+ } else {
+ maxchan = (ahc->features & AHC_TWIN) ? 2 : 1;
+ }
+ targ = 0;
+ if (target != CAM_TARGET_WILDCARD) {
+ targ = target;
+ maxtarg = targ + 1;
+ } else {
+ maxtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+ }
+ clun = 0;
+ if (lun != CAM_LUN_WILDCARD) {
+ clun = lun;
+ maxlun = clun + 1;
+ } else {
+ maxlun = AHC_NUM_LUNS;
+ }
+
+ count = 0;
+ for (; chan < maxchan; chan++) {
+
+ for (; targ < maxtarg; targ++) {
+
+ for (; clun < maxlun; clun++) {
+ struct ahc_linux_device *dev;
+ struct ahc_busyq *busyq;
+ struct ahc_cmd *acmd;
+
+ dev = ahc_linux_get_device(ahc, chan,
+ targ, clun,
+ /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ busyq = &dev->busyq;
+ while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+ Scsi_Cmnd *cmd;
+
+ cmd = &acmd_scsi_cmd(acmd);
+ TAILQ_REMOVE(busyq, acmd,
+ acmd_links.tqe);
+ count++;
+ cmd->result = status << 16;
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ }
+ }
+ }
+ }
+
+ return (count);
+}
+
+static void
+ahc_linux_thread_run_complete_queue(struct ahc_softc *ahc)
+{
+ u_long flags;
+
+ ahc_lock(ahc, &flags);
+ del_timer(&ahc->platform_data->completeq_timer);
+ ahc->platform_data->flags &= ~AHC_RUN_CMPLT_Q_TIMER;
+ ahc_linux_run_complete_queue(ahc);
+ ahc_unlock(ahc, &flags);
+}
+
+static void
+ahc_linux_start_dv(struct ahc_softc *ahc)
+{
+
+ /*
+ * Freeze the simq and signal ahc_linux_queue to not let any
+ * more commands through.
+ */
+ if ((ahc->platform_data->flags & AHC_DV_ACTIVE) == 0) {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s: Waking DV thread\n", ahc_name(ahc));
+#endif
+
+ ahc->platform_data->flags |= AHC_DV_ACTIVE;
+ ahc_linux_freeze_simq(ahc);
+
+ /* Wake up the DV kthread */
+ up(&ahc->platform_data->dv_sem);
+ }
+}
+
+static void
+ahc_linux_kill_dv_thread(struct ahc_softc *ahc)
+{
+ u_long s;
+
+ ahc_lock(ahc, &s);
+ if (ahc->platform_data->dv_pid != 0) {
+ ahc->platform_data->flags |= AHC_DV_SHUTDOWN;
+ ahc_unlock(ahc, &s);
+ up(&ahc->platform_data->dv_sem);
+
+ /*
+ * Use the eh_sem as an indicator that the
+ * dv thread is exiting. Note that the dv
+ * thread must still return after performing
+ * the up on our semaphore before it has
+ * completely exited this module. Unfortunately,
+ * there seems to be no easy way to wait for the
+ * exit of a thread for which you are not the
+ * parent (dv threads are parented by init).
+ * Cross your fingers...
+ */
+ down(&ahc->platform_data->eh_sem);
+
+ /*
+ * Mark the dv thread as already dead. This
+ * avoids attempting to kill it a second time.
+ * This is necessary because we must kill the
+ * DV thread before calling ahc_free() in the
+ * module shutdown case to avoid bogus locking
+ * in the SCSI mid-layer, but we ahc_free() is
+ * called without killing the DV thread in the
+ * instance detach case, so ahc_platform_free()
+ * calls us again to verify that the DV thread
+ * is dead.
+ */
+ ahc->platform_data->dv_pid = 0;
+ } else {
+ ahc_unlock(ahc, &s);
+ }
+}
+
+static int
+ahc_linux_dv_thread(void *data)
+{
+ struct ahc_softc *ahc;
+ int target;
+ u_long s;
+
+ ahc = (struct ahc_softc *)data;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("Launching DV Thread\n");
+#endif
+
+ /*
+ * Complete thread creation.
+ */
+ lock_kernel();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * Don't care about any signals.
+ */
+ siginitsetinv(&current->blocked, 0);
+
+ daemonize();
+ sprintf(current->comm, "ahc_dv_%d", ahc->unit);
+#else
+ daemonize("ahc_dv_%d", ahc->unit);
+ current->flags |= PF_FREEZE;
+#endif
+ unlock_kernel();
+
+ while (1) {
+ /*
+ * Use down_interruptible() rather than down() to
+ * avoid inclusion in the load average.
+ */
+ down_interruptible(&ahc->platform_data->dv_sem);
+
+ /* Check to see if we've been signaled to exit */
+ ahc_lock(ahc, &s);
+ if ((ahc->platform_data->flags & AHC_DV_SHUTDOWN) != 0) {
+ ahc_unlock(ahc, &s);
+ break;
+ }
+ ahc_unlock(ahc, &s);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s: Beginning Domain Validation\n",
+ ahc_name(ahc));
+#endif
+
+ /*
+ * Wait for any pending commands to drain before proceeding.
+ */
+ ahc_lock(ahc, &s);
+ while (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+ ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_EMPTY;
+ ahc_unlock(ahc, &s);
+ down_interruptible(&ahc->platform_data->dv_sem);
+ ahc_lock(ahc, &s);
+ }
+
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ while (AHC_DV_SIMQ_FROZEN(ahc) == 0) {
+ ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_RELEASE;
+ ahc_unlock(ahc, &s);
+ down_interruptible(&ahc->platform_data->dv_sem);
+ ahc_lock(ahc, &s);
+ }
+ ahc_unlock(ahc, &s);
+
+ for (target = 0; target < AHC_NUM_TARGETS; target++)
+ ahc_linux_dv_target(ahc, target);
+
+ ahc_lock(ahc, &s);
+ ahc->platform_data->flags &= ~AHC_DV_ACTIVE;
+ ahc_unlock(ahc, &s);
+
+ /*
+ * Release the SIMQ so that normal commands are
+ * allowed to continue on the bus.
+ */
+ ahc_linux_release_simq((u_long)ahc);
+ }
+ up(&ahc->platform_data->eh_sem);
+ return (0);
+}
+
+#define AHC_LINUX_DV_INQ_SHORT_LEN 36
+#define AHC_LINUX_DV_INQ_LEN 256
+#define AHC_LINUX_DV_TIMEOUT (HZ / 4)
+
+#define AHC_SET_DV_STATE(ahc, targ, newstate) \
+ ahc_set_dv_state(ahc, targ, newstate, __LINE__)
+
+static __inline void
+ahc_set_dv_state(struct ahc_softc *ahc, struct ahc_linux_target *targ,
+ ahc_dv_state newstate, u_int line)
+{
+ ahc_dv_state oldstate;
+
+ oldstate = targ->dv_state;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s:%d: Going from state %d to state %d\n",
+ ahc_name(ahc), line, oldstate, newstate);
+#endif
+
+ if (oldstate == newstate)
+ targ->dv_state_retry++;
+ else
+ targ->dv_state_retry = 0;
+ targ->dv_state = newstate;
+}
+
+static void
+ahc_linux_dv_target(struct ahc_softc *ahc, u_int target_offset)
+{
+ struct ahc_devinfo devinfo;
+ struct ahc_linux_target *targ;
+ struct scsi_cmnd *cmd;
+ struct scsi_device *scsi_dev;
+ struct scsi_sense_data *sense;
+ uint8_t *buffer;
+ u_long s;
+ u_int timeout;
+ int echo_size;
+
+ sense = NULL;
+ buffer = NULL;
+ echo_size = 0;
+ ahc_lock(ahc, &s);
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL || (targ->flags & AHC_DV_REQUIRED) == 0) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ ahc_compile_devinfo(&devinfo,
+ targ->channel == 0 ? ahc->our_id : ahc->our_id_b,
+ targ->target, /*lun*/0, targ->channel + 'A',
+ ROLE_INITIATOR);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Performing DV\n");
+ }
+#endif
+
+ ahc_unlock(ahc, &s);
+
+ cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK);
+ scsi_dev = malloc(sizeof(struct scsi_device), M_DEVBUF, M_WAITOK);
+ scsi_dev->host = ahc->platform_data->host;
+ scsi_dev->id = devinfo.target;
+ scsi_dev->lun = devinfo.lun;
+ scsi_dev->channel = devinfo.channel - 'A';
+ ahc->platform_data->dv_scsi_dev = scsi_dev;
+
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_INQ_SHORT_ASYNC);
+
+ while (targ->dv_state != AHC_DV_STATE_EXIT) {
+ timeout = AHC_LINUX_DV_TIMEOUT;
+ switch (targ->dv_state) {
+ case AHC_DV_STATE_INQ_SHORT_ASYNC:
+ case AHC_DV_STATE_INQ_ASYNC:
+ case AHC_DV_STATE_INQ_ASYNC_VERIFY:
+ /*
+ * Set things to async narrow to reduce the
+ * chance that the INQ will fail.
+ */
+ ahc_lock(ahc, &s);
+ ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_unlock(ahc, &s);
+ timeout = 10 * HZ;
+ targ->flags &= ~AHC_INQ_VALID;
+ /* FALLTHROUGH */
+ case AHC_DV_STATE_INQ_VERIFY:
+ {
+ u_int inq_len;
+
+ if (targ->dv_state == AHC_DV_STATE_INQ_SHORT_ASYNC)
+ inq_len = AHC_LINUX_DV_INQ_SHORT_LEN;
+ else
+ inq_len = targ->inq_data->additional_length + 5;
+ ahc_linux_dv_inq(ahc, cmd, &devinfo, targ, inq_len);
+ break;
+ }
+ case AHC_DV_STATE_TUR:
+ case AHC_DV_STATE_BUSY:
+ timeout = 5 * HZ;
+ ahc_linux_dv_tur(ahc, cmd, &devinfo);
+ break;
+ case AHC_DV_STATE_REBD:
+ ahc_linux_dv_rebd(ahc, cmd, &devinfo, targ);
+ break;
+ case AHC_DV_STATE_WEB:
+ ahc_linux_dv_web(ahc, cmd, &devinfo, targ);
+ break;
+
+ case AHC_DV_STATE_REB:
+ ahc_linux_dv_reb(ahc, cmd, &devinfo, targ);
+ break;
+
+ case AHC_DV_STATE_SU:
+ ahc_linux_dv_su(ahc, cmd, &devinfo, targ);
+ timeout = 50 * HZ;
+ break;
+
+ default:
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Unknown DV state %d\n", targ->dv_state);
+ goto out;
+ }
+
+ /* Queue the command and wait for it to complete */
+ /* Abuse eh_timeout in the scsi_cmnd struct for our purposes */
+ init_timer(&cmd->eh_timeout);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ /*
+ * All of the printfs during negotiation
+ * really slow down the negotiation.
+ * Add a bit of time just to be safe.
+ */
+ timeout += HZ;
+#endif
+ scsi_add_timer(cmd, timeout, ahc_linux_dv_timeout);
+ /*
+ * In 2.5.X, it is assumed that all calls from the
+ * "midlayer" (which we are emulating) will have the
+ * ahc host lock held. For other kernels, the
+ * io_request_lock must be held.
+ */
+#if AHC_SCSI_HAS_HOST_LOCK != 0
+ ahc_lock(ahc, &s);
+#else
+ spin_lock_irqsave(&io_request_lock, s);
+#endif
+ ahc_linux_queue(cmd, ahc_linux_dv_complete);
+#if AHC_SCSI_HAS_HOST_LOCK != 0
+ ahc_unlock(ahc, &s);
+#else
+ spin_unlock_irqrestore(&io_request_lock, s);
+#endif
+ down_interruptible(&ahc->platform_data->dv_cmd_sem);
+ /*
+ * Wait for the SIMQ to be released so that DV is the
+ * only reason the queue is frozen.
+ */
+ ahc_lock(ahc, &s);
+ while (AHC_DV_SIMQ_FROZEN(ahc) == 0) {
+ ahc->platform_data->flags |= AHC_DV_WAIT_SIMQ_RELEASE;
+ ahc_unlock(ahc, &s);
+ down_interruptible(&ahc->platform_data->dv_sem);
+ ahc_lock(ahc, &s);
+ }
+ ahc_unlock(ahc, &s);
+
+ ahc_linux_dv_transition(ahc, cmd, &devinfo, targ);
+ }
+
+out:
+ if ((targ->flags & AHC_INQ_VALID) != 0
+ && ahc_linux_get_device(ahc, devinfo.channel - 'A',
+ devinfo.target, devinfo.lun,
+ /*alloc*/FALSE) == NULL) {
+ /*
+ * The DV state machine failed to configure this device.
+ * This is normal if DV is disabled. Since we have inquiry
+ * data, filter it and use the "optimistic" negotiation
+ * parameters found in the inquiry string.
+ */
+ ahc_linux_filter_inquiry(ahc, &devinfo);
+ if ((targ->flags & (AHC_BASIC_DV|AHC_ENHANCED_DV)) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("DV failed to configure device. "
+ "Please file a bug report against "
+ "this driver.\n");
+ }
+ }
+
+ if (cmd != NULL)
+ free(cmd, M_DEVBUF);
+
+ if (ahc->platform_data->dv_scsi_dev != NULL) {
+ free(ahc->platform_data->dv_scsi_dev, M_DEVBUF);
+ ahc->platform_data->dv_scsi_dev = NULL;
+ }
+
+ ahc_lock(ahc, &s);
+ if (targ->dv_buffer != NULL) {
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = NULL;
+ }
+ if (targ->dv_buffer1 != NULL) {
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = NULL;
+ }
+ targ->flags &= ~AHC_DV_REQUIRED;
+ if (targ->refcount == 0)
+ ahc_linux_free_target(ahc, targ);
+ ahc_unlock(ahc, &s);
+}
+
+static void
+ahc_linux_dv_transition(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ)
+{
+ u_int32_t status;
+
+ status = aic_error_action(cmd, targ->inq_data,
+ ahc_cmd_get_transaction_status(cmd),
+ ahc_cmd_get_scsi_status(cmd));
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Entering ahc_linux_dv_transition, state= %d, "
+ "status= 0x%x, cmd->result= 0x%x\n", targ->dv_state,
+ status, cmd->result);
+ }
+#endif
+
+ switch (targ->dv_state) {
+ case AHC_DV_STATE_INQ_SHORT_ASYNC:
+ case AHC_DV_STATE_INQ_ASYNC:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state+1);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHC_DV_STATE_INQ_ASYNC_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ u_int xportflags;
+ u_int spi3data;
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHC_LINUX_DV_INQ_LEN) != 0) {
+ /*
+ * Inquiry data must have changed.
+ * Try from the top again.
+ */
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ }
+
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state+1);
+ targ->flags |= AHC_INQ_VALID;
+ if (ahc_linux_user_dv_setting(ahc) == 0)
+ break;
+
+ xportflags = targ->inq_data->flags;
+ if ((xportflags & (SID_Sync|SID_WBus16)) == 0)
+ break;
+
+ spi3data = targ->inq_data->spi3data;
+ switch (spi3data & SID_SPI_CLOCK_DT_ST) {
+ default:
+ case SID_SPI_CLOCK_ST:
+ /* Assume only basic DV is supported. */
+ targ->flags |= AHC_BASIC_DV;
+ break;
+ case SID_SPI_CLOCK_DT:
+ case SID_SPI_CLOCK_DT_ST:
+ targ->flags |= AHC_ENHANCED_DV;
+ break;
+ }
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+
+ if ((status & SS_ERRMASK) == EBUSY)
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+ case AHC_DV_STATE_INQ_VERIFY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+
+ if (memcmp(targ->inq_data, targ->dv_buffer,
+ AHC_LINUX_DV_INQ_LEN) == 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ int i;
+
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Inquiry buffer mismatch:");
+ for (i = 0; i < AHC_LINUX_DV_INQ_LEN; i++) {
+ if ((i & 0xF) == 0)
+ printf("\n ");
+ printf("0x%x:0x0%x ",
+ ((uint8_t *)targ->inq_data)[i],
+ targ->dv_buffer[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ } else if ((status & SS_ERRMASK) == EBUSY)
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ if (targ->dv_state_retry < 10)
+ break;
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Failed DV inquiry, skipping\n");
+ }
+#endif
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_TUR:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ if ((targ->flags & AHC_BASIC_DV) != 0) {
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_VERIFY);
+ } else if ((targ->flags & AHC_ENHANCED_DV) != 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REBD);
+ } else {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ }
+ break;
+ case SS_RETRY:
+ case SS_TUR:
+ if ((status & SS_ERRMASK) == EBUSY) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_BUSY);
+ break;
+ }
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry >= 10) {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV TUR reties exhausted\n");
+ }
+#endif
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ if (status & SSQ_DELAY)
+ ssleep(1);
+
+ break;
+ case SS_START:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_SU);
+ break;
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_REBD:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ {
+ uint32_t echo_size;
+
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_WEB);
+ echo_size = scsi_3btoul(&targ->dv_buffer[1]);
+ echo_size &= 0x1FFF;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Echo buffer size= %d\n", echo_size);
+ }
+#endif
+ if (echo_size == 0) {
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+
+ /* Generate the buffer pattern */
+ targ->dv_echo_size = echo_size;
+ ahc_linux_generate_dv_pattern(targ);
+ /*
+ * Setup initial negotiation values.
+ */
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ break;
+ }
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ)
+ targ->dv_state_retry--;
+ if (targ->dv_state_retry <= 10)
+ break;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV REBD reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ case SS_FATAL:
+ default:
+ /*
+ * Setup initial negotiation values
+ * and try level 1 DV.
+ */
+ ahc_linux_filter_inquiry(ahc, devinfo);
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_INQ_VERIFY);
+ targ->dv_echo_size = 0;
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_WEB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_REB);
+ break;
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ /*
+ * Do not count "falling back"
+ * against our retries.
+ */
+ targ->dv_state_retry = 0;
+ }
+ if (targ->dv_state_retry <= 10)
+ break;
+ /* FALLTHROUGH */
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV WEB reties exhausted\n");
+ }
+#endif
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_REB:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ if (memcmp(targ->dv_buffer, targ->dv_buffer1,
+ targ->dv_echo_size) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0)
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ else
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_WEB);
+ break;
+ }
+
+ if (targ->dv_buffer != NULL) {
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = NULL;
+ }
+ if (targ->dv_buffer1 != NULL) {
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = NULL;
+ }
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if ((status & SSQ_FALLBACK) != 0) {
+ if (ahc_linux_fallback(ahc, devinfo) != 0) {
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_EXIT);
+ break;
+ }
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_WEB);
+ }
+ if (targ->dv_state_retry <= 10) {
+ if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0)
+ msleep(ahc->our_id*1000/10);
+ break;
+ }
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV REB reties exhausted\n");
+ }
+#endif
+ /* FALLTHROUGH */
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_SU:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ case AHC_DV_STATE_BUSY:
+ switch (status & SS_MASK) {
+ case SS_NOP:
+ case SS_INQ_REFRESH:
+ AHC_SET_DV_STATE(ahc, targ,
+ AHC_DV_STATE_INQ_SHORT_ASYNC);
+ break;
+ case SS_TUR:
+ case SS_RETRY:
+ AHC_SET_DV_STATE(ahc, targ, targ->dv_state);
+ if (ahc_cmd_get_transaction_status(cmd)
+ == CAM_REQUEUE_REQ) {
+ targ->dv_state_retry--;
+ } else if (targ->dv_state_retry < 60) {
+ if ((status & SSQ_DELAY) != 0)
+ ssleep(1);
+ } else {
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("DV BUSY reties exhausted\n");
+ }
+#endif
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ }
+ break;
+ default:
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+ break;
+
+ default:
+ printf("%s: Invalid DV completion state %d\n", ahc_name(ahc),
+ targ->dv_state);
+ AHC_SET_DV_STATE(ahc, targ, AHC_DV_STATE_EXIT);
+ break;
+ }
+}
+
+static void
+ahc_linux_dv_fill_cmd(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo)
+{
+ memset(cmd, 0, sizeof(struct scsi_cmnd));
+ cmd->device = ahc->platform_data->dv_scsi_dev;
+ cmd->scsi_done = ahc_linux_dv_complete;
+}
+
+/*
+ * Synthesize an inquiry command. On the return trip, it'll be
+ * sniffed and the device transfer settings set for us.
+ */
+static void
+ahc_linux_dv_inq(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ,
+ u_int request_length)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending INQ\n");
+ }
+#endif
+ if (targ->inq_data == NULL)
+ targ->inq_data = malloc(AHC_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ if (targ->dv_state > AHC_DV_STATE_INQ_ASYNC) {
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHC_LINUX_DV_INQ_LEN,
+ M_DEVBUF, M_WAITOK);
+ }
+
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = INQUIRY;
+ cmd->cmnd[4] = request_length;
+ cmd->request_bufflen = request_length;
+ if (targ->dv_state > AHC_DV_STATE_INQ_ASYNC)
+ cmd->request_buffer = targ->dv_buffer;
+ else
+ cmd->request_buffer = targ->inq_data;
+ memset(cmd->request_buffer, 0, AHC_LINUX_DV_INQ_LEN);
+}
+
+static void
+ahc_linux_dv_tur(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending TUR\n");
+ }
+#endif
+ /* Do a TUR to clear out any non-fatal transitional state */
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = TEST_UNIT_READY;
+}
+
+#define AHC_REBD_LEN 4
+
+static void
+ahc_linux_dv_rebd(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending REBD\n");
+ }
+#endif
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(AHC_REBD_LEN, M_DEVBUF, M_WAITOK);
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0b;
+ scsi_ulto3b(AHC_REBD_LEN, &cmd->cmnd[6]);
+ cmd->request_bufflen = AHC_REBD_LEN;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahc_linux_dv_web(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending WEB\n");
+ }
+#endif
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_WRITE;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = WRITE_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer;
+}
+
+static void
+ahc_linux_dv_reb(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo, struct ahc_linux_target *targ)
+{
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending REB\n");
+ }
+#endif
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_READ;
+ cmd->cmd_len = 10;
+ cmd->cmnd[0] = READ_BUFFER;
+ cmd->cmnd[1] = 0x0a;
+ scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]);
+ cmd->request_bufflen = targ->dv_echo_size;
+ cmd->underflow = cmd->request_bufflen;
+ cmd->request_buffer = targ->dv_buffer1;
+}
+
+static void
+ahc_linux_dv_su(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+ struct ahc_devinfo *devinfo,
+ struct ahc_linux_target *targ)
+{
+ u_int le;
+
+ le = SID_IS_REMOVABLE(targ->inq_data) ? SSS_LOEJ : 0;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Sending SU\n");
+ }
+#endif
+ ahc_linux_dv_fill_cmd(ahc, cmd, devinfo);
+ cmd->sc_data_direction = SCSI_DATA_NONE;
+ cmd->cmd_len = 6;
+ cmd->cmnd[0] = START_STOP_UNIT;
+ cmd->cmnd[4] = le | SSS_START;
+}
+
+static int
+ahc_linux_fallback(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_transinfo *goal;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *syncrate;
+ u_long s;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int cur_speed;
+ u_int wide_speed;
+ u_int narrow_speed;
+ u_int fallback_speed;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Trying to fallback\n");
+ }
+#endif
+ ahc_lock(ahc, &s);
+ targ = ahc->platform_data->targets[devinfo->target_offset];
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ goal = &tinfo->goal;
+ width = goal->width;
+ period = goal->period;
+ offset = goal->offset;
+ ppr_options = goal->ppr_options;
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
+ if (targ->dv_next_narrow_period == 0)
+ targ->dv_next_narrow_period = MAX(period, AHC_SYNCRATE_ULTRA2);
+ if (targ->dv_next_wide_period == 0)
+ targ->dv_next_wide_period = period;
+ if (targ->dv_max_width == 0)
+ targ->dv_max_width = width;
+ if (targ->dv_max_ppr_options == 0)
+ targ->dv_max_ppr_options = ppr_options;
+ if (targ->dv_last_ppr_options == 0)
+ targ->dv_last_ppr_options = ppr_options;
+
+ cur_speed = aic_calc_speed(width, period, offset, AHC_SYNCRATE_MIN);
+ wide_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_16_BIT,
+ targ->dv_next_wide_period,
+ MAX_OFFSET,
+ AHC_SYNCRATE_MIN);
+ narrow_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_8_BIT,
+ targ->dv_next_narrow_period,
+ MAX_OFFSET,
+ AHC_SYNCRATE_MIN);
+ fallback_speed = aic_calc_speed(width, period+1, offset,
+ AHC_SYNCRATE_MIN);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ printf("cur_speed= %d, wide_speed= %d, narrow_speed= %d, "
+ "fallback_speed= %d\n", cur_speed, wide_speed,
+ narrow_speed, fallback_speed);
+ }
+#endif
+
+ if (cur_speed > 160000) {
+ /*
+ * Paced/DT/IU_REQ only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ } else if (cur_speed > 80000) {
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+ /*
+ * Try without IU_REQ as it may be confusing
+ * an expander.
+ */
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else {
+ /*
+ * Paced/DT only transfer speeds. All we
+ * can do is fallback in terms of syncrate.
+ */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else if (cur_speed > 3300) {
+
+ /*
+ * In this range we the following
+ * options ordered from highest to
+ * lowest desireability:
+ *
+ * o Wide/DT
+ * o Wide/non-DT
+ * o Narrow at a potentally higher sync rate.
+ *
+ * All modes are tested with and without IU_REQ
+ * set since using IUs may confuse an expander.
+ */
+ if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+
+ ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+ } else if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+ /*
+ * Try going non-DT.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ } else if (targ->dv_last_ppr_options != 0) {
+ /*
+ * Try without QAS or any other PPR options.
+ * We may need a non-PPR message to work with
+ * an expander. We look at the "last PPR options"
+ * so we will perform this fallback even if the
+ * target responded to our PPR negotiation with
+ * no option bits set.
+ */
+ ppr_options = 0;
+ } else if (width == MSG_EXT_WDTR_BUS_16_BIT) {
+ /*
+ * If the next narrow speed is greater than
+ * the next wide speed, fallback to narrow.
+ * Otherwise fallback to the next DT/Wide setting.
+ * The narrow async speed will always be smaller
+ * than the wide async speed, so handle this case
+ * specifically.
+ */
+ ppr_options = targ->dv_max_ppr_options;
+ if (narrow_speed > fallback_speed
+ || period >= AHC_ASYNC_XFER_PERIOD) {
+ targ->dv_next_wide_period = period+1;
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ period = targ->dv_next_narrow_period;
+ } else {
+ period++;
+ }
+ } else if ((ahc->features & AHC_WIDE) != 0
+ && targ->dv_max_width != 0
+ && wide_speed >= fallback_speed
+ && (targ->dv_next_wide_period <= AHC_ASYNC_XFER_PERIOD
+ || period >= AHC_ASYNC_XFER_PERIOD)) {
+
+ /*
+ * We are narrow. Try falling back
+ * to the next wide speed with
+ * all supported ppr options set.
+ */
+ targ->dv_next_narrow_period = period+1;
+ width = MSG_EXT_WDTR_BUS_16_BIT;
+ period = targ->dv_next_wide_period;
+ ppr_options = targ->dv_max_ppr_options;
+ } else {
+ /* Only narrow fallback is allowed. */
+ period++;
+ ppr_options = targ->dv_max_ppr_options;
+ }
+ } else {
+ ahc_unlock(ahc, &s);
+ return (-1);
+ }
+ offset = MAX_OFFSET;
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
+ AHC_SYNCRATE_DT);
+ ahc_set_width(ahc, devinfo, width, AHC_TRANS_GOAL, FALSE);
+ if (period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ if (width == MSG_EXT_WDTR_BUS_8_BIT)
+ targ->dv_next_narrow_period = AHC_ASYNC_XFER_PERIOD;
+ else
+ targ->dv_next_wide_period = AHC_ASYNC_XFER_PERIOD;
+ }
+ ahc_set_syncrate(ahc, devinfo, syncrate, period, offset,
+ ppr_options, AHC_TRANS_GOAL, FALSE);
+ targ->dv_last_ppr_options = ppr_options;
+ ahc_unlock(ahc, &s);
+ return (0);
+}
+
+static void
+ahc_linux_dv_timeout(struct scsi_cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+ struct scb *scb;
+ u_long flags;
+
+ ahc = *((struct ahc_softc **)cmd->device->host->hostdata);
+ ahc_lock(ahc, &flags);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV) {
+ printf("%s: Timeout while doing DV command %x.\n",
+ ahc_name(ahc), cmd->cmnd[0]);
+ ahc_dump_card_state(ahc);
+ }
+#endif
+
+ /*
+ * Guard against "done race". No action is
+ * required if we just completed.
+ */
+ if ((scb = (struct scb *)cmd->host_scribble) == NULL) {
+ ahc_unlock(ahc, &flags);
+ return;
+ }
+
+ /*
+ * Command has not completed. Mark this
+ * SCB as having failing status prior to
+ * resetting the bus, so we get the correct
+ * error code.
+ */
+ if ((scb->flags & SCB_SENSE) != 0)
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ else
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ ahc_reset_channel(ahc, cmd->device->channel + 'A', /*initiate*/TRUE);
+
+ /*
+ * Add a minimal bus settle delay for devices that are slow to
+ * respond after bus resets.
+ */
+ ahc_linux_freeze_simq(ahc);
+ init_timer(&ahc->platform_data->reset_timer);
+ ahc->platform_data->reset_timer.data = (u_long)ahc;
+ ahc->platform_data->reset_timer.expires = jiffies + HZ / 2;
+ ahc->platform_data->reset_timer.function =
+ (ahc_linux_callback_t *)ahc_linux_release_simq;
+ add_timer(&ahc->platform_data->reset_timer);
+ if (ahc_linux_next_device_to_run(ahc) != NULL)
+ ahc_schedule_runq(ahc);
+ ahc_linux_run_complete_queue(ahc);
+ ahc_unlock(ahc, &flags);
+}
+
+static void
+ahc_linux_dv_complete(struct scsi_cmnd *cmd)
+{
+ struct ahc_softc *ahc;
+
+ ahc = *((struct ahc_softc **)cmd->device->host->hostdata);
+
+ /* Delete the DV timer before it goes off! */
+ scsi_delete_timer(cmd);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_DV)
+ printf("%s:%d:%d: Command completed, status= 0x%x\n",
+ ahc_name(ahc), cmd->device->channel,
+ cmd->device->id, cmd->result);
+#endif
+
+ /* Wake up the state machine */
+ up(&ahc->platform_data->dv_cmd_sem);
+}
+
+static void
+ahc_linux_generate_dv_pattern(struct ahc_linux_target *targ)
+{
+ uint16_t b;
+ u_int i;
+ u_int j;
+
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ targ->dv_buffer = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ targ->dv_buffer1 = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK);
+
+ i = 0;
+ b = 0x0001;
+ for (j = 0 ; i < targ->dv_echo_size; j++) {
+ if (j < 32) {
+ /*
+ * 32bytes of sequential numbers.
+ */
+ targ->dv_buffer[i++] = j & 0xff;
+ } else if (j < 48) {
+ /*
+ * 32bytes of repeating 0x0000, 0xffff.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xff : 0x00;
+ } else if (j < 64) {
+ /*
+ * 32bytes of repeating 0x5555, 0xaaaa.
+ */
+ targ->dv_buffer[i++] = (j & 0x02) ? 0xaa : 0x55;
+ } else {
+ /*
+ * Remaining buffer is filled with a repeating
+ * patter of:
+ *
+ * 0xffff
+ * ~0x0001 << shifted once in each loop.
+ */
+ if (j & 0x02) {
+ if (j & 0x01) {
+ targ->dv_buffer[i++] = ~(b >> 8) & 0xff;
+ b <<= 1;
+ if (b == 0x0000)
+ b = 0x0001;
+ } else {
+ targ->dv_buffer[i++] = (~b & 0xff);
+ }
+ } else {
+ targ->dv_buffer[i++] = 0xff;
+ }
+ }
+ }
+}
+
+static u_int
+ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ static int warned_user;
+ u_int tags;
+
+ tags = 0;
+ if ((ahc->user_discenable & devinfo->target_mask) != 0) {
+ if (ahc->unit >= NUM_ELEMENTS(aic7xxx_tag_info)) {
+ if (warned_user == 0) {
+
+ printf(KERN_WARNING
+"aic7xxx: WARNING: Insufficient tag_info instances\n"
+"aic7xxx: for installed controllers. Using defaults\n"
+"aic7xxx: Please update the aic7xxx_tag_info array in\n"
+"aic7xxx: the aic7xxx_osm..c source file.\n");
+ warned_user++;
+ }
+ tags = AHC_MAX_QUEUE;
+ } else {
+ adapter_tag_info_t *tag_info;
+
+ tag_info = &aic7xxx_tag_info[ahc->unit];
+ tags = tag_info->tag_commands[devinfo->target_offset];
+ if (tags > AHC_MAX_QUEUE)
+ tags = AHC_MAX_QUEUE;
+ }
+ }
+ return (tags);
+}
+
+static u_int
+ahc_linux_user_dv_setting(struct ahc_softc *ahc)
+{
+ static int warned_user;
+ int dv;
+
+ if (ahc->unit >= NUM_ELEMENTS(aic7xxx_dv_settings)) {
+ if (warned_user == 0) {
+
+ printf(KERN_WARNING
+"aic7xxx: WARNING: Insufficient dv settings instances\n"
+"aic7xxx: for installed controllers. Using defaults\n"
+"aic7xxx: Please update the aic7xxx_dv_settings array\n"
+"aic7xxx: in the aic7xxx_osm.c source file.\n");
+ warned_user++;
+ }
+ dv = -1;
+ } else {
+
+ dv = aic7xxx_dv_settings[ahc->unit];
+ }
+
+ if (dv < 0) {
+ u_long s;
+
+ /*
+ * Apply the default.
+ */
+ /*
+ * XXX - Enable DV on non-U160 controllers once it
+ * has been tested there.
+ */
+ ahc_lock(ahc, &s);
+ dv = (ahc->features & AHC_DT);
+ if (ahc->seep_config != 0
+ && ahc->seep_config->signature >= CFSIGNATURE2)
+ dv = (ahc->seep_config->adapter_control & CFENABLEDV);
+ ahc_unlock(ahc, &s);
+ }
+ return (dv);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahc_linux_device_queue_depth(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev)
+{
+ struct ahc_devinfo devinfo;
+ u_int tags;
+
+ ahc_compile_devinfo(&devinfo,
+ dev->target->channel == 0
+ ? ahc->our_id : ahc->our_id_b,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+ tags = ahc_linux_user_tagdepth(ahc, &devinfo);
+ if (tags != 0
+ && dev->scsi_device != NULL
+ && dev->scsi_device->tagged_supported != 0) {
+
+ ahc_set_tags(ahc, &devinfo, AHC_QUEUE_TAGGED);
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Tagged Queuing enabled. Depth %d\n", tags);
+ } else {
+ ahc_set_tags(ahc, &devinfo, AHC_QUEUE_NONE);
+ }
+}
+
+static void
+ahc_linux_run_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+ struct ahc_cmd *acmd;
+ struct scsi_cmnd *cmd;
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ uint16_t mask;
+
+ if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
+ panic("running device on run list");
+
+ while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+ && dev->openings > 0 && dev->qfrozen == 0) {
+
+ /*
+ * Schedule us to run later. The only reason we are not
+ * running is because the whole controller Q is frozen.
+ */
+ if (ahc->platform_data->qfrozen != 0
+ && AHC_DV_SIMQ_FROZEN(ahc) == 0) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ return;
+ }
+ /*
+ * Get an scb to use.
+ */
+ if ((scb = ahc_get_scb(ahc)) == NULL) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+ dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ ahc->flags |= AHC_RESOURCE_SHORTAGE;
+ return;
+ }
+ TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+ cmd = &acmd_scsi_cmd(acmd);
+ scb->io_ctx = cmd;
+ scb->platform_data->dev = dev;
+ hscb = scb->hscb;
+ cmd->host_scribble = (char *)scb;
+
+ /*
+ * Fill out basics of the HSCB.
+ */
+ hscb->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahc, cmd);
+ hscb->lun = cmd->device->lun;
+ mask = SCB_GET_TARGET_MASK(ahc, scb);
+ tinfo = ahc_fetch_transinfo(ahc, SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_OUR_ID(scb),
+ SCB_GET_TARGET(ahc, scb), &tstate);
+ hscb->scsirate = tinfo->scsirate;
+ hscb->scsioffset = tinfo->curr.offset;
+ if ((tstate->ultraenb & mask) != 0)
+ hscb->control |= ULTRAENB;
+
+ if ((ahc->user_discenable & mask) != 0)
+ hscb->control |= DISCENB;
+
+ if (AHC_DV_CMD(cmd) != 0)
+ scb->flags |= SCB_SILENT;
+
+ if ((tstate->auto_negotiate & mask) != 0) {
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
+ }
+
+ if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ int msg_bytes;
+ uint8_t tag_msgs[2];
+
+ msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
+ if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
+ hscb->control |= tag_msgs[0];
+ if (tag_msgs[0] == MSG_ORDERED_TASK)
+ dev->commands_since_idle_or_otag = 0;
+ } else
+#endif
+ if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
+ && (dev->flags & AHC_DEV_Q_TAGGED) != 0) {
+ hscb->control |= MSG_ORDERED_TASK;
+ dev->commands_since_idle_or_otag = 0;
+ } else {
+ hscb->control |= MSG_SIMPLE_TASK;
+ }
+ }
+
+ hscb->cdb_len = cmd->cmd_len;
+ if (hscb->cdb_len <= 12) {
+ memcpy(hscb->shared_data.cdb, cmd->cmnd, hscb->cdb_len);
+ } else {
+ memcpy(hscb->cdb32, cmd->cmnd, hscb->cdb_len);
+ scb->flags |= SCB_CDB32_PTR;
+ }
+
+ scb->platform_data->xfer_len = 0;
+ ahc_set_residual(scb, 0);
+ ahc_set_sense_residual(scb, 0);
+ scb->sg_count = 0;
+ if (cmd->use_sg != 0) {
+ struct ahc_dma_seg *sg;
+ struct scatterlist *cur_seg;
+ struct scatterlist *end_seg;
+ int nseg;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ nseg = pci_map_sg(ahc->dev_softc, cur_seg, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ end_seg = cur_seg + nseg;
+ /* Copy the segments into the SG list. */
+ sg = scb->sg_list;
+ /*
+ * The sg_count may be larger than nseg if
+ * a transfer crosses a 32bit page.
+ */
+ while (cur_seg < end_seg) {
+ dma_addr_t addr;
+ bus_size_t len;
+ int consumed;
+
+ addr = sg_dma_address(cur_seg);
+ len = sg_dma_len(cur_seg);
+ consumed = ahc_linux_map_seg(ahc, scb,
+ sg, addr, len);
+ sg += consumed;
+ scb->sg_count += consumed;
+ cur_seg++;
+ }
+ sg--;
+ sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+ /*
+ * Reset the sg list pointer.
+ */
+ scb->hscb->sgptr =
+ ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+ /*
+ * Copy the first SG into the "current"
+ * data pointer area.
+ */
+ scb->hscb->dataptr = scb->sg_list->addr;
+ scb->hscb->datacnt = scb->sg_list->len;
+ } else if (cmd->request_bufflen != 0) {
+ struct ahc_dma_seg *sg;
+ dma_addr_t addr;
+
+ sg = scb->sg_list;
+ addr = pci_map_single(ahc->dev_softc,
+ cmd->request_buffer,
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ scb->platform_data->buf_busaddr = addr;
+ scb->sg_count = ahc_linux_map_seg(ahc, scb,
+ sg, addr,
+ cmd->request_bufflen);
+ sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+ /*
+ * Reset the sg list pointer.
+ */
+ scb->hscb->sgptr =
+ ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+ /*
+ * Copy the first SG into the "current"
+ * data pointer area.
+ */
+ scb->hscb->dataptr = sg->addr;
+ scb->hscb->datacnt = sg->len;
+ } else {
+ scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL);
+ scb->hscb->dataptr = 0;
+ scb->hscb->datacnt = 0;
+ scb->sg_count = 0;
+ }
+
+ ahc_sync_sglist(ahc, scb, BUS_DMASYNC_PREWRITE);
+ LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+ dev->openings--;
+ dev->active++;
+ dev->commands_issued++;
+ if ((dev->flags & AHC_DEV_PERIODIC_OTAG) != 0)
+ dev->commands_since_idle_or_otag++;
+
+ /*
+ * We only allow one untagged transaction
+ * per target in the initiator role unless
+ * we are storing a full busy target *lun*
+ * table in SCB space.
+ */
+ if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0
+ && (ahc->features & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+ int target_offset;
+
+ target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+ untagged_q = &(ahc->untagged_queues[target_offset]);
+ TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
+ scb->flags |= SCB_UNTAGGEDQ;
+ if (TAILQ_FIRST(untagged_q) != scb)
+ continue;
+ }
+ scb->flags |= SCB_ACTIVE;
+ ahc_queue_scb(ahc, scb);
+ }
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+irqreturn_t
+ahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct ahc_softc *ahc;
+ u_long flags;
+ int ours;
+
+ ahc = (struct ahc_softc *) dev_id;
+ ahc_lock(ahc, &flags);
+ ours = ahc_intr(ahc);
+ if (ahc_linux_next_device_to_run(ahc) != NULL)
+ ahc_schedule_runq(ahc);
+ ahc_linux_run_complete_queue(ahc);
+ ahc_unlock(ahc, &flags);
+ return IRQ_RETVAL(ours);
+}
+
+void
+ahc_platform_flushwork(struct ahc_softc *ahc)
+{
+
+ while (ahc_linux_run_complete_queue(ahc) != NULL)
+ ;
+}
+
+static struct ahc_linux_target*
+ahc_linux_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target)
+{
+ struct ahc_linux_target *targ;
+ u_int target_offset;
+
+ target_offset = target;
+ if (channel != 0)
+ target_offset += 8;
+
+ targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT);
+ if (targ == NULL)
+ return (NULL);
+ memset(targ, 0, sizeof(*targ));
+ targ->channel = channel;
+ targ->target = target;
+ targ->ahc = ahc;
+ targ->flags = AHC_DV_REQUIRED;
+ ahc->platform_data->targets[target_offset] = targ;
+ return (targ);
+}
+
+static void
+ahc_linux_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ)
+{
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_offset;
+ char channel;
+
+ /*
+ * Force a negotiation to async/narrow on any
+ * future command to this device unless a bus
+ * reset occurs between now and that command.
+ */
+ channel = 'A' + targ->channel;
+ our_id = ahc->our_id;
+ target_offset = targ->target;
+ if (targ->channel != 0) {
+ target_offset += 8;
+ our_id = ahc->our_id_b;
+ }
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ targ->target, &tstate);
+ ahc_compile_devinfo(&devinfo, our_id, targ->target, CAM_LUN_WILDCARD,
+ channel, ROLE_INITIATOR);
+ ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_update_neg_request(ahc, &devinfo, tstate, tinfo, AHC_NEG_ALWAYS);
+ ahc->platform_data->targets[target_offset] = NULL;
+ if (targ->inq_data != NULL)
+ free(targ->inq_data, M_DEVBUF);
+ if (targ->dv_buffer != NULL)
+ free(targ->dv_buffer, M_DEVBUF);
+ if (targ->dv_buffer1 != NULL)
+ free(targ->dv_buffer1, M_DEVBUF);
+ free(targ, M_DEVBUF);
+}
+
+static struct ahc_linux_device*
+ahc_linux_alloc_device(struct ahc_softc *ahc,
+ struct ahc_linux_target *targ, u_int lun)
+{
+ struct ahc_linux_device *dev;
+
+ dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+ if (dev == NULL)
+ return (NULL);
+ memset(dev, 0, sizeof(*dev));
+ init_timer(&dev->timer);
+ TAILQ_INIT(&dev->busyq);
+ dev->flags = AHC_DEV_UNCONFIGURED;
+ dev->lun = lun;
+ dev->target = targ;
+
+ /*
+ * We start out life using untagged
+ * transactions of which we allow one.
+ */
+ dev->openings = 1;
+
+ /*
+ * Set maxtags to 0. This will be changed if we
+ * later determine that we are dealing with
+ * a tagged queuing capable device.
+ */
+ dev->maxtags = 0;
+
+ targ->refcount++;
+ targ->devices[lun] = dev;
+ return (dev);
+}
+
+static void
+__ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+ struct ahc_linux_target *targ;
+
+ targ = dev->target;
+ targ->devices[dev->lun] = NULL;
+ free(dev, M_DEVBUF);
+ targ->refcount--;
+ if (targ->refcount == 0
+ && (targ->flags & AHC_DV_REQUIRED) == 0)
+ ahc_linux_free_target(ahc, targ);
+}
+
+static void
+ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+ del_timer_sync(&dev->timer);
+ __ahc_linux_free_device(ahc, dev);
+}
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel,
+ u_int target, u_int lun, ac_code code, void *arg)
+{
+ switch (code) {
+ case AC_TRANSFER_NEG:
+ {
+ char buf[80];
+ struct ahc_linux_target *targ;
+ struct info_str info;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ int target_offset;
+
+ info.buffer = buf;
+ info.length = sizeof(buf);
+ info.offset = 0;
+ info.pos = 0;
+ tinfo = ahc_fetch_transinfo(ahc, channel,
+ channel == 'A' ? ahc->our_id
+ : ahc->our_id_b,
+ target, &tstate);
+
+ /*
+ * Don't bother reporting results while
+ * negotiations are still pending.
+ */
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ /*
+ * Don't bother reporting results that
+ * are identical to those last reported.
+ */
+ target_offset = target;
+ if (channel == 'B')
+ target_offset += 8;
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL)
+ break;
+ if (tinfo->curr.period == targ->last_tinfo.period
+ && tinfo->curr.width == targ->last_tinfo.width
+ && tinfo->curr.offset == targ->last_tinfo.offset
+ && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options)
+ if (bootverbose == 0)
+ break;
+
+ targ->last_tinfo.period = tinfo->curr.period;
+ targ->last_tinfo.width = tinfo->curr.width;
+ targ->last_tinfo.offset = tinfo->curr.offset;
+ targ->last_tinfo.ppr_options = tinfo->curr.ppr_options;
+
+ printf("(%s:%c:", ahc_name(ahc), channel);
+ if (target == CAM_TARGET_WILDCARD)
+ printf("*): ");
+ else
+ printf("%d): ", target);
+ ahc_format_transinfo(&info, &tinfo->curr);
+ if (info.pos < info.length)
+ *info.buffer = '\0';
+ else
+ buf[info.length - 1] = '\0';
+ printf("%s", buf);
+ break;
+ }
+ case AC_SENT_BDR:
+ {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ WARN_ON(lun != CAM_LUN_WILDCARD);
+ scsi_report_device_reset(ahc->platform_data->host,
+ channel - 'A', target);
+#else
+ Scsi_Device *scsi_dev;
+
+ /*
+ * Find the SCSI device associated with this
+ * request and indicate that a UA is expected.
+ */
+ for (scsi_dev = ahc->platform_data->host->host_queue;
+ scsi_dev != NULL; scsi_dev = scsi_dev->next) {
+ if (channel - 'A' == scsi_dev->channel
+ && target == scsi_dev->id
+ && (lun == CAM_LUN_WILDCARD
+ || lun == scsi_dev->lun)) {
+ scsi_dev->was_reset = 1;
+ scsi_dev->expecting_cc_ua = 1;
+ }
+ }
+#endif
+ break;
+ }
+ case AC_BUS_RESET:
+ if (ahc->platform_data->host != NULL) {
+ scsi_report_bus_reset(ahc->platform_data->host,
+ channel - 'A');
+ }
+ break;
+ default:
+ panic("ahc_send_async: Unexpected async event");
+ }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahc_done(struct ahc_softc *ahc, struct scb *scb)
+{
+ Scsi_Cmnd *cmd;
+ struct ahc_linux_device *dev;
+
+ LIST_REMOVE(scb, pending_links);
+ if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+ struct scb_tailq *untagged_q;
+ int target_offset;
+
+ target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+ untagged_q = &(ahc->untagged_queues[target_offset]);
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ ahc_run_untagged_queue(ahc, untagged_q);
+ }
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ printf("SCB %d done'd twice\n", scb->hscb->tag);
+ ahc_dump_card_state(ahc);
+ panic("Stopping for safety");
+ }
+ cmd = scb->io_ctx;
+ dev = scb->platform_data->dev;
+ dev->active--;
+ dev->openings++;
+ if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+ cmd->result &= ~(CAM_DEV_QFRZN << 16);
+ dev->qfrozen--;
+ }
+ ahc_linux_unmap_scb(ahc, scb);
+
+ /*
+ * Guard against stale sense data.
+ * The Linux mid-layer assumes that sense
+ * was retrieved anytime the first byte of
+ * the sense buffer looks "sane".
+ */
+ cmd->sense_buffer[0] = 0;
+ if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+ uint32_t amount_xferred;
+
+ amount_xferred =
+ ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+ if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MISC) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Set CAM_UNCOR_PARITY\n");
+ }
+#endif
+ ahc_set_transaction_status(scb, CAM_UNCOR_PARITY);
+#ifdef AHC_REPORT_UNDERFLOWS
+ /*
+ * This code is disabled by default as some
+ * clients of the SCSI system do not properly
+ * initialize the underflow parameter. This
+ * results in spurious termination of commands
+ * that complete as expected (e.g. underflow is
+ * allowed as command can return variable amounts
+ * of data.
+ */
+ } else if (amount_xferred < scb->io_ctx->underflow) {
+ u_int i;
+
+ ahc_print_path(ahc, scb);
+ printf("CDB:");
+ for (i = 0; i < scb->io_ctx->cmd_len; i++)
+ printf(" 0x%x", scb->io_ctx->cmnd[i]);
+ printf("\n");
+ ahc_print_path(ahc, scb);
+ printf("Saw underflow (%ld of %ld bytes). "
+ "Treated as error\n",
+ ahc_get_residual(scb),
+ ahc_get_transfer_length(scb));
+ ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+#endif
+ } else {
+ ahc_set_transaction_status(scb, CAM_REQ_CMP);
+ }
+ } else if (ahc_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+ ahc_linux_handle_scsi_status(ahc, dev, scb);
+ } else if (ahc_get_transaction_status(scb) == CAM_SEL_TIMEOUT) {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ if (AHC_DV_CMD(cmd) == FALSE)
+ dev->target->flags &= ~AHC_DV_REQUIRED;
+ }
+ /*
+ * Start DV for devices that require it assuming the first command
+ * sent does not result in a selection timeout.
+ */
+ if (ahc_get_transaction_status(scb) != CAM_SEL_TIMEOUT
+ && (dev->target->flags & AHC_DV_REQUIRED) != 0)
+ ahc_linux_start_dv(ahc);
+
+ if (dev->openings == 1
+ && ahc_get_transaction_status(scb) == CAM_REQ_CMP
+ && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+ dev->tag_success_count++;
+ /*
+ * Some devices deal with temporary internal resource
+ * shortages by returning queue full. When the queue
+ * full occurrs, we throttle back. Slowly try to get
+ * back to our previous queue depth.
+ */
+ if ((dev->openings + dev->active) < dev->maxtags
+ && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) {
+ dev->tag_success_count = 0;
+ dev->openings++;
+ }
+
+ if (dev->active == 0)
+ dev->commands_since_idle_or_otag = 0;
+
+ if (TAILQ_EMPTY(&dev->busyq)) {
+ if ((dev->flags & AHC_DEV_UNCONFIGURED) != 0
+ && dev->active == 0
+ && (dev->flags & AHC_DEV_TIMER_ACTIVE) == 0)
+ ahc_linux_free_device(ahc, dev);
+ } else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+ TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+ dev->flags |= AHC_DEV_ON_RUN_LIST;
+ }
+
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ printf("Recovery SCB completes\n");
+ if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
+ || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ if ((ahc->platform_data->flags & AHC_UP_EH_SEMAPHORE) != 0) {
+ ahc->platform_data->flags &= ~AHC_UP_EH_SEMAPHORE;
+ up(&ahc->platform_data->eh_sem);
+ }
+ }
+
+ ahc_free_scb(ahc, scb);
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+
+ if ((ahc->platform_data->flags & AHC_DV_WAIT_SIMQ_EMPTY) != 0
+ && LIST_FIRST(&ahc->pending_scbs) == NULL) {
+ ahc->platform_data->flags &= ~AHC_DV_WAIT_SIMQ_EMPTY;
+ up(&ahc->platform_data->dv_sem);
+ }
+
+}
+
+static void
+ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
+ struct ahc_linux_device *dev, struct scb *scb)
+{
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo,
+ ahc->our_id,
+ dev->target->target, dev->lun,
+ dev->target->channel == 0 ? 'A' : 'B',
+ ROLE_INITIATOR);
+
+ /*
+ * We don't currently trust the mid-layer to
+ * properly deal with queue full or busy. So,
+ * when one occurs, we tell the mid-layer to
+ * unconditionally requeue the command to us
+ * so that we can retry it ourselves. We also
+ * implement our own throttling mechanism so
+ * we don't clobber the device with too many
+ * commands.
+ */
+ switch (ahc_get_scsi_status(scb)) {
+ default:
+ break;
+ case SCSI_STATUS_CHECK_COND:
+ case SCSI_STATUS_CMD_TERMINATED:
+ {
+ Scsi_Cmnd *cmd;
+
+ /*
+ * Copy sense information to the OS's cmd
+ * structure if it is available.
+ */
+ cmd = scb->io_ctx;
+ if (scb->flags & SCB_SENSE) {
+ u_int sense_size;
+
+ sense_size = MIN(sizeof(struct scsi_sense_data)
+ - ahc_get_sense_residual(scb),
+ sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer,
+ ahc_get_sense_buf(ahc, scb), sense_size);
+ if (sense_size < sizeof(cmd->sense_buffer))
+ memset(&cmd->sense_buffer[sense_size], 0,
+ sizeof(cmd->sense_buffer) - sense_size);
+ cmd->result |= (DRIVER_SENSE << 24);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_SENSE) {
+ int i;
+
+ printf("Copied %d bytes of sense data:",
+ sense_size);
+ for (i = 0; i < sense_size; i++) {
+ if ((i & 0xF) == 0)
+ printf("\n");
+ printf("0x%x ", cmd->sense_buffer[i]);
+ }
+ printf("\n");
+ }
+#endif
+ }
+ break;
+ }
+ case SCSI_STATUS_QUEUE_FULL:
+ {
+ /*
+ * By the time the core driver has returned this
+ * command, all other commands that were queued
+ * to us but not the device have been returned.
+ * This ensures that dev->active is equal to
+ * the number of commands actually queued to
+ * the device.
+ */
+ dev->tag_success_count = 0;
+ if (dev->active != 0) {
+ /*
+ * Drop our opening count to the number
+ * of commands currently outstanding.
+ */
+ dev->openings = 0;
+/*
+ ahc_print_path(ahc, scb);
+ printf("Dropping tag count to %d\n", dev->active);
+ */
+ if (dev->active == dev->tags_on_last_queuefull) {
+
+ dev->last_queuefull_same_count++;
+ /*
+ * If we repeatedly see a queue full
+ * at the same queue depth, this
+ * device has a fixed number of tag
+ * slots. Lock in this tag depth
+ * so we stop seeing queue fulls from
+ * this device.
+ */
+ if (dev->last_queuefull_same_count
+ == AHC_LOCK_TAGS_COUNT) {
+ dev->maxtags = dev->active;
+ ahc_print_path(ahc, scb);
+ printf("Locking max tag count at %d\n",
+ dev->active);
+ }
+ } else {
+ dev->tags_on_last_queuefull = dev->active;
+ dev->last_queuefull_same_count = 0;
+ }
+ ahc_set_transaction_status(scb, CAM_REQUEUE_REQ);
+ ahc_set_scsi_status(scb, SCSI_STATUS_OK);
+ ahc_platform_set_tags(ahc, &devinfo,
+ (dev->flags & AHC_DEV_Q_BASIC)
+ ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
+ break;
+ }
+ /*
+ * Drop down to a single opening, and treat this
+ * as if the target returned BUSY SCSI status.
+ */
+ dev->openings = 1;
+ ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);
+ ahc_platform_set_tags(ahc, &devinfo,
+ (dev->flags & AHC_DEV_Q_BASIC)
+ ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
+ /* FALLTHROUGH */
+ }
+ case SCSI_STATUS_BUSY:
+ {
+ /*
+ * Set a short timer to defer sending commands for
+ * a bit since Linux will not delay in this case.
+ */
+ if ((dev->flags & AHC_DEV_TIMER_ACTIVE) != 0) {
+ printf("%s:%c:%d: Device Timer still active during "
+ "busy processing\n", ahc_name(ahc),
+ dev->target->channel, dev->target->target);
+ break;
+ }
+ dev->flags |= AHC_DEV_TIMER_ACTIVE;
+ dev->qfrozen++;
+ init_timer(&dev->timer);
+ dev->timer.data = (u_long)dev;
+ dev->timer.expires = jiffies + (HZ/2);
+ dev->timer.function = ahc_linux_dev_timed_unfreeze;
+ add_timer(&dev->timer);
+ break;
+ }
+ }
+}
+
+static void
+ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+{
+ /*
+ * Typically, the complete queue has very few entries
+ * queued to it before the queue is emptied by
+ * ahc_linux_run_complete_queue, so sorting the entries
+ * by generation number should be inexpensive.
+ * We perform the sort so that commands that complete
+ * with an error are retuned in the order origionally
+ * queued to the controller so that any subsequent retries
+ * are performed in order. The underlying ahc routines do
+ * not guarantee the order that aborted commands will be
+ * returned to us.
+ */
+ struct ahc_completeq *completeq;
+ struct ahc_cmd *list_cmd;
+ struct ahc_cmd *acmd;
+
+ /*
+ * Map CAM error codes into Linux Error codes. We
+ * avoid the conversion so that the DV code has the
+ * full error information available when making
+ * state change decisions.
+ */
+ if (AHC_DV_CMD(cmd) == FALSE) {
+ u_int new_status;
+
+ switch (ahc_cmd_get_transaction_status(cmd)) {
+ case CAM_REQ_INPROG:
+ case CAM_REQ_CMP:
+ case CAM_SCSI_STATUS_ERROR:
+ new_status = DID_OK;
+ break;
+ case CAM_REQ_ABORTED:
+ new_status = DID_ABORT;
+ break;
+ case CAM_BUSY:
+ new_status = DID_BUS_BUSY;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ new_status = DID_BAD_TARGET;
+ break;
+ case CAM_SEL_TIMEOUT:
+ new_status = DID_NO_CONNECT;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ new_status = DID_RESET;
+ break;
+ case CAM_UNCOR_PARITY:
+ new_status = DID_PARITY;
+ break;
+ case CAM_CMD_TIMEOUT:
+ new_status = DID_TIME_OUT;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_REQ_CMP_ERR:
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_NO_HBA:
+ case CAM_DATA_RUN_ERR:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_SEQUENCE_FAIL:
+ case CAM_CCB_LEN_ERR:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TERMIO:
+ case CAM_UNREC_HBA_ERROR:
+ case CAM_REQ_TOO_BIG:
+ new_status = DID_ERROR;
+ break;
+ case CAM_REQUEUE_REQ:
+ /*
+ * If we want the request requeued, make sure there
+ * are sufficent retries. In the old scsi error code,
+ * we used to be able to specify a result code that
+ * bypassed the retry count. Now we must use this
+ * hack. We also "fake" a check condition with
+ * a sense code of ABORTED COMMAND. This seems to
+ * evoke a retry even if this command is being sent
+ * via the eh thread. Ick! Ick! Ick!
+ */
+ if (cmd->retries > 0)
+ cmd->retries--;
+ new_status = DID_OK;
+ ahc_cmd_set_scsi_status(cmd, SCSI_STATUS_CHECK_COND);
+ cmd->result |= (DRIVER_SENSE << 24);
+ memset(cmd->sense_buffer, 0,
+ sizeof(cmd->sense_buffer));
+ cmd->sense_buffer[0] = SSD_ERRCODE_VALID
+ | SSD_CURRENT_ERROR;
+ cmd->sense_buffer[2] = SSD_KEY_ABORTED_COMMAND;
+ break;
+ default:
+ /* We should never get here */
+ new_status = DID_ERROR;
+ break;
+ }
+
+ ahc_cmd_set_transaction_status(cmd, new_status);
+ }
+
+ completeq = &ahc->platform_data->completeq;
+ list_cmd = TAILQ_FIRST(completeq);
+ acmd = (struct ahc_cmd *)cmd;
+ while (list_cmd != NULL
+ && acmd_scsi_cmd(list_cmd).serial_number
+ < acmd_scsi_cmd(acmd).serial_number)
+ list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+ if (list_cmd != NULL)
+ TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+ else
+ TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static void
+ahc_linux_filter_inquiry(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ struct scsi_inquiry_data *sid;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_transinfo *user;
+ struct ahc_transinfo *goal;
+ struct ahc_transinfo *curr;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *syncrate;
+ struct ahc_linux_device *dev;
+ u_int maxsync;
+ u_int width;
+ u_int period;
+ u_int offset;
+ u_int ppr_options;
+ u_int trans_version;
+ u_int prot_version;
+
+ /*
+ * Determine if this lun actually exists. If so,
+ * hold on to its corresponding device structure.
+ * If not, make sure we release the device and
+ * don't bother processing the rest of this inquiry
+ * command.
+ */
+ dev = ahc_linux_get_device(ahc, devinfo->channel - 'A',
+ devinfo->target, devinfo->lun,
+ /*alloc*/TRUE);
+
+ sid = (struct scsi_inquiry_data *)dev->target->inq_data;
+ if (SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+ dev->flags &= ~AHC_DEV_UNCONFIGURED;
+ } else {
+ dev->flags |= AHC_DEV_UNCONFIGURED;
+ return;
+ }
+
+ /*
+ * Update our notion of this device's transfer
+ * negotiation capabilities.
+ */
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ user = &tinfo->user;
+ goal = &tinfo->goal;
+ curr = &tinfo->curr;
+ width = user->width;
+ period = user->period;
+ offset = user->offset;
+ ppr_options = user->ppr_options;
+ trans_version = user->transport_version;
+ prot_version = MIN(user->protocol_version, SID_ANSI_REV(sid));
+
+ /*
+ * Only attempt SPI3/4 once we've verified that
+ * the device claims to support SPI3/4 features.
+ */
+ if (prot_version < SCSI_REV_2)
+ trans_version = SID_ANSI_REV(sid);
+ else
+ trans_version = SCSI_REV_2;
+
+ if ((sid->flags & SID_WBus16) == 0)
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+ if ((sid->flags & SID_Sync) == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ if ((sid->spi3data & SID_SPI_QAS) == 0)
+ ppr_options &= ~MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+ ppr_options &= MSG_EXT_PPR_QAS_REQ;
+ if ((sid->spi3data & SID_SPI_IUS) == 0)
+ ppr_options &= (MSG_EXT_PPR_DT_REQ
+ | MSG_EXT_PPR_QAS_REQ);
+
+ if (prot_version > SCSI_REV_2
+ && ppr_options != 0)
+ trans_version = user->transport_version;
+
+ ahc_validate_width(ahc, /*tinfo limit*/NULL, &width, ROLE_UNKNOWN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ else if ((ahc->features & AHC_ULTRA) != 0)
+ maxsync = AHC_SYNCRATE_ULTRA;
+ else
+ maxsync = AHC_SYNCRATE_FAST;
+
+ syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, maxsync);
+ ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate,
+ &offset, width, ROLE_UNKNOWN);
+ if (offset == 0 || period == 0) {
+ period = 0;
+ offset = 0;
+ ppr_options = 0;
+ }
+ /* Apply our filtered user settings. */
+ curr->transport_version = trans_version;
+ curr->protocol_version = prot_version;
+ ahc_set_width(ahc, devinfo, width, AHC_TRANS_GOAL, /*paused*/FALSE);
+ ahc_set_syncrate(ahc, devinfo, syncrate, period,
+ offset, ppr_options, AHC_TRANS_GOAL,
+ /*paused*/FALSE);
+}
+
+static void
+ahc_linux_sem_timeout(u_long arg)
+{
+ struct ahc_softc *ahc;
+ u_long s;
+
+ ahc = (struct ahc_softc *)arg;
+
+ ahc_lock(ahc, &s);
+ if ((ahc->platform_data->flags & AHC_UP_EH_SEMAPHORE) != 0) {
+ ahc->platform_data->flags &= ~AHC_UP_EH_SEMAPHORE;
+ up(&ahc->platform_data->eh_sem);
+ }
+ ahc_unlock(ahc, &s);
+}
+
+static void
+ahc_linux_freeze_simq(struct ahc_softc *ahc)
+{
+ ahc->platform_data->qfrozen++;
+ if (ahc->platform_data->qfrozen == 1) {
+ scsi_block_requests(ahc->platform_data->host);
+
+ /* XXX What about Twin channels? */
+ ahc_platform_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ);
+ }
+}
+
+static void
+ahc_linux_release_simq(u_long arg)
+{
+ struct ahc_softc *ahc;
+ u_long s;
+ int unblock_reqs;
+
+ ahc = (struct ahc_softc *)arg;
+
+ unblock_reqs = 0;
+ ahc_lock(ahc, &s);
+ if (ahc->platform_data->qfrozen > 0)
+ ahc->platform_data->qfrozen--;
+ if (ahc->platform_data->qfrozen == 0)
+ unblock_reqs = 1;
+ if (AHC_DV_SIMQ_FROZEN(ahc)
+ && ((ahc->platform_data->flags & AHC_DV_WAIT_SIMQ_RELEASE) != 0)) {
+ ahc->platform_data->flags &= ~AHC_DV_WAIT_SIMQ_RELEASE;
+ up(&ahc->platform_data->dv_sem);
+ }
+ ahc_schedule_runq(ahc);
+ ahc_unlock(ahc, &s);
+ /*
+ * There is still a race here. The mid-layer
+ * should keep its own freeze count and use
+ * a bottom half handler to run the queues
+ * so we can unblock with our own lock held.
+ */
+ if (unblock_reqs)
+ scsi_unblock_requests(ahc->platform_data->host);
+}
+
+static void
+ahc_linux_dev_timed_unfreeze(u_long arg)
+{
+ struct ahc_linux_device *dev;
+ struct ahc_softc *ahc;
+ u_long s;
+
+ dev = (struct ahc_linux_device *)arg;
+ ahc = dev->target->ahc;
+ ahc_lock(ahc, &s);
+ dev->flags &= ~AHC_DEV_TIMER_ACTIVE;
+ if (dev->qfrozen > 0)
+ dev->qfrozen--;
+ if (dev->qfrozen == 0
+ && (dev->flags & AHC_DEV_ON_RUN_LIST) == 0)
+ ahc_linux_run_device_queue(ahc, dev);
+ if (TAILQ_EMPTY(&dev->busyq)
+ && dev->active == 0)
+ __ahc_linux_free_device(ahc, dev);
+ ahc_unlock(ahc, &s);
+}
+
+static int
+ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
+{
+ struct ahc_softc *ahc;
+ struct ahc_cmd *acmd;
+ struct ahc_cmd *list_acmd;
+ struct ahc_linux_device *dev;
+ struct scb *pending_scb;
+ u_long s;
+ u_int saved_scbptr;
+ u_int active_scb_index;
+ u_int last_phase;
+ u_int saved_scsiid;
+ u_int cdb_byte;
+ int retval;
+ int was_paused;
+ int paused;
+ int wait;
+ int disconnected;
+
+ pending_scb = NULL;
+ paused = FALSE;
+ wait = FALSE;
+ ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+ acmd = (struct ahc_cmd *)cmd;
+
+ printf("%s:%d:%d:%d: Attempting to queue a%s message\n",
+ ahc_name(ahc), cmd->device->channel,
+ cmd->device->id, cmd->device->lun,
+ flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+ printf("CDB:");
+ for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
+ printf(" 0x%x", cmd->cmnd[cdb_byte]);
+ printf("\n");
+
+ /*
+ * In all versions of Linux, we have to work around
+ * a major flaw in how the mid-layer is locked down
+ * if we are to sleep successfully in our error handler
+ * while allowing our interrupt handler to run. Since
+ * the midlayer acquires either the io_request_lock or
+ * our lock prior to calling us, we must use the
+ * spin_unlock_irq() method for unlocking our lock.
+ * This will force interrupts to be enabled on the
+ * current CPU. Since the EH thread should not have
+ * been running with CPU interrupts disabled other than
+ * by acquiring either the io_request_lock or our own
+ * lock, this *should* be safe.
+ */
+ ahc_midlayer_entrypoint_lock(ahc, &s);
+
+ /*
+ * First determine if we currently own this command.
+ * Start by searching the device queue. If not found
+ * there, check the pending_scb list. If not found
+ * at all, and the system wanted us to just abort the
+ * command, return success.
+ */
+ dev = ahc_linux_get_device(ahc, cmd->device->channel, cmd->device->id,
+ cmd->device->lun, /*alloc*/FALSE);
+
+ if (dev == NULL) {
+ /*
+ * No target device for this command exists,
+ * so we must not still own the command.
+ */
+ printf("%s:%d:%d:%d: Is not an active device\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ retval = SUCCESS;
+ goto no_cmd;
+ }
+
+ TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+ if (list_acmd == acmd)
+ break;
+ }
+
+ if (list_acmd != NULL) {
+ printf("%s:%d:%d:%d: Command found on device queue\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ if (flag == SCB_ABORT) {
+ TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+ cmd->result = DID_ABORT << 16;
+ ahc_linux_queue_cmd_complete(ahc, cmd);
+ retval = SUCCESS;
+ goto done;
+ }
+ }
+
+ if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0
+ && ahc_search_untagged_queues(ahc, cmd, cmd->device->id,
+ cmd->device->channel + 'A',
+ cmd->device->lun,
+ CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) {
+ printf("%s:%d:%d:%d: Command found on untagged queue\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ retval = SUCCESS;
+ goto done;
+ }
+
+ /*
+ * See if we can find a matching cmd in the pending list.
+ */
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ if (pending_scb->io_ctx == cmd)
+ break;
+ }
+
+ if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+ /* Any SCB for this device will do for a target reset */
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ if (ahc_match_scb(ahc, pending_scb, cmd->device->id,
+ cmd->device->channel + 'A',
+ CAM_LUN_WILDCARD,
+ SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+ break;
+ }
+ }
+
+ if (pending_scb == NULL) {
+ printf("%s:%d:%d:%d: Command not found\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ goto no_cmd;
+ }
+
+ if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+ /*
+ * We can't queue two recovery actions using the same SCB
+ */
+ retval = FAILED;
+ goto done;
+ }
+
+ /*
+ * Ensure that the card doesn't do anything
+ * behind our back and that we didn't "just" miss
+ * an interrupt that would affect this cmd.
+ */
+ was_paused = ahc_is_paused(ahc);
+ ahc_pause_and_flushwork(ahc);
+ paused = TRUE;
+
+ if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+ printf("%s:%d:%d:%d: Command already completed\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ goto no_cmd;
+ }
+
+ printf("%s: At time of recovery, card was %spaused\n",
+ ahc_name(ahc), was_paused ? "" : "not ");
+ ahc_dump_card_state(ahc);
+
+ disconnected = TRUE;
+ if (flag == SCB_ABORT) {
+ if (ahc_search_qinfifo(ahc, cmd->device->id,
+ cmd->device->channel + 'A',
+ cmd->device->lun,
+ pending_scb->hscb->tag,
+ ROLE_INITIATOR, CAM_REQ_ABORTED,
+ SEARCH_COMPLETE) > 0) {
+ printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+ ahc_name(ahc), cmd->device->channel,
+ cmd->device->id, cmd->device->lun);
+ retval = SUCCESS;
+ goto done;
+ }
+ } else if (ahc_search_qinfifo(ahc, cmd->device->id,
+ cmd->device->channel + 'A',
+ cmd->device->lun, pending_scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_COUNT) > 0) {
+ disconnected = FALSE;
+ }
+
+ if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {
+ struct scb *bus_scb;
+
+ bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG));
+ if (bus_scb == pending_scb)
+ disconnected = FALSE;
+ else if (flag != SCB_ABORT
+ && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid
+ && ahc_inb(ahc, SAVED_LUN) == SCB_GET_LUN(pending_scb))
+ disconnected = FALSE;
+ }
+
+ /*
+ * At this point, pending_scb is the scb associated with the
+ * passed in command. That command is currently active on the
+ * bus, is in the disconnected state, or we're hoping to find
+ * a command for the same target active on the bus to abuse to
+ * send a BDR. Queue the appropriate message based on which of
+ * these states we are in.
+ */
+ last_phase = ahc_inb(ahc, LASTPHASE);
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ active_scb_index = ahc_inb(ahc, SCB_TAG);
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ if (last_phase != P_BUSFREE
+ && (pending_scb->hscb->tag == active_scb_index
+ || (flag == SCB_DEVICE_RESET
+ && SCSIID_TARGET(ahc, saved_scsiid) == cmd->device->id))) {
+
+ /*
+ * We're active on the bus, so assert ATN
+ * and hope that the target responds.
+ */
+ pending_scb = ahc_lookup_scb(ahc, active_scb_index);
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+ printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ wait = TRUE;
+ } else if (disconnected) {
+
+ /*
+ * Actually re-queue this SCB in an attempt
+ * to select the device before it reconnects.
+ * In either case (selection or reselection),
+ * we will now issue the approprate message
+ * to the timed-out device.
+ *
+ * Set the MK_MESSAGE control bit indicating
+ * that we desire to send a message. We
+ * also set the disconnected flag since
+ * in the paging case there is no guarantee
+ * that our SCB control byte matches the
+ * version on the card. We don't want the
+ * sequencer to abort the command thinking
+ * an unsolicited reselection occurred.
+ */
+ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+ pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+ /*
+ * Remove any cached copy of this SCB in the
+ * disconnected list in preparation for the
+ * queuing of our abort SCB. We use the
+ * same element in the SCB, SCB_NEXT, for
+ * both the qinfifo and the disconnected list.
+ */
+ ahc_search_disc_list(ahc, cmd->device->id,
+ cmd->device->channel + 'A',
+ cmd->device->lun, pending_scb->hscb->tag,
+ /*stop_on_first*/TRUE,
+ /*remove*/TRUE,
+ /*save_state*/FALSE);
+
+ /*
+ * In the non-paging case, the sequencer will
+ * never re-reference the in-core SCB.
+ * To make sure we are notified during
+ * reslection, set the MK_MESSAGE flag in
+ * the card's copy of the SCB.
+ */
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag);
+ ahc_outb(ahc, SCB_CONTROL,
+ ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE);
+ }
+
+ /*
+ * Clear out any entries in the QINFIFO first
+ * so we are the next SCB for this target
+ * to run.
+ */
+ ahc_search_qinfifo(ahc, cmd->device->id,
+ cmd->device->channel + 'A',
+ cmd->device->lun, SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ ahc_qinfifo_requeue_tail(ahc, pending_scb);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ ahc_print_path(ahc, pending_scb);
+ printf("Device is disconnected, re-queuing SCB\n");
+ wait = TRUE;
+ } else {
+ printf("%s:%d:%d:%d: Unable to deliver message\n",
+ ahc_name(ahc), cmd->device->channel, cmd->device->id,
+ cmd->device->lun);
+ retval = FAILED;
+ goto done;
+ }
+
+no_cmd:
+ /*
+ * Our assumption is that if we don't have the command, no
+ * recovery action was required, so we return success. Again,
+ * the semantics of the mid-layer recovery engine are not
+ * well defined, so this may change in time.
+ */
+ retval = SUCCESS;
+done:
+ if (paused)
+ ahc_unpause(ahc);
+ if (wait) {
+ struct timer_list timer;
+ int ret;
+
+ ahc->platform_data->flags |= AHC_UP_EH_SEMAPHORE;
+ spin_unlock_irq(&ahc->platform_data->spin_lock);
+ init_timer(&timer);
+ timer.data = (u_long)ahc;
+ timer.expires = jiffies + (5 * HZ);
+ timer.function = ahc_linux_sem_timeout;
+ add_timer(&timer);
+ printf("Recovery code sleeping\n");
+ down(&ahc->platform_data->eh_sem);
+ printf("Recovery code awake\n");
+ ret = del_timer_sync(&timer);
+ if (ret == 0) {
+ printf("Timer Expired\n");
+ retval = FAILED;
+ }
+ spin_lock_irq(&ahc->platform_data->spin_lock);
+ }
+ ahc_schedule_runq(ahc);
+ ahc_linux_run_complete_queue(ahc);
+ ahc_midlayer_entrypoint_unlock(ahc, &s);
+ return (retval);
+}
+
+void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+ struct ahc_linux_device *dev;
+ int channel;
+ int maxchannel;
+ int target;
+ int maxtarget;
+ int lun;
+ int i;
+
+ maxchannel = (ahc->features & AHC_TWIN) ? 1 : 0;
+ maxtarget = (ahc->features & AHC_WIDE) ? 15 : 7;
+ for (channel = 0; channel <= maxchannel; channel++) {
+
+ for (target = 0; target <=maxtarget; target++) {
+
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_cmd *acmd;
+
+ dev = ahc_linux_get_device(ahc, channel, target,
+ lun, /*alloc*/FALSE);
+ if (dev == NULL)
+ continue;
+
+ printf("DevQ(%d:%d:%d): ",
+ channel, target, lun);
+ i = 0;
+ TAILQ_FOREACH(acmd, &dev->busyq,
+ acmd_links.tqe) {
+ if (i++ > AHC_SCB_MAX)
+ break;
+ }
+ printf("%d waiting\n", i);
+ }
+ }
+ }
+}
+
+static void ahc_linux_exit(void);
+
+static int __init
+ahc_linux_init(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ int rc = ahc_linux_detect(&aic7xxx_driver_template);
+ if (rc)
+ return rc;
+ ahc_linux_exit();
+ return -ENODEV;
+#else
+ scsi_register_module(MODULE_SCSI_HA, &aic7xxx_driver_template);
+ if (aic7xxx_driver_template.present == 0) {
+ scsi_unregister_module(MODULE_SCSI_HA,
+ &aic7xxx_driver_template);
+ return (-ENODEV);
+ }
+
+ return (0);
+#endif
+}
+
+static void
+ahc_linux_exit(void)
+{
+ struct ahc_softc *ahc;
+
+ /*
+ * Shutdown DV threads before going into the SCSI mid-layer.
+ * This avoids situations where the mid-layer locks the entire
+ * kernel so that waiting for our DV threads to exit leads
+ * to deadlock.
+ */
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+
+ ahc_linux_kill_dv_thread(ahc);
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ /*
+ * In 2.4 we have to unregister from the PCI core _after_
+ * unregistering from the scsi midlayer to avoid dangling
+ * references.
+ */
+ scsi_unregister_module(MODULE_SCSI_HA, &aic7xxx_driver_template);
+#endif
+ ahc_linux_pci_exit();
+ ahc_linux_eisa_exit();
+}
+
+module_init(ahc_linux_init);
+module_exit(ahc_linux_exit);
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h
new file mode 100644
index 000000000000..db3bd6321dd4
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -0,0 +1,1130 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#151 $
+ *
+ */
+#ifndef _AIC7XXX_LINUX_H_
+#define _AIC7XXX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include <linux/interrupt.h> /* For tasklet support. */
+#include <linux/config.h>
+#include <linux/slab.h>
+
+/* Core SCSI definitions */
+#define AIC_LIB_PREFIX ahc
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+#include "aiclib.h"
+
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC7XXX_DEBUG_ENABLE
+#ifdef CONFIG_AIC7XXX_DEBUG_MASK
+#define AHC_DEBUG 1
+#define AHC_DEBUG_OPTS CONFIG_AIC7XXX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHC_DEBUG 1
+#endif
+/* No debugging code. */
+#endif
+
+/************************* Forward Declarations *******************************/
+struct ahc_softc;
+typedef struct pci_dev *ahc_dev_softc_t;
+typedef Scsi_Cmnd *ahc_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahc_htobe16(x) cpu_to_be16(x)
+#define ahc_htobe32(x) cpu_to_be32(x)
+#define ahc_htobe64(x) cpu_to_be64(x)
+#define ahc_htole16(x) cpu_to_le16(x)
+#define ahc_htole32(x) cpu_to_le32(x)
+#define ahc_htole64(x) cpu_to_le64(x)
+
+#define ahc_be16toh(x) be16_to_cpu(x)
+#define ahc_be32toh(x) be32_to_cpu(x)
+#define ahc_be64toh(x) be64_to_cpu(x)
+#define ahc_le16toh(x) le16_to_cpu(x)
+#define ahc_le32toh(x) le32_to_cpu(x)
+#define ahc_le64toh(x) le64_to_cpu(x)
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#if defined(__BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#if defined(__LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+/************************* Configuration Data *********************************/
+extern u_int aic7xxx_no_probe;
+extern u_int aic7xxx_allow_memio;
+extern int aic7xxx_detect_complete;
+extern Scsi_Host_Template aic7xxx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+typedef uint32_t bus_size_t;
+
+typedef enum {
+ BUS_SPACE_MEMIO,
+ BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+ u_long ioport;
+ volatile uint8_t __iomem *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+ dma_addr_t ds_addr;
+ bus_size_t ds_len;
+} bus_dma_segment_t;
+
+struct ahc_linux_dma_tag
+{
+ bus_size_t alignment;
+ bus_size_t boundary;
+ bus_size_t maxsize;
+};
+typedef struct ahc_linux_dma_tag* bus_dma_tag_t;
+
+struct ahc_linux_dmamap
+{
+ dma_addr_t bus_addr;
+};
+typedef struct ahc_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, dma_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK 0x0
+#define BUS_DMA_NOWAIT 0x1
+#define BUS_DMA_ALLOCNOW 0x2
+#define BUS_DMA_LOAD_SEGS 0x4 /*
+ * Argument is an S/G list not
+ * a single buffer.
+ */
+
+#define BUS_SPACE_MAXADDR 0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT 0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT 0xFFFFFFFF
+
+int ahc_dma_tag_create(struct ahc_softc *, bus_dma_tag_t /*parent*/,
+ bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+ dma_addr_t /*lowaddr*/, dma_addr_t /*highaddr*/,
+ bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+ bus_size_t /*maxsize*/, int /*nsegments*/,
+ bus_size_t /*maxsegsz*/, int /*flags*/,
+ bus_dma_tag_t */*dma_tagp*/);
+
+void ahc_dma_tag_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/);
+
+int ahc_dmamem_alloc(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+ void** /*vaddr*/, int /*flags*/,
+ bus_dmamap_t* /*mapp*/);
+
+void ahc_dmamem_free(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+ void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void ahc_dmamap_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/,
+ bus_dmamap_t /*map*/);
+
+int ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t /*dmat*/,
+ bus_dmamap_t /*map*/, void * /*buf*/,
+ bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+ void */*callback_arg*/, int /*flags*/);
+
+int ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahc_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD 0x01 /* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD 0x02 /* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE 0x04 /* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE 0x08 /* post-write synchronization */
+
+/*
+ * XXX
+ * ahc_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API. Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory. Perhaps we need
+ * to do an mb()?
+ */
+#define ahc_dmamap_sync(ahc, dma_tag, dmamap, offset, len, op)
+
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahc_timer_t;
+
+/********************************** Includes **********************************/
+#ifdef CONFIG_AIC7XXX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#include "aic7xxx.h"
+
+/***************************** Timer Facilities *******************************/
+#define ahc_timer_init init_timer
+#define ahc_timer_stop del_timer_sync
+typedef void ahc_linux_callback_t (u_long);
+static __inline void ahc_timer_reset(ahc_timer_t *timer, int usec,
+ ahc_callback_t *func, void *arg);
+static __inline void ahc_scb_timer_reset(struct scb *scb, u_int usec);
+
+static __inline void
+ahc_timer_reset(ahc_timer_t *timer, int usec, ahc_callback_t *func, void *arg)
+{
+ struct ahc_softc *ahc;
+
+ ahc = (struct ahc_softc *)arg;
+ del_timer(timer);
+ timer->data = (u_long)arg;
+ timer->expires = jiffies + (usec * HZ)/1000000;
+ timer->function = (ahc_linux_callback_t*)func;
+ add_timer(timer);
+}
+
+static __inline void
+ahc_scb_timer_reset(struct scb *scb, u_int usec)
+{
+ mod_timer(&scb->io_ctx->eh_timeout, jiffies + (usec * HZ)/1000000);
+}
+
+/***************************** SMP support ************************************/
+#include <linux/spinlock.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) || defined(SCSI_HAS_HOST_LOCK))
+#define AHC_SCSI_HAS_HOST_LOCK 1
+#else
+#define AHC_SCSI_HAS_HOST_LOCK 0
+#endif
+
+#define AIC7XXX_DRIVER_VERSION "6.2.36"
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros. The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahc_cmd_internal {
+ /* Area owned by the Linux scsi layer. */
+ uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)];
+ union {
+ STAILQ_ENTRY(ahc_cmd) ste;
+ LIST_ENTRY(ahc_cmd) le;
+ TAILQ_ENTRY(ahc_cmd) tqe;
+ } links;
+ uint32_t end;
+};
+
+struct ahc_cmd {
+ union {
+ struct ahc_cmd_internal icmd;
+ struct scsi_cmnd scsi_cmd;
+ } un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle. The structure allocated for a device only becomes persistent
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahc_busyq, ahc_cmd);
+typedef enum {
+ AHC_DEV_UNCONFIGURED = 0x01,
+ AHC_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+ AHC_DEV_TIMER_ACTIVE = 0x04, /* Our timer is active */
+ AHC_DEV_ON_RUN_LIST = 0x08, /* Queued to be run later */
+ AHC_DEV_Q_BASIC = 0x10, /* Allow basic device queuing */
+ AHC_DEV_Q_TAGGED = 0x20, /* Allow full SCSI2 command queueing */
+ AHC_DEV_PERIODIC_OTAG = 0x40, /* Send OTAG to prevent starvation */
+ AHC_DEV_SLAVE_CONFIGURED = 0x80 /* slave_configure() has been called */
+} ahc_linux_dev_flags;
+
+struct ahc_linux_target;
+struct ahc_linux_device {
+ TAILQ_ENTRY(ahc_linux_device) links;
+ struct ahc_busyq busyq;
+
+ /*
+ * The number of transactions currently
+ * queued to the device.
+ */
+ int active;
+
+ /*
+ * The currently allowed number of
+ * transactions that can be queued to
+ * the device. Must be signed for
+ * conversion from tagged to untagged
+ * mode where the device may have more
+ * than one outstanding active transaction.
+ */
+ int openings;
+
+ /*
+ * A positive count indicates that this
+ * device's queue is halted.
+ */
+ u_int qfrozen;
+
+ /*
+ * Cumulative command counter.
+ */
+ u_long commands_issued;
+
+ /*
+ * The number of tagged transactions when
+ * running at our current opening level
+ * that have been successfully received by
+ * this device since the last QUEUE FULL.
+ */
+ u_int tag_success_count;
+#define AHC_TAG_SUCCESS_INTERVAL 50
+
+ ahc_linux_dev_flags flags;
+
+ /*
+ * Per device timer.
+ */
+ struct timer_list timer;
+
+ /*
+ * The high limit for the tags variable.
+ */
+ u_int maxtags;
+
+ /*
+ * The computed number of tags outstanding
+ * at the time of the last QUEUE FULL event.
+ */
+ u_int tags_on_last_queuefull;
+
+ /*
+ * How many times we have seen a queue full
+ * with the same number of tags. This is used
+ * to stop our adaptive queue depth algorithm
+ * on devices with a fixed number of tags.
+ */
+ u_int last_queuefull_same_count;
+#define AHC_LOCK_TAGS_COUNT 50
+
+ /*
+ * How many transactions have been queued
+ * without the device going idle. We use
+ * this statistic to determine when to issue
+ * an ordered tag to prevent transaction
+ * starvation. This statistic is only updated
+ * if the AHC_DEV_PERIODIC_OTAG flag is set
+ * on this device.
+ */
+ u_int commands_since_idle_or_otag;
+#define AHC_OTAG_THRESH 500
+
+ int lun;
+ Scsi_Device *scsi_device;
+ struct ahc_linux_target *target;
+};
+
+typedef enum {
+ AHC_DV_REQUIRED = 0x01,
+ AHC_INQ_VALID = 0x02,
+ AHC_BASIC_DV = 0x04,
+ AHC_ENHANCED_DV = 0x08
+} ahc_linux_targ_flags;
+
+/* DV States */
+typedef enum {
+ AHC_DV_STATE_EXIT = 0,
+ AHC_DV_STATE_INQ_SHORT_ASYNC,
+ AHC_DV_STATE_INQ_ASYNC,
+ AHC_DV_STATE_INQ_ASYNC_VERIFY,
+ AHC_DV_STATE_TUR,
+ AHC_DV_STATE_REBD,
+ AHC_DV_STATE_INQ_VERIFY,
+ AHC_DV_STATE_WEB,
+ AHC_DV_STATE_REB,
+ AHC_DV_STATE_SU,
+ AHC_DV_STATE_BUSY
+} ahc_dv_state;
+
+struct ahc_linux_target {
+ struct ahc_linux_device *devices[AHC_NUM_LUNS];
+ int channel;
+ int target;
+ int refcount;
+ struct ahc_transinfo last_tinfo;
+ struct ahc_softc *ahc;
+ ahc_linux_targ_flags flags;
+ struct scsi_inquiry_data *inq_data;
+ /*
+ * The next "fallback" period to use for narrow/wide transfers.
+ */
+ uint8_t dv_next_narrow_period;
+ uint8_t dv_next_wide_period;
+ uint8_t dv_max_width;
+ uint8_t dv_max_ppr_options;
+ uint8_t dv_last_ppr_options;
+ u_int dv_echo_size;
+ ahc_dv_state dv_state;
+ u_int dv_state_retry;
+ char *dv_buffer;
+ char *dv_buffer1;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require. So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/*
+ * We dynamically adjust the number of segments in pre-2.5 kernels to
+ * avoid fragmentation issues in the SCSI mid-layer's private memory
+ * allocator. See aic7xxx_osm.c ahc_linux_size_nseg() for details.
+ */
+extern u_int ahc_linux_nseg;
+#define AHC_NSEG ahc_linux_nseg
+#define AHC_LINUX_MIN_NSEG 64
+#else
+#define AHC_NSEG 128
+#endif
+
+/*
+ * Per-SCB OSM storage.
+ */
+typedef enum {
+ AHC_UP_EH_SEMAPHORE = 0x1
+} ahc_linux_scb_flags;
+
+struct scb_platform_data {
+ struct ahc_linux_device *dev;
+ dma_addr_t buf_busaddr;
+ uint32_t xfer_len;
+ uint32_t sense_resid; /* Auto-Sense residual */
+ ahc_linux_scb_flags flags;
+};
+
+/*
+ * Define a structure used for each host adapter. All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+typedef enum {
+ AHC_DV_WAIT_SIMQ_EMPTY = 0x01,
+ AHC_DV_WAIT_SIMQ_RELEASE = 0x02,
+ AHC_DV_ACTIVE = 0x04,
+ AHC_DV_SHUTDOWN = 0x08,
+ AHC_RUN_CMPLT_Q_TIMER = 0x10
+} ahc_linux_softc_flags;
+
+TAILQ_HEAD(ahc_completeq, ahc_cmd);
+
+struct ahc_platform_data {
+ /*
+ * Fields accessed from interrupt context.
+ */
+ struct ahc_linux_target *targets[AHC_NUM_TARGETS];
+ TAILQ_HEAD(, ahc_linux_device) device_runq;
+ struct ahc_completeq completeq;
+
+ spinlock_t spin_lock;
+ struct tasklet_struct runq_tasklet;
+ u_int qfrozen;
+ pid_t dv_pid;
+ struct timer_list completeq_timer;
+ struct timer_list reset_timer;
+ struct semaphore eh_sem;
+ struct semaphore dv_sem;
+ struct semaphore dv_cmd_sem; /* XXX This needs to be in
+ * the target struct
+ */
+ struct scsi_device *dv_scsi_dev;
+ struct Scsi_Host *host; /* pointer to scsi host */
+#define AHC_LINUX_NOIRQ ((uint32_t)~0)
+ uint32_t irq; /* IRQ for this adapter */
+ uint32_t bios_address;
+ uint32_t mem_busaddr; /* Mem Base Addr */
+ uint64_t hw_dma_mask;
+ ahc_linux_softc_flags flags;
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahc_delay(long);
+static __inline void
+ahc_delay(long usec)
+{
+ /*
+ * udelay on Linux can have problems for
+ * multi-millisecond waits. Wait at most
+ * 1024us per call.
+ */
+ while (usec > 0) {
+ udelay(usec % 1024);
+ usec -= 1024;
+ }
+}
+
+
+/***************************** Low Level I/O **********************************/
+static __inline uint8_t ahc_inb(struct ahc_softc * ahc, long port);
+static __inline void ahc_outb(struct ahc_softc * ahc, long port, uint8_t val);
+static __inline void ahc_outsb(struct ahc_softc * ahc, long port,
+ uint8_t *, int count);
+static __inline void ahc_insb(struct ahc_softc * ahc, long port,
+ uint8_t *, int count);
+
+static __inline uint8_t
+ahc_inb(struct ahc_softc * ahc, long port)
+{
+ uint8_t x;
+
+ if (ahc->tag == BUS_SPACE_MEMIO) {
+ x = readb(ahc->bsh.maddr + port);
+ } else {
+ x = inb(ahc->bsh.ioport + port);
+ }
+ mb();
+ return (x);
+}
+
+static __inline void
+ahc_outb(struct ahc_softc * ahc, long port, uint8_t val)
+{
+ if (ahc->tag == BUS_SPACE_MEMIO) {
+ writeb(val, ahc->bsh.maddr + port);
+ } else {
+ outb(val, ahc->bsh.ioport + port);
+ }
+ mb();
+}
+
+static __inline void
+ahc_outsb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ ahc_outb(ahc, port, *array++);
+}
+
+static __inline void
+ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+ int i;
+
+ /*
+ * There is probably a more efficient way to do this on Linux
+ * but we don't use this for anything speed critical and this
+ * should work.
+ */
+ for (i = 0; i < count; i++)
+ *array++ = ahc_inb(ahc, port);
+}
+
+/**************************** Initialization **********************************/
+int ahc_linux_register_host(struct ahc_softc *,
+ Scsi_Host_Template *);
+
+uint64_t ahc_linux_get_memsize(void);
+
+/*************************** Pretty Printing **********************************/
+struct info_str {
+ char *buffer;
+ int length;
+ off_t offset;
+ int pos;
+};
+
+void ahc_format_transinfo(struct info_str *info,
+ struct ahc_transinfo *tinfo);
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahc_lockinit(struct ahc_softc *);
+static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock acquisition and release of the above lock in midlayer entry points. */
+static __inline void ahc_midlayer_entrypoint_lock(struct ahc_softc *,
+ unsigned long *flags);
+static __inline void ahc_midlayer_entrypoint_unlock(struct ahc_softc *,
+ unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahc_done_lockinit(struct ahc_softc *);
+static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during ahc_list manipulation and ahc softc frees */
+extern spinlock_t ahc_list_spinlock;
+static __inline void ahc_list_lockinit(void);
+static __inline void ahc_list_lock(unsigned long *flags);
+static __inline void ahc_list_unlock(unsigned long *flags);
+
+static __inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+ spin_lock_init(&ahc->platform_data->spin_lock);
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ spin_lock_irqsave(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_midlayer_entrypoint_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ /*
+ * In 2.5.X and some 2.4.X versions, the midlayer takes our
+ * lock just before calling us, so we avoid locking again.
+ * For other kernel versions, the io_request_lock is taken
+ * just before our entry point is called. In this case, we
+ * trade the io_request_lock for our per-softc lock.
+ */
+#if AHC_SCSI_HAS_HOST_LOCK == 0
+ spin_unlock(&io_request_lock);
+ spin_lock(&ahc->platform_data->spin_lock);
+#endif
+}
+
+static __inline void
+ahc_midlayer_entrypoint_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+#if AHC_SCSI_HAS_HOST_LOCK == 0
+ spin_unlock(&ahc->platform_data->spin_lock);
+ spin_lock(&io_request_lock);
+#endif
+}
+
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+ /*
+ * In 2.5.X, our own lock is held during completions.
+ * In previous versions, the io_request_lock is used.
+ * In either case, we can't initialize this lock again.
+ */
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+#if AHC_SCSI_HAS_HOST_LOCK == 0
+ spin_lock_irqsave(&io_request_lock, *flags);
+#endif
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+#if AHC_SCSI_HAS_HOST_LOCK == 0
+ spin_unlock_irqrestore(&io_request_lock, *flags);
+#endif
+}
+
+static __inline void
+ahc_list_lockinit(void)
+{
+ spin_lock_init(&ahc_list_spinlock);
+}
+
+static __inline void
+ahc_list_lock(unsigned long *flags)
+{
+ spin_lock_irqsave(&ahc_list_spinlock, *flags);
+}
+
+static __inline void
+ahc_list_unlock(unsigned long *flags)
+{
+ spin_unlock_irqrestore(&ahc_list_spinlock, *flags);
+}
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR 0x00
+#define PCIR_VENDOR 0x00
+#define PCIR_DEVICE 0x02
+#define PCIR_COMMAND 0x04
+#define PCIM_CMD_PORTEN 0x0001
+#define PCIM_CMD_MEMEN 0x0002
+#define PCIM_CMD_BUSMASTEREN 0x0004
+#define PCIM_CMD_MWRICEN 0x0010
+#define PCIM_CMD_PERRESPEN 0x0040
+#define PCIM_CMD_SERRESPEN 0x0100
+#define PCIR_STATUS 0x06
+#define PCIR_REVID 0x08
+#define PCIR_PROGIF 0x09
+#define PCIR_SUBCLASS 0x0a
+#define PCIR_CLASS 0x0b
+#define PCIR_CACHELNSZ 0x0c
+#define PCIR_LATTIMER 0x0d
+#define PCIR_HEADERTYPE 0x0e
+#define PCIM_MFDEV 0x80
+#define PCIR_BIST 0x0f
+#define PCIR_CAP_PTR 0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS 0x10
+#define PCIR_SUBVEND_0 0x2c
+#define PCIR_SUBDEV_0 0x2e
+
+extern struct pci_driver aic7xxx_pci_driver;
+
+typedef enum
+{
+ AHC_POWER_STATE_D0,
+ AHC_POWER_STATE_D1,
+ AHC_POWER_STATE_D2,
+ AHC_POWER_STATE_D3
+} ahc_power_state;
+
+/**************************** VL/EISA Routines ********************************/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) \
+ && (defined(__i386__) || defined(__alpha__)) \
+ && (!defined(CONFIG_EISA)))
+#define CONFIG_EISA
+#endif
+
+#ifdef CONFIG_EISA
+extern uint32_t aic7xxx_probe_eisa_vl;
+int ahc_linux_eisa_init(void);
+void ahc_linux_eisa_exit(void);
+int aic7770_map_registers(struct ahc_softc *ahc,
+ u_int port);
+int aic7770_map_int(struct ahc_softc *ahc, u_int irq);
+#else
+static inline int ahc_linux_eisa_init(void) {
+ return -ENODEV;
+}
+static inline void ahc_linux_eisa_exit(void) {
+}
+#endif
+
+/******************************* PCI Routines *********************************/
+#ifdef CONFIG_PCI
+int ahc_linux_pci_init(void);
+void ahc_linux_pci_exit(void);
+int ahc_pci_map_registers(struct ahc_softc *ahc);
+int ahc_pci_map_int(struct ahc_softc *ahc);
+
+static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci,
+ int reg, int width);
+
+static __inline uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+ switch (width) {
+ case 1:
+ {
+ uint8_t retval;
+
+ pci_read_config_byte(pci, reg, &retval);
+ return (retval);
+ }
+ case 2:
+ {
+ uint16_t retval;
+ pci_read_config_word(pci, reg, &retval);
+ return (retval);
+ }
+ case 4:
+ {
+ uint32_t retval;
+ pci_read_config_dword(pci, reg, &retval);
+ return (retval);
+ }
+ default:
+ panic("ahc_pci_read_config: Read size too big");
+ /* NOTREACHED */
+ return (0);
+ }
+}
+
+static __inline void ahc_pci_write_config(ahc_dev_softc_t pci,
+ int reg, uint32_t value,
+ int width);
+
+static __inline void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+ switch (width) {
+ case 1:
+ pci_write_config_byte(pci, reg, value);
+ break;
+ case 2:
+ pci_write_config_word(pci, reg, value);
+ break;
+ case 4:
+ pci_write_config_dword(pci, reg, value);
+ break;
+ default:
+ panic("ahc_pci_write_config: Write size too big");
+ /* NOTREACHED */
+ }
+}
+
+static __inline int ahc_get_pci_function(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+ return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahc_get_pci_slot(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+ return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahc_get_pci_bus(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+ return (pci->bus->number);
+}
+#else
+static inline int ahc_linux_pci_init(void) {
+ return 0;
+}
+static inline void ahc_linux_pci_exit(void) {
+}
+#endif
+
+static __inline void ahc_flush_device_writes(struct ahc_softc *);
+static __inline void
+ahc_flush_device_writes(struct ahc_softc *ahc)
+{
+ /* XXX Is this sufficient for all architectures??? */
+ ahc_inb(ahc, INTSTAT);
+}
+
+/**************************** Proc FS Support *********************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+int ahc_linux_proc_info(char *, char **, off_t, int, int, int);
+#else
+int ahc_linux_proc_info(struct Scsi_Host *, char *, char **,
+ off_t, int, int);
+#endif
+
+/*************************** Domain Validation ********************************/
+#define AHC_DV_CMD(cmd) ((cmd)->scsi_done == ahc_linux_dv_complete)
+#define AHC_DV_SIMQ_FROZEN(ahc) \
+ ((((ahc)->platform_data->flags & AHC_DV_ACTIVE) != 0) \
+ && (ahc)->platform_data->qfrozen == 1)
+
+/*********************** Transaction Access Wrappers *************************/
+static __inline void ahc_cmd_set_transaction_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static __inline void ahc_cmd_set_scsi_status(Scsi_Cmnd *, uint32_t);
+static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static __inline uint32_t ahc_cmd_get_transaction_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahc_get_transaction_status(struct scb *);
+static __inline uint32_t ahc_cmd_get_scsi_status(Scsi_Cmnd *cmd);
+static __inline uint32_t ahc_get_scsi_status(struct scb *);
+static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static __inline u_long ahc_get_transfer_length(struct scb *);
+static __inline int ahc_get_transfer_dir(struct scb *);
+static __inline void ahc_set_residual(struct scb *, u_long);
+static __inline void ahc_set_sense_residual(struct scb *scb, u_long resid);
+static __inline u_long ahc_get_residual(struct scb *);
+static __inline u_long ahc_get_sense_residual(struct scb *);
+static __inline int ahc_perform_autosense(struct scb *);
+static __inline uint32_t ahc_get_sense_bufsize(struct ahc_softc *,
+ struct scb *);
+static __inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
+ struct ahc_devinfo *);
+static __inline void ahc_platform_scb_free(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline void ahc_freeze_scb(struct scb *scb);
+
+static __inline
+void ahc_cmd_set_transaction_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~(CAM_STATUS_MASK << 16);
+ cmd->result |= status << 16;
+}
+
+static __inline
+void ahc_set_transaction_status(struct scb *scb, uint32_t status)
+{
+ ahc_cmd_set_transaction_status(scb->io_ctx,status);
+}
+
+static __inline
+void ahc_cmd_set_scsi_status(Scsi_Cmnd *cmd, uint32_t status)
+{
+ cmd->result &= ~0xFFFF;
+ cmd->result |= status;
+}
+
+static __inline
+void ahc_set_scsi_status(struct scb *scb, uint32_t status)
+{
+ ahc_cmd_set_scsi_status(scb->io_ctx, status);
+}
+
+static __inline
+uint32_t ahc_cmd_get_transaction_status(Scsi_Cmnd *cmd)
+{
+ return ((cmd->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+ return (ahc_cmd_get_transaction_status(scb->io_ctx));
+}
+
+static __inline
+uint32_t ahc_cmd_get_scsi_status(Scsi_Cmnd *cmd)
+{
+ return (cmd->result & 0xFFFF);
+}
+
+static __inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+ return (ahc_cmd_get_scsi_status(scb->io_ctx));
+}
+
+static __inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+ /*
+ * Nothing to do for linux as the incoming transaction
+ * has no concept of tag/non tagged, etc.
+ */
+}
+
+static __inline
+u_long ahc_get_transfer_length(struct scb *scb)
+{
+ return (scb->platform_data->xfer_len);
+}
+
+static __inline
+int ahc_get_transfer_dir(struct scb *scb)
+{
+ return (scb->io_ctx->sc_data_direction);
+}
+
+static __inline
+void ahc_set_residual(struct scb *scb, u_long resid)
+{
+ scb->io_ctx->resid = resid;
+}
+
+static __inline
+void ahc_set_sense_residual(struct scb *scb, u_long resid)
+{
+ scb->platform_data->sense_resid = resid;
+}
+
+static __inline
+u_long ahc_get_residual(struct scb *scb)
+{
+ return (scb->io_ctx->resid);
+}
+
+static __inline
+u_long ahc_get_sense_residual(struct scb *scb)
+{
+ return (scb->platform_data->sense_resid);
+}
+
+static __inline
+int ahc_perform_autosense(struct scb *scb)
+{
+ /*
+ * We always perform autosense in Linux.
+ * On other platforms this is set on a
+ * per-transaction basis.
+ */
+ return (1);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+ return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void
+ahc_notify_xfer_settings_change(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo)
+{
+ /* Nothing to do here for linux */
+}
+
+static __inline void
+ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+}
+
+int ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void ahc_platform_free(struct ahc_softc *ahc);
+void ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+
+static __inline void
+ahc_freeze_scb(struct scb *scb)
+{
+ if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
+ scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+ scb->platform_data->dev->qfrozen++;
+ }
+}
+
+void ahc_platform_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo, ahc_queue_alg);
+int ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+irqreturn_t
+ ahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs);
+void ahc_platform_flushwork(struct ahc_softc *ahc);
+int ahc_softc_comp(struct ahc_softc *, struct ahc_softc *);
+void ahc_done(struct ahc_softc*, struct scb*);
+void ahc_send_async(struct ahc_softc *, char channel,
+ u_int target, u_int lun, ac_code, void *);
+void ahc_print_path(struct ahc_softc *, struct scb *);
+void ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+#ifdef CONFIG_PCI
+#define AHC_PCI_CONFIG 1
+#else
+#define AHC_PCI_CONFIG 0
+#endif
+#define bootverbose aic7xxx_verbose
+extern u_int aic7xxx_verbose;
+#endif /* _AIC7XXX_LINUX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
new file mode 100644
index 000000000000..6f6674aa31ef
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -0,0 +1,389 @@
+/*
+ * Linux driver attachment glue for PCI based controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#47 $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_pci.h"
+
+static int ahc_linux_pci_dev_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static int ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc,
+ u_long *base);
+static int ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+ u_long *bus_addr,
+ uint8_t __iomem **maddr);
+static void ahc_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* Define the macro locally since it's different for different class of chips.
+*/
+#define ID(x) ID_C(x, PCI_CLASS_STORAGE_SCSI)
+
+static struct pci_device_id ahc_linux_pci_id_table[] = {
+ /* aic7850 based controllers */
+ ID(ID_AHA_2902_04_10_15_20C_30C),
+ /* aic7860 based controllers */
+ ID(ID_AHA_2930CU),
+ ID(ID_AHA_1480A & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK),
+ /* aic7870 based controllers */
+ ID(ID_AHA_2940),
+ ID(ID_AHA_3940),
+ ID(ID_AHA_398X),
+ ID(ID_AHA_2944),
+ ID(ID_AHA_3944),
+ ID(ID_AHA_4944),
+ /* aic7880 based controllers */
+ ID(ID_AHA_2940U & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_3940U & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2944U & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_3944U & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_398XU & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_4944U & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2930U & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK),
+ ID(ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK),
+ /* aic7890 based controllers */
+ ID(ID_AHA_2930U2),
+ ID(ID_AHA_2940U2B),
+ ID(ID_AHA_2940U2_OEM),
+ ID(ID_AHA_2940U2),
+ ID(ID_AHA_2950U2B),
+ ID16(ID_AIC7890_ARO & ID_AIC7895_ARO_MASK),
+ ID(ID_AAA_131U2),
+ /* aic7890 based controllers */
+ ID(ID_AHA_29160),
+ ID(ID_AHA_29160_CPQ),
+ ID(ID_AHA_29160N),
+ ID(ID_AHA_29160C),
+ ID(ID_AHA_29160B),
+ ID(ID_AHA_19160B),
+ ID(ID_AIC7892_ARO),
+ /* aic7892 based controllers */
+ ID(ID_AHA_2940U_DUAL),
+ ID(ID_AHA_3940AU),
+ ID(ID_AHA_3944AU),
+ ID(ID_AIC7895_ARO),
+ ID(ID_AHA_3950U2B_0),
+ ID(ID_AHA_3950U2B_1),
+ ID(ID_AHA_3950U2D_0),
+ ID(ID_AHA_3950U2D_1),
+ ID(ID_AIC7896_ARO),
+ /* aic7899 based controllers */
+ ID(ID_AHA_3960D),
+ ID(ID_AHA_3960D_CPQ),
+ ID(ID_AIC7899_ARO),
+ /* Generic chip probes for devices we don't know exactly. */
+ ID(ID_AIC7850 & ID_DEV_VENDOR_MASK),
+ ID(ID_AIC7855 & ID_DEV_VENDOR_MASK),
+ ID(ID_AIC7859 & ID_DEV_VENDOR_MASK),
+ ID(ID_AIC7860 & ID_DEV_VENDOR_MASK),
+ ID(ID_AIC7870 & ID_DEV_VENDOR_MASK),
+ ID(ID_AIC7880 & ID_DEV_VENDOR_MASK),
+ ID16(ID_AIC7890 & ID_9005_GENERIC_MASK),
+ ID16(ID_AIC7892 & ID_9005_GENERIC_MASK),
+ ID(ID_AIC7895 & ID_DEV_VENDOR_MASK),
+ ID16(ID_AIC7896 & ID_9005_GENERIC_MASK),
+ ID16(ID_AIC7899 & ID_9005_GENERIC_MASK),
+ ID(ID_AIC7810 & ID_DEV_VENDOR_MASK),
+ ID(ID_AIC7815 & ID_DEV_VENDOR_MASK),
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
+
+struct pci_driver aic7xxx_pci_driver = {
+ .name = "aic7xxx",
+ .probe = ahc_linux_pci_dev_probe,
+ .remove = ahc_linux_pci_dev_remove,
+ .id_table = ahc_linux_pci_id_table
+};
+
+static void
+ahc_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+ struct ahc_softc *ahc;
+ u_long l;
+
+ /*
+ * We should be able to just perform
+ * the free directly, but check our
+ * list for extra sanity.
+ */
+ ahc_list_lock(&l);
+ ahc = ahc_find_softc((struct ahc_softc *)pci_get_drvdata(pdev));
+ if (ahc != NULL) {
+ u_long s;
+
+ TAILQ_REMOVE(&ahc_tailq, ahc, links);
+ ahc_list_unlock(&l);
+ ahc_lock(ahc, &s);
+ ahc_intr_enable(ahc, FALSE);
+ ahc_unlock(ahc, &s);
+ ahc_free(ahc);
+ } else
+ ahc_list_unlock(&l);
+}
+
+static int
+ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ char buf[80];
+ const uint64_t mask_39bit = 0x7FFFFFFFFFULL;
+ struct ahc_softc *ahc;
+ ahc_dev_softc_t pci;
+ struct ahc_pci_identity *entry;
+ char *name;
+ int error;
+
+ /*
+ * Some BIOSen report the same device multiple times.
+ */
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ struct pci_dev *probed_pdev;
+
+ probed_pdev = ahc->dev_softc;
+ if (probed_pdev->bus->number == pdev->bus->number
+ && probed_pdev->devfn == pdev->devfn)
+ break;
+ }
+ if (ahc != NULL) {
+ /* Skip duplicate. */
+ return (-ENODEV);
+ }
+
+ pci = pdev;
+ entry = ahc_find_pci_device(pci);
+ if (entry == NULL)
+ return (-ENODEV);
+
+ /*
+ * Allocate a softc for this card and
+ * set it up for attachment by our
+ * common detect routine.
+ */
+ sprintf(buf, "ahc_pci:%d:%d:%d",
+ ahc_get_pci_bus(pci),
+ ahc_get_pci_slot(pci),
+ ahc_get_pci_function(pci));
+ name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+ if (name == NULL)
+ return (-ENOMEM);
+ strcpy(name, buf);
+ ahc = ahc_alloc(NULL, name);
+ if (ahc == NULL)
+ return (-ENOMEM);
+ if (pci_enable_device(pdev)) {
+ ahc_free(ahc);
+ return (-ENODEV);
+ }
+ pci_set_master(pdev);
+
+ if (sizeof(dma_addr_t) > 4
+ && ahc_linux_get_memsize() > 0x80000000
+ && pci_set_dma_mask(pdev, mask_39bit) == 0) {
+ ahc->flags |= AHC_39BIT_ADDRESSING;
+ ahc->platform_data->hw_dma_mask = mask_39bit;
+ } else {
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n");
+ return (-ENODEV);
+ }
+ ahc->platform_data->hw_dma_mask = DMA_32BIT_MASK;
+ }
+ ahc->dev_softc = pci;
+ error = ahc_pci_config(ahc, entry);
+ if (error != 0) {
+ ahc_free(ahc);
+ return (-error);
+ }
+ pci_set_drvdata(pdev, ahc);
+ if (aic7xxx_detect_complete) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ ahc_linux_register_host(ahc, &aic7xxx_driver_template);
+#else
+ printf("aic7xxx: ignoring PCI device found after "
+ "initialization\n");
+ return (-ENODEV);
+#endif
+ }
+ return (0);
+}
+
+int
+ahc_linux_pci_init(void)
+{
+ /* Translate error or zero return into zero or one */
+ return pci_module_init(&aic7xxx_pci_driver) ? 0 : 1;
+}
+
+void
+ahc_linux_pci_exit(void)
+{
+ pci_unregister_driver(&aic7xxx_pci_driver);
+}
+
+static int
+ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, u_long *base)
+{
+ if (aic7xxx_allow_memio == 0)
+ return (ENOMEM);
+
+ *base = pci_resource_start(ahc->dev_softc, 0);
+ if (*base == 0)
+ return (ENOMEM);
+ if (request_region(*base, 256, "aic7xxx") == 0)
+ return (ENOMEM);
+ return (0);
+}
+
+static int
+ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+ u_long *bus_addr,
+ uint8_t __iomem **maddr)
+{
+ u_long start;
+ int error;
+
+ error = 0;
+ start = pci_resource_start(ahc->dev_softc, 1);
+ if (start != 0) {
+ *bus_addr = start;
+ if (request_mem_region(start, 0x1000, "aic7xxx") == 0)
+ error = ENOMEM;
+ if (error == 0) {
+ *maddr = ioremap_nocache(start, 256);
+ if (*maddr == NULL) {
+ error = ENOMEM;
+ release_mem_region(start, 0x1000);
+ }
+ }
+ } else
+ error = ENOMEM;
+ return (error);
+}
+
+int
+ahc_pci_map_registers(struct ahc_softc *ahc)
+{
+ uint32_t command;
+ u_long base;
+ uint8_t __iomem *maddr;
+ int error;
+
+ /*
+ * If its allowed, we prefer memory mapped access.
+ */
+ command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
+ command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+ base = 0;
+ maddr = NULL;
+ error = ahc_linux_pci_reserve_mem_region(ahc, &base, &maddr);
+ if (error == 0) {
+ ahc->platform_data->mem_busaddr = base;
+ ahc->tag = BUS_SPACE_MEMIO;
+ ahc->bsh.maddr = maddr;
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+ command | PCIM_CMD_MEMEN, 4);
+
+ /*
+ * Do a quick test to see if memory mapped
+ * I/O is functioning correctly.
+ */
+ if (ahc_pci_test_register_access(ahc) != 0) {
+
+ printf("aic7xxx: PCI Device %d:%d:%d "
+ "failed memory mapped test. Using PIO.\n",
+ ahc_get_pci_bus(ahc->dev_softc),
+ ahc_get_pci_slot(ahc->dev_softc),
+ ahc_get_pci_function(ahc->dev_softc));
+ iounmap(maddr);
+ release_mem_region(ahc->platform_data->mem_busaddr,
+ 0x1000);
+ ahc->bsh.maddr = NULL;
+ maddr = NULL;
+ } else
+ command |= PCIM_CMD_MEMEN;
+ } else {
+ printf("aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
+ "unavailable. Cannot memory map device.\n",
+ ahc_get_pci_bus(ahc->dev_softc),
+ ahc_get_pci_slot(ahc->dev_softc),
+ ahc_get_pci_function(ahc->dev_softc),
+ base);
+ }
+
+ /*
+ * We always prefer memory mapped access.
+ */
+ if (maddr == NULL) {
+
+ error = ahc_linux_pci_reserve_io_region(ahc, &base);
+ if (error == 0) {
+ ahc->tag = BUS_SPACE_PIO;
+ ahc->bsh.ioport = base;
+ command |= PCIM_CMD_PORTEN;
+ } else {
+ printf("aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
+ "unavailable. Cannot map device.\n",
+ ahc_get_pci_bus(ahc->dev_softc),
+ ahc_get_pci_slot(ahc->dev_softc),
+ ahc_get_pci_function(ahc->dev_softc),
+ base);
+ }
+ }
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
+ return (error);
+}
+
+int
+ahc_pci_map_int(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = request_irq(ahc->dev_softc->irq, ahc_linux_isr,
+ SA_SHIRQ, "aic7xxx", ahc);
+ if (error == 0)
+ ahc->platform_data->irq = ahc->dev_softc->irq;
+
+ return (-error);
+}
+
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c
new file mode 100644
index 000000000000..7ddcc97fb243
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c
@@ -0,0 +1,2407 @@
+/*
+ * Product specific probe and attach routines for:
+ * 3940, 2940, aic7895, aic7890, aic7880,
+ * aic7870, aic7860 and aic7850 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#69 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#include "aic7xxx_pci.h"
+
+static __inline uint64_t
+ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+ uint64_t id;
+
+ id = subvendor
+ | (subdevice << 16)
+ | ((uint64_t)vendor << 32)
+ | ((uint64_t)device << 48);
+
+ return (id);
+}
+
+#define AHC_PCI_IOADDR PCIR_MAPS /* I/O Address */
+#define AHC_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define DEVID_9005_TYPE_HBA 0x0 /* Standard Card */
+#define DEVID_9005_TYPE_AAA 0x3 /* RAID Card */
+#define DEVID_9005_TYPE_SISL 0x5 /* Container ROMB */
+#define DEVID_9005_TYPE_MB 0xF /* On Motherboard */
+
+#define DEVID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define DEVID_9005_MAXRATE_U160 0x0
+#define DEVID_9005_MAXRATE_ULTRA2 0x1
+#define DEVID_9005_MAXRATE_ULTRA 0x2
+#define DEVID_9005_MAXRATE_FAST 0x3
+
+#define DEVID_9005_MFUNC(id) (((id) & 0x40) >> 6)
+
+#define DEVID_9005_CLASS(id) (((id) & 0xFF00) >> 8)
+#define DEVID_9005_CLASS_SPI 0x0 /* Parallel SCSI */
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define SUBID_9005_TYPE_MB 0xF /* On Motherboard */
+#define SUBID_9005_TYPE_CARD 0x0 /* Standard Card */
+#define SUBID_9005_TYPE_LCCARD 0x1 /* Low Cost Card */
+#define SUBID_9005_TYPE_RAID 0x3 /* Combined with Raid */
+
+#define SUBID_9005_TYPE_KNOWN(id) \
+ ((((id) & 0xF) == SUBID_9005_TYPE_MB) \
+ || (((id) & 0xF) == SUBID_9005_TYPE_CARD) \
+ || (((id) & 0xF) == SUBID_9005_TYPE_LCCARD) \
+ || (((id) & 0xF) == SUBID_9005_TYPE_RAID))
+
+#define SUBID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define SUBID_9005_MAXRATE_ULTRA2 0x0
+#define SUBID_9005_MAXRATE_ULTRA 0x1
+#define SUBID_9005_MAXRATE_U160 0x2
+#define SUBID_9005_MAXRATE_RESERVED 0x3
+
+#define SUBID_9005_SEEPTYPE(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? ((id) & 0xC0) >> 6 \
+ : ((id) & 0x300) >> 8)
+#define SUBID_9005_SEEPTYPE_NONE 0x0
+#define SUBID_9005_SEEPTYPE_1K 0x1
+#define SUBID_9005_SEEPTYPE_2K_4K 0x2
+#define SUBID_9005_SEEPTYPE_RESERVED 0x3
+#define SUBID_9005_AUTOTERM(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? (((id) & 0x400) >> 10) == 0 \
+ : (((id) & 0x40) >> 6) == 0)
+
+#define SUBID_9005_NUMCHAN(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? ((id) & 0x300) >> 8 \
+ : ((id) & 0xC00) >> 10)
+
+#define SUBID_9005_LEGACYCONN(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? 0 \
+ : ((id) & 0x80) >> 7)
+
+#define SUBID_9005_MFUNCENB(id) \
+ ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB) \
+ ? ((id) & 0x800) >> 11 \
+ : ((id) & 0x1000) >> 12)
+/*
+ * Informational only. Should use chip register to be
+ * certain, but may be use in identification strings.
+ */
+#define SUBID_9005_CARD_SCSIWIDTH_MASK 0x2000
+#define SUBID_9005_CARD_PCIWIDTH_MASK 0x4000
+#define SUBID_9005_CARD_SEDIFF_MASK 0x8000
+
+static ahc_device_setup_t ahc_aic785X_setup;
+static ahc_device_setup_t ahc_aic7860_setup;
+static ahc_device_setup_t ahc_apa1480_setup;
+static ahc_device_setup_t ahc_aic7870_setup;
+static ahc_device_setup_t ahc_aha394X_setup;
+static ahc_device_setup_t ahc_aha494X_setup;
+static ahc_device_setup_t ahc_aha398X_setup;
+static ahc_device_setup_t ahc_aic7880_setup;
+static ahc_device_setup_t ahc_aha2940Pro_setup;
+static ahc_device_setup_t ahc_aha394XU_setup;
+static ahc_device_setup_t ahc_aha398XU_setup;
+static ahc_device_setup_t ahc_aic7890_setup;
+static ahc_device_setup_t ahc_aic7892_setup;
+static ahc_device_setup_t ahc_aic7895_setup;
+static ahc_device_setup_t ahc_aic7896_setup;
+static ahc_device_setup_t ahc_aic7899_setup;
+static ahc_device_setup_t ahc_aha29160C_setup;
+static ahc_device_setup_t ahc_raid_setup;
+static ahc_device_setup_t ahc_aha394XX_setup;
+static ahc_device_setup_t ahc_aha494XX_setup;
+static ahc_device_setup_t ahc_aha398XX_setup;
+
+struct ahc_pci_identity ahc_pci_ident_table [] =
+{
+ /* aic7850 based controllers */
+ {
+ ID_AHA_2902_04_10_15_20C_30C,
+ ID_ALL_MASK,
+ "Adaptec 2902/04/10/15/20C/30C SCSI adapter",
+ ahc_aic785X_setup
+ },
+ /* aic7860 based controllers */
+ {
+ ID_AHA_2930CU,
+ ID_ALL_MASK,
+ "Adaptec 2930CU SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_1480A & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 1480A Ultra SCSI adapter",
+ ahc_apa1480_setup
+ },
+ {
+ ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940A Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940A/CN Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2930C Ultra SCSI adapter (VAR)",
+ ahc_aic7860_setup
+ },
+ /* aic7870 based controllers */
+ {
+ ID_AHA_2940,
+ ID_ALL_MASK,
+ "Adaptec 2940 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AHA_3940,
+ ID_ALL_MASK,
+ "Adaptec 3940 SCSI adapter",
+ ahc_aha394X_setup
+ },
+ {
+ ID_AHA_398X,
+ ID_ALL_MASK,
+ "Adaptec 398X SCSI RAID adapter",
+ ahc_aha398X_setup
+ },
+ {
+ ID_AHA_2944,
+ ID_ALL_MASK,
+ "Adaptec 2944 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AHA_3944,
+ ID_ALL_MASK,
+ "Adaptec 3944 SCSI adapter",
+ ahc_aha394X_setup
+ },
+ {
+ ID_AHA_4944,
+ ID_ALL_MASK,
+ "Adaptec 4944 SCSI adapter",
+ ahc_aha494X_setup
+ },
+ /* aic7880 based controllers */
+ {
+ ID_AHA_2940U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_3940U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 3940 Ultra SCSI adapter",
+ ahc_aha394XU_setup
+ },
+ {
+ ID_AHA_2944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2944 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_3944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 3944 Ultra SCSI adapter",
+ ahc_aha394XU_setup
+ },
+ {
+ ID_AHA_398XU & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 398X Ultra SCSI RAID adapter",
+ ahc_aha398XU_setup
+ },
+ {
+ /*
+ * XXX Don't know the slot numbers
+ * so we can't identify channels
+ */
+ ID_AHA_4944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 4944 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_2930U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2930 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940 Pro Ultra SCSI adapter",
+ ahc_aha2940Pro_setup
+ },
+ {
+ ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940/CN Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ /* Ignore all SISL (AAC on MB) based controllers. */
+ {
+ ID_9005_SISL_ID,
+ ID_9005_SISL_MASK,
+ NULL,
+ NULL
+ },
+ /* aic7890 based controllers */
+ {
+ ID_AHA_2930U2,
+ ID_ALL_MASK,
+ "Adaptec 2930 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2B,
+ ID_ALL_MASK,
+ "Adaptec 2940B Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2_OEM,
+ ID_ALL_MASK,
+ "Adaptec 2940 Ultra2 SCSI adapter (OEM)",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2,
+ ID_ALL_MASK,
+ "Adaptec 2940 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2950U2B,
+ ID_ALL_MASK,
+ "Adaptec 2950 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AIC7890_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7890/91 Ultra2 SCSI adapter (ARO)",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AAA_131U2,
+ ID_ALL_MASK,
+ "Adaptec AAA-131 Ultra2 RAID adapter",
+ ahc_aic7890_setup
+ },
+ /* aic7892 based controllers */
+ {
+ ID_AHA_29160,
+ ID_ALL_MASK,
+ "Adaptec 29160 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160N,
+ ID_ALL_MASK,
+ "Adaptec 29160N Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160C,
+ ID_ALL_MASK,
+ "Adaptec 29160C Ultra160 SCSI adapter",
+ ahc_aha29160C_setup
+ },
+ {
+ ID_AHA_29160B,
+ ID_ALL_MASK,
+ "Adaptec 29160B Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_19160B,
+ ID_ALL_MASK,
+ "Adaptec 19160B Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AIC7892_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7892 Ultra160 SCSI adapter (ARO)",
+ ahc_aic7892_setup
+ },
+ /* aic7895 based controllers */
+ {
+ ID_AHA_2940U_DUAL,
+ ID_ALL_MASK,
+ "Adaptec 2940/DUAL Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AHA_3940AU,
+ ID_ALL_MASK,
+ "Adaptec 3940A Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AHA_3944AU,
+ ID_ALL_MASK,
+ "Adaptec 3944A Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AIC7895_ARO,
+ ID_AIC7895_ARO_MASK,
+ "Adaptec aic7895 Ultra SCSI adapter (ARO)",
+ ahc_aic7895_setup
+ },
+ /* aic7896/97 based controllers */
+ {
+ ID_AHA_3950U2B_0,
+ ID_ALL_MASK,
+ "Adaptec 3950B Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2B_1,
+ ID_ALL_MASK,
+ "Adaptec 3950B Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2D_0,
+ ID_ALL_MASK,
+ "Adaptec 3950D Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2D_1,
+ ID_ALL_MASK,
+ "Adaptec 3950D Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AIC7896_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7896/97 Ultra2 SCSI adapter (ARO)",
+ ahc_aic7896_setup
+ },
+ /* aic7899 based controllers */
+ {
+ ID_AHA_3960D,
+ ID_ALL_MASK,
+ "Adaptec 3960D Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AHA_3960D_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AIC7899_ARO,
+ ID_ALL_MASK,
+ "Adaptec aic7899 Ultra160 SCSI adapter (ARO)",
+ ahc_aic7899_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7850 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7850 SCSI adapter",
+ ahc_aic785X_setup
+ },
+ {
+ ID_AIC7855 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7855 SCSI adapter",
+ ahc_aic785X_setup
+ },
+ {
+ ID_AIC7859 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7859 SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AIC7860 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7860 Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AIC7870 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7870 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AIC7880 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7880 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AIC7890 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7890/91 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AIC7892 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7892 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AIC7895 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7895 Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AIC7896 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7896/97 Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AIC7899 & ID_9005_GENERIC_MASK,
+ ID_9005_GENERIC_MASK,
+ "Adaptec aic7899 Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AIC7810 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7810 RAID memory controller",
+ ahc_raid_setup
+ },
+ {
+ ID_AIC7815 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7815 RAID memory controller",
+ ahc_raid_setup
+ }
+};
+
+const u_int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table);
+
+#define AHC_394X_SLOT_CHANNEL_A 4
+#define AHC_394X_SLOT_CHANNEL_B 5
+
+#define AHC_398X_SLOT_CHANNEL_A 4
+#define AHC_398X_SLOT_CHANNEL_B 8
+#define AHC_398X_SLOT_CHANNEL_C 12
+
+#define AHC_494X_SLOT_CHANNEL_A 4
+#define AHC_494X_SLOT_CHANNEL_B 5
+#define AHC_494X_SLOT_CHANNEL_C 6
+#define AHC_494X_SLOT_CHANNEL_D 7
+
+#define DEVCONFIG 0x40
+#define PCIERRGENDIS 0x80000000ul
+#define SCBSIZE32 0x00010000ul /* aic789X only */
+#define REXTVALID 0x00001000ul /* ultra cards only */
+#define MPORTMODE 0x00000400ul /* aic7870+ only */
+#define RAMPSM 0x00000200ul /* aic7870+ only */
+#define VOLSENSE 0x00000100ul
+#define PCI64BIT 0x00000080ul /* 64Bit PCI bus (Ultra2 Only)*/
+#define SCBRAMSEL 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+
+#define CSIZE_LATTIME 0x0c
+#define CACHESIZE 0x0000003ful /* only 5 bits */
+#define LATTIME 0x0000ff00ul
+
+/* PCI STATUS definitions */
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+static int ahc_9005_subdevinfo_valid(uint16_t vendor, uint16_t device,
+ uint16_t subvendor, uint16_t subdevice);
+static int ahc_ext_scbram_present(struct ahc_softc *ahc);
+static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
+ int pcheck, int fast, int large);
+static void ahc_probe_ext_scbram(struct ahc_softc *ahc);
+static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+static void ahc_parse_pci_eeprom(struct ahc_softc *ahc,
+ struct seeprom_config *sc);
+static void configure_termination(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd,
+ u_int adapter_control,
+ u_int *sxfrctl1);
+
+static void ahc_new_term_detect(struct ahc_softc *ahc,
+ int *enableSEC_low,
+ int *enableSEC_high,
+ int *enablePRI_low,
+ int *enablePRI_high,
+ int *eeprom_present);
+static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *internal68_present,
+ int *externalcable_present,
+ int *eeprom_present);
+static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *externalcable_present,
+ int *eeprom_present);
+static void write_brdctl(struct ahc_softc *ahc, uint8_t value);
+static uint8_t read_brdctl(struct ahc_softc *ahc);
+static void ahc_pci_intr(struct ahc_softc *ahc);
+static int ahc_pci_chip_init(struct ahc_softc *ahc);
+static int ahc_pci_suspend(struct ahc_softc *ahc);
+static int ahc_pci_resume(struct ahc_softc *ahc);
+
+static int
+ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor,
+ uint16_t subdevice, uint16_t subvendor)
+{
+ int result;
+
+ /* Default to invalid. */
+ result = 0;
+ if (vendor == 0x9005
+ && subvendor == 0x9005
+ && subdevice != device
+ && SUBID_9005_TYPE_KNOWN(subdevice) != 0) {
+
+ switch (SUBID_9005_TYPE(subdevice)) {
+ case SUBID_9005_TYPE_MB:
+ break;
+ case SUBID_9005_TYPE_CARD:
+ case SUBID_9005_TYPE_LCCARD:
+ /*
+ * Currently only trust Adaptec cards to
+ * get the sub device info correct.
+ */
+ if (DEVID_9005_TYPE(device) == DEVID_9005_TYPE_HBA)
+ result = 1;
+ break;
+ case SUBID_9005_TYPE_RAID:
+ break;
+ default:
+ break;
+ }
+ }
+ return (result);
+}
+
+struct ahc_pci_identity *
+ahc_find_pci_device(ahc_dev_softc_t pci)
+{
+ uint64_t full_id;
+ uint16_t device;
+ uint16_t vendor;
+ uint16_t subdevice;
+ uint16_t subvendor;
+ struct ahc_pci_identity *entry;
+ u_int i;
+
+ vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+ device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+ subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+ subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ full_id = ahc_compose_id(device, vendor, subdevice, subvendor);
+
+ /*
+ * If the second function is not hooked up, ignore it.
+ * Unfortunately, not all MB vendors implement the
+ * subdevice ID as per the Adaptec spec, so do our best
+ * to sanity check it prior to accepting the subdevice
+ * ID as valid.
+ */
+ if (ahc_get_pci_function(pci) > 0
+ && ahc_9005_subdevinfo_valid(vendor, device, subvendor, subdevice)
+ && SUBID_9005_MFUNCENB(subdevice) == 0)
+ return (NULL);
+
+ for (i = 0; i < ahc_num_pci_devs; i++) {
+ entry = &ahc_pci_ident_table[i];
+ if (entry->full_id == (full_id & entry->id_mask)) {
+ /* Honor exclusion entries. */
+ if (entry->name == NULL)
+ return (NULL);
+ return (entry);
+ }
+ }
+ return (NULL);
+}
+
+int
+ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
+{
+ u_long l;
+ u_int command;
+ u_int our_id;
+ u_int sxfrctl1;
+ u_int scsiseq;
+ u_int dscommand0;
+ uint32_t devconfig;
+ int error;
+ uint8_t sblkctl;
+
+ our_id = 0;
+ error = entry->setup(ahc);
+ if (error != 0)
+ return (error);
+ ahc->chip |= AHC_PCI;
+ ahc->description = entry->name;
+
+ pci_set_power_state(ahc->dev_softc, AHC_POWER_STATE_D0);
+
+ error = ahc_pci_map_registers(ahc);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Before we continue probing the card, ensure that
+ * its interrupts are *disabled*. We don't want
+ * a misstep to hang the machine in an interrupt
+ * storm.
+ */
+ ahc_intr_enable(ahc, FALSE);
+
+ devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+
+ /*
+ * If we need to support high memory, enable dual
+ * address cycles. This bit must be set to enable
+ * high address bit generation even if we are on a
+ * 64bit bus (PCI64BIT set in devconfig).
+ */
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+
+ if (bootverbose)
+ printf("%s: Enabling 39Bit Addressing\n",
+ ahc_name(ahc));
+ devconfig |= DACEN;
+ }
+
+ /* Ensure that pci error generation, a test feature, is disabled. */
+ devconfig |= PCIERRGENDIS;
+
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+
+ /* Ensure busmastering is enabled */
+ command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/2);
+ command |= PCIM_CMD_BUSMASTEREN;
+
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/2);
+
+ /* On all PCI adapters, we allow SCB paging */
+ ahc->flags |= AHC_PAGESCBS;
+
+ error = ahc_softc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Disable PCI parity error checking. Users typically
+ * do this to work around broken PCI chipsets that get
+ * the parity timing wrong and thus generate lots of spurious
+ * errors. The chip only allows us to disable *all* parity
+ * error reporting when doing this, so CIO bus, scb ram, and
+ * scratch ram parity errors will be ignored too.
+ */
+ if ((ahc->flags & AHC_DISABLE_PCI_PERR) != 0)
+ ahc->seqctl |= FAILDIS;
+
+ ahc->bus_intr = ahc_pci_intr;
+ ahc->bus_chip_init = ahc_pci_chip_init;
+ ahc->bus_suspend = ahc_pci_suspend;
+ ahc->bus_resume = ahc_pci_resume;
+
+ /* Remeber how the card was setup in case there is no SEEPROM */
+ if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) {
+ ahc_pause(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+ else
+ our_id = ahc_inb(ahc, SCSIID) & OID;
+ sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ } else {
+ sxfrctl1 = STPWEN;
+ our_id = 7;
+ scsiseq = 0;
+ }
+
+ error = ahc_reset(ahc, /*reinit*/FALSE);
+ if (error != 0)
+ return (ENXIO);
+
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ /* Perform ALT-Mode Setup */
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc_outb(ahc, OPTIONMODE,
+ OPTIONMODE_DEFAULTS|AUTOACKEN|BUSFREEREV|EXPPHASEDIS);
+ ahc_outb(ahc, SFUNCT, sfunct);
+
+ /* Normal mode setup */
+ ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN
+ |TARGCRCENDEN);
+ }
+
+ dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ dscommand0 |= MPARCKEN|CACHETHEN;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ /*
+ * DPARCKEN doesn't work correctly on
+ * some MBs so don't use it.
+ */
+ dscommand0 &= ~DPARCKEN;
+ }
+
+ /*
+ * Handle chips that must have cache line
+ * streaming (dis/en)abled.
+ */
+ if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
+ dscommand0 |= CACHETHEN;
+
+ if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
+ dscommand0 &= ~CACHETHEN;
+
+ ahc_outb(ahc, DSCOMMAND0, dscommand0);
+
+ ahc->pci_cachesize =
+ ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME,
+ /*bytes*/1) & CACHESIZE;
+ ahc->pci_cachesize *= 4;
+
+ if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0
+ && ahc->pci_cachesize == 4) {
+
+ ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME,
+ 0, /*bytes*/1);
+ ahc->pci_cachesize = 0;
+ }
+
+ /*
+ * We cannot perform ULTRA speeds without the presense
+ * of the external precision resistor.
+ */
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ uint32_t devconfig;
+
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ if ((devconfig & REXTVALID) == 0)
+ ahc->features &= ~AHC_ULTRA;
+ }
+
+ /* See if we have a SEEPROM and perform auto-term */
+ check_extport(ahc, &sxfrctl1);
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_MAX|WR_DFTHRSH_MAX);
+ } else {
+ ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100);
+ }
+
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ /*
+ * PCI Adapter default setup
+ * Should only be used if the adapter does not have
+ * a SEEPROM.
+ */
+ /* See if someone else set us up already */
+ if ((ahc->flags & AHC_NO_BIOS_INIT) == 0
+ && scsiseq != 0) {
+ printf("%s: Using left over BIOS settings\n",
+ ahc_name(ahc));
+ ahc->flags &= ~AHC_USEDEFAULTS;
+ ahc->flags |= AHC_BIOS_ENABLED;
+ } else {
+ /*
+ * Assume only one connector and always turn
+ * on termination.
+ */
+ our_id = 0x07;
+ sxfrctl1 = STPWEN;
+ }
+ ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
+
+ ahc->our_id = our_id;
+ }
+
+ /*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+ ahc_probe_ext_scbram(ahc);
+
+ /*
+ * Record our termination setting for the
+ * generic initialization routine.
+ */
+ if ((sxfrctl1 & STPWEN) != 0)
+ ahc->flags |= AHC_TERM_ENB_A;
+
+ /*
+ * Save chip register configuration data for chip resets
+ * that occur during runtime and resume events.
+ */
+ ahc->bus_softc.pci_softc.devconfig =
+ ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+ ahc->bus_softc.pci_softc.command =
+ ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1);
+ ahc->bus_softc.pci_softc.csize_lattime =
+ ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME, /*bytes*/1);
+ ahc->bus_softc.pci_softc.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ ahc->bus_softc.pci_softc.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc->bus_softc.pci_softc.optionmode = ahc_inb(ahc, OPTIONMODE);
+ ahc->bus_softc.pci_softc.targcrccnt = ahc_inw(ahc, TARGCRCCNT);
+ ahc_outb(ahc, SFUNCT, sfunct);
+ ahc->bus_softc.pci_softc.crccontrol1 =
+ ahc_inb(ahc, CRCCONTROL1);
+ }
+ if ((ahc->features & AHC_MULTI_FUNC) != 0)
+ ahc->bus_softc.pci_softc.scbbaddr = ahc_inb(ahc, SCBBADDR);
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc->bus_softc.pci_softc.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
+
+ /* Core initialization */
+ error = ahc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Allow interrupts now that we are completely setup.
+ */
+ error = ahc_pci_map_int(ahc);
+ if (error != 0)
+ return (error);
+
+ ahc_list_lock(&l);
+ /*
+ * Link this softc in with all other ahc instances.
+ */
+ ahc_softc_insert(ahc);
+ ahc_list_unlock(&l);
+ return (0);
+}
+
+/*
+ * Test for the presense of external sram in an
+ * "unshared" configuration.
+ */
+static int
+ahc_ext_scbram_present(struct ahc_softc *ahc)
+{
+ u_int chip;
+ int ramps;
+ int single_user;
+ uint32_t devconfig;
+
+ chip = ahc->chip & AHC_CHIPID_MASK;
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ single_user = (devconfig & MPORTMODE) != 0;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
+ else if (chip == AHC_AIC7895 || chip == AHC_AIC7895C)
+ /*
+ * External SCBRAM arbitration is flakey
+ * on these chips. Unfortunately this means
+ * we don't use the extra SCB ram space on the
+ * 3940AUW.
+ */
+ ramps = 0;
+ else if (chip >= AHC_AIC7870)
+ ramps = (devconfig & RAMPSM) != 0;
+ else
+ ramps = 0;
+
+ if (ramps && single_user)
+ return (1);
+ return (0);
+}
+
+/*
+ * Enable external scbram.
+ */
+static void
+ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
+ int fast, int large)
+{
+ uint32_t devconfig;
+
+ if (ahc->features & AHC_MULTI_FUNC) {
+ /*
+ * Set the SCB Base addr (highest address bit)
+ * depending on which channel we are.
+ */
+ ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc));
+ }
+
+ ahc->flags &= ~AHC_LSCBS_ENABLED;
+ if (large)
+ ahc->flags |= AHC_LSCBS_ENABLED;
+ devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int dscommand0;
+
+ dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ if (enable)
+ dscommand0 &= ~INTSCBRAMSEL;
+ else
+ dscommand0 |= INTSCBRAMSEL;
+ if (large)
+ dscommand0 &= ~USCBSIZE32;
+ else
+ dscommand0 |= USCBSIZE32;
+ ahc_outb(ahc, DSCOMMAND0, dscommand0);
+ } else {
+ if (fast)
+ devconfig &= ~EXTSCBTIME;
+ else
+ devconfig |= EXTSCBTIME;
+ if (enable)
+ devconfig &= ~SCBRAMSEL;
+ else
+ devconfig |= SCBRAMSEL;
+ if (large)
+ devconfig &= ~SCBSIZE32;
+ else
+ devconfig |= SCBSIZE32;
+ }
+ if (pcheck)
+ devconfig |= EXTSCBPEN;
+ else
+ devconfig &= ~EXTSCBPEN;
+
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+}
+
+/*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+static void
+ahc_probe_ext_scbram(struct ahc_softc *ahc)
+{
+ int num_scbs;
+ int test_num_scbs;
+ int enable;
+ int pcheck;
+ int fast;
+ int large;
+
+ enable = FALSE;
+ pcheck = FALSE;
+ fast = FALSE;
+ large = FALSE;
+ num_scbs = 0;
+
+ if (ahc_ext_scbram_present(ahc) == 0)
+ goto done;
+
+ /*
+ * Probe for the best parameters to use.
+ */
+ ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
+ num_scbs = ahc_probe_scbs(ahc);
+ if (num_scbs == 0) {
+ /* The SRAM wasn't really present. */
+ goto done;
+ }
+ enable = TRUE;
+
+ /*
+ * Clear any outstanding parity error
+ * and ensure that parity error reporting
+ * is enabled.
+ */
+ ahc_outb(ahc, SEQCTL, 0);
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+ /* Now see if we can do parity */
+ ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
+ num_scbs = ahc_probe_scbs(ahc);
+ if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+ || (ahc_inb(ahc, ERROR) & MPARERR) == 0)
+ pcheck = TRUE;
+
+ /* Clear any resulting parity error */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+ /* Now see if we can do fast timing */
+ ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
+ test_num_scbs = ahc_probe_scbs(ahc);
+ if (test_num_scbs == num_scbs
+ && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+ || (ahc_inb(ahc, ERROR) & MPARERR) == 0))
+ fast = TRUE;
+
+ /*
+ * See if we can use large SCBs and still maintain
+ * the same overall count of SCBs.
+ */
+ if ((ahc->features & AHC_LARGE_SCBS) != 0) {
+ ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
+ test_num_scbs = ahc_probe_scbs(ahc);
+ if (test_num_scbs >= num_scbs) {
+ large = TRUE;
+ num_scbs = test_num_scbs;
+ if (num_scbs >= 64) {
+ /*
+ * We have enough space to move the
+ * "busy targets table" into SCB space
+ * and make it qualify all the way to the
+ * lun level.
+ */
+ ahc->flags |= AHC_SCB_BTT;
+ }
+ }
+ }
+done:
+ /*
+ * Disable parity error reporting until we
+ * can load instruction ram.
+ */
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+ /* Clear any latched parity error */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+ if (bootverbose && enable) {
+ printf("%s: External SRAM, %s access%s, %dbytes/SCB\n",
+ ahc_name(ahc), fast ? "fast" : "slow",
+ pcheck ? ", parity checking enabled" : "",
+ large ? 64 : 32);
+ }
+ ahc_scbram_config(ahc, enable, pcheck, fast, large);
+}
+
+/*
+ * Perform some simple tests that should catch situations where
+ * our registers are invalidly mapped.
+ */
+int
+ahc_pci_test_register_access(struct ahc_softc *ahc)
+{
+ int error;
+ u_int status1;
+ uint32_t cmd;
+ uint8_t hcntrl;
+
+ error = EIO;
+
+ /*
+ * Enable PCI error interrupt status, but suppress NMIs
+ * generated by SERR raised due to target aborts.
+ */
+ cmd = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/2);
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+ cmd & ~PCIM_CMD_SERRESPEN, /*bytes*/2);
+
+ /*
+ * First a simple test to see if any
+ * registers can be read. Reading
+ * HCNTRL has no side effects and has
+ * at least one bit that is guaranteed to
+ * be zero so it is a good register to
+ * use for this test.
+ */
+ hcntrl = ahc_inb(ahc, HCNTRL);
+ if (hcntrl == 0xFF)
+ goto fail;
+
+ /*
+ * Next create a situation where write combining
+ * or read prefetching could be initiated by the
+ * CPU or host bridge. Our device does not support
+ * either, so look for data corruption and/or flagged
+ * PCI errors. First pause without causing another
+ * chip reset.
+ */
+ hcntrl &= ~CHIPRST;
+ ahc_outb(ahc, HCNTRL, hcntrl|PAUSE);
+ while (ahc_is_paused(ahc) == 0)
+ ;
+
+ /* Clear any PCI errors that occurred before our driver attached. */
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+ status1, /*bytes*/1);
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+
+ ahc_outb(ahc, SEQCTL, PERRORDIS);
+ ahc_outb(ahc, SCBPTR, 0);
+ ahc_outl(ahc, SCB_BASE, 0x5aa555aa);
+ if (ahc_inl(ahc, SCB_BASE) != 0x5aa555aa)
+ goto fail;
+
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ if ((status1 & STA) != 0)
+ goto fail;
+
+ error = 0;
+
+fail:
+ /* Silently clear any latched errors. */
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+ ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+ status1, /*bytes*/1);
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, cmd, /*bytes*/2);
+ return (error);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static void
+check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+{
+ struct seeprom_descriptor sd;
+ struct seeprom_config *sc;
+ int have_seeprom;
+ int have_autoterm;
+
+ sd.sd_ahc = ahc;
+ sd.sd_control_offset = SEECTL;
+ sd.sd_status_offset = SEECTL;
+ sd.sd_dataout_offset = SEECTL;
+ sc = ahc->seep_config;
+
+ /*
+ * For some multi-channel devices, the c46 is simply too
+ * small to work. For the other controller types, we can
+ * get our information from either SEEPROM type. Set the
+ * type to start our probe with accordingly.
+ */
+ if (ahc->flags & AHC_LARGE_SEEPROM)
+ sd.sd_chip = C56_66;
+ else
+ sd.sd_chip = C46;
+
+ sd.sd_MS = SEEMS;
+ sd.sd_RDY = SEERDY;
+ sd.sd_CS = SEECS;
+ sd.sd_CK = SEECK;
+ sd.sd_DO = SEEDO;
+ sd.sd_DI = SEEDI;
+
+ have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+ if (have_seeprom) {
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahc_name(ahc));
+
+ for (;;) {
+ u_int start_addr;
+
+ start_addr = 32 * (ahc->channel - 'A');
+
+ have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+ start_addr,
+ sizeof(*sc)/2);
+
+ if (have_seeprom)
+ have_seeprom = ahc_verify_cksum(sc);
+
+ if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+ if (bootverbose) {
+ if (have_seeprom == 0)
+ printf ("checksum error\n");
+ else
+ printf ("done.\n");
+ }
+ break;
+ }
+ sd.sd_chip = C56_66;
+ }
+ ahc_release_seeprom(&sd);
+ }
+
+ if (!have_seeprom) {
+ /*
+ * Pull scratch ram settings and treat them as
+ * if they are the contents of an seeprom if
+ * the 'ADPT' signature is found in SCB2.
+ * We manually compose the data as 16bit values
+ * to avoid endian issues.
+ */
+ ahc_outb(ahc, SCBPTR, 2);
+ if (ahc_inb(ahc, SCB_BASE) == 'A'
+ && ahc_inb(ahc, SCB_BASE + 1) == 'D'
+ && ahc_inb(ahc, SCB_BASE + 2) == 'P'
+ && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
+ uint16_t *sc_data;
+ int i;
+
+ sc_data = (uint16_t *)sc;
+ for (i = 0; i < 32; i++, sc_data++) {
+ int j;
+
+ j = i * 2;
+ *sc_data = ahc_inb(ahc, SRAM_BASE + j)
+ | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
+ }
+ have_seeprom = ahc_verify_cksum(sc);
+ if (have_seeprom)
+ ahc->flags |= AHC_SCB_CONFIG_USED;
+ }
+ /*
+ * Clear any SCB parity errors in case this data and
+ * its associated parity was not initialized by the BIOS
+ */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+ }
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available.\n", ahc_name(ahc));
+ ahc->flags |= AHC_USEDEFAULTS;
+ free(ahc->seep_config, M_DEVBUF);
+ ahc->seep_config = NULL;
+ sc = NULL;
+ } else {
+ ahc_parse_pci_eeprom(ahc, sc);
+ }
+
+ /*
+ * Cards that have the external logic necessary to talk to
+ * a SEEPROM, are almost certain to have the remaining logic
+ * necessary for auto-termination control. This assumption
+ * hasn't failed yet...
+ */
+ have_autoterm = have_seeprom;
+
+ /*
+ * Some low-cost chips have SEEPROM and auto-term control built
+ * in, instead of using a GAL. They can tell us directly
+ * if the termination logic is enabled.
+ */
+ if ((ahc->features & AHC_SPIOCAP) != 0) {
+ if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
+ have_autoterm = FALSE;
+ }
+
+ if (have_autoterm) {
+ ahc->flags |= AHC_HAS_TERM_LOGIC;
+ ahc_acquire_seeprom(ahc, &sd);
+ configure_termination(ahc, &sd, sc->adapter_control, sxfrctl1);
+ ahc_release_seeprom(&sd);
+ } else if (have_seeprom) {
+ *sxfrctl1 &= ~STPWEN;
+ if ((sc->adapter_control & CFSTERM) != 0)
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose)
+ printf("%s: Low byte termination %sabled\n",
+ ahc_name(ahc),
+ (*sxfrctl1 & STPWEN) ? "en" : "dis");
+ }
+}
+
+static void
+ahc_parse_pci_eeprom(struct ahc_softc *ahc, struct seeprom_config *sc)
+{
+ /*
+ * Put the data we've collected down into SRAM
+ * where ahc_init will find it.
+ */
+ int i;
+ int max_targ = sc->max_targets & CFMAXTARG;
+ u_int scsi_conf;
+ uint16_t discenable;
+ uint16_t ultraenb;
+
+ discenable = 0;
+ ultraenb = 0;
+ if ((sc->adapter_control & CFULTRAEN) != 0) {
+ /*
+ * Determine if this adapter has a "newstyle"
+ * SEEPROM format.
+ */
+ for (i = 0; i < max_targ; i++) {
+ if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0) {
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < max_targ; i++) {
+ u_int scsirate;
+ uint16_t target_mask;
+
+ target_mask = 0x01 << i;
+ if (sc->device_flags[i] & CFDISC)
+ discenable |= target_mask;
+ if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
+ if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0)
+ ultraenb |= target_mask;
+ } else if ((sc->adapter_control & CFULTRAEN) != 0) {
+ ultraenb |= target_mask;
+ }
+ if ((sc->device_flags[i] & CFXFER) == 0x04
+ && (ultraenb & target_mask) != 0) {
+ /* Treat 10MHz as a non-ultra speed */
+ sc->device_flags[i] &= ~CFXFER;
+ ultraenb &= ~target_mask;
+ }
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+
+ if (sc->device_flags[i] & CFSYNCH)
+ offset = MAX_OFFSET_ULTRA2;
+ else
+ offset = 0;
+ ahc_outb(ahc, TARG_OFFSET + i, offset);
+
+ /*
+ * The ultra enable bits contain the
+ * high bit of the ultra2 sync rate
+ * field.
+ */
+ scsirate = (sc->device_flags[i] & CFXFER)
+ | ((ultraenb & target_mask) ? 0x8 : 0x0);
+ if (sc->device_flags[i] & CFWIDEB)
+ scsirate |= WIDEXFER;
+ } else {
+ scsirate = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFSYNCH)
+ scsirate |= SOFS;
+ if (sc->device_flags[i] & CFWIDEB)
+ scsirate |= WIDEXFER;
+ }
+ ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
+ }
+ ahc->our_id = sc->brtime_id & CFSCSIID;
+
+ scsi_conf = (ahc->our_id & 0x7);
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc->adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ ahc->flags |= (sc->adapter_control & CFBOOTCHAN) >> CFBOOTCHANSHIFT;
+
+ if (sc->bios_control & CFEXTEND)
+ ahc->flags |= AHC_EXTENDED_TRANS_A;
+
+ if (sc->bios_control & CFBIOSEN)
+ ahc->flags |= AHC_BIOS_ENABLED;
+ if (ahc->features & AHC_ULTRA
+ && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
+ /* Should we enable Ultra mode? */
+ if (!(sc->adapter_control & CFULTRAEN))
+ /* Treat us as a non-ultra card */
+ ultraenb = 0;
+ }
+
+ if (sc->signature == CFSIGNATURE
+ || sc->signature == CFSIGNATURE2) {
+ uint32_t devconfig;
+
+ /* Honor the STPWLEVEL settings */
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ devconfig &= ~STPWLEVEL;
+ if ((sc->bios_control & CFSTPWLEVEL) != 0)
+ devconfig |= STPWLEVEL;
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+ devconfig, /*bytes*/4);
+ }
+ /* Set SCSICONF info */
+ ahc_outb(ahc, SCSICONF, scsi_conf);
+ ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+ ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+ ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
+ ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
+}
+
+static void
+configure_termination(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd,
+ u_int adapter_control,
+ u_int *sxfrctl1)
+{
+ uint8_t brddat;
+
+ brddat = 0;
+
+ /*
+ * Update the settings in sxfrctl1 to match the
+ * termination settings
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * SEECS must be on for the GALS to latch
+ * the data properly. Be sure to leave MS
+ * on or we will release the seeprom.
+ */
+ SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
+ if ((adapter_control & CFAUTOTERM) != 0
+ || (ahc->features & AHC_NEW_TERMCTL) != 0) {
+ int internal50_present;
+ int internal68_present;
+ int externalcable_present;
+ int eeprom_present;
+ int enableSEC_low;
+ int enableSEC_high;
+ int enablePRI_low;
+ int enablePRI_high;
+ int sum;
+
+ enableSEC_low = 0;
+ enableSEC_high = 0;
+ enablePRI_low = 0;
+ enablePRI_high = 0;
+ if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
+ ahc_new_term_detect(ahc, &enableSEC_low,
+ &enableSEC_high,
+ &enablePRI_low,
+ &enablePRI_high,
+ &eeprom_present);
+ if ((adapter_control & CFSEAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual SE Termination\n",
+ ahc_name(ahc));
+ enableSEC_low = (adapter_control & CFSELOWTERM);
+ enableSEC_high =
+ (adapter_control & CFSEHIGHTERM);
+ }
+ if ((adapter_control & CFAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual LVD Termination\n",
+ ahc_name(ahc));
+ enablePRI_low = (adapter_control & CFSTERM);
+ enablePRI_high = (adapter_control & CFWSTERM);
+ }
+ /* Make the table calculations below happy */
+ internal50_present = 0;
+ internal68_present = 1;
+ externalcable_present = 1;
+ } else if ((ahc->features & AHC_SPIOCAP) != 0) {
+ aic785X_cable_detect(ahc, &internal50_present,
+ &externalcable_present,
+ &eeprom_present);
+ /* Can never support a wide connector. */
+ internal68_present = 0;
+ } else {
+ aic787X_cable_detect(ahc, &internal50_present,
+ &internal68_present,
+ &externalcable_present,
+ &eeprom_present);
+ }
+
+ if ((ahc->features & AHC_WIDE) == 0)
+ internal68_present = 0;
+
+ if (bootverbose
+ && (ahc->features & AHC_ULTRA2) == 0) {
+ printf("%s: internal 50 cable %s present",
+ ahc_name(ahc),
+ internal50_present ? "is":"not");
+
+ if ((ahc->features & AHC_WIDE) != 0)
+ printf(", internal 68 cable %s present",
+ internal68_present ? "is":"not");
+ printf("\n%s: external cable %s present\n",
+ ahc_name(ahc),
+ externalcable_present ? "is":"not");
+ }
+ if (bootverbose)
+ printf("%s: BIOS eeprom %s present\n",
+ ahc_name(ahc), eeprom_present ? "is" : "not");
+
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
+ /*
+ * The 50 pin connector is a separate bus,
+ * so force it to always be terminated.
+ * In the future, perform current sensing
+ * to determine if we are in the middle of
+ * a properly terminated bus.
+ */
+ internal50_present = 0;
+ }
+
+ /*
+ * Now set the termination based on what
+ * we found.
+ * Flash Enable = BRDDAT7
+ * Secondary High Term Enable = BRDDAT6
+ * Secondary Low Term Enable = BRDDAT5 (7890)
+ * Primary High Term Enable = BRDDAT4 (7890)
+ */
+ if ((ahc->features & AHC_ULTRA2) == 0
+ && (internal50_present != 0)
+ && (internal68_present != 0)
+ && (externalcable_present != 0)) {
+ printf("%s: Illegal cable configuration!!. "
+ "Only two connectors on the "
+ "adapter may be used at a "
+ "time!\n", ahc_name(ahc));
+
+ /*
+ * Pretend there are no cables in the hope
+ * that having all of the termination on
+ * gives us a more stable bus.
+ */
+ internal50_present = 0;
+ internal68_present = 0;
+ externalcable_present = 0;
+ }
+
+ if ((ahc->features & AHC_WIDE) != 0
+ && ((externalcable_present == 0)
+ || (internal68_present == 0)
+ || (enableSEC_high != 0))) {
+ brddat |= BRDDAT6;
+ if (bootverbose) {
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+ printf("%s: 68 pin termination "
+ "Enabled\n", ahc_name(ahc));
+ else
+ printf("%s: %sHigh byte termination "
+ "Enabled\n", ahc_name(ahc),
+ enableSEC_high ? "Secondary "
+ : "");
+ }
+ }
+
+ sum = internal50_present + internal68_present
+ + externalcable_present;
+ if (sum < 2 || (enableSEC_low != 0)) {
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brddat |= BRDDAT5;
+ else
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose) {
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+ printf("%s: 50 pin termination "
+ "Enabled\n", ahc_name(ahc));
+ else
+ printf("%s: %sLow byte termination "
+ "Enabled\n", ahc_name(ahc),
+ enableSEC_low ? "Secondary "
+ : "");
+ }
+ }
+
+ if (enablePRI_low != 0) {
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose)
+ printf("%s: Primary Low Byte termination "
+ "Enabled\n", ahc_name(ahc));
+ }
+
+ /*
+ * Setup STPWEN before setting up the rest of
+ * the termination per the tech note on the U160 cards.
+ */
+ ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+ if (enablePRI_high != 0) {
+ brddat |= BRDDAT4;
+ if (bootverbose)
+ printf("%s: Primary High Byte "
+ "termination Enabled\n",
+ ahc_name(ahc));
+ }
+
+ write_brdctl(ahc, brddat);
+
+ } else {
+ if ((adapter_control & CFSTERM) != 0) {
+ *sxfrctl1 |= STPWEN;
+
+ if (bootverbose)
+ printf("%s: %sLow byte termination Enabled\n",
+ ahc_name(ahc),
+ (ahc->features & AHC_ULTRA2) ? "Primary "
+ : "");
+ }
+
+ if ((adapter_control & CFWSTERM) != 0
+ && (ahc->features & AHC_WIDE) != 0) {
+ brddat |= BRDDAT6;
+ if (bootverbose)
+ printf("%s: %sHigh byte termination Enabled\n",
+ ahc_name(ahc),
+ (ahc->features & AHC_ULTRA2)
+ ? "Secondary " : "");
+ }
+
+ /*
+ * Setup STPWEN before setting up the rest of
+ * the termination per the tech note on the U160 cards.
+ */
+ ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+ if ((ahc->features & AHC_WIDE) != 0)
+ write_brdctl(ahc, brddat);
+ }
+ SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
+}
+
+static void
+ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
+ int *enableSEC_high, int *enablePRI_low,
+ int *enablePRI_high, int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ /*
+ * BRDDAT7 = Eeprom
+ * BRDDAT6 = Enable Secondary High Byte termination
+ * BRDDAT5 = Enable Secondary Low Byte termination
+ * BRDDAT4 = Enable Primary high byte termination
+ * BRDDAT3 = Enable Primary low byte termination
+ */
+ brdctl = read_brdctl(ahc);
+ *eeprom_present = brdctl & BRDDAT7;
+ *enableSEC_high = (brdctl & BRDDAT6);
+ *enableSEC_low = (brdctl & BRDDAT5);
+ *enablePRI_high = (brdctl & BRDDAT4);
+ *enablePRI_low = (brdctl & BRDDAT3);
+}
+
+static void
+aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *internal68_present, int *externalcable_present,
+ int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ /*
+ * First read the status of our cables.
+ * Set the rom bank to 0 since the
+ * bank setting serves as a multiplexor
+ * for the cable detection logic.
+ * BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(ahc, 0);
+
+ /*
+ * Now read the state of the internal
+ * connectors. BRDDAT6 is INT50 and
+ * BRDDAT7 is INT68.
+ */
+ brdctl = read_brdctl(ahc);
+ *internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
+ *internal68_present = (brdctl & BRDDAT7) ? 0 : 1;
+
+ /*
+ * Set the rom bank to 1 and determine
+ * the other signals.
+ */
+ write_brdctl(ahc, BRDDAT5);
+
+ /*
+ * Now read the state of the external
+ * connectors. BRDDAT6 is EXT68 and
+ * BRDDAT7 is EPROMPS.
+ */
+ brdctl = read_brdctl(ahc);
+ *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+ *eeprom_present = (brdctl & BRDDAT7) ? 1 : 0;
+}
+
+static void
+aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *externalcable_present, int *eeprom_present)
+{
+ uint8_t brdctl;
+ uint8_t spiocap;
+
+ spiocap = ahc_inb(ahc, SPIOCAP);
+ spiocap &= ~SOFTCMDEN;
+ spiocap |= EXT_BRDCTL;
+ ahc_outb(ahc, SPIOCAP, spiocap);
+ ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
+ ahc_flush_device_writes(ahc);
+ ahc_delay(500);
+ ahc_outb(ahc, BRDCTL, 0);
+ ahc_flush_device_writes(ahc);
+ ahc_delay(500);
+ brdctl = ahc_inb(ahc, BRDCTL);
+ *internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
+ *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+ *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
+}
+
+int
+ahc_acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+{
+ int wait;
+
+ if ((ahc->features & AHC_SPIOCAP) != 0
+ && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
+ return (0);
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the chip reset, there
+ * should be no contention.
+ */
+ SEEPROM_OUTB(sd, sd->sd_MS);
+ wait = 1000; /* 1 second timeout in msec */
+ while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
+ ahc_delay(1000); /* delay 1 msec */
+ }
+ if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
+ SEEPROM_OUTB(sd, 0);
+ return (0);
+ }
+ return(1);
+}
+
+void
+ahc_release_seeprom(struct seeprom_descriptor *sd)
+{
+ /* Release access to the memory port and the serial EEPROM. */
+ SEEPROM_OUTB(sd, 0);
+}
+
+static void
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
+{
+ uint8_t brdctl;
+
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ brdctl = BRDSTB;
+ if (ahc->channel == 'B')
+ brdctl |= BRDCS;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ brdctl = 0;
+ } else {
+ brdctl = BRDSTB|BRDCS;
+ }
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ brdctl |= value;
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brdctl |= BRDSTB_ULTRA2;
+ else
+ brdctl &= ~BRDSTB;
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brdctl = 0;
+ else
+ brdctl &= ~BRDCS;
+ ahc_outb(ahc, BRDCTL, brdctl);
+}
+
+static uint8_t
+read_brdctl(struct ahc_softc *ahc)
+{
+ uint8_t brdctl;
+ uint8_t value;
+
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ brdctl = BRDRW;
+ if (ahc->channel == 'B')
+ brdctl |= BRDCS;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ brdctl = BRDRW_ULTRA2;
+ } else {
+ brdctl = BRDRW|BRDCS;
+ }
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_flush_device_writes(ahc);
+ value = ahc_inb(ahc, BRDCTL);
+ ahc_outb(ahc, BRDCTL, 0);
+ return (value);
+}
+
+static void
+ahc_pci_intr(struct ahc_softc *ahc)
+{
+ u_int error;
+ u_int status1;
+
+ error = ahc_inb(ahc, ERROR);
+ if ((error & PCIERRSTAT) == 0)
+ return;
+
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+
+ printf("%s: PCI error Interrupt at seqaddr = 0x%x\n",
+ ahc_name(ahc),
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+ if (status1 & DPE) {
+ ahc->pci_target_perr_count++;
+ printf("%s: Data Parity Error Detected during address "
+ "or write data phase\n", ahc_name(ahc));
+ }
+ if (status1 & SSE) {
+ printf("%s: Signal System Error Detected\n", ahc_name(ahc));
+ }
+ if (status1 & RMA) {
+ printf("%s: Received a Master Abort\n", ahc_name(ahc));
+ }
+ if (status1 & RTA) {
+ printf("%s: Received a Target Abort\n", ahc_name(ahc));
+ }
+ if (status1 & STA) {
+ printf("%s: Signaled a Target Abort\n", ahc_name(ahc));
+ }
+ if (status1 & DPR) {
+ printf("%s: Data Parity Error has been reported via PERR#\n",
+ ahc_name(ahc));
+ }
+
+ /* Clear latched errors. */
+ ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+ status1, /*bytes*/1);
+
+ if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) {
+ printf("%s: Latched PCIERR interrupt with "
+ "no status bits set\n", ahc_name(ahc));
+ } else {
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ }
+
+ if (ahc->pci_target_perr_count > AHC_PCI_TARGET_PERR_THRESH) {
+ printf(
+"%s: WARNING WARNING WARNING WARNING\n"
+"%s: Too many PCI parity errors observed as a target.\n"
+"%s: Some device on this bus is generating bad parity.\n"
+"%s: This is an error *observed by*, not *generated by*, this controller.\n"
+"%s: PCI parity error checking has been disabled.\n"
+"%s: WARNING WARNING WARNING WARNING\n",
+ ahc_name(ahc), ahc_name(ahc), ahc_name(ahc),
+ ahc_name(ahc), ahc_name(ahc), ahc_name(ahc));
+ ahc->seqctl |= FAILDIS;
+ ahc_outb(ahc, SEQCTL, ahc->seqctl);
+ }
+ ahc_unpause(ahc);
+}
+
+static int
+ahc_pci_chip_init(struct ahc_softc *ahc)
+{
+ ahc_outb(ahc, DSCOMMAND0, ahc->bus_softc.pci_softc.dscommand0);
+ ahc_outb(ahc, DSPCISTATUS, ahc->bus_softc.pci_softc.dspcistatus);
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc_outb(ahc, OPTIONMODE, ahc->bus_softc.pci_softc.optionmode);
+ ahc_outw(ahc, TARGCRCCNT, ahc->bus_softc.pci_softc.targcrccnt);
+ ahc_outb(ahc, SFUNCT, sfunct);
+ ahc_outb(ahc, CRCCONTROL1,
+ ahc->bus_softc.pci_softc.crccontrol1);
+ }
+ if ((ahc->features & AHC_MULTI_FUNC) != 0)
+ ahc_outb(ahc, SCBBADDR, ahc->bus_softc.pci_softc.scbbaddr);
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, DFF_THRSH, ahc->bus_softc.pci_softc.dff_thrsh);
+
+ return (ahc_chip_init(ahc));
+}
+
+static int
+ahc_pci_suspend(struct ahc_softc *ahc)
+{
+ return (ahc_suspend(ahc));
+}
+
+static int
+ahc_pci_resume(struct ahc_softc *ahc)
+{
+
+ pci_set_power_state(ahc->dev_softc, AHC_POWER_STATE_D0);
+
+ /*
+ * We assume that the OS has restored our register
+ * mappings, etc. Just update the config space registers
+ * that the OS doesn't know about and rely on our chip
+ * reset handler to handle the rest.
+ */
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4,
+ ahc->bus_softc.pci_softc.devconfig);
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1,
+ ahc->bus_softc.pci_softc.command);
+ ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME, /*bytes*/1,
+ ahc->bus_softc.pci_softc.csize_lattime);
+ if ((ahc->flags & AHC_HAS_TERM_LOGIC) != 0) {
+ struct seeprom_descriptor sd;
+ u_int sxfrctl1;
+
+ sd.sd_ahc = ahc;
+ sd.sd_control_offset = SEECTL;
+ sd.sd_status_offset = SEECTL;
+ sd.sd_dataout_offset = SEECTL;
+
+ ahc_acquire_seeprom(ahc, &sd);
+ configure_termination(ahc, &sd,
+ ahc->seep_config->adapter_control,
+ &sxfrctl1);
+ ahc_release_seeprom(&sd);
+ }
+ return (ahc_resume(ahc));
+}
+
+static int
+ahc_aic785X_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7850;
+ ahc->features = AHC_AIC7850_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1)
+ ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ ahc->instruction_ram_size = 512;
+ return (0);
+}
+
+static int
+ahc_aic7860_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7860;
+ ahc->features = AHC_AIC7860_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1)
+ ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ ahc->instruction_ram_size = 512;
+ return (0);
+}
+
+static int
+ahc_apa1480_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7860_setup(ahc);
+ if (error != 0)
+ return (error);
+ ahc->features |= AHC_REMOVABLE;
+ return (0);
+}
+
+static int
+ahc_aic7870_setup(struct ahc_softc *ahc)
+{
+
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7870;
+ ahc->features = AHC_AIC7870_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ ahc->instruction_ram_size = 512;
+ return (0);
+}
+
+static int
+ahc_aha394X_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7870_setup(ahc);
+ if (error == 0)
+ error = ahc_aha394XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aha398X_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7870_setup(ahc);
+ if (error == 0)
+ error = ahc_aha398XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aha494X_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7870_setup(ahc);
+ if (error == 0)
+ error = ahc_aha494XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aic7880_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7880;
+ ahc->features = AHC_AIC7880_FE;
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1) {
+ ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ } else {
+ ahc->bugs |= AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+ }
+ ahc->instruction_ram_size = 512;
+ return (0);
+}
+
+static int
+ahc_aha2940Pro_setup(struct ahc_softc *ahc)
+{
+
+ ahc->flags |= AHC_INT50_SPEEDFLEX;
+ return (ahc_aic7880_setup(ahc));
+}
+
+static int
+ahc_aha394XU_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7880_setup(ahc);
+ if (error == 0)
+ error = ahc_aha394XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aha398XU_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7880_setup(ahc);
+ if (error == 0)
+ error = ahc_aha398XX_setup(ahc);
+ return (error);
+}
+
+static int
+ahc_aic7890_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7890;
+ ahc->features = AHC_AIC7890_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev == 0)
+ ahc->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
+ ahc->instruction_ram_size = 768;
+ return (0);
+}
+
+static int
+ahc_aic7892_setup(struct ahc_softc *ahc)
+{
+
+ ahc->channel = 'A';
+ ahc->chip = AHC_AIC7892;
+ ahc->features = AHC_AIC7892_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+ ahc->instruction_ram_size = 1024;
+ return (0);
+}
+
+static int
+ahc_aic7895_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+ uint8_t rev;
+
+ pci = ahc->dev_softc;
+ ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ /*
+ * The 'C' revision of the aic7895 has a few additional features.
+ */
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 4) {
+ ahc->chip = AHC_AIC7895C;
+ ahc->features = AHC_AIC7895C_FE;
+ } else {
+ u_int command;
+
+ ahc->chip = AHC_AIC7895;
+ ahc->features = AHC_AIC7895_FE;
+
+ /*
+ * The BIOS disables the use of MWI transactions
+ * since it does not have the MWI bug work around
+ * we have. Disabling MWI reduces performance, so
+ * turn it on again.
+ */
+ command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1);
+ command |= PCIM_CMD_MWRICEN;
+ ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1);
+ ahc->bugs |= AHC_PCI_MWI_BUG;
+ }
+ /*
+ * XXX Does CACHETHEN really not work??? What about PCI retry?
+ * on C level chips. Need to test, but for now, play it safe.
+ */
+ ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
+ | AHC_CACHETHEN_BUG;
+
+#if 0
+ uint32_t devconfig;
+
+ /*
+ * Cachesize must also be zero due to stray DAC
+ * problem when sitting behind some bridges.
+ */
+ ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1);
+ devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1);
+ devconfig |= MRDCEN;
+ ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1);
+#endif
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->instruction_ram_size = 512;
+ return (0);
+}
+
+static int
+ahc_aic7896_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ ahc->chip = AHC_AIC7896;
+ ahc->features = AHC_AIC7896_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->bugs |= AHC_CACHETHEN_DIS_BUG;
+ ahc->instruction_ram_size = 768;
+ return (0);
+}
+
+static int
+ahc_aic7899_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ ahc->chip = AHC_AIC7899;
+ ahc->features = AHC_AIC7899_FE;
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+ ahc->instruction_ram_size = 1024;
+ return (0);
+}
+
+static int
+ahc_aha29160C_setup(struct ahc_softc *ahc)
+{
+ int error;
+
+ error = ahc_aic7899_setup(ahc);
+ if (error != 0)
+ return (error);
+ ahc->features |= AHC_REMOVABLE;
+ return (0);
+}
+
+static int
+ahc_raid_setup(struct ahc_softc *ahc)
+{
+ printf("RAID functionality unsupported\n");
+ return (ENXIO);
+}
+
+static int
+ahc_aha394XX_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_394X_SLOT_CHANNEL_A:
+ ahc->channel = 'A';
+ break;
+ case AHC_394X_SLOT_CHANNEL_B:
+ ahc->channel = 'B';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ ahc->channel = 'A';
+ }
+ return (0);
+}
+
+static int
+ahc_aha398XX_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_398X_SLOT_CHANNEL_A:
+ ahc->channel = 'A';
+ break;
+ case AHC_398X_SLOT_CHANNEL_B:
+ ahc->channel = 'B';
+ break;
+ case AHC_398X_SLOT_CHANNEL_C:
+ ahc->channel = 'C';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ ahc->channel = 'A';
+ break;
+ }
+ ahc->flags |= AHC_LARGE_SEEPROM;
+ return (0);
+}
+
+static int
+ahc_aha494XX_setup(struct ahc_softc *ahc)
+{
+ ahc_dev_softc_t pci;
+
+ pci = ahc->dev_softc;
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_494X_SLOT_CHANNEL_A:
+ ahc->channel = 'A';
+ break;
+ case AHC_494X_SLOT_CHANNEL_B:
+ ahc->channel = 'B';
+ break;
+ case AHC_494X_SLOT_CHANNEL_C:
+ ahc->channel = 'C';
+ break;
+ case AHC_494X_SLOT_CHANNEL_D:
+ ahc->channel = 'D';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ ahc->channel = 'A';
+ }
+ ahc->flags |= AHC_LARGE_SEEPROM;
+ return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.h b/drivers/scsi/aic7xxx/aic7xxx_pci.h
new file mode 100644
index 000000000000..be27fcb20346
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.h
@@ -0,0 +1,124 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ *
+ */
+#ifndef _AIC7XXX_PCI_H_
+#define _AIC7XXX_PCI_H_
+
+#define ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK 0xFFF0FFFF00000000ull
+#define ID_9005_SISL_MASK 0x000FFFFF00000000ull
+#define ID_9005_SISL_ID 0x0005900500000000ull
+#define ID_AIC7850 0x5078900400000000ull
+#define ID_AHA_2902_04_10_15_20C_30C 0x5078900478509004ull
+#define ID_AIC7855 0x5578900400000000ull
+#define ID_AIC7859 0x3860900400000000ull
+#define ID_AHA_2930CU 0x3860900438699004ull
+#define ID_AIC7860 0x6078900400000000ull
+#define ID_AIC7860C 0x6078900478609004ull
+#define ID_AHA_1480A 0x6075900400000000ull
+#define ID_AHA_2940AU_0 0x6178900400000000ull
+#define ID_AHA_2940AU_1 0x6178900478619004ull
+#define ID_AHA_2940AU_CN 0x2178900478219004ull
+#define ID_AHA_2930C_VAR 0x6038900438689004ull
+
+#define ID_AIC7870 0x7078900400000000ull
+#define ID_AHA_2940 0x7178900400000000ull
+#define ID_AHA_3940 0x7278900400000000ull
+#define ID_AHA_398X 0x7378900400000000ull
+#define ID_AHA_2944 0x7478900400000000ull
+#define ID_AHA_3944 0x7578900400000000ull
+#define ID_AHA_4944 0x7678900400000000ull
+
+#define ID_AIC7880 0x8078900400000000ull
+#define ID_AIC7880_B 0x8078900478809004ull
+#define ID_AHA_2940U 0x8178900400000000ull
+#define ID_AHA_3940U 0x8278900400000000ull
+#define ID_AHA_2944U 0x8478900400000000ull
+#define ID_AHA_3944U 0x8578900400000000ull
+#define ID_AHA_398XU 0x8378900400000000ull
+#define ID_AHA_4944U 0x8678900400000000ull
+#define ID_AHA_2940UB 0x8178900478819004ull
+#define ID_AHA_2930U 0x8878900478889004ull
+#define ID_AHA_2940U_PRO 0x8778900478879004ull
+#define ID_AHA_2940U_CN 0x0078900478009004ull
+
+#define ID_AIC7895 0x7895900478959004ull
+#define ID_AIC7895_ARO 0x7890900478939004ull
+#define ID_AIC7895_ARO_MASK 0xFFF0FFFFFFFFFFFFull
+#define ID_AHA_2940U_DUAL 0x7895900478919004ull
+#define ID_AHA_3940AU 0x7895900478929004ull
+#define ID_AHA_3944AU 0x7895900478949004ull
+
+#define ID_AIC7890 0x001F9005000F9005ull
+#define ID_AIC7890_ARO 0x00139005000F9005ull
+#define ID_AAA_131U2 0x0013900500039005ull
+#define ID_AHA_2930U2 0x0011900501819005ull
+#define ID_AHA_2940U2B 0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM 0x0010900521809005ull
+#define ID_AHA_2940U2 0x00109005A1809005ull
+#define ID_AHA_2950U2B 0x00109005E1009005ull
+
+#define ID_AIC7892 0x008F9005FFFF9005ull
+#define ID_AIC7892_ARO 0x00839005FFFF9005ull
+#define ID_AHA_29160 0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ 0x00809005E2A00E11ull
+#define ID_AHA_29160N 0x0080900562A09005ull
+#define ID_AHA_29160C 0x0080900562209005ull
+#define ID_AHA_29160B 0x00809005E2209005ull
+#define ID_AHA_19160B 0x0081900562A19005ull
+
+#define ID_AIC7896 0x005F9005FFFF9005ull
+#define ID_AIC7896_ARO 0x00539005FFFF9005ull
+#define ID_AHA_3950U2B_0 0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1 0x00509005F5009005ull
+#define ID_AHA_3950U2D_0 0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1 0x00519005B5009005ull
+
+#define ID_AIC7899 0x00CF9005FFFF9005ull
+#define ID_AIC7899_ARO 0x00C39005FFFF9005ull
+#define ID_AHA_3960D 0x00C09005F6209005ull
+#define ID_AHA_3960D_CPQ 0x00C09005F6200E11ull
+
+#define ID_AIC7810 0x1078900400000000ull
+#define ID_AIC7815 0x7815900400000000ull
+
+#endif /* _AIC7XXX_PCI_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c
new file mode 100644
index 000000000000..85e80eecc9d0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#29 $
+ */
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+static void copy_mem_info(struct info_str *info, char *data, int len);
+static int copy_info(struct info_str *info, char *fmt, ...);
+static void ahc_dump_target_state(struct ahc_softc *ahc,
+ struct info_str *info,
+ u_int our_id, char channel,
+ u_int target_id, u_int target_offset);
+static void ahc_dump_device_state(struct info_str *info,
+ struct ahc_linux_device *dev);
+static int ahc_proc_write_seeprom(struct ahc_softc *ahc,
+ char *buffer, int length);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->offset + info->length)
+ len = info->offset + info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+
+ if (info->pos < info->offset) {
+ off_t partial;
+
+ partial = info->offset - info->pos;
+ data += partial;
+ info->pos += partial;
+ len -= partial;
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer, data, len);
+ info->pos += len;
+ info->buffer += len;
+ }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return (len);
+}
+
+void
+ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
+{
+ u_int speed;
+ u_int freq;
+ u_int mb;
+
+ speed = 3300;
+ freq = 0;
+ if (tinfo->offset != 0) {
+ freq = aic_calc_syncsrate(tinfo->period);
+ speed = freq;
+ }
+ speed *= (0x01 << tinfo->width);
+ mb = speed / 1000;
+ if (mb > 0)
+ copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+ else
+ copy_info(info, "%dKB/s transfers", speed);
+
+ if (freq != 0) {
+ copy_info(info, " (%d.%03dMHz%s, offset %d",
+ freq / 1000, freq % 1000,
+ (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+ ? " DT" : "", tinfo->offset);
+ }
+
+ if (tinfo->width > 0) {
+ if (freq != 0) {
+ copy_info(info, ", ");
+ } else {
+ copy_info(info, " (");
+ }
+ copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+ } else if (freq != 0) {
+ copy_info(info, ")");
+ }
+ copy_info(info, "\n");
+}
+
+static void
+ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
+ u_int our_id, char channel, u_int target_id,
+ u_int target_offset)
+{
+ struct ahc_linux_target *targ;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ int lun;
+
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ if ((ahc->features & AHC_TWIN) != 0)
+ copy_info(info, "Channel %c ", channel);
+ copy_info(info, "Target %d Negotiation Settings\n", target_id);
+ copy_info(info, "\tUser: ");
+ ahc_format_transinfo(info, &tinfo->user);
+ targ = ahc->platform_data->targets[target_offset];
+ if (targ == NULL)
+ return;
+
+ copy_info(info, "\tGoal: ");
+ ahc_format_transinfo(info, &tinfo->goal);
+ copy_info(info, "\tCurr: ");
+ ahc_format_transinfo(info, &tinfo->curr);
+
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_linux_device *dev;
+
+ dev = targ->devices[lun];
+
+ if (dev == NULL)
+ continue;
+
+ ahc_dump_device_state(info, dev);
+ }
+}
+
+static void
+ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev)
+{
+ copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+ dev->target->channel + 'A', dev->target->target, dev->lun);
+
+ copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
+ copy_info(info, "\t\tCommands Active %d\n", dev->active);
+ copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+ copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+ copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+static int
+ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
+{
+ struct seeprom_descriptor sd;
+ int have_seeprom;
+ u_long s;
+ int paused;
+ int written;
+
+ /* Default to failure. */
+ written = -EINVAL;
+ ahc_lock(ahc, &s);
+ paused = ahc_is_paused(ahc);
+ if (!paused)
+ ahc_pause(ahc);
+
+ if (length != sizeof(struct seeprom_config)) {
+ printf("ahc_proc_write_seeprom: incorrect buffer size\n");
+ goto done;
+ }
+
+ have_seeprom = ahc_verify_cksum((struct seeprom_config*)buffer);
+ if (have_seeprom == 0) {
+ printf("ahc_proc_write_seeprom: cksum verification failed\n");
+ goto done;
+ }
+
+ sd.sd_ahc = ahc;
+#if AHC_PCI_CONFIG > 0
+ if ((ahc->chip & AHC_PCI) != 0) {
+ sd.sd_control_offset = SEECTL;
+ sd.sd_status_offset = SEECTL;
+ sd.sd_dataout_offset = SEECTL;
+ if (ahc->flags & AHC_LARGE_SEEPROM)
+ sd.sd_chip = C56_66;
+ else
+ sd.sd_chip = C46;
+ sd.sd_MS = SEEMS;
+ sd.sd_RDY = SEERDY;
+ sd.sd_CS = SEECS;
+ sd.sd_CK = SEECK;
+ sd.sd_DO = SEEDO;
+ sd.sd_DI = SEEDI;
+ have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+ } else
+#endif
+ if ((ahc->chip & AHC_VL) != 0) {
+ sd.sd_control_offset = SEECTL_2840;
+ sd.sd_status_offset = STATUS_2840;
+ sd.sd_dataout_offset = STATUS_2840;
+ sd.sd_chip = C46;
+ sd.sd_MS = 0;
+ sd.sd_RDY = EEPROM_TF;
+ sd.sd_CS = CS_2840;
+ sd.sd_CK = CK_2840;
+ sd.sd_DO = DO_2840;
+ sd.sd_DI = DI_2840;
+ have_seeprom = TRUE;
+ } else {
+ printf("ahc_proc_write_seeprom: unsupported adapter type\n");
+ goto done;
+ }
+
+ if (!have_seeprom) {
+ printf("ahc_proc_write_seeprom: No Serial EEPROM\n");
+ goto done;
+ } else {
+ u_int start_addr;
+
+ if (ahc->seep_config == NULL) {
+ ahc->seep_config = malloc(sizeof(*ahc->seep_config),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->seep_config == NULL) {
+ printf("aic7xxx: Unable to allocate serial "
+ "eeprom buffer. Write failing\n");
+ goto done;
+ }
+ }
+ printf("aic7xxx: Writing Serial EEPROM\n");
+ start_addr = 32 * (ahc->channel - 'A');
+ ahc_write_seeprom(&sd, (u_int16_t *)buffer, start_addr,
+ sizeof(struct seeprom_config)/2);
+ ahc_read_seeprom(&sd, (uint16_t *)ahc->seep_config,
+ start_addr, sizeof(struct seeprom_config)/2);
+#if AHC_PCI_CONFIG > 0
+ if ((ahc->chip & AHC_VL) == 0)
+ ahc_release_seeprom(&sd);
+#endif
+ written = length;
+ }
+
+done:
+ if (!paused)
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ return (written);
+}
+
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ahc_linux_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+#else
+ahc_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start,
+ off_t offset, int length, int inout)
+#endif
+{
+ struct ahc_softc *ahc;
+ struct info_str info;
+ char ahc_info[256];
+ u_long s;
+ u_int max_targ;
+ u_int i;
+ int retval;
+
+ retval = -EINVAL;
+ ahc_list_lock(&s);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+ if (ahc->platform_data->host->host_no == hostno)
+ break;
+ }
+#else
+ ahc = ahc_find_softc(*(struct ahc_softc **)shost->hostdata);
+#endif
+
+ if (ahc == NULL)
+ goto done;
+
+ /* Has data been written to the file? */
+ if (inout == TRUE) {
+ retval = ahc_proc_write_seeprom(ahc, buffer, length);
+ goto done;
+ }
+
+ if (start)
+ *start = buffer;
+
+ info.buffer = buffer;
+ info.length = length;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
+ AIC7XXX_DRIVER_VERSION);
+ copy_info(&info, "%s\n", ahc->description);
+ ahc_controller_info(ahc, ahc_info);
+ copy_info(&info, "%s\n", ahc_info);
+ copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n",
+ ahc->scb_data->numscbs, AHC_NSEG);
+
+
+ if (ahc->seep_config == NULL)
+ copy_info(&info, "No Serial EEPROM\n");
+ else {
+ copy_info(&info, "Serial EEPROM:\n");
+ for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "0x%.4x ",
+ ((uint16_t*)ahc->seep_config)[i]);
+ }
+ copy_info(&info, "\n");
+ }
+ copy_info(&info, "\n");
+
+ max_targ = 15;
+ if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
+ }
+
+ ahc_dump_target_state(ahc, &info, our_id,
+ channel, target_id, i);
+ }
+ retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+ ahc_list_unlock(&s);
+ return (retval);
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
new file mode 100644
index 000000000000..7c1390ed1179
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
@@ -0,0 +1,1787 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $
+ */
+typedef int (ahc_reg_print_t)(u_int, u_int *, u_int);
+typedef struct ahc_reg_parse_entry {
+ char *name;
+ uint8_t value;
+ uint8_t mask;
+} ahc_reg_parse_entry_t;
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiseq_print;
+#else
+#define ahc_scsiseq_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISEQ", 0x00, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl0_print;
+#else
+#define ahc_sxfrctl0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SXFRCTL0", 0x01, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl1_print;
+#else
+#define ahc_sxfrctl1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SXFRCTL1", 0x02, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsisigo_print;
+#else
+#define ahc_scsisigo_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISIGO", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsisigi_print;
+#else
+#define ahc_scsisigi_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISIGI", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsirate_print;
+#else
+#define ahc_scsirate_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIRATE", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiid_print;
+#else
+#define ahc_scsiid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIID", 0x05, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsidatl_print;
+#else
+#define ahc_scsidatl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIDATL", 0x06, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsidath_print;
+#else
+#define ahc_scsidath_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIDATH", 0x07, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_stcnt_print;
+#else
+#define ahc_stcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "STCNT", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_optionmode_print;
+#else
+#define ahc_optionmode_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "OPTIONMODE", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_targcrccnt_print;
+#else
+#define ahc_targcrccnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TARGCRCCNT", 0x0a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_clrsint0_print;
+#else
+#define ahc_clrsint0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CLRSINT0", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat0_print;
+#else
+#define ahc_sstat0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT0", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_clrsint1_print;
+#else
+#define ahc_clrsint1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CLRSINT1", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat1_print;
+#else
+#define ahc_sstat1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT1", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat2_print;
+#else
+#define ahc_sstat2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT2", 0x0d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat3_print;
+#else
+#define ahc_sstat3_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SSTAT3", 0x0e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiid_ultra2_print;
+#else
+#define ahc_scsiid_ultra2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIID_ULTRA2", 0x0f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_simode0_print;
+#else
+#define ahc_simode0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SIMODE0", 0x10, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_simode1_print;
+#else
+#define ahc_simode1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SIMODE1", 0x11, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsibusl_print;
+#else
+#define ahc_scsibusl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIBUSL", 0x12, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsibush_print;
+#else
+#define ahc_scsibush_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIBUSH", 0x13, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl2_print;
+#else
+#define ahc_sxfrctl2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SXFRCTL2", 0x13, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_shaddr_print;
+#else
+#define ahc_shaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SHADDR", 0x14, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seltimer_print;
+#else
+#define ahc_seltimer_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SELTIMER", 0x18, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_selid_print;
+#else
+#define ahc_selid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SELID", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scamctl_print;
+#else
+#define ahc_scamctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCAMCTL", 0x1a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_targid_print;
+#else
+#define ahc_targid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TARGID", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_spiocap_print;
+#else
+#define ahc_spiocap_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SPIOCAP", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_brdctl_print;
+#else
+#define ahc_brdctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BRDCTL", 0x1d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seectl_print;
+#else
+#define ahc_seectl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEECTL", 0x1e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sblkctl_print;
+#else
+#define ahc_sblkctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SBLKCTL", 0x1f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_busy_targets_print;
+#else
+#define ahc_busy_targets_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BUSY_TARGETS", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ultra_enb_print;
+#else
+#define ahc_ultra_enb_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ULTRA_ENB", 0x30, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_disc_dsb_print;
+#else
+#define ahc_disc_dsb_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DISC_DSB", 0x32, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_cmdsize_table_tail_print;
+#else
+#define ahc_cmdsize_table_tail_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CMDSIZE_TABLE_TAIL", 0x34, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_mwi_residual_print;
+#else
+#define ahc_mwi_residual_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "MWI_RESIDUAL", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_next_queued_scb_print;
+#else
+#define ahc_next_queued_scb_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "NEXT_QUEUED_SCB", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_msg_out_print;
+#else
+#define ahc_msg_out_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "MSG_OUT", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dmaparams_print;
+#else
+#define ahc_dmaparams_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DMAPARAMS", 0x3b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seq_flags_print;
+#else
+#define ahc_seq_flags_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQ_FLAGS", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_saved_scsiid_print;
+#else
+#define ahc_saved_scsiid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SAVED_SCSIID", 0x3d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_saved_lun_print;
+#else
+#define ahc_saved_lun_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SAVED_LUN", 0x3e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_lastphase_print;
+#else
+#define ahc_lastphase_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "LASTPHASE", 0x3f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_waiting_scbh_print;
+#else
+#define ahc_waiting_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "WAITING_SCBH", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_disconnected_scbh_print;
+#else
+#define ahc_disconnected_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DISCONNECTED_SCBH", 0x41, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_free_scbh_print;
+#else
+#define ahc_free_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "FREE_SCBH", 0x42, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_complete_scbh_print;
+#else
+#define ahc_complete_scbh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "COMPLETE_SCBH", 0x43, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hscb_addr_print;
+#else
+#define ahc_hscb_addr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HSCB_ADDR", 0x44, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_shared_data_addr_print;
+#else
+#define ahc_shared_data_addr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SHARED_DATA_ADDR", 0x48, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_kernel_qinpos_print;
+#else
+#define ahc_kernel_qinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "KERNEL_QINPOS", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qinpos_print;
+#else
+#define ahc_qinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QINPOS", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoutpos_print;
+#else
+#define ahc_qoutpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOUTPOS", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_kernel_tqinpos_print;
+#else
+#define ahc_kernel_tqinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "KERNEL_TQINPOS", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_tqinpos_print;
+#else
+#define ahc_tqinpos_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TQINPOS", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_arg_1_print;
+#else
+#define ahc_arg_1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ARG_1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_arg_2_print;
+#else
+#define ahc_arg_2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ARG_2", 0x52, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_last_msg_print;
+#else
+#define ahc_last_msg_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "LAST_MSG", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiseq_template_print;
+#else
+#define ahc_scsiseq_template_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSISEQ_TEMPLATE", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ha_274_biosglobal_print;
+#else
+#define ahc_ha_274_biosglobal_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HA_274_BIOSGLOBAL", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seq_flags2_print;
+#else
+#define ahc_seq_flags2_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQ_FLAGS2", 0x57, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiconf_print;
+#else
+#define ahc_scsiconf_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSICONF", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_intdef_print;
+#else
+#define ahc_intdef_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "INTDEF", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hostconf_print;
+#else
+#define ahc_hostconf_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HOSTCONF", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ha_274_biosctrl_print;
+#else
+#define ahc_ha_274_biosctrl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HA_274_BIOSCTRL", 0x5f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqctl_print;
+#else
+#define ahc_seqctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQCTL", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqram_print;
+#else
+#define ahc_seqram_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQRAM", 0x61, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqaddr0_print;
+#else
+#define ahc_seqaddr0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQADDR0", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqaddr1_print;
+#else
+#define ahc_seqaddr1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEQADDR1", 0x63, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_accum_print;
+#else
+#define ahc_accum_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ACCUM", 0x64, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sindex_print;
+#else
+#define ahc_sindex_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SINDEX", 0x65, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dindex_print;
+#else
+#define ahc_dindex_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DINDEX", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_allones_print;
+#else
+#define ahc_allones_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ALLONES", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_allzeros_print;
+#else
+#define ahc_allzeros_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ALLZEROS", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_none_print;
+#else
+#define ahc_none_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "NONE", 0x6a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_flags_print;
+#else
+#define ahc_flags_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "FLAGS", 0x6b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sindir_print;
+#else
+#define ahc_sindir_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SINDIR", 0x6c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dindir_print;
+#else
+#define ahc_dindir_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DINDIR", 0x6d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_function1_print;
+#else
+#define ahc_function1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "FUNCTION1", 0x6e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_stack_print;
+#else
+#define ahc_stack_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "STACK", 0x6f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_targ_offset_print;
+#else
+#define ahc_targ_offset_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "TARG_OFFSET", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sram_base_print;
+#else
+#define ahc_sram_base_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SRAM_BASE", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_bctl_print;
+#else
+#define ahc_bctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BCTL", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dscommand0_print;
+#else
+#define ahc_dscommand0_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DSCOMMAND0", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_bustime_print;
+#else
+#define ahc_bustime_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BUSTIME", 0x85, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dscommand1_print;
+#else
+#define ahc_dscommand1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DSCOMMAND1", 0x85, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_busspd_print;
+#else
+#define ahc_busspd_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "BUSSPD", 0x86, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hs_mailbox_print;
+#else
+#define ahc_hs_mailbox_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HS_MAILBOX", 0x86, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dspcistatus_print;
+#else
+#define ahc_dspcistatus_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DSPCISTATUS", 0x86, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hcntrl_print;
+#else
+#define ahc_hcntrl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HCNTRL", 0x87, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_haddr_print;
+#else
+#define ahc_haddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HADDR", 0x88, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hcnt_print;
+#else
+#define ahc_hcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HCNT", 0x8c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scbptr_print;
+#else
+#define ahc_scbptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCBPTR", 0x90, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_intstat_print;
+#else
+#define ahc_intstat_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "INTSTAT", 0x91, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_clrint_print;
+#else
+#define ahc_clrint_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CLRINT", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_error_print;
+#else
+#define ahc_error_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "ERROR", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfcntrl_print;
+#else
+#define ahc_dfcntrl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFCNTRL", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfstatus_print;
+#else
+#define ahc_dfstatus_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFSTATUS", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfwaddr_print;
+#else
+#define ahc_dfwaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFWADDR", 0x95, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfraddr_print;
+#else
+#define ahc_dfraddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFRADDR", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfdat_print;
+#else
+#define ahc_dfdat_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFDAT", 0x99, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scbcnt_print;
+#else
+#define ahc_scbcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCBCNT", 0x9a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qinfifo_print;
+#else
+#define ahc_qinfifo_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QINFIFO", 0x9b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qincnt_print;
+#else
+#define ahc_qincnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QINCNT", 0x9c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoutfifo_print;
+#else
+#define ahc_qoutfifo_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOUTFIFO", 0x9d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_crccontrol1_print;
+#else
+#define ahc_crccontrol1_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CRCCONTROL1", 0x9d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoutcnt_print;
+#else
+#define ahc_qoutcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOUTCNT", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiphase_print;
+#else
+#define ahc_scsiphase_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCSIPHASE", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sfunct_print;
+#else
+#define ahc_sfunct_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SFUNCT", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_base_print;
+#else
+#define ahc_scb_base_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_BASE", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_cdb_ptr_print;
+#else
+#define ahc_scb_cdb_ptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_CDB_PTR", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_residual_sgptr_print;
+#else
+#define ahc_scb_residual_sgptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_RESIDUAL_SGPTR", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsi_status_print;
+#else
+#define ahc_scb_scsi_status_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSI_STATUS", 0xa8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_target_phases_print;
+#else
+#define ahc_scb_target_phases_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TARGET_PHASES", 0xa9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_target_data_dir_print;
+#else
+#define ahc_scb_target_data_dir_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TARGET_DATA_DIR", 0xaa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_target_itag_print;
+#else
+#define ahc_scb_target_itag_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TARGET_ITAG", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_dataptr_print;
+#else
+#define ahc_scb_dataptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_DATAPTR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_datacnt_print;
+#else
+#define ahc_scb_datacnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_DATACNT", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_sgptr_print;
+#else
+#define ahc_scb_sgptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SGPTR", 0xb4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_control_print;
+#else
+#define ahc_scb_control_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_CONTROL", 0xb8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsiid_print;
+#else
+#define ahc_scb_scsiid_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSIID", 0xb9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_lun_print;
+#else
+#define ahc_scb_lun_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_LUN", 0xba, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_tag_print;
+#else
+#define ahc_scb_tag_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_TAG", 0xbb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_cdb_len_print;
+#else
+#define ahc_scb_cdb_len_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_CDB_LEN", 0xbc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsirate_print;
+#else
+#define ahc_scb_scsirate_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSIRATE", 0xbd, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsioffset_print;
+#else
+#define ahc_scb_scsioffset_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_SCSIOFFSET", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_next_print;
+#else
+#define ahc_scb_next_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_NEXT", 0xbf, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_64_spare_print;
+#else
+#define ahc_scb_64_spare_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_64_SPARE", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seectl_2840_print;
+#else
+#define ahc_seectl_2840_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SEECTL_2840", 0xc0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_status_2840_print;
+#else
+#define ahc_status_2840_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "STATUS_2840", 0xc1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_64_btt_print;
+#else
+#define ahc_scb_64_btt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCB_64_BTT", 0xd0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_cchaddr_print;
+#else
+#define ahc_cchaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCHADDR", 0xe0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_cchcnt_print;
+#else
+#define ahc_cchcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCHCNT", 0xe8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccsgram_print;
+#else
+#define ahc_ccsgram_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSGRAM", 0xe9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccsgaddr_print;
+#else
+#define ahc_ccsgaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSGADDR", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccsgctl_print;
+#else
+#define ahc_ccsgctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSGCTL", 0xeb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbram_print;
+#else
+#define ahc_ccscbram_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBRAM", 0xec, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbaddr_print;
+#else
+#define ahc_ccscbaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBADDR", 0xed, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbctl_print;
+#else
+#define ahc_ccscbctl_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBCTL", 0xee, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbcnt_print;
+#else
+#define ahc_ccscbcnt_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBCNT", 0xef, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scbbaddr_print;
+#else
+#define ahc_scbbaddr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SCBBADDR", 0xf0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_ccscbptr_print;
+#else
+#define ahc_ccscbptr_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "CCSCBPTR", 0xf1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_hnscb_qoff_print;
+#else
+#define ahc_hnscb_qoff_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "HNSCB_QOFF", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_snscb_qoff_print;
+#else
+#define ahc_snscb_qoff_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SNSCB_QOFF", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sdscb_qoff_print;
+#else
+#define ahc_sdscb_qoff_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SDSCB_QOFF", 0xf8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_qoff_ctlsta_print;
+#else
+#define ahc_qoff_ctlsta_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "QOFF_CTLSTA", 0xfa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dff_thrsh_print;
+#else
+#define ahc_dff_thrsh_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "DFF_THRSH", 0xfb, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sg_cache_shadow_print;
+#else
+#define ahc_sg_cache_shadow_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SG_CACHE_SHADOW", 0xfc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sg_cache_pre_print;
+#else
+#define ahc_sg_cache_pre_print(regvalue, cur_col, wrap) \
+ ahc_print_register(NULL, 0, "SG_CACHE_PRE", 0xfc, regvalue, cur_col, wrap)
+#endif
+
+
+#define SCSISEQ 0x00
+#define TEMODE 0x80
+#define SCSIRSTO 0x01
+
+#define SXFRCTL0 0x01
+#define DFON 0x80
+#define DFPEXP 0x40
+#define FAST20 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+
+#define SXFRCTL1 0x02
+#define STIMESEL 0x18
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define SCSISIGO 0x03
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define SCSISIGI 0x03
+#define P_DATAIN_DT 0x60
+#define P_DATAOUT_DT 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIRATE 0x04
+#define SXFR 0x70
+#define SOFS 0x0f
+#define SXFR_ULTRA2 0x0f
+#define WIDEXFER 0x80
+#define ENABLE_CRC 0x40
+#define SINGLE_EDGE 0x10
+
+#define SCSIID 0x05
+#define SCSIOFFSET 0x05
+#define SOFS_ULTRA2 0x7f
+
+#define SCSIDATL 0x06
+
+#define SCSIDATH 0x07
+
+#define STCNT 0x08
+
+#define OPTIONMODE 0x08
+#define OPTIONMODE_DEFAULTS 0x03
+#define AUTORATEEN 0x80
+#define AUTOACKEN 0x40
+#define ATNMGMNTEN 0x20
+#define BUSFREEREV 0x10
+#define EXPPHASEDIS 0x08
+#define SCSIDATL_IMGEN 0x04
+#define AUTO_MSGOUT_DE 0x02
+#define DIS_MSGIN_DUALEDGE 0x01
+
+#define TARGCRCCNT 0x0a
+
+#define CLRSINT0 0x0b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRIOERR 0x08
+#define CLRSWRAP 0x08
+#define CLRSPIORDY 0x02
+
+#define SSTAT0 0x0b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define SWRAP 0x08
+#define IOERR 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define CLRSINT1 0x0c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x0c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x0d
+#define SFCNT 0x1f
+#define OVERRUN 0x80
+#define SHVALID 0x40
+#define EXP_ACTIVE 0x10
+#define CRCVALERR 0x08
+#define CRCENDERR 0x04
+#define CRCREQERR 0x02
+#define DUAL_EDGE_ERR 0x01
+
+#define SSTAT3 0x0e
+#define SCSICNT 0xf0
+#define U2OFFCNT 0x7f
+#define OFFCNT 0x0f
+
+#define SCSIID_ULTRA2 0x0f
+
+#define SIMODE0 0x10
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+#define SIMODE1 0x11
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+#define SCSIBUSL 0x12
+
+#define SCSIBUSH 0x13
+
+#define SXFRCTL2 0x13
+#define ASYNC_SETUP 0x07
+#define AUTORSTDIS 0x10
+#define CMDDMAEN 0x08
+
+#define SHADDR 0x14
+
+#define SELTIMER 0x18
+#define TARGIDIN 0x18
+#define STAGE6 0x20
+#define STAGE5 0x10
+#define STAGE4 0x08
+#define STAGE3 0x04
+#define STAGE2 0x02
+#define STAGE1 0x01
+
+#define SELID 0x19
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define SCAMCTL 0x1a
+#define SCAMLVL 0x03
+#define ENSCAMSELO 0x80
+#define CLRSCAMSELID 0x40
+#define ALTSTIM 0x20
+#define DFLTTID 0x10
+
+#define TARGID 0x1b
+
+#define SPIOCAP 0x1b
+#define SOFT1 0x80
+#define SOFT0 0x40
+#define SOFTCMDEN 0x20
+#define EXT_BRDCTL 0x10
+#define SEEPROM 0x08
+#define EEPROM 0x04
+#define ROM 0x02
+#define SSPIOCPS 0x01
+
+#define BRDCTL 0x1d
+#define BRDDAT7 0x80
+#define BRDDAT6 0x40
+#define BRDDAT5 0x20
+#define BRDDAT4 0x10
+#define BRDSTB 0x10
+#define BRDDAT3 0x08
+#define BRDCS 0x08
+#define BRDDAT2 0x04
+#define BRDRW 0x04
+#define BRDRW_ULTRA2 0x02
+#define BRDCTL1 0x02
+#define BRDCTL0 0x01
+#define BRDSTB_ULTRA2 0x01
+
+#define SEECTL 0x1e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+#define SBLKCTL 0x1f
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define AUTOFLUSHDIS 0x20
+#define ENAB40 0x08
+#define SELBUSB 0x08
+#define ENAB20 0x04
+#define SELWIDE 0x02
+#define XCVR 0x01
+
+#define BUSY_TARGETS 0x20
+#define TARG_SCSIRATE 0x20
+
+#define ULTRA_ENB 0x30
+#define CMDSIZE_TABLE 0x30
+
+#define DISC_DSB 0x32
+
+#define CMDSIZE_TABLE_TAIL 0x34
+
+#define MWI_RESIDUAL 0x38
+#define TARG_IMMEDIATE_SCB 0x38
+
+#define NEXT_QUEUED_SCB 0x39
+
+#define MSG_OUT 0x3a
+
+#define DMAPARAMS 0x3b
+#define PRELOADEN 0x80
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAEN 0x10
+#define SDMAENACK 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define SEQ_FLAGS 0x3c
+#define NOT_IDENTIFIED 0x80
+#define NO_CDB_SENT 0x40
+#define TARGET_CMD_IS_TAGGED 0x40
+#define DPHASE 0x20
+#define TARG_CMD_PENDING 0x10
+#define CMDPHASE_PENDING 0x08
+#define DPHASE_PENDING 0x04
+#define SPHASE_PENDING 0x02
+#define NO_DISCONNECT 0x01
+
+#define SAVED_SCSIID 0x3d
+
+#define SAVED_LUN 0x3e
+
+#define LASTPHASE 0x3f
+#define P_MESGIN 0xe0
+#define PHASE_MASK 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define P_DATAIN 0x40
+#define P_BUSFREE 0x01
+#define P_DATAOUT 0x00
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+
+#define WAITING_SCBH 0x40
+
+#define DISCONNECTED_SCBH 0x41
+
+#define FREE_SCBH 0x42
+
+#define COMPLETE_SCBH 0x43
+
+#define HSCB_ADDR 0x44
+
+#define SHARED_DATA_ADDR 0x48
+
+#define KERNEL_QINPOS 0x4c
+
+#define QINPOS 0x4d
+
+#define QOUTPOS 0x4e
+
+#define KERNEL_TQINPOS 0x4f
+
+#define TQINPOS 0x50
+
+#define ARG_1 0x51
+#define RETURN_1 0x51
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
+#define EXIT_MSG_LOOP 0x08
+#define CONT_MSG_LOOP 0x04
+#define CONT_TARG_SESSION 0x02
+
+#define ARG_2 0x52
+#define RETURN_2 0x52
+
+#define LAST_MSG 0x53
+
+#define SCSISEQ_TEMPLATE 0x54
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+
+#define HA_274_BIOSGLOBAL 0x56
+#define INITIATOR_TAG 0x56
+#define HA_274_EXTENDED_TRANS 0x01
+
+#define SEQ_FLAGS2 0x57
+#define TARGET_MSG_PENDING 0x02
+#define SCB_DMA 0x01
+
+#define SCSICONF 0x5a
+#define HWSCSIID 0x0f
+#define HSCSIID 0x07
+#define TERM_ENB 0x80
+#define RESET_SCSI 0x40
+#define ENSPCHK 0x20
+
+#define INTDEF 0x5c
+#define VECTOR 0x0f
+#define EDGE_TRIG 0x80
+
+#define HOSTCONF 0x5d
+
+#define HA_274_BIOSCTRL 0x5f
+#define BIOSDISABLED 0x30
+#define BIOSMODE 0x30
+#define CHANNEL_B_PRIMARY 0x08
+
+#define SEQCTL 0x60
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+#define SEQRAM 0x61
+
+#define SEQADDR0 0x62
+
+#define SEQADDR1 0x63
+#define SEQADDR1_MASK 0x01
+
+#define ACCUM 0x64
+
+#define SINDEX 0x65
+
+#define DINDEX 0x66
+
+#define ALLONES 0x69
+
+#define ALLZEROS 0x6a
+
+#define NONE 0x6a
+
+#define FLAGS 0x6b
+#define ZERO 0x02
+#define CARRY 0x01
+
+#define SINDIR 0x6c
+
+#define DINDIR 0x6d
+
+#define FUNCTION1 0x6e
+
+#define STACK 0x6f
+
+#define TARG_OFFSET 0x70
+
+#define SRAM_BASE 0x70
+
+#define BCTL 0x84
+#define ACE 0x08
+#define ENABLE 0x01
+
+#define DSCOMMAND0 0x84
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
+#define INTSCBRAMSEL 0x08
+#define RAMPS 0x04
+#define USCBSIZE32 0x02
+#define CIOPARCKEN 0x01
+
+#define BUSTIME 0x85
+#define BOFF 0xf0
+#define BON 0x0f
+
+#define DSCOMMAND1 0x85
+#define DSLATT 0xfc
+#define HADDLDSEL1 0x02
+#define HADDLDSEL0 0x01
+
+#define BUSSPD 0x86
+#define DFTHRSH 0xc0
+#define DFTHRSH_75 0x80
+#define STBOFF 0x38
+#define STBON 0x07
+
+#define HS_MAILBOX 0x86
+#define HOST_MAILBOX 0xf0
+#define HOST_TQINPOS 0x80
+#define SEQ_MAILBOX 0x0f
+
+#define DSPCISTATUS 0x86
+#define DFTHRSH_100 0xc0
+
+#define HCNTRL 0x87
+#define POWRDN 0x40
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HADDR 0x88
+
+#define HCNT 0x8c
+
+#define SCBPTR 0x90
+
+#define INTSTAT 0x91
+#define SEQINT_MASK 0xf1
+#define OUT_OF_RANGE 0xe1
+#define NO_FREE_SCB 0xd1
+#define SCB_MISMATCH 0xc1
+#define MISSED_BUSFREE 0xb1
+#define MKMSG_FAILED 0xa1
+#define DATA_OVERRUN 0x91
+#define PERR_DETECTED 0x81
+#define BAD_STATUS 0x71
+#define HOST_MSG_LOOP 0x61
+#define PDATA_REINIT 0x51
+#define IGN_WIDE_RES 0x41
+#define NO_MATCH 0x31
+#define PROTO_VIOLATION 0x21
+#define SEND_REJECT 0x11
+#define INT_PEND 0x0f
+#define BAD_PHASE 0x01
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define SEQINT 0x01
+
+#define CLRINT 0x92
+#define CLRPARERR 0x10
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define ERROR 0x92
+#define CIOPARERR 0x80
+#define PCIERRSTAT 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+#define DFCNTRL 0x93
+
+#define DFSTATUS 0x94
+#define PRELOAD_AVAIL 0x80
+#define DFCACHETH 0x40
+#define FIFOQWDEMP 0x20
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define DFWADDR 0x95
+
+#define DFRADDR 0x97
+
+#define DFDAT 0x99
+
+#define SCBCNT 0x9a
+#define SCBCNT_MASK 0x1f
+#define SCBAUTO 0x80
+
+#define QINFIFO 0x9b
+
+#define QINCNT 0x9c
+
+#define QOUTFIFO 0x9d
+
+#define CRCCONTROL1 0x9d
+#define CRCONSEEN 0x80
+#define CRCVALCHKEN 0x40
+#define CRCENDCHKEN 0x20
+#define CRCREQCHKEN 0x10
+#define TARGCRCENDEN 0x08
+#define TARGCRCCNTEN 0x04
+
+#define QOUTCNT 0x9e
+
+#define SCSIPHASE 0x9e
+#define DATA_PHASE_MASK 0x03
+#define STATUS_PHASE 0x20
+#define COMMAND_PHASE 0x10
+#define MSG_IN_PHASE 0x08
+#define MSG_OUT_PHASE 0x04
+#define DATA_IN_PHASE 0x02
+#define DATA_OUT_PHASE 0x01
+
+#define SFUNCT 0x9f
+#define ALT_MODE 0x80
+
+#define SCB_BASE 0xa0
+
+#define SCB_CDB_PTR 0xa0
+#define SCB_RESIDUAL_DATACNT 0xa0
+#define SCB_CDB_STORE 0xa0
+
+#define SCB_RESIDUAL_SGPTR 0xa4
+
+#define SCB_SCSI_STATUS 0xa8
+
+#define SCB_TARGET_PHASES 0xa9
+
+#define SCB_TARGET_DATA_DIR 0xaa
+
+#define SCB_TARGET_ITAG 0xab
+
+#define SCB_DATAPTR 0xac
+
+#define SCB_DATACNT 0xb0
+#define SG_HIGH_ADDR_BITS 0x7f
+#define SG_LAST_SEG 0x80
+
+#define SCB_SGPTR 0xb4
+#define SG_RESID_VALID 0x04
+#define SG_FULL_RESID 0x02
+#define SG_LIST_NULL 0x01
+
+#define SCB_CONTROL 0xb8
+#define SCB_TAG_TYPE 0x03
+#define STATUS_RCVD 0x80
+#define TARGET_SCB 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define MK_MESSAGE 0x10
+#define ULTRAENB 0x08
+#define DISCONNECTED 0x04
+
+#define SCB_SCSIID 0xb9
+#define TID 0xf0
+#define TWIN_TID 0x70
+#define OID 0x0f
+#define TWIN_CHNLB 0x80
+
+#define SCB_LUN 0xba
+#define LID 0x3f
+#define SCB_XFERLEN_ODD 0x80
+
+#define SCB_TAG 0xbb
+
+#define SCB_CDB_LEN 0xbc
+
+#define SCB_SCSIRATE 0xbd
+
+#define SCB_SCSIOFFSET 0xbe
+
+#define SCB_NEXT 0xbf
+
+#define SCB_64_SPARE 0xc0
+
+#define SEECTL_2840 0xc0
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0xc1
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define EEPROM_TF 0x80
+#define DI_2840 0x01
+
+#define SCB_64_BTT 0xd0
+
+#define CCHADDR 0xe0
+
+#define CCHCNT 0xe8
+
+#define CCSGRAM 0xe9
+
+#define CCSGADDR 0xea
+
+#define CCSGCTL 0xeb
+#define CCSGDONE 0x80
+#define CCSGEN 0x08
+#define SG_FETCH_NEEDED 0x02
+#define CCSGRESET 0x01
+
+#define CCSCBRAM 0xec
+
+#define CCSCBADDR 0xed
+
+#define CCSCBCTL 0xee
+#define CCSCBDONE 0x80
+#define ARRDONE 0x40
+#define CCARREN 0x10
+#define CCSCBEN 0x08
+#define CCSCBDIR 0x04
+#define CCSCBRESET 0x01
+
+#define CCSCBCNT 0xef
+
+#define SCBBADDR 0xf0
+
+#define CCSCBPTR 0xf1
+
+#define HNSCB_QOFF 0xf4
+
+#define SNSCB_QOFF 0xf6
+
+#define SDSCB_QOFF 0xf8
+
+#define QOFF_CTLSTA 0xfa
+#define SCB_QSIZE 0x07
+#define SCB_QSIZE_256 0x06
+#define SCB_AVAIL 0x40
+#define SNSCB_ROLLOVER 0x20
+#define SDSCB_ROLLOVER 0x10
+
+#define DFF_THRSH 0xfb
+#define WR_DFTHRSH 0x70
+#define WR_DFTHRSH_MAX 0x70
+#define WR_DFTHRSH_90 0x60
+#define WR_DFTHRSH_85 0x50
+#define WR_DFTHRSH_75 0x40
+#define WR_DFTHRSH_63 0x30
+#define WR_DFTHRSH_50 0x20
+#define WR_DFTHRSH_25 0x10
+#define RD_DFTHRSH 0x07
+#define RD_DFTHRSH_MAX 0x07
+#define RD_DFTHRSH_90 0x06
+#define RD_DFTHRSH_85 0x05
+#define RD_DFTHRSH_75 0x04
+#define RD_DFTHRSH_63 0x03
+#define RD_DFTHRSH_50 0x02
+#define RD_DFTHRSH_25 0x01
+#define RD_DFTHRSH_MIN 0x00
+#define WR_DFTHRSH_MIN 0x00
+
+#define SG_CACHE_SHADOW 0xfc
+#define SG_ADDR_MASK 0xf8
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+#define SG_CACHE_PRE 0xfc
+
+
+#define MAX_OFFSET_ULTRA2 0x7f
+#define MAX_OFFSET_16BIT 0x08
+#define BUS_8_BIT 0x00
+#define TARGET_CMD_CMPLT 0xfe
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define MAX_OFFSET_8BIT 0x0f
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+#define TID_SHIFT 0x04
+#define SCB_DOWNLOAD_SIZE_64 0x30
+#define HOST_MAILBOX_SHIFT 0x04
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define CCSGRAM_MAXSEGS 0x10
+#define SCB_LIST_NULL 0xff
+#define SG_SIZEOF 0x08
+#define SCB_DOWNLOAD_SIZE 0x20
+#define SEQ_MAILBOX_SHIFT 0x00
+#define TARGET_DATA_IN 0x01
+#define HOST_MSG 0xff
+#define MAX_OFFSET 0x7f
+#define BUS_16_BIT 0x01
+#define SCB_UPLOAD_SIZE 0x20
+#define STACK_SIZE 0x04
+
+
+/* Downloaded Constant Definitions */
+#define INVERTED_CACHESIZE_MASK 0x03
+#define SG_PREFETCH_ADDR_MASK 0x06
+#define SG_PREFETCH_ALIGN_MASK 0x05
+#define QOUTFIFO_OFFSET 0x00
+#define SG_PREFETCH_CNT 0x04
+#define CACHESIZE_MASK 0x02
+#define QINFIFO_OFFSET 0x01
+#define DOWNLOAD_CONST_COUNT 0x07
+
+
+/* Exported Labels */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped
new file mode 100644
index 000000000000..9c713775d44a
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped
@@ -0,0 +1,1681 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $
+ */
+
+#include "aic7xxx_osm.h"
+
+static ahc_reg_parse_entry_t SCSISEQ_parse_table[] = {
+ { "SCSIRSTO", 0x01, 0x01 },
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "ENAUTOATNI", 0x04, 0x04 },
+ { "ENAUTOATNO", 0x08, 0x08 },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "ENSELO", 0x40, 0x40 },
+ { "TEMODE", 0x80, 0x80 }
+};
+
+int
+ahc_scsiseq_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISEQ_parse_table, 8, "SCSISEQ",
+ 0x00, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SXFRCTL0_parse_table[] = {
+ { "CLRCHN", 0x02, 0x02 },
+ { "SCAMEN", 0x04, 0x04 },
+ { "SPIOEN", 0x08, 0x08 },
+ { "CLRSTCNT", 0x10, 0x10 },
+ { "FAST20", 0x20, 0x20 },
+ { "DFPEXP", 0x40, 0x40 },
+ { "DFON", 0x80, 0x80 }
+};
+
+int
+ahc_sxfrctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SXFRCTL0_parse_table, 7, "SXFRCTL0",
+ 0x01, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SXFRCTL1_parse_table[] = {
+ { "STPWEN", 0x01, 0x01 },
+ { "ACTNEGEN", 0x02, 0x02 },
+ { "ENSTIMER", 0x04, 0x04 },
+ { "ENSPCHK", 0x20, 0x20 },
+ { "SWRAPEN", 0x40, 0x40 },
+ { "BITBUCKET", 0x80, 0x80 },
+ { "STIMESEL", 0x18, 0x18 }
+};
+
+int
+ahc_sxfrctl1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SXFRCTL1_parse_table, 7, "SXFRCTL1",
+ 0x02, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSISIGO_parse_table[] = {
+ { "ACKO", 0x01, 0x01 },
+ { "REQO", 0x02, 0x02 },
+ { "BSYO", 0x04, 0x04 },
+ { "SELO", 0x08, 0x08 },
+ { "ATNO", 0x10, 0x10 },
+ { "MSGO", 0x20, 0x20 },
+ { "IOO", 0x40, 0x40 },
+ { "CDO", 0x80, 0x80 },
+ { "P_DATAOUT", 0x00, 0x00 },
+ { "P_DATAIN", 0x40, 0x40 },
+ { "P_COMMAND", 0x80, 0x80 },
+ { "P_MESGOUT", 0xa0, 0xa0 },
+ { "P_STATUS", 0xc0, 0xc0 },
+ { "PHASE_MASK", 0xe0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 }
+};
+
+int
+ahc_scsisigo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISIGO_parse_table, 15, "SCSISIGO",
+ 0x03, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSISIGI_parse_table[] = {
+ { "ACKI", 0x01, 0x01 },
+ { "REQI", 0x02, 0x02 },
+ { "BSYI", 0x04, 0x04 },
+ { "SELI", 0x08, 0x08 },
+ { "ATNI", 0x10, 0x10 },
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "P_DATAOUT", 0x00, 0x00 },
+ { "P_DATAOUT_DT", 0x20, 0x20 },
+ { "P_DATAIN", 0x40, 0x40 },
+ { "P_DATAIN_DT", 0x60, 0x60 },
+ { "P_COMMAND", 0x80, 0x80 },
+ { "P_MESGOUT", 0xa0, 0xa0 },
+ { "P_STATUS", 0xc0, 0xc0 },
+ { "PHASE_MASK", 0xe0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 }
+};
+
+int
+ahc_scsisigi_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISIGI_parse_table, 17, "SCSISIGI",
+ 0x03, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIRATE_parse_table[] = {
+ { "SINGLE_EDGE", 0x10, 0x10 },
+ { "ENABLE_CRC", 0x40, 0x40 },
+ { "WIDEXFER", 0x80, 0x80 },
+ { "SXFR_ULTRA2", 0x0f, 0x0f },
+ { "SOFS", 0x0f, 0x0f },
+ { "SXFR", 0x70, 0x70 }
+};
+
+int
+ahc_scsirate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIRATE_parse_table, 6, "SCSIRATE",
+ 0x04, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIID_parse_table[] = {
+ { "TWIN_CHNLB", 0x80, 0x80 },
+ { "OID", 0x0f, 0x0f },
+ { "TWIN_TID", 0x70, 0x70 },
+ { "SOFS_ULTRA2", 0x7f, 0x7f },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahc_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIID_parse_table, 5, "SCSIID",
+ 0x05, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsidatl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIDATL",
+ 0x06, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsidath_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIDATH",
+ 0x07, regvalue, cur_col, wrap));
+}
+
+int
+ahc_stcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "STCNT",
+ 0x08, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t OPTIONMODE_parse_table[] = {
+ { "DIS_MSGIN_DUALEDGE", 0x01, 0x01 },
+ { "AUTO_MSGOUT_DE", 0x02, 0x02 },
+ { "SCSIDATL_IMGEN", 0x04, 0x04 },
+ { "EXPPHASEDIS", 0x08, 0x08 },
+ { "BUSFREEREV", 0x10, 0x10 },
+ { "ATNMGMNTEN", 0x20, 0x20 },
+ { "AUTOACKEN", 0x40, 0x40 },
+ { "AUTORATEEN", 0x80, 0x80 },
+ { "OPTIONMODE_DEFAULTS",0x03, 0x03 }
+};
+
+int
+ahc_optionmode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(OPTIONMODE_parse_table, 9, "OPTIONMODE",
+ 0x08, regvalue, cur_col, wrap));
+}
+
+int
+ahc_targcrccnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TARGCRCCNT",
+ 0x0a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CLRSINT0_parse_table[] = {
+ { "CLRSPIORDY", 0x02, 0x02 },
+ { "CLRSWRAP", 0x08, 0x08 },
+ { "CLRIOERR", 0x08, 0x08 },
+ { "CLRSELINGO", 0x10, 0x10 },
+ { "CLRSELDI", 0x20, 0x20 },
+ { "CLRSELDO", 0x40, 0x40 }
+};
+
+int
+ahc_clrsint0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CLRSINT0_parse_table, 6, "CLRSINT0",
+ 0x0b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT0_parse_table[] = {
+ { "DMADONE", 0x01, 0x01 },
+ { "SPIORDY", 0x02, 0x02 },
+ { "SDONE", 0x04, 0x04 },
+ { "SWRAP", 0x08, 0x08 },
+ { "IOERR", 0x08, 0x08 },
+ { "SELINGO", 0x10, 0x10 },
+ { "SELDI", 0x20, 0x20 },
+ { "SELDO", 0x40, 0x40 },
+ { "TARGET", 0x80, 0x80 }
+};
+
+int
+ahc_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT0_parse_table, 9, "SSTAT0",
+ 0x0b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CLRSINT1_parse_table[] = {
+ { "CLRREQINIT", 0x01, 0x01 },
+ { "CLRPHASECHG", 0x02, 0x02 },
+ { "CLRSCSIPERR", 0x04, 0x04 },
+ { "CLRBUSFREE", 0x08, 0x08 },
+ { "CLRSCSIRSTI", 0x20, 0x20 },
+ { "CLRATNO", 0x40, 0x40 },
+ { "CLRSELTIMEO", 0x80, 0x80 }
+};
+
+int
+ahc_clrsint1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CLRSINT1_parse_table, 7, "CLRSINT1",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT1_parse_table[] = {
+ { "REQINIT", 0x01, 0x01 },
+ { "PHASECHG", 0x02, 0x02 },
+ { "SCSIPERR", 0x04, 0x04 },
+ { "BUSFREE", 0x08, 0x08 },
+ { "PHASEMIS", 0x10, 0x10 },
+ { "SCSIRSTI", 0x20, 0x20 },
+ { "ATNTARG", 0x40, 0x40 },
+ { "SELTO", 0x80, 0x80 }
+};
+
+int
+ahc_sstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT1_parse_table, 8, "SSTAT1",
+ 0x0c, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT2_parse_table[] = {
+ { "DUAL_EDGE_ERR", 0x01, 0x01 },
+ { "CRCREQERR", 0x02, 0x02 },
+ { "CRCENDERR", 0x04, 0x04 },
+ { "CRCVALERR", 0x08, 0x08 },
+ { "EXP_ACTIVE", 0x10, 0x10 },
+ { "SHVALID", 0x40, 0x40 },
+ { "OVERRUN", 0x80, 0x80 },
+ { "SFCNT", 0x1f, 0x1f }
+};
+
+int
+ahc_sstat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT2_parse_table, 8, "SSTAT2",
+ 0x0d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SSTAT3_parse_table[] = {
+ { "OFFCNT", 0x0f, 0x0f },
+ { "U2OFFCNT", 0x7f, 0x7f },
+ { "SCSICNT", 0xf0, 0xf0 }
+};
+
+int
+ahc_sstat3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SSTAT3_parse_table, 3, "SSTAT3",
+ 0x0e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIID_ULTRA2_parse_table[] = {
+ { "OID", 0x0f, 0x0f },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahc_scsiid_ultra2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIID_ULTRA2_parse_table, 2, "SCSIID_ULTRA2",
+ 0x0f, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SIMODE0_parse_table[] = {
+ { "ENDMADONE", 0x01, 0x01 },
+ { "ENSPIORDY", 0x02, 0x02 },
+ { "ENSDONE", 0x04, 0x04 },
+ { "ENSWRAP", 0x08, 0x08 },
+ { "ENIOERR", 0x08, 0x08 },
+ { "ENSELINGO", 0x10, 0x10 },
+ { "ENSELDI", 0x20, 0x20 },
+ { "ENSELDO", 0x40, 0x40 }
+};
+
+int
+ahc_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SIMODE0_parse_table, 8, "SIMODE0",
+ 0x10, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SIMODE1_parse_table[] = {
+ { "ENREQINIT", 0x01, 0x01 },
+ { "ENPHASECHG", 0x02, 0x02 },
+ { "ENSCSIPERR", 0x04, 0x04 },
+ { "ENBUSFREE", 0x08, 0x08 },
+ { "ENPHASEMIS", 0x10, 0x10 },
+ { "ENSCSIRST", 0x20, 0x20 },
+ { "ENATNTARG", 0x40, 0x40 },
+ { "ENSELTIMO", 0x80, 0x80 }
+};
+
+int
+ahc_simode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SIMODE1_parse_table, 8, "SIMODE1",
+ 0x11, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsibusl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIBUSL",
+ 0x12, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsibush_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCSIBUSH",
+ 0x13, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SXFRCTL2_parse_table[] = {
+ { "CMDDMAEN", 0x08, 0x08 },
+ { "AUTORSTDIS", 0x10, 0x10 },
+ { "ASYNC_SETUP", 0x07, 0x07 }
+};
+
+int
+ahc_sxfrctl2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SXFRCTL2_parse_table, 3, "SXFRCTL2",
+ 0x13, regvalue, cur_col, wrap));
+}
+
+int
+ahc_shaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SHADDR",
+ 0x14, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SELTIMER_parse_table[] = {
+ { "STAGE1", 0x01, 0x01 },
+ { "STAGE2", 0x02, 0x02 },
+ { "STAGE3", 0x04, 0x04 },
+ { "STAGE4", 0x08, 0x08 },
+ { "STAGE5", 0x10, 0x10 },
+ { "STAGE6", 0x20, 0x20 }
+};
+
+int
+ahc_seltimer_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SELTIMER_parse_table, 6, "SELTIMER",
+ 0x18, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SELID_parse_table[] = {
+ { "ONEBIT", 0x08, 0x08 },
+ { "SELID_MASK", 0xf0, 0xf0 }
+};
+
+int
+ahc_selid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SELID_parse_table, 2, "SELID",
+ 0x19, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCAMCTL_parse_table[] = {
+ { "DFLTTID", 0x10, 0x10 },
+ { "ALTSTIM", 0x20, 0x20 },
+ { "CLRSCAMSELID", 0x40, 0x40 },
+ { "ENSCAMSELO", 0x80, 0x80 },
+ { "SCAMLVL", 0x03, 0x03 }
+};
+
+int
+ahc_scamctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCAMCTL_parse_table, 5, "SCAMCTL",
+ 0x1a, regvalue, cur_col, wrap));
+}
+
+int
+ahc_targid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TARGID",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SPIOCAP_parse_table[] = {
+ { "SSPIOCPS", 0x01, 0x01 },
+ { "ROM", 0x02, 0x02 },
+ { "EEPROM", 0x04, 0x04 },
+ { "SEEPROM", 0x08, 0x08 },
+ { "EXT_BRDCTL", 0x10, 0x10 },
+ { "SOFTCMDEN", 0x20, 0x20 },
+ { "SOFT0", 0x40, 0x40 },
+ { "SOFT1", 0x80, 0x80 }
+};
+
+int
+ahc_spiocap_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SPIOCAP_parse_table, 8, "SPIOCAP",
+ 0x1b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BRDCTL_parse_table[] = {
+ { "BRDCTL0", 0x01, 0x01 },
+ { "BRDSTB_ULTRA2", 0x01, 0x01 },
+ { "BRDCTL1", 0x02, 0x02 },
+ { "BRDRW_ULTRA2", 0x02, 0x02 },
+ { "BRDRW", 0x04, 0x04 },
+ { "BRDDAT2", 0x04, 0x04 },
+ { "BRDCS", 0x08, 0x08 },
+ { "BRDDAT3", 0x08, 0x08 },
+ { "BRDSTB", 0x10, 0x10 },
+ { "BRDDAT4", 0x10, 0x10 },
+ { "BRDDAT5", 0x20, 0x20 },
+ { "BRDDAT6", 0x40, 0x40 },
+ { "BRDDAT7", 0x80, 0x80 }
+};
+
+int
+ahc_brdctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BRDCTL_parse_table, 13, "BRDCTL",
+ 0x1d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEECTL_parse_table[] = {
+ { "SEEDI", 0x01, 0x01 },
+ { "SEEDO", 0x02, 0x02 },
+ { "SEECK", 0x04, 0x04 },
+ { "SEECS", 0x08, 0x08 },
+ { "SEERDY", 0x10, 0x10 },
+ { "SEEMS", 0x20, 0x20 },
+ { "EXTARBREQ", 0x40, 0x40 },
+ { "EXTARBACK", 0x80, 0x80 }
+};
+
+int
+ahc_seectl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEECTL_parse_table, 8, "SEECTL",
+ 0x1e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SBLKCTL_parse_table[] = {
+ { "XCVR", 0x01, 0x01 },
+ { "SELWIDE", 0x02, 0x02 },
+ { "ENAB20", 0x04, 0x04 },
+ { "SELBUSB", 0x08, 0x08 },
+ { "ENAB40", 0x08, 0x08 },
+ { "AUTOFLUSHDIS", 0x20, 0x20 },
+ { "DIAGLEDON", 0x40, 0x40 },
+ { "DIAGLEDEN", 0x80, 0x80 }
+};
+
+int
+ahc_sblkctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SBLKCTL_parse_table, 8, "SBLKCTL",
+ 0x1f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_busy_targets_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "BUSY_TARGETS",
+ 0x20, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ultra_enb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ULTRA_ENB",
+ 0x30, regvalue, cur_col, wrap));
+}
+
+int
+ahc_disc_dsb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DISC_DSB",
+ 0x32, regvalue, cur_col, wrap));
+}
+
+int
+ahc_cmdsize_table_tail_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CMDSIZE_TABLE_TAIL",
+ 0x34, regvalue, cur_col, wrap));
+}
+
+int
+ahc_mwi_residual_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "MWI_RESIDUAL",
+ 0x38, regvalue, cur_col, wrap));
+}
+
+int
+ahc_next_queued_scb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "NEXT_QUEUED_SCB",
+ 0x39, regvalue, cur_col, wrap));
+}
+
+int
+ahc_msg_out_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "MSG_OUT",
+ 0x3a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DMAPARAMS_parse_table[] = {
+ { "FIFORESET", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SDMAEN", 0x10, 0x10 },
+ { "SDMAENACK", 0x10, 0x10 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "WIDEODD", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahc_dmaparams_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DMAPARAMS_parse_table, 10, "DMAPARAMS",
+ 0x3b, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQ_FLAGS_parse_table[] = {
+ { "NO_DISCONNECT", 0x01, 0x01 },
+ { "SPHASE_PENDING", 0x02, 0x02 },
+ { "DPHASE_PENDING", 0x04, 0x04 },
+ { "CMDPHASE_PENDING", 0x08, 0x08 },
+ { "TARG_CMD_PENDING", 0x10, 0x10 },
+ { "DPHASE", 0x20, 0x20 },
+ { "NO_CDB_SENT", 0x40, 0x40 },
+ { "TARGET_CMD_IS_TAGGED",0x40, 0x40 },
+ { "NOT_IDENTIFIED", 0x80, 0x80 }
+};
+
+int
+ahc_seq_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQ_FLAGS_parse_table, 9, "SEQ_FLAGS",
+ 0x3c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_saved_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SAVED_SCSIID",
+ 0x3d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_saved_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SAVED_LUN",
+ 0x3e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t LASTPHASE_parse_table[] = {
+ { "MSGI", 0x20, 0x20 },
+ { "IOI", 0x40, 0x40 },
+ { "CDI", 0x80, 0x80 },
+ { "P_DATAOUT", 0x00, 0x00 },
+ { "P_BUSFREE", 0x01, 0x01 },
+ { "P_DATAIN", 0x40, 0x40 },
+ { "P_COMMAND", 0x80, 0x80 },
+ { "P_MESGOUT", 0xa0, 0xa0 },
+ { "P_STATUS", 0xc0, 0xc0 },
+ { "PHASE_MASK", 0xe0, 0xe0 },
+ { "P_MESGIN", 0xe0, 0xe0 }
+};
+
+int
+ahc_lastphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(LASTPHASE_parse_table, 11, "LASTPHASE",
+ 0x3f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_waiting_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "WAITING_SCBH",
+ 0x40, regvalue, cur_col, wrap));
+}
+
+int
+ahc_disconnected_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DISCONNECTED_SCBH",
+ 0x41, regvalue, cur_col, wrap));
+}
+
+int
+ahc_free_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "FREE_SCBH",
+ 0x42, regvalue, cur_col, wrap));
+}
+
+int
+ahc_complete_scbh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "COMPLETE_SCBH",
+ 0x43, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hscb_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HSCB_ADDR",
+ 0x44, regvalue, cur_col, wrap));
+}
+
+int
+ahc_shared_data_addr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SHARED_DATA_ADDR",
+ 0x48, regvalue, cur_col, wrap));
+}
+
+int
+ahc_kernel_qinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "KERNEL_QINPOS",
+ 0x4c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QINPOS",
+ 0x4d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qoutpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QOUTPOS",
+ 0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahc_kernel_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "KERNEL_TQINPOS",
+ 0x4f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_tqinpos_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TQINPOS",
+ 0x50, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t ARG_1_parse_table[] = {
+ { "CONT_TARG_SESSION", 0x02, 0x02 },
+ { "CONT_MSG_LOOP", 0x04, 0x04 },
+ { "EXIT_MSG_LOOP", 0x08, 0x08 },
+ { "MSGOUT_PHASEMIS", 0x10, 0x10 },
+ { "SEND_REJ", 0x20, 0x20 },
+ { "SEND_SENSE", 0x40, 0x40 },
+ { "SEND_MSG", 0x80, 0x80 }
+};
+
+int
+ahc_arg_1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(ARG_1_parse_table, 7, "ARG_1",
+ 0x51, regvalue, cur_col, wrap));
+}
+
+int
+ahc_arg_2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ARG_2",
+ 0x52, regvalue, cur_col, wrap));
+}
+
+int
+ahc_last_msg_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "LAST_MSG",
+ 0x53, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSISEQ_TEMPLATE_parse_table[] = {
+ { "ENAUTOATNP", 0x02, 0x02 },
+ { "ENAUTOATNI", 0x04, 0x04 },
+ { "ENAUTOATNO", 0x08, 0x08 },
+ { "ENRSELI", 0x10, 0x10 },
+ { "ENSELI", 0x20, 0x20 },
+ { "ENSELO", 0x40, 0x40 }
+};
+
+int
+ahc_scsiseq_template_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSISEQ_TEMPLATE_parse_table, 6, "SCSISEQ_TEMPLATE",
+ 0x54, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HA_274_BIOSGLOBAL_parse_table[] = {
+ { "HA_274_EXTENDED_TRANS",0x01, 0x01 }
+};
+
+int
+ahc_ha_274_biosglobal_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HA_274_BIOSGLOBAL_parse_table, 1, "HA_274_BIOSGLOBAL",
+ 0x56, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQ_FLAGS2_parse_table[] = {
+ { "SCB_DMA", 0x01, 0x01 },
+ { "TARGET_MSG_PENDING", 0x02, 0x02 }
+};
+
+int
+ahc_seq_flags2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQ_FLAGS2_parse_table, 2, "SEQ_FLAGS2",
+ 0x57, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSICONF_parse_table[] = {
+ { "ENSPCHK", 0x20, 0x20 },
+ { "RESET_SCSI", 0x40, 0x40 },
+ { "TERM_ENB", 0x80, 0x80 },
+ { "HSCSIID", 0x07, 0x07 },
+ { "HWSCSIID", 0x0f, 0x0f }
+};
+
+int
+ahc_scsiconf_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSICONF_parse_table, 5, "SCSICONF",
+ 0x5a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t INTDEF_parse_table[] = {
+ { "EDGE_TRIG", 0x80, 0x80 },
+ { "VECTOR", 0x0f, 0x0f }
+};
+
+int
+ahc_intdef_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(INTDEF_parse_table, 2, "INTDEF",
+ 0x5c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hostconf_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HOSTCONF",
+ 0x5d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HA_274_BIOSCTRL_parse_table[] = {
+ { "CHANNEL_B_PRIMARY", 0x08, 0x08 },
+ { "BIOSMODE", 0x30, 0x30 },
+ { "BIOSDISABLED", 0x30, 0x30 }
+};
+
+int
+ahc_ha_274_biosctrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HA_274_BIOSCTRL_parse_table, 3, "HA_274_BIOSCTRL",
+ 0x5f, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQCTL_parse_table[] = {
+ { "LOADRAM", 0x01, 0x01 },
+ { "SEQRESET", 0x02, 0x02 },
+ { "STEP", 0x04, 0x04 },
+ { "BRKADRINTEN", 0x08, 0x08 },
+ { "FASTMODE", 0x10, 0x10 },
+ { "FAILDIS", 0x20, 0x20 },
+ { "PAUSEDIS", 0x40, 0x40 },
+ { "PERRORDIS", 0x80, 0x80 }
+};
+
+int
+ahc_seqctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQCTL_parse_table, 8, "SEQCTL",
+ 0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahc_seqram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SEQRAM",
+ 0x61, regvalue, cur_col, wrap));
+}
+
+int
+ahc_seqaddr0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SEQADDR0",
+ 0x62, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEQADDR1_parse_table[] = {
+ { "SEQADDR1_MASK", 0x01, 0x01 }
+};
+
+int
+ahc_seqaddr1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEQADDR1_parse_table, 1, "SEQADDR1",
+ 0x63, regvalue, cur_col, wrap));
+}
+
+int
+ahc_accum_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ACCUM",
+ 0x64, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SINDEX",
+ 0x65, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dindex_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DINDEX",
+ 0x66, regvalue, cur_col, wrap));
+}
+
+int
+ahc_allones_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ALLONES",
+ 0x69, regvalue, cur_col, wrap));
+}
+
+int
+ahc_allzeros_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "ALLZEROS",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+int
+ahc_none_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "NONE",
+ 0x6a, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t FLAGS_parse_table[] = {
+ { "CARRY", 0x01, 0x01 },
+ { "ZERO", 0x02, 0x02 }
+};
+
+int
+ahc_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(FLAGS_parse_table, 2, "FLAGS",
+ 0x6b, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SINDIR",
+ 0x6c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dindir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DINDIR",
+ 0x6d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_function1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "FUNCTION1",
+ 0x6e, regvalue, cur_col, wrap));
+}
+
+int
+ahc_stack_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "STACK",
+ 0x6f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_targ_offset_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "TARG_OFFSET",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sram_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SRAM_BASE",
+ 0x70, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BCTL_parse_table[] = {
+ { "ENABLE", 0x01, 0x01 },
+ { "ACE", 0x08, 0x08 }
+};
+
+int
+ahc_bctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BCTL_parse_table, 2, "BCTL",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DSCOMMAND0_parse_table[] = {
+ { "CIOPARCKEN", 0x01, 0x01 },
+ { "USCBSIZE32", 0x02, 0x02 },
+ { "RAMPS", 0x04, 0x04 },
+ { "INTSCBRAMSEL", 0x08, 0x08 },
+ { "EXTREQLCK", 0x10, 0x10 },
+ { "MPARCKEN", 0x20, 0x20 },
+ { "DPARCKEN", 0x40, 0x40 },
+ { "CACHETHEN", 0x80, 0x80 }
+};
+
+int
+ahc_dscommand0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DSCOMMAND0_parse_table, 8, "DSCOMMAND0",
+ 0x84, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BUSTIME_parse_table[] = {
+ { "BON", 0x0f, 0x0f },
+ { "BOFF", 0xf0, 0xf0 }
+};
+
+int
+ahc_bustime_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BUSTIME_parse_table, 2, "BUSTIME",
+ 0x85, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DSCOMMAND1_parse_table[] = {
+ { "HADDLDSEL0", 0x01, 0x01 },
+ { "HADDLDSEL1", 0x02, 0x02 },
+ { "DSLATT", 0xfc, 0xfc }
+};
+
+int
+ahc_dscommand1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DSCOMMAND1_parse_table, 3, "DSCOMMAND1",
+ 0x85, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t BUSSPD_parse_table[] = {
+ { "STBON", 0x07, 0x07 },
+ { "STBOFF", 0x38, 0x38 },
+ { "DFTHRSH_75", 0x80, 0x80 },
+ { "DFTHRSH", 0xc0, 0xc0 },
+ { "DFTHRSH_100", 0xc0, 0xc0 }
+};
+
+int
+ahc_busspd_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(BUSSPD_parse_table, 5, "BUSSPD",
+ 0x86, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HS_MAILBOX_parse_table[] = {
+ { "SEQ_MAILBOX", 0x0f, 0x0f },
+ { "HOST_TQINPOS", 0x80, 0x80 },
+ { "HOST_MAILBOX", 0xf0, 0xf0 }
+};
+
+int
+ahc_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HS_MAILBOX_parse_table, 3, "HS_MAILBOX",
+ 0x86, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DSPCISTATUS_parse_table[] = {
+ { "DFTHRSH_100", 0xc0, 0xc0 }
+};
+
+int
+ahc_dspcistatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DSPCISTATUS_parse_table, 1, "DSPCISTATUS",
+ 0x86, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t HCNTRL_parse_table[] = {
+ { "CHIPRST", 0x01, 0x01 },
+ { "CHIPRSTACK", 0x01, 0x01 },
+ { "INTEN", 0x02, 0x02 },
+ { "PAUSE", 0x04, 0x04 },
+ { "IRQMS", 0x08, 0x08 },
+ { "SWINT", 0x10, 0x10 },
+ { "POWRDN", 0x40, 0x40 }
+};
+
+int
+ahc_hcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(HCNTRL_parse_table, 7, "HCNTRL",
+ 0x87, regvalue, cur_col, wrap));
+}
+
+int
+ahc_haddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HADDR",
+ 0x88, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HCNT",
+ 0x8c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCBPTR",
+ 0x90, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t INTSTAT_parse_table[] = {
+ { "SEQINT", 0x01, 0x01 },
+ { "CMDCMPLT", 0x02, 0x02 },
+ { "SCSIINT", 0x04, 0x04 },
+ { "BRKADRINT", 0x08, 0x08 },
+ { "BAD_PHASE", 0x01, 0x01 },
+ { "INT_PEND", 0x0f, 0x0f },
+ { "SEND_REJECT", 0x11, 0x11 },
+ { "PROTO_VIOLATION", 0x21, 0x21 },
+ { "NO_MATCH", 0x31, 0x31 },
+ { "IGN_WIDE_RES", 0x41, 0x41 },
+ { "PDATA_REINIT", 0x51, 0x51 },
+ { "HOST_MSG_LOOP", 0x61, 0x61 },
+ { "BAD_STATUS", 0x71, 0x71 },
+ { "PERR_DETECTED", 0x81, 0x81 },
+ { "DATA_OVERRUN", 0x91, 0x91 },
+ { "MKMSG_FAILED", 0xa1, 0xa1 },
+ { "MISSED_BUSFREE", 0xb1, 0xb1 },
+ { "SCB_MISMATCH", 0xc1, 0xc1 },
+ { "NO_FREE_SCB", 0xd1, 0xd1 },
+ { "OUT_OF_RANGE", 0xe1, 0xe1 },
+ { "SEQINT_MASK", 0xf1, 0xf1 }
+};
+
+int
+ahc_intstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(INTSTAT_parse_table, 21, "INTSTAT",
+ 0x91, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CLRINT_parse_table[] = {
+ { "CLRSEQINT", 0x01, 0x01 },
+ { "CLRCMDINT", 0x02, 0x02 },
+ { "CLRSCSIINT", 0x04, 0x04 },
+ { "CLRBRKADRINT", 0x08, 0x08 },
+ { "CLRPARERR", 0x10, 0x10 }
+};
+
+int
+ahc_clrint_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CLRINT_parse_table, 5, "CLRINT",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t ERROR_parse_table[] = {
+ { "ILLHADDR", 0x01, 0x01 },
+ { "ILLSADDR", 0x02, 0x02 },
+ { "ILLOPCODE", 0x04, 0x04 },
+ { "SQPARERR", 0x08, 0x08 },
+ { "DPARERR", 0x10, 0x10 },
+ { "MPARERR", 0x20, 0x20 },
+ { "PCIERRSTAT", 0x40, 0x40 },
+ { "CIOPARERR", 0x80, 0x80 }
+};
+
+int
+ahc_error_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(ERROR_parse_table, 8, "ERROR",
+ 0x92, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DFCNTRL_parse_table[] = {
+ { "FIFORESET", 0x01, 0x01 },
+ { "FIFOFLUSH", 0x02, 0x02 },
+ { "DIRECTION", 0x04, 0x04 },
+ { "HDMAEN", 0x08, 0x08 },
+ { "HDMAENACK", 0x08, 0x08 },
+ { "SDMAEN", 0x10, 0x10 },
+ { "SDMAENACK", 0x10, 0x10 },
+ { "SCSIEN", 0x20, 0x20 },
+ { "WIDEODD", 0x40, 0x40 },
+ { "PRELOADEN", 0x80, 0x80 }
+};
+
+int
+ahc_dfcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DFCNTRL_parse_table, 10, "DFCNTRL",
+ 0x93, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DFSTATUS_parse_table[] = {
+ { "FIFOEMP", 0x01, 0x01 },
+ { "FIFOFULL", 0x02, 0x02 },
+ { "DFTHRESH", 0x04, 0x04 },
+ { "HDONE", 0x08, 0x08 },
+ { "MREQPEND", 0x10, 0x10 },
+ { "FIFOQWDEMP", 0x20, 0x20 },
+ { "DFCACHETH", 0x40, 0x40 },
+ { "PRELOAD_AVAIL", 0x80, 0x80 }
+};
+
+int
+ahc_dfstatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DFSTATUS_parse_table, 8, "DFSTATUS",
+ 0x94, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dfwaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DFWADDR",
+ 0x95, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dfraddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DFRADDR",
+ 0x97, regvalue, cur_col, wrap));
+}
+
+int
+ahc_dfdat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "DFDAT",
+ 0x99, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCBCNT_parse_table[] = {
+ { "SCBAUTO", 0x80, 0x80 },
+ { "SCBCNT_MASK", 0x1f, 0x1f }
+};
+
+int
+ahc_scbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCBCNT_parse_table, 2, "SCBCNT",
+ 0x9a, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qinfifo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QINFIFO",
+ 0x9b, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qincnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QINCNT",
+ 0x9c, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qoutfifo_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QOUTFIFO",
+ 0x9d, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CRCCONTROL1_parse_table[] = {
+ { "TARGCRCCNTEN", 0x04, 0x04 },
+ { "TARGCRCENDEN", 0x08, 0x08 },
+ { "CRCREQCHKEN", 0x10, 0x10 },
+ { "CRCENDCHKEN", 0x20, 0x20 },
+ { "CRCVALCHKEN", 0x40, 0x40 },
+ { "CRCONSEEN", 0x80, 0x80 }
+};
+
+int
+ahc_crccontrol1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CRCCONTROL1_parse_table, 6, "CRCCONTROL1",
+ 0x9d, regvalue, cur_col, wrap));
+}
+
+int
+ahc_qoutcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "QOUTCNT",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCSIPHASE_parse_table[] = {
+ { "DATA_OUT_PHASE", 0x01, 0x01 },
+ { "DATA_IN_PHASE", 0x02, 0x02 },
+ { "MSG_OUT_PHASE", 0x04, 0x04 },
+ { "MSG_IN_PHASE", 0x08, 0x08 },
+ { "COMMAND_PHASE", 0x10, 0x10 },
+ { "STATUS_PHASE", 0x20, 0x20 },
+ { "DATA_PHASE_MASK", 0x03, 0x03 }
+};
+
+int
+ahc_scsiphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCSIPHASE_parse_table, 7, "SCSIPHASE",
+ 0x9e, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SFUNCT_parse_table[] = {
+ { "ALT_MODE", 0x80, 0x80 }
+};
+
+int
+ahc_sfunct_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SFUNCT_parse_table, 1, "SFUNCT",
+ 0x9f, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_BASE",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_cdb_ptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_CDB_PTR",
+ 0xa0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_residual_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_RESIDUAL_SGPTR",
+ 0xa4, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_scsi_status_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_SCSI_STATUS",
+ 0xa8, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_target_phases_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TARGET_PHASES",
+ 0xa9, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_target_data_dir_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TARGET_DATA_DIR",
+ 0xaa, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_target_itag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TARGET_ITAG",
+ 0xab, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_dataptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_DATAPTR",
+ 0xac, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_DATACNT_parse_table[] = {
+ { "SG_LAST_SEG", 0x80, 0x80 },
+ { "SG_HIGH_ADDR_BITS", 0x7f, 0x7f }
+};
+
+int
+ahc_scb_datacnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_DATACNT_parse_table, 2, "SCB_DATACNT",
+ 0xb0, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_SGPTR_parse_table[] = {
+ { "SG_LIST_NULL", 0x01, 0x01 },
+ { "SG_FULL_RESID", 0x02, 0x02 },
+ { "SG_RESID_VALID", 0x04, 0x04 }
+};
+
+int
+ahc_scb_sgptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_SGPTR_parse_table, 3, "SCB_SGPTR",
+ 0xb4, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_CONTROL_parse_table[] = {
+ { "DISCONNECTED", 0x04, 0x04 },
+ { "ULTRAENB", 0x08, 0x08 },
+ { "MK_MESSAGE", 0x10, 0x10 },
+ { "TAG_ENB", 0x20, 0x20 },
+ { "DISCENB", 0x40, 0x40 },
+ { "TARGET_SCB", 0x80, 0x80 },
+ { "STATUS_RCVD", 0x80, 0x80 },
+ { "SCB_TAG_TYPE", 0x03, 0x03 }
+};
+
+int
+ahc_scb_control_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_CONTROL_parse_table, 8, "SCB_CONTROL",
+ 0xb8, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_SCSIID_parse_table[] = {
+ { "TWIN_CHNLB", 0x80, 0x80 },
+ { "OID", 0x0f, 0x0f },
+ { "TWIN_TID", 0x70, 0x70 },
+ { "TID", 0xf0, 0xf0 }
+};
+
+int
+ahc_scb_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_SCSIID_parse_table, 4, "SCB_SCSIID",
+ 0xb9, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SCB_LUN_parse_table[] = {
+ { "SCB_XFERLEN_ODD", 0x80, 0x80 },
+ { "LID", 0x3f, 0x3f }
+};
+
+int
+ahc_scb_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SCB_LUN_parse_table, 2, "SCB_LUN",
+ 0xba, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_TAG",
+ 0xbb, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_cdb_len_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_CDB_LEN",
+ 0xbc, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_scsirate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_SCSIRATE",
+ 0xbd, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_scsioffset_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_SCSIOFFSET",
+ 0xbe, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_next_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_NEXT",
+ 0xbf, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_64_spare_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_64_SPARE",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SEECTL_2840_parse_table[] = {
+ { "DO_2840", 0x01, 0x01 },
+ { "CK_2840", 0x02, 0x02 },
+ { "CS_2840", 0x04, 0x04 }
+};
+
+int
+ahc_seectl_2840_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SEECTL_2840_parse_table, 3, "SEECTL_2840",
+ 0xc0, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t STATUS_2840_parse_table[] = {
+ { "DI_2840", 0x01, 0x01 },
+ { "EEPROM_TF", 0x80, 0x80 },
+ { "ADSEL", 0x1e, 0x1e },
+ { "BIOS_SEL", 0x60, 0x60 }
+};
+
+int
+ahc_status_2840_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(STATUS_2840_parse_table, 4, "STATUS_2840",
+ 0xc1, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_64_btt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCB_64_BTT",
+ 0xd0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_cchaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCHADDR",
+ 0xe0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_cchcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCHCNT",
+ 0xe8, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccsgram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSGRAM",
+ 0xe9, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccsgaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSGADDR",
+ 0xea, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CCSGCTL_parse_table[] = {
+ { "CCSGRESET", 0x01, 0x01 },
+ { "SG_FETCH_NEEDED", 0x02, 0x02 },
+ { "CCSGEN", 0x08, 0x08 },
+ { "CCSGDONE", 0x80, 0x80 }
+};
+
+int
+ahc_ccsgctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CCSGCTL_parse_table, 4, "CCSGCTL",
+ 0xeb, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbram_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBRAM",
+ 0xec, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBADDR",
+ 0xed, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t CCSCBCTL_parse_table[] = {
+ { "CCSCBRESET", 0x01, 0x01 },
+ { "CCSCBDIR", 0x04, 0x04 },
+ { "CCSCBEN", 0x08, 0x08 },
+ { "CCARREN", 0x10, 0x10 },
+ { "ARRDONE", 0x40, 0x40 },
+ { "CCSCBDONE", 0x80, 0x80 }
+};
+
+int
+ahc_ccscbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(CCSCBCTL_parse_table, 6, "CCSCBCTL",
+ 0xee, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBCNT",
+ 0xef, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scbbaddr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SCBBADDR",
+ 0xf0, regvalue, cur_col, wrap));
+}
+
+int
+ahc_ccscbptr_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "CCSCBPTR",
+ 0xf1, regvalue, cur_col, wrap));
+}
+
+int
+ahc_hnscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "HNSCB_QOFF",
+ 0xf4, regvalue, cur_col, wrap));
+}
+
+int
+ahc_snscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SNSCB_QOFF",
+ 0xf6, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sdscb_qoff_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(NULL, 0, "SDSCB_QOFF",
+ 0xf8, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t QOFF_CTLSTA_parse_table[] = {
+ { "SDSCB_ROLLOVER", 0x10, 0x10 },
+ { "SNSCB_ROLLOVER", 0x20, 0x20 },
+ { "SCB_AVAIL", 0x40, 0x40 },
+ { "SCB_QSIZE_256", 0x06, 0x06 },
+ { "SCB_QSIZE", 0x07, 0x07 }
+};
+
+int
+ahc_qoff_ctlsta_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(QOFF_CTLSTA_parse_table, 5, "QOFF_CTLSTA",
+ 0xfa, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t DFF_THRSH_parse_table[] = {
+ { "RD_DFTHRSH_MIN", 0x00, 0x00 },
+ { "WR_DFTHRSH_MIN", 0x00, 0x00 },
+ { "RD_DFTHRSH_25", 0x01, 0x01 },
+ { "RD_DFTHRSH_50", 0x02, 0x02 },
+ { "RD_DFTHRSH_63", 0x03, 0x03 },
+ { "RD_DFTHRSH_75", 0x04, 0x04 },
+ { "RD_DFTHRSH_85", 0x05, 0x05 },
+ { "RD_DFTHRSH_90", 0x06, 0x06 },
+ { "RD_DFTHRSH", 0x07, 0x07 },
+ { "RD_DFTHRSH_MAX", 0x07, 0x07 },
+ { "WR_DFTHRSH_25", 0x10, 0x10 },
+ { "WR_DFTHRSH_50", 0x20, 0x20 },
+ { "WR_DFTHRSH_63", 0x30, 0x30 },
+ { "WR_DFTHRSH_75", 0x40, 0x40 },
+ { "WR_DFTHRSH_85", 0x50, 0x50 },
+ { "WR_DFTHRSH_90", 0x60, 0x60 },
+ { "WR_DFTHRSH", 0x70, 0x70 },
+ { "WR_DFTHRSH_MAX", 0x70, 0x70 }
+};
+
+int
+ahc_dff_thrsh_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(DFF_THRSH_parse_table, 18, "DFF_THRSH",
+ 0xfb, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SG_CACHE_SHADOW_parse_table[] = {
+ { "LAST_SEG_DONE", 0x01, 0x01 },
+ { "LAST_SEG", 0x02, 0x02 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahc_sg_cache_shadow_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SG_CACHE_SHADOW_parse_table, 3, "SG_CACHE_SHADOW",
+ 0xfc, regvalue, cur_col, wrap));
+}
+
+static ahc_reg_parse_entry_t SG_CACHE_PRE_parse_table[] = {
+ { "LAST_SEG_DONE", 0x01, 0x01 },
+ { "LAST_SEG", 0x02, 0x02 },
+ { "SG_ADDR_MASK", 0xf8, 0xf8 }
+};
+
+int
+ahc_sg_cache_pre_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+ return (ahc_print_register(SG_CACHE_PRE_parse_table, 3, "SG_CACHE_PRE",
+ 0xfc, regvalue, cur_col, wrap));
+}
+
diff --git a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
new file mode 100644
index 000000000000..cf411368a871
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
@@ -0,0 +1,1307 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ * from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $
+ */
+static uint8_t seqprog[] = {
+ 0xb2, 0x00, 0x00, 0x08,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0x00, 0x65, 0xec, 0x59,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0xff, 0x6a, 0x24, 0x08,
+ 0x40, 0x00, 0x40, 0x68,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0x40, 0x00, 0x40, 0x68,
+ 0xff, 0x40, 0x3c, 0x60,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0x60, 0x0b, 0x42, 0x68,
+ 0x40, 0xfa, 0x12, 0x78,
+ 0x01, 0x4d, 0xc8, 0x30,
+ 0x00, 0x4c, 0x12, 0x70,
+ 0x01, 0x39, 0xa2, 0x30,
+ 0x00, 0x6a, 0xc0, 0x5e,
+ 0x01, 0x51, 0x20, 0x31,
+ 0x01, 0x57, 0xae, 0x00,
+ 0x0d, 0x6a, 0x76, 0x00,
+ 0x00, 0x51, 0x12, 0x5e,
+ 0x01, 0x51, 0xc8, 0x30,
+ 0x00, 0x39, 0xc8, 0x60,
+ 0x00, 0xbb, 0x30, 0x70,
+ 0xc1, 0x6a, 0xd8, 0x5e,
+ 0x01, 0xbf, 0x72, 0x30,
+ 0x01, 0x40, 0x7e, 0x31,
+ 0x01, 0x90, 0x80, 0x30,
+ 0x01, 0xf6, 0xd4, 0x30,
+ 0x01, 0x4d, 0x9a, 0x18,
+ 0xfe, 0x57, 0xae, 0x08,
+ 0x01, 0x40, 0x20, 0x31,
+ 0x00, 0x65, 0xcc, 0x58,
+ 0x60, 0x0b, 0x40, 0x78,
+ 0x08, 0x6a, 0x18, 0x00,
+ 0x08, 0x11, 0x22, 0x00,
+ 0x60, 0x0b, 0x00, 0x78,
+ 0x40, 0x0b, 0xfa, 0x68,
+ 0x80, 0x0b, 0xb6, 0x78,
+ 0x20, 0x6a, 0x16, 0x00,
+ 0xa4, 0x6a, 0x06, 0x00,
+ 0x08, 0x6a, 0x78, 0x00,
+ 0x01, 0x50, 0xc8, 0x30,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0xfc, 0x5d,
+ 0x01, 0x6a, 0xdc, 0x01,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0xfc, 0x5d,
+ 0x01, 0x6a, 0x26, 0x01,
+ 0xf0, 0x19, 0x7a, 0x08,
+ 0x0f, 0x18, 0xc8, 0x08,
+ 0x0f, 0x0f, 0xc8, 0x08,
+ 0x0f, 0x05, 0xc8, 0x08,
+ 0x00, 0x3d, 0x7a, 0x00,
+ 0x08, 0x1f, 0x6e, 0x78,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0x01, 0x3d, 0xd8, 0x31,
+ 0x01, 0x3d, 0x32, 0x31,
+ 0x10, 0x03, 0x4e, 0x79,
+ 0x00, 0x65, 0xf2, 0x58,
+ 0x80, 0x66, 0xae, 0x78,
+ 0x01, 0x66, 0xd8, 0x31,
+ 0x01, 0x66, 0x32, 0x31,
+ 0x3f, 0x66, 0x7c, 0x08,
+ 0x40, 0x66, 0x82, 0x68,
+ 0x01, 0x3c, 0x78, 0x00,
+ 0x10, 0x03, 0x9e, 0x78,
+ 0x00, 0x65, 0xf2, 0x58,
+ 0xe0, 0x66, 0xc8, 0x18,
+ 0x00, 0x65, 0xaa, 0x50,
+ 0xdd, 0x66, 0xc8, 0x18,
+ 0x00, 0x65, 0xaa, 0x48,
+ 0x01, 0x66, 0xd8, 0x31,
+ 0x01, 0x66, 0x32, 0x31,
+ 0x10, 0x03, 0x4e, 0x79,
+ 0x00, 0x65, 0xf2, 0x58,
+ 0x01, 0x66, 0xd8, 0x31,
+ 0x01, 0x66, 0x32, 0x31,
+ 0x01, 0x66, 0xac, 0x30,
+ 0x40, 0x3c, 0x78, 0x00,
+ 0xff, 0x6a, 0xd8, 0x01,
+ 0xff, 0x6a, 0x32, 0x01,
+ 0x10, 0x3c, 0x78, 0x00,
+ 0x02, 0x57, 0x40, 0x69,
+ 0x10, 0x03, 0x3e, 0x69,
+ 0x00, 0x65, 0x20, 0x41,
+ 0x02, 0x57, 0xae, 0x00,
+ 0x00, 0x65, 0x9e, 0x40,
+ 0x61, 0x6a, 0xd8, 0x5e,
+ 0x08, 0x51, 0x20, 0x71,
+ 0x02, 0x0b, 0xb2, 0x78,
+ 0x00, 0x65, 0xae, 0x40,
+ 0x1a, 0x01, 0x02, 0x00,
+ 0xf0, 0x19, 0x7a, 0x08,
+ 0x0f, 0x0f, 0xc8, 0x08,
+ 0x0f, 0x05, 0xc8, 0x08,
+ 0x00, 0x3d, 0x7a, 0x00,
+ 0x08, 0x1f, 0xc4, 0x78,
+ 0x80, 0x3d, 0x7a, 0x00,
+ 0x20, 0x6a, 0x16, 0x00,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x00, 0x65, 0xb2, 0x5e,
+ 0x00, 0x65, 0x12, 0x40,
+ 0x20, 0x11, 0xd2, 0x68,
+ 0x20, 0x6a, 0x18, 0x00,
+ 0x20, 0x11, 0x22, 0x00,
+ 0xf7, 0x1f, 0xca, 0x08,
+ 0x80, 0xb9, 0xd8, 0x78,
+ 0x08, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0x3e, 0x30,
+ 0x01, 0xb9, 0x1e, 0x30,
+ 0x7f, 0xb9, 0x0a, 0x08,
+ 0x01, 0xb9, 0x0a, 0x30,
+ 0x01, 0x54, 0xca, 0x30,
+ 0x80, 0xb8, 0xe6, 0x78,
+ 0x80, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0x00, 0x34,
+ 0x01, 0x54, 0x00, 0x34,
+ 0x08, 0xb8, 0xee, 0x78,
+ 0x20, 0x01, 0x02, 0x00,
+ 0x02, 0xbd, 0x08, 0x34,
+ 0x01, 0xbd, 0x08, 0x34,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0xf4, 0x78,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x01, 0x06, 0xcc, 0x34,
+ 0xb2, 0x00, 0x00, 0x08,
+ 0x01, 0x40, 0x20, 0x31,
+ 0x01, 0xbf, 0x80, 0x30,
+ 0x01, 0xb9, 0x7a, 0x30,
+ 0x3f, 0xba, 0x7c, 0x08,
+ 0x00, 0x65, 0xea, 0x58,
+ 0x80, 0x0b, 0xc4, 0x79,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x01, 0xab, 0xac, 0x30,
+ 0xe4, 0x6a, 0x6e, 0x5d,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x80, 0x3e, 0x84, 0x5d,
+ 0x20, 0xb8, 0x18, 0x79,
+ 0x20, 0x6a, 0x84, 0x5d,
+ 0x00, 0xab, 0x84, 0x5d,
+ 0x01, 0xa9, 0x78, 0x30,
+ 0x10, 0xb8, 0x20, 0x79,
+ 0xe4, 0x6a, 0x6e, 0x5d,
+ 0x00, 0x65, 0xae, 0x40,
+ 0x10, 0x03, 0x3c, 0x69,
+ 0x08, 0x3c, 0x5a, 0x69,
+ 0x04, 0x3c, 0x92, 0x69,
+ 0x02, 0x3c, 0x98, 0x69,
+ 0x01, 0x3c, 0x44, 0x79,
+ 0xff, 0x6a, 0x70, 0x00,
+ 0x00, 0x65, 0xa4, 0x59,
+ 0x00, 0x6a, 0xc0, 0x5e,
+ 0xff, 0x38, 0x30, 0x71,
+ 0x0d, 0x6a, 0x76, 0x00,
+ 0x00, 0x38, 0x12, 0x5e,
+ 0x00, 0x65, 0xea, 0x58,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0x18, 0x41,
+ 0xa4, 0x6a, 0x06, 0x00,
+ 0x00, 0x65, 0xf2, 0x58,
+ 0xfd, 0x57, 0xae, 0x08,
+ 0x00, 0x65, 0xae, 0x40,
+ 0xe4, 0x6a, 0x6e, 0x5d,
+ 0x20, 0x3c, 0x4a, 0x79,
+ 0x02, 0x6a, 0x84, 0x5d,
+ 0x04, 0x6a, 0x84, 0x5d,
+ 0x01, 0x03, 0x4c, 0x69,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0xff, 0x6a, 0x24, 0x08,
+ 0xff, 0x6a, 0x06, 0x08,
+ 0x01, 0x6a, 0x7e, 0x00,
+ 0x00, 0x65, 0xa4, 0x59,
+ 0x00, 0x65, 0x04, 0x40,
+ 0x80, 0x86, 0xc8, 0x08,
+ 0x01, 0x4f, 0xc8, 0x30,
+ 0x00, 0x50, 0x6c, 0x61,
+ 0xc4, 0x6a, 0x6e, 0x5d,
+ 0x40, 0x3c, 0x68, 0x79,
+ 0x28, 0x6a, 0x84, 0x5d,
+ 0x00, 0x65, 0x4c, 0x41,
+ 0x08, 0x6a, 0x84, 0x5d,
+ 0x00, 0x65, 0x4c, 0x41,
+ 0x84, 0x6a, 0x6e, 0x5d,
+ 0x00, 0x65, 0xf2, 0x58,
+ 0x01, 0x66, 0xc8, 0x30,
+ 0x01, 0x64, 0xd8, 0x31,
+ 0x01, 0x64, 0x32, 0x31,
+ 0x5b, 0x64, 0xc8, 0x28,
+ 0x30, 0x64, 0xca, 0x18,
+ 0x01, 0x6c, 0xc8, 0x30,
+ 0xff, 0x64, 0x8e, 0x79,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0x80, 0x79,
+ 0x01, 0x64, 0x86, 0x61,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x01, 0x06, 0xd8, 0x31,
+ 0x01, 0x06, 0x32, 0x31,
+ 0xff, 0x64, 0xc8, 0x18,
+ 0xff, 0x64, 0x80, 0x69,
+ 0xf7, 0x3c, 0x78, 0x08,
+ 0x00, 0x65, 0x20, 0x41,
+ 0x40, 0xaa, 0x7e, 0x10,
+ 0x04, 0xaa, 0x6e, 0x5d,
+ 0x00, 0x65, 0x56, 0x42,
+ 0xc4, 0x6a, 0x6e, 0x5d,
+ 0xc0, 0x6a, 0x7e, 0x00,
+ 0x00, 0xa8, 0x84, 0x5d,
+ 0xe4, 0x6a, 0x06, 0x00,
+ 0x00, 0x6a, 0x84, 0x5d,
+ 0x00, 0x65, 0x4c, 0x41,
+ 0x10, 0x3c, 0xa8, 0x69,
+ 0x00, 0xbb, 0x8a, 0x44,
+ 0x18, 0x6a, 0xda, 0x01,
+ 0x01, 0x69, 0xd8, 0x31,
+ 0x1c, 0x6a, 0xd0, 0x01,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xb0, 0x79,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x01, 0x93, 0x26, 0x01,
+ 0x03, 0x6a, 0x2a, 0x01,
+ 0x01, 0x69, 0x32, 0x31,
+ 0x1c, 0x6a, 0xe0, 0x5d,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0xa8, 0x5e,
+ 0x01, 0x50, 0xa0, 0x18,
+ 0x02, 0x6a, 0x22, 0x05,
+ 0x1a, 0x01, 0x02, 0x00,
+ 0x80, 0x6a, 0x74, 0x00,
+ 0x40, 0x6a, 0x78, 0x00,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x00, 0x65, 0xd8, 0x5d,
+ 0x01, 0x3f, 0xc8, 0x30,
+ 0xbf, 0x64, 0x56, 0x7a,
+ 0x80, 0x64, 0x9e, 0x73,
+ 0xa0, 0x64, 0x00, 0x74,
+ 0xc0, 0x64, 0xf4, 0x73,
+ 0xe0, 0x64, 0x30, 0x74,
+ 0x01, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0xff, 0x6a, 0x24, 0x08,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x09, 0x0c, 0xe6, 0x79,
+ 0x08, 0x0c, 0x04, 0x68,
+ 0xb1, 0x6a, 0xd8, 0x5e,
+ 0xff, 0x6a, 0x26, 0x09,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x02, 0x6a, 0x08, 0x30,
+ 0xff, 0x6a, 0x08, 0x08,
+ 0xdf, 0x01, 0x02, 0x08,
+ 0x01, 0x6a, 0x7e, 0x00,
+ 0xc0, 0x6a, 0x78, 0x04,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x08, 0xa4, 0x48, 0x19,
+ 0x00, 0xa5, 0x4a, 0x21,
+ 0x00, 0xa6, 0x4c, 0x21,
+ 0x00, 0xa7, 0x4e, 0x25,
+ 0x08, 0xeb, 0xdc, 0x7e,
+ 0x80, 0xeb, 0x06, 0x7a,
+ 0xff, 0x6a, 0xd6, 0x09,
+ 0x08, 0xeb, 0x0a, 0x6a,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x80, 0xa3, 0xdc, 0x6e,
+ 0x88, 0xeb, 0x20, 0x72,
+ 0x08, 0xeb, 0xdc, 0x6e,
+ 0x04, 0xea, 0x24, 0xe2,
+ 0x08, 0xee, 0xdc, 0x6e,
+ 0x04, 0x6a, 0xd0, 0x81,
+ 0x05, 0xa4, 0xc0, 0x89,
+ 0x03, 0xa5, 0xc2, 0x31,
+ 0x09, 0x6a, 0xd6, 0x05,
+ 0x00, 0x65, 0x08, 0x5a,
+ 0x06, 0xa4, 0xd4, 0x89,
+ 0x80, 0x94, 0xdc, 0x7e,
+ 0x07, 0xe9, 0x10, 0x31,
+ 0x01, 0xe9, 0x46, 0x31,
+ 0x00, 0xa3, 0xba, 0x5e,
+ 0x00, 0x65, 0xfa, 0x59,
+ 0x01, 0xa4, 0xca, 0x30,
+ 0x80, 0xa3, 0x34, 0x7a,
+ 0x02, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0xf8, 0x31,
+ 0x80, 0x93, 0x26, 0x01,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x01, 0x8c, 0xc8, 0x30,
+ 0x00, 0x88, 0xc8, 0x18,
+ 0x02, 0x64, 0xc8, 0x88,
+ 0xff, 0x64, 0xdc, 0x7e,
+ 0xff, 0x8d, 0x4a, 0x6a,
+ 0xff, 0x8e, 0x4a, 0x6a,
+ 0x03, 0x8c, 0xd4, 0x98,
+ 0x00, 0x65, 0xdc, 0x56,
+ 0x01, 0x64, 0x70, 0x30,
+ 0xff, 0x64, 0xc8, 0x10,
+ 0x01, 0x64, 0xc8, 0x18,
+ 0x00, 0x8c, 0x18, 0x19,
+ 0xff, 0x8d, 0x1a, 0x21,
+ 0xff, 0x8e, 0x1c, 0x25,
+ 0xc0, 0x3c, 0x5a, 0x7a,
+ 0x21, 0x6a, 0xd8, 0x5e,
+ 0xa8, 0x6a, 0x76, 0x00,
+ 0x79, 0x6a, 0x76, 0x00,
+ 0x40, 0x3f, 0x62, 0x6a,
+ 0x04, 0x3b, 0x76, 0x00,
+ 0x04, 0x6a, 0xd4, 0x81,
+ 0x20, 0x3c, 0x6a, 0x7a,
+ 0x51, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0x82, 0x42,
+ 0x20, 0x3c, 0x78, 0x00,
+ 0x00, 0xb3, 0xba, 0x5e,
+ 0x07, 0xac, 0x10, 0x31,
+ 0x05, 0xb3, 0x46, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xac, 0x6a, 0xee, 0x5d,
+ 0xa3, 0x6a, 0xcc, 0x00,
+ 0xb3, 0x6a, 0xf2, 0x5d,
+ 0x00, 0x65, 0x3a, 0x5a,
+ 0xfd, 0xa4, 0x48, 0x09,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0x00, 0x65, 0xe6, 0x5d,
+ 0x01, 0xa4, 0x94, 0x7a,
+ 0x04, 0x3b, 0x76, 0x08,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x80, 0x02, 0x04, 0x00,
+ 0x10, 0x0c, 0x8a, 0x7a,
+ 0x03, 0x9e, 0x8c, 0x6a,
+ 0x7f, 0x02, 0x04, 0x08,
+ 0x91, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x01, 0xa4, 0xca, 0x30,
+ 0x80, 0xa3, 0x9a, 0x7a,
+ 0x02, 0x65, 0xca, 0x00,
+ 0x01, 0x65, 0xf8, 0x31,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x00, 0x65, 0x0e, 0x5a,
+ 0x01, 0xfc, 0xa8, 0x6a,
+ 0x80, 0x0b, 0x9e, 0x6a,
+ 0x10, 0x0c, 0x9e, 0x7a,
+ 0x20, 0x93, 0x9e, 0x6a,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x02, 0xfc, 0xb2, 0x7a,
+ 0x40, 0x0d, 0xc6, 0x6a,
+ 0x01, 0xa4, 0x48, 0x01,
+ 0x00, 0x65, 0xc6, 0x42,
+ 0x40, 0x0d, 0xb8, 0x6a,
+ 0x00, 0x65, 0x0e, 0x5a,
+ 0x00, 0x65, 0xaa, 0x42,
+ 0x80, 0xfc, 0xc2, 0x7a,
+ 0x80, 0xa4, 0xc2, 0x6a,
+ 0xff, 0xa5, 0x4a, 0x19,
+ 0xff, 0xa6, 0x4c, 0x21,
+ 0xff, 0xa7, 0x4e, 0x21,
+ 0xf8, 0xfc, 0x48, 0x09,
+ 0x7f, 0xa3, 0x46, 0x09,
+ 0x04, 0x3b, 0xe2, 0x6a,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x01, 0x94, 0xc8, 0x7a,
+ 0x01, 0x94, 0xc8, 0x7a,
+ 0x01, 0x94, 0xc8, 0x7a,
+ 0x01, 0x94, 0xc8, 0x7a,
+ 0x01, 0x94, 0xc8, 0x7a,
+ 0x01, 0xa4, 0xe0, 0x7a,
+ 0x01, 0xfc, 0xd6, 0x7a,
+ 0x01, 0x94, 0xe2, 0x6a,
+ 0x01, 0x94, 0xe2, 0x6a,
+ 0x01, 0x94, 0xe2, 0x6a,
+ 0x00, 0x65, 0x82, 0x42,
+ 0x01, 0x94, 0xe0, 0x7a,
+ 0x10, 0x94, 0xe2, 0x6a,
+ 0xd7, 0x93, 0x26, 0x09,
+ 0x28, 0x93, 0xe6, 0x6a,
+ 0x01, 0x85, 0x0a, 0x01,
+ 0x02, 0xfc, 0xee, 0x6a,
+ 0x01, 0x14, 0x46, 0x31,
+ 0xff, 0x6a, 0x10, 0x09,
+ 0xfe, 0x85, 0x0a, 0x09,
+ 0xff, 0x38, 0xfc, 0x6a,
+ 0x80, 0xa3, 0xfc, 0x7a,
+ 0x80, 0x0b, 0xfa, 0x7a,
+ 0x04, 0x3b, 0xfc, 0x7a,
+ 0xbf, 0x3b, 0x76, 0x08,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x00, 0x65, 0x0e, 0x5a,
+ 0x01, 0x0b, 0x0a, 0x6b,
+ 0x10, 0x0c, 0xfe, 0x7a,
+ 0x04, 0x93, 0x08, 0x6b,
+ 0x01, 0x94, 0x06, 0x7b,
+ 0x10, 0x94, 0x08, 0x6b,
+ 0xc7, 0x93, 0x26, 0x09,
+ 0x01, 0x99, 0xd4, 0x30,
+ 0x38, 0x93, 0x0c, 0x6b,
+ 0xff, 0x08, 0x5a, 0x6b,
+ 0xff, 0x09, 0x5a, 0x6b,
+ 0xff, 0x0a, 0x5a, 0x6b,
+ 0xff, 0x38, 0x28, 0x7b,
+ 0x04, 0x14, 0x10, 0x31,
+ 0x01, 0x38, 0x18, 0x31,
+ 0x02, 0x6a, 0x1a, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x14, 0x6a, 0xf4, 0x5d,
+ 0x00, 0x38, 0xe0, 0x5d,
+ 0xff, 0x6a, 0x70, 0x08,
+ 0x00, 0x65, 0x54, 0x43,
+ 0x80, 0xa3, 0x2e, 0x7b,
+ 0x01, 0xa4, 0x48, 0x01,
+ 0x00, 0x65, 0x5a, 0x43,
+ 0x08, 0xeb, 0x34, 0x7b,
+ 0x00, 0x65, 0x0e, 0x5a,
+ 0x08, 0xeb, 0x30, 0x6b,
+ 0x07, 0xe9, 0x10, 0x31,
+ 0x01, 0xe9, 0xca, 0x30,
+ 0x01, 0x65, 0x46, 0x31,
+ 0x00, 0x6a, 0xba, 0x5e,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xa4, 0x6a, 0xf4, 0x5d,
+ 0x08, 0x6a, 0xe0, 0x5d,
+ 0x0d, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0xa8, 0x5e,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x00, 0x65, 0x8a, 0x5e,
+ 0x01, 0x99, 0x46, 0x31,
+ 0x00, 0xa3, 0xba, 0x5e,
+ 0x01, 0x88, 0x10, 0x31,
+ 0x00, 0x65, 0x3a, 0x5a,
+ 0x00, 0x65, 0xfa, 0x59,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0x00, 0x65, 0xe6, 0x5d,
+ 0x80, 0x0b, 0x82, 0x6a,
+ 0x80, 0x0b, 0x62, 0x6b,
+ 0x01, 0x0c, 0x5c, 0x7b,
+ 0x10, 0x0c, 0x82, 0x7a,
+ 0x03, 0x9e, 0x82, 0x6a,
+ 0x00, 0x65, 0x04, 0x5a,
+ 0x00, 0x6a, 0xba, 0x5e,
+ 0x01, 0xa4, 0x82, 0x6b,
+ 0xff, 0x38, 0x78, 0x7b,
+ 0x01, 0x38, 0xc8, 0x30,
+ 0x00, 0x08, 0x40, 0x19,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x00, 0x09, 0x42, 0x21,
+ 0x00, 0x0a, 0x44, 0x21,
+ 0xff, 0x6a, 0x70, 0x08,
+ 0x00, 0x65, 0x7a, 0x43,
+ 0x03, 0x08, 0x40, 0x31,
+ 0x03, 0x08, 0x40, 0x31,
+ 0x01, 0x08, 0x40, 0x31,
+ 0x01, 0x09, 0x42, 0x31,
+ 0x01, 0x0a, 0x44, 0x31,
+ 0xfd, 0xb4, 0x68, 0x09,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x04, 0x3c, 0xcc, 0x79,
+ 0xfb, 0x3c, 0x78, 0x08,
+ 0x04, 0x93, 0x20, 0x79,
+ 0x01, 0x0c, 0x8e, 0x6b,
+ 0x80, 0xba, 0x20, 0x79,
+ 0x80, 0x04, 0x20, 0x79,
+ 0xe4, 0x6a, 0x6e, 0x5d,
+ 0x23, 0x6a, 0x84, 0x5d,
+ 0x01, 0x6a, 0x84, 0x5d,
+ 0x00, 0x65, 0x20, 0x41,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x80, 0x3c, 0xa2, 0x7b,
+ 0x21, 0x6a, 0xd8, 0x5e,
+ 0x01, 0xbc, 0x18, 0x31,
+ 0x02, 0x6a, 0x1a, 0x31,
+ 0x02, 0x6a, 0xf8, 0x01,
+ 0x01, 0xbc, 0x10, 0x30,
+ 0x02, 0x6a, 0x12, 0x30,
+ 0x01, 0xbc, 0x10, 0x30,
+ 0xff, 0x6a, 0x12, 0x08,
+ 0xff, 0x6a, 0x14, 0x08,
+ 0xf3, 0xbc, 0xd4, 0x18,
+ 0xa0, 0x6a, 0xc8, 0x53,
+ 0x04, 0xa0, 0x10, 0x31,
+ 0xac, 0x6a, 0x26, 0x01,
+ 0x04, 0xa0, 0x10, 0x31,
+ 0x03, 0x08, 0x18, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xa0, 0x6a, 0xf4, 0x5d,
+ 0x00, 0xbc, 0xe0, 0x5d,
+ 0x3d, 0x6a, 0x26, 0x01,
+ 0x00, 0x65, 0xe0, 0x43,
+ 0xff, 0x6a, 0x10, 0x09,
+ 0xa4, 0x6a, 0x26, 0x01,
+ 0x0c, 0xa0, 0x32, 0x31,
+ 0x05, 0x6a, 0x26, 0x01,
+ 0x35, 0x6a, 0x26, 0x01,
+ 0x0c, 0xa0, 0x32, 0x31,
+ 0x36, 0x6a, 0x26, 0x01,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x35, 0x6a, 0x26, 0x01,
+ 0x00, 0x65, 0x9c, 0x5e,
+ 0x00, 0x65, 0x9c, 0x5e,
+ 0x02, 0x93, 0x26, 0x01,
+ 0xbf, 0x3c, 0x78, 0x08,
+ 0x04, 0x0b, 0xe6, 0x6b,
+ 0x10, 0x0c, 0xe2, 0x7b,
+ 0x01, 0x03, 0xe6, 0x6b,
+ 0x20, 0x93, 0xe8, 0x6b,
+ 0x04, 0x0b, 0xee, 0x6b,
+ 0x40, 0x3c, 0x78, 0x00,
+ 0xc7, 0x93, 0x26, 0x09,
+ 0x38, 0x93, 0xf0, 0x6b,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x80, 0x3c, 0x56, 0x6c,
+ 0x01, 0x06, 0x50, 0x31,
+ 0x80, 0xb8, 0x70, 0x01,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x10, 0x3f, 0x06, 0x00,
+ 0x10, 0x6a, 0x06, 0x00,
+ 0x01, 0x3a, 0xca, 0x30,
+ 0x80, 0x65, 0x1c, 0x64,
+ 0x10, 0xb8, 0x40, 0x6c,
+ 0xc0, 0x3e, 0xca, 0x00,
+ 0x40, 0xb8, 0x0c, 0x6c,
+ 0xbf, 0x65, 0xca, 0x08,
+ 0x20, 0xb8, 0x20, 0x7c,
+ 0x01, 0x65, 0x0c, 0x30,
+ 0x00, 0x65, 0xd8, 0x5d,
+ 0xa0, 0x3f, 0x28, 0x64,
+ 0x23, 0xb8, 0x0c, 0x08,
+ 0x00, 0x65, 0xd8, 0x5d,
+ 0xa0, 0x3f, 0x28, 0x64,
+ 0x00, 0xbb, 0x20, 0x44,
+ 0xff, 0x65, 0x20, 0x64,
+ 0x00, 0x65, 0x40, 0x44,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0x01, 0x65, 0x0c, 0x30,
+ 0x00, 0x65, 0xd8, 0x5d,
+ 0xa0, 0x3f, 0xfc, 0x73,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0x01, 0x3a, 0xa6, 0x30,
+ 0x08, 0x6a, 0x74, 0x00,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x64, 0x6a, 0x68, 0x5d,
+ 0x80, 0x64, 0xd8, 0x6c,
+ 0x04, 0x64, 0x9a, 0x74,
+ 0x02, 0x64, 0xaa, 0x74,
+ 0x00, 0x6a, 0x60, 0x74,
+ 0x03, 0x64, 0xc8, 0x74,
+ 0x23, 0x64, 0x48, 0x74,
+ 0x08, 0x64, 0x5c, 0x74,
+ 0x61, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0xd8, 0x5d,
+ 0x08, 0x51, 0xce, 0x71,
+ 0x00, 0x65, 0x40, 0x44,
+ 0x80, 0x04, 0x5a, 0x7c,
+ 0x51, 0x6a, 0x5e, 0x5d,
+ 0x01, 0x51, 0x5a, 0x64,
+ 0x01, 0xa4, 0x52, 0x7c,
+ 0x80, 0xba, 0x5c, 0x6c,
+ 0x41, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0x5c, 0x44,
+ 0x21, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0x5c, 0x44,
+ 0x07, 0x6a, 0x54, 0x5d,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x80, 0xb8, 0x56, 0x7c,
+ 0xc0, 0x3c, 0x6a, 0x7c,
+ 0x80, 0x3c, 0x56, 0x6c,
+ 0xff, 0xa8, 0x6a, 0x6c,
+ 0x40, 0x3c, 0x56, 0x6c,
+ 0x10, 0xb8, 0x6e, 0x7c,
+ 0xa1, 0x6a, 0xd8, 0x5e,
+ 0x01, 0xb4, 0x74, 0x6c,
+ 0x02, 0xb4, 0x76, 0x6c,
+ 0x01, 0xa4, 0x76, 0x7c,
+ 0xff, 0xa8, 0x86, 0x7c,
+ 0x04, 0xb4, 0x68, 0x01,
+ 0x01, 0x6a, 0x76, 0x00,
+ 0x00, 0xbb, 0x12, 0x5e,
+ 0xff, 0xa8, 0x86, 0x7c,
+ 0x71, 0x6a, 0xd8, 0x5e,
+ 0x40, 0x51, 0x86, 0x64,
+ 0x00, 0x65, 0xb2, 0x5e,
+ 0x00, 0x65, 0xde, 0x41,
+ 0x00, 0xbb, 0x8a, 0x5c,
+ 0x00, 0x65, 0xde, 0x41,
+ 0x00, 0x65, 0xb2, 0x5e,
+ 0x01, 0x65, 0xa2, 0x30,
+ 0x01, 0xf8, 0xc8, 0x30,
+ 0x01, 0x4e, 0xc8, 0x30,
+ 0x00, 0x6a, 0xb6, 0xdd,
+ 0x00, 0x51, 0xc8, 0x5d,
+ 0x01, 0x4e, 0x9c, 0x18,
+ 0x02, 0x6a, 0x22, 0x05,
+ 0xc0, 0x3c, 0x56, 0x6c,
+ 0x04, 0xb8, 0x70, 0x01,
+ 0x00, 0x65, 0xd4, 0x5e,
+ 0x20, 0xb8, 0xde, 0x69,
+ 0x01, 0xbb, 0xa2, 0x30,
+ 0x3f, 0xba, 0x7c, 0x08,
+ 0x00, 0xb9, 0xce, 0x5c,
+ 0x00, 0x65, 0xde, 0x41,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x20, 0x3c, 0xcc, 0x79,
+ 0x20, 0x3c, 0x5c, 0x7c,
+ 0x01, 0xa4, 0xb8, 0x7c,
+ 0x01, 0xb4, 0x68, 0x01,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0x00, 0x65, 0x5c, 0x44,
+ 0x04, 0x14, 0x58, 0x31,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x08, 0xa0, 0x60, 0x31,
+ 0xac, 0x6a, 0xcc, 0x00,
+ 0x14, 0x6a, 0xf4, 0x5d,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0xa0, 0x6a, 0xec, 0x5d,
+ 0x00, 0x65, 0xcc, 0x41,
+ 0xdf, 0x3c, 0x78, 0x08,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0x5c, 0x44,
+ 0x4c, 0x65, 0xcc, 0x28,
+ 0x01, 0x3e, 0x20, 0x31,
+ 0xd0, 0x66, 0xcc, 0x18,
+ 0x20, 0x66, 0xcc, 0x18,
+ 0x01, 0x51, 0xda, 0x34,
+ 0x4c, 0x3d, 0xca, 0x28,
+ 0x3f, 0x64, 0x7c, 0x08,
+ 0xd0, 0x65, 0xca, 0x18,
+ 0x01, 0x3e, 0x20, 0x31,
+ 0x30, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xe6, 0x4c,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x20, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xee, 0x54,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x20, 0x65, 0xca, 0x18,
+ 0xe0, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0xf8, 0x4c,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xd0, 0x65, 0xd4, 0x18,
+ 0x00, 0x65, 0x00, 0x55,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x01, 0x6c, 0xa2, 0x30,
+ 0xff, 0x51, 0x12, 0x75,
+ 0x00, 0x51, 0x8e, 0x5d,
+ 0x01, 0x51, 0x20, 0x31,
+ 0x00, 0x65, 0x34, 0x45,
+ 0x3f, 0xba, 0xc8, 0x08,
+ 0x00, 0x3e, 0x34, 0x75,
+ 0x00, 0x65, 0xb0, 0x5e,
+ 0x80, 0x3c, 0x78, 0x00,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x00, 0x65, 0xd8, 0x5d,
+ 0x01, 0x3c, 0x78, 0x00,
+ 0xe0, 0x3f, 0x50, 0x65,
+ 0x02, 0x3c, 0x78, 0x00,
+ 0x20, 0x12, 0x50, 0x65,
+ 0x51, 0x6a, 0x5e, 0x5d,
+ 0x00, 0x51, 0x8e, 0x5d,
+ 0x51, 0x6a, 0x5e, 0x5d,
+ 0x01, 0x51, 0x20, 0x31,
+ 0x04, 0x3c, 0x78, 0x00,
+ 0x01, 0xb9, 0xc8, 0x30,
+ 0x00, 0x3d, 0x4e, 0x65,
+ 0x08, 0x3c, 0x78, 0x00,
+ 0x3f, 0xba, 0xc8, 0x08,
+ 0x00, 0x3e, 0x4e, 0x65,
+ 0x10, 0x3c, 0x78, 0x00,
+ 0x04, 0xb8, 0x4e, 0x7d,
+ 0xfb, 0xb8, 0x70, 0x09,
+ 0x20, 0xb8, 0x44, 0x6d,
+ 0x01, 0x90, 0xc8, 0x30,
+ 0xff, 0x6a, 0xa2, 0x00,
+ 0x00, 0x3d, 0xce, 0x5c,
+ 0x01, 0x64, 0x20, 0x31,
+ 0xff, 0x6a, 0x78, 0x08,
+ 0x00, 0x65, 0xea, 0x58,
+ 0x10, 0xb8, 0x5c, 0x7c,
+ 0xff, 0x6a, 0x54, 0x5d,
+ 0x00, 0x65, 0x5c, 0x44,
+ 0x00, 0x65, 0xb0, 0x5e,
+ 0x31, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0x5c, 0x44,
+ 0x10, 0x3f, 0x06, 0x00,
+ 0x10, 0x6a, 0x06, 0x00,
+ 0x01, 0x65, 0x74, 0x34,
+ 0x81, 0x6a, 0xd8, 0x5e,
+ 0x00, 0x65, 0x60, 0x45,
+ 0x01, 0x06, 0xd4, 0x30,
+ 0x01, 0x0c, 0x60, 0x7d,
+ 0x04, 0x0c, 0x5a, 0x6d,
+ 0xe0, 0x03, 0x7e, 0x08,
+ 0xe0, 0x3f, 0xcc, 0x61,
+ 0x01, 0x65, 0xcc, 0x30,
+ 0x01, 0x12, 0xda, 0x34,
+ 0x01, 0x06, 0xd4, 0x34,
+ 0x01, 0x03, 0x6e, 0x6d,
+ 0x40, 0x03, 0xcc, 0x08,
+ 0x01, 0x65, 0x06, 0x30,
+ 0x40, 0x65, 0xc8, 0x08,
+ 0x00, 0x66, 0x7c, 0x75,
+ 0x40, 0x65, 0x7c, 0x7d,
+ 0x00, 0x65, 0x7c, 0x5d,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0x86, 0x7d,
+ 0x01, 0x65, 0x0c, 0x30,
+ 0x02, 0x0b, 0x8a, 0x7d,
+ 0xf7, 0x01, 0x02, 0x0c,
+ 0x01, 0x65, 0xc8, 0x30,
+ 0xff, 0x41, 0xae, 0x75,
+ 0x01, 0x41, 0x20, 0x31,
+ 0xff, 0x6a, 0xa4, 0x00,
+ 0x00, 0x65, 0x9e, 0x45,
+ 0xff, 0xbf, 0xae, 0x75,
+ 0x01, 0x90, 0xa4, 0x30,
+ 0x01, 0xbf, 0x20, 0x31,
+ 0x00, 0xbb, 0x98, 0x65,
+ 0xff, 0x52, 0xac, 0x75,
+ 0x01, 0xbf, 0xcc, 0x30,
+ 0x01, 0x90, 0xca, 0x30,
+ 0x01, 0x52, 0x20, 0x31,
+ 0x01, 0x66, 0x7e, 0x31,
+ 0x01, 0x65, 0x20, 0x35,
+ 0x01, 0xbf, 0x82, 0x34,
+ 0x01, 0x64, 0xa2, 0x30,
+ 0x00, 0x6a, 0xc0, 0x5e,
+ 0x0d, 0x6a, 0x76, 0x00,
+ 0x00, 0x51, 0x12, 0x46,
+ 0x01, 0x65, 0xa4, 0x30,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0x06, 0x5e,
+ 0x01, 0x6a, 0xd0, 0x01,
+ 0x01, 0x6a, 0xdc, 0x05,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x48, 0x6a, 0x06, 0x5e,
+ 0x01, 0x6a, 0xe0, 0x5d,
+ 0x01, 0x6a, 0x26, 0x05,
+ 0x01, 0x65, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xcc, 0x7d,
+ 0xff, 0x6a, 0xdc, 0x0d,
+ 0x01, 0x65, 0x32, 0x31,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0xa8, 0x46,
+ 0x81, 0x6a, 0xd8, 0x5e,
+ 0x01, 0x0c, 0xd8, 0x7d,
+ 0x04, 0x0c, 0xd6, 0x6d,
+ 0xe0, 0x03, 0x06, 0x08,
+ 0xe0, 0x03, 0x7e, 0x0c,
+ 0x01, 0x65, 0x18, 0x31,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x0d,
+ 0x01, 0x8c, 0x10, 0x30,
+ 0x01, 0x8d, 0x12, 0x30,
+ 0x01, 0x8e, 0x14, 0x34,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x30,
+ 0x01, 0x6c, 0xda, 0x34,
+ 0x3d, 0x64, 0xa4, 0x28,
+ 0x55, 0x64, 0xc8, 0x28,
+ 0x00, 0x65, 0x06, 0x46,
+ 0x2e, 0x64, 0xa4, 0x28,
+ 0x66, 0x64, 0xc8, 0x28,
+ 0x00, 0x6c, 0xda, 0x18,
+ 0x01, 0x52, 0xc8, 0x30,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0x00, 0x6c, 0xda, 0x24,
+ 0x01, 0x65, 0xc8, 0x30,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x44, 0x6a, 0x02, 0x5e,
+ 0x01, 0x90, 0xe2, 0x31,
+ 0x04, 0x3b, 0x26, 0x7e,
+ 0x30, 0x6a, 0xd0, 0x01,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x1d, 0x6a, 0xdc, 0x01,
+ 0xdc, 0xee, 0x22, 0x66,
+ 0x00, 0x65, 0x3e, 0x46,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x01, 0x6a, 0xdc, 0x01,
+ 0x20, 0xa0, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0x2e, 0x7e,
+ 0x11, 0x6a, 0xdc, 0x01,
+ 0x50, 0xee, 0x32, 0x66,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x09, 0x6a, 0xdc, 0x01,
+ 0x88, 0xee, 0x38, 0x66,
+ 0x19, 0x6a, 0xdc, 0x01,
+ 0xd8, 0xee, 0x3c, 0x66,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x18, 0xee, 0x40, 0x6e,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x44, 0x6a, 0x02, 0x5e,
+ 0x20, 0x6a, 0xe0, 0x5d,
+ 0x01, 0x3b, 0x26, 0x31,
+ 0x04, 0x3b, 0x5a, 0x6e,
+ 0xa0, 0x6a, 0xca, 0x00,
+ 0x20, 0x65, 0xc8, 0x18,
+ 0x00, 0x65, 0x98, 0x5e,
+ 0x00, 0x65, 0x52, 0x66,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0xa8, 0x46,
+ 0xa0, 0x6a, 0xcc, 0x00,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x20, 0x94, 0x5e, 0x6e,
+ 0x10, 0x94, 0x60, 0x6e,
+ 0x08, 0x94, 0x7a, 0x6e,
+ 0x08, 0x94, 0x7a, 0x6e,
+ 0x08, 0x94, 0x7a, 0x6e,
+ 0xff, 0x8c, 0xc8, 0x10,
+ 0xc1, 0x64, 0xc8, 0x18,
+ 0xf8, 0x64, 0xc8, 0x08,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x00, 0x66, 0x6e, 0x66,
+ 0xc0, 0x66, 0xaa, 0x76,
+ 0x60, 0x66, 0xc8, 0x18,
+ 0x3d, 0x64, 0xc8, 0x28,
+ 0x00, 0x65, 0x5e, 0x46,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0x7c, 0x6e,
+ 0x00, 0x62, 0xc4, 0x18,
+ 0x00, 0x65, 0xa8, 0x5e,
+ 0x00, 0x65, 0x88, 0x5e,
+ 0x00, 0x65, 0x88, 0x5e,
+ 0x00, 0x65, 0x88, 0x5e,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x30,
+ 0x01, 0x99, 0xda, 0x34,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x31,
+ 0x01, 0x6c, 0x32, 0x35,
+ 0x08, 0x94, 0xa8, 0x7e,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0xac, 0x6e,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x04, 0xb8, 0xd4, 0x6e,
+ 0x01, 0x42, 0x7e, 0x31,
+ 0xff, 0x6a, 0x76, 0x01,
+ 0x01, 0x90, 0x84, 0x34,
+ 0xff, 0x6a, 0x76, 0x05,
+ 0x01, 0x85, 0x0a, 0x01,
+ 0x7f, 0x65, 0x10, 0x09,
+ 0xfe, 0x85, 0x0a, 0x0d,
+ 0xff, 0x42, 0xd0, 0x66,
+ 0xff, 0x41, 0xc8, 0x66,
+ 0xd1, 0x6a, 0xd8, 0x5e,
+ 0xff, 0x6a, 0xca, 0x04,
+ 0x01, 0x41, 0x20, 0x31,
+ 0x01, 0xbf, 0x82, 0x30,
+ 0x01, 0x6a, 0x76, 0x00,
+ 0x00, 0xbb, 0x12, 0x46,
+ 0x01, 0x42, 0x20, 0x31,
+ 0x01, 0xbf, 0x84, 0x34,
+ 0x01, 0x41, 0x7e, 0x31,
+ 0x01, 0x90, 0x82, 0x34,
+ 0x01, 0x65, 0x22, 0x31,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xff, 0x6a, 0xd4, 0x0c
+};
+
+typedef int ahc_patch_func_t (struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch23_func;
+
+static int
+ahc_patch23_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch22_func;
+
+static int
+ahc_patch22_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_CMD_CHAN) == 0);
+}
+
+static ahc_patch_func_t ahc_patch21_func;
+
+static int
+ahc_patch21_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_QUEUE_REGS) == 0);
+}
+
+static ahc_patch_func_t ahc_patch20_func;
+
+static int
+ahc_patch20_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_WIDE) != 0);
+}
+
+static ahc_patch_func_t ahc_patch19_func;
+
+static int
+ahc_patch19_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_SCB_BTT) != 0);
+}
+
+static ahc_patch_func_t ahc_patch18_func;
+
+static int
+ahc_patch18_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch17_func;
+
+static int
+ahc_patch17_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch16_func;
+
+static int
+ahc_patch16_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch15_func;
+
+static int
+ahc_patch15_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_ULTRA2) == 0);
+}
+
+static ahc_patch_func_t ahc_patch14_func;
+
+static int
+ahc_patch14_func(struct ahc_softc *ahc)
+{
+ return ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0);
+}
+
+static ahc_patch_func_t ahc_patch13_func;
+
+static int
+ahc_patch13_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_39BIT_ADDRESSING) != 0);
+}
+
+static ahc_patch_func_t ahc_patch12_func;
+
+static int
+ahc_patch12_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_HS_MAILBOX) != 0);
+}
+
+static ahc_patch_func_t ahc_patch11_func;
+
+static int
+ahc_patch11_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_ULTRA) != 0);
+}
+
+static ahc_patch_func_t ahc_patch10_func;
+
+static int
+ahc_patch10_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_MULTI_TID) != 0);
+}
+
+static ahc_patch_func_t ahc_patch9_func;
+
+static int
+ahc_patch9_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_CMD_CHAN) != 0);
+}
+
+static ahc_patch_func_t ahc_patch8_func;
+
+static int
+ahc_patch8_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_INITIATORROLE) != 0);
+}
+
+static ahc_patch_func_t ahc_patch7_func;
+
+static int
+ahc_patch7_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_TARGETROLE) != 0);
+}
+
+static ahc_patch_func_t ahc_patch6_func;
+
+static int
+ahc_patch6_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_DT) == 0);
+}
+
+static ahc_patch_func_t ahc_patch5_func;
+
+static int
+ahc_patch5_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch4_func;
+
+static int
+ahc_patch4_func(struct ahc_softc *ahc)
+{
+ return ((ahc->flags & AHC_PAGESCBS) != 0);
+}
+
+static ahc_patch_func_t ahc_patch3_func;
+
+static int
+ahc_patch3_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_QUEUE_REGS) != 0);
+}
+
+static ahc_patch_func_t ahc_patch2_func;
+
+static int
+ahc_patch2_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_TWIN) != 0);
+}
+
+static ahc_patch_func_t ahc_patch1_func;
+
+static int
+ahc_patch1_func(struct ahc_softc *ahc)
+{
+ return ((ahc->features & AHC_ULTRA2) != 0);
+}
+
+static ahc_patch_func_t ahc_patch0_func;
+
+static int
+ahc_patch0_func(struct ahc_softc *ahc)
+{
+ return (0);
+}
+
+static struct patch {
+ ahc_patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {
+ { ahc_patch1_func, 4, 1, 1 },
+ { ahc_patch2_func, 6, 2, 1 },
+ { ahc_patch2_func, 9, 1, 1 },
+ { ahc_patch3_func, 11, 1, 2 },
+ { ahc_patch0_func, 12, 2, 1 },
+ { ahc_patch4_func, 15, 1, 2 },
+ { ahc_patch0_func, 16, 1, 1 },
+ { ahc_patch5_func, 22, 2, 1 },
+ { ahc_patch3_func, 27, 1, 2 },
+ { ahc_patch0_func, 28, 1, 1 },
+ { ahc_patch6_func, 34, 1, 1 },
+ { ahc_patch7_func, 37, 54, 19 },
+ { ahc_patch8_func, 37, 1, 1 },
+ { ahc_patch9_func, 42, 3, 2 },
+ { ahc_patch0_func, 45, 3, 1 },
+ { ahc_patch10_func, 49, 1, 2 },
+ { ahc_patch0_func, 50, 2, 3 },
+ { ahc_patch1_func, 50, 1, 2 },
+ { ahc_patch0_func, 51, 1, 1 },
+ { ahc_patch2_func, 53, 2, 1 },
+ { ahc_patch9_func, 55, 1, 2 },
+ { ahc_patch0_func, 56, 1, 1 },
+ { ahc_patch9_func, 60, 1, 2 },
+ { ahc_patch0_func, 61, 1, 1 },
+ { ahc_patch9_func, 71, 1, 2 },
+ { ahc_patch0_func, 72, 1, 1 },
+ { ahc_patch9_func, 75, 1, 2 },
+ { ahc_patch0_func, 76, 1, 1 },
+ { ahc_patch9_func, 79, 1, 2 },
+ { ahc_patch0_func, 80, 1, 1 },
+ { ahc_patch8_func, 91, 9, 4 },
+ { ahc_patch1_func, 93, 1, 2 },
+ { ahc_patch0_func, 94, 1, 1 },
+ { ahc_patch2_func, 96, 2, 1 },
+ { ahc_patch2_func, 105, 4, 1 },
+ { ahc_patch1_func, 109, 1, 2 },
+ { ahc_patch0_func, 110, 2, 3 },
+ { ahc_patch2_func, 110, 1, 2 },
+ { ahc_patch0_func, 111, 1, 1 },
+ { ahc_patch7_func, 112, 4, 2 },
+ { ahc_patch0_func, 116, 1, 1 },
+ { ahc_patch11_func, 117, 2, 1 },
+ { ahc_patch1_func, 119, 1, 2 },
+ { ahc_patch0_func, 120, 1, 1 },
+ { ahc_patch7_func, 121, 4, 1 },
+ { ahc_patch7_func, 131, 95, 11 },
+ { ahc_patch4_func, 151, 1, 1 },
+ { ahc_patch1_func, 168, 1, 1 },
+ { ahc_patch12_func, 173, 1, 2 },
+ { ahc_patch0_func, 174, 1, 1 },
+ { ahc_patch9_func, 185, 1, 2 },
+ { ahc_patch0_func, 186, 1, 1 },
+ { ahc_patch9_func, 195, 1, 2 },
+ { ahc_patch0_func, 196, 1, 1 },
+ { ahc_patch9_func, 212, 6, 2 },
+ { ahc_patch0_func, 218, 6, 1 },
+ { ahc_patch8_func, 226, 20, 2 },
+ { ahc_patch1_func, 241, 1, 1 },
+ { ahc_patch1_func, 248, 1, 2 },
+ { ahc_patch0_func, 249, 2, 2 },
+ { ahc_patch11_func, 250, 1, 1 },
+ { ahc_patch9_func, 258, 27, 3 },
+ { ahc_patch1_func, 274, 10, 2 },
+ { ahc_patch13_func, 277, 1, 1 },
+ { ahc_patch14_func, 285, 14, 1 },
+ { ahc_patch1_func, 301, 1, 2 },
+ { ahc_patch0_func, 302, 1, 1 },
+ { ahc_patch9_func, 305, 1, 1 },
+ { ahc_patch13_func, 310, 1, 1 },
+ { ahc_patch9_func, 311, 2, 2 },
+ { ahc_patch0_func, 313, 4, 1 },
+ { ahc_patch14_func, 317, 1, 1 },
+ { ahc_patch15_func, 319, 2, 3 },
+ { ahc_patch9_func, 319, 1, 2 },
+ { ahc_patch0_func, 320, 1, 1 },
+ { ahc_patch6_func, 325, 1, 2 },
+ { ahc_patch0_func, 326, 1, 1 },
+ { ahc_patch1_func, 330, 47, 11 },
+ { ahc_patch6_func, 337, 2, 4 },
+ { ahc_patch7_func, 337, 1, 1 },
+ { ahc_patch8_func, 338, 1, 1 },
+ { ahc_patch0_func, 339, 1, 1 },
+ { ahc_patch16_func, 340, 1, 1 },
+ { ahc_patch6_func, 356, 6, 3 },
+ { ahc_patch16_func, 356, 5, 1 },
+ { ahc_patch0_func, 362, 7, 1 },
+ { ahc_patch13_func, 372, 5, 1 },
+ { ahc_patch0_func, 377, 52, 17 },
+ { ahc_patch14_func, 377, 1, 1 },
+ { ahc_patch7_func, 379, 2, 2 },
+ { ahc_patch17_func, 380, 1, 1 },
+ { ahc_patch9_func, 383, 1, 1 },
+ { ahc_patch18_func, 390, 1, 1 },
+ { ahc_patch14_func, 395, 9, 3 },
+ { ahc_patch9_func, 396, 3, 2 },
+ { ahc_patch0_func, 399, 3, 1 },
+ { ahc_patch9_func, 407, 6, 2 },
+ { ahc_patch0_func, 413, 9, 2 },
+ { ahc_patch13_func, 413, 1, 1 },
+ { ahc_patch13_func, 422, 2, 1 },
+ { ahc_patch14_func, 424, 1, 1 },
+ { ahc_patch9_func, 426, 1, 2 },
+ { ahc_patch0_func, 427, 1, 1 },
+ { ahc_patch7_func, 428, 1, 1 },
+ { ahc_patch7_func, 429, 1, 1 },
+ { ahc_patch8_func, 430, 3, 3 },
+ { ahc_patch6_func, 431, 1, 2 },
+ { ahc_patch0_func, 432, 1, 1 },
+ { ahc_patch9_func, 433, 1, 1 },
+ { ahc_patch15_func, 434, 1, 2 },
+ { ahc_patch13_func, 434, 1, 1 },
+ { ahc_patch14_func, 436, 9, 4 },
+ { ahc_patch9_func, 436, 1, 1 },
+ { ahc_patch9_func, 443, 2, 1 },
+ { ahc_patch0_func, 445, 4, 3 },
+ { ahc_patch9_func, 445, 1, 2 },
+ { ahc_patch0_func, 446, 3, 1 },
+ { ahc_patch1_func, 450, 2, 1 },
+ { ahc_patch7_func, 452, 10, 2 },
+ { ahc_patch0_func, 462, 1, 1 },
+ { ahc_patch8_func, 463, 118, 22 },
+ { ahc_patch1_func, 465, 3, 2 },
+ { ahc_patch0_func, 468, 5, 3 },
+ { ahc_patch9_func, 468, 2, 2 },
+ { ahc_patch0_func, 470, 3, 1 },
+ { ahc_patch1_func, 475, 2, 2 },
+ { ahc_patch0_func, 477, 6, 3 },
+ { ahc_patch9_func, 477, 2, 2 },
+ { ahc_patch0_func, 479, 3, 1 },
+ { ahc_patch1_func, 485, 2, 2 },
+ { ahc_patch0_func, 487, 9, 7 },
+ { ahc_patch9_func, 487, 5, 6 },
+ { ahc_patch19_func, 487, 1, 2 },
+ { ahc_patch0_func, 488, 1, 1 },
+ { ahc_patch19_func, 490, 1, 2 },
+ { ahc_patch0_func, 491, 1, 1 },
+ { ahc_patch0_func, 492, 4, 1 },
+ { ahc_patch6_func, 497, 3, 2 },
+ { ahc_patch0_func, 500, 1, 1 },
+ { ahc_patch6_func, 510, 1, 2 },
+ { ahc_patch0_func, 511, 1, 1 },
+ { ahc_patch20_func, 548, 7, 1 },
+ { ahc_patch3_func, 583, 1, 2 },
+ { ahc_patch0_func, 584, 1, 1 },
+ { ahc_patch21_func, 587, 1, 1 },
+ { ahc_patch8_func, 589, 106, 33 },
+ { ahc_patch4_func, 591, 1, 1 },
+ { ahc_patch1_func, 597, 2, 2 },
+ { ahc_patch0_func, 599, 1, 1 },
+ { ahc_patch1_func, 602, 1, 2 },
+ { ahc_patch0_func, 603, 1, 1 },
+ { ahc_patch9_func, 604, 3, 3 },
+ { ahc_patch15_func, 605, 1, 1 },
+ { ahc_patch0_func, 607, 4, 1 },
+ { ahc_patch19_func, 616, 2, 2 },
+ { ahc_patch0_func, 618, 1, 1 },
+ { ahc_patch19_func, 622, 10, 3 },
+ { ahc_patch5_func, 624, 8, 1 },
+ { ahc_patch0_func, 632, 9, 2 },
+ { ahc_patch5_func, 633, 8, 1 },
+ { ahc_patch4_func, 643, 1, 2 },
+ { ahc_patch0_func, 644, 1, 1 },
+ { ahc_patch19_func, 645, 1, 2 },
+ { ahc_patch0_func, 646, 3, 2 },
+ { ahc_patch4_func, 648, 1, 1 },
+ { ahc_patch5_func, 649, 1, 1 },
+ { ahc_patch5_func, 652, 1, 1 },
+ { ahc_patch5_func, 654, 1, 1 },
+ { ahc_patch4_func, 656, 2, 2 },
+ { ahc_patch0_func, 658, 2, 1 },
+ { ahc_patch5_func, 660, 1, 1 },
+ { ahc_patch5_func, 663, 1, 1 },
+ { ahc_patch5_func, 666, 1, 1 },
+ { ahc_patch19_func, 670, 1, 1 },
+ { ahc_patch19_func, 673, 1, 1 },
+ { ahc_patch4_func, 679, 1, 1 },
+ { ahc_patch6_func, 682, 1, 2 },
+ { ahc_patch0_func, 683, 1, 1 },
+ { ahc_patch7_func, 695, 16, 1 },
+ { ahc_patch4_func, 711, 20, 1 },
+ { ahc_patch9_func, 732, 4, 2 },
+ { ahc_patch0_func, 736, 4, 1 },
+ { ahc_patch9_func, 740, 4, 2 },
+ { ahc_patch0_func, 744, 3, 1 },
+ { ahc_patch6_func, 750, 1, 1 },
+ { ahc_patch22_func, 752, 14, 1 },
+ { ahc_patch7_func, 766, 3, 1 },
+ { ahc_patch9_func, 778, 24, 8 },
+ { ahc_patch19_func, 782, 1, 2 },
+ { ahc_patch0_func, 783, 1, 1 },
+ { ahc_patch15_func, 788, 4, 2 },
+ { ahc_patch0_func, 792, 7, 3 },
+ { ahc_patch23_func, 792, 5, 2 },
+ { ahc_patch0_func, 797, 2, 1 },
+ { ahc_patch0_func, 802, 42, 3 },
+ { ahc_patch18_func, 814, 18, 2 },
+ { ahc_patch0_func, 832, 1, 1 },
+ { ahc_patch4_func, 856, 1, 1 },
+ { ahc_patch4_func, 857, 3, 2 },
+ { ahc_patch0_func, 860, 1, 1 },
+ { ahc_patch13_func, 861, 3, 1 },
+ { ahc_patch4_func, 864, 12, 1 }
+};
+
+static struct cs {
+ uint16_t begin;
+ uint16_t end;
+} critical_sections[] = {
+ { 11, 18 },
+ { 21, 30 },
+ { 711, 727 },
+ { 857, 860 },
+ { 864, 870 },
+ { 872, 874 },
+ { 874, 876 }
+};
+
+static const int num_critical_sections = sizeof(critical_sections)
+ / sizeof(*critical_sections);
diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile
new file mode 100644
index 000000000000..8c91fda6482c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/Makefile
@@ -0,0 +1,78 @@
+PROG= aicasm
+
+.SUFFIXES= .l .y .c .h
+
+CSRCS= aicasm.c aicasm_symbol.c
+YSRCS= aicasm_gram.y aicasm_macro_gram.y
+LSRCS= aicasm_scan.l aicasm_macro_scan.l
+
+GENHDRS= aicdb.h $(YSRCS:.y=.h)
+GENSRCS= $(YSRCS:.y=.c) $(LSRCS:.l=.c)
+
+SRCS= ${CSRCS} ${GENSRCS}
+LIBS= -ldb
+clean-files:= ${GENSRCS} ${GENHDRS} $(YSRCS:.y=.output) $(PROG)
+# Override default kernel CFLAGS. This is a userland app.
+AICASM_CFLAGS:= -I/usr/include -I.
+YFLAGS= -d
+
+NOMAN= noman
+
+ifneq ($(HOSTCC),)
+AICASM_CC= $(HOSTCC)
+else
+AICASM_CC= $(CC)
+endif
+
+ifdef DEBUG
+CFLAGS+= -DDEBUG -g
+YFLAGS+= -t -v
+LFLAGS= -d
+endif
+
+$(PROG): ${GENHDRS} $(SRCS)
+ $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG) $(LIBS)
+
+aicdb.h:
+ @if [ -e "/usr/include/db4/db_185.h" ]; then \
+ echo "#include <db4/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db3/db_185.h" ]; then \
+ echo "#include <db3/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db2/db_185.h" ]; then \
+ echo "#include <db2/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db1/db_185.h" ]; then \
+ echo "#include <db1/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db/db_185.h" ]; then \
+ echo "#include <db/db_185.h>" > aicdb.h; \
+ elif [ -e "/usr/include/db_185.h" ]; then \
+ echo "#include <db_185.h>" > aicdb.h; \
+ else \
+ echo "*** Install db development libraries"; \
+ fi
+
+clean:
+ rm -f $(clean-files)
+
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+aicasm_gram.c: aicasm_gram.h
+aicasm_gram.c aicasm_gram.h: aicasm_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) $<
+ mv $(<:.y=).tab.c $(<:.y=.c)
+ mv $(<:.y=).tab.h $(<:.y=.h)
+
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+aicasm_macro_gram.c: aicasm_macro_gram.h
+aicasm_macro_gram.c aicasm_macro_gram.h: aicasm_macro_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $<
+ mv $(<:.y=).tab.c $(<:.y=.c)
+ mv $(<:.y=).tab.h $(<:.y=.h)
+
+aicasm_scan.c: aicasm_scan.l
+ $(LEX) $(LFLAGS) -o$@ $<
+
+aicasm_macro_scan.c: aicasm_macro_scan.l
+ $(LEX) $(LFLAGS) -Pmm -o$@ $<
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.c b/drivers/scsi/aic7xxx/aicasm/aicasm.c
new file mode 100644
index 000000000000..c34639481904
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -0,0 +1,835 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler
+ *
+ * Copyright (c) 1997, 1998, 2000, 2001 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.c#22 $
+ *
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#if linux
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+typedef struct patch {
+ STAILQ_ENTRY(patch) links;
+ int patch_func;
+ u_int begin;
+ u_int skip_instr;
+ u_int skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(void);
+static void output_listing(char *ifilename);
+static void dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals);
+
+struct path_list search_path;
+int includes_search_curdir;
+char *appname;
+char *stock_include_file;
+FILE *ofile;
+char *ofilename;
+char *regfilename;
+FILE *regfile;
+char *listfilename;
+FILE *listfile;
+char *regdiagfilename;
+FILE *regdiagfile;
+int src_mode;
+int dst_mode;
+
+static STAILQ_HEAD(,instruction) seq_program;
+struct cs_tailq cs_tailq;
+struct scope_list scope_stack;
+symlist_t patch_functions;
+
+#if DEBUG
+extern int yy_flex_debug;
+extern int mm_flex_debug;
+extern int yydebug;
+extern int mmdebug;
+#endif
+extern FILE *yyin;
+extern int yyparse(void);
+
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int ch;
+ int retval;
+ char *inputfilename;
+ scope_t *sentinal;
+
+ STAILQ_INIT(&patches);
+ SLIST_INIT(&search_path);
+ STAILQ_INIT(&seq_program);
+ TAILQ_INIT(&cs_tailq);
+ SLIST_INIT(&scope_stack);
+
+ /* Set Sentinal scope node */
+ sentinal = scope_alloc();
+ sentinal->type = SCOPE_ROOT;
+
+ includes_search_curdir = 1;
+ appname = *argv;
+ regfile = NULL;
+ listfile = NULL;
+#if DEBUG
+ yy_flex_debug = 0;
+ mm_flex_debug = 0;
+ yydebug = 0;
+ mmdebug = 0;
+#endif
+ while ((ch = getopt(argc, argv, "d:i:l:n:o:p:r:I:")) != -1) {
+ switch(ch) {
+ case 'd':
+#if DEBUG
+ if (strcmp(optarg, "s") == 0) {
+ yy_flex_debug = 1;
+ mm_flex_debug = 1;
+ } else if (strcmp(optarg, "p") == 0) {
+ yydebug = 1;
+ mmdebug = 1;
+ } else {
+ fprintf(stderr, "%s: -d Requires either an "
+ "'s' or 'p' argument\n", appname);
+ usage();
+ }
+#else
+ stop("-d: Assembler not built with debugging "
+ "information", EX_SOFTWARE);
+#endif
+ break;
+ case 'i':
+ stock_include_file = optarg;
+ break;
+ case 'l':
+ /* Create a program listing */
+ if ((listfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ listfilename = optarg;
+ break;
+ case 'n':
+ /* Don't complain about the -nostdinc directrive */
+ if (strcmp(optarg, "ostdinc")) {
+ fprintf(stderr, "%s: Unknown option -%c%s\n",
+ appname, ch, optarg);
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'o':
+ if ((ofile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ ofilename = optarg;
+ break;
+ case 'p':
+ /* Create Register Diagnostic "printing" Functions */
+ if ((regdiagfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ regdiagfilename = optarg;
+ break;
+ case 'r':
+ if ((regfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ regfilename = optarg;
+ break;
+ case 'I':
+ {
+ path_entry_t include_dir;
+
+ if (strcmp(optarg, "-") == 0) {
+ if (includes_search_curdir == 0) {
+ fprintf(stderr, "%s: Warning - '-I-' "
+ "specified multiple "
+ "times\n", appname);
+ }
+ includes_search_curdir = 0;
+ for (include_dir = SLIST_FIRST(&search_path);
+ include_dir != NULL;
+ include_dir = SLIST_NEXT(include_dir,
+ links))
+ /*
+ * All entries before a '-I-' only
+ * apply to includes specified with
+ * quotes instead of "<>".
+ */
+ include_dir->quoted_includes_only = 1;
+ } else {
+ include_dir =
+ (path_entry_t)malloc(sizeof(*include_dir));
+ if (include_dir == NULL) {
+ perror(optarg);
+ stop(NULL, EX_OSERR);
+ }
+ include_dir->directory = strdup(optarg);
+ if (include_dir->directory == NULL) {
+ perror(optarg);
+ stop(NULL, EX_OSERR);
+ }
+ include_dir->quoted_includes_only = 0;
+ SLIST_INSERT_HEAD(&search_path, include_dir,
+ links);
+ }
+ break;
+ }
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ fprintf(stderr, "%s: No input file specifiled\n", appname);
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (regdiagfile != NULL
+ && (regfile == NULL || stock_include_file == NULL)) {
+ fprintf(stderr,
+ "%s: The -p option requires the -r and -i options.\n",
+ appname);
+ usage();
+ /* NOTREACHED */
+ }
+ symtable_open();
+ inputfilename = *argv;
+ include_file(*argv, SOURCE_FILE);
+ retval = yyparse();
+ if (retval == 0) {
+ if (SLIST_FIRST(&scope_stack) == NULL
+ || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+ stop("Unterminated conditional expression", EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /* Process outmost scope */
+ process_scope(SLIST_FIRST(&scope_stack));
+ /*
+ * Decend the tree of scopes and insert/emit
+ * patches as appropriate. We perform a depth first
+ * tranversal, recursively handling each scope.
+ */
+ /* start at the root scope */
+ dump_scope(SLIST_FIRST(&scope_stack));
+
+ /* Patch up forward jump addresses */
+ back_patch();
+
+ if (ofile != NULL)
+ output_code();
+ if (regfile != NULL)
+ symtable_dump(regfile, regdiagfile);
+ if (listfile != NULL)
+ output_listing(inputfilename);
+ }
+
+ stop(NULL, 0);
+ /* NOTREACHED */
+ return (0);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]\n"
+" [-r register_output_file [-p register_diag_file -i includefile]]\n"
+" [-l program_list_file]\n"
+" input_file\n", appname);
+ exit(EX_USAGE);
+}
+
+static void
+back_patch()
+{
+ struct instruction *cur_instr;
+
+ for (cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links)) {
+ if (cur_instr->patch_label != NULL) {
+ struct ins_format3 *f3_instr;
+ u_int address;
+
+ if (cur_instr->patch_label->type != LABEL) {
+ char buf[255];
+
+ snprintf(buf, sizeof(buf),
+ "Undefined label %s",
+ cur_instr->patch_label->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+ f3_instr = &cur_instr->format.format3;
+ address = f3_instr->address;
+ address += cur_instr->patch_label->info.linfo->address;
+ f3_instr->address = address;
+ }
+ }
+}
+
+static void
+output_code()
+{
+ struct instruction *cur_instr;
+ patch_t *cur_patch;
+ critical_section_t *cs;
+ symbol_node_t *cur_node;
+ int instrcount;
+
+ instrcount = 0;
+ fprintf(ofile,
+"/*\n"
+" * DO NOT EDIT - This file is automatically generated\n"
+" * from the following source files:\n"
+" *\n"
+"%s */\n", versions);
+
+ fprintf(ofile, "static uint8_t seqprog[] = {\n");
+ for (cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links)) {
+
+ fprintf(ofile, "%s\t0x%02x, 0x%02x, 0x%02x, 0x%02x",
+ cur_instr == STAILQ_FIRST(&seq_program) ? "" : ",\n",
+#if BYTE_ORDER == LITTLE_ENDIAN
+ cur_instr->format.bytes[0],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[3]);
+#else
+ cur_instr->format.bytes[3],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[0]);
+#endif
+ instrcount++;
+ }
+ fprintf(ofile, "\n};\n\n");
+
+ if (patch_arg_list == NULL)
+ stop("Patch argument list not defined",
+ EX_DATAERR);
+
+ /*
+ * Output patch information. Patch functions first.
+ */
+ fprintf(ofile,
+"typedef int %spatch_func_t (%s);\n", prefix, patch_arg_list);
+
+ for (cur_node = SLIST_FIRST(&patch_functions);
+ cur_node != NULL;
+ cur_node = SLIST_NEXT(cur_node,links)) {
+ fprintf(ofile,
+"static %spatch_func_t %spatch%d_func;\n"
+"\n"
+"static int\n"
+"%spatch%d_func(%s)\n"
+"{\n"
+" return (%s);\n"
+"}\n\n",
+ prefix,
+ prefix,
+ cur_node->symbol->info.condinfo->func_num,
+ prefix,
+ cur_node->symbol->info.condinfo->func_num,
+ patch_arg_list,
+ cur_node->symbol->name);
+ }
+
+ fprintf(ofile,
+"static struct patch {\n"
+" %spatch_func_t *patch_func;\n"
+" uint32_t begin :10,\n"
+" skip_instr :10,\n"
+" skip_patch :12;\n"
+"} patches[] = {\n", prefix);
+
+ for (cur_patch = STAILQ_FIRST(&patches);
+ cur_patch != NULL;
+ cur_patch = STAILQ_NEXT(cur_patch,links)) {
+ fprintf(ofile, "%s\t{ %spatch%d_func, %d, %d, %d }",
+ cur_patch == STAILQ_FIRST(&patches) ? "" : ",\n",
+ prefix,
+ cur_patch->patch_func, cur_patch->begin,
+ cur_patch->skip_instr, cur_patch->skip_patch);
+ }
+
+ fprintf(ofile, "\n};\n\n");
+
+ fprintf(ofile,
+"static struct cs {\n"
+" uint16_t begin;\n"
+" uint16_t end;\n"
+"} critical_sections[] = {\n");
+
+ for (cs = TAILQ_FIRST(&cs_tailq);
+ cs != NULL;
+ cs = TAILQ_NEXT(cs, links)) {
+ fprintf(ofile, "%s\t{ %d, %d }",
+ cs == TAILQ_FIRST(&cs_tailq) ? "" : ",\n",
+ cs->begin_addr, cs->end_addr);
+ }
+
+ fprintf(ofile, "\n};\n\n");
+
+ fprintf(ofile,
+"static const int num_critical_sections = sizeof(critical_sections)\n"
+" / sizeof(*critical_sections);\n");
+
+ fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
+}
+
+static void
+dump_scope(scope_t *scope)
+{
+ scope_t *cur_scope;
+
+ /*
+ * Emit the first patch for this scope
+ */
+ emit_patch(scope, 0);
+
+ /*
+ * Dump each scope within this one.
+ */
+ cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+ while (cur_scope != NULL) {
+
+ dump_scope(cur_scope);
+
+ cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+ }
+
+ /*
+ * Emit the second, closing, patch for this scope
+ */
+ emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+ patch_info_t *pinfo;
+ patch_t *new_patch;
+
+ pinfo = &scope->patches[patch];
+
+ if (pinfo->skip_instr == 0)
+ /* No-Op patch */
+ return;
+
+ new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+ if (new_patch == NULL)
+ stop("Could not malloc patch structure", EX_OSERR);
+
+ memset(new_patch, 0, sizeof(*new_patch));
+
+ if (patch == 0) {
+ new_patch->patch_func = scope->func_num;
+ new_patch->begin = scope->begin_addr;
+ } else {
+ new_patch->patch_func = 0;
+ new_patch->begin = scope->end_addr;
+ }
+ new_patch->skip_instr = pinfo->skip_instr;
+ new_patch->skip_patch = pinfo->skip_patch;
+ STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
+void
+output_listing(char *ifilename)
+{
+ char buf[1024];
+ FILE *ifile;
+ struct instruction *cur_instr;
+ patch_t *cur_patch;
+ symbol_node_t *cur_func;
+ int *func_values;
+ int instrcount;
+ int instrptr;
+ int line;
+ int func_count;
+ int skip_addr;
+
+ instrcount = 0;
+ instrptr = 0;
+ line = 1;
+ skip_addr = 0;
+ if ((ifile = fopen(ifilename, "r")) == NULL) {
+ perror(ifilename);
+ stop(NULL, EX_DATAERR);
+ }
+
+ /*
+ * Determine which options to apply to this listing.
+ */
+ for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL;
+ cur_func = SLIST_NEXT(cur_func, links))
+ func_count++;
+
+ func_values = NULL;
+ if (func_count != 0) {
+ func_values = (int *)malloc(func_count * sizeof(int));
+
+ if (func_values == NULL)
+ stop("Could not malloc", EX_OSERR);
+
+ func_values[0] = 0; /* FALSE func */
+ func_count--;
+
+ /*
+ * Ask the user to fill in the return values for
+ * the rest of the functions.
+ */
+
+
+ for (cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+ cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+ int input;
+
+ fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+ fprintf(stdout,
+ "Enter the return value for "
+ "this expression[T/F]:");
+
+ while (1) {
+
+ input = getchar();
+ input = toupper(input);
+
+ if (input == 'T') {
+ func_values[func_count] = 1;
+ break;
+ } else if (input == 'F') {
+ func_values[func_count] = 0;
+ break;
+ }
+ }
+ if (isatty(fileno(stdin)) == 0)
+ putchar(input);
+ }
+ fprintf(stdout, "\nThanks!\n");
+ }
+
+ /* Now output the listing */
+ cur_patch = STAILQ_FIRST(&patches);
+ for (cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
+
+ if (check_patch(&cur_patch, instrcount,
+ &skip_addr, func_values) == 0) {
+ /* Don't count this instruction as it is in a patch
+ * that was removed.
+ */
+ continue;
+ }
+
+ while (line < cur_instr->srcline) {
+ fgets(buf, sizeof(buf), ifile);
+ fprintf(listfile, "\t\t%s", buf);
+ line++;
+ }
+ fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr,
+#if BYTE_ORDER == LITTLE_ENDIAN
+ cur_instr->format.bytes[0],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[3]);
+#else
+ cur_instr->format.bytes[3],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[0]);
+#endif
+ fgets(buf, sizeof(buf), ifile);
+ fprintf(listfile, "\t%s", buf);
+ line++;
+ instrptr++;
+ }
+ /* Dump the remainder of the file */
+ while(fgets(buf, sizeof(buf), ifile) != NULL)
+ fprintf(listfile, "\t\t%s", buf);
+
+ fclose(ifile);
+}
+
+static int
+check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals)
+{
+ patch_t *cur_patch;
+
+ cur_patch = *start_patch;
+
+ while (cur_patch != NULL && start_instr == cur_patch->begin) {
+ if (func_vals[cur_patch->patch_func] == 0) {
+ int skip;
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ for (skip = cur_patch->skip_patch;
+ skip > 0 && cur_patch != NULL;
+ skip--)
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Print out error information if appropriate, and clean up before
+ * terminating the program.
+ */
+void
+stop(const char *string, int err_code)
+{
+ if (string != NULL) {
+ fprintf(stderr, "%s: ", appname);
+ if (yyfilename != NULL) {
+ fprintf(stderr, "Stopped at file %s, line %d - ",
+ yyfilename, yylineno);
+ }
+ fprintf(stderr, "%s\n", string);
+ }
+
+ if (ofile != NULL) {
+ fclose(ofile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, ofilename);
+ unlink(ofilename);
+ }
+ }
+
+ if (regfile != NULL) {
+ fclose(regfile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, regfilename);
+ unlink(regfilename);
+ }
+ }
+
+ if (listfile != NULL) {
+ fclose(listfile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, listfilename);
+ unlink(listfilename);
+ }
+ }
+
+ symlist_free(&patch_functions);
+ symtable_close();
+
+ exit(err_code);
+}
+
+struct instruction *
+seq_alloc()
+{
+ struct instruction *new_instr;
+
+ new_instr = (struct instruction *)malloc(sizeof(struct instruction));
+ if (new_instr == NULL)
+ stop("Unable to malloc instruction object", EX_SOFTWARE);
+ memset(new_instr, 0, sizeof(*new_instr));
+ STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
+ new_instr->srcline = yylineno;
+ return new_instr;
+}
+
+critical_section_t *
+cs_alloc()
+{
+ critical_section_t *new_cs;
+
+ new_cs= (critical_section_t *)malloc(sizeof(critical_section_t));
+ if (new_cs == NULL)
+ stop("Unable to malloc critical_section object", EX_SOFTWARE);
+ memset(new_cs, 0, sizeof(*new_cs));
+
+ TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links);
+ return new_cs;
+}
+
+scope_t *
+scope_alloc()
+{
+ scope_t *new_scope;
+
+ new_scope = (scope_t *)malloc(sizeof(scope_t));
+ if (new_scope == NULL)
+ stop("Unable to malloc scope object", EX_SOFTWARE);
+ memset(new_scope, 0, sizeof(*new_scope));
+ TAILQ_INIT(&new_scope->inner_scope);
+
+ if (SLIST_FIRST(&scope_stack) != NULL) {
+ TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+ new_scope, scope_links);
+ }
+ /* This patch is now the current scope */
+ SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+ return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+ /*
+ * We are "leaving" this scope. We should now have
+ * enough information to process the lists of scopes
+ * we encapsulate.
+ */
+ scope_t *cur_scope;
+ u_int skip_patch_count;
+ u_int skip_instr_count;
+
+ cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ while (cur_scope != NULL) {
+ u_int patch0_patch_skip;
+
+ patch0_patch_skip = 0;
+ switch (cur_scope->type) {
+ case SCOPE_IF:
+ case SCOPE_ELSE_IF:
+ if (skip_instr_count != 0) {
+ /* Create a tail patch */
+ patch0_patch_skip++;
+ cur_scope->patches[1].skip_patch =
+ skip_patch_count + 1;
+ cur_scope->patches[1].skip_instr =
+ skip_instr_count;
+ }
+
+ /* Count Head patch */
+ patch0_patch_skip++;
+
+ /* Count any patches contained in our inner scope */
+ patch0_patch_skip += cur_scope->inner_scope_patches;
+
+ cur_scope->patches[0].skip_patch = patch0_patch_skip;
+ cur_scope->patches[0].skip_instr =
+ cur_scope->end_addr - cur_scope->begin_addr;
+
+ skip_instr_count += cur_scope->patches[0].skip_instr;
+
+ skip_patch_count += patch0_patch_skip;
+ if (cur_scope->type == SCOPE_IF) {
+ scope->inner_scope_patches += skip_patch_count;
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ }
+ break;
+ case SCOPE_ELSE:
+ /* Count any patches contained in our innter scope */
+ skip_patch_count += cur_scope->inner_scope_patches;
+
+ skip_instr_count += cur_scope->end_addr
+ - cur_scope->begin_addr;
+ break;
+ case SCOPE_ROOT:
+ stop("Unexpected scope type encountered", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+ }
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.h b/drivers/scsi/aic7xxx/aicasm/aicasm.h
new file mode 100644
index 000000000000..51678dd46ff7
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.h
@@ -0,0 +1,95 @@
+/*
+ * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.h#14 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct path_entry {
+ char *directory;
+ int quoted_includes_only;
+ SLIST_ENTRY(path_entry) links;
+} *path_entry_t;
+
+typedef enum {
+ QUOTED_INCLUDE,
+ BRACKETED_INCLUDE,
+ SOURCE_FILE
+} include_type;
+
+SLIST_HEAD(path_list, path_entry);
+
+extern struct path_list search_path;
+extern struct cs_tailq cs_tailq;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
+extern int includes_search_curdir; /* False if we've seen -I- */
+extern char *appname;
+extern char *stock_include_file;
+extern int yylineno;
+extern char *yyfilename;
+extern char *prefix;
+extern char *patch_arg_list;
+extern char *versions;
+extern int src_mode;
+extern int dst_mode;
+struct symbol;
+
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+void expand_macro(struct symbol *macro_symbol);
+struct instruction *seq_alloc(void);
+struct critical_section *cs_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
new file mode 100644
index 000000000000..67e046d96625
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
@@ -0,0 +1,1945 @@
+%{
+/*
+ * Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_gram.y#29 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+int yylineno;
+char *yyfilename;
+char stock_prefix[] = "aic_";
+char *prefix = stock_prefix;
+char *patch_arg_list;
+char *versions;
+static char errbuf[255];
+static char regex_pattern[255];
+static symbol_t *cur_symbol;
+static symbol_t *field_symbol;
+static symbol_t *scb_or_sram_symbol;
+static symtype cur_symtype;
+static symbol_ref_t accumulator;
+static symbol_ref_t mode_ptr;
+static symbol_ref_t allones;
+static symbol_ref_t allzeros;
+static symbol_ref_t none;
+static symbol_ref_t sindex;
+static int instruction_ptr;
+static int num_srams;
+static int sram_or_scb_offset;
+static int download_constant_count;
+static int in_critical_section;
+static u_int enum_increment;
+static u_int enum_next_value;
+
+static void process_field(int field_type, symbol_t *sym, int mask);
+static void initialize_symbol(symbol_t *symbol);
+static void add_macro_arg(const char *argtext, int position);
+static void add_macro_body(const char *bodytext);
+static void process_register(symbol_t **p_symbol);
+static void format_1_instr(int opcode, symbol_ref_t *dest,
+ expression_t *immed, symbol_ref_t *src, int ret);
+static void format_2_instr(int opcode, symbol_ref_t *dest,
+ expression_t *places, symbol_ref_t *src, int ret);
+static void format_3_instr(int opcode, symbol_ref_t *src,
+ expression_t *immed, symbol_ref_t *address);
+static void test_readable_symbol(symbol_t *symbol);
+static void test_writable_symbol(symbol_t *symbol);
+static void type_check(symbol_t *symbol, expression_t *expression, int and_op);
+static void make_expression(expression_t *immed, int value);
+static void add_conditional(symbol_t *symbol);
+static void add_version(const char *verstring);
+static int is_download_const(expression_t *immed);
+
+#define SRAM_SYMNAME "SRAM_BASE"
+#define SCB_SYMNAME "SCB_BASE"
+%}
+
+%union {
+ u_int value;
+ char *str;
+ symbol_t *sym;
+ symbol_ref_t sym_ref;
+ expression_t expression;
+}
+
+%token T_REGISTER
+
+%token <value> T_CONST
+
+%token T_EXPORT
+
+%token T_DOWNLOAD
+
+%token T_SCB
+
+%token T_SRAM
+
+%token T_ALIAS
+
+%token T_SIZE
+
+%token T_EXPR_LSHIFT
+
+%token T_EXPR_RSHIFT
+
+%token <value> T_ADDRESS
+
+%token T_ACCESS_MODE
+
+%token T_MODES
+
+%token T_DEFINE
+
+%token T_SET_SRC_MODE
+
+%token T_SET_DST_MODE
+
+%token <value> T_MODE
+
+%token T_BEGIN_CS
+
+%token T_END_CS
+
+%token T_FIELD
+
+%token T_ENUM
+
+%token T_MASK
+
+%token <value> T_NUMBER
+
+%token <str> T_PATH T_STRING T_ARG T_MACROBODY
+
+%token <sym> T_CEXPR
+
+%token T_EOF T_INCLUDE T_VERSION T_PREFIX T_PATCH_ARG_LIST
+
+%token <value> T_SHR T_SHL T_ROR T_ROL
+
+%token <value> T_MVI T_MOV T_CLR T_BMOV
+
+%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
+
+%token <value> T_ADD T_ADC
+
+%token <value> T_INC T_DEC
+
+%token <value> T_STC T_CLC
+
+%token <value> T_CMP T_NOT T_XOR
+
+%token <value> T_TEST T_AND
+
+%token <value> T_OR
+
+%token T_RET
+
+%token T_NOP
+
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX T_MODE_PTR
+
+%token T_A
+
+%token <sym> T_SYMBOL
+
+%token T_NL
+
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
+
+%type <sym_ref> reg_symbol address destination source opt_source
+
+%type <expression> expression immediate immediate_or_a
+
+%type <value> export ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+
+%type <value> mode_value mode_list macro_arglist
+
+%left '|'
+%left '&'
+%left T_EXPR_LSHIFT T_EXPR_RSHIFT
+%left '+' '-'
+%left '*' '/'
+%right '~'
+%nonassoc UMINUS
+%%
+
+program:
+ include
+| program include
+| prefix
+| program prefix
+| patch_arg_list
+| program patch_arg_list
+| version
+| program version
+| register
+| program register
+| constant
+| program constant
+| macrodefn
+| program macrodefn
+| scratch_ram
+| program scratch_ram
+| scb
+| program scb
+| label
+| program label
+| set_src_mode
+| program set_src_mode
+| set_dst_mode
+| program set_dst_mode
+| critical_section_start
+| program critical_section_start
+| critical_section_end
+| program critical_section_end
+| conditional
+| program conditional
+| code
+| program code
+;
+
+include:
+ T_INCLUDE '<' T_PATH '>'
+ {
+ include_file($3, BRACKETED_INCLUDE);
+ }
+| T_INCLUDE '"' T_PATH '"'
+ {
+ include_file($3, QUOTED_INCLUDE);
+ }
+;
+
+prefix:
+ T_PREFIX '=' T_STRING
+ {
+ if (prefix != stock_prefix)
+ stop("Prefix multiply defined",
+ EX_DATAERR);
+ prefix = strdup($3);
+ if (prefix == NULL)
+ stop("Unable to record prefix", EX_SOFTWARE);
+ }
+;
+
+patch_arg_list:
+ T_PATCH_ARG_LIST '=' T_STRING
+ {
+ if (patch_arg_list != NULL)
+ stop("Patch argument list multiply defined",
+ EX_DATAERR);
+ patch_arg_list = strdup($3);
+ if (patch_arg_list == NULL)
+ stop("Unable to record patch arg list", EX_SOFTWARE);
+ }
+;
+
+version:
+ T_VERSION '=' T_STRING
+ { add_version($3); }
+;
+
+register:
+ T_REGISTER { cur_symtype = REGISTER; } reg_definition
+;
+
+reg_definition:
+ T_SYMBOL '{'
+ {
+ if ($1->type != UNINITIALIZED) {
+ stop("Register multiply defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cur_symbol = $1;
+ cur_symbol->type = cur_symtype;
+ initialize_symbol(cur_symbol);
+ }
+ reg_attribute_list
+ '}'
+ {
+ /*
+ * Default to allowing everything in for registers
+ * with no bit or mask definitions.
+ */
+ if (cur_symbol->info.rinfo->valid_bitmask == 0)
+ cur_symbol->info.rinfo->valid_bitmask = 0xFF;
+
+ if (cur_symbol->info.rinfo->size == 0)
+ cur_symbol->info.rinfo->size = 1;
+
+ /*
+ * This might be useful for registers too.
+ */
+ if (cur_symbol->type != REGISTER) {
+ if (cur_symbol->info.rinfo->address == 0)
+ cur_symbol->info.rinfo->address =
+ sram_or_scb_offset;
+ sram_or_scb_offset +=
+ cur_symbol->info.rinfo->size;
+ }
+ cur_symbol = NULL;
+ }
+;
+
+reg_attribute_list:
+ reg_attribute
+| reg_attribute_list reg_attribute
+;
+
+reg_attribute:
+ reg_address
+| size
+| access_mode
+| modes
+| field_defn
+| enum_defn
+| mask_defn
+| alias
+| accumulator
+| mode_pointer
+| allones
+| allzeros
+| none
+| sindex
+;
+
+reg_address:
+ T_ADDRESS T_NUMBER
+ {
+ cur_symbol->info.rinfo->address = $2;
+ }
+;
+
+size:
+ T_SIZE T_NUMBER
+ {
+ cur_symbol->info.rinfo->size = $2;
+ if (scb_or_sram_symbol != NULL) {
+ u_int max_addr;
+ u_int sym_max_addr;
+
+ max_addr = scb_or_sram_symbol->info.rinfo->address
+ + scb_or_sram_symbol->info.rinfo->size;
+ sym_max_addr = cur_symbol->info.rinfo->address
+ + cur_symbol->info.rinfo->size;
+
+ if (sym_max_addr > max_addr)
+ stop("SCB or SRAM space exhausted", EX_DATAERR);
+ }
+ }
+;
+
+access_mode:
+ T_ACCESS_MODE T_MODE
+ {
+ cur_symbol->info.rinfo->mode = $2;
+ }
+;
+
+modes:
+ T_MODES mode_list
+ {
+ cur_symbol->info.rinfo->modes = $2;
+ }
+;
+
+mode_list:
+ mode_value
+ {
+ $$ = $1;
+ }
+| mode_list ',' mode_value
+ {
+ $$ = $1 | $3;
+ }
+;
+
+mode_value:
+ T_NUMBER
+ {
+ if ($1 > 4) {
+ stop("Valid register modes range between 0 and 4.",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ $$ = (0x1 << $1);
+ }
+| T_SYMBOL
+ {
+ symbol_t *symbol;
+
+ symbol = $1;
+ if (symbol->type != CONST) {
+ stop("Only \"const\" symbols allowed in "
+ "mode definitions.", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if (symbol->info.cinfo->value > 4) {
+ stop("Valid register modes range between 0 and 4.",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$ = (0x1 << symbol->info.cinfo->value);
+ }
+;
+
+field_defn:
+ T_FIELD
+ {
+ field_symbol = NULL;
+ enum_next_value = 0;
+ enum_increment = 1;
+ }
+ '{' enum_entry_list '}'
+| T_FIELD T_SYMBOL expression
+ {
+ process_field(FIELD, $2, $3.value);
+ field_symbol = $2;
+ enum_next_value = 0;
+ enum_increment = 0x01 << (ffs($3.value) - 1);
+ }
+ '{' enum_entry_list '}'
+| T_FIELD T_SYMBOL expression
+ {
+ process_field(FIELD, $2, $3.value);
+ }
+;
+
+enum_defn:
+ T_ENUM
+ {
+ field_symbol = NULL;
+ enum_next_value = 0;
+ enum_increment = 1;
+ }
+ '{' enum_entry_list '}'
+| T_ENUM T_SYMBOL expression
+ {
+ process_field(ENUM, $2, $3.value);
+ field_symbol = $2;
+ enum_next_value = 0;
+ enum_increment = 0x01 << (ffs($3.value) - 1);
+ }
+ '{' enum_entry_list '}'
+;
+
+enum_entry_list:
+ enum_entry
+| enum_entry_list ',' enum_entry
+;
+
+enum_entry:
+ T_SYMBOL
+ {
+ process_field(ENUM_ENTRY, $1, enum_next_value);
+ enum_next_value += enum_increment;
+ }
+| T_SYMBOL expression
+ {
+ process_field(ENUM_ENTRY, $1, $2.value);
+ enum_next_value = $2.value + enum_increment;
+ }
+;
+
+mask_defn:
+ T_MASK T_SYMBOL expression
+ {
+ process_field(MASK, $2, $3.value);
+ }
+;
+
+alias:
+ T_ALIAS T_SYMBOL
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of register alias",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = ALIAS;
+ initialize_symbol($2);
+ $2->info.ainfo->parent = cur_symbol;
+ }
+;
+
+accumulator:
+ T_ACCUM
+ {
+ if (accumulator.symbol != NULL) {
+ stop("Only one accumulator definition allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ accumulator.symbol = cur_symbol;
+ }
+;
+
+mode_pointer:
+ T_MODE_PTR
+ {
+ if (mode_ptr.symbol != NULL) {
+ stop("Only one mode pointer definition allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ mode_ptr.symbol = cur_symbol;
+ }
+;
+
+allones:
+ T_ALLONES
+ {
+ if (allones.symbol != NULL) {
+ stop("Only one definition of allones allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ allones.symbol = cur_symbol;
+ }
+;
+
+allzeros:
+ T_ALLZEROS
+ {
+ if (allzeros.symbol != NULL) {
+ stop("Only one definition of allzeros allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ allzeros.symbol = cur_symbol;
+ }
+;
+
+none:
+ T_NONE
+ {
+ if (none.symbol != NULL) {
+ stop("Only one definition of none allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ none.symbol = cur_symbol;
+ }
+;
+
+sindex:
+ T_SINDEX
+ {
+ if (sindex.symbol != NULL) {
+ stop("Only one definition of sindex allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ sindex.symbol = cur_symbol;
+ }
+;
+
+expression:
+ expression '|' expression
+ {
+ $$.value = $1.value | $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '&' expression
+ {
+ $$.value = $1.value & $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '+' expression
+ {
+ $$.value = $1.value + $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '-' expression
+ {
+ $$.value = $1.value - $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| expression '*' expression
+ {
+ $$.value = $1.value * $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| expression '/' expression
+ {
+ $$.value = $1.value / $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| expression T_EXPR_LSHIFT expression
+ {
+ $$.value = $1.value << $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression T_EXPR_RSHIFT expression
+ {
+ $$.value = $1.value >> $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| '(' expression ')'
+ {
+ $$ = $2;
+ }
+| '~' expression
+ {
+ $$ = $2;
+ $$.value = (~$$.value) & 0xFF;
+ }
+| '-' expression %prec UMINUS
+ {
+ $$ = $2;
+ $$.value = -$$.value;
+ }
+| T_NUMBER
+ {
+ $$.value = $1;
+ SLIST_INIT(&$$.referenced_syms);
+ }
+| T_SYMBOL
+ {
+ symbol_t *symbol;
+
+ symbol = $1;
+ switch (symbol->type) {
+ case ALIAS:
+ symbol = $1->info.ainfo->parent;
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ $$.value = symbol->info.rinfo->address;
+ break;
+ case MASK:
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ $$.value = symbol->info.finfo->value;
+ break;
+ case DOWNLOAD_CONST:
+ case CONST:
+ $$.value = symbol->info.cinfo->value;
+ break;
+ case UNINITIALIZED:
+ default:
+ {
+ snprintf(errbuf, sizeof(errbuf),
+ "Undefined symbol %s referenced",
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ break;
+ }
+ }
+ SLIST_INIT(&$$.referenced_syms);
+ symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
+ }
+;
+
+constant:
+ T_CONST T_SYMBOL expression
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a constant",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = CONST;
+ initialize_symbol($2);
+ $2->info.cinfo->value = $3.value;
+ }
+| T_CONST T_SYMBOL T_DOWNLOAD
+ {
+ if ($1) {
+ stop("Invalid downloaded constant declaration",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a downloaded constant",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = DOWNLOAD_CONST;
+ initialize_symbol($2);
+ $2->info.cinfo->value = download_constant_count++;
+ }
+;
+
+macrodefn_prologue:
+ T_DEFINE T_SYMBOL
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a macro",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cur_symbol = $2;
+ cur_symbol->type = MACRO;
+ initialize_symbol(cur_symbol);
+ }
+;
+
+macrodefn:
+ macrodefn_prologue T_MACROBODY
+ {
+ add_macro_body($2);
+ }
+| macrodefn_prologue '(' macro_arglist ')' T_MACROBODY
+ {
+ add_macro_body($5);
+ cur_symbol->info.macroinfo->narg = $3;
+ }
+;
+
+macro_arglist:
+ {
+ /* Macros can take no arguments */
+ $$ = 0;
+ }
+| T_ARG
+ {
+ $$ = 1;
+ add_macro_arg($1, 0);
+ }
+| macro_arglist ',' T_ARG
+ {
+ if ($1 == 0) {
+ stop("Comma without preceeding argument in arg list",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$ = $1 + 1;
+ add_macro_arg($3, $1);
+ }
+;
+
+scratch_ram:
+ T_SRAM '{'
+ {
+ snprintf(errbuf, sizeof(errbuf), "%s%d", SRAM_SYMNAME,
+ num_srams);
+ cur_symbol = symtable_get(SRAM_SYMNAME);
+ cur_symtype = SRAMLOC;
+ cur_symbol->type = SRAMLOC;
+ initialize_symbol(cur_symbol);
+ }
+ reg_address
+ {
+ sram_or_scb_offset = cur_symbol->info.rinfo->address;
+ }
+ size
+ {
+ scb_or_sram_symbol = cur_symbol;
+ }
+ scb_or_sram_attributes
+ '}'
+ {
+ cur_symbol = NULL;
+ scb_or_sram_symbol = NULL;
+ }
+;
+
+scb:
+ T_SCB '{'
+ {
+ cur_symbol = symtable_get(SCB_SYMNAME);
+ cur_symtype = SCBLOC;
+ if (cur_symbol->type != UNINITIALIZED) {
+ stop("Only one SRAM definition allowed",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ cur_symbol->type = SCBLOC;
+ initialize_symbol(cur_symbol);
+ /* 64 bytes of SCB space */
+ cur_symbol->info.rinfo->size = 64;
+ }
+ reg_address
+ {
+ sram_or_scb_offset = cur_symbol->info.rinfo->address;
+ }
+ size
+ {
+ scb_or_sram_symbol = cur_symbol;
+ }
+ scb_or_sram_attributes
+ '}'
+ {
+ cur_symbol = NULL;
+ scb_or_sram_symbol = NULL;
+ }
+;
+
+scb_or_sram_attributes:
+ /* NULL definition is okay */
+| modes
+| scb_or_sram_reg_list
+| modes scb_or_sram_reg_list
+;
+
+scb_or_sram_reg_list:
+ reg_definition
+| scb_or_sram_reg_list reg_definition
+;
+
+reg_symbol:
+ T_SYMBOL
+ {
+ process_register(&$1);
+ $$.symbol = $1;
+ $$.offset = 0;
+ }
+| T_SYMBOL '[' T_SYMBOL ']'
+ {
+ process_register(&$1);
+ if ($3->type != CONST) {
+ stop("register offset must be a constant", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) {
+ stop("Accessing offset beyond range of register",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = $1;
+ $$.offset = $3->info.cinfo->value;
+ }
+| T_SYMBOL '[' T_NUMBER ']'
+ {
+ process_register(&$1);
+ if (($3 + 1) > $1->info.rinfo->size) {
+ stop("Accessing offset beyond range of register",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = $1;
+ $$.offset = $3;
+ }
+| T_A
+ {
+ if (accumulator.symbol == NULL) {
+ stop("No accumulator has been defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = accumulator.symbol;
+ $$.offset = 0;
+ }
+;
+
+destination:
+ reg_symbol
+ {
+ test_writable_symbol($1.symbol);
+ $$ = $1;
+ }
+;
+
+immediate:
+ expression
+ { $$ = $1; }
+;
+
+immediate_or_a:
+ expression
+ {
+ if ($1.value == 0 && is_download_const(&$1) == 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "\nExpression evaluates to 0 and thus "
+ "references the accumulator.\n "
+ "If this is the desired effect, use 'A' "
+ "instead.\n");
+ stop(errbuf, EX_DATAERR);
+ }
+ $$ = $1;
+ }
+| T_A
+ {
+ SLIST_INIT(&$$.referenced_syms);
+ symlist_add(&$$.referenced_syms, accumulator.symbol,
+ SYMLIST_INSERT_HEAD);
+ $$.value = 0;
+ }
+;
+
+source:
+ reg_symbol
+ {
+ test_readable_symbol($1.symbol);
+ $$ = $1;
+ }
+;
+
+opt_source:
+ {
+ $$.symbol = NULL;
+ $$.offset = 0;
+ }
+| ',' source
+ { $$ = $2; }
+;
+
+ret:
+ { $$ = 0; }
+| T_RET
+ { $$ = 1; }
+;
+
+set_src_mode:
+ T_SET_SRC_MODE T_NUMBER ';'
+ {
+ src_mode = $2;
+ }
+;
+
+set_dst_mode:
+ T_SET_DST_MODE T_NUMBER ';'
+ {
+ dst_mode = $2;
+ }
+;
+
+critical_section_start:
+ T_BEGIN_CS ';'
+ {
+ critical_section_t *cs;
+
+ if (in_critical_section != FALSE) {
+ stop("Critical Section within Critical Section",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cs = cs_alloc();
+ cs->begin_addr = instruction_ptr;
+ in_critical_section = TRUE;
+ }
+;
+
+critical_section_end:
+ T_END_CS ';'
+ {
+ critical_section_t *cs;
+
+ if (in_critical_section == FALSE) {
+ stop("Unballanced 'end_cs'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cs = TAILQ_LAST(&cs_tailq, cs_tailq);
+ cs->end_addr = instruction_ptr;
+ in_critical_section = FALSE;
+ }
+;
+
+export:
+ { $$ = 0; }
+| T_EXPORT
+ { $$ = 1; }
+;
+
+label:
+ export T_SYMBOL ':'
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Program label multiply defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = LABEL;
+ initialize_symbol($2);
+ $2->info.linfo->address = instruction_ptr;
+ $2->info.linfo->exported = $1;
+ }
+;
+
+address:
+ T_SYMBOL
+ {
+ $$.symbol = $1;
+ $$.offset = 0;
+ }
+| T_SYMBOL '+' T_NUMBER
+ {
+ $$.symbol = $1;
+ $$.offset = $3;
+ }
+| T_SYMBOL '-' T_NUMBER
+ {
+ $$.symbol = $1;
+ $$.offset = -$3;
+ }
+| '.'
+ {
+ $$.symbol = NULL;
+ $$.offset = 0;
+ }
+| '.' '+' T_NUMBER
+ {
+ $$.symbol = NULL;
+ $$.offset = $3;
+ }
+| '.' '-' T_NUMBER
+ {
+ $$.symbol = NULL;
+ $$.offset = -$3;
+ }
+;
+
+conditional:
+ T_IF T_CEXPR '{'
+ {
+ scope_t *new_scope;
+
+ add_conditional($2);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $2->info.condinfo->func_num;
+ }
+| T_ELSE T_IF T_CEXPR '{'
+ {
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == T_ELSE) {
+
+ stop("'else if' without leading 'if'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ add_conditional($3);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $3->info.condinfo->func_num;
+ }
+| T_ELSE '{'
+ {
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == SCOPE_ELSE) {
+
+ stop("'else' without leading 'if'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE;
+ new_scope->begin_addr = instruction_ptr;
+ }
+;
+
+conditional:
+ '}'
+ {
+ scope_t *scope_context;
+
+ scope_context = SLIST_FIRST(&scope_stack);
+ if (scope_context->type == SCOPE_ROOT) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ scope_context->end_addr = instruction_ptr;
+
+ /* Pop the scope */
+ SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+ process_scope(scope_context);
+
+ if (SLIST_FIRST(&scope_stack) == NULL) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+;
+
+f1_opcode:
+ T_AND { $$ = AIC_OP_AND; }
+| T_XOR { $$ = AIC_OP_XOR; }
+| T_ADD { $$ = AIC_OP_ADD; }
+| T_ADC { $$ = AIC_OP_ADC; }
+;
+
+code:
+ f1_opcode destination ',' immediate_or_a opt_source ret ';'
+ {
+ format_1_instr($1, &$2, &$4, &$5, $6);
+ }
+;
+
+code:
+ T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
+ {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
+ }
+;
+
+code:
+ T_INC destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_DEC destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, -1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_CLC ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, -1);
+ format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
+ }
+| T_CLC T_MVI destination ',' immediate_or_a ret ';'
+ {
+ format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
+ }
+;
+
+code:
+ T_STC ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
+ }
+| T_STC destination ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
+ }
+;
+
+code:
+ T_BMOV destination ',' source ',' immediate ret ';'
+ {
+ format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+ }
+;
+
+code:
+ T_MOV destination ',' source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_BMOV, &$2, &immed, &$4, $5);
+ }
+;
+
+code:
+ T_MVI destination ',' immediate ret ';'
+ {
+ if ($4.value == 0
+ && is_download_const(&$4) == 0) {
+ expression_t immed;
+
+ /*
+ * Allow move immediates of 0 so that macros,
+ * that can't know the immediate's value and
+ * otherwise compensate, still work.
+ */
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_BMOV, &$2, &immed, &allzeros, $5);
+ } else {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+ }
+ }
+;
+
+code:
+ T_NOT destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_CLR destination ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
+ }
+;
+
+code:
+ T_NOP ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
+ }
+;
+
+code:
+ T_RET ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
+ }
+;
+
+ /*
+ * This grammer differs from the one in the aic7xxx
+ * reference manual since the grammer listed there is
+ * ambiguous and causes a shift/reduce conflict.
+ * It also seems more logical as the "immediate"
+ * argument is listed as the second arg like the
+ * other formats.
+ */
+
+f2_opcode:
+ T_SHL { $$ = AIC_OP_SHL; }
+| T_SHR { $$ = AIC_OP_SHR; }
+| T_ROL { $$ = AIC_OP_ROL; }
+| T_ROR { $$ = AIC_OP_ROR; }
+;
+
+code:
+ f2_opcode destination ',' expression opt_source ret ';'
+ {
+ format_2_instr($1, &$2, &$4, &$5, $6);
+ }
+;
+
+jmp_jc_jnc_call:
+ T_JMP { $$ = AIC_OP_JMP; }
+| T_JC { $$ = AIC_OP_JC; }
+| T_JNC { $$ = AIC_OP_JNC; }
+| T_CALL { $$ = AIC_OP_CALL; }
+;
+
+jz_jnz:
+ T_JZ { $$ = AIC_OP_JZ; }
+| T_JNZ { $$ = AIC_OP_JNZ; }
+;
+
+je_jne:
+ T_JE { $$ = AIC_OP_JE; }
+| T_JNE { $$ = AIC_OP_JNE; }
+;
+
+code:
+ jmp_jc_jnc_call address ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0);
+ format_3_instr($1, &sindex, &immed, &$2);
+ }
+;
+
+code:
+ T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_TEST source ',' immediate_or_a jz_jnz address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_CMP source ',' immediate_or_a je_jne address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_MOV source jmp_jc_jnc_call address ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0);
+ format_3_instr($3, &$2, &immed, &$4);
+ }
+;
+
+code:
+ T_MVI immediate jmp_jc_jnc_call address ';'
+ {
+ format_3_instr($3, &allzeros, &$2, &$4);
+ }
+;
+
+%%
+
+static void
+process_field(int field_type, symbol_t *sym, int value)
+{
+ /*
+ * Add the current register to its
+ * symbol list, if it already exists,
+ * warn if we are setting it to a
+ * different value, or in the bit to
+ * the "allowed bits" of this register.
+ */
+ if (sym->type == UNINITIALIZED) {
+ sym->type = field_type;
+ initialize_symbol(sym);
+ sym->info.finfo->value = value;
+ if (field_type != ENUM_ENTRY) {
+ if (field_type != MASK && value == 0) {
+ stop("Empty Field, or Enum", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ sym->info.finfo->value = value;
+ sym->info.finfo->mask = value;
+ } else if (field_symbol != NULL) {
+ sym->info.finfo->mask = field_symbol->info.finfo->value;
+ } else {
+ sym->info.finfo->mask = 0xFF;
+ }
+ } else if (sym->type != field_type) {
+ stop("Field definition mirrors a definition of the same "
+ " name, but a different type", EX_DATAERR);
+ /* NOTREACHED */
+ } else if (value != sym->info.finfo->value) {
+ stop("Field redefined with a conflicting value", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ /* Fail if this symbol is already listed */
+ if (symlist_search(&(sym->info.finfo->symrefs),
+ cur_symbol->name) != NULL) {
+ stop("Field defined multiple times for register", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ symlist_add(&(sym->info.finfo->symrefs), cur_symbol,
+ SYMLIST_INSERT_HEAD);
+ cur_symbol->info.rinfo->valid_bitmask |= sym->info.finfo->mask;
+ cur_symbol->info.rinfo->typecheck_masks = TRUE;
+ symlist_add(&(cur_symbol->info.rinfo->fields), sym, SYMLIST_SORT);
+}
+
+static void
+initialize_symbol(symbol_t *symbol)
+{
+ switch (symbol->type) {
+ case UNINITIALIZED:
+ stop("Call to initialize_symbol with type field unset",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ case REGISTER:
+ case SRAMLOC:
+ case SCBLOC:
+ symbol->info.rinfo =
+ (struct reg_info *)malloc(sizeof(struct reg_info));
+ if (symbol->info.rinfo == NULL) {
+ stop("Can't create register info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.rinfo, 0,
+ sizeof(struct reg_info));
+ SLIST_INIT(&(symbol->info.rinfo->fields));
+ /*
+ * Default to allowing access in all register modes
+ * or to the mode specified by the SCB or SRAM space
+ * we are in.
+ */
+ if (scb_or_sram_symbol != NULL)
+ symbol->info.rinfo->modes =
+ scb_or_sram_symbol->info.rinfo->modes;
+ else
+ symbol->info.rinfo->modes = ~0;
+ break;
+ case ALIAS:
+ symbol->info.ainfo =
+ (struct alias_info *)malloc(sizeof(struct alias_info));
+ if (symbol->info.ainfo == NULL) {
+ stop("Can't create alias info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.ainfo, 0,
+ sizeof(struct alias_info));
+ break;
+ case MASK:
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ symbol->info.finfo =
+ (struct field_info *)malloc(sizeof(struct field_info));
+ if (symbol->info.finfo == NULL) {
+ stop("Can't create field info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.finfo, 0, sizeof(struct field_info));
+ SLIST_INIT(&(symbol->info.finfo->symrefs));
+ break;
+ case CONST:
+ case DOWNLOAD_CONST:
+ symbol->info.cinfo =
+ (struct const_info *)malloc(sizeof(struct const_info));
+ if (symbol->info.cinfo == NULL) {
+ stop("Can't create alias info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.cinfo, 0,
+ sizeof(struct const_info));
+ break;
+ case LABEL:
+ symbol->info.linfo =
+ (struct label_info *)malloc(sizeof(struct label_info));
+ if (symbol->info.linfo == NULL) {
+ stop("Can't create label info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.linfo, 0,
+ sizeof(struct label_info));
+ break;
+ case CONDITIONAL:
+ symbol->info.condinfo =
+ (struct cond_info *)malloc(sizeof(struct cond_info));
+ if (symbol->info.condinfo == NULL) {
+ stop("Can't create conditional info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.condinfo, 0,
+ sizeof(struct cond_info));
+ break;
+ case MACRO:
+ symbol->info.macroinfo =
+ (struct macro_info *)malloc(sizeof(struct macro_info));
+ if (symbol->info.macroinfo == NULL) {
+ stop("Can't create macro info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.macroinfo, 0,
+ sizeof(struct macro_info));
+ STAILQ_INIT(&symbol->info.macroinfo->args);
+ break;
+ default:
+ stop("Call to initialize_symbol with invalid symbol type",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+ struct macro_arg *marg;
+ int i;
+ int retval;
+
+
+ if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+ stop("Invalid current symbol for adding macro arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ marg = (struct macro_arg *)malloc(sizeof(*marg));
+ if (marg == NULL) {
+ stop("Can't create macro_arg structure", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ marg->replacement_text = NULL;
+ retval = snprintf(regex_pattern, sizeof(regex_pattern),
+ "[^-/A-Za-z0-9_](%s)([^-/A-Za-z0-9_]|$)",
+ argtext);
+ if (retval >= sizeof(regex_pattern)) {
+ stop("Regex text buffer too small for arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ retval = regcomp(&marg->arg_regex, regex_pattern, REG_EXTENDED);
+ if (retval != 0) {
+ stop("Regex compilation failed", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ STAILQ_INSERT_TAIL(&cur_symbol->info.macroinfo->args, marg, links);
+}
+
+static void
+add_macro_body(const char *bodytext)
+{
+ if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+ stop("Invalid current symbol for adding macro arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ cur_symbol->info.macroinfo->body = strdup(bodytext);
+ if (cur_symbol->info.macroinfo->body == NULL) {
+ stop("Can't duplicate macro body text", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+static void
+process_register(symbol_t **p_symbol)
+{
+ symbol_t *symbol = *p_symbol;
+
+ if (symbol->type == UNINITIALIZED) {
+ snprintf(errbuf, sizeof(errbuf), "Undefined register %s",
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ } else if (symbol->type == ALIAS) {
+ *p_symbol = symbol->info.ainfo->parent;
+ } else if ((symbol->type != REGISTER)
+ && (symbol->type != SCBLOC)
+ && (symbol->type != SRAMLOC)) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Specified symbol %s is not a register",
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ }
+}
+
+static void
+format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
+ symbol_ref_t *src, int ret)
+{
+ struct instruction *instr;
+ struct ins_format1 *f1_instr;
+
+ if (src->symbol == NULL)
+ src = dest;
+
+ /* Test register permissions */
+ test_writable_symbol(dest->symbol);
+ test_readable_symbol(src->symbol);
+
+ /* Ensure that immediate makes sense for this destination */
+ type_check(dest->symbol, immed, opcode);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f1_instr = &instr->format.format1;
+ f1_instr->ret = ret ? 1 : 0;
+ f1_instr->opcode = opcode;
+ f1_instr->destination = dest->symbol->info.rinfo->address
+ + dest->offset;
+ f1_instr->source = src->symbol->info.rinfo->address
+ + src->offset;
+ f1_instr->immediate = immed->value;
+
+ if (is_download_const(immed))
+ f1_instr->parity = 1;
+ else if (dest->symbol == mode_ptr.symbol) {
+ u_int src_value;
+ u_int dst_value;
+
+ /*
+ * Attempt to update mode information if
+ * we are operating on the mode register.
+ */
+ if (src->symbol == allones.symbol)
+ src_value = 0xFF;
+ else if (src->symbol == allzeros.symbol)
+ src_value = 0;
+ else if (src->symbol == mode_ptr.symbol)
+ src_value = (dst_mode << 4) | src_mode;
+ else
+ goto cant_update;
+
+ switch (opcode) {
+ case AIC_OP_AND:
+ dst_value = src_value & immed->value;
+ break;
+ case AIC_OP_XOR:
+ dst_value = src_value ^ immed->value;
+ break;
+ case AIC_OP_ADD:
+ dst_value = (src_value + immed->value) & 0xFF;
+ break;
+ case AIC_OP_OR:
+ dst_value = src_value | immed->value;
+ break;
+ case AIC_OP_BMOV:
+ dst_value = src_value;
+ break;
+ default:
+ goto cant_update;
+ }
+ src_mode = dst_value & 0xF;
+ dst_mode = (dst_value >> 4) & 0xF;
+ }
+
+cant_update:
+ symlist_free(&immed->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
+ symbol_ref_t *src, int ret)
+{
+ struct instruction *instr;
+ struct ins_format2 *f2_instr;
+ uint8_t shift_control;
+
+ if (src->symbol == NULL)
+ src = dest;
+
+ /* Test register permissions */
+ test_writable_symbol(dest->symbol);
+ test_readable_symbol(src->symbol);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f2_instr = &instr->format.format2;
+ f2_instr->ret = ret ? 1 : 0;
+ f2_instr->opcode = AIC_OP_ROL;
+ f2_instr->destination = dest->symbol->info.rinfo->address
+ + dest->offset;
+ f2_instr->source = src->symbol->info.rinfo->address
+ + src->offset;
+ if (places->value > 8 || places->value <= 0) {
+ stop("illegal shift value", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ switch (opcode) {
+ case AIC_OP_SHL:
+ if (places->value == 8)
+ shift_control = 0xf0;
+ else
+ shift_control = (places->value << 4) | places->value;
+ break;
+ case AIC_OP_SHR:
+ if (places->value == 8) {
+ shift_control = 0xf8;
+ } else {
+ shift_control = (places->value << 4)
+ | (8 - places->value)
+ | 0x08;
+ }
+ break;
+ case AIC_OP_ROL:
+ shift_control = places->value & 0x7;
+ break;
+ case AIC_OP_ROR:
+ shift_control = (8 - places->value) | 0x08;
+ break;
+ default:
+ shift_control = 0; /* Quiet Compiler */
+ stop("Invalid shift operation specified", EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ };
+ f2_instr->shift_control = shift_control;
+ symlist_free(&places->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+format_3_instr(int opcode, symbol_ref_t *src,
+ expression_t *immed, symbol_ref_t *address)
+{
+ struct instruction *instr;
+ struct ins_format3 *f3_instr;
+ int addr;
+
+ /* Test register permissions */
+ test_readable_symbol(src->symbol);
+
+ /* Ensure that immediate makes sense for this source */
+ type_check(src->symbol, immed, opcode);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f3_instr = &instr->format.format3;
+ if (address->symbol == NULL) {
+ /* 'dot' referrence. Use the current instruction pointer */
+ addr = instruction_ptr + address->offset;
+ } else if (address->symbol->type == UNINITIALIZED) {
+ /* forward reference */
+ addr = address->offset;
+ instr->patch_label = address->symbol;
+ } else
+ addr = address->symbol->info.linfo->address + address->offset;
+ f3_instr->opcode = opcode;
+ f3_instr->address = addr;
+ f3_instr->source = src->symbol->info.rinfo->address
+ + src->offset;
+ f3_instr->immediate = immed->value;
+
+ if (is_download_const(immed))
+ f3_instr->parity = 1;
+
+ symlist_free(&immed->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+test_readable_symbol(symbol_t *symbol)
+{
+
+ if ((symbol->info.rinfo->modes & (0x1 << src_mode)) == 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Register %s unavailable in source reg mode %d",
+ symbol->name, src_mode);
+ stop(errbuf, EX_DATAERR);
+ }
+
+ if (symbol->info.rinfo->mode == WO) {
+ stop("Write Only register specified as source",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+}
+
+static void
+test_writable_symbol(symbol_t *symbol)
+{
+
+ if ((symbol->info.rinfo->modes & (0x1 << dst_mode)) == 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Register %s unavailable in destination reg mode %d",
+ symbol->name, dst_mode);
+ stop(errbuf, EX_DATAERR);
+ }
+
+ if (symbol->info.rinfo->mode == RO) {
+ stop("Read Only register specified as destination",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+}
+
+static void
+type_check(symbol_t *symbol, expression_t *expression, int opcode)
+{
+ symbol_node_t *node;
+ int and_op;
+
+ and_op = FALSE;
+ if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ)
+ and_op = TRUE;
+
+ /*
+ * Make sure that we aren't attempting to write something
+ * that hasn't been defined. If this is an and operation,
+ * this is a mask, so "undefined" bits are okay.
+ */
+ if (and_op == FALSE
+ && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Invalid bit(s) 0x%x in immediate written to %s",
+ expression->value & ~symbol->info.rinfo->valid_bitmask,
+ symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Now make sure that all of the symbols referenced by the
+ * expression are defined for this register.
+ */
+ if (symbol->info.rinfo->typecheck_masks != FALSE) {
+ for(node = expression->referenced_syms.slh_first;
+ node != NULL;
+ node = node->links.sle_next) {
+ if ((node->symbol->type == MASK
+ || node->symbol->type == FIELD
+ || node->symbol->type == ENUM
+ || node->symbol->type == ENUM_ENTRY)
+ && symlist_search(&node->symbol->info.finfo->symrefs,
+ symbol->name) == NULL) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Invalid field or mask %s "
+ "for register %s",
+ node->symbol->name, symbol->name);
+ stop(errbuf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+ }
+}
+
+static void
+make_expression(expression_t *immed, int value)
+{
+ SLIST_INIT(&immed->referenced_syms);
+ immed->value = value & 0xff;
+}
+
+static void
+add_conditional(symbol_t *symbol)
+{
+ static int numfuncs;
+
+ if (numfuncs == 0) {
+ /* add a special conditional, "0" */
+ symbol_t *false_func;
+
+ false_func = symtable_get("0");
+ if (false_func->type != UNINITIALIZED) {
+ stop("Conditional expression '0' "
+ "conflicts with a symbol", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ false_func->type = CONDITIONAL;
+ initialize_symbol(false_func);
+ false_func->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+ }
+
+ /* This condition has occurred before */
+ if (symbol->type == CONDITIONAL)
+ return;
+
+ if (symbol->type != UNINITIALIZED) {
+ stop("Conditional expression conflicts with a symbol",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ symbol->type = CONDITIONAL;
+ initialize_symbol(symbol);
+ symbol->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
+}
+
+static void
+add_version(const char *verstring)
+{
+ const char prefix[] = " * ";
+ int newlen;
+ int oldlen;
+
+ newlen = strlen(verstring) + strlen(prefix);
+ oldlen = 0;
+ if (versions != NULL)
+ oldlen = strlen(versions);
+ versions = realloc(versions, newlen + oldlen + 2);
+ if (versions == NULL)
+ stop("Can't allocate version string", EX_SOFTWARE);
+ strcpy(&versions[oldlen], prefix);
+ strcpy(&versions[oldlen + strlen(prefix)], verstring);
+ versions[newlen + oldlen] = '\n';
+ versions[newlen + oldlen + 1] = '\0';
+}
+
+void
+yyerror(const char *string)
+{
+ stop(string, EX_DATAERR);
+}
+
+static int
+is_download_const(expression_t *immed)
+{
+ if ((immed->referenced_syms.slh_first != NULL)
+ && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
+ return (TRUE);
+
+ return (FALSE);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
new file mode 100644
index 000000000000..3e80f07df49c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
@@ -0,0 +1,131 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_insformat.h#11 $
+ *
+ * $FreeBSD$
+ */
+
+struct ins_format1 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+struct ins_format2 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ shift_control : 8;
+#endif
+};
+
+struct ins_format3 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ address : 10,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+union ins_formats {
+ struct ins_format1 format1;
+ struct ins_format2 format2;
+ struct ins_format3 format3;
+ uint8_t bytes[4];
+ uint32_t integer;
+};
+struct instruction {
+ union ins_formats format;
+ u_int srcline;
+ struct symbol *patch_label;
+ STAILQ_ENTRY(instruction) links;
+};
+
+#define AIC_OP_OR 0x0
+#define AIC_OP_AND 0x1
+#define AIC_OP_XOR 0x2
+#define AIC_OP_ADD 0x3
+#define AIC_OP_ADC 0x4
+#define AIC_OP_ROL 0x5
+#define AIC_OP_BMOV 0x6
+
+#define AIC_OP_JMP 0x8
+#define AIC_OP_JC 0x9
+#define AIC_OP_JNC 0xa
+#define AIC_OP_CALL 0xb
+#define AIC_OP_JNE 0xc
+#define AIC_OP_JNZ 0xd
+#define AIC_OP_JE 0xe
+#define AIC_OP_JZ 0xf
+
+/* Pseudo Ops */
+#define AIC_OP_SHL 0x10
+#define AIC_OP_SHR 0x20
+#define AIC_OP_ROR 0x30
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
new file mode 100644
index 000000000000..439f760b34b5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
@@ -0,0 +1,164 @@
+%{
+/*
+ * Sub-parser for macro invocation in the Aic7xxx SCSI
+ * Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_macro_gram.y#5 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+static symbol_t *macro_symbol;
+
+static void add_macro_arg(const char *argtext, int position);
+
+%}
+
+%union {
+ int value;
+ char *str;
+ symbol_t *sym;
+}
+
+
+%token <str> T_ARG
+
+%token <sym> T_SYMBOL
+
+%type <value> macro_arglist
+
+%%
+
+macrocall:
+ T_SYMBOL '('
+ {
+ macro_symbol = $1;
+ }
+ macro_arglist ')'
+ {
+ if (macro_symbol->info.macroinfo->narg != $4) {
+ printf("Narg == %d", macro_symbol->info.macroinfo->narg);
+ stop("Too few arguments for macro invocation",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ macro_symbol = NULL;
+ YYACCEPT;
+ }
+;
+
+macro_arglist:
+ {
+ /* Macros can take 0 arguments */
+ $$ = 0;
+ }
+| T_ARG
+ {
+ $$ = 1;
+ add_macro_arg($1, 1);
+ }
+| macro_arglist ',' T_ARG
+ {
+ if ($1 == 0) {
+ stop("Comma without preceeding argument in arg list",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$ = $1 + 1;
+ add_macro_arg($3, $$);
+ }
+;
+
+%%
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+ struct macro_arg *marg;
+ int i;
+
+ if (macro_symbol == NULL || macro_symbol->type != MACRO) {
+ stop("Invalid current symbol for adding macro arg",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ /*
+ * Macro Invocation. Find the appropriate argument and fill
+ * in the replace ment text for this call.
+ */
+ i = 0;
+ STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+ i++;
+ if (i == argnum)
+ break;
+ }
+ if (marg == NULL) {
+ stop("Too many arguments for macro invocation", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ marg->replacement_text = strdup(argtext);
+ if (marg->replacement_text == NULL) {
+ stop("Unable to replicate replacement text", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+void
+mmerror(const char *string)
+{
+ stop(string, EX_DATAERR);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
new file mode 100644
index 000000000000..f06e7035cb35
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
@@ -0,0 +1,156 @@
+%{
+/*
+ * Sub-Lexical Analyzer for macro invokation in
+ * the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_macro_scan.l#8 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_macro_gram.h"
+
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int parren_count;
+static char buf[255];
+%}
+
+WORD [A-Za-z_][-A-Za-z_0-9]*
+SPACE [ \t]+
+MCARG [^(), \t]+
+
+%x ARGLIST
+
+%%
+\n {
+ ++yylineno;
+ }
+\r ;
+<ARGLIST>{SPACE} ;
+<ARGLIST>\( {
+ parren_count++;
+ if (parren_count == 1) {
+ string_buf_ptr = string_buf;
+ return ('(');
+ }
+ *string_buf_ptr++ = '(';
+ }
+<ARGLIST>\) {
+ if (parren_count == 1) {
+ if (string_buf_ptr != string_buf) {
+ /*
+ * Return an argument and
+ * rescan this parren so we
+ * can return it as well.
+ */
+ *string_buf_ptr = '\0';
+ mmlval.str = string_buf;
+ string_buf_ptr = string_buf;
+ unput(')');
+ return T_ARG;
+ }
+ BEGIN INITIAL;
+ return (')');
+ }
+ parren_count--;
+ *string_buf_ptr++ = ')';
+ }
+<ARGLIST>{MCARG} {
+ char *yptr;
+
+ yptr = mmtext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+<ARGLIST>\, {
+ if (string_buf_ptr != string_buf) {
+ /*
+ * Return an argument and
+ * rescan this comma so we
+ * can return it as well.
+ */
+ *string_buf_ptr = '\0';
+ mmlval.str = string_buf;
+ string_buf_ptr = string_buf;
+ unput(',');
+ return T_ARG;
+ }
+ return ',';
+ }
+{WORD}[(] {
+ /* May be a symbol or a macro invocation. */
+ mmlval.sym = symtable_get(mmtext);
+ if (mmlval.sym->type != MACRO) {
+ stop("Expecting Macro Name",
+ EX_DATAERR);
+ }
+ unput('(');
+ parren_count = 0;
+ BEGIN ARGLIST;
+ return T_SYMBOL;
+ }
+. {
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c'", mmtext[0]);
+ stop(buf, EX_DATAERR);
+ }
+%%
+
+int
+mmwrap()
+{
+ stop("EOF encountered in macro call", EX_DATAERR);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
new file mode 100644
index 000000000000..45c0b233d0bc
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
@@ -0,0 +1,607 @@
+%{
+/*
+ * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_scan.l#19 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_gram.h"
+
+/* This is used for macro body capture too, so err on the large size. */
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int parren_count;
+static int quote_count;
+static char buf[255];
+%}
+
+PATH ([/]*[-A-Za-z0-9_.])+
+WORD [A-Za-z_][-A-Za-z_0-9]*
+SPACE [ \t]+
+MCARG [^(), \t]+
+MBODY ((\\[^\n])*[^\n\\]*)+
+
+%x COMMENT
+%x CEXPR
+%x INCLUDE
+%x STRING
+%x MACRODEF
+%x MACROARGLIST
+%x MACROCALLARGS
+%x MACROBODY
+
+%%
+\n { ++yylineno; }
+\r ;
+"/*" { BEGIN COMMENT; /* Enter comment eating state */ }
+<COMMENT>"/*" { fprintf(stderr, "Warning! Comment within comment."); }
+<COMMENT>\n { ++yylineno; }
+<COMMENT>[^*/\n]* ;
+<COMMENT>"*"+[^*/\n]* ;
+<COMMENT>"/"+[^*/\n]* ;
+<COMMENT>"*"+"/" { BEGIN INITIAL; }
+if[ \t]*\( {
+ string_buf_ptr = string_buf;
+ parren_count = 1;
+ BEGIN CEXPR;
+ return T_IF;
+ }
+<CEXPR>\( { *string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\) {
+ parren_count--;
+ if (parren_count == 0) {
+ /* All done */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.sym = symtable_get(string_buf);
+ return T_CEXPR;
+ } else {
+ *string_buf_ptr++ = ')';
+ }
+ }
+<CEXPR>\n { ++yylineno; }
+<CEXPR>\r ;
+<CEXPR>[^()\n]+ {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr != '\0') {
+ /* Remove duplicate spaces */
+ if (*yptr == '\t')
+ *yptr = ' ';
+ if (*yptr == ' '
+ && string_buf_ptr != string_buf
+ && string_buf_ptr[-1] == ' ')
+ yptr++;
+ else
+ *string_buf_ptr++ = *yptr++;
+ }
+ }
+
+VERSION { return T_VERSION; }
+PREFIX { return T_PREFIX; }
+PATCH_ARG_LIST { return T_PATCH_ARG_LIST; }
+\" {
+ string_buf_ptr = string_buf;
+ BEGIN STRING;
+ }
+<STRING>[^"]+ {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+<STRING>\" {
+ /* All done */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.str = string_buf;
+ return T_STRING;
+ }
+{SPACE} ;
+
+ /* Register/SCB/SRAM definition keywords */
+export { return T_EXPORT; }
+register { return T_REGISTER; }
+const { yylval.value = FALSE; return T_CONST; }
+download { return T_DOWNLOAD; }
+address { return T_ADDRESS; }
+access_mode { return T_ACCESS_MODE; }
+modes { return T_MODES; }
+RW|RO|WO {
+ if (strcmp(yytext, "RW") == 0)
+ yylval.value = RW;
+ else if (strcmp(yytext, "RO") == 0)
+ yylval.value = RO;
+ else
+ yylval.value = WO;
+ return T_MODE;
+ }
+BEGIN_CRITICAL { return T_BEGIN_CS; }
+END_CRITICAL { return T_END_CS; }
+SET_SRC_MODE { return T_SET_SRC_MODE; }
+SET_DST_MODE { return T_SET_DST_MODE; }
+field { return T_FIELD; }
+enum { return T_ENUM; }
+mask { return T_MASK; }
+alias { return T_ALIAS; }
+size { return T_SIZE; }
+scb { return T_SCB; }
+scratch_ram { return T_SRAM; }
+accumulator { return T_ACCUM; }
+mode_pointer { return T_MODE_PTR; }
+allones { return T_ALLONES; }
+allzeros { return T_ALLZEROS; }
+none { return T_NONE; }
+sindex { return T_SINDEX; }
+A { return T_A; }
+
+ /* Opcodes */
+shl { return T_SHL; }
+shr { return T_SHR; }
+ror { return T_ROR; }
+rol { return T_ROL; }
+mvi { return T_MVI; }
+mov { return T_MOV; }
+clr { return T_CLR; }
+jmp { return T_JMP; }
+jc { return T_JC; }
+jnc { return T_JNC; }
+je { return T_JE; }
+jne { return T_JNE; }
+jz { return T_JZ; }
+jnz { return T_JNZ; }
+call { return T_CALL; }
+add { return T_ADD; }
+adc { return T_ADC; }
+bmov { return T_BMOV; }
+inc { return T_INC; }
+dec { return T_DEC; }
+stc { return T_STC; }
+clc { return T_CLC; }
+cmp { return T_CMP; }
+not { return T_NOT; }
+xor { return T_XOR; }
+test { return T_TEST;}
+and { return T_AND; }
+or { return T_OR; }
+ret { return T_RET; }
+nop { return T_NOP; }
+else { return T_ELSE; }
+
+ /* Allowed Symbols */
+\<\< { return T_EXPR_LSHIFT; }
+\>\> { return T_EXPR_RSHIFT; }
+[-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
+
+ /* Number processing */
+0[0-7]* {
+ yylval.value = strtol(yytext, NULL, 8);
+ return T_NUMBER;
+ }
+
+0[xX][0-9a-fA-F]+ {
+ yylval.value = strtoul(yytext + 2, NULL, 16);
+ return T_NUMBER;
+ }
+
+[1-9][0-9]* {
+ yylval.value = strtol(yytext, NULL, 10);
+ return T_NUMBER;
+ }
+ /* Include Files */
+#include{SPACE} {
+ BEGIN INCLUDE;
+ quote_count = 0;
+ return T_INCLUDE;
+ }
+<INCLUDE>[<] { return yytext[0]; }
+<INCLUDE>[>] { BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>[\"] {
+ if (quote_count != 0)
+ BEGIN INITIAL;
+ quote_count++;
+ return yytext[0];
+ }
+<INCLUDE>{PATH} {
+ char *yptr;
+
+ yptr = yytext;
+ string_buf_ptr = string_buf;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ yylval.str = string_buf;
+ *string_buf_ptr = '\0';
+ return T_PATH;
+ }
+<INCLUDE>. { stop("Invalid include line", EX_DATAERR); }
+#define{SPACE} {
+ BEGIN MACRODEF;
+ return T_DEFINE;
+ }
+<MACRODEF>{WORD}{SPACE} {
+ char *yptr;
+
+ /* Strip space and return as a normal symbol */
+ yptr = yytext;
+ while (*yptr != ' ' && *yptr != '\t')
+ yptr++;
+ *yptr = '\0';
+ yylval.sym = symtable_get(yytext);
+ string_buf_ptr = string_buf;
+ BEGIN MACROBODY;
+ return T_SYMBOL;
+ }
+<MACRODEF>{WORD}\( {
+ /*
+ * We store the symbol with its opening
+ * parren so we can differentiate macros
+ * that take args from macros with the
+ * same name that do not take args as
+ * is allowed in C.
+ */
+ BEGIN MACROARGLIST;
+ yylval.sym = symtable_get(yytext);
+ unput('(');
+ return T_SYMBOL;
+ }
+<MACROARGLIST>{WORD} {
+ yylval.str = yytext;
+ return T_ARG;
+ }
+<MACROARGLIST>{SPACE} ;
+<MACROARGLIST>[(,] {
+ return yytext[0];
+ }
+<MACROARGLIST>[)] {
+ string_buf_ptr = string_buf;
+ BEGIN MACROBODY;
+ return ')';
+ }
+<MACROARGLIST>. {
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c' in macro argument list",
+ yytext[0]);
+ stop(buf, EX_DATAERR);
+ }
+<MACROCALLARGS>{SPACE} ;
+<MACROCALLARGS>\( {
+ parren_count++;
+ if (parren_count == 1)
+ return ('(');
+ *string_buf_ptr++ = '(';
+ }
+<MACROCALLARGS>\) {
+ parren_count--;
+ if (parren_count == 0) {
+ BEGIN INITIAL;
+ return (')');
+ }
+ *string_buf_ptr++ = ')';
+ }
+<MACROCALLARGS>{MCARG} {
+ char *yptr;
+
+ yptr = yytext;
+ while (*yptr)
+ *string_buf_ptr++ = *yptr++;
+ }
+<MACROCALLARGS>\, {
+ if (string_buf_ptr != string_buf) {
+ /*
+ * Return an argument and
+ * rescan this comma so we
+ * can return it as well.
+ */
+ *string_buf_ptr = '\0';
+ yylval.str = string_buf;
+ string_buf_ptr = string_buf;
+ unput(',');
+ return T_ARG;
+ }
+ return ',';
+ }
+<MACROBODY>\\\n {
+ /* Eat escaped newlines. */
+ ++yylineno;
+ }
+<MACROBODY>\r ;
+<MACROBODY>\n {
+ /* Macros end on the first unescaped newline. */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.str = string_buf;
+ ++yylineno;
+ return T_MACROBODY;
+ }
+<MACROBODY>{MBODY} {
+ char *yptr;
+ char c;
+
+ yptr = yytext;
+ while (c = *yptr++) {
+ /*
+ * Strip carriage returns.
+ */
+ if (c == '\r')
+ continue;
+ *string_buf_ptr++ = c;
+ }
+ }
+{WORD}\( {
+ char *yptr;
+ char *ycopy;
+
+ /* May be a symbol or a macro invocation. */
+ yylval.sym = symtable_get(yytext);
+ if (yylval.sym->type == MACRO) {
+ YY_BUFFER_STATE old_state;
+ YY_BUFFER_STATE temp_state;
+
+ ycopy = strdup(yytext);
+ yptr = ycopy + yyleng;
+ while (yptr > ycopy)
+ unput(*--yptr);
+ old_state = YY_CURRENT_BUFFER;
+ temp_state =
+ yy_create_buffer(stdin,
+ YY_BUF_SIZE);
+ yy_switch_to_buffer(temp_state);
+ mm_switch_to_buffer(old_state);
+ mmparse();
+ mm_switch_to_buffer(temp_state);
+ yy_switch_to_buffer(old_state);
+ mm_delete_buffer(temp_state);
+ expand_macro(yylval.sym);
+ } else {
+ if (yylval.sym->type == UNINITIALIZED) {
+ /* Try without the '(' */
+ symbol_delete(yylval.sym);
+ yytext[yyleng-1] = '\0';
+ yylval.sym =
+ symtable_get(yytext);
+ }
+ unput('(');
+ return T_SYMBOL;
+ }
+ }
+{WORD} {
+ yylval.sym = symtable_get(yytext);
+ if (yylval.sym->type == MACRO) {
+ expand_macro(yylval.sym);
+ } else {
+ return T_SYMBOL;
+ }
+ }
+. {
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c'", yytext[0]);
+ stop(buf, EX_DATAERR);
+ }
+%%
+
+typedef struct include {
+ YY_BUFFER_STATE buffer;
+ int lineno;
+ char *filename;
+ SLIST_ENTRY(include) links;
+}include_t;
+
+SLIST_HEAD(, include) include_stack;
+
+void
+include_file(char *file_name, include_type type)
+{
+ FILE *newfile;
+ include_t *include;
+
+ newfile = NULL;
+ /* Try the current directory first */
+ if (includes_search_curdir != 0 || type == SOURCE_FILE)
+ newfile = fopen(file_name, "r");
+
+ if (newfile == NULL && type != SOURCE_FILE) {
+ path_entry_t include_dir;
+ for (include_dir = search_path.slh_first;
+ include_dir != NULL;
+ include_dir = include_dir->links.sle_next) {
+ char fullname[PATH_MAX];
+
+ if ((include_dir->quoted_includes_only == TRUE)
+ && (type != QUOTED_INCLUDE))
+ continue;
+
+ snprintf(fullname, sizeof(fullname),
+ "%s/%s", include_dir->directory, file_name);
+
+ if ((newfile = fopen(fullname, "r")) != NULL)
+ break;
+ }
+ }
+
+ if (newfile == NULL) {
+ perror(file_name);
+ stop("Unable to open input file", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ if (type != SOURCE_FILE) {
+ include = (include_t *)malloc(sizeof(include_t));
+ if (include == NULL) {
+ stop("Unable to allocate include stack entry",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ include->buffer = YY_CURRENT_BUFFER;
+ include->lineno = yylineno;
+ include->filename = yyfilename;
+ SLIST_INSERT_HEAD(&include_stack, include, links);
+ }
+ yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
+ yylineno = 1;
+ yyfilename = strdup(file_name);
+}
+
+static void next_substitution(struct symbol *mac_symbol, const char *body_pos,
+ const char **next_match,
+ struct macro_arg **match_marg, regmatch_t *match);
+
+void
+expand_macro(struct symbol *macro_symbol)
+{
+ struct macro_arg *marg;
+ struct macro_arg *match_marg;
+ const char *body_head;
+ const char *body_pos;
+ const char *next_match;
+
+ /*
+ * Due to the nature of unput, we must work
+ * backwards through the macro body performing
+ * any expansions.
+ */
+ body_head = macro_symbol->info.macroinfo->body;
+ body_pos = body_head + strlen(body_head);
+ while (body_pos > body_head) {
+ regmatch_t match;
+
+ next_match = body_head;
+ match_marg = NULL;
+ next_substitution(macro_symbol, body_pos, &next_match,
+ &match_marg, &match);
+
+ /* Put back everything up until the replacement. */
+ while (body_pos > next_match)
+ unput(*--body_pos);
+
+ /* Perform the replacement. */
+ if (match_marg != NULL) {
+ const char *strp;
+
+ next_match = match_marg->replacement_text;
+ strp = next_match + strlen(next_match);
+ while (strp > next_match)
+ unput(*--strp);
+
+ /* Skip past the unexpanded macro arg. */
+ body_pos -= match.rm_eo - match.rm_so;
+ }
+ }
+
+ /* Cleanup replacement text. */
+ STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+ free(marg->replacement_text);
+ }
+}
+
+/*
+ * Find the next substitution in the macro working backwards from
+ * body_pos until the beginning of the macro buffer. next_match
+ * should be initialized to the beginning of the macro buffer prior
+ * to calling this routine.
+ */
+static void
+next_substitution(struct symbol *mac_symbol, const char *body_pos,
+ const char **next_match, struct macro_arg **match_marg,
+ regmatch_t *match)
+{
+ regmatch_t matches[2];
+ struct macro_arg *marg;
+ const char *search_pos;
+ int retval;
+
+ do {
+ search_pos = *next_match;
+
+ STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) {
+
+ retval = regexec(&marg->arg_regex, search_pos, 2,
+ matches, 0);
+ if (retval == 0
+ && (matches[1].rm_eo + search_pos) <= body_pos
+ && (matches[1].rm_eo + search_pos) > *next_match) {
+ *match = matches[1];
+ *next_match = match->rm_eo + search_pos;
+ *match_marg = marg;
+ }
+ }
+ } while (search_pos != *next_match);
+}
+
+int
+yywrap()
+{
+ include_t *include;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ (void)fclose(yyin);
+ if (yyfilename != NULL)
+ free(yyfilename);
+ yyfilename = NULL;
+ include = include_stack.slh_first;
+ if (include != NULL) {
+ yy_switch_to_buffer(include->buffer);
+ yylineno = include->lineno;
+ yyfilename = include->filename;
+ SLIST_REMOVE_HEAD(&include_stack, links);
+ free(include);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
new file mode 100644
index 000000000000..f1f448dff569
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
@@ -0,0 +1,677 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.c#24 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#ifdef __linux__
+#include "aicdb.h"
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "aicasm_symbol.h"
+#include "aicasm.h"
+
+static DB *symtable;
+
+symbol_t *
+symbol_create(char *name)
+{
+ symbol_t *new_symbol;
+
+ new_symbol = (symbol_t *)malloc(sizeof(symbol_t));
+ if (new_symbol == NULL) {
+ perror("Unable to create new symbol");
+ exit(EX_SOFTWARE);
+ }
+ memset(new_symbol, 0, sizeof(*new_symbol));
+ new_symbol->name = strdup(name);
+ if (new_symbol->name == NULL)
+ stop("Unable to strdup symbol name", EX_SOFTWARE);
+ new_symbol->type = UNINITIALIZED;
+ return (new_symbol);
+}
+
+void
+symbol_delete(symbol_t *symbol)
+{
+ if (symtable != NULL) {
+ DBT key;
+
+ key.data = symbol->name;
+ key.size = strlen(symbol->name);
+ symtable->del(symtable, &key, /*flags*/0);
+ }
+ switch(symbol->type) {
+ case SCBLOC:
+ case SRAMLOC:
+ case REGISTER:
+ if (symbol->info.rinfo != NULL)
+ free(symbol->info.rinfo);
+ break;
+ case ALIAS:
+ if (symbol->info.ainfo != NULL)
+ free(symbol->info.ainfo);
+ break;
+ case MASK:
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ if (symbol->info.finfo != NULL) {
+ symlist_free(&symbol->info.finfo->symrefs);
+ free(symbol->info.finfo);
+ }
+ break;
+ case DOWNLOAD_CONST:
+ case CONST:
+ if (symbol->info.cinfo != NULL)
+ free(symbol->info.cinfo);
+ break;
+ case LABEL:
+ if (symbol->info.linfo != NULL)
+ free(symbol->info.linfo);
+ break;
+ case UNINITIALIZED:
+ default:
+ break;
+ }
+ free(symbol->name);
+ free(symbol);
+}
+
+void
+symtable_open()
+{
+ symtable = dbopen(/*filename*/NULL,
+ O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH,
+ /*openinfo*/NULL);
+
+ if (symtable == NULL) {
+ perror("Symbol table creation failed");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+void
+symtable_close()
+{
+ if (symtable != NULL) {
+ DBT key;
+ DBT data;
+
+ while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) {
+ symbol_t *stored_ptr;
+
+ memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+ symbol_delete(stored_ptr);
+ }
+ symtable->close(symtable);
+ }
+}
+
+/*
+ * The semantics of get is to return an uninitialized symbol entry
+ * if a lookup fails.
+ */
+symbol_t *
+symtable_get(char *name)
+{
+ symbol_t *stored_ptr;
+ DBT key;
+ DBT data;
+ int retval;
+
+ key.data = (void *)name;
+ key.size = strlen(name);
+
+ if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) {
+ if (retval == -1) {
+ perror("Symbol table get operation failed");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ } else if (retval == 1) {
+ /* Symbol wasn't found, so create a new one */
+ symbol_t *new_symbol;
+
+ new_symbol = symbol_create(name);
+ data.data = &new_symbol;
+ data.size = sizeof(new_symbol);
+ if (symtable->put(symtable, &key, &data,
+ /*flags*/0) !=0) {
+ perror("Symtable put failed");
+ exit(EX_SOFTWARE);
+ }
+ return (new_symbol);
+ } else {
+ perror("Unexpected return value from db get routine");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ }
+ memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+ return (stored_ptr);
+}
+
+symbol_node_t *
+symlist_search(symlist_t *symlist, char *symname)
+{
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(symlist);
+ while(curnode != NULL) {
+ if (strcmp(symname, curnode->symbol->name) == 0)
+ break;
+ curnode = SLIST_NEXT(curnode, links);
+ }
+ return (curnode);
+}
+
+void
+symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
+{
+ symbol_node_t *newnode;
+
+ newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t));
+ if (newnode == NULL) {
+ stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ newnode->symbol = symbol;
+ if (how == SYMLIST_SORT) {
+ symbol_node_t *curnode;
+ int field;
+
+ field = FALSE;
+ switch(symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ break;
+ case FIELD:
+ case MASK:
+ case ENUM:
+ case ENUM_ENTRY:
+ field = TRUE;
+ break;
+ default:
+ stop("symlist_add: Invalid symbol type for sorting",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ curnode = SLIST_FIRST(symlist);
+ if (curnode == NULL
+ || (field
+ && (curnode->symbol->type > newnode->symbol->type
+ || (curnode->symbol->type == newnode->symbol->type
+ && (curnode->symbol->info.finfo->value >
+ newnode->symbol->info.finfo->value))))
+ || (!field && (curnode->symbol->info.rinfo->address >
+ newnode->symbol->info.rinfo->address))) {
+ SLIST_INSERT_HEAD(symlist, newnode, links);
+ return;
+ }
+
+ while (1) {
+ if (SLIST_NEXT(curnode, links) == NULL) {
+ SLIST_INSERT_AFTER(curnode, newnode,
+ links);
+ break;
+ } else {
+ symbol_t *cursymbol;
+
+ cursymbol = SLIST_NEXT(curnode, links)->symbol;
+ if ((field
+ && (cursymbol->type > symbol->type
+ || (cursymbol->type == symbol->type
+ && (cursymbol->info.finfo->value >
+ symbol->info.finfo->value))))
+ || (!field
+ && (cursymbol->info.rinfo->address >
+ symbol->info.rinfo->address))) {
+ SLIST_INSERT_AFTER(curnode, newnode,
+ links);
+ break;
+ }
+ }
+ curnode = SLIST_NEXT(curnode, links);
+ }
+ } else {
+ SLIST_INSERT_HEAD(symlist, newnode, links);
+ }
+}
+
+void
+symlist_free(symlist_t *symlist)
+{
+ symbol_node_t *node1, *node2;
+
+ node1 = SLIST_FIRST(symlist);
+ while (node1 != NULL) {
+ node2 = SLIST_NEXT(node1, links);
+ free(node1);
+ node1 = node2;
+ }
+ SLIST_INIT(symlist);
+}
+
+void
+symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2)
+{
+ symbol_node_t *node;
+
+ *symlist_dest = *symlist_src1;
+ while((node = SLIST_FIRST(symlist_src2)) != NULL) {
+ SLIST_REMOVE_HEAD(symlist_src2, links);
+ SLIST_INSERT_HEAD(symlist_dest, node, links);
+ }
+
+ /* These are now empty */
+ SLIST_INIT(symlist_src1);
+ SLIST_INIT(symlist_src2);
+}
+
+void
+aic_print_file_prologue(FILE *ofile)
+{
+
+ if (ofile == NULL)
+ return;
+
+ fprintf(ofile,
+"/*\n"
+" * DO NOT EDIT - This file is automatically generated\n"
+" * from the following source files:\n"
+" *\n"
+"%s */\n",
+ versions);
+}
+
+void
+aic_print_include(FILE *dfile, char *include_file)
+{
+
+ if (dfile == NULL)
+ return;
+ fprintf(dfile, "\n#include \"%s\"\n\n", include_file);
+}
+
+void
+aic_print_reg_dump_types(FILE *ofile)
+{
+ if (ofile == NULL)
+ return;
+
+ fprintf(ofile,
+"typedef int (%sreg_print_t)(u_int, u_int *, u_int);\n"
+"typedef struct %sreg_parse_entry {\n"
+" char *name;\n"
+" uint8_t value;\n"
+" uint8_t mask;\n"
+"} %sreg_parse_entry_t;\n"
+"\n",
+ prefix, prefix, prefix);
+}
+
+static void
+aic_print_reg_dump_start(FILE *dfile, symbol_node_t *regnode)
+{
+ if (dfile == NULL)
+ return;
+
+ fprintf(dfile,
+"static %sreg_parse_entry_t %s_parse_table[] = {\n",
+ prefix,
+ regnode->symbol->name);
+}
+
+static void
+aic_print_reg_dump_end(FILE *ofile, FILE *dfile,
+ symbol_node_t *regnode, u_int num_entries)
+{
+ char *lower_name;
+ char *letter;
+
+ lower_name = strdup(regnode->symbol->name);
+ if (lower_name == NULL)
+ stop("Unable to strdup symbol name", EX_SOFTWARE);
+
+ for (letter = lower_name; *letter != '\0'; letter++)
+ *letter = tolower(*letter);
+
+ if (dfile != NULL) {
+ if (num_entries != 0)
+ fprintf(dfile,
+"\n"
+"};\n"
+"\n");
+
+ fprintf(dfile,
+"int\n"
+"%s%s_print(u_int regvalue, u_int *cur_col, u_int wrap)\n"
+"{\n"
+" return (%sprint_register(%s%s, %d, \"%s\",\n"
+" 0x%02x, regvalue, cur_col, wrap));\n"
+"}\n"
+"\n",
+ prefix,
+ lower_name,
+ prefix,
+ num_entries != 0 ? regnode->symbol->name : "NULL",
+ num_entries != 0 ? "_parse_table" : "",
+ num_entries,
+ regnode->symbol->name,
+ regnode->symbol->info.rinfo->address);
+ }
+
+ fprintf(ofile,
+"#if AIC_DEBUG_REGISTERS\n"
+"%sreg_print_t %s%s_print;\n"
+"#else\n"
+"#define %s%s_print(regvalue, cur_col, wrap) \\\n"
+" %sprint_register(NULL, 0, \"%s\", 0x%02x, regvalue, cur_col, wrap)\n"
+"#endif\n"
+"\n",
+ prefix,
+ prefix,
+ lower_name,
+ prefix,
+ lower_name,
+ prefix,
+ regnode->symbol->name,
+ regnode->symbol->info.rinfo->address);
+}
+
+static void
+aic_print_reg_dump_entry(FILE *dfile, symbol_node_t *curnode)
+{
+ int num_tabs;
+
+ if (dfile == NULL)
+ return;
+
+ fprintf(dfile,
+" { \"%s\",",
+ curnode->symbol->name);
+
+ num_tabs = 3 - (strlen(curnode->symbol->name) + 5) / 8;
+
+ while (num_tabs-- > 0)
+ fputc('\t', dfile);
+ fprintf(dfile, "0x%02x, 0x%02x }",
+ curnode->symbol->info.finfo->value,
+ curnode->symbol->info.finfo->mask);
+}
+
+void
+symtable_dump(FILE *ofile, FILE *dfile)
+{
+ /*
+ * Sort the registers by address with a simple insertion sort.
+ * Put bitmasks next to the first register that defines them.
+ * Put constants at the end.
+ */
+ symlist_t registers;
+ symlist_t masks;
+ symlist_t constants;
+ symlist_t download_constants;
+ symlist_t aliases;
+ symlist_t exported_labels;
+ symbol_node_t *curnode;
+ symbol_node_t *regnode;
+ DBT key;
+ DBT data;
+ int flag;
+ u_int i;
+
+ if (symtable == NULL)
+ return;
+
+ SLIST_INIT(&registers);
+ SLIST_INIT(&masks);
+ SLIST_INIT(&constants);
+ SLIST_INIT(&download_constants);
+ SLIST_INIT(&aliases);
+ SLIST_INIT(&exported_labels);
+ flag = R_FIRST;
+ while (symtable->seq(symtable, &key, &data, flag) == 0) {
+ symbol_t *cursym;
+
+ memcpy(&cursym, data.data, sizeof(cursym));
+ switch(cursym->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ symlist_add(&registers, cursym, SYMLIST_SORT);
+ break;
+ case MASK:
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ symlist_add(&masks, cursym, SYMLIST_SORT);
+ break;
+ case CONST:
+ symlist_add(&constants, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case DOWNLOAD_CONST:
+ symlist_add(&download_constants, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case ALIAS:
+ symlist_add(&aliases, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case LABEL:
+ if (cursym->info.linfo->exported == 0)
+ break;
+ symlist_add(&exported_labels, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ default:
+ break;
+ }
+ flag = R_NEXT;
+ }
+
+ /* Register dianostic functions/declarations first. */
+ aic_print_file_prologue(ofile);
+ aic_print_reg_dump_types(ofile);
+ aic_print_file_prologue(dfile);
+ aic_print_include(dfile, stock_include_file);
+ SLIST_FOREACH(curnode, &registers, links) {
+
+ switch(curnode->symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ {
+ symlist_t *fields;
+ symbol_node_t *fieldnode;
+ int num_entries;
+
+ num_entries = 0;
+ fields = &curnode->symbol->info.rinfo->fields;
+ SLIST_FOREACH(fieldnode, fields, links) {
+ if (num_entries == 0)
+ aic_print_reg_dump_start(dfile,
+ curnode);
+ else if (dfile != NULL)
+ fputs(",\n", dfile);
+ num_entries++;
+ aic_print_reg_dump_entry(dfile, fieldnode);
+ }
+ aic_print_reg_dump_end(ofile, dfile,
+ curnode, num_entries);
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Fold in the masks and bits */
+ while (SLIST_FIRST(&masks) != NULL) {
+ char *regname;
+
+ curnode = SLIST_FIRST(&masks);
+ SLIST_REMOVE_HEAD(&masks, links);
+
+ regnode = SLIST_FIRST(&curnode->symbol->info.finfo->symrefs);
+ regname = regnode->symbol->name;
+ regnode = symlist_search(&registers, regname);
+ SLIST_INSERT_AFTER(regnode, curnode, links);
+ }
+
+ /* Add the aliases */
+ while (SLIST_FIRST(&aliases) != NULL) {
+ char *regname;
+
+ curnode = SLIST_FIRST(&aliases);
+ SLIST_REMOVE_HEAD(&aliases, links);
+
+ regname = curnode->symbol->info.ainfo->parent->name;
+ regnode = symlist_search(&registers, regname);
+ SLIST_INSERT_AFTER(regnode, curnode, links);
+ }
+
+ /* Output generated #defines. */
+ while (SLIST_FIRST(&registers) != NULL) {
+ symbol_node_t *curnode;
+ u_int value;
+ char *tab_str;
+ char *tab_str2;
+
+ curnode = SLIST_FIRST(&registers);
+ SLIST_REMOVE_HEAD(&registers, links);
+ switch(curnode->symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ fprintf(ofile, "\n");
+ value = curnode->symbol->info.rinfo->address;
+ tab_str = "\t";
+ tab_str2 = "\t\t";
+ break;
+ case ALIAS:
+ {
+ symbol_t *parent;
+
+ parent = curnode->symbol->info.ainfo->parent;
+ value = parent->info.rinfo->address;
+ tab_str = "\t";
+ tab_str2 = "\t\t";
+ break;
+ }
+ case MASK:
+ case FIELD:
+ case ENUM:
+ case ENUM_ENTRY:
+ value = curnode->symbol->info.finfo->value;
+ tab_str = "\t\t";
+ tab_str2 = "\t";
+ break;
+ default:
+ value = 0; /* Quiet compiler */
+ tab_str = NULL;
+ tab_str2 = NULL;
+ stop("symtable_dump: Invalid symbol type "
+ "encountered", EX_SOFTWARE);
+ break;
+ }
+ fprintf(ofile, "#define%s%-16s%s0x%02x\n",
+ tab_str, curnode->symbol->name, tab_str2,
+ value);
+ free(curnode);
+ }
+ fprintf(ofile, "\n\n");
+
+ while (SLIST_FIRST(&constants) != NULL) {
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(&constants);
+ SLIST_REMOVE_HEAD(&constants, links);
+ fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.cinfo->value);
+ free(curnode);
+ }
+
+
+ fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+ for (i = 0; SLIST_FIRST(&download_constants) != NULL; i++) {
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(&download_constants);
+ SLIST_REMOVE_HEAD(&download_constants, links);
+ fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.cinfo->value);
+ free(curnode);
+ }
+ fprintf(ofile, "#define\tDOWNLOAD_CONST_COUNT\t0x%02x\n", i);
+
+ fprintf(ofile, "\n\n/* Exported Labels */\n");
+
+ while (SLIST_FIRST(&exported_labels) != NULL) {
+ symbol_node_t *curnode;
+
+ curnode = SLIST_FIRST(&exported_labels);
+ SLIST_REMOVE_HEAD(&exported_labels, links);
+ fprintf(ofile, "#define\tLABEL_%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.linfo->address);
+ free(curnode);
+ }
+}
+
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
new file mode 100644
index 000000000000..afc22e8b4903
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
@@ -0,0 +1,207 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.h#17 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+typedef enum {
+ UNINITIALIZED,
+ REGISTER,
+ ALIAS,
+ SCBLOC,
+ SRAMLOC,
+ ENUM_ENTRY,
+ FIELD,
+ MASK,
+ ENUM,
+ CONST,
+ DOWNLOAD_CONST,
+ LABEL,
+ CONDITIONAL,
+ MACRO
+} symtype;
+
+typedef enum {
+ RO = 0x01,
+ WO = 0x02,
+ RW = 0x03
+}amode_t;
+
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
+struct reg_info {
+ u_int address;
+ int size;
+ amode_t mode;
+ symlist_t fields;
+ uint8_t valid_bitmask;
+ uint8_t modes;
+ int typecheck_masks;
+};
+
+struct field_info {
+ symlist_t symrefs;
+ uint8_t value;
+ uint8_t mask;
+};
+
+struct const_info {
+ u_int value;
+ int define;
+};
+
+struct alias_info {
+ struct symbol *parent;
+};
+
+struct label_info {
+ int address;
+ int exported;
+};
+
+struct cond_info {
+ int func_num;
+};
+
+struct macro_arg {
+ STAILQ_ENTRY(macro_arg) links;
+ regex_t arg_regex;
+ char *replacement_text;
+};
+STAILQ_HEAD(macro_arg_list, macro_arg) args;
+
+struct macro_info {
+ struct macro_arg_list args;
+ int narg;
+ const char* body;
+};
+
+typedef struct expression_info {
+ symlist_t referenced_syms;
+ int value;
+} expression_t;
+
+typedef struct symbol {
+ char *name;
+ symtype type;
+ union {
+ struct reg_info *rinfo;
+ struct field_info *finfo;
+ struct const_info *cinfo;
+ struct alias_info *ainfo;
+ struct label_info *linfo;
+ struct cond_info *condinfo;
+ struct macro_info *macroinfo;
+ }info;
+} symbol_t;
+
+typedef struct symbol_ref {
+ symbol_t *symbol;
+ int offset;
+} symbol_ref_t;
+
+typedef struct symbol_node {
+ SLIST_ENTRY(symbol_node) links;
+ symbol_t *symbol;
+} symbol_node_t;
+
+typedef struct critical_section {
+ TAILQ_ENTRY(critical_section) links;
+ int begin_addr;
+ int end_addr;
+} critical_section_t;
+
+typedef enum {
+ SCOPE_ROOT,
+ SCOPE_IF,
+ SCOPE_ELSE_IF,
+ SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+ int skip_patch;
+ int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+ SLIST_ENTRY(scope) scope_stack_links;
+ TAILQ_ENTRY(scope) scope_links;
+ TAILQ_HEAD(, scope) inner_scope;
+ scope_type type;
+ int inner_scope_patches;
+ int begin_addr;
+ int end_addr;
+ patch_info_t patches[2];
+ int func_num;
+} scope_t;
+
+TAILQ_HEAD(cs_tailq, critical_section);
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
+
+void symbol_delete(symbol_t *symbol);
+
+void symtable_open(void);
+
+void symtable_close(void);
+
+symbol_t *
+ symtable_get(char *name);
+
+symbol_node_t *
+ symlist_search(symlist_t *symlist, char *symname);
+
+void
+ symlist_add(symlist_t *symlist, symbol_t *symbol, int how);
+#define SYMLIST_INSERT_HEAD 0x00
+#define SYMLIST_SORT 0x01
+
+void symlist_free(symlist_t *symlist);
+
+void symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2);
+void symtable_dump(FILE *ofile, FILE *dfile);
diff --git a/drivers/scsi/aic7xxx/aiclib.c b/drivers/scsi/aic7xxx/aiclib.c
new file mode 100644
index 000000000000..79bfd9efd8ed
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.c
@@ -0,0 +1,1412 @@
+/*
+ * Implementation of Utility functions for all SCSI device types.
+ *
+ * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Kenneth D. Merry.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.38 2002/09/23 04:56:35 mjacob Exp $
+ * $Id$
+ */
+
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+
+/* Core SCSI definitions */
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aiclib.h"
+#include "cam.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+#ifndef ERESTART
+#define ERESTART -1 /* restart syscall */
+#endif
+#ifndef EJUSTRETURN
+#define EJUSTRETURN -2 /* don't modify regs, just return */
+#endif
+
+static int ascentrycomp(const void *key, const void *member);
+static int senseentrycomp(const void *key, const void *member);
+static void fetchtableentries(int sense_key, int asc, int ascq,
+ struct scsi_inquiry_data *,
+ const struct sense_key_table_entry **,
+ const struct asc_table_entry **);
+static void * scsibsearch(const void *key, const void *base, size_t nmemb,
+ size_t size,
+ int (*compar)(const void *, const void *));
+typedef int (cam_quirkmatch_t)(caddr_t, caddr_t);
+static int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern,
+ int str_len);
+static caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table,
+ int num_entries, int entry_size,
+ cam_quirkmatch_t *comp_func);
+
+#define SCSI_NO_SENSE_STRINGS 1
+#if !defined(SCSI_NO_SENSE_STRINGS)
+#define SST(asc, ascq, action, desc) \
+ asc, ascq, action, desc
+#else
+static const char empty_string[] = "";
+
+#define SST(asc, ascq, action, desc) \
+ asc, ascq, action, empty_string
+#endif
+
+static const struct sense_key_table_entry sense_key_table[] =
+{
+ { SSD_KEY_NO_SENSE, SS_NOP, "NO SENSE" },
+ { SSD_KEY_RECOVERED_ERROR, SS_NOP|SSQ_PRINT_SENSE, "RECOVERED ERROR" },
+ {
+ SSD_KEY_NOT_READY, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY,
+ "NOT READY"
+ },
+ { SSD_KEY_MEDIUM_ERROR, SS_RDEF, "MEDIUM ERROR" },
+ { SSD_KEY_HARDWARE_ERROR, SS_RDEF, "HARDWARE FAILURE" },
+ { SSD_KEY_ILLEGAL_REQUEST, SS_FATAL|EINVAL, "ILLEGAL REQUEST" },
+ { SSD_KEY_UNIT_ATTENTION, SS_FATAL|ENXIO, "UNIT ATTENTION" },
+ { SSD_KEY_DATA_PROTECT, SS_FATAL|EACCES, "DATA PROTECT" },
+ { SSD_KEY_BLANK_CHECK, SS_FATAL|ENOSPC, "BLANK CHECK" },
+ { SSD_KEY_Vendor_Specific, SS_FATAL|EIO, "Vendor Specific" },
+ { SSD_KEY_COPY_ABORTED, SS_FATAL|EIO, "COPY ABORTED" },
+ { SSD_KEY_ABORTED_COMMAND, SS_RDEF, "ABORTED COMMAND" },
+ { SSD_KEY_EQUAL, SS_NOP, "EQUAL" },
+ { SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" },
+ { SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" },
+ { SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" }
+};
+
+static const int sense_key_table_size =
+ sizeof(sense_key_table)/sizeof(sense_key_table[0]);
+
+static struct asc_table_entry quantum_fireball_entries[] = {
+ {SST(0x04, 0x0b, SS_START|SSQ_DECREMENT_COUNT|ENXIO,
+ "Logical unit not ready, initializing cmd. required")}
+};
+
+static struct asc_table_entry sony_mo_entries[] = {
+ {SST(0x04, 0x00, SS_START|SSQ_DECREMENT_COUNT|ENXIO,
+ "Logical unit not ready, cause not reportable")}
+};
+
+static struct scsi_sense_quirk_entry sense_quirk_table[] = {
+ {
+ /*
+ * The Quantum Fireball ST and SE like to return 0x04 0x0b when
+ * they really should return 0x04 0x02. 0x04,0x0b isn't
+ * defined in any SCSI spec, and it isn't mentioned in the
+ * hardware manual for these drives.
+ */
+ {T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"},
+ /*num_sense_keys*/0,
+ sizeof(quantum_fireball_entries)/sizeof(struct asc_table_entry),
+ /*sense key entries*/NULL,
+ quantum_fireball_entries
+ },
+ {
+ /*
+ * This Sony MO drive likes to return 0x04, 0x00 when it
+ * isn't spun up.
+ */
+ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SONY", "SMO-*", "*"},
+ /*num_sense_keys*/0,
+ sizeof(sony_mo_entries)/sizeof(struct asc_table_entry),
+ /*sense key entries*/NULL,
+ sony_mo_entries
+ }
+};
+
+static const int sense_quirk_table_size =
+ sizeof(sense_quirk_table)/sizeof(sense_quirk_table[0]);
+
+static struct asc_table_entry asc_table[] = {
+/*
+ * From File: ASC-NUM.TXT
+ * SCSI ASC/ASCQ Assignments
+ * Numeric Sorted Listing
+ * as of 5/12/97
+ *
+ * D - DIRECT ACCESS DEVICE (SBC) device column key
+ * .T - SEQUENTIAL ACCESS DEVICE (SSC) -------------------
+ * . L - PRINTER DEVICE (SSC) blank = reserved
+ * . P - PROCESSOR DEVICE (SPC) not blank = allowed
+ * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC)
+ * . . R - CD DEVICE (MMC)
+ * . . S - SCANNER DEVICE (SGC)
+ * . . .O - OPTICAL MEMORY DEVICE (SBC)
+ * . . . M - MEDIA CHANGER DEVICE (SMC)
+ * . . . C - COMMUNICATION DEVICE (SSC)
+ * . . . .A - STORAGE ARRAY DEVICE (SCC)
+ * . . . . E - ENCLOSURE SERVICES DEVICE (SES)
+ * DTLPWRSOMCAE ASC ASCQ Action Description
+ * ------------ ---- ---- ------ -----------------------------------*/
+/* DTLPWRSOMCAE */{SST(0x00, 0x00, SS_NOP,
+ "No additional sense information") },
+/* T S */{SST(0x00, 0x01, SS_RDEF,
+ "Filemark detected") },
+/* T S */{SST(0x00, 0x02, SS_RDEF,
+ "End-of-partition/medium detected") },
+/* T */{SST(0x00, 0x03, SS_RDEF,
+ "Setmark detected") },
+/* T S */{SST(0x00, 0x04, SS_RDEF,
+ "Beginning-of-partition/medium detected") },
+/* T S */{SST(0x00, 0x05, SS_RDEF,
+ "End-of-data detected") },
+/* DTLPWRSOMCAE */{SST(0x00, 0x06, SS_RDEF,
+ "I/O process terminated") },
+/* R */{SST(0x00, 0x11, SS_FATAL|EBUSY,
+ "Audio play operation in progress") },
+/* R */{SST(0x00, 0x12, SS_NOP,
+ "Audio play operation paused") },
+/* R */{SST(0x00, 0x13, SS_NOP,
+ "Audio play operation successfully completed") },
+/* R */{SST(0x00, 0x14, SS_RDEF,
+ "Audio play operation stopped due to error") },
+/* R */{SST(0x00, 0x15, SS_NOP,
+ "No current audio status to return") },
+/* DTLPWRSOMCAE */{SST(0x00, 0x16, SS_FATAL|EBUSY,
+ "Operation in progress") },
+/* DTL WRSOM AE */{SST(0x00, 0x17, SS_RDEF,
+ "Cleaning requested") },
+/* D W O */{SST(0x01, 0x00, SS_RDEF,
+ "No index/sector signal") },
+/* D WR OM */{SST(0x02, 0x00, SS_RDEF,
+ "No seek complete") },
+/* DTL W SO */{SST(0x03, 0x00, SS_RDEF,
+ "Peripheral device write fault") },
+/* T */{SST(0x03, 0x01, SS_RDEF,
+ "No write current") },
+/* T */{SST(0x03, 0x02, SS_RDEF,
+ "Excessive write errors") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x00,
+ SS_TUR|SSQ_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EIO,
+ "Logical unit not ready, cause not reportable") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x01,
+ SS_TUR|SSQ_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY,
+ "Logical unit is in process of becoming ready") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x02, SS_START|SSQ_DECREMENT_COUNT|ENXIO,
+ "Logical unit not ready, initializing cmd. required") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x03, SS_FATAL|ENXIO,
+ "Logical unit not ready, manual intervention required")},
+/* DTL O */{SST(0x04, 0x04, SS_FATAL|EBUSY,
+ "Logical unit not ready, format in progress") },
+/* DT W OMCA */{SST(0x04, 0x05, SS_FATAL|EBUSY,
+ "Logical unit not ready, rebuild in progress") },
+/* DT W OMCA */{SST(0x04, 0x06, SS_FATAL|EBUSY,
+ "Logical unit not ready, recalculation in progress") },
+/* DTLPWRSOMCAE */{SST(0x04, 0x07, SS_FATAL|EBUSY,
+ "Logical unit not ready, operation in progress") },
+/* R */{SST(0x04, 0x08, SS_FATAL|EBUSY,
+ "Logical unit not ready, long write in progress") },
+/* DTL WRSOMCAE */{SST(0x05, 0x00, SS_RDEF,
+ "Logical unit does not respond to selection") },
+/* D WR OM */{SST(0x06, 0x00, SS_RDEF,
+ "No reference position found") },
+/* DTL WRSOM */{SST(0x07, 0x00, SS_RDEF,
+ "Multiple peripheral devices selected") },
+/* DTL WRSOMCAE */{SST(0x08, 0x00, SS_RDEF,
+ "Logical unit communication failure") },
+/* DTL WRSOMCAE */{SST(0x08, 0x01, SS_RDEF,
+ "Logical unit communication time-out") },
+/* DTL WRSOMCAE */{SST(0x08, 0x02, SS_RDEF,
+ "Logical unit communication parity error") },
+/* DT R OM */{SST(0x08, 0x03, SS_RDEF,
+ "Logical unit communication crc error (ultra-dma/32)")},
+/* DT WR O */{SST(0x09, 0x00, SS_RDEF,
+ "Track following error") },
+/* WR O */{SST(0x09, 0x01, SS_RDEF,
+ "Tracking servo failure") },
+/* WR O */{SST(0x09, 0x02, SS_RDEF,
+ "Focus servo failure") },
+/* WR O */{SST(0x09, 0x03, SS_RDEF,
+ "Spindle servo failure") },
+/* DT WR O */{SST(0x09, 0x04, SS_RDEF,
+ "Head select fault") },
+/* DTLPWRSOMCAE */{SST(0x0A, 0x00, SS_FATAL|ENOSPC,
+ "Error log overflow") },
+/* DTLPWRSOMCAE */{SST(0x0B, 0x00, SS_RDEF,
+ "Warning") },
+/* DTLPWRSOMCAE */{SST(0x0B, 0x01, SS_RDEF,
+ "Specified temperature exceeded") },
+/* DTLPWRSOMCAE */{SST(0x0B, 0x02, SS_RDEF,
+ "Enclosure degraded") },
+/* T RS */{SST(0x0C, 0x00, SS_RDEF,
+ "Write error") },
+/* D W O */{SST(0x0C, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Write error - recovered with auto reallocation") },
+/* D W O */{SST(0x0C, 0x02, SS_RDEF,
+ "Write error - auto reallocation failed") },
+/* D W O */{SST(0x0C, 0x03, SS_RDEF,
+ "Write error - recommend reassignment") },
+/* DT W O */{SST(0x0C, 0x04, SS_RDEF,
+ "Compression check miscompare error") },
+/* DT W O */{SST(0x0C, 0x05, SS_RDEF,
+ "Data expansion occurred during compression") },
+/* DT W O */{SST(0x0C, 0x06, SS_RDEF,
+ "Block not compressible") },
+/* R */{SST(0x0C, 0x07, SS_RDEF,
+ "Write error - recovery needed") },
+/* R */{SST(0x0C, 0x08, SS_RDEF,
+ "Write error - recovery failed") },
+/* R */{SST(0x0C, 0x09, SS_RDEF,
+ "Write error - loss of streaming") },
+/* R */{SST(0x0C, 0x0A, SS_RDEF,
+ "Write error - padding blocks added") },
+/* D W O */{SST(0x10, 0x00, SS_RDEF,
+ "ID CRC or ECC error") },
+/* DT WRSO */{SST(0x11, 0x00, SS_RDEF,
+ "Unrecovered read error") },
+/* DT W SO */{SST(0x11, 0x01, SS_RDEF,
+ "Read retries exhausted") },
+/* DT W SO */{SST(0x11, 0x02, SS_RDEF,
+ "Error too long to correct") },
+/* DT W SO */{SST(0x11, 0x03, SS_RDEF,
+ "Multiple read errors") },
+/* D W O */{SST(0x11, 0x04, SS_RDEF,
+ "Unrecovered read error - auto reallocate failed") },
+/* WR O */{SST(0x11, 0x05, SS_RDEF,
+ "L-EC uncorrectable error") },
+/* WR O */{SST(0x11, 0x06, SS_RDEF,
+ "CIRC unrecovered error") },
+/* W O */{SST(0x11, 0x07, SS_RDEF,
+ "Data re-synchronization error") },
+/* T */{SST(0x11, 0x08, SS_RDEF,
+ "Incomplete block read") },
+/* T */{SST(0x11, 0x09, SS_RDEF,
+ "No gap found") },
+/* DT O */{SST(0x11, 0x0A, SS_RDEF,
+ "Miscorrected error") },
+/* D W O */{SST(0x11, 0x0B, SS_RDEF,
+ "Unrecovered read error - recommend reassignment") },
+/* D W O */{SST(0x11, 0x0C, SS_RDEF,
+ "Unrecovered read error - recommend rewrite the data")},
+/* DT WR O */{SST(0x11, 0x0D, SS_RDEF,
+ "De-compression CRC error") },
+/* DT WR O */{SST(0x11, 0x0E, SS_RDEF,
+ "Cannot decompress using declared algorithm") },
+/* R */{SST(0x11, 0x0F, SS_RDEF,
+ "Error reading UPC/EAN number") },
+/* R */{SST(0x11, 0x10, SS_RDEF,
+ "Error reading ISRC number") },
+/* R */{SST(0x11, 0x11, SS_RDEF,
+ "Read error - loss of streaming") },
+/* D W O */{SST(0x12, 0x00, SS_RDEF,
+ "Address mark not found for id field") },
+/* D W O */{SST(0x13, 0x00, SS_RDEF,
+ "Address mark not found for data field") },
+/* DTL WRSO */{SST(0x14, 0x00, SS_RDEF,
+ "Recorded entity not found") },
+/* DT WR O */{SST(0x14, 0x01, SS_RDEF,
+ "Record not found") },
+/* T */{SST(0x14, 0x02, SS_RDEF,
+ "Filemark or setmark not found") },
+/* T */{SST(0x14, 0x03, SS_RDEF,
+ "End-of-data not found") },
+/* T */{SST(0x14, 0x04, SS_RDEF,
+ "Block sequence error") },
+/* DT W O */{SST(0x14, 0x05, SS_RDEF,
+ "Record not found - recommend reassignment") },
+/* DT W O */{SST(0x14, 0x06, SS_RDEF,
+ "Record not found - data auto-reallocated") },
+/* DTL WRSOM */{SST(0x15, 0x00, SS_RDEF,
+ "Random positioning error") },
+/* DTL WRSOM */{SST(0x15, 0x01, SS_RDEF,
+ "Mechanical positioning error") },
+/* DT WR O */{SST(0x15, 0x02, SS_RDEF,
+ "Positioning error detected by read of medium") },
+/* D W O */{SST(0x16, 0x00, SS_RDEF,
+ "Data synchronization mark error") },
+/* D W O */{SST(0x16, 0x01, SS_RDEF,
+ "Data sync error - data rewritten") },
+/* D W O */{SST(0x16, 0x02, SS_RDEF,
+ "Data sync error - recommend rewrite") },
+/* D W O */{SST(0x16, 0x03, SS_NOP|SSQ_PRINT_SENSE,
+ "Data sync error - data auto-reallocated") },
+/* D W O */{SST(0x16, 0x04, SS_RDEF,
+ "Data sync error - recommend reassignment") },
+/* DT WRSO */{SST(0x17, 0x00, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with no error correction applied") },
+/* DT WRSO */{SST(0x17, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with retries") },
+/* DT WR O */{SST(0x17, 0x02, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with positive head offset") },
+/* DT WR O */{SST(0x17, 0x03, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with negative head offset") },
+/* WR O */{SST(0x17, 0x04, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with retries and/or CIRC applied") },
+/* D WR O */{SST(0x17, 0x05, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data using previous sector id") },
+/* D W O */{SST(0x17, 0x06, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - data auto-reallocated") },
+/* D W O */{SST(0x17, 0x07, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - recommend reassignment")},
+/* D W O */{SST(0x17, 0x08, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - recommend rewrite") },
+/* D W O */{SST(0x17, 0x09, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data without ECC - data rewritten") },
+/* D W O */{SST(0x18, 0x00, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with error correction applied") },
+/* D WR O */{SST(0x18, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with error corr. & retries applied") },
+/* D WR O */{SST(0x18, 0x02, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data - data auto-reallocated") },
+/* R */{SST(0x18, 0x03, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with CIRC") },
+/* R */{SST(0x18, 0x04, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with L-EC") },
+/* D WR O */{SST(0x18, 0x05, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data - recommend reassignment") },
+/* D WR O */{SST(0x18, 0x06, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data - recommend rewrite") },
+/* D W O */{SST(0x18, 0x07, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered data with ECC - data rewritten") },
+/* D O */{SST(0x19, 0x00, SS_RDEF,
+ "Defect list error") },
+/* D O */{SST(0x19, 0x01, SS_RDEF,
+ "Defect list not available") },
+/* D O */{SST(0x19, 0x02, SS_RDEF,
+ "Defect list error in primary list") },
+/* D O */{SST(0x19, 0x03, SS_RDEF,
+ "Defect list error in grown list") },
+/* DTLPWRSOMCAE */{SST(0x1A, 0x00, SS_RDEF,
+ "Parameter list length error") },
+/* DTLPWRSOMCAE */{SST(0x1B, 0x00, SS_RDEF,
+ "Synchronous data transfer error") },
+/* D O */{SST(0x1C, 0x00, SS_RDEF,
+ "Defect list not found") },
+/* D O */{SST(0x1C, 0x01, SS_RDEF,
+ "Primary defect list not found") },
+/* D O */{SST(0x1C, 0x02, SS_RDEF,
+ "Grown defect list not found") },
+/* D W O */{SST(0x1D, 0x00, SS_FATAL,
+ "Miscompare during verify operation" )},
+/* D W O */{SST(0x1E, 0x00, SS_NOP|SSQ_PRINT_SENSE,
+ "Recovered id with ecc correction") },
+/* D O */{SST(0x1F, 0x00, SS_RDEF,
+ "Partial defect list transfer") },
+/* DTLPWRSOMCAE */{SST(0x20, 0x00, SS_FATAL|EINVAL,
+ "Invalid command operation code") },
+/* DT WR OM */{SST(0x21, 0x00, SS_FATAL|EINVAL,
+ "Logical block address out of range" )},
+/* DT WR OM */{SST(0x21, 0x01, SS_FATAL|EINVAL,
+ "Invalid element address") },
+/* D */{SST(0x22, 0x00, SS_FATAL|EINVAL,
+ "Illegal function") }, /* Deprecated. Use 20 00, 24 00, or 26 00 instead */
+/* DTLPWRSOMCAE */{SST(0x24, 0x00, SS_FATAL|EINVAL,
+ "Invalid field in CDB") },
+/* DTLPWRSOMCAE */{SST(0x25, 0x00, SS_FATAL|ENXIO,
+ "Logical unit not supported") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x00, SS_FATAL|EINVAL,
+ "Invalid field in parameter list") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x01, SS_FATAL|EINVAL,
+ "Parameter not supported") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x02, SS_FATAL|EINVAL,
+ "Parameter value invalid") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x03, SS_FATAL|EINVAL,
+ "Threshold parameters not supported") },
+/* DTLPWRSOMCAE */{SST(0x26, 0x04, SS_FATAL|EINVAL,
+ "Invalid release of active persistent reservation") },
+/* DT W O */{SST(0x27, 0x00, SS_FATAL|EACCES,
+ "Write protected") },
+/* DT W O */{SST(0x27, 0x01, SS_FATAL|EACCES,
+ "Hardware write protected") },
+/* DT W O */{SST(0x27, 0x02, SS_FATAL|EACCES,
+ "Logical unit software write protected") },
+/* T */{SST(0x27, 0x03, SS_FATAL|EACCES,
+ "Associated write protect") },
+/* T */{SST(0x27, 0x04, SS_FATAL|EACCES,
+ "Persistent write protect") },
+/* T */{SST(0x27, 0x05, SS_FATAL|EACCES,
+ "Permanent write protect") },
+/* DTLPWRSOMCAE */{SST(0x28, 0x00, SS_RDEF,
+ "Not ready to ready change, medium may have changed") },
+/* DTLPWRSOMCAE */{SST(0x28, 0x01, SS_FATAL|ENXIO,
+ "Import or export element accessed") },
+/*
+ * XXX JGibbs - All of these should use the same errno, but I don't think
+ * ENXIO is the correct choice. Should we borrow from the networking
+ * errnos? ECONNRESET anyone?
+ */
+/* DTLPWRSOMCAE */{SST(0x29, 0x00, SS_RDEF,
+ "Power on, reset, or bus device reset occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x01, SS_RDEF,
+ "Power on occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x02, SS_RDEF,
+ "Scsi bus reset occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x03, SS_RDEF,
+ "Bus device reset function occurred") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x04, SS_RDEF,
+ "Device internal reset") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x05, SS_RDEF,
+ "Transceiver mode changed to single-ended") },
+/* DTLPWRSOMCAE */{SST(0x29, 0x06, SS_RDEF,
+ "Transceiver mode changed to LVD") },
+/* DTL WRSOMCAE */{SST(0x2A, 0x00, SS_RDEF,
+ "Parameters changed") },
+/* DTL WRSOMCAE */{SST(0x2A, 0x01, SS_RDEF,
+ "Mode parameters changed") },
+/* DTL WRSOMCAE */{SST(0x2A, 0x02, SS_RDEF,
+ "Log parameters changed") },
+/* DTLPWRSOMCAE */{SST(0x2A, 0x03, SS_RDEF,
+ "Reservations preempted") },
+/* DTLPWRSO C */{SST(0x2B, 0x00, SS_RDEF,
+ "Copy cannot execute since host cannot disconnect") },
+/* DTLPWRSOMCAE */{SST(0x2C, 0x00, SS_RDEF,
+ "Command sequence error") },
+/* S */{SST(0x2C, 0x01, SS_RDEF,
+ "Too many windows specified") },
+/* S */{SST(0x2C, 0x02, SS_RDEF,
+ "Invalid combination of windows specified") },
+/* R */{SST(0x2C, 0x03, SS_RDEF,
+ "Current program area is not empty") },
+/* R */{SST(0x2C, 0x04, SS_RDEF,
+ "Current program area is empty") },
+/* T */{SST(0x2D, 0x00, SS_RDEF,
+ "Overwrite error on update in place") },
+/* DTLPWRSOMCAE */{SST(0x2F, 0x00, SS_RDEF,
+ "Commands cleared by another initiator") },
+/* DT WR OM */{SST(0x30, 0x00, SS_RDEF,
+ "Incompatible medium installed") },
+/* DT WR O */{SST(0x30, 0x01, SS_RDEF,
+ "Cannot read medium - unknown format") },
+/* DT WR O */{SST(0x30, 0x02, SS_RDEF,
+ "Cannot read medium - incompatible format") },
+/* DT */{SST(0x30, 0x03, SS_RDEF,
+ "Cleaning cartridge installed") },
+/* DT WR O */{SST(0x30, 0x04, SS_RDEF,
+ "Cannot write medium - unknown format") },
+/* DT WR O */{SST(0x30, 0x05, SS_RDEF,
+ "Cannot write medium - incompatible format") },
+/* DT W O */{SST(0x30, 0x06, SS_RDEF,
+ "Cannot format medium - incompatible medium") },
+/* DTL WRSOM AE */{SST(0x30, 0x07, SS_RDEF,
+ "Cleaning failure") },
+/* R */{SST(0x30, 0x08, SS_RDEF,
+ "Cannot write - application code mismatch") },
+/* R */{SST(0x30, 0x09, SS_RDEF,
+ "Current session not fixated for append") },
+/* DT WR O */{SST(0x31, 0x00, SS_RDEF,
+ "Medium format corrupted") },
+/* D L R O */{SST(0x31, 0x01, SS_RDEF,
+ "Format command failed") },
+/* D W O */{SST(0x32, 0x00, SS_RDEF,
+ "No defect spare location available") },
+/* D W O */{SST(0x32, 0x01, SS_RDEF,
+ "Defect list update failure") },
+/* T */{SST(0x33, 0x00, SS_RDEF,
+ "Tape length error") },
+/* DTLPWRSOMCAE */{SST(0x34, 0x00, SS_RDEF,
+ "Enclosure failure") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x00, SS_RDEF,
+ "Enclosure services failure") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x01, SS_RDEF,
+ "Unsupported enclosure function") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x02, SS_RDEF,
+ "Enclosure services unavailable") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x03, SS_RDEF,
+ "Enclosure services transfer failure") },
+/* DTLPWRSOMCAE */{SST(0x35, 0x04, SS_RDEF,
+ "Enclosure services transfer refused") },
+/* L */{SST(0x36, 0x00, SS_RDEF,
+ "Ribbon, ink, or toner failure") },
+/* DTL WRSOMCAE */{SST(0x37, 0x00, SS_RDEF,
+ "Rounded parameter") },
+/* DTL WRSOMCAE */{SST(0x39, 0x00, SS_RDEF,
+ "Saving parameters not supported") },
+/* DTL WRSOM */{SST(0x3A, 0x00, SS_NOP,
+ "Medium not present") },
+/* DT WR OM */{SST(0x3A, 0x01, SS_NOP,
+ "Medium not present - tray closed") },
+/* DT WR OM */{SST(0x3A, 0x01, SS_NOP,
+ "Medium not present - tray open") },
+/* DT WR OM */{SST(0x3A, 0x03, SS_NOP,
+ "Medium not present - Loadable") },
+/* DT WR OM */{SST(0x3A, 0x04, SS_NOP,
+ "Medium not present - medium auxiliary "
+ "memory accessible") },
+/* DT WR OM */{SST(0x3A, 0xFF, SS_NOP, NULL) },/* Range 0x05->0xFF */
+/* TL */{SST(0x3B, 0x00, SS_RDEF,
+ "Sequential positioning error") },
+/* T */{SST(0x3B, 0x01, SS_RDEF,
+ "Tape position error at beginning-of-medium") },
+/* T */{SST(0x3B, 0x02, SS_RDEF,
+ "Tape position error at end-of-medium") },
+/* L */{SST(0x3B, 0x03, SS_RDEF,
+ "Tape or electronic vertical forms unit not ready") },
+/* L */{SST(0x3B, 0x04, SS_RDEF,
+ "Slew failure") },
+/* L */{SST(0x3B, 0x05, SS_RDEF,
+ "Paper jam") },
+/* L */{SST(0x3B, 0x06, SS_RDEF,
+ "Failed to sense top-of-form") },
+/* L */{SST(0x3B, 0x07, SS_RDEF,
+ "Failed to sense bottom-of-form") },
+/* T */{SST(0x3B, 0x08, SS_RDEF,
+ "Reposition error") },
+/* S */{SST(0x3B, 0x09, SS_RDEF,
+ "Read past end of medium") },
+/* S */{SST(0x3B, 0x0A, SS_RDEF,
+ "Read past beginning of medium") },
+/* S */{SST(0x3B, 0x0B, SS_RDEF,
+ "Position past end of medium") },
+/* T S */{SST(0x3B, 0x0C, SS_RDEF,
+ "Position past beginning of medium") },
+/* DT WR OM */{SST(0x3B, 0x0D, SS_FATAL|ENOSPC,
+ "Medium destination element full") },
+/* DT WR OM */{SST(0x3B, 0x0E, SS_RDEF,
+ "Medium source element empty") },
+/* R */{SST(0x3B, 0x0F, SS_RDEF,
+ "End of medium reached") },
+/* DT WR OM */{SST(0x3B, 0x11, SS_RDEF,
+ "Medium magazine not accessible") },
+/* DT WR OM */{SST(0x3B, 0x12, SS_RDEF,
+ "Medium magazine removed") },
+/* DT WR OM */{SST(0x3B, 0x13, SS_RDEF,
+ "Medium magazine inserted") },
+/* DT WR OM */{SST(0x3B, 0x14, SS_RDEF,
+ "Medium magazine locked") },
+/* DT WR OM */{SST(0x3B, 0x15, SS_RDEF,
+ "Medium magazine unlocked") },
+/* DTLPWRSOMCAE */{SST(0x3D, 0x00, SS_RDEF,
+ "Invalid bits in identify message") },
+/* DTLPWRSOMCAE */{SST(0x3E, 0x00, SS_RDEF,
+ "Logical unit has not self-configured yet") },
+/* DTLPWRSOMCAE */{SST(0x3E, 0x01, SS_RDEF,
+ "Logical unit failure") },
+/* DTLPWRSOMCAE */{SST(0x3E, 0x02, SS_RDEF,
+ "Timeout on logical unit") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x00, SS_RDEF,
+ "Target operating conditions have changed") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x01, SS_RDEF,
+ "Microcode has been changed") },
+/* DTLPWRSOMC */{SST(0x3F, 0x02, SS_RDEF,
+ "Changed operating definition") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x03, SS_INQ_REFRESH|SSQ_DECREMENT_COUNT,
+ "Inquiry data has changed") },
+/* DT WR OMCAE */{SST(0x3F, 0x04, SS_RDEF,
+ "Component device attached") },
+/* DT WR OMCAE */{SST(0x3F, 0x05, SS_RDEF,
+ "Device identifier changed") },
+/* DT WR OMCAE */{SST(0x3F, 0x06, SS_RDEF,
+ "Redundancy group created or modified") },
+/* DT WR OMCAE */{SST(0x3F, 0x07, SS_RDEF,
+ "Redundancy group deleted") },
+/* DT WR OMCAE */{SST(0x3F, 0x08, SS_RDEF,
+ "Spare created or modified") },
+/* DT WR OMCAE */{SST(0x3F, 0x09, SS_RDEF,
+ "Spare deleted") },
+/* DT WR OMCAE */{SST(0x3F, 0x0A, SS_RDEF,
+ "Volume set created or modified") },
+/* DT WR OMCAE */{SST(0x3F, 0x0B, SS_RDEF,
+ "Volume set deleted") },
+/* DT WR OMCAE */{SST(0x3F, 0x0C, SS_RDEF,
+ "Volume set deassigned") },
+/* DT WR OMCAE */{SST(0x3F, 0x0D, SS_RDEF,
+ "Volume set reassigned") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x0E, SS_RDEF,
+ "Reported luns data has changed") },
+/* DTLPWRSOMCAE */{SST(0x3F, 0x0F, SS_RETRY|SSQ_DECREMENT_COUNT
+ | SSQ_DELAY_RANDOM|EBUSY,
+ "Echo buffer overwritten") },
+/* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF, "Medium Loadable") },
+/* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF,
+ "Medium auxiliary memory accessible") },
+/* D */{SST(0x40, 0x00, SS_RDEF,
+ "Ram failure") }, /* deprecated - use 40 NN instead */
+/* DTLPWRSOMCAE */{SST(0x40, 0x80, SS_RDEF,
+ "Diagnostic failure: ASCQ = Component ID") },
+/* DTLPWRSOMCAE */{SST(0x40, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL) },/* Range 0x80->0xFF */
+/* D */{SST(0x41, 0x00, SS_RDEF,
+ "Data path failure") }, /* deprecated - use 40 NN instead */
+/* D */{SST(0x42, 0x00, SS_RDEF,
+ "Power-on or self-test failure") }, /* deprecated - use 40 NN instead */
+/* DTLPWRSOMCAE */{SST(0x43, 0x00, SS_RDEF,
+ "Message error") },
+/* DTLPWRSOMCAE */{SST(0x44, 0x00, SS_RDEF,
+ "Internal target failure") },
+/* DTLPWRSOMCAE */{SST(0x45, 0x00, SS_RDEF,
+ "Select or reselect failure") },
+/* DTLPWRSOMC */{SST(0x46, 0x00, SS_RDEF,
+ "Unsuccessful soft reset") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x00, SS_RDEF|SSQ_FALLBACK,
+ "SCSI parity error") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x01, SS_RDEF|SSQ_FALLBACK,
+ "Data Phase CRC error detected") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x02, SS_RDEF|SSQ_FALLBACK,
+ "SCSI parity error detected during ST data phase") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x03, SS_RDEF|SSQ_FALLBACK,
+ "Information Unit iuCRC error") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x04, SS_RDEF|SSQ_FALLBACK,
+ "Asynchronous information protection error detected") },
+/* DTLPWRSOMCAE */{SST(0x47, 0x05, SS_RDEF|SSQ_FALLBACK,
+ "Protocol server CRC error") },
+/* DTLPWRSOMCAE */{SST(0x48, 0x00, SS_RDEF|SSQ_FALLBACK,
+ "Initiator detected error message received") },
+/* DTLPWRSOMCAE */{SST(0x49, 0x00, SS_RDEF,
+ "Invalid message error") },
+/* DTLPWRSOMCAE */{SST(0x4A, 0x00, SS_RDEF,
+ "Command phase error") },
+/* DTLPWRSOMCAE */{SST(0x4B, 0x00, SS_RDEF,
+ "Data phase error") },
+/* DTLPWRSOMCAE */{SST(0x4C, 0x00, SS_RDEF,
+ "Logical unit failed self-configuration") },
+/* DTLPWRSOMCAE */{SST(0x4D, 0x00, SS_RDEF,
+ "Tagged overlapped commands: ASCQ = Queue tag ID") },
+/* DTLPWRSOMCAE */{SST(0x4D, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL)}, /* Range 0x00->0xFF */
+/* DTLPWRSOMCAE */{SST(0x4E, 0x00, SS_RDEF,
+ "Overlapped commands attempted") },
+/* T */{SST(0x50, 0x00, SS_RDEF,
+ "Write append error") },
+/* T */{SST(0x50, 0x01, SS_RDEF,
+ "Write append position error") },
+/* T */{SST(0x50, 0x02, SS_RDEF,
+ "Position error related to timing") },
+/* T O */{SST(0x51, 0x00, SS_RDEF,
+ "Erase failure") },
+/* T */{SST(0x52, 0x00, SS_RDEF,
+ "Cartridge fault") },
+/* DTL WRSOM */{SST(0x53, 0x00, SS_RDEF,
+ "Media load or eject failed") },
+/* T */{SST(0x53, 0x01, SS_RDEF,
+ "Unload tape failure") },
+/* DT WR OM */{SST(0x53, 0x02, SS_RDEF,
+ "Medium removal prevented") },
+/* P */{SST(0x54, 0x00, SS_RDEF,
+ "Scsi to host system interface failure") },
+/* P */{SST(0x55, 0x00, SS_RDEF,
+ "System resource failure") },
+/* D O */{SST(0x55, 0x01, SS_FATAL|ENOSPC,
+ "System buffer full") },
+/* R */{SST(0x57, 0x00, SS_RDEF,
+ "Unable to recover table-of-contents") },
+/* O */{SST(0x58, 0x00, SS_RDEF,
+ "Generation does not exist") },
+/* O */{SST(0x59, 0x00, SS_RDEF,
+ "Updated block read") },
+/* DTLPWRSOM */{SST(0x5A, 0x00, SS_RDEF,
+ "Operator request or state change input") },
+/* DT WR OM */{SST(0x5A, 0x01, SS_RDEF,
+ "Operator medium removal request") },
+/* DT W O */{SST(0x5A, 0x02, SS_RDEF,
+ "Operator selected write protect") },
+/* DT W O */{SST(0x5A, 0x03, SS_RDEF,
+ "Operator selected write permit") },
+/* DTLPWRSOM */{SST(0x5B, 0x00, SS_RDEF,
+ "Log exception") },
+/* DTLPWRSOM */{SST(0x5B, 0x01, SS_RDEF,
+ "Threshold condition met") },
+/* DTLPWRSOM */{SST(0x5B, 0x02, SS_RDEF,
+ "Log counter at maximum") },
+/* DTLPWRSOM */{SST(0x5B, 0x03, SS_RDEF,
+ "Log list codes exhausted") },
+/* D O */{SST(0x5C, 0x00, SS_RDEF,
+ "RPL status change") },
+/* D O */{SST(0x5C, 0x01, SS_NOP|SSQ_PRINT_SENSE,
+ "Spindles synchronized") },
+/* D O */{SST(0x5C, 0x02, SS_RDEF,
+ "Spindles not synchronized") },
+/* DTLPWRSOMCAE */{SST(0x5D, 0x00, SS_RDEF,
+ "Failure prediction threshold exceeded") },
+/* DTLPWRSOMCAE */{SST(0x5D, 0xFF, SS_RDEF,
+ "Failure prediction threshold exceeded (false)") },
+/* DTLPWRSO CA */{SST(0x5E, 0x00, SS_RDEF,
+ "Low power condition on") },
+/* DTLPWRSO CA */{SST(0x5E, 0x01, SS_RDEF,
+ "Idle condition activated by timer") },
+/* DTLPWRSO CA */{SST(0x5E, 0x02, SS_RDEF,
+ "Standby condition activated by timer") },
+/* DTLPWRSO CA */{SST(0x5E, 0x03, SS_RDEF,
+ "Idle condition activated by command") },
+/* DTLPWRSO CA */{SST(0x5E, 0x04, SS_RDEF,
+ "Standby condition activated by command") },
+/* S */{SST(0x60, 0x00, SS_RDEF,
+ "Lamp failure") },
+/* S */{SST(0x61, 0x00, SS_RDEF,
+ "Video acquisition error") },
+/* S */{SST(0x61, 0x01, SS_RDEF,
+ "Unable to acquire video") },
+/* S */{SST(0x61, 0x02, SS_RDEF,
+ "Out of focus") },
+/* S */{SST(0x62, 0x00, SS_RDEF,
+ "Scan head positioning error") },
+/* R */{SST(0x63, 0x00, SS_RDEF,
+ "End of user area encountered on this track") },
+/* R */{SST(0x63, 0x01, SS_FATAL|ENOSPC,
+ "Packet does not fit in available space") },
+/* R */{SST(0x64, 0x00, SS_RDEF,
+ "Illegal mode for this track") },
+/* R */{SST(0x64, 0x01, SS_RDEF,
+ "Invalid packet size") },
+/* DTLPWRSOMCAE */{SST(0x65, 0x00, SS_RDEF,
+ "Voltage fault") },
+/* S */{SST(0x66, 0x00, SS_RDEF,
+ "Automatic document feeder cover up") },
+/* S */{SST(0x66, 0x01, SS_RDEF,
+ "Automatic document feeder lift up") },
+/* S */{SST(0x66, 0x02, SS_RDEF,
+ "Document jam in automatic document feeder") },
+/* S */{SST(0x66, 0x03, SS_RDEF,
+ "Document miss feed automatic in document feeder") },
+/* A */{SST(0x67, 0x00, SS_RDEF,
+ "Configuration failure") },
+/* A */{SST(0x67, 0x01, SS_RDEF,
+ "Configuration of incapable logical units failed") },
+/* A */{SST(0x67, 0x02, SS_RDEF,
+ "Add logical unit failed") },
+/* A */{SST(0x67, 0x03, SS_RDEF,
+ "Modification of logical unit failed") },
+/* A */{SST(0x67, 0x04, SS_RDEF,
+ "Exchange of logical unit failed") },
+/* A */{SST(0x67, 0x05, SS_RDEF,
+ "Remove of logical unit failed") },
+/* A */{SST(0x67, 0x06, SS_RDEF,
+ "Attachment of logical unit failed") },
+/* A */{SST(0x67, 0x07, SS_RDEF,
+ "Creation of logical unit failed") },
+/* A */{SST(0x68, 0x00, SS_RDEF,
+ "Logical unit not configured") },
+/* A */{SST(0x69, 0x00, SS_RDEF,
+ "Data loss on logical unit") },
+/* A */{SST(0x69, 0x01, SS_RDEF,
+ "Multiple logical unit failures") },
+/* A */{SST(0x69, 0x02, SS_RDEF,
+ "Parity/data mismatch") },
+/* A */{SST(0x6A, 0x00, SS_RDEF,
+ "Informational, refer to log") },
+/* A */{SST(0x6B, 0x00, SS_RDEF,
+ "State change has occurred") },
+/* A */{SST(0x6B, 0x01, SS_RDEF,
+ "Redundancy level got better") },
+/* A */{SST(0x6B, 0x02, SS_RDEF,
+ "Redundancy level got worse") },
+/* A */{SST(0x6C, 0x00, SS_RDEF,
+ "Rebuild failure occurred") },
+/* A */{SST(0x6D, 0x00, SS_RDEF,
+ "Recalculate failure occurred") },
+/* A */{SST(0x6E, 0x00, SS_RDEF,
+ "Command to logical unit failed") },
+/* T */{SST(0x70, 0x00, SS_RDEF,
+ "Decompression exception short: ASCQ = Algorithm ID") },
+/* T */{SST(0x70, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL) }, /* Range 0x00 -> 0xFF */
+/* T */{SST(0x71, 0x00, SS_RDEF,
+ "Decompression exception long: ASCQ = Algorithm ID") },
+/* T */{SST(0x71, 0xFF, SS_RDEF|SSQ_RANGE,
+ NULL) }, /* Range 0x00 -> 0xFF */
+/* R */{SST(0x72, 0x00, SS_RDEF,
+ "Session fixation error") },
+/* R */{SST(0x72, 0x01, SS_RDEF,
+ "Session fixation error writing lead-in") },
+/* R */{SST(0x72, 0x02, SS_RDEF,
+ "Session fixation error writing lead-out") },
+/* R */{SST(0x72, 0x03, SS_RDEF,
+ "Session fixation error - incomplete track in session") },
+/* R */{SST(0x72, 0x04, SS_RDEF,
+ "Empty or partially written reserved track") },
+/* R */{SST(0x73, 0x00, SS_RDEF,
+ "CD control error") },
+/* R */{SST(0x73, 0x01, SS_RDEF,
+ "Power calibration area almost full") },
+/* R */{SST(0x73, 0x02, SS_FATAL|ENOSPC,
+ "Power calibration area is full") },
+/* R */{SST(0x73, 0x03, SS_RDEF,
+ "Power calibration area error") },
+/* R */{SST(0x73, 0x04, SS_RDEF,
+ "Program memory area update failure") },
+/* R */{SST(0x73, 0x05, SS_RDEF,
+ "program memory area is full") }
+};
+
+static const int asc_table_size = sizeof(asc_table)/sizeof(asc_table[0]);
+
+struct asc_key
+{
+ int asc;
+ int ascq;
+};
+
+static int
+ascentrycomp(const void *key, const void *member)
+{
+ int asc;
+ int ascq;
+ const struct asc_table_entry *table_entry;
+
+ asc = ((const struct asc_key *)key)->asc;
+ ascq = ((const struct asc_key *)key)->ascq;
+ table_entry = (const struct asc_table_entry *)member;
+
+ if (asc >= table_entry->asc) {
+
+ if (asc > table_entry->asc)
+ return (1);
+
+ if (ascq <= table_entry->ascq) {
+ /* Check for ranges */
+ if (ascq == table_entry->ascq
+ || ((table_entry->action & SSQ_RANGE) != 0
+ && ascq >= (table_entry - 1)->ascq))
+ return (0);
+ return (-1);
+ }
+ return (1);
+ }
+ return (-1);
+}
+
+static int
+senseentrycomp(const void *key, const void *member)
+{
+ int sense_key;
+ const struct sense_key_table_entry *table_entry;
+
+ sense_key = *((const int *)key);
+ table_entry = (const struct sense_key_table_entry *)member;
+
+ if (sense_key >= table_entry->sense_key) {
+ if (sense_key == table_entry->sense_key)
+ return (0);
+ return (1);
+ }
+ return (-1);
+}
+
+static void
+fetchtableentries(int sense_key, int asc, int ascq,
+ struct scsi_inquiry_data *inq_data,
+ const struct sense_key_table_entry **sense_entry,
+ const struct asc_table_entry **asc_entry)
+{
+ void *match;
+ const struct asc_table_entry *asc_tables[2];
+ const struct sense_key_table_entry *sense_tables[2];
+ struct asc_key asc_ascq;
+ size_t asc_tables_size[2];
+ size_t sense_tables_size[2];
+ int num_asc_tables;
+ int num_sense_tables;
+ int i;
+
+ /* Default to failure */
+ *sense_entry = NULL;
+ *asc_entry = NULL;
+ match = NULL;
+ if (inq_data != NULL)
+ match = cam_quirkmatch((void *)inq_data,
+ (void *)sense_quirk_table,
+ sense_quirk_table_size,
+ sizeof(*sense_quirk_table),
+ aic_inquiry_match);
+
+ if (match != NULL) {
+ struct scsi_sense_quirk_entry *quirk;
+
+ quirk = (struct scsi_sense_quirk_entry *)match;
+ asc_tables[0] = quirk->asc_info;
+ asc_tables_size[0] = quirk->num_ascs;
+ asc_tables[1] = asc_table;
+ asc_tables_size[1] = asc_table_size;
+ num_asc_tables = 2;
+ sense_tables[0] = quirk->sense_key_info;
+ sense_tables_size[0] = quirk->num_sense_keys;
+ sense_tables[1] = sense_key_table;
+ sense_tables_size[1] = sense_key_table_size;
+ num_sense_tables = 2;
+ } else {
+ asc_tables[0] = asc_table;
+ asc_tables_size[0] = asc_table_size;
+ num_asc_tables = 1;
+ sense_tables[0] = sense_key_table;
+ sense_tables_size[0] = sense_key_table_size;
+ num_sense_tables = 1;
+ }
+
+ asc_ascq.asc = asc;
+ asc_ascq.ascq = ascq;
+ for (i = 0; i < num_asc_tables; i++) {
+ void *found_entry;
+
+ found_entry = scsibsearch(&asc_ascq, asc_tables[i],
+ asc_tables_size[i],
+ sizeof(**asc_tables),
+ ascentrycomp);
+
+ if (found_entry) {
+ *asc_entry = (struct asc_table_entry *)found_entry;
+ break;
+ }
+ }
+
+ for (i = 0; i < num_sense_tables; i++) {
+ void *found_entry;
+
+ found_entry = scsibsearch(&sense_key, sense_tables[i],
+ sense_tables_size[i],
+ sizeof(**sense_tables),
+ senseentrycomp);
+
+ if (found_entry) {
+ *sense_entry =
+ (struct sense_key_table_entry *)found_entry;
+ break;
+ }
+ }
+}
+
+static void *
+scsibsearch(const void *key, const void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *))
+{
+ const void *entry;
+ u_int l;
+ u_int u;
+ u_int m;
+
+ l = -1;
+ u = nmemb;
+ while (l + 1 != u) {
+ m = (l + u) / 2;
+ entry = base + m * size;
+ if (compar(key, entry) > 0)
+ l = m;
+ else
+ u = m;
+ }
+
+ entry = base + u * size;
+ if (u == nmemb
+ || compar(key, entry) != 0)
+ return (NULL);
+
+ return ((void *)entry);
+}
+
+/*
+ * Compare string with pattern, returning 0 on match.
+ * Short pattern matches trailing blanks in name,
+ * wildcard '*' in pattern matches rest of name,
+ * wildcard '?' matches a single non-space character.
+ */
+static int
+cam_strmatch(const uint8_t *str, const uint8_t *pattern, int str_len)
+{
+
+ while (*pattern != '\0'&& str_len > 0) {
+
+ if (*pattern == '*') {
+ return (0);
+ }
+ if ((*pattern != *str)
+ && (*pattern != '?' || *str == ' ')) {
+ return (1);
+ }
+ pattern++;
+ str++;
+ str_len--;
+ }
+ while (str_len > 0 && *str++ == ' ')
+ str_len--;
+
+ return (str_len);
+}
+
+static caddr_t
+cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries,
+ int entry_size, cam_quirkmatch_t *comp_func)
+{
+ for (; num_entries > 0; num_entries--, quirk_table += entry_size) {
+ if ((*comp_func)(target, quirk_table) == 0)
+ return (quirk_table);
+ }
+ return (NULL);
+}
+
+void
+aic_sense_desc(int sense_key, int asc, int ascq,
+ struct scsi_inquiry_data *inq_data,
+ const char **sense_key_desc, const char **asc_desc)
+{
+ const struct asc_table_entry *asc_entry;
+ const struct sense_key_table_entry *sense_entry;
+
+ fetchtableentries(sense_key, asc, ascq,
+ inq_data,
+ &sense_entry,
+ &asc_entry);
+
+ *sense_key_desc = sense_entry->desc;
+
+ if (asc_entry != NULL)
+ *asc_desc = asc_entry->desc;
+ else if (asc >= 0x80 && asc <= 0xff)
+ *asc_desc = "Vendor Specific ASC";
+ else if (ascq >= 0x80 && ascq <= 0xff)
+ *asc_desc = "Vendor Specific ASCQ";
+ else
+ *asc_desc = "Reserved ASC/ASCQ pair";
+}
+
+/*
+ * Given sense and device type information, return the appropriate action.
+ * If we do not understand the specific error as identified by the ASC/ASCQ
+ * pair, fall back on the more generic actions derived from the sense key.
+ */
+aic_sense_action
+aic_sense_error_action(struct scsi_sense_data *sense_data,
+ struct scsi_inquiry_data *inq_data, uint32_t sense_flags)
+{
+ const struct asc_table_entry *asc_entry;
+ const struct sense_key_table_entry *sense_entry;
+ int error_code, sense_key, asc, ascq;
+ aic_sense_action action;
+
+ scsi_extract_sense(sense_data, &error_code, &sense_key, &asc, &ascq);
+
+ if (error_code == SSD_DEFERRED_ERROR) {
+ /*
+ * XXX dufault@FreeBSD.org
+ * This error doesn't relate to the command associated
+ * with this request sense. A deferred error is an error
+ * for a command that has already returned GOOD status
+ * (see SCSI2 8.2.14.2).
+ *
+ * By my reading of that section, it looks like the current
+ * command has been cancelled, we should now clean things up
+ * (hopefully recovering any lost data) and then retry the
+ * current command. There are two easy choices, both wrong:
+ *
+ * 1. Drop through (like we had been doing), thus treating
+ * this as if the error were for the current command and
+ * return and stop the current command.
+ *
+ * 2. Issue a retry (like I made it do) thus hopefully
+ * recovering the current transfer, and ignoring the
+ * fact that we've dropped a command.
+ *
+ * These should probably be handled in a device specific
+ * sense handler or punted back up to a user mode daemon
+ */
+ action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE;
+ } else {
+ fetchtableentries(sense_key, asc, ascq,
+ inq_data,
+ &sense_entry,
+ &asc_entry);
+
+ /*
+ * Override the 'No additional Sense' entry (0,0)
+ * with the error action of the sense key.
+ */
+ if (asc_entry != NULL
+ && (asc != 0 || ascq != 0))
+ action = asc_entry->action;
+ else
+ action = sense_entry->action;
+
+ if (sense_key == SSD_KEY_RECOVERED_ERROR) {
+ /*
+ * The action succeeded but the device wants
+ * the user to know that some recovery action
+ * was required.
+ */
+ action &= ~(SS_MASK|SSQ_MASK|SS_ERRMASK);
+ action |= SS_NOP|SSQ_PRINT_SENSE;
+ } else if (sense_key == SSD_KEY_ILLEGAL_REQUEST) {
+ if ((sense_flags & SF_QUIET_IR) != 0)
+ action &= ~SSQ_PRINT_SENSE;
+ } else if (sense_key == SSD_KEY_UNIT_ATTENTION) {
+ if ((sense_flags & SF_RETRY_UA) != 0
+ && (action & SS_MASK) == SS_FAIL) {
+ action &= ~(SS_MASK|SSQ_MASK);
+ action |= SS_RETRY|SSQ_DECREMENT_COUNT|
+ SSQ_PRINT_SENSE;
+ }
+ }
+ }
+
+ if ((sense_flags & SF_PRINT_ALWAYS) != 0)
+ action |= SSQ_PRINT_SENSE;
+ else if ((sense_flags & SF_NO_PRINT) != 0)
+ action &= ~SSQ_PRINT_SENSE;
+
+ return (action);
+}
+
+/*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+int
+aic_inquiry_match(caddr_t inqbuffer, caddr_t table_entry)
+{
+ struct scsi_inquiry_pattern *entry;
+ struct scsi_inquiry_data *inq;
+
+ entry = (struct scsi_inquiry_pattern *)table_entry;
+ inq = (struct scsi_inquiry_data *)inqbuffer;
+
+ if (((SID_TYPE(inq) == entry->type)
+ || (entry->type == T_ANY))
+ && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE
+ : entry->media_type & SIP_MEDIA_FIXED)
+ && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0)
+ && (cam_strmatch(inq->product, entry->product,
+ sizeof(inq->product)) == 0)
+ && (cam_strmatch(inq->revision, entry->revision,
+ sizeof(inq->revision)) == 0)) {
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static struct {
+ u_int period_factor;
+ u_int period; /* in 100ths of ns */
+} scsi_syncrates[] = {
+ { 0x08, 625 }, /* FAST-160 */
+ { 0x09, 1250 }, /* FAST-80 */
+ { 0x0a, 2500 }, /* FAST-40 40MHz */
+ { 0x0b, 3030 }, /* FAST-40 33MHz */
+ { 0x0c, 5000 } /* FAST-20 */
+};
+
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+u_int
+aic_calc_syncsrate(u_int period_factor)
+{
+ int i;
+ int num_syncrates;
+
+ num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+ /* See if the period is in the "exception" table */
+ for (i = 0; i < num_syncrates; i++) {
+
+ if (period_factor == scsi_syncrates[i].period_factor) {
+ /* Period in kHz */
+ return (100000000 / scsi_syncrates[i].period);
+ }
+ }
+
+ /*
+ * Wasn't in the table, so use the standard
+ * 4 times conversion.
+ */
+ return (10000000 / (period_factor * 4 * 10));
+}
+
+/*
+ * Return speed in KB/s.
+ */
+u_int
+aic_calc_speed(u_int width, u_int period, u_int offset, u_int min_rate)
+{
+ u_int freq;
+
+ if (offset != 0 && period < min_rate)
+ freq = aic_calc_syncsrate(period);
+ else
+ /* Roughly 3.3MB/s for async */
+ freq = 3300;
+ freq <<= width;
+ return (freq);
+}
+
+uint32_t
+aic_error_action(struct scsi_cmnd *cmd, struct scsi_inquiry_data *inq_data,
+ cam_status status, u_int scsi_status)
+{
+ aic_sense_action err_action;
+ int sense;
+
+ sense = (cmd->result >> 24) == DRIVER_SENSE;
+
+ switch (status) {
+ case CAM_REQ_CMP:
+ err_action = SS_NOP;
+ break;
+ case CAM_AUTOSENSE_FAIL:
+ case CAM_SCSI_STATUS_ERROR:
+
+ switch (scsi_status) {
+ case SCSI_STATUS_OK:
+ case SCSI_STATUS_COND_MET:
+ case SCSI_STATUS_INTERMED:
+ case SCSI_STATUS_INTERMED_COND_MET:
+ err_action = SS_NOP;
+ break;
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+ if (sense != 0) {
+ struct scsi_sense_data *sense;
+
+ sense = (struct scsi_sense_data *)
+ &cmd->sense_buffer;
+ err_action =
+ aic_sense_error_action(sense, inq_data, 0);
+
+ } else {
+ err_action = SS_RETRY|SSQ_FALLBACK
+ | SSQ_DECREMENT_COUNT|EIO;
+ }
+ break;
+ case SCSI_STATUS_QUEUE_FULL:
+ case SCSI_STATUS_BUSY:
+ err_action = SS_RETRY|SSQ_DELAY|SSQ_MANY
+ | SSQ_DECREMENT_COUNT|EBUSY;
+ break;
+ case SCSI_STATUS_RESERV_CONFLICT:
+ default:
+ err_action = SS_FAIL|EBUSY;
+ break;
+ }
+ break;
+ case CAM_CMD_TIMEOUT:
+ case CAM_REQ_CMP_ERR:
+ case CAM_UNEXP_BUSFREE:
+ case CAM_UNCOR_PARITY:
+ case CAM_DATA_RUN_ERR:
+ err_action = SS_RETRY|SSQ_FALLBACK|EIO;
+ break;
+ case CAM_UA_ABORT:
+ case CAM_UA_TERMIO:
+ case CAM_MSG_REJECT_REC:
+ case CAM_SEL_TIMEOUT:
+ err_action = SS_FAIL|EIO;
+ break;
+ case CAM_REQ_INVALID:
+ case CAM_PATH_INVALID:
+ case CAM_DEV_NOT_THERE:
+ case CAM_NO_HBA:
+ case CAM_PROVIDE_FAIL:
+ case CAM_REQ_TOO_BIG:
+ case CAM_RESRC_UNAVAIL:
+ case CAM_BUSY:
+ default:
+ /* panic?? These should never occur in our application. */
+ err_action = SS_FAIL|EIO;
+ break;
+ case CAM_SCSI_BUS_RESET:
+ case CAM_BDR_SENT:
+ case CAM_REQUEUE_REQ:
+ /* Unconditional requeue */
+ err_action = SS_RETRY;
+ break;
+ }
+
+ return (err_action);
+}
+
+char *
+aic_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth,
+ aic_option_callback_t *callback, u_long callback_arg)
+{
+ char *tok_end;
+ char *tok_end2;
+ int i;
+ int instance;
+ int targ;
+ int done;
+ char tok_list[] = {'.', ',', '{', '}', '\0'};
+
+ /* All options use a ':' name/arg separator */
+ if (*opt_arg != ':')
+ return (opt_arg);
+ opt_arg++;
+ instance = -1;
+ targ = -1;
+ done = FALSE;
+ /*
+ * Restore separator that may be in
+ * the middle of our option argument.
+ */
+ tok_end = strchr(opt_arg, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while (!done) {
+ switch (*opt_arg) {
+ case '{':
+ if (instance == -1) {
+ instance = 0;
+ } else {
+ if (depth > 1) {
+ if (targ == -1)
+ targ = 0;
+ } else {
+ printf("Malformed Option %s\n",
+ opt_name);
+ done = TRUE;
+ }
+ }
+ opt_arg++;
+ break;
+ case '}':
+ if (targ != -1)
+ targ = -1;
+ else if (instance != -1)
+ instance = -1;
+ opt_arg++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (targ >= 0)
+ targ++;
+ else if (instance >= 0)
+ instance++;
+ opt_arg++;
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ tok_end = end;
+ for (i = 0; tok_list[i]; i++) {
+ tok_end2 = strchr(opt_arg, tok_list[i]);
+ if ((tok_end2) && (tok_end2 < tok_end))
+ tok_end = tok_end2;
+ }
+ callback(callback_arg, instance, targ,
+ simple_strtol(opt_arg, NULL, 0));
+ opt_arg = tok_end;
+ break;
+ }
+ }
+ return (opt_arg);
+}
diff --git a/drivers/scsi/aic7xxx/aiclib.h b/drivers/scsi/aic7xxx/aiclib.h
new file mode 100644
index 000000000000..bfe6f954d3c4
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.h
@@ -0,0 +1,1085 @@
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_all.h,v 1.21 2002/10/08 17:12:44 ken Exp $
+ *
+ * Copyright (c) 2003 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ */
+
+#ifndef _AICLIB_H
+#define _AICLIB_H
+
+/*
+ * Linux Interrupt Support.
+ */
+#ifndef IRQ_RETVAL
+typedef void irqreturn_t;
+#define IRQ_RETVAL(x)
+#endif
+
+/*
+ * SCSI command format
+ */
+
+/*
+ * Define dome bits that are in ALL (or a lot of) scsi commands
+ */
+#define SCSI_CTL_LINK 0x01
+#define SCSI_CTL_FLAG 0x02
+#define SCSI_CTL_VENDOR 0xC0
+#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */
+#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */
+
+#define SCSI_MAX_CDBLEN 16 /*
+ * 16 byte commands are in the
+ * SCSI-3 spec
+ */
+/* 6byte CDBs special case 0 length to be 256 */
+#define SCSI_CDB6_LEN(len) ((len) == 0 ? 256 : len)
+
+/*
+ * This type defines actions to be taken when a particular sense code is
+ * received. Right now, these flags are only defined to take up 16 bits,
+ * but can be expanded in the future if necessary.
+ */
+typedef enum {
+ SS_NOP = 0x000000, /* Do nothing */
+ SS_RETRY = 0x010000, /* Retry the command */
+ SS_FAIL = 0x020000, /* Bail out */
+ SS_START = 0x030000, /* Send a Start Unit command to the device,
+ * then retry the original command.
+ */
+ SS_TUR = 0x040000, /* Send a Test Unit Ready command to the
+ * device, then retry the original command.
+ */
+ SS_REQSENSE = 0x050000, /* Send a RequestSense command to the
+ * device, then retry the original command.
+ */
+ SS_INQ_REFRESH = 0x060000,
+ SS_MASK = 0xff0000
+} aic_sense_action;
+
+typedef enum {
+ SSQ_NONE = 0x0000,
+ SSQ_DECREMENT_COUNT = 0x0100, /* Decrement the retry count */
+ SSQ_MANY = 0x0200, /* send lots of recovery commands */
+ SSQ_RANGE = 0x0400, /*
+ * This table entry represents the
+ * end of a range of ASCQs that
+ * have identical error actions
+ * and text.
+ */
+ SSQ_PRINT_SENSE = 0x0800,
+ SSQ_DELAY = 0x1000, /* Delay before retry. */
+ SSQ_DELAY_RANDOM = 0x2000, /* Randomized delay before retry. */
+ SSQ_FALLBACK = 0x4000, /* Do a speed fallback to recover */
+ SSQ_MASK = 0xff00
+} aic_sense_action_qualifier;
+
+/* Mask for error status values */
+#define SS_ERRMASK 0xff
+
+/* The default, retyable, error action */
+#define SS_RDEF SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE|EIO
+
+/* The retyable, error action, with table specified error code */
+#define SS_RET SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE
+
+/* Fatal error action, with table specified error code */
+#define SS_FATAL SS_FAIL|SSQ_PRINT_SENSE
+
+struct scsi_generic
+{
+ uint8_t opcode;
+ uint8_t bytes[11];
+};
+
+struct scsi_request_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_test_unit_ready
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[3];
+ uint8_t control;
+};
+
+struct scsi_send_diag
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SSD_UOL 0x01
+#define SSD_DOL 0x02
+#define SSD_SELFTEST 0x04
+#define SSD_PF 0x10
+ uint8_t unused[1];
+ uint8_t paramlen[2];
+ uint8_t control;
+};
+
+struct scsi_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_inquiry
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SI_EVPD 0x01
+ uint8_t page_code;
+ uint8_t reserved;
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_mode_sense_6
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SMS_DBD 0x08
+ uint8_t page;
+#define SMS_PAGE_CODE 0x3F
+#define SMS_VENDOR_SPECIFIC_PAGE 0x00
+#define SMS_DISCONNECT_RECONNECT_PAGE 0x02
+#define SMS_PERIPHERAL_DEVICE_PAGE 0x09
+#define SMS_CONTROL_MODE_PAGE 0x0A
+#define SMS_ALL_PAGES_PAGE 0x3F
+#define SMS_PAGE_CTRL_MASK 0xC0
+#define SMS_PAGE_CTRL_CURRENT 0x00
+#define SMS_PAGE_CTRL_CHANGEABLE 0x40
+#define SMS_PAGE_CTRL_DEFAULT 0x80
+#define SMS_PAGE_CTRL_SAVED 0xC0
+ uint8_t unused;
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_mode_sense_10
+{
+ uint8_t opcode;
+ uint8_t byte2; /* same bits as small version */
+ uint8_t page; /* same bits as small version */
+ uint8_t unused[4];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_mode_select_6
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_mode_select_10
+{
+ uint8_t opcode;
+ uint8_t byte2; /* same bits as small version */
+ uint8_t unused[5];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+/*
+ * When sending a mode select to a tape drive, the medium type must be 0.
+ */
+struct scsi_mode_hdr_6
+{
+ uint8_t datalen;
+ uint8_t medium_type;
+ uint8_t dev_specific;
+ uint8_t block_descr_len;
+};
+
+struct scsi_mode_hdr_10
+{
+ uint8_t datalen[2];
+ uint8_t medium_type;
+ uint8_t dev_specific;
+ uint8_t reserved[2];
+ uint8_t block_descr_len[2];
+};
+
+struct scsi_mode_block_descr
+{
+ uint8_t density_code;
+ uint8_t num_blocks[3];
+ uint8_t reserved;
+ uint8_t block_len[3];
+};
+
+struct scsi_log_sense
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SLS_SP 0x01
+#define SLS_PPC 0x02
+ uint8_t page;
+#define SLS_PAGE_CODE 0x3F
+#define SLS_ALL_PAGES_PAGE 0x00
+#define SLS_OVERRUN_PAGE 0x01
+#define SLS_ERROR_WRITE_PAGE 0x02
+#define SLS_ERROR_READ_PAGE 0x03
+#define SLS_ERROR_READREVERSE_PAGE 0x04
+#define SLS_ERROR_VERIFY_PAGE 0x05
+#define SLS_ERROR_NONMEDIUM_PAGE 0x06
+#define SLS_ERROR_LASTN_PAGE 0x07
+#define SLS_PAGE_CTRL_MASK 0xC0
+#define SLS_PAGE_CTRL_THRESHOLD 0x00
+#define SLS_PAGE_CTRL_CUMULATIVE 0x40
+#define SLS_PAGE_CTRL_THRESH_DEFAULT 0x80
+#define SLS_PAGE_CTRL_CUMUL_DEFAULT 0xC0
+ uint8_t reserved[2];
+ uint8_t paramptr[2];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_log_select
+{
+ uint8_t opcode;
+ uint8_t byte2;
+/* SLS_SP 0x01 */
+#define SLS_PCR 0x02
+ uint8_t page;
+/* SLS_PAGE_CTRL_MASK 0xC0 */
+/* SLS_PAGE_CTRL_THRESHOLD 0x00 */
+/* SLS_PAGE_CTRL_CUMULATIVE 0x40 */
+/* SLS_PAGE_CTRL_THRESH_DEFAULT 0x80 */
+/* SLS_PAGE_CTRL_CUMUL_DEFAULT 0xC0 */
+ uint8_t reserved[4];
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_log_header
+{
+ uint8_t page;
+ uint8_t reserved;
+ uint8_t datalen[2];
+};
+
+struct scsi_log_param_header {
+ uint8_t param_code[2];
+ uint8_t param_control;
+#define SLP_LP 0x01
+#define SLP_LBIN 0x02
+#define SLP_TMC_MASK 0x0C
+#define SLP_TMC_ALWAYS 0x00
+#define SLP_TMC_EQUAL 0x04
+#define SLP_TMC_NOTEQUAL 0x08
+#define SLP_TMC_GREATER 0x0C
+#define SLP_ETC 0x10
+#define SLP_TSD 0x20
+#define SLP_DS 0x40
+#define SLP_DU 0x80
+ uint8_t param_len;
+};
+
+struct scsi_control_page {
+ uint8_t page_code;
+ uint8_t page_length;
+ uint8_t rlec;
+#define SCB_RLEC 0x01 /*Report Log Exception Cond*/
+ uint8_t queue_flags;
+#define SCP_QUEUE_ALG_MASK 0xF0
+#define SCP_QUEUE_ALG_RESTRICTED 0x00
+#define SCP_QUEUE_ALG_UNRESTRICTED 0x10
+#define SCP_QUEUE_ERR 0x02 /*Queued I/O aborted for CACs*/
+#define SCP_QUEUE_DQUE 0x01 /*Queued I/O disabled*/
+ uint8_t eca_and_aen;
+#define SCP_EECA 0x80 /*Enable Extended CA*/
+#define SCP_RAENP 0x04 /*Ready AEN Permission*/
+#define SCP_UAAENP 0x02 /*UA AEN Permission*/
+#define SCP_EAENP 0x01 /*Error AEN Permission*/
+ uint8_t reserved;
+ uint8_t aen_holdoff_period[2];
+};
+
+struct scsi_reserve
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_release
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_prevent
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[2];
+ uint8_t how;
+ uint8_t control;
+};
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+
+struct scsi_sync_cache
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t begin_lba[4];
+ uint8_t reserved;
+ uint8_t lb_count[2];
+ uint8_t control;
+};
+
+
+struct scsi_changedef
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused1;
+ uint8_t how;
+ uint8_t unused[4];
+ uint8_t datalen;
+ uint8_t control;
+};
+
+struct scsi_read_buffer
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define RWB_MODE 0x07
+#define RWB_MODE_HDR_DATA 0x00
+#define RWB_MODE_DATA 0x02
+#define RWB_MODE_DOWNLOAD 0x04
+#define RWB_MODE_DOWNLOAD_SAVE 0x05
+ uint8_t buffer_id;
+ uint8_t offset[3];
+ uint8_t length[3];
+ uint8_t control;
+};
+
+struct scsi_write_buffer
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t buffer_id;
+ uint8_t offset[3];
+ uint8_t length[3];
+ uint8_t control;
+};
+
+struct scsi_rw_6
+{
+ uint8_t opcode;
+ uint8_t addr[3];
+/* only 5 bits are valid in the MSB address byte */
+#define SRW_TOPADDR 0x1F
+ uint8_t length;
+ uint8_t control;
+};
+
+struct scsi_rw_10
+{
+ uint8_t opcode;
+#define SRW10_RELADDR 0x01
+#define SRW10_FUA 0x08
+#define SRW10_DPO 0x10
+ uint8_t byte2;
+ uint8_t addr[4];
+ uint8_t reserved;
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_rw_12
+{
+ uint8_t opcode;
+#define SRW12_RELADDR 0x01
+#define SRW12_FUA 0x08
+#define SRW12_DPO 0x10
+ uint8_t byte2;
+ uint8_t addr[4];
+ uint8_t length[4];
+ uint8_t reserved;
+ uint8_t control;
+};
+
+struct scsi_start_stop_unit
+{
+ uint8_t opcode;
+ uint8_t byte2;
+#define SSS_IMMED 0x01
+ uint8_t reserved[2];
+ uint8_t how;
+#define SSS_START 0x01
+#define SSS_LOEJ 0x02
+ uint8_t control;
+};
+
+#define SC_SCSI_1 0x01
+#define SC_SCSI_2 0x03
+
+/*
+ * Opcodes
+ */
+
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define READ_6 0x08
+#define WRITE_6 0x0a
+#define INQUIRY 0x12
+#define MODE_SELECT_6 0x15
+#define MODE_SENSE_6 0x1a
+#define START_STOP_UNIT 0x1b
+#define START_STOP 0x1b
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define RECEIVE_DIAGNOSTIC 0x1c
+#define SEND_DIAGNOSTIC 0x1d
+#define PREVENT_ALLOW 0x1e
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define WRITE_10 0x2a
+#define POSITION_TO_ELEMENT 0x2b
+#define SYNCHRONIZE_CACHE 0x35
+#define WRITE_BUFFER 0x3b
+#define READ_BUFFER 0x3c
+#define CHANGE_DEFINITION 0x40
+#define LOG_SELECT 0x4c
+#define LOG_SENSE 0x4d
+#ifdef XXXCAM
+#define MODE_SENSE_10 0x5A
+#endif
+#define MODE_SELECT_10 0x55
+#define MOVE_MEDIUM 0xa5
+#define READ_12 0xa8
+#define WRITE_12 0xaa
+#define READ_ELEMENT_STATUS 0xb8
+
+
+/*
+ * Device Types
+ */
+#define T_DIRECT 0x00
+#define T_SEQUENTIAL 0x01
+#define T_PRINTER 0x02
+#define T_PROCESSOR 0x03
+#define T_WORM 0x04
+#define T_CDROM 0x05
+#define T_SCANNER 0x06
+#define T_OPTICAL 0x07
+#define T_CHANGER 0x08
+#define T_COMM 0x09
+#define T_ASC0 0x0a
+#define T_ASC1 0x0b
+#define T_STORARRAY 0x0c
+#define T_ENCLOSURE 0x0d
+#define T_RBC 0x0e
+#define T_OCRW 0x0f
+#define T_NODEVICE 0x1F
+#define T_ANY 0xFF /* Used in Quirk table matches */
+
+#define T_REMOV 1
+#define T_FIXED 0
+
+/*
+ * This length is the initial inquiry length used by the probe code, as
+ * well as the legnth necessary for aic_print_inquiry() to function
+ * correctly. If either use requires a different length in the future,
+ * the two values should be de-coupled.
+ */
+#define SHORT_INQUIRY_LENGTH 36
+
+struct scsi_inquiry_data
+{
+ uint8_t device;
+#define SID_TYPE(inq_data) ((inq_data)->device & 0x1f)
+#define SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5)
+#define SID_QUAL_LU_CONNECTED 0x00 /*
+ * The specified peripheral device
+ * type is currently connected to
+ * logical unit. If the target cannot
+ * determine whether or not a physical
+ * device is currently connected, it
+ * shall also use this peripheral
+ * qualifier when returning the INQUIRY
+ * data. This peripheral qualifier
+ * does not mean that the device is
+ * ready for access by the initiator.
+ */
+#define SID_QUAL_LU_OFFLINE 0x01 /*
+ * The target is capable of supporting
+ * the specified peripheral device type
+ * on this logical unit; however, the
+ * physical device is not currently
+ * connected to this logical unit.
+ */
+#define SID_QUAL_RSVD 0x02
+#define SID_QUAL_BAD_LU 0x03 /*
+ * The target is not capable of
+ * supporting a physical device on
+ * this logical unit. For this
+ * peripheral qualifier the peripheral
+ * device type shall be set to 1Fh to
+ * provide compatibility with previous
+ * versions of SCSI. All other
+ * peripheral device type values are
+ * reserved for this peripheral
+ * qualifier.
+ */
+#define SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
+ uint8_t dev_qual2;
+#define SID_QUAL2 0x7F
+#define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+ uint8_t version;
+#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
+#define SCSI_REV_0 0
+#define SCSI_REV_CCS 1
+#define SCSI_REV_2 2
+#define SCSI_REV_SPC 3
+#define SCSI_REV_SPC2 4
+
+#define SID_ECMA 0x38
+#define SID_ISO 0xC0
+ uint8_t response_format;
+#define SID_AENC 0x80
+#define SID_TrmIOP 0x40
+ uint8_t additional_length;
+ uint8_t reserved[2];
+ uint8_t flags;
+#define SID_SftRe 0x01
+#define SID_CmdQue 0x02
+#define SID_Linked 0x08
+#define SID_Sync 0x10
+#define SID_WBus16 0x20
+#define SID_WBus32 0x40
+#define SID_RelAdr 0x80
+#define SID_VENDOR_SIZE 8
+ char vendor[SID_VENDOR_SIZE];
+#define SID_PRODUCT_SIZE 16
+ char product[SID_PRODUCT_SIZE];
+#define SID_REVISION_SIZE 4
+ char revision[SID_REVISION_SIZE];
+ /*
+ * The following fields were taken from SCSI Primary Commands - 2
+ * (SPC-2) Revision 14, Dated 11 November 1999
+ */
+#define SID_VENDOR_SPECIFIC_0_SIZE 20
+ uint8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE];
+ /*
+ * An extension of SCSI Parallel Specific Values
+ */
+#define SID_SPI_IUS 0x01
+#define SID_SPI_QAS 0x02
+#define SID_SPI_CLOCK_ST 0x00
+#define SID_SPI_CLOCK_DT 0x04
+#define SID_SPI_CLOCK_DT_ST 0x0C
+#define SID_SPI_MASK 0x0F
+ uint8_t spi3data;
+ uint8_t reserved2;
+ /*
+ * Version Descriptors, stored 2 byte values.
+ */
+ uint8_t version1[2];
+ uint8_t version2[2];
+ uint8_t version3[2];
+ uint8_t version4[2];
+ uint8_t version5[2];
+ uint8_t version6[2];
+ uint8_t version7[2];
+ uint8_t version8[2];
+
+ uint8_t reserved3[22];
+
+#define SID_VENDOR_SPECIFIC_1_SIZE 160
+ uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
+};
+
+struct scsi_vpd_unit_serial_number
+{
+ uint8_t device;
+ uint8_t page_code;
+#define SVPD_UNIT_SERIAL_NUMBER 0x80
+ uint8_t reserved;
+ uint8_t length; /* serial number length */
+#define SVPD_SERIAL_NUM_SIZE 251
+ uint8_t serial_num[SVPD_SERIAL_NUM_SIZE];
+};
+
+struct scsi_read_capacity
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t addr[4];
+ uint8_t unused[3];
+ uint8_t control;
+};
+
+struct scsi_read_capacity_data
+{
+ uint8_t addr[4];
+ uint8_t length[4];
+};
+
+struct scsi_report_luns
+{
+ uint8_t opcode;
+ uint8_t byte2;
+ uint8_t unused[3];
+ uint8_t addr[4];
+ uint8_t control;
+};
+
+struct scsi_report_luns_data {
+ uint8_t length[4]; /* length of LUN inventory, in bytes */
+ uint8_t reserved[4]; /* unused */
+ /*
+ * LUN inventory- we only support the type zero form for now.
+ */
+ struct {
+ uint8_t lundata[8];
+ } luns[1];
+};
+#define RPL_LUNDATA_ATYP_MASK 0xc0 /* MBZ for type 0 lun */
+#define RPL_LUNDATA_T0LUN 1 /* @ lundata[1] */
+
+
+struct scsi_sense_data
+{
+ uint8_t error_code;
+#define SSD_ERRCODE 0x7F
+#define SSD_CURRENT_ERROR 0x70
+#define SSD_DEFERRED_ERROR 0x71
+#define SSD_ERRCODE_VALID 0x80
+ uint8_t segment;
+ uint8_t flags;
+#define SSD_KEY 0x0F
+#define SSD_KEY_NO_SENSE 0x00
+#define SSD_KEY_RECOVERED_ERROR 0x01
+#define SSD_KEY_NOT_READY 0x02
+#define SSD_KEY_MEDIUM_ERROR 0x03
+#define SSD_KEY_HARDWARE_ERROR 0x04
+#define SSD_KEY_ILLEGAL_REQUEST 0x05
+#define SSD_KEY_UNIT_ATTENTION 0x06
+#define SSD_KEY_DATA_PROTECT 0x07
+#define SSD_KEY_BLANK_CHECK 0x08
+#define SSD_KEY_Vendor_Specific 0x09
+#define SSD_KEY_COPY_ABORTED 0x0a
+#define SSD_KEY_ABORTED_COMMAND 0x0b
+#define SSD_KEY_EQUAL 0x0c
+#define SSD_KEY_VOLUME_OVERFLOW 0x0d
+#define SSD_KEY_MISCOMPARE 0x0e
+#define SSD_KEY_RESERVED 0x0f
+#define SSD_ILI 0x20
+#define SSD_EOM 0x40
+#define SSD_FILEMARK 0x80
+ uint8_t info[4];
+ uint8_t extra_len;
+ uint8_t cmd_spec_info[4];
+ uint8_t add_sense_code;
+ uint8_t add_sense_code_qual;
+ uint8_t fru;
+ uint8_t sense_key_spec[3];
+#define SSD_SCS_VALID 0x80
+#define SSD_FIELDPTR_CMD 0x40
+#define SSD_BITPTR_VALID 0x08
+#define SSD_BITPTR_VALUE 0x07
+#define SSD_MIN_SIZE 18
+ uint8_t extra_bytes[14];
+#define SSD_FULL_SIZE sizeof(struct scsi_sense_data)
+};
+
+struct scsi_mode_header_6
+{
+ uint8_t data_length; /* Sense data length */
+ uint8_t medium_type;
+ uint8_t dev_spec;
+ uint8_t blk_desc_len;
+};
+
+struct scsi_mode_header_10
+{
+ uint8_t data_length[2];/* Sense data length */
+ uint8_t medium_type;
+ uint8_t dev_spec;
+ uint8_t unused[2];
+ uint8_t blk_desc_len[2];
+};
+
+struct scsi_mode_page_header
+{
+ uint8_t page_code;
+ uint8_t page_length;
+};
+
+struct scsi_mode_blk_desc
+{
+ uint8_t density;
+ uint8_t nblocks[3];
+ uint8_t reserved;
+ uint8_t blklen[3];
+};
+
+#define SCSI_DEFAULT_DENSITY 0x00 /* use 'default' density */
+#define SCSI_SAME_DENSITY 0x7f /* use 'same' density- >= SCSI-2 only */
+
+
+/*
+ * Status Byte
+ */
+#define SCSI_STATUS_OK 0x00
+#define SCSI_STATUS_CHECK_COND 0x02
+#define SCSI_STATUS_COND_MET 0x04
+#define SCSI_STATUS_BUSY 0x08
+#define SCSI_STATUS_INTERMED 0x10
+#define SCSI_STATUS_INTERMED_COND_MET 0x14
+#define SCSI_STATUS_RESERV_CONFLICT 0x18
+#define SCSI_STATUS_CMD_TERMINATED 0x22 /* Obsolete in SAM-2 */
+#define SCSI_STATUS_QUEUE_FULL 0x28
+#define SCSI_STATUS_ACA_ACTIVE 0x30
+#define SCSI_STATUS_TASK_ABORTED 0x40
+
+struct scsi_inquiry_pattern {
+ uint8_t type;
+ uint8_t media_type;
+#define SIP_MEDIA_REMOVABLE 0x01
+#define SIP_MEDIA_FIXED 0x02
+ const char *vendor;
+ const char *product;
+ const char *revision;
+};
+
+struct scsi_static_inquiry_pattern {
+ uint8_t type;
+ uint8_t media_type;
+ char vendor[SID_VENDOR_SIZE+1];
+ char product[SID_PRODUCT_SIZE+1];
+ char revision[SID_REVISION_SIZE+1];
+};
+
+struct scsi_sense_quirk_entry {
+ struct scsi_inquiry_pattern inq_pat;
+ int num_sense_keys;
+ int num_ascs;
+ struct sense_key_table_entry *sense_key_info;
+ struct asc_table_entry *asc_info;
+};
+
+struct sense_key_table_entry {
+ uint8_t sense_key;
+ uint32_t action;
+ const char *desc;
+};
+
+struct asc_table_entry {
+ uint8_t asc;
+ uint8_t ascq;
+ uint32_t action;
+ const char *desc;
+};
+
+struct op_table_entry {
+ uint8_t opcode;
+ uint16_t opmask;
+ const char *desc;
+};
+
+struct scsi_op_quirk_entry {
+ struct scsi_inquiry_pattern inq_pat;
+ int num_ops;
+ struct op_table_entry *op_table;
+};
+
+typedef enum {
+ SSS_FLAG_NONE = 0x00,
+ SSS_FLAG_PRINT_COMMAND = 0x01
+} scsi_sense_string_flags;
+
+extern const char *scsi_sense_key_text[];
+
+/************************* Large Disk Handling ********************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+static __inline int aic_sector_div(u_long capacity, int heads, int sectors);
+
+static __inline int
+aic_sector_div(u_long capacity, int heads, int sectors)
+{
+ return (capacity / (heads * sectors));
+}
+#else
+static __inline int aic_sector_div(sector_t capacity, int heads, int sectors);
+
+static __inline int
+aic_sector_div(sector_t capacity, int heads, int sectors)
+{
+ /* ugly, ugly sector_div calling convention.. */
+ sector_div(capacity, (heads * sectors));
+ return (int)capacity;
+}
+#endif
+
+/**************************** Module Library Hack *****************************/
+/*
+ * What we'd like to do is have a single "scsi library" module that both the
+ * aic7xxx and aic79xx drivers could load and depend on. A cursory examination
+ * of implementing module dependencies in Linux (handling the install and
+ * initrd cases) does not look promissing. For now, we just duplicate this
+ * code in both drivers using a simple symbol renaming scheme that hides this
+ * hack from the drivers.
+ */
+#define AIC_LIB_ENTRY_CONCAT(x, prefix) prefix ## x
+#define AIC_LIB_ENTRY_EXPAND(x, prefix) AIC_LIB_ENTRY_CONCAT(x, prefix)
+#define AIC_LIB_ENTRY(x) AIC_LIB_ENTRY_EXPAND(x, AIC_LIB_PREFIX)
+
+#define aic_sense_desc AIC_LIB_ENTRY(_sense_desc)
+#define aic_sense_error_action AIC_LIB_ENTRY(_sense_error_action)
+#define aic_error_action AIC_LIB_ENTRY(_error_action)
+#define aic_op_desc AIC_LIB_ENTRY(_op_desc)
+#define aic_cdb_string AIC_LIB_ENTRY(_cdb_string)
+#define aic_print_inquiry AIC_LIB_ENTRY(_print_inquiry)
+#define aic_calc_syncsrate AIC_LIB_ENTRY(_calc_syncrate)
+#define aic_calc_syncparam AIC_LIB_ENTRY(_calc_syncparam)
+#define aic_calc_speed AIC_LIB_ENTRY(_calc_speed)
+#define aic_inquiry_match AIC_LIB_ENTRY(_inquiry_match)
+#define aic_static_inquiry_match AIC_LIB_ENTRY(_static_inquiry_match)
+#define aic_parse_brace_option AIC_LIB_ENTRY(_parse_brace_option)
+
+/******************************************************************************/
+
+void aic_sense_desc(int /*sense_key*/, int /*asc*/,
+ int /*ascq*/, struct scsi_inquiry_data*,
+ const char** /*sense_key_desc*/,
+ const char** /*asc_desc*/);
+aic_sense_action aic_sense_error_action(struct scsi_sense_data*,
+ struct scsi_inquiry_data*,
+ uint32_t /*sense_flags*/);
+uint32_t aic_error_action(struct scsi_cmnd *,
+ struct scsi_inquiry_data *,
+ cam_status, u_int);
+
+#define SF_RETRY_UA 0x01
+#define SF_NO_PRINT 0x02
+#define SF_QUIET_IR 0x04 /* Be quiet about Illegal Request reponses */
+#define SF_PRINT_ALWAYS 0x08
+
+
+const char * aic_op_desc(uint16_t /*opcode*/, struct scsi_inquiry_data*);
+char * aic_cdb_string(uint8_t* /*cdb_ptr*/, char* /*cdb_string*/,
+ size_t /*len*/);
+void aic_print_inquiry(struct scsi_inquiry_data*);
+
+u_int aic_calc_syncsrate(u_int /*period_factor*/);
+u_int aic_calc_syncparam(u_int /*period*/);
+u_int aic_calc_speed(u_int width, u_int period, u_int offset,
+ u_int min_rate);
+
+int aic_inquiry_match(caddr_t /*inqbuffer*/,
+ caddr_t /*table_entry*/);
+int aic_static_inquiry_match(caddr_t /*inqbuffer*/,
+ caddr_t /*table_entry*/);
+
+typedef void aic_option_callback_t(u_long, int, int, int32_t);
+char * aic_parse_brace_option(char *opt_name, char *opt_arg,
+ char *end, int depth,
+ aic_option_callback_t *, u_long);
+
+static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
+ int *error_code, int *sense_key,
+ int *asc, int *ascq);
+static __inline void scsi_ulto2b(uint32_t val, uint8_t *bytes);
+static __inline void scsi_ulto3b(uint32_t val, uint8_t *bytes);
+static __inline void scsi_ulto4b(uint32_t val, uint8_t *bytes);
+static __inline uint32_t scsi_2btoul(uint8_t *bytes);
+static __inline uint32_t scsi_3btoul(uint8_t *bytes);
+static __inline int32_t scsi_3btol(uint8_t *bytes);
+static __inline uint32_t scsi_4btoul(uint8_t *bytes);
+
+static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
+ int *error_code, int *sense_key,
+ int *asc, int *ascq)
+{
+ *error_code = sense->error_code & SSD_ERRCODE;
+ *sense_key = sense->flags & SSD_KEY;
+ *asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0;
+ *ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0;
+}
+
+static __inline void
+scsi_ulto2b(uint32_t val, uint8_t *bytes)
+{
+
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static __inline void
+scsi_ulto3b(uint32_t val, uint8_t *bytes)
+{
+
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static __inline void
+scsi_ulto4b(uint32_t val, uint8_t *bytes)
+{
+
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static __inline uint32_t
+scsi_2btoul(uint8_t *bytes)
+{
+ uint32_t rv;
+
+ rv = (bytes[0] << 8) |
+ bytes[1];
+ return (rv);
+}
+
+static __inline uint32_t
+scsi_3btoul(uint8_t *bytes)
+{
+ uint32_t rv;
+
+ rv = (bytes[0] << 16) |
+ (bytes[1] << 8) |
+ bytes[2];
+ return (rv);
+}
+
+static __inline int32_t
+scsi_3btol(uint8_t *bytes)
+{
+ uint32_t rc = scsi_3btoul(bytes);
+
+ if (rc & 0x00800000)
+ rc |= 0xff000000;
+
+ return (int32_t) rc;
+}
+
+static __inline uint32_t
+scsi_4btoul(uint8_t *bytes)
+{
+ uint32_t rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+
+/* Macros for generating the elements of the PCI ID tables. */
+
+#define GETID(v, s) (unsigned)(((v) >> (s)) & 0xFFFF ?: PCI_ANY_ID)
+
+#define ID_C(x, c) \
+{ \
+ GETID(x,32), GETID(x,48), GETID(x,0), GETID(x,16), \
+ (c) << 8, 0xFFFF00, 0 \
+}
+
+#define ID2C(x) \
+ ID_C(x, PCI_CLASS_STORAGE_SCSI), \
+ ID_C(x, PCI_CLASS_STORAGE_RAID)
+
+#define IDIROC(x) ((x) | ~ID_ALL_IROC_MASK)
+
+/* Generate IDs for all 16 possibilites.
+ * The argument has already masked out
+ * the 4 least significant bits of the device id.
+ * (e.g., mask: ID_9005_GENERIC_MASK).
+ */
+#define ID16(x) \
+ ID(x), \
+ ID((x) | 0x0001000000000000ull), \
+ ID((x) | 0x0002000000000000ull), \
+ ID((x) | 0x0003000000000000ull), \
+ ID((x) | 0x0004000000000000ull), \
+ ID((x) | 0x0005000000000000ull), \
+ ID((x) | 0x0006000000000000ull), \
+ ID((x) | 0x0007000000000000ull), \
+ ID((x) | 0x0008000000000000ull), \
+ ID((x) | 0x0009000000000000ull), \
+ ID((x) | 0x000A000000000000ull), \
+ ID((x) | 0x000B000000000000ull), \
+ ID((x) | 0x000C000000000000ull), \
+ ID((x) | 0x000D000000000000ull), \
+ ID((x) | 0x000E000000000000ull), \
+ ID((x) | 0x000F000000000000ull)
+
+#endif /*_AICLIB_H */
diff --git a/drivers/scsi/aic7xxx/cam.h b/drivers/scsi/aic7xxx/cam.h
new file mode 100644
index 000000000000..d40ba0760c76
--- /dev/null
+++ b/drivers/scsi/aic7xxx/cam.h
@@ -0,0 +1,111 @@
+/*
+ * Data structures and definitions for the CAM system.
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2000 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/cam.h#15 $
+ */
+
+#ifndef _AIC7XXX_CAM_H
+#define _AIC7XXX_CAM_H 1
+
+#include <linux/types.h>
+
+#define CAM_BUS_WILDCARD ((u_int)~0)
+#define CAM_TARGET_WILDCARD ((u_int)~0)
+#define CAM_LUN_WILDCARD ((u_int)~0)
+
+/* CAM Status field values */
+typedef enum {
+ CAM_REQ_INPROG, /* CCB request is in progress */
+ CAM_REQ_CMP, /* CCB request completed without error */
+ CAM_REQ_ABORTED, /* CCB request aborted by the host */
+ CAM_UA_ABORT, /* Unable to abort CCB request */
+ CAM_REQ_CMP_ERR, /* CCB request completed with an error */
+ CAM_BUSY, /* CAM subsytem is busy */
+ CAM_REQ_INVALID, /* CCB request was invalid */
+ CAM_PATH_INVALID, /* Supplied Path ID is invalid */
+ CAM_SEL_TIMEOUT, /* Target Selection Timeout */
+ CAM_CMD_TIMEOUT, /* Command timeout */
+ CAM_SCSI_STATUS_ERROR, /* SCSI error, look at error code in CCB */
+ CAM_SCSI_BUS_RESET, /* SCSI Bus Reset Sent/Received */
+ CAM_UNCOR_PARITY, /* Uncorrectable parity error occurred */
+ CAM_AUTOSENSE_FAIL, /* Autosense: request sense cmd fail */
+ CAM_NO_HBA, /* No HBA Detected Error */
+ CAM_DATA_RUN_ERR, /* Data Overrun error */
+ CAM_UNEXP_BUSFREE, /* Unexpected Bus Free */
+ CAM_SEQUENCE_FAIL, /* Protocol Violation */
+ CAM_CCB_LEN_ERR, /* CCB length supplied is inadequate */
+ CAM_PROVIDE_FAIL, /* Unable to provide requested capability */
+ CAM_BDR_SENT, /* A SCSI BDR msg was sent to target */
+ CAM_REQ_TERMIO, /* CCB request terminated by the host */
+ CAM_UNREC_HBA_ERROR, /* Unrecoverable Host Bus Adapter Error */
+ CAM_REQ_TOO_BIG, /* The request was too large for this host */
+ CAM_UA_TERMIO, /* Unable to terminate I/O CCB request */
+ CAM_MSG_REJECT_REC, /* Message Reject Received */
+ CAM_DEV_NOT_THERE, /* SCSI Device Not Installed/there */
+ CAM_RESRC_UNAVAIL, /* Resource Unavailable */
+ /*
+ * This request should be requeued to preserve
+ * transaction ordering. This typically occurs
+ * when the SIM recognizes an error that should
+ * freeze the queue and must place additional
+ * requests for the target at the sim level
+ * back into the XPT queue.
+ */
+ CAM_REQUEUE_REQ,
+ CAM_DEV_QFRZN = 0x40,
+
+ CAM_STATUS_MASK = 0x3F
+} cam_status;
+
+/*
+ * Definitions for the asynchronous callback CCB fields.
+ */
+typedef enum {
+ AC_GETDEV_CHANGED = 0x800,/* Getdev info might have changed */
+ AC_INQ_CHANGED = 0x400,/* Inquiry info might have changed */
+ AC_TRANSFER_NEG = 0x200,/* New transfer settings in effect */
+ AC_LOST_DEVICE = 0x100,/* A device went away */
+ AC_FOUND_DEVICE = 0x080,/* A new device was found */
+ AC_PATH_DEREGISTERED = 0x040,/* A path has de-registered */
+ AC_PATH_REGISTERED = 0x020,/* A new path has been registered */
+ AC_SENT_BDR = 0x010,/* A BDR message was sent to target */
+ AC_SCSI_AEN = 0x008,/* A SCSI AEN has been received */
+ AC_UNSOL_RESEL = 0x002,/* Unsolicited reselection occurred */
+ AC_BUS_RESET = 0x001 /* A SCSI bus reset occurred */
+} ac_code;
+
+typedef enum {
+ CAM_DIR_IN = SCSI_DATA_READ,
+ CAM_DIR_OUT = SCSI_DATA_WRITE,
+ CAM_DIR_NONE = SCSI_DATA_NONE
+} ccb_flags;
+
+#endif /* _AIC7XXX_CAM_H */
diff --git a/drivers/scsi/aic7xxx/queue.h b/drivers/scsi/aic7xxx/queue.h
new file mode 100644
index 000000000000..8adf8003a164
--- /dev/null
+++ b/drivers/scsi/aic7xxx/queue.h
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.38 2000/05/26 02:06:56 jake Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ CIRCLEQ
+ * _HEAD + + + + +
+ * _HEAD_INITIALIZER + + + + +
+ * _ENTRY + + + + +
+ * _INIT + + + + +
+ * _EMPTY + + + + +
+ * _FIRST + + + + +
+ * _NEXT + + + + +
+ * _PREV - - - + +
+ * _LAST - - + + +
+ * _FOREACH + + + + +
+ * _FOREACH_REVERSE - - - + +
+ * _INSERT_HEAD + + + + +
+ * _INSERT_BEFORE - + - + +
+ * _INSERT_AFTER + + + + +
+ * _INSERT_TAIL - - + + +
+ * _REMOVE_HEAD + - + - -
+ * _REMOVE + + + + +
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = \
+ SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
+ } \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ STAILQ_LAST((head)) = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_LAST(head) (*(head)->stqh_last)
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD(head, field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ if ((STAILQ_NEXT(curelm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+ } \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+} while (0)
+
+/*
+ * Circular queue declarations.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { (void *)&(head), (void *)&(head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = CIRCLEQ_FIRST((head)); \
+ (var) != (void *)(head); \
+ (var) = CIRCLEQ_NEXT((var), field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = CIRCLEQ_LAST((head)); \
+ (var) != (void *)(head); \
+ (var) = CIRCLEQ_PREV((var), field))
+
+#define CIRCLEQ_INIT(head) do { \
+ CIRCLEQ_FIRST((head)) = (void *)(head); \
+ CIRCLEQ_LAST((head)) = (void *)(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field); \
+ CIRCLEQ_PREV((elm), field) = (listelm); \
+ if (CIRCLEQ_NEXT((listelm), field) == (void *)(head)) \
+ CIRCLEQ_LAST((head)) = (elm); \
+ else \
+ CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
+ CIRCLEQ_NEXT((listelm), field) = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = (listelm); \
+ CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field); \
+ if (CIRCLEQ_PREV((listelm), field) == (void *)(head)) \
+ CIRCLEQ_FIRST((head)) = (elm); \
+ else \
+ CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
+ CIRCLEQ_PREV((listelm), field) = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head)); \
+ CIRCLEQ_PREV((elm), field) = (void *)(head); \
+ if (CIRCLEQ_LAST((head)) == (void *)(head)) \
+ CIRCLEQ_LAST((head)) = (elm); \
+ else \
+ CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm); \
+ CIRCLEQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ CIRCLEQ_NEXT((elm), field) = (void *)(head); \
+ CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head)); \
+ if (CIRCLEQ_FIRST((head)) == (void *)(head)) \
+ CIRCLEQ_FIRST((head)) = (elm); \
+ else \
+ CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm); \
+ CIRCLEQ_LAST((head)) = (elm); \
+} while (0)
+
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if (CIRCLEQ_NEXT((elm), field) == (void *)(head)) \
+ CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field); \
+ else \
+ CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) = \
+ CIRCLEQ_PREV((elm), field); \
+ if (CIRCLEQ_PREV((elm), field) == (void *)(head)) \
+ CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field); \
+ else \
+ CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) = \
+ CIRCLEQ_NEXT((elm), field); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/drivers/scsi/aic7xxx/scsi_iu.h b/drivers/scsi/aic7xxx/scsi_iu.h
new file mode 100644
index 000000000000..0eafd3c17730
--- /dev/null
+++ b/drivers/scsi/aic7xxx/scsi_iu.h
@@ -0,0 +1,39 @@
+/*
+ * This file is in the public domain.
+ */
+#ifndef _SCSI_SCSI_IU_H
+#define _SCSI_SCSI_IU_H 1
+
+struct scsi_status_iu_header
+{
+ u_int8_t reserved[2];
+ u_int8_t flags;
+#define SIU_SNSVALID 0x2
+#define SIU_RSPVALID 0x1
+ u_int8_t status;
+ u_int8_t sense_length[4];
+ u_int8_t pkt_failures_length[4];
+ u_int8_t pkt_failures[1];
+};
+
+#define SIU_PKTFAIL_OFFSET(siu) 12
+#define SIU_PKTFAIL_CODE(siu) (scsi_4btoul((siu)->pkt_failures) & 0xFF)
+#define SIU_PFC_NONE 0
+#define SIU_PFC_CIU_FIELDS_INVALID 2
+#define SIU_PFC_TMF_NOT_SUPPORTED 4
+#define SIU_PFC_TMF_FAILED 5
+#define SIU_PFC_INVALID_TYPE_CODE 6
+#define SIU_PFC_ILLEGAL_REQUEST 7
+#define SIU_SENSE_OFFSET(siu) \
+ (12 + (((siu)->flags & SIU_RSPVALID) \
+ ? scsi_4btoul((siu)->pkt_failures_length) \
+ : 0))
+
+#define SIU_TASKMGMT_NONE 0x00
+#define SIU_TASKMGMT_ABORT_TASK 0x01
+#define SIU_TASKMGMT_ABORT_TASK_SET 0x02
+#define SIU_TASKMGMT_CLEAR_TASK_SET 0x04
+#define SIU_TASKMGMT_LUN_RESET 0x08
+#define SIU_TASKMGMT_TARGET_RESET 0x20
+#define SIU_TASKMGMT_CLEAR_ACA 0x40
+#endif /*_SCSI_SCSI_IU_H*/
diff --git a/drivers/scsi/aic7xxx/scsi_message.h b/drivers/scsi/aic7xxx/scsi_message.h
new file mode 100644
index 000000000000..75811e245ec7
--- /dev/null
+++ b/drivers/scsi/aic7xxx/scsi_message.h
@@ -0,0 +1,70 @@
+/*
+ * This file is in the public domain.
+ * $FreeBSD: src/sys/cam/scsi/scsi_message.h,v 1.2 2000/05/01 20:21:29 peter Exp $
+ */
+
+/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE 0x00 /* M/M */
+#define MSG_TASK_COMPLETE 0x00 /* M/M */ /* SPI3 Terminology */
+#define MSG_EXTENDED 0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
+#define MSG_ABORT 0x06 /* O/M */
+#define MSG_ABORT_TASK_SET 0x06 /* O/M */ /* SPI3 Terminology */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+#define MSG_PARITY_ERROR 0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET 0x0c /* O/M */
+#define MSG_TARGET_RESET 0x0c /* O/M */ /* SPI3 Terminology */
+#define MSG_ABORT_TAG 0x0d /* O/O */
+#define MSG_ABORT_TASK 0x0d /* O/O */ /* SPI3 Terminology */
+#define MSG_CLEAR_QUEUE 0x0e /* O/O */
+#define MSG_CLEAR_TASK_SET 0x0e /* O/O */ /* SPI3 Terminology */
+#define MSG_INIT_RECOVERY 0x0f /* O/O */ /* Deprecated in SPI3 */
+#define MSG_REL_RECOVERY 0x10 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_TERM_IO_PROC 0x11 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_CLEAR_ACA 0x16 /* O/O */ /* SPI3 */
+#define MSG_LOGICAL_UNIT_RESET 0x17 /* O/O */ /* SPI3 */
+#define MSG_QAS_REQUEST 0x55 /* O/O */ /* SPI3 */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_SIMPLE_TASK 0x20 /* O/O */ /* SPI3 Terminology */
+#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
+#define MSG_HEAD_OF_QUEUE_TASK 0x21 /* O/O */ /* SPI3 Terminology */
+#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
+#define MSG_ORDERED_TASK 0x22 /* O/O */ /* SPI3 Terminology */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+#define MSG_ACA_TASK 0x24 /* 0/0 */ /* SPI3 */
+
+/* Identify message */ /* M/M */
+#define MSG_IDENTIFYFLAG 0x80
+#define MSG_IDENTIFY_DISCFLAG 0x40
+#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG)
+#define MSG_IDENTIFY_LUNMASK 0x3F
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR 0x01
+#define MSG_EXT_SDTR_LEN 0x03
+
+#define MSG_EXT_WDTR 0x03
+#define MSG_EXT_WDTR_LEN 0x02
+#define MSG_EXT_WDTR_BUS_8_BIT 0x00
+#define MSG_EXT_WDTR_BUS_16_BIT 0x01
+#define MSG_EXT_WDTR_BUS_32_BIT 0x02 /* Deprecated in SPI3 */
+
+#define MSG_EXT_PPR 0x04 /* SPI3 */
+#define MSG_EXT_PPR_LEN 0x06
+#define MSG_EXT_PPR_PCOMP_EN 0x80
+#define MSG_EXT_PPR_RTI 0x40
+#define MSG_EXT_PPR_RD_STRM 0x20
+#define MSG_EXT_PPR_WR_FLOW 0x10
+#define MSG_EXT_PPR_HOLD_MCS 0x08
+#define MSG_EXT_PPR_QAS_REQ 0x04
+#define MSG_EXT_PPR_DT_REQ 0x02
+#define MSG_EXT_PPR_IU_REQ 0x01
diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c
new file mode 100644
index 000000000000..a6e7bb0d53f4
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old.c
@@ -0,0 +1,11178 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ * Substantially modified to include support for wide and twin bus
+ * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * SCB paging, and other rework of the code.
+ *
+ * Parts of this driver were also based on the FreeBSD driver by
+ * Justin T. Gibbs. His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ * Thanks also go to (in alphabetical order) the following:
+ *
+ * Rory Bolt - Sequencer bug fixes
+ * Jay Estabrook - Initial DEC Alpha support
+ * Doug Ledford - Much needed abort/reset bug fixes
+ * Kai Makisara - DMAing of SCBs
+ *
+ * A Boot time option was also added for not resetting the scsi bus.
+ *
+ * Form: aic7xxx=extended
+ * aic7xxx=no_reset
+ * aic7xxx=ultra
+ * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level
+ * aic7xxx=verbose
+ *
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
+ *-M*************************************************************************/
+
+/*+M**************************************************************************
+ *
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs. Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ * 1: Import of the latest FreeBSD sequencer code for this driver
+ * 2: Modification of kernel code to accommodate different sequencer semantics
+ * 3: Extensive changes throughout kernel portion of driver to improve
+ * abort/reset processing and error hanndling
+ * 4: Other work contributed by various people on the Internet
+ * 5: Changes to printk information and verbosity selection code
+ * 6: General reliability related changes, especially in IRQ management
+ * 7: Modifications to the default probe/attach order for supported cards
+ * 8: SMP friendliness has been improved
+ *
+ * Overall, this driver represents a significant departure from the official
+ * aic7xxx driver released by Dan Eischen in two ways. First, in the code
+ * itself. A diff between the two version of the driver is now a several
+ * thousand line diff. Second, in approach to solving the same problem. The
+ * problem is importing the FreeBSD aic7xxx driver code to linux can be a
+ * difficult and time consuming process, that also can be error prone. Dan
+ * Eischen's official driver uses the approach that the linux and FreeBSD
+ * drivers should be as identical as possible. To that end, his next version
+ * of this driver will be using a mid-layer code library that he is developing
+ * to moderate communications between the linux mid-level SCSI code and the
+ * low level FreeBSD driver. He intends to be able to essentially drop the
+ * FreeBSD driver into the linux kernel with only a few minor tweaks to some
+ * include files and the like and get things working, making for fast easy
+ * imports of the FreeBSD code into linux.
+ *
+ * I disagree with Dan's approach. Not that I don't think his way of doing
+ * things would be nice, easy to maintain, and create a more uniform driver
+ * between FreeBSD and Linux. I have no objection to those issues. My
+ * disagreement is on the needed functionality. There simply are certain
+ * things that are done differently in FreeBSD than linux that will cause
+ * problems for this driver regardless of any middle ware Dan implements.
+ * The biggest example of this at the moment is interrupt semantics. Linux
+ * doesn't provide the same protection techniques as FreeBSD does, nor can
+ * they be easily implemented in any middle ware code since they would truly
+ * belong in the kernel proper and would effect all drivers. For the time
+ * being, I see issues such as these as major stumbling blocks to the
+ * reliability of code based upon such middle ware. Therefore, I choose to
+ * use a different approach to importing the FreeBSD code that doesn't
+ * involve any middle ware type code. My approach is to import the sequencer
+ * code from FreeBSD wholesale. Then, to only make changes in the kernel
+ * portion of the driver as they are needed for the new sequencer semantics.
+ * In this way, the portion of the driver that speaks to the rest of the
+ * linux kernel is fairly static and can be changed/modified to solve
+ * any problems one might encounter without concern for the FreeBSD driver.
+ *
+ * Note: If time and experience should prove me wrong that the middle ware
+ * code Dan writes is reliable in its operation, then I'll retract my above
+ * statements. But, for those that don't know, I'm from Missouri (in the US)
+ * and our state motto is "The Show-Me State". Well, before I will put
+ * faith into it, you'll have to show me that it works :)
+ *
+ *_M*************************************************************************/
+
+/*
+ * The next three defines are user configurable. These should be the only
+ * defines a user might need to get in here and change. There are other
+ * defines buried deeper in the code, but those really shouldn't need touched
+ * under normal conditions.
+ */
+
+/*
+ * AIC7XXX_STRICT_PCI_SETUP
+ * Should we assume the PCI config options on our controllers are set with
+ * sane and proper values, or should we be anal about our PCI config
+ * registers and force them to what we want? The main advantage to
+ * defining this option is on non-Intel hardware where the BIOS may not
+ * have been run to set things up, or if you have one of the BIOSless
+ * Adaptec controllers, such as a 2910, that don't get set up by the
+ * BIOS. However, keep in mind that we really do set the most important
+ * items in the driver regardless of this setting, this only controls some
+ * of the more esoteric PCI options on these cards. In that sense, I
+ * would default to leaving this off. However, if people wish to try
+ * things both ways, that would also help me to know if there are some
+ * machines where it works one way but not another.
+ *
+ * -- July 7, 17:09
+ * OK...I need this on my machine for testing, so the default is to
+ * leave it defined.
+ *
+ * -- July 7, 18:49
+ * I needed it for testing, but it didn't make any difference, so back
+ * off she goes.
+ *
+ * -- July 16, 23:04
+ * I turned it back on to try and compensate for the 2.1.x PCI code
+ * which no longer relies solely on the BIOS and now tries to set
+ * things itself.
+ */
+
+#define AIC7XXX_STRICT_PCI_SETUP
+
+/*
+ * AIC7XXX_VERBOSE_DEBUGGING
+ * This option enables a lot of extra printk();s in the code, surrounded
+ * by if (aic7xxx_verbose ...) statements. Executing all of those if
+ * statements and the extra checks can get to where it actually does have
+ * an impact on CPU usage and such, as well as code size. Disabling this
+ * define will keep some of those from becoming part of the code.
+ *
+ * NOTE: Currently, this option has no real effect, I will be adding the
+ * various #ifdef's in the code later when I've decided a section is
+ * complete and no longer needs debugging. OK...a lot of things are now
+ * surrounded by this define, so turning this off does have an impact.
+ */
+
+/*
+ * #define AIC7XXX_VERBOSE_DEBUGGING
+ */
+
+#include <linux/module.h>
+#include <stdarg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aic7xxx_old/aic7xxx.h"
+
+#include "aic7xxx_old/sequencer.h"
+#include "aic7xxx_old/scsi_message.h"
+#include "aic7xxx_old/aic7xxx_reg.h"
+#include <scsi/scsicam.h>
+
+#include <linux/stat.h>
+#include <linux/slab.h> /* for kmalloc() */
+
+#include <linux/config.h> /* for CONFIG_PCI */
+
+#define AIC7XXX_C_VERSION "5.2.6"
+
+#define ALL_TARGETS -1
+#define ALL_CHANNELS -1
+#define ALL_LUNS -1
+#define MAX_TARGETS 16
+#define MAX_LUNS 8
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#if defined(__powerpc__) || defined(__i386__) || defined(__x86_64__)
+# define MMAPIO
+#endif
+
+/*
+ * You can try raising me for better performance or lowering me if you have
+ * flaky devices that go off the scsi bus when hit with too many tagged
+ * commands (like some IBM SCSI-3 LVD drives).
+ */
+#define AIC7XXX_CMDS_PER_DEVICE 32
+
+typedef struct
+{
+ unsigned char tag_commands[16]; /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver not to the default tag depth
+ * everywhere.
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+ 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system. By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above). When 255, the driver will
+ * not enable tagged queueing for that particular device. When positive
+ * (> 0) and (< 255) the values in the array are used for the queue_depth.
+ * Note that the maximum value for an entry is 254, but you're insane if
+ * you try to use that many commands on one device.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ * to modify in order to change things is found after this fake one.
+ *
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
+ {DEFAULT_TAG_COMMANDS},
+ {{255, 16, 4, 255, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS}
+};
+
+
+/*
+ * Define an array of board names that can be indexed by aha_type.
+ * Don't forget to change this when changing the types!
+ */
+static const char *board_names[] = {
+ "AIC-7xxx Unknown", /* AIC_NONE */
+ "Adaptec AIC-7810 Hardware RAID Controller", /* AIC_7810 */
+ "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
+ "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
+ "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
+ "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
+ "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
+ "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
+ "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
+ "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
+ "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
+ "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
+ "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
+ "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
+ "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
+ "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
+ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
+ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
+ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */
+ "Adaptec AHA-2940UW Pro Ultra SCSI host adapter", /* AIC_7887 */
+ "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */
+ "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */
+ "Adaptec AHA-293X Ultra2 SCSI host adapter", /* AIC_7890 */
+ "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */
+ "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */
+ "Adaptec AHA-394X Ultra2 SCSI host adapter", /* AIC_7897 */
+ "Adaptec AHA-395X Ultra2 SCSI host adapter", /* AIC_7897 */
+ "Adaptec PCMCIA SCSI controller", /* card bus stuff */
+ "Adaptec AIC-7892 Ultra 160/m SCSI host adapter", /* AIC_7892 */
+ "Adaptec AIC-7899 Ultra 160/m SCSI host adapter", /* AIC_7899 */
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+/*
+ * What we want to do is have the higher level scsi driver requeue
+ * the command to us. There is no specific driver status for this
+ * condition, but the higher level scsi driver will requeue the
+ * command on a DID_BUS_BUSY error.
+ *
+ * Upon further inspection and testing, it seems that DID_BUS_BUSY
+ * will *always* retry the command. We can get into an infinite loop
+ * if this happens when we really want some sort of counter that
+ * will automatically abort/reset the command after so many retries.
+ * Using DID_ERROR will do just that. (Made by a suggestion by
+ * Doug Ledford 8/1/96)
+ */
+#define DID_RETRY_COMMAND DID_ERROR
+
+#define HSCSIID 0x07
+#define SCSI_RESET 0x040
+
+/*
+ * EISA/VL-bus stuff
+ */
+#define MINSLOT 1
+#define MAXSLOT 15
+#define SLOTBASE(x) ((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
+
+/*
+ * Standard EISA Host ID regs (Offset from slot base)
+ */
+#define AHC_HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
+#define AHC_HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
+#define AHC_HID2 0x82 /* product */
+#define AHC_HID3 0x83 /* firmware revision */
+
+/*
+ * AIC-7770 I/O range to reserve for a card
+ */
+#define MINREG 0xC00
+#define MAXREG 0xCFF
+
+#define INTDEF 0x5C /* Interrupt Definition Register */
+
+/*
+ * AIC-78X0 PCI registers
+ */
+#define CLASS_PROGIF_REVID 0x08
+#define DEVREVID 0x000000FFul
+#define PROGINFC 0x0000FF00ul
+#define SUBCLASS 0x00FF0000ul
+#define BASECLASS 0xFF000000ul
+
+#define CSIZE_LATTIME 0x0C
+#define CACHESIZE 0x0000003Ful /* only 5 bits */
+#define LATTIME 0x0000FF00ul
+
+#define DEVCONFIG 0x40
+#define SCBSIZE32 0x00010000ul /* aic789X only */
+#define MPORTMODE 0x00000400ul /* aic7870 only */
+#define RAMPSM 0x00000200ul /* aic7870 only */
+#define RAMPSM_ULTRA2 0x00000004
+#define VOLSENSE 0x00000100ul
+#define SCBRAMSEL 0x00000080ul
+#define SCBRAMSEL_ULTRA2 0x00000008
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+
+#define SCAMCTL 0x1a /* Ultra2 only */
+#define CCSCBBADDR 0xf0 /* aic7895/6/7 */
+
+/*
+ * Define the different types of SEEPROMs on aic7xxx adapters
+ * and make it also represent the address size used in accessing
+ * its registers. The 93C46 chips have 1024 bits organized into
+ * 64 16-bit words, while the 93C56 chips have 2048 bits organized
+ * into 128 16-bit words. The C46 chips use 6 bits to address
+ * each word, while the C56 and C66 (4096 bits) use 8 bits to
+ * address each word.
+ */
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
+
+/*
+ *
+ * Define the format of the SEEPROM registers (16 bits).
+ *
+ */
+struct seeprom_config {
+
+/*
+ * SCSI ID Configuration Flags
+ */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device (wide card) */
+#define CFSYNCHISULTRA 0x0040 /* CFSYNC is an ultra offset */
+#define CFNEWULTRAFORMAT 0x0080 /* Use the Ultra2 SEEPROM format */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+#define CFMULTILUN 0x0800 /* probe mult luns in BIOS scan */
+#define CFWBCACHEYES 0x4000 /* Enable W-Behind Cache on drive */
+#define CFWBCACHENC 0xc000 /* Don't change W-Behind Cache */
+/* UNUSED 0x3000 */
+ unsigned short device_flags[16]; /* words 0-15 */
+
+/*
+ * BIOS Control Bits
+ */
+#define CFSUPREM 0x0001 /* support all removable drives */
+#define CFSUPREMB 0x0002 /* support removable drives for boot only */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+/* UNUSED 0x0008 */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+/* UNUSED 0x0040 */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+/* UNUSED 0xFF00 */
+ unsigned short bios_control; /* word 16 */
+
+/*
+ * Host Adapter Control Bits
+ */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
+#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
+#define CFBPRIMARY 0x0100 /* Channel B primary on 7895 chipsets */
+#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Term */
+#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */
+/* UNUSED 0xF280 */
+ unsigned short adapter_control; /* word 17 */
+
+/*
+ * Bus Release, Host Adapter ID
+ */
+#define CFSCSIID 0x000F /* host adapter SCSI ID */
+/* UNUSED 0x00F0 */
+#define CFBRTIME 0xFF00 /* bus release time */
+ unsigned short brtime_id; /* word 18 */
+
+/*
+ * Maximum targets
+ */
+#define CFMAXTARG 0x00FF /* maximum targets */
+/* UNUSED 0xFF00 */
+ unsigned short max_targets; /* word 19 */
+
+ unsigned short res_1[11]; /* words 20-30 */
+ unsigned short checksum; /* word 31 */
+};
+
+#define SELBUS_MASK 0x0a
+#define SELNARROW 0x00
+#define SELBUSB 0x08
+#define SINGLE_BUS 0x00
+
+#define SCB_TARGET(scb) \
+ (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb) \
+ ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb) \
+ (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
+
+/*
+ * If an error occurs during a data transfer phase, run the command
+ * to completion - it's easier that way - making a note of the error
+ * condition in this location. This then will modify a DID_OK status
+ * into an appropriate error for the higher-level SCSI code.
+ */
+#define aic7xxx_error(cmd) ((cmd)->SCp.Status)
+
+/*
+ * Keep track of the targets returned status.
+ */
+#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command)
+
+/*
+ * The position of the SCSI commands scb within the scb array.
+ */
+#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in)
+
+/*
+ * The stored DMA mapping for single-buffer data transfers.
+ */
+#define aic7xxx_mapping(cmd) ((cmd)->SCp.phase)
+
+/*
+ * Get out private data area from a scsi cmd pointer
+ */
+#define AIC_DEV(cmd) ((struct aic_dev_data *)(cmd)->device->hostdata)
+
+/*
+ * So we can keep track of our host structs
+ */
+static struct aic7xxx_host *first_aic7xxx = NULL;
+
+/*
+ * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
+ * in the scatter-gather lists. We need to convert the virtual
+ * addresses to physical addresses.
+ */
+struct hw_scatterlist {
+ unsigned int address;
+ unsigned int length;
+};
+
+/*
+ * Maximum number of SG segments these cards can support.
+ */
+#define AIC7XXX_MAX_SG 128
+
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 255
+
+
+struct aic7xxx_hwscb {
+/* ------------ Begin hardware supported fields ---------------- */
+/* 0*/ unsigned char control;
+/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
+/* 2*/ unsigned char target_status;
+/* 3*/ unsigned char SG_segment_count;
+/* 4*/ unsigned int SG_list_pointer;
+/* 8*/ unsigned char residual_SG_segment_count;
+/* 9*/ unsigned char residual_data_count[3];
+/*12*/ unsigned int data_pointer;
+/*16*/ unsigned int data_count;
+/*20*/ unsigned int SCSI_cmd_pointer;
+/*24*/ unsigned char SCSI_cmd_length;
+/*25*/ unsigned char tag; /* Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
+ * via PIO to initialize a transaction.
+ */
+/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
+ * or disconnected down in the sequencer.
+ */
+/*27*/ unsigned char prev;
+/*28*/ unsigned int pad; /*
+ * Unused by the kernel, but we require
+ * the padding so that the array of
+ * hardware SCBs is aligned on 32 byte
+ * boundaries so the sequencer can index
+ */
+};
+
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_DTR_SCB = 0x0001,
+ SCB_WAITINGQ = 0x0002,
+ SCB_ACTIVE = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_ABORT = 0x0010,
+ SCB_DEVICE_RESET = 0x0020,
+ SCB_RESET = 0x0040,
+ SCB_RECOVERY_SCB = 0x0080,
+ SCB_MSGOUT_PPR = 0x0100,
+ SCB_MSGOUT_SENT = 0x0200,
+ SCB_MSGOUT_SDTR = 0x0400,
+ SCB_MSGOUT_WDTR = 0x0800,
+ SCB_MSGOUT_BITS = SCB_MSGOUT_PPR |
+ SCB_MSGOUT_SENT |
+ SCB_MSGOUT_SDTR |
+ SCB_MSGOUT_WDTR,
+ SCB_QUEUED_ABORT = 0x1000,
+ SCB_QUEUED_FOR_DONE = 0x2000,
+ SCB_WAS_BUSY = 0x4000,
+ SCB_QUEUE_FULL = 0x8000
+} scb_flag_type;
+
+typedef enum {
+ AHC_FNONE = 0x00000000,
+ AHC_PAGESCBS = 0x00000001,
+ AHC_CHANNEL_B_PRIMARY = 0x00000002,
+ AHC_USEDEFAULTS = 0x00000004,
+ AHC_INDIRECT_PAGING = 0x00000008,
+ AHC_CHNLB = 0x00000020,
+ AHC_CHNLC = 0x00000040,
+ AHC_EXTEND_TRANS_A = 0x00000100,
+ AHC_EXTEND_TRANS_B = 0x00000200,
+ AHC_TERM_ENB_A = 0x00000400,
+ AHC_TERM_ENB_SE_LOW = 0x00000400,
+ AHC_TERM_ENB_B = 0x00000800,
+ AHC_TERM_ENB_SE_HIGH = 0x00000800,
+ AHC_HANDLING_REQINITS = 0x00001000,
+ AHC_TARGETMODE = 0x00002000,
+ AHC_NEWEEPROM_FMT = 0x00004000,
+ /*
+ * Here ends the FreeBSD defined flags and here begins the linux defined
+ * flags. NOTE: I did not preserve the old flag name during this change
+ * specifically to force me to evaluate what flags were being used properly
+ * and what flags weren't. This way, I could clean up the flag usage on
+ * a use by use basis. Doug Ledford
+ */
+ AHC_MOTHERBOARD = 0x00020000,
+ AHC_NO_STPWEN = 0x00040000,
+ AHC_RESET_DELAY = 0x00080000,
+ AHC_A_SCANNED = 0x00100000,
+ AHC_B_SCANNED = 0x00200000,
+ AHC_MULTI_CHANNEL = 0x00400000,
+ AHC_BIOS_ENABLED = 0x00800000,
+ AHC_SEEPROM_FOUND = 0x01000000,
+ AHC_TERM_ENB_LVD = 0x02000000,
+ AHC_ABORT_PENDING = 0x04000000,
+ AHC_RESET_PENDING = 0x08000000,
+#define AHC_IN_ISR_BIT 28
+ AHC_IN_ISR = 0x10000000,
+ AHC_IN_ABORT = 0x20000000,
+ AHC_IN_RESET = 0x40000000,
+ AHC_EXTERNAL_SRAM = 0x80000000
+} ahc_flag_type;
+
+typedef enum {
+ AHC_NONE = 0x0000,
+ AHC_CHIPID_MASK = 0x00ff,
+ AHC_AIC7770 = 0x0001,
+ AHC_AIC7850 = 0x0002,
+ AHC_AIC7860 = 0x0003,
+ AHC_AIC7870 = 0x0004,
+ AHC_AIC7880 = 0x0005,
+ AHC_AIC7890 = 0x0006,
+ AHC_AIC7895 = 0x0007,
+ AHC_AIC7896 = 0x0008,
+ AHC_AIC7892 = 0x0009,
+ AHC_AIC7899 = 0x000a,
+ AHC_VL = 0x0100,
+ AHC_EISA = 0x0200,
+ AHC_PCI = 0x0400,
+} ahc_chip;
+
+typedef enum {
+ AHC_FENONE = 0x0000,
+ AHC_ULTRA = 0x0001,
+ AHC_ULTRA2 = 0x0002,
+ AHC_WIDE = 0x0004,
+ AHC_TWIN = 0x0008,
+ AHC_MORE_SRAM = 0x0010,
+ AHC_CMD_CHAN = 0x0020,
+ AHC_QUEUE_REGS = 0x0040,
+ AHC_SG_PRELOAD = 0x0080,
+ AHC_SPIOCAP = 0x0100,
+ AHC_ULTRA3 = 0x0200,
+ AHC_NEW_AUTOTERM = 0x0400,
+ AHC_AIC7770_FE = AHC_FENONE,
+ AHC_AIC7850_FE = AHC_SPIOCAP,
+ AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP,
+ AHC_AIC7870_FE = AHC_FENONE,
+ AHC_AIC7880_FE = AHC_ULTRA,
+ AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
+ AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_NEW_AUTOTERM,
+ AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
+ AHC_AIC7896_FE = AHC_AIC7890_FE,
+ AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_ULTRA3,
+ AHC_AIC7899_FE = AHC_AIC7890_FE|AHC_ULTRA3,
+} ahc_feature;
+
+#define SCB_DMA_ADDR(scb, addr) ((unsigned long)(addr) + (scb)->scb_dma->dma_offset)
+
+struct aic7xxx_scb_dma {
+ unsigned long dma_offset; /* Correction you have to add
+ * to virtual address to get
+ * dma handle in this region */
+ dma_addr_t dma_address; /* DMA handle of the start,
+ * for unmap */
+ unsigned int dma_len; /* DMA length */
+};
+
+typedef enum {
+ AHC_BUG_NONE = 0x0000,
+ AHC_BUG_TMODE_WIDEODD = 0x0001,
+ AHC_BUG_AUTOFLUSH = 0x0002,
+ AHC_BUG_CACHETHEN = 0x0004,
+ AHC_BUG_CACHETHEN_DIS = 0x0008,
+ AHC_BUG_PCI_2_1_RETRY = 0x0010,
+ AHC_BUG_PCI_MWI = 0x0020,
+ AHC_BUG_SCBCHAN_UPLOAD = 0x0040,
+} ahc_bugs;
+
+struct aic7xxx_scb {
+ struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */
+ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
+ struct aic7xxx_scb *q_next; /* next scb in queue */
+ volatile scb_flag_type flags; /* current state of scb */
+ struct hw_scatterlist *sg_list; /* SG list in adapter format */
+ unsigned char tag_action;
+ unsigned char sg_count;
+ unsigned char *sense_cmd; /*
+ * Allocate 6 characters for
+ * sense command.
+ */
+ unsigned char *cmnd;
+ unsigned int sg_length; /* We init this during buildscb so we
+ * don't have to calculate anything
+ * during underflow/overflow/stat code
+ */
+ void *kmalloc_ptr;
+ struct aic7xxx_scb_dma *scb_dma;
+};
+
+/*
+ * Define a linked list of SCBs.
+ */
+typedef struct {
+ struct aic7xxx_scb *head;
+ struct aic7xxx_scb *tail;
+} scb_queue_type;
+
+static struct {
+ unsigned char errno;
+ const char *errmesg;
+} hard_error[] = {
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Ram Parity Error" },
+ { DPARERR, "Data-Path Ram Parity Error" },
+ { MPARERR, "Scratch Ram/SCB Array Ram Parity Error" },
+ { PCIERRSTAT,"PCI Error detected" },
+ { CIOPARERR, "CIOBUS Parity Error" }
+};
+
+static unsigned char
+generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
+
+typedef struct {
+ scb_queue_type free_scbs; /*
+ * SCBs assigned to free slot on
+ * card (no paging required)
+ */
+ struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB];
+ struct aic7xxx_hwscb *hscbs;
+ unsigned char numscbs; /* current number of scbs */
+ unsigned char maxhscbs; /* hardware scbs */
+ unsigned char maxscbs; /* max scbs including pageable scbs */
+ dma_addr_t hscbs_dma; /* DMA handle to hscbs */
+ unsigned int hscbs_dma_len; /* length of the above DMA area */
+ void *hscb_kmalloc_ptr;
+} scb_data_type;
+
+struct target_cmd {
+ unsigned char mesg_bytes[4];
+ unsigned char command[28];
+};
+
+#define AHC_TRANS_CUR 0x0001
+#define AHC_TRANS_ACTIVE 0x0002
+#define AHC_TRANS_GOAL 0x0004
+#define AHC_TRANS_USER 0x0008
+#define AHC_TRANS_QUITE 0x0010
+typedef struct {
+ unsigned char width;
+ unsigned char period;
+ unsigned char offset;
+ unsigned char options;
+} transinfo_type;
+
+struct aic_dev_data {
+ volatile scb_queue_type delayed_scbs;
+ volatile unsigned short temp_q_depth;
+ unsigned short max_q_depth;
+ volatile unsigned char active_cmds;
+ /*
+ * Statistics Kept:
+ *
+ * Total Xfers (count for each command that has a data xfer),
+ * broken down by reads && writes.
+ *
+ * Further sorted into a few bins for keeping tabs on how many commands
+ * we get of various sizes.
+ *
+ */
+ long w_total; /* total writes */
+ long r_total; /* total reads */
+ long barrier_total; /* total num of REQ_BARRIER commands */
+ long ordered_total; /* How many REQ_BARRIER commands we
+ used ordered tags to satisfy */
+ long w_bins[6]; /* binned write */
+ long r_bins[6]; /* binned reads */
+ transinfo_type cur;
+ transinfo_type goal;
+#define BUS_DEVICE_RESET_PENDING 0x01
+#define DEVICE_RESET_DELAY 0x02
+#define DEVICE_PRINT_DTR 0x04
+#define DEVICE_WAS_BUSY 0x08
+#define DEVICE_DTR_SCANNED 0x10
+#define DEVICE_SCSI_3 0x20
+ volatile unsigned char flags;
+ unsigned needppr:1;
+ unsigned needppr_copy:1;
+ unsigned needsdtr:1;
+ unsigned needsdtr_copy:1;
+ unsigned needwdtr:1;
+ unsigned needwdtr_copy:1;
+ unsigned dtr_pending:1;
+ struct scsi_device *SDptr;
+ struct list_head list;
+};
+
+/*
+ * Define a structure used for each host adapter. Note, in order to avoid
+ * problems with architectures I can't test on (because I don't have one,
+ * such as the Alpha based systems) which happen to give faults for
+ * non-aligned memory accesses, care was taken to align this structure
+ * in a way that gauranteed all accesses larger than 8 bits were aligned
+ * on the appropriate boundary. It's also organized to try and be more
+ * cache line efficient. Be careful when changing this lest you might hurt
+ * overall performance and bring down the wrath of the masses.
+ */
+struct aic7xxx_host {
+ /*
+ * This is the first 64 bytes in the host struct
+ */
+
+ /*
+ * We are grouping things here....first, items that get either read or
+ * written with nearly every interrupt
+ */
+ volatile long flags;
+ ahc_feature features; /* chip features */
+ unsigned long base; /* card base address */
+ volatile unsigned char __iomem *maddr; /* memory mapped address */
+ unsigned long isr_count; /* Interrupt count */
+ unsigned long spurious_int;
+ scb_data_type *scb_data;
+ struct aic7xxx_cmd_queue {
+ Scsi_Cmnd *head;
+ Scsi_Cmnd *tail;
+ } completeq;
+
+ /*
+ * Things read/written on nearly every entry into aic7xxx_queue()
+ */
+ volatile scb_queue_type waiting_scbs;
+ unsigned char unpause; /* unpause value for HCNTRL */
+ unsigned char pause; /* pause value for HCNTRL */
+ volatile unsigned char qoutfifonext;
+ volatile unsigned char activescbs; /* active scbs */
+ volatile unsigned char max_activescbs;
+ volatile unsigned char qinfifonext;
+ volatile unsigned char *untagged_scbs;
+ volatile unsigned char *qoutfifo;
+ volatile unsigned char *qinfifo;
+
+ unsigned char dev_last_queue_full[MAX_TARGETS];
+ unsigned char dev_last_queue_full_count[MAX_TARGETS];
+ unsigned short ultraenb; /* Gets downloaded to card as a
+ bitmap */
+ unsigned short discenable; /* Gets downloaded to card as a
+ bitmap */
+ transinfo_type user[MAX_TARGETS];
+
+ unsigned char msg_buf[13]; /* The message for the target */
+ unsigned char msg_type;
+#define MSG_TYPE_NONE 0x00
+#define MSG_TYPE_INITIATOR_MSGOUT 0x01
+#define MSG_TYPE_INITIATOR_MSGIN 0x02
+ unsigned char msg_len; /* Length of message */
+ unsigned char msg_index; /* Index into msg_buf array */
+
+
+ /*
+ * We put the less frequently used host structure items after the more
+ * frequently used items to try and ease the burden on the cache subsystem.
+ * These entries are not *commonly* accessed, whereas the preceding entries
+ * are accessed very often.
+ */
+
+ unsigned int irq; /* IRQ for this adapter */
+ int instance; /* aic7xxx instance number */
+ int scsi_id; /* host adapter SCSI ID */
+ int scsi_id_b; /* channel B for twin adapters */
+ unsigned int bios_address;
+ int board_name_index;
+ unsigned short bios_control; /* bios control - SEEPROM */
+ unsigned short adapter_control; /* adapter control - SEEPROM */
+ struct pci_dev *pdev;
+ unsigned char pci_bus;
+ unsigned char pci_device_fn;
+ struct seeprom_config sc;
+ unsigned short sc_type;
+ unsigned short sc_size;
+ struct aic7xxx_host *next; /* allow for multiple IRQs */
+ struct Scsi_Host *host; /* pointer to scsi host */
+ struct list_head aic_devs; /* all aic_dev structs on host */
+ int host_no; /* SCSI host number */
+ unsigned long mbase; /* I/O memory address */
+ ahc_chip chip; /* chip type */
+ ahc_bugs bugs;
+ dma_addr_t fifo_dma; /* DMA handle for fifo arrays */
+
+};
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
+ */
+#define AHC_SYNCRATE_ULTRA3 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA 3
+#define AHC_SYNCRATE_FAST 6
+#define AHC_SYNCRATE_CRC 0x40
+#define AHC_SYNCRATE_SE 0x10
+static struct aic7xxx_syncrate {
+ /* Rates in Ultra mode have bit 8 of sxfr set */
+#define ULTRA_SXFR 0x100
+ int sxfr_ultra2;
+ int sxfr;
+ unsigned char period;
+ const char *rate[2];
+} aic7xxx_syncrates[] = {
+ { 0x42, 0x000, 9, {"80.0", "160.0"} },
+ { 0x13, 0x000, 10, {"40.0", "80.0"} },
+ { 0x14, 0x000, 11, {"33.0", "66.6"} },
+ { 0x15, 0x100, 12, {"20.0", "40.0"} },
+ { 0x16, 0x110, 15, {"16.0", "32.0"} },
+ { 0x17, 0x120, 18, {"13.4", "26.8"} },
+ { 0x18, 0x000, 25, {"10.0", "20.0"} },
+ { 0x19, 0x010, 31, {"8.0", "16.0"} },
+ { 0x1a, 0x020, 37, {"6.67", "13.3"} },
+ { 0x1b, 0x030, 43, {"5.7", "11.4"} },
+ { 0x10, 0x040, 50, {"5.0", "10.0"} },
+ { 0x00, 0x050, 56, {"4.4", "8.8" } },
+ { 0x00, 0x060, 62, {"4.0", "8.0" } },
+ { 0x00, 0x070, 68, {"3.6", "7.2" } },
+ { 0x00, 0x000, 0, {NULL, NULL} },
+};
+
+#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \
+ (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+ ((scb->hscb)->target_channel_lun & 0x07)
+
+#define CTL_OF_CMD(cmd) ((cmd->device->channel) & 0x01), \
+ ((cmd->device->id) & 0x0f), \
+ ((cmd->device->lun) & 0x07)
+
+#define TARGET_INDEX(cmd) ((cmd)->device->id | ((cmd)->device->channel << 3))
+
+/*
+ * A nice little define to make doing our printks a little easier
+ */
+
+#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
+#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ * cards in the system. This should be fixed. Exceptions to this
+ * rule are noted in the comments.
+ */
+
+/*
+ * Use this as the default queue depth when setting tagged queueing on.
+ */
+static unsigned int aic7xxx_default_queue_depth = AIC7XXX_CMDS_PER_DEVICE;
+
+/*
+ * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static unsigned int aic7xxx_no_reset = 0;
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips. The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect. Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from. The only exceptions to this are when a controller
+ * has its BIOS disabled. So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number. We also force
+ * all controllers with their BIOS disabled to the end of the list. This
+ * works on *almost* all computers. Where it doesn't work, we have this
+ * option. Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end. That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+/*
+ * Should we force EXTENDED translation on a controller.
+ * 0 == Use whatever is in the SEEPROM or default to off
+ * 1 == Use whatever is in the SEEPROM or default to on
+ */
+static unsigned int aic7xxx_extended = 0;
+/*
+ * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
+ * -1 = Use detected settings.
+ * 0 = Force Edge triggered mode.
+ * 1 = Force Level triggered mode.
+ */
+static int aic7xxx_irq_trigger = -1;
+/*
+ * This variable is used to override the termination settings on a controller.
+ * This should not be used under normal conditions. However, in the case
+ * that a controller does not have a readable SEEPROM (so that we can't
+ * read the SEEPROM settings directly) and that a controller has a buggered
+ * version of the cable detection logic, this can be used to force the
+ * correct termination. It is preferable to use the manual termination
+ * settings in the BIOS if possible, but some motherboard controllers store
+ * those settings in a format we can't read. In other cases, auto term
+ * should also work, but the chipset was put together with no auto term
+ * logic (common on motherboard controllers). In those cases, we have
+ * 32 bits here to work with. That's good for 8 controllers/channels. The
+ * bits are organized as 4 bits per channel, with scsi0 getting the lowest
+ * 4 bits in the int. A 1 in a bit position indicates the termination setting
+ * that corresponds to that bit should be enabled, a 0 is disabled.
+ * It looks something like this:
+ *
+ * 0x0f = 1111-Single Ended Low Byte Termination on/off
+ * ||\-Single Ended High Byte Termination on/off
+ * |\-LVD Low Byte Termination on/off
+ * \-LVD High Byte Termination on/off
+ *
+ * For non-Ultra2 controllers, the upper 2 bits are not important. So, to
+ * enable both high byte and low byte termination on scsi0, I would need to
+ * make sure that the override_term variable was set to 0x03 (bits 0011).
+ * To make sure that all termination is enabled on an Ultra2 controller at
+ * scsi2 and only high byte termination on scsi1 and high and low byte
+ * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
+ *
+ * For the most part, users should never have to use this, that's why I
+ * left it fairly cryptic instead of easy to understand. If you need it,
+ * most likely someone will be telling you what your's needs to be set to.
+ */
+static int aic7xxx_override_term = -1;
+/*
+ * Certain motherboard chipset controllers tend to screw
+ * up the polarity of the term enable output pin. Use this variable
+ * to force the correct polarity for your system. This is a bitfield variable
+ * similar to the previous one, but this one has one bit per channel instead
+ * of four.
+ * 0 = Force the setting to active low.
+ * 1 = Force setting to active high.
+ * Most Adaptec cards are active high, several motherboards are active low.
+ * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
+ * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
+ * to active high, you would need to set stpwlev=0x9 (bits 1001).
+ *
+ * People shouldn't need to use this, but if you are experiencing lots of
+ * SCSI timeout problems, this may help. There is one sure way to test what
+ * this option needs to be. Using a boot floppy to boot the system, configure
+ * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
+ * if needed then also pass a value to override_term to make sure that the
+ * driver is enabling SCSI termination, then set this variable to either 0
+ * or 1. When the driver boots, make sure there are *NO* SCSI cables
+ * connected to your controller. If it finds and inits the controller
+ * without problem, then the setting you passed to stpwlev was correct. If
+ * the driver goes into a reset loop and hangs the system, then you need the
+ * other setting for this variable. If neither setting lets the machine
+ * boot then you have definite termination problems that may not be fixable.
+ */
+static int aic7xxx_stpwlev = -1;
+/*
+ * Set this to non-0 in order to force the driver to panic the kernel
+ * and print out debugging info on a SCSI abort or reset cycle.
+ */
+static int aic7xxx_panic_on_abort = 0;
+/*
+ * PCI bus parity checking of the Adaptec controllers. This is somewhat
+ * dubious at best. To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ * 0 = Shut off PCI parity check
+ * -1 = Normal polarity pci parity checking
+ * 1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number. That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+/*
+ * Set this to any non-0 value to cause us to dump the contents of all
+ * the card's registers in a hex dump format tailored to each model of
+ * controller.
+ *
+ * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION.
+ * YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
+ * ONLY
+ */
+static int aic7xxx_dump_card = 0;
+/*
+ * Set this to a non-0 value to make us dump out the 32 bit instruction
+ * registers on the card after completing the sequencer download. This
+ * allows the actual sequencer download to be verified. It is possible
+ * to use this option and still boot up and run your system. This is
+ * only intended for debugging purposes.
+ */
+static int aic7xxx_dump_sequencer = 0;
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices. Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers. NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+static int aic7xxx_no_probe = 0;
+/*
+ * On some machines, enabling the external SCB RAM isn't reliable yet. I
+ * haven't had time to make test patches for things like changing the
+ * timing mode on that external RAM either. Some of those changes may
+ * fix the problem. Until then though, we default to external SCB RAM
+ * off and give a command line option to enable it.
+ */
+static int aic7xxx_scbram = 0;
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ * 0 - 256ms
+ * 1 - 128ms
+ * 2 - 64ms
+ * 3 - 32ms
+ * We default to 64ms because it's fast. Some old SCSI-I devices need a
+ * longer time. The final value has to be left shifted by 3, hence 0x10
+ * is the final value.
+ */
+static int aic7xxx_seltime = 0x10;
+/*
+ * So that insmod can find the variable and make it point to something
+ */
+#ifdef MODULE
+static char * aic7xxx = NULL;
+module_param(aic7xxx, charp, 0);
+#endif
+
+#define VERBOSE_NORMAL 0x0000
+#define VERBOSE_NEGOTIATION 0x0001
+#define VERBOSE_SEQINT 0x0002
+#define VERBOSE_SCSIINT 0x0004
+#define VERBOSE_PROBE 0x0008
+#define VERBOSE_PROBE2 0x0010
+#define VERBOSE_NEGOTIATION2 0x0020
+#define VERBOSE_MINOR_ERROR 0x0040
+#define VERBOSE_TRACING 0x0080
+#define VERBOSE_ABORT 0x0f00
+#define VERBOSE_ABORT_MID 0x0100
+#define VERBOSE_ABORT_FIND 0x0200
+#define VERBOSE_ABORT_PROCESS 0x0400
+#define VERBOSE_ABORT_RETURN 0x0800
+#define VERBOSE_RESET 0xf000
+#define VERBOSE_RESET_MID 0x1000
+#define VERBOSE_RESET_FIND 0x2000
+#define VERBOSE_RESET_PROCESS 0x4000
+#define VERBOSE_RESET_RETURN 0x8000
+static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
+ VERBOSE_PROBE; /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * We're going to start putting in function declarations so that order of
+ * functions is no longer important. As needed, they are added here.
+ *
+ ***************************************************************************/
+
+static int aic7xxx_release(struct Scsi_Host *host);
+static void aic7xxx_set_syncrate(struct aic7xxx_host *p,
+ struct aic7xxx_syncrate *syncrate, int target, int channel,
+ unsigned int period, unsigned int offset, unsigned char options,
+ unsigned int type, struct aic_dev_data *aic_dev);
+static void aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel,
+ int lun, unsigned int width, unsigned int type,
+ struct aic_dev_data *aic_dev);
+static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd);
+static void aic7xxx_print_card(struct aic7xxx_host *p);
+static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
+static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
+#endif
+
+/****************************************************************************
+ *
+ * These functions are now used. They happen to be wrapped in useless
+ * inb/outb port read/writes around the real reads and writes because it
+ * seems that certain very fast CPUs have a problem dealing with us when
+ * going at full speed.
+ *
+ ***************************************************************************/
+
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+#ifdef MMAPIO
+ unsigned char x;
+ if(p->maddr)
+ {
+ x = readb(p->maddr + port);
+ }
+ else
+ {
+ x = inb(p->base + port);
+ }
+ return(x);
+#else
+ return(inb(p->base + port));
+#endif
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+#ifdef MMAPIO
+ if(p->maddr)
+ {
+ writeb(val, p->maddr + port);
+ mb(); /* locked operation in order to force CPU ordering */
+ readb(p->maddr + HCNTRL); /* dummy read to flush the PCI write */
+ }
+ else
+ {
+ outb(val, p->base + port);
+ mb(); /* locked operation in order to force CPU ordering */
+ }
+#else
+ outb(val, p->base + port);
+ mb(); /* locked operation in order to force CPU ordering */
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_setup
+ *
+ * Description:
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=unpause:0x0A,extended
+ *-F*************************************************************************/
+static int
+aic7xxx_setup(char *s)
+{
+ int i, n;
+ char *p;
+ char *end;
+
+ static struct {
+ const char *name;
+ unsigned int *flag;
+ } options[] = {
+ { "extended", &aic7xxx_extended },
+ { "no_reset", &aic7xxx_no_reset },
+ { "irq_trigger", &aic7xxx_irq_trigger },
+ { "verbose", &aic7xxx_verbose },
+ { "reverse_scan",&aic7xxx_reverse_scan },
+ { "override_term", &aic7xxx_override_term },
+ { "stpwlev", &aic7xxx_stpwlev },
+ { "no_probe", &aic7xxx_no_probe },
+ { "panic_on_abort", &aic7xxx_panic_on_abort },
+ { "pci_parity", &aic7xxx_pci_parity },
+ { "dump_card", &aic7xxx_dump_card },
+ { "dump_sequencer", &aic7xxx_dump_sequencer },
+ { "default_queue_depth", &aic7xxx_default_queue_depth },
+ { "scbram", &aic7xxx_scbram },
+ { "seltime", &aic7xxx_seltime },
+ { "tag_info", NULL }
+ };
+
+ end = strchr(s, '\0');
+
+ while ((p = strsep(&s, ",.")) != NULL)
+ {
+ for (i = 0; i < ARRAY_SIZE(options); i++)
+ {
+ n = strlen(options[i].name);
+ if (!strncmp(options[i].name, p, n))
+ {
+ if (!strncmp(p, "tag_info", n))
+ {
+ if (p[n] == ':')
+ {
+ char *base;
+ char *tok, *tok_end, *tok_end2;
+ char tok_list[] = { '.', ',', '{', '}', '\0' };
+ int i, instance = -1, device = -1;
+ unsigned char done = FALSE;
+
+ base = p;
+ tok = base + n + 1; /* Forward us just past the ':' */
+ tok_end = strchr(tok, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while(!done)
+ {
+ switch(*tok)
+ {
+ case '{':
+ if (instance == -1)
+ instance = 0;
+ else if (device == -1)
+ device = 0;
+ tok++;
+ break;
+ case '}':
+ if (device != -1)
+ device = -1;
+ else if (instance != -1)
+ instance = -1;
+ tok++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (device >= 0)
+ device++;
+ else if (instance >= 0)
+ instance++;
+ if ( (device >= MAX_TARGETS) ||
+ (instance >= ARRAY_SIZE(aic7xxx_tag_info)) )
+ done = TRUE;
+ tok++;
+ if (!done)
+ {
+ base = tok;
+ }
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ done = TRUE;
+ tok_end = strchr(tok, '\0');
+ for(i=0; tok_list[i]; i++)
+ {
+ tok_end2 = strchr(tok, tok_list[i]);
+ if ( (tok_end2) && (tok_end2 < tok_end) )
+ {
+ tok_end = tok_end2;
+ done = FALSE;
+ }
+ }
+ if ( (instance >= 0) && (device >= 0) &&
+ (instance < ARRAY_SIZE(aic7xxx_tag_info)) &&
+ (device < MAX_TARGETS) )
+ aic7xxx_tag_info[instance].tag_commands[device] =
+ simple_strtoul(tok, NULL, 0) & 0xff;
+ tok = tok_end;
+ break;
+ }
+ }
+ while((p != base) && (p != NULL))
+ p = strsep(&s, ",.");
+ }
+ }
+ else if (p[n] == ':')
+ {
+ *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+ if(!strncmp(p, "seltime", n))
+ {
+ *(options[i].flag) = (*(options[i].flag) % 4) << 3;
+ }
+ }
+ else if (!strncmp(p, "verbose", n))
+ {
+ *(options[i].flag) = 0xff29;
+ }
+ else
+ {
+ *(options[i].flag) = ~(*(options[i].flag));
+ if(!strncmp(p, "seltime", n))
+ {
+ *(options[i].flag) = (*(options[i].flag) % 4) << 3;
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+__setup("aic7xxx=", aic7xxx_setup);
+
+/*+F*************************************************************************
+ * Function:
+ * pause_sequencer
+ *
+ * Description:
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ *-F*************************************************************************/
+static void
+pause_sequencer(struct aic7xxx_host *p)
+{
+ aic_outb(p, p->pause, HCNTRL);
+ while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
+ {
+ ;
+ }
+ if(p->features & AHC_ULTRA2)
+ {
+ aic_inb(p, CCSCBCTL);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * unpause_sequencer
+ *
+ * Description:
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ *-F*************************************************************************/
+static void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
+{
+ if (unpause_always ||
+ ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
+ !(p->flags & AHC_HANDLING_REQINITS) ) )
+ {
+ aic_outb(p, p->unpause, HCNTRL);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * restart_sequencer
+ *
+ * Description:
+ * Restart the sequencer program from address zero. This assumes
+ * that the sequencer is already paused.
+ *-F*************************************************************************/
+static void
+restart_sequencer(struct aic7xxx_host *p)
+{
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+ aic_outb(p, FASTMODE, SEQCTL);
+}
+
+/*
+ * We include the aic7xxx_seq.c file here so that the other defines have
+ * already been made, and so that it comes before the code that actually
+ * downloads the instructions (since we don't typically use function
+ * prototype, our code has to be ordered that way, it's a left-over from
+ * the original driver days.....I should fix it some time DL).
+ */
+#include "aic7xxx_old/aic7xxx_seq.c"
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_check_patch
+ *
+ * Description:
+ * See if the next patch to download should be downloaded.
+ *-F*************************************************************************/
+static int
+aic7xxx_check_patch(struct aic7xxx_host *p,
+ struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
+{
+ struct sequencer_patch *cur_patch;
+ struct sequencer_patch *last_patch;
+ int num_patches;
+
+ num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch);
+ last_patch = &sequencer_patches[num_patches];
+ cur_patch = *start_patch;
+
+ while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
+ {
+ if (cur_patch->patch_func(p) == 0)
+ {
+ /*
+ * Start rejecting code.
+ */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ }
+ else
+ {
+ /*
+ * Found an OK patch. Advance the patch pointer to the next patch
+ * and wait for our instruction pointer to get here.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /*
+ * Still skipping
+ */
+ return (0);
+ return(1);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_download_instr
+ *
+ * Description:
+ * Find the next patch to download.
+ *-F*************************************************************************/
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
+ unsigned char *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ unsigned char opcode;
+
+ instr = *(union ins_formats*) &seqprog[instrptr * 4];
+
+ instr.integer = le32_to_cpu(instr.integer);
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode)
+ {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ struct sequencer_patch *cur_patch;
+ int address_offset;
+ unsigned int address;
+ int skip_addr;
+ int i;
+
+ fmt3_ins = &instr.format3;
+ address_offset = 0;
+ address = fmt3_ins->address;
+ cur_patch = sequencer_patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;)
+ {
+ aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
+ if (skip_addr > i)
+ {
+ int end_addr;
+
+ end_addr = min_t(int, address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ address -= address_offset;
+ fmt3_ins->address = address;
+ /* Fall Through to the next code section */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0)
+ {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ /* Fall Through to the next code section */
+ case AIC_OP_ROL:
+ if ((p->features & AHC_ULTRA2) != 0)
+ {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for ( i=0, count=0; i < 31; i++)
+ {
+ unsigned int mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if (!(count & 0x01))
+ instr.format1.parity = 1;
+ }
+ else
+ {
+ if (fmt3_ins != NULL)
+ {
+ instr.integer = fmt3_ins->immediate |
+ (fmt3_ins->source << 8) |
+ (fmt3_ins->address << 16) |
+ (fmt3_ins->opcode << 25);
+ }
+ else
+ {
+ instr.integer = fmt1_ins->immediate |
+ (fmt1_ins->source << 8) |
+ (fmt1_ins->destination << 16) |
+ (fmt1_ins->ret << 24) |
+ (fmt1_ins->opcode << 25);
+ }
+ }
+ aic_outb(p, (instr.integer & 0xff), SEQRAM);
+ aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
+ aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
+ aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
+ udelay(10);
+ break;
+
+ default:
+ panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+ break;
+ }
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_loadseq
+ *
+ * Description:
+ * Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(struct aic7xxx_host *p)
+{
+ struct sequencer_patch *cur_patch;
+ int i;
+ int downloaded;
+ int skip_addr;
+ unsigned char download_consts[4] = {0, 0, 0, 0};
+
+ if (aic7xxx_verbose & VERBOSE_PROBE)
+ {
+ printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
+ }
+#if 0
+ download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+#endif
+ download_consts[TMODE_NUMCMDS] = 0;
+ cur_patch = &sequencer_patches[0];
+ downloaded = 0;
+ skip_addr = 0;
+
+ aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+
+ for (i = 0; i < sizeof(seqprog) / 4; i++)
+ {
+ if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
+ {
+ /* Skip this instruction for this configuration. */
+ continue;
+ }
+ aic7xxx_download_instr(p, i, &download_consts[0]);
+ downloaded++;
+ }
+
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+ aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+ unpause_sequencer(p, TRUE);
+ mdelay(1);
+ pause_sequencer(p);
+ aic_outb(p, FASTMODE, SEQCTL);
+ if (aic7xxx_verbose & VERBOSE_PROBE)
+ {
+ printk(" %d instructions downloaded\n", downloaded);
+ }
+ if (aic7xxx_dump_sequencer)
+ aic7xxx_print_sequencer(p, downloaded);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_sequencer
+ *
+ * Description:
+ * Print the contents of the sequencer memory to the screen.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
+{
+ int i, k, temp;
+
+ aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+
+ k = 0;
+ for (i=0; i < downloaded; i++)
+ {
+ if ( k == 0 )
+ printk("%03x: ", i);
+ temp = aic_inb(p, SEQRAM);
+ temp |= (aic_inb(p, SEQRAM) << 8);
+ temp |= (aic_inb(p, SEQRAM) << 16);
+ temp |= (aic_inb(p, SEQRAM) << 24);
+ printk("%08x", temp);
+ if ( ++k == 8 )
+ {
+ printk("\n");
+ k = 0;
+ }
+ else
+ printk(" ");
+ }
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+ aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+ unpause_sequencer(p, TRUE);
+ mdelay(1);
+ pause_sequencer(p);
+ aic_outb(p, FASTMODE, SEQCTL);
+ printk("\n");
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_info
+ *
+ * Description:
+ * Return a string describing the driver.
+ *-F*************************************************************************/
+static const char *
+aic7xxx_info(struct Scsi_Host *dooh)
+{
+ static char buffer[256];
+ char *bp;
+ struct aic7xxx_host *p;
+
+ bp = &buffer[0];
+ p = (struct aic7xxx_host *)dooh->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+ strcat(bp, AIC7XXX_C_VERSION);
+ strcat(bp, "/");
+ strcat(bp, AIC7XXX_H_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, board_names[p->board_name_index]);
+ strcat(bp, ">");
+
+ return(bp);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_find_syncrate
+ *
+ * Description:
+ * Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static struct aic7xxx_syncrate *
+aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
+ unsigned int maxsync, unsigned char *options)
+{
+ struct aic7xxx_syncrate *syncrate;
+ int done = FALSE;
+
+ switch(*options)
+ {
+ case MSG_EXT_PPR_OPTION_DT_CRC:
+ case MSG_EXT_PPR_OPTION_DT_UNITS:
+ if(!(p->features & AHC_ULTRA3))
+ {
+ *options = 0;
+ maxsync = max_t(unsigned int, maxsync, AHC_SYNCRATE_ULTRA2);
+ }
+ break;
+ case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+ case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+ if(!(p->features & AHC_ULTRA3))
+ {
+ *options = 0;
+ maxsync = max_t(unsigned int, maxsync, AHC_SYNCRATE_ULTRA2);
+ }
+ else
+ {
+ /*
+ * we don't support the Quick Arbitration variants of dual edge
+ * clocking. As it turns out, we want to send back the
+ * same basic option, but without the QA attribute.
+ * We know that we are responding because we would never set
+ * these options ourself, we would only respond to them.
+ */
+ switch(*options)
+ {
+ case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+ *options = MSG_EXT_PPR_OPTION_DT_CRC;
+ break;
+ case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+ *options = MSG_EXT_PPR_OPTION_DT_UNITS;
+ break;
+ }
+ }
+ break;
+ default:
+ *options = 0;
+ maxsync = max_t(unsigned int, maxsync, AHC_SYNCRATE_ULTRA2);
+ break;
+ }
+ syncrate = &aic7xxx_syncrates[maxsync];
+ while ( (syncrate->rate[0] != NULL) &&
+ (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
+ {
+ if (*period <= syncrate->period)
+ {
+ switch(*options)
+ {
+ case MSG_EXT_PPR_OPTION_DT_CRC:
+ case MSG_EXT_PPR_OPTION_DT_UNITS:
+ if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+ {
+ done = TRUE;
+ /*
+ * oops, we went too low for the CRC/DualEdge signalling, so
+ * clear the options byte
+ */
+ *options = 0;
+ /*
+ * We'll be sending a reply to this packet to set the options
+ * properly, so unilaterally set the period as well.
+ */
+ *period = syncrate->period;
+ }
+ else
+ {
+ done = TRUE;
+ if(syncrate == &aic7xxx_syncrates[maxsync])
+ {
+ *period = syncrate->period;
+ }
+ }
+ break;
+ default:
+ if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+ {
+ done = TRUE;
+ if(syncrate == &aic7xxx_syncrates[maxsync])
+ {
+ *period = syncrate->period;
+ }
+ }
+ break;
+ }
+ if(done)
+ {
+ break;
+ }
+ }
+ syncrate++;
+ }
+ if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
+ ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
+ {
+ /*
+ * Use async transfers for this target
+ */
+ *options = 0;
+ *period = 255;
+ syncrate = NULL;
+ }
+ return (syncrate);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_find_period
+ *
+ * Description:
+ * Look up the valid SCSIRATE to period conversion in our table
+ *-F*************************************************************************/
+static unsigned int
+aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
+ unsigned int maxsync)
+{
+ struct aic7xxx_syncrate *syncrate;
+
+ if (p->features & AHC_ULTRA2)
+ {
+ scsirate &= SXFR_ULTRA2;
+ }
+ else
+ {
+ scsirate &= SXFR;
+ }
+
+ syncrate = &aic7xxx_syncrates[maxsync];
+ while (syncrate->rate[0] != NULL)
+ {
+ if (p->features & AHC_ULTRA2)
+ {
+ if (syncrate->sxfr_ultra2 == 0)
+ break;
+ else if (scsirate == syncrate->sxfr_ultra2)
+ return (syncrate->period);
+ else if (scsirate == (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC))
+ return (syncrate->period);
+ }
+ else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
+ {
+ return (syncrate->period);
+ }
+ syncrate++;
+ }
+ return (0); /* async */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_validate_offset
+ *
+ * Description:
+ * Set a valid offset value for a particular card in use and transfer
+ * settings in use.
+ *-F*************************************************************************/
+static void
+aic7xxx_validate_offset(struct aic7xxx_host *p,
+ struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
+{
+ unsigned int maxoffset;
+
+ /* Limit offset to what the card (and device) can do */
+ if (syncrate == NULL)
+ {
+ maxoffset = 0;
+ }
+ else if (p->features & AHC_ULTRA2)
+ {
+ maxoffset = MAX_OFFSET_ULTRA2;
+ }
+ else
+ {
+ if (wide)
+ maxoffset = MAX_OFFSET_16BIT;
+ else
+ maxoffset = MAX_OFFSET_8BIT;
+ }
+ *offset = min(*offset, maxoffset);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_syncrate
+ *
+ * Description:
+ * Set the actual syncrate down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
+ int target, int channel, unsigned int period, unsigned int offset,
+ unsigned char options, unsigned int type, struct aic_dev_data *aic_dev)
+{
+ unsigned char tindex;
+ unsigned short target_mask;
+ unsigned char lun, old_options;
+ unsigned int old_period, old_offset;
+
+ tindex = target | (channel << 3);
+ target_mask = 0x01 << tindex;
+ lun = aic_inb(p, SCB_TCL) & 0x07;
+
+ if (syncrate == NULL)
+ {
+ period = 0;
+ offset = 0;
+ }
+
+ old_period = aic_dev->cur.period;
+ old_offset = aic_dev->cur.offset;
+ old_options = aic_dev->cur.options;
+
+
+ if (type & AHC_TRANS_CUR)
+ {
+ unsigned int scsirate;
+
+ scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+ if (p->features & AHC_ULTRA2)
+ {
+ scsirate &= ~SXFR_ULTRA2;
+ if (syncrate != NULL)
+ {
+ switch(options)
+ {
+ case MSG_EXT_PPR_OPTION_DT_UNITS:
+ /*
+ * mask off the CRC bit in the xfer settings
+ */
+ scsirate |= (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC);
+ break;
+ default:
+ scsirate |= syncrate->sxfr_ultra2;
+ break;
+ }
+ }
+ if (type & AHC_TRANS_ACTIVE)
+ {
+ aic_outb(p, offset, SCSIOFFSET);
+ }
+ aic_outb(p, offset, TARG_OFFSET + tindex);
+ }
+ else /* Not an Ultra2 controller */
+ {
+ scsirate &= ~(SXFR|SOFS);
+ p->ultraenb &= ~target_mask;
+ if (syncrate != NULL)
+ {
+ if (syncrate->sxfr & ULTRA_SXFR)
+ {
+ p->ultraenb |= target_mask;
+ }
+ scsirate |= (syncrate->sxfr & SXFR);
+ scsirate |= (offset & SOFS);
+ }
+ if (type & AHC_TRANS_ACTIVE)
+ {
+ unsigned char sxfrctl0;
+
+ sxfrctl0 = aic_inb(p, SXFRCTL0);
+ sxfrctl0 &= ~FAST20;
+ if (p->ultraenb & target_mask)
+ sxfrctl0 |= FAST20;
+ aic_outb(p, sxfrctl0, SXFRCTL0);
+ }
+ aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+ aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
+ }
+ if (type & AHC_TRANS_ACTIVE)
+ {
+ aic_outb(p, scsirate, SCSIRATE);
+ }
+ aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+ aic_dev->cur.period = period;
+ aic_dev->cur.offset = offset;
+ aic_dev->cur.options = options;
+ if ( !(type & AHC_TRANS_QUITE) &&
+ (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (aic_dev->flags & DEVICE_PRINT_DTR) )
+ {
+ if (offset)
+ {
+ int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
+
+ printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
+ "offset %d.\n", p->host_no, channel, target, lun,
+ syncrate->rate[rate_mod], offset);
+ }
+ else
+ {
+ printk(INFO_LEAD "Using asynchronous transfers.\n",
+ p->host_no, channel, target, lun);
+ }
+ aic_dev->flags &= ~DEVICE_PRINT_DTR;
+ }
+ }
+
+ if (type & AHC_TRANS_GOAL)
+ {
+ aic_dev->goal.period = period;
+ aic_dev->goal.offset = offset;
+ aic_dev->goal.options = options;
+ }
+
+ if (type & AHC_TRANS_USER)
+ {
+ p->user[tindex].period = period;
+ p->user[tindex].offset = offset;
+ p->user[tindex].options = options;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_width
+ *
+ * Description:
+ * Set the actual width down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
+ unsigned int width, unsigned int type, struct aic_dev_data *aic_dev)
+{
+ unsigned char tindex;
+ unsigned short target_mask;
+ unsigned int old_width;
+
+ tindex = target | (channel << 3);
+ target_mask = 1 << tindex;
+
+ old_width = aic_dev->cur.width;
+
+ if (type & AHC_TRANS_CUR)
+ {
+ unsigned char scsirate;
+
+ scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+
+ scsirate &= ~WIDEXFER;
+ if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ scsirate |= WIDEXFER;
+
+ aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+
+ if (type & AHC_TRANS_ACTIVE)
+ aic_outb(p, scsirate, SCSIRATE);
+
+ aic_dev->cur.width = width;
+
+ if ( !(type & AHC_TRANS_QUITE) &&
+ (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (aic_dev->flags & DEVICE_PRINT_DTR) )
+ {
+ printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
+ lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
+ }
+ }
+
+ if (type & AHC_TRANS_GOAL)
+ aic_dev->goal.width = width;
+ if (type & AHC_TRANS_USER)
+ p->user[tindex].width = width;
+
+ if (aic_dev->goal.offset)
+ {
+ if (p->features & AHC_ULTRA2)
+ {
+ aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
+ }
+ else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ {
+ aic_dev->goal.offset = MAX_OFFSET_16BIT;
+ }
+ else
+ {
+ aic_dev->goal.offset = MAX_OFFSET_8BIT;
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_init
+ *
+ * Description:
+ * SCB queue initialization.
+ *
+ *-F*************************************************************************/
+static void
+scbq_init(volatile scb_queue_type *queue)
+{
+ queue->head = NULL;
+ queue->tail = NULL;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_insert_head
+ *
+ * Description:
+ * Add an SCB to the head of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+ scb->q_next = queue->head;
+ queue->head = scb;
+ if (queue->tail == NULL) /* If list was empty, update tail. */
+ queue->tail = queue->head;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_remove_head
+ *
+ * Description:
+ * Remove an SCB from the head of the list.
+ *
+ *-F*************************************************************************/
+static inline struct aic7xxx_scb *
+scbq_remove_head(volatile scb_queue_type *queue)
+{
+ struct aic7xxx_scb * scbp;
+
+ scbp = queue->head;
+ if (queue->head != NULL)
+ queue->head = queue->head->q_next;
+ if (queue->head == NULL) /* If list is now empty, update tail. */
+ queue->tail = NULL;
+ return(scbp);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_remove
+ *
+ * Description:
+ * Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+ if (queue->head == scb)
+ {
+ /* At beginning of queue, remove from head. */
+ scbq_remove_head(queue);
+ }
+ else
+ {
+ struct aic7xxx_scb *curscb = queue->head;
+
+ /*
+ * Search until the next scb is the one we're looking for, or
+ * we run out of queue.
+ */
+ while ((curscb != NULL) && (curscb->q_next != scb))
+ {
+ curscb = curscb->q_next;
+ }
+ if (curscb != NULL)
+ {
+ /* Found it. */
+ curscb->q_next = scb->q_next;
+ if (scb->q_next == NULL)
+ {
+ /* Update the tail when removing the tail. */
+ queue->tail = curscb;
+ }
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_insert_tail
+ *
+ * Description:
+ * Add an SCB at the tail of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+ scb->q_next = NULL;
+ if (queue->tail != NULL) /* Add the scb at the end of the list. */
+ queue->tail->q_next = scb;
+ queue->tail = scb; /* Update the tail. */
+ if (queue->head == NULL) /* If list was empty, update head. */
+ queue->head = queue->tail;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_match_scb
+ *
+ * Description:
+ * Checks to see if an scb matches the target/channel as specified.
+ * If target is ALL_TARGETS (-1), then we're looking for any device
+ * on the specified channel; this happens when a channel is going
+ * to be reset and all devices on that channel must be aborted.
+ *-F*************************************************************************/
+static int
+aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ int target, int channel, int lun, unsigned char tag)
+{
+ int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+ int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
+ int slun = scb->hscb->target_channel_lun & 0x07;
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == ALL_TARGETS));
+ if (match != 0)
+ match = ((lun == slun) || (lun == ALL_LUNS));
+ if (match != 0)
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+ return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ * Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+ /*
+ * Invalidate the tag so that aic7xxx_find_scb doesn't think
+ * it's active
+ */
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+
+ aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
+ aic_outb(p, aic_inb(p, SCBPTR), FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ * Removes the current SCB from the disconnected list and adds it
+ * to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr,
+ unsigned char prev)
+{
+ unsigned char next;
+
+ aic_outb(p, scbptr, SCBPTR);
+ next = aic_inb(p, SCB_NEXT);
+ aic7xxx_add_curscb_to_free_list(p);
+
+ if (prev != SCB_LIST_NULL)
+ {
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
+ }
+ else
+ {
+ aic_outb(p, next, DISCONNECTED_SCBH);
+ }
+
+ return next;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_busy_target
+ *
+ * Description:
+ * Set the specified target busy.
+ *-F*************************************************************************/
+static inline void
+aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_index_busy_target
+ *
+ * Description:
+ * Returns the index of the busy target, and optionally sets the
+ * target inactive.
+ *-F*************************************************************************/
+static inline unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
+ int unbusy)
+{
+ unsigned char busy_scbid;
+
+ busy_scbid = p->untagged_scbs[tcl];
+ if (unbusy)
+ {
+ p->untagged_scbs[tcl] = SCB_LIST_NULL;
+ }
+ return (busy_scbid);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_find_scb
+ *
+ * Description:
+ * Look through the SCB array of the card and attempt to find the
+ * hardware SCB that corresponds to the passed in SCB. Return
+ * SCB_LIST_NULL if unsuccessful. This routine assumes that the
+ * card is already paused.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ unsigned char saved_scbptr;
+ unsigned char curindex;
+
+ saved_scbptr = aic_inb(p, SCBPTR);
+ curindex = 0;
+ for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
+ {
+ aic_outb(p, curindex, SCBPTR);
+ if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
+ {
+ break;
+ }
+ }
+ aic_outb(p, saved_scbptr, SCBPTR);
+ if (curindex >= p->scb_data->maxhscbs)
+ {
+ curindex = SCB_LIST_NULL;
+ }
+
+ return (curindex);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_allocate_scb
+ *
+ * Description:
+ * Get an SCB from the free list or by allocating a new one.
+ *-F*************************************************************************/
+static int
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
+{
+ struct aic7xxx_scb *scbp = NULL;
+ int scb_size = (sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG) + 12 + 6;
+ int i;
+ int step = PAGE_SIZE / 1024;
+ unsigned long scb_count = 0;
+ struct hw_scatterlist *hsgp;
+ struct aic7xxx_scb *scb_ap;
+ struct aic7xxx_scb_dma *scb_dma;
+ unsigned char *bufs;
+
+ if (p->scb_data->numscbs < p->scb_data->maxscbs)
+ {
+ /*
+ * Calculate the optimal number of SCBs to allocate.
+ *
+ * NOTE: This formula works because the sizeof(sg_array) is always
+ * 1024. Therefore, scb_size * i would always be > PAGE_SIZE *
+ * (i/step). The (i-1) allows the left hand side of the equation
+ * to grow into the right hand side to a point of near perfect
+ * efficiency since scb_size * (i -1) is growing slightly faster
+ * than the right hand side. If the number of SG array elements
+ * is changed, this function may not be near so efficient any more.
+ *
+ * Since the DMA'able buffers are now allocated in a separate
+ * chunk this algorithm has been modified to match. The '12'
+ * and '6' factors in scb_size are for the DMA'able command byte
+ * and sensebuffers respectively. -DaveM
+ */
+ for ( i=step;; i *= 2 )
+ {
+ if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
+ {
+ i /= 2;
+ break;
+ }
+ }
+ scb_count = min( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
+ scb_ap = (struct aic7xxx_scb *)kmalloc(sizeof (struct aic7xxx_scb) * scb_count
+ + sizeof(struct aic7xxx_scb_dma), GFP_ATOMIC);
+ if (scb_ap == NULL)
+ return(0);
+ scb_dma = (struct aic7xxx_scb_dma *)&scb_ap[scb_count];
+ hsgp = (struct hw_scatterlist *)
+ pci_alloc_consistent(p->pdev, scb_size * scb_count,
+ &scb_dma->dma_address);
+ if (hsgp == NULL)
+ {
+ kfree(scb_ap);
+ return(0);
+ }
+ bufs = (unsigned char *)&hsgp[scb_count * AIC7XXX_MAX_SG];
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ if (p->scb_data->numscbs == 0)
+ printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
+ p->host_no, -1, -1, -1, scb_count);
+ else
+ printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
+ p->host_no, -1, -1, -1, scb_count);
+ }
+#endif
+ memset(scb_ap, 0, sizeof (struct aic7xxx_scb) * scb_count);
+ scb_dma->dma_offset = (unsigned long)scb_dma->dma_address
+ - (unsigned long)hsgp;
+ scb_dma->dma_len = scb_size * scb_count;
+ for (i=0; i < scb_count; i++)
+ {
+ scbp = &scb_ap[i];
+ scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
+ scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
+ scbp->sense_cmd = bufs;
+ scbp->cmnd = bufs + 6;
+ bufs += 12 + 6;
+ scbp->scb_dma = scb_dma;
+ memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
+ scbp->hscb->tag = p->scb_data->numscbs;
+ /*
+ * Place in the scb array; never is removed
+ */
+ p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
+ scbq_insert_tail(&p->scb_data->free_scbs, scbp);
+ }
+ scbp->kmalloc_ptr = scb_ap;
+ }
+ return(scb_count);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_queue_cmd_complete
+ *
+ * Description:
+ * Due to race conditions present in the SCSI subsystem, it is easier
+ * to queue completed commands, then call scsi_done() on them when
+ * we're finished. This function queues the completed commands.
+ *-F*************************************************************************/
+static void
+aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+ aic7xxx_position(cmd) = SCB_LIST_NULL;
+ cmd->host_scribble = (char *)p->completeq.head;
+ p->completeq.head = cmd;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_done_cmds_complete
+ *
+ * Description:
+ * Process the completed command queue.
+ *-F*************************************************************************/
+static void
+aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
+{
+ Scsi_Cmnd *cmd;
+
+ while (p->completeq.head != NULL)
+ {
+ cmd = p->completeq.head;
+ p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ cmd->scsi_done(cmd);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_free_scb
+ *
+ * Description:
+ * Free the scb and insert into the free scb list.
+ *-F*************************************************************************/
+static void
+aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+
+ scb->flags = SCB_FREE;
+ scb->cmd = NULL;
+ scb->sg_count = 0;
+ scb->sg_length = 0;
+ scb->tag_action = 0;
+ scb->hscb->control = 0;
+ scb->hscb->target_status = 0;
+ scb->hscb->target_channel_lun = SCB_LIST_NULL;
+
+ scbq_insert_head(&p->scb_data->free_scbs, scb);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_done
+ *
+ * Description:
+ * Calls the higher level scsi done function and frees the scb.
+ *-F*************************************************************************/
+static void
+aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ Scsi_Cmnd *cmd = scb->cmd;
+ struct aic_dev_data *aic_dev = cmd->device->hostdata;
+ int tindex = TARGET_INDEX(cmd);
+ struct aic7xxx_scb *scbp;
+ unsigned char queue_depth;
+
+ if (cmd->use_sg > 1)
+ {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ }
+ else if (cmd->request_bufflen)
+ pci_unmap_single(p->pdev, aic7xxx_mapping(cmd),
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ if (scb->flags & SCB_SENSE)
+ {
+ pci_unmap_single(p->pdev,
+ le32_to_cpu(scb->sg_list[0].address),
+ sizeof(cmd->sense_buffer),
+ PCI_DMA_FROMDEVICE);
+ }
+ if (scb->flags & SCB_RECOVERY_SCB)
+ {
+ p->flags &= ~AHC_ABORT_PENDING;
+ }
+ if (scb->flags & (SCB_RESET|SCB_ABORT))
+ {
+ cmd->result |= (DID_RESET << 16);
+ }
+
+ if ((scb->flags & SCB_MSGOUT_BITS) != 0)
+ {
+ unsigned short mask;
+ int message_error = FALSE;
+
+ mask = 0x01 << tindex;
+
+ /*
+ * Check to see if we get an invalid message or a message error
+ * after failing to negotiate a wide or sync transfer message.
+ */
+ if ((scb->flags & SCB_SENSE) &&
+ ((scb->cmd->sense_buffer[12] == 0x43) || /* INVALID_MESSAGE */
+ (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR */
+ {
+ message_error = TRUE;
+ }
+
+ if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ if (message_error)
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (aic_dev->flags & DEVICE_PRINT_DTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Wide Negotiation "
+ "processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ }
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
+ }
+ }
+ if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ if (message_error)
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (aic_dev->flags & DEVICE_PRINT_DTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Sync Negotiation "
+ "processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ aic_dev->flags &= ~DEVICE_PRINT_DTR;
+ }
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
+ }
+ }
+ if (scb->flags & SCB_MSGOUT_PPR)
+ {
+ if(message_error)
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (aic_dev->flags & DEVICE_PRINT_DTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Parallel Protocol "
+ "Request processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Parallel Protocol Request negotiation to this "
+ "device.\n", p->host_no, CTL_OF_SCB(scb));
+ }
+ /*
+ * Disable PPR negotiation and revert back to WDTR and SDTR setup
+ */
+ aic_dev->needppr = aic_dev->needppr_copy = 0;
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
+ }
+ }
+ }
+
+ queue_depth = aic_dev->temp_q_depth;
+ if (queue_depth >= aic_dev->active_cmds)
+ {
+ scbp = scbq_remove_head(&aic_dev->delayed_scbs);
+ if (scbp)
+ {
+ if (queue_depth == 1)
+ {
+ /*
+ * Give extra preference to untagged devices, such as CD-R devices
+ * This makes it more likely that a drive *won't* stuff up while
+ * waiting on data at a critical time, such as CD-R writing and
+ * audio CD ripping operations. Should also benefit tape drives.
+ */
+ scbq_insert_head(&p->waiting_scbs, scbp);
+ }
+ else
+ {
+ scbq_insert_tail(&p->waiting_scbs, scbp);
+ }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
+ p->host_no, CTL_OF_SCB(scbp));
+#endif
+ if (queue_depth > aic_dev->active_cmds)
+ {
+ scbp = scbq_remove_head(&aic_dev->delayed_scbs);
+ if (scbp)
+ scbq_insert_tail(&p->waiting_scbs, scbp);
+ }
+ }
+ }
+ if (!(scb->tag_action))
+ {
+ aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun,
+ /* unbusy */ TRUE);
+ if (cmd->device->simple_tags)
+ {
+ aic_dev->temp_q_depth = aic_dev->max_q_depth;
+ }
+ }
+ if(scb->flags & SCB_DTR_SCB)
+ {
+ aic_dev->dtr_pending = 0;
+ }
+ aic_dev->active_cmds--;
+ p->activescbs--;
+
+ if ((scb->sg_length >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
+ {
+ long *ptr;
+ int x, i;
+
+
+ if (rq_data_dir(cmd->request) == WRITE)
+ {
+ aic_dev->w_total++;
+ ptr = aic_dev->w_bins;
+ }
+ else
+ {
+ aic_dev->r_total++;
+ ptr = aic_dev->r_bins;
+ }
+ if(cmd->device->simple_tags && cmd->request->flags & REQ_HARDBARRIER)
+ {
+ aic_dev->barrier_total++;
+ if(scb->tag_action == MSG_ORDERED_Q_TAG)
+ aic_dev->ordered_total++;
+ }
+ x = scb->sg_length;
+ x >>= 10;
+ for(i=0; i<6; i++)
+ {
+ x >>= 2;
+ if(!x) {
+ ptr[i]++;
+ break;
+ }
+ }
+ if(i == 6 && x)
+ ptr[5]++;
+ }
+ aic7xxx_free_scb(p, scb);
+ aic7xxx_queue_cmd_complete(p, cmd);
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_run_done_queue
+ *
+ * Description:
+ * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ * aborted list, and adds each scb to the free list. If complete
+ * is TRUE, we also process the commands complete list.
+ *-F*************************************************************************/
+static void
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
+{
+ struct aic7xxx_scb *scb;
+ int i, found = 0;
+
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ scb = p->scb_data->scb_array[i];
+ if (scb->flags & SCB_QUEUED_FOR_DONE)
+ {
+ if (scb->flags & SCB_QUEUE_FULL)
+ {
+ scb->cmd->result = QUEUE_FULL << 1;
+ }
+ else
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Aborting scb %d\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+ /*
+ * Clear any residual information since the normal aic7xxx_done() path
+ * doesn't touch the residuals.
+ */
+ scb->hscb->residual_SG_segment_count = 0;
+ scb->hscb->residual_data_count[0] = 0;
+ scb->hscb->residual_data_count[1] = 0;
+ scb->hscb->residual_data_count[2] = 0;
+ }
+ found++;
+ aic7xxx_done(p, scb);
+ }
+ }
+ if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
+ {
+ printk(INFO_LEAD "%d commands found and queued for "
+ "completion.\n", p->host_no, -1, -1, -1, found);
+ }
+ if (complete)
+ {
+ aic7xxx_done_cmds_complete(p);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort_waiting_scb
+ *
+ * Description:
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ unsigned char scbpos, unsigned char prev)
+{
+ unsigned char curscb, next;
+
+ /*
+ * Select the SCB we want to abort and pull the next pointer out of it.
+ */
+ curscb = aic_inb(p, SCBPTR);
+ aic_outb(p, scbpos, SCBPTR);
+ next = aic_inb(p, SCB_NEXT);
+
+ aic7xxx_add_curscb_to_free_list(p);
+
+ /*
+ * Update the waiting list
+ */
+ if (prev == SCB_LIST_NULL)
+ {
+ /*
+ * First in the list
+ */
+ aic_outb(p, next, WAITING_SCBH);
+ }
+ else
+ {
+ /*
+ * Select the scb that pointed to us and update its next pointer.
+ */
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
+ }
+ /*
+ * Point us back at the original scb position and inform the SCSI
+ * system that the command has been aborted.
+ */
+ aic_outb(p, curscb, SCBPTR);
+ return (next);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_search_qinfifo
+ *
+ * Description:
+ * Search the queue-in FIFO for matching SCBs and conditionally
+ * requeue. Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, int channel,
+ int lun, unsigned char tag, int flags, int requeue,
+ volatile scb_queue_type *queue)
+{
+ int found;
+ unsigned char qinpos, qintail;
+ struct aic7xxx_scb *scbp;
+
+ found = 0;
+ qinpos = aic_inb(p, QINPOS);
+ qintail = p->qinfifonext;
+
+ p->qinfifonext = qinpos;
+
+ while (qinpos != qintail)
+ {
+ scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ /*
+ * We found an scb that needs to be removed.
+ */
+ if (requeue && (queue != NULL))
+ {
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ scbq_remove(queue, scbp);
+ scbq_remove(&p->waiting_scbs, scbp);
+ scbq_remove(&AIC_DEV(scbp->cmd)->delayed_scbs, scbp);
+ AIC_DEV(scbp->cmd)->active_cmds++;
+ p->activescbs++;
+ }
+ scbq_insert_tail(queue, scbp);
+ AIC_DEV(scbp->cmd)->active_cmds--;
+ p->activescbs--;
+ scbp->flags |= SCB_WAITINGQ;
+ if ( !(scbp->tag_action & TAG_ENB) )
+ {
+ aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ TRUE);
+ }
+ }
+ else if (requeue)
+ {
+ p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
+ }
+ else
+ {
+ /*
+ * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
+ * flags we were called with, presumeably so aic7xxx_run_done_queue
+ * can find this scb
+ */
+ scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
+ if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ FALSE) == scbp->hscb->tag)
+ {
+ aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ TRUE);
+ }
+ }
+ found++;
+ }
+ else
+ {
+ p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
+ }
+ }
+ /*
+ * Now that we've done the work, clear out any left over commands in the
+ * qinfifo and update the KERNEL_QINPOS down on the card.
+ *
+ * NOTE: This routine expect the sequencer to already be paused when
+ * it is run....make sure it's that way!
+ */
+ qinpos = p->qinfifonext;
+ while(qinpos != qintail)
+ {
+ p->qinfifo[qinpos++] = SCB_LIST_NULL;
+ }
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_scb_on_qoutfifo
+ *
+ * Description:
+ * Is the scb that was passed to us currently on the qoutfifo?
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int i=0;
+
+ while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
+ {
+ if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
+ return TRUE;
+ else
+ i++;
+ }
+ return FALSE;
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_device
+ *
+ * Description:
+ * The device at the given target/channel has been reset. Abort
+ * all active and queued scbs for that target/channel. This function
+ * need not worry about linked next pointers because if was a MSG_ABORT_TAG
+ * then we had a tagged command (no linked next), if it was MSG_ABORT or
+ * MSG_BUS_DEV_RESET then the device won't know about any commands any more
+ * and no busy commands will exist, and if it was a bus reset, then nothing
+ * knows about any linked next commands any more. In all cases, we don't
+ * need to worry about the linked next or busy scb, we just need to clear
+ * them.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
+ int lun, unsigned char tag)
+{
+ struct aic7xxx_scb *scbp, *prev_scbp;
+ struct scsi_device *sd;
+ unsigned char active_scb, tcl, scb_tag;
+ int i = 0, init_lists = FALSE;
+ struct aic_dev_data *aic_dev;
+
+ /*
+ * Restore this when we're done
+ */
+ active_scb = aic_inb(p, SCBPTR);
+ scb_tag = aic_inb(p, SCB_TAG);
+
+ if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+ {
+ printk(INFO_LEAD "Reset device, hardware_scb %d,\n",
+ p->host_no, channel, target, lun, active_scb);
+ printk(INFO_LEAD "Current scb %d, SEQADDR 0x%x, LASTPHASE "
+ "0x%x\n",
+ p->host_no, channel, target, lun, scb_tag,
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, LASTPHASE));
+ printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n",
+ p->host_no, channel, target, lun,
+ (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0,
+ aic_inb(p, SG_COUNT), aic_inb(p, SCSISIGI));
+ printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%x\n",
+ p->host_no, channel, target, lun, aic_inb(p, SSTAT0),
+ aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
+ }
+
+ /*
+ * Deal with the busy target and linked next issues.
+ */
+ list_for_each_entry(aic_dev, &p->aic_devs, list)
+ {
+ if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+ printk(INFO_LEAD "processing aic_dev %p\n", p->host_no, channel, target,
+ lun, aic_dev);
+ sd = aic_dev->SDptr;
+
+ if((target != ALL_TARGETS && target != sd->id) ||
+ (channel != ALL_CHANNELS && channel != sd->channel))
+ continue;
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning up status information "
+ "and delayed_scbs.\n", p->host_no, sd->channel, sd->id, sd->lun);
+ aic_dev->flags &= ~BUS_DEVICE_RESET_PENDING;
+ if ( tag == SCB_LIST_NULL )
+ {
+ aic_dev->dtr_pending = 0;
+ aic_dev->needppr = aic_dev->needppr_copy;
+ aic_dev->needsdtr = aic_dev->needsdtr_copy;
+ aic_dev->needwdtr = aic_dev->needwdtr_copy;
+ aic_dev->flags = DEVICE_PRINT_DTR;
+ aic_dev->temp_q_depth = aic_dev->max_q_depth;
+ }
+ tcl = (sd->id << 4) | (sd->channel << 3) | sd->lun;
+ if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
+ (tag == SCB_LIST_NULL) )
+ aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
+ prev_scbp = NULL;
+ scbp = aic_dev->delayed_scbs.head;
+ while (scbp != NULL)
+ {
+ prev_scbp = scbp;
+ scbp = scbp->q_next;
+ if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+ {
+ scbq_remove(&aic_dev->delayed_scbs, prev_scbp);
+ if (prev_scbp->flags & SCB_WAITINGQ)
+ {
+ aic_dev->active_cmds++;
+ p->activescbs++;
+ }
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ }
+ }
+ }
+
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
+ aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+ SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
+
+/*
+ * Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
+ * ABORT/RESET commands.
+ */
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
+ target, lun );
+ {
+ struct aic7xxx_scb *scbp, *prev_scbp;
+
+ prev_scbp = NULL;
+ scbp = p->waiting_scbs.head;
+ while (scbp != NULL)
+ {
+ prev_scbp = scbp;
+ scbp = scbp->q_next;
+ if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+ {
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ if (prev_scbp->flags & SCB_WAITINGQ)
+ {
+ AIC_DEV(prev_scbp->cmd)->active_cmds++;
+ p->activescbs++;
+ }
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ }
+ }
+ }
+
+
+ /*
+ * Search waiting for selection list.
+ */
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning waiting for selection "
+ "list.\n", p->host_no, channel, target, lun);
+ {
+ unsigned char next, prev, scb_index;
+
+ next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
+ while (next != SCB_LIST_NULL)
+ {
+ aic_outb(p, next, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index >= p->scb_data->numscbs)
+ {
+ /*
+ * No aic7xxx_verbose check here.....we want to see this since it
+ * means either the kernel driver or the sequencer screwed things up
+ */
+ printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+ "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic_inb(p, SCB_NEXT);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ else
+ {
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ AIC_DEV(scbp->cmd)->active_cmds++;
+ p->activescbs++;
+ }
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ if (prev == SCB_LIST_NULL)
+ {
+ /*
+ * This is either the first scb on the waiting list, or we
+ * have already yanked the first and haven't left any behind.
+ * Either way, we need to turn off the selection hardware if
+ * it isn't already off.
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ aic_outb(p, CLRSELTIMEO, CLRSINT1);
+ }
+ }
+ else
+ {
+ prev = next;
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ }
+ }
+
+ /*
+ * Go through disconnected list and remove any entries we have queued
+ * for completion, zeroing their control byte too.
+ */
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning disconnected scbs "
+ "list.\n", p->host_no, channel, target, lun);
+ if (p->flags & AHC_PAGESCBS)
+ {
+ unsigned char next, prev, scb_index;
+
+ next = aic_inb(p, DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+ while (next != SCB_LIST_NULL)
+ {
+ aic_outb(p, next, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
+ "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
+ }
+ else
+ {
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ AIC_DEV(scbp->cmd)->active_cmds++;
+ p->activescbs++;
+ }
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->hscb->control = 0;
+ }
+ else
+ {
+ prev = next;
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ }
+ }
+
+ /*
+ * Walk the free list making sure no entries on the free list have
+ * a valid SCB_TAG value or SCB_CONTROL byte.
+ */
+ if (p->flags & AHC_PAGESCBS)
+ {
+ unsigned char next;
+
+ next = aic_inb(p, FREE_SCBH);
+ while (next != SCB_LIST_NULL)
+ {
+ aic_outb(p, next, SCBPTR);
+ if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
+ target, lun);
+ init_lists = TRUE;
+ next = SCB_LIST_NULL;
+ }
+ else
+ {
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list.
+ */
+ if (init_lists)
+ {
+ aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+ }
+ for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
+ {
+ unsigned char scbid;
+
+ aic_outb(p, i, SCBPTR);
+ if (init_lists)
+ {
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ else
+ {
+ scbid = aic_inb(p, SCB_TAG);
+ if (scbid < p->scb_data->numscbs)
+ {
+ scbp = p->scb_data->scb_array[scbid];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ aic_outb(p, 0, SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ }
+ }
+ }
+
+ /*
+ * Go through the entire SCB array now and look for commands for
+ * for this target that are stillactive. These are other (most likely
+ * tagged) commands that were disconnected when the reset occurred.
+ * Any commands we find here we know this about, it wasn't on any queue,
+ * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
+ * lists, so it really must have been a paged out SCB. In that case,
+ * we shouldn't need to bother with updating any counters, just mark
+ * the correct flags and go on.
+ */
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ scbp = p->scb_data->scb_array[i];
+ if ((scbp->flags & SCB_ACTIVE) &&
+ aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
+ !aic7xxx_scb_on_qoutfifo(p, scbp))
+ {
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ scbq_remove(&p->waiting_scbs, scbp);
+ scbq_remove(&AIC_DEV(scbp->cmd)->delayed_scbs, scbp);
+ AIC_DEV(scbp->cmd)->active_cmds++;
+ p->activescbs++;
+ }
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ }
+ }
+
+ aic_outb(p, active_scb, SCBPTR);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_clear_intstat
+ *
+ * Description:
+ * Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+ /* Clear any interrupt conditions this may have caused. */
+ aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
+ aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+ CLRPHASECHG | CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_current_bus
+ *
+ * Description:
+ * Reset the current SCSI bus.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
+{
+
+ /* Disable reset interrupts. */
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
+
+ /* Turn off the bus' current operations, after all, we shouldn't have any
+ * valid commands left to cause a RSELI and SELO once we've tossed the
+ * bus away with this reset, so we might as well shut down the sequencer
+ * until the bus is restarted as oppossed to saving the current settings
+ * and restoring them (which makes no sense to me). */
+
+ /* Turn on the bus reset. */
+ aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
+ while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
+ mdelay(5);
+
+ /*
+ * Some of the new Ultra2 chipsets need a longer delay after a chip
+ * reset than just the init setup creates, so we have to delay here
+ * before we go into a reset in order to make the chips happy.
+ */
+ if (p->features & AHC_ULTRA2)
+ mdelay(250);
+ else
+ mdelay(50);
+
+ /* Turn off the bus reset. */
+ aic_outb(p, 0, SCSISEQ);
+ mdelay(10);
+
+ aic7xxx_clear_intstat(p);
+ /* Re-enable reset interrupts. */
+ aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_channel
+ *
+ * Description:
+ * Reset the channel.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
+{
+ unsigned long offset_min, offset_max;
+ unsigned char sblkctl;
+ int cur_channel;
+
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
+ p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
+
+
+ if (channel == 1)
+ {
+ offset_min = 8;
+ offset_max = 16;
+ }
+ else
+ {
+ if (p->features & AHC_TWIN)
+ {
+ /* Channel A */
+ offset_min = 0;
+ offset_max = 8;
+ }
+ else
+ {
+ offset_min = 0;
+ if (p->features & AHC_WIDE)
+ {
+ offset_max = 16;
+ }
+ else
+ {
+ offset_max = 8;
+ }
+ }
+ }
+
+ while (offset_min < offset_max)
+ {
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ aic_outb(p, 0, TARG_SCSIRATE + offset_min);
+ if (p->features & AHC_ULTRA2)
+ {
+ aic_outb(p, 0, TARG_OFFSET + offset_min);
+ }
+ offset_min++;
+ }
+
+ /*
+ * Reset the bus and unpause/restart the controller
+ */
+ sblkctl = aic_inb(p, SBLKCTL);
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ cur_channel = (sblkctl & SELBUSB) >> 3;
+ else
+ cur_channel = 0;
+ if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
+ {
+ /*
+ * Case 1: Command for another bus is active
+ */
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
+ channel, -1, -1);
+ /*
+ * Stealthily reset the other bus without upsetting the current bus.
+ */
+ aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
+ if (initiate_reset)
+ {
+ aic7xxx_reset_current_bus(p);
+ }
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ aic_outb(p, sblkctl, SBLKCTL);
+ }
+ else
+ {
+ /*
+ * Case 2: A command from this bus is active or we're idle.
+ */
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
+ channel, -1, -1);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+ SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_len = 0;
+ if (initiate_reset)
+ {
+ aic7xxx_reset_current_bus(p);
+ }
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ }
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
+ /*
+ * Clean up all the state information for the pending transactions
+ * on this bus.
+ */
+ aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+
+ if ( !(p->features & AHC_TWIN) )
+ {
+ restart_sequencer(p);
+ }
+
+ return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_run_waiting_queues
+ *
+ * Description:
+ * Scan the awaiting_scbs queue downloading and starting as many
+ * scbs as we can.
+ *-F*************************************************************************/
+static void
+aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
+{
+ struct aic7xxx_scb *scb;
+ struct aic_dev_data *aic_dev;
+ int sent;
+
+
+ if (p->waiting_scbs.head == NULL)
+ return;
+
+ sent = 0;
+
+ /*
+ * First handle SCBs that are waiting but have been assigned a slot.
+ */
+ while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
+ {
+ aic_dev = scb->cmd->device->hostdata;
+ if ( !scb->tag_action )
+ {
+ aic_dev->temp_q_depth = 1;
+ }
+ if ( aic_dev->active_cmds >= aic_dev->temp_q_depth)
+ {
+ scbq_insert_tail(&aic_dev->delayed_scbs, scb);
+ }
+ else
+ {
+ scb->flags &= ~SCB_WAITINGQ;
+ aic_dev->active_cmds++;
+ p->activescbs++;
+ if ( !(scb->tag_action) )
+ {
+ aic7xxx_busy_target(p, scb);
+ }
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ sent++;
+ }
+ }
+ if (sent)
+ {
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ {
+ pause_sequencer(p);
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ unpause_sequencer(p, FALSE);
+ }
+ if (p->activescbs > p->max_activescbs)
+ p->max_activescbs = p->activescbs;
+ }
+}
+
+#ifdef CONFIG_PCI
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_pci_intr
+ *
+ * Description:
+ * Check the scsi card for PCI errors and clear the interrupt
+ *
+ * NOTE: If you don't have this function and a 2940 card encounters
+ * a PCI error condition, the machine will end up locked as the
+ * interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
+static void
+aic7xxx_pci_intr(struct aic7xxx_host *p)
+{
+ unsigned char status1;
+
+ pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
+
+ if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+ "phase.\n", p->host_no, -1, -1, -1);
+ if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+ "PERR#\n", p->host_no, -1, -1, -1);
+
+ pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
+ if (status1 & (DPR|RMA|RTA))
+ aic_outb(p, CLRPARERR, CLRINT);
+
+ if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
+ aic7xxx_panic_abort(p, NULL);
+
+}
+#endif /* CONFIG_PCI */
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_ppr
+ *
+ * Description:
+ * Build up a Parallel Protocol Request message for use with SCSI-3
+ * devices.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
+ p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.period;
+ p->msg_buf[p->msg_index++] = 0;
+ p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.offset;
+ p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.width;
+ p->msg_buf[p->msg_index++] = AIC_DEV(scb->cmd)->goal.options;
+ p->msg_len += 8;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_sdtr
+ *
+ * Description:
+ * Constucts a synchronous data transfer message in the message
+ * buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, unsigned char period,
+ unsigned char offset)
+{
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
+ p->msg_buf[p->msg_index++] = period;
+ p->msg_buf[p->msg_index++] = offset;
+ p->msg_len += 5;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_wdtr
+ *
+ * Description:
+ * Constucts a wide data transfer message in the message buffer
+ * on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
+{
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
+ p->msg_buf[p->msg_index++] = bus_width;
+ p->msg_len += 4;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_calc_residual
+ *
+ * Description:
+ * Calculate the residual data not yet transferred.
+ *-F*************************************************************************/
+static void
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+ int actual, i;
+
+ cmd = scb->cmd;
+ hscb = scb->hscb;
+
+ /*
+ * Don't destroy valid residual information with
+ * residual coming from a check sense operation.
+ */
+ if (((scb->hscb->control & DISCONNECTED) == 0) &&
+ (scb->flags & SCB_SENSE) == 0)
+ {
+ /*
+ * We had an underflow. At this time, there's only
+ * one other driver that bothers to check for this,
+ * and cmd->underflow seems to be set rather half-
+ * heartedly in the higher-level SCSI code.
+ */
+ actual = scb->sg_length;
+ for (i=1; i < hscb->residual_SG_segment_count; i++)
+ {
+ actual -= scb->sg_list[scb->sg_count - i].length;
+ }
+ actual -= (hscb->residual_data_count[2] << 16) |
+ (hscb->residual_data_count[1] << 8) |
+ hscb->residual_data_count[0];
+
+ if (actual < cmd->underflow)
+ {
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ {
+ printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
+ "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
+ (rq_data_dir(cmd->request) == WRITE) ? "wrote" : "read", actual,
+ hscb->residual_SG_segment_count);
+ printk(INFO_LEAD "status 0x%x.\n", p->host_no, CTL_OF_SCB(scb),
+ hscb->target_status);
+ }
+ /*
+ * In 2.4, only send back the residual information, don't flag this
+ * as an error. Before 2.4 we had to flag this as an error because
+ * the mid layer didn't check residual data counts to see if the
+ * command needs retried.
+ */
+ cmd->resid = scb->sg_length - actual;
+ aic7xxx_status(cmd) = hscb->target_status;
+ }
+ }
+
+ /*
+ * Clean out the residual information in the SCB for the
+ * next consumer.
+ */
+ hscb->residual_data_count[2] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_SG_segment_count = 0;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_device_reset
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
+{
+ unsigned char tindex = target;
+
+ tindex |= ((channel & 0x01) << 3);
+
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ aic_outb(p, 0, TARG_SCSIRATE + tindex);
+ if (p->features & AHC_ULTRA2)
+ aic_outb(p, 0, TARG_OFFSET + tindex);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
+ target, -1);
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_seqint
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ struct aic7xxx_scb *scb;
+ struct aic_dev_data *aic_dev;
+ unsigned short target_mask;
+ unsigned char target, lun, tindex;
+ unsigned char queue_flag = FALSE;
+ char channel;
+ int result;
+
+ target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ else
+ channel = 0;
+ tindex = target + (channel << 3);
+ lun = aic_inb(p, SAVED_TCL) & 0x07;
+ target_mask = (0x01 << tindex);
+
+ /*
+ * Go ahead and clear the SEQINT now, that avoids any interrupt race
+ * conditions later on in case we enable some other interrupt.
+ */
+ aic_outb(p, CLRSEQINT, CLRINT);
+ switch (intstat & SEQINT_MASK)
+ {
+ case NO_MATCH:
+ {
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+ SCSISEQ);
+ printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
+ "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
+ printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+ p->host_no, channel, target, lun,
+ aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, NULL);
+ }
+ break;
+
+ case SEND_REJECT:
+ {
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
+ "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
+ aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
+ }
+ break;
+
+ case NO_IDENT:
+ {
+ /*
+ * The reconnecting target either did not send an identify
+ * message, or did, but we didn't find an SCB to match and
+ * before it could respond to our ATN/abort, it hit a dataphase.
+ * The only safe thing to do is to blow it away with a bus
+ * reset.
+ */
+ if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
+ printk(INFO_LEAD "Target did not send an IDENTIFY message; "
+ "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
+ lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
+
+ aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+ aic7xxx_run_done_queue(p, TRUE);
+
+ }
+ break;
+
+ case BAD_PHASE:
+ if (aic_inb(p, LASTPHASE) == P_BUSFREE)
+ {
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
+ target, lun);
+ restart_sequencer(p);
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
+ channel, target, lun);
+ }
+ break;
+
+ case EXTENDED_MSG:
+ {
+ p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ p->msg_len = 0;
+ p->msg_index = 0;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
+ channel, target, lun);
+#endif
+
+ /*
+ * To actually receive the message, simply turn on
+ * REQINIT interrupts and let our interrupt handler
+ * do the rest (REQINIT should already be true).
+ */
+ p->flags |= AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+
+ /*
+ * We don't want the sequencer unpaused yet so we return early
+ */
+ return;
+ }
+
+ case REJECT_MSG:
+ {
+ /*
+ * What we care about here is if we had an outstanding SDTR
+ * or WDTR message for this target. If we did, this is a
+ * signal that the target is refusing negotiation.
+ */
+ unsigned char scb_index;
+ unsigned char last_msg;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ aic_dev = AIC_DEV(scb->cmd);
+ last_msg = aic_inb(p, LAST_MSG);
+
+ if ( (last_msg == MSG_IDENTIFYFLAG) &&
+ (scb->tag_action) &&
+ !(scb->flags & SCB_MSGOUT_BITS) )
+ {
+ if (scb->tag_action == MSG_ORDERED_Q_TAG)
+ {
+ /*
+ * OK...the device seems able to accept tagged commands, but
+ * not ordered tag commands, only simple tag commands. So, we
+ * disable ordered tag commands and go on with life just like
+ * normal.
+ */
+ scsi_adjust_queue_depth(scb->cmd->device, MSG_SIMPLE_TAG,
+ scb->cmd->device->queue_depth);
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ scb->hscb->control &= ~SCB_TAG_TYPE;
+ scb->hscb->control |= MSG_SIMPLE_Q_TAG;
+ aic_outb(p, scb->hscb->control, SCB_CONTROL);
+ /*
+ * OK..we set the tag type to simple tag command, now we re-assert
+ * ATNO and hope this will take us into the identify phase again
+ * so we can resend the tag type and info to the device.
+ */
+ aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
+ {
+ unsigned char i;
+ struct aic7xxx_scb *scbp;
+ int old_verbose;
+ /*
+ * Hmmmm....the device is flaking out on tagged commands.
+ */
+ scsi_adjust_queue_depth(scb->cmd->device, 0 /* untagged */,
+ p->host->cmd_per_lun);
+ aic_dev->max_q_depth = aic_dev->temp_q_depth = 1;
+ /*
+ * We set this command up as a bus device reset. However, we have
+ * to clear the tag type as it's causing us problems. We shouldnt
+ * have to worry about any other commands being active, since if
+ * the device is refusing tagged commands, this should be the
+ * first tagged command sent to the device, however, we do have
+ * to worry about any other tagged commands that may already be
+ * in the qinfifo. The easiest way to do this, is to issue a BDR,
+ * send all the commands back to the mid level code, then let them
+ * come back and get rebuilt as untagged commands.
+ */
+ scb->tag_action = 0;
+ scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
+ aic_outb(p, scb->hscb->control, SCB_CONTROL);
+
+ old_verbose = aic7xxx_verbose;
+ aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
+ for (i=0; i < p->scb_data->numscbs; i++)
+ {
+ scbp = p->scb_data->scb_array[i];
+ if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
+ {
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
+ {
+ aic7xxx_reset_device(p, target, channel, lun, i);
+ }
+ }
+ }
+ aic7xxx_run_done_queue(p, TRUE);
+ aic7xxx_verbose = old_verbose;
+ /*
+ * Wait until after the for loop to set the busy index since
+ * aic7xxx_reset_device will clear the busy index during its
+ * operation.
+ */
+ aic7xxx_busy_target(p, scb);
+ printk(INFO_LEAD "Device is refusing tagged commands, using "
+ "untagged I/O.\n", p->host_no, channel, target, lun);
+ aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ }
+ else if (scb->flags & SCB_MSGOUT_PPR)
+ {
+ /*
+ * As per the draft specs, any device capable of supporting any of
+ * the option values other than 0 are not allowed to reject the
+ * PPR message. Instead, they must negotiate out what they do
+ * support instead of rejecting our offering or else they cause
+ * a parity error during msg_out phase to signal that they don't
+ * like our settings.
+ */
+ aic_dev->needppr = aic_dev->needppr_copy = 0;
+ aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+ (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE), aic_dev);
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
+ aic_dev);
+ aic_dev->goal.options = aic_dev->dtr_pending = 0;
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Device is rejecting PPR messages, falling "
+ "back.\n", p->host_no, channel, target, lun);
+ }
+ if ( aic_dev->goal.width )
+ {
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
+ aic_dev->dtr_pending = 1;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ }
+ if ( aic_dev->goal.offset )
+ {
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
+ if( !aic_dev->dtr_pending )
+ {
+ aic_dev->dtr_pending = 1;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ }
+ if ( aic_dev->dtr_pending )
+ {
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ }
+ else if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ /*
+ * note 8bit xfers and clear flag
+ */
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+ (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR), aic_dev);
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
+ aic_dev);
+ if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Device is rejecting WDTR messages, using "
+ "narrow transfers.\n", p->host_no, channel, target, lun);
+ }
+ aic_dev->needsdtr = aic_dev->needsdtr_copy;
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ /*
+ * note asynch xfers and clear flag
+ */
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+ (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL), aic_dev);
+ if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Device is rejecting SDTR messages, using "
+ "async transfers.\n", p->host_no, channel, target, lun);
+ }
+ }
+ else if (aic7xxx_verbose & VERBOSE_SEQINT)
+ {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. "
+ "Ignoring.\n", p->host_no, channel, target, lun);
+ }
+ }
+ break;
+
+ case BAD_STATUS:
+ {
+ unsigned char scb_index;
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+
+ /* The sequencer will notify us when a command has an error that
+ * would be of interest to the kernel. This allows us to leave
+ * the sequencer running in the common case of command completes
+ * without error. The sequencer will have DMA'd the SCB back
+ * up to us, so we can reference the drivers SCB array.
+ *
+ * Set the default return value to 0 indicating not to send
+ * sense. The sense code will change this if needed and this
+ * reduces code duplication.
+ */
+ aic_outb(p, 0, RETURN_1);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
+ p->host_no, channel, target, lun, intstat, scb_index);
+ break;
+ }
+ scb = p->scb_data->scb_array[scb_index];
+ hscb = scb->hscb;
+
+ if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
+ " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
+ scb_index, scb->flags, (unsigned long) scb->cmd);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ aic_dev = AIC_DEV(scb->cmd);
+ hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
+ aic7xxx_status(cmd) = hscb->target_status;
+
+ cmd->result = hscb->target_status;
+
+ switch (status_byte(hscb->target_status))
+ {
+ case GOOD:
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Interrupted for status of GOOD???\n",
+ p->host_no, CTL_OF_SCB(scb));
+ break;
+
+ case COMMAND_TERMINATED:
+ case CHECK_CONDITION:
+ if ( !(scb->flags & SCB_SENSE) )
+ {
+ /*
+ * Send a sense command to the requesting target.
+ * XXX - revisit this and get rid of the memcopys.
+ */
+ memcpy(scb->sense_cmd, &generic_sense[0],
+ sizeof(generic_sense));
+
+ scb->sense_cmd[1] = (cmd->device->lun << 5);
+ scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+ scb->sg_list[0].length =
+ cpu_to_le32(sizeof(cmd->sense_buffer));
+ scb->sg_list[0].address =
+ cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer,
+ sizeof(cmd->sense_buffer),
+ PCI_DMA_FROMDEVICE));
+
+ /*
+ * XXX - We should allow disconnection, but can't as it
+ * might allow overlapped tagged commands.
+ */
+ /* hscb->control &= DISCENB; */
+ hscb->control = 0;
+ hscb->target_status = 0;
+ hscb->SG_list_pointer =
+ cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list));
+ hscb->SCSI_cmd_pointer =
+ cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd));
+ hscb->data_count = scb->sg_list[0].length;
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+ hscb->residual_SG_segment_count = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[2] = 0;
+
+ scb->sg_count = hscb->SG_segment_count = 1;
+ scb->sg_length = sizeof(cmd->sense_buffer);
+ scb->tag_action = 0;
+ scb->flags |= SCB_SENSE;
+ /*
+ * Ensure the target is busy since this will be an
+ * an untagged request.
+ */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ if (scb->flags & SCB_MSGOUT_BITS)
+ printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
+ CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
+ "SDTR" : "WDTR");
+ else
+ printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
+ CTL_OF_SCB(scb));
+ }
+#endif
+ aic7xxx_busy_target(p, scb);
+ aic_outb(p, SEND_SENSE, RETURN_1);
+ aic7xxx_error(cmd) = DID_OK;
+ break;
+ } /* first time sense, no errors */
+ printk(INFO_LEAD "CHECK_CONDITION on REQUEST_SENSE, returning "
+ "an error.\n", p->host_no, CTL_OF_SCB(scb));
+ aic7xxx_error(cmd) = DID_ERROR;
+ scb->flags &= ~SCB_SENSE;
+ break;
+
+ case QUEUE_FULL:
+ queue_flag = TRUE; /* Mark that this is a QUEUE_FULL and */
+ case BUSY: /* drop through to here */
+ {
+ struct aic7xxx_scb *next_scbp, *prev_scbp;
+ unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
+ /*
+ * We have to look three places for queued commands:
+ * 1: p->waiting_scbs queue
+ * 2: QINFIFO
+ * 3: WAITING_SCBS list on card (for commands that are started
+ * but haven't yet made it to the device)
+ *
+ * Of special note here is that commands on 2 or 3 above will
+ * have already been marked as active, while commands on 1 will
+ * not. The aic7xxx_done() function will want to unmark them
+ * from active, so any commands we pull off of 1 need to
+ * up the active count.
+ */
+ next_scbp = p->waiting_scbs.head;
+ while ( next_scbp != NULL )
+ {
+ prev_scbp = next_scbp;
+ next_scbp = next_scbp->q_next;
+ if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
+ SCB_LIST_NULL) )
+ {
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ scb->flags = SCB_QUEUED_FOR_DONE | SCB_QUEUE_FULL;
+ p->activescbs++;
+ aic_dev->active_cmds++;
+ }
+ }
+ aic7xxx_search_qinfifo(p, target, channel, lun,
+ SCB_LIST_NULL, SCB_QUEUED_FOR_DONE | SCB_QUEUE_FULL,
+ FALSE, NULL);
+ next_scbp = NULL;
+ active_hscb = aic_inb(p, SCBPTR);
+ prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
+ next_hscb = aic_inb(p, WAITING_SCBH);
+ while (next_hscb != SCB_LIST_NULL)
+ {
+ aic_outb(p, next_hscb, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index < p->scb_data->numscbs)
+ {
+ next_scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
+ SCB_LIST_NULL) )
+ {
+ next_scbp->flags = SCB_QUEUED_FOR_DONE | SCB_QUEUE_FULL;
+ next_hscb = aic_inb(p, SCB_NEXT);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ if (prev_hscb == SCB_LIST_NULL)
+ {
+ /* We were first on the list,
+ * so we kill the selection
+ * hardware. Let the sequencer
+ * re-init the hardware itself
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ aic_outb(p, CLRSELTIMEO, CLRSINT1);
+ aic_outb(p, next_hscb, WAITING_SCBH);
+ }
+ else
+ {
+ aic_outb(p, prev_hscb, SCBPTR);
+ aic_outb(p, next_hscb, SCB_NEXT);
+ }
+ }
+ else
+ {
+ prev_hscb = next_hscb;
+ next_hscb = aic_inb(p, SCB_NEXT);
+ }
+ } /* scb_index >= p->scb_data->numscbs */
+ }
+ aic_outb(p, active_hscb, SCBPTR);
+ aic7xxx_run_done_queue(p, FALSE);
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
+ (aic7xxx_verbose > 0xffff) )
+ {
+ if (queue_flag)
+ printk(INFO_LEAD "Queue full received; queue depth %d, "
+ "active %d\n", p->host_no, CTL_OF_SCB(scb),
+ aic_dev->max_q_depth, aic_dev->active_cmds);
+ else
+ printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
+ }
+#endif
+ if (queue_flag)
+ {
+ int diff;
+ result = scsi_track_queue_full(cmd->device,
+ aic_dev->active_cmds);
+ if ( result < 0 )
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ printk(INFO_LEAD "Tagged Command Queueing disabled.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ diff = aic_dev->max_q_depth - p->host->cmd_per_lun;
+ aic_dev->temp_q_depth = 1;
+ aic_dev->max_q_depth = 1;
+ }
+ else if ( result > 0 )
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
+ CTL_OF_SCB(scb), result);
+ diff = aic_dev->max_q_depth - result;
+ aic_dev->max_q_depth = result;
+ /* temp_q_depth could have been dropped to 1 for an untagged
+ * command that might be coming up */
+ if(aic_dev->temp_q_depth > result)
+ aic_dev->temp_q_depth = result;
+ }
+ /* We should free up the no unused SCB entries. But, that's
+ * a difficult thing to do because we use a direct indexed
+ * array, so we can't just take any entries and free them,
+ * we *have* to free the ones at the end of the array, and
+ * they very well could be in use right now, which means
+ * in order to do this right, we have to add a delayed
+ * freeing mechanism tied into the scb_free() code area.
+ * We'll add that later.
+ */
+ }
+ break;
+ }
+
+ default:
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->target_status);
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+ } /* end switch */
+ } /* end else of */
+ }
+ break;
+
+ case AWAITING_MSG:
+ {
+ unsigned char scb_index, msg_out;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ msg_out = aic_inb(p, MSG_OUT);
+ scb = p->scb_data->scb_array[scb_index];
+ aic_dev = AIC_DEV(scb->cmd);
+ p->msg_index = p->msg_len = 0;
+ /*
+ * This SCB had a MK_MESSAGE set in its control byte informing
+ * the sequencer that we wanted to send a special message to
+ * this target.
+ */
+
+ if ( !(scb->flags & SCB_DEVICE_RESET) &&
+ (msg_out == MSG_IDENTIFYFLAG) &&
+ (scb->hscb->control & TAG_ENB) )
+ {
+ p->msg_buf[p->msg_index++] = scb->tag_action;
+ p->msg_buf[p->msg_index++] = scb->hscb->tag;
+ p->msg_len += 2;
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET)
+ {
+ p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
+ p->msg_len++;
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset mailed.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ else if (scb->flags & SCB_ABORT)
+ {
+ if (scb->tag_action)
+ {
+ p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
+ }
+ else
+ {
+ p->msg_buf[p->msg_index++] = MSG_ABORT;
+ }
+ p->msg_len++;
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ }
+ else if (scb->flags & SCB_MSGOUT_PPR)
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
+ p->host_no, CTL_OF_SCB(scb),
+ aic_dev->goal.period,
+ aic_dev->goal.offset,
+ aic_dev->goal.width,
+ aic_dev->goal.options);
+ }
+ aic7xxx_construct_ppr(p, scb);
+ }
+ else if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ }
+ aic7xxx_construct_wdtr(p, aic_dev->goal.width);
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ unsigned int max_sync, period;
+ unsigned char options = 0;
+ /*
+ * Now that the device is selected, use the bits in SBLKCTL and
+ * SSTAT2 to determine the max sync rate for this device.
+ */
+ if (p->features & AHC_ULTRA2)
+ {
+ if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+ !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+ {
+ max_sync = AHC_SYNCRATE_ULTRA2;
+ }
+ else
+ {
+ max_sync = AHC_SYNCRATE_ULTRA;
+ }
+ }
+ else if (p->features & AHC_ULTRA)
+ {
+ max_sync = AHC_SYNCRATE_ULTRA;
+ }
+ else
+ {
+ max_sync = AHC_SYNCRATE_FAST;
+ }
+ period = aic_dev->goal.period;
+ aic7xxx_find_syncrate(p, &period, max_sync, &options);
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
+ CTL_OF_SCB(scb), period,
+ aic_dev->goal.offset);
+ }
+ aic7xxx_construct_sdtr(p, period, aic_dev->goal.offset);
+ }
+ else
+ {
+ panic("aic7xxx: AWAITING_MSG for an SCB that does "
+ "not have a waiting message.\n");
+ }
+ /*
+ * We've set everything up to send our message, now to actually do
+ * so we need to enable reqinit interrupts and let the interrupt
+ * handler do the rest. We don't want to unpause the sequencer yet
+ * though so we'll return early. We also have to make sure that
+ * we clear the SEQINT *BEFORE* we set the REQINIT handler active
+ * or else it's possible on VLB cards to lose the first REQINIT
+ * interrupt. Edge triggered EISA cards could also lose this
+ * interrupt, although PCI and level triggered cards should not
+ * have this problem since they continually interrupt the kernel
+ * until we take care of the situation.
+ */
+ scb->flags |= SCB_MSGOUT_SENT;
+ p->msg_index = 0;
+ p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ p->flags |= AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+ return;
+ }
+ break;
+
+ case DATA_OVERRUN:
+ {
+ unsigned char scb_index = aic_inb(p, SCB_TAG);
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned int i;
+
+ scb = (p->scb_data->scb_array[scb_index]);
+ /*
+ * XXX - What do we really want to do on an overrun? The
+ * mid-level SCSI code should handle this, but for now,
+ * we'll just indicate that the command should retried.
+ * If we retrieved sense info on this target, then the
+ * base SENSE info should have been saved prior to the
+ * overrun error. In that case, we return DID_OK and let
+ * the mid level code pick up on the sense info. Otherwise
+ * we return DID_ERROR so the command will get retried.
+ */
+ if ( !(scb->flags & SCB_SENSE) )
+ {
+ printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
+ p->host_no, CTL_OF_SCB(scb),
+ (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
+ printk(KERN_WARNING " %s seen Data Phase. Length=%d, NumSGs=%d.\n",
+ (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
+ scb->sg_length, scb->sg_count);
+ printk(KERN_WARNING " Raw SCSI Command: 0x");
+ for (i = 0; i < scb->hscb->SCSI_cmd_length; i++)
+ {
+ printk("%02x ", scb->cmd->cmnd[i]);
+ }
+ printk("\n");
+ if(aic7xxx_verbose > 0xffff)
+ {
+ for (i = 0; i < scb->sg_count; i++)
+ {
+ printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n",
+ i,
+ le32_to_cpu(scb->sg_list[i].address),
+ le32_to_cpu(scb->sg_list[i].length) );
+ }
+ }
+ aic7xxx_error(scb->cmd) = DID_ERROR;
+ }
+ else
+ printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ break;
+
+ case WIDE_RESIDUE:
+ {
+ unsigned char resid_sgcnt, index;
+ unsigned char scb_index = aic_inb(p, SCB_TAG);
+ unsigned int cur_addr, resid_dcnt;
+ unsigned int native_addr, native_length, sg_addr;
+ int i;
+
+ if(scb_index > p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "invalid scb_index during WIDE_RESIDUE.\n",
+ p->host_no, -1, -1, -1);
+ /*
+ * XXX: Add error handling here
+ */
+ break;
+ }
+ scb = p->scb_data->scb_array[scb_index];
+ if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "invalid scb during WIDE_RESIDUE flags:0x%x "
+ "scb->cmd:0x%lx\n", p->host_no, CTL_OF_SCB(scb),
+ scb->flags, (unsigned long)scb->cmd);
+ break;
+ }
+ if(aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Got WIDE_RESIDUE message, patching up data "
+ "pointer.\n", p->host_no, CTL_OF_SCB(scb));
+
+ /*
+ * We have a valid scb to use on this WIDE_RESIDUE message, so
+ * we need to walk the sg list looking for this particular sg
+ * segment, then see if we happen to be at the very beginning of
+ * the segment. If we are, then we have to back things up to
+ * the previous segment. If not, then we simply need to remove
+ * one byte from this segments address and add one to the byte
+ * count.
+ */
+ cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) |
+ (aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24);
+ sg_addr = aic_inb(p, SG_COUNT + 1) | (aic_inb(p, SG_COUNT + 2) << 8) |
+ (aic_inb(p, SG_COUNT + 3) << 16) | (aic_inb(p, SG_COUNT + 4) << 24);
+ resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT);
+ resid_dcnt = aic_inb(p, SCB_RESID_DCNT) |
+ (aic_inb(p, SCB_RESID_DCNT + 1) << 8) |
+ (aic_inb(p, SCB_RESID_DCNT + 2) << 16);
+ index = scb->sg_count - ((resid_sgcnt) ? resid_sgcnt : 1);
+ native_addr = le32_to_cpu(scb->sg_list[index].address);
+ native_length = le32_to_cpu(scb->sg_list[index].length);
+ /*
+ * If resid_dcnt == native_length, then we just loaded this SG
+ * segment and we need to back it up one...
+ */
+ if(resid_dcnt == native_length)
+ {
+ if(index == 0)
+ {
+ /*
+ * Oops, this isn't right, we can't back up to before the
+ * beginning. This must be a bogus message, ignore it.
+ */
+ break;
+ }
+ resid_dcnt = 1;
+ resid_sgcnt += 1;
+ native_addr = le32_to_cpu(scb->sg_list[index - 1].address);
+ native_length = le32_to_cpu(scb->sg_list[index - 1].length);
+ cur_addr = native_addr + (native_length - 1);
+ sg_addr -= sizeof(struct hw_scatterlist);
+ }
+ else
+ {
+ /*
+ * resid_dcnt != native_length, so we are in the middle of a SG
+ * element. Back it up one byte and leave the rest alone.
+ */
+ resid_dcnt += 1;
+ cur_addr -= 1;
+ }
+
+ /*
+ * Output the new addresses and counts to the right places on the
+ * card.
+ */
+ aic_outb(p, resid_sgcnt, SG_COUNT);
+ aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT);
+ aic_outb(p, sg_addr & 0xff, SG_COUNT + 1);
+ aic_outb(p, (sg_addr >> 8) & 0xff, SG_COUNT + 2);
+ aic_outb(p, (sg_addr >> 16) & 0xff, SG_COUNT + 3);
+ aic_outb(p, (sg_addr >> 24) & 0xff, SG_COUNT + 4);
+ aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT);
+ aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1);
+ aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2);
+
+ /*
+ * The sequencer actually wants to find the new address
+ * in the SHADDR register set. On the Ultra2 and later controllers
+ * this register set is readonly. In order to get the right number
+ * into the register, you actually have to enter it in HADDR and then
+ * use the PRELOADEN bit of DFCNTRL to drop it through from the
+ * HADDR register to the SHADDR register. On non-Ultra2 controllers,
+ * we simply write it direct.
+ */
+ if(p->features & AHC_ULTRA2)
+ {
+ /*
+ * We might as well be accurate and drop both the resid_dcnt and
+ * cur_addr into HCNT and HADDR and have both of them drop
+ * through to the shadow layer together.
+ */
+ aic_outb(p, resid_dcnt & 0xff, HCNT);
+ aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1);
+ aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2);
+ aic_outb(p, cur_addr & 0xff, HADDR);
+ aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
+ aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
+ aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
+ aic_outb(p, aic_inb(p, DMAPARAMS) | PRELOADEN, DFCNTRL);
+ udelay(1);
+ aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL);
+ i=0;
+ while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000))
+ {
+ udelay(1);
+ }
+ }
+ else
+ {
+ aic_outb(p, cur_addr & 0xff, SHADDR);
+ aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1);
+ aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2);
+ aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3);
+ }
+ }
+ break;
+
+ case SEQ_SG_FIXUP:
+ {
+ unsigned char scb_index, tmp;
+ int sg_addr, sg_length;
+
+ scb_index = aic_inb(p, SCB_TAG);
+
+ if(scb_index > p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "invalid scb_index during SEQ_SG_FIXUP.\n",
+ p->host_no, -1, -1, -1);
+ printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+ "0x%x\n", p->host_no, -1, -1, -1,
+ aic_inb(p, SCSISIGI),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+ printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
+ p->host_no, -1, -1, -1, aic_inb(p, SG_CACHEPTR),
+ aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
+ aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
+ /*
+ * XXX: Add error handling here
+ */
+ break;
+ }
+ scb = p->scb_data->scb_array[scb_index];
+ if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "invalid scb during SEQ_SG_FIXUP flags:0x%x "
+ "scb->cmd:0x%p\n", p->host_no, CTL_OF_SCB(scb),
+ scb->flags, scb->cmd);
+ printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+ "0x%x\n", p->host_no, CTL_OF_SCB(scb),
+ aic_inb(p, SCSISIGI),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+ printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n",
+ p->host_no, CTL_OF_SCB(scb), aic_inb(p, SG_CACHEPTR),
+ aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 |
+ aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT));
+ break;
+ }
+ if(aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Fixing up SG address for sequencer.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ /*
+ * Advance the SG pointer to the next element in the list
+ */
+ tmp = aic_inb(p, SG_NEXT);
+ tmp += SG_SIZEOF;
+ aic_outb(p, tmp, SG_NEXT);
+ if( tmp < SG_SIZEOF )
+ aic_outb(p, aic_inb(p, SG_NEXT + 1) + 1, SG_NEXT + 1);
+ tmp = aic_inb(p, SG_COUNT) - 1;
+ aic_outb(p, tmp, SG_COUNT);
+ sg_addr = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].address);
+ sg_length = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].length);
+ /*
+ * Now stuff the element we just advanced past down onto the
+ * card so it can be stored in the residual area.
+ */
+ aic_outb(p, sg_addr & 0xff, HADDR);
+ aic_outb(p, (sg_addr >> 8) & 0xff, HADDR + 1);
+ aic_outb(p, (sg_addr >> 16) & 0xff, HADDR + 2);
+ aic_outb(p, (sg_addr >> 24) & 0xff, HADDR + 3);
+ aic_outb(p, sg_length & 0xff, HCNT);
+ aic_outb(p, (sg_length >> 8) & 0xff, HCNT + 1);
+ aic_outb(p, (sg_length >> 16) & 0xff, HCNT + 2);
+ aic_outb(p, (tmp << 2) | ((tmp == 1) ? LAST_SEG : 0), SG_CACHEPTR);
+ aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
+ while(aic_inb(p, SSTAT0) & SDONE) udelay(1);
+ while(aic_inb(p, DFCNTRL) & (HDMAEN|SCSIEN)) aic_outb(p, 0, DFCNTRL);
+ }
+ break;
+
+#ifdef AIC7XXX_NOT_YET
+ case TRACEPOINT2:
+ {
+ printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
+ channel, target, lun);
+ }
+ break;
+
+ /* XXX Fill these in later */
+ case MSG_BUFFER_BUSY:
+ printk("aic7xxx: Message buffer busy.\n");
+ break;
+ case MSGIN_PHASEMIS:
+ printk("aic7xxx: Message-in phasemis.\n");
+ break;
+#endif
+
+ default: /* unknown */
+ printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+ p->host_no, channel, target, lun, intstat,
+ aic_inb(p, SCSISIGI));
+ break;
+ }
+
+ /*
+ * Clear the sequencer interrupt and unpause the sequencer.
+ */
+ unpause_sequencer(p, /* unpause always */ TRUE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_parse_msg
+ *
+ * Description:
+ * Parses incoming messages into actions on behalf of
+ * aic7xxx_handle_reqinit
+ *_F*************************************************************************/
+static int
+aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int reject, reply, done;
+ unsigned char target_scsirate, tindex;
+ unsigned short target_mask;
+ unsigned char target, channel, lun;
+ unsigned char bus_width, new_bus_width;
+ unsigned char trans_options, new_trans_options;
+ unsigned int period, new_period, offset, new_offset, maxsync;
+ struct aic7xxx_syncrate *syncrate;
+ struct aic_dev_data *aic_dev;
+
+ target = scb->cmd->device->id;
+ channel = scb->cmd->device->channel;
+ lun = scb->cmd->device->lun;
+ reply = reject = done = FALSE;
+ tindex = TARGET_INDEX(scb->cmd);
+ aic_dev = AIC_DEV(scb->cmd);
+ target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+ target_mask = (0x01 << tindex);
+
+ /*
+ * Parse as much of the message as is available,
+ * rejecting it if we don't support it. When
+ * the entire message is available and has been
+ * handled, return TRUE indicating that we have
+ * parsed an entire message.
+ */
+
+ if (p->msg_buf[0] != MSG_EXTENDED)
+ {
+ reject = TRUE;
+ }
+
+ /*
+ * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
+ * using the SDTR messages. We need the PPR messages to enable the
+ * higher speeds that include things like Dual Edge clocking.
+ */
+ if (p->features & AHC_ULTRA2)
+ {
+ if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+ !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+ {
+ if (p->features & AHC_ULTRA3)
+ maxsync = AHC_SYNCRATE_ULTRA3;
+ else
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ }
+ else
+ {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ }
+ }
+ else if (p->features & AHC_ULTRA)
+ {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ }
+ else
+ {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+
+ /*
+ * Just accept the length byte outright and perform
+ * more checking once we know the message type.
+ */
+
+ if ( !reject && (p->msg_len > 2) )
+ {
+ switch(p->msg_buf[2])
+ {
+ case MSG_EXT_SDTR:
+ {
+
+ if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
+ {
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
+ {
+ break;
+ }
+
+ period = new_period = p->msg_buf[3];
+ offset = new_offset = p->msg_buf[4];
+ trans_options = new_trans_options = 0;
+ bus_width = new_bus_width = target_scsirate & WIDEXFER;
+
+ /*
+ * If our current max syncrate is in the Ultra3 range, bump it back
+ * down to Ultra2 since we can't negotiate DT transfers using SDTR
+ */
+ if(maxsync == AHC_SYNCRATE_ULTRA3)
+ maxsync = AHC_SYNCRATE_ULTRA2;
+
+ /*
+ * We might have a device that is starting negotiation with us
+ * before we can start up negotiation with it....be prepared to
+ * have a device ask for a higher speed then we want to give it
+ * in that case
+ */
+ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
+ {
+ if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
+ {
+ /*
+ * We shouldn't get here unless this is a narrow drive, wide
+ * devices should trigger this same section of code in the WDTR
+ * handler first instead.
+ */
+ aic_dev->goal.width = MSG_EXT_WDTR_BUS_8_BIT;
+ aic_dev->goal.options = 0;
+ if(p->user[tindex].offset)
+ {
+ aic_dev->needsdtr_copy = 1;
+ aic_dev->goal.period = max_t(unsigned char, 10,p->user[tindex].period);
+ if(p->features & AHC_ULTRA2)
+ {
+ aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
+ }
+ else
+ {
+ aic_dev->goal.offset = MAX_OFFSET_8BIT;
+ }
+ }
+ else
+ {
+ aic_dev->needsdtr_copy = 0;
+ aic_dev->goal.period = 255;
+ aic_dev->goal.offset = 0;
+ }
+ aic_dev->flags |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
+ }
+ else if (aic_dev->needsdtr_copy == 0)
+ {
+ /*
+ * This is a preemptive message from the target, we've already
+ * scanned this target and set our options for it, and we
+ * don't need a SDTR with this target (for whatever reason),
+ * so reject this incoming SDTR
+ */
+ reject = TRUE;
+ break;
+ }
+
+ /* The device is sending this message first and we have to reply */
+ reply = TRUE;
+
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Received pre-emptive SDTR message from "
+ "target.\n", p->host_no, CTL_OF_SCB(scb));
+ }
+ /*
+ * Validate the values the device passed to us against our SEEPROM
+ * settings. We don't have to do this if we aren't replying since
+ * the device isn't allowed to send values greater than the ones
+ * we first sent to it.
+ */
+ new_period = max_t(unsigned int, period, aic_dev->goal.period);
+ new_offset = min_t(unsigned int, offset, aic_dev->goal.offset);
+ }
+
+ /*
+ * Use our new_period, new_offset, bus_width, and card options
+ * to determine the actual syncrate settings
+ */
+ syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
+ &trans_options);
+ aic7xxx_validate_offset(p, syncrate, &new_offset, bus_width);
+
+ /*
+ * Did we drop to async? If so, send a reply regardless of whether
+ * or not we initiated this negotiation.
+ */
+ if ((new_offset == 0) && (new_offset != offset))
+ {
+ aic_dev->needsdtr_copy = 0;
+ reply = TRUE;
+ }
+
+ /*
+ * Did we start this, if not, or if we went too low and had to
+ * go async, then send an SDTR back to the target
+ */
+ if(reply)
+ {
+ /* when sending a reply, make sure that the goal settings are
+ * updated along with current and active since the code that
+ * will actually build the message for the sequencer uses the
+ * goal settings as its guidelines.
+ */
+ aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
+ new_offset, trans_options,
+ AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
+ aic_dev);
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ else
+ {
+ aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
+ new_offset, trans_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
+ aic_dev->needsdtr = 0;
+ }
+ done = TRUE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+
+ if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
+ {
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
+ {
+ break;
+ }
+
+ bus_width = new_bus_width = p->msg_buf[3];
+
+ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
+ {
+ switch(bus_width)
+ {
+ default:
+ {
+ reject = TRUE;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ ((aic_dev->flags & DEVICE_PRINT_DTR) ||
+ (aic7xxx_verbose > 0xffff)) )
+ {
+ printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+ p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+ }
+ } /* We fall through on purpose */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ aic_dev->goal.width = MSG_EXT_WDTR_BUS_8_BIT;
+ aic_dev->needwdtr_copy &= ~target_mask;
+ break;
+ }
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ {
+ break;
+ }
+ }
+ aic_dev->needwdtr = 0;
+ aic7xxx_set_width(p, target, channel, lun, new_bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
+ }
+ else
+ {
+ if ( !(aic_dev->flags & DEVICE_DTR_SCANNED) )
+ {
+ /*
+ * Well, we now know the WDTR and SYNC caps of this device since
+ * it contacted us first, mark it as such and copy the user stuff
+ * over to the goal stuff.
+ */
+ if( (p->features & AHC_WIDE) && p->user[tindex].width )
+ {
+ aic_dev->goal.width = MSG_EXT_WDTR_BUS_16_BIT;
+ aic_dev->needwdtr_copy = 1;
+ }
+
+ /*
+ * Devices that support DT transfers don't start WDTR requests
+ */
+ aic_dev->goal.options = 0;
+
+ if(p->user[tindex].offset)
+ {
+ aic_dev->needsdtr_copy = 1;
+ aic_dev->goal.period = max_t(unsigned char, 10, p->user[tindex].period);
+ if(p->features & AHC_ULTRA2)
+ {
+ aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
+ }
+ else if( aic_dev->goal.width )
+ {
+ aic_dev->goal.offset = MAX_OFFSET_16BIT;
+ }
+ else
+ {
+ aic_dev->goal.offset = MAX_OFFSET_8BIT;
+ }
+ } else {
+ aic_dev->needsdtr_copy = 0;
+ aic_dev->goal.period = 255;
+ aic_dev->goal.offset = 0;
+ }
+
+ aic_dev->flags |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
+ }
+ else if (aic_dev->needwdtr_copy == 0)
+ {
+ /*
+ * This is a preemptive message from the target, we've already
+ * scanned this target and set our options for it, and we
+ * don't need a WDTR with this target (for whatever reason),
+ * so reject this incoming WDTR
+ */
+ reject = TRUE;
+ break;
+ }
+
+ /* The device is sending this message first and we have to reply */
+ reply = TRUE;
+
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Received pre-emptive WDTR message from "
+ "target.\n", p->host_no, CTL_OF_SCB(scb));
+ }
+ switch(bus_width)
+ {
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ {
+ if ( (p->features & AHC_WIDE) &&
+ (aic_dev->goal.width == MSG_EXT_WDTR_BUS_16_BIT) )
+ {
+ new_bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ } /* Fall through if we aren't a wide card */
+ default:
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ aic_dev->needwdtr_copy = 0;
+ new_bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ }
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ aic_dev->needwdtr = 0;
+ if(aic_dev->dtr_pending == 0)
+ {
+ /* there is no other command with SCB_DTR_SCB already set that will
+ * trigger the release of the dtr_pending bit. Both set the bit
+ * and set scb->flags |= SCB_DTR_SCB
+ */
+ aic_dev->dtr_pending = 1;
+ scb->flags |= SCB_DTR_SCB;
+ }
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ /* when sending a reply, make sure that the goal settings are
+ * updated along with current and active since the code that
+ * will actually build the message for the sequencer uses the
+ * goal settings as its guidelines.
+ */
+ aic7xxx_set_width(p, target, channel, lun, new_bus_width,
+ AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
+ aic_dev);
+ }
+
+ /*
+ * By virtue of the SCSI spec, a WDTR message negates any existing
+ * SDTR negotiations. So, even if needsdtr isn't marked for this
+ * device, we still have to do a new SDTR message if the device
+ * supports SDTR at all. Therefore, we check needsdtr_copy instead
+ * of needstr.
+ */
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
+ aic_dev);
+ aic_dev->needsdtr = aic_dev->needsdtr_copy;
+ done = TRUE;
+ break;
+ }
+ case MSG_EXT_PPR:
+ {
+
+ if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
+ {
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
+ {
+ break;
+ }
+
+ period = new_period = p->msg_buf[3];
+ offset = new_offset = p->msg_buf[5];
+ bus_width = new_bus_width = p->msg_buf[6];
+ trans_options = new_trans_options = p->msg_buf[7] & 0xf;
+
+ if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)\n",
+ p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
+ trans_options);
+ }
+
+ /*
+ * We might have a device that is starting negotiation with us
+ * before we can start up negotiation with it....be prepared to
+ * have a device ask for a higher speed then we want to give it
+ * in that case
+ */
+ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
+ {
+ /* Have we scanned the device yet? */
+ if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
+ {
+ /* The device is electing to use PPR messages, so we will too until
+ * we know better */
+ aic_dev->needppr = aic_dev->needppr_copy = 1;
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
+
+ /* We know the device is SCSI-3 compliant due to PPR */
+ aic_dev->flags |= DEVICE_SCSI_3;
+
+ /*
+ * Not only is the device starting this up, but it also hasn't
+ * been scanned yet, so this would likely be our TUR or our
+ * INQUIRY command at scan time, so we need to use the
+ * settings from the SEEPROM if they existed. Of course, even
+ * if we didn't find a SEEPROM, we stuffed default values into
+ * the user settings anyway, so use those in all cases.
+ */
+ aic_dev->goal.width = p->user[tindex].width;
+ if(p->user[tindex].offset)
+ {
+ aic_dev->goal.period = p->user[tindex].period;
+ aic_dev->goal.options = p->user[tindex].options;
+ if(p->features & AHC_ULTRA2)
+ {
+ aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
+ }
+ else if( aic_dev->goal.width &&
+ (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+ p->features & AHC_WIDE )
+ {
+ aic_dev->goal.offset = MAX_OFFSET_16BIT;
+ }
+ else
+ {
+ aic_dev->goal.offset = MAX_OFFSET_8BIT;
+ }
+ }
+ else
+ {
+ aic_dev->goal.period = 255;
+ aic_dev->goal.offset = 0;
+ aic_dev->goal.options = 0;
+ }
+ aic_dev->flags |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR;
+ }
+ else if (aic_dev->needppr_copy == 0)
+ {
+ /*
+ * This is a preemptive message from the target, we've already
+ * scanned this target and set our options for it, and we
+ * don't need a PPR with this target (for whatever reason),
+ * so reject this incoming PPR
+ */
+ reject = TRUE;
+ break;
+ }
+
+ /* The device is sending this message first and we have to reply */
+ reply = TRUE;
+
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Received pre-emptive PPR message from "
+ "target.\n", p->host_no, CTL_OF_SCB(scb));
+ }
+
+ }
+
+ switch(bus_width)
+ {
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ {
+ if ( (aic_dev->goal.width == MSG_EXT_WDTR_BUS_16_BIT) &&
+ p->features & AHC_WIDE)
+ {
+ break;
+ }
+ }
+ default:
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ ((aic_dev->flags & DEVICE_PRINT_DTR) ||
+ (aic7xxx_verbose > 0xffff)) )
+ {
+ reply = TRUE;
+ printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+ p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+ }
+ } /* We fall through on purpose */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ /*
+ * According to the spec, if we aren't wide, we also can't be
+ * Dual Edge so clear the options byte
+ */
+ new_trans_options = 0;
+ new_bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ }
+
+ if(reply)
+ {
+ /* when sending a reply, make sure that the goal settings are
+ * updated along with current and active since the code that
+ * will actually build the message for the sequencer uses the
+ * goal settings as its guidelines.
+ */
+ aic7xxx_set_width(p, target, channel, lun, new_bus_width,
+ AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
+ aic_dev);
+ syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
+ &new_trans_options);
+ aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width);
+ aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
+ new_offset, new_trans_options,
+ AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR,
+ aic_dev);
+ }
+ else
+ {
+ aic7xxx_set_width(p, target, channel, lun, new_bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
+ syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync,
+ &new_trans_options);
+ aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width);
+ aic7xxx_set_syncrate(p, syncrate, target, channel, new_period,
+ new_offset, new_trans_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR, aic_dev);
+ }
+
+ /*
+ * As it turns out, if we don't *have* to have PPR messages, then
+ * configure ourselves not to use them since that makes some
+ * external drive chassis work (those chassis can't parse PPR
+ * messages and they mangle the SCSI bus until you send a WDTR
+ * and SDTR that they can understand).
+ */
+ if(new_trans_options == 0)
+ {
+ aic_dev->needppr = aic_dev->needppr_copy = 0;
+ if(new_offset)
+ {
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
+ }
+ if (new_bus_width)
+ {
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
+ }
+ }
+
+ if((new_offset == 0) && (offset != 0))
+ {
+ /*
+ * Oops, the syncrate went to low for this card and we fell off
+ * to async (should never happen with a device that uses PPR
+ * messages, but have to be complete)
+ */
+ reply = TRUE;
+ }
+
+ if(reply)
+ {
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_PPR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ else
+ {
+ aic_dev->needppr = 0;
+ }
+ done = TRUE;
+ break;
+ }
+ default:
+ {
+ reject = TRUE;
+ break;
+ }
+ } /* end of switch(p->msg_type) */
+ } /* end of if (!reject && (p->msg_len > 2)) */
+
+ if (!reply && reject)
+ {
+ aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ done = TRUE;
+ }
+ return(done);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_reqinit
+ *
+ * Description:
+ * Interrupt handler for REQINIT interrupts (used to transfer messages to
+ * and from devices).
+ *_F*************************************************************************/
+static void
+aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ unsigned char lastbyte;
+ unsigned char phasemis;
+ int done = FALSE;
+
+ switch(p->msg_type)
+ {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ if (p->msg_len == 0)
+ panic("aic7xxx: REQINIT with no active message!\n");
+
+ lastbyte = (p->msg_index == (p->msg_len - 1));
+ phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
+
+ if (lastbyte || phasemis)
+ {
+ /* Time to end the message */
+ p->msg_len = 0;
+ p->msg_type = MSG_TYPE_NONE;
+ /*
+ * NOTE-TO-MYSELF: If you clear the REQINIT after you
+ * disable REQINITs, then cases of REJECT_MSG stop working
+ * and hang the bus
+ */
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+
+ if (phasemis == 0)
+ {
+ aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
+ aic_outb(p, 0, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Completed sending of REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+#endif
+ }
+ else
+ {
+ aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+#endif
+ }
+ unpause_sequencer(p, TRUE);
+ }
+ else
+ {
+ /*
+ * Present the byte on the bus (clearing REQINIT) but don't
+ * unpause the sequencer.
+ */
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ aic_outb(p, p->msg_buf[p->msg_index++], SCSIDATL);
+ }
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
+
+ if (phasemis == 0)
+ {
+ p->msg_len++;
+ /* Pull the byte in without acking it */
+ p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
+ done = aic7xxx_parse_msg(p, scb);
+ /* Ack the byte */
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ aic_inb(p, SCSIDATL);
+ p->msg_index++;
+ }
+ if (phasemis || done)
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ if (phasemis)
+ printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ else
+ printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+#endif
+ /* Time to end our message session */
+ p->msg_len = 0;
+ p->msg_type = MSG_TYPE_NONE;
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ unpause_sequencer(p, TRUE);
+ }
+ break;
+ }
+ default:
+ {
+ panic("aic7xxx: Unknown REQINIT message type.\n");
+ break;
+ }
+ } /* End of switch(p->msg_type) */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_scsiint
+ *
+ * Description:
+ * Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ unsigned char scb_index;
+ unsigned char status;
+ struct aic7xxx_scb *scb;
+ struct aic_dev_data *aic_dev;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ status = aic_inb(p, SSTAT1);
+
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
+ }
+ }
+ else
+ {
+ scb = NULL;
+ }
+
+
+ if ((status & SCSIRSTI) != 0)
+ {
+ int channel;
+
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ else
+ channel = 0;
+
+ if (aic7xxx_verbose & VERBOSE_RESET)
+ printk(WARN_LEAD "Someone else reset the channel!!\n",
+ p->host_no, channel, -1, -1);
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, NULL);
+ /*
+ * Go through and abort all commands for the channel, but do not
+ * reset the channel again.
+ */
+ aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+ aic7xxx_run_done_queue(p, TRUE);
+ scb = NULL;
+ }
+ else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
+ {
+ /*
+ * First look at what phase we were last in. If it's message-out,
+ * chances are pretty good that the bus free was in response to
+ * one of our abort requests.
+ */
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
+ unsigned char target = (saved_tcl >> 4) & 0x0F;
+ int channel;
+ int printerror = TRUE;
+
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ else
+ channel = 0;
+
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+ SCSISEQ);
+ if (lastphase == P_MESGOUT)
+ {
+ unsigned char message;
+
+ message = aic_inb(p, SINDEX);
+
+ if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+ (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+ aic7xxx_run_done_queue(p, TRUE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_BUS_DEV_RESET)
+ {
+ aic7xxx_handle_device_reset(p, target, channel);
+ scb = NULL;
+ printerror = 0;
+ }
+ }
+ if ( (scb != NULL) && (scb->flags & SCB_DTR_SCB) )
+ {
+ /*
+ * Hmmm...error during a negotiation command. Either we have a
+ * borken bus, or the device doesn't like our negotiation message.
+ * Since we check the INQUIRY data of a device before sending it
+ * negotiation messages, assume the bus is borken for whatever
+ * reason. Complete the command.
+ */
+ printerror = 0;
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag);
+ aic7xxx_run_done_queue(p, TRUE);
+ scb = NULL;
+ }
+ if (printerror != 0)
+ {
+ if (scb != NULL)
+ {
+ unsigned char tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ tag = scb->hscb->tag;
+ }
+ else
+ {
+ tag = SCB_LIST_NULL;
+ }
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
+ aic7xxx_run_done_queue(p, TRUE);
+ }
+ else
+ {
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ aic7xxx_run_done_queue(p, TRUE);
+ }
+ printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
+ "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+ scb = NULL;
+ }
+ aic_outb(p, MSG_NOOP, MSG_OUT);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+ SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, CLRBUSFREE, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ restart_sequencer(p);
+ unpause_sequencer(p, TRUE);
+ }
+ else if ((status & SELTO) != 0)
+ {
+ unsigned char scbptr;
+ unsigned char nextscb;
+ Scsi_Cmnd *cmd;
+
+ scbptr = aic_inb(p, WAITING_SCBH);
+ if (scbptr > p->scb_data->maxhscbs)
+ {
+ /*
+ * I'm still trying to track down exactly how this happens, but until
+ * I find it, this code will make sure we aren't passing bogus values
+ * into the SCBPTR register, even if that register will just wrap
+ * things around, we still don't like having out of range variables.
+ *
+ * NOTE: Don't check the aic7xxx_verbose variable, I want this message
+ * to always be displayed.
+ */
+ printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
+ p->host_no, -1, -1, -1, scbptr);
+ if (p->scb_data->maxhscbs > 4)
+ scbptr &= (p->scb_data->maxhscbs - 1);
+ else
+ scbptr &= 0x03;
+ }
+ aic_outb(p, scbptr, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+
+ scb = NULL;
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
+ }
+ }
+ if (scb == NULL)
+ {
+ printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
+ p->host_no, -1, -1, -1, scb_index);
+ printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+ "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, NULL);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ cmd->result = (DID_TIME_OUT << 16);
+
+ /*
+ * Clear out this hardware SCB
+ */
+ aic_outb(p, 0, SCB_CONTROL);
+
+ /*
+ * Clear out a few values in the card that are in an undetermined
+ * state.
+ */
+ aic_outb(p, MSG_NOOP, MSG_OUT);
+
+ /*
+ * Shift the waiting for selection queue forward
+ */
+ nextscb = aic_inb(p, SCB_NEXT);
+ aic_outb(p, nextscb, WAITING_SCBH);
+
+ /*
+ * Put this SCB back on the free list.
+ */
+ aic7xxx_add_curscb_to_free_list(p);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
+#endif
+ if (scb->flags & SCB_QUEUED_ABORT)
+ {
+ /*
+ * We know that this particular SCB had to be the queued abort since
+ * the disconnected SCB would have gotten a reconnect instead.
+ * What we need to do then is to let the command timeout again so
+ * we get a reset since this abort just failed.
+ */
+ cmd->result = 0;
+ scb = NULL;
+ }
+ }
+ /*
+ * Keep the sequencer from trying to restart any selections
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ /*
+ * Make sure the data bits on the bus are released
+ * Don't do this on 7770 chipsets, it makes them give us
+ * a BRKADDRINT and kills the card.
+ */
+ if( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+ aic_outb(p, 0, SCSIBUSL);
+
+ /*
+ * Delay for the selection timeout delay period then stop the selection
+ */
+ udelay(301);
+ aic_outb(p, CLRSELINGO, CLRSINT0);
+ /*
+ * Clear out all the interrupt status bits
+ */
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ /*
+ * Restarting the sequencer will stop the selection and make sure devices
+ * are allowed to reselect in.
+ */
+ restart_sequencer(p);
+ unpause_sequencer(p, TRUE);
+ }
+ else if (scb == NULL)
+ {
+ printk(WARN_LEAD "aic7xxx_isr - referenced scb not valid "
+ "during scsiint 0x%x scb(%d)\n"
+ " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+ p->host_no, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
+ aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+ /*
+ * Turn off the interrupt and set status to zero, so that it
+ * falls through the rest of the SCSIINT code.
+ */
+ aic_outb(p, status, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ else if (status & SCSIPERR)
+ {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ */
+ char *phase;
+ Scsi_Cmnd *cmd;
+ unsigned char mesg_out = MSG_NOOP;
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned char sstat2 = aic_inb(p, SSTAT2);
+
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ if( (p->features & AHC_ULTRA3) &&
+ (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) &&
+ (lastphase == P_DATAIN) )
+ {
+ printk(WARN_LEAD "CRC error during %s phase.\n",
+ p->host_no, CTL_OF_SCB(scb), phase);
+ if(sstat2 & CRCVALERR)
+ {
+ printk(WARN_LEAD " CRC error in intermediate CRC packet.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ if(sstat2 & CRCENDERR)
+ {
+ printk(WARN_LEAD " CRC error in ending CRC packet.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ if(sstat2 & CRCREQERR)
+ {
+ printk(WARN_LEAD " Target incorrectly requested a CRC packet.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ if(sstat2 & DUAL_EDGE_ERROR)
+ {
+ printk(WARN_LEAD " Dual Edge transmission error.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ }
+ else if( (lastphase == P_MESGOUT) &&
+ (scb->flags & SCB_MSGOUT_PPR) )
+ {
+ /*
+ * As per the draft specs, any device capable of supporting any of
+ * the option values other than 0 are not allowed to reject the
+ * PPR message. Instead, they must negotiate out what they do
+ * support instead of rejecting our offering or else they cause
+ * a parity error during msg_out phase to signal that they don't
+ * like our settings.
+ */
+ aic_dev = AIC_DEV(scb->cmd);
+ aic_dev->needppr = aic_dev->needppr_copy = 0;
+ aic7xxx_set_width(p, scb->cmd->device->id, scb->cmd->device->channel, scb->cmd->device->lun,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE),
+ aic_dev);
+ aic7xxx_set_syncrate(p, NULL, scb->cmd->device->id, scb->cmd->device->channel, 0, 0,
+ 0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE,
+ aic_dev);
+ aic_dev->goal.options = 0;
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "parity error during PPR message, reverting "
+ "to WDTR/SDTR\n", p->host_no, CTL_OF_SCB(scb));
+ }
+ if ( aic_dev->goal.width )
+ {
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
+ }
+ if ( aic_dev->goal.offset )
+ {
+ if( aic_dev->goal.period <= 9 )
+ {
+ aic_dev->goal.period = 10;
+ }
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
+ }
+ scb = NULL;
+ }
+
+ /*
+ * We've set the hardware to assert ATN if we get a parity
+ * error on "in" phases, so all we need to do is stuff the
+ * message buffer with the appropriate message. "In" phases
+ * have set mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP)
+ {
+ aic_outb(p, mesg_out, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ scb = NULL;
+ }
+ aic_outb(p, CLRSCSIPERR, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ }
+ else if ( (status & REQINIT) &&
+ (p->flags & AHC_HANDLING_REQINITS) )
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
+ CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
+#endif
+ aic7xxx_handle_reqinit(p, scb);
+ return;
+ }
+ else
+ {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ if (aic7xxx_verbose & VERBOSE_SCSIINT)
+ printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
+ p->host_no, -1, -1, -1, status);
+ aic_outb(p, status, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ if (scb != NULL)
+ {
+ aic7xxx_done(p, scb);
+ }
+}
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void
+aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
+{
+ unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
+ int i, bogus, lost;
+ static unsigned char scb_status[AIC7XXX_MAXSCB];
+
+#define SCB_NO_LIST 0
+#define SCB_FREE_LIST 1
+#define SCB_WAITING_LIST 2
+#define SCB_DISCONNECTED_LIST 4
+#define SCB_CURRENTLY_ACTIVE 8
+
+ /*
+ * Note, these checks will fail on a regular basis once the machine moves
+ * beyond the bus scan phase. The problem is race conditions concerning
+ * the scbs and where they are linked in. When you have 30 or so commands
+ * outstanding on the bus, and run this twice with every interrupt, the
+ * chances get pretty good that you'll catch the sequencer with an SCB
+ * only partially linked in. Therefore, once we pass the scan phase
+ * of the bus, we really should disable this function.
+ */
+ bogus = FALSE;
+ memset(&scb_status[0], 0, sizeof(scb_status));
+ pause_sequencer(p);
+ saved_scbptr = aic_inb(p, SCBPTR);
+ if (saved_scbptr >= p->scb_data->maxhscbs)
+ {
+ printk("Bogus SCBPTR %d\n", saved_scbptr);
+ bogus = TRUE;
+ }
+ scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
+ free_scbh = aic_inb(p, FREE_SCBH);
+ if ( (free_scbh != SCB_LIST_NULL) &&
+ (free_scbh >= p->scb_data->maxhscbs) )
+ {
+ printk("Bogus FREE_SCBH %d\n", free_scbh);
+ bogus = TRUE;
+ }
+ else
+ {
+ temp = free_scbh;
+ while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+ {
+ if(scb_status[temp] & 0x07)
+ {
+ printk("HSCB %d on multiple lists, status 0x%02x", temp,
+ scb_status[temp] | SCB_FREE_LIST);
+ bogus = TRUE;
+ }
+ scb_status[temp] |= SCB_FREE_LIST;
+ aic_outb(p, temp, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ }
+ }
+
+ dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
+ if ( (dis_scbh != SCB_LIST_NULL) &&
+ (dis_scbh >= p->scb_data->maxhscbs) )
+ {
+ printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
+ bogus = TRUE;
+ }
+ else
+ {
+ temp = dis_scbh;
+ while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+ {
+ if(scb_status[temp] & 0x07)
+ {
+ printk("HSCB %d on multiple lists, status 0x%02x", temp,
+ scb_status[temp] | SCB_DISCONNECTED_LIST);
+ bogus = TRUE;
+ }
+ scb_status[temp] |= SCB_DISCONNECTED_LIST;
+ aic_outb(p, temp, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ }
+ }
+
+ wait_scbh = aic_inb(p, WAITING_SCBH);
+ if ( (wait_scbh != SCB_LIST_NULL) &&
+ (wait_scbh >= p->scb_data->maxhscbs) )
+ {
+ printk("Bogus WAITING_SCBH %d\n", wait_scbh);
+ bogus = TRUE;
+ }
+ else
+ {
+ temp = wait_scbh;
+ while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+ {
+ if(scb_status[temp] & 0x07)
+ {
+ printk("HSCB %d on multiple lists, status 0x%02x", temp,
+ scb_status[temp] | SCB_WAITING_LIST);
+ bogus = TRUE;
+ }
+ scb_status[temp] |= SCB_WAITING_LIST;
+ aic_outb(p, temp, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ }
+ }
+
+ lost=0;
+ for(i=0; i < p->scb_data->maxhscbs; i++)
+ {
+ aic_outb(p, i, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ if ( ((temp != SCB_LIST_NULL) &&
+ (temp >= p->scb_data->maxhscbs)) )
+ {
+ printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
+ bogus = TRUE;
+ }
+ if ( temp == i )
+ {
+ printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
+ bogus = TRUE;
+ }
+ if (scb_status[i] == 0)
+ lost++;
+ if (lost > 1)
+ {
+ printk("Too many lost scbs.\n");
+ bogus=TRUE;
+ }
+ }
+ aic_outb(p, saved_scbptr, SCBPTR);
+ unpause_sequencer(p, FALSE);
+ if (bogus)
+ {
+ printk("Bogus parameters found in card SCB array structures.\n");
+ printk("%s\n", buffer);
+ aic7xxx_panic_abort(p, NULL);
+ }
+ return;
+}
+#endif
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_command_completion_intr
+ *
+ * Description:
+ * SCSI command completion interrupt handler.
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p)
+{
+ struct aic7xxx_scb *scb = NULL;
+ struct aic_dev_data *aic_dev;
+ Scsi_Cmnd *cmd;
+ unsigned char scb_index, tindex;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) )
+ printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
+#endif
+
+ /*
+ * Read the INTSTAT location after clearing the CMDINT bit. This forces
+ * any posted PCI writes to flush to memory. Gerard Roudier suggested
+ * this fix to the possible race of clearing the CMDINT bit but not
+ * having all command bytes flushed onto the qoutfifo.
+ */
+ aic_outb(p, CLRCMDINT, CLRINT);
+ aic_inb(p, INTSTAT);
+ /*
+ * The sequencer will continue running when it
+ * issues this interrupt. There may be >1 commands
+ * finished, so loop until we've processed them all.
+ */
+
+ while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
+ {
+ scb_index = p->qoutfifo[p->qoutfifonext];
+ p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
+ if ( scb_index >= p->scb_data->numscbs )
+ {
+ printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
+ -1, -1, -1, scb_index);
+ continue;
+ }
+ scb = p->scb_data->scb_array[scb_index];
+ if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
+ "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
+ (unsigned long) scb->cmd);
+ continue;
+ }
+ tindex = TARGET_INDEX(scb->cmd);
+ aic_dev = AIC_DEV(scb->cmd);
+ if (scb->flags & SCB_QUEUED_ABORT)
+ {
+ pause_sequencer(p);
+ if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) &&
+ (aic_inb(p, SCB_TAG) == scb->hscb->tag) )
+ {
+ unpause_sequencer(p, FALSE);
+ continue;
+ }
+ aic7xxx_reset_device(p, scb->cmd->device->id, scb->cmd->device->channel,
+ scb->cmd->device->lun, scb->hscb->tag);
+ scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT |
+ SCB_QUEUED_ABORT);
+ unpause_sequencer(p, FALSE);
+ }
+ else if (scb->flags & SCB_ABORT)
+ {
+ /*
+ * We started to abort this, but it completed on us, let it
+ * through as successful
+ */
+ scb->flags &= ~(SCB_ABORT|SCB_RESET);
+ }
+ else if (scb->flags & SCB_SENSE)
+ {
+ char *buffer = &scb->cmd->sense_buffer[0];
+
+ if (buffer[12] == 0x47 || buffer[12] == 0x54)
+ {
+ /*
+ * Signal that we need to re-negotiate things.
+ */
+ aic_dev->needppr = aic_dev->needppr_copy;
+ aic_dev->needsdtr = aic_dev->needsdtr_copy;
+ aic_dev->needwdtr = aic_dev->needwdtr_copy;
+ }
+ }
+ cmd = scb->cmd;
+ if (scb->hscb->residual_SG_segment_count != 0)
+ {
+ aic7xxx_calculate_residual(p, scb);
+ }
+ cmd->result |= (aic7xxx_error(cmd) << 16);
+ aic7xxx_done(p, scb);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_isr
+ *
+ * Description:
+ * SCSI controller interrupt handler.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aic7xxx_host *p;
+ unsigned char intstat;
+
+ p = (struct aic7xxx_host *)dev_id;
+
+ /*
+ * Just a few sanity checks. Make sure that we have an int pending.
+ * Also, if PCI, then we are going to check for a PCI bus error status
+ * should we get too many spurious interrupts.
+ */
+ if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
+ {
+#ifdef CONFIG_PCI
+ if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
+ !(p->flags & AHC_HANDLING_REQINITS) )
+ {
+ if ( aic_inb(p, ERROR) & PCIERRSTAT )
+ {
+ aic7xxx_pci_intr(p);
+ }
+ p->spurious_int = 0;
+ }
+ else if ( !(p->flags & AHC_HANDLING_REQINITS) )
+ {
+ p->spurious_int++;
+ }
+#endif
+ return;
+ }
+
+ p->spurious_int = 0;
+
+ /*
+ * Keep track of interrupts for /proc/scsi
+ */
+ p->isr_count++;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+ (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+ aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
+#endif
+
+ /*
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
+ */
+ if (intstat & CMDCMPLT)
+ {
+ aic7xxx_handle_command_completion_intr(p);
+ }
+
+ if (intstat & BRKADRINT)
+ {
+ int i;
+ unsigned char errno = aic_inb(p, ERROR);
+
+ printk(KERN_ERR "(scsi%d) BRKADRINT error(0x%x):\n", p->host_no, errno);
+ for (i = 0; i < ARRAY_SIZE(hard_error); i++)
+ {
+ if (errno & hard_error[i].errno)
+ {
+ printk(KERN_ERR " %s\n", hard_error[i].errmesg);
+ }
+ }
+ printk(KERN_ERR "(scsi%d) SEQADDR=0x%x\n", p->host_no,
+ (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, NULL);
+#ifdef CONFIG_PCI
+ if (errno & PCIERRSTAT)
+ aic7xxx_pci_intr(p);
+#endif
+ if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
+ {
+ panic("aic7xxx: unrecoverable BRKADRINT.\n");
+ }
+ if (errno & ILLHADDR)
+ {
+ printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
+ "pausing controller!\n", p->host_no);
+ }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (errno & DPARERR)
+ {
+ if (aic_inb(p, DMAPARAMS) & DIRECTION)
+ printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
+ else
+ printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
+ }
+#endif
+ aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
+ unpause_sequencer(p, FALSE);
+ }
+
+ if (intstat & SEQINT)
+ {
+ /*
+ * Read the CCSCBCTL register to work around a bug in the Ultra2 cards
+ */
+ if(p->features & AHC_ULTRA2)
+ {
+ aic_inb(p, CCSCBCTL);
+ }
+ aic7xxx_handle_seqint(p, intstat);
+ }
+
+ if (intstat & SCSIINT)
+ {
+ aic7xxx_handle_scsiint(p, intstat);
+ }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+ (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+ aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
+#endif
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * do_aic7xxx_isr
+ *
+ * Description:
+ * This is a gross hack to solve a problem in linux kernels 2.1.85 and
+ * above. Please, children, do not try this at home, and if you ever see
+ * anything like it, please inform the Gross Hack Police immediately
+ *-F*************************************************************************/
+static irqreturn_t
+do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long cpu_flags;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *)dev_id;
+ if(!p)
+ return IRQ_NONE;
+ spin_lock_irqsave(p->host->host_lock, cpu_flags);
+ p->flags |= AHC_IN_ISR;
+ do
+ {
+ aic7xxx_isr(irq, dev_id, regs);
+ } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
+ aic7xxx_done_cmds_complete(p);
+ aic7xxx_run_waiting_queues(p);
+ p->flags &= ~AHC_IN_ISR;
+ spin_unlock_irqrestore(p->host->host_lock, cpu_flags);
+
+ return IRQ_HANDLED;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_init_transinfo
+ *
+ * Description:
+ * Set up the initial aic_dev values from the BIOS settings and from
+ * INQUIRY results
+ *-F*************************************************************************/
+static void
+aic7xxx_init_transinfo(struct aic7xxx_host *p, struct aic_dev_data *aic_dev)
+{
+ Scsi_Device *sdpnt = aic_dev->SDptr;
+ unsigned char tindex;
+
+ tindex = sdpnt->id | (sdpnt->channel << 3);
+ if (!(aic_dev->flags & DEVICE_DTR_SCANNED))
+ {
+ aic_dev->flags |= DEVICE_DTR_SCANNED;
+
+ if ( sdpnt->wdtr && (p->features & AHC_WIDE) )
+ {
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 1;
+ aic_dev->goal.width = p->user[tindex].width;
+ }
+ else
+ {
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
+ pause_sequencer(p);
+ aic7xxx_set_width(p, sdpnt->id, sdpnt->channel, sdpnt->lun,
+ MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
+ AHC_TRANS_GOAL |
+ AHC_TRANS_CUR), aic_dev );
+ unpause_sequencer(p, FALSE);
+ }
+ if ( sdpnt->sdtr && p->user[tindex].offset )
+ {
+ aic_dev->goal.period = p->user[tindex].period;
+ aic_dev->goal.options = p->user[tindex].options;
+ if (p->features & AHC_ULTRA2)
+ aic_dev->goal.offset = MAX_OFFSET_ULTRA2;
+ else if (aic_dev->goal.width == MSG_EXT_WDTR_BUS_16_BIT)
+ aic_dev->goal.offset = MAX_OFFSET_16BIT;
+ else
+ aic_dev->goal.offset = MAX_OFFSET_8BIT;
+ if ( sdpnt->ppr && p->user[tindex].period <= 9 &&
+ p->user[tindex].options )
+ {
+ aic_dev->needppr = aic_dev->needppr_copy = 1;
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
+ aic_dev->needwdtr = aic_dev->needwdtr_copy = 0;
+ aic_dev->flags |= DEVICE_SCSI_3;
+ }
+ else
+ {
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 1;
+ aic_dev->goal.period = max_t(unsigned char, 10, aic_dev->goal.period);
+ aic_dev->goal.options = 0;
+ }
+ }
+ else
+ {
+ aic_dev->needsdtr = aic_dev->needsdtr_copy = 0;
+ aic_dev->goal.period = 255;
+ aic_dev->goal.offset = 0;
+ aic_dev->goal.options = 0;
+ }
+ aic_dev->flags |= DEVICE_PRINT_DTR;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_slave_alloc
+ *
+ * Description:
+ * Set up the initial aic_dev struct pointers
+ *-F*************************************************************************/
+static int
+aic7xxx_slave_alloc(Scsi_Device *SDptr)
+{
+ struct aic7xxx_host *p = (struct aic7xxx_host *)SDptr->host->hostdata;
+ struct aic_dev_data *aic_dev;
+
+ aic_dev = kmalloc(sizeof(struct aic_dev_data), GFP_ATOMIC | GFP_KERNEL);
+ if(!aic_dev)
+ return 1;
+ /*
+ * Check to see if channel was scanned.
+ */
+
+ if (!(p->flags & AHC_A_SCANNED) && (SDptr->channel == 0))
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(INFO_LEAD "Scanning channel for devices.\n",
+ p->host_no, 0, -1, -1);
+ p->flags |= AHC_A_SCANNED;
+ }
+ else
+ {
+ if (!(p->flags & AHC_B_SCANNED) && (SDptr->channel == 1))
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(INFO_LEAD "Scanning channel for devices.\n",
+ p->host_no, 1, -1, -1);
+ p->flags |= AHC_B_SCANNED;
+ }
+ }
+
+ memset(aic_dev, 0, sizeof(struct aic_dev_data));
+ SDptr->hostdata = aic_dev;
+ aic_dev->SDptr = SDptr;
+ aic_dev->max_q_depth = 1;
+ aic_dev->temp_q_depth = 1;
+ scbq_init(&aic_dev->delayed_scbs);
+ INIT_LIST_HEAD(&aic_dev->list);
+ list_add_tail(&aic_dev->list, &p->aic_devs);
+ return 0;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_device_queue_depth
+ *
+ * Description:
+ * Determines the queue depth for a given device. There are two ways
+ * a queue depth can be obtained for a tagged queueing device. One
+ * way is the default queue depth which is determined by whether
+ * aic7xxx_default_queue_depth. The other is by the aic7xxx_tag_info
+ * array.
+ *
+ * If tagged queueing isn't supported on the device, then we set the
+ * depth to p->host->hostt->cmd_per_lun for internal driver queueing.
+ * as the default queue depth. Otherwise, we use either 4 or 8 as the
+ * default queue depth (dependent on the number of hardware SCBs).
+ * The other way we determine queue depth is through the use of the
+ * aic7xxx_tag_info array which is enabled by defining
+ * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized
+ * with queue depths for individual devices. It also allows tagged
+ * queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+ int tag_enabled = FALSE;
+ struct aic_dev_data *aic_dev = device->hostdata;
+ unsigned char tindex;
+
+ tindex = device->id | (device->channel << 3);
+
+ if (device->simple_tags)
+ return; // We've already enabled this device
+
+ if (device->tagged_supported)
+ {
+ tag_enabled = TRUE;
+
+ if (!(p->discenable & (1 << tindex)))
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ printk(INFO_LEAD "Disconnection disabled, unable to "
+ "enable tagged queueing.\n",
+ p->host_no, device->channel, device->id, device->lun);
+ tag_enabled = FALSE;
+ }
+ else
+ {
+ if (p->instance >= ARRAY_SIZE(aic7xxx_tag_info))
+ {
+ static int print_warning = TRUE;
+ if(print_warning)
+ {
+ printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
+ " installed controllers.\n");
+ printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
+ " the aic7xxx.c source file.\n");
+ print_warning = FALSE;
+ }
+ aic_dev->max_q_depth = aic_dev->temp_q_depth =
+ aic7xxx_default_queue_depth;
+ }
+ else
+ {
+
+ if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
+ {
+ tag_enabled = FALSE;
+ }
+ else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
+ {
+ aic_dev->max_q_depth = aic_dev->temp_q_depth =
+ aic7xxx_default_queue_depth;
+ }
+ else
+ {
+ aic_dev->max_q_depth = aic_dev->temp_q_depth =
+ aic7xxx_tag_info[p->instance].tag_commands[tindex];
+ }
+ }
+ }
+ }
+ if (tag_enabled)
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Tagged queuing enabled, queue depth %d.\n",
+ p->host_no, device->channel, device->id,
+ device->lun, aic_dev->max_q_depth);
+ }
+ scsi_adjust_queue_depth(device, MSG_ORDERED_TAG, aic_dev->max_q_depth);
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Tagged queuing disabled, queue depth %d.\n",
+ p->host_no, device->channel, device->id,
+ device->lun, device->host->cmd_per_lun);
+ }
+ scsi_adjust_queue_depth(device, 0, device->host->cmd_per_lun);
+ }
+ return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_slave_destroy
+ *
+ * Description:
+ * prepare for this device to go away
+ *-F*************************************************************************/
+static void
+aic7xxx_slave_destroy(Scsi_Device *SDptr)
+{
+ struct aic_dev_data *aic_dev = SDptr->hostdata;
+
+ list_del(&aic_dev->list);
+ SDptr->hostdata = NULL;
+ kfree(aic_dev);
+ return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_slave_configure
+ *
+ * Description:
+ * Configure the device we are attaching to the controller. This is
+ * where we get to do things like scan the INQUIRY data, set queue
+ * depths, allocate command structs, etc.
+ *-F*************************************************************************/
+static int
+aic7xxx_slave_configure(Scsi_Device *SDptr)
+{
+ struct aic7xxx_host *p = (struct aic7xxx_host *) SDptr->host->hostdata;
+ struct aic_dev_data *aic_dev;
+ int scbnum;
+
+ aic_dev = (struct aic_dev_data *)SDptr->hostdata;
+
+ aic7xxx_init_transinfo(p, aic_dev);
+ aic7xxx_device_queue_depth(p, SDptr);
+ if(list_empty(&aic_dev->list))
+ list_add_tail(&aic_dev->list, &p->aic_devs);
+
+ scbnum = 0;
+ list_for_each_entry(aic_dev, &p->aic_devs, list) {
+ scbnum += aic_dev->max_q_depth;
+ }
+ while (scbnum > p->scb_data->numscbs)
+ {
+ /*
+ * Pre-allocate the needed SCBs to get around the possibility of having
+ * to allocate some when memory is more or less exhausted and we need
+ * the SCB in order to perform a swap operation (possible deadlock)
+ */
+ if ( aic7xxx_allocate_scb(p) == 0 )
+ break;
+ }
+
+
+ return(0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_probe
+ *
+ * Description:
+ * Probing for EISA boards: it looks like the first two bytes
+ * are a manufacturer code - three characters, five bits each:
+ *
+ * BYTE 0 BYTE 1 BYTE 2 BYTE 3
+ * ?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ * The characters are baselined off ASCII '@', so add that value
+ * to each to get the real ASCII code for it. The next two bytes
+ * appear to be a product and revision number, probably vendor-
+ * specific. This is what is being searched for at each port,
+ * and what should probably correspond to the ID= field in the
+ * ECU's .cfg file for the card - if your card is not detected,
+ * make sure your signature is listed in the array.
+ *
+ * The fourth byte's lowest bit seems to be an enabled/disabled
+ * flag (rest of the bits are reserved?).
+ *
+ * NOTE: This function is only needed on Intel and Alpha platforms,
+ * the other platforms we support don't have EISA/VLB busses. So,
+ * we #ifdef this entire function to avoid compiler warnings about
+ * an unused function.
+ *-F*************************************************************************/
+#if defined(__i386__) || defined(__alpha__)
+static int
+aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
+{
+ int i;
+ unsigned char buf[4];
+
+ static struct {
+ int n;
+ unsigned char signature[sizeof(buf)];
+ ahc_chip type;
+ int bios_disabled;
+ } AIC7xxx[] = {
+ { 4, { 0x04, 0x90, 0x77, 0x70 },
+ AHC_AIC7770|AHC_EISA, FALSE }, /* mb 7770 */
+ { 4, { 0x04, 0x90, 0x77, 0x71 },
+ AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
+ { 4, { 0x04, 0x90, 0x77, 0x56 },
+ AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
+ { 4, { 0x04, 0x90, 0x77, 0x57 },
+ AHC_AIC7770|AHC_VL, TRUE } /* 284x BIOS disabled */
+ };
+
+ /*
+ * The VL-bus cards need to be primed by
+ * writing before a signature check.
+ */
+ for (i = 0; i < sizeof(buf); i++)
+ {
+ outb(0x80 + i, base);
+ buf[i] = inb(base + i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(AIC7xxx); i++)
+ {
+ /*
+ * Signature match on enabled card?
+ */
+ if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
+ {
+ if (inb(base + 4) & 1)
+ {
+ if (AIC7xxx[i].bios_disabled)
+ {
+ *flags |= AHC_USEDEFAULTS;
+ }
+ else
+ {
+ *flags |= AHC_BIOS_ENABLED;
+ }
+ return (i);
+ }
+
+ printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+ "disabled at slot %d, ignored.\n", slot);
+ }
+ }
+
+ return (-1);
+}
+#endif /* (__i386__) || (__alpha__) */
+
+
+/*+F*************************************************************************
+ * Function:
+ * read_2840_seeprom
+ *
+ * Description:
+ * Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ *
+ * See read_seeprom (for the 2940) for the instruction set of the 93C46
+ * chip.
+ *
+ * The 2840 interface to the 93C46 serial EEPROM is through the
+ * STATUS_2840 and SEECTL_2840 registers. The CS_2840, CK_2840, and
+ * DO_2840 bits of the SEECTL_2840 register are connected to the chip
+ * select, clock, and data out lines respectively of the serial EEPROM.
+ * The DI_2840 bit of the STATUS_2840 is connected to the data in line
+ * of the serial EEPROM. The EEPROM_TF bit of STATUS_2840 register is
+ * useful in that it gives us an 800 nsec timer. After a read from the
+ * SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
+ * later.
+ *-F*************************************************************************/
+static int
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
+{
+ int i = 0, k = 0;
+ unsigned char temp;
+ unsigned short checksum = 0;
+ unsigned short *seeprom = (unsigned short *) sc;
+ struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+ };
+ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+ while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0) \
+ { \
+ ; /* Do nothing */ \
+ } \
+ (void) aic_inb(p, SEECTL_2840);
+
+ /*
+ * Read the first 32 registers of the seeprom. For the 2840,
+ * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
+ * but only the first 32 are used by Adaptec BIOS. The loop
+ * will range from 0 to 31.
+ */
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ /*
+ * Send chip select for one clock cycle.
+ */
+ aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
+ CLOCK_PULSE(p);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++)
+ {
+ temp = CS_2840 | seeprom_read.bits[i];
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ temp = temp ^ CK_2840;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+ /*
+ * Send the 6 bit address (MSB first, LSB last).
+ */
+ for (i = 5; i >= 0; i--)
+ {
+ temp = k;
+ temp = (temp >> i) & 1; /* Mask out all but lower bit. */
+ temp = CS_2840 | temp;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ temp = temp ^ CK_2840;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ for (i = 0; i <= 16; i++)
+ {
+ temp = CS_2840;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ temp = temp ^ CK_2840;
+ seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+ /*
+ * The serial EEPROM has a checksum in the last word. Keep a
+ * running checksum for all words read except for the last
+ * word. We'll verify the checksum after all words have been
+ * read.
+ */
+ if (k < (sizeof(*sc) / 2) - 1)
+ {
+ checksum = checksum + seeprom[k];
+ }
+
+ /*
+ * Reset the chip select for the next command cycle.
+ */
+ aic_outb(p, 0, SEECTL_2840);
+ CLOCK_PULSE(p);
+ aic_outb(p, CK_2840, SEECTL_2840);
+ CLOCK_PULSE(p);
+ aic_outb(p, 0, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+
+#if 0
+ printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+ printk("Serial EEPROM:");
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ if (((k % 8) == 0) && (k != 0))
+ {
+ printk("\n ");
+ }
+ printk(" 0x%x", seeprom[k]);
+ }
+ printk("\n");
+#endif
+
+ if (checksum != sc->checksum)
+ {
+ printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+ return (0);
+ }
+
+ return (1);
+#undef CLOCK_PULSE
+}
+
+#define CLOCK_PULSE(p) \
+ do { \
+ int limit = 0; \
+ do { \
+ mb(); \
+ pause_sequencer(p); /* This is just to generate some PCI */ \
+ /* traffic so the PCI read is flushed */ \
+ /* it shouldn't be needed, but some */ \
+ /* chipsets do indeed appear to need */ \
+ /* something to force PCI reads to get */ \
+ /* flushed */ \
+ udelay(1); /* Do nothing */ \
+ } while (((aic_inb(p, SEECTL) & SEERDY) == 0) && (++limit < 1000)); \
+ } while(0)
+
+/*+F*************************************************************************
+ * Function:
+ * acquire_seeprom
+ *
+ * Description:
+ * Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the 7870 chip reset, there
+ * should be no contention.
+ */
+ aic_outb(p, SEEMS, SEECTL);
+ CLOCK_PULSE(p);
+ if ((aic_inb(p, SEECTL) & SEERDY) == 0)
+ {
+ aic_outb(p, 0, SEECTL);
+ return (0);
+ }
+ return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * release_seeprom
+ *
+ * Description:
+ * Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static void
+release_seeprom(struct aic7xxx_host *p)
+{
+ /*
+ * Make sure the SEEPROM is ready before we release it.
+ */
+ CLOCK_PULSE(p);
+ aic_outb(p, 0, SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * read_seeprom
+ *
+ * Description:
+ * Reads the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ *
+ * The instruction set of the 93C46/56/66 chips is as follows:
+ *
+ * Start OP
+ * Function Bit Code Address Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must precede
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ * *Note: The 93C56 and 93C66 have 8 address bits.
+ *
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively. While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
+ * register. After successful arbitration for the memory port, the
+ * SEECS bit of the SEECTL register is connected to the chip select.
+ * The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
+ * and data in lines respectively. The SEERDY bit of SEECTL is useful
+ * in that it gives us an 800 nsec timer. After a write to the SEECTL
+ * register, the SEERDY goes high 800 nsec later. The one exception
+ * to this is when we first request access to the memory port. The
+ * SEERDY goes high to signify that access has been granted and, for
+ * this case, has no implied timing.
+ *-F*************************************************************************/
+static int
+read_seeprom(struct aic7xxx_host *p, int offset,
+ unsigned short *scarray, unsigned int len, seeprom_chip_type chip)
+{
+ int i = 0, k;
+ unsigned char temp;
+ unsigned short checksum = 0;
+ struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+ };
+ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+ /*
+ * Request access of the memory port.
+ */
+ if (acquire_seeprom(p) == 0)
+ {
+ return (0);
+ }
+
+ /*
+ * Read 'len' registers of the seeprom. For the 7870, the 93C46
+ * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+ * the first 32 are used by Adaptec BIOS. Some adapters use the
+ * 93C56 SEEPROM which is a 2048-bit device. The loop will range
+ * from 0 to 'len' - 1.
+ */
+ for (k = 0; k < len; k++)
+ {
+ /*
+ * Send chip select for one clock cycle.
+ */
+ aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
+ CLOCK_PULSE(p);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++)
+ {
+ temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ temp = temp ^ SEECK;
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ }
+ /*
+ * Send the 6 or 8 bit address (MSB first, LSB last).
+ */
+ for (i = ((int) chip - 1); i >= 0; i--)
+ {
+ temp = k + offset;
+ temp = (temp >> i) & 1; /* Mask out all but lower bit. */
+ temp = SEEMS | SEECS | (temp << 1);
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ temp = temp ^ SEECK;
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ for (i = 0; i <= 16; i++)
+ {
+ temp = SEEMS | SEECS;
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ temp = temp ^ SEECK;
+ scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * The serial EEPROM should have a checksum in the last word.
+ * Keep a running checksum for all words read except for the
+ * last word. We'll verify the checksum after all words have
+ * been read.
+ */
+ if (k < (len - 1))
+ {
+ checksum = checksum + scarray[k];
+ }
+
+ /*
+ * Reset the chip select for the next command cycle.
+ */
+ aic_outb(p, SEEMS, SEECTL);
+ CLOCK_PULSE(p);
+ aic_outb(p, SEEMS | SEECK, SEECTL);
+ CLOCK_PULSE(p);
+ aic_outb(p, SEEMS, SEECTL);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * Release access to the memory port and the serial EEPROM.
+ */
+ release_seeprom(p);
+
+#if 0
+ printk("Computed checksum 0x%x, checksum read 0x%x\n",
+ checksum, scarray[len - 1]);
+ printk("Serial EEPROM:");
+ for (k = 0; k < len; k++)
+ {
+ if (((k % 8) == 0) && (k != 0))
+ {
+ printk("\n ");
+ }
+ printk(" 0x%x", scarray[k]);
+ }
+ printk("\n");
+#endif
+ if ( (checksum != scarray[len - 1]) || (checksum == 0) )
+ {
+ return (0);
+ }
+
+ return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * read_brdctl
+ *
+ * Description:
+ * Reads the BRDCTL register.
+ *-F*************************************************************************/
+static unsigned char
+read_brdctl(struct aic7xxx_host *p)
+{
+ unsigned char brdctl, value;
+
+ /*
+ * Make sure the SEEPROM is ready before we access it
+ */
+ CLOCK_PULSE(p);
+ if (p->features & AHC_ULTRA2)
+ {
+ brdctl = BRDRW_ULTRA2;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ value = aic_inb(p, BRDCTL);
+ CLOCK_PULSE(p);
+ return(value);
+ }
+ brdctl = BRDRW;
+ if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
+ (p->flags & AHC_CHNLB) )
+ {
+ brdctl |= BRDCS;
+ }
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ value = aic_inb(p, BRDCTL);
+ CLOCK_PULSE(p);
+ aic_outb(p, 0, BRDCTL);
+ CLOCK_PULSE(p);
+ return (value);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * write_brdctl
+ *
+ * Description:
+ * Writes a value to the BRDCTL register.
+ *-F*************************************************************************/
+static void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
+{
+ unsigned char brdctl;
+
+ /*
+ * Make sure the SEEPROM is ready before we access it
+ */
+ CLOCK_PULSE(p);
+ if (p->features & AHC_ULTRA2)
+ {
+ brdctl = value;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ brdctl |= BRDSTB_ULTRA2;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ brdctl &= ~BRDSTB_ULTRA2;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ read_brdctl(p);
+ CLOCK_PULSE(p);
+ }
+ else
+ {
+ brdctl = BRDSTB;
+ if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
+ (p->flags & AHC_CHNLB) )
+ {
+ brdctl |= BRDCS;
+ }
+ brdctl = BRDSTB | BRDCS;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ brdctl |= value;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ brdctl &= ~BRDSTB;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ brdctl &= ~BRDCS;
+ aic_outb(p, brdctl, BRDCTL);
+ CLOCK_PULSE(p);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic785x_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on aic785x class controller chips
+ *-F*************************************************************************/
+static void
+aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
+ int *ext_present, int *eeprom)
+{
+ unsigned char brdctl;
+
+ aic_outb(p, BRDRW | BRDCS, BRDCTL);
+ CLOCK_PULSE(p);
+ aic_outb(p, 0, BRDCTL);
+ CLOCK_PULSE(p);
+ brdctl = aic_inb(p, BRDCTL);
+ CLOCK_PULSE(p);
+ *int_50 = !(brdctl & BRDDAT5);
+ *ext_present = !(brdctl & BRDDAT6);
+ *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
+}
+
+#undef CLOCK_PULSE
+
+/*+F*************************************************************************
+ * Function:
+ * aic2940_uwpro_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on the 2940-UWPro cards
+ *
+ * NOTE: This function assumes the SEEPROM will have already been acquired
+ * prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic2940_uwpro_wide_cable_detect(struct aic7xxx_host *p, int *int_68,
+ int *ext_68, int *eeprom)
+{
+ unsigned char brdctl;
+
+ /*
+ * First read the status of our cables. Set the rom bank to
+ * 0 since the bank setting serves as a multiplexor for the
+ * cable detection logic. BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(p, 0);
+
+ /*
+ * Now we read the state of the internal 68 connector. BRDDAT6
+ * is don't care, BRDDAT7 is internal 68. The cable is
+ * present if the bit is 0
+ */
+ brdctl = read_brdctl(p);
+ *int_68 = !(brdctl & BRDDAT7);
+
+ /*
+ * Set the bank bit in brdctl and then read the external cable state
+ * and the EEPROM status
+ */
+ write_brdctl(p, BRDDAT5);
+ brdctl = read_brdctl(p);
+
+ *ext_68 = !(brdctl & BRDDAT6);
+ *eeprom = !(brdctl & BRDDAT7);
+
+ /*
+ * We're done, the calling function will release the SEEPROM for us
+ */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic787x_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on aic787x class controller chips
+ *
+ * NOTE: This function assumes the SEEPROM will have already been acquired
+ * prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
+ int *ext_present, int *eeprom)
+{
+ unsigned char brdctl;
+
+ /*
+ * First read the status of our cables. Set the rom bank to
+ * 0 since the bank setting serves as a multiplexor for the
+ * cable detection logic. BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(p, 0);
+
+ /*
+ * Now we read the state of the two internal connectors. BRDDAT6
+ * is internal 50, BRDDAT7 is internal 68. For each, the cable is
+ * present if the bit is 0
+ */
+ brdctl = read_brdctl(p);
+ *int_50 = !(brdctl & BRDDAT6);
+ *int_68 = !(brdctl & BRDDAT7);
+
+ /*
+ * Set the bank bit in brdctl and then read the external cable state
+ * and the EEPROM status
+ */
+ write_brdctl(p, BRDDAT5);
+ brdctl = read_brdctl(p);
+
+ *ext_present = !(brdctl & BRDDAT6);
+ *eeprom = !(brdctl & BRDDAT7);
+
+ /*
+ * We're done, the calling function will release the SEEPROM for us
+ */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic787x_ultra2_term_detect
+ *
+ * Description:
+ * Detect the termination settings present on ultra2 class controllers
+ *
+ * NOTE: This function assumes the SEEPROM will have already been acquired
+ * prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
+ int *enableSE_high, int *enableLVD_low,
+ int *enableLVD_high, int *eprom_present)
+{
+ unsigned char brdctl;
+
+ brdctl = read_brdctl(p);
+
+ *eprom_present = (brdctl & BRDDAT7);
+ *enableSE_high = (brdctl & BRDDAT6);
+ *enableSE_low = (brdctl & BRDDAT5);
+ *enableLVD_high = (brdctl & BRDDAT4);
+ *enableLVD_low = (brdctl & BRDDAT3);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * configure_termination
+ *
+ * Description:
+ * Configures the termination settings on PCI adapters that have
+ * SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p)
+{
+ int internal50_present = 0;
+ int internal68_present = 0;
+ int external_present = 0;
+ int eprom_present = 0;
+ int enableSE_low = 0;
+ int enableSE_high = 0;
+ int enableLVD_low = 0;
+ int enableLVD_high = 0;
+ unsigned char brddat = 0;
+ unsigned char max_target = 0;
+ unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
+
+ if (acquire_seeprom(p))
+ {
+ if (p->features & (AHC_WIDE|AHC_TWIN))
+ max_target = 16;
+ else
+ max_target = 8;
+ aic_outb(p, SEEMS | SEECS, SEECTL);
+ sxfrctl1 &= ~STPWEN;
+ /*
+ * The termination/cable detection logic is split into three distinct
+ * groups. Ultra2 and later controllers, 2940UW-Pro controllers, and
+ * older 7850, 7860, 7870, 7880, and 7895 controllers. Each has its
+ * own unique way of detecting their cables and writing the results
+ * back to the card.
+ */
+ if (p->features & AHC_ULTRA2)
+ {
+ /*
+ * As long as user hasn't overridden term settings, always check the
+ * cable detection logic
+ */
+ if (aic7xxx_override_term == -1)
+ {
+ aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
+ &enableLVD_low, &enableLVD_high,
+ &eprom_present);
+ }
+
+ /*
+ * If the user is overriding settings, then they have been preserved
+ * to here as fake adapter_control entries. Parse them and allow
+ * them to override the detected settings (if we even did detection).
+ */
+ if (!(p->adapter_control & CFSEAUTOTERM))
+ {
+ enableSE_low = (p->adapter_control & CFSTERM);
+ enableSE_high = (p->adapter_control & CFWSTERM);
+ }
+ if (!(p->adapter_control & CFAUTOTERM))
+ {
+ enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
+ }
+
+ /*
+ * Now take those settings that we have and translate them into the
+ * values that must be written into the registers.
+ *
+ * Flash Enable = BRDDAT7
+ * Secondary High Term Enable = BRDDAT6
+ * Secondary Low Term Enable = BRDDAT5
+ * LVD/Primary High Term Enable = BRDDAT4
+ * LVD/Primary Low Term Enable = STPWEN bit in SXFRCTL1
+ */
+ if (enableLVD_low != 0)
+ {
+ sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_LVD;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) LVD/Primary Low byte termination "
+ "Enabled\n", p->host_no);
+ }
+
+ if (enableLVD_high != 0)
+ {
+ brddat |= BRDDAT4;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) LVD/Primary High byte termination "
+ "Enabled\n", p->host_no);
+ }
+
+ if (enableSE_low != 0)
+ {
+ brddat |= BRDDAT5;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Secondary Low byte termination "
+ "Enabled\n", p->host_no);
+ }
+
+ if (enableSE_high != 0)
+ {
+ brddat |= BRDDAT6;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Secondary High byte termination "
+ "Enabled\n", p->host_no);
+ }
+ }
+ else if (p->features & AHC_NEW_AUTOTERM)
+ {
+ /*
+ * The 50 pin connector termination is controlled by STPWEN in the
+ * SXFRCTL1 register. Since the Adaptec docs typically say the
+ * controller is not allowed to be in the middle of a cable and
+ * this is the only connection on that stub of the bus, there is
+ * no need to even check for narrow termination, it's simply
+ * always on.
+ */
+ sxfrctl1 |= STPWEN;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Narrow channel termination Enabled\n",
+ p->host_no);
+
+ if (p->adapter_control & CFAUTOTERM)
+ {
+ aic2940_uwpro_wide_cable_detect(p, &internal68_present,
+ &external_present,
+ &eprom_present);
+ printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+ "Ext-68 %s)\n", p->host_no,
+ "Don't Care",
+ internal68_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
+ eprom_present ? "is" : "is not");
+ if (internal68_present && external_present)
+ {
+ brddat = 0;
+ p->flags &= ~AHC_TERM_ENB_SE_HIGH;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Wide channel termination Disabled\n",
+ p->host_no);
+ }
+ else
+ {
+ brddat = BRDDAT6;
+ p->flags |= AHC_TERM_ENB_SE_HIGH;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
+ p->host_no);
+ }
+ }
+ else
+ {
+ /*
+ * The termination of the Wide channel is done more like normal
+ * though, and the setting of this termination is done by writing
+ * either a 0 or 1 to BRDDAT6 of the BRDDAT register
+ */
+ if (p->adapter_control & CFWSTERM)
+ {
+ brddat = BRDDAT6;
+ p->flags |= AHC_TERM_ENB_SE_HIGH;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
+ p->host_no);
+ }
+ else
+ {
+ brddat = 0;
+ }
+ }
+ }
+ else
+ {
+ if (p->adapter_control & CFAUTOTERM)
+ {
+ if (p->flags & AHC_MOTHERBOARD)
+ {
+ printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
+ p->host_no);
+ printk(KERN_INFO "(scsi%d) Please verify driver detected settings "
+ "are correct.\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) If not, then please properly set the "
+ "device termination\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting "
+ "CTRL-A when prompted\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
+ }
+ /* Configure auto termination. */
+
+ if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
+ {
+ aic787x_cable_detect(p, &internal50_present, &internal68_present,
+ &external_present, &eprom_present);
+ }
+ else
+ {
+ aic785x_cable_detect(p, &internal50_present, &external_present,
+ &eprom_present);
+ }
+
+ if (max_target <= 8)
+ internal68_present = 0;
+
+ if (max_target > 8)
+ {
+ printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+ "Ext-68 %s)\n", p->host_no,
+ internal50_present ? "YES" : "NO",
+ internal68_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
+ }
+ else
+ {
+ printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
+ p->host_no,
+ internal50_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
+ }
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
+ eprom_present ? "is" : "is not");
+
+ /*
+ * Now set the termination based on what we found. BRDDAT6
+ * controls wide termination enable.
+ * Flash Enable = BRDDAT7
+ * SE High Term Enable = BRDDAT6
+ */
+ if (internal50_present && internal68_present && external_present)
+ {
+ printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n",
+ p->host_no);
+ printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
+ "in use at a time!\n", p->host_no);
+ /*
+ * Force termination (low and high byte) on. This is safer than
+ * leaving it completely off, especially since this message comes
+ * most often from motherboard controllers that don't even have 3
+ * connectors, but instead are failing the cable detection.
+ */
+ internal50_present = external_present = 0;
+ enableSE_high = enableSE_low = 1;
+ }
+
+ if ((max_target > 8) &&
+ ((external_present == 0) || (internal68_present == 0)) )
+ {
+ brddat |= BRDDAT6;
+ p->flags |= AHC_TERM_ENB_SE_HIGH;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+ p->host_no);
+ }
+
+ if ( ((internal50_present ? 1 : 0) +
+ (internal68_present ? 1 : 0) +
+ (external_present ? 1 : 0)) <= 1 )
+ {
+ sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_SE_LOW;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+ p->host_no);
+ }
+ }
+ else /* p->adapter_control & CFAUTOTERM */
+ {
+ if (p->adapter_control & CFSTERM)
+ {
+ sxfrctl1 |= STPWEN;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+ p->host_no);
+ }
+
+ if (p->adapter_control & CFWSTERM)
+ {
+ brddat |= BRDDAT6;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+ p->host_no);
+ }
+ }
+ }
+
+ aic_outb(p, sxfrctl1, SXFRCTL1);
+ write_brdctl(p, brddat);
+ release_seeprom(p);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * detect_maxscb
+ *
+ * Description:
+ * Detects the maximum number of SCBs for the controller and returns
+ * the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+ int i;
+
+ /*
+ * It's possible that we've already done this for multichannel
+ * adapters.
+ */
+ if (p->scb_data->maxhscbs == 0)
+ {
+ /*
+ * We haven't initialized the SCB settings yet. Walk the SCBs to
+ * determince how many there are.
+ */
+ aic_outb(p, 0, FREE_SCBH);
+
+ for (i = 0; i < AIC7XXX_MAXSCB; i++)
+ {
+ aic_outb(p, i, SCBPTR);
+ aic_outb(p, i, SCB_CONTROL);
+ if (aic_inb(p, SCB_CONTROL) != i)
+ break;
+ aic_outb(p, 0, SCBPTR);
+ if (aic_inb(p, SCB_CONTROL) != 0)
+ break;
+
+ aic_outb(p, i, SCBPTR);
+ aic_outb(p, 0, SCB_CONTROL); /* Clear the control byte. */
+ aic_outb(p, i + 1, SCB_NEXT); /* Set the next pointer. */
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG); /* Make the tag invalid. */
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS); /* no busy untagged */
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
+ }
+
+ /* Make sure the last SCB terminates the free list. */
+ aic_outb(p, i - 1, SCBPTR);
+ aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+
+ /* Ensure we clear the first (0) SCBs control byte. */
+ aic_outb(p, 0, SCBPTR);
+ aic_outb(p, 0, SCB_CONTROL);
+
+ p->scb_data->maxhscbs = i;
+ /*
+ * Use direct indexing instead for speed
+ */
+ if ( i == AIC7XXX_MAXSCB )
+ p->flags &= ~AHC_PAGESCBS;
+ }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_register
+ *
+ * Description:
+ * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
+ int reset_delay)
+{
+ int i, result;
+ int max_targets;
+ int found = 1;
+ unsigned char term, scsi_conf;
+ struct Scsi_Host *host;
+
+ host = p->host;
+
+ p->scb_data->maxscbs = AIC7XXX_MAXSCB;
+ host->can_queue = AIC7XXX_MAXSCB;
+ host->cmd_per_lun = 3;
+ host->sg_tablesize = AIC7XXX_MAX_SG;
+ host->this_id = p->scsi_id;
+ host->io_port = p->base;
+ host->n_io_port = 0xFF;
+ host->base = p->mbase;
+ host->irq = p->irq;
+ if (p->features & AHC_WIDE)
+ {
+ host->max_id = 16;
+ }
+ if (p->features & AHC_TWIN)
+ {
+ host->max_channel = 1;
+ }
+
+ p->host = host;
+ p->host_no = host->host_no;
+ host->unique_id = p->instance;
+ p->isr_count = 0;
+ p->next = NULL;
+ p->completeq.head = NULL;
+ p->completeq.tail = NULL;
+ scbq_init(&p->scb_data->free_scbs);
+ scbq_init(&p->waiting_scbs);
+ INIT_LIST_HEAD(&p->aic_devs);
+
+ /*
+ * We currently have no commands of any type
+ */
+ p->qinfifonext = 0;
+ p->qoutfifonext = 0;
+
+ printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
+ board_names[p->board_name_index]);
+ switch(p->chip)
+ {
+ case (AHC_AIC7770|AHC_EISA):
+ printk("EISA slot %d\n", p->pci_device_fn);
+ break;
+ case (AHC_AIC7770|AHC_VL):
+ printk("VLB slot %d\n", p->pci_device_fn);
+ break;
+ default:
+ printk("PCI %d/%d/%d\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
+ PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+ if (p->features & AHC_TWIN)
+ {
+ printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+ p->host_no, p->scsi_id, p->scsi_id_b);
+ }
+ else
+ {
+ char *channel;
+
+ channel = "";
+
+ if ((p->flags & AHC_MULTI_CHANNEL) != 0)
+ {
+ channel = " A";
+
+ if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
+ {
+ channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+ }
+ }
+ if (p->features & AHC_WIDE)
+ {
+ printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
+ }
+ else
+ {
+ printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
+ }
+ printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
+ }
+ aic_outb(p, 0, SEQ_FLAGS);
+
+ detect_maxscb(p);
+
+ printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+ p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+ p->base, p->irq);
+ printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at %p\n",
+ p->host_no, p->mbase, p->maddr);
+ }
+
+#ifdef CONFIG_PCI
+ /*
+ * Now that we know our instance number, we can set the flags we need to
+ * force termination if need be.
+ */
+ if (aic7xxx_stpwlev != -1)
+ {
+ /*
+ * This option only applies to PCI controllers.
+ */
+ if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+ {
+ unsigned char devconfig;
+
+ pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
+ if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
+ {
+ devconfig |= STPWLEVEL;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("(scsi%d) Force setting STPWLEVEL bit\n", p->host_no);
+ }
+ else
+ {
+ devconfig &= ~STPWLEVEL;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no);
+ }
+ pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
+ }
+ }
+#endif
+
+ /*
+ * That took care of devconfig and stpwlev, now for the actual termination
+ * settings.
+ */
+ if (aic7xxx_override_term != -1)
+ {
+ /*
+ * Again, this only applies to PCI controllers. We don't have problems
+ * with the termination on 274x controllers to the best of my knowledge.
+ */
+ if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+ {
+ unsigned char term_override;
+
+ term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
+ p->adapter_control &=
+ ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
+ if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
+ {
+ p->adapter_control |= CFLVDSTERM;
+ }
+ if (term_override & 0x02)
+ {
+ p->adapter_control |= CFWSTERM;
+ }
+ if (term_override & 0x01)
+ {
+ p->adapter_control |= CFSTERM;
+ }
+ }
+ }
+
+ if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
+ {
+ if (p->features & AHC_SPIOCAP)
+ {
+ if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
+ /*
+ * Update the settings in sxfrctl1 to match the termination
+ * settings.
+ */
+ configure_termination(p);
+ }
+ else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
+ {
+ configure_termination(p);
+ }
+ }
+
+ /*
+ * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+ */
+ if (p->features & AHC_TWIN)
+ {
+ /* Select channel B */
+ aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+
+ if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
+ term = (aic_inb(p, SXFRCTL1) & STPWEN);
+ else
+ term = ((p->flags & AHC_TERM_ENB_B) ? STPWEN : 0);
+
+ aic_outb(p, p->scsi_id_b, SCSIID);
+ scsi_conf = aic_inb(p, SCSICONF + 1);
+ aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+ aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term |
+ ENSTIMER | ACTNEGEN, SXFRCTL1);
+ aic_outb(p, 0, SIMODE0);
+ aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+ aic_outb(p, 0, SCSIRATE);
+
+ /* Select channel A */
+ aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+ }
+
+ if (p->features & AHC_ULTRA2)
+ {
+ aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
+ }
+ else
+ {
+ aic_outb(p, p->scsi_id, SCSIID);
+ }
+ if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
+ term = (aic_inb(p, SXFRCTL1) & STPWEN);
+ else
+ term = ((p->flags & (AHC_TERM_ENB_A|AHC_TERM_ENB_LVD)) ? STPWEN : 0);
+ scsi_conf = aic_inb(p, SCSICONF);
+ aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+ aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term |
+ ENSTIMER | ACTNEGEN, SXFRCTL1);
+ aic_outb(p, 0, SIMODE0);
+ /*
+ * If we are a cardbus adapter then don't enable SCSI reset detection.
+ * We shouldn't likely be sharing SCSI busses with someone else, and
+ * if we don't have a cable currently plugged into the controller then
+ * we won't have a power source for the SCSI termination, which means
+ * we'll see infinite incoming bus resets.
+ */
+ if(p->flags & AHC_NO_STPWEN)
+ aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1);
+ else
+ aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+ aic_outb(p, 0, SCSIRATE);
+ if ( p->features & AHC_ULTRA2)
+ aic_outb(p, 0, SCSIOFFSET);
+
+ /*
+ * Look at the information that board initialization or the board
+ * BIOS has left us. In the lower four bits of each target's
+ * scratch space any value other than 0 indicates that we should
+ * initiate synchronous transfers. If it's zero, the user or the
+ * BIOS has decided to disable synchronous negotiation to that
+ * target so we don't activate the needsdtr flag.
+ */
+ if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
+ {
+ max_targets = 8;
+ }
+ else
+ {
+ max_targets = 16;
+ }
+
+ if (!(aic7xxx_no_reset))
+ {
+ /*
+ * If we reset the bus, then clear the transfer settings, else leave
+ * them be.
+ */
+ aic_outb(p, 0, ULTRA_ENB);
+ aic_outb(p, 0, ULTRA_ENB + 1);
+ p->ultraenb = 0;
+ }
+
+ /*
+ * Allocate enough hardware scbs to handle the maximum number of
+ * concurrent transactions we can have. We have to make sure that
+ * the allocated memory is contiguous memory. The Linux kmalloc
+ * routine should only allocate contiguous memory, but note that
+ * this could be a problem if kmalloc() is changed.
+ */
+ {
+ size_t array_size;
+ unsigned int hscb_physaddr;
+
+ array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+ if (p->scb_data->hscbs == NULL)
+ {
+ /* pci_alloc_consistent enforces the alignment already and
+ * clears the area as well.
+ */
+ p->scb_data->hscbs = pci_alloc_consistent(p->pdev, array_size,
+ &p->scb_data->hscbs_dma);
+ /* We have to use pci_free_consistent, not kfree */
+ p->scb_data->hscb_kmalloc_ptr = NULL;
+ p->scb_data->hscbs_dma_len = array_size;
+ }
+ if (p->scb_data->hscbs == NULL)
+ {
+ printk("(scsi%d) Unable to allocate hardware SCB array; "
+ "failing detection.\n", p->host_no);
+ aic_outb(p, 0, SIMODE1);
+ p->irq = 0;
+ return(0);
+ }
+
+ hscb_physaddr = p->scb_data->hscbs_dma;
+ aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
+ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
+ aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
+ aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
+
+ /* Set up the fifo areas at the same time */
+ p->untagged_scbs = pci_alloc_consistent(p->pdev, 3*256, &p->fifo_dma);
+ if (p->untagged_scbs == NULL)
+ {
+ printk("(scsi%d) Unable to allocate hardware FIFO arrays; "
+ "failing detection.\n", p->host_no);
+ p->irq = 0;
+ return(0);
+ }
+
+ p->qoutfifo = p->untagged_scbs + 256;
+ p->qinfifo = p->qoutfifo + 256;
+ for (i = 0; i < 256; i++)
+ {
+ p->untagged_scbs[i] = SCB_LIST_NULL;
+ p->qinfifo[i] = SCB_LIST_NULL;
+ p->qoutfifo[i] = SCB_LIST_NULL;
+ }
+
+ hscb_physaddr = p->fifo_dma;
+ aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
+ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
+ aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
+ aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+ }
+
+ /* The Q-FIFOs we just set up are all empty */
+ aic_outb(p, 0, QINPOS);
+ aic_outb(p, 0, KERNEL_QINPOS);
+ aic_outb(p, 0, QOUTPOS);
+
+ if(p->features & AHC_QUEUE_REGS)
+ {
+ aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
+ aic_outb(p, 0, SDSCB_QOFF);
+ aic_outb(p, 0, SNSCB_QOFF);
+ aic_outb(p, 0, HNSCB_QOFF);
+ }
+
+ /*
+ * We don't have any waiting selections or disconnected SCBs.
+ */
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+
+ /*
+ * Message out buffer starts empty
+ */
+ aic_outb(p, MSG_NOOP, MSG_OUT);
+ aic_outb(p, MSG_NOOP, LAST_MSG);
+
+ /*
+ * Set all the other asundry items that haven't been set yet.
+ * This includes just dumping init values to a lot of registers simply
+ * to make sure they've been touched and are ready for use parity wise
+ * speaking.
+ */
+ aic_outb(p, 0, TMODE_CMDADDR);
+ aic_outb(p, 0, TMODE_CMDADDR + 1);
+ aic_outb(p, 0, TMODE_CMDADDR + 2);
+ aic_outb(p, 0, TMODE_CMDADDR + 3);
+ aic_outb(p, 0, TMODE_CMDADDR_NEXT);
+
+ /*
+ * Link us into the list of valid hosts
+ */
+ p->next = first_aic7xxx;
+ first_aic7xxx = p;
+
+ /*
+ * Allocate the first set of scbs for this controller. This is to stream-
+ * line code elsewhere in the driver. If we have to check for the existence
+ * of scbs in certain code sections, it slows things down. However, as
+ * soon as we register the IRQ for this card, we could get an interrupt that
+ * includes possibly the SCSI_RSTI interrupt. If we catch that interrupt
+ * then we are likely to segfault if we don't have at least one chunk of
+ * SCBs allocated or add checks all through the reset code to make sure
+ * that the SCBs have been allocated which is an invalid running condition
+ * and therefore I think it's preferable to simply pre-allocate the first
+ * chunk of SCBs.
+ */
+ aic7xxx_allocate_scb(p);
+
+ /*
+ * Load the sequencer program, then re-enable the board -
+ * resetting the AIC-7770 disables it, leaving the lights
+ * on with nobody home.
+ */
+ aic7xxx_loadseq(p);
+
+ /*
+ * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
+ */
+ aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
+
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ {
+ aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */
+ }
+
+ if ( !(aic7xxx_no_reset) )
+ {
+ if (p->features & AHC_TWIN)
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
+ aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+ aic7xxx_reset_current_bus(p);
+ aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+ }
+ /* Reset SCSI bus A. */
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ { /* In case we are a 3940, 3985, or 7895, print the right channel */
+ char *channel = "";
+ if (p->flags & AHC_MULTI_CHANNEL)
+ {
+ channel = " A";
+ if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+ channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+ }
+ printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+ }
+
+ aic7xxx_reset_current_bus(p);
+
+ }
+ else
+ {
+ if (!reset_delay)
+ {
+ printk(KERN_INFO "(scsi%d) Not resetting SCSI bus. Note: Don't use "
+ "the no_reset\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
+ "for it.\n", p->host_no);
+ }
+ }
+
+ /*
+ * Register IRQ with the kernel. Only allow sharing IRQs with
+ * PCI devices.
+ */
+ if (!(p->chip & AHC_PCI))
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+ }
+ else
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+ "aic7xxx", p));
+ if (result < 0)
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+ "aic7xxx", p));
+ }
+ }
+ if (result < 0)
+ {
+ printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
+ "controller.\n", p->host_no, p->irq);
+ aic_outb(p, 0, SIMODE1);
+ p->irq = 0;
+ return (0);
+ }
+
+ if(aic_inb(p, INTSTAT) & INT_PEND)
+ printk(INFO_LEAD "spurious interrupt during configuration, cleared.\n",
+ p->host_no, -1, -1 , -1);
+ aic7xxx_clear_intstat(p);
+
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_chip_reset
+ *
+ * Description:
+ * Perform a chip reset on the aic7xxx SCSI controller. The controller
+ * is paused upon return.
+ *-F*************************************************************************/
+static int
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+ unsigned char sblkctl;
+ int wait;
+
+ /*
+ * For some 274x boards, we must clear the CHIPRST bit and pause
+ * the sequencer. For some reason, this makes the driver work.
+ */
+ aic_outb(p, PAUSE | CHIPRST, HCNTRL);
+
+ /*
+ * In the future, we may call this function as a last resort for
+ * error handling. Let's be nice and not do any unnecessary delays.
+ */
+ wait = 1000; /* 1 msec (1000 * 1 msec) */
+ while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
+ {
+ udelay(1); /* 1 usec */
+ }
+
+ pause_sequencer(p);
+
+ sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
+ if (p->chip & AHC_PCI)
+ sblkctl &= ~SELBUSB;
+ switch( sblkctl )
+ {
+ case 0: /* normal narrow card */
+ break;
+ case 2: /* Wide card */
+ p->features |= AHC_WIDE;
+ break;
+ case 8: /* Twin card */
+ p->features |= AHC_TWIN;
+ p->flags |= AHC_MULTI_CHANNEL;
+ break;
+ default: /* hmmm...we don't know what this is */
+ printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
+ aic_inb(p, SBLKCTL) & 0x0a);
+ return(-1);
+ }
+ return(0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_alloc
+ *
+ * Description:
+ * Allocate and initialize a host structure. Returns NULL upon error
+ * and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp)
+{
+ struct aic7xxx_host *p = NULL;
+ struct Scsi_Host *host;
+
+ /*
+ * Allocate a storage area by registering us with the mid-level
+ * SCSI layer.
+ */
+ host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+ if (host != NULL)
+ {
+ p = (struct aic7xxx_host *) host->hostdata;
+ memset(p, 0, sizeof(struct aic7xxx_host));
+ *p = *temp;
+ p->host = host;
+
+ p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+ if (p->scb_data != NULL)
+ {
+ memset(p->scb_data, 0, sizeof(scb_data_type));
+ scbq_init (&p->scb_data->free_scbs);
+ }
+ else
+ {
+ /*
+ * For some reason we don't have enough memory. Free the
+ * allocated memory for the aic7xxx_host struct, and return NULL.
+ */
+ release_region(p->base, MAXREG - MINREG);
+ scsi_unregister(host);
+ return(NULL);
+ }
+ p->host_no = host->host_no;
+ }
+ scsi_set_device(host, &p->pdev->dev);
+ return (p);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_free
+ *
+ * Description:
+ * Frees and releases all resources associated with an instance of
+ * the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free(struct aic7xxx_host *p)
+{
+ int i;
+
+ /*
+ * Free the allocated hardware SCB space.
+ */
+ if (p->scb_data != NULL)
+ {
+ struct aic7xxx_scb_dma *scb_dma = NULL;
+ if (p->scb_data->hscbs != NULL)
+ {
+ pci_free_consistent(p->pdev, p->scb_data->hscbs_dma_len,
+ p->scb_data->hscbs, p->scb_data->hscbs_dma);
+ p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
+ }
+ /*
+ * Free the driver SCBs. These were allocated on an as-need
+ * basis. We allocated these in groups depending on how many
+ * we could fit into a given amount of RAM. The tail SCB for
+ * these allocations has a pointer to the alloced area.
+ */
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ if (p->scb_data->scb_array[i]->scb_dma != scb_dma)
+ {
+ scb_dma = p->scb_data->scb_array[i]->scb_dma;
+ pci_free_consistent(p->pdev, scb_dma->dma_len,
+ (void *)((unsigned long)scb_dma->dma_address
+ - scb_dma->dma_offset),
+ scb_dma->dma_address);
+ }
+ if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL)
+ kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
+ p->scb_data->scb_array[i] = NULL;
+ }
+
+ /*
+ * Free the SCB data area.
+ */
+ kfree(p->scb_data);
+ }
+
+ pci_free_consistent(p->pdev, 3*256, (void *)p->untagged_scbs, p->fifo_dma);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_load_seeprom
+ *
+ * Description:
+ * Load the seeprom and configure adapter and target settings.
+ * Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+ int have_seeprom = 0;
+ int i, max_targets, mask;
+ unsigned char scsirate, scsi_conf;
+ unsigned short scarray[128];
+ struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+ }
+ switch (p->chip)
+ {
+ case (AHC_AIC7770|AHC_EISA): /* None of these adapters have seeproms. */
+ if (aic_inb(p, SCSICONF) & TERM_ENB)
+ p->flags |= AHC_TERM_ENB_A;
+ if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
+ p->flags |= AHC_TERM_ENB_B;
+ break;
+
+ case (AHC_AIC7770|AHC_VL):
+ have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+ break;
+
+ default:
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, p->sc_type);
+ if (!have_seeprom)
+ {
+ if(p->sc_type == C46)
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, C56_66);
+ else
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, C46);
+ }
+ if (!have_seeprom)
+ {
+ p->sc_size = 128;
+ have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, p->sc_type);
+ if (!have_seeprom)
+ {
+ if(p->sc_type == C46)
+ have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, C56_66);
+ else
+ have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, C46);
+ }
+ }
+ break;
+ }
+
+ if (!have_seeprom)
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("\naic7xxx: No SEEPROM available.\n");
+ }
+ p->flags |= AHC_NEWEEPROM_FMT;
+ if (aic_inb(p, SCSISEQ) == 0)
+ {
+ p->flags |= AHC_USEDEFAULTS;
+ p->flags &= ~AHC_BIOS_ENABLED;
+ p->scsi_id = p->scsi_id_b = 7;
+ *sxfrctl1 |= STPWEN;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Using default values.\n");
+ }
+ }
+ else if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Using leftover BIOS values.\n");
+ }
+ if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
+ {
+ p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+ sc->adapter_control &= ~CFAUTOTERM;
+ sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
+ }
+ if (aic7xxx_extended)
+ p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+ else
+ p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("done\n");
+ }
+
+ /*
+ * Note things in our flags
+ */
+ p->flags |= AHC_SEEPROM_FOUND;
+
+ /*
+ * Update the settings in sxfrctl1 to match the termination settings.
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * Get our SCSI ID from the SEEPROM setting...
+ */
+ p->scsi_id = (sc->brtime_id & CFSCSIID);
+
+ /*
+ * First process the settings that are different between the VLB
+ * and PCI adapter seeproms.
+ */
+ if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
+ {
+ /* VLB adapter seeproms */
+ if (sc->bios_control & CF284XEXTEND)
+ p->flags |= AHC_EXTEND_TRANS_A;
+
+ if (sc->adapter_control & CF284XSTERM)
+ {
+ *sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+ }
+ }
+ else
+ {
+ /* PCI adapter seeproms */
+ if (sc->bios_control & CFEXTEND)
+ p->flags |= AHC_EXTEND_TRANS_A;
+ if (sc->bios_control & CFBIOSEN)
+ p->flags |= AHC_BIOS_ENABLED;
+ else
+ p->flags &= ~AHC_BIOS_ENABLED;
+
+ if (sc->adapter_control & CFSTERM)
+ {
+ *sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+ }
+ }
+ memcpy(&p->sc, sc, sizeof(struct seeprom_config));
+ }
+
+ p->discenable = 0;
+
+ /*
+ * Limit to 16 targets just in case. The 2842 for one is known to
+ * blow the max_targets setting, future cards might also.
+ */
+ max_targets = ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8);
+
+ if (have_seeprom)
+ {
+ for (i = 0; i < max_targets; i++)
+ {
+ if( ((p->features & AHC_ULTRA) &&
+ !(sc->adapter_control & CFULTRAEN) &&
+ (sc->device_flags[i] & CFSYNCHISULTRA)) ||
+ (sc->device_flags[i] & CFNEWULTRAFORMAT) )
+ {
+ p->flags |= AHC_NEWEEPROM_FMT;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < max_targets; i++)
+ {
+ mask = (0x01 << i);
+ if (!have_seeprom)
+ {
+ if (aic_inb(p, SCSISEQ) != 0)
+ {
+ /*
+ * OK...the BIOS set things up and left behind the settings we need.
+ * Just make our sc->device_flags[i] entry match what the card has
+ * set for this device.
+ */
+ p->discenable =
+ ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
+ p->ultraenb =
+ (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
+ sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
+ if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
+ sc->device_flags[i] |= CFWIDEB;
+ if (p->features & AHC_ULTRA2)
+ {
+ if (aic_inb(p, TARG_OFFSET + i))
+ {
+ sc->device_flags[i] |= CFSYNCH;
+ sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
+ if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
+ sc->device_flags[i] |= CFSYNCHISULTRA;
+ }
+ }
+ else
+ {
+ if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
+ {
+ sc->device_flags[i] |= CFSYNCH;
+ if (p->features & AHC_ULTRA)
+ sc->device_flags[i] |= ((p->ultraenb & mask) ?
+ CFSYNCHISULTRA : 0);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Assume the BIOS has NOT been run on this card and nothing between
+ * the card and the devices is configured yet.
+ */
+ sc->device_flags[i] = CFDISC;
+ if (p->features & AHC_WIDE)
+ sc->device_flags[i] |= CFWIDEB;
+ if (p->features & AHC_ULTRA3)
+ sc->device_flags[i] |= 2;
+ else if (p->features & AHC_ULTRA2)
+ sc->device_flags[i] |= 3;
+ else if (p->features & AHC_ULTRA)
+ sc->device_flags[i] |= CFSYNCHISULTRA;
+ sc->device_flags[i] |= CFSYNCH;
+ aic_outb(p, 0, TARG_SCSIRATE + i);
+ if (p->features & AHC_ULTRA2)
+ aic_outb(p, 0, TARG_OFFSET + i);
+ }
+ }
+ if (sc->device_flags[i] & CFDISC)
+ {
+ p->discenable |= mask;
+ }
+ if (p->flags & AHC_NEWEEPROM_FMT)
+ {
+ if ( !(p->features & AHC_ULTRA2) )
+ {
+ /*
+ * I know of two different Ultra BIOSes that do this differently.
+ * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
+ * be == to 0x03 and SYNCHISULTRA to be true to mean 40MByte/s
+ * while on the IBM Netfinity 5000 they want the same thing
+ * to be something else, while flags[i] & CFXFER == 0x03 and
+ * SYNCHISULTRA false should be 40MByte/s. So, we set both to
+ * 40MByte/s and the lower speeds be damned. People will have
+ * to select around the conversely mapped lower speeds in order
+ * to select lower speeds on these boards.
+ */
+ if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+ ((sc->device_flags[i] & CFXFER) == 0x03) )
+ {
+ sc->device_flags[i] &= ~CFXFER;
+ sc->device_flags[i] |= CFSYNCHISULTRA;
+ }
+ if (sc->device_flags[i] & CFSYNCHISULTRA)
+ {
+ p->ultraenb |= mask;
+ }
+ }
+ else if ( !(sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+ (p->features & AHC_ULTRA2) &&
+ (sc->device_flags[i] & CFSYNCHISULTRA) )
+ {
+ p->ultraenb |= mask;
+ }
+ }
+ else if (sc->adapter_control & CFULTRAEN)
+ {
+ p->ultraenb |= mask;
+ }
+ if ( (sc->device_flags[i] & CFSYNCH) == 0)
+ {
+ sc->device_flags[i] &= ~CFXFER;
+ p->ultraenb &= ~mask;
+ p->user[i].offset = 0;
+ p->user[i].period = 0;
+ p->user[i].options = 0;
+ }
+ else
+ {
+ if (p->features & AHC_ULTRA3)
+ {
+ p->user[i].offset = MAX_OFFSET_ULTRA2;
+ if( (sc->device_flags[i] & CFXFER) < 0x03 )
+ {
+ scsirate = (sc->device_flags[i] & CFXFER);
+ p->user[i].options = MSG_EXT_PPR_OPTION_DT_CRC;
+ }
+ else
+ {
+ scsirate = (sc->device_flags[i] & CFXFER) |
+ ((p->ultraenb & mask) ? 0x18 : 0x10);
+ p->user[i].options = 0;
+ }
+ p->user[i].period = aic7xxx_find_period(p, scsirate,
+ AHC_SYNCRATE_ULTRA3);
+ }
+ else if (p->features & AHC_ULTRA2)
+ {
+ p->user[i].offset = MAX_OFFSET_ULTRA2;
+ scsirate = (sc->device_flags[i] & CFXFER) |
+ ((p->ultraenb & mask) ? 0x18 : 0x10);
+ p->user[i].options = 0;
+ p->user[i].period = aic7xxx_find_period(p, scsirate,
+ AHC_SYNCRATE_ULTRA2);
+ }
+ else
+ {
+ scsirate = (sc->device_flags[i] & CFXFER) << 4;
+ p->user[i].options = 0;
+ p->user[i].offset = MAX_OFFSET_8BIT;
+ if (p->features & AHC_ULTRA)
+ {
+ short ultraenb;
+ ultraenb = aic_inb(p, ULTRA_ENB) |
+ (aic_inb(p, ULTRA_ENB + 1) << 8);
+ p->user[i].period = aic7xxx_find_period(p, scsirate,
+ (p->ultraenb & mask) ?
+ AHC_SYNCRATE_ULTRA :
+ AHC_SYNCRATE_FAST);
+ }
+ else
+ p->user[i].period = aic7xxx_find_period(p, scsirate,
+ AHC_SYNCRATE_FAST);
+ }
+ }
+ if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
+ {
+ p->user[i].width = MSG_EXT_WDTR_BUS_16_BIT;
+ }
+ else
+ {
+ p->user[i].width = MSG_EXT_WDTR_BUS_8_BIT;
+ }
+ }
+ aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+ aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+
+ /*
+ * We set the p->ultraenb from the SEEPROM to begin with, but now we make
+ * it match what is already down in the card. If we are doing a reset
+ * on the card then this will get put back to a default state anyway.
+ * This allows us to not have to pre-emptively negotiate when using the
+ * no_reset option.
+ */
+ if (p->features & AHC_ULTRA)
+ p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
+
+
+ scsi_conf = (p->scsi_id & HSCSIID);
+
+ if(have_seeprom)
+ {
+ p->adapter_control = sc->adapter_control;
+ p->bios_control = sc->bios_control;
+
+ switch (p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7895:
+ case AHC_AIC7896:
+ case AHC_AIC7899:
+ if (p->adapter_control & CFBPRIMARY)
+ p->flags |= AHC_CHANNEL_B_PRIMARY;
+ default:
+ break;
+ }
+
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ }
+ else
+ {
+ scsi_conf |= ENSPCHK | RESET_SCSI;
+ }
+
+ /*
+ * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
+ * The 2842 and 2742 cards already have these registers set and we don't
+ * want to muck with them since we don't set all the bits they do.
+ */
+ if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+ {
+ /* Set the host ID */
+ aic_outb(p, scsi_conf, SCSICONF);
+ /* In case we are a wide card */
+ aic_outb(p, p->scsi_id, SCSICONF + 1);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_configure_bugs
+ *
+ * Description:
+ * Take the card passed in and set the appropriate bug flags based upon
+ * the card model. Also make any changes needed to device registers or
+ * PCI registers while we are here.
+ *-F*************************************************************************/
+static void
+aic7xxx_configure_bugs(struct aic7xxx_host *p)
+{
+ unsigned short tmp_word;
+
+ switch(p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7860:
+ p->bugs |= AHC_BUG_PCI_2_1_RETRY;
+ /* fall through */
+ case AHC_AIC7850:
+ case AHC_AIC7870:
+ p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI;
+ break;
+ case AHC_AIC7880:
+ p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_PCI_2_1_RETRY |
+ AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI;
+ break;
+ case AHC_AIC7890:
+ p->bugs |= AHC_BUG_AUTOFLUSH | AHC_BUG_CACHETHEN;
+ break;
+ case AHC_AIC7892:
+ p->bugs |= AHC_BUG_SCBCHAN_UPLOAD;
+ break;
+ case AHC_AIC7895:
+ p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_PCI_2_1_RETRY |
+ AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI;
+ break;
+ case AHC_AIC7896:
+ p->bugs |= AHC_BUG_CACHETHEN_DIS;
+ break;
+ case AHC_AIC7899:
+ p->bugs |= AHC_BUG_SCBCHAN_UPLOAD;
+ break;
+ default:
+ /* Nothing to do */
+ break;
+ }
+
+ /*
+ * Now handle the bugs that require PCI register or card register tweaks
+ */
+ pci_read_config_word(p->pdev, PCI_COMMAND, &tmp_word);
+ if(p->bugs & AHC_BUG_PCI_MWI)
+ {
+ tmp_word &= ~PCI_COMMAND_INVALIDATE;
+ }
+ else
+ {
+ tmp_word |= PCI_COMMAND_INVALIDATE;
+ }
+ pci_write_config_word(p->pdev, PCI_COMMAND, tmp_word);
+
+ if(p->bugs & AHC_BUG_CACHETHEN)
+ {
+ aic_outb(p, aic_inb(p, DSCOMMAND0) & ~CACHETHEN, DSCOMMAND0);
+ }
+ else if (p->bugs & AHC_BUG_CACHETHEN_DIS)
+ {
+ aic_outb(p, aic_inb(p, DSCOMMAND0) | CACHETHEN, DSCOMMAND0);
+ }
+
+ return;
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_detect
+ *
+ * Description:
+ * Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe(). A sequence of
+ * probe(), attach()/detach(), and init() makes more sense than
+ * one do-it-all function. This may be useful when (and if) the
+ * mid-level SCSI code is overhauled.
+ *-F*************************************************************************/
+static int
+aic7xxx_detect(Scsi_Host_Template *template)
+{
+ struct aic7xxx_host *temp_p = NULL;
+ struct aic7xxx_host *current_p = NULL;
+ struct aic7xxx_host *list_p = NULL;
+ int found = 0;
+#if defined(__i386__) || defined(__alpha__)
+ ahc_flag_type flags = 0;
+ int type;
+#endif
+ unsigned char sxfrctl1;
+#if defined(__i386__) || defined(__alpha__)
+ unsigned char hcntrl, hostconf;
+ unsigned int slot, base;
+#endif
+
+#ifdef MODULE
+ /*
+ * If we are called as a module, the aic7xxx pointer may not be null
+ * and it would point to our bootup string, just like on the lilo
+ * command line. IF not NULL, then process this config string with
+ * aic7xxx_setup
+ */
+ if(aic7xxx)
+ aic7xxx_setup(aic7xxx);
+#endif
+
+ template->proc_name = "aic7xxx";
+ template->sg_tablesize = AIC7XXX_MAX_SG;
+
+
+#ifdef CONFIG_PCI
+ /*
+ * PCI-bus probe.
+ */
+ {
+ static struct
+ {
+ unsigned short vendor_id;
+ unsigned short device_id;
+ ahc_chip chip;
+ ahc_flag_type flags;
+ ahc_feature features;
+ int board_name_index;
+ unsigned short seeprom_size;
+ unsigned short seeprom_type;
+ } const aic_pdevs[] = {
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
+ AHC_FNONE, AHC_FENONE, 1,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
+ AHC_PAGESCBS, AHC_AIC7850_FE, 5,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
+ AHC_PAGESCBS, AHC_AIC7850_FE, 6,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 7,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 7,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 7,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 7,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
+ AHC_AIC7860_FE, 7,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 8,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
+ AHC_AIC7870_FE, 9,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7870_FE, 11,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7870_FE, 12,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
+ AHC_AIC7880_FE, 14,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7880_FE, 16,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7880_FE, 17,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 19,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7895_FE, 20,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7890_FE, 21,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7890_FE, 21,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7890_FE, 22,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7890_FE, 23,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7896_FE, 24,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7896_FE, 25,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7896_FE, 26,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWEN,
+ AHC_AIC7860_FE, 27,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7892_FE, 28,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7892_FE, 28,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7892_FE, 28,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7892_FE, 28,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7899_FE, 29,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7899_FE, 29,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7899_FE, 29,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7899_FE, 29,
+ 32, C56_66 },
+ };
+
+ unsigned short command;
+ unsigned int devconfig, i, oldverbose;
+ struct pci_dev *pdev = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(aic_pdevs); i++)
+ {
+ pdev = NULL;
+ while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
+ aic_pdevs[i].device_id,
+ pdev))) {
+ if (pci_enable_device(pdev))
+ continue;
+ if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
+ {
+ if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
+ {
+ printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
+ "supported by\n");
+ printk(KERN_INFO " this driver, we are ignoring it.\n");
+ }
+ }
+ else if ( (temp_p = kmalloc(sizeof(struct aic7xxx_host),
+ GFP_ATOMIC)) != NULL )
+ {
+ memset(temp_p, 0, sizeof(struct aic7xxx_host));
+ temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
+ temp_p->flags = aic_pdevs[i].flags;
+ temp_p->features = aic_pdevs[i].features;
+ temp_p->board_name_index = aic_pdevs[i].board_name_index;
+ temp_p->sc_size = aic_pdevs[i].seeprom_size;
+ temp_p->sc_type = aic_pdevs[i].seeprom_type;
+
+ /*
+ * Read sundry information from PCI BIOS.
+ */
+ temp_p->irq = pdev->irq;
+ temp_p->pdev = pdev;
+ temp_p->pci_bus = pdev->bus->number;
+ temp_p->pci_device_fn = pdev->devfn;
+ temp_p->base = pci_resource_start(pdev, 0);
+ temp_p->mbase = pci_resource_start(pdev, 1);
+ current_p = list_p;
+ while(current_p && temp_p)
+ {
+ if ( ((current_p->pci_bus == temp_p->pci_bus) &&
+ (current_p->pci_device_fn == temp_p->pci_device_fn)) ||
+ (temp_p->base && (current_p->base == temp_p->base)) ||
+ (temp_p->mbase && (current_p->mbase == temp_p->mbase)) )
+ {
+ /* duplicate PCI entry, skip it */
+ kfree(temp_p);
+ temp_p = NULL;
+ continue;
+ }
+ current_p = current_p->next;
+ }
+ if(pci_request_regions(temp_p->pdev, "aic7xxx"))
+ {
+ printk("aic7xxx: <%s> at PCI %d/%d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ temp_p->pci_bus,
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ printk("aic7xxx: I/O ports already in use, ignoring.\n");
+ kfree(temp_p);
+ continue;
+ }
+
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at PCI %d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
+ (int)command);
+ }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+ command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+ command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
+ command &= ~PCI_COMMAND_INVALIDATE;
+ if (aic7xxx_pci_parity == 0)
+ command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
+ }
+ devconfig |= 0x80000040;
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+
+ temp_p->unpause = INTEN;
+ temp_p->pause = temp_p->unpause | PAUSE;
+ if ( ((temp_p->base == 0) &&
+ (temp_p->mbase == 0)) ||
+ (temp_p->irq == 0) )
+ {
+ printk("aic7xxx: <%s> at PCI %d/%d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ temp_p->pci_bus,
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+ goto skip_pci_controller;
+ }
+
+#ifdef MMAPIO
+ if ( !(temp_p->base) || !(temp_p->flags & AHC_MULTI_CHANNEL) ||
+ ((temp_p->chip != (AHC_AIC7870 | AHC_PCI)) &&
+ (temp_p->chip != (AHC_AIC7880 | AHC_PCI))) )
+ {
+ temp_p->maddr = ioremap_nocache(temp_p->mbase, 256);
+ if(temp_p->maddr)
+ {
+ /*
+ * We need to check the I/O with the MMAPed address. Some machines
+ * simply fail to work with MMAPed I/O and certain controllers.
+ */
+ if(aic_inb(temp_p, HCNTRL) == 0xff)
+ {
+ /*
+ * OK.....we failed our test....go back to programmed I/O
+ */
+ printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ temp_p->pci_bus,
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
+ "Programmed I/O.\n");
+ iounmap(temp_p->maddr);
+ temp_p->maddr = NULL;
+ if(temp_p->base == 0)
+ {
+ printk("aic7xxx: <%s> at PCI %d/%d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ temp_p->pci_bus,
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+ goto skip_pci_controller;
+ }
+ }
+ }
+ }
+#endif
+
+ /*
+ * We HAVE to make sure the first pause_sequencer() and all other
+ * subsequent I/O that isn't PCI config space I/O takes place
+ * after the MMAPed I/O region is configured and tested. The
+ * problem is the PowerPC architecture that doesn't support
+ * programmed I/O at all, so we have to have the MMAP I/O set up
+ * for this pause to even work on those machines.
+ */
+ pause_sequencer(temp_p);
+
+ /*
+ * Clear out any pending PCI error status messages. Also set
+ * verbose to 0 so that we don't emit strange PCI error messages
+ * while cleaning out the current status bits.
+ */
+ oldverbose = aic7xxx_verbose;
+ aic7xxx_verbose = 0;
+ aic7xxx_pci_intr(temp_p);
+ aic7xxx_verbose = oldverbose;
+
+ temp_p->bios_address = 0;
+
+ /*
+ * Remember how the card was setup in case there is no seeprom.
+ */
+ if (temp_p->features & AHC_ULTRA2)
+ temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
+ else
+ temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+ /*
+ * Get current termination setting
+ */
+ sxfrctl1 = aic_inb(temp_p, SXFRCTL1);
+
+ if (aic7xxx_chip_reset(temp_p) == -1)
+ {
+ goto skip_pci_controller;
+ }
+ /*
+ * Very quickly put the term setting back into the register since
+ * the chip reset may cause odd things to happen. This is to keep
+ * LVD busses with lots of drives from draining the power out of
+ * the diffsense line before we get around to running the
+ * configure_termination() function. Also restore the STPWLEVEL
+ * bit of DEVCONFIG
+ */
+ aic_outb(temp_p, sxfrctl1, SXFRCTL1);
+ pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig);
+ sxfrctl1 &= STPWEN;
+
+ /*
+ * We need to set the CHNL? assignments before loading the SEEPROM
+ * The 3940 and 3985 cards (original stuff, not any of the later
+ * stuff) are 7870 and 7880 class chips. The Ultra2 stuff falls
+ * under 7896 and 7897. The 7895 is in a class by itself :)
+ */
+ switch (temp_p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7870: /* 3840 / 3985 */
+ case AHC_AIC7880: /* 3840 UW / 3985 UW */
+ if(temp_p->flags & AHC_MULTI_CHANNEL)
+ {
+ switch(PCI_SLOT(temp_p->pci_device_fn))
+ {
+ case 5:
+ temp_p->flags |= AHC_CHNLB;
+ break;
+ case 8:
+ temp_p->flags |= AHC_CHNLB;
+ break;
+ case 12:
+ temp_p->flags |= AHC_CHNLC;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case AHC_AIC7895: /* 7895 */
+ case AHC_AIC7896: /* 7896/7 */
+ case AHC_AIC7899: /* 7899 */
+ if (PCI_FUNC(pdev->devfn) != 0)
+ {
+ temp_p->flags |= AHC_CHNLB;
+ }
+ /*
+ * The 7895 is the only chipset that sets the SCBSIZE32 param
+ * in the DEVCONFIG register. The Ultra2 chipsets use
+ * the DSCOMMAND0 register instead.
+ */
+ if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+ {
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ devconfig |= SCBSIZE32;
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Loading of the SEEPROM needs to come after we've set the flags
+ * to indicate possible CHNLB and CHNLC assigments. Otherwise,
+ * on 394x and 398x cards we'll end up reading the wrong settings
+ * for channels B and C
+ */
+ switch (temp_p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7892:
+ case AHC_AIC7899:
+ aic_outb(temp_p, 0, SCAMCTL);
+ /*
+ * Switch to the alt mode of the chip...
+ */
+ aic_outb(temp_p, aic_inb(temp_p, SFUNCT) | ALT_MODE, SFUNCT);
+ /*
+ * Set our options...the last two items set our CRC after x byte
+ * count in target mode...
+ */
+ aic_outb(temp_p, AUTO_MSGOUT_DE | DIS_MSGIN_DUALEDGE, OPTIONMODE);
+ aic_outb(temp_p, 0x00, 0x0b);
+ aic_outb(temp_p, 0x10, 0x0a);
+ /*
+ * switch back to normal mode...
+ */
+ aic_outb(temp_p, aic_inb(temp_p, SFUNCT) & ~ALT_MODE, SFUNCT);
+ aic_outb(temp_p, CRCVALCHKEN | CRCENDCHKEN | CRCREQCHKEN |
+ TARGCRCENDEN | TARGCRCCNTEN,
+ CRCCONTROL1);
+ aic_outb(temp_p, ((aic_inb(temp_p, DSCOMMAND0) | USCBSIZE32 |
+ MPARCKEN | CIOPARCKEN | CACHETHEN) &
+ ~DPARCKEN), DSCOMMAND0);
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ case AHC_AIC7890:
+ case AHC_AIC7896:
+ aic_outb(temp_p, 0, SCAMCTL);
+ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | MPARCKEN | USCBSIZE32 |
+ CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ case AHC_AIC7850:
+ case AHC_AIC7860:
+ /*
+ * Set the DSCOMMAND0 register on these cards different from
+ * on the 789x cards. Also, read the SEEPROM as well.
+ */
+ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | MPARCKEN) & ~DPARCKEN,
+ DSCOMMAND0);
+ /* FALLTHROUGH */
+ default:
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ case AHC_AIC7880:
+ /*
+ * Check the rev of the chipset before we change DSCOMMAND0
+ */
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ if ((devconfig & 0xff) >= 1)
+ {
+ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | MPARCKEN) & ~DPARCKEN,
+ DSCOMMAND0);
+ }
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ }
+
+
+ /*
+ * and then we need another switch based on the type in order to
+ * make sure the channel B primary flag is set properly on 7895
+ * controllers....Arrrgggghhh!!! We also have to catch the fact
+ * that when you disable the BIOS on the 7895 on the Intel DK440LX
+ * motherboard, and possibly others, it only sets the BIOS disabled
+ * bit on the A channel...I think I'm starting to lean towards
+ * going postal....
+ */
+ switch(temp_p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7895:
+ case AHC_AIC7896:
+ case AHC_AIC7899:
+ current_p = list_p;
+ while(current_p != NULL)
+ {
+ if ( (current_p->pci_bus == temp_p->pci_bus) &&
+ (PCI_SLOT(current_p->pci_device_fn) ==
+ PCI_SLOT(temp_p->pci_device_fn)) )
+ {
+ if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+ {
+ temp_p->flags |=
+ (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+ temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+ temp_p->flags |=
+ (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+ }
+ else
+ {
+ current_p->flags |=
+ (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+ current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+ current_p->flags |=
+ (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+ }
+ }
+ current_p = current_p->next;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * We only support external SCB RAM on the 7895/6/7 chipsets.
+ * We could support it on the 7890/1 easy enough, but I don't
+ * know of any 7890/1 based cards that have it. I do know
+ * of 7895/6/7 cards that have it and they work properly.
+ */
+ switch(temp_p->chip & AHC_CHIPID_MASK)
+ {
+ default:
+ break;
+ case AHC_AIC7895:
+ case AHC_AIC7896:
+ case AHC_AIC7899:
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ if (temp_p->features & AHC_ULTRA2)
+ {
+ if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) &&
+ (aic7xxx_scbram) )
+ {
+ aic_outb(temp_p,
+ aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
+ DSCOMMAND0);
+ temp_p->flags |= AHC_EXTERNAL_SRAM;
+ devconfig |= EXTSCBPEN;
+ }
+ else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
+ {
+ printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ temp_p->pci_bus,
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ printk("aic7xxx: external SCB RAM detected, "
+ "but not enabled\n");
+ }
+ }
+ else
+ {
+ if ((devconfig & RAMPSM) && (aic7xxx_scbram))
+ {
+ devconfig &= ~SCBRAMSEL;
+ devconfig |= EXTSCBPEN;
+ temp_p->flags |= AHC_EXTERNAL_SRAM;
+ }
+ else if (devconfig & RAMPSM)
+ {
+ printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ temp_p->pci_bus,
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ printk("aic7xxx: external SCB RAM detected, "
+ "but not enabled\n");
+ }
+ }
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+ if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
+ (temp_p->flags & AHC_CHNLB) )
+ aic_outb(temp_p, 1, CCSCBBADDR);
+ break;
+ }
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ aic_outb(temp_p,
+ (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
+ SBLKCTL);
+
+ /*
+ * We don't know where this is set in the SEEPROM or by the
+ * BIOS, so we default to 100%. On Ultra2 controllers, use 75%
+ * instead.
+ */
+ if (temp_p->features & AHC_ULTRA2)
+ {
+ aic_outb(temp_p, RD_DFTHRSH_MAX | WR_DFTHRSH_MAX, DFF_THRSH);
+ }
+ else
+ {
+ aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
+ }
+
+ /*
+ * Call our function to fixup any bugs that exist on this chipset.
+ * This may muck with PCI settings and other device settings, so
+ * make sure it's after all the other PCI and device register
+ * tweaks so it can back out bad settings on specific broken cards.
+ */
+ aic7xxx_configure_bugs(temp_p);
+
+ if ( list_p == NULL )
+ {
+ list_p = current_p = temp_p;
+ }
+ else
+ {
+ current_p = list_p;
+ while(current_p->next != NULL)
+ current_p = current_p->next;
+ current_p->next = temp_p;
+ }
+ temp_p->next = NULL;
+ found++;
+ continue;
+skip_pci_controller:
+#ifdef CONFIG_PCI
+ pci_release_regions(temp_p->pdev);
+#endif
+ kfree(temp_p);
+ } /* Found an Adaptec PCI device. */
+ else /* Well, we found one, but we couldn't get any memory */
+ {
+ printk("aic7xxx: Found <%s>\n",
+ board_names[aic_pdevs[i].board_name_index]);
+ printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
+ "skipping.\n");
+ }
+ } /* while(pdev=....) */
+ } /* for PCI_DEVICES */
+ }
+#endif /* CONFIG_PCI */
+
+#if defined(__i386__) || defined(__alpha__)
+ /*
+ * EISA/VL-bus card signature probe.
+ */
+ slot = MINSLOT;
+ while ( (slot <= MAXSLOT) &&
+ !(aic7xxx_no_probe) )
+ {
+ base = SLOTBASE(slot) + MINREG;
+
+ if (!request_region(base, MAXREG - MINREG, "aic7xxx"))
+ {
+ /*
+ * Some other driver has staked a
+ * claim to this i/o region already.
+ */
+ slot++;
+ continue; /* back to the beginning of the for loop */
+ }
+ flags = 0;
+ type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
+ if (type == -1)
+ {
+ release_region(base, MAXREG - MINREG);
+ slot++;
+ continue;
+ }
+ temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
+ if (temp_p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ release_region(base, MAXREG - MINREG);
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
+
+ /*
+ * Pause the card preserving the IRQ type. Allow the operator
+ * to override the IRQ trigger.
+ */
+ if (aic7xxx_irq_trigger == 1)
+ hcntrl = IRQMS; /* Level */
+ else if (aic7xxx_irq_trigger == 0)
+ hcntrl = 0; /* Edge */
+ else
+ hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
+ memset(temp_p, 0, sizeof(struct aic7xxx_host));
+ temp_p->unpause = hcntrl | INTEN;
+ temp_p->pause = hcntrl | PAUSE | INTEN;
+ temp_p->base = base;
+ temp_p->mbase = 0;
+ temp_p->maddr = NULL;
+ temp_p->pci_bus = 0;
+ temp_p->pci_device_fn = slot;
+ aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
+ while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+ if (aic7xxx_chip_reset(temp_p) == -1)
+ temp_p->irq = 0;
+ else
+ temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+ temp_p->flags |= AHC_PAGESCBS;
+
+ switch (temp_p->irq)
+ {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
+
+ default:
+ printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+ "level %d, ignoring.\n", temp_p->irq);
+ kfree(temp_p);
+ release_region(base, MAXREG - MINREG);
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
+
+ /*
+ * We are commited now, everything has been checked and this card
+ * has been found, now we just set it up
+ */
+
+ /*
+ * Insert our new struct into the list at the end
+ */
+ if (list_p == NULL)
+ {
+ list_p = current_p = temp_p;
+ }
+ else
+ {
+ current_p = list_p;
+ while (current_p->next != NULL)
+ current_p = current_p->next;
+ current_p->next = temp_p;
+ }
+
+ switch (type)
+ {
+ case 0:
+ temp_p->board_name_index = 2;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[2], slot);
+ /* FALLTHROUGH */
+ case 1:
+ {
+ temp_p->chip = AHC_AIC7770 | AHC_EISA;
+ temp_p->features |= AHC_AIC7770_FE;
+ temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
+
+ /*
+ * Get the primary channel information. Right now we don't
+ * do anything with this, but someday we will be able to inform
+ * the mid-level SCSI code which channel is primary.
+ */
+ if (temp_p->board_name_index == 0)
+ {
+ temp_p->board_name_index = 3;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[3], slot);
+ }
+ if (temp_p->bios_control & CHANNEL_B_PRIMARY)
+ {
+ temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
+ }
+
+ if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
+ {
+ temp_p->flags &= ~AHC_BIOS_ENABLED;
+ }
+ else
+ {
+ temp_p->flags &= ~AHC_USEDEFAULTS;
+ temp_p->flags |= AHC_BIOS_ENABLED;
+ if ( (temp_p->bios_control & 0x20) == 0 )
+ {
+ temp_p->bios_address = 0xcc000;
+ temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
+ }
+ else
+ {
+ temp_p->bios_address = 0xd0000;
+ temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
+ }
+ }
+ temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
+ temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
+ if (temp_p->features & AHC_WIDE)
+ {
+ temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
+ temp_p->scsi_id_b = temp_p->scsi_id;
+ }
+ else
+ {
+ temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
+ temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
+ }
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ }
+
+ case 2:
+ case 3:
+ temp_p->chip = AHC_AIC7770 | AHC_VL;
+ temp_p->features |= AHC_AIC7770_FE;
+ if (type == 2)
+ temp_p->flags |= AHC_BIOS_ENABLED;
+ else
+ temp_p->flags &= ~AHC_BIOS_ENABLED;
+ if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
+ sxfrctl1 = STPWEN;
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ temp_p->board_name_index = 4;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at VLB %d\n",
+ board_names[2], slot);
+ switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
+ {
+ case 0x00:
+ temp_p->bios_address = 0xe0000;
+ break;
+ case 0x20:
+ temp_p->bios_address = 0xc8000;
+ break;
+ case 0x40:
+ temp_p->bios_address = 0xd0000;
+ break;
+ case 0x60:
+ temp_p->bios_address = 0xd8000;
+ break;
+ default:
+ break; /* can't get here */
+ }
+ break;
+
+ default: /* Won't get here. */
+ break;
+ }
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
+ (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
+ temp_p->irq,
+ (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
+ }
+
+ /*
+ * All the 7770 based chipsets have this bug
+ */
+ temp_p->bugs |= AHC_BUG_TMODE_WIDEODD;
+
+ /*
+ * Set the FIFO threshold and the bus off time.
+ */
+ hostconf = aic_inb(temp_p, HOSTCONF);
+ aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
+ aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
+ slot++;
+ found++;
+ }
+
+#endif /* defined(__i386__) || defined(__alpha__) */
+
+ /*
+ * Now, we re-order the probed devices by BIOS address and BUS class.
+ * In general, we follow this algorithm to make the adapters show up
+ * in the same order under linux that the computer finds them.
+ * 1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
+ * address, going from lowest to highest.
+ * 2: All PCI controllers with BIOS_ENABLED next, according to BIOS
+ * address, going from lowest to highest.
+ * 3: Remaining VLB/EISA controllers going in slot order.
+ * 4: Remaining PCI controllers, going in PCI device order (reversable)
+ */
+
+ {
+ struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
+ struct aic7xxx_host *vlb, *pci;
+ struct aic7xxx_host *prev_p;
+ struct aic7xxx_host *p;
+ unsigned char left;
+
+ prev_p = vlb = pci = NULL;
+
+ temp_p = list_p;
+ while (temp_p != NULL)
+ {
+ switch(temp_p->chip & ~AHC_CHIPID_MASK)
+ {
+ case AHC_EISA:
+ case AHC_VL:
+ {
+ p = temp_p;
+ if (p->flags & AHC_BIOS_ENABLED)
+ vlb = sort_list[0];
+ else
+ vlb = sort_list[2];
+
+ if (vlb == NULL)
+ {
+ vlb = temp_p;
+ temp_p = temp_p->next;
+ vlb->next = NULL;
+ }
+ else
+ {
+ current_p = vlb;
+ prev_p = NULL;
+ while ( (current_p != NULL) &&
+ (current_p->bios_address < temp_p->bios_address))
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ vlb = temp_p;
+ temp_p = temp_p->next;
+ vlb->next = current_p;
+ }
+ }
+
+ if (p->flags & AHC_BIOS_ENABLED)
+ sort_list[0] = vlb;
+ else
+ sort_list[2] = vlb;
+
+ break;
+ }
+ default: /* All PCI controllers fall through to default */
+ {
+
+ p = temp_p;
+ if (p->flags & AHC_BIOS_ENABLED)
+ pci = sort_list[1];
+ else
+ pci = sort_list[3];
+
+ if (pci == NULL)
+ {
+ pci = temp_p;
+ temp_p = temp_p->next;
+ pci->next = NULL;
+ }
+ else
+ {
+ current_p = pci;
+ prev_p = NULL;
+ if (!aic7xxx_reverse_scan)
+ {
+ while ( (current_p != NULL) &&
+ ( (PCI_SLOT(current_p->pci_device_fn) |
+ (current_p->pci_bus << 8)) <
+ (PCI_SLOT(temp_p->pci_device_fn) |
+ (temp_p->pci_bus << 8)) ) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ else
+ {
+ while ( (current_p != NULL) &&
+ ( (PCI_SLOT(current_p->pci_device_fn) |
+ (current_p->pci_bus << 8)) >
+ (PCI_SLOT(temp_p->pci_device_fn) |
+ (temp_p->pci_bus << 8)) ) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ /*
+ * Are we dealing with a 7895/6/7/9 where we need to sort the
+ * channels as well, if so, the bios_address values should
+ * be the same
+ */
+ if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
+ (temp_p->pci_bus == current_p->pci_bus) &&
+ (PCI_SLOT(temp_p->pci_device_fn) ==
+ PCI_SLOT(current_p->pci_device_fn)) )
+ {
+ if (temp_p->flags & AHC_CHNLB)
+ {
+ if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ else
+ {
+ if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ }
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ pci = temp_p;
+ temp_p = temp_p->next;
+ pci->next = current_p;
+ }
+ }
+
+ if (p->flags & AHC_BIOS_ENABLED)
+ sort_list[1] = pci;
+ else
+ sort_list[3] = pci;
+
+ break;
+ }
+ } /* End of switch(temp_p->type) */
+ } /* End of while (temp_p != NULL) */
+ /*
+ * At this point, the cards have been broken into 4 sorted lists, now
+ * we run through the lists in order and register each controller
+ */
+ {
+ int i;
+
+ left = found;
+ for (i=0; i<ARRAY_SIZE(sort_list); i++)
+ {
+ temp_p = sort_list[i];
+ while(temp_p != NULL)
+ {
+ template->name = board_names[temp_p->board_name_index];
+ p = aic7xxx_alloc(template, temp_p);
+ if (p != NULL)
+ {
+ p->instance = found - left;
+ if (aic7xxx_register(template, p, (--left)) == 0)
+ {
+ found--;
+ aic7xxx_release(p->host);
+ scsi_unregister(p->host);
+ }
+ else if (aic7xxx_dump_card)
+ {
+ pause_sequencer(p);
+ aic7xxx_print_card(p);
+ aic7xxx_print_scratch_ram(p);
+ unpause_sequencer(p, TRUE);
+ }
+ }
+ current_p = temp_p;
+ temp_p = (struct aic7xxx_host *)temp_p->next;
+ kfree(current_p);
+ }
+ }
+ }
+ }
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_buildscb
+ *
+ * Description:
+ * Build a SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
+ struct aic7xxx_scb *scb)
+{
+ unsigned short mask;
+ struct aic7xxx_hwscb *hscb;
+ struct aic_dev_data *aic_dev = cmd->device->hostdata;
+ struct scsi_device *sdptr = cmd->device;
+ unsigned char tindex = TARGET_INDEX(cmd);
+ struct request *req = cmd->request;
+
+ mask = (0x01 << tindex);
+ hscb = scb->hscb;
+
+ /*
+ * Setup the control byte if we need negotiation and have not
+ * already requested it.
+ */
+ hscb->control = 0;
+ scb->tag_action = 0;
+
+ if (p->discenable & mask)
+ {
+ hscb->control |= DISCENB;
+ /* We always force TEST_UNIT_READY to untagged */
+ if (cmd->cmnd[0] != TEST_UNIT_READY && sdptr->simple_tags)
+ {
+ if (req->flags & REQ_HARDBARRIER)
+ {
+ if(sdptr->ordered_tags)
+ {
+ hscb->control |= MSG_ORDERED_Q_TAG;
+ scb->tag_action = MSG_ORDERED_Q_TAG;
+ }
+ }
+ else
+ {
+ hscb->control |= MSG_SIMPLE_Q_TAG;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ }
+ }
+ }
+ if ( !(aic_dev->dtr_pending) &&
+ (aic_dev->needppr || aic_dev->needwdtr || aic_dev->needsdtr) &&
+ (aic_dev->flags & DEVICE_DTR_SCANNED) )
+ {
+ aic_dev->dtr_pending = 1;
+ scb->tag_action = 0;
+ hscb->control &= DISCENB;
+ hscb->control |= MK_MESSAGE;
+ if(aic_dev->needppr)
+ {
+ scb->flags |= SCB_MSGOUT_PPR;
+ }
+ else if(aic_dev->needwdtr)
+ {
+ scb->flags |= SCB_MSGOUT_WDTR;
+ }
+ else if(aic_dev->needsdtr)
+ {
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ scb->flags |= SCB_DTR_SCB;
+ }
+ hscb->target_channel_lun = ((cmd->device->id << 4) & 0xF0) |
+ ((cmd->device->channel & 0x01) << 3) | (cmd->device->lun & 0x07);
+
+ /*
+ * The interpretation of request_buffer and request_bufflen
+ * changes depending on whether or not use_sg is zero; a
+ * non-zero use_sg indicates the number of elements in the
+ * scatter-gather array.
+ */
+
+ /*
+ * XXX - this relies on the host data being stored in a
+ * little-endian format.
+ */
+ hscb->SCSI_cmd_length = cmd->cmd_len;
+ memcpy(scb->cmnd, cmd->cmnd, cmd->cmd_len);
+ hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->cmnd));
+
+ if (cmd->use_sg)
+ {
+ struct scatterlist *sg; /* Must be mid-level SCSI code scatterlist */
+
+ /*
+ * We must build an SG list in adapter format, as the kernel's SG list
+ * cannot be used directly because of data field size (__alpha__)
+ * differences and the kernel SG list uses virtual addresses where
+ * we need physical addresses.
+ */
+ int i, use_sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ scb->sg_length = 0;
+ use_sg = pci_map_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ /*
+ * Copy the segments into the SG array. NOTE!!! - We used to
+ * have the first entry both in the data_pointer area and the first
+ * SG element. That has changed somewhat. We still have the first
+ * entry in both places, but now we download the address of
+ * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
+ */
+ for (i = 0; i < use_sg; i++)
+ {
+ unsigned int len = sg_dma_len(sg+i);
+ scb->sg_list[i].address = cpu_to_le32(sg_dma_address(sg+i));
+ scb->sg_list[i].length = cpu_to_le32(len);
+ scb->sg_length += len;
+ }
+ /* Copy the first SG into the data pointer area. */
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->data_count = scb->sg_list[0].length;
+ scb->sg_count = i;
+ hscb->SG_segment_count = i;
+ hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[1]));
+ }
+ else
+ {
+ if (cmd->request_bufflen)
+ {
+ unsigned int address = pci_map_single(p->pdev, cmd->request_buffer,
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ aic7xxx_mapping(cmd) = address;
+ scb->sg_list[0].address = cpu_to_le32(address);
+ scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen);
+ scb->sg_count = 1;
+ scb->sg_length = cmd->request_bufflen;
+ hscb->SG_segment_count = 1;
+ hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[0]));
+ hscb->data_count = scb->sg_list[0].length;
+ hscb->data_pointer = scb->sg_list[0].address;
+ }
+ else
+ {
+ scb->sg_count = 0;
+ scb->sg_length = 0;
+ hscb->SG_segment_count = 0;
+ hscb->SG_list_pointer = 0;
+ hscb->data_count = 0;
+ hscb->data_pointer = 0;
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_queue
+ *
+ * Description:
+ * Queue a SCB to the controller.
+ *-F*************************************************************************/
+static int
+aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+ struct aic7xxx_host *p;
+ struct aic7xxx_scb *scb;
+ struct aic_dev_data *aic_dev;
+
+ p = (struct aic7xxx_host *) cmd->device->host->hostdata;
+
+ aic_dev = cmd->device->hostdata;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic_dev->active_cmds > aic_dev->max_q_depth)
+ {
+ printk(WARN_LEAD "Commands queued exceeds queue "
+ "depth, active=%d\n",
+ p->host_no, CTL_OF_CMD(cmd),
+ aic_dev->active_cmds);
+ }
+#endif
+
+ scb = scbq_remove_head(&p->scb_data->free_scbs);
+ if (scb == NULL)
+ {
+ aic7xxx_allocate_scb(p);
+ scb = scbq_remove_head(&p->scb_data->free_scbs);
+ if(scb == NULL)
+ {
+ printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
+ CTL_OF_CMD(cmd));
+ return 1;
+ }
+ }
+ scb->cmd = cmd;
+
+ /*
+ * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
+ * is set up properly, and the parity error flag is reset, then send
+ * the SCB to the sequencer and watch the fun begin.
+ */
+ aic7xxx_position(cmd) = scb->hscb->tag;
+ cmd->scsi_done = fn;
+ cmd->result = DID_OK;
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ aic7xxx_error(cmd) = DID_OK;
+ aic7xxx_status(cmd) = 0;
+ cmd->host_scribble = NULL;
+
+ /*
+ * Construct the SCB beforehand, so the sequencer is
+ * paused a minimal amount of time.
+ */
+ aic7xxx_buildscb(p, cmd, scb);
+
+ scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
+
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ aic7xxx_run_waiting_queues(p);
+ return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_bus_device_reset
+ *
+ * Description:
+ * Abort or reset the current SCSI command(s). If the scb has not
+ * previously been aborted, then we attempt to send a BUS_DEVICE_RESET
+ * message to the target. If the scb has previously been unsuccessfully
+ * aborted, then we will reset the channel and have all devices renegotiate.
+ * Returns an enumerated type that indicates the status of the operation.
+ *-F*************************************************************************/
+static int
+aic7xxx_bus_device_reset(Scsi_Cmnd *cmd)
+{
+ struct aic7xxx_host *p;
+ struct aic7xxx_scb *scb;
+ struct aic7xxx_hwscb *hscb;
+ int channel;
+ unsigned char saved_scbptr, lastphase;
+ unsigned char hscb_index;
+ int disconnected;
+ struct aic_dev_data *aic_dev;
+
+ if(cmd == NULL)
+ {
+ printk(KERN_ERR "aic7xxx_bus_device_reset: called with NULL cmd!\n");
+ return FAILED;
+ }
+ p = (struct aic7xxx_host *)cmd->device->host->hostdata;
+ aic_dev = AIC_DEV(cmd);
+ if(aic7xxx_position(cmd) < p->scb_data->numscbs)
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ else
+ return FAILED;
+
+ hscb = scb->hscb;
+
+ aic7xxx_isr(p->irq, (void *)p, NULL);
+ aic7xxx_done_cmds_complete(p);
+ /* If the command was already complete or just completed, then we didn't
+ * do a reset, return FAILED */
+ if(!(scb->flags & SCB_ACTIVE))
+ return FAILED;
+
+ pause_sequencer(p);
+ lastphase = aic_inb(p, LASTPHASE);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ {
+ printk(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
+ p->host_no, CTL_OF_SCB(scb), scb->flags);
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ printk("Data-Out phase\n");
+ break;
+ case P_DATAIN:
+ printk("Data-In phase\n");
+ break;
+ case P_COMMAND:
+ printk("Command phase\n");
+ break;
+ case P_MESGOUT:
+ printk("Message-Out phase\n");
+ break;
+ case P_STATUS:
+ printk("Status phase\n");
+ break;
+ case P_MESGIN:
+ printk("Message-In phase\n");
+ break;
+ default:
+ /*
+ * We're not in a valid phase, so assume we're idle.
+ */
+ printk("while idle, LASTPHASE = 0x%x\n", lastphase);
+ break;
+ }
+ printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+ "0x%x\n", p->host_no, CTL_OF_SCB(scb),
+ aic_inb(p, SCSISIGI),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+ printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n", p->host_no,
+ CTL_OF_SCB(scb),
+ (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0,
+ aic_inb(p, SSTAT2),
+ aic_inb(p, STCNT + 2) << 16 | aic_inb(p, STCNT + 1) << 8 |
+ aic_inb(p, STCNT));
+ }
+
+ channel = cmd->device->channel;
+
+ /*
+ * Send a Device Reset Message:
+ * The target that is holding up the bus may not be the same as
+ * the one that triggered this timeout (different commands have
+ * different timeout lengths). Our strategy here is to queue an
+ * abort message to the timed out target if it is disconnected.
+ * Otherwise, if we have an active target we stuff the message buffer
+ * with an abort message and assert ATN in the hopes that the target
+ * will let go of the bus and go to the mesgout phase. If this
+ * fails, we'll get another timeout a few seconds later which will
+ * attempt a bus reset.
+ */
+ saved_scbptr = aic_inb(p, SCBPTR);
+ disconnected = FALSE;
+
+ if (lastphase != P_BUSFREE)
+ {
+ if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Invalid SCB ID %d is active, "
+ "SCB flags = 0x%x.\n", p->host_no,
+ CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
+ unpause_sequencer(p, FALSE);
+ return FAILED;
+ }
+ if (scb->hscb->tag == aic_inb(p, SCB_TAG))
+ {
+ if ( (lastphase == P_MESGOUT) || (lastphase == P_MESGIN) )
+ {
+ printk(WARN_LEAD "Device reset, Message buffer "
+ "in use\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ return FAILED;
+ }
+
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Device reset message in "
+ "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ aic7xxx_error(cmd) = DID_RESET;
+ aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
+ /* Send the abort message to the active SCB. */
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, lastphase | ATNO, SCSISIGO);
+ unpause_sequencer(p, FALSE);
+ spin_unlock_irq(p->host->host_lock);
+ ssleep(1);
+ spin_lock_irq(p->host->host_lock);
+ if(aic_dev->flags & BUS_DEVICE_RESET_PENDING)
+ return FAILED;
+ else
+ return SUCCESS;
+ }
+ } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
+ /*
+ * Simply set the MK_MESSAGE flag and the SEQINT handler will do
+ * the rest on a reconnect/connect.
+ */
+ scb->hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ aic_dev->flags |= BUS_DEVICE_RESET_PENDING;
+ /*
+ * Check to see if the command is on the qinfifo. If it is, then we will
+ * not need to queue the command again since the card should start it soon
+ */
+ if (aic7xxx_search_qinfifo(p, cmd->device->channel, cmd->device->id, cmd->device->lun, hscb->tag,
+ 0, TRUE, NULL) == 0)
+ {
+ disconnected = TRUE;
+ if ((hscb_index = aic7xxx_find_scb(p, scb)) != SCB_LIST_NULL)
+ {
+ unsigned char scb_control;
+
+ aic_outb(p, hscb_index, SCBPTR);
+ scb_control = aic_inb(p, SCB_CONTROL);
+ /*
+ * If the DISCONNECTED bit is not set in SCB_CONTROL, then we are
+ * actually on the waiting list, not disconnected, and we don't
+ * need to requeue the command.
+ */
+ disconnected = (scb_control & DISCONNECTED);
+ aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
+ }
+ if (disconnected)
+ {
+ /*
+ * Actually requeue this SCB in case we can select the
+ * device before it reconnects. This can result in the command
+ * being on the qinfifo twice, but we don't care because it will
+ * all get cleaned up if/when the reset takes place.
+ */
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Queueing device reset command.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ scb->flags |= SCB_QUEUED_ABORT;
+ }
+ }
+ aic_outb(p, saved_scbptr, SCBPTR);
+ unpause_sequencer(p, FALSE);
+ spin_unlock_irq(p->host->host_lock);
+ msleep(1000/4);
+ spin_lock_irq(p->host->host_lock);
+ if(aic_dev->flags & BUS_DEVICE_RESET_PENDING)
+ return FAILED;
+ else
+ return SUCCESS;
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_panic_abort
+ *
+ * Description:
+ * Abort the current SCSI command(s).
+ *-F*************************************************************************/
+static void
+aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+
+ printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION);
+ printk("Controller type:\n %s\n", board_names[p->board_name_index]);
+ printk("p->flags=0x%lx, p->chip=0x%x, p->features=0x%x, "
+ "sequencer %s paused\n",
+ p->flags, p->chip, p->features,
+ (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+ pause_sequencer(p);
+ disable_irq(p->irq);
+ aic7xxx_print_card(p);
+ aic7xxx_print_scratch_ram(p);
+ spin_unlock_irq(p->host->host_lock);
+ for(;;) barrier();
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort
+ *
+ * Description:
+ * Abort the current SCSI command(s).
+ *-F*************************************************************************/
+static int
+aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+ struct aic7xxx_scb *scb = NULL;
+ struct aic7xxx_host *p;
+ int found=0, disconnected;
+ unsigned char saved_hscbptr, hscbptr, scb_control;
+ struct aic_dev_data *aic_dev;
+
+ if(cmd == NULL)
+ {
+ printk(KERN_ERR "aic7xxx_abort: called with NULL cmd!\n");
+ return FAILED;
+ }
+ p = (struct aic7xxx_host *)cmd->device->host->hostdata;
+ aic_dev = AIC_DEV(cmd);
+ if(aic7xxx_position(cmd) < p->scb_data->numscbs)
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ else
+ return FAILED;
+
+ aic7xxx_isr(p->irq, (void *)p, NULL);
+ aic7xxx_done_cmds_complete(p);
+ /* If the command was already complete or just completed, then we didn't
+ * do a reset, return FAILED */
+ if(!(scb->flags & SCB_ACTIVE))
+ return FAILED;
+
+ pause_sequencer(p);
+
+ /*
+ * I added a new config option to the driver: "panic_on_abort" that will
+ * cause the driver to panic and the machine to stop on the first abort
+ * or reset call into the driver. At that point, it prints out a lot of
+ * useful information for me which I can then use to try and debug the
+ * problem. Simply enable the boot time prompt in order to activate this
+ * code.
+ */
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, cmd);
+
+ if (aic7xxx_verbose & VERBOSE_ABORT)
+ {
+ printk(INFO_LEAD "Aborting scb %d, flags 0x%x, SEQADDR 0x%x, LASTPHASE "
+ "0x%x\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags,
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, LASTPHASE));
+ printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n",
+ p->host_no, CTL_OF_SCB(scb), (p->features & AHC_ULTRA2) ?
+ aic_inb(p, SG_CACHEPTR) : 0, aic_inb(p, SG_COUNT),
+ aic_inb(p, SCSISIGI));
+ printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%x\n",
+ p->host_no, CTL_OF_SCB(scb), aic_inb(p, SSTAT0),
+ aic_inb(p, SSTAT1), aic_inb(p, SSTAT2));
+ }
+
+ if (scb->flags & SCB_WAITINGQ)
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB found on waiting list and "
+ "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ scbq_remove(&p->waiting_scbs, scb);
+ scbq_remove(&aic_dev->delayed_scbs, scb);
+ aic_dev->active_cmds++;
+ p->activescbs++;
+ scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+ scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ goto success;
+ }
+
+/*
+ * We just checked the waiting_q, now for the QINFIFO
+ */
+ if ( ((found = aic7xxx_search_qinfifo(p, cmd->device->id, cmd->device->channel,
+ cmd->device->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+ FALSE, NULL)) != 0) &&
+ (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
+ {
+ printk(INFO_LEAD "SCB found in QINFIFO and aborted.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ goto success;
+ }
+
+/*
+ * QINFIFO, waitingq, completeq done. Next, check WAITING_SCB list in card
+ */
+
+ saved_hscbptr = aic_inb(p, SCBPTR);
+ if ((hscbptr = aic7xxx_find_scb(p, scb)) != SCB_LIST_NULL)
+ {
+ aic_outb(p, hscbptr, SCBPTR);
+ scb_control = aic_inb(p, SCB_CONTROL);
+ disconnected = scb_control & DISCONNECTED;
+ /*
+ * If the DISCONNECTED bit is not set in SCB_CONTROL, then we are
+ * either currently active or on the waiting list.
+ */
+ if(!disconnected && aic_inb(p, LASTPHASE) == P_BUSFREE) {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB found on hardware waiting"
+ " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ /* If we are the only waiting command, stop the selection engine */
+ if (aic_inb(p, WAITING_SCBH) == hscbptr && aic_inb(p, SCB_NEXT) ==
+ SCB_LIST_NULL)
+ {
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ aic_outb(p, CLRSELTIMEO, CLRSINT1);
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ }
+ else
+ {
+ unsigned char prev, next;
+ prev = SCB_LIST_NULL;
+ next = aic_inb(p, WAITING_SCBH);
+ while(next != SCB_LIST_NULL)
+ {
+ aic_outb(p, next, SCBPTR);
+ if (next == hscbptr)
+ {
+ next = aic_inb(p, SCB_NEXT);
+ if (prev != SCB_LIST_NULL)
+ {
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
+ }
+ else
+ aic_outb(p, next, WAITING_SCBH);
+ aic_outb(p, hscbptr, SCBPTR);
+ next = SCB_LIST_NULL;
+ }
+ else
+ {
+ prev = next;
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ }
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic7xxx_add_curscb_to_free_list(p);
+ scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ goto success;
+ }
+ else if (!disconnected)
+ {
+ /*
+ * We are the currently active command
+ */
+ if((aic_inb(p, LASTPHASE) == P_MESGIN) ||
+ (aic_inb(p, LASTPHASE) == P_MESGOUT))
+ {
+ /*
+ * Message buffer busy, unable to abort
+ */
+ printk(INFO_LEAD "message buffer busy, unable to abort.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ return FAILED;
+ }
+ /* Fallthrough to below, set ATNO after we set SCB_CONTROL */
+ }
+ aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
+ if(!disconnected)
+ {
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ aic_outb(p, saved_hscbptr, SCBPTR);
+ }
+ else
+ {
+ /*
+ * The scb isn't in the card at all and it is active and it isn't in
+ * any of the queues, so it must be disconnected and paged out. Fall
+ * through to the code below.
+ */
+ disconnected = 1;
+ }
+
+ p->flags |= AHC_ABORT_PENDING;
+ scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+ scb->hscb->control |= MK_MESSAGE;
+ if(disconnected)
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB disconnected. Queueing Abort"
+ " SCB.\n", p->host_no, CTL_OF_SCB(scb));
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ }
+ unpause_sequencer(p, FALSE);
+ spin_unlock_irq(p->host->host_lock);
+ msleep(1000/4);
+ spin_lock_irq(p->host->host_lock);
+ if (p->flags & AHC_ABORT_PENDING)
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
+ printk(INFO_LEAD "Abort never delivered, returning FAILED\n", p->host_no,
+ CTL_OF_CMD(cmd));
+ p->flags &= ~AHC_ABORT_PENDING;
+ return FAILED;
+ }
+ if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
+ printk(INFO_LEAD "Abort successful.\n", p->host_no, CTL_OF_CMD(cmd));
+ return SUCCESS;
+
+success:
+ if (aic7xxx_verbose & VERBOSE_ABORT_RETURN)
+ printk(INFO_LEAD "Abort successful.\n", p->host_no, CTL_OF_CMD(cmd));
+ aic7xxx_run_done_queue(p, TRUE);
+ unpause_sequencer(p, FALSE);
+ return SUCCESS;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset
+ *
+ * Description:
+ * Resetting the bus always succeeds - is has to, otherwise the
+ * kernel will panic! Try a surgical technique - sending a BUS
+ * DEVICE RESET message - on the offending target before pulling
+ * the SCSI bus reset line.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset(Scsi_Cmnd *cmd)
+{
+ struct aic7xxx_scb *scb;
+ struct aic7xxx_host *p;
+ struct aic_dev_data *aic_dev;
+
+ p = (struct aic7xxx_host *) cmd->device->host->hostdata;
+ aic_dev = AIC_DEV(cmd);
+ if(aic7xxx_position(cmd) < p->scb_data->numscbs)
+ {
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ if (scb->cmd != cmd)
+ scb = NULL;
+ }
+ else
+ {
+ scb = NULL;
+ }
+
+ /*
+ * I added a new config option to the driver: "panic_on_abort" that will
+ * cause the driver to panic and the machine to stop on the first abort
+ * or reset call into the driver. At that point, it prints out a lot of
+ * useful information for me which I can then use to try and debug the
+ * problem. Simply enable the boot time prompt in order to activate this
+ * code.
+ */
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, cmd);
+
+ pause_sequencer(p);
+
+ while((aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
+ {
+ aic7xxx_isr(p->irq, p, (void *)NULL );
+ pause_sequencer(p);
+ }
+ aic7xxx_done_cmds_complete(p);
+
+ if(scb && (scb->cmd == NULL))
+ {
+ /*
+ * We just completed the command when we ran the isr stuff, so we no
+ * longer have it.
+ */
+ unpause_sequencer(p, FALSE);
+ return SUCCESS;
+ }
+
+/*
+ * By this point, we want to already know what we are going to do and
+ * only have the following code implement our course of action.
+ */
+ aic7xxx_reset_channel(p, cmd->device->channel, TRUE);
+ if (p->features & AHC_TWIN)
+ {
+ aic7xxx_reset_channel(p, cmd->device->channel ^ 0x01, TRUE);
+ restart_sequencer(p);
+ }
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
+ aic7xxx_clear_intstat(p);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_index = 0;
+ p->msg_len = 0;
+ aic7xxx_run_done_queue(p, TRUE);
+ unpause_sequencer(p, FALSE);
+ spin_unlock_irq(p->host->host_lock);
+ ssleep(2);
+ spin_lock_irq(p->host->host_lock);
+ return SUCCESS;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_biosparam
+ *
+ * Description:
+ * Return the disk geometry for the given SCSI device.
+ *
+ * Note:
+ * This function is broken for today's really large drives and needs
+ * fixed.
+ *-F*************************************************************************/
+static int
+aic7xxx_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ sector_t heads, sectors, cylinders;
+ int ret;
+ struct aic7xxx_host *p;
+ unsigned char *buf;
+
+ p = (struct aic7xxx_host *) sdev->host->hostdata;
+ buf = scsi_bios_ptable(bdev);
+
+ if ( buf )
+ {
+ ret = scsi_partsize(buf, capacity, &geom[2], &geom[0], &geom[1]);
+ kfree(buf);
+ if ( ret != -1 )
+ return(ret);
+ }
+
+ heads = 64;
+ sectors = 32;
+ cylinders = capacity >> 11;
+
+ if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = capacity >> 14;
+ if(capacity > (65535 * heads * sectors))
+ cylinders = 65535;
+ else
+ cylinders = ((unsigned int)capacity) / (unsigned int)(heads * sectors);
+ }
+
+ geom[0] = (int)heads;
+ geom[1] = (int)sectors;
+ geom[2] = (int)cylinders;
+
+ return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_release
+ *
+ * Description:
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ *-F*************************************************************************/
+static int
+aic7xxx_release(struct Scsi_Host *host)
+{
+ struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+ struct aic7xxx_host *next, *prev;
+
+ if(p->irq)
+ free_irq(p->irq, p);
+#ifdef MMAPIO
+ if(p->maddr)
+ {
+ iounmap(p->maddr);
+ }
+#endif /* MMAPIO */
+ if(!p->pdev)
+ release_region(p->base, MAXREG - MINREG);
+#ifdef CONFIG_PCI
+ else
+ pci_release_regions(p->pdev);
+#endif
+ prev = NULL;
+ next = first_aic7xxx;
+ while(next != NULL)
+ {
+ if(next == p)
+ {
+ if(prev == NULL)
+ first_aic7xxx = next->next;
+ else
+ prev->next = next->next;
+ }
+ else
+ {
+ prev = next;
+ }
+ next = next->next;
+ }
+ aic7xxx_free(p);
+ return(0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_card
+ *
+ * Description:
+ * Print out all of the control registers on the card
+ *
+ * NOTE: This function is not yet safe for use on the VLB and EISA
+ * controllers, so it isn't used on those controllers at all.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_card(struct aic7xxx_host *p)
+{
+ int i, j, k, chip;
+ static struct register_ranges {
+ int num_ranges;
+ int range_val[32];
+ } cards_ds[] = {
+ { 0, {0,} }, /* none */
+ {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
+ { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
+ 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
+ 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
+ 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+ 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+ 0xfe, 0xff} },
+ {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
+ 0x9f, 0x9f, 0xe0, 0xf1} },
+ {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
+ 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+ 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+ 0xfe, 0xff} },
+ {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7892*/
+ 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+ 0xe0, 0xf1, 0xf4, 0xfc} },
+ {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7899*/
+ 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+ 0xe0, 0xf1, 0xf4, 0xfc} },
+ };
+ chip = p->chip & AHC_CHIPID_MASK;
+ printk("%s at ",
+ board_names[p->board_name_index]);
+ switch(p->chip & ~AHC_CHIPID_MASK)
+ {
+ case AHC_VL:
+ printk("VLB Slot %d.\n", p->pci_device_fn);
+ break;
+ case AHC_EISA:
+ printk("EISA Slot %d.\n", p->pci_device_fn);
+ break;
+ case AHC_PCI:
+ default:
+ printk("PCI %d/%d/%d.\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
+ PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+
+ /*
+ * the registers on the card....
+ */
+ printk("Card Dump:\n");
+ k = 0;
+ for(i=0; i<cards_ds[chip].num_ranges; i++)
+ {
+ for(j = cards_ds[chip].range_val[ i * 2 ];
+ j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
+ j++)
+ {
+ printk("%02x:%02x ", j, aic_inb(p, j));
+ if(++k == 13)
+ {
+ printk("\n");
+ k=0;
+ }
+ }
+ }
+ if(k != 0)
+ printk("\n");
+
+ /*
+ * If this was an Ultra2 controller, then we just hosed the card in terms
+ * of the QUEUE REGS. This function is only called at init time or by
+ * the panic_abort function, so it's safe to assume a generic init time
+ * setting here
+ */
+
+ if(p->features & AHC_QUEUE_REGS)
+ {
+ aic_outb(p, 0, SDSCB_QOFF);
+ aic_outb(p, 0, SNSCB_QOFF);
+ aic_outb(p, 0, HNSCB_QOFF);
+ }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_scratch_ram
+ *
+ * Description:
+ * Print out the scratch RAM values on the card.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
+{
+ int i, k;
+
+ k = 0;
+ printk("Scratch RAM:\n");
+ for(i = SRAM_BASE; i < SEQCTL; i++)
+ {
+ printk("%02x:%02x ", i, aic_inb(p, i));
+ if(++k == 13)
+ {
+ printk("\n");
+ k=0;
+ }
+ }
+ if (p->features & AHC_MORE_SRAM)
+ {
+ for(i = TARG_OFFSET; i < 0x80; i++)
+ {
+ printk("%02x:%02x ", i, aic_inb(p, i));
+ if(++k == 13)
+ {
+ printk("\n");
+ k=0;
+ }
+ }
+ }
+ printk("\n");
+}
+
+
+#include "aic7xxx_old/aic7xxx_proc.c"
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(AIC7XXX_H_VERSION);
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_info = aic7xxx_proc_info,
+ .detect = aic7xxx_detect,
+ .release = aic7xxx_release,
+ .info = aic7xxx_info,
+ .queuecommand = aic7xxx_queue,
+ .slave_alloc = aic7xxx_slave_alloc,
+ .slave_configure = aic7xxx_slave_configure,
+ .slave_destroy = aic7xxx_slave_destroy,
+ .bios_param = aic7xxx_biosparam,
+ .eh_abort_handler = aic7xxx_abort,
+ .eh_device_reset_handler = aic7xxx_bus_device_reset,
+ .eh_host_reset_handler = aic7xxx_reset,
+ .can_queue = 255,
+ .this_id = -1,
+ .max_sectors = 2048,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+#include "scsi_module.c"
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.h b/drivers/scsi/aic7xxx_old/aic7xxx.h
new file mode 100644
index 000000000000..0116c8128a6b
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/aic7xxx.h
@@ -0,0 +1,28 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: aic7xxx.h,v 3.2 1996/07/23 03:37:26 deang Exp $
+ *-M*************************************************************************/
+#ifndef _aic7xxx_h
+#define _aic7xxx_h
+
+#define AIC7XXX_H_VERSION "5.2.0"
+
+#endif /* _aic7xxx_h */
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.reg b/drivers/scsi/aic7xxx_old/aic7xxx.reg
new file mode 100644
index 000000000000..f67b4bced01c
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/aic7xxx.reg
@@ -0,0 +1,1401 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-1998 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $
+ */
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+ address 0x000
+ access_mode RW
+ bit TEMODE 0x80
+ bit ENSELO 0x40
+ bit ENSELI 0x20
+ bit ENRSELI 0x10
+ bit ENAUTOATNO 0x08
+ bit ENAUTOATNI 0x04
+ bit ENAUTOATNP 0x02
+ bit SCSIRSTO 0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+ address 0x001
+ access_mode RW
+ bit DFON 0x80
+ bit DFPEXP 0x40
+ bit FAST20 0x20
+ bit CLRSTCNT 0x10
+ bit SPIOEN 0x08
+ bit SCAMEN 0x04
+ bit CLRCHN 0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+ address 0x002
+ access_mode RW
+ bit BITBUCKET 0x80
+ bit SWRAPEN 0x40
+ bit ENSPCHK 0x20
+ mask STIMESEL 0x18
+ bit ENSTIMER 0x04
+ bit ACTNEGEN 0x02
+ bit STPWEN 0x01 /* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+ address 0x003
+ access_mode RO
+ bit CDI 0x80
+ bit IOI 0x40
+ bit MSGI 0x20
+ bit ATNI 0x10
+ bit SELI 0x08
+ bit BSYI 0x04
+ bit REQI 0x02
+ bit ACKI 0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus. Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+ address 0x003
+ access_mode WO
+ bit CDO 0x80
+ bit IOO 0x40
+ bit MSGO 0x20
+ bit ATNO 0x10
+ bit SELO 0x08
+ bit BSYO 0x04
+ bit REQO 0x02
+ bit ACKO 0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers. Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+ address 0x004
+ access_mode RW
+ bit WIDEXFER 0x80 /* Wide transfer control */
+ mask SXFR 0x70 /* Sync transfer rate */
+ mask SXFR_ULTRA2 0x7f /* Sync transfer rate */
+ mask SOFS 0x0f /* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID {
+ address 0x005
+ access_mode RW
+ mask TID 0xf0 /* Target ID mask */
+ mask OID 0x0f /* Our ID mask */
+ /*
+ * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+ * The aic7890/91 allow an offset of up to 127 transfers in both wide
+ * and narrow mode.
+ */
+ alias SCSIOFFSET
+ mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode. SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+ address 0x006
+ access_mode RW
+}
+
+register SCSIDATH {
+ address 0x007
+ access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus. The counter is decremented only once
+ * the data has been safely transferred. SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+register STCNT {
+ address 0x008
+ size 3
+ access_mode RW
+}
+
+/*
+ * Option Mode Register (Alternate Mode) (p. 5-198)
+ * This register is used to set certain options on Ultra3 based chips.
+ * The chip must be in alternate mode (bit ALT_MODE in SFUNCT must be set)
+ */
+register OPTIONMODE {
+ address 0x008
+ access_mode RW
+ bit AUTORATEEN 0x80
+ bit AUTOACKEN 0x40
+ bit ATNMGMNTEN 0x20
+ bit BUSFREEREV 0x10
+ bit EXPPHASEDIS 0x08
+ bit SCSIDATL_IMGEN 0x04
+ bit AUTO_MSGOUT_DE 0x02
+ bit DIS_MSGIN_DUALEDGE 0x01
+}
+
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+ address 0x00b
+ access_mode WO
+ bit CLRSELDO 0x40
+ bit CLRSELDI 0x20
+ bit CLRSELINGO 0x10
+ bit CLRSWRAP 0x08
+ bit CLRSPIORDY 0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0 {
+ address 0x00b
+ access_mode RO
+ bit TARGET 0x80 /* Board acting as target */
+ bit SELDO 0x40 /* Selection Done */
+ bit SELDI 0x20 /* Board has been selected */
+ bit SELINGO 0x10 /* Selection In Progress */
+ bit SWRAP 0x08 /* 24bit counter wrap */
+ bit IOERR 0x08 /* LVD Tranceiver mode changed */
+ bit SDONE 0x04 /* STCNT = 0x000000 */
+ bit SPIORDY 0x02 /* SCSI PIO Ready */
+ bit DMADONE 0x01 /* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+ address 0x00c
+ access_mode WO
+ bit CLRSELTIMEO 0x80
+ bit CLRATNO 0x40
+ bit CLRSCSIRSTI 0x20
+ bit CLRBUSFREE 0x08
+ bit CLRSCSIPERR 0x04
+ bit CLRPHASECHG 0x02
+ bit CLRREQINIT 0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1 {
+ address 0x00c
+ access_mode RO
+ bit SELTO 0x80
+ bit ATNTARG 0x40
+ bit SCSIRSTI 0x20
+ bit PHASEMIS 0x10
+ bit BUSFREE 0x08
+ bit SCSIPERR 0x04
+ bit PHASECHG 0x02
+ bit REQINIT 0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+ address 0x00d
+ access_mode RO
+ bit OVERRUN 0x80
+ bit SHVALID 0x40
+ bit WIDE_RES 0x20
+ bit EXP_ACTIVE 0x10 /* SCSI Expander Active */
+ bit CRCVALERR 0x08 /* CRC Value Error */
+ bit CRCENDERR 0x04 /* CRC End Error */
+ bit CRCREQERR 0x02 /* CRC REQ Error */
+ bit DUAL_EDGE_ERROR 0x01 /* Invalid pins for Dual Edge phase */
+ mask SFCNT 0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+ address 0x00e
+ access_mode RO
+ mask SCSICNT 0xf0
+ mask OFFCNT 0x0f
+}
+
+/*
+ * SCSI ID for the aic7890/91 chips
+ */
+register SCSIID_ULTRA2 {
+ address 0x00f
+ access_mode RW
+ mask TID 0xf0 /* Target ID mask */
+ mask OID 0x0f /* Our ID mask */
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+ address 0x010
+ access_mode RW
+ bit ENSELDO 0x40
+ bit ENSELDI 0x20
+ bit ENSELINGO 0x10
+ bit ENSWRAP 0x08
+ bit ENIOERR 0x08 /* LVD Tranceiver mode changes */
+ bit ENSDONE 0x04
+ bit ENSPIORDY 0x02
+ bit ENDMADONE 0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+ address 0x011
+ access_mode RW
+ bit ENSELTIMO 0x80
+ bit ENATNTARG 0x40
+ bit ENSCSIRST 0x20
+ bit ENPHASEMIS 0x10
+ bit ENBUSFREE 0x08
+ bit ENSCSIPERR 0x04
+ bit ENPHASECHG 0x02
+ bit ENREQINIT 0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+ address 0x012
+ access_mode RO
+}
+
+register SCSIBUSH {
+ address 0x013
+ access_mode RO
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus. They are counted up in the same
+ * manner as STCNT is counted down. SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+ address 0x014
+ size 4
+ access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+ address 0x018
+ access_mode RW
+ bit STAGE6 0x20
+ bit STAGE5 0x10
+ bit STAGE4 0x08
+ bit STAGE3 0x04
+ bit STAGE2 0x02
+ bit STAGE1 0x01
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+ address 0x019
+ access_mode RW
+ mask SELID_MASK 0xf0
+ bit ONEBIT 0x08
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection. On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+ address 0x01b
+ access_mode RW
+ bit SOFT1 0x80
+ bit SOFT0 0x40
+ bit SOFTCMDEN 0x20
+ bit HAS_BRDCTL 0x10 /* External Board control */
+ bit SEEPROM 0x08 /* External serial eeprom logic */
+ bit EEPROM 0x04 /* Writable external BIOS ROM */
+ bit ROM 0x02 /* Logic for accessing external ROM */
+ bit SSPIOCPS 0x01 /* Termination and cable detection */
+}
+
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection. In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+ address 0x01f
+ access_mode RW
+ bit DIAGLEDEN 0x80 /* Aic78X0 only */
+ bit DIAGLEDON 0x40 /* Aic78X0 only */
+ bit AUTOFLUSHDIS 0x20
+ bit SELBUSB 0x08
+ bit ENAB40 0x08 /* LVD transceiver active */
+ bit ENAB20 0x04 /* SE/HVD transceiver active */
+ bit SELWIDE 0x02
+ bit XCVR 0x01 /* External transceiver active */
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+ address 0x060
+ access_mode RW
+ bit PERRORDIS 0x80
+ bit PAUSEDIS 0x40
+ bit FAILDIS 0x20
+ bit FASTMODE 0x10
+ bit BRKADRINTEN 0x08
+ bit STEP 0x04
+ bit SEQRESET 0x02
+ bit LOADRAM 0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
+ * four bytes in succession. The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+ address 0x061
+ access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+ address 0x062
+ access_mode RW
+}
+
+register SEQADDR1 {
+ address 0x063
+ access_mode RW
+ mask SEQADDR1_MASK 0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+ address 0x064
+ access_mode RW
+ accumulator
+}
+
+register SINDEX {
+ address 0x065
+ access_mode RW
+ sindex
+}
+
+register DINDEX {
+ address 0x066
+ access_mode RW
+}
+
+register ALLONES {
+ address 0x069
+ access_mode RO
+ allones
+}
+
+register ALLZEROS {
+ address 0x06a
+ access_mode RO
+ allzeros
+}
+
+register NONE {
+ address 0x06a
+ access_mode WO
+ none
+}
+
+register FLAGS {
+ address 0x06b
+ access_mode RO
+ bit ZERO 0x02
+ bit CARRY 0x01
+}
+
+register SINDIR {
+ address 0x06c
+ access_mode RO
+}
+
+register DINDIR {
+ address 0x06d
+ access_mode WO
+}
+
+register FUNCTION1 {
+ address 0x06e
+ access_mode RW
+}
+
+register STACK {
+ address 0x06f
+ access_mode RO
+}
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+ address 0x084
+ access_mode RW
+ bit ACE 0x08
+ bit ENABLE 0x01
+}
+
+register DSCOMMAND0 {
+ address 0x084
+ access_mode RW
+ bit CACHETHEN 0x80
+ bit DPARCKEN 0x40
+ bit MPARCKEN 0x20
+ bit EXTREQLCK 0x10
+ bit INTSCBRAMSEL 0x08
+ bit RAMPS 0x04
+ bit USCBSIZE32 0x02
+ bit CIOPARCKEN 0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND {
+ address 0x084
+ access_mode RW
+ bit CACHETHEN 0x80 /* Cache Threshold enable */
+ bit DPARCKEN 0x40 /* Data Parity Check Enable */
+ bit MPARCKEN 0x20 /* Memory Parity Check Enable */
+ bit EXTREQLCK 0x10 /* External Request Lock */
+}
+
+/*
+ * Bus On/Off Time (p. 3-44)
+ */
+register BUSTIME {
+ address 0x085
+ access_mode RW
+ mask BOFF 0xf0
+ mask BON 0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45)
+ */
+register BUSSPD {
+ address 0x086
+ access_mode RW
+ mask DFTHRSH 0xc0
+ mask STBOFF 0x38
+ mask STBON 0x07
+ mask DFTHRSH_100 0xc0
+}
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+ address 0x087
+ access_mode RW
+ bit POWRDN 0x40
+ bit SWINT 0x10
+ bit IRQMS 0x08
+ bit PAUSE 0x04
+ bit INTEN 0x02
+ bit CHIPRST 0x01
+ bit CHIPRSTACK 0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+ address 0x088
+ size 4
+ access_mode RW
+}
+
+register HCNT {
+ address 0x08c
+ size 3
+ access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+ address 0x090
+ access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+ address 0x091
+ access_mode RW
+ bit BRKADRINT 0x08
+ bit SCSIINT 0x04
+ bit CMDCMPLT 0x02
+ bit SEQINT 0x01
+ mask BAD_PHASE SEQINT /* unknown scsi bus phase */
+ mask SEND_REJECT 0x10|SEQINT /* sending a message reject */
+ mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/
+ mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
+ mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */
+ mask WIDE_RESIDUE 0x50|SEQINT /* need kernel to back up */
+ /* the SG array for us */
+ mask REJECT_MSG 0x60|SEQINT /* Reject message received */
+ mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
+ mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */
+ mask AWAITING_MSG 0xa0|SEQINT /*
+ * Kernel requested to specify
+ * a message to this target
+ * (command was null), so tell
+ * it that it can fill the
+ * message buffer.
+ */
+ mask SEQ_SG_FIXUP 0xb0|SEQINT /* need help with fixing up
+ * the sg array pointer after
+ * a phasemis with no valid
+ * sg elements in the shadow
+ * pipeline.
+ */
+ mask TRACEPOINT2 0xc0|SEQINT
+ mask MSGIN_PHASEMIS 0xd0|SEQINT /*
+ * Target changed phase on us
+ * when we were expecting
+ * another msgin byte.
+ */
+ mask DATA_OVERRUN 0xe0|SEQINT /*
+ * Target attempted to write
+ * beyond the bounds of its
+ * command.
+ */
+
+ mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */
+ mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors. You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+ address 0x092
+ access_mode RO
+ bit CIOPARERR 0x80 /* Ultra2 only */
+ bit PCIERRSTAT 0x40 /* PCI only */
+ bit MPARERR 0x20 /* PCI only */
+ bit DPARERR 0x10 /* PCI only */
+ bit SQPARERR 0x08
+ bit ILLOPCODE 0x04
+ bit ILLSADDR 0x02
+ bit DSCTMOUT 0x02 /* Ultra3 only */
+ bit ILLHADDR 0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+ address 0x092
+ access_mode WO
+ bit CLRPARERR 0x10 /* PCI only */
+ bit CLRBRKADRINT 0x08
+ bit CLRSCSIINT 0x04
+ bit CLRCMDINT 0x02
+ bit CLRSEQINT 0x01
+}
+
+register DFCNTRL {
+ address 0x093
+ access_mode RW
+ bit PRELOADEN 0x80 /* aic7890 only */
+ bit WIDEODD 0x40
+ bit SCSIEN 0x20
+ bit SDMAEN 0x10
+ bit SDMAENACK 0x10
+ bit HDMAEN 0x08
+ bit HDMAENACK 0x08
+ bit DIRECTION 0x04
+ bit FIFOFLUSH 0x02
+ bit FIFORESET 0x01
+}
+
+register DFSTATUS {
+ address 0x094
+ access_mode RO
+ bit PRELOAD_AVAIL 0x80
+ bit DWORDEMP 0x20
+ bit MREQPEND 0x10
+ bit HDONE 0x08
+ bit DFTHRESH 0x04
+ bit FIFOFULL 0x02
+ bit FIFOEMP 0x01
+}
+
+register DFDAT {
+ address 0x099
+ access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+ address 0x09a
+ access_mode RW
+ bit SCBAUTO 0x80
+ mask SCBCNT_MASK 0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+ address 0x09b
+ access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT {
+ address 0x09c
+ access_mode RO
+}
+
+/*
+ * SCSIDATL IMAGE Register (p. 5-104)
+ * Write to this register also go to SCSIDATL but this register will preserve
+ * the data for later reading as long as the SCSIDATL_IMGEN bit in the
+ * OPTIONMODE register is set.
+ */
+register SCSIDATL_IMG {
+ address 0x09c
+ access_mode RW
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+ address 0x09d
+ access_mode WO
+}
+
+/*
+ * CRC Control 1 Register (p. 5-105)
+ * Control bits for the Ultra 160/m CRC facilities
+ */
+register CRCCONTROL1 {
+ address 0x09d
+ access_mode RW
+ bit CRCONSEEN 0x80 /* CRC ON Single Edge ENable */
+ bit CRCVALCHKEN 0x40 /* CRC Value Check Enable */
+ bit CRCENDCHKEN 0x20 /* CRC End Check Enable */
+ bit CRCREQCHKEN 0x10
+ bit TARGCRCENDEN 0x08 /* Enable End CRC transfer when target */
+ bit TARGCRCCNTEN 0x04 /* Enable CRC transfer when target */
+}
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+ address 0x09e
+ access_mode RO
+}
+
+/*
+ * SCSI Phase Register (p. 5-106)
+ * Current bus phase
+ */
+register SCSIPHASE {
+ address 0x09e
+ access_mode RO
+ bit SP_STATUS 0x20
+ bit SP_COMMAND 0x10
+ bit SP_MSG_IN 0x08
+ bit SP_MSG_OUT 0x04
+ bit SP_DATA_IN 0x02
+ bit SP_DATA_OUT 0x01
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+ bit ALT_MODE 0x80
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+ address 0x0a0
+ SCB_CONTROL {
+ size 1
+ bit MK_MESSAGE 0x80
+ bit DISCENB 0x40
+ bit TAG_ENB 0x20
+ bit DISCONNECTED 0x04
+ mask SCB_TAG_TYPE 0x03
+ }
+ SCB_TCL {
+ size 1
+ bit SELBUSB 0x08
+ mask TID 0xf0
+ mask LID 0x07
+ }
+ SCB_TARGET_STATUS {
+ size 1
+ }
+ SCB_SGCOUNT {
+ size 1
+ }
+ SCB_SGPTR {
+ size 4
+ }
+ SCB_RESID_SGCNT {
+ size 1
+ }
+ SCB_RESID_DCNT {
+ size 3
+ }
+ SCB_DATAPTR {
+ size 4
+ }
+ SCB_DATACNT {
+ /*
+ * Really only 3 bytes, but padded to make
+ * the kernel's job easier.
+ */
+ size 4
+ }
+ SCB_CMDPTR {
+ size 4
+ }
+ SCB_CMDLEN {
+ size 1
+ }
+ SCB_TAG {
+ size 1
+ }
+ SCB_NEXT {
+ size 1
+ }
+ SCB_PREV {
+ size 1
+ }
+ SCB_BUSYTARGETS {
+ size 4
+ }
+}
+
+const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+ address 0x0c0
+ access_mode RW
+ bit CS_2840 0x04
+ bit CK_2840 0x02
+ bit DO_2840 0x01
+}
+
+register STATUS_2840 {
+ address 0x0c1
+ access_mode RW
+ bit EEPROM_TF 0x80
+ mask BIOS_SEL 0x60
+ mask ADSEL 0x1e
+ bit DI_2840 0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register DSPCISTATUS {
+ address 0x086
+ mask DFTHRSH_100 0xc0
+}
+
+register CCHADDR {
+ address 0x0E0
+ size 8
+}
+
+register CCHCNT {
+ address 0x0E8
+}
+
+register CCSGRAM {
+ address 0x0E9
+}
+
+register CCSGADDR {
+ address 0x0EA
+}
+
+register CCSGCTL {
+ address 0x0EB
+ bit CCSGDONE 0x80
+ bit CCSGEN 0x08
+ bit FLAG 0x02
+ bit CCSGRESET 0x01
+}
+
+register CCSCBCNT {
+ address 0xEF
+}
+
+register CCSCBCTL {
+ address 0x0EE
+ bit CCSCBDONE 0x80
+ bit ARRDONE 0x40 /* SCB Array prefetch done */
+ bit CCARREN 0x10
+ bit CCSCBEN 0x08
+ bit CCSCBDIR 0x04
+ bit CCSCBRESET 0x01
+}
+
+register CCSCBADDR {
+ address 0x0ED
+}
+
+register CCSCBRAM {
+ address 0xEC
+}
+
+register CCSCBPTR {
+ address 0x0F1
+}
+
+register HNSCB_QOFF {
+ address 0x0F4
+}
+
+register HESCB_QOFF {
+ address 0x0F5
+}
+
+register SNSCB_QOFF {
+ address 0x0F6
+}
+
+register SESCB_QOFF {
+ address 0x0F7
+}
+
+register SDSCB_QOFF {
+ address 0x0F8
+}
+
+register QOFF_CTLSTA {
+ address 0x0FA
+ bit ESTABLISH_SCB_AVAIL 0x80
+ bit SCB_AVAIL 0x40
+ bit SNSCB_ROLLOVER 0x20
+ bit SDSCB_ROLLOVER 0x10
+ bit SESCB_ROLLOVER 0x08
+ mask SCB_QSIZE 0x07
+ mask SCB_QSIZE_256 0x06
+}
+
+register DFF_THRSH {
+ address 0x0FB
+ mask WR_DFTHRSH 0x70
+ mask RD_DFTHRSH 0x07
+ mask RD_DFTHRSH_MIN 0x00
+ mask RD_DFTHRSH_25 0x01
+ mask RD_DFTHRSH_50 0x02
+ mask RD_DFTHRSH_63 0x03
+ mask RD_DFTHRSH_75 0x04
+ mask RD_DFTHRSH_85 0x05
+ mask RD_DFTHRSH_90 0x06
+ mask RD_DFTHRSH_MAX 0x07
+ mask WR_DFTHRSH_MIN 0x00
+ mask WR_DFTHRSH_25 0x10
+ mask WR_DFTHRSH_50 0x20
+ mask WR_DFTHRSH_63 0x30
+ mask WR_DFTHRSH_75 0x40
+ mask WR_DFTHRSH_85 0x50
+ mask WR_DFTHRSH_90 0x60
+ mask WR_DFTHRSH_MAX 0x70
+}
+
+register SG_CACHEPTR {
+ access_mode RW
+ address 0x0fc
+ mask SG_USER_DATA 0xfc
+ bit LAST_SEG 0x02
+ bit LAST_SEG_DONE 0x01
+}
+
+register BRDCTL {
+ address 0x01d
+ bit BRDDAT7 0x80
+ bit BRDDAT6 0x40
+ bit BRDDAT5 0x20
+ bit BRDSTB 0x10
+ bit BRDCS 0x08
+ bit BRDRW 0x04
+ bit BRDCTL1 0x02
+ bit BRDCTL0 0x01
+ /* 7890 Definitions */
+ bit BRDDAT4 0x10
+ bit BRDDAT3 0x08
+ bit BRDDAT2 0x04
+ bit BRDRW_ULTRA2 0x02
+ bit BRDSTB_ULTRA2 0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device. In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device. When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM. When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.
+ *
+ * After successful arbitration for the memory port, the SEECS bit of
+ * the SEECTL register is connected to the chip select. The SEECK,
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in
+ * lines respectively. The SEERDY bit of SEECTL is useful in that it
+ * gives us an 800 nsec timer. After a write to the SEECTL register,
+ * the SEERDY goes high 800 nsec later. The one exception to this is
+ * when we first request access to the memory port. The SEERDY goes
+ * high to signify that access has been granted and, for this case, has
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to
+ * read the serial EEPROM.
+ */
+register SEECTL {
+ address 0x01e
+ bit EXTARBACK 0x80
+ bit EXTARBREQ 0x40
+ bit SEEMS 0x20
+ bit SEERDY 0x10
+ bit SEECS 0x08
+ bit SEECK 0x04
+ bit SEEDO 0x02
+ bit SEEDI 0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE). The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space. This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+ address 0x020
+
+ /*
+ * 1 byte per target starting at this address for configuration values
+ */
+ TARG_SCSIRATE {
+ size 16
+ }
+ /*
+ * Bit vector of targets that have ULTRA enabled.
+ */
+ ULTRA_ENB {
+ size 2
+ }
+ /*
+ * Bit vector of targets that have disconnection disabled.
+ */
+ DISC_DSB {
+ size 2
+ }
+ /*
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
+ */
+ MSG_OUT {
+ size 1
+ }
+ /* Parameters for DMA Logic */
+ DMAPARAMS {
+ size 1
+ bit PRELOADEN 0x80
+ bit WIDEODD 0x40
+ bit SCSIEN 0x20
+ bit SDMAEN 0x10
+ bit SDMAENACK 0x10
+ bit HDMAEN 0x08
+ bit HDMAENACK 0x08
+ bit DIRECTION 0x04
+ bit FIFOFLUSH 0x02
+ bit FIFORESET 0x01
+ }
+ SEQ_FLAGS {
+ size 1
+ bit IDENTIFY_SEEN 0x80
+ bit SCBPTR_VALID 0x20
+ bit DPHASE 0x10
+ bit AMTARGET 0x08
+ bit WIDE_BUS 0x02
+ bit TWIN_BUS 0x01
+ }
+ /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+ SAVED_TCL {
+ size 1
+ }
+ /* Working value of the number of SG segments left */
+ SG_COUNT {
+ size 1
+ }
+ /* Working value of SG pointer */
+ SG_NEXT {
+ size 4
+ }
+ /*
+ * The last bus phase as seen by the sequencer.
+ */
+ LASTPHASE {
+ size 1
+ bit CDI 0x80
+ bit IOI 0x40
+ bit MSGI 0x20
+ mask PHASE_MASK CDI|IOI|MSGI
+ mask P_DATAOUT 0x00
+ mask P_DATAIN IOI
+ mask P_COMMAND CDI
+ mask P_MESGOUT CDI|MSGI
+ mask P_STATUS CDI|IOI
+ mask P_MESGIN CDI|IOI|MSGI
+ mask P_BUSFREE 0x01
+ }
+ /*
+ * head of list of SCBs awaiting
+ * selection
+ */
+ WAITING_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that are
+ * disconnected. Used for SCB
+ * paging.
+ */
+ DISCONNECTED_SCBH {
+ size 1
+ }
+ /*
+ * head of list of SCBs that are
+ * not in use. Used for SCB paging.
+ */
+ FREE_SCBH {
+ size 1
+ }
+ /*
+ * Address of the hardware scb array in the host.
+ */
+ HSCB_ADDR {
+ size 4
+ }
+ /*
+ * Address of the 256 byte array storing the SCBID of outstanding
+ * untagged SCBs indexed by TCL.
+ */
+ SCBID_ADDR {
+ size 4
+ }
+ /*
+ * Address of the array of command descriptors used to store
+ * information about incoming selections.
+ */
+ TMODE_CMDADDR {
+ size 4
+ }
+ KERNEL_QINPOS {
+ size 1
+ }
+ QINPOS {
+ size 1
+ }
+ QOUTPOS {
+ size 1
+ }
+ /*
+ * Offset into the command descriptor array for the next
+ * available desciptor to use.
+ */
+ TMODE_CMDADDR_NEXT {
+ size 1
+ }
+ ARG_1 {
+ size 1
+ mask SEND_MSG 0x80
+ mask SEND_SENSE 0x40
+ mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
+ alias RETURN_1
+ }
+ ARG_2 {
+ size 1
+ alias RETURN_2
+ }
+
+ /*
+ * Snapshot of MSG_OUT taken after each message is sent.
+ */
+ LAST_MSG {
+ size 1
+ }
+
+ /*
+ * Number of times we have filled the CCSGRAM with prefetched
+ * SG elements.
+ */
+ PREFETCH_CNT {
+ size 1
+ }
+
+
+ /*
+ * These are reserved registers in the card's scratch ram. Some of
+ * the values are specified in the AHA2742 technical reference manual
+ * and are initialized by the BIOS at boot time.
+ */
+ SCSICONF {
+ address 0x05a
+ size 1
+ bit TERM_ENB 0x80
+ bit RESET_SCSI 0x40
+ mask HSCSIID 0x07 /* our SCSI ID */
+ mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
+ }
+ HOSTCONF {
+ address 0x05d
+ size 1
+ }
+ HA_274_BIOSCTRL {
+ address 0x05f
+ size 1
+ mask BIOSMODE 0x30
+ mask BIOSDISABLED 0x30
+ bit CHANNEL_B_PRIMARY 0x08
+ }
+ /*
+ * Per target SCSI offset values for Ultra2 controllers.
+ */
+ TARG_OFFSET {
+ address 0x070
+ size 16
+ }
+}
+
+const SCB_LIST_NULL 0xff
+
+const CCSGADDR_MAX 0x80
+const CCSGRAM_MAXSEGS 16
+
+/* Offsets into the SCBID array where different data is stored */
+const UNTAGGEDSCB_OFFSET 0
+const QOUTFIFO_OFFSET 1
+const QINFIFO_OFFSET 2
+
+/* WDTR Message values */
+const BUS_8_BIT 0x00
+const BUS_16_BIT 0x01
+const BUS_32_BIT 0x02
+
+/* Offset maximums */
+const MAX_OFFSET_8BIT 0x0f
+const MAX_OFFSET_16BIT 0x08
+const MAX_OFFSET_ULTRA2 0x7f
+const HOST_MSG 0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+const CMD_GROUP0_BYTE_DELTA -4
+const CMD_GROUP2_BYTE_DELTA -6
+const CMD_GROUP4_BYTE_DELTA 4
+const CMD_GROUP5_BYTE_DELTA 11
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+
+/*
+ * Number of command descriptors in the command descriptor array.
+ */
+const TMODE_NUMCMDS download
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.seq b/drivers/scsi/aic7xxx_old/aic7xxx.seq
new file mode 100644
index 000000000000..f6fc4b75b5a5
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/aic7xxx.seq
@@ -0,0 +1,1539 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-1999 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License (GPL) and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
+ */
+
+#include "aic7xxx.reg"
+#include "scsi_message.h"
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration. The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time. This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection. The solution used here is to
+ * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
+ * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes,
+ * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued. The sequencer will
+ * automatically consume the entries.
+ */
+
+reset:
+ clr SCSISIGO; /* De-assert BSY */
+ and SXFRCTL1, ~BITBUCKET;
+ /* Always allow reselection */
+ mvi SCSISEQ, ENRSELI|ENAUTOATNP;
+
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ /* Ensure that no DMA operations are in progress */
+ clr CCSGCTL;
+ clr CCSCBCTL;
+ }
+
+ call clear_target_state;
+poll_for_work:
+ and SXFRCTL0, ~SPIOEN;
+ if ((p->features & AHC_QUEUE_REGS) == 0) {
+ mov A, QINPOS;
+ }
+poll_for_work_loop:
+ if ((p->features & AHC_QUEUE_REGS) == 0) {
+ and SEQCTL, ~PAUSEDIS;
+ }
+ test SSTAT0, SELDO|SELDI jnz selection;
+ test SCSISEQ, ENSELO jnz poll_for_work;
+ if ((p->features & AHC_TWIN) != 0) {
+ /*
+ * Twin channel devices cannot handle things like SELTO
+ * interrupts on the "background" channel. So, if we
+ * are selecting, keep polling the current channel util
+ * either a selection or reselection occurs.
+ */
+ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
+ test SSTAT0, SELDO|SELDI jnz selection;
+ test SCSISEQ, ENSELO jnz poll_for_work;
+ xor SBLKCTL,SELBUSB; /* Toggle back */
+ }
+ cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+test_queue:
+ /* Has the driver posted any work for us? */
+ if ((p->features & AHC_QUEUE_REGS) != 0) {
+ test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+ mov NONE, SNSCB_QOFF;
+ inc QINPOS;
+ } else {
+ or SEQCTL, PAUSEDIS;
+ cmp KERNEL_QINPOS, A je poll_for_work_loop;
+ inc QINPOS;
+ and SEQCTL, ~PAUSEDIS;
+ }
+
+/*
+ * We have at least one queued SCB now and we don't have any
+ * SCBs in the list of SCBs awaiting selection. If we have
+ * any SCBs available for use, pull the tag from the QINFIFO
+ * and get to work on it.
+ */
+ if ((p->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ }
+
+dequeue_scb:
+ add A, -1, QINPOS;
+ mvi QINFIFO_OFFSET call fetch_byte;
+
+ if ((p->flags & AHC_PAGESCBS) == 0) {
+ /* In the non-paging case, the SCBID == hardware SCB index */
+ mov SCBPTR, RETURN_2;
+ }
+dma_queued_scb:
+/*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov RETURN_2 call dma_scb;
+
+/*
+ * Preset the residual fields in case we never go through a data phase.
+ * This isn't done by the host so we can avoid a DMA to clear these
+ * fields for the normal case of I/O that completes without underrun
+ * or overrun conditions.
+ */
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_RESID_DCNT, SCB_DATACNT, 3;
+ } else {
+ mov SCB_RESID_DCNT[0],SCB_DATACNT[0];
+ mov SCB_RESID_DCNT[1],SCB_DATACNT[1];
+ mov SCB_RESID_DCNT[2],SCB_DATACNT[2];
+ }
+ mov SCB_RESID_SGCNT, SCB_SGCOUNT;
+
+start_scb:
+ /*
+ * Place us on the waiting list in case our selection
+ * doesn't win during bus arbitration.
+ */
+ mov SCB_NEXT,WAITING_SCBH;
+ mov WAITING_SCBH, SCBPTR;
+start_waiting:
+ /*
+ * Pull the first entry off of the waiting SCB list.
+ */
+ mov SCBPTR, WAITING_SCBH;
+ call start_selection;
+ jmp poll_for_work;
+
+start_selection:
+ if ((p->features & AHC_TWIN) != 0) {
+ and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+ and A,SELBUSB,SCB_TCL; /* Get new channel bit */
+ or SINDEX,A;
+ mov SBLKCTL,SINDEX; /* select channel */
+ }
+initialize_scsiid:
+ if ((p->features & AHC_ULTRA2) != 0) {
+ and A, TID, SCB_TCL; /* Get target ID */
+ and SCSIID_ULTRA2, OID; /* Clear old target */
+ or SCSIID_ULTRA2, A;
+ } else {
+ and A, TID, SCB_TCL; /* Get target ID */
+ and SCSIID, OID; /* Clear old target */
+ or SCSIID, A;
+ }
+ mov SCSIDATL, ALLZEROS; /* clear out the latched */
+ /* data register, this */
+ /* fixes a bug on some */
+ /* controllers where the */
+ /* last byte written to */
+ /* this register can leak */
+ /* onto the data bus at */
+ /* bad times, such as during */
+ /* selection timeouts */
+ mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+
+/*
+ * Initialize Ultra mode setting and clear the SCSI channel.
+ * SINDEX should contain any additional bit's the client wants
+ * set in SXFRCTL0.
+ */
+initialize_channel:
+ or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
+ if ((p->features & AHC_ULTRA) != 0) {
+ultra:
+ mvi SINDEX, ULTRA_ENB+1;
+ test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */
+ dec SINDEX;
+ultra_2:
+ mov FUNCTION1,SAVED_TCL;
+ mov A,FUNCTION1;
+ test SINDIR, A jz ndx_dtr;
+ or SXFRCTL0, FAST20;
+ }
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ * The SCSIRATE settings for each target are stored in an array
+ * based at TARG_SCSIRATE.
+ */
+ndx_dtr:
+ shr A,4,SAVED_TCL;
+ if ((p->features & AHC_TWIN) != 0) {
+ test SBLKCTL,SELBUSB jz ndx_dtr_2;
+ or SAVED_TCL, SELBUSB;
+ or A,0x08; /* Channel B entries add 8 */
+ndx_dtr_2:
+ }
+
+ if ((p->features & AHC_ULTRA2) != 0) {
+ add SINDEX, TARG_OFFSET, A;
+ mov SCSIOFFSET, SINDIR;
+ }
+
+ add SINDEX,TARG_SCSIRATE,A;
+ mov SCSIRATE,SINDIR ret;
+
+
+selection:
+ test SSTAT0,SELDO jnz select_out;
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+initiator_reselect:
+ mvi CLRSINT0, CLRSELDI;
+ /* XXX test for and handle ONE BIT condition */
+ and SAVED_TCL, SELID_MASK, SELID;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE; /*
+ * We aren't expecting a
+ * bus free, so interrupt
+ * the kernel driver if it
+ * happens.
+ */
+ mvi SPIOEN call initialize_channel;
+ mvi MSG_OUT, MSG_NOOP; /* No message to send */
+ jmp ITloop;
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list. This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH. Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select_out:
+ /* Turn off the selection hardware */
+ mvi SCSISEQ, ENRSELI|ENAUTOATNP; /*
+ * ATN on parity errors
+ * for "in" phases
+ */
+ mvi CLRSINT0, CLRSELDO;
+ mov SCBPTR, WAITING_SCBH;
+ mov WAITING_SCBH,SCB_NEXT;
+ mov SAVED_TCL, SCB_TCL;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE; /*
+ * We aren't expecting a
+ * bus free, so interrupt
+ * the kernel driver if it
+ * happens.
+ */
+ mvi SPIOEN call initialize_channel;
+/*
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted.
+ */
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+ or SEQ_FLAGS, IDENTIFY_SEEN;
+
+/*
+ * Main loop for information transfer phases. Wait for the target
+ * to assert REQ before checking MSG, C/D and I/O for the bus phase.
+ */
+ITloop:
+ call phase_lock;
+
+ mov A, LASTPHASE;
+
+ test A, ~P_DATAIN jz p_data;
+ cmp A,P_COMMAND je p_command;
+ cmp A,P_MESGOUT je p_mesgout;
+ cmp A,P_STATUS je p_status;
+ cmp A,P_MESGIN je p_mesgin;
+
+ mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */
+ jmp ITloop; /* Try reading the bus again. */
+
+await_busfree:
+ and SIMODE1, ~ENBUSFREE;
+ call clear_target_state;
+ mov NONE, SCSIDATL; /* Ack the last byte */
+ and SXFRCTL0, ~SPIOEN;
+ test SSTAT1,REQINIT|BUSFREE jz .;
+ test SSTAT1, BUSFREE jnz poll_for_work;
+ mvi INTSTAT, BAD_PHASE;
+
+clear_target_state:
+ /*
+ * We assume that the kernel driver may reset us
+ * at any time, even in the middle of a DMA, so
+ * clear DFCNTRL too.
+ */
+ clr DFCNTRL;
+
+ /*
+ * We don't know the target we will connect to,
+ * so default to narrow transfers to avoid
+ * parity problems.
+ */
+ if ((p->features & AHC_ULTRA2) != 0) {
+ bmov SCSIRATE, ALLZEROS, 2;
+ } else {
+ clr SCSIRATE;
+ and SXFRCTL0, ~(FAST20);
+ }
+ mvi LASTPHASE, P_BUSFREE;
+ /* clear target specific flags */
+ clr SEQ_FLAGS ret;
+
+
+data_phase_reinit:
+/*
+ * If we re-enter the data phase after going through another phase, the
+ * STCNT may have been cleared, so restore it from the residual field.
+ * On Ultra2, we have to put it into the HCNT field because we have to
+ * drop the data down into the shadow layer via the preload ability.
+ */
+ if ((p->features & AHC_ULTRA2) != 0) {
+ bmov HADDR, SHADDR, 4;
+ bmov HCNT, SCB_RESID_DCNT, 3;
+ }
+ if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ bmov STCNT, SCB_RESID_DCNT, 3;
+ }
+ if ((p->features & AHC_CMD_CHAN) == 0) {
+ mvi DINDEX, STCNT;
+ mvi SCB_RESID_DCNT call bcopy_3;
+ }
+ jmp data_phase_loop;
+p_data:
+ if ((p->features & AHC_ULTRA2) != 0) {
+ mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+ } else {
+ mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+ }
+ test LASTPHASE, IOI jnz . + 2;
+ or DMAPARAMS, DIRECTION;
+ call assert; /*
+ * Ensure entering a data
+ * phase is okay - seen identify, etc.
+ */
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ mvi CCSGADDR, CCSGADDR_MAX;
+ }
+
+ test SEQ_FLAGS, DPHASE jnz data_phase_reinit;
+ or SEQ_FLAGS, DPHASE; /* we've seen a data phase */
+ /*
+ * Initialize the DMA address and counter from the SCB.
+ * Also set SG_COUNT and SG_NEXT in memory since we cannot
+ * modify the values in the SCB itself until we see a
+ * save data pointers message.
+ */
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SCB_DATAPTR, 7;
+ bmov SG_COUNT, SCB_SGCOUNT, 5;
+ if ((p->features & AHC_ULTRA2) == 0) {
+ bmov STCNT, HCNT, 3;
+ }
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_DATAPTR call bcopy_7;
+ call set_stcnt_from_hcnt;
+ mvi DINDEX, SG_COUNT;
+ mvi SCB_SGCOUNT call bcopy_5;
+ }
+data_phase_loop:
+ /* Guard against overruns */
+ test SG_COUNT, 0xff jnz data_phase_inbounds;
+/*
+ * Turn on 'Bit Bucket' mode, set the transfer count to
+ * 16meg and let the target run until it changes phase.
+ * When the transfer completes, notify the host that we
+ * had an overrun.
+ */
+ or SXFRCTL1,BITBUCKET;
+ and DMAPARAMS, ~(HDMAEN|SDMAEN);
+ if ((p->features & AHC_ULTRA2) != 0) {
+ bmov HCNT, ALLONES, 3;
+ }
+ if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ bmov STCNT, ALLONES, 3;
+ }
+ if ((p->features & AHC_CMD_CHAN) == 0) {
+ mvi STCNT[0], 0xFF;
+ mvi STCNT[1], 0xFF;
+ mvi STCNT[2], 0xFF;
+ }
+
+data_phase_inbounds:
+/* If we are the last SG block, tell the hardware. */
+ if ((p->features & AHC_ULTRA2) != 0) {
+ shl A, 2, SG_COUNT;
+ cmp SG_COUNT,0x01 jne data_phase_wideodd;
+ or A, LAST_SEG;
+ } else {
+ cmp SG_COUNT,0x01 jne data_phase_wideodd;
+ and DMAPARAMS, ~WIDEODD;
+ }
+data_phase_wideodd:
+ if ((p->features & AHC_ULTRA2) != 0) {
+ mov SG_CACHEPTR, A;
+ mov DFCNTRL, DMAPARAMS; /* start the operation */
+ test SXFRCTL1, BITBUCKET jnz data_phase_overrun;
+u2_preload_wait:
+ test SSTAT1, PHASEMIS jnz u2_phasemis;
+ test DFSTATUS, PRELOAD_AVAIL jz u2_preload_wait;
+ } else {
+ mov DMAPARAMS call dma;
+data_phase_dma_done:
+/* Go tell the host about any overruns */
+ test SXFRCTL1,BITBUCKET jnz data_phase_overrun;
+
+/* Exit if we had an underrun. dma clears SINDEX in this case. */
+ test SINDEX,0xff jz data_phase_finish;
+ }
+/*
+ * Advance the scatter-gather pointers
+ */
+sg_advance:
+ if ((p->features & AHC_ULTRA2) != 0) {
+ cmp SG_COUNT, 0x01 je u2_data_phase_finish;
+ } else {
+ dec SG_COUNT;
+ test SG_COUNT, 0xff jz data_phase_finish;
+ }
+
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+
+ /*
+ * Do we have any prefetch left???
+ */
+ cmp CCSGADDR, CCSGADDR_MAX jne prefetch_avail;
+
+ /*
+ * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+ */
+ add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
+ mvi A, CCSGADDR_MAX;
+ jc . + 2;
+ shl A, 3, SG_COUNT;
+ mov CCHCNT, A;
+ bmov CCHADDR, SG_NEXT, 4;
+ mvi CCSGCTL, CCSGEN|CCSGRESET;
+ test CCSGCTL, CCSGDONE jz .;
+ and CCSGCTL, ~CCSGEN;
+ test CCSGCTL, CCSGEN jnz .;
+ mvi CCSGCTL, CCSGRESET;
+prefetch_avail:
+ bmov HADDR, CCSGRAM, 8;
+ if ((p->features & AHC_ULTRA2) == 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ dec SG_COUNT;
+ }
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SG_NEXT call bcopy_4;
+
+ mvi HCNT[0],SG_SIZEOF;
+ clr HCNT[1];
+ clr HCNT[2];
+
+ or DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+ call dma_finish;
+
+/*
+ * Copy data from FIFO into SCB data pointer and data count.
+ * This assumes that the SG segments are of the form:
+ * struct ahc_dma_seg {
+ * u_int32_t addr; four bytes, little-endian order
+ * u_int32_t len; four bytes, little endian order
+ * };
+ */
+ mvi DINDEX, HADDR;
+ call dfdat_in_7;
+ call set_stcnt_from_hcnt;
+ }
+/* Advance the SG pointer */
+ clr A; /* add sizeof(struct scatter) */
+ add SG_NEXT[0],SG_SIZEOF;
+ adc SG_NEXT[1],A;
+
+ if ((p->features & AHC_ULTRA2) != 0) {
+ jmp data_phase_loop;
+ } else {
+ test SSTAT1, REQINIT jz .;
+ test SSTAT1,PHASEMIS jz data_phase_loop;
+ }
+
+
+/*
+ * We've loaded all of our segments into the preload layer. Now, we simply
+ * have to wait for it to finish or for us to get a phasemis. And, since
+ * we'll get a phasemis if we do finish, all we really need to do is wait
+ * for a phasemis then check if we did actually complete all the segments.
+ */
+ if ((p->features & AHC_ULTRA2) != 0) {
+u2_data_phase_finish:
+ test SSTAT1, PHASEMIS jnz u2_phasemis;
+ test SG_CACHEPTR, LAST_SEG_DONE jz u2_data_phase_finish;
+ clr SG_COUNT;
+ test SSTAT1, REQINIT jz .;
+ test SSTAT1, PHASEMIS jz data_phase_loop;
+u2_phasemis:
+ call ultra2_dmafinish;
+ test SG_CACHEPTR, LAST_SEG_DONE jnz data_phase_finish;
+ test SSTAT2, SHVALID jnz u2_fixup_residual;
+ mvi INTSTAT, SEQ_SG_FIXUP;
+ jmp data_phase_finish;
+u2_fixup_residual:
+ shr ARG_1, 2, SG_CACHEPTR;
+u2_phasemis_loop:
+ and A, 0x3f, SG_COUNT;
+ cmp ARG_1, A je data_phase_finish;
+/*
+ * Subtract SG_SIZEOF from the SG_NEXT pointer and add 1 to the SG_COUNT
+ */
+ clr A;
+ add SG_NEXT[0], -SG_SIZEOF;
+ adc SG_NEXT[1], 0xff;
+ inc SG_COUNT;
+ jmp u2_phasemis_loop;
+ }
+
+data_phase_finish:
+/*
+ * After a DMA finishes, save the SG and STCNT residuals back into the SCB
+ * We use STCNT instead of HCNT, since it's a reflection of how many bytes
+ * were transferred on the SCSI (as opposed to the host) bus.
+ */
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_RESID_DCNT, STCNT, 3;
+ mov SCB_RESID_SGCNT, SG_COUNT;
+ if ((p->features & AHC_ULTRA2) != 0) {
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ }
+ } else {
+ mov SCB_RESID_DCNT[0],STCNT[0];
+ mov SCB_RESID_DCNT[1],STCNT[1];
+ mov SCB_RESID_DCNT[2],STCNT[2];
+ mov SCB_RESID_SGCNT, SG_COUNT;
+ }
+
+ jmp ITloop;
+
+data_phase_overrun:
+/*
+ * Turn off BITBUCKET mode and notify the host
+ */
+ if ((p->features & AHC_ULTRA2) != 0) {
+/*
+ * Wait for the target to quit transferring data on the SCSI bus
+ */
+ test SSTAT1, PHASEMIS jz .;
+ call ultra2_dmafinish;
+ }
+ and SXFRCTL1, ~BITBUCKET;
+ mvi INTSTAT,DATA_OVERRUN;
+ jmp ITloop;
+
+
+
+
+/*
+ * Actually turn off the DMA hardware, save our current position into the
+ * proper residual variables, wait for the next REQ signal, then jump to
+ * the ITloop. Jumping to the ITloop ensures that if we happen to get
+ * brought into the data phase again (or are still in it after our last
+ * segment) that we will properly signal an overrun to the kernel.
+ */
+ if ((p->features & AHC_ULTRA2) != 0) {
+ultra2_dmafinish:
+ test DFCNTRL, DIRECTION jnz ultra2_dmahalt;
+ and DFCNTRL, ~SCSIEN;
+ test DFCNTRL, SCSIEN jnz .;
+ if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) {
+ or DFCNTRL, FIFOFLUSH;
+ }
+ultra2_dmafifoflush:
+ if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) {
+ /*
+ * hardware bug alert! This needless set of jumps
+ * works around a glitch in the silicon. When the
+ * PCI DMA fifo goes empty, but there is still SCSI
+ * data to be flushed into the PCI DMA fifo (and from
+ * there on into main memory), the FIFOEMP bit will
+ * come on between the time when the PCI DMA buffer
+ * went empty and the next bit of data is copied from
+ * the SCSI fifo into the PCI fifo. It should only
+ * come on when both FIFOs (meaning the entire FIFO
+ * chain) are emtpy. Since it can take up to 4 cycles
+ * for new data to be copied from the SCSI fifo into
+ * the PCI fifo, testing for FIFOEMP status for 4
+ * extra times gives the needed time for any
+ * remaining SCSI fifo data to be put in the PCI fifo
+ * before we declare it *truly* empty.
+ */
+ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+ }
+ test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+ test DFSTATUS, MREQPEND jnz .;
+ultra2_dmahalt:
+ and DFCNTRL, ~(HDMAEN|SCSIEN);
+ test DFCNTRL, (HDMAEN|SCSIEN) jnz .;
+ ret;
+ }
+
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+ call assert;
+
+/*
+ * Load HADDR and HCNT.
+ */
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SCB_CMDPTR, 5;
+ bmov HCNT[1], ALLZEROS, 2;
+ if ((p->features & AHC_ULTRA2) == 0) {
+ bmov STCNT, HCNT, 3;
+ }
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_CMDPTR call bcopy_5;
+ clr HCNT[1];
+ clr HCNT[2];
+ call set_stcnt_from_hcnt;
+ }
+
+ if ((p->features & AHC_ULTRA2) == 0) {
+ mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+ } else {
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+ test SSTAT0, SDONE jnz .;
+p_command_dma_loop:
+ test SSTAT0, SDONE jnz p_command_ultra2_dma_done;
+ test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */
+p_command_ultra2_dma_done:
+ test SCSISIGI, REQI jz p_command_ultra2_shutdown;
+ test SSTAT1, (PHASEMIS|REQINIT) jz p_command_ultra2_dma_done;
+p_command_ultra2_shutdown:
+ and DFCNTRL, ~(HDMAEN|SCSIEN);
+ test DFCNTRL, (HDMAEN|SCSIEN) jnz .;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ }
+ jmp ITloop;
+
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+ call assert;
+
+ mov SCB_TARGET_STATUS, SCSIDATL;
+ jmp ITloop;
+
+/*
+ * Message out phase. If MSG_OUT is 0x80, build I full indentify message
+ * sequence and send it to the target. In addition, if the MK_MESSAGE bit
+ * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
+ * it's own message.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the hsot to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+ or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+p_mesgout:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+p_mesgout_identify:
+ if ((p->features & AHC_WIDE) != 0) {
+ and SINDEX,0xf,SCB_TCL; /* lun */
+ } else {
+ and SINDEX,0x7,SCB_TCL; /* lun */
+ }
+ and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */
+ or SINDEX,A; /* or in disconnect privledge */
+ or SINDEX,MSG_IDENTIFYFLAG;
+p_mesgout_mk_message:
+ test SCB_CONTROL,MK_MESSAGE jz p_mesgout_tag;
+ mov SCSIDATL, SINDEX; /* Send the last byte */
+ jmp p_mesgout_from_host + 1;/* Skip HOST_MSG test */
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDATL, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCB_TAG jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to send a message
+ * if it asks.
+ */
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ mvi INTSTAT,AWAITING_MSG;
+ nop;
+ /*
+ * Did the host detect a phase change?
+ */
+ cmp RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDATL, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is a message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
+
+p_mesgout_done:
+ mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
+ mov LAST_MSG, MSG_OUT;
+ cmp MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
+ and SCB_CONTROL, ~MK_MESSAGE;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
+ jmp ITloop;
+
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+ mvi ACCUM call inb_first; /* read the 1st message byte */
+
+ test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
+ cmp A,MSG_DISCONNECT je mesgin_disconnect;
+ cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
+ cmp ALLZEROS,A je mesgin_complete;
+ cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
+ cmp A,MSG_EXTENDED je mesgin_extended;
+ cmp A,MSG_MESSAGE_REJECT je mesgin_reject;
+ cmp A,MSG_NOOP je mesgin_done;
+ cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_wide_residue;
+
+rej_mesgin:
+/*
+ * We have no idea what this message in is, so we issue a message reject
+ * and hope for the best. In any case, rejection should be a rare
+ * occurrence - signal the driver when it happens.
+ */
+ mvi INTSTAT,SEND_REJECT; /* let driver know */
+
+ mvi MSG_MESSAGE_REJECT call mk_mesg;
+
+mesgin_done:
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+ jmp ITloop;
+
+
+mesgin_complete:
+/*
+ * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt. Before doing so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information. In the case of a non zero status byte, we
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved. If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command and set
+ * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload
+ * the SCB, and process it as the next command by adding it to the waiting list.
+ * If the kernel driver does not wish to request sense, it need only clear
+ * RETURN_1, and the command is allowed to complete normally. We don't bother
+ * to post to the QOUTFIFO in the error cases since it would require extra
+ * work in the kernel driver to ensure that the entry was removed before the
+ * command complete code tried processing it.
+ */
+
+/*
+ * First check for residuals
+ */
+ test SCB_RESID_SGCNT,0xff jnz upload_scb;
+ test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */
+upload_scb:
+ mvi DMAPARAMS, FIFORESET;
+ mov SCB_TAG call dma_scb;
+check_status:
+ test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */
+ mvi INTSTAT,BAD_STATUS; /* let driver know */
+ nop;
+ cmp RETURN_1, SEND_SENSE jne complete;
+ /* This SCB becomes the next to execute as it will retrieve sense */
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov SCB_TAG call dma_scb;
+add_to_waiting_list:
+ mov SCB_NEXT,WAITING_SCBH;
+ mov WAITING_SCBH, SCBPTR;
+ /*
+ * Prepare our selection hardware before the busfree so we have a
+ * high probability of winning arbitration.
+ */
+ call start_selection;
+ jmp await_busfree;
+
+complete:
+ /* If we are untagged, clear our address up in host ram */
+ test SCB_CONTROL, TAG_ENB jnz complete_post;
+ mov A, SAVED_TCL;
+ mvi UNTAGGEDSCB_OFFSET call post_byte_setup;
+ mvi SCB_LIST_NULL call post_byte;
+
+complete_post:
+ /* Post the SCB and issue an interrupt */
+ if ((p->features & AHC_QUEUE_REGS) != 0) {
+ mov A, SDSCB_QOFF;
+ } else {
+ mov A, QOUTPOS;
+ }
+ mvi QOUTFIFO_OFFSET call post_byte_setup;
+ mov SCB_TAG call post_byte;
+ if ((p->features & AHC_QUEUE_REGS) == 0) {
+ inc QOUTPOS;
+ }
+ mvi INTSTAT,CMDCMPLT;
+
+add_to_free_list:
+ call add_scb_to_free_list;
+ jmp await_busfree;
+
+/*
+ * Is it an extended message? Copy the message to our message buffer and
+ * notify the host. The host will tell us whether to reject this message,
+ * respond to it with the message that the host placed in our message buffer,
+ * or simply to do nothing.
+ */
+mesgin_extended:
+ mvi INTSTAT,EXTENDED_MSG; /* let driver know */
+ jmp ITloop;
+
+/*
+ * Is it a disconnect message? Set a flag in the SCB to remind us
+ * and await the bus going free.
+ */
+mesgin_disconnect:
+ or SCB_CONTROL,DISCONNECTED;
+ call add_scb_to_disc_list;
+ jmp await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them. This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ */
+mesgin_sdptrs:
+ test SEQ_FLAGS, DPHASE jz mesgin_done;
+ /*
+ * The SCB SGPTR becomes the next one we'll download,
+ * and the SCB DATAPTR becomes the current SHADDR.
+ * Use the residual number since STCNT is corrupted by
+ * any message transfer.
+ */
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_SGCOUNT, SG_COUNT, 5;
+ bmov SCB_DATAPTR, SHADDR, 4;
+ bmov SCB_DATACNT, SCB_RESID_DCNT, 3;
+ } else {
+ mvi DINDEX, SCB_SGCOUNT;
+ mvi SG_COUNT call bcopy_5;
+ mvi DINDEX, SCB_DATAPTR;
+ mvi SHADDR call bcopy_4;
+ mvi SCB_RESID_DCNT call bcopy_3;
+ }
+ jmp mesgin_done;
+
+/*
+ * Restore pointers message? Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
+mesgin_rdptrs:
+ and SEQ_FLAGS, ~DPHASE; /*
+ * We'll reload them
+ * the next time through
+ * the dataphase.
+ */
+ jmp mesgin_done;
+
+/*
+ * Identify message? For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+
+ if ((p->features & AHC_WIDE) != 0) {
+ and A,0x0f; /* lun in lower four bits */
+ } else {
+ and A,0x07; /* lun in lower three bits */
+ }
+ or SAVED_TCL,A; /* SAVED_TCL should be complete now */
+
+ mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */
+ call get_untagged_SCBID;
+ cmp ARG_1, SCB_LIST_NULL je snoop_tag;
+ if ((p->flags & AHC_PAGESCBS) != 0) {
+ test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB;
+ }
+ /*
+ * If the SCB was found in the disconnected list (as is
+ * always the case in non-paging scenarios), SCBPTR is already
+ * set to the correct SCB. So, simply setup the SCB and get
+ * on with things.
+ */
+ mov SCBPTR call rem_scb_from_disc_list;
+ jmp setup_SCB;
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB. With SCB paging, this requires using search for both tagged
+ * and non-tagged transactions since the SCB may exist in any slot.
+ * If we're not using SCB paging, we can use the tag as the direct
+ * index to the SCB.
+ */
+snoop_tag:
+ mov NONE,SCSIDATL; /* ACK Identify MSG */
+snoop_tag_loop:
+ call phase_lock;
+ cmp LASTPHASE, P_MESGIN jne not_found;
+ cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+ mvi ARG_1 call inb_next; /* tag value */
+
+use_retrieveSCB:
+ call retrieveSCB;
+setup_SCB:
+ mov A, SAVED_TCL;
+ cmp SCB_TCL, A jne not_found_cleanup_scb;
+ test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
+ and SCB_CONTROL,~DISCONNECTED;
+ or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ and SCB_CONTROL, ~MK_MESSAGE;
+ mvi HOST_MSG call mk_mesg;
+ jmp mesgin_done;
+
+not_found_cleanup_scb:
+ test SCB_CONTROL, DISCONNECTED jz . + 3;
+ call add_scb_to_disc_list;
+ jmp not_found;
+ call add_scb_to_free_list;
+not_found:
+ mvi INTSTAT, NO_MATCH;
+ mvi MSG_BUS_DEV_RESET call mk_mesg;
+ jmp mesgin_done;
+
+/*
+ * Message reject? Let the kernel driver handle this. If we have an
+ * outstanding WDTR or SDTR negotiation, assume that it's a response from
+ * the target selecting 8bit or asynchronous transfer, otherwise just ignore
+ * it since we have no clue what it pertains to.
+ */
+mesgin_reject:
+ mvi INTSTAT, REJECT_MSG;
+ jmp mesgin_done;
+
+/*
+ * Wide Residue. We handle the simple cases, but pass of the one hard case
+ * to the kernel (when the residue byte happened to cause us to advance our
+ * sg element array, so we know have to back that advance out).
+ */
+mesgin_wide_residue:
+ mvi ARG_1 call inb_next; /* ACK the wide_residue and get */
+ /* the size byte */
+/*
+ * In order for this to be reliable, we have to do all sorts of horrible
+ * magic in terms of resetting the datafifo and reloading the shadow layer
+ * with the correct new values (so that a subsequent save data pointers
+ * message will do the right thing). We let the kernel do that work.
+ */
+ mvi INTSTAT, WIDE_RESIDUE;
+ jmp mesgin_done;
+
+/*
+ * [ ADD MORE MESSAGE HANDLING HERE ]
+ */
+
+/*
+ * Locking the driver out, build a one-byte message passed in SINDEX
+ * if there is no active message already. SINDEX is returned intact.
+ */
+mk_mesg:
+ or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
+ mov MSG_OUT,SINDEX ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from. So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL. When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called. inb_{first,next}
+ * use the same calling convention as inb.
+ */
+
+inb_next:
+ mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
+inb_next_wait:
+ /*
+ * If there is a parity error, wait for the kernel to
+ * see the interrupt and prepare our message response
+ * before continuing.
+ */
+ test SSTAT1, REQINIT jz inb_next_wait;
+ test SSTAT1, SCSIPERR jnz .;
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+ mov DINDEX,SINDEX;
+ mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/
+inb_last:
+ mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
+
+
+mesgin_phasemis:
+/*
+ * We expected to receive another byte, but the target changed phase
+ */
+ mvi INTSTAT, MSGIN_PHASEMIS;
+ jmp ITloop;
+
+/*
+ * DMA data transfer. HADDR and HCNT must be loaded first, and
+ * SINDEX should contain the value to load DFCNTRL with - 0x3d for
+ * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
+ * during initialization.
+ */
+if ((p->features & AHC_ULTRA2) == 0) {
+dma:
+ mov DFCNTRL,SINDEX;
+dma_loop:
+ test SSTAT0,DMADONE jnz dma_dmadone;
+ test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */
+dma_phasemis:
+ test SSTAT0,SDONE jnz dma_checkfifo;
+ mov SINDEX,ALLZEROS; /* Notify caller of phasemiss */
+
+/*
+ * We will be "done" DMAing when the transfer count goes to zero, or
+ * the target changes the phase (in light of this, it makes sense that
+ * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
+ * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
+ * magically on STCNT=0 or a phase change, so just wait for FIFO empty
+ * status.
+ */
+dma_checkfifo:
+ test DFCNTRL,DIRECTION jnz dma_fifoempty;
+dma_fifoflush:
+ test DFSTATUS,FIFOEMP jz dma_fifoflush;
+
+dma_fifoempty:
+ /* Don't clobber an inprogress host data transfer */
+ test DFSTATUS, MREQPEND jnz dma_fifoempty;
+/*
+ * Now shut the DMA enables off and make sure that the DMA enables are
+ * actually off first lest we get an ILLSADDR.
+ */
+dma_dmadone:
+ cmp LASTPHASE, P_COMMAND je dma_await_nreq;
+ test SCSIRATE, 0x0f jnz dma_shutdown;
+dma_await_nreq:
+ test SCSISIGI, REQI jz dma_shutdown;
+ test SSTAT1, (PHASEMIS|REQINIT) jz dma_await_nreq;
+dma_shutdown:
+ and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+ /*
+ * Some revisions of the aic7880 have a problem where, if the
+ * data fifo is full, but the PCI input latch is not empty,
+ * HDMAEN cannot be cleared. The fix used here is to attempt
+ * to drain the data fifo until there is space for the input
+ * latch to drain and HDMAEN de-asserts.
+ */
+ if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) {
+ mov NONE, DFDAT;
+ }
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+}
+return:
+ ret;
+
+/*
+ * Assert that if we've been reselected, then we've seen an IDENTIFY
+ * message.
+ */
+assert:
+ test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */
+
+ mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */
+
+/*
+ * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
+ * or by the SCBID ARG_1. The search begins at the SCB index passed in
+ * via SINDEX which is an SCB that must be on the disconnected list. If
+ * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
+ * is set to the proper SCB.
+ */
+findSCB:
+ mov SCBPTR,SINDEX; /* Initialize SCBPTR */
+ cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID;
+ mov A, SAVED_TCL;
+ mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */
+findSCB_by_SCBID:
+ mov A, ARG_1; /* Tag passed in ARG_1 */
+ mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */
+findSCB_next:
+ mov ARG_2, SCBPTR;
+ cmp SCB_NEXT, SCB_LIST_NULL je notFound;
+ mov SCBPTR,SCB_NEXT;
+ dec SINDEX; /* Last comparison moved us too far */
+findSCB_loop:
+ cmp SINDIR, A jne findSCB_next;
+ mov SINDEX, SCBPTR ret;
+notFound:
+ mvi SINDEX, SCB_LIST_NULL ret;
+
+/*
+ * Retrieve an SCB by SCBID first searching the disconnected list falling
+ * back to DMA'ing the SCB down from the host. This routine assumes that
+ * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
+ * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL,
+ * we go directly to the host for the SCB.
+ */
+retrieveSCB:
+ test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host;
+ mov SCBPTR call findSCB; /* Continue the search */
+ cmp SINDEX, SCB_LIST_NULL je retrieve_from_host;
+
+/*
+ * This routine expects SINDEX to contain the index of the SCB to be
+ * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
+ * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
+ * if it is at the head.
+ */
+rem_scb_from_disc_list:
+/* Remove this SCB from the disconnection list */
+ cmp ARG_2, SCB_LIST_NULL je rHead;
+ mov DINDEX, SCB_NEXT;
+ mov SCBPTR, ARG_2;
+ mov SCB_NEXT, DINDEX;
+ mov SCBPTR, SINDEX ret;
+rHead:
+ mov DISCONNECTED_SCBH,SCB_NEXT ret;
+
+retrieve_from_host:
+/*
+ * We didn't find it. Pull an SCB and DMA down the one we want.
+ * We should never get here in the non-paging case.
+ */
+ mov ALLZEROS call get_free_or_disc_scb;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ /* Jump instead of call as we want to return anyway */
+ mov ARG_1 jmp dma_scb;
+
+/*
+ * Determine whether a target is using tagged or non-tagged transactions
+ * by first looking for a matching transaction based on the TCL and if
+ * that fails, looking up this device in the host's untagged SCB array.
+ * The TCL to search for is assumed to be in SAVED_TCL. The value is
+ * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
+ * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
+ * in an SCB instead of having to go to the host.
+ */
+get_untagged_SCBID:
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
+ mvi ARG_1, SCB_LIST_NULL;
+ mov DISCONNECTED_SCBH call findSCB;
+ cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host;
+ or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
+ test SCB_CONTROL, TAG_ENB jnz . + 2;
+ mov ARG_1, SCB_TAG ret;
+ mvi ARG_1, SCB_LIST_NULL ret;
+
+/*
+ * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
+ * and a base address of SCBID_ADDR. The byte is returned in RETURN_2.
+ */
+fetch_byte:
+ mov ARG_2, SINDEX;
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi CCHCNT, 1;
+ mvi CCSGCTL, CCSGEN|CCSGRESET;
+ test CCSGCTL, CCSGDONE jz .;
+ mvi CCSGCTL, CCSGRESET;
+ bmov RETURN_2, CCSGRAM, 1 ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi HCNT[0], 1;
+ clr HCNT[1];
+ clr HCNT[2];
+ mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+ call dma_finish;
+ mov RETURN_2, DFDAT ret;
+ }
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ */
+post_byte_setup:
+ mov ARG_2, SINDEX;
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi CCHCNT, 1;
+ mvi CCSCBCTL, CCSCBRESET ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi HCNT[0], 1;
+ clr HCNT[1];
+ clr HCNT[2];
+ mvi DFCNTRL, FIFORESET ret;
+ }
+
+post_byte:
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ bmov CCSCBRAM, SINDEX, 1;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL ret;
+ } else {
+ mov DFDAT, SINDEX;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+ }
+
+get_SCBID_from_host:
+ mov A, SAVED_TCL;
+ mvi UNTAGGEDSCB_OFFSET call fetch_byte;
+ mov RETURN_1, RETURN_2 ret;
+
+phase_lock:
+ test SSTAT1, REQINIT jz phase_lock;
+ test SSTAT1, SCSIPERR jnz phase_lock;
+ and SCSISIGO, PHASE_MASK, SCSISIGI;
+ and LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+if ((p->features & AHC_CMD_CHAN) == 0) {
+set_stcnt_from_hcnt:
+ mov STCNT[0], HCNT[0];
+ mov STCNT[1], HCNT[1];
+ mov STCNT[2], HCNT[2] ret;
+
+bcopy_7:
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR;
+bcopy_5:
+ mov DINDIR, SINDIR;
+bcopy_4:
+ mov DINDIR, SINDIR;
+bcopy_3:
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR;
+ mov DINDIR, SINDIR ret;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+ shr ARG_2, 3, A;
+ shl A, 5;
+/*
+ * Setup addr assuming that A + (ARG_1 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+ add DINDIR, A, SINDIR;
+ mov A, ARG_2;
+ adc DINDIR, A, SINDIR;
+ clr A;
+ adc DINDIR, A, SINDIR;
+ adc DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch and SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+ mov A, SINDEX;
+ if ((p->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi HSCB_ADDR call set_32byte_addr;
+ mov CCSCBPTR, SCBPTR;
+ mvi CCHCNT, 32;
+ test DMAPARAMS, DIRECTION jz dma_scb_tohost;
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+ jmp dma_scb_finish;
+dma_scb_tohost:
+ if ((p->features & AHC_ULTRA2) == 0) {
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov CCSCBRAM, SCB_CONTROL, 32;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ }
+ if ((p->features & AHC_ULTRA2) != 0) {
+ if ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0) {
+ mvi CCSCBCTL, CCARREN|CCSCBRESET;
+ cmp CCSCBCTL, ARRDONE|CCARREN jne .;
+ mvi CCHCNT, 32;
+ mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
+ } else {
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+ }
+ }
+dma_scb_finish:
+ clr CCSCBCTL;
+ test CCSCBCTL, CCARREN|CCSCBEN jnz .;
+ ret;
+ }
+ if ((p->features & AHC_CMD_CHAN) == 0) {
+ mvi DINDEX, HADDR;
+ mvi HSCB_ADDR call set_32byte_addr;
+ mvi HCNT[0], 32;
+ clr HCNT[1];
+ clr HCNT[2];
+ mov DFCNTRL, DMAPARAMS;
+ test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
+ /* Fill it with the SCB data */
+copy_scb_tofifo:
+ mvi SINDEX, SCB_CONTROL;
+ add A, 32, SINDEX;
+copy_scb_tofifo_loop:
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ cmp SINDEX, A jne copy_scb_tofifo_loop;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+dma_scb_fromhost:
+ mvi DINDEX, SCB_CONTROL;
+ if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) {
+ /*
+ * Set the A to -24. It it hits 0, then we let
+ * our code fall through to dfdat_in_8 to complete
+ * the last of the copy.
+ *
+ * Also, things happen 8 bytes at a time in this
+ * case, so we may need to drain the fifo at most
+ * 3 times to keep things flowing
+ */
+ mvi A, -24;
+dma_scb_hang_fifo:
+ /* Wait for the first bit of data to hit the fifo */
+ test DFSTATUS, FIFOEMP jnz .;
+dma_scb_hang_wait:
+ /* OK, now they've started to transfer into the fifo,
+ * so wait for them to stop trying to transfer any
+ * more data.
+ */
+ test DFSTATUS, MREQPEND jnz .;
+ /*
+ * OK, they started, then they stopped, now see if they
+ * managed to complete the job before stopping. Try
+ * it multiple times to give the chip a few cycles to
+ * set the flag if it did complete.
+ */
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ test DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+ /*
+ * Too bad, the chip didn't complete the DMA, but there
+ * aren't any more memory requests pending, so that
+ * means it stopped part way through and hung. That's
+ * our bug, so now we drain what data there is in the
+ * fifo in order to get things going again.
+ */
+dma_scb_hang_empty_fifo:
+ call dfdat_in_8;
+ add A, 8;
+ add SINDEX, A, HCNT;
+ /*
+ * If there are another 8 bytes of data waiting in the
+ * fifo, then the carry bit will be set as a result
+ * of the above add command (unless A is non-negative,
+ * in which case the carry bit won't be set).
+ */
+ jc dma_scb_hang_empty_fifo;
+ /*
+ * We've emptied the fifo now, but we wouldn't have got
+ * here if the memory transfer hadn't stopped part way
+ * through, so go back up to the beginning of the
+ * loop and start over. When it succeeds in getting
+ * all the data down, HDONE will be set and we'll
+ * jump to the code just below here.
+ */
+ jmp dma_scb_hang_fifo;
+dma_scb_hang_dma_done:
+ and DFCNTRL, ~HDMAEN;
+ test DFCNTRL, HDMAEN jnz .;
+ call dfdat_in_8;
+ add A, 8;
+ cmp A, 8 jne . - 2;
+ ret;
+ } else {
+ call dma_finish;
+ call dfdat_in_8;
+ call dfdat_in_8;
+ call dfdat_in_8;
+ }
+dfdat_in_8:
+ mov DINDIR,DFDAT;
+dfdat_in_7:
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT ret;
+ }
+
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+if ((p->features & AHC_CMD_CHAN) == 0) {
+dma_finish:
+ test DFSTATUS,HDONE jz dma_finish;
+ /* Turn off DMA */
+ and DFCNTRL, ~HDMAEN;
+ test DFCNTRL, HDMAEN jnz .;
+ ret;
+}
+
+add_scb_to_free_list:
+ if ((p->flags & AHC_PAGESCBS) != 0) {
+ mov SCB_NEXT, FREE_SCBH;
+ mov FREE_SCBH, SCBPTR;
+ }
+ mvi SCB_TAG, SCB_LIST_NULL ret;
+
+if ((p->flags & AHC_PAGESCBS) != 0) {
+get_free_or_disc_scb:
+ cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+ mvi SINDEX, SCB_LIST_NULL ret;
+dequeue_disc_scb:
+ mov SCBPTR, DISCONNECTED_SCBH;
+dma_up_scb:
+ mvi DMAPARAMS, FIFORESET;
+ mov SCB_TAG call dma_scb;
+unlink_disc_scb:
+ mov DISCONNECTED_SCBH, SCB_NEXT ret;
+dequeue_free_scb:
+ mov SCBPTR, FREE_SCBH;
+ mov FREE_SCBH, SCB_NEXT ret;
+}
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list. This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+ mov SCB_NEXT, DISCONNECTED_SCBH;
+ mov DISCONNECTED_SCBH, SCBPTR ret;
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx_proc.c b/drivers/scsi/aic7xxx_old/aic7xxx_proc.c
new file mode 100644
index 000000000000..3bf334931a8a
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/aic7xxx_proc.c
@@ -0,0 +1,374 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver proc support for Linux.
+ *
+ * Copyright (c) 1995, 1996 Dean W. Gehnert
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ----------------------------------------------------------------
+ * o Modified from the EATA-DMA /proc support.
+ * o Additional support for device block statistics provided by
+ * Matthew Jacob.
+ * o Correction of overflow by Heinz Mauelshagen
+ * o Adittional corrections by Doug Ledford
+ *
+ * Dean W. Gehnert, deang@teleport.com, 05/01/96
+ *
+ * $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
+ *-M*************************************************************************/
+
+#include <linux/config.h>
+
+#define BLS (&aic7xxx_buffer[size])
+#define HDRB \
+" 0 - 4K 4 - 16K 16 - 64K 64 - 256K 256K - 1M 1M+"
+
+#ifdef PROC_DEBUG
+extern int vsprintf(char *, const char *, va_list);
+
+static void
+proc_debug(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ printk(buf);
+ va_end(ap);
+}
+#else /* PROC_DEBUG */
+# define proc_debug(fmt, args...)
+#endif /* PROC_DEBUG */
+
+static int aic7xxx_buffer_size = 0;
+static char *aic7xxx_buffer = NULL;
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_info
+ *
+ * Description:
+ * Set parameters for the driver from the /proc filesystem.
+ *-F*************************************************************************/
+static int
+aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+ proc_debug("aic7xxx_set_info(): %s\n", buffer);
+ return (-ENOSYS); /* Currently this is a no-op */
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_proc_info
+ *
+ * Description:
+ * Return information to handle /proc support for the driver.
+ *-F*************************************************************************/
+int
+aic7xxx_proc_info ( struct Scsi_Host *HBAptr, char *buffer, char **start, off_t offset, int length,
+ int inout)
+{
+ struct aic7xxx_host *p;
+ struct aic_dev_data *aic_dev;
+ struct scsi_device *sdptr;
+ int size = 0;
+ unsigned char i;
+ unsigned char tindex;
+
+ for(p=first_aic7xxx; p && p->host != HBAptr; p=p->next)
+ ;
+
+ if (!p)
+ {
+ size += sprintf(buffer, "Can't find adapter for host number %d\n", HBAptr->host_no);
+ if (size > length)
+ {
+ return (size);
+ }
+ else
+ {
+ return (length);
+ }
+ }
+
+ if (inout == TRUE) /* Has data been written to the file? */
+ {
+ return (aic7xxx_set_info(buffer, length, HBAptr));
+ }
+
+ p = (struct aic7xxx_host *) HBAptr->hostdata;
+
+ /*
+ * It takes roughly 1K of space to hold all relevant card info, not
+ * counting any proc stats, so we start out with a 1.5k buffer size and
+ * if proc_stats is defined, then we sweep the stats structure to see
+ * how many drives we will be printing out for and add 384 bytes per
+ * device with active stats.
+ *
+ * Hmmmm...that 1.5k seems to keep growing as items get added so they
+ * can be easily viewed for debugging purposes. So, we bumped that
+ * 1.5k to 4k so we can quit having to bump it all the time.
+ */
+
+ size = 4096;
+ list_for_each_entry(aic_dev, &p->aic_devs, list)
+ size += 512;
+ if (aic7xxx_buffer_size != size)
+ {
+ if (aic7xxx_buffer != NULL)
+ {
+ kfree(aic7xxx_buffer);
+ aic7xxx_buffer_size = 0;
+ }
+ aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
+ }
+ if (aic7xxx_buffer == NULL)
+ {
+ size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
+ __LINE__);
+ return size;
+ }
+ aic7xxx_buffer_size = size;
+
+ size = 0;
+ size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
+ size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
+ size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Adapter Configuration:\n");
+ size += sprintf(BLS, " SCSI Adapter: %s\n",
+ board_names[p->board_name_index]);
+ if (p->flags & AHC_TWIN)
+ size += sprintf(BLS, " Twin Channel Controller ");
+ else
+ {
+ char *channel = "";
+ char *ultra = "";
+ char *wide = "Narrow ";
+ if (p->flags & AHC_MULTI_CHANNEL)
+ {
+ channel = " Channel A";
+ if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+ channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
+ }
+ if (p->features & AHC_WIDE)
+ wide = "Wide ";
+ if (p->features & AHC_ULTRA3)
+ {
+ switch(p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7892:
+ case AHC_AIC7899:
+ ultra = "Ultra-160/m LVD/SE ";
+ break;
+ default:
+ ultra = "Ultra-3 LVD/SE ";
+ break;
+ }
+ }
+ else if (p->features & AHC_ULTRA2)
+ ultra = "Ultra-2 LVD/SE ";
+ else if (p->features & AHC_ULTRA)
+ ultra = "Ultra ";
+ size += sprintf(BLS, " %s%sController%s ",
+ ultra, wide, channel);
+ }
+ switch(p->chip & ~AHC_CHIPID_MASK)
+ {
+ case AHC_VL:
+ size += sprintf(BLS, "at VLB slot %d\n", p->pci_device_fn);
+ break;
+ case AHC_EISA:
+ size += sprintf(BLS, "at EISA slot %d\n", p->pci_device_fn);
+ break;
+ default:
+ size += sprintf(BLS, "at PCI %d/%d/%d\n", p->pci_bus,
+ PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+ if( !(p->maddr) )
+ {
+ size += sprintf(BLS, " Programmed I/O Base: %lx\n", p->base);
+ }
+ else
+ {
+ size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
+ }
+ if( (p->chip & (AHC_VL | AHC_EISA)) )
+ {
+ size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address);
+ }
+ size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
+ (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
+ ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
+ "SEEPROM not found, using leftover BIOS values.") );
+ size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n",
+ (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
+ size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
+ size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n",
+ p->activescbs, p->max_activescbs);
+ size += sprintf(BLS, " Allocated %d, HW %d, "
+ "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
+ p->scb_data->maxscbs);
+ if (p->flags & AHC_EXTERNAL_SRAM)
+ size += sprintf(BLS, " Using External SCB SRAM\n");
+ size += sprintf(BLS, " Interrupts: %ld", p->isr_count);
+ if (p->chip & AHC_EISA)
+ {
+ size += sprintf(BLS, " %s\n",
+ (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
+ }
+ else
+ {
+ size += sprintf(BLS, "\n");
+ }
+ size += sprintf(BLS, " BIOS Control Word: 0x%04x\n",
+ p->bios_control);
+ size += sprintf(BLS, " Adapter Control Word: 0x%04x\n",
+ p->adapter_control);
+ size += sprintf(BLS, " Extended Translation: %sabled\n",
+ (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
+ size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
+ if (p->features & (AHC_ULTRA | AHC_ULTRA2))
+ {
+ size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb);
+ }
+ size += sprintf(BLS, "Default Tag Queue Depth: %d\n", aic7xxx_default_queue_depth);
+ size += sprintf(BLS, " Tagged Queue By Device array for aic7xxx host "
+ "instance %d:\n", p->instance);
+ size += sprintf(BLS, " {");
+ for(i=0; i < (MAX_TARGETS - 1); i++)
+ size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
+ size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
+
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Statistics:\n\n");
+ list_for_each_entry(aic_dev, &p->aic_devs, list)
+ {
+ sdptr = aic_dev->SDptr;
+ tindex = sdptr->channel << 3 | sdptr->id;
+ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+ p->host_no, sdptr->channel, sdptr->id, sdptr->lun);
+ size += sprintf(BLS, " Device using %s/%s",
+ (aic_dev->cur.width == MSG_EXT_WDTR_BUS_16_BIT) ?
+ "Wide" : "Narrow",
+ (aic_dev->cur.offset != 0) ?
+ "Sync transfers at " : "Async transfers.\n" );
+ if (aic_dev->cur.offset != 0)
+ {
+ struct aic7xxx_syncrate *sync_rate;
+ unsigned char options = aic_dev->cur.options;
+ int period = aic_dev->cur.period;
+ int rate = (aic_dev->cur.width ==
+ MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+ sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
+ if (sync_rate != NULL)
+ {
+ size += sprintf(BLS, "%s MByte/sec, offset %d\n",
+ sync_rate->rate[rate],
+ aic_dev->cur.offset );
+ }
+ else
+ {
+ size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
+ aic_dev->cur.offset );
+ }
+ }
+ size += sprintf(BLS, " Transinfo settings: ");
+ size += sprintf(BLS, "current(%d/%d/%d/%d), ",
+ aic_dev->cur.period,
+ aic_dev->cur.offset,
+ aic_dev->cur.width,
+ aic_dev->cur.options);
+ size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
+ aic_dev->goal.period,
+ aic_dev->goal.offset,
+ aic_dev->goal.width,
+ aic_dev->goal.options);
+ size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
+ p->user[tindex].period,
+ p->user[tindex].offset,
+ p->user[tindex].width,
+ p->user[tindex].options);
+ if(sdptr->simple_tags)
+ {
+ size += sprintf(BLS, " Tagged Command Queueing Enabled, Ordered Tags %s, Depth %d/%d\n", sdptr->ordered_tags ? "Enabled" : "Disabled", sdptr->queue_depth, aic_dev->max_q_depth);
+ }
+ if(aic_dev->barrier_total)
+ size += sprintf(BLS, " Total transfers %ld:\n (%ld/%ld/%ld/%ld reads/writes/REQ_BARRIER/Ordered Tags)\n",
+ aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total,
+ aic_dev->barrier_total, aic_dev->ordered_total);
+ else
+ size += sprintf(BLS, " Total transfers %ld:\n (%ld/%ld reads/writes)\n",
+ aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total);
+ size += sprintf(BLS, "%s\n", HDRB);
+ size += sprintf(BLS, " Reads:");
+ for (i = 0; i < ARRAY_SIZE(aic_dev->r_bins); i++)
+ {
+ size += sprintf(BLS, " %10ld", aic_dev->r_bins[i]);
+ }
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, " Writes:");
+ for (i = 0; i < ARRAY_SIZE(aic_dev->w_bins); i++)
+ {
+ size += sprintf(BLS, " %10ld", aic_dev->w_bins[i]);
+ }
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "\n\n");
+ }
+ if (size >= aic7xxx_buffer_size)
+ {
+ printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
+ }
+
+ if (offset > size - 1)
+ {
+ kfree(aic7xxx_buffer);
+ aic7xxx_buffer = NULL;
+ aic7xxx_buffer_size = length = 0;
+ *start = NULL;
+ }
+ else
+ {
+ *start = buffer;
+ length = min_t(int, length, size - offset);
+ memcpy(buffer, &aic7xxx_buffer[offset], length);
+ }
+
+ return (length);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx_reg.h b/drivers/scsi/aic7xxx_old/aic7xxx_reg.h
new file mode 100644
index 000000000000..27f2334abc71
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/aic7xxx_reg.h
@@ -0,0 +1,629 @@
+/*
+ * DO NOT EDIT - This file is automatically generated.
+ */
+
+#define SCSISEQ 0x00
+#define TEMODE 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+#define SXFRCTL0 0x01
+#define DFON 0x80
+#define DFPEXP 0x40
+#define FAST20 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+
+#define SXFRCTL1 0x02
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define SCSISIGO 0x03
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define SCSISIGI 0x03
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIRATE 0x04
+#define WIDEXFER 0x80
+#define SXFR_ULTRA2 0x7f
+#define SXFR 0x70
+#define SOFS 0x0f
+
+#define SCSIID 0x05
+#define SCSIOFFSET 0x05
+#define SOFS_ULTRA2 0x7f
+
+#define SCSIDATL 0x06
+
+#define SCSIDATH 0x07
+
+#define STCNT 0x08
+
+#define OPTIONMODE 0x08
+#define AUTORATEEN 0x80
+#define AUTOACKEN 0x40
+#define ATNMGMNTEN 0x20
+#define BUSFREEREV 0x10
+#define EXPPHASEDIS 0x08
+#define SCSIDATL_IMGEN 0x04
+#define AUTO_MSGOUT_DE 0x02
+#define DIS_MSGIN_DUALEDGE 0x01
+
+#define CLRSINT0 0x0b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+#define CLRSPIORDY 0x02
+
+#define SSTAT0 0x0b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define IOERR 0x08
+#define SWRAP 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define CLRSINT1 0x0c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x0c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x0d
+#define OVERRUN 0x80
+#define SHVALID 0x40
+#define WIDE_RES 0x20
+#define SFCNT 0x1f
+#define EXP_ACTIVE 0x10
+#define CRCVALERR 0x08
+#define CRCENDERR 0x04
+#define CRCREQERR 0x02
+#define DUAL_EDGE_ERROR 0x01
+
+#define SSTAT3 0x0e
+#define SCSICNT 0xf0
+#define OFFCNT 0x0f
+
+#define SCSIID_ULTRA2 0x0f
+#define OID 0x0f
+
+#define SIMODE0 0x10
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+#define SIMODE1 0x11
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+#define SCSIBUSL 0x12
+
+#define SCSIBUSH 0x13
+
+#define SHADDR 0x14
+
+#define SELTIMER 0x18
+#define STAGE6 0x20
+#define STAGE5 0x10
+#define STAGE4 0x08
+#define STAGE3 0x04
+#define STAGE2 0x02
+#define STAGE1 0x01
+
+#define SELID 0x19
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define SPIOCAP 0x1b
+#define SOFT1 0x80
+#define SOFT0 0x40
+#define SOFTCMDEN 0x20
+#define HAS_BRDCTL 0x10
+#define SEEPROM 0x08
+#define EEPROM 0x04
+#define ROM 0x02
+#define SSPIOCPS 0x01
+
+#define BRDCTL 0x1d
+#define BRDDAT7 0x80
+#define BRDDAT6 0x40
+#define BRDDAT5 0x20
+#define BRDDAT4 0x10
+#define BRDSTB 0x10
+#define BRDCS 0x08
+#define BRDDAT3 0x08
+#define BRDDAT2 0x04
+#define BRDRW 0x04
+#define BRDRW_ULTRA2 0x02
+#define BRDCTL1 0x02
+#define BRDSTB_ULTRA2 0x01
+#define BRDCTL0 0x01
+
+#define SEECTL 0x1e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+#define SBLKCTL 0x1f
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define AUTOFLUSHDIS 0x20
+#define ENAB40 0x08
+#define ENAB20 0x04
+#define SELWIDE 0x02
+#define XCVR 0x01
+
+#define SRAM_BASE 0x20
+
+#define TARG_SCSIRATE 0x20
+
+#define ULTRA_ENB 0x30
+
+#define DISC_DSB 0x32
+
+#define MSG_OUT 0x34
+
+#define DMAPARAMS 0x35
+#define PRELOADEN 0x80
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAENACK 0x10
+#define SDMAEN 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define SEQ_FLAGS 0x36
+#define IDENTIFY_SEEN 0x80
+#define SCBPTR_VALID 0x20
+#define DPHASE 0x10
+#define AMTARGET 0x08
+#define WIDE_BUS 0x02
+#define TWIN_BUS 0x01
+
+#define SAVED_TCL 0x37
+
+#define SG_COUNT 0x38
+
+#define SG_NEXT 0x39
+
+#define LASTPHASE 0x3d
+#define P_MESGIN 0xe0
+#define PHASE_MASK 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define CDI 0x80
+#define IOI 0x40
+#define P_DATAIN 0x40
+#define MSGI 0x20
+#define P_BUSFREE 0x01
+#define P_DATAOUT 0x00
+
+#define WAITING_SCBH 0x3e
+
+#define DISCONNECTED_SCBH 0x3f
+
+#define FREE_SCBH 0x40
+
+#define HSCB_ADDR 0x41
+
+#define SCBID_ADDR 0x45
+
+#define TMODE_CMDADDR 0x49
+
+#define KERNEL_QINPOS 0x4d
+
+#define QINPOS 0x4e
+
+#define QOUTPOS 0x4f
+
+#define TMODE_CMDADDR_NEXT 0x50
+
+#define ARG_1 0x51
+#define RETURN_1 0x51
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
+
+#define ARG_2 0x52
+#define RETURN_2 0x52
+
+#define LAST_MSG 0x53
+
+#define PREFETCH_CNT 0x54
+
+#define SCSICONF 0x5a
+#define TERM_ENB 0x80
+#define RESET_SCSI 0x40
+#define HWSCSIID 0x0f
+#define HSCSIID 0x07
+
+#define HOSTCONF 0x5d
+
+#define HA_274_BIOSCTRL 0x5f
+#define BIOSMODE 0x30
+#define BIOSDISABLED 0x30
+#define CHANNEL_B_PRIMARY 0x08
+
+#define SEQCTL 0x60
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+#define SEQRAM 0x61
+
+#define SEQADDR0 0x62
+
+#define SEQADDR1 0x63
+#define SEQADDR1_MASK 0x01
+
+#define ACCUM 0x64
+
+#define SINDEX 0x65
+
+#define DINDEX 0x66
+
+#define ALLONES 0x69
+
+#define ALLZEROS 0x6a
+
+#define NONE 0x6a
+
+#define FLAGS 0x6b
+#define ZERO 0x02
+#define CARRY 0x01
+
+#define SINDIR 0x6c
+
+#define DINDIR 0x6d
+
+#define FUNCTION1 0x6e
+
+#define STACK 0x6f
+
+#define TARG_OFFSET 0x70
+
+#define BCTL 0x84
+#define ACE 0x08
+#define ENABLE 0x01
+
+#define DSCOMMAND0 0x84
+#define INTSCBRAMSEL 0x08
+#define RAMPS 0x04
+#define USCBSIZE32 0x02
+#define CIOPARCKEN 0x01
+
+#define DSCOMMAND 0x84
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
+
+#define BUSTIME 0x85
+#define BOFF 0xf0
+#define BON 0x0f
+
+#define BUSSPD 0x86
+#define DFTHRSH 0xc0
+#define STBOFF 0x38
+#define STBON 0x07
+
+#define DSPCISTATUS 0x86
+#define DFTHRSH_100 0xc0
+
+#define HCNTRL 0x87
+#define POWRDN 0x40
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HADDR 0x88
+
+#define HCNT 0x8c
+
+#define SCBPTR 0x90
+
+#define INTSTAT 0x91
+#define SEQINT_MASK 0xf1
+#define DATA_OVERRUN 0xe1
+#define MSGIN_PHASEMIS 0xd1
+#define TRACEPOINT2 0xc1
+#define SEQ_SG_FIXUP 0xb1
+#define AWAITING_MSG 0xa1
+#define RESIDUAL 0x81
+#define BAD_STATUS 0x71
+#define REJECT_MSG 0x61
+#define WIDE_RESIDUE 0x51
+#define EXTENDED_MSG 0x41
+#define NO_MATCH 0x31
+#define NO_IDENT 0x21
+#define SEND_REJECT 0x11
+#define INT_PEND 0x0f
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define BAD_PHASE 0x01
+#define SEQINT 0x01
+
+#define CLRINT 0x92
+#define CLRPARERR 0x10
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define ERROR 0x92
+#define CIOPARERR 0x80
+#define PCIERRSTAT 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define DSCTMOUT 0x02
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+#define DFCNTRL 0x93
+
+#define DFSTATUS 0x94
+#define PRELOAD_AVAIL 0x80
+#define DWORDEMP 0x20
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define DFDAT 0x99
+
+#define SCBCNT 0x9a
+#define SCBAUTO 0x80
+#define SCBCNT_MASK 0x1f
+
+#define QINFIFO 0x9b
+
+#define QINCNT 0x9c
+
+#define SCSIDATL_IMG 0x9c
+
+#define QOUTFIFO 0x9d
+
+#define CRCCONTROL1 0x9d
+#define CRCONSEEN 0x80
+#define CRCVALCHKEN 0x40
+#define CRCENDCHKEN 0x20
+#define CRCREQCHKEN 0x10
+#define TARGCRCENDEN 0x08
+#define TARGCRCCNTEN 0x04
+
+#define SCSIPHASE 0x9e
+#define SP_STATUS 0x20
+#define SP_COMMAND 0x10
+#define SP_MSG_IN 0x08
+#define SP_MSG_OUT 0x04
+#define SP_DATA_IN 0x02
+#define SP_DATA_OUT 0x01
+
+#define QOUTCNT 0x9e
+
+#define SFUNCT 0x9f
+#define ALT_MODE 0x80
+
+#define SCB_CONTROL 0xa0
+#define MK_MESSAGE 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+
+#define SCB_BASE 0xa0
+
+#define SCB_TCL 0xa1
+#define TID 0xf0
+#define SELBUSB 0x08
+#define LID 0x07
+
+#define SCB_TARGET_STATUS 0xa2
+
+#define SCB_SGCOUNT 0xa3
+
+#define SCB_SGPTR 0xa4
+
+#define SCB_RESID_SGCNT 0xa8
+
+#define SCB_RESID_DCNT 0xa9
+
+#define SCB_DATAPTR 0xac
+
+#define SCB_DATACNT 0xb0
+
+#define SCB_CMDPTR 0xb4
+
+#define SCB_CMDLEN 0xb8
+
+#define SCB_TAG 0xb9
+
+#define SCB_NEXT 0xba
+
+#define SCB_PREV 0xbb
+
+#define SCB_BUSYTARGETS 0xbc
+
+#define SEECTL_2840 0xc0
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0xc1
+#define EEPROM_TF 0x80
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define DI_2840 0x01
+
+#define CCHADDR 0xe0
+
+#define CCHCNT 0xe8
+
+#define CCSGRAM 0xe9
+
+#define CCSGADDR 0xea
+
+#define CCSGCTL 0xeb
+#define CCSGDONE 0x80
+#define CCSGEN 0x08
+#define FLAG 0x02
+#define CCSGRESET 0x01
+
+#define CCSCBRAM 0xec
+
+#define CCSCBADDR 0xed
+
+#define CCSCBCTL 0xee
+#define CCSCBDONE 0x80
+#define ARRDONE 0x40
+#define CCARREN 0x10
+#define CCSCBEN 0x08
+#define CCSCBDIR 0x04
+#define CCSCBRESET 0x01
+
+#define CCSCBCNT 0xef
+
+#define CCSCBPTR 0xf1
+
+#define HNSCB_QOFF 0xf4
+
+#define HESCB_QOFF 0xf5
+
+#define SNSCB_QOFF 0xf6
+
+#define SESCB_QOFF 0xf7
+
+#define SDSCB_QOFF 0xf8
+
+#define QOFF_CTLSTA 0xfa
+#define ESTABLISH_SCB_AVAIL 0x80
+#define SCB_AVAIL 0x40
+#define SNSCB_ROLLOVER 0x20
+#define SDSCB_ROLLOVER 0x10
+#define SESCB_ROLLOVER 0x08
+#define SCB_QSIZE 0x07
+#define SCB_QSIZE_256 0x06
+
+#define DFF_THRSH 0xfb
+#define WR_DFTHRSH 0x70
+#define WR_DFTHRSH_MAX 0x70
+#define WR_DFTHRSH_90 0x60
+#define WR_DFTHRSH_85 0x50
+#define WR_DFTHRSH_75 0x40
+#define WR_DFTHRSH_63 0x30
+#define WR_DFTHRSH_50 0x20
+#define WR_DFTHRSH_25 0x10
+#define RD_DFTHRSH_MAX 0x07
+#define RD_DFTHRSH 0x07
+#define RD_DFTHRSH_90 0x06
+#define RD_DFTHRSH_85 0x05
+#define RD_DFTHRSH_75 0x04
+#define RD_DFTHRSH_63 0x03
+#define RD_DFTHRSH_50 0x02
+#define RD_DFTHRSH_25 0x01
+#define WR_DFTHRSH_MIN 0x00
+#define RD_DFTHRSH_MIN 0x00
+
+#define SG_CACHEPTR 0xfc
+#define SG_USER_DATA 0xfc
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+
+#define CMD_GROUP2_BYTE_DELTA 0xfa
+#define MAX_OFFSET_8BIT 0x0f
+#define BUS_16_BIT 0x01
+#define QINFIFO_OFFSET 0x02
+#define CMD_GROUP5_BYTE_DELTA 0x0b
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define MAX_OFFSET_ULTRA2 0x7f
+#define MAX_OFFSET_16BIT 0x08
+#define BUS_8_BIT 0x00
+#define QOUTFIFO_OFFSET 0x01
+#define UNTAGGEDSCB_OFFSET 0x00
+#define CCSGRAM_MAXSEGS 0x10
+#define SCB_LIST_NULL 0xff
+#define SG_SIZEOF 0x08
+#define CMD_GROUP4_BYTE_DELTA 0x04
+#define CMD_GROUP0_BYTE_DELTA 0xfc
+#define HOST_MSG 0xff
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+
+
+/* Downloaded Constant Definitions */
+#define TMODE_NUMCMDS 0x00
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx_seq.c b/drivers/scsi/aic7xxx_old/aic7xxx_seq.c
new file mode 100644
index 000000000000..e1bc140e9735
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/aic7xxx_seq.c
@@ -0,0 +1,817 @@
+/*
+ * DO NOT EDIT - This file is automatically generated.
+ */
+static unsigned char seqprog[] = {
+ 0xff, 0x6a, 0x06, 0x08,
+ 0x7f, 0x02, 0x04, 0x08,
+ 0x12, 0x6a, 0x00, 0x00,
+ 0xff, 0x6a, 0xd6, 0x09,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x00, 0x65, 0xca, 0x58,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0xff, 0x4e, 0xc8, 0x08,
+ 0xbf, 0x60, 0xc0, 0x08,
+ 0x60, 0x0b, 0x86, 0x68,
+ 0x40, 0x00, 0x0c, 0x68,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0x60, 0x0b, 0x86, 0x68,
+ 0x40, 0x00, 0x0c, 0x68,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0xff, 0x3e, 0x48, 0x60,
+ 0x40, 0xfa, 0x10, 0x78,
+ 0xff, 0xf6, 0xd4, 0x08,
+ 0x01, 0x4e, 0x9c, 0x18,
+ 0x40, 0x60, 0xc0, 0x00,
+ 0x00, 0x4d, 0x10, 0x70,
+ 0x01, 0x4e, 0x9c, 0x18,
+ 0xbf, 0x60, 0xc0, 0x08,
+ 0x00, 0x6a, 0x86, 0x5c,
+ 0xff, 0x4e, 0xc8, 0x18,
+ 0x02, 0x6a, 0x70, 0x5b,
+ 0xff, 0x52, 0x20, 0x09,
+ 0x0d, 0x6a, 0x6a, 0x00,
+ 0x00, 0x52, 0xe6, 0x5b,
+ 0x03, 0xb0, 0x52, 0x31,
+ 0xff, 0xb0, 0x52, 0x09,
+ 0xff, 0xb1, 0x54, 0x09,
+ 0xff, 0xb2, 0x56, 0x09,
+ 0xff, 0xa3, 0x50, 0x09,
+ 0xff, 0x3e, 0x74, 0x09,
+ 0xff, 0x90, 0x7c, 0x08,
+ 0xff, 0x3e, 0x20, 0x09,
+ 0x00, 0x65, 0x4e, 0x58,
+ 0x00, 0x65, 0x0c, 0x40,
+ 0xf7, 0x1f, 0xca, 0x08,
+ 0x08, 0xa1, 0xc8, 0x08,
+ 0x00, 0x65, 0xca, 0x00,
+ 0xff, 0x65, 0x3e, 0x08,
+ 0xf0, 0xa1, 0xc8, 0x08,
+ 0x0f, 0x0f, 0x1e, 0x08,
+ 0x00, 0x0f, 0x1e, 0x00,
+ 0xf0, 0xa1, 0xc8, 0x08,
+ 0x0f, 0x05, 0x0a, 0x08,
+ 0x00, 0x05, 0x0a, 0x00,
+ 0xff, 0x6a, 0x0c, 0x08,
+ 0x5a, 0x6a, 0x00, 0x04,
+ 0x12, 0x65, 0x02, 0x00,
+ 0x31, 0x6a, 0xca, 0x00,
+ 0x80, 0x37, 0x6e, 0x68,
+ 0xff, 0x65, 0xca, 0x18,
+ 0xff, 0x37, 0xdc, 0x08,
+ 0xff, 0x6e, 0xc8, 0x08,
+ 0x00, 0x6c, 0x76, 0x78,
+ 0x20, 0x01, 0x02, 0x00,
+ 0x4c, 0x37, 0xc8, 0x28,
+ 0x08, 0x1f, 0x7e, 0x78,
+ 0x08, 0x37, 0x6e, 0x00,
+ 0x08, 0x64, 0xc8, 0x00,
+ 0x70, 0x64, 0xca, 0x18,
+ 0xff, 0x6c, 0x0a, 0x08,
+ 0x20, 0x64, 0xca, 0x18,
+ 0xff, 0x6c, 0x08, 0x0c,
+ 0x40, 0x0b, 0x96, 0x68,
+ 0x20, 0x6a, 0x16, 0x00,
+ 0xf0, 0x19, 0x6e, 0x08,
+ 0x08, 0x6a, 0x18, 0x00,
+ 0x08, 0x11, 0x22, 0x00,
+ 0x08, 0x6a, 0x66, 0x58,
+ 0x08, 0x6a, 0x68, 0x00,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x12, 0x6a, 0x00, 0x00,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0xff, 0x3e, 0x20, 0x09,
+ 0xff, 0xba, 0x7c, 0x08,
+ 0xff, 0xa1, 0x6e, 0x08,
+ 0x08, 0x6a, 0x18, 0x00,
+ 0x08, 0x11, 0x22, 0x00,
+ 0x08, 0x6a, 0x66, 0x58,
+ 0x80, 0x6a, 0x68, 0x00,
+ 0x80, 0x36, 0x6c, 0x00,
+ 0x00, 0x65, 0xba, 0x5b,
+ 0xff, 0x3d, 0xc8, 0x08,
+ 0xbf, 0x64, 0xe2, 0x78,
+ 0x80, 0x64, 0xc8, 0x71,
+ 0xa0, 0x64, 0xf8, 0x71,
+ 0xc0, 0x64, 0xf0, 0x71,
+ 0xe0, 0x64, 0x38, 0x72,
+ 0x01, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0x00, 0x65, 0xca, 0x58,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x09, 0x0c, 0xc4, 0x78,
+ 0x08, 0x0c, 0x0c, 0x68,
+ 0x01, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0x26, 0x09,
+ 0x02, 0x6a, 0x08, 0x30,
+ 0xff, 0x6a, 0x08, 0x08,
+ 0xdf, 0x01, 0x02, 0x08,
+ 0x01, 0x6a, 0x7a, 0x00,
+ 0xff, 0x6a, 0x6c, 0x0c,
+ 0x04, 0x14, 0x10, 0x31,
+ 0x03, 0xa9, 0x18, 0x31,
+ 0x03, 0xa9, 0x10, 0x30,
+ 0x08, 0x6a, 0xcc, 0x00,
+ 0xa9, 0x6a, 0xd0, 0x5b,
+ 0x00, 0x65, 0x02, 0x41,
+ 0xa8, 0x6a, 0x6a, 0x00,
+ 0x79, 0x6a, 0x6a, 0x00,
+ 0x40, 0x3d, 0xea, 0x68,
+ 0x04, 0x35, 0x6a, 0x00,
+ 0x00, 0x65, 0x2a, 0x5b,
+ 0x80, 0x6a, 0xd4, 0x01,
+ 0x10, 0x36, 0xd6, 0x68,
+ 0x10, 0x36, 0x6c, 0x00,
+ 0x07, 0xac, 0x10, 0x31,
+ 0x05, 0xa3, 0x70, 0x30,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xac, 0x6a, 0xc8, 0x5b,
+ 0x00, 0x65, 0xc2, 0x5b,
+ 0x38, 0x6a, 0xcc, 0x00,
+ 0xa3, 0x6a, 0xcc, 0x5b,
+ 0xff, 0x38, 0x12, 0x69,
+ 0x80, 0x02, 0x04, 0x00,
+ 0xe7, 0x35, 0x6a, 0x08,
+ 0x03, 0x69, 0x18, 0x31,
+ 0x03, 0x69, 0x10, 0x30,
+ 0xff, 0x6a, 0x10, 0x00,
+ 0xff, 0x6a, 0x12, 0x00,
+ 0xff, 0x6a, 0x14, 0x00,
+ 0x22, 0x38, 0xc8, 0x28,
+ 0x01, 0x38, 0x1c, 0x61,
+ 0x02, 0x64, 0xc8, 0x00,
+ 0x01, 0x38, 0x1c, 0x61,
+ 0xbf, 0x35, 0x6a, 0x08,
+ 0xff, 0x64, 0xf8, 0x09,
+ 0xff, 0x35, 0x26, 0x09,
+ 0x80, 0x02, 0xa4, 0x69,
+ 0x10, 0x0c, 0x7a, 0x69,
+ 0x80, 0x94, 0x22, 0x79,
+ 0x00, 0x35, 0x0a, 0x5b,
+ 0x80, 0x02, 0xa4, 0x69,
+ 0xff, 0x65, 0x94, 0x79,
+ 0x01, 0x38, 0x70, 0x71,
+ 0xff, 0x38, 0x70, 0x18,
+ 0xff, 0x38, 0x94, 0x79,
+ 0x80, 0xea, 0x4a, 0x61,
+ 0xef, 0x38, 0xc8, 0x18,
+ 0x80, 0x6a, 0xc8, 0x00,
+ 0x00, 0x65, 0x3c, 0x49,
+ 0x33, 0x38, 0xc8, 0x28,
+ 0xff, 0x64, 0xd0, 0x09,
+ 0x04, 0x39, 0xc0, 0x31,
+ 0x09, 0x6a, 0xd6, 0x01,
+ 0x80, 0xeb, 0x42, 0x79,
+ 0xf7, 0xeb, 0xd6, 0x09,
+ 0x08, 0xeb, 0x46, 0x69,
+ 0x01, 0x6a, 0xd6, 0x01,
+ 0x08, 0xe9, 0x10, 0x31,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0xff, 0x38, 0x70, 0x18,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x39, 0x6a, 0xce, 0x5b,
+ 0x08, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x0d, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x78, 0x5c,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x00, 0x65, 0x6a, 0x5c,
+ 0x00, 0x65, 0xc2, 0x5b,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x08, 0x39, 0x72, 0x18,
+ 0x00, 0x3a, 0x74, 0x20,
+ 0x00, 0x65, 0x02, 0x41,
+ 0x01, 0x0c, 0x6c, 0x79,
+ 0x10, 0x0c, 0x02, 0x79,
+ 0x10, 0x0c, 0x7a, 0x69,
+ 0x01, 0xfc, 0x70, 0x79,
+ 0xff, 0x6a, 0x70, 0x08,
+ 0x01, 0x0c, 0x76, 0x79,
+ 0x10, 0x0c, 0x02, 0x79,
+ 0x00, 0x65, 0xae, 0x59,
+ 0x01, 0xfc, 0x94, 0x69,
+ 0x40, 0x0d, 0x84, 0x69,
+ 0xb1, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x94, 0x41,
+ 0x2e, 0xfc, 0xa2, 0x28,
+ 0x3f, 0x38, 0xc8, 0x08,
+ 0x00, 0x51, 0x94, 0x71,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0xf8, 0x39, 0x72, 0x18,
+ 0xff, 0x3a, 0x74, 0x20,
+ 0x01, 0x38, 0x70, 0x18,
+ 0x00, 0x65, 0x86, 0x41,
+ 0x03, 0x08, 0x52, 0x31,
+ 0xff, 0x38, 0x50, 0x09,
+ 0x12, 0x01, 0x02, 0x00,
+ 0xff, 0x08, 0x52, 0x09,
+ 0xff, 0x09, 0x54, 0x09,
+ 0xff, 0x0a, 0x56, 0x09,
+ 0xff, 0x38, 0x50, 0x09,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x10, 0x0c, 0xa4, 0x79,
+ 0x00, 0x65, 0xae, 0x59,
+ 0x7f, 0x02, 0x04, 0x08,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x04, 0x93, 0xc2, 0x69,
+ 0xdf, 0x93, 0x26, 0x09,
+ 0x20, 0x93, 0xb2, 0x69,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x01, 0x94, 0xb6, 0x79,
+ 0x01, 0x94, 0xb6, 0x79,
+ 0x01, 0x94, 0xb6, 0x79,
+ 0x01, 0x94, 0xb6, 0x79,
+ 0x01, 0x94, 0xb6, 0x79,
+ 0x10, 0x94, 0xc0, 0x69,
+ 0xd7, 0x93, 0x26, 0x09,
+ 0x28, 0x93, 0xc4, 0x69,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x00, 0x65, 0x2a, 0x5b,
+ 0x05, 0xb4, 0x10, 0x31,
+ 0x02, 0x6a, 0x1a, 0x31,
+ 0x03, 0x8c, 0x10, 0x30,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xb4, 0x6a, 0xcc, 0x5b,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x00, 0x65, 0xc2, 0x5b,
+ 0x3d, 0x6a, 0x0a, 0x5b,
+ 0xac, 0x6a, 0x26, 0x01,
+ 0x04, 0x0b, 0xde, 0x69,
+ 0x04, 0x0b, 0xe4, 0x69,
+ 0x10, 0x0c, 0xe0, 0x79,
+ 0x02, 0x03, 0xe8, 0x79,
+ 0x11, 0x0c, 0xe4, 0x79,
+ 0xd7, 0x93, 0x26, 0x09,
+ 0x28, 0x93, 0xea, 0x69,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x00, 0x65, 0x2a, 0x5b,
+ 0xff, 0x06, 0x44, 0x09,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x10, 0x3d, 0x06, 0x00,
+ 0xff, 0x34, 0xca, 0x08,
+ 0x80, 0x65, 0x1c, 0x62,
+ 0x0f, 0xa1, 0xca, 0x08,
+ 0x07, 0xa1, 0xca, 0x08,
+ 0x40, 0xa0, 0xc8, 0x08,
+ 0x00, 0x65, 0xca, 0x00,
+ 0x80, 0x65, 0xca, 0x00,
+ 0x80, 0xa0, 0x0c, 0x7a,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x00, 0x65, 0x1e, 0x42,
+ 0x20, 0xa0, 0x24, 0x7a,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x00, 0x65, 0xba, 0x5b,
+ 0xa0, 0x3d, 0x2c, 0x62,
+ 0x23, 0xa0, 0x0c, 0x08,
+ 0x00, 0x65, 0xba, 0x5b,
+ 0xa0, 0x3d, 0x2c, 0x62,
+ 0x00, 0xb9, 0x24, 0x42,
+ 0xff, 0x65, 0x24, 0x62,
+ 0xa1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x10, 0x51, 0x2c, 0x72,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x00, 0x65, 0xba, 0x5b,
+ 0xa0, 0x3d, 0xf6, 0x71,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0xff, 0x34, 0xa6, 0x08,
+ 0x80, 0x34, 0x34, 0x62,
+ 0x7f, 0xa0, 0x40, 0x09,
+ 0x08, 0x6a, 0x68, 0x00,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x64, 0x6a, 0x00, 0x5b,
+ 0x80, 0x64, 0xaa, 0x6a,
+ 0x04, 0x64, 0x8c, 0x72,
+ 0x02, 0x64, 0x92, 0x72,
+ 0x00, 0x6a, 0x54, 0x72,
+ 0x03, 0x64, 0xa6, 0x72,
+ 0x01, 0x64, 0x88, 0x72,
+ 0x07, 0x64, 0xe8, 0x72,
+ 0x08, 0x64, 0x50, 0x72,
+ 0x23, 0x64, 0xec, 0x72,
+ 0x11, 0x6a, 0x22, 0x01,
+ 0x07, 0x6a, 0xf2, 0x5a,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0xff, 0xa8, 0x58, 0x6a,
+ 0xff, 0xa2, 0x70, 0x7a,
+ 0x01, 0x6a, 0x6a, 0x00,
+ 0x00, 0xb9, 0xe6, 0x5b,
+ 0xff, 0xa2, 0x70, 0x7a,
+ 0x71, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x40, 0x51, 0x70, 0x62,
+ 0x0d, 0x6a, 0x6a, 0x00,
+ 0x00, 0xb9, 0xe6, 0x5b,
+ 0xff, 0x3e, 0x74, 0x09,
+ 0xff, 0x90, 0x7c, 0x08,
+ 0x00, 0x65, 0x4e, 0x58,
+ 0x00, 0x65, 0xbc, 0x40,
+ 0x20, 0xa0, 0x78, 0x6a,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0x00, 0x6a, 0x90, 0x5b,
+ 0xff, 0x6a, 0xa6, 0x5b,
+ 0xff, 0xf8, 0xc8, 0x08,
+ 0xff, 0x4f, 0xc8, 0x08,
+ 0x01, 0x6a, 0x90, 0x5b,
+ 0x00, 0xb9, 0xa6, 0x5b,
+ 0x01, 0x4f, 0x9e, 0x18,
+ 0x02, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x80, 0x5c,
+ 0x00, 0x65, 0xbc, 0x40,
+ 0x41, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0x04, 0xa0, 0x40, 0x01,
+ 0x00, 0x65, 0x98, 0x5c,
+ 0x00, 0x65, 0xbc, 0x40,
+ 0x10, 0x36, 0x50, 0x7a,
+ 0x05, 0x38, 0x46, 0x31,
+ 0x04, 0x14, 0x58, 0x31,
+ 0x03, 0xa9, 0x60, 0x31,
+ 0xa3, 0x6a, 0xcc, 0x00,
+ 0x38, 0x6a, 0xcc, 0x5b,
+ 0xac, 0x6a, 0xcc, 0x00,
+ 0x14, 0x6a, 0xce, 0x5b,
+ 0xa9, 0x6a, 0xd0, 0x5b,
+ 0x00, 0x65, 0x50, 0x42,
+ 0xef, 0x36, 0x6c, 0x08,
+ 0x00, 0x65, 0x50, 0x42,
+ 0x0f, 0x64, 0xc8, 0x08,
+ 0x07, 0x64, 0xc8, 0x08,
+ 0x00, 0x37, 0x6e, 0x00,
+ 0xff, 0x6a, 0xa4, 0x00,
+ 0x00, 0x65, 0x60, 0x5b,
+ 0xff, 0x51, 0xbc, 0x72,
+ 0x20, 0x36, 0xc6, 0x7a,
+ 0x00, 0x90, 0x4e, 0x5b,
+ 0x00, 0x65, 0xc8, 0x42,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0x00, 0x65, 0xba, 0x5b,
+ 0xe0, 0x3d, 0xe2, 0x62,
+ 0x20, 0x12, 0xe2, 0x62,
+ 0x51, 0x6a, 0xf6, 0x5a,
+ 0x00, 0x65, 0x48, 0x5b,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0x00, 0xa1, 0xda, 0x62,
+ 0x04, 0xa0, 0xda, 0x7a,
+ 0xfb, 0xa0, 0x40, 0x09,
+ 0x80, 0x36, 0x6c, 0x00,
+ 0x80, 0xa0, 0x50, 0x7a,
+ 0x7f, 0xa0, 0x40, 0x09,
+ 0xff, 0x6a, 0xf2, 0x5a,
+ 0x00, 0x65, 0x50, 0x42,
+ 0x04, 0xa0, 0xe0, 0x7a,
+ 0x00, 0x65, 0x98, 0x5c,
+ 0x00, 0x65, 0xe2, 0x42,
+ 0x00, 0x65, 0x80, 0x5c,
+ 0x31, 0x6a, 0x22, 0x01,
+ 0x0c, 0x6a, 0xf2, 0x5a,
+ 0x00, 0x65, 0x50, 0x42,
+ 0x61, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x50, 0x42,
+ 0x51, 0x6a, 0xf6, 0x5a,
+ 0x51, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x50, 0x42,
+ 0x10, 0x3d, 0x06, 0x00,
+ 0xff, 0x65, 0x68, 0x0c,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0x01, 0x0c, 0xf8, 0x7a,
+ 0x04, 0x0c, 0xfa, 0x6a,
+ 0xe0, 0x03, 0x7a, 0x08,
+ 0xe0, 0x3d, 0x06, 0x63,
+ 0xff, 0x65, 0xcc, 0x08,
+ 0xff, 0x12, 0xda, 0x0c,
+ 0xff, 0x06, 0xd4, 0x0c,
+ 0xd1, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0xaa, 0x40,
+ 0xff, 0x65, 0x26, 0x09,
+ 0x01, 0x0b, 0x1a, 0x6b,
+ 0x10, 0x0c, 0x0c, 0x7b,
+ 0x04, 0x0b, 0x14, 0x6b,
+ 0xff, 0x6a, 0xca, 0x08,
+ 0x04, 0x93, 0x18, 0x6b,
+ 0x01, 0x94, 0x16, 0x7b,
+ 0x10, 0x94, 0x18, 0x6b,
+ 0x80, 0x3d, 0x1e, 0x73,
+ 0x0f, 0x04, 0x22, 0x6b,
+ 0x02, 0x03, 0x22, 0x7b,
+ 0x11, 0x0c, 0x1e, 0x7b,
+ 0xc7, 0x93, 0x26, 0x09,
+ 0xff, 0x99, 0xd4, 0x08,
+ 0x38, 0x93, 0x24, 0x6b,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x80, 0x36, 0x28, 0x6b,
+ 0x21, 0x6a, 0x22, 0x05,
+ 0xff, 0x65, 0x20, 0x09,
+ 0xff, 0x51, 0x36, 0x63,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0xa1, 0x6a, 0x42, 0x43,
+ 0xff, 0x51, 0xc8, 0x08,
+ 0xb9, 0x6a, 0x42, 0x43,
+ 0xff, 0x90, 0xa4, 0x08,
+ 0xff, 0xba, 0x46, 0x73,
+ 0xff, 0xba, 0x20, 0x09,
+ 0xff, 0x65, 0xca, 0x18,
+ 0x00, 0x6c, 0x3a, 0x63,
+ 0xff, 0x90, 0xca, 0x0c,
+ 0xff, 0x6a, 0xca, 0x04,
+ 0x20, 0x36, 0x5a, 0x7b,
+ 0x00, 0x90, 0x2e, 0x5b,
+ 0xff, 0x65, 0x5a, 0x73,
+ 0xff, 0x52, 0x58, 0x73,
+ 0xff, 0xba, 0xcc, 0x08,
+ 0xff, 0x52, 0x20, 0x09,
+ 0xff, 0x66, 0x74, 0x09,
+ 0xff, 0x65, 0x20, 0x0d,
+ 0xff, 0xba, 0x7e, 0x0c,
+ 0x00, 0x6a, 0x86, 0x5c,
+ 0x0d, 0x6a, 0x6a, 0x00,
+ 0x00, 0x51, 0xe6, 0x43,
+ 0xff, 0x3f, 0xb4, 0x73,
+ 0xff, 0x6a, 0xa2, 0x00,
+ 0x00, 0x3f, 0x2e, 0x5b,
+ 0xff, 0x65, 0xb4, 0x73,
+ 0x20, 0x36, 0x6c, 0x00,
+ 0x20, 0xa0, 0x6e, 0x6b,
+ 0xff, 0xb9, 0xa2, 0x0c,
+ 0xff, 0x6a, 0xa2, 0x04,
+ 0xff, 0x65, 0xa4, 0x08,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xda, 0x5b,
+ 0x01, 0x6a, 0xd0, 0x01,
+ 0x09, 0x6a, 0xd6, 0x01,
+ 0x80, 0xeb, 0x7a, 0x7b,
+ 0x01, 0x6a, 0xd6, 0x01,
+ 0x01, 0xe9, 0xa4, 0x34,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xda, 0x5b,
+ 0x01, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x0d, 0x6a, 0x26, 0x01,
+ 0x00, 0x65, 0x78, 0x5c,
+ 0xff, 0x99, 0xa4, 0x0c,
+ 0xff, 0x65, 0xa4, 0x08,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xda, 0x5b,
+ 0x01, 0x6a, 0xd0, 0x01,
+ 0x01, 0x6a, 0xdc, 0x05,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xda, 0x5b,
+ 0x01, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x01, 0x6a, 0x26, 0x05,
+ 0x01, 0x65, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xaa, 0x7b,
+ 0xff, 0x6a, 0xdc, 0x0d,
+ 0xff, 0x65, 0x32, 0x09,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x78, 0x44,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0x00, 0x6a, 0x70, 0x5b,
+ 0xff, 0x52, 0xa2, 0x0c,
+ 0x01, 0x0c, 0xba, 0x7b,
+ 0x04, 0x0c, 0xba, 0x6b,
+ 0xe0, 0x03, 0x06, 0x08,
+ 0xe0, 0x03, 0x7a, 0x0c,
+ 0xff, 0x8c, 0x10, 0x08,
+ 0xff, 0x8d, 0x12, 0x08,
+ 0xff, 0x8e, 0x14, 0x0c,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x0c,
+ 0x3d, 0x64, 0xa4, 0x28,
+ 0x55, 0x64, 0xc8, 0x28,
+ 0x00, 0x6c, 0xda, 0x18,
+ 0xff, 0x52, 0xc8, 0x08,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0x00, 0x6c, 0xda, 0x24,
+ 0xff, 0x65, 0xc8, 0x08,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x41, 0x6a, 0xd6, 0x5b,
+ 0xff, 0x90, 0xe2, 0x09,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x04, 0x35, 0xf8, 0x7b,
+ 0x1d, 0x6a, 0xdc, 0x01,
+ 0xdc, 0xee, 0xf4, 0x63,
+ 0x00, 0x65, 0x0e, 0x44,
+ 0x01, 0x6a, 0xdc, 0x01,
+ 0x20, 0xa0, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xfe, 0x7b,
+ 0x11, 0x6a, 0xdc, 0x01,
+ 0x50, 0xee, 0x02, 0x64,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x09, 0x6a, 0xdc, 0x01,
+ 0x88, 0xee, 0x08, 0x64,
+ 0x19, 0x6a, 0xdc, 0x01,
+ 0xd8, 0xee, 0x0c, 0x64,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x18, 0xee, 0x10, 0x6c,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x41, 0x6a, 0xd6, 0x5b,
+ 0x20, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0xff, 0x35, 0x26, 0x09,
+ 0x04, 0x35, 0x3c, 0x6c,
+ 0xa0, 0x6a, 0xca, 0x00,
+ 0x20, 0x65, 0xc8, 0x18,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0x00, 0x65, 0x26, 0x64,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x78, 0x44,
+ 0xa0, 0x6a, 0xcc, 0x00,
+ 0xe8, 0x6a, 0xc8, 0x00,
+ 0x01, 0x94, 0x40, 0x6c,
+ 0x10, 0x94, 0x42, 0x6c,
+ 0x08, 0x94, 0x54, 0x6c,
+ 0x08, 0x94, 0x54, 0x6c,
+ 0x08, 0x94, 0x54, 0x6c,
+ 0x00, 0x65, 0x68, 0x5c,
+ 0x08, 0x64, 0xc8, 0x18,
+ 0x00, 0x8c, 0xca, 0x18,
+ 0x00, 0x65, 0x4a, 0x4c,
+ 0x00, 0x65, 0x40, 0x44,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0x56, 0x6c,
+ 0x00, 0x65, 0x68, 0x5c,
+ 0x08, 0x64, 0xc8, 0x18,
+ 0x08, 0x64, 0x58, 0x64,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x00, 0x65, 0x78, 0x5c,
+ 0x00, 0x65, 0x68, 0x5c,
+ 0x00, 0x65, 0x68, 0x5c,
+ 0x00, 0x65, 0x68, 0x5c,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x0c,
+ 0x08, 0x94, 0x78, 0x7c,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0x7c, 0x6c,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0xff, 0x40, 0x74, 0x09,
+ 0xff, 0x90, 0x80, 0x08,
+ 0xff, 0x6a, 0x72, 0x05,
+ 0xff, 0x40, 0x94, 0x64,
+ 0xff, 0x3f, 0x8c, 0x64,
+ 0xff, 0x6a, 0xca, 0x04,
+ 0xff, 0x3f, 0x20, 0x09,
+ 0x01, 0x6a, 0x6a, 0x00,
+ 0x00, 0xb9, 0xe6, 0x5b,
+ 0xff, 0xba, 0x7e, 0x0c,
+ 0xff, 0x40, 0x20, 0x09,
+ 0xff, 0xba, 0x80, 0x0c,
+ 0xff, 0x3f, 0x74, 0x09,
+ 0xff, 0x90, 0x7e, 0x0c,
+};
+
+static int aic7xxx_patch15_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch15_func(struct aic7xxx_host *p)
+{
+ return ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0);
+}
+
+static int aic7xxx_patch14_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch14_func(struct aic7xxx_host *p)
+{
+ return ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0);
+}
+
+static int aic7xxx_patch13_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch13_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_WIDE) != 0);
+}
+
+static int aic7xxx_patch12_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch12_func(struct aic7xxx_host *p)
+{
+ return ((p->bugs & AHC_BUG_AUTOFLUSH) != 0);
+}
+
+static int aic7xxx_patch11_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch11_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_ULTRA2) == 0);
+}
+
+static int aic7xxx_patch10_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch10_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_CMD_CHAN) == 0);
+}
+
+static int aic7xxx_patch9_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch9_func(struct aic7xxx_host *p)
+{
+ return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
+static int aic7xxx_patch8_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch8_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_ULTRA) != 0);
+}
+
+static int aic7xxx_patch7_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch7_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_ULTRA2) != 0);
+}
+
+static int aic7xxx_patch6_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch6_func(struct aic7xxx_host *p)
+{
+ return ((p->flags & AHC_PAGESCBS) == 0);
+}
+
+static int aic7xxx_patch5_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch5_func(struct aic7xxx_host *p)
+{
+ return ((p->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic7xxx_patch4_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch4_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic7xxx_patch3_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch3_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_TWIN) != 0);
+}
+
+static int aic7xxx_patch2_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch2_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic7xxx_patch1_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch1_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic7xxx_patch0_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch0_func(struct aic7xxx_host *p)
+{
+ return (0);
+}
+
+struct sequencer_patch {
+ int (*patch_func)(struct aic7xxx_host *);
+ unsigned int begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} sequencer_patches[] = {
+ { aic7xxx_patch1_func, 3, 2, 1 },
+ { aic7xxx_patch2_func, 7, 1, 1 },
+ { aic7xxx_patch2_func, 8, 1, 1 },
+ { aic7xxx_patch3_func, 11, 4, 1 },
+ { aic7xxx_patch4_func, 16, 3, 2 },
+ { aic7xxx_patch0_func, 19, 4, 1 },
+ { aic7xxx_patch5_func, 23, 1, 1 },
+ { aic7xxx_patch6_func, 26, 1, 1 },
+ { aic7xxx_patch1_func, 29, 1, 2 },
+ { aic7xxx_patch0_func, 30, 3, 1 },
+ { aic7xxx_patch3_func, 39, 4, 1 },
+ { aic7xxx_patch7_func, 43, 3, 2 },
+ { aic7xxx_patch0_func, 46, 3, 1 },
+ { aic7xxx_patch8_func, 52, 7, 1 },
+ { aic7xxx_patch3_func, 60, 3, 1 },
+ { aic7xxx_patch7_func, 63, 2, 1 },
+ { aic7xxx_patch7_func, 102, 1, 2 },
+ { aic7xxx_patch0_func, 103, 2, 1 },
+ { aic7xxx_patch7_func, 107, 2, 1 },
+ { aic7xxx_patch9_func, 109, 1, 1 },
+ { aic7xxx_patch10_func, 110, 2, 1 },
+ { aic7xxx_patch7_func, 113, 1, 2 },
+ { aic7xxx_patch0_func, 114, 1, 1 },
+ { aic7xxx_patch1_func, 118, 1, 1 },
+ { aic7xxx_patch1_func, 121, 3, 3 },
+ { aic7xxx_patch11_func, 123, 1, 1 },
+ { aic7xxx_patch0_func, 124, 5, 1 },
+ { aic7xxx_patch7_func, 132, 1, 1 },
+ { aic7xxx_patch9_func, 133, 1, 1 },
+ { aic7xxx_patch10_func, 134, 3, 1 },
+ { aic7xxx_patch7_func, 137, 3, 2 },
+ { aic7xxx_patch0_func, 140, 2, 1 },
+ { aic7xxx_patch7_func, 142, 5, 2 },
+ { aic7xxx_patch0_func, 147, 3, 1 },
+ { aic7xxx_patch7_func, 150, 1, 2 },
+ { aic7xxx_patch0_func, 151, 2, 1 },
+ { aic7xxx_patch1_func, 153, 15, 4 },
+ { aic7xxx_patch11_func, 166, 1, 2 },
+ { aic7xxx_patch0_func, 167, 1, 1 },
+ { aic7xxx_patch0_func, 168, 10, 1 },
+ { aic7xxx_patch7_func, 181, 1, 2 },
+ { aic7xxx_patch0_func, 182, 2, 1 },
+ { aic7xxx_patch7_func, 184, 18, 1 },
+ { aic7xxx_patch1_func, 202, 3, 3 },
+ { aic7xxx_patch7_func, 204, 1, 1 },
+ { aic7xxx_patch0_func, 205, 4, 1 },
+ { aic7xxx_patch7_func, 210, 2, 1 },
+ { aic7xxx_patch7_func, 215, 13, 3 },
+ { aic7xxx_patch12_func, 218, 1, 1 },
+ { aic7xxx_patch12_func, 219, 4, 1 },
+ { aic7xxx_patch1_func, 229, 3, 3 },
+ { aic7xxx_patch11_func, 231, 1, 1 },
+ { aic7xxx_patch0_func, 232, 5, 1 },
+ { aic7xxx_patch11_func, 237, 1, 2 },
+ { aic7xxx_patch0_func, 238, 9, 1 },
+ { aic7xxx_patch13_func, 254, 1, 2 },
+ { aic7xxx_patch0_func, 255, 1, 1 },
+ { aic7xxx_patch4_func, 316, 1, 2 },
+ { aic7xxx_patch0_func, 317, 1, 1 },
+ { aic7xxx_patch2_func, 320, 1, 1 },
+ { aic7xxx_patch1_func, 330, 3, 2 },
+ { aic7xxx_patch0_func, 333, 5, 1 },
+ { aic7xxx_patch13_func, 341, 1, 2 },
+ { aic7xxx_patch0_func, 342, 1, 1 },
+ { aic7xxx_patch5_func, 347, 1, 1 },
+ { aic7xxx_patch11_func, 389, 15, 2 },
+ { aic7xxx_patch14_func, 402, 1, 1 },
+ { aic7xxx_patch1_func, 441, 7, 2 },
+ { aic7xxx_patch0_func, 448, 8, 1 },
+ { aic7xxx_patch1_func, 457, 4, 2 },
+ { aic7xxx_patch0_func, 461, 6, 1 },
+ { aic7xxx_patch1_func, 467, 4, 2 },
+ { aic7xxx_patch0_func, 471, 3, 1 },
+ { aic7xxx_patch10_func, 481, 10, 1 },
+ { aic7xxx_patch1_func, 500, 22, 5 },
+ { aic7xxx_patch11_func, 508, 4, 1 },
+ { aic7xxx_patch7_func, 512, 7, 3 },
+ { aic7xxx_patch15_func, 512, 5, 2 },
+ { aic7xxx_patch0_func, 517, 2, 1 },
+ { aic7xxx_patch10_func, 522, 50, 3 },
+ { aic7xxx_patch14_func, 543, 17, 2 },
+ { aic7xxx_patch0_func, 560, 4, 1 },
+ { aic7xxx_patch10_func, 572, 4, 1 },
+ { aic7xxx_patch5_func, 576, 2, 1 },
+ { aic7xxx_patch5_func, 579, 9, 1 },
+
+};
diff --git a/drivers/scsi/aic7xxx_old/scsi_message.h b/drivers/scsi/aic7xxx_old/scsi_message.h
new file mode 100644
index 000000000000..a79f89c65173
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/scsi_message.h
@@ -0,0 +1,49 @@
+/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE 0x00 /* M/M */
+#define MSG_EXTENDED 0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
+#define MSG_ABORT 0x06 /* O/M */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+#define MSG_PARITY_ERROR 0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET 0x0c /* O/M */
+#define MSG_ABORT_TAG 0x0d /* O/O */
+#define MSG_CLEAR_QUEUE 0x0e /* O/O */
+#define MSG_INIT_RECOVERY 0x0f /* O/O */
+#define MSG_REL_RECOVERY 0x10 /* O/O */
+#define MSG_TERM_IO_PROC 0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+
+/* Identify message */ /* M/M */
+#define MSG_IDENTIFYFLAG 0x80
+#define MSG_IDENTIFY_DISCFLAG 0x40
+#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG)
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR 0x01
+#define MSG_EXT_SDTR_LEN 0x03
+
+#define MSG_EXT_WDTR 0x03
+#define MSG_EXT_WDTR_LEN 0x02
+#define MSG_EXT_WDTR_BUS_8_BIT 0x00
+#define MSG_EXT_WDTR_BUS_16_BIT 0x01
+#define MSG_EXT_WDTR_BUS_32_BIT 0x02
+
+#define MSG_EXT_PPR 0x04
+#define MSG_EXT_PPR_LEN 0x06
+#define MSG_EXT_PPR_OPTION_ST 0x00
+#define MSG_EXT_PPR_OPTION_DT_CRC 0x02
+#define MSG_EXT_PPR_OPTION_DT_UNITS 0x03
+#define MSG_EXT_PPR_OPTION_DT_CRC_QUICK 0x04
+#define MSG_EXT_PPR_OPTION_DT_UNITS_QUICK 0x05
diff --git a/drivers/scsi/aic7xxx_old/sequencer.h b/drivers/scsi/aic7xxx_old/sequencer.h
new file mode 100644
index 000000000000..ee66855222b1
--- /dev/null
+++ b/drivers/scsi/aic7xxx_old/sequencer.h
@@ -0,0 +1,135 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sequencer.h,v 1.3 1997/09/27 19:37:31 gibbs Exp $
+ */
+
+#ifdef __LITTLE_ENDIAN_BITFIELD
+struct ins_format1 {
+ unsigned int
+ immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+};
+
+struct ins_format2 {
+ unsigned int
+ shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+};
+
+struct ins_format3 {
+ unsigned int
+ immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
+};
+#elif defined(__BIG_ENDIAN_BITFIELD)
+struct ins_format1 {
+ unsigned int
+ parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ immediate : 8;
+};
+
+struct ins_format2 {
+ unsigned int
+ parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ shift_control : 8;
+};
+
+struct ins_format3 {
+ unsigned int
+ parity : 1,
+ opcode : 4,
+ address : 10,
+ source : 9,
+ immediate : 8;
+};
+#endif
+
+union ins_formats {
+ struct ins_format1 format1;
+ struct ins_format2 format2;
+ struct ins_format3 format3;
+ unsigned char bytes[4];
+ unsigned int integer;
+};
+struct instruction {
+ union ins_formats format;
+ unsigned int srcline;
+ struct symbol *patch_label;
+ struct {
+ struct instruction *stqe_next;
+ } links;
+};
+
+#define AIC_OP_OR 0x0
+#define AIC_OP_AND 0x1
+#define AIC_OP_XOR 0x2
+#define AIC_OP_ADD 0x3
+#define AIC_OP_ADC 0x4
+#define AIC_OP_ROL 0x5
+#define AIC_OP_BMOV 0x6
+
+#define AIC_OP_JMP 0x8
+#define AIC_OP_JC 0x9
+#define AIC_OP_JNC 0xa
+#define AIC_OP_CALL 0xb
+#define AIC_OP_JNE 0xc
+#define AIC_OP_JNZ 0xd
+#define AIC_OP_JE 0xe
+#define AIC_OP_JZ 0xf
+
+/* Pseudo Ops */
+#define AIC_OP_SHL 0x10
+#define AIC_OP_SHR 0x20
+#define AIC_OP_ROR 0x30
diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c
new file mode 100644
index 000000000000..5f13546d6392
--- /dev/null
+++ b/drivers/scsi/amiga7xx.c
@@ -0,0 +1,141 @@
+/*
+ * Detection routine for the NCR53c710 based Amiga SCSI Controllers for Linux.
+ * Amiga MacroSystemUS WarpEngine SCSI controller.
+ * Amiga Technologies A4000T SCSI controller.
+ * Amiga Technologies/DKB A4091 SCSI controller.
+ *
+ * Written 1997 by Alan Hourihane <alanh@fairlite.demon.co.uk>
+ * plus modifications of the 53c7xx.c driver to support the Amiga.
+ */
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/zorro.h>
+#include <linux/stat.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "53c7xx.h"
+#include "amiga7xx.h"
+
+
+static int amiga7xx_register_one(Scsi_Host_Template *tpnt,
+ unsigned long address)
+{
+ long long options;
+ int clock;
+
+ if (!request_mem_region(address, 0x1000, "ncr53c710"))
+ return 0;
+
+ address = (unsigned long)z_ioremap(address, 0x1000);
+ options = OPTION_MEMORY_MAPPED | OPTION_DEBUG_TEST1 | OPTION_INTFLY |
+ OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS |
+ OPTION_DISCONNECT;
+ clock = 50000000; /* 50 MHz SCSI Clock */
+ ncr53c7xx_init(tpnt, 0, 710, address, 0, IRQ_AMIGA_PORTS, DMA_NONE,
+ options, clock);
+ return 1;
+}
+
+
+#ifdef CONFIG_ZORRO
+
+static struct {
+ zorro_id id;
+ unsigned long offset;
+ int absolute; /* offset is absolute address */
+} amiga7xx_table[] = {
+ { .id = ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS, .offset = 0xf40000,
+ .absolute = 1 },
+ { .id = ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx, .offset = 0x40000 },
+ { .id = ZORRO_PROD_CBM_A4091_1, .offset = 0x800000 },
+ { .id = ZORRO_PROD_CBM_A4091_2, .offset = 0x800000 },
+ { .id = ZORRO_PROD_GVP_GFORCE_040_060, .offset = 0x40000 },
+ { 0 }
+};
+
+static int __init amiga7xx_zorro_detect(Scsi_Host_Template *tpnt)
+{
+ int num = 0, i;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ for (i = 0; amiga7xx_table[i].id; i++)
+ if (z->id == amiga7xx_table[i].id)
+ break;
+ if (!amiga7xx_table[i].id)
+ continue;
+ if (amiga7xx_table[i].absolute)
+ address = amiga7xx_table[i].offset;
+ else
+ address = z->resource.start + amiga7xx_table[i].offset;
+ num += amiga7xx_register_one(tpnt, address);
+ }
+ return num;
+}
+
+#endif /* CONFIG_ZORRO */
+
+
+int __init amiga7xx_detect(Scsi_Host_Template *tpnt)
+{
+ static unsigned char called = 0;
+ int num = 0;
+
+ if (called || !MACH_IS_AMIGA)
+ return 0;
+
+ tpnt->proc_name = "Amiga7xx";
+
+ if (AMIGAHW_PRESENT(A4000_SCSI))
+ num += amiga7xx_register_one(tpnt, 0xdd0040);
+
+#ifdef CONFIG_ZORRO
+ num += amiga7xx_zorro_detect(tpnt);
+#endif
+
+ called = 1;
+ return num;
+}
+
+static int amiga7xx_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "Amiga NCR53c710 SCSI",
+ .detect = amiga7xx_detect,
+ .release = amiga7xx_release,
+ .queuecommand = NCR53c7xx_queue_command,
+ .abort = NCR53c7xx_abort,
+ .reset = NCR53c7xx_reset,
+ .can_queue = 24,
+ .this_id = 7,
+ .sg_tablesize = 63,
+ .cmd_per_lun = 3,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/amiga7xx.h b/drivers/scsi/amiga7xx.h
new file mode 100644
index 000000000000..8cc54a5b889e
--- /dev/null
+++ b/drivers/scsi/amiga7xx.h
@@ -0,0 +1,23 @@
+#ifndef AMIGA7XX_H
+
+#include <linux/types.h>
+
+int amiga7xx_detect(Scsi_Host_Template *);
+const char *NCR53c7x0_info(void);
+int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int NCR53c7xx_abort(Scsi_Cmnd *);
+int NCR53c7x0_release (struct Scsi_Host *);
+int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int);
+void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 3
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 24
+#endif
+
+#include <scsi/scsicam.h>
+
+#endif /* AMIGA7XX_H */
diff --git a/drivers/scsi/arm/Kconfig b/drivers/scsi/arm/Kconfig
new file mode 100644
index 000000000000..54b32868aaf7
--- /dev/null
+++ b/drivers/scsi/arm/Kconfig
@@ -0,0 +1,89 @@
+#
+# SCSI driver configuration for Acorn
+#
+config SCSI_ACORNSCSI_3
+ tristate "Acorn SCSI card (aka30) support"
+ depends on ARCH_ACORN && SCSI
+ help
+ This enables support for the Acorn SCSI card (aka30). If you have an
+ Acorn system with one of these, say Y. If unsure, say N.
+
+config SCSI_ACORNSCSI_TAGGED_QUEUE
+ bool "Support SCSI 2 Tagged queueing"
+ depends on SCSI_ACORNSCSI_3
+ help
+ Say Y here to enable tagged queuing support on the Acorn SCSI card.
+
+ This is a feature of SCSI-2 which improves performance: the host
+ adapter can send several SCSI commands to a device's queue even if
+ previous commands haven't finished yet. Some SCSI devices don't
+ implement this properly, so the safe answer is N.
+
+config SCSI_ACORNSCSI_SYNC
+ bool "Support SCSI 2 Synchronous Transfers"
+ depends on SCSI_ACORNSCSI_3
+ help
+ Say Y here to enable synchronous transfer negotiation with all
+ targets on the Acorn SCSI card.
+
+ In general, this improves performance; however some SCSI devices
+ don't implement it properly, so the safe answer is N.
+
+config SCSI_ARXESCSI
+ tristate "ARXE SCSI support"
+ depends on ARCH_ACORN && SCSI
+ help
+ Around 1991, Arxe Systems Limited released a high density floppy
+ disc interface for the Acorn Archimedes range, to allow the use of
+ HD discs from the then new A5000 on earlier models. This interface
+ was either sold on its own or with an integral SCSI controller.
+ Technical details on this NCR53c94-based device are available at
+ <http://www.cryton.demon.co.uk/acornbits/scsi_arxe.html>
+ Say Y here to compile in support for the SCSI controller.
+
+config SCSI_CUMANA_2
+ tristate "CumanaSCSI II support"
+ depends on ARCH_ACORN && SCSI
+ help
+ This enables support for the Cumana SCSI II card. If you have an
+ Acorn system with one of these, say Y. If unsure, say N.
+
+config SCSI_EESOXSCSI
+ tristate "EESOX support"
+ depends on ARCH_ACORN && SCSI
+ help
+ This enables support for the EESOX SCSI card. If you have an Acorn
+ system with one of these, say Y, otherwise say N.
+
+config SCSI_POWERTECSCSI
+ tristate "PowerTec support"
+ depends on ARCH_ACORN && SCSI
+ help
+ This enables support for the Powertec SCSI card on Acorn systems. If
+ you have one of these, say Y. If unsure, say N.
+
+comment "The following drivers are not fully supported"
+ depends on ARCH_ACORN && EXPERIMENTAL
+
+config SCSI_CUMANA_1
+ tristate "CumanaSCSI I support (EXPERIMENTAL)"
+ depends on ARCH_ACORN && EXPERIMENTAL && SCSI
+ help
+ This enables support for the Cumana SCSI I card. If you have an
+ Acorn system with one of these, say Y. If unsure, say N.
+
+config SCSI_ECOSCSI
+ tristate "EcoScsi support (EXPERIMENTAL)"
+ depends on ARCH_ACORN && EXPERIMENTAL && (ARCH_ARC || ARCH_A5K) && SCSI
+ help
+ This enables support for the EcoSCSI card -- a small card that sits
+ in the Econet socket. If you have an Acorn system with one of these,
+ say Y. If unsure, say N.
+
+config SCSI_OAK1
+ tristate "Oak SCSI support (EXPERIMENTAL)"
+ depends on ARCH_ACORN && EXPERIMENTAL && SCSI
+ help
+ This enables support for the Oak SCSI card. If you have an Acorn
+ system with one of these, say Y. If unsure, say N.
+
diff --git a/drivers/scsi/arm/Makefile b/drivers/scsi/arm/Makefile
new file mode 100644
index 000000000000..e8db17924c1d
--- /dev/null
+++ b/drivers/scsi/arm/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for drivers/scsi/arm
+#
+
+acornscsi_mod-objs := acornscsi.o acornscsi-io.o
+
+obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o
+obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o
+obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o
+obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o
+obj-$(CONFIG_SCSI_ECOSCSI) += ecoscsi.o
+obj-$(CONFIG_SCSI_OAK1) += oak.o
+obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o
+obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o
diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S
new file mode 100644
index 000000000000..93467e6ac923
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.S
@@ -0,0 +1,145 @@
+/*
+ * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+#if (IO_BASE == (PCIO_BASE & 0xff000000))
+#define ADDR(off,reg) \
+ tst off, $0x80000000 ;\
+ mov reg, $IO_BASE ;\
+ orreq reg, reg, $(PCIO_BASE & 0x00ff0000)
+#else
+#define ADDR(off,reg) \
+ tst off, $0x80000000 ;\
+ movne reg, $IO_BASE ;\
+ moveq reg, $(PCIO_BASE & 0xff000000) ;\
+ orreq reg, reg, $(PCIO_BASE & 0x00ff0000)
+#endif
+
+@ Purpose: transfer a block of data from the acorn scsi card to memory
+@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
+@ Returns: nothing
+
+ .align
+ENTRY(__acornscsi_in)
+ stmfd sp!, {r4 - r7, lr}
+ bic r0, r0, #3
+ mov lr, #0xff
+ orr lr, lr, #0xff00
+acornscsi_in16lp:
+ subs r2, r2, #16
+ bmi acornscsi_in8
+ ldmia r0!, {r3, r4, r5, r6}
+ and r3, r3, lr
+ orr r3, r3, r4, lsl #16
+ and r4, r5, lr
+ orr r4, r4, r6, lsl #16
+ ldmia r0!, {r5, r6, r7, ip}
+ and r5, r5, lr
+ orr r5, r5, r6, lsl #16
+ and r6, r7, lr
+ orr r6, r6, ip, lsl #16
+ stmia r1!, {r3 - r6}
+ bne acornscsi_in16lp
+ LOADREGS(fd, sp!, {r4 - r7, pc})
+
+acornscsi_in8: adds r2, r2, #8
+ bmi acornscsi_in4
+ ldmia r0!, {r3, r4, r5, r6}
+ and r3, r3, lr
+ orr r3, r3, r4, lsl #16
+ and r4, r5, lr
+ orr r4, r4, r6, lsl #16
+ stmia r1!, {r3 - r4}
+ LOADREGS(eqfd, sp!, {r4 - r7, pc})
+ sub r2, r2, #8
+
+acornscsi_in4: adds r2, r2, #4
+ bmi acornscsi_in2
+ ldmia r0!, {r3, r4}
+ and r3, r3, lr
+ orr r3, r3, r4, lsl #16
+ str r3, [r1], #4
+ LOADREGS(eqfd, sp!, {r4 - r7, pc})
+ sub r2, r2, #4
+
+acornscsi_in2: adds r2, r2, #2
+ ldr r3, [r0], #4
+ and r3, r3, lr
+ strb r3, [r1], #1
+ mov r3, r3, lsr #8
+ strplb r3, [r1], #1
+ LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@ Purpose: transfer a block of data from memory to the acorn scsi card
+@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
+@ Returns: nothing
+
+ENTRY(__acornscsi_out)
+ stmfd sp!, {r4 - r6, lr}
+ bic r0, r0, #3
+acornscsi_out16lp:
+ subs r2, r2, #16
+ bmi acornscsi_out8
+ ldmia r1!, {r4, r6, ip, lr}
+ mov r3, r4, lsl #16
+ orr r3, r3, r3, lsr #16
+ mov r4, r4, lsr #16
+ orr r4, r4, r4, lsl #16
+ mov r5, r6, lsl #16
+ orr r5, r5, r5, lsr #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r6, lsl #16
+ stmia r0!, {r3, r4, r5, r6}
+ mov r3, ip, lsl #16
+ orr r3, r3, r3, lsr #16
+ mov r4, ip, lsr #16
+ orr r4, r4, r4, lsl #16
+ mov ip, lr, lsl #16
+ orr ip, ip, ip, lsr #16
+ mov lr, lr, lsr #16
+ orr lr, lr, lr, lsl #16
+ stmia r0!, {r3, r4, ip, lr}
+ bne acornscsi_out16lp
+ LOADREGS(fd, sp!, {r4 - r6, pc})
+
+acornscsi_out8: adds r2, r2, #8
+ bmi acornscsi_out4
+ ldmia r1!, {r4, r6}
+ mov r3, r4, lsl #16
+ orr r3, r3, r3, lsr #16
+ mov r4, r4, lsr #16
+ orr r4, r4, r4, lsl #16
+ mov r5, r6, lsl #16
+ orr r5, r5, r5, lsr #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r6, lsl #16
+ stmia r0!, {r3, r4, r5, r6}
+ LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+ sub r2, r2, #8
+acornscsi_out4: adds r2, r2, #4
+ bmi acornscsi_out2
+ ldr r4, [r1], #4
+ mov r3, r4, lsl #16
+ orr r3, r3, r3, lsr #16
+ mov r4, r4, lsr #16
+ orr r4, r4, r4, lsl #16
+ stmia r0!, {r3, r4}
+ LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+ sub r2, r2, #4
+acornscsi_out2: adds r2, r2, #2
+ ldr r3, [r1], #2
+ strb r3, [r0], #1
+ mov r3, r3, lsr #8
+ strplb r3, [r0], #1
+ LOADREGS(fd, sp!, {r4 - r6, pc})
+
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
new file mode 100644
index 000000000000..24dd0b890dd2
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi.c
@@ -0,0 +1,3130 @@
+/*
+ * linux/drivers/acorn/scsi/acornscsi.c
+ *
+ * Acorn SCSI 3 driver
+ * By R.M.King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Abandoned using the Select and Transfer command since there were
+ * some nasty races between our software and the target devices that
+ * were not easy to solve, and the device errata had a lot of entries
+ * for this command, some of them quite nasty...
+ *
+ * Changelog:
+ * 26-Sep-1997 RMK Re-jigged to use the queue module.
+ * Re-coded state machine to be based on driver
+ * state not scsi state. Should be easier to debug.
+ * Added acornscsi_release to clean up properly.
+ * Updated proc/scsi reporting.
+ * 05-Oct-1997 RMK Implemented writing to SCSI devices.
+ * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/
+ * reconnect race condition causing a warning message.
+ * 12-Oct-1997 RMK Added catch for re-entering interrupt routine.
+ * 15-Oct-1997 RMK Improved handling of commands.
+ * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h.
+ * 13-Dec-1998 RMK Better abort code and command handling. Extra state
+ * transitions added to allow dodgy devices to work.
+ */
+#define DEBUG_NO_WRITE 1
+#define DEBUG_QUEUES 2
+#define DEBUG_DMA 4
+#define DEBUG_ABORT 8
+#define DEBUG_DISCON 16
+#define DEBUG_CONNECT 32
+#define DEBUG_PHASES 64
+#define DEBUG_WRITE 128
+#define DEBUG_LINK 256
+#define DEBUG_MESSAGES 512
+#define DEBUG_RESET 1024
+#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
+ DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
+ DEBUG_DMA|DEBUG_QUEUES)
+
+/* DRIVER CONFIGURATION
+ *
+ * SCSI-II Tagged queue support.
+ *
+ * I don't have any SCSI devices that support it, so it is totally untested
+ * (except to make sure that it doesn't interfere with any non-tagging
+ * devices). It is not fully implemented either - what happens when a
+ * tagging device reconnects???
+ *
+ * You can tell if you have a device that supports tagged queueing my
+ * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported
+ * as '2 TAG'.
+ *
+ * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config
+ * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug,
+ * comment out the undef.
+ */
+#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+/*
+ * SCSI-II Linked command support.
+ *
+ * The higher level code doesn't support linked commands yet, and so the option
+ * is undef'd here.
+ */
+#undef CONFIG_SCSI_ACORNSCSI_LINK
+/*
+ * SCSI-II Synchronous transfer support.
+ *
+ * Tried and tested...
+ *
+ * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max)
+ * SDTR_PERIOD - period of REQ signal (min=125, max=1020)
+ * DEFAULT_PERIOD - default REQ period.
+ */
+#define SDTR_SIZE 12
+#define SDTR_PERIOD 125
+#define DEFAULT_PERIOD 500
+
+/*
+ * Debugging information
+ *
+ * DEBUG - bit mask from list above
+ * DEBUG_TARGET - is defined to the target number if you want to debug
+ * a specific target. [only recon/write/dma].
+ */
+#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE)
+/* only allow writing to SCSI device 0 */
+#define NO_WRITE 0xFE
+/*#define DEBUG_TARGET 2*/
+/*
+ * Select timeout time (in 10ms units)
+ *
+ * This is the timeout used between the start of selection and the WD33C93
+ * chip deciding that the device isn't responding.
+ */
+#define TIMEOUT_TIME 10
+/*
+ * Define this if you want to have verbose explaination of SCSI
+ * status/messages.
+ */
+#undef CONFIG_ACORNSCSI_CONSTANTS
+/*
+ * Define this if you want to use the on board DMAC [don't remove this option]
+ * If not set, then use PIO mode (not currently supported).
+ */
+#define USE_DMAC
+
+/*
+ * ====================================================================================
+ */
+
+#ifdef DEBUG_TARGET
+#define DBG(cmd,xxx...) \
+ if (cmd->device->id == DEBUG_TARGET) { \
+ xxx; \
+ }
+#else
+#define DBG(cmd,xxx...) xxx
+#endif
+
+#ifndef STRINGIFY
+#define STRINGIFY(x) #x
+#endif
+#define STRx(x) STRINGIFY(x)
+#define NO_WRITE_STR STRx(NO_WRITE)
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+#include "acornscsi.h"
+#include "msgqueue.h"
+#include "scsi.h"
+
+#include <scsi/scsicam.h>
+
+#define VER_MAJOR 2
+#define VER_MINOR 0
+#define VER_PATCH 6
+
+#ifndef ABORT_TAG
+#define ABORT_TAG 0xd
+#else
+#error "Yippee! ABORT TAG is now defined! Remove this error!"
+#endif
+
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+#error SCSI2 LINKed commands not supported (yet)!
+#endif
+
+#ifdef USE_DMAC
+/*
+ * DMAC setup parameters
+ */
+#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP)
+#define INIT_DEVCON1 (DEVCON1_BHLD)
+#define DMAC_READ (MODECON_READ)
+#define DMAC_WRITE (MODECON_WRITE)
+#define INIT_SBICDMA (CTRL_DMABURST)
+
+#define scsi_xferred have_data_in
+
+/*
+ * Size of on-board DMA buffer
+ */
+#define DMAC_BUFFER_SIZE 65536
+#endif
+
+#define STATUS_BUFFER_TO_PRINT 24
+
+unsigned int sdtr_period = SDTR_PERIOD;
+unsigned int sdtr_size = SDTR_SIZE;
+
+static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
+static int acornscsi_reconnect_finish(AS_Host *host);
+static void acornscsi_dma_cleanup(AS_Host *host);
+static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
+
+/* ====================================================================================
+ * Miscellaneous
+ */
+
+static inline void
+sbic_arm_write(unsigned int io_port, int reg, int value)
+{
+ __raw_writeb(reg, io_port);
+ __raw_writeb(value, io_port + 4);
+}
+
+#define sbic_arm_writenext(io,val) \
+ __raw_writeb((val), (io) + 4)
+
+static inline
+int sbic_arm_read(unsigned int io_port, int reg)
+{
+ if(reg == SBIC_ASR)
+ return __raw_readl(io_port) & 255;
+ __raw_writeb(reg, io_port);
+ return __raw_readl(io_port + 4) & 255;
+}
+
+#define sbic_arm_readnext(io) \
+ __raw_readb((io) + 4)
+
+#ifdef USE_DMAC
+#define dmac_read(io_port,reg) \
+ inb((io_port) + (reg))
+
+#define dmac_write(io_port,reg,value) \
+ ({ outb((value), (io_port) + (reg)); })
+
+#define dmac_clearintr(io_port) \
+ ({ outb(0, (io_port)); })
+
+static inline
+unsigned int dmac_address(unsigned int io_port)
+{
+ return dmac_read(io_port, DMAC_TXADRHI) << 16 |
+ dmac_read(io_port, DMAC_TXADRMD) << 8 |
+ dmac_read(io_port, DMAC_TXADRLO);
+}
+
+static
+void acornscsi_dumpdma(AS_Host *host, char *where)
+{
+ unsigned int mode, addr, len;
+
+ mode = dmac_read(host->dma.io_port, DMAC_MODECON);
+ addr = dmac_address(host->dma.io_port);
+ len = dmac_read(host->dma.io_port, DMAC_TXCNTHI) << 8 |
+ dmac_read(host->dma.io_port, DMAC_TXCNTLO);
+
+ printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
+ host->host->host_no, where,
+ mode, addr, (len + 1) & 0xffff,
+ dmac_read(host->dma.io_port, DMAC_MASKREG));
+
+ printk("DMA @%06x, ", host->dma.start_addr);
+ printk("BH @%p +%04x, ", host->scsi.SCp.ptr,
+ host->scsi.SCp.this_residual);
+ printk("DT @+%04x ST @+%04x", host->dma.transferred,
+ host->scsi.SCp.scsi_xferred);
+ printk("\n");
+}
+#endif
+
+static
+unsigned long acornscsi_sbic_xfcount(AS_Host *host)
+{
+ unsigned long length;
+
+ length = sbic_arm_read(host->scsi.io_port, SBIC_TRANSCNTH) << 16;
+ length |= sbic_arm_readnext(host->scsi.io_port) << 8;
+ length |= sbic_arm_readnext(host->scsi.io_port);
+
+ return length;
+}
+
+static int
+acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)
+{
+ int asr;
+
+ do {
+ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+
+ if ((asr & stat_mask) == stat)
+ return 0;
+
+ udelay(1);
+ } while (--timeout);
+
+ printk("scsi%d: timeout while %s\n", host->host->host_no, msg);
+
+ return -1;
+}
+
+static
+int acornscsi_sbic_issuecmd(AS_Host *host, int command)
+{
+ if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))
+ return -1;
+
+ sbic_arm_write(host->scsi.io_port, SBIC_CMND, command);
+
+ return 0;
+}
+
+static void
+acornscsi_csdelay(unsigned int cs)
+{
+ unsigned long target_jiffies, flags;
+
+ target_jiffies = jiffies + 1 + cs * HZ / 100;
+
+ local_save_flags(flags);
+ local_irq_enable();
+
+ while (time_before(jiffies, target_jiffies)) barrier();
+
+ local_irq_restore(flags);
+}
+
+static
+void acornscsi_resetcard(AS_Host *host)
+{
+ unsigned int i, timeout;
+
+ /* assert reset line */
+ host->card.page_reg = 0x80;
+ outb(host->card.page_reg, host->card.io_page);
+
+ /* wait 3 cs. SCSI standard says 25ms. */
+ acornscsi_csdelay(3);
+
+ host->card.page_reg = 0;
+ outb(host->card.page_reg, host->card.io_page);
+
+ /*
+ * Should get a reset from the card
+ */
+ timeout = 1000;
+ do {
+ if (inb(host->card.io_intr) & 8)
+ break;
+ udelay(1);
+ } while (--timeout);
+
+ if (timeout == 0)
+ printk("scsi%d: timeout while resetting card\n",
+ host->host->host_no);
+
+ sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+ sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+
+ /* setup sbic - WD33C93A */
+ sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id);
+ sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET);
+
+ /*
+ * Command should cause a reset interrupt
+ */
+ timeout = 1000;
+ do {
+ if (inb(host->card.io_intr) & 8)
+ break;
+ udelay(1);
+ } while (--timeout);
+
+ if (timeout == 0)
+ printk("scsi%d: timeout while resetting card\n",
+ host->host->host_no);
+
+ sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+ if (sbic_arm_read(host->scsi.io_port, SBIC_SSR) != 0x01)
+ printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+ host->host->host_no);
+
+ sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
+ sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME);
+ sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+ sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+
+ host->card.page_reg = 0x40;
+ outb(host->card.page_reg, host->card.io_page);
+
+ /* setup dmac - uPC71071 */
+ dmac_write(host->dma.io_port, DMAC_INIT, 0);
+#ifdef USE_DMAC
+ dmac_write(host->dma.io_port, DMAC_INIT, INIT_8BIT);
+ dmac_write(host->dma.io_port, DMAC_CHANNEL, CHANNEL_0);
+ dmac_write(host->dma.io_port, DMAC_DEVCON0, INIT_DEVCON0);
+ dmac_write(host->dma.io_port, DMAC_DEVCON1, INIT_DEVCON1);
+#endif
+
+ host->SCpnt = NULL;
+ host->scsi.phase = PHASE_IDLE;
+ host->scsi.disconnectable = 0;
+
+ memset(host->busyluns, 0, sizeof(host->busyluns));
+
+ for (i = 0; i < 8; i++) {
+ host->device[i].sync_state = SYNC_NEGOCIATE;
+ host->device[i].disconnect_ok = 1;
+ }
+
+ /* wait 25 cs. SCSI standard says 250ms. */
+ acornscsi_csdelay(25);
+}
+
+/*=============================================================================================
+ * Utility routines (eg. debug)
+ */
+#ifdef CONFIG_ACORNSCSI_CONSTANTS
+static char *acornscsi_interrupttype[] = {
+ "rst", "suc", "p/a", "3",
+ "term", "5", "6", "7",
+ "serv", "9", "a", "b",
+ "c", "d", "e", "f"
+};
+
+static signed char acornscsi_map[] = {
+ 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11,
+ -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,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11,
+ -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,
+ -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,
+ -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,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char *acornscsi_interruptcode[] = {
+ /* 0 */
+ "reset - normal mode", /* 00 */
+ "reset - advanced mode", /* 01 */
+
+ /* 2 */
+ "sel", /* 11 */
+ "sel+xfer", /* 16 */
+ "data-out", /* 18 */
+ "data-in", /* 19 */
+ "cmd", /* 1A */
+ "stat", /* 1B */
+ "??-out", /* 1C */
+ "??-in", /* 1D */
+ "msg-out", /* 1E */
+ "msg-in", /* 1F */
+
+ /* 12 */
+ "/ACK asserted", /* 20 */
+ "save-data-ptr", /* 21 */
+ "{re}sel", /* 22 */
+
+ /* 15 */
+ "inv cmd", /* 40 */
+ "unexpected disconnect", /* 41 */
+ "sel timeout", /* 42 */
+ "P err", /* 43 */
+ "P err+ATN", /* 44 */
+ "bad status byte", /* 47 */
+
+ /* 21 */
+ "resel, no id", /* 80 */
+ "resel", /* 81 */
+ "discon", /* 85 */
+};
+
+static
+void print_scsi_status(unsigned int ssr)
+{
+ if (acornscsi_map[ssr] != -1)
+ printk("%s:%s",
+ acornscsi_interrupttype[(ssr >> 4)],
+ acornscsi_interruptcode[acornscsi_map[ssr]]);
+ else
+ printk("%X:%X", ssr >> 4, ssr & 0x0f);
+}
+#endif
+
+static
+void print_sbic_status(int asr, int ssr, int cmdphase)
+{
+#ifdef CONFIG_ACORNSCSI_CONSTANTS
+ printk("sbic: %c%c%c%c%c%c ",
+ asr & ASR_INT ? 'I' : 'i',
+ asr & ASR_LCI ? 'L' : 'l',
+ asr & ASR_BSY ? 'B' : 'b',
+ asr & ASR_CIP ? 'C' : 'c',
+ asr & ASR_PE ? 'P' : 'p',
+ asr & ASR_DBR ? 'D' : 'd');
+ printk("scsi: ");
+ print_scsi_status(ssr);
+ printk(" ph %02X\n", cmdphase);
+#else
+ printk("sbic: %02X scsi: %X:%X ph: %02X\n",
+ asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
+#endif
+}
+
+static void
+acornscsi_dumplogline(AS_Host *host, int target, int line)
+{
+ unsigned long prev;
+ signed int ptr;
+
+ ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;
+ if (ptr < 0)
+ ptr += STATUS_BUFFER_SIZE;
+
+ printk("%c: %3s:", target == 8 ? 'H' : '0' + target,
+ line == 0 ? "ph" : line == 1 ? "ssr" : "int");
+
+ prev = host->status[target][ptr].when;
+
+ for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+ unsigned long time_diff;
+
+ if (!host->status[target][ptr].when)
+ continue;
+
+ switch (line) {
+ case 0:
+ printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',
+ host->status[target][ptr].ph);
+ break;
+
+ case 1:
+ printk(" %02X", host->status[target][ptr].ssr);
+ break;
+
+ case 2:
+ time_diff = host->status[target][ptr].when - prev;
+ prev = host->status[target][ptr].when;
+ if (time_diff == 0)
+ printk("==^");
+ else if (time_diff >= 100)
+ printk(" ");
+ else
+ printk(" %02ld", time_diff);
+ break;
+ }
+ }
+
+ printk("\n");
+}
+
+static
+void acornscsi_dumplog(AS_Host *host, int target)
+{
+ do {
+ acornscsi_dumplogline(host, target, 0);
+ acornscsi_dumplogline(host, target, 1);
+ acornscsi_dumplogline(host, target, 2);
+
+ if (target == 8)
+ break;
+
+ target = 8;
+ } while (1);
+}
+
+static
+char acornscsi_target(AS_Host *host)
+{
+ if (host->SCpnt)
+ return '0' + host->SCpnt->device->id;
+ return 'H';
+}
+
+/*
+ * Prototype: cmdtype_t acornscsi_cmdtype(int command)
+ * Purpose : differentiate READ from WRITE from other commands
+ * Params : command - command to interpret
+ * Returns : CMD_READ - command reads data,
+ * CMD_WRITE - command writes data,
+ * CMD_MISC - everything else
+ */
+static inline
+cmdtype_t acornscsi_cmdtype(int command)
+{
+ switch (command) {
+ case WRITE_6: case WRITE_10: case WRITE_12:
+ return CMD_WRITE;
+ case READ_6: case READ_10: case READ_12:
+ return CMD_READ;
+ default:
+ return CMD_MISC;
+ }
+}
+
+/*
+ * Prototype: int acornscsi_datadirection(int command)
+ * Purpose : differentiate between commands that have a DATA IN phase
+ * and a DATA OUT phase
+ * Params : command - command to interpret
+ * Returns : DATADIR_OUT - data out phase expected
+ * DATADIR_IN - data in phase expected
+ */
+static
+datadir_t acornscsi_datadirection(int command)
+{
+ switch (command) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea:
+ return DATADIR_OUT;
+ default:
+ return DATADIR_IN;
+ }
+}
+
+/*
+ * Purpose : provide values for synchronous transfers with 33C93.
+ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * Modified by Russell King for 8MHz WD33C93A
+ */
+static struct sync_xfer_tbl {
+ unsigned int period_ns;
+ unsigned char reg_value;
+} sync_xfer_table[] = {
+ { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 },
+ { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 },
+ { 874, 0x70 }, { 999, 0x00 }, { 0, 0 }
+};
+
+/*
+ * Prototype: int acornscsi_getperiod(unsigned char syncxfer)
+ * Purpose : period for the synchronous transfer setting
+ * Params : syncxfer SYNCXFER register value
+ * Returns : period in ns.
+ */
+static
+int acornscsi_getperiod(unsigned char syncxfer)
+{
+ int i;
+
+ syncxfer &= 0xf0;
+ if (syncxfer == 0x10)
+ syncxfer = 0;
+
+ for (i = 1; sync_xfer_table[i].period_ns; i++)
+ if (syncxfer == sync_xfer_table[i].reg_value)
+ return sync_xfer_table[i].period_ns;
+ return 0;
+}
+
+/*
+ * Prototype: int round_period(unsigned int period)
+ * Purpose : return index into above table for a required REQ period
+ * Params : period - time (ns) for REQ
+ * Returns : table index
+ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ */
+static inline
+int round_period(unsigned int period)
+{
+ int i;
+
+ for (i = 1; sync_xfer_table[i].period_ns; i++) {
+ if ((period <= sync_xfer_table[i].period_ns) &&
+ (period > sync_xfer_table[i - 1].period_ns))
+ return i;
+ }
+ return 7;
+}
+
+/*
+ * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
+ * Purpose : calculate value for 33c93s SYNC register
+ * Params : period - time (ns) for REQ
+ * offset - offset in bytes between REQ/ACK
+ * Returns : value for SYNC register
+ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ */
+static
+unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+ return sync_xfer_table[round_period(period)].reg_value |
+ ((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
+}
+
+/* ====================================================================================
+ * Command functions
+ */
+/*
+ * Function: acornscsi_kick(AS_Host *host)
+ * Purpose : kick next command to interface
+ * Params : host - host to send command to
+ * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
+ * Notes : interrupts are always disabled!
+ */
+static
+intr_ret_t acornscsi_kick(AS_Host *host)
+{
+ int from_queue = 0;
+ Scsi_Cmnd *SCpnt;
+
+ /* first check to see if a command is waiting to be executed */
+ SCpnt = host->origSCpnt;
+ host->origSCpnt = NULL;
+
+ /* retrieve next command */
+ if (!SCpnt) {
+ SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);
+ if (!SCpnt)
+ return INTR_IDLE;
+
+ from_queue = 1;
+ }
+
+ if (host->scsi.disconnectable && host->SCpnt) {
+ queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
+ host->scsi.disconnectable = 0;
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+ DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",
+ host->host->host_no, acornscsi_target(host)));
+#endif
+ host->SCpnt = NULL;
+ }
+
+ /*
+ * If we have an interrupt pending, then we may have been reselected.
+ * In this case, we don't want to write to the registers
+ */
+ if (!(sbic_arm_read(host->scsi.io_port, SBIC_ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+ sbic_arm_write(host->scsi.io_port, SBIC_DESTID, SCpnt->device->id);
+ sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_SELWITHATN);
+ }
+
+ /*
+ * claim host busy - all of these must happen atomically wrt
+ * our interrupt routine. Failure means command loss.
+ */
+ host->scsi.phase = PHASE_CONNECTING;
+ host->SCpnt = SCpnt;
+ host->scsi.SCp = SCpnt->SCp;
+ host->dma.xfer_setup = 0;
+ host->dma.xfer_required = 0;
+ host->dma.xfer_done = 0;
+
+#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
+ DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",
+ host->host->host_no, '0' + SCpnt->device->id,
+ SCpnt->cmnd[0]));
+#endif
+
+ if (from_queue) {
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+ /*
+ * tagged queueing - allocate a new tag to this command
+ */
+ if (SCpnt->device->simple_tags) {
+ SCpnt->device->current_tag += 1;
+ if (SCpnt->device->current_tag == 0)
+ SCpnt->device->current_tag = 1;
+ SCpnt->tag = SCpnt->device->current_tag;
+ } else
+#endif
+ set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
+
+ host->stats.removes += 1;
+
+ switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {
+ case CMD_WRITE:
+ host->stats.writes += 1;
+ break;
+ case CMD_READ:
+ host->stats.reads += 1;
+ break;
+ case CMD_MISC:
+ host->stats.miscs += 1;
+ break;
+ }
+ }
+
+ return INTR_PROCESSING;
+}
+
+/*
+ * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+ * Purpose : complete processing for command
+ * Params : host - interface that completed
+ * result - driver byte of result
+ */
+static
+void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+{
+ Scsi_Cmnd *SCpnt = *SCpntp;
+
+ /* clean up */
+ sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+
+ host->stats.fins += 1;
+
+ if (SCpnt) {
+ *SCpntp = NULL;
+
+ acornscsi_dma_cleanup(host);
+
+ SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
+
+ /*
+ * In theory, this should not happen. In practice, it seems to.
+ * Only trigger an error if the device attempts to report all happy
+ * but with untransferred buffers... If we don't do something, then
+ * data loss will occur. Should we check SCpnt->underflow here?
+ * It doesn't appear to be set to something meaningful by the higher
+ * levels all the time.
+ */
+ if (result == DID_OK) {
+ int xfer_warn = 0;
+
+ if (SCpnt->underflow == 0) {
+ if (host->scsi.SCp.ptr &&
+ acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)
+ xfer_warn = 1;
+ } else {
+ if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||
+ host->scsi.SCp.scsi_xferred != host->dma.transferred)
+ xfer_warn = 1;
+ }
+
+ /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)
+ * Targets which break data transfers into multiple
+ * connections shall end each successful connection
+ * (except possibly the last) with a SAVE DATA
+ * POINTER - DISCONNECT message sequence.
+ *
+ * This makes it difficult to ensure that a transfer has
+ * completed. If we reach the end of a transfer during
+ * the command, then we can only have finished the transfer.
+ * therefore, if we seem to have some data remaining, this
+ * is not a problem.
+ */
+ if (host->dma.xfer_done)
+ xfer_warn = 0;
+
+ if (xfer_warn) {
+ switch (status_byte(SCpnt->result)) {
+ case CHECK_CONDITION:
+ case COMMAND_TERMINATED:
+ case BUSY:
+ case QUEUE_FULL:
+ case RESERVATION_CONFLICT:
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
+ host->host->host_no, SCpnt->result);
+ print_command(SCpnt->cmnd);
+ acornscsi_dumpdma(host, "done");
+ acornscsi_dumplog(host, SCpnt->device->id);
+ SCpnt->result &= 0xffff;
+ SCpnt->result |= DID_ERROR << 16;
+ }
+ }
+ }
+
+ if (!SCpnt->scsi_done)
+ panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
+
+ clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
+
+ SCpnt->scsi_done(SCpnt);
+ } else
+ printk("scsi%d: null command in acornscsi_done", host->host->host_no);
+
+ host->scsi.phase = PHASE_IDLE;
+}
+
+/* ====================================================================================
+ * DMA routines
+ */
+/*
+ * Purpose : update SCSI Data Pointer
+ * Notes : this will only be one SG entry or less
+ */
+static
+void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
+{
+ SCp->ptr += length;
+ SCp->this_residual -= length;
+
+ if (SCp->this_residual == 0 && next_SCp(SCp) == 0)
+ host->dma.xfer_done = 1;
+}
+
+/*
+ * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,
+ * unsigned int start_addr, unsigned int length)
+ * Purpose : read data from DMA RAM
+ * Params : host - host to transfer from
+ * ptr - DRAM address
+ * start_addr - host mem address
+ * length - number of bytes to transfer
+ * Notes : this will only be one SG entry or less
+ */
+static
+void acornscsi_data_read(AS_Host *host, char *ptr,
+ unsigned int start_addr, unsigned int length)
+{
+ extern void __acornscsi_in(int port, char *buf, int len);
+ unsigned int page, offset, len = length;
+
+ page = (start_addr >> 12);
+ offset = start_addr & ((1 << 12) - 1);
+
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
+
+ while (len > 0) {
+ unsigned int this_len;
+
+ if (len + offset > (1 << 12))
+ this_len = (1 << 12) - offset;
+ else
+ this_len = len;
+
+ __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len);
+
+ offset += this_len;
+ ptr += this_len;
+ len -= this_len;
+
+ if (offset == (1 << 12)) {
+ offset = 0;
+ page ++;
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
+ }
+ }
+ outb(host->card.page_reg, host->card.io_page);
+}
+
+/*
+ * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,
+ * unsigned int start_addr, unsigned int length)
+ * Purpose : write data to DMA RAM
+ * Params : host - host to transfer from
+ * ptr - DRAM address
+ * start_addr - host mem address
+ * length - number of bytes to transfer
+ * Notes : this will only be one SG entry or less
+ */
+static
+void acornscsi_data_write(AS_Host *host, char *ptr,
+ unsigned int start_addr, unsigned int length)
+{
+ extern void __acornscsi_out(int port, char *buf, int len);
+ unsigned int page, offset, len = length;
+
+ page = (start_addr >> 12);
+ offset = start_addr & ((1 << 12) - 1);
+
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
+
+ while (len > 0) {
+ unsigned int this_len;
+
+ if (len + offset > (1 << 12))
+ this_len = (1 << 12) - offset;
+ else
+ this_len = len;
+
+ __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len);
+
+ offset += this_len;
+ ptr += this_len;
+ len -= this_len;
+
+ if (offset == (1 << 12)) {
+ offset = 0;
+ page ++;
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
+ }
+ }
+ outb(host->card.page_reg, host->card.io_page);
+}
+
+/* =========================================================================================
+ * On-board DMA routines
+ */
+#ifdef USE_DMAC
+/*
+ * Prototype: void acornscsi_dmastop(AS_Host *host)
+ * Purpose : stop all DMA
+ * Params : host - host on which to stop DMA
+ * Notes : This is called when leaving DATA IN/OUT phase,
+ * or when interface is RESET
+ */
+static inline
+void acornscsi_dma_stop(AS_Host *host)
+{
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
+ dmac_clearintr(host->dma.io_intr_clear);
+
+#if (DEBUG & DEBUG_DMA)
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));
+#endif
+}
+
+/*
+ * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
+ * Purpose : setup DMA controller for data transfer
+ * Params : host - host to setup
+ * direction - data transfer direction
+ * Notes : This is called when entering DATA I/O phase, not
+ * while we're in a DATA I/O phase
+ */
+static
+void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
+{
+ unsigned int address, length, mode;
+
+ host->dma.direction = direction;
+
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
+
+ if (direction == DMA_OUT) {
+#if (DEBUG & DEBUG_NO_WRITE)
+ if (NO_WRITE & (1 << host->SCpnt->device->id)) {
+ printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
+ host->host->host_no, acornscsi_target(host));
+ return;
+ }
+#endif
+ mode = DMAC_WRITE;
+ } else
+ mode = DMAC_READ;
+
+ /*
+ * Allocate some buffer space, limited to half the buffer size
+ */
+ length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+ if (length) {
+ host->dma.start_addr = address = host->dma.free_addr;
+ host->dma.free_addr = (host->dma.free_addr + length) &
+ (DMAC_BUFFER_SIZE - 1);
+
+ /*
+ * Transfer data to DMA memory
+ */
+ if (direction == DMA_OUT)
+ acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
+ length);
+
+ length -= 1;
+ dmac_write(host->dma.io_port, DMAC_TXCNTLO, length);
+ dmac_write(host->dma.io_port, DMAC_TXCNTHI, length >> 8);
+ dmac_write(host->dma.io_port, DMAC_TXADRLO, address);
+ dmac_write(host->dma.io_port, DMAC_TXADRMD, address >> 8);
+ dmac_write(host->dma.io_port, DMAC_TXADRHI, 0);
+ dmac_write(host->dma.io_port, DMAC_MODECON, mode);
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);
+
+#if (DEBUG & DEBUG_DMA)
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));
+#endif
+ host->dma.xfer_setup = 1;
+ }
+}
+
+/*
+ * Function: void acornscsi_dma_cleanup(AS_Host *host)
+ * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
+ * Params : host - host to finish
+ * Notes : This is called when a command is:
+ * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT
+ * : This must not return until all transfers are completed.
+ */
+static
+void acornscsi_dma_cleanup(AS_Host *host)
+{
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
+ dmac_clearintr(host->dma.io_intr_clear);
+
+ /*
+ * Check for a pending transfer
+ */
+ if (host->dma.xfer_required) {
+ host->dma.xfer_required = 0;
+ if (host->dma.direction == DMA_IN)
+ acornscsi_data_read(host, host->dma.xfer_ptr,
+ host->dma.xfer_start, host->dma.xfer_length);
+ }
+
+ /*
+ * Has a transfer been setup?
+ */
+ if (host->dma.xfer_setup) {
+ unsigned int transferred;
+
+ host->dma.xfer_setup = 0;
+
+#if (DEBUG & DEBUG_DMA)
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi"));
+#endif
+
+ /*
+ * Calculate number of bytes transferred from DMA.
+ */
+ transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
+ host->dma.transferred += transferred;
+
+ if (host->dma.direction == DMA_IN)
+ acornscsi_data_read(host, host->scsi.SCp.ptr,
+ host->dma.start_addr, transferred);
+
+ /*
+ * Update SCSI pointers
+ */
+ acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
+#if (DEBUG & DEBUG_DMA)
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));
+#endif
+ }
+}
+
+/*
+ * Function: void acornscsi_dmacintr(AS_Host *host)
+ * Purpose : handle interrupts from DMAC device
+ * Params : host - host to process
+ * Notes : If reading, we schedule the read to main memory &
+ * allow the transfer to continue.
+ * : If writing, we fill the onboard DMA memory from main
+ * memory.
+ * : Called whenever DMAC finished it's current transfer.
+ */
+static
+void acornscsi_dma_intr(AS_Host *host)
+{
+ unsigned int address, length, transferred;
+
+#if (DEBUG & DEBUG_DMA)
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));
+#endif
+
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON);
+ dmac_clearintr(host->dma.io_intr_clear);
+
+ /*
+ * Calculate amount transferred via DMA
+ */
+ transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
+ host->dma.transferred += transferred;
+
+ /*
+ * Schedule DMA transfer off board
+ */
+ if (host->dma.direction == DMA_IN) {
+ host->dma.xfer_start = host->dma.start_addr;
+ host->dma.xfer_length = transferred;
+ host->dma.xfer_ptr = host->scsi.SCp.ptr;
+ host->dma.xfer_required = 1;
+ }
+
+ acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
+
+ /*
+ * Allocate some buffer space, limited to half the on-board RAM size
+ */
+ length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+ if (length) {
+ host->dma.start_addr = address = host->dma.free_addr;
+ host->dma.free_addr = (host->dma.free_addr + length) &
+ (DMAC_BUFFER_SIZE - 1);
+
+ /*
+ * Transfer data to DMA memory
+ */
+ if (host->dma.direction == DMA_OUT)
+ acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
+ length);
+
+ length -= 1;
+ dmac_write(host->dma.io_port, DMAC_TXCNTLO, length);
+ dmac_write(host->dma.io_port, DMAC_TXCNTHI, length >> 8);
+ dmac_write(host->dma.io_port, DMAC_TXADRLO, address);
+ dmac_write(host->dma.io_port, DMAC_TXADRMD, address >> 8);
+ dmac_write(host->dma.io_port, DMAC_TXADRHI, 0);
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);
+
+#if (DEBUG & DEBUG_DMA)
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));
+#endif
+ } else {
+ host->dma.xfer_setup = 0;
+#if 0
+ /*
+ * If the interface still wants more, then this is an error.
+ * We give it another byte, but we also attempt to raise an
+ * attention condition. We continue giving one byte until
+ * the device recognises the attention.
+ */
+ if (dmac_read(host->dma.io_port, DMAC_STATUS) & STATUS_RQ0) {
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+
+ dmac_write(host->dma.io_port, DMAC_TXCNTLO, 0);
+ dmac_write(host->dma.io_port, DMAC_TXCNTHI, 0);
+ dmac_write(host->dma.io_port, DMAC_TXADRLO, 0);
+ dmac_write(host->dma.io_port, DMAC_TXADRMD, 0);
+ dmac_write(host->dma.io_port, DMAC_TXADRHI, 0);
+ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);
+ }
+#endif
+ }
+}
+
+/*
+ * Function: void acornscsi_dma_xfer(AS_Host *host)
+ * Purpose : transfer data between AcornSCSI and memory
+ * Params : host - host to process
+ */
+static
+void acornscsi_dma_xfer(AS_Host *host)
+{
+ host->dma.xfer_required = 0;
+
+ if (host->dma.direction == DMA_IN)
+ acornscsi_data_read(host, host->dma.xfer_ptr,
+ host->dma.xfer_start, host->dma.xfer_length);
+}
+
+/*
+ * Function: void acornscsi_dma_adjust(AS_Host *host)
+ * Purpose : adjust DMA pointers & count for bytes transferred to
+ * SBIC but not SCSI bus.
+ * Params : host - host to adjust DMA count for
+ */
+static
+void acornscsi_dma_adjust(AS_Host *host)
+{
+ if (host->dma.xfer_setup) {
+ signed long transferred;
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));
+#endif
+ /*
+ * Calculate correct DMA address - DMA is ahead of SCSI bus while
+ * writing.
+ * host->scsi.SCp.scsi_xferred is the number of bytes
+ * actually transferred to/from the SCSI bus.
+ * host->dma.transferred is the number of bytes transferred
+ * over DMA since host->dma.start_addr was last set.
+ *
+ * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred
+ * - host->dma.transferred
+ */
+ transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;
+ if (transferred < 0)
+ printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
+ host->host->host_no, acornscsi_target(host), transferred);
+ else if (transferred == 0)
+ host->dma.xfer_setup = 0;
+ else {
+ transferred += host->dma.start_addr;
+ dmac_write(host->dma.io_port, DMAC_TXADRLO, transferred);
+ dmac_write(host->dma.io_port, DMAC_TXADRMD, transferred >> 8);
+ dmac_write(host->dma.io_port, DMAC_TXADRHI, transferred >> 16);
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));
+#endif
+ }
+ }
+}
+#endif
+
+/* =========================================================================================
+ * Data I/O
+ */
+static int
+acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)
+{
+ unsigned int asr, timeout = max_timeout;
+ int my_ptr = *ptr;
+
+ while (my_ptr < len) {
+ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+
+ if (asr & ASR_DBR) {
+ timeout = max_timeout;
+
+ sbic_arm_write(host->scsi.io_port, SBIC_DATA, bytes[my_ptr++]);
+ } else if (asr & ASR_INT)
+ break;
+ else if (--timeout == 0)
+ break;
+ udelay(1);
+ }
+
+ *ptr = my_ptr;
+
+ return (timeout == 0) ? -1 : 0;
+}
+
+/*
+ * Function: void acornscsi_sendcommand(AS_Host *host)
+ * Purpose : send a command to a target
+ * Params : host - host which is connected to target
+ */
+static void
+acornscsi_sendcommand(AS_Host *host)
+{
+ Scsi_Cmnd *SCpnt = host->SCpnt;
+
+ sbic_arm_write(host->scsi.io_port, SBIC_TRANSCNTH, 0);
+ sbic_arm_writenext(host->scsi.io_port, 0);
+ sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
+
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
+
+ if (acornscsi_write_pio(host, SCpnt->cmnd,
+ (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))
+ printk("scsi%d: timeout while sending command\n", host->host->host_no);
+
+ host->scsi.phase = PHASE_COMMAND;
+}
+
+static
+void acornscsi_sendmessage(AS_Host *host)
+{
+ unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);
+ unsigned int msgnr;
+ struct message *msg;
+
+#if (DEBUG & DEBUG_MESSAGES)
+ printk("scsi%d.%c: sending message ",
+ host->host->host_no, acornscsi_target(host));
+#endif
+
+ switch (message_length) {
+ case 0:
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");
+
+ sbic_arm_write(host->scsi.io_port, SBIC_DATA, NOP);
+
+ host->scsi.last_message = NOP;
+#if (DEBUG & DEBUG_MESSAGES)
+ printk("NOP");
+#endif
+ break;
+
+ case 1:
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+ msg = msgqueue_getmsg(&host->scsi.msgs, 0);
+
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");
+
+ sbic_arm_write(host->scsi.io_port, SBIC_DATA, msg->msg[0]);
+
+ host->scsi.last_message = msg->msg[0];
+#if (DEBUG & DEBUG_MESSAGES)
+ print_msg(msg->msg);
+#endif
+ break;
+
+ default:
+ /*
+ * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14)
+ * 'When a target sends this (MESSAGE_REJECT) message, it
+ * shall change to MESSAGE IN phase and send this message
+ * prior to requesting additional message bytes from the
+ * initiator. This provides an interlock so that the
+ * initiator can determine which message byte is rejected.
+ */
+ sbic_arm_write(host->scsi.io_port, SBIC_TRANSCNTH, 0);
+ sbic_arm_writenext(host->scsi.io_port, 0);
+ sbic_arm_writenext(host->scsi.io_port, message_length);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
+
+ msgnr = 0;
+ while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {
+ unsigned int i;
+#if (DEBUG & DEBUG_MESSAGES)
+ print_msg(msg);
+#endif
+ i = 0;
+ if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))
+ printk("scsi%d: timeout while sending message\n", host->host->host_no);
+
+ host->scsi.last_message = msg->msg[0];
+ if (msg->msg[0] == EXTENDED_MESSAGE)
+ host->scsi.last_message |= msg->msg[2] << 8;
+
+ if (i != msg->length)
+ break;
+ }
+ break;
+ }
+#if (DEBUG & DEBUG_MESSAGES)
+ printk("\n");
+#endif
+}
+
+/*
+ * Function: void acornscsi_readstatusbyte(AS_Host *host)
+ * Purpose : Read status byte from connected target
+ * Params : host - host connected to target
+ */
+static
+void acornscsi_readstatusbyte(AS_Host *host)
+{
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");
+ host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, SBIC_DATA);
+}
+
+/*
+ * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)
+ * Purpose : Read one message byte from connected target
+ * Params : host - host connected to target
+ */
+static
+unsigned char acornscsi_readmessagebyte(AS_Host *host)
+{
+ unsigned char message;
+
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");
+
+ message = sbic_arm_read(host->scsi.io_port, SBIC_DATA);
+
+ /* wait for MSGIN-XFER-PAUSED */
+ acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");
+
+ sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+
+ return message;
+}
+
+/*
+ * Function: void acornscsi_message(AS_Host *host)
+ * Purpose : Read complete message from connected target & action message
+ * Params : host - host connected to target
+ */
+static
+void acornscsi_message(AS_Host *host)
+{
+ unsigned char message[16];
+ unsigned int msgidx = 0, msglen = 1;
+
+ do {
+ message[msgidx] = acornscsi_readmessagebyte(host);
+
+ switch (msgidx) {
+ case 0:
+ if (message[0] == EXTENDED_MESSAGE ||
+ (message[0] >= 0x20 && message[0] <= 0x2f))
+ msglen = 2;
+ break;
+
+ case 1:
+ if (message[0] == EXTENDED_MESSAGE)
+ msglen += message[msgidx];
+ break;
+ }
+ msgidx += 1;
+ if (msgidx < msglen) {
+ acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
+
+ /* wait for next msg-in */
+ acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");
+ sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+ }
+ } while (msgidx < msglen);
+
+#if (DEBUG & DEBUG_MESSAGES)
+ printk("scsi%d.%c: message in: ",
+ host->host->host_no, acornscsi_target(host));
+ print_msg(message);
+ printk("\n");
+#endif
+
+ if (host->scsi.phase == PHASE_RECONNECTED) {
+ /*
+ * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
+ * 'Whenever a target reconnects to an initiator to continue
+ * a tagged I/O process, the SIMPLE QUEUE TAG message shall
+ * be sent immediately following the IDENTIFY message...'
+ */
+ if (message[0] == SIMPLE_QUEUE_TAG)
+ host->scsi.reconnected.tag = message[1];
+ if (acornscsi_reconnect_finish(host))
+ host->scsi.phase = PHASE_MSGIN;
+ }
+
+ switch (message[0]) {
+ case ABORT:
+ case ABORT_TAG:
+ case COMMAND_COMPLETE:
+ if (host->scsi.phase != PHASE_STATUSIN) {
+ printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
+ host->host->host_no, acornscsi_target(host));
+ acornscsi_dumplog(host, host->SCpnt->device->id);
+ }
+ host->scsi.phase = PHASE_DONE;
+ host->scsi.SCp.Message = message[0];
+ break;
+
+ case SAVE_POINTERS:
+ /*
+ * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20)
+ * 'The SAVE DATA POINTER message is sent from a target to
+ * direct the initiator to copy the active data pointer to
+ * the saved data pointer for the current I/O process.
+ */
+ acornscsi_dma_cleanup(host);
+ host->SCpnt->SCp = host->scsi.SCp;
+ host->SCpnt->SCp.sent_command = 0;
+ host->scsi.phase = PHASE_MSGIN;
+ break;
+
+ case RESTORE_POINTERS:
+ /*
+ * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19)
+ * 'The RESTORE POINTERS message is sent from a target to
+ * direct the initiator to copy the most recently saved
+ * command, data, and status pointers for the I/O process
+ * to the corresponding active pointers. The command and
+ * status pointers shall be restored to the beginning of
+ * the present command and status areas.'
+ */
+ acornscsi_dma_cleanup(host);
+ host->scsi.SCp = host->SCpnt->SCp;
+ host->scsi.phase = PHASE_MSGIN;
+ break;
+
+ case DISCONNECT:
+ /*
+ * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2)
+ * 'On those occasions when an error or exception condition occurs
+ * and the target elects to repeat the information transfer, the
+ * target may repeat the transfer either issuing a RESTORE POINTERS
+ * message or by disconnecting without issuing a SAVE POINTERS
+ * message. When reconnection is completed, the most recent
+ * saved pointer values are restored.'
+ */
+ acornscsi_dma_cleanup(host);
+ host->scsi.phase = PHASE_DISCONNECT;
+ break;
+
+ case MESSAGE_REJECT:
+#if 0 /* this isn't needed any more */
+ /*
+ * If we were negociating sync transfer, we don't yet know if
+ * this REJECT is for the sync transfer or for the tagged queue/wide
+ * transfer. Re-initiate sync transfer negociation now, and if
+ * we got a REJECT in response to SDTR, then it'll be set to DONE.
+ */
+ if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST)
+ host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE;
+#endif
+
+ /*
+ * If we have any messages waiting to go out, then assert ATN now
+ */
+ if (msgqueue_msglength(&host->scsi.msgs))
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+
+ switch (host->scsi.last_message) {
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ /*
+ * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
+ * If a target does not implement tagged queuing and a queue tag
+ * message is received, it shall respond with a MESSAGE REJECT
+ * message and accept the I/O process as if it were untagged.
+ */
+ printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
+ host->host->host_no, acornscsi_target(host));
+ host->SCpnt->device->simple_tags = 0;
+ set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns);
+ break;
+#endif
+ case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
+ /*
+ * Target can't handle synchronous transfers
+ */
+ printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
+ host->host->host_no, acornscsi_target(host));
+ host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA;
+ host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS;
+ sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case QUEUE_FULL:
+ /* TODO: target queue is full */
+ break;
+
+ case SIMPLE_QUEUE_TAG:
+ /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */
+ printk("scsi%d.%c: reconnect queue tag %02X\n",
+ host->host->host_no, acornscsi_target(host),
+ message[1]);
+ break;
+
+ case EXTENDED_MESSAGE:
+ switch (message[2]) {
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+ case EXTENDED_SDTR:
+ if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) {
+ /*
+ * We requested synchronous transfers. This isn't quite right...
+ * We can only say if this succeeded if we proceed on to execute the
+ * command from this message. If we get a MESSAGE PARITY ERROR,
+ * and the target retries fail, then we fallback to asynchronous mode
+ */
+ host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED;
+ printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
+ host->host->host_no, acornscsi_target(host),
+ message[4], message[3] * 4);
+ host->device[host->SCpnt->device->id].sync_xfer =
+ calc_sync_xfer(message[3] * 4, message[4]);
+ } else {
+ unsigned char period, length;
+ /*
+ * Target requested synchronous transfers. The agreement is only
+ * to be in operation AFTER the target leaves message out phase.
+ */
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ period = max_t(unsigned int, message[3], sdtr_period / 4);
+ length = min_t(unsigned int, message[4], sdtr_size);
+ msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
+ EXTENDED_SDTR, period, length);
+ host->device[host->SCpnt->device->id].sync_xfer =
+ calc_sync_xfer(period * 4, length);
+ }
+ sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
+ break;
+#else
+ /* We do not accept synchronous transfers. Respond with a
+ * MESSAGE_REJECT.
+ */
+#endif
+
+ case EXTENDED_WDTR:
+ /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT
+ * to a wide data transfer request.
+ */
+ default:
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ msgqueue_flush(&host->scsi.msgs);
+ msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
+ break;
+ }
+ break;
+
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /*
+ * We don't support linked commands yet
+ */
+ if (0) {
+#if (DEBUG & DEBUG_LINK)
+ printk("scsi%d.%c: lun %d tag %d linked command complete\n",
+ host->host->host_no, acornscsi_target(host), host->SCpnt->tag);
+#endif
+ /*
+ * A linked command should only terminate with one of these messages
+ * if there are more linked commands available.
+ */
+ if (!host->SCpnt->next_link) {
+ printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
+ instance->host_no, acornscsi_target(host), host->SCpnt->tag);
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
+ } else {
+ Scsi_Cmnd *SCpnt = host->SCpnt;
+
+ acornscsi_dma_cleanup(host);
+
+ host->SCpnt = host->SCpnt->next_link;
+ host->SCpnt->tag = SCpnt->tag;
+ SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
+ SCpnt->done(SCpnt);
+
+ /* initialise host->SCpnt->SCp */
+ }
+ break;
+ }
+#endif
+
+ default: /* reject message */
+ printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
+ host->host->host_no, acornscsi_target(host),
+ message[0]);
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ msgqueue_flush(&host->scsi.msgs);
+ msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
+ host->scsi.phase = PHASE_MSGIN;
+ break;
+ }
+ acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
+}
+
+/*
+ * Function: int acornscsi_buildmessages(AS_Host *host)
+ * Purpose : build the connection messages for a host
+ * Params : host - host to add messages to
+ */
+static
+void acornscsi_buildmessages(AS_Host *host)
+{
+#if 0
+ /* does the device need resetting? */
+ if (cmd_reset) {
+ msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);
+ return;
+ }
+#endif
+
+ msgqueue_addmsg(&host->scsi.msgs, 1,
+ IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok,
+ host->SCpnt->device->lun));
+
+#if 0
+ /* does the device need the current command aborted */
+ if (cmd_aborted) {
+ acornscsi_abortcmd(host->SCpnt->tag);
+ return;
+ }
+#endif
+
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+ if (host->SCpnt->tag) {
+ unsigned int tag_type;
+
+ if (host->SCpnt->cmnd[0] == REQUEST_SENSE ||
+ host->SCpnt->cmnd[0] == TEST_UNIT_READY ||
+ host->SCpnt->cmnd[0] == INQUIRY)
+ tag_type = HEAD_OF_QUEUE_TAG;
+ else
+ tag_type = SIMPLE_QUEUE_TAG;
+ msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
+ }
+#endif
+
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+ if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) {
+ host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST;
+ msgqueue_addmsg(&host->scsi.msgs, 5,
+ EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+ sdtr_period / 4, sdtr_size);
+ }
+#endif
+}
+
+/*
+ * Function: int acornscsi_starttransfer(AS_Host *host)
+ * Purpose : transfer data to/from connected target
+ * Params : host - host to which target is connected
+ * Returns : 0 if failure
+ */
+static
+int acornscsi_starttransfer(AS_Host *host)
+{
+ int residual;
+
+ if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
+ printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
+ host->host->host_no, acornscsi_target(host));
+ return 0;
+ }
+
+ residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred;
+
+ sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);
+ sbic_arm_writenext(host->scsi.io_port, residual >> 16);
+ sbic_arm_writenext(host->scsi.io_port, residual >> 8);
+ sbic_arm_writenext(host->scsi.io_port, residual);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
+ return 1;
+}
+
+/* =========================================================================================
+ * Connection & Disconnection
+ */
+/*
+ * Function : acornscsi_reconnect(AS_Host *host)
+ * Purpose : reconnect a previously disconnected command
+ * Params : host - host specific data
+ * Remarks : SCSI spec says:
+ * 'The set of active pointers is restored from the set
+ * of saved pointers upon reconnection of the I/O process'
+ */
+static
+int acornscsi_reconnect(AS_Host *host)
+{
+ unsigned int target, lun, ok = 0;
+
+ target = sbic_arm_read(host->scsi.io_port, SBIC_SOURCEID);
+
+ if (!(target & 8))
+ printk(KERN_ERR "scsi%d: invalid source id after reselection "
+ "- device fault?\n",
+ host->host->host_no);
+
+ target &= 7;
+
+ if (host->SCpnt && !host->scsi.disconnectable) {
+ printk(KERN_ERR "scsi%d.%d: reconnected while command in "
+ "progress to target %d?\n",
+ host->host->host_no, target, host->SCpnt->device->id);
+ host->SCpnt = NULL;
+ }
+
+ lun = sbic_arm_read(host->scsi.io_port, SBIC_DATA) & 7;
+
+ host->scsi.reconnected.target = target;
+ host->scsi.reconnected.lun = lun;
+ host->scsi.reconnected.tag = 0;
+
+ if (host->scsi.disconnectable && host->SCpnt &&
+ host->SCpnt->device->id == target && host->SCpnt->device->lun == lun)
+ ok = 1;
+
+ if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))
+ ok = 1;
+
+ ADD_STATUS(target, 0x81, host->scsi.phase, 0);
+
+ if (ok) {
+ host->scsi.phase = PHASE_RECONNECTED;
+ } else {
+ /* this doesn't seem to work */
+ printk(KERN_ERR "scsi%d.%c: reselected with no command "
+ "to reconnect with\n",
+ host->host->host_no, '0' + target);
+ acornscsi_dumplog(host, target);
+ acornscsi_abortcmd(host, 0);
+ if (host->SCpnt) {
+ queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
+ host->SCpnt = NULL;
+ }
+ }
+ acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
+ return !ok;
+}
+
+/*
+ * Function: int acornscsi_reconect_finish(AS_Host *host)
+ * Purpose : finish reconnecting a command
+ * Params : host - host to complete
+ * Returns : 0 if failed
+ */
+static
+int acornscsi_reconnect_finish(AS_Host *host)
+{
+ if (host->scsi.disconnectable && host->SCpnt) {
+ host->scsi.disconnectable = 0;
+ if (host->SCpnt->device->id == host->scsi.reconnected.target &&
+ host->SCpnt->device->lun == host->scsi.reconnected.lun &&
+ host->SCpnt->tag == host->scsi.reconnected.tag) {
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+ DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
+ host->host->host_no, acornscsi_target(host)));
+#endif
+ } else {
+ queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+ DBG(host->SCpnt, printk("scsi%d.%c: had to move command "
+ "to disconnected queue\n",
+ host->host->host_no, acornscsi_target(host)));
+#endif
+ host->SCpnt = NULL;
+ }
+ }
+ if (!host->SCpnt) {
+ host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,
+ host->scsi.reconnected.target,
+ host->scsi.reconnected.lun,
+ host->scsi.reconnected.tag);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+ DBG(host->SCpnt, printk("scsi%d.%c: had to get command",
+ host->host->host_no, acornscsi_target(host)));
+#endif
+ }
+
+ if (!host->SCpnt)
+ acornscsi_abortcmd(host, host->scsi.reconnected.tag);
+ else {
+ /*
+ * Restore data pointer from SAVED pointers.
+ */
+ host->scsi.SCp = host->SCpnt->SCp;
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+ printk(", data pointers: [%p, %X]",
+ host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
+#endif
+ }
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+ printk("\n");
+#endif
+
+ host->dma.transferred = host->scsi.SCp.scsi_xferred;
+
+ return host->SCpnt != NULL;
+}
+
+/*
+ * Function: void acornscsi_disconnect_unexpected(AS_Host *host)
+ * Purpose : handle an unexpected disconnect
+ * Params : host - host on which disconnect occurred
+ */
+static
+void acornscsi_disconnect_unexpected(AS_Host *host)
+{
+ printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",
+ host->host->host_no, acornscsi_target(host));
+#if (DEBUG & DEBUG_ABORT)
+ acornscsi_dumplog(host, 8);
+#endif
+
+ acornscsi_done(host, &host->SCpnt, DID_ERROR);
+}
+
+/*
+ * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
+ * Purpose : abort a currently executing command
+ * Params : host - host with connected command to abort
+ * tag - tag to abort
+ */
+static
+void acornscsi_abortcmd(AS_Host *host, unsigned char tag)
+{
+ host->scsi.phase = PHASE_ABORTED;
+ sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_ASSERTATN);
+
+ msgqueue_flush(&host->scsi.msgs);
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+ if (tag)
+ msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
+ else
+#endif
+ msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
+}
+
+/* ==========================================================================================
+ * Interrupt routines.
+ */
+/*
+ * Function: int acornscsi_sbicintr(AS_Host *host)
+ * Purpose : handle interrupts from SCSI device
+ * Params : host - host to process
+ * Returns : INTR_PROCESS if expecting another SBIC interrupt
+ * INTR_IDLE if no interrupt
+ * INTR_NEXT_COMMAND if we have finished processing the command
+ */
+static
+intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
+{
+ unsigned int asr, ssr;
+
+ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+ if (!(asr & ASR_INT))
+ return INTR_IDLE;
+
+ ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+
+#if (DEBUG & DEBUG_PHASES)
+ print_sbic_status(asr, ssr, host->scsi.phase);
+#endif
+
+ ADD_STATUS(8, ssr, host->scsi.phase, in_irq);
+
+ if (host->SCpnt && !host->scsi.disconnectable)
+ ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
+
+ switch (ssr) {
+ case 0x00: /* reset state - not advanced */
+ printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
+ host->host->host_no);
+ /* setup sbic - WD33C93A */
+ sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id);
+ sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET);
+ return INTR_IDLE;
+
+ case 0x01: /* reset state - advanced */
+ sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);
+ sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME);
+ sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+ sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+ msgqueue_flush(&host->scsi.msgs);
+ return INTR_IDLE;
+
+ case 0x41: /* unexpected disconnect aborted command */
+ acornscsi_disconnect_unexpected(host);
+ return INTR_NEXT_COMMAND;
+ }
+
+ switch (host->scsi.phase) {
+ case PHASE_CONNECTING: /* STATE: command removed from issue queue */
+ switch (ssr) {
+ case 0x11: /* -> PHASE_CONNECTED */
+ /* BUS FREE -> SELECTION */
+ host->scsi.phase = PHASE_CONNECTED;
+ msgqueue_flush(&host->scsi.msgs);
+ host->dma.transferred = host->scsi.SCp.scsi_xferred;
+ /* 33C93 gives next interrupt indicating bus phase */
+ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+ if (!(asr & ASR_INT))
+ break;
+ ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+ ADD_STATUS(8, ssr, host->scsi.phase, 1);
+ ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1);
+ goto connected;
+
+ case 0x42: /* select timed out */
+ /* -> PHASE_IDLE */
+ acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
+ return INTR_NEXT_COMMAND;
+
+ case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */
+ /* BUS FREE -> RESELECTION */
+ host->origSCpnt = host->SCpnt;
+ host->SCpnt = NULL;
+ msgqueue_flush(&host->scsi.msgs);
+ acornscsi_reconnect(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ }
+ return INTR_PROCESSING;
+
+ connected:
+ case PHASE_CONNECTED: /* STATE: device selected ok */
+ switch (ssr) {
+#ifdef NONSTANDARD
+ case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
+ /* SELECTION -> COMMAND */
+ acornscsi_sendcommand(host);
+ break;
+
+ case 0x8b: /* -> PHASE_STATUS */
+ /* SELECTION -> STATUS */
+ acornscsi_readstatusbyte(host);
+ host->scsi.phase = PHASE_STATUSIN;
+ break;
+#endif
+
+ case 0x8e: /* -> PHASE_MSGOUT */
+ /* SELECTION ->MESSAGE OUT */
+ host->scsi.phase = PHASE_MSGOUT;
+ acornscsi_buildmessages(host);
+ acornscsi_sendmessage(host);
+ break;
+
+ /* these should not happen */
+ case 0x85: /* target disconnected */
+ acornscsi_done(host, &host->SCpnt, DID_ERROR);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */
+ /*
+ * SCSI standard says that MESSAGE OUT phases can be followed by a
+ * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
+ */
+ switch (ssr) {
+ case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
+ case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
+ /* MESSAGE OUT -> COMMAND */
+ acornscsi_sendcommand(host);
+ break;
+
+ case 0x8b: /* -> PHASE_STATUS */
+ case 0x1b: /* -> PHASE_STATUS */
+ /* MESSAGE OUT -> STATUS */
+ acornscsi_readstatusbyte(host);
+ host->scsi.phase = PHASE_STATUSIN;
+ break;
+
+ case 0x8e: /* -> PHASE_MSGOUT */
+ /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
+ acornscsi_sendmessage(host);
+ break;
+
+ case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ /* MESSAGE OUT -> MESSAGE IN */
+ acornscsi_message(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_COMMAND: /* STATE: connected & command sent */
+ switch (ssr) {
+ case 0x18: /* -> PHASE_DATAOUT */
+ /* COMMAND -> DATA OUT */
+ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_dma_setup(host, DMA_OUT);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ host->scsi.phase = PHASE_DATAOUT;
+ return INTR_IDLE;
+
+ case 0x19: /* -> PHASE_DATAIN */
+ /* COMMAND -> DATA IN */
+ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_dma_setup(host, DMA_IN);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ host->scsi.phase = PHASE_DATAIN;
+ return INTR_IDLE;
+
+ case 0x1b: /* -> PHASE_STATUS */
+ /* COMMAND -> STATUS */
+ acornscsi_readstatusbyte(host);
+ host->scsi.phase = PHASE_STATUSIN;
+ break;
+
+ case 0x1e: /* -> PHASE_MSGOUT */
+ /* COMMAND -> MESSAGE OUT */
+ acornscsi_sendmessage(host);
+ break;
+
+ case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ /* COMMAND -> MESSAGE IN */
+ acornscsi_message(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */
+ if (ssr == 0x85) { /* -> PHASE_IDLE */
+ host->scsi.disconnectable = 1;
+ host->scsi.reconnected.tag = 0;
+ host->scsi.phase = PHASE_IDLE;
+ host->stats.disconnects += 1;
+ } else {
+ printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_NEXT_COMMAND;
+
+ case PHASE_IDLE: /* STATE: disconnected */
+ if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */
+ acornscsi_reconnect(host);
+ else {
+ printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */
+ /*
+ * Command reconnected - if MESGIN, get message - it may be
+ * the tag. If not, get command out of disconnected queue
+ */
+ /*
+ * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
+ * reconnect I_T_L command
+ */
+ if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
+ return INTR_IDLE;
+ ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);
+ switch (ssr) {
+ case 0x88: /* data out phase */
+ /* -> PHASE_DATAOUT */
+ /* MESSAGE IN -> DATA OUT */
+ acornscsi_dma_setup(host, DMA_OUT);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ host->scsi.phase = PHASE_DATAOUT;
+ return INTR_IDLE;
+
+ case 0x89: /* data in phase */
+ /* -> PHASE_DATAIN */
+ /* MESSAGE IN -> DATA IN */
+ acornscsi_dma_setup(host, DMA_IN);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ host->scsi.phase = PHASE_DATAIN;
+ return INTR_IDLE;
+
+ case 0x8a: /* command out */
+ /* MESSAGE IN -> COMMAND */
+ acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
+ break;
+
+ case 0x8b: /* status in */
+ /* -> PHASE_STATUSIN */
+ /* MESSAGE IN -> STATUS */
+ acornscsi_readstatusbyte(host);
+ host->scsi.phase = PHASE_STATUSIN;
+ break;
+
+ case 0x8e: /* message out */
+ /* -> PHASE_MSGOUT */
+ /* MESSAGE IN -> MESSAGE OUT */
+ acornscsi_sendmessage(host);
+ break;
+
+ case 0x8f: /* message in */
+ acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_DATAIN: /* STATE: transferred data in */
+ /*
+ * This is simple - if we disconnect then the DMA address & count is
+ * correct.
+ */
+ switch (ssr) {
+ case 0x19: /* -> PHASE_DATAIN */
+ case 0x89: /* -> PHASE_DATAIN */
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ return INTR_IDLE;
+
+ case 0x1b: /* -> PHASE_STATUSIN */
+ case 0x4b: /* -> PHASE_STATUSIN */
+ case 0x8b: /* -> PHASE_STATUSIN */
+ /* DATA IN -> STATUS */
+ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_readstatusbyte(host);
+ host->scsi.phase = PHASE_STATUSIN;
+ break;
+
+ case 0x1e: /* -> PHASE_MSGOUT */
+ case 0x4e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
+ /* DATA IN -> MESSAGE OUT */
+ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_sendmessage(host);
+ break;
+
+ case 0x1f: /* message in */
+ case 0x4f: /* message in */
+ case 0x8f: /* message in */
+ /* DATA IN -> MESSAGE IN */
+ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_DATAOUT: /* STATE: transferred data out */
+ /*
+ * This is more complicated - if we disconnect, the DMA could be 12
+ * bytes ahead of us. We need to correct this.
+ */
+ switch (ssr) {
+ case 0x18: /* -> PHASE_DATAOUT */
+ case 0x88: /* -> PHASE_DATAOUT */
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ return INTR_IDLE;
+
+ case 0x1b: /* -> PHASE_STATUSIN */
+ case 0x4b: /* -> PHASE_STATUSIN */
+ case 0x8b: /* -> PHASE_STATUSIN */
+ /* DATA OUT -> STATUS */
+ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_dma_adjust(host);
+ acornscsi_readstatusbyte(host);
+ host->scsi.phase = PHASE_STATUSIN;
+ break;
+
+ case 0x1e: /* -> PHASE_MSGOUT */
+ case 0x4e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
+ /* DATA OUT -> MESSAGE OUT */
+ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_dma_adjust(host);
+ acornscsi_sendmessage(host);
+ break;
+
+ case 0x1f: /* message in */
+ case 0x4f: /* message in */
+ case 0x8f: /* message in */
+ /* DATA OUT -> MESSAGE IN */
+ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_dma_adjust(host);
+ acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_STATUSIN: /* STATE: status in complete */
+ switch (ssr) {
+ case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+ case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+ /* STATUS -> MESSAGE IN */
+ acornscsi_message(host);
+ break;
+
+ case 0x1e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
+ /* STATUS -> MESSAGE OUT */
+ acornscsi_sendmessage(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_MSGIN: /* STATE: message in */
+ switch (ssr) {
+ case 0x1e: /* -> PHASE_MSGOUT */
+ case 0x4e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
+ /* MESSAGE IN -> MESSAGE OUT */
+ acornscsi_sendmessage(host);
+ break;
+
+ case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+ case 0x2f:
+ case 0x4f:
+ case 0x8f:
+ acornscsi_message(host);
+ break;
+
+ case 0x85:
+ printk("scsi%d.%c: strange message in disconnection\n",
+ host->host->host_no, acornscsi_target(host));
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ acornscsi_done(host, &host->SCpnt, DID_ERROR);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_DONE: /* STATE: received status & message */
+ switch (ssr) {
+ case 0x85: /* -> PHASE_IDLE */
+ acornscsi_done(host, &host->SCpnt, DID_OK);
+ return INTR_NEXT_COMMAND;
+
+ case 0x1e:
+ case 0x8e:
+ acornscsi_sendmessage(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ case PHASE_ABORTED:
+ switch (ssr) {
+ case 0x85:
+ if (host->SCpnt)
+ acornscsi_done(host, &host->SCpnt, DID_ABORT);
+ else {
+ clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
+ host->busyluns);
+ host->scsi.phase = PHASE_IDLE;
+ }
+ return INTR_NEXT_COMMAND;
+
+ case 0x1e:
+ case 0x2e:
+ case 0x4e:
+ case 0x8e:
+ acornscsi_sendmessage(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);
+ }
+ return INTR_PROCESSING;
+}
+
+/*
+ * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
+ * Purpose : handle interrupts from Acorn SCSI card
+ * Params : irq - interrupt number
+ * dev_id - device specific data (AS_Host structure)
+ * regs - processor registers when interrupt occurred
+ */
+static irqreturn_t
+acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ AS_Host *host = (AS_Host *)dev_id;
+ intr_ret_t ret;
+ int iostatus;
+ int in_irq = 0;
+
+ do {
+ ret = INTR_IDLE;
+
+ iostatus = inb(host->card.io_intr);
+
+ if (iostatus & 2) {
+ acornscsi_dma_intr(host);
+ iostatus = inb(host->card.io_intr);
+ }
+
+ if (iostatus & 8)
+ ret = acornscsi_sbicintr(host, in_irq);
+
+ /*
+ * If we have a transfer pending, start it.
+ * Only start it if the interface has already started transferring
+ * it's data
+ */
+ if (host->dma.xfer_required)
+ acornscsi_dma_xfer(host);
+
+ if (ret == INTR_NEXT_COMMAND)
+ ret = acornscsi_kick(host);
+
+ in_irq = 1;
+ } while (ret != INTR_IDLE);
+
+ return IRQ_HANDLED;
+}
+
+/*=============================================================================================
+ * Interfaces between interrupt handler and rest of scsi code
+ */
+
+/*
+ * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+ * Purpose : queues a SCSI command
+ * Params : cmd - SCSI command
+ * done - function called on completion, with pointer to command descriptor
+ * Returns : 0, or < 0 on error.
+ */
+int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
+
+ if (!done) {
+ /* there should be some way of rejecting errors like this without panicing... */
+ panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
+ host->host->host_no, SCpnt);
+ return -EINVAL;
+ }
+
+#if (DEBUG & DEBUG_NO_WRITE)
+ if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) {
+ printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
+ host->host->host_no, '0' + SCpnt->device->id);
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ }
+#endif
+
+ SCpnt->scsi_done = done;
+ SCpnt->host_scribble = NULL;
+ SCpnt->result = 0;
+ SCpnt->tag = 0;
+ SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
+ SCpnt->SCp.sent_command = 0;
+ SCpnt->SCp.scsi_xferred = 0;
+
+ init_SCp(SCpnt);
+
+ host->stats.queues += 1;
+
+ {
+ unsigned long flags;
+
+ if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
+ SCpnt->result = DID_ERROR << 16;
+ done(SCpnt);
+ return 0;
+ }
+ local_irq_save(flags);
+ if (host->scsi.phase == PHASE_IDLE)
+ acornscsi_kick(host);
+ local_irq_restore(flags);
+ }
+ return 0;
+}
+
+/*
+ * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+ * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
+ * Params : SCpntp1 - pointer to command to return
+ * SCpntp2 - pointer to command to check
+ * result - result to pass back to mid-level done function
+ * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
+ */
+static inline
+void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+{
+ Scsi_Cmnd *SCpnt = *SCpntp1;
+
+ if (SCpnt) {
+ *SCpntp1 = NULL;
+
+ SCpnt->result = result;
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ if (SCpnt == *SCpntp2)
+ *SCpntp2 = NULL;
+}
+
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
+/*
+ * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt)
+ * Purpose : abort a command on this host
+ * Params : SCpnt - command to abort
+ * Returns : our abort status
+ */
+static enum res_abort
+acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt)
+{
+ enum res_abort res = res_not_running;
+
+ if (queue_remove_cmd(&host->queues.issue, SCpnt)) {
+ /*
+ * The command was on the issue queue, and has not been
+ * issued yet. We can remove the command from the queue,
+ * and acknowledge the abort. Neither the devices nor the
+ * interface know about the command.
+ */
+//#if (DEBUG & DEBUG_ABORT)
+ printk("on issue queue ");
+//#endif
+ res = res_success;
+ } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) {
+ /*
+ * The command was on the disconnected queue. Simply
+ * acknowledge the abort condition, and when the target
+ * reconnects, we will give it an ABORT message. The
+ * target should then disconnect, and we will clear
+ * the busylun bit.
+ */
+//#if (DEBUG & DEBUG_ABORT)
+ printk("on disconnected queue ");
+//#endif
+ res = res_success;
+ } else if (host->SCpnt == SCpnt) {
+ unsigned long flags;
+
+//#if (DEBUG & DEBUG_ABORT)
+ printk("executing ");
+//#endif
+
+ local_irq_save(flags);
+ switch (host->scsi.phase) {
+ /*
+ * If the interface is idle, and the command is 'disconnectable',
+ * then it is the same as on the disconnected queue. We simply
+ * remove all traces of the command. When the target reconnects,
+ * we will give it an ABORT message since the command could not
+ * be found. When the target finally disconnects, we will clear
+ * the busylun bit.
+ */
+ case PHASE_IDLE:
+ if (host->scsi.disconnectable) {
+ host->scsi.disconnectable = 0;
+ host->SCpnt = NULL;
+ res = res_success;
+ }
+ break;
+
+ /*
+ * If the command has connected and done nothing further,
+ * simply force a disconnect. We also need to clear the
+ * busylun bit.
+ */
+ case PHASE_CONNECTED:
+ sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_DISCONNECT);
+ host->SCpnt = NULL;
+ res = res_success_clear;
+ break;
+
+ default:
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ res = res_snooze;
+ }
+ local_irq_restore(flags);
+ } else if (host->origSCpnt == SCpnt) {
+ /*
+ * The command will be executed next, but a command
+ * is currently using the interface. This is similar to
+ * being on the issue queue, except the busylun bit has
+ * been set.
+ */
+ host->origSCpnt = NULL;
+//#if (DEBUG & DEBUG_ABORT)
+ printk("waiting for execution ");
+//#endif
+ res = res_success_clear;
+ } else
+ printk("unknown ");
+
+ return res;
+}
+
+/*
+ * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt)
+ * Purpose : abort a command on this host
+ * Params : SCpnt - command to abort
+ * Returns : one of SCSI_ABORT_ macros
+ */
+int acornscsi_abort(Scsi_Cmnd *SCpnt)
+{
+ AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata;
+ int result;
+
+ host->stats.aborts += 1;
+
+#if (DEBUG & DEBUG_ABORT)
+ {
+ int asr, ssr;
+ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+ ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+
+ printk(KERN_WARNING "acornscsi_abort: ");
+ print_sbic_status(asr, ssr, host->scsi.phase);
+ acornscsi_dumplog(host, SCpnt->device->id);
+ }
+#endif
+
+ printk("scsi%d: ", host->host->host_no);
+
+ switch (acornscsi_do_abort(host, SCpnt)) {
+ /*
+ * We managed to find the command and cleared it out.
+ * We do not expect the command to be executing on the
+ * target, but we have set the busylun bit.
+ */
+ case res_success_clear:
+//#if (DEBUG & DEBUG_ABORT)
+ printk("clear ");
+//#endif
+ clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns);
+
+ /*
+ * We found the command, and cleared it out. Either
+ * the command is still known to be executing on the
+ * target, or the busylun bit is not set.
+ */
+ case res_success:
+//#if (DEBUG & DEBUG_ABORT)
+ printk("success\n");
+//#endif
+ SCpnt->result = DID_ABORT << 16;
+ SCpnt->scsi_done(SCpnt);
+ result = SCSI_ABORT_SUCCESS;
+ break;
+
+ /*
+ * We did find the command, but unfortunately we couldn't
+ * unhook it from ourselves. Wait some more, and if it
+ * still doesn't complete, reset the interface.
+ */
+ case res_snooze:
+//#if (DEBUG & DEBUG_ABORT)
+ printk("snooze\n");
+//#endif
+ result = SCSI_ABORT_SNOOZE;
+ break;
+
+ /*
+ * The command could not be found (either because it completed,
+ * or it got dropped.
+ */
+ default:
+ case res_not_running:
+ acornscsi_dumplog(host, SCpnt->device->id);
+#if (DEBUG & DEBUG_ABORT)
+ result = SCSI_ABORT_SNOOZE;
+#else
+ result = SCSI_ABORT_NOT_RUNNING;
+#endif
+//#if (DEBUG & DEBUG_ABORT)
+ printk("not running\n");
+//#endif
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ * Purpose : reset a command on this host/reset this host
+ * Params : SCpnt - command causing reset
+ * result - what type of reset to perform
+ * Returns : one of SCSI_RESET_ macros
+ */
+int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+ AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;
+ Scsi_Cmnd *SCptr;
+
+ host->stats.resets += 1;
+
+#if (DEBUG & DEBUG_RESET)
+ {
+ int asr, ssr;
+
+ asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR);
+ ssr = sbic_arm_read(host->scsi.io_port, SBIC_SSR);
+
+ printk(KERN_WARNING "acornscsi_reset: ");
+ print_sbic_status(asr, ssr, host->scsi.phase);
+ acornscsi_dumplog(host, SCpnt->device->id);
+ }
+#endif
+
+ acornscsi_dma_stop(host);
+
+ SCptr = host->SCpnt;
+
+ /*
+ * do hard reset. This resets all devices on this host, and so we
+ * must set the reset status on all commands.
+ */
+ acornscsi_resetcard(host);
+
+ /*
+ * report reset on commands current connected/disconnected
+ */
+ acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET);
+
+ while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)
+ acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET);
+
+ if (SCpnt) {
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS;
+}
+
+/*==============================================================================================
+ * initialisation & miscellaneous support
+ */
+
+/*
+ * Function: char *acornscsi_info(struct Scsi_Host *host)
+ * Purpose : return a string describing this interface
+ * Params : host - host to give information on
+ * Returns : a constant string
+ */
+const
+char *acornscsi_info(struct Scsi_Host *host)
+{
+ static char string[100], *p;
+
+ p = string;
+
+ p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d"
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+ " SYNC"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+ " TAG"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+ " LINK"
+#endif
+#if (DEBUG & DEBUG_NO_WRITE)
+ " NOWRITE ("NO_WRITE_STR")"
+#endif
+ , host->hostt->name, host->io_port, host->irq,
+ VER_MAJOR, VER_MINOR, VER_PATCH);
+ return string;
+}
+
+int acornscsi_proc_info(struct Scsi_Host *instance, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ int pos, begin = 0, devidx;
+ Scsi_Device *scd;
+ AS_Host *host;
+ char *p = buffer;
+
+ if (inout == 1)
+ return -EINVAL;
+
+ host = (AS_Host *)instance->hostdata;
+
+ p += sprintf(p, "AcornSCSI driver v%d.%d.%d"
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+ " SYNC"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+ " TAG"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+ " LINK"
+#endif
+#if (DEBUG & DEBUG_NO_WRITE)
+ " NOWRITE ("NO_WRITE_STR")"
+#endif
+ "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
+
+ p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n",
+ host->scsi.io_port, host->scsi.irq);
+#ifdef USE_DMAC
+ p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n",
+ host->dma.io_port, host->scsi.irq);
+#endif
+
+ p += sprintf(p, "Statistics:\n"
+ "Queued commands: %-10u Issued commands: %-10u\n"
+ "Done commands : %-10u Reads : %-10u\n"
+ "Writes : %-10u Others : %-10u\n"
+ "Disconnects : %-10u Aborts : %-10u\n"
+ "Resets : %-10u\n\nLast phases:",
+ host->stats.queues, host->stats.removes,
+ host->stats.fins, host->stats.reads,
+ host->stats.writes, host->stats.miscs,
+ host->stats.disconnects, host->stats.aborts,
+ host->stats.resets);
+
+ for (devidx = 0; devidx < 9; devidx ++) {
+ unsigned int statptr, prev;
+
+ p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
+ statptr = host->status_ptr[devidx] - 10;
+
+ if ((signed int)statptr < 0)
+ statptr += STATUS_BUFFER_SIZE;
+
+ prev = host->status[devidx][statptr].when;
+
+ for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+ if (host->status[devidx][statptr].when) {
+ p += sprintf(p, "%c%02X:%02X+%2ld",
+ host->status[devidx][statptr].irq ? '-' : ' ',
+ host->status[devidx][statptr].ph,
+ host->status[devidx][statptr].ssr,
+ (host->status[devidx][statptr].when - prev) < 100 ?
+ (host->status[devidx][statptr].when - prev) : 99);
+ prev = host->status[devidx][statptr].when;
+ }
+ }
+ }
+
+ p += sprintf(p, "\nAttached devices:\n");
+
+ shost_for_each_device(scd, instance) {
+ p += sprintf(p, "Device/Lun TaggedQ Sync\n");
+ p += sprintf(p, " %d/%d ", scd->id, scd->lun);
+ if (scd->tagged_supported)
+ p += sprintf(p, "%3sabled(%3d) ",
+ scd->simple_tags ? "en" : "dis",
+ scd->current_tag);
+ else
+ p += sprintf(p, "unsupported ");
+
+ if (host->device[scd->id].sync_xfer & 15)
+ p += sprintf(p, "offset %d, %d ns\n",
+ host->device[scd->id].sync_xfer & 15,
+ acornscsi_getperiod(host->device[scd->id].sync_xfer));
+ else
+ p += sprintf(p, "async\n");
+
+ pos = p - buffer;
+ if (pos + begin < offset) {
+ begin += pos;
+ p = buffer;
+ }
+ pos = p - buffer;
+ if (pos + begin > offset + length) {
+ scsi_device_put(scd);
+ break;
+ }
+ }
+
+ pos = p - buffer;
+
+ *start = buffer + (offset - begin);
+ pos -= offset - begin;
+
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+static Scsi_Host_Template acornscsi_template = {
+ .module = THIS_MODULE,
+ .proc_info = acornscsi_proc_info,
+ .name = "AcornSCSI",
+ .info = acornscsi_info,
+ .queuecommand = acornscsi_queuecmd,
+#warning fixme
+ .abort = acornscsi_abort,
+ .reset = acornscsi_reset,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .unchecked_isa_dma = 0,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "acornscsi",
+};
+
+static int __devinit
+acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ AS_Host *ashost;
+ int ret = -ENOMEM;
+
+ host = scsi_host_alloc(&acornscsi_template, sizeof(AS_Host));
+ if (!host)
+ goto out;
+
+ ashost = (AS_Host *)host->hostdata;
+
+ host->io_port = ecard_address(ec, ECARD_MEMC, 0);
+ host->irq = ec->irq;
+
+ ashost->host = host;
+ ashost->scsi.io_port = ioaddr(host->io_port + 0x800);
+ ashost->scsi.irq = host->irq;
+ ashost->card.io_intr = POD_SPACE(host->io_port) + 0x800;
+ ashost->card.io_page = POD_SPACE(host->io_port) + 0xc00;
+ ashost->card.io_ram = ioaddr(host->io_port);
+ ashost->dma.io_port = host->io_port + 0xc00;
+ ashost->dma.io_intr_clear = POD_SPACE(host->io_port) + 0x800;
+
+ ec->irqaddr = (char *)ioaddr(ashost->card.io_intr);
+ ec->irqmask = 0x0a;
+
+ ret = -EBUSY;
+ if (!request_region(host->io_port + 0x800, 2, "acornscsi(sbic)"))
+ goto err_1;
+ if (!request_region(ashost->card.io_intr, 1, "acornscsi(intr)"))
+ goto err_2;
+ if (!request_region(ashost->card.io_page, 1, "acornscsi(page)"))
+ goto err_3;
+#ifdef USE_DMAC
+ if (!request_region(ashost->dma.io_port, 256, "acornscsi(dmac)"))
+ goto err_4;
+#endif
+ if (!request_region(host->io_port, 2048, "acornscsi(ram)"))
+ goto err_5;
+
+ ret = request_irq(host->irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", ashost);
+ if (ret) {
+ printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n",
+ host->host_no, ashost->scsi.irq, ret);
+ goto err_6;
+ }
+
+ memset(&ashost->stats, 0, sizeof (ashost->stats));
+ queue_initialise(&ashost->queues.issue);
+ queue_initialise(&ashost->queues.disconnected);
+ msgqueue_initialise(&ashost->scsi.msgs);
+
+ acornscsi_resetcard(ashost);
+
+ ret = scsi_add_host(host, &ec->dev);
+ if (ret)
+ goto err_7;
+
+ scsi_scan_host(host);
+ goto out;
+
+ err_7:
+ free_irq(host->irq, ashost);
+ err_6:
+ release_region(host->io_port, 2048);
+ err_5:
+#ifdef USE_DMAC
+ release_region(ashost->dma.io_port, 256);
+#endif
+ err_4:
+ release_region(ashost->card.io_page, 1);
+ err_3:
+ release_region(ashost->card.io_intr, 1);
+ err_2:
+ release_region(host->io_port + 0x800, 2);
+ err_1:
+ scsi_host_put(host);
+ out:
+ return ret;
+}
+
+static void __devexit acornscsi_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ AS_Host *ashost = (AS_Host *)host->hostdata;
+
+ ecard_set_drvdata(ec, NULL);
+ scsi_remove_host(host);
+
+ /*
+ * Put card into RESET state
+ */
+ outb(0x80, ashost->card.io_page);
+
+ free_irq(host->irq, ashost);
+
+ release_region(host->io_port + 0x800, 2);
+ release_region(ashost->card.io_intr, 1);
+ release_region(ashost->card.io_page, 1);
+ release_region(ashost->dma.io_port, 256);
+ release_region(host->io_port, 2048);
+
+ msgqueue_free(&ashost->scsi.msgs);
+ queue_free(&ashost->queues.disconnected);
+ queue_free(&ashost->queues.issue);
+ scsi_host_put(host);
+}
+
+static const struct ecard_id acornscsi_cids[] = {
+ { MANU_ACORN, PROD_ACORN_SCSI },
+ { 0xffff, 0xffff },
+};
+
+static struct ecard_driver acornscsi_driver = {
+ .probe = acornscsi_probe,
+ .remove = __devexit_p(acornscsi_remove),
+ .id_table = acornscsi_cids,
+ .drv = {
+ .name = "acornscsi",
+ },
+};
+
+static int __init acornscsi_init(void)
+{
+ return ecard_register_driver(&acornscsi_driver);
+}
+
+static void __exit acornscsi_exit(void)
+{
+ ecard_remove_driver(&acornscsi_driver);
+}
+
+module_init(acornscsi_init);
+module_exit(acornscsi_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("AcornSCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/acornscsi.h b/drivers/scsi/arm/acornscsi.h
new file mode 100644
index 000000000000..03881f091645
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi.h
@@ -0,0 +1,358 @@
+/*
+ * linux/drivers/acorn/scsi/acornscsi.h
+ *
+ * Copyright (C) 1997 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Acorn SCSI driver
+ */
+#ifndef ACORNSCSI_H
+#define ACORNSCSI_H
+
+/* SBIC registers */
+#define SBIC_OWNID 0
+#define OWNID_FS1 (1<<7)
+#define OWNID_FS2 (1<<6)
+#define OWNID_EHP (1<<4)
+#define OWNID_EAF (1<<3)
+
+#define SBIC_CTRL 1
+#define CTRL_DMAMODE (1<<7)
+#define CTRL_DMADBAMODE (1<<6)
+#define CTRL_DMABURST (1<<5)
+#define CTRL_DMAPOLLED 0
+#define CTRL_HHP (1<<4)
+#define CTRL_EDI (1<<3)
+#define CTRL_IDI (1<<2)
+#define CTRL_HA (1<<1)
+#define CTRL_HSP (1<<0)
+
+#define SBIC_TIMEOUT 2
+#define SBIC_TOTSECTS 3
+#define SBIC_TOTHEADS 4
+#define SBIC_TOTCYLH 5
+#define SBIC_TOTCYLL 6
+#define SBIC_LOGADDRH 7
+#define SBIC_LOGADDRM2 8
+#define SBIC_LOGADDRM1 9
+#define SBIC_LOGADDRL 10
+#define SBIC_SECTORNUM 11
+#define SBIC_HEADNUM 12
+#define SBIC_CYLH 13
+#define SBIC_CYLL 14
+#define SBIC_TARGETLUN 15
+#define TARGETLUN_TLV (1<<7)
+#define TARGETLUN_DOK (1<<6)
+
+#define SBIC_CMNDPHASE 16
+#define SBIC_SYNCHTRANSFER 17
+#define SYNCHTRANSFER_OF0 0x00
+#define SYNCHTRANSFER_OF1 0x01
+#define SYNCHTRANSFER_OF2 0x02
+#define SYNCHTRANSFER_OF3 0x03
+#define SYNCHTRANSFER_OF4 0x04
+#define SYNCHTRANSFER_OF5 0x05
+#define SYNCHTRANSFER_OF6 0x06
+#define SYNCHTRANSFER_OF7 0x07
+#define SYNCHTRANSFER_OF8 0x08
+#define SYNCHTRANSFER_OF9 0x09
+#define SYNCHTRANSFER_OF10 0x0A
+#define SYNCHTRANSFER_OF11 0x0B
+#define SYNCHTRANSFER_OF12 0x0C
+#define SYNCHTRANSFER_8DBA 0x00
+#define SYNCHTRANSFER_2DBA 0x20
+#define SYNCHTRANSFER_3DBA 0x30
+#define SYNCHTRANSFER_4DBA 0x40
+#define SYNCHTRANSFER_5DBA 0x50
+#define SYNCHTRANSFER_6DBA 0x60
+#define SYNCHTRANSFER_7DBA 0x70
+
+#define SBIC_TRANSCNTH 18
+#define SBIC_TRANSCNTM 19
+#define SBIC_TRANSCNTL 20
+#define SBIC_DESTID 21
+#define DESTID_SCC (1<<7)
+#define DESTID_DPD (1<<6)
+
+#define SBIC_SOURCEID 22
+#define SOURCEID_ER (1<<7)
+#define SOURCEID_ES (1<<6)
+#define SOURCEID_DSP (1<<5)
+#define SOURCEID_SIV (1<<4)
+
+#define SBIC_SSR 23
+#define SBIC_CMND 24
+#define CMND_RESET 0x00
+#define CMND_ABORT 0x01
+#define CMND_ASSERTATN 0x02
+#define CMND_NEGATEACK 0x03
+#define CMND_DISCONNECT 0x04
+#define CMND_RESELECT 0x05
+#define CMND_SELWITHATN 0x06
+#define CMND_SELECT 0x07
+#define CMND_SELECTATNTRANSFER 0x08
+#define CMND_SELECTTRANSFER 0x09
+#define CMND_RESELECTRXDATA 0x0A
+#define CMND_RESELECTTXDATA 0x0B
+#define CMND_WAITFORSELRECV 0x0C
+#define CMND_SENDSTATCMD 0x0D
+#define CMND_SENDDISCONNECT 0x0E
+#define CMND_SETIDI 0x0F
+#define CMND_RECEIVECMD 0x10
+#define CMND_RECEIVEDTA 0x11
+#define CMND_RECEIVEMSG 0x12
+#define CMND_RECEIVEUSP 0x13
+#define CMND_SENDCMD 0x14
+#define CMND_SENDDATA 0x15
+#define CMND_SENDMSG 0x16
+#define CMND_SENDUSP 0x17
+#define CMND_TRANSLATEADDR 0x18
+#define CMND_XFERINFO 0x20
+#define CMND_SBT (1<<7)
+
+#define SBIC_DATA 25
+#define SBIC_ASR 26
+#define ASR_INT (1<<7)
+#define ASR_LCI (1<<6)
+#define ASR_BSY (1<<5)
+#define ASR_CIP (1<<4)
+#define ASR_PE (1<<1)
+#define ASR_DBR (1<<0)
+
+/* DMAC registers */
+#define DMAC_INIT 0x00
+#define INIT_8BIT (1)
+
+#define DMAC_CHANNEL 0x80
+#define CHANNEL_0 0x00
+#define CHANNEL_1 0x01
+#define CHANNEL_2 0x02
+#define CHANNEL_3 0x03
+
+#define DMAC_TXCNTLO 0x01
+#define DMAC_TXCNTHI 0x81
+#define DMAC_TXADRLO 0x02
+#define DMAC_TXADRMD 0x82
+#define DMAC_TXADRHI 0x03
+
+#define DMAC_DEVCON0 0x04
+#define DEVCON0_AKL (1<<7)
+#define DEVCON0_RQL (1<<6)
+#define DEVCON0_EXW (1<<5)
+#define DEVCON0_ROT (1<<4)
+#define DEVCON0_CMP (1<<3)
+#define DEVCON0_DDMA (1<<2)
+#define DEVCON0_AHLD (1<<1)
+#define DEVCON0_MTM (1<<0)
+
+#define DMAC_DEVCON1 0x84
+#define DEVCON1_WEV (1<<1)
+#define DEVCON1_BHLD (1<<0)
+
+#define DMAC_MODECON 0x05
+#define MODECON_WOED 0x01
+#define MODECON_VERIFY 0x00
+#define MODECON_READ 0x04
+#define MODECON_WRITE 0x08
+#define MODECON_AUTOINIT 0x10
+#define MODECON_ADDRDIR 0x20
+#define MODECON_DEMAND 0x00
+#define MODECON_SINGLE 0x40
+#define MODECON_BLOCK 0x80
+#define MODECON_CASCADE 0xC0
+
+#define DMAC_STATUS 0x85
+#define STATUS_TC0 (1<<0)
+#define STATUS_RQ0 (1<<4)
+
+#define DMAC_TEMPLO 0x06
+#define DMAC_TEMPHI 0x86
+#define DMAC_REQREG 0x07
+#define DMAC_MASKREG 0x87
+#define MASKREG_M0 0x01
+#define MASKREG_M1 0x02
+#define MASKREG_M2 0x04
+#define MASKREG_M3 0x08
+
+/* miscellaneous internal variables */
+
+#define POD_SPACE(x) ((x) + 0xd0000)
+#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0)
+#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1)
+
+/*
+ * SCSI driver phases
+ */
+typedef enum {
+ PHASE_IDLE, /* we're not planning on doing anything */
+ PHASE_CONNECTING, /* connecting to a target */
+ PHASE_CONNECTED, /* connected to a target */
+ PHASE_MSGOUT, /* message out to device */
+ PHASE_RECONNECTED, /* reconnected */
+ PHASE_COMMANDPAUSED, /* command partly sent */
+ PHASE_COMMAND, /* command all sent */
+ PHASE_DATAOUT, /* data out to device */
+ PHASE_DATAIN, /* data in from device */
+ PHASE_STATUSIN, /* status in from device */
+ PHASE_MSGIN, /* message in from device */
+ PHASE_DONE, /* finished */
+ PHASE_ABORTED, /* aborted */
+ PHASE_DISCONNECT, /* disconnecting */
+} phase_t;
+
+/*
+ * After interrupt, what to do now
+ */
+typedef enum {
+ INTR_IDLE, /* not expecting another IRQ */
+ INTR_NEXT_COMMAND, /* start next command */
+ INTR_PROCESSING, /* interrupt routine still processing */
+} intr_ret_t;
+
+/*
+ * DMA direction
+ */
+typedef enum {
+ DMA_OUT, /* DMA from memory to chip */
+ DMA_IN /* DMA from chip to memory */
+} dmadir_t;
+
+/*
+ * Synchronous transfer state
+ */
+typedef enum { /* Synchronous transfer state */
+ SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/
+ SYNC_NEGOCIATE, /* start negociation */
+ SYNC_SENT_REQUEST, /* sent SDTR message */
+ SYNC_COMPLETED, /* received SDTR reply */
+} syncxfer_t;
+
+/*
+ * Command type
+ */
+typedef enum { /* command type */
+ CMD_READ, /* READ_6, READ_10, READ_12 */
+ CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */
+ CMD_MISC, /* Others */
+} cmdtype_t;
+
+/*
+ * Data phase direction
+ */
+typedef enum { /* Data direction */
+ DATADIR_IN, /* Data in phase expected */
+ DATADIR_OUT /* Data out phase expected */
+} datadir_t;
+
+#include "queue.h"
+#include "msgqueue.h"
+
+#define STATUS_BUFFER_SIZE 32
+/*
+ * This is used to dump the previous states of the SBIC
+ */
+struct status_entry {
+ unsigned long when;
+ unsigned char ssr;
+ unsigned char ph;
+ unsigned char irq;
+ unsigned char unused;
+};
+
+#define ADD_STATUS(_q,_ssr,_ph,_irq) \
+({ \
+ host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \
+ host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \
+ host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \
+ host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \
+ host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \
+})
+
+/*
+ * AcornSCSI host specific data
+ */
+typedef struct acornscsi_hostdata {
+ /* miscellaneous */
+ struct Scsi_Host *host; /* host */
+ Scsi_Cmnd *SCpnt; /* currently processing command */
+ Scsi_Cmnd *origSCpnt; /* original connecting command */
+
+ /* driver information */
+ struct {
+ unsigned int io_port; /* base address of WD33C93 */
+ unsigned int irq; /* interrupt */
+ phase_t phase; /* current phase */
+
+ struct {
+ unsigned char target; /* reconnected target */
+ unsigned char lun; /* reconnected lun */
+ unsigned char tag; /* reconnected tag */
+ } reconnected;
+
+ Scsi_Pointer SCp; /* current commands data pointer */
+
+ MsgQueue_t msgs;
+
+ unsigned short last_message; /* last message to be sent */
+ unsigned char disconnectable:1; /* this command can be disconnected */
+ } scsi;
+
+ /* statistics information */
+ struct {
+ unsigned int queues;
+ unsigned int removes;
+ unsigned int fins;
+ unsigned int reads;
+ unsigned int writes;
+ unsigned int miscs;
+ unsigned int disconnects;
+ unsigned int aborts;
+ unsigned int resets;
+ } stats;
+
+ /* queue handling */
+ struct {
+ Queue_t issue; /* issue queue */
+ Queue_t disconnected; /* disconnected command queue */
+ } queues;
+
+ /* per-device info */
+ struct {
+ unsigned char sync_xfer; /* synchronous transfer (SBIC value) */
+ syncxfer_t sync_state; /* sync xfer negociation state */
+ unsigned char disconnect_ok:1; /* device can disconnect */
+ } device[8];
+ unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */
+
+ /* DMA info */
+ struct {
+ unsigned int io_port; /* base address of DMA controller */
+ unsigned int io_intr_clear; /* address of DMA interrupt clear */
+ unsigned int free_addr; /* next free address */
+ unsigned int start_addr; /* start address of current transfer */
+ dmadir_t direction; /* dma direction */
+ unsigned int transferred; /* number of bytes transferred */
+ unsigned int xfer_start; /* scheduled DMA transfer start */
+ unsigned int xfer_length; /* scheduled DMA transfer length */
+ char *xfer_ptr; /* pointer to area */
+ unsigned char xfer_required:1; /* set if we need to transfer something */
+ unsigned char xfer_setup:1; /* set if DMA is setup */
+ unsigned char xfer_done:1; /* set if DMA reached end of BH list */
+ } dma;
+
+ /* card info */
+ struct {
+ unsigned int io_intr; /* base address of interrupt id reg */
+ unsigned int io_page; /* base address of page reg */
+ unsigned int io_ram; /* base address of RAM access */
+ unsigned char page_reg; /* current setting of page reg */
+ } card;
+
+ unsigned char status_ptr[9];
+ struct status_entry status[9][STATUS_BUFFER_SIZE];
+} AS_Host;
+
+#endif /* ACORNSCSI_H */
diff --git a/drivers/scsi/arm/arxescsi.c b/drivers/scsi/arm/arxescsi.c
new file mode 100644
index 000000000000..29811f5891ee
--- /dev/null
+++ b/drivers/scsi/arm/arxescsi.c
@@ -0,0 +1,395 @@
+/*
+ * linux/arch/arm/drivers/scsi/arxescsi.c
+ *
+ * Copyright (C) 1997-2000 Russell King, Stefan Hanske
+ *
+ * This driver is based on experimentation. Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c
+ * 22-01-1998 RMK 0.0.1 Updated to 2.1.80
+ * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
+ * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card
+ * enabled writing
+ * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing
+ * (arxescsi_pseudo_dma_write)
+ * 02-04-2000 RMK 0.1.1 Updated for new error handling code.
+ * 22-10-2000 SH Updated for new registering scheme.
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+#include "fas216.h"
+
+struct arxescsi_info {
+ FAS216_Info info;
+ struct expansion_card *ec;
+ void __iomem *base;
+};
+
+#define DMADATA_OFFSET (0x200)
+
+#define DMASTAT_OFFSET (0x600)
+#define DMASTAT_DRQ (1 << 0)
+
+#define CSTATUS_IRQ (1 << 0)
+
+#define VERSION "1.10 (23/01/2003 2.5.57)"
+
+/*
+ * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
+ */
+static fasdmatype_t
+arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ /*
+ * We don't do real DMA
+ */
+ return fasdma_pseudo;
+}
+
+static void arxescsi_pseudo_dma_write(unsigned char *addr, void __iomem *base)
+{
+ __asm__ __volatile__(
+ " stmdb sp!, {r0-r12}\n"
+ " mov r3, %0\n"
+ " mov r1, %1\n"
+ " add r2, r1, #512\n"
+ " mov r4, #256\n"
+ ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n"
+ " mov r5, r6, lsl #16\n"
+ " mov r7, r8, lsl #16\n"
+ ".loop_2: ldrb r0, [r1, #1536]\n"
+ " tst r0, #1\n"
+ " beq .loop_2\n"
+ " stmia r2, {r5-r8}\n\t"
+ " mov r9, r10, lsl #16\n"
+ " mov r11, r12, lsl #16\n"
+ ".loop_3: ldrb r0, [r1, #1536]\n"
+ " tst r0, #1\n"
+ " beq .loop_3\n"
+ " stmia r2, {r9-r12}\n"
+ " subs r4, r4, #16\n"
+ " bne .loop_1\n"
+ " ldmia sp!, {r0-r12}\n"
+ :
+ : "r" (addr), "r" (base));
+}
+
+/*
+ * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * transfer - minimum number of bytes we expect to transfer
+ */
+static void
+arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, int transfer)
+{
+ struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
+ unsigned int length, error = 0;
+ void __iomem *base = info->info.scsi.io_base;
+ unsigned char *addr;
+
+ length = SCp->this_residual;
+ addr = SCp->ptr;
+
+ if (direction == DMA_OUT) {
+ unsigned int word;
+ while (length > 256) {
+ if (readb(base + 0x80) & STAT_INT) {
+ error = 1;
+ break;
+ }
+ arxescsi_pseudo_dma_write(addr, base);
+ addr += 256;
+ length -= 256;
+ }
+
+ if (!error)
+ while (length > 0) {
+ if (readb(base + 0x80) & STAT_INT)
+ break;
+
+ if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
+ continue;
+
+ word = *addr | *(addr + 1) << 8;
+
+ writew(word, base + DMADATA_OFFSET);
+ if (length > 1) {
+ addr += 2;
+ length -= 2;
+ } else {
+ addr += 1;
+ length -= 1;
+ }
+ }
+ }
+ else {
+ if (transfer && (transfer & 255)) {
+ while (length >= 256) {
+ if (readb(base + 0x80) & STAT_INT) {
+ error = 1;
+ break;
+ }
+
+ if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
+ continue;
+
+ readsw(base + DMADATA_OFFSET, addr, 256 >> 1);
+ addr += 256;
+ length -= 256;
+ }
+ }
+
+ if (!(error))
+ while (length > 0) {
+ unsigned long word;
+
+ if (readb(base + 0x80) & STAT_INT)
+ break;
+
+ if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ))
+ continue;
+
+ word = readw(base + DMADATA_OFFSET);
+ *addr++ = word;
+ if (--length > 0) {
+ *addr++ = word >> 8;
+ length --;
+ }
+ }
+ }
+}
+
+/*
+ * Function: int arxescsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ */
+static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+ /*
+ * no DMA to stop
+ */
+}
+
+/*
+ * Function: const char *arxescsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+static const char *arxescsi_info(struct Scsi_Host *host)
+{
+ struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
+ static char string[150];
+
+ sprintf(string, "%s (%s) in slot %d v%s",
+ host->hostt->name, info->info.scsi.type, info->ec->slot_no,
+ VERSION);
+
+ return string;
+}
+
+/*
+ * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+ * int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+static int
+arxescsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length,
+ int inout)
+{
+ struct arxescsi_info *info;
+ char *p = buffer;
+ int pos;
+
+ info = (struct arxescsi_info *)host->hostdata;
+ if (inout == 1)
+ return -EINVAL;
+
+ p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION);
+ p += fas216_print_host(&info->info, p);
+ p += fas216_print_stats(&info->info, p);
+ p += fas216_print_devices(&info->info, p);
+
+ *start = buffer + offset;
+ pos = p - buffer - offset;
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+static Scsi_Host_Template arxescsi_template = {
+ .proc_info = arxescsi_proc_info,
+ .name = "ARXE SCSI card",
+ .info = arxescsi_info,
+ .queuecommand = fas216_noqueue_command,
+ .eh_host_reset_handler = fas216_eh_host_reset,
+ .eh_bus_reset_handler = fas216_eh_bus_reset,
+ .eh_device_reset_handler = fas216_eh_device_reset,
+ .eh_abort_handler = fas216_eh_abort,
+ .can_queue = 0,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "arxescsi",
+};
+
+static int __devinit
+arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ struct arxescsi_info *info;
+ unsigned long resbase, reslen;
+ void __iomem *base;
+ int ret;
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ resbase = ecard_resource_start(ec, ECARD_RES_MEMC);
+ reslen = ecard_resource_len(ec, ECARD_RES_MEMC);
+ base = ioremap(resbase, reslen);
+ if (!base) {
+ ret = -ENOMEM;
+ goto out_region;
+ }
+
+ host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info));
+ if (!host) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ info = (struct arxescsi_info *)host->hostdata;
+ info->ec = ec;
+ info->base = base;
+
+ info->info.scsi.io_base = base + 0x2000;
+ info->info.scsi.irq = NO_IRQ;
+ info->info.scsi.dma = NO_DMA;
+ info->info.scsi.io_shift = 5;
+ info->info.ifcfg.clockrate = 24; /* MHz */
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = 200; /* ns */
+ info->info.ifcfg.sync_max_depth = 0;
+ info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 0;
+ info->info.ifcfg.wide_max_size = 0;
+ info->info.ifcfg.capabilities = FASCAP_PSEUDODMA;
+ info->info.dma.setup = arxescsi_dma_setup;
+ info->info.dma.pseudo = arxescsi_dma_pseudo;
+ info->info.dma.stop = arxescsi_dma_stop;
+
+ ec->irqaddr = base;
+ ec->irqmask = CSTATUS_IRQ;
+
+ ret = fas216_init(host);
+ if (ret)
+ goto out_unregister;
+
+ ret = fas216_add(host, &ec->dev);
+ if (ret == 0)
+ goto out;
+
+ fas216_release(host);
+ out_unregister:
+ scsi_host_put(host);
+ out_unmap:
+ iounmap(base);
+ out_region:
+ ecard_release_resources(ec);
+ out:
+ return ret;
+}
+
+static void __devexit arxescsi_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata;
+
+ ecard_set_drvdata(ec, NULL);
+ fas216_remove(host);
+
+ iounmap(info->base);
+
+ fas216_release(host);
+ scsi_host_put(host);
+ ecard_release_resources(ec);
+}
+
+static const struct ecard_id arxescsi_cids[] = {
+ { MANU_ARXE, PROD_ARXE_SCSI },
+ { 0xffff, 0xffff },
+};
+
+static struct ecard_driver arxescsi_driver = {
+ .probe = arxescsi_probe,
+ .remove = __devexit_p(arxescsi_remove),
+ .id_table = arxescsi_cids,
+ .drv = {
+ .name = "arxescsi",
+ },
+};
+
+static int __init init_arxe_scsi_driver(void)
+{
+ return ecard_register_driver(&arxescsi_driver);
+}
+
+static void __exit exit_arxe_scsi_driver(void)
+{
+ ecard_remove_driver(&arxescsi_driver);
+}
+
+module_init(init_arxe_scsi_driver);
+module_exit(exit_arxe_scsi_driver);
+
+MODULE_AUTHOR("Stefan Hanske");
+MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/arm/cumana_1.c b/drivers/scsi/arm/cumana_1.c
new file mode 100644
index 000000000000..27271bfc01d7
--- /dev/null
+++ b/drivers/scsi/arm/cumana_1.c
@@ -0,0 +1,357 @@
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1995-2002, Russell King
+ */
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+
+#include <scsi/scsicam.h>
+
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+#define CUMANASCSI_PUBLIC_RELEASE 1
+
+#define NCR5380_implementation_fields int port, ctrl
+#define NCR5380_local_declare() struct Scsi_Host *_instance
+#define NCR5380_setup(instance) _instance = instance
+#define NCR5380_read(reg) cumanascsi_read(_instance, reg)
+#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value)
+#define NCR5380_intr cumanascsi_intr
+#define NCR5380_queue_command cumanascsi_queue_command
+#define NCR5380_proc_info cumanascsi_proc_info
+
+#define BOARD_NORMAL 0
+#define BOARD_NCR53C400 1
+
+#include "../NCR5380.h"
+
+void cumanascsi_setup(char *str, int *ints)
+{
+}
+
+const char *cumanascsi_info(struct Scsi_Host *spnt)
+{
+ return "";
+}
+
+#ifdef NOT_EFFICIENT
+#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
+#define STAT(p) inb((p)+1)
+#define IN(p) inb((p))
+#define OUT(v,p) outb((v), (p))
+#else
+#define CTRL(p,v) (p[-2308] = (*ctrl = (v)))
+#define STAT(p) (p[4])
+#define IN(p) (*(p))
+#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p)))
+#define OUT(v,p) (*(p) = (v))
+#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v))
+#endif
+#define L(v) (((v)<<16)|((v) & 0x0000ffff))
+#define H(v) (((v)>>16)|((v) & 0xffff0000))
+
+static inline int
+NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len)
+{
+ int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+ int oldctrl = *ctrl;
+ unsigned long *laddr;
+#ifdef NOT_EFFICIENT
+ int iobase = instance->io_port;
+ int dma_io = iobase & ~(0x3C0000>>2);
+#else
+ volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port);
+ volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000);
+#endif
+
+ if(!len) return 0;
+
+ CTRL(iobase, 0x02);
+ laddr = (unsigned long *)addr;
+ while(len >= 32)
+ {
+ int status;
+ unsigned long v;
+ status = STAT(iobase);
+ if(status & 0x80)
+ goto end;
+ if(!(status & 0x40))
+ continue;
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+ len -= 32;
+ if(len == 0)
+ break;
+ }
+
+ addr = (unsigned char *)laddr;
+ CTRL(iobase, 0x12);
+ while(len > 0)
+ {
+ int status;
+ status = STAT(iobase);
+ if(status & 0x80)
+ goto end;
+ if(status & 0x40)
+ {
+ OUT(*addr++, dma_io);
+ if(--len == 0)
+ break;
+ }
+
+ status = STAT(iobase);
+ if(status & 0x80)
+ goto end;
+ if(status & 0x40)
+ {
+ OUT(*addr++, dma_io);
+ if(--len == 0)
+ break;
+ }
+ }
+end:
+ CTRL(iobase, oldctrl|0x40);
+ return len;
+}
+
+static inline int
+NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len)
+{
+ int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+ int oldctrl = *ctrl;
+ unsigned long *laddr;
+#ifdef NOT_EFFICIENT
+ int iobase = instance->io_port;
+ int dma_io = iobase & ~(0x3C0000>>2);
+#else
+ volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port);
+ volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000);
+#endif
+
+ if(!len) return 0;
+
+ CTRL(iobase, 0x00);
+ laddr = (unsigned long *)addr;
+ while(len >= 32)
+ {
+ int status;
+ status = STAT(iobase);
+ if(status & 0x80)
+ goto end;
+ if(!(status & 0x40))
+ continue;
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+ len -= 32;
+ if(len == 0)
+ break;
+ }
+
+ addr = (unsigned char *)laddr;
+ CTRL(iobase, 0x10);
+ while(len > 0)
+ {
+ int status;
+ status = STAT(iobase);
+ if(status & 0x80)
+ goto end;
+ if(status & 0x40)
+ {
+ *addr++ = IN(dma_io);
+ if(--len == 0)
+ break;
+ }
+
+ status = STAT(iobase);
+ if(status & 0x80)
+ goto end;
+ if(status & 0x40)
+ {
+ *addr++ = IN(dma_io);
+ if(--len == 0)
+ break;
+ }
+ }
+end:
+ CTRL(iobase, oldctrl|0x40);
+ return len;
+}
+
+#undef STAT
+#undef CTRL
+#undef IN
+#undef OUT
+
+#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
+
+static char cumanascsi_read(struct Scsi_Host *instance, int reg)
+{
+ unsigned int iobase = instance->io_port;
+ int i;
+ int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+ CTRL(iobase, 0);
+ i = inb(iobase + 64 + reg);
+ CTRL(iobase, 0x40);
+
+ return i;
+}
+
+static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+ int iobase = instance->io_port;
+ int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+ CTRL(iobase, 0);
+ outb(value, iobase + 64 + reg);
+ CTRL(iobase, 0x40);
+}
+
+#undef CTRL
+
+#include "../NCR5380.c"
+
+static Scsi_Host_Template cumanascsi_template = {
+ .module = THIS_MODULE,
+ .name = "Cumana 16-bit SCSI",
+ .info = cumanascsi_info,
+ .queuecommand = cumanascsi_queue_command,
+ .eh_abort_handler = NCR5380_abort,
+ .eh_device_reset_handler= NCR5380_device_reset,
+ .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .unchecked_isa_dma = 0,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "CumanaSCSI-1",
+};
+
+static int __devinit
+cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ int ret = -ENOMEM;
+
+ host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata));
+ if (!host)
+ goto out;
+
+ host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800;
+ host->irq = ec->irq;
+
+ NCR5380_init(host, 0);
+
+ host->n_io_port = 255;
+ if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) {
+ ret = -EBUSY;
+ goto out_free;
+ }
+
+ ((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0;
+ outb(0x00, host->io_port - 577);
+
+ ret = request_irq(host->irq, cumanascsi_intr, SA_INTERRUPT,
+ "CumanaSCSI-1", host);
+ if (ret) {
+ printk("scsi%d: IRQ%d not free: %d\n",
+ host->host_no, host->irq, ret);
+ goto out_release;
+ }
+
+ printk("scsi%d: at port 0x%08lx irq %d",
+ host->host_no, host->io_port, host->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE);
+ printk("\nscsi%d:", host->host_no);
+ NCR5380_print_options(host);
+ printk("\n");
+
+ ret = scsi_add_host(host, &ec->dev);
+ if (ret)
+ goto out_free_irq;
+
+ scsi_scan_host(host);
+ goto out;
+
+ out_free_irq:
+ free_irq(host->irq, host);
+ out_release:
+ release_region(host->io_port, host->n_io_port);
+ out_free:
+ scsi_host_put(host);
+ out:
+ return ret;
+}
+
+static void __devexit cumanascsi1_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+
+ ecard_set_drvdata(ec, NULL);
+
+ scsi_remove_host(host);
+ free_irq(host->irq, host);
+ NCR5380_exit(host);
+ release_region(host->io_port, host->n_io_port);
+ scsi_host_put(host);
+}
+
+static const struct ecard_id cumanascsi1_cids[] = {
+ { MANU_CUMANA, PROD_CUMANA_SCSI_1 },
+ { 0xffff, 0xffff }
+};
+
+static struct ecard_driver cumanascsi1_driver = {
+ .probe = cumanascsi1_probe,
+ .remove = __devexit_p(cumanascsi1_remove),
+ .id_table = cumanascsi1_cids,
+ .drv = {
+ .name = "cumanascsi1",
+ },
+};
+
+static int __init cumanascsi_init(void)
+{
+ return ecard_register_driver(&cumanascsi1_driver);
+}
+
+static void __exit cumanascsi_exit(void)
+{
+ ecard_remove_driver(&cumanascsi1_driver);
+}
+
+module_init(cumanascsi_init);
+module_exit(cumanascsi_exit);
+
+MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c
new file mode 100644
index 000000000000..0ef0644eeb29
--- /dev/null
+++ b/drivers/scsi/arm/cumana_2.c
@@ -0,0 +1,556 @@
+/*
+ * linux/drivers/acorn/scsi/cumana_2.c
+ *
+ * Copyright (C) 1997-2005 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Changelog:
+ * 30-08-1997 RMK 0.0.0 Created, READONLY version.
+ * 22-01-1998 RMK 0.0.1 Updated to 2.1.80.
+ * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
+ * 02-05-1998 RMK 0.0.2 Updated & added DMA support.
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
+ * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth.
+ * 02-04-2000 RMK 0.0.4 Updated for new error handling code.
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+#include "fas216.h"
+#include "scsi.h"
+
+#include <scsi/scsicam.h>
+
+#define CUMANASCSI2_STATUS (0x0000)
+#define STATUS_INT (1 << 0)
+#define STATUS_DRQ (1 << 1)
+#define STATUS_LATCHED (1 << 3)
+
+#define CUMANASCSI2_ALATCH (0x0014)
+#define ALATCH_ENA_INT (3)
+#define ALATCH_DIS_INT (2)
+#define ALATCH_ENA_TERM (5)
+#define ALATCH_DIS_TERM (4)
+#define ALATCH_ENA_BIT32 (11)
+#define ALATCH_DIS_BIT32 (10)
+#define ALATCH_ENA_DMA (13)
+#define ALATCH_DIS_DMA (12)
+#define ALATCH_DMA_OUT (15)
+#define ALATCH_DMA_IN (14)
+
+#define CUMANASCSI2_PSEUDODMA (0x0200)
+
+#define CUMANASCSI2_FAS216_OFFSET (0x0300)
+#define CUMANASCSI2_FAS216_SHIFT 2
+
+/*
+ * Version
+ */
+#define VERSION "1.00 (13/11/2002 2.5.47)"
+
+/*
+ * Use term=0,1,0,0,0 to turn terminators on/off
+ */
+static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
+#define NR_SG 256
+
+struct cumanascsi2_info {
+ FAS216_Info info;
+ struct expansion_card *ec;
+ void __iomem *base;
+ unsigned int terms; /* Terminator state */
+ struct scatterlist sg[NR_SG]; /* Scatter DMA list */
+};
+
+#define CSTATUS_IRQ (1 << 0)
+#define CSTATUS_DRQ (1 << 1)
+
+/* Prototype: void cumanascsi_2_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on Cumana SCSI 2 card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr)
+{
+ struct cumanascsi2_info *info = ec->irq_data;
+ writeb(ALATCH_ENA_INT, info->base + CUMANASCSI2_ALATCH);
+}
+
+/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on Cumana SCSI 2 card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr)
+{
+ struct cumanascsi2_info *info = ec->irq_data;
+ writeb(ALATCH_DIS_INT, info->base + CUMANASCSI2_ALATCH);
+}
+
+static const expansioncard_ops_t cumanascsi_2_ops = {
+ .irqenable = cumanascsi_2_irqenable,
+ .irqdisable = cumanascsi_2_irqdisable,
+};
+
+/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off)
+ * Purpose : Turn the Cumana SCSI 2 terminators on or off
+ * Params : host - card to turn on/off
+ * : on_off - !0 to turn on, 0 to turn off
+ */
+static void
+cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off)
+{
+ struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
+
+ if (on_off) {
+ info->terms = 1;
+ writeb(ALATCH_ENA_TERM, info->base + CUMANASCSI2_ALATCH);
+ } else {
+ info->terms = 0;
+ writeb(ALATCH_DIS_TERM, info->base + CUMANASCSI2_ALATCH);
+ }
+}
+
+/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs)
+ * Purpose : handle interrupts from Cumana SCSI 2 card
+ * Params : irq - interrupt number
+ * dev_id - user-defined (Scsi_Host structure)
+ * regs - processor registers at interrupt
+ */
+static irqreturn_t
+cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct cumanascsi2_info *info = dev_id;
+
+ return fas216_intr(&info->info);
+}
+
+/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : type of transfer to be performed
+ */
+static fasdmatype_t
+cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
+ struct device *dev = scsi_get_device(host);
+ int dmach = info->info.scsi.dma;
+
+ writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);
+
+ if (dmach != NO_DMA &&
+ (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
+ int bufs, map_dir, dma_dir, alatch_dir;
+
+ bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
+
+ if (direction == DMA_OUT)
+ map_dir = DMA_TO_DEVICE,
+ dma_dir = DMA_MODE_WRITE,
+ alatch_dir = ALATCH_DMA_OUT;
+ else
+ map_dir = DMA_FROM_DEVICE,
+ dma_dir = DMA_MODE_READ,
+ alatch_dir = ALATCH_DMA_IN;
+
+ dma_map_sg(dev, info->sg, bufs + 1, map_dir);
+
+ disable_dma(dmach);
+ set_dma_sg(dmach, info->sg, bufs + 1);
+ writeb(alatch_dir, info->base + CUMANASCSI2_ALATCH);
+ set_dma_mode(dmach, dma_dir);
+ enable_dma(dmach);
+ writeb(ALATCH_ENA_DMA, info->base + CUMANASCSI2_ALATCH);
+ writeb(ALATCH_DIS_BIT32, info->base + CUMANASCSI2_ALATCH);
+ return fasdma_real_all;
+ }
+
+ /*
+ * If we're not doing DMA,
+ * we'll do pseudo DMA
+ */
+ return fasdma_pio;
+}
+
+/*
+ * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * transfer - minimum number of bytes we expect to transfer
+ */
+static void
+cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, int transfer)
+{
+ struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
+ unsigned int length;
+ unsigned char *addr;
+
+ length = SCp->this_residual;
+ addr = SCp->ptr;
+
+ if (direction == DMA_OUT)
+#if 0
+ while (length > 1) {
+ unsigned long word;
+ unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
+
+ if (status & STATUS_INT)
+ goto end;
+
+ if (!(status & STATUS_DRQ))
+ continue;
+
+ word = *addr | *(addr + 1) << 8;
+ writew(word, info->base + CUMANASCSI2_PSEUDODMA);
+ addr += 2;
+ length -= 2;
+ }
+#else
+ printk ("PSEUDO_OUT???\n");
+#endif
+ else {
+ if (transfer && (transfer & 255)) {
+ while (length >= 256) {
+ unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
+
+ if (status & STATUS_INT)
+ return;
+
+ if (!(status & STATUS_DRQ))
+ continue;
+
+ readsw(info->base + CUMANASCSI2_PSEUDODMA,
+ addr, 256 >> 1);
+ addr += 256;
+ length -= 256;
+ }
+ }
+
+ while (length > 0) {
+ unsigned long word;
+ unsigned int status = readb(info->base + CUMANASCSI2_STATUS);
+
+ if (status & STATUS_INT)
+ return;
+
+ if (!(status & STATUS_DRQ))
+ continue;
+
+ word = readw(info->base + CUMANASCSI2_PSEUDODMA);
+ *addr++ = word;
+ if (--length > 0) {
+ *addr++ = word >> 8;
+ length --;
+ }
+ }
+ }
+}
+
+/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ */
+static void
+cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+ struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
+ if (info->info.scsi.dma != NO_DMA) {
+ writeb(ALATCH_DIS_DMA, info->base + CUMANASCSI2_ALATCH);
+ disable_dma(info->info.scsi.dma);
+ }
+}
+
+/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *cumanascsi_2_info(struct Scsi_Host *host)
+{
+ struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
+ static char string[150];
+
+ sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
+ host->hostt->name, info->info.scsi.type, info->ec->slot_no,
+ VERSION, info->terms ? "n" : "ff");
+
+ return string;
+}
+
+/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+ * Purpose : Set a driver specific function
+ * Params : host - host to setup
+ * : buffer - buffer containing string describing operation
+ * : length - length of string
+ * Returns : -EINVAL, or 0
+ */
+static int
+cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+{
+ int ret = length;
+
+ if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) {
+ buffer += 11;
+ length -= 11;
+
+ if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
+ if (buffer[5] == '1')
+ cumanascsi_2_terminator_ctl(host, 1);
+ else if (buffer[5] == '0')
+ cumanascsi_2_terminator_ctl(host, 0);
+ else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset,
+ * int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int cumanascsi_2_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ struct cumanascsi2_info *info;
+ char *p = buffer;
+ int pos;
+
+ if (inout == 1)
+ return cumanascsi_2_set_proc_info(host, buffer, length);
+
+ info = (struct cumanascsi2_info *)host->hostdata;
+
+ p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION);
+ p += fas216_print_host(&info->info, p);
+ p += sprintf(p, "Term : o%s\n",
+ info->terms ? "n" : "ff");
+
+ p += fas216_print_stats(&info->info, p);
+ p += fas216_print_devices(&info->info, p);
+
+ *start = buffer + offset;
+ pos = p - buffer - offset;
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+static Scsi_Host_Template cumanascsi2_template = {
+ .module = THIS_MODULE,
+ .proc_info = cumanascsi_2_proc_info,
+ .name = "Cumana SCSI II",
+ .info = cumanascsi_2_info,
+ .queuecommand = fas216_queue_command,
+ .eh_host_reset_handler = fas216_eh_host_reset,
+ .eh_bus_reset_handler = fas216_eh_bus_reset,
+ .eh_device_reset_handler = fas216_eh_device_reset,
+ .eh_abort_handler = fas216_eh_abort,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "cumanascsi2",
+};
+
+static int __devinit
+cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ struct cumanascsi2_info *info;
+ unsigned long resbase, reslen;
+ void __iomem *base;
+ int ret;
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ resbase = ecard_resource_start(ec, ECARD_RES_MEMC);
+ reslen = ecard_resource_len(ec, ECARD_RES_MEMC);
+ base = ioremap(resbase, reslen);
+ if (!base) {
+ ret = -ENOMEM;
+ goto out_region;
+ }
+
+ host = scsi_host_alloc(&cumanascsi2_template,
+ sizeof(struct cumanascsi2_info));
+ if (!host) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ ecard_set_drvdata(ec, host);
+
+ info = (struct cumanascsi2_info *)host->hostdata;
+ info->ec = ec;
+ info->base = base;
+
+ cumanascsi_2_terminator_ctl(host, term[ec->slot_no]);
+
+ info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET;
+ info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT;
+ info->info.scsi.irq = ec->irq;
+ info->info.scsi.dma = ec->dma;
+ info->info.ifcfg.clockrate = 40; /* MHz */
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = 200; /* ns */
+ info->info.ifcfg.sync_max_depth = 7;
+ info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 1;
+ info->info.ifcfg.wide_max_size = 0;
+ info->info.ifcfg.capabilities = FASCAP_PSEUDODMA;
+ info->info.dma.setup = cumanascsi_2_dma_setup;
+ info->info.dma.pseudo = cumanascsi_2_dma_pseudo;
+ info->info.dma.stop = cumanascsi_2_dma_stop;
+
+ ec->irqaddr = info->base + CUMANASCSI2_STATUS;
+ ec->irqmask = STATUS_INT;
+ ec->irq_data = info;
+ ec->ops = &cumanascsi_2_ops;
+
+ ret = fas216_init(host);
+ if (ret)
+ goto out_free;
+
+ ret = request_irq(ec->irq, cumanascsi_2_intr,
+ SA_INTERRUPT, "cumanascsi2", info);
+ if (ret) {
+ printk("scsi%d: IRQ%d not free: %d\n",
+ host->host_no, ec->irq, ret);
+ goto out_release;
+ }
+
+ if (info->info.scsi.dma != NO_DMA) {
+ if (request_dma(info->info.scsi.dma, "cumanascsi2")) {
+ printk("scsi%d: DMA%d not free, using PIO\n",
+ host->host_no, info->info.scsi.dma);
+ info->info.scsi.dma = NO_DMA;
+ } else {
+ set_dma_speed(info->info.scsi.dma, 180);
+ info->info.ifcfg.capabilities |= FASCAP_DMA;
+ }
+ }
+
+ ret = fas216_add(host, &ec->dev);
+ if (ret == 0)
+ goto out;
+
+ if (info->info.scsi.dma != NO_DMA)
+ free_dma(info->info.scsi.dma);
+ free_irq(ec->irq, host);
+
+ out_release:
+ fas216_release(host);
+
+ out_free:
+ scsi_host_put(host);
+
+ out_unmap:
+ iounmap(base);
+
+ out_region:
+ ecard_release_resources(ec);
+
+ out:
+ return ret;
+}
+
+static void __devexit cumanascsi2_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata;
+
+ ecard_set_drvdata(ec, NULL);
+ fas216_remove(host);
+
+ if (info->info.scsi.dma != NO_DMA)
+ free_dma(info->info.scsi.dma);
+ free_irq(ec->irq, info);
+
+ iounmap(info->base);
+
+ fas216_release(host);
+ scsi_host_put(host);
+ ecard_release_resources(ec);
+}
+
+static const struct ecard_id cumanascsi2_cids[] = {
+ { MANU_CUMANA, PROD_CUMANA_SCSI_2 },
+ { 0xffff, 0xffff },
+};
+
+static struct ecard_driver cumanascsi2_driver = {
+ .probe = cumanascsi2_probe,
+ .remove = __devexit_p(cumanascsi2_remove),
+ .id_table = cumanascsi2_cids,
+ .drv = {
+ .name = "cumanascsi2",
+ },
+};
+
+static int __init cumanascsi2_init(void)
+{
+ return ecard_register_driver(&cumanascsi2_driver);
+}
+
+static void __exit cumanascsi2_exit(void)
+{
+ ecard_remove_driver(&cumanascsi2_driver);
+}
+
+module_init(cumanascsi2_init);
+module_exit(cumanascsi2_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines");
+MODULE_PARM(term, "1-8i");
+MODULE_PARM_DESC(term, "SCSI bus termination");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/ecoscsi.c b/drivers/scsi/arm/ecoscsi.c
new file mode 100644
index 000000000000..303648a84709
--- /dev/null
+++ b/drivers/scsi/arm/ecoscsi.c
@@ -0,0 +1,239 @@
+#define AUTOSENSE
+/* #define PSEUDO_DMA */
+
+/*
+ * EcoSCSI Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+
+#define NCR5380_implementation_fields int port, ctrl
+#define NCR5380_local_declare() struct Scsi_Host *_instance
+#define NCR5380_setup(instance) _instance = instance
+
+#define NCR5380_read(reg) ecoscsi_read(_instance, reg)
+#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value)
+
+#define NCR5380_intr ecoscsi_intr
+#define NCR5380_queue_command ecoscsi_queue_command
+#define NCR5380_proc_info ecoscsi_proc_info
+
+#include "../NCR5380.h"
+
+#define ECOSCSI_PUBLIC_RELEASE 1
+
+static char ecoscsi_read(struct Scsi_Host *instance, int reg)
+{
+ int iobase = instance->io_port;
+ outb(reg | 8, iobase);
+ return inb(iobase + 1);
+}
+
+static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+ int iobase = instance->io_port;
+ outb(reg | 8, iobase);
+ outb(value, iobase + 1);
+}
+
+/*
+ * Function : ecoscsi_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void ecoscsi_setup(char *str, int *ints)
+{
+}
+
+const char * ecoscsi_info (struct Scsi_Host *spnt)
+{
+ return "";
+}
+
+#if 0
+#define STAT(p) inw(p + 144)
+
+static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr,
+ int len)
+{
+ int iobase = host->io_port;
+printk("writing %p len %d\n",addr, len);
+ if(!len) return -1;
+
+ while(1)
+ {
+ int status;
+ while(((status = STAT(iobase)) & 0x100)==0);
+ }
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr,
+ int len)
+{
+ int iobase = host->io_port;
+ int iobase2= host->io_port + 0x100;
+ unsigned char *start = addr;
+ int s;
+printk("reading %p len %d\n",addr, len);
+ outb(inb(iobase + 128), iobase + 135);
+ while(len > 0)
+ {
+ int status,b,i, timeout;
+ timeout = 0x07FFFFFF;
+ while(((status = STAT(iobase)) & 0x100)==0)
+ {
+ timeout--;
+ if(status & 0x200 || !timeout)
+ {
+ printk("status = %p\n",status);
+ outb(0, iobase + 135);
+ return 1;
+ }
+ }
+ if(len >= 128)
+ {
+ for(i=0; i<64; i++)
+ {
+ b = inw(iobase + 136);
+ *addr++ = b;
+ *addr++ = b>>8;
+ }
+ len -= 128;
+ }
+ else
+ {
+ b = inw(iobase + 136);
+ *addr ++ = b;
+ len -= 1;
+ if(len)
+ *addr ++ = b>>8;
+ len -= 1;
+ }
+ }
+ outb(0, iobase + 135);
+ printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]);
+ return 1;
+}
+#endif
+#undef STAT
+
+#define BOARD_NORMAL 0
+#define BOARD_NCR53C400 1
+
+#include "../NCR5380.c"
+
+static Scsi_Host_Template ecoscsi_template = {
+ .module = THIS_MODULE,
+ .name = "Serial Port EcoSCSI NCR5380",
+ .proc_name = "ecoscsi",
+ .info = ecoscsi_info,
+ .queuecommand = ecoscsi_queue_command,
+ .eh_abort_handler = NCR5380_abort,
+ .eh_device_reset_handler= NCR5380_device_reset,
+ .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+static struct Scsi_Host *host;
+
+static int __init ecoscsi_init(void)
+{
+
+ host = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata));
+ if (!host)
+ return 0;
+
+ host->io_port = 0x80ce8000;
+ host->n_io_port = 144;
+ host->irq = IRQ_NONE;
+
+ if (!(request_region(host->io_port, host->n_io_port, "ecoscsi")) )
+ goto unregister_scsi;
+
+ ecoscsi_write(host, MODE_REG, 0x20); /* Is it really SCSI? */
+ if (ecoscsi_read(host, MODE_REG) != 0x20) /* Write to a reg. */
+ goto release_reg;
+
+ ecoscsi_write(host, MODE_REG, 0x00 ); /* it back. */
+ if (ecoscsi_read(host, MODE_REG) != 0x00)
+ goto release_reg;
+
+ NCR5380_init(host, 0);
+
+ printk("scsi%d: at port 0x%08lx irqs disabled", host->host_no, host->io_port);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ host->can_queue, host->cmd_per_lun, ECOSCSI_PUBLIC_RELEASE);
+ printk("\nscsi%d:", host->host_no);
+ NCR5380_print_options(host);
+ printk("\n");
+
+ scsi_add_host(host, NULL); /* XXX handle failure */
+ scsi_scan_host(host);
+ return 0;
+
+release_reg:
+ release_region(host->io_port, host->n_io_port);
+unregister_scsi:
+ scsi_host_put(host);
+ return -ENODEV;
+}
+
+static void __exit ecoscsi_exit(void)
+{
+ scsi_remove_host(host);
+
+ if (shpnt->irq != IRQ_NONE)
+ free_irq(shpnt->irq, NULL);
+ NCR5380_exit(host);
+ if (shpnt->io_port)
+ release_region(shpnt->io_port, shpnt->n_io_port);
+
+ scsi_host_put(host);
+ return 0;
+}
+
+module_init(ecoscsi_init);
+module_exit(ecoscsi_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c
new file mode 100644
index 000000000000..78b7e543471b
--- /dev/null
+++ b/drivers/scsi/arm/eesox.c
@@ -0,0 +1,680 @@
+/*
+ * linux/drivers/acorn/scsi/eesox.c
+ *
+ * Copyright (C) 1997-2005 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This driver is based on experimentation. Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ * 01-10-1997 RMK Created, READONLY version
+ * 15-02-1998 RMK READ/WRITE version
+ * added DMA support and hardware definitions
+ * 14-03-1998 RMK Updated DMA support
+ * Added terminator control
+ * 15-04-1998 RMK Only do PIO if FAS216 will allow it.
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
+ * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new
+ * error handling code.
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/pgtable.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+#include "fas216.h"
+#include "scsi.h"
+
+#include <scsi/scsicam.h>
+
+#define EESOX_FAS216_OFFSET 0x3000
+#define EESOX_FAS216_SHIFT 5
+
+#define EESOX_DMASTAT 0x2800
+#define EESOX_STAT_INTR 0x01
+#define EESOX_STAT_DMA 0x02
+
+#define EESOX_CONTROL 0x2800
+#define EESOX_INTR_ENABLE 0x04
+#define EESOX_TERM_ENABLE 0x02
+#define EESOX_RESET 0x01
+
+#define EESOX_DMADATA 0x3800
+
+#define VERSION "1.10 (17/01/2003 2.5.59)"
+
+/*
+ * Use term=0,1,0,0,0 to turn terminators on/off
+ */
+static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
+#define NR_SG 256
+
+struct eesoxscsi_info {
+ FAS216_Info info;
+ struct expansion_card *ec;
+ void __iomem *base;
+ void __iomem *ctl_port;
+ unsigned int control;
+ struct scatterlist sg[NR_SG]; /* Scatter DMA list */
+};
+
+/* Prototype: void eesoxscsi_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on EESOX SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+eesoxscsi_irqenable(struct expansion_card *ec, int irqnr)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;
+
+ info->control |= EESOX_INTR_ENABLE;
+
+ writeb(info->control, info->ctl_port);
+}
+
+/* Prototype: void eesoxscsi_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on EESOX SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data;
+
+ info->control &= ~EESOX_INTR_ENABLE;
+
+ writeb(info->control, info->ctl_port);
+}
+
+static const expansioncard_ops_t eesoxscsi_ops = {
+ .irqenable = eesoxscsi_irqenable,
+ .irqdisable = eesoxscsi_irqdisable,
+};
+
+/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off)
+ * Purpose : Turn the EESOX SCSI terminators on or off
+ * Params : host - card to turn on/off
+ * : on_off - !0 to turn on, 0 to turn off
+ */
+static void
+eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ if (on_off)
+ info->control |= EESOX_TERM_ENABLE;
+ else
+ info->control &= ~EESOX_TERM_ENABLE;
+
+ writeb(info->control, info->ctl_port);
+ spin_unlock_irqrestore(host->host_lock, flags);
+}
+
+/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs)
+ * Purpose : handle interrupts from EESOX SCSI card
+ * Params : irq - interrupt number
+ * dev_id - user-defined (Scsi_Host structure)
+ * regs - processor registers at interrupt
+ */
+static irqreturn_t
+eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct eesoxscsi_info *info = dev_id;
+
+ return fas216_intr(&info->info);
+}
+
+/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : type of transfer to be performed
+ */
+static fasdmatype_t
+eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+ struct device *dev = scsi_get_device(host);
+ int dmach = info->info.scsi.dma;
+
+ if (dmach != NO_DMA &&
+ (min_type == fasdma_real_all || SCp->this_residual >= 512)) {
+ int bufs, map_dir, dma_dir;
+
+ bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
+
+ if (direction == DMA_OUT)
+ map_dir = DMA_TO_DEVICE,
+ dma_dir = DMA_MODE_WRITE;
+ else
+ map_dir = DMA_FROM_DEVICE,
+ dma_dir = DMA_MODE_READ;
+
+ dma_map_sg(dev, info->sg, bufs + 1, map_dir);
+
+ disable_dma(dmach);
+ set_dma_sg(dmach, info->sg, bufs + 1);
+ set_dma_mode(dmach, dma_dir);
+ enable_dma(dmach);
+ return fasdma_real_all;
+ }
+ /*
+ * We don't do DMA, we only do slow PIO
+ *
+ * Some day, we will do Pseudo DMA
+ */
+ return fasdma_pseudo;
+}
+
+static void eesoxscsi_buffer_in(void *buf, int length, void __iomem *base)
+{
+ const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;
+ const void __iomem *reg_dmastat = base + EESOX_DMASTAT;
+ const void __iomem *reg_dmadata = base + EESOX_DMADATA;
+ const register unsigned long mask = 0xffff;
+
+ do {
+ unsigned int status;
+
+ /*
+ * Interrupt request?
+ */
+ status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));
+ if (status & STAT_INT)
+ break;
+
+ /*
+ * DMA request active?
+ */
+ status = readb(reg_dmastat);
+ if (!(status & EESOX_STAT_DMA))
+ continue;
+
+ /*
+ * Get number of bytes in FIFO
+ */
+ status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;
+ if (status > 16)
+ status = 16;
+ if (status > length)
+ status = length;
+
+ /*
+ * Align buffer.
+ */
+ if (((u32)buf) & 2 && status >= 2) {
+ *(u16 *)buf = readl(reg_dmadata);
+ buf += 2;
+ status -= 2;
+ length -= 2;
+ }
+
+ if (status >= 8) {
+ unsigned long l1, l2;
+
+ l1 = readl(reg_dmadata) & mask;
+ l1 |= readl(reg_dmadata) << 16;
+ l2 = readl(reg_dmadata) & mask;
+ l2 |= readl(reg_dmadata) << 16;
+ *(u32 *)buf = l1;
+ buf += 4;
+ *(u32 *)buf = l2;
+ buf += 4;
+ length -= 8;
+ continue;
+ }
+
+ if (status >= 4) {
+ unsigned long l1;
+
+ l1 = readl(reg_dmadata) & mask;
+ l1 |= readl(reg_dmadata) << 16;
+
+ *(u32 *)buf = l1;
+ buf += 4;
+ length -= 4;
+ continue;
+ }
+
+ if (status >= 2) {
+ *(u16 *)buf = readl(reg_dmadata);
+ buf += 2;
+ length -= 2;
+ }
+ } while (length);
+}
+
+static void eesoxscsi_buffer_out(void *buf, int length, void __iomem *base)
+{
+ const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;
+ const void __iomem *reg_dmastat = base + EESOX_DMASTAT;
+ const void __iomem *reg_dmadata = base + EESOX_DMADATA;
+
+ do {
+ unsigned int status;
+
+ /*
+ * Interrupt request?
+ */
+ status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT));
+ if (status & STAT_INT)
+ break;
+
+ /*
+ * DMA request active?
+ */
+ status = readb(reg_dmastat);
+ if (!(status & EESOX_STAT_DMA))
+ continue;
+
+ /*
+ * Get number of bytes in FIFO
+ */
+ status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF;
+ if (status > 16)
+ status = 16;
+ status = 16 - status;
+ if (status > length)
+ status = length;
+ status &= ~1;
+
+ /*
+ * Align buffer.
+ */
+ if (((u32)buf) & 2 && status >= 2) {
+ writel(*(u16 *)buf << 16, reg_dmadata);
+ buf += 2;
+ status -= 2;
+ length -= 2;
+ }
+
+ if (status >= 8) {
+ unsigned long l1, l2;
+
+ l1 = *(u32 *)buf;
+ buf += 4;
+ l2 = *(u32 *)buf;
+ buf += 4;
+
+ writel(l1 << 16, reg_dmadata);
+ writel(l1, reg_dmadata);
+ writel(l2 << 16, reg_dmadata);
+ writel(l2, reg_dmadata);
+ length -= 8;
+ continue;
+ }
+
+ if (status >= 4) {
+ unsigned long l1;
+
+ l1 = *(u32 *)buf;
+ buf += 4;
+
+ writel(l1 << 16, reg_dmadata);
+ writel(l1, reg_dmadata);
+ length -= 4;
+ continue;
+ }
+
+ if (status >= 2) {
+ writel(*(u16 *)buf << 16, reg_dmadata);
+ buf += 2;
+ length -= 2;
+ }
+ } while (length);
+}
+
+static void
+eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t dir, int transfer_size)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+ if (dir == DMA_IN) {
+ eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, info->base);
+ } else {
+ eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, info->base);
+ }
+}
+
+/* Prototype: int eesoxscsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ */
+static void
+eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+ if (info->info.scsi.dma != NO_DMA)
+ disable_dma(info->info.scsi.dma);
+}
+
+/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *eesoxscsi_info(struct Scsi_Host *host)
+{
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+ static char string[150];
+
+ sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
+ host->hostt->name, info->info.scsi.type, info->ec->slot_no,
+ VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff");
+
+ return string;
+}
+
+/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+ * Purpose : Set a driver specific function
+ * Params : host - host to setup
+ * : buffer - buffer containing string describing operation
+ * : length - length of string
+ * Returns : -EINVAL, or 0
+ */
+static int
+eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+{
+ int ret = length;
+
+ if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) {
+ buffer += 9;
+ length -= 9;
+
+ if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
+ if (buffer[5] == '1')
+ eesoxscsi_terminator_ctl(host, 1);
+ else if (buffer[5] == '0')
+ eesoxscsi_terminator_ctl(host, 0);
+ else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset,
+ * int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int eesoxscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ struct eesoxscsi_info *info;
+ char *p = buffer;
+ int pos;
+
+ if (inout == 1)
+ return eesoxscsi_set_proc_info(host, buffer, length);
+
+ info = (struct eesoxscsi_info *)host->hostdata;
+
+ p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION);
+ p += fas216_print_host(&info->info, p);
+ p += sprintf(p, "Term : o%s\n",
+ info->control & EESOX_TERM_ENABLE ? "n" : "ff");
+
+ p += fas216_print_stats(&info->info, p);
+ p += fas216_print_devices(&info->info, p);
+
+ *start = buffer + offset;
+ pos = p - buffer - offset;
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+static ssize_t eesoxscsi_show_term(struct device *dev, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+
+ return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0);
+}
+
+static ssize_t eesoxscsi_store_term(struct device *dev, const char *buf, size_t len)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+ unsigned long flags;
+
+ if (len > 1) {
+ spin_lock_irqsave(host->host_lock, flags);
+ if (buf[0] != '0') {
+ info->control |= EESOX_TERM_ENABLE;
+ } else {
+ info->control &= ~EESOX_TERM_ENABLE;
+ }
+ writeb(info->control, info->ctl_port);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
+ eesoxscsi_show_term, eesoxscsi_store_term);
+
+static Scsi_Host_Template eesox_template = {
+ .module = THIS_MODULE,
+ .proc_info = eesoxscsi_proc_info,
+ .name = "EESOX SCSI",
+ .info = eesoxscsi_info,
+ .queuecommand = fas216_queue_command,
+ .eh_host_reset_handler = fas216_eh_host_reset,
+ .eh_bus_reset_handler = fas216_eh_bus_reset,
+ .eh_device_reset_handler = fas216_eh_device_reset,
+ .eh_abort_handler = fas216_eh_abort,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "eesox",
+};
+
+static int __devinit
+eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ struct eesoxscsi_info *info;
+ unsigned long resbase, reslen;
+ void __iomem *base;
+ int ret;
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST);
+ reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST);
+ base = ioremap(resbase, reslen);
+ if (!base) {
+ ret = -ENOMEM;
+ goto out_region;
+ }
+
+ host = scsi_host_alloc(&eesox_template,
+ sizeof(struct eesoxscsi_info));
+ if (!host) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ ecard_set_drvdata(ec, host);
+
+ info = (struct eesoxscsi_info *)host->hostdata;
+ info->ec = ec;
+ info->base = base;
+ info->ctl_port = base + EESOX_CONTROL;
+ info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0;
+ writeb(info->control, info->ctl_port);
+
+ info->info.scsi.io_base = base + EESOX_FAS216_OFFSET;
+ info->info.scsi.io_shift = EESOX_FAS216_SHIFT;
+ info->info.scsi.irq = ec->irq;
+ info->info.scsi.dma = ec->dma;
+ info->info.ifcfg.clockrate = 40; /* MHz */
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = 200; /* ns */
+ info->info.ifcfg.sync_max_depth = 7;
+ info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 1;
+ info->info.ifcfg.wide_max_size = 0;
+ info->info.ifcfg.capabilities = FASCAP_PSEUDODMA;
+ info->info.dma.setup = eesoxscsi_dma_setup;
+ info->info.dma.pseudo = eesoxscsi_dma_pseudo;
+ info->info.dma.stop = eesoxscsi_dma_stop;
+
+ ec->irqaddr = base + EESOX_DMASTAT;
+ ec->irqmask = EESOX_STAT_INTR;
+ ec->irq_data = info;
+ ec->ops = &eesoxscsi_ops;
+
+ device_create_file(&ec->dev, &dev_attr_bus_term);
+
+ ret = fas216_init(host);
+ if (ret)
+ goto out_free;
+
+ ret = request_irq(ec->irq, eesoxscsi_intr, 0, "eesoxscsi", info);
+ if (ret) {
+ printk("scsi%d: IRQ%d not free: %d\n",
+ host->host_no, ec->irq, ret);
+ goto out_remove;
+ }
+
+ if (info->info.scsi.dma != NO_DMA) {
+ if (request_dma(info->info.scsi.dma, "eesox")) {
+ printk("scsi%d: DMA%d not free, DMA disabled\n",
+ host->host_no, info->info.scsi.dma);
+ info->info.scsi.dma = NO_DMA;
+ } else {
+ set_dma_speed(info->info.scsi.dma, 180);
+ info->info.ifcfg.capabilities |= FASCAP_DMA;
+ info->info.ifcfg.cntl3 |= CNTL3_BS8;
+ }
+ }
+
+ ret = fas216_add(host, &ec->dev);
+ if (ret == 0)
+ goto out;
+
+ if (info->info.scsi.dma != NO_DMA)
+ free_dma(info->info.scsi.dma);
+ free_irq(ec->irq, host);
+
+ out_remove:
+ fas216_remove(host);
+
+ out_free:
+ device_remove_file(&ec->dev, &dev_attr_bus_term);
+ scsi_host_put(host);
+
+ out_unmap:
+ iounmap(base);
+
+ out_region:
+ ecard_release_resources(ec);
+
+ out:
+ return ret;
+}
+
+static void __devexit eesoxscsi_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata;
+
+ ecard_set_drvdata(ec, NULL);
+ fas216_remove(host);
+
+ if (info->info.scsi.dma != NO_DMA)
+ free_dma(info->info.scsi.dma);
+ free_irq(ec->irq, info);
+
+ device_remove_file(&ec->dev, &dev_attr_bus_term);
+
+ iounmap(info->base);
+
+ fas216_release(host);
+ scsi_host_put(host);
+ ecard_release_resources(ec);
+}
+
+static const struct ecard_id eesoxscsi_cids[] = {
+ { MANU_EESOX, PROD_EESOX_SCSI2 },
+ { 0xffff, 0xffff },
+};
+
+static struct ecard_driver eesoxscsi_driver = {
+ .probe = eesoxscsi_probe,
+ .remove = __devexit_p(eesoxscsi_remove),
+ .id_table = eesoxscsi_cids,
+ .drv = {
+ .name = "eesoxscsi",
+ },
+};
+
+static int __init eesox_init(void)
+{
+ return ecard_register_driver(&eesoxscsi_driver);
+}
+
+static void __exit eesox_exit(void)
+{
+ ecard_remove_driver(&eesoxscsi_driver);
+}
+
+module_init(eesox_init);
+module_exit(eesox_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines");
+MODULE_PARM(term, "1-8i");
+MODULE_PARM_DESC(term, "SCSI bus termination");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
new file mode 100644
index 000000000000..5411e850c833
--- /dev/null
+++ b/drivers/scsi/arm/fas216.c
@@ -0,0 +1,3043 @@
+/*
+ * linux/drivers/acorn/scsi/fas216.c
+ *
+ * Copyright (C) 1997-2003 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
+ * other sources, including:
+ * the AMD Am53CF94 data sheet
+ * the AMD Am53C94 data sheet
+ *
+ * This is a generic driver. To use it, have a look at cumana_2.c. You
+ * should define your own structure that overlays FAS216_Info, eg:
+ * struct my_host_data {
+ * FAS216_Info info;
+ * ... my host specific data ...
+ * };
+ *
+ * Changelog:
+ * 30-08-1997 RMK Created
+ * 14-09-1997 RMK Started disconnect support
+ * 08-02-1998 RMK Corrected real DMA support
+ * 15-02-1998 RMK Started sync xfer support
+ * 06-04-1998 RMK Tightened conditions for printing incomplete
+ * transfers
+ * 02-05-1998 RMK Added extra checks in fas216_reset
+ * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns
+ * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
+ * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT
+ * 02-04-2000 RMK Converted to use the new error handling, and
+ * automatically request sense data upon check
+ * condition status from targets.
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+#include "fas216.h"
+#include "scsi.h"
+
+/* NOTE: SCSI2 Synchronous transfers *require* DMA according to
+ * the data sheet. This restriction is crazy, especially when
+ * you only want to send 16 bytes! What were the guys who
+ * designed this chip on at that time? Did they read the SCSI2
+ * spec at all? The following sections are taken from the SCSI2
+ * standard (s2r10) concerning this:
+ *
+ * > IMPLEMENTORS NOTES:
+ * > (1) Re-negotiation at every selection is not recommended, since a
+ * > significant performance impact is likely.
+ *
+ * > The implied synchronous agreement shall remain in effect until a BUS DEVICE
+ * > RESET message is received, until a hard reset condition occurs, or until one
+ * > of the two SCSI devices elects to modify the agreement. The default data
+ * > transfer mode is asynchronous data transfer mode. The default data transfer
+ * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard
+ * > reset condition.
+ *
+ * In total, this means that once you have elected to use synchronous
+ * transfers, you must always use DMA.
+ *
+ * I was thinking that this was a good chip until I found this restriction ;(
+ */
+#define SCSI2_SYNC
+#undef SCSI2_TAG
+
+#undef DEBUG_CONNECT
+#undef DEBUG_MESSAGES
+
+#undef CHECK_STRUCTURE
+
+#define LOG_CONNECT (1 << 0)
+#define LOG_BUSSERVICE (1 << 1)
+#define LOG_FUNCTIONDONE (1 << 2)
+#define LOG_MESSAGES (1 << 3)
+#define LOG_BUFFER (1 << 4)
+#define LOG_ERROR (1 << 8)
+
+static int level_mask = LOG_ERROR;
+
+module_param(level_mask, int, 0644);
+
+static int __init fas216_log_setup(char *str)
+{
+ char *s;
+
+ level_mask = 0;
+
+ while ((s = strsep(&str, ",")) != NULL) {
+ switch (s[0]) {
+ case 'a':
+ if (strcmp(s, "all") == 0)
+ level_mask |= -1;
+ break;
+ case 'b':
+ if (strncmp(s, "bus", 3) == 0)
+ level_mask |= LOG_BUSSERVICE;
+ if (strncmp(s, "buf", 3) == 0)
+ level_mask |= LOG_BUFFER;
+ break;
+ case 'c':
+ level_mask |= LOG_CONNECT;
+ break;
+ case 'e':
+ level_mask |= LOG_ERROR;
+ break;
+ case 'm':
+ level_mask |= LOG_MESSAGES;
+ break;
+ case 'n':
+ if (strcmp(s, "none") == 0)
+ level_mask = 0;
+ break;
+ case 's':
+ level_mask |= LOG_FUNCTIONDONE;
+ break;
+ }
+ }
+ return 1;
+}
+
+__setup("fas216_logging=", fas216_log_setup);
+
+static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg)
+{
+ unsigned int off = reg << info->scsi.io_shift;
+ return readb(info->scsi.io_base + off);
+}
+
+static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val)
+{
+ unsigned int off = reg << info->scsi.io_shift;
+ writeb(val, info->scsi.io_base + off);
+}
+
+static void fas216_dumpstate(FAS216_Info *info)
+{
+ unsigned char is, stat, inst;
+
+ is = fas216_readb(info, REG_IS);
+ stat = fas216_readb(info, REG_STAT);
+ inst = fas216_readb(info, REG_INST);
+
+ printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X"
+ " INST=%02X IS=%02X CFIS=%02X",
+ fas216_readb(info, REG_CTCL),
+ fas216_readb(info, REG_CTCM),
+ fas216_readb(info, REG_CMD), stat, inst, is,
+ fas216_readb(info, REG_CFIS));
+ printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n",
+ fas216_readb(info, REG_CNTL1),
+ fas216_readb(info, REG_CNTL2),
+ fas216_readb(info, REG_CNTL3),
+ fas216_readb(info, REG_CTCH));
+}
+
+static void print_SCp(Scsi_Pointer *SCp, const char *prefix, const char *suffix)
+{
+ printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s",
+ prefix, SCp->ptr, SCp->this_residual, SCp->buffer,
+ SCp->buffers_residual, suffix);
+}
+
+static void fas216_dumpinfo(FAS216_Info *info)
+{
+ static int used = 0;
+ int i;
+
+ if (used++)
+ return;
+
+ printk("FAS216_Info=\n");
+ printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n",
+ info->magic_start, info->host, info->SCpnt,
+ info->origSCpnt);
+ printk(" scsi={ io_shift=%X irq=%X cfg={ %X %X %X %X }\n",
+ info->scsi.io_shift, info->scsi.irq,
+ info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2],
+ info->scsi.cfg[3]);
+ printk(" type=%p phase=%X\n",
+ info->scsi.type, info->scsi.phase);
+ print_SCp(&info->scsi.SCp, " SCp={ ", " }\n");
+ printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n",
+ info->scsi.async_stp,
+ info->scsi.disconnectable, info->scsi.aborting);
+ printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n"
+ " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n",
+ info->stats.queues, info->stats.removes, info->stats.fins,
+ info->stats.reads, info->stats.writes, info->stats.miscs,
+ info->stats.disconnects, info->stats.aborts, info->stats.bus_resets,
+ info->stats.host_resets);
+ printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n",
+ info->ifcfg.clockrate, info->ifcfg.select_timeout,
+ info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth);
+ for (i = 0; i < 8; i++) {
+ printk(" busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
+ i, info->busyluns[i], i,
+ info->device[i].disconnect_ok, info->device[i].stp,
+ info->device[i].sof, info->device[i].sync_state);
+ }
+ printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
+ info->dma.transfer_type, info->dma.setup,
+ info->dma.pseudo, info->dma.stop);
+ printk(" internal_done=%X magic_end=%lX }\n",
+ info->internal_done, info->magic_end);
+}
+
+#ifdef CHECK_STRUCTURE
+static void __fas216_checkmagic(FAS216_Info *info, const char *func)
+{
+ int corruption = 0;
+ if (info->magic_start != MAGIC) {
+ printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n");
+ corruption++;
+ }
+ if (info->magic_end != MAGIC) {
+ printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n");
+ corruption++;
+ }
+ if (corruption) {
+ fas216_dumpinfo(info);
+ panic("scsi memory space corrupted in %s", func);
+ }
+}
+#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__)
+#else
+#define fas216_checkmagic(info)
+#endif
+
+static const char *fas216_bus_phase(int stat)
+{
+ static const char *phases[] = {
+ "DATA OUT", "DATA IN",
+ "COMMAND", "STATUS",
+ "MISC OUT", "MISC IN",
+ "MESG OUT", "MESG IN"
+ };
+
+ return phases[stat & STAT_BUSMASK];
+}
+
+static const char *fas216_drv_phase(FAS216_Info *info)
+{
+ static const char *phases[] = {
+ [PHASE_IDLE] = "idle",
+ [PHASE_SELECTION] = "selection",
+ [PHASE_COMMAND] = "command",
+ [PHASE_DATAOUT] = "data out",
+ [PHASE_DATAIN] = "data in",
+ [PHASE_MSGIN] = "message in",
+ [PHASE_MSGIN_DISCONNECT]= "disconnect",
+ [PHASE_MSGOUT_EXPECT] = "expect message out",
+ [PHASE_MSGOUT] = "message out",
+ [PHASE_STATUS] = "status",
+ [PHASE_DONE] = "done",
+ };
+
+ if (info->scsi.phase < ARRAY_SIZE(phases) &&
+ phases[info->scsi.phase])
+ return phases[info->scsi.phase];
+ return "???";
+}
+
+static char fas216_target(FAS216_Info *info)
+{
+ if (info->SCpnt)
+ return '0' + info->SCpnt->device->id;
+ else
+ return 'H';
+}
+
+static void
+fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap)
+{
+ static char buf[1024];
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ printk("scsi%d.%c: %s", info->host->host_no, target, buf);
+}
+
+static void
+fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...)
+{
+ va_list args;
+
+ if (level != 0 && !(level & level_mask))
+ return;
+
+ va_start(args, fmt);
+ fas216_do_log(info, '0' + SCpnt->device->id, fmt, args);
+ va_end(args);
+
+ printk(" CDB: ");
+ print_command(SCpnt->cmnd);
+}
+
+static void
+fas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...)
+{
+ va_list args;
+
+ if (level != 0 && !(level & level_mask))
+ return;
+
+ if (target < 0)
+ target = 'H';
+ else
+ target += '0';
+
+ va_start(args, fmt);
+ fas216_do_log(info, target, fmt, args);
+ va_end(args);
+
+ printk("\n");
+}
+
+static void fas216_log(FAS216_Info *info, int level, char *fmt, ...)
+{
+ va_list args;
+
+ if (level != 0 && !(level & level_mask))
+ return;
+
+ va_start(args, fmt);
+ fas216_do_log(info, fas216_target(info), fmt, args);
+ va_end(args);
+
+ printk("\n");
+}
+
+#define PH_SIZE 32
+
+static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE];
+static int ph_ptr;
+
+static void add_debug_list(int stat, int ssr, int isr, int ph)
+{
+ ph_list[ph_ptr].stat = stat;
+ ph_list[ph_ptr].ssr = ssr;
+ ph_list[ph_ptr].isr = isr;
+ ph_list[ph_ptr].ph = ph;
+
+ ph_ptr = (ph_ptr + 1) & (PH_SIZE-1);
+}
+
+static struct { int command; void *from; } cmd_list[8];
+static int cmd_ptr;
+
+static void fas216_cmd(FAS216_Info *info, unsigned int command)
+{
+ cmd_list[cmd_ptr].command = command;
+ cmd_list[cmd_ptr].from = __builtin_return_address(0);
+
+ cmd_ptr = (cmd_ptr + 1) & 7;
+
+ fas216_writeb(info, REG_CMD, command);
+}
+
+static void print_debug_list(void)
+{
+ int i;
+
+ i = ph_ptr;
+
+ printk(KERN_ERR "SCSI IRQ trail\n");
+ do {
+ printk(" %02x:%02x:%02x:%1x",
+ ph_list[i].stat, ph_list[i].ssr,
+ ph_list[i].isr, ph_list[i].ph);
+ i = (i + 1) & (PH_SIZE - 1);
+ if (((i ^ ph_ptr) & 7) == 0)
+ printk("\n");
+ } while (i != ph_ptr);
+ if ((i ^ ph_ptr) & 7)
+ printk("\n");
+
+ i = cmd_ptr;
+ printk(KERN_ERR "FAS216 commands: ");
+ do {
+ printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from);
+ i = (i + 1) & 7;
+ } while (i != cmd_ptr);
+ printk("\n");
+}
+
+static void fas216_done(FAS216_Info *info, unsigned int result);
+
+/**
+ * fas216_get_last_msg - retrive last message from the list
+ * @info: interface to search
+ * @pos: current fifo position
+ *
+ * Retrieve a last message from the list, using position in fifo.
+ */
+static inline unsigned short
+fas216_get_last_msg(FAS216_Info *info, int pos)
+{
+ unsigned short packed_msg = NOP;
+ struct message *msg;
+ int msgnr = 0;
+
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ if (pos >= msg->fifo)
+ break;
+ }
+
+ if (msg) {
+ if (msg->msg[0] == EXTENDED_MESSAGE)
+ packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8;
+ else
+ packed_msg = msg->msg[0];
+ }
+
+ fas216_log(info, LOG_MESSAGES,
+ "Message: %04x found at position %02x\n", packed_msg, pos);
+
+ return packed_msg;
+}
+
+/**
+ * fas216_syncperiod - calculate STP register value
+ * @info: state structure for interface connected to device
+ * @ns: period in ns (between subsequent bytes)
+ *
+ * Calculate value to be loaded into the STP register for a given period
+ * in ns. Returns a value suitable for REG_STP.
+ */
+static int fas216_syncperiod(FAS216_Info *info, int ns)
+{
+ int value = (info->ifcfg.clockrate * ns) / 1000;
+
+ fas216_checkmagic(info);
+
+ if (value < 4)
+ value = 4;
+ else if (value > 35)
+ value = 35;
+
+ return value & 31;
+}
+
+/**
+ * fas216_set_sync - setup FAS216 chip for specified transfer period.
+ * @info: state structure for interface connected to device
+ * @target: target
+ *
+ * Correctly setup FAS216 chip for specified transfer period.
+ * Notes : we need to switch the chip out of FASTSCSI mode if we have
+ * a transfer period >= 200ns - otherwise the chip will violate
+ * the SCSI timings.
+ */
+static void fas216_set_sync(FAS216_Info *info, int target)
+{
+ unsigned int cntl3;
+
+ fas216_writeb(info, REG_SOF, info->device[target].sof);
+ fas216_writeb(info, REG_STP, info->device[target].stp);
+
+ cntl3 = info->scsi.cfg[2];
+ if (info->device[target].period >= (200 / 4))
+ cntl3 = cntl3 & ~CNTL3_FASTSCSI;
+
+ fas216_writeb(info, REG_CNTL3, cntl3);
+}
+
+/* Synchronous transfer support
+ *
+ * Note: The SCSI II r10 spec says (5.6.12):
+ *
+ * (2) Due to historical problems with early host adapters that could
+ * not accept an SDTR message, some targets may not initiate synchronous
+ * negotiation after a power cycle as required by this standard. Host
+ * adapters that support synchronous mode may avoid the ensuing failure
+ * modes when the target is independently power cycled by initiating a
+ * synchronous negotiation on each REQUEST SENSE and INQUIRY command.
+ * This approach increases the SCSI bus overhead and is not recommended
+ * for new implementations. The correct method is to respond to an
+ * SDTR message with a MESSAGE REJECT message if the either the
+ * initiator or target devices does not support synchronous transfers
+ * or does not want to negotiate for synchronous transfers at the time.
+ * Using the correct method assures compatibility with wide data
+ * transfers and future enhancements.
+ *
+ * We will always initiate a synchronous transfer negotiation request on
+ * every INQUIRY or REQUEST SENSE message, unless the target itself has
+ * at some point performed a synchronous transfer negotiation request, or
+ * we have synchronous transfers disabled for this device.
+ */
+
+/**
+ * fas216_handlesync - Handle a synchronous transfer message
+ * @info: state structure for interface
+ * @msg: message from target
+ *
+ * Handle a synchronous transfer message from the target
+ */
+static void fas216_handlesync(FAS216_Info *info, char *msg)
+{
+ struct fas216_device *dev = &info->device[info->SCpnt->device->id];
+ enum { sync, async, none, reject } res = none;
+
+#ifdef SCSI2_SYNC
+ switch (msg[0]) {
+ case MESSAGE_REJECT:
+ /* Synchronous transfer request failed.
+ * Note: SCSI II r10:
+ *
+ * SCSI devices that are capable of synchronous
+ * data transfers shall not respond to an SDTR
+ * message with a MESSAGE REJECT message.
+ *
+ * Hence, if we get this condition, we disable
+ * negotiation for this device.
+ */
+ if (dev->sync_state == neg_inprogress) {
+ dev->sync_state = neg_invalid;
+ res = async;
+ }
+ break;
+
+ case EXTENDED_MESSAGE:
+ switch (dev->sync_state) {
+ /* We don't accept synchronous transfer requests.
+ * Respond with a MESSAGE_REJECT to prevent a
+ * synchronous transfer agreement from being reached.
+ */
+ case neg_invalid:
+ res = reject;
+ break;
+
+ /* We were not negotiating a synchronous transfer,
+ * but the device sent us a negotiation request.
+ * Honour the request by sending back a SDTR
+ * message containing our capability, limited by
+ * the targets capability.
+ */
+ default:
+ fas216_cmd(info, CMD_SETATN);
+ if (msg[4] > info->ifcfg.sync_max_depth)
+ msg[4] = info->ifcfg.sync_max_depth;
+ if (msg[3] < 1000 / info->ifcfg.clockrate)
+ msg[3] = 1000 / info->ifcfg.clockrate;
+
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 5,
+ EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+ msg[3], msg[4]);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+ /* This is wrong. The agreement is not in effect
+ * until this message is accepted by the device
+ */
+ dev->sync_state = neg_targcomplete;
+ res = sync;
+ break;
+
+ /* We initiated the synchronous transfer negotiation,
+ * and have successfully received a response from the
+ * target. The synchronous transfer agreement has been
+ * reached. Note: if the values returned are out of our
+ * bounds, we must reject the message.
+ */
+ case neg_inprogress:
+ res = reject;
+ if (msg[4] <= info->ifcfg.sync_max_depth &&
+ msg[3] >= 1000 / info->ifcfg.clockrate) {
+ dev->sync_state = neg_complete;
+ res = sync;
+ }
+ break;
+ }
+ }
+#else
+ res = reject;
+#endif
+
+ switch (res) {
+ case sync:
+ dev->period = msg[3];
+ dev->sof = msg[4];
+ dev->stp = fas216_syncperiod(info, msg[3] * 4);
+ fas216_set_sync(info, info->SCpnt->device->id);
+ break;
+
+ case reject:
+ fas216_cmd(info, CMD_SETATN);
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+ case async:
+ dev->period = info->ifcfg.asyncperiod / 4;
+ dev->sof = 0;
+ dev->stp = info->scsi.async_stp;
+ fas216_set_sync(info, info->SCpnt->device->id);
+ break;
+
+ case none:
+ break;
+ }
+}
+
+/**
+ * fas216_updateptrs - update data pointers after transfer suspended/paused
+ * @info: interface's local pointer to update
+ * @bytes_transferred: number of bytes transferred
+ *
+ * Update data pointers after transfer suspended/paused
+ */
+static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
+{
+ Scsi_Pointer *SCp = &info->scsi.SCp;
+
+ fas216_checkmagic(info);
+
+ BUG_ON(bytes_transferred < 0);
+
+ info->SCpnt->request_bufflen -= bytes_transferred;
+
+ while (bytes_transferred != 0) {
+ if (SCp->this_residual > bytes_transferred)
+ break;
+ /*
+ * We have used up this buffer. Move on to the
+ * next buffer.
+ */
+ bytes_transferred -= SCp->this_residual;
+ if (!next_SCp(SCp) && bytes_transferred) {
+ printk(KERN_WARNING "scsi%d.%c: out of buffers\n",
+ info->host->host_no, '0' + info->SCpnt->device->id);
+ return;
+ }
+ }
+
+ SCp->this_residual -= bytes_transferred;
+ if (SCp->this_residual)
+ SCp->ptr += bytes_transferred;
+ else
+ SCp->ptr = NULL;
+}
+
+/**
+ * fas216_pio - transfer data off of/on to card using programmed IO
+ * @info: interface to transfer data to/from
+ * @direction: direction to transfer data (DMA_OUT/DMA_IN)
+ *
+ * Transfer data off of/on to card using programmed IO.
+ * Notes: this is incredibly slow.
+ */
+static void fas216_pio(FAS216_Info *info, fasdmadir_t direction)
+{
+ Scsi_Pointer *SCp = &info->scsi.SCp;
+
+ fas216_checkmagic(info);
+
+ if (direction == DMA_OUT)
+ fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp));
+ else
+ put_next_SCp_byte(SCp, fas216_readb(info, REG_FF));
+
+ if (SCp->this_residual == 0)
+ next_SCp(SCp);
+}
+
+static void fas216_set_stc(FAS216_Info *info, unsigned int length)
+{
+ fas216_writeb(info, REG_STCL, length);
+ fas216_writeb(info, REG_STCM, length >> 8);
+ fas216_writeb(info, REG_STCH, length >> 16);
+}
+
+static unsigned int fas216_get_ctc(FAS216_Info *info)
+{
+ return fas216_readb(info, REG_CTCL) +
+ (fas216_readb(info, REG_CTCM) << 8) +
+ (fas216_readb(info, REG_CTCH) << 16);
+}
+
+/**
+ * fas216_cleanuptransfer - clean up after a transfer has completed.
+ * @info: interface to clean up
+ *
+ * Update the data pointers according to the number of bytes transferred
+ * on the SCSI bus.
+ */
+static void fas216_cleanuptransfer(FAS216_Info *info)
+{
+ unsigned long total, residual, fifo;
+ fasdmatype_t dmatype = info->dma.transfer_type;
+
+ info->dma.transfer_type = fasdma_none;
+
+ /*
+ * PIO transfers do not need to be cleaned up.
+ */
+ if (dmatype == fasdma_pio || dmatype == fasdma_none)
+ return;
+
+ if (dmatype == fasdma_real_all)
+ total = info->SCpnt->request_bufflen;
+ else
+ total = info->scsi.SCp.this_residual;
+
+ residual = fas216_get_ctc(info);
+
+ fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+
+ fas216_log(info, LOG_BUFFER, "cleaning up from previous "
+ "transfer: length 0x%06x, residual 0x%x, fifo %d",
+ total, residual, fifo);
+
+ /*
+ * If we were performing Data-Out, the transfer counter
+ * counts down each time a byte is transferred by the
+ * host to the FIFO. This means we must include the
+ * bytes left in the FIFO from the transfer counter.
+ */
+ if (info->scsi.phase == PHASE_DATAOUT)
+ residual += fifo;
+
+ fas216_updateptrs(info, total - residual);
+}
+
+/**
+ * fas216_transfer - Perform a DMA/PIO transfer off of/on to card
+ * @info: interface from which device disconnected from
+ *
+ * Start a DMA/PIO transfer off of/on to card
+ */
+static void fas216_transfer(FAS216_Info *info)
+{
+ fasdmadir_t direction;
+ fasdmatype_t dmatype;
+
+ fas216_log(info, LOG_BUFFER,
+ "starttransfer: buffer %p length 0x%06x reqlen 0x%06x",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
+ info->SCpnt->request_bufflen);
+
+ if (!info->scsi.SCp.ptr) {
+ fas216_log(info, LOG_ERROR, "null buffer passed to "
+ "fas216_starttransfer");
+ print_SCp(&info->scsi.SCp, "SCp: ", "\n");
+ print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n");
+ return;
+ }
+
+ /*
+ * If we have a synchronous transfer agreement in effect, we must
+ * use DMA mode. If we are using asynchronous transfers, we may
+ * use DMA mode or PIO mode.
+ */
+ if (info->device[info->SCpnt->device->id].sof)
+ dmatype = fasdma_real_all;
+ else
+ dmatype = fasdma_pio;
+
+ if (info->scsi.phase == PHASE_DATAOUT)
+ direction = DMA_OUT;
+ else
+ direction = DMA_IN;
+
+ if (info->dma.setup)
+ dmatype = info->dma.setup(info->host, &info->scsi.SCp,
+ direction, dmatype);
+ info->dma.transfer_type = dmatype;
+
+ if (dmatype == fasdma_real_all)
+ fas216_set_stc(info, info->SCpnt->request_bufflen);
+ else
+ fas216_set_stc(info, info->scsi.SCp.this_residual);
+
+ switch (dmatype) {
+ case fasdma_pio:
+ fas216_log(info, LOG_BUFFER, "PIO transfer");
+ fas216_writeb(info, REG_SOF, 0);
+ fas216_writeb(info, REG_STP, info->scsi.async_stp);
+ fas216_cmd(info, CMD_TRANSFERINFO);
+ fas216_pio(info, direction);
+ break;
+
+ case fasdma_pseudo:
+ fas216_log(info, LOG_BUFFER, "pseudo transfer");
+ fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA);
+ info->dma.pseudo(info->host, &info->scsi.SCp,
+ direction, info->SCpnt->transfersize);
+ break;
+
+ case fasdma_real_block:
+ fas216_log(info, LOG_BUFFER, "block dma transfer");
+ fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA);
+ break;
+
+ case fasdma_real_all:
+ fas216_log(info, LOG_BUFFER, "total dma transfer");
+ fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA);
+ break;
+
+ default:
+ fas216_log(info, LOG_BUFFER | LOG_ERROR,
+ "invalid FAS216 DMA type");
+ break;
+ }
+}
+
+/**
+ * fas216_stoptransfer - Stop a DMA transfer onto / off of the card
+ * @info: interface from which device disconnected from
+ *
+ * Called when we switch away from DATA IN or DATA OUT phases.
+ */
+static void fas216_stoptransfer(FAS216_Info *info)
+{
+ fas216_checkmagic(info);
+
+ if (info->dma.transfer_type == fasdma_real_all ||
+ info->dma.transfer_type == fasdma_real_block)
+ info->dma.stop(info->host, &info->scsi.SCp);
+
+ fas216_cleanuptransfer(info);
+
+ if (info->scsi.phase == PHASE_DATAIN) {
+ unsigned int fifo;
+
+ /*
+ * If we were performing Data-In, then the FIFO counter
+ * contains the number of bytes not transferred via DMA
+ * from the on-board FIFO. Read them manually.
+ */
+ fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+ while (fifo && info->scsi.SCp.ptr) {
+ *info->scsi.SCp.ptr = fas216_readb(info, REG_FF);
+ fas216_updateptrs(info, 1);
+ fifo--;
+ }
+ } else {
+ /*
+ * After a Data-Out phase, there may be unsent
+ * bytes left in the FIFO. Flush them out.
+ */
+ fas216_cmd(info, CMD_FLUSHFIFO);
+ }
+}
+
+static void fas216_aborttransfer(FAS216_Info *info)
+{
+ fas216_checkmagic(info);
+
+ if (info->dma.transfer_type == fasdma_real_all ||
+ info->dma.transfer_type == fasdma_real_block)
+ info->dma.stop(info->host, &info->scsi.SCp);
+
+ info->dma.transfer_type = fasdma_none;
+ fas216_cmd(info, CMD_FLUSHFIFO);
+}
+
+static void fas216_kick(FAS216_Info *info);
+
+/**
+ * fas216_disconnected_intr - handle device disconnection
+ * @info: interface from which device disconnected from
+ *
+ * Handle device disconnection
+ */
+static void fas216_disconnect_intr(FAS216_Info *info)
+{
+ unsigned long flags;
+
+ fas216_checkmagic(info);
+
+ fas216_log(info, LOG_CONNECT, "disconnect phase=%02x",
+ info->scsi.phase);
+
+ msgqueue_flush(&info->scsi.msgs);
+
+ switch (info->scsi.phase) {
+ case PHASE_SELECTION: /* while selecting - no target */
+ case PHASE_SELSTEPS:
+ fas216_done(info, DID_NO_CONNECT);
+ break;
+
+ case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */
+ info->scsi.disconnectable = 1;
+ info->scsi.phase = PHASE_IDLE;
+ info->stats.disconnects += 1;
+ spin_lock_irqsave(&info->host_lock, flags);
+ if (info->scsi.phase == PHASE_IDLE)
+ fas216_kick(info);
+ spin_unlock_irqrestore(&info->host_lock, flags);
+ break;
+
+ case PHASE_DONE: /* at end of command - complete */
+ fas216_done(info, DID_OK);
+ break;
+
+ case PHASE_MSGOUT: /* message out - possible ABORT message */
+ if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) {
+ info->scsi.aborting = 0;
+ fas216_done(info, DID_ABORT);
+ break;
+ }
+
+ default: /* huh? */
+ printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n",
+ info->host->host_no, fas216_target(info), fas216_drv_phase(info));
+ print_debug_list();
+ fas216_stoptransfer(info);
+ fas216_done(info, DID_ERROR);
+ break;
+ }
+}
+
+/**
+ * fas216_reselected_intr - start reconnection of a device
+ * @info: interface which was reselected
+ *
+ * Start reconnection of a device
+ */
+static void
+fas216_reselected_intr(FAS216_Info *info)
+{
+ unsigned int cfis, i;
+ unsigned char msg[4];
+ unsigned char target, lun, tag;
+
+ fas216_checkmagic(info);
+
+ WARN_ON(info->scsi.phase == PHASE_SELECTION ||
+ info->scsi.phase == PHASE_SELSTEPS);
+
+ cfis = fas216_readb(info, REG_CFIS);
+
+ fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x",
+ info->scsi.phase, cfis);
+
+ cfis &= CFIS_CF;
+
+ if (cfis < 2 || cfis > 4) {
+ printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
+ info->host->host_no);
+ goto bad_message;
+ }
+
+ for (i = 0; i < cfis; i++)
+ msg[i] = fas216_readb(info, REG_FF);
+
+ if (!(msg[0] & (1 << info->host->this_id)) ||
+ !(msg[1] & 0x80))
+ goto initiator_error;
+
+ target = msg[0] & ~(1 << info->host->this_id);
+ target = ffs(target) - 1;
+ lun = msg[1] & 7;
+ tag = 0;
+
+ if (cfis >= 3) {
+ if (msg[2] != SIMPLE_QUEUE_TAG)
+ goto initiator_error;
+
+ tag = msg[3];
+ }
+
+ /* set up for synchronous transfers */
+ fas216_writeb(info, REG_SDID, target);
+ fas216_set_sync(info, target);
+ msgqueue_flush(&info->scsi.msgs);
+
+ fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x",
+ target, lun, tag);
+
+ if (info->scsi.disconnectable && info->SCpnt) {
+ info->scsi.disconnectable = 0;
+ if (info->SCpnt->device->id == target &&
+ info->SCpnt->device->lun == lun &&
+ info->SCpnt->tag == tag) {
+ fas216_log(info, LOG_CONNECT, "reconnected previously executing command");
+ } else {
+ queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
+ fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue");
+ info->SCpnt = NULL;
+ }
+ }
+ if (!info->SCpnt) {
+ info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected,
+ target, lun, tag);
+ fas216_log(info, LOG_CONNECT, "had to get command");
+ }
+
+ if (info->SCpnt) {
+ /*
+ * Restore data pointer from SAVED data pointer
+ */
+ info->scsi.SCp = info->SCpnt->SCp;
+
+ fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ info->scsi.phase = PHASE_MSGIN;
+ } else {
+ /*
+ * Our command structure not found - abort the
+ * command on the target. Since we have no
+ * record of this command, we can't send
+ * an INITIATOR DETECTED ERROR message.
+ */
+ fas216_cmd(info, CMD_SETATN);
+
+#if 0
+ if (tag)
+ msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag);
+ else
+#endif
+ msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ info->scsi.aborting = 1;
+ }
+
+ fas216_cmd(info, CMD_MSGACCEPTED);
+ return;
+
+ initiator_error:
+ printk(KERN_ERR "scsi%d.H: error during reselection: bytes",
+ info->host->host_no);
+ for (i = 0; i < cfis; i++)
+ printk(" %02x", msg[i]);
+ printk("\n");
+ bad_message:
+ fas216_cmd(info, CMD_SETATN);
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ fas216_cmd(info, CMD_MSGACCEPTED);
+}
+
+static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen)
+{
+ int i;
+
+ switch (message[0]) {
+ case COMMAND_COMPLETE:
+ if (msglen != 1)
+ goto unrecognised;
+
+ printk(KERN_ERR "scsi%d.%c: command complete with no "
+ "status in MESSAGE_IN?\n",
+ info->host->host_no, fas216_target(info));
+ break;
+
+ case SAVE_POINTERS:
+ if (msglen != 1)
+ goto unrecognised;
+
+ /*
+ * Save current data pointer to SAVED data pointer
+ * SCSI II standard says that we must not acknowledge
+ * this until we have really saved pointers.
+ * NOTE: we DO NOT save the command nor status pointers
+ * as required by the SCSI II standard. These always
+ * point to the start of their respective areas.
+ */
+ info->SCpnt->SCp = info->scsi.SCp;
+ info->SCpnt->SCp.sent_command = 0;
+ fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER,
+ "save data pointers: [%p, %X]",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ break;
+
+ case RESTORE_POINTERS:
+ if (msglen != 1)
+ goto unrecognised;
+
+ /*
+ * Restore current data pointer from SAVED data pointer
+ */
+ info->scsi.SCp = info->SCpnt->SCp;
+ fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER,
+ "restore data pointers: [%p, 0x%x]",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ break;
+
+ case DISCONNECT:
+ if (msglen != 1)
+ goto unrecognised;
+
+ info->scsi.phase = PHASE_MSGIN_DISCONNECT;
+ break;
+
+ case MESSAGE_REJECT:
+ if (msglen != 1)
+ goto unrecognised;
+
+ switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) {
+ case EXTENDED_MESSAGE | EXTENDED_SDTR << 8:
+ fas216_handlesync(info, message);
+ break;
+
+ default:
+ fas216_log(info, 0, "reject, last message 0x%04x",
+ fas216_get_last_msg(info, info->scsi.msgin_fifo));
+ }
+ break;
+
+ case NOP:
+ break;
+
+ case EXTENDED_MESSAGE:
+ if (msglen < 3)
+ goto unrecognised;
+
+ switch (message[2]) {
+ case EXTENDED_SDTR: /* Sync transfer negotiation request/reply */
+ fas216_handlesync(info, message);
+ break;
+
+ default:
+ goto unrecognised;
+ }
+ break;
+
+ default:
+ goto unrecognised;
+ }
+ return;
+
+unrecognised:
+ fas216_log(info, 0, "unrecognised message, rejecting");
+ printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info));
+ for (i = 0; i < msglen; i++)
+ printk("%s%02X", i & 31 ? " " : "\n ", message[i]);
+ printk("\n");
+
+ /*
+ * Something strange seems to be happening here -
+ * I can't use SETATN since the chip gives me an
+ * invalid command interrupt when I do. Weird.
+ */
+fas216_cmd(info, CMD_NOP);
+fas216_dumpstate(info);
+ fas216_cmd(info, CMD_SETATN);
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+fas216_dumpstate(info);
+}
+
+static int fas216_wait_cmd(FAS216_Info *info, int cmd)
+{
+ int tout;
+ int stat;
+
+ fas216_cmd(info, cmd);
+
+ for (tout = 1000; tout; tout -= 1) {
+ stat = fas216_readb(info, REG_STAT);
+ if (stat & (STAT_INT|STAT_PARITYERROR))
+ break;
+ udelay(1);
+ }
+
+ return stat;
+}
+
+static int fas216_get_msg_byte(FAS216_Info *info)
+{
+ unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED);
+
+ if ((stat & STAT_INT) == 0)
+ goto timedout;
+
+ if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+ goto unexpected_phase_change;
+
+ fas216_readb(info, REG_INST);
+
+ stat = fas216_wait_cmd(info, CMD_TRANSFERINFO);
+
+ if ((stat & STAT_INT) == 0)
+ goto timedout;
+
+ if (stat & STAT_PARITYERROR)
+ goto parity_error;
+
+ if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+ goto unexpected_phase_change;
+
+ fas216_readb(info, REG_INST);
+
+ return fas216_readb(info, REG_FF);
+
+timedout:
+ fas216_log(info, LOG_ERROR, "timed out waiting for message byte");
+ return -1;
+
+unexpected_phase_change:
+ fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat);
+ return -2;
+
+parity_error:
+ fas216_log(info, LOG_ERROR, "parity error during message in phase");
+ return -3;
+}
+
+/**
+ * fas216_message - handle a function done interrupt from FAS216 chip
+ * @info: interface which caused function done interrupt
+ *
+ * Handle a function done interrupt from FAS216 chip
+ */
+static void fas216_message(FAS216_Info *info)
+{
+ unsigned char *message = info->scsi.message;
+ unsigned int msglen = 1;
+ int msgbyte = 0;
+
+ fas216_checkmagic(info);
+
+ message[0] = fas216_readb(info, REG_FF);
+
+ if (message[0] == EXTENDED_MESSAGE) {
+ msgbyte = fas216_get_msg_byte(info);
+
+ if (msgbyte >= 0) {
+ message[1] = msgbyte;
+
+ for (msglen = 2; msglen < message[1] + 2; msglen++) {
+ msgbyte = fas216_get_msg_byte(info);
+
+ if (msgbyte >= 0)
+ message[msglen] = msgbyte;
+ else
+ break;
+ }
+ }
+ }
+
+ if (msgbyte == -3)
+ goto parity_error;
+
+#ifdef DEBUG_MESSAGES
+ {
+ int i;
+
+ printk("scsi%d.%c: message in: ",
+ info->host->host_no, fas216_target(info));
+ for (i = 0; i < msglen; i++)
+ printk("%02X ", message[i]);
+ printk("\n");
+ }
+#endif
+
+ fas216_parse_message(info, message, msglen);
+ fas216_cmd(info, CMD_MSGACCEPTED);
+ return;
+
+parity_error:
+ fas216_cmd(info, CMD_SETATN);
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ fas216_cmd(info, CMD_MSGACCEPTED);
+ return;
+}
+
+/**
+ * fas216_send_command - send command after all message bytes have been sent
+ * @info: interface which caused bus service
+ *
+ * Send a command to a target after all message bytes have been sent
+ */
+static void fas216_send_command(FAS216_Info *info)
+{
+ int i;
+
+ fas216_checkmagic(info);
+
+ fas216_cmd(info, CMD_NOP|CMD_WITHDMA);
+ fas216_cmd(info, CMD_FLUSHFIFO);
+
+ /* load command */
+ for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++)
+ fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]);
+
+ fas216_cmd(info, CMD_TRANSFERINFO);
+
+ info->scsi.phase = PHASE_COMMAND;
+}
+
+/**
+ * fas216_send_messageout - handle bus service to send a message
+ * @info: interface which caused bus service
+ *
+ * Handle bus service to send a message.
+ * Note: We do not allow the device to change the data direction!
+ */
+static void fas216_send_messageout(FAS216_Info *info, int start)
+{
+ unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs);
+
+ fas216_checkmagic(info);
+
+ fas216_cmd(info, CMD_FLUSHFIFO);
+
+ if (tot_msglen) {
+ struct message *msg;
+ int msgnr = 0;
+
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ int i;
+
+ for (i = start; i < msg->length; i++)
+ fas216_writeb(info, REG_FF, msg->msg[i]);
+
+ msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF);
+ start = 0;
+ }
+ } else
+ fas216_writeb(info, REG_FF, NOP);
+
+ fas216_cmd(info, CMD_TRANSFERINFO);
+
+ info->scsi.phase = PHASE_MSGOUT;
+}
+
+/**
+ * fas216_busservice_intr - handle bus service interrupt from FAS216 chip
+ * @info: interface which caused bus service interrupt
+ * @stat: Status register contents
+ * @is: SCSI Status register contents
+ *
+ * Handle a bus service interrupt from FAS216 chip
+ */
+static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is)
+{
+ fas216_checkmagic(info);
+
+ fas216_log(info, LOG_BUSSERVICE,
+ "bus service: stat=%02x is=%02x phase=%02x",
+ stat, is, info->scsi.phase);
+
+ switch (info->scsi.phase) {
+ case PHASE_SELECTION:
+ if ((is & IS_BITS) != IS_MSGBYTESENT)
+ goto bad_is;
+ break;
+
+ case PHASE_SELSTEPS:
+ switch (is & IS_BITS) {
+ case IS_SELARB:
+ case IS_MSGBYTESENT:
+ goto bad_is;
+
+ case IS_NOTCOMMAND:
+ case IS_EARLYPHASE:
+ if ((stat & STAT_BUSMASK) == STAT_MESGIN)
+ break;
+ goto bad_is;
+
+ case IS_COMPLETE:
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ fas216_cmd(info, CMD_NOP);
+
+#define STATE(st,ph) ((ph) << 3 | (st))
+ /* This table describes the legal SCSI state transitions,
+ * as described by the SCSI II spec.
+ */
+ switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) {
+ case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */
+ case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */
+ case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */
+ case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */
+ info->scsi.phase = PHASE_DATAIN;
+ fas216_transfer(info);
+ return;
+
+ case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */
+ case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */
+ fas216_cleanuptransfer(info);
+ fas216_transfer(info);
+ return;
+
+ case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */
+ fas216_cmd(info, CMD_FLUSHFIFO);
+ info->scsi.phase = PHASE_DATAOUT;
+ fas216_transfer(info);
+ return;
+
+ case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */
+ case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */
+ fas216_stoptransfer(info);
+ case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */
+ case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */
+ case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */
+ case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */
+ fas216_cmd(info, CMD_INITCMDCOMPLETE);
+ info->scsi.phase = PHASE_STATUS;
+ return;
+
+ case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */
+ case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */
+ fas216_stoptransfer(info);
+ case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */
+ case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */
+ case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */
+ info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+ fas216_cmd(info, CMD_FLUSHFIFO);
+ fas216_cmd(info, CMD_TRANSFERINFO);
+ info->scsi.phase = PHASE_MSGIN;
+ return;
+
+ case STATE(STAT_MESGIN, PHASE_MSGIN):
+ info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+ fas216_cmd(info, CMD_TRANSFERINFO);
+ return;
+
+ case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */
+ case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */
+ fas216_send_command(info);
+ info->scsi.phase = PHASE_COMMAND;
+ return;
+
+
+ /*
+ * Selection -> Message Out
+ */
+ case STATE(STAT_MESGOUT, PHASE_SELECTION):
+ fas216_send_messageout(info, 1);
+ return;
+
+ /*
+ * Message Out -> Message Out
+ */
+ case STATE(STAT_MESGOUT, PHASE_SELSTEPS):
+ case STATE(STAT_MESGOUT, PHASE_MSGOUT):
+ /*
+ * If we get another message out phase, this usually
+ * means some parity error occurred. Resend complete
+ * set of messages. If we have more than one byte to
+ * send, we need to assert ATN again.
+ */
+ if (info->device[info->SCpnt->device->id].parity_check) {
+ /*
+ * We were testing... good, the device
+ * supports parity checking.
+ */
+ info->device[info->SCpnt->device->id].parity_check = 0;
+ info->device[info->SCpnt->device->id].parity_enabled = 1;
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+ }
+
+ if (msgqueue_msglength(&info->scsi.msgs) > 1)
+ fas216_cmd(info, CMD_SETATN);
+ /*FALLTHROUGH*/
+
+ /*
+ * Any -> Message Out
+ */
+ case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT):
+ fas216_send_messageout(info, 0);
+ return;
+
+ /* Error recovery rules.
+ * These either attempt to abort or retry the operation.
+ * TODO: we need more of these
+ */
+ case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */
+ /* error - we've sent out all the command bytes
+ * we have.
+ * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS
+ * to include the command bytes sent for this to work
+ * correctly.
+ */
+ printk(KERN_ERR "scsi%d.%c: "
+ "target trying to receive more command bytes\n",
+ info->host->host_no, fas216_target(info));
+ fas216_cmd(info, CMD_SETATN);
+ fas216_set_stc(info, 15);
+ fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA);
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ return;
+ }
+
+ if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) {
+ printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat));
+ msgqueue_flush(&info->scsi.msgs);
+ fas216_cmd(info, CMD_SETATN);
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ info->scsi.aborting = 1;
+ fas216_cmd(info, CMD_TRANSFERINFO);
+ return;
+ }
+ printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat),
+ fas216_drv_phase(info));
+ print_debug_list();
+ return;
+
+bad_is:
+ fas216_log(info, 0, "bus service at step %d?", is & IS_BITS);
+ fas216_dumpstate(info);
+ print_debug_list();
+
+ fas216_done(info, DID_ERROR);
+}
+
+/**
+ * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip
+ * @info: interface which caused function done interrupt
+ * @stat: Status register contents
+ * @is: SCSI Status register contents
+ *
+ * Handle a function done interrupt from FAS216 chip
+ */
+static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is)
+{
+ unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF;
+
+ fas216_checkmagic(info);
+
+ fas216_log(info, LOG_FUNCTIONDONE,
+ "function done: stat=%02x is=%02x phase=%02x",
+ stat, is, info->scsi.phase);
+
+ switch (info->scsi.phase) {
+ case PHASE_STATUS: /* status phase - read status and msg */
+ if (fifo_len != 2) {
+ fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len);
+ }
+ /*
+ * Read status then message byte.
+ */
+ info->scsi.SCp.Status = fas216_readb(info, REG_FF);
+ info->scsi.SCp.Message = fas216_readb(info, REG_FF);
+ info->scsi.phase = PHASE_DONE;
+ fas216_cmd(info, CMD_MSGACCEPTED);
+ break;
+
+ case PHASE_IDLE:
+ case PHASE_SELECTION:
+ case PHASE_SELSTEPS:
+ break;
+
+ case PHASE_MSGIN: /* message in phase */
+ if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+ info->scsi.msgin_fifo = fifo_len;
+ fas216_message(info);
+ break;
+ }
+
+ default:
+ fas216_log(info, 0, "internal phase %s for function done?"
+ " What do I do with this?",
+ fas216_target(info), fas216_drv_phase(info));
+ }
+}
+
+static void fas216_bus_reset(FAS216_Info *info)
+{
+ neg_t sync_state;
+ int i;
+
+ msgqueue_flush(&info->scsi.msgs);
+
+ sync_state = neg_invalid;
+
+#ifdef SCSI2_SYNC
+ if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA))
+ sync_state = neg_wait;
+#endif
+
+ info->scsi.phase = PHASE_IDLE;
+ info->SCpnt = NULL; /* bug! */
+ memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp));
+
+ for (i = 0; i < 8; i++) {
+ info->device[i].disconnect_ok = info->ifcfg.disconnect_ok;
+ info->device[i].sync_state = sync_state;
+ info->device[i].period = info->ifcfg.asyncperiod / 4;
+ info->device[i].stp = info->scsi.async_stp;
+ info->device[i].sof = 0;
+ info->device[i].wide_xfer = 0;
+ }
+
+ info->rst_bus_status = 1;
+ wake_up(&info->eh_wait);
+}
+
+/**
+ * fas216_intr - handle interrupts to progress a command
+ * @info: interface to service
+ *
+ * Handle interrupts from the interface to progress a command
+ */
+irqreturn_t fas216_intr(FAS216_Info *info)
+{
+ unsigned char inst, is, stat;
+ int handled = IRQ_NONE;
+
+ fas216_checkmagic(info);
+
+ stat = fas216_readb(info, REG_STAT);
+ is = fas216_readb(info, REG_IS);
+ inst = fas216_readb(info, REG_INST);
+
+ add_debug_list(stat, is, inst, info->scsi.phase);
+
+ if (stat & STAT_INT) {
+ if (inst & INST_BUSRESET) {
+ fas216_log(info, 0, "bus reset detected");
+ fas216_bus_reset(info);
+ scsi_report_bus_reset(info->host, 0);
+ } else if (inst & INST_ILLEGALCMD) {
+ fas216_log(info, LOG_ERROR, "illegal command given\n");
+ fas216_dumpstate(info);
+ print_debug_list();
+ } else if (inst & INST_DISCONNECT)
+ fas216_disconnect_intr(info);
+ else if (inst & INST_RESELECTED) /* reselected */
+ fas216_reselected_intr(info);
+ else if (inst & INST_BUSSERVICE) /* bus service request */
+ fas216_busservice_intr(info, stat, is);
+ else if (inst & INST_FUNCDONE) /* function done */
+ fas216_funcdone_intr(info, stat, is);
+ else
+ fas216_log(info, 0, "unknown interrupt received:"
+ " phase %s inst %02X is %02X stat %02X",
+ fas216_drv_phase(info), inst, is, stat);
+ handled = IRQ_HANDLED;
+ }
+ return handled;
+}
+
+static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+ int tot_msglen;
+
+ /* following what the ESP driver says */
+ fas216_set_stc(info, 0);
+ fas216_cmd(info, CMD_NOP | CMD_WITHDMA);
+
+ /* flush FIFO */
+ fas216_cmd(info, CMD_FLUSHFIFO);
+
+ /* load bus-id and timeout */
+ fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id));
+ fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout);
+
+ /* synchronous transfers */
+ fas216_set_sync(info, SCpnt->device->id);
+
+ tot_msglen = msgqueue_msglength(&info->scsi.msgs);
+
+#ifdef DEBUG_MESSAGES
+ {
+ struct message *msg;
+ int msgnr = 0, i;
+
+ printk("scsi%d.%c: message out: ",
+ info->host->host_no, '0' + SCpnt->device->id);
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ printk("{ ");
+ for (i = 0; i < msg->length; i++)
+ printk("%02x ", msg->msg[i]);
+ printk("} ");
+ }
+ printk("\n");
+ }
+#endif
+
+ if (tot_msglen == 1 || tot_msglen == 3) {
+ /*
+ * We have an easy message length to send...
+ */
+ struct message *msg;
+ int msgnr = 0, i;
+
+ info->scsi.phase = PHASE_SELSTEPS;
+
+ /* load message bytes */
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ for (i = 0; i < msg->length; i++)
+ fas216_writeb(info, REG_FF, msg->msg[i]);
+ msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF);
+ }
+
+ /* load command */
+ for (i = 0; i < SCpnt->cmd_len; i++)
+ fas216_writeb(info, REG_FF, SCpnt->cmnd[i]);
+
+ if (tot_msglen == 1)
+ fas216_cmd(info, CMD_SELECTATN);
+ else
+ fas216_cmd(info, CMD_SELECTATN3);
+ } else {
+ /*
+ * We have an unusual number of message bytes to send.
+ * Load first byte into fifo, and issue SELECT with ATN and
+ * stop steps.
+ */
+ struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0);
+
+ fas216_writeb(info, REG_FF, msg->msg[0]);
+ msg->fifo = 1;
+
+ fas216_cmd(info, CMD_SELECTATNSTOP);
+ }
+}
+
+/*
+ * Decide whether we need to perform a parity test on this device.
+ * Can also be used to force parity error conditions during initial
+ * information transfer phase (message out) for test purposes.
+ */
+static int parity_test(FAS216_Info *info, int target)
+{
+#if 0
+ if (target == 3) {
+ info->device[target].parity_check = 0;
+ return 1;
+ }
+#endif
+ return info->device[target].parity_check;
+}
+
+static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+ int disconnect_ok;
+
+ /*
+ * claim host busy
+ */
+ info->scsi.phase = PHASE_SELECTION;
+ info->scsi.SCp = SCpnt->SCp;
+ info->SCpnt = SCpnt;
+ info->dma.transfer_type = fasdma_none;
+
+ if (parity_test(info, SCpnt->device->id))
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE);
+ else
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+
+ /*
+ * Don't allow request sense commands to disconnect.
+ */
+ disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE &&
+ info->device[SCpnt->device->id].disconnect_ok;
+
+ /*
+ * build outgoing message bytes
+ */
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun));
+
+ /*
+ * add tag message if required
+ */
+ if (SCpnt->tag)
+ msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
+
+ do {
+#ifdef SCSI2_SYNC
+ if ((info->device[SCpnt->device->id].sync_state == neg_wait ||
+ info->device[SCpnt->device->id].sync_state == neg_complete) &&
+ (SCpnt->cmnd[0] == REQUEST_SENSE ||
+ SCpnt->cmnd[0] == INQUIRY)) {
+ info->device[SCpnt->device->id].sync_state = neg_inprogress;
+ msgqueue_addmsg(&info->scsi.msgs, 5,
+ EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+ 1000 / info->ifcfg.clockrate,
+ info->ifcfg.sync_max_depth);
+ break;
+ }
+#endif
+ } while (0);
+
+ __fas216_start_command(info, SCpnt);
+}
+
+static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+#ifdef SCSI2_TAG
+ /*
+ * tagged queuing - allocate a new tag to this command
+ */
+ if (SCpnt->device->simple_tags && SCpnt->cmnd[0] != REQUEST_SENSE &&
+ SCpnt->cmnd[0] != INQUIRY) {
+ SCpnt->device->current_tag += 1;
+ if (SCpnt->device->current_tag == 0)
+ SCpnt->device->current_tag = 1;
+ SCpnt->tag = SCpnt->device->current_tag;
+ } else
+#endif
+ set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns);
+
+ info->stats.removes += 1;
+ switch (SCpnt->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ info->stats.writes += 1;
+ break;
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ info->stats.reads += 1;
+ break;
+ default:
+ info->stats.miscs += 1;
+ break;
+ }
+}
+
+static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+ struct message *msg;
+
+ /*
+ * claim host busy
+ */
+ info->scsi.phase = PHASE_SELECTION;
+ info->scsi.SCp = SCpnt->SCp;
+ info->SCpnt = SCpnt;
+ info->dma.transfer_type = fasdma_none;
+
+ fas216_log(info, LOG_ERROR, "sending bus device reset");
+
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET);
+
+ /* following what the ESP driver says */
+ fas216_set_stc(info, 0);
+ fas216_cmd(info, CMD_NOP | CMD_WITHDMA);
+
+ /* flush FIFO */
+ fas216_cmd(info, CMD_FLUSHFIFO);
+
+ /* load bus-id and timeout */
+ fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id));
+ fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout);
+
+ /* synchronous transfers */
+ fas216_set_sync(info, SCpnt->device->id);
+
+ msg = msgqueue_getmsg(&info->scsi.msgs, 0);
+
+ fas216_writeb(info, REG_FF, BUS_DEVICE_RESET);
+ msg->fifo = 1;
+
+ fas216_cmd(info, CMD_SELECTATNSTOP);
+}
+
+/**
+ * fas216_kick - kick a command to the interface
+ * @info: our host interface to kick
+ *
+ * Kick a command to the interface, interface should be idle.
+ * Notes: Interrupts are always disabled!
+ */
+static void fas216_kick(FAS216_Info *info)
+{
+ Scsi_Cmnd *SCpnt = NULL;
+#define TYPE_OTHER 0
+#define TYPE_RESET 1
+#define TYPE_QUEUE 2
+ int where_from = TYPE_OTHER;
+
+ fas216_checkmagic(info);
+
+ /*
+ * Obtain the next command to process.
+ */
+ do {
+ if (info->rstSCpnt) {
+ SCpnt = info->rstSCpnt;
+ /* don't remove it */
+ where_from = TYPE_RESET;
+ break;
+ }
+
+ if (info->reqSCpnt) {
+ SCpnt = info->reqSCpnt;
+ info->reqSCpnt = NULL;
+ break;
+ }
+
+ if (info->origSCpnt) {
+ SCpnt = info->origSCpnt;
+ info->origSCpnt = NULL;
+ break;
+ }
+
+ /* retrieve next command */
+ if (!SCpnt) {
+ SCpnt = queue_remove_exclude(&info->queues.issue,
+ info->busyluns);
+ where_from = TYPE_QUEUE;
+ break;
+ }
+ } while (0);
+
+ if (!SCpnt) {
+ /*
+ * no command pending, so enable reselection.
+ */
+ fas216_cmd(info, CMD_ENABLESEL);
+ return;
+ }
+
+ /*
+ * We're going to start a command, so disable reselection
+ */
+ fas216_cmd(info, CMD_DISABLESEL);
+
+ if (info->scsi.disconnectable && info->SCpnt) {
+ fas216_log(info, LOG_CONNECT,
+ "moved command for %d to disconnected queue",
+ info->SCpnt->device->id);
+ queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
+ info->scsi.disconnectable = 0;
+ info->SCpnt = NULL;
+ }
+
+ fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt,
+ "starting");
+
+ switch (where_from) {
+ case TYPE_QUEUE:
+ fas216_allocate_tag(info, SCpnt);
+ case TYPE_OTHER:
+ fas216_start_command(info, SCpnt);
+ break;
+ case TYPE_RESET:
+ fas216_do_bus_device_reset(info, SCpnt);
+ break;
+ }
+
+ fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+
+ /*
+ * should now get either DISCONNECT or
+ * (FUNCTION DONE with BUS SERVICE) interrupt
+ */
+}
+
+/*
+ * Clean up from issuing a BUS DEVICE RESET message to a device.
+ */
+static void
+fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result)
+{
+ fas216_log(info, LOG_ERROR, "fas216 device reset complete");
+
+ info->rstSCpnt = NULL;
+ info->rst_dev_status = 1;
+ wake_up(&info->eh_wait);
+}
+
+/**
+ * fas216_rq_sns_done - Finish processing automatic request sense command
+ * @info: interface that completed
+ * @SCpnt: command that completed
+ * @result: driver byte of result
+ *
+ * Finish processing automatic request sense command
+ */
+static void
+fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result)
+{
+ fas216_log_target(info, LOG_CONNECT, SCpnt->device->id,
+ "request sense complete, result=0x%04x%02x%02x",
+ result, SCpnt->SCp.Message, SCpnt->SCp.Status);
+
+ if (result != DID_OK || SCpnt->SCp.Status != GOOD)
+ /*
+ * Something went wrong. Make sure that we don't
+ * have valid data in the sense buffer that could
+ * confuse the higher levels.
+ */
+ memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
+//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id);
+//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); }
+ /*
+ * Note that we don't set SCpnt->result, since that should
+ * reflect the status of the command that we were asked by
+ * the upper layers to process. This would have been set
+ * correctly by fas216_std_done.
+ */
+ SCpnt->scsi_done(SCpnt);
+}
+
+/**
+ * fas216_std_done - finish processing of standard command
+ * @info: interface that completed
+ * @SCpnt: command that completed
+ * @result: driver byte of result
+ *
+ * Finish processing of standard command
+ */
+static void
+fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result)
+{
+ info->stats.fins += 1;
+
+ SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
+ info->scsi.SCp.Status;
+
+ fas216_log_command(info, LOG_CONNECT, SCpnt,
+ "command complete, result=0x%08x", SCpnt->result);
+
+ /*
+ * If the driver detected an error, we're all done.
+ */
+ if (host_byte(SCpnt->result) != DID_OK ||
+ msg_byte(SCpnt->result) != COMMAND_COMPLETE)
+ goto done;
+
+ /*
+ * If the command returned CHECK_CONDITION or COMMAND_TERMINATED
+ * status, request the sense information.
+ */
+ if (status_byte(SCpnt->result) == CHECK_CONDITION ||
+ status_byte(SCpnt->result) == COMMAND_TERMINATED)
+ goto request_sense;
+
+ /*
+ * If the command did not complete with GOOD status,
+ * we are all done here.
+ */
+ if (status_byte(SCpnt->result) != GOOD)
+ goto done;
+
+ /*
+ * We have successfully completed a command. Make sure that
+ * we do not have any buffers left to transfer. The world
+ * is not perfect, and we seem to occasionally hit this.
+ * It can be indicative of a buggy driver, target or the upper
+ * levels of the SCSI code.
+ */
+ if (info->scsi.SCp.ptr) {
+ switch (SCpnt->cmnd[0]) {
+ case INQUIRY:
+ case START_STOP:
+ case MODE_SENSE:
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: incomplete data transfer "
+ "detected: res=%08X ptr=%p len=%X CDB: ",
+ info->host->host_no, '0' + SCpnt->device->id,
+ SCpnt->result, info->scsi.SCp.ptr,
+ info->scsi.SCp.this_residual);
+ print_command(SCpnt->cmnd);
+ SCpnt->result &= ~(255 << 16);
+ SCpnt->result |= DID_BAD_TARGET << 16;
+ goto request_sense;
+ }
+ }
+
+done:
+ if (SCpnt->scsi_done) {
+ SCpnt->scsi_done(SCpnt);
+ return;
+ }
+
+ panic("scsi%d.H: null scsi_done function in fas216_done",
+ info->host->host_no);
+
+
+request_sense:
+ if (SCpnt->cmnd[0] == REQUEST_SENSE)
+ goto done;
+
+ fas216_log_target(info, LOG_CONNECT, SCpnt->device->id,
+ "requesting sense");
+ memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd));
+ SCpnt->cmnd[0] = REQUEST_SENSE;
+ SCpnt->cmnd[1] = SCpnt->device->lun << 5;
+ SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+ SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer;
+ SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer);
+ SCpnt->SCp.Message = 0;
+ SCpnt->SCp.Status = 0;
+ SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
+ SCpnt->sc_data_direction = SCSI_DATA_READ;
+ SCpnt->use_sg = 0;
+ SCpnt->tag = 0;
+ SCpnt->host_scribble = (void *)fas216_rq_sns_done;
+
+ /*
+ * Place this command into the high priority "request
+ * sense" slot. This will be the very next command
+ * executed, unless a target connects to us.
+ */
+ if (info->reqSCpnt)
+ printk(KERN_WARNING "scsi%d.%c: loosing request command\n",
+ info->host->host_no, '0' + SCpnt->device->id);
+ info->reqSCpnt = SCpnt;
+}
+
+/**
+ * fas216_done - complete processing for current command
+ * @info: interface that completed
+ * @result: driver byte of result
+ *
+ * Complete processing for current command
+ */
+static void fas216_done(FAS216_Info *info, unsigned int result)
+{
+ void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int);
+ Scsi_Cmnd *SCpnt;
+ unsigned long flags;
+
+ fas216_checkmagic(info);
+
+ if (!info->SCpnt)
+ goto no_command;
+
+ SCpnt = info->SCpnt;
+ info->SCpnt = NULL;
+ info->scsi.phase = PHASE_IDLE;
+
+ if (info->scsi.aborting) {
+ fas216_log(info, 0, "uncaught abort - returning DID_ABORT");
+ result = DID_ABORT;
+ info->scsi.aborting = 0;
+ }
+
+ /*
+ * Sanity check the completion - if we have zero bytes left
+ * to transfer, we should not have a valid pointer.
+ */
+ if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) {
+ printk("scsi%d.%c: zero bytes left to transfer, but "
+ "buffer pointer still valid: ptr=%p len=%08x CDB: ",
+ info->host->host_no, '0' + SCpnt->device->id,
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ info->scsi.SCp.ptr = NULL;
+ print_command(SCpnt->cmnd);
+ }
+
+ /*
+ * Clear down this command as completed. If we need to request
+ * the sense information, fas216_kick will re-assert the busy
+ * status.
+ */
+ info->device[SCpnt->device->id].parity_check = 0;
+ clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns);
+
+ fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble;
+ fn(info, SCpnt, result);
+
+ if (info->scsi.irq != NO_IRQ) {
+ spin_lock_irqsave(&info->host_lock, flags);
+ if (info->scsi.phase == PHASE_IDLE)
+ fas216_kick(info);
+ spin_unlock_irqrestore(&info->host_lock, flags);
+ }
+ return;
+
+no_command:
+ panic("scsi%d.H: null command in fas216_done",
+ info->host->host_no);
+}
+
+/**
+ * fas216_queue_command - queue a command for adapter to process.
+ * @SCpnt: Command to queue
+ * @done: done function to call once command is complete
+ *
+ * Queue a command for adapter to process.
+ * Returns: 0 on success, else error.
+ * Notes: io_request_lock is held, interrupts are disabled.
+ */
+int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+ int result;
+
+ fas216_checkmagic(info);
+
+ fas216_log_command(info, LOG_CONNECT, SCpnt,
+ "received command (%p)", SCpnt);
+
+ SCpnt->scsi_done = done;
+ SCpnt->host_scribble = (void *)fas216_std_done;
+ SCpnt->result = 0;
+
+ init_SCp(SCpnt);
+
+ info->stats.queues += 1;
+ SCpnt->tag = 0;
+
+ spin_lock(&info->host_lock);
+
+ /*
+ * Add command into execute queue and let it complete under
+ * whatever scheme we're using.
+ */
+ result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt);
+
+ /*
+ * If we successfully added the command,
+ * kick the interface to get it moving.
+ */
+ if (result == 0 && info->scsi.phase == PHASE_IDLE)
+ fas216_kick(info);
+ spin_unlock(&info->host_lock);
+
+ fas216_log_target(info, LOG_CONNECT, -1, "queue %s",
+ result ? "failure" : "success");
+
+ return result;
+}
+
+/**
+ * fas216_internal_done - trigger restart of a waiting thread in fas216_noqueue_command
+ * @SCpnt: Command to wake
+ *
+ * Trigger restart of a waiting thread in fas216_command
+ */
+static void fas216_internal_done(Scsi_Cmnd *SCpnt)
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+
+ fas216_checkmagic(info);
+
+ info->internal_done = 1;
+}
+
+/**
+ * fas216_noqueue_command - process a command for the adapter.
+ * @SCpnt: Command to queue
+ *
+ * Queue a command for adapter to process.
+ * Returns: scsi result code.
+ * Notes: io_request_lock is held, interrupts are disabled.
+ */
+int fas216_noqueue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+
+ fas216_checkmagic(info);
+
+ /*
+ * We should only be using this if we don't have an interrupt.
+ * Provide some "incentive" to use the queueing code.
+ */
+ BUG_ON(info->scsi.irq != NO_IRQ);
+
+ info->internal_done = 0;
+ fas216_queue_command(SCpnt, fas216_internal_done);
+
+ /*
+ * This wastes time, since we can't return until the command is
+ * complete. We can't sleep either since we may get re-entered!
+ * However, we must re-enable interrupts, or else we'll be
+ * waiting forever.
+ */
+ spin_unlock_irq(info->host->host_lock);
+
+ while (!info->internal_done) {
+ /*
+ * If we don't have an IRQ, then we must poll the card for
+ * it's interrupt, and use that to call this driver's
+ * interrupt routine. That way, we keep the command
+ * progressing. Maybe we can add some inteligence here
+ * and go to sleep if we know that the device is going
+ * to be some time (eg, disconnected).
+ */
+ if (fas216_readb(info, REG_STAT) & STAT_INT) {
+ spin_lock_irq(info->host->host_lock);
+ fas216_intr(info);
+ spin_unlock_irq(info->host->host_lock);
+ }
+ }
+
+ spin_lock_irq(info->host->host_lock);
+
+ done(SCpnt);
+
+ return 0;
+}
+
+/*
+ * Error handler timeout function. Indicate that we timed out,
+ * and wake up any error handler process so it can continue.
+ */
+static void fas216_eh_timer(unsigned long data)
+{
+ FAS216_Info *info = (FAS216_Info *)data;
+
+ fas216_log(info, LOG_ERROR, "error handling timed out\n");
+
+ del_timer(&info->eh_timer);
+
+ if (info->rst_bus_status == 0)
+ info->rst_bus_status = -1;
+ if (info->rst_dev_status == 0)
+ info->rst_dev_status = -1;
+
+ wake_up(&info->eh_wait);
+}
+
+enum res_find {
+ res_failed, /* not found */
+ res_success, /* command on issue queue */
+ res_hw_abort /* command on disconnected dev */
+};
+
+/**
+ * fas216_do_abort - decide how to abort a command
+ * @SCpnt: command to abort
+ *
+ * Decide how to abort a command.
+ * Returns: abort status
+ */
+static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+ enum res_find res = res_failed;
+
+ if (queue_remove_cmd(&info->queues.issue, SCpnt)) {
+ /*
+ * The command was on the issue queue, and has not been
+ * issued yet. We can remove the command from the queue,
+ * and acknowledge the abort. Neither the device nor the
+ * interface know about the command.
+ */
+ printk("on issue queue ");
+
+ res = res_success;
+ } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) {
+ /*
+ * The command was on the disconnected queue. We must
+ * reconnect with the device if possible, and send it
+ * an abort message.
+ */
+ printk("on disconnected queue ");
+
+ res = res_hw_abort;
+ } else if (info->SCpnt == SCpnt) {
+ printk("executing ");
+
+ switch (info->scsi.phase) {
+ /*
+ * If the interface is idle, and the command is 'disconnectable',
+ * then it is the same as on the disconnected queue.
+ */
+ case PHASE_IDLE:
+ if (info->scsi.disconnectable) {
+ info->scsi.disconnectable = 0;
+ info->SCpnt = NULL;
+ res = res_hw_abort;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (info->origSCpnt == SCpnt) {
+ /*
+ * The command will be executed next, but a command
+ * is currently using the interface. This is similar to
+ * being on the issue queue, except the busylun bit has
+ * been set.
+ */
+ info->origSCpnt = NULL;
+ clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns);
+ printk("waiting for execution ");
+ res = res_success;
+ } else
+ printk("unknown ");
+
+ return res;
+}
+
+/**
+ * fas216_eh_abort - abort this command
+ * @SCpnt: command to abort
+ *
+ * Abort this command.
+ * Returns: FAILED if unable to abort
+ * Notes: io_request_lock is taken, and irqs are disabled
+ */
+int fas216_eh_abort(Scsi_Cmnd *SCpnt)
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+ int result = FAILED;
+
+ fas216_checkmagic(info);
+
+ info->stats.aborts += 1;
+
+ printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no);
+ print_command(SCpnt->data_cmnd);
+
+ print_debug_list();
+ fas216_dumpstate(info);
+
+ printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt);
+
+ switch (fas216_find_command(info, SCpnt)) {
+ /*
+ * We found the command, and cleared it out. Either
+ * the command is still known to be executing on the
+ * target, or the busylun bit is not set.
+ */
+ case res_success:
+ printk("success\n");
+ result = SUCCESS;
+ break;
+
+ /*
+ * We need to reconnect to the target and send it an
+ * ABORT or ABORT_TAG message. We can only do this
+ * if the bus is free.
+ */
+ case res_hw_abort:
+
+
+ /*
+ * We are unable to abort the command for some reason.
+ */
+ default:
+ case res_failed:
+ printk("failed\n");
+ break;
+ }
+
+ return result;
+}
+
+/**
+ * fas216_eh_device_reset - Reset the device associated with this command
+ * @SCpnt: command specifing device to reset
+ *
+ * Reset the device associated with this command.
+ * Returns: FAILED if unable to reset.
+ * Notes: We won't be re-entered, so we'll only have one device
+ * reset on the go at one time.
+ */
+int fas216_eh_device_reset(Scsi_Cmnd *SCpnt)
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+ unsigned long flags;
+ int i, res = FAILED, target = SCpnt->device->id;
+
+ fas216_log(info, LOG_ERROR, "device reset for target %d", target);
+
+ spin_lock_irqsave(&info->host_lock, flags);
+
+ do {
+ /*
+ * If we are currently connected to a device, and
+ * it is the device we want to reset, there is
+ * nothing we can do here. Chances are it is stuck,
+ * and we need a bus reset.
+ */
+ if (info->SCpnt && !info->scsi.disconnectable &&
+ info->SCpnt->device->id == SCpnt->device->id)
+ break;
+
+ /*
+ * We're going to be resetting this device. Remove
+ * all pending commands from the driver. By doing
+ * so, we guarantee that we won't touch the command
+ * structures except to process the reset request.
+ */
+ queue_remove_all_target(&info->queues.issue, target);
+ queue_remove_all_target(&info->queues.disconnected, target);
+ if (info->origSCpnt && info->origSCpnt->device->id == target)
+ info->origSCpnt = NULL;
+ if (info->reqSCpnt && info->reqSCpnt->device->id == target)
+ info->reqSCpnt = NULL;
+ for (i = 0; i < 8; i++)
+ clear_bit(target * 8 + i, info->busyluns);
+
+ /*
+ * Hijack this SCSI command structure to send
+ * a bus device reset message to this device.
+ */
+ SCpnt->host_scribble = (void *)fas216_devicereset_done;
+
+ info->rst_dev_status = 0;
+ info->rstSCpnt = SCpnt;
+
+ if (info->scsi.phase == PHASE_IDLE)
+ fas216_kick(info);
+
+ mod_timer(&info->eh_timer, 30 * HZ);
+ spin_unlock_irqrestore(&info->host_lock, flags);
+
+ /*
+ * Wait up to 30 seconds for the reset to complete.
+ */
+ wait_event(info->eh_wait, info->rst_dev_status);
+
+ del_timer_sync(&info->eh_timer);
+ spin_lock_irqsave(&info->host_lock, flags);
+ info->rstSCpnt = NULL;
+
+ if (info->rst_dev_status == 1)
+ res = SUCCESS;
+ } while (0);
+
+ SCpnt->host_scribble = NULL;
+ spin_unlock_irqrestore(&info->host_lock, flags);
+
+ fas216_log(info, LOG_ERROR, "device reset complete: %s\n",
+ res == SUCCESS ? "success" : "failed");
+
+ return res;
+}
+
+/**
+ * fas216_eh_bus_reset - Reset the bus associated with the command
+ * @SCpnt: command specifing bus to reset
+ *
+ * Reset the bus associated with the command.
+ * Returns: FAILED if unable to reset.
+ * Notes: Further commands are blocked.
+ */
+int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt)
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+ unsigned long flags;
+ Scsi_Device *SDpnt;
+
+ fas216_checkmagic(info);
+ fas216_log(info, LOG_ERROR, "resetting bus");
+
+ info->stats.bus_resets += 1;
+
+ spin_lock_irqsave(&info->host_lock, flags);
+
+ /*
+ * Stop all activity on this interface.
+ */
+ fas216_aborttransfer(info);
+ fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]);
+
+ /*
+ * Clear any pending interrupts.
+ */
+ while (fas216_readb(info, REG_STAT) & STAT_INT)
+ fas216_readb(info, REG_INST);
+
+ info->rst_bus_status = 0;
+
+ /*
+ * For each attached hard-reset device, clear out
+ * all command structures. Leave the running
+ * command in place.
+ */
+ shost_for_each_device(SDpnt, info->host) {
+ int i;
+
+ if (SDpnt->soft_reset)
+ continue;
+
+ queue_remove_all_target(&info->queues.issue, SDpnt->id);
+ queue_remove_all_target(&info->queues.disconnected, SDpnt->id);
+ if (info->origSCpnt && info->origSCpnt->device->id == SDpnt->id)
+ info->origSCpnt = NULL;
+ if (info->reqSCpnt && info->reqSCpnt->device->id == SDpnt->id)
+ info->reqSCpnt = NULL;
+ info->SCpnt = NULL;
+
+ for (i = 0; i < 8; i++)
+ clear_bit(SDpnt->id * 8 + i, info->busyluns);
+ }
+
+ info->scsi.phase = PHASE_IDLE;
+
+ /*
+ * Reset the SCSI bus. Device cleanup happens in
+ * the interrupt handler.
+ */
+ fas216_cmd(info, CMD_RESETSCSI);
+
+ mod_timer(&info->eh_timer, jiffies + HZ);
+ spin_unlock_irqrestore(&info->host_lock, flags);
+
+ /*
+ * Wait one second for the interrupt.
+ */
+ wait_event(info->eh_wait, info->rst_bus_status);
+ del_timer_sync(&info->eh_timer);
+
+ fas216_log(info, LOG_ERROR, "bus reset complete: %s\n",
+ info->rst_bus_status == 1 ? "success" : "failed");
+
+ return info->rst_bus_status == 1 ? SUCCESS : FAILED;
+}
+
+/**
+ * fas216_init_chip - Initialise FAS216 state after reset
+ * @info: state structure for interface
+ *
+ * Initialise FAS216 state after reset
+ */
+static void fas216_init_chip(FAS216_Info *info)
+{
+ unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7;
+ fas216_writeb(info, REG_CLKF, clock);
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+ fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]);
+ fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]);
+ fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout);
+ fas216_writeb(info, REG_SOF, 0);
+ fas216_writeb(info, REG_STP, info->scsi.async_stp);
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+}
+
+/**
+ * fas216_eh_host_reset - Reset the host associated with this command
+ * @SCpnt: command specifing host to reset
+ *
+ * Reset the host associated with this command.
+ * Returns: FAILED if unable to reset.
+ * Notes: io_request_lock is taken, and irqs are disabled
+ */
+int fas216_eh_host_reset(Scsi_Cmnd *SCpnt)
+{
+ FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata;
+
+ fas216_checkmagic(info);
+
+ printk("scsi%d.%c: %s: resetting host\n",
+ info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__);
+
+ /*
+ * Reset the SCSI chip.
+ */
+ fas216_cmd(info, CMD_RESETCHIP);
+
+ /*
+ * Ugly ugly ugly!
+ * We need to release the host_lock and enable
+ * IRQs if we sleep, but we must relock and disable
+ * IRQs after the sleep.
+ */
+ spin_unlock_irq(info->host->host_lock);
+ msleep(50 * 1000/100);
+ spin_lock_irq(info->host->host_lock);
+
+ /*
+ * Release the SCSI reset.
+ */
+ fas216_cmd(info, CMD_NOP);
+
+ fas216_init_chip(info);
+
+ return SUCCESS;
+}
+
+#define TYPE_UNKNOWN 0
+#define TYPE_NCR53C90 1
+#define TYPE_NCR53C90A 2
+#define TYPE_NCR53C9x 3
+#define TYPE_Am53CF94 4
+#define TYPE_EmFAS216 5
+#define TYPE_QLFAS216 6
+
+static char *chip_types[] = {
+ "unknown",
+ "NS NCR53C90",
+ "NS NCR53C90A",
+ "NS NCR53C9x",
+ "AMD Am53CF94",
+ "Emulex FAS216",
+ "QLogic FAS216"
+};
+
+static int fas216_detect_type(FAS216_Info *info)
+{
+ int family, rev;
+
+ /*
+ * Reset the chip.
+ */
+ fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+ udelay(50);
+ fas216_writeb(info, REG_CMD, CMD_NOP);
+
+ /*
+ * Check to see if control reg 2 is present.
+ */
+ fas216_writeb(info, REG_CNTL3, 0);
+ fas216_writeb(info, REG_CNTL2, CNTL2_S2FE);
+
+ /*
+ * If we are unable to read back control reg 2
+ * correctly, it is not present, and we have a
+ * NCR53C90.
+ */
+ if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE)
+ return TYPE_NCR53C90;
+
+ /*
+ * Now, check control register 3
+ */
+ fas216_writeb(info, REG_CNTL2, 0);
+ fas216_writeb(info, REG_CNTL3, 0);
+ fas216_writeb(info, REG_CNTL3, 5);
+
+ /*
+ * If we are unable to read the register back
+ * correctly, we have a NCR53C90A
+ */
+ if (fas216_readb(info, REG_CNTL3) != 5)
+ return TYPE_NCR53C90A;
+
+ /*
+ * Now read the ID from the chip.
+ */
+ fas216_writeb(info, REG_CNTL3, 0);
+
+ fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK);
+ fas216_writeb(info, REG_CNTL3, 0);
+
+ fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+ udelay(50);
+ fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP);
+
+ fas216_writeb(info, REG_CNTL2, CNTL2_ENF);
+ fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+ udelay(50);
+ fas216_writeb(info, REG_CMD, CMD_NOP);
+
+ rev = fas216_readb(info, REG_ID);
+ family = rev >> 3;
+ rev &= 7;
+
+ switch (family) {
+ case 0x01:
+ if (rev == 4)
+ return TYPE_Am53CF94;
+ break;
+
+ case 0x02:
+ switch (rev) {
+ case 2:
+ return TYPE_EmFAS216;
+ case 3:
+ return TYPE_QLFAS216;
+ }
+ break;
+
+ default:
+ break;
+ }
+ printk("family %x rev %x\n", family, rev);
+ return TYPE_NCR53C9x;
+}
+
+/**
+ * fas216_reset_state - Initialise driver internal state
+ * @info: state to initialise
+ *
+ * Initialise driver internal state
+ */
+static void fas216_reset_state(FAS216_Info *info)
+{
+ int i;
+
+ fas216_checkmagic(info);
+
+ fas216_bus_reset(info);
+
+ /*
+ * Clear out all stale info in our state structure
+ */
+ memset(info->busyluns, 0, sizeof(info->busyluns));
+ info->scsi.disconnectable = 0;
+ info->scsi.aborting = 0;
+
+ for (i = 0; i < 8; i++) {
+ info->device[i].parity_enabled = 0;
+ info->device[i].parity_check = 1;
+ }
+
+ /*
+ * Drain all commands on disconnected queue
+ */
+ while (queue_remove(&info->queues.disconnected) != NULL);
+
+ /*
+ * Remove executing commands.
+ */
+ info->SCpnt = NULL;
+ info->reqSCpnt = NULL;
+ info->rstSCpnt = NULL;
+ info->origSCpnt = NULL;
+}
+
+/**
+ * fas216_init - initialise FAS/NCR/AMD SCSI structures.
+ * @host: a driver-specific filled-out structure
+ *
+ * Initialise FAS/NCR/AMD SCSI structures.
+ * Returns: 0 on success
+ */
+int fas216_init(struct Scsi_Host *host)
+{
+ FAS216_Info *info = (FAS216_Info *)host->hostdata;
+
+ info->magic_start = MAGIC;
+ info->magic_end = MAGIC;
+ info->host = host;
+ info->scsi.cfg[0] = host->this_id | CNTL1_PERE;
+ info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE;
+ info->scsi.cfg[2] = info->ifcfg.cntl3 |
+ CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM;
+ info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+ info->rst_dev_status = -1;
+ info->rst_bus_status = -1;
+ init_waitqueue_head(&info->eh_wait);
+ init_timer(&info->eh_timer);
+ info->eh_timer.data = (unsigned long)info;
+ info->eh_timer.function = fas216_eh_timer;
+
+ spin_lock_init(&info->host_lock);
+
+ memset(&info->stats, 0, sizeof(info->stats));
+
+ msgqueue_initialise(&info->scsi.msgs);
+
+ if (!queue_initialise(&info->queues.issue))
+ return -ENOMEM;
+
+ if (!queue_initialise(&info->queues.disconnected)) {
+ queue_free(&info->queues.issue);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * fas216_add - initialise FAS/NCR/AMD SCSI ic.
+ * @host: a driver-specific filled-out structure
+ * @dev: parent device
+ *
+ * Initialise FAS/NCR/AMD SCSI ic.
+ * Returns: 0 on success
+ */
+int fas216_add(struct Scsi_Host *host, struct device *dev)
+{
+ FAS216_Info *info = (FAS216_Info *)host->hostdata;
+ int type, ret;
+
+ if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) {
+ printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n",
+ info->ifcfg.clockrate);
+ return -EINVAL;
+ }
+
+ fas216_reset_state(info);
+ type = fas216_detect_type(info);
+ info->scsi.type = chip_types[type];
+
+ udelay(300);
+
+ /*
+ * Initialise the chip correctly.
+ */
+ fas216_init_chip(info);
+
+ /*
+ * Reset the SCSI bus. We don't want to see
+ * the resulting reset interrupt, so mask it
+ * out.
+ */
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR);
+ fas216_writeb(info, REG_CMD, CMD_RESETSCSI);
+
+ /*
+ * scsi standard says wait 250ms
+ */
+ spin_unlock_irq(info->host->host_lock);
+ msleep(100*1000/100);
+ spin_lock_irq(info->host->host_lock);
+
+ fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+ fas216_readb(info, REG_INST);
+
+ fas216_checkmagic(info);
+
+ ret = scsi_add_host(host, dev);
+ if (ret)
+ fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+ else
+ scsi_scan_host(host);
+
+ return ret;
+}
+
+void fas216_remove(struct Scsi_Host *host)
+{
+ FAS216_Info *info = (FAS216_Info *)host->hostdata;
+
+ fas216_checkmagic(info);
+ scsi_remove_host(host);
+
+ fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+ scsi_host_put(host);
+}
+
+/**
+ * fas216_release - release all resources for FAS/NCR/AMD SCSI ic.
+ * @host: a driver-specific filled-out structure
+ *
+ * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic.
+ */
+void fas216_release(struct Scsi_Host *host)
+{
+ FAS216_Info *info = (FAS216_Info *)host->hostdata;
+
+ queue_free(&info->queues.disconnected);
+ queue_free(&info->queues.issue);
+}
+
+int fas216_print_host(FAS216_Info *info, char *buffer)
+{
+ return sprintf(buffer,
+ "\n"
+ "Chip : %s\n"
+ " Address: 0x%p\n"
+ " IRQ : %d\n"
+ " DMA : %d\n",
+ info->scsi.type, info->scsi.io_base,
+ info->scsi.irq, info->scsi.dma);
+}
+
+int fas216_print_stats(FAS216_Info *info, char *buffer)
+{
+ char *p = buffer;
+
+ p += sprintf(p, "\n"
+ "Command Statistics:\n"
+ " Queued : %u\n"
+ " Issued : %u\n"
+ " Completed : %u\n"
+ " Reads : %u\n"
+ " Writes : %u\n"
+ " Others : %u\n"
+ " Disconnects: %u\n"
+ " Aborts : %u\n"
+ " Bus resets : %u\n"
+ " Host resets: %u\n",
+ info->stats.queues, info->stats.removes,
+ info->stats.fins, info->stats.reads,
+ info->stats.writes, info->stats.miscs,
+ info->stats.disconnects, info->stats.aborts,
+ info->stats.bus_resets, info->stats.host_resets);
+
+ return p - buffer;
+}
+
+int fas216_print_devices(FAS216_Info *info, char *buffer)
+{
+ struct fas216_device *dev;
+ Scsi_Device *scd;
+ char *p = buffer;
+
+ p += sprintf(p, "Device/Lun TaggedQ Parity Sync\n");
+
+ shost_for_each_device(scd, info->host) {
+ dev = &info->device[scd->id];
+ p += sprintf(p, " %d/%d ", scd->id, scd->lun);
+ if (scd->tagged_supported)
+ p += sprintf(p, "%3sabled(%3d) ",
+ scd->simple_tags ? "en" : "dis",
+ scd->current_tag);
+ else
+ p += sprintf(p, "unsupported ");
+
+ p += sprintf(p, "%3sabled ", dev->parity_enabled ? "en" : "dis");
+
+ if (dev->sof)
+ p += sprintf(p, "offset %d, %d ns\n",
+ dev->sof, dev->period * 4);
+ else
+ p += sprintf(p, "async\n");
+ }
+
+ return p - buffer;
+}
+
+EXPORT_SYMBOL(fas216_init);
+EXPORT_SYMBOL(fas216_add);
+EXPORT_SYMBOL(fas216_queue_command);
+EXPORT_SYMBOL(fas216_noqueue_command);
+EXPORT_SYMBOL(fas216_intr);
+EXPORT_SYMBOL(fas216_remove);
+EXPORT_SYMBOL(fas216_release);
+EXPORT_SYMBOL(fas216_eh_abort);
+EXPORT_SYMBOL(fas216_eh_device_reset);
+EXPORT_SYMBOL(fas216_eh_bus_reset);
+EXPORT_SYMBOL(fas216_eh_host_reset);
+EXPORT_SYMBOL(fas216_print_host);
+EXPORT_SYMBOL(fas216_print_stats);
+EXPORT_SYMBOL(fas216_print_devices);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h
new file mode 100644
index 000000000000..60a2a120205b
--- /dev/null
+++ b/drivers/scsi/arm/fas216.h
@@ -0,0 +1,394 @@
+/*
+ * linux/drivers/acorn/scsi/fas216.h
+ *
+ * Copyright (C) 1997-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * FAS216 generic driver
+ */
+#ifndef FAS216_H
+#define FAS216_H
+
+#ifndef NO_IRQ
+#define NO_IRQ 255
+#endif
+
+#include "queue.h"
+#include "msgqueue.h"
+
+/* FAS register definitions */
+
+/* transfer count low */
+#define REG_CTCL (0)
+#define REG_STCL (0)
+
+/* transfer count medium */
+#define REG_CTCM (1)
+#define REG_STCM (1)
+
+/* fifo data */
+#define REG_FF (2)
+
+/* command */
+#define REG_CMD (3)
+#define CMD_NOP 0x00
+#define CMD_FLUSHFIFO 0x01
+#define CMD_RESETCHIP 0x02
+#define CMD_RESETSCSI 0x03
+
+#define CMD_TRANSFERINFO 0x10
+#define CMD_INITCMDCOMPLETE 0x11
+#define CMD_MSGACCEPTED 0x12
+#define CMD_PADBYTES 0x18
+#define CMD_SETATN 0x1a
+#define CMD_RSETATN 0x1b
+
+#define CMD_SELECTWOATN 0x41
+#define CMD_SELECTATN 0x42
+#define CMD_SELECTATNSTOP 0x43
+#define CMD_ENABLESEL 0x44
+#define CMD_DISABLESEL 0x45
+#define CMD_SELECTATN3 0x46
+#define CMD_RESEL3 0x47
+
+#define CMD_WITHDMA 0x80
+
+/* status register (read) */
+#define REG_STAT (4)
+#define STAT_IO (1 << 0) /* IO phase */
+#define STAT_CD (1 << 1) /* CD phase */
+#define STAT_MSG (1 << 2) /* MSG phase */
+#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */
+#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */
+#define STAT_PARITYERROR (1 << 5) /* Parity error */
+#define STAT_REALBAD (1 << 6) /* Something bad */
+#define STAT_INT (1 << 7) /* Interrupt */
+
+#define STAT_BUSMASK (STAT_MSG|STAT_CD|STAT_IO)
+#define STAT_DATAOUT (0) /* Data out */
+#define STAT_DATAIN (STAT_IO) /* Data in */
+#define STAT_COMMAND (STAT_CD) /* Command out */
+#define STAT_STATUS (STAT_CD|STAT_IO) /* Status In */
+#define STAT_MESGOUT (STAT_MSG|STAT_CD) /* Message out */
+#define STAT_MESGIN (STAT_MSG|STAT_CD|STAT_IO) /* Message In */
+
+/* bus ID for select / reselect */
+#define REG_SDID (4)
+#define BUSID(target) ((target) & 7)
+
+/* Interrupt status register (read) */
+#define REG_INST (5)
+#define INST_SELWOATN (1 << 0) /* Select w/o ATN */
+#define INST_SELATN (1 << 1) /* Select w/ATN */
+#define INST_RESELECTED (1 << 2) /* Reselected */
+#define INST_FUNCDONE (1 << 3) /* Function done */
+#define INST_BUSSERVICE (1 << 4) /* Bus service */
+#define INST_DISCONNECT (1 << 5) /* Disconnect */
+#define INST_ILLEGALCMD (1 << 6) /* Illegal command */
+#define INST_BUSRESET (1 << 7) /* SCSI Bus reset */
+
+/* Timeout register (write) */
+#define REG_STIM (5)
+
+/* Sequence step register (read) */
+#define REG_IS (6)
+#define IS_BITS 0x07
+#define IS_SELARB 0x00 /* Select & Arb ok */
+#define IS_MSGBYTESENT 0x01 /* One byte message sent*/
+#define IS_NOTCOMMAND 0x02 /* Not in command state */
+#define IS_EARLYPHASE 0x03 /* Early phase change */
+#define IS_COMPLETE 0x04 /* Command ok */
+#define IS_SOF 0x08 /* Sync off flag */
+
+/* Transfer period step (write) */
+#define REG_STP (6)
+
+/* Synchronous Offset (write) */
+#define REG_SOF (7)
+
+/* Fifo state register (read) */
+#define REG_CFIS (7)
+#define CFIS_CF 0x1f /* Num bytes in FIFO */
+#define CFIS_IS 0xe0 /* Step */
+
+/* config register 1 */
+#define REG_CNTL1 (8)
+#define CNTL1_CID (7 << 0) /* Chip ID */
+#define CNTL1_STE (1 << 3) /* Self test enable */
+#define CNTL1_PERE (1 << 4) /* Parity enable reporting en. */
+#define CNTL1_PTE (1 << 5) /* Parity test enable */
+#define CNTL1_DISR (1 << 6) /* Disable Irq on SCSI reset */
+#define CNTL1_ETM (1 << 7) /* Extended Timing Mode */
+
+/* Clock conversion factor (read) */
+#define REG_CLKF (9)
+#define CLKF_F37MHZ 0x00 /* 35.01 - 40 MHz */
+#define CLKF_F10MHZ 0x02 /* 10 MHz */
+#define CLKF_F12MHZ 0x03 /* 10.01 - 15 MHz */
+#define CLKF_F17MHZ 0x04 /* 15.01 - 20 MHz */
+#define CLKF_F22MHZ 0x05 /* 20.01 - 25 MHz */
+#define CLKF_F27MHZ 0x06 /* 25.01 - 30 MHz */
+#define CLKF_F32MHZ 0x07 /* 30.01 - 35 MHz */
+
+/* Chip test register (write) */
+#define REG_FTM (10)
+#define TEST_FTM 0x01 /* Force target mode */
+#define TEST_FIM 0x02 /* Force initiator mode */
+#define TEST_FHI 0x04 /* Force high impedance mode */
+
+/* Configuration register 2 (read/write) */
+#define REG_CNTL2 (11)
+#define CNTL2_PGDP (1 << 0) /* Pass Th/Generate Data Parity */
+#define CNTL2_PGRP (1 << 1) /* Pass Th/Generate Reg Parity */
+#define CNTL2_ACDPE (1 << 2) /* Abort on Cmd/Data Parity Err */
+#define CNTL2_S2FE (1 << 3) /* SCSI2 Features Enable */
+#define CNTL2_TSDR (1 << 4) /* Tristate DREQ */
+#define CNTL2_SBO (1 << 5) /* Select Byte Order */
+#define CNTL2_ENF (1 << 6) /* Enable features */
+#define CNTL2_DAE (1 << 7) /* Data Alignment Enable */
+
+/* Configuration register 3 (read/write) */
+#define REG_CNTL3 (12)
+#define CNTL3_BS8 (1 << 0) /* Burst size 8 */
+#define CNTL3_MDM (1 << 1) /* Modify DMA mode */
+#define CNTL3_LBTM (1 << 2) /* Last Byte Transfer mode */
+#define CNTL3_FASTCLK (1 << 3) /* Fast SCSI clocking */
+#define CNTL3_FASTSCSI (1 << 4) /* Fast SCSI */
+#define CNTL3_G2CB (1 << 5) /* Group2 SCSI support */
+#define CNTL3_QTAG (1 << 6) /* Enable 3 byte msgs */
+#define CNTL3_ADIDCHK (1 << 7) /* Additional ID check */
+
+/* High transfer count (read/write) */
+#define REG_CTCH (14)
+#define REG_STCH (14)
+
+/* ID register (read only) */
+#define REG_ID (14)
+
+/* Data alignment */
+#define REG_DAL (15)
+
+typedef enum {
+ PHASE_IDLE, /* we're not planning on doing anything */
+ PHASE_SELECTION, /* selecting a device */
+ PHASE_SELSTEPS, /* selection with command steps */
+ PHASE_COMMAND, /* command sent */
+ PHASE_MESSAGESENT, /* selected, and we're sending cmd */
+ PHASE_DATAOUT, /* data out to device */
+ PHASE_DATAIN, /* data in from device */
+ PHASE_MSGIN, /* message in from device */
+ PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */
+ PHASE_MSGOUT, /* after message out phase */
+ PHASE_MSGOUT_EXPECT, /* expecting message out */
+ PHASE_STATUS, /* status from device */
+ PHASE_DONE /* Command complete */
+} phase_t;
+
+typedef enum {
+ DMA_OUT, /* DMA from memory to chip */
+ DMA_IN /* DMA from chip to memory */
+} fasdmadir_t;
+
+typedef enum {
+ fasdma_none, /* No dma */
+ fasdma_pio, /* PIO mode */
+ fasdma_pseudo, /* Pseudo DMA */
+ fasdma_real_block, /* Real DMA, on block by block basis */
+ fasdma_real_all /* Real DMA, on request by request */
+} fasdmatype_t;
+
+typedef enum {
+ neg_wait, /* Negociate with device */
+ neg_inprogress, /* Negociation sent */
+ neg_complete, /* Negociation complete */
+ neg_targcomplete, /* Target completed negociation */
+ neg_invalid /* Negociation not supported */
+} neg_t;
+
+#define MAGIC 0x441296bdUL
+#define NR_MSGS 8
+
+#define FASCAP_DMA (1 << 0)
+#define FASCAP_PSEUDODMA (1 << 1)
+
+typedef struct {
+ unsigned long magic_start;
+ spinlock_t host_lock;
+ struct Scsi_Host *host; /* host */
+ Scsi_Cmnd *SCpnt; /* currently processing command */
+ Scsi_Cmnd *origSCpnt; /* original connecting command */
+ Scsi_Cmnd *reqSCpnt; /* request sense command */
+ Scsi_Cmnd *rstSCpnt; /* reset command */
+ Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */
+ int next_pending; /* next pending device */
+
+ /*
+ * Error recovery
+ */
+ wait_queue_head_t eh_wait;
+ struct timer_list eh_timer;
+ unsigned int rst_dev_status;
+ unsigned int rst_bus_status;
+
+ /* driver information */
+ struct {
+ phase_t phase; /* current phase */
+ void __iomem *io_base; /* iomem base of FAS216 */
+ unsigned int io_shift; /* shift to adjust reg offsets by */
+ unsigned char cfg[4]; /* configuration registers */
+ const char *type; /* chip type */
+ unsigned int irq; /* interrupt */
+ int dma; /* dma channel */
+
+ Scsi_Pointer SCp; /* current commands data pointer */
+
+ MsgQueue_t msgs; /* message queue for connected device */
+
+ unsigned int async_stp; /* Async transfer STP value */
+ unsigned char msgin_fifo; /* bytes in fifo at time of message in */
+ unsigned char message[256]; /* last message received from device */
+
+ unsigned char disconnectable:1; /* this command can be disconnected */
+ unsigned char aborting:1; /* aborting command */
+ } scsi;
+
+ /* statistics information */
+ struct {
+ unsigned int queues;
+ unsigned int removes;
+ unsigned int fins;
+ unsigned int reads;
+ unsigned int writes;
+ unsigned int miscs;
+ unsigned int disconnects;
+ unsigned int aborts;
+ unsigned int bus_resets;
+ unsigned int host_resets;
+ } stats;
+
+ /* configuration information */
+ struct {
+ unsigned char clockrate; /* clock rate of FAS device (MHz) */
+ unsigned char select_timeout; /* timeout (R5) */
+ unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */
+ unsigned char wide_max_size; /* Maximum wide transfer size */
+ unsigned char cntl3; /* Control Reg 3 */
+ unsigned int asyncperiod; /* Async transfer period (ns) */
+ unsigned int capabilities; /* driver capabilities */
+ unsigned int disconnect_ok:1; /* Disconnects allowed? */
+ } ifcfg;
+
+ /* queue handling */
+ struct {
+ Queue_t issue; /* issue queue */
+ Queue_t disconnected; /* disconnected command queue */
+ } queues;
+
+ /* per-device info */
+ struct fas216_device {
+ unsigned char disconnect_ok:1; /* device can disconnect */
+ unsigned char parity_enabled:1; /* parity checking enabled */
+ unsigned char parity_check:1; /* need to check parity checking */
+ unsigned char period; /* sync xfer period in (*4ns) */
+ unsigned char stp; /* synchronous transfer period */
+ unsigned char sof; /* synchronous offset register */
+ unsigned char wide_xfer; /* currently negociated wide transfer */
+ neg_t sync_state; /* synchronous transfer mode */
+ neg_t wide_state; /* wide transfer mode */
+ } device[8];
+ unsigned long busyluns[64/sizeof(unsigned long)];/* array of bits indicating LUNs busy */
+
+ /* dma */
+ struct {
+ fasdmatype_t transfer_type; /* current type of DMA transfer */
+ fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma);
+ void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer);
+ void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp);
+ } dma;
+
+ /* miscellaneous */
+ int internal_done; /* flag to indicate request done */
+ unsigned long magic_end;
+} FAS216_Info;
+
+/* Function: int fas216_init (struct Scsi_Host *instance)
+ * Purpose : initialise FAS/NCR/AMD SCSI structures.
+ * Params : instance - a driver-specific filled-out structure
+ * Returns : 0 on success
+ */
+extern int fas216_init (struct Scsi_Host *instance);
+
+/* Function: int fas216_add (struct Scsi_Host *instance, struct device *dev)
+ * Purpose : initialise FAS/NCR/AMD SCSI ic.
+ * Params : instance - a driver-specific filled-out structure
+ * Returns : 0 on success
+ */
+extern int fas216_add (struct Scsi_Host *instance, struct device *dev);
+
+/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ * Purpose : queue a command for adapter to process.
+ * Params : SCpnt - Command to queue
+ * done - done function to call once command is complete
+ * Returns : 0 - success, else error
+ */
+extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+/* Function: int fas216_noqueue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ * Purpose : queue a command for adapter to process, and process it to completion.
+ * Params : SCpnt - Command to queue
+ * done - done function to call once command is complete
+ * Returns : 0 - success, else error
+ */
+extern int fas216_noqueue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+/* Function: irqreturn_t fas216_intr (FAS216_Info *info)
+ * Purpose : handle interrupts from the interface to progress a command
+ * Params : info - interface to service
+ */
+extern irqreturn_t fas216_intr (FAS216_Info *info);
+
+extern void fas216_remove (struct Scsi_Host *instance);
+
+/* Function: void fas216_release (struct Scsi_Host *instance)
+ * Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic.
+ * Params : instance - a driver-specific filled-out structure
+ * Returns : 0 on success
+ */
+extern void fas216_release (struct Scsi_Host *instance);
+
+extern int fas216_print_host(FAS216_Info *info, char *buffer);
+extern int fas216_print_stats(FAS216_Info *info, char *buffer);
+extern int fas216_print_devices(FAS216_Info *info, char *buffer);
+
+/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt)
+ * Purpose : abort this command
+ * Params : SCpnt - command to abort
+ * Returns : FAILED if unable to abort
+ */
+extern int fas216_eh_abort(Scsi_Cmnd *SCpnt);
+
+/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt)
+ * Purpose : Reset the device associated with this command
+ * Params : SCpnt - command specifing device to reset
+ * Returns : FAILED if unable to reset
+ */
+extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt);
+
+/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt)
+ * Purpose : Reset the complete bus associated with this command
+ * Params : SCpnt - command specifing bus to reset
+ * Returns : FAILED if unable to reset
+ */
+extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt);
+
+/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt)
+ * Purpose : Reset the host associated with this command
+ * Params : SCpnt - command specifing host to reset
+ * Returns : FAILED if unable to reset
+ */
+extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt);
+
+#endif /* FAS216_H */
diff --git a/drivers/scsi/arm/msgqueue.c b/drivers/scsi/arm/msgqueue.c
new file mode 100644
index 000000000000..7c95c7582b29
--- /dev/null
+++ b/drivers/scsi/arm/msgqueue.c
@@ -0,0 +1,171 @@
+/*
+ * linux/drivers/acorn/scsi/msgqueue.c
+ *
+ * Copyright (C) 1997-1998 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * message queue handling
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+
+#include "msgqueue.h"
+
+/*
+ * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq)
+ * Purpose : Allocate a message queue entry
+ * Params : msgq - message queue to claim entry for
+ * Returns : message queue entry or NULL.
+ */
+static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq)
+{
+ struct msgqueue_entry *mq;
+
+ if ((mq = msgq->free) != NULL)
+ msgq->free = mq->next;
+
+ return mq;
+}
+
+/*
+ * Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq)
+ * Purpose : free a message queue entry
+ * Params : msgq - message queue to free entry from
+ * mq - message queue entry to free
+ */
+static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq)
+{
+ if (mq) {
+ mq->next = msgq->free;
+ msgq->free = mq;
+ }
+}
+
+/*
+ * Function: void msgqueue_initialise(MsgQueue_t *msgq)
+ * Purpose : initialise a message queue
+ * Params : msgq - queue to initialise
+ */
+void msgqueue_initialise(MsgQueue_t *msgq)
+{
+ int i;
+
+ msgq->qe = NULL;
+ msgq->free = &msgq->entries[0];
+
+ for (i = 0; i < NR_MESSAGES; i++)
+ msgq->entries[i].next = &msgq->entries[i + 1];
+
+ msgq->entries[NR_MESSAGES - 1].next = NULL;
+}
+
+
+/*
+ * Function: void msgqueue_free(MsgQueue_t *msgq)
+ * Purpose : free a queue
+ * Params : msgq - queue to free
+ */
+void msgqueue_free(MsgQueue_t *msgq)
+{
+}
+
+/*
+ * Function: int msgqueue_msglength(MsgQueue_t *msgq)
+ * Purpose : calculate the total length of all messages on the message queue
+ * Params : msgq - queue to examine
+ * Returns : number of bytes of messages in queue
+ */
+int msgqueue_msglength(MsgQueue_t *msgq)
+{
+ struct msgqueue_entry *mq = msgq->qe;
+ int length = 0;
+
+ for (mq = msgq->qe; mq; mq = mq->next)
+ length += mq->msg.length;
+
+ return length;
+}
+
+/*
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
+ * Purpose : return a message
+ * Params : msgq - queue to obtain message from
+ * : msgno - message number
+ * Returns : pointer to message string, or NULL
+ */
+struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
+{
+ struct msgqueue_entry *mq;
+
+ for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--);
+
+ return mq ? &mq->msg : NULL;
+}
+
+/*
+ * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
+ * Purpose : add a message onto a message queue
+ * Params : msgq - queue to add message on
+ * length - length of message
+ * ... - message bytes
+ * Returns : != 0 if successful
+ */
+int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
+{
+ struct msgqueue_entry *mq = mqe_alloc(msgq);
+ va_list ap;
+
+ if (mq) {
+ struct msgqueue_entry **mqp;
+ int i;
+
+ va_start(ap, length);
+ for (i = 0; i < length; i++)
+ mq->msg.msg[i] = va_arg(ap, unsigned int);
+ va_end(ap);
+
+ mq->msg.length = length;
+ mq->msg.fifo = 0;
+ mq->next = NULL;
+
+ mqp = &msgq->qe;
+ while (*mqp)
+ mqp = &(*mqp)->next;
+
+ *mqp = mq;
+ }
+
+ return mq != NULL;
+}
+
+/*
+ * Function: void msgqueue_flush(MsgQueue_t *msgq)
+ * Purpose : flush all messages from message queue
+ * Params : msgq - queue to flush
+ */
+void msgqueue_flush(MsgQueue_t *msgq)
+{
+ struct msgqueue_entry *mq, *mqnext;
+
+ for (mq = msgq->qe; mq; mq = mqnext) {
+ mqnext = mq->next;
+ mqe_free(msgq, mq);
+ }
+ msgq->qe = NULL;
+}
+
+EXPORT_SYMBOL(msgqueue_initialise);
+EXPORT_SYMBOL(msgqueue_free);
+EXPORT_SYMBOL(msgqueue_msglength);
+EXPORT_SYMBOL(msgqueue_getmsg);
+EXPORT_SYMBOL(msgqueue_addmsg);
+EXPORT_SYMBOL(msgqueue_flush);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SCSI message queue handling");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/msgqueue.h b/drivers/scsi/arm/msgqueue.h
new file mode 100644
index 000000000000..41c7333df3e3
--- /dev/null
+++ b/drivers/scsi/arm/msgqueue.h
@@ -0,0 +1,82 @@
+/*
+ * linux/drivers/acorn/scsi/msgqueue.h
+ *
+ * Copyright (C) 1997 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * message queue handling
+ */
+#ifndef MSGQUEUE_H
+#define MSGQUEUE_H
+
+struct message {
+ char msg[8];
+ int length;
+ int fifo;
+};
+
+struct msgqueue_entry {
+ struct message msg;
+ struct msgqueue_entry *next;
+};
+
+#define NR_MESSAGES 4
+
+typedef struct {
+ struct msgqueue_entry *qe;
+ struct msgqueue_entry *free;
+ struct msgqueue_entry entries[NR_MESSAGES];
+} MsgQueue_t;
+
+/*
+ * Function: void msgqueue_initialise(MsgQueue_t *msgq)
+ * Purpose : initialise a message queue
+ * Params : msgq - queue to initialise
+ */
+extern void msgqueue_initialise(MsgQueue_t *msgq);
+
+/*
+ * Function: void msgqueue_free(MsgQueue_t *msgq)
+ * Purpose : free a queue
+ * Params : msgq - queue to free
+ */
+extern void msgqueue_free(MsgQueue_t *msgq);
+
+/*
+ * Function: int msgqueue_msglength(MsgQueue_t *msgq)
+ * Purpose : calculate the total length of all messages on the message queue
+ * Params : msgq - queue to examine
+ * Returns : number of bytes of messages in queue
+ */
+extern int msgqueue_msglength(MsgQueue_t *msgq);
+
+/*
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
+ * Purpose : return a message & its length
+ * Params : msgq - queue to obtain message from
+ * : msgno - message number
+ * Returns : pointer to message string, or NULL
+ */
+extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno);
+
+/*
+ * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
+ * Purpose : add a message onto a message queue
+ * Params : msgq - queue to add message on
+ * length - length of message
+ * ... - message bytes
+ * Returns : != 0 if successful
+ */
+extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...);
+
+/*
+ * Function: void msgqueue_flush(MsgQueue_t *msgq)
+ * Purpose : flush all messages from message queue
+ * Params : msgq - queue to flush
+ */
+extern void msgqueue_flush(MsgQueue_t *msgq);
+
+#endif
diff --git a/drivers/scsi/arm/oak.c b/drivers/scsi/arm/oak.c
new file mode 100644
index 000000000000..ff2554f4cb80
--- /dev/null
+++ b/drivers/scsi/arm/oak.c
@@ -0,0 +1,217 @@
+/*
+ * Oak Generic NCR5380 driver
+ *
+ * Copyright 1995-2002, Russell King
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+
+#define AUTOSENSE
+/*#define PSEUDO_DMA*/
+
+#define OAKSCSI_PUBLIC_RELEASE 1
+
+#define NCR5380_read(reg) oakscsi_read(_instance, reg)
+#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value)
+#define NCR5380_intr oakscsi_intr
+#define NCR5380_queue_command oakscsi_queue_command
+#define NCR5380_proc_info oakscsi_proc_info
+
+#define NCR5380_implementation_fields int port, ctrl
+#define NCR5380_local_declare() struct Scsi_Host *_instance
+#define NCR5380_setup(instance) _instance = instance
+
+#define BOARD_NORMAL 0
+#define BOARD_NCR53C400 1
+
+#include "../NCR5380.h"
+
+#undef START_DMA_INITIATOR_RECEIVE_REG
+#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128)
+
+const char * oakscsi_info (struct Scsi_Host *spnt)
+{
+ return "";
+}
+
+#define STAT(p) inw(p + 144)
+extern void inswb(int from, void *to, int len);
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+ int len)
+{
+ int iobase = instance->io_port;
+printk("writing %p len %d\n",addr, len);
+ if(!len) return -1;
+
+ while(1)
+ {
+ int status;
+ while(((status = STAT(iobase)) & 0x100)==0);
+ }
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+ int len)
+{
+ int iobase = instance->io_port;
+printk("reading %p len %d\n", addr, len);
+ while(len > 0)
+ {
+ int status, timeout;
+ unsigned long b;
+
+ timeout = 0x01FFFFFF;
+
+ while(((status = STAT(iobase)) & 0x100)==0)
+ {
+ timeout--;
+ if(status & 0x200 || !timeout)
+ {
+ printk("status = %08X\n",status);
+ return 1;
+ }
+ }
+ if(len >= 128)
+ {
+ inswb(iobase + 136, addr, 128);
+ addr += 128;
+ len -= 128;
+ }
+ else
+ {
+ b = (unsigned long) inw(iobase + 136);
+ *addr ++ = b;
+ len -= 1;
+ if(len)
+ *addr ++ = b>>8;
+ len -= 1;
+ }
+ }
+ return 0;
+}
+
+#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg)))
+#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg)))
+
+#undef STAT
+
+#include "../NCR5380.c"
+
+static Scsi_Host_Template oakscsi_template = {
+ .module = THIS_MODULE,
+ .proc_info = oakscsi_proc_info,
+ .name = "Oak 16-bit SCSI",
+ .info = oakscsi_info,
+ .queuecommand = oakscsi_queue_command,
+ .eh_abort_handler = NCR5380_abort,
+ .eh_device_reset_handler= NCR5380_device_reset,
+ .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "oakscsi",
+};
+
+static int __devinit
+oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ int ret = -ENOMEM;
+
+ host = scsi_host_alloc(&oakscsi_template, sizeof(struct NCR5380_hostdata));
+ if (!host)
+ goto out;
+
+ host->io_port = ecard_address(ec, ECARD_MEMC, 0);
+ host->irq = IRQ_NONE;
+ host->n_io_port = 255;
+
+ ret = -EBUSY;
+ if (!request_region (host->io_port, host->n_io_port, "Oak SCSI"))
+ goto unreg;
+
+ NCR5380_init(host, 0);
+
+ printk("scsi%d: at port 0x%08lx irqs disabled",
+ host->host_no, host->io_port);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE);
+ printk("\nscsi%d:", host->host_no);
+ NCR5380_print_options(host);
+ printk("\n");
+
+ ret = scsi_add_host(host, &ec->dev);
+ if (ret)
+ goto out_release;
+
+ scsi_scan_host(host);
+ goto out;
+
+ out_release:
+ release_region(host->io_port, host->n_io_port);
+ unreg:
+ scsi_host_put(host);
+ out:
+ return ret;
+}
+
+static void __devexit oakscsi_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+
+ ecard_set_drvdata(ec, NULL);
+ scsi_remove_host(host);
+
+ NCR5380_exit(host);
+ release_region(host->io_port, host->n_io_port);
+ scsi_host_put(host);
+}
+
+static const struct ecard_id oakscsi_cids[] = {
+ { MANU_OAK, PROD_OAK_SCSI },
+ { 0xffff, 0xffff }
+};
+
+static struct ecard_driver oakscsi_driver = {
+ .probe = oakscsi_probe,
+ .remove = __devexit_p(oakscsi_remove),
+ .id_table = oakscsi_cids,
+ .drv = {
+ .name = "oakscsi",
+ },
+};
+
+static int __init oakscsi_init(void)
+{
+ return ecard_register_driver(&oakscsi_driver);
+}
+
+static void __exit oakscsi_exit(void)
+{
+ ecard_remove_driver(&oakscsi_driver);
+}
+
+module_init(oakscsi_init);
+module_exit(oakscsi_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Oak SCSI driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/arm/powertec.c b/drivers/scsi/arm/powertec.c
new file mode 100644
index 000000000000..54f23be6460f
--- /dev/null
+++ b/drivers/scsi/arm/powertec.c
@@ -0,0 +1,472 @@
+/*
+ * linux/drivers/acorn/scsi/powertec.c
+ *
+ * Copyright (C) 1997-2005 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+
+#include "../scsi.h"
+#include <scsi/scsi_host.h>
+#include "fas216.h"
+#include "scsi.h"
+
+#include <scsi/scsicam.h>
+
+#define POWERTEC_FAS216_OFFSET 0x3000
+#define POWERTEC_FAS216_SHIFT 6
+
+#define POWERTEC_INTR_STATUS 0x2000
+#define POWERTEC_INTR_BIT 0x80
+
+#define POWERTEC_RESET_CONTROL 0x1018
+#define POWERTEC_RESET_BIT 1
+
+#define POWERTEC_TERM_CONTROL 0x2018
+#define POWERTEC_TERM_ENABLE 1
+
+#define POWERTEC_INTR_CONTROL 0x101c
+#define POWERTEC_INTR_ENABLE 1
+#define POWERTEC_INTR_DISABLE 0
+
+#define VERSION "1.10 (19/01/2003 2.5.59)"
+
+/*
+ * Use term=0,1,0,0,0 to turn terminators on/off.
+ * One entry per slot.
+ */
+static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+
+#define NR_SG 256
+
+struct powertec_info {
+ FAS216_Info info;
+ struct expansion_card *ec;
+ void __iomem *base;
+ unsigned int term_ctl;
+ struct scatterlist sg[NR_SG];
+};
+
+/* Prototype: void powertecscsi_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on Powertec SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+powertecscsi_irqenable(struct expansion_card *ec, int irqnr)
+{
+ struct powertec_info *info = ec->irq_data;
+ writeb(POWERTEC_INTR_ENABLE, info->base + POWERTEC_INTR_CONTROL);
+}
+
+/* Prototype: void powertecscsi_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on Powertec SCSI card
+ * Params : ec - expansion card structure
+ * : irqnr - interrupt number
+ */
+static void
+powertecscsi_irqdisable(struct expansion_card *ec, int irqnr)
+{
+ struct powertec_info *info = ec->irq_data;
+ writeb(POWERTEC_INTR_DISABLE, info->base + POWERTEC_INTR_CONTROL);
+}
+
+static const expansioncard_ops_t powertecscsi_ops = {
+ .irqenable = powertecscsi_irqenable,
+ .irqdisable = powertecscsi_irqdisable,
+};
+
+/* Prototype: void powertecscsi_terminator_ctl(host, on_off)
+ * Purpose : Turn the Powertec SCSI terminators on or off
+ * Params : host - card to turn on/off
+ * : on_off - !0 to turn on, 0 to turn off
+ */
+static void
+powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off)
+{
+ struct powertec_info *info = (struct powertec_info *)host->hostdata;
+
+ info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0;
+ writeb(info->term_ctl, info->base + POWERTEC_TERM_CONTROL);
+}
+
+/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs)
+ * Purpose : handle interrupts from Powertec SCSI card
+ * Params : irq - interrupt number
+ * dev_id - user-defined (Scsi_Host structure)
+ * regs - processor registers at interrupt
+ */
+static irqreturn_t
+powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct powertec_info *info = dev_id;
+
+ return fas216_intr(&info->info);
+}
+
+/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : type of transfer to be performed
+ */
+static fasdmatype_t
+powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ struct powertec_info *info = (struct powertec_info *)host->hostdata;
+ struct device *dev = scsi_get_device(host);
+ int dmach = info->info.scsi.dma;
+
+ if (info->info.ifcfg.capabilities & FASCAP_DMA &&
+ min_type == fasdma_real_all) {
+ int bufs, map_dir, dma_dir;
+
+ bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG);
+
+ if (direction == DMA_OUT)
+ map_dir = DMA_TO_DEVICE,
+ dma_dir = DMA_MODE_WRITE;
+ else
+ map_dir = DMA_FROM_DEVICE,
+ dma_dir = DMA_MODE_READ;
+
+ dma_map_sg(dev, info->sg, bufs + 1, map_dir);
+
+ disable_dma(dmach);
+ set_dma_sg(dmach, info->sg, bufs + 1);
+ set_dma_mode(dmach, dma_dir);
+ enable_dma(dmach);
+ return fasdma_real_all;
+ }
+
+ /*
+ * If we're not doing DMA,
+ * we'll do slow PIO
+ */
+ return fasdma_pio;
+}
+
+/* Prototype: int powertecscsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ */
+static void
+powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+ struct powertec_info *info = (struct powertec_info *)host->hostdata;
+ if (info->info.scsi.dma != NO_DMA)
+ disable_dma(info->info.scsi.dma);
+}
+
+/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *powertecscsi_info(struct Scsi_Host *host)
+{
+ struct powertec_info *info = (struct powertec_info *)host->hostdata;
+ static char string[150];
+
+ sprintf(string, "%s (%s) in slot %d v%s terminators o%s",
+ host->hostt->name, info->info.scsi.type, info->ec->slot_no,
+ VERSION, info->term_ctl ? "n" : "ff");
+
+ return string;
+}
+
+/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+ * Purpose : Set a driver specific function
+ * Params : host - host to setup
+ * : buffer - buffer containing string describing operation
+ * : length - length of string
+ * Returns : -EINVAL, or 0
+ */
+static int
+powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length)
+{
+ int ret = length;
+
+ if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) {
+ buffer += 12;
+ length -= 12;
+
+ if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {
+ if (buffer[5] == '1')
+ powertecscsi_terminator_ctl(host, 1);
+ else if (buffer[5] == '0')
+ powertecscsi_terminator_ctl(host, 0);
+ else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
+ * int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int powertecscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ struct powertec_info *info;
+ char *p = buffer;
+ int pos;
+
+ if (inout == 1)
+ return powertecscsi_set_proc_info(host, buffer, length);
+
+ info = (struct powertec_info *)host->hostdata;
+
+ p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION);
+ p += fas216_print_host(&info->info, p);
+ p += sprintf(p, "Term : o%s\n",
+ info->term_ctl ? "n" : "ff");
+
+ p += fas216_print_stats(&info->info, p);
+ p += fas216_print_devices(&info->info, p);
+
+ *start = buffer + offset;
+ pos = p - buffer - offset;
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+static ssize_t powertecscsi_show_term(struct device *dev, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct powertec_info *info = (struct powertec_info *)host->hostdata;
+
+ return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0);
+}
+
+static ssize_t
+powertecscsi_store_term(struct device *dev, const char *buf, size_t len)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+
+ if (len > 1)
+ powertecscsi_terminator_ctl(host, buf[0] != '0');
+
+ return len;
+}
+
+static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR,
+ powertecscsi_show_term, powertecscsi_store_term);
+
+static Scsi_Host_Template powertecscsi_template = {
+ .module = THIS_MODULE,
+ .proc_info = powertecscsi_proc_info,
+ .name = "PowerTec SCSI",
+ .info = powertecscsi_info,
+ .queuecommand = fas216_queue_command,
+ .eh_host_reset_handler = fas216_eh_host_reset,
+ .eh_bus_reset_handler = fas216_eh_bus_reset,
+ .eh_device_reset_handler = fas216_eh_device_reset,
+ .eh_abort_handler = fas216_eh_abort,
+
+ .can_queue = 8,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .use_clustering = ENABLE_CLUSTERING,
+ .proc_name = "powertec",
+};
+
+static int __devinit
+powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct Scsi_Host *host;
+ struct powertec_info *info;
+ unsigned long resbase, reslen;
+ void __iomem *base;
+ int ret;
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST);
+ reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST);
+ base = ioremap(resbase, reslen);
+ if (!base) {
+ ret = -ENOMEM;
+ goto out_region;
+ }
+
+ host = scsi_host_alloc(&powertecscsi_template,
+ sizeof (struct powertec_info));
+ if (!host) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ ecard_set_drvdata(ec, host);
+
+ info = (struct powertec_info *)host->hostdata;
+ info->base = base;
+ powertecscsi_terminator_ctl(host, term[ec->slot_no]);
+
+ info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET;
+ info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT;
+ info->info.scsi.irq = ec->irq;
+ info->info.scsi.dma = ec->dma;
+ info->info.ifcfg.clockrate = 40; /* MHz */
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = 200; /* ns */
+ info->info.ifcfg.sync_max_depth = 7;
+ info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 1;
+ info->info.ifcfg.wide_max_size = 0;
+ info->info.ifcfg.capabilities = 0;
+ info->info.dma.setup = powertecscsi_dma_setup;
+ info->info.dma.pseudo = NULL;
+ info->info.dma.stop = powertecscsi_dma_stop;
+
+ ec->irqaddr = base + POWERTEC_INTR_STATUS;
+ ec->irqmask = POWERTEC_INTR_BIT;
+ ec->irq_data = info;
+ ec->ops = &powertecscsi_ops;
+
+ device_create_file(&ec->dev, &dev_attr_bus_term);
+
+ ret = fas216_init(host);
+ if (ret)
+ goto out_free;
+
+ ret = request_irq(ec->irq, powertecscsi_intr,
+ SA_INTERRUPT, "powertec", info);
+ if (ret) {
+ printk("scsi%d: IRQ%d not free: %d\n",
+ host->host_no, ec->irq, ret);
+ goto out_release;
+ }
+
+ if (info->info.scsi.dma != NO_DMA) {
+ if (request_dma(info->info.scsi.dma, "powertec")) {
+ printk("scsi%d: DMA%d not free, using PIO\n",
+ host->host_no, info->info.scsi.dma);
+ info->info.scsi.dma = NO_DMA;
+ } else {
+ set_dma_speed(info->info.scsi.dma, 180);
+ info->info.ifcfg.capabilities |= FASCAP_DMA;
+ }
+ }
+
+ ret = fas216_add(host, &ec->dev);
+ if (ret == 0)
+ goto out;
+
+ if (info->info.scsi.dma != NO_DMA)
+ free_dma(info->info.scsi.dma);
+ free_irq(ec->irq, host);
+
+ out_release:
+ fas216_release(host);
+
+ out_free:
+ device_remove_file(&ec->dev, &dev_attr_bus_term);
+ scsi_host_put(host);
+
+ out_unmap:
+ iounmap(base);
+
+ out_region:
+ ecard_release_resources(ec);
+
+ out:
+ return ret;
+}
+
+static void __devexit powertecscsi_remove(struct expansion_card *ec)
+{
+ struct Scsi_Host *host = ecard_get_drvdata(ec);
+ struct powertec_info *info = (struct powertec_info *)host->hostdata;
+
+ ecard_set_drvdata(ec, NULL);
+ fas216_remove(host);
+
+ device_remove_file(&ec->dev, &dev_attr_bus_term);
+
+ if (info->info.scsi.dma != NO_DMA)
+ free_dma(info->info.scsi.dma);
+ free_irq(ec->irq, info);
+
+ iounmap(info->base);
+
+ fas216_release(host);
+ scsi_host_put(host);
+ ecard_release_resources(ec);
+}
+
+static const struct ecard_id powertecscsi_cids[] = {
+ { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI },
+ { 0xffff, 0xffff },
+};
+
+static struct ecard_driver powertecscsi_driver = {
+ .probe = powertecscsi_probe,
+ .remove = __devexit_p(powertecscsi_remove),
+ .id_table = powertecscsi_cids,
+ .drv = {
+ .name = "powertecscsi",
+ },
+};
+
+static int __init powertecscsi_init(void)
+{
+ return ecard_register_driver(&powertecscsi_driver);
+}
+
+static void __exit powertecscsi_exit(void)
+{
+ ecard_remove_driver(&powertecscsi_driver);
+}
+
+module_init(powertecscsi_init);
+module_exit(powertecscsi_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Powertec SCSI driver");
+MODULE_PARM(term, "1-8i");
+MODULE_PARM_DESC(term, "SCSI bus termination");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/queue.c b/drivers/scsi/arm/queue.c
new file mode 100644
index 000000000000..e6d159270d29
--- /dev/null
+++ b/drivers/scsi/arm/queue.c
@@ -0,0 +1,319 @@
+/*
+ * linux/drivers/acorn/scsi/queue.c: queue handling primitives
+ *
+ * Copyright (C) 1997-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Changelog:
+ * 15-Sep-1997 RMK Created.
+ * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude
+ * not updating internal linked list properly
+ * (was causing commands to go missing).
+ * 30-Aug-2000 RMK Use Linux list handling and spinlocks
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/init.h>
+
+#include "../scsi.h"
+
+#define DEBUG
+
+typedef struct queue_entry {
+ struct list_head list;
+ Scsi_Cmnd *SCpnt;
+#ifdef DEBUG
+ unsigned long magic;
+#endif
+} QE_t;
+
+#ifdef DEBUG
+#define QUEUE_MAGIC_FREE 0xf7e1c9a3
+#define QUEUE_MAGIC_USED 0xf7e1cc33
+
+#define SET_MAGIC(q,m) ((q)->magic = (m))
+#define BAD_MAGIC(q,m) ((q)->magic != (m))
+#else
+#define SET_MAGIC(q,m) do { } while (0)
+#define BAD_MAGIC(q,m) (0)
+#endif
+
+#include "queue.h"
+
+#define NR_QE 32
+
+/*
+ * Function: void queue_initialise (Queue_t *queue)
+ * Purpose : initialise a queue
+ * Params : queue - queue to initialise
+ */
+int queue_initialise (Queue_t *queue)
+{
+ unsigned int nqueues = NR_QE;
+ QE_t *q;
+
+ spin_lock_init(&queue->queue_lock);
+ INIT_LIST_HEAD(&queue->head);
+ INIT_LIST_HEAD(&queue->free);
+
+ /*
+ * If life was easier, then SCpnt would have a
+ * host-available list head, and we wouldn't
+ * need to keep free lists or allocate this
+ * memory.
+ */
+ queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL);
+ if (q) {
+ for (; nqueues; q++, nqueues--) {
+ SET_MAGIC(q, QUEUE_MAGIC_FREE);
+ q->SCpnt = NULL;
+ list_add(&q->list, &queue->free);
+ }
+ }
+
+ return queue->alloc != NULL;
+}
+
+/*
+ * Function: void queue_free (Queue_t *queue)
+ * Purpose : free a queue
+ * Params : queue - queue to free
+ */
+void queue_free (Queue_t *queue)
+{
+ if (!list_empty(&queue->head))
+ printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
+ if (queue->alloc)
+ kfree(queue->alloc);
+}
+
+
+/*
+ * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
+ * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
+ * Params : queue - destination queue
+ * SCpnt - command to add
+ * head - add command to head of queue
+ * Returns : 0 on error, !0 on success
+ */
+int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
+{
+ unsigned long flags;
+ struct list_head *l;
+ QE_t *q;
+ int ret = 0;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ if (list_empty(&queue->free))
+ goto empty;
+
+ l = queue->free.next;
+ list_del(l);
+
+ q = list_entry(l, QE_t, list);
+ if (BAD_MAGIC(q, QUEUE_MAGIC_FREE))
+ BUG();
+
+ SET_MAGIC(q, QUEUE_MAGIC_USED);
+ q->SCpnt = SCpnt;
+
+ if (head)
+ list_add(l, &queue->head);
+ else
+ list_add_tail(l, &queue->head);
+
+ ret = 1;
+empty:
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+ return ret;
+}
+
+static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
+{
+ QE_t *q;
+
+ /*
+ * Move the entry from the "used" list onto the "free" list
+ */
+ list_del(ent);
+ q = list_entry(ent, QE_t, list);
+ if (BAD_MAGIC(q, QUEUE_MAGIC_USED))
+ BUG();
+
+ SET_MAGIC(q, QUEUE_MAGIC_FREE);
+ list_add(ent, &queue->free);
+
+ return q->SCpnt;
+}
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude)
+ * Purpose : remove a SCSI command from a queue
+ * Params : queue - queue to remove command from
+ * exclude - bit array of target&lun which is busy
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
+{
+ unsigned long flags;
+ struct list_head *l;
+ Scsi_Cmnd *SCpnt = NULL;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ list_for_each(l, &queue->head) {
+ QE_t *q = list_entry(l, QE_t, list);
+ if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) {
+ SCpnt = __queue_remove(queue, l);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+
+ return SCpnt;
+}
+
+/*
+ * Function: Scsi_Cmnd *queue_remove (queue)
+ * Purpose : removes first SCSI command from a queue
+ * Params : queue - queue to remove command from
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+Scsi_Cmnd *queue_remove(Queue_t *queue)
+{
+ unsigned long flags;
+ Scsi_Cmnd *SCpnt = NULL;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ if (!list_empty(&queue->head))
+ SCpnt = __queue_remove(queue, queue->head.next);
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+
+ return SCpnt;
+}
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
+ * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
+ * Params : queue - queue to remove command from
+ * target - target that we want
+ * lun - lun on device
+ * tag - tag on device
+ * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
+ */
+Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag)
+{
+ unsigned long flags;
+ struct list_head *l;
+ Scsi_Cmnd *SCpnt = NULL;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ list_for_each(l, &queue->head) {
+ QE_t *q = list_entry(l, QE_t, list);
+ if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
+ q->SCpnt->tag == tag) {
+ SCpnt = __queue_remove(queue, l);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+
+ return SCpnt;
+}
+
+/*
+ * Function: queue_remove_all_target(queue, target)
+ * Purpose : remove all SCSI commands from the queue for a specified target
+ * Params : queue - queue to remove command from
+ * target - target device id
+ * Returns : nothing
+ */
+void queue_remove_all_target(Queue_t *queue, int target)
+{
+ unsigned long flags;
+ struct list_head *l;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ list_for_each(l, &queue->head) {
+ QE_t *q = list_entry(l, QE_t, list);
+ if (q->SCpnt->device->id == target)
+ __queue_remove(queue, l);
+ }
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+}
+
+/*
+ * Function: int queue_probetgtlun (queue, target, lun)
+ * Purpose : check to see if we have a command in the queue for the specified
+ * target/lun.
+ * Params : queue - queue to look in
+ * target - target we want to probe
+ * lun - lun on target
+ * Returns : 0 if not found, != 0 if found
+ */
+int queue_probetgtlun (Queue_t *queue, int target, int lun)
+{
+ unsigned long flags;
+ struct list_head *l;
+ int found = 0;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ list_for_each(l, &queue->head) {
+ QE_t *q = list_entry(l, QE_t, list);
+ if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+
+ return found;
+}
+
+/*
+ * Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : remove a specific command from the queues
+ * Params : queue - queue to look in
+ * SCpnt - command to find
+ * Returns : 0 if not found
+ */
+int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt)
+{
+ unsigned long flags;
+ struct list_head *l;
+ int found = 0;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ list_for_each(l, &queue->head) {
+ QE_t *q = list_entry(l, QE_t, list);
+ if (q->SCpnt == SCpnt) {
+ __queue_remove(queue, l);
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+
+ return found;
+}
+
+EXPORT_SYMBOL(queue_initialise);
+EXPORT_SYMBOL(queue_free);
+EXPORT_SYMBOL(__queue_add);
+EXPORT_SYMBOL(queue_remove);
+EXPORT_SYMBOL(queue_remove_exclude);
+EXPORT_SYMBOL(queue_remove_tgtluntag);
+EXPORT_SYMBOL(queue_remove_cmd);
+EXPORT_SYMBOL(queue_remove_all_target);
+EXPORT_SYMBOL(queue_probetgtlun);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SCSI command queueing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/arm/queue.h b/drivers/scsi/arm/queue.h
new file mode 100644
index 000000000000..0c9dec4c1716
--- /dev/null
+++ b/drivers/scsi/arm/queue.h
@@ -0,0 +1,105 @@
+/*
+ * linux/drivers/acorn/scsi/queue.h: queue handling
+ *
+ * Copyright (C) 1997 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef QUEUE_H
+#define QUEUE_H
+
+typedef struct {
+ struct list_head head;
+ struct list_head free;
+ spinlock_t queue_lock;
+ void *alloc; /* start of allocated mem */
+} Queue_t;
+
+/*
+ * Function: void queue_initialise (Queue_t *queue)
+ * Purpose : initialise a queue
+ * Params : queue - queue to initialise
+ */
+extern int queue_initialise (Queue_t *queue);
+
+/*
+ * Function: void queue_free (Queue_t *queue)
+ * Purpose : free a queue
+ * Params : queue - queue to free
+ */
+extern void queue_free (Queue_t *queue);
+
+/*
+ * Function: Scsi_Cmnd *queue_remove (queue)
+ * Purpose : removes first SCSI command from a queue
+ * Params : queue - queue to remove command from
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+extern Scsi_Cmnd *queue_remove (Queue_t *queue);
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude)
+ * Purpose : remove a SCSI command from a queue
+ * Params : queue - queue to remove command from
+ * exclude - array of busy LUNs
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude);
+
+#define queue_add_cmd_ordered(queue,SCpnt) \
+ __queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE)
+#define queue_add_cmd_tail(queue,SCpnt) \
+ __queue_add(queue,SCpnt,0)
+/*
+ * Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
+ * Purpose : Add a new command onto a queue
+ * Params : queue - destination queue
+ * SCpnt - command to add
+ * head - add command to head of queue
+ * Returns : 0 on error, !0 on success
+ */
+extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head);
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
+ * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
+ * Params : queue - queue to remove command from
+ * target - target that we want
+ * lun - lun on device
+ * tag - tag on device
+ * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
+ */
+extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag);
+
+/*
+ * Function: queue_remove_all_target(queue, target)
+ * Purpose : remove all SCSI commands from the queue for a specified target
+ * Params : queue - queue to remove command from
+ * target - target device id
+ * Returns : nothing
+ */
+extern void queue_remove_all_target(Queue_t *queue, int target);
+
+/*
+ * Function: int queue_probetgtlun (queue, target, lun)
+ * Purpose : check to see if we have a command in the queue for the specified
+ * target/lun.
+ * Params : queue - queue to look in
+ * target - target we want to probe
+ * lun - lun on target
+ * Returns : 0 if not found, != 0 if found
+ */
+extern int queue_probetgtlun (Queue_t *queue, int target, int lun);
+
+/*
+ * Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : remove a specific command from the queues
+ * Params : queue - queue to look in
+ * SCpnt - command to find
+ * Returns : 0 if not found
+ */
+int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt);
+
+#endif /* QUEUE_H */
diff --git a/drivers/scsi/arm/scsi.h b/drivers/scsi/arm/scsi.h
new file mode 100644
index 000000000000..2f1b3f4bf954
--- /dev/null
+++ b/drivers/scsi/arm/scsi.h
@@ -0,0 +1,115 @@
+/*
+ * linux/drivers/acorn/scsi/scsi.h
+ *
+ * Copyright (C) 2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Commonly used scsi driver functions.
+ */
+
+#define BELT_AND_BRACES
+
+/*
+ * The scatter-gather list handling. This contains all
+ * the yucky stuff that needs to be fixed properly.
+ */
+static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int max)
+{
+ int bufs = SCp->buffers_residual;
+
+ BUG_ON(bufs + 1 > max);
+
+ sg->page = virt_to_page(SCp->ptr);
+ sg->offset = offset_in_page(SCp->ptr);
+ sg->length = SCp->this_residual;
+
+ if (bufs)
+ memcpy(sg + 1, SCp->buffer + 1,
+ sizeof(struct scatterlist) * bufs);
+ return bufs + 1;
+}
+
+static inline int next_SCp(Scsi_Pointer *SCp)
+{
+ int ret = SCp->buffers_residual;
+ if (ret) {
+ SCp->buffer++;
+ SCp->buffers_residual--;
+ SCp->ptr = (char *)
+ (page_address(SCp->buffer->page) +
+ SCp->buffer->offset);
+ SCp->this_residual = SCp->buffer->length;
+ } else {
+ SCp->ptr = NULL;
+ SCp->this_residual = 0;
+ }
+ return ret;
+}
+
+static inline unsigned char get_next_SCp_byte(Scsi_Pointer *SCp)
+{
+ char c = *SCp->ptr;
+
+ SCp->ptr += 1;
+ SCp->this_residual -= 1;
+
+ return c;
+}
+
+static inline void put_next_SCp_byte(Scsi_Pointer *SCp, unsigned char c)
+{
+ *SCp->ptr = c;
+ SCp->ptr += 1;
+ SCp->this_residual -= 1;
+}
+
+static inline void init_SCp(Scsi_Cmnd *SCpnt)
+{
+ memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer));
+
+ if (SCpnt->use_sg) {
+ unsigned long len = 0;
+ int buf;
+
+ SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer;
+ SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+ SCpnt->SCp.ptr = (char *)
+ (page_address(SCpnt->SCp.buffer->page) +
+ SCpnt->SCp.buffer->offset);
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+
+#ifdef BELT_AND_BRACES
+ /*
+ * Calculate correct buffer length. Some commands
+ * come in with the wrong request_bufflen.
+ */
+ for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++)
+ len += SCpnt->SCp.buffer[buf].length;
+
+ if (SCpnt->request_bufflen != len)
+ printk(KERN_WARNING "scsi%d.%c: bad request buffer "
+ "length %d, should be %ld\n", SCpnt->device->host->host_no,
+ '0' + SCpnt->device->id, SCpnt->request_bufflen, len);
+ SCpnt->request_bufflen = len;
+#endif
+ } else {
+ SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ }
+
+ /*
+ * If the upper SCSI layers pass a buffer, but zero length,
+ * we aren't interested in the buffer pointer.
+ */
+ if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) {
+#if 0 //def BELT_AND_BRACES
+ printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for "
+ "command ", SCpnt->host->host_no, '0' + SCpnt->target);
+ print_command(SCpnt->cmnd);
+#endif
+ SCpnt->SCp.ptr = NULL;
+ }
+}
diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
new file mode 100644
index 000000000000..ce19728aa8a4
--- /dev/null
+++ b/drivers/scsi/ata_piix.c
@@ -0,0 +1,690 @@
+/*
+
+ ata_piix.c - Intel PATA/SATA controllers
+
+ Maintained by: Jeff Garzik <jgarzik@pobox.com>
+ Please ALWAYS copy linux-ide@vger.kernel.org
+ on emails.
+
+
+ Copyright 2003-2004 Red Hat Inc
+ Copyright 2003-2004 Jeff Garzik
+
+
+ Copyright header from piix.c:
+
+ Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
+ Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
+ Copyright (C) 2003 Red Hat Inc <alan@redhat.com>
+
+ May be copied or modified under the terms of the GNU General Public License
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "ata_piix"
+#define DRV_VERSION "1.03"
+
+enum {
+ PIIX_IOCFG = 0x54, /* IDE I/O configuration register */
+ ICH5_PMR = 0x90, /* port mapping register */
+ ICH5_PCS = 0x92, /* port control and status */
+
+ PIIX_FLAG_AHCI = (1 << 28), /* AHCI possible */
+ PIIX_FLAG_CHECKINTR = (1 << 29), /* make sure PCI INTx enabled */
+ PIIX_FLAG_COMBINED = (1 << 30), /* combined mode possible */
+
+ /* combined mode. if set, PATA is channel 0.
+ * if clear, PATA is channel 1.
+ */
+ PIIX_COMB_PATA_P0 = (1 << 1),
+ PIIX_COMB = (1 << 2), /* combined mode enabled? */
+
+ PIIX_PORT_PRESENT = (1 << 0),
+ PIIX_PORT_ENABLED = (1 << 4),
+
+ PIIX_80C_PRI = (1 << 5) | (1 << 4),
+ PIIX_80C_SEC = (1 << 7) | (1 << 6),
+
+ ich5_pata = 0,
+ ich5_sata = 1,
+ piix4_pata = 2,
+ ich6_sata = 3,
+ ich6_sata_rm = 4,
+ ich7_sata = 5,
+};
+
+static int piix_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+
+static void piix_pata_phy_reset(struct ata_port *ap);
+static void piix_sata_phy_reset(struct ata_port *ap);
+static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev);
+static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev);
+
+static unsigned int in_module_init = 1;
+
+static struct pci_device_id piix_pci_tbl[] = {
+#ifdef ATA_ENABLE_PATA
+ { 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata },
+ { 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata },
+ { 0x8086, 0x25a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata },
+#endif
+
+ /* NOTE: The following PCI ids must be kept in sync with the
+ * list in drivers/pci/quirks.c.
+ */
+
+ { 0x8086, 0x24d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
+ { 0x8086, 0x24df, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
+ { 0x8086, 0x25a3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
+ { 0x8086, 0x25b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_sata },
+ { 0x8086, 0x2651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata },
+ { 0x8086, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_rm },
+ { 0x8086, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_sata_rm },
+ { 0x8086, 0x27c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata },
+ { 0x8086, 0x27c4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_sata },
+
+ { } /* terminate list */
+};
+
+static struct pci_driver piix_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = piix_pci_tbl,
+ .probe = piix_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template piix_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations piix_pata_ops = {
+ .port_disable = ata_port_disable,
+ .set_piomode = piix_set_piomode,
+ .set_dmamode = piix_set_dmamode,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .phy_reset = piix_pata_phy_reset,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .eng_timeout = ata_eng_timeout,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static struct ata_port_operations piix_sata_ops = {
+ .port_disable = ata_port_disable,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .phy_reset = piix_sata_phy_reset,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .eng_timeout = ata_eng_timeout,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static struct ata_port_info piix_port_info[] = {
+ /* ich5_pata */
+ {
+ .sht = &piix_sht,
+ .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST |
+ PIIX_FLAG_CHECKINTR,
+ .pio_mask = 0x1f, /* pio0-4 */
+#if 0
+ .mwdma_mask = 0x06, /* mwdma1-2 */
+#else
+ .mwdma_mask = 0x00, /* mwdma broken */
+#endif
+ .udma_mask = 0x3f, /* udma0-5 */
+ .port_ops = &piix_pata_ops,
+ },
+
+ /* ich5_sata */
+ {
+ .sht = &piix_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST |
+ PIIX_FLAG_COMBINED | PIIX_FLAG_CHECKINTR,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &piix_sata_ops,
+ },
+
+ /* piix4_pata */
+ {
+ .sht = &piix_sht,
+ .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST,
+ .pio_mask = 0x1f, /* pio0-4 */
+#if 0
+ .mwdma_mask = 0x06, /* mwdma1-2 */
+#else
+ .mwdma_mask = 0x00, /* mwdma broken */
+#endif
+ .udma_mask = ATA_UDMA_MASK_40C,
+ .port_ops = &piix_pata_ops,
+ },
+
+ /* ich6_sata */
+ {
+ .sht = &piix_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST |
+ PIIX_FLAG_COMBINED | PIIX_FLAG_CHECKINTR |
+ ATA_FLAG_SLAVE_POSS,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &piix_sata_ops,
+ },
+
+ /* ich6_sata_rm */
+ {
+ .sht = &piix_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST |
+ PIIX_FLAG_COMBINED | PIIX_FLAG_CHECKINTR |
+ ATA_FLAG_SLAVE_POSS | PIIX_FLAG_AHCI,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &piix_sata_ops,
+ },
+
+ /* ich7_sata */
+ {
+ .sht = &piix_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST |
+ PIIX_FLAG_COMBINED | PIIX_FLAG_CHECKINTR |
+ ATA_FLAG_SLAVE_POSS | PIIX_FLAG_AHCI,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &piix_sata_ops,
+ },
+};
+
+static struct pci_bits piix_enable_bits[] = {
+ { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */
+ { 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */
+};
+
+MODULE_AUTHOR("Andre Hedrick, Alan Cox, Andrzej Krzysztofowicz, Jeff Garzik");
+MODULE_DESCRIPTION("SCSI low-level driver for Intel PIIX/ICH ATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+/**
+ * piix_pata_cbl_detect - Probe host controller cable detect info
+ * @ap: Port for which cable detect info is desired
+ *
+ * Read 80c cable indicator from ATA PCI device's PCI config
+ * register. This register is normally set by firmware (BIOS).
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ */
+static void piix_pata_cbl_detect(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+ u8 tmp, mask;
+
+ /* no 80c support in host controller? */
+ if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0)
+ goto cbl40;
+
+ /* check BIOS cable detect results */
+ mask = ap->hard_port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC;
+ pci_read_config_byte(pdev, PIIX_IOCFG, &tmp);
+ if ((tmp & mask) == 0)
+ goto cbl40;
+
+ ap->cbl = ATA_CBL_PATA80;
+ return;
+
+cbl40:
+ ap->cbl = ATA_CBL_PATA40;
+ ap->udma_mask &= ATA_UDMA_MASK_40C;
+}
+
+/**
+ * piix_pata_phy_reset - Probe specified port on PATA host controller
+ * @ap: Port to probe
+ *
+ * Probe PATA phy.
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ */
+
+static void piix_pata_phy_reset(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+
+ if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->hard_port_no])) {
+ ata_port_disable(ap);
+ printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
+ return;
+ }
+
+ piix_pata_cbl_detect(ap);
+
+ ata_port_probe(ap);
+
+ ata_bus_reset(ap);
+}
+
+/**
+ * piix_sata_probe - Probe PCI device for present SATA devices
+ * @ap: Port associated with the PCI device we wish to probe
+ *
+ * Reads SATA PCI device's PCI config register Port Configuration
+ * and Status (PCS) to determine port and device availability.
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ *
+ * RETURNS:
+ * Non-zero if device detected, zero otherwise.
+ */
+static int piix_sata_probe (struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+ int combined = (ap->flags & ATA_FLAG_SLAVE_POSS);
+ int orig_mask, mask, i;
+ u8 pcs;
+
+ mask = (PIIX_PORT_PRESENT << ap->hard_port_no) |
+ (PIIX_PORT_ENABLED << ap->hard_port_no);
+
+ pci_read_config_byte(pdev, ICH5_PCS, &pcs);
+ orig_mask = (int) pcs & 0xff;
+
+ /* TODO: this is vaguely wrong for ICH6 combined mode,
+ * where only two of the four SATA ports are mapped
+ * onto a single ATA channel. It is also vaguely inaccurate
+ * for ICH5, which has only two ports. However, this is ok,
+ * as further device presence detection code will handle
+ * any false positives produced here.
+ */
+
+ for (i = 0; i < 4; i++) {
+ mask = (PIIX_PORT_PRESENT << i) | (PIIX_PORT_ENABLED << i);
+
+ if ((orig_mask & mask) == mask)
+ if (combined || (i == ap->hard_port_no))
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * piix_sata_phy_reset - Probe specified port on SATA host controller
+ * @ap: Port to probe
+ *
+ * Probe SATA phy.
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ */
+
+static void piix_sata_phy_reset(struct ata_port *ap)
+{
+ if (!piix_sata_probe(ap)) {
+ ata_port_disable(ap);
+ printk(KERN_INFO "ata%u: SATA port has no device.\n", ap->id);
+ return;
+ }
+
+ ap->cbl = ATA_CBL_SATA;
+
+ ata_port_probe(ap);
+
+ ata_bus_reset(ap);
+}
+
+/**
+ * piix_set_piomode - Initialize host controller PATA PIO timings
+ * @ap: Port whose timings we are configuring
+ * @adev: um
+ * @pio: PIO mode, 0 - 4
+ *
+ * Set PIO mode for device, in host controller PCI config space.
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ */
+
+static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev)
+{
+ unsigned int pio = adev->pio_mode - XFER_PIO_0;
+ struct pci_dev *dev = to_pci_dev(ap->host_set->dev);
+ unsigned int is_slave = (adev->devno != 0);
+ unsigned int master_port= ap->hard_port_no ? 0x42 : 0x40;
+ unsigned int slave_port = 0x44;
+ u16 master_data;
+ u8 slave_data;
+
+ static const /* ISP RTC */
+ u8 timings[][2] = { { 0, 0 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 1 },
+ { 2, 3 }, };
+
+ pci_read_config_word(dev, master_port, &master_data);
+ if (is_slave) {
+ master_data |= 0x4000;
+ /* enable PPE, IE and TIME */
+ master_data |= 0x0070;
+ pci_read_config_byte(dev, slave_port, &slave_data);
+ slave_data &= (ap->hard_port_no ? 0x0f : 0xf0);
+ slave_data |=
+ (timings[pio][0] << 2) |
+ (timings[pio][1] << (ap->hard_port_no ? 4 : 0));
+ } else {
+ master_data &= 0xccf8;
+ /* enable PPE, IE and TIME */
+ master_data |= 0x0007;
+ master_data |=
+ (timings[pio][0] << 12) |
+ (timings[pio][1] << 8);
+ }
+ pci_write_config_word(dev, master_port, master_data);
+ if (is_slave)
+ pci_write_config_byte(dev, slave_port, slave_data);
+}
+
+/**
+ * piix_set_dmamode - Initialize host controller PATA PIO timings
+ * @ap: Port whose timings we are configuring
+ * @adev: um
+ * @udma: udma mode, 0 - 6
+ *
+ * Set UDMA mode for device, in host controller PCI config space.
+ *
+ * LOCKING:
+ * None (inherited from caller).
+ */
+
+static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev)
+{
+ unsigned int udma = adev->dma_mode; /* FIXME: MWDMA too */
+ struct pci_dev *dev = to_pci_dev(ap->host_set->dev);
+ u8 maslave = ap->hard_port_no ? 0x42 : 0x40;
+ u8 speed = udma;
+ unsigned int drive_dn = (ap->hard_port_no ? 2 : 0) + adev->devno;
+ int a_speed = 3 << (drive_dn * 4);
+ int u_flag = 1 << drive_dn;
+ int v_flag = 0x01 << drive_dn;
+ int w_flag = 0x10 << drive_dn;
+ int u_speed = 0;
+ int sitre;
+ u16 reg4042, reg4a;
+ u8 reg48, reg54, reg55;
+
+ pci_read_config_word(dev, maslave, &reg4042);
+ DPRINTK("reg4042 = 0x%04x\n", reg4042);
+ sitre = (reg4042 & 0x4000) ? 1 : 0;
+ pci_read_config_byte(dev, 0x48, &reg48);
+ pci_read_config_word(dev, 0x4a, &reg4a);
+ pci_read_config_byte(dev, 0x54, &reg54);
+ pci_read_config_byte(dev, 0x55, &reg55);
+
+ switch(speed) {
+ case XFER_UDMA_4:
+ case XFER_UDMA_2: u_speed = 2 << (drive_dn * 4); break;
+ case XFER_UDMA_6:
+ case XFER_UDMA_5:
+ case XFER_UDMA_3:
+ case XFER_UDMA_1: u_speed = 1 << (drive_dn * 4); break;
+ case XFER_UDMA_0: u_speed = 0 << (drive_dn * 4); break;
+ case XFER_MW_DMA_2:
+ case XFER_MW_DMA_1: break;
+ default:
+ BUG();
+ return;
+ }
+
+ if (speed >= XFER_UDMA_0) {
+ if (!(reg48 & u_flag))
+ pci_write_config_byte(dev, 0x48, reg48 | u_flag);
+ if (speed == XFER_UDMA_5) {
+ pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
+ } else {
+ pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
+ }
+ if ((reg4a & a_speed) != u_speed)
+ pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed);
+ if (speed > XFER_UDMA_2) {
+ if (!(reg54 & v_flag))
+ pci_write_config_byte(dev, 0x54, reg54 | v_flag);
+ } else
+ pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
+ } else {
+ if (reg48 & u_flag)
+ pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
+ if (reg4a & a_speed)
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ if (reg54 & v_flag)
+ pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
+ if (reg55 & w_flag)
+ pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
+ }
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+ u16 pci_command;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+ if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+ pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+ }
+}
+
+#define AHCI_PCI_BAR 5
+#define AHCI_GLOBAL_CTL 0x04
+#define AHCI_ENABLE (1 << 31)
+static int piix_disable_ahci(struct pci_dev *pdev)
+{
+ void *mmio;
+ unsigned long addr;
+ u32 tmp;
+ int rc = 0;
+
+ /* BUG: pci_enable_device has not yet been called. This
+ * works because this device is usually set up by BIOS.
+ */
+
+ addr = pci_resource_start(pdev, AHCI_PCI_BAR);
+ if (!addr || !pci_resource_len(pdev, AHCI_PCI_BAR))
+ return 0;
+
+ mmio = ioremap(addr, 64);
+ if (!mmio)
+ return -ENOMEM;
+
+ tmp = readl(mmio + AHCI_GLOBAL_CTL);
+ if (tmp & AHCI_ENABLE) {
+ tmp &= ~AHCI_ENABLE;
+ writel(tmp, mmio + AHCI_GLOBAL_CTL);
+
+ tmp = readl(mmio + AHCI_GLOBAL_CTL);
+ if (tmp & AHCI_ENABLE)
+ rc = -EIO;
+ }
+
+ iounmap(mmio);
+ return rc;
+}
+
+/**
+ * piix_init_one - Register PIIX ATA PCI device with kernel services
+ * @pdev: PCI device to register
+ * @ent: Entry in piix_pci_tbl matching with @pdev
+ *
+ * Called from kernel PCI layer. We probe for combined mode (sigh),
+ * and then hand over control to libata, for it to do the rest.
+ *
+ * LOCKING:
+ * Inherited from PCI layer (may sleep).
+ *
+ * RETURNS:
+ * Zero on success, or -ERRNO value.
+ */
+
+static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_port_info *port_info[2];
+ unsigned int combined = 0, n_ports = 1;
+ unsigned int pata_chan = 0, sata_chan = 0;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ /* no hotplugging support (FIXME) */
+ if (!in_module_init)
+ return -ENODEV;
+
+ port_info[0] = &piix_port_info[ent->driver_data];
+ port_info[1] = NULL;
+
+ if (port_info[0]->host_flags & PIIX_FLAG_AHCI) {
+ int rc = piix_disable_ahci(pdev);
+ if (rc)
+ return rc;
+ }
+
+ if (port_info[0]->host_flags & PIIX_FLAG_COMBINED) {
+ u8 tmp;
+ pci_read_config_byte(pdev, ICH5_PMR, &tmp);
+
+ if (tmp & PIIX_COMB) {
+ combined = 1;
+ if (tmp & PIIX_COMB_PATA_P0)
+ sata_chan = 1;
+ else
+ pata_chan = 1;
+ }
+ }
+
+ /* On ICH5, some BIOSen disable the interrupt using the
+ * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3.
+ * On ICH6, this bit has the same effect, but only when
+ * MSI is disabled (and it is disabled, as we don't use
+ * message-signalled interrupts currently).
+ */
+ if (port_info[0]->host_flags & PIIX_FLAG_CHECKINTR)
+ pci_enable_intx(pdev);
+
+ if (combined) {
+ port_info[sata_chan] = &piix_port_info[ent->driver_data];
+ port_info[sata_chan]->host_flags |= ATA_FLAG_SLAVE_POSS;
+ port_info[pata_chan] = &piix_port_info[ich5_pata];
+ n_ports++;
+
+ printk(KERN_WARNING DRV_NAME ": combined mode detected\n");
+ }
+
+ return ata_pci_init_one(pdev, port_info, n_ports);
+}
+
+/**
+ * piix_init -
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ *
+ */
+
+static int __init piix_init(void)
+{
+ int rc;
+
+ DPRINTK("pci_module_init\n");
+ rc = pci_module_init(&piix_pci_driver);
+ if (rc)
+ return rc;
+
+ in_module_init = 0;
+
+ DPRINTK("done\n");
+ return 0;
+}
+
+/**
+ * piix_exit -
+ *
+ * LOCKING:
+ *
+ */
+
+static void __exit piix_exit(void)
+{
+ pci_unregister_driver(&piix_pci_driver);
+}
+
+module_init(piix_init);
+module_exit(piix_exit);
+
diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c
new file mode 100644
index 000000000000..5d1e78ebed83
--- /dev/null
+++ b/drivers/scsi/atari_NCR5380.c
@@ -0,0 +1,2986 @@
+/*
+ * NCR 5380 generic driver routines. These should make it *trivial*
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
+ *
+ * Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
+ * this file, too:
+ *
+ * - Some of the debug statements were incorrect (undefined variables and the
+ * like). I fixed that.
+ *
+ * - In information_transfer(), I think a #ifdef was wrong. Looking at the
+ * possible DMA transfer size should also happen for REAL_DMA. I added this
+ * in the #if statement.
+ *
+ * - When using real DMA, information_transfer() should return in a DATAOUT
+ * phase after starting the DMA. It has nothing more to do.
+ *
+ * - The interrupt service routine should run main after end of DMA, too (not
+ * only after RESELECTION interrupts). Additionally, it should _not_ test
+ * for more interrupts after running main, since a DMA process may have
+ * been started and interrupts are turned on now. The new int could happen
+ * inside the execution of NCR5380_intr(), leading to recursive
+ * calls.
+ *
+ * - I've added a function merge_contiguous_buffers() that tries to
+ * merge scatter-gather buffers that are located at contiguous
+ * physical addresses and can be processed with the same DMA setup.
+ * Since most scatter-gather operations work on a page (4K) of
+ * 4 buffers (1K), in more than 90% of all cases three interrupts and
+ * DMA setup actions are saved.
+ *
+ * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
+ * and USLEEP, because these were messing up readability and will never be
+ * needed for Atari SCSI.
+ *
+ * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
+ * stuff), and 'main' is executed in a bottom half if awoken by an
+ * interrupt.
+ *
+ * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
+ * constructs. In my eyes, this made the source rather unreadable, so I
+ * finally replaced that by the *_PRINTK() macros.
+ *
+ */
+
+/*
+ * Further development / testing that should be done :
+ * 1. Test linked command handling code after Eric is ready with
+ * the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) \
+ { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \
+ if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) \
+ { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \
+ (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \
+ if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip. This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver. To use it on a different platform,
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem,
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system. So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the
+ * NCR5380_print_options command, which should be called from the
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 :
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations.
+ *
+ * The workaround for this is to keep track of devices that have
+ * disconnected. If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ *
+ * Some tweaking of N and M needs to be done. An algorithm based
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function. It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the
+ * issue queue and calling NCR5380_select() if a nexus
+ * is not established.
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work. If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr() which will in turn call NCR5380_reselect
+ * to reestablish a nexus. This will run main if necessary.
+ *
+ * On command termination, the done function will be called as
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips. To use it, you write an architecture specific functions
+ * and macros and include this file in your driver.
+ *
+ * These macros control options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * These macros MUST be defined :
+ *
+ * NCR5380_read(register) - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions :
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes
+ * that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define
+ *
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ * NCR5380_proc_info
+ *
+ * to be the global entry points into the specific driver, ie
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID. If the
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used. Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/* Macros ease life... :-) */
+#define SETUP_HOSTDATA(in) \
+ struct NCR5380_hostdata *hostdata = \
+ (struct NCR5380_hostdata *)(in)->hostdata
+#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
+
+#define NEXT(cmd) ((Scsi_Cmnd *)((cmd)->host_scribble))
+#define NEXTADDR(cmd) ((Scsi_Cmnd **)&((cmd)->host_scribble))
+
+#define HOSTNO instance->host_no
+#define H_NO(cmd) (cmd)->device->host->host_no
+
+#ifdef SUPPORT_TAGS
+
+/*
+ * Functions for handling tagged queuing
+ * =====================================
+ *
+ * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
+ *
+ * Using consecutive numbers for the tags is no good idea in my eyes. There
+ * could be wrong re-usings if the counter (8 bit!) wraps and some early
+ * command has been preempted for a long time. My solution: a bitfield for
+ * remembering used tags.
+ *
+ * There's also the problem that each target has a certain queue size, but we
+ * cannot know it in advance :-( We just see a QUEUE_FULL status being
+ * returned. So, in this case, the driver internal queue size assumption is
+ * reduced to the number of active tags if QUEUE_FULL is returned by the
+ * target. The command is returned to the mid-level, but with status changed
+ * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
+ * correctly.
+ *
+ * We're also not allowed running tagged commands as long as an untagged
+ * command is active. And REQUEST SENSE commands after a contingent allegiance
+ * condition _must_ be untagged. To keep track whether an untagged command has
+ * been issued, the host->busy array is still employed, as it is without
+ * support for tagged queuing.
+ *
+ * One could suspect that there are possible race conditions between
+ * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
+ * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
+ * which already guaranteed to be running at most once. It is also the only
+ * place where tags/LUNs are allocated. So no other allocation can slip
+ * between that pair, there could only happen a reselection, which can free a
+ * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
+ * important: the tag bit must be cleared before 'nr_allocated' is decreased.
+ */
+
+/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
+#undef TAG_NONE
+#define TAG_NONE 0xff
+
+typedef struct {
+ DECLARE_BITMAP(allocated, MAX_TAGS);
+ int nr_allocated;
+ int queue_size;
+} TAG_ALLOC;
+
+static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
+
+
+static void __init init_tags( void )
+{
+ int target, lun;
+ TAG_ALLOC *ta;
+
+ if (!setup_use_tagged_queuing)
+ return;
+
+ for( target = 0; target < 8; ++target ) {
+ for( lun = 0; lun < 8; ++lun ) {
+ ta = &TagAlloc[target][lun];
+ bitmap_zero(ta->allocated, MAX_TAGS);
+ ta->nr_allocated = 0;
+ /* At the beginning, assume the maximum queue size we could
+ * support (MAX_TAGS). This value will be decreased if the target
+ * returns QUEUE_FULL status.
+ */
+ ta->queue_size = MAX_TAGS;
+ }
+ }
+}
+
+
+/* Check if we can issue a command to this LUN: First see if the LUN is marked
+ * busy by an untagged command. If the command should use tagged queuing, also
+ * check that there is a free tag and the target's queue won't overflow. This
+ * function should be called with interrupts disabled to avoid race
+ * conditions.
+ */
+
+static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+ SETUP_HOSTDATA(cmd->device->host);
+
+ if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))
+ return( 1 );
+ if (!should_be_tagged ||
+ !setup_use_tagged_queuing || !cmd->device->tagged_supported)
+ return( 0 );
+ if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >=
+ TagAlloc[cmd->device->id][cmd->device->lun].queue_size ) {
+ TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n",
+ H_NO(cmd), cmd->device->id, cmd->device->lun );
+ return( 1 );
+ }
+ return( 0 );
+}
+
+
+/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
+ * must be called before!), or reserve the LUN in 'busy' if the command is
+ * untagged.
+ */
+
+static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+ SETUP_HOSTDATA(cmd->device->host);
+
+ /* If we or the target don't support tagged queuing, allocate the LUN for
+ * an untagged command.
+ */
+ if (!should_be_tagged ||
+ !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
+ cmd->tag = TAG_NONE;
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged "
+ "command\n", H_NO(cmd), cmd->device->id, cmd->device->lun );
+ }
+ else {
+ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+
+ cmd->tag = find_first_zero_bit( ta->allocated, MAX_TAGS );
+ set_bit( cmd->tag, ta->allocated );
+ ta->nr_allocated++;
+ TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
+ "(now %d tags in use)\n",
+ H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun,
+ ta->nr_allocated );
+ }
+}
+
+
+/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
+ * unlock the LUN.
+ */
+
+static void cmd_free_tag( Scsi_Cmnd *cmd )
+{
+ SETUP_HOSTDATA(cmd->device->host);
+
+ if (cmd->tag == TAG_NONE) {
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n",
+ H_NO(cmd), cmd->device->id, cmd->device->lun );
+ }
+ else if (cmd->tag >= MAX_TAGS) {
+ printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
+ H_NO(cmd), cmd->tag );
+ }
+ else {
+ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+ clear_bit( cmd->tag, ta->allocated );
+ ta->nr_allocated--;
+ TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
+ H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun );
+ }
+}
+
+
+static void free_all_tags( void )
+{
+ int target, lun;
+ TAG_ALLOC *ta;
+
+ if (!setup_use_tagged_queuing)
+ return;
+
+ for( target = 0; target < 8; ++target ) {
+ for( lun = 0; lun < 8; ++lun ) {
+ ta = &TagAlloc[target][lun];
+ bitmap_zero(ta->allocated, MAX_TAGS);
+ ta->nr_allocated = 0;
+ }
+ }
+}
+
+#endif /* SUPPORT_TAGS */
+
+
+/*
+ * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+ *
+ * Purpose: Try to merge several scatter-gather requests into one DMA
+ * transfer. This is possible if the scatter buffers lie on
+ * physical contiguous addresses.
+ *
+ * Parameters: Scsi_Cmnd *cmd
+ * The command to work on. The first scatter buffer's data are
+ * assumed to be already transfered into ptr/this_residual.
+ */
+
+static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+{
+ unsigned long endaddr;
+#if (NDEBUG & NDEBUG_MERGING)
+ unsigned long oldlen = cmd->SCp.this_residual;
+ int cnt = 1;
+#endif
+
+ for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
+ cmd->SCp.buffers_residual &&
+ virt_to_phys(page_address(cmd->SCp.buffer[1].page)+
+ cmd->SCp.buffer[1].offset) == endaddr; ) {
+ MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
+ cmd->SCp.buffer[1].address, endaddr);
+#if (NDEBUG & NDEBUG_MERGING)
+ ++cnt;
+#endif
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual += cmd->SCp.buffer->length;
+ endaddr += cmd->SCp.buffer->length;
+ }
+#if (NDEBUG & NDEBUG_MERGING)
+ if (oldlen != cmd->SCp.this_residual)
+ MER_PRINTK("merged %d buffers from %p, new length %08x\n",
+ cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
+#endif
+}
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the
+ * start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+ /*
+ * Initialize the Scsi Pointer field so that all of the commands in the
+ * various queues are valid.
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *)page_address(cmd->SCp.buffer->page)+
+ cmd->SCp.buffer->offset;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ /* ++roman: Try to merge some scatter-buffers if they are at
+ * contiguous physical addresses.
+ */
+ merge_contiguous_buffers( cmd );
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+}
+
+#include <linux/config.h>
+#include <linux/delay.h>
+
+#if NDEBUG
+static struct {
+ unsigned char mask;
+ const char * name;}
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
+ { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
+ { SR_SEL, "SEL" }, {0, NULL}},
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
+ "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
+ {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+ {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
+ {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+ unsigned char status, data, basr, mr, icr, i;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+ status = NCR5380_read(STATUS_REG);
+ mr = NCR5380_read(MODE_REG);
+ icr = NCR5380_read(INITIATOR_COMMAND_REG);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ local_irq_restore(flags);
+ printk("STATUS_REG: %02x ", status);
+ for (i = 0; signals[i].mask ; ++i)
+ if (status & signals[i].mask)
+ printk(",%s", signals[i].name);
+ printk("\nBASR: %02x ", basr);
+ for (i = 0; basrs[i].mask ; ++i)
+ if (basr & basrs[i].mask)
+ printk(",%s", basrs[i].name);
+ printk("\nICR: %02x ", icr);
+ for (i = 0; icrs[i].mask; ++i)
+ if (icr & icrs[i].mask)
+ printk(",%s", icrs[i].name);
+ printk("\nMODE: %02x ", mr);
+ for (i = 0; mrs[i].mask; ++i)
+ if (mr & mrs[i].mask)
+ printk(",%s", mrs[i].name);
+ printk("\n");
+}
+
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+ {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+ {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+ {PHASE_UNKNOWN, "UNKNOWN"}};
+
+/*
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance)
+{
+ unsigned char status;
+ int i;
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i);
+ printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
+ }
+}
+
+#else /* !NDEBUG */
+
+/* dummies... */
+__inline__ void NCR5380_print(struct Scsi_Host *instance) { };
+__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { };
+
+#endif
+
+/*
+ * ++roman: New scheme of calling NCR5380_main()
+ *
+ * If we're not in an interrupt, we can call our main directly, it cannot be
+ * already running. Else, we queue it on a task queue, if not 'main_running'
+ * tells us that a lower level is already executing it. This way,
+ * 'main_running' needs not be protected in a special way.
+ *
+ * queue_main() is a utility function for putting our main onto the task
+ * queue, if main_running is false. It should be called only from a
+ * interrupt or bottom half.
+ */
+
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+
+static volatile int main_running = 0;
+static DECLARE_WORK(NCR5380_tqueue, (void (*)(void*))NCR5380_main, NULL);
+
+static __inline__ void queue_main(void)
+{
+ if (!main_running) {
+ /* If in interrupt and NCR5380_main() not already running,
+ queue it on the 'immediate' task queue, to be processed
+ immediately after the current interrupt processing has
+ finished. */
+ schedule_work(&NCR5380_tqueue);
+ }
+ /* else: nothing to do: the running NCR5380_main() will pick up
+ any newly queued command. */
+}
+
+
+static inline void NCR5380_all_init (void)
+{
+ static int done = 0;
+ if (!done) {
+ INI_PRINTK("scsi : NCR5380_all_init()\n");
+ done = 1;
+ }
+}
+
+
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ * options that were selected.
+ *
+ * Inputs : instance, pointer to this instance. Unused.
+ */
+
+static void __init NCR5380_print_options (struct Scsi_Host *instance)
+{
+ printk(" generic options"
+#ifdef AUTOSENSE
+ " AUTOSENSE"
+#endif
+#ifdef REAL_DMA
+ " REAL DMA"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef SUPPORT_TAGS
+ " SCSI-2 TAGGED QUEUING"
+#endif
+ );
+ printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ * NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance)
+{
+ char *pr_bfr;
+ char *start;
+ int len;
+
+ NCR_PRINT(NDEBUG_ANY);
+ NCR_PRINT_PHASE(NDEBUG_ANY);
+
+ pr_bfr = (char *) __get_free_page(GFP_ATOMIC);
+ if (!pr_bfr) {
+ printk("NCR5380_print_status: no memory for print buffer\n");
+ return;
+ }
+ len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0);
+ pr_bfr[len] = 0;
+ printk("\n%s\n", pr_bfr);
+ free_page((unsigned long) pr_bfr);
+}
+
+
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+*/
+
+#undef SPRINTF
+#define SPRINTF(fmt,args...) \
+ do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
+ pos += sprintf(pos, fmt , ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
+
+static
+int NCR5380_proc_info (struct Scsi_Host *instance, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ char *pos = buffer;
+ struct NCR5380_hostdata *hostdata;
+ Scsi_Cmnd *ptr;
+ unsigned long flags;
+ off_t begin = 0;
+#define check_offset() \
+ do { \
+ if (pos - buffer < offset - begin) { \
+ begin += pos - buffer; \
+ pos = buffer; \
+ } \
+ } while (0)
+
+ hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+
+ if (inout) { /* Has data been written to the file ? */
+ return(-ENOSYS); /* Currently this is a no-op */
+ }
+ SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
+ check_offset();
+ local_irq_save(flags);
+ SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't");
+ check_offset();
+ if (!hostdata->connected)
+ SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
+ else
+ pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
+ pos, buffer, length);
+ SPRINTF("scsi%d: issue_queue\n", HOSTNO);
+ check_offset();
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+ check_offset();
+ }
+
+ SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
+ check_offset();
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = NEXT(ptr)) {
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+ check_offset();
+ }
+
+ local_irq_restore(flags);
+ *start = buffer + (offset - begin);
+ if (pos - buffer < offset - begin)
+ return 0;
+ else if (pos - buffer - (offset - begin) < length)
+ return pos - buffer - (offset - begin);
+ return length;
+}
+
+static char *
+lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
+{
+ int i, s;
+ unsigned char *command;
+ SPRINTF("scsi%d: destination target %d, lun %d\n",
+ H_NO(cmd), cmd->device->id, cmd->device->lun);
+ SPRINTF(" command = ");
+ command = cmd->cmnd;
+ SPRINTF("%2d (0x%02x)", command[0], command[0]);
+ for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ SPRINTF(" %02x", command[i]);
+ SPRINTF("\n");
+ return pos;
+}
+
+
+/*
+ * Function : void NCR5380_init (struct Scsi_Host *instance)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
+ *
+ */
+
+static int NCR5380_init (struct Scsi_Host *instance, int flags)
+{
+ int i;
+ SETUP_HOSTDATA(instance);
+
+ NCR5380_all_init();
+
+ hostdata->aborted = 0;
+ hostdata->id_mask = 1 << instance->this_id;
+ hostdata->id_higher_mask = 0;
+ for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+ if (i > hostdata->id_mask)
+ hostdata->id_higher_mask |= i;
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+#ifdef SUPPORT_TAGS
+ init_tags();
+#endif
+#if defined (REAL_DMA)
+ hostdata->dma_len = 0;
+#endif
+ hostdata->targets_present = 0;
+ hostdata->connected = NULL;
+ hostdata->issue_queue = NULL;
+ hostdata->disconnected_queue = NULL;
+ hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
+
+ if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance;
+ }
+
+
+#ifndef AUTOSENSE
+ if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
+ printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
+ " without AUTOSENSE option, contingent allegiance conditions may\n"
+ " be incorrectly cleared.\n", HOSTNO);
+#endif /* def AUTOSENSE */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
+ *
+ */
+
+static
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+ SETUP_HOSTDATA(cmd->device->host);
+ Scsi_Cmnd *tmp;
+ int oldto;
+ unsigned long flags;
+ extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
+ H_NO(cmd));
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ return 0;
+ }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+
+#ifdef NCR5380_STATS
+# if 0
+ if (!hostdata->connected && !hostdata->issue_queue &&
+ !hostdata->disconnected_queue) {
+ hostdata->timebase = jiffies;
+ }
+# endif
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_write[cmd->device->id] += cmd->request_bufflen;
+ hostdata->pendingw++;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_read[cmd->device->id] += cmd->request_bufflen;
+ hostdata->pendingr++;
+ break;
+ }
+#endif
+
+ /*
+ * We use the host_scribble field as a pointer to the next command
+ * in a queue
+ */
+
+ NEXT(cmd) = NULL;
+ cmd->scsi_done = done;
+
+ cmd->result = 0;
+
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+ local_irq_save(flags);
+ /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
+ * Otherwise a running NCR5380_main may steal the lock.
+ * Lock before actually inserting due to fairness reasons explained in
+ * atari_scsi.c. If we insert first, then it's impossible for this driver
+ * to release the lock.
+ * Stop timer for this command while waiting for the lock, or timeouts
+ * may happen (and they really do), and it's no good if the command doesn't
+ * appear in any of the queues.
+ * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+ * because also a timer int can trigger an abort or reset, which would
+ * alter queues and touch the lock.
+ */
+ if (!IS_A_TT()) {
+ oldto = update_timeout(cmd, 0);
+ falcon_get_lock();
+ update_timeout(cmd, oldto);
+ }
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
+ NEXT(tmp); tmp = NEXT(tmp))
+ ;
+ LIST(cmd, tmp);
+ NEXT(tmp) = cmd;
+ }
+ local_irq_restore(flags);
+
+ QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd),
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+
+ /* If queue_command() is called from an interrupt (real one or bottom
+ * half), we let queue_main() do the job of taking care about main. If it
+ * is already running, this is a no-op, else main will be queued.
+ *
+ * If we're not in an interrupt, we can call NCR5380_main()
+ * unconditionally, because it cannot be already running.
+ */
+ if (in_interrupt() || ((flags >> 8) & 7) >= 6)
+ queue_main();
+ else
+ NCR5380_main(NULL);
+ return 0;
+}
+
+/*
+ * Function : NCR5380_main (void)
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ */
+
+static void NCR5380_main (void *bl)
+{
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *instance = first_instance;
+ struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
+ int done;
+ unsigned long flags;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ * this should prevent any race conditions.
+ *
+ * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+ * because also a timer int can trigger an abort or reset, which can
+ * alter queues and touch the Falcon lock.
+ */
+
+ /* Tell int handlers main() is now already executing. Note that
+ no races are possible here. If an int comes in before
+ 'main_running' is set here, and queues/executes main via the
+ task queue, it doesn't do any harm, just this instance of main
+ won't find any work left to do. */
+ if (main_running)
+ return;
+ main_running = 1;
+
+ local_save_flags(flags);
+ do {
+ local_irq_disable(); /* Freeze request queues */
+ done = 1;
+
+ if (!hostdata->connected) {
+ MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO );
+ /*
+ * Search through the issue_queue for a command destined
+ * for a target that's not busy.
+ */
+#if (NDEBUG & NDEBUG_LISTS)
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
+ tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
+ ;
+ /*printk("%p ", tmp);*/
+ if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {
+
+#if (NDEBUG & NDEBUG_LISTS)
+ if (prev != tmp)
+ printk("MAIN tmp=%p target=%d busy=%d lun=%d\n",
+ tmp, tmp->device->id, hostdata->busy[tmp->device->id],
+ tmp->device->lun);
+#endif
+ /* When we find one, remove it from the issue queue. */
+ /* ++guenther: possible race with Falcon locking */
+ if (
+#ifdef SUPPORT_TAGS
+ !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
+#else
+ !(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))
+#endif
+ ) {
+ /* ++guenther: just to be sure, this must be atomic */
+ local_irq_disable();
+ if (prev) {
+ REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+ NEXT(prev) = NEXT(tmp);
+ } else {
+ REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
+ hostdata->issue_queue = NEXT(tmp);
+ }
+ NEXT(tmp) = NULL;
+ falcon_dont_release++;
+
+ /* reenable interrupts after finding one */
+ local_irq_restore(flags);
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+ MAIN_PRINTK("scsi%d: main(): command for target %d "
+ "lun %d removed from issue_queue\n",
+ HOSTNO, tmp->device->id, tmp->device->lun);
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+ /* ++roman: ...and the standard also requires that
+ * REQUEST SENSE command are untagged.
+ */
+
+#ifdef SUPPORT_TAGS
+ cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );
+#endif
+ if (!NCR5380_select(instance, tmp,
+ (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
+ TAG_NEXT)) {
+ falcon_dont_release--;
+ /* release if target did not response! */
+ falcon_release_lock_if_possible( hostdata );
+ break;
+ } else {
+ local_irq_disable();
+ LIST(tmp, hostdata->issue_queue);
+ NEXT(tmp) = hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( tmp );
+#endif
+ falcon_dont_release--;
+ local_irq_restore(flags);
+ MAIN_PRINTK("scsi%d: main(): select() failed, "
+ "returned to issue_queue\n", HOSTNO);
+ if (hostdata->connected)
+ break;
+ }
+ } /* if target/lun/target queue is not busy */
+ } /* for issue_queue */
+ } /* if (!hostdata->connected) */
+
+ if (hostdata->connected
+#ifdef REAL_DMA
+ && !hostdata->dma_len
+#endif
+ ) {
+ local_irq_restore(flags);
+ MAIN_PRINTK("scsi%d: main: performing information transfer\n",
+ HOSTNO);
+ NCR5380_information_transfer(instance);
+ MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO);
+ done = 0;
+ }
+ } while (!done);
+
+ /* Better allow ints _after_ 'main_running' has been cleared, else
+ an interrupt could believe we'll pick up the work it left for
+ us, but we won't see it anymore here... */
+ main_running = 0;
+ local_irq_restore(flags);
+}
+
+
+#ifdef REAL_DMA
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : Called by interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would finish the DMA transfer).
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+static void NCR5380_dma_complete( struct Scsi_Host *instance )
+{
+ SETUP_HOSTDATA(instance);
+ int transfered, saved_data = 0, overrun = 0, cnt, toPIO;
+ unsigned char **data, p;
+ volatile int *count;
+
+ if (!hostdata->connected) {
+ printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
+ "no connected cmd\n", HOSTNO);
+ return;
+ }
+
+ if (atari_read_overruns) {
+ p = hostdata->connected->SCp.phase;
+ if (p & SR_IO) {
+ udelay(10);
+ if ((((NCR5380_read(BUS_AND_STATUS_REG)) &
+ (BASR_PHASE_MATCH|BASR_ACK)) ==
+ (BASR_PHASE_MATCH|BASR_ACK))) {
+ saved_data = NCR5380_read(INPUT_DATA_REG);
+ overrun = 1;
+ DMA_PRINTK("scsi%d: read overrun handled\n", HOSTNO);
+ }
+ }
+ }
+
+ DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
+ HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
+ NCR5380_read(STATUS_REG));
+
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
+ hostdata->dma_len = 0;
+
+ data = (unsigned char **) &(hostdata->connected->SCp.ptr);
+ count = &(hostdata->connected->SCp.this_residual);
+ *data += transfered;
+ *count -= transfered;
+
+ if (atari_read_overruns) {
+ if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) {
+ cnt = toPIO = atari_read_overruns;
+ if (overrun) {
+ DMA_PRINTK("Got an input overrun, using saved byte\n");
+ *(*data)++ = saved_data;
+ (*count)--;
+ cnt--;
+ toPIO--;
+ }
+ DMA_PRINTK("Doing %d-byte PIO to 0x%08lx\n", cnt, (long)*data);
+ NCR5380_transfer_pio(instance, &p, &cnt, data);
+ *count -= toPIO - cnt;
+ }
+ }
+}
+#endif /* REAL_DMA */
+
+
+/*
+ * Function : void NCR5380_intr (int irq)
+ *
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static irqreturn_t NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *instance = first_instance;
+ int done = 1, handled = 0;
+ unsigned char basr;
+
+ INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO);
+
+ /* Look for pending interrupts */
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr);
+ /* dispatch to appropriate routine if found and done=0 */
+ if (basr & BASR_IRQ) {
+ NCR_PRINT(NDEBUG_INTR);
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
+ done = 0;
+ ENABLE_IRQ();
+ INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO);
+ NCR5380_reselect(instance);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else if (basr & BASR_PARITY_ERROR) {
+ INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+ INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO);
+ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else {
+ /*
+ * The rest of the interrupt conditions can occur only during a
+ * DMA transfer
+ */
+
+#if defined(REAL_DMA)
+ /*
+ * We should only get PHASE MISMATCH and EOP interrupts if we have
+ * DMA enabled, so do a sanity check based on the current setting
+ * of the MODE register.
+ */
+
+ if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
+ ((basr & BASR_END_DMA_TRANSFER) ||
+ !(basr & BASR_PHASE_MATCH))) {
+
+ INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
+ NCR5380_dma_complete( instance );
+ done = 0;
+ ENABLE_IRQ();
+ } else
+#endif /* REAL_DMA */
+ {
+/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
+ if (basr & BASR_PHASE_MATCH)
+ printk(KERN_NOTICE "scsi%d: unknown interrupt, "
+ "BASR 0x%x, MR 0x%x, SR 0x%x\n",
+ HOSTNO, basr, NCR5380_read(MODE_REG),
+ NCR5380_read(STATUS_REG));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ } /* if !(SELECTION || PARITY) */
+ handled = 1;
+ } /* BASR & IRQ */
+ else {
+ printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
+ "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
+ NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+
+ if (!done) {
+ INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO);
+ /* Put a call to NCR5380_main() on the queue... */
+ queue_main();
+ }
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef NCR5380_STATS
+static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
+{
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_write[cmd->device->id] += cmd->request_bufflen;*/
+ hostdata->pendingw--;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_read[cmd->device->id] += cmd->request_bufflen;*/
+ hostdata->pendingr--;
+ break;
+ }
+}
+#endif
+
+/*
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ * int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ * including ARBITRATION, SELECTION, and initial message out for
+ * IDENTIFY and queue messages.
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this
+ * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
+ * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
+ * the command that is presently connected.
+ *
+ * Returns : -1 if selection could not execute for some reason,
+ * 0 if selection succeeded or failed because the target
+ * did not respond.
+ *
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
+ *
+ * If successful : I_T_L or I_T_L_Q nexus will be established,
+ * instance->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
+ *
+ * If failed (no target) : cmd->scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned char tmp[3], phase;
+ unsigned char *data;
+ int len;
+ unsigned long timeout;
+ unsigned long flags;
+
+ hostdata->restart_select = 0;
+ NCR_PRINT(NDEBUG_ARBITRATION);
+ ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO,
+ instance->this_id);
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION.
+ */
+
+ local_irq_save(flags);
+ if (hostdata->connected) {
+ local_irq_restore(flags);
+ return -1;
+ }
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+ /*
+ * Start arbitration.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+ local_irq_restore(flags);
+
+ /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && time_before(jiffies, timeout) && !hostdata->connected)
+ ;
+ if (time_after_eq(jiffies, timeout))
+ {
+ printk("scsi : arbitration timeout at %d\n", __LINE__);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && !hostdata->connected);
+#endif
+
+ ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO);
+
+ if (hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ return -1;
+ }
+ /*
+ * The arbitration delay is 2.2us, but this is a minimum and there is
+ * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+ * the integral nature of udelay().
+ *
+ */
+
+ udelay(3);
+
+ /* Check for lost arbitration */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+ (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
+ HOSTNO);
+ return -1;
+ }
+
+ /* after/during arbitration, BSY should be asserted.
+ IBM DPES-31080 Version S31Q works now */
+ /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL |
+ ICR_ASSERT_BSY ) ;
+
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
+ HOSTNO);
+ return -1;
+ }
+
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
+ * a minimum so we'll udelay ceil(1.2)
+ */
+
+#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
+ /* ++roman: But some targets (see above :-) seem to need a bit more... */
+ udelay(15);
+#else
+ udelay(2);
+#endif
+
+ if (hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return -1;
+ }
+
+ ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO);
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
+ * the host and target ID's on the SCSI bus.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id)));
+
+ /*
+ * Raise ATN while SEL is true before BSY goes false from arbitration,
+ * since this is the only way to guarantee that we'll get a MESSAGE OUT
+ * phase immediately after selection.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+ NCR5380_write(MODE_REG, MR_BASE);
+
+ /*
+ * Reselect interrupts must be turned off prior to the dropping of BSY,
+ * otherwise we will trigger an interrupt.
+ */
+
+ if (hostdata->connected) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return -1;
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /*
+ * The initiator shall then wait at least two deskew delays and release
+ * the BSY signal.
+ */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+
+ /* Reset BSY */
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+ /*
+ * Something weird happens when we cease to drive BSY - looks
+ * like the board/chip is letting us do another read before the
+ * appropriate propagation delay has expired, and we're confusing
+ * a BSY signal from ourselves as the target's response to SELECTION.
+ *
+ * A small delay (the 'C++' frontend breaks the pipeline with an
+ * unnecessary jump, making it work on my 386-33/Trantor T128, the
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
+ * work there.
+ *
+ * wingel suggests that this could be due to failing to wait
+ * one deskew delay.
+ */
+
+ udelay(1);
+
+ SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->device->id);
+
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
+ * selection.
+ */
+
+ timeout = jiffies + 25;
+
+ /*
+ * XXX very interesting - we're seeing a bounce where the BSY we
+ * asserted is being reflected / still asserted (propagation delay?)
+ * and it's detecting as true. Sigh.
+ */
+
+#if 0
+ /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
+ * IO while SEL is true. But again, there are some disks out the in the
+ * world that do that nevertheless. (Somebody claimed that this announces
+ * reselection capability of the target.) So we better skip that test and
+ * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
+ */
+
+ while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) &
+ (SR_BSY | SR_IO)));
+
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_reselect(instance);
+ printk (KERN_ERR "scsi%d: reselection after won arbitration?\n",
+ HOSTNO);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+#else
+ while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+#endif
+
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
+ * change the DATA BUS. -wingel
+ */
+
+ udelay(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ if (hostdata->targets_present & (1 << cmd->device->id)) {
+ printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
+ if (hostdata->restart_select)
+ printk(KERN_NOTICE "\trestart select\n");
+ NCR_PRINT(NDEBUG_ANY);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ cmd->result = DID_BAD_TARGET << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return 0;
+ }
+
+ hostdata->targets_present |= (1 << cmd->device->id);
+
+ /*
+ * Since we followed the SCSI spec, and raised ATN while SEL
+ * was true but before BSY was false during selection, the information
+ * transfer phase should be a MESSAGE OUT phase so that we can send the
+ * IDENTIFY message.
+ *
+ * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+ * message (2 bytes) with a tag ID that we increment with every command
+ * until it wraps back to 0.
+ *
+ * XXX - it turns out that there are some broken SCSI-II devices,
+ * which claim to support tagged queuing but fail when more than
+ * some number of commands are issued at once.
+ */
+
+ /* Wait for start of REQ/ACK handshake */
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+ SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
+ HOSTNO, cmd->device->id);
+ tmp[0] = IDENTIFY(1, cmd->device->lun);
+
+#ifdef SUPPORT_TAGS
+ if (cmd->tag != TAG_NONE) {
+ tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
+ tmp[2] = cmd->tag;
+ len = 3;
+ } else
+ len = 1;
+#else
+ len = 1;
+ cmd->tag=0;
+#endif /* SUPPORT_TAGS */
+
+ /* Send message(s) */
+ data = tmp;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO);
+ /* XXX need to handle errors here */
+ hostdata->connected = cmd;
+#ifndef SUPPORT_TAGS
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+#endif
+
+ initialize_SCp(cmd);
+
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes are transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio( struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
+ * phase specified in the appropriate bits of the TARGET COMMAND
+ * REGISTER match the STATUS REGISTER
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+ do {
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
+ */
+ while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+ HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO);
+
+ /* Check for phase mismatch */
+ if ((tmp & PHASE_MASK) != p) {
+ PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO);
+ NCR_PRINT_PHASE(NDEBUG_PIO);
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to / from memory */
+ if (!(p & SR_IO))
+ NCR5380_write(OUTPUT_DATA_REG, *d);
+ else
+ *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+ ++d;
+
+ /*
+ * The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ */
+
+ if (!(p & SR_IO)) {
+ if (!((p & SR_MSG) && c > 1)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA);
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ } else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ }
+ } else {
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ }
+
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+ HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO);
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking :
+ * 1. We were in MSGOUT phase, and we are on the last byte of the
+ * message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in a MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear and the target may proceed as normal.
+ */
+ if (!(p == PHASE_MSGIN && c == 1)) {
+ if (p == PHASE_MSGOUT && c > 1)
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ else
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+ } while (--c);
+
+ PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c);
+
+ *count = c;
+ *data = d;
+ tmp = NCR5380_read(STATUS_REG);
+ /* The phase read from the bus is valid if either REQ is (already)
+ * asserted or if ACK hasn't been released yet. The latter is the case if
+ * we're in MSGIN and all wanted bytes have been received. */
+ if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
+ *phase = tmp & PHASE_MASK;
+ else
+ *phase = PHASE_UNKNOWN;
+
+ if (!c || (*phase == p))
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ *
+ * Purpose : abort the currently established nexus. Should only be
+ * called from a routine which can drop into a
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host)
+{
+ unsigned char tmp, *msgptr, phase;
+ int len;
+
+ /* Request message out phase */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
+ * other phase and will have to source/sink data.
+ *
+ * We really don't care what value was on the bus or what value
+ * the target sees, so we just handshake.
+ */
+
+ while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ }
+
+ tmp = ABORT;
+ msgptr = &tmp;
+ len = 1;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+ /*
+ * If we got here, and the command completed successfully,
+ * we're about to go into bus free state.
+ */
+
+ return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ * or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma( struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ SETUP_HOSTDATA(instance);
+ register int c = *count;
+ register unsigned char p = *phase;
+ register unsigned char *d = *data;
+ unsigned char tmp;
+ unsigned long flags;
+
+ if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
+ *phase = tmp;
+ return -1;
+ }
+
+ if (atari_read_overruns && (p & SR_IO)) {
+ c -= atari_read_overruns;
+ }
+
+ DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
+ HOSTNO, (p & SR_IO) ? "reading" : "writing",
+ c, (p & SR_IO) ? "to" : "from", d);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+#ifdef REAL_DMA
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
+#endif /* def REAL_DMA */
+
+ if (IS_A_TT()) {
+ /* On the Medusa, it is a must to initialize the DMA before
+ * starting the NCR. This is also the cleaner way for the TT.
+ */
+ local_irq_save(flags);
+ hostdata->dma_len = (p & SR_IO) ?
+ NCR5380_dma_read_setup(instance, d, c) :
+ NCR5380_dma_write_setup(instance, d, c);
+ local_irq_restore(flags);
+ }
+
+ if (p & SR_IO)
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
+ NCR5380_write(START_DMA_SEND_REG, 0);
+ }
+
+ if (!IS_A_TT()) {
+ /* On the Falcon, the DMA setup must be done after the last */
+ /* NCR access, else the DMA setup gets trashed!
+ */
+ local_irq_save(flags);
+ hostdata->dma_len = (p & SR_IO) ?
+ NCR5380_dma_read_setup(instance, d, c) :
+ NCR5380_dma_write_setup(instance, d, c);
+ local_irq_restore(flags);
+ }
+ return 0;
+}
+#endif /* defined(REAL_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here
+ * to recover from an unexpected bus free condition.
+ */
+
+static void NCR5380_information_transfer (struct Scsi_Host *instance)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned long flags;
+ unsigned char msgout = NOP;
+ int sink = 0;
+ int len;
+#if defined(REAL_DMA)
+ int transfersize;
+#endif
+ unsigned char *data;
+ unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+ Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+
+ while (1) {
+ tmp = NCR5380_read(STATUS_REG);
+ /* We only have a valid SCSI phase when REQ is asserted */
+ if (tmp & SR_REQ) {
+ phase = (tmp & PHASE_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+ NCR_PRINT_PHASE(NDEBUG_INFORMATION);
+ }
+
+ if (sink && (phase != PHASE_MSGOUT)) {
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 0;
+ continue;
+ }
+
+ switch (phase) {
+ case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+ printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
+ "aborted\n", HOSTNO);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ return;
+#endif
+ case PHASE_DATAIN:
+ /*
+ * If there is no room left in the current buffer in the
+ * scatter-gather list, move onto the next one.
+ */
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page)+
+ cmd->SCp.buffer->offset;
+ /* ++roman: Try to merge some scatter-buffers if
+ * they are at contiguous physical addresses.
+ */
+ merge_contiguous_buffers( cmd );
+ INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
+ HOSTNO, cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual);
+ }
+
+ /*
+ * The preferred transfer method is going to be
+ * PSEUDO-DMA for systems that are strictly PIO,
+ * since we can let the hardware do the handshaking.
+ *
+ * For this to work, we need to know the transfersize
+ * ahead of time, since the pseudo-DMA code will sit
+ * in an unconditional loop.
+ */
+
+/* ++roman: I suggest, this should be
+ * #if def(REAL_DMA)
+ * instead of leaving REAL_DMA out.
+ */
+
+#if defined(REAL_DMA)
+ if (!cmd->device->borken &&
+ (transfersize = NCR5380_dma_xfer_len(instance,cmd,phase)) > 31) {
+ len = transfersize;
+ cmd->SCp.phase = phase;
+ if (NCR5380_transfer_dma(instance, &phase,
+ &len, (unsigned char **) &cmd->SCp.ptr)) {
+ /*
+ * If the watchdog timer fires, all future
+ * accesses to this device will use the
+ * polled-IO. */
+ printk(KERN_NOTICE "scsi%d: switching target %d "
+ "lun %d to slow handshake\n", HOSTNO,
+ cmd->device->id, cmd->device->lun);
+ cmd->device->borken = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ /* XXX - need to source or sink data here, as appropriate */
+ } else {
+#ifdef REAL_DMA
+ /* ++roman: When using real DMA,
+ * information_transfer() should return after
+ * starting DMA since it has nothing more to
+ * do.
+ */
+ return;
+#else
+ cmd->SCp.this_residual -= transfersize - len;
+#endif
+ }
+ } else
+#endif /* defined(REAL_DMA) */
+ NCR5380_transfer_pio(instance, &phase,
+ (int *) &cmd->SCp.this_residual, (unsigned char **)
+ &cmd->SCp.ptr);
+ break;
+ case PHASE_MSGIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Message = tmp;
+
+ switch (tmp) {
+ /*
+ * Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ *
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands.
+ */
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ LNK_PRINTK("scsi%d: target %d lun %d linked command "
+ "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Sanity check : A linked command should only terminate
+ * with one of these messages if there are more linked
+ * commands available.
+ */
+
+ if (!cmd->next_link) {
+ printk(KERN_NOTICE "scsi%d: target %d lun %d "
+ "linked command complete, no next_link\n",
+ HOSTNO, cmd->device->id, cmd->device->lun);
+ sink = 1;
+ do_abort (instance);
+ return;
+ }
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process; copy it
+ * and don't free it! */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ LNK_PRINTK("scsi%d: target %d lun %d linked request "
+ "done, calling scsi_done().\n",
+ HOSTNO, cmd->device->id, cmd->device->lun);
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+#endif /* def LINKED */
+ case ABORT:
+ case COMMAND_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* ++guenther: possible race with Falcon locking */
+ falcon_dont_release++;
+ hostdata->connected = NULL;
+ QU_PRINTK("scsi%d: command for target %d, lun %d "
+ "completed\n", HOSTNO, cmd->device->id, cmd->device->lun);
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+ if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
+ /* Turn a QUEUE FULL status into BUSY, I think the
+ * mid level cannot handle QUEUE FULL :-( (The
+ * command is retried after BUSY). Also update our
+ * queue size to the number of currently issued
+ * commands now.
+ */
+ /* ++Andreas: the mid level code knows about
+ QUEUE_FULL now. */
+ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+ TAG_PRINTK("scsi%d: target %d lun %d returned "
+ "QUEUE_FULL after %d commands\n",
+ HOSTNO, cmd->device->id, cmd->device->lun,
+ ta->nr_allocated);
+ if (ta->queue_size > ta->nr_allocated)
+ ta->nr_allocated = ta->queue_size;
+ }
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+
+ /*
+ * I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ *
+ * If it was a REQUEST SENSE command, we need some way to
+ * differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the
+ * result code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed
+ */
+
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (status_byte(cmd->SCp.Status) != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+
+#ifdef AUTOSENSE
+ if ((cmd->cmnd[0] != REQUEST_SENSE) &&
+ (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
+ ASEN_PRINTK("scsi%d: performing request sense\n",
+ HOSTNO);
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+
+ cmd->use_sg = 0;
+ /* this is initialized from initialize_SCp
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ */
+ cmd->request_buffer = (char *) cmd->sense_buffer;
+ cmd->request_bufflen = sizeof(cmd->sense_buffer);
+
+ local_irq_save(flags);
+ LIST(cmd,hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ local_irq_restore(flags);
+ QU_PRINTK("scsi%d: REQUEST SENSE added to head of "
+ "issue queue\n", H_NO(cmd));
+ } else
+#endif /* def AUTOSENSE */
+ {
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+
+ falcon_dont_release--;
+ /* ++roman: For Falcon SCSI, release the lock on the
+ * ST-DMA here if no other commands are waiting on the
+ * disconnected queue.
+ */
+ falcon_release_lock_if_possible( hostdata );
+ return;
+ case MESSAGE_REJECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ switch (hostdata->last_message) {
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ /* The target obviously doesn't support tagged
+ * queuing, even though it announced this ability in
+ * its INQUIRY data ?!? (maybe only this LUN?) Ok,
+ * clear 'tagged_supported' and lock the LUN, since
+ * the command is treated as untagged further on.
+ */
+ cmd->device->tagged_supported = 0;
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ cmd->tag = TAG_NONE;
+ TAG_PRINTK("scsi%d: target %d lun %d rejected "
+ "QUEUE_TAG message; tagged queuing "
+ "disabled\n",
+ HOSTNO, cmd->device->id, cmd->device->lun);
+ break;
+ }
+ break;
+ case DISCONNECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ local_irq_save(flags);
+ cmd->device->disconnect = 1;
+ LIST(cmd,hostdata->disconnected_queue);
+ NEXT(cmd) = hostdata->disconnected_queue;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = cmd;
+ local_irq_restore(flags);
+ QU_PRINTK("scsi%d: command for target %d lun %d was "
+ "moved from connected to the "
+ "disconnected_queue\n", HOSTNO,
+ cmd->device->id, cmd->device->lun);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /* Wait for bus free to avoid nasty timeouts */
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+ return;
+ /*
+ * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible.
+ */
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ break;
+ case EXTENDED_MESSAGE:
+/*
+ * Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't
+ * include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.
+ */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ /* Accept first byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO);
+
+ len = 2;
+ data = extended_msg + 1;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO,
+ (int)extended_msg[1], (int)extended_msg[2]);
+
+ if (!len && extended_msg[1] <=
+ (sizeof (extended_msg) - 1)) {
+ /* Accept third byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = extended_msg[1] - 1;
+ data = extended_msg + 3;
+ phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ EXT_PRINTK("scsi%d: message received, residual %d\n",
+ HOSTNO, len);
+
+ switch (extended_msg[2]) {
+ case EXTENDED_SDTR:
+ case EXTENDED_WDTR:
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ tmp = 0;
+ }
+ } else if (len) {
+ printk(KERN_NOTICE "scsi%d: error receiving "
+ "extended message\n", HOSTNO);
+ tmp = 0;
+ } else {
+ printk(KERN_NOTICE "scsi%d: extended message "
+ "code %02x length %d is too long\n",
+ HOSTNO, extended_msg[2], extended_msg[1]);
+ tmp = 0;
+ }
+ /* Fall through to reject message */
+
+ /*
+ * If we get something weird that we aren't expecting,
+ * reject it.
+ */
+ default:
+ if (!tmp) {
+ printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
+ print_msg (extended_msg);
+ printk("\n");
+ } else if (tmp != EXTENDED_MESSAGE)
+ printk(KERN_DEBUG "scsi%d: rejecting unknown "
+ "message %02x from target %d, lun %d\n",
+ HOSTNO, tmp, cmd->device->id, cmd->device->lun);
+ else
+ printk(KERN_DEBUG "scsi%d: rejecting unknown "
+ "extended message "
+ "code %02x, length %d from target %d, lun %d\n",
+ HOSTNO, extended_msg[1], extended_msg[0],
+ cmd->device->id, cmd->device->lun);
+
+
+ msgout = MESSAGE_REJECT;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ break;
+ } /* switch (tmp) */
+ break;
+ case PHASE_MSGOUT:
+ len = 1;
+ data = &msgout;
+ hostdata->last_message = msgout;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (msgout == ABORT) {
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ hostdata->connected = NULL;
+ cmd->result = DID_ERROR << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ falcon_release_lock_if_possible( hostdata );
+ return;
+ }
+ msgout = NOP;
+ break;
+ case PHASE_CMDOUT:
+ len = cmd->cmd_len;
+ data = cmd->cmnd;
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
+ */
+ NCR5380_transfer_pio(instance, &phase, &len,
+ &data);
+ break;
+ case PHASE_STATIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Status = tmp;
+ break;
+ default:
+ printk("scsi%d: unknown phase\n", HOSTNO);
+ NCR_PRINT(NDEBUG_ANY);
+ } /* switch(phase) */
+ } /* if (tmp * SR_REQ) */
+ } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned char target_mask;
+ unsigned char lun, phase;
+ int len;
+#ifdef SUPPORT_TAGS
+ unsigned char tag;
+#endif
+ unsigned char msg[3];
+ unsigned char *data;
+ Scsi_Cmnd *tmp = NULL, *prev;
+/* unsigned long flags; */
+
+ /*
+ * Disable arbitration, etc. since the host adapter obviously
+ * lost, and tell an interrupted NCR5380_select() to restart.
+ */
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ hostdata->restart_select = 1;
+
+ target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+ RSL_PRINTK("scsi%d: reselect\n", HOSTNO);
+
+ /*
+ * At this point, we have detected that our SCSI ID is on the bus,
+ * SEL is true and BSY was false for at least one bus settle delay
+ * (400 ns).
+ *
+ * We must assert BSY ourselves, until the target drops the SEL
+ * signal.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+
+ while (NCR5380_read(STATUS_REG) & SR_SEL);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * Wait for target to go into MSGIN.
+ */
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+ len = 1;
+ data = msg;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+ if (!(msg[0] & 0x80)) {
+ printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
+ print_msg(msg);
+ do_abort(instance);
+ return;
+ }
+ lun = (msg[0] & 0x07);
+
+#ifdef SUPPORT_TAGS
+ /* If the phase is still MSGIN, the target wants to send some more
+ * messages. In case it supports tagged queuing, this is probably a
+ * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
+ */
+ tag = TAG_NONE;
+ if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
+ /* Accept previous IDENTIFY message by clearing ACK */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ len = 2;
+ data = msg+1;
+ if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
+ msg[1] == SIMPLE_QUEUE_TAG)
+ tag = msg[2];
+ TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at "
+ "reselection\n", HOSTNO, target_mask, lun, tag);
+ }
+#endif
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = NEXT(tmp) ) {
+ if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
+#ifdef SUPPORT_TAGS
+ && (tag == tmp->tag)
+#endif
+ ) {
+ /* ++guenther: prevent race with falcon_release_lock */
+ falcon_dont_release++;
+ if (prev) {
+ REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+ NEXT(prev) = NEXT(tmp);
+ } else {
+ REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
+ hostdata->disconnected_queue = NEXT(tmp);
+ }
+ NEXT(tmp) = NULL;
+ break;
+ }
+ }
+
+ if (!tmp) {
+ printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
+#ifdef SUPPORT_TAGS
+ "tag %d "
+#endif
+ "not in disconnected_queue.\n",
+ HOSTNO, target_mask, lun
+#ifdef SUPPORT_TAGS
+ , tag
+#endif
+ );
+ /*
+ * Since we have an established nexus that we can't do anything
+ * with, we must abort it.
+ */
+ do_abort(instance);
+ return;
+ }
+
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ hostdata->connected = tmp;
+ RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+ HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag);
+ falcon_dont_release--;
+}
+
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
+ * used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
+ * a problem, we could implement longjmp() / setjmp(), setjmp()
+ * called where the loop started in NCR5380_main().
+ */
+
+static
+int NCR5380_abort (Scsi_Cmnd *cmd)
+{
+ struct Scsi_Host *instance = cmd->device->host;
+ SETUP_HOSTDATA(instance);
+ Scsi_Cmnd *tmp, **prev;
+ unsigned long flags;
+
+ printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ local_irq_save(flags);
+
+ if (!IS_A_TT() && !falcon_got_lock)
+ printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_abort\n",
+ HOSTNO);
+
+ ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
+ NCR5380_read(BUS_AND_STATUS_REG),
+ NCR5380_read(STATUS_REG));
+
+#if 1
+/*
+ * Case 1 : If the command is the currently executing command,
+ * we'll set the aborted flag and return control so that
+ * information transfer routine can exit cleanly.
+ */
+
+ if (hostdata->connected == cmd) {
+
+ ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
+/*
+ * Since we can't change phases until we've completed the current
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/*
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */
+
+ if (do_abort(instance) == 0) {
+ hostdata->aborted = 1;
+ hostdata->connected = NULL;
+ cmd->result = DID_ABORT << 16;
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ local_irq_restore(flags);
+ cmd->scsi_done(cmd);
+ falcon_release_lock_if_possible( hostdata );
+ return SCSI_ABORT_SUCCESS;
+ } else {
+/* local_irq_restore(flags); */
+ printk("scsi%d: abort of connected command failed!\n", HOSTNO);
+ return SCSI_ABORT_ERROR;
+ }
+ }
+#endif
+
+/*
+ * Case 2 : If the command hasn't been issued yet, we simply remove it
+ * from the issue queue.
+ */
+ for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, NEXT(tmp));
+ (*prev) = NEXT(tmp);
+ NEXT(tmp) = NULL;
+ tmp->result = DID_ABORT << 16;
+ local_irq_restore(flags);
+ ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n",
+ HOSTNO);
+ /* Tagged queuing note: no tag to free here, hasn't been assigned
+ * yet... */
+ tmp->scsi_done(tmp);
+ falcon_release_lock_if_possible( hostdata );
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected) {
+ local_irq_restore(flags);
+ ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO);
+ return SCSI_ABORT_SNOOZE;
+ }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select(). The easiest way to implement this
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that
+ * device reselected.
+ *
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+ tmp = NEXT(tmp))
+ if (cmd == tmp) {
+ local_irq_restore(flags);
+ ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO);
+
+ if (NCR5380_select (instance, cmd, (int) cmd->tag))
+ return SCSI_ABORT_BUSY;
+
+ ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO);
+
+ do_abort (instance);
+
+ local_irq_save(flags);
+ for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
+ tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+ tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, NEXT(tmp));
+ *prev = NEXT(tmp);
+ NEXT(tmp) = NULL;
+ tmp->result = DID_ABORT << 16;
+ /* We must unlock the tag/LUN immediately here, since the
+ * target goes to BUS FREE and doesn't send us another
+ * message (COMMAND_COMPLETE or the like)
+ */
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( tmp );
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ local_irq_restore(flags);
+ tmp->scsi_done(tmp);
+ falcon_release_lock_if_possible( hostdata );
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ local_irq_restore(flags);
+ printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n"
+ KERN_INFO " before abortion\n", HOSTNO);
+
+/* Maybe it is sufficient just to release the ST-DMA lock... (if
+ * possible at all) At least, we should check if the lock could be
+ * released after the abort, in case it is kept due to some bug.
+ */
+ falcon_release_lock_if_possible( hostdata );
+
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/*
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */
+
+static int NCR5380_bus_reset( Scsi_Cmnd *cmd)
+{
+ SETUP_HOSTDATA(cmd->device->host);
+ int i;
+ unsigned long flags;
+#if 1
+ Scsi_Cmnd *connected, *disconnected_queue;
+#endif
+
+ if (!IS_A_TT() && !falcon_got_lock)
+ printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_reset\n",
+ H_NO(cmd) );
+
+ NCR5380_print_status (cmd->device->host);
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+ udelay (40);
+ /* reset NCR registers */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_write( MODE_REG, MR_BASE );
+ NCR5380_write( TARGET_COMMAND_REG, 0 );
+ NCR5380_write( SELECT_ENABLE_REG, 0 );
+ /* ++roman: reset interrupt condition! otherwise no interrupts don't get
+ * through anymore ... */
+ (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */
+ /* XXX see below XXX */
+
+ /* MSch: old-style reset: actually abort all command processing here */
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; to avoid problems with re-inserting the commands
+ * into the issue_queue (via scsi_done()), the aborted commands are
+ * remembered in local variables first.
+ */
+ local_irq_save(flags);
+ connected = (Scsi_Cmnd *)hostdata->connected;
+ hostdata->connected = NULL;
+ disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
+ hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+ free_all_tags();
+#endif
+ for( i = 0; i < 8; ++i )
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dma_len = 0;
+#endif
+ local_irq_restore(flags);
+
+ /* In order to tell the mid-level code which commands were aborted,
+ * set the command status to DID_RESET and call scsi_done() !!!
+ * This ultimately aborts processing of these commands in the mid-level.
+ */
+
+ if ((cmd = connected)) {
+ ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+ cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+ cmd->scsi_done( cmd );
+ }
+
+ for (i = 0; (cmd = disconnected_queue); ++i) {
+ disconnected_queue = NEXT(cmd);
+ NEXT(cmd) = NULL;
+ cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+ cmd->scsi_done( cmd );
+ }
+ if (i > 0)
+ ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i);
+
+/* The Falcon lock should be released after a reset...
+ */
+/* ++guenther: moved to atari_scsi_reset(), to prevent a race between
+ * unlocking and enabling dma interrupt.
+ */
+/* falcon_release_lock_if_possible( hostdata );*/
+
+ /* since all commands have been explicitly terminated, we need to tell
+ * the midlevel code that the reset was SUCCESSFUL, and there is no
+ * need to 'wake up' the commands by a request_sense
+ */
+ return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+#else /* 1 */
+
+ /* MSch: new-style reset handling: let the mid-level do what it can */
+
+ /* ++guenther: MID-LEVEL IS STILL BROKEN.
+ * Mid-level is supposed to requeue all commands that were active on the
+ * various low-level queues. In fact it does this, but that's not enough
+ * because all these commands are subject to timeout. And if a timeout
+ * happens for any removed command, *_abort() is called but all queues
+ * are now empty. Abort then gives up the falcon lock, which is fatal,
+ * since the mid-level will queue more commands and must have the lock
+ * (it's all happening inside timer interrupt handler!!).
+ * Even worse, abort will return NOT_RUNNING for all those commands not
+ * on any queue, so they won't be retried ...
+ *
+ * Conclusion: either scsi.c disables timeout for all resetted commands
+ * immediately, or we lose! As of linux-2.0.20 it doesn't.
+ */
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; so clear the low-level status here to avoid
+ * conflicts when the mid-level code tries to wake up the affected
+ * commands!
+ */
+
+ if (hostdata->issue_queue)
+ ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
+ if (hostdata->connected)
+ ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+ if (hostdata->disconnected_queue)
+ ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
+
+ local_irq_save(flags);
+ hostdata->issue_queue = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+ free_all_tags();
+#endif
+ for( i = 0; i < 8; ++i )
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dma_len = 0;
+#endif
+ local_irq_restore(flags);
+
+ /* we did no complete reset of all commands, so a wakeup is required */
+ return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET;
+#endif /* 1 */
+}
+
+/* Local Variables: */
+/* tab-width: 8 */
+/* End: */
diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c
new file mode 100644
index 000000000000..7026045527fd
--- /dev/null
+++ b/drivers/scsi/atari_dma_emul.c
@@ -0,0 +1,466 @@
+/*
+ * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades.
+ *
+ * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * This code was written using the Hades TOS source code as a
+ * reference. This source code can be found on the home page
+ * of Medusa Computer Systems.
+ *
+ * Version 0.1, 1997-09-24.
+ *
+ * This code should be considered experimental. It has only been
+ * tested on a Hades with a 68060. It might not work on a Hades
+ * with a 68040. Make backups of your hard drives before using
+ * this code.
+ */
+
+#include <asm/uaccess.h>
+
+#define hades_dma_ctrl (*(unsigned char *) 0xffff8717)
+#define hades_psdm_reg (*(unsigned char *) 0xffff8741)
+
+#define TRANSFER_SIZE 16
+
+struct m68040_frame {
+ unsigned long effaddr; /* effective address */
+ unsigned short ssw; /* special status word */
+ unsigned short wb3s; /* write back 3 status */
+ unsigned short wb2s; /* write back 2 status */
+ unsigned short wb1s; /* write back 1 status */
+ unsigned long faddr; /* fault address */
+ unsigned long wb3a; /* write back 3 address */
+ unsigned long wb3d; /* write back 3 data */
+ unsigned long wb2a; /* write back 2 address */
+ unsigned long wb2d; /* write back 2 data */
+ unsigned long wb1a; /* write back 1 address */
+ unsigned long wb1dpd0; /* write back 1 data/push data 0*/
+ unsigned long pd1; /* push data 1*/
+ unsigned long pd2; /* push data 2*/
+ unsigned long pd3; /* push data 3*/
+};
+
+static void writeback (unsigned short wbs, unsigned long wba,
+ unsigned long wbd, void *old_buserr)
+{
+ mm_segment_t fs = get_fs();
+ static void *save_buserr;
+
+ __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t"
+ "move.l %0,8(%%a0)\n\t"
+ :
+ : "r" (&&bus_error)
+ : "a0" );
+
+ save_buserr = old_buserr;
+
+ set_fs (MAKE_MM_SEG(wbs & WBTM_040));
+
+ switch (wbs & WBSIZ_040) {
+ case BA_SIZE_BYTE:
+ put_user (wbd & 0xff, (char *)wba);
+ break;
+ case BA_SIZE_WORD:
+ put_user (wbd & 0xffff, (short *)wba);
+ break;
+ case BA_SIZE_LONG:
+ put_user (wbd, (int *)wba);
+ break;
+ }
+
+ set_fs (fs);
+ return;
+
+bus_error:
+ __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t"
+ "bcs.s .jump_old\n\t"
+ "cmp.l %1,2(%%sp)\n\t"
+ "bls.s .restore_old\n"
+ ".jump_old:\n\t"
+ "move.l %2,-(%%sp)\n\t"
+ "rts\n"
+ ".restore_old:\n\t"
+ "move.l %%a0,-(%%sp)\n\t"
+ "movec.l %%vbr,%%a0\n\t"
+ "move.l %2,8(%%a0)\n\t"
+ "move.l (%%sp)+,%%a0\n\t"
+ "rte\n\t"
+ :
+ : "i" (writeback), "i" (&&bus_error),
+ "m" (save_buserr) );
+}
+
+/*
+ * static inline void set_restdata_reg(unsigned char *cur_addr)
+ *
+ * Set the rest data register if necessary.
+ */
+
+static inline void set_restdata_reg(unsigned char *cur_addr)
+{
+ if (((long) cur_addr & ~3) != 0)
+ tt_scsi_dma.dma_restdata =
+ *((unsigned long *) ((long) cur_addr & ~3));
+}
+
+/*
+ * void hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp)
+ *
+ * This code emulates TT SCSI DMA on the Hades.
+ *
+ * Note the following:
+ *
+ * 1. When there is no byte available to read from the SCSI bus, or
+ * when a byte cannot yet bet written to the SCSI bus, a bus
+ * error occurs when reading or writing the pseudo DMA data
+ * register (hades_psdm_reg). We have to catch this bus error
+ * and try again to read or write the byte. If after several tries
+ * we still get a bus error, the interrupt handler is left. When
+ * the byte can be read or written, the interrupt handler is
+ * called again.
+ *
+ * 2. The SCSI interrupt must be disabled in this interrupt handler.
+ *
+ * 3. If we set the EOP signal, the SCSI controller still expects one
+ * byte to be read or written. Therefore the last byte is transferred
+ * separately, after setting the EOP signal.
+ *
+ * 4. When this function is left, the address pointer (start_addr) is
+ * converted to a physical address. Because it points one byte
+ * further than the last transferred byte, it can point outside the
+ * current page. If virt_to_phys() is called with this address we
+ * might get an access error. Therefore virt_to_phys() is called with
+ * start_addr - 1 if the count has reached zero. The result is
+ * increased with one.
+ */
+
+static irqreturn_t hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned long dma_base;
+ register unsigned long dma_cnt asm ("d3");
+ static long save_buserr;
+ register unsigned long save_sp asm ("d4");
+ register int tries asm ("d5");
+ register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4");
+ register unsigned char *eff_addr;
+ register unsigned char *psdm_reg;
+ unsigned long rem;
+
+ atari_disable_irq(IRQ_TT_MFP_SCSI);
+
+ /*
+ * Read the dma address and count registers.
+ */
+
+ dma_base = SCSI_DMA_READ_P(dma_addr);
+ dma_cnt = SCSI_DMA_READ_P(dma_cnt);
+
+ /*
+ * Check if DMA is still enabled.
+ */
+
+ if ((tt_scsi_dma.dma_ctrl & 2) == 0)
+ {
+ atari_enable_irq(IRQ_TT_MFP_SCSI);
+ return IRQ_HANDLED;
+ }
+
+ if (dma_cnt == 0)
+ {
+ printk(KERN_NOTICE "DMA emulation: count is zero.\n");
+ tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */
+ atari_enable_irq(IRQ_TT_MFP_SCSI);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Install new bus error routine.
+ */
+
+ __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t"
+ "move.l 8(%%a0),%0\n\t"
+ "move.l %1,8(%%a0)\n\t"
+ : "=&r" (save_buserr)
+ : "r" (&&scsi_bus_error)
+ : "a0" );
+
+ hades_dma_ctrl &= 0xfc; /* Bus error and EOP off. */
+
+ /*
+ * Save the stack pointer.
+ */
+
+ __asm__ __volatile__ ("move.l %%sp,%0\n\t"
+ : "=&r" (save_sp) );
+
+ tries = 100; /* Maximum number of bus errors. */
+ start_addr = phys_to_virt(dma_base);
+ end_addr = start_addr + dma_cnt;
+
+scsi_loop:
+ dma_cnt--;
+ rem = dma_cnt & (TRANSFER_SIZE - 1);
+ dma_cnt &= ~(TRANSFER_SIZE - 1);
+ psdm_reg = &hades_psdm_reg;
+
+ if (tt_scsi_dma.dma_ctrl & 1) /* Read or write? */
+ {
+ /*
+ * SCSI write. Abort when count is zero.
+ */
+
+ switch (rem)
+ {
+ case 0:
+ while (dma_cnt > 0)
+ {
+ dma_cnt -= TRANSFER_SIZE;
+
+ *psdm_reg = *start_addr++;
+ case 15:
+ *psdm_reg = *start_addr++;
+ case 14:
+ *psdm_reg = *start_addr++;
+ case 13:
+ *psdm_reg = *start_addr++;
+ case 12:
+ *psdm_reg = *start_addr++;
+ case 11:
+ *psdm_reg = *start_addr++;
+ case 10:
+ *psdm_reg = *start_addr++;
+ case 9:
+ *psdm_reg = *start_addr++;
+ case 8:
+ *psdm_reg = *start_addr++;
+ case 7:
+ *psdm_reg = *start_addr++;
+ case 6:
+ *psdm_reg = *start_addr++;
+ case 5:
+ *psdm_reg = *start_addr++;
+ case 4:
+ *psdm_reg = *start_addr++;
+ case 3:
+ *psdm_reg = *start_addr++;
+ case 2:
+ *psdm_reg = *start_addr++;
+ case 1:
+ *psdm_reg = *start_addr++;
+ }
+ }
+
+ hades_dma_ctrl |= 1; /* Set EOP. */
+ udelay(10);
+ *psdm_reg = *start_addr++; /* Dummy byte. */
+ tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */
+ }
+ else
+ {
+ /*
+ * SCSI read. Abort when count is zero.
+ */
+
+ switch (rem)
+ {
+ case 0:
+ while (dma_cnt > 0)
+ {
+ dma_cnt -= TRANSFER_SIZE;
+
+ *start_addr++ = *psdm_reg;
+ case 15:
+ *start_addr++ = *psdm_reg;
+ case 14:
+ *start_addr++ = *psdm_reg;
+ case 13:
+ *start_addr++ = *psdm_reg;
+ case 12:
+ *start_addr++ = *psdm_reg;
+ case 11:
+ *start_addr++ = *psdm_reg;
+ case 10:
+ *start_addr++ = *psdm_reg;
+ case 9:
+ *start_addr++ = *psdm_reg;
+ case 8:
+ *start_addr++ = *psdm_reg;
+ case 7:
+ *start_addr++ = *psdm_reg;
+ case 6:
+ *start_addr++ = *psdm_reg;
+ case 5:
+ *start_addr++ = *psdm_reg;
+ case 4:
+ *start_addr++ = *psdm_reg;
+ case 3:
+ *start_addr++ = *psdm_reg;
+ case 2:
+ *start_addr++ = *psdm_reg;
+ case 1:
+ *start_addr++ = *psdm_reg;
+ }
+ }
+
+ hades_dma_ctrl |= 1; /* Set EOP. */
+ udelay(10);
+ *start_addr++ = *psdm_reg;
+ tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */
+
+ set_restdata_reg(start_addr);
+ }
+
+ if (start_addr != end_addr)
+ printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n");
+
+ dma_cnt = end_addr - start_addr;
+
+scsi_end:
+ dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 :
+ virt_to_phys(start_addr);
+
+ SCSI_DMA_WRITE_P(dma_addr, dma_base);
+ SCSI_DMA_WRITE_P(dma_cnt, dma_cnt);
+
+ /*
+ * Restore old bus error routine.
+ */
+
+ __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t"
+ "move.l %0,8(%%a0)\n\t"
+ :
+ : "r" (save_buserr)
+ : "a0" );
+
+ atari_enable_irq(IRQ_TT_MFP_SCSI);
+
+ return IRQ_HANDLED;
+
+scsi_bus_error:
+ /*
+ * First check if the bus error is caused by our code.
+ * If not, call the original handler.
+ */
+
+ __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t"
+ "bcs.s .old_vector\n\t"
+ "cmp.l %1,2(%%sp)\n\t"
+ "bls.s .scsi_buserr\n"
+ ".old_vector:\n\t"
+ "move.l %2,-(%%sp)\n\t"
+ "rts\n"
+ ".scsi_buserr:\n\t"
+ :
+ : "i" (&&scsi_loop), "i" (&&scsi_end),
+ "m" (save_buserr) );
+
+ if (CPU_IS_060)
+ {
+ /*
+ * Get effective address and restore the stack.
+ */
+
+ __asm__ __volatile__ ("move.l 8(%%sp),%0\n\t"
+ "move.l %1,%%sp\n\t"
+ : "=a&" (eff_addr)
+ : "r" (save_sp) );
+ }
+ else
+ {
+ register struct m68040_frame *frame;
+
+ __asm__ __volatile__ ("lea 8(%%sp),%0\n\t"
+ : "=a&" (frame) );
+
+ if (tt_scsi_dma.dma_ctrl & 1)
+ {
+ /*
+ * Bus error while writing.
+ */
+
+ if (frame->wb3s & WBV_040)
+ {
+ if (frame->wb3a == (long) &hades_psdm_reg)
+ start_addr--;
+ else
+ writeback(frame->wb3s, frame->wb3a,
+ frame->wb3d, &&scsi_bus_error);
+ }
+
+ if (frame->wb2s & WBV_040)
+ {
+ if (frame->wb2a == (long) &hades_psdm_reg)
+ start_addr--;
+ else
+ writeback(frame->wb2s, frame->wb2a,
+ frame->wb2d, &&scsi_bus_error);
+ }
+
+ if (frame->wb1s & WBV_040)
+ {
+ if (frame->wb1a == (long) &hades_psdm_reg)
+ start_addr--;
+ }
+ }
+ else
+ {
+ /*
+ * Bus error while reading.
+ */
+
+ if (frame->wb3s & WBV_040)
+ writeback(frame->wb3s, frame->wb3a,
+ frame->wb3d, &&scsi_bus_error);
+ }
+
+ eff_addr = (unsigned char *) frame->faddr;
+
+ __asm__ __volatile__ ("move.l %0,%%sp\n\t"
+ :
+ : "r" (save_sp) );
+ }
+
+ dma_cnt = end_addr - start_addr;
+
+ if (eff_addr == &hades_psdm_reg)
+ {
+ /*
+ * Bus error occurred while reading the pseudo
+ * DMA register. Time out.
+ */
+
+ tries--;
+
+ if (tries <= 0)
+ {
+ if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */
+ set_restdata_reg(start_addr);
+
+ if (dma_cnt <= 1)
+ printk(KERN_CRIT "DMA emulation: Fatal "
+ "error while %s the last byte.\n",
+ (tt_scsi_dma.dma_ctrl & 1)
+ ? "writing" : "reading");
+
+ goto scsi_end;
+ }
+ else
+ goto scsi_loop;
+ }
+ else
+ {
+ /*
+ * Bus error during pseudo DMA transfer.
+ * Terminate the DMA transfer.
+ */
+
+ hades_dma_ctrl |= 3; /* Set EOP and bus error. */
+ if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */
+ set_restdata_reg(start_addr);
+ goto scsi_end;
+ }
+}
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
new file mode 100644
index 000000000000..af8adb629b33
--- /dev/null
+++ b/drivers/scsi/atari_scsi.c
@@ -0,0 +1,1163 @@
+/*
+ * atari_scsi.c -- Device dependent functions for the Atari generic SCSI port
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * Loosely based on the work of Robert De Vries' team and added:
+ * - working real DMA
+ * - Falcon support (untested yet!) ++bjoern fixed and now it works
+ * - lots of extensions and bug fixes.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+/**************************************************************************/
+/* */
+/* Notes for Falcon SCSI: */
+/* ---------------------- */
+/* */
+/* Since the Falcon SCSI uses the ST-DMA chip, that is shared among */
+/* several device drivers, locking and unlocking the access to this */
+/* chip is required. But locking is not possible from an interrupt, */
+/* since it puts the process to sleep if the lock is not available. */
+/* This prevents "late" locking of the DMA chip, i.e. locking it just */
+/* before using it, since in case of disconnection-reconnection */
+/* commands, the DMA is started from the reselection interrupt. */
+/* */
+/* Two possible schemes for ST-DMA-locking would be: */
+/* 1) The lock is taken for each command separately and disconnecting */
+/* is forbidden (i.e. can_queue = 1). */
+/* 2) The DMA chip is locked when the first command comes in and */
+/* released when the last command is finished and all queues are */
+/* empty. */
+/* The first alternative would result in bad performance, since the */
+/* interleaving of commands would not be used. The second is unfair to */
+/* other drivers using the ST-DMA, because the queues will seldom be */
+/* totally empty if there is a lot of disk traffic. */
+/* */
+/* For this reasons I decided to employ a more elaborate scheme: */
+/* - First, we give up the lock every time we can (for fairness), this */
+/* means every time a command finishes and there are no other commands */
+/* on the disconnected queue. */
+/* - If there are others waiting to lock the DMA chip, we stop */
+/* issuing commands, i.e. moving them onto the issue queue. */
+/* Because of that, the disconnected queue will run empty in a */
+/* while. Instead we go to sleep on a 'fairness_queue'. */
+/* - If the lock is released, all processes waiting on the fairness */
+/* queue will be woken. The first of them tries to re-lock the DMA, */
+/* the others wait for the first to finish this task. After that, */
+/* they can all run on and do their commands... */
+/* This sounds complicated (and it is it :-(), but it seems to be a */
+/* good compromise between fairness and performance: As long as no one */
+/* else wants to work with the ST-DMA chip, SCSI can go along as */
+/* usual. If now someone else comes, this behaviour is changed to a */
+/* "fairness mode": just already initiated commands are finished and */
+/* then the lock is released. The other one waiting will probably win */
+/* the race for locking the DMA, since it was waiting for longer. And */
+/* after it has finished, SCSI can go ahead again. Finally: I hope I */
+/* have not produced any deadlock possibilities! */
+/* */
+/**************************************************************************/
+
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#define NDEBUG (0)
+
+#define NDEBUG_ABORT 0x800000
+#define NDEBUG_TAGS 0x1000000
+#define NDEBUG_MERGING 0x2000000
+
+#define AUTOSENSE
+/* For the Atari version, use only polled IO or REAL_DMA */
+#define REAL_DMA
+/* Support tagged queuing? (on devices that are able to... :-) */
+#define SUPPORT_TAGS
+#define MAX_TAGS 32
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/nvram.h>
+#include <linux/bitops.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/traps.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "atari_scsi.h"
+#include "NCR5380.h"
+#include <asm/atari_stdma.h>
+#include <asm/atari_stram.h>
+#include <asm/io.h>
+
+#include <linux/stat.h>
+
+#define IS_A_TT() ATARIHW_PRESENT(TT_SCSI)
+
+#define SCSI_DMA_WRITE_P(elt,val) \
+ do { \
+ unsigned long v = val; \
+ tt_scsi_dma.elt##_lo = v & 0xff; \
+ v >>= 8; \
+ tt_scsi_dma.elt##_lmd = v & 0xff; \
+ v >>= 8; \
+ tt_scsi_dma.elt##_hmd = v & 0xff; \
+ v >>= 8; \
+ tt_scsi_dma.elt##_hi = v & 0xff; \
+ } while(0)
+
+#define SCSI_DMA_READ_P(elt) \
+ (((((((unsigned long)tt_scsi_dma.elt##_hi << 8) | \
+ (unsigned long)tt_scsi_dma.elt##_hmd) << 8) | \
+ (unsigned long)tt_scsi_dma.elt##_lmd) << 8) | \
+ (unsigned long)tt_scsi_dma.elt##_lo)
+
+
+static inline void SCSI_DMA_SETADR(unsigned long adr)
+{
+ st_dma.dma_lo = (unsigned char)adr;
+ MFPDELAY();
+ adr >>= 8;
+ st_dma.dma_md = (unsigned char)adr;
+ MFPDELAY();
+ adr >>= 8;
+ st_dma.dma_hi = (unsigned char)adr;
+ MFPDELAY();
+}
+
+static inline unsigned long SCSI_DMA_GETADR(void)
+{
+ unsigned long adr;
+ adr = st_dma.dma_lo;
+ MFPDELAY();
+ adr |= (st_dma.dma_md & 0xff) << 8;
+ MFPDELAY();
+ adr |= (st_dma.dma_hi & 0xff) << 16;
+ MFPDELAY();
+ return adr;
+}
+
+static inline void ENABLE_IRQ(void)
+{
+ if (IS_A_TT())
+ atari_enable_irq(IRQ_TT_MFP_SCSI);
+ else
+ atari_enable_irq(IRQ_MFP_FSCSI);
+}
+
+static inline void DISABLE_IRQ(void)
+{
+ if (IS_A_TT())
+ atari_disable_irq(IRQ_TT_MFP_SCSI);
+ else
+ atari_disable_irq(IRQ_MFP_FSCSI);
+}
+
+
+#define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \
+ (atari_scsi_host->hostdata))->dma_len)
+
+/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
+ * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
+ * need ten times the standard value... */
+#ifndef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
+#define AFTER_RESET_DELAY (HZ/2)
+#else
+#define AFTER_RESET_DELAY (5*HZ/2)
+#endif
+
+/***************************** Prototypes *****************************/
+
+#ifdef REAL_DMA
+static int scsi_dma_is_ignored_buserr( unsigned char dma_stat );
+static void atari_scsi_fetch_restbytes( void );
+static long atari_scsi_dma_residual( struct Scsi_Host *instance );
+static int falcon_classify_cmd( Scsi_Cmnd *cmd );
+static unsigned long atari_dma_xfer_len( unsigned long wanted_len,
+ Scsi_Cmnd *cmd, int write_flag );
+#endif
+static irqreturn_t scsi_tt_intr( int irq, void *dummy, struct pt_regs *fp);
+static irqreturn_t scsi_falcon_intr( int irq, void *dummy, struct pt_regs *fp);
+static void falcon_release_lock_if_possible( struct NCR5380_hostdata *
+ hostdata );
+static void falcon_get_lock( void );
+#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
+static void atari_scsi_reset_boot( void );
+#endif
+static unsigned char atari_scsi_tt_reg_read( unsigned char reg );
+static void atari_scsi_tt_reg_write( unsigned char reg, unsigned char value);
+static unsigned char atari_scsi_falcon_reg_read( unsigned char reg );
+static void atari_scsi_falcon_reg_write( unsigned char reg, unsigned char value );
+
+/************************* End of Prototypes **************************/
+
+
+static struct Scsi_Host *atari_scsi_host = NULL;
+static unsigned char (*atari_scsi_reg_read)( unsigned char reg );
+static void (*atari_scsi_reg_write)( unsigned char reg, unsigned char value );
+
+#ifdef REAL_DMA
+static unsigned long atari_dma_residual, atari_dma_startaddr;
+static short atari_dma_active;
+/* pointer to the dribble buffer */
+static char *atari_dma_buffer = NULL;
+/* precalculated physical address of the dribble buffer */
+static unsigned long atari_dma_phys_buffer;
+/* != 0 tells the Falcon int handler to copy data from the dribble buffer */
+static char *atari_dma_orig_addr;
+/* size of the dribble buffer; 4k seems enough, since the Falcon cannot use
+ * scatter-gather anyway, so most transfers are 1024 byte only. In the rare
+ * cases where requests to physical contiguous buffers have been merged, this
+ * request is <= 4k (one page). So I don't think we have to split transfers
+ * just due to this buffer size...
+ */
+#define STRAM_BUFFER_SIZE (4096)
+/* mask for address bits that can't be used with the ST-DMA */
+static unsigned long atari_dma_stram_mask;
+#define STRAM_ADDR(a) (((a) & atari_dma_stram_mask) == 0)
+/* number of bytes to cut from a transfer to handle NCR overruns */
+static int atari_read_overruns = 0;
+#endif
+
+static int setup_can_queue = -1;
+MODULE_PARM(setup_can_queue, "i");
+static int setup_cmd_per_lun = -1;
+MODULE_PARM(setup_cmd_per_lun, "i");
+static int setup_sg_tablesize = -1;
+MODULE_PARM(setup_sg_tablesize, "i");
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+MODULE_PARM(setup_use_tagged_queuing, "i");
+#endif
+static int setup_hostid = -1;
+MODULE_PARM(setup_hostid, "i");
+
+
+#if defined(CONFIG_TT_DMA_EMUL)
+#include "atari_dma_emul.c"
+#endif
+
+#if defined(REAL_DMA)
+
+static int scsi_dma_is_ignored_buserr( unsigned char dma_stat )
+{
+ int i;
+ unsigned long addr = SCSI_DMA_READ_P( dma_addr ), end_addr;
+
+ if (dma_stat & 0x01) {
+
+ /* A bus error happens when DMA-ing from the last page of a
+ * physical memory chunk (DMA prefetch!), but that doesn't hurt.
+ * Check for this case:
+ */
+
+ for( i = 0; i < m68k_num_memory; ++i ) {
+ end_addr = m68k_memory[i].addr +
+ m68k_memory[i].size;
+ if (end_addr <= addr && addr <= end_addr + 4)
+ return( 1 );
+ }
+ }
+ return( 0 );
+}
+
+
+#if 0
+/* Dead code... wasn't called anyway :-) and causes some trouble, because at
+ * end-of-DMA, both SCSI ints are triggered simultaneously, so the NCR int has
+ * to clear the DMA int pending bit before it allows other level 6 interrupts.
+ */
+static void scsi_dma_buserr (int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned char dma_stat = tt_scsi_dma.dma_ctrl;
+
+ /* Don't do anything if a NCR interrupt is pending. Probably it's just
+ * masked... */
+ if (atari_irq_pending( IRQ_TT_MFP_SCSI ))
+ return;
+
+ printk("Bad SCSI DMA interrupt! dma_addr=0x%08lx dma_stat=%02x dma_cnt=%08lx\n",
+ SCSI_DMA_READ_P(dma_addr), dma_stat, SCSI_DMA_READ_P(dma_cnt));
+ if (dma_stat & 0x80) {
+ if (!scsi_dma_is_ignored_buserr( dma_stat ))
+ printk( "SCSI DMA bus error -- bad DMA programming!\n" );
+ }
+ else {
+ /* Under normal circumstances we never should get to this point,
+ * since both interrupts are triggered simultaneously and the 5380
+ * int has higher priority. When this irq is handled, that DMA
+ * interrupt is cleared. So a warning message is printed here.
+ */
+ printk( "SCSI DMA intr ?? -- this shouldn't happen!\n" );
+ }
+}
+#endif
+
+#endif
+
+
+static irqreturn_t scsi_tt_intr (int irq, void *dummy, struct pt_regs *fp)
+{
+#ifdef REAL_DMA
+ int dma_stat;
+
+ dma_stat = tt_scsi_dma.dma_ctrl;
+
+ INT_PRINTK("scsi%d: NCR5380 interrupt, DMA status = %02x\n",
+ atari_scsi_host->host_no, dma_stat & 0xff);
+
+ /* Look if it was the DMA that has interrupted: First possibility
+ * is that a bus error occurred...
+ */
+ if (dma_stat & 0x80) {
+ if (!scsi_dma_is_ignored_buserr( dma_stat )) {
+ printk(KERN_ERR "SCSI DMA caused bus error near 0x%08lx\n",
+ SCSI_DMA_READ_P(dma_addr));
+ printk(KERN_CRIT "SCSI DMA bus error -- bad DMA programming!");
+ }
+ }
+
+ /* If the DMA is active but not finished, we have the case
+ * that some other 5380 interrupt occurred within the DMA transfer.
+ * This means we have residual bytes, if the desired end address
+ * is not yet reached. Maybe we have to fetch some bytes from the
+ * rest data register, too. The residual must be calculated from
+ * the address pointer, not the counter register, because only the
+ * addr reg counts bytes not yet written and pending in the rest
+ * data reg!
+ */
+ if ((dma_stat & 0x02) && !(dma_stat & 0x40)) {
+ atari_dma_residual = HOSTDATA_DMALEN - (SCSI_DMA_READ_P( dma_addr ) -
+ atari_dma_startaddr);
+
+ DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n",
+ atari_dma_residual);
+
+ if ((signed int)atari_dma_residual < 0)
+ atari_dma_residual = 0;
+ if ((dma_stat & 1) == 0) {
+ /* After read operations, we maybe have to
+ transport some rest bytes */
+ atari_scsi_fetch_restbytes();
+ }
+ else {
+ /* There seems to be a nasty bug in some SCSI-DMA/NCR
+ combinations: If a target disconnects while a write
+ operation is going on, the address register of the
+ DMA may be a few bytes farer than it actually read.
+ This is probably due to DMA prefetching and a delay
+ between DMA and NCR. Experiments showed that the
+ dma_addr is 9 bytes to high, but this could vary.
+ The problem is, that the residual is thus calculated
+ wrong and the next transfer will start behind where
+ it should. So we round up the residual to the next
+ multiple of a sector size, if it isn't already a
+ multiple and the originally expected transfer size
+ was. The latter condition is there to ensure that
+ the correction is taken only for "real" data
+ transfers and not for, e.g., the parameters of some
+ other command. These shouldn't disconnect anyway.
+ */
+ if (atari_dma_residual & 0x1ff) {
+ DMA_PRINTK("SCSI DMA: DMA bug corrected, "
+ "difference %ld bytes\n",
+ 512 - (atari_dma_residual & 0x1ff));
+ atari_dma_residual = (atari_dma_residual + 511) & ~0x1ff;
+ }
+ }
+ tt_scsi_dma.dma_ctrl = 0;
+ }
+
+ /* If the DMA is finished, fetch the rest bytes and turn it off */
+ if (dma_stat & 0x40) {
+ atari_dma_residual = 0;
+ if ((dma_stat & 1) == 0)
+ atari_scsi_fetch_restbytes();
+ tt_scsi_dma.dma_ctrl = 0;
+ }
+
+#endif /* REAL_DMA */
+
+ NCR5380_intr (0, 0, 0);
+
+#if 0
+ /* To be sure the int is not masked */
+ atari_enable_irq( IRQ_TT_MFP_SCSI );
+#endif
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t scsi_falcon_intr (int irq, void *dummy, struct pt_regs *fp)
+{
+#ifdef REAL_DMA
+ int dma_stat;
+
+ /* Turn off DMA and select sector counter register before
+ * accessing the status register (Atari recommendation!)
+ */
+ st_dma.dma_mode_status = 0x90;
+ dma_stat = st_dma.dma_mode_status;
+
+ /* Bit 0 indicates some error in the DMA process... don't know
+ * what happened exactly (no further docu).
+ */
+ if (!(dma_stat & 0x01)) {
+ /* DMA error */
+ printk(KERN_CRIT "SCSI DMA error near 0x%08lx!\n", SCSI_DMA_GETADR());
+ }
+
+ /* If the DMA was active, but now bit 1 is not clear, it is some
+ * other 5380 interrupt that finishes the DMA transfer. We have to
+ * calculate the number of residual bytes and give a warning if
+ * bytes are stuck in the ST-DMA fifo (there's no way to reach them!)
+ */
+ if (atari_dma_active && (dma_stat & 0x02)) {
+ unsigned long transferred;
+
+ transferred = SCSI_DMA_GETADR() - atari_dma_startaddr;
+ /* The ST-DMA address is incremented in 2-byte steps, but the
+ * data are written only in 16-byte chunks. If the number of
+ * transferred bytes is not divisible by 16, the remainder is
+ * lost somewhere in outer space.
+ */
+ if (transferred & 15)
+ printk(KERN_ERR "SCSI DMA error: %ld bytes lost in "
+ "ST-DMA fifo\n", transferred & 15);
+
+ atari_dma_residual = HOSTDATA_DMALEN - transferred;
+ DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n",
+ atari_dma_residual);
+ }
+ else
+ atari_dma_residual = 0;
+ atari_dma_active = 0;
+
+ if (atari_dma_orig_addr) {
+ /* If the dribble buffer was used on a read operation, copy the DMA-ed
+ * data to the original destination address.
+ */
+ memcpy(atari_dma_orig_addr, phys_to_virt(atari_dma_startaddr),
+ HOSTDATA_DMALEN - atari_dma_residual);
+ atari_dma_orig_addr = NULL;
+ }
+
+#endif /* REAL_DMA */
+
+ NCR5380_intr (0, 0, 0);
+ return IRQ_HANDLED;
+}
+
+
+#ifdef REAL_DMA
+static void atari_scsi_fetch_restbytes( void )
+{
+ int nr;
+ char *src, *dst;
+ unsigned long phys_dst;
+
+ /* fetch rest bytes in the DMA register */
+ phys_dst = SCSI_DMA_READ_P(dma_addr);
+ nr = phys_dst & 3;
+ if (nr) {
+ /* there are 'nr' bytes left for the last long address
+ before the DMA pointer */
+ phys_dst ^= nr;
+ DMA_PRINTK("SCSI DMA: there are %d rest bytes for phys addr 0x%08lx",
+ nr, phys_dst);
+ /* The content of the DMA pointer is a physical address! */
+ dst = phys_to_virt(phys_dst);
+ DMA_PRINTK(" = virt addr %p\n", dst);
+ for (src = (char *)&tt_scsi_dma.dma_restdata; nr != 0; --nr)
+ *dst++ = *src++;
+ }
+}
+#endif /* REAL_DMA */
+
+
+static int falcon_got_lock = 0;
+static DECLARE_WAIT_QUEUE_HEAD(falcon_fairness_wait);
+static int falcon_trying_lock = 0;
+static DECLARE_WAIT_QUEUE_HEAD(falcon_try_wait);
+static int falcon_dont_release = 0;
+
+/* This function releases the lock on the DMA chip if there is no
+ * connected command and the disconnected queue is empty. On
+ * releasing, instances of falcon_get_lock are awoken, that put
+ * themselves to sleep for fairness. They can now try to get the lock
+ * again (but others waiting longer more probably will win).
+ */
+
+static void
+falcon_release_lock_if_possible( struct NCR5380_hostdata * hostdata )
+{
+ unsigned long flags;
+
+ if (IS_A_TT()) return;
+
+ local_irq_save(flags);
+
+ if (falcon_got_lock &&
+ !hostdata->disconnected_queue &&
+ !hostdata->issue_queue &&
+ !hostdata->connected) {
+
+ if (falcon_dont_release) {
+#if 0
+ printk("WARNING: Lock release not allowed. Ignored\n");
+#endif
+ local_irq_restore(flags);
+ return;
+ }
+ falcon_got_lock = 0;
+ stdma_release();
+ wake_up( &falcon_fairness_wait );
+ }
+
+ local_irq_restore(flags);
+}
+
+/* This function manages the locking of the ST-DMA.
+ * If the DMA isn't locked already for SCSI, it tries to lock it by
+ * calling stdma_lock(). But if the DMA is locked by the SCSI code and
+ * there are other drivers waiting for the chip, we do not issue the
+ * command immediately but wait on 'falcon_fairness_queue'. We will be
+ * waked up when the DMA is unlocked by some SCSI interrupt. After that
+ * we try to get the lock again.
+ * But we must be prepared that more than one instance of
+ * falcon_get_lock() is waiting on the fairness queue. They should not
+ * try all at once to call stdma_lock(), one is enough! For that, the
+ * first one sets 'falcon_trying_lock', others that see that variable
+ * set wait on the queue 'falcon_try_wait'.
+ * Complicated, complicated.... Sigh...
+ */
+
+static void falcon_get_lock( void )
+{
+ unsigned long flags;
+
+ if (IS_A_TT()) return;
+
+ local_irq_save(flags);
+
+ while( !in_interrupt() && falcon_got_lock && stdma_others_waiting() )
+ sleep_on( &falcon_fairness_wait );
+
+ while (!falcon_got_lock) {
+ if (in_interrupt())
+ panic( "Falcon SCSI hasn't ST-DMA lock in interrupt" );
+ if (!falcon_trying_lock) {
+ falcon_trying_lock = 1;
+ stdma_lock(scsi_falcon_intr, NULL);
+ falcon_got_lock = 1;
+ falcon_trying_lock = 0;
+ wake_up( &falcon_try_wait );
+ }
+ else {
+ sleep_on( &falcon_try_wait );
+ }
+ }
+
+ local_irq_restore(flags);
+ if (!falcon_got_lock)
+ panic("Falcon SCSI: someone stole the lock :-(\n");
+}
+
+
+/* This is the wrapper function for NCR5380_queue_command(). It just
+ * tries to get the lock on the ST-DMA (see above) and then calls the
+ * original function.
+ */
+
+#if 0
+int atari_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+ /* falcon_get_lock();
+ * ++guenther: moved to NCR5380_queue_command() to prevent
+ * race condition, see there for an explanation.
+ */
+ return( NCR5380_queue_command( cmd, done ) );
+}
+#endif
+
+
+int atari_scsi_detect (Scsi_Host_Template *host)
+{
+ static int called = 0;
+ struct Scsi_Host *instance;
+
+ if (!MACH_IS_ATARI ||
+ (!ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(TT_SCSI)) ||
+ called)
+ return( 0 );
+
+ host->proc_name = "Atari";
+
+ atari_scsi_reg_read = IS_A_TT() ? atari_scsi_tt_reg_read :
+ atari_scsi_falcon_reg_read;
+ atari_scsi_reg_write = IS_A_TT() ? atari_scsi_tt_reg_write :
+ atari_scsi_falcon_reg_write;
+
+ /* setup variables */
+ host->can_queue =
+ (setup_can_queue > 0) ? setup_can_queue :
+ IS_A_TT() ? ATARI_TT_CAN_QUEUE : ATARI_FALCON_CAN_QUEUE;
+ host->cmd_per_lun =
+ (setup_cmd_per_lun > 0) ? setup_cmd_per_lun :
+ IS_A_TT() ? ATARI_TT_CMD_PER_LUN : ATARI_FALCON_CMD_PER_LUN;
+ /* Force sg_tablesize to 0 on a Falcon! */
+ host->sg_tablesize =
+ !IS_A_TT() ? ATARI_FALCON_SG_TABLESIZE :
+ (setup_sg_tablesize >= 0) ? setup_sg_tablesize : ATARI_TT_SG_TABLESIZE;
+
+ if (setup_hostid >= 0)
+ host->this_id = setup_hostid;
+ else {
+ /* use 7 as default */
+ host->this_id = 7;
+ /* Test if a host id is set in the NVRam */
+ if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) {
+ unsigned char b = nvram_read_byte( 14 );
+ /* Arbitration enabled? (for TOS) If yes, use configured host ID */
+ if (b & 0x80)
+ host->this_id = b & 7;
+ }
+ }
+
+#ifdef SUPPORT_TAGS
+ if (setup_use_tagged_queuing < 0)
+ setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING;
+#endif
+#ifdef REAL_DMA
+ /* If running on a Falcon and if there's TT-Ram (i.e., more than one
+ * memory block, since there's always ST-Ram in a Falcon), then allocate a
+ * STRAM_BUFFER_SIZE byte dribble buffer for transfers from/to alternative
+ * Ram.
+ */
+ if (MACH_IS_ATARI && ATARIHW_PRESENT(ST_SCSI) &&
+ !ATARIHW_PRESENT(EXTD_DMA) && m68k_num_memory > 1) {
+ atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI");
+ if (!atari_dma_buffer) {
+ printk( KERN_ERR "atari_scsi_detect: can't allocate ST-RAM "
+ "double buffer\n" );
+ return( 0 );
+ }
+ atari_dma_phys_buffer = virt_to_phys( atari_dma_buffer );
+ atari_dma_orig_addr = 0;
+ }
+#endif
+ instance = scsi_register (host, sizeof (struct NCR5380_hostdata));
+ if(instance == NULL)
+ {
+ atari_stram_free(atari_dma_buffer);
+ atari_dma_buffer = 0;
+ return 0;
+ }
+ atari_scsi_host = instance;
+ /* Set irq to 0, to avoid that the mid-level code disables our interrupt
+ * during queue_command calls. This is completely unnecessary, and even
+ * worse causes bad problems on the Falcon, where the int is shared with
+ * IDE and floppy! */
+ instance->irq = 0;
+
+#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
+ atari_scsi_reset_boot();
+#endif
+ NCR5380_init (instance, 0);
+
+ if (IS_A_TT()) {
+
+ /* This int is actually "pseudo-slow", i.e. it acts like a slow
+ * interrupt after having cleared the pending flag for the DMA
+ * interrupt. */
+ if (request_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr, IRQ_TYPE_SLOW,
+ "SCSI NCR5380", scsi_tt_intr)) {
+ printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting",IRQ_TT_MFP_SCSI);
+ scsi_unregister(atari_scsi_host);
+ atari_stram_free(atari_dma_buffer);
+ atari_dma_buffer = 0;
+ return 0;
+ }
+ tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */
+#ifdef REAL_DMA
+ tt_scsi_dma.dma_ctrl = 0;
+ atari_dma_residual = 0;
+#ifdef CONFIG_TT_DMA_EMUL
+ if (MACH_IS_HADES) {
+ if (request_irq(IRQ_AUTO_2, hades_dma_emulator,
+ IRQ_TYPE_PRIO, "Hades DMA emulator",
+ hades_dma_emulator)) {
+ printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting (MACH_IS_HADES)",IRQ_AUTO_2);
+ free_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr);
+ scsi_unregister(atari_scsi_host);
+ atari_stram_free(atari_dma_buffer);
+ atari_dma_buffer = 0;
+ return 0;
+ }
+ }
+#endif
+ if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+ /* While the read overruns (described by Drew Eckhardt in
+ * NCR5380.c) never happened on TTs, they do in fact on the Medusa
+ * (This was the cause why SCSI didn't work right for so long
+ * there.) Since handling the overruns slows down a bit, I turned
+ * the #ifdef's into a runtime condition.
+ *
+ * In principle it should be sufficient to do max. 1 byte with
+ * PIO, but there is another problem on the Medusa with the DMA
+ * rest data register. So 'atari_read_overruns' is currently set
+ * to 4 to avoid having transfers that aren't a multiple of 4. If
+ * the rest data bug is fixed, this can be lowered to 1.
+ */
+ atari_read_overruns = 4;
+ }
+#endif /*REAL_DMA*/
+ }
+ else { /* ! IS_A_TT */
+
+ /* Nothing to do for the interrupt: the ST-DMA is initialized
+ * already by atari_init_INTS()
+ */
+
+#ifdef REAL_DMA
+ atari_dma_residual = 0;
+ atari_dma_active = 0;
+ atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000
+ : 0xff000000);
+#endif
+ }
+
+ printk(KERN_INFO "scsi%d: options CAN_QUEUE=%d CMD_PER_LUN=%d SCAT-GAT=%d "
+#ifdef SUPPORT_TAGS
+ "TAGGED-QUEUING=%s "
+#endif
+ "HOSTID=%d",
+ instance->host_no, instance->hostt->can_queue,
+ instance->hostt->cmd_per_lun,
+ instance->hostt->sg_tablesize,
+#ifdef SUPPORT_TAGS
+ setup_use_tagged_queuing ? "yes" : "no",
+#endif
+ instance->hostt->this_id );
+ NCR5380_print_options (instance);
+ printk ("\n");
+
+ called = 1;
+ return( 1 );
+}
+
+#ifdef MODULE
+int atari_scsi_release (struct Scsi_Host *sh)
+{
+ if (IS_A_TT())
+ free_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr);
+ if (atari_dma_buffer)
+ atari_stram_free (atari_dma_buffer);
+ return 1;
+}
+#endif
+
+void __init atari_scsi_setup(char *str, int *ints)
+{
+ /* Format of atascsi parameter is:
+ * atascsi=<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
+ * Defaults depend on TT or Falcon, hostid determined at run time.
+ * Negative values mean don't change.
+ */
+
+ if (ints[0] < 1) {
+ printk( "atari_scsi_setup: no arguments!\n" );
+ return;
+ }
+
+ if (ints[0] >= 1) {
+ if (ints[1] > 0)
+ /* no limits on this, just > 0 */
+ setup_can_queue = ints[1];
+ }
+ if (ints[0] >= 2) {
+ if (ints[2] > 0)
+ setup_cmd_per_lun = ints[2];
+ }
+ if (ints[0] >= 3) {
+ if (ints[3] >= 0) {
+ setup_sg_tablesize = ints[3];
+ /* Must be <= SG_ALL (255) */
+ if (setup_sg_tablesize > SG_ALL)
+ setup_sg_tablesize = SG_ALL;
+ }
+ }
+ if (ints[0] >= 4) {
+ /* Must be between 0 and 7 */
+ if (ints[4] >= 0 && ints[4] <= 7)
+ setup_hostid = ints[4];
+ else if (ints[4] > 7)
+ printk( "atari_scsi_setup: invalid host ID %d !\n", ints[4] );
+ }
+#ifdef SUPPORT_TAGS
+ if (ints[0] >= 5) {
+ if (ints[5] >= 0)
+ setup_use_tagged_queuing = !!ints[5];
+ }
+#endif
+}
+
+int atari_scsi_bus_reset(Scsi_Cmnd *cmd)
+{
+ int rv;
+ struct NCR5380_hostdata *hostdata =
+ (struct NCR5380_hostdata *)cmd->device->host->hostdata;
+
+ /* For doing the reset, SCSI interrupts must be disabled first,
+ * since the 5380 raises its IRQ line while _RST is active and we
+ * can't disable interrupts completely, since we need the timer.
+ */
+ /* And abort a maybe active DMA transfer */
+ if (IS_A_TT()) {
+ atari_turnoff_irq( IRQ_TT_MFP_SCSI );
+#ifdef REAL_DMA
+ tt_scsi_dma.dma_ctrl = 0;
+#endif /* REAL_DMA */
+ }
+ else {
+ atari_turnoff_irq( IRQ_MFP_FSCSI );
+#ifdef REAL_DMA
+ st_dma.dma_mode_status = 0x90;
+ atari_dma_active = 0;
+ atari_dma_orig_addr = NULL;
+#endif /* REAL_DMA */
+ }
+
+ rv = NCR5380_bus_reset(cmd);
+
+ /* Re-enable ints */
+ if (IS_A_TT()) {
+ atari_turnon_irq( IRQ_TT_MFP_SCSI );
+ }
+ else {
+ atari_turnon_irq( IRQ_MFP_FSCSI );
+ }
+ if ((rv & SCSI_RESET_ACTION) == SCSI_RESET_SUCCESS)
+ falcon_release_lock_if_possible(hostdata);
+
+ return( rv );
+}
+
+
+#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
+static void __init atari_scsi_reset_boot(void)
+{
+ unsigned long end;
+
+ /*
+ * Do a SCSI reset to clean up the bus during initialization. No messing
+ * with the queues, interrupts, or locks necessary here.
+ */
+
+ printk( "Atari SCSI: resetting the SCSI bus..." );
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+ /* The min. reset hold time is 25us, so 40us should be enough */
+ udelay( 50 );
+ /* reset RST and interrupt */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ end = jiffies + AFTER_RESET_DELAY;
+ while (time_before(jiffies, end))
+ barrier();
+
+ printk( " done\n" );
+}
+#endif
+
+
+const char * atari_scsi_info (struct Scsi_Host *host)
+{
+ /* atari_scsi_detect() is verbose enough... */
+ static const char string[] = "Atari native SCSI";
+ return string;
+}
+
+
+#if defined(REAL_DMA)
+
+unsigned long atari_scsi_dma_setup( struct Scsi_Host *instance, void *data,
+ unsigned long count, int dir )
+{
+ unsigned long addr = virt_to_phys( data );
+
+ DMA_PRINTK("scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, "
+ "dir = %d\n", instance->host_no, data, addr, count, dir);
+
+ if (!IS_A_TT() && !STRAM_ADDR(addr)) {
+ /* If we have a non-DMAable address on a Falcon, use the dribble
+ * buffer; 'orig_addr' != 0 in the read case tells the interrupt
+ * handler to copy data from the dribble buffer to the originally
+ * wanted address.
+ */
+ if (dir)
+ memcpy( atari_dma_buffer, data, count );
+ else
+ atari_dma_orig_addr = data;
+ addr = atari_dma_phys_buffer;
+ }
+
+ atari_dma_startaddr = addr; /* Needed for calculating residual later. */
+
+ /* Cache cleanup stuff: On writes, push any dirty cache out before sending
+ * it to the peripheral. (Must be done before DMA setup, since at least
+ * the ST-DMA begins to fill internal buffers right after setup. For
+ * reads, invalidate any cache, may be altered after DMA without CPU
+ * knowledge.
+ *
+ * ++roman: For the Medusa, there's no need at all for that cache stuff,
+ * because the hardware does bus snooping (fine!).
+ */
+ dma_cache_maintenance( addr, count, dir );
+
+ if (count == 0)
+ printk(KERN_NOTICE "SCSI warning: DMA programmed for 0 bytes !\n");
+
+ if (IS_A_TT()) {
+ tt_scsi_dma.dma_ctrl = dir;
+ SCSI_DMA_WRITE_P( dma_addr, addr );
+ SCSI_DMA_WRITE_P( dma_cnt, count );
+ tt_scsi_dma.dma_ctrl = dir | 2;
+ }
+ else { /* ! IS_A_TT */
+
+ /* set address */
+ SCSI_DMA_SETADR( addr );
+
+ /* toggle direction bit to clear FIFO and set DMA direction */
+ dir <<= 8;
+ st_dma.dma_mode_status = 0x90 | dir;
+ st_dma.dma_mode_status = 0x90 | (dir ^ 0x100);
+ st_dma.dma_mode_status = 0x90 | dir;
+ udelay(40);
+ /* On writes, round up the transfer length to the next multiple of 512
+ * (see also comment at atari_dma_xfer_len()). */
+ st_dma.fdc_acces_seccount = (count + (dir ? 511 : 0)) >> 9;
+ udelay(40);
+ st_dma.dma_mode_status = 0x10 | dir;
+ udelay(40);
+ /* need not restore value of dir, only boolean value is tested */
+ atari_dma_active = 1;
+ }
+
+ return( count );
+}
+
+
+static long atari_scsi_dma_residual( struct Scsi_Host *instance )
+{
+ return( atari_dma_residual );
+}
+
+
+#define CMD_SURELY_BLOCK_MODE 0
+#define CMD_SURELY_BYTE_MODE 1
+#define CMD_MODE_UNKNOWN 2
+
+static int falcon_classify_cmd( Scsi_Cmnd *cmd )
+{
+ unsigned char opcode = cmd->cmnd[0];
+
+ if (opcode == READ_DEFECT_DATA || opcode == READ_LONG ||
+ opcode == READ_BUFFER)
+ return( CMD_SURELY_BYTE_MODE );
+ else if (opcode == READ_6 || opcode == READ_10 ||
+ opcode == 0xa8 /* READ_12 */ || opcode == READ_REVERSE ||
+ opcode == RECOVER_BUFFERED_DATA) {
+ /* In case of a sequential-access target (tape), special care is
+ * needed here: The transfer is block-mode only if the 'fixed' bit is
+ * set! */
+ if (cmd->device->type == TYPE_TAPE && !(cmd->cmnd[1] & 1))
+ return( CMD_SURELY_BYTE_MODE );
+ else
+ return( CMD_SURELY_BLOCK_MODE );
+ }
+ else
+ return( CMD_MODE_UNKNOWN );
+}
+
+
+/* This function calculates the number of bytes that can be transferred via
+ * DMA. On the TT, this is arbitrary, but on the Falcon we have to use the
+ * ST-DMA chip. There are only multiples of 512 bytes possible and max.
+ * 255*512 bytes :-( This means also, that defining READ_OVERRUNS is not
+ * possible on the Falcon, since that would require to program the DMA for
+ * n*512 - atari_read_overrun bytes. But it seems that the Falcon doesn't have
+ * the overrun problem, so this question is academic :-)
+ */
+
+static unsigned long atari_dma_xfer_len( unsigned long wanted_len,
+ Scsi_Cmnd *cmd,
+ int write_flag )
+{
+ unsigned long possible_len, limit;
+#ifndef CONFIG_TT_DMA_EMUL
+ if (MACH_IS_HADES)
+ /* Hades has no SCSI DMA at all :-( Always force use of PIO */
+ return( 0 );
+#endif
+ if (IS_A_TT())
+ /* TT SCSI DMA can transfer arbitrary #bytes */
+ return( wanted_len );
+
+ /* ST DMA chip is stupid -- only multiples of 512 bytes! (and max.
+ * 255*512 bytes, but this should be enough)
+ *
+ * ++roman: Aaargl! Another Falcon-SCSI problem... There are some commands
+ * that return a number of bytes which cannot be known beforehand. In this
+ * case, the given transfer length is an "allocation length". Now it
+ * can happen that this allocation length is a multiple of 512 bytes and
+ * the DMA is used. But if not n*512 bytes really arrive, some input data
+ * will be lost in the ST-DMA's FIFO :-( Thus, we have to distinguish
+ * between commands that do block transfers and those that do byte
+ * transfers. But this isn't easy... there are lots of vendor specific
+ * commands, and the user can issue any command via the
+ * SCSI_IOCTL_SEND_COMMAND.
+ *
+ * The solution: We classify SCSI commands in 1) surely block-mode cmd.s,
+ * 2) surely byte-mode cmd.s and 3) cmd.s with unknown mode. In case 1)
+ * and 3), the thing to do is obvious: allow any number of blocks via DMA
+ * or none. In case 2), we apply some heuristic: Byte mode is assumed if
+ * the transfer (allocation) length is < 1024, hoping that no cmd. not
+ * explicitly known as byte mode have such big allocation lengths...
+ * BTW, all the discussion above applies only to reads. DMA writes are
+ * unproblematic anyways, since the targets aborts the transfer after
+ * receiving a sufficient number of bytes.
+ *
+ * Another point: If the transfer is from/to an non-ST-RAM address, we
+ * use the dribble buffer and thus can do only STRAM_BUFFER_SIZE bytes.
+ */
+
+ if (write_flag) {
+ /* Write operation can always use the DMA, but the transfer size must
+ * be rounded up to the next multiple of 512 (atari_dma_setup() does
+ * this).
+ */
+ possible_len = wanted_len;
+ }
+ else {
+ /* Read operations: if the wanted transfer length is not a multiple of
+ * 512, we cannot use DMA, since the ST-DMA cannot split transfers
+ * (no interrupt on DMA finished!)
+ */
+ if (wanted_len & 0x1ff)
+ possible_len = 0;
+ else {
+ /* Now classify the command (see above) and decide whether it is
+ * allowed to do DMA at all */
+ switch( falcon_classify_cmd( cmd )) {
+ case CMD_SURELY_BLOCK_MODE:
+ possible_len = wanted_len;
+ break;
+ case CMD_SURELY_BYTE_MODE:
+ possible_len = 0; /* DMA prohibited */
+ break;
+ case CMD_MODE_UNKNOWN:
+ default:
+ /* For unknown commands assume block transfers if the transfer
+ * size/allocation length is >= 1024 */
+ possible_len = (wanted_len < 1024) ? 0 : wanted_len;
+ break;
+ }
+ }
+ }
+
+ /* Last step: apply the hard limit on DMA transfers */
+ limit = (atari_dma_buffer && !STRAM_ADDR( virt_to_phys(cmd->SCp.ptr) )) ?
+ STRAM_BUFFER_SIZE : 255*512;
+ if (possible_len > limit)
+ possible_len = limit;
+
+ if (possible_len != wanted_len)
+ DMA_PRINTK("Sorry, must cut DMA transfer size to %ld bytes "
+ "instead of %ld\n", possible_len, wanted_len);
+
+ return( possible_len );
+}
+
+
+#endif /* REAL_DMA */
+
+
+/* NCR5380 register access functions
+ *
+ * There are separate functions for TT and Falcon, because the access
+ * methods are quite different. The calling macros NCR5380_read and
+ * NCR5380_write call these functions via function pointers.
+ */
+
+static unsigned char atari_scsi_tt_reg_read( unsigned char reg )
+{
+ return( tt_scsi_regp[reg * 2] );
+}
+
+static void atari_scsi_tt_reg_write( unsigned char reg, unsigned char value )
+{
+ tt_scsi_regp[reg * 2] = value;
+}
+
+static unsigned char atari_scsi_falcon_reg_read( unsigned char reg )
+{
+ dma_wd.dma_mode_status= (u_short)(0x88 + reg);
+ return( (u_char)dma_wd.fdc_acces_seccount );
+}
+
+static void atari_scsi_falcon_reg_write( unsigned char reg, unsigned char value )
+{
+ dma_wd.dma_mode_status = (u_short)(0x88 + reg);
+ dma_wd.fdc_acces_seccount = (u_short)value;
+}
+
+
+#include "atari_NCR5380.c"
+
+static Scsi_Host_Template driver_template = {
+ .proc_info = atari_scsi_proc_info,
+ .name = "Atari native SCSI",
+ .detect = atari_scsi_detect,
+ .release = atari_scsi_release,
+ .info = atari_scsi_info,
+ .queuecommand = atari_scsi_queue_command,
+ .eh_abort_handler = atari_scsi_abort,
+ .eh_bus_reset_handler = atari_scsi_bus_reset,
+ .can_queue = 0, /* initialized at run-time */
+ .this_id = 0, /* initialized at run-time */
+ .sg_tablesize = 0, /* initialized at run-time */
+ .cmd_per_lun = 0, /* initialized at run-time */
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/atari_scsi.h b/drivers/scsi/atari_scsi.h
new file mode 100644
index 000000000000..cc1256988841
--- /dev/null
+++ b/drivers/scsi/atari_scsi.h
@@ -0,0 +1,248 @@
+/*
+ * atari_scsi.h -- Header file for the Atari native SCSI driver
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * (Loosely based on the work of Robert De Vries' team)
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+#ifndef ATARI_SCSI_H
+#define ATARI_SCSI_H
+
+/* (I_HAVE_OVERRUNS stuff removed) */
+
+#ifndef ASM
+int atari_scsi_detect (Scsi_Host_Template *);
+const char *atari_scsi_info (struct Scsi_Host *);
+int atari_scsi_reset (Scsi_Cmnd *, unsigned int);
+#ifdef MODULE
+int atari_scsi_release (struct Scsi_Host *);
+#else
+#define atari_scsi_release NULL
+#endif
+
+/* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher
+ * values should work, too; try it! (but cmd_per_lun costs memory!) */
+
+/* But there seems to be a bug somewhere that requires CAN_QUEUE to be
+ * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since
+ * changed CMD_PER_LUN... */
+
+/* Note: The Falcon currently uses 8/1 setting due to unsolved problems with
+ * cmd_per_lun != 1 */
+
+#define ATARI_TT_CAN_QUEUE 16
+#define ATARI_TT_CMD_PER_LUN 8
+#define ATARI_TT_SG_TABLESIZE SG_ALL
+
+#define ATARI_FALCON_CAN_QUEUE 8
+#define ATARI_FALCON_CMD_PER_LUN 1
+#define ATARI_FALCON_SG_TABLESIZE SG_NONE
+
+#define DEFAULT_USE_TAGGED_QUEUING 0
+
+
+#define NCR5380_implementation_fields /* none */
+
+#define NCR5380_read(reg) atari_scsi_reg_read( reg )
+#define NCR5380_write(reg, value) atari_scsi_reg_write( reg, value )
+
+#define NCR5380_intr atari_scsi_intr
+#define NCR5380_queue_command atari_scsi_queue_command
+#define NCR5380_abort atari_scsi_abort
+#define NCR5380_proc_info atari_scsi_proc_info
+#define NCR5380_dma_read_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 0)
+#define NCR5380_dma_write_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 1)
+#define NCR5380_dma_residual(inst) atari_scsi_dma_residual( inst )
+#define NCR5380_dma_xfer_len(i,cmd,phase) \
+ atari_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
+
+/* Debugging printk definitions:
+ *
+ * ARB -> arbitration
+ * ASEN -> auto-sense
+ * DMA -> DMA
+ * HSH -> PIO handshake
+ * INF -> information transfer
+ * INI -> initialization
+ * INT -> interrupt
+ * LNK -> linked commands
+ * MAIN -> NCR5380_main() control flow
+ * NDAT -> no data-out phase
+ * NWR -> no write commands
+ * PIO -> PIO transfers
+ * PDMA -> pseudo DMA (unused on Atari)
+ * QU -> queues
+ * RSL -> reselections
+ * SEL -> selections
+ * USL -> usleep cpde (unused on Atari)
+ * LBS -> last byte sent (unused on Atari)
+ * RSS -> restarting of selections
+ * EXT -> extended messages
+ * ABRT -> aborting and resetting
+ * TAG -> queue tag handling
+ * MER -> merging of consec. buffers
+ *
+ */
+
+#if NDEBUG & NDEBUG_ARBITRATION
+#define ARB_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ARB_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_AUTOSENSE
+#define ASEN_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ASEN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_DMA
+#define DMA_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define DMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_HANDSHAKE
+#define HSH_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define HSH_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INFORMATION
+#define INF_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INF_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INIT
+#define INI_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INI_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INTR
+#define INT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LINKED
+#define LNK_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define LNK_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MAIN
+#define MAIN_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define MAIN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_DATAOUT
+#define NDAT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define NDAT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_WRITE
+#define NWR_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define NWR_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PIO
+#define PIO_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define PIO_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PSEUDO_DMA
+#define PDMA_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define PDMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_QUEUES
+#define QU_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define QU_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESELECTION
+#define RSL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define RSL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_SELECTION
+#define SEL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define SEL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_USLEEP
+#define USL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define USL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LAST_BYTE_SENT
+#define LBS_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define LBS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESTART_SELECT
+#define RSS_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define RSS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_EXTENDED
+#define EXT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define EXT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_ABORT
+#define ABRT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ABRT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_TAGS
+#define TAG_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define TAG_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MERGING
+#define MER_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define MER_PRINTK(format, args...)
+#endif
+
+/* conditional macros for NCR5380_print_{,phase,status} */
+
+#define NCR_PRINT(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0)
+
+#define NCR_PRINT_PHASE(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0)
+
+#define NCR_PRINT_STATUS(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
+
+
+#endif /* ndef ASM */
+#endif /* ATARI_SCSI_H */
+
+
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
new file mode 100644
index 000000000000..45b75ddacaab
--- /dev/null
+++ b/drivers/scsi/atp870u.c
@@ -0,0 +1,3970 @@
+/*
+ * Copyright (C) 1997 Wu Ching Chen
+ * 2.1.x update (C) 1998 Krzysztof G. Baranowski
+ * 2.5.x update (C) 2002 Red Hat <alan@redhat.com>
+ * 2.6.x update (C) 2004 Red Hat <alan@redhat.com>
+ *
+ * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
+ *
+ * Wu Ching Chen : NULL pointer fixes 2000/06/02
+ * support atp876 chip
+ * enable 32 bit fifo transfer
+ * support cdrom & remove device run ultra speed
+ * fix disconnect bug 2000/12/21
+ * support atp880 chip lvd u160 2001/05/15
+ * fix prd table bug 2001/09/12 (7.1)
+ *
+ * atp885 support add by ACARD Hao Ping Lian 2005/01/05
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/blkdev.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "atp870u.h"
+
+static struct scsi_host_template atp870u_template;
+static void send_s870(struct atp_unit *dev,unsigned char c);
+static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c);
+static void tscam_885(void);
+
+static irqreturn_t atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ unsigned short int tmpcip, id;
+ unsigned char i, j, c, target_id, lun,cmdp;
+ unsigned char *prd;
+ struct scsi_cmnd *workreq;
+ unsigned int workport, tmport, tmport1;
+ unsigned long adrcnt, k;
+#ifdef ED_DBGP
+ unsigned long l;
+#endif
+ int errstus;
+ struct Scsi_Host *host = dev_id;
+ struct atp_unit *dev = (struct atp_unit *)&host->hostdata;
+
+ for (c = 0; c < 2; c++) {
+ tmport = dev->ioport[c] + 0x1f;
+ j = inb(tmport);
+ if ((j & 0x80) != 0)
+ {
+ goto ch_sel;
+ }
+ dev->in_int[c] = 0;
+ }
+ return IRQ_NONE;
+ch_sel:
+#ifdef ED_DBGP
+ printk("atp870u_intr_handle enter\n");
+#endif
+ dev->in_int[c] = 1;
+ cmdp = inb(dev->ioport[c] + 0x10);
+ workport = dev->ioport[c];
+ if (dev->working[c] != 0) {
+ if (dev->dev_id == ATP885_DEVID) {
+ tmport1 = workport + 0x16;
+ if ((inb(tmport1) & 0x80) == 0)
+ outb((inb(tmport1) | 0x80), tmport1);
+ }
+ tmpcip = dev->pciport[c];
+ if ((inb(tmpcip) & 0x08) != 0)
+ {
+ tmpcip += 0x2;
+ for (k=0; k < 1000; k++) {
+ if ((inb(tmpcip) & 0x08) == 0) {
+ goto stop_dma;
+ }
+ if ((inb(tmpcip) & 0x01) == 0) {
+ goto stop_dma;
+ }
+ }
+ }
+stop_dma:
+ tmpcip = dev->pciport[c];
+ outb(0x00, tmpcip);
+ tmport -= 0x08;
+
+ i = inb(tmport);
+
+ if (dev->dev_id == ATP885_DEVID) {
+ tmpcip += 2;
+ outb(0x06, tmpcip);
+ tmpcip -= 2;
+ }
+
+ tmport -= 0x02;
+ target_id = inb(tmport);
+ tmport += 0x02;
+
+ /*
+ * Remap wide devices onto id numbers
+ */
+
+ if ((target_id & 0x40) != 0) {
+ target_id = (target_id & 0x07) | 0x08;
+ } else {
+ target_id &= 0x07;
+ }
+
+ if ((j & 0x40) != 0) {
+ if (dev->last_cmd[c] == 0xff) {
+ dev->last_cmd[c] = target_id;
+ }
+ dev->last_cmd[c] |= 0x40;
+ }
+ if (dev->dev_id == ATP885_DEVID)
+ dev->r1f[c][target_id] |= j;
+#ifdef ED_DBGP
+ printk("atp870u_intr_handle status = %x\n",i);
+#endif
+ if (i == 0x85) {
+ if ((dev->last_cmd[c] & 0xf0) != 0x40) {
+ dev->last_cmd[c] = 0xff;
+ }
+ if (dev->dev_id == ATP885_DEVID) {
+ tmport -= 0x05;
+ adrcnt = 0;
+ ((unsigned char *) &adrcnt)[2] = inb(tmport++);
+ ((unsigned char *) &adrcnt)[1] = inb(tmport++);
+ ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ if (dev->id[c][target_id].last_len != adrcnt)
+ {
+ k = dev->id[c][target_id].last_len;
+ k -= adrcnt;
+ dev->id[c][target_id].tran_len = k;
+ dev->id[c][target_id].last_len = adrcnt;
+ }
+#ifdef ED_DBGP
+ printk("tmport = %x dev->id[c][target_id].last_len = %d dev->id[c][target_id].tran_len = %d\n",tmport,dev->id[c][target_id].last_len,dev->id[c][target_id].tran_len);
+#endif
+ }
+
+ /*
+ * Flip wide
+ */
+ if (dev->wide_id[c] != 0) {
+ tmport = workport + 0x1b;
+ outb(0x01, tmport);
+ while ((inb(tmport) & 0x01) != 0x01) {
+ outb(0x01, tmport);
+ }
+ }
+ /*
+ * Issue more commands
+ */
+ spin_lock_irqsave(dev->host->host_lock, flags);
+ if (((dev->quhd[c] != dev->quend[c]) || (dev->last_cmd[c] != 0xff)) &&
+ (dev->in_snd[c] == 0)) {
+#ifdef ED_DBGP
+ printk("Call sent_s870\n");
+#endif
+ send_s870(dev,c);
+ }
+ spin_unlock_irqrestore(dev->host->host_lock, flags);
+ /*
+ * Done
+ */
+ dev->in_int[c] = 0;
+#ifdef ED_DBGP
+ printk("Status 0x85 return\n");
+#endif
+ goto handled;
+ }
+
+ if (i == 0x40) {
+ dev->last_cmd[c] |= 0x40;
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+
+ if (i == 0x21) {
+ if ((dev->last_cmd[c] & 0xf0) != 0x40) {
+ dev->last_cmd[c] = 0xff;
+ }
+ tmport -= 0x05;
+ adrcnt = 0;
+ ((unsigned char *) &adrcnt)[2] = inb(tmport++);
+ ((unsigned char *) &adrcnt)[1] = inb(tmport++);
+ ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ k = dev->id[c][target_id].last_len;
+ k -= adrcnt;
+ dev->id[c][target_id].tran_len = k;
+ dev->id[c][target_id].last_len = adrcnt;
+ tmport -= 0x04;
+ outb(0x41, tmport);
+ tmport += 0x08;
+ outb(0x08, tmport);
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+
+ if (dev->dev_id == ATP885_DEVID) {
+ if ((i == 0x4c) || (i == 0x4d) || (i == 0x8c) || (i == 0x8d)) {
+ if ((i == 0x4c) || (i == 0x8c))
+ i=0x48;
+ else
+ i=0x49;
+ }
+
+ }
+ if ((i == 0x80) || (i == 0x8f)) {
+#ifdef ED_DBGP
+ printk(KERN_DEBUG "Device reselect\n");
+#endif
+ lun = 0;
+ tmport -= 0x07;
+ if (cmdp == 0x44 || i==0x80) {
+ tmport += 0x0d;
+ lun = inb(tmport) & 0x07;
+ } else {
+ if ((dev->last_cmd[c] & 0xf0) != 0x40) {
+ dev->last_cmd[c] = 0xff;
+ }
+ if (cmdp == 0x41) {
+#ifdef ED_DBGP
+ printk("cmdp = 0x41\n");
+#endif
+ tmport += 0x02;
+ adrcnt = 0;
+ ((unsigned char *) &adrcnt)[2] = inb(tmport++);
+ ((unsigned char *) &adrcnt)[1] = inb(tmport++);
+ ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ k = dev->id[c][target_id].last_len;
+ k -= adrcnt;
+ dev->id[c][target_id].tran_len = k;
+ dev->id[c][target_id].last_len = adrcnt;
+ tmport += 0x04;
+ outb(0x08, tmport);
+ dev->in_int[c] = 0;
+ goto handled;
+ } else {
+#ifdef ED_DBGP
+ printk("cmdp != 0x41\n");
+#endif
+ outb(0x46, tmport);
+ dev->id[c][target_id].dirct = 0x00;
+ tmport += 0x02;
+ outb(0x00, tmport++);
+ outb(0x00, tmport++);
+ outb(0x00, tmport++);
+ tmport += 0x03;
+ outb(0x08, tmport);
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+ }
+ if (dev->last_cmd[c] != 0xff) {
+ dev->last_cmd[c] |= 0x40;
+ }
+ if (dev->dev_id == ATP885_DEVID) {
+ j = inb(dev->baseport + 0x29) & 0xfe;
+ outb(j, dev->baseport + 0x29);
+ tmport = workport + 0x16;
+ } else {
+ tmport = workport + 0x10;
+ outb(0x45, tmport);
+ tmport += 0x06;
+ }
+
+ target_id = inb(tmport);
+ /*
+ * Remap wide identifiers
+ */
+ if ((target_id & 0x10) != 0) {
+ target_id = (target_id & 0x07) | 0x08;
+ } else {
+ target_id &= 0x07;
+ }
+ if (dev->dev_id == ATP885_DEVID) {
+ tmport = workport + 0x10;
+ outb(0x45, tmport);
+ }
+ workreq = dev->id[c][target_id].curr_req;
+#ifdef ED_DBGP
+ printk(KERN_DEBUG "Channel = %d ID = %d LUN = %d CDB",c,workreq->device->id,workreq->device->lun);
+ for(l=0;l<workreq->cmd_len;l++)
+ {
+ printk(KERN_DEBUG " %x",workreq->cmnd[l]);
+ }
+#endif
+
+ tmport = workport + 0x0f;
+ outb(lun, tmport);
+ tmport += 0x02;
+ outb(dev->id[c][target_id].devsp, tmport++);
+ adrcnt = dev->id[c][target_id].tran_len;
+ k = dev->id[c][target_id].last_len;
+
+ outb(((unsigned char *) &k)[2], tmport++);
+ outb(((unsigned char *) &k)[1], tmport++);
+ outb(((unsigned char *) &k)[0], tmport++);
+#ifdef ED_DBGP
+ printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k, inb(tmport-1), inb(tmport-2), inb(tmport-3));
+#endif
+ /* Remap wide */
+ j = target_id;
+ if (target_id > 7) {
+ j = (j & 0x07) | 0x40;
+ }
+ /* Add direction */
+ j |= dev->id[c][target_id].dirct;
+ outb(j, tmport++);
+ outb(0x80,tmport);
+
+ /* enable 32 bit fifo transfer */
+ if (dev->dev_id == ATP885_DEVID) {
+ tmpcip = dev->pciport[c] + 1;
+ i=inb(tmpcip) & 0xf3;
+ //j=workreq->cmnd[0];
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ i |= 0x0c;
+ }
+ outb(i,tmpcip);
+ } else if ((dev->dev_id == ATP880_DEVID1) ||
+ (dev->dev_id == ATP880_DEVID2) ) {
+ tmport = workport - 0x05;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);
+ } else {
+ outb((unsigned char) (inb(tmport) & 0x3f), tmport);
+ }
+ } else {
+ tmport = workport + 0x3a;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ outb((unsigned char) ((inb(tmport) & 0xf3) | 0x08), tmport);
+ } else {
+ outb((unsigned char) (inb(tmport) & 0xf3), tmport);
+ }
+ }
+ tmport = workport + 0x1b;
+ j = 0;
+ id = 1;
+ id = id << target_id;
+ /*
+ * Is this a wide device
+ */
+ if ((id & dev->wide_id[c]) != 0) {
+ j |= 0x01;
+ }
+ outb(j, tmport);
+ while ((inb(tmport) & 0x01) != j) {
+ outb(j,tmport);
+ }
+ if (dev->id[c][target_id].last_len == 0) {
+ tmport = workport + 0x18;
+ outb(0x08, tmport);
+ dev->in_int[c] = 0;
+#ifdef ED_DBGP
+ printk("dev->id[c][target_id].last_len = 0\n");
+#endif
+ goto handled;
+ }
+#ifdef ED_DBGP
+ printk("target_id = %d adrcnt = %d\n",target_id,adrcnt);
+#endif
+ prd = dev->id[c][target_id].prd_pos;
+ while (adrcnt != 0) {
+ id = ((unsigned short int *)prd)[2];
+ if (id == 0) {
+ k = 0x10000;
+ } else {
+ k = id;
+ }
+ if (k > adrcnt) {
+ ((unsigned short int *)prd)[2] = (unsigned short int)
+ (k - adrcnt);
+ ((unsigned long *)prd)[0] += adrcnt;
+ adrcnt = 0;
+ dev->id[c][target_id].prd_pos = prd;
+ } else {
+ adrcnt -= k;
+ dev->id[c][target_id].prdaddr += 0x08;
+ prd += 0x08;
+ if (adrcnt == 0) {
+ dev->id[c][target_id].prd_pos = prd;
+ }
+ }
+ }
+ tmpcip = dev->pciport[c] + 0x04;
+ outl(dev->id[c][target_id].prdaddr, tmpcip);
+#ifdef ED_DBGP
+ printk("dev->id[%d][%d].prdaddr 0x%8x\n", c, target_id, dev->id[c][target_id].prdaddr);
+#endif
+ if (dev->dev_id == ATP885_DEVID) {
+ tmpcip -= 0x04;
+ } else {
+ tmpcip -= 0x02;
+ outb(0x06, tmpcip);
+ outb(0x00, tmpcip);
+ tmpcip -= 0x02;
+ }
+ tmport = workport + 0x18;
+ /*
+ * Check transfer direction
+ */
+ if (dev->id[c][target_id].dirct != 0) {
+ outb(0x08, tmport);
+ outb(0x01, tmpcip);
+ dev->in_int[c] = 0;
+#ifdef ED_DBGP
+ printk("status 0x80 return dirct != 0\n");
+#endif
+ goto handled;
+ }
+ outb(0x08, tmport);
+ outb(0x09, tmpcip);
+ dev->in_int[c] = 0;
+#ifdef ED_DBGP
+ printk("status 0x80 return dirct = 0\n");
+#endif
+ goto handled;
+ }
+
+ /*
+ * Current scsi request on this target
+ */
+
+ workreq = dev->id[c][target_id].curr_req;
+
+ if (i == 0x42) {
+ if ((dev->last_cmd[c] & 0xf0) != 0x40)
+ {
+ dev->last_cmd[c] = 0xff;
+ }
+ errstus = 0x02;
+ workreq->result = errstus;
+ goto go_42;
+ }
+ if (i == 0x16) {
+ if ((dev->last_cmd[c] & 0xf0) != 0x40) {
+ dev->last_cmd[c] = 0xff;
+ }
+ errstus = 0;
+ tmport -= 0x08;
+ errstus = inb(tmport);
+ if (((dev->r1f[c][target_id] & 0x10) != 0)&&(dev->dev_id==ATP885_DEVID)) {
+ printk(KERN_WARNING "AEC67162 CRC ERROR !\n");
+ errstus = 0x02;
+ }
+ workreq->result = errstus;
+go_42:
+ if (dev->dev_id == ATP885_DEVID) {
+ j = inb(dev->baseport + 0x29) | 0x01;
+ outb(j, dev->baseport + 0x29);
+ }
+ /*
+ * Complete the command
+ */
+ if (workreq->use_sg) {
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)workreq->buffer,
+ workreq->use_sg,
+ workreq->sc_data_direction);
+ } else if (workreq->request_bufflen &&
+ workreq->sc_data_direction != DMA_NONE) {
+ pci_unmap_single(dev->pdev,
+ workreq->SCp.dma_handle,
+ workreq->request_bufflen,
+ workreq->sc_data_direction);
+ }
+ spin_lock_irqsave(dev->host->host_lock, flags);
+ (*workreq->scsi_done) (workreq);
+#ifdef ED_DBGP
+ printk("workreq->scsi_done\n");
+#endif
+ /*
+ * Clear it off the queue
+ */
+ dev->id[c][target_id].curr_req = NULL;
+ dev->working[c]--;
+ spin_unlock_irqrestore(dev->host->host_lock, flags);
+ /*
+ * Take it back wide
+ */
+ if (dev->wide_id[c] != 0) {
+ tmport = workport + 0x1b;
+ outb(0x01, tmport);
+ while ((inb(tmport) & 0x01) != 0x01) {
+ outb(0x01, tmport);
+ }
+ }
+ /*
+ * If there is stuff to send and nothing going then send it
+ */
+ spin_lock_irqsave(dev->host->host_lock, flags);
+ if (((dev->last_cmd[c] != 0xff) || (dev->quhd[c] != dev->quend[c])) &&
+ (dev->in_snd[c] == 0)) {
+#ifdef ED_DBGP
+ printk("Call sent_s870(scsi_done)\n");
+#endif
+ send_s870(dev,c);
+ }
+ spin_unlock_irqrestore(dev->host->host_lock, flags);
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+ if ((dev->last_cmd[c] & 0xf0) != 0x40) {
+ dev->last_cmd[c] = 0xff;
+ }
+ if (i == 0x4f) {
+ i = 0x89;
+ }
+ i &= 0x0f;
+ if (i == 0x09) {
+ tmpcip += 4;
+ outl(dev->id[c][target_id].prdaddr, tmpcip);
+ tmpcip = tmpcip - 2;
+ outb(0x06, tmpcip);
+ outb(0x00, tmpcip);
+ tmpcip = tmpcip - 2;
+ tmport = workport + 0x10;
+ outb(0x41, tmport);
+ if (dev->dev_id == ATP885_DEVID) {
+ tmport += 2;
+ k = dev->id[c][target_id].last_len;
+ outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);
+ outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);
+ outb((unsigned char) (((unsigned char *) (&k))[0]), tmport);
+ dev->id[c][target_id].dirct = 0x00;
+ tmport += 0x04;
+ } else {
+ dev->id[c][target_id].dirct = 0x00;
+ tmport += 0x08;
+ }
+ outb(0x08, tmport);
+ outb(0x09, tmpcip);
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+ if (i == 0x08) {
+ tmpcip += 4;
+ outl(dev->id[c][target_id].prdaddr, tmpcip);
+ tmpcip = tmpcip - 2;
+ outb(0x06, tmpcip);
+ outb(0x00, tmpcip);
+ tmpcip = tmpcip - 2;
+ tmport = workport + 0x10;
+ outb(0x41, tmport);
+ if (dev->dev_id == ATP885_DEVID) {
+ tmport += 2;
+ k = dev->id[c][target_id].last_len;
+ outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);
+ outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);
+ outb((unsigned char) (((unsigned char *) (&k))[0]), tmport++);
+ } else {
+ tmport += 5;
+ }
+ outb((unsigned char) (inb(tmport) | 0x20), tmport);
+ dev->id[c][target_id].dirct = 0x20;
+ tmport += 0x03;
+ outb(0x08, tmport);
+ outb(0x01, tmpcip);
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+ tmport -= 0x07;
+ if (i == 0x0a) {
+ outb(0x30, tmport);
+ } else {
+ outb(0x46, tmport);
+ }
+ dev->id[c][target_id].dirct = 0x00;
+ tmport += 0x02;
+ outb(0x00, tmport++);
+ outb(0x00, tmport++);
+ outb(0x00, tmport++);
+ tmport += 0x03;
+ outb(0x08, tmport);
+ dev->in_int[c] = 0;
+ goto handled;
+ } else {
+// tmport = workport + 0x17;
+// inb(tmport);
+// dev->working[c] = 0;
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+
+handled:
+#ifdef ED_DBGP
+ printk("atp870u_intr_handle exit\n");
+#endif
+ return IRQ_HANDLED;
+}
+/**
+ * atp870u_queuecommand - Queue SCSI command
+ * @req_p: request block
+ * @done: completion function
+ *
+ * Queue a command to the ATP queue. Called with the host lock held.
+ */
+static int atp870u_queuecommand(struct scsi_cmnd * req_p,
+ void (*done) (struct scsi_cmnd *))
+{
+ unsigned char c;
+ unsigned int tmport,m;
+ struct atp_unit *dev;
+ struct Scsi_Host *host;
+
+ c = req_p->device->channel;
+ req_p->sense_buffer[0]=0;
+ req_p->resid = 0;
+ if (req_p->device->channel > 1) {
+ req_p->result = 0x00040000;
+ done(req_p);
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : req_p->device->channel > 1\n");
+#endif
+ return 0;
+ }
+
+ host = req_p->device->host;
+ dev = (struct atp_unit *)&host->hostdata;
+
+
+
+ m = 1;
+ m = m << req_p->device->id;
+
+ /*
+ * Fake a timeout for missing targets
+ */
+
+ if ((m & dev->active_id[c]) == 0) {
+ req_p->result = 0x00040000;
+ done(req_p);
+ return 0;
+ }
+
+ if (done) {
+ req_p->scsi_done = done;
+ } else {
+#ifdef ED_DBGP
+ printk( "atp870u_queuecommand: done can't be NULL\n");
+#endif
+ req_p->result = 0;
+ done(req_p);
+ return 0;
+ }
+
+ /*
+ * Count new command
+ */
+ dev->quend[c]++;
+ if (dev->quend[c] >= qcnt) {
+ dev->quend[c] = 0;
+ }
+
+ /*
+ * Check queue state
+ */
+ if (dev->quhd[c] == dev->quend[c]) {
+ if (dev->quend[c] == 0) {
+ dev->quend[c] = qcnt;
+ }
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : dev->quhd[c] == dev->quend[c]\n");
+#endif
+ dev->quend[c]--;
+ req_p->result = 0x00020000;
+ done(req_p);
+ return 0;
+ }
+ dev->quereq[c][dev->quend[c]] = req_p;
+ tmport = dev->ioport[c] + 0x1c;
+#ifdef ED_DBGP
+ printk("dev->ioport[c] = %x inb(tmport) = %x dev->in_int[%d] = %d dev->in_snd[%d] = %d\n",dev->ioport[c],inb(tmport),c,dev->in_int[c],c,dev->in_snd[c]);
+#endif
+ if ((inb(tmport) == 0) && (dev->in_int[c] == 0) && (dev->in_snd[c] == 0)) {
+#ifdef ED_DBGP
+ printk("Call sent_s870(atp870u_queuecommand)\n");
+#endif
+ send_s870(dev,c);
+ }
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : exit\n");
+#endif
+ return 0;
+}
+
+/**
+ * send_s870 - send a command to the controller
+ * @host: host
+ *
+ * On entry there is work queued to be done. We move some of that work to the
+ * controller itself.
+ *
+ * Caller holds the host lock.
+ */
+static void send_s870(struct atp_unit *dev,unsigned char c)
+{
+ unsigned int tmport;
+ struct scsi_cmnd *workreq;
+ unsigned int i;//,k;
+ unsigned char j, target_id;
+ unsigned char *prd;
+ unsigned short int tmpcip, w;
+ unsigned long l, bttl = 0;
+ unsigned int workport;
+ struct scatterlist *sgpnt;
+ unsigned long sg_count;
+
+ if (dev->in_snd[c] != 0) {
+#ifdef ED_DBGP
+ printk("cmnd in_snd\n");
+#endif
+ return;
+ }
+#ifdef ED_DBGP
+ printk("Sent_s870 enter\n");
+#endif
+ dev->in_snd[c] = 1;
+ if ((dev->last_cmd[c] != 0xff) && ((dev->last_cmd[c] & 0x40) != 0)) {
+ dev->last_cmd[c] &= 0x0f;
+ workreq = dev->id[c][dev->last_cmd[c]].curr_req;
+ if (workreq != NULL) { /* check NULL pointer */
+ goto cmd_subp;
+ }
+ dev->last_cmd[c] = 0xff;
+ if (dev->quhd[c] == dev->quend[c]) {
+ dev->in_snd[c] = 0;
+ return ;
+ }
+ }
+ if ((dev->last_cmd[c] != 0xff) && (dev->working[c] != 0)) {
+ dev->in_snd[c] = 0;
+ return ;
+ }
+ dev->working[c]++;
+ j = dev->quhd[c];
+ dev->quhd[c]++;
+ if (dev->quhd[c] >= qcnt) {
+ dev->quhd[c] = 0;
+ }
+ workreq = dev->quereq[c][dev->quhd[c]];
+ if (dev->id[c][workreq->device->id].curr_req == 0) {
+ dev->id[c][workreq->device->id].curr_req = workreq;
+ dev->last_cmd[c] = workreq->device->id;
+ goto cmd_subp;
+ }
+ dev->quhd[c] = j;
+ dev->working[c]--;
+ dev->in_snd[c] = 0;
+ return;
+cmd_subp:
+ workport = dev->ioport[c];
+ tmport = workport + 0x1f;
+ if ((inb(tmport) & 0xb0) != 0) {
+ goto abortsnd;
+ }
+ tmport = workport + 0x1c;
+ if (inb(tmport) == 0) {
+ goto oktosend;
+ }
+abortsnd:
+#ifdef ED_DBGP
+ printk("Abort to Send\n");
+#endif
+ dev->last_cmd[c] |= 0x40;
+ dev->in_snd[c] = 0;
+ return;
+oktosend:
+#ifdef ED_DBGP
+ printk("OK to Send\n");
+ printk("CDB");
+ for(i=0;i<workreq->cmd_len;i++) {
+ printk(" %x",workreq->cmnd[i]);
+ }
+ printk("\nChannel = %d ID = %d LUN = %d\n",c,workreq->device->id,workreq->device->lun);
+#endif
+ if (dev->dev_id == ATP885_DEVID) {
+ j = inb(dev->baseport + 0x29) & 0xfe;
+ outb(j, dev->baseport + 0x29);
+ dev->r1f[c][workreq->device->id] = 0;
+ }
+
+ if (workreq->cmnd[0] == READ_CAPACITY) {
+ if (workreq->request_bufflen > 8) {
+ workreq->request_bufflen = 0x08;
+ }
+ }
+ if (workreq->cmnd[0] == 0x00) {
+ workreq->request_bufflen = 0;
+ }
+
+ tmport = workport + 0x1b;
+ j = 0;
+ target_id = workreq->device->id;
+
+ /*
+ * Wide ?
+ */
+ w = 1;
+ w = w << target_id;
+ if ((w & dev->wide_id[c]) != 0) {
+ j |= 0x01;
+ }
+ outb(j, tmport);
+ while ((inb(tmport) & 0x01) != j) {
+ outb(j,tmport);
+#ifdef ED_DBGP
+ printk("send_s870 while loop 1\n");
+#endif
+ }
+ /*
+ * Write the command
+ */
+
+ tmport = workport;
+ outb(workreq->cmd_len, tmport++);
+ outb(0x2c, tmport++);
+ if (dev->dev_id == ATP885_DEVID) {
+ outb(0x7f, tmport++);
+ } else {
+ outb(0xcf, tmport++);
+ }
+ for (i = 0; i < workreq->cmd_len; i++) {
+ outb(workreq->cmnd[i], tmport++);
+ }
+ tmport = workport + 0x0f;
+ outb(workreq->device->lun, tmport);
+ tmport += 0x02;
+ /*
+ * Write the target
+ */
+ outb(dev->id[c][target_id].devsp, tmport++);
+#ifdef ED_DBGP
+ printk("dev->id[%d][%d].devsp = %2x\n",c,target_id,dev->id[c][target_id].devsp);
+#endif
+ /*
+ * Figure out the transfer size
+ */
+ if (workreq->use_sg) {
+#ifdef ED_DBGP
+ printk("Using SGL\n");
+#endif
+ l = 0;
+
+ sgpnt = (struct scatterlist *) workreq->request_buffer;
+ sg_count = pci_map_sg(dev->pdev, sgpnt, workreq->use_sg,
+ workreq->sc_data_direction);
+
+ for (i = 0; i < workreq->use_sg; i++) {
+ if (sgpnt[i].length == 0 || workreq->use_sg > ATP870U_SCATTER) {
+ panic("Foooooooood fight!");
+ }
+ l += sgpnt[i].length;
+ }
+#ifdef ED_DBGP
+ printk( "send_s870: workreq->use_sg %d, sg_count %d l %8ld\n", workreq->use_sg, sg_count, l);
+#endif
+ } else if(workreq->request_bufflen && workreq->sc_data_direction != PCI_DMA_NONE) {
+#ifdef ED_DBGP
+ printk("Not using SGL\n");
+#endif
+ workreq->SCp.dma_handle = pci_map_single(dev->pdev, workreq->request_buffer,
+ workreq->request_bufflen,
+ workreq->sc_data_direction);
+ l = workreq->request_bufflen;
+#ifdef ED_DBGP
+ printk( "send_s870: workreq->use_sg %d, l %8ld\n", workreq->use_sg, l);
+#endif
+ } else l = 0;
+ /*
+ * Write transfer size
+ */
+ outb((unsigned char) (((unsigned char *) (&l))[2]), tmport++);
+ outb((unsigned char) (((unsigned char *) (&l))[1]), tmport++);
+ outb((unsigned char) (((unsigned char *) (&l))[0]), tmport++);
+ j = target_id;
+ dev->id[c][j].last_len = l;
+ dev->id[c][j].tran_len = 0;
+#ifdef ED_DBGP
+ printk("dev->id[%2d][%2d].last_len = %d\n",c,j,dev->id[c][j].last_len);
+#endif
+ /*
+ * Flip the wide bits
+ */
+ if ((j & 0x08) != 0) {
+ j = (j & 0x07) | 0x40;
+ }
+ /*
+ * Check transfer direction
+ */
+ if (workreq->sc_data_direction == DMA_TO_DEVICE) {
+ outb((unsigned char) (j | 0x20), tmport++);
+ } else {
+ outb(j, tmport++);
+ }
+ outb((unsigned char) (inb(tmport) | 0x80), tmport);
+ outb(0x80, tmport);
+ tmport = workport + 0x1c;
+ dev->id[c][target_id].dirct = 0;
+ if (l == 0) {
+ if (inb(tmport) == 0) {
+ tmport = workport + 0x18;
+#ifdef ED_DBGP
+ printk("change SCSI_CMD_REG 0x08\n");
+#endif
+ outb(0x08, tmport);
+ } else {
+ dev->last_cmd[c] |= 0x40;
+ }
+ dev->in_snd[c] = 0;
+ return;
+ }
+ tmpcip = dev->pciport[c];
+ prd = dev->id[c][target_id].prd_table;
+ dev->id[c][target_id].prd_pos = prd;
+
+ /*
+ * Now write the request list. Either as scatter/gather or as
+ * a linear chain.
+ */
+
+ if (workreq->use_sg) {
+ sgpnt = (struct scatterlist *) workreq->request_buffer;
+ i = 0;
+ for (j = 0; j < workreq->use_sg; j++) {
+ bttl = sg_dma_address(&sgpnt[j]);
+ l=sg_dma_len(&sgpnt[j]);
+#ifdef ED_DBGP
+ printk("1. bttl %x, l %x\n",bttl, l);
+#endif
+ while (l > 0x10000) {
+ (((u16 *) (prd))[i + 3]) = 0x0000;
+ (((u16 *) (prd))[i + 2]) = 0x0000;
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+ l -= 0x10000;
+ bttl += 0x10000;
+ i += 0x04;
+ }
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+ (((u16 *) (prd))[i + 2]) = cpu_to_le16(l);
+ (((u16 *) (prd))[i + 3]) = 0;
+ i += 0x04;
+ }
+ (((u16 *) (prd))[i - 1]) = cpu_to_le16(0x8000);
+#ifdef ED_DBGP
+ printk("prd %4x %4x %4x %4x\n",(((unsigned short int *)prd)[0]),(((unsigned short int *)prd)[1]),(((unsigned short int *)prd)[2]),(((unsigned short int *)prd)[3]));
+ printk("2. bttl %x, l %x\n",bttl, l);
+#endif
+ } else {
+ /*
+ * For a linear request write a chain of blocks
+ */
+ bttl = workreq->SCp.dma_handle;
+ l = workreq->request_bufflen;
+ i = 0;
+#ifdef ED_DBGP
+ printk("3. bttl %x, l %x\n",bttl, l);
+#endif
+ while (l > 0x10000) {
+ (((u16 *) (prd))[i + 3]) = 0x0000;
+ (((u16 *) (prd))[i + 2]) = 0x0000;
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+ l -= 0x10000;
+ bttl += 0x10000;
+ i += 0x04;
+ }
+ (((u16 *) (prd))[i + 3]) = cpu_to_le16(0x8000);
+ (((u16 *) (prd))[i + 2]) = cpu_to_le16(l);
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+#ifdef ED_DBGP
+ printk("prd %4x %4x %4x %4x\n",(((unsigned short int *)prd)[0]),(((unsigned short int *)prd)[1]),(((unsigned short int *)prd)[2]),(((unsigned short int *)prd)[3]));
+ printk("4. bttl %x, l %x\n",bttl, l);
+#endif
+
+ }
+ tmpcip += 4;
+#ifdef ED_DBGP
+ printk("send_s870: prdaddr_2 0x%8x tmpcip %x target_id %d\n", dev->id[c][target_id].prdaddr,tmpcip,target_id);
+#endif
+ outl(dev->id[c][target_id].prdaddr, tmpcip);
+ tmpcip = tmpcip - 2;
+ outb(0x06, tmpcip);
+ outb(0x00, tmpcip);
+ if (dev->dev_id == ATP885_DEVID) {
+ tmpcip--;
+ j=inb(tmpcip) & 0xf3;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) ||
+ (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ j |= 0x0c;
+ }
+ outb(j,tmpcip);
+ tmpcip--;
+ } else if ((dev->dev_id == ATP880_DEVID1) ||
+ (dev->dev_id == ATP880_DEVID2)) {
+ tmpcip =tmpcip -2;
+ tmport = workport - 0x05;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);
+ } else {
+ outb((unsigned char) (inb(tmport) & 0x3f), tmport);
+ }
+ } else {
+ tmpcip =tmpcip -2;
+ tmport = workport + 0x3a;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ outb((inb(tmport) & 0xf3) | 0x08, tmport);
+ } else {
+ outb(inb(tmport) & 0xf3, tmport);
+ }
+ }
+ tmport = workport + 0x1c;
+
+ if(workreq->sc_data_direction == DMA_TO_DEVICE) {
+ dev->id[c][target_id].dirct = 0x20;
+ if (inb(tmport) == 0) {
+ tmport = workport + 0x18;
+ outb(0x08, tmport);
+ outb(0x01, tmpcip);
+#ifdef ED_DBGP
+ printk( "start DMA(to target)\n");
+#endif
+ } else {
+ dev->last_cmd[c] |= 0x40;
+ }
+ dev->in_snd[c] = 0;
+ return;
+ }
+ if (inb(tmport) == 0) {
+ tmport = workport + 0x18;
+ outb(0x08, tmport);
+ outb(0x09, tmpcip);
+#ifdef ED_DBGP
+ printk( "start DMA(to host)\n");
+#endif
+ } else {
+ dev->last_cmd[c] |= 0x40;
+ }
+ dev->in_snd[c] = 0;
+ return;
+
+}
+
+static unsigned char fun_scam(struct atp_unit *dev, unsigned short int *val)
+{
+ unsigned int tmport;
+ unsigned short int i, k;
+ unsigned char j;
+
+ tmport = dev->ioport[0] + 0x1c;
+ outw(*val, tmport);
+FUN_D7:
+ for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
+ k = inw(tmport);
+ j = (unsigned char) (k >> 8);
+ if ((k & 0x8000) != 0) { /* DB7 all release? */
+ goto FUN_D7;
+ }
+ }
+ *val |= 0x4000; /* assert DB6 */
+ outw(*val, tmport);
+ *val &= 0xdfff; /* assert DB5 */
+ outw(*val, tmport);
+FUN_D5:
+ for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
+ if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */
+ goto FUN_D5;
+ }
+ }
+ *val |= 0x8000; /* no DB4-0, assert DB7 */
+ *val &= 0xe0ff;
+ outw(*val, tmport);
+ *val &= 0xbfff; /* release DB6 */
+ outw(*val, tmport);
+FUN_D6:
+ for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
+ if ((inw(tmport) & 0x4000) != 0) { /* DB6 all release? */
+ goto FUN_D6;
+ }
+ }
+
+ return j;
+}
+
+static void tscam(struct Scsi_Host *host)
+{
+
+ unsigned int tmport;
+ unsigned char i, j, k;
+ unsigned long n;
+ unsigned short int m, assignid_map, val;
+ unsigned char mbuf[33], quintet[2];
+ struct atp_unit *dev = (struct atp_unit *)&host->hostdata;
+ static unsigned char g2q_tab[8] = {
+ 0x38, 0x31, 0x32, 0x2b, 0x34, 0x2d, 0x2e, 0x27
+ };
+
+/* I can't believe we need this before we've even done anything. Remove it
+ * and see if anyone bitches.
+ for (i = 0; i < 0x10; i++) {
+ udelay(0xffff);
+ }
+ */
+
+ tmport = dev->ioport[0] + 1;
+ outb(0x08, tmport++);
+ outb(0x7f, tmport);
+ tmport = dev->ioport[0] + 0x11;
+ outb(0x20, tmport);
+
+ if ((dev->scam_on & 0x40) == 0) {
+ return;
+ }
+ m = 1;
+ m <<= dev->host_id[0];
+ j = 16;
+ if (dev->chip_ver < 4) {
+ m |= 0xff00;
+ j = 8;
+ }
+ assignid_map = m;
+ tmport = dev->ioport[0] + 0x02;
+ outb(0x02, tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+
+ for (i = 0; i < j; i++) {
+ m = 1;
+ m = m << i;
+ if ((m & assignid_map) != 0) {
+ continue;
+ }
+ tmport = dev->ioport[0] + 0x0f;
+ outb(0, tmport++);
+ tmport += 0x02;
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ if (i > 7) {
+ k = (i & 0x07) | 0x40;
+ } else {
+ k = i;
+ }
+ outb(k, tmport++);
+ tmport = dev->ioport[0] + 0x1b;
+ if (dev->chip_ver == 4) {
+ outb(0x01, tmport);
+ } else {
+ outb(0x00, tmport);
+ }
+wait_rdyok:
+ tmport = dev->ioport[0] + 0x18;
+ outb(0x09, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ k = inb(tmport);
+ if (k != 0x16) {
+ if ((k == 0x85) || (k == 0x42)) {
+ continue;
+ }
+ tmport = dev->ioport[0] + 0x10;
+ outb(0x41, tmport);
+ goto wait_rdyok;
+ }
+ assignid_map |= m;
+
+ }
+ tmport = dev->ioport[0] + 0x02;
+ outb(0x7f, tmport);
+ tmport = dev->ioport[0] + 0x1b;
+ outb(0x02, tmport);
+
+ outb(0, 0x80);
+
+ val = 0x0080; /* bsy */
+ tmport = dev->ioport[0] + 0x1c;
+ outw(val, tmport);
+ val |= 0x0040; /* sel */
+ outw(val, tmport);
+ val |= 0x0004; /* msg */
+ outw(val, tmport);
+ inb(0x80); /* 2 deskew delay(45ns*2=90ns) */
+ val &= 0x007f; /* no bsy */
+ outw(val, tmport);
+ mdelay(128);
+ val &= 0x00fb; /* after 1ms no msg */
+ outw(val, tmport);
+wait_nomsg:
+ if ((inb(tmport) & 0x04) != 0) {
+ goto wait_nomsg;
+ }
+ outb(1, 0x80);
+ udelay(100);
+ for (n = 0; n < 0x30000; n++) {
+ if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
+ goto wait_io;
+ }
+ }
+ goto TCM_SYNC;
+wait_io:
+ for (n = 0; n < 0x30000; n++) {
+ if ((inb(tmport) & 0x81) == 0x0081) {
+ goto wait_io1;
+ }
+ }
+ goto TCM_SYNC;
+wait_io1:
+ inb(0x80);
+ val |= 0x8003; /* io,cd,db7 */
+ outw(val, tmport);
+ inb(0x80);
+ val &= 0x00bf; /* no sel */
+ outw(val, tmport);
+ outb(2, 0x80);
+TCM_SYNC:
+ udelay(0x800);
+ if ((inb(tmport) & 0x80) == 0x00) { /* bsy ? */
+ outw(0, tmport--);
+ outb(0, tmport);
+ tmport = dev->ioport[0] + 0x15;
+ outb(0, tmport);
+ tmport += 0x03;
+ outb(0x09, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0)
+ cpu_relax();
+ tmport -= 0x08;
+ inb(tmport);
+ return;
+ }
+ val &= 0x00ff; /* synchronization */
+ val |= 0x3f00;
+ fun_scam(dev, &val);
+ outb(3, 0x80);
+ val &= 0x00ff; /* isolation */
+ val |= 0x2000;
+ fun_scam(dev, &val);
+ outb(4, 0x80);
+ i = 8;
+ j = 0;
+TCM_ID:
+ if ((inw(tmport) & 0x2000) == 0) {
+ goto TCM_ID;
+ }
+ outb(5, 0x80);
+ val &= 0x00ff; /* get ID_STRING */
+ val |= 0x2000;
+ k = fun_scam(dev, &val);
+ if ((k & 0x03) == 0) {
+ goto TCM_5;
+ }
+ mbuf[j] <<= 0x01;
+ mbuf[j] &= 0xfe;
+ if ((k & 0x02) != 0) {
+ mbuf[j] |= 0x01;
+ }
+ i--;
+ if (i > 0) {
+ goto TCM_ID;
+ }
+ j++;
+ i = 8;
+ goto TCM_ID;
+
+TCM_5: /* isolation complete.. */
+/* mbuf[32]=0;
+ printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
+ i = 15;
+ j = mbuf[0];
+ if ((j & 0x20) != 0) { /* bit5=1:ID upto 7 */
+ i = 7;
+ }
+ if ((j & 0x06) == 0) { /* IDvalid? */
+ goto G2Q5;
+ }
+ k = mbuf[1];
+small_id:
+ m = 1;
+ m <<= k;
+ if ((m & assignid_map) == 0) {
+ goto G2Q_QUIN;
+ }
+ if (k > 0) {
+ k--;
+ goto small_id;
+ }
+G2Q5: /* srch from max acceptable ID# */
+ k = i; /* max acceptable ID# */
+G2Q_LP:
+ m = 1;
+ m <<= k;
+ if ((m & assignid_map) == 0) {
+ goto G2Q_QUIN;
+ }
+ if (k > 0) {
+ k--;
+ goto G2Q_LP;
+ }
+G2Q_QUIN: /* k=binID#, */
+ assignid_map |= m;
+ if (k < 8) {
+ quintet[0] = 0x38; /* 1st dft ID<8 */
+ } else {
+ quintet[0] = 0x31; /* 1st ID>=8 */
+ }
+ k &= 0x07;
+ quintet[1] = g2q_tab[k];
+
+ val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */
+ m = quintet[0] << 8;
+ val |= m;
+ fun_scam(dev, &val);
+ val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */
+ m = quintet[1] << 8;
+ val |= m;
+ fun_scam(dev, &val);
+
+ goto TCM_SYNC;
+
+}
+
+static void is870(struct atp_unit *dev, unsigned int wkport)
+{
+ unsigned int tmport;
+ unsigned char i, j, k, rmb, n;
+ unsigned short int m;
+ static unsigned char mbuf[512];
+ static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
+ static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
+ static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ static unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0c, 0x0e };
+ static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x0c, 0x07 };
+ static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
+
+ tmport = wkport + 0x3a;
+ outb((unsigned char) (inb(tmport) | 0x10), tmport);
+
+ for (i = 0; i < 16; i++) {
+ if ((dev->chip_ver != 4) && (i > 7)) {
+ break;
+ }
+ m = 1;
+ m = m << i;
+ if ((m & dev->active_id[0]) != 0) {
+ continue;
+ }
+ if (i == dev->host_id[0]) {
+ printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[0]);
+ continue;
+ }
+ tmport = wkport + 0x1b;
+ if (dev->chip_ver == 4) {
+ outb(0x01, tmport);
+ } else {
+ outb(0x00, tmport);
+ }
+ tmport = wkport + 1;
+ outb(0x08, tmport++);
+ outb(0x7f, tmport++);
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ j = i;
+ if ((j & 0x08) != 0) {
+ j = (j & 0x07) | 0x40;
+ }
+ outb(j, tmport);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+ dev->active_id[0] |= m;
+
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+ tmport = wkport + 0x04;
+ outb(0x00, tmport);
+
+phase_cmd:
+ tmport = wkport + 0x18;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ tmport = wkport + 0x10;
+ outb(0x41, tmport);
+ goto phase_cmd;
+ }
+sel_ok:
+ tmport = wkport + 3;
+ outb(inqd[0], tmport++);
+ outb(inqd[1], tmport++);
+ outb(inqd[2], tmport++);
+ outb(inqd[3], tmport++);
+ outb(inqd[4], tmport++);
+ outb(inqd[5], tmport);
+ tmport += 0x07;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(inqd[6], tmport++);
+ outb(inqd[7], tmport++);
+ tmport += 0x03;
+ outb(inqd[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+ tmport = wkport + 0x1b;
+ if (dev->chip_ver == 4)
+ outb(0x00, tmport);
+
+ tmport = wkport + 0x18;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ j = 0;
+rd_inq_data:
+ k = inb(tmport);
+ if ((k & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[j++] = inb(tmport);
+ tmport += 0x06;
+ goto rd_inq_data;
+ }
+ if ((k & 0x80) == 0) {
+ goto rd_inq_data;
+ }
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j == 0x16) {
+ goto inq_ok;
+ }
+ tmport = wkport + 0x10;
+ outb(0x46, tmport);
+ tmport += 0x02;
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ tmport += 0x03;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x16) {
+ goto sel_ok;
+ }
+inq_ok:
+ mbuf[36] = 0;
+ printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
+ dev->id[0][i].devtype = mbuf[0];
+ rmb = mbuf[1];
+ n = mbuf[7];
+ if (dev->chip_ver != 4) {
+ goto not_wide;
+ }
+ if ((mbuf[7] & 0x60) == 0) {
+ goto not_wide;
+ }
+ if ((dev->global_map[0] & 0x20) == 0) {
+ goto not_wide;
+ }
+ tmport = wkport + 0x1b;
+ outb(0x01, tmport);
+ tmport = wkport + 3;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+try_wide:
+ j = 0;
+ tmport = wkport + 0x14;
+ outb(0x05, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(wide[j++], tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto try_wide;
+ }
+ continue;
+widep_out:
+ tmport = wkport + 0x18;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(0, tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto widep_out;
+ }
+ continue;
+widep_in:
+ tmport = wkport + 0x14;
+ outb(0xff, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+widep_in1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto widep_in1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto widep_in1;
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto widep_out;
+ }
+ continue;
+widep_cmd:
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+ tmport = wkport + 0x14;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ if (j == 0x4e) {
+ goto widep_out;
+ }
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ goto not_wide;
+ }
+ if (mbuf[1] != 0x02) {
+ goto not_wide;
+ }
+ if (mbuf[2] != 0x03) {
+ goto not_wide;
+ }
+ if (mbuf[3] != 0x01) {
+ goto not_wide;
+ }
+ m = 1;
+ m = m << i;
+ dev->wide_id[0] |= m;
+not_wide:
+ if ((dev->id[0][i].devtype == 0x00) || (dev->id[0][i].devtype == 0x07) || ((dev->id[0][i].devtype == 0x05) && ((n & 0x10) != 0))) {
+ goto set_sync;
+ }
+ continue;
+set_sync:
+ tmport = wkport + 0x1b;
+ j = 0;
+ if ((m & dev->wide_id[0]) != 0) {
+ j |= 0x01;
+ }
+ outb(j, tmport);
+ tmport = wkport + 3;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+try_sync:
+ j = 0;
+ tmport = wkport + 0x14;
+ outb(0x06, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ if ((m & dev->wide_id[0]) != 0) {
+ outb(synw[j++], tmport);
+ } else {
+ if ((m & dev->ultra_map[0]) != 0) {
+ outb(synu[j++], tmport);
+ } else {
+ outb(synn[j++], tmport);
+ }
+ }
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto try_sync;
+ }
+ continue;
+phase_outs:
+ tmport = wkport + 0x18;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00) {
+ if ((inb(tmport) & 0x01) != 0x00) {
+ tmport -= 0x06;
+ outb(0x00, tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j == 0x85) {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto phase_outs;
+ }
+ continue;
+phase_ins:
+ tmport = wkport + 0x14;
+ outb(0xff, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+phase_ins1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0x00) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto phase_ins1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto phase_ins1;
+ }
+ tmport -= 0x08;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport);
+ if (j == 0x85) {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto phase_outs;
+ }
+ continue;
+phase_cmds:
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+tar_dcons:
+ tmport = wkport + 0x14;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ continue;
+ }
+ if (mbuf[1] != 0x03) {
+ continue;
+ }
+ if (mbuf[4] == 0x00) {
+ continue;
+ }
+ if (mbuf[3] > 0x64) {
+ continue;
+ }
+ if (mbuf[4] > 0x0c) {
+ mbuf[4] = 0x0c;
+ }
+ dev->id[0][i].devsp = mbuf[4];
+ if ((mbuf[3] < 0x0d) && (rmb == 0)) {
+ j = 0xa0;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x1a) {
+ j = 0x20;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x33) {
+ j = 0x40;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x4c) {
+ j = 0x50;
+ goto set_syn_ok;
+ }
+ j = 0x60;
+set_syn_ok:
+ dev->id[0][i].devsp = (dev->id[0][i].devsp & 0x0f) | j;
+ }
+ tmport = wkport + 0x3a;
+ outb((unsigned char) (inb(tmport) & 0xef), tmport);
+}
+
+static void is880(struct atp_unit *dev, unsigned int wkport)
+{
+ unsigned int tmport;
+ unsigned char i, j, k, rmb, n, lvdmode;
+ unsigned short int m;
+ static unsigned char mbuf[512];
+ static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
+ static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
+ static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
+ static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ unsigned char synuw[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
+ static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
+ static unsigned char u3[9] = { 0x80, 1, 6, 4, 0x09, 00, 0x0e, 0x01, 0x02 };
+
+ lvdmode = inb(wkport + 0x3f) & 0x40;
+
+ for (i = 0; i < 16; i++) {
+ m = 1;
+ m = m << i;
+ if ((m & dev->active_id[0]) != 0) {
+ continue;
+ }
+ if (i == dev->host_id[0]) {
+ printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[0]);
+ continue;
+ }
+ tmport = wkport + 0x5b;
+ outb(0x01, tmport);
+ tmport = wkport + 0x41;
+ outb(0x08, tmport++);
+ outb(0x7f, tmport++);
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ j = i;
+ if ((j & 0x08) != 0) {
+ j = (j & 0x07) | 0x40;
+ }
+ outb(j, tmport);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+ dev->active_id[0] |= m;
+
+ tmport = wkport + 0x50;
+ outb(0x30, tmport);
+ tmport = wkport + 0x54;
+ outb(0x00, tmport);
+
+phase_cmd:
+ tmport = wkport + 0x58;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ tmport = wkport + 0x50;
+ outb(0x41, tmport);
+ goto phase_cmd;
+ }
+sel_ok:
+ tmport = wkport + 0x43;
+ outb(inqd[0], tmport++);
+ outb(inqd[1], tmport++);
+ outb(inqd[2], tmport++);
+ outb(inqd[3], tmport++);
+ outb(inqd[4], tmport++);
+ outb(inqd[5], tmport);
+ tmport += 0x07;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(inqd[6], tmport++);
+ outb(inqd[7], tmport++);
+ tmport += 0x03;
+ outb(inqd[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+ tmport = wkport + 0x5b;
+ outb(0x00, tmport);
+ tmport = wkport + 0x58;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ j = 0;
+rd_inq_data:
+ k = inb(tmport);
+ if ((k & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[j++] = inb(tmport);
+ tmport += 0x06;
+ goto rd_inq_data;
+ }
+ if ((k & 0x80) == 0) {
+ goto rd_inq_data;
+ }
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j == 0x16) {
+ goto inq_ok;
+ }
+ tmport = wkport + 0x50;
+ outb(0x46, tmport);
+ tmport += 0x02;
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ tmport += 0x03;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x16)
+ goto sel_ok;
+
+inq_ok:
+ mbuf[36] = 0;
+ printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
+ dev->id[0][i].devtype = mbuf[0];
+ rmb = mbuf[1];
+ n = mbuf[7];
+ if ((mbuf[7] & 0x60) == 0) {
+ goto not_wide;
+ }
+ if ((i < 8) && ((dev->global_map[0] & 0x20) == 0)) {
+ goto not_wide;
+ }
+ if (lvdmode == 0) {
+ goto chg_wide;
+ }
+ if (dev->sp[0][i] != 0x04) // force u2
+ {
+ goto chg_wide;
+ }
+
+ tmport = wkport + 0x5b;
+ outb(0x01, tmport);
+ tmport = wkport + 0x43;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+try_u3:
+ j = 0;
+ tmport = wkport + 0x54;
+ outb(0x09, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(u3[j++], tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto u3p_in;
+ }
+ if (j == 0x0a) {
+ goto u3p_cmd;
+ }
+ if (j == 0x0e) {
+ goto try_u3;
+ }
+ continue;
+u3p_out:
+ tmport = wkport + 0x58;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(0, tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto u3p_in;
+ }
+ if (j == 0x0a) {
+ goto u3p_cmd;
+ }
+ if (j == 0x0e) {
+ goto u3p_out;
+ }
+ continue;
+u3p_in:
+ tmport = wkport + 0x54;
+ outb(0x09, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+u3p_in1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto u3p_in1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto u3p_in1;
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto u3p_in;
+ }
+ if (j == 0x0a) {
+ goto u3p_cmd;
+ }
+ if (j == 0x0e) {
+ goto u3p_out;
+ }
+ continue;
+u3p_cmd:
+ tmport = wkport + 0x50;
+ outb(0x30, tmport);
+ tmport = wkport + 0x54;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ if (j == 0x4e) {
+ goto u3p_out;
+ }
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ goto chg_wide;
+ }
+ if (mbuf[1] != 0x06) {
+ goto chg_wide;
+ }
+ if (mbuf[2] != 0x04) {
+ goto chg_wide;
+ }
+ if (mbuf[3] == 0x09) {
+ m = 1;
+ m = m << i;
+ dev->wide_id[0] |= m;
+ dev->id[0][i].devsp = 0xce;
+ continue;
+ }
+chg_wide:
+ tmport = wkport + 0x5b;
+ outb(0x01, tmport);
+ tmport = wkport + 0x43;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
+ continue;
+
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+try_wide:
+ j = 0;
+ tmport = wkport + 0x54;
+ outb(0x05, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(wide[j++], tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto try_wide;
+ }
+ continue;
+widep_out:
+ tmport = wkport + 0x58;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(0, tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto widep_out;
+ }
+ continue;
+widep_in:
+ tmport = wkport + 0x54;
+ outb(0xff, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+widep_in1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto widep_in1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto widep_in1;
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto widep_out;
+ }
+ continue;
+widep_cmd:
+ tmport = wkport + 0x50;
+ outb(0x30, tmport);
+ tmport = wkport + 0x54;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ if (j == 0x4e) {
+ goto widep_out;
+ }
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ goto not_wide;
+ }
+ if (mbuf[1] != 0x02) {
+ goto not_wide;
+ }
+ if (mbuf[2] != 0x03) {
+ goto not_wide;
+ }
+ if (mbuf[3] != 0x01) {
+ goto not_wide;
+ }
+ m = 1;
+ m = m << i;
+ dev->wide_id[0] |= m;
+not_wide:
+ if ((dev->id[0][i].devtype == 0x00) || (dev->id[0][i].devtype == 0x07) || ((dev->id[0][i].devtype == 0x05) && ((n & 0x10) != 0))) {
+ m = 1;
+ m = m << i;
+ if ((dev->async[0] & m) != 0) {
+ goto set_sync;
+ }
+ }
+ continue;
+set_sync:
+ if (dev->sp[0][i] == 0x02) {
+ synu[4] = 0x0c;
+ synuw[4] = 0x0c;
+ } else {
+ if (dev->sp[0][i] >= 0x03) {
+ synu[4] = 0x0a;
+ synuw[4] = 0x0a;
+ }
+ }
+ tmport = wkport + 0x5b;
+ j = 0;
+ if ((m & dev->wide_id[0]) != 0) {
+ j |= 0x01;
+ }
+ outb(j, tmport);
+ tmport = wkport + 0x43;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[0][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+ continue;
+ }
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+
+try_sync:
+ j = 0;
+ tmport = wkport + 0x54;
+ outb(0x06, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ if ((m & dev->wide_id[0]) != 0) {
+ if ((m & dev->ultra_map[0]) != 0) {
+ outb(synuw[j++], tmport);
+ } else {
+ outb(synw[j++], tmport);
+ }
+ } else {
+ if ((m & dev->ultra_map[0]) != 0) {
+ outb(synu[j++], tmport);
+ } else {
+ outb(synn[j++], tmport);
+ }
+ }
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto try_sync;
+ }
+ continue;
+phase_outs:
+ tmport = wkport + 0x58;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00) {
+ if ((inb(tmport) & 0x01) != 0x00) {
+ tmport -= 0x06;
+ outb(0x00, tmport);
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j == 0x85) {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto phase_outs;
+ }
+ continue;
+phase_ins:
+ tmport = wkport + 0x54;
+ outb(0x06, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+phase_ins1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0x00) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto phase_ins1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto phase_ins1;
+ }
+ tmport -= 0x08;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ j = inb(tmport);
+ if (j == 0x85) {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto phase_outs;
+ }
+ continue;
+phase_cmds:
+ tmport = wkport + 0x50;
+ outb(0x30, tmport);
+tar_dcons:
+ tmport = wkport + 0x54;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ continue;
+ }
+ if (mbuf[1] != 0x03) {
+ continue;
+ }
+ if (mbuf[4] == 0x00) {
+ continue;
+ }
+ if (mbuf[3] > 0x64) {
+ continue;
+ }
+ if (mbuf[4] > 0x0e) {
+ mbuf[4] = 0x0e;
+ }
+ dev->id[0][i].devsp = mbuf[4];
+ if (mbuf[3] < 0x0c) {
+ j = 0xb0;
+ goto set_syn_ok;
+ }
+ if ((mbuf[3] < 0x0d) && (rmb == 0)) {
+ j = 0xa0;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x1a) {
+ j = 0x20;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x33) {
+ j = 0x40;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x4c) {
+ j = 0x50;
+ goto set_syn_ok;
+ }
+ j = 0x60;
+set_syn_ok:
+ dev->id[0][i].devsp = (dev->id[0][i].devsp & 0x0f) | j;
+ }
+}
+
+static void atp870u_free_tables(struct Scsi_Host *host)
+{
+ struct atp_unit *atp_dev = (struct atp_unit *)&host->hostdata;
+ int j, k;
+ for (j=0; j < 2; j++) {
+ for (k = 0; k < 16; k++) {
+ if (!atp_dev->id[j][k].prd_table)
+ continue;
+ pci_free_consistent(atp_dev->pdev, 1024, atp_dev->id[j][k].prd_table, atp_dev->id[j][k].prdaddr);
+ atp_dev->id[j][k].prd_table = NULL;
+ }
+ }
+}
+
+static int atp870u_init_tables(struct Scsi_Host *host)
+{
+ struct atp_unit *atp_dev = (struct atp_unit *)&host->hostdata;
+ int c,k;
+ for(c=0;c < 2;c++) {
+ for(k=0;k<16;k++) {
+ atp_dev->id[c][k].prd_table = pci_alloc_consistent(atp_dev->pdev, 1024, &(atp_dev->id[c][k].prdaddr));
+ if (!atp_dev->id[c][k].prd_table) {
+ printk("atp870u_init_tables fail\n");
+ atp870u_free_tables(host);
+ return -ENOMEM;
+ }
+ atp_dev->id[c][k].devsp=0x20;
+ atp_dev->id[c][k].devtype = 0x7f;
+ atp_dev->id[c][k].curr_req = NULL;
+ }
+
+ atp_dev->active_id[c] = 0;
+ atp_dev->wide_id[c] = 0;
+ atp_dev->host_id[c] = 0x07;
+ atp_dev->quhd[c] = 0;
+ atp_dev->quend[c] = 0;
+ atp_dev->last_cmd[c] = 0xff;
+ atp_dev->in_snd[c] = 0;
+ atp_dev->in_int[c] = 0;
+
+ for (k = 0; k < qcnt; k++) {
+ atp_dev->quereq[c][k] = NULL;
+ }
+ for (k = 0; k < 16; k++) {
+ atp_dev->id[c][k].curr_req = NULL;
+ atp_dev->sp[c][k] = 0x04;
+ }
+ }
+ return 0;
+}
+
+/* return non-zero on detection */
+static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ unsigned char k, m, c;
+ unsigned long flags;
+ unsigned int base_io, tmport, error,n;
+ unsigned char host_id;
+ struct Scsi_Host *shpnt = NULL;
+ struct atp_unit atp_dev, *p;
+ unsigned char setupdata[2][16];
+ int count = 0;
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+
+ if (!pci_set_dma_mask(pdev, 0xFFFFFFFFUL)) {
+ printk(KERN_INFO "atp870u: use 32bit DMA mask.\n");
+ } else {
+ printk(KERN_ERR "atp870u: DMA mask required but not available.\n");
+ return -EIO;
+ }
+
+ memset(&atp_dev, 0, sizeof atp_dev);
+ /*
+ * It's probably easier to weed out some revisions like
+ * this than via the PCI device table
+ */
+ if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610) {
+ error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atp_dev.chip_ver);
+ if (atp_dev.chip_ver < 2)
+ return -EIO;
+ }
+
+ switch (ent->device) {
+ case PCI_DEVICE_ID_ARTOP_AEC7612UW:
+ case PCI_DEVICE_ID_ARTOP_AEC7612SUW:
+ case ATP880_DEVID1:
+ case ATP880_DEVID2:
+ case ATP885_DEVID:
+ atp_dev.chip_ver = 0x04;
+ default:
+ break;
+ }
+ base_io = pci_resource_start(pdev, 0);
+ base_io &= 0xfffffff8;
+
+ if ((ent->device == ATP880_DEVID1)||(ent->device == ATP880_DEVID2)) {
+ error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atp_dev.chip_ver);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);//JCC082803
+
+ host_id = inb(base_io + 0x39);
+ host_id >>= 0x04;
+
+ printk(KERN_INFO " ACARD AEC-67160 PCI Ultra3 LVD Host Adapter: %d"
+ " IO:%x, IRQ:%d.\n", count, base_io, pdev->irq);
+ atp_dev.ioport[0] = base_io + 0x40;
+ atp_dev.pciport[0] = base_io + 0x28;
+ atp_dev.dev_id = ent->device;
+ atp_dev.host_id[0] = host_id;
+
+ tmport = base_io + 0x22;
+ atp_dev.scam_on = inb(tmport);
+ tmport += 0x13;
+ atp_dev.global_map[0] = inb(tmport);
+ tmport += 0x07;
+ atp_dev.ultra_map[0] = inw(tmport);
+
+ n = 0x3f09;
+next_fblk_880:
+ if (n >= 0x4000)
+ goto flash_ok_880;
+
+ m = 0;
+ outw(n, base_io + 0x34);
+ n += 0x0002;
+ if (inb(base_io + 0x30) == 0xff)
+ goto flash_ok_880;
+
+ atp_dev.sp[0][m++] = inb(base_io + 0x30);
+ atp_dev.sp[0][m++] = inb(base_io + 0x31);
+ atp_dev.sp[0][m++] = inb(base_io + 0x32);
+ atp_dev.sp[0][m++] = inb(base_io + 0x33);
+ outw(n, base_io + 0x34);
+ n += 0x0002;
+ atp_dev.sp[0][m++] = inb(base_io + 0x30);
+ atp_dev.sp[0][m++] = inb(base_io + 0x31);
+ atp_dev.sp[0][m++] = inb(base_io + 0x32);
+ atp_dev.sp[0][m++] = inb(base_io + 0x33);
+ outw(n, base_io + 0x34);
+ n += 0x0002;
+ atp_dev.sp[0][m++] = inb(base_io + 0x30);
+ atp_dev.sp[0][m++] = inb(base_io + 0x31);
+ atp_dev.sp[0][m++] = inb(base_io + 0x32);
+ atp_dev.sp[0][m++] = inb(base_io + 0x33);
+ outw(n, base_io + 0x34);
+ n += 0x0002;
+ atp_dev.sp[0][m++] = inb(base_io + 0x30);
+ atp_dev.sp[0][m++] = inb(base_io + 0x31);
+ atp_dev.sp[0][m++] = inb(base_io + 0x32);
+ atp_dev.sp[0][m++] = inb(base_io + 0x33);
+ n += 0x0018;
+ goto next_fblk_880;
+flash_ok_880:
+ outw(0, base_io + 0x34);
+ atp_dev.ultra_map[0] = 0;
+ atp_dev.async[0] = 0;
+ for (k = 0; k < 16; k++) {
+ n = 1;
+ n = n << k;
+ if (atp_dev.sp[0][k] > 1) {
+ atp_dev.ultra_map[0] |= n;
+ } else {
+ if (atp_dev.sp[0][k] == 0)
+ atp_dev.async[0] |= n;
+ }
+ }
+ atp_dev.async[0] = ~(atp_dev.async[0]);
+ outb(atp_dev.global_map[0], base_io + 0x35);
+
+ shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
+ if (!shpnt)
+ return -ENOMEM;
+
+ p = (struct atp_unit *)&shpnt->hostdata;
+
+ atp_dev.host = shpnt;
+ atp_dev.pdev = pdev;
+ pci_set_drvdata(pdev, p);
+ memcpy(p, &atp_dev, sizeof atp_dev);
+ if (atp870u_init_tables(shpnt) < 0) {
+ printk(KERN_ERR "Unable to allocate tables for Acard controller\n");
+ goto unregister;
+ }
+
+ if (request_irq(pdev->irq, atp870u_intr_handle, SA_SHIRQ, "atp880i", shpnt)) {
+ printk(KERN_ERR "Unable to allocate IRQ%d for Acard controller.\n", pdev->irq);
+ goto free_tables;
+ }
+
+ spin_lock_irqsave(shpnt->host_lock, flags);
+ tmport = base_io + 0x38;
+ k = inb(tmport) & 0x80;
+ outb(k, tmport);
+ tmport += 0x03;
+ outb(0x20, tmport);
+ mdelay(32);
+ outb(0, tmport);
+ mdelay(32);
+ tmport = base_io + 0x5b;
+ inb(tmport);
+ tmport -= 0x04;
+ inb(tmport);
+ tmport = base_io + 0x40;
+ outb((host_id | 0x08), tmport);
+ tmport += 0x18;
+ outb(0, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0)
+ mdelay(1);
+ tmport -= 0x08;
+ inb(tmport);
+ tmport = base_io + 0x41;
+ outb(8, tmport++);
+ outb(0x7f, tmport);
+ tmport = base_io + 0x51;
+ outb(0x20, tmport);
+
+ tscam(shpnt);
+ is880(p, base_io);
+ tmport = base_io + 0x38;
+ outb(0xb0, tmport);
+ shpnt->max_id = 16;
+ shpnt->this_id = host_id;
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 0x60; /* Number of bytes of I/O space used */
+ shpnt->irq = pdev->irq;
+ } else if (ent->device == ATP885_DEVID) {
+ printk(KERN_INFO " ACARD AEC-67162 PCI Ultra3 LVD Host Adapter: IO:%x, IRQ:%d.\n"
+ , base_io, pdev->irq);
+
+ atp_dev.pdev = pdev;
+ atp_dev.dev_id = ent->device;
+ atp_dev.baseport = base_io;
+ atp_dev.ioport[0] = base_io + 0x80;
+ atp_dev.ioport[1] = base_io + 0xc0;
+ atp_dev.pciport[0] = base_io + 0x40;
+ atp_dev.pciport[1] = base_io + 0x50;
+
+ shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
+ if (!shpnt)
+ return -ENOMEM;
+
+ p = (struct atp_unit *)&shpnt->hostdata;
+
+ atp_dev.host = shpnt;
+ atp_dev.pdev = pdev;
+ pci_set_drvdata(pdev, p);
+ memcpy(p, &atp_dev, sizeof(struct atp_unit));
+ if (atp870u_init_tables(shpnt) < 0)
+ goto unregister;
+
+#ifdef ED_DBGP
+ printk("request_irq() shpnt %p hostdata %p\n", shpnt, p);
+#endif
+ if (request_irq(pdev->irq, atp870u_intr_handle, SA_SHIRQ, "atp870u", shpnt)) {
+ printk(KERN_ERR "Unable to allocate IRQ for Acard controller.\n");
+ goto free_tables;
+ }
+
+ spin_lock_irqsave(shpnt->host_lock, flags);
+
+ c=inb(base_io + 0x29);
+ outb((c | 0x04),base_io + 0x29);
+
+ n=0x1f80;
+next_fblk_885:
+ if (n >= 0x2000) {
+ goto flash_ok_885;
+ }
+ outw(n,base_io + 0x3c);
+ if (inl(base_io + 0x38) == 0xffffffff) {
+ goto flash_ok_885;
+ }
+ for (m=0; m < 2; m++) {
+ p->global_map[m]= 0;
+ for (k=0; k < 4; k++) {
+ outw(n++,base_io + 0x3c);
+ ((unsigned long *)&setupdata[m][0])[k]=inl(base_io + 0x38);
+ }
+ for (k=0; k < 4; k++) {
+ outw(n++,base_io + 0x3c);
+ ((unsigned long *)&p->sp[m][0])[k]=inl(base_io + 0x38);
+ }
+ n += 8;
+ }
+ goto next_fblk_885;
+flash_ok_885:
+#ifdef ED_DBGP
+ printk( "Flash Read OK\n");
+#endif
+ c=inb(base_io + 0x29);
+ outb((c & 0xfb),base_io + 0x29);
+ for (c=0;c < 2;c++) {
+ p->ultra_map[c]=0;
+ p->async[c] = 0;
+ for (k=0; k < 16; k++) {
+ n=1;
+ n = n << k;
+ if (p->sp[c][k] > 1) {
+ p->ultra_map[c] |= n;
+ } else {
+ if (p->sp[c][k] == 0) {
+ p->async[c] |= n;
+ }
+ }
+ }
+ p->async[c] = ~(p->async[c]);
+
+ if (p->global_map[c] == 0) {
+ k=setupdata[c][1];
+ if ((k & 0x40) != 0)
+ p->global_map[c] |= 0x20;
+ k &= 0x07;
+ p->global_map[c] |= k;
+ if ((setupdata[c][2] & 0x04) != 0)
+ p->global_map[c] |= 0x08;
+ p->host_id[c] = setupdata[c][0] & 0x07;
+ }
+ }
+
+ k = inb(base_io + 0x28) & 0x8f;
+ k |= 0x10;
+ outb(k, base_io + 0x28);
+ outb(0x80, base_io + 0x41);
+ outb(0x80, base_io + 0x51);
+ mdelay(100);
+ outb(0, base_io + 0x41);
+ outb(0, base_io + 0x51);
+ mdelay(1000);
+ inb(base_io + 0x9b);
+ inb(base_io + 0x97);
+ inb(base_io + 0xdb);
+ inb(base_io + 0xd7);
+ tmport = base_io + 0x80;
+ k=p->host_id[0];
+ if (k > 7)
+ k = (k & 0x07) | 0x40;
+ k |= 0x08;
+ outb(k, tmport);
+ tmport += 0x18;
+ outb(0, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0)
+ cpu_relax();
+
+ tmport -= 0x08;
+ inb(tmport);
+ tmport = base_io + 0x81;
+ outb(8, tmport++);
+ outb(0x7f, tmport);
+ tmport = base_io + 0x91;
+ outb(0x20, tmport);
+
+ tmport = base_io + 0xc0;
+ k=p->host_id[1];
+ if (k > 7)
+ k = (k & 0x07) | 0x40;
+ k |= 0x08;
+ outb(k, tmport);
+ tmport += 0x18;
+ outb(0, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0)
+ cpu_relax();
+
+ tmport -= 0x08;
+ inb(tmport);
+ tmport = base_io + 0xc1;
+ outb(8, tmport++);
+ outb(0x7f, tmport);
+ tmport = base_io + 0xd1;
+ outb(0x20, tmport);
+
+ tscam_885();
+ printk(KERN_INFO " Scanning Channel A SCSI Device ...\n");
+ is885(p, base_io + 0x80, 0);
+ printk(KERN_INFO " Scanning Channel B SCSI Device ...\n");
+ is885(p, base_io + 0xc0, 1);
+
+ k = inb(base_io + 0x28) & 0xcf;
+ k |= 0xc0;
+ outb(k, base_io + 0x28);
+ k = inb(base_io + 0x1f) | 0x80;
+ outb(k, base_io + 0x1f);
+ k = inb(base_io + 0x29) | 0x01;
+ outb(k, base_io + 0x29);
+#ifdef ED_DBGP
+ //printk("atp885: atp_host[0] 0x%p\n", atp_host[0]);
+#endif
+ shpnt->max_id = 16;
+ shpnt->max_lun = (p->global_map[0] & 0x07) + 1;
+ shpnt->max_channel = 1;
+ shpnt->this_id = p->host_id[0];
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 0xff; /* Number of bytes of I/O space used */
+ shpnt->irq = pdev->irq;
+
+ } else {
+ error = pci_read_config_byte(pdev, 0x49, &host_id);
+
+ printk(KERN_INFO " ACARD AEC-671X PCI Ultra/W SCSI-2/3 Host Adapter: %d "
+ "IO:%x, IRQ:%d.\n", count, base_io, pdev->irq);
+
+ atp_dev.ioport[0] = base_io;
+ atp_dev.pciport[0] = base_io + 0x20;
+ atp_dev.dev_id = ent->device;
+ host_id &= 0x07;
+ atp_dev.host_id[0] = host_id;
+ tmport = base_io + 0x22;
+ atp_dev.scam_on = inb(tmport);
+ tmport += 0x0b;
+ atp_dev.global_map[0] = inb(tmport++);
+ atp_dev.ultra_map[0] = inw(tmport);
+
+ if (atp_dev.ultra_map[0] == 0) {
+ atp_dev.scam_on = 0x00;
+ atp_dev.global_map[0] = 0x20;
+ atp_dev.ultra_map[0] = 0xffff;
+ }
+
+ shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
+ if (!shpnt)
+ return -ENOMEM;
+
+ p = (struct atp_unit *)&shpnt->hostdata;
+
+ atp_dev.host = shpnt;
+ atp_dev.pdev = pdev;
+ pci_set_drvdata(pdev, p);
+ memcpy(p, &atp_dev, sizeof atp_dev);
+ if (atp870u_init_tables(shpnt) < 0)
+ goto unregister;
+
+ if (request_irq(pdev->irq, atp870u_intr_handle, SA_SHIRQ, "atp870i", shpnt)) {
+ printk(KERN_ERR "Unable to allocate IRQ%d for Acard controller.\n", pdev->irq);
+ goto free_tables;
+ }
+
+ spin_lock_irqsave(shpnt->host_lock, flags);
+ if (atp_dev.chip_ver > 0x07) { /* check if atp876 chip then enable terminator */
+ tmport = base_io + 0x3e;
+ outb(0x00, tmport);
+ }
+
+ tmport = base_io + 0x3a;
+ k = (inb(tmport) & 0xf3) | 0x10;
+ outb(k, tmport);
+ outb((k & 0xdf), tmport);
+ mdelay(32);
+ outb(k, tmport);
+ mdelay(32);
+ tmport = base_io;
+ outb((host_id | 0x08), tmport);
+ tmport += 0x18;
+ outb(0, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0)
+ mdelay(1);
+
+ tmport -= 0x08;
+ inb(tmport);
+ tmport = base_io + 1;
+ outb(8, tmport++);
+ outb(0x7f, tmport);
+ tmport = base_io + 0x11;
+ outb(0x20, tmport);
+
+ tscam(shpnt);
+ is870(p, base_io);
+ tmport = base_io + 0x3a;
+ outb((inb(tmport) & 0xef), tmport);
+ tmport++;
+ outb((inb(tmport) | 0x20), tmport);
+ if (atp_dev.chip_ver == 4)
+ shpnt->max_id = 16;
+ else
+ shpnt->max_id = 7;
+ shpnt->this_id = host_id;
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */
+ shpnt->irq = pdev->irq;
+ }
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+ if(ent->device==ATP885_DEVID) {
+ if(!request_region(base_io, 0xff, "atp870u")) /* Register the IO ports that we use */
+ goto request_io_fail;
+ } else if((ent->device==ATP880_DEVID1)||(ent->device==ATP880_DEVID2)) {
+ if(!request_region(base_io, 0x60, "atp870u")) /* Register the IO ports that we use */
+ goto request_io_fail;
+ } else {
+ if(!request_region(base_io, 0x40, "atp870u")) /* Register the IO ports that we use */
+ goto request_io_fail;
+ }
+ count++;
+ if (scsi_add_host(shpnt, &pdev->dev))
+ goto scsi_add_fail;
+ scsi_scan_host(shpnt);
+#ifdef ED_DBGP
+ printk("atp870u_prob : exit\n");
+#endif
+ return 0;
+
+scsi_add_fail:
+ printk("atp870u_prob:scsi_add_fail\n");
+ if(ent->device==ATP885_DEVID) {
+ release_region(base_io, 0xff);
+ } else if((ent->device==ATP880_DEVID1)||(ent->device==ATP880_DEVID2)) {
+ release_region(base_io, 0x60);
+ } else {
+ release_region(base_io, 0x40);
+ }
+request_io_fail:
+ printk("atp870u_prob:request_io_fail\n");
+ free_irq(pdev->irq, shpnt);
+free_tables:
+ printk("atp870u_prob:free_table\n");
+ atp870u_free_tables(shpnt);
+unregister:
+ printk("atp870u_prob:unregister\n");
+ scsi_host_put(shpnt);
+ return -1;
+}
+
+/* The abort command does not leave the device in a clean state where
+ it is available to be used again. Until this gets worked out, we will
+ leave it commented out. */
+
+static int atp870u_abort(struct scsi_cmnd * SCpnt)
+{
+ unsigned char j, k, c;
+ struct scsi_cmnd *workrequ;
+ unsigned int tmport;
+ struct atp_unit *dev;
+ struct Scsi_Host *host;
+ host = SCpnt->device->host;
+
+ dev = (struct atp_unit *)&host->hostdata;
+ c=SCpnt->device->channel;
+ printk(" atp870u: abort Channel = %x \n", c);
+ printk("working=%x last_cmd=%x ", dev->working[c], dev->last_cmd[c]);
+ printk(" quhdu=%x quendu=%x ", dev->quhd[c], dev->quend[c]);
+ tmport = dev->ioport[c];
+ for (j = 0; j < 0x18; j++) {
+ printk(" r%2x=%2x", j, inb(tmport++));
+ }
+ tmport += 0x04;
+ printk(" r1c=%2x", inb(tmport));
+ tmport += 0x03;
+ printk(" r1f=%2x in_snd=%2x ", inb(tmport), dev->in_snd[c]);
+ tmport= dev->pciport[c];
+ printk(" d00=%2x", inb(tmport));
+ tmport += 0x02;
+ printk(" d02=%2x", inb(tmport));
+ for(j=0;j<16;j++) {
+ if (dev->id[c][j].curr_req != NULL) {
+ workrequ = dev->id[c][j].curr_req;
+ printk("\n que cdb= ");
+ for (k=0; k < workrequ->cmd_len; k++) {
+ printk(" %2x ",workrequ->cmnd[k]);
+ }
+ printk(" last_lenu= %x ",(unsigned int)dev->id[c][j].last_len);
+ }
+ }
+ return SUCCESS;
+}
+
+static const char *atp870u_info(struct Scsi_Host *notused)
+{
+ static char buffer[128];
+
+ strcpy(buffer, "ACARD AEC-6710/6712/67160 PCI Ultra/W/LVD SCSI-3 Adapter Driver V2.6+ac ");
+
+ return buffer;
+}
+
+#define BLS buffer + len + size
+int atp870u_proc_info(struct Scsi_Host *HBAptr, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ static u8 buff[512];
+ int size = 0;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ if (inout)
+ return -EINVAL;
+ if (offset == 0)
+ memset(buff, 0, sizeof(buff));
+ size += sprintf(BLS, "ACARD AEC-671X Driver Version: 2.6+ac\n");
+ len += size;
+ pos = begin + len;
+ size = 0;
+
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Adapter Configuration:\n");
+ size += sprintf(BLS, " Base IO: %#.4lx\n", HBAptr->io_port);
+ size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
+ len += size;
+ pos = begin + len;
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length) {
+ len = length; /* Ending slop */
+ }
+ return (len);
+}
+
+
+static int atp870u_biosparam(struct scsi_device *disk, struct block_device *dev,
+ sector_t capacity, int *ip)
+{
+ int heads, sectors, cylinders;
+
+ heads = 64;
+ sectors = 32;
+ cylinders = (unsigned long)capacity / (heads * sectors);
+ if (cylinders > 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = (unsigned long)capacity / (heads * sectors);
+ }
+ ip[0] = heads;
+ ip[1] = sectors;
+ ip[2] = cylinders;
+
+ return 0;
+}
+
+static void atp870u_remove (struct pci_dev *pdev)
+{
+ struct atp_unit *devext = pci_get_drvdata(pdev);
+ struct Scsi_Host *pshost = devext->host;
+
+
+ scsi_remove_host(pshost);
+ printk(KERN_INFO "free_irq : %d\n",pshost->irq);
+ free_irq(pshost->irq, pshost);
+ release_region(pshost->io_port, pshost->n_io_port);
+ printk(KERN_INFO "atp870u_free_tables : %p\n",pshost);
+ atp870u_free_tables(pshost);
+ printk(KERN_INFO "scsi_host_put : %p\n",pshost);
+ scsi_host_put(pshost);
+ printk(KERN_INFO "pci_set_drvdata : %p\n",pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+MODULE_LICENSE("GPL");
+
+static struct scsi_host_template atp870u_template = {
+ .module = THIS_MODULE,
+ .name = "atp870u" /* name */,
+ .proc_name = "atp870u",
+ .proc_info = atp870u_proc_info,
+ .info = atp870u_info /* info */,
+ .queuecommand = atp870u_queuecommand /* queuecommand */,
+ .eh_abort_handler = atp870u_abort /* abort */,
+ .bios_param = atp870u_biosparam /* biosparm */,
+ .can_queue = qcnt /* can_queue */,
+ .this_id = 7 /* SCSI ID */,
+ .sg_tablesize = ATP870U_SCATTER /*SG_ALL*/ /*SG_NONE*/,
+ .cmd_per_lun = ATP870U_CMDLUN /* commands per lun */,
+ .use_clustering = ENABLE_CLUSTERING,
+ .max_sectors = ATP870U_MAX_SECTORS,
+};
+
+static struct pci_device_id atp870u_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP885_DEVID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP880_DEVID1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, ATP880_DEVID2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7610) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612UW) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612U) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612S) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612D) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_AEC7612SUW) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_8060) },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, atp870u_id_table);
+
+static struct pci_driver atp870u_driver = {
+ .id_table = atp870u_id_table,
+ .name = "atp870u",
+ .probe = atp870u_probe,
+ .remove = __devexit_p(atp870u_remove),
+};
+
+static int __init atp870u_init(void)
+{
+#ifdef ED_DBGP
+ printk("atp870u_init: Entry\n");
+#endif
+ return pci_register_driver(&atp870u_driver);
+}
+
+static void __exit atp870u_exit(void)
+{
+#ifdef ED_DBGP
+ printk("atp870u_exit: Entry\n");
+#endif
+ pci_unregister_driver(&atp870u_driver);
+}
+
+static void tscam_885(void)
+{
+ unsigned char i;
+
+ for (i = 0; i < 0x2; i++) {
+ mdelay(300);
+ }
+ return;
+}
+
+
+
+static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c)
+{
+ unsigned int tmport;
+ unsigned char i, j, k, rmb, n, lvdmode;
+ unsigned short int m;
+ static unsigned char mbuf[512];
+ static unsigned char satn[9] = {0, 0, 0, 0, 0, 0, 0, 6, 6};
+ static unsigned char inqd[9] = {0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6};
+ static unsigned char synn[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
+ unsigned char synu[6] = {0x80, 1, 3, 1, 0x0a, 0x0e};
+ static unsigned char synw[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
+ unsigned char synuw[6] = {0x80, 1, 3, 1, 0x0a, 0x0e};
+ static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0};
+ static unsigned char u3[9] = { 0x80,1,6,4,0x09,00,0x0e,0x01,0x02 };
+
+ lvdmode=inb(wkport + 0x1b) >> 7;
+
+ for (i = 0; i < 16; i++) {
+ m = 1;
+ m = m << i;
+ if ((m & dev->active_id[c]) != 0) {
+ continue;
+ }
+ if (i == dev->host_id[c]) {
+ printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[c]);
+ continue;
+ }
+ tmport = wkport + 0x1b;
+ outb(0x01, tmport);
+ tmport = wkport + 0x01;
+ outb(0x08, tmport++);
+ outb(0x7f, tmport++);
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[c][i].devsp, tmport++);
+
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ j = i;
+ if ((j & 0x08) != 0) {
+ j = (j & 0x07) | 0x40;
+ }
+ outb(j, tmport);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+ continue;
+ }
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+ dev->active_id[c] |= m;
+
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+ tmport = wkport + 0x14;
+ outb(0x00, tmport);
+
+phase_cmd:
+ tmport = wkport + 0x18;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ tmport = wkport + 0x10;
+ outb(0x41, tmport);
+ goto phase_cmd;
+ }
+sel_ok:
+ tmport = wkport + 0x03;
+ outb(inqd[0], tmport++);
+ outb(inqd[1], tmport++);
+ outb(inqd[2], tmport++);
+ outb(inqd[3], tmport++);
+ outb(inqd[4], tmport++);
+ outb(inqd[5], tmport);
+ tmport += 0x07;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[c][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(inqd[6], tmport++);
+ outb(inqd[7], tmport++);
+ tmport += 0x03;
+ outb(inqd[8], tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+ continue;
+ }
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+ tmport = wkport + 0x1b;
+ outb(0x00, tmport);
+ tmport = wkport + 0x18;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ j = 0;
+rd_inq_data:
+ k = inb(tmport);
+ if ((k & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[j++] = inb(tmport);
+ tmport += 0x06;
+ goto rd_inq_data;
+ }
+ if ((k & 0x80) == 0) {
+ goto rd_inq_data;
+ }
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j == 0x16) {
+ goto inq_ok;
+ }
+ tmport = wkport + 0x10;
+ outb(0x46, tmport);
+ tmport += 0x02;
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ tmport += 0x03;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ if (inb(tmport) != 0x16) {
+ goto sel_ok;
+ }
+inq_ok:
+ mbuf[36] = 0;
+ printk( KERN_INFO" ID: %2d %s\n", i, &mbuf[8]);
+ dev->id[c][i].devtype = mbuf[0];
+ rmb = mbuf[1];
+ n = mbuf[7];
+ if ((mbuf[7] & 0x60) == 0) {
+ goto not_wide;
+ }
+ if ((i < 8) && ((dev->global_map[c] & 0x20) == 0)) {
+ goto not_wide;
+ }
+ if (lvdmode == 0) {
+ goto chg_wide;
+ }
+ if (dev->sp[c][i] != 0x04) { // force u2
+ goto chg_wide;
+ }
+
+ tmport = wkport + 0x1b;
+ outb(0x01, tmport);
+ tmport = wkport + 0x03;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[c][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+ continue;
+ }
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+try_u3:
+ j = 0;
+ tmport = wkport + 0x14;
+ outb(0x09, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(u3[j++], tmport);
+ tmport += 0x06;
+ }
+ cpu_relax();
+ }
+ tmport -= 0x08;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto u3p_in;
+ }
+ if (j == 0x0a) {
+ goto u3p_cmd;
+ }
+ if (j == 0x0e) {
+ goto try_u3;
+ }
+ continue;
+u3p_out:
+ tmport = wkport + 0x18;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(0, tmport);
+ tmport += 0x06;
+ }
+ cpu_relax();
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto u3p_in;
+ }
+ if (j == 0x0a) {
+ goto u3p_cmd;
+ }
+ if (j == 0x0e) {
+ goto u3p_out;
+ }
+ continue;
+u3p_in:
+ tmport = wkport + 0x14;
+ outb(0x09, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+u3p_in1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto u3p_in1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto u3p_in1;
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto u3p_in;
+ }
+ if (j == 0x0a) {
+ goto u3p_cmd;
+ }
+ if (j == 0x0e) {
+ goto u3p_out;
+ }
+ continue;
+u3p_cmd:
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+ tmport = wkport + 0x14;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00);
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ if (j == 0x4e) {
+ goto u3p_out;
+ }
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ goto chg_wide;
+ }
+ if (mbuf[1] != 0x06) {
+ goto chg_wide;
+ }
+ if (mbuf[2] != 0x04) {
+ goto chg_wide;
+ }
+ if (mbuf[3] == 0x09) {
+ m = 1;
+ m = m << i;
+ dev->wide_id[c] |= m;
+ dev->id[c][i].devsp = 0xce;
+#ifdef ED_DBGP
+ printk("dev->id[%2d][%2d].devsp = %2x\n",c,i,dev->id[c][i].devsp);
+#endif
+ continue;
+ }
+chg_wide:
+ tmport = wkport + 0x1b;
+ outb(0x01, tmport);
+ tmport = wkport + 0x03;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[c][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+ continue;
+ }
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+try_wide:
+ j = 0;
+ tmport = wkport + 0x14;
+ outb(0x05, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(wide[j++], tmport);
+ tmport += 0x06;
+ }
+ cpu_relax();
+ }
+ tmport -= 0x08;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto try_wide;
+ }
+ continue;
+widep_out:
+ tmport = wkport + 0x18;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ outb(0, tmport);
+ tmport += 0x06;
+ }
+ cpu_relax();
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto widep_out;
+ }
+ continue;
+widep_in:
+ tmport = wkport + 0x14;
+ outb(0xff, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+widep_in1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto widep_in1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto widep_in1;
+ }
+ tmport -= 0x08;
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto widep_in;
+ }
+ if (j == 0x0a) {
+ goto widep_cmd;
+ }
+ if (j == 0x0e) {
+ goto widep_out;
+ }
+ continue;
+widep_cmd:
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+ tmport = wkport + 0x14;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ if (j == 0x4e) {
+ goto widep_out;
+ }
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ goto not_wide;
+ }
+ if (mbuf[1] != 0x02) {
+ goto not_wide;
+ }
+ if (mbuf[2] != 0x03) {
+ goto not_wide;
+ }
+ if (mbuf[3] != 0x01) {
+ goto not_wide;
+ }
+ m = 1;
+ m = m << i;
+ dev->wide_id[c] |= m;
+not_wide:
+ if ((dev->id[c][i].devtype == 0x00) || (dev->id[c][i].devtype == 0x07) ||
+ ((dev->id[c][i].devtype == 0x05) && ((n & 0x10) != 0))) {
+ m = 1;
+ m = m << i;
+ if ((dev->async[c] & m) != 0) {
+ goto set_sync;
+ }
+ }
+ continue;
+set_sync:
+ if (dev->sp[c][i] == 0x02) {
+ synu[4]=0x0c;
+ synuw[4]=0x0c;
+ } else {
+ if (dev->sp[c][i] >= 0x03) {
+ synu[4]=0x0a;
+ synuw[4]=0x0a;
+ }
+ }
+ tmport = wkport + 0x1b;
+ j = 0;
+ if ((m & dev->wide_id[c]) != 0) {
+ j |= 0x01;
+ }
+ outb(j, tmport);
+ tmport = wkport + 0x03;
+ outb(satn[0], tmport++);
+ outb(satn[1], tmport++);
+ outb(satn[2], tmport++);
+ outb(satn[3], tmport++);
+ outb(satn[4], tmport++);
+ outb(satn[5], tmport++);
+ tmport += 0x06;
+ outb(0, tmport);
+ tmport += 0x02;
+ outb(dev->id[c][i].devsp, tmport++);
+ outb(0, tmport++);
+ outb(satn[6], tmport++);
+ outb(satn[7], tmport++);
+ tmport += 0x03;
+ outb(satn[8], tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+ continue;
+ }
+ while (inb(tmport) != 0x8e)
+ cpu_relax();
+try_sync:
+ j = 0;
+ tmport = wkport + 0x14;
+ outb(0x06, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0) {
+ if ((inb(tmport) & 0x01) != 0) {
+ tmport -= 0x06;
+ if ((m & dev->wide_id[c]) != 0) {
+ if ((m & dev->ultra_map[c]) != 0) {
+ outb(synuw[j++], tmport);
+ } else {
+ outb(synw[j++], tmport);
+ }
+ } else {
+ if ((m & dev->ultra_map[c]) != 0) {
+ outb(synu[j++], tmport);
+ } else {
+ outb(synn[j++], tmport);
+ }
+ }
+ tmport += 0x06;
+ }
+ }
+ tmport -= 0x08;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ j = inb(tmport) & 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto try_sync;
+ }
+ continue;
+phase_outs:
+ tmport = wkport + 0x18;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00) {
+ if ((inb(tmport) & 0x01) != 0x00) {
+ tmport -= 0x06;
+ outb(0x00, tmport);
+ tmport += 0x06;
+ }
+ cpu_relax();
+ }
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j == 0x85) {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto phase_outs;
+ }
+ continue;
+phase_ins:
+ tmport = wkport + 0x14;
+ outb(0x06, tmport);
+ tmport += 0x04;
+ outb(0x20, tmport);
+ tmport += 0x07;
+ k = 0;
+phase_ins1:
+ j = inb(tmport);
+ if ((j & 0x01) != 0x00) {
+ tmport -= 0x06;
+ mbuf[k++] = inb(tmport);
+ tmport += 0x06;
+ goto phase_ins1;
+ }
+ if ((j & 0x80) == 0x00) {
+ goto phase_ins1;
+ }
+ tmport -= 0x08;
+ while ((inb(tmport) & 0x80) == 0x00);
+ j = inb(tmport);
+ if (j == 0x85) {
+ goto tar_dcons;
+ }
+ j &= 0x0f;
+ if (j == 0x0f) {
+ goto phase_ins;
+ }
+ if (j == 0x0a) {
+ goto phase_cmds;
+ }
+ if (j == 0x0e) {
+ goto phase_outs;
+ }
+ continue;
+phase_cmds:
+ tmport = wkport + 0x10;
+ outb(0x30, tmport);
+tar_dcons:
+ tmport = wkport + 0x14;
+ outb(0x00, tmport);
+ tmport += 0x04;
+ outb(0x08, tmport);
+ tmport += 0x07;
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ j = inb(tmport);
+ if (j != 0x16) {
+ continue;
+ }
+ if (mbuf[0] != 0x01) {
+ continue;
+ }
+ if (mbuf[1] != 0x03) {
+ continue;
+ }
+ if (mbuf[4] == 0x00) {
+ continue;
+ }
+ if (mbuf[3] > 0x64) {
+ continue;
+ }
+ if (mbuf[4] > 0x0e) {
+ mbuf[4] = 0x0e;
+ }
+ dev->id[c][i].devsp = mbuf[4];
+ if (mbuf[3] < 0x0c){
+ j = 0xb0;
+ goto set_syn_ok;
+ }
+ if ((mbuf[3] < 0x0d) && (rmb == 0)) {
+ j = 0xa0;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x1a) {
+ j = 0x20;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x33) {
+ j = 0x40;
+ goto set_syn_ok;
+ }
+ if (mbuf[3] < 0x4c) {
+ j = 0x50;
+ goto set_syn_ok;
+ }
+ j = 0x60;
+ set_syn_ok:
+ dev->id[c][i].devsp = (dev->id[c][i].devsp & 0x0f) | j;
+#ifdef ED_DBGP
+ printk("dev->id[%2d][%2d].devsp = %2x\n",c,i,dev->id[c][i].devsp);
+#endif
+ }
+ tmport = wkport + 0x16;
+ outb(0x80, tmport);
+}
+
+module_init(atp870u_init);
+module_exit(atp870u_exit);
+
diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h
new file mode 100644
index 000000000000..89f43af39cf2
--- /dev/null
+++ b/drivers/scsi/atp870u.h
@@ -0,0 +1,66 @@
+#ifndef _ATP870U_H
+#define _ATP870U_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* I/O Port */
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+#define qcnt 32
+#define ATP870U_SCATTER 128
+#define ATP870U_CMDLUN 1
+
+#define MAX_ADAPTER 8
+#define MAX_SCSI_ID 16
+#define ATP870U_MAX_SECTORS 128
+
+#define ATP885_DEVID 0x808A
+#define ATP880_DEVID1 0x8080
+#define ATP880_DEVID2 0x8081
+
+//#define ED_DBGP
+
+struct atp_unit
+{
+ unsigned long baseport;
+ unsigned long ioport[2];
+ unsigned long pciport[2];
+ unsigned long irq;
+ unsigned char last_cmd[2];
+ unsigned char in_snd[2];
+ unsigned char in_int[2];
+ unsigned char quhd[2];
+ unsigned char quend[2];
+ unsigned char global_map[2];
+ unsigned char chip_ver;
+ unsigned char scam_on;
+ unsigned char host_id[2];
+ unsigned int working[2];
+ unsigned short wide_id[2];
+ unsigned short active_id[2];
+ unsigned short ultra_map[2];
+ unsigned short async[2];
+ unsigned short dev_id;
+ unsigned char sp[2][16];
+ unsigned char r1f[2][16];
+ struct scsi_cmnd *quereq[2][qcnt];
+ struct atp_id
+ {
+ unsigned char dirct;
+ unsigned char devsp;
+ unsigned char devtype;
+ unsigned long tran_len;
+ unsigned long last_len;
+ unsigned char *prd_pos;
+ unsigned char *prd_table;
+ dma_addr_t prdaddr;
+ struct scsi_cmnd *curr_req;
+ } id[2][16];
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
+ unsigned int unit;
+};
+
+#endif
diff --git a/drivers/scsi/blz1230.c b/drivers/scsi/blz1230.c
new file mode 100644
index 000000000000..4cd9fcf4dc50
--- /dev/null
+++ b/drivers/scsi/blz1230.c
@@ -0,0 +1,352 @@
+/* blz1230.c: Driver for Blizzard 1230 SCSI IV Controller.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * This driver is based on the CyberStorm driver, hence the occasional
+ * reference to CyberStorm.
+ */
+
+/* TODO:
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ * to the caches and the Sparc MMU mapping.
+ * 2) Make as few routines required outside the generic driver. A lot of the
+ * routines in this file used to be inline!
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+#define MKIV 1
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define BLZ1230_ESP_ADDR 0x8000
+#define BLZ1230_DMA_ADDR 0x10000
+#define BLZ1230II_ESP_ADDR 0x10000
+#define BLZ1230II_DMA_ADDR 0x10021
+
+
+/* The Blizzard 1230 DMA interface
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Only two things can be programmed in the Blizzard DMA:
+ * 1) The data direction is controlled by the status of bit 31 (1 = write)
+ * 2) The source/dest address (word aligned, shifted one right) in bits 30-0
+ *
+ * Program DMA by first latching the highest byte of the address/direction
+ * (i.e. bits 31-24 of the long word constructed as described in steps 1+2
+ * above). Then write each byte of the address/direction (starting with the
+ * top byte, working down) to the DMA address register.
+ *
+ * Figure out interrupt status by reading the ESP status byte.
+ */
+struct blz1230_dma_registers {
+ volatile unsigned char dma_addr; /* DMA address [0x0000] */
+ unsigned char dmapad2[0x7fff];
+ volatile unsigned char dma_latch; /* DMA latch [0x8000] */
+};
+
+struct blz1230II_dma_registers {
+ volatile unsigned char dma_addr; /* DMA address [0x0000] */
+ unsigned char dmapad2[0xf];
+ volatile unsigned char dma_latch; /* DMA latch [0x0010] */
+};
+
+#define BLZ1230_DMA_WRITE 0x80000000
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are transferred to the ESP chip
+ * via PIO.
+ */
+
+/***************************************************************** Detection */
+int __init blz1230_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+ struct ESP_regs *eregs;
+ unsigned long board;
+
+#if MKIV
+#define REAL_BLZ1230_ID ZORRO_PROD_PHASE5_BLIZZARD_1230_IV_1260
+#define REAL_BLZ1230_ESP_ADDR BLZ1230_ESP_ADDR
+#define REAL_BLZ1230_DMA_ADDR BLZ1230_DMA_ADDR
+#else
+#define REAL_BLZ1230_ID ZORRO_PROD_PHASE5_BLIZZARD_1230_II_FASTLANE_Z3_CYBERSCSI_CYBERSTORM060
+#define REAL_BLZ1230_ESP_ADDR BLZ1230II_ESP_ADDR
+#define REAL_BLZ1230_DMA_ADDR BLZ1230II_DMA_ADDR
+#endif
+
+ if ((z = zorro_find_device(REAL_BLZ1230_ID, z))) {
+ board = z->resource.start;
+ if (request_mem_region(board+REAL_BLZ1230_ESP_ADDR,
+ sizeof(struct ESP_regs), "NCR53C9x")) {
+ /* Do some magic to figure out if the blizzard is
+ * equipped with a SCSI controller
+ */
+ address = ZTWO_VADDR(board);
+ eregs = (struct ESP_regs *)(address + REAL_BLZ1230_ESP_ADDR);
+ esp = esp_allocate(tpnt, (void *)board+REAL_BLZ1230_ESP_ADDR);
+
+ esp_write(eregs->esp_cfg1, (ESP_CONFIG1_PENABLE | 7));
+ udelay(5);
+ if(esp_read(eregs->esp_cfg1) != (ESP_CONFIG1_PENABLE | 7))
+ goto err_out;
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_led_on = 0;
+ esp->dma_led_off = 0;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ /* SCSI chip speed */
+ esp->cfreq = 40000000;
+
+ /* The DMA registers on the Blizzard are mapped
+ * relative to the device (i.e. in the same Zorro
+ * I/O block).
+ */
+ esp->dregs = (void *)(address + REAL_BLZ1230_DMA_ADDR);
+
+ /* ESP register base */
+ esp->eregs = eregs;
+
+ /* Set the command buffer */
+ esp->esp_command = cmd_buffer;
+ esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
+
+ esp->irq = IRQ_AMIGA_PORTS;
+ esp->slot = board+REAL_BLZ1230_ESP_ADDR;
+ if (request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
+ "Blizzard 1230 SCSI IV", esp->ehost))
+ goto err_out;
+
+ /* Figure out our scsi ID on the bus */
+ esp->scsi_id = 7;
+
+ /* We don't have a differential SCSI-bus. */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ }
+ return 0;
+
+ err_out:
+ scsi_unregister(esp->ehost);
+ esp_deallocate(esp);
+ release_mem_region(board+REAL_BLZ1230_ESP_ADDR,
+ sizeof(struct ESP_regs));
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Since the Blizzard DMA is fully dedicated to the ESP chip,
+ * the number of bytes sent (to the ESP chip) equals the number
+ * of bytes in the FIFO - there is no buffering in the DMA controller.
+ * XXXX Do I read this right? It is from host to ESP, right?
+ */
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ /* I don't think there's any limit on the Blizzard DMA. So we use what
+ * the ESP chip can handle (24 bit).
+ */
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x1000000)
+ sz = 0x1000000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+ custom.intreqr, custom.intenar));
+}
+
+void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+#if MKIV
+ struct blz1230_dma_registers *dregs =
+ (struct blz1230_dma_registers *) (esp->dregs);
+#else
+ struct blz1230II_dma_registers *dregs =
+ (struct blz1230II_dma_registers *) (esp->dregs);
+#endif
+
+ cache_clear(addr, length);
+
+ addr >>= 1;
+ addr &= ~(BLZ1230_DMA_WRITE);
+
+ /* First set latch */
+ dregs->dma_latch = (addr >> 24) & 0xff;
+
+ /* Then pump the address to the DMA address register */
+#if MKIV
+ dregs->dma_addr = (addr >> 24) & 0xff;
+#endif
+ dregs->dma_addr = (addr >> 16) & 0xff;
+ dregs->dma_addr = (addr >> 8) & 0xff;
+ dregs->dma_addr = (addr ) & 0xff;
+}
+
+void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+#if MKIV
+ struct blz1230_dma_registers *dregs =
+ (struct blz1230_dma_registers *) (esp->dregs);
+#else
+ struct blz1230II_dma_registers *dregs =
+ (struct blz1230II_dma_registers *) (esp->dregs);
+#endif
+
+ cache_push(addr, length);
+
+ addr >>= 1;
+ addr |= BLZ1230_DMA_WRITE;
+
+ /* First set latch */
+ dregs->dma_latch = (addr >> 24) & 0xff;
+
+ /* Then pump the address to the DMA address register */
+#if MKIV
+ dregs->dma_addr = (addr >> 24) & 0xff;
+#endif
+ dregs->dma_addr = (addr >> 16) & 0xff;
+ dregs->dma_addr = (addr >> 8) & 0xff;
+ dregs->dma_addr = (addr ) & 0xff;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR);
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+#define HOSTS_C
+
+int blz1230_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ unsigned long address = (unsigned long)((struct NCR_ESP *)instance->hostdata)->edev;
+ esp_deallocate((struct NCR_ESP *)instance->hostdata);
+ esp_release();
+ release_mem_region(address, sizeof(struct ESP_regs));
+ free_irq(IRQ_AMIGA_PORTS, esp_intr);
+#endif
+ return 1;
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "esp-blz1230",
+ .proc_info = esp_proc_info,
+ .name = "Blizzard1230 SCSI IV",
+ .detect = blz1230_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = blz1230_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/blz2060.c b/drivers/scsi/blz2060.c
new file mode 100644
index 000000000000..c5221d0de5ca
--- /dev/null
+++ b/drivers/scsi/blz2060.c
@@ -0,0 +1,306 @@
+/* blz2060.c: Driver for Blizzard 2060 SCSI Controller.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * This driver is based on the CyberStorm driver, hence the occasional
+ * reference to CyberStorm.
+ */
+
+/* TODO:
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ * to the caches and the Sparc MMU mapping.
+ * 2) Make as few routines required outside the generic driver. A lot of the
+ * routines in this file used to be inline!
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define BLZ2060_ESP_ADDR 0x1ff00
+#define BLZ2060_DMA_ADDR 0x1ffe0
+
+
+/* The Blizzard 2060 DMA interface
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Only two things can be programmed in the Blizzard DMA:
+ * 1) The data direction is controlled by the status of bit 31 (1 = write)
+ * 2) The source/dest address (word aligned, shifted one right) in bits 30-0
+ *
+ * Figure out interrupt status by reading the ESP status byte.
+ */
+struct blz2060_dma_registers {
+ volatile unsigned char dma_led_ctrl; /* DMA led control [0x000] */
+ unsigned char dmapad1[0x0f];
+ volatile unsigned char dma_addr0; /* DMA address (MSB) [0x010] */
+ unsigned char dmapad2[0x03];
+ volatile unsigned char dma_addr1; /* DMA address [0x014] */
+ unsigned char dmapad3[0x03];
+ volatile unsigned char dma_addr2; /* DMA address [0x018] */
+ unsigned char dmapad4[0x03];
+ volatile unsigned char dma_addr3; /* DMA address (LSB) [0x01c] */
+};
+
+#define BLZ2060_DMA_WRITE 0x80000000
+
+/* DMA control bits */
+#define BLZ2060_DMA_LED 0x02 /* HD led control 1 = off */
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static void dma_led_off(struct NCR_ESP *esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are transferred to the ESP chip
+ * via PIO.
+ */
+
+/***************************************************************** Detection */
+int __init blz2060_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+
+ if ((z = zorro_find_device(ZORRO_PROD_PHASE5_BLIZZARD_2060, z))) {
+ unsigned long board = z->resource.start;
+ if (request_mem_region(board+BLZ2060_ESP_ADDR,
+ sizeof(struct ESP_regs), "NCR53C9x")) {
+ esp = esp_allocate(tpnt, (void *)board+BLZ2060_ESP_ADDR);
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_led_on = &dma_led_on;
+ esp->dma_led_off = &dma_led_off;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ /* SCSI chip speed */
+ esp->cfreq = 40000000;
+
+ /* The DMA registers on the Blizzard are mapped
+ * relative to the device (i.e. in the same Zorro
+ * I/O block).
+ */
+ address = (unsigned long)ZTWO_VADDR(board);
+ esp->dregs = (void *)(address + BLZ2060_DMA_ADDR);
+
+ /* ESP register base */
+ esp->eregs = (struct ESP_regs *)(address + BLZ2060_ESP_ADDR);
+
+ /* Set the command buffer */
+ esp->esp_command = cmd_buffer;
+ esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
+
+ esp->irq = IRQ_AMIGA_PORTS;
+ request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
+ "Blizzard 2060 SCSI", esp->ehost);
+
+ /* Figure out our scsi ID on the bus */
+ esp->scsi_id = 7;
+
+ /* We don't have a differential SCSI-bus. */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ }
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Since the Blizzard DMA is fully dedicated to the ESP chip,
+ * the number of bytes sent (to the ESP chip) equals the number
+ * of bytes in the FIFO - there is no buffering in the DMA controller.
+ * XXXX Do I read this right? It is from host to ESP, right?
+ */
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ /* I don't think there's any limit on the Blizzard DMA. So we use what
+ * the ESP chip can handle (24 bit).
+ */
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x1000000)
+ sz = 0x1000000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+ custom.intreqr, custom.intenar));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct blz2060_dma_registers *dregs =
+ (struct blz2060_dma_registers *) (esp->dregs);
+
+ cache_clear(addr, length);
+
+ addr >>= 1;
+ addr &= ~(BLZ2060_DMA_WRITE);
+ dregs->dma_addr3 = (addr ) & 0xff;
+ dregs->dma_addr2 = (addr >> 8) & 0xff;
+ dregs->dma_addr1 = (addr >> 16) & 0xff;
+ dregs->dma_addr0 = (addr >> 24) & 0xff;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct blz2060_dma_registers *dregs =
+ (struct blz2060_dma_registers *) (esp->dregs);
+
+ cache_push(addr, length);
+
+ addr >>= 1;
+ addr |= BLZ2060_DMA_WRITE;
+ dregs->dma_addr3 = (addr ) & 0xff;
+ dregs->dma_addr2 = (addr >> 8) & 0xff;
+ dregs->dma_addr1 = (addr >> 16) & 0xff;
+ dregs->dma_addr0 = (addr >> 24) & 0xff;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR);
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+ ((struct blz2060_dma_registers *) (esp->dregs))->dma_led_ctrl =
+ BLZ2060_DMA_LED;
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+ ((struct blz2060_dma_registers *) (esp->dregs))->dma_led_ctrl = 0;
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+#define HOSTS_C
+
+int blz2060_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ unsigned long address = (unsigned long)((struct NCR_ESP *)instance->hostdata)->edev;
+
+ esp_deallocate((struct NCR_ESP *)instance->hostdata);
+ esp_release();
+ release_mem_region(address, sizeof(struct ESP_regs));
+ free_irq(IRQ_AMIGA_PORTS, esp_intr);
+#endif
+ return 1;
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "esp-blz2060",
+ .proc_info = esp_proc_info,
+ .name = "Blizzard2060 SCSI",
+ .detect = blz2060_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = blz2060_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/bvme6000.c b/drivers/scsi/bvme6000.c
new file mode 100644
index 000000000000..29c7ed30c09e
--- /dev/null
+++ b/drivers/scsi/bvme6000.c
@@ -0,0 +1,78 @@
+/*
+ * Detection routine for the NCR53c710 based BVME6000 SCSI Controllers for Linux.
+ *
+ * Based on work by Alan Hourihane
+ */
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/zorro.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/bvme6000hw.h>
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "53c7xx.h"
+#include "bvme6000.h"
+
+#include<linux/stat.h>
+
+
+int bvme6000_scsi_detect(Scsi_Host_Template *tpnt)
+{
+ static unsigned char called = 0;
+ int clock;
+ long long options;
+
+ if (called)
+ return 0;
+ if (!MACH_IS_BVME6000)
+ return 0;
+
+ tpnt->proc_name = "BVME6000";
+
+ options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
+
+ clock = 40000000; /* 66MHz SCSI Clock */
+
+ ncr53c7xx_init(tpnt, 0, 710, (unsigned long)BVME_NCR53C710_BASE,
+ 0, BVME_IRQ_SCSI, DMA_NONE,
+ options, clock);
+ called = 1;
+ return 1;
+}
+
+static int bvme6000_scsi_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "BVME6000 NCR53c710 SCSI",
+ .detect = bvme6000_scsi_detect,
+ .release = bvme6000_scsi_release,
+ .queuecommand = NCR53c7xx_queue_command,
+ .abort = NCR53c7xx_abort,
+ .reset = NCR53c7xx_reset,
+ .can_queue = 24,
+ .this_id = 7,
+ .sg_tablesize = 63,
+ .cmd_per_lun = 3,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/bvme6000.h b/drivers/scsi/bvme6000.h
new file mode 100644
index 000000000000..49b6bbb0978e
--- /dev/null
+++ b/drivers/scsi/bvme6000.h
@@ -0,0 +1,24 @@
+#ifndef BVME6000_SCSI_H
+#define BVME6000_SCSI_H
+
+#include <linux/types.h>
+
+int bvme6000_scsi_detect(Scsi_Host_Template *);
+const char *NCR53c7x0_info(void);
+int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int NCR53c7xx_abort(Scsi_Cmnd *);
+int NCR53c7x0_release (struct Scsi_Host *);
+int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int);
+void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 3
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 24
+#endif
+
+#include <scsi/scsicam.h>
+
+#endif /* BVME6000_SCSI_H */
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
new file mode 100644
index 000000000000..d625fdebe052
--- /dev/null
+++ b/drivers/scsi/constants.c
@@ -0,0 +1,1448 @@
+/*
+ * ASCII values for a number of symbolic constants, printing functions,
+ * etc.
+ * Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422)
+ * Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002)
+ * by D. Gilbert and aeb (20020609)
+ * Additions for SPC-3 T10/1416-D Rev 21 22 Sept 2004, D. Gilbert 20041025
+ */
+
+#include <linux/config.h>
+#include <linux/blkdev.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_request.h>
+#include <scsi/scsi_eh.h>
+
+
+
+/* Commands with service actions that change the command name */
+#define MAINTENANCE_IN 0xa3
+#define MAINTENANCE_OUT 0xa4
+#define SERVICE_ACTION_IN_12 0xab
+#define SERVICE_ACTION_OUT_12 0xa9
+#define SERVICE_ACTION_IN_16 0x9e
+#define SERVICE_ACTION_OUT_16 0x9f
+#define VARIABLE_LENGTH_CMD 0x7f
+
+
+
+#ifdef CONFIG_SCSI_CONSTANTS
+static const char * cdb_byte0_names[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit/Rewind", NULL, "Request Sense",
+/* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL,
+ "Reasssign Blocks",
+/* 08-0d */ "Read (6)", NULL, "Write (6)", "Seek (6)", NULL, NULL,
+/* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 13-16 */ "Verify (6)", "Recover Buffered Data", "Mode Select (6)",
+ "Reserve (6)",
+/* 17-1a */ "Release (6)", "Copy", "Erase", "Mode Sense (6)",
+/* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic",
+/* 1e-1f */ "Prevent/Allow Medium Removal", NULL,
+/* 20-22 */ NULL, NULL, NULL,
+/* 23-28 */ "Read Format Capacities", "Set Window",
+ "Read Capacity (10)", NULL, NULL, "Read (10)",
+/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase (10)",
+ "Read updated block",
+/* 2e-31 */ "Write Verify (10)", "Verify (10)", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
+/* 35-37 */ "Synchronize Cache (10)", "Lock/Unlock Cache (10)",
+ "Read Defect Data(10)",
+/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
+ "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long (10)", "Write Long (10)",
+/* 40-41 */ "Change Definition", "Write Same (10)",
+/* 42-48 */ "Read sub-channel", "Read TOC/PMA/ATIP", "Read density support",
+ "Play audio (10)", "Get configuration", "Play audio msf",
+ "Play audio track/index",
+/* 49-4f */ "Play track relative (10)", "Get event status notification",
+ "Pause/resume", "Log Select", "Log Sense", "Stop play/scan",
+ NULL,
+/* 50-55 */ "Xdwrite", "Xpwrite, Read disk info", "Xdread, Read track info",
+ "Reserve track", "Send OPC info", "Mode Select (10)",
+/* 56-5b */ "Reserve (10)", "Release (10)", "Repair track", "Read master cue",
+ "Mode Sense (10)", "Close track/session",
+/* 5c-5f */ "Read buffer capacity", "Send cue sheet", "Persistent reserve in",
+ "Persistent reserve out",
+/* 60-67 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+/* 68-6f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+/* 70-77 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+/* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Variable length",
+/* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", "Extended copy",
+ "Receive copy results",
+/* 85-89 */ "Memory Export In (16)", "Access control in", "Access control out",
+ "Read (16)", "Memory Export Out (16)",
+/* 8a-8f */ "Write (16)", NULL, "Read attributes", "Write attributes",
+ "Write and verify (16)", "Verify (16)",
+/* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)",
+ "Lock/unlock cache (16)", "Write same (16)", NULL,
+/* 95-99 */ NULL, NULL, NULL, NULL, NULL,
+/* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in (16)",
+ "Service action out (16)",
+/* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance in",
+ "Maintenance out", "Move medium/play audio(12)",
+/* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)",
+ "Play track relative(12)",
+/* aa-ae */ "Write(12)", NULL, "Erase(12), Get Performance",
+ "Read DVD structure", "Write and verify(12)",
+/* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)",
+/* b2-b4 */ "Search data low(12)", "Set limits(12)",
+ "Read element status attached",
+/* b5-b6 */ "Request volume element address", "Send volume tag, set streaming",
+/* b7-b9 */ "Read defect data(12)", "Read element status", "Read CD msf",
+/* ba-bc */ "Redundancy group (in), Scan",
+ "Redundancy group (out), Set cd-rom speed", "Spare in, Play cd",
+/* bd-bf */ "Spare out, Mechanism status", "Volume set in, Read cd",
+ "Volume set out, Send DVD structure",
+};
+
+struct value_name_pair {
+ int value;
+ const char * name;
+};
+
+static const struct value_name_pair maint_in_arr[] = {
+ {0x5, "Report device identifier"},
+ {0xa, "Report target port groups"},
+ {0xb, "Report aliases"},
+ {0xc, "Report supported operation codes"},
+ {0xd, "Report supported task management functions"},
+ {0xe, "Report priority"},
+};
+#define MAINT_IN_SZ \
+ (int)(sizeof(maint_in_arr) / sizeof(maint_in_arr[0]))
+
+static const struct value_name_pair maint_out_arr[] = {
+ {0x6, "Set device identifier"},
+ {0xa, "Set target port groups"},
+ {0xb, "Change aliases"},
+ {0xe, "Set priority"},
+};
+#define MAINT_OUT_SZ \
+ (int)(sizeof(maint_out_arr) / sizeof(maint_out_arr[0]))
+
+static const struct value_name_pair serv_in12_arr[] = {
+ {0x1, "Read media serial number"},
+};
+#define SERV_IN12_SZ \
+ (int)(sizeof(serv_in12_arr) / sizeof(serv_in12_arr[0]))
+
+static const struct value_name_pair serv_out12_arr[] = {
+ {-1, "dummy entry"},
+};
+#define SERV_OUT12_SZ \
+ (int)(sizeof(serv_out12_arr) / sizeof(serv_in12_arr[0]))
+
+static const struct value_name_pair serv_in16_arr[] = {
+ {0x10, "Read capacity(16)"},
+ {0x11, "Read long(16)"},
+};
+#define SERV_IN16_SZ \
+ (int)(sizeof(serv_in16_arr) / sizeof(serv_in16_arr[0]))
+
+static const struct value_name_pair serv_out16_arr[] = {
+ {0x11, "Write long(16)"},
+ {0x1f, "Notify data transfer device(16)"},
+};
+#define SERV_OUT16_SZ \
+ (int)(sizeof(serv_out16_arr) / sizeof(serv_in16_arr[0]))
+
+static const struct value_name_pair variable_length_arr[] = {
+ {0x1, "Rebuild(32)"},
+ {0x2, "Regenerate(32)"},
+ {0x3, "Xdread(32)"},
+ {0x4, "Xdwrite(32)"},
+ {0x5, "Xdwrite extended(32)"},
+ {0x6, "Xpwrite(32)"},
+ {0x7, "Xdwriteread(32)"},
+ {0x8, "Xdwrite extended(64)"},
+ {0x9, "Read(32)"},
+ {0xa, "Verify(32)"},
+ {0xb, "Write(32)"},
+ {0xc, "Write an verify(32)"},
+ {0xd, "Write same(32)"},
+ {0x8801, "Format OSD"},
+ {0x8802, "Create (osd)"},
+ {0x8803, "List (osd)"},
+ {0x8805, "Read (osd)"},
+ {0x8806, "Write (osd)"},
+ {0x8807, "Append (osd)"},
+ {0x8808, "Flush (osd)"},
+ {0x880a, "Remove (osd)"},
+ {0x880b, "Create partition (osd)"},
+ {0x880c, "Remove partition (osd)"},
+ {0x880e, "Get attributes (osd)"},
+ {0x880f, "Set attributes (osd)"},
+ {0x8812, "Create and write (osd)"},
+ {0x8815, "Create collection (osd)"},
+ {0x8816, "Remove collection (osd)"},
+ {0x8817, "List collection (osd)"},
+ {0x8818, "Set key (osd)"},
+ {0x8819, "Set master key (osd)"},
+ {0x881a, "Flush collection (osd)"},
+ {0x881b, "Flush partition (osd)"},
+ {0x881c, "Flush OSD"},
+ {0x8f7e, "Perform SCSI command (osd)"},
+ {0x8f7f, "Perform task management function (osd)"},
+};
+#define VARIABLE_LENGTH_SZ \
+ (int)(sizeof(variable_length_arr) / sizeof(variable_length_arr[0]))
+
+static const char * get_sa_name(const struct value_name_pair * arr,
+ int arr_sz, int service_action)
+{
+ int k;
+
+ for (k = 0; k < arr_sz; ++k, ++arr) {
+ if (service_action == arr->value)
+ break;
+ }
+ return (k < arr_sz) ? arr->name : NULL;
+}
+
+/* attempt to guess cdb length if cdb_len==0 . No trailing linefeed. */
+static void print_opcode_name(unsigned char * cdbp, int cdb_len,
+ int start_of_line)
+{
+ int sa, len, cdb0;
+ const char * name;
+ const char * leadin = start_of_line ? KERN_INFO : "";
+
+ cdb0 = cdbp[0];
+ switch(cdb0) {
+ case VARIABLE_LENGTH_CMD:
+ len = cdbp[7] + 8;
+ if (len < 10) {
+ printk("%sshort variable length command, "
+ "len=%d ext_len=%d", leadin, len, cdb_len);
+ break;
+ }
+ sa = (cdbp[8] << 8) + cdbp[9];
+ name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
+ if (name) {
+ printk("%s%s", leadin, name);
+ if ((cdb_len > 0) && (len != cdb_len))
+ printk(", in_cdb_len=%d, ext_len=%d",
+ len, cdb_len);
+ } else {
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ if ((cdb_len > 0) && (len != cdb_len))
+ printk(", in_cdb_len=%d, ext_len=%d",
+ len, cdb_len);
+ }
+ break;
+ case MAINTENANCE_IN:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ case MAINTENANCE_OUT:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(maint_out_arr, MAINT_OUT_SZ, sa);
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ case SERVICE_ACTION_IN_12:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(serv_in12_arr, SERV_IN12_SZ, sa);
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ case SERVICE_ACTION_OUT_12:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(serv_out12_arr, SERV_OUT12_SZ, sa);
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ case SERVICE_ACTION_IN_16:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(serv_in16_arr, SERV_IN16_SZ, sa);
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ case SERVICE_ACTION_OUT_16:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(serv_out16_arr, SERV_OUT16_SZ, sa);
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ default:
+ if (cdb0 < 0xc0) {
+ name = cdb_byte0_names[cdb0];
+ if (name)
+ printk("%s%s", leadin, name);
+ else
+ printk("%scdb[0]=0x%x (reserved)",
+ leadin, cdb0);
+ } else
+ printk("%scdb[0]=0x%x (vendor)", leadin, cdb0);
+ break;
+ }
+}
+
+#else /* ifndef CONFIG_SCSI_CONSTANTS */
+
+static void print_opcode_name(unsigned char * cdbp, int cdb_len,
+ int start_of_line)
+{
+ int sa, len, cdb0;
+ const char * leadin = start_of_line ? KERN_INFO : "";
+
+ cdb0 = cdbp[0];
+ switch(cdb0) {
+ case VARIABLE_LENGTH_CMD:
+ len = cdbp[7] + 8;
+ if (len < 10) {
+ printk("%sshort opcode=0x%x command, len=%d "
+ "ext_len=%d", leadin, cdb0, len, cdb_len);
+ break;
+ }
+ sa = (cdbp[8] << 8) + cdbp[9];
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ if (len != cdb_len)
+ printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
+ break;
+ case MAINTENANCE_IN:
+ case MAINTENANCE_OUT:
+ case SERVICE_ACTION_IN_12:
+ case SERVICE_ACTION_OUT_12:
+ case SERVICE_ACTION_IN_16:
+ case SERVICE_ACTION_OUT_16:
+ sa = cdbp[1] & 0x1f;
+ printk("%scdb[0]=0x%x, sa=0x%x", leadin, cdb0, sa);
+ break;
+ default:
+ if (cdb0 < 0xc0)
+ printk("%scdb[0]=0x%x", leadin, cdb0);
+ else
+ printk("%scdb[0]=0x%x (vendor)", leadin, cdb0);
+ break;
+ }
+}
+#endif
+
+void __scsi_print_command(unsigned char *command)
+{
+ int k, len;
+
+ print_opcode_name(command, 0, 1);
+ if (VARIABLE_LENGTH_CMD == command[0])
+ len = command[7] + 8;
+ else
+ len = COMMAND_SIZE(command[0]);
+ /* print out all bytes in cdb */
+ for (k = 0; k < len; ++k)
+ printk(" %02x", command[k]);
+ printk("\n");
+}
+EXPORT_SYMBOL(__scsi_print_command);
+
+/* This function (perhaps with the addition of peripheral device type)
+ * is more approriate than __scsi_print_command(). Perhaps that static
+ * can be dropped later if it replaces the __scsi_print_command version.
+ */
+static void scsi_print_cdb(unsigned char *cdb, int cdb_len, int start_of_line)
+{
+ int k;
+
+ print_opcode_name(cdb, cdb_len, start_of_line);
+ /* print out all bytes in cdb */
+ printk(":");
+ for (k = 0; k < cdb_len; ++k)
+ printk(" %02x", cdb[k]);
+ printk("\n");
+}
+
+/**
+ *
+ * print_status - print scsi status description
+ * @scsi_status: scsi status value
+ *
+ * If the status is recognized, the description is printed.
+ * Otherwise "Unknown status" is output. No trailing space.
+ * If CONFIG_SCSI_CONSTANTS is not set, then print status in hex
+ * (e.g. "0x2" for Check Condition).
+ **/
+void
+scsi_print_status(unsigned char scsi_status) {
+#ifdef CONFIG_SCSI_CONSTANTS
+ const char * ccp;
+
+ switch (scsi_status) {
+ case 0: ccp = "Good"; break;
+ case 0x2: ccp = "Check Condition"; break;
+ case 0x4: ccp = "Condition Met"; break;
+ case 0x8: ccp = "Busy"; break;
+ case 0x10: ccp = "Intermediate"; break;
+ case 0x14: ccp = "Intermediate-Condition Met"; break;
+ case 0x18: ccp = "Reservation Conflict"; break;
+ case 0x22: ccp = "Command Terminated"; break; /* obsolete */
+ case 0x28: ccp = "Task set Full"; break; /* was: Queue Full */
+ case 0x30: ccp = "ACA Active"; break;
+ case 0x40: ccp = "Task Aborted"; break;
+ default: ccp = "Unknown status";
+ }
+ printk(KERN_INFO "%s", ccp);
+#else
+ printk(KERN_INFO "0x%0x", scsi_status);
+#endif
+}
+EXPORT_SYMBOL(scsi_print_status);
+
+#ifdef CONFIG_SCSI_CONSTANTS
+
+struct error_info {
+ unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */
+ const char * text;
+};
+
+static struct error_info additional[] =
+{
+ {0x0000, "No additional sense information"},
+ {0x0001, "Filemark detected"},
+ {0x0002, "End-of-partition/medium detected"},
+ {0x0003, "Setmark detected"},
+ {0x0004, "Beginning-of-partition/medium detected"},
+ {0x0005, "End-of-data detected"},
+ {0x0006, "I/O process terminated"},
+ {0x0011, "Audio play operation in progress"},
+ {0x0012, "Audio play operation paused"},
+ {0x0013, "Audio play operation successfully completed"},
+ {0x0014, "Audio play operation stopped due to error"},
+ {0x0015, "No current audio status to return"},
+ {0x0016, "Operation in progress"},
+ {0x0017, "Cleaning requested"},
+ {0x0018, "Erase operation in progress"},
+ {0x0019, "Locate operation in progress"},
+ {0x001A, "Rewind operation in progress"},
+ {0x001B, "Set capacity operation in progress"},
+ {0x001C, "Verify operation in progress"},
+
+ {0x0100, "No index/sector signal"},
+
+ {0x0200, "No seek complete"},
+
+ {0x0300, "Peripheral device write fault"},
+ {0x0301, "No write current"},
+ {0x0302, "Excessive write errors"},
+
+ {0x0400, "Logical unit not ready, cause not reportable"},
+ {0x0401, "Logical unit is in process of becoming ready"},
+ {0x0402, "Logical unit not ready, initializing cmd. required"},
+ {0x0403, "Logical unit not ready, manual intervention required"},
+ {0x0404, "Logical unit not ready, format in progress"},
+ {0x0405, "Logical unit not ready, rebuild in progress"},
+ {0x0406, "Logical unit not ready, recalculation in progress"},
+ {0x0407, "Logical unit not ready, operation in progress"},
+ {0x0408, "Logical unit not ready, long write in progress"},
+ {0x0409, "Logical unit not ready, self-test in progress"},
+ {0x040A, "Logical unit not accessible, asymmetric access state "
+ "transition"},
+ {0x040B, "Logical unit not accessible, target port in standby state"},
+ {0x040C, "Logical unit not accessible, target port in unavailable "
+ "state"},
+ {0x0410, "Logical unit not ready, auxiliary memory not accessible"},
+ {0x0411, "Logical unit not ready, notify (enable spinup) required"},
+ {0x0412, "Logical unit not ready, offline"},
+
+ {0x0500, "Logical unit does not respond to selection"},
+
+ {0x0600, "No reference position found"},
+
+ {0x0700, "Multiple peripheral devices selected"},
+
+ {0x0800, "Logical unit communication failure"},
+ {0x0801, "Logical unit communication time-out"},
+ {0x0802, "Logical unit communication parity error"},
+ {0x0803, "Logical unit communication CRC error (Ultra-DMA/32)"},
+ {0x0804, "Unreachable copy target"},
+
+ {0x0900, "Track following error"},
+ {0x0901, "Tracking servo failure"},
+ {0x0902, "Focus servo failure"},
+ {0x0903, "Spindle servo failure"},
+ {0x0904, "Head select fault"},
+
+ {0x0A00, "Error log overflow"},
+
+ {0x0B00, "Warning"},
+ {0x0B01, "Warning - specified temperature exceeded"},
+ {0x0B02, "Warning - enclosure degraded"},
+
+ {0x0C00, "Write error"},
+ {0x0C01, "Write error - recovered with auto reallocation"},
+ {0x0C02, "Write error - auto reallocation failed"},
+ {0x0C03, "Write error - recommend reassignment"},
+ {0x0C04, "Compression check miscompare error"},
+ {0x0C05, "Data expansion occurred during compression"},
+ {0x0C06, "Block not compressible"},
+ {0x0C07, "Write error - recovery needed"},
+ {0x0C08, "Write error - recovery failed"},
+ {0x0C09, "Write error - loss of streaming"},
+ {0x0C0A, "Write error - padding blocks added"},
+ {0x0C0B, "Auxiliary memory write error"},
+ {0x0C0C, "Write error - unexpected unsolicited data"},
+ {0x0C0D, "Write error - not enough unsolicited data"},
+
+ {0x0D00, "Error detected by third party temporary initiator"},
+ {0x0D01, "Third party device failure"},
+ {0x0D02, "Copy target device not reachable"},
+ {0x0D03, "Incorrect copy target device type"},
+ {0x0D04, "Copy target device data underrun"},
+ {0x0D05, "Copy target device data overrun"},
+
+ {0x0E00, "Invalid information unit"},
+ {0x0E01, "Information unit too short"},
+ {0x0E02, "Information unit too long"},
+
+ {0x1000, "Id CRC or ECC error"},
+ {0x1001, "Data block guard check failed"},
+ {0x1002, "Data block application tag check failed"},
+ {0x1003, "Data block reference tag check failed"},
+
+ {0x1100, "Unrecovered read error"},
+ {0x1101, "Read retries exhausted"},
+ {0x1102, "Error too long to correct"},
+ {0x1103, "Multiple read errors"},
+ {0x1104, "Unrecovered read error - auto reallocate failed"},
+ {0x1105, "L-EC uncorrectable error"},
+ {0x1106, "CIRC unrecovered error"},
+ {0x1107, "Data re-synchronization error"},
+ {0x1108, "Incomplete block read"},
+ {0x1109, "No gap found"},
+ {0x110A, "Miscorrected error"},
+ {0x110B, "Unrecovered read error - recommend reassignment"},
+ {0x110C, "Unrecovered read error - recommend rewrite the data"},
+ {0x110D, "De-compression CRC error"},
+ {0x110E, "Cannot decompress using declared algorithm"},
+ {0x110F, "Error reading UPC/EAN number"},
+ {0x1110, "Error reading ISRC number"},
+ {0x1111, "Read error - loss of streaming"},
+ {0x1112, "Auxiliary memory read error"},
+ {0x1113, "Read error - failed retransmission request"},
+
+ {0x1200, "Address mark not found for id field"},
+
+ {0x1300, "Address mark not found for data field"},
+
+ {0x1400, "Recorded entity not found"},
+ {0x1401, "Record not found"},
+ {0x1402, "Filemark or setmark not found"},
+ {0x1403, "End-of-data not found"},
+ {0x1404, "Block sequence error"},
+ {0x1405, "Record not found - recommend reassignment"},
+ {0x1406, "Record not found - data auto-reallocated"},
+ {0x1407, "Locate operation failure"},
+
+ {0x1500, "Random positioning error"},
+ {0x1501, "Mechanical positioning error"},
+ {0x1502, "Positioning error detected by read of medium"},
+
+ {0x1600, "Data synchronization mark error"},
+ {0x1601, "Data sync error - data rewritten"},
+ {0x1602, "Data sync error - recommend rewrite"},
+ {0x1603, "Data sync error - data auto-reallocated"},
+ {0x1604, "Data sync error - recommend reassignment"},
+
+ {0x1700, "Recovered data with no error correction applied"},
+ {0x1701, "Recovered data with retries"},
+ {0x1702, "Recovered data with positive head offset"},
+ {0x1703, "Recovered data with negative head offset"},
+ {0x1704, "Recovered data with retries and/or circ applied"},
+ {0x1705, "Recovered data using previous sector id"},
+ {0x1706, "Recovered data without ECC - data auto-reallocated"},
+ {0x1707, "Recovered data without ECC - recommend reassignment"},
+ {0x1708, "Recovered data without ECC - recommend rewrite"},
+ {0x1709, "Recovered data without ECC - data rewritten"},
+
+ {0x1800, "Recovered data with error correction applied"},
+ {0x1801, "Recovered data with error corr. & retries applied"},
+ {0x1802, "Recovered data - data auto-reallocated"},
+ {0x1803, "Recovered data with CIRC"},
+ {0x1804, "Recovered data with L-EC"},
+ {0x1805, "Recovered data - recommend reassignment"},
+ {0x1806, "Recovered data - recommend rewrite"},
+ {0x1807, "Recovered data with ECC - data rewritten"},
+ {0x1808, "Recovered data with linking"},
+
+ {0x1900, "Defect list error"},
+ {0x1901, "Defect list not available"},
+ {0x1902, "Defect list error in primary list"},
+ {0x1903, "Defect list error in grown list"},
+
+ {0x1A00, "Parameter list length error"},
+
+ {0x1B00, "Synchronous data transfer error"},
+
+ {0x1C00, "Defect list not found"},
+ {0x1C01, "Primary defect list not found"},
+ {0x1C02, "Grown defect list not found"},
+
+ {0x1D00, "Miscompare during verify operation"},
+
+ {0x1E00, "Recovered id with ECC correction"},
+
+ {0x1F00, "Partial defect list transfer"},
+
+ {0x2000, "Invalid command operation code"},
+ {0x2001, "Access denied - initiator pending-enrolled"},
+ {0x2002, "Access denied - no access rights"},
+ {0x2003, "Access denied - invalid mgmt id key"},
+ {0x2004, "Illegal command while in write capable state"},
+ {0x2005, "Obsolete"},
+ {0x2006, "Illegal command while in explicit address mode"},
+ {0x2007, "Illegal command while in implicit address mode"},
+ {0x2008, "Access denied - enrollment conflict"},
+ {0x2009, "Access denied - invalid LU identifier"},
+ {0x200A, "Access denied - invalid proxy token"},
+ {0x200B, "Access denied - ACL LUN conflict"},
+
+ {0x2100, "Logical block address out of range"},
+ {0x2101, "Invalid element address"},
+ {0x2102, "Invalid address for write"},
+
+ {0x2200, "Illegal function (use 20 00, 24 00, or 26 00)"},
+
+ {0x2400, "Invalid field in cdb"},
+ {0x2401, "CDB decryption error"},
+ {0x2404, "Security audit value frozen"},
+ {0x2405, "Security working key frozen"},
+ {0x2406, "Nonce not unique"},
+ {0x2407, "Nonce timestamp out of range"},
+
+ {0x2500, "Logical unit not supported"},
+
+ {0x2600, "Invalid field in parameter list"},
+ {0x2601, "Parameter not supported"},
+ {0x2602, "Parameter value invalid"},
+ {0x2603, "Threshold parameters not supported"},
+ {0x2604, "Invalid release of persistent reservation"},
+ {0x2605, "Data decryption error"},
+ {0x2606, "Too many target descriptors"},
+ {0x2607, "Unsupported target descriptor type code"},
+ {0x2608, "Too many segment descriptors"},
+ {0x2609, "Unsupported segment descriptor type code"},
+ {0x260A, "Unexpected inexact segment"},
+ {0x260B, "Inline data length exceeded"},
+ {0x260C, "Invalid operation for copy source or destination"},
+ {0x260D, "Copy segment granularity violation"},
+ {0x260E, "Invalid parameter while port is enabled"},
+ {0x260F, "Invalid data-out buffer integrity"},
+
+ {0x2700, "Write protected"},
+ {0x2701, "Hardware write protected"},
+ {0x2702, "Logical unit software write protected"},
+ {0x2703, "Associated write protect"},
+ {0x2704, "Persistent write protect"},
+ {0x2705, "Permanent write protect"},
+ {0x2706, "Conditional write protect"},
+
+ {0x2800, "Not ready to ready change, medium may have changed"},
+ {0x2801, "Import or export element accessed"},
+
+ {0x2900, "Power on, reset, or bus device reset occurred"},
+ {0x2901, "Power on occurred"},
+ {0x2902, "Scsi bus reset occurred"},
+ {0x2903, "Bus device reset function occurred"},
+ {0x2904, "Device internal reset"},
+ {0x2905, "Transceiver mode changed to single-ended"},
+ {0x2906, "Transceiver mode changed to lvd"},
+ {0x2907, "I_T nexus loss occurred"},
+
+ {0x2A00, "Parameters changed"},
+ {0x2A01, "Mode parameters changed"},
+ {0x2A02, "Log parameters changed"},
+ {0x2A03, "Reservations preempted"},
+ {0x2A04, "Reservations released"},
+ {0x2A05, "Registrations preempted"},
+ {0x2A06, "Asymmetric access state changed"},
+ {0x2A07, "Implicit asymmetric access state transition failed"},
+ {0x2A08, "Priority changed"},
+ {0x2A09, "Capacity data has changed"},
+
+ {0x2B00, "Copy cannot execute since host cannot disconnect"},
+
+ {0x2C00, "Command sequence error"},
+ {0x2C01, "Too many windows specified"},
+ {0x2C02, "Invalid combination of windows specified"},
+ {0x2C03, "Current program area is not empty"},
+ {0x2C04, "Current program area is empty"},
+ {0x2C05, "Illegal power condition request"},
+ {0x2C06, "Persistent prevent conflict"},
+ {0x2C07, "Previous busy status"},
+ {0x2C08, "Previous task set full status"},
+ {0x2C09, "Previous reservation conflict status"},
+ {0x2C0A, "Partition or collection contains user objects"},
+ {0x2C0B, "Not reserved"},
+
+ {0x2D00, "Overwrite error on update in place"},
+
+ {0x2E00, "Insufficient time for operation"},
+
+ {0x2F00, "Commands cleared by another initiator"},
+
+ {0x3000, "Incompatible medium installed"},
+ {0x3001, "Cannot read medium - unknown format"},
+ {0x3002, "Cannot read medium - incompatible format"},
+ {0x3003, "Cleaning cartridge installed"},
+ {0x3004, "Cannot write medium - unknown format"},
+ {0x3005, "Cannot write medium - incompatible format"},
+ {0x3006, "Cannot format medium - incompatible medium"},
+ {0x3007, "Cleaning failure"},
+ {0x3008, "Cannot write - application code mismatch"},
+ {0x3009, "Current session not fixated for append"},
+ {0x300A, "Cleaning request rejected"},
+ {0x300C, "WORM medium, overwrite attempted"},
+ {0x3010, "Medium not formatted"},
+
+ {0x3100, "Medium format corrupted"},
+ {0x3101, "Format command failed"},
+ {0x3102, "Zoned formatting failed due to spare linking"},
+
+ {0x3200, "No defect spare location available"},
+ {0x3201, "Defect list update failure"},
+
+ {0x3300, "Tape length error"},
+
+ {0x3400, "Enclosure failure"},
+
+ {0x3500, "Enclosure services failure"},
+ {0x3501, "Unsupported enclosure function"},
+ {0x3502, "Enclosure services unavailable"},
+ {0x3503, "Enclosure services transfer failure"},
+ {0x3504, "Enclosure services transfer refused"},
+ {0x3505, "Enclosure services checksum error"},
+
+ {0x3600, "Ribbon, ink, or toner failure"},
+
+ {0x3700, "Rounded parameter"},
+
+ {0x3800, "Event status notification"},
+ {0x3802, "Esn - power management class event"},
+ {0x3804, "Esn - media class event"},
+ {0x3806, "Esn - device busy class event"},
+
+ {0x3900, "Saving parameters not supported"},
+
+ {0x3A00, "Medium not present"},
+ {0x3A01, "Medium not present - tray closed"},
+ {0x3A02, "Medium not present - tray open"},
+ {0x3A03, "Medium not present - loadable"},
+ {0x3A04, "Medium not present - medium auxiliary memory accessible"},
+
+ {0x3B00, "Sequential positioning error"},
+ {0x3B01, "Tape position error at beginning-of-medium"},
+ {0x3B02, "Tape position error at end-of-medium"},
+ {0x3B03, "Tape or electronic vertical forms unit not ready"},
+ {0x3B04, "Slew failure"},
+ {0x3B05, "Paper jam"},
+ {0x3B06, "Failed to sense top-of-form"},
+ {0x3B07, "Failed to sense bottom-of-form"},
+ {0x3B08, "Reposition error"},
+ {0x3B09, "Read past end of medium"},
+ {0x3B0A, "Read past beginning of medium"},
+ {0x3B0B, "Position past end of medium"},
+ {0x3B0C, "Position past beginning of medium"},
+ {0x3B0D, "Medium destination element full"},
+ {0x3B0E, "Medium source element empty"},
+ {0x3B0F, "End of medium reached"},
+ {0x3B11, "Medium magazine not accessible"},
+ {0x3B12, "Medium magazine removed"},
+ {0x3B13, "Medium magazine inserted"},
+ {0x3B14, "Medium magazine locked"},
+ {0x3B15, "Medium magazine unlocked"},
+ {0x3B16, "Mechanical positioning or changer error"},
+ {0x3B17, "Read past end of user object"},
+
+ {0x3D00, "Invalid bits in identify message"},
+
+ {0x3E00, "Logical unit has not self-configured yet"},
+ {0x3E01, "Logical unit failure"},
+ {0x3E02, "Timeout on logical unit"},
+ {0x3E03, "Logical unit failed self-test"},
+ {0x3E04, "Logical unit unable to update self-test log"},
+
+ {0x3F00, "Target operating conditions have changed"},
+ {0x3F01, "Microcode has been changed"},
+ {0x3F02, "Changed operating definition"},
+ {0x3F03, "Inquiry data has changed"},
+ {0x3F04, "Component device attached"},
+ {0x3F05, "Device identifier changed"},
+ {0x3F06, "Redundancy group created or modified"},
+ {0x3F07, "Redundancy group deleted"},
+ {0x3F08, "Spare created or modified"},
+ {0x3F09, "Spare deleted"},
+ {0x3F0A, "Volume set created or modified"},
+ {0x3F0B, "Volume set deleted"},
+ {0x3F0C, "Volume set deassigned"},
+ {0x3F0D, "Volume set reassigned"},
+ {0x3F0E, "Reported luns data has changed"},
+ {0x3F0F, "Echo buffer overwritten"},
+ {0x3F10, "Medium loadable"},
+ {0x3F11, "Medium auxiliary memory accessible"},
+/*
+ * {0x40NN, "Ram failure"},
+ * {0x40NN, "Diagnostic failure on component nn"},
+ * {0x41NN, "Data path failure"},
+ * {0x42NN, "Power-on or self-test failure"},
+ */
+ {0x4300, "Message error"},
+
+ {0x4400, "Internal target failure"},
+
+ {0x4500, "Select or reselect failure"},
+
+ {0x4600, "Unsuccessful soft reset"},
+
+ {0x4700, "Scsi parity error"},
+ {0x4701, "Data phase CRC error detected"},
+ {0x4702, "Scsi parity error detected during st data phase"},
+ {0x4703, "Information unit CRC error detected"},
+ {0x4704, "Asynchronous information protection error detected"},
+ {0x4705, "Protocol service CRC error"},
+ {0x477f, "Some commands cleared by iSCSI Protocol event"},
+
+ {0x4800, "Initiator detected error message received"},
+
+ {0x4900, "Invalid message error"},
+
+ {0x4A00, "Command phase error"},
+
+ {0x4B00, "Data phase error"},
+ {0x4B01, "Invalid target port transfer tag received"},
+ {0x4B02, "Too much write data"},
+ {0x4B03, "Ack/nak timeout"},
+ {0x4B04, "Nak received"},
+ {0x4B05, "Data offset error"},
+ {0x4B06, "Initiator response timeout"},
+
+ {0x4C00, "Logical unit failed self-configuration"},
+/*
+ * {0x4DNN, "Tagged overlapped commands (nn = queue tag)"},
+ */
+ {0x4E00, "Overlapped commands attempted"},
+
+ {0x5000, "Write append error"},
+ {0x5001, "Write append position error"},
+ {0x5002, "Position error related to timing"},
+
+ {0x5100, "Erase failure"},
+ {0x5101, "Erase failure - incomplete erase operation detected"},
+
+ {0x5200, "Cartridge fault"},
+
+ {0x5300, "Media load or eject failed"},
+ {0x5301, "Unload tape failure"},
+ {0x5302, "Medium removal prevented"},
+
+ {0x5400, "Scsi to host system interface failure"},
+
+ {0x5500, "System resource failure"},
+ {0x5501, "System buffer full"},
+ {0x5502, "Insufficient reservation resources"},
+ {0x5503, "Insufficient resources"},
+ {0x5504, "Insufficient registration resources"},
+ {0x5505, "Insufficient access control resources"},
+ {0x5506, "Auxiliary memory out of space"},
+ {0x5507, "Quota error"},
+
+ {0x5700, "Unable to recover table-of-contents"},
+
+ {0x5800, "Generation does not exist"},
+
+ {0x5900, "Updated block read"},
+
+ {0x5A00, "Operator request or state change input"},
+ {0x5A01, "Operator medium removal request"},
+ {0x5A02, "Operator selected write protect"},
+ {0x5A03, "Operator selected write permit"},
+
+ {0x5B00, "Log exception"},
+ {0x5B01, "Threshold condition met"},
+ {0x5B02, "Log counter at maximum"},
+ {0x5B03, "Log list codes exhausted"},
+
+ {0x5C00, "Rpl status change"},
+ {0x5C01, "Spindles synchronized"},
+ {0x5C02, "Spindles not synchronized"},
+
+ {0x5D00, "Failure prediction threshold exceeded"},
+ {0x5D01, "Media failure prediction threshold exceeded"},
+ {0x5D02, "Logical unit failure prediction threshold exceeded"},
+ {0x5D03, "Spare area exhaustion prediction threshold exceeded"},
+ {0x5D10, "Hardware impending failure general hard drive failure"},
+ {0x5D11, "Hardware impending failure drive error rate too high"},
+ {0x5D12, "Hardware impending failure data error rate too high"},
+ {0x5D13, "Hardware impending failure seek error rate too high"},
+ {0x5D14, "Hardware impending failure too many block reassigns"},
+ {0x5D15, "Hardware impending failure access times too high"},
+ {0x5D16, "Hardware impending failure start unit times too high"},
+ {0x5D17, "Hardware impending failure channel parametrics"},
+ {0x5D18, "Hardware impending failure controller detected"},
+ {0x5D19, "Hardware impending failure throughput performance"},
+ {0x5D1A, "Hardware impending failure seek time performance"},
+ {0x5D1B, "Hardware impending failure spin-up retry count"},
+ {0x5D1C, "Hardware impending failure drive calibration retry count"},
+ {0x5D20, "Controller impending failure general hard drive failure"},
+ {0x5D21, "Controller impending failure drive error rate too high"},
+ {0x5D22, "Controller impending failure data error rate too high"},
+ {0x5D23, "Controller impending failure seek error rate too high"},
+ {0x5D24, "Controller impending failure too many block reassigns"},
+ {0x5D25, "Controller impending failure access times too high"},
+ {0x5D26, "Controller impending failure start unit times too high"},
+ {0x5D27, "Controller impending failure channel parametrics"},
+ {0x5D28, "Controller impending failure controller detected"},
+ {0x5D29, "Controller impending failure throughput performance"},
+ {0x5D2A, "Controller impending failure seek time performance"},
+ {0x5D2B, "Controller impending failure spin-up retry count"},
+ {0x5D2C, "Controller impending failure drive calibration retry count"},
+ {0x5D30, "Data channel impending failure general hard drive failure"},
+ {0x5D31, "Data channel impending failure drive error rate too high"},
+ {0x5D32, "Data channel impending failure data error rate too high"},
+ {0x5D33, "Data channel impending failure seek error rate too high"},
+ {0x5D34, "Data channel impending failure too many block reassigns"},
+ {0x5D35, "Data channel impending failure access times too high"},
+ {0x5D36, "Data channel impending failure start unit times too high"},
+ {0x5D37, "Data channel impending failure channel parametrics"},
+ {0x5D38, "Data channel impending failure controller detected"},
+ {0x5D39, "Data channel impending failure throughput performance"},
+ {0x5D3A, "Data channel impending failure seek time performance"},
+ {0x5D3B, "Data channel impending failure spin-up retry count"},
+ {0x5D3C, "Data channel impending failure drive calibration retry "
+ "count"},
+ {0x5D40, "Servo impending failure general hard drive failure"},
+ {0x5D41, "Servo impending failure drive error rate too high"},
+ {0x5D42, "Servo impending failure data error rate too high"},
+ {0x5D43, "Servo impending failure seek error rate too high"},
+ {0x5D44, "Servo impending failure too many block reassigns"},
+ {0x5D45, "Servo impending failure access times too high"},
+ {0x5D46, "Servo impending failure start unit times too high"},
+ {0x5D47, "Servo impending failure channel parametrics"},
+ {0x5D48, "Servo impending failure controller detected"},
+ {0x5D49, "Servo impending failure throughput performance"},
+ {0x5D4A, "Servo impending failure seek time performance"},
+ {0x5D4B, "Servo impending failure spin-up retry count"},
+ {0x5D4C, "Servo impending failure drive calibration retry count"},
+ {0x5D50, "Spindle impending failure general hard drive failure"},
+ {0x5D51, "Spindle impending failure drive error rate too high"},
+ {0x5D52, "Spindle impending failure data error rate too high"},
+ {0x5D53, "Spindle impending failure seek error rate too high"},
+ {0x5D54, "Spindle impending failure too many block reassigns"},
+ {0x5D55, "Spindle impending failure access times too high"},
+ {0x5D56, "Spindle impending failure start unit times too high"},
+ {0x5D57, "Spindle impending failure channel parametrics"},
+ {0x5D58, "Spindle impending failure controller detected"},
+ {0x5D59, "Spindle impending failure throughput performance"},
+ {0x5D5A, "Spindle impending failure seek time performance"},
+ {0x5D5B, "Spindle impending failure spin-up retry count"},
+ {0x5D5C, "Spindle impending failure drive calibration retry count"},
+ {0x5D60, "Firmware impending failure general hard drive failure"},
+ {0x5D61, "Firmware impending failure drive error rate too high"},
+ {0x5D62, "Firmware impending failure data error rate too high"},
+ {0x5D63, "Firmware impending failure seek error rate too high"},
+ {0x5D64, "Firmware impending failure too many block reassigns"},
+ {0x5D65, "Firmware impending failure access times too high"},
+ {0x5D66, "Firmware impending failure start unit times too high"},
+ {0x5D67, "Firmware impending failure channel parametrics"},
+ {0x5D68, "Firmware impending failure controller detected"},
+ {0x5D69, "Firmware impending failure throughput performance"},
+ {0x5D6A, "Firmware impending failure seek time performance"},
+ {0x5D6B, "Firmware impending failure spin-up retry count"},
+ {0x5D6C, "Firmware impending failure drive calibration retry count"},
+ {0x5DFF, "Failure prediction threshold exceeded (false)"},
+
+ {0x5E00, "Low power condition on"},
+ {0x5E01, "Idle condition activated by timer"},
+ {0x5E02, "Standby condition activated by timer"},
+ {0x5E03, "Idle condition activated by command"},
+ {0x5E04, "Standby condition activated by command"},
+ {0x5E41, "Power state change to active"},
+ {0x5E42, "Power state change to idle"},
+ {0x5E43, "Power state change to standby"},
+ {0x5E45, "Power state change to sleep"},
+ {0x5E47, "Power state change to device control"},
+
+ {0x6000, "Lamp failure"},
+
+ {0x6100, "Video acquisition error"},
+ {0x6101, "Unable to acquire video"},
+ {0x6102, "Out of focus"},
+
+ {0x6200, "Scan head positioning error"},
+
+ {0x6300, "End of user area encountered on this track"},
+ {0x6301, "Packet does not fit in available space"},
+
+ {0x6400, "Illegal mode for this track"},
+ {0x6401, "Invalid packet size"},
+
+ {0x6500, "Voltage fault"},
+
+ {0x6600, "Automatic document feeder cover up"},
+ {0x6601, "Automatic document feeder lift up"},
+ {0x6602, "Document jam in automatic document feeder"},
+ {0x6603, "Document miss feed automatic in document feeder"},
+
+ {0x6700, "Configuration failure"},
+ {0x6701, "Configuration of incapable logical units failed"},
+ {0x6702, "Add logical unit failed"},
+ {0x6703, "Modification of logical unit failed"},
+ {0x6704, "Exchange of logical unit failed"},
+ {0x6705, "Remove of logical unit failed"},
+ {0x6706, "Attachment of logical unit failed"},
+ {0x6707, "Creation of logical unit failed"},
+ {0x6708, "Assign failure occurred"},
+ {0x6709, "Multiply assigned logical unit"},
+ {0x670A, "Set target port groups command failed"},
+
+ {0x6800, "Logical unit not configured"},
+
+ {0x6900, "Data loss on logical unit"},
+ {0x6901, "Multiple logical unit failures"},
+ {0x6902, "Parity/data mismatch"},
+
+ {0x6A00, "Informational, refer to log"},
+
+ {0x6B00, "State change has occurred"},
+ {0x6B01, "Redundancy level got better"},
+ {0x6B02, "Redundancy level got worse"},
+
+ {0x6C00, "Rebuild failure occurred"},
+
+ {0x6D00, "Recalculate failure occurred"},
+
+ {0x6E00, "Command to logical unit failed"},
+
+ {0x6F00, "Copy protection key exchange failure - authentication "
+ "failure"},
+ {0x6F01, "Copy protection key exchange failure - key not present"},
+ {0x6F02, "Copy protection key exchange failure - key not established"},
+ {0x6F03, "Read of scrambled sector without authentication"},
+ {0x6F04, "Media region code is mismatched to logical unit region"},
+ {0x6F05, "Drive region must be permanent/region reset count error"},
+/*
+ * {0x70NN, "Decompression exception short algorithm id of nn"},
+ */
+ {0x7100, "Decompression exception long algorithm id"},
+
+ {0x7200, "Session fixation error"},
+ {0x7201, "Session fixation error writing lead-in"},
+ {0x7202, "Session fixation error writing lead-out"},
+ {0x7203, "Session fixation error - incomplete track in session"},
+ {0x7204, "Empty or partially written reserved track"},
+ {0x7205, "No more track reservations allowed"},
+
+ {0x7300, "Cd control error"},
+ {0x7301, "Power calibration area almost full"},
+ {0x7302, "Power calibration area is full"},
+ {0x7303, "Power calibration area error"},
+ {0x7304, "Program memory area update failure"},
+ {0x7305, "Program memory area is full"},
+ {0x7306, "RMA/PMA is almost full"},
+ {0, NULL}
+};
+
+struct error_info2 {
+ unsigned char code1, code2_min, code2_max;
+ const char * fmt;
+};
+
+static struct error_info2 additional2[] =
+{
+ {0x40,0x00,0x7f,"Ram failure (%x)"},
+ {0x40,0x80,0xff,"Diagnostic failure on component (%x)"},
+ {0x41,0x00,0xff,"Data path failure (%x)"},
+ {0x42,0x00,0xff,"Power-on or self-test failure (%x)"},
+ {0x4D,0x00,0xff,"Tagged overlapped commands (queue tag %x)"},
+ {0x70,0x00,0xff,"Decompression exception short algorithm id of %x"},
+ {0, 0, 0, NULL}
+};
+
+/* description of the sense key values */
+static const char *snstext[] = {
+ "No Sense", /* 0: There is no sense information */
+ "Recovered Error", /* 1: The last command completed successfully
+ but used error correction */
+ "Not Ready", /* 2: The addressed target is not ready */
+ "Medium Error", /* 3: Data error detected on the medium */
+ "Hardware Error", /* 4: Controller or device failure */
+ "Illegal Request", /* 5: Error in request */
+ "Unit Attention", /* 6: Removable medium was changed, or
+ the target has been reset, or ... */
+ "Data Protect", /* 7: Access to the data is blocked */
+ "Blank Check", /* 8: Reached unexpected written or unwritten
+ region of the medium */
+ "Vendor Specific(9)",
+ "Copy Aborted", /* A: COPY or COMPARE was aborted */
+ "Aborted Command", /* B: The target aborted the command */
+ "Equal", /* C: A SEARCH DATA command found data equal */
+ "Volume Overflow", /* D: Medium full with still data to be written */
+ "Miscompare", /* E: Source data and data on the medium
+ do not agree */
+};
+#endif
+
+/* Get sense key string or NULL if not available */
+const char *
+scsi_sense_key_string(unsigned char key) {
+#ifdef CONFIG_SCSI_CONSTANTS
+ if (key <= 0xE)
+ return snstext[key];
+#endif
+ return NULL;
+}
+EXPORT_SYMBOL(scsi_sense_key_string);
+
+/*
+ * Get additional sense code string or NULL if not available.
+ * This string may contain a "%x" and should be printed with ascq as arg.
+ */
+const char *
+scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
+#ifdef CONFIG_SCSI_CONSTANTS
+ int i;
+ unsigned short code = ((asc << 8) | ascq);
+
+ for (i=0; additional[i].text; i++)
+ if (additional[i].code12 == code)
+ return additional[i].text;
+ for (i=0; additional2[i].fmt; i++)
+ if (additional2[i].code1 == asc &&
+ additional2[i].code2_min >= ascq &&
+ additional2[i].code2_max <= ascq)
+ return additional2[i].fmt;
+#endif
+ return NULL;
+}
+EXPORT_SYMBOL(scsi_extd_sense_format);
+
+/* Print extended sense information; no leadin, no linefeed */
+static void
+scsi_show_extd_sense(unsigned char asc, unsigned char ascq)
+{
+ const char *extd_sense_fmt = scsi_extd_sense_format(asc, ascq);
+
+ if (extd_sense_fmt) {
+ if (strstr(extd_sense_fmt, "%x")) {
+ printk("Additional sense: ");
+ printk(extd_sense_fmt, ascq);
+ } else
+ printk("Additional sense: %s", extd_sense_fmt);
+ } else {
+ if (asc >= 0x80)
+ printk("<<vendor>> ASC=0x%x ASCQ=0x%x", asc, ascq);
+ if (ascq >= 0x80)
+ printk("ASC=0x%x <<vendor>> ASCQ=0x%x", asc, ascq);
+ else
+ printk("ASC=0x%x ASCQ=0x%x", asc, ascq);
+ }
+}
+
+/* Print sense information */
+void
+__scsi_print_sense(const char *name, const unsigned char *sense_buffer,
+ int sense_len)
+{
+ int k, num, res;
+ unsigned int info;
+ const char *error;
+ const char *sense_txt;
+ struct scsi_sense_hdr ssh;
+
+ res = scsi_normalize_sense(sense_buffer, sense_len, &ssh);
+ if (0 == res) {
+ /* this may be SCSI-1 sense data */
+ num = (sense_len < 32) ? sense_len : 32;
+ printk(KERN_INFO "Unrecognized sense data (in hex):");
+ for (k = 0; k < num; ++k) {
+ if (0 == (k % 16)) {
+ printk("\n");
+ printk(KERN_INFO " ");
+ }
+ printk("%02x ", sense_buffer[k]);
+ }
+ printk("\n");
+ return;
+ }
+
+ /* An example of deferred is when an earlier write to disk cache
+ * succeeded, but now the disk discovers that it cannot write the
+ * data to the magnetic media.
+ */
+ error = scsi_sense_is_deferred(&ssh) ?
+ "<<DEFERRED>>" : "Current";
+ printk(KERN_INFO "%s: %s", name, error);
+ if (ssh.response_code >= 0x72)
+ printk(" [descriptor]");
+
+ sense_txt = scsi_sense_key_string(ssh.sense_key);
+ if (sense_txt)
+ printk(": sense key: %s\n", sense_txt);
+ else
+ printk(": sense key=0x%x\n", ssh.sense_key);
+ printk(KERN_INFO " ");
+ scsi_show_extd_sense(ssh.asc, ssh.ascq);
+ printk("\n");
+
+ if (ssh.response_code < 0x72) {
+ /* only decode extras for "fixed" format now */
+ char buff[80];
+ int blen, fixed_valid;
+
+ fixed_valid = sense_buffer[0] & 0x80;
+ info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) |
+ (sense_buffer[5] << 8) | sense_buffer[6]);
+ res = 0;
+ memset(buff, 0, sizeof(buff));
+ blen = sizeof(buff) - 1;
+ if (fixed_valid)
+ res += snprintf(buff + res, blen - res,
+ "Info fld=0x%x", info);
+ if (sense_buffer[2] & 0x80) {
+ /* current command has read a filemark */
+ if (res > 0)
+ res += snprintf(buff + res, blen - res, ", ");
+ res += snprintf(buff + res, blen - res, "FMK");
+ }
+ if (sense_buffer[2] & 0x40) {
+ /* end-of-medium condition exists */
+ if (res > 0)
+ res += snprintf(buff + res, blen - res, ", ");
+ res += snprintf(buff + res, blen - res, "EOM");
+ }
+ if (sense_buffer[2] & 0x20) {
+ /* incorrect block length requested */
+ if (res > 0)
+ res += snprintf(buff + res, blen - res, ", ");
+ res += snprintf(buff + res, blen - res, "ILI");
+ }
+ if (res > 0)
+ printk(KERN_INFO "%s\n", buff);
+ } else if (ssh.additional_length > 0) {
+ /* descriptor format with sense descriptors */
+ num = 8 + ssh.additional_length;
+ num = (sense_len < num) ? sense_len : num;
+ printk(KERN_INFO "Descriptor sense data with sense "
+ "descriptors (in hex):");
+ for (k = 0; k < num; ++k) {
+ if (0 == (k % 16)) {
+ printk("\n");
+ printk(KERN_INFO " ");
+ }
+ printk("%02x ", sense_buffer[k]);
+ }
+ printk("\n");
+ }
+}
+EXPORT_SYMBOL(__scsi_print_sense);
+
+void scsi_print_sense(const char *devclass, struct scsi_cmnd *cmd)
+{
+ const char *name = devclass;
+
+ if (cmd->request->rq_disk)
+ name = cmd->request->rq_disk->disk_name;
+ __scsi_print_sense(name, cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
+}
+EXPORT_SYMBOL(scsi_print_sense);
+
+void scsi_print_req_sense(const char *devclass, struct scsi_request *sreq)
+{
+ const char *name = devclass;
+
+ if (sreq->sr_request->rq_disk)
+ name = sreq->sr_request->rq_disk->disk_name;
+ __scsi_print_sense(name, sreq->sr_sense_buffer, SCSI_SENSE_BUFFERSIZE);
+}
+EXPORT_SYMBOL(scsi_print_req_sense);
+
+#ifdef CONFIG_SCSI_CONSTANTS
+static const char *one_byte_msgs[] = {
+/* 0x00 */ "Command Complete", NULL, "Save Pointers",
+/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error",
+/* 0x06 */ "Abort", "Message Reject", "Nop", "Message Parity Error",
+/* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag",
+/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue",
+/* 0x0f */ "Initiate Recovery", "Release Recovery"
+};
+#define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs) / sizeof (const char *))
+
+static const char *two_byte_msgs[] = {
+/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag"
+/* 0x23 */ "Ignore Wide Residue"
+};
+#define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
+
+static const char *extended_msgs[] = {
+/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
+/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request"
+};
+#define NO_EXTENDED_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
+
+
+int scsi_print_msg (const unsigned char *msg)
+{
+ int len = 0, i;
+ if (msg[0] == EXTENDED_MESSAGE) {
+ len = 3 + msg[1];
+ if (msg[2] < NO_EXTENDED_MSGS)
+ printk ("%s ", extended_msgs[msg[2]]);
+ else
+ printk ("Extended Message, reserved code (0x%02x) ",
+ (int) msg[2]);
+ switch (msg[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ printk("pointer = %d", (int) (msg[3] << 24) |
+ (msg[4] << 16) | (msg[5] << 8) | msg[6]);
+ break;
+ case EXTENDED_SDTR:
+ printk("period = %d ns, offset = %d",
+ (int) msg[3] * 4, (int) msg[4]);
+ break;
+ case EXTENDED_WDTR:
+ printk("width = 2^%d bytes", msg[3]);
+ break;
+ default:
+ for (i = 2; i < len; ++i)
+ printk("%02x ", msg[i]);
+ }
+ /* Identify */
+ } else if (msg[0] & 0x80) {
+ printk("Identify disconnect %sallowed %s %d ",
+ (msg[0] & 0x40) ? "" : "not ",
+ (msg[0] & 0x20) ? "target routine" : "lun",
+ msg[0] & 0x7);
+ len = 1;
+ /* Normal One byte */
+ } else if (msg[0] < 0x1f) {
+ if (msg[0] < NO_ONE_BYTE_MSGS)
+ printk(one_byte_msgs[msg[0]]);
+ else
+ printk("reserved (%02x) ", msg[0]);
+ len = 1;
+ /* Two byte */
+ } else if (msg[0] <= 0x2f) {
+ if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
+ printk("%s %02x ", two_byte_msgs[msg[0] - 0x20],
+ msg[1]);
+ else
+ printk("reserved two byte (%02x %02x) ",
+ msg[0], msg[1]);
+ len = 2;
+ } else
+ printk("reserved");
+ return len;
+}
+EXPORT_SYMBOL(scsi_print_msg);
+
+#else /* ifndef CONFIG_SCSI_CONSTANTS */
+
+int scsi_print_msg (const unsigned char *msg)
+{
+ int len = 0, i;
+
+ if (msg[0] == EXTENDED_MESSAGE) {
+ len = 3 + msg[1];
+ for (i = 0; i < len; ++i)
+ printk("%02x ", msg[i]);
+ /* Identify */
+ } else if (msg[0] & 0x80) {
+ printk("%02x ", msg[0]);
+ len = 1;
+ /* Normal One byte */
+ } else if (msg[0] < 0x1f) {
+ printk("%02x ", msg[0]);
+ len = 1;
+ /* Two byte */
+ } else if (msg[0] <= 0x2f) {
+ printk("%02x %02x", msg[0], msg[1]);
+ len = 2;
+ } else
+ printk("%02x ", msg[0]);
+ return len;
+}
+EXPORT_SYMBOL(scsi_print_msg);
+#endif /* ! CONFIG_SCSI_CONSTANTS */
+
+void scsi_print_command(struct scsi_cmnd *cmd)
+{
+ /* Assume appended output (i.e. not at start of line) */
+ printk("scsi%d : destination target %d, lun %d\n",
+ cmd->device->host->host_no,
+ cmd->device->id,
+ cmd->device->lun);
+ printk(KERN_INFO " command: ");
+ scsi_print_cdb(cmd->cmnd, cmd->cmd_len, 0);
+}
+EXPORT_SYMBOL(scsi_print_command);
+
+#ifdef CONFIG_SCSI_CONSTANTS
+
+static const char * hostbyte_table[]={
+"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET",
+"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",
+"DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY"};
+#define NUM_HOSTBYTE_STRS (sizeof(hostbyte_table) / sizeof(const char *))
+
+void scsi_print_hostbyte(int scsiresult)
+{
+ int hb = host_byte(scsiresult);
+
+ printk("Hostbyte=0x%02x", hb);
+ if (hb < NUM_HOSTBYTE_STRS)
+ printk("(%s) ", hostbyte_table[hb]);
+ else
+ printk("is invalid ");
+}
+#else
+void scsi_print_hostbyte(int scsiresult)
+{
+ printk("Hostbyte=0x%02x ", host_byte(scsiresult));
+}
+#endif
+
+#ifdef CONFIG_SCSI_CONSTANTS
+
+static const char * driverbyte_table[]={
+"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
+"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"};
+#define NUM_DRIVERBYTE_STRS (sizeof(driverbyte_table) / sizeof(const char *))
+
+static const char * driversuggest_table[]={"SUGGEST_OK",
+"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
+"SUGGEST_5", "SUGGEST_6", "SUGGEST_7", "SUGGEST_SENSE"};
+#define NUM_SUGGEST_STRS (sizeof(driversuggest_table) / sizeof(const char *))
+
+void scsi_print_driverbyte(int scsiresult)
+{
+ int dr = (driver_byte(scsiresult) & DRIVER_MASK);
+ int su = ((driver_byte(scsiresult) & SUGGEST_MASK) >> 4);
+
+ printk("Driverbyte=0x%02x ", driver_byte(scsiresult));
+ printk("(%s,%s) ",
+ (dr < NUM_DRIVERBYTE_STRS ? driverbyte_table[dr] : "invalid"),
+ (su < NUM_SUGGEST_STRS ? driversuggest_table[su] : "invalid"));
+}
+#else
+void scsi_print_driverbyte(int scsiresult)
+{
+ printk("Driverbyte=0x%02x ", driver_byte(scsiresult));
+}
+#endif
diff --git a/drivers/scsi/cpqfcTS.h b/drivers/scsi/cpqfcTS.h
new file mode 100644
index 000000000000..7ce53d88cb96
--- /dev/null
+++ b/drivers/scsi/cpqfcTS.h
@@ -0,0 +1,19 @@
+#ifndef CPQFCTS_H
+#define CPQFCTS_H
+#include "cpqfcTSstructs.h"
+
+// These functions are required by the Linux SCSI layers
+extern int cpqfcTS_detect(Scsi_Host_Template *);
+extern int cpqfcTS_release(struct Scsi_Host *);
+extern const char * cpqfcTS_info(struct Scsi_Host *);
+extern int cpqfcTS_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+extern int cpqfcTS_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+extern int cpqfcTS_abort(Scsi_Cmnd *);
+extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int);
+extern int cpqfcTS_eh_abort(Scsi_Cmnd *Cmnd);
+extern int cpqfcTS_eh_device_reset(Scsi_Cmnd *);
+extern int cpqfcTS_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int[]);
+extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg);
+
+#endif /* CPQFCTS_H */
diff --git a/drivers/scsi/cpqfcTSchip.h b/drivers/scsi/cpqfcTSchip.h
new file mode 100644
index 000000000000..14b83373861f
--- /dev/null
+++ b/drivers/scsi/cpqfcTSchip.h
@@ -0,0 +1,238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSCHIP_H
+#define CPQFCTSCHIP_H
+#ifndef TACHYON_CHIP_INC
+
+// FC-PH (Physical) specification levels for Login payloads
+// NOTE: These are NOT strictly complied with by any FC vendors
+
+#define FC_PH42 0x08
+#define FC_PH43 0x09
+#define FC_PH3 0x20
+
+#define TACHLITE_TS_RX_SIZE 1024 // max inbound frame size
+// "I" prefix is for Include
+
+#define IVENDID 0x00 // word
+#define IDEVID 0x02
+#define ITLCFGCMD 0x04
+#define IMEMBASE 0x18 // Tachyon
+#define ITLMEMBASE 0x1C // Tachlite
+#define IIOBASEL 0x10 // Tachyon I/O base address, lower 256 bytes
+#define IIOBASEU 0x14 // Tachyon I/O base address, upper 256 bytes
+#define ITLIOBASEL 0x14 // TachLite I/O base address, lower 256 bytes
+#define ITLIOBASEU 0x18 // TachLite I/O base address, upper 256 bytes
+#define ITLRAMBASE 0x20 // TL on-board RAM start
+#define ISROMBASE 0x24
+#define IROMBASE 0x30
+
+#define ICFGCMD 0x04 // PCI config - PCI config access (word)
+#define ICFGSTAT 0x06 // PCI status (R - word)
+#define IRCTR_WCTR 0x1F2 // ROM control / pre-fetch wait counter
+#define IPCIMCTR 0x1F3 // PCI master control register
+#define IINTPEND 0x1FD // Interrupt pending (I/O Upper - Tachyon & TL)
+#define IINTEN 0x1FE // Interrupt enable (I/O Upper - Tachyon & TL)
+#define IINTSTAT 0x1FF // Interrupt status (I/O Upper - Tachyon & TL)
+
+#define IMQ_BASE 0x80
+#define IMQ_LENGTH 0x84
+#define IMQ_CONSUMER_INDEX 0x88
+#define IMQ_PRODUCER_INDEX 0x8C // Tach copies its INDX to bits 0-7 of value
+
+/*
+// IOBASE UPPER
+#define SFSBQ_BASE 0x00 // single-frame sequences
+#define SFSBQ_LENGTH 0x04
+#define SFSBQ_PRODUCER_INDEX 0x08
+#define SFSBQ_CONSUMER_INDEX 0x0C // (R)
+#define SFS_BUFFER_LENGTH 0X10
+ // SCSI-FCP hardware assists
+#define SEST_BASE 0x40 // SSCI Exchange State Table
+#define SEST_LENGTH 0x44
+#define SCSI_BUFFER_LENGTH 0x48
+#define SEST_LINKED_LIST 0x4C
+
+#define TACHYON_My_ID 0x6C
+#define TACHYON_CONFIGURATION 0x84 // (R/W) reset val 2
+#define TACHYON_CONTROL 0x88
+#define TACHYON_STATUS 0x8C // (R)
+#define TACHYON_FLUSH_SEST 0x90 // (R/W)
+#define TACHYON_EE_CREDIT_TMR 0x94 // (R)
+#define TACHYON_BB_CREDIT_TMR 0x98 // (R)
+#define TACHYON_RCV_FRAME_ERR 0x9C // (R)
+#define FRAME_MANAGER_CONFIG 0xC0 // (R/W)
+#define FRAME_MANAGER_CONTROL 0xC4
+#define FRAME_MANAGER_STATUS 0xC8 // (R)
+#define FRAME_MANAGER_ED_TOV 0xCC
+#define FRAME_MANAGER_LINK_ERR1 0xD0 // (R)
+#define FRAME_MANAGER_LINK_ERR2 0xD4 // (R)
+#define FRAME_MANAGER_TIMEOUT2 0xD8 // (W)
+#define FRAME_MANAGER_BB_CREDIT 0xDC // (R)
+#define FRAME_MANAGER_WWN_HI 0xE0 // (R/W)
+#define FRAME_MANAGER_WWN_LO 0xE4 // (R/W)
+#define FRAME_MANAGER_RCV_AL_PA 0xE8 // (R)
+#define FRAME_MANAGER_PRIMITIVE 0xEC // {K28.5} byte1 byte2 byte3
+*/
+
+#define TL_MEM_ERQ_BASE 0x0 //ERQ Base
+#define TL_IO_ERQ_BASE 0x0 //ERQ base
+
+#define TL_MEM_ERQ_LENGTH 0x4 //ERQ Length
+#define TL_IO_ERQ_LENGTH 0x4 //ERQ Length
+
+#define TL_MEM_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register
+#define TL_IO_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register
+#define TL_IO_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index
+#define TL_IO_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index
+
+#define TL_MEM_SFQ_BASE 0x50 //SFQ Base
+#define TL_IO_SFQ_BASE 0x50 //SFQ base
+
+#define TL_MEM_SFQ_LENGTH 0x54 //SFQ Length
+#define TL_IO_SFQ_LENGTH 0x54 //SFQ Length
+
+#define TL_MEM_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index
+#define TL_IO_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index
+
+#define TL_MEM_IMQ_BASE 0x80 //IMQ Base
+#define TL_IO_IMQ_BASE 0x80 //IMQ base
+
+#define TL_MEM_IMQ_LENGTH 0x84 //IMQ Length
+#define TL_IO_IMQ_LENGTH 0x84 //IMQ Length
+
+#define TL_MEM_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index
+#define TL_IO_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index
+
+#define TL_MEM_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register
+#define TL_IO_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register
+
+#define TL_MEM_SEST_BASE 0x140 //SFQ Base
+#define TL_IO_SEST_BASE 0x40 //SFQ base
+
+#define TL_MEM_SEST_LENGTH 0x144 //SFQ Length
+#define TL_IO_SEST_LENGTH 0x44 //SFQ Length
+
+#define TL_MEM_SEST_LINKED_LIST 0x14C
+
+#define TL_MEM_SEST_SG_PAGE 0x168 // Extended Scatter/Gather page size
+
+#define TL_MEM_TACH_My_ID 0x16C
+#define TL_IO_TACH_My_ID 0x6C //My AL_PA ID
+
+#define TL_MEM_TACH_CONFIG 0x184 //Tachlite Configuration register
+#define TL_IO_CONFIG 0x84 //Tachlite Configuration register
+
+#define TL_MEM_TACH_CONTROL 0x188 //Tachlite Control register
+#define TL_IO_CTR 0x88 //Tachlite Control register
+
+#define TL_MEM_TACH_STATUS 0x18C //Tachlite Status register
+#define TL_IO_STAT 0x8C //Tachlite Status register
+
+#define TL_MEM_FM_CONFIG 0x1C0 //Frame Manager Configuration register
+#define TL_IO_FM_CONFIG 0xC0 //Frame Manager Configuration register
+
+#define TL_MEM_FM_CONTROL 0x1C4 //Frame Manager Control
+#define TL_IO_FM_CTL 0xC4 //Frame Manager Control
+
+#define TL_MEM_FM_STATUS 0x1C8 //Frame Manager Status
+#define TL_IO_FM_STAT 0xC8 //Frame Manager Status
+
+#define TL_MEM_FM_LINK_STAT1 0x1D0 //Frame Manager Link Status 1
+#define TL_IO_FM_LINK_STAT1 0xD0 //Frame Manager Link Status 1
+
+#define TL_MEM_FM_LINK_STAT2 0x1D4 //Frame Manager Link Status 2
+#define TL_IO_FM_LINK_STAT2 0xD4 //Frame Manager Link Status 2
+
+#define TL_MEM_FM_TIMEOUT2 0x1D8 // (W)
+
+#define TL_MEM_FM_BB_CREDIT0 0x1DC
+
+#define TL_MEM_FM_WWN_HI 0x1E0 //Frame Manager World Wide Name High
+#define TL_IO_FM_WWN_HI 0xE0 //Frame Manager World Wide Name High
+
+#define TL_MEM_FM_WWN_LO 0x1E4 //Frame Manager World Wide Name LOW
+#define TL_IO_FM_WWN_LO 0xE4 //Frame Manager World Wide Name Low
+
+#define TL_MEM_FM_RCV_AL_PA 0x1E8 //Frame Manager AL_PA Received register
+#define TL_IO_FM_ALPA 0xE8 //Frame Manager AL_PA Received register
+
+#define TL_MEM_FM_ED_TOV 0x1CC
+
+#define TL_IO_ROMCTR 0xFA //TL PCI ROM Control Register
+#define TL_IO_PCIMCTR 0xFB //TL PCI Master Control Register
+#define TL_IO_SOFTRST 0xFC //Tachlite Configuration register
+#define TL_MEM_SOFTRST 0x1FC //Tachlite Configuration register
+
+// completion message types (bit 8 set means Interrupt generated)
+// CM_Type
+#define OUTBOUND_COMPLETION 0
+#define ERROR_IDLE_COMPLETION 0x01
+#define OUT_HI_PRI_COMPLETION 0x01
+#define INBOUND_MFS_COMPLETION 0x02
+#define INBOUND_000_COMPLETION 0x03
+#define INBOUND_SFS_COMPLETION 0x04 // Tachyon & TachLite
+#define ERQ_FROZEN_COMPLETION 0x06 // TachLite
+#define INBOUND_C1_TIMEOUT 0x05
+#define INBOUND_BUSIED_FRAME 0x06
+#define SFS_BUF_WARN 0x07
+#define FCP_FROZEN_COMPLETION 0x07 // TachLite
+#define MFS_BUF_WARN 0x08
+#define IMQ_BUF_WARN 0x09
+#define FRAME_MGR_INTERRUPT 0x0A
+#define READ_STATUS 0x0B
+#define INBOUND_SCSI_DATA_COMPLETION 0x0C
+#define INBOUND_FCP_XCHG_COMPLETION 0x0C // TachLite
+#define INBOUND_SCSI_DATA_COMMAND 0x0D
+#define BAD_SCSI_FRAME 0x0E
+#define INB_SCSI_STATUS_COMPLETION 0x0F
+#define BUFFER_PROCESSED_COMPLETION 0x11
+
+// FC-AL (Tachyon) Loop Port State Machine defs
+// (loop "Up" states)
+#define MONITORING 0x0
+#define ARBITRATING 0x1
+#define ARBITRAT_WON 0x2
+#define OPEN 0x3
+#define OPENED 0x4
+#define XMITTD_CLOSE 0x5
+#define RCVD_CLOSE 0x6
+#define TRANSFER 0x7
+
+// (loop "Down" states)
+#define INITIALIZING 0x8
+#define O_I_INIT 0x9
+#define O_I_PROTOCOL 0xa
+#define O_I_LIP_RCVD 0xb
+#define HOST_CONTROL 0xc
+#define LOOP_FAIL 0xd
+// (no 0xe)
+#define OLD_PORT 0xf
+
+
+
+#define TACHYON_CHIP_INC
+#endif
+#endif /* CPQFCTSCHIP_H */
diff --git a/drivers/scsi/cpqfcTScontrol.c b/drivers/scsi/cpqfcTScontrol.c
new file mode 100644
index 000000000000..bd94c70f473d
--- /dev/null
+++ b/drivers/scsi/cpqfcTScontrol.c
@@ -0,0 +1,2231 @@
+/* Copyright 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+/* These functions control the host bus adapter (HBA) hardware. The main chip
+ control takes place in the interrupt handler where we process the IMQ
+ (Inbound Message Queue). The IMQ is Tachyon's way of communicating FC link
+ events and state information to the driver. The Single Frame Queue (SFQ)
+ buffers incoming FC frames for processing by the driver. References to
+ "TL/TS UG" are for:
+ "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed.
+ Hewlitt Packard Manual Part Number 5968-1083E.
+*/
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h> // request_region() prototype
+#include <linux/sched.h>
+#include <linux/slab.h> // need "kfree" for ext. S/G pages
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O
+#include <asm/irq.h>
+#include <linux/spinlock.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h> // Scsi_Host definition for INT handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define IMQ_DEBUG 1
+
+static void fcParseLinkStatusCounters(TACHYON * fcChip);
+static void CpqTsGetSFQEntry(TACHYON * fcChip,
+ USHORT pi, ULONG * buffr, BOOLEAN UpdateChip);
+
+static void
+cpqfc_free_dma_consistent(CPQFCHBA *cpqfcHBAdata)
+{
+ // free up the primary EXCHANGES struct and Link Q
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ if (fcChip->Exchanges != NULL)
+ pci_free_consistent(cpqfcHBAdata->PciDev, sizeof(FC_EXCHANGES),
+ fcChip->Exchanges, fcChip->exch_dma_handle);
+ fcChip->Exchanges = NULL;
+ if (cpqfcHBAdata->fcLQ != NULL)
+ pci_free_consistent(cpqfcHBAdata->PciDev, sizeof(FC_LINK_QUE),
+ cpqfcHBAdata->fcLQ, cpqfcHBAdata->fcLQ_dma_handle);
+ cpqfcHBAdata->fcLQ = NULL;
+}
+
+// Note special requirements for Q alignment! (TL/TS UG pg. 190)
+// We place critical index pointers at end of QUE elements to assist
+// in non-symbolic (i.e. memory dump) debugging
+// opcode defines placement of Queues (e.g. local/external RAM)
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ int iStatus=0;
+ unsigned long ulAddr;
+ dma_addr_t ERQdma, IMQdma, SPQdma, SESTdma;
+ int i;
+
+ // NOTE! fcMemManager() will return system virtual addresses.
+ // System (kernel) virtual addresses, though non-paged, still
+ // aren't physical addresses. Convert to PHYSICAL_ADDRESS for Tachyon's
+ // DMA use.
+ ENTER("CreateTachLiteQues");
+
+
+ // Allocate primary EXCHANGES array...
+ fcChip->Exchanges = NULL;
+ cpqfcHBAdata->fcLQ = NULL;
+
+ /* printk("Allocating %u for %u Exchanges ",
+ (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID); */
+ fcChip->Exchanges = pci_alloc_consistent(cpqfcHBAdata->PciDev,
+ sizeof(FC_EXCHANGES), &fcChip->exch_dma_handle);
+ /* printk("@ %p\n", fcChip->Exchanges); */
+
+ if( fcChip->Exchanges == NULL ) // fatal error!!
+ {
+ printk("pci_alloc_consistent failure on Exchanges: fatal error\n");
+ return -1;
+ }
+ // zero out the entire EXCHANGE space
+ memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES));
+
+
+ /* printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE)); */
+ cpqfcHBAdata->fcLQ = pci_alloc_consistent(cpqfcHBAdata->PciDev,
+ sizeof( FC_LINK_QUE), &cpqfcHBAdata->fcLQ_dma_handle);
+ /* printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH); */
+
+ if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!!
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk("pci_alloc_consistent() failure on fc Link Que: fatal error\n");
+ return -1;
+ }
+ // zero out the entire EXCHANGE space
+ memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));
+
+ // Verify that basic Tach I/O registers are not NULL
+ if( !fcChip->Registers.ReMapMemBase )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk("HBA base address NULL: fatal error\n");
+ return -1;
+ }
+
+
+ // Initialize the fcMemManager memory pairs (stores allocated/aligned
+ // pairs for future freeing)
+ memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem));
+
+
+ // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes)
+
+ fcChip->ERQ = fcMemManager( cpqfcHBAdata->PciDev,
+ &cpqfcHBAdata->dynamic_mem[0],
+ sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L, &ERQdma);
+ if( !fcChip->ERQ )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk("pci_alloc_consistent/alignment failure on ERQ: fatal error\n");
+ return -1;
+ }
+ fcChip->ERQ->length = ERQ_LEN-1;
+ ulAddr = (ULONG) ERQdma;
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->ERQ->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Allocate Tach's Inbound Message Queue (32 bytes per entry)
+
+ fcChip->IMQ = fcMemManager( cpqfcHBAdata->PciDev,
+ &cpqfcHBAdata->dynamic_mem[0],
+ sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L, &IMQdma );
+ if( !fcChip->IMQ )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk("pci_alloc_consistent/alignment failure on IMQ: fatal error\n");
+ return -1;
+ }
+ fcChip->IMQ->length = IMQ_LEN-1;
+
+ ulAddr = IMQdma;
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->IMQ->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Allocate Tach's Single Frame Queue (64 bytes per entry)
+ fcChip->SFQ = fcMemManager( cpqfcHBAdata->PciDev,
+ &cpqfcHBAdata->dynamic_mem[0],
+ sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L, &SPQdma );
+ if( !fcChip->SFQ )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk("pci_alloc_consistent/alignment failure on SFQ: fatal error\n");
+ return -1;
+ }
+ fcChip->SFQ->length = SFQ_LEN-1; // i.e. Que length [# entries -
+ // min. 32; max. 4096 (0xffff)]
+
+ ulAddr = SPQdma;
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->SFQ->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Allocate SCSI Exchange State Table; aligned nearest @sizeof
+ // power-of-2 boundary
+ // LIVE DANGEROUSLY! Assume the boundary for SEST mem will
+ // be on physical page (e.g. 4k) boundary.
+ /* printk("Allocating %u for TachSEST for %u Exchanges\n",
+ (ULONG)sizeof(TachSEST), TACH_SEST_LEN); */
+ fcChip->SEST = fcMemManager( cpqfcHBAdata->PciDev,
+ &cpqfcHBAdata->dynamic_mem[0],
+ sizeof(TachSEST), 4, 0L, &SESTdma );
+// sizeof(TachSEST), 64*TACH_SEST_LEN, 0L );
+ if( !fcChip->SEST )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk("pci_alloc_consistent/alignment failure on SEST: fatal error\n");
+ return -1;
+ }
+
+ for( i=0; i < TACH_SEST_LEN; i++) // for each exchange
+ fcChip->SEST->sgPages[i] = NULL;
+
+ fcChip->SEST->length = TACH_SEST_LEN; // e.g. DON'T subtract one
+ // (TL/TS UG, pg 153)
+
+ ulAddr = SESTdma;
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->SEST->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Now that structures are defined,
+ // fill in Tachyon chip registers...
+
+ // EEEEEEEE EXCHANGE REQUEST QUEUE
+
+ writel( fcChip->ERQ->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+
+ writel( fcChip->ERQ->length,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH));
+
+
+ fcChip->ERQ->producerIndex = 0L;
+ writel( fcChip->ERQ->producerIndex,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX));
+
+
+ // NOTE! write consumer index last, since the write
+ // causes Tachyon to process the other registers
+
+ ulAddr = ((unsigned long)&fcChip->ERQ->consumerIndex -
+ (unsigned long)fcChip->ERQ) + (unsigned long) ERQdma;
+
+ // NOTE! Tachyon DMAs to the ERQ consumer Index host
+ // address; must be correctly aligned
+ writel( (ULONG)ulAddr,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR));
+
+
+
+ // IIIIIIIIIIIII INBOUND MESSAGE QUEUE
+ // Tell Tachyon where the Que starts
+
+ // set the Host's pointer for Tachyon to access
+
+ /* printk(" cpqfcTS: writing IMQ BASE %Xh ", fcChip->IMQ->base ); */
+ writel( fcChip->IMQ->base,
+ (fcChip->Registers.ReMapMemBase + IMQ_BASE));
+
+ writel( fcChip->IMQ->length,
+ (fcChip->Registers.ReMapMemBase + IMQ_LENGTH));
+
+ writel( fcChip->IMQ->consumerIndex,
+ (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+
+
+ // NOTE: TachLite DMAs to the producerIndex host address
+ // must be correctly aligned with address bits 1-0 cleared
+ // Writing the BASE register clears the PI register, so write it last
+ ulAddr = ((unsigned long)&fcChip->IMQ->producerIndex -
+ (unsigned long)fcChip->IMQ) + (unsigned long) IMQdma;
+
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+ printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+#if DBG
+ printk(" PI %Xh\n", (ULONG)ulAddr );
+#endif
+ writel( (ULONG)ulAddr,
+ (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX));
+
+
+
+ // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE
+ // Tell TachLite where the Que starts
+
+ writel( fcChip->SFQ->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE));
+
+ writel( fcChip->SFQ->length,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH));
+
+
+ // tell TachLite where SEST table is & how long
+ writel( fcChip->SEST->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE));
+
+ /* printk(" cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n",
+ fcChip->SEST, fcChip->SEST->base,
+ fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE); */
+
+ writel( fcChip->SEST->length,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH));
+
+ writel( (TL_EXT_SG_PAGE_COUNT-1),
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE));
+
+
+ LEAVE("CreateTachLiteQues");
+
+ return iStatus;
+}
+
+
+
+// function to return TachLite to Power On state
+// 1st - reset tachyon ('SOFT' reset)
+// others - future
+
+int CpqTsResetTachLite(void *pHBA, int type)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ ULONG ulBuff, i;
+ int ret_status=0; // def. success
+
+ ENTER("ResetTach");
+
+ switch(type)
+ {
+
+ case CLEAR_FCPORTS:
+
+ // in case he was running previously, mask Tach's interrupt
+ writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+
+ // de-allocate mem for any Logged in ports
+ // (e.g., our module is unloading)
+ // search the forward linked list, de-allocating
+ // the memory we allocated when the port was initially logged in
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort;
+ PFC_LOGGEDIN_PORT ptr;
+// printk("checking for allocated LoggedInPorts...\n");
+
+ while( pLoggedInPort )
+ {
+ ptr = pLoggedInPort;
+ pLoggedInPort = ptr->pNextPort;
+// printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n",
+// ptr, ptr->port_id);
+ kfree( ptr );
+ }
+ }
+ // (continue resetting hardware...)
+
+ case 1: // RESTART Tachyon (power-up state)
+
+ // in case he was running previously, mask Tach's interrupt
+ writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+ // turn OFF laser (NOTE: laser is turned
+ // off during reset, because GPIO4 is cleared
+ // to 0 by reset action - see TLUM, sec 7.22)
+ // However, CPQ 64-bit HBAs have a "health
+ // circuit" which keeps laser ON for a brief
+ // period after it is turned off ( < 1s)
+
+ fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0);
+
+
+
+ // soft reset timing constraints require:
+ // 1. set RST to 1
+ // 2. read SOFTRST register
+ // (128 times per R. Callison code)
+ // 3. clear PCI ints
+ // 4. clear RST to 0
+ writel( 0xff000001L,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+
+ for( i=0; i<128; i++)
+ ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST);
+
+ // clear the soft reset
+ for( i=0; i<8; i++)
+ writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+
+
+
+ // clear out our copy of Tach regs,
+ // because they must be invalid now,
+ // since TachLite reset all his regs.
+ CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs
+ cpqfcTSClearLinkStatusCounters(fcChip); // clear our s/w accumulators
+ // lower bits give GBIC info
+ fcChip->Registers.TYstatus.value =
+ readl( fcChip->Registers.TYstatus.address );
+ break;
+
+/*
+ case 2: // freeze SCSI
+ case 3: // reset Outbound command que (ERQ)
+ case 4: // unfreeze OSM (Outbound Seq. Man.) 'er'
+ case 5: // report status
+
+ break;
+*/
+ default:
+ ret_status = -1; // invalid option passed to RESET function
+ break;
+ }
+ LEAVE("ResetTach");
+ return ret_status;
+}
+
+
+
+
+
+
+// 'addrBase' is IOBaseU for both TachLite and (older) Tachyon
+int CpqTsLaserControl( void* addrBase, int opcode )
+{
+ ULONG dwBuff;
+
+ dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg
+ // (change only bit 4)
+ if( opcode == 1)
+ dwBuff |= ~0xffffffefL; // set - ON
+ else
+ dwBuff &= 0xffffffefL; // clear - OFF
+ writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg
+ return 0;
+}
+
+
+
+
+
+// Use controller's "Options" field to determine loopback mode (if any)
+// internal loopback (silicon - no GBIC)
+// external loopback (GBIC - no FC loop)
+// no loopback: L_PORT, external cable from GBIC required
+
+int CpqTsInitializeFrameManager( void *pChip, int opcode)
+{
+ PTACHYON fcChip;
+ int iStatus;
+ ULONG wwnLo, wwnHi; // for readback verification
+
+ ENTER("InitializeFrameManager");
+ fcChip = (PTACHYON)pChip;
+ if( !fcChip->Registers.ReMapMemBase ) // undefined controller?
+ return -1;
+
+ // TL/TS UG, pg. 184
+ // 0x0065 = 100ms for RT_TOV
+ // 0x01f5 = 500ms for ED_TOV
+ // 0x07D1 = 2000ms
+ fcChip->Registers.ed_tov.value = 0x006507D1;
+ writel( fcChip->Registers.ed_tov.value,
+ (fcChip->Registers.ed_tov.address));
+
+
+ // Set LP_TOV to the FC-AL2 specified 2 secs.
+ // TL/TS UG, pg. 185
+ writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+
+
+ // Now try to read the WWN from the adapter's NVRAM
+ iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ
+
+ if( iStatus ) // NVRAM read failed?
+ {
+ printk(" WARNING! HBA NVRAM WWN read failed - make alias\n");
+ // make up a WWN. If NULL or duplicated on loop, FC loop may hang!
+
+
+ fcChip->Registers.wwn_hi = (__u32)jiffies;
+ fcChip->Registers.wwn_hi |= 0x50000000L;
+ fcChip->Registers.wwn_lo = 0x44556677L;
+ }
+
+
+ writel( fcChip->Registers.wwn_hi,
+ fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI);
+
+ writel( fcChip->Registers.wwn_lo,
+ fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+
+
+ // readback for verification:
+ wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI );
+
+ wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+ // test for correct chip register WRITE/READ
+ DEBUG_PCI( printk(" WWN %08X%08X\n",
+ fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) );
+
+ if( wwnHi != fcChip->Registers.wwn_hi ||
+ wwnLo != fcChip->Registers.wwn_lo )
+ {
+ printk( "cpqfcTS: WorldWideName register load failed\n");
+ return -1; // FAILED!
+ }
+
+
+
+ // set Frame Manager Initialize command
+ fcChip->Registers.FMcontrol.value = 0x06;
+
+ // Note: for test/debug purposes, we may use "Hard" address,
+ // but we completely support "soft" addressing, including
+ // dynamically changing our address.
+ if( fcChip->Options.intLoopback == 1 ) // internal loopback
+ fcChip->Registers.FMconfig.value = 0x0f002080L;
+ else if( fcChip->Options.extLoopback == 1 ) // internal loopback
+ fcChip->Registers.FMconfig.value = 0x0f004080L;
+ else // L_Port
+ fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+// fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick)
+// fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+
+ // write config to FM
+
+ if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback )
+ // (also need LASER for real LOOP)
+ fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER
+
+ writel( fcChip->Registers.FMconfig.value,
+ fcChip->Registers.FMconfig.address);
+
+
+ // issue INITIALIZE command to FM - ACTION!
+ writel( fcChip->Registers.FMcontrol.value,
+ fcChip->Registers.FMcontrol.address);
+
+ LEAVE("InitializeFrameManager");
+
+ return 0;
+}
+
+
+
+
+
+// This "look ahead" function examines the IMQ for occurrence of
+// "type". Returns 1 if found, 0 if not.
+static int PeekIMQEntry( PTACHYON fcChip, ULONG type)
+{
+ ULONG CI = fcChip->IMQ->consumerIndex;
+ ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes
+
+ while( CI != PI )
+ { // proceed with search
+ if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check
+
+ switch( type )
+ {
+ case ELS_LILP_FRAME:
+ {
+ // first, we need to find an Inbound Completion message,
+ // If we find it, check the incoming frame payload (1st word)
+ // for LILP frame
+ if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 )
+ {
+ TachFCHDR_GCMND* fchs;
+#error This is too much stack
+ ULONG ulFibreFrame[2048/4]; // max DWORDS in incoming FC Frame
+ USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL);
+
+ CpqTsGetSFQEntry( fcChip,
+ SFQpi, // SFQ producer ndx
+ ulFibreFrame, // contiguous dest. buffer
+ FALSE); // DON'T update chip--this is a "lookahead"
+
+ fchs = (TachFCHDR_GCMND*)&ulFibreFrame;
+ if( fchs->pl[0] == ELS_LILP_FRAME)
+ {
+ return 1; // found the LILP frame!
+ }
+ else
+ {
+ // keep looking...
+ }
+ }
+ }
+ break;
+
+ case OUTBOUND_COMPLETION:
+ if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 )
+ {
+
+ // any OCM errors?
+ if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L )
+ return 1; // found OCM error
+ }
+ break;
+
+
+
+ default:
+ break;
+ }
+ }
+ return 0; // failed to find "type"
+}
+
+
+static void SetTachTOV( CPQFCHBA* cpqfcHBAdata)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ // TL/TS UG, pg. 184
+ // 0x0065 = 100ms for RT_TOV
+ // 0x01f5 = 500ms for ED_TOV
+ // 0x07d1 = 2000ms for ED_TOV
+
+ // SANMark Level 1 requires an "initialization backoff"
+ // (See "SANMark Test Suite Level 1":
+ // initialization_timeout.fcal.SANMark-1.fc)
+ // We have to use 2sec, 24sec, then 128sec when login/
+ // port discovery processes fail to complete.
+
+ // when port discovery completes (logins done), we set
+ // ED_TOV to 500ms -- this is the normal operational case
+ // On the first Link Down, we'll move to 2 secs (7D1 ms)
+ if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5)
+ fcChip->Registers.ed_tov.value = 0x006507D1;
+
+ // If we get another LST after we moved TOV to 2 sec,
+ // increase to 24 seconds (5DC1 ms) per SANMark!
+ else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1)
+ fcChip->Registers.ed_tov.value = 0x00655DC1;
+
+ // If we get still another LST, set the max TOV (Tachyon
+ // has only 16 bits for ms timer, so the max is 65.5 sec)
+ else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1)
+ fcChip->Registers.ed_tov.value = 0x0065FFFF;
+
+ writel( fcChip->Registers.ed_tov.value,
+ (fcChip->Registers.ed_tov.address));
+ // keep the same 2sec LP_TOV
+ writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+}
+
+
+// The IMQ is an array with IMQ_LEN length, each element (QEntry)
+// with eight 32-bit words. Tachyon PRODUCES a QEntry with each
+// message it wants to send to the host. The host CONSUMES IMQ entries
+
+// This function copies the current
+// (or oldest not-yet-processed) QEntry to
+// the caller, clears/ re-enables the interrupt, and updates the
+// (Host) Consumer Index.
+// Return value:
+// 0 message processed, none remain (producer and consumer
+// indexes match)
+// 1 message processed, more messages remain
+// -1 no message processed - none were available to process
+// Remarks:
+// TL/TS UG specifices that the following actions for
+// INTA_L handling:
+// 1. read PCI Interrupt Status register (0xff)
+// 2. all IMQ messages should be processed before writing the
+// IMQ consumer index.
+
+
+int CpqTsProcessIMQEntry(void *host)
+{
+ struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ int iStatus;
+ USHORT i, RPCset, DPCset;
+ ULONG x_ID;
+ ULONG ulBuff, dwStatus;
+ TachFCHDR_GCMND* fchs;
+#error This is too much stack
+ ULONG ulFibreFrame[2048/4]; // max number of DWORDS in incoming Fibre Frame
+ UCHAR ucInboundMessageType; // Inbound CM, dword 3 "type" field
+
+ ENTER("ProcessIMQEntry");
+
+
+ // check TachLite's IMQ producer index -
+ // is a new message waiting for us?
+ // equal indexes means empty que
+
+ if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex )
+ { // need to process message
+
+
+#ifdef IMQ_DEBUG
+ printk("PI %X, CI %X type: %X\n",
+ fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex,
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type);
+#endif
+ // Examine Completion Messages in IMQ
+ // what CM_Type?
+ switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type
+ & 0xffL) )
+ {
+ case OUTBOUND_COMPLETION:
+
+ // Remarks:
+ // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries
+ // (starting at 0), and SFS entries (starting at
+ // SEST_LEN -- outside the SEST space).
+ // Psuedo code:
+ // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index
+ // range check - x_ID
+ // if x_ID outside 'Transactions' length, error - exit
+ // if any OCM error, copy error status to Exchange slot
+ // if FCP ASSIST transaction (x_ID within SEST),
+ // call fcComplete (to App)
+ // ...
+
+
+ ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1];
+ x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID
+ // Range check CM OX/RX_ID value...
+ if( x_ID < TACH_MAX_XID ) // don't go beyond array space
+ {
+
+
+ if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete?
+ RPCset = 1; // (SEST transactions only)
+ else
+ RPCset = 0;
+
+ if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete?
+ DPCset = 1; // (SEST transactions only)
+ else
+ DPCset = 0;
+ // set the status for this Outbound transaction's ID
+ dwStatus = 0L;
+ if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error)
+ dwStatus |= SESTPROG_ERR;
+
+ ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2];
+ if( ulBuff & 0x7a000000L ) // any other errs?
+ {
+ if( ulBuff & 0x40000000L )
+ dwStatus |= INV_ENTRY;
+ if( ulBuff & 0x20000000L )
+ dwStatus |= FRAME_TO; // FTO
+ if( ulBuff & 0x10000000L )
+ dwStatus |= HOSTPROG_ERR;
+ if( ulBuff & 0x08000000L )
+ dwStatus |= LINKFAIL_TX;
+ if( ulBuff & 0x02000000L )
+ dwStatus |= ABORTSEQ_NOTIFY; // ASN
+ }
+
+
+ if( dwStatus ) // any errors?
+ {
+ // set the Outbound Completion status
+ Exchanges->fcExchange[ x_ID ].status |= dwStatus;
+
+ // if this Outbound frame was for a SEST entry, automatically
+ // reque it in the case of LINKFAIL (it will restart on PDISC)
+ if( x_ID < TACH_SEST_LEN )
+ {
+
+ printk(" #OCM error %Xh x_ID %X# ",
+ dwStatus, x_ID);
+
+ Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default
+
+
+ // We Q ABTS for each exchange.
+ // NOTE: We can get FRAME_TO on bad alpa (device gone). Since
+ // bad alpa is reported before FRAME_TO, examine the status
+ // flags to see if the device is removed. If so, DON'T
+ // post an ABTS, since it will be terminated by the bad alpa
+ // message.
+ if( dwStatus & FRAME_TO ) // check for device removed...
+ {
+ if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) )
+ {
+ // presumes device is still there: send ABTS.
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+ }
+ }
+ else // Abort all other errors
+ {
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+ }
+
+ // if the HPE bit is set, we have to CLose the LOOP
+ // (see TL/TS UG, pg. 239)
+
+ if( dwStatus &= HOSTPROG_ERR )
+ // set CL bit (see TL/TS UG, pg. 172)
+ writel( 4, fcChip->Registers.FMcontrol.address);
+ }
+ }
+ // NOTE: we don't necessarily care about ALL completion messages...
+ // SCSI resp. complete OR
+ if( ((x_ID < TACH_SEST_LEN) && RPCset)||
+ (x_ID >= TACH_SEST_LEN) ) // non-SCSI command
+ {
+ // exchange done; complete to upper levels with status
+ // (if necessary) and free the exchange slot
+
+
+ if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+ // A Request or Reply has been sent
+ { // signal waiting WorkerThread
+
+ up( cpqfcHBAdata->TYOBcomplete); // frame is OUT of Tach
+
+ // WorkerThread will complete Xchng
+ }
+ else // X_ID is for FCP assist (SEST)
+ {
+ // TBD (target mode)
+// fcCompleteExchange( fcChip, x_ID); // TRE completed
+ }
+ }
+ }
+ else // ERROR CONDITION! bogus x_ID in completion message
+ {
+
+ printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID);
+
+ }
+
+
+
+ // Load the Frame Manager's error counters. We check them here
+ // because presumably the link is up and healthy enough for the
+ // counters to be meaningful (i.e., don't check them while loop
+ // is initializing).
+ fcChip->Registers.FMLinkStatus1.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus1.address);
+
+ fcChip->Registers.FMLinkStatus2.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus2.address);
+
+
+ fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+ break;
+
+
+
+ case ERROR_IDLE_COMPLETION: // TachLite Error Idle...
+
+ // We usually get this when the link goes down during heavy traffic.
+ // For now, presume that if SEST Exchanges are open, we will
+ // get this as our cue to INVALIDATE all SEST entries
+ // (and we OWN all the SEST entries).
+ // See TL/TS UG, pg. 53
+
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+
+ // Does this VALid SEST entry need to be invalidated for Abort?
+ fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
+ }
+
+ CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK
+
+ break;
+
+
+ case INBOUND_SFS_COMPLETION: //0x04
+ // NOTE! we must process this SFQ message to avoid SFQ filling
+ // up and stopping TachLite. Incoming commands are placed here,
+ // as well as 'unknown' frames (e.g. LIP loop position data)
+ // write this CM's producer index to global...
+ // TL/TS UG, pg 234:
+ // Type: 0 - reserved
+ // 1 - Unassisted FCP
+ // 2 - BAD FCP
+ // 3 - Unkown Frame
+ // 4-F reserved
+
+
+ fcChip->SFQ->producerIndex = (USHORT)
+ (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL);
+
+
+ ucInboundMessageType = 0; // default to useless frame
+
+ // we can only process two Types: 1, Unassisted FCP, and 3, Unknown
+ // Also, we aren't interested in processing frame fragments
+ // so don't Que anything with 'LKF' bit set
+ if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2]
+ & 0x40000000) ) // 'LKF' link failure bit clear?
+ {
+ ucInboundMessageType = (UCHAR) // ICM DWord3, "Type"
+ (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL);
+ }
+ else
+ {
+ fcChip->fcStats.linkFailRX++;
+// printk("LKF (link failure) bit set on inbound message\n");
+ }
+
+ // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff
+ CpqTsGetSFQEntry(
+ fcChip, // i.e. this Device Object
+ (USHORT)fcChip->SFQ->producerIndex, // SFQ producer ndx
+ ulFibreFrame, TRUE); // contiguous destination buffer, update chip
+
+ // analyze the incoming frame outside the INT handler...
+ // (i.e., Worker)
+
+ if( ucInboundMessageType == 1 )
+ {
+ fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame
+ // don't fill up our Q with garbage - only accept FCP-CMND
+ // or XRDY frames
+ if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND
+ {
+ // someone sent us a SCSI command
+
+// fcPutScsiQue( cpqfcHBAdata,
+// SFQ_UNASSISTED_FCP, ulFibreFrame);
+ }
+ else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status)
+ (fchs->d_id & 0xFF000000) == 0x05000000 ) // XRDY
+ {
+ ULONG x_ID;
+ // Unfortunately, ABTS requires a Freeze on the chip so
+ // we can modify the shared memory SEST. When frozen,
+ // any received Exchange frames cannot be processed by
+ // Tachyon, so they will be dumped in here. It is too
+ // complex to attempt the reconstruct these frames in
+ // the correct Exchange context, so we simply seek to
+ // find status or transfer ready frames, and cause the
+ // exchange to complete with errors before the timeout
+ // expires. We use a Linux Scsi Cmnd result code that
+ // causes immediate retry.
+
+
+ // Do we have an open exchange that matches this s_id
+ // and ox_id?
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+ if( (fchs->s_id & 0xFFFFFF) ==
+ (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF)
+ &&
+ (fchs->ox_rx_id & 0xFFFF0000) ==
+ (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) )
+ {
+ // printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id );
+ // simulate the anticipated error - since the
+ // SEST was frozen, frames were lost...
+ Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME;
+
+ // presumes device is still there: send ABTS.
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+ break; // done
+ }
+ }
+ }
+
+ }
+
+ else if( ucInboundMessageType == 3)
+ {
+ // FC Link Service frames (e.g. PLOGI, ACC) come in here.
+ cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame);
+
+ }
+
+ else if( ucInboundMessageType == 2 ) // "bad FCP"?
+ {
+#ifdef IMQ_DEBUG
+ printk("Bad FCP incoming frame discarded\n");
+#endif
+ }
+
+ else // don't know this type
+ {
+#ifdef IMQ_DEBUG
+ printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType);
+#endif
+ }
+
+ // Check the Frame Manager's error counters. We check them here
+ // because presumably the link is up and healthy enough for the
+ // counters to be meaningful (i.e., don't check them while loop
+ // is initializing).
+ fcChip->Registers.FMLinkStatus1.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus1.address);
+
+
+ fcChip->Registers.FMLinkStatus2.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus2.address);
+
+
+ break;
+
+
+
+
+ // We get this CM because we issued a freeze
+ // command to stop outbound frames. We issue the
+ // freeze command at Link Up time; when this message
+ // is received, the ERQ base can be switched and PDISC
+ // frames can be sent.
+
+
+ case ERQ_FROZEN_COMPLETION: // note: expect ERQ followed immediately
+ // by FCP when freezing TL
+ fcChip->Registers.TYstatus.value = // read what's frozen
+ readl(fcChip->Registers.TYstatus.address);
+ // (do nothing; wait for FCP frozen message)
+ break;
+ case FCP_FROZEN_COMPLETION:
+
+ fcChip->Registers.TYstatus.value = // read what's frozen
+ readl(fcChip->Registers.TYstatus.address);
+
+ // Signal the kernel thread to proceed with SEST modification
+ up( cpqfcHBAdata->TachFrozen);
+
+ break;
+
+
+
+ case INBOUND_C1_TIMEOUT:
+ case MFS_BUF_WARN:
+ case IMQ_BUF_WARN:
+ break;
+
+
+
+
+
+ // In older Tachyons, we 'clear' the internal 'core' interrupt state
+ // by reading the FMstatus register. In newer TachLite (Tachyon),
+ // we must WRITE the register
+ // to clear the condition (TL/TS UG, pg 179)
+ case FRAME_MGR_INTERRUPT:
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+
+ fcChip->Registers.FMstatus.value =
+ readl( fcChip->Registers.FMstatus.address );
+
+ // PROBLEM: It is possible, especially with "dumb" hubs that
+ // don't automatically LIP on by-pass of ports that are going
+ // away, for the hub by-pass process to destroy critical
+ // ordered sets of a frame. The result of this is a hung LPSM
+ // (Loop Port State Machine), which on Tachyon results in a
+ // (default 2 sec) Loop State Timeout (LST) FM message. We
+ // want to avoid this relatively huge timeout by detecting
+ // likely scenarios which will result in LST.
+ // To do this, we could examine FMstatus for Loss of Synchronization
+ // and/or Elastic Store (ES) errors. Of these, Elastic Store is better
+ // because we get this indication more quickly than the LOS.
+ // Not all ES errors are harmfull, so we don't want to LIP on every
+ // ES. Instead, on every ES, detect whether our LPSM in in one
+ // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE,
+ // or RECEIVED CLOSE. (See TL/TS UG, pg. 181)
+ // If any of these LPSM states are detected
+ // in combination with the LIP while LDn is not set,
+ // send an FM init (LIP F7,F7 for loops)!
+ // It is critical to the physical link stability NOT to reset (LIP)
+ // more than absolutely necessary; this is a basic premise of the
+ // SANMark level 1 spec.
+ {
+ ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4;
+
+ if( (fcChip->Registers.FMstatus.value & 0x400) // ElasticStore?
+ &&
+ !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn
+ &&
+ !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF
+ {
+ if( (Lpsm != 0) || // not MONITORING? or
+ !(Lpsm & 0x8) )// not already offline?
+ {
+ // now check the particular LST states...
+ if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) ||
+ (Lpsm == OPENED) || (Lpsm == XMITTD_CLOSE) ||
+ (Lpsm == RCVD_CLOSE) )
+ {
+ // re-init the loop before it hangs itself!
+ printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm);
+
+
+ fcChip->fcStats.FMinits++;
+ writel( 6, fcChip->Registers.FMcontrol.address); // LIP
+ }
+ }
+ }
+ else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST?
+ {
+ printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm);
+
+ fcChip->fcStats.FMinits++;
+ writel( 6, fcChip->Registers.FMcontrol.address); // LIP
+ }
+ }
+
+
+ // clear only the 'interrupting' type bits for this REG read
+ writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L),
+ fcChip->Registers.FMstatus.address);
+
+
+ // copy frame manager status to unused ULONG slot
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] =
+ fcChip->Registers.FMstatus.value; // (for debugging)
+
+
+ // Load the Frame Manager's error counters. We check them here
+ // because presumably the link is up and healthy enough for the
+ // counters to be meaningful (i.e., don't check them while loop
+ // is initializing).
+ fcChip->Registers.FMLinkStatus1.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus1.address);
+
+ fcChip->Registers.FMLinkStatus2.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus2.address);
+
+ // Get FM BB_Credit Zero Reg - does not clear on READ
+ fcChip->Registers.FMBB_CreditZero.value = // get TL's counter
+ readl(fcChip->Registers.FMBB_CreditZero.address);
+
+
+
+ fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+
+
+ // LINK DOWN
+
+ if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit
+ {
+
+#ifdef IMQ_DEBUG
+ printk("LinkDn\n");
+#endif
+ printk(" #LDn# ");
+
+ fcChip->fcStats.linkDown++;
+
+ SetTachTOV( cpqfcHBAdata); // must set according to SANMark
+
+ // Check the ERQ - force it to be "empty" to prevent Tach
+ // from sending out frames before we do logins.
+
+
+ if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex)
+ {
+// printk("#ERQ PI != CI#");
+ CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only
+ fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0;
+ writel( fcChip->ERQ->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+ // re-writing base forces ERQ PI to equal CI
+
+ }
+
+ // link down transition occurred -- port_ids can change
+ // on next LinkUp, so we must invalidate current logins
+ // (and any I/O in progress) until PDISC or PLOGI/PRLI
+ // completes
+ {
+ pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+
+ if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+ {
+ pLoggedInPort->LOGO_timer = 3; // we want 2 seconds
+ // but Timer granularity
+ // is 1 second
+ }
+ // suspend any I/O in progress until
+ // PDISC received...
+ pLoggedInPort->prli = FALSE; // block FCP-SCSI commands
+
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ } // ... all Previously known ports checked
+ }
+
+ // since any hot plugging device may NOT support LILP frames
+ // (such as early Tachyon chips), clear this flag indicating
+ // we shouldn't use (our copy of) a LILP map.
+ // If we receive an LILP frame, we'll set it again.
+ fcChip->Options.LILPin = 0; // our LILPmap is invalid
+ cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports!
+
+ // also, we want to invalidate (i.e. INITIATOR_ABORT) any
+ // open Login exchanges, in case the LinkDown happened in the
+ // middle of logins. It's possible that some ports already
+ // ACCepted login commands which we have not processed before
+ // another LinkDown occurred. Any accepted Login exhanges are
+ // invalidated by LinkDown, even before they are acknowledged.
+ // It's also possible for a port to have a Queued Reply or Request
+ // for login which was interrupted by LinkDown; it may come later,
+ // but it will be unacceptable to us.
+
+ // we must scan the entire exchange space, find every Login type
+ // originated by us, and abort it. This is NOT an abort due to
+ // timeout, so we don't actually send abort to the other port -
+ // we just complete it to free up the fcExchange slot.
+
+ for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++)
+ { // looking for Extended Link Serv.Exchanges
+ if( Exchanges->fcExchange[i].type == ELS_PDISC ||
+ Exchanges->fcExchange[i].type == ELS_PLOGI ||
+ Exchanges->fcExchange[i].type == ELS_PRLI )
+ {
+ // ABORT the exchange!
+#ifdef IMQ_DEBUG
+ printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n",
+ i, Exchanges->fcExchange[i].type,
+ Exchanges->fcExchange[i].fchs.d_id);
+#endif
+
+ Exchanges->fcExchange[i].status |= INITIATOR_ABORT;
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, i); // abort on LDn
+ }
+ }
+
+ }
+
+ // ################ LINK UP ##################
+ if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit
+ { // AL_PA could have changed
+
+ // We need the following code, duplicated from LinkDn condition,
+ // because it's possible for the Tachyon to re-initialize (hard
+ // reset) without ever getting a LinkDn indication.
+ pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+ if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+ {
+ pLoggedInPort->LOGO_timer = 3; // we want 2 seconds
+ // but Timer granularity
+ // is 1 second
+
+ // suspend any I/O in progress until
+ // PDISC received...
+
+ }
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ } // ... all Previously known ports checked
+
+ // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA)
+ fcChip->Registers.rcv_al_pa.value =
+ readl(fcChip->Registers.rcv_al_pa.address);
+
+ // Now, if our acquired address is DIFFERENT from our
+ // previous one, we are not allow to do PDISC - we
+ // must go back to PLOGI, which will terminate I/O in
+ // progress for ALL logged in FC devices...
+ // (This is highly unlikely).
+
+ if( (fcChip->Registers.my_al_pa & 0xFF) !=
+ ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) )
+ {
+
+// printk(" #our HBA port_id changed!# "); // FC port_id changed!!
+
+ pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ } // ... all Previously known ports checked
+
+ // when the port_id changes, we must terminate
+ // all open exchanges.
+ cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED);
+
+ }
+
+ // Replace the entire 24-bit port_id. We only know the
+ // lower 8 bits (alpa) from Tachyon; if a FLOGI is done,
+ // we'll get the upper 16-bits from the FLOGI ACC frame.
+ // If someone plugs into Fabric switch, we'll do FLOGI and
+ // get full 24-bit port_id; someone could then remove and
+ // hot-plug us into a dumb hub. If we send a 24-bit PLOGI
+ // to a "private" loop device, it might blow up.
+ // Consequently, we force the upper 16-bits of port_id to
+ // be re-set on every LinkUp transition
+ fcChip->Registers.my_al_pa =
+ (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF;
+
+
+ // copy frame manager status to unused ULONG slot
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+ fcChip->Registers.my_al_pa; // (for debugging)
+
+ // for TachLite, we need to write the acquired al_pa
+ // back into the FMconfig register, because after
+ // first initialization, the AQ (prev. acq.) bit gets
+ // set, causing TL FM to use the AL_PA field in FMconfig.
+ // (In Tachyon, FM writes the acquired AL_PA for us.)
+ ulBuff = readl( fcChip->Registers.FMconfig.address);
+ ulBuff &= 0x00ffffffL; // mask out current al_pa
+ ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa
+ fcChip->Registers.FMconfig.value = ulBuff; // copy it back
+ writel( fcChip->Registers.FMconfig.value, // put in TachLite
+ fcChip->Registers.FMconfig.address);
+
+
+#ifdef IMQ_DEBUG
+ printk("#LUp %Xh, FMstat 0x%08X#",
+ fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value);
+#endif
+
+ // also set the WRITE-ONLY My_ID Register (for Fabric
+ // initialization)
+ writel( fcChip->Registers.my_al_pa,
+ fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID);
+
+
+ fcChip->fcStats.linkUp++;
+
+ // reset TL statistics counters
+ // (we ignore these error counters
+ // while link is down)
+ ulBuff = // just reset TL's counter
+ readl( fcChip->Registers.FMLinkStatus1.address);
+
+ ulBuff = // just reset TL's counter
+ readl( fcChip->Registers.FMLinkStatus2.address);
+
+ // for initiator, need to start verifying ports (e.g. PDISC)
+
+
+
+
+
+
+ CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK
+
+ // Tachyon creates an interesting problem for us on LILP frames.
+ // Instead of writing the incoming LILP frame into the SFQ before
+ // indicating LINK UP (the actual order of events), Tachyon tells
+ // us LINK UP, and later us the LILP. So we delay, then examine the
+ // IMQ for an Inbound CM (x04); if found, we can set
+ // LINKACTIVE after processing the LILP. Otherwise, just proceed.
+ // Since Tachyon imposes this time delay (and doesn't tell us
+ // what it is), we have to impose a delay before "Peeking" the IMQ
+ // for Tach hardware (DMA) delivery.
+ // Processing LILP is required by SANMark
+ udelay( 1000); // microsec delay waiting for LILP (if it comes)
+ if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) )
+ { // found SFQ LILP, which will post LINKACTIVE
+// printk("skipping LINKACTIVE post\n");
+
+ }
+ else
+ cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame);
+ }
+
+
+
+ // ******* Set Fabric Login indication ********
+ if( fcChip->Registers.FMstatus.value & 0x2000 )
+ {
+ printk(" #Fabric# ");
+ fcChip->Options.fabric = 1;
+ }
+ else
+ fcChip->Options.fabric = 0;
+
+
+
+ // ******* LIP(F8,x) or BAD AL_PA? ********
+ if( fcChip->Registers.FMstatus.value & 0x30000L )
+ {
+ // copy the error AL_PAs
+ fcChip->Registers.rcv_al_pa.value =
+ readl(fcChip->Registers.rcv_al_pa.address);
+
+ // Bad AL_PA?
+ if( fcChip->Registers.FMstatus.value & 0x10000L )
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+
+ // copy "BAD" al_pa field
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+ (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8;
+
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort )
+ {
+ // Just in case we got this BAD_ALPA because a device
+ // quietly disappeared (can happen on non-managed hubs such
+ // as the Vixel Rapport 1000),
+ // do an Implicit Logout. We never expect this on a Logged
+ // in port (but do expect it on port discovery).
+ // (As a reasonable alternative, this could be changed to
+ // simply start the implicit logout timer, giving the device
+ // several seconds to "come back".)
+ //
+ printk(" #BAD alpa %Xh# ",
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]);
+ cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+ }
+ }
+ // LIP(f8,x)?
+ if( fcChip->Registers.FMstatus.value & 0x20000L )
+ {
+ // for debugging, copy al_pa field
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] =
+ (fcChip->Registers.rcv_al_pa.value & 0xffL);
+ // get the other port's al_pa
+ // (one that sent LIP(F8,?) )
+ }
+ }
+
+ // Elastic store err
+ if( fcChip->Registers.FMstatus.value & 0x400L )
+ {
+ // don't count e-s if loop is down!
+ if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) )
+ fcChip->fcStats.e_stores++;
+
+ }
+ }
+ break;
+
+
+ case INBOUND_FCP_XCHG_COMPLETION: // 0x0C
+
+ // Remarks:
+ // On Tachlite TL/TS, we get this message when the data phase
+ // of a SEST inbound transfer is complete. For example, if a WRITE command
+ // was received with OX_ID 0, we might respond with XFER_RDY with
+ // RX_ID 8001. This would start the SEST controlled data phases. When
+ // all data frames are received, we get this inbound completion. This means
+ // we should send a status frame to complete the status phase of the
+ // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data
+ // frames.
+ // See Outbound CM discussion of x_IDs
+ // Psuedo Code
+ // Get SEST index (x_ID)
+ // x_ID out of range, return (err condition)
+ // set status bits from 2nd dword
+ // free transactionID & SEST entry
+ // call fcComplete with transactionID & status
+
+ ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0];
+ x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID
+ // (mask out MSB "direction" bit)
+ // Range check CM OX/RX_ID value...
+ if( x_ID < TACH_SEST_LEN ) // don't go beyond SEST array space
+ {
+
+//#define FCP_COMPLETION_DBG 1
+#ifdef FCP_COMPLETION_DBG
+ printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n",
+ x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd);
+#endif
+ if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or -
+ // time to send response frame?
+ RPCset = 1; // (SEST transaction)
+ else
+ RPCset = 0;
+ // set the status for this Inbound SCSI transaction's ID
+ dwStatus = 0L;
+ if( ulBuff & 0x70000000L ) // any errs?
+ {
+
+ if( ulBuff & 0x40000000L )
+ dwStatus |= LINKFAIL_RX;
+
+ if( ulBuff & 0x20000000L )
+ dwStatus |= COUNT_ERROR;
+
+ if( ulBuff & 0x10000000L )
+ dwStatus |= OVERFLOW;
+ }
+
+
+ // FCP transaction done - copy status
+ Exchanges->fcExchange[ x_ID ].status = dwStatus;
+
+
+ // Did the exchange get an FCP-RSP response frame?
+ // (Note the little endian/big endian FC payload difference)
+
+ if( RPCset ) // SEST transaction Response frame rec'd
+ {
+ // complete the command in our driver...
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev,fcChip, x_ID);
+
+ } // end "RPCset"
+
+ else // ("target" logic)
+ {
+ // Tachlite says all data frames have been received - now it's time
+ // to analyze data transfer (successful?), then send a response
+ // frame for this exchange
+
+ ulFibreFrame[0] = x_ID; // copy for later reference
+
+ // if this was a TWE, we have to send satus response
+ if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE )
+ {
+// fcPutScsiQue( cpqfcHBAdata,
+// NEED_FCP_RSP, ulFibreFrame); // (ulFibreFrame not used here)
+ }
+ }
+ }
+ else // ERROR CONDITION! bogus x_ID in completion message
+ {
+ printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID);
+ }
+
+ break;
+
+
+
+
+ case INBOUND_SCSI_DATA_COMMAND:
+ case BAD_SCSI_FRAME:
+ case INB_SCSI_STATUS_COMPLETION:
+ case BUFFER_PROCESSED_COMPLETION:
+ break;
+ }
+
+ // Tachyon is producing;
+ // we are consuming
+ fcChip->IMQ->consumerIndex++; // increment OUR consumerIndex
+ if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover
+ fcChip->IMQ->consumerIndex = 0L; // reset it
+
+
+ if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex )
+ { // all Messages are processed -
+ iStatus = 0; // no more messages to process
+
+ }
+ else
+ iStatus = 1; // more messages to process
+
+ // update TachLite's ConsumerIndex... (clears INTA_L)
+ // NOTE: according to TL/TS UG, the
+ // "host must return completion messages in sequential order".
+ // Does this mean one at a time, in the order received? We
+ // presume so.
+
+ writel( fcChip->IMQ->consumerIndex,
+ (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+
+#if IMQ_DEBUG
+ printk("Process IMQ: writing consumer ndx %d\n ",
+ fcChip->IMQ->consumerIndex);
+ printk("PI %X, CI %X\n",
+ fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex );
+#endif
+
+
+
+ }
+ else
+ {
+ // hmmm... why did we get interrupted/called with no message?
+ iStatus = -1; // nothing to process
+#if IMQ_DEBUG
+ printk("Process IMQ: no message PI %Xh CI %Xh",
+ fcChip->IMQ->producerIndex,
+ fcChip->IMQ->consumerIndex);
+#endif
+ }
+
+ LEAVE("ProcessIMQEntry");
+
+ return iStatus;
+}
+
+
+
+
+
+// This routine initializes Tachyon according to the following
+// options (opcode1):
+// 1 - RESTART Tachyon, simulate power on condition by shutting
+// down laser, resetting the hardware, de-allocating all buffers;
+// continue
+// 2 - Config Tachyon / PCI registers;
+// continue
+// 3 - Allocating memory and setting Tachyon queues (write Tachyon regs);
+// continue
+// 4 - Config frame manager registers, initialize, turn on laser
+//
+// Returns:
+// -1 on fatal error
+// 0 on success
+
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ ULONG ulBuff;
+ UCHAR bBuff;
+ int iStatus=-1; // assume failure
+
+ ENTER("InitializeTachLite");
+
+ // verify board's base address (sanity check)
+
+ if( !fcChip->Registers.ReMapMemBase) // NULL address for card?
+ return -1; // FATAL error!
+
+
+
+ switch( opcode1 )
+ {
+ case 1: // restore hardware to power-on (hard) restart
+
+
+ iStatus = fcChip->ResetTachyon(
+ cpqfcHBAdata, opcode2); // laser off, reset hardware
+ // de-allocate aligned buffers
+
+
+/* TBD // reset FC link Q (producer and consumer = 0)
+ fcLinkQReset(cpqfcHBAdata);
+
+*/
+
+ if( iStatus )
+ break;
+
+ case 2: // Config PCI/Tachyon registers
+ // NOTE: For Tach TL/TS, bit 31 must be set to 1. For TS chips, a read
+ // of bit 31 indicates state of M66EN signal; if 1, chip may run at
+ // 33-66MHz (see TL/TS UG, pg 159)
+
+ ulBuff = 0x80000000; // TachLite Configuration Register
+
+ writel( ulBuff, fcChip->Registers.TYconfig.address);
+// ulBuff = 0x0147L; // CpqTs PCI CFGCMD register
+// WritePCIConfiguration( fcChip->Backplane.bus,
+// fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4);
+// ulBuff = 0x0L; // test!
+// ReadPCIConfiguration( fcChip->Backplane.bus,
+// fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4);
+
+ // read back for reference...
+ fcChip->Registers.TYconfig.value =
+ readl( fcChip->Registers.TYconfig.address );
+
+ // what is the PCI bus width?
+ pci_read_config_byte( cpqfcHBAdata->PciDev,
+ 0x43, // PCIMCTR offset
+ &bBuff);
+
+ fcChip->Registers.PCIMCTR = bBuff;
+
+ // set string identifying the chip on the circuit board
+
+ fcChip->Registers.TYstatus.value =
+ readl( fcChip->Registers.TYstatus.address);
+
+ {
+// Now that we are supporting multiple boards, we need to change
+// this logic to check for PCI vendor/device IDs...
+// for now, quick & dirty is simply checking Chip rev
+
+ ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5;
+ UCHAR Minor = (UCHAR)(RevId & 0x3);
+ UCHAR Major = (UCHAR)((RevId & 0x1C) >>2);
+
+ /* printk(" HBA Tachyon RevId %d.%d\n", Major, Minor); */
+ if( (Major == 1) && (Minor == 2) )
+ {
+ sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12);
+
+ }
+ else if( (Major == 1) && (Minor == 3) )
+ {
+ sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13);
+ }
+ else if( (Major == 2) && (Minor == 1) )
+ {
+ sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21);
+ }
+ else
+ sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN);
+ }
+
+
+
+ case 3: // allocate mem, set Tachyon Que registers
+ iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2);
+
+ if( iStatus )
+ break;
+
+ // now that the Queues exist, Tach can DMA to them, so
+ // we can begin processing INTs
+ // INTEN register - enable INT (TachLite interrupt)
+ writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN);
+
+ // Fall through
+ case 4: // Config Fame Manager, Init Loop Command, laser on
+
+ // L_PORT or loopback
+ // depending on Options
+ iStatus = CpqTsInitializeFrameManager( fcChip,0 );
+ if( iStatus )
+ {
+ // failed to initialize Frame Manager
+ break;
+ }
+
+ default:
+ break;
+ }
+ LEAVE("InitializeTachLite");
+
+ return iStatus;
+}
+
+
+
+
+// Depending on the type of platform memory allocation (e.g. dynamic),
+// it's probably best to free memory in opposite order as it was allocated.
+// Order of allocation: see other function
+
+
+int CpqTsDestroyTachLiteQues( void *pHBA, int opcode)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ USHORT i, iStatus=0;
+ void* vPtr; // mem Align manager sets this to the freed address on success
+ unsigned long ulPtr; // for 64-bit pointer cast (e.g. Alpa machine)
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PSGPAGES j, next;
+
+ ENTER("DestroyTachLiteQues");
+
+ if( fcChip->SEST )
+ {
+ // search out and free Pool for Extended S/G list pages
+
+ for( i=0; i < TACH_SEST_LEN; i++) // for each exchange
+ {
+ // It's possible that extended S/G pages were allocated, mapped, and
+ // not cleared due to error conditions or O/S driver termination.
+ // Make sure they're all gone.
+ if (Exchanges->fcExchange[i].Cmnd != NULL)
+ cpqfc_pci_unmap(cpqfcHBAdata->PciDev, Exchanges->fcExchange[i].Cmnd,
+ fcChip, i); // undo DMA mappings.
+
+ for (j=fcChip->SEST->sgPages[i] ; j != NULL ; j = next) {
+ next = j->next;
+ kfree(j);
+ }
+ fcChip->SEST->sgPages[i] = NULL;
+ }
+ ulPtr = (unsigned long)fcChip->SEST;
+ vPtr = fcMemManager( cpqfcHBAdata->PciDev,
+ &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr, NULL ); // 'free' mem
+ fcChip->SEST = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("SEST mem not freed\n");
+ iStatus = -1;
+ }
+ }
+
+ if( fcChip->SFQ )
+ {
+
+ ulPtr = (unsigned long)fcChip->SFQ;
+ vPtr = fcMemManager( cpqfcHBAdata->PciDev,
+ &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr, NULL ); // 'free' mem
+ fcChip->SFQ = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("SFQ mem not freed\n");
+ iStatus = -2;
+ }
+ }
+
+
+ if( fcChip->IMQ )
+ {
+ // clear Indexes to show empty Queue
+ fcChip->IMQ->producerIndex = 0;
+ fcChip->IMQ->consumerIndex = 0;
+
+ ulPtr = (unsigned long)fcChip->IMQ;
+ vPtr = fcMemManager( cpqfcHBAdata->PciDev, &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr, NULL ); // 'free' mem
+ fcChip->IMQ = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("IMQ mem not freed\n");
+ iStatus = -3;
+ }
+ }
+
+ if( fcChip->ERQ ) // release memory blocks used by the queues
+ {
+ ulPtr = (unsigned long)fcChip->ERQ;
+ vPtr = fcMemManager( cpqfcHBAdata->PciDev, &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr, NULL ); // 'free' mem
+ fcChip->ERQ = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("ERQ mem not freed\n");
+ iStatus = -4;
+ }
+ }
+
+ // free up the primary EXCHANGES struct and Link Q
+ cpqfc_free_dma_consistent(cpqfcHBAdata);
+
+ LEAVE("DestroyTachLiteQues");
+
+ return iStatus; // non-zero (failed) if any memory not freed
+}
+
+
+
+
+
+// The SFQ is an array with SFQ_LEN length, each element (QEntry)
+// with eight 32-bit words. TachLite places incoming FC frames (i.e.
+// a valid FC frame with our AL_PA ) in contiguous SFQ entries
+// and sends a completion message telling the host where the frame is
+// in the que.
+// This function copies the current (or oldest not-yet-processed) QEntry to
+// a caller's contiguous buffer and updates the Tachyon chip's consumer index
+//
+// NOTE:
+// An FC frame may consume one or many SFQ entries. We know the total
+// length from the completion message. The caller passes a buffer large
+// enough for the complete message (max 2k).
+
+static void CpqTsGetSFQEntry(
+ PTACHYON fcChip,
+ USHORT producerNdx,
+ ULONG *ulDestPtr, // contiguous destination buffer
+ BOOLEAN UpdateChip)
+{
+ ULONG total_bytes=0;
+ ULONG consumerIndex = fcChip->SFQ->consumerIndex;
+
+ // check passed copy of SFQ producer index -
+ // is a new message waiting for us?
+ // equal indexes means SFS is copied
+
+ while( producerNdx != consumerIndex )
+ { // need to process message
+ total_bytes += 64; // maintain count to prevent writing past buffer
+ // don't allow copies over Fibre Channel defined length!
+ if( total_bytes <= 2048 )
+ {
+ memcpy( ulDestPtr,
+ &fcChip->SFQ->QEntry[consumerIndex],
+ 64 ); // each SFQ entry is 64 bytes
+ ulDestPtr += 16; // advance pointer to next 64 byte block
+ }
+ // Tachyon is producing,
+ // and we are consuming
+
+ if( ++consumerIndex >= SFQ_LEN)// check for rollover
+ consumerIndex = 0L; // reset it
+ }
+
+ // if specified, update the Tachlite chip ConsumerIndex...
+ if( UpdateChip )
+ {
+ fcChip->SFQ->consumerIndex = consumerIndex;
+ writel( fcChip->SFQ->consumerIndex,
+ fcChip->Registers.SFQconsumerIndex.address);
+ }
+}
+
+
+
+// TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO,
+// and Exchange Request Queue (ERQ) on error recover -
+// (e.g. whenever a LIP occurs). Here
+// we routinely RESUME by clearing these bits, but only if the loop is up
+// to avoid ERROR IDLE messages forever.
+
+void CpqTsUnFreezeTachlite( void *pChip, int type )
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+ fcChip->Registers.TYcontrol.value =
+ readl(fcChip->Registers.TYcontrol.address);
+
+ // (bit 4 of value is GBIC LASER)
+ // if we 'unfreeze' the core machines before the loop is healthy
+ // (i.e. FLT, OS, LS failure bits set in FMstatus)
+ // we can get 'error idle' messages forever. Verify that
+ // FMstatus (Link Status) is OK before unfreezing.
+
+ if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear?
+ !(fcChip->Registers.FMstatus.value & 0x80 )) // Active LPSM?
+ {
+ fcChip->Registers.TYcontrol.value &= ~0x300L; // clear FEQ, FFA
+ if( type == 1 ) // unfreeze ERQ only
+ {
+// printk("Unfreezing ERQ\n");
+ fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ
+ }
+ else // unfreeze both ERQ and FCP-ASSIST (SEST)
+ {
+// printk("Unfreezing ERQ & FCP-ASSIST\n");
+
+ // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ
+ fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ
+ }
+
+ writel( fcChip->Registers.TYcontrol.value,
+ fcChip->Registers.TYcontrol.address);
+
+ }
+ // readback for verify (TachLite still frozen?)
+ fcChip->Registers.TYstatus.value =
+ readl(fcChip->Registers.TYstatus.address);
+}
+
+
+// Whenever an FC Exchange Abort is required, we must manipulate the
+// Host/Tachyon shared memory SEST table. Before doing this, we
+// must freeze Tachyon, which flushes certain buffers and ensure we
+// can manipulate the SEST without contention.
+// This freeze function will result in FCP & ERQ FROZEN completion
+// messages (per argument "type").
+
+void CpqTsFreezeTachlite( void *pChip, int type )
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+ fcChip->Registers.TYcontrol.value =
+ readl(fcChip->Registers.TYcontrol.address);
+
+ //set FFA, FEQ - freezes SCSI assist and ERQ
+ if( type == 1) // freeze ERQ only
+ fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser)
+ else // freeze both FCP assists (SEST) and ERQ
+ fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser)
+
+ writel( fcChip->Registers.TYcontrol.value,
+ fcChip->Registers.TYcontrol.address);
+
+}
+
+
+
+
+// TL has two Frame Manager Link Status Registers, with three 8-bit
+// fields each. These eight bit counters are cleared after each read,
+// so we define six 32-bit accumulators for these TL counters. This
+// function breaks out each 8-bit field and adds the value to the existing
+// sum. (s/w counters cleared independently)
+
+void fcParseLinkStatusCounters(PTACHYON fcChip)
+{
+ UCHAR bBuff;
+ ULONG ulBuff;
+
+
+// The BB0 timer usually increments when TL is initialized, resulting
+// in an initially bogus count. If our own counter is ZERO, it means we
+// are reading this thing for the first time, so we ignore the first count.
+// Also, reading the register does not clear it, so we have to keep an
+// additional static counter to detect rollover (yuk).
+
+ if( fcChip->fcStats.lastBB0timer == 0L) // TL was reset? (ignore 1st values)
+ {
+ // get TL's register counter - the "last" count
+ fcChip->fcStats.lastBB0timer =
+ fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+ }
+ else // subsequent pass - check for rollover
+ {
+ // "this" count
+ ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+ if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened
+ {
+ // counter advanced to max...
+ fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer);
+ fcChip->fcStats.BB0_Timer += ulBuff; // plus some more
+
+
+ }
+ else // no rollover -- more counts or no change
+ {
+ fcChip->fcStats.BB0_Timer += (ulBuff - fcChip->fcStats.lastBB0timer);
+
+ }
+
+ fcChip->fcStats.lastBB0timer = ulBuff;
+ }
+
+
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24);
+ fcChip->fcStats.LossofSignal += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16);
+ fcChip->fcStats.BadRXChar += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8);
+ fcChip->fcStats.LossofSync += bBuff;
+
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24);
+ fcChip->fcStats.Rx_EOFa += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16);
+ fcChip->fcStats.Dis_Frm += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8);
+ fcChip->fcStats.Bad_CRC += bBuff;
+}
+
+
+void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip)
+{
+ ENTER("ClearLinkStatusCounters");
+ memset( &fcChip->fcStats, 0, sizeof( FCSTATS));
+ LEAVE("ClearLinkStatusCounters");
+
+}
+
+
+
+
+// The following function reads the I2C hardware to get the adapter's
+// World Wide Name (WWN).
+// If the WWN is "500805f1fadb43e8" (as printed on the card), the
+// Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register
+// is fadb43e8.
+// In the NVRAM, the bytes appear as:
+// [2d] ..
+// [2e] ..
+// [2f] 50
+// [30] 08
+// [31] 05
+// [32] f1
+// [33] fa
+// [34] db
+// [35] 43
+// [36] e8
+//
+// In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will
+// be correctly loaded by Tachyon silicon. In the login payload, bytes
+// must be correctly swapped for Big Endian format.
+
+int CpqTsReadWriteWWN( PVOID pChip, int Read)
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+ unsigned short i, count = NVRAM_SIZE;
+ UCHAR nvRam[NVRAM_SIZE], WWNbuf[8];
+ ULONG ulBuff;
+ int iStatus=-1; // assume failure
+ int WWNoffset;
+
+ ENTER("ReadWriteWWN");
+ // Now try to read the WWN from the adapter's NVRAM
+
+ if( Read ) // READing NVRAM WWN?
+ {
+ ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address,
+ fcChip->Registers.TYcontrol.address,
+ count, &nvRam[0] );
+
+ if( ulBuff ) // NVRAM read successful?
+ {
+ iStatus = 0; // success!
+
+ // for engineering/ prototype boards, the data may be
+ // invalid (GIGO, usually all "FF"); this prevents the
+ // parse routine from working correctly, which means
+ // nothing will be written to our passed buffer.
+
+ WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam );
+
+ if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly
+ {
+ printk( "CAUTION: Copying NVRAM data on fcChip\n");
+ for( i= 0; i < 8; i++)
+ WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work
+ }
+
+ fcChip->Registers.wwn_hi = 0L;
+ fcChip->Registers.wwn_lo = 0L;
+ for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM
+ {
+ ulBuff = 0L;
+ ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i));
+ fcChip->Registers.wwn_hi |= ulBuff;
+ }
+ for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM
+ {
+ ulBuff = 0L;
+ ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i));
+ fcChip->Registers.wwn_lo |= ulBuff;
+ }
+ } // done reading
+ else
+ {
+
+ printk( "cpqfcTS: NVRAM read failed\n");
+
+ }
+ }
+
+ else // WRITE
+ {
+
+ // NOTE: WRITE not supported & not used in released driver.
+
+
+ printk("ReadWriteNRAM: can't write NVRAM; aborting write\n");
+ }
+
+ LEAVE("ReadWriteWWN");
+ return iStatus;
+}
+
+
+
+
+
+// The following function reads or writes the entire "NVRAM" contents of
+// the I2C hardware (i.e. the NM24C03). Note that HP's 5121A (TS 66Mhz)
+// adapter does not use the NM24C03 chip, so this function only works on
+// Compaq's adapters.
+
+int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read)
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+ ULONG ulBuff;
+ UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array
+ int iStatus=-1; // assume failure
+
+
+ if( Read ) // READing NVRAM?
+ {
+ ulBuff = cpqfcTS_ReadNVRAM( // TRUE on success
+ fcChip->Registers.TYstatus.address,
+ fcChip->Registers.TYcontrol.address,
+ 256, // bytes to write
+ ucPtr ); // source ptr
+
+
+ if( ulBuff )
+ iStatus = 0; // success
+ else
+ {
+#ifdef DBG
+ printk( "CAUTION: NVRAM read failed\n");
+#endif
+ }
+ } // done reading
+
+ else // WRITING NVRAM
+ {
+
+ printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n");
+ }
+
+ return iStatus;
+}
diff --git a/drivers/scsi/cpqfcTSi2c.c b/drivers/scsi/cpqfcTSi2c.c
new file mode 100644
index 000000000000..b38a6a9a55a3
--- /dev/null
+++ b/drivers/scsi/cpqfcTSi2c.c
@@ -0,0 +1,493 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+// These functions control the NVRAM I2C hardware on
+// non-intelligent Fibre Host Adapters.
+// The primary purpose is to read the HBA's NVRAM to get adapter's
+// manufactured WWN to copy into Tachyon chip registers
+// Orignal source author unknown
+
+#include <linux/types.h>
+enum boolean { FALSE, TRUE } ;
+
+
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+
+
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O
+
+#include "cpqfcTSchip.h"
+
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data );
+/*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout,
+ USHORT startOffset, // e.g. 0x2f for WWN start
+ USHORT count,
+ UCHAR *buf );
+*/
+
+//
+// Tachlite GPIO2, GPIO3 (I2C) DEFINES
+// The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data)
+// GPIO2 drives SDA, and GPIO3 drives SCL
+//
+// Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0
+// and clear writes 1. The input lines (read in TL status) is NOT inverted
+// This really helps confuse the code and debugging.
+
+#define SET_DATA_HI 0x0
+#define SET_DATA_LO 0x8
+#define SET_CLOCK_HI 0x0
+#define SET_CLOCK_LO 0x4
+
+#define SENSE_DATA_HI 0x8
+#define SENSE_DATA_LO 0x0
+#define SENSE_CLOCK_HI 0x4
+#define SENSE_CLOCK_LO 0x0
+
+#define SLAVE_READ_ADDRESS 0xA1
+#define SLAVE_WRITE_ADDRESS 0xA0
+
+
+static void i2c_delay(ULONG mstime);
+static void tl_i2c_clock_pulse( UCHAR , void* GPIOout);
+static UCHAR tl_read_i2c_data( void* );
+
+
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_RX_ACK
+//
+// This routine receives an acknowledge over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout )
+{
+ unsigned long value;
+
+ // do clock pulse, let data line float high
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+ // slave must drive data low for acknowledge
+ value = tl_read_i2c_data( GPIOin);
+ if (value & SENSE_DATA_HI )
+ return( FALSE );
+
+ return( TRUE );
+}
+//-----------------------------------------------------------------------------
+//
+// Name: READ_I2C_REG
+//
+// This routine reads the I2C control register using the global
+// IO address stored in gpioreg.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_read_i2c_data( void* gpioreg )
+{
+ return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3
+}
+//-----------------------------------------------------------------------------
+//
+// Name: WRITE_I2C_REG
+//
+// This routine writes the I2C control register using the global
+// IO address stored in gpioreg.
+// In Tachlite, we don't want to modify other bits in TL Control reg.
+//
+//-----------------------------------------------------------------------------
+static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value )
+{
+ ULONG temp;
+
+ // First read the register and clear out the old bits
+ temp = readl( gpioregOUT ) & 0xfffffff3L;
+
+ // Now or in the new data and send it back out
+ writel( temp | value, gpioregOUT);
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_TX_START
+//
+// This routine transmits a start condition over the I2C bus.
+// 1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH,
+// wait 5us to stabilize.
+// 2. With SCL still HIGH, drive SDA low. The low transition marks
+// the start condition to NM24Cxx (the chip)
+// NOTE! In TL control reg., output 1 means chip sees LOW
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout )
+{
+ unsigned short i;
+ ULONG value;
+
+ if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI))
+ {
+ // start with clock high, let data float high
+ tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+ // keep sending clock pulses if slave is driving data line
+ for (i = 0; i < 10; i++)
+ {
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+ if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+ break;
+ }
+
+ // if he's still driving data low after 10 clocks, abort
+ value = tl_read_i2c_data( GPIOin ); // read status
+ if (!(value & 0x08) )
+ return( FALSE );
+ }
+
+
+ // To START, bring data low while clock high
+ tl_write_i2c_reg( GPIOout, SET_CLOCK_HI | SET_DATA_LO );
+
+ i2c_delay(0);
+
+ return( TRUE ); // TX start successful
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_TX_STOP
+//
+// This routine transmits a stop condition over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+
+static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout )
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ // Send clock pulse, drive data line low
+ tl_i2c_clock_pulse( SET_DATA_LO, GPIOout );
+
+ // To STOP, bring data high while clock high
+ tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+ // Give the data line time to float high
+ i2c_delay(0);
+
+ // If slave is driving data line low, there's a problem; retry
+ if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+ return( TRUE ); // TX STOP successful!
+ }
+
+ return( FALSE ); // error
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_TX_uchar
+//
+// This routine transmits a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data )
+{
+ UCHAR bit;
+
+ for (bit = 0x80; bit; bit >>= 1)
+ {
+ if( data & bit )
+ tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout);
+ else
+ tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout);
+ }
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_RX_uchar
+//
+// This routine receives a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout )
+{
+ UCHAR bit;
+ UCHAR data = 0;
+
+
+ for (bit = 0x80; bit; bit >>= 1) {
+ // do clock pulse, let data line float high
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+ // read data line
+ if ( tl_read_i2c_data( GPIOin) & 0x08 )
+ data |= bit;
+ }
+
+ return (data);
+}
+//*****************************************************************************
+//*****************************************************************************
+// Function: read_i2c_nvram
+// Arguments: UCHAR count number of bytes to read
+// UCHAR *buf area to store the bytes read
+// Returns: 0 - failed
+// 1 - success
+//*****************************************************************************
+//*****************************************************************************
+unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+ UCHAR *buf )
+{
+ unsigned short i;
+
+ if( !( tl_i2c_tx_start(GPIOin, GPIOout) ))
+ return FALSE;
+
+ // Select the NVRAM for "dummy" write, to set the address
+ tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS );
+ if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+ return( FALSE );
+
+ // Now send the address where we want to start reading
+ tl_i2c_tx_byte( GPIOout , 0 );
+ if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+ return( FALSE );
+
+ // Send a repeated start condition and select the
+ // slave for reading now.
+ if( tl_i2c_tx_start(GPIOin, GPIOout) )
+ tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS );
+
+ if ( !tl_i2c_rx_ack(GPIOin, GPIOout) )
+ return( FALSE );
+
+ // this loop will now read out the data and store it
+ // in the buffer pointed to by buf
+ for ( i=0; i<count; i++)
+ {
+ *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout);
+
+ // Send ACK by holding data line low for 1 clock
+ if ( i < (count-1) )
+ tl_i2c_clock_pulse( 0x08, GPIOout );
+ else {
+ // Don't send ack for final byte
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+ }
+ }
+
+ tl_i2c_tx_stop(GPIOin, GPIOout);
+
+ return( TRUE );
+}
+
+//****************************************************************
+//
+//
+//
+// routines to set and clear the data and clock bits
+//
+//
+//
+//****************************************************************
+
+static void tl_set_clock(void* gpioreg)
+{
+ ULONG ret_val;
+
+ ret_val = readl( gpioreg );
+ ret_val &= 0xffffffFBL; // clear GPIO2 (SCL)
+ writel( ret_val, gpioreg);
+}
+
+static void tl_clr_clock(void* gpioreg)
+{
+ ULONG ret_val;
+
+ ret_val = readl( gpioreg );
+ ret_val |= SET_CLOCK_LO;
+ writel( ret_val, gpioreg);
+}
+
+//*****************************************************************
+//
+//
+// This routine will advance the clock by one period
+//
+//
+//*****************************************************************
+static void tl_i2c_clock_pulse( UCHAR value, void* GPIOout )
+{
+ ULONG ret_val;
+
+ // clear the clock bit
+ tl_clr_clock( GPIOout );
+
+ i2c_delay(0);
+
+
+ // read the port to preserve non-I2C bits
+ ret_val = readl( GPIOout );
+
+ // clear the data & clock bits
+ ret_val &= 0xFFFFFFf3;
+
+ // write the value passed in...
+ // data can only change while clock is LOW!
+ ret_val |= value; // the data
+ ret_val |= SET_CLOCK_LO; // the clock
+ writel( ret_val, GPIOout );
+
+ i2c_delay(0);
+
+
+ //set clock bit
+ tl_set_clock( GPIOout);
+}
+
+
+
+
+//*****************************************************************
+//
+//
+// This routine returns the 64-bit WWN
+//
+//
+//*****************************************************************
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf )
+{
+ ULONG len;
+ ULONG sub_len;
+ ULONG ptr_inc;
+ ULONG i;
+ ULONG j;
+ UCHAR *data_ptr;
+ UCHAR z;
+ UCHAR name;
+ UCHAR sub_name;
+ UCHAR done;
+ int iReturn=0; // def. 0 offset is failure to find WWN field
+
+
+
+ data_ptr = (UCHAR *)buf;
+
+ done = FALSE;
+ i = 0;
+
+ while ( (i < 128) && (!done) )
+ {
+ z = data_ptr[i];\
+ if ( !(z & 0x80) )
+ {
+ len = 1 + (z & 0x07);
+
+ name = (z & 0x78) >> 3;
+ if (name == 0x0F)
+ done = TRUE;
+ }
+ else
+ {
+ name = z & 0x7F;
+ len = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8);
+
+ switch (name)
+ {
+ case 0x0D:
+ //
+ j = i + 3;
+ //
+ if ( data_ptr[j] == 0x3b ) {
+ len = 6;
+ break;
+ }
+
+ while ( j<(i+len) ) {
+ sub_name = (data_ptr[j] & 0x3f);
+ sub_len = data_ptr[j+1] +
+ (data_ptr[j+2] << 8);
+ ptr_inc = sub_len + 3;
+ switch (sub_name)
+ {
+ case 0x3C:
+ memcpy( wwnbuf, &data_ptr[j+3], 8);
+ iReturn = j+3;
+ break;
+ default:
+ break;
+ }
+ j += ptr_inc;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ //
+ i += len;
+ } // end while
+ return iReturn;
+}
+
+
+
+
+
+// define a short 5 micro sec delay, and longer (ms) delay
+
+static void i2c_delay(ULONG mstime)
+{
+ ULONG i;
+
+// NOTE: we only expect to use these delays when reading
+// our adapter's NVRAM, which happens only during adapter reset.
+// Delay technique from "Linux Device Drivers", A. Rubini
+// (1st Ed.) pg 137.
+
+// printk(" delay %lx ", mstime);
+ if( mstime ) // ms delay?
+ {
+ // delay technique
+ for( i=0; i < mstime; i++)
+ udelay(1000); // 1ms per loop
+
+ }
+ else // 5 micro sec delay
+
+ udelay( 5 ); // micro secs
+
+// printk("done\n");
+}
+
+
+
diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c
new file mode 100644
index 000000000000..2eeb493f5a2b
--- /dev/null
+++ b/drivers/scsi/cpqfcTSinit.c
@@ -0,0 +1,2098 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+ * IOCTL and procfs added by Jouke Numan
+ * SMP testing by Chel Van Gennip
+ *
+ * portions copied from:
+ * QLogic CPQFCTS SCSI-FCP
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ * Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu>
+ * Chris Loveland <cwl@iol.unh.edu> to support the isp2100 and isp2200
+*/
+
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/ioport.h> // request_region() prototype
+#include <linux/completion.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h> // ioctl related
+#include <asm/irq.h>
+#include <linux/spinlock.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+#include "cpqfcTStrigger.h"
+
+#include "cpqfcTS.h"
+
+/* Embedded module documentation macros - see module.h */
+MODULE_AUTHOR("Compaq Computer Corporation");
+MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.5.4");
+MODULE_LICENSE("GPL");
+
+int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags);
+
+// This struct was originally defined in
+// /usr/src/linux/include/linux/proc_fs.h
+// since it's only partially implemented, we only use first
+// few fields...
+// NOTE: proc_fs changes in 2.4 kernel
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_cpqfcTS =
+{
+ PROC_SCSI_CPQFCTS, // ushort low_ino (enumerated list)
+ 7, // ushort namelen
+ DEV_NAME, // const char* name
+ S_IFDIR | S_IRUGO | S_IXUGO, // mode_t mode
+ 2 // nlink_t nlink
+ // etc. ...
+};
+
+
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,7)
+# define CPQFC_DECLARE_COMPLETION(x) DECLARE_COMPLETION(x)
+# define CPQFC_WAITING waiting
+# define CPQFC_COMPLETE(x) complete(x)
+# define CPQFC_WAIT_FOR_COMPLETION(x) wait_for_completion(x);
+#else
+# define CPQFC_DECLARE_COMPLETION(x) DECLARE_MUTEX_LOCKED(x)
+# define CPQFC_WAITING sem
+# define CPQFC_COMPLETE(x) up(x)
+# define CPQFC_WAIT_FOR_COMPLETION(x) down(x)
+#endif
+
+static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba);
+
+/* local function to load our per-HBA (local) data for chip
+ registers, FC link state, all FC exchanges, etc.
+
+ We allocate space and compute address offsets for the
+ most frequently accessed addresses; others (like World Wide
+ Name) are not necessary.
+*/
+static void Cpqfc_initHBAdata(CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
+{
+
+ cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr
+
+ // since x86 port space is 64k, we only need the lower 16 bits
+ cpqfcHBAdata->fcChip.Registers.IOBaseL =
+ PciDev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
+
+ cpqfcHBAdata->fcChip.Registers.IOBaseU =
+ PciDev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK;
+
+ // 32-bit memory addresses
+ cpqfcHBAdata->fcChip.Registers.MemBase =
+ PciDev->resource[3].start & PCI_BASE_ADDRESS_MEM_MASK;
+
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase =
+ ioremap( PciDev->resource[3].start & PCI_BASE_ADDRESS_MEM_MASK,
+ 0x200);
+
+ cpqfcHBAdata->fcChip.Registers.RAMBase =
+ PciDev->resource[4].start;
+
+ cpqfcHBAdata->fcChip.Registers.SROMBase = // NULL for HP TS adapter
+ PciDev->resource[5].start;
+
+ // now the Tachlite chip registers
+ // the REGISTER struct holds both the physical address & last
+ // written value (some TL registers are WRITE ONLY)
+
+ cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_SFQ_CONSUMER_INDEX;
+
+ cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX;
+
+ // TL Frame Manager
+ cpqfcHBAdata->fcChip.Registers.FMconfig.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONFIG;
+ cpqfcHBAdata->fcChip.Registers.FMcontrol.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONTROL;
+ cpqfcHBAdata->fcChip.Registers.FMstatus.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_STATUS;
+ cpqfcHBAdata->fcChip.Registers.FMLinkStatus1.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT1;
+ cpqfcHBAdata->fcChip.Registers.FMLinkStatus2.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT2;
+ cpqfcHBAdata->fcChip.Registers.FMBB_CreditZero.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_BB_CREDIT0;
+
+ // TL Control Regs
+ cpqfcHBAdata->fcChip.Registers.TYconfig.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONFIG;
+ cpqfcHBAdata->fcChip.Registers.TYcontrol.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONTROL;
+ cpqfcHBAdata->fcChip.Registers.TYstatus.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_STATUS;
+ cpqfcHBAdata->fcChip.Registers.rcv_al_pa.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_RCV_AL_PA;
+ cpqfcHBAdata->fcChip.Registers.ed_tov.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_ED_TOV;
+
+
+ cpqfcHBAdata->fcChip.Registers.INTEN.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTEN;
+ cpqfcHBAdata->fcChip.Registers.INTPEND.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTPEND;
+ cpqfcHBAdata->fcChip.Registers.INTSTAT.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTSTAT;
+
+ DEBUG_PCI(printk(" cpqfcHBAdata->fcChip.Registers. :\n"));
+ DEBUG_PCI(printk(" IOBaseL = %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseL));
+ DEBUG_PCI(printk(" IOBaseU = %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseU));
+
+ /* printk(" ioremap'd Membase: %p\n", cpqfcHBAdata->fcChip.Registers.ReMapMemBase); */
+
+ DEBUG_PCI(printk(" SFQconsumerIndex.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address));
+ DEBUG_PCI(printk(" ERQproducerIndex.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address));
+ DEBUG_PCI(printk(" TYconfig.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.TYconfig.address));
+ DEBUG_PCI(printk(" FMconfig.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.FMconfig.address));
+ DEBUG_PCI(printk(" FMcontrol.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.FMcontrol.address));
+
+ // set default options for FC controller (chip)
+ cpqfcHBAdata->fcChip.Options.initiator = 1; // default: SCSI initiator
+ cpqfcHBAdata->fcChip.Options.target = 0; // default: SCSI target
+ cpqfcHBAdata->fcChip.Options.extLoopback = 0;// default: no loopback @GBIC
+ cpqfcHBAdata->fcChip.Options.intLoopback = 0;// default: no loopback inside chip
+
+ // set highest and lowest FC-PH version the adapter/driver supports
+ // (NOT strict compliance)
+ cpqfcHBAdata->fcChip.highest_FCPH_ver = FC_PH3;
+ cpqfcHBAdata->fcChip.lowest_FCPH_ver = FC_PH43;
+
+ // set function points for this controller / adapter
+ cpqfcHBAdata->fcChip.ResetTachyon = CpqTsResetTachLite;
+ cpqfcHBAdata->fcChip.FreezeTachyon = CpqTsFreezeTachlite;
+ cpqfcHBAdata->fcChip.UnFreezeTachyon = CpqTsUnFreezeTachlite;
+ cpqfcHBAdata->fcChip.CreateTachyonQues = CpqTsCreateTachLiteQues;
+ cpqfcHBAdata->fcChip.DestroyTachyonQues = CpqTsDestroyTachLiteQues;
+ cpqfcHBAdata->fcChip.InitializeTachyon = CpqTsInitializeTachLite;
+ cpqfcHBAdata->fcChip.LaserControl = CpqTsLaserControl;
+ cpqfcHBAdata->fcChip.ProcessIMQEntry = CpqTsProcessIMQEntry;
+ cpqfcHBAdata->fcChip.InitializeFrameManager = CpqTsInitializeFrameManager;
+ cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN;
+ cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM;
+
+ if (cpqfc_alloc_private_data_pool(cpqfcHBAdata) != 0) {
+ printk(KERN_WARNING
+ "cpqfc: unable to allocate pool for passthru ioctls. "
+ "Passthru ioctls disabled.\n");
+ }
+}
+
+
+/* (borrowed from linux/drivers/scsi/hosts.c) */
+static void launch_FCworker_thread(struct Scsi_Host *HostAdapter)
+{
+ DECLARE_MUTEX_LOCKED(sem);
+
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+ ENTER("launch_FC_worker_thread");
+
+ cpqfcHBAdata->notify_wt = &sem;
+
+ /* must unlock before kernel_thread(), for it may cause a reschedule. */
+ spin_unlock_irq(HostAdapter->host_lock);
+ kernel_thread((int (*)(void *))cpqfcTSWorkerThread,
+ (void *) HostAdapter, 0);
+ /*
+ * Now wait for the kernel error thread to initialize itself
+
+ */
+ down (&sem);
+ spin_lock_irq(HostAdapter->host_lock);
+ cpqfcHBAdata->notify_wt = NULL;
+
+ LEAVE("launch_FC_worker_thread");
+
+}
+
+
+/* "Entry" point to discover if any supported PCI
+ bus adapter can be found
+*/
+/* We're supporting:
+ * Compaq 64-bit, 66MHz HBA with Tachyon TS
+ * Agilent XL2
+ * HP Tachyon
+ */
+#define HBA_TYPES 3
+
+#ifndef PCI_DEVICE_ID_COMPAQ_
+#define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc
+#endif
+
+static struct SupportedPCIcards cpqfc_boards[] __initdata = {
+ {PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TACHYON},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_TACHLITE},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_TACHYON},
+};
+
+
+int cpqfcTS_detect(Scsi_Host_Template *ScsiHostTemplate)
+{
+ int NumberOfAdapters=0; // how many of our PCI adapters are found?
+ struct pci_dev *PciDev = NULL;
+ struct Scsi_Host *HostAdapter = NULL;
+ CPQFCHBA *cpqfcHBAdata = NULL;
+ struct timer_list *cpqfcTStimer = NULL;
+ int i;
+
+ ENTER("cpqfcTS_detect");
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+ ScsiHostTemplate->proc_dir = &proc_scsi_cpqfcTS;
+#else
+ ScsiHostTemplate->proc_name = "cpqfcTS";
+#endif
+
+ for( i=0; i < HBA_TYPES; i++)
+ {
+ // look for all HBAs of each type
+
+ while((PciDev = pci_find_device(cpqfc_boards[i].vendor_id,
+ cpqfc_boards[i].device_id, PciDev)))
+ {
+
+ if (pci_enable_device(PciDev)) {
+ printk(KERN_ERR
+ "cpqfc: can't enable PCI device at %s\n", pci_name(PciDev));
+ goto err_continue;
+ }
+
+ if (pci_set_dma_mask(PciDev, CPQFCTS_DMA_MASK) != 0) {
+ printk(KERN_WARNING
+ "cpqfc: HBA cannot support required DMA mask, skipping.\n");
+ goto err_disable_dev;
+ }
+
+ // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes...
+ /* printk(" scsi_register allocating %d bytes for FC HBA\n",
+ (ULONG)sizeof(CPQFCHBA)); */
+
+ HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) );
+
+ if(HostAdapter == NULL) {
+ printk(KERN_WARNING
+ "cpqfc: can't register SCSI HBA, skipping.\n");
+ goto err_disable_dev;
+ }
+ DEBUG_PCI( printk(" HBA found!\n"));
+ DEBUG_PCI( printk(" HostAdapter->PciDev->irq = %u\n", PciDev->irq) );
+ DEBUG_PCI(printk(" PciDev->baseaddress[0]= %lx\n",
+ PciDev->resource[0].start));
+ DEBUG_PCI(printk(" PciDev->baseaddress[1]= %lx\n",
+ PciDev->resource[1].start));
+ DEBUG_PCI(printk(" PciDev->baseaddress[2]= %lx\n",
+ PciDev->resource[2].start));
+ DEBUG_PCI(printk(" PciDev->baseaddress[3]= %lx\n",
+ PciDev->resource[3].start));
+
+ scsi_set_device(HostAdapter, &PciDev->dev);
+ HostAdapter->irq = PciDev->irq; // copy for Scsi layers
+
+ // HP Tachlite uses two (255-byte) ranges of Port I/O (lower & upper),
+ // for a total I/O port address space of 512 bytes.
+ // mask out the I/O port address (lower) & record
+ HostAdapter->io_port = (unsigned int)
+ PciDev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
+ HostAdapter->n_io_port = 0xff;
+
+ // i.e., expect 128 targets (arbitrary number), while the
+ // RA-4000 supports 32 LUNs
+ HostAdapter->max_id = 0; // incremented as devices log in
+ HostAdapter->max_lun = CPQFCTS_MAX_LUN; // LUNs per FC device
+ HostAdapter->max_channel = CPQFCTS_MAX_CHANNEL; // multiple busses?
+
+ // get the pointer to our HBA specific data... (one for
+ // each HBA on the PCI bus(ses)).
+ cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+ // make certain our data struct is clear
+ memset( cpqfcHBAdata, 0, sizeof( CPQFCHBA ) );
+
+
+ // initialize our HBA info
+ cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+ cpqfcHBAdata->HostAdapter = HostAdapter; // back ptr
+ Cpqfc_initHBAdata( cpqfcHBAdata, PciDev ); // fill MOST fields
+
+ cpqfcHBAdata->HBAnum = NumberOfAdapters;
+ spin_lock_init(&cpqfcHBAdata->hba_spinlock);
+
+ // request necessary resources and check for conflicts
+ if( request_irq( HostAdapter->irq,
+ cpqfcTS_intr_handler,
+ SA_INTERRUPT | SA_SHIRQ,
+ DEV_NAME,
+ HostAdapter) )
+ {
+ printk(KERN_WARNING "cpqfc: IRQ %u already used\n", HostAdapter->irq);
+ goto err_unregister;
+ }
+
+ // Since we have two 256-byte I/O port ranges (upper
+ // and lower), check them both
+ if( !request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU,
+ 0xff, DEV_NAME ) )
+ {
+ printk(KERN_WARNING "cpqfc: address in use: %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseU);
+ goto err_free_irq;
+ }
+
+ if( !request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL,
+ 0xff, DEV_NAME ) )
+ {
+ printk(KERN_WARNING "cpqfc: address in use: %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseL);
+ goto err_release_region_U;
+ }
+
+ // OK, we have grabbed everything we need now.
+ DEBUG_PCI(printk(" Reserved 255 I/O addresses @ %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseL ));
+ DEBUG_PCI(printk(" Reserved 255 I/O addresses @ %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseU ));
+
+
+
+ // start our kernel worker thread
+
+ spin_lock_irq(HostAdapter->host_lock);
+ launch_FCworker_thread(HostAdapter);
+
+
+ // start our TimerTask...
+
+ cpqfcTStimer = &cpqfcHBAdata->cpqfcTStimer;
+
+ init_timer( cpqfcTStimer); // Linux clears next/prev values
+ cpqfcTStimer->expires = jiffies + HZ; // one second
+ cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter
+ cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping
+
+ add_timer( cpqfcTStimer); // give it to Linux
+
+
+ // now initialize our hardware...
+ if (cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1)) {
+ printk(KERN_WARNING "cpqfc: initialization of HBA hardware failed.\n");
+ goto err_release_region_L;
+ }
+
+ cpqfcHBAdata->fcStatsTime = jiffies; // (for FC Statistics delta)
+
+ // give our HBA time to initialize and login current devices...
+ {
+ // The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000,
+ // has the following algorithm for FL_Port startup:
+ // Time(sec) Action
+ // 0: Device Plugin and LIP(F7,F7) transmission
+ // 1.0 LIP incoming
+ // 1.027 LISA incoming, no CLS! (link not up)
+ // 1.028 NOS incoming (switch test for N_Port)
+ // 1.577 ED_TOV expired, transmit LIPs again
+ // 3.0 LIP(F8,F7) incoming (switch passes Tach Prim.Sig)
+ // 3.028 LILP received, link up, FLOGI starts
+ // slowest(worst) case, measured on 1Gb Finisar GT analyzer
+
+ unsigned long stop_time;
+
+ spin_unlock_irq(HostAdapter->host_lock);
+ stop_time = jiffies + 4*HZ;
+ while ( time_before(jiffies, stop_time) )
+ schedule(); // (our worker task needs to run)
+
+ }
+
+ spin_lock_irq(HostAdapter->host_lock);
+ NumberOfAdapters++;
+ spin_unlock_irq(HostAdapter->host_lock);
+
+ continue;
+
+err_release_region_L:
+ release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff );
+err_release_region_U:
+ release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff );
+err_free_irq:
+ free_irq( HostAdapter->irq, HostAdapter);
+err_unregister:
+ scsi_unregister( HostAdapter);
+err_disable_dev:
+ pci_disable_device( PciDev );
+err_continue:
+ continue;
+ } // end of while()
+ }
+
+ LEAVE("cpqfcTS_detect");
+
+ return NumberOfAdapters;
+}
+
+#ifdef SUPPORT_RESET
+static void my_ioctl_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->CPQFC_WAITING != NULL)
+ CPQFC_COMPLETE(req->CPQFC_WAITING);
+}
+#endif
+
+static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba)
+{
+ hba->private_data_bits = NULL;
+ hba->private_data_pool = NULL;
+ hba->private_data_bits =
+ kmalloc(((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /
+ BITS_PER_LONG)*sizeof(unsigned long),
+ GFP_KERNEL);
+ if (hba->private_data_bits == NULL)
+ return -1;
+ memset(hba->private_data_bits, 0,
+ ((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /
+ BITS_PER_LONG)*sizeof(unsigned long));
+ hba->private_data_pool = kmalloc(sizeof(cpqfc_passthru_private_t) *
+ CPQFC_MAX_PASSTHRU_CMDS, GFP_KERNEL);
+ if (hba->private_data_pool == NULL) {
+ kfree(hba->private_data_bits);
+ hba->private_data_bits = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+static void cpqfc_free_private_data_pool(CPQFCHBA *hba)
+{
+ kfree(hba->private_data_bits);
+ kfree(hba->private_data_pool);
+}
+
+int is_private_data_of_cpqfc(CPQFCHBA *hba, void *pointer)
+{
+ /* Is pointer within our private data pool?
+ We use Scsi_Request->upper_private_data (normally
+ reserved for upper layer drivers, e.g. the sg driver)
+ We check to see if the pointer is ours by looking at
+ its address. Is this ok? Hmm, it occurs to me that
+ a user app might do something bad by using sg to send
+ a cpqfc passthrough ioctl with upper_data_private
+ forged to be somewhere in our pool..., though they'd
+ normally have to be root already to do this. */
+
+ return (pointer != NULL &&
+ pointer >= (void *) hba->private_data_pool &&
+ pointer < (void *) hba->private_data_pool +
+ sizeof(*hba->private_data_pool) *
+ CPQFC_MAX_PASSTHRU_CMDS);
+}
+
+cpqfc_passthru_private_t *cpqfc_alloc_private_data(CPQFCHBA *hba)
+{
+ int i;
+
+ do {
+ i = find_first_zero_bit(hba->private_data_bits,
+ CPQFC_MAX_PASSTHRU_CMDS);
+ if (i == CPQFC_MAX_PASSTHRU_CMDS)
+ return NULL;
+ } while ( test_and_set_bit(i & (BITS_PER_LONG - 1),
+ hba->private_data_bits+(i/BITS_PER_LONG)) != 0);
+ return &hba->private_data_pool[i];
+}
+
+void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data)
+{
+ int i;
+ i = data - hba->private_data_pool;
+ clear_bit(i&(BITS_PER_LONG-1),
+ hba->private_data_bits+(i/BITS_PER_LONG));
+}
+
+int cpqfcTS_ioctl( struct scsi_device *ScsiDev, int Cmnd, void *arg)
+{
+ int result = 0;
+ struct Scsi_Host *HostAdapter = ScsiDev->host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ PFC_LOGGEDIN_PORT pLoggedInPort = NULL;
+ struct scsi_cmnd *DumCmnd;
+ int i, j;
+ VENDOR_IOCTL_REQ ioc;
+ cpqfc_passthru_t *vendor_cmd;
+ Scsi_Device *SDpnt;
+ Scsi_Request *ScsiPassThruReq;
+ cpqfc_passthru_private_t *privatedata;
+
+ ENTER("cpqfcTS_ioctl ");
+
+ // printk("ioctl CMND %d", Cmnd);
+ switch (Cmnd) {
+ // Passthrough provides a mechanism to bypass the RAID
+ // or other controller and talk directly to the devices
+ // (e.g. physical disk drive)
+ // Passthrough commands, unfortunately, tend to be vendor
+ // specific; this is tailored to COMPAQ's RAID (RA4x00)
+ case CPQFCTS_SCSI_PASSTHRU:
+ {
+ void *buf = NULL; // for kernel space buffer for user data
+
+ /* Check that our pool got allocated ok. */
+ if (cpqfcHBAdata->private_data_pool == NULL)
+ return -ENOMEM;
+
+ if( !arg)
+ return -EINVAL;
+
+ // must be super user to send stuff directly to the
+ // controller and/or physical drives...
+ if( !capable(CAP_SYS_RAWIO) )
+ return -EPERM;
+
+ // copy the caller's struct to our space.
+ if( copy_from_user( &ioc, arg, sizeof( VENDOR_IOCTL_REQ)))
+ return( -EFAULT);
+
+ vendor_cmd = ioc.argp; // i.e., CPQ specific command struct
+
+ // If necessary, grab a kernel/DMA buffer
+ if( vendor_cmd->len)
+ {
+ buf = kmalloc( vendor_cmd->len, GFP_KERNEL);
+ if( !buf)
+ return -ENOMEM;
+ }
+ // Now build a Scsi_Request to pass down...
+ ScsiPassThruReq = scsi_allocate_request(ScsiDev, GFP_KERNEL);
+ if (ScsiPassThruReq == NULL) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+ ScsiPassThruReq->upper_private_data =
+ cpqfc_alloc_private_data(cpqfcHBAdata);
+ if (ScsiPassThruReq->upper_private_data == NULL) {
+ kfree(buf);
+ scsi_release_request(ScsiPassThruReq); // "de-allocate"
+ return -ENOMEM;
+ }
+
+ if (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) {
+ if (vendor_cmd->len) { // Need data from user?
+ if (copy_from_user(buf, vendor_cmd->bufp,
+ vendor_cmd->len)) {
+ kfree(buf);
+ cpqfc_free_private_data(cpqfcHBAdata,
+ ScsiPassThruReq->upper_private_data);
+ scsi_release_request(ScsiPassThruReq);
+ return( -EFAULT);
+ }
+ }
+ ScsiPassThruReq->sr_data_direction = SCSI_DATA_WRITE;
+ } else if (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) {
+ ScsiPassThruReq->sr_data_direction = SCSI_DATA_READ;
+ } else
+ // maybe this means a bug in the user app
+ ScsiPassThruReq->sr_data_direction = SCSI_DATA_NONE;
+
+ ScsiPassThruReq->sr_cmd_len = 0; // set correctly by scsi_do_req()
+ ScsiPassThruReq->sr_sense_buffer[0] = 0;
+ ScsiPassThruReq->sr_sense_buffer[2] = 0;
+
+ // We copy the scheme used by sd.c:spinup_disk() to submit commands
+ // to our own HBA. We do this in order to stall the
+ // thread calling the IOCTL until it completes, and use
+ // the same "_quecommand" function for synchronizing
+ // FC Link events with our "worker thread".
+
+ privatedata = ScsiPassThruReq->upper_private_data;
+ privatedata->bus = vendor_cmd->bus;
+ privatedata->pdrive = vendor_cmd->pdrive;
+
+ // eventually gets us to our own _quecommand routine
+ scsi_wait_req(ScsiPassThruReq,
+ &vendor_cmd->cdb[0], buf, vendor_cmd->len,
+ 10*HZ, // timeout
+ 1); // retries
+ result = ScsiPassThruReq->sr_result;
+
+ // copy any sense data back to caller
+ if( result != 0 )
+ {
+ memcpy( vendor_cmd->sense_data, // see struct def - size=40
+ ScsiPassThruReq->sr_sense_buffer,
+ sizeof(ScsiPassThruReq->sr_sense_buffer) <
+ sizeof(vendor_cmd->sense_data) ?
+ sizeof(ScsiPassThruReq->sr_sense_buffer) :
+ sizeof(vendor_cmd->sense_data)
+ );
+ }
+ SDpnt = ScsiPassThruReq->sr_device;
+ /* upper_private_data is already freed in call_scsi_done() */
+ scsi_release_request(ScsiPassThruReq); // "de-allocate"
+ ScsiPassThruReq = NULL;
+
+ // need to pass data back to user (space)?
+ if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) &&
+ vendor_cmd->len )
+ if( copy_to_user( vendor_cmd->bufp, buf, vendor_cmd->len))
+ result = -EFAULT;
+
+ if( buf)
+ kfree( buf);
+
+ return result;
+ }
+
+ case CPQFCTS_GETPCIINFO:
+ {
+ cpqfc_pci_info_struct pciinfo;
+
+ if( !arg)
+ return -EINVAL;
+
+
+
+ pciinfo.bus = cpqfcHBAdata->PciDev->bus->number;
+ pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn;
+ pciinfo.board_id = cpqfcHBAdata->PciDev->device |
+ (cpqfcHBAdata->PciDev->vendor <<16);
+
+ if(copy_to_user( arg, &pciinfo, sizeof(cpqfc_pci_info_struct)))
+ return( -EFAULT);
+ return 0;
+ }
+
+ case CPQFCTS_GETDRIVVER:
+ {
+ DriverVer_type DriverVer =
+ CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR);
+
+ if( !arg)
+ return -EINVAL;
+
+ if(copy_to_user( arg, &DriverVer, sizeof(DriverVer)))
+ return( -EFAULT);
+ return 0;
+ }
+
+
+
+ case CPQFC_IOCTL_FC_TARGET_ADDRESS:
+ // can we find an FC device mapping to this SCSI target?
+/* DumCmnd.channel = ScsiDev->channel; */ // For searching
+/* DumCmnd.target = ScsiDev->id; */
+/* DumCmnd.lun = ScsiDev->lun; */
+
+ DumCmnd = scsi_get_command (ScsiDev, GFP_KERNEL);
+ if (!DumCmnd)
+ return -ENOMEM;
+
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ DumCmnd, // search Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+ scsi_put_command (DumCmnd);
+ if (pLoggedInPort == NULL) {
+ result = -ENXIO;
+ break;
+ }
+ result = access_ok(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)) ? 0 : -EFAULT;
+ if (result) break;
+
+ put_user(pLoggedInPort->port_id,
+ &((Scsi_FCTargAddress *) arg)->host_port_id);
+
+ for( i=3,j=0; i>=0; i--) // copy the LOGIN port's WWN
+ put_user(pLoggedInPort->u.ucWWN[i],
+ &((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+ for( i=7; i>3; i--) // copy the LOGIN port's WWN
+ put_user(pLoggedInPort->u.ucWWN[i],
+ &((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+ break;
+
+
+ case CPQFC_IOCTL_FC_TDR:
+
+ result = cpqfcTS_TargetDeviceReset( ScsiDev, 0);
+
+ break;
+
+
+
+
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ LEAVE("cpqfcTS_ioctl");
+ return result;
+}
+
+
+/* "Release" the Host Bus Adapter...
+ disable interrupts, stop the HBA, release the interrupt,
+ and free all resources */
+
+int cpqfcTS_release(struct Scsi_Host *HostAdapter)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+
+ ENTER("cpqfcTS_release");
+
+ DEBUG_PCI( printk(" cpqfcTS: delete timer...\n"));
+ del_timer( &cpqfcHBAdata->cpqfcTStimer);
+
+ // disable the hardware...
+ DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n"));
+ cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS);
+
+ // kill kernel thread
+ if( cpqfcHBAdata->worker_thread ) // (only if exists)
+ {
+ DECLARE_MUTEX_LOCKED(sem); // synchronize thread kill
+
+ cpqfcHBAdata->notify_wt = &sem;
+ DEBUG_PCI( printk(" killing kernel thread\n"));
+ send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1);
+ down( &sem);
+ cpqfcHBAdata->notify_wt = NULL;
+
+ }
+
+ cpqfc_free_private_data_pool(cpqfcHBAdata);
+ // free Linux resources
+ DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n"));
+ free_irq( HostAdapter->irq, HostAdapter);
+ scsi_unregister( HostAdapter);
+ release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff);
+ release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff);
+ /* we get "vfree: bad address" executing this - need to investigate...
+ if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) !=
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase)
+ vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+*/
+ pci_disable_device( cpqfcHBAdata->PciDev);
+
+ LEAVE("cpqfcTS_release");
+ return 0;
+}
+
+
+const char * cpqfcTS_info(struct Scsi_Host *HostAdapter)
+{
+ static char buf[300];
+ CPQFCHBA *cpqfcHBA;
+ int BusSpeed, BusWidth;
+
+ // get the pointer to our Scsi layer HBA buffer
+ cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+
+ BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ?
+ 64 : 32;
+
+ if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000)
+ BusSpeed = 66;
+ else
+ BusSpeed = 33;
+
+ sprintf(buf,
+"%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d",
+ cpqfcHBA->fcChip.Name,
+ cpqfcHBA->fcChip.Registers.wwn_hi,
+ cpqfcHBA->fcChip.Registers.wwn_lo,
+ cpqfcHBA->PciDev->bus->number,
+ cpqfcHBA->PciDev->device,
+ HostAdapter->irq,
+ cpqfcHBA->fcChip.Registers.IOBaseL,
+ cpqfcHBA->fcChip.Registers.MemBase,
+ BusWidth,
+ BusSpeed,
+ VER_MAJOR, VER_MINOR, VER_SUBMINOR
+);
+
+
+ cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+ cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+ return buf;
+}
+
+//
+// /proc/scsi support. The following routines allow us to do 'normal'
+// sprintf like calls to return the currently requested piece (buflenght
+// chars, starting at bufoffset) of the file. Although procfs allows for
+// a 1 Kb bytes overflow after te supplied buffer, I consider it bad
+// programming to use it to make programming a little simpler. This piece
+// of coding is borrowed from ncr53c8xx.c with some modifications
+//
+struct info_str
+{
+ char *buffer; // Pointer to output buffer
+ int buflength; // It's length
+ int bufoffset; // File offset corresponding with buf[0]
+ int buffillen; // Current filled length
+ int filpos; // Current file offset
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int datalen)
+{
+
+ if (info->filpos < info->bufoffset) { // Current offset before buffer offset
+ if (info->filpos + datalen <= info->bufoffset) {
+ info->filpos += datalen; // Discard if completely before buffer
+ return;
+ } else { // Partial copy, set to begin
+ data += (info->bufoffset - info->filpos);
+ datalen -= (info->bufoffset - info->filpos);
+ info->filpos = info->bufoffset;
+ }
+ }
+
+ info->filpos += datalen; // Update current offset
+
+ if (info->buffillen == info->buflength) // Buffer full, discard
+ return;
+
+ if (info->buflength - info->buffillen < datalen) // Overflows buffer ?
+ datalen = info->buflength - info->buffillen;
+
+ memcpy(info->buffer + info->buffillen, data, datalen);
+ info->buffillen += datalen;
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[400];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+
+// Routine to get data for /proc RAM filesystem
+//
+int cpqfcTS_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length,
+ int inout)
+{
+ struct scsi_cmnd *DumCmnd;
+ struct scsi_device *ScsiDev;
+ int Chan, Targ, i;
+ struct info_str info;
+ CPQFCHBA *cpqfcHBA;
+ PTACHYON fcChip;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ char buf[81];
+
+ if (inout) return -EINVAL;
+
+ // get the pointer to our Scsi layer HBA buffer
+ cpqfcHBA = (CPQFCHBA *)host->hostdata;
+ fcChip = &cpqfcHBA->fcChip;
+
+ *start = buffer;
+
+ info.buffer = buffer;
+ info.buflength = length;
+ info.bufoffset = offset;
+ info.filpos = 0;
+ info.buffillen = 0;
+ copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR);
+ cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]);
+ cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+ copy_info(&info, "%s\n", buf);
+
+#define DISPLAY_WWN_INFO
+#ifdef DISPLAY_WWN_INFO
+ ScsiDev = scsi_get_host_dev (host);
+ if (!ScsiDev)
+ return -ENOMEM;
+ DumCmnd = scsi_get_command (ScsiDev, GFP_KERNEL);
+ if (!DumCmnd) {
+ scsi_free_host_dev (ScsiDev);
+ return -ENOMEM;
+ }
+ copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n");
+ for ( Chan=0; Chan <= host->max_channel; Chan++) {
+ DumCmnd->device->channel = Chan;
+ for (Targ=0; Targ <= host->max_id; Targ++) {
+ DumCmnd->device->id = Targ;
+ if ((pLoggedInPort = fcFindLoggedInPort( fcChip,
+ DumCmnd, // search Scsi Nexus
+ 0, // DON'T search list for FC port id
+ NULL, // DON'T search list for FC WWN
+ NULL))){ // DON'T care about end of list
+ copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ",
+ host->host_no, Chan, Targ);
+ for( i=3; i>=0; i--) // copy the LOGIN port's WWN
+ copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+ for( i=7; i>3; i--) // copy the LOGIN port's WWN
+ copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+ copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id);
+ }
+ }
+ }
+
+ scsi_put_command (DumCmnd);
+ scsi_free_host_dev (ScsiDev);
+#endif
+
+
+
+
+
+// Unfortunately, the proc_info buffer isn't big enough
+// for everything we would like...
+// For FC stats, compile this and turn off WWN stuff above
+//#define DISPLAY_FC_STATS
+#ifdef DISPLAY_FC_STATS
+// get the Fibre Channel statistics
+ {
+ int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ;
+ int days,hours,minutes,secs;
+
+ days = DeltaSecs / (3600*24); // days
+ hours = (DeltaSecs% (3600*24)) / 3600; // hours
+ minutes = (DeltaSecs%3600 /60); // minutes
+ secs = DeltaSecs%60; // secs
+copy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n",
+ days, hours, minutes, secs);
+ }
+
+ cpqfcHBA->fcStatsTime = jiffies; // (for next delta)
+
+ copy_info( &info, " LinkUp %9u LinkDown %u\n",
+ fcChip->fcStats.linkUp, fcChip->fcStats.linkDown);
+
+ copy_info( &info, " Loss of Signal %9u Loss of Sync %u\n",
+ fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync);
+
+ copy_info( &info, " Discarded Frames %9u Bad CRC Frame %u\n",
+ fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC);
+
+ copy_info( &info, " TACH LinkFailTX %9u TACH LinkFailRX %u\n",
+ fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX);
+
+ copy_info( &info, " TACH RxEOFa %9u TACH Elastic Store %u\n",
+ fcChip->fcStats.Rx_EOFa, fcChip->fcStats.e_stores);
+
+ copy_info( &info, " BufferCreditWait %9uus TACH FM Inits %u\n",
+ fcChip->fcStats.BB0_Timer*10, fcChip->fcStats.FMinits );
+
+ copy_info( &info, " FC-2 Timeouts %9u FC-2 Logouts %u\n",
+ fcChip->fcStats.timeouts, fcChip->fcStats.logouts);
+
+ copy_info( &info, " FC-2 Aborts %9u FC-4 Aborts %u\n",
+ fcChip->fcStats.FC2aborted, fcChip->fcStats.FC4aborted);
+
+ // clear the counters
+ cpqfcTSClearLinkStatusCounters( fcChip);
+#endif
+
+ return info.buffillen;
+}
+
+
+#if DEBUG_CMND
+
+UCHAR *ScsiToAscii( UCHAR ScsiCommand)
+{
+
+/*++
+
+Routine Description:
+
+ Converts a SCSI command to a text string for debugging purposes.
+
+
+Arguments:
+
+ ScsiCommand -- hex value SCSI Command
+
+
+Return Value:
+
+ An ASCII, null-terminated string if found, else returns NULL.
+
+Original code from M. McGowen, Compaq
+--*/
+
+
+ switch (ScsiCommand)
+ {
+ case 0x00:
+ return( "Test Unit Ready" );
+
+ case 0x01:
+ return( "Rezero Unit or Rewind" );
+
+ case 0x02:
+ return( "Request Block Address" );
+
+ case 0x03:
+ return( "Requese Sense" );
+
+ case 0x04:
+ return( "Format Unit" );
+
+ case 0x05:
+ return( "Read Block Limits" );
+
+ case 0x07:
+ return( "Reassign Blocks" );
+
+ case 0x08:
+ return( "Read (6)" );
+
+ case 0x0a:
+ return( "Write (6)" );
+
+ case 0x0b:
+ return( "Seek (6)" );
+
+ case 0x12:
+ return( "Inquiry" );
+
+ case 0x15:
+ return( "Mode Select (6)" );
+
+ case 0x16:
+ return( "Reserve" );
+
+ case 0x17:
+ return( "Release" );
+
+ case 0x1a:
+ return( "ModeSen(6)" );
+
+ case 0x1b:
+ return( "Start/Stop Unit" );
+
+ case 0x1c:
+ return( "Receive Diagnostic Results" );
+
+ case 0x1d:
+ return( "Send Diagnostic" );
+
+ case 0x25:
+ return( "Read Capacity" );
+
+ case 0x28:
+ return( "Read (10)" );
+
+ case 0x2a:
+ return( "Write (10)" );
+
+ case 0x2b:
+ return( "Seek (10)" );
+
+ case 0x2e:
+ return( "Write and Verify" );
+
+ case 0x2f:
+ return( "Verify" );
+
+ case 0x34:
+ return( "Pre-Fetch" );
+
+ case 0x35:
+ return( "Synchronize Cache" );
+
+ case 0x37:
+ return( "Read Defect Data (10)" );
+
+ case 0x3b:
+ return( "Write Buffer" );
+
+ case 0x3c:
+ return( "Read Buffer" );
+
+ case 0x3e:
+ return( "Read Long" );
+
+ case 0x3f:
+ return( "Write Long" );
+
+ case 0x41:
+ return( "Write Same" );
+
+ case 0x4c:
+ return( "Log Select" );
+
+ case 0x4d:
+ return( "Log Sense" );
+
+ case 0x56:
+ return( "Reserve (10)" );
+
+ case 0x57:
+ return( "Release (10)" );
+
+ case 0xa0:
+ return( "ReportLuns" );
+
+ case 0xb7:
+ return( "Read Defect Data (12)" );
+
+ case 0xca:
+ return( "Peripheral Device Addressing SCSI Passthrough" );
+
+ case 0xcb:
+ return( "Compaq Array Firmware Passthrough" );
+
+ default:
+ return( NULL );
+ }
+
+} // end ScsiToAscii()
+
+void cpqfcTS_print_scsi_cmd(Scsi_Cmnd * cmd)
+{
+
+printk("cpqfcTS: (%s) chnl 0x%02x, trgt = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
+ ScsiToAscii( cmd->cmnd[0]), cmd->channel, cmd->target, cmd->lun, cmd->cmd_len);
+
+if( cmd->cmnd[0] == 0) // Test Unit Ready?
+{
+ int i;
+
+ printk("Cmnd->request_bufflen = 0x%X, ->use_sg = %d, ->bufflen = %d\n",
+ cmd->request_bufflen, cmd->use_sg, cmd->bufflen);
+ printk("Cmnd->request_buffer = %p, ->sglist_len = %d, ->buffer = %p\n",
+ cmd->request_buffer, cmd->sglist_len, cmd->buffer);
+ for (i = 0; i < cmd->cmd_len; i++)
+ printk("0x%02x ", cmd->cmnd[i]);
+ printk("\n");
+}
+
+}
+
+#endif /* DEBUG_CMND */
+
+
+
+
+static void QueCmndOnBoardLock( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+ int i;
+
+ for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+ { // find spare slot
+ if( cpqfcHBAdata->BoardLockCmnd[i] == NULL )
+ {
+ cpqfcHBAdata->BoardLockCmnd[i] = Cmnd;
+// printk(" BoardLockCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+ break;
+ }
+ }
+ if( i >= CPQFCTS_REQ_QUEUE_LEN)
+ {
+ printk(" cpqfcTS WARNING: Lost Cmnd %p on BoardLock Q full!", Cmnd);
+ }
+
+}
+
+
+static void QueLinkDownCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+ int indx;
+
+ // Remember the command ptr so we can return; we'll complete when
+ // the device comes back, causing immediate retry
+ for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++)
+ {
+ if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available?
+ {
+#ifdef DUMMYCMND_DBG
+ printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx);
+#endif
+ cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd;
+ break;
+ }
+ }
+
+ if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd??
+ {
+ // this will result in an _abort call later (with possible trouble)
+ printk("no buffer for LinkDnCmnd!! %p\n", Cmnd);
+ }
+}
+
+
+
+
+
+// The file <scsi/scsi_host.h> says not to call scsi_done from
+// inside _queuecommand, so we'll do it from the heartbeat timer
+// (clarification: Turns out it's ok to call scsi_done from queuecommand
+// for cases that don't go to the hardware like scsi cmds destined
+// for LUNs we know don't exist, so this code might be simplified...)
+
+static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+ int i;
+ // printk(" can't find target %d\n", Cmnd->target);
+
+ for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+ { // find spare slot
+ if( cpqfcHBAdata->BadTargetCmnd[i] == NULL )
+ {
+ cpqfcHBAdata->BadTargetCmnd[i] = Cmnd;
+// printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+ break;
+ }
+ }
+}
+
+
+// This is the "main" entry point for Linux Scsi commands --
+// it all starts here.
+
+int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *HostAdapter = Cmnd->device->host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ TachFCHDR_GCMND fchs; // only use for FC destination id field
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ ULONG ulStatus, SESTtype;
+ LONG ExchangeID;
+
+
+
+
+ ENTER("cpqfcTS_queuecommand");
+
+ PCI_TRACEO( (ULONG)Cmnd, 0x98)
+
+
+ Cmnd->scsi_done = done;
+#ifdef DEBUG_CMND
+ cpqfcTS_print_scsi_cmd( Cmnd);
+#endif
+
+ // prevent board contention with kernel thread...
+
+ if( cpqfcHBAdata->BoardLock )
+ {
+// printk(" @BrdLck Hld@ ");
+ QueCmndOnBoardLock( cpqfcHBAdata, Cmnd);
+ }
+
+ else
+ {
+
+ // in the current system (2.2.12), this routine is called
+ // after spin_lock_irqsave(), so INTs are disabled. However,
+ // we might have something pending in the LinkQ, which
+ // might cause the WorkerTask to run. In case that
+ // happens, make sure we lock it out.
+
+
+
+ PCI_TRACE( 0x98)
+ CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+ PCI_TRACE( 0x98)
+
+ // can we find an FC device mapping to this SCSI target?
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ Cmnd, // search Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort == NULL ) // not found!
+ {
+// printk(" @Q bad targ cmnd %p@ ", Cmnd);
+ QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+ }
+ else if (Cmnd->device->lun >= CPQFCTS_MAX_LUN)
+ {
+ printk(KERN_WARNING "cpqfc: Invalid LUN: %d\n", Cmnd->device->lun);
+ QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+ }
+
+ else // we know what FC device to send to...
+ {
+
+ // does this device support FCP target functions?
+ // (determined by PRLI field)
+
+ if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+ {
+ printk(" Doesn't support TARGET functions port_id %Xh\n",
+ pLoggedInPort->port_id );
+ QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+ }
+
+ // In this case (previous login OK), the device is temporarily
+ // unavailable waiting for re-login, in which case we expect it
+ // to be back in between 25 - 500ms.
+ // If the FC port doesn't log back in within several seconds
+ // (i.e. implicit "logout"), or we get an explicit logout,
+ // we set "device_blocked" in Scsi_Device struct; in this
+ // case 30 seconds will elapse before Linux/Scsi sends another
+ // command to the device.
+ else if( pLoggedInPort->prli != TRUE )
+ {
+// printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n",
+// Cmnd->channel, Cmnd->target, pLoggedInPort->port_id);
+ QueLinkDownCmnd( cpqfcHBAdata, Cmnd);
+// Need to use "blocked" flag??
+// Cmnd->device->device_blocked = TRUE; // just let it timeout
+ }
+ else // device supports TARGET functions, and is logged in...
+ {
+ // (context of fchs is to "reply" to...)
+ fchs.s_id = pLoggedInPort->port_id; // destination FC address
+
+ // what is the data direction? For data TO the device,
+ // we need IWE (Intiator Write Entry). Otherwise, IRE.
+
+ if( Cmnd->cmnd[0] == WRITE_10 ||
+ Cmnd->cmnd[0] == WRITE_6 ||
+ Cmnd->cmnd[0] == WRITE_BUFFER ||
+ Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE || // CPQ specific
+ Cmnd->cmnd[0] == MODE_SELECT )
+ {
+ SESTtype = SCSI_IWE; // data from HBA to Device
+ }
+ else
+ SESTtype = SCSI_IRE; // data from Device to HBA
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ SESTtype, // e.g. Initiator Read Entry (IRE)
+ &fchs, // we are originator; only use d_id
+ Cmnd, // Linux SCSI command (with scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+
+ {
+ if( cpqfcHBAdata->BoardLock )
+ {
+ TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+ printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID);
+ }
+
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ PCI_TRACEO( ExchangeID, 0xB8)
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+ printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus );
+ }
+ } // end good BuildExchange status
+
+ else // SEST table probably full -- why? hardware hang?
+ {
+ printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus);
+ }
+ } // end can't do FCP-SCSI target functions
+ } // end can't find target (FC device)
+
+ CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+ }
+
+ PCI_TRACEO( (ULONG)Cmnd, 0x9C)
+ LEAVE("cpqfcTS_queuecommand");
+ return 0;
+}
+
+
+// Entry point for upper Scsi layer intiated abort. Typically
+// this is called if the command (for hard disk) fails to complete
+// in 30 seconds. This driver intends to complete all disk commands
+// within Exchange ".timeOut" seconds (now 7) with target status, or
+// in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes
+// immediate retry.
+// If any disk commands get the _abort call, except for the case that
+// the physical device was removed or unavailable due to hardware
+// errors, it should be considered a driver error and reported to
+// the author.
+
+int cpqfcTS_abort(Scsi_Cmnd *Cmnd)
+{
+// printk(" cpqfcTS_abort called?? \n");
+ return 0;
+}
+
+int cpqfcTS_eh_abort(Scsi_Cmnd *Cmnd)
+{
+
+ struct Scsi_Host *HostAdapter = Cmnd->device->host;
+ // get the pointer to our Scsi layer HBA buffer
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ int i;
+ ENTER("cpqfcTS_eh_abort");
+
+ Cmnd->result = DID_ABORT <<16; // assume we'll find it
+
+ printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd);
+ // See if we can find a Cmnd pointer that matches...
+ // The most likely case is we accepted the command
+ // from Linux Scsi (e.g. ceated a SEST entry) and it
+ // got lost somehow. If we can't find any reference
+ // to the passed pointer, we can only presume it
+ // got completed as far as our driver is concerned.
+ // If we found it, we will try to abort it through
+ // common mechanism. If FC ABTS is successful (ACC)
+ // or is rejected (RJT) by target, we will call
+ // Scsi "done" quickly. Otherwise, the ABTS will timeout
+ // and we'll call "done" later.
+
+ // Search the SEST exchanges for a matching Cmnd ptr.
+ for( i=0; i< TACH_SEST_LEN; i++)
+ {
+ if( Exchanges->fcExchange[i].Cmnd == Cmnd )
+ {
+
+ // found it!
+ printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type);
+
+ Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default
+ Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later)
+
+ // Since we need to immediately return the aborted Cmnd to Scsi
+ // upper layers, we can't make future reference to any of its
+ // fields (e.g the Nexus).
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i);
+
+ break;
+ }
+ }
+
+ if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST?
+ {
+ // now search our non-SEST buffers (i.e. Cmnd waiting to
+ // start on the HBA or waiting to complete with error for retry).
+
+ // first check BadTargetCmnd
+ for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+ {
+ if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd )
+ {
+ cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+ printk("in BadTargetCmnd Q\n");
+ goto Done; // exit
+ }
+ }
+
+ // if not found above...
+
+ for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++)
+ {
+ if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd )
+ {
+ cpqfcHBAdata->LinkDnCmnd[i] = NULL;
+ printk("in LinkDnCmnd Q\n");
+ goto Done;
+ }
+ }
+
+
+ for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+ { // find spare slot
+ if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd )
+ {
+ cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+ printk("in BoardLockCmnd Q\n");
+ goto Done;
+ }
+ }
+
+ Cmnd->result = DID_ERROR <<16; // Hmmm...
+ printk("Not found! ");
+// panic("_abort");
+ }
+
+Done:
+
+// panic("_abort");
+ LEAVE("cpqfcTS_eh_abort");
+ return 0; // (see scsi.h)
+}
+
+
+// FCP-SCSI Target Device Reset
+// See dpANS Fibre Channel Protocol for SCSI
+// X3.269-199X revision 12, pg 25
+
+#ifdef SUPPORT_RESET
+
+int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev,
+ unsigned int reset_flags)
+{
+ int timeout = 10*HZ;
+ int retries = 1;
+ char scsi_cdb[12];
+ int result;
+ Scsi_Cmnd * SCpnt;
+ Scsi_Device * SDpnt;
+
+// FIXME, cpqfcTS_TargetDeviceReset needs to be fixed
+// similarly to how the passthrough ioctl was fixed
+// around the 2.5.30 kernel. Scsi_Cmnd replaced with
+// Scsi_Request, etc.
+// For now, so people don't fall into a hole...
+
+ // printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags);
+
+ if (ScsiDev->host->eh_active) return FAILED;
+
+ memset( scsi_cdb, 0, sizeof( scsi_cdb));
+
+ scsi_cdb[0] = RELEASE;
+
+ SCpnt = scsi_get_command(ScsiDev, GFP_KERNEL);
+ {
+ CPQFC_DECLARE_COMPLETION(wait);
+
+ SCpnt->SCp.buffers_residual = FCP_TARGET_RESET;
+
+ // FIXME: this would panic, SCpnt->request would be NULL.
+ SCpnt->request->CPQFC_WAITING = &wait;
+ scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries);
+ CPQFC_WAIT_FOR_COMPLETION(&wait);
+ SCpnt->request->CPQFC_WAITING = NULL;
+ }
+
+
+ if(driver_byte(SCpnt->result) != 0)
+ switch(SCpnt->sense_buffer[2] & 0xf) {
+ case ILLEGAL_REQUEST:
+ if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0;
+ else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
+ break;
+ case NOT_READY: // This happens if there is no disc in drive
+ if(dev->removable && (cmd[0] != TEST_UNIT_READY)){
+ printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n");
+ break;
+ }
+ case UNIT_ATTENTION:
+ if (dev->removable){
+ dev->changed = 1;
+ SCpnt->result = 0; // This is no longer considered an error
+ // gag this error, VFS will log it anyway /axboe
+ // printk(KERN_INFO "Disc change detected.\n");
+ break;
+ };
+ default: // Fall through for non-removable media
+ printk("SCSI error: host %d id %d lun %d return code = %x\n",
+ dev->host->host_no,
+ dev->id,
+ dev->lun,
+ SCpnt->result);
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(SCpnt->sense_buffer[0]),
+ sense_error(SCpnt->sense_buffer[0]),
+ SCpnt->sense_buffer[2] & 0xf);
+
+ };
+ result = SCpnt->result;
+
+ SDpnt = SCpnt->device;
+ scsi_put_command(SCpnt);
+ SCpnt = NULL;
+
+ // printk(" LEAVING cpqfcTS_TargetDeviceReset() - return SUCCESS \n");
+ return SUCCESS;
+}
+
+#else
+int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev,
+ unsigned int reset_flags)
+{
+ return -ENOTSUPP;
+}
+
+#endif /* SUPPORT_RESET */
+
+int cpqfcTS_eh_device_reset(Scsi_Cmnd *Cmnd)
+{
+ int retval;
+ Scsi_Device *SDpnt = Cmnd->device;
+ // printk(" ENTERING cpqfcTS_eh_device_reset() \n");
+ spin_unlock_irq(Cmnd->device->host->host_lock);
+ retval = cpqfcTS_TargetDeviceReset( SDpnt, 0);
+ spin_lock_irq(Cmnd->device->host->host_lock);
+ return retval;
+}
+
+
+int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
+{
+
+ ENTER("cpqfcTS_reset");
+
+ LEAVE("cpqfcTS_reset");
+ return SCSI_RESET_ERROR; /* Bus Reset Not supported */
+}
+
+/* This function determines the bios parameters for a given
+ harddisk. These tend to be numbers that are made up by the
+ host adapter. Parameters:
+ size, device number, list (heads, sectors,cylinders).
+ (from hosts.h)
+*/
+
+int cpqfcTS_biosparam(struct scsi_device *sdev, struct block_device *n,
+ sector_t capacity, int ip[])
+{
+ int size = capacity;
+
+ ENTER("cpqfcTS_biosparam");
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+
+ if( ip[2] > 1024 )
+ {
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / (ip[0] * ip[1]);
+ }
+
+ LEAVE("cpqfcTS_biosparam");
+ return 0;
+}
+
+
+
+irqreturn_t cpqfcTS_intr_handler( int irq,
+ void *dev_id,
+ struct pt_regs *regs)
+{
+
+ unsigned long flags, InfLoopBrk=0;
+ struct Scsi_Host *HostAdapter = dev_id;
+ CPQFCHBA *cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+ int MoreMessages = 1; // assume we have something to do
+ UCHAR IntPending;
+ int handled = 0;
+
+ ENTER("intr_handler");
+ spin_lock_irqsave( HostAdapter->host_lock, flags);
+ // is this our INT?
+ IntPending = readb( cpqfcHBA->fcChip.Registers.INTPEND.address);
+
+ // broken boards can generate messages forever, so
+ // prevent the infinite loop
+#define INFINITE_IMQ_BREAK 10000
+ if( IntPending )
+ {
+ handled = 1;
+ // mask our HBA interrupts until we handle it...
+ writeb( 0, cpqfcHBA->fcChip.Registers.INTEN.address);
+
+ if( IntPending & 0x4) // "INT" - Tach wrote to IMQ
+ {
+ while( (++InfLoopBrk < INFINITE_IMQ_BREAK) && (MoreMessages ==1) )
+ {
+ MoreMessages = CpqTsProcessIMQEntry( HostAdapter); // ret 0 when done
+ }
+ if( InfLoopBrk >= INFINITE_IMQ_BREAK )
+ {
+ printk("WARNING: Compaq FC adapter generating excessive INTs -REPLACE\n");
+ printk("or investigate alternate causes (e.g. physical FC layer)\n");
+ }
+
+ else // working normally - re-enable INTs and continue
+ writeb( 0x1F, cpqfcHBA->fcChip.Registers.INTEN.address);
+
+ } // (...ProcessIMQEntry() clears INT by writing IMQ consumer)
+ else // indications of errors or problems...
+ // these usually indicate critical system hardware problems.
+ {
+ if( IntPending & 0x10 )
+ printk(" cpqfcTS adapter external memory parity error detected\n");
+ if( IntPending & 0x8 )
+ printk(" cpqfcTS adapter PCI master address crossed 45-bit boundary\n");
+ if( IntPending & 0x2 )
+ printk(" cpqfcTS adapter DMA error detected\n");
+ if( IntPending & 0x1 ) {
+ UCHAR IntStat;
+ printk(" cpqfcTS adapter PCI error detected\n");
+ IntStat = readb( cpqfcHBA->fcChip.Registers.INTSTAT.address);
+ printk("cpqfc: ISR = 0x%02x\n", IntStat);
+ if (IntStat & 0x1) {
+ __u16 pcistat;
+ /* read the pci status register */
+ pci_read_config_word(cpqfcHBA->PciDev, 0x06, &pcistat);
+ printk("PCI status register is 0x%04x\n", pcistat);
+ if (pcistat & 0x8000) printk("Parity Error Detected.\n");
+ if (pcistat & 0x4000) printk("Signalled System Error\n");
+ if (pcistat & 0x2000) printk("Received Master Abort\n");
+ if (pcistat & 0x1000) printk("Received Target Abort\n");
+ if (pcistat & 0x0800) printk("Signalled Target Abort\n");
+ }
+ if (IntStat & 0x4) printk("(INT)\n");
+ if (IntStat & 0x8)
+ printk("CRS: PCI master address crossed 46 bit bouandary\n");
+ if (IntStat & 0x10) printk("MRE: external memory parity error.\n");
+ }
+ }
+ }
+ spin_unlock_irqrestore( HostAdapter->host_lock, flags);
+ LEAVE("intr_handler");
+ return IRQ_RETVAL(handled);
+}
+
+
+
+
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[])
+{
+ // Verify GBIC type (if any) and correct Tachyon Port State Machine
+ // (GBIC) module definition is:
+ // GPIO1, GPIO0, GPIO4 for MD2, MD1, MD0. The input states appear
+ // to be inverted -- i.e., a setting of 111 is read when there is NO
+ // GBIC present. The Module Def (MD) spec says 000 is "no GBIC"
+ // Hard code the bit states to detect Copper,
+ // Long wave (single mode), Short wave (multi-mode), and absent GBIC
+
+ ULONG ulBuff;
+
+ sprintf( cErrorString, "\nGBIC detected: ");
+
+ ulBuff = fcChip->Registers.TYstatus.value & 0x13;
+ switch( ulBuff )
+ {
+ case 0x13: // GPIO4, GPIO1, GPIO0 = 111; no GBIC!
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "NONE! ");
+ return FALSE;
+
+
+ case 0x11: // Copper GBIC detected
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Copper. ");
+ break;
+
+ case 0x10: // Long-wave (single mode) GBIC detected
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Long-wave. ");
+ break;
+ case 0x1: // Short-wave (multi mode) GBIC detected
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Short-wave. ");
+ break;
+ default: // unknown GBIC - presumably it will work (?)
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Unknown. ");
+
+ break;
+ } // end switch GBIC detection
+
+ return TRUE;
+}
+
+
+
+
+
+
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[])
+{
+ // Tachyon's Frame Manager LPSM in LinkDown state?
+ // (For non-loop port, check PSM instead.)
+ // return string with state and FALSE is Link Down
+
+ int LinkUp;
+
+ if( fcChip->Registers.FMstatus.value & 0x80 )
+ LinkUp = FALSE;
+ else
+ LinkUp = TRUE;
+
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ " LPSM %Xh ",
+ (fcChip->Registers.FMstatus.value >>4) & 0xf );
+
+
+ switch( fcChip->Registers.FMstatus.value & 0xF0)
+ {
+ // bits set in LPSM
+ case 0x10:
+ sprintf( &cErrorString[ strlen( cErrorString)], "ARB");
+ break;
+ case 0x20:
+ sprintf( &cErrorString[ strlen( cErrorString)], "ARBwon");
+ break;
+ case 0x30:
+ sprintf( &cErrorString[ strlen( cErrorString)], "OPEN");
+ break;
+ case 0x40:
+ sprintf( &cErrorString[ strlen( cErrorString)], "OPENed");
+ break;
+ case 0x50:
+ sprintf( &cErrorString[ strlen( cErrorString)], "XmitCLS");
+ break;
+ case 0x60:
+ sprintf( &cErrorString[ strlen( cErrorString)], "RxCLS");
+ break;
+ case 0x70:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Xfer");
+ break;
+ case 0x80:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Init");
+ break;
+ case 0x90:
+ sprintf( &cErrorString[ strlen( cErrorString)], "O-IInitFin");
+ break;
+ case 0xa0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "O-IProtocol");
+ break;
+ case 0xb0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "O-ILipRcvd");
+ break;
+ case 0xc0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "HostControl");
+ break;
+ case 0xd0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "LoopFail");
+ break;
+ case 0xe0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Offline");
+ break;
+ case 0xf0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "OldPort");
+ break;
+ case 0:
+ default:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Monitor");
+ break;
+
+ }
+
+ return LinkUp;
+}
+
+
+
+
+#include "linux/slab.h"
+
+// Dynamic memory allocation alignment routines
+// HP's Tachyon Fibre Channel Controller chips require
+// certain memory queues and register pointers to be aligned
+// on various boundaries, usually the size of the Queue in question.
+// Alignment might be on 2, 4, 8, ... or even 512 byte boundaries.
+// Since most O/Ss don't allow this (usually only Cache aligned -
+// 32-byte boundary), these routines provide generic alignment (after
+// O/S allocation) at any boundary, and store the original allocated
+// pointer for deletion (O/S free function). Typically, we expect
+// these functions to only be called at HBA initialization and
+// removal time (load and unload times)
+// ALGORITHM notes:
+// Memory allocation varies by compiler and platform. In the worst case,
+// we are only assured BYTE alignment, but in the best case, we can
+// request allocation on any desired boundary. Our strategy: pad the
+// allocation request size (i.e. waste memory) so that we are assured
+// of passing desired boundary near beginning of contiguous space, then
+// mask out lower address bits.
+// We define the following algorithm:
+// allocBoundary - compiler/platform specific address alignment
+// in number of bytes (default is single byte; i.e. 1)
+// n_alloc - number of bytes application wants @ aligned address
+// ab - alignment boundary, in bytes (e.g. 4, 32, ...)
+// t_alloc - total allocation needed to ensure desired boundary
+// mask - to clear least significant address bits for boundary
+// Compute:
+// t_alloc = n_alloc + (ab - allocBoundary)
+// allocate t_alloc bytes @ alloc_address
+// mask = NOT (ab - 1)
+// (e.g. if ab=32 _0001 1111 -> _1110 0000
+// aligned_address = alloc_address & mask
+// set n_alloc bytes to 0
+// return aligned_address (NULL if failed)
+//
+// If u32_AlignedAddress is non-zero, then search for BaseAddress (stored
+// from previous allocation). If found, invoke call to FREE the memory.
+// Return NULL if BaseAddress not found
+
+// we need about 8 allocations per HBA. Figuring at most 10 HBAs per server
+// size the dynamic_mem array at 80.
+
+void* fcMemManager( struct pci_dev *pdev, ALIGNED_MEM *dynamic_mem,
+ ULONG n_alloc, ULONG ab, ULONG u32_AlignedAddress,
+ dma_addr_t *dma_handle)
+{
+ USHORT allocBoundary=1; // compiler specific - worst case 1
+ // best case - replace malloc() call
+ // with function that allocates exactly
+ // at desired boundary
+
+ unsigned long ulAddress;
+ ULONG t_alloc, i;
+ void *alloc_address = 0; // def. error code / address not found
+ LONG mask; // must be 32-bits wide!
+
+ ENTER("fcMemManager");
+ if( u32_AlignedAddress ) // are we freeing existing memory?
+ {
+// printk(" freeing AlignedAddress %Xh\n", u32_AlignedAddress);
+ for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for the base address
+ {
+// printk("dynamic_mem[%u].AlignedAddress %lX\n", i, dynamic_mem[i].AlignedAddress);
+ if( dynamic_mem[i].AlignedAddress == u32_AlignedAddress )
+ {
+ alloc_address = dynamic_mem[i].BaseAllocated; // 'success' status
+ pci_free_consistent(pdev,dynamic_mem[i].size,
+ alloc_address,
+ dynamic_mem[i].dma_handle);
+ dynamic_mem[i].BaseAllocated = 0; // clear for next use
+ dynamic_mem[i].AlignedAddress = 0;
+ dynamic_mem[i].size = 0;
+ break; // quit for loop; done
+ }
+ }
+ }
+ else if( n_alloc ) // want new memory?
+ {
+ dma_addr_t handle;
+ t_alloc = n_alloc + (ab - allocBoundary); // pad bytes for alignment
+// printk("pci_alloc_consistent() for Tach alignment: %ld bytes\n", t_alloc);
+
+// (would like to) allow thread block to free pages
+ alloc_address = // total bytes (NumberOfBytes)
+ pci_alloc_consistent(pdev, t_alloc, &handle);
+
+ // now mask off least sig. bits of address
+ if( alloc_address ) // (only if non-NULL)
+ {
+ // find place to store ptr, so we
+ // can free it later...
+
+ mask = (LONG)(ab - 1); // mask all low-order bits
+ mask = ~mask; // invert bits
+ for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for free slot
+ {
+ if( dynamic_mem[i].BaseAllocated == 0) // take 1st available
+ {
+ dynamic_mem[i].BaseAllocated = alloc_address;// address from O/S
+ dynamic_mem[i].dma_handle = handle;
+ if (dma_handle != NULL)
+ {
+// printk("handle = %p, ab=%d, boundary = %d, mask=0x%08x\n",
+// handle, ab, allocBoundary, mask);
+ *dma_handle = (dma_addr_t)
+ ((((ULONG)handle) + (ab - allocBoundary)) & mask);
+ }
+ dynamic_mem[i].size = t_alloc;
+ break;
+ }
+ }
+ ulAddress = (unsigned long)alloc_address;
+
+ ulAddress += (ab - allocBoundary); // add the alignment bytes-
+ // then truncate address...
+ alloc_address = (void*)(ulAddress & mask);
+
+ dynamic_mem[i].AlignedAddress =
+ (ULONG)(ulAddress & mask); // 32bit Tach address
+ memset( alloc_address, 0, n_alloc ); // clear new memory
+ }
+ else // O/S dynamic mem alloc failed!
+ alloc_address = 0; // (for debugging breakpt)
+
+ }
+
+ LEAVE("fcMemManager");
+ return alloc_address; // good (or NULL) address
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .detect = cpqfcTS_detect,
+ .release = cpqfcTS_release,
+ .info = cpqfcTS_info,
+ .proc_info = cpqfcTS_proc_info,
+ .ioctl = cpqfcTS_ioctl,
+ .queuecommand = cpqfcTS_queuecommand,
+ .eh_device_reset_handler = cpqfcTS_eh_device_reset,
+ .eh_abort_handler = cpqfcTS_eh_abort,
+ .bios_param = cpqfcTS_biosparam,
+ .can_queue = CPQFCTS_REQ_QUEUE_LEN,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CPQFCTS_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
+
diff --git a/drivers/scsi/cpqfcTSioctl.h b/drivers/scsi/cpqfcTSioctl.h
new file mode 100644
index 000000000000..825536969126
--- /dev/null
+++ b/drivers/scsi/cpqfcTSioctl.h
@@ -0,0 +1,94 @@
+// for user apps, make sure data size types are defined
+// with
+
+
+#define CCPQFCTS_IOC_MAGIC 'Z'
+
+typedef struct
+{
+ __u8 bus;
+ __u8 dev_fn;
+ __u32 board_id;
+} cpqfc_pci_info_struct;
+
+typedef __u32 DriverVer_type;
+/*
+typedef union
+{
+ struct // Peripheral Unit Device
+ {
+ __u8 Bus:6;
+ __u8 Mode:2; // b00
+ __u8 Dev;
+ } PeripDev;
+ struct // Volume Set Address
+ {
+ __u8 DevMSB:6;
+ __u8 Mode:2; // b01
+ __u8 DevLSB;
+ } LogDev;
+ struct // Logical Unit Device (SCSI-3, SCC-2 defined)
+ {
+ __u8 Targ:6;
+ __u8 Mode:2; // b10
+ __u8 Dev:5;
+ __u8 Bus:3;
+
+ } LogUnit;
+} SCSI3Addr_struct;
+
+
+typedef struct
+{
+ SCSI3Addr_struct FCP_Nexus;
+ __u8 cdb[16];
+} PassThru_Command_struct;
+*/
+
+/* this is nearly duplicated in idashare.h */
+typedef struct {
+ int lc; /* Controller number */
+ int node; /* Node (box) number */
+ int ld; /* Logical Drive on this box, if required */
+ __u32 nexus; /* SCSI Nexus */
+ void *argp; /* Argument pointer */
+} VENDOR_IOCTL_REQ;
+
+
+typedef struct {
+ char cdb[16]; /* SCSI CDB for the pass-through */
+ ushort bus; /* Target bus on the box */
+ ushort pdrive; /* Physical drive on the box */
+ int len; /* Length of the data area of the CDB */
+ int sense_len; /* Length of the sense data */
+ char sense_data[40]; /* Sense data */
+ void *bufp; /* Data area for the CDB */
+ char rw_flag; /* Read CDB or Write CDB */
+} cpqfc_passthru_t;
+
+/*
+** Defines for the IOCTLS.
+*/
+
+#define VENDOR_READ_OPCODE 0x26
+#define VENDOR_WRITE_OPCODE 0x27
+
+#define CPQFCTS_GETPCIINFO _IOR( CCPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct)
+#define CPQFCTS_GETDRIVVER _IOR( CCPQFCTS_IOC_MAGIC, 9, DriverVer_type)
+
+#define CPQFCTS_SCSI_PASSTHRU _IOWR( CCPQFCTS_IOC_MAGIC,11, VENDOR_IOCTL_REQ)
+
+/* We would rather have equivalent generic, low-level driver agnostic
+ioctls that do what CPQFC_IOCTL_FC_TARGET_ADDRESS and
+CPQFC_IOCTL_FC_TDR 0x5388 do, but currently, we do not have them,
+consequently applications would have to know they are talking to cpqfc. */
+
+/* Used to get Fibre Channel WWN and port_id from device */
+// #define CPQFC_IOCTL_FC_TARGET_ADDRESS 0x5387
+#define CPQFC_IOCTL_FC_TARGET_ADDRESS \
+ _IOR( CCPQFCTS_IOC_MAGIC, 13, Scsi_FCTargAddress)
+
+/* Used to invoke Target Defice Reset for Fibre Channel */
+// #define CPQFC_IOCTL_FC_TDR 0x5388
+#define CPQFC_IOCTL_FC_TDR _IO( CCPQFCTS_IOC_MAGIC, 15)
+
diff --git a/drivers/scsi/cpqfcTSstructs.h b/drivers/scsi/cpqfcTSstructs.h
new file mode 100644
index 000000000000..0bae3298c44b
--- /dev/null
+++ b/drivers/scsi/cpqfcTSstructs.h
@@ -0,0 +1,1530 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSSTRUCTS_H
+#define CPQFCTSSTRUCTS_H
+
+#include <linux/timer.h> // timer declaration in our host data
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include "cpqfcTSioctl.h"
+
+#define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \
+ for( wait_time=jiffies + (secs*HZ); \
+ time_before(jiffies, wait_time) ;) ; }
+
+#define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
+// don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c
+#define VER_MAJOR 2
+#define VER_MINOR 5
+#define VER_SUBMINOR 4
+
+// Macros for kernel (esp. SMP) tracing using a PCI analyzer
+// (e.g. x86).
+//#define PCI_KERNEL_TRACE
+#ifdef PCI_KERNEL_TRACE
+#define PCI_TRACE(x) inl( fcChip->Registers.IOBaseL +x);
+#define PCI_TRACEO(x,y) outl( x, (fcChip->Registers.IOBaseL +y));
+#else
+
+#define PCI_TRACE(x)
+#define PCI_TRACEO(x,y)
+#endif
+
+
+//#define DEBUG_CMND 1 // debug output for Linux Scsi CDBs
+//#define DUMMYCMND_DBG 1
+
+//#define DEBUG_CPQFCTS 1
+//#undef DEBUG_CPQFCTS
+#ifdef DEBUG_CPQFCTS
+#define ENTER(x) printk("cpqfcts : entering %s()\n", x);
+#define LEAVE(x) printk("cpqfcts : leaving %s()\n", x);
+#define DEBUG(x) x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif /* DEBUG_CPQFCTS */
+
+//#define DEBUG_CPQFCTS_PCI 1
+//#undef DEBUG_CPQFCTS_PCI
+#if DEBUG_CPQFCTS_PCI
+#define DEBUG_PCI(x) x
+#else
+#define DEBUG_PCI(x)
+#endif /* DEBUG_CPQFCTS_PCI */
+
+#define STACHLITE66_TS12 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2"
+#define STACHLITE66_TS13 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.3"
+#define STACHLITE_UNKNOWN "Compaq FibreChannel HBA Tachyon Chip/Board Ver??"
+#define SAGILENT_XL2_21 "Agilent FC HBA, Tachyon XL2 HPFC-5200B/2.1"
+
+// PDA is Peripheral Device Address, VSA is Volume Set Addressing
+// Linux SCSI parameters
+#define CPQFCTS_MAX_TARGET_ID 64
+
+// Note, changing CPQFCTS_MAX_LUN to less than 32 (e.g, 8) will result in
+// strange behavior if a box with more than, e.g. 8, is on the loop.
+#define CPQFCTS_MAX_LUN 32 // The RA-4x00 supports 32 (Linux SCSI supports 8)
+#define CPQFCTS_MAX_CHANNEL 0 // One FC port on cpqfcTS HBA
+
+#define CPQFCTS_CMD_PER_LUN 15 // power of 2 -1, must be >0
+#define CPQFCTS_REQ_QUEUE_LEN (TACH_SEST_LEN/2) // must be < TACH_SEST_LEN
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#ifndef DECLARE_MUTEX_LOCKED
+#define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED
+#endif
+
+#define DEV_NAME "cpqfcTS"
+
+struct SupportedPCIcards
+{
+ __u16 vendor_id;
+ __u16 device_id;
+};
+
+// nn:nn denotes bit field
+ // TachyonHeader struct def.
+ // the fields shared with ODB
+ // need to have same value
+
+
+
+
+#ifndef BYTE
+//typedef UCHAR BYTE;
+typedef __u8 BYTE;
+#endif
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef LONG
+typedef __s32 LONG;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+#ifndef PVOID
+typedef void * PVOID;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+
+
+// macro for FC-PH reject codes
+// payload format for LS_RJT (FC payloads are big endian):
+// byte 0 1 2 3 (MSB)
+// DWORD 0 01 00 00 00
+// DWORD 1 resvd code expl. vendor
+
+#define LS_RJT_REASON( code, expl) (( code<<8) | (expl <<16))
+
+
+#define TachLiteSTATUS 0x12
+
+// Fibre Channel EXCHANGE status codes for Tachyon chips/ driver software
+// 32-bit ERROR word defines
+#define INVALID_ARGS 0x1
+#define LNKDWN_OSLS 0x2
+#define LNKDWN_LASER 0x4
+#define OUTQUE_FULL 0x8
+#define DRIVERQ_FULL 0x10
+#define SEST_FULL 0x20
+#define BAD_ALPA 0x40
+#define OVERFLOW 0x80 // inbound CM
+#define COUNT_ERROR 0x100 // inbound CM
+#define LINKFAIL_RX 0x200 // inbound CM
+#define ABORTSEQ_NOTIFY 0x400 // outbound CM
+#define LINKFAIL_TX 0x800 // outbound CM
+#define HOSTPROG_ERR 0x1000 // outbound CM
+#define FRAME_TO 0x2000 // outbound CM
+#define INV_ENTRY 0x4000 // outbound CM
+#define SESTPROG_ERR 0x8000 // outbound CM
+#define OUTBOUND_TIMEOUT 0x10000L // timeout waiting for Tachyon outbound CM
+#define INITIATOR_ABORT 0x20000L // initiator exchange timeout or O/S ABORT
+#define MEMPOOL_FAIL 0x40000L // O/S memory pool allocation failed
+#define FC2_TIMEOUT 0x80000L // driver timeout for lost frames
+#define TARGET_ABORT 0x100000L // ABTS received from FC port
+#define EXCHANGE_QUEUED 0x200000L // e.g. Link State was LDn on fcStart
+#define PORTID_CHANGED 0x400000L // fc Port address changed
+#define DEVICE_REMOVED 0x800000L // fc Port address changed
+// Several error scenarios result in SEST Exchange frames
+// unexpectedly arriving in the SFQ
+#define SFQ_FRAME 0x1000000L // SFQ frames from open Exchange
+
+// Maximum number of Host Bus Adapters (HBA) / controllers supported
+// only important for mem allocation dimensions - increase as necessary
+
+#define MAX_ADAPTERS 8
+#define MAX_RX_PAYLOAD 1024 // hardware dependent max frame payload
+// Tach header struc defines
+#define SOFi3 0x7
+#define SOFf 0x8
+#define SOFn3 0xB
+#define EOFn 0x5
+#define EOFt 0x6
+
+// FCP R_CTL defines
+#define FCP_CMND 0x6
+#define FCP_XFER_RDY 0x5
+#define FCP_RSP 0x7
+#define FCP_RESPONSE 0x777 // (arbitrary #)
+#define NEED_FCP_RSP 0x77 // (arbitrary #)
+#define FCP_DATA 0x1
+
+#define RESET_TACH 0x100 // Reset Tachyon/TachLite
+#define SCSI_IWE 0x2000 // initiator write entry (for SEST)
+#define SCSI_IRE 0x3000 // initiator read entry (for SEST)
+#define SCSI_TRE 0x400 // target read entry (for SEST)
+#define SCSI_TWE 0x500 // target write entry (for SEST)
+#define TOGGLE_LASER 0x800
+#define LIP 0x900
+#define CLEAR_FCPORTS 99 // (arbitrary #) free mem for Logged in ports
+#define FMINIT 0x707 // (arbitrary) for Frame Manager Init command
+
+// BLS == Basic Link Service
+// ELS == Extended Link Service
+#define BLS_NOP 4
+#define BLS_ABTS 0x10 // FC-PH Basic Link Service Abort Sequence
+#define BLS_ABTS_ACC 0x100 // FC-PH Basic Link Service Abort Sequence Accept
+#define BLS_ABTS_RJT 0x101 // FC-PH Basic Link Service Abort Sequence Reject
+#define ELS_PLOGI 0x03 // FC-PH Port Login (arbitrary assign)
+#define ELS_SCR 0x70 // (arb assign) State Change Registration (Fabric)
+#define FCS_NSR 0x72 // (arb assign) Name Service Request (Fabric)
+#define ELS_FLOGI 0x44 // (arb assign) Fabric Login
+#define ELS_FDISC 0x41 // (arb assign) Fabric Discovery (Login)
+#define ELS_PDISC 0x50 // FC-PH2 Port Discovery
+#define ELS_ABTX 0x06 // FC-PH Abort Exchange
+#define ELS_LOGO 0x05 // FC-PH Port Logout
+#define ELS_PRLI 0x20 // FCP-SCSI Process Login
+#define ELS_PRLO 0x21 // FCP-SCSI Process Logout
+#define ELS_LOGO_ACC 0x07 // {FC-PH} Port Logout Accept
+#define ELS_PLOGI_ACC 0x08 // {FC-PH} Port Login Accept
+#define ELS_ACC 0x18 // {FC-PH} (generic) ACCept
+#define ELS_PRLI_ACC 0x22 // {FCP-SCSI} Process Login Accept
+#define ELS_RJT 0x1000000
+#define SCSI_REPORT_LUNS 0x0A0
+#define FCP_TARGET_RESET 0x200
+
+#define ELS_LILP_FRAME 0x00000711 // 1st payload word of LILP frame
+
+#define SFQ_UNASSISTED_FCP 1 // ICM, DWord3, "Type" unassisted FCP
+#define SFQ_UNKNOWN 0x31 // (arbitrary) ICM, DWord3, "Type" unknown
+
+// these "LINK" bits refer to loop or non-loop
+#define LINKACTIVE 0x2 // fcLinkQ type - LINK UP Tachyon FM 'Lup' bit set
+#define LINKDOWN 0xf2 // fcLinkQ type - LINK DOWN Tachyon FM 'Ldn' bit set
+
+//#define VOLUME_SET_ADDRESSING 1 // "channel" or "bus" 1
+
+typedef struct // 32 bytes hdr ONLY (e.g. FCP_DATA buffer for SEST)
+{
+ ULONG reserved; // dword 0 (don't use)
+ ULONG sof_eof;
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+} TachFCHDR;
+
+ // NOTE!! the following struct MUST be 64 bytes.
+typedef struct // 32 bytes hdr + 32 bytes payload
+{
+ ULONG reserved; // dword 0 (don't use - must clear to 0)
+ ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+//---------
+ __u32 pl[8]; // dwords 8-15 frame data payload
+} TachFCHDR_CMND;
+
+
+typedef struct // 32 bytes hdr + 120 bytes payload
+{
+ ULONG reserved; // dword 0 (don't use - must clear to 0)
+ ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+//---------
+ __u32 pl[30]; // largest necessary payload (for LOGIN cmnds)
+} TachFCHDR_GCMND;
+
+typedef struct // 32 bytes hdr + 64 bytes payload
+{
+ ULONG reserved; // dword 0 (don't use)
+ ULONG sof_eof;
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+//---------
+ __u32 pl[18]; // payload for FCP-RSP (response buffer) RA-4x00 is 72bytes
+} TachFCHDR_RSP;
+
+
+
+
+
+
+// Inbound Message Queue structures...
+typedef struct // each entry 8 words (32 bytes)
+{
+ ULONG type; // IMQ completion message types
+ ULONG word[7]; // remainder of structure
+ // interpreted by IMQ type
+} TachyonIMQE;
+
+
+// Queues for TachLite not in original Tachyon
+// ERQ - Exchange Request Queue (for outbound commands)
+// SFQ - Single Frame Queue (for incoming frames)
+
+ // Define Tachyon Outbound Command Que
+ // (Since many Tachyon registers are Read
+ // only, maintain copies for debugging)
+ // most Tach ques need power-of-2 sizes,
+ // where registers are loaded with po2 -1
+#define TACH_SEST_LEN 512 // TachLite SEST
+
+#define ELS_EXCHANGES 64 // e.g. PLOGI, RSCN, ...
+// define the total number of outstanding (simultaneous) exchanges
+#define TACH_MAX_XID (TACH_SEST_LEN + ELS_EXCHANGES) // ELS exchanges
+
+#define ERQ_LEN 128 // power of 2, max 4096
+
+// Inbound Message Queue structures...
+#define IMQ_LEN 512 // minimum 4 entries [(power of 2) - 1]
+typedef struct // 8 words - 32 bytes
+{
+ TachyonIMQE QEntry[IMQ_LEN];
+ ULONG producerIndex; // IMQ Producer Index register
+ // @32 byte align
+ ULONG consumerIndex; // Consumer Index register (in Tachyon)
+ ULONG length; // Length register
+ ULONG base;
+} TachyonIMQ; // @ 32 * IMQ_LEN align
+
+
+
+typedef struct // inbound completion message
+{
+ ULONG Type;
+ ULONG Index;
+ ULONG TransferLength;
+} TachyonInbCM;
+
+
+
+// arbitrary numeric tags for TL structures
+#define TL_FCHS 1 // TachLite Fibre Channel Header Structure
+#define TL_IWE 2 // initiator write entry (for SEST)
+#define TL_TWE 3 // target write entry (for SEST)
+#define TL_IRE 4 // initiator read entry (for SEST)
+#define TL_TRE 5 // target read entry (for SEST)
+#define TL_IRB 6 // I/O request block
+
+ // for INCOMING frames
+#define SFQ_LEN 32 // minimum 32 entries, max 4096
+
+typedef struct // Single Frame Que
+{
+ TachFCHDR_CMND QEntry[SFQ_LEN]; // must be 64 bytes!!
+ ULONG producerIndex; // IMQ Producer Index register
+ // @32 byte align
+ ULONG consumerIndex; // Consumer Index register (in Tachyon)
+ ULONG length; // Length register
+ ULONG base;
+} TachLiteSFQ;
+
+
+typedef struct // I/O Request Block flags
+{
+ UCHAR BRD : 1;
+ UCHAR : 1; // reserved
+ UCHAR SFA : 1;
+ UCHAR DNC : 1;
+ UCHAR DIN : 1;
+ UCHAR DCM : 1;
+ UCHAR CTS : 1;
+ UCHAR SBV : 1; // IRB entry valid - IRB'B' only
+} IRBflags;
+
+typedef struct // I/O Request Block
+{ // Request 'A'
+ ULONG Req_A_SFS_Len; // total frame len (hdr + payload), min 32
+ ULONG Req_A_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent)
+ ULONG Req_A_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa)
+ ULONG Req_A_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST
+ // Request 'B'
+ ULONG Req_B_SFS_Len; // total frame len (hdr + payload), min 32
+ ULONG Req_B_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent)
+ ULONG Req_B_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa)
+ ULONG Req_B_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST
+} TachLiteIRB;
+
+
+typedef struct // TachLite placeholder for IRBs
+{ // aligned @sizeof(ERQ) for TachLite
+ // MAX commands is sum of SEST len and ERQ
+ // we know that each SEST entry requires an
+ // IRB (ERQ) entry; in addition, we provide
+ // ERQ_LEN
+ TachLiteIRB QEntry[ERQ_LEN]; // Base register; entries 32 bytes ea.
+ ULONG consumerIndex; // Consumer Index register
+ ULONG producerIndex; // ERQ Producer Index register
+ ULONG length; // Length register
+ ULONG base; // copy of base ptr for debug
+ // struct is sized for largest expected cmnd (LOGIN)
+} TachLiteERQ;
+
+// for now, just 32 bit DMA, eventually 40something, with code changes
+#define CPQFCTS_DMA_MASK ((unsigned long) (0x00000000FFFFFFFF))
+
+#define TL_MAX_SG_ELEM_LEN 0x7ffff // Max buffer length a single S/G entry
+ // may represent (a hardware limitation). The
+ // only reason to ever change this is if you
+ // want to exercise very-hard-to-reach code in
+ // cpqfcTSworker.c:build_SEST_sglist().
+
+#define TL_DANGER_SGPAGES 7 // arbitrary high water mark for # of S/G pages
+ // we must exceed to elicit a warning indicative
+ // of EXTREMELY large data transfers or
+ // EXTREME memory fragmentation.
+ // (means we just used up 2048 S/G elements,
+ // Never seen this is real life, only in
+ // testing with tricked up driver.)
+
+#define TL_EXT_SG_PAGE_COUNT 256 // Number of Extended Scatter/Gather a/l PAIRS
+ // Tachyon register (IOBaseU 0x68)
+ // power-of-2 value ONLY! 4 min, 256 max
+
+ // byte len is #Pairs * 2 ULONG/Pair * 4 bytes/ULONG
+#define TL_EXT_SG_PAGE_BYTELEN (TL_EXT_SG_PAGE_COUNT *2 *4)
+
+
+
+// SEST entry types: IWE, IRE, TWE, TRE
+typedef struct
+{
+ ULONG Hdr_Len;
+ ULONG Hdr_Addr;
+ ULONG RSP_Len;
+ ULONG RSP_Addr;
+ ULONG Buff_Off;
+#define USES_EXTENDED_SGLIST(this_sest, x_ID) \
+ (!((this_sest)->u[ x_ID ].IWE.Buff_Off & 0x80000000))
+ ULONG Link;
+ ULONG RX_ID;
+ ULONG Data_Len;
+ ULONG Exp_RO;
+ ULONG Exp_Byte_Cnt;
+ // --- extended/local Gather Len/Address pairs
+ ULONG GLen1;
+ ULONG GAddr1;
+ ULONG GLen2;
+ ULONG GAddr2;
+ ULONG GLen3;
+ ULONG GAddr3;
+} TachLiteIWE;
+
+
+typedef struct
+{
+ ULONG Seq_Accum;
+ ULONG reserved; // must clear to 0
+ ULONG RSP_Len;
+ ULONG RSP_Addr;
+ ULONG Buff_Off;
+ ULONG Buff_Index; // ULONG 5
+ ULONG Exp_RO;
+ ULONG Byte_Count;
+ ULONG reserved_; // ULONG 8
+ ULONG Exp_Byte_Cnt;
+ // --- extended/local Scatter Len/Address pairs
+ ULONG SLen1;
+ ULONG SAddr1;
+ ULONG SLen2;
+ ULONG SAddr2;
+ ULONG SLen3;
+ ULONG SAddr3;
+} TachLiteIRE;
+
+
+typedef struct // Target Write Entry
+{
+ ULONG Seq_Accum; // dword 0
+ ULONG reserved; // dword 1 must clear to 0
+ ULONG Remote_Node_ID;
+ ULONG reserved1; // dword 3 must clear to 0
+ ULONG Buff_Off;
+ ULONG Buff_Index; // ULONG 5
+ ULONG Exp_RO;
+ ULONG Byte_Count;
+ ULONG reserved_; // ULONG 8
+ ULONG Exp_Byte_Cnt;
+ // --- extended/local Scatter Len/Address pairs
+ ULONG SLen1;
+ ULONG SAddr1;
+ ULONG SLen2;
+ ULONG SAddr2;
+ ULONG SLen3;
+ ULONG SAddr3;
+} TachLiteTWE;
+
+typedef struct
+{
+ ULONG Hdr_Len;
+ ULONG Hdr_Addr;
+ ULONG RSP_Len; // DWord 2
+ ULONG RSP_Addr;
+ ULONG Buff_Off;
+ ULONG Buff_Index; // DWord 5
+ ULONG reserved;
+ ULONG Data_Len;
+ ULONG reserved_;
+ ULONG reserved__;
+ // --- extended/local Gather Len/Address pairs
+ ULONG GLen1; // DWord A
+ ULONG GAddr1;
+ ULONG GLen2;
+ ULONG GAddr2;
+ ULONG GLen3;
+ ULONG GAddr3;
+} TachLiteTRE;
+
+typedef struct ext_sg_page_ptr_t *PSGPAGES;
+typedef struct ext_sg_page_ptr_t
+{
+ unsigned char page[TL_EXT_SG_PAGE_BYTELEN * 2]; // 2x for alignment
+ dma_addr_t busaddr; // need the bus addresses and
+ unsigned int maplen; // lengths for later pci unmapping.
+ PSGPAGES next;
+} SGPAGES; // linked list of S/G pairs, by Exchange
+
+typedef struct // SCSI Exchange State Table
+{
+ union // Entry can be IWE, IRE, TWE, TRE
+ { // 64 bytes per entry
+ TachLiteIWE IWE;
+ TachLiteIRE IRE;
+ TachLiteTWE TWE;
+ TachLiteTRE TRE;
+ } u[TACH_SEST_LEN];
+
+ TachFCHDR DataHDR[TACH_SEST_LEN]; // for SEST FCP_DATA frame hdr (no pl)
+ TachFCHDR_RSP RspHDR[TACH_SEST_LEN]; // space for SEST FCP_RSP frame
+ PSGPAGES sgPages[TACH_SEST_LEN]; // head of linked list of Pool-allocations
+ ULONG length; // Length register
+ ULONG base; // copy of base ptr for debug
+} TachSEST;
+
+
+
+typedef struct // each register has it's own address
+ // and value (used for write-only regs)
+{
+ void* address;
+ volatile ULONG value;
+} FCREGISTER;
+
+typedef struct // Host copy - TachLite Registers
+{
+ ULONG IOBaseL, IOBaseU; // I/O port lower and upper TL register addresses
+ ULONG MemBase; // memory mapped register addresses
+ void* ReMapMemBase; // O/S VM reference for MemBase
+ ULONG wwn_hi; // WWN is set once at startup
+ ULONG wwn_lo;
+ ULONG my_al_pa; // al_pa received after LIP()
+ ULONG ROMCTR; // flags for on-board RAM/ROM
+ ULONG RAMBase; // on-board RAM (i.e. some Tachlites)
+ ULONG SROMBase; // on-board EEPROM (some Tachlites)
+ ULONG PCIMCTR; // PCI Master Control Reg (has bus width)
+
+ FCREGISTER INTEN; // copy of interrupt enable mask
+ FCREGISTER INTPEND; // interrupt pending
+ FCREGISTER INTSTAT; // interrupt status
+ FCREGISTER SFQconsumerIndex;
+ FCREGISTER ERQproducerIndex;
+ FCREGISTER TYconfig; // TachYon (chip level)
+ FCREGISTER TYcontrol;
+ FCREGISTER TYstatus;
+ FCREGISTER FMconfig; // Frame Manager (FC loop level)
+ FCREGISTER FMcontrol;
+ FCREGISTER FMstatus;
+ FCREGISTER FMLinkStatus1;
+ FCREGISTER FMLinkStatus2;
+ FCREGISTER FMBB_CreditZero;
+ FCREGISTER status;
+ FCREGISTER ed_tov; // error detect time-out value
+ FCREGISTER rcv_al_pa; // received arb. loop physical address
+ FCREGISTER primitive; // e.g. LIP(), OPN(), ...
+} TL_REGISTERS;
+
+
+
+typedef struct
+{
+ ULONG ok;
+ ULONG invalidArgs;
+ ULONG linkDown;
+ ULONG linkUp;
+ ULONG outQueFull;
+ ULONG SESTFull;
+ ULONG hpe; // host programming err (from Tach)
+ ULONG FC4aborted; // aborts from Application or upper driver layer
+ ULONG FC2aborted; // aborts from our driver's timeouts
+ ULONG timeouts; // our driver timeout (on individual exchanges)
+ ULONG logouts; // explicit - sent LOGO; implicit - device removed
+ ULONG retries;
+ ULONG linkFailTX;
+ ULONG linkFailRX;
+ ULONG CntErrors; // byte count expected != count received (typ. SEST)
+ ULONG e_stores; // elastic store errs
+ ULONG resets; // hard or soft controller resets
+ ULONG FMinits; // TACH Frame Manager Init (e.g. LIPs)
+ ULONG lnkQueFull; // too many LOGIN, loop commands
+ ULONG ScsiQueFull; // too many FCP-SCSI inbound frames
+ ULONG LossofSignal; // FM link status 1 regs
+ ULONG BadRXChar; // FM link status 1 regs
+ ULONG LossofSync; // FM link status 1 regs
+ ULONG Rx_EOFa; // FM link status 2 regs (received EOFa)
+ ULONG Dis_Frm; // FM link status 2 regs (discarded frames)
+ ULONG Bad_CRC; // FM link status 2 regs
+ ULONG BB0_Timer; // FM BB_Credit Zero Timer Reg
+ ULONG loopBreaks; // infinite loop exits
+ ULONG lastBB0timer; // static accum. buffer needed by Tachlite
+} FCSTATS;
+
+
+typedef struct // Config Options
+{ // LS Bit first
+ USHORT : 1; // bit0:
+ USHORT flogi : 1; // bit1: We sent FLOGI - wait for Fabric logins
+ USHORT fabric: 1; // bit2: Tachyon detected Fabric (FM stat LG)
+ USHORT LILPin: 1; // bit3: We can use an FC-AL LILP frame
+ USHORT target: 1; // bit4: this Port has SCSI target capability
+ USHORT initiator: 1; // bit5: this Port has SCSI initiator capability
+ USHORT extLoopback: 1; // bit6: loopback at GBIC
+ USHORT intLoopback: 1; // bit7: loopback in HP silicon
+ USHORT : 1; // bit8:
+ USHORT : 1; // bit9:
+ USHORT : 1; // bit10:
+ USHORT : 1; // bit11:
+ USHORT : 1; // bit12:
+ USHORT : 1; // bit13:
+ USHORT : 1; // bit14:
+ USHORT : 1; // bit15:
+} FC_OPTIONS;
+
+
+
+typedef struct dyn_mem_pair
+{
+ void *BaseAllocated; // address as allocated from O/S;
+ unsigned long AlignedAddress; // aligned address (used by Tachyon DMA)
+ dma_addr_t dma_handle;
+ size_t size;
+} ALIGNED_MEM;
+
+
+
+
+// these structs contain only CRUCIAL (stuff we actually use) parameters
+// from FC-PH(n) logins. (Don't save entire LOGIN payload to save mem.)
+
+// Implicit logout happens when the loop goes down - we require PDISC
+// to restore. Explicit logout is when WE decide never to talk to someone,
+// or when a target refuses to talk to us, i.e. sends us a LOGO frame or
+// LS_RJT reject in response to our PLOGI request.
+
+#define IMPLICIT_LOGOUT 1
+#define EXPLICIT_LOGOUT 2
+
+typedef struct
+{
+ UCHAR channel; // SCSI "bus"
+ UCHAR target;
+ UCHAR InqDeviceType; // byte 0 from SCSI Inquiry response
+ UCHAR VolumeSetAddressing; // FCP-SCSI LUN coding (40h for VSA)
+ UCHAR LunMasking; // True if selective presentation supported
+ UCHAR lun[CPQFCTS_MAX_LUN];
+} SCSI_NEXUS;
+
+
+typedef struct
+{
+ union
+ {
+ UCHAR ucWWN[8]; // a FC 64-bit World Wide Name/ PortID of target
+ // addressing of single target on single loop...
+ u64 liWWN;
+ } u;
+
+ ULONG port_id; // a FC 24-bit address of port (lower 8 bits = al_pa)
+
+#define REPORT_LUNS_PL 256
+ UCHAR ReportLunsPayload[REPORT_LUNS_PL];
+
+ SCSI_NEXUS ScsiNexus; // LUNs per FC device
+
+ ULONG LOGO_counter; // might try several times before logging out for good
+ ULONG LOGO_timer; // after LIP, ports expecting PDISC must time-out and
+ // LOGOut if successful PDISC not completed in 2 secs
+
+ ULONG concurrent_seq; // must be 1 or greater
+ ULONG rx_data_size; // e.g. 128, 256, 1024, 2048 per FC-PH spec
+ ULONG BB_credit;
+ ULONG EE_credit;
+
+ ULONG fcp_info; // from PRLI (i.e. INITIATOR/ TARGET flags)
+ // flags for login process
+ BOOLEAN Originator; // Login sequence Originated (if false, we
+ // responded to another port's login sequence)
+ BOOLEAN plogi; // PLOGI frame ACCepted (originated or responded)
+ BOOLEAN pdisc; // PDISC frame was ORIGINATED (self-login logic)
+ BOOLEAN prli; // PRLI frame ACCepted (originated or responded)
+ BOOLEAN flogi; // FLOGI frame ACCepted (originated or responded)
+ BOOLEAN logo; // port permanently logged out (invalid login param)
+ BOOLEAN flogiReq; // Fabric login required (set in LIP process)
+ UCHAR highest_ver;
+ UCHAR lowest_ver;
+
+
+ // when the "target" (actually FC Port) is waiting for login
+ // (e.g. after Link reset), set the device_blocked bit;
+ // after Port completes login, un-block target.
+ UCHAR device_blocked; // see Scsi_Device struct
+
+ // define singly-linked list of logged-in ports
+ // once a port_id is identified, it is remembered,
+ // even if the port is removed indefinitely
+ PVOID pNextPort; // actually, type PFC_LOGGEDIN_PORT; void for Compiler
+
+} FC_LOGGEDIN_PORT, *PFC_LOGGEDIN_PORT;
+
+
+
+// This serves as the ESB (Exchange Status Block),
+// and has timeout counter; used for ABORTs
+typedef struct
+{ // FC-1 X_IDs
+ ULONG type; // ELS_PLOGI, SCSI_IWE, ... (0 if free)
+ PFC_LOGGEDIN_PORT pLoggedInPort; // FC device on other end of Exchange
+ Scsi_Cmnd *Cmnd; // Linux SCSI command packet includes S/G list
+ ULONG timeOut; // units of ??, DEC by driver, Abort when 0
+ ULONG reTries; // need one or more retries?
+ ULONG status; // flags indicating errors (0 if none)
+ TachLiteIRB IRB; // I/O Request Block, gets copied to ERQ
+ TachFCHDR_GCMND fchs; // location of IRB's Req_A_SFS_Addr
+} FC_EXCHANGE, *PFC_EXCHANGE;
+
+// Unfortunately, Linux limits our kmalloc() allocations to 128k.
+// Because of this and the fact that our ScsiRegister allocation
+// is also constrained, we move this large structure out for
+// allocation after Scsi Register.
+// (In other words, this cumbersome indirection is necessary
+// because of kernel memory allocation constraints!)
+
+typedef struct // we will allocate this dynamically
+{
+ FC_EXCHANGE fcExchange[ TACH_MAX_XID ];
+} FC_EXCHANGES;
+
+
+
+
+
+
+
+
+
+
+
+typedef struct
+{
+ char Name[64]; // name of controller ("HP Tachlite TL Rev2.0, 33MHz, 64bit bus")
+ //PVOID pAdapterDevExt; // back pointer to device object/extension
+ ULONG ChipType; // local numeric key for Tachyon Type / Rev.
+ ULONG status; // our Driver - logical status
+
+ TL_REGISTERS Registers; // reg addresses & host memory copies
+ // FC-4 mapping of 'transaction' to X_IDs
+ UCHAR LILPmap[32*4]; // Loop Position Map of ALPAs (late FC-AL only)
+ FC_OPTIONS Options; // e.g. Target, Initiator, loopback...
+ UCHAR highest_FCPH_ver; // FC-PH version limits
+ UCHAR lowest_FCPH_ver; // FC-PH version limits
+
+ FC_EXCHANGES *Exchanges;
+ ULONG fcLsExchangeLRU; // Least Recently Used counter (Link Service)
+ ULONG fcSestExchangeLRU; // Least Recently Used counter (FCP-SCSI)
+ FC_LOGGEDIN_PORT fcPorts; // linked list of every FC port ever seen
+ FCSTATS fcStats; // FC comm err counters
+
+ // Host memory QUEUE pointers
+ TachLiteERQ *ERQ; // Exchange Request Que
+ TachyonIMQ *IMQ; // Inbound Message Que
+ TachLiteSFQ *SFQ; // Single Frame Queue
+ TachSEST *SEST; // SCSI Exchange State Table
+
+ dma_addr_t exch_dma_handle;
+
+ // these function pointers are for "generic" functions, which are
+ // replaced with Host Bus Adapter types at
+ // runtime.
+ int (*CreateTachyonQues)( void* , int);
+ int (*DestroyTachyonQues)( void* , int);
+ int (*LaserControl)(void*, int ); // e.g. On/Off
+ int (*ResetTachyon)(void*, int );
+ void (*FreezeTachyon)(void*, int );
+ void (*UnFreezeTachyon)(void*, int );
+ int (*InitializeTachyon)(void*, int, int );
+ int (*InitializeFrameManager)(void*, int );
+ int (*ProcessIMQEntry)(void*);
+ int (*ReadWriteWWN)(void*, int ReadWrite);
+ int (*ReadWriteNVRAM)(void*, void*, int ReadWrite);
+
+} TACHYON, *PTACHYON;
+
+
+void cpqfcTSClearLinkStatusCounters(TACHYON * fcChip);
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode);
+int CpqTsDestroyTachLiteQues( void* , int);
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2);
+
+int CpqTsProcessIMQEntry(void* pHBA);
+int CpqTsResetTachLite(void *pHBA, int type);
+void CpqTsFreezeTachlite(void *pHBA, int type);
+void CpqTsUnFreezeTachlite(void *pHBA, int type);
+int CpqTsInitializeFrameManager(void *pHBA, int);
+int CpqTsLaserControl( void* addrBase, int opcode );
+int CpqTsReadWriteWWN(void*, int ReadWrite);
+int CpqTsReadWriteNVRAM(void*, void* data, int ReadWrite);
+
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter);
+void cpqfcTSWorkerThread( void *host);
+
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf );
+ULONG cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+ UCHAR *buf );
+
+BOOLEAN tl_write_i2c_nvram( void* GPIOin, void* GPIOout,
+ USHORT startOffset, // e.g. 0x2f for WWN start
+ USHORT count,
+ UCHAR *buf );
+
+
+// define misc functions
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]);
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]);
+void* fcMemManager( struct pci_dev *pdev,
+ ALIGNED_MEM *dyn_mem_pair, ULONG n_alloc, ULONG ab,
+ ULONG ulAlignedAddress, dma_addr_t *dma_handle);
+
+void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt);
+
+//ULONG virt_to_phys( PVOID virtaddr );
+
+
+// Linux interrupt handler
+irqreturn_t cpqfcTS_intr_handler( int irq,void *dev_id,struct pt_regs *regs);
+void cpqfcTSheartbeat( unsigned long ptr );
+
+
+
+// The biggest Q element we deal with is Aborts - we
+// need 4 bytes for x_ID, and a Scsi_Cmnd (~284 bytes)
+//#define LINKQ_ITEM_SIZE ((4+sizeof(Scsi_Cmnd)+3)/4)
+#define LINKQ_ITEM_SIZE (3*16)
+typedef struct
+{
+ ULONG Type; // e.g. LINKUP, SFQENTRY, PDISC, BLS_ABTS, ...
+ ULONG ulBuff[ LINKQ_ITEM_SIZE ];
+} LINKQ_ITEM;
+
+#define FC_LINKQ_DEPTH TACH_MAX_XID
+typedef struct
+{
+ ULONG producer;
+ ULONG consumer; // when producer equals consumer, Q empty
+
+ LINKQ_ITEM Qitem[ FC_LINKQ_DEPTH ];
+
+} FC_LINK_QUE, *PFC_LINK_QUE;
+
+
+ // DPC routines post to here on Inbound SCSI frames
+ // User thread processes
+#define FC_SCSIQ_DEPTH 32
+
+typedef struct
+{
+ int Type; // e.g. SCSI
+ ULONG ulBuff[ 3*16 ];
+} SCSIQ_ITEM;
+
+typedef struct
+{
+ ULONG producer;
+ ULONG consumer; // when producer equals consumer, Q empty
+
+ SCSIQ_ITEM Qitem[ FC_SCSIQ_DEPTH ];
+
+} FC_SCSI_QUE, *PFC_SCSI_QUE;
+
+typedef struct {
+ /* This is tacked on to a Scsi_Request in upper_private_data
+ for pasthrough ioctls, as a place to hold data that can't
+ be stashed anywhere else in the Scsi_Request. We differentiate
+ this from _real_ upper_private_data by checking if the virt addr
+ is within our special pool. */
+ ushort bus;
+ ushort pdrive;
+} cpqfc_passthru_private_t;
+
+#define CPQFC_MAX_PASSTHRU_CMDS 100
+
+#define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST
+
+// Linux space allocated per HBA (chip state, etc.)
+typedef struct
+{
+ struct Scsi_Host *HostAdapter; // back pointer to Linux Scsi struct
+
+ TACHYON fcChip; // All Tachyon registers, Queues, functions
+ ALIGNED_MEM dynamic_mem[DYNAMIC_ALLOCATIONS];
+
+ struct pci_dev *PciDev;
+ dma_addr_t fcLQ_dma_handle;
+
+ Scsi_Cmnd *LinkDnCmnd[CPQFCTS_REQ_QUEUE_LEN]; // collects Cmnds during LDn
+ // (for Acceptable targets)
+ Scsi_Cmnd *BoardLockCmnd[CPQFCTS_REQ_QUEUE_LEN]; // SEST was full
+
+ Scsi_Cmnd *BadTargetCmnd[CPQFCTS_MAX_TARGET_ID]; // missing targets
+
+ u_char HBAnum; // 0-based host number
+
+
+ struct timer_list cpqfcTStimer; // FC utility timer for implicit
+ // logouts, FC protocol timeouts, etc.
+ int fcStatsTime; // Statistics delta reporting time
+
+ struct task_struct *worker_thread; // our kernel thread
+ int PortDiscDone; // set by SendLogins(), cleared by LDn
+
+ struct semaphore *TachFrozen;
+ struct semaphore *TYOBcomplete; // handshake for Tach outbound frames
+ struct semaphore *fcQueReady; // FibreChannel work for our kernel thread
+ struct semaphore *notify_wt; // synchronizes kernel thread kill
+ struct semaphore *BoardLock;
+
+ PFC_LINK_QUE fcLQ; // the WorkerThread operates on this
+
+ spinlock_t hba_spinlock; // held/released by WorkerThread
+ cpqfc_passthru_private_t *private_data_pool;
+ unsigned long *private_data_bits;
+
+} CPQFCHBA;
+
+#define CPQ_SPINLOCK_HBA( x ) spin_lock(&x->hba_spinlock);
+#define CPQ_SPINUNLOCK_HBA(x) spin_unlock(&x->hba_spinlock);
+
+
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pFcPort);
+
+
+void cpqfcTSTerminateExchange( CPQFCHBA*, SCSI_NEXUS *target, int );
+
+PFC_LOGGEDIN_PORT fcPortLoggedIn(
+ CPQFCHBA *cpqfcHBAdata,
+ TachFCHDR_GCMND* fchs,
+ BOOLEAN,
+ BOOLEAN);
+void fcProcessLoggedIn(
+ CPQFCHBA *cpqfcHBAdata, TachFCHDR_GCMND* fchs);
+
+
+ULONG cpqfcTSBuildExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG type, // e.g. PLOGI
+ TachFCHDR_GCMND* InFCHS, // incoming FCHS
+ void *Data, // the CDB, scatter/gather, etc.
+ LONG *ExchangeID ); // allocated exchange ID
+
+ULONG cpqfcTSStartExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ LONG ExchangeID );
+
+void cpqfcTSCompleteExchange(
+ struct pci_dev *pcidev,
+ PTACHYON fcChip,
+ ULONG exchange_ID);
+
+
+PFC_LOGGEDIN_PORT fcFindLoggedInPort(
+ PTACHYON fcChip,
+ Scsi_Cmnd *Cmnd, // (We want the channel/target/lun Nexus from Cmnd)
+ ULONG port_id, // search linked list for al_pa, or
+ UCHAR wwn[8], // search linked list for WWN, or...
+ PFC_LOGGEDIN_PORT *pLastLoggedInPort
+);
+
+void cpqfcTSPutLinkQue(
+ CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent);
+
+void fcPutScsiQue(
+ CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent);
+
+void fcLinkQReset(
+ CPQFCHBA *);
+void fcScsiQReset(
+ CPQFCHBA *);
+void fcSestReset(
+ CPQFCHBA *);
+
+void cpqfc_pci_unmap(struct pci_dev *pcidev,
+ Scsi_Cmnd *cmd,
+ PTACHYON fcChip,
+ ULONG x_ID);
+
+extern const UCHAR valid_al_pa[];
+extern const int number_of_al_pa;
+
+#define FCP_RESID_UNDER 0x80000
+#define FCP_RESID_OVER 0x40000
+#define FCP_SNS_LEN_VALID 0x20000
+#define FCP_RSP_LEN_VALID 0x10000
+
+// RSP_CODE definitions (dpANS Fibre Channel Protocol for SCSI, pg 34)
+#define FCP_DATA_LEN_NOT_BURST_LEN 0x1000000
+#define FCP_CMND_FIELD_INVALID 0x2000000
+#define FCP_DATA_RO_NOT_XRDY_RO 0x3000000
+#define FCP_TASKFUNCTION_NS 0x4000000
+#define FCP_TASKFUNCTION_FAIL 0x5000000
+
+// FCP-SCSI response status struct
+typedef struct // see "TachFCHDR_RSP" definition - 64 bytes
+{
+ __u32 reserved;
+ __u32 reserved1;
+ __u32 fcp_status; // field validity and SCSI status
+ __u32 fcp_resid;
+ __u32 fcp_sns_len; // length of FCP_SNS_INFO field
+ __u32 fcp_rsp_len; // length of FCP_RSP_INFO field (expect 8)
+ __u32 fcp_rsp_info; // 4 bytes of FCP protocol response information
+ __u32 fcp_rsp_info2; // (4 more bytes, since most implementations use 8)
+ __u8 fcp_sns_info[36]; // bytes for SCSI sense (ASC, ASCQ)
+
+} FCP_STATUS_RESPONSE, *PFCP_STATUS_RESPONSE;
+
+
+// Fabric State Change Registration
+typedef struct scrpl
+{
+ __u32 command;
+ __u32 function;
+} SCR_PL;
+
+// Fabric Name Service Request
+typedef struct nsrpl
+{
+ __u32 CT_Rev; // (& IN_ID) WORD 0
+ __u32 FCS_Type; // WORD 1
+ __u32 Command_code; // WORD 2
+ __u32 reason_code; // WORD 3
+ __u32 FCP; // WORD 4 (lower byte)
+
+} NSR_PL;
+
+
+
+// "FC.H"
+#define MAX_RX_SIZE 0x800 // Max Receive Buffer Size is 2048
+#define MIN_RX_SIZE 0x100 // Min Size is 256, per FC-PLDA Spec
+#define MAX_TARGET_RXIDS SEST_DEPTH
+#define TARGET_RX_SIZE SEST_BUFFER_LENGTH
+
+#define CLASS_1 0x01
+#define CLASS_2 0x02
+#define CLASS_3 0x03
+
+#define FC_PH42 0x08
+#define FC_PH43 0x09
+#define FC_PH3 0x20
+
+#define RR_TOV 2 // Minimum Time for target to wait for
+ // PDISC after a LIP.
+#define E_D_TOV 2 // Minimum Time to wait for Sequence
+ // Completion.
+#define R_A_TOV 0 // Minimum Time for Target to wait
+ // before reclaiming resources.
+//
+// R_CTL Field
+//
+// Routing Bits (31-28)
+//
+#define FC4_DEVICE_DATA 0x00000000
+#define EXT_LINK_DATA 0x20000000
+#define FC4_LINK_DATA 0x30000000
+#define VIDEO_DATA 0x40000000
+#define BASIC_LINK_DATA 0x80000000
+#define LINK_CONTROL 0xC0000000
+#define ROUTING_MASK 0xF0000000
+
+//
+// Information Bits (27-24)
+//
+#define UNCAT_INFORMATION 0x00000000
+#define SOLICITED_DATA 0x01000000
+#define UNSOLICITED_CONTROL 0x02000000
+#define SOLICITED_CONTROL 0x03000000
+#define UNSOLICITED_DATA 0x04000000
+#define DATA_DESCRIPTOR 0x05000000
+#define UNSOLICITED_COMMAND 0x06000000
+#define COMMAND_STATUS 0x07000000
+#define INFO_MASK 0x0F000000
+//
+// (Link Control Codes)
+//
+#define ACK_1 0x00000000
+#define ACK_0_OR_N 0x01000000
+#define P_RJT 0x02000000
+#define F_RJT 0x03000000
+#define P_BSY 0x04000000
+#define FABRIC_BUSY_TO_DF 0x05000000 // Fabric Busy to Data Frame
+#define FABRIC_BUSY_TO_LC 0x06000000 // Fabric Busy to Link Ctl Frame
+#define LINK_CREDIT_RESET 0x07000000
+//
+// (Link Service Command Codes)
+//
+//#define LS_RJT 0x01000000 // LS Reject
+
+#define LS_ACC 0x02000000 // LS Accept
+#define LS_PLOGI 0x03000000 // N_PORT Login
+#define LS_FLOGI 0x04000000 // F_PORT Login
+#define LS_LOGO 0x05000000 // Logout
+#define LS_ABTX 0x06000000 // Abort Exchange
+#define LS_RCS 0x07000000 // Read Connection Status
+#define LS_RES 0x08000000 // Read Exchange Status
+#define LS_RSS 0x09000000 // Read Sequence Status
+#define LS_RSI 0x0A000000 // Request Seq Initiative
+#define LS_ESTS 0x0B000000 // Establish Steaming
+#define LS_ESTC 0x0C000000 // Estimate Credit
+#define LS_ADVC 0x0D000000 // Advice Credit
+#define LS_RTV 0x0E000000 // Read Timeout Value
+#define LS_RLS 0x0F000000 // Read Link Status
+#define LS_ECHO 0x10000000 // Echo
+#define LS_TEST 0x11000000 // Test
+#define LS_RRQ 0x12000000 // Reinstate Rec. Qual.
+#define LS_PRLI 0x20000000 // Process Login
+#define LS_PRLO 0x21000000 // Process Logout
+#define LS_TPRLO 0x24000000 // 3rd Party Process Logout
+#define LS_PDISC 0x50000000 // Process Discovery
+#define LS_FDISC 0x51000000 // Fabric Discovery
+#define LS_ADISC 0x52000000 // Discover Address
+#define LS_RNC 0x53000000 // Report Node Capability
+#define LS_SCR 0x62000000 // State Change Registration
+#define LS_MASK 0xFF000000
+
+//
+// TYPE Bit Masks
+//
+#define BASIC_LINK_SERVICE 0x00000000
+#define EXT_LINK_SERVICE 0x01000000
+
+#define LLC 0x04000000
+#define LLC_SNAP 0x05000000
+#define SCSI_FCP 0x08000000
+#define SCSI_GPP 0x09000000
+#define IPI3_MASTER 0x11000000
+#define IPI3_SLAVE 0x12000000
+#define IPI3_PEER 0x13000000
+#define CP_IPI3_MASTER 0x15000000
+#define CP_IPI3_SLAVE 0x16000000
+#define CP_IPI3_PEER 0x17000000
+#define SBCCS_CHANNEL 0x19000000
+#define SBCCS_CONTROL 0x1A000000
+#define FIBRE_SERVICES 0x20000000
+#define FC_FG 0x21000000
+#define FC_XS 0x22000000
+#define FC_AL 0x23000000
+#define SNMP 0x24000000
+#define HIPPI_FP 0x40000000
+#define TYPE_MASK 0xFF000000
+
+typedef struct {
+ UCHAR seq_id_valid;
+ UCHAR seq_id;
+ USHORT reserved; // 2 bytes reserved
+ ULONG ox_rx_id;
+ USHORT low_seq_cnt;
+ USHORT high_seq_cnt;
+} BA_ACC_PAYLOAD;
+
+typedef struct {
+ UCHAR reserved;
+ UCHAR reason_code;
+ UCHAR reason_explain;
+ UCHAR vendor_unique;
+} BA_RJT_PAYLOAD;
+
+
+typedef struct {
+ ULONG command_code;
+ ULONG sid;
+ USHORT ox_id;
+ USHORT rx_id;
+} RRQ_MESSAGE;
+
+typedef struct {
+ ULONG command_code;
+ UCHAR vendor;
+ UCHAR explain;
+ UCHAR reason;
+ UCHAR reserved;
+} REJECT_MESSAGE;
+
+
+#define N_OR_F_PORT 0x1000
+#define RANDOM_RELATIVE_OFFSET 0x4000
+#define CONTINUOSLY_INCREASING 0x8000
+
+#define CLASS_VALID 0x8000
+#define INTERMIX_MODE 0x4000
+#define TRANSPARENT_STACKED 0x2000
+#define LOCKDOWN_STACKED 0x1000
+#define SEQ_DELIVERY 0x800
+
+#define XID_NOT_SUPPORTED 0x00
+#define XID_SUPPORTED 0x4000
+#define XID_REQUIRED 0xC000
+
+#define ASSOCIATOR_NOT_SUPPORTED 0x00
+#define ASSOCIATOR_SUPPORTED 0x1000
+#define ASSOCIATOR_REQUIRED 0x3000
+
+#define INIT_ACK0_SUPPORT 0x800
+#define INIT_ACKN_SUPPORT 0x400
+
+#define RECIP_ACK0_SUPPORT 0x8000
+#define RECIP_ACKN_SUPPORT 0x4000
+
+#define X_ID_INTERLOCK 0x2000
+
+#define ERROR_POLICY 0x1800 // Error Policy Supported
+#define ERROR_DISCARD 0x00 // Only Discard Supported
+#define ERROR_DISC_PROCESS 0x02 // Discard and process supported
+
+#define NODE_ID 0x01
+#define IEEE_EXT 0x20
+
+//
+// Categories Supported Per Sequence
+//
+#define CATEGORIES_PER_SEQUENCE 0x300
+#define ONE_CATEGORY_SEQUENCE 0x00 // 1 Category per Sequence
+#define TWO_CATEGORY_SEQUENCE 0x01 // 2 Categories per Sequence
+#define MANY_CATEGORY_SEQUENCE 0x03 // > 2 Categories/Sequence
+
+typedef struct {
+
+ USHORT initiator_control;
+ USHORT service_options;
+
+ USHORT rx_data_size;
+ USHORT recipient_control;
+
+ USHORT ee_credit;
+ USHORT concurrent_sequences;
+
+ USHORT reserved;
+ USHORT open_sequences;
+
+} CLASS_PARAMETERS;
+
+typedef struct {
+ ULONG login_cmd;
+ //
+ // Common Service Parameters
+ //
+ struct {
+
+ USHORT bb_credit;
+ UCHAR lowest_ver;
+ UCHAR highest_ver;
+
+ USHORT bb_rx_size;
+ USHORT common_features;
+
+ USHORT rel_offset;
+ USHORT concurrent_seq;
+
+
+ ULONG e_d_tov;
+ } cmn_services;
+
+ //
+ // Port Name
+ //
+ UCHAR port_name[8];
+
+ //
+ // Node/Fabric Name
+ //
+ UCHAR node_name[8];
+
+ //
+ // Class 1, 2 and 3 Service Parameters
+ //
+ CLASS_PARAMETERS class1;
+ CLASS_PARAMETERS class2;
+ CLASS_PARAMETERS class3;
+
+ ULONG reserved[4];
+
+ //
+ // Vendor Version Level
+ //
+ UCHAR vendor_id[2];
+ UCHAR vendor_version[6];
+ ULONG buffer_size;
+ USHORT rxid_start;
+ USHORT total_rxids;
+} LOGIN_PAYLOAD;
+
+
+typedef struct
+{
+ ULONG cmd; // 4 bytes
+ UCHAR n_port_identifier[3];
+ UCHAR reserved;
+ UCHAR port_name[8];
+} LOGOUT_PAYLOAD;
+
+
+//
+// PRLI Request Service Parameter Defines
+//
+#define PRLI_ACC 0x01
+#define PRLI_REQ 0x02
+#define ORIG_PROCESS_ASSOC_VALID 0x8000
+#define RESP_PROCESS_ASSOC_VALID 0x4000
+#define ESTABLISH_PAIR 0x2000
+#define DATA_OVERLAY_ALLOWED 0x40
+#define INITIATOR_FUNCTION 0x20
+#define TARGET_FUNCTION 0x10
+#define CMD_DATA_MIXED 0x08
+#define DATA_RESP_MIXED 0x04
+#define READ_XFER_RDY 0x02
+#define WRITE_XFER_RDY 0x01
+
+#define RESPONSE_CODE_MASK 0xF00
+#define REQUEST_EXECUTED 0x100
+#define NO_RESOURCES 0x200
+#define INIT_NOT_COMPLETE 0x300
+#define IMAGE_DOES_NOT_EXIST 0x400
+#define BAD_PREDEFINED_COND 0x500
+#define REQ_EXEC_COND 0x600
+#define NO_MULTI_PAGE 0x700
+
+typedef struct {
+ USHORT payload_length;
+ UCHAR page_length;
+ UCHAR cmd;
+
+
+ ULONG valid;
+
+ ULONG orig_process_associator;
+
+ ULONG resp_process_associator;
+
+ ULONG fcp_info;
+} PRLI_REQUEST;
+
+typedef struct {
+
+ USHORT payload_length;
+ UCHAR page_length;
+ UCHAR cmd;
+
+ ULONG valid;
+ ULONG orig_process_associator;
+
+ ULONG resp_process_associator;
+ ULONG reserved;
+} PRLO_REQUEST;
+
+typedef struct {
+ ULONG cmd;
+
+ ULONG hard_address;
+
+ UCHAR port_name[8];
+
+ UCHAR node_name[8];
+
+ ULONG s_id;
+} ADISC_PAYLOAD;
+
+struct ext_sg_entry_t {
+ __u32 len:18; /* buffer length, bits 0-17 */
+ __u32 uba:13; /* upper bus address bits 18-31 */
+ __u32 lba; /* lower bus address bits 0-31 */
+};
+
+
+// J. McCarty's LINK.H
+//
+// LS_RJT Reason Codes
+//
+
+#define INVALID_COMMAND_CODE 0x01
+#define LOGICAL_ERROR 0x03
+#define LOGICAL_BUSY 0x05
+#define PROTOCOL_ERROR 0x07
+#define UNABLE_TO_PERFORM 0x09
+#define COMMAND_NOT_SUPPORTED 0x0B
+#define LS_VENDOR_UNIQUE 0xFF
+
+//
+// LS_RJT Reason Codes Explanations
+//
+#define NO_REASON 0x00
+#define OPTIONS_ERROR 0x01
+#define INITIATOR_CTL_ERROR 0x03
+#define RECIPIENT_CTL_ERROR 0x05
+#define DATA_FIELD_SIZE_ERROR 0x07
+#define CONCURRENT_SEQ_ERROR 0x09
+#define CREDIT_ERROR 0x0B
+#define INVALID_PORT_NAME 0x0D
+#define INVALID_NODE_NAME 0x0E
+#define INVALID_CSP 0x0F // Invalid Service Parameters
+#define INVALID_ASSOC_HDR 0x11 // Invalid Association Header
+#define ASSOC_HDR_REQUIRED 0x13 // Association Header Required
+#define LS_INVALID_S_ID 0x15
+#define INVALID_OX_RX_ID 0x17 // Invalid OX_ID RX_ID Combination
+#define CMD_IN_PROCESS 0x19
+#define INVALID_IDENTIFIER 0x1F // Invalid N_PORT Identifier
+#define INVALID_SEQ_ID 0x21
+#define ABT_INVALID_XCHNG 0x23 // Attempt to Abort an invalid Exchange
+#define ABT_INACTIVE_XCHNG 0x25 // Attempt to Abort an inactive Exchange
+#define NEED_REC_QUAL 0x27 // Recovery Qualifier required
+#define NO_LOGIN_RESOURCES 0x29 // No resources to support login
+#define NO_DATA 0x2A // Unable to supply requested data
+#define REQUEST_NOT_SUPPORTED 0x2C // Request Not Supported
+
+//
+// Link Control Codes
+//
+
+//
+// P_BSY Action Codes
+//
+#define SEQUENCE_TERMINATED 0x01000000
+#define SEQUENCE_ACTIVE 0x02000000
+
+//
+// P_BSY Reason Codes
+//
+#define PHYS_NPORT_BUSY 0x010000
+#define NPORT_RESOURCE_BUSY 0x020000
+
+//
+// P_RJT, F_RJT Action Codes
+//
+
+#define RETRYABLE_ERROR 0x01000000
+#define NON_RETRYABLE_ERROR 0x02000000
+
+//
+// P_RJT, F_RJT Reason Codes
+//
+#define INVALID_D_ID 0x010000
+#define INVALID_S_ID 0x020000
+#define NPORT_NOT_AVAIL_TMP 0x030000
+#define NPORT_NOT_AVAIL_PERM 0x040000
+#define CLASS_NOT_SUPPORTED 0x050000
+#define USAGE_ERROR 0x060000
+#define TYPE_NOT_SUPPORTED 0x070000
+#define INVAL_LINK_CONTROL 0x080000
+#define INVAL_R_CTL 0x090000
+#define INVAL_F_CTL 0x0A0000
+#define INVAL_OX_ID 0x0B0000
+#define INVAL_RX_ID 0x0C0000
+#define INVAL_SEQ_ID 0x0D0000
+#define INVAL_DF_CTL 0x0E0000
+#define INVAL_SEQ_CNT 0x0F0000
+#define INVAL_PARAMS 0x100000
+#define EXCHANGE_ERROR 0x110000
+#define LS_PROTOCOL_ERROR 0x120000
+#define INCORRECT_LENGTH 0x130000
+#define UNEXPECTED_ACK 0x140000
+#define LOGIN_REQ 0x160000
+#define EXCESSIVE_SEQ 0x170000
+#define NO_EXCHANGE 0x180000
+#define SEC_HDR_NOT_SUPPORTED 0x190000
+#define NO_FABRIC 0x1A0000
+#define P_VENDOR_UNIQUE 0xFF0000
+
+//
+// BA_RJT Reason Codes
+//
+#define BA_INVALID_COMMAND 0x00010000
+#define BA_LOGICAL_ERROR 0x00030000
+#define BA_LOGICAL_BUSY 0x00050000
+#define BA_PROTOCOL_ERROR 0x00070000
+#define BA_UNABLE_TO_PERFORM 0x00090000
+
+//
+// BA_RJT Reason Explanation Codes
+//
+#define BA_NO_REASON 0x00000000
+#define BA_INVALID_OX_RX 0x00000300
+#define BA_SEQUENCE_ABORTED 0x00000500
+
+
+
+#endif /* CPQFCTSSTRUCTS_H */
+
diff --git a/drivers/scsi/cpqfcTStrigger.c b/drivers/scsi/cpqfcTStrigger.c
new file mode 100644
index 000000000000..dbb7e65159a0
--- /dev/null
+++ b/drivers/scsi/cpqfcTStrigger.c
@@ -0,0 +1,33 @@
+// Routine to trigger Finisar GTA analyzer. Runs of GPIO2
+// NOTE: DEBUG ONLY! Could interfere with FCMNGR/Miniport operation
+// since it writes directly to the Tachyon board. This function
+// developed for Compaq HBA Tachyon TS v1.2 (Rev X5 PCB)
+
+#include "cpqfcTStrigger.h"
+#if TRIGGERABLE_HBA
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+void TriggerHBA( void* IOBaseUpper, int Print)
+{
+ __u32 long value;
+
+ // get initial value in hopes of not modifying any other GPIO line
+ IOBaseUpper += 0x188; // TachTL/TS Control reg
+
+ value = readl( IOBaseUpper);
+ // set HIGH to trigger external analyzer (tested on Dolche Finisar 1Gb GTA)
+ // The Finisar anaylzer triggers on low-to-high TTL transition
+ value |= 0x01; // set bit 0
+
+ writel( value, IOBaseUpper);
+
+ if( Print)
+ printk( " -GPIO0 set- ");
+}
+
+#endif
diff --git a/drivers/scsi/cpqfcTStrigger.h b/drivers/scsi/cpqfcTStrigger.h
new file mode 100644
index 000000000000..c961792e6be0
--- /dev/null
+++ b/drivers/scsi/cpqfcTStrigger.h
@@ -0,0 +1,8 @@
+// don't do this unless you have the right hardware!
+#define TRIGGERABLE_HBA 0
+#if TRIGGERABLE_HBA
+void TriggerHBA( void*, int);
+#else
+#define TriggerHBA(x, y)
+#endif
+
diff --git a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c
new file mode 100644
index 000000000000..a5fd7427e9da
--- /dev/null
+++ b/drivers/scsi/cpqfcTSworker.c
@@ -0,0 +1,6516 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * Written by Don Zimmerman
+*/
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/smp_lock.h>
+#include <linux/pci.h>
+
+#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h> // struct Scsi_Host definition for T handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+#include "cpqfcTStrigger.h"
+
+//#define LOGIN_DBG 1
+
+// REMARKS:
+// Since Tachyon chips may be permitted to wait from 500ms up to 2 sec
+// to empty an outgoing frame from its FIFO to the Fibre Channel stream,
+// we cannot do everything we need to in the interrupt handler. Specifically,
+// every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be
+// suspended until the login sequences have been completed. Login commands
+// are frames just like SCSI commands are frames; they are subject to the same
+// timeout issues and delays. Also, various specs provide up to 2 seconds for
+// devices to log back in (i.e. respond with ACC to a login frame), so I/O to
+// that device has to be suspended.
+// A serious problem here occurs on highly loaded FC-AL systems. If our FC port
+// has a low priority (e.g. high arbitrated loop physical address, alpa), and
+// some other device is hogging bandwidth (permissible under FC-AL), we might
+// time out thinking the link is hung, when it's simply busy. Many such
+// considerations complicate the design. Although Tachyon assumes control
+// (in silicon) for many link-specific issues, the Linux driver is left with the
+// rest, which turns out to be a difficult, time critical chore.
+
+// These "worker" functions will handle things like FC Logins; all
+// processes with I/O to our device must wait for the Login to complete
+// and (if successful) I/O to resume. In the event of a malfunctioning or
+// very busy loop, it may take hundreds of millisecs or even seconds to complete
+// a frame send. We don't want to hang up the entire server (and all
+// processes which don't depend on Fibre) during this wait.
+
+// The Tachyon chip can have around 30,000 I/O operations ("exchanges")
+// open at one time. However, each exchange must be initiated
+// synchronously (i.e. each of the 30k I/O had to be started one at a
+// time by sending a starting frame via Tachyon's outbound que).
+
+// To accommodate kernel "module" build, this driver limits the exchanges
+// to 256, because of the contiguous physical memory limitation of 128M.
+
+// Typical FC Exchanges are opened presuming the FC frames start without errors,
+// while Exchange completion is handled in the interrupt handler. This
+// optimizes performance for the "everything's working" case.
+// However, when we have FC related errors or hot plugging of FC ports, we pause
+// I/O and handle FC-specific tasks in the worker thread. These FC-specific
+// functions will handle things like FC Logins and Aborts. As the Login sequence
+// completes to each and every target, I/O can resume to that target.
+
+// Our kernel "worker thread" must share the HBA with threads calling
+// "queuecommand". We define a "BoardLock" semaphore which indicates
+// to "queuecommand" that the HBA is unavailable, and Cmnds are added to a
+// board lock Q. When the worker thread finishes with the board, the board
+// lock Q commands are completed with status causing immediate retry.
+// Typically, the board is locked while Logins are in progress after an
+// FC Link Down condition. When Cmnds are re-queued after board lock, the
+// particular Scsi channel/target may or may not have logged back in. When
+// the device is waiting for login, the "prli" flag is clear, in which case
+// commands are passed to a Link Down Q. Whenever the login finally completes,
+// the LinkDown Q is completed, again with status causing immediate retry.
+// When FC devices are logged in, we build and start FC commands to the
+// devices.
+
+// NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices
+// that never log back in (e.g. physically removed) is NOT completely
+// understood. I've still seen instances of system hangs on failed Write
+// commands (possibly from the ext2 layer?) on device removal. Such special
+// cases need to be evaluated from a system/application view - e.g., how
+// exactly does the system want me to complete commands when the device is
+// physically removed??
+
+// local functions
+
+static void SetLoginFields(
+ PFC_LOGGEDIN_PORT pLoggedInPort,
+ TachFCHDR_GCMND* fchs,
+ BOOLEAN PDisc,
+ BOOLEAN Originator);
+
+static void AnalyzeIncomingFrame(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG QNdx );
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds );
+
+static int verify_PLOGI( PTACHYON fcChip,
+ TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+
+static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type);
+static void BuildLinkServicePayload(
+ PTACHYON fcChip, ULONG type, void* payload);
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID);
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata);
+
+static void RevalidateSEST( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void IssueReportLunsCommand(
+ CPQFCHBA* cpqfcHBAdata,
+ TachFCHDR_GCMND* fchs);
+
+// (see scsi_error.c comments on kernel task creation)
+
+void cpqfcTSWorkerThread( void *host)
+{
+ struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+#ifdef PCI_KERNEL_TRACE
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+#endif
+ DECLARE_MUTEX_LOCKED(fcQueReady);
+ DECLARE_MUTEX_LOCKED(fcTYOBcomplete);
+ DECLARE_MUTEX_LOCKED(TachFrozen);
+ DECLARE_MUTEX_LOCKED(BoardLock);
+
+ ENTER("WorkerThread");
+
+ lock_kernel();
+ daemonize("cpqfcTS_wt_%d", HostAdapter->host_no);
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+
+
+ cpqfcHBAdata->fcQueReady = &fcQueReady; // primary wait point
+ cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete;
+ cpqfcHBAdata->TachFrozen = &TachFrozen;
+
+
+ cpqfcHBAdata->worker_thread = current;
+
+ unlock_kernel();
+
+ if( cpqfcHBAdata->notify_wt != NULL )
+ up( cpqfcHBAdata->notify_wt); // OK to continue
+
+ while(1)
+ {
+ unsigned long flags;
+
+ down_interruptible( &fcQueReady); // wait for something to do
+
+ if (signal_pending(current) )
+ break;
+
+ PCI_TRACE( 0x90)
+ // first, take the IO lock so the SCSI upper layers can't call
+ // into our _quecommand function (this also disables INTs)
+ spin_lock_irqsave( HostAdapter->host_lock, flags); // STOP _que function
+ PCI_TRACE( 0x90)
+
+ CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+ // next, set this pointer to indicate to the _quecommand function
+ // that the board is in use, so it should que the command and
+ // immediately return (we don't actually require the semaphore function
+ // in this driver rev)
+
+ cpqfcHBAdata->BoardLock = &BoardLock;
+
+ PCI_TRACE( 0x90)
+
+ // release the IO lock (and re-enable interrupts)
+ spin_unlock_irqrestore( HostAdapter->host_lock, flags);
+
+ // disable OUR HBA interrupt (keep them off as much as possible
+ // during error recovery)
+ disable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+ // OK, let's process the Fibre Channel Link Q and do the work
+ cpqfcTS_WorkTask( HostAdapter);
+
+ // hopefully, no more "work" to do;
+ // re-enable our INTs for "normal" completion processing
+ enable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+
+ cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued
+ CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+
+ // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+ CompleteBoardLockCmnd( cpqfcHBAdata);
+
+
+ }
+ // hopefully, the signal was for our module exit...
+ if( cpqfcHBAdata->notify_wt != NULL )
+ up( cpqfcHBAdata->notify_wt); // yep, we're outta here
+}
+
+
+// Freeze Tachyon routine.
+// If Tachyon is already frozen, return FALSE
+// If Tachyon is not frozen, call freeze function, return TRUE
+//
+static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ BOOLEAN FrozeTach = FALSE;
+ // It's possible that the chip is already frozen; if so,
+ // "Freezing" again will NOT! generate another Freeze
+ // Completion Message.
+
+ if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000)
+ { // (need to freeze...)
+ fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+ // 2. Get Tach freeze confirmation
+ // (synchronize SEST manipulation with Freeze Completion Message)
+ // we need INTs on so semaphore can be set.
+ enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore
+ down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem.
+ // can we TIMEOUT semaphore wait?? TBD
+ disable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+ FrozeTach = TRUE;
+ } // (else, already frozen)
+
+ return FrozeTach;
+}
+
+
+
+
+// This is the kernel worker thread task, which processes FC
+// tasks which were queued by the Interrupt handler or by
+// other WorkTask functions.
+
+#define DBG 1
+//#undef DBG
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG QconsumerNdx;
+ LONG ExchangeID;
+ ULONG ulStatus=0;
+ TachFCHDR_GCMND fchs;
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+
+ ENTER("WorkTask");
+
+ // copy current index to work on
+ QconsumerNdx = fcLQ->consumer;
+
+ PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90)
+
+
+ // NOTE: when this switch completes, we will "consume" the Que item
+// printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type);
+ switch( fcLQ->Qitem[QconsumerNdx].Type )
+ {
+ // incoming frame - link service (ACC, UNSOL REQ, etc.)
+ // or FCP-SCSI command
+ case SFQ_UNKNOWN:
+ AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx );
+
+ break;
+
+
+
+ case EXCHANGE_QUEUED: // an Exchange (i.e. FCP-SCSI) was previously
+ // Queued because the link was down. The
+ // heartbeat timer detected it and Queued it here.
+ // We attempt to start it again, and if
+ // successful we clear the EXCHANGE_Q flag.
+ // If the link doesn't come up, the Exchange
+ // will eventually time-out.
+
+ ExchangeID = (LONG) // x_ID copied from DPC timeout function
+ fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+
+ // It's possible that a Q'd exchange could have already
+ // been started by other logic (e.g. ABTS process)
+ // Don't start if already started (Q'd flag clear)
+
+ if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED )
+ {
+// printk(" *Start Q'd x_ID %Xh: type %Xh ",
+// ExchangeID, Exchanges->fcExchange[ExchangeID].type);
+
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID);
+ if( !ulStatus )
+ {
+// printk("success* ");
+ }
+ else
+ {
+#ifdef DBG
+
+ if( ulStatus == EXCHANGE_QUEUED)
+ printk("Queued* ");
+ else
+ printk("failed* ");
+
+#endif
+ }
+ }
+ break;
+
+
+ case LINKDOWN:
+ // (lots of things already done in INT handler) future here?
+ break;
+
+
+ case LINKACTIVE: // Tachyon set the Lup bit in FM status
+ // NOTE: some misbehaving FC ports (like Tach2.1)
+ // can re-LIP immediately after a LIP completes.
+
+ // if "initiator", need to verify LOGs with ports
+// printk("\n*LNKUP* ");
+
+ if( fcChip->Options.initiator )
+ SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data
+ // if SendLogins successfully completes, PortDiscDone
+ // will be set.
+
+
+ // If SendLogins was successful, then we expect to get incoming
+ // ACCepts or REJECTs, which are handled below.
+
+ break;
+
+ // LinkService and Fabric request/reply processing
+ case ELS_FDISC: // need to send Fabric Discovery (Login)
+ case ELS_FLOGI: // need to send Fabric Login
+ case ELS_SCR: // need to send State Change Registration
+ case FCS_NSR: // need to send Name Service Request
+ case ELS_PLOGI: // need to send PLOGI
+ case ELS_ACC: // send generic ACCept
+ case ELS_PLOGI_ACC: // need to send ELS ACCept frame to recv'd PLOGI
+ case ELS_PRLI_ACC: // need to send ELS ACCept frame to recv'd PRLI
+ case ELS_LOGO: // need to send ELS LOGO (logout)
+ case ELS_LOGO_ACC: // need to send ELS ACCept frame to recv'd PLOGI
+ case ELS_RJT: // ReJecT reply
+ case ELS_PRLI: // need to send ELS PRLI
+
+
+// printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type);
+ // if PortDiscDone is not set, it means the SendLogins routine
+ // failed to complete -- assume that LDn occurred, so login frames
+ // are invalid
+ if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+ {
+ printk("Discard Q'd ELS login frame\n");
+ break;
+ }
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI
+ (TachFCHDR_GCMND*)
+ fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+
+ }
+ }
+
+ else // Xchange setup failed...
+ printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+
+ break;
+
+ case SCSI_REPORT_LUNS:
+ // pass the incoming frame (actually, it's a PRLI frame)
+ // so we can send REPORT_LUNS, in order to determine VSA/PDU
+ // FCP-SCSI Lun address mode
+ IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*)
+ fcLQ->Qitem[QconsumerNdx].ulBuff);
+
+ break;
+
+
+
+
+ case BLS_ABTS: // need to ABORT one or more exchanges
+ {
+ LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+ BOOLEAN FrozeTach = FALSE;
+
+ if ( x_ID >= TACH_SEST_LEN ) // (in)sanity check
+ {
+// printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID);
+ break;
+ }
+
+
+ if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE
+ {
+// printk(" ABTS %Xh Scsi Cmnd null! ", x_ID);
+
+ break; // nothing to abort!
+ }
+
+//#define ABTS_DBG
+#ifdef ABTS_DBG
+ printk("INV SEST[%X] ", x_ID);
+ if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT)
+ {
+ printk("FC2TO");
+ }
+ if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)
+ {
+ printk("IA");
+ }
+ if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED)
+ {
+ printk("PORTID");
+ }
+ if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED)
+ {
+ printk("DEVRM");
+ }
+ if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX)
+ {
+ printk("LKF");
+ }
+ if( Exchanges->fcExchange[x_ID].status & FRAME_TO)
+ {
+ printk("FRMTO");
+ }
+ if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY)
+ {
+ printk("ABSQ");
+ }
+ if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME)
+ {
+ printk("SFQFR");
+ }
+
+ if( Exchanges->fcExchange[ x_ID].type == 0x2000)
+ printk(" WR");
+ else if( Exchanges->fcExchange[ x_ID].type == 0x3000)
+ printk(" RD");
+ else if( Exchanges->fcExchange[ x_ID].type == 0x10)
+ printk(" ABTS");
+ else
+ printk(" %Xh", Exchanges->fcExchange[ x_ID].type);
+
+ if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT))
+ {
+ printk(" Cmd %p, ",
+ Exchanges->fcExchange[ x_ID].Cmnd);
+
+ printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n",
+ cpqfcHBAdata->HBAnum,
+ Exchanges->fcExchange[ x_ID].Cmnd->channel,
+ Exchanges->fcExchange[ x_ID].Cmnd->target,
+ Exchanges->fcExchange[ x_ID].Cmnd->lun,
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+ }
+ else // assume that Cmnd ptr is invalid on _abort()
+ {
+ printk(" Cmd ptr invalid\n");
+ }
+
+#endif
+
+
+ // Steps to ABORT a SEST exchange:
+ // 1. Freeze TL SCSI assists & ERQ (everything)
+ // 2. Receive FROZEN inbound CM (must succeed!)
+ // 3. Invalidate x_ID SEST entry
+ // 4. Resume TL SCSI assists & ERQ (everything)
+ // 5. Build/start on exchange - change "type" to BLS_ABTS,
+ // timeout to X sec (RA_TOV from PLDA is actually 0)
+ // 6. Set Exchange Q'd status if ABTS cannot be started,
+ // or simply complete Exchange in "Terminate" condition
+
+ PCI_TRACEO( x_ID, 0xB4)
+
+ // 1 & 2 . Freeze Tach & get confirmation of freeze
+ FrozeTach = FreezeTach( cpqfcHBAdata);
+
+ // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange.
+ // FC2_TIMEOUT means we are originating the abort, while
+ // TARGET_ABORT means we are ACCepting an abort.
+ // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are
+ // all from Tachyon:
+ // Exchange was corrupted by LDn or other FC physical failure
+ // INITIATOR_ABORT means the upper layer driver/application
+ // requested the abort.
+
+
+
+ // clear bit 31 (VALid), to invalidate & take control from TL
+ fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
+
+
+ // examine and Tach's "Linked List" for IWEs that
+ // received (nearly) simultaneous transfer ready (XRDY)
+ // repair linked list if necessary (TBD!)
+ // (If we ignore the "Linked List", we will time out
+ // WRITE commands where we received the FCP-SCSI XFRDY
+ // frame (because Tachyon didn't processes it). Linked List
+ // management should be done as an optimization.
+
+// readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST ));
+
+
+
+
+ // 4. Resume all Tachlite functions (for other open Exchanges)
+ // as quickly as possible to allow other exchanges to other ports
+ // to resume. Freezing Tachyon may cause cascading errors, because
+ // any received SEST frame cannot be processed by the SEST.
+ // Don't "unfreeze" unless Link is operational
+ if( FrozeTach ) // did we just freeze it (above)?
+ fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+
+ PCI_TRACEO( x_ID, 0xB4)
+
+ // Note there is no confirmation that the chip is "unfrozen". Also,
+ // if the Link is down when unfreeze is called, it has no effect.
+ // Chip will unfreeze when the Link is back up.
+
+ // 5. Now send out Abort commands if possible
+ // Some Aborts can't be "sent" (Port_id changed or gone);
+ // if the device is gone, there is no port_id to send the ABTS to.
+
+ if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED)
+ &&
+ !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) )
+ {
+ Exchanges->fcExchange[ x_ID].type = BLS_ABTS;
+ fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id;
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ BLS_ABTS,
+ &fchs, // (uses only s_id)
+ NULL, // (no scatter/gather list for ABTS)
+ &x_ID );// ABTS on this Exchange ID
+
+ if( !ulStatus ) // Exchange setup build OK?
+ {
+
+ // ABTS may be needed because an Exchange was corrupted
+ // by a Link disruption. If the Link is UP, we can
+ // presume that this ABTS can start immediately; otherwise,
+ // set Que'd status so the Login functions
+ // can restart it when the FC physical Link is restored
+ if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init?
+ {
+// printk(" *set Q status x_ID %Xh on LDn* ", x_ID);
+ Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED;
+ }
+
+ else // what FC device (port_id) does the Cmd belong to?
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort =
+ Exchanges->fcExchange[ x_ID].pLoggedInPort;
+
+ // if Port is logged in, we might start the abort.
+
+ if( (pLoggedInPort != NULL)
+ &&
+ (pLoggedInPort->prli == TRUE) )
+ {
+ // it's possible that an Exchange has already been Queued
+ // to start after Login completes. Check and don't
+ // start it (again) here if Q'd status set
+// printk(" ABTS xchg %Xh ", x_ID);
+ if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED)
+ {
+// printk("already Q'd ");
+ }
+ else
+ {
+// printk("starting ");
+
+ fcChip->fcStats.FC2aborted++;
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+ if( !ulStatus )
+ {
+ // OK
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ }
+ else
+ {
+/* printk("ABTS exchange start failed -status %Xh, x_ID %Xh ",
+ ulStatus, x_ID);
+*/
+ }
+ }
+ }
+ else
+ {
+/* printk(" ABTS NOT starting xchg %Xh, %p ",
+ x_ID, pLoggedInPort);
+ if( pLoggedInPort )
+ printk("prli %d ", pLoggedInPort->prli);
+*/
+ }
+ }
+ }
+ else // what the #@!
+ { // how do we fail to build an Exchange for ABTS??
+ printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n",
+ ulStatus, x_ID);
+ }
+ }
+ else // abort without ABTS -- just complete exchange/Cmnd to Linux
+ {
+// printk(" *Terminating x_ID %Xh on %Xh* ",
+// x_ID, Exchanges->fcExchange[x_ID].status);
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, x_ID);
+
+ }
+ } // end of ABTS case
+ break;
+
+
+
+ case BLS_ABTS_ACC: // need to ACCept one ABTS
+ // (NOTE! this code not updated for Linux yet..)
+
+
+ printk(" *ABTS_ACC* ");
+ // 1. Freeze TL
+
+ fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+ memcpy( // copy the incoming ABTS frame
+ &fchs,
+ fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+ sizeof( fchs));
+
+ // 3. OK, Tachyon is frozen so we can invalidate SEST entry
+ // (if necessary)
+ // Status FC2_TIMEOUT means we are originating the abort, while
+ // TARGET_ABORT means we are ACCepting an abort
+
+ ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange
+// printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID);
+
+
+ // sanity check on received ExchangeID
+ if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT )
+ {
+ // clear bit 31 (VALid), to invalidate & take control from TL
+// printk("Invalidating SEST exchange %Xh\n", ExchangeID);
+ fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF;
+ }
+
+
+ // 4. Resume all Tachlite functions (for other open Exchanges)
+ // as quickly as possible to allow other exchanges to other ports
+ // to resume. Freezing Tachyon for too long may royally screw
+ // up everything!
+ fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+ // Note there is no confirmation that the chip is "unfrozen". Also,
+ // if the Link is down when unfreeze is called, it has no effect.
+ // Chip will unfreeze when the Link is back up.
+
+ // 5. Now send out Abort ACC reply for this exchange
+ Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC;
+
+ fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id;
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ BLS_ABTS_ACC,
+ &fchs,
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+
+ }
+ }
+ break;
+
+
+ case BLS_ABTS_RJT: // need to ReJecT one ABTS; reject implies the
+ // exchange doesn't exist in the TARGET context.
+ // ExchangeID has to come from LinkService space.
+
+ printk(" *ABTS_RJT* ");
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ BLS_ABTS_RJT,
+ (TachFCHDR_GCMND*)
+ fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup OK?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ // If it fails, we aren't required to retry.
+ }
+ if( ulStatus )
+ {
+ printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+ }
+ else
+ {
+ printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+
+ }
+
+ break;
+
+
+
+ default:
+ break;
+ } // end switch
+//doNothing:
+ // done with this item - now set the NEXT index
+
+ if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test
+ {
+ fcLQ->consumer = 0;
+ }
+ else
+ {
+ fcLQ->consumer++;
+ }
+
+ PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94)
+
+ LEAVE("WorkTask");
+ return;
+}
+
+
+
+
+// When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login)
+// commands come in, post to the LinkQ so that action can be taken outside the
+// interrupt handler.
+// This circular Q works like Tachyon's que - the producer points to the next
+// (unused) entry. Called by Interrupt handler, WorkerThread, Timer
+// sputlinkq
+void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+// FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+ ULONG ndx;
+
+ ENTER("cpqfcTSPutLinkQ");
+
+ ndx = fcLQ->producer;
+
+ ndx += 1; // test for Que full
+
+
+
+ if( ndx >= FC_LINKQ_DEPTH ) // rollover test
+ ndx = 0;
+
+ if( ndx == fcLQ->consumer ) // QUE full test
+ {
+ // QUE was full! lost LK command (fatal to logic)
+ fcChip->fcStats.lnkQueFull++;
+
+ printk("*LinkQ Full!*");
+ TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+/*
+ {
+ int i;
+ printk("LinkQ PI %d, CI %d\n", fcLQ->producer,
+ fcLQ->consumer);
+
+ for( i=0; i< FC_LINKQ_DEPTH; )
+ {
+ printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type);
+ if( (++i %8) == 0) printk("\n");
+ }
+
+ }
+*/
+ printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung
+ }
+ else // QUE next element
+ {
+ // Prevent certain multiple (back-to-back) requests.
+ // This is important in that we don't want to issue multiple
+ // ABTS for the same Exchange, or do multiple FM inits, etc.
+ // We can never be sure of the timing of events reported to
+ // us by Tach's IMQ, which can depend on system/bus speeds,
+ // FC physical link circumstances, etc.
+
+ if( (fcLQ->producer != fcLQ->consumer)
+ &&
+ (Type == FMINIT) )
+ {
+ LONG lastNdx; // compute previous producer index
+ if( fcLQ->producer)
+ lastNdx = fcLQ->producer- 1;
+ else
+ lastNdx = FC_LINKQ_DEPTH-1;
+
+
+ if( fcLQ->Qitem[lastNdx].Type == FMINIT)
+ {
+// printk(" *skip FMINIT Q post* ");
+// goto DoneWithPutQ;
+ }
+
+ }
+
+ // OK, add the Q'd item...
+
+ fcLQ->Qitem[fcLQ->producer].Type = Type;
+
+ memcpy(
+ fcLQ->Qitem[fcLQ->producer].ulBuff,
+ QueContent,
+ sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff));
+
+ fcLQ->producer = ndx; // increment Que producer
+
+ // set semaphore to wake up Kernel (worker) thread
+ //
+ up( cpqfcHBAdata->fcQueReady );
+ }
+
+//DoneWithPutQ:
+
+ LEAVE("cpqfcTSPutLinkQ");
+}
+
+
+
+
+// reset device ext FC link Q
+void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata)
+
+{
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+ fcLQ->producer = 0;
+ fcLQ->consumer = 0;
+
+}
+
+
+
+
+
+// When Tachyon gets an unassisted FCP-SCSI frame, post here so
+// an arbitrary context thread (e.g. IOCTL loopback test function)
+// can process it.
+
+// (NOTE: Not revised for Linux)
+// This Q works like Tachyon's que - the producer points to the next
+// (unused) entry.
+void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent)
+{
+// CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+// PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+// ULONG ndx;
+
+// ULONG *pExchangeID;
+// LONG ExchangeID;
+
+/*
+ KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock);
+ ndx = pDevExt->fcScsiQue.producer + 1; // test for Que full
+
+ if( ndx >= FC_SCSIQ_DEPTH ) // rollover test
+ ndx = 0;
+
+ if( ndx == pDevExt->fcScsiQue.consumer ) // QUE full test
+ {
+ // QUE was full! lost LK command (fatal to logic)
+ fcChip->fcStats.ScsiQueFull++;
+#ifdef DBG
+ printk( "fcPutScsiQue - FULL!\n");
+#endif
+
+ }
+ else // QUE next element
+ {
+ pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type;
+
+ if( Type == FCP_RSP )
+ {
+ // this TL inbound message type means that a TL SEST exchange has
+ // copied an FCP response frame into a buffer pointed to by the SEST
+ // entry. That buffer is allocated in the SEST structure at ->RspHDR.
+ // Copy the RspHDR for use by the Que handler.
+ pExchangeID = (ULONG *)QueContent;
+
+ memcpy(
+ pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+ &fcChip->SEST->RspHDR[ *pExchangeID ],
+ sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size)
+
+ }
+ else
+ {
+ memcpy(
+ pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+ QueContent,
+ sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff));
+ }
+
+ pDevExt->fcScsiQue.producer = ndx; // increment Que
+
+
+ KeSetEvent( &pDevExt->TYIBscsi, // signal any waiting thread
+ 0, // no priority boost
+ FALSE ); // no waiting later for this event
+ }
+ KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock);
+*/
+}
+
+
+
+
+
+
+
+static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pFcPort)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric
+ {
+ fcChip->fcStats.logouts++;
+ printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n",
+ (ULONG)pFcPort->u.liWWN,
+ (ULONG)(pFcPort->u.liWWN >>32),
+ pFcPort->port_id);
+
+ // Terminate I/O with this (Linux) Scsi target
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pFcPort->ScsiNexus,
+ DEVICE_REMOVED);
+ }
+
+ // Do an "implicit logout" - we can't really Logout the device
+ // (i.e. with LOGOut Request) because of port_id confusion
+ // (i.e. the Other port has no port_id).
+ // A new login for that WWN will have to re-write port_id (0 invalid)
+ pFcPort->port_id = 0; // invalid!
+ pFcPort->pdisc = FALSE;
+ pFcPort->prli = FALSE;
+ pFcPort->plogi = FALSE;
+ pFcPort->flogi = FALSE;
+ pFcPort->LOGO_timer = 0;
+ pFcPort->device_blocked = TRUE; // block Scsi Requests
+ pFcPort->ScsiNexus.VolumeSetAddressing=0;
+}
+
+
+// On FC-AL, there is a chance that a previously known device can
+// be quietly removed (e.g. with non-managed hub),
+// while a NEW device (with different WWN) took the same alpa or
+// even 24-bit port_id. This chance is unlikely but we must always
+// check for it.
+static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ // set "other port" at beginning of fcPorts list
+ PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort;
+ while( pOtherPortWithPortId )
+ {
+ if( (pOtherPortWithPortId->port_id ==
+ pLoggedInPort->port_id)
+ &&
+ (pOtherPortWithPortId != pLoggedInPort) )
+ {
+ // trouble! (Implicitly) Log the other guy out
+ printk(" *port_id %Xh is duplicated!* ",
+ pOtherPortWithPortId->port_id);
+ cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId);
+ }
+ pOtherPortWithPortId = pOtherPortWithPortId->pNextPort;
+ }
+}
+
+
+
+
+
+
+// Dynamic Memory Allocation for newly discovered FC Ports.
+// For simplicity, maintain fcPorts structs for ALL
+// for discovered devices, including those we never do I/O with
+// (e.g. Fabric addresses)
+
+static PFC_LOGGEDIN_PORT CreateFcPort(
+ CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pLastLoggedInPort,
+ TachFCHDR_GCMND* fchs,
+ LOGIN_PAYLOAD* plogi)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL;
+ int i;
+
+
+ printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id);
+ for( i=3; i>=0; i--) // copy the LOGIN port's WWN
+ printk("%02X", plogi->port_name[i]);
+ for( i=7; i>3; i--) // copy the LOGIN port's WWN
+ printk("%02X", plogi->port_name[i]);
+
+
+ // allocate mem for new port
+ // (these are small and rare allocations...)
+ pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC );
+
+
+ // allocation succeeded? Fill out NEW PORT
+ if( pNextLoggedInPort )
+ {
+ // clear out any garbage (sometimes exists)
+ memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT));
+
+
+ // If we login to a Fabric, we don't want to treat it
+ // as a SCSI device...
+ if( (fchs->s_id & 0xFFF000) != 0xFFF000)
+ {
+ int i;
+
+ // create a unique "virtual" SCSI Nexus (for now, just a
+ // new target ID) -- we will update channel/target on REPORT_LUNS
+ // special case for very first SCSI target...
+ if( cpqfcHBAdata->HostAdapter->max_id == 0)
+ {
+ pNextLoggedInPort->ScsiNexus.target = 0;
+ fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub"
+ }
+ else
+ {
+ pNextLoggedInPort->ScsiNexus.target =
+ cpqfcHBAdata->HostAdapter->max_id;
+ }
+
+ // initialize the lun[] Nexus struct for lun masking
+ for( i=0; i< CPQFCTS_MAX_LUN; i++)
+ pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED
+
+ pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port
+
+ printk(" SCSI Chan/Trgt %d/%d",
+ pNextLoggedInPort->ScsiNexus.channel,
+ pNextLoggedInPort->ScsiNexus.target);
+
+ // tell Scsi layers about the new target...
+ cpqfcHBAdata->HostAdapter->max_id++;
+// printk("HostAdapter->max_id = %d\n",
+// cpqfcHBAdata->HostAdapter->max_id);
+ }
+ else
+ {
+ // device is NOT SCSI (in case of Fabric)
+ pNextLoggedInPort->ScsiNexus.target = -1; // invalid
+ }
+
+ // create forward link to new port
+ pLastLoggedInPort->pNextPort = pNextLoggedInPort;
+ printk("\n");
+
+ }
+ return pNextLoggedInPort; // NULL on allocation failure
+} // end NEW PORT (WWN) logic
+
+
+
+// For certain cases, we want to terminate exchanges without
+// sending ABTS to the device. Examples include when an FC
+// device changed it's port_id after Loop re-init, or when
+// the device sent us a logout. In the case of changed port_id,
+// we want to complete the command and return SOFT_ERROR to
+// force a re-try. In the case of LOGOut, we might return
+// BAD_TARGET if the device is really gone.
+// Since we must ensure that Tachyon is not operating on the
+// exchange, we have to freeze the chip
+// sterminateex
+void cpqfcTSTerminateExchange(
+ CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG x_ID;
+
+ if( ScsiNexus )
+ {
+// printk("TerminateExchange: ScsiNexus chan/target %d/%d\n",
+// ScsiNexus->channel, ScsiNexus->target);
+
+ }
+
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+ if( Exchanges->fcExchange[x_ID].type ) // in use?
+ {
+ if( ScsiNexus == NULL ) // our HBA changed - term. all
+ {
+ Exchanges->fcExchange[x_ID].status = TerminateStatus;
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID );
+ }
+ else
+ {
+ // If a device, according to WWN, has been removed, it's
+ // port_id may be used by another working device, so we
+ // have to terminate by SCSI target, NOT port_id.
+ if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress?
+ {
+ if( (Exchanges->fcExchange[x_ID].Cmnd->device->id == ScsiNexus->target)
+ &&
+ (Exchanges->fcExchange[x_ID].Cmnd->device->channel == ScsiNexus->channel))
+ {
+ Exchanges->fcExchange[x_ID].status = TerminateStatus;
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out
+ }
+ }
+
+ // (in case we ever need it...)
+ // all SEST structures have a remote node ID at SEST DWORD 2
+ // if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8)
+ // == port_id)
+ }
+ }
+ }
+}
+
+
+static void ProcessELS_Request(
+ CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+// FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+// ULONG ox_id = (fchs->ox_rx_id >>16);
+ PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort;
+ BOOLEAN NeedReject = FALSE;
+ ULONG ls_reject_code = 0; // default don'n know??
+
+
+ // Check the incoming frame for a supported ELS type
+ switch( fchs->pl[0] & 0xFFFF)
+ {
+ case 0x0050: // PDISC?
+
+ // Payload for PLOGI and PDISC is identical (request & reply)
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+
+ // PDISC payload OK. If critical login fields
+ // (e.g. WWN) matches last login for this port_id,
+ // we may resume any prior exchanges
+ // with the other port
+
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort != NULL) // WWN found (prior login OK)
+ {
+
+ if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+ {
+ // Yes. We were expecting PDISC?
+ if( pLoggedInPort->pdisc )
+ {
+ // Yes; set fields accordingly. (PDISC, not Originator)
+ SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE);
+
+ // send 'ACC' reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+ fchs );
+
+ // OK to resume I/O...
+ }
+ else
+ {
+ printk("Not expecting PDISC (pdisc=FALSE)\n");
+ NeedReject = TRUE;
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+ }
+ }
+ else
+ {
+ if( pLoggedInPort->port_id != 0)
+ {
+ printk("PDISC PortID change: old %Xh, new %Xh\n",
+ pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+ }
+ NeedReject = TRUE;
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+
+ }
+ }
+ else
+ {
+ printk("PDISC Request from unknown WWN\n");
+ NeedReject = TRUE;
+
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME);
+ }
+
+ }
+ else // Payload unacceptable
+ {
+ printk("payload unacceptable\n");
+ NeedReject = TRUE; // reject code already set
+
+ }
+
+ if( NeedReject)
+ {
+ ULONG port_id;
+ // The PDISC failed. Set login struct flags accordingly,
+ // terminate any I/O to this port, and Q a PLOGI
+ if( pLoggedInPort )
+ {
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort->plogi = FALSE;
+
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ port_id = pLoggedInPort->port_id;
+ }
+ else
+ {
+ port_id = fchs->s_id &0xFFFFFF;
+ }
+ fchs->reserved = ls_reject_code; // borrow this (unused) field
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+ }
+
+ break;
+
+
+
+ case 0x0003: // PLOGI?
+
+ // Payload for PLOGI and PDISC is identical (request & reply)
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+ BOOLEAN NeedReject = FALSE;
+
+ // PDISC payload OK. If critical login fields
+ // (e.g. WWN) matches last login for this port_id,
+ // we may resume any prior exchanges
+ // with the other port
+
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort == NULL) // WWN not found -New Port
+ {
+ pLoggedInPort = CreateFcPort(
+ cpqfcHBAdata,
+ pLastLoggedInPort,
+ fchs,
+ &logi);
+ if( pLoggedInPort == NULL )
+ {
+ printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+ // Now Q a LOGOut Request, since we won't be talking to that device
+
+ NeedReject = TRUE;
+
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES);
+
+ }
+ }
+ if( !NeedReject )
+ {
+
+ // OK - we have valid fcPort ptr; set fields accordingly.
+ // (not PDISC, not Originator)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+ // send 'ACC' reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+ fchs );
+ }
+ }
+ else // Payload unacceptable
+ {
+ printk("payload unacceptable\n");
+ NeedReject = TRUE; // reject code already set
+ }
+
+ if( NeedReject)
+ {
+ // The PDISC failed. Set login struct flags accordingly,
+ // terminate any I/O to this port, and Q a PLOGI
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort->plogi = FALSE;
+
+ fchs->reserved = ls_reject_code; // borrow this (unused) field
+
+ // send 'RJT' reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+ }
+
+ // terminate any exchanges with this device...
+ if( pLoggedInPort )
+ {
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ }
+ break;
+
+
+
+ case 0x1020: // PRLI?
+ {
+ BOOLEAN NeedReject = TRUE;
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ (fchs->s_id & 0xFFFFFF), // search linked list for port_id
+ NULL, // DON'T search linked list for WWN
+ NULL); // don't care
+
+ if( pLoggedInPort == NULL )
+ {
+ // huh?
+ printk(" Unexpected PRLI Request -not logged in!\n");
+
+ // set reject reason code
+ ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+
+ // Q a LOGOut here?
+ }
+ else
+ {
+ // verify the PRLI ACC payload
+ if( !verify_PRLI( fchs, &ls_reject_code) )
+ {
+ // PRLI Reply is acceptable; were we expecting it?
+ if( pLoggedInPort->plogi )
+ {
+ // yes, we expected the PRLI ACC (not PDISC; not Originator)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+ // Q an ACCept Reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PRLI_ACC,
+ fchs );
+
+ NeedReject = FALSE;
+ }
+ else
+ {
+ // huh?
+ printk(" (unexpected) PRLI REQEST with plogi FALSE\n");
+
+ // set reject reason code
+ ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+
+ // Q a LOGOut here?
+
+ }
+ }
+ else
+ {
+ printk(" PRLI REQUEST payload failed verify\n");
+ // (reject code set by "verify")
+
+ // Q a LOGOut here?
+ }
+ }
+
+ if( NeedReject )
+ {
+ // Q a ReJecT Reply with reason code
+ fchs->reserved = ls_reject_code;
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+ }
+ }
+ break;
+
+
+
+
+ case 0x0005: // LOGOut?
+ {
+ // was this LOGOUT because we sent a ELS_PDISC to an FC device
+ // with changed (or new) port_id, or does the port refuse
+ // to communicate to us?
+ // We maintain a logout counter - if we get 3 consecutive LOGOuts,
+ // give up!
+ LOGOUT_PAYLOAD logo;
+ BOOLEAN GiveUpOnDevice = FALSE;
+ ULONG ls_reject_code = 0;
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logo.port_name[0], // search linked list for WWN
+ NULL); // don't care about end of list
+
+ if( pLoggedInPort ) // found the device?
+ {
+ // Q an ACC reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_LOGO_ACC, // Q Type
+ fchs ); // device to respond to
+
+ // set login struct fields (LOGO_counter increment)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+ // are we an Initiator?
+ if( fcChip->Options.initiator)
+ {
+ // we're an Initiator, so check if we should
+ // try (another?) login
+
+ // Fabrics routinely log out from us after
+ // getting device info - don't try to log them
+ // back in.
+ if( (fchs->s_id & 0xFFF000) == 0xFFF000 )
+ {
+ ; // do nothing
+ }
+ else if( pLoggedInPort->LOGO_counter <= 3)
+ {
+ // try (another) login (PLOGI request)
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PLOGI, // Q Type
+ fchs );
+
+ // Terminate I/O with "retry" potential
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus,
+ PORTID_CHANGED);
+ }
+ else
+ {
+ printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n",
+ fchs->s_id &&0xFFFFFF);
+ GiveUpOnDevice = TRUE;
+ }
+ }
+ else
+ {
+ GiveUpOnDevice = TRUE;
+ }
+
+
+ if( GiveUpOnDevice == TRUE )
+ {
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus,
+ DEVICE_REMOVED);
+ }
+ }
+ else // we don't know this WWN!
+ {
+ // Q a ReJecT Reply with reason code
+ fchs->reserved = ls_reject_code;
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+ }
+ }
+ break;
+
+
+
+
+ // FABRIC only case
+ case 0x0461: // ELS RSCN (Registered State Change Notification)?
+ {
+ int Ports;
+ int i;
+ __u32 Buff;
+ // Typically, one or more devices have been added to or dropped
+ // from the Fabric.
+ // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997)
+ // The first 32-bit word has a 2-byte Payload Length, which
+ // includes the 4 bytes of the first word. Consequently,
+ // this PL len must never be less than 4, must be a multiple of 4,
+ // and has a specified max value 256.
+ // (Endianess!)
+ Ports = ((fchs->pl[0] >>24) - 4) / 4;
+ Ports = Ports > 63 ? 63 : Ports;
+
+ printk(" RSCN ports: %d\n", Ports);
+ if( Ports <= 0 ) // huh?
+ {
+ // ReJecT the command
+ fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0);
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+
+ break;
+ }
+ else // Accept the command
+ {
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_ACC, // Q Type
+ fchs );
+ }
+
+ // Check the "address format" to determine action.
+ // We have 3 cases:
+ // 0 = Port Address; 24-bit address of affected device
+ // 1 = Area Address; MS 16 bits valid
+ // 2 = Domain Address; MS 8 bits valid
+ for( i=0; i<Ports; i++)
+ {
+ BigEndianSwap( (UCHAR*)&fchs->pl[i+1],(UCHAR*)&Buff, 4);
+ switch( Buff & 0xFF000000)
+ {
+
+ case 0: // Port Address?
+
+ case 0x01000000: // Area Domain?
+ case 0x02000000: // Domain Address
+ // For example, "port_id" 0x201300
+ // OK, let's try a Name Service Request (Query)
+ fchs->s_id = 0xFFFFFC; // Name Server Address
+ cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+
+ break;
+
+
+ default: // huh? new value on version change?
+ break;
+ }
+ }
+ }
+ break;
+
+
+
+
+ default: // don't support this request (yet)
+ // set reject reason code
+ fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM,
+ REQUEST_NOT_SUPPORTED);
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+ break;
+ }
+}
+
+
+static void ProcessELS_Reply(
+ CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ox_id = (fchs->ox_rx_id >>16);
+ ULONG ls_reject_code;
+ PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+
+ // If this is a valid reply, then we MUST have sent a request.
+ // Verify that we can find a valid request OX_ID corresponding to
+ // this reply
+
+
+ if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+ {
+ printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ",
+ ox_id, fchs->ox_rx_id & 0xffff);
+ goto Quit; // exit this routine
+ }
+
+
+ // Is the reply a RJT (reject)?
+ if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply?
+ {
+// ****** REJECT REPLY ********
+ switch( Exchanges->fcExchange[ox_id].type )
+ {
+
+ case ELS_FDISC: // we sent out Fabric Discovery
+ case ELS_FLOGI: // we sent out FLOGI
+
+ printk("RJT received on Fabric Login from %Xh, reason %Xh\n",
+ fchs->s_id, fchs->pl[1]);
+
+ break;
+
+ default:
+ break;
+ }
+
+ goto Done;
+ }
+
+ // OK, we have an ACCept...
+ // What's the ACC type? (according to what we sent)
+ switch( Exchanges->fcExchange[ox_id].type )
+ {
+
+ case ELS_PLOGI: // we sent out PLOGI
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+
+ // login ACC payload acceptable; search for WWN in our list
+ // of fcPorts
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort == NULL) // WWN not found - new port
+ {
+
+ pLoggedInPort = CreateFcPort(
+ cpqfcHBAdata,
+ pLastLoggedInPort,
+ fchs,
+ &logi);
+
+ if( pLoggedInPort == NULL )
+ {
+ printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+ // Now Q a LOGOut Request, since we won't be talking to that device
+
+ goto Done; // exit with error! dropped login frame
+ }
+ }
+ else // WWN was already known. Ensure that any open
+ // exchanges for this WWN are terminated.
+ // NOTE: It's possible that a device can change its
+ // 24-bit port_id after a Link init or Fabric change
+ // (e.g. LIP or Fabric RSCN). In that case, the old
+ // 24-bit port_id may be duplicated, or no longer exist.
+ {
+
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ }
+
+ // We have an fcPort struct - set fields accordingly
+ // not PDISC, originator
+ SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+
+ // We just set a "port_id"; is it duplicated?
+ TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+ // For Fabric operation, we issued PLOGI to 0xFFFFFC
+ // so we can send SCR (State Change Registration)
+ // Check for this special case...
+ if( fchs->s_id == 0xFFFFFC )
+ {
+ // PLOGI ACC was a Fabric response... issue SCR
+ fchs->s_id = 0xFFFFFD; // address for SCR
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs);
+ }
+
+ else
+ {
+ // Now we need a PRLI to enable FCP-SCSI operation
+ // set flags and Q up a ELS_PRLI
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs);
+ }
+ }
+ else
+ {
+ // login payload unacceptable - reason in ls_reject_code
+ // Q up a Logout Request
+ printk("Login Payload unacceptable\n");
+
+ }
+ break;
+
+
+ // PDISC logic very similar to PLOGI, except we never want
+ // to allocate mem for "new" port, and we set flags differently
+ // (might combine later with PLOGI logic for efficiency)
+ case ELS_PDISC: // we sent out PDISC
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+ BOOLEAN NeedLogin = FALSE;
+
+ // login payload acceptable; search for WWN in our list
+ // of (previously seen) fcPorts
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort != NULL) // WWN found?
+ {
+ // WWN has same port_id as last login? (Of course, a properly
+ // working FC device should NEVER ACCept a PDISC if it's
+ // port_id changed, but check just in case...)
+ if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+ {
+ // Yes. We were expecting PDISC?
+ if( pLoggedInPort->pdisc )
+ {
+ int i;
+
+
+ // PDISC expected -- set fields. (PDISC, Originator)
+ SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE);
+
+ // We are ready to resume FCP-SCSI to this device...
+ // Do we need to start anything that was Queued?
+
+ for( i=0; i< TACH_SEST_LEN; i++)
+ {
+ // see if any exchange for this PDISC'd port was queued
+ if( ((fchs->s_id &0xFFFFFF) ==
+ (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF))
+ &&
+ (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED))
+ {
+ fchs->reserved = i; // copy ExchangeID
+// printk(" *Q x_ID %Xh after PDISC* ",i);
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs );
+ }
+ }
+
+ // Complete commands Q'd while we were waiting for Login
+
+ UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+ }
+ else
+ {
+ printk("Not expecting PDISC (pdisc=FALSE)\n");
+ NeedLogin = TRUE;
+ }
+ }
+ else
+ {
+ printk("PDISC PortID change: old %Xh, new %Xh\n",
+ pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+ NeedLogin = TRUE;
+
+ }
+ }
+ else
+ {
+ printk("PDISC ACC from unknown WWN\n");
+ NeedLogin = TRUE;
+ }
+
+ if( NeedLogin)
+ {
+
+ // The PDISC failed. Set login struct flags accordingly,
+ // terminate any I/O to this port, and Q a PLOGI
+ if( pLoggedInPort ) // FC device previously known?
+ {
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_LOGO, // Q Type
+ fchs ); // has port_id to send to
+
+ // There are a variety of error scenarios which can result
+ // in PDISC failure, so as a catchall, add the check for
+ // duplicate port_id.
+ TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+// TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort->plogi = FALSE;
+
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ }
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs );
+ }
+ }
+ else
+ {
+ // login payload unacceptable - reason in ls_reject_code
+ // Q up a Logout Request
+ printk("ERROR: Login Payload unacceptable!\n");
+
+ }
+
+ break;
+
+
+
+ case ELS_PRLI: // we sent out PRLI
+
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ (fchs->s_id & 0xFFFFFF), // search linked list for port_id
+ NULL, // DON'T search linked list for WWN
+ NULL); // don't care
+
+ if( pLoggedInPort == NULL )
+ {
+ // huh?
+ printk(" Unexpected PRLI ACCept frame!\n");
+
+ // Q a LOGOut here?
+
+ goto Done;
+ }
+
+ // verify the PRLI ACC payload
+ if( !verify_PRLI( fchs, &ls_reject_code) )
+ {
+ // PRLI Reply is acceptable; were we expecting it?
+ if( pLoggedInPort->plogi )
+ {
+ // yes, we expected the PRLI ACC (not PDISC; Originator)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+
+ // OK, let's send a REPORT_LUNS command to determine
+ // whether VSA or PDA FCP-LUN addressing is used.
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+
+ // It's possible that a device we were talking to changed
+ // port_id, and has logged back in. This function ensures
+ // that I/O will resume.
+ UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+
+ }
+ else
+ {
+ // huh?
+ printk(" (unexpected) PRLI ACCept with plogi FALSE\n");
+
+ // Q a LOGOut here?
+ goto Done;
+ }
+ }
+ else
+ {
+ printk(" PRLI ACCept payload failed verify\n");
+
+ // Q a LOGOut here?
+ }
+
+ break;
+
+ case ELS_FLOGI: // we sent out FLOGI (Fabric Login)
+
+ // update the upper 16 bits of our port_id in Tachyon
+ // the switch adds those upper 16 bits when responding
+ // to us (i.e. we are the destination_id)
+ fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF);
+ writel( fcChip->Registers.my_al_pa,
+ fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+
+ // now send out a PLOGI to the well known port_id 0xFFFFFC
+ fchs->s_id = 0xFFFFFC;
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs);
+
+ break;
+
+
+ case ELS_FDISC: // we sent out FDISC (Fabric Discovery (Login))
+
+ printk( " ELS_FDISC success ");
+ break;
+
+
+ case ELS_SCR: // we sent out State Change Registration
+ // now we can issue Name Service Request to find any
+ // Fabric-connected devices we might want to login to.
+
+
+ fchs->s_id = 0xFFFFFC; // Name Server Address
+ cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+
+
+ break;
+
+
+ default:
+ printk(" *Discarding unknown ACC frame, xID %04X/%04X* ",
+ ox_id, fchs->ox_rx_id & 0xffff);
+ break;
+ }
+
+
+Done:
+ // Regardless of whether the Reply is valid or not, the
+ // the exchange is done - complete
+ cpqfcTSCompleteExchange(cpqfcHBAdata->PciDev, fcChip, (fchs->ox_rx_id >>16));
+
+Quit:
+ return;
+}
+
+
+
+
+
+
+// **************** Fibre Channel Services **************
+// This is where we process the Directory (Name) Service Reply
+// to know which devices are on the Fabric
+
+static void ProcessFCS_Reply(
+ CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ox_id = (fchs->ox_rx_id >>16);
+// ULONG ls_reject_code;
+// PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+
+ // If this is a valid reply, then we MUST have sent a request.
+ // Verify that we can find a valid request OX_ID corresponding to
+ // this reply
+
+ if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+ {
+ printk(" *Discarding Reply frame, xID %04X/%04X* ",
+ ox_id, fchs->ox_rx_id & 0xffff);
+ goto Quit; // exit this routine
+ }
+
+
+ // OK, we were expecting it. Now check to see if it's a
+ // "Name Service" Reply, and if so force a re-validation of
+ // Fabric device logins (i.e. Start the login timeout and
+ // send PDISC or PLOGI)
+ // (Endianess Byte Swap?)
+ if( fchs->pl[1] == 0x02FC ) // Name Service
+ {
+ // got a new (or NULL) list of Fabric attach devices...
+ // Invalidate current logins
+
+ PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+
+ if( (pLoggedInPort->port_id & 0xFFFF00) // Fabric device?
+ &&
+ (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port
+ {
+ pLoggedInPort->LOGO_timer = 6; // what's the Fabric timeout??
+ // suspend any I/O in progress until
+ // PDISC received...
+ pLoggedInPort->prli = FALSE; // block FCP-SCSI commands
+ }
+
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ }
+
+ if( fchs->pl[2] == 0x0280) // ACCept?
+ {
+ // Send PLOGI or PDISC to these Fabric devices
+ SendLogins( cpqfcHBAdata, &fchs->pl[4] );
+ }
+
+
+ // As of this writing, the only reason to reject is because NO
+ // devices are left on the Fabric. We already started
+ // "logged out" timers; if the device(s) don't come
+ // back, we'll do the implicit logout in the heart beat
+ // timer routine
+ else // ReJecT
+ {
+ // this just means no Fabric device is visible at this instant
+ }
+ }
+
+ // Regardless of whether the Reply is valid or not, the
+ // the exchange is done - complete
+ cpqfcTSCompleteExchange(cpqfcHBAdata->PciDev, fcChip, (fchs->ox_rx_id >>16));
+
+Quit:
+ return;
+}
+
+
+
+
+
+
+
+static void AnalyzeIncomingFrame(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG QNdx )
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+ TachFCHDR_GCMND* fchs =
+ (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff;
+// ULONG ls_reject_code; // reason for rejecting login
+ LONG ExchangeID;
+// FC_LOGGEDIN_PORT *pLoggedInPort;
+ BOOLEAN AbortAccept;
+
+ ENTER("AnalyzeIncomingFrame");
+
+
+
+ switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown
+ {
+
+ case SFQ_UNKNOWN: // unknown frame (e.g. LIP position frame, NOP, etc.)
+
+
+ // ********* FC-4 Device Data/ Fibre Channel Service *************
+ if( ((fchs->d_id &0xF0000000) == 0) // R_CTL (upper nibble) 0x0?
+ &&
+ (fchs->f_ctl & 0x20000000) ) // TYPE 20h is Fibre Channel Service
+ {
+
+ // ************** FCS Reply **********************
+
+ if( (fchs->d_id & 0xff000000L) == 0x03000000L) // (31:23 R_CTL)
+ {
+ ProcessFCS_Reply( cpqfcHBAdata, fchs );
+
+ } // end of FCS logic
+
+ }
+
+
+ // *********** Extended Link Service **************
+
+ else if( fchs->d_id & 0x20000000 // R_CTL 0x2?
+ &&
+ (fchs->f_ctl & 0x01000000) ) // TYPE = 1
+ {
+
+ // these frames are either a response to
+ // something we sent (0x23) or "unsolicited"
+ // frames (0x22).
+
+
+ // **************Extended Link REPLY **********************
+ // R_CTL Solicited Control Reply
+
+ if( (fchs->d_id & 0xff000000L) == 0x23000000L) // (31:23 R_CTL)
+ {
+
+ ProcessELS_Reply( cpqfcHBAdata, fchs );
+
+ } // end of "R_CTL Solicited Control Reply"
+
+
+
+
+ // **************Extended Link REQUEST **********************
+ // (unsolicited commands from another port or task...)
+
+ // R_CTL Ext Link REQUEST
+ else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+ (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame)
+ {
+
+
+
+ ProcessELS_Request( cpqfcHBAdata, fchs );
+
+ }
+
+
+
+ // ************** LILP **********************
+ else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+ (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames)
+
+ {
+ // SANMark specifies that when available, we must use
+ // the LILP frame to determine which ALPAs to send Port Discovery
+ // to...
+
+ if( fchs->pl[0] == 0x0711L) // ELS_PLOGI?
+ {
+// UCHAR *ptr = (UCHAR*)&fchs->pl[1];
+// printk(" %d ALPAs found\n", *ptr);
+ memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs
+ fcChip->Options.LILPin = 1; // our LILPmap is valid!
+ // now post to make Port Discovery happen...
+ cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs);
+ }
+ }
+ }
+
+
+ // ***************** BASIC LINK SERVICE *****************
+
+ else if( fchs->d_id & 0x80000000 // R_CTL:
+ && // Basic Link Service Request
+ !(fchs->f_ctl & 0xFF000000) ) // type=0 for BLS
+ {
+
+ // Check for ABTS (Abort Sequence)
+ if( (fchs->d_id & 0x8F000000) == 0x81000000)
+ {
+ // look for OX_ID, S_ID pair that matches in our
+ // fcExchanges table; if found, reply with ACCept and complete
+ // the exchange
+
+ // Per PLDA, an ABTS is sent by an initiator; therefore
+ // assume that if we have an exhange open to the port who
+ // sent ABTS, it will be the d_id of what we sent.
+ for( ExchangeID = 0, AbortAccept=FALSE;
+ ExchangeID < TACH_SEST_LEN; ExchangeID++)
+ {
+ // Valid "target" exchange 24-bit port_id matches?
+ // NOTE: For the case of handling Intiator AND Target
+ // functions on the same chip, we can have TWO Exchanges
+ // with the same OX_ID -- OX_ID/FFFF for the CMND, and
+ // OX_ID/RX_ID for the XRDY or DATA frame(s). Ideally,
+ // we would like to support ABTS from Initiators or Targets,
+ // but it's not clear that can be supported on Tachyon for
+ // all cases (requires more investigation).
+
+ if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE ||
+ Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE)
+ &&
+ ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+ (fchs->s_id & 0xFFFFFF)) )
+ {
+
+ // target xchnge port_id matches -- how about OX_ID?
+ if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000)
+ == (fchs->ox_rx_id & 0xFFFF0000) )
+ // yes! post ACCept response; will be completed by fcStart
+ {
+ Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT;
+
+ // copy (add) rx_id field for simplified ACCept reply
+ fchs->ox_rx_id =
+ Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id;
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ BLS_ABTS_ACC, // Q Type
+ fchs ); // void QueContent
+ AbortAccept = TRUE;
+ printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n",
+ fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id);
+ break; // ABTS can affect only ONE exchange -exit loop
+ }
+ }
+ } // end of FOR loop
+ if( !AbortAccept ) // can't ACCept ABTS - send Reject
+ {
+ printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n",
+ fchs->ox_rx_id);
+ if( Exchanges->fcExchange[ ExchangeID].type
+ &&
+ !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len
+ & 0x80000000))
+ {
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID);
+ }
+ else
+ {
+ printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n",
+ ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len);
+ }
+ }
+ }
+
+ // Check for BLS {ABTS? (Abort Sequence)} ACCept
+ else if( (fchs->d_id & 0x8F000000) == 0x84000000)
+ {
+ // target has responded with ACC for our ABTS;
+ // complete the indicated exchange with ABORTED status
+ // Make no checks for correct RX_ID, since
+ // all we need to conform ABTS ACC is the OX_ID.
+ // Verify that the d_id matches!
+
+ ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+// printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n",
+// fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff,
+// Exchanges->fcExchange[ExchangeID].status);
+
+
+
+ if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+ {
+ // Does "target" exchange 24-bit port_id match?
+ // (See "NOTE" above for handling Intiator AND Target in
+ // the same device driver)
+ // First, if this is a target response, then we originated
+ // (initiated) it with BLS_ABTS:
+
+ if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+ &&
+ // Second, does the source of this ACC match the destination
+ // of who we originally sent it to?
+ ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+ (fchs->s_id & 0xFFFFFF)) )
+ {
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID );
+ }
+ }
+ }
+ // Check for BLS {ABTS? (Abort Sequence)} ReJecT
+ else if( (fchs->d_id & 0x8F000000) == 0x85000000)
+ {
+ // target has responded with RJT for our ABTS;
+ // complete the indicated exchange with ABORTED status
+ // Make no checks for correct RX_ID, since
+ // all we need to conform ABTS ACC is the OX_ID.
+ // Verify that the d_id matches!
+
+ ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+// printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n",
+// fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff);
+
+ if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+ {
+ // Does "target" exchange 24-bit port_id match?
+ // (See "NOTE" above for handling Intiator AND Target in
+ // the same device driver)
+ // First, if this is a target response, then we originated
+ // (initiated) it with BLS_ABTS:
+
+ if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+ &&
+ // Second, does the source of this ACC match the destination
+ // of who we originally sent it to?
+ ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+ (fchs->s_id & 0xFFFFFF)) )
+ {
+ // YES! NOTE: There is a bug in CPQ's RA-4000 box
+ // where the "reason code" isn't returned in the payload
+ // For now, simply presume the reject is because the target
+ // already completed the exchange...
+
+// printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID);
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID );
+ }
+ }
+ } // end of ABTS check
+ } // end of Basic Link Service Request
+ break;
+
+ default:
+ printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n",
+ fcLQ->Qitem[QNdx].Type,
+ fcLQ->Qitem[QNdx].Type);
+ break;
+ }
+}
+
+
+// Function for Port Discovery necessary after every FC
+// initialization (e.g. LIP).
+// Also may be called if from Fabric Name Service logic.
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds )
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ulStatus=0;
+ TachFCHDR_GCMND fchs; // copy fields for transmission
+ int i;
+ ULONG loginType;
+ LONG ExchangeID;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ __u32 PortIds[ number_of_al_pa];
+ int NumberOfPorts=0;
+
+ // We're going to presume (for now) that our limit of Fabric devices
+ // is the same as the number of alpa on a private loop (126 devices).
+ // (Of course this could be changed to support however many we have
+ // memory for).
+ memset( &PortIds[0], 0, sizeof(PortIds));
+
+ // First, check if this login is for our own Link Initialization
+ // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices
+ // from a switch. If we are logging into Fabric devices, we'll
+ // have a non-NULL FabricPortId pointer
+
+ if( FabricPortIds != NULL) // may need logins
+ {
+ int LastPort=FALSE;
+ i = 0;
+ while( !LastPort)
+ {
+ // port IDs From NSR payload; byte swap needed?
+ BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4);
+
+// printk("FPortId[%d] %Xh ", i, PortIds[i]);
+ if( PortIds[i] & 0x80000000)
+ LastPort = TRUE;
+
+ PortIds[i] &= 0xFFFFFF; // get 24-bit port_id
+ // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge)
+ // erroneously use ALPA 0.
+ if( PortIds[i] ) // need non-zero port_id...
+ i++;
+
+ if( i >= number_of_al_pa ) // (in)sanity check
+ break;
+ FabricPortIds++; // next...
+ }
+
+ NumberOfPorts = i;
+// printk("NumberOf Fabric ports %d", NumberOfPorts);
+ }
+
+ else // need to send logins on our "local" link
+ {
+
+ // are we a loop port? If so, check for reception of LILP frame,
+ // and if received use it (SANMark requirement)
+ if( fcChip->Options.LILPin )
+ {
+ int j=0;
+ // sanity check on number of ALPAs from LILP frame...
+ // For format of LILP frame, see FC-AL specs or
+ // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8)
+ // First byte is number of ALPAs
+ i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0];
+ NumberOfPorts = i;
+// printk(" LILP alpa count %d ", i);
+ while( i > 0)
+ {
+ PortIds[j] = fcChip->LILPmap[1+ j];
+ j++; i--;
+ }
+ }
+ else // have to send login to everybody
+ {
+ int j=0;
+ i = number_of_al_pa;
+ NumberOfPorts = i;
+ while( i > 0)
+ {
+ PortIds[j] = valid_al_pa[j]; // all legal ALPAs
+ j++; i--;
+ }
+ }
+ }
+
+
+ // Now we have a copy of the port_ids (and how many)...
+ for( i = 0; i < NumberOfPorts; i++)
+ {
+ // 24-bit FC Port ID
+ fchs.s_id = PortIds[i]; // note: only 8-bits used for ALPA
+
+
+ // don't log into ourselves (Linux Scsi disk scan will stop on
+ // no TARGET support error on us, and quit trying for rest of devices)
+ if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) )
+ continue;
+
+ // fabric login needed?
+ if( (fchs.s_id == 0) ||
+ (fcChip->Options.fabric == 1) )
+ {
+ fcChip->Options.flogi = 1; // fabric needs longer for login
+ // Do we need FLOGI or FDISC?
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search SCSI Nexus
+ 0xFFFFFC, // search linked list for Fabric port_id
+ NULL, // don't search WWN
+ NULL); // (don't care about end of list)
+
+ if( pLoggedInPort ) // If found, we have prior experience with
+ // this port -- check whether PDISC is needed
+ {
+ if( pLoggedInPort->flogi )
+ {
+ // does the switch support FDISC?? (FLOGI for now...)
+ loginType = ELS_FLOGI; // prior FLOGI still valid
+ }
+ else
+ loginType = ELS_FLOGI; // expired FLOGI
+ }
+ else // first FLOGI?
+ loginType = ELS_FLOGI;
+
+
+ fchs.s_id = 0xFFFFFE; // well known F_Port address
+
+ // Fabrics are not required to support FDISC, and
+ // it's not clear if that helps us anyway, since
+ // we'll want a Name Service Request to re-verify
+ // visible devices...
+ // Consequently, we always want our upper 16 bit
+ // port_id to be zero (we'll be rejected if we
+ // use our prior port_id if we've been plugged into
+ // a different switch port).
+ // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87)
+ // If our ALPA is 55h for instance, we want the FC frame
+ // s_id to be 0x000055, while Tach's my_al_pa register
+ // must be 0x000155, to force an OPN at ALPA 0
+ // (the Fabric port)
+ fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI
+ writel( fcChip->Registers.my_al_pa | 0x0100,
+ fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+ }
+
+ else // not FLOGI...
+ {
+ // should we send PLOGI or PDISC? Check if any prior port_id
+ // (e.g. alpa) completed a PLOGI/PRLI exchange by checking
+ // the pdisc flag.
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search SCSI Nexus
+ fchs.s_id, // search linked list for al_pa
+ NULL, // don't search WWN
+ NULL); // (don't care about end of list)
+
+
+
+ if( pLoggedInPort ) // If found, we have prior experience with
+ // this port -- check whether PDISC is needed
+ {
+ if( pLoggedInPort->pdisc )
+ {
+ loginType = ELS_PDISC; // prior PLOGI and PRLI maybe still valid
+
+ }
+ else
+ loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC
+ }
+ else // never talked to this port_id before
+ loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC
+ }
+
+
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ loginType, // e.g. PLOGI
+ &fchs, // no incoming frame (we are originator)
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup OK?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+
+ if( loginType == ELS_PDISC )
+ {
+ // now, we really shouldn't Revalidate SEST exchanges until
+ // we get an ACC reply from our target and verify that
+ // the target address/WWN is unchanged. However, when a fast
+ // target gets the PDISC, they can send SEST Exchange data
+ // before we even get around to processing the PDISC ACC.
+ // Consequently, we lose the I/O.
+ // To avoid this, go ahead and Revalidate when the PDISC goes
+ // out, anticipating that the ACC will be truly acceptable
+ // (this happens 99.9999....% of the time).
+ // If we revalidate a SEST write, and write data goes to a
+ // target that is NOT the one we originated the WRITE to,
+ // that target is required (FCP-SCSI specs, etc) to discard
+ // our WRITE data.
+
+ // Re-validate SEST entries (Tachyon hardware assists)
+ RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+ //TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+ }
+ }
+ else // give up immediately on error
+ {
+#ifdef LOGIN_DBG
+ printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus );
+#endif
+ break;
+ }
+
+
+ if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc.
+ {
+ ulStatus = LNKDWN_OSLS;
+#ifdef LOGIN_DBG
+ printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id);
+#endif
+ break;
+ }
+ // Check the exchange for bad status (i.e. FrameTimeOut),
+ // and complete on bad status (most likely due to BAD_ALPA)
+ // on LDn, DPC function may already complete (ABORT) a started
+ // exchange, so check type first (type = 0 on complete).
+ if( Exchanges->fcExchange[ExchangeID].status )
+ {
+#ifdef LOGIN_DBG
+ printk("completing x_ID %X on status %Xh\n",
+ ExchangeID, Exchanges->fcExchange[ExchangeID].status);
+#endif
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID);
+ }
+ }
+ else // Xchange setup failed...
+ {
+#ifdef LOGIN_DBG
+ printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+#endif
+ break;
+ }
+ }
+ if( !ulStatus )
+ {
+ // set the event signifying that all ALPAs were sent out.
+#ifdef LOGIN_DBG
+ printk("SendLogins: PortDiscDone\n");
+#endif
+ cpqfcHBAdata->PortDiscDone = 1;
+
+
+ // TL/TS UG, pg. 184
+ // 0x0065 = 100ms for RT_TOV
+ // 0x01f5 = 500ms for ED_TOV
+ fcChip->Registers.ed_tov.value = 0x006501f5L;
+ writel( fcChip->Registers.ed_tov.value,
+ (fcChip->Registers.ed_tov.address));
+
+ // set the LP_TOV back to ED_TOV (i.e. 500 ms)
+ writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+ }
+ else
+ {
+ printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n",
+ ExchangeID, fchs.s_id, ulStatus);
+ }
+ LEAVE("SendLogins");
+
+}
+
+
+// for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi",
+// D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9)
+static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
+{
+ struct Scsi_Host *HostAdapter = Cmnd->device->host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ int LunListLen=0;
+ int i;
+ ULONG x_ID = 0xFFFFFFFF;
+ UCHAR *ucBuff = Cmnd->request_buffer;
+
+// printk("cpqfcTS: ReportLunsDone \n");
+ // first, we need to find the Exchange for this command,
+ // so we can find the fcPort struct to make the indicated
+ // changes.
+ for( i=0; i< TACH_SEST_LEN; i++)
+ {
+ if( Exchanges->fcExchange[i].type // exchange defined?
+ &&
+ (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches?
+
+ {
+ x_ID = i; // found exchange!
+ break;
+ }
+ }
+ if( x_ID == 0xFFFFFFFF)
+ {
+// printk("cpqfcTS: ReportLuns failed - no FC Exchange\n");
+ goto Done; // Report Luns FC Exchange gone;
+ // exchange probably Terminated by Implicit logout
+ }
+
+
+ // search linked list for the port_id we sent INQUIRY to
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus (we will set it)
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( !pLoggedInPort )
+ {
+// printk("cpqfcTS: ReportLuns failed - device gone\n");
+ goto Done; // error! can't find logged in Port
+ }
+ LunListLen = ucBuff[3];
+ LunListLen += ucBuff[2]>>8;
+
+ if( !LunListLen ) // failed
+ {
+ // generically speaking, a soft error means we should retry...
+ if( (Cmnd->result >> 16) == DID_SOFT_ERROR )
+ {
+ if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+ (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+ {
+ TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs;
+ // did we fail because of "check condition, device reset?"
+ // e.g. the device was reset (i.e., at every power up)
+ // retry the Report Luns
+
+ // who are we sending it to?
+ // we know this because we have a copy of the command
+ // frame from the original Report Lun command -
+ // switch the d_id/s_id fields, because the Exchange Build
+ // context is "reply to source".
+
+ fchs->s_id = fchs->d_id; // (temporarily re-use the struct)
+ cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+ }
+ }
+ else // probably, the device doesn't support Report Luns
+ pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0;
+ }
+ else // we have LUN info - check VSA mode
+ {
+ // for now, assume all LUNs will have same addr mode
+ // for VSA, payload byte 8 will be 0x40; otherwise, 0
+ pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8];
+
+ // Since we got a Report Luns answer, set lun masking flag
+ pLoggedInPort->ScsiNexus.LunMasking = 1;
+
+ if( LunListLen > 8*CPQFCTS_MAX_LUN) // We expect CPQFCTS_MAX_LUN max
+ LunListLen = 8*CPQFCTS_MAX_LUN;
+
+/*
+ printk("Device WWN %08X%08X Reports Luns @: ",
+ (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF),
+ (ULONG)(pLoggedInPort->u.liWWN>>32));
+
+ for( i=8; i<LunListLen+8; i+=8)
+ {
+ printk("%02X%02X ", ucBuff[i], ucBuff[i+1] );
+ }
+ printk("\n");
+*/
+
+ // Since the device was kind enough to tell us where the
+ // LUNs are, lets ensure they are contiguous for Linux's
+ // SCSI driver scan, which expects them to start at 0.
+ // Since Linux only supports 8 LUNs, only copy the first
+ // eight from the report luns command
+
+ // e.g., the Compaq RA4x00 f/w Rev 2.54 and above may report
+ // LUNs 4001, 4004, etc., because other LUNs are masked from
+ // this HBA (owned by someone else). We'll make those appear as
+ // LUN 0, 1... to Linux
+ {
+ int j;
+ int AppendLunList = 0;
+ // Walk through the LUN list. The 'j' array number is
+ // Linux's lun #, while the value of .lun[j] is the target's
+ // lun #.
+ // Once we build a LUN list, it's possible for a known device
+ // to go offline while volumes (LUNs) are added. Later,
+ // the device will do another PLOGI ... Report Luns command,
+ // and we must not alter the existing Linux Lun map.
+ // (This will be very rare).
+ for( j=0; j < CPQFCTS_MAX_LUN; j++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[j] != 0xFF )
+ {
+ AppendLunList = 1;
+ break;
+ }
+ }
+ if( AppendLunList )
+ {
+ int k;
+ int FreeLunIndex;
+// printk("cpqfcTS: AppendLunList\n");
+
+ // If we get a new Report Luns, we cannot change
+ // any existing LUN mapping! (Only additive entry)
+ // For all LUNs in ReportLun list
+ // if RL lun != ScsiNexus lun
+ // if RL lun present in ScsiNexus lun[], continue
+ // else find ScsiNexus lun[]==FF and add, continue
+
+ for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[j] != ucBuff[i+1] )
+ {
+ // something changed from the last Report Luns
+ printk(" cpqfcTS: Report Lun change!\n");
+ for( k=0, FreeLunIndex=CPQFCTS_MAX_LUN;
+ k < CPQFCTS_MAX_LUN; k++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[k] == 0xFF)
+ {
+ FreeLunIndex = k;
+ break;
+ }
+ if( pLoggedInPort->ScsiNexus.lun[k] == ucBuff[i+1] )
+ break; // we already masked this lun
+ }
+ if( k >= CPQFCTS_MAX_LUN )
+ {
+ printk(" no room for new LUN %d\n", ucBuff[i+1]);
+ }
+ else if( k == FreeLunIndex ) // need to add LUN
+ {
+ pLoggedInPort->ScsiNexus.lun[k] = ucBuff[i+1];
+// printk("add [%d]->%02d\n", k, pLoggedInPort->ScsiNexus.lun[k]);
+
+ }
+ else
+ {
+ // lun already known
+ }
+ break;
+ }
+ }
+ // print out the new list...
+ for( j=0; j< CPQFCTS_MAX_LUN; j++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[j] == 0xFF)
+ break; // done
+// printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+ }
+ }
+ else
+ {
+// printk("Linux SCSI LUNs[] -> Device LUNs: ");
+ // first time - this is easy
+ for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+ {
+ pLoggedInPort->ScsiNexus.lun[j] = ucBuff[i+1];
+// printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+ }
+// printk("\n");
+ }
+ }
+ }
+
+Done: ;
+}
+
+extern int is_private_data_of_cpqfc(CPQFCHBA *hba, void * pointer);
+extern void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data);
+
+static void
+call_scsi_done(Scsi_Cmnd *Cmnd)
+{
+ CPQFCHBA *hba;
+ hba = (CPQFCHBA *) Cmnd->device->host->hostdata;
+ // Was this command a cpqfc passthru ioctl ?
+ if (Cmnd->sc_request != NULL && Cmnd->device->host != NULL &&
+ Cmnd->device->host->hostdata != NULL &&
+ is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->device->host->hostdata,
+ Cmnd->sc_request->upper_private_data)) {
+ cpqfc_free_private_data(hba,
+ Cmnd->sc_request->upper_private_data);
+ Cmnd->sc_request->upper_private_data = NULL;
+ Cmnd->result &= 0xff00ffff;
+ Cmnd->result |= (DID_PASSTHROUGH << 16); // prevents retry
+ }
+ if (Cmnd->scsi_done != NULL)
+ (*Cmnd->scsi_done)(Cmnd);
+}
+
+// After successfully getting a "Process Login" (PRLI) from an
+// FC port, we want to Discover the LUNs so that we know the
+// addressing type (e.g., FCP-SCSI Volume Set Address, Peripheral
+// Unit Device), and whether SSP (Selective Storage Presentation or
+// Lun Masking) has made the LUN numbers non-zero based or
+// non-contiguous. To remain backward compatible with the SCSI-2
+// driver model, which expects a contiguous LUNs starting at 0,
+// will use the ReportLuns info to map from "device" to "Linux"
+// LUNs.
+static void IssueReportLunsCommand(
+ CPQFCHBA* cpqfcHBAdata,
+ TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ struct scsi_cmnd *Cmnd = NULL;
+ struct scsi_device *ScsiDev = NULL;
+ LONG x_ID;
+ ULONG ulStatus;
+ UCHAR *ucBuff;
+
+ if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+ {
+ printk("Discard Q'd ReportLun command\n");
+ goto Done;
+ }
+
+ // find the device (from port_id) we're talking to
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus
+ fchs->s_id & 0xFFFFFF,
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+ if( pLoggedInPort ) // we'd BETTER find it!
+ {
+
+
+ if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+ goto Done; // forget it - FC device not a "target"
+
+
+ ScsiDev = scsi_get_host_dev (cpqfcHBAdata->HostAdapter);
+ if (!ScsiDev)
+ goto Done;
+
+ Cmnd = scsi_get_command (ScsiDev, GFP_KERNEL);
+ if (!Cmnd)
+ goto Done;
+
+ ucBuff = pLoggedInPort->ReportLunsPayload;
+
+ memset( ucBuff, 0, REPORT_LUNS_PL);
+
+ Cmnd->scsi_done = ScsiReportLunsDone;
+
+ Cmnd->request_buffer = pLoggedInPort->ReportLunsPayload;
+ Cmnd->request_bufflen = REPORT_LUNS_PL;
+
+ Cmnd->cmnd[0] = 0xA0;
+ Cmnd->cmnd[8] = REPORT_LUNS_PL >> 8;
+ Cmnd->cmnd[9] = (UCHAR)REPORT_LUNS_PL;
+ Cmnd->cmd_len = 12;
+
+ Cmnd->device->channel = pLoggedInPort->ScsiNexus.channel;
+ Cmnd->device->id = pLoggedInPort->ScsiNexus.target;
+
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ SCSI_IRE,
+ fchs,
+ Cmnd, // buffer for Report Lun data
+ &x_ID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+
+ }
+ }
+
+ else // Xchange setup failed...
+ printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+ }
+ else // like, we just got a PRLI ACC, and now the port is gone?
+ {
+ printk(" can't send ReportLuns - no login for port_id %Xh\n",
+ fchs->s_id & 0xFFFFFF);
+ }
+
+
+
+Done:
+
+ if (Cmnd)
+ scsi_put_command (Cmnd);
+ if (ScsiDev)
+ scsi_free_host_dev (ScsiDev);
+}
+
+
+
+
+
+
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata)
+{
+ int i;
+ for( i = CPQFCTS_REQ_QUEUE_LEN-1; i>= 0; i--)
+ {
+ if( cpqfcHBAdata->BoardLockCmnd[i] != NULL )
+ {
+ Scsi_Cmnd *Cmnd = cpqfcHBAdata->BoardLockCmnd[i];
+ cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+ Cmnd->result = (DID_SOFT_ERROR << 16); // ask for retry
+// printk(" BoardLockCmnd[%d] %p Complete, chnl/target/lun %d/%d/%d\n",
+// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+ call_scsi_done(Cmnd);
+ }
+ }
+}
+
+
+
+
+
+
+// runs every 1 second for FC exchange timeouts and implicit FC device logouts
+
+void cpqfcTSheartbeat( unsigned long ptr )
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)ptr;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+ ULONG i;
+ unsigned long flags;
+ DECLARE_MUTEX_LOCKED(BoardLock);
+
+ PCI_TRACE( 0xA8)
+
+ if( cpqfcHBAdata->BoardLock) // Worker Task Running?
+ goto Skip;
+
+ // STOP _que function
+ spin_lock_irqsave( cpqfcHBAdata->HostAdapter->host_lock, flags);
+
+ PCI_TRACE( 0xA8)
+
+
+ cpqfcHBAdata->BoardLock = &BoardLock; // stop Linux SCSI command queuing
+
+ // release the IO lock (and re-enable interrupts)
+ spin_unlock_irqrestore( cpqfcHBAdata->HostAdapter->host_lock, flags);
+
+ // Ensure no contention from _quecommand or Worker process
+ CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+
+ PCI_TRACE( 0xA8)
+
+
+ disable_irq( cpqfcHBAdata->HostAdapter->irq); // our IRQ
+
+ // Complete the "bad target" commands (normally only used during
+ // initialization, since we aren't supposed to call "scsi_done"
+ // inside the queuecommand() function). (this is overly contorted,
+ // scsi_done can be safely called from queuecommand for
+ // this bad target case. May want to simplify this later)
+
+ for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+ {
+ if( cpqfcHBAdata->BadTargetCmnd[i] )
+ {
+ Scsi_Cmnd *Cmnd = cpqfcHBAdata->BadTargetCmnd[i];
+ cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+ Cmnd->result = (DID_BAD_TARGET << 16);
+ call_scsi_done(Cmnd);
+ }
+ else
+ break;
+ }
+
+
+ // logged in ports -- re-login check (ports required to verify login with
+ // PDISC after LIP within 2 secs)
+
+ // prevent contention
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, check to see if
+ // time is up!
+ {
+ // Important: we only detect "timeout" condition on TRANSITION
+ // from non-zero to zero
+ if( pLoggedInPort->LOGO_timer ) // time-out "armed"?
+ {
+ if( !(--pLoggedInPort->LOGO_timer) ) // DEC from 1 to 0?
+ {
+ // LOGOUT time! Per PLDA, PDISC hasn't complete in 2 secs, so
+ // issue LOGO request and destroy all I/O with other FC port(s).
+
+/*
+ printk(" ~cpqfcTS heartbeat: LOGOut!~ ");
+ printk("Linux SCSI Chanl/Target %d/%d (port_id %06Xh) WWN %08X%08X\n",
+ pLoggedInPort->ScsiNexus.channel,
+ pLoggedInPort->ScsiNexus.target,
+ pLoggedInPort->port_id,
+ (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF),
+ (ULONG)(pLoggedInPort->u.liWWN>>32));
+
+*/
+ cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+
+ }
+ // else simply decremented - maybe next time...
+ }
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ }
+
+
+
+
+
+ // ************ FC EXCHANGE TIMEOUT CHECK **************
+
+ for( i=0; i< TACH_MAX_XID; i++)
+ {
+ if( Exchanges->fcExchange[i].type ) // exchange defined?
+ {
+
+ if( !Exchanges->fcExchange[i].timeOut ) // time expired
+ {
+ // Set Exchange timeout status
+ Exchanges->fcExchange[i].status |= FC2_TIMEOUT;
+
+ if( i >= TACH_SEST_LEN ) // Link Service Exchange
+ {
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, i); // Don't "abort" LinkService
+ }
+
+ else // SEST Exchange TO -- may post ABTS to Worker Thread Que
+ {
+ // (Make sure we don't keep timing it out; let other functions
+ // complete it or set the timeOut as needed)
+ Exchanges->fcExchange[i].timeOut = 30000; // seconds default
+
+ if( Exchanges->fcExchange[i].type
+ &
+ (BLS_ABTS | BLS_ABTS_ACC ) )
+ {
+ // For BLS_ABTS*, an upper level might still have
+ // an outstanding command waiting for low-level completion.
+ // Also, in the case of a WRITE, we MUST get confirmation
+ // of either ABTS ACC or RJT before re-using the Exchange.
+ // It's possible that the RAID cache algorithm can hang
+ // if we fail to complete a WRITE to a LBA, when a READ
+ // comes later to that same LBA. Therefore, we must
+ // ensure that the target verifies receipt of ABTS for
+ // the exchange
+
+ printk("~TO Q'd ABTS (x_ID %Xh)~ ", i);
+// TriggerHBA( fcChip->Registers.ReMapMemBase);
+
+ // On timeout of a ABTS exchange, check to
+ // see if the FC device has a current valid login.
+ // If so, restart it.
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ Exchanges->fcExchange[i].Cmnd, // find Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ // device exists?
+ if( pLoggedInPort ) // device exists?
+ {
+ if( pLoggedInPort->prli ) // logged in for FCP-SCSI?
+ {
+ // attempt to restart the ABTS
+ printk(" ~restarting ABTS~ ");
+ cpqfcTSStartExchange( cpqfcHBAdata, i );
+
+ }
+ }
+ }
+ else // not an ABTS
+ {
+
+ // We expect the WorkerThread to change the xchng type to
+ // abort and set appropriate timeout.
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i ); // timed-out
+ }
+ }
+ }
+ else // time not expired...
+ {
+ // decrement timeout: 1 or more seconds left
+ --Exchanges->fcExchange[i].timeOut;
+ }
+ }
+ }
+
+
+ enable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+
+ CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+ cpqfcHBAdata->BoardLock = NULL; // Linux SCSI commands may be queued
+
+ // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+ CompleteBoardLockCmnd( cpqfcHBAdata);
+
+
+ // restart the timer to run again (1 sec later)
+Skip:
+ mod_timer( &cpqfcHBAdata->cpqfcTStimer, jiffies + HZ);
+
+ PCI_TRACEO( i, 0xA8)
+ return;
+}
+
+
+// put valid FC-AL physical address in spec order
+static const UCHAR valid_al_pa[]={
+ 0xef, 0xe8, 0xe4, 0xe2,
+ 0xe1, 0xE0, 0xDC, 0xDA,
+ 0xD9, 0xD6, 0xD5, 0xD4,
+ 0xD3, 0xD2, 0xD1, 0xCe,
+ 0xCd, 0xCc, 0xCb, 0xCa,
+ 0xC9, 0xC7, 0xC6, 0xC5,
+ 0xC3, 0xBc, 0xBa, 0xB9,
+ 0xB6, 0xB5, 0xB4, 0xB3,
+ 0xB2, 0xB1, 0xae, 0xad,
+ 0xAc, 0xAb, 0xAa, 0xA9,
+
+ 0xA7, 0xA6, 0xA5, 0xA3,
+ 0x9f, 0x9e, 0x9d, 0x9b,
+ 0x98, 0x97, 0x90, 0x8f,
+ 0x88, 0x84, 0x82, 0x81,
+ 0x80, 0x7c, 0x7a, 0x79,
+ 0x76, 0x75, 0x74, 0x73,
+ 0x72, 0x71, 0x6e, 0x6d,
+ 0x6c, 0x6b, 0x6a, 0x69,
+ 0x67, 0x66, 0x65, 0x63,
+ 0x5c, 0x5a, 0x59, 0x56,
+
+ 0x55, 0x54, 0x53, 0x52,
+ 0x51, 0x4e, 0x4d, 0x4c,
+ 0x4b, 0x4a, 0x49, 0x47,
+ 0x46, 0x45, 0x43, 0x3c,
+ 0x3a, 0x39, 0x36, 0x35,
+ 0x34, 0x33, 0x32, 0x31,
+ 0x2e, 0x2d, 0x2c, 0x2b,
+ 0x2a, 0x29, 0x27, 0x26,
+ 0x25, 0x23, 0x1f, 0x1E,
+ 0x1d, 0x1b, 0x18, 0x17,
+
+ 0x10, 0x0f, 8, 4, 2, 1 }; // ALPA 0 (Fabric) is special case
+
+const int number_of_al_pa = (sizeof(valid_al_pa) );
+
+
+
+// this function looks up an al_pa from the table of valid al_pa's
+// we decrement from the last decimal loop ID, because soft al_pa
+// (our typical case) are assigned with highest priority (and high al_pa)
+// first. See "In-Depth FC-AL", R. Kembel pg. 38
+// INPUTS:
+// al_pa - 24 bit port identifier (8 bit al_pa on private loop)
+// RETURN:
+// Loop ID - serves are index to array of logged in ports
+// -1 - invalid al_pa (not all 8 bit values are legal)
+
+#if (0)
+static int GetLoopID( ULONG al_pa )
+{
+ int i;
+
+ for( i = number_of_al_pa -1; i >= 0; i--) // dec.
+ {
+ if( valid_al_pa[i] == (UCHAR)al_pa ) // take lowest 8 bits
+ return i; // success - found valid al_pa; return decimal LoopID
+ }
+ return -1; // failed - not found
+}
+#endif
+
+extern cpqfc_passthru_private_t *cpqfc_private(Scsi_Request *sr);
+
+// Search the singly (forward) linked list "fcPorts" looking for
+// either the SCSI target (if != -1), port_id (if not NULL),
+// or WWN (if not null), in that specific order.
+// If we find a SCSI nexus (from Cmnd arg), set the SCp.phase
+// field according to VSA or PDU
+// RETURNS:
+// Ptr to logged in port struct if found
+// (NULL if not found)
+// pLastLoggedInPort - ptr to last struct (for adding new ones)
+//
+PFC_LOGGEDIN_PORT fcFindLoggedInPort(
+ PTACHYON fcChip,
+ Scsi_Cmnd *Cmnd, // search linked list for Scsi Nexus (channel/target/lun)
+ ULONG port_id, // search linked list for al_pa, or
+ UCHAR wwn[8], // search linked list for WWN, or...
+ PFC_LOGGEDIN_PORT *pLastLoggedInPort )
+
+{
+ PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+ BOOLEAN target_id_valid=FALSE;
+ BOOLEAN port_id_valid=FALSE;
+ BOOLEAN wwn_valid=FALSE;
+ int i;
+
+
+ if( Cmnd != NULL )
+ target_id_valid = TRUE;
+
+ else if( port_id ) // note! 24-bit NULL address is illegal
+ port_id_valid = TRUE;
+
+ else
+ {
+ if( wwn ) // non-null arg? (OK to pass NULL when not searching WWN)
+ {
+ for( i=0; i<8; i++) // valid WWN passed? NULL WWN invalid
+ {
+ if( wwn[i] != 0 )
+ wwn_valid = TRUE; // any non-zero byte makes (presumably) valid
+ }
+ }
+ }
+ // check other options ...
+
+
+ // In case multiple search options are given, we use a priority
+ // scheme:
+ // While valid pLoggedIn Ptr
+ // If port_id is valid
+ // if port_id matches, return Ptr
+ // If wwn is valid
+ // if wwn matches, return Ptr
+ // Next Ptr in list
+ //
+ // Return NULL (not found)
+
+
+ while( pLoggedInPort ) // NULL marks end of list (1st ptr always valid)
+ {
+ if( pLastLoggedInPort ) // caller's pointer valid?
+ *pLastLoggedInPort = pLoggedInPort; // end of linked list
+
+ if( target_id_valid )
+ {
+ // check Linux Scsi Cmnd for channel/target Nexus match
+ // (all luns are accessed through matching "pLoggedInPort")
+ if( (pLoggedInPort->ScsiNexus.target == Cmnd->device->id)
+ &&
+ (pLoggedInPort->ScsiNexus.channel == Cmnd->device->channel))
+ {
+ // For "passthru" modes, the IOCTL caller is responsible
+ // for setting the FCP-LUN addressing
+ if (Cmnd->sc_request != NULL && Cmnd->device->host != NULL &&
+ Cmnd->device->host->hostdata != NULL &&
+ is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->device->host->hostdata,
+ Cmnd->sc_request->upper_private_data)) {
+ /* This is a passthru... */
+ cpqfc_passthru_private_t *pd;
+ pd = Cmnd->sc_request->upper_private_data;
+ Cmnd->SCp.phase = pd->bus;
+ // Cmnd->SCp.have_data_in = pd->pdrive;
+ Cmnd->SCp.have_data_in = Cmnd->device->lun;
+ } else {
+ /* This is not a passthru... */
+
+ // set the FCP-LUN addressing type
+ Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing;
+
+ // set the Device Type we got from the snooped INQUIRY string
+ Cmnd->SCp.Message = pLoggedInPort->ScsiNexus.InqDeviceType;
+
+ // handle LUN masking; if not "default" (illegal) lun value,
+ // the use it. These lun values are set by a successful
+ // Report Luns command
+ if( pLoggedInPort->ScsiNexus.LunMasking == 1)
+ {
+ if (Cmnd->device->lun > sizeof(pLoggedInPort->ScsiNexus.lun))
+ return NULL;
+ // we KNOW all the valid LUNs... 0xFF is invalid!
+ Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->device->lun];
+ if (pLoggedInPort->ScsiNexus.lun[Cmnd->device->lun] == 0xFF)
+ return NULL;
+ // printk("xlating lun %d to 0x%02x\n", Cmnd->lun,
+ // pLoggedInPort->ScsiNexus.lun[Cmnd->lun]);
+ }
+ else
+ Cmnd->SCp.have_data_in = Cmnd->device->lun; // Linux & target luns match
+ }
+ break; // found it!
+ }
+ }
+
+ if( port_id_valid ) // look for alpa first
+ {
+ if( pLoggedInPort->port_id == port_id )
+ break; // found it!
+ }
+ if( wwn_valid ) // look for wwn second
+ {
+
+ if( !memcmp( &pLoggedInPort->u.ucWWN[0], &wwn[0], 8))
+ {
+ // all 8 bytes of WWN match
+ break; // found it!
+ }
+ }
+
+ pLoggedInPort = pLoggedInPort->pNextPort; // try next port
+ }
+
+ return pLoggedInPort;
+}
+
+
+
+
+//
+// We need to examine the SEST table and re-validate
+// any open Exchanges for this LoggedInPort
+// To make Tachyon pay attention, Freeze FCP assists,
+// set VAL bits, Unfreeze FCP assists
+static void RevalidateSEST( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG x_ID;
+ BOOLEAN TachFroze = FALSE;
+
+
+ // re-validate any SEST exchanges that are permitted
+ // to survive the link down (e.g., good PDISC performed)
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+
+ // If the SEST entry port_id matches the pLoggedInPort,
+ // we need to re-validate
+ if( (Exchanges->fcExchange[ x_ID].type == SCSI_IRE)
+ ||
+ (Exchanges->fcExchange[ x_ID].type == SCSI_IWE))
+ {
+
+ if( (Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF) // (24-bit port ID)
+ == pLoggedInPort->port_id)
+ {
+// printk(" re-val xID %Xh ", x_ID);
+ if( !TachFroze ) // freeze if not already frozen
+ TachFroze |= FreezeTach( cpqfcHBAdata);
+ fcChip->SEST->u[ x_ID].IWE.Hdr_Len |= 0x80000000; // set VAL bit
+ }
+ }
+ }
+
+ if( TachFroze)
+ {
+ fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+ }
+}
+
+
+// Complete an Linux Cmnds that we Queued because
+// our FC link was down (cause immediate retry)
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ Scsi_Cmnd* *SCptr = &cpqfcHBAdata->LinkDnCmnd[0];
+ Scsi_Cmnd *Cmnd;
+ int indx;
+
+
+
+ // if the device was previously "blocked", make sure
+ // we unblock it so Linux SCSI will resume
+
+ pLoggedInPort->device_blocked = FALSE; // clear our flag
+
+ // check the Link Down command ptr buffer;
+ // we can complete now causing immediate retry
+ for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++, SCptr++)
+ {
+ if( *SCptr != NULL ) // scsi command to complete?
+ {
+#ifdef DUMMYCMND_DBG
+ printk("complete Cmnd %p in LinkDnCmnd[%d]\n", *SCptr,indx);
+#endif
+ Cmnd = *SCptr;
+
+
+ // Are there any Q'd commands for this target?
+ if( (Cmnd->device->id == pLoggedInPort->ScsiNexus.target)
+ &&
+ (Cmnd->device->channel == pLoggedInPort->ScsiNexus.channel) )
+ {
+ Cmnd->result = (DID_SOFT_ERROR <<16); // force retry
+ if( Cmnd->scsi_done == NULL)
+ {
+ printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n",
+ pLoggedInPort->port_id);
+ }
+ else
+ call_scsi_done(Cmnd);
+ *SCptr = NULL; // free this slot for next use
+ }
+ }
+ }
+}
+
+
+//#define WWN_DBG 1
+
+static void SetLoginFields(
+ PFC_LOGGEDIN_PORT pLoggedInPort,
+ TachFCHDR_GCMND* fchs,
+ BOOLEAN PDisc,
+ BOOLEAN Originator)
+{
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+ PRLI_REQUEST prli; // copy for BIG ENDIAN switch
+ int i;
+#ifdef WWN_DBG
+ ULONG ulBuff;
+#endif
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort->Originator = Originator;
+ pLoggedInPort->port_id = fchs->s_id & 0xFFFFFF;
+
+ switch( fchs->pl[0] & 0xffff )
+ {
+ case 0x00000002: // PLOGI or PDISC ACCept?
+ if( PDisc ) // PDISC accept
+ goto PDISC_case;
+
+ case 0x00000003: // ELS_PLOGI or ELS_PLOGI_ACC
+
+ // Login BB_credit typically 0 for Tachyons
+ pLoggedInPort->BB_credit = logi.cmn_services.bb_credit;
+
+ // e.g. 128, 256, 1024, 2048 per FC-PH spec
+ // We have to use this when setting up SEST Writes,
+ // since that determines frame size we send.
+ pLoggedInPort->rx_data_size = logi.class3.rx_data_size;
+ pLoggedInPort->plogi = TRUE;
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->logo = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->LOGO_counter = 0;// ELS_PLOGI resets
+ pLoggedInPort->LOGO_timer = 0;// ELS_PLOGI resets
+
+ // was this PLOGI to a Fabric?
+ if( pLoggedInPort->port_id == 0xFFFFFC ) // well know address
+ pLoggedInPort->flogi = TRUE;
+
+
+ for( i=0; i<8; i++) // copy the LOGIN port's WWN
+ pLoggedInPort->u.ucWWN[i] = logi.port_name[i];
+
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("PLOGI port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh fcPort %p\n", ulBuff, pLoggedInPort);
+#endif
+ break;
+
+
+
+
+ case 0x00000005: // ELS_LOGO (logout)
+
+
+ pLoggedInPort->plogi = FALSE;
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->logo = TRUE; // ELS_PLOGI resets
+ pLoggedInPort->LOGO_counter++; // ELS_PLOGI resets
+ pLoggedInPort->LOGO_timer = 0;
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("LOGO port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh\n", ulBuff);
+#endif
+ break;
+
+
+
+PDISC_case:
+ case 0x00000050: // ELS_PDISC or ELS_PDISC_ACC
+ pLoggedInPort->LOGO_timer = 0; // stop the time-out
+
+ pLoggedInPort->prli = TRUE; // ready to accept FCP-SCSI I/O
+
+
+
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("PDISC port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh\n", ulBuff);
+#endif
+
+
+
+ break;
+
+
+
+ case 0x1020L: // PRLI?
+ case 0x1002L: // PRLI ACCept?
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli));
+
+ pLoggedInPort->fcp_info = prli.fcp_info; // target/initiator flags
+ pLoggedInPort->prli = TRUE; // PLOGI resets, PDISC doesn't
+
+ pLoggedInPort->pdisc = TRUE; // expect to send (or receive) PDISC
+ // next time
+ pLoggedInPort->LOGO_timer = 0; // will be set next LinkDown
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("PRLI port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh\n", ulBuff);
+#endif
+
+ break;
+
+ }
+
+ return;
+}
+
+
+
+
+
+
+static void BuildLinkServicePayload( PTACHYON fcChip, ULONG type, void* payload)
+{
+ LOGIN_PAYLOAD *plogi; // FC-PH Port Login
+ LOGIN_PAYLOAD PlogiPayload; // copy for BIG ENDIAN switch
+ PRLI_REQUEST *prli; // FCP-SCSI Process Login
+ PRLI_REQUEST PrliPayload; // copy for BIG ENDIAN switch
+ LOGOUT_PAYLOAD *logo;
+ LOGOUT_PAYLOAD LogoutPayload;
+// PRLO_REQUEST *prlo;
+// PRLO_REQUEST PrloPayload;
+ REJECT_MESSAGE rjt, *prjt;
+
+ memset( &PlogiPayload, 0, sizeof( PlogiPayload));
+ plogi = &PlogiPayload; // load into stack buffer,
+ // then BIG-ENDIAN switch a copy to caller
+
+
+ switch( type ) // payload type can be ELS_PLOGI, ELS_PRLI, ADISC, ...
+ {
+ case ELS_FDISC:
+ case ELS_FLOGI:
+ case ELS_PLOGI_ACC: // FC-PH PORT Login Accept
+ case ELS_PLOGI: // FC-PH PORT Login
+ case ELS_PDISC: // FC-PH2 Port Discovery - same payload as ELS_PLOGI
+ plogi->login_cmd = LS_PLOGI;
+ if( type == ELS_PDISC)
+ plogi->login_cmd = LS_PDISC;
+ else if( type == ELS_PLOGI_ACC )
+ plogi->login_cmd = LS_ACC;
+
+ plogi->cmn_services.bb_credit = 0x00;
+ plogi->cmn_services.lowest_ver = fcChip->lowest_FCPH_ver;
+ plogi->cmn_services.highest_ver = fcChip->highest_FCPH_ver;
+ plogi->cmn_services.bb_rx_size = TACHLITE_TS_RX_SIZE;
+ plogi->cmn_services.common_features = CONTINUOSLY_INCREASING |
+ RANDOM_RELATIVE_OFFSET;
+
+ // fill in with World Wide Name based Port Name - 8 UCHARs
+ // get from Tach registers WWN hi & lo
+ LoadWWN( fcChip, plogi->port_name, 0);
+ // fill in with World Wide Name based Node/Fabric Name - 8 UCHARs
+ // get from Tach registers WWN hi & lo
+ LoadWWN( fcChip, plogi->node_name, 1);
+
+ // For Seagate Drives.
+ //
+ plogi->cmn_services.common_features |= 0x800;
+ plogi->cmn_services.rel_offset = 0xFE;
+ plogi->cmn_services.concurrent_seq = 1;
+ plogi->class1.service_options = 0x00;
+ plogi->class2.service_options = 0x00;
+ plogi->class3.service_options = CLASS_VALID;
+ plogi->class3.initiator_control = 0x00;
+ plogi->class3.rx_data_size = MAX_RX_PAYLOAD;
+ plogi->class3.recipient_control =
+ ERROR_DISCARD | ONE_CATEGORY_SEQUENCE;
+ plogi->class3.concurrent_sequences = 1;
+ plogi->class3.open_sequences = 1;
+ plogi->vendor_id[0] = 'C'; plogi->vendor_id[1] = 'Q';
+ plogi->vendor_version[0] = 'C'; plogi->vendor_version[1] = 'Q';
+ plogi->vendor_version[2] = ' '; plogi->vendor_version[3] = '0';
+ plogi->vendor_version[4] = '0'; plogi->vendor_version[5] = '0';
+
+
+ // FLOGI specific fields... (see FC-FLA, Rev 2.7, Aug 1999, sec 5.1)
+ if( (type == ELS_FLOGI) || (type == ELS_FDISC) )
+ {
+ if( type == ELS_FLOGI )
+ plogi->login_cmd = LS_FLOGI;
+ else
+ plogi->login_cmd = LS_FDISC;
+
+ plogi->cmn_services.lowest_ver = 0x20;
+ plogi->cmn_services.common_features = 0x0800;
+ plogi->cmn_services.rel_offset = 0;
+ plogi->cmn_services.concurrent_seq = 0;
+
+ plogi->class3.service_options = 0x8800;
+ plogi->class3.rx_data_size = 0;
+ plogi->class3.recipient_control = 0;
+ plogi->class3.concurrent_sequences = 0;
+ plogi->class3.open_sequences = 0;
+ }
+
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&PlogiPayload, payload, sizeof(PlogiPayload));
+ break;
+
+
+ case ELS_ACC: // generic Extended Link Service ACCept
+ plogi->login_cmd = LS_ACC;
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&PlogiPayload, payload, 4);
+ break;
+
+
+
+ case ELS_SCR: // Fabric State Change Registration
+ {
+ SCR_PL scr; // state change registration
+
+ memset( &scr, 0, sizeof(scr));
+
+ scr.command = LS_SCR; // 0x62000000
+ // see FC-FLA, Rev 2.7, Table A.22 (pg 82)
+ scr.function = 3; // 1 = Events detected by Fabric
+ // 2 = N_Port detected registration
+ // 3 = Full registration
+
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&scr, payload, sizeof(SCR_PL));
+ }
+
+ break;
+
+
+ case FCS_NSR: // Fabric Name Service Request
+ {
+ NSR_PL nsr; // Name Server Req. payload
+
+ memset( &nsr, 0, sizeof(NSR_PL));
+
+ // see Brocade Fabric Programming Guide,
+ // Rev 1.3, pg 4-44
+ nsr.CT_Rev = 0x01000000;
+ nsr.FCS_Type = 0xFC020000;
+ nsr.Command_code = 0x01710000;
+ nsr.FCP = 8;
+
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&nsr, payload, sizeof(NSR_PL));
+ }
+
+ break;
+
+
+
+
+ case ELS_LOGO: // FC-PH PORT LogOut
+ logo = &LogoutPayload; // load into stack buffer,
+ // then BIG-ENDIAN switch a copy to caller
+ logo->cmd = LS_LOGO;
+ // load the 3 UCHARs of the node name
+ // (if private loop, upper two UCHARs 0)
+ logo->reserved = 0;
+
+ logo->n_port_identifier[0] = (UCHAR)(fcChip->Registers.my_al_pa);
+ logo->n_port_identifier[1] =
+ (UCHAR)(fcChip->Registers.my_al_pa>>8);
+ logo->n_port_identifier[2] =
+ (UCHAR)(fcChip->Registers.my_al_pa>>16);
+ // fill in with World Wide Name based Port Name - 8 UCHARs
+ // get from Tach registers WWN hi & lo
+ LoadWWN( fcChip, logo->port_name, 0);
+
+ BigEndianSwap( (UCHAR*)&LogoutPayload,
+ payload, sizeof(LogoutPayload) ); // 16 UCHAR struct
+ break;
+
+
+ case ELS_LOGO_ACC: // Logout Accept (FH-PH pg 149, table 74)
+ logo = &LogoutPayload; // load into stack buffer,
+ // then BIG-ENDIAN switch a copy to caller
+ logo->cmd = LS_ACC;
+ BigEndianSwap( (UCHAR*)&LogoutPayload, payload, 4 ); // 4 UCHAR cmnd
+ break;
+
+
+ case ELS_RJT: // ELS_RJT link service reject (FH-PH pg 155)
+
+ prjt = (REJECT_MESSAGE*)payload; // pick up passed data
+ rjt.command_code = ELS_RJT;
+ // reverse fields, because of Swap that follows...
+ rjt.vendor = prjt->reserved; // vendor specific
+ rjt.explain = prjt->reason; //
+ rjt.reason = prjt->explain; //
+ rjt.reserved = prjt->vendor; //
+ // BIG-ENDIAN switch a copy to caller
+ BigEndianSwap( (UCHAR*)&rjt, payload, 8 ); // 8 UCHAR cmnd
+ break;
+
+
+
+
+
+ case ELS_PRLI_ACC: // Process Login ACCept
+ case ELS_PRLI: // Process Login
+ case ELS_PRLO: // Process Logout
+ memset( &PrliPayload, 0, sizeof( PrliPayload));
+ prli = &PrliPayload; // load into stack buffer,
+
+ if( type == ELS_PRLI )
+ prli->cmd = 0x20; // Login
+ else if( type == ELS_PRLO )
+ prli->cmd = 0x21; // Logout
+ else if( type == ELS_PRLI_ACC )
+ {
+ prli->cmd = 0x02; // Login ACCept
+ prli->valid = REQUEST_EXECUTED;
+ }
+
+
+ prli->valid |= SCSI_FCP | ESTABLISH_PAIR;
+ prli->fcp_info = READ_XFER_RDY;
+ prli->page_length = 0x10;
+ prli->payload_length = 20;
+ // Can be initiator AND target
+
+ if( fcChip->Options.initiator )
+ prli->fcp_info |= INITIATOR_FUNCTION;
+ if( fcChip->Options.target )
+ prli->fcp_info |= TARGET_FUNCTION;
+
+ BigEndianSwap( (UCHAR*)&PrliPayload, payload, prli->payload_length);
+ break;
+
+
+
+ default: // no can do - programming error
+ printk(" BuildLinkServicePayload unknown!\n");
+ break;
+ }
+}
+
+// loads 8 UCHARs for PORT name or NODE name base on
+// controller's WWN.
+void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type)
+{
+ UCHAR* bPtr, i;
+
+ switch( type )
+ {
+ case 0: // Port_Name
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+ for( i =0; i<4; i++)
+ dest[i] = *bPtr++;
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+ for( i =4; i<8; i++)
+ dest[i] = *bPtr++;
+ break;
+ case 1: // Node/Fabric _Name
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+ for( i =0; i<4; i++)
+ dest[i] = *bPtr++;
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+ for( i =4; i<8; i++)
+ dest[i] = *bPtr++;
+ break;
+ }
+
+}
+
+
+
+// We check the Port Login payload for required values. Note that
+// ELS_PLOGI and ELS_PDISC (Port DISCover) use the same payload.
+
+
+int verify_PLOGI( PTACHYON fcChip,
+ TachFCHDR_GCMND* fchs,
+ ULONG* reject_explain)
+{
+ LOGIN_PAYLOAD login;
+
+ // source, dest, len (should be mult. of 4)
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&login, sizeof(login));
+
+ // check FC version
+ // if other port's highest supported version
+ // is less than our lowest, and
+ // if other port's lowest
+ if( login.cmn_services.highest_ver < fcChip->lowest_FCPH_ver ||
+ login.cmn_services.lowest_ver > fcChip->highest_FCPH_ver )
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+ return LOGICAL_ERROR;
+ }
+
+ // Receive Data Field Size must be >=128
+ // per FC-PH
+ if (login.cmn_services.bb_rx_size < 128)
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, DATA_FIELD_SIZE_ERROR);
+ return LOGICAL_ERROR;
+ }
+
+ // Only check Class 3 params
+ if( login.class3.service_options & CLASS_VALID)
+ {
+ if (login.class3.rx_data_size < 128)
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INVALID_CSP);
+ return LOGICAL_ERROR;
+ }
+ if( login.class3.initiator_control & XID_REQUIRED)
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INITIATOR_CTL_ERROR);
+ return LOGICAL_ERROR;
+ }
+ }
+ return 0; // success
+}
+
+
+
+
+int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain)
+{
+ PRLI_REQUEST prli; // buffer for BIG ENDIAN
+
+ // source, dest, len (should be mult. of 4)
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli));
+
+ if( prli.fcp_info == 0 ) // i.e., not target or initiator?
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+ return LOGICAL_ERROR;
+ }
+
+ return 0; // success
+}
+
+
+// SWAP UCHARs as required by Fibre Channel (i.e. BIG ENDIAN)
+// INPUTS:
+// source - ptr to LITTLE ENDIAN ULONGS
+// cnt - number of UCHARs to switch (should be mult. of ULONG)
+// OUTPUTS:
+// dest - ptr to BIG ENDIAN copy
+// RETURN:
+// none
+//
+void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt)
+{
+ int i,j;
+
+ source+=3; // start at MSB of 1st ULONG
+ for( j=0; j < cnt; j+=4, source+=4, dest+=4) // every ULONG
+ {
+ for( i=0; i<4; i++) // every UCHAR in ULONG
+ *(dest+i) = *(source-i);
+ }
+}
+
+
+
+
+// Build FC Exchanges............
+
+static void buildFCPstatus(
+ PTACHYON fcChip,
+ ULONG ExchangeID);
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type );
+
+static ULONG build_SEST_sgList(
+ struct pci_dev *pcidev,
+ ULONG *SESTalPairStart,
+ Scsi_Cmnd *Cmnd,
+ ULONG *sgPairs,
+ PSGPAGES *sgPages_head // link list of TL Ext. S/G pages from O/S Pool
+);
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd,
+ UCHAR* payload, ULONG type, ULONG fcp_dl );
+
+
+/*
+ IRB
+ ERQ __________________
+ | | / | Req_A_SFS_Len | ____________________
+ |----------| / | Req_A_SFS_Addr |------->| Reserved |
+ | IRB | / | Req_A_D_ID | | SOF EOF TimeStamp |
+ |-----------/ | Req_A_SEST_Index |-+ | R_CTL | D_ID |
+ | IRB | | Req_B... | | | CS_CTL| S_ID |
+ |-----------\ | | | | TYPE | F_CTL |
+ | IRB | \ | | | | SEQ_ID | SEQ_CNT |
+ |----------- \ | | +-->+--| OX_ID | RX_ID |
+ | | \ |__________________| | | RO |
+ | | pl (payload/cmnd) |
+ | | ..... |
+ | |___________________|
+ |
+ |
++-------------------------------------------+
+|
+|
+| e.g. IWE
+| SEST __________________ for FCP_DATA
+| | | / | | Hdr_Len | ____________________
+| |----------| / | Hdr_Addr_Addr |------->| Reserved |
+| | [0] | / |Remote_ID| RSP_Len| | SOF EOF TimeStamp |
+| |-----------/ | RSP_Addr |---+ | R_CTL | D_ID |
++-> [1] | | | Buff_Off | | | CS_CTL| S_ID |
+ |-----------\ |BuffIndex| Link | | | TYPE | F_CTL |
+ | [2] | \ | Rsvd | RX_ID | | | SEQ_ID | SEQ_CNT |
+ |----------- \ | Data_Len | | | OX_ID | RX_ID |
+ | ... | \ | Exp_RO | | | RO |
+ |----------| | Exp_Byte_Cnt | | |___________________|
+ | SEST_LEN | +--| Len | |
+ |__________| | | Address | |
+ | | ... | | for FCP_RSP
+ | |__________________| | ____________________
+ | +----| Reserved |
+ | | SOF EOF TimeStamp |
+ | | R_CTL | D_ID |
+ | | CS_CTL| S_ID |
+ +--- local or extended | .... |
+ scatter/gather lists
+ defining upper-layer
+ data (e.g. from user's App)
+
+
+*/
+// All TachLite commands must start with a SFS (Single Frame Sequence)
+// command. In the simplest case (a NOP Basic Link command),
+// only one frame header and ERQ entry is required. The most complex
+// case is the SCSI assisted command, which requires an ERQ entry,
+// SEST entry, and several frame headers and data buffers all
+// logically linked together.
+// Inputs:
+// cpqfcHBAdata - controller struct
+// type - PLOGI, SCSI_IWE, etc.
+// InFCHS - Incoming Tachlite FCHS which prompted this exchange
+// (only s_id set if we are originating)
+// Data - PVOID to data struct consistent with "type"
+// fcExchangeIndex - pointer to OX/RD ID value of built exchange
+// Return:
+// fcExchangeIndex - OX/RD ID value if successful
+// 0 - success
+// INVALID_ARGS - NULL/ invalid passed args
+// BAD_ALPA - Bad source al_pa address
+// LNKDWN_OSLS - Link Down (according to this controller)
+// OUTQUE_FULL - Outbound Que full
+// DRIVERQ_FULL - controller's Exchange array full
+// SEST_FULL - SEST table full
+//
+// Remarks:
+// Psuedo code:
+// Check for NULL pointers / bad args
+// Build outgoing FCHS - the header/payload struct
+// Build IRB (for ERQ entry)
+// if SCSI command, build SEST entry (e.g. IWE, TRE,...)
+// return success
+
+//sbuildex
+ULONG cpqfcTSBuildExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG type, // e.g. PLOGI
+ TachFCHDR_GCMND* InFCHS, // incoming FCHS
+ void *Data, // the CDB, scatter/gather, etc.
+ LONG *fcExchangeIndex ) // points to allocated exchange,
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ulStatus = 0; // assume OK
+ USHORT ox_ID, rx_ID=0xFFFF;
+ ULONG SfsLen=0L;
+ TachLiteIRB* pIRB;
+ IRBflags IRB_flags;
+ UCHAR *pIRB_flags = (UCHAR*)&IRB_flags;
+ TachFCHDR_GCMND* CMDfchs;
+ TachFCHDR* dataHDR; // 32 byte HEADER ONLY FCP-DATA buffer
+ TachFCHDR_RSP* rspHDR; // 32 byte header + RSP payload
+ Scsi_Cmnd *Cmnd = (Scsi_Cmnd*)Data; // Linux Scsi CDB, S/G, ...
+ TachLiteIWE* pIWE;
+ TachLiteIRE* pIRE;
+ TachLiteTWE* pTWE;
+ TachLiteTRE* pTRE;
+ ULONG fcp_dl; // total byte length of DATA transferred
+ ULONG fl; // frame length (FC frame size, 128, 256, 512, 1024)
+ ULONG sgPairs; // number of valid scatter/gather pairs
+ int FCP_SCSI_command;
+ BA_ACC_PAYLOAD *ba_acc;
+ BA_RJT_PAYLOAD *ba_rjt;
+
+ // check passed ARGS
+ if( !fcChip->ERQ ) // NULL ptr means uninitialized Tachlite chip
+ return INVALID_ARGS;
+
+
+ if( type == SCSI_IRE ||
+ type == SCSI_TRE ||
+ type == SCSI_IWE ||
+ type == SCSI_TWE)
+ FCP_SCSI_command = 1;
+
+ else
+ FCP_SCSI_command = 0;
+
+
+ // for commands that pass payload data (e.g. SCSI write)
+ // examine command struct - verify that the
+ // length of s/g buffers is adequate for total payload
+ // length (end of list is NULL address)
+
+ if( FCP_SCSI_command )
+ {
+ if( Data ) // must have data descriptor (S/G list -- at least
+ // one address with at least 1 byte of data)
+ {
+ // something to do (later)?
+ }
+
+ else
+ return INVALID_ARGS; // invalid DATA ptr
+ }
+
+
+
+ // we can build an Exchange for later Queuing (on the TL chip)
+ // if an empty slot is available in the DevExt for this controller
+ // look for available Exchange slot...
+
+ if( type != FCP_RESPONSE &&
+ type != BLS_ABTS &&
+ type != BLS_ABTS_ACC ) // already have Exchange slot!
+ *fcExchangeIndex = FindFreeExchange( fcChip, type );
+
+ if( *fcExchangeIndex != -1 ) // Exchange is available?
+ {
+ // assign tmp ptr (shorthand)
+ CMDfchs = &Exchanges->fcExchange[ *fcExchangeIndex].fchs;
+
+ if( Cmnd != NULL ) // (necessary for ABTS cases)
+ {
+ Exchanges->fcExchange[ *fcExchangeIndex].Cmnd = Cmnd; // Linux Scsi
+ Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort =
+ fcFindLoggedInPort( fcChip,
+ Exchanges->fcExchange[ *fcExchangeIndex].Cmnd, // find Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ }
+
+
+ // Build the command frame header (& data) according
+ // to command type
+
+ // fields common for all SFS frame types
+ CMDfchs->reserved = 0L; // must clear
+ CMDfchs->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; LCr=0, no TS
+
+ // get the destination port_id from incoming FCHS
+ // (initialized before calling if we're Originator)
+ // Frame goes to port it was from - the source_id
+
+ CMDfchs->d_id = InFCHS->s_id &0xFFFFFF; // destination (add R_CTL later)
+ CMDfchs->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+ // now enter command-specific fields
+ switch( type )
+ {
+
+ case BLS_NOP: // FC defined basic link service command NO-OP
+ // ensure unique X_IDs! (use tracking function)
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32L; // add len to LSB (header only - no payload)
+
+ // TYPE[31-24] 00 Basic Link Service
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ CMDfchs->d_id |= 0x80000000L; // R_CTL = 80 for NOP (Basic Link Ser.)
+ CMDfchs->f_ctl = 0x00310000L; // xchng originator, 1st seq,....
+ CMDfchs->seq_cnt = 0x0L;
+ CMDfchs->ox_rx_id = 0xFFFF; // RX_ID for now; OX_ID on start
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ CMDfchs->pl[0] = 0xaabbccddL; // words 8-15 frame data payload (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // seconds
+ // (NOP should complete ~instantly)
+ break;
+
+
+
+
+ case BLS_ABTS_ACC: // Abort Sequence ACCept
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload)
+
+ CMDfchs->d_id |= 0x84000000L; // R_CTL = 84 for BASIC ACCept
+ // TYPE[31-24] 00 Basic Link Service
+ // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+ CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI
+ // CMDfchs->seq_id & count might be set from DataHdr?
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+ // (Timeout in case of weird error)
+
+ // now set the ACCept payload...
+ ba_acc = (BA_ACC_PAYLOAD*)&CMDfchs->pl[0];
+ memset( ba_acc, 0, sizeof( BA_ACC_PAYLOAD));
+ // Since PLDA requires (only) entire Exchange aborts, we don't need
+ // to worry about what the last sequence was.
+
+ // We expect that a "target" task is accepting the abort, so we
+ // can use the OX/RX ID pair
+ ba_acc->ox_rx_id = CMDfchs->ox_rx_id;
+
+ // source, dest, #bytes
+ BigEndianSwap((UCHAR *)&CMDfchs->ox_rx_id, (UCHAR *)&ba_acc->ox_rx_id, 4);
+
+ ba_acc->low_seq_cnt = 0;
+ ba_acc->high_seq_cnt = 0xFFFF;
+
+
+ break;
+
+
+ case BLS_ABTS_RJT: // Abort Sequence ACCept
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload)
+
+ CMDfchs->d_id |= 0x85000000L; // R_CTL = 85 for BASIC ReJecT
+ // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+ // TYPE[31-24] 00 Basic Link Service
+ CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI
+ // CMDfchs->seq_id & count might be set from DataHdr?
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+ // (Timeout in case of weird error)
+
+ CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // copy from sender!
+
+ // now set the ReJecT payload...
+ ba_rjt = (BA_RJT_PAYLOAD*)&CMDfchs->pl[0];
+ memset( ba_rjt, 0, sizeof( BA_RJT_PAYLOAD));
+
+ // We expect that a "target" task couldn't find the Exhange in the
+ // array of active exchanges, so we use a new LinkService X_ID.
+ // See Reject payload description in FC-PH (Rev 4.3), pg. 140
+ ba_rjt->reason_code = 0x09; // "unable to perform command request"
+ ba_rjt->reason_explain = 0x03; // invalid OX/RX ID pair
+
+
+ break;
+
+
+ case BLS_ABTS: // FC defined basic link service command ABTS
+ // Abort Sequence
+
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32L; // add len to LSB (header only - no payload)
+
+ // TYPE[31-24] 00 Basic Link Service
+ // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+ CMDfchs->d_id |= 0x81000000L; // R_CTL = 81 for ABTS
+ CMDfchs->f_ctl = 0x00110000L; // xchnge originator, last seq, xfer SI
+ // CMDfchs->seq_id & count might be set from DataHdr?
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+ // (ABTS must timeout when responder is gone)
+ break;
+
+
+
+ case FCS_NSR: // Fabric Name Service Request
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+ // OX_ID, linked to Driver Transaction ID
+ // (fix-up at Queing time)
+ CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+ // OX_ID set at ERQueing time
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += (32L + sizeof(NSR_PL)); // add len (header & NSR payload)
+
+ CMDfchs->d_id |= 0x02000000L; // R_CTL = 02 for -
+ // Name Service Request: Unsolicited
+ // TYPE[31-24] 01 Extended Link Service
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ CMDfchs->f_ctl = 0x20210000L;
+ // OX_ID will be fixed-up at Tachyon enqueing time
+ CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+
+
+
+
+
+ break;
+
+
+
+
+ case ELS_PLOGI: // FC-PH extended link service command Port Login
+ // (May, 2000)
+ // NOTE! This special case facilitates SANMark testing. The SANMark
+ // test script for initialization-timeout.fcal.SANMark-1.fc
+ // "eats" the OPN() primitive without issuing an R_RDY, causing
+ // Tachyon to report LST (loop state timeout), which causes a
+ // LIP. To avoid this, simply send out the frame (i.e. assuming a
+ // buffer credit of 1) without waiting for R_RDY. Many FC devices
+ // (other than Tachyon) have been doing this for years. We don't
+ // ever want to do this for non-Link Service frames unless the
+ // other device really did report non-zero login BB credit (i.e.
+ // in the PLOGI ACCept frame).
+// CMDfchs->sof_eof |= 0x00000400L; // LCr=1
+
+ case ELS_FDISC: // Fabric Discovery (Login)
+ case ELS_FLOGI: // Fabric Login
+ case ELS_SCR: // Fabric State Change Registration
+ case ELS_LOGO: // FC-PH extended link service command Port Logout
+ case ELS_PDISC: // FC-PH extended link service cmnd Port Discovery
+ case ELS_PRLI: // FC-PH extended link service cmnd Process Login
+
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+ // OX_ID, linked to Driver Transaction ID
+ // (fix-up at Queing time)
+ CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+ // OX_ID set at ERQueing time
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ if( type == ELS_LOGO )
+ SfsLen += (32L + 16L); // add len (header & PLOGI payload)
+ else if( type == ELS_PRLI )
+ SfsLen += (32L + 20L); // add len (header & PRLI payload)
+ else if( type == ELS_SCR )
+ SfsLen += (32L + sizeof(SCR_PL)); // add len (header & SCR payload)
+ else
+ SfsLen += (32L + 116L); // add len (header & PLOGI payload)
+
+ CMDfchs->d_id |= 0x22000000L; // R_CTL = 22 for -
+ // Extended Link_Data: Unsolicited Control
+ // TYPE[31-24] 01 Extended Link Service
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ CMDfchs->f_ctl = 0x01210000L;
+ // OX_ID will be fixed-up at Tachyon enqueing time
+ CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+ break;
+
+
+
+ case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+ case ELS_RJT: // extended link service reject (add reason)
+ case ELS_ACC: // ext. link service generic accept
+ case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+ case ELS_PRLI_ACC: // ext. link service process login accept
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // assume done
+ // ensure unique X_IDs! (use tracking function)
+ // OX_ID from initiator cmd
+ ox_ID = (USHORT)(InFCHS->ox_rx_id >> 16);
+ rx_ID = 0xFFFF; // RX_ID, linked to Driver Exchange ID
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ if( type == ELS_RJT )
+ {
+ SfsLen += (32L + 8L); // add len (header + payload)
+
+ // ELS_RJT reason codes (utilize unused "reserved" field)
+ CMDfchs->pl[0] = 1;
+ CMDfchs->pl[1] = InFCHS->reserved;
+
+ }
+ else if( (type == ELS_LOGO_ACC) || (type == ELS_ACC) )
+ SfsLen += (32L + 4L); // add len (header + payload)
+ else if( type == ELS_PLOGI_ACC )
+ SfsLen += (32L + 116L); // add len (header + payload)
+ else if( type == ELS_PRLI_ACC )
+ SfsLen += (32L + 20L); // add len (header + payload)
+
+ CMDfchs->d_id |= 0x23000000L; // R_CTL = 23 for -
+ // Extended Link_Data: Control Reply
+ // TYPE[31-24] 01 Extended Link Service
+ // f_ctl[23:0] exchg responder, last seq, e_s, tsi
+ CMDfchs->f_ctl = 0x01990000L;
+ CMDfchs->seq_cnt = 0x0L;
+ CMDfchs->ox_rx_id = 0L; // clear
+ CMDfchs->ox_rx_id = ox_ID; // load upper 16 bits
+ CMDfchs->ox_rx_id <<= 16; // shift them
+
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+ break;
+
+
+ // Fibre Channel SCSI 'originator' sequences...
+ // (originator means 'initiator' in FCP-SCSI)
+
+ case SCSI_IWE: // TachLite Initiator Write Entry
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort =
+ Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort;
+
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // FC2 timeout
+
+ // first, build FCP_CMND
+ // unique X_ID fix-ups in StartExchange
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index)
+
+ // NOTE: unlike FC LinkService login frames, normal
+ // SCSI commands are sent without outgoing verification
+ IRB_flags.DCM = 1; // Disable completion message for Cmnd frame
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 64L; // add len to LSB (header & CMND payload)
+
+ CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ // valid RO
+ CMDfchs->f_ctl = 0x08210008L;
+ CMDfchs->seq_cnt = 0x0L;
+ CMDfchs->ox_rx_id = 0L; // clear for now (-or- in later)
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ // now, fill out FCP-DATA header
+ // (use buffer inside SEST object)
+ dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+ dataHDR->reserved = 0L; // must clear
+ dataHDR->sof_eof = 0x75002000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS
+ dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+ dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] xfer S.I.| valid RO
+ dataHDR->f_ctl = 0x08010008L;
+ dataHDR->seq_cnt = 0x02000000L; // sequence ID: df_ctl : seqence count
+ dataHDR->ox_rx_id = 0L; // clear; fix-up dataHDR fields later
+ dataHDR->ro = 0x0L; // relative offset (n/a)
+
+ // Now setup the SEST entry
+ pIWE = &fcChip->SEST->u[ *fcExchangeIndex ].IWE;
+
+ // fill out the IWE:
+
+ // VALid entry:Dir outbound:DCM:enable CM:enal INT: FC frame len
+ pIWE->Hdr_Len = 0x8e000020L; // data frame Len always 32 bytes
+
+
+ // from login parameters with other port, what's the largest frame
+ // we can send?
+ if( pLoggedInPort == NULL)
+ {
+ ulStatus = INVALID_ARGS; // failed! give up
+ break;
+ }
+ if( pLoggedInPort->rx_data_size >= 2048)
+ fl = 0x00020000; // 2048 code (only support 1024!)
+ else if( pLoggedInPort->rx_data_size >= 1024)
+ fl = 0x00020000; // 1024 code
+ else if( pLoggedInPort->rx_data_size >= 512)
+ fl = 0x00010000; // 512 code
+ else
+ fl = 0; // 128 bytes -- should never happen
+
+
+ pIWE->Hdr_Len |= fl; // add xmit FC frame len for data phase
+ pIWE->Hdr_Addr = fcChip->SEST->base +
+ ((unsigned long)&fcChip->SEST->DataHDR[*fcExchangeIndex] -
+ (unsigned long)fcChip->SEST);
+
+ pIWE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+ pIWE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+ memset( &fcChip->SEST->RspHDR[ *fcExchangeIndex].pl, 0,
+ sizeof( FCP_STATUS_RESPONSE) ); // clear out previous status
+
+ pIWE->RSP_Addr = fcChip->SEST->base +
+ ((unsigned long)&fcChip->SEST->RspHDR[*fcExchangeIndex] -
+ (unsigned long)fcChip->SEST);
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ cpqfcHBAdata->PciDev,
+ &pIWE->GLen1,
+ Cmnd, // S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+
+ // Now that we know total data length in
+ // the passed S/G buffer, set FCP CMND frame
+ build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pIWE->Buff_Off = 0x78000000L; // extended data | (no offset)
+ else // local data pointers (in SEST)
+ pIWE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+ // ULONG 5
+ pIWE->Link = 0x0000ffffL; // Buff_Index | Link
+
+ pIWE->RX_ID = 0x0L; // DWord 6: RX_ID set by target XFER_RDY
+
+ // DWord 7
+ pIWE->Data_Len = 0L; // TL enters rcv'd XFER_RDY BURST_LEN
+ pIWE->Exp_RO = 0L; // DWord 8
+ // DWord 9
+ pIWE->Exp_Byte_Cnt = fcp_dl; // sum of gather buffers
+ }
+ break;
+
+
+
+
+
+ case SCSI_IRE: // TachLite Initiator Read Entry
+
+ if( Cmnd->timeout != 0)
+ {
+// printk("Cmnd->timeout %d\n", Cmnd->timeout);
+ // per Linux Scsi
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = Cmnd->timeout;
+ }
+ else // use our best guess, based on FC & device
+ {
+
+ if( Cmnd->SCp.Message == 1 ) // Tape device? (from INQUIRY)
+ {
+ // turn off our timeouts (for now...)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 0xFFFFFFFF;
+ }
+ else
+ {
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // per SCSI req.
+ }
+ }
+
+
+ // first, build FCP_CMND
+
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index)
+ // NOTE: unlike FC LinkService login frames,
+ // normal SCSI commands are sent "open loop"
+ IRB_flags.DCM = 1; // Disable completion message for Cmnd frame
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 64L; // add len to LSB (header & CMND payload)
+
+ CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ // valid RO
+ CMDfchs->f_ctl = 0x08210008L;
+ CMDfchs->seq_cnt = 0x0L;
+ // x_ID & data direction bit set later
+ CMDfchs->ox_rx_id = 0xFFFF; // clear
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+
+
+ // Now setup the SEST entry
+ pIRE = &fcChip->SEST->u[ *fcExchangeIndex ].IRE;
+
+ // fill out the IRE:
+ // VALid entry:Dir outbound:enable CM:enal INT:
+ pIRE->Seq_Accum = 0xCE000000L; // VAL,DIR inbound,DCM| INI,DAT,RSP
+
+ pIRE->reserved = 0L;
+ pIRE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+ pIRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+ pIRE->RSP_Addr = fcChip->SEST->base +
+ ((unsigned long)&fcChip->SEST->RspHDR[*fcExchangeIndex] -
+ (unsigned long)fcChip->SEST);
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ cpqfcHBAdata->PciDev,
+ &pIRE->SLen1,
+ Cmnd, // SCSI command Data desc. with S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ // It is permissible to have a ZERO LENGTH Read command.
+ // If there is the case, simply set fcp_dl (and Exp_Byte_Cnt)
+ // to 0 and continue.
+ if( Cmnd->request_bufflen == 0 )
+ {
+ fcp_dl = 0; // no FC DATA frames expected
+
+ }
+ else
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+ }
+
+ // now that we know the S/G length, build CMND payload
+ build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pIRE->Buff_Off = 0x00000000; // DWord 4: extended s/g list, no offset
+ else
+ pIRE->Buff_Off = 0x80000000; // local data, no offset
+
+ pIRE->Buff_Index = 0x0L; // DWord 5: Buff_Index | Reserved
+
+ pIRE->Exp_RO = 0x0L; // DWord 6: Expected Rel. Offset
+
+ pIRE->Byte_Count = 0; // DWord 7: filled in by TL on err
+ pIRE->reserved_ = 0; // DWord 8: reserved
+ // NOTE: 0 length READ is OK.
+ pIRE->Exp_Byte_Cnt = fcp_dl;// DWord 9: sum of scatter buffers
+
+ break;
+
+
+
+
+ // Fibre Channel SCSI 'responder' sequences...
+ // (originator means 'target' in FCP-SCSI)
+ case SCSI_TWE: // TachLite Target Write Entry
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+ // first, build FCP_CMND
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (XFER_RDY)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+ CMDfchs->d_id |= (0x05000000L); // R_CTL = 5 for XFER_RDY
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg responder, 1st seq, xfer S.I.
+ // valid RO
+ CMDfchs->f_ctl = 0x08810008L;
+ CMDfchs->seq_cnt = 0x01000000; // sequence ID: df_ctl: sequence count
+ // use originator (other port's) OX_ID
+ CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // we want upper 16 bits
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ // now, fill out FCP-RSP header
+ // (use buffer inside SEST object)
+
+ rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+ rspHDR->reserved = 0L; // must clear
+ rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS
+ rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+ rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] responder|last seq| xfer S.I.
+ rspHDR->f_ctl = 0x08910000L;
+ rspHDR->seq_cnt = 0x03000000; // sequence ID
+ rspHDR->ox_rx_id = InFCHS->ox_rx_id; // gives us OX_ID
+ rspHDR->ro = 0x0L; // relative offset (n/a)
+
+
+ // Now setup the SEST entry
+
+ pTWE = &fcChip->SEST->u[ *fcExchangeIndex ].TWE;
+
+ // fill out the TWE:
+
+ // VALid entry:Dir outbound:enable CM:enal INT:
+ pTWE->Seq_Accum = 0xC4000000L; // upper word flags
+ pTWE->reserved = 0L;
+ pTWE->Remote_Node_ID = 0L; // no more auto RSP frame! (TL/TS change)
+ pTWE->Remote_Node_ID |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ cpqfcHBAdata->PciDev,
+ &pTWE->SLen1,
+ Cmnd, // S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+
+ // now that we know the S/G length, build CMND payload
+ build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pTWE->Buff_Off = 0x00000000; // extended s/g list, no offset
+ else
+ pTWE->Buff_Off = 0x80000000; // local data, no offset
+
+ pTWE->Buff_Index = 0; // Buff_Index | Link
+ pTWE->Exp_RO = 0;
+ pTWE->Byte_Count = 0; // filled in by TL on err
+ pTWE->reserved_ = 0;
+ pTWE->Exp_Byte_Cnt = fcp_dl;// sum of scatter buffers
+
+ break;
+
+
+
+
+
+
+ case SCSI_TRE: // TachLite Target Read Entry
+
+ // It doesn't make much sense for us to "time-out" a READ,
+ // but we'll use it for design consistency and internal error recovery.
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+ // I/O request block settings...
+ *pIRB_flags = 0; // clear IRB flags
+ // check PRLI (process login) info
+ // to see if Initiator Requires XFER_RDY
+ // if not, don't send one!
+ // { PRLI check...}
+ IRB_flags.SFA = 0; // don't send XFER_RDY - start data
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+
+
+ // now, fill out FCP-DATA header
+ // (use buffer inside SEST object)
+ dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+
+ dataHDR->reserved = 0L; // must clear
+ dataHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS,noLCr,no TS
+ dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+ dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg responder, not 1st seq, xfer S.I.
+ // valid RO
+ dataHDR->f_ctl = 0x08810008L;
+ dataHDR->seq_cnt = 0x01000000; // sequence ID (no XRDY)
+ dataHDR->ox_rx_id = InFCHS->ox_rx_id & 0xFFFF0000; // we want upper 16 bits
+ dataHDR->ro = 0x0L; // relative offset (n/a)
+
+ // now, fill out FCP-RSP header
+ // (use buffer inside SEST object)
+ rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+
+ rspHDR->reserved = 0L; // must clear
+ rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS
+ rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+ rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] responder|last seq| xfer S.I.
+ rspHDR->f_ctl = 0x08910000L;
+ rspHDR->seq_cnt = 0x02000000; // sequence ID: df_ctl: sequence count
+
+ rspHDR->ro = 0x0L; // relative offset (n/a)
+
+
+ // Now setup the SEST entry
+ pTRE = &fcChip->SEST->u[ *fcExchangeIndex ].TRE;
+
+
+ // VALid entry:Dir outbound:enable CM:enal INT:
+ pTRE->Hdr_Len = 0x86010020L; // data frame Len always 32 bytes
+ pTRE->Hdr_Addr = // bus address of dataHDR;
+ fcChip->SEST->base +
+ ((unsigned long)&fcChip->SEST->DataHDR[ *fcExchangeIndex ] -
+ (unsigned long)fcChip->SEST);
+
+ pTRE->RSP_Len = 64L; // hdr+data (TL assisted RSP frame)
+ pTRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+ pTRE->RSP_Addr = // bus address of rspHDR
+ fcChip->SEST->base +
+ ((unsigned long)&fcChip->SEST->RspHDR[ *fcExchangeIndex ] -
+ (unsigned long)fcChip->SEST);
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ cpqfcHBAdata->PciDev,
+ &pTRE->GLen1,
+ Cmnd, // S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+
+ // no payload or command to build -- READ doesn't need XRDY
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pTRE->Buff_Off = 0x78000000L; // extended data | (no offset)
+ else // local data pointers (in SEST)
+ pTRE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+ // ULONG 5
+ pTRE->Buff_Index = 0L; // Buff_Index | reserved
+ pTRE->reserved = 0x0L; // DWord 6
+
+ // DWord 7: NOTE: zero length will
+ // hang TachLite!
+ pTRE->Data_Len = fcp_dl; // e.g. sum of scatter buffers
+
+ pTRE->reserved_ = 0L; // DWord 8
+ pTRE->reserved__ = 0L; // DWord 9
+
+ break;
+
+
+
+
+
+
+
+ case FCP_RESPONSE:
+ // Target response frame: this sequence uses an OX/RX ID
+ // pair from a completed SEST exchange. We built most
+ // of the response frame when we created the TWE/TRE.
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (RSP)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += sizeof(TachFCHDR_RSP);// add SFS len (header & RSP payload)
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].type =
+ FCP_RESPONSE; // change Exchange type to "response" phase
+
+ // take advantage of prior knowledge of OX/RX_ID pair from
+ // previous XFER outbound frame (still in fchs of exchange)
+ fcChip->SEST->RspHDR[ *fcExchangeIndex ].ox_rx_id =
+ CMDfchs->ox_rx_id;
+
+ // Check the status of the DATA phase of the exchange so we can report
+ // status to the initiator
+ buildFCPstatus( fcChip, *fcExchangeIndex); // set RSP payload fields
+
+ memcpy(
+ CMDfchs, // re-use same XFER fchs for Response frame
+ &fcChip->SEST->RspHDR[ *fcExchangeIndex ],
+ sizeof( TachFCHDR_RSP ));
+
+
+ break;
+
+ default:
+ printk("cpqfcTS: don't know how to build FC type: %Xh(%d)\n", type,type);
+ break;
+
+ }
+
+
+
+ if( !ulStatus) // no errors above?
+ {
+ // FCHS is built; now build IRB
+
+ // link the just built FCHS (the "command") to the IRB entry
+ // for this Exchange.
+ pIRB = &Exchanges->fcExchange[ *fcExchangeIndex].IRB;
+
+ // len & flags according to command type above
+ pIRB->Req_A_SFS_Len = SfsLen; // includes IRB flags & len
+ pIRB->Req_A_SFS_Addr = // TL needs physical addr of frame to send
+ fcChip->exch_dma_handle + (unsigned long)CMDfchs -
+ (unsigned long)Exchanges;
+
+ pIRB->Req_A_SFS_D_ID = CMDfchs->d_id << 8; // Dest_ID must be consistent!
+
+ // Exchange is complete except for "fix-up" fields to be set
+ // at Tachyon Queuing time:
+ // IRB->Req_A_Trans_ID (OX_ID/ RX_ID):
+ // for SEST entry, lower bits correspond to actual FC Exchange ID
+ // fchs->OX_ID or RX_ID
+ }
+ else
+ {
+#ifdef DBG
+ printk( "FC Error: SEST build Pool Allocation failed\n");
+#endif
+ // return resources...
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, *fcExchangeIndex); // SEST build failed
+ }
+ }
+ else // no Exchanges available
+ {
+ ulStatus = SEST_FULL;
+ printk( "FC Error: no fcExchanges available\n");
+ }
+ return ulStatus;
+}
+
+
+
+
+
+
+// set RSP payload fields
+static void buildFCPstatus( PTACHYON fcChip, ULONG ExchangeID)
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ExchangeID]; // shorthand
+ PFCP_STATUS_RESPONSE pFcpStatus;
+
+ memset( &fcChip->SEST->RspHDR[ ExchangeID ].pl, 0,
+ sizeof( FCP_STATUS_RESPONSE) );
+ if( pExchange->status ) // something wrong?
+ {
+ pFcpStatus = (PFCP_STATUS_RESPONSE) // cast RSP buffer for this xchng
+ &fcChip->SEST->RspHDR[ ExchangeID ].pl;
+ if( pExchange->status & COUNT_ERROR )
+ {
+
+ // set FCP response len valid (so we can report count error)
+ pFcpStatus->fcp_status |= FCP_RSP_LEN_VALID;
+ pFcpStatus->fcp_rsp_len = 0x04000000; // 4 byte len (BIG Endian)
+
+ pFcpStatus->fcp_rsp_info = FCP_DATA_LEN_NOT_BURST_LEN; // RSP_CODE
+ }
+ }
+}
+
+
+static dma_addr_t
+cpqfc_pci_map_sg_page(
+ struct pci_dev *pcidev,
+ ULONG *hw_paddr, // where to put phys addr for HW use
+ void *sgp_vaddr, // the virtual address of the sg page
+ dma_addr_t *umap_paddr, // where to put phys addr for unmap
+ unsigned int *maplen, // where to store sg entry length
+ int PairCount) // number of sg pairs used in the page.
+{
+ unsigned long aligned_addr = (unsigned long) sgp_vaddr;
+
+ *maplen = PairCount * 8;
+ aligned_addr += TL_EXT_SG_PAGE_BYTELEN;
+ aligned_addr &= ~(TL_EXT_SG_PAGE_BYTELEN -1);
+
+ *umap_paddr = pci_map_single(pcidev, (void *) aligned_addr,
+ *maplen, PCI_DMA_TODEVICE);
+ *hw_paddr = (ULONG) *umap_paddr;
+
+# if BITS_PER_LONG > 32
+ if( *umap_paddr >>32 ) {
+ printk("cqpfcTS:Tach SG DMA addr %p>32 bits\n",
+ (void*)umap_paddr);
+ return 0;
+ }
+# endif
+ return *umap_paddr;
+}
+
+static void
+cpqfc_undo_SEST_mappings(struct pci_dev *pcidev,
+ unsigned long contigaddr, int len, int dir,
+ struct scatterlist *sgl, int use_sg,
+ PSGPAGES *sgPages_head,
+ int allocated_pages)
+{
+ PSGPAGES i, next;
+
+ if (contigaddr != (unsigned long) NULL)
+ pci_unmap_single(pcidev, contigaddr, len, dir);
+
+ if (sgl != NULL)
+ pci_unmap_sg(pcidev, sgl, use_sg, dir);
+
+ for (i=*sgPages_head; i != NULL ;i = next)
+ {
+ pci_unmap_single(pcidev, i->busaddr, i->maplen,
+ scsi_to_pci_dma_dir(PCI_DMA_TODEVICE));
+ i->busaddr = (dma_addr_t) NULL;
+ i->maplen = 0L;
+ next = i->next;
+ kfree(i);
+ }
+ *sgPages_head = NULL;
+}
+
+// This routine builds scatter/gather lists into SEST entries
+// INPUTS:
+// SESTalPair - SEST address @DWordA "Local Buffer Length"
+// sgList - Scatter/Gather linked list of Len/Address data buffers
+// OUTPUT:
+// sgPairs - number of valid address/length pairs
+// Remarks:
+// The SEST data buffer pointers only depend on number of
+// length/ address pairs, NOT on the type (IWE, TRE,...)
+// Up to 3 pairs can be referenced in the SEST - more than 3
+// require this Extended S/G list page. The page holds 4, 8, 16...
+// len/addr pairs, per Scatter/Gather List Page Length Reg.
+// TachLite allows pages to be linked to any depth.
+
+//#define DBG_SEST_SGLIST 1 // for printing out S/G pairs with Ext. pages
+
+static int ap_hi_water = TL_DANGER_SGPAGES;
+
+static ULONG build_SEST_sgList(
+ struct pci_dev *pcidev,
+ ULONG *SESTalPairStart, // the 3 len/address buffers in SEST
+ Scsi_Cmnd *Cmnd,
+ ULONG *sgPairs,
+ PSGPAGES *sgPages_head) // link list of TL Ext. S/G pages from O/S Pool
+
+{
+ ULONG i, AllocatedPages=0; // Tach Ext. S/G page allocations
+ ULONG* alPair = SESTalPairStart;
+ ULONG* ext_sg_page_phys_addr_place = NULL;
+ int PairCount;
+ unsigned long ulBuff, contigaddr;
+ ULONG total_data_len=0; // (in bytes)
+ ULONG bytes_to_go = Cmnd->request_bufflen; // total xfer (S/G sum)
+ ULONG thisMappingLen;
+ struct scatterlist *sgl = NULL; // S/G list (Linux format)
+ int sg_count, totalsgs;
+ dma_addr_t busaddr;
+ unsigned long thislen, offset;
+ PSGPAGES *sgpage = sgPages_head;
+ PSGPAGES prev_page = NULL;
+
+# define WE_HAVE_SG_LIST (sgl != (unsigned long) NULL)
+ contigaddr = (unsigned long) NULL;
+
+ if( !Cmnd->use_sg ) // no S/G list?
+ {
+ if (bytes_to_go <= TL_MAX_SG_ELEM_LEN)
+ {
+ *sgPairs = 1; // use "local" S/G pair in SEST entry
+ // (for now, ignore address bits above #31)
+
+ *alPair++ = bytes_to_go; // bits 18-0, length
+
+ if (bytes_to_go != 0) {
+ contigaddr = ulBuff = pci_map_single(pcidev,
+ Cmnd->request_buffer,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ // printk("ms %p ", ulBuff);
+ }
+ else {
+ // No data transfer, (e.g.: Test Unit Ready)
+ // printk("btg=0 ");
+ *sgPairs = 0;
+ memset(alPair, 0, sizeof(*alPair));
+ return 0;
+ }
+
+# if BITS_PER_LONG > 32
+ if( ulBuff >>32 ) {
+ printk("FATAL! Tachyon DMA address %p "
+ "exceeds 32 bits\n", (void*)ulBuff );
+ return 0;
+ }
+# endif
+ *alPair = (ULONG)ulBuff;
+ return bytes_to_go;
+ }
+ else // We have a single large (too big) contiguous buffer.
+ { // We will have to break it up. We'll use the scatter
+ // gather code way below, but use contigaddr instead
+ // of sg_dma_addr(). (this is a very rare case).
+
+ unsigned long btg;
+ contigaddr = pci_map_single(pcidev, Cmnd->request_buffer,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+
+ // printk("contigaddr = %p, len = %d\n",
+ // (void *) contigaddr, bytes_to_go);
+ totalsgs = 0;
+ for (btg = bytes_to_go; btg > 0; ) {
+ btg -= ( btg > TL_MAX_SG_ELEM_LEN ?
+ TL_MAX_SG_ELEM_LEN : btg );
+ totalsgs++;
+ }
+ sgl = NULL;
+ *sgPairs = totalsgs;
+ }
+ }
+ else // we do have a scatter gather list
+ {
+ // [TBD - update for Linux to support > 32 bits addressing]
+ // since the format for local & extended S/G lists is different,
+ // check if S/G pairs exceeds 3.
+ // *sgPairs = Cmnd->use_sg; Nope, that's wrong.
+
+ sgl = (struct scatterlist*)Cmnd->request_buffer;
+ sg_count = pci_map_sg(pcidev, sgl, Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ if( sg_count <= 3 ) {
+
+ // we need to be careful here that no individual mapping
+ // is too large, and if any is, that breaking it up
+ // doesn't push us over 3 sgs, or, if it does, that we
+ // handle that case. Tachyon can take 0x7FFFF bits for length,
+ // but sg structure uses "unsigned int", on the face of it,
+ // up to 0xFFFFFFFF or even more.
+
+ int i;
+ unsigned long thislen;
+
+ totalsgs = 0;
+ for (i=0;i<sg_count;i++) {
+ thislen = sg_dma_len(&sgl[i]);
+ while (thislen >= TL_MAX_SG_ELEM_LEN) {
+ totalsgs++;
+ thislen -= TL_MAX_SG_ELEM_LEN;
+ }
+ if (thislen > 0) totalsgs++;
+ }
+ *sgPairs = totalsgs;
+ } else totalsgs = 999; // as a first estimate, definitely >3,
+
+ // if (totalsgs != sg_count)
+ // printk("totalsgs = %d, sgcount=%d\n",totalsgs,sg_count);
+ }
+
+ if( totalsgs <= 3 ) // can (must) use "local" SEST list
+ {
+ while( bytes_to_go)
+ {
+ offset = 0L;
+
+ if ( WE_HAVE_SG_LIST )
+ thisMappingLen = sg_dma_len(sgl);
+ else // or contiguous buffer?
+ thisMappingLen = bytes_to_go;
+
+ while (thisMappingLen > 0)
+ {
+ thislen = thisMappingLen > TL_MAX_SG_ELEM_LEN ?
+ TL_MAX_SG_ELEM_LEN : thisMappingLen;
+ bytes_to_go = bytes_to_go - thislen;
+
+ // we have L/A pair; L = thislen, A = physicalAddress
+ // load into SEST...
+
+ total_data_len += thislen;
+ *alPair = thislen; // bits 18-0, length
+
+ alPair++;
+
+ if ( WE_HAVE_SG_LIST )
+ ulBuff = sg_dma_address(sgl) + offset;
+ else
+ ulBuff = contigaddr + offset;
+
+ offset += thislen;
+
+# if BITS_PER_LONG > 32
+ if( ulBuff >>32 ) {
+ printk("cqpfcTS: 2Tach DMA address %p > 32 bits\n",
+ (void*)ulBuff );
+ printk("%s = %p, offset = %ld\n",
+ WE_HAVE_SG_LIST ? "ulBuff" : "contigaddr",
+ WE_HAVE_SG_LIST ? (void *) ulBuff : (void *) contigaddr,
+ offset);
+ return 0;
+ }
+# endif
+ *alPair++ = (ULONG)ulBuff; // lower 32 bits (31-0)
+ thisMappingLen -= thislen;
+ }
+
+ if ( WE_HAVE_SG_LIST ) ++sgl; // next S/G pair
+ else if (bytes_to_go != 0) printk("BTG not zero!\n");
+
+# ifdef DBG_SEST_SGLIST
+ printk("L=%d ", thisMappingLen);
+ printk("btg=%d ", bytes_to_go);
+# endif
+
+ }
+ // printk("i:%d\n", *sgPairs);
+ }
+ else // more than 3 pairs requires Extended S/G page (Pool Allocation)
+ {
+ // clear out SEST DWORDs (local S/G addr) C-F (A-B set in following logic)
+ for( i=2; i<6; i++)
+ alPair[i] = 0;
+
+ PairCount = TL_EXT_SG_PAGE_COUNT; // forces initial page allocation
+ totalsgs = 0;
+ while( bytes_to_go )
+ {
+ // Per SEST format, we can support 524287 byte lengths per
+ // S/G pair. Typical user buffers are 4k, and very rarely
+ // exceed 12k due to fragmentation of physical memory pages.
+ // However, on certain O/S system (not "user") buffers (on platforms
+ // with huge memories), it's possible to exceed this
+ // length in a single S/G address/len mapping, so we have to handle
+ // that.
+
+ offset = 0L;
+ if ( WE_HAVE_SG_LIST )
+ thisMappingLen = sg_dma_len(sgl);
+ else
+ thisMappingLen = bytes_to_go;
+
+ while (thisMappingLen > 0)
+ {
+ thislen = thisMappingLen > TL_MAX_SG_ELEM_LEN ?
+ TL_MAX_SG_ELEM_LEN : thisMappingLen;
+ // printk("%d/%d/%d\n", thislen, thisMappingLen, bytes_to_go);
+
+ // should we load into "this" extended S/G page, or allocate
+ // new page?
+
+ if( PairCount >= TL_EXT_SG_PAGE_COUNT )
+ {
+ // Now, we have to map the previous page, (triggering buffer bounce)
+ // The first time thru the loop, there won't be a previous page.
+ if (prev_page != NULL) // is there a prev page?
+ {
+ // this code is normally kind of hard to trigger,
+ // you have to use up more than 256 scatter gather
+ // elements to get here. Cranking down TL_MAX_SG_ELEM_LEN
+ // to an absurdly low value (128 bytes or so) to artificially
+ // break i/o's into a zillion pieces is how I tested it.
+ busaddr = cpqfc_pci_map_sg_page(pcidev,
+ ext_sg_page_phys_addr_place,
+ prev_page->page,
+ &prev_page->busaddr,
+ &prev_page->maplen,
+ PairCount);
+ }
+ // Allocate the TL Extended S/G list page. We have
+ // to allocate twice what we want to ensure required TL alignment
+ // (Tachlite TL/TS User Man. Rev 6.0, p 168)
+ // We store the original allocated PVOID so we can free later
+ *sgpage = kmalloc( sizeof(SGPAGES), GFP_ATOMIC);
+ if ( ! *sgpage )
+ {
+ printk("cpqfc: Allocation failed @ %d S/G page allocations\n",
+ AllocatedPages);
+ total_data_len = 0; // failure!! Ext. S/G is All-or-none affair
+
+ // unmap the previous mappings, if any.
+
+ cpqfc_undo_SEST_mappings(pcidev, contigaddr,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction),
+ sgl, Cmnd->use_sg, sgPages_head, AllocatedPages+1);
+
+ // FIXME: testing shows that if we get here,
+ // it's bad news. (this has been this way for a long
+ // time though, AFAIK. Not that that excuses it.)
+
+ return 0; // give up (and probably hang the system)
+ }
+ // clear out memory we just allocated
+ memset( (*sgpage)->page,0,TL_EXT_SG_PAGE_BYTELEN*2);
+ (*sgpage)->next = NULL;
+ (*sgpage)->busaddr = (dma_addr_t) NULL;
+ (*sgpage)->maplen = 0L;
+
+ // align the memory - TL requires sizeof() Ext. S/G page alignment.
+ // We doubled the actual required size so we could mask off LSBs
+ // to get desired offset
+
+ ulBuff = (unsigned long) (*sgpage)->page;
+ ulBuff += TL_EXT_SG_PAGE_BYTELEN;
+ ulBuff &= ~(TL_EXT_SG_PAGE_BYTELEN -1);
+
+ // set pointer, in SEST if first Ext. S/G page, or in last pair
+ // of linked Ext. S/G pages... (Only 32-bit PVOIDs, so just
+ // load lower 32 bits)
+ // NOTE: the Len field must be '0' if this is the first Ext. S/G
+ // pointer in SEST, and not 0 otherwise (we know thislen != 0).
+
+ *alPair = (alPair != SESTalPairStart) ? thislen : 0;
+
+# ifdef DBG_SEST_SGLIST
+ printk("PairCount %d @%p even %Xh, ",
+ PairCount, alPair, *alPair);
+# endif
+
+ // Save the place where we need to store the physical
+ // address of this scatter gather page which we get when we map it
+ // (and mapping we can do only after we fill it in.)
+ alPair++; // next DWORD, will contain phys addr of the ext page
+ ext_sg_page_phys_addr_place = alPair;
+
+ // Now, set alPair = the virtual addr of the (Extended) S/G page
+ // which will accept the Len/ PhysicalAddress pairs
+ alPair = (ULONG *) ulBuff;
+
+ AllocatedPages++;
+ if (AllocatedPages >= ap_hi_water)
+ {
+ // This message should rarely, if ever, come out.
+ // Previously (cpqfc version <= 2.0.5) the driver would
+ // just puke if more than 4 SG pages were used, and nobody
+ // ever complained about that. This only comes out if
+ // more than 8 pages are used.
+
+ printk(KERN_WARNING
+ "cpqfc: Possible danger. %d scatter gather pages used.\n"
+ "cpqfc: detected seemingly extreme memory "
+ "fragmentation or huge data transfers.\n",
+ AllocatedPages);
+ ap_hi_water = AllocatedPages+1;
+ }
+
+ PairCount = 1; // starting new Ext. S/G page
+ prev_page = (*sgpage); // remember this page, for next time thru
+ sgpage = &((*sgpage)->next);
+ } // end of new TL Ext. S/G page allocation
+
+ *alPair = thislen; // bits 18-0, length (range check above)
+
+# ifdef DBG_SEST_SGLIST
+ printk("PairCount %d @%p, even %Xh, ", PairCount, alPair, *alPair);
+# endif
+
+ alPair++; // next DWORD, physical address
+
+ if ( WE_HAVE_SG_LIST )
+ ulBuff = sg_dma_address(sgl) + offset;
+ else
+ ulBuff = contigaddr + offset;
+ offset += thislen;
+
+# if BITS_PER_LONG > 32
+ if( ulBuff >>32 )
+ {
+ printk("cqpfcTS: 1Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+ printk("%s = %p, offset = %ld\n",
+ WE_HAVE_SG_LIST ? "ulBuff" : "contigaddr",
+ WE_HAVE_SG_LIST ? (void *) ulBuff : (void *) contigaddr,
+ offset);
+ return 0;
+ }
+# endif
+
+ *alPair = (ULONG) ulBuff; // lower 32 bits (31-0)
+
+# ifdef DBG_SEST_SGLIST
+ printk("odd %Xh\n", *alPair);
+# endif
+ alPair++; // next DWORD, next address/length pair
+
+ PairCount++; // next Length/Address pair
+
+ // if (PairCount > pc_hi_water)
+ // {
+ // printk("pc hi = %d ", PairCount);
+ // pc_hi_water = PairCount;
+ // }
+ bytes_to_go -= thislen;
+ total_data_len += thislen;
+ thisMappingLen -= thislen;
+ totalsgs++;
+ } // while (thisMappingLen > 0)
+ if ( WE_HAVE_SG_LIST ) sgl++; // next S/G pair
+ } // while (bytes_to_go)
+
+ // printk("Totalsgs=%d\n", totalsgs);
+ *sgPairs = totalsgs;
+
+ // PCI map (and bounce) the last (and usually only) extended SG page
+ busaddr = cpqfc_pci_map_sg_page(pcidev,
+ ext_sg_page_phys_addr_place,
+ prev_page->page,
+ &prev_page->busaddr,
+ &prev_page->maplen,
+ PairCount);
+ }
+ return total_data_len;
+}
+
+
+
+// The Tachlite SEST table is referenced to OX_ID (or RX_ID). To optimize
+// performance and debuggability, we index the Exchange structure to FC X_ID
+// This enables us to build exchanges for later en-queing to Tachyon,
+// provided we have an open X_ID slot. At Tachyon queing time, we only
+// need an ERQ slot; then "fix-up" references in the
+// IRB, FCHS, etc. as needed.
+// RETURNS:
+// 0 if successful
+// non-zero on error
+//sstartex
+ULONG cpqfcTSStartExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ LONG ExchangeID )
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ ExchangeID ]; // shorthand
+ USHORT producer, consumer;
+ ULONG ulStatus=0;
+ short int ErqIndex;
+ BOOLEAN CompleteExchange = FALSE; // e.g. ACC replies are complete
+ BOOLEAN SestType=FALSE;
+ ULONG InboundData=0;
+
+ // We will manipulate Tachlite chip registers here to successfully
+ // start exchanges.
+
+ // Check that link is not down -- we can't start an exchange on a
+ // down link!
+
+ if( fcChip->Registers.FMstatus.value & 0x80) // LPSM offline?
+ {
+printk("fcStartExchange: PSM offline (%Xh), x_ID %Xh, type %Xh, port_id %Xh\n",
+ fcChip->Registers.FMstatus.value & 0xFF,
+ ExchangeID,
+ pExchange->type,
+ pExchange->fchs.d_id);
+
+ if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+ {
+ // Our most popular LinkService commands are port discovery types
+ // (PLOGI/ PDISC...), which are implicitly nullified by Link Down
+ // events, so it makes no sense to Que them. However, ABTS should
+ // be queued, since exchange sequences are likely destroyed by
+ // Link Down events, and we want to notify other ports of broken
+ // sequences by aborting the corresponding exchanges.
+ if( pExchange->type != BLS_ABTS )
+ {
+ ulStatus = LNKDWN_OSLS;
+ goto Done;
+ // don't Que most LinkServ exchanges on LINK DOWN
+ }
+ }
+
+ printk("fcStartExchange: Que x_ID %Xh, type %Xh\n",
+ ExchangeID, pExchange->type);
+ pExchange->status |= EXCHANGE_QUEUED;
+ ulStatus = EXCHANGE_QUEUED;
+ goto Done;
+ }
+
+ // Make sure ERQ has available space.
+
+ producer = (USHORT)fcChip->ERQ->producerIndex; // copies for logical arith.
+ consumer = (USHORT)fcChip->ERQ->consumerIndex;
+ producer++; // We are testing for full que by incrementing
+
+ if( producer >= ERQ_LEN ) // rollover condition?
+ producer = 0;
+ if( consumer != producer ) // ERQ not full?
+ {
+ // ****************** Need Atomic access to chip registers!!********
+
+ // remember ERQ PI for copying IRB
+ ErqIndex = (USHORT)fcChip->ERQ->producerIndex;
+ fcChip->ERQ->producerIndex = producer; // this is written to Tachyon
+ // we have an ERQ slot! If SCSI command, need SEST slot
+ // otherwise we are done.
+
+ // Note that Tachyon requires that bit 15 of the OX_ID or RX_ID be
+ // set according to direction of data to/from Tachyon for SEST assists.
+ // For consistency, enforce this rule for Link Service (non-SEST)
+ // exchanges as well.
+
+ // fix-up the X_ID field in IRB
+ pExchange->IRB.Req_A_Trans_ID = ExchangeID & 0x7FFF; // 15-bit field
+
+ // fix-up the X_ID field in fchs -- depends on Originator or Responder,
+ // outgoing or incoming data?
+ switch( pExchange->type )
+ {
+ // ORIGINATOR types... we're setting our OX_ID and
+ // defaulting the responder's RX_ID to 0xFFFF
+
+ case SCSI_IRE:
+ // Requirement: set MSB of x_ID for Incoming TL data
+ // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+ InboundData = 0x8000;
+
+ case SCSI_IWE:
+ SestType = TRUE;
+ pExchange->fchs.ox_rx_id = (ExchangeID | InboundData);
+ pExchange->fchs.ox_rx_id <<= 16; // MSW shift
+ pExchange->fchs.ox_rx_id |= 0xffff; // add default RX_ID
+
+ // now fix-up the Data HDR OX_ID (TL automatically does rx_id)
+ // (not necessary for IRE -- data buffer unused)
+ if( pExchange->type == SCSI_IWE)
+ {
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id =
+ pExchange->fchs.ox_rx_id;
+
+ }
+
+ break;
+
+
+ case FCS_NSR: // ext. link service Name Service Request
+ case ELS_SCR: // ext. link service State Change Registration
+ case ELS_FDISC:// ext. link service login
+ case ELS_FLOGI:// ext. link service login
+ case ELS_LOGO: // FC-PH extended link service logout
+ case BLS_NOP: // Basic link service No OPeration
+ case ELS_PLOGI:// ext. link service login (PLOGI)
+ case ELS_PDISC:// ext. link service login (PDISC)
+ case ELS_PRLI: // ext. link service process login
+
+ pExchange->fchs.ox_rx_id = ExchangeID;
+ pExchange->fchs.ox_rx_id <<= 16; // MSW shift
+ pExchange->fchs.ox_rx_id |= 0xffff; // and RX_ID
+
+ break;
+
+
+
+
+ // RESPONDER types... we must set our RX_ID while preserving
+ // sender's OX_ID
+ // outgoing (or no) data
+ case ELS_RJT: // extended link service reject
+ case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+ case ELS_ACC: // ext. generic link service accept
+ case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+ case ELS_PRLI_ACC: // ext. link service process login accept
+
+ CompleteExchange = TRUE; // Reply (ACC or RJT) is end of exchange
+ pExchange->fchs.ox_rx_id |= (ExchangeID & 0xFFFF);
+
+ break;
+
+
+ // since we are a Responder, OX_ID should already be set by
+ // cpqfcTSBuildExchange(). We need to -OR- in RX_ID
+ case SCSI_TWE:
+ SestType = TRUE;
+ // Requirement: set MSB of x_ID for Incoming TL data
+ // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+
+ pExchange->fchs.ox_rx_id &= 0xFFFF0000; // clear RX_ID
+ // Requirement: set MSB of RX_ID for Incoming TL data
+ // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+ pExchange->fchs.ox_rx_id |= (ExchangeID | 0x8000);
+ break;
+
+
+ case SCSI_TRE:
+ SestType = TRUE;
+
+ // there is no XRDY for SEST target read; the data
+ // header needs to be updated. Also update the RSP
+ // exchange IDs for the status frame, in case it is sent automatically
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id |= ExchangeID;
+ fcChip->SEST->RspHDR[ ExchangeID ].ox_rx_id =
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+
+ // for easier FCP response logic (works for TWE and TRE),
+ // copy exchange IDs. (Not needed if TRE 'RSP' bit set)
+ pExchange->fchs.ox_rx_id =
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+
+ break;
+
+
+ case FCP_RESPONSE: // using existing OX_ID/ RX_ID pair,
+ // start SFS FCP-RESPONSE frame
+ // OX/RX_ID should already be set! (See "fcBuild" above)
+ CompleteExchange = TRUE; // RSP is end of FCP-SCSI exchange
+
+
+ break;
+
+
+ case BLS_ABTS_RJT: // uses new RX_ID, since SEST x_ID non-existent
+ case BLS_ABTS_ACC: // using existing OX_ID/ RX_ID pair from SEST entry
+ CompleteExchange = TRUE; // ACC or RJT marks end of FCP-SCSI exchange
+ case BLS_ABTS: // using existing OX_ID/ RX_ID pair from SEST entry
+
+
+ break;
+
+
+ default:
+ printk("Error on fcStartExchange: undefined type %Xh(%d)\n",
+ pExchange->type, pExchange->type);
+ return INVALID_ARGS;
+ }
+
+
+ // X_ID fields are entered -- copy IRB to Tachyon's ERQ
+
+
+ memcpy(
+ &fcChip->ERQ->QEntry[ ErqIndex ], // dest.
+ &pExchange->IRB,
+ 32); // fixed (hardware) length!
+
+ PCI_TRACEO( ExchangeID, 0xA0)
+
+ // ACTION! May generate INT and IMQ entry
+ writel( fcChip->ERQ->producerIndex,
+ fcChip->Registers.ERQproducerIndex.address);
+
+
+ if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+ {
+
+ // wait for completion! (TDB -- timeout and chip reset)
+
+
+ PCI_TRACEO( ExchangeID, 0xA4)
+
+ enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Sem.
+
+ down_interruptible( cpqfcHBAdata->TYOBcomplete);
+
+ disable_irq( cpqfcHBAdata->HostAdapter->irq);
+ PCI_TRACE( 0xA4)
+
+ // On login exchanges, BAD_ALPA (non-existent port_id) results in
+ // FTO (Frame Time Out) on the Outbound Completion message.
+ // If we got an FTO status, complete the exchange (free up slot)
+ if( CompleteExchange || // flag from Reply frames
+ pExchange->status ) // typically, can get FRAME_TO
+ {
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID);
+ }
+ }
+
+ else // SEST Exchange
+ {
+ ulStatus = 0; // ship & pray success (e.g. FCP-SCSI)
+
+ if( CompleteExchange ) // by Type of exchange (e.g. end-of-xchng)
+ {
+ cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, ExchangeID);
+ }
+
+ else
+ pExchange->status &= ~EXCHANGE_QUEUED; // clear ExchangeQueued flag
+
+ }
+ }
+
+
+ else // ERQ 'producer' = 'consumer' and QUE is full
+ {
+ ulStatus = OUTQUE_FULL; // Outbound (ERQ) Que full
+ }
+
+Done:
+ PCI_TRACE( 0xA0)
+ return ulStatus;
+}
+
+
+
+
+
+// Scan fcController->fcExchanges array for a usuable index (a "free"
+// exchange).
+// Inputs:
+// fcChip - pointer to TachLite chip structure
+// Return:
+// index - exchange array element where exchange can be built
+// -1 - exchange array is full
+// REMARKS:
+// Although this is a (yuk!) linear search, we presume
+// that the system will complete exchanges about as quickly as
+// they are submitted. A full Exchange array (and hence, max linear
+// search time for free exchange slot) almost guarantees a Fibre problem
+// of some sort.
+// In the interest of making exchanges easier to debug, we want a LRU
+// (Least Recently Used) scheme.
+
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type )
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG i;
+ ULONG ulStatus=-1; // assume failure
+
+
+ if( type == SCSI_IRE ||
+ type == SCSI_TRE ||
+ type == SCSI_IWE ||
+ type == SCSI_TWE)
+ {
+ // SCSI type - X_IDs should be from 0 to TACH_SEST_LEN-1
+ if( fcChip->fcSestExchangeLRU >= TACH_SEST_LEN) // rollover?
+ fcChip->fcSestExchangeLRU = 0;
+ i = fcChip->fcSestExchangeLRU; // typically it's already free!
+
+ if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+ {
+ ulStatus = 0; // success!
+ }
+
+ else
+ { // YUK! we need to do a linear search for free element.
+ // Fragmentation of the fcExchange array is due to excessively
+ // long completions or timeouts.
+
+ while( TRUE )
+ {
+ if( ++i >= TACH_SEST_LEN ) // rollover check
+ i = 0; // beginning of SEST X_IDs
+
+// printk( "looping for SCSI xchng ID: i=%d, type=%Xh\n",
+// i, Exchanges->fcExchange[i].type);
+
+ if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+ {
+ ulStatus = 0; // success!
+ break;
+ }
+ if( i == fcChip->fcSestExchangeLRU ) // wrapped-around array?
+ {
+ printk( "SEST X_ID space full\n");
+ break; // failed - prevent inf. loop
+ }
+ }
+ }
+ fcChip->fcSestExchangeLRU = i + 1; // next! (rollover check next pass)
+ }
+
+
+
+ else // Link Service type - X_IDs should be from TACH_SEST_LEN
+ // to TACH_MAX_XID
+ {
+ if( fcChip->fcLsExchangeLRU >= TACH_MAX_XID || // range check
+ fcChip->fcLsExchangeLRU < TACH_SEST_LEN ) // (e.g. startup)
+ fcChip->fcLsExchangeLRU = TACH_SEST_LEN;
+
+ i = fcChip->fcLsExchangeLRU; // typically it's already free!
+ if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+ {
+ ulStatus = 0; // success!
+ }
+
+ else
+ { // YUK! we need to do a linear search for free element
+ // Fragmentation of the fcExchange array is due to excessively
+ // long completions or timeouts.
+
+ while( TRUE )
+ {
+ if( ++i >= TACH_MAX_XID ) // rollover check
+ i = TACH_SEST_LEN;// beginning of Link Service X_IDs
+
+// printk( "looping for xchng ID: i=%d, type=%Xh\n",
+// i, Exchanges->fcExchange[i].type);
+
+ if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+ {
+ ulStatus = 0; // success!
+ break;
+ }
+ if( i == fcChip->fcLsExchangeLRU ) // wrapped-around array?
+ {
+ printk( "LinkService X_ID space full\n");
+ break; // failed - prevent inf. loop
+ }
+ }
+ }
+ fcChip->fcLsExchangeLRU = i + 1; // next! (rollover check next pass)
+
+ }
+
+ if( !ulStatus ) // success?
+ Exchanges->fcExchange[i].type = type; // allocate it.
+
+ else
+ i = -1; // error - all exchanges "open"
+
+ return i;
+}
+
+static void
+cpqfc_pci_unmap_extended_sg(struct pci_dev *pcidev,
+ PTACHYON fcChip,
+ ULONG x_ID)
+{
+ // Unmaps the memory regions used to hold the scatter gather lists
+
+ PSGPAGES i;
+
+ // Were there any such regions needing unmapping?
+ if (! USES_EXTENDED_SGLIST(fcChip->SEST, x_ID))
+ return; // No such regions, we're outta here.
+
+ // for each extended scatter gather region needing unmapping...
+ for (i=fcChip->SEST->sgPages[x_ID] ; i != NULL ; i = i->next)
+ pci_unmap_single(pcidev, i->busaddr, i->maplen,
+ scsi_to_pci_dma_dir(PCI_DMA_TODEVICE));
+}
+
+// Called also from cpqfcTScontrol.o, so can't be static
+void
+cpqfc_pci_unmap(struct pci_dev *pcidev,
+ Scsi_Cmnd *cmd,
+ PTACHYON fcChip,
+ ULONG x_ID)
+{
+ // Undo the DMA mappings
+ if (cmd->use_sg) { // Used scatter gather list for data buffer?
+ cpqfc_pci_unmap_extended_sg(pcidev, fcChip, x_ID);
+ pci_unmap_sg(pcidev, cmd->buffer, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ // printk("umsg %d\n", cmd->use_sg);
+ }
+ else if (cmd->request_bufflen) {
+ // printk("ums %p ", fcChip->SEST->u[ x_ID ].IWE.GAddr1);
+ pci_unmap_single(pcidev, fcChip->SEST->u[ x_ID ].IWE.GAddr1,
+ cmd->request_bufflen,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ }
+}
+
+// We call this routine to free an Exchange for any reason:
+// completed successfully, completed with error, aborted, etc.
+
+// returns FALSE if Exchange failed and "retry" is acceptable
+// returns TRUE if Exchange was successful, or retry is impossible
+// (e.g. port/device gone).
+//scompleteexchange
+
+void cpqfcTSCompleteExchange(
+ struct pci_dev *pcidev,
+ PTACHYON fcChip,
+ ULONG x_ID)
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ int already_unmapped = 0;
+
+ if( x_ID < TACH_SEST_LEN ) // SEST-based (or LinkServ for FCP exchange)
+ {
+ if( Exchanges->fcExchange[ x_ID ].Cmnd == NULL ) // what#@!
+ {
+// TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+ printk(" x_ID %Xh, type %Xh, NULL ptr!\n", x_ID,
+ Exchanges->fcExchange[ x_ID ].type);
+
+ goto CleanUpSestResources; // this path should be very rare.
+ }
+
+ // we have Linux Scsi Cmnd ptr..., now check our Exchange status
+ // to decide how to complete this SEST FCP exchange
+
+ if( Exchanges->fcExchange[ x_ID ].status ) // perhaps a Tach indicated problem,
+ // or abnormal exchange completion
+ {
+ // set FCP Link statistics
+
+ if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT)
+ fcChip->fcStats.timeouts++;
+ if( Exchanges->fcExchange[ x_ID ].status & INITIATOR_ABORT)
+ fcChip->fcStats.FC4aborted++;
+ if( Exchanges->fcExchange[ x_ID ].status & COUNT_ERROR)
+ fcChip->fcStats.CntErrors++;
+ if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX)
+ fcChip->fcStats.linkFailTX++;
+ if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_RX)
+ fcChip->fcStats.linkFailRX++;
+ if( Exchanges->fcExchange[ x_ID ].status & OVERFLOW)
+ fcChip->fcStats.CntErrors++;
+
+ // First, see if the Scsi upper level initiated an ABORT on this
+ // exchange...
+ if( Exchanges->fcExchange[ x_ID ].status == INITIATOR_ABORT )
+ {
+ printk(" DID_ABORT, x_ID %Xh, Cmnd %p ",
+ x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ goto CleanUpSestResources; // (we don't expect Linux _aborts)
+ }
+
+ // Did our driver timeout the Exchange, or did Tachyon indicate
+ // a failure during transmission? Ask for retry with "SOFT_ERROR"
+ else if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT)
+ {
+// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+ }
+
+ // Did frame(s) for an open exchange arrive in the SFQ,
+ // meaning the SEST was unable to process them?
+ else if( Exchanges->fcExchange[ x_ID ].status & SFQ_FRAME)
+ {
+// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+ }
+
+ // Did our driver timeout the Exchange, or did Tachyon indicate
+ // a failure during transmission? Ask for retry with "SOFT_ERROR"
+ else if(
+ (Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) ||
+ (Exchanges->fcExchange[ x_ID ].status & PORTID_CHANGED) ||
+ (Exchanges->fcExchange[ x_ID ].status & FRAME_TO) ||
+ (Exchanges->fcExchange[ x_ID ].status & INV_ENTRY) ||
+ (Exchanges->fcExchange[ x_ID ].status & ABORTSEQ_NOTIFY) )
+
+
+ {
+// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+
+
+ }
+
+ // e.g., a LOGOut happened, or device never logged back in.
+ else if( Exchanges->fcExchange[ x_ID ].status & DEVICE_REMOVED)
+ {
+// printk(" *LOGOut or timeout on login!* ");
+ // trigger?
+// TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_BAD_TARGET <<16);
+ }
+
+
+ // Did Tachyon indicate a CNT error? We need further analysis
+ // to determine if the exchange is acceptable
+ else if( Exchanges->fcExchange[ x_ID ].status == COUNT_ERROR)
+ {
+ UCHAR ScsiStatus;
+ FCP_STATUS_RESPONSE *pFcpStatus =
+ (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+
+ ScsiStatus = pFcpStatus->fcp_status >>24;
+
+ // If the command is a SCSI Read/Write type, we don't tolerate
+ // count errors of any kind; assume the count error is due to
+ // a dropped frame and ask for retry...
+
+ if(( (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x8) ||
+ (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x28) ||
+ (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0xA) ||
+ (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x2A) )
+ &&
+ ScsiStatus == 0 )
+ {
+ // ask for retry
+/* printk("COUNT_ERROR retry, x_ID %Xh, status %Xh, Cmnd %p\n",
+ x_ID, Exchanges->fcExchange[ x_ID ].status,
+ Exchanges->fcExchange[ x_ID ].Cmnd);*/
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+ }
+
+ else // need more analysis
+ {
+ cpqfcTSCheckandSnoopFCP(fcChip, x_ID); // (will set ->result)
+ }
+ }
+
+ // default: NOTE! We don't ever want to get here. Getting here
+ // implies something new is happening that we've never had a test
+ // case for. Need code maintenance! Return "ERROR"
+ else
+ {
+ unsigned int stat = Exchanges->fcExchange[ x_ID ].status;
+ printk("DEFAULT result %Xh, x_ID %Xh, Cmnd %p",
+ Exchanges->fcExchange[ x_ID ].status, x_ID,
+ Exchanges->fcExchange[ x_ID ].Cmnd);
+
+ if (stat & INVALID_ARGS) printk(" INVALID_ARGS ");
+ if (stat & LNKDWN_OSLS) printk(" LNKDWN_OSLS ");
+ if (stat & LNKDWN_LASER) printk(" LNKDWN_LASER ");
+ if (stat & OUTQUE_FULL) printk(" OUTQUE_FULL ");
+ if (stat & DRIVERQ_FULL) printk(" DRIVERQ_FULL ");
+ if (stat & SEST_FULL) printk(" SEST_FULL ");
+ if (stat & BAD_ALPA) printk(" BAD_ALPA ");
+ if (stat & OVERFLOW) printk(" OVERFLOW ");
+ if (stat & COUNT_ERROR) printk(" COUNT_ERROR ");
+ if (stat & LINKFAIL_RX) printk(" LINKFAIL_RX ");
+ if (stat & ABORTSEQ_NOTIFY) printk(" ABORTSEQ_NOTIFY ");
+ if (stat & LINKFAIL_TX) printk(" LINKFAIL_TX ");
+ if (stat & HOSTPROG_ERR) printk(" HOSTPROG_ERR ");
+ if (stat & FRAME_TO) printk(" FRAME_TO ");
+ if (stat & INV_ENTRY) printk(" INV_ENTRY ");
+ if (stat & SESTPROG_ERR) printk(" SESTPROG_ERR ");
+ if (stat & OUTBOUND_TIMEOUT) printk(" OUTBOUND_TIMEOUT ");
+ if (stat & INITIATOR_ABORT) printk(" INITIATOR_ABORT ");
+ if (stat & MEMPOOL_FAIL) printk(" MEMPOOL_FAIL ");
+ if (stat & FC2_TIMEOUT) printk(" FC2_TIMEOUT ");
+ if (stat & TARGET_ABORT) printk(" TARGET_ABORT ");
+ if (stat & EXCHANGE_QUEUED) printk(" EXCHANGE_QUEUED ");
+ if (stat & PORTID_CHANGED) printk(" PORTID_CHANGED ");
+ if (stat & DEVICE_REMOVED) printk(" DEVICE_REMOVED ");
+ if (stat & SFQ_FRAME) printk(" SFQ_FRAME ");
+ printk("\n");
+
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_ERROR <<16);
+ }
+ }
+ else // definitely no Tach problem, but perhaps an FCP problem
+ {
+ // set FCP Link statistic
+ fcChip->fcStats.ok++;
+ cpqfcTSCheckandSnoopFCP( fcChip, x_ID); // (will set ->result)
+ }
+
+ cpqfc_pci_unmap(pcidev, Exchanges->fcExchange[x_ID].Cmnd,
+ fcChip, x_ID); // undo DMA mappings.
+ already_unmapped = 1;
+
+ // OK, we've set the Scsi "->result" field, so proceed with calling
+ // Linux Scsi "done" (if not NULL), and free any kernel memory we
+ // may have allocated for the exchange.
+
+ PCI_TRACEO( (ULONG)Exchanges->fcExchange[x_ID].Cmnd, 0xAC);
+ // complete the command back to upper Scsi drivers
+ if( Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done != NULL)
+ {
+ // Calling "done" on an Linux _abort() aborted
+ // Cmnd causes a kernel panic trying to re-free mem.
+ // Actually, we shouldn't do anything with an _abort CMND
+ if( Exchanges->fcExchange[ x_ID ].Cmnd->result != (DID_ABORT<<16) )
+ {
+ PCI_TRACE(0xAC)
+ call_scsi_done(Exchanges->fcExchange[ x_ID ].Cmnd);
+ }
+ else
+ {
+// printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ }
+ }
+ else{
+ printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID,
+ Exchanges->fcExchange[ x_ID ].type,
+ Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]);
+ printk(" cpqfcTS: Null scsi_done function pointer!\n");
+ }
+
+
+ // Now, clean up non-Scsi_Cmnd items...
+CleanUpSestResources:
+
+ if (!already_unmapped)
+ cpqfc_pci_unmap(pcidev, Exchanges->fcExchange[x_ID].Cmnd,
+ fcChip, x_ID); // undo DMA mappings.
+
+ // Was an Extended Scatter/Gather page allocated? We know
+ // this by checking DWORD 4, bit 31 ("LOC") of SEST entry
+ if( !(fcChip->SEST->u[ x_ID ].IWE.Buff_Off & 0x80000000))
+ {
+ PSGPAGES p, next;
+
+ // extended S/G list was used -- Free the allocated ext. S/G pages
+ for (p = fcChip->SEST->sgPages[x_ID]; p != NULL; p = next) {
+ next = p->next;
+ kfree(p);
+ }
+ fcChip->SEST->sgPages[x_ID] = NULL;
+ }
+
+ Exchanges->fcExchange[ x_ID ].Cmnd = NULL;
+ } // Done with FCP (SEST) exchanges
+
+
+ // the remaining logic is common to ALL Exchanges:
+ // FCP(SEST) and LinkServ.
+
+ Exchanges->fcExchange[ x_ID ].type = 0; // there -- FREE!
+ Exchanges->fcExchange[ x_ID ].status = 0;
+
+ PCI_TRACEO( x_ID, 0xAC)
+
+
+ return;
+} // (END of CompleteExchange function)
+
+
+
+
+// Unfortunately, we must snoop all command completions in
+// order to manipulate certain return fields, and take note of
+// device types, etc., to facilitate the Fibre-Channel to SCSI
+// "mapping".
+// (Watch for BIG Endian confusion on some payload fields)
+void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID)
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ Scsi_Cmnd *Cmnd = Exchanges->fcExchange[ x_ID].Cmnd;
+ FCP_STATUS_RESPONSE *pFcpStatus =
+ (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+ UCHAR ScsiStatus;
+
+ ScsiStatus = pFcpStatus->fcp_status >>24;
+
+#ifdef FCP_COMPLETION_DBG
+ printk("ScsiStatus = 0x%X\n", ScsiStatus);
+#endif
+
+ // First, check FCP status
+ if( pFcpStatus->fcp_status & FCP_RSP_LEN_VALID )
+ {
+ // check response code (RSP_CODE) -- most popular is bad len
+ // 1st 4 bytes of rsp info -- only byte 3 interesting
+ if( pFcpStatus->fcp_rsp_info & FCP_DATA_LEN_NOT_BURST_LEN )
+ {
+
+ // do we EVER get here?
+ printk("cpqfcTS: FCP data len not burst len, x_ID %Xh\n", x_ID);
+ }
+ }
+
+ // for now, go by the ScsiStatus, and manipulate certain
+ // commands when necessary...
+ if( ScsiStatus == 0) // SCSI status byte "good"?
+ {
+ Cmnd->result = 0; // everything's OK
+
+ if( (Cmnd->cmnd[0] == INQUIRY))
+ {
+ UCHAR *InquiryData = Cmnd->request_buffer;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+
+ // We need to manipulate INQUIRY
+ // strings for COMPAQ RAID controllers to force
+ // Linux to scan additional LUNs. Namely, set
+ // the Inquiry string byte 2 (ANSI-approved version)
+ // to 2.
+
+ if( !memcmp( &InquiryData[8], "COMPAQ", 6 ))
+ {
+ InquiryData[2] = 0x2; // claim SCSI-2 compliance,
+ // so multiple LUNs may be scanned.
+ // (no SCSI-2 problems known in CPQ)
+ }
+
+ // snoop the Inquiry to detect Disk, Tape, etc. type
+ // (search linked list for the port_id we sent INQUIRY to)
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus (we will set it)
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort )
+ {
+ pLoggedInPort->ScsiNexus.InqDeviceType = InquiryData[0];
+ }
+ else
+ {
+ printk("cpqfcTS: can't find LoggedIn FC port %06X for INQUIRY\n",
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+ }
+ }
+ }
+
+
+ // Scsi Status not good -- pass it back to caller
+
+ else
+ {
+ Cmnd->result = ScsiStatus; // SCSI status byte is 1st
+
+ // check for valid "sense" data
+
+ if( pFcpStatus->fcp_status & FCP_SNS_LEN_VALID )
+ { // limit Scsi Sense field length!
+ int SenseLen = pFcpStatus->fcp_sns_len >>24; // (BigEndian) lower byte
+
+ SenseLen = SenseLen > sizeof( Cmnd->sense_buffer) ?
+ sizeof( Cmnd->sense_buffer) : SenseLen;
+
+
+#ifdef FCP_COMPLETION_DBG
+ printk("copy sense_buffer %p, len %d, result %Xh\n",
+ Cmnd->sense_buffer, SenseLen, Cmnd->result);
+#endif
+
+ // NOTE: There is some dispute over the FCP response
+ // format. Most FC devices assume that FCP_RSP_INFO
+ // is 8 bytes long, in spite of the fact that FCP_RSP_LEN
+ // is (virtually) always 0 and the field is "invalid".
+ // Some other devices assume that
+ // the FCP_SNS_INFO begins after FCP_RSP_LEN bytes (i.e. 0)
+ // when the FCP_RSP is invalid (this almost appears to be
+ // one of those "religious" issues).
+ // Consequently, we test the usual position of FCP_SNS_INFO
+ // for 7Xh, since the SCSI sense format says the first
+ // byte ("error code") should be 0x70 or 0x71. In practice,
+ // we find that every device does in fact have 0x70 or 0x71
+ // in the first byte position, so this test works for all
+ // FC devices.
+ // (This logic is especially effective for the CPQ/DEC HSG80
+ // & HSG60 controllers).
+
+ if( (pFcpStatus->fcp_sns_info[0] & 0x70) == 0x70 )
+ memcpy( Cmnd->sense_buffer,
+ &pFcpStatus->fcp_sns_info[0], SenseLen);
+ else
+ {
+ unsigned char *sbPtr =
+ (unsigned char *)&pFcpStatus->fcp_sns_info[0];
+ sbPtr -= 8; // back up 8 bytes hoping to find the
+ // start of the sense buffer
+ memcpy( Cmnd->sense_buffer, sbPtr, SenseLen);
+ }
+
+ // in the special case of Device Reset, tell upper layer
+ // to immediately retry (with SOFT_ERROR status)
+ // look for Sense Key Unit Attention (0x6) with ASC Device
+ // Reset (0x29)
+ // printk("SenseLen %d, Key = 0x%X, ASC = 0x%X\n",
+ // SenseLen, Cmnd->sense_buffer[2],
+ // Cmnd->sense_buffer[12]);
+ if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+ (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+ {
+ Cmnd->result |= (DID_SOFT_ERROR << 16); // "Host" status byte 3rd
+ }
+
+ // check for SenseKey "HARDWARE ERROR", ASC InternalTargetFailure
+ else if( ((Cmnd->sense_buffer[2] & 0xF) == 0x4) && // "hardware error"
+ (Cmnd->sense_buffer[12] == 0x44) ) // Addtl. Sense Code
+ {
+// printk("HARDWARE_ERROR, Channel/Target/Lun %d/%d/%d\n",
+// Cmnd->channel, Cmnd->target, Cmnd->lun);
+ Cmnd->result |= (DID_ERROR << 16); // "Host" status byte 3rd
+ }
+
+ } // (end of sense len valid)
+
+ // there is no sense data to help out Linux's Scsi layers...
+ // We'll just return the Scsi status and hope he will "do the
+ // right thing"
+ else
+ {
+ // as far as we know, the Scsi status is sufficient
+ Cmnd->result |= (DID_OK << 16); // "Host" status byte 3rd
+ }
+ }
+}
+
+
+
+//PPPPPPPPPPPPPPPPPPPPPPPPP PAYLOAD PPPPPPPPP
+// build data PAYLOAD; SCSI FCP_CMND I.U.
+// remember BIG ENDIAN payload - DWord values must be byte-reversed
+// (hence the affinity for byte pointer building).
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd,
+ UCHAR* payload, ULONG type, ULONG fcp_dl )
+{
+ int i;
+
+
+ switch( type)
+ {
+
+ case SCSI_IWE:
+ case SCSI_IRE:
+ // 8 bytes FCP_LUN
+ // Peripheral Device or Volume Set addressing, and LUN mapping
+ // When the FC port was looked up, we copied address mode
+ // and any LUN mask to the scratch pad SCp.phase & .mode
+
+ *payload++ = (UCHAR)Cmnd->SCp.phase;
+
+ // Now, because of "lun masking"
+ // (aka selective storage presentation),
+ // the contiguous Linux Scsi lun number may not match the
+ // device's lun number, so we may have to "map".
+
+ *payload++ = (UCHAR)Cmnd->SCp.have_data_in;
+
+ // We don't know of anyone in the FC business using these
+ // extra "levels" of addressing. In fact, confusion still exists
+ // just using the FIRST level... ;-)
+
+ *payload++ = 0; // 2nd level addressing
+ *payload++ = 0;
+ *payload++ = 0; // 3rd level addressing
+ *payload++ = 0;
+ *payload++ = 0; // 4th level addressing
+ *payload++ = 0;
+
+ // 4 bytes Control Field FCP_CNTL
+ *payload++ = 0; // byte 0: (MSB) reserved
+ *payload++ = 0; // byte 1: task codes
+
+ // byte 2: task management flags
+ // another "use" of the spare field to accomplish TDR
+ // note combination needed
+ if( (Cmnd->cmnd[0] == RELEASE) &&
+ (Cmnd->SCp.buffers_residual == FCP_TARGET_RESET) )
+ {
+ Cmnd->cmnd[0] = 0; // issue "Test Unit Ready" for TDR
+ *payload++ = 0x20; // target device reset bit
+ }
+ else
+ *payload++ = 0; // no TDR
+ // byte 3: (LSB) execution management codes
+ // bit 0 write, bit 1 read (don't set together)
+
+ if( fcp_dl != 0 )
+ {
+ if( type == SCSI_IWE ) // WRITE
+ *payload++ = 1;
+ else // READ
+ *payload++ = 2;
+ }
+ else
+ {
+ // On some devices, if RD or WR bits are set,
+ // and fcp_dl is 0, they will generate an error on the command.
+ // (i.e., if direction is specified, they insist on a length).
+ *payload++ = 0; // no data (necessary for CPQ)
+ }
+
+
+ // NOTE: clean this up if/when MAX_COMMAND_SIZE is increased to 16
+ // FCP_CDB allows 16 byte SCSI command descriptor blk;
+ // Linux SCSI CDB array is MAX_COMMAND_SIZE (12 at this time...)
+ for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++)
+ *payload++ = Cmnd->cmnd[i];
+
+ // if( Cmnd->cmd_len == 16 )
+ // {
+ // memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
+ // }
+ payload+= (16 - i);
+
+ // FCP_DL is largest number of expected data bytes
+ // per CDB (i.e. read/write command)
+ *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL
+ *payload++ = (UCHAR)(fcp_dl >>16);
+ *payload++ = (UCHAR)(fcp_dl >>8);
+ *payload++ = (UCHAR)fcp_dl; // (LSB)
+ break;
+
+ case SCSI_TWE: // need FCP_XFER_RDY
+ *payload++ = 0; // (4 bytes) DATA_RO (MSB byte 0)
+ *payload++ = 0;
+ *payload++ = 0;
+ *payload++ = 0; // LSB (byte 3)
+ // (4 bytes) BURST_LEN
+ // size of following FCP_DATA payload
+ *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL
+ *payload++ = (UCHAR)(fcp_dl >>16);
+ *payload++ = (UCHAR)(fcp_dl >>8);
+ *payload++ = (UCHAR)fcp_dl; // (LSB)
+ // 4 bytes RESERVED
+ *payload++ = 0;
+ *payload++ = 0;
+ *payload++ = 0;
+ *payload++ = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/scsi/cyberstorm.c b/drivers/scsi/cyberstorm.c
new file mode 100644
index 000000000000..bdbca85d1675
--- /dev/null
+++ b/drivers/scsi/cyberstorm.c
@@ -0,0 +1,377 @@
+/* cyberstorm.c: Driver for CyberStorm SCSI Controller.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * The CyberStorm SCSI driver is based on David S. Miller's ESP driver
+ * for the Sparc computers.
+ *
+ * This work was made possible by Phase5 who willingly (and most generously)
+ * supported me with hardware and all the information I needed.
+ */
+
+/* TODO:
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ * to the caches and the Sparc MMU mapping.
+ * 2) Make as few routines required outside the generic driver. A lot of the
+ * routines in this file used to be inline!
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define CYBER_ESP_ADDR 0xf400
+#define CYBER_DMA_ADDR 0xf800
+
+
+/* The CyberStorm DMA interface */
+struct cyber_dma_registers {
+ volatile unsigned char dma_addr0; /* DMA address (MSB) [0x000] */
+ unsigned char dmapad1[1];
+ volatile unsigned char dma_addr1; /* DMA address [0x002] */
+ unsigned char dmapad2[1];
+ volatile unsigned char dma_addr2; /* DMA address [0x004] */
+ unsigned char dmapad3[1];
+ volatile unsigned char dma_addr3; /* DMA address (LSB) [0x006] */
+ unsigned char dmapad4[0x3fb];
+ volatile unsigned char cond_reg; /* DMA cond (ro) [0x402] */
+#define ctrl_reg cond_reg /* DMA control (wo) [0x402] */
+};
+
+/* DMA control bits */
+#define CYBER_DMA_LED 0x80 /* HD led control 1 = on */
+#define CYBER_DMA_WRITE 0x40 /* DMA direction. 1 = write */
+#define CYBER_DMA_Z3 0x20 /* 16 (Z2) or 32 (CHIP/Z3) bit DMA transfer */
+
+/* DMA status bits */
+#define CYBER_DMA_HNDL_INTR 0x80 /* DMA IRQ pending? */
+
+/* The bits below appears to be Phase5 Debug bits only; they were not
+ * described by Phase5 so using them may seem a bit stupid...
+ */
+#define CYBER_HOST_ID 0x02 /* If set, host ID should be 7, otherwise
+ * it should be 6.
+ */
+#define CYBER_SLOW_CABLE 0x08 /* If *not* set, assume SLOW_CABLE */
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static void dma_led_off(struct NCR_ESP *esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static unsigned char ctrl_data = 0; /* Keep backup of the stuff written
+ * to ctrl_reg. Always write a copy
+ * to this register when writing to
+ * the hardware register!
+ */
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are transferred to the ESP chip
+ * via PIO.
+ */
+
+/***************************************************************** Detection */
+int __init cyber_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ unsigned long board = z->resource.start;
+ if ((z->id == ZORRO_PROD_PHASE5_BLIZZARD_1220_CYBERSTORM ||
+ z->id == ZORRO_PROD_PHASE5_BLIZZARD_1230_II_FASTLANE_Z3_CYBERSCSI_CYBERSTORM060) &&
+ request_mem_region(board+CYBER_ESP_ADDR,
+ sizeof(struct ESP_regs), "NCR53C9x")) {
+ /* Figure out if this is a CyberStorm or really a
+ * Fastlane/Blizzard Mk II by looking at the board size.
+ * CyberStorm maps 64kB
+ * (ZORRO_PROD_PHASE5_BLIZZARD_1220_CYBERSTORM does anyway)
+ */
+ if(z->resource.end-board != 0xffff) {
+ release_mem_region(board+CYBER_ESP_ADDR,
+ sizeof(struct ESP_regs));
+ return 0;
+ }
+ esp = esp_allocate(tpnt, (void *)board+CYBER_ESP_ADDR);
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_led_on = &dma_led_on;
+ esp->dma_led_off = &dma_led_off;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ /* SCSI chip speed */
+ esp->cfreq = 40000000;
+
+ /* The DMA registers on the CyberStorm are mapped
+ * relative to the device (i.e. in the same Zorro
+ * I/O block).
+ */
+ address = (unsigned long)ZTWO_VADDR(board);
+ esp->dregs = (void *)(address + CYBER_DMA_ADDR);
+
+ /* ESP register base */
+ esp->eregs = (struct ESP_regs *)(address + CYBER_ESP_ADDR);
+
+ /* Set the command buffer */
+ esp->esp_command = cmd_buffer;
+ esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
+
+ esp->irq = IRQ_AMIGA_PORTS;
+ request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
+ "CyberStorm SCSI", esp->ehost);
+ /* Figure out our scsi ID on the bus */
+ /* The DMA cond flag contains a hardcoded jumper bit
+ * which can be used to select host number 6 or 7.
+ * However, even though it may change, we use a hardcoded
+ * value of 7.
+ */
+ esp->scsi_id = 7;
+
+ /* We don't have a differential SCSI-bus. */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ }
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Since the CyberStorm DMA is fully dedicated to the ESP chip,
+ * the number of bytes sent (to the ESP chip) equals the number
+ * of bytes in the FIFO - there is no buffering in the DMA controller.
+ * XXXX Do I read this right? It is from host to ESP, right?
+ */
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ /* I don't think there's any limit on the CyberDMA. So we use what
+ * the ESP chip can handle (24 bit).
+ */
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x1000000)
+ sz = 0x1000000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+ esp->esp_id, ((struct cyber_dma_registers *)
+ (esp->dregs))->cond_reg));
+ ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+ custom.intreqr, custom.intenar));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct cyber_dma_registers *dregs =
+ (struct cyber_dma_registers *) esp->dregs;
+
+ cache_clear(addr, length);
+
+ addr &= ~(1);
+ dregs->dma_addr0 = (addr >> 24) & 0xff;
+ dregs->dma_addr1 = (addr >> 16) & 0xff;
+ dregs->dma_addr2 = (addr >> 8) & 0xff;
+ dregs->dma_addr3 = (addr ) & 0xff;
+ ctrl_data &= ~(CYBER_DMA_WRITE);
+
+ /* Check if physical address is outside Z2 space and of
+ * block length/block aligned in memory. If this is the
+ * case, enable 32 bit transfer. In all other cases, fall back
+ * to 16 bit transfer.
+ * Obviously 32 bit transfer should be enabled if the DMA address
+ * and length are 32 bit aligned. However, this leads to some
+ * strange behavior. Even 64 bit aligned addr/length fails.
+ * Until I've found a reason for this, 32 bit transfer is only
+ * used for full-block transfers (1kB).
+ * -jskov
+ */
+#if 0
+ if((addr & 0x3fc) || length & 0x3ff || ((addr > 0x200000) &&
+ (addr < 0xff0000)))
+ ctrl_data &= ~(CYBER_DMA_Z3); /* Z2, do 16 bit DMA */
+ else
+ ctrl_data |= CYBER_DMA_Z3; /* CHIP/Z3, do 32 bit DMA */
+#else
+ ctrl_data &= ~(CYBER_DMA_Z3); /* Z2, do 16 bit DMA */
+#endif
+ dregs->ctrl_reg = ctrl_data;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct cyber_dma_registers *dregs =
+ (struct cyber_dma_registers *) esp->dregs;
+
+ cache_push(addr, length);
+
+ addr |= 1;
+ dregs->dma_addr0 = (addr >> 24) & 0xff;
+ dregs->dma_addr1 = (addr >> 16) & 0xff;
+ dregs->dma_addr2 = (addr >> 8) & 0xff;
+ dregs->dma_addr3 = (addr ) & 0xff;
+ ctrl_data |= CYBER_DMA_WRITE;
+
+ /* See comment above */
+#if 0
+ if((addr & 0x3fc) || length & 0x3ff || ((addr > 0x200000) &&
+ (addr < 0xff0000)))
+ ctrl_data &= ~(CYBER_DMA_Z3); /* Z2, do 16 bit DMA */
+ else
+ ctrl_data |= CYBER_DMA_Z3; /* CHIP/Z3, do 32 bit DMA */
+#else
+ ctrl_data &= ~(CYBER_DMA_Z3); /* Z2, do 16 bit DMA */
+#endif
+ dregs->ctrl_reg = ctrl_data;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ /* It's important to check the DMA IRQ bit in the correct way! */
+ return ((esp_read(esp->eregs->esp_status) & ESP_STAT_INTR) &&
+ ((((struct cyber_dma_registers *)(esp->dregs))->cond_reg) &
+ CYBER_DMA_HNDL_INTR));
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+ ctrl_data &= ~CYBER_DMA_LED;
+ ((struct cyber_dma_registers *)(esp->dregs))->ctrl_reg = ctrl_data;
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+ ctrl_data |= CYBER_DMA_LED;
+ ((struct cyber_dma_registers *)(esp->dregs))->ctrl_reg = ctrl_data;
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+#define HOSTS_C
+
+int cyber_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ unsigned long address = (unsigned long)((struct NCR_ESP *)instance->hostdata)->edev;
+
+ esp_deallocate((struct NCR_ESP *)instance->hostdata);
+ esp_release();
+ release_mem_region(address, sizeof(struct ESP_regs));
+ free_irq(IRQ_AMIGA_PORTS, esp_intr);
+#endif
+ return 1;
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "esp-cyberstorm",
+ .proc_info = esp_proc_info,
+ .name = "CyberStorm SCSI",
+ .detect = cyber_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = cyber_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/cyberstormII.c b/drivers/scsi/cyberstormII.c
new file mode 100644
index 000000000000..845d9259821e
--- /dev/null
+++ b/drivers/scsi/cyberstormII.c
@@ -0,0 +1,314 @@
+/* cyberstormII.c: Driver for CyberStorm SCSI Mk II
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * This driver is based on cyberstorm.c
+ */
+
+/* TODO:
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ * to the caches and the Sparc MMU mapping.
+ * 2) Make as few routines required outside the generic driver. A lot of the
+ * routines in this file used to be inline!
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define CYBERII_ESP_ADDR 0x1ff03
+#define CYBERII_DMA_ADDR 0x1ff43
+
+
+/* The CyberStorm II DMA interface */
+struct cyberII_dma_registers {
+ volatile unsigned char cond_reg; /* DMA cond (ro) [0x000] */
+#define ctrl_reg cond_reg /* DMA control (wo) [0x000] */
+ unsigned char dmapad4[0x3f];
+ volatile unsigned char dma_addr0; /* DMA address (MSB) [0x040] */
+ unsigned char dmapad1[3];
+ volatile unsigned char dma_addr1; /* DMA address [0x044] */
+ unsigned char dmapad2[3];
+ volatile unsigned char dma_addr2; /* DMA address [0x048] */
+ unsigned char dmapad3[3];
+ volatile unsigned char dma_addr3; /* DMA address (LSB) [0x04c] */
+};
+
+/* DMA control bits */
+#define CYBERII_DMA_LED 0x02 /* HD led control 1 = on */
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static void dma_led_off(struct NCR_ESP *esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are transferred to the ESP chip
+ * via PIO.
+ */
+
+/***************************************************************** Detection */
+int __init cyberII_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+ struct ESP_regs *eregs;
+
+ if ((z = zorro_find_device(ZORRO_PROD_PHASE5_CYBERSTORM_MK_II, z))) {
+ unsigned long board = z->resource.start;
+ if (request_mem_region(board+CYBERII_ESP_ADDR,
+ sizeof(struct ESP_regs), "NCR53C9x")) {
+ /* Do some magic to figure out if the CyberStorm Mk II
+ * is equipped with a SCSI controller
+ */
+ address = (unsigned long)ZTWO_VADDR(board);
+ eregs = (struct ESP_regs *)(address + CYBERII_ESP_ADDR);
+
+ esp = esp_allocate(tpnt, (void *)board+CYBERII_ESP_ADDR);
+
+ esp_write(eregs->esp_cfg1, (ESP_CONFIG1_PENABLE | 7));
+ udelay(5);
+ if(esp_read(eregs->esp_cfg1) != (ESP_CONFIG1_PENABLE | 7)) {
+ esp_deallocate(esp);
+ scsi_unregister(esp->ehost);
+ release_mem_region(board+CYBERII_ESP_ADDR,
+ sizeof(struct ESP_regs));
+ return 0; /* Bail out if address did not hold data */
+ }
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_led_on = &dma_led_on;
+ esp->dma_led_off = &dma_led_off;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ /* SCSI chip speed */
+ esp->cfreq = 40000000;
+
+ /* The DMA registers on the CyberStorm are mapped
+ * relative to the device (i.e. in the same Zorro
+ * I/O block).
+ */
+ esp->dregs = (void *)(address + CYBERII_DMA_ADDR);
+
+ /* ESP register base */
+ esp->eregs = eregs;
+
+ /* Set the command buffer */
+ esp->esp_command = cmd_buffer;
+ esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
+
+ esp->irq = IRQ_AMIGA_PORTS;
+ request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
+ "CyberStorm SCSI Mk II", esp->ehost);
+
+ /* Figure out our scsi ID on the bus */
+ esp->scsi_id = 7;
+
+ /* We don't have a differential SCSI-bus. */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ }
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Since the CyberStorm DMA is fully dedicated to the ESP chip,
+ * the number of bytes sent (to the ESP chip) equals the number
+ * of bytes in the FIFO - there is no buffering in the DMA controller.
+ * XXXX Do I read this right? It is from host to ESP, right?
+ */
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ /* I don't think there's any limit on the CyberDMA. So we use what
+ * the ESP chip can handle (24 bit).
+ */
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x1000000)
+ sz = 0x1000000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+ esp->esp_id, ((struct cyberII_dma_registers *)
+ (esp->dregs))->cond_reg));
+ ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+ custom.intreqr, custom.intenar));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct cyberII_dma_registers *dregs =
+ (struct cyberII_dma_registers *) esp->dregs;
+
+ cache_clear(addr, length);
+
+ addr &= ~(1);
+ dregs->dma_addr0 = (addr >> 24) & 0xff;
+ dregs->dma_addr1 = (addr >> 16) & 0xff;
+ dregs->dma_addr2 = (addr >> 8) & 0xff;
+ dregs->dma_addr3 = (addr ) & 0xff;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct cyberII_dma_registers *dregs =
+ (struct cyberII_dma_registers *) esp->dregs;
+
+ cache_push(addr, length);
+
+ addr |= 1;
+ dregs->dma_addr0 = (addr >> 24) & 0xff;
+ dregs->dma_addr1 = (addr >> 16) & 0xff;
+ dregs->dma_addr2 = (addr >> 8) & 0xff;
+ dregs->dma_addr3 = (addr ) & 0xff;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ /* It's important to check the DMA IRQ bit in the correct way! */
+ return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR);
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+ ((struct cyberII_dma_registers *)(esp->dregs))->ctrl_reg &= ~CYBERII_DMA_LED;
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+ ((struct cyberII_dma_registers *)(esp->dregs))->ctrl_reg |= CYBERII_DMA_LED;
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+#define HOSTS_C
+
+int cyberII_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ unsigned long address = (unsigned long)((struct NCR_ESP *)instance->hostdata)->edev;
+
+ esp_deallocate((struct NCR_ESP *)instance->hostdata);
+ esp_release();
+ release_mem_region(address, sizeof(struct ESP_regs));
+ free_irq(IRQ_AMIGA_PORTS, esp_intr);
+#endif
+ return 1;
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "esp-cyberstormII",
+ .proc_info = esp_proc_info,
+ .name = "CyberStorm Mk II SCSI",
+ .detect = cyberII_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = cyberII_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
new file mode 100644
index 000000000000..cca41cf8d3e7
--- /dev/null
+++ b/drivers/scsi/dc395x.c
@@ -0,0 +1,4942 @@
+/*
+ * dc395x.c
+ *
+ * Device Driver for Tekram DC395(U/UW/F), DC315(U)
+ * PCI SCSI Bus Master Host Adapter
+ * (SCSI chip set used Tekram ASIC TRM-S1040)
+ *
+ * Authors:
+ * C.L. Huang <ching@tekram.com.tw>
+ * Erich Chen <erich@tekram.com.tw>
+ * (C) Copyright 1995-1999 Tekram Technology Co., Ltd.
+ *
+ * Kurt Garloff <garloff@suse.de>
+ * (C) 1999-2000 Kurt Garloff
+ *
+ * Oliver Neukum <oliver@neukum.name>
+ * Ali Akcaagac <aliakc@web.de>
+ * Jamie Lenehan <lenehan@twibble.org>
+ * (C) 2003
+ *
+ * License: GNU GPL
+ *
+ *************************************************************************
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ************************************************************************
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsicam.h> /* needed for scsicam_bios_param */
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "dc395x.h"
+
+#define DC395X_NAME "dc395x"
+#define DC395X_BANNER "Tekram DC395(U/UW/F), DC315(U) - ASIC TRM-S1040"
+#define DC395X_VERSION "v2.05, 2004/03/08"
+
+/*---------------------------------------------------------------------------
+ Features
+ ---------------------------------------------------------------------------*/
+/*
+ * Set to disable parts of the driver
+ */
+/*#define DC395x_NO_DISCONNECT*/
+/*#define DC395x_NO_TAGQ*/
+/*#define DC395x_NO_SYNC*/
+/*#define DC395x_NO_WIDE*/
+
+/*---------------------------------------------------------------------------
+ Debugging
+ ---------------------------------------------------------------------------*/
+/*
+ * Types of debugging that can be enabled and disabled
+ */
+#define DBG_KG 0x0001
+#define DBG_0 0x0002
+#define DBG_1 0x0004
+#define DBG_SG 0x0020
+#define DBG_FIFO 0x0040
+#define DBG_PIO 0x0080
+
+
+/*
+ * Set set of things to output debugging for.
+ * Undefine to remove all debugging
+ */
+/*#define DEBUG_MASK (DBG_0|DBG_1|DBG_SG|DBG_FIFO|DBG_PIO)*/
+/*#define DEBUG_MASK DBG_0*/
+
+
+/*
+ * Output a kernel mesage at the specified level and append the
+ * driver name and a ": " to the start of the message
+ */
+#define dprintkl(level, format, arg...) \
+ printk(level DC395X_NAME ": " format , ## arg)
+
+
+#ifdef DEBUG_MASK
+/*
+ * print a debug message - this is formated with KERN_DEBUG, then the
+ * driver name followed by a ": " and then the message is output.
+ * This also checks that the specified debug level is enabled before
+ * outputing the message
+ */
+#define dprintkdbg(type, format, arg...) \
+ do { \
+ if ((type) & (DEBUG_MASK)) \
+ dprintkl(KERN_DEBUG , format , ## arg); \
+ } while (0)
+
+/*
+ * Check if the specified type of debugging is enabled
+ */
+#define debug_enabled(type) ((DEBUG_MASK) & (type))
+
+#else
+/*
+ * No debugging. Do nothing
+ */
+#define dprintkdbg(type, format, arg...) \
+ do {} while (0)
+#define debug_enabled(type) (0)
+
+#endif
+
+
+#ifndef PCI_VENDOR_ID_TEKRAM
+#define PCI_VENDOR_ID_TEKRAM 0x1DE1 /* Vendor ID */
+#endif
+#ifndef PCI_DEVICE_ID_TEKRAM_TRMS1040
+#define PCI_DEVICE_ID_TEKRAM_TRMS1040 0x0391 /* Device ID */
+#endif
+
+
+#define DC395x_LOCK_IO(dev,flags) spin_lock_irqsave(((struct Scsi_Host *)dev)->host_lock, flags)
+#define DC395x_UNLOCK_IO(dev,flags) spin_unlock_irqrestore(((struct Scsi_Host *)dev)->host_lock, flags)
+
+#define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address)))
+#define DC395x_read16(acb,address) (u16)(inw(acb->io_port_base + (address)))
+#define DC395x_read32(acb,address) (u32)(inl(acb->io_port_base + (address)))
+#define DC395x_write8(acb,address,value) outb((value), acb->io_port_base + (address))
+#define DC395x_write16(acb,address,value) outw((value), acb->io_port_base + (address))
+#define DC395x_write32(acb,address,value) outl((value), acb->io_port_base + (address))
+
+/* cmd->result */
+#define RES_TARGET 0x000000FF /* Target State */
+#define RES_TARGET_LNX STATUS_MASK /* Only official ... */
+#define RES_ENDMSG 0x0000FF00 /* End Message */
+#define RES_DID 0x00FF0000 /* DID_ codes */
+#define RES_DRV 0xFF000000 /* DRIVER_ codes */
+
+#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt))
+#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1)
+
+#define SET_RES_TARGET(who,tgt) { who &= ~RES_TARGET; who |= (int)(tgt); }
+#define SET_RES_TARGET_LNX(who,tgt) { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; }
+#define SET_RES_MSG(who,msg) { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; }
+#define SET_RES_DID(who,did) { who &= ~RES_DID; who |= (int)(did) << 16; }
+#define SET_RES_DRV(who,drv) { who &= ~RES_DRV; who |= (int)(drv) << 24; }
+
+#define TAG_NONE 255
+
+/*
+ * srb->segement_x is the hw sg list. It is always allocated as a
+ * DC395x_MAX_SG_LISTENTRY entries in a linear block which does not
+ * cross a page boundy.
+ */
+#define SEGMENTX_LEN (sizeof(struct SGentry)*DC395x_MAX_SG_LISTENTRY)
+#define VIRTX_LEN (sizeof(void *) * DC395x_MAX_SG_LISTENTRY)
+
+struct SGentry {
+ u32 address; /* bus! address */
+ u32 length;
+};
+
+/* The SEEPROM structure for TRM_S1040 */
+struct NVRamTarget {
+ u8 cfg0; /* Target configuration byte 0 */
+ u8 period; /* Target period */
+ u8 cfg2; /* Target configuration byte 2 */
+ u8 cfg3; /* Target configuration byte 3 */
+};
+
+struct NvRamType {
+ u8 sub_vendor_id[2]; /* 0,1 Sub Vendor ID */
+ u8 sub_sys_id[2]; /* 2,3 Sub System ID */
+ u8 sub_class; /* 4 Sub Class */
+ u8 vendor_id[2]; /* 5,6 Vendor ID */
+ u8 device_id[2]; /* 7,8 Device ID */
+ u8 reserved; /* 9 Reserved */
+ struct NVRamTarget target[DC395x_MAX_SCSI_ID];
+ /** 10,11,12,13
+ ** 14,15,16,17
+ ** ....
+ ** ....
+ ** 70,71,72,73
+ */
+ u8 scsi_id; /* 74 Host Adapter SCSI ID */
+ u8 channel_cfg; /* 75 Channel configuration */
+ u8 delay_time; /* 76 Power on delay time */
+ u8 max_tag; /* 77 Maximum tags */
+ u8 reserved0; /* 78 */
+ u8 boot_target; /* 79 */
+ u8 boot_lun; /* 80 */
+ u8 reserved1; /* 81 */
+ u16 reserved2[22]; /* 82,..125 */
+ u16 cksum; /* 126,127 */
+};
+
+struct ScsiReqBlk {
+ struct list_head list; /* next/prev ptrs for srb lists */
+ struct DeviceCtlBlk *dcb;
+ struct scsi_cmnd *cmd;
+
+ struct SGentry *segment_x; /* Linear array of hw sg entries (up to 64 entries) */
+ u32 sg_bus_addr; /* Bus address of sg list (ie, of segment_x) */
+
+ u8 sg_count; /* No of HW sg entries for this request */
+ u8 sg_index; /* Index of HW sg entry for this request */
+ u32 total_xfer_length; /* Total number of bytes remaining to be transfered */
+ void **virt_map;
+ unsigned char *virt_addr; /* Virtual address of current transfer position */
+
+ /*
+ * The sense buffer handling function, request_sense, uses
+ * the first hw sg entry (segment_x[0]) and the transfer
+ * length (total_xfer_length). While doing this it stores the
+ * original values into the last sg hw list
+ * (srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1] and the
+ * total_xfer_length in xferred. These values are restored in
+ * pci_unmap_srb_sense. This is the only place xferred is used.
+ */
+ u32 xferred; /* Saved copy of total_xfer_length */
+
+ u16 state;
+
+ u8 msgin_buf[6];
+ u8 msgout_buf[6];
+
+ u8 adapter_status;
+ u8 target_status;
+ u8 msg_count;
+ u8 end_message;
+
+ u8 tag_number;
+ u8 status;
+ u8 retry_count;
+ u8 flag;
+
+ u8 scsi_phase;
+};
+
+struct DeviceCtlBlk {
+ struct list_head list; /* next/prev ptrs for the dcb list */
+ struct AdapterCtlBlk *acb;
+ struct list_head srb_going_list; /* head of going srb list */
+ struct list_head srb_waiting_list; /* head of waiting srb list */
+
+ struct ScsiReqBlk *active_srb;
+ u32 tag_mask;
+
+ u16 max_command;
+
+ u8 target_id; /* SCSI Target ID (SCSI Only) */
+ u8 target_lun; /* SCSI Log. Unit (SCSI Only) */
+ u8 identify_msg;
+ u8 dev_mode;
+
+ u8 inquiry7; /* To store Inquiry flags */
+ u8 sync_mode; /* 0:async mode */
+ u8 min_nego_period; /* for nego. */
+ u8 sync_period; /* for reg. */
+
+ u8 sync_offset; /* for reg. and nego.(low nibble) */
+ u8 flag;
+ u8 dev_type;
+ u8 init_tcq_flag;
+};
+
+struct AdapterCtlBlk {
+ struct Scsi_Host *scsi_host;
+
+ unsigned long io_port_base;
+ unsigned long io_port_len;
+
+ struct list_head dcb_list; /* head of going dcb list */
+ struct DeviceCtlBlk *dcb_run_robin;
+ struct DeviceCtlBlk *active_dcb;
+
+ struct list_head srb_free_list; /* head of free srb list */
+ struct ScsiReqBlk *tmp_srb;
+ struct timer_list waiting_timer;
+ struct timer_list selto_timer;
+
+ u16 srb_count;
+
+ u8 sel_timeout;
+
+ unsigned int irq_level;
+ u8 tag_max_num;
+ u8 acb_flag;
+ u8 gmode2;
+
+ u8 config;
+ u8 lun_chk;
+ u8 scan_devices;
+ u8 hostid_bit;
+
+ u8 dcb_map[DC395x_MAX_SCSI_ID];
+ struct DeviceCtlBlk *children[DC395x_MAX_SCSI_ID][32];
+
+ struct pci_dev *dev;
+
+ u8 msg_len;
+
+ struct ScsiReqBlk srb_array[DC395x_MAX_SRB_CNT];
+ struct ScsiReqBlk srb;
+
+ struct NvRamType eeprom; /* eeprom settings for this adapter */
+};
+
+
+/*---------------------------------------------------------------------------
+ Forward declarations
+ ---------------------------------------------------------------------------*/
+static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void command_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void msgout_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void data_out_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void data_in_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void command_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void status_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void msgin_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void nop0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void nop1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status);
+static void set_basic_config(struct AdapterCtlBlk *acb);
+static void cleanup_after_transfer(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb);
+static void reset_scsi_bus(struct AdapterCtlBlk *acb);
+static void data_io_transfer(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb, u16 io_dir);
+static void disconnect(struct AdapterCtlBlk *acb);
+static void reselect(struct AdapterCtlBlk *acb);
+static u8 start_scsi(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb);
+static inline void enable_msgout_abort(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb);
+static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb);
+static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_code,
+ struct scsi_cmnd *cmd, u8 force);
+static void scsi_reset_detect(struct AdapterCtlBlk *acb);
+static void pci_unmap_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb);
+static void pci_unmap_srb_sense(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb);
+static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb);
+static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb);
+static void set_xfer_rate(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb);
+static void waiting_timeout(unsigned long ptr);
+
+
+/*---------------------------------------------------------------------------
+ Static Data
+ ---------------------------------------------------------------------------*/
+static u16 current_sync_offset = 0;
+
+static void *dc395x_scsi_phase0[] = {
+ data_out_phase0,/* phase:0 */
+ data_in_phase0, /* phase:1 */
+ command_phase0, /* phase:2 */
+ status_phase0, /* phase:3 */
+ nop0, /* phase:4 PH_BUS_FREE .. initial phase */
+ nop0, /* phase:5 PH_BUS_FREE .. initial phase */
+ msgout_phase0, /* phase:6 */
+ msgin_phase0, /* phase:7 */
+};
+
+static void *dc395x_scsi_phase1[] = {
+ data_out_phase1,/* phase:0 */
+ data_in_phase1, /* phase:1 */
+ command_phase1, /* phase:2 */
+ status_phase1, /* phase:3 */
+ nop1, /* phase:4 PH_BUS_FREE .. initial phase */
+ nop1, /* phase:5 PH_BUS_FREE .. initial phase */
+ msgout_phase1, /* phase:6 */
+ msgin_phase1, /* phase:7 */
+};
+
+/*
+ *Fast20: 000 50ns, 20.0 MHz
+ * 001 75ns, 13.3 MHz
+ * 010 100ns, 10.0 MHz
+ * 011 125ns, 8.0 MHz
+ * 100 150ns, 6.6 MHz
+ * 101 175ns, 5.7 MHz
+ * 110 200ns, 5.0 MHz
+ * 111 250ns, 4.0 MHz
+ *
+ *Fast40(LVDS): 000 25ns, 40.0 MHz
+ * 001 50ns, 20.0 MHz
+ * 010 75ns, 13.3 MHz
+ * 011 100ns, 10.0 MHz
+ * 100 125ns, 8.0 MHz
+ * 101 150ns, 6.6 MHz
+ * 110 175ns, 5.7 MHz
+ * 111 200ns, 5.0 MHz
+ */
+/*static u8 clock_period[] = {12,19,25,31,37,44,50,62};*/
+
+/* real period:48ns,76ns,100ns,124ns,148ns,176ns,200ns,248ns */
+static u8 clock_period[] = { 12, 18, 25, 31, 37, 43, 50, 62 };
+static u16 clock_speed[] = { 200, 133, 100, 80, 67, 58, 50, 40 };
+
+
+/*---------------------------------------------------------------------------
+ Configuration
+ ---------------------------------------------------------------------------*/
+/*
+ * Module/boot parameters currently effect *all* instances of the
+ * card in the system.
+ */
+
+/*
+ * Command line parameters are stored in a structure below.
+ * These are the index's into the structure for the various
+ * command line options.
+ */
+#define CFG_ADAPTER_ID 0
+#define CFG_MAX_SPEED 1
+#define CFG_DEV_MODE 2
+#define CFG_ADAPTER_MODE 3
+#define CFG_TAGS 4
+#define CFG_RESET_DELAY 5
+
+#define CFG_NUM 6 /* number of configuration items */
+
+
+/*
+ * Value used to indicate that a command line override
+ * hasn't been used to modify the value.
+ */
+#define CFG_PARAM_UNSET -1
+
+
+/*
+ * Hold command line parameters.
+ */
+struct ParameterData {
+ int value; /* value of this setting */
+ int min; /* minimum value */
+ int max; /* maximum value */
+ int def; /* default value */
+ int safe; /* safe value */
+};
+static struct ParameterData __devinitdata cfg_data[] = {
+ { /* adapter id */
+ CFG_PARAM_UNSET,
+ 0,
+ 15,
+ 7,
+ 7
+ },
+ { /* max speed */
+ CFG_PARAM_UNSET,
+ 0,
+ 7,
+ 1, /* 13.3Mhz */
+ 4, /* 6.7Hmz */
+ },
+ { /* dev mode */
+ CFG_PARAM_UNSET,
+ 0,
+ 0x3f,
+ NTC_DO_PARITY_CHK | NTC_DO_DISCONNECT | NTC_DO_SYNC_NEGO |
+ NTC_DO_WIDE_NEGO | NTC_DO_TAG_QUEUEING |
+ NTC_DO_SEND_START,
+ NTC_DO_PARITY_CHK | NTC_DO_SEND_START
+ },
+ { /* adapter mode */
+ CFG_PARAM_UNSET,
+ 0,
+ 0x2f,
+#ifdef CONFIG_SCSI_MULTI_LUN
+ NAC_SCANLUN |
+#endif
+ NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET
+ /*| NAC_ACTIVE_NEG*/,
+ NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET | 0x08
+ },
+ { /* tags */
+ CFG_PARAM_UNSET,
+ 0,
+ 5,
+ 3, /* 16 tags (??) */
+ 2,
+ },
+ { /* reset delay */
+ CFG_PARAM_UNSET,
+ 0,
+ 180,
+ 1, /* 1 second */
+ 10, /* 10 seconds */
+ }
+};
+
+
+/*
+ * Safe settings. If set to zero the the BIOS/default values with
+ * command line overrides will be used. If set to 1 then safe and
+ * slow settings will be used.
+ */
+static int use_safe_settings = 0;
+module_param_named(safe, use_safe_settings, bool, 0);
+MODULE_PARM_DESC(safe, "Use safe and slow settings only. Default: false");
+
+
+module_param_named(adapter_id, cfg_data[CFG_ADAPTER_ID].value, int, 0);
+MODULE_PARM_DESC(adapter_id, "Adapter SCSI ID. Default 7 (0-15)");
+
+module_param_named(max_speed, cfg_data[CFG_MAX_SPEED].value, int, 0);
+MODULE_PARM_DESC(max_speed, "Maximum bus speed. Default 1 (0-7) Speeds: 0=20, 1=13.3, 2=10, 3=8, 4=6.7, 5=5.8, 6=5, 7=4 Mhz");
+
+module_param_named(dev_mode, cfg_data[CFG_DEV_MODE].value, int, 0);
+MODULE_PARM_DESC(dev_mode, "Device mode.");
+
+module_param_named(adapter_mode, cfg_data[CFG_ADAPTER_MODE].value, int, 0);
+MODULE_PARM_DESC(adapter_mode, "Adapter mode.");
+
+module_param_named(tags, cfg_data[CFG_TAGS].value, int, 0);
+MODULE_PARM_DESC(tags, "Number of tags (1<<x). Default 3 (0-5)");
+
+module_param_named(reset_delay, cfg_data[CFG_RESET_DELAY].value, int, 0);
+MODULE_PARM_DESC(reset_delay, "Reset delay in seconds. Default 1 (0-180)");
+
+
+/**
+ * set_safe_settings - if the use_safe_settings option is set then
+ * set all values to the safe and slow values.
+ **/
+static void __devinit set_safe_settings(void)
+{
+ if (use_safe_settings)
+ {
+ int i;
+
+ dprintkl(KERN_INFO, "Using safe settings.\n");
+ for (i = 0; i < CFG_NUM; i++)
+ {
+ cfg_data[i].value = cfg_data[i].safe;
+ }
+ }
+}
+
+
+/**
+ * fix_settings - reset any boot parameters which are out of range
+ * back to the default values.
+ **/
+static void __devinit fix_settings(void)
+{
+ int i;
+
+ dprintkdbg(DBG_1,
+ "setup: AdapterId=%08x MaxSpeed=%08x DevMode=%08x "
+ "AdapterMode=%08x Tags=%08x ResetDelay=%08x\n",
+ cfg_data[CFG_ADAPTER_ID].value,
+ cfg_data[CFG_MAX_SPEED].value,
+ cfg_data[CFG_DEV_MODE].value,
+ cfg_data[CFG_ADAPTER_MODE].value,
+ cfg_data[CFG_TAGS].value,
+ cfg_data[CFG_RESET_DELAY].value);
+ for (i = 0; i < CFG_NUM; i++)
+ {
+ if (cfg_data[i].value < cfg_data[i].min
+ || cfg_data[i].value > cfg_data[i].max)
+ cfg_data[i].value = cfg_data[i].def;
+ }
+}
+
+
+
+/*
+ * Mapping from the eeprom delay index value (index into this array)
+ * to the the number of actual seconds that the delay should be for.
+ */
+static char __devinitdata eeprom_index_to_delay_map[] =
+ { 1, 3, 5, 10, 16, 30, 60, 120 };
+
+
+/**
+ * eeprom_index_to_delay - Take the eeprom delay setting and convert it
+ * into a number of seconds.
+ *
+ * @eeprom: The eeprom structure in which we find the delay index to map.
+ **/
+static void __devinit eeprom_index_to_delay(struct NvRamType *eeprom)
+{
+ eeprom->delay_time = eeprom_index_to_delay_map[eeprom->delay_time];
+}
+
+
+/**
+ * delay_to_eeprom_index - Take a delay in seconds and return the
+ * closest eeprom index which will delay for at least that amount of
+ * seconds.
+ *
+ * @delay: The delay, in seconds, to find the eeprom index for.
+ **/
+static int __devinit delay_to_eeprom_index(int delay)
+{
+ u8 idx = 0;
+ while (idx < 7 && eeprom_index_to_delay_map[idx] < delay)
+ idx++;
+ return idx;
+}
+
+
+/**
+ * eeprom_override - Override the eeprom settings, in the provided
+ * eeprom structure, with values that have been set on the command
+ * line.
+ *
+ * @eeprom: The eeprom data to override with command line options.
+ **/
+static void __devinit eeprom_override(struct NvRamType *eeprom)
+{
+ u8 id;
+
+ /* Adapter Settings */
+ if (cfg_data[CFG_ADAPTER_ID].value != CFG_PARAM_UNSET)
+ eeprom->scsi_id = (u8)cfg_data[CFG_ADAPTER_ID].value;
+
+ if (cfg_data[CFG_ADAPTER_MODE].value != CFG_PARAM_UNSET)
+ eeprom->channel_cfg = (u8)cfg_data[CFG_ADAPTER_MODE].value;
+
+ if (cfg_data[CFG_RESET_DELAY].value != CFG_PARAM_UNSET)
+ eeprom->delay_time = delay_to_eeprom_index(
+ cfg_data[CFG_RESET_DELAY].value);
+
+ if (cfg_data[CFG_TAGS].value != CFG_PARAM_UNSET)
+ eeprom->max_tag = (u8)cfg_data[CFG_TAGS].value;
+
+ /* Device Settings */
+ for (id = 0; id < DC395x_MAX_SCSI_ID; id++) {
+ if (cfg_data[CFG_DEV_MODE].value != CFG_PARAM_UNSET)
+ eeprom->target[id].cfg0 =
+ (u8)cfg_data[CFG_DEV_MODE].value;
+
+ if (cfg_data[CFG_MAX_SPEED].value != CFG_PARAM_UNSET)
+ eeprom->target[id].period =
+ (u8)cfg_data[CFG_MAX_SPEED].value;
+
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ ---------------------------------------------------------------------------*/
+
+static unsigned int list_size(struct list_head *head)
+{
+ unsigned int count = 0;
+ struct list_head *pos;
+ list_for_each(pos, head)
+ count++;
+ return count;
+}
+
+
+static struct DeviceCtlBlk *dcb_get_next(struct list_head *head,
+ struct DeviceCtlBlk *pos)
+{
+ int use_next = 0;
+ struct DeviceCtlBlk* next = NULL;
+ struct DeviceCtlBlk* i;
+
+ if (list_empty(head))
+ return NULL;
+
+ /* find supplied dcb and then select the next one */
+ list_for_each_entry(i, head, list)
+ if (use_next) {
+ next = i;
+ break;
+ } else if (i == pos) {
+ use_next = 1;
+ }
+ /* if no next one take the head one (ie, wraparound) */
+ if (!next)
+ list_for_each_entry(i, head, list) {
+ next = i;
+ break;
+ }
+
+ return next;
+}
+
+
+static void free_tag(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
+{
+ if (srb->tag_number < 255) {
+ dcb->tag_mask &= ~(1 << srb->tag_number); /* free tag mask */
+ srb->tag_number = 255;
+ }
+}
+
+
+/* Find cmd in SRB list */
+inline static struct ScsiReqBlk *find_cmd(struct scsi_cmnd *cmd,
+ struct list_head *head)
+{
+ struct ScsiReqBlk *i;
+ list_for_each_entry(i, head, list)
+ if (i->cmd == cmd)
+ return i;
+ return NULL;
+}
+
+
+static struct ScsiReqBlk *srb_get_free(struct AdapterCtlBlk *acb)
+{
+ struct list_head *head = &acb->srb_free_list;
+ struct ScsiReqBlk *srb = NULL;
+
+ if (!list_empty(head)) {
+ srb = list_entry(head->next, struct ScsiReqBlk, list);
+ list_del(head->next);
+ dprintkdbg(DBG_0, "srb_get_free: srb=%p\n", srb);
+ }
+ return srb;
+}
+
+
+static void srb_free_insert(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
+{
+ dprintkdbg(DBG_0, "srb_free_insert: srb=%p\n", srb);
+ list_add_tail(&srb->list, &acb->srb_free_list);
+}
+
+
+static void srb_waiting_insert(struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ dprintkdbg(DBG_0, "srb_waiting_insert: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+ list_add(&srb->list, &dcb->srb_waiting_list);
+}
+
+
+static void srb_waiting_append(struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ dprintkdbg(DBG_0, "srb_waiting_append: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+ list_add_tail(&srb->list, &dcb->srb_waiting_list);
+}
+
+
+static void srb_going_append(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
+{
+ dprintkdbg(DBG_0, "srb_going_append: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+ list_add_tail(&srb->list, &dcb->srb_going_list);
+}
+
+
+static void srb_going_remove(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
+{
+ struct ScsiReqBlk *i;
+ struct ScsiReqBlk *tmp;
+ dprintkdbg(DBG_0, "srb_going_remove: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+
+ list_for_each_entry_safe(i, tmp, &dcb->srb_going_list, list)
+ if (i == srb) {
+ list_del(&srb->list);
+ break;
+ }
+}
+
+
+static void srb_waiting_remove(struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ struct ScsiReqBlk *i;
+ struct ScsiReqBlk *tmp;
+ dprintkdbg(DBG_0, "srb_waiting_remove: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+
+ list_for_each_entry_safe(i, tmp, &dcb->srb_waiting_list, list)
+ if (i == srb) {
+ list_del(&srb->list);
+ break;
+ }
+}
+
+
+static void srb_going_to_waiting_move(struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ dprintkdbg(DBG_0,
+ "srb_going_to_waiting_move: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+ list_move(&srb->list, &dcb->srb_waiting_list);
+}
+
+
+static void srb_waiting_to_going_move(struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ dprintkdbg(DBG_0,
+ "srb_waiting_to_going_move: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+ list_move(&srb->list, &dcb->srb_going_list);
+}
+
+
+/* Sets the timer to wake us up */
+static void waiting_set_timer(struct AdapterCtlBlk *acb, unsigned long to)
+{
+ if (timer_pending(&acb->waiting_timer))
+ return;
+ init_timer(&acb->waiting_timer);
+ acb->waiting_timer.function = waiting_timeout;
+ acb->waiting_timer.data = (unsigned long) acb;
+ if (time_before(jiffies + to, acb->scsi_host->last_reset - HZ / 2))
+ acb->waiting_timer.expires =
+ acb->scsi_host->last_reset - HZ / 2 + 1;
+ else
+ acb->waiting_timer.expires = jiffies + to + 1;
+ add_timer(&acb->waiting_timer);
+}
+
+
+/* Send the next command from the waiting list to the bus */
+static void waiting_process_next(struct AdapterCtlBlk *acb)
+{
+ struct DeviceCtlBlk *start = NULL;
+ struct DeviceCtlBlk *pos;
+ struct DeviceCtlBlk *dcb;
+ struct ScsiReqBlk *srb;
+ struct list_head *dcb_list_head = &acb->dcb_list;
+
+ if (acb->active_dcb
+ || (acb->acb_flag & (RESET_DETECT + RESET_DONE + RESET_DEV)))
+ return;
+
+ if (timer_pending(&acb->waiting_timer))
+ del_timer(&acb->waiting_timer);
+
+ if (list_empty(dcb_list_head))
+ return;
+
+ /*
+ * Find the starting dcb. Need to find it again in the list
+ * since the list may have changed since we set the ptr to it
+ */
+ list_for_each_entry(dcb, dcb_list_head, list)
+ if (dcb == acb->dcb_run_robin) {
+ start = dcb;
+ break;
+ }
+ if (!start) {
+ /* This can happen! */
+ start = list_entry(dcb_list_head->next, typeof(*start), list);
+ acb->dcb_run_robin = start;
+ }
+
+
+ /*
+ * Loop over the dcb, but we start somewhere (potentially) in
+ * the middle of the loop so we need to manully do this.
+ */
+ pos = start;
+ do {
+ struct list_head *waiting_list_head = &pos->srb_waiting_list;
+
+ /* Make sure, the next another device gets scheduled ... */
+ acb->dcb_run_robin = dcb_get_next(dcb_list_head,
+ acb->dcb_run_robin);
+
+ if (list_empty(waiting_list_head) ||
+ pos->max_command <= list_size(&pos->srb_going_list)) {
+ /* move to next dcb */
+ pos = dcb_get_next(dcb_list_head, pos);
+ } else {
+ srb = list_entry(waiting_list_head->next,
+ struct ScsiReqBlk, list);
+
+ /* Try to send to the bus */
+ if (!start_scsi(acb, pos, srb))
+ srb_waiting_to_going_move(pos, srb);
+ else
+ waiting_set_timer(acb, HZ/50);
+ break;
+ }
+ } while (pos != start);
+}
+
+
+/* Wake up waiting queue */
+static void waiting_timeout(unsigned long ptr)
+{
+ unsigned long flags;
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)ptr;
+ dprintkdbg(DBG_1,
+ "waiting_timeout: Queue woken up by timer. acb=%p\n", acb);
+ DC395x_LOCK_IO(acb->scsi_host, flags);
+ waiting_process_next(acb);
+ DC395x_UNLOCK_IO(acb->scsi_host, flags);
+}
+
+
+/* Get the DCB for a given ID/LUN combination */
+static struct DeviceCtlBlk *find_dcb(struct AdapterCtlBlk *acb, u8 id, u8 lun)
+{
+ return acb->children[id][lun];
+}
+
+
+/* Send SCSI Request Block (srb) to adapter (acb) */
+static void send_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+
+ if (dcb->max_command <= list_size(&dcb->srb_going_list) ||
+ acb->active_dcb ||
+ (acb->acb_flag & (RESET_DETECT + RESET_DONE + RESET_DEV))) {
+ srb_waiting_append(dcb, srb);
+ waiting_process_next(acb);
+ return;
+ }
+
+ if (!start_scsi(acb, dcb, srb))
+ srb_going_append(dcb, srb);
+ else {
+ srb_waiting_insert(dcb, srb);
+ waiting_set_timer(acb, HZ / 50);
+ }
+}
+
+
+/* Prepare SRB for being sent to Device DCB w/ command *cmd */
+static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ enum dma_data_direction dir = cmd->sc_data_direction;
+ dprintkdbg(DBG_0, "build_srb: (pid#%li) <%02i-%i>\n",
+ cmd->pid, dcb->target_id, dcb->target_lun);
+
+ srb->dcb = dcb;
+ srb->cmd = cmd;
+ srb->sg_count = 0;
+ srb->total_xfer_length = 0;
+ srb->sg_bus_addr = 0;
+ srb->virt_addr = NULL;
+ srb->sg_index = 0;
+ srb->adapter_status = 0;
+ srb->target_status = 0;
+ srb->msg_count = 0;
+ srb->status = 0;
+ srb->flag = 0;
+ srb->state = 0;
+ srb->retry_count = 0;
+ srb->tag_number = TAG_NONE;
+ srb->scsi_phase = PH_BUS_FREE; /* initial phase */
+ srb->end_message = 0;
+
+ if (dir == PCI_DMA_NONE || !cmd->request_buffer) {
+ dprintkdbg(DBG_0,
+ "build_srb: [0] len=%d buf=%p use_sg=%d !MAP=%08x\n",
+ cmd->bufflen, cmd->request_buffer,
+ cmd->use_sg, srb->segment_x[0].address);
+ } else if (cmd->use_sg) {
+ int i;
+ u32 reqlen = cmd->request_bufflen;
+ struct scatterlist *sl = (struct scatterlist *)
+ cmd->request_buffer;
+ struct SGentry *sgp = srb->segment_x;
+ srb->sg_count = pci_map_sg(dcb->acb->dev, sl, cmd->use_sg,
+ dir);
+ dprintkdbg(DBG_0,
+ "build_srb: [n] len=%d buf=%p use_sg=%d segs=%d\n",
+ reqlen, cmd->request_buffer, cmd->use_sg,
+ srb->sg_count);
+
+ for (i = 0; i < srb->sg_count; i++) {
+ u32 seglen = (u32)sg_dma_len(sl + i);
+ sgp[i].address = (u32)sg_dma_address(sl + i);
+ sgp[i].length = seglen;
+ srb->total_xfer_length += seglen;
+ srb->virt_map[i] = kmap(sl[i].page);
+ }
+ srb->virt_addr = srb->virt_map[0];
+ sgp += srb->sg_count - 1;
+
+ /*
+ * adjust last page if too big as it is allocated
+ * on even page boundaries
+ */
+ if (srb->total_xfer_length > reqlen) {
+ sgp->length -= (srb->total_xfer_length - reqlen);
+ srb->total_xfer_length = reqlen;
+ }
+
+ /* Fixup for WIDE padding - make sure length is even */
+ if (dcb->sync_period & WIDE_SYNC &&
+ srb->total_xfer_length % 2) {
+ srb->total_xfer_length++;
+ sgp->length++;
+ }
+
+ srb->sg_bus_addr = pci_map_single(dcb->acb->dev,
+ srb->segment_x,
+ SEGMENTX_LEN,
+ PCI_DMA_TODEVICE);
+
+ dprintkdbg(DBG_SG, "build_srb: [n] map sg %p->%08x(%05x)\n",
+ srb->segment_x, srb->sg_bus_addr, SEGMENTX_LEN);
+ } else {
+ srb->total_xfer_length = cmd->request_bufflen;
+ srb->sg_count = 1;
+ srb->segment_x[0].address =
+ pci_map_single(dcb->acb->dev, cmd->request_buffer,
+ srb->total_xfer_length, dir);
+
+ /* Fixup for WIDE padding - make sure length is even */
+ if (dcb->sync_period & WIDE_SYNC && srb->total_xfer_length % 2)
+ srb->total_xfer_length++;
+
+ srb->segment_x[0].length = srb->total_xfer_length;
+ srb->virt_addr = cmd->request_buffer;
+ dprintkdbg(DBG_0,
+ "build_srb: [1] len=%d buf=%p use_sg=%d map=%08x\n",
+ srb->total_xfer_length, cmd->request_buffer,
+ cmd->use_sg, srb->segment_x[0].address);
+ }
+}
+
+
+/**
+ * dc395x_queue_command - queue scsi command passed from the mid
+ * layer, invoke 'done' on completion
+ *
+ * @cmd: pointer to scsi command object
+ * @done: function pointer to be invoked on completion
+ *
+ * Returns 1 if the adapter (host) is busy, else returns 0. One
+ * reason for an adapter to be busy is that the number
+ * of outstanding queued commands is already equal to
+ * struct Scsi_Host::can_queue .
+ *
+ * Required: if struct Scsi_Host::can_queue is ever non-zero
+ * then this function is required.
+ *
+ * Locks: struct Scsi_Host::host_lock held on entry (with "irqsave")
+ * and is expected to be held on return.
+ *
+ **/
+static int dc395x_queue_command(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct DeviceCtlBlk *dcb;
+ struct ScsiReqBlk *srb;
+ struct AdapterCtlBlk *acb =
+ (struct AdapterCtlBlk *)cmd->device->host->hostdata;
+ dprintkdbg(DBG_0, "queue_command: (pid#%li) <%02i-%i> cmnd=0x%02x\n",
+ cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+
+ /* Assume BAD_TARGET; will be cleared later */
+ cmd->result = DID_BAD_TARGET << 16;
+
+ /* ignore invalid targets */
+ if (cmd->device->id >= acb->scsi_host->max_id ||
+ cmd->device->lun >= acb->scsi_host->max_lun ||
+ cmd->device->lun >31) {
+ goto complete;
+ }
+
+ /* does the specified lun on the specified device exist */
+ if (!(acb->dcb_map[cmd->device->id] & (1 << cmd->device->lun))) {
+ dprintkl(KERN_INFO, "queue_command: Ignore target <%02i-%i>\n",
+ cmd->device->id, cmd->device->lun);
+ goto complete;
+ }
+
+ /* do we have a DCB for the device */
+ dcb = find_dcb(acb, cmd->device->id, cmd->device->lun);
+ if (!dcb) {
+ /* should never happen */
+ dprintkl(KERN_ERR, "queue_command: No such device <%02i-%i>",
+ cmd->device->id, cmd->device->lun);
+ goto complete;
+ }
+
+ /* set callback and clear result in the command */
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+ srb = srb_get_free(acb);
+ if (!srb)
+ {
+ /*
+ * Return 1 since we are unable to queue this command at this
+ * point in time.
+ */
+ dprintkdbg(DBG_0, "queue_command: No free srb's\n");
+ return 1;
+ }
+
+ build_srb(cmd, dcb, srb);
+
+ if (!list_empty(&dcb->srb_waiting_list)) {
+ /* append to waiting queue */
+ srb_waiting_append(dcb, srb);
+ waiting_process_next(acb);
+ } else {
+ /* process immediately */
+ send_srb(acb, srb);
+ }
+ dprintkdbg(DBG_1, "queue_command: (pid#%li) done\n", cmd->pid);
+ return 0;
+
+complete:
+ /*
+ * Complete the command immediatey, and then return 0 to
+ * indicate that we have handled the command. This is usually
+ * done when the commad is for things like non existent
+ * devices.
+ */
+ done(cmd);
+ return 0;
+}
+
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int dc395x_bios_param(struct scsi_device *sdev,
+ struct block_device *bdev, sector_t capacity, int *info)
+{
+#ifdef CONFIG_SCSI_DC395x_TRMS1040_TRADMAP
+ int heads, sectors, cylinders;
+ struct AdapterCtlBlk *acb;
+ int size = capacity;
+
+ dprintkdbg(DBG_0, "dc395x_bios_param..............\n");
+ acb = (struct AdapterCtlBlk *)sdev->host->hostdata;
+ heads = 64;
+ sectors = 32;
+ cylinders = size / (heads * sectors);
+
+ if ((acb->gmode2 & NAC_GREATER_1G) && (cylinders > 1024)) {
+ heads = 255;
+ sectors = 63;
+ cylinders = size / (heads * sectors);
+ }
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ return 0;
+#else
+ return scsicam_bios_param(bdev, capacity, info);
+#endif
+}
+
+
+static void dump_register_info(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
+{
+ u16 pstat;
+ struct pci_dev *dev = acb->dev;
+ pci_read_config_word(dev, PCI_STATUS, &pstat);
+ if (!dcb)
+ dcb = acb->active_dcb;
+ if (!srb && dcb)
+ srb = dcb->active_srb;
+ if (srb) {
+ if (!srb->cmd)
+ dprintkl(KERN_INFO, "dump: srb=%p cmd=%p OOOPS!\n",
+ srb, srb->cmd);
+ else
+ dprintkl(KERN_INFO, "dump: srb=%p cmd=%p (pid#%li) "
+ "cmnd=0x%02x <%02i-%i>\n",
+ srb, srb->cmd, srb->cmd->pid,
+ srb->cmd->cmnd[0], srb->cmd->device->id,
+ srb->cmd->device->lun);
+ printk(" sglist=%p cnt=%i idx=%i len=%i\n",
+ srb->segment_x, srb->sg_count, srb->sg_index,
+ srb->total_xfer_length);
+ printk(" state=0x%04x status=0x%02x phase=0x%02x (%sconn.)\n",
+ srb->state, srb->status, srb->scsi_phase,
+ (acb->active_dcb) ? "" : "not");
+ }
+ dprintkl(KERN_INFO, "dump: SCSI{status=0x%04x fifocnt=0x%02x "
+ "signals=0x%02x irqstat=0x%02x sync=0x%02x target=0x%02x "
+ "rselid=0x%02x ctr=0x%08x irqen=0x%02x config=0x%04x "
+ "config2=0x%02x cmd=0x%02x selto=0x%02x}\n",
+ DC395x_read16(acb, TRM_S1040_SCSI_STATUS),
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
+ DC395x_read8(acb, TRM_S1040_SCSI_SIGNAL),
+ DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS),
+ DC395x_read8(acb, TRM_S1040_SCSI_SYNC),
+ DC395x_read8(acb, TRM_S1040_SCSI_TARGETID),
+ DC395x_read8(acb, TRM_S1040_SCSI_IDMSG),
+ DC395x_read32(acb, TRM_S1040_SCSI_COUNTER),
+ DC395x_read8(acb, TRM_S1040_SCSI_INTEN),
+ DC395x_read16(acb, TRM_S1040_SCSI_CONFIG0),
+ DC395x_read8(acb, TRM_S1040_SCSI_CONFIG2),
+ DC395x_read8(acb, TRM_S1040_SCSI_COMMAND),
+ DC395x_read8(acb, TRM_S1040_SCSI_TIMEOUT));
+ dprintkl(KERN_INFO, "dump: DMA{cmd=0x%04x fifocnt=0x%02x fstat=0x%02x "
+ "irqstat=0x%02x irqen=0x%02x cfg=0x%04x tctr=0x%08x "
+ "ctctr=0x%08x addr=0x%08x:0x%08x}\n",
+ DC395x_read16(acb, TRM_S1040_DMA_COMMAND),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
+ DC395x_read8(acb, TRM_S1040_DMA_STATUS),
+ DC395x_read8(acb, TRM_S1040_DMA_INTEN),
+ DC395x_read16(acb, TRM_S1040_DMA_CONFIG),
+ DC395x_read32(acb, TRM_S1040_DMA_XCNT),
+ DC395x_read32(acb, TRM_S1040_DMA_CXCNT),
+ DC395x_read32(acb, TRM_S1040_DMA_XHIGHADDR),
+ DC395x_read32(acb, TRM_S1040_DMA_XLOWADDR));
+ dprintkl(KERN_INFO, "dump: gen{gctrl=0x%02x gstat=0x%02x gtmr=0x%02x} "
+ "pci{status=0x%04x}\n",
+ DC395x_read8(acb, TRM_S1040_GEN_CONTROL),
+ DC395x_read8(acb, TRM_S1040_GEN_STATUS),
+ DC395x_read8(acb, TRM_S1040_GEN_TIMER),
+ pstat);
+}
+
+
+static inline void clear_fifo(struct AdapterCtlBlk *acb, char *txt)
+{
+#if debug_enabled(DBG_FIFO)
+ u8 lines = DC395x_read8(acb, TRM_S1040_SCSI_SIGNAL);
+ u8 fifocnt = DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT);
+ if (!(fifocnt & 0x40))
+ dprintkdbg(DBG_FIFO,
+ "clear_fifo: (%i bytes) on phase %02x in %s\n",
+ fifocnt & 0x3f, lines, txt);
+#endif
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_CLRFIFO);
+}
+
+
+static void reset_dev_param(struct AdapterCtlBlk *acb)
+{
+ struct DeviceCtlBlk *dcb;
+ struct NvRamType *eeprom = &acb->eeprom;
+ dprintkdbg(DBG_0, "reset_dev_param: acb=%p\n", acb);
+
+ list_for_each_entry(dcb, &acb->dcb_list, list) {
+ u8 period_index;
+
+ dcb->sync_mode &= ~(SYNC_NEGO_DONE + WIDE_NEGO_DONE);
+ dcb->sync_period = 0;
+ dcb->sync_offset = 0;
+
+ dcb->dev_mode = eeprom->target[dcb->target_id].cfg0;
+ period_index = eeprom->target[dcb->target_id].period & 0x07;
+ dcb->min_nego_period = clock_period[period_index];
+ if (!(dcb->dev_mode & NTC_DO_WIDE_NEGO)
+ || !(acb->config & HCC_WIDE_CARD))
+ dcb->sync_mode &= ~WIDE_NEGO_ENABLE;
+ }
+}
+
+
+/*
+ * perform a hard reset on the SCSI bus
+ * @cmd - some command for this host (for fetching hooks)
+ * Returns: SUCCESS (0x2002) on success, else FAILED (0x2003).
+ */
+static int dc395x_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+ struct AdapterCtlBlk *acb =
+ (struct AdapterCtlBlk *)cmd->device->host->hostdata;
+ dprintkl(KERN_INFO,
+ "eh_bus_reset: (pid#%li) target=<%02i-%i> cmd=%p\n",
+ cmd->pid, cmd->device->id, cmd->device->lun, cmd);
+
+ if (timer_pending(&acb->waiting_timer))
+ del_timer(&acb->waiting_timer);
+
+ /*
+ * disable interrupt
+ */
+ DC395x_write8(acb, TRM_S1040_DMA_INTEN, 0x00);
+ DC395x_write8(acb, TRM_S1040_SCSI_INTEN, 0x00);
+ DC395x_write8(acb, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE);
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, DMARESETMODULE);
+
+ reset_scsi_bus(acb);
+ udelay(500);
+
+ /* We may be in serious trouble. Wait some seconds */
+ acb->scsi_host->last_reset =
+ jiffies + 3 * HZ / 2 +
+ HZ * acb->eeprom.delay_time;
+
+ /*
+ * re-enable interrupt
+ */
+ /* Clear SCSI FIFO */
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO);
+ clear_fifo(acb, "eh_bus_reset");
+ /* Delete pending IRQ */
+ DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS);
+ set_basic_config(acb);
+
+ reset_dev_param(acb);
+ doing_srb_done(acb, DID_RESET, cmd, 0);
+ acb->active_dcb = NULL;
+ acb->acb_flag = 0; /* RESET_DETECT, RESET_DONE ,RESET_DEV */
+ waiting_process_next(acb);
+
+ return SUCCESS;
+}
+
+
+/*
+ * abort an errant SCSI command
+ * @cmd - command to be aborted
+ * Returns: SUCCESS (0x2002) on success, else FAILED (0x2003).
+ */
+static int dc395x_eh_abort(struct scsi_cmnd *cmd)
+{
+ /*
+ * Look into our command queues: If it has not been sent already,
+ * we remove it and return success. Otherwise fail.
+ */
+ struct AdapterCtlBlk *acb =
+ (struct AdapterCtlBlk *)cmd->device->host->hostdata;
+ struct DeviceCtlBlk *dcb;
+ struct ScsiReqBlk *srb;
+ dprintkl(KERN_INFO, "eh_abort: (pid#%li) target=<%02i-%i> cmd=%p\n",
+ cmd->pid, cmd->device->id, cmd->device->lun, cmd);
+
+ dcb = find_dcb(acb, cmd->device->id, cmd->device->lun);
+ if (!dcb) {
+ dprintkl(KERN_DEBUG, "eh_abort: No such device\n");
+ return FAILED;
+ }
+
+ srb = find_cmd(cmd, &dcb->srb_waiting_list);
+ if (srb) {
+ srb_waiting_remove(dcb, srb);
+ pci_unmap_srb_sense(acb, srb);
+ pci_unmap_srb(acb, srb);
+ free_tag(dcb, srb);
+ srb_free_insert(acb, srb);
+ dprintkl(KERN_DEBUG, "eh_abort: Command was waiting\n");
+ cmd->result = DID_ABORT << 16;
+ return SUCCESS;
+ }
+ srb = find_cmd(cmd, &dcb->srb_going_list);
+ if (srb) {
+ dprintkl(KERN_DEBUG, "eh_abort: Command in progress");
+ /* XXX: Should abort the command here */
+ } else {
+ dprintkl(KERN_DEBUG, "eh_abort: Command not found");
+ }
+ return FAILED;
+}
+
+
+/* SDTR */
+static void build_sdtr(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ u8 *ptr = srb->msgout_buf + srb->msg_count;
+ if (srb->msg_count > 1) {
+ dprintkl(KERN_INFO,
+ "build_sdtr: msgout_buf BUSY (%i: %02x %02x)\n",
+ srb->msg_count, srb->msgout_buf[0],
+ srb->msgout_buf[1]);
+ return;
+ }
+ if (!(dcb->dev_mode & NTC_DO_SYNC_NEGO)) {
+ dcb->sync_offset = 0;
+ dcb->min_nego_period = 200 >> 2;
+ } else if (dcb->sync_offset == 0)
+ dcb->sync_offset = SYNC_NEGO_OFFSET;
+
+ *ptr++ = MSG_EXTENDED; /* (01h) */
+ *ptr++ = 3; /* length */
+ *ptr++ = EXTENDED_SDTR; /* (01h) */
+ *ptr++ = dcb->min_nego_period; /* Transfer period (in 4ns) */
+ *ptr++ = dcb->sync_offset; /* Transfer period (max. REQ/ACK dist) */
+ srb->msg_count += 5;
+ srb->state |= SRB_DO_SYNC_NEGO;
+}
+
+
+/* WDTR */
+static void build_wdtr(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ u8 wide = ((dcb->dev_mode & NTC_DO_WIDE_NEGO) &
+ (acb->config & HCC_WIDE_CARD)) ? 1 : 0;
+ u8 *ptr = srb->msgout_buf + srb->msg_count;
+ if (srb->msg_count > 1) {
+ dprintkl(KERN_INFO,
+ "build_wdtr: msgout_buf BUSY (%i: %02x %02x)\n",
+ srb->msg_count, srb->msgout_buf[0],
+ srb->msgout_buf[1]);
+ return;
+ }
+ *ptr++ = MSG_EXTENDED; /* (01h) */
+ *ptr++ = 2; /* length */
+ *ptr++ = EXTENDED_WDTR; /* (03h) */
+ *ptr++ = wide;
+ srb->msg_count += 4;
+ srb->state |= SRB_DO_WIDE_NEGO;
+}
+
+
+#if 0
+/* Timer to work around chip flaw: When selecting and the bus is
+ * busy, we sometimes miss a Selection timeout IRQ */
+void selection_timeout_missed(unsigned long ptr);
+/* Sets the timer to wake us up */
+static void selto_timer(struct AdapterCtlBlk *acb)
+{
+ if (timer_pending(&acb->selto_timer))
+ return;
+ acb->selto_timer.function = selection_timeout_missed;
+ acb->selto_timer.data = (unsigned long) acb;
+ if (time_before
+ (jiffies + HZ, acb->scsi_host->last_reset + HZ / 2))
+ acb->selto_timer.expires =
+ acb->scsi_host->last_reset + HZ / 2 + 1;
+ else
+ acb->selto_timer.expires = jiffies + HZ + 1;
+ add_timer(&acb->selto_timer);
+}
+
+
+void selection_timeout_missed(unsigned long ptr)
+{
+ unsigned long flags;
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)ptr;
+ struct ScsiReqBlk *srb;
+ dprintkl(KERN_DEBUG, "Chip forgot to produce SelTO IRQ!\n");
+ if (!acb->active_dcb || !acb->active_dcb->active_srb) {
+ dprintkl(KERN_DEBUG, "... but no cmd pending? Oops!\n");
+ return;
+ }
+ DC395x_LOCK_IO(acb->scsi_host, flags);
+ srb = acb->active_dcb->active_srb;
+ disconnect(acb);
+ DC395x_UNLOCK_IO(acb->scsi_host, flags);
+}
+#endif
+
+
+static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
+ struct ScsiReqBlk* srb)
+{
+ u16 s_stat2, return_code;
+ u8 s_stat, scsicommand, i, identify_message;
+ u8 *ptr;
+ dprintkdbg(DBG_0, "start_scsi: (pid#%li) <%02i-%i> srb=%p\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun, srb);
+
+ srb->tag_number = TAG_NONE; /* acb->tag_max_num: had error read in eeprom */
+
+ s_stat = DC395x_read8(acb, TRM_S1040_SCSI_SIGNAL);
+ s_stat2 = 0;
+ s_stat2 = DC395x_read16(acb, TRM_S1040_SCSI_STATUS);
+#if 1
+ if (s_stat & 0x20 /* s_stat2 & 0x02000 */ ) {
+ dprintkdbg(DBG_KG, "start_scsi: (pid#%li) BUSY %02x %04x\n",
+ srb->cmd->pid, s_stat, s_stat2);
+ /*
+ * Try anyway?
+ *
+ * We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection
+ * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed!
+ * (This is likely to be a bug in the hardware. Obviously, most people
+ * only have one initiator per SCSI bus.)
+ * Instead let this fail and have the timer make sure the command is
+ * tried again after a short time
+ */
+ /*selto_timer (acb); */
+ return 1;
+ }
+#endif
+ if (acb->active_dcb) {
+ dprintkl(KERN_DEBUG, "start_scsi: (pid#%li) Attempt to start a"
+ "command while another command (pid#%li) is active.",
+ srb->cmd->pid,
+ acb->active_dcb->active_srb ?
+ acb->active_dcb->active_srb->cmd->pid : 0);
+ return 1;
+ }
+ if (DC395x_read16(acb, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) {
+ dprintkdbg(DBG_KG, "start_scsi: (pid#%li) Failed (busy)\n",
+ srb->cmd->pid);
+ return 1;
+ }
+ /* Allow starting of SCSI commands half a second before we allow the mid-level
+ * to queue them again after a reset */
+ if (time_before(jiffies, acb->scsi_host->last_reset - HZ / 2)) {
+ dprintkdbg(DBG_KG, "start_scsi: Refuse cmds (reset wait)\n");
+ return 1;
+ }
+
+ /* Flush FIFO */
+ clear_fifo(acb, "start_scsi");
+ DC395x_write8(acb, TRM_S1040_SCSI_HOSTID, acb->scsi_host->this_id);
+ DC395x_write8(acb, TRM_S1040_SCSI_TARGETID, dcb->target_id);
+ DC395x_write8(acb, TRM_S1040_SCSI_SYNC, dcb->sync_period);
+ DC395x_write8(acb, TRM_S1040_SCSI_OFFSET, dcb->sync_offset);
+ srb->scsi_phase = PH_BUS_FREE; /* initial phase */
+
+ identify_message = dcb->identify_msg;
+ /*DC395x_TRM_write8(TRM_S1040_SCSI_IDMSG, identify_message); */
+ /* Don't allow disconnection for AUTO_REQSENSE: Cont.All.Cond.! */
+ if (srb->flag & AUTO_REQSENSE)
+ identify_message &= 0xBF;
+
+ if (((srb->cmd->cmnd[0] == INQUIRY)
+ || (srb->cmd->cmnd[0] == REQUEST_SENSE)
+ || (srb->flag & AUTO_REQSENSE))
+ && (((dcb->sync_mode & WIDE_NEGO_ENABLE)
+ && !(dcb->sync_mode & WIDE_NEGO_DONE))
+ || ((dcb->sync_mode & SYNC_NEGO_ENABLE)
+ && !(dcb->sync_mode & SYNC_NEGO_DONE)))
+ && (dcb->target_lun == 0)) {
+ srb->msgout_buf[0] = identify_message;
+ srb->msg_count = 1;
+ scsicommand = SCMD_SEL_ATNSTOP;
+ srb->state = SRB_MSGOUT;
+#ifndef SYNC_FIRST
+ if (dcb->sync_mode & WIDE_NEGO_ENABLE
+ && dcb->inquiry7 & SCSI_INQ_WBUS16) {
+ build_wdtr(acb, dcb, srb);
+ goto no_cmd;
+ }
+#endif
+ if (dcb->sync_mode & SYNC_NEGO_ENABLE
+ && dcb->inquiry7 & SCSI_INQ_SYNC) {
+ build_sdtr(acb, dcb, srb);
+ goto no_cmd;
+ }
+ if (dcb->sync_mode & WIDE_NEGO_ENABLE
+ && dcb->inquiry7 & SCSI_INQ_WBUS16) {
+ build_wdtr(acb, dcb, srb);
+ goto no_cmd;
+ }
+ srb->msg_count = 0;
+ }
+ /* Send identify message */
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, identify_message);
+
+ scsicommand = SCMD_SEL_ATN;
+ srb->state = SRB_START_;
+#ifndef DC395x_NO_TAGQ
+ if ((dcb->sync_mode & EN_TAG_QUEUEING)
+ && (identify_message & 0xC0)) {
+ /* Send Tag message */
+ u32 tag_mask = 1;
+ u8 tag_number = 0;
+ while (tag_mask & dcb->tag_mask
+ && tag_number <= dcb->max_command) {
+ tag_mask = tag_mask << 1;
+ tag_number++;
+ }
+ if (tag_number >= dcb->max_command) {
+ dprintkl(KERN_WARNING, "start_scsi: (pid#%li) "
+ "Out of tags target=<%02i-%i>)\n",
+ srb->cmd->pid, srb->cmd->device->id,
+ srb->cmd->device->lun);
+ srb->state = SRB_READY;
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL,
+ DO_HWRESELECT);
+ return 1;
+ }
+ /* Send Tag id */
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, MSG_SIMPLE_QTAG);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, tag_number);
+ dcb->tag_mask |= tag_mask;
+ srb->tag_number = tag_number;
+ scsicommand = SCMD_SEL_ATN3;
+ srb->state = SRB_START_;
+ }
+#endif
+/*polling:*/
+ /* Send CDB ..command block ......... */
+ dprintkdbg(DBG_KG, "start_scsi: (pid#%li) <%02i-%i> cmnd=0x%02x tag=%i\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun,
+ srb->cmd->cmnd[0], srb->tag_number);
+ if (srb->flag & AUTO_REQSENSE) {
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, REQUEST_SENSE);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, (dcb->target_lun << 5));
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO,
+ sizeof(srb->cmd->sense_buffer));
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ } else {
+ ptr = (u8 *)srb->cmd->cmnd;
+ for (i = 0; i < srb->cmd->cmd_len; i++)
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, *ptr++);
+ }
+ no_cmd:
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL,
+ DO_HWRESELECT | DO_DATALATCH);
+ if (DC395x_read16(acb, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) {
+ /*
+ * If start_scsi return 1:
+ * we caught an interrupt (must be reset or reselection ... )
+ * : Let's process it first!
+ */
+ dprintkdbg(DBG_0, "start_scsi: (pid#%li) <%02i-%i> Failed - busy\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun);
+ srb->state = SRB_READY;
+ free_tag(dcb, srb);
+ srb->msg_count = 0;
+ return_code = 1;
+ /* This IRQ should NOT get lost, as we did not acknowledge it */
+ } else {
+ /*
+ * If start_scsi returns 0:
+ * we know that the SCSI processor is free
+ */
+ srb->scsi_phase = PH_BUS_FREE; /* initial phase */
+ dcb->active_srb = srb;
+ acb->active_dcb = dcb;
+ return_code = 0;
+ /* it's important for atn stop */
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL,
+ DO_DATALATCH | DO_HWRESELECT);
+ /* SCSI command */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, scsicommand);
+ }
+ return return_code;
+}
+
+
+#define DC395x_ENABLE_MSGOUT \
+ DC395x_write16 (acb, TRM_S1040_SCSI_CONTROL, DO_SETATN); \
+ srb->state |= SRB_MSGOUT
+
+
+/* abort command */
+static inline void enable_msgout_abort(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb)
+{
+ srb->msgout_buf[0] = ABORT;
+ srb->msg_count = 1;
+ DC395x_ENABLE_MSGOUT;
+ srb->state &= ~SRB_MSGIN;
+ srb->state |= SRB_MSGOUT;
+}
+
+
+/**
+ * dc395x_handle_interrupt - Handle an interrupt that has been confirmed to
+ * have been triggered for this card.
+ *
+ * @acb: a pointer to the adpter control block
+ * @scsi_status: the status return when we checked the card
+ **/
+static void dc395x_handle_interrupt(struct AdapterCtlBlk *acb,
+ u16 scsi_status)
+{
+ struct DeviceCtlBlk *dcb;
+ struct ScsiReqBlk *srb;
+ u16 phase;
+ u8 scsi_intstatus;
+ unsigned long flags;
+ void (*dc395x_statev)(struct AdapterCtlBlk *, struct ScsiReqBlk *,
+ u16 *);
+
+ DC395x_LOCK_IO(acb->scsi_host, flags);
+
+ /* This acknowledges the IRQ */
+ scsi_intstatus = DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS);
+ if ((scsi_status & 0x2007) == 0x2002)
+ dprintkl(KERN_DEBUG,
+ "COP after COP completed? %04x\n", scsi_status);
+ if (debug_enabled(DBG_KG)) {
+ if (scsi_intstatus & INT_SELTIMEOUT)
+ dprintkdbg(DBG_KG, "handle_interrupt: Selection timeout\n");
+ }
+ /*dprintkl(KERN_DEBUG, "handle_interrupt: intstatus = 0x%02x ", scsi_intstatus); */
+
+ if (timer_pending(&acb->selto_timer))
+ del_timer(&acb->selto_timer);
+
+ if (scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) {
+ disconnect(acb); /* bus free interrupt */
+ goto out_unlock;
+ }
+ if (scsi_intstatus & INT_RESELECTED) {
+ reselect(acb);
+ goto out_unlock;
+ }
+ if (scsi_intstatus & INT_SELECT) {
+ dprintkl(KERN_INFO, "Host does not support target mode!\n");
+ goto out_unlock;
+ }
+ if (scsi_intstatus & INT_SCSIRESET) {
+ scsi_reset_detect(acb);
+ goto out_unlock;
+ }
+ if (scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) {
+ dcb = acb->active_dcb;
+ if (!dcb) {
+ dprintkl(KERN_DEBUG,
+ "Oops: BusService (%04x %02x) w/o ActiveDCB!\n",
+ scsi_status, scsi_intstatus);
+ goto out_unlock;
+ }
+ srb = dcb->active_srb;
+ if (dcb->flag & ABORT_DEV_) {
+ dprintkdbg(DBG_0, "MsgOut Abort Device.....\n");
+ enable_msgout_abort(acb, srb);
+ }
+
+ /* software sequential machine */
+ phase = (u16)srb->scsi_phase;
+
+ /*
+ * 62037 or 62137
+ * call dc395x_scsi_phase0[]... "phase entry"
+ * handle every phase before start transfer
+ */
+ /* data_out_phase0, phase:0 */
+ /* data_in_phase0, phase:1 */
+ /* command_phase0, phase:2 */
+ /* status_phase0, phase:3 */
+ /* nop0, phase:4 PH_BUS_FREE .. initial phase */
+ /* nop0, phase:5 PH_BUS_FREE .. initial phase */
+ /* msgout_phase0, phase:6 */
+ /* msgin_phase0, phase:7 */
+ dc395x_statev = dc395x_scsi_phase0[phase];
+ dc395x_statev(acb, srb, &scsi_status);
+
+ /*
+ * if there were any exception occured scsi_status
+ * will be modify to bus free phase new scsi_status
+ * transfer out from ... previous dc395x_statev
+ */
+ srb->scsi_phase = scsi_status & PHASEMASK;
+ phase = (u16)scsi_status & PHASEMASK;
+
+ /*
+ * call dc395x_scsi_phase1[]... "phase entry" handle
+ * every phase to do transfer
+ */
+ /* data_out_phase1, phase:0 */
+ /* data_in_phase1, phase:1 */
+ /* command_phase1, phase:2 */
+ /* status_phase1, phase:3 */
+ /* nop1, phase:4 PH_BUS_FREE .. initial phase */
+ /* nop1, phase:5 PH_BUS_FREE .. initial phase */
+ /* msgout_phase1, phase:6 */
+ /* msgin_phase1, phase:7 */
+ dc395x_statev = dc395x_scsi_phase1[phase];
+ dc395x_statev(acb, srb, &scsi_status);
+ }
+ out_unlock:
+ DC395x_UNLOCK_IO(acb->scsi_host, flags);
+}
+
+
+static irqreturn_t dc395x_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)dev_id;
+ u16 scsi_status;
+ u8 dma_status;
+ irqreturn_t handled = IRQ_NONE;
+
+ /*
+ * Check for pending interupt
+ */
+ scsi_status = DC395x_read16(acb, TRM_S1040_SCSI_STATUS);
+ dma_status = DC395x_read8(acb, TRM_S1040_DMA_STATUS);
+ if (scsi_status & SCSIINTERRUPT) {
+ /* interupt pending - let's process it! */
+ dc395x_handle_interrupt(acb, scsi_status);
+ handled = IRQ_HANDLED;
+ }
+ else if (dma_status & 0x20) {
+ /* Error from the DMA engine */
+ dprintkl(KERN_INFO, "Interrupt from DMA engine: 0x%02x!\n", dma_status);
+#if 0
+ dprintkl(KERN_INFO, "This means DMA error! Try to handle ...\n");
+ if (acb->active_dcb) {
+ acb->active_dcb-> flag |= ABORT_DEV_;
+ if (acb->active_dcb->active_srb)
+ enable_msgout_abort(acb, acb->active_dcb->active_srb);
+ }
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, ABORTXFER | CLRXFIFO);
+#else
+ dprintkl(KERN_INFO, "Ignoring DMA error (probably a bad thing) ...\n");
+ acb = NULL;
+#endif
+ handled = IRQ_HANDLED;
+ }
+
+ return handled;
+}
+
+
+static void msgout_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "msgout_phase0: (pid#%li)\n", srb->cmd->pid);
+ if (srb->state & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT))
+ *pscsi_status = PH_BUS_FREE; /*.. initial phase */
+
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ srb->state &= ~SRB_MSGOUT;
+}
+
+
+static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ u16 i;
+ u8 *ptr;
+ dprintkdbg(DBG_0, "msgout_phase1: (pid#%li)\n", srb->cmd->pid);
+
+ clear_fifo(acb, "msgout_phase1");
+ if (!(srb->state & SRB_MSGOUT)) {
+ srb->state |= SRB_MSGOUT;
+ dprintkl(KERN_DEBUG,
+ "msgout_phase1: (pid#%li) Phase unexpected\n",
+ srb->cmd->pid); /* So what ? */
+ }
+ if (!srb->msg_count) {
+ dprintkdbg(DBG_0, "msgout_phase1: (pid#%li) NOP msg\n",
+ srb->cmd->pid);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, MSG_NOP);
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
+ return;
+ }
+ ptr = (u8 *)srb->msgout_buf;
+ for (i = 0; i < srb->msg_count; i++)
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, *ptr++);
+ srb->msg_count = 0;
+ if (srb->msgout_buf[0] == MSG_ABORT)
+ srb->state = SRB_ABORT_SENT;
+
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
+}
+
+
+static void command_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "command_phase0: (pid#%li)\n", srb->cmd->pid);
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
+}
+
+
+static void command_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ struct DeviceCtlBlk *dcb;
+ u8 *ptr;
+ u16 i;
+ dprintkdbg(DBG_0, "command_phase1: (pid#%li)\n", srb->cmd->pid);
+
+ clear_fifo(acb, "command_phase1");
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_CLRATN);
+ if (!(srb->flag & AUTO_REQSENSE)) {
+ ptr = (u8 *)srb->cmd->cmnd;
+ for (i = 0; i < srb->cmd->cmd_len; i++) {
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, *ptr);
+ ptr++;
+ }
+ } else {
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, REQUEST_SENSE);
+ dcb = acb->active_dcb;
+ /* target id */
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, (dcb->target_lun << 5));
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO,
+ sizeof(srb->cmd->sense_buffer));
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ }
+ srb->state |= SRB_COMMAND;
+ /* it's important for atn stop */
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
+ /* SCSI command */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
+}
+
+
+/*
+ * Verify that the remaining space in the hw sg lists is the same as
+ * the count of remaining bytes in srb->total_xfer_length
+ */
+static void sg_verify_length(struct ScsiReqBlk *srb)
+{
+ if (debug_enabled(DBG_SG)) {
+ unsigned len = 0;
+ unsigned idx = srb->sg_index;
+ struct SGentry *psge = srb->segment_x + idx;
+ for (; idx < srb->sg_count; psge++, idx++)
+ len += psge->length;
+ if (len != srb->total_xfer_length)
+ dprintkdbg(DBG_SG,
+ "Inconsistent SRB S/G lengths (Tot=%i, Count=%i) !!\n",
+ srb->total_xfer_length, len);
+ }
+}
+
+
+/*
+ * Compute the next Scatter Gather list index and adjust its length
+ * and address if necessary; also compute virt_addr
+ */
+static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
+{
+ u8 idx;
+ struct scatterlist *sg;
+ struct scsi_cmnd *cmd = srb->cmd;
+ int segment = cmd->use_sg;
+ u32 xferred = srb->total_xfer_length - left; /* bytes transfered */
+ struct SGentry *psge = srb->segment_x + srb->sg_index;
+ void **virt = srb->virt_map;
+
+ dprintkdbg(DBG_0,
+ "sg_update_list: Transfered %i of %i bytes, %i remain\n",
+ xferred, srb->total_xfer_length, left);
+ if (xferred == 0) {
+ /* nothing to update since we did not transfer any data */
+ return;
+ }
+
+ sg_verify_length(srb);
+ srb->total_xfer_length = left; /* update remaining count */
+ for (idx = srb->sg_index; idx < srb->sg_count; idx++) {
+ if (xferred >= psge->length) {
+ /* Complete SG entries done */
+ xferred -= psge->length;
+ } else {
+ /* Partial SG entry done */
+ psge->length -= xferred;
+ psge->address += xferred;
+ srb->sg_index = idx;
+ pci_dma_sync_single_for_device(srb->dcb->
+ acb->dev,
+ srb->sg_bus_addr,
+ SEGMENTX_LEN,
+ PCI_DMA_TODEVICE);
+ break;
+ }
+ psge++;
+ }
+ sg_verify_length(srb);
+
+ /* we need the corresponding virtual address */
+ if (!segment) {
+ srb->virt_addr += xferred;
+ return;
+ }
+
+ /* We have to walk the scatterlist to find it */
+ sg = (struct scatterlist *)cmd->request_buffer;
+ idx = 0;
+ while (segment--) {
+ unsigned long mask =
+ ~((unsigned long)sg->length - 1) & PAGE_MASK;
+ if ((sg_dma_address(sg) & mask) == (psge->address & mask)) {
+ srb->virt_addr = virt[idx] + (psge->address & ~PAGE_MASK);
+ return;
+ }
+ ++sg;
+ ++idx;
+ }
+
+ dprintkl(KERN_ERR, "sg_update_list: sg_to_virt failed\n");
+ srb->virt_addr = NULL;
+}
+
+
+/*
+ * We have transfered a single byte (PIO mode?) and need to update
+ * the count of bytes remaining (total_xfer_length) and update the sg
+ * entry to either point to next byte in the current sg entry, or of
+ * already at the end to point to the start of the next sg entry
+ */
+static void sg_subtract_one(struct ScsiReqBlk *srb)
+{
+ srb->total_xfer_length--;
+ srb->segment_x[srb->sg_index].length--;
+ if (srb->total_xfer_length &&
+ !srb->segment_x[srb->sg_index].length) {
+ if (debug_enabled(DBG_PIO))
+ printk(" (next segment)");
+ srb->sg_index++;
+ sg_update_list(srb, srb->total_xfer_length);
+ }
+}
+
+
+/*
+ * cleanup_after_transfer
+ *
+ * Makes sure, DMA and SCSI engine are empty, after the transfer has finished
+ * KG: Currently called from StatusPhase1 ()
+ * Should probably also be called from other places
+ * Best might be to call it in DataXXPhase0, if new phase will differ
+ */
+static void cleanup_after_transfer(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb)
+{
+ /*DC395x_write8 (TRM_S1040_DMA_STATUS, FORCEDMACOMP); */
+ if (DC395x_read16(acb, TRM_S1040_DMA_COMMAND) & 0x0001) { /* read */
+ if (!(DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) & 0x40))
+ clear_fifo(acb, "cleanup/in");
+ if (!(DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT) & 0x80))
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO);
+ } else { /* write */
+ if (!(DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT) & 0x80))
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO);
+ if (!(DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) & 0x40))
+ clear_fifo(acb, "cleanup/out");
+ }
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
+}
+
+
+/*
+ * Those no of bytes will be transfered w/ PIO through the SCSI FIFO
+ * Seems to be needed for unknown reasons; could be a hardware bug :-(
+ */
+#define DC395x_LASTPIO 4
+
+
+static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+ u16 scsi_status = *pscsi_status;
+ u32 d_left_counter = 0;
+ dprintkdbg(DBG_0, "data_out_phase0: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
+
+ /*
+ * KG: We need to drain the buffers before we draw any conclusions!
+ * This means telling the DMA to push the rest into SCSI, telling
+ * SCSI to push the rest to the bus.
+ * However, the device might have been the one to stop us (phase
+ * change), and the data in transit just needs to be accounted so
+ * it can be retransmitted.)
+ */
+ /*
+ * KG: Stop DMA engine pushing more data into the SCSI FIFO
+ * If we need more data, the DMA SG list will be freshly set up, anyway
+ */
+ dprintkdbg(DBG_PIO, "data_out_phase0: "
+ "DMA{fifcnt=0x%02x fifostat=0x%02x} "
+ "SCSI{fifocnt=0x%02x cnt=0x%06x status=0x%04x} total=0x%06x\n",
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
+ DC395x_read32(acb, TRM_S1040_SCSI_COUNTER), scsi_status,
+ srb->total_xfer_length);
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, STOPDMAXFER | CLRXFIFO);
+
+ if (!(srb->state & SRB_XFERPAD)) {
+ if (scsi_status & PARITYERROR)
+ srb->status |= PARITY_ERROR;
+
+ /*
+ * KG: Right, we can't just rely on the SCSI_COUNTER, because this
+ * is the no of bytes it got from the DMA engine not the no it
+ * transferred successfully to the device. (And the difference could
+ * be as much as the FIFO size, I guess ...)
+ */
+ if (!(scsi_status & SCSIXFERDONE)) {
+ /*
+ * when data transfer from DMA FIFO to SCSI FIFO
+ * if there was some data left in SCSI FIFO
+ */
+ d_left_counter =
+ (u32)(DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) &
+ 0x1F);
+ if (dcb->sync_period & WIDE_SYNC)
+ d_left_counter <<= 1;
+
+ dprintkdbg(DBG_KG, "data_out_phase0: FIFO contains %i %s\n"
+ "SCSI{fifocnt=0x%02x cnt=0x%08x} "
+ "DMA{fifocnt=0x%04x cnt=0x%02x ctr=0x%08x}\n",
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
+ (dcb->sync_period & WIDE_SYNC) ? "words" : "bytes",
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
+ DC395x_read32(acb, TRM_S1040_SCSI_COUNTER),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
+ DC395x_read32(acb, TRM_S1040_DMA_CXCNT));
+ }
+ /*
+ * calculate all the residue data that not yet transfered
+ * SCSI transfer counter + left in SCSI FIFO data
+ *
+ * .....TRM_S1040_SCSI_COUNTER (24bits)
+ * The counter always decrement by one for every SCSI byte transfer.
+ * .....TRM_S1040_SCSI_FIFOCNT ( 5bits)
+ * The counter is SCSI FIFO offset counter (in units of bytes or! words)
+ */
+ if (srb->total_xfer_length > DC395x_LASTPIO)
+ d_left_counter +=
+ DC395x_read32(acb, TRM_S1040_SCSI_COUNTER);
+
+ /* Is this a good idea? */
+ /*clear_fifo(acb, "DOP1"); */
+ /* KG: What is this supposed to be useful for? WIDE padding stuff? */
+ if (d_left_counter == 1 && dcb->sync_period & WIDE_SYNC
+ && srb->cmd->request_bufflen % 2) {
+ d_left_counter = 0;
+ dprintkl(KERN_INFO,
+ "data_out_phase0: Discard 1 byte (0x%02x)\n",
+ scsi_status);
+ }
+ /*
+ * KG: Oops again. Same thinko as above: The SCSI might have been
+ * faster than the DMA engine, so that it ran out of data.
+ * In that case, we have to do just nothing!
+ * But: Why the interrupt: No phase change. No XFERCNT_2_ZERO. Or?
+ */
+ /*
+ * KG: This is nonsense: We have been WRITING data to the bus
+ * If the SCSI engine has no bytes left, how should the DMA engine?
+ */
+ if (d_left_counter == 0) {
+ srb->total_xfer_length = 0;
+ } else {
+ /*
+ * if transfer not yet complete
+ * there were some data residue in SCSI FIFO or
+ * SCSI transfer counter not empty
+ */
+ long oldxferred =
+ srb->total_xfer_length - d_left_counter;
+ const int diff =
+ (dcb->sync_period & WIDE_SYNC) ? 2 : 1;
+ sg_update_list(srb, d_left_counter);
+ /* KG: Most ugly hack! Apparently, this works around a chip bug */
+ if ((srb->segment_x[srb->sg_index].length ==
+ diff && srb->cmd->use_sg)
+ || ((oldxferred & ~PAGE_MASK) ==
+ (PAGE_SIZE - diff))
+ ) {
+ dprintkl(KERN_INFO, "data_out_phase0: "
+ "Work around chip bug (%i)?\n", diff);
+ d_left_counter =
+ srb->total_xfer_length - diff;
+ sg_update_list(srb, d_left_counter);
+ /*srb->total_xfer_length -= diff; */
+ /*srb->virt_addr += diff; */
+ /*if (srb->cmd->use_sg) */
+ /* srb->sg_index++; */
+ }
+ }
+ }
+ if ((*pscsi_status & PHASEMASK) != PH_DATA_OUT) {
+ cleanup_after_transfer(acb, srb);
+ }
+}
+
+
+static void data_out_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "data_out_phase1: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
+ clear_fifo(acb, "data_out_phase1");
+ /* do prepare before transfer when data out phase */
+ data_io_transfer(acb, srb, XFERDATAOUT);
+}
+
+
+static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ u16 scsi_status = *pscsi_status;
+ u32 d_left_counter = 0;
+ dprintkdbg(DBG_0, "data_in_phase0: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
+
+ /*
+ * KG: DataIn is much more tricky than DataOut. When the device is finished
+ * and switches to another phase, the SCSI engine should be finished too.
+ * But: There might still be bytes left in its FIFO to be fetched by the DMA
+ * engine and transferred to memory.
+ * We should wait for the FIFOs to be emptied by that (is there any way to
+ * enforce this?) and then stop the DMA engine, because it might think, that
+ * there are more bytes to follow. Yes, the device might disconnect prior to
+ * having all bytes transferred!
+ * Also we should make sure that all data from the DMA engine buffer's really
+ * made its way to the system memory! Some documentation on this would not
+ * seem to be a bad idea, actually.
+ */
+ if (!(srb->state & SRB_XFERPAD)) {
+ if (scsi_status & PARITYERROR) {
+ dprintkl(KERN_INFO, "data_in_phase0: (pid#%li) "
+ "Parity Error\n", srb->cmd->pid);
+ srb->status |= PARITY_ERROR;
+ }
+ /*
+ * KG: We should wait for the DMA FIFO to be empty ...
+ * but: it would be better to wait first for the SCSI FIFO and then the
+ * the DMA FIFO to become empty? How do we know, that the device not already
+ * sent data to the FIFO in a MsgIn phase, eg.?
+ */
+ if (!(DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT) & 0x80)) {
+#if 0
+ int ctr = 6000000;
+ dprintkl(KERN_DEBUG,
+ "DIP0: Wait for DMA FIFO to flush ...\n");
+ /*DC395x_write8 (TRM_S1040_DMA_CONTROL, STOPDMAXFER); */
+ /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 7); */
+ /*DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_IN); */
+ while (!
+ (DC395x_read16(acb, TRM_S1040_DMA_FIFOSTAT) &
+ 0x80) && --ctr);
+ if (ctr < 6000000 - 1)
+ dprintkl(KERN_DEBUG
+ "DIP0: Had to wait for DMA ...\n");
+ if (!ctr)
+ dprintkl(KERN_ERR,
+ "Deadlock in DIP0 waiting for DMA FIFO empty!!\n");
+ /*DC395x_write32 (TRM_S1040_SCSI_COUNTER, 0); */
+#endif
+ dprintkdbg(DBG_KG, "data_in_phase0: "
+ "DMA{fifocnt=0x%02x fifostat=0x%02x}\n",
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT));
+ }
+ /* Now: Check remainig data: The SCSI counters should tell us ... */
+ d_left_counter = DC395x_read32(acb, TRM_S1040_SCSI_COUNTER)
+ + ((DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) & 0x1f)
+ << ((srb->dcb->sync_period & WIDE_SYNC) ? 1 :
+ 0));
+ dprintkdbg(DBG_KG, "data_in_phase0: "
+ "SCSI{fifocnt=0x%02x%s ctr=0x%08x} "
+ "DMA{fifocnt=0x%02x fifostat=0x%02x ctr=0x%08x} "
+ "Remain{totxfer=%i scsi_fifo+ctr=%i}\n",
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
+ (srb->dcb->sync_period & WIDE_SYNC) ? "words" : "bytes",
+ DC395x_read32(acb, TRM_S1040_SCSI_COUNTER),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
+ DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
+ DC395x_read32(acb, TRM_S1040_DMA_CXCNT),
+ srb->total_xfer_length, d_left_counter);
+#if DC395x_LASTPIO
+ /* KG: Less than or equal to 4 bytes can not be transfered via DMA, it seems. */
+ if (d_left_counter
+ && srb->total_xfer_length <= DC395x_LASTPIO) {
+ /*u32 addr = (srb->segment_x[srb->sg_index].address); */
+ /*sg_update_list (srb, d_left_counter); */
+ dprintkdbg(DBG_PIO, "data_in_phase0: PIO (%i %s) to "
+ "%p for remaining %i bytes:",
+ DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) & 0x1f,
+ (srb->dcb->sync_period & WIDE_SYNC) ?
+ "words" : "bytes",
+ srb->virt_addr,
+ srb->total_xfer_length);
+ if (srb->dcb->sync_period & WIDE_SYNC)
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
+ CFG2_WIDEFIFO);
+ while (DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) != 0x40) {
+ u8 byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ *(srb->virt_addr)++ = byte;
+ if (debug_enabled(DBG_PIO))
+ printk(" %02x", byte);
+ d_left_counter--;
+ sg_subtract_one(srb);
+ }
+ if (srb->dcb->sync_period & WIDE_SYNC) {
+#if 1
+ /* Read the last byte ... */
+ if (srb->total_xfer_length > 0) {
+ u8 byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ *(srb->virt_addr)++ = byte;
+ srb->total_xfer_length--;
+ if (debug_enabled(DBG_PIO))
+ printk(" %02x", byte);
+ }
+#endif
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
+ }
+ /*printk(" %08x", *(u32*)(bus_to_virt (addr))); */
+ /*srb->total_xfer_length = 0; */
+ if (debug_enabled(DBG_PIO))
+ printk("\n");
+ }
+#endif /* DC395x_LASTPIO */
+
+#if 0
+ /*
+ * KG: This was in DATAOUT. Does it also belong here?
+ * Nobody seems to know what counter and fifo_cnt count exactly ...
+ */
+ if (!(scsi_status & SCSIXFERDONE)) {
+ /*
+ * when data transfer from DMA FIFO to SCSI FIFO
+ * if there was some data left in SCSI FIFO
+ */
+ d_left_counter =
+ (u32)(DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) &
+ 0x1F);
+ if (srb->dcb->sync_period & WIDE_SYNC)
+ d_left_counter <<= 1;
+ /*
+ * if WIDE scsi SCSI FIFOCNT unit is word !!!
+ * so need to *= 2
+ * KG: Seems to be correct ...
+ */
+ }
+#endif
+ /* KG: This should not be needed any more! */
+ if (d_left_counter == 0
+ || (scsi_status & SCSIXFERCNT_2_ZERO)) {
+#if 0
+ int ctr = 6000000;
+ u8 TempDMAstatus;
+ do {
+ TempDMAstatus =
+ DC395x_read8(acb, TRM_S1040_DMA_STATUS);
+ } while (!(TempDMAstatus & DMAXFERCOMP) && --ctr);
+ if (!ctr)
+ dprintkl(KERN_ERR,
+ "Deadlock in DataInPhase0 waiting for DMA!!\n");
+ srb->total_xfer_length = 0;
+#endif
+ srb->total_xfer_length = d_left_counter;
+ } else { /* phase changed */
+ /*
+ * parsing the case:
+ * when a transfer not yet complete
+ * but be disconnected by target
+ * if transfer not yet complete
+ * there were some data residue in SCSI FIFO or
+ * SCSI transfer counter not empty
+ */
+ sg_update_list(srb, d_left_counter);
+ }
+ }
+ /* KG: The target may decide to disconnect: Empty FIFO before! */
+ if ((*pscsi_status & PHASEMASK) != PH_DATA_IN) {
+ cleanup_after_transfer(acb, srb);
+ }
+}
+
+
+static void data_in_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "data_in_phase1: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
+ data_io_transfer(acb, srb, XFERDATAIN);
+}
+
+
+static void data_io_transfer(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb, u16 io_dir)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+ u8 bval;
+ dprintkdbg(DBG_0,
+ "data_io_transfer: (pid#%li) <%02i-%i> %c len=%i, sg=(%i/%i)\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun,
+ ((io_dir & DMACMD_DIR) ? 'r' : 'w'),
+ srb->total_xfer_length, srb->sg_index, srb->sg_count);
+ if (srb == acb->tmp_srb)
+ dprintkl(KERN_ERR, "data_io_transfer: Using tmp_srb!\n");
+ if (srb->sg_index >= srb->sg_count) {
+ /* can't happen? out of bounds error */
+ return;
+ }
+
+ if (srb->total_xfer_length > DC395x_LASTPIO) {
+ u8 dma_status = DC395x_read8(acb, TRM_S1040_DMA_STATUS);
+ /*
+ * KG: What should we do: Use SCSI Cmd 0x90/0x92?
+ * Maybe, even ABORTXFER would be appropriate
+ */
+ if (dma_status & XFERPENDING) {
+ dprintkl(KERN_DEBUG, "data_io_transfer: Xfer pending! "
+ "Expect trouble!\n");
+ dump_register_info(acb, dcb, srb);
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO);
+ }
+ /* clear_fifo(acb, "IO"); */
+ /*
+ * load what physical address of Scatter/Gather list table
+ * want to be transfer
+ */
+ srb->state |= SRB_DATA_XFER;
+ DC395x_write32(acb, TRM_S1040_DMA_XHIGHADDR, 0);
+ if (srb->cmd->use_sg) { /* with S/G */
+ io_dir |= DMACMD_SG;
+ DC395x_write32(acb, TRM_S1040_DMA_XLOWADDR,
+ srb->sg_bus_addr +
+ sizeof(struct SGentry) *
+ srb->sg_index);
+ /* load how many bytes in the sg list table */
+ DC395x_write32(acb, TRM_S1040_DMA_XCNT,
+ ((u32)(srb->sg_count -
+ srb->sg_index) << 3));
+ } else { /* without S/G */
+ io_dir &= ~DMACMD_SG;
+ DC395x_write32(acb, TRM_S1040_DMA_XLOWADDR,
+ srb->segment_x[0].address);
+ DC395x_write32(acb, TRM_S1040_DMA_XCNT,
+ srb->segment_x[0].length);
+ }
+ /* load total transfer length (24bits) max value 16Mbyte */
+ DC395x_write32(acb, TRM_S1040_SCSI_COUNTER,
+ srb->total_xfer_length);
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ if (io_dir & DMACMD_DIR) { /* read */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND,
+ SCMD_DMA_IN);
+ DC395x_write16(acb, TRM_S1040_DMA_COMMAND, io_dir);
+ } else {
+ DC395x_write16(acb, TRM_S1040_DMA_COMMAND, io_dir);
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND,
+ SCMD_DMA_OUT);
+ }
+
+ }
+#if DC395x_LASTPIO
+ else if (srb->total_xfer_length > 0) { /* The last four bytes: Do PIO */
+ /*
+ * load what physical address of Scatter/Gather list table
+ * want to be transfer
+ */
+ srb->state |= SRB_DATA_XFER;
+ /* load total transfer length (24bits) max value 16Mbyte */
+ DC395x_write32(acb, TRM_S1040_SCSI_COUNTER,
+ srb->total_xfer_length);
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ if (io_dir & DMACMD_DIR) { /* read */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND,
+ SCMD_FIFO_IN);
+ } else { /* write */
+ int ln = srb->total_xfer_length;
+ if (srb->dcb->sync_period & WIDE_SYNC)
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
+ CFG2_WIDEFIFO);
+ dprintkdbg(DBG_PIO,
+ "data_io_transfer: PIO %i bytes from %p:",
+ srb->total_xfer_length, srb->virt_addr);
+
+ while (srb->total_xfer_length) {
+ if (debug_enabled(DBG_PIO))
+ printk(" %02x", (unsigned char) *(srb->virt_addr));
+
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO,
+ *(srb->virt_addr)++);
+
+ sg_subtract_one(srb);
+ }
+ if (srb->dcb->sync_period & WIDE_SYNC) {
+ if (ln % 2) {
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 0);
+ if (debug_enabled(DBG_PIO))
+ printk(" |00");
+ }
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
+ }
+ /*DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, ln); */
+ if (debug_enabled(DBG_PIO))
+ printk("\n");
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND,
+ SCMD_FIFO_OUT);
+ }
+ }
+#endif /* DC395x_LASTPIO */
+ else { /* xfer pad */
+ u8 data = 0, data2 = 0;
+ if (srb->sg_count) {
+ srb->adapter_status = H_OVER_UNDER_RUN;
+ srb->status |= OVER_RUN;
+ }
+ /*
+ * KG: despite the fact that we are using 16 bits I/O ops
+ * the SCSI FIFO is only 8 bits according to the docs
+ * (we can set bit 1 in 0x8f to serialize FIFO access ...)
+ */
+ if (dcb->sync_period & WIDE_SYNC) {
+ DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, 2);
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
+ CFG2_WIDEFIFO);
+ if (io_dir & DMACMD_DIR) {
+ data = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ data2 = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ } else {
+ /* Danger, Robinson: If you find KGs
+ * scattered over the wide disk, the driver
+ * or chip is to blame :-( */
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 'K');
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 'G');
+ }
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
+ } else {
+ DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, 1);
+ /* Danger, Robinson: If you find a collection of Ks on your disk
+ * something broke :-( */
+ if (io_dir & DMACMD_DIR)
+ data = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ else
+ DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 'K');
+ }
+ srb->state |= SRB_XFERPAD;
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ /* SCSI command */
+ bval = (io_dir & DMACMD_DIR) ? SCMD_FIFO_IN : SCMD_FIFO_OUT;
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, bval);
+ }
+}
+
+
+static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "status_phase0: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
+ srb->target_status = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ srb->end_message = DC395x_read8(acb, TRM_S1040_SCSI_FIFO); /* get message */
+ srb->state = SRB_COMPLETED;
+ *pscsi_status = PH_BUS_FREE; /*.. initial phase */
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT);
+}
+
+
+static void status_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "status_phase1: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
+ srb->state = SRB_STATUS;
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_COMP);
+}
+
+
+/* Check if the message is complete */
+static inline u8 msgin_completed(u8 * msgbuf, u32 len)
+{
+ if (*msgbuf == EXTENDED_MESSAGE) {
+ if (len < 2)
+ return 0;
+ if (len < msgbuf[1] + 2)
+ return 0;
+ } else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) /* two byte messages */
+ if (len < 2)
+ return 0;
+ return 1;
+}
+
+/* reject_msg */
+static inline void msgin_reject(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb)
+{
+ srb->msgout_buf[0] = MESSAGE_REJECT;
+ srb->msg_count = 1;
+ DC395x_ENABLE_MSGOUT;
+ srb->state &= ~SRB_MSGIN;
+ srb->state |= SRB_MSGOUT;
+ dprintkl(KERN_INFO, "msgin_reject: 0x%02x <%02i-%i>\n",
+ srb->msgin_buf[0],
+ srb->dcb->target_id, srb->dcb->target_lun);
+}
+
+
+static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb, u8 tag)
+{
+ struct ScsiReqBlk *srb = NULL;
+ struct ScsiReqBlk *i;
+ dprintkdbg(DBG_0, "msgin_qtag: (pid#%li) tag=%i srb=%p\n",
+ srb->cmd->pid, tag, srb);
+
+ if (!(dcb->tag_mask & (1 << tag)))
+ dprintkl(KERN_DEBUG,
+ "msgin_qtag: tag_mask=0x%08x does not reserve tag %i!\n",
+ dcb->tag_mask, tag);
+
+ if (list_empty(&dcb->srb_going_list))
+ goto mingx0;
+ list_for_each_entry(i, &dcb->srb_going_list, list) {
+ if (i->tag_number == tag) {
+ srb = i;
+ break;
+ }
+ }
+ if (!srb)
+ goto mingx0;
+
+ dprintkdbg(DBG_0, "msgin_qtag: (pid#%li) <%02i-%i>\n",
+ srb->cmd->pid, srb->dcb->target_id, srb->dcb->target_lun);
+ if (dcb->flag & ABORT_DEV_) {
+ /*srb->state = SRB_ABORT_SENT; */
+ enable_msgout_abort(acb, srb);
+ }
+
+ if (!(srb->state & SRB_DISCONNECT))
+ goto mingx0;
+
+ memcpy(srb->msgin_buf, dcb->active_srb->msgin_buf, acb->msg_len);
+ srb->state |= dcb->active_srb->state;
+ srb->state |= SRB_DATA_XFER;
+ dcb->active_srb = srb;
+ /* How can we make the DORS happy? */
+ return srb;
+
+ mingx0:
+ srb = acb->tmp_srb;
+ srb->state = SRB_UNEXPECT_RESEL;
+ dcb->active_srb = srb;
+ srb->msgout_buf[0] = MSG_ABORT_TAG;
+ srb->msg_count = 1;
+ DC395x_ENABLE_MSGOUT;
+ dprintkl(KERN_DEBUG, "msgin_qtag: Unknown tag %i - abort\n", tag);
+ return srb;
+}
+
+
+static inline void reprogram_regs(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb)
+{
+ DC395x_write8(acb, TRM_S1040_SCSI_TARGETID, dcb->target_id);
+ DC395x_write8(acb, TRM_S1040_SCSI_SYNC, dcb->sync_period);
+ DC395x_write8(acb, TRM_S1040_SCSI_OFFSET, dcb->sync_offset);
+ set_xfer_rate(acb, dcb);
+}
+
+
+/* set async transfer mode */
+static void msgin_set_async(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+ dprintkl(KERN_DEBUG, "msgin_set_async: No sync transfers <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun);
+
+ dcb->sync_mode &= ~(SYNC_NEGO_ENABLE);
+ dcb->sync_mode |= SYNC_NEGO_DONE;
+ /*dcb->sync_period &= 0; */
+ dcb->sync_offset = 0;
+ dcb->min_nego_period = 200 >> 2; /* 200ns <=> 5 MHz */
+ srb->state &= ~SRB_DO_SYNC_NEGO;
+ reprogram_regs(acb, dcb);
+ if ((dcb->sync_mode & WIDE_NEGO_ENABLE)
+ && !(dcb->sync_mode & WIDE_NEGO_DONE)) {
+ build_wdtr(acb, dcb, srb);
+ DC395x_ENABLE_MSGOUT;
+ dprintkdbg(DBG_0, "msgin_set_async(rej): Try WDTR anyway\n");
+ }
+}
+
+
+/* set sync transfer mode */
+static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+ u8 bval;
+ int fact;
+ dprintkdbg(DBG_1, "msgin_set_sync: <%02i> Sync: %ins "
+ "(%02i.%01i MHz) Offset %i\n",
+ dcb->target_id, srb->msgin_buf[3] << 2,
+ (250 / srb->msgin_buf[3]),
+ ((250 % srb->msgin_buf[3]) * 10) / srb->msgin_buf[3],
+ srb->msgin_buf[4]);
+
+ if (srb->msgin_buf[4] > 15)
+ srb->msgin_buf[4] = 15;
+ if (!(dcb->dev_mode & NTC_DO_SYNC_NEGO))
+ dcb->sync_offset = 0;
+ else if (dcb->sync_offset == 0)
+ dcb->sync_offset = srb->msgin_buf[4];
+ if (srb->msgin_buf[4] > dcb->sync_offset)
+ srb->msgin_buf[4] = dcb->sync_offset;
+ else
+ dcb->sync_offset = srb->msgin_buf[4];
+ bval = 0;
+ while (bval < 7 && (srb->msgin_buf[3] > clock_period[bval]
+ || dcb->min_nego_period >
+ clock_period[bval]))
+ bval++;
+ if (srb->msgin_buf[3] < clock_period[bval])
+ dprintkl(KERN_INFO,
+ "msgin_set_sync: Increase sync nego period to %ins\n",
+ clock_period[bval] << 2);
+ srb->msgin_buf[3] = clock_period[bval];
+ dcb->sync_period &= 0xf0;
+ dcb->sync_period |= ALT_SYNC | bval;
+ dcb->min_nego_period = srb->msgin_buf[3];
+
+ if (dcb->sync_period & WIDE_SYNC)
+ fact = 500;
+ else
+ fact = 250;
+
+ dprintkl(KERN_INFO,
+ "Target %02i: %s Sync: %ins Offset %i (%02i.%01i MB/s)\n",
+ dcb->target_id, (fact == 500) ? "Wide16" : "",
+ dcb->min_nego_period << 2, dcb->sync_offset,
+ (fact / dcb->min_nego_period),
+ ((fact % dcb->min_nego_period) * 10 +
+ dcb->min_nego_period / 2) / dcb->min_nego_period);
+
+ if (!(srb->state & SRB_DO_SYNC_NEGO)) {
+ /* Reply with corrected SDTR Message */
+ dprintkl(KERN_DEBUG, "msgin_set_sync: answer w/%ins %i\n",
+ srb->msgin_buf[3] << 2, srb->msgin_buf[4]);
+
+ memcpy(srb->msgout_buf, srb->msgin_buf, 5);
+ srb->msg_count = 5;
+ DC395x_ENABLE_MSGOUT;
+ dcb->sync_mode |= SYNC_NEGO_DONE;
+ } else {
+ if ((dcb->sync_mode & WIDE_NEGO_ENABLE)
+ && !(dcb->sync_mode & WIDE_NEGO_DONE)) {
+ build_wdtr(acb, dcb, srb);
+ DC395x_ENABLE_MSGOUT;
+ dprintkdbg(DBG_0, "msgin_set_sync: Also try WDTR\n");
+ }
+ }
+ srb->state &= ~SRB_DO_SYNC_NEGO;
+ dcb->sync_mode |= SYNC_NEGO_DONE | SYNC_NEGO_ENABLE;
+
+ reprogram_regs(acb, dcb);
+}
+
+
+static inline void msgin_set_nowide(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+ dprintkdbg(DBG_1, "msgin_set_nowide: <%02i>\n", dcb->target_id);
+
+ dcb->sync_period &= ~WIDE_SYNC;
+ dcb->sync_mode &= ~(WIDE_NEGO_ENABLE);
+ dcb->sync_mode |= WIDE_NEGO_DONE;
+ srb->state &= ~SRB_DO_WIDE_NEGO;
+ reprogram_regs(acb, dcb);
+ if ((dcb->sync_mode & SYNC_NEGO_ENABLE)
+ && !(dcb->sync_mode & SYNC_NEGO_DONE)) {
+ build_sdtr(acb, dcb, srb);
+ DC395x_ENABLE_MSGOUT;
+ dprintkdbg(DBG_0, "msgin_set_nowide: Rejected. Try SDTR anyway\n");
+ }
+}
+
+static void msgin_set_wide(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
+{
+ struct DeviceCtlBlk *dcb = srb->dcb;
+ u8 wide = (dcb->dev_mode & NTC_DO_WIDE_NEGO
+ && acb->config & HCC_WIDE_CARD) ? 1 : 0;
+ dprintkdbg(DBG_1, "msgin_set_wide: <%02i>\n", dcb->target_id);
+
+ if (srb->msgin_buf[3] > wide)
+ srb->msgin_buf[3] = wide;
+ /* Completed */
+ if (!(srb->state & SRB_DO_WIDE_NEGO)) {
+ dprintkl(KERN_DEBUG,
+ "msgin_set_wide: Wide nego initiated <%02i>\n",
+ dcb->target_id);
+ memcpy(srb->msgout_buf, srb->msgin_buf, 4);
+ srb->msg_count = 4;
+ srb->state |= SRB_DO_WIDE_NEGO;
+ DC395x_ENABLE_MSGOUT;
+ }
+
+ dcb->sync_mode |= (WIDE_NEGO_ENABLE | WIDE_NEGO_DONE);
+ if (srb->msgin_buf[3] > 0)
+ dcb->sync_period |= WIDE_SYNC;
+ else
+ dcb->sync_period &= ~WIDE_SYNC;
+ srb->state &= ~SRB_DO_WIDE_NEGO;
+ /*dcb->sync_mode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); */
+ dprintkdbg(DBG_1,
+ "msgin_set_wide: Wide (%i bit) negotiated <%02i>\n",
+ (8 << srb->msgin_buf[3]), dcb->target_id);
+ reprogram_regs(acb, dcb);
+ if ((dcb->sync_mode & SYNC_NEGO_ENABLE)
+ && !(dcb->sync_mode & SYNC_NEGO_DONE)) {
+ build_sdtr(acb, dcb, srb);
+ DC395x_ENABLE_MSGOUT;
+ dprintkdbg(DBG_0, "msgin_set_wide: Also try SDTR.\n");
+ }
+}
+
+
+/*
+ * extended message codes:
+ *
+ * code description
+ *
+ * 02h Reserved
+ * 00h MODIFY DATA POINTER
+ * 01h SYNCHRONOUS DATA TRANSFER REQUEST
+ * 03h WIDE DATA TRANSFER REQUEST
+ * 04h - 7Fh Reserved
+ * 80h - FFh Vendor specific
+ */
+static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ struct DeviceCtlBlk *dcb = acb->active_dcb;
+ dprintkdbg(DBG_0, "msgin_phase0: (pid#%li)\n", srb->cmd->pid);
+
+ srb->msgin_buf[acb->msg_len++] = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
+ if (msgin_completed(srb->msgin_buf, acb->msg_len)) {
+ /* Now eval the msg */
+ switch (srb->msgin_buf[0]) {
+ case DISCONNECT:
+ srb->state = SRB_DISCONNECT;
+ break;
+
+ case SIMPLE_QUEUE_TAG:
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ srb =
+ msgin_qtag(acb, dcb,
+ srb->msgin_buf[1]);
+ break;
+
+ case MESSAGE_REJECT:
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL,
+ DO_CLRATN | DO_DATALATCH);
+ /* A sync nego message was rejected ! */
+ if (srb->state & SRB_DO_SYNC_NEGO) {
+ msgin_set_async(acb, srb);
+ break;
+ }
+ /* A wide nego message was rejected ! */
+ if (srb->state & SRB_DO_WIDE_NEGO) {
+ msgin_set_nowide(acb, srb);
+ break;
+ }
+ enable_msgout_abort(acb, srb);
+ /*srb->state |= SRB_ABORT_SENT */
+ break;
+
+ case EXTENDED_MESSAGE:
+ /* SDTR */
+ if (srb->msgin_buf[1] == 3
+ && srb->msgin_buf[2] == EXTENDED_SDTR) {
+ msgin_set_sync(acb, srb);
+ break;
+ }
+ /* WDTR */
+ if (srb->msgin_buf[1] == 2
+ && srb->msgin_buf[2] == EXTENDED_WDTR
+ && srb->msgin_buf[3] <= 2) { /* sanity check ... */
+ msgin_set_wide(acb, srb);
+ break;
+ }
+ msgin_reject(acb, srb);
+ break;
+
+ case MSG_IGNOREWIDE:
+ /* Discard wide residual */
+ dprintkdbg(DBG_0, "msgin_phase0: Ignore Wide Residual!\n");
+ break;
+
+ case COMMAND_COMPLETE:
+ /* nothing has to be done */
+ break;
+
+ case SAVE_POINTERS:
+ /*
+ * SAVE POINTER may be ignored as we have the struct
+ * ScsiReqBlk* associated with the scsi command.
+ */
+ dprintkdbg(DBG_0, "msgin_phase0: (pid#%li) "
+ "SAVE POINTER rem=%i Ignore\n",
+ srb->cmd->pid, srb->total_xfer_length);
+ break;
+
+ case RESTORE_POINTERS:
+ dprintkdbg(DBG_0, "msgin_phase0: RESTORE POINTER. Ignore\n");
+ break;
+
+ case ABORT:
+ dprintkdbg(DBG_0, "msgin_phase0: (pid#%li) "
+ "<%02i-%i> ABORT msg\n",
+ srb->cmd->pid, dcb->target_id,
+ dcb->target_lun);
+ dcb->flag |= ABORT_DEV_;
+ enable_msgout_abort(acb, srb);
+ break;
+
+ default:
+ /* reject unknown messages */
+ if (srb->msgin_buf[0] & IDENTIFY_BASE) {
+ dprintkdbg(DBG_0, "msgin_phase0: Identify msg\n");
+ srb->msg_count = 1;
+ srb->msgout_buf[0] = dcb->identify_msg;
+ DC395x_ENABLE_MSGOUT;
+ srb->state |= SRB_MSGOUT;
+ /*break; */
+ }
+ msgin_reject(acb, srb);
+ }
+
+ /* Clear counter and MsgIn state */
+ srb->state &= ~SRB_MSGIN;
+ acb->msg_len = 0;
+ }
+ *pscsi_status = PH_BUS_FREE;
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important ... you know! */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT);
+}
+
+
+static void msgin_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+ dprintkdbg(DBG_0, "msgin_phase1: (pid#%li)\n", srb->cmd->pid);
+ clear_fifo(acb, "msgin_phase1");
+ DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, 1);
+ if (!(srb->state & SRB_MSGIN)) {
+ srb->state &= ~SRB_DISCONNECT;
+ srb->state |= SRB_MSGIN;
+ }
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ /* SCSI command */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN);
+}
+
+
+static void nop0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+}
+
+
+static void nop1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
+ u16 *pscsi_status)
+{
+}
+
+
+static void set_xfer_rate(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb)
+{
+ struct DeviceCtlBlk *i;
+
+ /* set all lun device's period, offset */
+ if (dcb->identify_msg & 0x07)
+ return;
+
+ if (acb->scan_devices) {
+ current_sync_offset = dcb->sync_offset;
+ return;
+ }
+
+ list_for_each_entry(i, &acb->dcb_list, list)
+ if (i->target_id == dcb->target_id) {
+ i->sync_period = dcb->sync_period;
+ i->sync_offset = dcb->sync_offset;
+ i->sync_mode = dcb->sync_mode;
+ i->min_nego_period = dcb->min_nego_period;
+ }
+}
+
+
+static void disconnect(struct AdapterCtlBlk *acb)
+{
+ struct DeviceCtlBlk *dcb = acb->active_dcb;
+ struct ScsiReqBlk *srb;
+
+ if (!dcb) {
+ dprintkl(KERN_ERR, "disconnect: No such device\n");
+ udelay(500);
+ /* Suspend queue for a while */
+ acb->scsi_host->last_reset =
+ jiffies + HZ / 2 +
+ HZ * acb->eeprom.delay_time;
+ clear_fifo(acb, "disconnectEx");
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT);
+ return;
+ }
+ srb = dcb->active_srb;
+ acb->active_dcb = NULL;
+ dprintkdbg(DBG_0, "disconnect: (pid#%li)\n", srb->cmd->pid);
+
+ srb->scsi_phase = PH_BUS_FREE; /* initial phase */
+ clear_fifo(acb, "disconnect");
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT);
+ if (srb->state & SRB_UNEXPECT_RESEL) {
+ dprintkl(KERN_ERR,
+ "disconnect: Unexpected reselection <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun);
+ srb->state = 0;
+ waiting_process_next(acb);
+ } else if (srb->state & SRB_ABORT_SENT) {
+ dcb->flag &= ~ABORT_DEV_;
+ acb->scsi_host->last_reset = jiffies + HZ / 2 + 1;
+ dprintkl(KERN_ERR, "disconnect: SRB_ABORT_SENT\n");
+ doing_srb_done(acb, DID_ABORT, srb->cmd, 1);
+ waiting_process_next(acb);
+ } else {
+ if ((srb->state & (SRB_START_ + SRB_MSGOUT))
+ || !(srb->
+ state & (SRB_DISCONNECT + SRB_COMPLETED))) {
+ /*
+ * Selection time out
+ * SRB_START_ || SRB_MSGOUT || (!SRB_DISCONNECT && !SRB_COMPLETED)
+ */
+ /* Unexp. Disc / Sel Timeout */
+ if (srb->state != SRB_START_
+ && srb->state != SRB_MSGOUT) {
+ srb->state = SRB_READY;
+ dprintkl(KERN_DEBUG,
+ "disconnect: (pid#%li) Unexpected\n",
+ srb->cmd->pid);
+ srb->target_status = SCSI_STAT_SEL_TIMEOUT;
+ goto disc1;
+ } else {
+ /* Normal selection timeout */
+ dprintkdbg(DBG_KG, "disconnect: (pid#%li) "
+ "<%02i-%i> SelTO\n", srb->cmd->pid,
+ dcb->target_id, dcb->target_lun);
+ if (srb->retry_count++ > DC395x_MAX_RETRIES
+ || acb->scan_devices) {
+ srb->target_status =
+ SCSI_STAT_SEL_TIMEOUT;
+ goto disc1;
+ }
+ free_tag(dcb, srb);
+ srb_going_to_waiting_move(dcb, srb);
+ dprintkdbg(DBG_KG,
+ "disconnect: (pid#%li) Retry\n",
+ srb->cmd->pid);
+ waiting_set_timer(acb, HZ / 20);
+ }
+ } else if (srb->state & SRB_DISCONNECT) {
+ u8 bval = DC395x_read8(acb, TRM_S1040_SCSI_SIGNAL);
+ /*
+ * SRB_DISCONNECT (This is what we expect!)
+ */
+ if (bval & 0x40) {
+ dprintkdbg(DBG_0, "disconnect: SCSI bus stat "
+ " 0x%02x: ACK set! Other controllers?\n",
+ bval);
+ /* It could come from another initiator, therefore don't do much ! */
+ } else
+ waiting_process_next(acb);
+ } else if (srb->state & SRB_COMPLETED) {
+ disc1:
+ /*
+ ** SRB_COMPLETED
+ */
+ free_tag(dcb, srb);
+ dcb->active_srb = NULL;
+ srb->state = SRB_FREE;
+ srb_done(acb, dcb, srb);
+ }
+ }
+}
+
+
+static void reselect(struct AdapterCtlBlk *acb)
+{
+ struct DeviceCtlBlk *dcb = acb->active_dcb;
+ struct ScsiReqBlk *srb = NULL;
+ u16 rsel_tar_lun_id;
+ u8 id, lun;
+ u8 arblostflag = 0;
+ dprintkdbg(DBG_0, "reselect: acb=%p\n", acb);
+
+ clear_fifo(acb, "reselect");
+ /*DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); */
+ /* Read Reselected Target ID and LUN */
+ rsel_tar_lun_id = DC395x_read16(acb, TRM_S1040_SCSI_TARGETID);
+ if (dcb) { /* Arbitration lost but Reselection win */
+ srb = dcb->active_srb;
+ if (!srb) {
+ dprintkl(KERN_DEBUG, "reselect: Arb lost Resel won, "
+ "but active_srb == NULL\n");
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ return;
+ }
+ /* Why the if ? */
+ if (!acb->scan_devices) {
+ dprintkdbg(DBG_KG, "reselect: (pid#%li) <%02i-%i> "
+ "Arb lost but Resel win rsel=%i stat=0x%04x\n",
+ srb->cmd->pid, dcb->target_id,
+ dcb->target_lun, rsel_tar_lun_id,
+ DC395x_read16(acb, TRM_S1040_SCSI_STATUS));
+ arblostflag = 1;
+ /*srb->state |= SRB_DISCONNECT; */
+
+ srb->state = SRB_READY;
+ free_tag(dcb, srb);
+ srb_going_to_waiting_move(dcb, srb);
+ waiting_set_timer(acb, HZ / 20);
+
+ /* return; */
+ }
+ }
+ /* Read Reselected Target Id and LUN */
+ if (!(rsel_tar_lun_id & (IDENTIFY_BASE << 8)))
+ dprintkl(KERN_DEBUG, "reselect: Expects identify msg. "
+ "Got %i!\n", rsel_tar_lun_id);
+ id = rsel_tar_lun_id & 0xff;
+ lun = (rsel_tar_lun_id >> 8) & 7;
+ dcb = find_dcb(acb, id, lun);
+ if (!dcb) {
+ dprintkl(KERN_ERR, "reselect: From non existent device "
+ "<%02i-%i>\n", id, lun);
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ return;
+ }
+ acb->active_dcb = dcb;
+
+ if (!(dcb->dev_mode & NTC_DO_DISCONNECT))
+ dprintkl(KERN_DEBUG, "reselect: in spite of forbidden "
+ "disconnection? <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun);
+
+ if (dcb->sync_mode & EN_TAG_QUEUEING /*&& !arblostflag */) {
+ srb = acb->tmp_srb;
+ dcb->active_srb = srb;
+ } else {
+ /* There can be only one! */
+ srb = dcb->active_srb;
+ if (!srb || !(srb->state & SRB_DISCONNECT)) {
+ /*
+ * abort command
+ */
+ dprintkl(KERN_DEBUG,
+ "reselect: w/o disconnected cmds <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun);
+ srb = acb->tmp_srb;
+ srb->state = SRB_UNEXPECT_RESEL;
+ dcb->active_srb = srb;
+ enable_msgout_abort(acb, srb);
+ } else {
+ if (dcb->flag & ABORT_DEV_) {
+ /*srb->state = SRB_ABORT_SENT; */
+ enable_msgout_abort(acb, srb);
+ } else
+ srb->state = SRB_DATA_XFER;
+
+ }
+ }
+ srb->scsi_phase = PH_BUS_FREE; /* initial phase */
+
+ /* Program HA ID, target ID, period and offset */
+ dprintkdbg(DBG_0, "reselect: select <%i>\n", dcb->target_id);
+ DC395x_write8(acb, TRM_S1040_SCSI_HOSTID, acb->scsi_host->this_id); /* host ID */
+ DC395x_write8(acb, TRM_S1040_SCSI_TARGETID, dcb->target_id); /* target ID */
+ DC395x_write8(acb, TRM_S1040_SCSI_OFFSET, dcb->sync_offset); /* offset */
+ DC395x_write8(acb, TRM_S1040_SCSI_SYNC, dcb->sync_period); /* sync period, wide */
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
+ /* SCSI command */
+ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT);
+}
+
+
+static inline u8 tagq_blacklist(char *name)
+{
+#ifndef DC395x_NO_TAGQ
+#if 0
+ u8 i;
+ for (i = 0; i < BADDEVCNT; i++)
+ if (memcmp(name, DC395x_baddevname1[i], 28) == 0)
+ return 1;
+#endif
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+
+static void disc_tagq_set(struct DeviceCtlBlk *dcb, struct ScsiInqData *ptr)
+{
+ /* Check for SCSI format (ANSI and Response data format) */
+ if ((ptr->Vers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2) {
+ if ((ptr->Flags & SCSI_INQ_CMDQUEUE)
+ && (dcb->dev_mode & NTC_DO_TAG_QUEUEING) &&
+ /*(dcb->dev_mode & NTC_DO_DISCONNECT) */
+ /* ((dcb->dev_type == TYPE_DISK)
+ || (dcb->dev_type == TYPE_MOD)) && */
+ !tagq_blacklist(((char *)ptr) + 8)) {
+ if (dcb->max_command == 1)
+ dcb->max_command =
+ dcb->acb->tag_max_num;
+ dcb->sync_mode |= EN_TAG_QUEUEING;
+ /*dcb->tag_mask = 0; */
+ } else
+ dcb->max_command = 1;
+ }
+}
+
+
+static void add_dev(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiInqData *ptr)
+{
+ u8 bval1 = ptr->DevType & SCSI_DEVTYPE;
+ dcb->dev_type = bval1;
+ /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */
+ disc_tagq_set(dcb, ptr);
+}
+
+
+/* unmap mapped pci regions from SRB */
+static void pci_unmap_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
+{
+ struct scsi_cmnd *cmd = srb->cmd;
+ enum dma_data_direction dir = cmd->sc_data_direction;
+ if (cmd->use_sg && dir != PCI_DMA_NONE) {
+ int i;
+ /* unmap DC395x SG list */
+ dprintkdbg(DBG_SG, "pci_unmap_srb: list=%08x(%05x)\n",
+ srb->sg_bus_addr, SEGMENTX_LEN);
+ pci_unmap_single(acb->dev, srb->sg_bus_addr,
+ SEGMENTX_LEN,
+ PCI_DMA_TODEVICE);
+ dprintkdbg(DBG_SG, "pci_unmap_srb: segs=%i buffer=%p\n",
+ cmd->use_sg, cmd->request_buffer);
+ /* unmap the sg segments */
+ for (i = 0; i < srb->sg_count; i++)
+ kunmap(virt_to_page(srb->virt_map[i]));
+ pci_unmap_sg(acb->dev,
+ (struct scatterlist *)cmd->request_buffer,
+ cmd->use_sg, dir);
+ } else if (cmd->request_buffer && dir != PCI_DMA_NONE) {
+ dprintkdbg(DBG_SG, "pci_unmap_srb: buffer=%08x(%05x)\n",
+ srb->segment_x[0].address, cmd->request_bufflen);
+ pci_unmap_single(acb->dev, srb->segment_x[0].address,
+ cmd->request_bufflen, dir);
+ }
+}
+
+
+/* unmap mapped pci sense buffer from SRB */
+static void pci_unmap_srb_sense(struct AdapterCtlBlk *acb,
+ struct ScsiReqBlk *srb)
+{
+ if (!(srb->flag & AUTO_REQSENSE))
+ return;
+ /* Unmap sense buffer */
+ dprintkdbg(DBG_SG, "pci_unmap_srb_sense: buffer=%08x\n",
+ srb->segment_x[0].address);
+ pci_unmap_single(acb->dev, srb->segment_x[0].address,
+ srb->segment_x[0].length, PCI_DMA_FROMDEVICE);
+ /* Restore SG stuff */
+ srb->total_xfer_length = srb->xferred;
+ srb->segment_x[0].address =
+ srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1].address;
+ srb->segment_x[0].length =
+ srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1].length;
+}
+
+
+/*
+ * Complete execution of a SCSI command
+ * Signal completion to the generic SCSI driver
+ */
+static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ u8 tempcnt, status;
+ struct scsi_cmnd *cmd = srb->cmd;
+ struct ScsiInqData *ptr;
+ enum dma_data_direction dir = cmd->sc_data_direction;
+
+ if (cmd->use_sg) {
+ struct scatterlist* sg = (struct scatterlist *)cmd->request_buffer;
+ ptr = (struct ScsiInqData *)(srb->virt_map[0] + sg->offset);
+ } else {
+ ptr = (struct ScsiInqData *)(cmd->request_buffer);
+ }
+
+ dprintkdbg(DBG_1, "srb_done: (pid#%li) <%02i-%i>\n", srb->cmd->pid,
+ srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_SG, "srb_done: srb=%p sg=%i(%i/%i) buf=%p addr=%p\n",
+ srb, cmd->use_sg, srb->sg_index, srb->sg_count,
+ cmd->request_buffer, ptr);
+ status = srb->target_status;
+ if (srb->flag & AUTO_REQSENSE) {
+ dprintkdbg(DBG_0, "srb_done: AUTO_REQSENSE1\n");
+ pci_unmap_srb_sense(acb, srb);
+ /*
+ ** target status..........................
+ */
+ srb->flag &= ~AUTO_REQSENSE;
+ srb->adapter_status = 0;
+ srb->target_status = CHECK_CONDITION << 1;
+ if (debug_enabled(DBG_1)) {
+ switch (cmd->sense_buffer[2] & 0x0f) {
+ case NOT_READY:
+ dprintkl(KERN_DEBUG,
+ "ReqSense: NOT_READY cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
+ cmd->cmnd[0], dcb->target_id,
+ dcb->target_lun, status, acb->scan_devices);
+ break;
+ case UNIT_ATTENTION:
+ dprintkl(KERN_DEBUG,
+ "ReqSense: UNIT_ATTENTION cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
+ cmd->cmnd[0], dcb->target_id,
+ dcb->target_lun, status, acb->scan_devices);
+ break;
+ case ILLEGAL_REQUEST:
+ dprintkl(KERN_DEBUG,
+ "ReqSense: ILLEGAL_REQUEST cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
+ cmd->cmnd[0], dcb->target_id,
+ dcb->target_lun, status, acb->scan_devices);
+ break;
+ case MEDIUM_ERROR:
+ dprintkl(KERN_DEBUG,
+ "ReqSense: MEDIUM_ERROR cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
+ cmd->cmnd[0], dcb->target_id,
+ dcb->target_lun, status, acb->scan_devices);
+ break;
+ case HARDWARE_ERROR:
+ dprintkl(KERN_DEBUG,
+ "ReqSense: HARDWARE_ERROR cmnd=0x%02x <%02i-%i> stat=%i scan=%i ",
+ cmd->cmnd[0], dcb->target_id,
+ dcb->target_lun, status, acb->scan_devices);
+ break;
+ }
+ if (cmd->sense_buffer[7] >= 6)
+ printk("sense=0x%02x ASC=0x%02x ASCQ=0x%02x "
+ "(0x%08x 0x%08x)\n",
+ cmd->sense_buffer[2], cmd->sense_buffer[12],
+ cmd->sense_buffer[13],
+ *((unsigned int *)(cmd->sense_buffer + 3)),
+ *((unsigned int *)(cmd->sense_buffer + 8)));
+ else
+ printk("sense=0x%02x No ASC/ASCQ (0x%08x)\n",
+ cmd->sense_buffer[2],
+ *((unsigned int *)(cmd->sense_buffer + 3)));
+ }
+
+ if (status == (CHECK_CONDITION << 1)) {
+ cmd->result = DID_BAD_TARGET << 16;
+ goto ckc_e;
+ }
+ dprintkdbg(DBG_0, "srb_done: AUTO_REQSENSE2\n");
+
+ if (srb->total_xfer_length
+ && srb->total_xfer_length >= cmd->underflow)
+ cmd->result =
+ MK_RES_LNX(DRIVER_SENSE, DID_OK,
+ srb->end_message, CHECK_CONDITION);
+ /*SET_RES_DID(cmd->result,DID_OK) */
+ else
+ cmd->result =
+ MK_RES_LNX(DRIVER_SENSE, DID_OK,
+ srb->end_message, CHECK_CONDITION);
+
+ goto ckc_e;
+ }
+
+/*************************************************************/
+ if (status) {
+ /*
+ * target status..........................
+ */
+ if (status_byte(status) == CHECK_CONDITION) {
+ request_sense(acb, dcb, srb);
+ return;
+ } else if (status_byte(status) == QUEUE_FULL) {
+ tempcnt = (u8)list_size(&dcb->srb_going_list);
+ dprintkl(KERN_INFO, "QUEUE_FULL for dev <%02i-%i> with %i cmnds\n",
+ dcb->target_id, dcb->target_lun, tempcnt);
+ if (tempcnt > 1)
+ tempcnt--;
+ dcb->max_command = tempcnt;
+ free_tag(dcb, srb);
+ srb_going_to_waiting_move(dcb, srb);
+ waiting_set_timer(acb, HZ / 20);
+ srb->adapter_status = 0;
+ srb->target_status = 0;
+ return;
+ } else if (status == SCSI_STAT_SEL_TIMEOUT) {
+ srb->adapter_status = H_SEL_TIMEOUT;
+ srb->target_status = 0;
+ cmd->result = DID_NO_CONNECT << 16;
+ } else {
+ srb->adapter_status = 0;
+ SET_RES_DID(cmd->result, DID_ERROR);
+ SET_RES_MSG(cmd->result, srb->end_message);
+ SET_RES_TARGET(cmd->result, status);
+
+ }
+ } else {
+ /*
+ ** process initiator status..........................
+ */
+ status = srb->adapter_status;
+ if (status & H_OVER_UNDER_RUN) {
+ srb->target_status = 0;
+ SET_RES_DID(cmd->result, DID_OK);
+ SET_RES_MSG(cmd->result, srb->end_message);
+ } else if (srb->status & PARITY_ERROR) {
+ SET_RES_DID(cmd->result, DID_PARITY);
+ SET_RES_MSG(cmd->result, srb->end_message);
+ } else { /* No error */
+
+ srb->adapter_status = 0;
+ srb->target_status = 0;
+ SET_RES_DID(cmd->result, DID_OK);
+ }
+ }
+
+ if (dir != PCI_DMA_NONE) {
+ if (cmd->use_sg)
+ pci_dma_sync_sg_for_cpu(acb->dev,
+ (struct scatterlist *)cmd->
+ request_buffer, cmd->use_sg, dir);
+ else if (cmd->request_buffer)
+ pci_dma_sync_single_for_cpu(acb->dev,
+ srb->segment_x[0].address,
+ cmd->request_bufflen, dir);
+ }
+
+ if ((cmd->result & RES_DID) == 0 && cmd->cmnd[0] == INQUIRY
+ && cmd->cmnd[2] == 0 && cmd->request_bufflen >= 8
+ && dir != PCI_DMA_NONE && ptr && (ptr->Vers & 0x07) >= 2)
+ dcb->inquiry7 = ptr->Flags;
+/* Check Error Conditions */
+ ckc_e:
+
+ /*if( srb->cmd->cmnd[0] == INQUIRY && */
+ /* (host_byte(cmd->result) == DID_OK || status_byte(cmd->result) & CHECK_CONDITION) ) */
+ if (cmd->cmnd[0] == INQUIRY && (cmd->result == (DID_OK << 16)
+ || status_byte(cmd->
+ result) &
+ CHECK_CONDITION)) {
+
+ if (!dcb->init_tcq_flag) {
+ add_dev(acb, dcb, ptr);
+ dcb->init_tcq_flag = 1;
+ }
+
+ }
+
+
+ /* Here is the info for Doug Gilbert's sg3 ... */
+ cmd->resid = srb->total_xfer_length;
+ /* This may be interpreted by sb. or not ... */
+ cmd->SCp.this_residual = srb->total_xfer_length;
+ cmd->SCp.buffers_residual = 0;
+ if (debug_enabled(DBG_KG)) {
+ if (srb->total_xfer_length)
+ dprintkdbg(DBG_KG, "srb_done: (pid#%li) <%02i-%i> "
+ "cmnd=0x%02x Missed %i bytes\n",
+ cmd->pid, cmd->device->id, cmd->device->lun,
+ cmd->cmnd[0], srb->total_xfer_length);
+ }
+
+ srb_going_remove(dcb, srb);
+ /* Add to free list */
+ if (srb == acb->tmp_srb)
+ dprintkl(KERN_ERR, "srb_done: ERROR! Completed cmd with tmp_srb\n");
+ else {
+ dprintkdbg(DBG_0, "srb_done: (pid#%li) done result=0x%08x\n",
+ cmd->pid, cmd->result);
+ srb_free_insert(acb, srb);
+ }
+ pci_unmap_srb(acb, srb);
+
+ cmd->scsi_done(cmd);
+ waiting_process_next(acb);
+}
+
+
+/* abort all cmds in our queues */
+static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag,
+ struct scsi_cmnd *cmd, u8 force)
+{
+ struct DeviceCtlBlk *dcb;
+ dprintkl(KERN_INFO, "doing_srb_done: pids ");
+
+ list_for_each_entry(dcb, &acb->dcb_list, list) {
+ struct ScsiReqBlk *srb;
+ struct ScsiReqBlk *tmp;
+ struct scsi_cmnd *p;
+
+ list_for_each_entry_safe(srb, tmp, &dcb->srb_going_list, list) {
+ enum dma_data_direction dir;
+ int result;
+
+ p = srb->cmd;
+ dir = p->sc_data_direction;
+ result = MK_RES(0, did_flag, 0, 0);
+ printk("G:%li(%02i-%i) ", p->pid,
+ p->device->id, p->device->lun);
+ srb_going_remove(dcb, srb);
+ free_tag(dcb, srb);
+ srb_free_insert(acb, srb);
+ p->result = result;
+ pci_unmap_srb_sense(acb, srb);
+ pci_unmap_srb(acb, srb);
+ if (force) {
+ /* For new EH, we normally don't need to give commands back,
+ * as they all complete or all time out */
+ p->scsi_done(p);
+ }
+ }
+ if (!list_empty(&dcb->srb_going_list))
+ dprintkl(KERN_DEBUG,
+ "How could the ML send cmnds to the Going queue? <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun);
+ if (dcb->tag_mask)
+ dprintkl(KERN_DEBUG,
+ "tag_mask for <%02i-%i> should be empty, is %08x!\n",
+ dcb->target_id, dcb->target_lun,
+ dcb->tag_mask);
+
+ /* Waiting queue */
+ list_for_each_entry_safe(srb, tmp, &dcb->srb_waiting_list, list) {
+ int result;
+ p = srb->cmd;
+
+ result = MK_RES(0, did_flag, 0, 0);
+ printk("W:%li<%02i-%i>", p->pid, p->device->id,
+ p->device->lun);
+ srb_waiting_remove(dcb, srb);
+ srb_free_insert(acb, srb);
+ p->result = result;
+ pci_unmap_srb_sense(acb, srb);
+ pci_unmap_srb(acb, srb);
+ if (force) {
+ /* For new EH, we normally don't need to give commands back,
+ * as they all complete or all time out */
+ cmd->scsi_done(cmd);
+ }
+ }
+ if (!list_empty(&dcb->srb_waiting_list))
+ dprintkl(KERN_DEBUG, "ML queued %i cmnds again to <%02i-%i>\n",
+ list_size(&dcb->srb_waiting_list), dcb->target_id,
+ dcb->target_lun);
+ dcb->flag &= ~ABORT_DEV_;
+ }
+ printk("\n");
+}
+
+
+static void reset_scsi_bus(struct AdapterCtlBlk *acb)
+{
+ dprintkdbg(DBG_0, "reset_scsi_bus: acb=%p\n", acb);
+ acb->acb_flag |= RESET_DEV; /* RESET_DETECT, RESET_DONE, RESET_DEV */
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI);
+
+ while (!(DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET))
+ /* nothing */;
+}
+
+
+static void set_basic_config(struct AdapterCtlBlk *acb)
+{
+ u8 bval;
+ u16 wval;
+ DC395x_write8(acb, TRM_S1040_SCSI_TIMEOUT, acb->sel_timeout);
+ if (acb->config & HCC_PARITY)
+ bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK;
+ else
+ bval = PHASELATCH | INITIATOR | BLOCKRST;
+
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG0, bval);
+
+ /* program configuration 1: Act_Neg (+ Act_Neg_Enh? + Fast_Filter? + DataDis?) */
+ DC395x_write8(acb, TRM_S1040_SCSI_CONFIG1, 0x03); /* was 0x13: default */
+ /* program Host ID */
+ DC395x_write8(acb, TRM_S1040_SCSI_HOSTID, acb->scsi_host->this_id);
+ /* set ansynchronous transfer */
+ DC395x_write8(acb, TRM_S1040_SCSI_OFFSET, 0x00);
+ /* Turn LED control off */
+ wval = DC395x_read16(acb, TRM_S1040_GEN_CONTROL) & 0x7F;
+ DC395x_write16(acb, TRM_S1040_GEN_CONTROL, wval);
+ /* DMA config */
+ wval = DC395x_read16(acb, TRM_S1040_DMA_CONFIG) & ~DMA_FIFO_CTRL;
+ wval |=
+ DMA_FIFO_HALF_HALF | DMA_ENHANCE /*| DMA_MEM_MULTI_READ */ ;
+ DC395x_write16(acb, TRM_S1040_DMA_CONFIG, wval);
+ /* Clear pending interrupt status */
+ DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS);
+ /* Enable SCSI interrupt */
+ DC395x_write8(acb, TRM_S1040_SCSI_INTEN, 0x7F);
+ DC395x_write8(acb, TRM_S1040_DMA_INTEN, EN_SCSIINTR | EN_DMAXFERERROR
+ /*| EN_DMAXFERABORT | EN_DMAXFERCOMP | EN_FORCEDMACOMP */
+ );
+}
+
+
+static void scsi_reset_detect(struct AdapterCtlBlk *acb)
+{
+ dprintkl(KERN_INFO, "scsi_reset_detect: acb=%p\n", acb);
+ /* delay half a second */
+ if (timer_pending(&acb->waiting_timer))
+ del_timer(&acb->waiting_timer);
+
+ DC395x_write8(acb, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE);
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, DMARESETMODULE);
+ /*DC395x_write8(acb, TRM_S1040_DMA_CONTROL,STOPDMAXFER); */
+ udelay(500);
+ /* Maybe we locked up the bus? Then lets wait even longer ... */
+ acb->scsi_host->last_reset =
+ jiffies + 5 * HZ / 2 +
+ HZ * acb->eeprom.delay_time;
+
+ clear_fifo(acb, "scsi_reset_detect");
+ set_basic_config(acb);
+ /*1.25 */
+ /*DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); */
+
+ if (acb->acb_flag & RESET_DEV) { /* RESET_DETECT, RESET_DONE, RESET_DEV */
+ acb->acb_flag |= RESET_DONE;
+ } else {
+ acb->acb_flag |= RESET_DETECT;
+ reset_dev_param(acb);
+ doing_srb_done(acb, DID_RESET, NULL, 1);
+ /*DC395x_RecoverSRB( acb ); */
+ acb->active_dcb = NULL;
+ acb->acb_flag = 0;
+ waiting_process_next(acb);
+ }
+}
+
+
+static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
+ struct ScsiReqBlk *srb)
+{
+ struct scsi_cmnd *cmd = srb->cmd;
+ dprintkdbg(DBG_1, "request_sense: (pid#%li) <%02i-%i>\n",
+ cmd->pid, cmd->device->id, cmd->device->lun);
+
+ srb->flag |= AUTO_REQSENSE;
+ srb->adapter_status = 0;
+ srb->target_status = 0;
+
+ /* KG: Can this prevent crap sense data ? */
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ /* Save some data */
+ srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1].address =
+ srb->segment_x[0].address;
+ srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1].length =
+ srb->segment_x[0].length;
+ srb->xferred = srb->total_xfer_length;
+ /* srb->segment_x : a one entry of S/G list table */
+ srb->total_xfer_length = sizeof(cmd->sense_buffer);
+ srb->segment_x[0].length = sizeof(cmd->sense_buffer);
+ /* Map sense buffer */
+ srb->segment_x[0].address =
+ pci_map_single(acb->dev, cmd->sense_buffer,
+ sizeof(cmd->sense_buffer), PCI_DMA_FROMDEVICE);
+ dprintkdbg(DBG_SG, "request_sense: map buffer %p->%08x(%05x)\n",
+ cmd->sense_buffer, srb->segment_x[0].address,
+ sizeof(cmd->sense_buffer));
+ srb->sg_count = 1;
+ srb->sg_index = 0;
+
+ if (start_scsi(acb, dcb, srb)) { /* Should only happen, if sb. else grabs the bus */
+ dprintkl(KERN_DEBUG,
+ "request_sense: (pid#%li) failed <%02i-%i>\n",
+ srb->cmd->pid, dcb->target_id, dcb->target_lun);
+ srb_going_to_waiting_move(dcb, srb);
+ waiting_set_timer(acb, HZ / 100);
+ }
+}
+
+
+/**
+ * device_alloc - Allocate a new device instance. This create the
+ * devices instance and sets up all the data items. The adapter
+ * instance is required to obtain confiuration information for this
+ * device. This does *not* add this device to the adapters device
+ * list.
+ *
+ * @acb: The adapter to obtain configuration information from.
+ * @target: The target for the new device.
+ * @lun: The lun for the new device.
+ *
+ * Return the new device if succesfull or NULL on failure.
+ **/
+static struct DeviceCtlBlk *device_alloc(struct AdapterCtlBlk *acb,
+ u8 target, u8 lun)
+{
+ struct NvRamType *eeprom = &acb->eeprom;
+ u8 period_index = eeprom->target[target].period & 0x07;
+ struct DeviceCtlBlk *dcb;
+
+ dcb = kmalloc(sizeof(struct DeviceCtlBlk), GFP_ATOMIC);
+ dprintkdbg(DBG_0, "device_alloc: <%02i-%i>\n", target, lun);
+ if (!dcb)
+ return NULL;
+ dcb->acb = NULL;
+ INIT_LIST_HEAD(&dcb->srb_going_list);
+ INIT_LIST_HEAD(&dcb->srb_waiting_list);
+ dcb->active_srb = NULL;
+ dcb->tag_mask = 0;
+ dcb->max_command = 1;
+ dcb->target_id = target;
+ dcb->target_lun = lun;
+#ifndef DC395x_NO_DISCONNECT
+ dcb->identify_msg =
+ IDENTIFY(dcb->dev_mode & NTC_DO_DISCONNECT, lun);
+#else
+ dcb->identify_msg = IDENTIFY(0, lun);
+#endif
+ dcb->dev_mode = eeprom->target[target].cfg0;
+ dcb->inquiry7 = 0;
+ dcb->sync_mode = 0;
+ dcb->min_nego_period = clock_period[period_index];
+ dcb->sync_period = 0;
+ dcb->sync_offset = 0;
+ dcb->flag = 0;
+
+#ifndef DC395x_NO_WIDE
+ if ((dcb->dev_mode & NTC_DO_WIDE_NEGO)
+ && (acb->config & HCC_WIDE_CARD))
+ dcb->sync_mode |= WIDE_NEGO_ENABLE;
+#endif
+#ifndef DC395x_NO_SYNC
+ if (dcb->dev_mode & NTC_DO_SYNC_NEGO)
+ if (!(lun) || current_sync_offset)
+ dcb->sync_mode |= SYNC_NEGO_ENABLE;
+#endif
+ if (dcb->target_lun != 0) {
+ /* Copy settings */
+ struct DeviceCtlBlk *p;
+ list_for_each_entry(p, &acb->dcb_list, list)
+ if (p->target_id == dcb->target_id)
+ break;
+ dprintkdbg(DBG_1,
+ "device_alloc: <%02i-%i> copy from <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun,
+ p->target_id, p->target_lun);
+ dcb->sync_mode = p->sync_mode;
+ dcb->sync_period = p->sync_period;
+ dcb->min_nego_period = p->min_nego_period;
+ dcb->sync_offset = p->sync_offset;
+ dcb->inquiry7 = p->inquiry7;
+ }
+ return dcb;
+}
+
+
+/**
+ * adapter_add_device - Adds the device instance to the adaptor instance.
+ *
+ * @acb: The adapter device to be updated
+ * @dcb: A newly created and intialised device instance to add.
+ **/
+static void adapter_add_device(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb)
+{
+ /* backpointer to adapter */
+ dcb->acb = acb;
+
+ /* set run_robin to this device if it is currently empty */
+ if (list_empty(&acb->dcb_list))
+ acb->dcb_run_robin = dcb;
+
+ /* add device to list */
+ list_add_tail(&dcb->list, &acb->dcb_list);
+
+ /* update device maps */
+ acb->dcb_map[dcb->target_id] |= (1 << dcb->target_lun);
+ acb->children[dcb->target_id][dcb->target_lun] = dcb;
+}
+
+
+/**
+ * adapter_remove_device - Removes the device instance from the adaptor
+ * instance. The device instance is not check in any way or freed by this.
+ * The caller is expected to take care of that. This will simply remove the
+ * device from the adapters data strcutures.
+ *
+ * @acb: The adapter device to be updated
+ * @dcb: A device that has previously been added to the adapter.
+ **/
+static void adapter_remove_device(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb)
+{
+ struct DeviceCtlBlk *i;
+ struct DeviceCtlBlk *tmp;
+ dprintkdbg(DBG_0, "adapter_remove_device: <%02i-%i>\n",
+ dcb->target_id, dcb->target_lun);
+
+ /* fix up any pointers to this device that we have in the adapter */
+ if (acb->active_dcb == dcb)
+ acb->active_dcb = NULL;
+ if (acb->dcb_run_robin == dcb)
+ acb->dcb_run_robin = dcb_get_next(&acb->dcb_list, dcb);
+
+ /* unlink from list */
+ list_for_each_entry_safe(i, tmp, &acb->dcb_list, list)
+ if (dcb == i) {
+ list_del(&i->list);
+ break;
+ }
+
+ /* clear map and children */
+ acb->dcb_map[dcb->target_id] &= ~(1 << dcb->target_lun);
+ acb->children[dcb->target_id][dcb->target_lun] = NULL;
+ dcb->acb = NULL;
+}
+
+
+/**
+ * adapter_remove_and_free_device - Removes a single device from the adapter
+ * and then frees the device information.
+ *
+ * @acb: The adapter device to be updated
+ * @dcb: A device that has previously been added to the adapter.
+ */
+static void adapter_remove_and_free_device(struct AdapterCtlBlk *acb,
+ struct DeviceCtlBlk *dcb)
+{
+ if (list_size(&dcb->srb_going_list) > 1) {
+ dprintkdbg(DBG_1, "adapter_remove_and_free_device: <%02i-%i> "
+ "Won't remove because of %i active requests.\n",
+ dcb->target_id, dcb->target_lun,
+ list_size(&dcb->srb_going_list));
+ return;
+ }
+ adapter_remove_device(acb, dcb);
+ kfree(dcb);
+}
+
+
+/**
+ * adapter_remove_and_free_all_devices - Removes and frees all of the
+ * devices associated with the specified adapter.
+ *
+ * @acb: The adapter from which all devices should be removed.
+ **/
+static void adapter_remove_and_free_all_devices(struct AdapterCtlBlk* acb)
+{
+ struct DeviceCtlBlk *dcb;
+ struct DeviceCtlBlk *tmp;
+ dprintkdbg(DBG_1, "adapter_remove_and_free_all_devices: num=%i\n",
+ list_size(&acb->dcb_list));
+
+ list_for_each_entry_safe(dcb, tmp, &acb->dcb_list, list)
+ adapter_remove_and_free_device(acb, dcb);
+}
+
+
+/**
+ * dc395x_slave_alloc - Called by the scsi mid layer to tell us about a new
+ * scsi device that we need to deal with. We allocate a new device and then
+ * insert that device into the adapters device list.
+ *
+ * @scsi_device: The new scsi device that we need to handle.
+ **/
+static int dc395x_slave_alloc(struct scsi_device *scsi_device)
+{
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)scsi_device->host->hostdata;
+ struct DeviceCtlBlk *dcb;
+
+ dcb = device_alloc(acb, scsi_device->id, scsi_device->lun);
+ if (!dcb)
+ return -ENOMEM;
+ adapter_add_device(acb, dcb);
+
+ return 0;
+}
+
+
+/**
+ * dc395x_slave_destroy - Called by the scsi mid layer to tell us about a
+ * device that is going away.
+ *
+ * @scsi_device: The new scsi device that we need to handle.
+ **/
+static void dc395x_slave_destroy(struct scsi_device *scsi_device)
+{
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)scsi_device->host->hostdata;
+ struct DeviceCtlBlk *dcb = find_dcb(acb, scsi_device->id, scsi_device->lun);
+ if (dcb)
+ adapter_remove_and_free_device(acb, dcb);
+}
+
+
+
+
+/**
+ * trms1040_wait_30us: wait for 30 us
+ *
+ * Waits for 30us (using the chip by the looks of it..)
+ *
+ * @io_port: base I/O address
+ **/
+static void __devinit trms1040_wait_30us(unsigned long io_port)
+{
+ /* ScsiPortStallExecution(30); wait 30 us */
+ outb(5, io_port + TRM_S1040_GEN_TIMER);
+ while (!(inb(io_port + TRM_S1040_GEN_STATUS) & GTIMEOUT))
+ /* nothing */ ;
+}
+
+
+/**
+ * trms1040_write_cmd - write the secified command and address to
+ * chip
+ *
+ * @io_port: base I/O address
+ * @cmd: SB + op code (command) to send
+ * @addr: address to send
+ **/
+static void __devinit trms1040_write_cmd(unsigned long io_port, u8 cmd, u8 addr)
+{
+ int i;
+ u8 send_data;
+
+ /* program SB + OP code */
+ for (i = 0; i < 3; i++, cmd <<= 1) {
+ send_data = NVR_SELECT;
+ if (cmd & 0x04) /* Start from bit 2 */
+ send_data |= NVR_BITOUT;
+
+ outb(send_data, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ outb((send_data | NVR_CLOCK),
+ io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ }
+
+ /* send address */
+ for (i = 0; i < 7; i++, addr <<= 1) {
+ send_data = NVR_SELECT;
+ if (addr & 0x40) /* Start from bit 6 */
+ send_data |= NVR_BITOUT;
+
+ outb(send_data, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ outb((send_data | NVR_CLOCK),
+ io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ }
+ outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+}
+
+
+/**
+ * trms1040_set_data - store a single byte in the eeprom
+ *
+ * Called from write all to write a single byte into the SSEEPROM
+ * Which is done one bit at a time.
+ *
+ * @io_port: base I/O address
+ * @addr: offset into EEPROM
+ * @byte: bytes to write
+ **/
+static void __devinit trms1040_set_data(unsigned long io_port, u8 addr, u8 byte)
+{
+ int i;
+ u8 send_data;
+
+ /* Send write command & address */
+ trms1040_write_cmd(io_port, 0x05, addr);
+
+ /* Write data */
+ for (i = 0; i < 8; i++, byte <<= 1) {
+ send_data = NVR_SELECT;
+ if (byte & 0x80) /* Start from bit 7 */
+ send_data |= NVR_BITOUT;
+
+ outb(send_data, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ outb((send_data | NVR_CLOCK), io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ }
+ outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ /* Disable chip select */
+ outb(0, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ /* Wait for write ready */
+ while (1) {
+ outb((NVR_SELECT | NVR_CLOCK), io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ if (inb(io_port + TRM_S1040_GEN_NVRAM) & NVR_BITIN)
+ break;
+ }
+
+ /* Disable chip select */
+ outb(0, io_port + TRM_S1040_GEN_NVRAM);
+}
+
+
+/**
+ * trms1040_write_all - write 128 bytes to the eeprom
+ *
+ * Write the supplied 128 bytes to the chips SEEPROM
+ *
+ * @eeprom: the data to write
+ * @io_port: the base io port
+ **/
+static void __devinit trms1040_write_all(struct NvRamType *eeprom, unsigned long io_port)
+{
+ u8 *b_eeprom = (u8 *)eeprom;
+ u8 addr;
+
+ /* Enable SEEPROM */
+ outb((inb(io_port + TRM_S1040_GEN_CONTROL) | EN_EEPROM),
+ io_port + TRM_S1040_GEN_CONTROL);
+
+ /* write enable */
+ trms1040_write_cmd(io_port, 0x04, 0xFF);
+ outb(0, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ /* write */
+ for (addr = 0; addr < 128; addr++, b_eeprom++)
+ trms1040_set_data(io_port, addr, *b_eeprom);
+
+ /* write disable */
+ trms1040_write_cmd(io_port, 0x04, 0x00);
+ outb(0, io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+
+ /* Disable SEEPROM */
+ outb((inb(io_port + TRM_S1040_GEN_CONTROL) & ~EN_EEPROM),
+ io_port + TRM_S1040_GEN_CONTROL);
+}
+
+
+/**
+ * trms1040_get_data - get a single byte from the eeprom
+ *
+ * Called from read all to read a single byte into the SSEEPROM
+ * Which is done one bit at a time.
+ *
+ * @io_port: base I/O address
+ * @addr: offset into SEEPROM
+ *
+ * Returns the the byte read.
+ **/
+static u8 __devinit trms1040_get_data(unsigned long io_port, u8 addr)
+{
+ int i;
+ u8 read_byte;
+ u8 result = 0;
+
+ /* Send read command & address */
+ trms1040_write_cmd(io_port, 0x06, addr);
+
+ /* read data */
+ for (i = 0; i < 8; i++) {
+ outb((NVR_SELECT | NVR_CLOCK), io_port + TRM_S1040_GEN_NVRAM);
+ trms1040_wait_30us(io_port);
+ outb(NVR_SELECT, io_port + TRM_S1040_GEN_NVRAM);
+
+ /* Get data bit while falling edge */
+ read_byte = inb(io_port + TRM_S1040_GEN_NVRAM);
+ result <<= 1;
+ if (read_byte & NVR_BITIN)
+ result |= 1;
+
+ trms1040_wait_30us(io_port);
+ }
+
+ /* Disable chip select */
+ outb(0, io_port + TRM_S1040_GEN_NVRAM);
+ return result;
+}
+
+
+/**
+ * trms1040_read_all - read all bytes from the eeprom
+ *
+ * Read the 128 bytes from the SEEPROM.
+ *
+ * @eeprom: where to store the data
+ * @io_port: the base io port
+ **/
+static void __devinit trms1040_read_all(struct NvRamType *eeprom, unsigned long io_port)
+{
+ u8 *b_eeprom = (u8 *)eeprom;
+ u8 addr;
+
+ /* Enable SEEPROM */
+ outb((inb(io_port + TRM_S1040_GEN_CONTROL) | EN_EEPROM),
+ io_port + TRM_S1040_GEN_CONTROL);
+
+ /* read details */
+ for (addr = 0; addr < 128; addr++, b_eeprom++)
+ *b_eeprom = trms1040_get_data(io_port, addr);
+
+ /* Disable SEEPROM */
+ outb((inb(io_port + TRM_S1040_GEN_CONTROL) & ~EN_EEPROM),
+ io_port + TRM_S1040_GEN_CONTROL);
+}
+
+
+
+/**
+ * check_eeprom - get and check contents of the eeprom
+ *
+ * Read seeprom 128 bytes into the memory provider in eeprom.
+ * Checks the checksum and if it's not correct it uses a set of default
+ * values.
+ *
+ * @eeprom: caller allocated strcuture to read the eeprom data into
+ * @io_port: io port to read from
+ **/
+static void __devinit check_eeprom(struct NvRamType *eeprom, unsigned long io_port)
+{
+ u16 *w_eeprom = (u16 *)eeprom;
+ u16 w_addr;
+ u16 cksum;
+ u32 d_addr;
+ u32 *d_eeprom;
+
+ trms1040_read_all(eeprom, io_port); /* read eeprom */
+
+ cksum = 0;
+ for (w_addr = 0, w_eeprom = (u16 *)eeprom; w_addr < 64;
+ w_addr++, w_eeprom++)
+ cksum += *w_eeprom;
+ if (cksum != 0x1234) {
+ /*
+ * Checksum is wrong.
+ * Load a set of defaults into the eeprom buffer
+ */
+ dprintkl(KERN_WARNING,
+ "EEProm checksum error: using default values and options.\n");
+ eeprom->sub_vendor_id[0] = (u8)PCI_VENDOR_ID_TEKRAM;
+ eeprom->sub_vendor_id[1] = (u8)(PCI_VENDOR_ID_TEKRAM >> 8);
+ eeprom->sub_sys_id[0] = (u8)PCI_DEVICE_ID_TEKRAM_TRMS1040;
+ eeprom->sub_sys_id[1] =
+ (u8)(PCI_DEVICE_ID_TEKRAM_TRMS1040 >> 8);
+ eeprom->sub_class = 0x00;
+ eeprom->vendor_id[0] = (u8)PCI_VENDOR_ID_TEKRAM;
+ eeprom->vendor_id[1] = (u8)(PCI_VENDOR_ID_TEKRAM >> 8);
+ eeprom->device_id[0] = (u8)PCI_DEVICE_ID_TEKRAM_TRMS1040;
+ eeprom->device_id[1] =
+ (u8)(PCI_DEVICE_ID_TEKRAM_TRMS1040 >> 8);
+ eeprom->reserved = 0x00;
+
+ for (d_addr = 0, d_eeprom = (u32 *)eeprom->target;
+ d_addr < 16; d_addr++, d_eeprom++)
+ *d_eeprom = 0x00000077; /* cfg3,cfg2,period,cfg0 */
+
+ *d_eeprom++ = 0x04000F07; /* max_tag,delay_time,channel_cfg,scsi_id */
+ *d_eeprom++ = 0x00000015; /* reserved1,boot_lun,boot_target,reserved0 */
+ for (d_addr = 0; d_addr < 12; d_addr++, d_eeprom++)
+ *d_eeprom = 0x00;
+
+ /* Now load defaults (maybe set by boot/module params) */
+ set_safe_settings();
+ fix_settings();
+ eeprom_override(eeprom);
+
+ eeprom->cksum = 0x00;
+ for (w_addr = 0, cksum = 0, w_eeprom = (u16 *)eeprom;
+ w_addr < 63; w_addr++, w_eeprom++)
+ cksum += *w_eeprom;
+
+ *w_eeprom = 0x1234 - cksum;
+ trms1040_write_all(eeprom, io_port);
+ eeprom->delay_time = cfg_data[CFG_RESET_DELAY].value;
+ } else {
+ set_safe_settings();
+ eeprom_index_to_delay(eeprom);
+ eeprom_override(eeprom);
+ }
+}
+
+
+/**
+ * print_eeprom_settings - output the eeprom settings
+ * to the kernel log so people can see what they were.
+ *
+ * @eeprom: The eeprom data strucutre to show details for.
+ **/
+static void __devinit print_eeprom_settings(struct NvRamType *eeprom)
+{
+ dprintkl(KERN_INFO, "Used settings: AdapterID=%02i, Speed=%i(%02i.%01iMHz), dev_mode=0x%02x\n",
+ eeprom->scsi_id,
+ eeprom->target[0].period,
+ clock_speed[eeprom->target[0].period] / 10,
+ clock_speed[eeprom->target[0].period] % 10,
+ eeprom->target[0].cfg0);
+ dprintkl(KERN_INFO, " AdaptMode=0x%02x, Tags=%i(%02i), DelayReset=%is\n",
+ eeprom->channel_cfg, eeprom->max_tag,
+ 1 << eeprom->max_tag, eeprom->delay_time);
+}
+
+
+/* Free SG tables */
+static void adapter_sg_tables_free(struct AdapterCtlBlk *acb)
+{
+ int i;
+ const unsigned srbs_per_page = PAGE_SIZE/SEGMENTX_LEN;
+
+ for (i = 0; i < DC395x_MAX_SRB_CNT; i += srbs_per_page)
+ kfree(acb->srb_array[i].segment_x);
+
+ vfree(acb->srb_array[0].virt_map);
+}
+
+
+/*
+ * Allocate SG tables; as we have to pci_map them, an SG list (struct SGentry*)
+ * should never cross a page boundary */
+static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb)
+{
+ const unsigned mem_needed = (DC395x_MAX_SRB_CNT+1)
+ *SEGMENTX_LEN;
+ int pages = (mem_needed+(PAGE_SIZE-1))/PAGE_SIZE;
+ const unsigned srbs_per_page = PAGE_SIZE/SEGMENTX_LEN;
+ int srb_idx = 0;
+ unsigned i = 0;
+ struct SGentry *ptr;
+ void **virt_array;
+
+ for (i = 0; i < DC395x_MAX_SRB_CNT; i++) {
+ acb->srb_array[i].segment_x = NULL;
+ acb->srb_array[i].virt_map = NULL;
+ }
+
+ dprintkdbg(DBG_1, "Allocate %i pages for SG tables\n", pages);
+ while (pages--) {
+ ptr = (struct SGentry *)kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!ptr) {
+ adapter_sg_tables_free(acb);
+ return 1;
+ }
+ dprintkdbg(DBG_1, "Allocate %li bytes at %p for SG segments %i\n",
+ PAGE_SIZE, ptr, srb_idx);
+ i = 0;
+ while (i < srbs_per_page && srb_idx < DC395x_MAX_SRB_CNT)
+ acb->srb_array[srb_idx++].segment_x =
+ ptr + (i++ * DC395x_MAX_SG_LISTENTRY);
+ }
+ if (i < srbs_per_page)
+ acb->srb.segment_x =
+ ptr + (i * DC395x_MAX_SG_LISTENTRY);
+ else
+ dprintkl(KERN_DEBUG, "No space for tmsrb SG table reserved?!\n");
+
+ virt_array = vmalloc((DC395x_MAX_SRB_CNT + 1) * DC395x_MAX_SG_LISTENTRY * sizeof(void*));
+
+ if (!virt_array) {
+ adapter_sg_tables_free(acb);
+ return 1;
+ }
+
+ for (i = 0; i < DC395x_MAX_SRB_CNT + 1; i++) {
+ acb->srb_array[i].virt_map = virt_array;
+ virt_array += DC395x_MAX_SG_LISTENTRY;
+ }
+
+ return 0;
+}
+
+
+
+/**
+ * adapter_print_config - print adapter connection and termination
+ * config
+ *
+ * The io port in the adapter needs to have been set before calling
+ * this function.
+ *
+ * @acb: The adapter to print the information for.
+ **/
+static void __devinit adapter_print_config(struct AdapterCtlBlk *acb)
+{
+ u8 bval;
+
+ bval = DC395x_read8(acb, TRM_S1040_GEN_STATUS);
+ dprintkl(KERN_INFO, "%sConnectors: ",
+ ((bval & WIDESCSI) ? "(Wide) " : ""));
+ if (!(bval & CON5068))
+ printk("ext%s ", !(bval & EXT68HIGH) ? "68" : "50");
+ if (!(bval & CON68))
+ printk("int68%s ", !(bval & INT68HIGH) ? "" : "(50)");
+ if (!(bval & CON50))
+ printk("int50 ");
+ if ((bval & (CON5068 | CON50 | CON68)) ==
+ 0 /*(CON5068 | CON50 | CON68) */ )
+ printk(" Oops! (All 3?) ");
+ bval = DC395x_read8(acb, TRM_S1040_GEN_CONTROL);
+ printk(" Termination: ");
+ if (bval & DIS_TERM)
+ printk("Disabled\n");
+ else {
+ if (bval & AUTOTERM)
+ printk("Auto ");
+ if (bval & LOW8TERM)
+ printk("Low ");
+ if (bval & UP8TERM)
+ printk("High ");
+ printk("\n");
+ }
+}
+
+
+/**
+ * adapter_init_params - Initialize the various parameters in the
+ * adapter structure. Note that the pointer to the scsi_host is set
+ * early (when this instance is created) and the io_port and irq
+ * values are set later after they have been reserved. This just gets
+ * everything set to a good starting position.
+ *
+ * The eeprom structure in the adapter needs to have been set before
+ * calling this function.
+ *
+ * @acb: The adapter to initialize.
+ **/
+static void __devinit adapter_init_params(struct AdapterCtlBlk *acb)
+{
+ struct NvRamType *eeprom = &acb->eeprom;
+ int i;
+
+ /* NOTE: acb->scsi_host is set at scsi_host/acb creation time */
+ /* NOTE: acb->io_port_base is set at port registration time */
+ /* NOTE: acb->io_port_len is set at port registration time */
+
+ INIT_LIST_HEAD(&acb->dcb_list);
+ acb->dcb_run_robin = NULL;
+ acb->active_dcb = NULL;
+
+ INIT_LIST_HEAD(&acb->srb_free_list);
+ /* temp SRB for Q tag used or abort command used */
+ acb->tmp_srb = &acb->srb;
+ init_timer(&acb->waiting_timer);
+ init_timer(&acb->selto_timer);
+
+ acb->srb_count = DC395x_MAX_SRB_CNT;
+
+ acb->sel_timeout = DC395x_SEL_TIMEOUT; /* timeout=250ms */
+ /* NOTE: acb->irq_level is set at IRQ registration time */
+
+ acb->tag_max_num = 1 << eeprom->max_tag;
+ if (acb->tag_max_num > 30)
+ acb->tag_max_num = 30;
+
+ acb->acb_flag = 0; /* RESET_DETECT, RESET_DONE, RESET_DEV */
+ acb->gmode2 = eeprom->channel_cfg;
+ acb->config = 0; /* NOTE: actually set in adapter_init_chip */
+
+ if (eeprom->channel_cfg & NAC_SCANLUN)
+ acb->lun_chk = 1;
+ acb->scan_devices = 1;
+
+ acb->scsi_host->this_id = eeprom->scsi_id;
+ acb->hostid_bit = (1 << acb->scsi_host->this_id);
+
+ for (i = 0; i < DC395x_MAX_SCSI_ID; i++)
+ acb->dcb_map[i] = 0;
+
+ acb->msg_len = 0;
+
+ /* link static array of srbs into the srb free list */
+ for (i = 0; i < acb->srb_count - 1; i++)
+ srb_free_insert(acb, &acb->srb_array[i]);
+}
+
+
+/**
+ * adapter_init_host - Initialize the scsi host instance based on
+ * values that we have already stored in the adapter instance. There's
+ * some mention that a lot of these are deprecated, so we won't use
+ * them (we'll use the ones in the adapter instance) but we'll fill
+ * them in in case something else needs them.
+ *
+ * The eeprom structure, irq and io ports in the adapter need to have
+ * been set before calling this function.
+ *
+ * @host: The scsi host instance to fill in the values for.
+ **/
+static void __devinit adapter_init_scsi_host(struct Scsi_Host *host)
+{
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)host->hostdata;
+ struct NvRamType *eeprom = &acb->eeprom;
+
+ host->max_cmd_len = 24;
+ host->can_queue = DC395x_MAX_CMD_QUEUE;
+ host->cmd_per_lun = DC395x_MAX_CMD_PER_LUN;
+ host->this_id = (int)eeprom->scsi_id;
+ host->io_port = acb->io_port_base;
+ host->n_io_port = acb->io_port_len;
+ host->dma_channel = -1;
+ host->unique_id = acb->io_port_base;
+ host->irq = acb->irq_level;
+ host->last_reset = jiffies;
+
+ host->max_id = 16;
+ if (host->max_id - 1 == eeprom->scsi_id)
+ host->max_id--;
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if (eeprom->channel_cfg & NAC_SCANLUN)
+ host->max_lun = 8;
+ else
+ host->max_lun = 1;
+#else
+ host->max_lun = 1;
+#endif
+
+}
+
+
+/**
+ * adapter_init_chip - Get the chip into a know state and figure out
+ * some of the settings that apply to this adapter.
+ *
+ * The io port in the adapter needs to have been set before calling
+ * this function. The config will be configured correctly on return.
+ *
+ * @acb: The adapter which we are to init.
+ **/
+static void __devinit adapter_init_chip(struct AdapterCtlBlk *acb)
+{
+ struct NvRamType *eeprom = &acb->eeprom;
+
+ /* Mask all the interrupt */
+ DC395x_write8(acb, TRM_S1040_DMA_INTEN, 0x00);
+ DC395x_write8(acb, TRM_S1040_SCSI_INTEN, 0x00);
+
+ /* Reset SCSI module */
+ DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_RSTMODULE);
+
+ /* Reset PCI/DMA module */
+ DC395x_write8(acb, TRM_S1040_DMA_CONTROL, DMARESETMODULE);
+ udelay(20);
+
+ /* program configuration 0 */
+ acb->config = HCC_AUTOTERM | HCC_PARITY;
+ if (DC395x_read8(acb, TRM_S1040_GEN_STATUS) & WIDESCSI)
+ acb->config |= HCC_WIDE_CARD;
+
+ if (eeprom->channel_cfg & NAC_POWERON_SCSI_RESET)
+ acb->config |= HCC_SCSI_RESET;
+
+ if (acb->config & HCC_SCSI_RESET) {
+ dprintkl(KERN_INFO, "Performing initial SCSI bus reset\n");
+ DC395x_write8(acb, TRM_S1040_SCSI_CONTROL, DO_RSTSCSI);
+
+ /*while (!( DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET )); */
+ /*spin_unlock_irq (&io_request_lock); */
+ udelay(500);
+
+ acb->scsi_host->last_reset =
+ jiffies + HZ / 2 +
+ HZ * acb->eeprom.delay_time;
+
+ /*spin_lock_irq (&io_request_lock); */
+ }
+}
+
+
+/**
+ * init_adapter - Grab the resource for the card, setup the adapter
+ * information, set the card into a known state, create the various
+ * tables etc etc. This basically gets all adapter information all up
+ * to date, intialised and gets the chip in sync with it.
+ *
+ * @host: This hosts adapter structure
+ * @io_port: The base I/O port
+ * @irq: IRQ
+ *
+ * Returns 0 if the initialization succeeds, any other value on
+ * failure.
+ **/
+static int __devinit adapter_init(struct AdapterCtlBlk *acb,
+ unsigned long io_port, u32 io_port_len, unsigned int irq)
+{
+ if (!request_region(io_port, io_port_len, DC395X_NAME)) {
+ dprintkl(KERN_ERR, "Failed to reserve IO region 0x%lx\n", io_port);
+ goto failed;
+ }
+ /* store port base to indicate we have registered it */
+ acb->io_port_base = io_port;
+ acb->io_port_len = io_port_len;
+
+ if (request_irq(irq, dc395x_interrupt, SA_SHIRQ, DC395X_NAME, acb)) {
+ /* release the region we just claimed */
+ dprintkl(KERN_INFO, "Failed to register IRQ\n");
+ goto failed;
+ }
+ /* store irq to indicate we have registered it */
+ acb->irq_level = irq;
+
+ /* get eeprom configuration information and command line settings etc */
+ check_eeprom(&acb->eeprom, io_port);
+ print_eeprom_settings(&acb->eeprom);
+
+ /* setup adapter control block */
+ adapter_init_params(acb);
+
+ /* display card connectors/termination settings */
+ adapter_print_config(acb);
+
+ if (adapter_sg_tables_alloc(acb)) {
+ dprintkl(KERN_DEBUG, "Memory allocation for SG tables failed\n");
+ goto failed;
+ }
+ adapter_init_scsi_host(acb->scsi_host);
+ adapter_init_chip(acb);
+ set_basic_config(acb);
+
+ dprintkdbg(DBG_0,
+ "adapter_init: acb=%p, pdcb_map=%p psrb_array=%p "
+ "size{acb=0x%04x dcb=0x%04x srb=0x%04x}\n",
+ acb, acb->dcb_map, acb->srb_array, sizeof(struct AdapterCtlBlk),
+ sizeof(struct DeviceCtlBlk), sizeof(struct ScsiReqBlk));
+ return 0;
+
+failed:
+ if (acb->irq_level)
+ free_irq(acb->irq_level, acb);
+ if (acb->io_port_base)
+ release_region(acb->io_port_base, acb->io_port_len);
+ adapter_sg_tables_free(acb);
+
+ return 1;
+}
+
+
+/**
+ * adapter_uninit_chip - cleanly shut down the scsi controller chip,
+ * stopping all operations and disabling interrupt generation on the
+ * card.
+ *
+ * @acb: The adapter which we are to shutdown.
+ **/
+static void adapter_uninit_chip(struct AdapterCtlBlk *acb)
+{
+ /* disable interrupts */
+ DC395x_write8(acb, TRM_S1040_DMA_INTEN, 0);
+ DC395x_write8(acb, TRM_S1040_SCSI_INTEN, 0);
+
+ /* reset the scsi bus */
+ if (acb->config & HCC_SCSI_RESET)
+ reset_scsi_bus(acb);
+
+ /* clear any pending interupt state */
+ DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS);
+}
+
+
+
+/**
+ * adapter_uninit - Shut down the chip and release any resources that
+ * we had allocated. Once this returns the adapter should not be used
+ * anymore.
+ *
+ * @acb: The adapter which we are to un-initialize.
+ **/
+static void adapter_uninit(struct AdapterCtlBlk *acb)
+{
+ unsigned long flags;
+ DC395x_LOCK_IO(acb->scsi_host, flags);
+
+ /* remove timers */
+ if (timer_pending(&acb->waiting_timer))
+ del_timer(&acb->waiting_timer);
+ if (timer_pending(&acb->selto_timer))
+ del_timer(&acb->selto_timer);
+
+ adapter_uninit_chip(acb);
+ adapter_remove_and_free_all_devices(acb);
+ DC395x_UNLOCK_IO(acb->scsi_host, flags);
+
+ if (acb->irq_level)
+ free_irq(acb->irq_level, acb);
+ if (acb->io_port_base)
+ release_region(acb->io_port_base, acb->io_port_len);
+
+ adapter_sg_tables_free(acb);
+}
+
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, args)
+
+#undef YESNO
+#define YESNO(YN) \
+ if (YN) SPRINTF(" Yes ");\
+ else SPRINTF(" No ")
+
+static int dc395x_proc_info(struct Scsi_Host *host, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)host->hostdata;
+ int spd, spd1;
+ char *pos = buffer;
+ struct DeviceCtlBlk *dcb;
+ unsigned long flags;
+ int dev;
+
+ if (inout) /* Has data been written to the file ? */
+ return -EPERM;
+
+ SPRINTF(DC395X_BANNER " PCI SCSI Host Adapter\n");
+ SPRINTF(" Driver Version " DC395X_VERSION "\n");
+
+ DC395x_LOCK_IO(acb->scsi_host, flags);
+
+ SPRINTF("SCSI Host Nr %i, ", host->host_no);
+ SPRINTF("DC395U/UW/F DC315/U %s\n",
+ (acb->config & HCC_WIDE_CARD) ? "Wide" : "");
+ SPRINTF("io_port_base 0x%04lx, ", acb->io_port_base);
+ SPRINTF("irq_level 0x%04x, ", acb->irq_level);
+ SPRINTF(" SelTimeout %ims\n", (1638 * acb->sel_timeout) / 1000);
+
+ SPRINTF("MaxID %i, MaxLUN %i, ", host->max_id, host->max_lun);
+ SPRINTF("AdapterID %i\n", host->this_id);
+
+ SPRINTF("tag_max_num %i", acb->tag_max_num);
+ /*SPRINTF(", DMA_Status %i\n", DC395x_read8(acb, TRM_S1040_DMA_STATUS)); */
+ SPRINTF(", FilterCfg 0x%02x",
+ DC395x_read8(acb, TRM_S1040_SCSI_CONFIG1));
+ SPRINTF(", DelayReset %is\n", acb->eeprom.delay_time);
+ /*SPRINTF("\n"); */
+
+ SPRINTF("Nr of DCBs: %i\n", list_size(&acb->dcb_list));
+ SPRINTF
+ ("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ acb->dcb_map[0], acb->dcb_map[1], acb->dcb_map[2],
+ acb->dcb_map[3], acb->dcb_map[4], acb->dcb_map[5],
+ acb->dcb_map[6], acb->dcb_map[7]);
+ SPRINTF
+ (" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ acb->dcb_map[8], acb->dcb_map[9], acb->dcb_map[10],
+ acb->dcb_map[11], acb->dcb_map[12], acb->dcb_map[13],
+ acb->dcb_map[14], acb->dcb_map[15]);
+
+ SPRINTF
+ ("Un ID LUN Prty Sync Wide DsCn SndS TagQ nego_period SyncFreq SyncOffs MaxCmd\n");
+
+ dev = 0;
+ list_for_each_entry(dcb, &acb->dcb_list, list) {
+ int nego_period;
+ SPRINTF("%02i %02i %02i ", dev, dcb->target_id,
+ dcb->target_lun);
+ YESNO(dcb->dev_mode & NTC_DO_PARITY_CHK);
+ YESNO(dcb->sync_offset);
+ YESNO(dcb->sync_period & WIDE_SYNC);
+ YESNO(dcb->dev_mode & NTC_DO_DISCONNECT);
+ YESNO(dcb->dev_mode & NTC_DO_SEND_START);
+ YESNO(dcb->sync_mode & EN_TAG_QUEUEING);
+ nego_period = clock_period[dcb->sync_period & 0x07] << 2;
+ if (dcb->sync_offset)
+ SPRINTF(" %03i ns ", nego_period);
+ else
+ SPRINTF(" (%03i ns)", (dcb->min_nego_period << 2));
+
+ if (dcb->sync_offset & 0x0f) {
+ spd = 1000 / (nego_period);
+ spd1 = 1000 % (nego_period);
+ spd1 = (spd1 * 10 + nego_period / 2) / (nego_period);
+ SPRINTF(" %2i.%1i M %02i ", spd, spd1,
+ (dcb->sync_offset & 0x0f));
+ } else
+ SPRINTF(" ");
+
+ /* Add more info ... */
+ SPRINTF(" %02i\n", dcb->max_command);
+ dev++;
+ }
+
+ if (timer_pending(&acb->waiting_timer))
+ SPRINTF("Waiting queue timer running\n");
+ else
+ SPRINTF("\n");
+
+ list_for_each_entry(dcb, &acb->dcb_list, list) {
+ struct ScsiReqBlk *srb;
+ if (!list_empty(&dcb->srb_waiting_list))
+ SPRINTF("DCB (%02i-%i): Waiting: %i:",
+ dcb->target_id, dcb->target_lun,
+ list_size(&dcb->srb_waiting_list));
+ list_for_each_entry(srb, &dcb->srb_waiting_list, list)
+ SPRINTF(" %li", srb->cmd->pid);
+ if (!list_empty(&dcb->srb_going_list))
+ SPRINTF("\nDCB (%02i-%i): Going : %i:",
+ dcb->target_id, dcb->target_lun,
+ list_size(&dcb->srb_going_list));
+ list_for_each_entry(srb, &dcb->srb_going_list, list)
+ SPRINTF(" %li", srb->cmd->pid);
+ if (!list_empty(&dcb->srb_waiting_list) || !list_empty(&dcb->srb_going_list))
+ SPRINTF("\n");
+ }
+
+ if (debug_enabled(DBG_1)) {
+ SPRINTF("DCB list for ACB %p:\n", acb);
+ list_for_each_entry(dcb, &acb->dcb_list, list) {
+ SPRINTF("%p -> ", dcb);
+ }
+ SPRINTF("END\n");
+ }
+
+ *start = buffer + offset;
+ DC395x_UNLOCK_IO(acb->scsi_host, flags);
+
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+
+
+static struct scsi_host_template dc395x_driver_template = {
+ .module = THIS_MODULE,
+ .proc_name = DC395X_NAME,
+ .proc_info = dc395x_proc_info,
+ .name = DC395X_BANNER " " DC395X_VERSION,
+ .queuecommand = dc395x_queue_command,
+ .bios_param = dc395x_bios_param,
+ .slave_alloc = dc395x_slave_alloc,
+ .slave_destroy = dc395x_slave_destroy,
+ .can_queue = DC395x_MAX_CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = DC395x_MAX_SG_TABLESIZE,
+ .cmd_per_lun = DC395x_MAX_CMD_PER_LUN,
+ .eh_abort_handler = dc395x_eh_abort,
+ .eh_bus_reset_handler = dc395x_eh_bus_reset,
+ .unchecked_isa_dma = 0,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+
+/**
+ * banner_display - Display banner on first instance of driver
+ * initialized.
+ **/
+static void banner_display(void)
+{
+ static int banner_done = 0;
+ if (!banner_done)
+ {
+ dprintkl(KERN_INFO, "%s %s\n", DC395X_BANNER, DC395X_VERSION);
+ banner_done = 1;
+ }
+}
+
+
+/**
+ * dc395x_init_one - Initialise a single instance of the adapter.
+ *
+ * The PCI layer will call this once for each instance of the adapter
+ * that it finds in the system. The pci_dev strcuture indicates which
+ * instance we are being called from.
+ *
+ * @dev: The PCI device to intialize.
+ * @id: Looks like a pointer to the entry in our pci device table
+ * that was actually matched by the PCI subsystem.
+ *
+ * Returns 0 on success, or an error code (-ve) on failure.
+ **/
+static int __devinit dc395x_init_one(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct Scsi_Host *scsi_host = NULL;
+ struct AdapterCtlBlk *acb = NULL;
+ unsigned long io_port_base;
+ unsigned int io_port_len;
+ unsigned int irq;
+
+ dprintkdbg(DBG_0, "Init one instance (%s)\n", pci_name(dev));
+ banner_display();
+
+ if (pci_enable_device(dev))
+ {
+ dprintkl(KERN_INFO, "PCI Enable device failed.\n");
+ return -ENODEV;
+ }
+ io_port_base = pci_resource_start(dev, 0) & PCI_BASE_ADDRESS_IO_MASK;
+ io_port_len = pci_resource_len(dev, 0);
+ irq = dev->irq;
+ dprintkdbg(DBG_0, "IO_PORT=0x%04lx, IRQ=0x%x\n", io_port_base, dev->irq);
+
+ /* allocate scsi host information (includes out adapter) */
+ scsi_host = scsi_host_alloc(&dc395x_driver_template,
+ sizeof(struct AdapterCtlBlk));
+ if (!scsi_host) {
+ dprintkl(KERN_INFO, "scsi_host_alloc failed\n");
+ goto fail;
+ }
+ acb = (struct AdapterCtlBlk*)scsi_host->hostdata;
+ acb->scsi_host = scsi_host;
+ acb->dev = dev;
+
+ /* initialise the adapter and everything we need */
+ if (adapter_init(acb, io_port_base, io_port_len, irq)) {
+ dprintkl(KERN_INFO, "adapter init failed\n");
+ goto fail;
+ }
+
+ pci_set_master(dev);
+
+ /* get the scsi mid level to scan for new devices on the bus */
+ if (scsi_add_host(scsi_host, &dev->dev)) {
+ dprintkl(KERN_ERR, "scsi_add_host failed\n");
+ goto fail;
+ }
+ pci_set_drvdata(dev, scsi_host);
+ scsi_scan_host(scsi_host);
+
+ return 0;
+
+fail:
+ if (acb != NULL)
+ adapter_uninit(acb);
+ if (scsi_host != NULL)
+ scsi_host_put(scsi_host);
+ pci_disable_device(dev);
+ return -ENODEV;
+}
+
+
+/**
+ * dc395x_remove_one - Called to remove a single instance of the
+ * adapter.
+ *
+ * @dev: The PCI device to intialize.
+ **/
+static void __devexit dc395x_remove_one(struct pci_dev *dev)
+{
+ struct Scsi_Host *scsi_host = pci_get_drvdata(dev);
+ struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)(scsi_host->hostdata);
+
+ dprintkdbg(DBG_0, "dc395x_remove_one: acb=%p\n", acb);
+
+ scsi_remove_host(scsi_host);
+ adapter_uninit(acb);
+ pci_disable_device(dev);
+ scsi_host_put(scsi_host);
+ pci_set_drvdata(dev, NULL);
+}
+
+
+static struct pci_device_id dc395x_pci_table[] = {
+ {
+ .vendor = PCI_VENDOR_ID_TEKRAM,
+ .device = PCI_DEVICE_ID_TEKRAM_TRMS1040,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, dc395x_pci_table);
+
+
+static struct pci_driver dc395x_driver = {
+ .name = DC395X_NAME,
+ .id_table = dc395x_pci_table,
+ .probe = dc395x_init_one,
+ .remove = __devexit_p(dc395x_remove_one),
+};
+
+
+/**
+ * dc395x_module_init - Module initialization function
+ *
+ * Used by both module and built-in driver to initialise this driver.
+ **/
+static int __init dc395x_module_init(void)
+{
+ return pci_module_init(&dc395x_driver);
+}
+
+
+/**
+ * dc395x_module_exit - Module cleanup function.
+ **/
+static void __exit dc395x_module_exit(void)
+{
+ pci_unregister_driver(&dc395x_driver);
+}
+
+
+module_init(dc395x_module_init);
+module_exit(dc395x_module_exit);
+
+MODULE_AUTHOR("C.L. Huang / Erich Chen / Kurt Garloff");
+MODULE_DESCRIPTION("SCSI host adapter driver for Tekram TRM-S1040 based adapters: Tekram DC395 and DC315 series");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dc395x.h b/drivers/scsi/dc395x.h
new file mode 100644
index 000000000000..b38360e5fe4f
--- /dev/null
+++ b/drivers/scsi/dc395x.h
@@ -0,0 +1,648 @@
+/************************************************************************/
+/* */
+/* dc395x.h */
+/* */
+/* Device Driver for Tekram DC395(U/UW/F), DC315(U) */
+/* PCI SCSI Bus Master Host Adapter */
+/* (SCSI chip set used Tekram ASIC TRM-S1040) */
+/* */
+/************************************************************************/
+#ifndef DC395x_H
+#define DC395x_H
+
+/************************************************************************/
+/* */
+/* Initial values */
+/* */
+/************************************************************************/
+#define DC395x_MAX_CMD_QUEUE 32
+/* #define DC395x_MAX_QTAGS 32 */
+#define DC395x_MAX_QTAGS 16
+#define DC395x_MAX_SCSI_ID 16
+#define DC395x_MAX_CMD_PER_LUN DC395x_MAX_QTAGS
+#define DC395x_MAX_SG_TABLESIZE 64 /* HW limitation */
+#define DC395x_MAX_SG_LISTENTRY 64 /* Must be equal or lower to previous */
+ /* item */
+#define DC395x_MAX_SRB_CNT 63
+/* #define DC395x_MAX_CAN_QUEUE 7 * DC395x_MAX_QTAGS */
+#define DC395x_MAX_CAN_QUEUE DC395x_MAX_SRB_CNT
+#define DC395x_END_SCAN 2
+#define DC395x_SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */
+#define DC395x_MAX_RETRIES 3
+
+#if 0
+#define SYNC_FIRST
+#endif
+
+#define NORM_REC_LVL 0
+
+/************************************************************************/
+/* */
+/* Various definitions */
+/* */
+/************************************************************************/
+#define BIT31 0x80000000
+#define BIT30 0x40000000
+#define BIT29 0x20000000
+#define BIT28 0x10000000
+#define BIT27 0x08000000
+#define BIT26 0x04000000
+#define BIT25 0x02000000
+#define BIT24 0x01000000
+#define BIT23 0x00800000
+#define BIT22 0x00400000
+#define BIT21 0x00200000
+#define BIT20 0x00100000
+#define BIT19 0x00080000
+#define BIT18 0x00040000
+#define BIT17 0x00020000
+#define BIT16 0x00010000
+#define BIT15 0x00008000
+#define BIT14 0x00004000
+#define BIT13 0x00002000
+#define BIT12 0x00001000
+#define BIT11 0x00000800
+#define BIT10 0x00000400
+#define BIT9 0x00000200
+#define BIT8 0x00000100
+#define BIT7 0x00000080
+#define BIT6 0x00000040
+#define BIT5 0x00000020
+#define BIT4 0x00000010
+#define BIT3 0x00000008
+#define BIT2 0x00000004
+#define BIT1 0x00000002
+#define BIT0 0x00000001
+
+/* UnitCtrlFlag */
+#define UNIT_ALLOCATED BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY BIT3
+
+/* UnitFlags */
+#define DASD_SUPPORT BIT0
+#define SCSI_SUPPORT BIT1
+#define ASPI_SUPPORT BIT2
+
+/* SRBState machine definition */
+#define SRB_FREE 0x0000
+#define SRB_WAIT 0x0001
+#define SRB_READY 0x0002
+#define SRB_MSGOUT 0x0004 /* arbitration+msg_out 1st byte */
+#define SRB_MSGIN 0x0008
+#define SRB_EXTEND_MSGIN 0x0010
+#define SRB_COMMAND 0x0020
+#define SRB_START_ 0x0040 /* arbitration+msg_out+command_out */
+#define SRB_DISCONNECT 0x0080
+#define SRB_DATA_XFER 0x0100
+#define SRB_XFERPAD 0x0200
+#define SRB_STATUS 0x0400
+#define SRB_COMPLETED 0x0800
+#define SRB_ABORT_SENT 0x1000
+#define SRB_DO_SYNC_NEGO 0x2000
+#define SRB_DO_WIDE_NEGO 0x4000
+#define SRB_UNEXPECT_RESEL 0x8000
+
+/************************************************************************/
+/* */
+/* ACB Config */
+/* */
+/************************************************************************/
+#define HCC_WIDE_CARD 0x20
+#define HCC_SCSI_RESET 0x10
+#define HCC_PARITY 0x08
+#define HCC_AUTOTERM 0x04
+#define HCC_LOW8TERM 0x02
+#define HCC_UP8TERM 0x01
+
+/* ACBFlag */
+#define RESET_DEV BIT0
+#define RESET_DETECT BIT1
+#define RESET_DONE BIT2
+
+/* DCBFlag */
+#define ABORT_DEV_ BIT0
+
+/* SRBstatus */
+#define SRB_OK BIT0
+#define ABORTION BIT1
+#define OVER_RUN BIT2
+#define UNDER_RUN BIT3
+#define PARITY_ERROR BIT4
+#define SRB_ERROR BIT5
+
+/* SRBFlag */
+#define DATAOUT BIT7
+#define DATAIN BIT6
+#define RESIDUAL_VALID BIT5
+#define ENABLE_TIMER BIT4
+#define RESET_DEV0 BIT2
+#define ABORT_DEV BIT1
+#define AUTO_REQSENSE BIT0
+
+/* Adapter status */
+#define H_STATUS_GOOD 0
+#define H_SEL_TIMEOUT 0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD 0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB 0x19
+#define H_BAD_CCB_OR_SG 0x1A
+#define H_ABORT 0x0FF
+
+/* SCSI BUS Status byte codes */
+#define SCSI_STAT_GOOD 0x0 /* Good status */
+#define SCSI_STAT_CHECKCOND 0x02 /* SCSI Check Condition */
+#define SCSI_STAT_CONDMET 0x04 /* Condition Met */
+#define SCSI_STAT_BUSY 0x08 /* Target busy status */
+#define SCSI_STAT_INTER 0x10 /* Intermediate status */
+#define SCSI_STAT_INTERCONDMET 0x14 /* Intermediate condition met */
+#define SCSI_STAT_RESCONFLICT 0x18 /* Reservation conflict */
+#define SCSI_STAT_CMDTERM 0x22 /* Command Terminated */
+#define SCSI_STAT_QUEUEFULL 0x28 /* Queue Full */
+#define SCSI_STAT_UNEXP_BUS_F 0xFD /* Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT 0xFF /* Selection Time out */
+
+/* Sync_Mode */
+#define SYNC_WIDE_TAG_ATNT_DISABLE 0
+#define SYNC_NEGO_ENABLE BIT0
+#define SYNC_NEGO_DONE BIT1
+#define WIDE_NEGO_ENABLE BIT2
+#define WIDE_NEGO_DONE BIT3
+#define WIDE_NEGO_STATE BIT4
+#define EN_TAG_QUEUEING BIT5
+#define EN_ATN_STOP BIT6
+
+#define SYNC_NEGO_OFFSET 15
+
+/* SCSI MSG BYTE */
+#define MSG_COMPLETE 0x00
+#define MSG_EXTENDED 0x01
+#define MSG_SAVE_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INITIATOR_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT_ 0x07
+#define MSG_NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_LINK_CMD_COMPL 0x0A
+#define MSG_LINK_CMD_COMPL_FLG 0x0B
+#define MSG_BUS_RESET 0x0C
+#define MSG_ABORT_TAG 0x0D
+#define MSG_SIMPLE_QTAG 0x20
+#define MSG_HEAD_QTAG 0x21
+#define MSG_ORDER_QTAG 0x22
+#define MSG_IGNOREWIDE 0x23
+#define MSG_IDENTIFY 0x80
+#define MSG_HOST_ID 0xC0
+
+/* SCSI STATUS BYTE */
+#define STATUS_GOOD 0x00
+#define CHECK_CONDITION_ 0x02
+#define STATUS_BUSY 0x08
+#define STATUS_INTERMEDIATE 0x10
+#define RESERVE_CONFLICT 0x18
+
+/* cmd->result */
+#define STATUS_MASK_ 0xFF
+#define MSG_MASK 0xFF00
+#define RETURN_MASK 0xFF0000
+
+/************************************************************************/
+/* */
+/* Inquiry Data format */
+/* */
+/************************************************************************/
+struct ScsiInqData
+{ /* INQ */
+ u8 DevType; /* Periph Qualifier & Periph Dev Type */
+ u8 RMB_TypeMod; /* rem media bit & Dev Type Modifier */
+ u8 Vers; /* ISO, ECMA, & ANSI versions */
+ u8 RDF; /* AEN, TRMIOP, & response data format */
+ u8 AddLen; /* length of additional data */
+ u8 Res1; /* reserved */
+ u8 Res2; /* reserved */
+ u8 Flags; /* RelADr, Wbus32, Wbus16, Sync, etc. */
+ u8 VendorID[8]; /* Vendor Identification */
+ u8 ProductID[16]; /* Product Identification */
+ u8 ProductRev[4]; /* Product Revision */
+};
+
+ /* Inquiry byte 0 masks */
+#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */
+#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */
+ /* Inquiry byte 1 mask */
+#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */
+ /* Peripheral Device Type definitions */
+ /* See include/scsi/scsi.h */
+#define TYPE_NODEV SCSI_DEVTYPE /* Unknown or no device type */
+#ifndef TYPE_PRINTER /* */
+# define TYPE_PRINTER 0x02 /* Printer device */
+#endif /* */
+#ifndef TYPE_COMM /* */
+# define TYPE_COMM 0x09 /* Communications device */
+#endif
+
+/************************************************************************/
+/* */
+/* Inquiry flag definitions (Inq data byte 7) */
+/* */
+/************************************************************************/
+#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing */
+#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */
+#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */
+#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */
+#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */
+#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */
+#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */
+
+#define ENABLE_CE 1
+#define DISABLE_CE 0
+#define EEPROM_READ 0x80
+
+/************************************************************************/
+/* */
+/* The PCI configuration register offset for TRM_S1040 */
+/* */
+/************************************************************************/
+#define TRM_S1040_ID 0x00 /* Vendor and Device ID */
+#define TRM_S1040_COMMAND 0x04 /* PCI command register */
+#define TRM_S1040_IOBASE 0x10 /* I/O Space base address */
+#define TRM_S1040_ROMBASE 0x30 /* Expansion ROM Base Address */
+#define TRM_S1040_INTLINE 0x3C /* Interrupt line */
+
+/************************************************************************/
+/* */
+/* The SCSI register offset for TRM_S1040 */
+/* */
+/************************************************************************/
+#define TRM_S1040_SCSI_STATUS 0x80 /* SCSI Status (R) */
+#define COMMANDPHASEDONE 0x2000 /* SCSI command phase done */
+#define SCSIXFERDONE 0x0800 /* SCSI SCSI transfer done */
+#define SCSIXFERCNT_2_ZERO 0x0100 /* SCSI SCSI transfer count to zero */
+#define SCSIINTERRUPT 0x0080 /* SCSI interrupt pending */
+#define COMMANDABORT 0x0040 /* SCSI command abort */
+#define SEQUENCERACTIVE 0x0020 /* SCSI sequencer active */
+#define PHASEMISMATCH 0x0010 /* SCSI phase mismatch */
+#define PARITYERROR 0x0008 /* SCSI parity error */
+
+#define PHASEMASK 0x0007 /* Phase MSG/CD/IO */
+#define PH_DATA_OUT 0x00 /* Data out phase */
+#define PH_DATA_IN 0x01 /* Data in phase */
+#define PH_COMMAND 0x02 /* Command phase */
+#define PH_STATUS 0x03 /* Status phase */
+#define PH_BUS_FREE 0x05 /* Invalid phase used as bus free */
+#define PH_MSG_OUT 0x06 /* Message out phase */
+#define PH_MSG_IN 0x07 /* Message in phase */
+
+#define TRM_S1040_SCSI_CONTROL 0x80 /* SCSI Control (W) */
+#define DO_CLRATN 0x0400 /* Clear ATN */
+#define DO_SETATN 0x0200 /* Set ATN */
+#define DO_CMDABORT 0x0100 /* Abort SCSI command */
+#define DO_RSTMODULE 0x0010 /* Reset SCSI chip */
+#define DO_RSTSCSI 0x0008 /* Reset SCSI bus */
+#define DO_CLRFIFO 0x0004 /* Clear SCSI transfer FIFO */
+#define DO_DATALATCH 0x0002 /* Enable SCSI bus data input (latched) */
+/* #define DO_DATALATCH 0x0000 */ /* KG: DISable SCSI bus data latch */
+#define DO_HWRESELECT 0x0001 /* Enable hardware reselection */
+
+#define TRM_S1040_SCSI_FIFOCNT 0x82 /* SCSI FIFO Counter 5bits(R) */
+#define TRM_S1040_SCSI_SIGNAL 0x83 /* SCSI low level signal (R/W) */
+
+#define TRM_S1040_SCSI_INTSTATUS 0x84 /* SCSI Interrupt Status (R) */
+#define INT_SCAM 0x80 /* SCAM selection interrupt */
+#define INT_SELECT 0x40 /* Selection interrupt */
+#define INT_SELTIMEOUT 0x20 /* Selection timeout interrupt */
+#define INT_DISCONNECT 0x10 /* Bus disconnected interrupt */
+#define INT_RESELECTED 0x08 /* Reselected interrupt */
+#define INT_SCSIRESET 0x04 /* SCSI reset detected interrupt */
+#define INT_BUSSERVICE 0x02 /* Bus service interrupt */
+#define INT_CMDDONE 0x01 /* SCSI command done interrupt */
+
+#define TRM_S1040_SCSI_OFFSET 0x84 /* SCSI Offset Count (W) */
+
+/************************************************************************/
+/* */
+/* Bit Name Definition */
+/* --------- ------------- ---------------------------- */
+/* 07-05 0 RSVD Reversed. Always 0. */
+/* 04 0 OFFSET4 Reversed for LVDS. Always 0. */
+/* 03-00 0 OFFSET[03:00] Offset number from 0 to 15 */
+/* */
+/************************************************************************/
+
+#define TRM_S1040_SCSI_SYNC 0x85 /* SCSI Synchronous Control (R/W) */
+#define LVDS_SYNC 0x20 /* Enable LVDS synchronous */
+#define WIDE_SYNC 0x10 /* Enable WIDE synchronous */
+#define ALT_SYNC 0x08 /* Enable Fast-20 alternate synchronous */
+
+/************************************************************************/
+/* */
+/* SYNCM 7 6 5 4 3 2 1 0 */
+/* Name RSVD RSVD LVDS WIDE ALTPERD PERIOD2 PERIOD1 PERIOD0 */
+/* Default 0 0 0 0 0 0 0 0 */
+/* */
+/* Bit Name Definition */
+/* --------- ------------- --------------------------- */
+/* 07-06 0 RSVD Reversed. Always read 0 */
+/* 05 0 LVDS Reversed. Always read 0 */
+/* 04 0 WIDE/WSCSI Enable wide (16-bits) SCSI */
+/* transfer. */
+/* 03 0 ALTPERD/ALTPD Alternate (Sync./Period) mode. */
+/* */
+/* @@ When this bit is set, */
+/* the synchronous period bits 2:0 */
+/* in the Synchronous Mode register */
+/* are used to transfer data */
+/* at the Fast-20 rate. */
+/* @@ When this bit is unset, */
+/* the synchronous period bits 2:0 */
+/* in the Synchronous Mode Register */
+/* are used to transfer data */
+/* at the Fast-10 rate (or Fast-40 w/ LVDS). */
+/* */
+/* 02-00 0 PERIOD[2:0]/ Synchronous SCSI Transfer Rate. */
+/* SXPD[02:00] These 3 bits specify */
+/* the Synchronous SCSI Transfer */
+/* Rate for Fast-20 and Fast-10. */
+/* These bits are also reset */
+/* by a SCSI Bus reset. */
+/* */
+/* For Fast-10 bit ALTPD = 0 and LVDS = 0 */
+/* and bit2,bit1,bit0 is defined as follows : */
+/* */
+/* 000 100ns, 10.0 MHz */
+/* 001 150ns, 6.6 MHz */
+/* 010 200ns, 5.0 MHz */
+/* 011 250ns, 4.0 MHz */
+/* 100 300ns, 3.3 MHz */
+/* 101 350ns, 2.8 MHz */
+/* 110 400ns, 2.5 MHz */
+/* 111 450ns, 2.2 MHz */
+/* */
+/* For Fast-20 bit ALTPD = 1 and LVDS = 0 */
+/* and bit2,bit1,bit0 is defined as follows : */
+/* */
+/* 000 50ns, 20.0 MHz */
+/* 001 75ns, 13.3 MHz */
+/* 010 100ns, 10.0 MHz */
+/* 011 125ns, 8.0 MHz */
+/* 100 150ns, 6.6 MHz */
+/* 101 175ns, 5.7 MHz */
+/* 110 200ns, 5.0 MHz */
+/* 111 250ns, 4.0 MHz KG: Maybe 225ns, 4.4 MHz */
+/* */
+/* For Fast-40 bit ALTPD = 0 and LVDS = 1 */
+/* and bit2,bit1,bit0 is defined as follows : */
+/* */
+/* 000 25ns, 40.0 MHz */
+/* 001 50ns, 20.0 MHz */
+/* 010 75ns, 13.3 MHz */
+/* 011 100ns, 10.0 MHz */
+/* 100 125ns, 8.0 MHz */
+/* 101 150ns, 6.6 MHz */
+/* 110 175ns, 5.7 MHz */
+/* 111 200ns, 5.0 MHz */
+/* */
+/************************************************************************/
+
+#define TRM_S1040_SCSI_TARGETID 0x86 /* SCSI Target ID (R/W) */
+#define TRM_S1040_SCSI_IDMSG 0x87 /* SCSI Identify Message (R) */
+#define TRM_S1040_SCSI_HOSTID 0x87 /* SCSI Host ID (W) */
+#define TRM_S1040_SCSI_COUNTER 0x88 /* SCSI Transfer Counter 24bits(R/W) */
+
+#define TRM_S1040_SCSI_INTEN 0x8C /* SCSI Interrupt Enable (R/W) */
+#define EN_SCAM 0x80 /* Enable SCAM selection interrupt */
+#define EN_SELECT 0x40 /* Enable selection interrupt */
+#define EN_SELTIMEOUT 0x20 /* Enable selection timeout interrupt */
+#define EN_DISCONNECT 0x10 /* Enable bus disconnected interrupt */
+#define EN_RESELECTED 0x08 /* Enable reselected interrupt */
+#define EN_SCSIRESET 0x04 /* Enable SCSI reset detected interrupt */
+#define EN_BUSSERVICE 0x02 /* Enable bus service interrupt */
+#define EN_CMDDONE 0x01 /* Enable SCSI command done interrupt */
+
+#define TRM_S1040_SCSI_CONFIG0 0x8D /* SCSI Configuration 0 (R/W) */
+#define PHASELATCH 0x40 /* Enable phase latch */
+#define INITIATOR 0x20 /* Enable initiator mode */
+#define PARITYCHECK 0x10 /* Enable parity check */
+#define BLOCKRST 0x01 /* Disable SCSI reset1 */
+
+#define TRM_S1040_SCSI_CONFIG1 0x8E /* SCSI Configuration 1 (R/W) */
+#define ACTIVE_NEGPLUS 0x10 /* Enhance active negation */
+#define FILTER_DISABLE 0x08 /* Disable SCSI data filter */
+#define FAST_FILTER 0x04 /* ? */
+#define ACTIVE_NEG 0x02 /* Enable active negation */
+
+#define TRM_S1040_SCSI_CONFIG2 0x8F /* SCSI Configuration 2 (R/W) */
+#define CFG2_WIDEFIFO 0x02 /* */
+
+#define TRM_S1040_SCSI_COMMAND 0x90 /* SCSI Command (R/W) */
+#define SCMD_COMP 0x12 /* Command complete */
+#define SCMD_SEL_ATN 0x60 /* Selection with ATN */
+#define SCMD_SEL_ATN3 0x64 /* Selection with ATN3 */
+#define SCMD_SEL_ATNSTOP 0xB8 /* Selection with ATN and Stop */
+#define SCMD_FIFO_OUT 0xC0 /* SCSI FIFO transfer out */
+#define SCMD_DMA_OUT 0xC1 /* SCSI DMA transfer out */
+#define SCMD_FIFO_IN 0xC2 /* SCSI FIFO transfer in */
+#define SCMD_DMA_IN 0xC3 /* SCSI DMA transfer in */
+#define SCMD_MSGACCEPT 0xD8 /* Message accept */
+
+/************************************************************************/
+/* */
+/* Code Command Description */
+/* ---- ---------------------------------------- */
+/* 02 Enable reselection with FIFO */
+/* 40 Select without ATN with FIFO */
+/* 60 Select with ATN with FIFO */
+/* 64 Select with ATN3 with FIFO */
+/* A0 Select with ATN and stop with FIFO */
+/* C0 Transfer information out with FIFO */
+/* C1 Transfer information out with DMA */
+/* C2 Transfer information in with FIFO */
+/* C3 Transfer information in with DMA */
+/* 12 Initiator command complete with FIFO */
+/* 50 Initiator transfer information out sequence without ATN */
+/* with FIFO */
+/* 70 Initiator transfer information out sequence with ATN */
+/* with FIFO */
+/* 74 Initiator transfer information out sequence with ATN3 */
+/* with FIFO */
+/* 52 Initiator transfer information in sequence without ATN */
+/* with FIFO */
+/* 72 Initiator transfer information in sequence with ATN */
+/* with FIFO */
+/* 76 Initiator transfer information in sequence with ATN3 */
+/* with FIFO */
+/* 90 Initiator transfer information out command complete */
+/* with FIFO */
+/* 92 Initiator transfer information in command complete */
+/* with FIFO */
+/* D2 Enable selection */
+/* 08 Reselection */
+/* 48 Disconnect command with FIFO */
+/* 88 Terminate command with FIFO */
+/* C8 Target command complete with FIFO */
+/* 18 SCAM Arbitration/ Selection */
+/* 5A Enable reselection */
+/* 98 Select without ATN with FIFO */
+/* B8 Select with ATN with FIFO */
+/* D8 Message Accepted */
+/* 58 NOP */
+/* */
+/************************************************************************/
+
+#define TRM_S1040_SCSI_TIMEOUT 0x91 /* SCSI Time Out Value (R/W) */
+#define TRM_S1040_SCSI_FIFO 0x98 /* SCSI FIFO (R/W) */
+
+#define TRM_S1040_SCSI_TCR0 0x9C /* SCSI Target Control 0 (R/W) */
+#define TCR0_WIDE_NEGO_DONE 0x8000 /* Wide nego done */
+#define TCR0_SYNC_NEGO_DONE 0x4000 /* Synchronous nego done */
+#define TCR0_ENABLE_LVDS 0x2000 /* Enable LVDS synchronous */
+#define TCR0_ENABLE_WIDE 0x1000 /* Enable WIDE synchronous */
+#define TCR0_ENABLE_ALT 0x0800 /* Enable alternate synchronous */
+#define TCR0_PERIOD_MASK 0x0700 /* Transfer rate */
+
+#define TCR0_DO_WIDE_NEGO 0x0080 /* Do wide NEGO */
+#define TCR0_DO_SYNC_NEGO 0x0040 /* Do sync NEGO */
+#define TCR0_DISCONNECT_EN 0x0020 /* Disconnection enable */
+#define TCR0_OFFSET_MASK 0x001F /* Offset number */
+
+#define TRM_S1040_SCSI_TCR1 0x9E /* SCSI Target Control 1 (R/W) */
+#define MAXTAG_MASK 0x7F00 /* Maximum tags (127) */
+#define NON_TAG_BUSY 0x0080 /* Non tag command active */
+#define ACTTAG_MASK 0x007F /* Active tags */
+
+/************************************************************************/
+/* */
+/* The DMA register offset for TRM_S1040 */
+/* */
+/************************************************************************/
+#define TRM_S1040_DMA_COMMAND 0xA0 /* DMA Command (R/W) */
+#define DMACMD_SG 0x02 /* Enable HW S/G support */
+#define DMACMD_DIR 0x01 /* 1 = read from SCSI write to Host */
+#define XFERDATAIN_SG 0x0103 /* Transfer data in w/ SG */
+#define XFERDATAOUT_SG 0x0102 /* Transfer data out w/ SG */
+#define XFERDATAIN 0x0101 /* Transfer data in w/o SG */
+#define XFERDATAOUT 0x0100 /* Transfer data out w/o SG */
+
+#define TRM_S1040_DMA_FIFOCNT 0xA1 /* DMA FIFO Counter (R) */
+
+#define TRM_S1040_DMA_CONTROL 0xA1 /* DMA Control (W) */
+#define DMARESETMODULE 0x10 /* Reset PCI/DMA module */
+#define STOPDMAXFER 0x08 /* Stop DMA transfer */
+#define ABORTXFER 0x04 /* Abort DMA transfer */
+#define CLRXFIFO 0x02 /* Clear DMA transfer FIFO */
+#define STARTDMAXFER 0x01 /* Start DMA transfer */
+
+#define TRM_S1040_DMA_FIFOSTAT 0xA2 /* DMA FIFO Status (R) */
+
+#define TRM_S1040_DMA_STATUS 0xA3 /* DMA Interrupt Status (R/W) */
+#define XFERPENDING 0x80 /* Transfer pending */
+#define SCSIBUSY 0x40 /* SCSI busy */
+#define GLOBALINT 0x20 /* DMA_INTEN bit 0-4 set */
+#define FORCEDMACOMP 0x10 /* Force DMA transfer complete */
+#define DMAXFERERROR 0x08 /* DMA transfer error */
+#define DMAXFERABORT 0x04 /* DMA transfer abort */
+#define DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */
+#define SCSICOMP 0x01 /* SCSI complete interrupt */
+
+#define TRM_S1040_DMA_INTEN 0xA4 /* DMA Interrupt Enable (R/W) */
+#define EN_FORCEDMACOMP 0x10 /* Force DMA transfer complete */
+#define EN_DMAXFERERROR 0x08 /* DMA transfer error */
+#define EN_DMAXFERABORT 0x04 /* DMA transfer abort */
+#define EN_DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */
+#define EN_SCSIINTR 0x01 /* Enable SCSI complete interrupt */
+
+#define TRM_S1040_DMA_CONFIG 0xA6 /* DMA Configuration (R/W) */
+#define DMA_ENHANCE 0x8000 /* Enable DMA enhance feature (SG?) */
+#define DMA_PCI_DUAL_ADDR 0x4000 /* */
+#define DMA_CFG_RES 0x2000 /* Always 1 */
+#define DMA_AUTO_CLR_FIFO 0x1000 /* DISable DMA auto clear FIFO */
+#define DMA_MEM_MULTI_READ 0x0800 /* */
+#define DMA_MEM_WRITE_INVAL 0x0400 /* Memory write and invalidate */
+#define DMA_FIFO_CTRL 0x0300 /* Control FIFO operation with DMA */
+#define DMA_FIFO_HALF_HALF 0x0200 /* Keep half filled on both read/write */
+
+#define TRM_S1040_DMA_XCNT 0xA8 /* DMA Transfer Counter (R/W), 24bits */
+#define TRM_S1040_DMA_CXCNT 0xAC /* DMA Current Transfer Counter (R) */
+#define TRM_S1040_DMA_XLOWADDR 0xB0 /* DMA Transfer Physical Low Address */
+#define TRM_S1040_DMA_XHIGHADDR 0xB4 /* DMA Transfer Physical High Address */
+
+/************************************************************************/
+/* */
+/* The general register offset for TRM_S1040 */
+/* */
+/************************************************************************/
+#define TRM_S1040_GEN_CONTROL 0xD4 /* Global Control */
+#define CTRL_LED 0x80 /* Control onboard LED */
+#define EN_EEPROM 0x10 /* Enable EEPROM programming */
+#define DIS_TERM 0x08 /* Disable onboard termination */
+#define AUTOTERM 0x04 /* Enable Auto SCSI terminator */
+#define LOW8TERM 0x02 /* Enable Lower 8 bit SCSI terminator */
+#define UP8TERM 0x01 /* Enable Upper 8 bit SCSI terminator */
+
+#define TRM_S1040_GEN_STATUS 0xD5 /* Global Status */
+#define GTIMEOUT 0x80 /* Global timer reach 0 */
+#define EXT68HIGH 0x40 /* Higher 8 bit connected externally */
+#define INT68HIGH 0x20 /* Higher 8 bit connected internally */
+#define CON5068 0x10 /* External 50/68 pin connected (low) */
+#define CON68 0x08 /* Internal 68 pin connected (low) */
+#define CON50 0x04 /* Internal 50 pin connected (low!) */
+#define WIDESCSI 0x02 /* Wide SCSI card */
+#define STATUS_LOAD_DEFAULT 0x01 /* */
+
+#define TRM_S1040_GEN_NVRAM 0xD6 /* Serial NON-VOLATILE RAM port */
+#define NVR_BITOUT 0x08 /* Serial data out */
+#define NVR_BITIN 0x04 /* Serial data in */
+#define NVR_CLOCK 0x02 /* Serial clock */
+#define NVR_SELECT 0x01 /* Serial select */
+
+#define TRM_S1040_GEN_EDATA 0xD7 /* Parallel EEPROM data port */
+#define TRM_S1040_GEN_EADDRESS 0xD8 /* Parallel EEPROM address */
+#define TRM_S1040_GEN_TIMER 0xDB /* Global timer */
+
+/************************************************************************/
+/* */
+/* NvmTarCfg0: Target configuration byte 0 :..pDCB->DevMode */
+/* */
+/************************************************************************/
+#define NTC_DO_WIDE_NEGO 0x20 /* Wide negotiate */
+#define NTC_DO_TAG_QUEUEING 0x10 /* Enable SCSI tag queuing */
+#define NTC_DO_SEND_START 0x08 /* Send start command SPINUP */
+#define NTC_DO_DISCONNECT 0x04 /* Enable SCSI disconnect */
+#define NTC_DO_SYNC_NEGO 0x02 /* Sync negotiation */
+#define NTC_DO_PARITY_CHK 0x01 /* (it sould define at NAC) */
+ /* Parity check enable */
+
+/************************************************************************/
+/* */
+/* Nvram Initiater bits definition */
+/* */
+/************************************************************************/
+#if 0
+#define MORE2_DRV BIT0
+#define GREATER_1G BIT1
+#define RST_SCSI_BUS BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK BIT4
+#define LUN_CHECK BIT5
+#endif
+
+/************************************************************************/
+/* */
+/* Nvram Adapter Cfg bits definition */
+/* */
+/************************************************************************/
+#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */
+#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */
+#define NAC_GREATER_1G 0x02 /* > 1G support enable */
+#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */
+/* #define NAC_DO_PARITY_CHK 0x08 */ /* Parity check enable */
+
+#endif
diff --git a/drivers/scsi/dec_esp.c b/drivers/scsi/dec_esp.c
new file mode 100644
index 000000000000..315f95a0d6c0
--- /dev/null
+++ b/drivers/scsi/dec_esp.c
@@ -0,0 +1,573 @@
+/*
+ * dec_esp.c: Driver for SCSI chips on IOASIC based TURBOchannel DECstations
+ * and TURBOchannel PMAZ-A cards
+ *
+ * TURBOchannel changes by Harald Koerfgen
+ * PMAZ-A support by David Airlie
+ *
+ * based on jazz_esp.c:
+ * Copyright (C) 1997 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * jazz_esp is based on David S. Miller's ESP driver and cyber_esp
+ *
+ * 20000819 - Small PMAZ-AA fixes by Florian Lohoff <flo@rfc822.org>
+ * Be warned the PMAZ-AA works currently as a single card.
+ * Dont try to put multiple cards in one machine - They are
+ * both detected but it may crash under high load garbling your
+ * data.
+ * 20001005 - Initialization fixes for 2.4.0-test9
+ * Florian Lohoff <flo@rfc822.org>
+ *
+ * Copyright (C) 2002, 2003 Maciej W. Rozycki
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/ioasic_ints.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/tc.h>
+
+#define DEC_SCSI_SREG 0
+#define DEC_SCSI_DMAREG 0x40000
+#define DEC_SCSI_SRAM 0x80000
+#define DEC_SCSI_DIAG 0xC0000
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static void dma_drain(struct NCR_ESP *esp);
+static int dma_can_transfer(struct NCR_ESP *esp, struct scsi_cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, u32 vaddress, int length);
+static void dma_init_write(struct NCR_ESP *esp, u32 vaddress, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, u32 addr, int count, int write);
+static void dma_mmu_get_scsi_one(struct NCR_ESP *esp, struct scsi_cmnd * sp);
+static void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, struct scsi_cmnd * sp);
+static void dma_advance_sg(struct scsi_cmnd * sp);
+
+static void pmaz_dma_drain(struct NCR_ESP *esp);
+static void pmaz_dma_init_read(struct NCR_ESP *esp, u32 vaddress, int length);
+static void pmaz_dma_init_write(struct NCR_ESP *esp, u32 vaddress, int length);
+static void pmaz_dma_ints_off(struct NCR_ESP *esp);
+static void pmaz_dma_ints_on(struct NCR_ESP *esp);
+static void pmaz_dma_setup(struct NCR_ESP *esp, u32 addr, int count, int write);
+static void pmaz_dma_mmu_get_scsi_one(struct NCR_ESP *esp, struct scsi_cmnd * sp);
+
+#define TC_ESP_RAM_SIZE 0x20000
+#define ESP_TGT_DMA_SIZE ((TC_ESP_RAM_SIZE/7) & ~(sizeof(int)-1))
+#define ESP_NCMD 7
+
+#define TC_ESP_DMAR_MASK 0x1ffff
+#define TC_ESP_DMAR_WRITE 0x80000000
+#define TC_ESP_DMA_ADDR(x) ((unsigned)(x) & TC_ESP_DMAR_MASK)
+
+u32 esp_virt_buffer;
+int scsi_current_length;
+
+volatile unsigned char cmd_buffer[16];
+volatile unsigned char pmaz_cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are trasfered to the ESP chip
+ * via PIO.
+ */
+
+static irqreturn_t scsi_dma_merr_int(int, void *, struct pt_regs *);
+static irqreturn_t scsi_dma_err_int(int, void *, struct pt_regs *);
+static irqreturn_t scsi_dma_int(int, void *, struct pt_regs *);
+
+static int dec_esp_detect(struct scsi_host_template * tpnt);
+
+static int dec_esp_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "dec_esp",
+ .proc_info = esp_proc_info,
+ .name = "NCR53C94",
+ .detect = dec_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = dec_esp_release,
+ .info = esp_info,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+
+#include "scsi_module.c"
+
+/***************************************************************** Detection */
+static int dec_esp_detect(Scsi_Host_Template * tpnt)
+{
+ struct NCR_ESP *esp;
+ struct ConfigDev *esp_dev;
+ int slot;
+ unsigned long mem_start;
+
+ if (IOASIC) {
+ esp_dev = 0;
+ esp = esp_allocate(tpnt, (void *) esp_dev);
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = &dma_drain;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+ esp->dma_led_off = 0;
+ esp->dma_led_on = 0;
+
+ /* virtual DMA functions */
+ esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
+ esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
+ esp->dma_mmu_release_scsi_one = 0;
+ esp->dma_mmu_release_scsi_sgl = 0;
+ esp->dma_advance_sg = &dma_advance_sg;
+
+
+ /* SCSI chip speed */
+ esp->cfreq = 25000000;
+
+ esp->dregs = 0;
+
+ /* ESP register base */
+ esp->eregs = (struct ESP_regs *) (system_base + IOASIC_SCSI);
+
+ /* Set the command buffer */
+ esp->esp_command = (volatile unsigned char *) cmd_buffer;
+
+ /* get virtual dma address for command buffer */
+ esp->esp_command_dvma = virt_to_phys(cmd_buffer);
+
+ esp->irq = dec_interrupt[DEC_IRQ_ASC];
+
+ esp->scsi_id = 7;
+
+ /* Check for differential SCSI-bus */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ if (request_irq(esp->irq, esp_intr, SA_INTERRUPT,
+ "ncr53c94", esp->ehost))
+ goto err_dealloc;
+ if (request_irq(dec_interrupt[DEC_IRQ_ASC_MERR],
+ scsi_dma_merr_int, SA_INTERRUPT,
+ "ncr53c94 error", esp->ehost))
+ goto err_free_irq;
+ if (request_irq(dec_interrupt[DEC_IRQ_ASC_ERR],
+ scsi_dma_err_int, SA_INTERRUPT,
+ "ncr53c94 overrun", esp->ehost))
+ goto err_free_irq_merr;
+ if (request_irq(dec_interrupt[DEC_IRQ_ASC_DMA],
+ scsi_dma_int, SA_INTERRUPT,
+ "ncr53c94 dma", esp->ehost))
+ goto err_free_irq_err;
+
+ }
+
+ if (TURBOCHANNEL) {
+ while ((slot = search_tc_card("PMAZ-AA")) >= 0) {
+ claim_tc_card(slot);
+
+ esp_dev = 0;
+ esp = esp_allocate(tpnt, (void *) esp_dev);
+
+ mem_start = get_tc_base_addr(slot);
+
+ /* Store base addr into esp struct */
+ esp->slot = PHYSADDR(mem_start);
+
+ esp->dregs = 0;
+ esp->eregs = (struct ESP_regs *) (mem_start + DEC_SCSI_SREG);
+ esp->do_pio_cmds = 1;
+
+ /* Set the command buffer */
+ esp->esp_command = (volatile unsigned char *) pmaz_cmd_buffer;
+
+ /* get virtual dma address for command buffer */
+ esp->esp_command_dvma = virt_to_phys(pmaz_cmd_buffer);
+
+ esp->cfreq = get_tc_speed();
+
+ esp->irq = get_tc_irq_nr(slot);
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &pmaz_dma_init_read;
+ esp->dma_init_write = &pmaz_dma_init_write;
+ esp->dma_ints_off = &pmaz_dma_ints_off;
+ esp->dma_ints_on = &pmaz_dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &pmaz_dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = &pmaz_dma_drain;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+ esp->dma_led_off = 0;
+ esp->dma_led_on = 0;
+
+ esp->dma_mmu_get_scsi_one = pmaz_dma_mmu_get_scsi_one;
+ esp->dma_mmu_get_scsi_sgl = 0;
+ esp->dma_mmu_release_scsi_one = 0;
+ esp->dma_mmu_release_scsi_sgl = 0;
+ esp->dma_advance_sg = 0;
+
+ if (request_irq(esp->irq, esp_intr, SA_INTERRUPT,
+ "PMAZ_AA", esp->ehost)) {
+ esp_deallocate(esp);
+ release_tc_card(slot);
+ continue;
+ }
+ esp->scsi_id = 7;
+ esp->diff = 0;
+ esp_initialize(esp);
+ }
+ }
+
+ if(nesps) {
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ return 0;
+
+err_free_irq_err:
+ free_irq(dec_interrupt[DEC_IRQ_ASC_ERR], scsi_dma_err_int);
+err_free_irq_merr:
+ free_irq(dec_interrupt[DEC_IRQ_ASC_MERR], scsi_dma_merr_int);
+err_free_irq:
+ free_irq(esp->irq, esp_intr);
+err_dealloc:
+ esp_deallocate(esp);
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static irqreturn_t scsi_dma_merr_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk("Got unexpected SCSI DMA Interrupt! < ");
+ printk("SCSI_DMA_MEMRDERR ");
+ printk(">\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t scsi_dma_err_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* empty */
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t scsi_dma_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 scsi_next_ptr;
+
+ scsi_next_ptr = ioasic_read(IO_REG_SCSI_DMA_P);
+
+ /* next page */
+ scsi_next_ptr = (((scsi_next_ptr >> 3) + PAGE_SIZE) & PAGE_MASK) << 3;
+ ioasic_write(IO_REG_SCSI_DMA_BP, scsi_next_ptr);
+ fast_iob();
+
+ return IRQ_HANDLED;
+}
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ return fifo_count;
+}
+
+static void dma_drain(struct NCR_ESP *esp)
+{
+ u32 nw, data0, data1, scsi_data_ptr;
+ u16 *p;
+
+ nw = ioasic_read(IO_REG_SCSI_SCR);
+
+ /*
+ * Is there something in the dma buffers left?
+ */
+ if (nw) {
+ scsi_data_ptr = ioasic_read(IO_REG_SCSI_DMA_P) >> 3;
+ p = phys_to_virt(scsi_data_ptr);
+ switch (nw) {
+ case 1:
+ data0 = ioasic_read(IO_REG_SCSI_SDR0);
+ p[0] = data0 & 0xffff;
+ break;
+ case 2:
+ data0 = ioasic_read(IO_REG_SCSI_SDR0);
+ p[0] = data0 & 0xffff;
+ p[1] = (data0 >> 16) & 0xffff;
+ break;
+ case 3:
+ data0 = ioasic_read(IO_REG_SCSI_SDR0);
+ data1 = ioasic_read(IO_REG_SCSI_SDR1);
+ p[0] = data0 & 0xffff;
+ p[1] = (data0 >> 16) & 0xffff;
+ p[2] = data1 & 0xffff;
+ break;
+ default:
+ printk("Strange: %d words in dma buffer left\n", nw);
+ break;
+ }
+ }
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, struct scsi_cmnd * sp)
+{
+ return sp->SCp.this_residual;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+}
+
+static void dma_init_read(struct NCR_ESP *esp, u32 vaddress, int length)
+{
+ u32 scsi_next_ptr, ioasic_ssr;
+ unsigned long flags;
+
+ if (vaddress & 3)
+ panic("dec_esp.c: unable to handle partial word transfers, yet...");
+
+ dma_cache_wback_inv((unsigned long) phys_to_virt(vaddress), length);
+
+ spin_lock_irqsave(&ioasic_ssr_lock, flags);
+
+ fast_mb();
+ ioasic_ssr = ioasic_read(IO_REG_SSR);
+
+ ioasic_ssr &= ~IO_SSR_SCSI_DMA_EN;
+ ioasic_write(IO_REG_SSR, ioasic_ssr);
+
+ fast_wmb();
+ ioasic_write(IO_REG_SCSI_SCR, 0);
+ ioasic_write(IO_REG_SCSI_DMA_P, vaddress << 3);
+
+ /* prepare for next page */
+ scsi_next_ptr = ((vaddress + PAGE_SIZE) & PAGE_MASK) << 3;
+ ioasic_write(IO_REG_SCSI_DMA_BP, scsi_next_ptr);
+
+ ioasic_ssr |= (IO_SSR_SCSI_DMA_DIR | IO_SSR_SCSI_DMA_EN);
+ fast_wmb();
+ ioasic_write(IO_REG_SSR, ioasic_ssr);
+
+ fast_iob();
+ spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
+}
+
+static void dma_init_write(struct NCR_ESP *esp, u32 vaddress, int length)
+{
+ u32 scsi_next_ptr, ioasic_ssr;
+ unsigned long flags;
+
+ if (vaddress & 3)
+ panic("dec_esp.c: unable to handle partial word transfers, yet...");
+
+ dma_cache_wback_inv((unsigned long) phys_to_virt(vaddress), length);
+
+ spin_lock_irqsave(&ioasic_ssr_lock, flags);
+
+ fast_mb();
+ ioasic_ssr = ioasic_read(IO_REG_SSR);
+
+ ioasic_ssr &= ~(IO_SSR_SCSI_DMA_DIR | IO_SSR_SCSI_DMA_EN);
+ ioasic_write(IO_REG_SSR, ioasic_ssr);
+
+ fast_wmb();
+ ioasic_write(IO_REG_SCSI_SCR, 0);
+ ioasic_write(IO_REG_SCSI_DMA_P, vaddress << 3);
+
+ /* prepare for next page */
+ scsi_next_ptr = ((vaddress + PAGE_SIZE) & PAGE_MASK) << 3;
+ ioasic_write(IO_REG_SCSI_DMA_BP, scsi_next_ptr);
+
+ ioasic_ssr |= IO_SSR_SCSI_DMA_EN;
+ fast_wmb();
+ ioasic_write(IO_REG_SSR, ioasic_ssr);
+
+ fast_iob();
+ spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(dec_interrupt[DEC_IRQ_ASC_DMA]);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(dec_interrupt[DEC_IRQ_ASC_DMA]);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ return (esp->eregs->esp_status & ESP_STAT_INTR);
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ /*
+ * FIXME: what's this good for?
+ */
+ return 1;
+}
+
+static void dma_setup(struct NCR_ESP *esp, u32 addr, int count, int write)
+{
+ /*
+ * DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if (write)
+ dma_init_read(esp, addr, count);
+ else
+ dma_init_write(esp, addr, count);
+}
+
+static void dma_mmu_get_scsi_one(struct NCR_ESP *esp, struct scsi_cmnd * sp)
+{
+ sp->SCp.ptr = (char *)virt_to_phys(sp->request_buffer);
+}
+
+static void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, struct scsi_cmnd * sp)
+{
+ int sz = sp->SCp.buffers_residual;
+ struct scatterlist *sg = sp->SCp.buffer;
+
+ while (sz >= 0) {
+ sg[sz].dma_address = page_to_phys(sg[sz].page) + sg[sz].offset;
+ sz--;
+ }
+ sp->SCp.ptr = (char *)(sp->SCp.buffer->dma_address);
+}
+
+static void dma_advance_sg(struct scsi_cmnd * sp)
+{
+ sp->SCp.ptr = (char *)(sp->SCp.buffer->dma_address);
+}
+
+static void pmaz_dma_drain(struct NCR_ESP *esp)
+{
+ memcpy(phys_to_virt(esp_virt_buffer),
+ (void *)KSEG1ADDR(esp->slot + DEC_SCSI_SRAM + ESP_TGT_DMA_SIZE),
+ scsi_current_length);
+}
+
+static void pmaz_dma_init_read(struct NCR_ESP *esp, u32 vaddress, int length)
+{
+ volatile u32 *dmareg =
+ (volatile u32 *)KSEG1ADDR(esp->slot + DEC_SCSI_DMAREG);
+
+ if (length > ESP_TGT_DMA_SIZE)
+ length = ESP_TGT_DMA_SIZE;
+
+ *dmareg = TC_ESP_DMA_ADDR(ESP_TGT_DMA_SIZE);
+
+ iob();
+
+ esp_virt_buffer = vaddress;
+ scsi_current_length = length;
+}
+
+static void pmaz_dma_init_write(struct NCR_ESP *esp, u32 vaddress, int length)
+{
+ volatile u32 *dmareg =
+ (volatile u32 *)KSEG1ADDR(esp->slot + DEC_SCSI_DMAREG);
+
+ memcpy((void *)KSEG1ADDR(esp->slot + DEC_SCSI_SRAM + ESP_TGT_DMA_SIZE),
+ phys_to_virt(vaddress), length);
+
+ wmb();
+ *dmareg = TC_ESP_DMAR_WRITE | TC_ESP_DMA_ADDR(ESP_TGT_DMA_SIZE);
+
+ iob();
+}
+
+static void pmaz_dma_ints_off(struct NCR_ESP *esp)
+{
+}
+
+static void pmaz_dma_ints_on(struct NCR_ESP *esp)
+{
+}
+
+static void pmaz_dma_setup(struct NCR_ESP *esp, u32 addr, int count, int write)
+{
+ /*
+ * DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if (write)
+ pmaz_dma_init_read(esp, addr, count);
+ else
+ pmaz_dma_init_write(esp, addr, count);
+}
+
+static void pmaz_dma_mmu_get_scsi_one(struct NCR_ESP *esp, struct scsi_cmnd * sp)
+{
+ sp->SCp.ptr = (char *)virt_to_phys(sp->request_buffer);
+}
diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c
new file mode 100644
index 000000000000..1d2242403db8
--- /dev/null
+++ b/drivers/scsi/dmx3191d.c
@@ -0,0 +1,173 @@
+/*
+ dmx3191d.c - driver for the Domex DMX3191D SCSI card.
+ Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
+ Portions Copyright (C) 2004 by Christoph Hellwig <hch@lst.de>
+
+ Based on the generic NCR5380 driver by Drew Eckhardt et al.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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 <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include <scsi/scsi_host.h>
+
+/*
+ * Defintions for the generic 5380 driver.
+ */
+#define AUTOSENSE
+
+#define NCR5380_read(reg) inb(port + reg)
+#define NCR5380_write(reg, value) outb(value, port + reg)
+
+#define NCR5380_implementation_fields unsigned int port
+#define NCR5380_local_declare() NCR5380_implementation_fields
+#define NCR5380_setup(instance) port = instance->io_port
+
+/*
+ * Includes needed for NCR5380.[ch] (XXX: Move them to NCR5380.h)
+ */
+#include <linux/delay.h>
+#include "scsi.h"
+
+#include "NCR5380.h"
+#include "NCR5380.c"
+
+#define DMX3191D_DRIVER_NAME "dmx3191d"
+#define DMX3191D_REGION_LEN 8
+
+
+static struct scsi_host_template dmx3191d_driver_template = {
+ .proc_name = DMX3191D_DRIVER_NAME,
+ .name = "Domex DMX3191D",
+ .queuecommand = NCR5380_queue_command,
+ .eh_abort_handler = NCR5380_abort,
+ .eh_bus_reset_handler = NCR5380_bus_reset,
+ .eh_device_reset_handler= NCR5380_device_reset,
+ .eh_host_reset_handler = NCR5380_host_reset,
+ .can_queue = 32,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+static int __devinit dmx3191d_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct Scsi_Host *shost;
+ unsigned long io;
+ int error = -ENODEV;
+
+ if (pci_enable_device(pdev))
+ goto out;
+
+ io = pci_resource_start(pdev, 0);
+ if (!request_region(io, DMX3191D_REGION_LEN, DMX3191D_DRIVER_NAME)) {
+ printk(KERN_ERR "dmx3191: region 0x%lx-0x%lx already reserved\n",
+ io, io + DMX3191D_REGION_LEN);
+ goto out_disable_device;
+ }
+
+ shost = scsi_host_alloc(&dmx3191d_driver_template,
+ sizeof(struct NCR5380_hostdata));
+ if (!shost)
+ goto out_release_region;
+ shost->io_port = io;
+ shost->irq = pdev->irq;
+
+ NCR5380_init(shost, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E);
+
+ if (request_irq(pdev->irq, NCR5380_intr, SA_SHIRQ,
+ DMX3191D_DRIVER_NAME, shost)) {
+ /*
+ * Steam powered scsi controllers run without an IRQ anyway
+ */
+ printk(KERN_WARNING "dmx3191: IRQ %d not available - "
+ "switching to polled mode.\n", pdev->irq);
+ shost->irq = SCSI_IRQ_NONE;
+ }
+
+ pci_set_drvdata(pdev, shost);
+
+ error = scsi_add_host(shost, &pdev->dev);
+ if (error)
+ goto out_free_irq;
+
+ scsi_scan_host(shost);
+ return 0;
+
+ out_free_irq:
+ free_irq(shost->irq, shost);
+ out_release_region:
+ release_region(shost->io_port, DMX3191D_REGION_LEN);
+ out_disable_device:
+ pci_disable_device(pdev);
+ out:
+ return error;
+}
+
+static void __devexit dmx3191d_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+
+ scsi_remove_host(shost);
+
+ NCR5380_exit(shost);
+
+ if (shost->irq != SCSI_IRQ_NONE)
+ free_irq(shost->irq, shost);
+ release_region(shost->io_port, DMX3191D_REGION_LEN);
+ pci_disable_device(pdev);
+
+ scsi_host_put(shost);
+}
+
+static struct pci_device_id dmx3191d_pci_tbl[] = {
+ {PCI_VENDOR_ID_DOMEX, PCI_DEVICE_ID_DOMEX_DMX3191D,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+ { }
+};
+MODULE_DEVICE_TABLE(pci, dmx3191d_pci_tbl);
+
+static struct pci_driver dmx3191d_pci_driver = {
+ .name = DMX3191D_DRIVER_NAME,
+ .id_table = dmx3191d_pci_tbl,
+ .probe = dmx3191d_probe_one,
+ .remove = __devexit_p(dmx3191d_remove_one),
+};
+
+static int __init dmx3191d_init(void)
+{
+ return pci_module_init(&dmx3191d_pci_driver);
+}
+
+static void __exit dmx3191d_exit(void)
+{
+ pci_unregister_driver(&dmx3191d_pci_driver);
+}
+
+module_init(dmx3191d_init);
+module_exit(dmx3191d_exit);
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("Domex DMX3191D SCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dpt/dpti_i2o.h b/drivers/scsi/dpt/dpti_i2o.h
new file mode 100644
index 000000000000..a9585f5235d9
--- /dev/null
+++ b/drivers/scsi/dpt/dpti_i2o.h
@@ -0,0 +1,459 @@
+#ifndef _SCSI_I2O_H
+#define _SCSI_I2O_H
+
+/* I2O kernel space accessible structures/APIs
+ *
+ * (c) Copyright 1999, 2000 Red Hat Software
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *************************************************************************
+ *
+ * This header file defined the I2O APIs/structures for use by
+ * the I2O kernel modules.
+ *
+ */
+
+#ifdef __KERNEL__ /* This file to be included by kernel only */
+
+#include <linux/i2o-dev.h>
+
+#include <asm/semaphore.h> /* Needed for MUTEX init macros */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/notifier.h>
+#include <asm/atomic.h>
+
+
+/*
+ * Tunable parameters first
+ */
+
+/* How many different OSM's are we allowing */
+#define MAX_I2O_MODULES 64
+
+#define I2O_EVT_CAPABILITY_OTHER 0x01
+#define I2O_EVT_CAPABILITY_CHANGED 0x02
+
+#define I2O_EVT_SENSOR_STATE_CHANGED 0x01
+
+//#ifdef __KERNEL__ /* ioctl stuff only thing exported to users */
+
+#define I2O_MAX_MANAGERS 4
+
+/*
+ * I2O Interface Objects
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+
+#define DECLARE_MUTEX(name) struct semaphore name=MUTEX
+
+typedef struct wait_queue *adpt_wait_queue_head_t;
+#define ADPT_DECLARE_WAIT_QUEUE_HEAD(wait) adpt_wait_queue_head_t wait = NULL
+typedef struct wait_queue adpt_wait_queue_t;
+#else
+
+#include <linux/wait.h>
+typedef wait_queue_head_t adpt_wait_queue_head_t;
+#define ADPT_DECLARE_WAIT_QUEUE_HEAD(wait) DECLARE_WAIT_QUEUE_HEAD(wait)
+typedef wait_queue_t adpt_wait_queue_t;
+
+#endif
+/*
+ * message structures
+ */
+
+struct i2o_message
+{
+ u8 version_offset;
+ u8 flags;
+ u16 size;
+ u32 target_tid:12;
+ u32 init_tid:12;
+ u32 function:8;
+ u32 initiator_context;
+ /* List follows */
+};
+
+struct adpt_device;
+struct _adpt_hba;
+struct i2o_device
+{
+ struct i2o_device *next; /* Chain */
+ struct i2o_device *prev;
+
+ char dev_name[8]; /* linux /dev name if available */
+ i2o_lct_entry lct_data;/* Device LCT information */
+ u32 flags;
+ struct proc_dir_entry* proc_entry; /* /proc dir */
+ struct adpt_device *owner;
+ struct _adpt_hba *controller; /* Controlling IOP */
+};
+
+/*
+ * Each I2O controller has one of these objects
+ */
+
+struct i2o_controller
+{
+ char name[16];
+ int unit;
+ int type;
+ int enabled;
+
+ struct notifier_block *event_notifer; /* Events */
+ atomic_t users;
+ struct i2o_device *devices; /* I2O device chain */
+ struct i2o_controller *next; /* Controller chain */
+
+};
+
+/*
+ * I2O System table entry
+ */
+struct i2o_sys_tbl_entry
+{
+ u16 org_id;
+ u16 reserved1;
+ u32 iop_id:12;
+ u32 reserved2:20;
+ u16 seg_num:12;
+ u16 i2o_version:4;
+ u8 iop_state;
+ u8 msg_type;
+ u16 frame_size;
+ u16 reserved3;
+ u32 last_changed;
+ u32 iop_capabilities;
+ u32 inbound_low;
+ u32 inbound_high;
+};
+
+struct i2o_sys_tbl
+{
+ u8 num_entries;
+ u8 version;
+ u16 reserved1;
+ u32 change_ind;
+ u32 reserved2;
+ u32 reserved3;
+ struct i2o_sys_tbl_entry iops[0];
+};
+
+/*
+ * I2O classes / subclasses
+ */
+
+/* Class ID and Code Assignments
+ * (LCT.ClassID.Version field)
+ */
+#define I2O_CLASS_VERSION_10 0x00
+#define I2O_CLASS_VERSION_11 0x01
+
+/* Class code names
+ * (from v1.5 Table 6-1 Class Code Assignments.)
+ */
+
+#define I2O_CLASS_EXECUTIVE 0x000
+#define I2O_CLASS_DDM 0x001
+#define I2O_CLASS_RANDOM_BLOCK_STORAGE 0x010
+#define I2O_CLASS_SEQUENTIAL_STORAGE 0x011
+#define I2O_CLASS_LAN 0x020
+#define I2O_CLASS_WAN 0x030
+#define I2O_CLASS_FIBRE_CHANNEL_PORT 0x040
+#define I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL 0x041
+#define I2O_CLASS_SCSI_PERIPHERAL 0x051
+#define I2O_CLASS_ATE_PORT 0x060
+#define I2O_CLASS_ATE_PERIPHERAL 0x061
+#define I2O_CLASS_FLOPPY_CONTROLLER 0x070
+#define I2O_CLASS_FLOPPY_DEVICE 0x071
+#define I2O_CLASS_BUS_ADAPTER_PORT 0x080
+#define I2O_CLASS_PEER_TRANSPORT_AGENT 0x090
+#define I2O_CLASS_PEER_TRANSPORT 0x091
+
+/* Rest of 0x092 - 0x09f reserved for peer-to-peer classes
+ */
+
+#define I2O_CLASS_MATCH_ANYCLASS 0xffffffff
+
+/* Subclasses
+ */
+
+#define I2O_SUBCLASS_i960 0x001
+#define I2O_SUBCLASS_HDM 0x020
+#define I2O_SUBCLASS_ISM 0x021
+
+/* Operation functions */
+
+#define I2O_PARAMS_FIELD_GET 0x0001
+#define I2O_PARAMS_LIST_GET 0x0002
+#define I2O_PARAMS_MORE_GET 0x0003
+#define I2O_PARAMS_SIZE_GET 0x0004
+#define I2O_PARAMS_TABLE_GET 0x0005
+#define I2O_PARAMS_FIELD_SET 0x0006
+#define I2O_PARAMS_LIST_SET 0x0007
+#define I2O_PARAMS_ROW_ADD 0x0008
+#define I2O_PARAMS_ROW_DELETE 0x0009
+#define I2O_PARAMS_TABLE_CLEAR 0x000A
+
+/*
+ * I2O serial number conventions / formats
+ * (circa v1.5)
+ */
+
+#define I2O_SNFORMAT_UNKNOWN 0
+#define I2O_SNFORMAT_BINARY 1
+#define I2O_SNFORMAT_ASCII 2
+#define I2O_SNFORMAT_UNICODE 3
+#define I2O_SNFORMAT_LAN48_MAC 4
+#define I2O_SNFORMAT_WAN 5
+
+/* Plus new in v2.0 (Yellowstone pdf doc)
+ */
+
+#define I2O_SNFORMAT_LAN64_MAC 6
+#define I2O_SNFORMAT_DDM 7
+#define I2O_SNFORMAT_IEEE_REG64 8
+#define I2O_SNFORMAT_IEEE_REG128 9
+#define I2O_SNFORMAT_UNKNOWN2 0xff
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define TRL_SINGLE_FIXED_LENGTH 0x00
+#define TRL_SINGLE_VARIABLE_LENGTH 0x40
+#define TRL_MULTIPLE_FIXED_LENGTH 0x80
+
+/*
+ * Messaging API values
+ */
+
+#define I2O_CMD_ADAPTER_ASSIGN 0xB3
+#define I2O_CMD_ADAPTER_READ 0xB2
+#define I2O_CMD_ADAPTER_RELEASE 0xB5
+#define I2O_CMD_BIOS_INFO_SET 0xA5
+#define I2O_CMD_BOOT_DEVICE_SET 0xA7
+#define I2O_CMD_CONFIG_VALIDATE 0xBB
+#define I2O_CMD_CONN_SETUP 0xCA
+#define I2O_CMD_DDM_DESTROY 0xB1
+#define I2O_CMD_DDM_ENABLE 0xD5
+#define I2O_CMD_DDM_QUIESCE 0xC7
+#define I2O_CMD_DDM_RESET 0xD9
+#define I2O_CMD_DDM_SUSPEND 0xAF
+#define I2O_CMD_DEVICE_ASSIGN 0xB7
+#define I2O_CMD_DEVICE_RELEASE 0xB9
+#define I2O_CMD_HRT_GET 0xA8
+#define I2O_CMD_ADAPTER_CLEAR 0xBE
+#define I2O_CMD_ADAPTER_CONNECT 0xC9
+#define I2O_CMD_ADAPTER_RESET 0xBD
+#define I2O_CMD_LCT_NOTIFY 0xA2
+#define I2O_CMD_OUTBOUND_INIT 0xA1
+#define I2O_CMD_PATH_ENABLE 0xD3
+#define I2O_CMD_PATH_QUIESCE 0xC5
+#define I2O_CMD_PATH_RESET 0xD7
+#define I2O_CMD_STATIC_MF_CREATE 0xDD
+#define I2O_CMD_STATIC_MF_RELEASE 0xDF
+#define I2O_CMD_STATUS_GET 0xA0
+#define I2O_CMD_SW_DOWNLOAD 0xA9
+#define I2O_CMD_SW_UPLOAD 0xAB
+#define I2O_CMD_SW_REMOVE 0xAD
+#define I2O_CMD_SYS_ENABLE 0xD1
+#define I2O_CMD_SYS_MODIFY 0xC1
+#define I2O_CMD_SYS_QUIESCE 0xC3
+#define I2O_CMD_SYS_TAB_SET 0xA3
+
+#define I2O_CMD_UTIL_NOP 0x00
+#define I2O_CMD_UTIL_ABORT 0x01
+#define I2O_CMD_UTIL_CLAIM 0x09
+#define I2O_CMD_UTIL_RELEASE 0x0B
+#define I2O_CMD_UTIL_PARAMS_GET 0x06
+#define I2O_CMD_UTIL_PARAMS_SET 0x05
+#define I2O_CMD_UTIL_EVT_REGISTER 0x13
+#define I2O_CMD_UTIL_EVT_ACK 0x14
+#define I2O_CMD_UTIL_CONFIG_DIALOG 0x10
+#define I2O_CMD_UTIL_DEVICE_RESERVE 0x0D
+#define I2O_CMD_UTIL_DEVICE_RELEASE 0x0F
+#define I2O_CMD_UTIL_LOCK 0x17
+#define I2O_CMD_UTIL_LOCK_RELEASE 0x19
+#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY 0x15
+
+#define I2O_CMD_SCSI_EXEC 0x81
+#define I2O_CMD_SCSI_ABORT 0x83
+#define I2O_CMD_SCSI_BUSRESET 0x27
+
+#define I2O_CMD_BLOCK_READ 0x30
+#define I2O_CMD_BLOCK_WRITE 0x31
+#define I2O_CMD_BLOCK_CFLUSH 0x37
+#define I2O_CMD_BLOCK_MLOCK 0x49
+#define I2O_CMD_BLOCK_MUNLOCK 0x4B
+#define I2O_CMD_BLOCK_MMOUNT 0x41
+#define I2O_CMD_BLOCK_MEJECT 0x43
+
+#define I2O_PRIVATE_MSG 0xFF
+
+/*
+ * Init Outbound Q status
+ */
+
+#define I2O_CMD_OUTBOUND_INIT_IN_PROGRESS 0x01
+#define I2O_CMD_OUTBOUND_INIT_REJECTED 0x02
+#define I2O_CMD_OUTBOUND_INIT_FAILED 0x03
+#define I2O_CMD_OUTBOUND_INIT_COMPLETE 0x04
+
+/*
+ * I2O Get Status State values
+ */
+
+#define ADAPTER_STATE_INITIALIZING 0x01
+#define ADAPTER_STATE_RESET 0x02
+#define ADAPTER_STATE_HOLD 0x04
+#define ADAPTER_STATE_READY 0x05
+#define ADAPTER_STATE_OPERATIONAL 0x08
+#define ADAPTER_STATE_FAILED 0x10
+#define ADAPTER_STATE_FAULTED 0x11
+
+/* I2O API function return values */
+
+#define I2O_RTN_NO_ERROR 0
+#define I2O_RTN_NOT_INIT 1
+#define I2O_RTN_FREE_Q_EMPTY 2
+#define I2O_RTN_TCB_ERROR 3
+#define I2O_RTN_TRANSACTION_ERROR 4
+#define I2O_RTN_ADAPTER_ALREADY_INIT 5
+#define I2O_RTN_MALLOC_ERROR 6
+#define I2O_RTN_ADPTR_NOT_REGISTERED 7
+#define I2O_RTN_MSG_REPLY_TIMEOUT 8
+#define I2O_RTN_NO_STATUS 9
+#define I2O_RTN_NO_FIRM_VER 10
+#define I2O_RTN_NO_LINK_SPEED 11
+
+/* Reply message status defines for all messages */
+
+#define I2O_REPLY_STATUS_SUCCESS 0x00
+#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01
+#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
+#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03
+#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04
+#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05
+#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06
+#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x08
+#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x09
+#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x0A
+#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0B
+#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80
+
+/* Status codes and Error Information for Parameter functions */
+
+#define I2O_PARAMS_STATUS_SUCCESS 0x00
+#define I2O_PARAMS_STATUS_BAD_KEY_ABORT 0x01
+#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE 0x02
+#define I2O_PARAMS_STATUS_BUFFER_FULL 0x03
+#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL 0x04
+#define I2O_PARAMS_STATUS_FIELD_UNREADABLE 0x05
+#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE 0x06
+#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS 0x07
+#define I2O_PARAMS_STATUS_INVALID_GROUP_ID 0x08
+#define I2O_PARAMS_STATUS_INVALID_OPERATION 0x09
+#define I2O_PARAMS_STATUS_NO_KEY_FIELD 0x0A
+#define I2O_PARAMS_STATUS_NO_SUCH_FIELD 0x0B
+#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP 0x0C
+#define I2O_PARAMS_STATUS_OPERATION_ERROR 0x0D
+#define I2O_PARAMS_STATUS_SCALAR_ERROR 0x0E
+#define I2O_PARAMS_STATUS_TABLE_ERROR 0x0F
+#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE 0x10
+
+/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error
+ * messages: Table 3-2 Detailed Status Codes.*/
+
+#define I2O_DSC_SUCCESS 0x0000
+#define I2O_DSC_BAD_KEY 0x0002
+#define I2O_DSC_TCL_ERROR 0x0003
+#define I2O_DSC_REPLY_BUFFER_FULL 0x0004
+#define I2O_DSC_NO_SUCH_PAGE 0x0005
+#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT 0x0006
+#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD 0x0007
+#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE 0x0009
+#define I2O_DSC_UNSUPPORTED_FUNCTION 0x000A
+#define I2O_DSC_DEVICE_LOCKED 0x000B
+#define I2O_DSC_DEVICE_RESET 0x000C
+#define I2O_DSC_INAPPROPRIATE_FUNCTION 0x000D
+#define I2O_DSC_INVALID_INITIATOR_ADDRESS 0x000E
+#define I2O_DSC_INVALID_MESSAGE_FLAGS 0x000F
+#define I2O_DSC_INVALID_OFFSET 0x0010
+#define I2O_DSC_INVALID_PARAMETER 0x0011
+#define I2O_DSC_INVALID_REQUEST 0x0012
+#define I2O_DSC_INVALID_TARGET_ADDRESS 0x0013
+#define I2O_DSC_MESSAGE_TOO_LARGE 0x0014
+#define I2O_DSC_MESSAGE_TOO_SMALL 0x0015
+#define I2O_DSC_MISSING_PARAMETER 0x0016
+#define I2O_DSC_TIMEOUT 0x0017
+#define I2O_DSC_UNKNOWN_ERROR 0x0018
+#define I2O_DSC_UNKNOWN_FUNCTION 0x0019
+#define I2O_DSC_UNSUPPORTED_VERSION 0x001A
+#define I2O_DSC_DEVICE_BUSY 0x001B
+#define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C
+
+/* Device Claim Types */
+#define I2O_CLAIM_PRIMARY 0x01000000
+#define I2O_CLAIM_MANAGEMENT 0x02000000
+#define I2O_CLAIM_AUTHORIZED 0x03000000
+#define I2O_CLAIM_SECONDARY 0x04000000
+
+/* Message header defines for VersionOffset */
+#define I2OVER15 0x0001
+#define I2OVER20 0x0002
+/* Default is 1.5, FIXME: Need support for both 1.5 and 2.0 */
+#define I2OVERSION I2OVER15
+#define SGL_OFFSET_0 I2OVERSION
+#define SGL_OFFSET_4 (0x0040 | I2OVERSION)
+#define SGL_OFFSET_5 (0x0050 | I2OVERSION)
+#define SGL_OFFSET_6 (0x0060 | I2OVERSION)
+#define SGL_OFFSET_7 (0x0070 | I2OVERSION)
+#define SGL_OFFSET_8 (0x0080 | I2OVERSION)
+#define SGL_OFFSET_9 (0x0090 | I2OVERSION)
+#define SGL_OFFSET_10 (0x00A0 | I2OVERSION)
+#define SGL_OFFSET_12 (0x00C0 | I2OVERSION)
+
+#define TRL_OFFSET_5 (0x0050 | I2OVERSION)
+#define TRL_OFFSET_6 (0x0060 | I2OVERSION)
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC 0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL 0x2000
+#define MSG_LAST 0x4000
+#define MSG_REPLY 0x8000
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE 0x00040000
+#define FIVE_WORD_MSG_SIZE 0x00050000
+#define SIX_WORD_MSG_SIZE 0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE 0x00090000
+#define TEN_WORD_MSG_SIZE 0x000A0000
+#define I2O_MESSAGE_SIZE(x) ((x)<<16)
+
+
+/* Special TID Assignments */
+
+#define ADAPTER_TID 0
+#define HOST_TID 1
+
+#define MSG_FRAME_SIZE 128
+#define NMBR_MSG_FRAMES 128
+
+#define MSG_POOL_SIZE 16384
+
+#define I2O_POST_WAIT_OK 0
+#define I2O_POST_WAIT_TIMEOUT -ETIMEDOUT
+
+
+#endif /* __KERNEL__ */
+
+#endif /* _SCSI_I2O_H */
diff --git a/drivers/scsi/dpt/dpti_ioctl.h b/drivers/scsi/dpt/dpti_ioctl.h
new file mode 100644
index 000000000000..82d24864be0c
--- /dev/null
+++ b/drivers/scsi/dpt/dpti_ioctl.h
@@ -0,0 +1,139 @@
+/***************************************************************************
+ dpti_ioctl.h - description
+ -------------------
+ begin : Thu Sep 7 2000
+ copyright : (C) 2001 by Adaptec
+
+ See Documentation/scsi/dpti.txt for history, notes, license info
+ and credits
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+/***************************************************************************
+ * This file is generated from osd_unix.h *
+ * *************************************************************************/
+
+#ifndef _dpti_ioctl_h
+#define _dpti_ioctl_h
+
+// IOCTL interface commands
+
+#ifndef _IOWR
+# define _IOWR(x,y,z) (((x)<<8)|y)
+#endif
+#ifndef _IOW
+# define _IOW(x,y,z) (((x)<<8)|y)
+#endif
+#ifndef _IOR
+# define _IOR(x,y,z) (((x)<<8)|y)
+#endif
+#ifndef _IO
+# define _IO(x,y) (((x)<<8)|y)
+#endif
+/* EATA PassThrough Command */
+#define EATAUSRCMD _IOWR('D',65,EATA_CP)
+/* Set Debug Level If Enabled */
+#define DPT_DEBUG _IOW('D',66,int)
+/* Get Signature Structure */
+#define DPT_SIGNATURE _IOR('D',67,dpt_sig_S)
+#if defined __bsdi__
+#define DPT_SIGNATURE_PACKED _IOR('D',67,dpt_sig_S_Packed)
+#endif
+/* Get Number Of DPT Adapters */
+#define DPT_NUMCTRLS _IOR('D',68,int)
+/* Get Adapter Info Structure */
+#define DPT_CTRLINFO _IOR('D',69,CtrlInfo)
+/* Get Statistics If Enabled */
+#define DPT_STATINFO _IO('D',70)
+/* Clear Stats If Enabled */
+#define DPT_CLRSTAT _IO('D',71)
+/* Get System Info Structure */
+#define DPT_SYSINFO _IOR('D',72,sysInfo_S)
+/* Set Timeout Value */
+#define DPT_TIMEOUT _IO('D',73)
+/* Get config Data */
+#define DPT_CONFIG _IO('D',74)
+/* Get Blink LED Code */
+#define DPT_BLINKLED _IOR('D',75,int)
+/* Get Statistical information (if available) */
+#define DPT_STATS_INFO _IOR('D',80,STATS_DATA)
+/* Clear the statistical information */
+#define DPT_STATS_CLEAR _IO('D',81)
+/* Get Performance metrics */
+#define DPT_PERF_INFO _IOR('D',82,dpt_perf_t)
+/* Send an I2O command */
+#define I2OUSRCMD _IO('D',76)
+/* Inform driver to re-acquire LCT information */
+#define I2ORESCANCMD _IO('D',77)
+/* Inform driver to reset adapter */
+#define I2ORESETCMD _IO('D',78)
+/* See if the target is mounted */
+#define DPT_TARGET_BUSY _IOR('D',79, TARGET_BUSY_T)
+
+
+ /* Structure Returned From Get Controller Info */
+
+typedef struct {
+ uCHAR state; /* Operational state */
+ uCHAR id; /* Host adapter SCSI id */
+ int vect; /* Interrupt vector number */
+ int base; /* Base I/O address */
+ int njobs; /* # of jobs sent to HA */
+ int qdepth; /* Controller queue depth. */
+ int wakebase; /* mpx wakeup base index. */
+ uLONG SGsize; /* Scatter/Gather list size. */
+ unsigned heads; /* heads for drives on cntlr. */
+ unsigned sectors; /* sectors for drives on cntlr. */
+ uCHAR do_drive32; /* Flag for Above 16 MB Ability */
+ uCHAR BusQuiet; /* SCSI Bus Quiet Flag */
+ char idPAL[4]; /* 4 Bytes Of The ID Pal */
+ uCHAR primary; /* 1 For Primary, 0 For Secondary */
+ uCHAR eataVersion; /* EATA Version */
+ uLONG cpLength; /* EATA Command Packet Length */
+ uLONG spLength; /* EATA Status Packet Length */
+ uCHAR drqNum; /* DRQ Index (0,5,6,7) */
+ uCHAR flag1; /* EATA Flags 1 (Byte 9) */
+ uCHAR flag2; /* EATA Flags 2 (Byte 30) */
+} CtrlInfo;
+
+typedef struct {
+ uSHORT length; // Remaining length of this
+ uSHORT drvrHBAnum; // Relative HBA # used by the driver
+ uLONG baseAddr; // Base I/O address
+ uSHORT blinkState; // Blink LED state (0=Not in blink LED)
+ uCHAR pciBusNum; // PCI Bus # (Optional)
+ uCHAR pciDeviceNum; // PCI Device # (Optional)
+ uSHORT hbaFlags; // Miscellaneous HBA flags
+ uSHORT Interrupt; // Interrupt set for this device.
+# if (defined(_DPT_ARC))
+ uLONG baseLength;
+ ADAPTER_OBJECT *AdapterObject;
+ LARGE_INTEGER DmaLogicalAddress;
+ PVOID DmaVirtualAddress;
+ LARGE_INTEGER ReplyLogicalAddress;
+ PVOID ReplyVirtualAddress;
+# else
+ uLONG reserved1; // Reserved for future expansion
+ uLONG reserved2; // Reserved for future expansion
+ uLONG reserved3; // Reserved for future expansion
+# endif
+} drvrHBAinfo_S;
+
+typedef struct TARGET_BUSY
+{
+ uLONG channel;
+ uLONG id;
+ uLONG lun;
+ uLONG isBusy;
+} TARGET_BUSY_T;
+
+#endif
+
diff --git a/drivers/scsi/dpt/dptsig.h b/drivers/scsi/dpt/dptsig.h
new file mode 100644
index 000000000000..95a4cce6c892
--- /dev/null
+++ b/drivers/scsi/dpt/dptsig.h
@@ -0,0 +1,339 @@
+/* BSDI dptsig.h,v 1.7 1998/06/03 19:15:00 karels Exp */
+
+/*
+ * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source form, with or without modification, are
+ * permitted provided that redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * This software is provided `as is' by Distributed Processing Technology and
+ * any express or implied warranties, including, but not limited to, the
+ * implied warranties of merchantability and fitness for a particular purpose,
+ * are disclaimed. In no event shall Distributed Processing Technology be
+ * liable for any direct, indirect, incidental, special, exemplary or
+ * consequential damages (including, but not limited to, procurement of
+ * substitute goods or services; loss of use, data, or profits; or business
+ * interruptions) however caused and on any theory of liability, whether in
+ * contract, strict liability, or tort (including negligence or otherwise)
+ * arising in any way out of the use of this driver software, even if advised
+ * of the possibility of such damage.
+ *
+ */
+
+#ifndef __DPTSIG_H_
+#define __DPTSIG_H_
+#ifdef _SINIX_ADDON
+#include "dpt.h"
+#endif
+/* DPT SIGNATURE SPEC AND HEADER FILE */
+/* Signature Version 1 (sorry no 'A') */
+
+/* to make sure we are talking the same size under all OS's */
+typedef unsigned char sigBYTE;
+typedef unsigned short sigWORD;
+#if (defined(_MULTI_DATAMODEL) && defined(sun) && !defined(_ILP32))
+typedef uint32_t sigLONG;
+#else
+typedef unsigned long sigLONG;
+#endif
+
+/*
+ * use sigWORDLittleEndian for:
+ * dsCapabilities
+ * dsDeviceSupp
+ * dsAdapterSupp
+ * dsApplication
+ * use sigLONGLittleEndian for:
+ * dsOS
+ * so that the sig can be standardised to Little Endian
+ */
+#if (defined(_DPT_BIG_ENDIAN))
+# define sigWORDLittleEndian(x) ((((x)&0xFF)<<8)|(((x)>>8)&0xFF))
+# define sigLONGLittleEndian(x) \
+ ((((x)&0xFF)<<24) | \
+ (((x)&0xFF00)<<8) | \
+ (((x)&0xFF0000L)>>8) | \
+ (((x)&0xFF000000L)>>24))
+#else
+# define sigWORDLittleEndian(x) (x)
+# define sigLONGLittleEndian(x) (x)
+#endif
+
+/* must make sure the structure is not word or double-word aligned */
+/* --------------------------------------------------------------- */
+/* Borland will ignore the following pragma: */
+/* Word alignment is OFF by default. If in the, IDE make */
+/* sure that Options | Compiler | Code Generation | Word Alignment */
+/* is not checked. If using BCC, do not use the -a option. */
+
+#ifndef NO_PACK
+#if defined (_DPT_AIX)
+#pragma options align=packed
+#else
+#pragma pack(1)
+#endif /* aix */
+#endif
+/* For the Macintosh */
+#if STRUCTALIGNMENTSUPPORTED
+#pragma options align=mac68k
+#endif
+
+
+/* Current Signature Version - sigBYTE dsSigVersion; */
+/* ------------------------------------------------------------------ */
+#define SIG_VERSION 1
+
+/* Processor Family - sigBYTE dsProcessorFamily; DISTINCT VALUES */
+/* ------------------------------------------------------------------ */
+/* What type of processor the file is meant to run on. */
+/* This will let us know whether to read sigWORDs as high/low or low/high. */
+#define PROC_INTEL 0x00 /* Intel 80x86 */
+#define PROC_MOTOROLA 0x01 /* Motorola 68K */
+#define PROC_MIPS4000 0x02 /* MIPS RISC 4000 */
+#define PROC_ALPHA 0x03 /* DEC Alpha */
+#define PROC_POWERPC 0x04 /* IBM Power PC */
+#define PROC_i960 0x05 /* Intel i960 */
+#define PROC_ULTRASPARC 0x06 /* SPARC processor */
+
+/* Specific Minimim Processor - sigBYTE dsProcessor; FLAG BITS */
+/* ------------------------------------------------------------------ */
+/* Different bit definitions dependent on processor_family */
+
+/* PROC_INTEL: */
+#define PROC_8086 0x01 /* Intel 8086 */
+#define PROC_286 0x02 /* Intel 80286 */
+#define PROC_386 0x04 /* Intel 80386 */
+#define PROC_486 0x08 /* Intel 80486 */
+#define PROC_PENTIUM 0x10 /* Intel 586 aka P5 aka Pentium */
+#define PROC_SEXIUM 0x20 /* Intel 686 aka P6 aka Pentium Pro or MMX */
+
+/* PROC_i960: */
+#define PROC_960RX 0x01 /* Intel 80960RC/RD */
+#define PROC_960HX 0x02 /* Intel 80960HA/HD/HT */
+
+/* PROC_MOTOROLA: */
+#define PROC_68000 0x01 /* Motorola 68000 */
+#define PROC_68010 0x02 /* Motorola 68010 */
+#define PROC_68020 0x04 /* Motorola 68020 */
+#define PROC_68030 0x08 /* Motorola 68030 */
+#define PROC_68040 0x10 /* Motorola 68040 */
+
+/* PROC_POWERPC */
+#define PROC_PPC601 0x01 /* PowerPC 601 */
+#define PROC_PPC603 0x02 /* PowerPC 603 */
+#define PROC_PPC604 0x04 /* PowerPC 604 */
+
+/* PROC_MIPS4000: */
+#define PROC_R4000 0x01 /* MIPS R4000 */
+
+/* Filetype - sigBYTE dsFiletype; DISTINCT VALUES */
+/* ------------------------------------------------------------------ */
+#define FT_EXECUTABLE 0 /* Executable Program */
+#define FT_SCRIPT 1 /* Script/Batch File??? */
+#define FT_HBADRVR 2 /* HBA Driver */
+#define FT_OTHERDRVR 3 /* Other Driver */
+#define FT_IFS 4 /* Installable Filesystem Driver */
+#define FT_ENGINE 5 /* DPT Engine */
+#define FT_COMPDRVR 6 /* Compressed Driver Disk */
+#define FT_LANGUAGE 7 /* Foreign Language file */
+#define FT_FIRMWARE 8 /* Downloadable or actual Firmware */
+#define FT_COMMMODL 9 /* Communications Module */
+#define FT_INT13 10 /* INT 13 style HBA Driver */
+#define FT_HELPFILE 11 /* Help file */
+#define FT_LOGGER 12 /* Event Logger */
+#define FT_INSTALL 13 /* An Install Program */
+#define FT_LIBRARY 14 /* Storage Manager Real-Mode Calls */
+#define FT_RESOURCE 15 /* Storage Manager Resource File */
+#define FT_MODEM_DB 16 /* Storage Manager Modem Database */
+
+/* Filetype flags - sigBYTE dsFiletypeFlags; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define FTF_DLL 0x01 /* Dynamic Link Library */
+#define FTF_NLM 0x02 /* Netware Loadable Module */
+#define FTF_OVERLAYS 0x04 /* Uses overlays */
+#define FTF_DEBUG 0x08 /* Debug version */
+#define FTF_TSR 0x10 /* TSR */
+#define FTF_SYS 0x20 /* DOS Loadable driver */
+#define FTF_PROTECTED 0x40 /* Runs in protected mode */
+#define FTF_APP_SPEC 0x80 /* Application Specific */
+#define FTF_ROM (FTF_SYS|FTF_TSR) /* Special Case */
+
+/* OEM - sigBYTE dsOEM; DISTINCT VALUES */
+/* ------------------------------------------------------------------ */
+#define OEM_DPT 0 /* DPT */
+#define OEM_ATT 1 /* ATT */
+#define OEM_NEC 2 /* NEC */
+#define OEM_ALPHA 3 /* Alphatronix */
+#define OEM_AST 4 /* AST */
+#define OEM_OLIVETTI 5 /* Olivetti */
+#define OEM_SNI 6 /* Siemens/Nixdorf */
+#define OEM_SUN 7 /* SUN Microsystems */
+
+/* Operating System - sigLONG dsOS; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define OS_DOS 0x00000001 /* PC/MS-DOS */
+#define OS_WINDOWS 0x00000002 /* Microsoft Windows 3.x */
+#define OS_WINDOWS_NT 0x00000004 /* Microsoft Windows NT */
+#define OS_OS2M 0x00000008 /* OS/2 1.2.x,MS 1.3.0,IBM 1.3.x - Monolithic */
+#define OS_OS2L 0x00000010 /* Microsoft OS/2 1.301 - LADDR */
+#define OS_OS22x 0x00000020 /* IBM OS/2 2.x */
+#define OS_NW286 0x00000040 /* Novell NetWare 286 */
+#define OS_NW386 0x00000080 /* Novell NetWare 386 */
+#define OS_GEN_UNIX 0x00000100 /* Generic Unix */
+#define OS_SCO_UNIX 0x00000200 /* SCO Unix */
+#define OS_ATT_UNIX 0x00000400 /* ATT Unix */
+#define OS_UNIXWARE 0x00000800 /* USL Unix */
+#define OS_INT_UNIX 0x00001000 /* Interactive Unix */
+#define OS_SOLARIS 0x00002000 /* SunSoft Solaris */
+#define OS_QNX 0x00004000 /* QNX for Tom Moch */
+#define OS_NEXTSTEP 0x00008000 /* NeXTSTEP/OPENSTEP/MACH */
+#define OS_BANYAN 0x00010000 /* Banyan Vines */
+#define OS_OLIVETTI_UNIX 0x00020000/* Olivetti Unix */
+#define OS_MAC_OS 0x00040000 /* Mac OS */
+#define OS_WINDOWS_95 0x00080000 /* Microsoft Windows '95 */
+#define OS_NW4x 0x00100000 /* Novell Netware 4.x */
+#define OS_BSDI_UNIX 0x00200000 /* BSDi Unix BSD/OS 2.0 and up */
+#define OS_AIX_UNIX 0x00400000 /* AIX Unix */
+#define OS_FREE_BSD 0x00800000 /* FreeBSD Unix */
+#define OS_LINUX 0x01000000 /* Linux */
+#define OS_DGUX_UNIX 0x02000000 /* Data General Unix */
+#define OS_SINIX_N 0x04000000 /* SNI SINIX-N */
+#define OS_PLAN9 0x08000000 /* ATT Plan 9 */
+#define OS_TSX 0x10000000 /* SNH TSX-32 */
+
+#define OS_OTHER 0x80000000 /* Other */
+
+/* Capabilities - sigWORD dsCapabilities; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define CAP_RAID0 0x0001 /* RAID-0 */
+#define CAP_RAID1 0x0002 /* RAID-1 */
+#define CAP_RAID3 0x0004 /* RAID-3 */
+#define CAP_RAID5 0x0008 /* RAID-5 */
+#define CAP_SPAN 0x0010 /* Spanning */
+#define CAP_PASS 0x0020 /* Provides passthrough */
+#define CAP_OVERLAP 0x0040 /* Passthrough supports overlapped commands */
+#define CAP_ASPI 0x0080 /* Supports ASPI Command Requests */
+#define CAP_ABOVE16MB 0x0100 /* ISA Driver supports greater than 16MB */
+#define CAP_EXTEND 0x8000 /* Extended info appears after description */
+#ifdef SNI_MIPS
+#define CAP_CACHEMODE 0x1000 /* dpt_force_cache is set in driver */
+#endif
+
+/* Devices Supported - sigWORD dsDeviceSupp; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define DEV_DASD 0x0001 /* DASD (hard drives) */
+#define DEV_TAPE 0x0002 /* Tape drives */
+#define DEV_PRINTER 0x0004 /* Printers */
+#define DEV_PROC 0x0008 /* Processors */
+#define DEV_WORM 0x0010 /* WORM drives */
+#define DEV_CDROM 0x0020 /* CD-ROM drives */
+#define DEV_SCANNER 0x0040 /* Scanners */
+#define DEV_OPTICAL 0x0080 /* Optical Drives */
+#define DEV_JUKEBOX 0x0100 /* Jukebox */
+#define DEV_COMM 0x0200 /* Communications Devices */
+#define DEV_OTHER 0x0400 /* Other Devices */
+#define DEV_ALL 0xFFFF /* All SCSI Devices */
+
+/* Adapters Families Supported - sigWORD dsAdapterSupp; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define ADF_2001 0x0001 /* PM2001 */
+#define ADF_2012A 0x0002 /* PM2012A */
+#define ADF_PLUS_ISA 0x0004 /* PM2011,PM2021 */
+#define ADF_PLUS_EISA 0x0008 /* PM2012B,PM2022 */
+#define ADF_SC3_ISA 0x0010 /* PM2021 */
+#define ADF_SC3_EISA 0x0020 /* PM2022,PM2122, etc */
+#define ADF_SC3_PCI 0x0040 /* SmartCache III PCI */
+#define ADF_SC4_ISA 0x0080 /* SmartCache IV ISA */
+#define ADF_SC4_EISA 0x0100 /* SmartCache IV EISA */
+#define ADF_SC4_PCI 0x0200 /* SmartCache IV PCI */
+#define ADF_SC5_PCI 0x0400 /* Fifth Generation I2O products */
+/*
+ * Combinations of products
+ */
+#define ADF_ALL_2000 (ADF_2001|ADF_2012A)
+#define ADF_ALL_PLUS (ADF_PLUS_ISA|ADF_PLUS_EISA)
+#define ADF_ALL_SC3 (ADF_SC3_ISA|ADF_SC3_EISA|ADF_SC3_PCI)
+#define ADF_ALL_SC4 (ADF_SC4_ISA|ADF_SC4_EISA|ADF_SC4_PCI)
+#define ADF_ALL_SC5 (ADF_SC5_PCI)
+/* All EATA Cacheing Products */
+#define ADF_ALL_CACHE (ADF_ALL_PLUS|ADF_ALL_SC3|ADF_ALL_SC4)
+/* All EATA Bus Mastering Products */
+#define ADF_ALL_MASTER (ADF_2012A|ADF_ALL_CACHE)
+/* All EATA Adapter Products */
+#define ADF_ALL_EATA (ADF_2001|ADF_ALL_MASTER)
+#define ADF_ALL ADF_ALL_EATA
+
+/* Application - sigWORD dsApplication; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define APP_DPTMGR 0x0001 /* DPT Storage Manager */
+#define APP_ENGINE 0x0002 /* DPT Engine */
+#define APP_SYTOS 0x0004 /* Sytron Sytos Plus */
+#define APP_CHEYENNE 0x0008 /* Cheyenne ARCServe + ARCSolo */
+#define APP_MSCDEX 0x0010 /* Microsoft CD-ROM extensions */
+#define APP_NOVABACK 0x0020 /* NovaStor Novaback */
+#define APP_AIM 0x0040 /* Archive Information Manager */
+
+/* Requirements - sigBYTE dsRequirements; FLAG BITS */
+/* ------------------------------------------------------------------ */
+#define REQ_SMARTROM 0x01 /* Requires SmartROM to be present */
+#define REQ_DPTDDL 0x02 /* Requires DPTDDL.SYS to be loaded */
+#define REQ_HBA_DRIVER 0x04 /* Requires an HBA driver to be loaded */
+#define REQ_ASPI_TRAN 0x08 /* Requires an ASPI Transport Modules */
+#define REQ_ENGINE 0x10 /* Requires a DPT Engine to be loaded */
+#define REQ_COMM_ENG 0x20 /* Requires a DPT Communications Engine */
+
+/*
+ * You may adjust dsDescription_size with an override to a value less than
+ * 50 so that the structure allocates less real space.
+ */
+#if (!defined(dsDescription_size))
+# define dsDescription_size 50
+#endif
+
+typedef struct dpt_sig {
+ char dsSignature[6]; /* ALWAYS "dPtSiG" */
+ sigBYTE dsSigVersion; /* signature version (currently 1) */
+ sigBYTE dsProcessorFamily; /* what type of processor */
+ sigBYTE dsProcessor; /* precise processor */
+ sigBYTE dsFiletype; /* type of file */
+ sigBYTE dsFiletypeFlags; /* flags to specify load type, etc. */
+ sigBYTE dsOEM; /* OEM file was created for */
+ sigLONG dsOS; /* which Operating systems */
+ sigWORD dsCapabilities; /* RAID levels, etc. */
+ sigWORD dsDeviceSupp; /* Types of SCSI devices supported */
+ sigWORD dsAdapterSupp; /* DPT adapter families supported */
+ sigWORD dsApplication; /* applications file is for */
+ sigBYTE dsRequirements; /* Other driver dependencies */
+ sigBYTE dsVersion; /* 1 */
+ sigBYTE dsRevision; /* 'J' */
+ sigBYTE dsSubRevision; /* '9' ' ' if N/A */
+ sigBYTE dsMonth; /* creation month */
+ sigBYTE dsDay; /* creation day */
+ sigBYTE dsYear; /* creation year since 1980 (1993=13) */
+ /* description (NULL terminated) */
+ char dsDescription[dsDescription_size];
+} dpt_sig_S;
+/* 32 bytes minimum - with no description. Put NULL at description[0] */
+/* 81 bytes maximum - with 49 character description plus NULL. */
+
+/* This line added at Roycroft's request */
+/* Microsoft's NT compiler gets confused if you do a pack and don't */
+/* restore it. */
+
+#ifndef NO_UNPACK
+#if defined (_DPT_AIX)
+#pragma options align=reset
+#elif defined (UNPACK_FOUR)
+#pragma pack(4)
+#else
+#pragma pack()
+#endif /* aix */
+#endif
+/* For the Macintosh */
+#if STRUCTALIGNMENTSUPPORTED
+#pragma options align=reset
+#endif
+
+#endif
diff --git a/drivers/scsi/dpt/osd_defs.h b/drivers/scsi/dpt/osd_defs.h
new file mode 100644
index 000000000000..de3ae5722982
--- /dev/null
+++ b/drivers/scsi/dpt/osd_defs.h
@@ -0,0 +1,79 @@
+/* BSDI osd_defs.h,v 1.4 1998/06/03 19:14:58 karels Exp */
+/*
+ * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source form, with or without modification, are
+ * permitted provided that redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * This software is provided `as is' by Distributed Processing Technology and
+ * any express or implied warranties, including, but not limited to, the
+ * implied warranties of merchantability and fitness for a particular purpose,
+ * are disclaimed. In no event shall Distributed Processing Technology be
+ * liable for any direct, indirect, incidental, special, exemplary or
+ * consequential damages (including, but not limited to, procurement of
+ * substitute goods or services; loss of use, data, or profits; or business
+ * interruptions) however caused and on any theory of liability, whether in
+ * contract, strict liability, or tort (including negligence or otherwise)
+ * arising in any way out of the use of this driver software, even if advised
+ * of the possibility of such damage.
+ *
+ */
+
+#ifndef _OSD_DEFS_H
+#define _OSD_DEFS_H
+
+/*File - OSD_DEFS.H
+ ****************************************************************************
+ *
+ *Description:
+ *
+ * This file contains the OS dependent defines. This file is included
+ *in osd_util.h and provides the OS specific defines for that file.
+ *
+ *Copyright Distributed Processing Technology, Corp.
+ * 140 Candace Dr.
+ * Maitland, Fl. 32751 USA
+ * Phone: (407) 830-5522 Fax: (407) 260-5366
+ * All Rights Reserved
+ *
+ *Author: Doug Anderson
+ *Date: 1/31/94
+ *
+ *Editors:
+ *
+ *Remarks:
+ *
+ *
+ *****************************************************************************/
+
+
+/*Definitions - Defines & Constants ----------------------------------------- */
+
+ /* Define the operating system */
+#if (defined(__linux__))
+# define _DPT_LINUX
+#elif (defined(__bsdi__))
+# define _DPT_BSDI
+#elif (defined(__FreeBSD__))
+# define _DPT_FREE_BSD
+#else
+# define _DPT_SCO
+#endif
+
+#if defined (ZIL_CURSES)
+#define _DPT_CURSES
+#else
+#define _DPT_MOTIF
+#endif
+
+ /* Redefine 'far' to nothing - no far pointer type required in UNIX */
+#define far
+
+ /* Define the mutually exclusive semaphore type */
+#define SEMAPHORE_T unsigned int *
+ /* Define a handle to a DLL */
+#define DLL_HANDLE_T unsigned int *
+
+#endif
diff --git a/drivers/scsi/dpt/osd_util.h b/drivers/scsi/dpt/osd_util.h
new file mode 100644
index 000000000000..4b56c0436ba2
--- /dev/null
+++ b/drivers/scsi/dpt/osd_util.h
@@ -0,0 +1,358 @@
+/* BSDI osd_util.h,v 1.8 1998/06/03 19:14:58 karels Exp */
+
+/*
+ * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source form, with or without modification, are
+ * permitted provided that redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * This software is provided `as is' by Distributed Processing Technology and
+ * any express or implied warranties, including, but not limited to, the
+ * implied warranties of merchantability and fitness for a particular purpose,
+ * are disclaimed. In no event shall Distributed Processing Technology be
+ * liable for any direct, indirect, incidental, special, exemplary or
+ * consequential damages (including, but not limited to, procurement of
+ * substitute goods or services; loss of use, data, or profits; or business
+ * interruptions) however caused and on any theory of liability, whether in
+ * contract, strict liability, or tort (including negligence or otherwise)
+ * arising in any way out of the use of this driver software, even if advised
+ * of the possibility of such damage.
+ *
+ */
+
+#ifndef __OSD_UTIL_H
+#define __OSD_UTIL_H
+
+/*File - OSD_UTIL.H
+ ****************************************************************************
+ *
+ *Description:
+ *
+ * This file contains defines and function prototypes that are
+ *operating system dependent. The resources defined in this file
+ *are not specific to any particular application.
+ *
+ *Copyright Distributed Processing Technology, Corp.
+ * 140 Candace Dr.
+ * Maitland, Fl. 32751 USA
+ * Phone: (407) 830-5522 Fax: (407) 260-5366
+ * All Rights Reserved
+ *
+ *Author: Doug Anderson
+ *Date: 1/7/94
+ *
+ *Editors:
+ *
+ *Remarks:
+ *
+ *
+ *****************************************************************************/
+
+
+/*Definitions - Defines & Constants ----------------------------------------- */
+
+/*----------------------------- */
+/* Operating system selections: */
+/*----------------------------- */
+
+/*#define _DPT_MSDOS */
+/*#define _DPT_WIN_3X */
+/*#define _DPT_WIN_4X */
+/*#define _DPT_WIN_NT */
+/*#define _DPT_NETWARE */
+/*#define _DPT_OS2 */
+/*#define _DPT_SCO */
+/*#define _DPT_UNIXWARE */
+/*#define _DPT_SOLARIS */
+/*#define _DPT_NEXTSTEP */
+/*#define _DPT_BANYAN */
+
+/*-------------------------------- */
+/* Include the OS specific defines */
+/*-------------------------------- */
+
+/*#define OS_SELECTION From Above List */
+/*#define SEMAPHORE_T ??? */
+/*#define DLL_HANDLE_T ??? */
+
+#if (defined(KERNEL) && (defined(__FreeBSD__) || defined(__bsdi__)))
+# include "i386/isa/dpt_osd_defs.h"
+#else
+# include "osd_defs.h"
+#endif
+
+#ifndef DPT_UNALIGNED
+ #define DPT_UNALIGNED
+#endif
+
+#ifndef DPT_EXPORT
+ #define DPT_EXPORT
+#endif
+
+#ifndef DPT_IMPORT
+ #define DPT_IMPORT
+#endif
+
+#ifndef DPT_RUNTIME_IMPORT
+ #define DPT_RUNTIME_IMPORT DPT_IMPORT
+#endif
+
+/*--------------------- */
+/* OS dependent defines */
+/*--------------------- */
+
+#if defined (_DPT_MSDOS) || defined (_DPT_WIN_3X)
+ #define _DPT_16_BIT
+#else
+ #define _DPT_32_BIT
+#endif
+
+#if defined (_DPT_SCO) || defined (_DPT_UNIXWARE) || defined (_DPT_SOLARIS) || defined (_DPT_AIX) || defined (SNI_MIPS) || defined (_DPT_BSDI) || defined (_DPT_FREE_BSD) || defined(_DPT_LINUX)
+ #define _DPT_UNIX
+#endif
+
+#if defined (_DPT_WIN_3x) || defined (_DPT_WIN_4X) || defined (_DPT_WIN_NT) \
+ || defined (_DPT_OS2)
+ #define _DPT_DLL_SUPPORT
+#endif
+
+#if !defined (_DPT_MSDOS) && !defined (_DPT_WIN_3X) && !defined (_DPT_NETWARE)
+ #define _DPT_PREEMPTIVE
+#endif
+
+#if !defined (_DPT_MSDOS) && !defined (_DPT_WIN_3X)
+ #define _DPT_MULTI_THREADED
+#endif
+
+#if !defined (_DPT_MSDOS)
+ #define _DPT_MULTI_TASKING
+#endif
+
+ /* These exist for platforms that */
+ /* chunk when accessing mis-aligned */
+ /* data */
+#if defined (SNI_MIPS) || defined (_DPT_SOLARIS)
+ #if defined (_DPT_BIG_ENDIAN)
+ #if !defined (_DPT_STRICT_ALIGN)
+ #define _DPT_STRICT_ALIGN
+ #endif
+ #endif
+#endif
+
+ /* Determine if in C or C++ mode */
+#ifdef __cplusplus
+ #define _DPT_CPP
+#else
+ #define _DPT_C
+#endif
+
+/*-------------------------------------------------------------------*/
+/* Under Solaris the compiler refuses to accept code like: */
+/* { {"DPT"}, 0, NULL .... }, */
+/* and complains about the {"DPT"} part by saying "cannot use { } */
+/* to initialize char*". */
+/* */
+/* By defining these ugly macros we can get around this and also */
+/* not have to copy and #ifdef large sections of code. I know that */
+/* these macros are *really* ugly, but they should help reduce */
+/* maintenance in the long run. */
+/* */
+/*-------------------------------------------------------------------*/
+#if !defined (DPTSQO)
+ #if defined (_DPT_SOLARIS)
+ #define DPTSQO
+ #define DPTSQC
+ #else
+ #define DPTSQO {
+ #define DPTSQC }
+ #endif /* solaris */
+#endif /* DPTSQO */
+
+
+/*---------------------- */
+/* OS dependent typedefs */
+/*---------------------- */
+
+#if defined (_DPT_MSDOS) || defined (_DPT_SCO)
+ #define BYTE unsigned char
+ #define WORD unsigned short
+#endif
+
+#ifndef _DPT_TYPEDEFS
+ #define _DPT_TYPEDEFS
+ typedef unsigned char uCHAR;
+ typedef unsigned short uSHORT;
+ typedef unsigned int uINT;
+ typedef unsigned long uLONG;
+
+ typedef union {
+ uCHAR u8[4];
+ uSHORT u16[2];
+ uLONG u32;
+ } access_U;
+#endif
+
+#if !defined (NULL)
+ #define NULL 0
+#endif
+
+
+/*Prototypes - function ----------------------------------------------------- */
+
+#ifdef __cplusplus
+ extern "C" { /* Declare all these functions as "C" functions */
+#endif
+
+/*------------------------ */
+/* Byte reversal functions */
+/*------------------------ */
+
+ /* Reverses the byte ordering of a 2 byte variable */
+#if (!defined(osdSwap2))
+ uSHORT osdSwap2(DPT_UNALIGNED uSHORT *);
+#endif // !osdSwap2
+
+ /* Reverses the byte ordering of a 4 byte variable and shifts left 8 bits */
+#if (!defined(osdSwap3))
+ uLONG osdSwap3(DPT_UNALIGNED uLONG *);
+#endif // !osdSwap3
+
+
+#ifdef _DPT_NETWARE
+ #include "novpass.h" /* For DPT_Bswapl() prototype */
+ /* Inline the byte swap */
+ #ifdef __cplusplus
+ inline uLONG osdSwap4(uLONG *inLong) {
+ return *inLong = DPT_Bswapl(*inLong);
+ }
+ #else
+ #define osdSwap4(inLong) DPT_Bswapl(inLong)
+ #endif // cplusplus
+#else
+ /* Reverses the byte ordering of a 4 byte variable */
+# if (!defined(osdSwap4))
+ uLONG osdSwap4(DPT_UNALIGNED uLONG *);
+# endif // !osdSwap4
+
+ /* The following functions ALWAYS swap regardless of the *
+ * presence of DPT_BIG_ENDIAN */
+
+ uSHORT trueSwap2(DPT_UNALIGNED uSHORT *);
+ uLONG trueSwap4(DPT_UNALIGNED uLONG *);
+
+#endif // netware
+
+
+/*-------------------------------------*
+ * Network order swap functions *
+ * *
+ * These functions/macros will be used *
+ * by the structure insert()/extract() *
+ * functions. *
+ *
+ * We will enclose all structure *
+ * portability modifications inside *
+ * #ifdefs. When we are ready, we *
+ * will #define DPT_PORTABLE to begin *
+ * using the modifications. *
+ *-------------------------------------*/
+uLONG netSwap4(uLONG val);
+
+#if defined (_DPT_BIG_ENDIAN)
+
+// for big-endian we need to swap
+
+#ifndef NET_SWAP_2
+#define NET_SWAP_2(x) (((x) >> 8) | ((x) << 8))
+#endif // NET_SWAP_2
+
+#ifndef NET_SWAP_4
+#define NET_SWAP_4(x) netSwap4((x))
+#endif // NET_SWAP_4
+
+#else
+
+// for little-endian we don't need to do anything
+
+#ifndef NET_SWAP_2
+#define NET_SWAP_2(x) (x)
+#endif // NET_SWAP_2
+
+#ifndef NET_SWAP_4
+#define NET_SWAP_4(x) (x)
+#endif // NET_SWAP_4
+
+#endif // big endian
+
+
+
+/*----------------------------------- */
+/* Run-time loadable module functions */
+/*----------------------------------- */
+
+ /* Loads the specified run-time loadable DLL */
+DLL_HANDLE_T osdLoadModule(uCHAR *);
+ /* Unloads the specified run-time loadable DLL */
+uSHORT osdUnloadModule(DLL_HANDLE_T);
+ /* Returns a pointer to a function inside a run-time loadable DLL */
+void * osdGetFnAddr(DLL_HANDLE_T,uCHAR *);
+
+/*--------------------------------------- */
+/* Mutually exclusive semaphore functions */
+/*--------------------------------------- */
+
+ /* Create a named semaphore */
+SEMAPHORE_T osdCreateNamedSemaphore(char *);
+ /* Create a mutually exlusive semaphore */
+SEMAPHORE_T osdCreateSemaphore(void);
+ /* create an event semaphore */
+SEMAPHORE_T osdCreateEventSemaphore(void);
+ /* create a named event semaphore */
+SEMAPHORE_T osdCreateNamedEventSemaphore(char *);
+
+ /* Destroy the specified mutually exclusive semaphore object */
+uSHORT osdDestroySemaphore(SEMAPHORE_T);
+ /* Request access to the specified mutually exclusive semaphore */
+uLONG osdRequestSemaphore(SEMAPHORE_T,uLONG);
+ /* Release access to the specified mutually exclusive semaphore */
+uSHORT osdReleaseSemaphore(SEMAPHORE_T);
+ /* wait for a event to happen */
+uLONG osdWaitForEventSemaphore(SEMAPHORE_T, uLONG);
+ /* signal an event */
+uLONG osdSignalEventSemaphore(SEMAPHORE_T);
+ /* reset the event */
+uLONG osdResetEventSemaphore(SEMAPHORE_T);
+
+/*----------------- */
+/* Thread functions */
+/*----------------- */
+
+ /* Releases control to the task switcher in non-preemptive */
+ /* multitasking operating systems. */
+void osdSwitchThreads(void);
+
+ /* Starts a thread function */
+uLONG osdStartThread(void *,void *);
+
+/* what is my thread id */
+uLONG osdGetThreadID(void);
+
+/* wakes up the specifed thread */
+void osdWakeThread(uLONG);
+
+/* osd sleep for x miliseconds */
+void osdSleep(uLONG);
+
+#define DPT_THREAD_PRIORITY_LOWEST 0x00
+#define DPT_THREAD_PRIORITY_NORMAL 0x01
+#define DPT_THREAD_PRIORITY_HIGHEST 0x02
+
+uCHAR osdSetThreadPriority(uLONG tid, uCHAR priority);
+
+#ifdef __cplusplus
+ } /* end the xtern "C" declaration */
+#endif
+
+#endif /* osd_util_h */
diff --git a/drivers/scsi/dpt/sys_info.h b/drivers/scsi/dpt/sys_info.h
new file mode 100644
index 000000000000..d23b70c8c768
--- /dev/null
+++ b/drivers/scsi/dpt/sys_info.h
@@ -0,0 +1,417 @@
+/* BSDI sys_info.h,v 1.6 1998/06/03 19:14:59 karels Exp */
+
+/*
+ * Copyright (c) 1996-1999 Distributed Processing Technology Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source form, with or without modification, are
+ * permitted provided that redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * This software is provided `as is' by Distributed Processing Technology and
+ * any express or implied warranties, including, but not limited to, the
+ * implied warranties of merchantability and fitness for a particular purpose,
+ * are disclaimed. In no event shall Distributed Processing Technology be
+ * liable for any direct, indirect, incidental, special, exemplary or
+ * consequential damages (including, but not limited to, procurement of
+ * substitute goods or services; loss of use, data, or profits; or business
+ * interruptions) however caused and on any theory of liability, whether in
+ * contract, strict liability, or tort (including negligence or otherwise)
+ * arising in any way out of the use of this driver software, even if advised
+ * of the possibility of such damage.
+ *
+ */
+
+#ifndef __SYS_INFO_H
+#define __SYS_INFO_H
+
+/*File - SYS_INFO.H
+ ****************************************************************************
+ *
+ *Description:
+ *
+ * This file contains structure definitions for the OS dependent
+ *layer system information buffers.
+ *
+ *Copyright Distributed Processing Technology, Corp.
+ * 140 Candace Dr.
+ * Maitland, Fl. 32751 USA
+ * Phone: (407) 830-5522 Fax: (407) 260-5366
+ * All Rights Reserved
+ *
+ *Author: Don Kemper
+ *Date: 5/10/94
+ *
+ *Editors:
+ *
+ *Remarks:
+ *
+ *
+ *****************************************************************************/
+
+
+/*Include Files ------------------------------------------------------------- */
+
+#include "osd_util.h"
+
+#ifndef NO_PACK
+#if defined (_DPT_AIX)
+#pragma options align=packed
+#else
+#pragma pack(1)
+#endif /* aix */
+#endif // no unpack
+
+
+/*struct - driveParam_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the drive parameters seen during
+ *booting.
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ struct driveParam_S {
+#else
+ typedef struct {
+#endif
+
+ uSHORT cylinders; /* Upto 1024 */
+ uCHAR heads; /* Upto 255 */
+ uCHAR sectors; /* Upto 63 */
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } driveParam_S;
+#endif
+/*driveParam_S - end */
+
+
+/*struct - sysInfo_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the command system information that
+ *should be returned by every OS dependent layer.
+ *
+ *---------------------------------------------------------------------------*/
+
+/*flags - bit definitions */
+#define SI_CMOS_Valid 0x0001
+#define SI_NumDrivesValid 0x0002
+#define SI_ProcessorValid 0x0004
+#define SI_MemorySizeValid 0x0008
+#define SI_DriveParamsValid 0x0010
+#define SI_SmartROMverValid 0x0020
+#define SI_OSversionValid 0x0040
+#define SI_OSspecificValid 0x0080 /* 1 if OS structure returned */
+#define SI_BusTypeValid 0x0100
+
+#define SI_ALL_VALID 0x0FFF /* All Std SysInfo is valid */
+#define SI_NO_SmartROM 0x8000
+
+/*busType - definitions */
+#define SI_ISA_BUS 0x00
+#define SI_MCA_BUS 0x01
+#define SI_EISA_BUS 0x02
+#define SI_PCI_BUS 0x04
+
+#ifdef __cplusplus
+ struct sysInfo_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR drive0CMOS; /* CMOS Drive 0 Type */
+ uCHAR drive1CMOS; /* CMOS Drive 1 Type */
+ uCHAR numDrives; /* 0040:0075 contents */
+ uCHAR processorFamily; /* Same as DPTSIG's definition */
+ uCHAR processorType; /* Same as DPTSIG's definition */
+ uCHAR smartROMMajorVersion;
+ uCHAR smartROMMinorVersion; /* SmartROM version */
+ uCHAR smartROMRevision;
+ uSHORT flags; /* See bit definitions above */
+ uSHORT conventionalMemSize; /* in KB */
+ uLONG extendedMemSize; /* in KB */
+ uLONG osType; /* Same as DPTSIG's definition */
+ uCHAR osMajorVersion;
+ uCHAR osMinorVersion; /* The OS version */
+ uCHAR osRevision;
+#ifdef _SINIX_ADDON
+ uCHAR busType; /* See defininitions above */
+ uSHORT osSubRevision;
+ uCHAR pad[2]; /* For alignment */
+#else
+ uCHAR osSubRevision;
+ uCHAR busType; /* See defininitions above */
+ uCHAR pad[3]; /* For alignment */
+#endif
+ driveParam_S drives[16]; /* SmartROM Logical Drives */
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } sysInfo_S;
+#endif
+/*sysInfo_S - end */
+
+
+/*struct - DOS_Info_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the system information specific to a
+ *DOS workstation.
+ *
+ *---------------------------------------------------------------------------*/
+
+/*flags - bit definitions */
+#define DI_DOS_HIGH 0x01 /* DOS is loaded high */
+#define DI_DPMI_VALID 0x02 /* DPMI version is valid */
+
+#ifdef __cplusplus
+ struct DOS_Info_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR flags; /* See bit definitions above */
+ uSHORT driverLocation; /* SmartROM BIOS address */
+ uSHORT DOS_version;
+ uSHORT DPMI_version;
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } DOS_Info_S;
+#endif
+/*DOS_Info_S - end */
+
+
+/*struct - Netware_Info_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the system information specific to a
+ *Netware machine.
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ struct Netware_Info_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR driverName[13]; /* ie PM12NW31.DSK */
+ uCHAR serverName[48];
+ uCHAR netwareVersion; /* The Netware OS version */
+ uCHAR netwareSubVersion;
+ uCHAR netwareRevision;
+ uSHORT maxConnections; /* Probably 250 or 1000 */
+ uSHORT connectionsInUse;
+ uSHORT maxVolumes;
+ uCHAR unused;
+ uCHAR SFTlevel;
+ uCHAR TTSlevel;
+
+ uCHAR clibMajorVersion; /* The CLIB.NLM version */
+ uCHAR clibMinorVersion;
+ uCHAR clibRevision;
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } Netware_Info_S;
+#endif
+/*Netware_Info_S - end */
+
+
+/*struct - OS2_Info_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the system information specific to an
+ *OS/2 machine.
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ struct OS2_Info_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR something;
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } OS2_Info_S;
+#endif
+/*OS2_Info_S - end */
+
+
+/*struct - WinNT_Info_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the system information specific to a
+ *Windows NT machine.
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ struct WinNT_Info_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR something;
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } WinNT_Info_S;
+#endif
+/*WinNT_Info_S - end */
+
+
+/*struct - SCO_Info_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the system information specific to an
+ *SCO UNIX machine.
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ struct SCO_Info_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR something;
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } SCO_Info_S;
+#endif
+/*SCO_Info_S - end */
+
+
+/*struct - USL_Info_S - start
+ *===========================================================================
+ *
+ *Description:
+ *
+ * This structure defines the system information specific to a
+ *USL UNIX machine.
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ struct USL_Info_S {
+#else
+ typedef struct {
+#endif
+
+ uCHAR something;
+
+#ifdef __cplusplus
+
+//---------- Portability Additions ----------- in sp_sinfo.cpp
+#ifdef DPT_PORTABLE
+ uSHORT netInsert(dptBuffer_S *buffer);
+ uSHORT netExtract(dptBuffer_S *buffer);
+#endif // DPT PORTABLE
+//--------------------------------------------
+
+ };
+#else
+ } USL_Info_S;
+#endif
+/*USL_Info_S - end */
+
+
+ /* Restore default structure packing */
+#ifndef NO_UNPACK
+#if defined (_DPT_AIX)
+#pragma options align=reset
+#elif defined (UNPACK_FOUR)
+#pragma pack(4)
+#else
+#pragma pack()
+#endif /* aix */
+#endif // no unpack
+
+#endif // __SYS_INFO_H
+
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
new file mode 100644
index 000000000000..53c9b93013f1
--- /dev/null
+++ b/drivers/scsi/dpt_i2o.c
@@ -0,0 +1,3381 @@
+/***************************************************************************
+ dpti.c - description
+ -------------------
+ begin : Thu Sep 7 2000
+ copyright : (C) 2000 by Adaptec
+
+ July 30, 2001 First version being submitted
+ for inclusion in the kernel. V2.4
+
+ See Documentation/scsi/dpti.txt for history, notes, license info
+ and credits
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+/***************************************************************************
+ * Sat Dec 20 2003 Go Taniguchi <go@turbolinux.co.jp>
+ - Support 2.6 kernel and DMA-mapping
+ - ioctl fix for raid tools
+ - use schedule_timeout in long long loop
+ **************************************************************************/
+
+/*#define DEBUG 1 */
+/*#define UARTDELAY 1 */
+
+/* On the real kernel ADDR32 should always be zero for 2.4. GFP_HIGH allocates
+ high pages. Keep the macro around because of the broken unmerged ia64 tree */
+
+#define ADDR32 (0)
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Deanna Bonds, with _lots_ of help from Mark Salyzyn");
+MODULE_DESCRIPTION("Adaptec I2O RAID Driver");
+
+////////////////////////////////////////////////////////////////
+
+#include <linux/ioctl.h> /* For SCSI-Passthrough */
+#include <asm/uaccess.h>
+
+#include <linux/stat.h>
+#include <linux/slab.h> /* for kmalloc() */
+#include <linux/config.h> /* for CONFIG_PCI */
+#include <linux/pci.h> /* for PCI support */
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h> /* for udelay */
+#include <linux/interrupt.h>
+#include <linux/kernel.h> /* for printk */
+#include <linux/sched.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#include <asm/processor.h> /* for boot_cpu_data */
+#include <asm/pgtable.h>
+#include <asm/io.h> /* for virt_to_bus, etc. */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "dpt/dptsig.h"
+#include "dpti.h"
+
+/*============================================================================
+ * Create a binary signature - this is read by dptsig
+ * Needed for our management apps
+ *============================================================================
+ */
+static dpt_sig_S DPTI_sig = {
+ {'d', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION,
+#ifdef __i386__
+ PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM,
+#elif defined(__ia64__)
+ PROC_INTEL, PROC_IA64,
+#elif defined(__sparc__)
+ PROC_ULTRASPARC, PROC_ULTRASPARC,
+#elif defined(__alpha__)
+ PROC_ALPHA, PROC_ALPHA,
+#else
+ (-1),(-1),
+#endif
+ FT_HBADRVR, 0, OEM_DPT, OS_LINUX, CAP_OVERLAP, DEV_ALL,
+ ADF_ALL_SC5, 0, 0, DPT_VERSION, DPT_REVISION, DPT_SUBREVISION,
+ DPT_MONTH, DPT_DAY, DPT_YEAR, "Adaptec Linux I2O RAID Driver"
+};
+
+
+
+
+/*============================================================================
+ * Globals
+ *============================================================================
+ */
+
+static DECLARE_MUTEX(adpt_configuration_lock);
+
+static struct i2o_sys_tbl *sys_tbl = NULL;
+static int sys_tbl_ind = 0;
+static int sys_tbl_len = 0;
+
+static adpt_hba* hbas[DPTI_MAX_HBA];
+static adpt_hba* hba_chain = NULL;
+static int hba_count = 0;
+
+static struct file_operations adpt_fops = {
+ .ioctl = adpt_ioctl,
+ .open = adpt_open,
+ .release = adpt_close
+};
+
+#ifdef REBOOT_NOTIFIER
+static struct notifier_block adpt_reboot_notifier =
+{
+ adpt_reboot_event,
+ NULL,
+ 0
+};
+#endif
+
+/* Structures and definitions for synchronous message posting.
+ * See adpt_i2o_post_wait() for description
+ * */
+struct adpt_i2o_post_wait_data
+{
+ int status;
+ u32 id;
+ adpt_wait_queue_head_t *wq;
+ struct adpt_i2o_post_wait_data *next;
+};
+
+static struct adpt_i2o_post_wait_data *adpt_post_wait_queue = NULL;
+static u32 adpt_post_wait_id = 0;
+static DEFINE_SPINLOCK(adpt_post_wait_lock);
+
+
+/*============================================================================
+ * Functions
+ *============================================================================
+ */
+
+static u8 adpt_read_blink_led(adpt_hba* host)
+{
+ if(host->FwDebugBLEDflag_P != 0) {
+ if( readb(host->FwDebugBLEDflag_P) == 0xbc ){
+ return readb(host->FwDebugBLEDvalue_P);
+ }
+ }
+ return 0;
+}
+
+/*============================================================================
+ * Scsi host template interface functions
+ *============================================================================
+ */
+
+static struct pci_device_id dptids[] = {
+ { PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ { PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci,dptids);
+
+static int adpt_detect(struct scsi_host_template* sht)
+{
+ struct pci_dev *pDev = NULL;
+ adpt_hba* pHba;
+
+ adpt_init();
+
+ PINFO("Detecting Adaptec I2O RAID controllers...\n");
+
+ /* search for all Adatpec I2O RAID cards */
+ while ((pDev = pci_find_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) {
+ if(pDev->device == PCI_DPT_DEVICE_ID ||
+ pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){
+ if(adpt_install_hba(sht, pDev) ){
+ PERROR("Could not Init an I2O RAID device\n");
+ PERROR("Will not try to detect others.\n");
+ return hba_count-1;
+ }
+ }
+ }
+
+ /* In INIT state, Activate IOPs */
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ // Activate does get status , init outbound, and get hrt
+ if (adpt_i2o_activate_hba(pHba) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ }
+ }
+
+
+ /* Active IOPs in HOLD state */
+
+rebuild_sys_tab:
+ if (hba_chain == NULL)
+ return 0;
+
+ /*
+ * If build_sys_table fails, we kill everything and bail
+ * as we can't init the IOPs w/o a system table
+ */
+ if (adpt_i2o_build_sys_table() < 0) {
+ adpt_i2o_sys_shutdown();
+ return 0;
+ }
+
+ PDEBUG("HBA's in HOLD state\n");
+
+ /* If IOP don't get online, we need to rebuild the System table */
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (adpt_i2o_online_hba(pHba) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ goto rebuild_sys_tab;
+ }
+ }
+
+ /* Active IOPs now in OPERATIONAL state */
+ PDEBUG("HBA's in OPERATIONAL state\n");
+
+ printk("dpti: If you have a lot of devices this could take a few minutes.\n");
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ printk(KERN_INFO"%s: Reading the hardware resource table.\n", pHba->name);
+ if (adpt_i2o_lct_get(pHba) < 0){
+ adpt_i2o_delete_hba(pHba);
+ continue;
+ }
+
+ if (adpt_i2o_parse_lct(pHba) < 0){
+ adpt_i2o_delete_hba(pHba);
+ continue;
+ }
+ adpt_inquiry(pHba);
+ }
+
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if( adpt_scsi_register(pHba,sht) < 0){
+ adpt_i2o_delete_hba(pHba);
+ continue;
+ }
+ pHba->initialized = TRUE;
+ pHba->state &= ~DPTI_STATE_RESET;
+ }
+
+ // Register our control device node
+ // nodes will need to be created in /dev to access this
+ // the nodes can not be created from within the driver
+ if (hba_count && register_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER, &adpt_fops)) {
+ adpt_i2o_sys_shutdown();
+ return 0;
+ }
+ return hba_count;
+}
+
+
+/*
+ * scsi_unregister will be called AFTER we return.
+ */
+static int adpt_release(struct Scsi_Host *host)
+{
+ adpt_hba* pHba = (adpt_hba*) host->hostdata[0];
+// adpt_i2o_quiesce_hba(pHba);
+ adpt_i2o_delete_hba(pHba);
+ scsi_unregister(host);
+ return 0;
+}
+
+
+static void adpt_inquiry(adpt_hba* pHba)
+{
+ u32 msg[14];
+ u32 *mptr;
+ u32 *lenptr;
+ int direction;
+ int scsidir;
+ u32 len;
+ u32 reqlen;
+ u8* buf;
+ u8 scb[16];
+ s32 rcode;
+
+ memset(msg, 0, sizeof(msg));
+ buf = (u8*)kmalloc(80,GFP_KERNEL|ADDR32);
+ if(!buf){
+ printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name);
+ return;
+ }
+ memset((void*)buf, 0, 36);
+
+ len = 36;
+ direction = 0x00000000;
+ scsidir =0x40000000; // DATA IN (iop<--dev)
+
+ reqlen = 14; // SINGLE SGE
+ /* Stick the headers on */
+ msg[0] = reqlen<<16 | SGL_OFFSET_12;
+ msg[1] = (0xff<<24|HOST_TID<<12|ADAPTER_TID);
+ msg[2] = 0;
+ msg[3] = 0;
+ // Adaptec/DPT Private stuff
+ msg[4] = I2O_CMD_SCSI_EXEC|DPT_ORGANIZATION_ID<<16;
+ msg[5] = ADAPTER_TID | 1<<16 /* Interpret*/;
+ /* Direction, disconnect ok | sense data | simple queue , CDBLen */
+ // I2O_SCB_FLAG_ENABLE_DISCONNECT |
+ // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG |
+ // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
+ msg[6] = scsidir|0x20a00000| 6 /* cmd len*/;
+
+ mptr=msg+7;
+
+ memset(scb, 0, sizeof(scb));
+ // Write SCSI command into the message - always 16 byte block
+ scb[0] = INQUIRY;
+ scb[1] = 0;
+ scb[2] = 0;
+ scb[3] = 0;
+ scb[4] = 36;
+ scb[5] = 0;
+ // Don't care about the rest of scb
+
+ memcpy(mptr, scb, sizeof(scb));
+ mptr+=4;
+ lenptr=mptr++; /* Remember me - fill in when we know */
+
+ /* Now fill in the SGList and command */
+ *lenptr = len;
+ *mptr++ = 0xD0000000|direction|len;
+ *mptr++ = virt_to_bus(buf);
+
+ // Send it on it's way
+ rcode = adpt_i2o_post_wait(pHba, msg, reqlen<<2, 120);
+ if (rcode != 0) {
+ sprintf(pHba->detail, "Adaptec I2O RAID");
+ printk(KERN_INFO "%s: Inquiry Error (%d)\n",pHba->name,rcode);
+ if (rcode != -ETIME && rcode != -EINTR)
+ kfree(buf);
+ } else {
+ memset(pHba->detail, 0, sizeof(pHba->detail));
+ memcpy(&(pHba->detail), "Vendor: Adaptec ", 16);
+ memcpy(&(pHba->detail[16]), " Model: ", 8);
+ memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16);
+ memcpy(&(pHba->detail[40]), " FW: ", 4);
+ memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4);
+ pHba->detail[48] = '\0'; /* precautionary */
+ kfree(buf);
+ }
+ adpt_i2o_status_get(pHba);
+ return ;
+}
+
+
+static int adpt_slave_configure(struct scsi_device * device)
+{
+ struct Scsi_Host *host = device->host;
+ adpt_hba* pHba;
+
+ pHba = (adpt_hba *) host->hostdata[0];
+
+ if (host->can_queue && device->tagged_supported) {
+ scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
+ host->can_queue - 1);
+ } else {
+ scsi_adjust_queue_depth(device, 0, 1);
+ }
+ return 0;
+}
+
+static int adpt_queue(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *))
+{
+ adpt_hba* pHba = NULL;
+ struct adpt_device* pDev = NULL; /* dpt per device information */
+ ulong timeout = jiffies + (TMOUT_SCSI*HZ);
+
+ cmd->scsi_done = done;
+ /*
+ * SCSI REQUEST_SENSE commands will be executed automatically by the
+ * Host Adapter for any errors, so they should not be executed
+ * explicitly unless the Sense Data is zero indicating that no error
+ * occurred.
+ */
+
+ if ((cmd->cmnd[0] == REQUEST_SENSE) && (cmd->sense_buffer[0] != 0)) {
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return 0;
+ }
+
+ pHba = (adpt_hba*)cmd->device->host->hostdata[0];
+ if (!pHba) {
+ return FAILED;
+ }
+
+ rmb();
+ /*
+ * TODO: I need to block here if I am processing ioctl cmds
+ * but if the outstanding cmds all finish before the ioctl,
+ * the scsi-core will not know to start sending cmds to me again.
+ * I need to a way to restart the scsi-cores queues or should I block
+ * calling scsi_done on the outstanding cmds instead
+ * for now we don't set the IOCTL state
+ */
+ if(((pHba->state) & DPTI_STATE_IOCTL) || ((pHba->state) & DPTI_STATE_RESET)) {
+ pHba->host->last_reset = jiffies;
+ pHba->host->resetting = 1;
+ return 1;
+ }
+
+ if(cmd->eh_state != SCSI_STATE_QUEUED){
+ // If we are not doing error recovery
+ mod_timer(&cmd->eh_timeout, timeout);
+ }
+
+ // TODO if the cmd->device if offline then I may need to issue a bus rescan
+ // followed by a get_lct to see if the device is there anymore
+ if((pDev = (struct adpt_device*) (cmd->device->hostdata)) == NULL) {
+ /*
+ * First command request for this device. Set up a pointer
+ * to the device structure. This should be a TEST_UNIT_READY
+ * command from scan_scsis_single.
+ */
+ if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun)) == NULL) {
+ // TODO: if any luns are at this bus, scsi id then fake a TEST_UNIT_READY and INQUIRY response
+ // with type 7F (for all luns less than the max for this bus,id) so the lun scan will continue.
+ cmd->result = (DID_NO_CONNECT << 16);
+ cmd->scsi_done(cmd);
+ return 0;
+ }
+ cmd->device->hostdata = pDev;
+ }
+ pDev->pScsi_dev = cmd->device;
+
+ /*
+ * If we are being called from when the device is being reset,
+ * delay processing of the command until later.
+ */
+ if (pDev->state & DPTI_DEV_RESET ) {
+ return FAILED;
+ }
+ return adpt_scsi_to_i2o(pHba, cmd, pDev);
+}
+
+static int adpt_bios_param(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int geom[])
+{
+ int heads=-1;
+ int sectors=-1;
+ int cylinders=-1;
+
+ // *** First lets set the default geometry ****
+
+ // If the capacity is less than ox2000
+ if (capacity < 0x2000 ) { // floppy
+ heads = 18;
+ sectors = 2;
+ }
+ // else if between 0x2000 and 0x20000
+ else if (capacity < 0x20000) {
+ heads = 64;
+ sectors = 32;
+ }
+ // else if between 0x20000 and 0x40000
+ else if (capacity < 0x40000) {
+ heads = 65;
+ sectors = 63;
+ }
+ // else if between 0x4000 and 0x80000
+ else if (capacity < 0x80000) {
+ heads = 128;
+ sectors = 63;
+ }
+ // else if greater than 0x80000
+ else {
+ heads = 255;
+ sectors = 63;
+ }
+ cylinders = sector_div(capacity, heads * sectors);
+
+ // Special case if CDROM
+ if(sdev->type == 5) { // CDROM
+ heads = 252;
+ sectors = 63;
+ cylinders = 1111;
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ PDEBUG("adpt_bios_param: exit\n");
+ return 0;
+}
+
+
+static const char *adpt_info(struct Scsi_Host *host)
+{
+ adpt_hba* pHba;
+
+ pHba = (adpt_hba *) host->hostdata[0];
+ return (char *) (pHba->detail);
+}
+
+static int adpt_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ struct adpt_device* d;
+ int id;
+ int chan;
+ int len = 0;
+ int begin = 0;
+ int pos = 0;
+ adpt_hba* pHba;
+ int unit;
+
+ *start = buffer;
+ if (inout == TRUE) {
+ /*
+ * The user has done a write and wants us to take the
+ * data in the buffer and do something with it.
+ * proc_scsiwrite calls us with inout = 1
+ *
+ * Read data from buffer (writing to us) - NOT SUPPORTED
+ */
+ return -EINVAL;
+ }
+
+ /*
+ * inout = 0 means the user has done a read and wants information
+ * returned, so we write information about the cards into the buffer
+ * proc_scsiread() calls us with inout = 0
+ */
+
+ // Find HBA (host bus adapter) we are looking for
+ down(&adpt_configuration_lock);
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (pHba->host == host) {
+ break; /* found adapter */
+ }
+ }
+ up(&adpt_configuration_lock);
+ if (pHba == NULL) {
+ return 0;
+ }
+ host = pHba->host;
+
+ len = sprintf(buffer , "Adaptec I2O RAID Driver Version: %s\n\n", DPT_I2O_VERSION);
+ len += sprintf(buffer+len, "%s\n", pHba->detail);
+ len += sprintf(buffer+len, "SCSI Host=scsi%d Control Node=/dev/%s irq=%d\n",
+ pHba->host->host_no, pHba->name, host->irq);
+ len += sprintf(buffer+len, "\tpost fifo size = %d\n\treply fifo size = %d\n\tsg table size = %d\n\n",
+ host->can_queue, (int) pHba->reply_fifo_size , host->sg_tablesize);
+
+ pos = begin + len;
+
+ /* CHECKPOINT */
+ if(pos > offset + length) {
+ goto stop_output;
+ }
+ if(pos <= offset) {
+ /*
+ * If we haven't even written to where we last left
+ * off (the last time we were called), reset the
+ * beginning pointer.
+ */
+ len = 0;
+ begin = pos;
+ }
+ len += sprintf(buffer+len, "Devices:\n");
+ for(chan = 0; chan < MAX_CHANNEL; chan++) {
+ for(id = 0; id < MAX_ID; id++) {
+ d = pHba->channel[chan].device[id];
+ while(d){
+ len += sprintf(buffer+len,"\t%-24.24s", d->pScsi_dev->vendor);
+ len += sprintf(buffer+len," Rev: %-8.8s\n", d->pScsi_dev->rev);
+ pos = begin + len;
+
+
+ /* CHECKPOINT */
+ if(pos > offset + length) {
+ goto stop_output;
+ }
+ if(pos <= offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ unit = d->pI2o_dev->lct_data.tid;
+ len += sprintf(buffer+len, "\tTID=%d, (Channel=%d, Target=%d, Lun=%d) (%s)\n\n",
+ unit, (int)d->scsi_channel, (int)d->scsi_id, (int)d->scsi_lun,
+ scsi_device_online(d->pScsi_dev)? "online":"offline");
+ pos = begin + len;
+
+ /* CHECKPOINT */
+ if(pos > offset + length) {
+ goto stop_output;
+ }
+ if(pos <= offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ d = d->next_lun;
+ }
+ }
+ }
+
+ /*
+ * begin is where we last checked our position with regards to offset
+ * begin is always less than offset. len is relative to begin. It
+ * is the number of bytes written past begin
+ *
+ */
+stop_output:
+ /* stop the output and calculate the correct length */
+ *(buffer + len) = '\0';
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length) {
+ len = length;
+ } else if(len < 0){
+ len = 0;
+ **start = '\0';
+ }
+ return len;
+}
+
+
+/*===========================================================================
+ * Error Handling routines
+ *===========================================================================
+ */
+
+static int adpt_abort(struct scsi_cmnd * cmd)
+{
+ adpt_hba* pHba = NULL; /* host bus adapter structure */
+ struct adpt_device* dptdevice; /* dpt per device information */
+ u32 msg[5];
+ int rcode;
+
+ if(cmd->serial_number == 0){
+ return FAILED;
+ }
+ pHba = (adpt_hba*) cmd->device->host->hostdata[0];
+ printk(KERN_INFO"%s: Trying to Abort cmd=%ld\n",pHba->name, cmd->serial_number);
+ if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
+ printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name);
+ return FAILED;
+ }
+
+ memset(msg, 0, sizeof(msg));
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid;
+ msg[2] = 0;
+ msg[3]= 0;
+ msg[4] = (u32)cmd;
+ if( (rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER)) != 0){
+ if(rcode == -EOPNOTSUPP ){
+ printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
+ return FAILED;
+ }
+ printk(KERN_INFO"%s: Abort cmd=%ld failed.\n",pHba->name, cmd->serial_number);
+ return FAILED;
+ }
+ printk(KERN_INFO"%s: Abort cmd=%ld complete.\n",pHba->name, cmd->serial_number);
+ return SUCCESS;
+}
+
+
+#define I2O_DEVICE_RESET 0x27
+// This is the same for BLK and SCSI devices
+// NOTE this is wrong in the i2o.h definitions
+// This is not currently supported by our adapter but we issue it anyway
+static int adpt_device_reset(struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ u32 msg[4];
+ u32 rcode;
+ int old_state;
+ struct adpt_device* d = (void*) cmd->device->hostdata;
+
+ pHba = (void*) cmd->device->host->hostdata[0];
+ printk(KERN_INFO"%s: Trying to reset device\n",pHba->name);
+ if (!d) {
+ printk(KERN_INFO"%s: Reset Device: Device Not found\n",pHba->name);
+ return FAILED;
+ }
+ memset(msg, 0, sizeof(msg));
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = (I2O_DEVICE_RESET<<24|HOST_TID<<12|d->tid);
+ msg[2] = 0;
+ msg[3] = 0;
+
+ old_state = d->state;
+ d->state |= DPTI_DEV_RESET;
+ if( (rcode = adpt_i2o_post_wait(pHba, (void*)msg,sizeof(msg), FOREVER)) ){
+ d->state = old_state;
+ if(rcode == -EOPNOTSUPP ){
+ printk(KERN_INFO"%s: Device reset not supported\n",pHba->name);
+ return FAILED;
+ }
+ printk(KERN_INFO"%s: Device reset failed\n",pHba->name);
+ return FAILED;
+ } else {
+ d->state = old_state;
+ printk(KERN_INFO"%s: Device reset successful\n",pHba->name);
+ return SUCCESS;
+ }
+}
+
+
+#define I2O_HBA_BUS_RESET 0x87
+// This version of bus reset is called by the eh_error handler
+static int adpt_bus_reset(struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ u32 msg[4];
+
+ pHba = (adpt_hba*)cmd->device->host->hostdata[0];
+ memset(msg, 0, sizeof(msg));
+ printk(KERN_WARNING"%s: Bus reset: SCSI Bus %d: tid: %d\n",pHba->name, cmd->device->channel,pHba->channel[cmd->device->channel].tid );
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid);
+ msg[2] = 0;
+ msg[3] = 0;
+ if(adpt_i2o_post_wait(pHba, (void*)msg,sizeof(msg), FOREVER) ){
+ printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name);
+ return FAILED;
+ } else {
+ printk(KERN_WARNING"%s: Bus reset success.\n",pHba->name);
+ return SUCCESS;
+ }
+}
+
+// This version of reset is called by the eh_error_handler
+static int adpt_reset(struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ int rcode;
+ pHba = (adpt_hba*)cmd->device->host->hostdata[0];
+ printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n",pHba->name,cmd->device->channel,pHba->channel[cmd->device->channel].tid );
+ rcode = adpt_hba_reset(pHba);
+ if(rcode == 0){
+ printk(KERN_WARNING"%s: HBA reset complete\n",pHba->name);
+ return SUCCESS;
+ } else {
+ printk(KERN_WARNING"%s: HBA reset failed (%x)\n",pHba->name, rcode);
+ return FAILED;
+ }
+}
+
+// This version of reset is called by the ioctls and indirectly from eh_error_handler via adpt_reset
+static int adpt_hba_reset(adpt_hba* pHba)
+{
+ int rcode;
+
+ pHba->state |= DPTI_STATE_RESET;
+
+ // Activate does get status , init outbound, and get hrt
+ if ((rcode=adpt_i2o_activate_hba(pHba)) < 0) {
+ printk(KERN_ERR "%s: Could not activate\n", pHba->name);
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+
+ if ((rcode=adpt_i2o_build_sys_table()) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+ PDEBUG("%s: in HOLD state\n",pHba->name);
+
+ if ((rcode=adpt_i2o_online_hba(pHba)) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+ PDEBUG("%s: in OPERATIONAL state\n",pHba->name);
+
+ if ((rcode=adpt_i2o_lct_get(pHba)) < 0){
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+
+ if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0){
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+ pHba->state &= ~DPTI_STATE_RESET;
+
+ adpt_fail_posted_scbs(pHba);
+ return 0; /* return success */
+}
+
+/*===========================================================================
+ *
+ *===========================================================================
+ */
+
+
+static void adpt_i2o_sys_shutdown(void)
+{
+ adpt_hba *pHba, *pNext;
+ struct adpt_i2o_post_wait_data *p1, *p2;
+
+ printk(KERN_INFO"Shutting down Adaptec I2O controllers.\n");
+ printk(KERN_INFO" This could take a few minutes if there are many devices attached\n");
+ /* Delete all IOPs from the controller chain */
+ /* They should have already been released by the
+ * scsi-core
+ */
+ for (pHba = hba_chain; pHba; pHba = pNext) {
+ pNext = pHba->next;
+ adpt_i2o_delete_hba(pHba);
+ }
+
+ /* Remove any timedout entries from the wait queue. */
+ p2 = NULL;
+// spin_lock_irqsave(&adpt_post_wait_lock, flags);
+ /* Nothing should be outstanding at this point so just
+ * free them
+ */
+ for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p2->next) {
+ kfree(p1);
+ }
+// spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
+ adpt_post_wait_queue = NULL;
+
+ printk(KERN_INFO "Adaptec I2O controllers down.\n");
+}
+
+/*
+ * reboot/shutdown notification.
+ *
+ * - Quiesce each IOP in the system
+ *
+ */
+
+#ifdef REBOOT_NOTIFIER
+static int adpt_reboot_event(struct notifier_block *n, ulong code, void *p)
+{
+
+ if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+
+ adpt_i2o_sys_shutdown();
+
+ return NOTIFY_DONE;
+}
+#endif
+
+
+static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev)
+{
+
+ adpt_hba* pHba = NULL;
+ adpt_hba* p = NULL;
+ ulong base_addr0_phys = 0;
+ ulong base_addr1_phys = 0;
+ u32 hba_map0_area_size = 0;
+ u32 hba_map1_area_size = 0;
+ void __iomem *base_addr_virt = NULL;
+ void __iomem *msg_addr_virt = NULL;
+
+ int raptorFlag = FALSE;
+ int i;
+
+ if(pci_enable_device(pDev)) {
+ return -EINVAL;
+ }
+ pci_set_master(pDev);
+ if (pci_set_dma_mask(pDev, 0xffffffffffffffffULL) &&
+ pci_set_dma_mask(pDev, 0xffffffffULL))
+ return -EINVAL;
+
+ base_addr0_phys = pci_resource_start(pDev,0);
+ hba_map0_area_size = pci_resource_len(pDev,0);
+
+ // Check if standard PCI card or single BAR Raptor
+ if(pDev->device == PCI_DPT_DEVICE_ID){
+ if(pDev->subsystem_device >=0xc032 && pDev->subsystem_device <= 0xc03b){
+ // Raptor card with this device id needs 4M
+ hba_map0_area_size = 0x400000;
+ } else { // Not Raptor - it is a PCI card
+ if(hba_map0_area_size > 0x100000 ){
+ hba_map0_area_size = 0x100000;
+ }
+ }
+ } else {// Raptor split BAR config
+ // Use BAR1 in this configuration
+ base_addr1_phys = pci_resource_start(pDev,1);
+ hba_map1_area_size = pci_resource_len(pDev,1);
+ raptorFlag = TRUE;
+ }
+
+
+ base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size);
+ if (!base_addr_virt) {
+ PERROR("dpti: adpt_config_hba: io remap failed\n");
+ return -EINVAL;
+ }
+
+ if(raptorFlag == TRUE) {
+ msg_addr_virt = ioremap(base_addr1_phys, hba_map1_area_size );
+ if (!msg_addr_virt) {
+ PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n");
+ iounmap(base_addr_virt);
+ return -EINVAL;
+ }
+ } else {
+ msg_addr_virt = base_addr_virt;
+ }
+
+ // Allocate and zero the data structure
+ pHba = kmalloc(sizeof(adpt_hba), GFP_KERNEL);
+ if( pHba == NULL) {
+ if(msg_addr_virt != base_addr_virt){
+ iounmap(msg_addr_virt);
+ }
+ iounmap(base_addr_virt);
+ return -ENOMEM;
+ }
+ memset(pHba, 0, sizeof(adpt_hba));
+
+ down(&adpt_configuration_lock);
+ for(i=0;i<DPTI_MAX_HBA;i++) {
+ if(hbas[i]==NULL) {
+ hbas[i]=pHba;
+ break;
+ }
+ }
+
+ if(hba_chain != NULL){
+ for(p = hba_chain; p->next; p = p->next);
+ p->next = pHba;
+ } else {
+ hba_chain = pHba;
+ }
+ pHba->next = NULL;
+ pHba->unit = hba_count;
+ sprintf(pHba->name, "dpti%d", i);
+ hba_count++;
+
+ up(&adpt_configuration_lock);
+
+ pHba->pDev = pDev;
+ pHba->base_addr_phys = base_addr0_phys;
+
+ // Set up the Virtual Base Address of the I2O Device
+ pHba->base_addr_virt = base_addr_virt;
+ pHba->msg_addr_virt = msg_addr_virt;
+ pHba->irq_mask = base_addr_virt+0x30;
+ pHba->post_port = base_addr_virt+0x40;
+ pHba->reply_port = base_addr_virt+0x44;
+
+ pHba->hrt = NULL;
+ pHba->lct = NULL;
+ pHba->lct_size = 0;
+ pHba->status_block = NULL;
+ pHba->post_count = 0;
+ pHba->state = DPTI_STATE_RESET;
+ pHba->pDev = pDev;
+ pHba->devices = NULL;
+
+ // Initializing the spinlocks
+ spin_lock_init(&pHba->state_lock);
+ spin_lock_init(&adpt_post_wait_lock);
+
+ if(raptorFlag == 0){
+ printk(KERN_INFO"Adaptec I2O RAID controller %d at %p size=%x irq=%d\n",
+ hba_count-1, base_addr_virt, hba_map0_area_size, pDev->irq);
+ } else {
+ printk(KERN_INFO"Adaptec I2O RAID controller %d irq=%d\n",hba_count-1, pDev->irq);
+ printk(KERN_INFO" BAR0 %p - size= %x\n",base_addr_virt,hba_map0_area_size);
+ printk(KERN_INFO" BAR1 %p - size= %x\n",msg_addr_virt,hba_map1_area_size);
+ }
+
+ if (request_irq (pDev->irq, adpt_isr, SA_SHIRQ, pHba->name, pHba)) {
+ printk(KERN_ERR"%s: Couldn't register IRQ %d\n", pHba->name, pDev->irq);
+ adpt_i2o_delete_hba(pHba);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static void adpt_i2o_delete_hba(adpt_hba* pHba)
+{
+ adpt_hba* p1;
+ adpt_hba* p2;
+ struct i2o_device* d;
+ struct i2o_device* next;
+ int i;
+ int j;
+ struct adpt_device* pDev;
+ struct adpt_device* pNext;
+
+
+ down(&adpt_configuration_lock);
+ // scsi_unregister calls our adpt_release which
+ // does a quiese
+ if(pHba->host){
+ free_irq(pHba->host->irq, pHba);
+ }
+ for(i=0;i<DPTI_MAX_HBA;i++) {
+ if(hbas[i]==pHba) {
+ hbas[i] = NULL;
+ }
+ }
+ p2 = NULL;
+ for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){
+ if(p1 == pHba) {
+ if(p2) {
+ p2->next = p1->next;
+ } else {
+ hba_chain = p1->next;
+ }
+ break;
+ }
+ }
+
+ hba_count--;
+ up(&adpt_configuration_lock);
+
+ iounmap(pHba->base_addr_virt);
+ if(pHba->msg_addr_virt != pHba->base_addr_virt){
+ iounmap(pHba->msg_addr_virt);
+ }
+ if(pHba->hrt) {
+ kfree(pHba->hrt);
+ }
+ if(pHba->lct){
+ kfree(pHba->lct);
+ }
+ if(pHba->status_block) {
+ kfree(pHba->status_block);
+ }
+ if(pHba->reply_pool){
+ kfree(pHba->reply_pool);
+ }
+
+ for(d = pHba->devices; d ; d = next){
+ next = d->next;
+ kfree(d);
+ }
+ for(i = 0 ; i < pHba->top_scsi_channel ; i++){
+ for(j = 0; j < MAX_ID; j++){
+ if(pHba->channel[i].device[j] != NULL){
+ for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){
+ pNext = pDev->next_lun;
+ kfree(pDev);
+ }
+ }
+ }
+ }
+ kfree(pHba);
+
+ if(hba_count <= 0){
+ unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER);
+ }
+}
+
+
+static int adpt_init(void)
+{
+ int i;
+
+ printk("Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n");
+ for (i = 0; i < DPTI_MAX_HBA; i++) {
+ hbas[i] = NULL;
+ }
+#ifdef REBOOT_NOTIFIER
+ register_reboot_notifier(&adpt_reboot_notifier);
+#endif
+
+ return 0;
+}
+
+
+static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun)
+{
+ struct adpt_device* d;
+
+ if(chan < 0 || chan >= MAX_CHANNEL)
+ return NULL;
+
+ if( pHba->channel[chan].device == NULL){
+ printk(KERN_DEBUG"Adaptec I2O RAID: Trying to find device before they are allocated\n");
+ return NULL;
+ }
+
+ d = pHba->channel[chan].device[id];
+ if(!d || d->tid == 0) {
+ return NULL;
+ }
+
+ /* If it is the only lun at that address then this should match*/
+ if(d->scsi_lun == lun){
+ return d;
+ }
+
+ /* else we need to look through all the luns */
+ for(d=d->next_lun ; d ; d = d->next_lun){
+ if(d->scsi_lun == lun){
+ return d;
+ }
+ }
+ return NULL;
+}
+
+
+static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout)
+{
+ // I used my own version of the WAIT_QUEUE_HEAD
+ // to handle some version differences
+ // When embedded in the kernel this could go back to the vanilla one
+ ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post);
+ int status = 0;
+ ulong flags = 0;
+ struct adpt_i2o_post_wait_data *p1, *p2;
+ struct adpt_i2o_post_wait_data *wait_data =
+ kmalloc(sizeof(struct adpt_i2o_post_wait_data),GFP_KERNEL);
+ adpt_wait_queue_t wait;
+
+ if(!wait_data){
+ return -ENOMEM;
+ }
+ /*
+ * The spin locking is needed to keep anyone from playing
+ * with the queue pointers and id while we do the same
+ */
+ spin_lock_irqsave(&adpt_post_wait_lock, flags);
+ // TODO we need a MORE unique way of getting ids
+ // to support async LCT get
+ wait_data->next = adpt_post_wait_queue;
+ adpt_post_wait_queue = wait_data;
+ adpt_post_wait_id++;
+ adpt_post_wait_id &= 0x7fff;
+ wait_data->id = adpt_post_wait_id;
+ spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
+
+ wait_data->wq = &adpt_wq_i2o_post;
+ wait_data->status = -ETIMEDOUT;
+
+ // this code is taken from kernel/sched.c:interruptible_sleep_on_timeout
+ wait.task = current;
+ init_waitqueue_entry(&wait, current);
+ spin_lock_irqsave(&adpt_wq_i2o_post.lock, flags);
+ __add_wait_queue(&adpt_wq_i2o_post, &wait);
+ spin_unlock(&adpt_wq_i2o_post.lock);
+
+ msg[2] |= 0x80000000 | ((u32)wait_data->id);
+ timeout *= HZ;
+ if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){
+ set_current_state(TASK_INTERRUPTIBLE);
+ if(pHba->host)
+ spin_unlock_irq(pHba->host->host_lock);
+ if (!timeout)
+ schedule();
+ else{
+ timeout = schedule_timeout(timeout);
+ if (timeout == 0) {
+ // I/O issued, but cannot get result in
+ // specified time. Freeing resorces is
+ // dangerous.
+ status = -ETIME;
+ }
+ }
+ if(pHba->host)
+ spin_lock_irq(pHba->host->host_lock);
+ }
+ spin_lock_irq(&adpt_wq_i2o_post.lock);
+ __remove_wait_queue(&adpt_wq_i2o_post, &wait);
+ spin_unlock_irqrestore(&adpt_wq_i2o_post.lock, flags);
+
+ if(status == -ETIMEDOUT){
+ printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit);
+ // We will have to free the wait_data memory during shutdown
+ return status;
+ }
+
+ /* Remove the entry from the queue. */
+ p2 = NULL;
+ spin_lock_irqsave(&adpt_post_wait_lock, flags);
+ for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p1->next) {
+ if(p1 == wait_data) {
+ if(p1->status == I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION ) {
+ status = -EOPNOTSUPP;
+ }
+ if(p2) {
+ p2->next = p1->next;
+ } else {
+ adpt_post_wait_queue = p1->next;
+ }
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
+
+ kfree(wait_data);
+
+ return status;
+}
+
+
+static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len)
+{
+
+ u32 m = EMPTY_QUEUE;
+ u32 __iomem *msg;
+ ulong timeout = jiffies + 30*HZ;
+ do {
+ rmb();
+ m = readl(pHba->post_port);
+ if (m != EMPTY_QUEUE) {
+ break;
+ }
+ if(time_after(jiffies,timeout)){
+ printk(KERN_WARNING"dpti%d: Timeout waiting for message frame!\n", pHba->unit);
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while(m == EMPTY_QUEUE);
+
+ msg = pHba->msg_addr_virt + m;
+ memcpy_toio(msg, data, len);
+ wmb();
+
+ //post message
+ writel(m, pHba->post_port);
+ wmb();
+
+ return 0;
+}
+
+
+static void adpt_i2o_post_wait_complete(u32 context, int status)
+{
+ struct adpt_i2o_post_wait_data *p1 = NULL;
+ /*
+ * We need to search through the adpt_post_wait
+ * queue to see if the given message is still
+ * outstanding. If not, it means that the IOP
+ * took longer to respond to the message than we
+ * had allowed and timer has already expired.
+ * Not much we can do about that except log
+ * it for debug purposes, increase timeout, and recompile
+ *
+ * Lock needed to keep anyone from moving queue pointers
+ * around while we're looking through them.
+ */
+
+ context &= 0x7fff;
+
+ spin_lock(&adpt_post_wait_lock);
+ for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
+ if(p1->id == context) {
+ p1->status = status;
+ spin_unlock(&adpt_post_wait_lock);
+ wake_up_interruptible(p1->wq);
+ return;
+ }
+ }
+ spin_unlock(&adpt_post_wait_lock);
+ // If this happens we lose commands that probably really completed
+ printk(KERN_DEBUG"dpti: Could Not find task %d in wait queue\n",context);
+ printk(KERN_DEBUG" Tasks in wait queue:\n");
+ for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
+ printk(KERN_DEBUG" %d\n",p1->id);
+ }
+ return;
+}
+
+static s32 adpt_i2o_reset_hba(adpt_hba* pHba)
+{
+ u32 msg[8];
+ u8* status;
+ u32 m = EMPTY_QUEUE ;
+ ulong timeout = jiffies + (TMOUT_IOPRESET*HZ);
+
+ if(pHba->initialized == FALSE) { // First time reset should be quick
+ timeout = jiffies + (25*HZ);
+ } else {
+ adpt_i2o_quiesce_hba(pHba);
+ }
+
+ do {
+ rmb();
+ m = readl(pHba->post_port);
+ if (m != EMPTY_QUEUE) {
+ break;
+ }
+ if(time_after(jiffies,timeout)){
+ printk(KERN_WARNING"Timeout waiting for message!\n");
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (m == EMPTY_QUEUE);
+
+ status = (u8*)kmalloc(4, GFP_KERNEL|ADDR32);
+ if(status == NULL) {
+ adpt_send_nop(pHba, m);
+ printk(KERN_ERR"IOP reset failed - no free memory.\n");
+ return -ENOMEM;
+ }
+ memset(status,0,4);
+
+ msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]=0;
+ msg[3]=0;
+ msg[4]=0;
+ msg[5]=0;
+ msg[6]=virt_to_bus(status);
+ msg[7]=0;
+
+ memcpy_toio(pHba->msg_addr_virt+m, msg, sizeof(msg));
+ wmb();
+ writel(m, pHba->post_port);
+ wmb();
+
+ while(*status == 0){
+ if(time_after(jiffies,timeout)){
+ printk(KERN_WARNING"%s: IOP Reset Timeout\n",pHba->name);
+ kfree(status);
+ return -ETIMEDOUT;
+ }
+ rmb();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if(*status == 0x01 /*I2O_EXEC_IOP_RESET_IN_PROGRESS*/) {
+ PDEBUG("%s: Reset in progress...\n", pHba->name);
+ // Here we wait for message frame to become available
+ // indicated that reset has finished
+ do {
+ rmb();
+ m = readl(pHba->post_port);
+ if (m != EMPTY_QUEUE) {
+ break;
+ }
+ if(time_after(jiffies,timeout)){
+ printk(KERN_ERR "%s:Timeout waiting for IOP Reset.\n",pHba->name);
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (m == EMPTY_QUEUE);
+ // Flush the offset
+ adpt_send_nop(pHba, m);
+ }
+ adpt_i2o_status_get(pHba);
+ if(*status == 0x02 ||
+ pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
+ printk(KERN_WARNING"%s: Reset reject, trying to clear\n",
+ pHba->name);
+ } else {
+ PDEBUG("%s: Reset completed.\n", pHba->name);
+ }
+
+ kfree(status);
+#ifdef UARTDELAY
+ // This delay is to allow someone attached to the card through the debug UART to
+ // set up the dump levels that they want before the rest of the initialization sequence
+ adpt_delay(20000);
+#endif
+ return 0;
+}
+
+
+static int adpt_i2o_parse_lct(adpt_hba* pHba)
+{
+ int i;
+ int max;
+ int tid;
+ struct i2o_device *d;
+ i2o_lct *lct = pHba->lct;
+ u8 bus_no = 0;
+ s16 scsi_id;
+ s16 scsi_lun;
+ u32 buf[10]; // larger than 7, or 8 ...
+ struct adpt_device* pDev;
+
+ if (lct == NULL) {
+ printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
+ return -1;
+ }
+
+ max = lct->table_size;
+ max -= 3;
+ max /= 9;
+
+ for(i=0;i<max;i++) {
+ if( lct->lct_entry[i].user_tid != 0xfff){
+ /*
+ * If we have hidden devices, we need to inform the upper layers about
+ * the possible maximum id reference to handle device access when
+ * an array is disassembled. This code has no other purpose but to
+ * allow us future access to devices that are currently hidden
+ * behind arrays, hotspares or have not been configured (JBOD mode).
+ */
+ if( lct->lct_entry[i].class_id != I2O_CLASS_RANDOM_BLOCK_STORAGE &&
+ lct->lct_entry[i].class_id != I2O_CLASS_SCSI_PERIPHERAL &&
+ lct->lct_entry[i].class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
+ continue;
+ }
+ tid = lct->lct_entry[i].tid;
+ // I2O_DPT_DEVICE_INFO_GROUP_NO;
+ if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
+ continue;
+ }
+ bus_no = buf[0]>>16;
+ scsi_id = buf[1];
+ scsi_lun = (buf[2]>>8 )&0xff;
+ if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
+ printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no);
+ continue;
+ }
+ if (scsi_id >= MAX_ID){
+ printk(KERN_WARNING"%s: SCSI ID %d out of range \n", pHba->name, bus_no);
+ continue;
+ }
+ if(bus_no > pHba->top_scsi_channel){
+ pHba->top_scsi_channel = bus_no;
+ }
+ if(scsi_id > pHba->top_scsi_id){
+ pHba->top_scsi_id = scsi_id;
+ }
+ if(scsi_lun > pHba->top_scsi_lun){
+ pHba->top_scsi_lun = scsi_lun;
+ }
+ continue;
+ }
+ d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
+ if(d==NULL)
+ {
+ printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name);
+ return -ENOMEM;
+ }
+
+ d->controller = (void*)pHba;
+ d->next = NULL;
+
+ memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
+
+ d->flags = 0;
+ tid = d->lct_data.tid;
+ adpt_i2o_report_hba_unit(pHba, d);
+ adpt_i2o_install_device(pHba, d);
+ }
+ bus_no = 0;
+ for(d = pHba->devices; d ; d = d->next) {
+ if(d->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT ||
+ d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PORT){
+ tid = d->lct_data.tid;
+ // TODO get the bus_no from hrt-but for now they are in order
+ //bus_no =
+ if(bus_no > pHba->top_scsi_channel){
+ pHba->top_scsi_channel = bus_no;
+ }
+ pHba->channel[bus_no].type = d->lct_data.class_id;
+ pHba->channel[bus_no].tid = tid;
+ if(adpt_i2o_query_scalar(pHba, tid, 0x0200, -1, buf, 28)>=0)
+ {
+ pHba->channel[bus_no].scsi_id = buf[1];
+ PDEBUG("Bus %d - SCSI ID %d.\n", bus_no, buf[1]);
+ }
+ // TODO remove - this is just until we get from hrt
+ bus_no++;
+ if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
+ printk(KERN_WARNING"%s: Channel number %d out of range - LCT\n", pHba->name, bus_no);
+ break;
+ }
+ }
+ }
+
+ // Setup adpt_device table
+ for(d = pHba->devices; d ; d = d->next) {
+ if(d->lct_data.class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
+ d->lct_data.class_id == I2O_CLASS_SCSI_PERIPHERAL ||
+ d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
+
+ tid = d->lct_data.tid;
+ scsi_id = -1;
+ // I2O_DPT_DEVICE_INFO_GROUP_NO;
+ if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) {
+ bus_no = buf[0]>>16;
+ scsi_id = buf[1];
+ scsi_lun = (buf[2]>>8 )&0xff;
+ if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
+ continue;
+ }
+ if (scsi_id >= MAX_ID) {
+ continue;
+ }
+ if( pHba->channel[bus_no].device[scsi_id] == NULL){
+ pDev = kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
+ if(pDev == NULL) {
+ return -ENOMEM;
+ }
+ pHba->channel[bus_no].device[scsi_id] = pDev;
+ memset(pDev,0,sizeof(struct adpt_device));
+ } else {
+ for( pDev = pHba->channel[bus_no].device[scsi_id];
+ pDev->next_lun; pDev = pDev->next_lun){
+ }
+ pDev->next_lun = kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
+ if(pDev->next_lun == NULL) {
+ return -ENOMEM;
+ }
+ memset(pDev->next_lun,0,sizeof(struct adpt_device));
+ pDev = pDev->next_lun;
+ }
+ pDev->tid = tid;
+ pDev->scsi_channel = bus_no;
+ pDev->scsi_id = scsi_id;
+ pDev->scsi_lun = scsi_lun;
+ pDev->pI2o_dev = d;
+ d->owner = pDev;
+ pDev->type = (buf[0])&0xff;
+ pDev->flags = (buf[0]>>8)&0xff;
+ if(scsi_id > pHba->top_scsi_id){
+ pHba->top_scsi_id = scsi_id;
+ }
+ if(scsi_lun > pHba->top_scsi_lun){
+ pHba->top_scsi_lun = scsi_lun;
+ }
+ }
+ if(scsi_id == -1){
+ printk(KERN_WARNING"Could not find SCSI ID for %s\n",
+ d->lct_data.identity_tag);
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Each I2O controller has a chain of devices on it - these match
+ * the useful parts of the LCT of the board.
+ */
+
+static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d)
+{
+ down(&adpt_configuration_lock);
+ d->controller=pHba;
+ d->owner=NULL;
+ d->next=pHba->devices;
+ d->prev=NULL;
+ if (pHba->devices != NULL){
+ pHba->devices->prev=d;
+ }
+ pHba->devices=d;
+ *d->dev_name = 0;
+
+ up(&adpt_configuration_lock);
+ return 0;
+}
+
+static int adpt_open(struct inode *inode, struct file *file)
+{
+ int minor;
+ adpt_hba* pHba;
+
+ //TODO check for root access
+ //
+ minor = iminor(inode);
+ if (minor >= hba_count) {
+ return -ENXIO;
+ }
+ down(&adpt_configuration_lock);
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (pHba->unit == minor) {
+ break; /* found adapter */
+ }
+ }
+ if (pHba == NULL) {
+ up(&adpt_configuration_lock);
+ return -ENXIO;
+ }
+
+// if(pHba->in_use){
+ // up(&adpt_configuration_lock);
+// return -EBUSY;
+// }
+
+ pHba->in_use = 1;
+ up(&adpt_configuration_lock);
+
+ return 0;
+}
+
+static int adpt_close(struct inode *inode, struct file *file)
+{
+ int minor;
+ adpt_hba* pHba;
+
+ minor = iminor(inode);
+ if (minor >= hba_count) {
+ return -ENXIO;
+ }
+ down(&adpt_configuration_lock);
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (pHba->unit == minor) {
+ break; /* found adapter */
+ }
+ }
+ up(&adpt_configuration_lock);
+ if (pHba == NULL) {
+ return -ENXIO;
+ }
+
+ pHba->in_use = 0;
+
+ return 0;
+}
+
+
+static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg)
+{
+ u32 msg[MAX_MESSAGE_SIZE];
+ u32* reply = NULL;
+ u32 size = 0;
+ u32 reply_size = 0;
+ u32 __user *user_msg = arg;
+ u32 __user * user_reply = NULL;
+ void *sg_list[pHba->sg_tablesize];
+ u32 sg_offset = 0;
+ u32 sg_count = 0;
+ int sg_index = 0;
+ u32 i = 0;
+ u32 rcode = 0;
+ void *p = NULL;
+ ulong flags = 0;
+
+ memset(&msg, 0, MAX_MESSAGE_SIZE*4);
+ // get user msg size in u32s
+ if(get_user(size, &user_msg[0])){
+ return -EFAULT;
+ }
+ size = size>>16;
+
+ user_reply = &user_msg[size];
+ if(size > MAX_MESSAGE_SIZE){
+ return -EFAULT;
+ }
+ size *= 4; // Convert to bytes
+
+ /* Copy in the user's I2O command */
+ if(copy_from_user(msg, user_msg, size)) {
+ return -EFAULT;
+ }
+ get_user(reply_size, &user_reply[0]);
+ reply_size = reply_size>>16;
+ if(reply_size > REPLY_FRAME_SIZE){
+ reply_size = REPLY_FRAME_SIZE;
+ }
+ reply_size *= 4;
+ reply = kmalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL);
+ if(reply == NULL) {
+ printk(KERN_WARNING"%s: Could not allocate reply buffer\n",pHba->name);
+ return -ENOMEM;
+ }
+ memset(reply,0,REPLY_FRAME_SIZE*4);
+ sg_offset = (msg[0]>>4)&0xf;
+ msg[2] = 0x40000000; // IOCTL context
+ msg[3] = (u32)reply;
+ memset(sg_list,0, sizeof(sg_list[0])*pHba->sg_tablesize);
+ if(sg_offset) {
+ // TODO 64bit fix
+ struct sg_simple_element *sg = (struct sg_simple_element*) (msg+sg_offset);
+ sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
+ if (sg_count > pHba->sg_tablesize){
+ printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count);
+ kfree (reply);
+ return -EINVAL;
+ }
+
+ for(i = 0; i < sg_count; i++) {
+ int sg_size;
+
+ if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) {
+ printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i, sg[i].flag_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ sg_size = sg[i].flag_count & 0xffffff;
+ /* Allocate memory for the transfer */
+ p = kmalloc(sg_size, GFP_KERNEL|ADDR32);
+ if(!p) {
+ printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ pHba->name,sg_size,i,sg_count);
+ rcode = -ENOMEM;
+ goto cleanup;
+ }
+ sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
+ /* Copy in the user's SG buffer if necessary */
+ if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
+ // TODO 64bit fix
+ if (copy_from_user(p,(void __user *)sg[i].addr_bus, sg_size)) {
+ printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ }
+ //TODO 64bit fix
+ sg[i].addr_bus = (u32)virt_to_bus(p);
+ }
+ }
+
+ do {
+ if(pHba->host)
+ spin_lock_irqsave(pHba->host->host_lock, flags);
+ // This state stops any new commands from enterring the
+ // controller while processing the ioctl
+// pHba->state |= DPTI_STATE_IOCTL;
+// We can't set this now - The scsi subsystem sets host_blocked and
+// the queue empties and stops. We need a way to restart the queue
+ rcode = adpt_i2o_post_wait(pHba, msg, size, FOREVER);
+ if (rcode != 0)
+ printk("adpt_i2o_passthru: post wait failed %d %p\n",
+ rcode, reply);
+// pHba->state &= ~DPTI_STATE_IOCTL;
+ if(pHba->host)
+ spin_unlock_irqrestore(pHba->host->host_lock, flags);
+ } while(rcode == -ETIMEDOUT);
+
+ if(rcode){
+ goto cleanup;
+ }
+
+ if(sg_offset) {
+ /* Copy back the Scatter Gather buffers back to user space */
+ u32 j;
+ // TODO 64bit fix
+ struct sg_simple_element* sg;
+ int sg_size;
+
+ // re-acquire the original message to handle correctly the sg copy operation
+ memset(&msg, 0, MAX_MESSAGE_SIZE*4);
+ // get user msg size in u32s
+ if(get_user(size, &user_msg[0])){
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ size = size>>16;
+ size *= 4;
+ /* Copy in the user's I2O command */
+ if (copy_from_user (msg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element*)(msg + sg_offset);
+ for (j = 0; j < sg_count; j++) {
+ /* Copy out the SG list to user's buffer if necessary */
+ if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
+ sg_size = sg[j].flag_count & 0xffffff;
+ // TODO 64bit fix
+ if (copy_to_user((void __user *)sg[j].addr_bus,sg_list[j], sg_size)) {
+ printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ /* Copy back the reply to user space */
+ if (reply_size) {
+ // we wrote our own values for context - now restore the user supplied ones
+ if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) {
+ printk(KERN_WARNING"%s: Could not copy message context FROM user\n",pHba->name);
+ rcode = -EFAULT;
+ }
+ if(copy_to_user(user_reply, reply, reply_size)) {
+ printk(KERN_WARNING"%s: Could not copy reply TO user\n",pHba->name);
+ rcode = -EFAULT;
+ }
+ }
+
+
+cleanup:
+ if (rcode != -ETIME && rcode != -EINTR)
+ kfree (reply);
+ while(sg_index) {
+ if(sg_list[--sg_index]) {
+ if (rcode != -ETIME && rcode != -EINTR)
+ kfree(sg_list[sg_index]);
+ }
+ }
+ return rcode;
+}
+
+
+/*
+ * This routine returns information about the system. This does not effect
+ * any logic and if the info is wrong - it doesn't matter.
+ */
+
+/* Get all the info we can not get from kernel services */
+static int adpt_system_info(void __user *buffer)
+{
+ sysInfo_S si;
+
+ memset(&si, 0, sizeof(si));
+
+ si.osType = OS_LINUX;
+ si.osMajorVersion = (u8) (LINUX_VERSION_CODE >> 16);
+ si.osMinorVersion = (u8) (LINUX_VERSION_CODE >> 8 & 0x0ff);
+ si.osRevision = (u8) (LINUX_VERSION_CODE & 0x0ff);
+ si.busType = SI_PCI_BUS;
+ si.processorFamily = DPTI_sig.dsProcessorFamily;
+
+#if defined __i386__
+ adpt_i386_info(&si);
+#elif defined (__ia64__)
+ adpt_ia64_info(&si);
+#elif defined(__sparc__)
+ adpt_sparc_info(&si);
+#elif defined (__alpha__)
+ adpt_alpha_info(&si);
+#else
+ si.processorType = 0xff ;
+#endif
+ if(copy_to_user(buffer, &si, sizeof(si))){
+ printk(KERN_WARNING"dpti: Could not copy buffer TO user\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+#if defined __ia64__
+static void adpt_ia64_info(sysInfo_S* si)
+{
+ // This is all the info we need for now
+ // We will add more info as our new
+ // managmenent utility requires it
+ si->processorType = PROC_IA64;
+}
+#endif
+
+
+#if defined __sparc__
+static void adpt_sparc_info(sysInfo_S* si)
+{
+ // This is all the info we need for now
+ // We will add more info as our new
+ // managmenent utility requires it
+ si->processorType = PROC_ULTRASPARC;
+}
+#endif
+
+#if defined __alpha__
+static void adpt_alpha_info(sysInfo_S* si)
+{
+ // This is all the info we need for now
+ // We will add more info as our new
+ // managmenent utility requires it
+ si->processorType = PROC_ALPHA;
+}
+#endif
+
+#if defined __i386__
+
+static void adpt_i386_info(sysInfo_S* si)
+{
+ // This is all the info we need for now
+ // We will add more info as our new
+ // managmenent utility requires it
+ switch (boot_cpu_data.x86) {
+ case CPU_386:
+ si->processorType = PROC_386;
+ break;
+ case CPU_486:
+ si->processorType = PROC_486;
+ break;
+ case CPU_586:
+ si->processorType = PROC_PENTIUM;
+ break;
+ default: // Just in case
+ si->processorType = PROC_PENTIUM;
+ break;
+ }
+}
+
+#endif
+
+
+static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg)
+{
+ int minor;
+ int error = 0;
+ adpt_hba* pHba;
+ ulong flags = 0;
+ void __user *argp = (void __user *)arg;
+
+ minor = iminor(inode);
+ if (minor >= DPTI_MAX_HBA){
+ return -ENXIO;
+ }
+ down(&adpt_configuration_lock);
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (pHba->unit == minor) {
+ break; /* found adapter */
+ }
+ }
+ up(&adpt_configuration_lock);
+ if(pHba == NULL){
+ return -ENXIO;
+ }
+
+ while((volatile u32) pHba->state & DPTI_STATE_RESET ) {
+ set_task_state(current,TASK_UNINTERRUPTIBLE);
+ schedule_timeout(2);
+
+ }
+
+ switch (cmd) {
+ // TODO: handle 3 cases
+ case DPT_SIGNATURE:
+ if (copy_to_user(argp, &DPTI_sig, sizeof(DPTI_sig))) {
+ return -EFAULT;
+ }
+ break;
+ case I2OUSRCMD:
+ return adpt_i2o_passthru(pHba, argp);
+
+ case DPT_CTRLINFO:{
+ drvrHBAinfo_S HbaInfo;
+
+#define FLG_OSD_PCI_VALID 0x0001
+#define FLG_OSD_DMA 0x0002
+#define FLG_OSD_I2O 0x0004
+ memset(&HbaInfo, 0, sizeof(HbaInfo));
+ HbaInfo.drvrHBAnum = pHba->unit;
+ HbaInfo.baseAddr = (ulong) pHba->base_addr_phys;
+ HbaInfo.blinkState = adpt_read_blink_led(pHba);
+ HbaInfo.pciBusNum = pHba->pDev->bus->number;
+ HbaInfo.pciDeviceNum=PCI_SLOT(pHba->pDev->devfn);
+ HbaInfo.Interrupt = pHba->pDev->irq;
+ HbaInfo.hbaFlags = FLG_OSD_PCI_VALID | FLG_OSD_DMA | FLG_OSD_I2O;
+ if(copy_to_user(argp, &HbaInfo, sizeof(HbaInfo))){
+ printk(KERN_WARNING"%s: Could not copy HbaInfo TO user\n",pHba->name);
+ return -EFAULT;
+ }
+ break;
+ }
+ case DPT_SYSINFO:
+ return adpt_system_info(argp);
+ case DPT_BLINKLED:{
+ u32 value;
+ value = (u32)adpt_read_blink_led(pHba);
+ if (copy_to_user(argp, &value, sizeof(value))) {
+ return -EFAULT;
+ }
+ break;
+ }
+ case I2ORESETCMD:
+ if(pHba->host)
+ spin_lock_irqsave(pHba->host->host_lock, flags);
+ adpt_hba_reset(pHba);
+ if(pHba->host)
+ spin_unlock_irqrestore(pHba->host->host_lock, flags);
+ break;
+ case I2ORESCANCMD:
+ adpt_rescan(pHba);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return error;
+}
+
+
+static irqreturn_t adpt_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct scsi_cmnd* cmd;
+ adpt_hba* pHba = dev_id;
+ u32 m;
+ ulong reply;
+ u32 status=0;
+ u32 context;
+ ulong flags = 0;
+ int handled = 0;
+
+ if (pHba == NULL){
+ printk(KERN_WARNING"adpt_isr: NULL dev_id\n");
+ return IRQ_NONE;
+ }
+ if(pHba->host)
+ spin_lock_irqsave(pHba->host->host_lock, flags);
+
+ while( readl(pHba->irq_mask) & I2O_INTERRUPT_PENDING_B) {
+ m = readl(pHba->reply_port);
+ if(m == EMPTY_QUEUE){
+ // Try twice then give up
+ rmb();
+ m = readl(pHba->reply_port);
+ if(m == EMPTY_QUEUE){
+ // This really should not happen
+ printk(KERN_ERR"dpti: Could not get reply frame\n");
+ goto out;
+ }
+ }
+ reply = (ulong)bus_to_virt(m);
+
+ if (readl(reply) & MSG_FAIL) {
+ u32 old_m = readl(reply+28);
+ ulong msg;
+ u32 old_context;
+ PDEBUG("%s: Failed message\n",pHba->name);
+ if(old_m >= 0x100000){
+ printk(KERN_ERR"%s: Bad preserved MFA (%x)- dropping frame\n",pHba->name,old_m);
+ writel(m,pHba->reply_port);
+ continue;
+ }
+ // Transaction context is 0 in failed reply frame
+ msg = (ulong)(pHba->msg_addr_virt + old_m);
+ old_context = readl(msg+12);
+ writel(old_context, reply+12);
+ adpt_send_nop(pHba, old_m);
+ }
+ context = readl(reply+8);
+ if(context & 0x40000000){ // IOCTL
+ ulong p = (ulong)(readl(reply+12));
+ if( p != 0) {
+ memcpy((void*)p, (void*)reply, REPLY_FRAME_SIZE * 4);
+ }
+ // All IOCTLs will also be post wait
+ }
+ if(context & 0x80000000){ // Post wait message
+ status = readl(reply+16);
+ if(status >> 24){
+ status &= 0xffff; /* Get detail status */
+ } else {
+ status = I2O_POST_WAIT_OK;
+ }
+ if(!(context & 0x40000000)) {
+ cmd = (struct scsi_cmnd*) readl(reply+12);
+ if(cmd != NULL) {
+ printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
+ }
+ }
+ adpt_i2o_post_wait_complete(context, status);
+ } else { // SCSI message
+ cmd = (struct scsi_cmnd*) readl(reply+12);
+ if(cmd != NULL){
+ if(cmd->serial_number != 0) { // If not timedout
+ adpt_i2o_to_scsi(reply, cmd);
+ }
+ }
+ }
+ writel(m, pHba->reply_port);
+ wmb();
+ rmb();
+ }
+ handled = 1;
+out: if(pHba->host)
+ spin_unlock_irqrestore(pHba->host->host_lock, flags);
+ return IRQ_RETVAL(handled);
+}
+
+static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* d)
+{
+ int i;
+ u32 msg[MAX_MESSAGE_SIZE];
+ u32* mptr;
+ u32 *lenptr;
+ int direction;
+ int scsidir;
+ u32 len;
+ u32 reqlen;
+ s32 rcode;
+
+ memset(msg, 0 , sizeof(msg));
+ len = cmd->request_bufflen;
+ direction = 0x00000000;
+
+ scsidir = 0x00000000; // DATA NO XFER
+ if(len) {
+ /*
+ * Set SCBFlags to indicate if data is being transferred
+ * in or out, or no data transfer
+ * Note: Do not have to verify index is less than 0 since
+ * cmd->cmnd[0] is an unsigned char
+ */
+ switch(cmd->sc_data_direction){
+ case DMA_FROM_DEVICE:
+ scsidir =0x40000000; // DATA IN (iop<--dev)
+ break;
+ case DMA_TO_DEVICE:
+ direction=0x04000000; // SGL OUT
+ scsidir =0x80000000; // DATA OUT (iop-->dev)
+ break;
+ case DMA_NONE:
+ break;
+ case DMA_BIDIRECTIONAL:
+ scsidir =0x40000000; // DATA IN (iop<--dev)
+ // Assume In - and continue;
+ break;
+ default:
+ printk(KERN_WARNING"%s: scsi opcode 0x%x not supported.\n",
+ pHba->name, cmd->cmnd[0]);
+ cmd->result = (DID_OK <<16) | (INITIATOR_ERROR << 8);
+ cmd->scsi_done(cmd);
+ return 0;
+ }
+ }
+ // msg[0] is set later
+ // I2O_CMD_SCSI_EXEC
+ msg[1] = ((0xff<<24)|(HOST_TID<<12)|d->tid);
+ msg[2] = 0;
+ msg[3] = (u32)cmd; /* We want the SCSI control block back */
+ // Our cards use the transaction context as the tag for queueing
+ // Adaptec/DPT Private stuff
+ msg[4] = I2O_CMD_SCSI_EXEC|(DPT_ORGANIZATION_ID<<16);
+ msg[5] = d->tid;
+ /* Direction, disconnect ok | sense data | simple queue , CDBLen */
+ // I2O_SCB_FLAG_ENABLE_DISCONNECT |
+ // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG |
+ // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
+ msg[6] = scsidir|0x20a00000|cmd->cmd_len;
+
+ mptr=msg+7;
+
+ // Write SCSI command into the message - always 16 byte block
+ memset(mptr, 0, 16);
+ memcpy(mptr, cmd->cmnd, cmd->cmd_len);
+ mptr+=4;
+ lenptr=mptr++; /* Remember me - fill in when we know */
+ reqlen = 14; // SINGLE SGE
+ /* Now fill in the SGList and command */
+ if(cmd->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *)cmd->request_buffer;
+ int sg_count = pci_map_sg(pHba->pDev, sg, cmd->use_sg,
+ cmd->sc_data_direction);
+
+
+ len = 0;
+ for(i = 0 ; i < sg_count; i++) {
+ *mptr++ = direction|0x10000000|sg_dma_len(sg);
+ len+=sg_dma_len(sg);
+ *mptr++ = sg_dma_address(sg);
+ sg++;
+ }
+ /* Make this an end of list */
+ mptr[-2] = direction|0xD0000000|sg_dma_len(sg-1);
+ reqlen = mptr - msg;
+ *lenptr = len;
+
+ if(cmd->underflow && len != cmd->underflow){
+ printk(KERN_WARNING"Cmd len %08X Cmd underflow %08X\n",
+ len, cmd->underflow);
+ }
+ } else {
+ *lenptr = len = cmd->request_bufflen;
+ if(len == 0) {
+ reqlen = 12;
+ } else {
+ *mptr++ = 0xD0000000|direction|cmd->request_bufflen;
+ *mptr++ = pci_map_single(pHba->pDev,
+ cmd->request_buffer,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+ }
+ }
+
+ /* Stick the headers on */
+ msg[0] = reqlen<<16 | ((reqlen > 12) ? SGL_OFFSET_12 : SGL_OFFSET_0);
+
+ // Send it on it's way
+ rcode = adpt_i2o_post_this(pHba, msg, reqlen<<2);
+ if (rcode == 0) {
+ return 0;
+ }
+ return rcode;
+}
+
+
+static s32 adpt_scsi_register(adpt_hba* pHba,struct scsi_host_template * sht)
+{
+ struct Scsi_Host *host = NULL;
+
+ host = scsi_register(sht, sizeof(adpt_hba*));
+ if (host == NULL) {
+ printk ("%s: scsi_register returned NULL\n",pHba->name);
+ return -1;
+ }
+ host->hostdata[0] = (unsigned long)pHba;
+ pHba->host = host;
+
+ host->irq = pHba->pDev->irq;
+ /* no IO ports, so don't have to set host->io_port and
+ * host->n_io_port
+ */
+ host->io_port = 0;
+ host->n_io_port = 0;
+ /* see comments in hosts.h */
+ host->max_id = 16;
+ host->max_lun = 256;
+ host->max_channel = pHba->top_scsi_channel + 1;
+ host->cmd_per_lun = 1;
+ host->unique_id = (uint) pHba;
+ host->sg_tablesize = pHba->sg_tablesize;
+ host->can_queue = pHba->post_fifo_size;
+
+ return 0;
+}
+
+
+static s32 adpt_i2o_to_scsi(ulong reply, struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ u32 hba_status;
+ u32 dev_status;
+ u32 reply_flags = readl(reply) & 0xff00; // Leave it shifted up 8 bits
+ // I know this would look cleaner if I just read bytes
+ // but the model I have been using for all the rest of the
+ // io is in 4 byte words - so I keep that model
+ u16 detailed_status = readl(reply+16) &0xffff;
+ dev_status = (detailed_status & 0xff);
+ hba_status = detailed_status >> 8;
+
+ // calculate resid for sg
+ cmd->resid = cmd->request_bufflen - readl(reply+5);
+
+ pHba = (adpt_hba*) cmd->device->host->hostdata[0];
+
+ cmd->sense_buffer[0] = '\0'; // initialize sense valid flag to false
+
+ if(!(reply_flags & MSG_FAIL)) {
+ switch(detailed_status & I2O_SCSI_DSC_MASK) {
+ case I2O_SCSI_DSC_SUCCESS:
+ cmd->result = (DID_OK << 16);
+ // handle underflow
+ if(readl(reply+5) < cmd->underflow ) {
+ cmd->result = (DID_ERROR <<16);
+ printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name);
+ }
+ break;
+ case I2O_SCSI_DSC_REQUEST_ABORTED:
+ cmd->result = (DID_ABORT << 16);
+ break;
+ case I2O_SCSI_DSC_PATH_INVALID:
+ case I2O_SCSI_DSC_DEVICE_NOT_PRESENT:
+ case I2O_SCSI_DSC_SELECTION_TIMEOUT:
+ case I2O_SCSI_DSC_COMMAND_TIMEOUT:
+ case I2O_SCSI_DSC_NO_ADAPTER:
+ case I2O_SCSI_DSC_RESOURCE_UNAVAILABLE:
+ printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%d) hba status=0x%x, dev status=0x%x, cmd=0x%x\n",
+ pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]);
+ cmd->result = (DID_TIME_OUT << 16);
+ break;
+ case I2O_SCSI_DSC_ADAPTER_BUSY:
+ case I2O_SCSI_DSC_BUS_BUSY:
+ cmd->result = (DID_BUS_BUSY << 16);
+ break;
+ case I2O_SCSI_DSC_SCSI_BUS_RESET:
+ case I2O_SCSI_DSC_BDR_MESSAGE_SENT:
+ cmd->result = (DID_RESET << 16);
+ break;
+ case I2O_SCSI_DSC_PARITY_ERROR_FAILURE:
+ printk(KERN_WARNING"%s: SCSI CMD parity error\n",pHba->name);
+ cmd->result = (DID_PARITY << 16);
+ break;
+ case I2O_SCSI_DSC_UNABLE_TO_ABORT:
+ case I2O_SCSI_DSC_COMPLETE_WITH_ERROR:
+ case I2O_SCSI_DSC_UNABLE_TO_TERMINATE:
+ case I2O_SCSI_DSC_MR_MESSAGE_RECEIVED:
+ case I2O_SCSI_DSC_AUTOSENSE_FAILED:
+ case I2O_SCSI_DSC_DATA_OVERRUN:
+ case I2O_SCSI_DSC_UNEXPECTED_BUS_FREE:
+ case I2O_SCSI_DSC_SEQUENCE_FAILURE:
+ case I2O_SCSI_DSC_REQUEST_LENGTH_ERROR:
+ case I2O_SCSI_DSC_PROVIDE_FAILURE:
+ case I2O_SCSI_DSC_REQUEST_TERMINATED:
+ case I2O_SCSI_DSC_IDE_MESSAGE_SENT:
+ case I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT:
+ case I2O_SCSI_DSC_MESSAGE_RECEIVED:
+ case I2O_SCSI_DSC_INVALID_CDB:
+ case I2O_SCSI_DSC_LUN_INVALID:
+ case I2O_SCSI_DSC_SCSI_TID_INVALID:
+ case I2O_SCSI_DSC_FUNCTION_UNAVAILABLE:
+ case I2O_SCSI_DSC_NO_NEXUS:
+ case I2O_SCSI_DSC_CDB_RECEIVED:
+ case I2O_SCSI_DSC_LUN_ALREADY_ENABLED:
+ case I2O_SCSI_DSC_QUEUE_FROZEN:
+ case I2O_SCSI_DSC_REQUEST_INVALID:
+ default:
+ printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
+ pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
+ hba_status, dev_status, cmd->cmnd[0]);
+ cmd->result = (DID_ERROR << 16);
+ break;
+ }
+
+ // copy over the request sense data if it was a check
+ // condition status
+ if(dev_status == 0x02 /*CHECK_CONDITION*/) {
+ u32 len = sizeof(cmd->sense_buffer);
+ len = (len > 40) ? 40 : len;
+ // Copy over the sense data
+ memcpy(cmd->sense_buffer, (void*)(reply+28) , len);
+ if(cmd->sense_buffer[0] == 0x70 /* class 7 */ &&
+ cmd->sense_buffer[2] == DATA_PROTECT ){
+ /* This is to handle an array failed */
+ cmd->result = (DID_TIME_OUT << 16);
+ printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
+ pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
+ hba_status, dev_status, cmd->cmnd[0]);
+
+ }
+ }
+ } else {
+ /* In this condtion we could not talk to the tid
+ * the card rejected it. We should signal a retry
+ * for a limitted number of retries.
+ */
+ cmd->result = (DID_TIME_OUT << 16);
+ printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%d) tid=%d, cmd=0x%x\n",
+ pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
+ ((struct adpt_device*)(cmd->device->hostdata))->tid, cmd->cmnd[0]);
+ }
+
+ cmd->result |= (dev_status);
+
+ if(cmd->scsi_done != NULL){
+ cmd->scsi_done(cmd);
+ }
+ return cmd->result;
+}
+
+
+static s32 adpt_rescan(adpt_hba* pHba)
+{
+ s32 rcode;
+ ulong flags = 0;
+
+ if(pHba->host)
+ spin_lock_irqsave(pHba->host->host_lock, flags);
+ if ((rcode=adpt_i2o_lct_get(pHba)) < 0)
+ goto out;
+ if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0)
+ goto out;
+ rcode = 0;
+out: if(pHba->host)
+ spin_unlock_irqrestore(pHba->host->host_lock, flags);
+ return rcode;
+}
+
+
+static s32 adpt_i2o_reparse_lct(adpt_hba* pHba)
+{
+ int i;
+ int max;
+ int tid;
+ struct i2o_device *d;
+ i2o_lct *lct = pHba->lct;
+ u8 bus_no = 0;
+ s16 scsi_id;
+ s16 scsi_lun;
+ u32 buf[10]; // at least 8 u32's
+ struct adpt_device* pDev = NULL;
+ struct i2o_device* pI2o_dev = NULL;
+
+ if (lct == NULL) {
+ printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
+ return -1;
+ }
+
+ max = lct->table_size;
+ max -= 3;
+ max /= 9;
+
+ // Mark each drive as unscanned
+ for (d = pHba->devices; d; d = d->next) {
+ pDev =(struct adpt_device*) d->owner;
+ if(!pDev){
+ continue;
+ }
+ pDev->state |= DPTI_DEV_UNSCANNED;
+ }
+
+ printk(KERN_INFO "%s: LCT has %d entries.\n", pHba->name,max);
+
+ for(i=0;i<max;i++) {
+ if( lct->lct_entry[i].user_tid != 0xfff){
+ continue;
+ }
+
+ if( lct->lct_entry[i].class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
+ lct->lct_entry[i].class_id == I2O_CLASS_SCSI_PERIPHERAL ||
+ lct->lct_entry[i].class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
+ tid = lct->lct_entry[i].tid;
+ if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
+ printk(KERN_ERR"%s: Could not query device\n",pHba->name);
+ continue;
+ }
+ bus_no = buf[0]>>16;
+ scsi_id = buf[1];
+ scsi_lun = (buf[2]>>8 )&0xff;
+ pDev = pHba->channel[bus_no].device[scsi_id];
+ /* da lun */
+ while(pDev) {
+ if(pDev->scsi_lun == scsi_lun) {
+ break;
+ }
+ pDev = pDev->next_lun;
+ }
+ if(!pDev ) { // Something new add it
+ d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
+ if(d==NULL)
+ {
+ printk(KERN_CRIT "Out of memory for I2O device data.\n");
+ return -ENOMEM;
+ }
+
+ d->controller = (void*)pHba;
+ d->next = NULL;
+
+ memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
+
+ d->flags = 0;
+ adpt_i2o_report_hba_unit(pHba, d);
+ adpt_i2o_install_device(pHba, d);
+
+ if(bus_no >= MAX_CHANNEL) { // Something wrong skip it
+ printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no);
+ continue;
+ }
+ pDev = pHba->channel[bus_no].device[scsi_id];
+ if( pDev == NULL){
+ pDev = kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
+ if(pDev == NULL) {
+ return -ENOMEM;
+ }
+ pHba->channel[bus_no].device[scsi_id] = pDev;
+ } else {
+ while (pDev->next_lun) {
+ pDev = pDev->next_lun;
+ }
+ pDev = pDev->next_lun = kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
+ if(pDev == NULL) {
+ return -ENOMEM;
+ }
+ }
+ memset(pDev,0,sizeof(struct adpt_device));
+ pDev->tid = d->lct_data.tid;
+ pDev->scsi_channel = bus_no;
+ pDev->scsi_id = scsi_id;
+ pDev->scsi_lun = scsi_lun;
+ pDev->pI2o_dev = d;
+ d->owner = pDev;
+ pDev->type = (buf[0])&0xff;
+ pDev->flags = (buf[0]>>8)&0xff;
+ // Too late, SCSI system has made up it's mind, but what the hey ...
+ if(scsi_id > pHba->top_scsi_id){
+ pHba->top_scsi_id = scsi_id;
+ }
+ if(scsi_lun > pHba->top_scsi_lun){
+ pHba->top_scsi_lun = scsi_lun;
+ }
+ continue;
+ } // end of new i2o device
+
+ // We found an old device - check it
+ while(pDev) {
+ if(pDev->scsi_lun == scsi_lun) {
+ if(!scsi_device_online(pDev->pScsi_dev)) {
+ printk(KERN_WARNING"%s: Setting device (%d,%d,%d) back online\n",
+ pHba->name,bus_no,scsi_id,scsi_lun);
+ if (pDev->pScsi_dev) {
+ scsi_device_set_state(pDev->pScsi_dev, SDEV_RUNNING);
+ }
+ }
+ d = pDev->pI2o_dev;
+ if(d->lct_data.tid != tid) { // something changed
+ pDev->tid = tid;
+ memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
+ if (pDev->pScsi_dev) {
+ pDev->pScsi_dev->changed = TRUE;
+ pDev->pScsi_dev->removable = TRUE;
+ }
+ }
+ // Found it - mark it scanned
+ pDev->state = DPTI_DEV_ONLINE;
+ break;
+ }
+ pDev = pDev->next_lun;
+ }
+ }
+ }
+ for (pI2o_dev = pHba->devices; pI2o_dev; pI2o_dev = pI2o_dev->next) {
+ pDev =(struct adpt_device*) pI2o_dev->owner;
+ if(!pDev){
+ continue;
+ }
+ // Drive offline drives that previously existed but could not be found
+ // in the LCT table
+ if (pDev->state & DPTI_DEV_UNSCANNED){
+ pDev->state = DPTI_DEV_OFFLINE;
+ printk(KERN_WARNING"%s: Device (%d,%d,%d) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun);
+ if (pDev->pScsi_dev) {
+ scsi_device_set_state(pDev->pScsi_dev, SDEV_OFFLINE);
+ }
+ }
+ }
+ return 0;
+}
+
+static void adpt_fail_posted_scbs(adpt_hba* pHba)
+{
+ struct scsi_cmnd* cmd = NULL;
+ struct scsi_device* d = NULL;
+
+ shost_for_each_device(d, pHba->host) {
+ unsigned long flags;
+ spin_lock_irqsave(&d->list_lock, flags);
+ list_for_each_entry(cmd, &d->cmd_list, list) {
+ if(cmd->serial_number == 0){
+ continue;
+ }
+ cmd->result = (DID_OK << 16) | (QUEUE_FULL <<1);
+ cmd->scsi_done(cmd);
+ }
+ spin_unlock_irqrestore(&d->list_lock, flags);
+ }
+}
+
+
+/*============================================================================
+ * Routines from i2o subsystem
+ *============================================================================
+ */
+
+
+
+/*
+ * Bring an I2O controller into HOLD state. See the spec.
+ */
+static int adpt_i2o_activate_hba(adpt_hba* pHba)
+{
+ int rcode;
+
+ if(pHba->initialized ) {
+ if (adpt_i2o_status_get(pHba) < 0) {
+ if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
+ printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
+ return rcode;
+ }
+ if (adpt_i2o_status_get(pHba) < 0) {
+ printk(KERN_INFO "HBA not responding.\n");
+ return -1;
+ }
+ }
+
+ if(pHba->status_block->iop_state == ADAPTER_STATE_FAULTED) {
+ printk(KERN_CRIT "%s: hardware fault\n", pHba->name);
+ return -1;
+ }
+
+ if (pHba->status_block->iop_state == ADAPTER_STATE_READY ||
+ pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL ||
+ pHba->status_block->iop_state == ADAPTER_STATE_HOLD ||
+ pHba->status_block->iop_state == ADAPTER_STATE_FAILED) {
+ adpt_i2o_reset_hba(pHba);
+ if (adpt_i2o_status_get(pHba) < 0 || pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
+ printk(KERN_ERR "%s: Failed to initialize.\n", pHba->name);
+ return -1;
+ }
+ }
+ } else {
+ if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
+ printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
+ return rcode;
+ }
+
+ }
+
+ if (adpt_i2o_init_outbound_q(pHba) < 0) {
+ return -1;
+ }
+
+ /* In HOLD state */
+
+ if (adpt_i2o_hrt_get(pHba) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Bring a controller online into OPERATIONAL state.
+ */
+
+static int adpt_i2o_online_hba(adpt_hba* pHba)
+{
+ if (adpt_i2o_systab_send(pHba) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ return -1;
+ }
+ /* In READY state */
+
+ if (adpt_i2o_enable_hba(pHba) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ return -1;
+ }
+
+ /* In OPERATIONAL state */
+ return 0;
+}
+
+static s32 adpt_send_nop(adpt_hba*pHba,u32 m)
+{
+ u32 __iomem *msg;
+ ulong timeout = jiffies + 5*HZ;
+
+ while(m == EMPTY_QUEUE){
+ rmb();
+ m = readl(pHba->post_port);
+ if(m != EMPTY_QUEUE){
+ break;
+ }
+ if(time_after(jiffies,timeout)){
+ printk(KERN_ERR "%s: Timeout waiting for message frame!\n",pHba->name);
+ return 2;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ msg = (u32 __iomem *)(pHba->msg_addr_virt + m);
+ writel( THREE_WORD_MSG_SIZE | SGL_OFFSET_0,&msg[0]);
+ writel( I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0,&msg[1]);
+ writel( 0,&msg[2]);
+ wmb();
+
+ writel(m, pHba->post_port);
+ wmb();
+ return 0;
+}
+
+static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba)
+{
+ u8 *status;
+ u32 __iomem *msg = NULL;
+ int i;
+ ulong timeout = jiffies + TMOUT_INITOUTBOUND*HZ;
+ u32* ptr;
+ u32 outbound_frame; // This had to be a 32 bit address
+ u32 m;
+
+ do {
+ rmb();
+ m = readl(pHba->post_port);
+ if (m != EMPTY_QUEUE) {
+ break;
+ }
+
+ if(time_after(jiffies,timeout)){
+ printk(KERN_WARNING"%s: Timeout waiting for message frame\n",pHba->name);
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while(m == EMPTY_QUEUE);
+
+ msg=(u32 __iomem *)(pHba->msg_addr_virt+m);
+
+ status = kmalloc(4,GFP_KERNEL|ADDR32);
+ if (status==NULL) {
+ adpt_send_nop(pHba, m);
+ printk(KERN_WARNING"%s: IOP reset failed - no free memory.\n",
+ pHba->name);
+ return -ENOMEM;
+ }
+ memset(status, 0, 4);
+
+ writel(EIGHT_WORD_MSG_SIZE| SGL_OFFSET_6, &msg[0]);
+ writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, &msg[1]);
+ writel(0, &msg[2]);
+ writel(0x0106, &msg[3]); /* Transaction context */
+ writel(4096, &msg[4]); /* Host page frame size */
+ writel((REPLY_FRAME_SIZE)<<16|0x80, &msg[5]); /* Outbound msg frame size and Initcode */
+ writel(0xD0000004, &msg[6]); /* Simple SG LE, EOB */
+ writel(virt_to_bus(status), &msg[7]);
+
+ writel(m, pHba->post_port);
+ wmb();
+
+ // Wait for the reply status to come back
+ do {
+ if (*status) {
+ if (*status != 0x01 /*I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS*/) {
+ break;
+ }
+ }
+ rmb();
+ if(time_after(jiffies,timeout)){
+ printk(KERN_WARNING"%s: Timeout Initializing\n",pHba->name);
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (1);
+
+ // If the command was successful, fill the fifo with our reply
+ // message packets
+ if(*status != 0x04 /*I2O_EXEC_OUTBOUND_INIT_COMPLETE*/) {
+ kfree((void*)status);
+ return -2;
+ }
+ kfree((void*)status);
+
+ if(pHba->reply_pool != NULL){
+ kfree(pHba->reply_pool);
+ }
+
+ pHba->reply_pool = (u32*)kmalloc(pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, GFP_KERNEL|ADDR32);
+ if(!pHba->reply_pool){
+ printk(KERN_ERR"%s: Could not allocate reply pool\n",pHba->name);
+ return -1;
+ }
+ memset(pHba->reply_pool, 0 , pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4);
+
+ ptr = pHba->reply_pool;
+ for(i = 0; i < pHba->reply_fifo_size; i++) {
+ outbound_frame = (u32)virt_to_bus(ptr);
+ writel(outbound_frame, pHba->reply_port);
+ wmb();
+ ptr += REPLY_FRAME_SIZE;
+ }
+ adpt_i2o_status_get(pHba);
+ return 0;
+}
+
+
+/*
+ * I2O System Table. Contains information about
+ * all the IOPs in the system. Used to inform IOPs
+ * about each other's existence.
+ *
+ * sys_tbl_ver is the CurrentChangeIndicator that is
+ * used by IOPs to track changes.
+ */
+
+
+
+static s32 adpt_i2o_status_get(adpt_hba* pHba)
+{
+ ulong timeout;
+ u32 m;
+ u32 __iomem *msg;
+ u8 *status_block=NULL;
+ ulong status_block_bus;
+
+ if(pHba->status_block == NULL) {
+ pHba->status_block = (i2o_status_block*)
+ kmalloc(sizeof(i2o_status_block),GFP_KERNEL|ADDR32);
+ if(pHba->status_block == NULL) {
+ printk(KERN_ERR
+ "dpti%d: Get Status Block failed; Out of memory. \n",
+ pHba->unit);
+ return -ENOMEM;
+ }
+ }
+ memset(pHba->status_block, 0, sizeof(i2o_status_block));
+ status_block = (u8*)(pHba->status_block);
+ status_block_bus = virt_to_bus(pHba->status_block);
+ timeout = jiffies+TMOUT_GETSTATUS*HZ;
+ do {
+ rmb();
+ m = readl(pHba->post_port);
+ if (m != EMPTY_QUEUE) {
+ break;
+ }
+ if(time_after(jiffies,timeout)){
+ printk(KERN_ERR "%s: Timeout waiting for message !\n",
+ pHba->name);
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while(m==EMPTY_QUEUE);
+
+
+ msg=(u32 __iomem *)(pHba->msg_addr_virt+m);
+
+ writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]);
+ writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, &msg[1]);
+ writel(1, &msg[2]);
+ writel(0, &msg[3]);
+ writel(0, &msg[4]);
+ writel(0, &msg[5]);
+ writel(((u32)status_block_bus)&0xffffffff, &msg[6]);
+ writel(0, &msg[7]);
+ writel(sizeof(i2o_status_block), &msg[8]); // 88 bytes
+
+ //post message
+ writel(m, pHba->post_port);
+ wmb();
+
+ while(status_block[87]!=0xff){
+ if(time_after(jiffies,timeout)){
+ printk(KERN_ERR"dpti%d: Get status timeout.\n",
+ pHba->unit);
+ return -ETIMEDOUT;
+ }
+ rmb();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ // Set up our number of outbound and inbound messages
+ pHba->post_fifo_size = pHba->status_block->max_inbound_frames;
+ if (pHba->post_fifo_size > MAX_TO_IOP_MESSAGES) {
+ pHba->post_fifo_size = MAX_TO_IOP_MESSAGES;
+ }
+
+ pHba->reply_fifo_size = pHba->status_block->max_outbound_frames;
+ if (pHba->reply_fifo_size > MAX_FROM_IOP_MESSAGES) {
+ pHba->reply_fifo_size = MAX_FROM_IOP_MESSAGES;
+ }
+
+ // Calculate the Scatter Gather list size
+ pHba->sg_tablesize = (pHba->status_block->inbound_frame_size * 4 -40)/ sizeof(struct sg_simple_element);
+ if (pHba->sg_tablesize > SG_LIST_ELEMENTS) {
+ pHba->sg_tablesize = SG_LIST_ELEMENTS;
+ }
+
+
+#ifdef DEBUG
+ printk("dpti%d: State = ",pHba->unit);
+ switch(pHba->status_block->iop_state) {
+ case 0x01:
+ printk("INIT\n");
+ break;
+ case 0x02:
+ printk("RESET\n");
+ break;
+ case 0x04:
+ printk("HOLD\n");
+ break;
+ case 0x05:
+ printk("READY\n");
+ break;
+ case 0x08:
+ printk("OPERATIONAL\n");
+ break;
+ case 0x10:
+ printk("FAILED\n");
+ break;
+ case 0x11:
+ printk("FAULTED\n");
+ break;
+ default:
+ printk("%x (unknown!!)\n",pHba->status_block->iop_state);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Get the IOP's Logical Configuration Table
+ */
+static int adpt_i2o_lct_get(adpt_hba* pHba)
+{
+ u32 msg[8];
+ int ret;
+ u32 buf[16];
+
+ if ((pHba->lct_size == 0) || (pHba->lct == NULL)){
+ pHba->lct_size = pHba->status_block->expected_lct_size;
+ }
+ do {
+ if (pHba->lct == NULL) {
+ pHba->lct = kmalloc(pHba->lct_size, GFP_KERNEL|ADDR32);
+ if(pHba->lct == NULL) {
+ printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n",
+ pHba->name);
+ return -ENOMEM;
+ }
+ }
+ memset(pHba->lct, 0, pHba->lct_size);
+
+ msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6;
+ msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2] = 0;
+ msg[3] = 0;
+ msg[4] = 0xFFFFFFFF; /* All devices */
+ msg[5] = 0x00000000; /* Report now */
+ msg[6] = 0xD0000000|pHba->lct_size;
+ msg[7] = virt_to_bus(pHba->lct);
+
+ if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 360))) {
+ printk(KERN_ERR "%s: LCT Get failed (status=%#10x.\n",
+ pHba->name, ret);
+ printk(KERN_ERR"Adaptec: Error Reading Hardware.\n");
+ return ret;
+ }
+
+ if ((pHba->lct->table_size << 2) > pHba->lct_size) {
+ pHba->lct_size = pHba->lct->table_size << 2;
+ kfree(pHba->lct);
+ pHba->lct = NULL;
+ }
+ } while (pHba->lct == NULL);
+
+ PDEBUG("%s: Hardware resource table read.\n", pHba->name);
+
+
+ // I2O_DPT_EXEC_IOP_BUFFERS_GROUP_NO;
+ if(adpt_i2o_query_scalar(pHba, 0 , 0x8000, -1, buf, sizeof(buf))>=0) {
+ pHba->FwDebugBufferSize = buf[1];
+ pHba->FwDebugBuffer_P = pHba->base_addr_virt + buf[0];
+ pHba->FwDebugFlags_P = pHba->FwDebugBuffer_P + FW_DEBUG_FLAGS_OFFSET;
+ pHba->FwDebugBLEDvalue_P = pHba->FwDebugBuffer_P + FW_DEBUG_BLED_OFFSET;
+ pHba->FwDebugBLEDflag_P = pHba->FwDebugBLEDvalue_P + 1;
+ pHba->FwDebugStrLength_P = pHba->FwDebugBuffer_P + FW_DEBUG_STR_LENGTH_OFFSET;
+ pHba->FwDebugBuffer_P += buf[2];
+ pHba->FwDebugFlags = 0;
+ }
+
+ return 0;
+}
+
+static int adpt_i2o_build_sys_table(void)
+{
+ adpt_hba* pHba = NULL;
+ int count = 0;
+
+ sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs
+ (hba_count) * sizeof(struct i2o_sys_tbl_entry);
+
+ if(sys_tbl)
+ kfree(sys_tbl);
+
+ sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL|ADDR32);
+ if(!sys_tbl) {
+ printk(KERN_WARNING "SysTab Set failed. Out of memory.\n");
+ return -ENOMEM;
+ }
+ memset(sys_tbl, 0, sys_tbl_len);
+
+ sys_tbl->num_entries = hba_count;
+ sys_tbl->version = I2OVERSION;
+ sys_tbl->change_ind = sys_tbl_ind++;
+
+ for(pHba = hba_chain; pHba; pHba = pHba->next) {
+ // Get updated Status Block so we have the latest information
+ if (adpt_i2o_status_get(pHba)) {
+ sys_tbl->num_entries--;
+ continue; // try next one
+ }
+
+ sys_tbl->iops[count].org_id = pHba->status_block->org_id;
+ sys_tbl->iops[count].iop_id = pHba->unit + 2;
+ sys_tbl->iops[count].seg_num = 0;
+ sys_tbl->iops[count].i2o_version = pHba->status_block->i2o_version;
+ sys_tbl->iops[count].iop_state = pHba->status_block->iop_state;
+ sys_tbl->iops[count].msg_type = pHba->status_block->msg_type;
+ sys_tbl->iops[count].frame_size = pHba->status_block->inbound_frame_size;
+ sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ??
+ sys_tbl->iops[count].iop_capabilities = pHba->status_block->iop_capabilities;
+ sys_tbl->iops[count].inbound_low = (u32)virt_to_bus((void*)pHba->post_port);
+ sys_tbl->iops[count].inbound_high = (u32)((u64)virt_to_bus((void*)pHba->post_port)>>32);
+
+ count++;
+ }
+
+#ifdef DEBUG
+{
+ u32 *table = (u32*)sys_tbl;
+ printk(KERN_DEBUG"sys_tbl_len=%d in 32bit words\n",(sys_tbl_len >>2));
+ for(count = 0; count < (sys_tbl_len >>2); count++) {
+ printk(KERN_INFO "sys_tbl[%d] = %0#10x\n",
+ count, table[count]);
+ }
+}
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Dump the information block associated with a given unit (TID)
+ */
+
+static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d)
+{
+ char buf[64];
+ int unit = d->lct_data.tid;
+
+ printk(KERN_INFO "TID %3.3d ", unit);
+
+ if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 3, buf, 16)>=0)
+ {
+ buf[16]=0;
+ printk(" Vendor: %-12.12s", buf);
+ }
+ if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 4, buf, 16)>=0)
+ {
+ buf[16]=0;
+ printk(" Device: %-12.12s", buf);
+ }
+ if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 6, buf, 8)>=0)
+ {
+ buf[8]=0;
+ printk(" Rev: %-12.12s\n", buf);
+ }
+#ifdef DEBUG
+ printk(KERN_INFO "\tClass: %.21s\n", adpt_i2o_get_class_name(d->lct_data.class_id));
+ printk(KERN_INFO "\tSubclass: 0x%04X\n", d->lct_data.sub_class);
+ printk(KERN_INFO "\tFlags: ");
+
+ if(d->lct_data.device_flags&(1<<0))
+ printk("C"); // ConfigDialog requested
+ if(d->lct_data.device_flags&(1<<1))
+ printk("U"); // Multi-user capable
+ if(!(d->lct_data.device_flags&(1<<4)))
+ printk("P"); // Peer service enabled!
+ if(!(d->lct_data.device_flags&(1<<5)))
+ printk("M"); // Mgmt service enabled!
+ printk("\n");
+#endif
+}
+
+#ifdef DEBUG
+/*
+ * Do i2o class name lookup
+ */
+static const char *adpt_i2o_get_class_name(int class)
+{
+ int idx = 16;
+ static char *i2o_class_name[] = {
+ "Executive",
+ "Device Driver Module",
+ "Block Device",
+ "Tape Device",
+ "LAN Interface",
+ "WAN Interface",
+ "Fibre Channel Port",
+ "Fibre Channel Device",
+ "SCSI Device",
+ "ATE Port",
+ "ATE Device",
+ "Floppy Controller",
+ "Floppy Device",
+ "Secondary Bus Port",
+ "Peer Transport Agent",
+ "Peer Transport",
+ "Unknown"
+ };
+
+ switch(class&0xFFF) {
+ case I2O_CLASS_EXECUTIVE:
+ idx = 0; break;
+ case I2O_CLASS_DDM:
+ idx = 1; break;
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ idx = 2; break;
+ case I2O_CLASS_SEQUENTIAL_STORAGE:
+ idx = 3; break;
+ case I2O_CLASS_LAN:
+ idx = 4; break;
+ case I2O_CLASS_WAN:
+ idx = 5; break;
+ case I2O_CLASS_FIBRE_CHANNEL_PORT:
+ idx = 6; break;
+ case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
+ idx = 7; break;
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ idx = 8; break;
+ case I2O_CLASS_ATE_PORT:
+ idx = 9; break;
+ case I2O_CLASS_ATE_PERIPHERAL:
+ idx = 10; break;
+ case I2O_CLASS_FLOPPY_CONTROLLER:
+ idx = 11; break;
+ case I2O_CLASS_FLOPPY_DEVICE:
+ idx = 12; break;
+ case I2O_CLASS_BUS_ADAPTER_PORT:
+ idx = 13; break;
+ case I2O_CLASS_PEER_TRANSPORT_AGENT:
+ idx = 14; break;
+ case I2O_CLASS_PEER_TRANSPORT:
+ idx = 15; break;
+ }
+ return i2o_class_name[idx];
+}
+#endif
+
+
+static s32 adpt_i2o_hrt_get(adpt_hba* pHba)
+{
+ u32 msg[6];
+ int ret, size = sizeof(i2o_hrt);
+
+ do {
+ if (pHba->hrt == NULL) {
+ pHba->hrt=kmalloc(size, GFP_KERNEL|ADDR32);
+ if (pHba->hrt == NULL) {
+ printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", pHba->name);
+ return -ENOMEM;
+ }
+ }
+
+ msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+ msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2]= 0;
+ msg[3]= 0;
+ msg[4]= (0xD0000000 | size); /* Simple transaction */
+ msg[5]= virt_to_bus(pHba->hrt); /* Dump it here */
+
+ if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg),20))) {
+ printk(KERN_ERR "%s: Unable to get HRT (status=%#10x)\n", pHba->name, ret);
+ return ret;
+ }
+
+ if (pHba->hrt->num_entries * pHba->hrt->entry_len << 2 > size) {
+ size = pHba->hrt->num_entries * pHba->hrt->entry_len << 2;
+ kfree(pHba->hrt);
+ pHba->hrt = NULL;
+ }
+ } while(pHba->hrt == NULL);
+ return 0;
+}
+
+/*
+ * Query one scalar group value or a whole scalar group.
+ */
+static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid,
+ int group, int field, void *buf, int buflen)
+{
+ u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field };
+ u8 *resblk;
+
+ int size;
+
+ /* 8 bytes for header */
+ resblk = kmalloc(sizeof(u8) * (8+buflen), GFP_KERNEL|ADDR32);
+ if (resblk == NULL) {
+ printk(KERN_CRIT "%s: query scalar failed; Out of memory.\n", pHba->name);
+ return -ENOMEM;
+ }
+
+ if (field == -1) /* whole group */
+ opblk[4] = -1;
+
+ size = adpt_i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, pHba, tid,
+ opblk, sizeof(opblk), resblk, sizeof(u8)*(8+buflen));
+ if (size == -ETIME) {
+ printk(KERN_WARNING "%s: issue params failed; Timed out.\n", pHba->name);
+ return -ETIME;
+ } else if (size == -EINTR) {
+ printk(KERN_WARNING "%s: issue params failed; Interrupted.\n", pHba->name);
+ return -EINTR;
+ }
+
+ memcpy(buf, resblk+8, buflen); /* cut off header */
+
+ kfree(resblk);
+ if (size < 0)
+ return size;
+
+ return buflen;
+}
+
+
+/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET
+ *
+ * This function can be used for all UtilParamsGet/Set operations.
+ * The OperationBlock is given in opblk-buffer,
+ * and results are returned in resblk-buffer.
+ * Note that the minimum sized resblk is 8 bytes and contains
+ * ResultCount, ErrorInfoSize, BlockStatus and BlockSize.
+ */
+static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid,
+ void *opblk, int oplen, void *resblk, int reslen)
+{
+ u32 msg[9];
+ u32 *res = (u32 *)resblk;
+ int wait_status;
+
+ msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5;
+ msg[1] = cmd << 24 | HOST_TID << 12 | tid;
+ msg[2] = 0;
+ msg[3] = 0;
+ msg[4] = 0;
+ msg[5] = 0x54000000 | oplen; /* OperationBlock */
+ msg[6] = virt_to_bus(opblk);
+ msg[7] = 0xD0000000 | reslen; /* ResultBlock */
+ msg[8] = virt_to_bus(resblk);
+
+ if ((wait_status = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 20))) {
+ printk("adpt_i2o_issue_params: post_wait failed (%p)\n", resblk);
+ return wait_status; /* -DetailedStatus */
+ }
+
+ if (res[1]&0x00FF0000) { /* BlockStatus != SUCCESS */
+ printk(KERN_WARNING "%s: %s - Error:\n ErrorInfoSize = 0x%02x, "
+ "BlockStatus = 0x%02x, BlockSize = 0x%04x\n",
+ pHba->name,
+ (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET"
+ : "PARAMS_GET",
+ res[1]>>24, (res[1]>>16)&0xFF, res[1]&0xFFFF);
+ return -((res[1] >> 16) & 0xFF); /* -BlockStatus */
+ }
+
+ return 4 + ((res[1] & 0x0000FFFF) << 2); /* bytes used in resblk */
+}
+
+
+static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba)
+{
+ u32 msg[4];
+ int ret;
+
+ adpt_i2o_status_get(pHba);
+
+ /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */
+
+ if((pHba->status_block->iop_state != ADAPTER_STATE_READY) &&
+ (pHba->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)){
+ return 0;
+ }
+
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2] = 0;
+ msg[3] = 0;
+
+ if((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
+ printk(KERN_INFO"dpti%d: Unable to quiesce (status=%#x).\n",
+ pHba->unit, -ret);
+ } else {
+ printk(KERN_INFO"dpti%d: Quiesced.\n",pHba->unit);
+ }
+
+ adpt_i2o_status_get(pHba);
+ return ret;
+}
+
+
+/*
+ * Enable IOP. Allows the IOP to resume external operations.
+ */
+static int adpt_i2o_enable_hba(adpt_hba* pHba)
+{
+ u32 msg[4];
+ int ret;
+
+ adpt_i2o_status_get(pHba);
+ if(!pHba->status_block){
+ return -ENOMEM;
+ }
+ /* Enable only allowed on READY state */
+ if(pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL)
+ return 0;
+
+ if(pHba->status_block->iop_state != ADAPTER_STATE_READY)
+ return -EINVAL;
+
+ msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]= 0;
+ msg[3]= 0;
+
+ if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
+ printk(KERN_WARNING"%s: Could not enable (status=%#10x).\n",
+ pHba->name, ret);
+ } else {
+ PDEBUG("%s: Enabled.\n", pHba->name);
+ }
+
+ adpt_i2o_status_get(pHba);
+ return ret;
+}
+
+
+static int adpt_i2o_systab_send(adpt_hba* pHba)
+{
+ u32 msg[12];
+ int ret;
+
+ msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6;
+ msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2] = 0;
+ msg[3] = 0;
+ msg[4] = (0<<16) | ((pHba->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */
+ msg[5] = 0; /* Segment 0 */
+
+ /*
+ * Provide three SGL-elements:
+ * System table (SysTab), Private memory space declaration and
+ * Private i/o space declaration
+ */
+ msg[6] = 0x54000000 | sys_tbl_len;
+ msg[7] = virt_to_phys(sys_tbl);
+ msg[8] = 0x54000000 | 0;
+ msg[9] = 0;
+ msg[10] = 0xD4000000 | 0;
+ msg[11] = 0;
+
+ if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 120))) {
+ printk(KERN_INFO "%s: Unable to set SysTab (status=%#10x).\n",
+ pHba->name, ret);
+ }
+#ifdef DEBUG
+ else {
+ PINFO("%s: SysTab set.\n", pHba->name);
+ }
+#endif
+
+ return ret;
+ }
+
+
+/*============================================================================
+ *
+ *============================================================================
+ */
+
+
+#ifdef UARTDELAY
+
+static static void adpt_delay(int millisec)
+{
+ int i;
+ for (i = 0; i < millisec; i++) {
+ udelay(1000); /* delay for one millisecond */
+ }
+}
+
+#endif
+
+static struct scsi_host_template driver_template = {
+ .name = "dpt_i2o",
+ .proc_name = "dpt_i2o",
+ .proc_info = adpt_proc_info,
+ .detect = adpt_detect,
+ .release = adpt_release,
+ .info = adpt_info,
+ .queuecommand = adpt_queue,
+ .eh_abort_handler = adpt_abort,
+ .eh_device_reset_handler = adpt_device_reset,
+ .eh_bus_reset_handler = adpt_bus_reset,
+ .eh_host_reset_handler = adpt_reset,
+ .bios_param = adpt_bios_param,
+ .slave_configure = adpt_slave_configure,
+ .can_queue = MAX_TO_IOP_MESSAGES,
+ .this_id = 7,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h
new file mode 100644
index 000000000000..426e15dd490e
--- /dev/null
+++ b/drivers/scsi/dpti.h
@@ -0,0 +1,359 @@
+/***************************************************************************
+ dpti.h - description
+ -------------------
+ begin : Thu Sep 7 2000
+ copyright : (C) 2001 by Adaptec
+
+ See Documentation/scsi/dpti.txt for history, notes, license info
+ and credits
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _DPT_H
+#define _DPT_H
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,00)
+#define MAX_TO_IOP_MESSAGES (210)
+#else
+#define MAX_TO_IOP_MESSAGES (255)
+#endif
+#define MAX_FROM_IOP_MESSAGES (255)
+
+
+/*
+ * SCSI interface function Prototypes
+ */
+
+static int adpt_detect(struct scsi_host_template * sht);
+static int adpt_queue(struct scsi_cmnd * cmd, void (*cmdcomplete) (struct scsi_cmnd *));
+static int adpt_abort(struct scsi_cmnd * cmd);
+static int adpt_reset(struct scsi_cmnd* cmd);
+static int adpt_release(struct Scsi_Host *host);
+static int adpt_slave_configure(struct scsi_device *);
+
+static const char *adpt_info(struct Scsi_Host *pSHost);
+static int adpt_bios_param(struct scsi_device * sdev, struct block_device *dev,
+ sector_t, int geom[]);
+
+static int adpt_bus_reset(struct scsi_cmnd* cmd);
+static int adpt_device_reset(struct scsi_cmnd* cmd);
+
+
+/*
+ * Scsi_Host_Template (see hosts.h)
+ */
+
+#define DPT_DRIVER_NAME "Adaptec I2O RAID"
+
+#ifndef HOSTS_C
+
+#include "dpt/sys_info.h"
+#include <linux/wait.h>
+#include "dpt/dpti_i2o.h"
+#include "dpt/dpti_ioctl.h"
+
+#define DPT_I2O_VERSION "2.4 Build 5go"
+#define DPT_VERSION 2
+#define DPT_REVISION '4'
+#define DPT_SUBREVISION '5'
+#define DPT_BETA ""
+#define DPT_MONTH 8
+#define DPT_DAY 7
+#define DPT_YEAR (2001-1980)
+
+#define DPT_DRIVER "dpt_i2o"
+#define DPTI_I2O_MAJOR (151)
+#define DPT_ORGANIZATION_ID (0x1B) /* For Private Messages */
+#define DPTI_MAX_HBA (16)
+#define MAX_CHANNEL (5) // Maximum Channel # Supported
+#define MAX_ID (128) // Maximum Target ID Supported
+
+/* Sizes in 4 byte words */
+#define REPLY_FRAME_SIZE (17)
+#define MAX_MESSAGE_SIZE (128)
+#define SG_LIST_ELEMENTS (56)
+
+#define EMPTY_QUEUE 0xffffffff
+#define I2O_INTERRUPT_PENDING_B (0x08)
+
+#define PCI_DPT_VENDOR_ID (0x1044) // DPT PCI Vendor ID
+#define PCI_DPT_DEVICE_ID (0xA501) // DPT PCI I2O Device ID
+#define PCI_DPT_RAPTOR_DEVICE_ID (0xA511)
+
+//#define REBOOT_NOTIFIER 1
+/* Debugging macro from Linux Device Drivers - Rubini */
+#undef PDEBUG
+#ifdef DEBUG
+//TODO add debug level switch
+# define PDEBUG(fmt, args...) printk(KERN_DEBUG "dpti: " fmt, ##args)
+# define PDEBUGV(fmt, args...) printk(KERN_DEBUG "dpti: " fmt, ##args)
+#else
+# define PDEBUG(fmt, args...) /* not debugging: nothing */
+# define PDEBUGV(fmt, args...) /* not debugging: nothing */
+#endif
+
+#define PERROR(fmt, args...) printk(KERN_ERR fmt, ##args)
+#define PWARN(fmt, args...) printk(KERN_WARNING fmt, ##args)
+#define PINFO(fmt, args...) printk(KERN_INFO fmt, ##args)
+#define PCRIT(fmt, args...) printk(KERN_CRIT fmt, ##args)
+
+#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+
+// Command timeouts
+#define FOREVER (0)
+#define TMOUT_INQUIRY (20)
+#define TMOUT_FLUSH (360/45)
+#define TMOUT_ABORT (30)
+#define TMOUT_SCSI (300)
+#define TMOUT_IOPRESET (360)
+#define TMOUT_GETSTATUS (15)
+#define TMOUT_INITOUTBOUND (15)
+#define TMOUT_LCT (360)
+
+
+#define I2O_SCSI_DEVICE_DSC_MASK 0x00FF
+
+#define I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION 0x000A
+
+#define I2O_SCSI_DSC_MASK 0xFF00
+#define I2O_SCSI_DSC_SUCCESS 0x0000
+#define I2O_SCSI_DSC_REQUEST_ABORTED 0x0200
+#define I2O_SCSI_DSC_UNABLE_TO_ABORT 0x0300
+#define I2O_SCSI_DSC_COMPLETE_WITH_ERROR 0x0400
+#define I2O_SCSI_DSC_ADAPTER_BUSY 0x0500
+#define I2O_SCSI_DSC_REQUEST_INVALID 0x0600
+#define I2O_SCSI_DSC_PATH_INVALID 0x0700
+#define I2O_SCSI_DSC_DEVICE_NOT_PRESENT 0x0800
+#define I2O_SCSI_DSC_UNABLE_TO_TERMINATE 0x0900
+#define I2O_SCSI_DSC_SELECTION_TIMEOUT 0x0A00
+#define I2O_SCSI_DSC_COMMAND_TIMEOUT 0x0B00
+#define I2O_SCSI_DSC_MR_MESSAGE_RECEIVED 0x0D00
+#define I2O_SCSI_DSC_SCSI_BUS_RESET 0x0E00
+#define I2O_SCSI_DSC_PARITY_ERROR_FAILURE 0x0F00
+#define I2O_SCSI_DSC_AUTOSENSE_FAILED 0x1000
+#define I2O_SCSI_DSC_NO_ADAPTER 0x1100
+#define I2O_SCSI_DSC_DATA_OVERRUN 0x1200
+#define I2O_SCSI_DSC_UNEXPECTED_BUS_FREE 0x1300
+#define I2O_SCSI_DSC_SEQUENCE_FAILURE 0x1400
+#define I2O_SCSI_DSC_REQUEST_LENGTH_ERROR 0x1500
+#define I2O_SCSI_DSC_PROVIDE_FAILURE 0x1600
+#define I2O_SCSI_DSC_BDR_MESSAGE_SENT 0x1700
+#define I2O_SCSI_DSC_REQUEST_TERMINATED 0x1800
+#define I2O_SCSI_DSC_IDE_MESSAGE_SENT 0x3300
+#define I2O_SCSI_DSC_RESOURCE_UNAVAILABLE 0x3400
+#define I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT 0x3500
+#define I2O_SCSI_DSC_MESSAGE_RECEIVED 0x3600
+#define I2O_SCSI_DSC_INVALID_CDB 0x3700
+#define I2O_SCSI_DSC_LUN_INVALID 0x3800
+#define I2O_SCSI_DSC_SCSI_TID_INVALID 0x3900
+#define I2O_SCSI_DSC_FUNCTION_UNAVAILABLE 0x3A00
+#define I2O_SCSI_DSC_NO_NEXUS 0x3B00
+#define I2O_SCSI_DSC_SCSI_IID_INVALID 0x3C00
+#define I2O_SCSI_DSC_CDB_RECEIVED 0x3D00
+#define I2O_SCSI_DSC_LUN_ALREADY_ENABLED 0x3E00
+#define I2O_SCSI_DSC_BUS_BUSY 0x3F00
+#define I2O_SCSI_DSC_QUEUE_FROZEN 0x4000
+
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#define HBA_FLAGS_INSTALLED_B 0x00000001 // Adapter Was Installed
+#define HBA_FLAGS_BLINKLED_B 0x00000002 // Adapter In Blink LED State
+#define HBA_FLAGS_IN_RESET 0x00000040 /* in reset */
+#define HBA_HOSTRESET_FAILED 0x00000080 /* adpt_resethost failed */
+
+
+// Device state flags
+#define DPTI_DEV_ONLINE 0x00
+#define DPTI_DEV_UNSCANNED 0x01
+#define DPTI_DEV_RESET 0x02
+#define DPTI_DEV_OFFLINE 0x04
+
+
+struct adpt_device {
+ struct adpt_device* next_lun;
+ u32 flags;
+ u32 type;
+ u32 capacity;
+ u32 block_size;
+ u8 scsi_channel;
+ u8 scsi_id;
+ u8 scsi_lun;
+ u8 state;
+ u16 tid;
+ struct i2o_device* pI2o_dev;
+ struct scsi_device *pScsi_dev;
+};
+
+struct adpt_channel {
+ struct adpt_device* device[MAX_ID]; /* used as an array of 128 scsi ids */
+ u8 scsi_id;
+ u8 type;
+ u16 tid;
+ u32 state;
+ struct i2o_device* pI2o_dev;
+};
+
+// HBA state flags
+#define DPTI_STATE_RESET (0x01)
+#define DPTI_STATE_IOCTL (0x02)
+
+typedef struct _adpt_hba {
+ struct _adpt_hba *next;
+ struct pci_dev *pDev;
+ struct Scsi_Host *host;
+ u32 state;
+ spinlock_t state_lock;
+ int unit;
+ int host_no; /* SCSI host number */
+ u8 initialized;
+ u8 in_use; /* is the management node open*/
+
+ char name[32];
+ char detail[55];
+
+ void __iomem *base_addr_virt;
+ void __iomem *msg_addr_virt;
+ ulong base_addr_phys;
+ void __iomem *post_port;
+ void __iomem *reply_port;
+ void __iomem *irq_mask;
+ u16 post_count;
+ u32 post_fifo_size;
+ u32 reply_fifo_size;
+ u32* reply_pool;
+ u32 sg_tablesize; // Scatter/Gather List Size.
+ u8 top_scsi_channel;
+ u8 top_scsi_id;
+ u8 top_scsi_lun;
+
+ i2o_status_block* status_block;
+ i2o_hrt* hrt;
+ i2o_lct* lct;
+ uint lct_size;
+ struct i2o_device* devices;
+ struct adpt_channel channel[MAX_CHANNEL];
+ struct proc_dir_entry* proc_entry; /* /proc dir */
+
+ void __iomem *FwDebugBuffer_P; // Virtual Address Of FW Debug Buffer
+ u32 FwDebugBufferSize; // FW Debug Buffer Size In Bytes
+ void __iomem *FwDebugStrLength_P;// Virtual Addr Of FW Debug String Len
+ void __iomem *FwDebugFlags_P; // Virtual Address Of FW Debug Flags
+ void __iomem *FwDebugBLEDflag_P;// Virtual Addr Of FW Debug BLED
+ void __iomem *FwDebugBLEDvalue_P;// Virtual Addr Of FW Debug BLED
+ u32 FwDebugFlags;
+} adpt_hba;
+
+struct sg_simple_element {
+ u32 flag_count;
+ u32 addr_bus;
+};
+
+/*
+ * Function Prototypes
+ */
+
+static void adpt_i2o_sys_shutdown(void);
+static int adpt_init(void);
+static int adpt_i2o_build_sys_table(void);
+static irqreturn_t adpt_isr(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef REBOOT_NOTIFIER
+static int adpt_reboot_event(struct notifier_block *n, ulong code, void *p);
+#endif
+
+static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d);
+static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid,
+ int group, int field, void *buf, int buflen);
+#ifdef DEBUG
+static const char *adpt_i2o_get_class_name(int class);
+#endif
+static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid,
+ void *opblk, int oplen, void *resblk, int reslen);
+static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout);
+static int adpt_i2o_lct_get(adpt_hba* pHba);
+static int adpt_i2o_parse_lct(adpt_hba* pHba);
+static int adpt_i2o_activate_hba(adpt_hba* pHba);
+static int adpt_i2o_enable_hba(adpt_hba* pHba);
+static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d);
+static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len);
+static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba);
+static s32 adpt_i2o_status_get(adpt_hba* pHba);
+static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba);
+static s32 adpt_i2o_hrt_get(adpt_hba* pHba);
+static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* dptdevice);
+static s32 adpt_i2o_to_scsi(ulong reply, struct scsi_cmnd* cmd);
+static s32 adpt_scsi_register(adpt_hba* pHba,struct scsi_host_template * sht);
+static s32 adpt_hba_reset(adpt_hba* pHba);
+static s32 adpt_i2o_reset_hba(adpt_hba* pHba);
+static s32 adpt_rescan(adpt_hba* pHba);
+static s32 adpt_i2o_reparse_lct(adpt_hba* pHba);
+static s32 adpt_send_nop(adpt_hba*pHba,u32 m);
+static void adpt_i2o_delete_hba(adpt_hba* pHba);
+static void adpt_inquiry(adpt_hba* pHba);
+static void adpt_fail_posted_scbs(adpt_hba* pHba);
+static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun);
+static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev) ;
+static int adpt_i2o_online_hba(adpt_hba* pHba);
+static void adpt_i2o_post_wait_complete(u32, int);
+static int adpt_i2o_systab_send(adpt_hba* pHba);
+
+static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg);
+static int adpt_open(struct inode *inode, struct file *file);
+static int adpt_close(struct inode *inode, struct file *file);
+
+
+#ifdef UARTDELAY
+static void adpt_delay(int millisec);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+static struct pci_dev* adpt_pci_find_device(uint vendor, struct pci_dev* from);
+#endif
+
+#if defined __ia64__
+static void adpt_ia64_info(sysInfo_S* si);
+#endif
+#if defined __sparc__
+static void adpt_sparc_info(sysInfo_S* si);
+#endif
+#if defined __alpha__
+static void adpt_sparc_info(sysInfo_S* si);
+#endif
+#if defined __i386__
+static void adpt_i386_info(sysInfo_S* si);
+#endif
+
+#define PRINT_BUFFER_SIZE 512
+
+#define HBA_FLAGS_DBG_FLAGS_MASK 0xffff0000 // Mask for debug flags
+#define HBA_FLAGS_DBG_KERNEL_PRINT_B 0x00010000 // Kernel Debugger Print
+#define HBA_FLAGS_DBG_FW_PRINT_B 0x00020000 // Firmware Debugger Print
+#define HBA_FLAGS_DBG_FUNCTION_ENTRY_B 0x00040000 // Function Entry Point
+#define HBA_FLAGS_DBG_FUNCTION_EXIT_B 0x00080000 // Function Exit
+#define HBA_FLAGS_DBG_ERROR_B 0x00100000 // Error Conditions
+#define HBA_FLAGS_DBG_INIT_B 0x00200000 // Init Prints
+#define HBA_FLAGS_DBG_OS_COMMANDS_B 0x00400000 // OS Command Info
+#define HBA_FLAGS_DBG_SCAN_B 0x00800000 // Device Scan
+
+#define FW_DEBUG_STR_LENGTH_OFFSET 0
+#define FW_DEBUG_FLAGS_OFFSET 4
+#define FW_DEBUG_BLED_OFFSET 8
+
+#define FW_DEBUG_FLAGS_NO_HEADERS_B 0x01
+#endif /* !HOSTS_C */
+#endif /* _DPT_H */
diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c
new file mode 100644
index 000000000000..da1aaa413fed
--- /dev/null
+++ b/drivers/scsi/dtc.c
@@ -0,0 +1,494 @@
+
+#define AUTOSENSE
+#define PSEUDO_DMA
+#define DONT_USE_INTR
+#define UNSAFE /* Leave interrupts enabled during pseudo-dma I/O */
+#define xNDEBUG (NDEBUG_INTR+NDEBUG_RESELECTION+\
+ NDEBUG_SELECTION+NDEBUG_ARBITRATION)
+#define DMA_WORKS_RIGHT
+
+
+/*
+ * DTC 3180/3280 driver, by
+ * Ray Van Tassle rayvt@comm.mot.com
+ *
+ * taken from ...
+ * Trantor T128/T128F/T228 driver by...
+ *
+ * Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+*/
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers.
+ * You probably want this.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - since the board is memory mapped,
+ * a BIOS signature is scanned for to locate the registers.
+ * An interrupt is triggered to autoprobe for the interrupt
+ * line.
+ *
+ * 2. With command line overrides - dtc=address,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+*/
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+#if 0
+#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+
+#include <asm/system.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "dtc.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+
+
+#define DTC_PUBLIC_RELEASE 2
+
+/*#define DTCDEBUG 0x1*/
+#define DTCDEBUG_INIT 0x1
+#define DTCDEBUG_TRANSFER 0x2
+
+/*
+ * The DTC3180 & 3280 boards are memory mapped.
+ *
+ */
+
+/*
+ */
+/* Offset from DTC_5380_OFFSET */
+#define DTC_CONTROL_REG 0x100 /* rw */
+#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */
+#define CSR_DIR_READ 0x40 /* rw direction, 1 = read 0 = write */
+
+#define CSR_RESET 0x80 /* wo Resets 53c400 */
+#define CSR_5380_REG 0x80 /* ro 5380 registers can be accessed */
+#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
+#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
+#define CSR_5380_INTR 0x10 /* rw Enable 5380 interrupts */
+#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Host buffer not ready */
+#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer ready */
+#define CSR_GATED_5380_IRQ 0x01 /* ro Last block xferred */
+#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR)
+
+
+#define DTC_BLK_CNT 0x101 /* rw
+ * # of 128-byte blocks to transfer */
+
+
+#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */
+
+#define DTC_SWITCH_REG 0x3982 /* ro - DIP switches */
+#define DTC_RESUME_XFER 0x3982 /* wo - resume data xfer
+ * after disconnect/reconnect*/
+
+#define DTC_5380_OFFSET 0x3880 /* 8 registers here, see NCR5380.h */
+
+/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */
+#define DTC_DATA_BUF 0x3900 /* rw 128 bytes long */
+
+static struct override {
+ unsigned int address;
+ int irq;
+} overrides
+#ifdef OVERRIDE
+[] __initdata = OVERRIDE;
+#else
+[4] __initdata = { {
+0, IRQ_AUTO}, {
+0, IRQ_AUTO}, {
+0, IRQ_AUTO}, {
+0, IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned long address;
+ int noauto;
+} bases[] __initdata = {
+ { 0xcc000, 0 },
+ { 0xc8000, 0 },
+ { 0xdc000, 0 },
+ { 0xd8000, 0 }
+};
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+static const struct signature {
+ const char *string;
+ int offset;
+} signatures[] = {
+ {"DATA TECHNOLOGY CORPORATION BIOS", 0x25},
+};
+
+#define NO_SIGNATURES (sizeof (signatures) / sizeof (struct signature))
+
+#ifndef MODULE
+/*
+ * Function : dtc_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+*/
+
+static void __init dtc_setup(char *str, int *ints)
+{
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("dtc_setup: usage dtc=address,irq\n");
+ else if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].address = ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].address == ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+#endif
+
+/*
+ * Function : int dtc_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes DTC 3180/3280 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+*/
+
+static int __init dtc_detect(Scsi_Host_Template * tpnt)
+{
+ static int current_override = 0, current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned int addr;
+ void __iomem *base;
+ int sig, count;
+
+ tpnt->proc_name = "dtc3x80";
+ tpnt->proc_info = &dtc_proc_info;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ addr = 0;
+ base = NULL;
+
+ if (overrides[current_override].address) {
+ addr = overrides[current_override].address;
+ base = ioremap(addr, 0x2000);
+ if (!base)
+ addr = 0;
+ } else
+ for (; !addr && (current_base < NO_BASES); ++current_base) {
+#if (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi-dtc : probing address %08x\n", bases[current_base].address);
+#endif
+ if (bases[current_base].noauto)
+ continue;
+ base = ioremap(bases[current_base].address, 0x2000);
+ if (!base)
+ continue;
+ for (sig = 0; sig < NO_SIGNATURES; ++sig) {
+ if (check_signature(base + signatures[sig].offset, signatures[sig].string, strlen(signatures[sig].string))) {
+ addr = bases[current_base].address;
+#if (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi-dtc : detected board.\n");
+#endif
+ goto found;
+ }
+ }
+ iounmap(base);
+ }
+
+#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi-dtc : base = %08x\n", addr);
+#endif
+
+ if (!addr)
+ break;
+
+found:
+ instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
+ if (instance == NULL)
+ break;
+
+ instance->base = addr;
+ ((struct NCR5380_hostdata *)(instance)->hostdata)->base = base;
+
+ NCR5380_init(instance, 0);
+
+ NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); /* Enable int's */
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, DTC_IRQS);
+
+#ifndef DONT_USE_INTR
+ /* With interrupts enabled, it will sometimes hang when doing heavy
+ * reads. So better not enable them until I finger it out. */
+ if (instance->irq != SCSI_IRQ_NONE)
+ if (request_irq(instance->irq, dtc_intr, SA_INTERRUPT, "dtc", instance)) {
+ printk(KERN_ERR "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+ }
+
+ if (instance->irq == SCSI_IRQ_NONE) {
+ printk(KERN_WARNING "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk(KERN_WARNING "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+#else
+ if (instance->irq != SCSI_IRQ_NONE)
+ printk(KERN_WARNING "scsi%d : interrupts not used. Might as well not jumper it.\n", instance->host_no);
+ instance->irq = SCSI_IRQ_NONE;
+#endif
+#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk(KERN_INFO "scsi%d : at 0x%05X", instance->host_no, (int) instance->base);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk(" interrupts disabled");
+ else
+ printk(" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, DTC_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int dtc_biosparam(Disk * disk, struct block_device *dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+*/
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+*/
+
+static int dtc_biosparam(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int *ip)
+{
+ int size = capacity;
+
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+
+
+/****************************************************************
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, reads len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+*/
+
+static int dtc_maxi = 0;
+static int dtc_wmaxi = 0;
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, int len)
+{
+ unsigned char *d = dst;
+ int i; /* For counting time spent in the poll-loop */
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ i = 0;
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
+ if (instance->irq == SCSI_IRQ_NONE)
+ NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ);
+ else
+ NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE);
+ NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */
+ rtrc(1);
+ while (len > 0) {
+ rtrc(2);
+ while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
+ ++i;
+ rtrc(3);
+ memcpy_fromio(d, base + DTC_DATA_BUF, 128);
+ d += 128;
+ len -= 128;
+ rtrc(7);
+ /*** with int's on, it sometimes hangs after here.
+ * Looks like something makes HBNR go away. */
+ }
+ rtrc(4);
+ while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
+ ++i;
+ NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
+ rtrc(0);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ if (i > dtc_maxi)
+ dtc_maxi = i;
+ return (0);
+}
+
+/****************************************************************
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+*/
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, int len)
+{
+ int i;
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
+ /* set direction (write) */
+ if (instance->irq == SCSI_IRQ_NONE)
+ NCR5380_write(DTC_CONTROL_REG, 0);
+ else
+ NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR);
+ NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */
+ for (i = 0; len > 0; ++i) {
+ rtrc(5);
+ /* Poll until the host buffer can accept data. */
+ while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
+ ++i;
+ rtrc(3);
+ memcpy_toio(base + DTC_DATA_BUF, src, 128);
+ src += 128;
+ len -= 128;
+ }
+ rtrc(4);
+ while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
+ ++i;
+ rtrc(6);
+ /* Wait until the last byte has been sent to the disk */
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+ ++i;
+ rtrc(7);
+ /* Check for parity error here. fixme. */
+ NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
+ rtrc(0);
+ if (i > dtc_wmaxi)
+ dtc_wmaxi = i;
+ return (0);
+}
+
+MODULE_LICENSE("GPL");
+
+#include "NCR5380.c"
+
+static int dtc_release(struct Scsi_Host *shost)
+{
+ NCR5380_local_declare();
+ NCR5380_setup(shost);
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ NCR5380_exit(shost);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ iounmap(base);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "DTC 3180/3280 ",
+ .detect = dtc_detect,
+ .release = dtc_release,
+ .queuecommand = dtc_queue_command,
+ .eh_abort_handler = dtc_abort,
+ .eh_bus_reset_handler = dtc_bus_reset,
+ .eh_device_reset_handler = dtc_device_reset,
+ .eh_host_reset_handler = dtc_host_reset,
+ .bios_param = dtc_biosparam,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h
new file mode 100644
index 000000000000..c4bcdbf338a2
--- /dev/null
+++ b/drivers/scsi/dtc.h
@@ -0,0 +1,99 @@
+/*
+ * DTC controller, taken from T128 driver by...
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 2.
+ *
+ * For more information, please consult
+ *
+ *
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+#ifndef DTC3280_H
+#define DTC3280_H
+
+static int dtc_abort(Scsi_Cmnd *);
+static int dtc_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int*);
+static int dtc_detect(Scsi_Host_Template *);
+static int dtc_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int dtc_bus_reset(Scsi_Cmnd *);
+static int dtc_device_reset(Scsi_Cmnd *);
+static int dtc_host_reset(Scsi_Cmnd *);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+#define NCR5380_implementation_fields \
+ void __iomem *base
+
+#define NCR5380_local_declare() \
+ void __iomem *base
+
+#define NCR5380_setup(instance) \
+ base = ((struct NCR5380_hostdata *)(instance)->hostdata)->base
+
+#define DTC_address(reg) (base + DTC_5380_OFFSET + reg)
+
+#define dbNCR5380_read(reg) \
+ (rval=readb(DTC_address(reg)), \
+ (((unsigned char) printk("DTC : read register %d at addr %p is: %02x\n"\
+ , (reg), DTC_address(reg), rval)), rval ) )
+
+#define dbNCR5380_write(reg, value) do { \
+ printk("DTC : write %02x to register %d at address %p\n", \
+ (value), (reg), DTC_address(reg)); \
+ writeb(value, DTC_address(reg));} while(0)
+
+
+#if !(DTCDEBUG & DTCDEBUG_TRANSFER)
+#define NCR5380_read(reg) (readb(DTC_address(reg)))
+#define NCR5380_write(reg, value) (writeb(value, DTC_address(reg)))
+#else
+#define NCR5380_read(reg) (readb(DTC_address(reg)))
+#define xNCR5380_read(reg) \
+ (((unsigned char) printk("DTC : read register %d at address %p\n"\
+ , (reg), DTC_address(reg))), readb(DTC_address(reg)))
+
+#define NCR5380_write(reg, value) do { \
+ printk("DTC : write %02x to register %d at address %p\n", \
+ (value), (reg), DTC_address(reg)); \
+ writeb(value, DTC_address(reg));} while(0)
+#endif
+
+#define NCR5380_intr dtc_intr
+#define NCR5380_queue_command dtc_queue_command
+#define NCR5380_abort dtc_abort
+#define NCR5380_bus_reset dtc_bus_reset
+#define NCR5380_device_reset dtc_device_reset
+#define NCR5380_host_reset dtc_host_reset
+#define NCR5380_proc_info dtc_proc_info
+
+/* 15 12 11 10
+ 1001 1100 0000 0000 */
+
+#define DTC_IRQS 0x9c00
+
+
+#endif /* DTC3280_H */
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
new file mode 100644
index 000000000000..81d16cfbe69e
--- /dev/null
+++ b/drivers/scsi/eata.c
@@ -0,0 +1,2621 @@
+/*
+ * eata.c - Low-level driver for EATA/DMA SCSI host adapters.
+ *
+ * 03 Jun 2003 Rev. 8.10 for linux-2.5.70
+ * + Update for new IRQ API.
+ * + Use "goto" when appropriate.
+ * + Drop eata.h.
+ * + Update for new module_param API.
+ * + Module parameters can now be specified only in the
+ * same format as the kernel boot options.
+ *
+ * boot option old module param
+ * ----------- ------------------
+ * addr,... io_port=addr,...
+ * lc:[y|n] linked_comm=[1|0]
+ * mq:xx max_queue_depth=xx
+ * tm:[0|1|2] tag_mode=[0|1|2]
+ * et:[y|n] ext_tran=[1|0]
+ * rs:[y|n] rev_scan=[1|0]
+ * ip:[y|n] isa_probe=[1|0]
+ * ep:[y|n] eisa_probe=[1|0]
+ * pp:[y|n] pci_probe=[1|0]
+ *
+ * A valid example using the new parameter format is:
+ * modprobe eata "eata=0x7410,0x230,lc:y,tm:0,mq:4,ep:n"
+ *
+ * which is equivalent to the old format:
+ * modprobe eata io_port=0x7410,0x230 linked_comm=1 tag_mode=0 \
+ * max_queue_depth=4 eisa_probe=0
+ *
+ * 12 Feb 2003 Rev. 8.04 for linux 2.5.60
+ * + Release irq before calling scsi_register.
+ *
+ * 12 Nov 2002 Rev. 8.02 for linux 2.5.47
+ * + Release driver_lock before calling scsi_register.
+ *
+ * 11 Nov 2002 Rev. 8.01 for linux 2.5.47
+ * + Fixed bios_param and scsicam_bios_param calling parameters.
+ *
+ * 28 Oct 2002 Rev. 8.00 for linux 2.5.44-ac4
+ * + Use new tcq and adjust_queue_depth api.
+ * + New command line option (tm:[0-2]) to choose the type of tags:
+ * 0 -> disable tagging ; 1 -> simple tags ; 2 -> ordered tags.
+ * Default is tm:0 (tagged commands disabled).
+ * For compatibility the "tc:" option is an alias of the "tm:"
+ * option; tc:n is equivalent to tm:0 and tc:y is equivalent to
+ * tm:1.
+ * + The tagged_comm module parameter has been removed, use tag_mode
+ * instead, equivalent to the "tm:" boot option.
+ *
+ * 10 Oct 2002 Rev. 7.70 for linux 2.5.42
+ * + Foreport from revision 6.70.
+ *
+ * 25 Jun 2002 Rev. 6.70 for linux 2.4.19
+ * + This release is the first one tested on a Big Endian platform:
+ * fixed endian-ness problem due to bitfields;
+ * fixed endian-ness problem in read_pio.
+ * + Added new options for selectively probing ISA, EISA and PCI bus:
+ *
+ * Boot option Parameter name Default according to
+ *
+ * ip:[y|n] isa_probe=[1|0] CONFIG_ISA defined
+ * ep:[y|n] eisa_probe=[1|0] CONFIG_EISA defined
+ * pp:[y|n] pci_probe=[1|0] CONFIG_PCI defined
+ *
+ * The default action is to perform probing if the corrisponding
+ * bus is configured and to skip probing otherwise.
+ *
+ * + If pci_probe is in effect and a list of I/O ports is specified
+ * as parameter or boot option, pci_enable_device() is performed
+ * on all pci devices matching PCI_CLASS_STORAGE_SCSI.
+ *
+ * 21 Feb 2002 Rev. 6.52 for linux 2.4.18
+ * + Backport from rev. 7.22 (use io_request_lock).
+ *
+ * 20 Feb 2002 Rev. 7.22 for linux 2.5.5
+ * + Remove any reference to virt_to_bus().
+ * + Fix pio hang while detecting multiple HBAs.
+ * + Fixed a board detection bug: in a system with
+ * multiple ISA/EISA boards, all but the first one
+ * were erroneously detected as PCI.
+ *
+ * 01 Jan 2002 Rev. 7.20 for linux 2.5.1
+ * + Use the dynamic DMA mapping API.
+ *
+ * 19 Dec 2001 Rev. 7.02 for linux 2.5.1
+ * + Use SCpnt->sc_data_direction if set.
+ * + Use sglist.page instead of sglist.address.
+ *
+ * 11 Dec 2001 Rev. 7.00 for linux 2.5.1
+ * + Use host->host_lock instead of io_request_lock.
+ *
+ * 1 May 2001 Rev. 6.05 for linux 2.4.4
+ * + Clean up all pci related routines.
+ * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d)
+ *
+ * 30 Jan 2001 Rev. 6.04 for linux 2.4.1
+ * + Call pci_resource_start after pci_enable_device.
+ *
+ * 25 Jan 2001 Rev. 6.03 for linux 2.4.0
+ * + "check_region" call replaced by "request_region".
+ *
+ * 22 Nov 2000 Rev. 6.02 for linux 2.4.0-test11
+ * + Return code checked when calling pci_enable_device.
+ * + Removed old scsi error handling support.
+ * + The obsolete boot option flag eh:n is silently ignored.
+ * + Removed error messages while a disk drive is powered up at
+ * boot time.
+ * + Improved boot messages: all tagged capable device are
+ * indicated as "tagged" or "soft-tagged" :
+ * - "soft-tagged" means that the driver is trying to do its
+ * own tagging (i.e. the tc:y option is in effect);
+ * - "tagged" means that the device supports tagged commands,
+ * but the driver lets the HBA be responsible for tagging
+ * support.
+ *
+ * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
+ * + Updated to the new __setup interface for boot command line options.
+ * + When loaded as a module, accepts the new parameter boot_options
+ * which value is a string with the same format of the kernel boot
+ * command line options. A valid example is:
+ * modprobe eata 'boot_options="0x7410,0x230,lc:y,tc:n,mq:4"'
+ *
+ * 9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17
+ * + 64bit cleanup for Linux/Alpha platform support
+ * (contribution from H.J. Lu).
+ *
+ * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11
+ * + Removed pre-2.2 source code compatibility.
+ * + Added call to pci_set_master.
+ *
+ * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
+ * + Added command line option (rs:[y|n]) to reverse the scan order
+ * of PCI boards. The default is rs:y, which reverses the BIOS order
+ * while registering PCI boards. The default value rs:y generates
+ * the same order of all previous revisions of this driver.
+ * Pls. note that "BIOS order" might have been reversed itself
+ * after the 2.1.9x PCI modifications in the linux kernel.
+ * The rs value is ignored when the explicit list of addresses
+ * is used by the "eata=port0,port1,..." command line option.
+ * + Added command line option (et:[y|n]) to force use of extended
+ * translation (255 heads, 63 sectors) as disk geometry.
+ * The default is et:n, which uses the disk geometry returned
+ * by scsicam_bios_param. The default value et:n is compatible with
+ * all previous revisions of this driver.
+ *
+ * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
+ * Increased busy timeout from 10 msec. to 200 msec. while
+ * processing interrupts.
+ *
+ * 16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
+ * Improved abort handling during the eh recovery process.
+ *
+ * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101
+ * The driver is now fully SMP safe, including the
+ * abort and reset routines.
+ * Added command line options (eh:[y|n]) to choose between
+ * new_eh_code and the old scsi code.
+ * If linux version >= 2.1.101 the default is eh:y, while the eh
+ * option is ignored for previous releases and the old scsi code
+ * is used.
+ *
+ * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
+ * Reworked interrupt handler.
+ *
+ * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
+ * Major reliability improvement: when a batch with overlapping
+ * requests is detected, requests are queued one at a time
+ * eliminating any possible board or drive reordering.
+ *
+ * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
+ * Improved SMP support (if linux version >= 2.1.95).
+ *
+ * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94
+ * Added support for new PCI code and IO-APIC remapping of irqs.
+ * Performance improvement: when sequential i/o is detected,
+ * always use direct sort instead of reverse sort.
+ *
+ * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ * io_port is now unsigned long.
+ *
+ * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ * Use new scsi error handling code (if linux version >= 2.1.88).
+ * Use new interrupt code.
+ *
+ * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
+ * Use of udelay inside the wait loops to avoid timeout
+ * problems with fast cpus.
+ * Removed check about useless calls to the interrupt service
+ * routine (reported on SMP systems only).
+ * At initialization time "sorted/unsorted" is displayed instead
+ * of "linked/unlinked" to reinforce the fact that "linking" is
+ * nothing but "elevator sorting" in the actual implementation.
+ *
+ * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ * Use of serial_number_at_timeout in abort and reset processing.
+ * Use of the __initfunc and __initdata macro in setup code.
+ * Minor cleanups in the list_statistics code.
+ * Increased controller busy timeout in order to better support
+ * slow SCSI devices.
+ *
+ * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ * When loading as a module, parameter passing is now supported
+ * both in 2.0 and in 2.1 style.
+ * Fixed data transfer direction for some SCSI opcodes.
+ * Immediate acknowledge to request sense commands.
+ * Linked commands to each disk device are now reordered by elevator
+ * sorting. Rare cases in which reordering of write requests could
+ * cause wrong results are managed.
+ * Fixed spurious timeouts caused by long simple queue tag sequences.
+ * New command line option (tm:[0-3]) to choose the type of tags:
+ * 0 -> mixed (default); 1 -> simple; 2 -> head; 3 -> ordered.
+ *
+ * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ * Added command line options to enable/disable linked commands
+ * (lc:[y|n]), tagged commands (tc:[y|n]) and to set the max queue
+ * depth (mq:xx). Default is "eata=lc:n,tc:n,mq:16".
+ * Improved command linking.
+ * Documented how to setup RAID-0 with DPT SmartRAID boards.
+ *
+ * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
+ * Added linked command support.
+ * Improved detection of PCI boards using ISA base addresses.
+ *
+ * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ * Added support for tagged commands and queue depth adjustment.
+ *
+ * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
+ * When CONFIG_PCI is defined, BIOS32 is used to include in the
+ * list of i/o ports to be probed all the PCI SCSI controllers.
+ * The list of i/o ports to be probed can be overwritten by the
+ * "eata=port0,port1,...." boot command line option.
+ * Scatter/gather lists are now allocated by a number of kmalloc
+ * calls, in order to avoid the previous size limit of 64Kb.
+ *
+ * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25
+ * Added support for EATA 2.0C, PCI, multichannel and wide SCSI.
+ *
+ * 27 Sep 1996 rev. 2.12 for linux 2.1.0
+ * Portability cleanups (virtual/bus addressing, little/big endian
+ * support).
+ *
+ * 09 Jul 1996 rev. 2.11 for linux 2.0.4
+ * Number of internal retries is now limited.
+ *
+ * 16 Apr 1996 rev. 2.10 for linux 1.3.90
+ * New argument "reset_flags" to the reset routine.
+ *
+ * 6 Jul 1995 rev. 2.01 for linux 1.3.7
+ * Update required by the new /proc/scsi support.
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * Now DEBUG_RESET is disabled by default.
+ * Register a board even if it does not assert DMA protocol support
+ * (DPT SK2011B does not report correctly the dmasup bit).
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ * New list of Data Out SCSI commands.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ * All external symbols renamed to avoid possible name conflicts.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ *
+ * 24 Jan 1995 rev. 1.13 for linux 1.1.85
+ * Use optimized board configuration, with a measured performance
+ * increase in the range 10%-20% on i/o throughput.
+ *
+ * 16 Jan 1995 rev. 1.12 for linux 1.1.81
+ * Fix mscp structure comments (no functional change).
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 17 Dec 1994 rev. 1.11 for linux 1.1.74
+ * Use the scsicam_bios_param routine. This allows an easy
+ * migration path from disk partition tables created using
+ * different SCSI drivers and non optimal disk geometry.
+ *
+ * 15 Dec 1994 rev. 1.10 for linux 1.1.74
+ * Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
+ * The host->block flag is set for all the detected ISA boards.
+ * The detect routine no longer enforces LEVEL triggering
+ * for EISA boards, it just prints a warning message.
+ *
+ * 30 Nov 1994 rev. 1.09 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
+ *
+ * 18 Nov 1994 rev. 1.08 for linux 1.1.64
+ * Forces sg_tablesize = 64 and can_queue = 64 if these
+ * values are not correctly detected (DPT PM2012).
+ *
+ * 14 Nov 1994 rev. 1.07 for linux 1.1.63 Final BETA release.
+ * 04 Aug 1994 rev. 1.00 for linux 1.1.39 First BETA release.
+ *
+ *
+ * This driver is based on the CAM (Common Access Method Committee)
+ * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
+ *
+ * Copyright (C) 1994-2003 Dario Ballabio (ballabio_dario@emc.com)
+ *
+ * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ */
+
+/*
+ *
+ * Here is a brief description of the DPT SCSI host adapters.
+ * All these boards provide an EATA/DMA compatible programming interface
+ * and are fully supported by this driver in any configuration, including
+ * multiple SCSI channels:
+ *
+ * PM2011B/9X - Entry Level ISA
+ * PM2021A/9X - High Performance ISA
+ * PM2012A Old EISA
+ * PM2012B Old EISA
+ * PM2022A/9X - Entry Level EISA
+ * PM2122A/9X - High Performance EISA
+ * PM2322A/9X - Extra High Performance EISA
+ * PM3021 - SmartRAID Adapter for ISA
+ * PM3222 - SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI)
+ * PM3224 - SmartRAID Adapter for PCI (PM3224W is 16-bit wide SCSI)
+ * PM33340UW - SmartRAID Adapter for PCI ultra wide multichannel
+ *
+ * The above list is just an indication: as a matter of fact all DPT
+ * boards using the EATA/DMA protocol are supported by this driver,
+ * since they use exactely the same programming interface.
+ *
+ * The DPT PM2001 provides only the EATA/PIO interface and hence is not
+ * supported by this driver.
+ *
+ * This code has been tested with up to 3 Distributed Processing Technology
+ * PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) EISA controllers,
+ * in any combination of private and shared IRQ.
+ * PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS
+ * v003.D0, firmware v07G.0).
+ *
+ * DPT SmartRAID boards support "Hardware Array" - a group of disk drives
+ * which are all members of the same RAID-0, RAID-1 or RAID-5 array implemented
+ * in host adapter hardware. Hardware Arrays are fully compatible with this
+ * driver, since they look to it as a single disk drive.
+ *
+ * WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
+ * as the current OS in the DPTMGR "Initial System Installation" menu.
+ * Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0),
+ * which is not supported by the actual SCSI subsystem.
+ * To get the "Array Group" functionality, the Linux MD driver must be used
+ * instead of the DPT "Array Group" feature.
+ *
+ * Multiple ISA, EISA and PCI boards can be configured in the same system.
+ * It is suggested to put all the EISA boards on the same IRQ level, all
+ * the PCI boards on another IRQ level, while ISA boards cannot share
+ * interrupts.
+ *
+ * If you configure multiple boards on the same IRQ, the interrupt must
+ * be _level_ triggered (not _edge_ triggered).
+ *
+ * This driver detects EATA boards by probes at fixed port addresses,
+ * so no BIOS32 or PCI BIOS support is required.
+ * The suggested way to detect a generic EATA PCI board is to force on it
+ * any unused EISA address, even if there are other controllers on the EISA
+ * bus, or even if you system has no EISA bus at all.
+ * Do not force any ISA address on EATA PCI boards.
+ *
+ * If PCI bios support is configured into the kernel, BIOS32 is used to
+ * include in the list of i/o ports to be probed all the PCI SCSI controllers.
+ *
+ * Due to a DPT BIOS "feature", it might not be possible to force an EISA
+ * address on more than a single DPT PCI board, so in this case you have to
+ * let the PCI BIOS assign the addresses.
+ *
+ * The sequence of detection probes is:
+ *
+ * - ISA 0x1F0;
+ * - PCI SCSI controllers (only if BIOS32 is available);
+ * - EISA/PCI 0x1C88 through 0xFC88 (corresponding to EISA slots 1 to 15);
+ * - ISA 0x170, 0x230, 0x330.
+ *
+ * The above list of detection probes can be totally replaced by the
+ * boot command line option: "eata=port0,port1,port2,...", where the
+ * port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
+ * For example using "eata=0x7410,0x7450,0x230", the driver probes
+ * only the two PCI addresses 0x7410 and 0x7450 and the ISA address 0x230,
+ * in this order; "eata=0" totally disables this driver.
+ *
+ * After the optional list of detection probes, other possible command line
+ * options are:
+ *
+ * et:y force use of extended translation (255 heads, 63 sectors);
+ * et:n use disk geometry detected by scsicam_bios_param;
+ * rs:y reverse scan order while detecting PCI boards;
+ * rs:n use BIOS order while detecting PCI boards;
+ * lc:y enables linked commands;
+ * lc:n disables linked commands;
+ * tm:0 disables tagged commands (same as tc:n);
+ * tm:1 use simple queue tags (same as tc:y);
+ * tm:2 use ordered queue tags (same as tc:2);
+ * mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
+ *
+ * The default value is: "eata=lc:n,mq:16,tm:0,et:n,rs:n".
+ * An example using the list of detection probes could be:
+ * "eata=0x7410,0x230,lc:y,tm:2,mq:4,et:n".
+ *
+ * When loading as a module, parameters can be specified as well.
+ * The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ * modprobe eata io_port=0x7410,0x230 linked_comm=1 \
+ * max_queue_depth=4 ext_tran=0 tag_mode=2 \
+ * rev_scan=1
+ *
+ * ----------------------------------------------------------------------------
+ * In this implementation, linked commands are designed to work with any DISK
+ * or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ * and reordering by elevator sorting commands directed to each device,
+ * without any relation with the actual SCSI protocol between the controller
+ * and the device.
+ * If Q is the queue depth reported at boot time for each device (also named
+ * cmds/lun) and Q > 2, whenever there is already an active command to the
+ * device all other commands to the same device (up to Q-1) are kept waiting
+ * in the elevator sorting queue. When the active command completes, the
+ * commands in this queue are sorted by sector address. The sort is chosen
+ * between increasing or decreasing by minimizing the seek distance between
+ * the sector of the commands just completed and the sector of the first
+ * command in the list to be sorted.
+ * Trivial math assures that the unsorted average seek distance when doing
+ * random seeks over S sectors is S/3.
+ * When (Q-1) requests are uniformly distributed over S sectors, the average
+ * distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ * average seek distance for (Q-1) random requests over S sectors is S/Q.
+ * The elevator sorting hence divides the seek distance by a factor Q/3.
+ * The above pure geometric remarks are valid in all cases and the
+ * driver effectively reduces the seek distance by the predicted factor
+ * when there are Q concurrent read i/o operations on the device, but this
+ * does not necessarily results in a noticeable performance improvement:
+ * your mileage may vary....
+ *
+ * Note: command reordering inside a batch of queued commands could cause
+ * wrong results only if there is at least one write request and the
+ * intersection (sector-wise) of all requests is not empty.
+ * When the driver detects a batch including overlapping requests
+ * (a really rare event) strict serial (pid) order is enforced.
+ * ----------------------------------------------------------------------------
+ * The extended translation option (et:y) is useful when using large physical
+ * disks/arrays. It could also be useful when switching between Adaptec boards
+ * and DPT boards without reformatting the disk.
+ * When a boot disk is partitioned with extended translation, in order to
+ * be able to boot it with a DPT board is could be necessary to add to
+ * lilo.conf additional commands as in the following example:
+ *
+ * fix-table
+ * disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546
+ *
+ * where the above geometry should be replaced with the one reported at
+ * power up by the DPT controller.
+ * ----------------------------------------------------------------------------
+ *
+ * The boards are named EATA0, EATA1,... according to the detection order.
+ *
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = 1 for all ISA boards.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+
+static int eata2x_detect(struct scsi_host_template *);
+static int eata2x_release(struct Scsi_Host *);
+static int eata2x_queuecommand(struct scsi_cmnd *,
+ void (*done) (struct scsi_cmnd *));
+static int eata2x_eh_abort(struct scsi_cmnd *);
+static int eata2x_eh_host_reset(struct scsi_cmnd *);
+static int eata2x_bios_param(struct scsi_device *, struct block_device *,
+ sector_t, int *);
+static int eata2x_slave_configure(struct scsi_device *);
+
+static struct scsi_host_template driver_template = {
+ .name = "EATA/DMA 2.0x rev. 8.10.00 ",
+ .detect = eata2x_detect,
+ .release = eata2x_release,
+ .queuecommand = eata2x_queuecommand,
+ .eh_abort_handler = eata2x_eh_abort,
+ .eh_device_reset_handler = NULL,
+ .eh_bus_reset_handler = NULL,
+ .eh_host_reset_handler = eata2x_eh_host_reset,
+ .bios_param = eata2x_bios_param,
+ .slave_configure = eata2x_slave_configure,
+ .this_id = 7,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+#if !defined(__BIG_ENDIAN_BITFIELD) && !defined(__LITTLE_ENDIAN_BITFIELD)
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#undef FORCE_CONFIG
+
+#undef DEBUG_LINKED_COMMANDS
+#undef DEBUG_DETECT
+#undef DEBUG_PCI_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_RESET
+#undef DEBUG_GENERATE_ERRORS
+#undef DEBUG_GENERATE_ABORTS
+#undef DEBUG_GEOMETRY
+
+#define MAX_ISA 4
+#define MAX_VESA 0
+#define MAX_EISA 15
+#define MAX_PCI 16
+#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
+#define MAX_CHANNEL 4
+#define MAX_LUN 32
+#define MAX_TARGET 32
+#define MAX_MAILBOXES 64
+#define MAX_SGLIST 64
+#define MAX_LARGE_SGLIST 122
+#define MAX_INTERNAL_RETRIES 64
+#define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
+
+#define SKIP ULONG_MAX
+#define FREE 0
+#define IN_USE 1
+#define LOCKED 2
+#define IN_RESET 3
+#define IGNORE 4
+#define READY 5
+#define ABORTING 6
+#define NO_DMA 0xff
+#define MAXLOOP 10000
+#define TAG_DISABLED 0
+#define TAG_SIMPLE 1
+#define TAG_ORDERED 2
+
+#define REG_CMD 7
+#define REG_STATUS 7
+#define REG_AUX_STATUS 8
+#define REG_DATA 0
+#define REG_DATA2 1
+#define REG_SEE 6
+#define REG_LOW 2
+#define REG_LM 3
+#define REG_MID 4
+#define REG_MSB 5
+#define REGION_SIZE 9UL
+#define MAX_ISA_ADDR 0x03ff
+#define MIN_EISA_ADDR 0x1c88
+#define MAX_EISA_ADDR 0xfc88
+#define BSY_ASSERTED 0x80
+#define DRQ_ASSERTED 0x08
+#define ABSY_ASSERTED 0x01
+#define IRQ_ASSERTED 0x02
+#define READ_CONFIG_PIO 0xf0
+#define SET_CONFIG_PIO 0xf1
+#define SEND_CP_PIO 0xf2
+#define RECEIVE_SP_PIO 0xf3
+#define TRUNCATE_XFR_PIO 0xf4
+#define RESET_PIO 0xf9
+#define READ_CONFIG_DMA 0xfd
+#define SET_CONFIG_DMA 0xfe
+#define SEND_CP_DMA 0xff
+#define ASOK 0x00
+#define ASST 0x01
+
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
+
+/* "EATA", in Big Endian format */
+#define EATA_SIG_BE 0x45415441
+
+/* Number of valid bytes in the board config structure for EATA 2.0x */
+#define EATA_2_0A_SIZE 28
+#define EATA_2_0B_SIZE 30
+#define EATA_2_0C_SIZE 34
+
+/* Board info structure */
+struct eata_info {
+ u_int32_t data_len; /* Number of valid bytes after this field */
+ u_int32_t sign; /* ASCII "EATA" signature */
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unchar version : 4,
+ : 4;
+ unchar haaval : 1,
+ ata : 1,
+ drqvld : 1,
+ dmasup : 1,
+ morsup : 1,
+ trnxfr : 1,
+ tarsup : 1,
+ ocsena : 1;
+#else
+ unchar : 4, /* unused low nibble */
+ version : 4; /* EATA version, should be 0x1 */
+ unchar ocsena : 1, /* Overlap Command Support Enabled */
+ tarsup : 1, /* Target Mode Supported */
+ trnxfr : 1, /* Truncate Transfer Cmd NOT Necessary */
+ morsup : 1, /* More Supported */
+ dmasup : 1, /* DMA Supported */
+ drqvld : 1, /* DRQ Index (DRQX) is valid */
+ ata : 1, /* This is an ATA device */
+ haaval : 1; /* Host Adapter Address Valid */
+#endif
+
+ ushort cp_pad_len; /* Number of pad bytes after cp_len */
+ unchar host_addr[4]; /* Host Adapter SCSI ID for channels 3, 2, 1, 0 */
+ u_int32_t cp_len; /* Number of valid bytes in cp */
+ u_int32_t sp_len; /* Number of valid bytes in sp */
+ ushort queue_size; /* Max number of cp that can be queued */
+ ushort unused;
+ ushort scatt_size; /* Max number of entries in scatter/gather table */
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unchar drqx : 2,
+ second : 1,
+ irq_tr : 1,
+ irq : 4;
+ unchar sync;
+ unchar : 4,
+ res1 : 1,
+ large_sg : 1,
+ forcaddr : 1,
+ isaena : 1;
+ unchar max_chan : 3,
+ max_id : 5;
+ unchar max_lun;
+ unchar eisa : 1,
+ pci : 1,
+ idquest : 1,
+ m1 : 1,
+ : 4;
+#else
+ unchar irq : 4, /* Interrupt Request assigned to this controller */
+ irq_tr : 1, /* 0 for edge triggered, 1 for level triggered */
+ second : 1, /* 1 if this is a secondary (not primary) controller */
+ drqx : 2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
+ unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
+
+ /* Structure extension defined in EATA 2.0B */
+ unchar isaena : 1, /* ISA i/o addressing is disabled/enabled */
+ forcaddr : 1, /* Port address has been forced */
+ large_sg : 1, /* 1 if large SG lists are supported */
+ res1 : 1,
+ : 4;
+ unchar max_id : 5, /* Max SCSI target ID number */
+ max_chan : 3; /* Max SCSI channel number on this board */
+
+ /* Structure extension defined in EATA 2.0C */
+ unchar max_lun; /* Max SCSI LUN number */
+ unchar
+ : 4,
+ m1 : 1, /* This is a PCI with an M1 chip installed */
+ idquest : 1, /* RAIDNUM returned is questionable */
+ pci : 1, /* This board is PCI */
+ eisa : 1; /* This board is EISA */
+#endif
+
+ unchar raidnum; /* Uniquely identifies this HBA in a system */
+ unchar notused;
+
+ ushort ipad[247];
+};
+
+/* Board config structure */
+struct eata_config {
+ ushort len; /* Number of bytes following this field */
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unchar : 4,
+ tarena : 1,
+ mdpena : 1,
+ ocena : 1,
+ edis : 1;
+#else
+ unchar edis : 1, /* Disable EATA interface after config command */
+ ocena : 1, /* Overlapped Commands Enabled */
+ mdpena : 1, /* Transfer all Modified Data Pointer Messages */
+ tarena : 1, /* Target Mode Enabled for this controller */
+ : 4;
+#endif
+ unchar cpad[511];
+};
+
+/* Returned status packet structure */
+struct mssp {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unchar eoc : 1,
+ adapter_status : 7;
+#else
+ unchar adapter_status : 7, /* State related to current command */
+ eoc : 1; /* End Of Command (1 = command completed) */
+#endif
+ unchar target_status; /* SCSI status received after data transfer */
+ unchar unused[2];
+ u_int32_t inv_res_len; /* Number of bytes not transferred */
+ u_int32_t cpp_index; /* Index of address set in cp */
+ char mess[12];
+};
+
+struct sg_list {
+ unsigned int address; /* Segment Address */
+ unsigned int num_bytes; /* Segment Length */
+};
+
+/* MailBox SCSI Command Packet */
+struct mscp {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unchar din : 1,
+ dout : 1,
+ interp : 1,
+ : 1,
+ sg : 1,
+ reqsen :1,
+ init : 1,
+ sreset : 1;
+ unchar sense_len;
+ unchar unused[3];
+ unchar : 7,
+ fwnest : 1;
+ unchar : 5,
+ hbaci : 1,
+ iat : 1,
+ phsunit : 1;
+ unchar channel : 3,
+ target : 5;
+ unchar one : 1,
+ dispri : 1,
+ luntar : 1,
+ lun : 5;
+#else
+ unchar sreset :1, /* SCSI Bus Reset Signal should be asserted */
+ init :1, /* Re-initialize controller and self test */
+ reqsen :1, /* Transfer Request Sense Data to addr using DMA */
+ sg :1, /* Use Scatter/Gather */
+ :1,
+ interp :1, /* The controller interprets cp, not the target */
+ dout :1, /* Direction of Transfer is Out (Host to Target) */
+ din :1; /* Direction of Transfer is In (Target to Host) */
+ unchar sense_len; /* Request Sense Length */
+ unchar unused[3];
+ unchar fwnest : 1, /* Send command to a component of an Array Group */
+ : 7;
+ unchar phsunit : 1, /* Send to Target Physical Unit (bypass RAID) */
+ iat : 1, /* Inhibit Address Translation */
+ hbaci : 1, /* Inhibit HBA Caching for this command */
+ : 5;
+ unchar target : 5, /* SCSI target ID */
+ channel : 3; /* SCSI channel number */
+ unchar lun : 5, /* SCSI logical unit number */
+ luntar : 1, /* This cp is for Target (not LUN) */
+ dispri : 1, /* Disconnect Privilege granted */
+ one : 1; /* 1 */
+#endif
+
+ unchar mess[3]; /* Massage to/from Target */
+ unchar cdb[12]; /* Command Descriptor Block */
+ u_int32_t data_len; /* If sg=0 Data Length, if sg=1 sglist length */
+ u_int32_t cpp_index; /* Index of address to be returned in sp */
+ u_int32_t data_address; /* If sg=0 Data Address, if sg=1 sglist address */
+ u_int32_t sp_dma_addr; /* Address where sp is DMA'ed when cp completes */
+ u_int32_t sense_addr; /* Address where Sense Data is DMA'ed on error */
+
+ /* Additional fields begin here. */
+ struct scsi_cmnd *SCpnt;
+
+ /* All the cp structure is zero filled by queuecommand except the
+ following CP_TAIL_SIZE bytes, initialized by detect */
+ dma_addr_t cp_dma_addr; /* dma handle for this cp structure */
+ struct sg_list *sglist; /* pointer to the allocated SG list */
+};
+
+#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t))
+
+struct hostdata {
+ struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
+ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
+ unsigned int last_cp_used; /* Index of last mailbox used */
+ unsigned int iocount; /* Total i/o done for this board */
+ int board_number; /* Number of this board */
+ char board_name[16]; /* Name of this board */
+ int in_reset; /* True if board is doing a reset */
+ int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */
+ int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If 1 redo i/o on target */
+ unsigned int retries; /* Number of internal retries */
+ unsigned long last_retried_pid; /* Pid of last retried command */
+ unsigned char subversion; /* Bus type, either ISA or EISA/PCI */
+ unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
+ unsigned char is_pci; /* 1 is bus type is PCI */
+ struct pci_dev *pdev; /* pdev for PCI bus, NULL otherwise */
+ struct mssp *sp_cpu_addr; /* cpu addr for DMA buffer sp */
+ dma_addr_t sp_dma_addr; /* dma handle for DMA buffer sp */
+ struct mssp sp; /* Local copy of sp buffer */
+};
+
+static struct Scsi_Host *sh[MAX_BOARDS];
+static const char *driver_name = "EATA";
+static char sha[MAX_BOARDS];
+static DEFINE_SPINLOCK(driver_lock);
+
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] = {
+
+ /* Space for MAX_INT_PARAM ports usable while loading as a module */
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+ SKIP, SKIP,
+
+ /* First ISA */
+ 0x1f0,
+
+ /* Space for MAX_PCI ports possibly reported by PCI_BIOS */
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+
+ /* MAX_EISA ports */
+ 0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
+ 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
+
+ /* Other (MAX_ISA - 1) ports */
+ 0x170, 0x230, 0x330,
+
+ /* End of list */
+ 0x0
+};
+
+/* Device is Big Endian */
+#define H2DEV(x) cpu_to_be32(x)
+#define DEV2H(x) be32_to_cpu(x)
+#define H2DEV16(x) cpu_to_be16(x)
+#define DEV2H16(x) be16_to_cpu(x)
+
+/* But transfer orientation from the 16 bit data register is Little Endian */
+#define REG2H(x) le16_to_cpu(x)
+
+static irqreturn_t do_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(struct scsi_device *, unsigned long, struct hostdata *,
+ unsigned int);
+static int do_trace = 0;
+static int setup_done = 0;
+static int link_statistics;
+static int ext_tran = 0;
+static int rev_scan = 1;
+
+#if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
+static int tag_mode = TAG_SIMPLE;
+#else
+static int tag_mode = TAG_DISABLED;
+#endif
+
+#if defined(CONFIG_SCSI_EATA_LINKED_COMMANDS)
+static int linked_comm = 1;
+#else
+static int linked_comm = 0;
+#endif
+
+#if defined(CONFIG_SCSI_EATA_MAX_TAGS)
+static int max_queue_depth = CONFIG_SCSI_EATA_MAX_TAGS;
+#else
+static int max_queue_depth = MAX_CMD_PER_LUN;
+#endif
+
+#if defined(CONFIG_ISA)
+static int isa_probe = 1;
+#else
+static int isa_probe = 0;
+#endif
+
+#if defined(CONFIG_EISA)
+static int eisa_probe = 1;
+#else
+static int eisa_probe = 0;
+#endif
+
+#if defined(CONFIG_PCI)
+static int pci_probe = 1;
+#else
+static int pci_probe = 0;
+#endif
+
+#define MAX_INT_PARAM 10
+#define MAX_BOOT_OPTIONS_SIZE 256
+static char boot_options[MAX_BOOT_OPTIONS_SIZE];
+
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+module_param_string(eata, boot_options, MAX_BOOT_OPTIONS_SIZE, 0);
+MODULE_PARM_DESC(eata, " equivalent to the \"eata=...\" kernel boot option."
+ " Example: modprobe eata \"eata=0x7410,0x230,lc:y,tm:0,mq:4,ep:n\"");
+MODULE_AUTHOR("Dario Ballabio");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EATA/DMA SCSI Driver");
+
+#endif
+
+static int eata2x_slave_configure(struct scsi_device *dev)
+{
+ int tqd, utqd;
+ char *tag_suffix, *link_suffix;
+ struct Scsi_Host *shost = dev->host;
+ struct hostdata *ha = (struct hostdata *)shost->hostdata;
+
+ utqd = MAX_CMD_PER_LUN;
+ tqd = max_queue_depth;
+
+ if (TLDEV(dev->type) && dev->tagged_supported) {
+ if (tag_mode == TAG_SIMPLE) {
+ scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd);
+ tag_suffix = ", simple tags";
+ } else if (tag_mode == TAG_ORDERED) {
+ scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd);
+ tag_suffix = ", ordered tags";
+ } else {
+ scsi_adjust_queue_depth(dev, 0, tqd);
+ tag_suffix = ", no tags";
+ }
+ } else if (TLDEV(dev->type) && linked_comm) {
+ scsi_adjust_queue_depth(dev, 0, tqd);
+ tag_suffix = ", untagged";
+ } else {
+ scsi_adjust_queue_depth(dev, 0, utqd);
+ tag_suffix = "";
+ }
+
+ if (TLDEV(dev->type) && linked_comm && dev->queue_depth > 2)
+ link_suffix = ", sorted";
+ else if (TLDEV(dev->type))
+ link_suffix = ", unsorted";
+ else
+ link_suffix = "";
+
+ printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
+ ha->board_name, shost->host_no, dev->channel, dev->id, dev->lun,
+ dev->queue_depth, link_suffix, tag_suffix);
+
+ return 0;
+}
+
+static int wait_on_busy(unsigned long iobase, unsigned int loop)
+{
+ while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED) {
+ udelay(1L);
+ if (--loop == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int do_dma(unsigned long iobase, unsigned long addr, unchar cmd)
+{
+ unsigned char *byaddr;
+ unsigned long devaddr;
+
+ if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP)))
+ return 1;
+
+ if (addr) {
+ devaddr = H2DEV(addr);
+ byaddr = (unsigned char *)&devaddr;
+ outb(byaddr[3], iobase + REG_LOW);
+ outb(byaddr[2], iobase + REG_LM);
+ outb(byaddr[1], iobase + REG_MID);
+ outb(byaddr[0], iobase + REG_MSB);
+ }
+
+ outb(cmd, iobase + REG_CMD);
+ return 0;
+}
+
+static int read_pio(unsigned long iobase, ushort * start, ushort * end)
+{
+ unsigned int loop = MAXLOOP;
+ ushort *p;
+
+ for (p = start; p <= end; p++) {
+ while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED)) {
+ udelay(1L);
+ if (--loop == 0)
+ return 1;
+ }
+ loop = MAXLOOP;
+ *p = REG2H(inw(iobase));
+ }
+
+ return 0;
+}
+
+static struct pci_dev *get_pci_dev(unsigned long port_base)
+{
+#if defined(CONFIG_PCI)
+ unsigned int addr;
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
+ addr = pci_resource_start(dev, 0);
+
+#if defined(DEBUG_PCI_DETECT)
+ printk("%s: get_pci_dev, bus %d, devfn 0x%x, addr 0x%x.\n",
+ driver_name, dev->bus->number, dev->devfn, addr);
+#endif
+
+ /* we are in so much trouble for a pci hotplug system with this driver
+ * anyway, so doing this at least lets people unload the driver and not
+ * cause memory problems, but in general this is a bad thing to do (this
+ * driver needs to be converted to the proper PCI api someday... */
+ pci_dev_put(dev);
+ if (addr + PCI_BASE_ADDRESS_0 == port_base)
+ return dev;
+ }
+#endif /* end CONFIG_PCI */
+ return NULL;
+}
+
+static void enable_pci_ports(void)
+{
+#if defined(CONFIG_PCI)
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
+#if defined(DEBUG_PCI_DETECT)
+ printk("%s: enable_pci_ports, bus %d, devfn 0x%x.\n",
+ driver_name, dev->bus->number, dev->devfn);
+#endif
+
+ if (pci_enable_device(dev))
+ printk
+ ("%s: warning, pci_enable_device failed, bus %d devfn 0x%x.\n",
+ driver_name, dev->bus->number, dev->devfn);
+ }
+
+#endif /* end CONFIG_PCI */
+}
+
+static int port_detect(unsigned long port_base, unsigned int j,
+ struct scsi_host_template *tpnt)
+{
+ unsigned char irq, dma_channel, subversion, i, is_pci = 0;
+ unsigned char protocol_rev;
+ struct eata_info info;
+ char *bus_type, dma_name[16];
+ struct pci_dev *pdev;
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+ struct Scsi_Host *shost;
+ struct hostdata *ha;
+ char name[16];
+
+ sprintf(name, "%s%d", driver_name, j);
+
+ if (!request_region(port_base, REGION_SIZE, driver_name)) {
+#if defined(DEBUG_DETECT)
+ printk("%s: address 0x%03lx in use, skipping probe.\n", name,
+ port_base);
+#endif
+ goto fail;
+ }
+
+ spin_lock_irq(&driver_lock);
+
+ if (do_dma(port_base, 0, READ_CONFIG_PIO)) {
+#if defined(DEBUG_DETECT)
+ printk("%s: detect, do_dma failed at 0x%03lx.\n", name,
+ port_base);
+#endif
+ goto freelock;
+ }
+
+ /* Read the info structure */
+ if (read_pio(port_base, (ushort *) & info, (ushort *) & info.ipad[0])) {
+#if defined(DEBUG_DETECT)
+ printk("%s: detect, read_pio failed at 0x%03lx.\n", name,
+ port_base);
+#endif
+ goto freelock;
+ }
+
+ info.data_len = DEV2H(info.data_len);
+ info.sign = DEV2H(info.sign);
+ info.cp_pad_len = DEV2H16(info.cp_pad_len);
+ info.cp_len = DEV2H(info.cp_len);
+ info.sp_len = DEV2H(info.sp_len);
+ info.scatt_size = DEV2H16(info.scatt_size);
+ info.queue_size = DEV2H16(info.queue_size);
+
+ /* Check the controller "EATA" signature */
+ if (info.sign != EATA_SIG_BE) {
+#if defined(DEBUG_DETECT)
+ printk("%s: signature 0x%04x discarded.\n", name, info.sign);
+#endif
+ goto freelock;
+ }
+
+ if (info.data_len < EATA_2_0A_SIZE) {
+ printk
+ ("%s: config structure size (%d bytes) too short, detaching.\n",
+ name, info.data_len);
+ goto freelock;
+ } else if (info.data_len == EATA_2_0A_SIZE)
+ protocol_rev = 'A';
+ else if (info.data_len == EATA_2_0B_SIZE)
+ protocol_rev = 'B';
+ else
+ protocol_rev = 'C';
+
+ if (protocol_rev != 'A' && info.forcaddr) {
+ printk("%s: warning, port address has been forced.\n", name);
+ bus_type = "PCI";
+ is_pci = 1;
+ subversion = ESA;
+ } else if (port_base > MAX_EISA_ADDR
+ || (protocol_rev == 'C' && info.pci)) {
+ bus_type = "PCI";
+ is_pci = 1;
+ subversion = ESA;
+ } else if (port_base >= MIN_EISA_ADDR
+ || (protocol_rev == 'C' && info.eisa)) {
+ bus_type = "EISA";
+ subversion = ESA;
+ } else if (protocol_rev == 'C' && !info.eisa && !info.pci) {
+ bus_type = "ISA";
+ subversion = ISA;
+ } else if (port_base > MAX_ISA_ADDR) {
+ bus_type = "PCI";
+ is_pci = 1;
+ subversion = ESA;
+ } else {
+ bus_type = "ISA";
+ subversion = ISA;
+ }
+
+ if (!info.haaval || info.ata) {
+ printk
+ ("%s: address 0x%03lx, unusable %s board (%d%d), detaching.\n",
+ name, port_base, bus_type, info.haaval, info.ata);
+ goto freelock;
+ }
+
+ if (info.drqvld) {
+ if (subversion == ESA)
+ printk("%s: warning, weird %s board using DMA.\n", name,
+ bus_type);
+
+ subversion = ISA;
+ dma_channel = dma_channel_table[3 - info.drqx];
+ } else {
+ if (subversion == ISA)
+ printk("%s: warning, weird %s board not using DMA.\n",
+ name, bus_type);
+
+ subversion = ESA;
+ dma_channel = NO_DMA;
+ }
+
+ if (!info.dmasup)
+ printk("%s: warning, DMA protocol support not asserted.\n",
+ name);
+
+ irq = info.irq;
+
+ if (subversion == ESA && !info.irq_tr)
+ printk
+ ("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
+ name, irq);
+
+ if (is_pci) {
+ pdev = get_pci_dev(port_base);
+ if (!pdev)
+ printk
+ ("%s: warning, failed to get pci_dev structure.\n",
+ name);
+ } else
+ pdev = NULL;
+
+ if (pdev && (irq != pdev->irq)) {
+ printk("%s: IRQ %u mapped to IO-APIC IRQ %u.\n", name, irq,
+ pdev->irq);
+ irq = pdev->irq;
+ }
+
+ /* Board detected, allocate its IRQ */
+ if (request_irq(irq, do_interrupt_handler,
+ SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+ driver_name, (void *)&sha[j])) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name,
+ irq);
+ goto freelock;
+ }
+
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ goto freeirq;
+ }
+#if defined(FORCE_CONFIG)
+ {
+ struct eata_config *cf;
+ dma_addr_t cf_dma_addr;
+
+ cf = pci_alloc_consistent(pdev, sizeof(struct eata_config),
+ &cf_dma_addr);
+
+ if (!cf) {
+ printk
+ ("%s: config, pci_alloc_consistent failed, detaching.\n",
+ name);
+ goto freedma;
+ }
+
+ /* Set board configuration */
+ memset((char *)cf, 0, sizeof(struct eata_config));
+ cf->len = (ushort) H2DEV16((ushort) 510);
+ cf->ocena = 1;
+
+ if (do_dma(port_base, cf_dma_addr, SET_CONFIG_DMA)) {
+ printk
+ ("%s: busy timeout sending configuration, detaching.\n",
+ name);
+ pci_free_consistent(pdev, sizeof(struct eata_config),
+ cf, cf_dma_addr);
+ goto freedma;
+ }
+
+ }
+#endif
+
+ spin_unlock_irq(&driver_lock);
+ sh[j] = shost = scsi_register(tpnt, sizeof(struct hostdata));
+ spin_lock_irq(&driver_lock);
+
+ if (shost == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+ goto freedma;
+ }
+
+ shost->io_port = port_base;
+ shost->unique_id = port_base;
+ shost->n_io_port = REGION_SIZE;
+ shost->dma_channel = dma_channel;
+ shost->irq = irq;
+ shost->sg_tablesize = (ushort) info.scatt_size;
+ shost->this_id = (ushort) info.host_addr[3];
+ shost->can_queue = (ushort) info.queue_size;
+ shost->cmd_per_lun = MAX_CMD_PER_LUN;
+
+ ha = (struct hostdata *)shost->hostdata;
+
+ memset(ha, 0, sizeof(struct hostdata));
+ ha->subversion = subversion;
+ ha->protocol_rev = protocol_rev;
+ ha->is_pci = is_pci;
+ ha->pdev = pdev;
+ ha->board_number = j;
+
+ if (ha->subversion == ESA)
+ shost->unchecked_isa_dma = 0;
+ else {
+ unsigned long flags;
+ shost->unchecked_isa_dma = 1;
+
+ flags = claim_dma_lock();
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ release_dma_lock(flags);
+
+ }
+
+ strcpy(ha->board_name, name);
+
+ /* DPT PM2012 does not allow to detect sg_tablesize correctly */
+ if (shost->sg_tablesize > MAX_SGLIST || shost->sg_tablesize < 2) {
+ printk("%s: detect, wrong n. of SG lists %d, fixed.\n",
+ ha->board_name, shost->sg_tablesize);
+ shost->sg_tablesize = MAX_SGLIST;
+ }
+
+ /* DPT PM2012 does not allow to detect can_queue correctly */
+ if (shost->can_queue > MAX_MAILBOXES || shost->can_queue < 2) {
+ printk("%s: detect, wrong n. of mbox %d, fixed.\n",
+ ha->board_name, shost->can_queue);
+ shost->can_queue = MAX_MAILBOXES;
+ }
+
+ if (protocol_rev != 'A') {
+ if (info.max_chan > 0 && info.max_chan < MAX_CHANNEL)
+ shost->max_channel = info.max_chan;
+
+ if (info.max_id > 7 && info.max_id < MAX_TARGET)
+ shost->max_id = info.max_id + 1;
+
+ if (info.large_sg && shost->sg_tablesize == MAX_SGLIST)
+ shost->sg_tablesize = MAX_LARGE_SGLIST;
+ }
+
+ if (protocol_rev == 'C') {
+ if (info.max_lun > 7 && info.max_lun < MAX_LUN)
+ shost->max_lun = info.max_lun + 1;
+ }
+
+ if (dma_channel == NO_DMA)
+ sprintf(dma_name, "%s", "BMST");
+ else
+ sprintf(dma_name, "DMA %u", dma_channel);
+
+ spin_unlock_irq(&driver_lock);
+
+ for (i = 0; i < shost->can_queue; i++)
+ ha->cp[i].cp_dma_addr = pci_map_single(ha->pdev,
+ &ha->cp[i],
+ sizeof(struct mscp),
+ PCI_DMA_BIDIRECTIONAL);
+
+ for (i = 0; i < shost->can_queue; i++) {
+ size_t sz = shost->sg_tablesize *sizeof(struct sg_list);
+ unsigned int gfp_mask = (shost->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC;
+ ha->cp[i].sglist = kmalloc(sz, gfp_mask);
+ if (!ha->cp[i].sglist) {
+ printk
+ ("%s: kmalloc SGlist failed, mbox %d, detaching.\n",
+ ha->board_name, i);
+ goto release;
+ }
+ }
+
+ if (!(ha->sp_cpu_addr = pci_alloc_consistent(ha->pdev,
+ sizeof(struct mssp),
+ &ha->sp_dma_addr))) {
+ printk("%s: pci_alloc_consistent failed, detaching.\n", ha->board_name);
+ goto release;
+ }
+
+ if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
+ max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
+
+ if (max_queue_depth < MAX_CMD_PER_LUN)
+ max_queue_depth = MAX_CMD_PER_LUN;
+
+ if (tag_mode != TAG_DISABLED && tag_mode != TAG_SIMPLE)
+ tag_mode = TAG_ORDERED;
+
+ if (j == 0) {
+ printk
+ ("EATA/DMA 2.0x: Copyright (C) 1994-2003 Dario Ballabio.\n");
+ printk
+ ("%s config options -> tm:%d, lc:%c, mq:%d, rs:%c, et:%c, "
+ "ip:%c, ep:%c, pp:%c.\n", driver_name, tag_mode,
+ YESNO(linked_comm), max_queue_depth, YESNO(rev_scan),
+ YESNO(ext_tran), YESNO(isa_probe), YESNO(eisa_probe),
+ YESNO(pci_probe));
+ }
+
+ printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n",
+ ha->board_name, ha->protocol_rev, bus_type,
+ (unsigned long)shost->io_port, shost->irq, dma_name,
+ shost->sg_tablesize, shost->can_queue);
+
+ if (shost->max_id > 8 || shost->max_lun > 8)
+ printk
+ ("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
+ ha->board_name, shost->max_id, shost->max_lun);
+
+ for (i = 0; i <= shost->max_channel; i++)
+ printk("%s: SCSI channel %u enabled, host target ID %d.\n",
+ ha->board_name, i, info.host_addr[3 - i]);
+
+#if defined(DEBUG_DETECT)
+ printk("%s: Vers. 0x%x, ocs %u, tar %u, trnxfr %u, more %u, SYNC 0x%x, "
+ "sec. %u, infol %d, cpl %d spl %d.\n", name, info.version,
+ info.ocsena, info.tarsup, info.trnxfr, info.morsup, info.sync,
+ info.second, info.data_len, info.cp_len, info.sp_len);
+
+ if (protocol_rev == 'B' || protocol_rev == 'C')
+ printk("%s: isaena %u, forcaddr %u, max_id %u, max_chan %u, "
+ "large_sg %u, res1 %u.\n", name, info.isaena,
+ info.forcaddr, info.max_id, info.max_chan, info.large_sg,
+ info.res1);
+
+ if (protocol_rev == 'C')
+ printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "
+ "raidnum %u.\n", name, info.max_lun, info.m1,
+ info.idquest, info.pci, info.eisa, info.raidnum);
+#endif
+
+ if (ha->pdev) {
+ pci_set_master(ha->pdev);
+ if (pci_set_dma_mask(ha->pdev, 0xffffffff))
+ printk("%s: warning, pci_set_dma_mask failed.\n",
+ ha->board_name);
+ }
+
+ return 1;
+
+ freedma:
+ if (subversion == ISA)
+ free_dma(dma_channel);
+ freeirq:
+ free_irq(irq, &sha[j]);
+ freelock:
+ spin_unlock_irq(&driver_lock);
+ release_region(port_base, REGION_SIZE);
+ fail:
+ return 0;
+
+ release:
+ eata2x_release(shost);
+ return 0;
+}
+
+static void internal_setup(char *str, int *ints)
+{
+ int i, argc = ints[0];
+ char *cur = str, *pc;
+
+ if (argc > 0) {
+ if (argc > MAX_INT_PARAM)
+ argc = MAX_INT_PARAM;
+
+ for (i = 0; i < argc; i++)
+ io_port[i] = ints[i + 1];
+
+ io_port[i] = 0;
+ setup_done = 1;
+ }
+
+ while (cur && (pc = strchr(cur, ':'))) {
+ int val = 0, c = *++pc;
+
+ if (c == 'n' || c == 'N')
+ val = 0;
+ else if (c == 'y' || c == 'Y')
+ val = 1;
+ else
+ val = (int)simple_strtoul(pc, NULL, 0);
+
+ if (!strncmp(cur, "lc:", 3))
+ linked_comm = val;
+ else if (!strncmp(cur, "tm:", 3))
+ tag_mode = val;
+ else if (!strncmp(cur, "tc:", 3))
+ tag_mode = val;
+ else if (!strncmp(cur, "mq:", 3))
+ max_queue_depth = val;
+ else if (!strncmp(cur, "ls:", 3))
+ link_statistics = val;
+ else if (!strncmp(cur, "et:", 3))
+ ext_tran = val;
+ else if (!strncmp(cur, "rs:", 3))
+ rev_scan = val;
+ else if (!strncmp(cur, "ip:", 3))
+ isa_probe = val;
+ else if (!strncmp(cur, "ep:", 3))
+ eisa_probe = val;
+ else if (!strncmp(cur, "pp:", 3))
+ pci_probe = val;
+
+ if ((cur = strchr(cur, ',')))
+ ++cur;
+ }
+
+ return;
+}
+
+static int option_setup(char *str)
+{
+ int ints[MAX_INT_PARAM];
+ char *cur = str;
+ int i = 1;
+
+ while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+ ints[i++] = simple_strtoul(cur, NULL, 0);
+
+ if ((cur = strchr(cur, ',')) != NULL)
+ cur++;
+ }
+
+ ints[0] = i - 1;
+ internal_setup(cur, ints);
+ return 1;
+}
+
+static void add_pci_ports(void)
+{
+#if defined(CONFIG_PCI)
+ unsigned int addr, k;
+ struct pci_dev *dev = NULL;
+
+ for (k = 0; k < MAX_PCI; k++) {
+
+ if (!(dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev)))
+ break;
+
+ if (pci_enable_device(dev)) {
+#if defined(DEBUG_PCI_DETECT)
+ printk
+ ("%s: detect, bus %d, devfn 0x%x, pci_enable_device failed.\n",
+ driver_name, dev->bus->number, dev->devfn);
+#endif
+
+ continue;
+ }
+
+ addr = pci_resource_start(dev, 0);
+
+#if defined(DEBUG_PCI_DETECT)
+ printk("%s: detect, seq. %d, bus %d, devfn 0x%x, addr 0x%x.\n",
+ driver_name, k, dev->bus->number, dev->devfn, addr);
+#endif
+
+ /* Order addresses according to rev_scan value */
+ io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
+ addr + PCI_BASE_ADDRESS_0;
+ }
+
+ pci_dev_put(dev);
+#endif /* end CONFIG_PCI */
+}
+
+static int eata2x_detect(struct scsi_host_template *tpnt)
+{
+ unsigned int j = 0, k;
+
+ tpnt->proc_name = "eata2x";
+
+ if (strlen(boot_options))
+ option_setup(boot_options);
+
+#if defined(MODULE)
+ /* io_port could have been modified when loading as a module */
+ if (io_port[0] != SKIP) {
+ setup_done = 1;
+ io_port[MAX_INT_PARAM] = 0;
+ }
+#endif
+
+ for (k = MAX_INT_PARAM; io_port[k]; k++)
+ if (io_port[k] == SKIP)
+ continue;
+ else if (io_port[k] <= MAX_ISA_ADDR) {
+ if (!isa_probe)
+ io_port[k] = SKIP;
+ } else if (io_port[k] >= MIN_EISA_ADDR
+ && io_port[k] <= MAX_EISA_ADDR) {
+ if (!eisa_probe)
+ io_port[k] = SKIP;
+ }
+
+ if (pci_probe) {
+ if (!setup_done)
+ add_pci_ports();
+ else
+ enable_pci_ports();
+ }
+
+ for (k = 0; io_port[k]; k++) {
+
+ if (io_port[k] == SKIP)
+ continue;
+
+ if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt))
+ j++;
+ }
+
+ num_boards = j;
+ return j;
+}
+
+static void map_dma(unsigned int i, struct hostdata *ha)
+{
+ unsigned int k, count, pci_dir;
+ struct scatterlist *sgpnt;
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &ha->cp[i];
+ SCpnt = cpp->SCpnt;
+ pci_dir = SCpnt->sc_data_direction;
+
+ if (SCpnt->sense_buffer)
+ cpp->sense_addr =
+ H2DEV(pci_map_single(ha->pdev, SCpnt->sense_buffer,
+ sizeof SCpnt->sense_buffer, PCI_DMA_FROMDEVICE));
+
+ cpp->sense_len = sizeof SCpnt->sense_buffer;
+
+ if (!SCpnt->use_sg) {
+
+ /* If we get here with PCI_DMA_NONE, pci_map_single triggers a BUG() */
+ if (!SCpnt->request_bufflen)
+ pci_dir = PCI_DMA_BIDIRECTIONAL;
+
+ if (SCpnt->request_buffer)
+ cpp->data_address = H2DEV(pci_map_single(ha->pdev,
+ SCpnt->
+ request_buffer,
+ SCpnt->
+ request_bufflen,
+ pci_dir));
+
+ cpp->data_len = H2DEV(SCpnt->request_bufflen);
+ return;
+ }
+
+ sgpnt = (struct scatterlist *)SCpnt->request_buffer;
+ count = pci_map_sg(ha->pdev, sgpnt, SCpnt->use_sg, pci_dir);
+
+ for (k = 0; k < count; k++) {
+ cpp->sglist[k].address = H2DEV(sg_dma_address(&sgpnt[k]));
+ cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(&sgpnt[k]));
+ }
+
+ cpp->sg = 1;
+ cpp->data_address = H2DEV(pci_map_single(ha->pdev, cpp->sglist,
+ SCpnt->use_sg *
+ sizeof(struct sg_list),
+ pci_dir));
+ cpp->data_len = H2DEV((SCpnt->use_sg * sizeof(struct sg_list)));
+}
+
+static void unmap_dma(unsigned int i, struct hostdata *ha)
+{
+ unsigned int pci_dir;
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &ha->cp[i];
+ SCpnt = cpp->SCpnt;
+ pci_dir = SCpnt->sc_data_direction;
+
+ if (DEV2H(cpp->sense_addr))
+ pci_unmap_single(ha->pdev, DEV2H(cpp->sense_addr),
+ DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
+
+ if (SCpnt->use_sg)
+ pci_unmap_sg(ha->pdev, SCpnt->request_buffer, SCpnt->use_sg,
+ pci_dir);
+
+ if (!DEV2H(cpp->data_len))
+ pci_dir = PCI_DMA_BIDIRECTIONAL;
+
+ if (DEV2H(cpp->data_address))
+ pci_unmap_single(ha->pdev, DEV2H(cpp->data_address),
+ DEV2H(cpp->data_len), pci_dir);
+}
+
+static void sync_dma(unsigned int i, struct hostdata *ha)
+{
+ unsigned int pci_dir;
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &ha->cp[i];
+ SCpnt = cpp->SCpnt;
+ pci_dir = SCpnt->sc_data_direction;
+
+ if (DEV2H(cpp->sense_addr))
+ pci_dma_sync_single_for_cpu(ha->pdev, DEV2H(cpp->sense_addr),
+ DEV2H(cpp->sense_len),
+ PCI_DMA_FROMDEVICE);
+
+ if (SCpnt->use_sg)
+ pci_dma_sync_sg_for_cpu(ha->pdev, SCpnt->request_buffer,
+ SCpnt->use_sg, pci_dir);
+
+ if (!DEV2H(cpp->data_len))
+ pci_dir = PCI_DMA_BIDIRECTIONAL;
+
+ if (DEV2H(cpp->data_address))
+ pci_dma_sync_single_for_cpu(ha->pdev,
+ DEV2H(cpp->data_address),
+ DEV2H(cpp->data_len), pci_dir);
+}
+
+static void scsi_to_dev_dir(unsigned int i, struct hostdata *ha)
+{
+ unsigned int k;
+
+ static const unsigned char data_out_cmds[] = {
+ 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+ 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
+ 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b, 0x5d
+ };
+
+ static const unsigned char data_none_cmds[] = {
+ 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+ 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+ 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00
+ };
+
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &ha->cp[i];
+ SCpnt = cpp->SCpnt;
+
+ if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
+ cpp->din = 1;
+ cpp->dout = 0;
+ return;
+ } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
+ cpp->din = 0;
+ cpp->dout = 1;
+ return;
+ } else if (SCpnt->sc_data_direction == DMA_NONE) {
+ cpp->din = 0;
+ cpp->dout = 0;
+ return;
+ }
+
+ if (SCpnt->sc_data_direction != DMA_BIDIRECTIONAL)
+ panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n",
+ ha->board_name);
+
+ for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+ if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+ cpp->dout = 1;
+ break;
+ }
+
+ if ((cpp->din = !cpp->dout))
+ for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+ if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+ cpp->din = 0;
+ break;
+ }
+
+}
+
+static int eata2x_queuecommand(struct scsi_cmnd *SCpnt,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct Scsi_Host *shost = SCpnt->device->host;
+ struct hostdata *ha = (struct hostdata *)shost->hostdata;
+ unsigned int i, k;
+ struct mscp *cpp;
+
+ if (SCpnt->host_scribble)
+ panic("%s: qcomm, pid %ld, SCpnt %p already active.\n",
+ ha->board_name, SCpnt->pid, SCpnt);
+
+ /* i is the mailbox number, look for the first free mailbox
+ starting from last_cp_used */
+ i = ha->last_cp_used + 1;
+
+ for (k = 0; k < shost->can_queue; k++, i++) {
+ if (i >= shost->can_queue)
+ i = 0;
+ if (ha->cp_stat[i] == FREE) {
+ ha->last_cp_used = i;
+ break;
+ }
+ }
+
+ if (k == shost->can_queue) {
+ printk("%s: qcomm, no free mailbox.\n", ha->board_name);
+ return 1;
+ }
+
+ /* Set pointer to control packet structure */
+ cpp = &ha->cp[i];
+
+ memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE);
+
+ /* Set pointer to status packet structure, Big Endian format */
+ cpp->sp_dma_addr = H2DEV(ha->sp_dma_addr);
+
+ SCpnt->scsi_done = done;
+ cpp->cpp_index = i;
+ SCpnt->host_scribble = (unsigned char *)&cpp->cpp_index;
+
+ if (do_trace)
+ printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
+ ha->board_name, i, SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid);
+
+ cpp->reqsen = 1;
+ cpp->dispri = 1;
+#if 0
+ if (SCpnt->device->type == TYPE_TAPE)
+ cpp->hbaci = 1;
+#endif
+ cpp->one = 1;
+ cpp->channel = SCpnt->device->channel;
+ cpp->target = SCpnt->device->id;
+ cpp->lun = SCpnt->device->lun;
+ cpp->SCpnt = SCpnt;
+ memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ /* Use data transfer direction SCpnt->sc_data_direction */
+ scsi_to_dev_dir(i, ha);
+
+ /* Map DMA buffers and SG list */
+ map_dma(i, ha);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type)) {
+ ha->cp_stat[i] = READY;
+ flush_dev(SCpnt->device, SCpnt->request->sector, ha, 0);
+ return 0;
+ }
+
+ /* Send control packet to the board */
+ if (do_dma(shost->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
+ unmap_dma(i, ha);
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n",
+ ha->board_name, SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid);
+ return 1;
+ }
+
+ ha->cp_stat[i] = IN_USE;
+ return 0;
+}
+
+static int eata2x_eh_abort(struct scsi_cmnd *SCarg)
+{
+ struct Scsi_Host *shost = SCarg->device->host;
+ struct hostdata *ha = (struct hostdata *)shost->hostdata;
+ unsigned int i;
+
+ if (SCarg->host_scribble == NULL) {
+ printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
+ ha->board_name, SCarg->device->channel, SCarg->device->id,
+ SCarg->device->lun, SCarg->pid);
+ return SUCCESS;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
+ ha->board_name, i, SCarg->device->channel, SCarg->device->id,
+ SCarg->device->lun, SCarg->pid);
+
+ if (i >= shost->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", ha->board_name);
+
+ if (wait_on_busy(shost->io_port, MAXLOOP)) {
+ printk("%s: abort, timeout error.\n", ha->board_name);
+ return FAILED;
+ }
+
+ if (ha->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", ha->board_name, i);
+ return SUCCESS;
+ }
+
+ if (ha->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", ha->board_name, i);
+
+ if (SCarg != ha->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ ha->board_name, i, SCarg, ha->cp[i].SCpnt);
+
+ if (inb(shost->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
+ printk("%s: abort, mbox %d, interrupt pending.\n",
+ ha->board_name, i);
+
+ if (SCarg->eh_state == SCSI_STATE_TIMEOUT) {
+ unmap_dma(i, ha);
+ SCarg->host_scribble = NULL;
+ ha->cp_stat[i] = FREE;
+ printk
+ ("%s, abort, mbox %d, eh_state timeout, pid %ld.\n",
+ ha->board_name, i, SCarg->pid);
+ return SUCCESS;
+ }
+
+ return FAILED;
+ }
+
+ if (ha->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", ha->board_name, i);
+ return FAILED;
+ }
+
+ if (ha->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", ha->board_name, i);
+ return SUCCESS;
+ }
+
+ if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
+ unmap_dma(i, ha);
+ SCarg->result = DID_ABORT << 16;
+ SCarg->host_scribble = NULL;
+ ha->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+ ha->board_name, i, SCarg->pid);
+ SCarg->scsi_done(SCarg);
+ return SUCCESS;
+ }
+
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", ha->board_name, i);
+}
+
+static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
+{
+ unsigned int i, time, k, c, limit = 0;
+ int arg_done = 0;
+ struct scsi_cmnd *SCpnt;
+ struct Scsi_Host *shost = SCarg->device->host;
+ struct hostdata *ha = (struct hostdata *)shost->hostdata;
+
+ printk("%s: reset, enter, target %d.%d:%d, pid %ld.\n",
+ ha->board_name, SCarg->device->channel, SCarg->device->id,
+ SCarg->device->lun, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", ha->board_name, SCarg->pid);
+
+ if (ha->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", ha->board_name);
+ return FAILED;
+ }
+
+ if (wait_on_busy(shost->io_port, MAXLOOP)) {
+ printk("%s: reset, exit, timeout error.\n", ha->board_name);
+ return FAILED;
+ }
+
+ ha->retries = 0;
+
+ for (c = 0; c <= shost->max_channel; c++)
+ for (k = 0; k < shost->max_id; k++) {
+ ha->target_redo[k][c] = 1;
+ ha->target_to[k][c] = 0;
+ }
+
+ for (i = 0; i < shost->can_queue; i++) {
+
+ if (ha->cp_stat[i] == FREE)
+ continue;
+
+ if (ha->cp_stat[i] == LOCKED) {
+ ha->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n",
+ ha->board_name, i);
+ continue;
+ }
+
+ if (!(SCpnt = ha->cp[i].SCpnt))
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
+
+ if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
+ ha->cp_stat[i] = ABORTING;
+ printk("%s: reset, mbox %d aborting, pid %ld.\n",
+ ha->board_name, i, SCpnt->pid);
+ }
+
+ else {
+ ha->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ ha->board_name, i, SCpnt->pid);
+ }
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", ha->board_name, i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", ha->board_name, i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n",
+ ha->board_name, i);
+
+ if (SCpnt == SCarg)
+ arg_done = 1;
+ }
+
+ if (do_dma(shost->io_port, 0, RESET_PIO)) {
+ printk("%s: reset, cannot reset, timeout error.\n", ha->board_name);
+ return FAILED;
+ }
+
+ printk("%s: reset, board reset done, enabling interrupts.\n", ha->board_name);
+
+#if defined(DEBUG_RESET)
+ do_trace = 1;
+#endif
+
+ ha->in_reset = 1;
+
+ spin_unlock_irq(shost->host_lock);
+ time = jiffies;
+ while ((jiffies - time) < (10 * HZ) && limit++ < 200000)
+ udelay(100L);
+ spin_lock_irq(shost->host_lock);
+
+ printk("%s: reset, interrupts disabled, loops %d.\n", ha->board_name, limit);
+
+ for (i = 0; i < shost->can_queue; i++) {
+
+ if (ha->cp_stat[i] == IN_RESET) {
+ SCpnt = ha->cp[i].SCpnt;
+ unmap_dma(i, ha);
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ ha->cp_stat[i] = LOCKED;
+
+ printk
+ ("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ ha->board_name, i, SCpnt->pid);
+ }
+
+ else if (ha->cp_stat[i] == ABORTING) {
+ SCpnt = ha->cp[i].SCpnt;
+ unmap_dma(i, ha);
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox was never queued to the adapter */
+ ha->cp_stat[i] = FREE;
+
+ printk
+ ("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+ ha->board_name, i, SCpnt->pid);
+ }
+
+ else
+ /* Any other mailbox has already been set free by interrupt */
+ continue;
+
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ ha->in_reset = 0;
+ do_trace = 0;
+
+ if (arg_done)
+ printk("%s: reset, exit, pid %ld done.\n", ha->board_name, SCarg->pid);
+ else
+ printk("%s: reset, exit.\n", ha->board_name);
+
+ return SUCCESS;
+}
+
+int eata2x_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *dkinfo)
+{
+ unsigned int size = capacity;
+
+ if (ext_tran || (scsicam_bios_param(bdev, capacity, dkinfo) < 0)) {
+ dkinfo[0] = 255;
+ dkinfo[1] = 63;
+ dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
+ }
+#if defined (DEBUG_GEOMETRY)
+ printk("%s: bios_param, head=%d, sec=%d, cyl=%d.\n", driver_name,
+ dkinfo[0], dkinfo[1], dkinfo[2]);
+#endif
+
+ return 0;
+}
+
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+ unsigned int rev)
+{
+ unsigned int i, j, k, y;
+ unsigned long x;
+
+ for (i = 0; i < n - 1; i++) {
+ k = i;
+
+ for (j = k + 1; j < n; j++)
+ if (rev) {
+ if (sk[j] > sk[k])
+ k = j;
+ } else {
+ if (sk[j] < sk[k])
+ k = j;
+ }
+
+ if (k != i) {
+ x = sk[k];
+ sk[k] = sk[i];
+ sk[i] = x;
+ y = da[k];
+ da[k] = da[i];
+ da[i] = y;
+ }
+ }
+
+ return;
+}
+
+static int reorder(struct hostdata *ha, unsigned long cursec,
+ unsigned int ihdlr, unsigned int il[], unsigned int n_ready)
+{
+ struct scsi_cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n;
+ unsigned int rev = 0, s = 1, r = 1;
+ unsigned int input_only = 1, overlap = 0;
+ unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+ unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
+ unsigned long ioseek = 0;
+
+ static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+ static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+ static unsigned int readysorted = 0, revcount = 0;
+ static unsigned long seeksorted = 0, seeknosort = 0;
+
+ if (link_statistics && !(++flushcount % link_statistics))
+ printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"
+ " av %ldK as %ldK.\n", flushcount, batchcount,
+ inputcount, ovlcount, readycount, readysorted, sortcount,
+ revcount, seeknosort / (readycount + 1),
+ seeksorted / (readycount + 1));
+
+ if (n_ready <= 1)
+ return 0;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n];
+ cpp = &ha->cp[k];
+ SCpnt = cpp->SCpnt;
+
+ if (!cpp->din)
+ input_only = 0;
+
+ if (SCpnt->request->sector < minsec)
+ minsec = SCpnt->request->sector;
+ if (SCpnt->request->sector > maxsec)
+ maxsec = SCpnt->request->sector;
+
+ sl[n] = SCpnt->request->sector;
+ ioseek += SCpnt->request->nr_sectors;
+
+ if (!n)
+ continue;
+
+ if (sl[n] < sl[n - 1])
+ s = 0;
+ if (sl[n] > sl[n - 1])
+ r = 0;
+
+ if (link_statistics) {
+ if (sl[n] > sl[n - 1])
+ seek += sl[n] - sl[n - 1];
+ else
+ seek += sl[n - 1] - sl[n];
+ }
+
+ }
+
+ if (link_statistics) {
+ if (cursec > sl[0])
+ seek += cursec - sl[0];
+ else
+ seek += sl[0] - cursec;
+ }
+
+ if (cursec > ((maxsec + minsec) / 2))
+ rev = 1;
+
+ if (ioseek > ((maxsec - minsec) / 2))
+ rev = 0;
+
+ if (!((rev && r) || (!rev && s)))
+ sort(sl, il, n_ready, rev);
+
+ if (!input_only)
+ for (n = 0; n < n_ready; n++) {
+ k = il[n];
+ cpp = &ha->cp[k];
+ SCpnt = cpp->SCpnt;
+ ll[n] = SCpnt->request->nr_sectors;
+ pl[n] = SCpnt->pid;
+
+ if (!n)
+ continue;
+
+ if ((sl[n] == sl[n - 1])
+ || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+ || (rev && ((sl[n] + ll[n]) > sl[n - 1])))
+ overlap = 1;
+ }
+
+ if (overlap)
+ sort(pl, il, n_ready, 0);
+
+ if (link_statistics) {
+ if (cursec > sl[0])
+ iseek = cursec - sl[0];
+ else
+ iseek = sl[0] - cursec;
+ batchcount++;
+ readycount += n_ready;
+ seeknosort += seek / 1024;
+ if (input_only)
+ inputcount++;
+ if (overlap) {
+ ovlcount++;
+ seeksorted += iseek / 1024;
+ } else
+ seeksorted += (iseek + maxsec - minsec) / 1024;
+ if (rev && !r) {
+ revcount++;
+ readysorted += n_ready;
+ }
+ if (!rev && !s) {
+ sortcount++;
+ readysorted += n_ready;
+ }
+ }
+#if defined(DEBUG_LINKED_COMMANDS)
+ if (link_statistics && (overlap || !(flushcount % link_statistics)))
+ for (n = 0; n < n_ready; n++) {
+ k = il[n];
+ cpp = &ha->cp[k];
+ SCpnt = cpp->SCpnt;
+ printk
+ ("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"
+ " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+ (ihdlr ? "ihdlr" : "qcomm"),
+ SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid, k, flushcount,
+ n_ready, SCpnt->request->sector,
+ SCpnt->request->nr_sectors, cursec, YESNO(s),
+ YESNO(r), YESNO(rev), YESNO(input_only),
+ YESNO(overlap), cpp->din);
+ }
+#endif
+ return overlap;
+}
+
+static void flush_dev(struct scsi_device *dev, unsigned long cursec,
+ struct hostdata *ha, unsigned int ihdlr)
+{
+ struct scsi_cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
+
+ for (k = 0; k < dev->host->can_queue; k++) {
+
+ if (ha->cp_stat[k] != READY && ha->cp_stat[k] != IN_USE)
+ continue;
+
+ cpp = &ha->cp[k];
+ SCpnt = cpp->SCpnt;
+
+ if (SCpnt->device != dev)
+ continue;
+
+ if (ha->cp_stat[k] == IN_USE)
+ return;
+
+ il[n_ready++] = k;
+ }
+
+ if (reorder(ha, cursec, ihdlr, il, n_ready))
+ n_ready = 1;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n];
+ cpp = &ha->cp[k];
+ SCpnt = cpp->SCpnt;
+
+ if (do_dma(dev->host->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
+ printk
+ ("%s: %s, target %d.%d:%d, pid %ld, mbox %d, adapter"
+ " busy, will abort.\n", ha->board_name,
+ (ihdlr ? "ihdlr" : "qcomm"),
+ SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid, k);
+ ha->cp_stat[k] = ABORTING;
+ continue;
+ }
+
+ ha->cp_stat[k] = IN_USE;
+ }
+}
+
+static irqreturn_t ihdlr(int irq, struct Scsi_Host *shost)
+{
+ struct scsi_cmnd *SCpnt;
+ unsigned int i, k, c, status, tstatus, reg;
+ struct mssp *spp;
+ struct mscp *cpp;
+ struct hostdata *ha = (struct hostdata *)shost->hostdata;
+
+ if (shost->irq != irq)
+ panic("%s: ihdlr, irq %d, shost->irq %d.\n", ha->board_name, irq,
+ shost->irq);
+
+ /* Check if this board need to be serviced */
+ if (!(inb(shost->io_port + REG_AUX_STATUS) & IRQ_ASSERTED))
+ goto none;
+
+ ha->iocount++;
+
+ if (do_trace)
+ printk("%s: ihdlr, enter, irq %d, count %d.\n", ha->board_name, irq,
+ ha->iocount);
+
+ /* Check if this board is still busy */
+ if (wait_on_busy(shost->io_port, 20 * MAXLOOP)) {
+ reg = inb(shost->io_port + REG_STATUS);
+ printk
+ ("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n",
+ ha->board_name, irq, reg, ha->iocount);
+ goto none;
+ }
+
+ spp = &ha->sp;
+
+ /* Make a local copy just before clearing the interrupt indication */
+ memcpy(spp, ha->sp_cpu_addr, sizeof(struct mssp));
+
+ /* Clear the completion flag and cp pointer on the dynamic copy of sp */
+ memset(ha->sp_cpu_addr, 0, sizeof(struct mssp));
+
+ /* Read the status register to clear the interrupt indication */
+ reg = inb(shost->io_port + REG_STATUS);
+
+#if defined (DEBUG_INTERRUPT)
+ {
+ unsigned char *bytesp;
+ int cnt;
+ bytesp = (unsigned char *)spp;
+ if (ha->iocount < 200) {
+ printk("sp[] =");
+ for (cnt = 0; cnt < 15; cnt++)
+ printk(" 0x%x", bytesp[cnt]);
+ printk("\n");
+ }
+ }
+#endif
+
+ /* Reject any sp with supspect data */
+ if (spp->eoc == 0 && ha->iocount > 1)
+ printk
+ ("%s: ihdlr, spp->eoc == 0, irq %d, reg 0x%x, count %d.\n",
+ ha->board_name, irq, reg, ha->iocount);
+ if (spp->cpp_index < 0 || spp->cpp_index >= shost->can_queue)
+ printk
+ ("%s: ihdlr, bad spp->cpp_index %d, irq %d, reg 0x%x, count %d.\n",
+ ha->board_name, spp->cpp_index, irq, reg, ha->iocount);
+ if (spp->eoc == 0 || spp->cpp_index < 0
+ || spp->cpp_index >= shost->can_queue)
+ goto handled;
+
+ /* Find the mailbox to be serviced on this board */
+ i = spp->cpp_index;
+
+ cpp = &(ha->cp[i]);
+
+#if defined(DEBUG_GENERATE_ABORTS)
+ if ((ha->iocount > 500) && ((ha->iocount % 500) < 3))
+ goto handled;
+#endif
+
+ if (ha->cp_stat[i] == IGNORE) {
+ ha->cp_stat[i] = FREE;
+ goto handled;
+ } else if (ha->cp_stat[i] == LOCKED) {
+ ha->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n", ha->board_name, i,
+ ha->iocount);
+ goto handled;
+ } else if (ha->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n", ha->board_name, i,
+ ha->iocount);
+ goto handled;
+ } else if (ha->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", ha->board_name, i);
+ else if (ha->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
+ ha->board_name, i, ha->cp_stat[i]);
+
+ ha->cp_stat[i] = FREE;
+ SCpnt = cpp->SCpnt;
+
+ if (SCpnt == NULL)
+ panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", ha->board_name,
+ i, SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
+ ha->board_name, i, SCpnt->pid,
+ *(unsigned int *)SCpnt->host_scribble);
+
+ sync_dma(i, ha);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type))
+ flush_dev(SCpnt->device, SCpnt->request->sector, ha, 1);
+
+ tstatus = status_byte(spp->target_status);
+
+#if defined(DEBUG_GENERATE_ERRORS)
+ if ((ha->iocount > 500) && ((ha->iocount % 200) < 2))
+ spp->adapter_status = 0x01;
+#endif
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+ && ha->target_redo[SCpnt->device->id][SCpnt->
+ device->
+ channel])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ ha->target_redo[SCpnt->device->id][SCpnt->device->
+ channel] = 0;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK &&
+ (!(tstatus == CHECK_CONDITION && ha->iocount <= 1000 &&
+ (SCpnt->sense_buffer[2] & 0xf) == NOT_READY)))
+ printk("%s: ihdlr, target %d.%d:%d, pid %ld, "
+ "target_status 0x%x, sense key 0x%x.\n",
+ ha->board_name,
+ SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
+ ha->target_to[SCpnt->device->id][SCpnt->device->channel] = 0;
+
+ if (ha->last_retried_pid == SCpnt->pid)
+ ha->retries = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+ case 0x02: /* Command Time Out */
+
+ if (ha->target_to[SCpnt->device->id][SCpnt->device->channel] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ ha->target_to[SCpnt->device->id][SCpnt->device->
+ channel]++;
+ }
+
+ break;
+
+ /* Perform a limited number of internal retries */
+ case 0x03: /* SCSI Bus Reset Received */
+ case 0x04: /* Initial Controller Power-up */
+
+ for (c = 0; c <= shost->max_channel; c++)
+ for (k = 0; k < shost->max_id; k++)
+ ha->target_redo[k][c] = 1;
+
+ if (SCpnt->device->type != TYPE_TAPE
+ && ha->retries < MAX_INTERNAL_RETRIES) {
+
+#if defined(DID_SOFT_ERROR)
+ status = DID_SOFT_ERROR << 16;
+#else
+ status = DID_BUS_BUSY << 16;
+#endif
+
+ ha->retries++;
+ ha->last_retried_pid = SCpnt->pid;
+ } else
+ status = DID_ERROR << 16;
+
+ break;
+ case 0x05: /* Unexpected Bus Phase */
+ case 0x06: /* Unexpected Bus Free */
+ case 0x07: /* Bus Parity Error */
+ case 0x08: /* SCSI Hung */
+ case 0x09: /* Unexpected Message Reject */
+ case 0x0a: /* SCSI Bus Reset Stuck */
+ case 0x0b: /* Auto Request-Sense Failed */
+ case 0x0c: /* Controller Ram Parity Error */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+
+#if defined(DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && ha->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && ha->iocount <= 1000) ||
+ do_trace || msg_byte(spp->target_status))
+#endif
+ printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"
+ " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+ ha->board_name, i, spp->adapter_status, spp->target_status,
+ SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid, reg, ha->iocount);
+
+ unmap_dma(i, ha);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ SCpnt->scsi_done(SCpnt);
+
+ if (do_trace)
+ printk("%s: ihdlr, exit, irq %d, count %d.\n", ha->board_name,
+ irq, ha->iocount);
+
+ handled:
+ return IRQ_HANDLED;
+ none:
+ return IRQ_NONE;
+}
+
+static irqreturn_t do_interrupt_handler(int irq, void *shap,
+ struct pt_regs *regs)
+{
+ struct Scsi_Host *shost;
+ unsigned int j;
+ unsigned long spin_flags;
+ irqreturn_t ret;
+
+ /* Check if the interrupt must be processed by this handler */
+ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards)
+ return IRQ_NONE;
+ shost = sh[j];
+
+ spin_lock_irqsave(shost->host_lock, spin_flags);
+ ret = ihdlr(irq, shost);
+ spin_unlock_irqrestore(shost->host_lock, spin_flags);
+ return ret;
+}
+
+static int eata2x_release(struct Scsi_Host *shost)
+{
+ struct hostdata *ha = (struct hostdata *)shost->hostdata;
+ unsigned int i;
+
+ for (i = 0; i < shost->can_queue; i++)
+ if ((&ha->cp[i])->sglist)
+ kfree((&ha->cp[i])->sglist);
+
+ for (i = 0; i < shost->can_queue; i++)
+ pci_unmap_single(ha->pdev, ha->cp[i].cp_dma_addr,
+ sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL);
+
+ if (ha->sp_cpu_addr)
+ pci_free_consistent(ha->pdev, sizeof(struct mssp),
+ ha->sp_cpu_addr, ha->sp_dma_addr);
+
+ free_irq(shost->irq, &sha[ha->board_number]);
+
+ if (shost->dma_channel != NO_DMA)
+ free_dma(shost->dma_channel);
+
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+#include "scsi_module.c"
+
+#ifndef MODULE
+__setup("eata=", option_setup);
+#endif /* end MODULE */
diff --git a/drivers/scsi/eata_generic.h b/drivers/scsi/eata_generic.h
new file mode 100644
index 000000000000..34bce2c9e92e
--- /dev/null
+++ b/drivers/scsi/eata_generic.h
@@ -0,0 +1,406 @@
+/********************************************************
+* Header file for eata_dma.c and eata_pio.c *
+* Linux EATA SCSI drivers *
+* (c) 1993-96 Michael Neuffer *
+* mike@i-Connect.Net *
+* neuffer@mail.uni-mainz.de *
+*********************************************************
+* last change: 96/08/14 *
+********************************************************/
+
+
+#ifndef _EATA_GENERIC_H
+#define _EATA_GENERIC_H
+
+
+
+/*********************************************
+ * Misc. definitions *
+ *********************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define R_LIMIT 0x20000
+
+#define MAXISA 4
+#define MAXEISA 16
+#define MAXPCI 16
+#define MAXIRQ 16
+#define MAXTARGET 16
+#define MAXCHANNEL 3
+
+#define IS_ISA 'I'
+#define IS_EISA 'E'
+#define IS_PCI 'P'
+
+#define BROKEN_INQUIRY 1
+
+#define BUSMASTER 0xff
+#define PIO 0xfe
+
+#define EATA_SIGNATURE 0x45415441 /* BIG ENDIAN coded "EATA" sig. */
+
+#define DPT_ID1 0x12
+#define DPT_ID2 0x14
+
+#define ATT_ID1 0x06
+#define ATT_ID2 0x94
+#define ATT_ID3 0x0
+
+#define NEC_ID1 0x38
+#define NEC_ID2 0xa3
+#define NEC_ID3 0x82
+
+
+#define EATA_CP_SIZE 44
+
+#define MAX_PCI_DEVICES 32 /* Maximum # Of Devices Per Bus */
+#define MAX_METHOD_2 16 /* Max Devices For Method 2 */
+#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
+
+#define SG_SIZE 64
+#define SG_SIZE_BIG 252 /* max. 8096 elements, 64k */
+
+#define UPPER_DEVICE_QUEUE_LIMIT 64 /* The limit we have to set for the
+ * device queue to keep the broken
+ * midlevel SCSI code from producing
+ * bogus timeouts
+ */
+
+#define TYPE_DISK_QUEUE 16
+#define TYPE_TAPE_QUEUE 4
+#define TYPE_ROM_QUEUE 4
+#define TYPE_OTHER_QUEUE 2
+
+#define FREE 0
+#define OK 0
+#define NO_TIMEOUT 0
+#define USED 1
+#define TIMEOUT 2
+#define RESET 4
+#define LOCKED 8
+#define ABORTED 16
+
+#define READ 0
+#define WRITE 1
+#define OTHER 2
+
+#define HD(cmd) ((hostdata *)&(cmd->device->host->hostdata))
+#define CD(cmd) ((struct eata_ccb *)(cmd->host_scribble))
+#define SD(host) ((hostdata *)&(host->hostdata))
+
+/***********************************************
+ * EATA Command & Register definitions *
+ ***********************************************/
+#define PCI_REG_DPTconfig 0x40
+#define PCI_REG_PumpModeAddress 0x44
+#define PCI_REG_PumpModeData 0x48
+#define PCI_REG_ConfigParam1 0x50
+#define PCI_REG_ConfigParam2 0x54
+
+
+#define EATA_CMD_PIO_SETUPTEST 0xc6
+#define EATA_CMD_PIO_READ_CONFIG 0xf0
+#define EATA_CMD_PIO_SET_CONFIG 0xf1
+#define EATA_CMD_PIO_SEND_CP 0xf2
+#define EATA_CMD_PIO_RECEIVE_SP 0xf3
+#define EATA_CMD_PIO_TRUNC 0xf4
+
+#define EATA_CMD_RESET 0xf9
+#define EATA_CMD_IMMEDIATE 0xfa
+
+#define EATA_CMD_DMA_READ_CONFIG 0xfd
+#define EATA_CMD_DMA_SET_CONFIG 0xfe
+#define EATA_CMD_DMA_SEND_CP 0xff
+
+#define ECS_EMULATE_SENSE 0xd4
+
+#define EATA_GENERIC_ABORT 0x00
+#define EATA_SPECIFIC_RESET 0x01
+#define EATA_BUS_RESET 0x02
+#define EATA_SPECIFIC_ABORT 0x03
+#define EATA_QUIET_INTR 0x04
+#define EATA_COLD_BOOT_HBA 0x06 /* Only as a last resort */
+#define EATA_FORCE_IO 0x07
+
+#define HA_CTRLREG 0x206 /* control register for HBA */
+#define HA_CTRL_DISINT 0x02 /* CTRLREG: disable interrupts */
+#define HA_CTRL_RESCPU 0x04 /* CTRLREG: reset processor */
+#define HA_CTRL_8HEADS 0x08 /* CTRLREG: set for drives with*
+ * >=8 heads (WD1003 rudimentary :-) */
+
+#define HA_WCOMMAND 0x07 /* command register offset */
+#define HA_WIFC 0x06 /* immediate command offset */
+#define HA_WCODE 0x05
+#define HA_WCODE2 0x04
+#define HA_WDMAADDR 0x02 /* DMA address LSB offset */
+#define HA_RAUXSTAT 0x08 /* aux status register offset*/
+#define HA_RSTATUS 0x07 /* status register offset */
+#define HA_RDATA 0x00 /* data register (16bit) */
+#define HA_WDATA 0x00 /* data register (16bit) */
+
+#define HA_ABUSY 0x01 /* aux busy bit */
+#define HA_AIRQ 0x02 /* aux IRQ pending bit */
+#define HA_SERROR 0x01 /* pr. command ended in error*/
+#define HA_SMORE 0x02 /* more data soon to come */
+#define HA_SCORR 0x04 /* data corrected */
+#define HA_SDRQ 0x08 /* data request active */
+#define HA_SSC 0x10 /* seek complete */
+#define HA_SFAULT 0x20 /* write fault */
+#define HA_SREADY 0x40 /* drive ready */
+#define HA_SBUSY 0x80 /* drive busy */
+#define HA_SDRDY HA_SSC+HA_SREADY+HA_SDRQ
+
+/**********************************************
+ * Message definitions *
+ **********************************************/
+
+#define HA_NO_ERROR 0x00 /* No Error */
+#define HA_ERR_SEL_TO 0x01 /* Selection Timeout */
+#define HA_ERR_CMD_TO 0x02 /* Command Timeout */
+#define HA_BUS_RESET 0x03 /* SCSI Bus Reset Received */
+#define HA_INIT_POWERUP 0x04 /* Initial Controller Power-up */
+#define HA_UNX_BUSPHASE 0x05 /* Unexpected Bus Phase */
+#define HA_UNX_BUS_FREE 0x06 /* Unexpected Bus Free */
+#define HA_BUS_PARITY 0x07 /* Bus Parity Error */
+#define HA_SCSI_HUNG 0x08 /* SCSI Hung */
+#define HA_UNX_MSGRJCT 0x09 /* Unexpected Message Rejected */
+#define HA_RESET_STUCK 0x0a /* SCSI Bus Reset Stuck */
+#define HA_RSENSE_FAIL 0x0b /* Auto Request-Sense Failed */
+#define HA_PARITY_ERR 0x0c /* Controller Ram Parity Error */
+#define HA_CP_ABORT_NA 0x0d /* Abort Message sent to non-active cmd */
+#define HA_CP_ABORTED 0x0e /* Abort Message sent to active cmd */
+#define HA_CP_RESET_NA 0x0f /* Reset Message sent to non-active cmd */
+#define HA_CP_RESET 0x10 /* Reset Message sent to active cmd */
+#define HA_ECC_ERR 0x11 /* Controller Ram ECC Error */
+#define HA_PCI_PARITY 0x12 /* PCI Parity Error */
+#define HA_PCI_MABORT 0x13 /* PCI Master Abort */
+#define HA_PCI_TABORT 0x14 /* PCI Target Abort */
+#define HA_PCI_STABORT 0x15 /* PCI Signaled Target Abort */
+
+/**********************************************
+ * Other definitions *
+ **********************************************/
+
+struct reg_bit { /* reading this one will clear the interrupt */
+ __u8 error:1; /* previous command ended in an error */
+ __u8 more:1; /* more DATA coming soon, poll BSY & DRQ (PIO) */
+ __u8 corr:1; /* data read was successfully corrected with ECC*/
+ __u8 drq:1; /* data request active */
+ __u8 sc:1; /* seek complete */
+ __u8 fault:1; /* write fault */
+ __u8 ready:1; /* drive ready */
+ __u8 busy:1; /* controller busy */
+};
+
+struct reg_abit { /* reading this won't clear the interrupt */
+ __u8 abusy:1; /* auxiliary busy */
+ __u8 irq:1; /* set when drive interrupt is asserted */
+ __u8 dummy:6;
+};
+
+struct eata_register { /* EATA register set */
+ __u8 data_reg[2]; /* R, couldn't figure this one out */
+ __u8 cp_addr[4]; /* W, CP address register */
+ union {
+ __u8 command; /* W, command code: [read|set] conf, send CP*/
+ struct reg_bit status; /* R, see register_bit1 */
+ __u8 statusbyte;
+ } ovr;
+ struct reg_abit aux_stat; /* R, see register_bit2 */
+};
+
+struct get_conf { /* Read Configuration Array */
+ __u32 len; /* Should return 0x22, 0x24, etc */
+ __u32 signature; /* Signature MUST be "EATA" */
+ __u8 version2:4,
+ version:4; /* EATA Version level */
+ __u8 OCS_enabled:1, /* Overlap Command Support enabled */
+ TAR_support:1, /* SCSI Target Mode supported */
+ TRNXFR:1, /* Truncate Transfer Cmd not necessary *
+ * Only used in PIO Mode */
+ MORE_support:1, /* MORE supported (only PIO Mode) */
+ DMA_support:1, /* DMA supported Driver uses only *
+ * this mode */
+ DMA_valid:1, /* DRQ value in Byte 30 is valid */
+ ATA:1, /* ATA device connected (not supported) */
+ HAA_valid:1; /* Hostadapter Address is valid */
+
+ __u16 cppadlen; /* Number of pad bytes send after CD data *
+ * set to zero for DMA commands */
+ __u8 scsi_id[4]; /* SCSI ID of controller 2-0 Byte 0 res. *
+ * if not, zero is returned */
+ __u32 cplen; /* CP length: number of valid cp bytes */
+ __u32 splen; /* Number of bytes returned after *
+ * Receive SP command */
+ __u16 queuesiz; /* max number of queueable CPs */
+ __u16 dummy;
+ __u16 SGsiz; /* max number of SG table entries */
+ __u8 IRQ:4, /* IRQ used this HA */
+ IRQ_TR:1, /* IRQ Trigger: 0=edge, 1=level */
+ SECOND:1, /* This is a secondary controller */
+ DMA_channel:2; /* DRQ index, DRQ is 2comp of DRQX */
+ __u8 sync; /* device at ID 7 tru 0 is running in *
+ * synchronous mode, this will disappear */
+ __u8 DSBLE:1, /* ISA i/o addressing is disabled */
+ FORCADR:1, /* i/o address has been forced */
+ SG_64K:1,
+ SG_UAE:1,
+ :4;
+ __u8 MAX_ID:5, /* Max number of SCSI target IDs */
+ MAX_CHAN:3; /* Number of SCSI busses on HBA */
+ __u8 MAX_LUN; /* Max number of LUNs */
+ __u8 :3,
+ AUTOTRM:1,
+ M1_inst:1,
+ ID_qest:1, /* Raidnum ID is questionable */
+ is_PCI:1, /* HBA is PCI */
+ is_EISA:1; /* HBA is EISA */
+ __u8 RAIDNUM; /* unique HBA identifier */
+ __u8 unused[474];
+};
+
+struct eata_sg_list
+{
+ __u32 data;
+ __u32 len;
+};
+
+struct eata_ccb { /* Send Command Packet structure */
+
+ __u8 SCSI_Reset:1, /* Cause a SCSI Bus reset on the cmd */
+ HBA_Init:1, /* Cause Controller to reinitialize */
+ Auto_Req_Sen:1, /* Do Auto Request Sense on errors */
+ scatter:1, /* Data Ptr points to a SG Packet */
+ Resrvd:1, /* RFU */
+ Interpret:1, /* Interpret the SCSI cdb of own use */
+ DataOut:1, /* Data Out phase with command */
+ DataIn:1; /* Data In phase with command */
+ __u8 reqlen; /* Request Sense Length *
+ * Valid if Auto_Req_Sen=1 */
+ __u8 unused[3];
+ __u8 FWNEST:1, /* send cmd to phys RAID component */
+ unused2:7;
+ __u8 Phsunit:1, /* physical unit on mirrored pair */
+ I_AT:1, /* inhibit address translation */
+ I_HBA_C:1, /* HBA inhibit caching */
+ unused3:5;
+
+ __u8 cp_id:5, /* SCSI Device ID of target */
+ cp_channel:3; /* SCSI Channel # of HBA */
+ __u8 cp_lun:3,
+ :2,
+ cp_luntar:1, /* CP is for target ROUTINE */
+ cp_dispri:1, /* Grant disconnect privilege */
+ cp_identify:1; /* Always TRUE */
+ __u8 cp_msg1; /* Message bytes 0-3 */
+ __u8 cp_msg2;
+ __u8 cp_msg3;
+ __u8 cp_cdb[12]; /* Command Descriptor Block */
+ __u32 cp_datalen; /* Data Transfer Length *
+ * If scatter=1 len of sg package */
+ void *cp_viraddr; /* address of this ccb */
+ __u32 cp_dataDMA; /* Data Address, if scatter=1 *
+ * address of scatter packet */
+ __u32 cp_statDMA; /* address for Status Packet */
+ __u32 cp_reqDMA; /* Request Sense Address, used if *
+ * CP command ends with error */
+ /* Additional CP info begins here */
+ __u32 timestamp; /* Needed to measure command latency */
+ __u32 timeout;
+ __u8 sizeindex;
+ __u8 rw_latency;
+ __u8 retries;
+ __u8 status; /* status of this queueslot */
+ struct scsi_cmnd *cmd; /* address of cmd */
+ struct eata_sg_list *sg_list;
+};
+
+
+struct eata_sp {
+ __u8 hba_stat:7, /* HBA status */
+ EOC:1; /* True if command finished */
+ __u8 scsi_stat; /* Target SCSI status */
+ __u8 reserved[2];
+ __u32 residue_len; /* Number of bytes not transferred */
+ struct eata_ccb *ccb; /* Address set in COMMAND PACKET */
+ __u8 msg[12];
+};
+
+typedef struct hstd {
+ __u8 vendor[9];
+ __u8 name[18];
+ __u8 revision[6];
+ __u8 EATA_revision;
+ __u32 firmware_revision;
+ __u8 HBA_number;
+ __u8 bustype; /* bustype of HBA */
+ __u8 channel; /* # of avail. scsi channels */
+ __u8 state; /* state of HBA */
+ __u8 primary; /* true if primary */
+ __u8 more_support:1, /* HBA supports MORE flag */
+ immediate_support:1, /* HBA supports IMMEDIATE CMDs*/
+ broken_INQUIRY:1; /* This is an EISA HBA with *
+ * broken INQUIRY */
+ __u8 do_latency; /* Latency measurement flag */
+ __u32 reads[13];
+ __u32 writes[13];
+ __u32 reads_lat[12][4];
+ __u32 writes_lat[12][4];
+ __u32 all_lat[4];
+ __u8 resetlevel[MAXCHANNEL];
+ __u32 last_ccb; /* Last used ccb */
+ __u32 cplen; /* size of CP in words */
+ __u16 cppadlen; /* pad length of cp in words */
+ __u16 queuesize;
+ __u16 sgsize; /* # of entries in the SG list*/
+ __u16 devflags; /* bits set for detected devices */
+ __u8 hostid; /* SCSI ID of HBA */
+ __u8 moresupport; /* HBA supports MORE flag */
+ struct Scsi_Host *next;
+ struct Scsi_Host *prev;
+ struct eata_sp sp; /* status packet */
+ struct eata_ccb ccb[0]; /* ccb array begins here */
+}hostdata;
+
+/* structure for max. 2 emulated drives */
+struct drive_geom_emul {
+ __u8 trans; /* translation flag 1=transl */
+ __u8 channel; /* SCSI channel number */
+ __u8 HBA; /* HBA number (prim/sec) */
+ __u8 id; /* drive id */
+ __u8 lun; /* drive lun */
+ __u32 heads; /* number of heads */
+ __u32 sectors; /* number of sectors */
+ __u32 cylinder; /* number of cylinders */
+};
+
+struct geom_emul {
+ __u8 bios_drives; /* number of emulated drives */
+ struct drive_geom_emul drv[2]; /* drive structures */
+};
+
+#endif /* _EATA_GENERIC_H */
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
new file mode 100644
index 000000000000..0ee49dc50b85
--- /dev/null
+++ b/drivers/scsi/eata_pio.c
@@ -0,0 +1,996 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI PIO driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all EATA-PIO boards *
+ * -only supports DASD devices *
+ * *
+ * (c)1993-96 Michael Neuffer, Alfred Arnold *
+ * neuffer@goofy.zdv.uni-mainz.de *
+ * a.arnold@kfa-juelich.de *
+ * *
+ * Updated 2002 by Alan Cox <alan@redhat.com> for Linux *
+ * 2.5.x and the newer locking and error handling *
+ * *
+ * This program is free software; you can redistribute it *
+ * and/or modify it under the terms of the GNU General *
+ * Public License as published by the Free Software *
+ * Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be *
+ * useful, but WITHOUT ANY WARRANTY; without even the *
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A *
+ * PARTICULAR PURPOSE. See the GNU General Public License *
+ * for more details. *
+ * *
+ * You should have received a copy of the GNU General *
+ * Public License along with this kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ * For the avoidance of doubt the "preferred form" of this *
+ * code is one which is in an open non patent encumbered *
+ * format. Where cryptographic key signing forms part of *
+ * the process of creating an executable the information *
+ * including keys needed to generate an equivalently *
+ * functional executable are deemed to be part of the *
+ * source code are deemed to be part of the source code. *
+ * *
+ ************************************************************
+ * last change: 2002/11/02 OS: Linux 2.5.45 *
+ ************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "eata_generic.h"
+#include "eata_pio.h"
+
+
+static uint ISAbases[MAXISA] = {
+ 0x1F0, 0x170, 0x330, 0x230
+};
+
+static uint ISAirqs[MAXISA] = {
+ 14, 12, 15, 11
+};
+
+static unsigned char EISAbases[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+static uint registered_HBAs;
+static struct Scsi_Host *last_HBA;
+static struct Scsi_Host *first_HBA;
+static unsigned char reg_IRQ[16];
+static unsigned char reg_IRQL[16];
+static unsigned long int_counter;
+static unsigned long queue_counter;
+
+static struct scsi_host_template driver_template;
+
+/*
+ * eata_proc_info
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+static int eata_pio_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset,
+ int length, int rw)
+{
+ static u8 buff[512];
+ int size, len = 0;
+ off_t begin = 0, pos = 0;
+
+ if (rw)
+ return -ENOSYS;
+ if (offset == 0)
+ memset(buff, 0, sizeof(buff));
+
+ size = sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: "
+ "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
+ len += size; pos = begin + len;
+ size = sprintf(buffer + len, "queued commands: %10ld\n"
+ "processed interrupts:%10ld\n", queue_counter, int_counter);
+ len += size; pos = begin + len;
+
+ size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
+ shost->host_no, SD(shost)->name);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Firmware revision: v%s\n",
+ SD(shost)->revision);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "IO: PIO\n");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) shost->base);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Host Bus: %s\n",
+ (SD(shost)->bustype == 'P')?"PCI ":
+ (SD(shost)->bustype == 'E')?"EISA":"ISA ");
+
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ stop_output:
+ DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
+
+ return (len);
+}
+
+static int eata_pio_release(struct Scsi_Host *sh)
+{
+ if (sh->irq && reg_IRQ[sh->irq] == 1)
+ free_irq(sh->irq, NULL);
+ else
+ reg_IRQ[sh->irq]--;
+ if (SD(sh)->channel == 0) {
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return 1;
+}
+
+static void IncStat(struct scsi_pointer *SCp, uint Increment)
+{
+ SCp->ptr += Increment;
+ if ((SCp->this_residual -= Increment) == 0) {
+ if ((--SCp->buffers_residual) == 0)
+ SCp->Status = 0;
+ else {
+ SCp->buffer++;
+ SCp->ptr = page_address(SCp->buffer->page) + SCp->buffer->offset;
+ SCp->this_residual = SCp->buffer->length;
+ }
+ }
+}
+
+static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs);
+
+static irqreturn_t do_eata_pio_int_handler(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ eata_pio_int_handler(irq, dev_id, regs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ uint eata_stat = 0xfffff;
+ struct scsi_cmnd *cmd;
+ hostdata *hd;
+ struct eata_ccb *cp;
+ uint base;
+ uint x, z;
+ struct Scsi_Host *sh;
+ unsigned short zwickel = 0;
+ unsigned char stat, odd;
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev)
+ {
+ if (sh->irq != irq)
+ continue;
+ if (inb((uint) sh->base + HA_RSTATUS) & HA_SBUSY)
+ continue;
+
+ int_counter++;
+
+ hd = SD(sh);
+
+ cp = &hd->ccb[0];
+ cmd = cp->cmd;
+ base = (uint) cmd->device->host->base;
+
+ do {
+ stat = inb(base + HA_RSTATUS);
+ if (stat & HA_SDRQ) {
+ if (cp->DataIn) {
+ z = 256;
+ odd = 0;
+ while ((cmd->SCp.Status) && ((z > 0) || (odd))) {
+ if (odd) {
+ *(cmd->SCp.ptr) = zwickel >> 8;
+ IncStat(&cmd->SCp, 1);
+ odd = 0;
+ }
+ x = min_t(unsigned int, z, cmd->SCp.this_residual / 2);
+ insw(base + HA_RDATA, cmd->SCp.ptr, x);
+ z -= x;
+ IncStat(&cmd->SCp, 2 * x);
+ if ((z > 0) && (cmd->SCp.this_residual == 1)) {
+ zwickel = inw(base + HA_RDATA);
+ *(cmd->SCp.ptr) = zwickel & 0xff;
+ IncStat(&cmd->SCp, 1);
+ z--;
+ odd = 1;
+ }
+ }
+ while (z > 0) {
+ zwickel = inw(base + HA_RDATA);
+ z--;
+ }
+ } else { /* cp->DataOut */
+
+ odd = 0;
+ z = 256;
+ while ((cmd->SCp.Status) && ((z > 0) || (odd))) {
+ if (odd) {
+ zwickel += *(cmd->SCp.ptr) << 8;
+ IncStat(&cmd->SCp, 1);
+ outw(zwickel, base + HA_RDATA);
+ z--;
+ odd = 0;
+ }
+ x = min_t(unsigned int, z, cmd->SCp.this_residual / 2);
+ outsw(base + HA_RDATA, cmd->SCp.ptr, x);
+ z -= x;
+ IncStat(&cmd->SCp, 2 * x);
+ if ((z > 0) && (cmd->SCp.this_residual == 1)) {
+ zwickel = *(cmd->SCp.ptr);
+ zwickel &= 0xff;
+ IncStat(&cmd->SCp, 1);
+ odd = 1;
+ }
+ }
+ while (z > 0 || odd) {
+ outw(zwickel, base + HA_RDATA);
+ z--;
+ odd = 0;
+ }
+ }
+ }
+ }
+ while ((stat & HA_SDRQ) || ((stat & HA_SMORE) && hd->moresupport));
+
+ /* terminate handler if HBA goes busy again, i.e. transfers
+ * more data */
+
+ if (stat & HA_SBUSY)
+ break;
+
+ /* OK, this is quite stupid, but I haven't found any correct
+ * way to get HBA&SCSI status so far */
+
+ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) {
+ cmd->result = (DID_OK << 16);
+ hd->devflags |= (1 << cp->cp_id);
+ } else if (hd->devflags & 1 << cp->cp_id)
+ cmd->result = (DID_OK << 16) + 0x02;
+ else
+ cmd->result = (DID_NO_CONNECT << 16);
+
+ if (cp->status == LOCKED) {
+ cp->status = FREE;
+ eata_stat = inb(base + HA_RSTATUS);
+ printk(KERN_CRIT "eata_pio: int_handler, freeing locked " "queueslot\n");
+ return;
+ }
+#if DBG_INTR2
+ if (stat != 0x50)
+ printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, cmd->result);
+#endif
+
+ cp->status = FREE; /* now we can release the slot */
+
+ cmd->scsi_done(cmd);
+ }
+
+ return;
+}
+
+static inline uint eata_pio_send_command(uint base, unsigned char command)
+{
+ uint loop = HZ / 2;
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return 1;
+
+ /* Enable interrupts for HBA. It is not the best way to do it at this
+ * place, but I hope that it doesn't interfere with the IDE driver
+ * initialization this way */
+
+ outb(HA_CTRL_8HEADS, base + HA_CTRLREG);
+
+ outb(command, base + HA_WCOMMAND);
+ return 0;
+}
+
+static int eata_pio_queue(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ uint x, y;
+ uint base;
+
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *cp;
+
+ queue_counter++;
+
+ hd = HD(cmd);
+ sh = cmd->device->host;
+ base = (uint) sh->base;
+
+ /* use only slot 0, as 2001 can handle only one cmd at a time */
+
+ y = x = 0;
+
+ if (hd->ccb[y].status != FREE) {
+
+ DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n", sh->can_queue, x, y));
+#if DEBUG_EATA
+ panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld " "intrno: %ld\n", queue_counter, int_counter);
+#else
+ panic(KERN_EMERG "eata_pio: run out of queue slots....\n");
+#endif
+ }
+
+ cp = &hd->ccb[y];
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ cp->status = USED; /* claim free slot */
+
+ DBG(DBG_QUEUE, printk(KERN_DEBUG "eata_pio_queue pid %ld, target: %x, lun:" " %x, y %d\n", cmd->pid, cmd->device->id, cmd->device->lun, y));
+
+ cmd->scsi_done = (void *) done;
+
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ cp->DataOut = 1; /* Output mode */
+ else
+ cp->DataIn = 0; /* Input mode */
+
+ cp->Interpret = (cmd->device->id == hd->hostid);
+ cp->cp_datalen = htonl((unsigned long) cmd->request_bufflen);
+ cp->Auto_Req_Sen = 0;
+ cp->cp_reqDMA = htonl(0);
+ cp->reqlen = 0;
+
+ cp->cp_id = cmd->device->id;
+ cp->cp_lun = cmd->device->lun;
+ cp->cp_dispri = 0;
+ cp->cp_identify = 1;
+ memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
+
+ cp->cp_statDMA = htonl(0);
+
+ cp->cp_viraddr = cp;
+ cp->cmd = cmd;
+ cmd->host_scribble = (char *) &hd->ccb[y];
+
+ if (cmd->use_sg == 0) {
+ cmd->SCp.buffers_residual = 1;
+ cmd->SCp.ptr = cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.buffer = NULL;
+ } else {
+ cmd->SCp.buffer = cmd->request_buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ }
+ cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes
+ * are to transfer */
+
+ if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) {
+ cmd->result = DID_BUS_BUSY << 16;
+ printk(KERN_NOTICE "eata_pio_queue target %d, pid %ld, HBA busy, " "returning DID_BUS_BUSY, done.\n", cmd->device->id, cmd->pid);
+ done(cmd);
+ cp->status = FREE;
+ return (0);
+ }
+ /* FIXME: timeout */
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ cpu_relax();
+ outsw(base + HA_RDATA, cp, hd->cplen);
+ outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
+ for (x = 0; x < hd->cppadlen; x++)
+ outw(0, base + HA_RDATA);
+
+ DBG(DBG_QUEUE, printk(KERN_DEBUG "Queued base %#.4lx pid: %ld target: %x " "lun: %x slot %d irq %d\n", (long) sh->base, cmd->pid, cmd->device->id, cmd->device->lun, y, sh->irq));
+
+ return (0);
+}
+
+static int eata_pio_abort(struct scsi_cmnd *cmd)
+{
+ uint loop = HZ;
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_abort called pid: %ld " "target: %x lun: %x reason %x\n", cmd->pid, cmd->device->id, cmd->device->lun, cmd->abort_reason));
+
+
+ while (inb(cmd->device->host->base + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk(KERN_WARNING "eata_pio: abort, timeout error.\n");
+ return FAILED;
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ return FAILED;
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n"));
+ /* We want to sleep a bit more here */
+ return FAILED; /* SNOOZE */
+ }
+ if (CD(cmd)->status == RESET) {
+ printk(KERN_WARNING "eata_pio: abort, command reset error.\n");
+ return FAILED;
+ }
+ if (CD(cmd)->status == LOCKED) {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot " "locked.\n"));
+ return FAILED;
+ }
+ panic("eata_pio: abort: invalid slot status\n");
+}
+
+static int eata_pio_host_reset(struct scsi_cmnd *cmd)
+{
+ uint x, limit = 0;
+ unsigned char success = 0;
+ struct scsi_cmnd *sp;
+ struct Scsi_Host *host = cmd->device->host;
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset called pid:%ld target:" " %x lun: %x reason %x\n", cmd->pid, cmd->device->id, cmd->device->lun, cmd->abort_reason));
+
+ if (HD(cmd)->state == RESET) {
+ printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n");
+ return FAILED;
+ }
+
+ /* force all slots to be free */
+
+ for (x = 0; x < cmd->device->host->can_queue; x++) {
+
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+ printk(KERN_WARNING "eata_pio_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+
+ if (sp == NULL)
+ panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
+ }
+
+ /* hard reset the HBA */
+ outb(EATA_CMD_RESET, (uint) cmd->device->host->base + HA_WCOMMAND);
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n"));
+ HD(cmd)->state = RESET;
+
+ spin_unlock_irq(host->host_lock);
+ msleep(3000);
+ spin_lock_irq(host->host_lock);
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, " "loops %d.\n", limit));
+
+ for (x = 0; x < cmd->device->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is terminated */
+ printk(KERN_WARNING "eata_pio_reset: reset ccb %d.\n", x);
+ HD(cmd)->ccb[x].status = FREE;
+
+ sp->scsi_done(sp);
+ }
+
+ HD(cmd)->state = 0;
+
+ if (success) { /* hmmm... */
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n"));
+ return SUCCESS;
+ } else {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n"));
+ return FAILED;
+ }
+}
+
+static char *get_pio_board_data(unsigned long base, uint irq, uint id, unsigned long cplen, unsigned short cppadlen)
+{
+ struct eata_ccb cp;
+ static char buff[256];
+ int z;
+
+ memset(&cp, 0, sizeof(struct eata_ccb));
+ memset(buff, 0, sizeof(buff));
+
+ cp.DataIn = 1;
+ cp.Interpret = 1; /* Interpret command */
+
+ cp.cp_datalen = htonl(254);
+ cp.cp_dataDMA = htonl(0);
+
+ cp.cp_id = id;
+ cp.cp_lun = 0;
+
+ cp.cp_cdb[0] = INQUIRY;
+ cp.cp_cdb[1] = 0;
+ cp.cp_cdb[2] = 0;
+ cp.cp_cdb[3] = 0;
+ cp.cp_cdb[4] = 254;
+ cp.cp_cdb[5] = 0;
+
+ if (eata_pio_send_command((uint) base, EATA_CMD_PIO_SEND_CP))
+ return (NULL);
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ));
+ outsw(base + HA_RDATA, &cp, cplen);
+ outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
+ for (z = 0; z < cppadlen; z++)
+ outw(0, base + HA_RDATA);
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY);
+ if (inb(base + HA_RSTATUS) & HA_SERROR)
+ return (NULL);
+ else if (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ return (NULL);
+ else {
+ insw(base + HA_RDATA, &buff, 127);
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+ return (buff);
+ }
+}
+
+static int get_pio_conf_PIO(u32 base, struct get_conf *buf)
+{
+ unsigned long loop = HZ / 2;
+ int z;
+ unsigned short *p;
+
+ if (!request_region(base, 9, "eata_pio"))
+ return 0;
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ goto fail;
+
+ DBG(DBG_PIO && DBG_PROBE, printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#x\n", base));
+ eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG);
+
+ loop = HZ / 2;
+ for (p = (unsigned short *) buf; (long) p <= ((long) buf + (sizeof(struct get_conf) / 2)); p++) {
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ goto fail;
+
+ loop = HZ / 2;
+ *p = inw(base + HA_RDATA);
+ }
+ if (inb(base + HA_RSTATUS) & HA_SERROR) {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during "
+ "transfer for HBA at %x\n", base));
+ goto fail;
+ }
+
+ if (htonl(EATA_SIGNATURE) != buf->signature)
+ goto fail;
+
+ DBG(DBG_PIO && DBG_PROBE, printk(KERN_NOTICE "EATA Controller found "
+ "at %#4x EATA Level: %x\n",
+ base, (uint) (buf->version)));
+
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+
+ if (!ALLOW_DMA_BOARDS) {
+ for (z = 0; z < MAXISA; z++)
+ if (base == ISAbases[z]) {
+ buf->IRQ = ISAirqs[z];
+ break;
+ }
+ }
+
+ return 1;
+
+ fail:
+ release_region(base, 9);
+ return 0;
+}
+
+static void print_pio_config(struct get_conf *gc)
+{
+ printk("Please check values: (read config data)\n");
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n", (uint) ntohl(gc->len), gc->version, gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support);
+ printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n", gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2], gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d FORCADR:%d MCH:%d RIDQ:%d\n", gc->IRQ, gc->IRQ_TR, gc->FORCADR, gc->MAX_CHAN, gc->ID_qest);
+}
+
+static uint print_selftest(uint base)
+{
+ unsigned char buffer[512];
+#ifdef VERBOSE_SETUP
+ int z;
+#endif
+
+ printk("eata_pio: executing controller self test & setup...\n");
+ while (inb(base + HA_RSTATUS) & HA_SBUSY);
+ outb(EATA_CMD_PIO_SETUPTEST, base + HA_WCOMMAND);
+ do {
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ /* nothing */ ;
+ if (inb(base + HA_RSTATUS) & HA_SDRQ) {
+ insw(base + HA_RDATA, &buffer, 256);
+#ifdef VERBOSE_SETUP
+ /* no beeps please... */
+ for (z = 0; z < 511 && buffer[z]; z++)
+ if (buffer[z] != 7)
+ printk("%c", buffer[z]);
+#endif
+ }
+ } while (inb(base + HA_RSTATUS) & (HA_SBUSY | HA_SDRQ));
+
+ return (!(inb(base + HA_RSTATUS) & HA_SERROR));
+}
+
+static int register_pio_HBA(long base, struct get_conf *gc)
+{
+ unsigned long size = 0;
+ char *buff;
+ unsigned long cplen;
+ unsigned short cppadlen;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+
+ DBG(DBG_REGISTER, print_pio_config(gc));
+
+ if (gc->DMA_support) {
+ printk("HBA at %#.4lx supports DMA. Please use EATA-DMA driver.\n", base);
+ if (!ALLOW_DMA_BOARDS)
+ return 0;
+ }
+
+ if ((buff = get_pio_board_data((uint) base, gc->IRQ, gc->scsi_id[3], cplen = (htonl(gc->cplen) + 1) / 2, cppadlen = (htons(gc->cppadlen) + 1) / 2)) == NULL) {
+ printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (unsigned long) base);
+ return 0;
+ }
+
+ if (!print_selftest(base) && !ALLOW_DMA_BOARDS) {
+ printk("HBA at %#lx failed while performing self test & setup.\n", (unsigned long) base);
+ return 0;
+ }
+
+ size = sizeof(hostdata) + (sizeof(struct eata_ccb) * ntohs(gc->queuesiz));
+
+ sh = scsi_register(&driver_template, size);
+ if (sh == NULL)
+ return 0;
+
+ if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, do_eata_pio_int_handler, SA_INTERRUPT, "EATA-PIO", sh)) {
+ reg_IRQ[gc->IRQ]++;
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = 1; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.\n", gc->IRQ);
+ return 0;
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ]) {
+ printk("Can't support more than one HBA on this IRQ,\n" " if the IRQ is edge triggered. Sorry.\n");
+ return 0;
+ } else
+ reg_IRQ[gc->IRQ]++;
+ }
+
+ hd = SD(sh);
+
+ memset(hd->ccb, 0, (sizeof(struct eata_ccb) * ntohs(gc->queuesiz)));
+ memset(hd->reads, 0, sizeof(unsigned long) * 26);
+
+ strlcpy(SD(sh)->vendor, &buff[8], sizeof(SD(sh)->vendor));
+ strlcpy(SD(sh)->name, &buff[16], sizeof(SD(sh)->name));
+ SD(sh)->revision[0] = buff[32];
+ SD(sh)->revision[1] = buff[33];
+ SD(sh)->revision[2] = buff[34];
+ SD(sh)->revision[3] = '.';
+ SD(sh)->revision[4] = buff[35];
+ SD(sh)->revision[5] = 0;
+
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ SD(sh)->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ SD(sh)->EATA_revision = 'b';
+ break;
+ case 0x22:
+ SD(sh)->EATA_revision = 'c';
+ break;
+ case 0x24:
+ SD(sh)->EATA_revision = 'z';
+ default:
+ SD(sh)->EATA_revision = '?';
+ }
+
+ if (ntohl(gc->len) >= 0x22) {
+ if (gc->is_PCI)
+ hd->bustype = IS_PCI;
+ else if (gc->is_EISA)
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ } else {
+ if (buff[21] == '4')
+ hd->bustype = IS_PCI;
+ else if (buff[21] == '2')
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ }
+
+ SD(sh)->cplen = cplen;
+ SD(sh)->cppadlen = cppadlen;
+ SD(sh)->hostid = gc->scsi_id[3];
+ SD(sh)->devflags = 1 << gc->scsi_id[3];
+ SD(sh)->moresupport = gc->MORE_support;
+ sh->unique_id = base;
+ sh->base = base;
+ sh->io_port = base;
+ sh->n_io_port = 9;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = PIO;
+ sh->this_id = gc->scsi_id[3];
+ sh->can_queue = 1;
+ sh->cmd_per_lun = 1;
+ sh->sg_tablesize = SG_ALL;
+
+ hd->channel = 0;
+
+ sh->max_id = 8;
+ sh->max_lun = 8;
+
+ if (gc->SECOND)
+ hd->primary = 0;
+ else
+ hd->primary = 1;
+
+ sh->unchecked_isa_dma = 0; /* We can only do PIO */
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if (hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+ return (1);
+}
+
+static void find_pio_ISA(struct get_conf *buf)
+{
+ int i;
+
+ for (i = 0; i < MAXISA; i++) {
+ if (!ISAbases[i])
+ continue;
+ if (!get_pio_conf_PIO(ISAbases[i], buf))
+ continue;
+ if (!register_pio_HBA(ISAbases[i], buf))
+ release_region(ISAbases[i], 9);
+ else
+ ISAbases[i] = 0;
+ }
+ return;
+}
+
+static void find_pio_EISA(struct get_conf *buf)
+{
+ u32 base;
+ int i;
+
+#ifdef CHECKPAL
+ u8 pal1, pal2, pal3;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i]) { /* Still a possibility ? */
+
+ base = 0x1c88 + (i * 0x1000);
+#ifdef CHECKPAL
+ pal1 = inb((u16) base - 8);
+ pal2 = inb((u16) base - 7);
+ pal3 = inb((u16) base - 6);
+
+ if (((pal1 == 0x12) && (pal2 == 0x14)) || ((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) || ((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
+ DBG(DBG_PROBE, printk(KERN_NOTICE "EISA EATA id tags found: " "%x %x %x \n", (int) pal1, (int) pal2, (int) pal3));
+#endif
+ if (get_pio_conf_PIO(base, buf)) {
+ DBG(DBG_PROBE && DBG_EISA, print_pio_config(buf));
+ if (buf->IRQ) {
+ if (!register_pio_HBA(base, buf))
+ release_region(base, 9);
+ } else {
+ printk(KERN_NOTICE "eata_dma: No valid IRQ. HBA " "removed from list\n");
+ release_region(base, 9);
+ }
+ }
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#ifdef CHECKPAL
+ }
+#endif
+ }
+ }
+ return;
+}
+
+static void find_pio_PCI(struct get_conf *buf)
+{
+#ifndef CONFIG_PCI
+ printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+#else
+ struct pci_dev *dev = NULL;
+ u32 base, x;
+
+ while ((dev = pci_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT, dev)) != NULL) {
+ DBG(DBG_PROBE && DBG_PCI, printk("eata_pio: find_PCI, HBA at %s\n", pci_name(dev)));
+ if (pci_enable_device(dev))
+ continue;
+ pci_set_master(dev);
+ base = pci_resource_flags(dev, 0);
+ if (base & IORESOURCE_MEM) {
+ printk("eata_pio: invalid base address of device %s\n", pci_name(dev));
+ continue;
+ }
+ base = pci_resource_start(dev, 0);
+ /* EISA tag there ? */
+ if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
+ continue; /* Jep, it's forced, so move on */
+ base += 0x10; /* Now, THIS is the real address */
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_pio_conf_PIO(base, buf)) {
+ if (buf->FORCADR) { /* If the address is forced */
+ release_region(base, 9);
+ continue; /* we'll find it later */
+ }
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+
+ if (!register_pio_HBA(base, buf)) {
+ release_region(base, 9);
+ continue;
+ }
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88) {
+ x = (base >> 12) & 0x0f;
+ EISAbases[x] = 0;
+ }
+ }
+#ifdef CHECK_BLINK
+ else if (check_blink_state(base)) {
+ printk("eata_pio: HBA is in BLINK state.\n" "Consult your HBAs manual to correct this.\n");
+ }
+#endif
+ }
+ }
+#endif /* #ifndef CONFIG_PCI */
+}
+
+static int eata_pio_detect(struct scsi_host_template *tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ int i;
+
+ find_pio_PCI(&gc);
+ find_pio_EISA(&gc);
+ find_pio_ISA(&gc);
+
+ for (i = 0; i <= MAXIRQ; i++)
+ if (reg_IRQ[i])
+ request_irq(i, do_eata_pio_int_handler, SA_INTERRUPT, "EATA-PIO", NULL);
+
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) PIO driver version: %d.%d%s\n"
+ "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n" " Alfred Arnold, a.arnold@kfa-juelich.de\n" "This release only supports DASD devices (harddisks)\n", VER_MAJOR, VER_MINOR, VER_SUB);
+
+ printk("Registered HBAs:\n");
+ printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr:" " QS: SG: CPL:\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4x %2d %d %d %c"
+ " %2d %2d %2d\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P') ?
+ "PCI " : (SD(HBA_ptr)->bustype == 'E') ? "EISA" : "ISA ",
+ (uint) HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel, HBA_ptr->this_id,
+ SD(HBA_ptr)->primary ? 'Y' : 'N', HBA_ptr->can_queue,
+ HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ }
+ return (registered_HBAs);
+}
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "eata_pio",
+ .name = "EATA (Extended Attachment) PIO driver",
+ .proc_info = eata_pio_proc_info,
+ .detect = eata_pio_detect,
+ .release = eata_pio_release,
+ .queuecommand = eata_pio_queue,
+ .eh_abort_handler = eata_pio_abort,
+ .eh_host_reset_handler = eata_pio_host_reset,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+MODULE_AUTHOR("Michael Neuffer, Alfred Arnold");
+MODULE_DESCRIPTION("EATA SCSI PIO driver");
+MODULE_LICENSE("GPL");
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/eata_pio.h b/drivers/scsi/eata_pio.h
new file mode 100644
index 000000000000..7deeb935748b
--- /dev/null
+++ b/drivers/scsi/eata_pio.h
@@ -0,0 +1,53 @@
+/********************************************************
+* Header file for eata_pio.c Linux EATA-PIO SCSI driver *
+* (c) 1993-96 Michael Neuffer *
+*********************************************************
+* last change: 2002/11/02 *
+********************************************************/
+
+
+#ifndef _EATA_PIO_H
+#define _EATA_PIO_H
+
+#define VER_MAJOR 0
+#define VER_MINOR 0
+#define VER_SUB "1b"
+
+/************************************************************************
+ * Here you can switch parts of the code on and of *
+ ************************************************************************/
+
+#define VERBOSE_SETUP /* show startup screen of 2001 */
+#define ALLOW_DMA_BOARDS 1
+
+/************************************************************************
+ * Debug options. *
+ * Enable DEBUG and whichever options you require. *
+ ************************************************************************/
+#define DEBUG_EATA 1 /* Enable debug code. */
+#define DPT_DEBUG 0 /* Bobs special */
+#define DBG_DELAY 0 /* Build in delays so debug messages can be
+ * be read before they vanish of the top of
+ * the screen!
+ */
+#define DBG_PROBE 0 /* Debug probe routines. */
+#define DBG_ISA 0 /* Trace ISA routines */
+#define DBG_EISA 0 /* Trace EISA routines */
+#define DBG_PCI 0 /* Trace PCI routines */
+#define DBG_PIO 0 /* Trace get_config_PIO */
+#define DBG_COM 0 /* Trace command call */
+#define DBG_QUEUE 0 /* Trace command queueing. */
+#define DBG_INTR 0 /* Trace interrupt service routine. */
+#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
+#define DBG_PROC_WRITE 0
+#define DBG_REGISTER 0 /* */
+#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort) */
+
+#if DEBUG_EATA
+#define DBG(x, y) if ((x)) {y;}
+#else
+#define DBG(x, y)
+#endif
+
+#endif /* _EATA_PIO_H */
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
new file mode 100644
index 000000000000..d8ab73b68031
--- /dev/null
+++ b/drivers/scsi/esp.c
@@ -0,0 +1,4402 @@
+/* $Id: esp.c,v 1.101 2002/01/15 06:48:55 davem Exp $
+ * esp.c: EnhancedScsiProcessor Sun SCSI driver code.
+ *
+ * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* TODO:
+ *
+ * 1) Maybe disable parity checking in config register one for SCSI1
+ * targets. (Gilmore says parity error on the SBus can lock up
+ * old sun4c's)
+ * 2) Add support for DMA2 pipelining.
+ * 3) Add tagged queueing.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include "esp.h"
+
+#include <asm/sbus.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#ifndef __sparc_v9__
+#include <asm/machines.h>
+#include <asm/idprom.h>
+#endif
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#define DEBUG_ESP
+/* #define DEBUG_ESP_HME */
+/* #define DEBUG_ESP_DATA */
+/* #define DEBUG_ESP_QUEUE */
+/* #define DEBUG_ESP_DISCONNECT */
+/* #define DEBUG_ESP_STATUS */
+/* #define DEBUG_ESP_PHASES */
+/* #define DEBUG_ESP_WORKBUS */
+/* #define DEBUG_STATE_MACHINE */
+/* #define DEBUG_ESP_CMDS */
+/* #define DEBUG_ESP_IRQS */
+/* #define DEBUG_SDTR */
+/* #define DEBUG_ESP_SG */
+
+/* Use the following to sprinkle debugging messages in a way which
+ * suits you if combinations of the above become too verbose when
+ * trying to track down a specific problem.
+ */
+/* #define DEBUG_ESP_MISC */
+
+#if defined(DEBUG_ESP)
+#define ESPLOG(foo) printk foo
+#else
+#define ESPLOG(foo)
+#endif /* (DEBUG_ESP) */
+
+#if defined(DEBUG_ESP_HME)
+#define ESPHME(foo) printk foo
+#else
+#define ESPHME(foo)
+#endif
+
+#if defined(DEBUG_ESP_DATA)
+#define ESPDATA(foo) printk foo
+#else
+#define ESPDATA(foo)
+#endif
+
+#if defined(DEBUG_ESP_QUEUE)
+#define ESPQUEUE(foo) printk foo
+#else
+#define ESPQUEUE(foo)
+#endif
+
+#if defined(DEBUG_ESP_DISCONNECT)
+#define ESPDISC(foo) printk foo
+#else
+#define ESPDISC(foo)
+#endif
+
+#if defined(DEBUG_ESP_STATUS)
+#define ESPSTAT(foo) printk foo
+#else
+#define ESPSTAT(foo)
+#endif
+
+#if defined(DEBUG_ESP_PHASES)
+#define ESPPHASE(foo) printk foo
+#else
+#define ESPPHASE(foo)
+#endif
+
+#if defined(DEBUG_ESP_WORKBUS)
+#define ESPBUS(foo) printk foo
+#else
+#define ESPBUS(foo)
+#endif
+
+#if defined(DEBUG_ESP_IRQS)
+#define ESPIRQ(foo) printk foo
+#else
+#define ESPIRQ(foo)
+#endif
+
+#if defined(DEBUG_SDTR)
+#define ESPSDTR(foo) printk foo
+#else
+#define ESPSDTR(foo)
+#endif
+
+#if defined(DEBUG_ESP_MISC)
+#define ESPMISC(foo) printk foo
+#else
+#define ESPMISC(foo)
+#endif
+
+/* Command phase enumeration. */
+enum {
+ not_issued = 0x00, /* Still in the issue_SC queue. */
+
+ /* Various forms of selecting a target. */
+#define in_slct_mask 0x10
+ in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */
+ in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */
+ in_slct_msg = 0x12, /* select, then send a message */
+ in_slct_tag = 0x13, /* select and send tagged queue msg */
+ in_slct_sneg = 0x14, /* select and acquire sync capabilities */
+
+ /* Any post selection activity. */
+#define in_phases_mask 0x20
+ in_datain = 0x20, /* Data is transferring from the bus */
+ in_dataout = 0x21, /* Data is transferring to the bus */
+ in_data_done = 0x22, /* Last DMA data operation done (maybe) */
+ in_msgin = 0x23, /* Eating message from target */
+ in_msgincont = 0x24, /* Eating more msg bytes from target */
+ in_msgindone = 0x25, /* Decide what to do with what we got */
+ in_msgout = 0x26, /* Sending message to target */
+ in_msgoutdone = 0x27, /* Done sending msg out */
+ in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */
+ in_cmdend = 0x29, /* Done sending slow cmd */
+ in_status = 0x2a, /* Was in status phase, finishing cmd */
+ in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */
+ in_the_dark = 0x2c, /* Don't know what bus phase we are in */
+
+ /* Special states, ie. not normal bus transitions... */
+#define in_spec_mask 0x80
+ in_abortone = 0x80, /* Aborting one command currently */
+ in_abortall = 0x81, /* Blowing away all commands we have */
+ in_resetdev = 0x82, /* SCSI target reset in progress */
+ in_resetbus = 0x83, /* SCSI bus reset in progress */
+ in_tgterror = 0x84, /* Target did something stupid */
+};
+
+enum {
+ /* Zero has special meaning, see skipahead[12]. */
+/*0*/ do_never,
+
+/*1*/ do_phase_determine,
+/*2*/ do_reset_bus,
+/*3*/ do_reset_complete,
+/*4*/ do_work_bus,
+/*5*/ do_intr_end
+};
+
+/* The master ring of all esp hosts we are managing in this driver. */
+static struct esp *espchain;
+static DEFINE_SPINLOCK(espchain_lock);
+static int esps_running = 0;
+
+/* Forward declarations. */
+static irqreturn_t esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+
+/* Debugging routines */
+struct esp_cmdstrings {
+ u8 cmdchar;
+ char *text;
+} esp_cmd_strings[] = {
+ /* Miscellaneous */
+ { ESP_CMD_NULL, "ESP_NOP", },
+ { ESP_CMD_FLUSH, "FIFO_FLUSH", },
+ { ESP_CMD_RC, "RSTESP", },
+ { ESP_CMD_RS, "RSTSCSI", },
+ /* Disconnected State Group */
+ { ESP_CMD_RSEL, "RESLCTSEQ", },
+ { ESP_CMD_SEL, "SLCTNATN", },
+ { ESP_CMD_SELA, "SLCTATN", },
+ { ESP_CMD_SELAS, "SLCTATNSTOP", },
+ { ESP_CMD_ESEL, "ENSLCTRESEL", },
+ { ESP_CMD_DSEL, "DISSELRESEL", },
+ { ESP_CMD_SA3, "SLCTATN3", },
+ { ESP_CMD_RSEL3, "RESLCTSEQ", },
+ /* Target State Group */
+ { ESP_CMD_SMSG, "SNDMSG", },
+ { ESP_CMD_SSTAT, "SNDSTATUS", },
+ { ESP_CMD_SDATA, "SNDDATA", },
+ { ESP_CMD_DSEQ, "DISCSEQ", },
+ { ESP_CMD_TSEQ, "TERMSEQ", },
+ { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", },
+ { ESP_CMD_DCNCT, "DISC", },
+ { ESP_CMD_RMSG, "RCVMSG", },
+ { ESP_CMD_RCMD, "RCVCMD", },
+ { ESP_CMD_RDATA, "RCVDATA", },
+ { ESP_CMD_RCSEQ, "RCVCMDSEQ", },
+ /* Initiator State Group */
+ { ESP_CMD_TI, "TRANSINFO", },
+ { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", },
+ { ESP_CMD_MOK, "MSGACCEPTED", },
+ { ESP_CMD_TPAD, "TPAD", },
+ { ESP_CMD_SATN, "SATN", },
+ { ESP_CMD_RATN, "RATN", },
+};
+#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))
+
+/* Print textual representation of an ESP command */
+static inline void esp_print_cmd(u8 espcmd)
+{
+ u8 dma_bit = espcmd & ESP_CMD_DMA;
+ int i;
+
+ espcmd &= ~dma_bit;
+ for (i = 0; i < NUM_ESP_COMMANDS; i++)
+ if (esp_cmd_strings[i].cmdchar == espcmd)
+ break;
+ if (i == NUM_ESP_COMMANDS)
+ printk("ESP_Unknown");
+ else
+ printk("%s%s", esp_cmd_strings[i].text,
+ ((dma_bit) ? "+DMA" : ""));
+}
+
+/* Print the status register's value */
+static inline void esp_print_statreg(u8 statreg)
+{
+ u8 phase;
+
+ printk("STATUS<");
+ phase = statreg & ESP_STAT_PMASK;
+ printk("%s,", (phase == ESP_DOP ? "DATA-OUT" :
+ (phase == ESP_DIP ? "DATA-IN" :
+ (phase == ESP_CMDP ? "COMMAND" :
+ (phase == ESP_STATP ? "STATUS" :
+ (phase == ESP_MOP ? "MSG-OUT" :
+ (phase == ESP_MIP ? "MSG_IN" :
+ "unknown")))))));
+ if (statreg & ESP_STAT_TDONE)
+ printk("TRANS_DONE,");
+ if (statreg & ESP_STAT_TCNT)
+ printk("TCOUNT_ZERO,");
+ if (statreg & ESP_STAT_PERR)
+ printk("P_ERROR,");
+ if (statreg & ESP_STAT_SPAM)
+ printk("SPAM,");
+ if (statreg & ESP_STAT_INTR)
+ printk("IRQ,");
+ printk(">");
+}
+
+/* Print the interrupt register's value */
+static inline void esp_print_ireg(u8 intreg)
+{
+ printk("INTREG< ");
+ if (intreg & ESP_INTR_S)
+ printk("SLCT_NATN ");
+ if (intreg & ESP_INTR_SATN)
+ printk("SLCT_ATN ");
+ if (intreg & ESP_INTR_RSEL)
+ printk("RSLCT ");
+ if (intreg & ESP_INTR_FDONE)
+ printk("FDONE ");
+ if (intreg & ESP_INTR_BSERV)
+ printk("BSERV ");
+ if (intreg & ESP_INTR_DC)
+ printk("DISCNCT ");
+ if (intreg & ESP_INTR_IC)
+ printk("ILL_CMD ");
+ if (intreg & ESP_INTR_SR)
+ printk("SCSI_BUS_RESET ");
+ printk(">");
+}
+
+/* Print the sequence step registers contents */
+static inline void esp_print_seqreg(u8 stepreg)
+{
+ stepreg &= ESP_STEP_VBITS;
+ printk("STEP<%s>",
+ (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" :
+ (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
+ (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
+ (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
+ (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
+ "UNKNOWN"))))));
+}
+
+static char *phase_string(int phase)
+{
+ switch (phase) {
+ case not_issued:
+ return "UNISSUED";
+ case in_slct_norm:
+ return "SLCTNORM";
+ case in_slct_stop:
+ return "SLCTSTOP";
+ case in_slct_msg:
+ return "SLCTMSG";
+ case in_slct_tag:
+ return "SLCTTAG";
+ case in_slct_sneg:
+ return "SLCTSNEG";
+ case in_datain:
+ return "DATAIN";
+ case in_dataout:
+ return "DATAOUT";
+ case in_data_done:
+ return "DATADONE";
+ case in_msgin:
+ return "MSGIN";
+ case in_msgincont:
+ return "MSGINCONT";
+ case in_msgindone:
+ return "MSGINDONE";
+ case in_msgout:
+ return "MSGOUT";
+ case in_msgoutdone:
+ return "MSGOUTDONE";
+ case in_cmdbegin:
+ return "CMDBEGIN";
+ case in_cmdend:
+ return "CMDEND";
+ case in_status:
+ return "STATUS";
+ case in_freeing:
+ return "FREEING";
+ case in_the_dark:
+ return "CLUELESS";
+ case in_abortone:
+ return "ABORTONE";
+ case in_abortall:
+ return "ABORTALL";
+ case in_resetdev:
+ return "RESETDEV";
+ case in_resetbus:
+ return "RESETBUS";
+ case in_tgterror:
+ return "TGTERROR";
+ default:
+ return "UNKNOWN";
+ };
+}
+
+#ifdef DEBUG_STATE_MACHINE
+static inline void esp_advance_phase(struct scsi_cmnd *s, int newphase)
+{
+ ESPLOG(("<%s>", phase_string(newphase)));
+ s->SCp.sent_command = s->SCp.phase;
+ s->SCp.phase = newphase;
+}
+#else
+#define esp_advance_phase(__s, __newphase) \
+ (__s)->SCp.sent_command = (__s)->SCp.phase; \
+ (__s)->SCp.phase = (__newphase);
+#endif
+
+#ifdef DEBUG_ESP_CMDS
+static inline void esp_cmd(struct esp *esp, u8 cmd)
+{
+ esp->espcmdlog[esp->espcmdent] = cmd;
+ esp->espcmdent = (esp->espcmdent + 1) & 31;
+ sbus_writeb(cmd, esp->eregs + ESP_CMD);
+}
+#else
+#define esp_cmd(__esp, __cmd) \
+ sbus_writeb((__cmd), ((__esp)->eregs) + ESP_CMD)
+#endif
+
+#define ESP_INTSOFF(__dregs) \
+ sbus_writel(sbus_readl((__dregs)+DMA_CSR)&~(DMA_INT_ENAB), (__dregs)+DMA_CSR)
+#define ESP_INTSON(__dregs) \
+ sbus_writel(sbus_readl((__dregs)+DMA_CSR)|DMA_INT_ENAB, (__dregs)+DMA_CSR)
+#define ESP_IRQ_P(__dregs) \
+ (sbus_readl((__dregs)+DMA_CSR) & (DMA_HNDL_INTR|DMA_HNDL_ERROR))
+
+/* How we use the various Linux SCSI data structures for operation.
+ *
+ * struct scsi_cmnd:
+ *
+ * We keep track of the synchronous capabilities of a target
+ * in the device member, using sync_min_period and
+ * sync_max_offset. These are the values we directly write
+ * into the ESP registers while running a command. If offset
+ * is zero the ESP will use asynchronous transfers.
+ * If the borken flag is set we assume we shouldn't even bother
+ * trying to negotiate for synchronous transfer as this target
+ * is really stupid. If we notice the target is dropping the
+ * bus, and we have been allowing it to disconnect, we clear
+ * the disconnect flag.
+ */
+
+
+/* Manipulation of the ESP command queues. Thanks to the aha152x driver
+ * and its author, Juergen E. Fischer, for the methods used here.
+ * Note that these are per-ESP queues, not global queues like
+ * the aha152x driver uses.
+ */
+static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
+{
+ struct scsi_cmnd *end;
+
+ new_SC->host_scribble = (unsigned char *) NULL;
+ if (!*SC)
+ *SC = new_SC;
+ else {
+ for (end=*SC;end->host_scribble;end=(struct scsi_cmnd *)end->host_scribble)
+ ;
+ end->host_scribble = (unsigned char *) new_SC;
+ }
+}
+
+static inline void prepend_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC)
+{
+ new_SC->host_scribble = (unsigned char *) *SC;
+ *SC = new_SC;
+}
+
+static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd **SC)
+{
+ struct scsi_cmnd *ptr;
+ ptr = *SC;
+ if (ptr)
+ *SC = (struct scsi_cmnd *) (*SC)->host_scribble;
+ return ptr;
+}
+
+static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC, int target, int lun)
+{
+ struct scsi_cmnd *ptr, *prev;
+
+ for (ptr = *SC, prev = NULL;
+ ptr && ((ptr->device->id != target) || (ptr->device->lun != lun));
+ prev = ptr, ptr = (struct scsi_cmnd *) ptr->host_scribble)
+ ;
+ if (ptr) {
+ if (prev)
+ prev->host_scribble=ptr->host_scribble;
+ else
+ *SC=(struct scsi_cmnd *)ptr->host_scribble;
+ }
+ return ptr;
+}
+
+/* Resetting various pieces of the ESP scsi driver chipset/buses. */
+static void esp_reset_dma(struct esp *esp)
+{
+ int can_do_burst16, can_do_burst32, can_do_burst64;
+ int can_do_sbus64;
+ u32 tmp;
+
+ can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
+ can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
+ can_do_burst64 = 0;
+ can_do_sbus64 = 0;
+ if (sbus_can_dma_64bit(esp->sdev))
+ can_do_sbus64 = 1;
+ if (sbus_can_burst64(esp->sdev))
+ can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
+
+ /* Punt the DVMA into a known state. */
+ if (esp->dma->revision != dvmahme) {
+ tmp = sbus_readl(esp->dregs + DMA_CSR);
+ sbus_writel(tmp | DMA_RST_SCSI, esp->dregs + DMA_CSR);
+ sbus_writel(tmp & ~DMA_RST_SCSI, esp->dregs + DMA_CSR);
+ }
+ switch (esp->dma->revision) {
+ case dvmahme:
+ /* This is the HME DVMA gate array. */
+
+ sbus_writel(DMA_RESET_FAS366, esp->dregs + DMA_CSR);
+ sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR);
+
+ esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB);
+ esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ);
+
+ if (can_do_burst64)
+ esp->prev_hme_dmacsr |= DMA_BRST64;
+ else if (can_do_burst32)
+ esp->prev_hme_dmacsr |= DMA_BRST32;
+
+ if (can_do_sbus64) {
+ esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
+ sbus_set_sbus64(esp->sdev, esp->bursts);
+ }
+
+ /* This chip is horrible. */
+ while (sbus_readl(esp->dregs + DMA_CSR) & DMA_PEND_READ)
+ udelay(1);
+
+ sbus_writel(0, esp->dregs + DMA_CSR);
+ sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
+
+ /* This is necessary to avoid having the SCSI channel
+ * engine lock up on us.
+ */
+ sbus_writel(0, esp->dregs + DMA_ADDR);
+
+ break;
+ case dvmarev2:
+ /* This is the gate array found in the sun4m
+ * NCR SBUS I/O subsystem.
+ */
+ if (esp->erev != esp100) {
+ tmp = sbus_readl(esp->dregs + DMA_CSR);
+ sbus_writel(tmp | DMA_3CLKS, esp->dregs + DMA_CSR);
+ }
+ break;
+ case dvmarev3:
+ tmp = sbus_readl(esp->dregs + DMA_CSR);
+ tmp &= ~DMA_3CLKS;
+ tmp |= DMA_2CLKS;
+ if (can_do_burst32) {
+ tmp &= ~DMA_BRST_SZ;
+ tmp |= DMA_BRST32;
+ }
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ break;
+ case dvmaesc1:
+ /* This is the DMA unit found on SCSI/Ether cards. */
+ tmp = sbus_readl(esp->dregs + DMA_CSR);
+ tmp |= DMA_ADD_ENABLE;
+ tmp &= ~DMA_BCNT_ENAB;
+ if (!can_do_burst32 && can_do_burst16) {
+ tmp |= DMA_ESC_BURST;
+ } else {
+ tmp &= ~(DMA_ESC_BURST);
+ }
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ break;
+ default:
+ break;
+ };
+ ESP_INTSON(esp->dregs);
+}
+
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static void __init esp_reset_esp(struct esp *esp)
+{
+ u8 family_code, version;
+ int i;
+
+ /* Now reset the ESP chip */
+ esp_cmd(esp, ESP_CMD_RC);
+ esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+ esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+
+ /* Reload the configuration registers */
+ sbus_writeb(esp->cfact, esp->eregs + ESP_CFACT);
+ esp->prev_stp = 0;
+ sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
+ esp->prev_soff = 0;
+ sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
+ sbus_writeb(esp->neg_defp, esp->eregs + ESP_TIMEO);
+
+ /* This is the only point at which it is reliable to read
+ * the ID-code for a fast ESP chip variants.
+ */
+ esp->max_period = ((35 * esp->ccycle) / 1000);
+ if (esp->erev == fast) {
+ version = sbus_readb(esp->eregs + ESP_UID);
+ family_code = (version & 0xf8) >> 3;
+ if (family_code == 0x02)
+ esp->erev = fas236;
+ else if (family_code == 0x0a)
+ esp->erev = fashme; /* Version is usually '5'. */
+ else
+ esp->erev = fas100a;
+ ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n",
+ esp->esp_id,
+ (esp->erev == fas236) ? "fas236" :
+ ((esp->erev == fas100a) ? "fas100a" :
+ "fasHME"), family_code, (version & 7)));
+
+ esp->min_period = ((4 * esp->ccycle) / 1000);
+ } else {
+ esp->min_period = ((5 * esp->ccycle) / 1000);
+ }
+ esp->max_period = (esp->max_period + 3)>>2;
+ esp->min_period = (esp->min_period + 3)>>2;
+
+ sbus_writeb(esp->config1, esp->eregs + ESP_CFG1);
+ switch (esp->erev) {
+ case esp100:
+ /* nothing to do */
+ break;
+ case esp100a:
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+ break;
+ case esp236:
+ /* Slow 236 */
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+ esp->prev_cfg3 = esp->config3[0];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ break;
+ case fashme:
+ esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
+ /* fallthrough... */
+ case fas236:
+ /* Fast 236 or HME */
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+ for (i = 0; i < 16; i++) {
+ if (esp->erev == fashme) {
+ u8 cfg3;
+
+ cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH;
+ if (esp->scsi_id >= 8)
+ cfg3 |= ESP_CONFIG3_IDBIT3;
+ esp->config3[i] |= cfg3;
+ } else {
+ esp->config3[i] |= ESP_CONFIG3_FCLK;
+ }
+ }
+ esp->prev_cfg3 = esp->config3[0];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ if (esp->erev == fashme) {
+ esp->radelay = 80;
+ } else {
+ if (esp->diff)
+ esp->radelay = 0;
+ else
+ esp->radelay = 96;
+ }
+ break;
+ case fas100a:
+ /* Fast 100a */
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+ for (i = 0; i < 16; i++)
+ esp->config3[i] |= ESP_CONFIG3_FCLOCK;
+ esp->prev_cfg3 = esp->config3[0];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ esp->radelay = 32;
+ break;
+ default:
+ panic("esp: what could it be... I wonder...");
+ break;
+ };
+
+ /* Eat any bitrot in the chip */
+ sbus_readb(esp->eregs + ESP_INTRPT);
+ udelay(100);
+}
+
+/* This places the ESP into a known state at boot time. */
+static void __init esp_bootup_reset(struct esp *esp)
+{
+ u8 tmp;
+
+ /* Reset the DMA */
+ esp_reset_dma(esp);
+
+ /* Reset the ESP */
+ esp_reset_esp(esp);
+
+ /* Reset the SCSI bus, but tell ESP not to generate an irq */
+ tmp = sbus_readb(esp->eregs + ESP_CFG1);
+ tmp |= ESP_CONFIG1_SRRDISAB;
+ sbus_writeb(tmp, esp->eregs + ESP_CFG1);
+
+ esp_cmd(esp, ESP_CMD_RS);
+ udelay(400);
+
+ sbus_writeb(esp->config1, esp->eregs + ESP_CFG1);
+
+ /* Eat any bitrot in the chip and we are done... */
+ sbus_readb(esp->eregs + ESP_INTRPT);
+}
+
+static void esp_chain_add(struct esp *esp)
+{
+ spin_lock_irq(&espchain_lock);
+ if (espchain) {
+ struct esp *elink = espchain;
+ while (elink->next)
+ elink = elink->next;
+ elink->next = esp;
+ } else {
+ espchain = esp;
+ }
+ esp->next = NULL;
+ spin_unlock_irq(&espchain_lock);
+}
+
+static void esp_chain_del(struct esp *esp)
+{
+ spin_lock_irq(&espchain_lock);
+ if (espchain == esp) {
+ espchain = esp->next;
+ } else {
+ struct esp *elink = espchain;
+ while (elink->next != esp)
+ elink = elink->next;
+ elink->next = esp->next;
+ }
+ esp->next = NULL;
+ spin_unlock_irq(&espchain_lock);
+}
+
+static int __init esp_find_dvma(struct esp *esp, struct sbus_dev *dma_sdev)
+{
+ struct sbus_dev *sdev = esp->sdev;
+ struct sbus_dma *dma;
+
+ if (dma_sdev != NULL) {
+ for_each_dvma(dma) {
+ if (dma->sdev == dma_sdev)
+ break;
+ }
+ } else {
+ for_each_dvma(dma) {
+ /* If allocated already, can't use it. */
+ if (dma->allocated)
+ continue;
+
+ if (dma->sdev == NULL)
+ break;
+
+ /* If bus + slot are the same and it has the
+ * correct OBP name, it's ours.
+ */
+ if (sdev->bus == dma->sdev->bus &&
+ sdev->slot == dma->sdev->slot &&
+ (!strcmp(dma->sdev->prom_name, "dma") ||
+ !strcmp(dma->sdev->prom_name, "espdma")))
+ break;
+ }
+ }
+
+ /* If we don't know how to handle the dvma,
+ * do not use this device.
+ */
+ if (dma == NULL) {
+ printk("Cannot find dvma for ESP%d's SCSI\n", esp->esp_id);
+ return -1;
+ }
+ if (dma->allocated) {
+ printk("esp%d: can't use my espdma\n", esp->esp_id);
+ return -1;
+ }
+ dma->allocated = 1;
+ esp->dma = dma;
+ esp->dregs = dma->regs;
+
+ return 0;
+}
+
+static int __init esp_map_regs(struct esp *esp, int hme)
+{
+ struct sbus_dev *sdev = esp->sdev;
+ struct resource *res;
+
+ /* On HME, two reg sets exist, first is DVMA,
+ * second is ESP registers.
+ */
+ if (hme)
+ res = &sdev->resource[1];
+ else
+ res = &sdev->resource[0];
+
+ esp->eregs = sbus_ioremap(res, 0, ESP_REG_SIZE, "ESP Registers");
+
+ if (esp->eregs == 0)
+ return -1;
+ return 0;
+}
+
+static int __init esp_map_cmdarea(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->sdev;
+
+ esp->esp_command = sbus_alloc_consistent(sdev, 16,
+ &esp->esp_command_dvma);
+ if (esp->esp_command == NULL ||
+ esp->esp_command_dvma == 0)
+ return -1;
+ return 0;
+}
+
+static int __init esp_register_irq(struct esp *esp)
+{
+ esp->ehost->irq = esp->irq = esp->sdev->irqs[0];
+
+ /* We used to try various overly-clever things to
+ * reduce the interrupt processing overhead on
+ * sun4c/sun4m when multiple ESP's shared the
+ * same IRQ. It was too complex and messy to
+ * sanely maintain.
+ */
+ if (request_irq(esp->ehost->irq, esp_intr,
+ SA_SHIRQ, "ESP SCSI", esp)) {
+ printk("esp%d: Cannot acquire irq line\n",
+ esp->esp_id);
+ return -1;
+ }
+
+ printk("esp%d: IRQ %s ", esp->esp_id,
+ __irq_itoa(esp->ehost->irq));
+
+ return 0;
+}
+
+static void __init esp_get_scsi_id(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->sdev;
+
+ esp->scsi_id = prom_getintdefault(esp->prom_node,
+ "initiator-id",
+ -1);
+ if (esp->scsi_id == -1)
+ esp->scsi_id = prom_getintdefault(esp->prom_node,
+ "scsi-initiator-id",
+ -1);
+ if (esp->scsi_id == -1)
+ esp->scsi_id = (sdev->bus == NULL) ? 7 :
+ prom_getintdefault(sdev->bus->prom_node,
+ "scsi-initiator-id",
+ 7);
+ esp->ehost->this_id = esp->scsi_id;
+ esp->scsi_id_mask = (1 << esp->scsi_id);
+
+}
+
+static void __init esp_get_clock_params(struct esp *esp)
+{
+ struct sbus_dev *sdev = esp->sdev;
+ int prom_node = esp->prom_node;
+ int sbus_prom_node;
+ unsigned int fmhz;
+ u8 ccf;
+
+ if (sdev != NULL && sdev->bus != NULL)
+ sbus_prom_node = sdev->bus->prom_node;
+ else
+ sbus_prom_node = 0;
+
+ /* This is getting messy but it has to be done
+ * correctly or else you get weird behavior all
+ * over the place. We are trying to basically
+ * figure out three pieces of information.
+ *
+ * a) Clock Conversion Factor
+ *
+ * This is a representation of the input
+ * crystal clock frequency going into the
+ * ESP on this machine. Any operation whose
+ * timing is longer than 400ns depends on this
+ * value being correct. For example, you'll
+ * get blips for arbitration/selection during
+ * high load or with multiple targets if this
+ * is not set correctly.
+ *
+ * b) Selection Time-Out
+ *
+ * The ESP isn't very bright and will arbitrate
+ * for the bus and try to select a target
+ * forever if you let it. This value tells
+ * the ESP when it has taken too long to
+ * negotiate and that it should interrupt
+ * the CPU so we can see what happened.
+ * The value is computed as follows (from
+ * NCR/Symbios chip docs).
+ *
+ * (Time Out Period) * (Input Clock)
+ * STO = ----------------------------------
+ * (8192) * (Clock Conversion Factor)
+ *
+ * You usually want the time out period to be
+ * around 250ms, I think we'll set it a little
+ * bit higher to account for fully loaded SCSI
+ * bus's and slow devices that don't respond so
+ * quickly to selection attempts. (yeah, I know
+ * this is out of spec. but there is a lot of
+ * buggy pieces of firmware out there so bite me)
+ *
+ * c) Imperical constants for synchronous offset
+ * and transfer period register values
+ *
+ * This entails the smallest and largest sync
+ * period we could ever handle on this ESP.
+ */
+
+ fmhz = prom_getintdefault(prom_node, "clock-frequency", -1);
+ if (fmhz == -1)
+ fmhz = (!sbus_prom_node) ? 0 :
+ prom_getintdefault(sbus_prom_node, "clock-frequency", -1);
+
+ if (fmhz <= (5000000))
+ ccf = 0;
+ else
+ ccf = (((5000000 - 1) + (fmhz))/(5000000));
+
+ if (!ccf || ccf > 8) {
+ /* If we can't find anything reasonable,
+ * just assume 20MHZ. This is the clock
+ * frequency of the older sun4c's where I've
+ * been unable to find the clock-frequency
+ * PROM property. All other machines provide
+ * useful values it seems.
+ */
+ ccf = ESP_CCF_F4;
+ fmhz = (20000000);
+ }
+
+ if (ccf == (ESP_CCF_F7 + 1))
+ esp->cfact = ESP_CCF_F0;
+ else if (ccf == ESP_CCF_NEVER)
+ esp->cfact = ESP_CCF_F2;
+ else
+ esp->cfact = ccf;
+ esp->raw_cfact = ccf;
+
+ esp->cfreq = fmhz;
+ esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+ esp->ctick = ESP_TICK(ccf, esp->ccycle);
+ esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+ esp->sync_defp = SYNC_DEFP_SLOW;
+
+ printk("SCSI ID %d Clk %dMHz CCYC=%d CCF=%d TOut %d ",
+ esp->scsi_id, (fmhz / 1000000),
+ (int)esp->ccycle, (int)ccf, (int) esp->neg_defp);
+}
+
+static void __init esp_get_bursts(struct esp *esp, struct sbus_dev *dma)
+{
+ struct sbus_dev *sdev = esp->sdev;
+ u8 bursts;
+
+ bursts = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff);
+
+ if (dma) {
+ u8 tmp = prom_getintdefault(dma->prom_node,
+ "burst-sizes", 0xff);
+ if (tmp != 0xff)
+ bursts &= tmp;
+ }
+
+ if (sdev->bus) {
+ u8 tmp = prom_getintdefault(sdev->bus->prom_node,
+ "burst-sizes", 0xff);
+ if (tmp != 0xff)
+ bursts &= tmp;
+ }
+
+ if (bursts == 0xff ||
+ (bursts & DMA_BURST16) == 0 ||
+ (bursts & DMA_BURST32) == 0)
+ bursts = (DMA_BURST32 - 1);
+
+ esp->bursts = bursts;
+}
+
+static void __init esp_get_revision(struct esp *esp)
+{
+ u8 tmp;
+
+ esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+ esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+
+ tmp = sbus_readb(esp->eregs + ESP_CFG2);
+ tmp &= ~ESP_CONFIG2_MAGIC;
+ if (tmp != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+ /* If what we write to cfg2 does not come back, cfg2
+ * is not implemented, therefore this must be a plain
+ * esp100.
+ */
+ esp->erev = esp100;
+ printk("NCR53C90(esp100)\n");
+ } else {
+ esp->config2 = 0;
+ esp->prev_cfg3 = esp->config3[0] = 5;
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+ sbus_writeb(0, esp->eregs + ESP_CFG3);
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+
+ tmp = sbus_readb(esp->eregs + ESP_CFG3);
+ if (tmp != 5) {
+ /* The cfg2 register is implemented, however
+ * cfg3 is not, must be esp100a.
+ */
+ esp->erev = esp100a;
+ printk("NCR53C90A(esp100a)\n");
+ } else {
+ int target;
+
+ for (target = 0; target < 16; target++)
+ esp->config3[target] = 0;
+ esp->prev_cfg3 = 0;
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+
+ /* All of cfg{1,2,3} implemented, must be one of
+ * the fas variants, figure out which one.
+ */
+ if (esp->raw_cfact > ESP_CCF_F5) {
+ esp->erev = fast;
+ esp->sync_defp = SYNC_DEFP_FAST;
+ printk("NCR53C9XF(espfast)\n");
+ } else {
+ esp->erev = esp236;
+ printk("NCR53C9x(esp236)\n");
+ }
+ esp->config2 = 0;
+ sbus_writeb(esp->config2, esp->eregs + ESP_CFG2);
+ }
+ }
+}
+
+static void __init esp_init_swstate(struct esp *esp)
+{
+ int i;
+
+ /* Command queues... */
+ esp->current_SC = NULL;
+ esp->disconnected_SC = NULL;
+ esp->issue_SC = NULL;
+
+ /* Target and current command state... */
+ esp->targets_present = 0;
+ esp->resetting_bus = 0;
+ esp->snip = 0;
+
+ init_waitqueue_head(&esp->reset_queue);
+
+ /* Debugging... */
+ for(i = 0; i < 32; i++)
+ esp->espcmdlog[i] = 0;
+ esp->espcmdent = 0;
+
+ /* MSG phase state... */
+ for(i = 0; i < 16; i++) {
+ esp->cur_msgout[i] = 0;
+ esp->cur_msgin[i] = 0;
+ }
+ esp->prevmsgout = esp->prevmsgin = 0;
+ esp->msgout_len = esp->msgin_len = 0;
+
+ /* Clear the one behind caches to hold unmatchable values. */
+ esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff;
+ esp->prev_hme_dmacsr = 0xffffffff;
+}
+
+static int __init detect_one_esp(struct scsi_host_template *tpnt, struct sbus_dev *esp_dev,
+ struct sbus_dev *espdma, struct sbus_bus *sbus,
+ int id, int hme)
+{
+ struct Scsi_Host *esp_host = scsi_register(tpnt, sizeof(struct esp));
+ struct esp *esp;
+
+ if (!esp_host) {
+ printk("ESP: Cannot register SCSI host\n");
+ return -1;
+ }
+ if (hme)
+ esp_host->max_id = 16;
+ esp = (struct esp *) esp_host->hostdata;
+ esp->ehost = esp_host;
+ esp->sdev = esp_dev;
+ esp->esp_id = id;
+ esp->prom_node = esp_dev->prom_node;
+ prom_getstring(esp->prom_node, "name", esp->prom_name,
+ sizeof(esp->prom_name));
+
+ esp_chain_add(esp);
+ if (esp_find_dvma(esp, espdma) < 0)
+ goto fail_unlink;
+ if (esp_map_regs(esp, hme) < 0) {
+ printk("ESP registers unmappable");
+ goto fail_dvma_release;
+ }
+ if (esp_map_cmdarea(esp) < 0) {
+ printk("ESP DVMA transport area unmappable");
+ goto fail_unmap_regs;
+ }
+ if (esp_register_irq(esp) < 0)
+ goto fail_unmap_cmdarea;
+
+ esp_get_scsi_id(esp);
+
+ esp->diff = prom_getbool(esp->prom_node, "differential");
+ if (esp->diff)
+ printk("Differential ");
+
+ esp_get_clock_params(esp);
+ esp_get_bursts(esp, espdma);
+ esp_get_revision(esp);
+ esp_init_swstate(esp);
+
+ esp_bootup_reset(esp);
+
+ return 0;
+
+fail_unmap_cmdarea:
+ sbus_free_consistent(esp->sdev, 16,
+ (void *) esp->esp_command,
+ esp->esp_command_dvma);
+
+fail_unmap_regs:
+ sbus_iounmap(esp->eregs, ESP_REG_SIZE);
+
+fail_dvma_release:
+ esp->dma->allocated = 0;
+
+fail_unlink:
+ esp_chain_del(esp);
+ scsi_unregister(esp_host);
+ return -1;
+}
+
+/* Detecting ESP chips on the machine. This is the simple and easy
+ * version.
+ */
+
+#ifdef CONFIG_SUN4
+
+#include <asm/sun4paddr.h>
+
+static int __init esp_detect(struct scsi_host_template *tpnt)
+{
+ static struct sbus_dev esp_dev;
+ int esps_in_use = 0;
+
+ espchain = 0;
+
+ if (sun4_esp_physaddr) {
+ memset (&esp_dev, 0, sizeof(esp_dev));
+ esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr;
+ esp_dev.irqs[0] = 4;
+ esp_dev.resource[0].start = sun4_esp_physaddr;
+ esp_dev.resource[0].end = sun4_esp_physaddr + ESP_REG_SIZE - 1;
+ esp_dev.resource[0].flags = IORESOURCE_IO;
+
+ if (!detect_one_esp(tpnt, &esp_dev, NULL, NULL, 0, 0))
+ esps_in_use++;
+ printk("ESP: Total of 1 ESP hosts found, %d actually in use.\n", esps_in_use);
+ esps_running = esps_in_use;
+ }
+ return esps_in_use;
+}
+
+#else /* !CONFIG_SUN4 */
+
+static int __init esp_detect(struct scsi_host_template *tpnt)
+{
+ struct sbus_bus *sbus;
+ struct sbus_dev *esp_dev, *sbdev_iter;
+ int nesps = 0, esps_in_use = 0;
+
+ espchain = 0;
+ if (!sbus_root) {
+#ifdef CONFIG_PCI
+ return 0;
+#else
+ panic("No SBUS in esp_detect()");
+#endif
+ }
+ for_each_sbus(sbus) {
+ for_each_sbusdev(sbdev_iter, sbus) {
+ struct sbus_dev *espdma = NULL;
+ int hme = 0;
+
+ /* Is it an esp sbus device? */
+ esp_dev = sbdev_iter;
+ if (strcmp(esp_dev->prom_name, "esp") &&
+ strcmp(esp_dev->prom_name, "SUNW,esp")) {
+ if (!strcmp(esp_dev->prom_name, "SUNW,fas")) {
+ hme = 1;
+ espdma = esp_dev;
+ } else {
+ if (!esp_dev->child ||
+ (strcmp(esp_dev->prom_name, "espdma") &&
+ strcmp(esp_dev->prom_name, "dma")))
+ continue; /* nope... */
+ espdma = esp_dev;
+ esp_dev = esp_dev->child;
+ if (strcmp(esp_dev->prom_name, "esp") &&
+ strcmp(esp_dev->prom_name, "SUNW,esp"))
+ continue; /* how can this happen? */
+ }
+ }
+
+ if (detect_one_esp(tpnt, esp_dev, espdma, sbus, nesps++, hme) < 0)
+ continue;
+
+ esps_in_use++;
+ } /* for each sbusdev */
+ } /* for each sbus */
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,
+ esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+}
+
+#endif /* !CONFIG_SUN4 */
+
+/*
+ */
+static int esp_release(struct Scsi_Host *host)
+{
+ struct esp *esp = (struct esp *) host->hostdata;
+
+ ESP_INTSOFF(esp->dregs);
+#if 0
+ esp_reset_dma(esp);
+ esp_reset_esp(esp);
+#endif
+
+ free_irq(esp->ehost->irq, esp);
+ sbus_free_consistent(esp->sdev, 16,
+ (void *) esp->esp_command, esp->esp_command_dvma);
+ sbus_iounmap(esp->eregs, ESP_REG_SIZE);
+ esp->dma->allocated = 0;
+ esp_chain_del(esp);
+
+ return 0;
+}
+
+/* The info function will return whatever useful
+ * information the developer sees fit. If not provided, then
+ * the name field will be used instead.
+ */
+static const char *esp_info(struct Scsi_Host *host)
+{
+ struct esp *esp;
+
+ esp = (struct esp *) host->hostdata;
+ switch (esp->erev) {
+ case esp100:
+ return "Sparc ESP100 (NCR53C90)";
+ case esp100a:
+ return "Sparc ESP100A (NCR53C90A)";
+ case esp236:
+ return "Sparc ESP236";
+ case fas236:
+ return "Sparc ESP236-FAST";
+ case fashme:
+ return "Sparc ESP366-HME";
+ case fas100a:
+ return "Sparc ESP100A-FAST";
+ default:
+ return "Bogon ESP revision";
+ };
+}
+
+/* From Wolfgang Stanglmeier's NCR scsi driver. */
+struct info_str
+{
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len)
+{
+ struct scsi_device *sdev;
+ struct info_str info;
+ int i;
+
+ info.buffer = ptr;
+ info.length = len;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "Sparc ESP Host Adapter:\n");
+ copy_info(&info, "\tPROM node\t\t%08x\n", (unsigned int) esp->prom_node);
+ copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name);
+ copy_info(&info, "\tESP Model\t\t");
+ switch (esp->erev) {
+ case esp100:
+ copy_info(&info, "ESP100\n");
+ break;
+ case esp100a:
+ copy_info(&info, "ESP100A\n");
+ break;
+ case esp236:
+ copy_info(&info, "ESP236\n");
+ break;
+ case fas236:
+ copy_info(&info, "FAS236\n");
+ break;
+ case fas100a:
+ copy_info(&info, "FAS100A\n");
+ break;
+ case fast:
+ copy_info(&info, "FAST\n");
+ break;
+ case fashme:
+ copy_info(&info, "Happy Meal FAS\n");
+ break;
+ case espunknown:
+ default:
+ copy_info(&info, "Unknown!\n");
+ break;
+ };
+ copy_info(&info, "\tDMA Revision\t\t");
+ switch (esp->dma->revision) {
+ case dvmarev0:
+ copy_info(&info, "Rev 0\n");
+ break;
+ case dvmaesc1:
+ copy_info(&info, "ESC Rev 1\n");
+ break;
+ case dvmarev1:
+ copy_info(&info, "Rev 1\n");
+ break;
+ case dvmarev2:
+ copy_info(&info, "Rev 2\n");
+ break;
+ case dvmarev3:
+ copy_info(&info, "Rev 3\n");
+ break;
+ case dvmarevplus:
+ copy_info(&info, "Rev 1+\n");
+ break;
+ case dvmahme:
+ copy_info(&info, "Rev HME/FAS\n");
+ break;
+ default:
+ copy_info(&info, "Unknown!\n");
+ break;
+ };
+ copy_info(&info, "\tLive Targets\t\t[ ");
+ for (i = 0; i < 15; i++) {
+ if (esp->targets_present & (1 << i))
+ copy_info(&info, "%d ", i);
+ }
+ copy_info(&info, "]\n\n");
+
+ /* Now describe the state of each existing target. */
+ copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n");
+
+ shost_for_each_device(sdev, esp->ehost) {
+ struct esp_device *esp_dev = sdev->hostdata;
+ uint id = sdev->id;
+
+ if (!(esp->targets_present & (1 << id)))
+ continue;
+
+ copy_info(&info, "%d\t\t", id);
+ copy_info(&info, "%08lx\t", esp->config3[id]);
+ copy_info(&info, "[%02lx,%02lx]\t\t\t",
+ esp_dev->sync_max_offset,
+ esp_dev->sync_min_period);
+ copy_info(&info, "%s\t\t",
+ esp_dev->disconnect ? "yes" : "no");
+ copy_info(&info, "%s\n",
+ (esp->config3[id] & ESP_CONFIG3_EWIDE) ? "yes" : "no");
+ }
+ return info.pos > info.offset? info.pos - info.offset : 0;
+}
+
+/* ESP proc filesystem code. */
+static int esp_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ struct esp *esp;
+
+ if (inout)
+ return -EINVAL; /* not yet */
+
+ for_each_esp(esp) {
+ if (esp->ehost == host)
+ break;
+ }
+ if (!esp)
+ return -EINVAL;
+
+ if (start)
+ *start = buffer;
+
+ return esp_host_info(esp, buffer, offset, length);
+}
+
+static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
+{
+ if (sp->use_sg == 0) {
+ sp->SCp.this_residual = sp->request_bufflen;
+ sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
+ sp->SCp.buffers_residual = 0;
+ if (sp->request_bufflen) {
+ sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer,
+ sp->SCp.this_residual,
+ sp->sc_data_direction);
+ sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in);
+ } else {
+ sp->SCp.ptr = NULL;
+ }
+ } else {
+ sp->SCp.buffer = (struct scatterlist *) sp->buffer;
+ sp->SCp.buffers_residual = sbus_map_sg(esp->sdev,
+ sp->SCp.buffer,
+ sp->use_sg,
+ sp->sc_data_direction);
+ sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer);
+ sp->SCp.ptr = (char *) ((unsigned long)sg_dma_address(sp->SCp.buffer));
+ }
+}
+
+static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp)
+{
+ if (sp->use_sg) {
+ sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg,
+ sp->sc_data_direction);
+ } else if (sp->request_bufflen) {
+ sbus_unmap_single(esp->sdev,
+ sp->SCp.have_data_in,
+ sp->request_bufflen,
+ sp->sc_data_direction);
+ }
+}
+
+static void esp_restore_pointers(struct esp *esp, struct scsi_cmnd *sp)
+{
+ struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
+
+ sp->SCp.ptr = ep->saved_ptr;
+ sp->SCp.buffer = ep->saved_buffer;
+ sp->SCp.this_residual = ep->saved_this_residual;
+ sp->SCp.buffers_residual = ep->saved_buffers_residual;
+}
+
+static void esp_save_pointers(struct esp *esp, struct scsi_cmnd *sp)
+{
+ struct esp_pointers *ep = &esp->data_pointers[sp->device->id];
+
+ ep->saved_ptr = sp->SCp.ptr;
+ ep->saved_buffer = sp->SCp.buffer;
+ ep->saved_this_residual = sp->SCp.this_residual;
+ ep->saved_buffers_residual = sp->SCp.buffers_residual;
+}
+
+/* Some rules:
+ *
+ * 1) Never ever panic while something is live on the bus.
+ * If there is to be any chance of syncing the disks this
+ * rule is to be obeyed.
+ *
+ * 2) Any target that causes a foul condition will no longer
+ * have synchronous transfers done to it, no questions
+ * asked.
+ *
+ * 3) Keep register accesses to a minimum. Think about some
+ * day when we have Xbus machines this is running on and
+ * the ESP chip is on the other end of the machine on a
+ * different board from the cpu where this is running.
+ */
+
+/* Fire off a command. We assume the bus is free and that the only
+ * case where we could see an interrupt is where we have disconnected
+ * commands active and they are trying to reselect us.
+ */
+static inline void esp_check_cmd(struct esp *esp, struct scsi_cmnd *sp)
+{
+ switch (sp->cmd_len) {
+ case 6:
+ case 10:
+ case 12:
+ esp->esp_slowcmd = 0;
+ break;
+
+ default:
+ esp->esp_slowcmd = 1;
+ esp->esp_scmdleft = sp->cmd_len;
+ esp->esp_scmdp = &sp->cmnd[0];
+ break;
+ };
+}
+
+static inline void build_sync_nego_msg(struct esp *esp, int period, int offset)
+{
+ esp->cur_msgout[0] = EXTENDED_MESSAGE;
+ esp->cur_msgout[1] = 3;
+ esp->cur_msgout[2] = EXTENDED_SDTR;
+ esp->cur_msgout[3] = period;
+ esp->cur_msgout[4] = offset;
+ esp->msgout_len = 5;
+}
+
+/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */
+static inline void build_wide_nego_msg(struct esp *esp, int size)
+{
+ esp->cur_msgout[0] = EXTENDED_MESSAGE;
+ esp->cur_msgout[1] = 2;
+ esp->cur_msgout[2] = EXTENDED_WDTR;
+ switch (size) {
+ case 32:
+ esp->cur_msgout[3] = 2;
+ break;
+ case 16:
+ esp->cur_msgout[3] = 1;
+ break;
+ case 8:
+ default:
+ esp->cur_msgout[3] = 0;
+ break;
+ };
+
+ esp->msgout_len = 4;
+}
+
+static void esp_exec_cmd(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr;
+ struct scsi_device *SDptr;
+ struct esp_device *esp_dev;
+ volatile u8 *cmdp = esp->esp_command;
+ u8 the_esp_command;
+ int lun, target;
+ int i;
+
+ /* Hold off if we have disconnected commands and
+ * an IRQ is showing...
+ */
+ if (esp->disconnected_SC && ESP_IRQ_P(esp->dregs))
+ return;
+
+ /* Grab first member of the issue queue. */
+ SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
+
+ /* Safe to panic here because current_SC is null. */
+ if (!SCptr)
+ panic("esp: esp_exec_cmd and issue queue is NULL");
+
+ SDptr = SCptr->device;
+ esp_dev = SDptr->hostdata;
+ lun = SCptr->device->lun;
+ target = SCptr->device->id;
+
+ esp->snip = 0;
+ esp->msgout_len = 0;
+
+ /* Send it out whole, or piece by piece? The ESP
+ * only knows how to automatically send out 6, 10,
+ * and 12 byte commands. I used to think that the
+ * Linux SCSI code would never throw anything other
+ * than that to us, but then again there is the
+ * SCSI generic driver which can send us anything.
+ */
+ esp_check_cmd(esp, SCptr);
+
+ /* If arbitration/selection is successful, the ESP will leave
+ * ATN asserted, causing the target to go into message out
+ * phase. The ESP will feed the target the identify and then
+ * the target can only legally go to one of command,
+ * datain/out, status, or message in phase, or stay in message
+ * out phase (should we be trying to send a sync negotiation
+ * message after the identify). It is not allowed to drop
+ * BSY, but some buggy targets do and we check for this
+ * condition in the selection complete code. Most of the time
+ * we'll make the command bytes available to the ESP and it
+ * will not interrupt us until it finishes command phase, we
+ * cannot do this for command sizes the ESP does not
+ * understand and in this case we'll get interrupted right
+ * when the target goes into command phase.
+ *
+ * It is absolutely _illegal_ in the presence of SCSI-2 devices
+ * to use the ESP select w/o ATN command. When SCSI-2 devices are
+ * present on the bus we _must_ always go straight to message out
+ * phase with an identify message for the target. Being that
+ * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
+ * selections should not confuse SCSI-1 we hope.
+ */
+
+ if (esp_dev->sync) {
+ /* this targets sync is known */
+#ifndef __sparc_v9__
+do_sync_known:
+#endif
+ if (esp_dev->disconnect)
+ *cmdp++ = IDENTIFY(1, lun);
+ else
+ *cmdp++ = IDENTIFY(0, lun);
+
+ if (esp->esp_slowcmd) {
+ the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+ esp_advance_phase(SCptr, in_slct_stop);
+ } else {
+ the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+ esp_advance_phase(SCptr, in_slct_norm);
+ }
+ } else if (!(esp->targets_present & (1<<target)) || !(esp_dev->disconnect)) {
+ /* After the bootup SCSI code sends both the
+ * TEST_UNIT_READY and INQUIRY commands we want
+ * to at least attempt allowing the device to
+ * disconnect.
+ */
+ ESPMISC(("esp: Selecting device for first time. target=%d "
+ "lun=%d\n", target, SCptr->device->lun));
+ if (!SDptr->borken && !esp_dev->disconnect)
+ esp_dev->disconnect = 1;
+
+ *cmdp++ = IDENTIFY(0, lun);
+ esp->prevmsgout = NOP;
+ esp_advance_phase(SCptr, in_slct_norm);
+ the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+
+ /* Take no chances... */
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ } else {
+ /* Sorry, I have had way too many problems with
+ * various CDROM devices on ESP. -DaveM
+ */
+ int cdrom_hwbug_wkaround = 0;
+
+#ifndef __sparc_v9__
+ /* Never allow disconnects or synchronous transfers on
+ * SparcStation1 and SparcStation1+. Allowing those
+ * to be enabled seems to lockup the machine completely.
+ */
+ if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+ (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+ /* But we are nice and allow tapes and removable
+ * disks (but not CDROMs) to disconnect.
+ */
+ if(SDptr->type == TYPE_TAPE ||
+ (SDptr->type != TYPE_ROM && SDptr->removable))
+ esp_dev->disconnect = 1;
+ else
+ esp_dev->disconnect = 0;
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync = 1;
+ esp->snip = 0;
+ goto do_sync_known;
+ }
+#endif /* !(__sparc_v9__) */
+
+ /* We've talked to this guy before,
+ * but never negotiated. Let's try,
+ * need to attempt WIDE first, before
+ * sync nego, as per SCSI 2 standard.
+ */
+ if (esp->erev == fashme && !esp_dev->wide) {
+ if (!SDptr->borken &&
+ SDptr->type != TYPE_ROM &&
+ SDptr->removable == 0) {
+ build_wide_nego_msg(esp, 16);
+ esp_dev->wide = 1;
+ esp->wnip = 1;
+ goto after_nego_msg_built;
+ } else {
+ esp_dev->wide = 1;
+ /* Fall through and try sync. */
+ }
+ }
+
+ if (!SDptr->borken) {
+ if ((SDptr->type == TYPE_ROM)) {
+ /* Nice try sucker... */
+ ESPMISC(("esp%d: Disabling sync for buggy "
+ "CDROM.\n", esp->esp_id));
+ cdrom_hwbug_wkaround = 1;
+ build_sync_nego_msg(esp, 0, 0);
+ } else if (SDptr->removable != 0) {
+ ESPMISC(("esp%d: Not negotiating sync/wide but "
+ "allowing disconnect for removable media.\n",
+ esp->esp_id));
+ build_sync_nego_msg(esp, 0, 0);
+ } else {
+ build_sync_nego_msg(esp, esp->sync_defp, 15);
+ }
+ } else {
+ build_sync_nego_msg(esp, 0, 0);
+ }
+ esp_dev->sync = 1;
+ esp->snip = 1;
+
+after_nego_msg_built:
+ /* A fix for broken SCSI1 targets, when they disconnect
+ * they lock up the bus and confuse ESP. So disallow
+ * disconnects for SCSI1 targets for now until we
+ * find a better fix.
+ *
+ * Addendum: This is funny, I figured out what was going
+ * on. The blotzed SCSI1 target would disconnect,
+ * one of the other SCSI2 targets or both would be
+ * disconnected as well. The SCSI1 target would
+ * stay disconnected long enough that we start
+ * up a command on one of the SCSI2 targets. As
+ * the ESP is arbitrating for the bus the SCSI1
+ * target begins to arbitrate as well to reselect
+ * the ESP. The SCSI1 target refuses to drop it's
+ * ID bit on the data bus even though the ESP is
+ * at ID 7 and is the obvious winner for any
+ * arbitration. The ESP is a poor sport and refuses
+ * to lose arbitration, it will continue indefinitely
+ * trying to arbitrate for the bus and can only be
+ * stopped via a chip reset or SCSI bus reset.
+ * Therefore _no_ disconnects for SCSI1 targets
+ * thank you very much. ;-)
+ */
+ if(((SDptr->scsi_level < 3) &&
+ (SDptr->type != TYPE_TAPE) &&
+ SDptr->removable == 0) ||
+ cdrom_hwbug_wkaround || SDptr->borken) {
+ ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
+ "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
+ esp_dev->disconnect = 0;
+ *cmdp++ = IDENTIFY(0, lun);
+ } else {
+ *cmdp++ = IDENTIFY(1, lun);
+ }
+
+ /* ESP fifo is only so big...
+ * Make this look like a slow command.
+ */
+ esp->esp_slowcmd = 1;
+ esp->esp_scmdleft = SCptr->cmd_len;
+ esp->esp_scmdp = &SCptr->cmnd[0];
+
+ the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+ esp_advance_phase(SCptr, in_slct_msg);
+ }
+
+ if (!esp->esp_slowcmd)
+ for (i = 0; i < SCptr->cmd_len; i++)
+ *cmdp++ = SCptr->cmnd[i];
+
+ /* HME sucks... */
+ if (esp->erev == fashme)
+ sbus_writeb((target & 0xf) | (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT),
+ esp->eregs + ESP_BUSID);
+ else
+ sbus_writeb(target & 7, esp->eregs + ESP_BUSID);
+ if (esp->prev_soff != esp_dev->sync_max_offset ||
+ esp->prev_stp != esp_dev->sync_min_period ||
+ (esp->erev > esp100a &&
+ esp->prev_cfg3 != esp->config3[target])) {
+ esp->prev_soff = esp_dev->sync_max_offset;
+ esp->prev_stp = esp_dev->sync_min_period;
+ sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
+ sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
+ if (esp->erev > esp100a) {
+ esp->prev_cfg3 = esp->config3[target];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ }
+ }
+ i = (cmdp - esp->esp_command);
+
+ if (esp->erev == fashme) {
+ esp_cmd(esp, ESP_CMD_FLUSH); /* Grrr! */
+
+ /* Set up the DMA and HME counters */
+ sbus_writeb(i, esp->eregs + ESP_TCLOW);
+ sbus_writeb(0, esp->eregs + ESP_TCMED);
+ sbus_writeb(0, esp->eregs + FAS_RLO);
+ sbus_writeb(0, esp->eregs + FAS_RHI);
+ esp_cmd(esp, the_esp_command);
+
+ /* Talk about touchy hardware... */
+ esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
+ (DMA_SCSI_DISAB | DMA_ENABLE)) &
+ ~(DMA_ST_WRITE));
+ sbus_writel(16, esp->dregs + DMA_COUNT);
+ sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
+ sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
+ } else {
+ u32 tmp;
+
+ /* Set up the DMA and ESP counters */
+ sbus_writeb(i, esp->eregs + ESP_TCLOW);
+ sbus_writeb(0, esp->eregs + ESP_TCMED);
+ tmp = sbus_readl(esp->dregs + DMA_CSR);
+ tmp &= ~DMA_ST_WRITE;
+ tmp |= DMA_ENABLE;
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ if (esp->dma->revision == dvmaesc1) {
+ if (i) /* Workaround ESC gate array SBUS rerun bug. */
+ sbus_writel(PAGE_SIZE, esp->dregs + DMA_COUNT);
+ }
+ sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
+
+ /* Tell ESP to "go". */
+ esp_cmd(esp, the_esp_command);
+ }
+}
+
+/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
+static int esp_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ struct esp *esp;
+
+ /* Set up func ptr and initial driver cmd-phase. */
+ SCpnt->scsi_done = done;
+ SCpnt->SCp.phase = not_issued;
+
+ /* We use the scratch area. */
+ ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->device->id, SCpnt->device->lun));
+ ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->device->lun));
+
+ esp = (struct esp *) SCpnt->device->host->hostdata;
+ esp_get_dmabufs(esp, SCpnt);
+ esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */
+
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0xff;
+ SCpnt->SCp.sent_command = 0;
+
+ /* Place into our queue. */
+ if (SCpnt->cmnd[0] == REQUEST_SENSE) {
+ ESPQUEUE(("RQSENSE\n"));
+ prepend_SC(&esp->issue_SC, SCpnt);
+ } else {
+ ESPQUEUE(("\n"));
+ append_SC(&esp->issue_SC, SCpnt);
+ }
+
+ /* Run it now if we can. */
+ if (!esp->current_SC && !esp->resetting_bus)
+ esp_exec_cmd(esp);
+
+ return 0;
+}
+
+/* Dump driver state. */
+static void esp_dump_cmd(struct scsi_cmnd *SCptr)
+{
+ ESPLOG(("[tgt<%02x> lun<%02x> "
+ "pphase<%s> cphase<%s>]",
+ SCptr->device->id, SCptr->device->lun,
+ phase_string(SCptr->SCp.sent_command),
+ phase_string(SCptr->SCp.phase)));
+}
+
+static void esp_dump_state(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+#ifdef DEBUG_ESP_CMDS
+ int i;
+#endif
+
+ ESPLOG(("esp%d: dumping state\n", esp->esp_id));
+ ESPLOG(("esp%d: dma -- cond_reg<%08x> addr<%08x>\n",
+ esp->esp_id,
+ sbus_readl(esp->dregs + DMA_CSR),
+ sbus_readl(esp->dregs + DMA_ADDR)));
+ ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+ esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
+ ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+ esp->esp_id,
+ sbus_readb(esp->eregs + ESP_STATUS),
+ sbus_readb(esp->eregs + ESP_SSTEP),
+ sbus_readb(esp->eregs + ESP_INTRPT)));
+#ifdef DEBUG_ESP_CMDS
+ printk("esp%d: last ESP cmds [", esp->esp_id);
+ i = (esp->espcmdent - 1) & 31;
+ printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
+ i = (i - 1) & 31;
+ printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
+ i = (i - 1) & 31;
+ printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
+ i = (i - 1) & 31;
+ printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">");
+ printk("]\n");
+#endif /* (DEBUG_ESP_CMDS) */
+
+ if (SCptr) {
+ ESPLOG(("esp%d: current command ", esp->esp_id));
+ esp_dump_cmd(SCptr);
+ }
+ ESPLOG(("\n"));
+ SCptr = esp->disconnected_SC;
+ ESPLOG(("esp%d: disconnected ", esp->esp_id));
+ while (SCptr) {
+ esp_dump_cmd(SCptr);
+ SCptr = (struct scsi_cmnd *) SCptr->host_scribble;
+ }
+ ESPLOG(("\n"));
+}
+
+/* Abort a command. The host_lock is acquired by caller. */
+static int esp_abort(struct scsi_cmnd *SCptr)
+{
+ struct esp *esp = (struct esp *) SCptr->device->host->hostdata;
+ int don;
+
+ ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
+ esp_dump_state(esp);
+
+ /* Wheee, if this is the current command on the bus, the
+ * best we can do is assert ATN and wait for msgout phase.
+ * This should even fix a hung SCSI bus when we lose state
+ * in the driver and timeout because the eventual phase change
+ * will cause the ESP to (eventually) give an interrupt.
+ */
+ if (esp->current_SC == SCptr) {
+ esp->cur_msgout[0] = ABORT;
+ esp->msgout_len = 1;
+ esp->msgout_ctr = 0;
+ esp_cmd(esp, ESP_CMD_SATN);
+ return SUCCESS;
+ }
+
+ /* If it is still in the issue queue then we can safely
+ * call the completion routine and report abort success.
+ */
+ don = (sbus_readl(esp->dregs + DMA_CSR) & DMA_INT_ENAB);
+ if (don) {
+ ESP_INTSOFF(esp->dregs);
+ }
+ if (esp->issue_SC) {
+ struct scsi_cmnd **prev, *this;
+ for (prev = (&esp->issue_SC), this = esp->issue_SC;
+ this != NULL;
+ prev = (struct scsi_cmnd **) &(this->host_scribble),
+ this = (struct scsi_cmnd *) this->host_scribble) {
+
+ if (this == SCptr) {
+ *prev = (struct scsi_cmnd *) this->host_scribble;
+ this->host_scribble = NULL;
+
+ esp_release_dmabufs(esp, this);
+ this->result = DID_ABORT << 16;
+ this->scsi_done(this);
+
+ if (don)
+ ESP_INTSON(esp->dregs);
+
+ return SUCCESS;
+ }
+ }
+ }
+
+ /* Yuck, the command to abort is disconnected, it is not
+ * worth trying to abort it now if something else is live
+ * on the bus at this time. So, we let the SCSI code wait
+ * a little bit and try again later.
+ */
+ if (esp->current_SC) {
+ if (don)
+ ESP_INTSON(esp->dregs);
+ return FAILED;
+ }
+
+ /* It's disconnected, we have to reconnect to re-establish
+ * the nexus and tell the device to abort. However, we really
+ * cannot 'reconnect' per se. Don't try to be fancy, just
+ * indicate failure, which causes our caller to reset the whole
+ * bus.
+ */
+
+ if (don)
+ ESP_INTSON(esp->dregs);
+
+ return FAILED;
+}
+
+/* We've sent ESP_CMD_RS to the ESP, the interrupt had just
+ * arrived indicating the end of the SCSI bus reset. Our job
+ * is to clean out the command queues and begin re-execution
+ * of SCSI commands once more.
+ */
+static int esp_finish_reset(struct esp *esp)
+{
+ struct scsi_cmnd *sp = esp->current_SC;
+
+ /* Clean up currently executing command, if any. */
+ if (sp != NULL) {
+ esp->current_SC = NULL;
+
+ esp_release_dmabufs(esp, sp);
+ sp->result = (DID_RESET << 16);
+
+ sp->scsi_done(sp);
+ }
+
+ /* Clean up disconnected queue, they have been invalidated
+ * by the bus reset.
+ */
+ if (esp->disconnected_SC) {
+ while ((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) {
+ esp_release_dmabufs(esp, sp);
+ sp->result = (DID_RESET << 16);
+
+ sp->scsi_done(sp);
+ }
+ }
+
+ /* SCSI bus reset is complete. */
+ esp->resetting_bus = 0;
+ wake_up(&esp->reset_queue);
+
+ /* Ok, now it is safe to get commands going once more. */
+ if (esp->issue_SC)
+ esp_exec_cmd(esp);
+
+ return do_intr_end;
+}
+
+static int esp_do_resetbus(struct esp *esp)
+{
+ ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
+ esp->resetting_bus = 1;
+ esp_cmd(esp, ESP_CMD_RS);
+
+ return do_intr_end;
+}
+
+/* Reset ESP chip, reset hanging bus, then kill active and
+ * disconnected commands for targets without soft reset.
+ *
+ * The host_lock is acquired by caller.
+ */
+static int esp_reset(struct scsi_cmnd *SCptr)
+{
+ struct esp *esp = (struct esp *) SCptr->device->host->hostdata;
+
+ (void) esp_do_resetbus(esp);
+
+ spin_unlock_irq(esp->ehost->host_lock);
+
+ wait_event(esp->reset_queue, (esp->resetting_bus == 0));
+
+ spin_lock_irq(esp->ehost->host_lock);
+
+ return SUCCESS;
+}
+
+/* Internal ESP done function. */
+static void esp_done(struct esp *esp, int error)
+{
+ struct scsi_cmnd *done_SC = esp->current_SC;
+
+ esp->current_SC = NULL;
+
+ esp_release_dmabufs(esp, done_SC);
+ done_SC->result = error;
+
+ done_SC->scsi_done(done_SC);
+
+ /* Bus is free, issue any commands in the queue. */
+ if (esp->issue_SC && !esp->current_SC)
+ esp_exec_cmd(esp);
+
+}
+
+/* Wheee, ESP interrupt engine. */
+
+/* Forward declarations. */
+static int esp_do_phase_determine(struct esp *esp);
+static int esp_do_data_finale(struct esp *esp);
+static int esp_select_complete(struct esp *esp);
+static int esp_do_status(struct esp *esp);
+static int esp_do_msgin(struct esp *esp);
+static int esp_do_msgindone(struct esp *esp);
+static int esp_do_msgout(struct esp *esp);
+static int esp_do_cmdbegin(struct esp *esp);
+
+#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP)
+#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP)
+
+/* Read any bytes found in the FAS366 fifo, storing them into
+ * the ESP driver software state structure.
+ */
+static void hme_fifo_read(struct esp *esp)
+{
+ u8 count = 0;
+ u8 status = esp->sreg;
+
+ /* Cannot safely frob the fifo for these following cases, but
+ * we must always read the fifo when the reselect interrupt
+ * is pending.
+ */
+ if (((esp->ireg & ESP_INTR_RSEL) == 0) &&
+ (sreg_datainp(status) ||
+ sreg_dataoutp(status) ||
+ (esp->current_SC &&
+ esp->current_SC->SCp.phase == in_data_done))) {
+ ESPHME(("<wkaround_skipped>"));
+ } else {
+ unsigned long fcnt = sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES;
+
+ /* The HME stores bytes in multiples of 2 in the fifo. */
+ ESPHME(("hme_fifo[fcnt=%d", (int)fcnt));
+ while (fcnt) {
+ esp->hme_fifo_workaround_buffer[count++] =
+ sbus_readb(esp->eregs + ESP_FDATA);
+ esp->hme_fifo_workaround_buffer[count++] =
+ sbus_readb(esp->eregs + ESP_FDATA);
+ ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1]));
+ fcnt--;
+ }
+ if (sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_F1BYTE) {
+ ESPHME(("<poke_byte>"));
+ sbus_writeb(0, esp->eregs + ESP_FDATA);
+ esp->hme_fifo_workaround_buffer[count++] =
+ sbus_readb(esp->eregs + ESP_FDATA);
+ ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1]));
+ ESPHME(("CMD_FLUSH"));
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ } else {
+ ESPHME(("no_xtra_byte"));
+ }
+ }
+ ESPHME(("wkarnd_cnt=%d]", (int)count));
+ esp->hme_fifo_workaround_count = count;
+}
+
+static inline void hme_fifo_push(struct esp *esp, u8 *bytes, u8 count)
+{
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ while (count) {
+ u8 tmp = *bytes++;
+ sbus_writeb(tmp, esp->eregs + ESP_FDATA);
+ sbus_writeb(0, esp->eregs + ESP_FDATA);
+ count--;
+ }
+}
+
+/* We try to avoid some interrupts by jumping ahead and see if the ESP
+ * has gotten far enough yet. Hence the following.
+ */
+static inline int skipahead1(struct esp *esp, struct scsi_cmnd *scp,
+ int prev_phase, int new_phase)
+{
+ if (scp->SCp.sent_command != prev_phase)
+ return 0;
+ if (ESP_IRQ_P(esp->dregs)) {
+ /* Yes, we are able to save an interrupt. */
+ if (esp->erev == fashme)
+ esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
+ esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR));
+ esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
+ if (esp->erev == fashme) {
+ /* This chip is really losing. */
+ ESPHME(("HME["));
+ /* Must latch fifo before reading the interrupt
+ * register else garbage ends up in the FIFO
+ * which confuses the driver utterly.
+ * Happy Meal indeed....
+ */
+ ESPHME(("fifo_workaround]"));
+ if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+ (esp->sreg2 & ESP_STAT2_F1BYTE))
+ hme_fifo_read(esp);
+ }
+ if (!(esp->ireg & ESP_INTR_SR))
+ return 0;
+ else
+ return do_reset_complete;
+ }
+ /* Ho hum, target is taking forever... */
+ scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+ return do_intr_end;
+}
+
+static inline int skipahead2(struct esp *esp, struct scsi_cmnd *scp,
+ int prev_phase1, int prev_phase2, int new_phase)
+{
+ if (scp->SCp.sent_command != prev_phase1 &&
+ scp->SCp.sent_command != prev_phase2)
+ return 0;
+ if (ESP_IRQ_P(esp->dregs)) {
+ /* Yes, we are able to save an interrupt. */
+ if (esp->erev == fashme)
+ esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
+ esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR));
+ esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
+ if (esp->erev == fashme) {
+ /* This chip is really losing. */
+ ESPHME(("HME["));
+
+ /* Must latch fifo before reading the interrupt
+ * register else garbage ends up in the FIFO
+ * which confuses the driver utterly.
+ * Happy Meal indeed....
+ */
+ ESPHME(("fifo_workaround]"));
+ if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+ (esp->sreg2 & ESP_STAT2_F1BYTE))
+ hme_fifo_read(esp);
+ }
+ if (!(esp->ireg & ESP_INTR_SR))
+ return 0;
+ else
+ return do_reset_complete;
+ }
+ /* Ho hum, target is taking forever... */
+ scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+ return do_intr_end;
+}
+
+/* Now some dma helpers. */
+static void dma_setup(struct esp *esp, __u32 addr, int count, int write)
+{
+ u32 nreg = sbus_readl(esp->dregs + DMA_CSR);
+
+ if (write)
+ nreg |= DMA_ST_WRITE;
+ else
+ nreg &= ~(DMA_ST_WRITE);
+ nreg |= DMA_ENABLE;
+ sbus_writel(nreg, esp->dregs + DMA_CSR);
+ if (esp->dma->revision == dvmaesc1) {
+ /* This ESC gate array sucks! */
+ __u32 src = addr;
+ __u32 dest = src + count;
+
+ if (dest & (PAGE_SIZE - 1))
+ count = PAGE_ALIGN(count);
+ sbus_writel(count, esp->dregs + DMA_COUNT);
+ }
+ sbus_writel(addr, esp->dregs + DMA_ADDR);
+}
+
+static void dma_drain(struct esp *esp)
+{
+ u32 tmp;
+
+ if (esp->dma->revision == dvmahme)
+ return;
+ if ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_FIFO_ISDRAIN) {
+ switch (esp->dma->revision) {
+ default:
+ tmp |= DMA_FIFO_STDRAIN;
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+
+ case dvmarev3:
+ case dvmaesc1:
+ while (sbus_readl(esp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN)
+ udelay(1);
+ };
+ }
+}
+
+static void dma_invalidate(struct esp *esp)
+{
+ u32 tmp;
+
+ if (esp->dma->revision == dvmahme) {
+ sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR);
+
+ esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
+ (DMA_PARITY_OFF | DMA_2CLKS |
+ DMA_SCSI_DISAB | DMA_INT_ENAB)) &
+ ~(DMA_ST_WRITE | DMA_ENABLE));
+
+ sbus_writel(0, esp->dregs + DMA_CSR);
+ sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
+
+ /* This is necessary to avoid having the SCSI channel
+ * engine lock up on us.
+ */
+ sbus_writel(0, esp->dregs + DMA_ADDR);
+ } else {
+ while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ)
+ udelay(1);
+
+ tmp &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
+ tmp |= DMA_FIFO_INV;
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ tmp &= ~DMA_FIFO_INV;
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ }
+}
+
+static inline void dma_flashclear(struct esp *esp)
+{
+ dma_drain(esp);
+ dma_invalidate(esp);
+}
+
+static int dma_can_transfer(struct esp *esp, struct scsi_cmnd *sp)
+{
+ __u32 base, end, sz;
+
+ if (esp->dma->revision == dvmarev3) {
+ sz = sp->SCp.this_residual;
+ if (sz > 0x1000000)
+ sz = 0x1000000;
+ } else {
+ base = ((__u32)((unsigned long)sp->SCp.ptr));
+ base &= (0x1000000 - 1);
+ end = (base + sp->SCp.this_residual);
+ if (end > 0x1000000)
+ end = 0x1000000;
+ sz = (end - base);
+ }
+ return sz;
+}
+
+/* Misc. esp helper macros. */
+#define esp_setcount(__eregs, __cnt, __hme) \
+ sbus_writeb(((__cnt)&0xff), (__eregs) + ESP_TCLOW); \
+ sbus_writeb((((__cnt)>>8)&0xff), (__eregs) + ESP_TCMED); \
+ if (__hme) { \
+ sbus_writeb((((__cnt)>>16)&0xff), (__eregs) + FAS_RLO); \
+ sbus_writeb(0, (__eregs) + FAS_RHI); \
+ }
+
+#define esp_getcount(__eregs, __hme) \
+ ((sbus_readb((__eregs) + ESP_TCLOW)&0xff) | \
+ ((sbus_readb((__eregs) + ESP_TCMED)&0xff) << 8) | \
+ ((__hme) ? sbus_readb((__eregs) + FAS_RLO) << 16 : 0))
+
+#define fcount(__esp) \
+ (((__esp)->erev == fashme) ? \
+ (__esp)->hme_fifo_workaround_count : \
+ sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_FBYTES)
+
+#define fnzero(__esp) \
+ (((__esp)->erev == fashme) ? 0 : \
+ sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_ONOTZERO)
+
+/* XXX speculative nops unnecessary when continuing amidst a data phase
+ * XXX even on esp100!!! another case of flooding the bus with I/O reg
+ * XXX writes...
+ */
+#define esp_maybe_nop(__esp) \
+ if ((__esp)->erev == esp100) \
+ esp_cmd((__esp), ESP_CMD_NULL)
+
+#define sreg_to_dataphase(__sreg) \
+ ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain)
+
+/* The ESP100 when in synchronous data phase, can mistake a long final
+ * REQ pulse from the target as an extra byte, it places whatever is on
+ * the data lines into the fifo. For now, we will assume when this
+ * happens that the target is a bit quirky and we don't want to
+ * be talking synchronously to it anyways. Regardless, we need to
+ * tell the ESP to eat the extraneous byte so that we can proceed
+ * to the next phase.
+ */
+static int esp100_sync_hwbug(struct esp *esp, struct scsi_cmnd *sp, int fifocnt)
+{
+ /* Do not touch this piece of code. */
+ if ((!(esp->erev == esp100)) ||
+ (!(sreg_datainp((esp->sreg = sbus_readb(esp->eregs + ESP_STATUS))) &&
+ !fifocnt) &&
+ !(sreg_dataoutp(esp->sreg) && !fnzero(esp)))) {
+ if (sp->SCp.phase == in_dataout)
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ return 0;
+ } else {
+ /* Async mode for this guy. */
+ build_sync_nego_msg(esp, 0, 0);
+
+ /* Ack the bogus byte, but set ATN first. */
+ esp_cmd(esp, ESP_CMD_SATN);
+ esp_cmd(esp, ESP_CMD_MOK);
+ return 1;
+ }
+}
+
+/* This closes the window during a selection with a reselect pending, because
+ * we use DMA for the selection process the FIFO should hold the correct
+ * contents if we get reselected during this process. So we just need to
+ * ack the possible illegal cmd interrupt pending on the esp100.
+ */
+static inline int esp100_reconnect_hwbug(struct esp *esp)
+{
+ u8 tmp;
+
+ if (esp->erev != esp100)
+ return 0;
+ tmp = sbus_readb(esp->eregs + ESP_INTRPT);
+ if (tmp & ESP_INTR_SR)
+ return 1;
+ return 0;
+}
+
+/* This verifies the BUSID bits during a reselection so that we know which
+ * target is talking to us.
+ */
+static inline int reconnect_target(struct esp *esp)
+{
+ int it, me = esp->scsi_id_mask, targ = 0;
+
+ if (2 != fcount(esp))
+ return -1;
+ if (esp->erev == fashme) {
+ /* HME does not latch it's own BUS ID bits during
+ * a reselection. Also the target number is given
+ * as an unsigned char, not as a sole bit number
+ * like the other ESP's do.
+ * Happy Meal indeed....
+ */
+ targ = esp->hme_fifo_workaround_buffer[0];
+ } else {
+ it = sbus_readb(esp->eregs + ESP_FDATA);
+ if (!(it & me))
+ return -1;
+ it &= ~me;
+ if (it & (it - 1))
+ return -1;
+ while (!(it & 1))
+ targ++, it >>= 1;
+ }
+ return targ;
+}
+
+/* This verifies the identify from the target so that we know which lun is
+ * being reconnected.
+ */
+static inline int reconnect_lun(struct esp *esp)
+{
+ int lun;
+
+ if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
+ return -1;
+ if (esp->erev == fashme)
+ lun = esp->hme_fifo_workaround_buffer[1];
+ else
+ lun = sbus_readb(esp->eregs + ESP_FDATA);
+
+ /* Yes, you read this correctly. We report lun of zero
+ * if we see parity error. ESP reports parity error for
+ * the lun byte, and this is the only way to hope to recover
+ * because the target is connected.
+ */
+ if (esp->sreg & ESP_STAT_PERR)
+ return 0;
+
+ /* Check for illegal bits being set in the lun. */
+ if ((lun & 0x40) || !(lun & 0x80))
+ return -1;
+
+ return lun & 7;
+}
+
+/* This puts the driver in a state where it can revitalize a command that
+ * is being continued due to reselection.
+ */
+static inline void esp_connect(struct esp *esp, struct scsi_cmnd *sp)
+{
+ struct esp_device *esp_dev = sp->device->hostdata;
+
+ if (esp->prev_soff != esp_dev->sync_max_offset ||
+ esp->prev_stp != esp_dev->sync_min_period ||
+ (esp->erev > esp100a &&
+ esp->prev_cfg3 != esp->config3[sp->device->id])) {
+ esp->prev_soff = esp_dev->sync_max_offset;
+ esp->prev_stp = esp_dev->sync_min_period;
+ sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
+ sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
+ if (esp->erev > esp100a) {
+ esp->prev_cfg3 = esp->config3[sp->device->id];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ }
+ }
+ esp->current_SC = sp;
+}
+
+/* This will place the current working command back into the issue queue
+ * if we are to receive a reselection amidst a selection attempt.
+ */
+static inline void esp_reconnect(struct esp *esp, struct scsi_cmnd *sp)
+{
+ if (!esp->disconnected_SC)
+ ESPLOG(("esp%d: Weird, being reselected but disconnected "
+ "command queue is empty.\n", esp->esp_id));
+ esp->snip = 0;
+ esp->current_SC = 0;
+ sp->SCp.phase = not_issued;
+ append_SC(&esp->issue_SC, sp);
+}
+
+/* Begin message in phase. */
+static int esp_do_msgin(struct esp *esp)
+{
+ /* Must be very careful with the fifo on the HME */
+ if ((esp->erev != fashme) ||
+ !(sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_FEMPTY))
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ esp_maybe_nop(esp);
+ esp_cmd(esp, ESP_CMD_TI);
+ esp->msgin_len = 1;
+ esp->msgin_ctr = 0;
+ esp_advance_phase(esp->current_SC, in_msgindone);
+ return do_work_bus;
+}
+
+/* This uses various DMA csr fields and the fifo flags count value to
+ * determine how many bytes were successfully sent/received by the ESP.
+ */
+static inline int esp_bytes_sent(struct esp *esp, int fifo_count)
+{
+ int rval = sbus_readl(esp->dregs + DMA_ADDR) - esp->esp_command_dvma;
+
+ if (esp->dma->revision == dvmarev1)
+ rval -= (4 - ((sbus_readl(esp->dregs + DMA_CSR) & DMA_READ_AHEAD)>>11));
+ return rval - fifo_count;
+}
+
+static inline void advance_sg(struct scsi_cmnd *sp)
+{
+ ++sp->SCp.buffer;
+ --sp->SCp.buffers_residual;
+ sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer);
+ sp->SCp.ptr = (char *)((unsigned long)sg_dma_address(sp->SCp.buffer));
+}
+
+/* Please note that the way I've coded these routines is that I _always_
+ * check for a disconnect during any and all information transfer
+ * phases. The SCSI standard states that the target _can_ cause a BUS
+ * FREE condition by dropping all MSG/CD/IO/BSY signals. Also note
+ * that during information transfer phases the target controls every
+ * change in phase, the only thing the initiator can do is "ask" for
+ * a message out phase by driving ATN true. The target can, and sometimes
+ * will, completely ignore this request so we cannot assume anything when
+ * we try to force a message out phase to abort/reset a target. Most of
+ * the time the target will eventually be nice and go to message out, so
+ * we may have to hold on to our state about what we want to tell the target
+ * for some period of time.
+ */
+
+/* I think I have things working here correctly. Even partial transfers
+ * within a buffer or sub-buffer should not upset us at all no matter
+ * how bad the target and/or ESP fucks things up.
+ */
+static int esp_do_data(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ int thisphase, hmuch;
+
+ ESPDATA(("esp_do_data: "));
+ esp_maybe_nop(esp);
+ thisphase = sreg_to_dataphase(esp->sreg);
+ esp_advance_phase(SCptr, thisphase);
+ ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
+ hmuch = dma_can_transfer(esp, SCptr);
+ if (hmuch > (64 * 1024) && (esp->erev != fashme))
+ hmuch = (64 * 1024);
+ ESPDATA(("hmuch<%d> ", hmuch));
+ esp->current_transfer_size = hmuch;
+
+ if (esp->erev == fashme) {
+ u32 tmp = esp->prev_hme_dmacsr;
+
+ /* Always set the ESP count registers first. */
+ esp_setcount(esp->eregs, hmuch, 1);
+
+ /* Get the DMA csr computed. */
+ tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
+ if (thisphase == in_datain)
+ tmp |= DMA_ST_WRITE;
+ else
+ tmp &= ~(DMA_ST_WRITE);
+ esp->prev_hme_dmacsr = tmp;
+
+ ESPDATA(("DMA|TI --> do_intr_end\n"));
+ if (thisphase == in_datain) {
+ sbus_writel(hmuch, esp->dregs + DMA_COUNT);
+ esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
+ } else {
+ esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
+ sbus_writel(hmuch, esp->dregs + DMA_COUNT);
+ }
+ sbus_writel((__u32)((unsigned long)SCptr->SCp.ptr), esp->dregs+DMA_ADDR);
+ sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR);
+ } else {
+ esp_setcount(esp->eregs, hmuch, 0);
+ dma_setup(esp, ((__u32)((unsigned long)SCptr->SCp.ptr)),
+ hmuch, (thisphase == in_datain));
+ ESPDATA(("DMA|TI --> do_intr_end\n"));
+ esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ return do_intr_end;
+}
+
+/* See how successful the data transfer was. */
+static int esp_do_data_finale(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;
+
+ ESPDATA(("esp_do_data_finale: "));
+
+ if (SCptr->SCp.phase == in_datain) {
+ if (esp->sreg & ESP_STAT_PERR) {
+ /* Yuck, parity error. The ESP asserts ATN
+ * so that we can go to message out phase
+ * immediately and inform the target that
+ * something bad happened.
+ */
+ ESPLOG(("esp%d: data bad parity detected.\n",
+ esp->esp_id));
+ esp->cur_msgout[0] = INITIATOR_ERROR;
+ esp->msgout_len = 1;
+ }
+ dma_drain(esp);
+ }
+ dma_invalidate(esp);
+
+ /* This could happen for the above parity error case. */
+ if (esp->ireg != ESP_INTR_BSERV) {
+ /* Please go to msgout phase, please please please... */
+ ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
+ esp->esp_id));
+ return esp_do_phase_determine(esp);
+ }
+
+ /* Check for partial transfers and other horrible events.
+ * Note, here we read the real fifo flags register even
+ * on HME broken adapters because we skip the HME fifo
+ * workaround code in esp_handle() if we are doing data
+ * phase things. We don't want to fuck directly with
+ * the fifo like that, especially if doing synchronous
+ * transfers! Also, will need to double the count on
+ * HME if we are doing wide transfers, as the HME fifo
+ * will move and count 16-bit quantities during wide data.
+ * SMCC _and_ Qlogic can both bite me.
+ */
+ fifocnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES);
+ if (esp->erev != fashme)
+ ecount = esp_getcount(esp->eregs, 0);
+ bytes_sent = esp->current_transfer_size;
+
+ ESPDATA(("trans_sz(%d), ", bytes_sent));
+ if (esp->erev == fashme) {
+ if (!(esp->sreg & ESP_STAT_TCNT)) {
+ ecount = esp_getcount(esp->eregs, 1);
+ bytes_sent -= ecount;
+ }
+
+ /* Always subtract any cruft remaining in the FIFO. */
+ if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE)
+ fifocnt <<= 1;
+ if (SCptr->SCp.phase == in_dataout)
+ bytes_sent -= fifocnt;
+
+ /* I have an IBM disk which exhibits the following
+ * behavior during writes to it. It disconnects in
+ * the middle of a partial transfer, the current sglist
+ * buffer is 1024 bytes, the disk stops data transfer
+ * at 512 bytes.
+ *
+ * However the FAS366 reports that 32 more bytes were
+ * transferred than really were. This is precisely
+ * the size of a fully loaded FIFO in wide scsi mode.
+ * The FIFO state recorded indicates that it is empty.
+ *
+ * I have no idea if this is a bug in the FAS366 chip
+ * or a bug in the firmware on this IBM disk. In any
+ * event the following seems to be a good workaround. -DaveM
+ */
+ if (bytes_sent != esp->current_transfer_size &&
+ SCptr->SCp.phase == in_dataout) {
+ int mask = (64 - 1);
+
+ if ((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0)
+ mask >>= 1;
+
+ if (bytes_sent & mask)
+ bytes_sent -= (bytes_sent & mask);
+ }
+ } else {
+ if (!(esp->sreg & ESP_STAT_TCNT))
+ bytes_sent -= ecount;
+ if (SCptr->SCp.phase == in_dataout)
+ bytes_sent -= fifocnt;
+ }
+
+ ESPDATA(("bytes_sent(%d), ", bytes_sent));
+
+ /* If we were in synchronous mode, check for peculiarities. */
+ if (esp->erev == fashme) {
+ if (esp_dev->sync_max_offset) {
+ if (SCptr->SCp.phase == in_dataout)
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ } else {
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ }
+ } else {
+ if (esp_dev->sync_max_offset)
+ bogus_data = esp100_sync_hwbug(esp, SCptr, fifocnt);
+ else
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ }
+
+ /* Until we are sure of what has happened, we are certainly
+ * in the dark.
+ */
+ esp_advance_phase(SCptr, in_the_dark);
+
+ if (bytes_sent < 0) {
+ /* I've seen this happen due to lost state in this
+ * driver. No idea why it happened, but allowing
+ * this value to be negative caused things to
+ * lock up. This allows greater chance of recovery.
+ * In fact every time I've seen this, it has been
+ * a driver bug without question.
+ */
+ ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
+ ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
+ esp->esp_id,
+ esp->current_transfer_size, fifocnt, ecount));
+ ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
+ esp->esp_id,
+ SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
+ ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id,
+ SCptr->device->id));
+ SCptr->device->borken = 1;
+ esp_dev->sync = 0;
+ bytes_sent = 0;
+ }
+
+ /* Update the state of our transfer. */
+ SCptr->SCp.ptr += bytes_sent;
+ SCptr->SCp.this_residual -= bytes_sent;
+ if (SCptr->SCp.this_residual < 0) {
+ /* shit */
+ ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id));
+ SCptr->SCp.this_residual = 0;
+ }
+
+ /* Maybe continue. */
+ if (!bogus_data) {
+ ESPDATA(("!bogus_data, "));
+
+ /* NO MATTER WHAT, we advance the scatterlist,
+ * if the target should decide to disconnect
+ * in between scatter chunks (which is common)
+ * we could die horribly! I used to have the sg
+ * advance occur only if we are going back into
+ * (or are staying in) a data phase, you can
+ * imagine the hell I went through trying to
+ * figure this out.
+ */
+ if (SCptr->use_sg && !SCptr->SCp.this_residual)
+ advance_sg(SCptr);
+ if (sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
+ ESPDATA(("to more data\n"));
+ return esp_do_data(esp);
+ }
+ ESPDATA(("to new phase\n"));
+ return esp_do_phase_determine(esp);
+ }
+ /* Bogus data, just wait for next interrupt. */
+ ESPLOG(("esp%d: bogus_data during end of data phase\n",
+ esp->esp_id));
+ return do_intr_end;
+}
+
+/* We received a non-good status return at the end of
+ * running a SCSI command. This is used to decide if
+ * we should clear our synchronous transfer state for
+ * such a device when that happens.
+ *
+ * The idea is that when spinning up a disk or rewinding
+ * a tape, we don't want to go into a loop re-negotiating
+ * synchronous capabilities over and over.
+ */
+static int esp_should_clear_sync(struct scsi_cmnd *sp)
+{
+ u8 cmd1 = sp->cmnd[0];
+ u8 cmd2 = sp->data_cmnd[0];
+
+ /* These cases are for spinning up a disk and
+ * waiting for that spinup to complete.
+ */
+ if (cmd1 == START_STOP ||
+ cmd2 == START_STOP)
+ return 0;
+
+ if (cmd1 == TEST_UNIT_READY ||
+ cmd2 == TEST_UNIT_READY)
+ return 0;
+
+ /* One more special case for SCSI tape drives,
+ * this is what is used to probe the device for
+ * completion of a rewind or tape load operation.
+ */
+ if (sp->device->type == TYPE_TAPE) {
+ if (cmd1 == MODE_SENSE ||
+ cmd2 == MODE_SENSE)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Either a command is completing or a target is dropping off the bus
+ * to continue the command in the background so we can do other work.
+ */
+static int esp_do_freebus(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ int rval;
+
+ rval = skipahead2(esp, SCptr, in_status, in_msgindone, in_freeing);
+ if (rval)
+ return rval;
+ if (esp->ireg != ESP_INTR_DC) {
+ ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
+ return do_reset_bus; /* target will not drop BSY... */
+ }
+ esp->msgout_len = 0;
+ esp->prevmsgout = NOP;
+ if (esp->prevmsgin == COMMAND_COMPLETE) {
+ /* Normal end of nexus. */
+ if (esp->disconnected_SC || (esp->erev == fashme))
+ esp_cmd(esp, ESP_CMD_ESEL);
+
+ if (SCptr->SCp.Status != GOOD &&
+ SCptr->SCp.Status != CONDITION_GOOD &&
+ ((1<<SCptr->device->id) & esp->targets_present) &&
+ esp_dev->sync &&
+ esp_dev->sync_max_offset) {
+ /* SCSI standard says that the synchronous capabilities
+ * should be renegotiated at this point. Most likely
+ * we are about to request sense from this target
+ * in which case we want to avoid using sync
+ * transfers until we are sure of the current target
+ * state.
+ */
+ ESPMISC(("esp: Status <%d> for target %d lun %d\n",
+ SCptr->SCp.Status, SCptr->device->id, SCptr->device->lun));
+
+ /* But don't do this when spinning up a disk at
+ * boot time while we poll for completion as it
+ * fills up the console with messages. Also, tapes
+ * can report not ready many times right after
+ * loading up a tape.
+ */
+ if (esp_should_clear_sync(SCptr) != 0)
+ esp_dev->sync = 0;
+ }
+ ESPDISC(("F<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
+ esp_done(esp, ((SCptr->SCp.Status & 0xff) |
+ ((SCptr->SCp.Message & 0xff)<<8) |
+ (DID_OK << 16)));
+ } else if (esp->prevmsgin == DISCONNECT) {
+ /* Normal disconnect. */
+ esp_cmd(esp, ESP_CMD_ESEL);
+ ESPDISC(("D<%02x,%02x>", SCptr->device->id, SCptr->device->lun));
+ append_SC(&esp->disconnected_SC, SCptr);
+ esp->current_SC = NULL;
+ if (esp->issue_SC)
+ esp_exec_cmd(esp);
+ } else {
+ /* Driver bug, we do not expect a disconnect here
+ * and should not have advanced the state engine
+ * to in_freeing.
+ */
+ ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
+ esp->esp_id));
+ return do_reset_bus;
+ }
+ return do_intr_end;
+}
+
+/* When a reselect occurs, and we cannot find the command to
+ * reconnect to in our queues, we do this.
+ */
+static int esp_bad_reconnect(struct esp *esp)
+{
+ struct scsi_cmnd *sp;
+
+ ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
+ esp->esp_id));
+ ESPLOG(("QUEUE DUMP\n"));
+ sp = esp->issue_SC;
+ ESPLOG(("esp%d: issue_SC[", esp->esp_id));
+ while (sp) {
+ ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
+ sp = (struct scsi_cmnd *) sp->host_scribble;
+ }
+ ESPLOG(("]\n"));
+ sp = esp->current_SC;
+ ESPLOG(("esp%d: current_SC[", esp->esp_id));
+ if (sp)
+ ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
+ else
+ ESPLOG(("<NULL>"));
+ ESPLOG(("]\n"));
+ sp = esp->disconnected_SC;
+ ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
+ while (sp) {
+ ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun));
+ sp = (struct scsi_cmnd *) sp->host_scribble;
+ }
+ ESPLOG(("]\n"));
+ return do_reset_bus;
+}
+
+/* Do the needy when a target tries to reconnect to us. */
+static int esp_do_reconnect(struct esp *esp)
+{
+ int lun, target;
+ struct scsi_cmnd *SCptr;
+
+ /* Check for all bogus conditions first. */
+ target = reconnect_target(esp);
+ if (target < 0) {
+ ESPDISC(("bad bus bits\n"));
+ return do_reset_bus;
+ }
+ lun = reconnect_lun(esp);
+ if (lun < 0) {
+ ESPDISC(("target=%2x, bad identify msg\n", target));
+ return do_reset_bus;
+ }
+
+ /* Things look ok... */
+ ESPDISC(("R<%02x,%02x>", target, lun));
+
+ /* Must not flush FIFO or DVMA on HME. */
+ if (esp->erev != fashme) {
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ if (esp100_reconnect_hwbug(esp))
+ return do_reset_bus;
+ esp_cmd(esp, ESP_CMD_NULL);
+ }
+
+ SCptr = remove_SC(&esp->disconnected_SC, (u8) target, (u8) lun);
+ if (!SCptr)
+ return esp_bad_reconnect(esp);
+
+ esp_connect(esp, SCptr);
+ esp_cmd(esp, ESP_CMD_MOK);
+
+ if (esp->erev == fashme)
+ sbus_writeb(((SCptr->device->id & 0xf) |
+ (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)),
+ esp->eregs + ESP_BUSID);
+
+ /* Reconnect implies a restore pointers operation. */
+ esp_restore_pointers(esp, SCptr);
+
+ esp->snip = 0;
+ esp_advance_phase(SCptr, in_the_dark);
+ return do_intr_end;
+}
+
+/* End of NEXUS (hopefully), pick up status + message byte then leave if
+ * all goes well.
+ */
+static int esp_do_status(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ int intr, rval;
+
+ rval = skipahead1(esp, SCptr, in_the_dark, in_status);
+ if (rval)
+ return rval;
+ intr = esp->ireg;
+ ESPSTAT(("esp_do_status: "));
+ if (intr != ESP_INTR_DC) {
+ int message_out = 0; /* for parity problems */
+
+ /* Ack the message. */
+ ESPSTAT(("ack msg, "));
+ esp_cmd(esp, ESP_CMD_MOK);
+
+ if (esp->erev != fashme) {
+ dma_flashclear(esp);
+
+ /* Wait till the first bits settle. */
+ while (esp->esp_command[0] == 0xff)
+ udelay(1);
+ } else {
+ esp->esp_command[0] = esp->hme_fifo_workaround_buffer[0];
+ esp->esp_command[1] = esp->hme_fifo_workaround_buffer[1];
+ }
+
+ ESPSTAT(("got something, "));
+ /* ESP chimes in with one of
+ *
+ * 1) function done interrupt:
+ * both status and message in bytes
+ * are available
+ *
+ * 2) bus service interrupt:
+ * only status byte was acquired
+ *
+ * 3) Anything else:
+ * can't happen, but we test for it
+ * anyways
+ *
+ * ALSO: If bad parity was detected on either
+ * the status _or_ the message byte then
+ * the ESP has asserted ATN on the bus
+ * and we must therefore wait for the
+ * next phase change.
+ */
+ if (intr & ESP_INTR_FDONE) {
+ /* We got it all, hallejulia. */
+ ESPSTAT(("got both, "));
+ SCptr->SCp.Status = esp->esp_command[0];
+ SCptr->SCp.Message = esp->esp_command[1];
+ esp->prevmsgin = SCptr->SCp.Message;
+ esp->cur_msgin[0] = SCptr->SCp.Message;
+ if (esp->sreg & ESP_STAT_PERR) {
+ /* There was bad parity for the
+ * message byte, the status byte
+ * was ok.
+ */
+ message_out = MSG_PARITY_ERROR;
+ }
+ } else if (intr == ESP_INTR_BSERV) {
+ /* Only got status byte. */
+ ESPLOG(("esp%d: got status only, ", esp->esp_id));
+ if (!(esp->sreg & ESP_STAT_PERR)) {
+ SCptr->SCp.Status = esp->esp_command[0];
+ SCptr->SCp.Message = 0xff;
+ } else {
+ /* The status byte had bad parity.
+ * we leave the scsi_pointer Status
+ * field alone as we set it to a default
+ * of CHECK_CONDITION in esp_queue.
+ */
+ message_out = INITIATOR_ERROR;
+ }
+ } else {
+ /* This shouldn't happen ever. */
+ ESPSTAT(("got bolixed\n"));
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp);
+ }
+
+ if (!message_out) {
+ ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
+ SCptr->SCp.Message));
+ if (SCptr->SCp.Message == COMMAND_COMPLETE) {
+ ESPSTAT(("and was COMMAND_COMPLETE\n"));
+ esp_advance_phase(SCptr, in_freeing);
+ return esp_do_freebus(esp);
+ } else {
+ ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
+ esp->esp_id));
+ esp->msgin_len = esp->msgin_ctr = 1;
+ esp_advance_phase(SCptr, in_msgindone);
+ return esp_do_msgindone(esp);
+ }
+ } else {
+ /* With luck we'll be able to let the target
+ * know that bad parity happened, it will know
+ * which byte caused the problems and send it
+ * again. For the case where the status byte
+ * receives bad parity, I do not believe most
+ * targets recover very well. We'll see.
+ */
+ ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
+ esp->esp_id, message_out));
+ esp->cur_msgout[0] = message_out;
+ esp->msgout_len = esp->msgout_ctr = 1;
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp);
+ }
+ } else {
+ /* If we disconnect now, all hell breaks loose. */
+ ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp);
+ }
+}
+
+static int esp_enter_status(struct esp *esp)
+{
+ u8 thecmd = ESP_CMD_ICCSEQ;
+
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ if (esp->erev != fashme) {
+ u32 tmp;
+
+ esp->esp_command[0] = esp->esp_command[1] = 0xff;
+ sbus_writeb(2, esp->eregs + ESP_TCLOW);
+ sbus_writeb(0, esp->eregs + ESP_TCMED);
+ tmp = sbus_readl(esp->dregs + DMA_CSR);
+ tmp |= (DMA_ST_WRITE | DMA_ENABLE);
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ if (esp->dma->revision == dvmaesc1)
+ sbus_writel(0x100, esp->dregs + DMA_COUNT);
+ sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
+ thecmd |= ESP_CMD_DMA;
+ }
+ esp_cmd(esp, thecmd);
+ esp_advance_phase(esp->current_SC, in_status);
+
+ return esp_do_status(esp);
+}
+
+static int esp_disconnect_amidst_phases(struct esp *esp)
+{
+ struct scsi_cmnd *sp = esp->current_SC;
+ struct esp_device *esp_dev = sp->device->hostdata;
+
+ /* This means real problems if we see this
+ * here. Unless we were actually trying
+ * to force the device to abort/reset.
+ */
+ ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
+ ESPLOG(("pphase<%s> cphase<%s>, ",
+ phase_string(sp->SCp.phase),
+ phase_string(sp->SCp.sent_command)));
+
+ if (esp->disconnected_SC != NULL || (esp->erev == fashme))
+ esp_cmd(esp, ESP_CMD_ESEL);
+
+ switch (esp->cur_msgout[0]) {
+ default:
+ /* We didn't expect this to happen at all. */
+ ESPLOG(("device is bolixed\n"));
+ esp_advance_phase(sp, in_tgterror);
+ esp_done(esp, (DID_ERROR << 16));
+ break;
+
+ case BUS_DEVICE_RESET:
+ ESPLOG(("device reset successful\n"));
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync = 0;
+ esp_advance_phase(sp, in_resetdev);
+ esp_done(esp, (DID_RESET << 16));
+ break;
+
+ case ABORT:
+ ESPLOG(("device abort successful\n"));
+ esp_advance_phase(sp, in_abortone);
+ esp_done(esp, (DID_ABORT << 16));
+ break;
+
+ };
+ return do_intr_end;
+}
+
+static int esp_enter_msgout(struct esp *esp)
+{
+ esp_advance_phase(esp->current_SC, in_msgout);
+ return esp_do_msgout(esp);
+}
+
+static int esp_enter_msgin(struct esp *esp)
+{
+ esp_advance_phase(esp->current_SC, in_msgin);
+ return esp_do_msgin(esp);
+}
+
+static int esp_enter_cmd(struct esp *esp)
+{
+ esp_advance_phase(esp->current_SC, in_cmdbegin);
+ return esp_do_cmdbegin(esp);
+}
+
+static int esp_enter_badphase(struct esp *esp)
+{
+ ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
+ esp->sreg & ESP_STAT_PMASK));
+ return do_reset_bus;
+}
+
+typedef int (*espfunc_t)(struct esp *);
+
+static espfunc_t phase_vector[] = {
+ esp_do_data, /* ESP_DOP */
+ esp_do_data, /* ESP_DIP */
+ esp_enter_cmd, /* ESP_CMDP */
+ esp_enter_status, /* ESP_STATP */
+ esp_enter_badphase, /* ESP_STAT_PMSG */
+ esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */
+ esp_enter_msgout, /* ESP_MOP */
+ esp_enter_msgin, /* ESP_MIP */
+};
+
+/* The target has control of the bus and we have to see where it has
+ * taken us.
+ */
+static int esp_do_phase_determine(struct esp *esp)
+{
+ if ((esp->ireg & ESP_INTR_DC) != 0)
+ return esp_disconnect_amidst_phases(esp);
+ return phase_vector[esp->sreg & ESP_STAT_PMASK](esp);
+}
+
+/* First interrupt after exec'ing a cmd comes here. */
+static int esp_select_complete(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ int cmd_bytes_sent, fcnt;
+
+ if (esp->erev != fashme)
+ esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS);
+
+ if (esp->erev == fashme)
+ fcnt = esp->hme_fifo_workaround_count;
+ else
+ fcnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES);
+
+ cmd_bytes_sent = esp_bytes_sent(esp, fcnt);
+ dma_invalidate(esp);
+
+ /* Let's check to see if a reselect happened
+ * while we we're trying to select. This must
+ * be checked first.
+ */
+ if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+ esp_reconnect(esp, SCptr);
+ return esp_do_reconnect(esp);
+ }
+
+ /* Looks like things worked, we should see a bus service &
+ * a function complete interrupt at this point. Note we
+ * are doing a direct comparison because we don't want to
+ * be fooled into thinking selection was successful if
+ * ESP_INTR_DC is set, see below.
+ */
+ if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+ /* target speaks... */
+ esp->targets_present |= (1<<SCptr->device->id);
+
+ /* What if the target ignores the sdtr? */
+ if (esp->snip)
+ esp_dev->sync = 1;
+
+ /* See how far, if at all, we got in getting
+ * the information out to the target.
+ */
+ switch (esp->seqreg) {
+ default:
+
+ case ESP_STEP_ASEL:
+ /* Arbitration won, target selected, but
+ * we are in some phase which is not command
+ * phase nor is it message out phase.
+ *
+ * XXX We've confused the target, obviously.
+ * XXX So clear it's state, but we also end
+ * XXX up clearing everyone elses. That isn't
+ * XXX so nice. I'd like to just reset this
+ * XXX target, but if I cannot even get it's
+ * XXX attention and finish selection to talk
+ * XXX to it, there is not much more I can do.
+ * XXX If we have a loaded bus we're going to
+ * XXX spend the next second or so renegotiating
+ * XXX for synchronous transfers.
+ */
+ ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
+ esp->esp_id, SCptr->device->id));
+
+ case ESP_STEP_SID:
+ /* Arbitration won, target selected, went
+ * to message out phase, sent one message
+ * byte, then we stopped. ATN is asserted
+ * on the SCSI bus and the target is still
+ * there hanging on. This is a legal
+ * sequence step if we gave the ESP a select
+ * and stop command.
+ *
+ * XXX See above, I could set the borken flag
+ * XXX in the device struct and retry the
+ * XXX command. But would that help for
+ * XXX tagged capable targets?
+ */
+
+ case ESP_STEP_NCMD:
+ /* Arbitration won, target selected, maybe
+ * sent the one message byte in message out
+ * phase, but we did not go to command phase
+ * in the end. Actually, we could have sent
+ * only some of the message bytes if we tried
+ * to send out the entire identify and tag
+ * message using ESP_CMD_SA3.
+ */
+ cmd_bytes_sent = 0;
+ break;
+
+ case ESP_STEP_PPC:
+ /* No, not the powerPC pinhead. Arbitration
+ * won, all message bytes sent if we went to
+ * message out phase, went to command phase
+ * but only part of the command was sent.
+ *
+ * XXX I've seen this, but usually in conjunction
+ * XXX with a gross error which appears to have
+ * XXX occurred between the time I told the
+ * XXX ESP to arbitrate and when I got the
+ * XXX interrupt. Could I have misloaded the
+ * XXX command bytes into the fifo? Actually,
+ * XXX I most likely missed a phase, and therefore
+ * XXX went into never never land and didn't even
+ * XXX know it. That was the old driver though.
+ * XXX What is even more peculiar is that the ESP
+ * XXX showed the proper function complete and
+ * XXX bus service bits in the interrupt register.
+ */
+
+ case ESP_STEP_FINI4:
+ case ESP_STEP_FINI5:
+ case ESP_STEP_FINI6:
+ case ESP_STEP_FINI7:
+ /* Account for the identify message */
+ if (SCptr->SCp.phase == in_slct_norm)
+ cmd_bytes_sent -= 1;
+ };
+
+ if (esp->erev != fashme)
+ esp_cmd(esp, ESP_CMD_NULL);
+
+ /* Be careful, we could really get fucked during synchronous
+ * data transfers if we try to flush the fifo now.
+ */
+ if ((esp->erev != fashme) && /* not a Happy Meal and... */
+ !fcnt && /* Fifo is empty and... */
+ /* either we are not doing synchronous transfers or... */
+ (!esp_dev->sync_max_offset ||
+ /* We are not going into data in phase. */
+ ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+ esp_cmd(esp, ESP_CMD_FLUSH); /* flush is safe */
+
+ /* See how far we got if this is not a slow command. */
+ if (!esp->esp_slowcmd) {
+ if (cmd_bytes_sent < 0)
+ cmd_bytes_sent = 0;
+ if (cmd_bytes_sent != SCptr->cmd_len) {
+ /* Crapola, mark it as a slowcmd
+ * so that we have some chance of
+ * keeping the command alive with
+ * good luck.
+ *
+ * XXX Actually, if we didn't send it all
+ * XXX this means either we didn't set things
+ * XXX up properly (driver bug) or the target
+ * XXX or the ESP detected parity on one of
+ * XXX the command bytes. This makes much
+ * XXX more sense, and therefore this code
+ * XXX should be changed to send out a
+ * XXX parity error message or if the status
+ * XXX register shows no parity error then
+ * XXX just expect the target to bring the
+ * XXX bus into message in phase so that it
+ * XXX can send us the parity error message.
+ * XXX SCSI sucks...
+ */
+ esp->esp_slowcmd = 1;
+ esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
+ esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
+ }
+ }
+
+ /* Now figure out where we went. */
+ esp_advance_phase(SCptr, in_the_dark);
+ return esp_do_phase_determine(esp);
+ }
+
+ /* Did the target even make it? */
+ if (esp->ireg == ESP_INTR_DC) {
+ /* wheee... nobody there or they didn't like
+ * what we told it to do, clean up.
+ */
+
+ /* If anyone is off the bus, but working on
+ * a command in the background for us, tell
+ * the ESP to listen for them.
+ */
+ if (esp->disconnected_SC)
+ esp_cmd(esp, ESP_CMD_ESEL);
+
+ if (((1<<SCptr->device->id) & esp->targets_present) &&
+ esp->seqreg != 0 &&
+ (esp->cur_msgout[0] == EXTENDED_MESSAGE) &&
+ (SCptr->SCp.phase == in_slct_msg ||
+ SCptr->SCp.phase == in_slct_stop)) {
+ /* shit */
+ esp->snip = 0;
+ ESPLOG(("esp%d: Failed synchronous negotiation for target %d "
+ "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun));
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync = 1; /* so we don't negotiate again */
+
+ /* Run the command again, this time though we
+ * won't try to negotiate for synchronous transfers.
+ *
+ * XXX I'd like to do something like send an
+ * XXX INITIATOR_ERROR or ABORT message to the
+ * XXX target to tell it, "Sorry I confused you,
+ * XXX please come back and I will be nicer next
+ * XXX time". But that requires having the target
+ * XXX on the bus, and it has dropped BSY on us.
+ */
+ esp->current_SC = NULL;
+ esp_advance_phase(SCptr, not_issued);
+ prepend_SC(&esp->issue_SC, SCptr);
+ esp_exec_cmd(esp);
+ return do_intr_end;
+ }
+
+ /* Ok, this is normal, this is what we see during boot
+ * or whenever when we are scanning the bus for targets.
+ * But first make sure that is really what is happening.
+ */
+ if (((1<<SCptr->device->id) & esp->targets_present)) {
+ ESPLOG(("esp%d: Warning, live target %d not responding to "
+ "selection.\n", esp->esp_id, SCptr->device->id));
+
+ /* This _CAN_ happen. The SCSI standard states that
+ * the target is to _not_ respond to selection if
+ * _it_ detects bad parity on the bus for any reason.
+ * Therefore, we assume that if we've talked successfully
+ * to this target before, bad parity is the problem.
+ */
+ esp_done(esp, (DID_PARITY << 16));
+ } else {
+ /* Else, there really isn't anyone there. */
+ ESPMISC(("esp: selection failure, maybe nobody there?\n"));
+ ESPMISC(("esp: target %d lun %d\n",
+ SCptr->device->id, SCptr->device->lun));
+ esp_done(esp, (DID_BAD_TARGET << 16));
+ }
+ return do_intr_end;
+ }
+
+ ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
+ printk("esp%d: Currently -- ", esp->esp_id);
+ esp_print_ireg(esp->ireg); printk(" ");
+ esp_print_statreg(esp->sreg); printk(" ");
+ esp_print_seqreg(esp->seqreg); printk("\n");
+ printk("esp%d: New -- ", esp->esp_id);
+ esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
+ esp->seqreg = sbus_readb(esp->eregs + ESP_SSTEP);
+ esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT);
+ esp_print_ireg(esp->ireg); printk(" ");
+ esp_print_statreg(esp->sreg); printk(" ");
+ esp_print_seqreg(esp->seqreg); printk("\n");
+ ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
+ return do_reset_bus; /* ugh... */
+}
+
+/* Continue reading bytes for msgin phase. */
+static int esp_do_msgincont(struct esp *esp)
+{
+ if (esp->ireg & ESP_INTR_BSERV) {
+ /* in the right phase too? */
+ if ((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
+ /* phew... */
+ esp_cmd(esp, ESP_CMD_TI);
+ esp_advance_phase(esp->current_SC, in_msgindone);
+ return do_intr_end;
+ }
+
+ /* We changed phase but ESP shows bus service,
+ * in this case it is most likely that we, the
+ * hacker who has been up for 20hrs straight
+ * staring at the screen, drowned in coffee
+ * smelling like retched cigarette ashes
+ * have miscoded something..... so, try to
+ * recover as best we can.
+ */
+ ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id));
+ }
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return do_phase_determine;
+}
+
+static int check_singlebyte_msg(struct esp *esp)
+{
+ esp->prevmsgin = esp->cur_msgin[0];
+ if (esp->cur_msgin[0] & 0x80) {
+ /* wheee... */
+ ESPLOG(("esp%d: target sends identify amidst phases\n",
+ esp->esp_id));
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return 0;
+ } else if (((esp->cur_msgin[0] & 0xf0) == 0x20) ||
+ (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
+ esp->msgin_len = 2;
+ esp_advance_phase(esp->current_SC, in_msgincont);
+ return 0;
+ }
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ switch (esp->cur_msgin[0]) {
+ default:
+ /* We don't want to hear about it. */
+ ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id,
+ esp->cur_msgin[0]));
+ return MESSAGE_REJECT;
+
+ case NOP:
+ ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
+ esp->current_SC->device->id));
+ return 0;
+
+ case RESTORE_POINTERS:
+ /* In this case we might also have to backup the
+ * "slow command" pointer. It is rare to get such
+ * a save/restore pointer sequence so early in the
+ * bus transition sequences, but cover it.
+ */
+ if (esp->esp_slowcmd) {
+ esp->esp_scmdleft = esp->current_SC->cmd_len;
+ esp->esp_scmdp = &esp->current_SC->cmnd[0];
+ }
+ esp_restore_pointers(esp, esp->current_SC);
+ return 0;
+
+ case SAVE_POINTERS:
+ esp_save_pointers(esp, esp->current_SC);
+ return 0;
+
+ case COMMAND_COMPLETE:
+ case DISCONNECT:
+ /* Freeing the bus, let it go. */
+ esp->current_SC->SCp.phase = in_freeing;
+ return 0;
+
+ case MESSAGE_REJECT:
+ ESPMISC(("msg reject, "));
+ if (esp->prevmsgout == EXTENDED_MESSAGE) {
+ struct esp_device *esp_dev = esp->current_SC->device->hostdata;
+
+ /* Doesn't look like this target can
+ * do synchronous or WIDE transfers.
+ */
+ ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n"));
+ esp_dev->sync = 1;
+ esp_dev->wide = 1;
+ esp_dev->sync_min_period = 0;
+ esp_dev->sync_max_offset = 0;
+ return 0;
+ } else {
+ ESPMISC(("not sync nego, sending ABORT\n"));
+ return ABORT;
+ }
+ };
+}
+
+/* Target negotiates for synchronous transfers before we do, this
+ * is legal although very strange. What is even funnier is that
+ * the SCSI2 standard specifically recommends against targets doing
+ * this because so many initiators cannot cope with this occurring.
+ */
+static int target_with_ants_in_pants(struct esp *esp,
+ struct scsi_cmnd *SCptr,
+ struct esp_device *esp_dev)
+{
+ if (esp_dev->sync || SCptr->device->borken) {
+ /* sorry, no can do */
+ ESPSDTR(("forcing to async, "));
+ build_sync_nego_msg(esp, 0, 0);
+ esp_dev->sync = 1;
+ esp->snip = 1;
+ ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
+ esp_advance_phase(SCptr, in_the_dark);
+ return EXTENDED_MESSAGE;
+ }
+
+ /* Ok, we'll check them out... */
+ return 0;
+}
+
+static void sync_report(struct esp *esp)
+{
+ int msg3, msg4;
+ char *type;
+
+ msg3 = esp->cur_msgin[3];
+ msg4 = esp->cur_msgin[4];
+ if (msg4) {
+ int hz = 1000000000 / (msg3 * 4);
+ int integer = hz / 1000000;
+ int fraction = (hz - (integer * 1000000)) / 10000;
+ if ((esp->erev == fashme) &&
+ (esp->config3[esp->current_SC->device->id] & ESP_CONFIG3_EWIDE)) {
+ type = "FAST-WIDE";
+ integer <<= 1;
+ fraction <<= 1;
+ } else if ((msg3 * 4) < 200) {
+ type = "FAST";
+ } else {
+ type = "synchronous";
+ }
+
+ /* Do not transform this back into one big printk
+ * again, it triggers a bug in our sparc64-gcc272
+ * sibling call optimization. -DaveM
+ */
+ ESPLOG((KERN_INFO "esp%d: target %d ",
+ esp->esp_id, esp->current_SC->device->id));
+ ESPLOG(("[period %dns offset %d %d.%02dMHz ",
+ (int) msg3 * 4, (int) msg4,
+ integer, fraction));
+ ESPLOG(("%s SCSI%s]\n", type,
+ (((msg3 * 4) < 200) ? "-II" : "")));
+ } else {
+ ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n",
+ esp->esp_id, esp->current_SC->device->id));
+ }
+}
+
+static int check_multibyte_msg(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ struct esp_device *esp_dev = SCptr->device->hostdata;
+ u8 regval = 0;
+ int message_out = 0;
+
+ ESPSDTR(("chk multibyte msg: "));
+ if (esp->cur_msgin[2] == EXTENDED_SDTR) {
+ int period = esp->cur_msgin[3];
+ int offset = esp->cur_msgin[4];
+
+ ESPSDTR(("is sync nego response, "));
+ if (!esp->snip) {
+ int rval;
+
+ /* Target negotiates first! */
+ ESPSDTR(("target jumps the gun, "));
+ message_out = EXTENDED_MESSAGE; /* we must respond */
+ rval = target_with_ants_in_pants(esp, SCptr, esp_dev);
+ if (rval)
+ return rval;
+ }
+
+ ESPSDTR(("examining sdtr, "));
+
+ /* Offset cannot be larger than ESP fifo size. */
+ if (offset > 15) {
+ ESPSDTR(("offset too big %2x, ", offset));
+ offset = 15;
+ ESPSDTR(("sending back new offset\n"));
+ build_sync_nego_msg(esp, period, offset);
+ return EXTENDED_MESSAGE;
+ }
+
+ if (offset && period > esp->max_period) {
+ /* Yeee, async for this slow device. */
+ ESPSDTR(("period too long %2x, ", period));
+ build_sync_nego_msg(esp, 0, 0);
+ ESPSDTR(("hoping for msgout\n"));
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return EXTENDED_MESSAGE;
+ } else if (offset && period < esp->min_period) {
+ ESPSDTR(("period too short %2x, ", period));
+ period = esp->min_period;
+ if (esp->erev > esp236)
+ regval = 4;
+ else
+ regval = 5;
+ } else if (offset) {
+ int tmp;
+
+ ESPSDTR(("period is ok, "));
+ tmp = esp->ccycle / 1000;
+ regval = (((period << 2) + tmp - 1) / tmp);
+ if (regval && ((esp->erev == fas100a ||
+ esp->erev == fas236 ||
+ esp->erev == fashme))) {
+ if (period >= 50)
+ regval--;
+ }
+ }
+
+ if (offset) {
+ u8 bit;
+
+ esp_dev->sync_min_period = (regval & 0x1f);
+ esp_dev->sync_max_offset = (offset | esp->radelay);
+ if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) {
+ if ((esp->erev == fas100a) || (esp->erev == fashme))
+ bit = ESP_CONFIG3_FAST;
+ else
+ bit = ESP_CONFIG3_FSCSI;
+ if (period < 50) {
+ /* On FAS366, if using fast-20 synchronous transfers
+ * we need to make sure the REQ/ACK assert/deassert
+ * control bits are clear.
+ */
+ if (esp->erev == fashme)
+ esp_dev->sync_max_offset &= ~esp->radelay;
+ esp->config3[SCptr->device->id] |= bit;
+ } else {
+ esp->config3[SCptr->device->id] &= ~bit;
+ }
+ esp->prev_cfg3 = esp->config3[SCptr->device->id];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ }
+ esp->prev_soff = esp_dev->sync_max_offset;
+ esp->prev_stp = esp_dev->sync_min_period;
+ sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
+ sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
+ ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
+ esp_dev->sync_max_offset,
+ esp_dev->sync_min_period,
+ esp->config3[SCptr->device->id]));
+
+ esp->snip = 0;
+ } else if (esp_dev->sync_max_offset) {
+ u8 bit;
+
+ /* back to async mode */
+ ESPSDTR(("unaccaptable sync nego, forcing async\n"));
+ esp_dev->sync_max_offset = 0;
+ esp_dev->sync_min_period = 0;
+ esp->prev_soff = 0;
+ esp->prev_stp = 0;
+ sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF);
+ sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP);
+ if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) {
+ if ((esp->erev == fas100a) || (esp->erev == fashme))
+ bit = ESP_CONFIG3_FAST;
+ else
+ bit = ESP_CONFIG3_FSCSI;
+ esp->config3[SCptr->device->id] &= ~bit;
+ esp->prev_cfg3 = esp->config3[SCptr->device->id];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+ }
+ }
+
+ sync_report(esp);
+
+ ESPSDTR(("chk multibyte msg: sync is known, "));
+ esp_dev->sync = 1;
+
+ if (message_out) {
+ ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
+ esp->esp_id));
+ build_sync_nego_msg(esp, period, offset);
+ esp_advance_phase(SCptr, in_the_dark);
+ return EXTENDED_MESSAGE;
+ }
+
+ ESPSDTR(("returning zero\n"));
+ esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
+ return 0;
+ } else if (esp->cur_msgin[2] == EXTENDED_WDTR) {
+ int size = 8 << esp->cur_msgin[3];
+
+ esp->wnip = 0;
+ if (esp->erev != fashme) {
+ ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n",
+ esp->esp_id));
+ message_out = MESSAGE_REJECT;
+ } else if (size > 16) {
+ ESPLOG(("esp%d: AIEEE wide transfer for %d size "
+ "not supported.\n", esp->esp_id, size));
+ message_out = MESSAGE_REJECT;
+ } else {
+ /* Things look good; let's see what we got. */
+ if (size == 16) {
+ /* Set config 3 register for this target. */
+ esp->config3[SCptr->device->id] |= ESP_CONFIG3_EWIDE;
+ } else {
+ /* Just make sure it was one byte sized. */
+ if (size != 8) {
+ ESPLOG(("esp%d: Aieee, wide nego of %d size.\n",
+ esp->esp_id, size));
+ message_out = MESSAGE_REJECT;
+ goto finish;
+ }
+ /* Pure paranoia. */
+ esp->config3[SCptr->device->id] &= ~(ESP_CONFIG3_EWIDE);
+ }
+ esp->prev_cfg3 = esp->config3[SCptr->device->id];
+ sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3);
+
+ /* Regardless, next try for sync transfers. */
+ build_sync_nego_msg(esp, esp->sync_defp, 15);
+ esp_dev->sync = 1;
+ esp->snip = 1;
+ message_out = EXTENDED_MESSAGE;
+ }
+ } else if (esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
+ ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
+ message_out = MESSAGE_REJECT;
+ }
+finish:
+ esp_advance_phase(SCptr, in_the_dark);
+ return message_out;
+}
+
+static int esp_do_msgindone(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ int message_out = 0, it = 0, rval;
+
+ rval = skipahead1(esp, SCptr, in_msgin, in_msgindone);
+ if (rval)
+ return rval;
+ if (SCptr->SCp.sent_command != in_status) {
+ if (!(esp->ireg & ESP_INTR_DC)) {
+ if (esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
+ message_out = MSG_PARITY_ERROR;
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ } else if (esp->erev != fashme &&
+ (it = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES)) != 1) {
+ /* We certainly dropped the ball somewhere. */
+ message_out = INITIATOR_ERROR;
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ } else if (!esp->msgin_len) {
+ if (esp->erev == fashme)
+ it = esp->hme_fifo_workaround_buffer[0];
+ else
+ it = sbus_readb(esp->eregs + ESP_FDATA);
+ esp_advance_phase(SCptr, in_msgincont);
+ } else {
+ /* it is ok and we want it */
+ if (esp->erev == fashme)
+ it = esp->cur_msgin[esp->msgin_ctr] =
+ esp->hme_fifo_workaround_buffer[0];
+ else
+ it = esp->cur_msgin[esp->msgin_ctr] =
+ sbus_readb(esp->eregs + ESP_FDATA);
+ esp->msgin_ctr++;
+ }
+ } else {
+ esp_advance_phase(SCptr, in_the_dark);
+ return do_work_bus;
+ }
+ } else {
+ it = esp->cur_msgin[0];
+ }
+ if (!message_out && esp->msgin_len) {
+ if (esp->msgin_ctr < esp->msgin_len) {
+ esp_advance_phase(SCptr, in_msgincont);
+ } else if (esp->msgin_len == 1) {
+ message_out = check_singlebyte_msg(esp);
+ } else if (esp->msgin_len == 2) {
+ if (esp->cur_msgin[0] == EXTENDED_MESSAGE) {
+ if ((it + 2) >= 15) {
+ message_out = MESSAGE_REJECT;
+ } else {
+ esp->msgin_len = (it + 2);
+ esp_advance_phase(SCptr, in_msgincont);
+ }
+ } else {
+ message_out = MESSAGE_REJECT; /* foo on you */
+ }
+ } else {
+ message_out = check_multibyte_msg(esp);
+ }
+ }
+ if (message_out < 0) {
+ return -message_out;
+ } else if (message_out) {
+ if (((message_out != 1) &&
+ ((message_out < 0x20) || (message_out & 0x80))))
+ esp->msgout_len = 1;
+ esp->cur_msgout[0] = message_out;
+ esp_cmd(esp, ESP_CMD_SATN);
+ esp_advance_phase(SCptr, in_the_dark);
+ esp->msgin_len = 0;
+ }
+ esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
+ esp->sreg &= ~(ESP_STAT_INTR);
+ if ((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
+ esp_cmd(esp, ESP_CMD_MOK);
+ if ((SCptr->SCp.sent_command == in_msgindone) &&
+ (SCptr->SCp.phase == in_freeing))
+ return esp_do_freebus(esp);
+ return do_intr_end;
+}
+
+static int esp_do_cmdbegin(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+
+ esp_advance_phase(SCptr, in_cmdend);
+ if (esp->erev == fashme) {
+ u32 tmp = sbus_readl(esp->dregs + DMA_CSR);
+ int i;
+
+ for (i = 0; i < esp->esp_scmdleft; i++)
+ esp->esp_command[i] = *esp->esp_scmdp++;
+ esp->esp_scmdleft = 0;
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ esp_setcount(esp->eregs, i, 1);
+ esp_cmd(esp, (ESP_CMD_DMA | ESP_CMD_TI));
+ tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
+ tmp &= ~(DMA_ST_WRITE);
+ sbus_writel(i, esp->dregs + DMA_COUNT);
+ sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR);
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ } else {
+ u8 tmp;
+
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ tmp = *esp->esp_scmdp++;
+ esp->esp_scmdleft--;
+ sbus_writeb(tmp, esp->eregs + ESP_FDATA);
+ esp_cmd(esp, ESP_CMD_TI);
+ }
+ return do_intr_end;
+}
+
+static int esp_do_cmddone(struct esp *esp)
+{
+ if (esp->erev == fashme)
+ dma_invalidate(esp);
+ else
+ esp_cmd(esp, ESP_CMD_NULL);
+
+ if (esp->ireg & ESP_INTR_BSERV) {
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return esp_do_phase_determine(esp);
+ }
+
+ ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
+ esp->esp_id));
+ return do_reset_bus;
+}
+
+static int esp_do_msgout(struct esp *esp)
+{
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ switch (esp->msgout_len) {
+ case 1:
+ if (esp->erev == fashme)
+ hme_fifo_push(esp, &esp->cur_msgout[0], 1);
+ else
+ sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA);
+
+ esp_cmd(esp, ESP_CMD_TI);
+ break;
+
+ case 2:
+ esp->esp_command[0] = esp->cur_msgout[0];
+ esp->esp_command[1] = esp->cur_msgout[1];
+
+ if (esp->erev == fashme) {
+ hme_fifo_push(esp, &esp->cur_msgout[0], 2);
+ esp_cmd(esp, ESP_CMD_TI);
+ } else {
+ dma_setup(esp, esp->esp_command_dvma, 2, 0);
+ esp_setcount(esp->eregs, 2, 0);
+ esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ break;
+
+ case 4:
+ esp->esp_command[0] = esp->cur_msgout[0];
+ esp->esp_command[1] = esp->cur_msgout[1];
+ esp->esp_command[2] = esp->cur_msgout[2];
+ esp->esp_command[3] = esp->cur_msgout[3];
+ esp->snip = 1;
+
+ if (esp->erev == fashme) {
+ hme_fifo_push(esp, &esp->cur_msgout[0], 4);
+ esp_cmd(esp, ESP_CMD_TI);
+ } else {
+ dma_setup(esp, esp->esp_command_dvma, 4, 0);
+ esp_setcount(esp->eregs, 4, 0);
+ esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ break;
+
+ case 5:
+ esp->esp_command[0] = esp->cur_msgout[0];
+ esp->esp_command[1] = esp->cur_msgout[1];
+ esp->esp_command[2] = esp->cur_msgout[2];
+ esp->esp_command[3] = esp->cur_msgout[3];
+ esp->esp_command[4] = esp->cur_msgout[4];
+ esp->snip = 1;
+
+ if (esp->erev == fashme) {
+ hme_fifo_push(esp, &esp->cur_msgout[0], 5);
+ esp_cmd(esp, ESP_CMD_TI);
+ } else {
+ dma_setup(esp, esp->esp_command_dvma, 5, 0);
+ esp_setcount(esp->eregs, 5, 0);
+ esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI);
+ }
+ break;
+
+ default:
+ /* whoops */
+ ESPMISC(("bogus msgout sending NOP\n"));
+ esp->cur_msgout[0] = NOP;
+
+ if (esp->erev == fashme) {
+ hme_fifo_push(esp, &esp->cur_msgout[0], 1);
+ } else {
+ sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA);
+ }
+
+ esp->msgout_len = 1;
+ esp_cmd(esp, ESP_CMD_TI);
+ break;
+ };
+
+ esp_advance_phase(esp->current_SC, in_msgoutdone);
+ return do_intr_end;
+}
+
+static int esp_do_msgoutdone(struct esp *esp)
+{
+ if (esp->msgout_len > 1) {
+ /* XXX HME/FAS ATN deassert workaround required,
+ * XXX no DMA flushing, only possible ESP_CMD_FLUSH
+ * XXX to kill the fifo.
+ */
+ if (esp->erev != fashme) {
+ u32 tmp;
+
+ while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ)
+ udelay(1);
+ tmp &= ~DMA_ENABLE;
+ sbus_writel(tmp, esp->dregs + DMA_CSR);
+ dma_invalidate(esp);
+ } else {
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ }
+ }
+ if (!(esp->ireg & ESP_INTR_DC)) {
+ if (esp->erev != fashme)
+ esp_cmd(esp, ESP_CMD_NULL);
+ switch (esp->sreg & ESP_STAT_PMASK) {
+ case ESP_MOP:
+ /* whoops, parity error */
+ ESPLOG(("esp%d: still in msgout, parity error assumed\n",
+ esp->esp_id));
+ if (esp->msgout_len > 1)
+ esp_cmd(esp, ESP_CMD_SATN);
+ esp_advance_phase(esp->current_SC, in_msgout);
+ return do_work_bus;
+
+ case ESP_DIP:
+ break;
+
+ default:
+ /* Happy Meal fifo is touchy... */
+ if ((esp->erev != fashme) &&
+ !fcount(esp) &&
+ !(((struct esp_device *)esp->current_SC->device->hostdata)->sync_max_offset))
+ esp_cmd(esp, ESP_CMD_FLUSH);
+ break;
+
+ };
+ } else {
+ ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id));
+ return do_reset_bus;
+ }
+
+ /* If we sent out a synchronous negotiation message, update
+ * our state.
+ */
+ if (esp->cur_msgout[2] == EXTENDED_MESSAGE &&
+ esp->cur_msgout[4] == EXTENDED_SDTR) {
+ esp->snip = 1; /* anal retentiveness... */
+ }
+
+ esp->prevmsgout = esp->cur_msgout[0];
+ esp->msgout_len = 0;
+ esp_advance_phase(esp->current_SC, in_the_dark);
+ return esp_do_phase_determine(esp);
+}
+
+static int esp_bus_unexpected(struct esp *esp)
+{
+ ESPLOG(("esp%d: command in weird state %2x\n",
+ esp->esp_id, esp->current_SC->SCp.phase));
+ return do_reset_bus;
+}
+
+static espfunc_t bus_vector[] = {
+ esp_do_data_finale,
+ esp_do_data_finale,
+ esp_bus_unexpected,
+ esp_do_msgin,
+ esp_do_msgincont,
+ esp_do_msgindone,
+ esp_do_msgout,
+ esp_do_msgoutdone,
+ esp_do_cmdbegin,
+ esp_do_cmddone,
+ esp_do_status,
+ esp_do_freebus,
+ esp_do_phase_determine,
+ esp_bus_unexpected,
+ esp_bus_unexpected,
+ esp_bus_unexpected,
+};
+
+/* This is the second tier in our dual-level SCSI state machine. */
+static int esp_work_bus(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr = esp->current_SC;
+ unsigned int phase;
+
+ ESPBUS(("esp_work_bus: "));
+ if (!SCptr) {
+ ESPBUS(("reconnect\n"));
+ return esp_do_reconnect(esp);
+ }
+ phase = SCptr->SCp.phase;
+ if ((phase & 0xf0) == in_phases_mask)
+ return bus_vector[(phase & 0x0f)](esp);
+ else if ((phase & 0xf0) == in_slct_mask)
+ return esp_select_complete(esp);
+ else
+ return esp_bus_unexpected(esp);
+}
+
+static espfunc_t isvc_vector[] = {
+ 0,
+ esp_do_phase_determine,
+ esp_do_resetbus,
+ esp_finish_reset,
+ esp_work_bus
+};
+
+/* Main interrupt handler for an esp adapter. */
+static void esp_handle(struct esp *esp)
+{
+ struct scsi_cmnd *SCptr;
+ int what_next = do_intr_end;
+
+ SCptr = esp->current_SC;
+
+ /* Check for errors. */
+ esp->sreg = sbus_readb(esp->eregs + ESP_STATUS);
+ esp->sreg &= (~ESP_STAT_INTR);
+ if (esp->erev == fashme) {
+ esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2);
+ esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS);
+ }
+
+ if (esp->sreg & (ESP_STAT_SPAM)) {
+ /* Gross error, could be due to one of:
+ *
+ * - top of fifo overwritten, could be because
+ * we tried to do a synchronous transfer with
+ * an offset greater than ESP fifo size
+ *
+ * - top of command register overwritten
+ *
+ * - DMA setup to go in one direction, SCSI
+ * bus points in the other, whoops
+ *
+ * - weird phase change during asynchronous
+ * data phase while we are initiator
+ */
+ ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
+
+ /* If a command is live on the bus we cannot safely
+ * reset the bus, so we'll just let the pieces fall
+ * where they may. Here we are hoping that the
+ * target will be able to cleanly go away soon
+ * so we can safely reset things.
+ */
+ if (!SCptr) {
+ ESPLOG(("esp%d: No current cmd during gross error, "
+ "resetting bus\n", esp->esp_id));
+ what_next = do_reset_bus;
+ goto state_machine;
+ }
+ }
+
+ if (sbus_readl(esp->dregs + DMA_CSR) & DMA_HNDL_ERROR) {
+ /* A DMA gate array error. Here we must
+ * be seeing one of two things. Either the
+ * virtual to physical address translation
+ * on the SBUS could not occur, else the
+ * translation it did get pointed to a bogus
+ * page. Ho hum...
+ */
+ ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id,
+ sbus_readl(esp->dregs + DMA_CSR)));
+
+ /* DMA gate array itself must be reset to clear the
+ * error condition.
+ */
+ esp_reset_dma(esp);
+
+ what_next = do_reset_bus;
+ goto state_machine;
+ }
+
+ esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); /* Unlatch intr reg */
+
+ if (esp->erev == fashme) {
+ /* This chip is really losing. */
+ ESPHME(("HME["));
+
+ ESPHME(("sreg2=%02x,", esp->sreg2));
+ /* Must latch fifo before reading the interrupt
+ * register else garbage ends up in the FIFO
+ * which confuses the driver utterly.
+ */
+ if (!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+ (esp->sreg2 & ESP_STAT2_F1BYTE)) {
+ ESPHME(("fifo_workaround]"));
+ hme_fifo_read(esp);
+ } else {
+ ESPHME(("no_fifo_workaround]"));
+ }
+ }
+
+ /* No current cmd is only valid at this point when there are
+ * commands off the bus or we are trying a reset.
+ */
+ if (!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
+ /* Panic is safe, since current_SC is null. */
+ ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
+ panic("esp_handle: current_SC == penguin within interrupt!");
+ }
+
+ if (esp->ireg & (ESP_INTR_IC)) {
+ /* Illegal command fed to ESP. Outside of obvious
+ * software bugs that could cause this, there is
+ * a condition with esp100 where we can confuse the
+ * ESP into an erroneous illegal command interrupt
+ * because it does not scrape the FIFO properly
+ * for reselection. See esp100_reconnect_hwbug()
+ * to see how we try very hard to avoid this.
+ */
+ ESPLOG(("esp%d: invalid command\n", esp->esp_id));
+
+ esp_dump_state(esp);
+
+ if (SCptr != NULL) {
+ /* Devices with very buggy firmware can drop BSY
+ * during a scatter list interrupt when using sync
+ * mode transfers. We continue the transfer as
+ * expected, the target drops the bus, the ESP
+ * gets confused, and we get a illegal command
+ * interrupt because the bus is in the disconnected
+ * state now and ESP_CMD_TI is only allowed when
+ * a nexus is alive on the bus.
+ */
+ ESPLOG(("esp%d: Forcing async and disabling disconnect for "
+ "target %d\n", esp->esp_id, SCptr->device->id));
+ SCptr->device->borken = 1; /* foo on you */
+ }
+
+ what_next = do_reset_bus;
+ } else if (!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
+ if (SCptr) {
+ unsigned int phase = SCptr->SCp.phase;
+
+ if (phase & in_phases_mask) {
+ what_next = esp_work_bus(esp);
+ } else if (phase & in_slct_mask) {
+ what_next = esp_select_complete(esp);
+ } else {
+ ESPLOG(("esp%d: interrupt for no good reason...\n",
+ esp->esp_id));
+ what_next = do_intr_end;
+ }
+ } else {
+ ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ }
+ } else if (esp->ireg & ESP_INTR_SR) {
+ ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
+ what_next = do_reset_complete;
+ } else if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
+ ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ } else if (esp->ireg & ESP_INTR_RSEL) {
+ if (SCptr == NULL) {
+ /* This is ok. */
+ what_next = esp_do_reconnect(esp);
+ } else if (SCptr->SCp.phase & in_slct_mask) {
+ /* Only selection code knows how to clean
+ * up properly.
+ */
+ ESPDISC(("Reselected during selection attempt\n"));
+ what_next = esp_select_complete(esp);
+ } else {
+ ESPLOG(("esp%d: Reselected while bus is busy\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ }
+ }
+
+ /* This is tier-one in our dual level SCSI state machine. */
+state_machine:
+ while (what_next != do_intr_end) {
+ if (what_next >= do_phase_determine &&
+ what_next < do_intr_end) {
+ what_next = isvc_vector[what_next](esp);
+ } else {
+ /* state is completely lost ;-( */
+ ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
+ esp->esp_id));
+ what_next = do_reset_bus;
+ }
+ }
+}
+
+/* Service only the ESP described by dev_id. */
+static irqreturn_t esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ struct esp *esp = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(esp->ehost->host_lock, flags);
+ if (ESP_IRQ_P(esp->dregs)) {
+ ESP_INTSOFF(esp->dregs);
+
+ ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id));
+ esp_handle(esp);
+ ESPIRQ((")"));
+
+ ESP_INTSON(esp->dregs);
+ }
+ spin_unlock_irqrestore(esp->ehost->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int esp_slave_alloc(struct scsi_device *SDptr)
+{
+ struct esp_device *esp_dev =
+ kmalloc(sizeof(struct esp_device), GFP_ATOMIC);
+
+ if (!esp_dev)
+ return -ENOMEM;
+ memset(esp_dev, 0, sizeof(struct esp_device));
+ SDptr->hostdata = esp_dev;
+ return 0;
+}
+
+static void esp_slave_destroy(struct scsi_device *SDptr)
+{
+ struct esp *esp = (struct esp *) SDptr->host->hostdata;
+
+ esp->targets_present &= ~(1 << SDptr->id);
+ kfree(SDptr->hostdata);
+ SDptr->hostdata = NULL;
+}
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "esp",
+ .proc_info = esp_proc_info,
+ .name = "Sun ESP 100/100a/200",
+ .detect = esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = esp_release,
+ .info = esp_info,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/esp.h b/drivers/scsi/esp.h
new file mode 100644
index 000000000000..73f7d6968ab6
--- /dev/null
+++ b/drivers/scsi/esp.h
@@ -0,0 +1,410 @@
+/* $Id: esp.h,v 1.29 2001/12/11 04:55:47 davem Exp $
+ * esp.h: Defines and structures for the Sparc ESP (Enhanced SCSI
+ * Processor) driver under Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_ESP_H
+#define _SPARC_ESP_H
+
+/* For dvma controller register definitions. */
+#include <asm/dma.h>
+
+/* The ESP SCSI controllers have their register sets in three
+ * "classes":
+ *
+ * 1) Registers which are both read and write.
+ * 2) Registers which are read only.
+ * 3) Registers which are write only.
+ *
+ * Yet, they all live within the same IO space.
+ */
+
+/* All the ESP registers are one byte each and are accessed longwords
+ * apart with a big-endian ordering to the bytes.
+ */
+ /* Access Description Offset */
+#define ESP_TCLOW 0x00UL /* rw Low bits of the transfer count 0x00 */
+#define ESP_TCMED 0x04UL /* rw Mid bits of the transfer count 0x04 */
+#define ESP_FDATA 0x08UL /* rw FIFO data bits 0x08 */
+#define ESP_CMD 0x0cUL /* rw SCSI command bits 0x0c */
+#define ESP_STATUS 0x10UL /* ro ESP status register 0x10 */
+#define ESP_BUSID ESP_STATUS /* wo Bus ID for select/reselect 0x10 */
+#define ESP_INTRPT 0x14UL /* ro Kind of interrupt 0x14 */
+#define ESP_TIMEO ESP_INTRPT /* wo Timeout value for select/resel 0x14 */
+#define ESP_SSTEP 0x18UL /* ro Sequence step register 0x18 */
+#define ESP_STP ESP_SSTEP /* wo Transfer period per sync 0x18 */
+#define ESP_FFLAGS 0x1cUL /* ro Bits of current FIFO info 0x1c */
+#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */
+#define ESP_CFG1 0x20UL /* rw First configuration register 0x20 */
+#define ESP_CFACT 0x24UL /* wo Clock conversion factor 0x24 */
+#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */
+#define ESP_CTEST 0x28UL /* wo Chip test register 0x28 */
+#define ESP_CFG2 0x2cUL /* rw Second configuration register 0x2c */
+#define ESP_CFG3 0x30UL /* rw Third configuration register 0x30 */
+#define ESP_TCHI 0x38UL /* rw High bits of transfer count 0x38 */
+#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */
+#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */
+#define ESP_FGRND 0x3cUL /* rw Data base for fifo 0x3c */
+#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */
+#define ESP_REG_SIZE 0x40UL
+
+/* Various revisions of the ESP board. */
+enum esp_rev {
+ esp100 = 0x00, /* NCR53C90 - very broken */
+ esp100a = 0x01, /* NCR53C90A */
+ esp236 = 0x02,
+ fas236 = 0x03,
+ fas100a = 0x04,
+ fast = 0x05,
+ fashme = 0x06,
+ espunknown = 0x07
+};
+
+/* We allocate one of these for each scsi device and attach it to
+ * SDptr->hostdata for use in the driver
+ */
+struct esp_device {
+ unsigned char sync_min_period;
+ unsigned char sync_max_offset;
+ unsigned sync:1;
+ unsigned wide:1;
+ unsigned disconnect:1;
+};
+
+struct scsi_cmnd;
+
+/* We get one of these for each ESP probed. */
+struct esp {
+ void __iomem *eregs; /* ESP controller registers */
+ void __iomem *dregs; /* DMA controller registers */
+ struct sbus_dma *dma; /* DMA controller sw state */
+ struct Scsi_Host *ehost; /* Backpointer to SCSI Host */
+ struct sbus_dev *sdev; /* Pointer to SBus entry */
+
+ /* ESP Configuration Registers */
+ u8 config1; /* Copy of the 1st config register */
+ u8 config2; /* Copy of the 2nd config register */
+ u8 config3[16]; /* Copy of the 3rd config register */
+
+ /* The current command we are sending to the ESP chip. This esp_command
+ * ptr needs to be mapped in DVMA area so we can send commands and read
+ * from the ESP fifo without burning precious CPU cycles. Programmed I/O
+ * sucks when we have the DVMA to do it for us. The ESP is stupid and will
+ * only send out 6, 10, and 12 byte SCSI commands, others we need to send
+ * one byte at a time. esp_slowcmd being set says that we are doing one
+ * of the command types ESP doesn't understand, esp_scmdp keeps track of
+ * which byte we are sending, esp_scmdleft says how many bytes to go.
+ */
+ volatile u8 *esp_command; /* Location of command (CPU view) */
+ __u32 esp_command_dvma;/* Location of command (DVMA view) */
+ unsigned char esp_clen; /* Length of this command */
+ unsigned char esp_slowcmd;
+ unsigned char *esp_scmdp;
+ unsigned char esp_scmdleft;
+
+ /* The following are used to determine the cause of an IRQ. Upon every
+ * IRQ entry we synchronize these with the hardware registers.
+ */
+ u8 ireg; /* Copy of ESP interrupt register */
+ u8 sreg; /* Copy of ESP status register */
+ u8 seqreg; /* Copy of ESP sequence step register */
+ u8 sreg2; /* Copy of HME status2 register */
+
+ /* To save register writes to the ESP, which can be expensive, we
+ * keep track of the previous value that various registers had for
+ * the last target we connected to. If they are the same for the
+ * current target, we skip the register writes as they are not needed.
+ */
+ u8 prev_soff, prev_stp;
+ u8 prev_cfg3, __cache_pad;
+
+ /* We also keep a cache of the previous FAS/HME DMA CSR register value. */
+ u32 prev_hme_dmacsr;
+
+ /* The HME is the biggest piece of shit I have ever seen. */
+ u8 hme_fifo_workaround_buffer[16 * 2];
+ u8 hme_fifo_workaround_count;
+
+ /* For each target we keep track of save/restore data
+ * pointer information. This needs to be updated majorly
+ * when we add support for tagged queueing. -DaveM
+ */
+ struct esp_pointers {
+ char *saved_ptr;
+ struct scatterlist *saved_buffer;
+ int saved_this_residual;
+ int saved_buffers_residual;
+ } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/;
+
+ /* Clock periods, frequencies, synchronization, etc. */
+ unsigned int cfreq; /* Clock frequency in HZ */
+ unsigned int cfact; /* Clock conversion factor */
+ unsigned int raw_cfact; /* Raw copy from probing */
+ unsigned int ccycle; /* One ESP clock cycle */
+ unsigned int ctick; /* One ESP clock time */
+ unsigned int radelay; /* FAST chip req/ack delay */
+ unsigned int neg_defp; /* Default negotiation period */
+ unsigned int sync_defp; /* Default sync transfer period */
+ unsigned int max_period; /* longest our period can be */
+ unsigned int min_period; /* shortest period we can withstand */
+
+ struct esp *next; /* Next ESP we probed or NULL */
+ char prom_name[64]; /* Name of ESP device from prom */
+ int prom_node; /* Prom node where ESP found */
+ int esp_id; /* Unique per-ESP ID number */
+
+ /* For slow to medium speed input clock rates we shoot for 5mb/s,
+ * but for high input clock rates we try to do 10mb/s although I
+ * don't think a transfer can even run that fast with an ESP even
+ * with DMA2 scatter gather pipelining.
+ */
+#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */
+#define SYNC_DEFP_FAST 0x19 /* 10mb/s */
+
+ unsigned int snip; /* Sync. negotiation in progress */
+ unsigned int wnip; /* WIDE negotiation in progress */
+ unsigned int targets_present;/* targets spoken to before */
+
+ int current_transfer_size; /* Set at beginning of data dma */
+
+ u8 espcmdlog[32]; /* Log of current esp cmds sent. */
+ u8 espcmdent; /* Current entry in esp cmd log. */
+
+ /* Misc. info about this ESP */
+ enum esp_rev erev; /* ESP revision */
+ int irq; /* SBus IRQ for this ESP */
+ int scsi_id; /* Who am I as initiator? */
+ int scsi_id_mask; /* Bitmask of 'me'. */
+ int diff; /* Differential SCSI bus? */
+ int bursts; /* Burst sizes our DVMA supports */
+
+ /* Our command queues, only one cmd lives in the current_SC queue. */
+ struct scsi_cmnd *issue_SC; /* Commands to be issued */
+ struct scsi_cmnd *current_SC; /* Who is currently working the bus */
+ struct scsi_cmnd *disconnected_SC;/* Commands disconnected from the bus */
+
+ /* Message goo */
+ u8 cur_msgout[16];
+ u8 cur_msgin[16];
+ u8 prevmsgout, prevmsgin;
+ u8 msgout_len, msgin_len;
+ u8 msgout_ctr, msgin_ctr;
+
+ /* States that we cannot keep in the per cmd structure because they
+ * cannot be assosciated with any specific command.
+ */
+ u8 resetting_bus;
+ wait_queue_head_t reset_queue;
+};
+
+/* Bitfield meanings for the above registers. */
+
+/* ESP config reg 1, read-write, found on all ESP chips */
+#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */
+#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */
+#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */
+#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */
+#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */
+#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */
+
+/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
+#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */
+#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */
+#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */
+#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */
+#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */
+#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */
+#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */
+#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */
+#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */
+#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */
+#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */
+#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */
+#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */
+
+/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
+#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */
+#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */
+#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */
+#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */
+#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */
+#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */
+#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */
+#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */
+#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */
+#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */
+#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */
+#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */
+#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */
+#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */
+#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */
+#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */
+
+/* ESP command register read-write */
+/* Group 1 commands: These may be sent at any point in time to the ESP
+ * chip. None of them can generate interrupts 'cept
+ * the "SCSI bus reset" command if you have not disabled
+ * SCSI reset interrupts in the config1 ESP register.
+ */
+#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */
+#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */
+#define ESP_CMD_RC 0x02 /* Chip reset */
+#define ESP_CMD_RS 0x03 /* SCSI bus reset */
+
+/* Group 2 commands: ESP must be an initiator and connected to a target
+ * for these commands to work.
+ */
+#define ESP_CMD_TI 0x10 /* Transfer Information */
+#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */
+#define ESP_CMD_MOK 0x12 /* Message okie-dokie */
+#define ESP_CMD_TPAD 0x18 /* Transfer Pad */
+#define ESP_CMD_SATN 0x1a /* Set ATN */
+#define ESP_CMD_RATN 0x1b /* De-assert ATN */
+
+/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected
+ * to a target as the initiator for these commands to work.
+ */
+#define ESP_CMD_SMSG 0x20 /* Send message */
+#define ESP_CMD_SSTAT 0x21 /* Send status */
+#define ESP_CMD_SDATA 0x22 /* Send data */
+#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */
+#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */
+#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */
+#define ESP_CMD_DCNCT 0x27 /* Disconnect */
+#define ESP_CMD_RMSG 0x28 /* Receive Message */
+#define ESP_CMD_RCMD 0x29 /* Receive Command */
+#define ESP_CMD_RDATA 0x2a /* Receive Data */
+#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */
+
+/* Group 4 commands: The ESP must be in the disconnected state and must
+ * not be connected to any targets as initiator for
+ * these commands to work.
+ */
+#define ESP_CMD_RSEL 0x40 /* Reselect */
+#define ESP_CMD_SEL 0x41 /* Select w/o ATN */
+#define ESP_CMD_SELA 0x42 /* Select w/ATN */
+#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */
+#define ESP_CMD_ESEL 0x44 /* Enable selection */
+#define ESP_CMD_DSEL 0x45 /* Disable selections */
+#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */
+#define ESP_CMD_RSEL3 0x47 /* Reselect3 */
+
+/* This bit enables the ESP's DMA on the SBus */
+#define ESP_CMD_DMA 0x80 /* Do DMA? */
+
+
+/* ESP status register read-only */
+#define ESP_STAT_PIO 0x01 /* IO phase bit */
+#define ESP_STAT_PCD 0x02 /* CD phase bit */
+#define ESP_STAT_PMSG 0x04 /* MSG phase bit */
+#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */
+#define ESP_STAT_TDONE 0x08 /* Transfer Completed */
+#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */
+#define ESP_STAT_PERR 0x20 /* Parity error */
+#define ESP_STAT_SPAM 0x40 /* Real bad error */
+/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
+ * bit on other revs of the ESP.
+ */
+#define ESP_STAT_INTR 0x80 /* Interrupt */
+
+/* HME only: status 2 register */
+#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */
+#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */
+#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */
+#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */
+#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */
+#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */
+#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */
+#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */
+
+/* The status register can be masked with ESP_STAT_PMASK and compared
+ * with the following values to determine the current phase the ESP
+ * (at least thinks it) is in. For our purposes we also add our own
+ * software 'done' bit for our phase management engine.
+ */
+#define ESP_DOP (0) /* Data Out */
+#define ESP_DIP (ESP_STAT_PIO) /* Data In */
+#define ESP_CMDP (ESP_STAT_PCD) /* Command */
+#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */
+#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */
+#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
+
+/* ESP interrupt register read-only */
+#define ESP_INTR_S 0x01 /* Select w/o ATN */
+#define ESP_INTR_SATN 0x02 /* Select w/ATN */
+#define ESP_INTR_RSEL 0x04 /* Reselected */
+#define ESP_INTR_FDONE 0x08 /* Function done */
+#define ESP_INTR_BSERV 0x10 /* Bus service */
+#define ESP_INTR_DC 0x20 /* Disconnect */
+#define ESP_INTR_IC 0x40 /* Illegal command given */
+#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */
+
+/* Interrupt status macros */
+#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR))
+#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC))
+#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN))
+#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S))
+#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \
+ (ESP_SELECT_WITHOUT_ATN_IRQ(esp)))
+#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL))
+
+/* ESP sequence step register read-only */
+#define ESP_STEP_VBITS 0x07 /* Valid bits */
+#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */
+#define ESP_STEP_SID 0x01 /* One msg byte sent */
+#define ESP_STEP_NCMD 0x02 /* Was not in command phase */
+#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd
+ * bytes to be lost
+ */
+#define ESP_STEP_FINI4 0x04 /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5 0x05
+#define ESP_STEP_FINI6 0x06
+#define ESP_STEP_FINI7 0x07
+
+/* ESP chip-test register read-write */
+#define ESP_TEST_TARG 0x01 /* Target test mode */
+#define ESP_TEST_INI 0x02 /* Initiator test mode */
+#define ESP_TEST_TS 0x04 /* Tristate test mode */
+
+/* ESP unique ID register read-only, found on fas236+fas100a only */
+#define ESP_UID_F100A 0x00 /* ESP FAS100A */
+#define ESP_UID_F236 0x02 /* ESP FAS236 */
+#define ESP_UID_REV 0x07 /* ESP revision */
+#define ESP_UID_FAM 0xf8 /* ESP family */
+
+/* ESP fifo flags register read-only */
+/* Note that the following implies a 16 byte FIFO on the ESP. */
+#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */
+#define ESP_FF_SSTEP 0xe0 /* Sequence step */
+
+/* ESP clock conversion factor register write-only */
+#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */
+#define ESP_CCF_NEVER 0x01 /* Set it to this and die */
+#define ESP_CCF_F2 0x02 /* 10MHz */
+#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */
+#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */
+#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */
+#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */
+#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */
+
+/* HME only... */
+#define ESP_BUSID_RESELID 0x10
+#define ESP_BUSID_CTR32BIT 0x40
+
+#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */
+#define ESP_TIMEO_CONST 8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000))
+
+/* For our interrupt engine. */
+#define for_each_esp(esp) \
+ for((esp) = espchain; (esp); (esp) = (esp)->next)
+
+#endif /* !(_SPARC_ESP_H) */
diff --git a/drivers/scsi/fastlane.c b/drivers/scsi/fastlane.c
new file mode 100644
index 000000000000..ae47612b3614
--- /dev/null
+++ b/drivers/scsi/fastlane.c
@@ -0,0 +1,421 @@
+/* fastlane.c: Driver for Phase5's Fastlane SCSI Controller.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * This driver is based on the CyberStorm driver, hence the occasional
+ * reference to CyberStorm.
+ *
+ * Betatesting & crucial adjustments by
+ * Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
+ *
+ */
+
+/* TODO:
+ *
+ * o According to the doc from laire, it is required to reset the DMA when
+ * the transfer is done. ATM we reset DMA just before every new
+ * dma_init_(read|write).
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ * to the caches and the Sparc MMU mapping.
+ * 2) Make as few routines required outside the generic driver. A lot of the
+ * routines in this file used to be inline!
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+/* Such day has just come... */
+#if 0
+/* Let this defined unless you really need to enable DMA IRQ one day */
+#define NODMAIRQ
+#endif
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define FASTLANE_ESP_ADDR 0x1000001
+#define FASTLANE_DMA_ADDR 0x1000041
+
+
+/* The Fastlane DMA interface */
+struct fastlane_dma_registers {
+ volatile unsigned char cond_reg; /* DMA status (ro) [0x0000] */
+#define ctrl_reg cond_reg /* DMA control (wo) [0x0000] */
+ unsigned char dmapad1[0x3f];
+ volatile unsigned char clear_strobe; /* DMA clear (wo) [0x0040] */
+};
+
+
+/* DMA status bits */
+#define FASTLANE_DMA_MINT 0x80
+#define FASTLANE_DMA_IACT 0x40
+#define FASTLANE_DMA_CREQ 0x20
+
+/* DMA control bits */
+#define FASTLANE_DMA_FCODE 0xa0
+#define FASTLANE_DMA_MASK 0xf3
+#define FASTLANE_DMA_LED 0x10 /* HD led control 1 = on */
+#define FASTLANE_DMA_WRITE 0x08 /* 1 = write */
+#define FASTLANE_DMA_ENABLE 0x04 /* Enable DMA */
+#define FASTLANE_DMA_EDI 0x02 /* Enable DMA IRQ ? */
+#define FASTLANE_DMA_ESI 0x01 /* Enable SCSI IRQ */
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddr, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static void dma_irq_exit(struct NCR_ESP *esp);
+static void dma_led_off(struct NCR_ESP *esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static unsigned char ctrl_data = 0; /* Keep backup of the stuff written
+ * to ctrl_reg. Always write a copy
+ * to this register when writing to
+ * the hardware register!
+ */
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are transferred to the ESP chip
+ * via PIO.
+ */
+
+static inline void dma_clear(struct NCR_ESP *esp)
+{
+ struct fastlane_dma_registers *dregs =
+ (struct fastlane_dma_registers *) (esp->dregs);
+ unsigned long *t;
+
+ ctrl_data = (ctrl_data & FASTLANE_DMA_MASK);
+ dregs->ctrl_reg = ctrl_data;
+
+ t = (unsigned long *)(esp->edev);
+
+ dregs->clear_strobe = 0;
+ *t = 0 ;
+}
+
+/***************************************************************** Detection */
+int __init fastlane_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+
+ if ((z = zorro_find_device(ZORRO_PROD_PHASE5_BLIZZARD_1230_II_FASTLANE_Z3_CYBERSCSI_CYBERSTORM060, z))) {
+ unsigned long board = z->resource.start;
+ if (request_mem_region(board+FASTLANE_ESP_ADDR,
+ sizeof(struct ESP_regs), "NCR53C9x")) {
+ /* Check if this is really a fastlane controller. The problem
+ * is that also the cyberstorm and blizzard controllers use
+ * this ID value. Fortunately only Fastlane maps in Z3 space
+ */
+ if (board < 0x1000000) {
+ goto err_release;
+ }
+ esp = esp_allocate(tpnt, (void *)board+FASTLANE_ESP_ADDR);
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = &dma_irq_exit;
+ esp->dma_led_on = &dma_led_on;
+ esp->dma_led_off = &dma_led_off;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ /* Initialize the portBits (enable IRQs) */
+ ctrl_data = (FASTLANE_DMA_FCODE |
+#ifndef NODMAIRQ
+ FASTLANE_DMA_EDI |
+#endif
+ FASTLANE_DMA_ESI);
+
+
+ /* SCSI chip clock */
+ esp->cfreq = 40000000;
+
+
+ /* Map the physical address space into virtual kernel space */
+ address = (unsigned long)
+ z_ioremap(board, z->resource.end-board+1);
+
+ if(!address){
+ printk("Could not remap Fastlane controller memory!");
+ goto err_unregister;
+ }
+
+
+ /* The DMA registers on the Fastlane are mapped
+ * relative to the device (i.e. in the same Zorro
+ * I/O block).
+ */
+ esp->dregs = (void *)(address + FASTLANE_DMA_ADDR);
+
+ /* ESP register base */
+ esp->eregs = (struct ESP_regs *)(address + FASTLANE_ESP_ADDR);
+
+ /* Board base */
+ esp->edev = (void *) address;
+
+ /* Set the command buffer */
+ esp->esp_command = cmd_buffer;
+ esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
+
+ esp->irq = IRQ_AMIGA_PORTS;
+ esp->slot = board+FASTLANE_ESP_ADDR;
+ if (request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
+ "Fastlane SCSI", esp->ehost)) {
+ printk(KERN_WARNING "Fastlane: Could not get IRQ%d, aborting.\n", IRQ_AMIGA_PORTS);
+ goto err_unmap;
+ }
+
+ /* Controller ID */
+ esp->scsi_id = 7;
+
+ /* We don't have a differential SCSI-bus. */
+ esp->diff = 0;
+
+ dma_clear(esp);
+ esp_initialize(esp);
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ }
+ return 0;
+
+ err_unmap:
+ z_iounmap((void *)address);
+ err_unregister:
+ scsi_unregister (esp->ehost);
+ err_release:
+ release_mem_region(z->resource.start+FASTLANE_ESP_ADDR,
+ sizeof(struct ESP_regs));
+ return 0;
+}
+
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Since the Fastlane DMA is fully dedicated to the ESP chip,
+ * the number of bytes sent (to the ESP chip) equals the number
+ * of bytes in the FIFO - there is no buffering in the DMA controller.
+ * XXXX Do I read this right? It is from host to ESP, right?
+ */
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0xfffc)
+ sz = 0xfffc;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+ esp->esp_id, ((struct fastlane_dma_registers *)
+ (esp->dregs))->cond_reg));
+ ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+ custom.intreqr, custom.intenar));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct fastlane_dma_registers *dregs =
+ (struct fastlane_dma_registers *) (esp->dregs);
+ unsigned long *t;
+
+ cache_clear(addr, length);
+
+ dma_clear(esp);
+
+ t = (unsigned long *)((addr & 0x00ffffff) + esp->edev);
+
+ dregs->clear_strobe = 0;
+ *t = addr;
+
+ ctrl_data = (ctrl_data & FASTLANE_DMA_MASK) | FASTLANE_DMA_ENABLE;
+ dregs->ctrl_reg = ctrl_data;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ struct fastlane_dma_registers *dregs =
+ (struct fastlane_dma_registers *) (esp->dregs);
+ unsigned long *t;
+
+ cache_push(addr, length);
+
+ dma_clear(esp);
+
+ t = (unsigned long *)((addr & 0x00ffffff) + (esp->edev));
+
+ dregs->clear_strobe = 0;
+ *t = addr;
+
+ ctrl_data = ((ctrl_data & FASTLANE_DMA_MASK) |
+ FASTLANE_DMA_ENABLE |
+ FASTLANE_DMA_WRITE);
+ dregs->ctrl_reg = ctrl_data;
+}
+
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static void dma_irq_exit(struct NCR_ESP *esp)
+{
+ struct fastlane_dma_registers *dregs =
+ (struct fastlane_dma_registers *) (esp->dregs);
+
+ dregs->ctrl_reg = ctrl_data & ~(FASTLANE_DMA_EDI|FASTLANE_DMA_ESI);
+#ifdef __mc68000__
+ nop();
+#endif
+ dregs->ctrl_reg = ctrl_data;
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ struct fastlane_dma_registers *dregs =
+ (struct fastlane_dma_registers *) (esp->dregs);
+ unsigned char dma_status;
+
+ dma_status = dregs->cond_reg;
+
+ if(dma_status & FASTLANE_DMA_IACT)
+ return 0; /* not our IRQ */
+
+ /* Return non-zero if ESP requested IRQ */
+ return (
+#ifndef NODMAIRQ
+ (dma_status & FASTLANE_DMA_CREQ) &&
+#endif
+ (!(dma_status & FASTLANE_DMA_MINT)) &&
+ (esp_read(((struct ESP_regs *) (esp->eregs))->esp_status) & ESP_STAT_INTR));
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+ ctrl_data &= ~FASTLANE_DMA_LED;
+ ((struct fastlane_dma_registers *)(esp->dregs))->ctrl_reg = ctrl_data;
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+ ctrl_data |= FASTLANE_DMA_LED;
+ ((struct fastlane_dma_registers *)(esp->dregs))->ctrl_reg = ctrl_data;
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+#define HOSTS_C
+
+int fastlane_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ unsigned long address = (unsigned long)((struct NCR_ESP *)instance->hostdata)->edev;
+ esp_deallocate((struct NCR_ESP *)instance->hostdata);
+ esp_release();
+ release_mem_region(address, sizeof(struct ESP_regs));
+ free_irq(IRQ_AMIGA_PORTS, esp_intr);
+#endif
+ return 1;
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "esp-fastlane",
+ .proc_info = esp_proc_info,
+ .name = "Fastlane SCSI",
+ .detect = fastlane_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = fastlane_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/fcal.c b/drivers/scsi/fcal.c
new file mode 100644
index 000000000000..0dad89d4cb3e
--- /dev/null
+++ b/drivers/scsi/fcal.c
@@ -0,0 +1,320 @@
+/* fcal.c: Fibre Channel Arbitrated Loop SCSI host adapter driver.
+ *
+ * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "../fc4/fcp_impl.h"
+#include "fcal.h"
+
+#include <linux/module.h>
+
+/* #define FCAL_DEBUG */
+
+#define fcal_printk printk ("FCAL %s: ", fc->name); printk
+
+#ifdef FCAL_DEBUG
+#define FCALD(x) fcal_printk x;
+#define FCALND(x) printk ("FCAL: "); printk x;
+#else
+#define FCALD(x)
+#define FCALND(x)
+#endif
+
+static unsigned char alpa2target[] = {
+0x7e, 0x7d, 0x7c, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x7a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79,
+0x78, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x76, 0xff, 0xff, 0x75, 0xff, 0x74, 0x73, 0x72,
+0xff, 0xff, 0xff, 0x71, 0xff, 0x70, 0x6f, 0x6e, 0xff, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0xff,
+0xff, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0xff, 0xff, 0x61, 0x60, 0xff, 0x5f, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x5e, 0xff, 0x5d, 0x5c, 0x5b, 0xff, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0xff,
+0xff, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0xff, 0xff, 0x4e, 0x4d, 0xff, 0x4c, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x4b, 0xff, 0x4a, 0x49, 0x48, 0xff, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0xff,
+0xff, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0xff, 0xff, 0x3b, 0x3a, 0xff, 0x39, 0xff, 0xff, 0xff,
+0x38, 0x37, 0x36, 0xff, 0x35, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33,
+0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x30, 0xff, 0xff, 0x2f, 0xff, 0x2e, 0x2d, 0x2c,
+0xff, 0xff, 0xff, 0x2b, 0xff, 0x2a, 0x29, 0x28, 0xff, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0xff,
+0xff, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0xff, 0xff, 0x1b, 0x1a, 0xff, 0x19, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x18, 0xff, 0x17, 0x16, 0x15, 0xff, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0xff,
+0xff, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0xff, 0xff, 0x08, 0x07, 0xff, 0x06, 0xff, 0xff, 0xff,
+0x05, 0x04, 0x03, 0xff, 0x02, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
+};
+
+static unsigned char target2alpa[] = {
+0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce,
+0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5, 0xb4, 0xb3,
+0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b,
+0x98, 0x97, 0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79, 0x76, 0x75, 0x74, 0x73,
+0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
+0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3c,
+0x3a, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x27, 0x26,
+0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17, 0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00
+};
+
+static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd);
+
+int fcal_slave_configure(Scsi_Device *device)
+{
+ int depth_to_use;
+
+ if (device->tagged_supported)
+ depth_to_use = /* 254 */ 8;
+ else
+ depth_to_use = 2;
+
+ scsi_adjust_queue_depth(device,
+ (device->tagged_supported ?
+ MSG_SIMPLE_TAG : 0),
+ depth_to_use);
+
+ return 0;
+}
+
+/* Detect all FC Arbitrated Loops attached to the machine.
+ fc4 module has done all the work for us... */
+int __init fcal_detect(Scsi_Host_Template *tpnt)
+{
+ int nfcals = 0;
+ fc_channel *fc;
+ int fcalcount;
+ int i;
+
+ tpnt->proc_name = "fcal";
+ fcalcount = 0;
+ for_each_online_fc_channel(fc)
+ if (fc->posmap)
+ fcalcount++;
+ FCALND(("%d channels online\n", fcalcount))
+ if (!fcalcount) {
+#if defined(MODULE) && defined(CONFIG_FC4_SOCAL_MODULE) && defined(CONFIG_KMOD)
+ request_module("socal");
+
+ for_each_online_fc_channel(fc)
+ if (fc->posmap)
+ fcalcount++;
+ if (!fcalcount)
+#endif
+ return 0;
+ }
+ for_each_online_fc_channel(fc) {
+ struct Scsi_Host *host;
+ long *ages;
+ struct fcal *fcal;
+
+ if (!fc->posmap) continue;
+
+ /* Strange, this is already registered to some other SCSI host, then it cannot be fcal */
+ if (fc->scsi_name[0]) continue;
+ memcpy (fc->scsi_name, "FCAL", 4);
+
+ fc->can_queue = FCAL_CAN_QUEUE;
+ fc->rsp_size = 64;
+ fc->encode_addr = fcal_encode_addr;
+
+ ages = kmalloc (128 * sizeof(long), GFP_KERNEL);
+ if (!ages) continue;
+
+ host = scsi_register (tpnt, sizeof (struct fcal));
+ if (!host)
+ {
+ kfree(ages);
+ continue;
+ }
+
+ if (!try_module_get(fc->module)) {
+ kfree(ages);
+ scsi_unregister(host);
+ continue;
+ }
+
+ nfcals++;
+
+ fcal = (struct fcal *)host->hostdata;
+
+ fc->fcp_register(fc, TYPE_SCSI_FCP, 0);
+
+ for (i = 0; i < fc->posmap->len; i++) {
+ int status, target, alpa;
+
+ alpa = fc->posmap->list[i];
+ FCALD(("Sending PLOGI to %02x\n", alpa))
+ target = alpa2target[alpa];
+ status = fc_do_plogi(fc, alpa, fcal->node_wwn + target,
+ fcal->nport_wwn + target);
+ FCALD(("PLOGI returned with status %d\n", status))
+ if (status != FC_STATUS_OK)
+ continue;
+ FCALD(("Sending PRLI to %02x\n", alpa))
+ status = fc_do_prli(fc, alpa);
+ FCALD(("PRLI returned with status %d\n", status))
+ if (status == FC_STATUS_OK)
+ fcal->map[target] = 1;
+ }
+
+ host->max_id = 127;
+ host->irq = fc->irq;
+#ifdef __sparc_v9__
+ host->unchecked_isa_dma = 1;
+#endif
+
+ fc->channels = 1;
+ fc->targets = 127;
+ fc->ages = ages;
+ memset (ages, 0, 128 * sizeof(long));
+
+ fcal->fc = fc;
+
+ FCALD(("Found FCAL\n"))
+ }
+ if (nfcals)
+#ifdef __sparc__
+ printk ("FCAL: Total of %d Sun Enterprise Network Array (A5000 or EX500) channels found\n", nfcals);
+#else
+ printk ("FCAL: Total of %d Fibre Channel Arbitrated Loops found\n", nfcals);
+#endif
+ return nfcals;
+}
+
+int fcal_release(struct Scsi_Host *host)
+{
+ struct fcal *fcal = (struct fcal *)host->hostdata;
+ fc_channel *fc = fcal->fc;
+
+ module_put(fc->module);
+
+ fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
+ FCALND((" releasing fcal.\n"));
+ kfree (fc->ages);
+ FCALND(("released fcal!\n"));
+ return 0;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int fcal_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ struct fcal *fcal;
+ fc_channel *fc;
+ char *pos = buffer;
+ int i, j;
+
+ if (inout) return length;
+
+ fcal = (struct fcal *)host->hostdata;
+ fc = fcal->fc;
+
+#ifdef __sparc__
+ SPRINTF ("Sun Enterprise Network Array (A5000 or E?500) on %s PROM node %x\n", fc->name, fc->dev->prom_node);
+#else
+ SPRINTF ("Fibre Channel Arbitrated Loop on %s\n", fc->name);
+#endif
+ SPRINTF ("Initiator AL-PA: %02x\n", fc->sid);
+
+ SPRINTF ("\nAttached devices:\n");
+
+ for (i = 0; i < fc->posmap->len; i++) {
+ unsigned char alpa = fc->posmap->list[i];
+ unsigned char target;
+ u32 *u1, *u2;
+
+ target = alpa2target[alpa];
+ u1 = (u32 *)&fcal->nport_wwn[target];
+ u2 = (u32 *)&fcal->node_wwn[target];
+ if (!u1[0] && !u1[1]) {
+ SPRINTF (" [AL-PA: %02x] Not responded to PLOGI\n", alpa);
+ } else if (!fcal->map[target]) {
+ SPRINTF (" [AL-PA: %02x, Port WWN: %08x%08x, Node WWN: %08x%08x] Not responded to PRLI\n",
+ alpa, u1[0], u1[1], u2[0], u2[1]);
+ } else {
+ Scsi_Device *scd;
+ shost_for_each_device(scd, host)
+ if (scd->id == target) {
+ SPRINTF (" [AL-PA: %02x, Id: %02d, Port WWN: %08x%08x, Node WWN: %08x%08x] ",
+ alpa, target, u1[0], u1[1], u2[0], u2[1]);
+ SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+ scsi_device_types[(short) scd->type] : "Unknown device");
+
+ for (j = 0; (j < 8) && (scd->vendor[j] >= 0x20); j++)
+ SPRINTF ("%c", scd->vendor[j]);
+ SPRINTF (" ");
+
+ for (j = 0; (j < 16) && (scd->model[j] >= 0x20); j++)
+ SPRINTF ("%c", scd->model[j]);
+
+ SPRINTF ("\n");
+ }
+ }
+ }
+ SPRINTF ("\n");
+
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+
+/*
+ For FC-AL, we use a simple addressing: we have just one channel 0,
+ and all AL-PAs are mapped to targets 0..0x7e
+ */
+static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd)
+{
+ struct fcal *f;
+
+ /* We don't support LUNs yet - I'm not sure if LUN should be in SCSI fcp_cdb, or in second byte of addr[0] */
+ if (SCpnt->cmnd[1] & 0xe0) return -EINVAL;
+ /* FC-PLDA tells us... */
+ memset(addr, 0, 8);
+ f = (struct fcal *)SCpnt->device->host->hostdata;
+ if (!f->map[SCpnt->device->id])
+ return -EINVAL;
+ /* Now, determine DID: It will be Native Identifier, so we zero upper
+ 2 bytes of the 3 byte DID, lowest byte will be AL-PA */
+ fcmd->did = target2alpa[SCpnt->device->id];
+ FCALD(("trying DID %06x\n", fcmd->did))
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "Fibre Channel Arbitrated Loop",
+ .detect = fcal_detect,
+ .release = fcal_release,
+ .proc_info = fcal_proc_info,
+ .queuecommand = fcp_scsi_queuecommand,
+ .slave_configure = fcal_slave_configure,
+ .can_queue = FCAL_CAN_QUEUE,
+ .this_id = -1,
+ .sg_tablesize = 1,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_abort_handler = fcp_scsi_abort,
+ .eh_device_reset_handler = fcp_scsi_dev_reset,
+ .eh_bus_reset_handler = fcp_scsi_bus_reset,
+ .eh_host_reset_handler = fcp_scsi_host_reset,
+};
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/fcal.h b/drivers/scsi/fcal.h
new file mode 100644
index 000000000000..21aa32ef9134
--- /dev/null
+++ b/drivers/scsi/fcal.h
@@ -0,0 +1,27 @@
+/* fcal.h: Generic Fibre Channel Arbitrated Loop SCSI host adapter driver definitions.
+ *
+ * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#ifndef _FCAL_H
+#define _FCAL_H
+
+#include "../fc4/fcp_impl.h"
+
+struct fcal {
+ /* fc must be first */
+ fc_channel *fc;
+ unsigned char map[128];
+ fc_wwn nport_wwn[128];
+ fc_wwn node_wwn[128];
+};
+
+/* Arbitrary constant. Cannot be too large, as fc4 layer has limitations
+ for a particular channel */
+#define FCAL_CAN_QUEUE 512
+
+int fcal_detect(Scsi_Host_Template *);
+int fcal_release(struct Scsi_Host *);
+int fcal_slave_configure(Scsi_Device *);
+
+#endif /* !(_FCAL_H) */
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
new file mode 100644
index 000000000000..770930e2aec3
--- /dev/null
+++ b/drivers/scsi/fd_mcs.c
@@ -0,0 +1,1369 @@
+/* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver
+ *
+ * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net)
+ *
+ * This driver is cloned from fdomain.* to specifically support
+ * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s
+ * also equipped with IBM Fast SCSI Adapter/A which is an OEM
+ * of MCS 700.
+ *
+ * This driver also supports Reply SB16/SCSI card (the SCSI part).
+ *
+ * What makes this driver different is that this driver is MCA only
+ * and it supports multiple adapters in the same system, IRQ
+ * sharing, some driver statistics, and maps highest SCSI id to sda.
+ * All cards are auto-detected.
+ *
+ * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4
+ *
+ * LILO command-line options:
+ * fd_mcs=<FIFO_COUNT>[,<FIFO_SIZE>]
+ *
+ * ********************************************************
+ * Please see Copyrights/Comments in fdomain.* for credits.
+ * Following is from fdomain.c for acknowledgement:
+ *
+ * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
+ * Revised: Wed Oct 2 11:10:55 1996 by r.faith@ieee.org
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
+ *
+ * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You 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.
+
+ **************************************************************************
+
+ NOTES ON USER DEFINABLE OPTIONS:
+
+ DEBUG: This turns on the printing of various debug information.
+
+ ENABLE_PARITY: This turns on SCSI parity checking. With the current
+ driver, all attached devices must support SCSI parity. If none of your
+ devices support parity, then you can probably get the driver to work by
+ turning this option off. I have no way of testing this, however, and it
+ would appear that no one ever uses this option.
+
+ FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
+ 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
+ the SCSI device, an interrupt will be raised. Therefore, this could be as
+ low as 0, or as high as 16. Note, however, that values which are too high
+ or too low seem to prevent any interrupts from occurring, and thereby lock
+ up the machine. I have found that 2 is a good number, but throughput may
+ be increased by changing this value to values which are close to 2.
+ Please let me know if you try any different values.
+ [*****Now a runtime option*****]
+
+ RESELECTION: This is no longer an option, since I gave up trying to
+ implement it in version 4.x of this driver. It did not improve
+ performance at all and made the driver unstable (because I never found one
+ of the two race conditions which were introduced by the multiple
+ outstanding command code). The instability seems a very high price to pay
+ just so that you don't have to wait for the tape to rewind. If you want
+ this feature implemented, send me patches. I'll be happy to send a copy
+ of my (broken) driver to anyone who would like to see a copy.
+
+ **************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/mca.h>
+#include <linux/spinlock.h>
+#include <scsi/scsicam.h>
+#include <linux/mca-legacy.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#define DRIVER_VERSION "v0.2 by ZP Gu<zpg@castle.net>"
+
+/* START OF USER DEFINABLE OPTIONS */
+
+#define DEBUG 0 /* Enable debugging output */
+#define ENABLE_PARITY 1 /* Enable SCSI Parity */
+
+/* END OF USER DEFINABLE OPTIONS */
+
+#if DEBUG
+#define EVERY_ACCESS 0 /* Write a line on every scsi access */
+#define ERRORS_ONLY 1 /* Only write a line if there is an error */
+#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
+#define DEBUG_ABORT 1 /* Debug abort() routine */
+#define DEBUG_RESET 1 /* Debug reset() routine */
+#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
+#else
+#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
+#define ERRORS_ONLY 0
+#define DEBUG_MESSAGES 0
+#define DEBUG_ABORT 0
+#define DEBUG_RESET 0
+#define DEBUG_RACE 0
+#endif
+
+/* Errors are reported on the line, so we don't need to report them again */
+#if EVERY_ACCESS
+#undef ERRORS_ONLY
+#define ERRORS_ONLY 0
+#endif
+
+#if ENABLE_PARITY
+#define PARITY_MASK 0x08
+#else
+#define PARITY_MASK 0x00
+#endif
+
+enum chip_type {
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+ tmc18c30 = 0x03,
+};
+
+enum {
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+enum in_port_type {
+ Read_SCSI_Data = 0,
+ SCSI_Status = 1,
+ TMC_Status = 2,
+ FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
+ Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
+ LSB_ID_Code = 5,
+ MSB_ID_Code = 6,
+ Read_Loopback = 7,
+ SCSI_Data_NoACK = 8,
+ Interrupt_Status = 9,
+ Configuration1 = 10,
+ Configuration2 = 11, /* tmc18c50/tmc18c30 only */
+ Read_FIFO = 12,
+ FIFO_Data_Count = 14
+};
+
+enum out_port_type {
+ Write_SCSI_Data = 0,
+ SCSI_Cntl = 1,
+ Interrupt_Cntl = 2,
+ SCSI_Mode_Cntl = 3,
+ TMC_Cntl = 4,
+ Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
+ Write_Loopback = 7,
+ IO_Control = 11, /* tmc18c30 only */
+ Write_FIFO = 12
+};
+
+struct fd_hostdata {
+ unsigned long _bios_base;
+ int _bios_major;
+ int _bios_minor;
+ volatile int _in_command;
+ Scsi_Cmnd *_current_SC;
+ enum chip_type _chip;
+ int _adapter_mask;
+ int _fifo_count; /* Number of 512 byte blocks before INTR */
+
+ char _adapter_name[64];
+#if DEBUG_RACE
+ volatile int _in_interrupt_flag;
+#endif
+
+ int _SCSI_Mode_Cntl_port;
+ int _FIFO_Data_Count_port;
+ int _Interrupt_Cntl_port;
+ int _Interrupt_Status_port;
+ int _Interrupt_Cond_port;
+ int _Read_FIFO_port;
+ int _Read_SCSI_Data_port;
+ int _SCSI_Cntl_port;
+ int _SCSI_Data_NoACK_port;
+ int _SCSI_Status_port;
+ int _TMC_Cntl_port;
+ int _TMC_Status_port;
+ int _Write_FIFO_port;
+ int _Write_SCSI_Data_port;
+
+ int _FIFO_Size; /* = 0x2000; 8k FIFO for
+ pre-tmc18c30 chips */
+ /* simple stats */
+ int _Bytes_Read;
+ int _Bytes_Written;
+ int _INTR_Processed;
+};
+
+#define FD_MAX_HOSTS 3 /* enough? */
+
+#define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata)
+#define bios_base (HOSTDATA(shpnt)->_bios_base)
+#define bios_major (HOSTDATA(shpnt)->_bios_major)
+#define bios_minor (HOSTDATA(shpnt)->_bios_minor)
+#define in_command (HOSTDATA(shpnt)->_in_command)
+#define current_SC (HOSTDATA(shpnt)->_current_SC)
+#define chip (HOSTDATA(shpnt)->_chip)
+#define adapter_mask (HOSTDATA(shpnt)->_adapter_mask)
+#define FIFO_COUNT (HOSTDATA(shpnt)->_fifo_count)
+#define adapter_name (HOSTDATA(shpnt)->_adapter_name)
+#if DEBUG_RACE
+#define in_interrupt_flag (HOSTDATA(shpnt)->_in_interrupt_flag)
+#endif
+#define SCSI_Mode_Cntl_port (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port)
+#define FIFO_Data_Count_port (HOSTDATA(shpnt)->_FIFO_Data_Count_port)
+#define Interrupt_Cntl_port (HOSTDATA(shpnt)->_Interrupt_Cntl_port)
+#define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port)
+#define Interrupt_Cond_port (HOSTDATA(shpnt)->_Interrupt_Cond_port)
+#define Read_FIFO_port (HOSTDATA(shpnt)->_Read_FIFO_port)
+#define Read_SCSI_Data_port (HOSTDATA(shpnt)->_Read_SCSI_Data_port)
+#define SCSI_Cntl_port (HOSTDATA(shpnt)->_SCSI_Cntl_port)
+#define SCSI_Data_NoACK_port (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port)
+#define SCSI_Status_port (HOSTDATA(shpnt)->_SCSI_Status_port)
+#define TMC_Cntl_port (HOSTDATA(shpnt)->_TMC_Cntl_port)
+#define TMC_Status_port (HOSTDATA(shpnt)->_TMC_Status_port)
+#define Write_FIFO_port (HOSTDATA(shpnt)->_Write_FIFO_port)
+#define Write_SCSI_Data_port (HOSTDATA(shpnt)->_Write_SCSI_Data_port)
+#define FIFO_Size (HOSTDATA(shpnt)->_FIFO_Size)
+#define Bytes_Read (HOSTDATA(shpnt)->_Bytes_Read)
+#define Bytes_Written (HOSTDATA(shpnt)->_Bytes_Written)
+#define INTR_Processed (HOSTDATA(shpnt)->_INTR_Processed)
+
+struct fd_mcs_adapters_struct {
+ char *name;
+ int id;
+ enum chip_type fd_chip;
+ int fifo_size;
+ int fifo_count;
+};
+
+#define REPLY_ID 0x5137
+
+static struct fd_mcs_adapters_struct fd_mcs_adapters[] = {
+ {"Future Domain SCSI Adapter MCS-700(18C50)",
+ 0x60e9,
+ tmc18c50,
+ 0x2000,
+ 4},
+ {"Future Domain SCSI Adapter MCS-600/700(TMC-1800)",
+ 0x6127,
+ tmc1800,
+ 0x2000,
+ 4},
+ {"Reply Sound Blaster/SCSI Adapter",
+ REPLY_ID,
+ tmc18c30,
+ 0x800,
+ 2},
+};
+
+#define FD_BRDS sizeof(fd_mcs_adapters)/sizeof(struct fd_mcs_adapters_struct)
+
+static irqreturn_t fd_mcs_intr(int irq, void *dev_id, struct pt_regs *regs);
+
+static unsigned long addresses[] = { 0xc8000, 0xca000, 0xce000, 0xde000 };
+static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
+static unsigned short interrupts[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+
+/* host information */
+static int found = 0;
+static struct Scsi_Host *hosts[FD_MAX_HOSTS + 1] = { NULL };
+
+static int user_fifo_count = 0;
+static int user_fifo_size = 0;
+
+static int __init fd_mcs_setup(char *str)
+{
+ static int done_setup = 0;
+ int ints[3];
+
+ get_options(str, 3, ints);
+ if (done_setup++ || ints[0] < 1 || ints[0] > 2 || ints[1] < 1 || ints[1] > 16) {
+ printk("fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n");
+ return 0;
+ }
+
+ user_fifo_count = ints[0] >= 1 ? ints[1] : 0;
+ user_fifo_size = ints[0] >= 2 ? ints[2] : 0;
+ return 1;
+}
+
+__setup("fd_mcs=", fd_mcs_setup);
+
+static void print_banner(struct Scsi_Host *shpnt)
+{
+ printk("scsi%d <fd_mcs>: ", shpnt->host_no);
+
+ if (bios_base) {
+ printk("BIOS at 0x%lX", bios_base);
+ } else {
+ printk("No BIOS");
+ }
+
+ printk(", HostID %d, %s Chip, IRQ %d, IO 0x%lX\n", shpnt->this_id, chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? "TMC-18C30" : (chip == tmc1800 ? "TMC-1800" : "Unknown")), shpnt->irq, shpnt->io_port);
+}
+
+
+static void do_pause(unsigned amount)
+{ /* Pause for amount*10 milliseconds */
+ do {
+ mdelay(10);
+ } while (--amount);
+}
+
+static void fd_mcs_make_bus_idle(struct Scsi_Host *shpnt)
+{
+ outb(0, SCSI_Cntl_port);
+ outb(0, SCSI_Mode_Cntl_port);
+ if (chip == tmc18c50 || chip == tmc18c30)
+ outb(0x21 | PARITY_MASK, TMC_Cntl_port); /* Clear forced intr. */
+ else
+ outb(0x01 | PARITY_MASK, TMC_Cntl_port);
+}
+
+static int fd_mcs_detect(Scsi_Host_Template * tpnt)
+{
+ int loop;
+ struct Scsi_Host *shpnt;
+
+ /* get id, port, bios, irq */
+ int slot;
+ u_char pos2, pos3, pos4;
+ int id, port, irq;
+ unsigned long bios;
+
+ /* if not MCA machine, return */
+ if (!MCA_bus)
+ return 0;
+
+ /* changeable? */
+ id = 7;
+
+ for (loop = 0; loop < FD_BRDS; loop++) {
+ slot = 0;
+ while (MCA_NOTFOUND != (slot = mca_find_adapter(fd_mcs_adapters[loop].id, slot))) {
+
+ /* if we get this far, an adapter has been detected and is
+ enabled */
+
+ printk(KERN_INFO "scsi <fd_mcs>: %s at slot %d\n", fd_mcs_adapters[loop].name, slot + 1);
+
+ pos2 = mca_read_stored_pos(slot, 2);
+ pos3 = mca_read_stored_pos(slot, 3);
+ pos4 = mca_read_stored_pos(slot, 4);
+
+ /* ready for next probe */
+ slot++;
+
+ if (fd_mcs_adapters[loop].id == REPLY_ID) { /* reply card */
+ static int reply_irq[] = { 10, 11, 14, 15 };
+
+ bios = 0; /* no bios */
+
+ if (pos2 & 0x2)
+ port = ports[pos4 & 0x3];
+ else
+ continue;
+
+ /* can't really disable it, same as irq=10 */
+ irq = reply_irq[((pos4 >> 2) & 0x1) + 2 * ((pos4 >> 4) & 0x1)];
+ } else {
+ bios = addresses[pos2 >> 6];
+ port = ports[(pos2 >> 4) & 0x03];
+ irq = interrupts[(pos2 >> 1) & 0x07];
+ }
+
+ if (irq) {
+ /* claim the slot */
+ mca_set_adapter_name(slot - 1, fd_mcs_adapters[loop].name);
+
+ /* check irq/region */
+ if (request_irq(irq, fd_mcs_intr, SA_SHIRQ, "fd_mcs", hosts)) {
+ printk(KERN_ERR "fd_mcs: interrupt is not available, skipping...\n");
+ continue;
+ }
+
+ /* request I/O region */
+ if (request_region(port, 0x10, "fd_mcs")) {
+ printk(KERN_ERR "fd_mcs: I/O region is already in use, skipping...\n");
+ continue;
+ }
+ /* register */
+ if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) {
+ printk(KERN_ERR "fd_mcs: scsi_register() failed\n");
+ release_region(port, 0x10);
+ free_irq(irq, hosts);
+ continue;
+ }
+
+
+ /* save name */
+ strcpy(adapter_name, fd_mcs_adapters[loop].name);
+
+ /* chip/fifo */
+ chip = fd_mcs_adapters[loop].fd_chip;
+ /* use boot time value if available */
+ FIFO_COUNT = user_fifo_count ? user_fifo_count : fd_mcs_adapters[loop].fifo_count;
+ FIFO_Size = user_fifo_size ? user_fifo_size : fd_mcs_adapters[loop].fifo_size;
+
+/* FIXME: Do we need to keep this bit of code inside NOT_USED around at all? */
+#ifdef NOT_USED
+ /* *************************************************** */
+ /* Try to toggle 32-bit mode. This only
+ works on an 18c30 chip. (User reports
+ say this works, so we should switch to
+ it in the near future.) */
+ outb(0x80, port + IO_Control);
+ if ((inb(port + Configuration2) & 0x80) == 0x80) {
+ outb(0x00, port + IO_Control);
+ if ((inb(port + Configuration2) & 0x80) == 0x00) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+
+ printk("FIRST: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
+ }
+ }
+
+ /* That should have worked, but appears to
+ have problems. Let's assume it is an
+ 18c30 if the RAM is disabled. */
+
+ if (inb(port + Configuration2) & 0x02) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+
+ printk("SECOND: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
+ }
+ /* *************************************************** */
+#endif
+
+ /* IBM/ANSI scsi scan ordering */
+ /* Stick this back in when the scsi.c changes are there */
+ shpnt->reverse_ordering = 1;
+
+
+ /* saving info */
+ hosts[found++] = shpnt;
+
+ shpnt->this_id = id;
+ shpnt->irq = irq;
+ shpnt->io_port = port;
+ shpnt->n_io_port = 0x10;
+
+ /* save */
+ bios_base = bios;
+ adapter_mask = (1 << id);
+
+ /* save more */
+ SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl;
+ FIFO_Data_Count_port = port + FIFO_Data_Count;
+ Interrupt_Cntl_port = port + Interrupt_Cntl;
+ Interrupt_Status_port = port + Interrupt_Status;
+ Interrupt_Cond_port = port + Interrupt_Cond;
+ Read_FIFO_port = port + Read_FIFO;
+ Read_SCSI_Data_port = port + Read_SCSI_Data;
+ SCSI_Cntl_port = port + SCSI_Cntl;
+ SCSI_Data_NoACK_port = port + SCSI_Data_NoACK;
+ SCSI_Status_port = port + SCSI_Status;
+ TMC_Cntl_port = port + TMC_Cntl;
+ TMC_Status_port = port + TMC_Status;
+ Write_FIFO_port = port + Write_FIFO;
+ Write_SCSI_Data_port = port + Write_SCSI_Data;
+
+ Bytes_Read = 0;
+ Bytes_Written = 0;
+ INTR_Processed = 0;
+
+ /* say something */
+ print_banner(shpnt);
+
+ /* reset */
+ outb(1, SCSI_Cntl_port);
+ do_pause(2);
+ outb(0, SCSI_Cntl_port);
+ do_pause(115);
+ outb(0, SCSI_Mode_Cntl_port);
+ outb(PARITY_MASK, TMC_Cntl_port);
+ /* done reset */
+ }
+ }
+
+ if (found == FD_MAX_HOSTS) {
+ printk("fd_mcs: detecting reached max=%d host adapters.\n", FD_MAX_HOSTS);
+ break;
+ }
+ }
+
+ return found;
+}
+
+static const char *fd_mcs_info(struct Scsi_Host *shpnt)
+{
+ return adapter_name;
+}
+
+static int TOTAL_INTR = 0;
+
+/*
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+static int fd_mcs_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ int len = 0;
+
+ if (inout)
+ return (-ENOSYS);
+
+ *start = buffer + offset;
+
+ len += sprintf(buffer + len, "Future Domain MCS-600/700 Driver %s\n", DRIVER_VERSION);
+ len += sprintf(buffer + len, "HOST #%d: %s\n", shpnt->host_no, adapter_name);
+ len += sprintf(buffer + len, "FIFO Size=0x%x, FIFO Count=%d\n", FIFO_Size, FIFO_COUNT);
+ len += sprintf(buffer + len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d\n\n", TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written);
+
+ if ((len -= offset) <= 0)
+ return 0;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+static int fd_mcs_select(struct Scsi_Host *shpnt, int target)
+{
+ int status;
+ unsigned long timeout;
+
+ outb(0x82, SCSI_Cntl_port); /* Bus Enable + Select */
+ outb(adapter_mask | (1 << target), SCSI_Data_NoACK_port);
+
+ /* Stop arbitration and enable parity */
+ outb(PARITY_MASK, TMC_Cntl_port);
+
+ timeout = 350; /* 350mS -- because of timeouts
+ (was 250mS) */
+
+ do {
+ status = inb(SCSI_Status_port); /* Read adapter status */
+ if (status & 1) { /* Busy asserted */
+ /* Enable SCSI Bus (on error, should make bus idle with 0) */
+ outb(0x80, SCSI_Cntl_port);
+ return 0;
+ }
+ udelay(1000); /* wait one msec */
+ } while (--timeout);
+
+ /* Make bus idle */
+ fd_mcs_make_bus_idle(shpnt);
+#if EVERY_ACCESS
+ if (!target)
+ printk("Selection failed\n");
+#endif
+#if ERRORS_ONLY
+ if (!target) {
+ static int flag = 0;
+
+ if (!flag) /* Skip first failure for all chips. */
+ ++flag;
+ else
+ printk("fd_mcs: Selection failed\n");
+ }
+#endif
+ return 1;
+}
+
+static void my_done(struct Scsi_Host *shpnt, int error)
+{
+ if (in_command) {
+ in_command = 0;
+ outb(0x00, Interrupt_Cntl_port);
+ fd_mcs_make_bus_idle(shpnt);
+ current_SC->result = error;
+ current_SC->scsi_done(current_SC);
+ } else {
+ panic("fd_mcs: my_done() called outside of command\n");
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+}
+
+/* only my_done needs to be protected */
+static irqreturn_t fd_mcs_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ int status;
+ int done = 0;
+ unsigned data_count, tmp_count;
+
+ int i = 0;
+ struct Scsi_Host *shpnt;
+
+ TOTAL_INTR++;
+
+ /* search for one adapter-response on shared interrupt */
+ while ((shpnt = hosts[i++])) {
+ if ((inb(TMC_Status_port)) & 1)
+ break;
+ }
+
+ /* return if some other device on this IRQ caused the interrupt */
+ if (!shpnt) {
+ return IRQ_NONE;
+ }
+
+ INTR_Processed++;
+
+ outb(0x00, Interrupt_Cntl_port);
+
+ /* Abort calls my_done, so we do nothing here. */
+ if (current_SC->SCp.phase & aborted) {
+#if DEBUG_ABORT
+ printk("Interrupt after abort, ignoring\n");
+#endif
+ /* return IRQ_HANDLED; */
+ }
+#if DEBUG_RACE
+ ++in_interrupt_flag;
+#endif
+
+ if (current_SC->SCp.phase & in_arbitration) {
+ status = inb(TMC_Status_port); /* Read adapter status */
+ if (!(status & 0x02)) {
+#if EVERY_ACCESS
+ printk(" AFAIL ");
+#endif
+ spin_lock_irqsave(shpnt->host_lock, flags);
+ my_done(shpnt, DID_BUS_BUSY << 16);
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+ return IRQ_HANDLED;
+ }
+ current_SC->SCp.phase = in_selection;
+
+ outb(0x40 | FIFO_COUNT, Interrupt_Cntl_port);
+
+ outb(0x82, SCSI_Cntl_port); /* Bus Enable + Select */
+ outb(adapter_mask | (1 << current_SC->device->id), SCSI_Data_NoACK_port);
+
+ /* Stop arbitration and enable parity */
+ outb(0x10 | PARITY_MASK, TMC_Cntl_port);
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return IRQ_HANDLED;
+ } else if (current_SC->SCp.phase & in_selection) {
+ status = inb(SCSI_Status_port);
+ if (!(status & 0x01)) {
+ /* Try again, for slow devices */
+ if (fd_mcs_select(shpnt, current_SC->device->id)) {
+#if EVERY_ACCESS
+ printk(" SFAIL ");
+#endif
+ spin_lock_irqsave(shpnt->host_lock, flags);
+ my_done(shpnt, DID_NO_CONNECT << 16);
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+ return IRQ_HANDLED;
+ } else {
+#if EVERY_ACCESS
+ printk(" AltSel ");
+#endif
+ /* Stop arbitration and enable parity */
+ outb(0x10 | PARITY_MASK, TMC_Cntl_port);
+ }
+ }
+ current_SC->SCp.phase = in_other;
+ outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
+ outb(0x80, SCSI_Cntl_port);
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return IRQ_HANDLED;
+ }
+
+ /* current_SC->SCp.phase == in_other: this is the body of the routine */
+
+ status = inb(SCSI_Status_port);
+
+ if (status & 0x10) { /* REQ */
+
+ switch (status & 0x0e) {
+
+ case 0x08: /* COMMAND OUT */
+ outb(current_SC->cmnd[current_SC->SCp.sent_command++], Write_SCSI_Data_port);
+#if EVERY_ACCESS
+ printk("CMD = %x,", current_SC->cmnd[current_SC->SCp.sent_command - 1]);
+#endif
+ break;
+ case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = -1;
+ outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
+ }
+ break;
+ case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = 1;
+ outb(0x90 | PARITY_MASK, TMC_Cntl_port);
+ }
+ break;
+ case 0x0c: /* STATUS IN */
+ current_SC->SCp.Status = inb(Read_SCSI_Data_port);
+#if EVERY_ACCESS
+ printk("Status = %x, ", current_SC->SCp.Status);
+#endif
+#if ERRORS_ONLY
+ if (current_SC->SCp.Status && current_SC->SCp.Status != 2 && current_SC->SCp.Status != 8) {
+ printk("ERROR fd_mcs: target = %d, command = %x, status = %x\n", current_SC->device->id, current_SC->cmnd[0], current_SC->SCp.Status);
+ }
+#endif
+ break;
+ case 0x0a: /* MESSAGE OUT */
+ outb(MESSAGE_REJECT, Write_SCSI_Data_port); /* Reject */
+ break;
+ case 0x0e: /* MESSAGE IN */
+ current_SC->SCp.Message = inb(Read_SCSI_Data_port);
+#if EVERY_ACCESS
+ printk("Message = %x, ", current_SC->SCp.Message);
+#endif
+ if (!current_SC->SCp.Message)
+ ++done;
+#if DEBUG_MESSAGES || EVERY_ACCESS
+ if (current_SC->SCp.Message) {
+ printk("fd_mcs: message = %x\n", current_SC->SCp.Message);
+ }
+#endif
+ break;
+ }
+ }
+
+ if (chip == tmc1800 && !current_SC->SCp.have_data_in && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
+ /* We have to get the FIFO direction
+ correct, so I've made a table based
+ on the SCSI Standard of which commands
+ appear to require a DATA OUT phase.
+ */
+ /*
+ p. 94: Command for all device types
+ CHANGE DEFINITION 40 DATA OUT
+ COMPARE 39 DATA OUT
+ COPY 18 DATA OUT
+ COPY AND VERIFY 3a DATA OUT
+ INQUIRY 12
+ LOG SELECT 4c DATA OUT
+ LOG SENSE 4d
+ MODE SELECT (6) 15 DATA OUT
+ MODE SELECT (10) 55 DATA OUT
+ MODE SENSE (6) 1a
+ MODE SENSE (10) 5a
+ READ BUFFER 3c
+ RECEIVE DIAGNOSTIC RESULTS 1c
+ REQUEST SENSE 03
+ SEND DIAGNOSTIC 1d DATA OUT
+ TEST UNIT READY 00
+ WRITE BUFFER 3b DATA OUT
+
+ p.178: Commands for direct-access devices (not listed on p. 94)
+ FORMAT UNIT 04 DATA OUT
+ LOCK-UNLOCK CACHE 36
+ PRE-FETCH 34
+ PREVENT-ALLOW MEDIUM REMOVAL 1e
+ READ (6)/RECEIVE 08
+ READ (10) 3c
+ READ CAPACITY 25
+ READ DEFECT DATA (10) 37
+ READ LONG 3e
+ REASSIGN BLOCKS 07 DATA OUT
+ RELEASE 17
+ RESERVE 16 DATA OUT
+ REZERO UNIT/REWIND 01
+ SEARCH DATA EQUAL (10) 31 DATA OUT
+ SEARCH DATA HIGH (10) 30 DATA OUT
+ SEARCH DATA LOW (10) 32 DATA OUT
+ SEEK (6) 0b
+ SEEK (10) 2b
+ SET LIMITS (10) 33
+ START STOP UNIT 1b
+ SYNCHRONIZE CACHE 35
+ VERIFY (10) 2f
+ WRITE (6)/PRINT/SEND 0a DATA OUT
+ WRITE (10)/SEND 2a DATA OUT
+ WRITE AND VERIFY (10) 2e DATA OUT
+ WRITE LONG 3f DATA OUT
+ WRITE SAME 41 DATA OUT ?
+
+ p. 261: Commands for sequential-access devices (not previously listed)
+ ERASE 19
+ LOAD UNLOAD 1b
+ LOCATE 2b
+ READ BLOCK LIMITS 05
+ READ POSITION 34
+ READ REVERSE 0f
+ RECOVER BUFFERED DATA 14
+ SPACE 11
+ WRITE FILEMARKS 10 ?
+
+ p. 298: Commands for printer devices (not previously listed)
+ ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
+ SLEW AND PRINT 0b DATA OUT -- same as seek
+ STOP PRINT 1b
+ SYNCHRONIZE BUFFER 10
+
+ p. 315: Commands for processor devices (not previously listed)
+
+ p. 321: Commands for write-once devices (not previously listed)
+ MEDIUM SCAN 38
+ READ (12) a8
+ SEARCH DATA EQUAL (12) b1 DATA OUT
+ SEARCH DATA HIGH (12) b0 DATA OUT
+ SEARCH DATA LOW (12) b2 DATA OUT
+ SET LIMITS (12) b3
+ VERIFY (12) af
+ WRITE (12) aa DATA OUT
+ WRITE AND VERIFY (12) ae DATA OUT
+
+ p. 332: Commands for CD-ROM devices (not previously listed)
+ PAUSE/RESUME 4b
+ PLAY AUDIO (10) 45
+ PLAY AUDIO (12) a5
+ PLAY AUDIO MSF 47
+ PLAY TRACK RELATIVE (10) 49
+ PLAY TRACK RELATIVE (12) a9
+ READ HEADER 44
+ READ SUB-CHANNEL 42
+ READ TOC 43
+
+ p. 370: Commands for scanner devices (not previously listed)
+ GET DATA BUFFER STATUS 34
+ GET WINDOW 25
+ OBJECT POSITION 31
+ SCAN 1b
+ SET WINDOW 24 DATA OUT
+
+ p. 391: Commands for optical memory devices (not listed)
+ ERASE (10) 2c
+ ERASE (12) ac
+ MEDIUM SCAN 38 DATA OUT
+ READ DEFECT DATA (12) b7
+ READ GENERATION 29
+ READ UPDATED BLOCK 2d
+ UPDATE BLOCK 3d DATA OUT
+
+ p. 419: Commands for medium changer devices (not listed)
+ EXCHANGE MEDIUM 46
+ INITIALIZE ELEMENT STATUS 07
+ MOVE MEDIUM a5
+ POSITION TO ELEMENT 2b
+ READ ELEMENT STATUS b8
+ REQUEST VOL. ELEMENT ADDRESS b5
+ SEND VOLUME TAG b6 DATA OUT
+
+ p. 454: Commands for communications devices (not listed previously)
+ GET MESSAGE (6) 08
+ GET MESSAGE (10) 28
+ GET MESSAGE (12) a8
+ */
+
+ switch (current_SC->cmnd[0]) {
+ case CHANGE_DEFINITION:
+ case COMPARE:
+ case COPY:
+ case COPY_VERIFY:
+ case LOG_SELECT:
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case SEND_DIAGNOSTIC:
+ case WRITE_BUFFER:
+
+ case FORMAT_UNIT:
+ case REASSIGN_BLOCKS:
+ case RESERVE:
+ case SEARCH_EQUAL:
+ case SEARCH_HIGH:
+ case SEARCH_LOW:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_VERIFY:
+ case 0x3f:
+ case 0x41:
+
+ case 0xb1:
+ case 0xb0:
+ case 0xb2:
+ case 0xaa:
+ case 0xae:
+
+ case 0x24:
+
+ case 0x38:
+ case 0x3d:
+
+ case 0xb6:
+
+ case 0xea: /* alternate number for WRITE LONG */
+
+ current_SC->SCp.have_data_in = -1;
+ outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
+ break;
+
+ case 0x00:
+ default:
+
+ current_SC->SCp.have_data_in = 1;
+ outb(0x90 | PARITY_MASK, TMC_Cntl_port);
+ break;
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
+ while ((data_count = FIFO_Size - inw(FIFO_Data_Count_port)) > 512) {
+#if EVERY_ACCESS
+ printk("DC=%d, ", data_count);
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count > 0) {
+#if EVERY_ACCESS
+ printk("%d OUT, ", data_count);
+#endif
+ if (data_count == 1) {
+ Bytes_Written++;
+
+ outb(*current_SC->SCp.ptr++, Write_FIFO_port);
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1;
+ tmp_count = data_count << 1;
+ outsw(Write_FIFO_port, current_SC->SCp.ptr, data_count);
+ current_SC->SCp.ptr += tmp_count;
+ Bytes_Written += tmp_count;
+ current_SC->SCp.this_residual -= tmp_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual) {
+ if (current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ } else
+ break;
+ }
+ }
+ } else if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
+ while ((data_count = inw(FIFO_Data_Count_port)) > 0) {
+#if EVERY_ACCESS
+ printk("DC=%d, ", data_count);
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count) {
+#if EVERY_ACCESS
+ printk("%d IN, ", data_count);
+#endif
+ if (data_count == 1) {
+ Bytes_Read++;
+ *current_SC->SCp.ptr++ = inb(Read_FIFO_port);
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1; /* Number of words */
+ tmp_count = data_count << 1;
+ insw(Read_FIFO_port, current_SC->SCp.ptr, data_count);
+ current_SC->SCp.ptr += tmp_count;
+ Bytes_Read += tmp_count;
+ current_SC->SCp.this_residual -= tmp_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual && current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ }
+ }
+ }
+
+ if (done) {
+#if EVERY_ACCESS
+ printk(" ** IN DONE %d ** ", current_SC->SCp.have_data_in);
+#endif
+
+#if ERRORS_ONLY
+ if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
+ if ((unsigned char) (*((char *) current_SC->request_buffer + 2)) & 0x0f) {
+ unsigned char key;
+ unsigned char code;
+ unsigned char qualifier;
+
+ key = (unsigned char) (*((char *) current_SC->request_buffer + 2)) & 0x0f;
+ code = (unsigned char) (*((char *) current_SC->request_buffer + 12));
+ qualifier = (unsigned char) (*((char *) current_SC->request_buffer + 13));
+
+ if (key != UNIT_ATTENTION && !(key == NOT_READY && code == 0x04 && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
+ && !(key == ILLEGAL_REQUEST && (code == 0x25 || code == 0x24 || !code)))
+
+ printk("fd_mcs: REQUEST SENSE " "Key = %x, Code = %x, Qualifier = %x\n", key, code, qualifier);
+ }
+ }
+#endif
+#if EVERY_ACCESS
+ printk("BEFORE MY_DONE. . .");
+#endif
+ spin_lock_irqsave(shpnt->host_lock, flags);
+ my_done(shpnt, (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+#if EVERY_ACCESS
+ printk("RETURNING.\n");
+#endif
+
+ } else {
+ if (current_SC->SCp.phase & disconnect) {
+ outb(0xd0 | FIFO_COUNT, Interrupt_Cntl_port);
+ outb(0x00, SCSI_Cntl_port);
+ } else {
+ outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
+ }
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return IRQ_HANDLED;
+}
+
+static int fd_mcs_release(struct Scsi_Host *shpnt)
+{
+ int i, this_host, irq_usage;
+
+ release_region(shpnt->io_port, shpnt->n_io_port);
+
+ this_host = -1;
+ irq_usage = 0;
+ for (i = 0; i < found; i++) {
+ if (shpnt == hosts[i])
+ this_host = i;
+ if (shpnt->irq == hosts[i]->irq)
+ irq_usage++;
+ }
+
+ /* only for the last one */
+ if (1 == irq_usage)
+ free_irq(shpnt->irq, hosts);
+
+ found--;
+
+ for (i = this_host; i < found; i++)
+ hosts[i] = hosts[i + 1];
+
+ hosts[found] = NULL;
+
+ return 0;
+}
+
+static int fd_mcs_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+
+ if (in_command) {
+ panic("fd_mcs: fd_mcs_queue() NOT REENTRANT!\n");
+ }
+#if EVERY_ACCESS
+ printk("queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n", SCpnt->target, *(unsigned char *) SCpnt->cmnd, SCpnt->use_sg, SCpnt->request_bufflen);
+#endif
+
+ fd_mcs_make_bus_idle(shpnt);
+
+ SCpnt->scsi_done = done; /* Save this for the done function */
+ current_SC = SCpnt;
+
+ /* Initialize static data */
+
+ if (current_SC->use_sg) {
+ current_SC->SCp.buffer = (struct scatterlist *) current_SC->request_buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
+ } else {
+ current_SC->SCp.ptr = (char *) current_SC->request_buffer;
+ current_SC->SCp.this_residual = current_SC->request_bufflen;
+ current_SC->SCp.buffer = NULL;
+ current_SC->SCp.buffers_residual = 0;
+ }
+
+
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+ current_SC->SCp.have_data_in = 0;
+ current_SC->SCp.sent_command = 0;
+ current_SC->SCp.phase = in_arbitration;
+
+ /* Start arbitration */
+ outb(0x00, Interrupt_Cntl_port);
+ outb(0x00, SCSI_Cntl_port); /* Disable data drivers */
+ outb(adapter_mask, SCSI_Data_NoACK_port); /* Set our id bit */
+ in_command = 1;
+ outb(0x20, Interrupt_Cntl_port);
+ outb(0x14 | PARITY_MASK, TMC_Cntl_port); /* Start arbitration */
+
+ return 0;
+}
+
+#if DEBUG_ABORT || DEBUG_RESET
+static void fd_mcs_print_info(Scsi_Cmnd * SCpnt)
+{
+ unsigned int imr;
+ unsigned int irr;
+ unsigned int isr;
+ struct Scsi_Host *shpnt = SCpnt->host;
+
+ if (!SCpnt || !SCpnt->host) {
+ printk("fd_mcs: cannot provide detailed information\n");
+ }
+
+ printk("%s\n", fd_mcs_info(SCpnt->host));
+ print_banner(SCpnt->host);
+ switch (SCpnt->SCp.phase) {
+ case in_arbitration:
+ printk("arbitration ");
+ break;
+ case in_selection:
+ printk("selection ");
+ break;
+ case in_other:
+ printk("other ");
+ break;
+ default:
+ printk("unknown ");
+ break;
+ }
+
+ printk("(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n", SCpnt->SCp.phase, SCpnt->device->id, *(unsigned char *) SCpnt->cmnd, SCpnt->use_sg, SCpnt->request_bufflen);
+ printk("sent_command = %d, have_data_in = %d, timeout = %d\n", SCpnt->SCp.sent_command, SCpnt->SCp.have_data_in, SCpnt->timeout);
+#if DEBUG_RACE
+ printk("in_interrupt_flag = %d\n", in_interrupt_flag);
+#endif
+
+ imr = (inb(0x0a1) << 8) + inb(0x21);
+ outb(0x0a, 0xa0);
+ irr = inb(0xa0) << 8;
+ outb(0x0a, 0x20);
+ irr += inb(0x20);
+ outb(0x0b, 0xa0);
+ isr = inb(0xa0) << 8;
+ outb(0x0b, 0x20);
+ isr += inb(0x20);
+
+ /* Print out interesting information */
+ printk("IMR = 0x%04x", imr);
+ if (imr & (1 << shpnt->irq))
+ printk(" (masked)");
+ printk(", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr);
+
+ printk("SCSI Status = 0x%02x\n", inb(SCSI_Status_port));
+ printk("TMC Status = 0x%02x", inb(TMC_Status_port));
+ if (inb(TMC_Status_port) & 1)
+ printk(" (interrupt)");
+ printk("\n");
+ printk("Interrupt Status = 0x%02x", inb(Interrupt_Status_port));
+ if (inb(Interrupt_Status_port) & 0x08)
+ printk(" (enabled)");
+ printk("\n");
+ if (chip == tmc18c50 || chip == tmc18c30) {
+ printk("FIFO Status = 0x%02x\n", inb(shpnt->io_port + FIFO_Status));
+ printk("Int. Condition = 0x%02x\n", inb(shpnt->io_port + Interrupt_Cond));
+ }
+ printk("Configuration 1 = 0x%02x\n", inb(shpnt->io_port + Configuration1));
+ if (chip == tmc18c50 || chip == tmc18c30)
+ printk("Configuration 2 = 0x%02x\n", inb(shpnt->io_port + Configuration2));
+}
+#endif
+
+static int fd_mcs_abort(Scsi_Cmnd * SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+
+ unsigned long flags;
+#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
+ printk("fd_mcs: abort ");
+#endif
+
+ spin_lock_irqsave(shpnt->host_lock, flags);
+ if (!in_command) {
+#if EVERY_ACCESS || ERRORS_ONLY
+ printk(" (not in command)\n");
+#endif
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+ return FAILED;
+ } else
+ printk("\n");
+
+#if DEBUG_ABORT
+ fd_mcs_print_info(SCpnt);
+#endif
+
+ fd_mcs_make_bus_idle(shpnt);
+
+ current_SC->SCp.phase |= aborted;
+
+ current_SC->result = DID_ABORT << 16;
+
+ /* Aborts are not done well. . . */
+ my_done(shpnt, DID_ABORT << 16);
+
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+ return SUCCESS;
+}
+
+static int fd_mcs_host_reset(Scsi_Cmnd * SCpnt)
+{
+ return FAILED;
+}
+
+static int fd_mcs_device_reset(Scsi_Cmnd * SCpnt)
+{
+ return FAILED;
+}
+
+static int fd_mcs_bus_reset(Scsi_Cmnd * SCpnt) {
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+
+#if DEBUG_RESET
+ static int called_once = 0;
+#endif
+
+#if ERRORS_ONLY
+ if (SCpnt)
+ printk("fd_mcs: SCSI Bus Reset\n");
+#endif
+
+#if DEBUG_RESET
+ if (called_once)
+ fd_mcs_print_info(current_SC);
+ called_once = 1;
+#endif
+
+ outb(1, SCSI_Cntl_port);
+ do_pause(2);
+ outb(0, SCSI_Cntl_port);
+ do_pause(115);
+ outb(0, SCSI_Mode_Cntl_port);
+ outb(PARITY_MASK, TMC_Cntl_port);
+
+ /* Unless this is the very first call (i.e., SCPnt == NULL), everything
+ is probably hosed at this point. We will, however, try to keep
+ things going by informing the high-level code that we need help. */
+ return SUCCESS;
+}
+
+#include <scsi/scsi_ioctl.h>
+
+static int fd_mcs_biosparam(struct scsi_device * disk, struct block_device *bdev,
+ sector_t capacity, int *info_array)
+{
+ unsigned char *p = scsi_bios_ptable(bdev);
+ int size = capacity;
+
+ /* BIOS >= 3.4 for MCA cards */
+ /* This algorithm was provided by Future Domain (much thanks!). */
+
+ if (p && p[65] == 0xaa && p[64] == 0x55 /* Partition table valid */
+ && p[4]) { /* Partition type */
+ /* The partition table layout is as follows:
+
+ Start: 0x1b3h
+ Offset: 0 = partition status
+ 1 = starting head
+ 2 = starting sector and cylinder (word, encoded)
+ 4 = partition type
+ 5 = ending head
+ 6 = ending sector and cylinder (word, encoded)
+ 8 = starting absolute sector (double word)
+ c = number of sectors (double word)
+ Signature: 0x1fe = 0x55aa
+
+ So, this algorithm assumes:
+ 1) the first partition table is in use,
+ 2) the data in the first entry is correct, and
+ 3) partitions never divide cylinders
+
+ Note that (1) may be FALSE for NetBSD (and other BSD flavors),
+ as well as for Linux. Note also, that Linux doesn't pay any
+ attention to the fields that are used by this algorithm -- it
+ only uses the absolute sector data. Recent versions of Linux's
+ fdisk(1) will fill this data in correctly, and forthcoming
+ versions will check for consistency.
+
+ Checking for a non-zero partition type is not part of the
+ Future Domain algorithm, but it seemed to be a reasonable thing
+ to do, especially in the Linux and BSD worlds. */
+
+ info_array[0] = p[5] + 1; /* heads */
+ info_array[1] = p[6] & 0x3f; /* sectors */
+ } else {
+ /* Note that this new method guarantees that there will always be
+ less than 1024 cylinders on a platter. This is good for drives
+ up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
+ if ((unsigned int) size >= 0x7e0000U)
+ {
+ info_array[0] = 0xff; /* heads = 255 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else if ((unsigned int) size >= 0x200000U) {
+ info_array[0] = 0x80; /* heads = 128 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else {
+ info_array[0] = 0x40; /* heads = 64 */
+ info_array[1] = 0x20; /* sectors = 32 */
+ }
+ }
+ /* For both methods, compute the cylinders */
+ info_array[2] = (unsigned int) size / (info_array[0] * info_array[1]);
+ kfree(p);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "fd_mcs",
+ .proc_info = fd_mcs_proc_info,
+ .detect = fd_mcs_detect,
+ .release = fd_mcs_release,
+ .info = fd_mcs_info,
+ .queuecommand = fd_mcs_queue,
+ .eh_abort_handler = fd_mcs_abort,
+ .eh_bus_reset_handler = fd_mcs_bus_reset,
+ .eh_host_reset_handler = fd_mcs_host_reset,
+ .eh_device_reset_handler = fd_mcs_device_reset,
+ .bios_param = fd_mcs_biosparam,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = 64,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
new file mode 100644
index 000000000000..a843c080c1d8
--- /dev/null
+++ b/drivers/scsi/fdomain.c
@@ -0,0 +1,1739 @@
+/* fdomain.c -- Future Domain TMC-16x0 SCSI driver
+ * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
+ * Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
+ * Shared IRQ supported added 7/7/2001 Alan Cox <alan@redhat.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, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You 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.
+
+ **************************************************************************
+
+ SUMMARY:
+
+ Future Domain BIOS versions supported for autodetect:
+ 2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61
+ Chips are supported:
+ TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70
+ Boards supported:
+ Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
+ Future Domain TMC-3260 (PCI)
+ Quantum ISA-200S, ISA-250MG
+ Adaptec AHA-2920A (PCI) [BUT *NOT* AHA-2920C -- use aic7xxx instead]
+ IBM ?
+ LILO/INSMOD command-line options:
+ fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]
+
+
+
+ NOTE:
+
+ The Adaptec AHA-2920C has an Adaptec AIC-7850 chip on it.
+ Use the aic7xxx driver for this board.
+
+ The Adaptec AHA-2920A has a Future Domain chip on it, so this is the right
+ driver for that card. Unfortunately, the boxes will probably just say
+ "2920", so you'll have to look on the card for a Future Domain logo, or a
+ letter after the 2920.
+
+
+
+ THANKS:
+
+ Thanks to Adaptec for providing PCI boards for testing. This finally
+ enabled me to test the PCI detection and correct it for PCI boards that do
+ not have a BIOS at a standard ISA location. For PCI boards, LILO/INSMOD
+ command-line options should no longer be needed. --RF 18Nov98
+
+
+
+ DESCRIPTION:
+
+ This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
+ TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a
+ 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
+ high-density external connector. The 1670 and 1680 have floppy disk
+ controllers built in. The TMC-3260 is a PCI bus card.
+
+ Future Domain's older boards are based on the TMC-1800 chip, and this
+ driver was originally written for a TMC-1680 board with the TMC-1800 chip.
+ More recently, boards are being produced with the TMC-18C50 and TMC-18C30
+ chips. The latest and greatest board may not work with this driver. If
+ you have to patch this driver so that it will recognize your board's BIOS
+ signature, then the driver may fail to function after the board is
+ detected.
+
+ Please note that the drive ordering that Future Domain implemented in BIOS
+ versions 3.4 and 3.5 is the opposite of the order (currently) used by the
+ rest of the SCSI industry. If you have BIOS version 3.4 or 3.5, and have
+ more than one drive, then the drive ordering will be the reverse of that
+ which you see under DOS. For example, under DOS SCSI ID 0 will be D: and
+ SCSI ID 1 will be C: (the boot device). Under Linux, SCSI ID 0 will be
+ /dev/sda and SCSI ID 1 will be /dev/sdb. The Linux ordering is consistent
+ with that provided by all the other SCSI drivers for Linux. If you want
+ this changed, you will probably have to patch the higher level SCSI code.
+ If you do so, please send me patches that are protected by #ifdefs.
+
+ If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
+ your board. Please refer to the Seagate driver for more information and
+ possible support.
+
+
+
+ HISTORY:
+
+ Linux Driver Driver
+ Version Version Date Support/Notes
+
+ 0.0 3 May 1992 V2.0 BIOS; 1800 chip
+ 0.97 1.9 28 Jul 1992
+ 0.98.6 3.1 27 Nov 1992
+ 0.99 3.2 9 Dec 1992
+
+ 0.99.3 3.3 10 Jan 1993 V3.0 BIOS
+ 0.99.5 3.5 18 Feb 1993
+ 0.99.10 3.6 15 May 1993 V3.2 BIOS; 18C50 chip
+ 0.99.11 3.17 3 Jul 1993 (now under RCS)
+ 0.99.12 3.18 13 Aug 1993
+ 0.99.14 5.6 31 Oct 1993 (reselection code removed)
+
+ 0.99.15 5.9 23 Jan 1994 V3.4 BIOS (preliminary)
+ 1.0.8/1.1.1 5.15 1 Apr 1994 V3.4 BIOS; 18C30 chip (preliminary)
+ 1.0.9/1.1.3 5.16 7 Apr 1994 V3.4 BIOS; 18C30 chip
+ 1.1.38 5.18 30 Jul 1994 36C70 chip (PCI version of 18C30)
+ 1.1.62 5.20 2 Nov 1994 V3.5 BIOS
+ 1.1.73 5.22 7 Dec 1994 Quantum ISA-200S board; V2.0 BIOS
+
+ 1.1.82 5.26 14 Jan 1995 V3.5 BIOS; TMC-1610M/MER/MEX board
+ 1.2.10 5.28 5 Jun 1995 Quantum ISA-250MG board; V2.0, V2.01 BIOS
+ 1.3.4 5.31 23 Jun 1995 PCI BIOS-32 detection (preliminary)
+ 1.3.7 5.33 4 Jul 1995 PCI BIOS-32 detection
+ 1.3.28 5.36 17 Sep 1995 V3.61 BIOS; LILO command-line support
+ 1.3.34 5.39 12 Oct 1995 V3.60 BIOS; /proc
+ 1.3.72 5.39 8 Feb 1996 Adaptec AHA-2920 board
+ 1.3.85 5.41 4 Apr 1996
+ 2.0.12 5.44 8 Aug 1996 Use ID 7 for all PCI cards
+ 2.1.1 5.45 2 Oct 1996 Update ROM accesses for 2.1.x
+ 2.1.97 5.46 23 Apr 1998 Rewritten PCI detection routines [mj]
+ 2.1.11x 5.47 9 Aug 1998 Touched for 8 SCSI disk majors support
+ 5.48 18 Nov 1998 BIOS no longer needed for PCI detection
+ 2.2.0 5.50 28 Dec 1998 Support insmod parameters
+
+
+ REFERENCES USED:
+
+ "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
+ 1990.
+
+ "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
+ Corporation, January 1992.
+
+ "LXT SCSI Products: Specifications and OEM Technical Manual (Revision
+ B/September 1991)", Maxtor Corporation, 1991.
+
+ "7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
+
+ "Draft Proposed American National Standard: Small Computer System
+ Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
+ revision 10h, October 17, 1991)
+
+ Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
+ Youngdale (ericy@cais.com), 1992.
+
+ Private communication, Tuong Le (Future Domain Engineering department),
+ 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and
+ TMC-18C30 detection.)
+
+ Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page
+ 60 (2.39: Disk Partition Table Layout).
+
+ "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page
+ 6-1.
+
+
+
+ NOTES ON REFERENCES:
+
+ The Maxtor manuals were free. Maxtor telephone technical support is
+ great!
+
+ The Future Domain manuals were $25 and $35. They document the chip, not
+ the TMC-16x0 boards, so some information I had to guess at. In 1992,
+ Future Domain sold DOS BIOS source for $250 and the UN*X driver source was
+ $750, but these required a non-disclosure agreement, so even if I could
+ have afforded them, they would *not* have been useful for writing this
+ publically distributable driver. Future Domain technical support has
+ provided some information on the phone and have sent a few useful FAXs.
+ They have been much more helpful since they started to recognize that the
+ word "Linux" refers to an operating system :-).
+
+
+
+ ALPHA TESTERS:
+
+ There are many other alpha testers that come and go as the driver
+ develops. The people listed here were most helpful in times of greatest
+ need (mostly early on -- I've probably left out a few worthy people in
+ more recent times):
+
+ Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken
+ Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@bruin@sterbbs.nl), Sakari
+ Aaltonen (sakaria@vipunen.hit.fi), John Rice (rice@xanth.cs.odu.edu), Brad
+ Yearwood (brad@optilink.com), and Ray Toy (toy@soho.crd.ge.com).
+
+ Special thanks to Tien-Wan Yang (twyang@cs.uh.edu), who graciously lent me
+ his 18C50-based card for debugging. He is the sole reason that this
+ driver works with the 18C50 chip.
+
+ Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for
+ the version 3.4 BIOS.
+
+ Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing
+ patches that support the TMC-3260, a PCI bus card with the 36C70 chip.
+ The 36C70 chip appears to be "completely compatible" with the 18C30 chip.
+
+ Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the
+ patch for the version 3.5 BIOS.
+
+ Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the
+ patch for the Quantum ISA-200S SCSI adapter.
+
+ Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards, to
+ Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to some
+ random TMC-1680 repackaged by IBM; and to Mintak Ng (mintak@panix.com) for
+ the version 3.61 BIOS signature.
+
+ Thanks for Mark Singer (elf@netcom.com) and Richard Simpson
+ (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
+ work on the Quantum RAM layout.
+
+ Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
+ providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
+ card (a PCI bus card with the 36C70 chip). Please send James PCI-related
+ bug reports.
+
+ Thanks to Tom Cavin (tec@usa1.com) for preliminary command-line option
+ patches.
+
+ New PCI detection code written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+
+ Insmod parameter code based on patches from Daniel Graham
+ <graham@balance.uoregon.edu>.
+
+ All of the alpha testers deserve much thanks.
+
+
+
+ NOTES ON USER DEFINABLE OPTIONS:
+
+ DEBUG: This turns on the printing of various debug information.
+
+ ENABLE_PARITY: This turns on SCSI parity checking. With the current
+ driver, all attached devices must support SCSI parity. If none of your
+ devices support parity, then you can probably get the driver to work by
+ turning this option off. I have no way of testing this, however, and it
+ would appear that no one ever uses this option.
+
+ FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
+ 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
+ the SCSI device, an interrupt will be raised. Therefore, this could be as
+ low as 0, or as high as 16. Note, however, that values which are too high
+ or too low seem to prevent any interrupts from occurring, and thereby lock
+ up the machine. I have found that 2 is a good number, but throughput may
+ be increased by changing this value to values which are close to 2.
+ Please let me know if you try any different values.
+
+ RESELECTION: This is no longer an option, since I gave up trying to
+ implement it in version 4.x of this driver. It did not improve
+ performance at all and made the driver unstable (because I never found one
+ of the two race conditions which were introduced by the multiple
+ outstanding command code). The instability seems a very high price to pay
+ just so that you don't have to wait for the tape to rewind. If you want
+ this feature implemented, send me patches. I'll be happy to send a copy
+ of my (broken) driver to anyone who would like to see a copy.
+
+ **************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <scsi/scsicam.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include "fdomain.h"
+
+MODULE_AUTHOR("Rickard E. Faith");
+MODULE_DESCRIPTION("Future domain SCSI driver");
+MODULE_LICENSE("GPL");
+
+
+#define VERSION "$Revision: 5.51 $"
+
+/* START OF USER DEFINABLE OPTIONS */
+
+#define DEBUG 0 /* Enable debugging output */
+#define ENABLE_PARITY 1 /* Enable SCSI Parity */
+#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */
+
+/* END OF USER DEFINABLE OPTIONS */
+
+#if DEBUG
+#define EVERY_ACCESS 0 /* Write a line on every scsi access */
+#define ERRORS_ONLY 1 /* Only write a line if there is an error */
+#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
+#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
+#define DEBUG_ABORT 1 /* Debug abort() routine */
+#define DEBUG_RESET 1 /* Debug reset() routine */
+#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
+#else
+#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
+#define ERRORS_ONLY 0
+#define DEBUG_DETECT 0
+#define DEBUG_MESSAGES 0
+#define DEBUG_ABORT 0
+#define DEBUG_RESET 0
+#define DEBUG_RACE 0
+#endif
+
+/* Errors are reported on the line, so we don't need to report them again */
+#if EVERY_ACCESS
+#undef ERRORS_ONLY
+#define ERRORS_ONLY 0
+#endif
+
+#if ENABLE_PARITY
+#define PARITY_MASK 0x08
+#else
+#define PARITY_MASK 0x00
+#endif
+
+enum chip_type {
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+ tmc18c30 = 0x03,
+};
+
+enum {
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+enum in_port_type {
+ Read_SCSI_Data = 0,
+ SCSI_Status = 1,
+ TMC_Status = 2,
+ FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
+ Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
+ LSB_ID_Code = 5,
+ MSB_ID_Code = 6,
+ Read_Loopback = 7,
+ SCSI_Data_NoACK = 8,
+ Interrupt_Status = 9,
+ Configuration1 = 10,
+ Configuration2 = 11, /* tmc18c50/tmc18c30 only */
+ Read_FIFO = 12,
+ FIFO_Data_Count = 14
+};
+
+enum out_port_type {
+ Write_SCSI_Data = 0,
+ SCSI_Cntl = 1,
+ Interrupt_Cntl = 2,
+ SCSI_Mode_Cntl = 3,
+ TMC_Cntl = 4,
+ Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
+ Write_Loopback = 7,
+ IO_Control = 11, /* tmc18c30 only */
+ Write_FIFO = 12
+};
+
+/* .bss will zero all the static variables below */
+static int port_base;
+static unsigned long bios_base;
+static void __iomem * bios_mem;
+static int bios_major;
+static int bios_minor;
+static int PCI_bus;
+static int Quantum; /* Quantum board variant */
+static int interrupt_level;
+static volatile int in_command;
+static struct scsi_cmnd *current_SC;
+static enum chip_type chip = unknown;
+static int adapter_mask;
+static int this_id;
+static int setup_called;
+
+#if DEBUG_RACE
+static volatile int in_interrupt_flag;
+#endif
+
+static int FIFO_Size = 0x2000; /* 8k FIFO for
+ pre-tmc18c30 chips */
+
+static irqreturn_t do_fdomain_16x0_intr( int irq, void *dev_id,
+ struct pt_regs * regs );
+/* Allow insmod parameters to be like LILO parameters. For example:
+ insmod fdomain fdomain=0x140,11 */
+static char * fdomain = NULL;
+module_param(fdomain, charp, 0);
+
+static unsigned long addresses[] = {
+ 0xc8000,
+ 0xca000,
+ 0xce000,
+ 0xde000,
+ 0xcc000, /* Extra addresses for PCI boards */
+ 0xd0000,
+ 0xe0000,
+};
+#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
+
+static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+
+/*
+
+ READ THIS BEFORE YOU ADD A SIGNATURE!
+
+ READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
+
+ READ EVERY WORD, ESPECIALLY THE WORD *NOT*
+
+ This driver works *ONLY* for Future Domain cards using the TMC-1800,
+ TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670,
+ and 1680. These are all 16-bit cards.
+
+ The following BIOS signature signatures are for boards which do *NOT*
+ work with this driver (these TMC-8xx and TMC-9xx boards may work with the
+ Seagate driver):
+
+ FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
+ FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
+ FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
+ FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
+ FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
+ FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
+ FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
+
+ (The cards which do *NOT* work are all 8-bit cards -- although some of
+ them have a 16-bit form-factor, the upper 8-bits are used only for IRQs
+ and are *NOT* used for data. You can tell the difference by following
+ the tracings on the circuit board -- if only the IRQ lines are involved,
+ you have a "8-bit" card, and should *NOT* use this driver.)
+
+*/
+
+static struct signature {
+ const char *signature;
+ int sig_offset;
+ int sig_length;
+ int major_bios_version;
+ int minor_bios_version;
+ int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */
+} signatures[] = {
+ /* 1 2 3 4 5 6 */
+ /* 123456789012345678901234567890123456789012345678901234567890 */
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 2, 0, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 },
+ { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 },
+ { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0, 0 },
+ { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2, 0 },
+ { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 },
+ { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
+ { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
+ { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 },
+ { "IBM F1 P264/32", 5, 14, 3, -1, 1 },
+ /* This next signature may not be a 3.5 bios */
+ { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
+ { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 3, 6, 0 },
+ { "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 3, 6, 0 },
+ { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 },
+
+ /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE
+ Also, fix the disk geometry code for your signature and send your
+ changes for faith@cs.unc.edu. Above all, do *NOT* change any old
+ signatures!
+
+ Note that the last line will match a "generic" 18XX bios. Because
+ Future Domain has changed the host SCSI ID and/or the location of the
+ geometry information in the on-board RAM area for each of the first
+ three BIOS's, it is still important to enter a fully qualified
+ signature in the table for any new BIOS's (after the host SCSI ID and
+ geometry location are verified). */
+};
+
+#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+
+static void print_banner( struct Scsi_Host *shpnt )
+{
+ if (!shpnt) return; /* This won't ever happen */
+
+ if (bios_major < 0 && bios_minor < 0) {
+ printk(KERN_INFO "scsi%d: <fdomain> No BIOS; using scsi id %d\n",
+ shpnt->host_no, shpnt->this_id);
+ } else {
+ printk(KERN_INFO "scsi%d: <fdomain> BIOS version ", shpnt->host_no);
+
+ if (bios_major >= 0) printk("%d.", bios_major);
+ else printk("?.");
+
+ if (bios_minor >= 0) printk("%d", bios_minor);
+ else printk("?.");
+
+ printk( " at 0x%lx using scsi id %d\n",
+ bios_base, shpnt->this_id );
+ }
+
+ /* If this driver works for later FD PCI
+ boards, we will have to modify banner
+ for additional PCI cards, but for now if
+ it's PCI it's a TMC-3260 - JTM */
+ printk(KERN_INFO "scsi%d: <fdomain> %s chip at 0x%x irq ",
+ shpnt->host_no,
+ chip == tmc1800 ? "TMC-1800" : (chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30") : "Unknown")),
+ port_base);
+
+ if (interrupt_level)
+ printk("%d", interrupt_level);
+ else
+ printk("<none>");
+
+ printk( "\n" );
+}
+
+int fdomain_setup(char *str)
+{
+ int ints[4];
+
+ (void)get_options(str, ARRAY_SIZE(ints), ints);
+
+ if (setup_called++ || ints[0] < 2 || ints[0] > 3) {
+ printk(KERN_INFO "scsi: <fdomain> Usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n");
+ printk(KERN_ERR "scsi: <fdomain> Bad LILO/INSMOD parameters?\n");
+ return 0;
+ }
+
+ port_base = ints[0] >= 1 ? ints[1] : 0;
+ interrupt_level = ints[0] >= 2 ? ints[2] : 0;
+ this_id = ints[0] >= 3 ? ints[3] : 0;
+
+ bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */
+ ++setup_called;
+ return 1;
+}
+
+__setup("fdomain=", fdomain_setup);
+
+
+static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */
+{
+ mdelay(10*amount);
+}
+
+inline static void fdomain_make_bus_idle( void )
+{
+ outb(0, port_base + SCSI_Cntl);
+ outb(0, port_base + SCSI_Mode_Cntl);
+ if (chip == tmc18c50 || chip == tmc18c30)
+ outb(0x21 | PARITY_MASK, port_base + TMC_Cntl); /* Clear forced intr. */
+ else
+ outb(0x01 | PARITY_MASK, port_base + TMC_Cntl);
+}
+
+static int fdomain_is_valid_port( int port )
+{
+#if DEBUG_DETECT
+ printk( " (%x%x),",
+ inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
+#endif
+
+ /* The MCA ID is a unique id for each MCA compatible board. We
+ are using ISA boards, but Future Domain provides the MCA ID
+ anyway. We can use this ID to ensure that this is a Future
+ Domain TMC-1660/TMC-1680.
+ */
+
+ if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
+ if (inb( port + LSB_ID_Code ) != 0x27) return 0;
+ if (inb( port + MSB_ID_Code ) != 0x61) return 0;
+ chip = tmc1800;
+ } else { /* test for 0xe960 id */
+ if (inb( port + MSB_ID_Code ) != 0x60) return 0;
+ chip = tmc18c50;
+
+ /* Try to toggle 32-bit mode. This only
+ works on an 18c30 chip. (User reports
+ say this works, so we should switch to
+ it in the near future.) */
+
+ outb( 0x80, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
+ outb( 0x00, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+ }
+ }
+ /* If that failed, we are an 18c50. */
+ }
+
+ return 1;
+}
+
+static int fdomain_test_loopback( void )
+{
+ int i;
+ int result;
+
+ for (i = 0; i < 255; i++) {
+ outb( i, port_base + Write_Loopback );
+ result = inb( port_base + Read_Loopback );
+ if (i != result)
+ return 1;
+ }
+ return 0;
+}
+
+/* fdomain_get_irq assumes that we have a valid MCA ID for a
+ TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the
+ bios_base matches these ports. If someone was unlucky enough to have
+ purchased more than one Future Domain board, then they will have to
+ modify this code, as we only detect one board here. [The one with the
+ lowest bios_base.]
+
+ Note that this routine is only used for systems without a PCI BIOS32
+ (e.g., ISA bus). For PCI bus systems, this routine will likely fail
+ unless one of the IRQs listed in the ints array is used by the board.
+ Sometimes it is possible to use the computer's BIOS setup screen to
+ configure a PCI system so that one of these IRQs will be used by the
+ Future Domain card. */
+
+static int fdomain_get_irq( int base )
+{
+ int options = inb(base + Configuration1);
+
+#if DEBUG_DETECT
+ printk("scsi: <fdomain> Options = %x\n", options);
+#endif
+
+ /* Check for board with lowest bios_base --
+ this isn't valid for the 18c30 or for
+ boards on the PCI bus, so just assume we
+ have the right board. */
+
+ if (chip != tmc18c30 && !PCI_bus && addresses[(options & 0xc0) >> 6 ] != bios_base)
+ return 0;
+ return ints[(options & 0x0e) >> 1];
+}
+
+static int fdomain_isa_detect( int *irq, int *iobase )
+{
+#ifndef PCMCIA
+ int i, j;
+ int base = 0xdeadbeef;
+ int flag = 0;
+
+#if DEBUG_DETECT
+ printk( "scsi: <fdomain> fdomain_isa_detect:" );
+#endif
+
+ for (i = 0; i < ADDRESS_COUNT; i++) {
+ void __iomem *p = ioremap(addresses[i], 0x2000);
+ if (!p)
+ continue;
+#if DEBUG_DETECT
+ printk( " %lx(%lx),", addresses[i], bios_base );
+#endif
+ for (j = 0; j < SIGNATURE_COUNT; j++) {
+ if (check_signature(p + signatures[j].sig_offset,
+ signatures[j].signature,
+ signatures[j].sig_length )) {
+ bios_major = signatures[j].major_bios_version;
+ bios_minor = signatures[j].minor_bios_version;
+ PCI_bus = (signatures[j].flag == 1);
+ Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
+ bios_base = addresses[i];
+ bios_mem = p;
+ goto found;
+ }
+ }
+ iounmap(p);
+ }
+
+found:
+ if (bios_major == 2) {
+ /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
+ Assuming the ROM is enabled (otherwise we wouldn't have been
+ able to read the ROM signature :-), then the ROM sets up the
+ RAM area with some magic numbers, such as a list of port
+ base addresses and a list of the disk "geometry" reported to
+ DOS (this geometry has nothing to do with physical geometry).
+ */
+
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ case 3: /* ISA_250MG */
+ base = readb(bios_mem + 0x1fa2) + (readb(bios_mem + 0x1fa3) << 8);
+ break;
+ case 4: /* ISA_200S (another one) */
+ base = readb(bios_mem + 0x1fa3) + (readb(bios_mem + 0x1fa4) << 8);
+ break;
+ default:
+ base = readb(bios_mem + 0x1fcc) + (readb(bios_mem + 0x1fcd) << 8);
+ break;
+ }
+
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+
+ for (i = 0; i < PORT_COUNT; i++) {
+ if (base == ports[i]) {
+ if (!request_region(base, 0x10, "fdomain"))
+ break;
+ if (!fdomain_is_valid_port(base)) {
+ release_region(base, 0x10);
+ break;
+ }
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+ return 1;
+ }
+ }
+
+ /* This is a bad sign. It usually means that someone patched the
+ BIOS signature list (the signatures variable) to contain a BIOS
+ signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
+
+#if DEBUG_DETECT
+ printk( " RAM FAILED, " );
+#endif
+ }
+
+ /* Anyway, the alternative to finding the address in the RAM is to just
+ search through every possible port address for one that is attached
+ to the Future Domain card. Don't panic, though, about reading all
+ these random port addresses -- there are rumors that the Future
+ Domain BIOS does something very similar.
+
+ Do not, however, check ports which the kernel knows are being used by
+ another driver. */
+
+ for (i = 0; i < PORT_COUNT; i++) {
+ base = ports[i];
+ if (!request_region(base, 0x10, "fdomain")) {
+#if DEBUG_DETECT
+ printk( " (%x inuse),", base );
+#endif
+ continue;
+ }
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+ flag = fdomain_is_valid_port(base);
+ if (flag)
+ break;
+ release_region(base, 0x10);
+ }
+
+#if DEBUG_DETECT
+ if (flag) printk( " SUCCESS\n" );
+ else printk( " FAILURE\n" );
+#endif
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+
+ return 1; /* success */
+#else
+ return 0;
+#endif
+}
+
+/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int*
+ iobase) This function gets the Interrupt Level and I/O base address from
+ the PCI configuration registers. */
+
+#ifdef CONFIG_PCI
+static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_pdev )
+{
+ unsigned int pci_irq; /* PCI interrupt line */
+ unsigned long pci_base; /* PCI I/O base address */
+ struct pci_dev *pdev = NULL;
+
+#if DEBUG_DETECT
+ /* Tell how to print a list of the known PCI devices from bios32 and
+ list vendor and device IDs being used if in debug mode. */
+
+ printk( "scsi: <fdomain> INFO: use lspci -v to see list of PCI devices\n" );
+ printk( "scsi: <fdomain> TMC-3260 detect:"
+ " Using Vendor ID: 0x%x and Device ID: 0x%x\n",
+ PCI_VENDOR_ID_FD,
+ PCI_DEVICE_ID_FD_36C70 );
+#endif
+
+ if ((pdev = pci_find_device(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, pdev)) == NULL)
+ return 0;
+ if (pci_enable_device(pdev)) return 0;
+
+#if DEBUG_DETECT
+ printk( "scsi: <fdomain> TMC-3260 detect:"
+ " PCI bus %u, device %u, function %u\n",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+#endif
+
+ /* We now have the appropriate device function for the FD board so we
+ just read the PCI config info from the registers. */
+
+ pci_base = pci_resource_start(pdev, 0);
+ pci_irq = pdev->irq;
+
+ if (!request_region( pci_base, 0x10, "fdomain" ))
+ return 0;
+
+ /* Now we have the I/O base address and interrupt from the PCI
+ configuration registers. */
+
+ *irq = pci_irq;
+ *iobase = pci_base;
+ *ret_pdev = pdev;
+
+#if DEBUG_DETECT
+ printk( "scsi: <fdomain> TMC-3260 detect:"
+ " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base );
+#endif
+
+ if (!fdomain_is_valid_port(pci_base)) {
+ printk(KERN_ERR "scsi: <fdomain> PCI card detected, but driver not loaded (invalid port)\n" );
+ release_region(pci_base, 0x10);
+ return 0;
+ }
+
+ /* Fill in a few global variables. Ugh. */
+ bios_major = bios_minor = -1;
+ PCI_bus = 1;
+ Quantum = 0;
+ bios_base = 0;
+
+ return 1;
+}
+#endif
+
+struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt )
+{
+ int retcode;
+ struct Scsi_Host *shpnt;
+ struct pci_dev *pdev = NULL;
+
+ if (setup_called) {
+#if DEBUG_DETECT
+ printk( "scsi: <fdomain> No BIOS, using port_base = 0x%x, irq = %d\n",
+ port_base, interrupt_level );
+#endif
+ if (!request_region(port_base, 0x10, "fdomain")) {
+ printk( "scsi: <fdomain> port 0x%x is busy\n", port_base );
+ printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
+ return NULL;
+ }
+ if (!fdomain_is_valid_port( port_base )) {
+ printk( "scsi: <fdomain> Cannot locate chip at port base 0x%x\n",
+ port_base );
+ printk( "scsi: <fdomain> Bad LILO/INSMOD parameters?\n" );
+ release_region(port_base, 0x10);
+ return NULL;
+ }
+ } else {
+ int flag = 0;
+
+#ifdef CONFIG_PCI
+ /* Try PCI detection first */
+ flag = fdomain_pci_bios_detect( &interrupt_level, &port_base, &pdev );
+#endif
+ if (!flag) {
+ /* Then try ISA bus detection */
+ flag = fdomain_isa_detect( &interrupt_level, &port_base );
+
+ if (!flag) {
+ printk( "scsi: <fdomain> Detection failed (no card)\n" );
+ return NULL;
+ }
+ }
+ }
+
+ fdomain_16x0_bus_reset(NULL);
+
+ if (fdomain_test_loopback()) {
+ printk(KERN_ERR "scsi: <fdomain> Detection failed (loopback test failed at port base 0x%x)\n", port_base);
+ if (setup_called) {
+ printk(KERN_ERR "scsi: <fdomain> Bad LILO/INSMOD parameters?\n");
+ }
+ release_region(port_base, 0x10);
+ return NULL;
+ }
+
+ if (this_id) {
+ tpnt->this_id = (this_id & 0x07);
+ adapter_mask = (1 << tpnt->this_id);
+ } else {
+ if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
+ tpnt->this_id = 7;
+ adapter_mask = 0x80;
+ } else {
+ tpnt->this_id = 6;
+ adapter_mask = 0x40;
+ }
+ }
+
+/* Print out a banner here in case we can't
+ get resources. */
+
+ shpnt = scsi_register( tpnt, 0 );
+ if(shpnt == NULL) {
+ release_region(port_base, 0x10);
+ return NULL;
+ }
+ shpnt->irq = interrupt_level;
+ shpnt->io_port = port_base;
+ scsi_set_device(shpnt, &pdev->dev);
+ shpnt->n_io_port = 0x10;
+ print_banner( shpnt );
+
+ /* Log IRQ with kernel */
+ if (!interrupt_level) {
+ printk(KERN_ERR "scsi: <fdomain> Card Detected, but driver not loaded (no IRQ)\n" );
+ release_region(port_base, 0x10);
+ return NULL;
+ } else {
+ /* Register the IRQ with the kernel */
+
+ retcode = request_irq( interrupt_level,
+ do_fdomain_16x0_intr, pdev?SA_SHIRQ:0, "fdomain", shpnt);
+
+ if (retcode < 0) {
+ if (retcode == -EINVAL) {
+ printk(KERN_ERR "scsi: <fdomain> IRQ %d is bad!\n", interrupt_level );
+ printk(KERN_ERR " This shouldn't happen!\n" );
+ printk(KERN_ERR " Send mail to faith@acm.org\n" );
+ } else if (retcode == -EBUSY) {
+ printk(KERN_ERR "scsi: <fdomain> IRQ %d is already in use!\n", interrupt_level );
+ printk(KERN_ERR " Please use another IRQ!\n" );
+ } else {
+ printk(KERN_ERR "scsi: <fdomain> Error getting IRQ %d\n", interrupt_level );
+ printk(KERN_ERR " This shouldn't happen!\n" );
+ printk(KERN_ERR " Send mail to faith@acm.org\n" );
+ }
+ printk(KERN_ERR "scsi: <fdomain> Detected, but driver not loaded (IRQ)\n" );
+ release_region(port_base, 0x10);
+ return NULL;
+ }
+ }
+ return shpnt;
+}
+
+static int fdomain_16x0_detect(struct scsi_host_template *tpnt)
+{
+ if (fdomain)
+ fdomain_setup(fdomain);
+ return (__fdomain_16x0_detect(tpnt) != NULL);
+}
+
+static const char *fdomain_16x0_info( struct Scsi_Host *ignore )
+{
+ static char buffer[128];
+ char *pt;
+
+ strcpy( buffer, "Future Domain 16-bit SCSI Driver Version" );
+ if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
+ strcat( buffer, strchr( VERSION, ':' ) + 1 );
+ pt = strrchr( buffer, '$') - 1;
+ if (!pt) /* Stripped RCS Revision string? */
+ pt = buffer + strlen( buffer ) - 1;
+ if (*pt != ' ')
+ ++pt;
+ *pt = '\0';
+ } else { /* Assume VERSION is a number */
+ strcat( buffer, " " VERSION );
+ }
+
+ return buffer;
+}
+
+#if 0
+static int fdomain_arbitrate( void )
+{
+ int status = 0;
+ unsigned long timeout;
+
+#if EVERY_ACCESS
+ printk( "fdomain_arbitrate()\n" );
+#endif
+
+ outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */
+ outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */
+ outb(0x04 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */
+
+ timeout = 500;
+ do {
+ status = inb(port_base + TMC_Status); /* Read adapter status */
+ if (status & 0x02) /* Arbitration complete */
+ return 0;
+ mdelay(1); /* Wait one millisecond */
+ } while (--timeout);
+
+ /* Make bus idle */
+ fdomain_make_bus_idle();
+
+#if EVERY_ACCESS
+ printk( "Arbitration failed, status = %x\n", status );
+#endif
+#if ERRORS_ONLY
+ printk( "scsi: <fdomain> Arbitration failed, status = %x\n", status );
+#endif
+ return 1;
+}
+#endif
+
+static int fdomain_select( int target )
+{
+ int status;
+ unsigned long timeout;
+#if ERRORS_ONLY
+ static int flag = 0;
+#endif
+
+ outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */
+ outb(adapter_mask | (1 << target), port_base + SCSI_Data_NoACK);
+
+ /* Stop arbitration and enable parity */
+ outb(PARITY_MASK, port_base + TMC_Cntl);
+
+ timeout = 350; /* 350 msec */
+
+ do {
+ status = inb(port_base + SCSI_Status); /* Read adapter status */
+ if (status & 1) { /* Busy asserted */
+ /* Enable SCSI Bus (on error, should make bus idle with 0) */
+ outb(0x80, port_base + SCSI_Cntl);
+ return 0;
+ }
+ mdelay(1); /* wait one msec */
+ } while (--timeout);
+ /* Make bus idle */
+ fdomain_make_bus_idle();
+#if EVERY_ACCESS
+ if (!target) printk( "Selection failed\n" );
+#endif
+#if ERRORS_ONLY
+ if (!target) {
+ if (!flag) /* Skip first failure for all chips. */
+ ++flag;
+ else
+ printk( "scsi: <fdomain> Selection failed\n" );
+ }
+#endif
+ return 1;
+}
+
+static void my_done(int error)
+{
+ if (in_command) {
+ in_command = 0;
+ outb(0x00, port_base + Interrupt_Cntl);
+ fdomain_make_bus_idle();
+ current_SC->result = error;
+ if (current_SC->scsi_done)
+ current_SC->scsi_done( current_SC );
+ else panic( "scsi: <fdomain> current_SC->scsi_done() == NULL" );
+ } else {
+ panic( "scsi: <fdomain> my_done() called outside of command\n" );
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+}
+
+static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id,
+ struct pt_regs * regs )
+{
+ unsigned long flags;
+ int status;
+ int done = 0;
+ unsigned data_count;
+
+ /* The fdomain_16x0_intr is only called via
+ the interrupt handler. The goal of the
+ sti() here is to allow other
+ interruptions while this routine is
+ running. */
+
+ /* Check for other IRQ sources */
+ if ((inb(port_base + TMC_Status) & 0x01) == 0)
+ return IRQ_NONE;
+
+ /* It is our IRQ */
+ outb(0x00, port_base + Interrupt_Cntl);
+
+ /* We usually have one spurious interrupt after each command. Ignore it. */
+ if (!in_command || !current_SC) { /* Spurious interrupt */
+#if EVERY_ACCESS
+ printk( "Spurious interrupt, in_command = %d, current_SC = %x\n",
+ in_command, current_SC );
+#endif
+ return IRQ_NONE;
+ }
+
+ /* Abort calls my_done, so we do nothing here. */
+ if (current_SC->SCp.phase & aborted) {
+#if DEBUG_ABORT
+ printk( "scsi: <fdomain> Interrupt after abort, ignoring\n" );
+#endif
+ /*
+ return IRQ_HANDLED; */
+ }
+
+#if DEBUG_RACE
+ ++in_interrupt_flag;
+#endif
+
+ if (current_SC->SCp.phase & in_arbitration) {
+ status = inb(port_base + TMC_Status); /* Read adapter status */
+ if (!(status & 0x02)) {
+#if EVERY_ACCESS
+ printk( " AFAIL " );
+#endif
+ spin_lock_irqsave(current_SC->device->host->host_lock, flags);
+ my_done( DID_BUS_BUSY << 16 );
+ spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
+ return IRQ_HANDLED;
+ }
+ current_SC->SCp.phase = in_selection;
+
+ outb(0x40 | FIFO_COUNT, port_base + Interrupt_Cntl);
+
+ outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */
+ outb(adapter_mask | (1 << current_SC->device->id), port_base + SCSI_Data_NoACK);
+
+ /* Stop arbitration and enable parity */
+ outb(0x10 | PARITY_MASK, port_base + TMC_Cntl);
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return IRQ_HANDLED;
+ } else if (current_SC->SCp.phase & in_selection) {
+ status = inb(port_base + SCSI_Status);
+ if (!(status & 0x01)) {
+ /* Try again, for slow devices */
+ if (fdomain_select( current_SC->device->id )) {
+#if EVERY_ACCESS
+ printk( " SFAIL " );
+#endif
+ spin_lock_irqsave(current_SC->device->host->host_lock, flags);
+ my_done( DID_NO_CONNECT << 16 );
+ spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
+ return IRQ_HANDLED;
+ } else {
+#if EVERY_ACCESS
+ printk( " AltSel " );
+#endif
+ /* Stop arbitration and enable parity */
+ outb(0x10 | PARITY_MASK, port_base + TMC_Cntl);
+ }
+ }
+ current_SC->SCp.phase = in_other;
+ outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl);
+ outb(0x80, port_base + SCSI_Cntl);
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return IRQ_HANDLED;
+ }
+
+ /* current_SC->SCp.phase == in_other: this is the body of the routine */
+
+ status = inb(port_base + SCSI_Status);
+
+ if (status & 0x10) { /* REQ */
+
+ switch (status & 0x0e) {
+
+ case 0x08: /* COMMAND OUT */
+ outb(current_SC->cmnd[current_SC->SCp.sent_command++],
+ port_base + Write_SCSI_Data);
+#if EVERY_ACCESS
+ printk( "CMD = %x,",
+ current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
+#endif
+ break;
+ case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = -1;
+ outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl);
+ }
+ break;
+ case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = 1;
+ outb(0x90 | PARITY_MASK, port_base + TMC_Cntl);
+ }
+ break;
+ case 0x0c: /* STATUS IN */
+ current_SC->SCp.Status = inb(port_base + Read_SCSI_Data);
+#if EVERY_ACCESS
+ printk( "Status = %x, ", current_SC->SCp.Status );
+#endif
+#if ERRORS_ONLY
+ if (current_SC->SCp.Status
+ && current_SC->SCp.Status != 2
+ && current_SC->SCp.Status != 8) {
+ printk( "scsi: <fdomain> target = %d, command = %x, status = %x\n",
+ current_SC->device->id,
+ current_SC->cmnd[0],
+ current_SC->SCp.Status );
+ }
+#endif
+ break;
+ case 0x0a: /* MESSAGE OUT */
+ outb(MESSAGE_REJECT, port_base + Write_SCSI_Data); /* Reject */
+ break;
+ case 0x0e: /* MESSAGE IN */
+ current_SC->SCp.Message = inb(port_base + Read_SCSI_Data);
+#if EVERY_ACCESS
+ printk( "Message = %x, ", current_SC->SCp.Message );
+#endif
+ if (!current_SC->SCp.Message) ++done;
+#if DEBUG_MESSAGES || EVERY_ACCESS
+ if (current_SC->SCp.Message) {
+ printk( "scsi: <fdomain> message = %x\n",
+ current_SC->SCp.Message );
+ }
+#endif
+ break;
+ }
+ }
+
+ if (chip == tmc1800 && !current_SC->SCp.have_data_in
+ && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
+
+ if(current_SC->sc_data_direction == DMA_TO_DEVICE)
+ {
+ current_SC->SCp.have_data_in = -1;
+ outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl);
+ }
+ else
+ {
+ current_SC->SCp.have_data_in = 1;
+ outb(0x90 | PARITY_MASK, port_base + TMC_Cntl);
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
+ while ((data_count = FIFO_Size - inw(port_base + FIFO_Data_Count)) > 512) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count ) ;
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count > 0) {
+#if EVERY_ACCESS
+ printk( "%d OUT, ", data_count );
+#endif
+ if (data_count == 1) {
+ outb(*current_SC->SCp.ptr++, port_base + Write_FIFO);
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1;
+ outsw(port_base + Write_FIFO, current_SC->SCp.ptr, data_count);
+ current_SC->SCp.ptr += 2 * data_count;
+ current_SC->SCp.this_residual -= 2 * data_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual) {
+ if (current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ } else
+ break;
+ }
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
+ while ((data_count = inw(port_base + FIFO_Data_Count)) > 0) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count );
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count) {
+#if EVERY_ACCESS
+ printk( "%d IN, ", data_count );
+#endif
+ if (data_count == 1) {
+ *current_SC->SCp.ptr++ = inb(port_base + Read_FIFO);
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1; /* Number of words */
+ insw(port_base + Read_FIFO, current_SC->SCp.ptr, data_count);
+ current_SC->SCp.ptr += 2 * data_count;
+ current_SC->SCp.this_residual -= 2 * data_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual
+ && current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ }
+ }
+ }
+
+ if (done) {
+#if EVERY_ACCESS
+ printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
+#endif
+
+#if ERRORS_ONLY
+ if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
+ if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) {
+ unsigned char key;
+ unsigned char code;
+ unsigned char qualifier;
+
+ key = (unsigned char)(*((char *)current_SC->request_buffer + 2))
+ & 0x0f;
+ code = (unsigned char)(*((char *)current_SC->request_buffer + 12));
+ qualifier = (unsigned char)(*((char *)current_SC->request_buffer
+ + 13));
+
+ if (key != UNIT_ATTENTION
+ && !(key == NOT_READY
+ && code == 0x04
+ && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
+ && !(key == ILLEGAL_REQUEST && (code == 0x25
+ || code == 0x24
+ || !code)))
+
+ printk( "scsi: <fdomain> REQUEST SENSE"
+ " Key = %x, Code = %x, Qualifier = %x\n",
+ key, code, qualifier );
+ }
+ }
+#endif
+#if EVERY_ACCESS
+ printk( "BEFORE MY_DONE. . ." );
+#endif
+ spin_lock_irqsave(current_SC->device->host->host_lock, flags);
+ my_done( (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
+ spin_unlock_irqrestore(current_SC->device->host->host_lock, flags);
+#if EVERY_ACCESS
+ printk( "RETURNING.\n" );
+#endif
+
+ } else {
+ if (current_SC->SCp.phase & disconnect) {
+ outb(0xd0 | FIFO_COUNT, port_base + Interrupt_Cntl);
+ outb(0x00, port_base + SCSI_Cntl);
+ } else {
+ outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl);
+ }
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return IRQ_HANDLED;
+}
+
+static int fdomain_16x0_queue(struct scsi_cmnd *SCpnt,
+ void (*done)(struct scsi_cmnd *))
+{
+ if (in_command) {
+ panic( "scsi: <fdomain> fdomain_16x0_queue() NOT REENTRANT!\n" );
+ }
+#if EVERY_ACCESS
+ printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+#endif
+
+ fdomain_make_bus_idle();
+
+ current_SC = SCpnt; /* Save this for the done function */
+ current_SC->scsi_done = done;
+
+ /* Initialize static data */
+
+ if (current_SC->use_sg) {
+ current_SC->SCp.buffer =
+ (struct scatterlist *)current_SC->request_buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
+ } else {
+ current_SC->SCp.ptr = (char *)current_SC->request_buffer;
+ current_SC->SCp.this_residual = current_SC->request_bufflen;
+ current_SC->SCp.buffer = NULL;
+ current_SC->SCp.buffers_residual = 0;
+ }
+
+
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+ current_SC->SCp.have_data_in = 0;
+ current_SC->SCp.sent_command = 0;
+ current_SC->SCp.phase = in_arbitration;
+
+ /* Start arbitration */
+ outb(0x00, port_base + Interrupt_Cntl);
+ outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */
+ outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */
+ ++in_command;
+ outb(0x20, port_base + Interrupt_Cntl);
+ outb(0x14 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */
+
+ return 0;
+}
+
+#if DEBUG_ABORT
+static void print_info(struct scsi_cmnd *SCpnt)
+{
+ unsigned int imr;
+ unsigned int irr;
+ unsigned int isr;
+
+ if (!SCpnt || !SCpnt->device || !SCpnt->device->host) {
+ printk(KERN_WARNING "scsi: <fdomain> Cannot provide detailed information\n");
+ return;
+ }
+
+ printk(KERN_INFO "%s\n", fdomain_16x0_info( SCpnt->device->host ) );
+ print_banner(SCpnt->device->host);
+ switch (SCpnt->SCp.phase) {
+ case in_arbitration: printk("arbitration"); break;
+ case in_selection: printk("selection"); break;
+ case in_other: printk("other"); break;
+ default: printk("unknown"); break;
+ }
+
+ printk( " (%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->SCp.phase,
+ SCpnt->device->id,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+ printk( "sent_command = %d, have_data_in = %d, timeout = %d\n",
+ SCpnt->SCp.sent_command,
+ SCpnt->SCp.have_data_in,
+ SCpnt->timeout );
+#if DEBUG_RACE
+ printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
+#endif
+
+ imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
+ outb( 0x0a, 0xa0 );
+ irr = inb( 0xa0 ) << 8;
+ outb( 0x0a, 0x20 );
+ irr += inb( 0x20 );
+ outb( 0x0b, 0xa0 );
+ isr = inb( 0xa0 ) << 8;
+ outb( 0x0b, 0x20 );
+ isr += inb( 0x20 );
+
+ /* Print out interesting information */
+ printk( "IMR = 0x%04x", imr );
+ if (imr & (1 << interrupt_level))
+ printk( " (masked)" );
+ printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr );
+
+ printk( "SCSI Status = 0x%02x\n", inb(port_base + SCSI_Status));
+ printk( "TMC Status = 0x%02x", inb(port_base + TMC_Status));
+ if (inb((port_base + TMC_Status) & 1))
+ printk( " (interrupt)" );
+ printk( "\n" );
+ printk("Interrupt Status = 0x%02x", inb(port_base + Interrupt_Status));
+ if (inb(port_base + Interrupt_Status) & 0x08)
+ printk( " (enabled)" );
+ printk( "\n" );
+ if (chip == tmc18c50 || chip == tmc18c30) {
+ printk("FIFO Status = 0x%02x\n", inb(port_base + FIFO_Status));
+ printk( "Int. Condition = 0x%02x\n",
+ inb( port_base + Interrupt_Cond ) );
+ }
+ printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ printk( "Configuration 2 = 0x%02x\n",
+ inb( port_base + Configuration2 ) );
+}
+#endif
+
+static int fdomain_16x0_abort(struct scsi_cmnd *SCpnt)
+{
+#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
+ printk( "scsi: <fdomain> abort " );
+#endif
+
+ if (!in_command) {
+#if EVERY_ACCESS || ERRORS_ONLY
+ printk( " (not in command)\n" );
+#endif
+ return FAILED;
+ } else printk( "\n" );
+
+#if DEBUG_ABORT
+ print_info( SCpnt );
+#endif
+
+ fdomain_make_bus_idle();
+ current_SC->SCp.phase |= aborted;
+ current_SC->result = DID_ABORT << 16;
+
+ /* Aborts are not done well. . . */
+ my_done(DID_ABORT << 16);
+ return SUCCESS;
+}
+
+int fdomain_16x0_bus_reset(struct scsi_cmnd *SCpnt)
+{
+ outb(1, port_base + SCSI_Cntl);
+ do_pause( 2 );
+ outb(0, port_base + SCSI_Cntl);
+ do_pause( 115 );
+ outb(0, port_base + SCSI_Mode_Cntl);
+ outb(PARITY_MASK, port_base + TMC_Cntl);
+ return SUCCESS;
+}
+
+static int fdomain_16x0_biosparam(struct scsi_device *sdev,
+ struct block_device *bdev,
+ sector_t capacity, int *info_array)
+{
+ int drive;
+ int size = capacity;
+ unsigned long offset;
+ struct drive_info {
+ unsigned short cylinders;
+ unsigned char heads;
+ unsigned char sectors;
+ } i;
+
+ /* NOTES:
+ The RAM area starts at 0x1f00 from the bios_base address.
+
+ For BIOS Version 2.0:
+
+ The drive parameter table seems to start at 0x1f30.
+ The first byte's purpose is not known.
+ Next is the cylinder, head, and sector information.
+ The last 4 bytes appear to be the drive's size in sectors.
+ The other bytes in the drive parameter table are unknown.
+ If anyone figures them out, please send me mail, and I will
+ update these notes.
+
+ Tape drives do not get placed in this table.
+
+ There is another table at 0x1fea:
+ If the byte is 0x01, then the SCSI ID is not in use.
+ If the byte is 0x18 or 0x48, then the SCSI ID is in use,
+ although tapes don't seem to be in this table. I haven't
+ seen any other numbers (in a limited sample).
+
+ 0x1f2d is a drive count (i.e., not including tapes)
+
+ The table at 0x1fcc are I/O ports addresses for the various
+ operations. I calculate these by hand in this driver code.
+
+
+
+ For the ISA-200S version of BIOS Version 2.0:
+
+ The drive parameter table starts at 0x1f33.
+
+ WARNING: Assume that the table entry is 25 bytes long. Someone needs
+ to check this for the Quantum ISA-200S card.
+
+
+
+ For BIOS Version 3.2:
+
+ The drive parameter table starts at 0x1f70. Each entry is
+ 0x0a bytes long. Heads are one less than we need to report.
+ */
+
+ if (MAJOR(bdev->bd_dev) != SCSI_DISK0_MAJOR) {
+ printk("scsi: <fdomain> fdomain_16x0_biosparam: too many disks");
+ return 0;
+ }
+ drive = MINOR(bdev->bd_dev) >> 4;
+
+ if (bios_major == 2) {
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ /* The value of 25 has never been verified.
+ It should probably be 15. */
+ offset = 0x1f33 + drive * 25;
+ break;
+ case 3: /* ISA_250MG */
+ offset = 0x1f36 + drive * 15;
+ break;
+ case 4: /* ISA_200S (another one) */
+ offset = 0x1f34 + drive * 15;
+ break;
+ default:
+ offset = 0x1f31 + drive * 25;
+ break;
+ }
+ memcpy_fromio( &i, bios_mem + offset, sizeof( struct drive_info ) );
+ info_array[0] = i.heads;
+ info_array[1] = i.sectors;
+ info_array[2] = i.cylinders;
+ } else if (bios_major == 3
+ && bios_minor >= 0
+ && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
+ memcpy_fromio( &i, bios_mem + 0x1f71 + drive * 10,
+ sizeof( struct drive_info ) );
+ info_array[0] = i.heads + 1;
+ info_array[1] = i.sectors;
+ info_array[2] = i.cylinders;
+ } else { /* 3.4 BIOS (and up?) */
+ /* This algorithm was provided by Future Domain (much thanks!). */
+ unsigned char *p = scsi_bios_ptable(bdev);
+
+ if (p && p[65] == 0xaa && p[64] == 0x55 /* Partition table valid */
+ && p[4]) { /* Partition type */
+
+ /* The partition table layout is as follows:
+
+ Start: 0x1b3h
+ Offset: 0 = partition status
+ 1 = starting head
+ 2 = starting sector and cylinder (word, encoded)
+ 4 = partition type
+ 5 = ending head
+ 6 = ending sector and cylinder (word, encoded)
+ 8 = starting absolute sector (double word)
+ c = number of sectors (double word)
+ Signature: 0x1fe = 0x55aa
+
+ So, this algorithm assumes:
+ 1) the first partition table is in use,
+ 2) the data in the first entry is correct, and
+ 3) partitions never divide cylinders
+
+ Note that (1) may be FALSE for NetBSD (and other BSD flavors),
+ as well as for Linux. Note also, that Linux doesn't pay any
+ attention to the fields that are used by this algorithm -- it
+ only uses the absolute sector data. Recent versions of Linux's
+ fdisk(1) will fill this data in correctly, and forthcoming
+ versions will check for consistency.
+
+ Checking for a non-zero partition type is not part of the
+ Future Domain algorithm, but it seemed to be a reasonable thing
+ to do, especially in the Linux and BSD worlds. */
+
+ info_array[0] = p[5] + 1; /* heads */
+ info_array[1] = p[6] & 0x3f; /* sectors */
+ } else {
+
+ /* Note that this new method guarantees that there will always be
+ less than 1024 cylinders on a platter. This is good for drives
+ up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
+
+ if ((unsigned int)size >= 0x7e0000U) {
+ info_array[0] = 0xff; /* heads = 255 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else if ((unsigned int)size >= 0x200000U) {
+ info_array[0] = 0x80; /* heads = 128 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else {
+ info_array[0] = 0x40; /* heads = 64 */
+ info_array[1] = 0x20; /* sectors = 32 */
+ }
+ }
+ /* For both methods, compute the cylinders */
+ info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
+ kfree(p);
+ }
+
+ return 0;
+}
+
+static int fdomain_16x0_release(struct Scsi_Host *shpnt)
+{
+ if (shpnt->irq)
+ free_irq(shpnt->irq, shpnt);
+ if (shpnt->io_port && shpnt->n_io_port)
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ return 0;
+}
+
+struct scsi_host_template fdomain_driver_template = {
+ .module = THIS_MODULE,
+ .name = "fdomain",
+ .proc_name = "fdomain",
+ .detect = fdomain_16x0_detect,
+ .info = fdomain_16x0_info,
+ .queuecommand = fdomain_16x0_queue,
+ .eh_abort_handler = fdomain_16x0_abort,
+ .eh_bus_reset_handler = fdomain_16x0_bus_reset,
+ .bios_param = fdomain_16x0_biosparam,
+ .release = fdomain_16x0_release,
+ .can_queue = 1,
+ .this_id = 6,
+ .sg_tablesize = 64,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+#ifndef PCMCIA
+#define driver_template fdomain_driver_template
+#include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h
new file mode 100644
index 000000000000..47021d9d4fe4
--- /dev/null
+++ b/drivers/scsi/fdomain.h
@@ -0,0 +1,24 @@
+/*
+ * fdomain.c -- Future Domain TMC-16x0 SCSI driver
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You 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.
+ */
+
+extern struct scsi_host_template fdomain_driver_template;
+extern int fdomain_setup(char *str);
+extern struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt );
+extern int fdomain_16x0_bus_reset(struct scsi_cmnd *SCpnt);
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
new file mode 100644
index 000000000000..ca9d5bd26ca3
--- /dev/null
+++ b/drivers/scsi/g_NCR5380.c
@@ -0,0 +1,947 @@
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * NCR53C400A extensions (c) 1996, Ingmar Baumgart
+ * ingmar@gonzo.schwaben.de
+ *
+ * DTC3181E extensions (c) 1997, Ronald van Cuijlenborg
+ * ronald.van.cuijlenborg@tip.nl or nutty@dds.nl
+ *
+ * Added ISAPNP support for DTC436 adapters,
+ * Thomas Sailer, sailer@ife.ee.ethz.ch
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * TODO : flesh out DMA support, find some one actually using this (I have
+ * a memory mapped Trantor board that works fine)
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. With command line overrides - NCR5380=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 2. With the GENERIC_NCR5380_OVERRIDE compile time define. This is
+ * specified as an array of address, irq, dma, board tuples. Ie, for
+ * one board at 0x350, IRQ5, no dma, I could say
+ * -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE, BOARD_NCR5380}}
+ *
+ * -1 should be specified for no or DMA interrupt, -2 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ *
+ * 3. When included as a module, with arguments passed on the command line:
+ * ncr_irq=xx the interrupt
+ * ncr_addr=xx the port or base address (for port or memory
+ * mapped, resp.)
+ * ncr_dma=xx the DMA
+ * ncr_5380=1 to set up for a NCR5380 board
+ * ncr_53c400=1 to set up for a NCR53C400 board
+ * e.g.
+ * modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+ * for a port mapped NCR5380 board or
+ * modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
+ * for a memory mapped NCR53C400 board with interrupts disabled.
+ *
+ * 255 should be specified for no or DMA interrupt, 254 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ *
+ */
+
+/*
+ * $Log: generic_NCR5380.c,v $
+ */
+
+/* settings for DTC3181E card with only Mustek scanner attached */
+#define USLEEP
+#define USLEEP_POLL 1
+#define USLEEP_SLEEP 20
+#define USLEEP_WAITLONG 500
+
+#define AUTOPROBE_IRQ
+#define AUTOSENSE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_GENERIC_NCR53C400
+#define NCR53C400_PSEUDO_DMA 1
+#define PSEUDO_DMA
+#define NCR53C400
+#define NCR5380_STATS
+#undef NCR5380_STAT_LIMIT
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blkdev.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "g_NCR5380.h"
+#include "NCR5380.h"
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/isapnp.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#define NCR_NOT_SET 0
+static int ncr_irq = NCR_NOT_SET;
+static int ncr_dma = NCR_NOT_SET;
+static int ncr_addr = NCR_NOT_SET;
+static int ncr_5380 = NCR_NOT_SET;
+static int ncr_53c400 = NCR_NOT_SET;
+static int ncr_53c400a = NCR_NOT_SET;
+static int dtc_3181e = NCR_NOT_SET;
+
+static struct override {
+ NCR5380_implementation_fields;
+ int irq;
+ int dma;
+ int board; /* Use NCR53c400, Ricoh, etc. extensions ? */
+} overrides
+#ifdef GENERIC_NCR5380_OVERRIDE
+[] __initdata = GENERIC_NCR5380_OVERRIDE;
+#else
+[1] __initdata = { { 0,},};
+#endif
+
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+#ifndef MODULE
+
+/**
+ * internal_setup - handle lilo command string override
+ * @board: BOARD_* identifier for the board
+ * @str: unused
+ * @ints: numeric parameters
+ *
+ * Do LILO command line initialization of the overrides array. Display
+ * errors when needed
+ *
+ * Locks: none
+ */
+
+static void __init internal_setup(int board, char *str, int *ints)
+{
+ static int commandline_current = 0;
+ switch (board) {
+ case BOARD_NCR5380:
+ if (ints[0] != 2 && ints[0] != 3) {
+ printk(KERN_ERR "generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
+ return;
+ }
+ break;
+ case BOARD_NCR53C400:
+ if (ints[0] != 2) {
+ printk(KERN_ERR "generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
+ return;
+ }
+ break;
+ case BOARD_NCR53C400A:
+ if (ints[0] != 2) {
+ printk(KERN_ERR "generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n");
+ return;
+ }
+ break;
+ case BOARD_DTC3181E:
+ if (ints[0] != 2) {
+ printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n");
+ return;
+ }
+ break;
+ }
+
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type) ints[1];
+ overrides[commandline_current].irq = ints[2];
+ if (ints[0] == 3)
+ overrides[commandline_current].dma = ints[3];
+ else
+ overrides[commandline_current].dma = DMA_NONE;
+ overrides[commandline_current].board = board;
+ ++commandline_current;
+ }
+}
+
+
+/**
+ * do_NCR53C80_setup - set up entry point
+ * @str: unused
+ *
+ * Setup function invoked at boot to parse the ncr5380= command
+ * line.
+ */
+
+static int __init do_NCR5380_setup(char *str)
+{
+ int ints[10];
+
+ get_options(str, sizeof(ints) / sizeof(int), ints);
+ internal_setup(BOARD_NCR5380, str, ints);
+ return 1;
+}
+
+/**
+ * do_NCR53C400_setup - set up entry point
+ * @str: unused
+ * @ints: integer parameters from kernel setup code
+ *
+ * Setup function invoked at boot to parse the ncr53c400= command
+ * line.
+ */
+
+static int __init do_NCR53C400_setup(char *str)
+{
+ int ints[10];
+
+ get_options(str, sizeof(ints) / sizeof(int), ints);
+ internal_setup(BOARD_NCR53C400, str, ints);
+ return 1;
+}
+
+/**
+ * do_NCR53C400A_setup - set up entry point
+ * @str: unused
+ * @ints: integer parameters from kernel setup code
+ *
+ * Setup function invoked at boot to parse the ncr53c400a= command
+ * line.
+ */
+
+static int __init do_NCR53C400A_setup(char *str)
+{
+ int ints[10];
+
+ get_options(str, sizeof(ints) / sizeof(int), ints);
+ internal_setup(BOARD_NCR53C400A, str, ints);
+ return 1;
+}
+
+/**
+ * do_DTC3181E_setup - set up entry point
+ * @str: unused
+ * @ints: integer parameters from kernel setup code
+ *
+ * Setup function invoked at boot to parse the dtc3181e= command
+ * line.
+ */
+
+static int __init do_DTC3181E_setup(char *str)
+{
+ int ints[10];
+
+ get_options(str, sizeof(ints) / sizeof(int), ints);
+ internal_setup(BOARD_DTC3181E, str, ints);
+ return 1;
+}
+
+#endif
+
+/**
+ * generic_NCR5380_detect - look for NCR5380 controllers
+ * @tpnt: the scsi template
+ *
+ * Scan for the present of NCR5380, NCR53C400, NCR53C400A, DTC3181E
+ * and DTC436(ISAPnP) controllers. If overrides have been set we use
+ * them.
+ *
+ * The caller supplied NCR5380_init function is invoked from here, before
+ * the interrupt line is taken.
+ *
+ * Locks: none
+ */
+
+int __init generic_NCR5380_detect(Scsi_Host_Template * tpnt)
+{
+ static int current_override = 0;
+ int count, i;
+ unsigned int *ports;
+ unsigned long region_size = 16;
+ static unsigned int __initdata ncr_53c400a_ports[] = {
+ 0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
+ };
+ static unsigned int __initdata dtc_3181e_ports[] = {
+ 0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0
+ };
+ int flags = 0;
+ struct Scsi_Host *instance;
+
+ if (ncr_irq != NCR_NOT_SET)
+ overrides[0].irq = ncr_irq;
+ if (ncr_dma != NCR_NOT_SET)
+ overrides[0].dma = ncr_dma;
+ if (ncr_addr != NCR_NOT_SET)
+ overrides[0].NCR5380_map_name = (NCR5380_map_type) ncr_addr;
+ if (ncr_5380 != NCR_NOT_SET)
+ overrides[0].board = BOARD_NCR5380;
+ else if (ncr_53c400 != NCR_NOT_SET)
+ overrides[0].board = BOARD_NCR53C400;
+ else if (ncr_53c400a != NCR_NOT_SET)
+ overrides[0].board = BOARD_NCR53C400A;
+ else if (dtc_3181e != NCR_NOT_SET)
+ overrides[0].board = BOARD_DTC3181E;
+
+ if (!current_override && isapnp_present()) {
+ struct pnp_dev *dev = NULL;
+ count = 0;
+ while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
+ if (count >= NO_OVERRIDES)
+ break;
+ if (pnp_device_attach(dev) < 0) {
+ printk(KERN_ERR "dtc436e probe: attach failed\n");
+ continue;
+ }
+ if (pnp_activate_dev(dev) < 0) {
+ printk(KERN_ERR "dtc436e probe: activate failed\n");
+ pnp_device_detach(dev);
+ continue;
+ }
+ if (!pnp_port_valid(dev, 0)) {
+ printk(KERN_ERR "dtc436e probe: no valid port\n");
+ pnp_device_detach(dev);
+ continue;
+ }
+ if (pnp_irq_valid(dev, 0))
+ overrides[count].irq = pnp_irq(dev, 0);
+ else
+ overrides[count].irq = SCSI_IRQ_NONE;
+ if (pnp_dma_valid(dev, 0))
+ overrides[count].dma = pnp_dma(dev, 0);
+ else
+ overrides[count].dma = DMA_NONE;
+ overrides[count].NCR5380_map_name = (NCR5380_map_type) pnp_port_start(dev, 0);
+ overrides[count].board = BOARD_DTC3181E;
+ count++;
+ }
+ }
+
+ tpnt->proc_name = "g_NCR5380";
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ if (!(overrides[current_override].NCR5380_map_name))
+ continue;
+
+ ports = NULL;
+ switch (overrides[current_override].board) {
+ case BOARD_NCR5380:
+ flags = FLAG_NO_PSEUDO_DMA;
+ break;
+ case BOARD_NCR53C400:
+ flags = FLAG_NCR53C400;
+ break;
+ case BOARD_NCR53C400A:
+ flags = FLAG_NO_PSEUDO_DMA;
+ ports = ncr_53c400a_ports;
+ break;
+ case BOARD_DTC3181E:
+ flags = FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E;
+ ports = dtc_3181e_ports;
+ break;
+ }
+
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ if (ports) {
+ /* wakeup sequence for the NCR53C400A and DTC3181E */
+
+ /* Disable the adapter and look for a free io port */
+ outb(0x59, 0x779);
+ outb(0xb9, 0x379);
+ outb(0xc5, 0x379);
+ outb(0xae, 0x379);
+ outb(0xa6, 0x379);
+ outb(0x00, 0x379);
+
+ if (overrides[current_override].NCR5380_map_name != PORT_AUTO)
+ for (i = 0; ports[i]; i++) {
+ if (!request_region(ports[i], 16, "ncr53c80"))
+ continue;
+ if (overrides[current_override].NCR5380_map_name == ports[i])
+ break;
+ release_region(ports[i], 16);
+ } else
+ for (i = 0; ports[i]; i++) {
+ if (!request_region(ports[i], 16, "ncr53c80"))
+ continue;
+ if (inb(ports[i]) == 0xff)
+ break;
+ release_region(ports[i], 16);
+ }
+ if (ports[i]) {
+ /* At this point we have our region reserved */
+ outb(0x59, 0x779);
+ outb(0xb9, 0x379);
+ outb(0xc5, 0x379);
+ outb(0xae, 0x379);
+ outb(0xa6, 0x379);
+ outb(0x80 | i, 0x379); /* set io port to be used */
+ outb(0xc0, ports[i] + 9);
+ if (inb(ports[i] + 9) != 0x80)
+ continue;
+ else
+ overrides[current_override].NCR5380_map_name = ports[i];
+ } else
+ continue;
+ }
+ else
+ {
+ /* Not a 53C400A style setup - just grab */
+ if(!(request_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380")))
+ continue;
+ region_size = NCR5380_region_size;
+ }
+#else
+ if(!request_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size, "ncr5380"))
+ continue;
+#endif
+ instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
+ if (instance == NULL) {
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ release_region(overrides[current_override].NCR5380_map_name, region_size);
+#else
+ release_mem_region(overrides[current_override].NCR5380_map_name, NCR5380_region_size);
+#endif
+ continue;
+ }
+
+ instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ instance->n_io_port = region_size;
+#endif
+
+ NCR5380_init(instance, flags);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, 0xffff);
+
+ if (instance->irq != SCSI_IRQ_NONE)
+ if (request_irq(instance->irq, generic_NCR5380_intr, SA_INTERRUPT, "NCR5380", instance)) {
+ printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+ }
+
+ if (instance->irq == SCSI_IRQ_NONE) {
+ printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+ printk(KERN_INFO "scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int) instance->NCR5380_instance_name);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk(" interrupts disabled");
+ else
+ printk(" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/**
+ * generic_NCR5380_info - reporting string
+ * @host: NCR5380 to report on
+ *
+ * Report driver information for the NCR5380
+ */
+
+const char *generic_NCR5380_info(struct Scsi_Host *host)
+{
+ static const char string[] = "Generic NCR5380/53C400 Driver";
+ return string;
+}
+
+/**
+ * generic_NCR5380_release_resources - free resources
+ * @instance: host adapter to clean up
+ *
+ * Free the generic interface resources from this adapter.
+ *
+ * Locks: none
+ */
+
+int generic_NCR5380_release_resources(struct Scsi_Host *instance)
+{
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ if (instance->irq != SCSI_IRQ_NONE)
+ free_irq(instance->irq, NULL);
+ NCR5380_exit(instance);
+
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ release_region(instance->NCR5380_instance_name, instance->n_io_port);
+#else
+ release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size);
+#endif
+
+
+ return 0;
+}
+
+#ifdef BIOSPARAM
+/**
+ * generic_NCR5380_biosparam
+ * @disk: disk to compute geometry for
+ * @dev: device identifier for this disk
+ * @ip: sizes to fill in
+ *
+ * Generates a BIOS / DOS compatible H-C-S mapping for the specified
+ * device / size.
+ *
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Someone
+ * using hard disks on a trantor should verify that this mapping
+ * corresponds to that used by the BIOS / ASPI driver by running the linux
+ * fdisk program and matching the H_C_S coordinates to what DOS uses.
+ *
+ * Locks: none
+ */
+
+static int
+generic_NCR5380_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *ip)
+{
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = capacity >> 11;
+ return 0;
+}
+#endif
+
+#if NCR53C400_PSEUDO_DMA
+
+/**
+ * NCR5380_pread - pseudo DMA read
+ * @instance: adapter to read from
+ * @dst: buffer to read into
+ * @len: buffer length
+ *
+ * Perform a psuedo DMA mode read from an NCR53C400 or equivalent
+ * controller
+ */
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int bl;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE | CSR_TRANS_DIR);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+ if ((bl = NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+ break;
+ }
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk(KERN_ERR "53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY);
+
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ {
+ int i;
+ for (i = 0; i < 128; i++)
+ dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
+ }
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ isa_memcpy_fromio(dst + start, NCR53C400_host_buffer + NCR5380_map_name, 128);
+#endif
+ start += 128;
+ blocks--;
+ }
+
+ if (blocks) {
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ {
+ // FIXME - no timeout
+ }
+
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ {
+ int i;
+ for (i = 0; i < 128; i++)
+ dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
+ }
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ isa_memcpy_fromio(dst + start, NCR53C400_host_buffer + NCR5380_map_name, 128);
+#endif
+ start += 128;
+ blocks--;
+ }
+
+ if (!(NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ printk("53C400r: no 53C80 gated irq after transfer");
+
+#if 0
+ /*
+ * DON'T DO THIS - THEY NEVER ARRIVE!
+ */
+ printk("53C400r: Waiting for 53C80 registers\n");
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+#endif
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
+ printk(KERN_ERR "53C400r: no end dma signal\n");
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ return 0;
+}
+
+/**
+ * NCR5380_write - pseudo DMA write
+ * @instance: adapter to read from
+ * @dst: buffer to read into
+ * @len: buffer length
+ *
+ * Perform a psuedo DMA mode read from an NCR53C400 or equivalent
+ * controller
+ */
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int bl;
+ int i;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk(KERN_ERR "53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+
+ if ((bl = NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+ break;
+ }
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ; // FIXME - timeout
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ {
+ for (i = 0; i < 128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start + i]);
+ }
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ isa_memcpy_toio(NCR53C400_host_buffer + NCR5380_map_name, src + start, 128);
+#endif
+ start += 128;
+ blocks--;
+ }
+ if (blocks) {
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ; // FIXME - no timeout
+
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+ {
+ for (i = 0; i < 128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start + i]);
+ }
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ isa_memcpy_toio(NCR53C400_host_buffer + NCR5380_map_name, src + start, 128);
+#endif
+ start += 128;
+ blocks--;
+ }
+
+#if 0
+ printk("53C400w: waiting for registers to be available\n");
+ THEY NEVER DO ! while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG);
+ printk("53C400w: Got em\n");
+#endif
+
+ /* Let's wait for this instead - could be ugly */
+ /* All documentation says to check for this. Maybe my hardware is too
+ * fast. Waiting for it seems to work fine! KLL
+ */
+ while (!(i = NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ ; // FIXME - no timeout
+
+ /*
+ * I know. i is certainly != 0 here but the loop is new. See previous
+ * comment.
+ */
+ if (i) {
+ if (!((i = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_END_DMA_TRANSFER))
+ printk(KERN_ERR "53C400w: No END OF DMA bit - WHOOPS! BASR=%0x\n", i);
+ } else
+ printk(KERN_ERR "53C400w: no 53C80 gated irq after transfer (last block)\n");
+
+#if 0
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
+ printk(KERN_ERR "53C400w: no end dma signal\n");
+ }
+#endif
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+ ; // TIMEOUT
+ return 0;
+}
+#endif /* PSEUDO_DMA */
+
+/*
+ * Include the NCR5380 core code that we build our driver around
+ */
+
+#include "NCR5380.c"
+
+#define PRINTP(x) len += sprintf(buffer+len, x)
+#define ANDP ,
+
+static int sprint_opcode(char *buffer, int len, int opcode)
+{
+ int start = len;
+ PRINTP("0x%02x " ANDP opcode);
+ return len - start;
+}
+
+static int sprint_command(char *buffer, int len, unsigned char *command)
+{
+ int i, s, start = len;
+ len += sprint_opcode(buffer, len, command[0]);
+ for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ PRINTP("%02x " ANDP command[i]);
+ PRINTP("\n");
+ return len - start;
+}
+
+/**
+ * sprintf_Scsi_Cmnd - print a scsi command
+ * @buffer: buffr to print into
+ * @len: buffer length
+ * @cmd: SCSI command block
+ *
+ * Print out the target and command data in hex
+ */
+
+static int sprint_Scsi_Cmnd(char *buffer, int len, Scsi_Cmnd * cmd)
+{
+ int start = len;
+ PRINTP("host number %d destination target %d, lun %d\n" ANDP cmd->device->host->host_no ANDP cmd->device->id ANDP cmd->device->lun);
+ PRINTP(" command = ");
+ len += sprint_command(buffer, len, cmd->cmnd);
+ return len - start;
+}
+
+/**
+ * generic_NCR5380_proc_info - /proc for NCR5380 driver
+ * @buffer: buffer to print into
+ * @start: start position
+ * @offset: offset into buffer
+ * @len: length
+ * @hostno: instance to affect
+ * @inout: read/write
+ *
+ * Provide the procfs information for the 5380 controller. We fill
+ * this with useful debugging information including the commands
+ * being executed, disconnected command queue and the statistical
+ * data
+ *
+ * Locks: global cli/lock for queue walk
+ */
+
+static int generic_NCR5380_proc_info(struct Scsi_Host *scsi_ptr, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ int len = 0;
+ NCR5380_local_declare();
+ unsigned long flags;
+ unsigned char status;
+ int i;
+ Scsi_Cmnd *ptr;
+ struct NCR5380_hostdata *hostdata;
+#ifdef NCR5380_STATS
+ Scsi_Device *dev;
+ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
+#endif
+
+ NCR5380_setup(scsi_ptr);
+ hostdata = (struct NCR5380_hostdata *) scsi_ptr->hostdata;
+
+ spin_lock_irqsave(scsi_ptr->host_lock, flags);
+ PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
+ PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE);
+ PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE);
+#ifdef NCR53C400
+ PRINTP("NCR53C400 extension version %d\n" ANDP NCR53C400_PUBLIC_RELEASE);
+ PRINTP("NCR53C400 card%s detected\n" ANDP(((struct NCR5380_hostdata *) scsi_ptr->hostdata)->flags & FLAG_NCR53C400) ? "" : " not");
+# if NCR53C400_PSEUDO_DMA
+ PRINTP("NCR53C400 pseudo DMA used\n");
+# endif
+#else
+ PRINTP("NO NCR53C400 driver extensions\n");
+#endif
+ PRINTP("Using %s mapping at %s 0x%lx, " ANDP STRVAL(NCR5380_map_config) ANDP STRVAL(NCR5380_map_name) ANDP scsi_ptr->NCR5380_instance_name);
+ if (scsi_ptr->irq == SCSI_IRQ_NONE)
+ PRINTP("no interrupt\n");
+ else
+ PRINTP("on interrupt %d\n" ANDP scsi_ptr->irq);
+
+#ifdef NCR5380_STATS
+ if (hostdata->connected || hostdata->issue_queue || hostdata->disconnected_queue)
+ PRINTP("There are commands pending, transfer rates may be crud\n");
+ if (hostdata->pendingr)
+ PRINTP(" %d pending reads" ANDP hostdata->pendingr);
+ if (hostdata->pendingw)
+ PRINTP(" %d pending writes" ANDP hostdata->pendingw);
+ if (hostdata->pendingr || hostdata->pendingw)
+ PRINTP("\n");
+ shost_for_each_device(dev, scsi_ptr) {
+ unsigned long br = hostdata->bytes_read[dev->id];
+ unsigned long bw = hostdata->bytes_write[dev->id];
+ long tr = hostdata->time_read[dev->id] / HZ;
+ long tw = hostdata->time_write[dev->id] / HZ;
+
+ PRINTP(" T:%d %s " ANDP dev->id ANDP(dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int) dev->type] : "Unknown");
+ for (i = 0; i < 8; i++)
+ if (dev->vendor[i] >= 0x20)
+ *(buffer + (len++)) = dev->vendor[i];
+ *(buffer + (len++)) = ' ';
+ for (i = 0; i < 16; i++)
+ if (dev->model[i] >= 0x20)
+ *(buffer + (len++)) = dev->model[i];
+ *(buffer + (len++)) = ' ';
+ for (i = 0; i < 4; i++)
+ if (dev->rev[i] >= 0x20)
+ *(buffer + (len++)) = dev->rev[i];
+ *(buffer + (len++)) = ' ';
+
+ PRINTP("\n%10ld kb read in %5ld secs" ANDP br / 1024 ANDP tr);
+ if (tr)
+ PRINTP(" @ %5ld bps" ANDP br / tr);
+
+ PRINTP("\n%10ld kb written in %5ld secs" ANDP bw / 1024 ANDP tw);
+ if (tw)
+ PRINTP(" @ %5ld bps" ANDP bw / tw);
+ PRINTP("\n");
+ }
+#endif
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ PRINTP("REQ not asserted, phase unknown.\n");
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i);
+ PRINTP("Phase %s\n" ANDP phases[i].name);
+ }
+
+ if (!hostdata->connected) {
+ PRINTP("No currently connected command\n");
+ } else {
+ len += sprint_Scsi_Cmnd(buffer, len, (Scsi_Cmnd *) hostdata->connected);
+ }
+
+ PRINTP("issue_queue\n");
+
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ len += sprint_Scsi_Cmnd(buffer, len, ptr);
+
+ PRINTP("disconnected_queue\n");
+
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ len += sprint_Scsi_Cmnd(buffer, len, ptr);
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ spin_unlock_irqrestore(scsi_ptr->host_lock, flags);
+ return len;
+}
+
+#undef PRINTP
+#undef ANDP
+
+static Scsi_Host_Template driver_template = {
+ .proc_info = generic_NCR5380_proc_info,
+ .name = "Generic NCR5380/NCR53C400 Scsi Driver",
+ .detect = generic_NCR5380_detect,
+ .release = generic_NCR5380_release_resources,
+ .info = generic_NCR5380_info,
+ .queuecommand = generic_NCR5380_queue_command,
+ .eh_abort_handler = generic_NCR5380_abort,
+ .eh_bus_reset_handler = generic_NCR5380_bus_reset,
+ .eh_device_reset_handler = generic_NCR5380_device_reset,
+ .eh_host_reset_handler = generic_NCR5380_host_reset,
+ .bios_param = NCR5380_BIOSPARAM,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include <linux/module.h>
+#include "scsi_module.c"
+
+module_param(ncr_irq, int, 0);
+module_param(ncr_dma, int, 0);
+module_param(ncr_addr, int, 0);
+module_param(ncr_5380, int, 0);
+module_param(ncr_53c400, int, 0);
+module_param(ncr_53c400a, int, 0);
+module_param(dtc_3181e, int, 0);
+MODULE_LICENSE("GPL");
+
+
+static struct isapnp_device_id id_table[] __devinitdata = {
+ {
+ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e),
+ 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+
+__setup("ncr5380=", do_NCR5380_setup);
+__setup("ncr53c400=", do_NCR53C400_setup);
+__setup("ncr53c400a=", do_NCR53C400A_setup);
+__setup("dtc3181e=", do_DTC3181E_setup);
diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
new file mode 100644
index 000000000000..0c04cefb2a88
--- /dev/null
+++ b/drivers/scsi/g_NCR5380.h
@@ -0,0 +1,131 @@
+/*
+ * Generic Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: generic_NCR5380.h,v $
+ */
+
+#ifndef GENERIC_NCR5380_H
+#define GENERIC_NCR5380_H
+
+#include <linux/config.h>
+
+#define GENERIC_NCR5380_PUBLIC_RELEASE 1
+
+#ifdef NCR53C400
+#define BIOSPARAM
+#define NCR5380_BIOSPARAM generic_NCR5380_biosparam
+#else
+#define NCR5380_BIOSPARAM NULL
+#endif
+
+#ifndef ASM
+static int generic_NCR5380_abort(Scsi_Cmnd *);
+static int generic_NCR5380_detect(Scsi_Host_Template *);
+static int generic_NCR5380_release_resources(struct Scsi_Host *);
+static int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int generic_NCR5380_bus_reset(Scsi_Cmnd *);
+static int generic_NCR5380_host_reset(Scsi_Cmnd *);
+static int generic_NCR5380_device_reset(Scsi_Cmnd *);
+static const char* generic_NCR5380_info(struct Scsi_Host *);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifndef HOSTS_C
+
+#define __STRVAL(x) #x
+#define STRVAL(x) __STRVAL(x)
+
+#ifndef CONFIG_SCSI_G_NCR5380_MEM
+
+#define NCR5380_map_config port
+#define NCR5380_map_type int
+#define NCR5380_map_name port
+#define NCR5380_instance_name io_port
+#define NCR53C400_register_offset 0
+#define NCR53C400_address_adjust 8
+
+#ifdef NCR53C400
+#define NCR5380_region_size 16
+#else
+#define NCR5380_region_size 8
+#endif
+
+#define NCR5380_read(reg) (inb(NCR5380_map_name + (reg)))
+#define NCR5380_write(reg, value) (outb((value), (NCR5380_map_name + (reg))))
+
+#else
+/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+
+#define NCR5380_map_config memory
+#define NCR5380_map_type unsigned long
+#define NCR5380_map_name base
+#define NCR5380_instance_name base
+#define NCR53C400_register_offset 0x108
+#define NCR53C400_address_adjust 0
+#define NCR53C400_mem_base 0x3880
+#define NCR53C400_host_buffer 0x3900
+#define NCR5380_region_size 0x3a00
+
+#define NCR5380_read(reg) isa_readb(NCR5380_map_name + NCR53C400_mem_base + (reg))
+#define NCR5380_write(reg, value) isa_writeb(value, NCR5380_map_name + NCR53C400_mem_base + (reg))
+#endif
+
+#define NCR5380_implementation_fields \
+ NCR5380_map_type NCR5380_map_name
+
+#define NCR5380_local_declare() \
+ register NCR5380_implementation_fields
+
+#define NCR5380_setup(instance) \
+ NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
+
+#define NCR5380_intr generic_NCR5380_intr
+#define NCR5380_queue_command generic_NCR5380_queue_command
+#define NCR5380_abort generic_NCR5380_abort
+#define NCR5380_bus_reset generic_NCR5380_bus_reset
+#define NCR5380_device_reset generic_NCR5380_device_reset
+#define NCR5380_host_reset generic_NCR5380_host_reset
+#define NCR5380_pread generic_NCR5380_pread
+#define NCR5380_pwrite generic_NCR5380_pwrite
+#define NCR5380_proc_info notyet_generic_proc_info
+
+#define BOARD_NCR5380 0
+#define BOARD_NCR53C400 1
+#define BOARD_NCR53C400A 2
+#define BOARD_DTC3181E 3
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* GENERIC_NCR5380_H */
+
diff --git a/drivers/scsi/g_NCR5380_mmio.c b/drivers/scsi/g_NCR5380_mmio.c
new file mode 100644
index 000000000000..8cdde71ba0c8
--- /dev/null
+++ b/drivers/scsi/g_NCR5380_mmio.c
@@ -0,0 +1,10 @@
+/*
+ * There is probably a nicer way to do this but this one makes
+ * pretty obvious what is happening. We rebuild the same file with
+ * different options for mmio versus pio.
+ */
+
+#define SCSI_G_NCR5380_MEM
+
+#include "g_NCR5380.c"
+
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
new file mode 100644
index 000000000000..cc0cb246b1e4
--- /dev/null
+++ b/drivers/scsi/gdth.c
@@ -0,0 +1,5738 @@
+/************************************************************************
+ * Linux driver for *
+ * ICP vortex GmbH: GDT ISA/EISA/PCI Disk Array Controllers *
+ * Intel Corporation: Storage RAID Controllers *
+ * *
+ * gdth.c *
+ * Copyright (C) 1995-04 ICP vortex GmbH, Achim Leubner *
+ * Copyright (C) 2002-04 Intel Corporation *
+ * Copyright (C) 2003-04 Adaptec Inc. *
+ * <achim_leubner@adaptec.com> *
+ * *
+ * Additions/Fixes: *
+ * Boji Tony Kannanthanam <boji.t.kannanthanam@intel.com> *
+ * Johannes Dinner <johannes_dinner@adaptec.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 kernel; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ * Linux kernel 2.2.x, 2.4.x, 2.6.x supported *
+ * *
+ * $Log: gdth.c,v $
+ * Revision 1.73 2004/03/31 13:33:03 achim
+ * Special command 0xfd implemented to detect 64-bit DMA support
+ *
+ * Revision 1.72 2004/03/17 08:56:04 achim
+ * 64-bit DMA only enabled if FW >= x.43
+ *
+ * Revision 1.71 2004/03/05 15:51:29 achim
+ * Screen service: separate message buffer, bugfixes
+ *
+ * Revision 1.70 2004/02/27 12:19:07 achim
+ * Bugfix: Reset bit in config (0xfe) call removed
+ *
+ * Revision 1.69 2004/02/20 09:50:24 achim
+ * Compatibility changes for kernels < 2.4.20
+ * Bugfix screen service command size
+ * pci_set_dma_mask() error handling added
+ *
+ * Revision 1.68 2004/02/19 15:46:54 achim
+ * 64-bit DMA bugfixes
+ * Drive size bugfix for drives > 1TB
+ *
+ * Revision 1.67 2004/01/14 13:11:57 achim
+ * Tool access over /proc no longer supported
+ * Bugfixes IOCTLs
+ *
+ * Revision 1.66 2003/12/19 15:04:06 achim
+ * Bugfixes support for drives > 2TB
+ *
+ * Revision 1.65 2003/12/15 11:21:56 achim
+ * 64-bit DMA support added
+ * Support for drives > 2 TB implemented
+ * Kernels 2.2.x, 2.4.x, 2.6.x supported
+ *
+ * Revision 1.64 2003/09/17 08:30:26 achim
+ * EISA/ISA controller scan disabled
+ * Command line switch probe_eisa_isa added
+ *
+ * Revision 1.63 2003/07/12 14:01:00 Daniele Bellucci <bellucda@tiscali.it>
+ * Minor cleanups in gdth_ioctl.
+ *
+ * Revision 1.62 2003/02/27 15:01:59 achim
+ * Dynamic DMA mapping implemented
+ * New (character device) IOCTL interface added
+ * Other controller related changes made
+ *
+ * Revision 1.61 2002/11/08 13:09:52 boji
+ * Added support for XSCALE based RAID Controllers
+ * Fixed SCREENSERVICE initialization in SMP cases
+ * Added checks for gdth_polling before GDTH_HA_LOCK
+ *
+ * Revision 1.60 2002/02/05 09:35:22 achim
+ * MODULE_LICENSE only if kernel >= 2.4.11
+ *
+ * Revision 1.59 2002/01/30 09:46:33 achim
+ * Small changes
+ *
+ * Revision 1.58 2002/01/29 15:30:02 achim
+ * Set default value of shared_access to Y
+ * New status S_CACHE_RESERV for clustering added
+ *
+ * Revision 1.57 2001/08/21 11:16:35 achim
+ * Bugfix free_irq()
+ *
+ * Revision 1.56 2001/08/09 11:19:39 achim
+ * Scsi_Host_Template changes
+ *
+ * Revision 1.55 2001/08/09 10:11:28 achim
+ * Command HOST_UNFREEZE_IO before cache service init.
+ *
+ * Revision 1.54 2001/07/20 13:48:12 achim
+ * Expand: gdth_analyse_hdrive() removed
+ *
+ * Revision 1.53 2001/07/17 09:52:49 achim
+ * Small OEM related change
+ *
+ * Revision 1.52 2001/06/19 15:06:20 achim
+ * New host command GDT_UNFREEZE_IO added
+ *
+ * Revision 1.51 2001/05/22 06:42:37 achim
+ * PCI: Subdevice ID added
+ *
+ * Revision 1.50 2001/05/17 13:42:16 achim
+ * Support for Intel Storage RAID Controllers added
+ *
+ * Revision 1.50 2001/05/17 12:12:34 achim
+ * Support for Intel Storage RAID Controllers added
+ *
+ * Revision 1.49 2001/03/15 15:07:17 achim
+ * New __setup interface for boot command line options added
+ *
+ * Revision 1.48 2001/02/06 12:36:28 achim
+ * Bugfix Cluster protocol
+ *
+ * Revision 1.47 2001/01/10 14:42:06 achim
+ * New switch shared_access added
+ *
+ * Revision 1.46 2001/01/09 08:11:35 achim
+ * gdth_command() removed
+ * meaning of Scsi_Pointer members changed
+ *
+ * Revision 1.45 2000/11/16 12:02:24 achim
+ * Changes for kernel 2.4
+ *
+ * Revision 1.44 2000/10/11 08:44:10 achim
+ * Clustering changes: New flag media_changed added
+ *
+ * Revision 1.43 2000/09/20 12:59:01 achim
+ * DPMEM remap functions for all PCI controller types implemented
+ * Small changes for ia64 platform
+ *
+ * Revision 1.42 2000/07/20 09:04:50 achim
+ * Small changes for kernel 2.4
+ *
+ * Revision 1.41 2000/07/04 14:11:11 achim
+ * gdth_analyse_hdrive() added to rescan drives after online expansion
+ *
+ * Revision 1.40 2000/06/27 11:24:16 achim
+ * Changes Clustering, Screenservice
+ *
+ * Revision 1.39 2000/06/15 13:09:04 achim
+ * Changes for gdth_do_cmd()
+ *
+ * Revision 1.38 2000/06/15 12:08:43 achim
+ * Bugfix gdth_sync_event(), service SCREENSERVICE
+ * Data direction for command 0xc2 changed to DOU
+ *
+ * Revision 1.37 2000/05/25 13:50:10 achim
+ * New driver parameter virt_ctr added
+ *
+ * Revision 1.36 2000/05/04 08:50:46 achim
+ * Event buffer now in gdth_ha_str
+ *
+ * Revision 1.35 2000/03/03 10:44:08 achim
+ * New event_string only valid for the RP controller family
+ *
+ * Revision 1.34 2000/03/02 14:55:29 achim
+ * New mechanism for async. event handling implemented
+ *
+ * Revision 1.33 2000/02/21 15:37:37 achim
+ * Bugfix Alpha platform + DPMEM above 4GB
+ *
+ * Revision 1.32 2000/02/14 16:17:37 achim
+ * Bugfix sense_buffer[] + raw devices
+ *
+ * Revision 1.31 2000/02/10 10:29:00 achim
+ * Delete sense_buffer[0], if command OK
+ *
+ * Revision 1.30 1999/11/02 13:42:39 achim
+ * ARRAY_DRV_LIST2 implemented
+ * Now 255 log. and 100 host drives supported
+ *
+ * Revision 1.29 1999/10/05 13:28:47 achim
+ * GDT_CLUST_RESET added
+ *
+ * Revision 1.28 1999/08/12 13:44:54 achim
+ * MOUNTALL removed
+ * Cluster drives -> removeable drives
+ *
+ * Revision 1.27 1999/06/22 07:22:38 achim
+ * Small changes
+ *
+ * Revision 1.26 1999/06/10 16:09:12 achim
+ * Cluster Host Drive support: Bugfixes
+ *
+ * Revision 1.25 1999/06/01 16:03:56 achim
+ * gdth_init_pci(): Manipulate config. space to start RP controller
+ *
+ * Revision 1.24 1999/05/26 11:53:06 achim
+ * Cluster Host Drive support added
+ *
+ * Revision 1.23 1999/03/26 09:12:31 achim
+ * Default value for hdr_channel set to 0
+ *
+ * Revision 1.22 1999/03/22 16:27:16 achim
+ * Bugfix: gdth_store_event() must not be locked with GDTH_LOCK_HA()
+ *
+ * Revision 1.21 1999/03/16 13:40:34 achim
+ * Problems with reserved drives solved
+ * gdth_eh_bus_reset() implemented
+ *
+ * Revision 1.20 1999/03/10 09:08:13 achim
+ * Bugfix: Corrections in gdth_direction_tab[] made
+ * Bugfix: Increase command timeout (gdth_update_timeout()) NOT in gdth_putq()
+ *
+ * Revision 1.19 1999/03/05 14:38:16 achim
+ * Bugfix: Heads/Sectors mapping for reserved devices possibly wrong
+ * -> gdth_eval_mapping() implemented, changes in gdth_bios_param()
+ * INIT_RETRIES set to 100s to avoid DEINIT-Timeout for controllers
+ * with BIOS disabled and memory test set to Intensive
+ * Enhanced /proc support
+ *
+ * Revision 1.18 1999/02/24 09:54:33 achim
+ * Command line parameter hdr_channel implemented
+ * Bugfix for EISA controllers + Linux 2.2.x
+ *
+ * Revision 1.17 1998/12/17 15:58:11 achim
+ * Command line parameters implemented
+ * Changes for Alpha platforms
+ * PCI controller scan changed
+ * SMP support improved (spin_lock_irqsave(),...)
+ * New async. events, new scan/reserve commands included
+ *
+ * Revision 1.16 1998/09/28 16:08:46 achim
+ * GDT_PCIMPR: DPMEM remapping, if required
+ * mdelay() added
+ *
+ * Revision 1.15 1998/06/03 14:54:06 achim
+ * gdth_delay(), gdth_flush() implemented
+ * Bugfix: gdth_release() changed
+ *
+ * Revision 1.14 1998/05/22 10:01:17 achim
+ * mj: pcibios_strerror() removed
+ * Improved SMP support (if version >= 2.1.95)
+ * gdth_halt(): halt_called flag added (if version < 2.1)
+ *
+ * Revision 1.13 1998/04/16 09:14:57 achim
+ * Reserve drives (for raw service) implemented
+ * New error handling code enabled
+ * Get controller name from board_info() IOCTL
+ * Final round of PCI device driver patches by Martin Mares
+ *
+ * Revision 1.12 1998/03/03 09:32:37 achim
+ * Fibre channel controller support added
+ *
+ * Revision 1.11 1998/01/27 16:19:14 achim
+ * SA_SHIRQ added
+ * add_timer()/del_timer() instead of GDTH_TIMER
+ * scsi_add_timer()/scsi_del_timer() instead of SCSI_TIMER
+ * New error handling included
+ *
+ * Revision 1.10 1997/10/31 12:29:57 achim
+ * Read heads/sectors from host drive
+ *
+ * Revision 1.9 1997/09/04 10:07:25 achim
+ * IO-mapping with virt_to_bus(), gdth_readb(), gdth_writeb(), ...
+ * register_reboot_notifier() to get a notify on shutown used
+ *
+ * Revision 1.8 1997/04/02 12:14:30 achim
+ * Version 1.00 (see gdth.h), tested with kernel 2.0.29
+ *
+ * Revision 1.7 1997/03/12 13:33:37 achim
+ * gdth_reset() changed, new async. events
+ *
+ * Revision 1.6 1997/03/04 14:01:11 achim
+ * Shutdown routine gdth_halt() implemented
+ *
+ * Revision 1.5 1997/02/21 09:08:36 achim
+ * New controller included (RP, RP1, RP2 series)
+ * IOCTL interface implemented
+ *
+ * Revision 1.4 1996/07/05 12:48:55 achim
+ * Function gdth_bios_param() implemented
+ * New constant GDTH_MAXC_P_L inserted
+ * GDT_WRITE_THR, GDT_EXT_INFO implemented
+ * Function gdth_reset() changed
+ *
+ * Revision 1.3 1996/05/10 09:04:41 achim
+ * Small changes for Linux 1.2.13
+ *
+ * Revision 1.2 1996/05/09 12:45:27 achim
+ * Loadable module support implemented
+ * /proc support corrections made
+ *
+ * Revision 1.1 1996/04/11 07:35:57 achim
+ * Initial revision
+ *
+ ************************************************************************/
+
+/* All GDT Disk Array Controllers are fully supported by this driver.
+ * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the
+ * PCI Fibre Channel Disk Array Controllers. See gdth.h for a complete
+ * list of all controller types.
+ *
+ * If you have one or more GDT3000/3020 EISA controllers with
+ * controller BIOS disabled, you have to set the IRQ values with the
+ * command line option "gdth=irq1,irq2,...", where the irq1,irq2,... are
+ * the IRQ values for the EISA controllers.
+ *
+ * After the optional list of IRQ values, other possible
+ * command line options are:
+ * disable:Y disable driver
+ * disable:N enable driver
+ * reserve_mode:0 reserve no drives for the raw service
+ * reserve_mode:1 reserve all not init., removable drives
+ * reserve_mode:2 reserve all not init. drives
+ * reserve_list:h,b,t,l,h,b,t,l,... reserve particular drive(s) with
+ * h- controller no., b- channel no.,
+ * t- target ID, l- LUN
+ * reverse_scan:Y reverse scan order for PCI controllers
+ * reverse_scan:N scan PCI controllers like BIOS
+ * max_ids:x x - target ID count per channel (1..MAXID)
+ * rescan:Y rescan all channels/IDs
+ * rescan:N use all devices found until now
+ * virt_ctr:Y map every channel to a virtual controller
+ * virt_ctr:N use multi channel support
+ * hdr_channel:x x - number of virtual bus for host drives
+ * shared_access:Y disable driver reserve/release protocol to
+ * access a shared resource from several nodes,
+ * appropiate controller firmware required
+ * shared_access:N enable driver reserve/release protocol
+ * probe_eisa_isa:Y scan for EISA/ISA controllers
+ * probe_eisa_isa:N do not scan for EISA/ISA controllers
+ * force_dma32:Y use only 32 bit DMA mode
+ * force_dma32:N use 64 bit DMA mode, if supported
+ *
+ * The default values are: "gdth=disable:N,reserve_mode:1,reverse_scan:N,
+ * max_ids:127,rescan:N,virt_ctr:N,hdr_channel:0,
+ * shared_access:Y,probe_eisa_isa:N,force_dma32:N".
+ * Here is another example: "gdth=reserve_list:0,1,2,0,0,1,3,0,rescan:Y".
+ *
+ * When loading the gdth driver as a module, the same options are available.
+ * You can set the IRQs with "IRQ=...". However, the syntax to specify the
+ * options changes slightly. You must replace all ',' between options
+ * with ' ' and all ':' with '=' and you must use
+ * '1' in place of 'Y' and '0' in place of 'N'.
+ *
+ * Default: "modprobe gdth disable=0 reserve_mode=1 reverse_scan=0
+ * max_ids=127 rescan=0 virt_ctr=0 hdr_channel=0 shared_access=0
+ * probe_eisa_isa=0 force_dma32=0"
+ * The other example: "modprobe gdth reserve_list=0,1,2,0,0,1,3,0 rescan=1".
+ */
+
+/* The meaning of the Scsi_Pointer members in this driver is as follows:
+ * ptr: Chaining
+ * this_residual: Command priority
+ * buffer: phys. DMA sense buffer
+ * dma_handle: phys. DMA buffer (kernel >= 2.4.0)
+ * buffers_residual: Timeout value
+ * Status: Command status (gdth_do_cmd()), DMA mem. mappings
+ * Message: Additional info (gdth_do_cmd()), DMA direction
+ * have_data_in: Flag for gdth_wait_completion()
+ * sent_command: Opcode special command
+ * phase: Service/parameter/return code special command
+ */
+
+
+/* interrupt coalescing */
+/* #define INT_COAL */
+
+/* statistics */
+#define GDTH_STATISTICS
+
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#ifdef GDTH_RTC
+#include <linux/mc146818rtc.h>
+#endif
+#include <linux/reboot.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/spinlock.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/blkdev.h>
+#else
+#include <linux/blk.h>
+#include "sd.h"
+#endif
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "gdth.h"
+#include "gdth_kcompat.h"
+
+static void gdth_delay(int milliseconds);
+static void gdth_eval_mapping(ulong32 size, ulong32 *cyls, int *heads, int *secs);
+static irqreturn_t gdth_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
+static int gdth_async_event(int hanum);
+static void gdth_log_event(gdth_evt_data *dvr, char *buffer);
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority);
+static void gdth_next(int hanum);
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp);
+static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, ushort source,
+ ushort idx, gdth_evt_data *evt);
+static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr);
+static void gdth_readapp_event(gdth_ha_str *ha, unchar application,
+ gdth_evt_str *estr);
+static void gdth_clear_events(void);
+
+static void gdth_copy_internal_data(int hanum,Scsi_Cmnd *scp,
+ char *buffer,ushort count);
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp);
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive);
+
+static int gdth_search_eisa(ushort eisa_adr);
+static int gdth_search_isa(ulong32 bios_adr);
+static int gdth_search_pci(gdth_pci_str *pcistr);
+static void gdth_search_dev(gdth_pci_str *pcistr, ushort *cnt,
+ ushort vendor, ushort dev);
+static void gdth_sort_pci(gdth_pci_str *pcistr, int cnt);
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha);
+static int gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha);
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha);
+
+static void gdth_enable_int(int hanum);
+static int gdth_get_status(unchar *pIStatus,int irq);
+static int gdth_test_busy(int hanum);
+static int gdth_get_cmd_index(int hanum);
+static void gdth_release_event(int hanum);
+static int gdth_wait(int hanum,int index,ulong32 time);
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong32 p1,
+ ulong64 p2,ulong64 p3);
+static int gdth_search_drives(int hanum);
+static int gdth_analyse_hdrive(int hanum, ushort hdrive);
+
+static const char *gdth_ctr_name(int hanum);
+
+static int gdth_open(struct inode *inode, struct file *filep);
+static int gdth_close(struct inode *inode, struct file *filep);
+static int gdth_ioctl(struct inode *inode, struct file *filep,
+ unsigned int cmd, unsigned long arg);
+
+static void gdth_flush(int hanum);
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf);
+
+#ifdef DEBUG_GDTH
+static unchar DebugState = DEBUG_GDTH;
+
+#ifdef __SERIAL__
+#define MAX_SERBUF 160
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int ser_printk(const char *fmt, ...);
+static char strbuf[MAX_SERBUF+1];
+#ifdef __COM2__
+#define COM_BASE 0x2f8
+#else
+#define COM_BASE 0x3f8
+#endif
+static void ser_init()
+{
+ unsigned port=COM_BASE;
+
+ outb(0x80,port+3);
+ outb(0,port+1);
+ /* 19200 Baud, if 9600: outb(12,port) */
+ outb(6, port);
+ outb(3,port+3);
+ outb(0,port+1);
+ /*
+ ser_putc('I');
+ ser_putc(' ');
+ */
+}
+
+static void ser_puts(char *str)
+{
+ char *ptr;
+
+ ser_init();
+ for (ptr=str;*ptr;++ptr)
+ ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+ unsigned port=COM_BASE;
+
+ while ((inb(port+5) & 0x20)==0);
+ outb(c,port);
+ if (c==0x0a)
+ {
+ while ((inb(port+5) & 0x20)==0);
+ outb(0x0d,port);
+ }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args,fmt);
+ i = vsprintf(strbuf,fmt,args);
+ ser_puts(strbuf);
+ va_end(args);
+ return i;
+}
+
+#define TRACE(a) {if (DebugState==1) {ser_printk a;}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a;}}
+#define TRACE3(a) {if (DebugState!=0) {ser_printk a;}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a) {if (DebugState==1) {printk a;}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {printk a;}}
+#define TRACE3(a) {if (DebugState!=0) {printk a;}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+#ifdef GDTH_STATISTICS
+static ulong32 max_rq=0, max_index=0, max_sg=0;
+#ifdef INT_COAL
+static ulong32 max_int_coal=0;
+#endif
+static ulong32 act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+static struct timer_list gdth_timer;
+#endif
+
+#define PTR2USHORT(a) (ushort)(ulong)(a)
+#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b)
+#define INDEX_OK(i,t) ((i)<sizeof(t)/sizeof((t)[0]))
+
+#define NUMDATA(a) ( (gdth_num_str *)((a)->hostdata))
+#define HADATA(a) (&((gdth_ext_str *)((a)->hostdata))->haext)
+#define CMDDATA(a) (&((gdth_ext_str *)((a)->hostdata))->cmdext)
+
+#define BUS_L2P(a,b) ((b)>(a)->virt_bus ? (b-1):(b))
+
+#define gdth_readb(addr) readb(addr)
+#define gdth_readw(addr) readw(addr)
+#define gdth_readl(addr) readl(addr)
+#define gdth_writeb(b,addr) writeb((b),(addr))
+#define gdth_writew(b,addr) writew((b),(addr))
+#define gdth_writel(b,addr) writel((b),(addr))
+
+static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
+static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
+static unchar gdth_polling; /* polling if TRUE */
+static unchar gdth_from_wait = FALSE; /* gdth_wait() */
+static int wait_index,wait_hanum; /* gdth_wait() */
+static int gdth_ctr_count = 0; /* controller count */
+static int gdth_ctr_vcount = 0; /* virt. ctr. count */
+static int gdth_ctr_released = 0; /* gdth_release() */
+static struct Scsi_Host *gdth_ctr_tab[MAXHA]; /* controller table */
+static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS]; /* virt. ctr. table */
+static unchar gdth_write_through = FALSE; /* write through */
+static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */
+static int elastidx;
+static int eoldidx;
+static int major;
+
+#define DIN 1 /* IN data direction */
+#define DOU 2 /* OUT data direction */
+#define DNO DIN /* no data transfer */
+#define DUN DIN /* unknown data direction */
+static unchar gdth_direction_tab[0x100] = {
+ DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
+ DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
+ DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DOU,DNO,DUN,DIN,DOU,DOU,
+ DOU,DOU,DOU,DNO,DIN,DNO,DNO,DIN,DOU,DOU,DOU,DOU,DIN,DOU,DIN,DOU,
+ DOU,DOU,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DOU,DIN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DIN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DIN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DOU,DUN,DNO,DUN,DOU,DOU,
+ DOU,DOU,DOU,DNO,DUN,DIN,DOU,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
+};
+
+/* LILO and modprobe/insmod parameters */
+/* IRQ list for GDT3000/3020 EISA controllers */
+static int irq[MAXHA] __initdata =
+{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+/* disable driver flag */
+static int disable __initdata = 0;
+/* reserve flag */
+static int reserve_mode = 1;
+/* reserve list */
+static int reserve_list[MAX_RES_ARGS] =
+{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+/* scan order for PCI controllers */
+static int reverse_scan = 0;
+/* virtual channel for the host drives */
+static int hdr_channel = 0;
+/* max. IDs per channel */
+static int max_ids = MAXID;
+/* rescan all IDs */
+static int rescan = 0;
+/* map channels to virtual controllers */
+static int virt_ctr = 0;
+/* shared access */
+static int shared_access = 1;
+/* enable support for EISA and ISA controllers */
+static int probe_eisa_isa = 0;
+/* 64 bit DMA mode, support for drives > 2 TB, if force_dma32 = 0 */
+static int force_dma32 = 0;
+
+/* parameters for modprobe/insmod */
+module_param_array(irq, int, NULL, 0);
+module_param(disable, int, 0);
+module_param(reserve_mode, int, 0);
+module_param_array(reserve_list, int, NULL, 0);
+module_param(reverse_scan, int, 0);
+module_param(hdr_channel, int, 0);
+module_param(max_ids, int, 0);
+module_param(rescan, int, 0);
+module_param(virt_ctr, int, 0);
+module_param(shared_access, int, 0);
+module_param(probe_eisa_isa, int, 0);
+module_param(force_dma32, int, 0);
+MODULE_AUTHOR("Achim Leubner");
+MODULE_LICENSE("GPL");
+
+/* ioctl interface */
+static struct file_operations gdth_fops = {
+ .ioctl = gdth_ioctl,
+ .open = gdth_open,
+ .release = gdth_close,
+};
+
+#include "gdth_proc.h"
+#include "gdth_proc.c"
+
+/* notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block gdth_notifier = {
+ gdth_halt, NULL, 0
+};
+
+
+static void gdth_delay(int milliseconds)
+{
+ if (milliseconds == 0) {
+ udelay(1);
+ } else {
+ mdelay(milliseconds);
+ }
+}
+
+static void gdth_eval_mapping(ulong32 size, ulong32 *cyls, int *heads, int *secs)
+{
+ *cyls = size /HEADS/SECS;
+ if (*cyls <= MAXCYLS) {
+ *heads = HEADS;
+ *secs = SECS;
+ } else { /* too high for 64*32 */
+ *cyls = size /MEDHEADS/MEDSECS;
+ if (*cyls <= MAXCYLS) {
+ *heads = MEDHEADS;
+ *secs = MEDSECS;
+ } else { /* too high for 127*63 */
+ *cyls = size /BIGHEADS/BIGSECS;
+ *heads = BIGHEADS;
+ *secs = BIGSECS;
+ }
+ }
+}
+
+/* controller search and initialization functions */
+
+static int __init gdth_search_eisa(ushort eisa_adr)
+{
+ ulong32 id;
+
+ TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
+ id = inl(eisa_adr+ID0REG);
+ if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */
+ if ((inb(eisa_adr+EISAREG) & 8) == 0)
+ return 0; /* not EISA configured */
+ return 1;
+ }
+ if (id == GDT3_ID) /* GDT3000 */
+ return 1;
+
+ return 0;
+}
+
+
+static int __init gdth_search_isa(ulong32 bios_adr)
+{
+ void __iomem *addr;
+ ulong32 id;
+
+ TRACE(("gdth_search_isa() bios adr. %x\n",bios_adr));
+ if ((addr = ioremap(bios_adr+BIOS_ID_OFFS, sizeof(ulong32))) != NULL) {
+ id = gdth_readl(addr);
+ iounmap(addr);
+ if (id == GDT2_ID) /* GDT2000 */
+ return 1;
+ }
+ return 0;
+}
+
+
+static int __init gdth_search_pci(gdth_pci_str *pcistr)
+{
+ ushort device, cnt;
+
+ TRACE(("gdth_search_pci()\n"));
+
+ cnt = 0;
+ for (device = 0; device <= PCI_DEVICE_ID_VORTEX_GDT6555; ++device)
+ gdth_search_dev(pcistr, &cnt, PCI_VENDOR_ID_VORTEX, device);
+ for (device = PCI_DEVICE_ID_VORTEX_GDT6x17RP;
+ device <= PCI_DEVICE_ID_VORTEX_GDTMAXRP; ++device)
+ gdth_search_dev(pcistr, &cnt, PCI_VENDOR_ID_VORTEX, device);
+ gdth_search_dev(pcistr, &cnt, PCI_VENDOR_ID_VORTEX,
+ PCI_DEVICE_ID_VORTEX_GDTNEWRX);
+ gdth_search_dev(pcistr, &cnt, PCI_VENDOR_ID_VORTEX,
+ PCI_DEVICE_ID_VORTEX_GDTNEWRX2);
+ gdth_search_dev(pcistr, &cnt, PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_SRC);
+ gdth_search_dev(pcistr, &cnt, PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_SRC_XSCALE);
+ return cnt;
+}
+
+/* Vortex only makes RAID controllers.
+ * We do not really want to specify all 550 ids here, so wildcard match.
+ */
+static struct pci_device_id gdthtable[] __attribute_used__ = {
+ {PCI_VENDOR_ID_VORTEX,PCI_ANY_ID,PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_SRC,PCI_ANY_ID,PCI_ANY_ID},
+ {PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_SRC_XSCALE,PCI_ANY_ID,PCI_ANY_ID},
+ {0}
+};
+MODULE_DEVICE_TABLE(pci,gdthtable);
+
+static void __init gdth_search_dev(gdth_pci_str *pcistr, ushort *cnt,
+ ushort vendor, ushort device)
+{
+ ulong base0, base1, base2;
+ struct pci_dev *pdev;
+
+ TRACE(("gdth_search_dev() cnt %d vendor %x device %x\n",
+ *cnt, vendor, device));
+
+ pdev = NULL;
+ while ((pdev = pci_find_device(vendor, device, pdev))
+ != NULL) {
+ if (pci_enable_device(pdev))
+ continue;
+ if (*cnt >= MAXHA)
+ return;
+ /* GDT PCI controller found, resources are already in pdev */
+ pcistr[*cnt].pdev = pdev;
+ pcistr[*cnt].vendor_id = vendor;
+ pcistr[*cnt].device_id = device;
+ pcistr[*cnt].subdevice_id = pdev->subsystem_device;
+ pcistr[*cnt].bus = pdev->bus->number;
+ pcistr[*cnt].device_fn = pdev->devfn;
+ pcistr[*cnt].irq = pdev->irq;
+ base0 = pci_resource_flags(pdev, 0);
+ base1 = pci_resource_flags(pdev, 1);
+ base2 = pci_resource_flags(pdev, 2);
+ if (device <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000/B */
+ device >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */
+ if (!(base0 & IORESOURCE_MEM))
+ continue;
+ pcistr[*cnt].dpmem = pci_resource_start(pdev, 0);
+ } else { /* GDT6110, GDT6120, .. */
+ if (!(base0 & IORESOURCE_MEM) ||
+ !(base2 & IORESOURCE_MEM) ||
+ !(base1 & IORESOURCE_IO))
+ continue;
+ pcistr[*cnt].dpmem = pci_resource_start(pdev, 2);
+ pcistr[*cnt].io_mm = pci_resource_start(pdev, 0);
+ pcistr[*cnt].io = pci_resource_start(pdev, 1);
+ }
+ TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n",
+ pcistr[*cnt].bus, PCI_SLOT(pcistr[*cnt].device_fn),
+ pcistr[*cnt].irq, pcistr[*cnt].dpmem));
+ (*cnt)++;
+ }
+}
+
+
+static void __init gdth_sort_pci(gdth_pci_str *pcistr, int cnt)
+{
+ gdth_pci_str temp;
+ int i, changed;
+
+ TRACE(("gdth_sort_pci() cnt %d\n",cnt));
+ if (cnt == 0)
+ return;
+
+ do {
+ changed = FALSE;
+ for (i = 0; i < cnt-1; ++i) {
+ if (!reverse_scan) {
+ if ((pcistr[i].bus > pcistr[i+1].bus) ||
+ (pcistr[i].bus == pcistr[i+1].bus &&
+ PCI_SLOT(pcistr[i].device_fn) >
+ PCI_SLOT(pcistr[i+1].device_fn))) {
+ temp = pcistr[i];
+ pcistr[i] = pcistr[i+1];
+ pcistr[i+1] = temp;
+ changed = TRUE;
+ }
+ } else {
+ if ((pcistr[i].bus < pcistr[i+1].bus) ||
+ (pcistr[i].bus == pcistr[i+1].bus &&
+ PCI_SLOT(pcistr[i].device_fn) <
+ PCI_SLOT(pcistr[i+1].device_fn))) {
+ temp = pcistr[i];
+ pcistr[i] = pcistr[i+1];
+ pcistr[i+1] = temp;
+ changed = TRUE;
+ }
+ }
+ }
+ } while (changed);
+}
+
+
+static int __init gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
+{
+ ulong32 retries,id;
+ unchar prot_ver,eisacf,i,irq_found;
+
+ TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
+
+ /* disable board interrupts, deinitialize services */
+ outb(0xff,eisa_adr+EDOORREG);
+ outb(0x00,eisa_adr+EDENABREG);
+ outb(0x00,eisa_adr+EINTENABREG);
+
+ outb(0xff,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (inb(eisa_adr+EDOORREG) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (DEINIT failed)\n");
+ return 0;
+ }
+ gdth_delay(1);
+ TRACE2(("wait for DEINIT: retries=%d\n",retries));
+ }
+ prot_ver = inb(eisa_adr+MAILBOXREG);
+ outb(0xff,eisa_adr+EDOORREG);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-EISA: Illegal protocol version\n");
+ return 0;
+ }
+ ha->bmic = eisa_adr;
+ ha->brd_phys = (ulong32)eisa_adr >> 12;
+
+ outl(0,eisa_adr+MAILBOXREG);
+ outl(0,eisa_adr+MAILBOXREG+4);
+ outl(0,eisa_adr+MAILBOXREG+8);
+ outl(0,eisa_adr+MAILBOXREG+12);
+
+ /* detect IRQ */
+ if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
+ ha->oem_id = OEM_ID_ICP;
+ ha->type = GDT_EISA;
+ ha->stype = id;
+ outl(1,eisa_adr+MAILBOXREG+8);
+ outb(0xfe,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (inb(eisa_adr+EDOORREG) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (get IRQ failed)\n");
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ ha->irq = inb(eisa_adr+MAILBOXREG);
+ outb(0xff,eisa_adr+EDOORREG);
+ TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
+ /* check the result */
+ if (ha->irq == 0) {
+ TRACE2(("Unknown IRQ, use IRQ table from cmd line !\n"));
+ for (i = 0, irq_found = FALSE;
+ i < MAXHA && irq[i] != 0xff; ++i) {
+ if (irq[i]==10 || irq[i]==11 || irq[i]==12 || irq[i]==14) {
+ irq_found = TRUE;
+ break;
+ }
+ }
+ if (irq_found) {
+ ha->irq = irq[i];
+ irq[i] = 0;
+ printk("GDT-EISA: Can not detect controller IRQ,\n");
+ printk("Use IRQ setting from command line (IRQ = %d)\n",
+ ha->irq);
+ } else {
+ printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
+ printk("the controller BIOS or use command line parameters\n");
+ return 0;
+ }
+ }
+ } else {
+ eisacf = inb(eisa_adr+EISAREG) & 7;
+ if (eisacf > 4) /* level triggered */
+ eisacf -= 4;
+ ha->irq = gdth_irq_tab[eisacf];
+ ha->oem_id = OEM_ID_ICP;
+ ha->type = GDT_EISA;
+ ha->stype = id;
+ }
+
+ ha->dma64_support = 0;
+ return 1;
+}
+
+
+static int __init gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha)
+{
+ register gdt2_dpram_str __iomem *dp2_ptr;
+ int i;
+ unchar irq_drq,prot_ver;
+ ulong32 retries;
+
+ TRACE(("gdth_init_isa() bios adr. %x\n",bios_adr));
+
+ ha->brd = ioremap(bios_adr, sizeof(gdt2_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp2_ptr = ha->brd;
+ gdth_writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */
+ /* reset interface area */
+ memset_io(&dp2_ptr->u, 0, sizeof(dp2_ptr->u));
+ if (gdth_readl(&dp2_ptr->u) != 0) {
+ printk("GDT-ISA: Initialization error (DPMEM write error)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, read DRQ and IRQ */
+ gdth_writeb(0xff, &dp2_ptr->io.irqdel);
+ gdth_writeb(0x00, &dp2_ptr->io.irqen);
+ gdth_writeb(0x00, &dp2_ptr->u.ic.S_Status);
+ gdth_writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
+
+ irq_drq = gdth_readb(&dp2_ptr->io.rq);
+ for (i=0; i<3; ++i) {
+ if ((irq_drq & 1)==0)
+ break;
+ irq_drq >>= 1;
+ }
+ ha->drq = gdth_drq_tab[i];
+
+ irq_drq = gdth_readb(&dp2_ptr->io.rq) >> 3;
+ for (i=1; i<5; ++i) {
+ if ((irq_drq & 1)==0)
+ break;
+ irq_drq >>= 1;
+ }
+ ha->irq = gdth_irq_tab[i];
+
+ /* deinitialize services */
+ gdth_writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)gdth_readl(&dp2_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0, &dp2_ptr->u.ic.Status);
+ gdth_writeb(0xff, &dp2_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-ISA: Illegal protocol version\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ ha->oem_id = OEM_ID_ICP;
+ ha->type = GDT_ISA;
+ ha->ic_all_size = sizeof(dp2_ptr->u);
+ ha->stype= GDT2_ID;
+ ha->brd_phys = bios_adr >> 4;
+
+ /* special request to controller BIOS */
+ gdth_writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
+ gdth_writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
+ gdth_writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
+ gdth_writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
+ gdth_writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ gdth_writeb(0, &dp2_ptr->u.ic.Status);
+ gdth_writeb(0xff, &dp2_ptr->io.irqdel);
+
+ ha->dma64_support = 0;
+ return 1;
+}
+
+
+static int __init gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)
+{
+ register gdt6_dpram_str __iomem *dp6_ptr;
+ register gdt6c_dpram_str __iomem *dp6c_ptr;
+ register gdt6m_dpram_str __iomem *dp6m_ptr;
+ ulong32 retries;
+ unchar prot_ver;
+ ushort command;
+ int i, found = FALSE;
+
+ TRACE(("gdth_init_pci()\n"));
+
+ if (pcistr->vendor_id == PCI_VENDOR_ID_INTEL)
+ ha->oem_id = OEM_ID_INTEL;
+ else
+ ha->oem_id = OEM_ID_ICP;
+ ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8);
+ ha->stype = (ulong32)pcistr->device_id;
+ ha->subdevice_id = pcistr->subdevice_id;
+ ha->irq = pcistr->irq;
+ ha->pdev = pcistr->pdev;
+
+ if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000/B */
+ TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = ioremap(pcistr->dpmem, sizeof(gdt6_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ /* check and reset interface area */
+ dp6_ptr = ha->brd;
+ gdth_writel(DPMEM_MAGIC, &dp6_ptr->u);
+ if (gdth_readl(&dp6_ptr->u) != DPMEM_MAGIC) {
+ printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n",
+ pcistr->dpmem);
+ found = FALSE;
+ for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+ iounmap(ha->brd);
+ ha->brd = ioremap(i, sizeof(ushort));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ if (gdth_readw(ha->brd) != 0xffff) {
+ TRACE2(("init_pci_old() address 0x%x busy\n", i));
+ continue;
+ }
+ iounmap(ha->brd);
+ pci_write_config_dword(pcistr->pdev,
+ PCI_BASE_ADDRESS_0, i);
+ ha->brd = ioremap(i, sizeof(gdt6_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6_ptr = ha->brd;
+ gdth_writel(DPMEM_MAGIC, &dp6_ptr->u);
+ if (gdth_readl(&dp6_ptr->u) == DPMEM_MAGIC) {
+ printk("GDT-PCI: Use free address at 0x%x\n", i);
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ printk("GDT-PCI: No free address found!\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ }
+ memset_io(&dp6_ptr->u, 0, sizeof(dp6_ptr->u));
+ if (gdth_readl(&dp6_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ gdth_writeb(0xff, &dp6_ptr->io.irqdel);
+ gdth_writeb(0x00, &dp6_ptr->io.irqen);
+ gdth_writeb(0x00, &dp6_ptr->u.ic.S_Status);
+ gdth_writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
+
+ gdth_writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)gdth_readl(&dp6_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0, &dp6_ptr->u.ic.S_Status);
+ gdth_writeb(0xff, &dp6_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCI;
+ ha->ic_all_size = sizeof(dp6_ptr->u);
+
+ /* special command to controller BIOS */
+ gdth_writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
+ gdth_writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
+ gdth_writel(0x00, &dp6_ptr->u.ic.S_Info[2]);
+ gdth_writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
+ gdth_writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ gdth_writeb(0, &dp6_ptr->u.ic.S_Status);
+ gdth_writeb(0xff, &dp6_ptr->io.irqdel);
+
+ ha->dma64_support = 0;
+
+ } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, ... */
+ ha->plx = (gdt6c_plx_regs *)pcistr->io;
+ TRACE2(("init_pci_new() dpmem %lx irq %d\n",
+ pcistr->dpmem,ha->irq));
+ ha->brd = ioremap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ /* check and reset interface area */
+ dp6c_ptr = ha->brd;
+ gdth_writel(DPMEM_MAGIC, &dp6c_ptr->u);
+ if (gdth_readl(&dp6c_ptr->u) != DPMEM_MAGIC) {
+ printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n",
+ pcistr->dpmem);
+ found = FALSE;
+ for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+ iounmap(ha->brd);
+ ha->brd = ioremap(i, sizeof(ushort));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ if (gdth_readw(ha->brd) != 0xffff) {
+ TRACE2(("init_pci_plx() address 0x%x busy\n", i));
+ continue;
+ }
+ iounmap(ha->brd);
+ pci_write_config_dword(pcistr->pdev,
+ PCI_BASE_ADDRESS_2, i);
+ ha->brd = ioremap(i, sizeof(gdt6c_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6c_ptr = ha->brd;
+ gdth_writel(DPMEM_MAGIC, &dp6c_ptr->u);
+ if (gdth_readl(&dp6c_ptr->u) == DPMEM_MAGIC) {
+ printk("GDT-PCI: Use free address at 0x%x\n", i);
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ printk("GDT-PCI: No free address found!\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ }
+ memset_io(&dp6c_ptr->u, 0, sizeof(dp6c_ptr->u));
+ if (gdth_readl(&dp6c_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ outb(0x00,PTR2USHORT(&ha->plx->control1));
+ outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+
+ gdth_writeb(0x00, &dp6c_ptr->u.ic.S_Status);
+ gdth_writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
+
+ gdth_writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)gdth_readl(&dp6c_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0, &dp6c_ptr->u.ic.Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCINEW;
+ ha->ic_all_size = sizeof(dp6c_ptr->u);
+
+ /* special command to controller BIOS */
+ gdth_writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
+ gdth_writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
+ gdth_writel(0x00, &dp6c_ptr->u.ic.S_Info[2]);
+ gdth_writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
+ gdth_writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ gdth_writeb(0, &dp6c_ptr->u.ic.S_Status);
+
+ ha->dma64_support = 0;
+
+ } else { /* MPR */
+ TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = ioremap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+
+ /* manipulate config. space to enable DPMEM, start RP controller */
+ pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command);
+ command |= 6;
+ pci_write_config_word(pcistr->pdev, PCI_COMMAND, command);
+ if (pci_resource_start(pcistr->pdev, 8) == 1UL)
+ pci_resource_start(pcistr->pdev, 8) = 0UL;
+ i = 0xFEFF0001UL;
+ pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, i);
+ gdth_delay(1);
+ pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS,
+ pci_resource_start(pcistr->pdev, 8));
+
+ dp6m_ptr = ha->brd;
+
+ /* Ensure that it is safe to access the non HW portions of DPMEM.
+ * Aditional check needed for Xscale based RAID controllers */
+ while( ((int)gdth_readb(&dp6m_ptr->i960r.sema0_reg) ) & 3 )
+ gdth_delay(1);
+
+ /* check and reset interface area */
+ gdth_writel(DPMEM_MAGIC, &dp6m_ptr->u);
+ if (gdth_readl(&dp6m_ptr->u) != DPMEM_MAGIC) {
+ printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n",
+ pcistr->dpmem);
+ found = FALSE;
+ for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+ iounmap(ha->brd);
+ ha->brd = ioremap(i, sizeof(ushort));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ if (gdth_readw(ha->brd) != 0xffff) {
+ TRACE2(("init_pci_mpr() address 0x%x busy\n", i));
+ continue;
+ }
+ iounmap(ha->brd);
+ pci_write_config_dword(pcistr->pdev,
+ PCI_BASE_ADDRESS_0, i);
+ ha->brd = ioremap(i, sizeof(gdt6m_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6m_ptr = ha->brd;
+ gdth_writel(DPMEM_MAGIC, &dp6m_ptr->u);
+ if (gdth_readl(&dp6m_ptr->u) == DPMEM_MAGIC) {
+ printk("GDT-PCI: Use free address at 0x%x\n", i);
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ printk("GDT-PCI: No free address found!\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ }
+ memset_io(&dp6m_ptr->u, 0, sizeof(dp6m_ptr->u));
+
+ /* disable board interrupts, deinit services */
+ gdth_writeb(gdth_readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ gdth_writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ gdth_writeb(0x00, &dp6m_ptr->u.ic.S_Status);
+ gdth_writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
+
+ gdth_writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6m_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)gdth_readl(&dp6m_ptr->u.ic.S_Info[0]);
+ gdth_writeb(0, &dp6m_ptr->u.ic.S_Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCIMPR;
+ ha->ic_all_size = sizeof(dp6m_ptr->u);
+
+ /* special command to controller BIOS */
+ gdth_writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
+ gdth_writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
+ gdth_writel(0x00, &dp6m_ptr->u.ic.S_Info[2]);
+ gdth_writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
+ gdth_writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ gdth_writeb(0, &dp6m_ptr->u.ic.S_Status);
+
+ /* read FW version to detect 64-bit DMA support */
+ gdth_writeb(0xfd, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ gdth_writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (gdth_readb(&dp6m_ptr->u.ic.S_Status) != 0xfd) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ iounmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)(gdth_readl(&dp6m_ptr->u.ic.S_Info[0]) >> 16);
+ gdth_writeb(0, &dp6m_ptr->u.ic.S_Status);
+ if (prot_ver < 0x2b) /* FW < x.43: no 64-bit DMA support */
+ ha->dma64_support = 0;
+ else
+ ha->dma64_support = 1;
+ }
+
+ return 1;
+}
+
+
+/* controller protocol functions */
+
+static void __init gdth_enable_int(int hanum)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ gdt2_dpram_str __iomem *dp2_ptr;
+ gdt6_dpram_str __iomem *dp6_ptr;
+ gdt6m_dpram_str __iomem *dp6m_ptr;
+
+ TRACE(("gdth_enable_int() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ if (ha->type == GDT_EISA) {
+ outb(0xff, ha->bmic + EDOORREG);
+ outb(0xff, ha->bmic + EDENABREG);
+ outb(0x01, ha->bmic + EINTENABREG);
+ } else if (ha->type == GDT_ISA) {
+ dp2_ptr = ha->brd;
+ gdth_writeb(1, &dp2_ptr->io.irqdel);
+ gdth_writeb(0, &dp2_ptr->u.ic.Cmd_Index);
+ gdth_writeb(1, &dp2_ptr->io.irqen);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = ha->brd;
+ gdth_writeb(1, &dp6_ptr->io.irqdel);
+ gdth_writeb(0, &dp6_ptr->u.ic.Cmd_Index);
+ gdth_writeb(1, &dp6_ptr->io.irqen);
+ } else if (ha->type == GDT_PCINEW) {
+ outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+ outb(0x03, PTR2USHORT(&ha->plx->control1));
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = ha->brd;
+ gdth_writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ gdth_writeb(gdth_readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+
+
+static int gdth_get_status(unchar *pIStatus,int irq)
+{
+ register gdth_ha_str *ha;
+ int i;
+
+ TRACE(("gdth_get_status() irq %d ctr_count %d\n",
+ irq,gdth_ctr_count));
+
+ *pIStatus = 0;
+ for (i=0; i<gdth_ctr_count; ++i) {
+ ha = HADATA(gdth_ctr_tab[i]);
+ if (ha->irq != (unchar)irq) /* check IRQ */
+ continue;
+ if (ha->type == GDT_EISA)
+ *pIStatus = inb((ushort)ha->bmic + EDOORREG);
+ else if (ha->type == GDT_ISA)
+ *pIStatus =
+ gdth_readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCI)
+ *pIStatus =
+ gdth_readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCINEW)
+ *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg));
+ else if (ha->type == GDT_PCIMPR)
+ *pIStatus =
+ gdth_readb(&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.edoor_reg);
+
+ if (*pIStatus)
+ return i; /* board found */
+ }
+ return -1;
+}
+
+
+static int gdth_test_busy(int hanum)
+{
+ register gdth_ha_str *ha;
+ register int gdtsema0 = 0;
+
+ TRACE(("gdth_test_busy() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA)
+ gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
+ else if (ha->type == GDT_ISA)
+ gdtsema0 = (int)gdth_readb(&((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ gdtsema0 = (int)gdth_readb(&((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCINEW)
+ gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
+ else if (ha->type == GDT_PCIMPR)
+ gdtsema0 =
+ (int)gdth_readb(&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.sema0_reg);
+
+ return (gdtsema0 & 1);
+}
+
+
+static int gdth_get_cmd_index(int hanum)
+{
+ register gdth_ha_str *ha;
+ int i;
+
+ TRACE(("gdth_get_cmd_index() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ for (i=0; i<GDTH_MAXCMDS; ++i) {
+ if (ha->cmd_tab[i].cmnd == UNUSED_CMND) {
+ ha->cmd_tab[i].cmnd = ha->pccb->RequestBuffer;
+ ha->cmd_tab[i].service = ha->pccb->Service;
+ ha->pccb->CommandIndex = (ulong32)i+2;
+ return (i+2);
+ }
+ }
+ return 0;
+}
+
+
+static void gdth_set_sema0(int hanum)
+{
+ register gdth_ha_str *ha;
+
+ TRACE(("gdth_set_sema0() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA) {
+ outb(1, ha->bmic + SEMA0REG);
+ } else if (ha->type == GDT_ISA) {
+ gdth_writeb(1, &((gdt2_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
+ } else if (ha->type == GDT_PCI) {
+ gdth_writeb(1, &((gdt6_dpram_str __iomem *)ha->brd)->u.ic.Sema0);
+ } else if (ha->type == GDT_PCINEW) {
+ outb(1, PTR2USHORT(&ha->plx->sema0_reg));
+ } else if (ha->type == GDT_PCIMPR) {
+ gdth_writeb(1, &((gdt6m_dpram_str __iomem *)ha->brd)->i960r.sema0_reg);
+ }
+}
+
+
+static void gdth_copy_command(int hanum)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmd_ptr;
+ register gdt6m_dpram_str __iomem *dp6m_ptr;
+ register gdt6c_dpram_str __iomem *dp6c_ptr;
+ gdt6_dpram_str __iomem *dp6_ptr;
+ gdt2_dpram_str __iomem *dp2_ptr;
+ ushort cp_count,dp_offset,cmd_no;
+
+ TRACE(("gdth_copy_command() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cp_count = ha->cmd_len;
+ dp_offset= ha->cmd_offs_dpmem;
+ cmd_no = ha->cmd_cnt;
+ cmd_ptr = ha->pccb;
+
+ ++ha->cmd_cnt;
+ if (ha->type == GDT_EISA)
+ return; /* no DPMEM, no copy */
+
+ /* set cpcount dword aligned */
+ if (cp_count & 3)
+ cp_count += (4 - (cp_count & 3));
+
+ ha->cmd_offs_dpmem += cp_count;
+
+ /* set offset and service, copy command to DPMEM */
+ if (ha->type == GDT_ISA) {
+ dp2_ptr = ha->brd;
+ gdth_writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
+ gdth_writew((ushort)cmd_ptr->Service,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = ha->brd;
+ gdth_writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
+ gdth_writew((ushort)cmd_ptr->Service,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCINEW) {
+ dp6c_ptr = ha->brd;
+ gdth_writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
+ gdth_writew((ushort)cmd_ptr->Service,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = ha->brd;
+ gdth_writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
+ gdth_writew((ushort)cmd_ptr->Service,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ }
+}
+
+
+static void gdth_release_event(int hanum)
+{
+ register gdth_ha_str *ha;
+
+ TRACE(("gdth_release_event() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+#ifdef GDTH_STATISTICS
+ {
+ ulong32 i,j;
+ for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
+ if (ha->cmd_tab[j].cmnd != UNUSED_CMND)
+ ++i;
+ }
+ if (max_index < i) {
+ max_index = i;
+ TRACE3(("GDT: max_index = %d\n",(ushort)i));
+ }
+ }
+#endif
+
+ if (ha->pccb->OpCode == GDT_INIT)
+ ha->pccb->Service |= 0x80;
+
+ if (ha->type == GDT_EISA) {
+ if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */
+ outl(ha->ccb_phys, ha->bmic + MAILBOXREG);
+ outb(ha->pccb->Service, ha->bmic + LDOORREG);
+ } else if (ha->type == GDT_ISA) {
+ gdth_writeb(0, &((gdt2_dpram_str __iomem *)ha->brd)->io.event);
+ } else if (ha->type == GDT_PCI) {
+ gdth_writeb(0, &((gdt6_dpram_str __iomem *)ha->brd)->io.event);
+ } else if (ha->type == GDT_PCINEW) {
+ outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
+ } else if (ha->type == GDT_PCIMPR) {
+ gdth_writeb(1, &((gdt6m_dpram_str __iomem *)ha->brd)->i960r.ldoor_reg);
+ }
+}
+
+
+static int gdth_wait(int hanum,int index,ulong32 time)
+{
+ gdth_ha_str *ha;
+ int answer_found = FALSE;
+
+ TRACE(("gdth_wait() hanum %d index %d time %d\n",hanum,index,time));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (index == 0)
+ return 1; /* no wait required */
+
+ gdth_from_wait = TRUE;
+ do {
+ gdth_interrupt((int)ha->irq,ha,NULL);
+ if (wait_hanum==hanum && wait_index==index) {
+ answer_found = TRUE;
+ break;
+ }
+ gdth_delay(1);
+ } while (--time);
+ gdth_from_wait = FALSE;
+
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+
+ return (answer_found);
+}
+
+
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong32 p1,
+ ulong64 p2,ulong64 p3)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmd_ptr;
+ int retries,index;
+
+ TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmd_ptr = ha->pccb;
+ memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str));
+
+ /* make command */
+ for (retries = INIT_RETRIES;;) {
+ cmd_ptr->Service = service;
+ cmd_ptr->RequestBuffer = INTERNAL_CMND;
+ if (!(index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ gdth_set_sema0(hanum);
+ cmd_ptr->OpCode = opcode;
+ cmd_ptr->BoardNode = LOCALBOARD;
+ if (service == CACHESERVICE) {
+ if (opcode == GDT_IOCTL) {
+ cmd_ptr->u.ioctl.subfunc = p1;
+ cmd_ptr->u.ioctl.channel = (ulong32)p2;
+ cmd_ptr->u.ioctl.param_size = (ushort)p3;
+ cmd_ptr->u.ioctl.p_param = ha->scratch_phys;
+ } else {
+ if (ha->cache_feat & GDT_64BIT) {
+ cmd_ptr->u.cache64.DeviceNo = (ushort)p1;
+ cmd_ptr->u.cache64.BlockNo = p2;
+ } else {
+ cmd_ptr->u.cache.DeviceNo = (ushort)p1;
+ cmd_ptr->u.cache.BlockNo = (ulong32)p2;
+ }
+ }
+ } else if (service == SCSIRAWSERVICE) {
+ if (ha->raw_feat & GDT_64BIT) {
+ cmd_ptr->u.raw64.direction = p1;
+ cmd_ptr->u.raw64.bus = (unchar)p2;
+ cmd_ptr->u.raw64.target = (unchar)p3;
+ cmd_ptr->u.raw64.lun = (unchar)(p3 >> 8);
+ } else {
+ cmd_ptr->u.raw.direction = p1;
+ cmd_ptr->u.raw.bus = (unchar)p2;
+ cmd_ptr->u.raw.target = (unchar)p3;
+ cmd_ptr->u.raw.lun = (unchar)(p3 >> 8);
+ }
+ } else if (service == SCREENSERVICE) {
+ if (opcode == GDT_REALTIME) {
+ *(ulong32 *)&cmd_ptr->u.screen.su.data[0] = p1;
+ *(ulong32 *)&cmd_ptr->u.screen.su.data[4] = (ulong32)p2;
+ *(ulong32 *)&cmd_ptr->u.screen.su.data[8] = (ulong32)p3;
+ }
+ }
+ ha->cmd_len = sizeof(gdth_cmd_str);
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ gdth_delay(20);
+ if (!gdth_wait(hanum,index,INIT_TIMEOUT)) {
+ printk("GDT: Initialization error (timeout service %d)\n",service);
+ return 0;
+ }
+ if (ha->status != S_BSY || --retries == 0)
+ break;
+ gdth_delay(1);
+ }
+
+ return (ha->status != S_OK ? 0:1);
+}
+
+
+/* search for devices */
+
+static int __init gdth_search_drives(int hanum)
+{
+ register gdth_ha_str *ha;
+ ushort cdev_cnt, i;
+ int ok;
+ ulong32 bus_no, drv_cnt, drv_no, j;
+ gdth_getch_str *chn;
+ gdth_drlist_str *drl;
+ gdth_iochan_str *ioc;
+ gdth_raw_iochan_str *iocr;
+ gdth_arcdl_str *alst;
+ gdth_alist_str *alst2;
+ gdth_oem_str_ioctl *oemstr;
+#ifdef INT_COAL
+ gdth_perf_modes *pmod;
+#endif
+
+#ifdef GDTH_RTC
+ unchar rtc[12];
+ ulong flags;
+#endif
+
+ TRACE(("gdth_search_drives() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ ok = 0;
+
+ /* initialize controller services, at first: screen service */
+ ha->screen_feat = 0;
+ if (!force_dma32) {
+ ok = gdth_internal_cmd(hanum,SCREENSERVICE,GDT_X_INIT_SCR,0,0,0);
+ if (ok)
+ ha->screen_feat = GDT_64BIT;
+ }
+ if (force_dma32 || (!ok && ha->status == (ushort)S_NOFUNC))
+ ok = gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0);
+ if (!ok) {
+ printk("GDT-HA %d: Initialization error screen service (code %d)\n",
+ hanum, ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
+
+#ifdef GDTH_RTC
+ /* read realtime clock info, send to controller */
+ /* 1. wait for the falling edge of update flag */
+ spin_lock_irqsave(&rtc_lock, flags);
+ for (j = 0; j < 1000000; ++j)
+ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+ break;
+ for (j = 0; j < 1000000; ++j)
+ if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+ break;
+ /* 2. read info */
+ do {
+ for (j = 0; j < 12; ++j)
+ rtc[j] = CMOS_READ(j);
+ } while (rtc[0] != CMOS_READ(0));
+ spin_lock_irqrestore(&rtc_lock, flags);
+ TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(ulong32 *)&rtc[0],
+ *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]));
+ /* 3. send to controller firmware */
+ gdth_internal_cmd(hanum,SCREENSERVICE,GDT_REALTIME, *(ulong32 *)&rtc[0],
+ *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]);
+#endif
+
+ /* unfreeze all IOs */
+ gdth_internal_cmd(hanum,CACHESERVICE,GDT_UNFREEZE_IO,0,0,0);
+
+ /* initialize cache service */
+ ha->cache_feat = 0;
+ if (!force_dma32) {
+ ok = gdth_internal_cmd(hanum,CACHESERVICE,GDT_X_INIT_HOST,LINUX_OS,0,0);
+ if (ok)
+ ha->cache_feat = GDT_64BIT;
+ }
+ if (force_dma32 || (!ok && ha->status == (ushort)S_NOFUNC))
+ ok = gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0);
+ if (!ok) {
+ printk("GDT-HA %d: Initialization error cache service (code %d)\n",
+ hanum, ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
+ cdev_cnt = (ushort)ha->info;
+ ha->fw_vers = ha->service;
+
+#ifdef INT_COAL
+ if (ha->type == GDT_PCIMPR) {
+ /* set perf. modes */
+ pmod = (gdth_perf_modes *)ha->pscratch;
+ pmod->version = 1;
+ pmod->st_mode = 1; /* enable one status buffer */
+ *((ulong64 *)&pmod->st_buff_addr1) = ha->coal_stat_phys;
+ pmod->st_buff_indx1 = COALINDEX;
+ pmod->st_buff_addr2 = 0;
+ pmod->st_buff_u_addr2 = 0;
+ pmod->st_buff_indx2 = 0;
+ pmod->st_buff_size = sizeof(gdth_coal_status) * MAXOFFSETS;
+ pmod->cmd_mode = 0; // disable all cmd buffers
+ pmod->cmd_buff_addr1 = 0;
+ pmod->cmd_buff_u_addr1 = 0;
+ pmod->cmd_buff_indx1 = 0;
+ pmod->cmd_buff_addr2 = 0;
+ pmod->cmd_buff_u_addr2 = 0;
+ pmod->cmd_buff_indx2 = 0;
+ pmod->cmd_buff_size = 0;
+ pmod->reserved1 = 0;
+ pmod->reserved2 = 0;
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,SET_PERF_MODES,
+ INVALID_CHANNEL,sizeof(gdth_perf_modes))) {
+ printk("GDT-HA %d: Interrupt coalescing activated\n", hanum);
+ }
+ }
+#endif
+
+ /* detect number of buses - try new IOCTL */
+ iocr = (gdth_raw_iochan_str *)ha->pscratch;
+ iocr->hdr.version = 0xffffffff;
+ iocr->hdr.list_entries = MAXBUS;
+ iocr->hdr.first_chan = 0;
+ iocr->hdr.last_chan = MAXBUS-1;
+ iocr->hdr.list_offset = GDTOFFSOF(gdth_raw_iochan_str, list[0]);
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,IOCHAN_RAW_DESC,
+ INVALID_CHANNEL,sizeof(gdth_raw_iochan_str))) {
+ TRACE2(("IOCHAN_RAW_DESC supported!\n"));
+ ha->bus_cnt = iocr->hdr.chan_count;
+ for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
+ if (iocr->list[bus_no].proc_id < MAXID)
+ ha->bus_id[bus_no] = iocr->list[bus_no].proc_id;
+ else
+ ha->bus_id[bus_no] = 0xff;
+ }
+ } else {
+ /* old method */
+ chn = (gdth_getch_str *)ha->pscratch;
+ for (bus_no = 0; bus_no < MAXBUS; ++bus_no) {
+ chn->channel_no = bus_no;
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ SCSI_CHAN_CNT | L_CTRL_PATTERN,
+ IO_CHANNEL | INVALID_CHANNEL,
+ sizeof(gdth_getch_str))) {
+ if (bus_no == 0) {
+ printk("GDT-HA %d: Error detecting channel count (0x%x)\n",
+ hanum, ha->status);
+ return 0;
+ }
+ break;
+ }
+ if (chn->siop_id < MAXID)
+ ha->bus_id[bus_no] = chn->siop_id;
+ else
+ ha->bus_id[bus_no] = 0xff;
+ }
+ ha->bus_cnt = (unchar)bus_no;
+ }
+ TRACE2(("gdth_search_drives() %d channels\n",ha->bus_cnt));
+
+ /* read cache configuration */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO,
+ INVALID_CHANNEL,sizeof(gdth_cinfo_str))) {
+ printk("GDT-HA %d: Initialization error cache service (code %d)\n",
+ hanum, ha->status);
+ return 0;
+ }
+ ha->cpar = ((gdth_cinfo_str *)ha->pscratch)->cpar;
+ TRACE2(("gdth_search_drives() cinfo: vs %x sta %d str %d dw %d b %d\n",
+ ha->cpar.version,ha->cpar.state,ha->cpar.strategy,
+ ha->cpar.write_back,ha->cpar.block_size));
+
+ /* read board info and features */
+ ha->more_proc = FALSE;
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO,
+ INVALID_CHANNEL,sizeof(gdth_binfo_str))) {
+ memcpy(&ha->binfo, (gdth_binfo_str *)ha->pscratch,
+ sizeof(gdth_binfo_str));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_FEATURES,
+ INVALID_CHANNEL,sizeof(gdth_bfeat_str))) {
+ TRACE2(("BOARD_INFO/BOARD_FEATURES supported\n"));
+ ha->bfeat = *(gdth_bfeat_str *)ha->pscratch;
+ ha->more_proc = TRUE;
+ }
+ } else {
+ TRACE2(("BOARD_INFO requires firmware >= 1.10/2.08\n"));
+ strcpy(ha->binfo.type_string, gdth_ctr_name(hanum));
+ }
+ TRACE2(("Controller name: %s\n",ha->binfo.type_string));
+
+ /* read more informations */
+ if (ha->more_proc) {
+ /* physical drives, channel addresses */
+ ioc = (gdth_iochan_str *)ha->pscratch;
+ ioc->hdr.version = 0xffffffff;
+ ioc->hdr.list_entries = MAXBUS;
+ ioc->hdr.first_chan = 0;
+ ioc->hdr.last_chan = MAXBUS-1;
+ ioc->hdr.list_offset = GDTOFFSOF(gdth_iochan_str, list[0]);
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,IOCHAN_DESC,
+ INVALID_CHANNEL,sizeof(gdth_iochan_str))) {
+ for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
+ ha->raw[bus_no].address = ioc->list[bus_no].address;
+ ha->raw[bus_no].local_no = ioc->list[bus_no].local_no;
+ }
+ } else {
+ for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
+ ha->raw[bus_no].address = IO_CHANNEL;
+ ha->raw[bus_no].local_no = bus_no;
+ }
+ }
+ for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) {
+ chn = (gdth_getch_str *)ha->pscratch;
+ chn->channel_no = ha->raw[bus_no].local_no;
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ SCSI_CHAN_CNT | L_CTRL_PATTERN,
+ ha->raw[bus_no].address | INVALID_CHANNEL,
+ sizeof(gdth_getch_str))) {
+ ha->raw[bus_no].pdev_cnt = chn->drive_cnt;
+ TRACE2(("Channel %d: %d phys. drives\n",
+ bus_no,chn->drive_cnt));
+ }
+ if (ha->raw[bus_no].pdev_cnt > 0) {
+ drl = (gdth_drlist_str *)ha->pscratch;
+ drl->sc_no = ha->raw[bus_no].local_no;
+ drl->sc_cnt = ha->raw[bus_no].pdev_cnt;
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ SCSI_DR_LIST | L_CTRL_PATTERN,
+ ha->raw[bus_no].address | INVALID_CHANNEL,
+ sizeof(gdth_drlist_str))) {
+ for (j = 0; j < ha->raw[bus_no].pdev_cnt; ++j)
+ ha->raw[bus_no].id_list[j] = drl->sc_list[j];
+ } else {
+ ha->raw[bus_no].pdev_cnt = 0;
+ }
+ }
+ }
+
+ /* logical drives */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_DRV_CNT,
+ INVALID_CHANNEL,sizeof(ulong32))) {
+ drv_cnt = *(ulong32 *)ha->pscratch;
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_DRV_LIST,
+ INVALID_CHANNEL,drv_cnt * sizeof(ulong32))) {
+ for (j = 0; j < drv_cnt; ++j) {
+ drv_no = ((ulong32 *)ha->pscratch)[j];
+ if (drv_no < MAX_LDRIVES) {
+ ha->hdr[drv_no].is_logdrv = TRUE;
+ TRACE2(("Drive %d is log. drive\n",drv_no));
+ }
+ }
+ }
+ alst = (gdth_arcdl_str *)ha->pscratch;
+ alst->entries_avail = MAX_LDRIVES;
+ alst->first_entry = 0;
+ alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]);
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ ARRAY_DRV_LIST2 | LA_CTRL_PATTERN,
+ INVALID_CHANNEL, sizeof(gdth_arcdl_str) +
+ (alst->entries_avail-1) * sizeof(gdth_alist_str))) {
+ for (j = 0; j < alst->entries_init; ++j) {
+ ha->hdr[j].is_arraydrv = alst->list[j].is_arrayd;
+ ha->hdr[j].is_master = alst->list[j].is_master;
+ ha->hdr[j].is_parity = alst->list[j].is_parity;
+ ha->hdr[j].is_hotfix = alst->list[j].is_hotfix;
+ ha->hdr[j].master_no = alst->list[j].cd_handle;
+ }
+ } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ ARRAY_DRV_LIST | LA_CTRL_PATTERN,
+ 0, 35 * sizeof(gdth_alist_str))) {
+ for (j = 0; j < 35; ++j) {
+ alst2 = &((gdth_alist_str *)ha->pscratch)[j];
+ ha->hdr[j].is_arraydrv = alst2->is_arrayd;
+ ha->hdr[j].is_master = alst2->is_master;
+ ha->hdr[j].is_parity = alst2->is_parity;
+ ha->hdr[j].is_hotfix = alst2->is_hotfix;
+ ha->hdr[j].master_no = alst2->cd_handle;
+ }
+ }
+ }
+ }
+
+ /* initialize raw service */
+ ha->raw_feat = 0;
+ if (!force_dma32) {
+ ok = gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_X_INIT_RAW,0,0,0);
+ if (ok)
+ ha->raw_feat = GDT_64BIT;
+ }
+ if (force_dma32 || (!ok && ha->status == (ushort)S_NOFUNC))
+ ok = gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0);
+ if (!ok) {
+ printk("GDT-HA %d: Initialization error raw service (code %d)\n",
+ hanum, ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
+
+ /* set/get features raw service (scatter/gather) */
+ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER,
+ 0,0)) {
+ TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
+ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0)) {
+ TRACE2(("gdth_search_dr(): get feat RAWSERVICE %d\n",
+ ha->info));
+ ha->raw_feat |= (ushort)ha->info;
+ }
+ }
+
+ /* set/get features cache service (equal to raw service) */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0,
+ SCATTER_GATHER,0)) {
+ TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n"));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) {
+ TRACE2(("gdth_search_dr(): get feat CACHESERV. %d\n",
+ ha->info));
+ ha->cache_feat |= (ushort)ha->info;
+ }
+ }
+
+ /* reserve drives for raw service */
+ if (reserve_mode != 0) {
+ gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_RESERVE_ALL,
+ reserve_mode == 1 ? 1 : 3, 0, 0);
+ TRACE2(("gdth_search_drives(): RESERVE_ALL code %d\n",
+ ha->status));
+ }
+ for (i = 0; i < MAX_RES_ARGS; i += 4) {
+ if (reserve_list[i] == hanum && reserve_list[i+1] < ha->bus_cnt &&
+ reserve_list[i+2] < ha->tid_cnt && reserve_list[i+3] < MAXLUN) {
+ TRACE2(("gdth_search_drives(): reserve ha %d bus %d id %d lun %d\n",
+ reserve_list[i], reserve_list[i+1],
+ reserve_list[i+2], reserve_list[i+3]));
+ if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_RESERVE,0,
+ reserve_list[i+1], reserve_list[i+2] |
+ (reserve_list[i+3] << 8))) {
+ printk("GDT-HA %d: Error raw service (RESERVE, code %d)\n",
+ hanum, ha->status);
+ }
+ }
+ }
+
+ /* Determine OEM string using IOCTL */
+ oemstr = (gdth_oem_str_ioctl *)ha->pscratch;
+ oemstr->params.ctl_version = 0x01;
+ oemstr->params.buffer_size = sizeof(oemstr->text);
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ CACHE_READ_OEM_STRING_RECORD,INVALID_CHANNEL,
+ sizeof(gdth_oem_str_ioctl))) {
+ TRACE2(("gdth_search_drives(): CACHE_READ_OEM_STRING_RECORD OK\n"));
+ printk("GDT-HA %d: Vendor: %s Name: %s\n",
+ hanum,oemstr->text.oem_company_name,ha->binfo.type_string);
+ /* Save the Host Drive inquiry data */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ strlcpy(ha->oem_name,oemstr->text.scsi_host_drive_inquiry_vendor_id,
+ sizeof(ha->oem_name));
+#else
+ strncpy(ha->oem_name,oemstr->text.scsi_host_drive_inquiry_vendor_id,7);
+ ha->oem_name[7] = '\0';
+#endif
+ } else {
+ /* Old method, based on PCI ID */
+ TRACE2(("gdth_search_drives(): CACHE_READ_OEM_STRING_RECORD failed\n"));
+ printk("GDT-HA %d: Name: %s\n",
+ hanum,ha->binfo.type_string);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ if (ha->oem_id == OEM_ID_INTEL)
+ strlcpy(ha->oem_name,"Intel ", sizeof(ha->oem_name));
+ else
+ strlcpy(ha->oem_name,"ICP ", sizeof(ha->oem_name));
+#else
+ if (ha->oem_id == OEM_ID_INTEL)
+ strcpy(ha->oem_name,"Intel ");
+ else
+ strcpy(ha->oem_name,"ICP ");
+#endif
+ }
+
+ /* scanning for host drives */
+ for (i = 0; i < cdev_cnt; ++i)
+ gdth_analyse_hdrive(hanum,i);
+
+ TRACE(("gdth_search_drives() OK\n"));
+ return 1;
+}
+
+static int gdth_analyse_hdrive(int hanum,ushort hdrive)
+{
+ register gdth_ha_str *ha;
+ ulong32 drv_cyls;
+ int drv_hds, drv_secs;
+
+ TRACE(("gdth_analyse_hdrive() hanum %d drive %d\n",hanum,hdrive));
+ if (hdrive >= MAX_HDRIVES)
+ return 0;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,hdrive,0,0))
+ return 0;
+ ha->hdr[hdrive].present = TRUE;
+ ha->hdr[hdrive].size = ha->info;
+
+ /* evaluate mapping (sectors per head, heads per cylinder) */
+ ha->hdr[hdrive].size &= ~SECS32;
+ if (ha->info2 == 0) {
+ gdth_eval_mapping(ha->hdr[hdrive].size,&drv_cyls,&drv_hds,&drv_secs);
+ } else {
+ drv_hds = ha->info2 & 0xff;
+ drv_secs = (ha->info2 >> 8) & 0xff;
+ drv_cyls = (ulong32)ha->hdr[hdrive].size / drv_hds / drv_secs;
+ }
+ ha->hdr[hdrive].heads = (unchar)drv_hds;
+ ha->hdr[hdrive].secs = (unchar)drv_secs;
+ /* round size */
+ ha->hdr[hdrive].size = drv_cyls * drv_hds * drv_secs;
+
+ if (ha->cache_feat & GDT_64BIT) {
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_X_INFO,hdrive,0,0)
+ && ha->info2 != 0) {
+ ha->hdr[hdrive].size = ((ulong64)ha->info2 << 32) | ha->info;
+ }
+ }
+ TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n",
+ hdrive,ha->hdr[hdrive].size,drv_hds,drv_secs));
+
+ /* get informations about device */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,hdrive,0,0)) {
+ TRACE2(("gdth_search_dr() cache drive %d devtype %d\n",
+ hdrive,ha->info));
+ ha->hdr[hdrive].devtype = (ushort)ha->info;
+ }
+
+ /* cluster info */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,hdrive,0,0)) {
+ TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
+ hdrive,ha->info));
+ if (!shared_access)
+ ha->hdr[hdrive].cluster_type = (unchar)ha->info;
+ }
+
+ /* R/W attributes */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,hdrive,0,0)) {
+ TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
+ hdrive,ha->info));
+ ha->hdr[hdrive].rw_attribs = (unchar)ha->info;
+ }
+
+ return 1;
+}
+
+
+/* command queueing/sending functions */
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority)
+{
+ register gdth_ha_str *ha;
+ register Scsi_Cmnd *pscp;
+ register Scsi_Cmnd *nscp;
+ ulong flags;
+ unchar b, t;
+
+ TRACE(("gdth_putq() priority %d\n",priority));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ scp->SCp.this_residual = (int)priority;
+ b = virt_ctr ? NUMDATA(scp->device->host)->busnum : scp->device->channel;
+ t = scp->device->id;
+ if (priority >= DEFAULT_PRI) {
+ if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) ||
+ (b == ha->virt_bus && t < MAX_HDRIVES && ha->hdr[t].lock)) {
+ TRACE2(("gdth_putq(): locked IO -> update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
+ }
+ }
+
+ if (ha->req_first==NULL) {
+ ha->req_first = scp; /* queue was empty */
+ scp->SCp.ptr = NULL;
+ } else { /* queue not empty */
+ pscp = ha->req_first;
+ nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ /* priority: 0-highest,..,0xff-lowest */
+ while (nscp && (unchar)nscp->SCp.this_residual <= priority) {
+ pscp = nscp;
+ nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ }
+ pscp->SCp.ptr = (char *)scp;
+ scp->SCp.ptr = (char *)nscp;
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+
+#ifdef GDTH_STATISTICS
+ flags = 0;
+ for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++flags;
+ if (max_rq < flags) {
+ max_rq = flags;
+ TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq));
+ }
+#endif
+}
+
+static void gdth_next(int hanum)
+{
+ register gdth_ha_str *ha;
+ register Scsi_Cmnd *pscp;
+ register Scsi_Cmnd *nscp;
+ unchar b, t, l, firsttime;
+ unchar this_cmd, next_cmd;
+ ulong flags = 0;
+ int cmd_index;
+
+ TRACE(("gdth_next() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (!gdth_polling)
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
+ this_cmd = firsttime = TRUE;
+ next_cmd = gdth_polling ? FALSE:TRUE;
+ cmd_index = 0;
+
+ for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
+ if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
+ pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ b = virt_ctr ? NUMDATA(nscp->device->host)->busnum : nscp->device->channel;
+ t = nscp->device->id;
+ l = nscp->device->lun;
+ if (nscp->SCp.this_residual >= DEFAULT_PRI) {
+ if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) ||
+ (b == ha->virt_bus && t < MAX_HDRIVES && ha->hdr[t].lock))
+ continue;
+ }
+
+ if (firsttime) {
+ if (gdth_test_busy(hanum)) { /* controller busy ? */
+ TRACE(("gdth_next() controller %d busy !\n",hanum));
+ if (!gdth_polling) {
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ return;
+ }
+ while (gdth_test_busy(hanum))
+ gdth_delay(1);
+ }
+ firsttime = FALSE;
+ }
+
+ if (nscp->done != gdth_scsi_done || nscp->cmnd[0] != 0xff) {
+ if (nscp->SCp.phase == -1) {
+ nscp->SCp.phase = CACHESERVICE; /* default: cache svc. */
+ if (nscp->cmnd[0] == TEST_UNIT_READY) {
+ TRACE2(("TEST_UNIT_READY Bus %d Id %d LUN %d\n",
+ b, t, l));
+ /* TEST_UNIT_READY -> set scan mode */
+ if ((ha->scan_mode & 0x0f) == 0) {
+ if (b == 0 && t == 0 && l == 0) {
+ ha->scan_mode |= 1;
+ TRACE2(("Scan mode: 0x%x\n", ha->scan_mode));
+ }
+ } else if ((ha->scan_mode & 0x0f) == 1) {
+ if (b == 0 && ((t == 0 && l == 1) ||
+ (t == 1 && l == 0))) {
+ nscp->SCp.sent_command = GDT_SCAN_START;
+ nscp->SCp.phase = ((ha->scan_mode & 0x10 ? 1:0) << 8)
+ | SCSIRAWSERVICE;
+ ha->scan_mode = 0x12;
+ TRACE2(("Scan mode: 0x%x (SCAN_START)\n",
+ ha->scan_mode));
+ } else {
+ ha->scan_mode &= 0x10;
+ TRACE2(("Scan mode: 0x%x\n", ha->scan_mode));
+ }
+ } else if (ha->scan_mode == 0x12) {
+ if (b == ha->bus_cnt && t == ha->tid_cnt-1) {
+ nscp->SCp.phase = SCSIRAWSERVICE;
+ nscp->SCp.sent_command = GDT_SCAN_END;
+ ha->scan_mode &= 0x10;
+ TRACE2(("Scan mode: 0x%x (SCAN_END)\n",
+ ha->scan_mode));
+ }
+ }
+ }
+ if (b == ha->virt_bus && nscp->cmnd[0] != INQUIRY &&
+ nscp->cmnd[0] != READ_CAPACITY && nscp->cmnd[0] != MODE_SENSE &&
+ (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) {
+ /* always GDT_CLUST_INFO! */
+ nscp->SCp.sent_command = GDT_CLUST_INFO;
+ }
+ }
+ }
+
+ if (nscp->SCp.sent_command != -1) {
+ if ((nscp->SCp.phase & 0xff) == CACHESERVICE) {
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+ this_cmd = FALSE;
+ next_cmd = FALSE;
+ } else if ((nscp->SCp.phase & 0xff) == SCSIRAWSERVICE) {
+ if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,BUS_L2P(ha,b))))
+ this_cmd = FALSE;
+ next_cmd = FALSE;
+ } else {
+ memset((char*)nscp->sense_buffer,0,16);
+ nscp->sense_buffer[0] = 0x70;
+ nscp->sense_buffer[2] = NOT_READY;
+ nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ }
+ } else if (nscp->done == gdth_scsi_done && nscp->cmnd[0] == 0xff) {
+ if (!(cmd_index=gdth_special_cmd(hanum,nscp)))
+ this_cmd = FALSE;
+ next_cmd = FALSE;
+ } else if (b != ha->virt_bus) {
+ if (ha->raw[BUS_L2P(ha,b)].io_cnt[t] >= GDTH_MAX_RAW ||
+ !(cmd_index=gdth_fill_raw_cmd(hanum,nscp,BUS_L2P(ha,b))))
+ this_cmd = FALSE;
+ else
+ ha->raw[BUS_L2P(ha,b)].io_cnt[t]++;
+ } else if (t >= MAX_HDRIVES || !ha->hdr[t].present || l != 0) {
+ TRACE2(("Command 0x%x to bus %d id %d lun %d -> IGNORE\n",
+ nscp->cmnd[0], b, t, l));
+ nscp->result = DID_BAD_TARGET << 16;
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ } else {
+ switch (nscp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case INQUIRY:
+ case REQUEST_SENSE:
+ case READ_CAPACITY:
+ case VERIFY:
+ case START_STOP:
+ case MODE_SENSE:
+ case SERVICE_ACTION_IN:
+ TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ if (ha->hdr[t].media_changed && nscp->cmnd[0] != INQUIRY) {
+ /* return UNIT_ATTENTION */
+ TRACE2(("cmd 0x%x target %d: UNIT_ATTENTION\n",
+ nscp->cmnd[0], t));
+ ha->hdr[t].media_changed = FALSE;
+ memset((char*)nscp->sense_buffer,0,16);
+ nscp->sense_buffer[0] = 0x70;
+ nscp->sense_buffer[2] = UNIT_ATTENTION;
+ nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ } else if (gdth_internal_cache_cmd(hanum,nscp))
+ nscp->scsi_done(nscp);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ if ( (nscp->cmnd[4]&1) && !(ha->hdr[t].devtype&1) ) {
+ TRACE(("Prevent r. nonremov. drive->do nothing\n"));
+ nscp->result = DID_OK << 16;
+ nscp->sense_buffer[0] = 0;
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ } else {
+ nscp->cmnd[3] = (ha->hdr[t].devtype&1) ? 1:0;
+ TRACE(("Prevent/allow r. %d rem. drive %d\n",
+ nscp->cmnd[4],nscp->cmnd[3]));
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+ this_cmd = FALSE;
+ }
+ break;
+
+ case RESERVE:
+ case RELEASE:
+ TRACE2(("cache cmd %s\n",nscp->cmnd[0] == RESERVE ?
+ "RESERVE" : "RELEASE"));
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+ this_cmd = FALSE;
+ break;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_16:
+ case WRITE_16:
+ if (ha->hdr[t].media_changed) {
+ /* return UNIT_ATTENTION */
+ TRACE2(("cmd 0x%x target %d: UNIT_ATTENTION\n",
+ nscp->cmnd[0], t));
+ ha->hdr[t].media_changed = FALSE;
+ memset((char*)nscp->sense_buffer,0,16);
+ nscp->sense_buffer[0] = 0x70;
+ nscp->sense_buffer[2] = UNIT_ATTENTION;
+ nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ } else if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+ this_cmd = FALSE;
+ break;
+
+ default:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x unknown\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ printk("GDT-HA %d: Unknown SCSI command 0x%x to cache service !\n",
+ hanum, nscp->cmnd[0]);
+ nscp->result = DID_ABORT << 16;
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ break;
+ }
+ }
+
+ if (!this_cmd)
+ break;
+ if (nscp == ha->req_first)
+ ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
+ else
+ pscp->SCp.ptr = nscp->SCp.ptr;
+ if (!next_cmd)
+ break;
+ }
+
+ if (ha->cmd_cnt > 0) {
+ gdth_release_event(hanum);
+ }
+
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+
+ if (gdth_polling && ha->cmd_cnt > 0) {
+ if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT))
+ printk("GDT-HA %d: Command %d timed out !\n",
+ hanum,cmd_index);
+ }
+}
+
+static void gdth_copy_internal_data(int hanum,Scsi_Cmnd *scp,
+ char *buffer,ushort count)
+{
+ ushort cpcount,i;
+ ushort cpsum,cpnow;
+ struct scatterlist *sl;
+ gdth_ha_str *ha;
+ char *address;
+
+ cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (scp->use_sg) {
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0,cpsum=0; i<scp->use_sg; ++i,++sl) {
+ unsigned long flags;
+ cpnow = (ushort)sl->length;
+ TRACE(("copy_internal() now %d sum %d count %d %d\n",
+ cpnow,cpsum,cpcount,(ushort)scp->bufflen));
+ if (cpsum+cpnow > cpcount)
+ cpnow = cpcount - cpsum;
+ cpsum += cpnow;
+ if (!sl->page) {
+ printk("GDT-HA %d: invalid sc/gt element in gdth_copy_internal_data()\n",
+ hanum);
+ return;
+ }
+ local_irq_save(flags);
+ address = kmap_atomic(sl->page, KM_BIO_SRC_IRQ) + sl->offset;
+ memcpy(address,buffer,cpnow);
+ flush_dcache_page(sl->page);
+ kunmap_atomic(address, KM_BIO_SRC_IRQ);
+ local_irq_restore(flags);
+ if (cpsum == cpcount)
+ break;
+ buffer += cpnow;
+ }
+ } else {
+ TRACE(("copy_internal() count %d\n",cpcount));
+ memcpy((char*)scp->request_buffer,buffer,cpcount);
+ }
+}
+
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp)
+{
+ register gdth_ha_str *ha;
+ unchar t;
+ gdth_inq_data inq;
+ gdth_rdcap_data rdc;
+ gdth_sense_data sd;
+ gdth_modep_data mpd;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->device->id;
+ TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
+ scp->cmnd[0],t));
+
+ scp->result = DID_OK << 16;
+ scp->sense_buffer[0] = 0;
+
+ switch (scp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case VERIFY:
+ case START_STOP:
+ TRACE2(("Test/Verify/Start hdrive %d\n",t));
+ break;
+
+ case INQUIRY:
+ TRACE2(("Inquiry hdrive %d devtype %d\n",
+ t,ha->hdr[t].devtype));
+ inq.type_qual = (ha->hdr[t].devtype&4) ? TYPE_ROM:TYPE_DISK;
+ /* you can here set all disks to removable, if you want to do
+ a flush using the ALLOW_MEDIUM_REMOVAL command */
+ inq.modif_rmb = 0x00;
+ if ((ha->hdr[t].devtype & 1) ||
+ (ha->hdr[t].cluster_type & CLUSTER_DRIVE))
+ inq.modif_rmb = 0x80;
+ inq.version = 2;
+ inq.resp_aenc = 2;
+ inq.add_length= 32;
+ strcpy(inq.vendor,ha->oem_name);
+ sprintf(inq.product,"Host Drive #%02d",t);
+ strcpy(inq.revision," ");
+ gdth_copy_internal_data(hanum,scp,(char*)&inq,sizeof(gdth_inq_data));
+ break;
+
+ case REQUEST_SENSE:
+ TRACE2(("Request sense hdrive %d\n",t));
+ sd.errorcode = 0x70;
+ sd.segno = 0x00;
+ sd.key = NO_SENSE;
+ sd.info = 0;
+ sd.add_length= 0;
+ gdth_copy_internal_data(hanum,scp,(char*)&sd,sizeof(gdth_sense_data));
+ break;
+
+ case MODE_SENSE:
+ TRACE2(("Mode sense hdrive %d\n",t));
+ memset((char*)&mpd,0,sizeof(gdth_modep_data));
+ mpd.hd.data_length = sizeof(gdth_modep_data);
+ mpd.hd.dev_par = (ha->hdr[t].devtype&2) ? 0x80:0;
+ mpd.hd.bd_length = sizeof(mpd.bd);
+ mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16;
+ mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8;
+ mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff);
+ gdth_copy_internal_data(hanum,scp,(char*)&mpd,sizeof(gdth_modep_data));
+ break;
+
+ case READ_CAPACITY:
+ TRACE2(("Read capacity hdrive %d\n",t));
+ if (ha->hdr[t].size > (ulong64)0xffffffff)
+ rdc.last_block_no = 0xffffffff;
+ else
+ rdc.last_block_no = cpu_to_be32(ha->hdr[t].size-1);
+ rdc.block_length = cpu_to_be32(SECTOR_SIZE);
+ gdth_copy_internal_data(hanum,scp,(char*)&rdc,sizeof(gdth_rdcap_data));
+ break;
+
+ case SERVICE_ACTION_IN:
+ if ((scp->cmnd[1] & 0x1f) == SAI_READ_CAPACITY_16 &&
+ (ha->cache_feat & GDT_64BIT)) {
+ gdth_rdcap16_data rdc16;
+
+ TRACE2(("Read capacity (16) hdrive %d\n",t));
+ rdc16.last_block_no = cpu_to_be64(ha->hdr[t].size-1);
+ rdc16.block_length = cpu_to_be32(SECTOR_SIZE);
+ gdth_copy_internal_data(hanum,scp,(char*)&rdc16,sizeof(gdth_rdcap16_data));
+ } else {
+ scp->result = DID_ABORT << 16;
+ }
+ break;
+
+ default:
+ TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
+ break;
+ }
+
+ if (!scp->SCp.have_data_in)
+ scp->SCp.have_data_in++;
+ else
+ return 1;
+
+ return 0;
+}
+
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ struct scatterlist *sl;
+ ulong32 cnt, blockcnt;
+ ulong64 no, blockno;
+ dma_addr_t phys_addr;
+ int i, cmd_index, read_write, sgcnt, mode64;
+ struct page *page;
+ ulong offset;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
+ scp->cmnd[0],scp->cmd_len,hdrive));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ mode64 = (ha->cache_feat & GDT_64BIT) ? TRUE : FALSE;
+ /* test for READ_16, WRITE_16 if !mode64 ? ---
+ not required, should not occur due to error return on
+ READ_CAPACITY_16 */
+
+ cmdp->Service = CACHESERVICE;
+ cmdp->RequestBuffer = scp;
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* fill command */
+ read_write = 0;
+ if (scp->SCp.sent_command != -1)
+ cmdp->OpCode = scp->SCp.sent_command; /* special cache cmd. */
+ else if (scp->cmnd[0] == RESERVE)
+ cmdp->OpCode = GDT_RESERVE_DRV;
+ else if (scp->cmnd[0] == RELEASE)
+ cmdp->OpCode = GDT_RELEASE_DRV;
+ else if (scp->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
+ if (scp->cmnd[4] & 1) /* prevent ? */
+ cmdp->OpCode = GDT_MOUNT;
+ else if (scp->cmnd[3] & 1) /* removable drive ? */
+ cmdp->OpCode = GDT_UNMOUNT;
+ else
+ cmdp->OpCode = GDT_FLUSH;
+ } else if (scp->cmnd[0] == WRITE_6 || scp->cmnd[0] == WRITE_10 ||
+ scp->cmnd[0] == WRITE_12 || scp->cmnd[0] == WRITE_16
+ ) {
+ read_write = 1;
+ if (gdth_write_through || ((ha->hdr[hdrive].rw_attribs & 1) &&
+ (ha->cache_feat & GDT_WR_THROUGH)))
+ cmdp->OpCode = GDT_WRITE_THR;
+ else
+ cmdp->OpCode = GDT_WRITE;
+ } else {
+ read_write = 2;
+ cmdp->OpCode = GDT_READ;
+ }
+
+ cmdp->BoardNode = LOCALBOARD;
+ if (mode64) {
+ cmdp->u.cache64.DeviceNo = hdrive;
+ cmdp->u.cache64.BlockNo = 1;
+ cmdp->u.cache64.sg_canz = 0;
+ } else {
+ cmdp->u.cache.DeviceNo = hdrive;
+ cmdp->u.cache.BlockNo = 1;
+ cmdp->u.cache.sg_canz = 0;
+ }
+
+ if (read_write) {
+ if (scp->cmd_len == 16) {
+ memcpy(&no, &scp->cmnd[2], sizeof(ulong64));
+ blockno = be64_to_cpu(no);
+ memcpy(&cnt, &scp->cmnd[10], sizeof(ulong32));
+ blockcnt = be32_to_cpu(cnt);
+ } else if (scp->cmd_len == 10) {
+ memcpy(&no, &scp->cmnd[2], sizeof(ulong32));
+ blockno = be32_to_cpu(no);
+ memcpy(&cnt, &scp->cmnd[7], sizeof(ushort));
+ blockcnt = be16_to_cpu(cnt);
+ } else {
+ memcpy(&no, &scp->cmnd[0], sizeof(ulong32));
+ blockno = be32_to_cpu(no) & 0x001fffffUL;
+ blockcnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
+ }
+ if (mode64) {
+ cmdp->u.cache64.BlockNo = blockno;
+ cmdp->u.cache64.BlockCnt = blockcnt;
+ } else {
+ cmdp->u.cache.BlockNo = (ulong32)blockno;
+ cmdp->u.cache.BlockCnt = blockcnt;
+ }
+
+ if (scp->use_sg) {
+ sl = (struct scatterlist *)scp->request_buffer;
+ sgcnt = scp->use_sg;
+ scp->SCp.Status = GDTH_MAP_SG;
+ scp->SCp.Message = (read_write == 1 ?
+ PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+ sgcnt = pci_map_sg(ha->pdev,sl,scp->use_sg,scp->SCp.Message);
+ if (mode64) {
+ cmdp->u.cache64.DestAddr= (ulong64)-1;
+ cmdp->u.cache64.sg_canz = sgcnt;
+ for (i=0; i<sgcnt; ++i,++sl) {
+ cmdp->u.cache64.sg_lst[i].sg_ptr = sg_dma_address(sl);
+#ifdef GDTH_DMA_STATISTICS
+ if (cmdp->u.cache64.sg_lst[i].sg_ptr > (ulong64)0xffffffff)
+ ha->dma64_cnt++;
+ else
+ ha->dma32_cnt++;
+#endif
+ cmdp->u.cache64.sg_lst[i].sg_len = sg_dma_len(sl);
+ }
+ } else {
+ cmdp->u.cache.DestAddr= 0xffffffff;
+ cmdp->u.cache.sg_canz = sgcnt;
+ for (i=0; i<sgcnt; ++i,++sl) {
+ cmdp->u.cache.sg_lst[i].sg_ptr = sg_dma_address(sl);
+#ifdef GDTH_DMA_STATISTICS
+ ha->dma32_cnt++;
+#endif
+ cmdp->u.cache.sg_lst[i].sg_len = sg_dma_len(sl);
+ }
+ }
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong32)sgcnt) {
+ max_sg = (ulong32)sgcnt;
+ TRACE3(("GDT: max_sg = %d\n",max_sg));
+ }
+#endif
+
+ } else {
+ scp->SCp.Status = GDTH_MAP_SINGLE;
+ scp->SCp.Message = (read_write == 1 ?
+ PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+ page = virt_to_page(scp->request_buffer);
+ offset = (ulong)scp->request_buffer & ~PAGE_MASK;
+ phys_addr = pci_map_page(ha->pdev,page,offset,
+ scp->request_bufflen,scp->SCp.Message);
+ scp->SCp.dma_handle = phys_addr;
+ if (mode64) {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ cmdp->u.cache64.DestAddr = (ulong64)-1;
+ cmdp->u.cache64.sg_canz = 1;
+ cmdp->u.cache64.sg_lst[0].sg_ptr = phys_addr;
+ cmdp->u.cache64.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.cache64.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.cache64.DestAddr = phys_addr;
+ cmdp->u.cache64.sg_canz= 0;
+ }
+ } else {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ cmdp->u.cache.DestAddr = 0xffffffff;
+ cmdp->u.cache.sg_canz = 1;
+ cmdp->u.cache.sg_lst[0].sg_ptr = phys_addr;
+ cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.cache.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.cache.DestAddr = phys_addr;
+ cmdp->u.cache.sg_canz= 0;
+ }
+ }
+ }
+ }
+ /* evaluate command size, check space */
+ if (mode64) {
+ TRACE(("cache cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
+ cmdp->u.cache64.DestAddr,cmdp->u.cache64.sg_canz,
+ cmdp->u.cache64.sg_lst[0].sg_ptr,
+ cmdp->u.cache64.sg_lst[0].sg_len));
+ TRACE(("cache cmd: cmd %d blockno. %d, blockcnt %d\n",
+ cmdp->OpCode,cmdp->u.cache64.BlockNo,cmdp->u.cache64.BlockCnt));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache64.sg_lst) +
+ (ushort)cmdp->u.cache64.sg_canz * sizeof(gdth_sg64_str);
+ } else {
+ TRACE(("cache cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
+ cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
+ cmdp->u.cache.sg_lst[0].sg_ptr,
+ cmdp->u.cache.sg_lst[0].sg_len));
+ TRACE(("cache cmd: cmd %d blockno. %d, blockcnt %d\n",
+ cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
+ (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
+ }
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_fill_cache() DPMEM overflow\n"));
+ ha->cmd_tab[cmd_index-2].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ struct scatterlist *sl;
+ ushort i;
+ dma_addr_t phys_addr, sense_paddr;
+ int cmd_index, sgcnt, mode64;
+ unchar t,l;
+ struct page *page;
+ ulong offset;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->device->id;
+ l = scp->device->lun;
+ cmdp = ha->pccb;
+ TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
+ scp->cmnd[0],b,t,l));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ mode64 = (ha->raw_feat & GDT_64BIT) ? TRUE : FALSE;
+
+ cmdp->Service = SCSIRAWSERVICE;
+ cmdp->RequestBuffer = scp;
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* fill command */
+ if (scp->SCp.sent_command != -1) {
+ cmdp->OpCode = scp->SCp.sent_command; /* special raw cmd. */
+ cmdp->BoardNode = LOCALBOARD;
+ if (mode64) {
+ cmdp->u.raw64.direction = (scp->SCp.phase >> 8);
+ TRACE2(("special raw cmd 0x%x param 0x%x\n",
+ cmdp->OpCode, cmdp->u.raw64.direction));
+ /* evaluate command size */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst);
+ } else {
+ cmdp->u.raw.direction = (scp->SCp.phase >> 8);
+ TRACE2(("special raw cmd 0x%x param 0x%x\n",
+ cmdp->OpCode, cmdp->u.raw.direction));
+ /* evaluate command size */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst);
+ }
+
+ } else {
+ page = virt_to_page(scp->sense_buffer);
+ offset = (ulong)scp->sense_buffer & ~PAGE_MASK;
+ sense_paddr = pci_map_page(ha->pdev,page,offset,
+ 16,PCI_DMA_FROMDEVICE);
+ scp->SCp.buffer = (struct scatterlist *)((ulong32)sense_paddr);
+ /* high part, if 64bit */
+ scp->host_scribble = (char *)(ulong32)((ulong64)sense_paddr >> 32);
+ cmdp->OpCode = GDT_WRITE; /* always */
+ cmdp->BoardNode = LOCALBOARD;
+ if (mode64) {
+ cmdp->u.raw64.reserved = 0;
+ cmdp->u.raw64.mdisc_time = 0;
+ cmdp->u.raw64.mcon_time = 0;
+ cmdp->u.raw64.clen = scp->cmd_len;
+ cmdp->u.raw64.target = t;
+ cmdp->u.raw64.lun = l;
+ cmdp->u.raw64.bus = b;
+ cmdp->u.raw64.priority = 0;
+ cmdp->u.raw64.sdlen = scp->request_bufflen;
+ cmdp->u.raw64.sense_len = 16;
+ cmdp->u.raw64.sense_data = sense_paddr;
+ cmdp->u.raw64.direction =
+ gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN;
+ memcpy(cmdp->u.raw64.cmd,scp->cmnd,16);
+ } else {
+ cmdp->u.raw.reserved = 0;
+ cmdp->u.raw.mdisc_time = 0;
+ cmdp->u.raw.mcon_time = 0;
+ cmdp->u.raw.clen = scp->cmd_len;
+ cmdp->u.raw.target = t;
+ cmdp->u.raw.lun = l;
+ cmdp->u.raw.bus = b;
+ cmdp->u.raw.priority = 0;
+ cmdp->u.raw.link_p = 0;
+ cmdp->u.raw.sdlen = scp->request_bufflen;
+ cmdp->u.raw.sense_len = 16;
+ cmdp->u.raw.sense_data = sense_paddr;
+ cmdp->u.raw.direction =
+ gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN;
+ memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
+ }
+
+ if (scp->use_sg) {
+ sl = (struct scatterlist *)scp->request_buffer;
+ sgcnt = scp->use_sg;
+ scp->SCp.Status = GDTH_MAP_SG;
+ scp->SCp.Message = PCI_DMA_BIDIRECTIONAL;
+ sgcnt = pci_map_sg(ha->pdev,sl,scp->use_sg,scp->SCp.Message);
+ if (mode64) {
+ cmdp->u.raw64.sdata = (ulong64)-1;
+ cmdp->u.raw64.sg_ranz = sgcnt;
+ for (i=0; i<sgcnt; ++i,++sl) {
+ cmdp->u.raw64.sg_lst[i].sg_ptr = sg_dma_address(sl);
+#ifdef GDTH_DMA_STATISTICS
+ if (cmdp->u.raw64.sg_lst[i].sg_ptr > (ulong64)0xffffffff)
+ ha->dma64_cnt++;
+ else
+ ha->dma32_cnt++;
+#endif
+ cmdp->u.raw64.sg_lst[i].sg_len = sg_dma_len(sl);
+ }
+ } else {
+ cmdp->u.raw.sdata = 0xffffffff;
+ cmdp->u.raw.sg_ranz = sgcnt;
+ for (i=0; i<sgcnt; ++i,++sl) {
+ cmdp->u.raw.sg_lst[i].sg_ptr = sg_dma_address(sl);
+#ifdef GDTH_DMA_STATISTICS
+ ha->dma32_cnt++;
+#endif
+ cmdp->u.raw.sg_lst[i].sg_len = sg_dma_len(sl);
+ }
+ }
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < sgcnt) {
+ max_sg = sgcnt;
+ TRACE3(("GDT: max_sg = %d\n",sgcnt));
+ }
+#endif
+
+ } else {
+ scp->SCp.Status = GDTH_MAP_SINGLE;
+ scp->SCp.Message = PCI_DMA_BIDIRECTIONAL;
+ page = virt_to_page(scp->request_buffer);
+ offset = (ulong)scp->request_buffer & ~PAGE_MASK;
+ phys_addr = pci_map_page(ha->pdev,page,offset,
+ scp->request_bufflen,scp->SCp.Message);
+ scp->SCp.dma_handle = phys_addr;
+
+ if (mode64) {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ cmdp->u.raw64.sdata = (ulong64)-1;
+ cmdp->u.raw64.sg_ranz= 1;
+ cmdp->u.raw64.sg_lst[0].sg_ptr = phys_addr;
+ cmdp->u.raw64.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.raw64.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.raw64.sdata = phys_addr;
+ cmdp->u.raw64.sg_ranz= 0;
+ }
+ } else {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ cmdp->u.raw.sdata = 0xffffffff;
+ cmdp->u.raw.sg_ranz= 1;
+ cmdp->u.raw.sg_lst[0].sg_ptr = phys_addr;
+ cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.raw.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.raw.sdata = phys_addr;
+ cmdp->u.raw.sg_ranz= 0;
+ }
+ }
+ }
+ if (mode64) {
+ TRACE(("raw cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
+ cmdp->u.raw64.sdata,cmdp->u.raw64.sg_ranz,
+ cmdp->u.raw64.sg_lst[0].sg_ptr,
+ cmdp->u.raw64.sg_lst[0].sg_len));
+ /* evaluate command size */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst) +
+ (ushort)cmdp->u.raw64.sg_ranz * sizeof(gdth_sg64_str);
+ } else {
+ TRACE(("raw cmd: addr. %x sganz %x sgptr0 %x sglen0 %x\n",
+ cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
+ cmdp->u.raw.sg_lst[0].sg_ptr,
+ cmdp->u.raw.sg_lst[0].sg_len));
+ /* evaluate command size */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
+ (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
+ }
+ }
+ /* check space */
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_fill_raw() DPMEM overflow\n"));
+ ha->cmd_tab[cmd_index-2].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ TRACE2(("gdth_special_cmd(): "));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str));
+ cmdp->RequestBuffer = scp;
+
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* evaluate command size, check space */
+ if (cmdp->OpCode == GDT_IOCTL) {
+ TRACE2(("IOCTL\n"));
+ ha->cmd_len =
+ GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong64);
+ } else if (cmdp->Service == CACHESERVICE) {
+ TRACE2(("cache command %d\n",cmdp->OpCode));
+ if (ha->cache_feat & GDT_64BIT)
+ ha->cmd_len =
+ GDTOFFSOF(gdth_cmd_str,u.cache64.sg_lst) + sizeof(gdth_sg64_str);
+ else
+ ha->cmd_len =
+ GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
+ } else if (cmdp->Service == SCSIRAWSERVICE) {
+ TRACE2(("raw command %d\n",cmdp->OpCode));
+ if (ha->raw_feat & GDT_64BIT)
+ ha->cmd_len =
+ GDTOFFSOF(gdth_cmd_str,u.raw64.sg_lst) + sizeof(gdth_sg64_str);
+ else
+ ha->cmd_len =
+ GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str);
+ }
+
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_special_cmd() DPMEM overflow\n"));
+ ha->cmd_tab[cmd_index-2].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+
+/* Controller event handling functions */
+static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, ushort source,
+ ushort idx, gdth_evt_data *evt)
+{
+ gdth_evt_str *e;
+ struct timeval tv;
+
+ /* no GDTH_LOCK_HA() ! */
+ TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
+ if (source == 0) /* no source -> no event */
+ return NULL;
+
+ if (ebuffer[elastidx].event_source == source &&
+ ebuffer[elastidx].event_idx == idx &&
+ ((evt->size != 0 && ebuffer[elastidx].event_data.size != 0 &&
+ !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+ (char *)&evt->eu, evt->size)) ||
+ (evt->size == 0 && ebuffer[elastidx].event_data.size == 0 &&
+ !strcmp((char *)&ebuffer[elastidx].event_data.event_string,
+ (char *)&evt->event_string)))) {
+ e = &ebuffer[elastidx];
+ do_gettimeofday(&tv);
+ e->last_stamp = tv.tv_sec;
+ ++e->same_count;
+ } else {
+ if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */
+ ++elastidx;
+ if (elastidx == MAX_EVENTS)
+ elastidx = 0;
+ if (elastidx == eoldidx) { /* reached mark ? */
+ ++eoldidx;
+ if (eoldidx == MAX_EVENTS)
+ eoldidx = 0;
+ }
+ }
+ e = &ebuffer[elastidx];
+ e->event_source = source;
+ e->event_idx = idx;
+ do_gettimeofday(&tv);
+ e->first_stamp = e->last_stamp = tv.tv_sec;
+ e->same_count = 1;
+ e->event_data = *evt;
+ e->application = 0;
+ }
+ return e;
+}
+
+static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+
+ TRACE2(("gdth_read_event() handle %d\n", handle));
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ if (handle == -1)
+ eindex = eoldidx;
+ else
+ eindex = handle;
+ estr->event_source = 0;
+
+ if (eindex >= MAX_EVENTS) {
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ return eindex;
+ }
+ e = &ebuffer[eindex];
+ if (e->event_source != 0) {
+ if (eindex != elastidx) {
+ if (++eindex == MAX_EVENTS)
+ eindex = 0;
+ } else {
+ eindex = -1;
+ }
+ memcpy(estr, e, sizeof(gdth_evt_str));
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ return eindex;
+}
+
+static void gdth_readapp_event(gdth_ha_str *ha,
+ unchar application, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+ unchar found = FALSE;
+
+ TRACE2(("gdth_readapp_event() app. %d\n", application));
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ eindex = eoldidx;
+ for (;;) {
+ e = &ebuffer[eindex];
+ if (e->event_source == 0)
+ break;
+ if ((e->application & application) == 0) {
+ e->application |= application;
+ found = TRUE;
+ break;
+ }
+ if (eindex == elastidx)
+ break;
+ if (++eindex == MAX_EVENTS)
+ eindex = 0;
+ }
+ if (found)
+ memcpy(estr, e, sizeof(gdth_evt_str));
+ else
+ estr->event_source = 0;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+
+static void gdth_clear_events(void)
+{
+ TRACE(("gdth_clear_events()"));
+
+ eoldidx = elastidx = 0;
+ ebuffer[0].event_source = 0;
+}
+
+
+/* SCSI interface functions */
+
+static irqreturn_t gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+{
+ gdth_ha_str *ha2 = (gdth_ha_str *)dev_id;
+ register gdth_ha_str *ha;
+ gdt6m_dpram_str __iomem *dp6m_ptr = NULL;
+ gdt6_dpram_str __iomem *dp6_ptr;
+ gdt2_dpram_str __iomem *dp2_ptr;
+ Scsi_Cmnd *scp;
+ int hanum, rval, i;
+ unchar IStatus;
+ ushort Service;
+ ulong flags = 0;
+#ifdef INT_COAL
+ int coalesced = FALSE;
+ int next = FALSE;
+ gdth_coal_status *pcs = NULL;
+ int act_int_coal = 0;
+#endif
+
+ TRACE(("gdth_interrupt() IRQ %d\n",irq));
+
+ /* if polling and not from gdth_wait() -> return */
+ if (gdth_polling) {
+ if (!gdth_from_wait) {
+ return IRQ_HANDLED;
+ }
+ }
+
+ if (!gdth_polling)
+ spin_lock_irqsave(&ha2->smp_lock, flags);
+ wait_index = 0;
+
+ /* search controller */
+ if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
+ /* spurious interrupt */
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ return IRQ_HANDLED;
+ }
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+#ifdef GDTH_STATISTICS
+ ++act_ints;
+#endif
+
+#ifdef INT_COAL
+ /* See if the fw is returning coalesced status */
+ if (IStatus == COALINDEX) {
+ /* Coalesced status. Setup the initial status
+ buffer pointer and flags */
+ pcs = ha->coal_stat;
+ coalesced = TRUE;
+ next = TRUE;
+ }
+
+ do {
+ if (coalesced) {
+ /* For coalesced requests all status
+ information is found in the status buffer */
+ IStatus = (unchar)(pcs->status & 0xff);
+ }
+#endif
+
+ if (ha->type == GDT_EISA) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ ha->status = inw(ha->bmic + MAILBOXREG+8);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
+ } else /* no error */
+ ha->status = S_OK;
+ ha->info = inl(ha->bmic + MAILBOXREG+12);
+ ha->service = inw(ha->bmic + MAILBOXREG+10);
+ ha->info2 = inl(ha->bmic + MAILBOXREG+4);
+
+ outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */
+ outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */
+ } else if (ha->type == GDT_ISA) {
+ dp2_ptr = ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ ha->status = gdth_readw(&dp2_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
+ } else /* no error */
+ ha->status = S_OK;
+ ha->info = gdth_readl(&dp2_ptr->u.ic.Info[0]);
+ ha->service = gdth_readw(&dp2_ptr->u.ic.Service);
+ ha->info2 = gdth_readl(&dp2_ptr->u.ic.Info[1]);
+
+ gdth_writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */
+ gdth_writeb(0, &dp2_ptr->u.ic.Cmd_Index);/* reset command index */
+ gdth_writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ ha->status = gdth_readw(&dp6_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
+ } else /* no error */
+ ha->status = S_OK;
+ ha->info = gdth_readl(&dp6_ptr->u.ic.Info[0]);
+ ha->service = gdth_readw(&dp6_ptr->u.ic.Service);
+ ha->info2 = gdth_readl(&dp6_ptr->u.ic.Info[1]);
+
+ gdth_writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */
+ gdth_writeb(0, &dp6_ptr->u.ic.Cmd_Index);/* reset command index */
+ gdth_writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCINEW) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ ha->status = inw(PTR2USHORT(&ha->plx->status));
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
+ } else
+ ha->status = S_OK;
+ ha->info = inl(PTR2USHORT(&ha->plx->info[0]));
+ ha->service = inw(PTR2USHORT(&ha->plx->service));
+ ha->info2 = inl(PTR2USHORT(&ha->plx->info[1]));
+
+ outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+ outb(0x00, PTR2USHORT(&ha->plx->sema1_reg));
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+#ifdef INT_COAL
+ if (coalesced)
+ ha->status = pcs->ext_status && 0xffff;
+ else
+#endif
+ ha->status = gdth_readw(&dp6m_ptr->i960r.status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status));
+ } else /* no error */
+ ha->status = S_OK;
+#ifdef INT_COAL
+ /* get information */
+ if (coalesced) {
+ ha->info = pcs->info0;
+ ha->info2 = pcs->info1;
+ ha->service = (pcs->ext_status >> 16) && 0xffff;
+ } else
+#endif
+ {
+ ha->info = gdth_readl(&dp6m_ptr->i960r.info[0]);
+ ha->service = gdth_readw(&dp6m_ptr->i960r.service);
+ ha->info2 = gdth_readl(&dp6m_ptr->i960r.info[1]);
+ }
+ /* event string */
+ if (IStatus == ASYNCINDEX) {
+ if (ha->service != SCREENSERVICE &&
+ (ha->fw_vers & 0xff) >= 0x1a) {
+ ha->dvr.severity = gdth_readb
+ (&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.severity);
+ for (i = 0; i < 256; ++i) {
+ ha->dvr.event_string[i] = gdth_readb
+ (&((gdt6m_dpram_str __iomem *)ha->brd)->i960r.evt_str[i]);
+ if (ha->dvr.event_string[i] == 0)
+ break;
+ }
+ }
+ }
+#ifdef INT_COAL
+ /* Make sure that non coalesced interrupts get cleared
+ before being handled by gdth_async_event/gdth_sync_event */
+ if (!coalesced)
+#endif
+ {
+ gdth_writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ gdth_writeb(0, &dp6m_ptr->i960r.sema1_reg);
+ }
+ } else {
+ TRACE2(("gdth_interrupt() unknown controller type\n"));
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ TRACE(("gdth_interrupt() index %d stat %d info %d\n",
+ IStatus,ha->status,ha->info));
+
+ if (gdth_from_wait) {
+ wait_hanum = hanum;
+ wait_index = (int)IStatus;
+ }
+
+ if (IStatus == ASYNCINDEX) {
+ TRACE2(("gdth_interrupt() async. event\n"));
+ gdth_async_event(hanum);
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ gdth_next(hanum);
+ return IRQ_HANDLED;
+ }
+
+ if (IStatus == SPEZINDEX) {
+ TRACE2(("Service unknown or not initialized !\n"));
+ ha->dvr.size = sizeof(ha->dvr.eu.driver);
+ ha->dvr.eu.driver.ionode = hanum;
+ gdth_store_event(ha, ES_DRIVER, 4, &ha->dvr);
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ return IRQ_HANDLED;
+ }
+ scp = ha->cmd_tab[IStatus-2].cmnd;
+ Service = ha->cmd_tab[IStatus-2].service;
+ ha->cmd_tab[IStatus-2].cmnd = UNUSED_CMND;
+ if (scp == UNUSED_CMND) {
+ TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
+ ha->dvr.size = sizeof(ha->dvr.eu.driver);
+ ha->dvr.eu.driver.ionode = hanum;
+ ha->dvr.eu.driver.index = IStatus;
+ gdth_store_event(ha, ES_DRIVER, 1, &ha->dvr);
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ return IRQ_HANDLED;
+ }
+ if (scp == INTERNAL_CMND) {
+ TRACE(("gdth_interrupt() answer to internal command\n"));
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ TRACE(("gdth_interrupt() sync. status\n"));
+ rval = gdth_sync_event(hanum,Service,IStatus,scp);
+ if (!gdth_polling)
+ spin_unlock_irqrestore(&ha2->smp_lock, flags);
+ if (rval == 2) {
+ gdth_putq(hanum,scp,scp->SCp.this_residual);
+ } else if (rval == 1) {
+ scp->scsi_done(scp);
+ }
+
+#ifdef INT_COAL
+ if (coalesced) {
+ /* go to the next status in the status buffer */
+ ++pcs;
+#ifdef GDTH_STATISTICS
+ ++act_int_coal;
+ if (act_int_coal > max_int_coal) {
+ max_int_coal = act_int_coal;
+ printk("GDT: max_int_coal = %d\n",(ushort)max_int_coal);
+ }
+#endif
+ /* see if there is another status */
+ if (pcs->status == 0)
+ /* Stop the coalesce loop */
+ next = FALSE;
+ }
+ } while (next);
+
+ /* coalescing only for new GDT_PCIMPR controllers available */
+ if (ha->type == GDT_PCIMPR && coalesced) {
+ gdth_writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ gdth_writeb(0, &dp6m_ptr->i960r.sema1_reg);
+ }
+#endif
+
+ gdth_next(hanum);
+ return IRQ_HANDLED;
+}
+
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp)
+{
+ register gdth_ha_str *ha;
+ gdth_msg_str *msg;
+ gdth_cmd_str *cmdp;
+ unchar b, t;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_sync_event() serv %d status %d\n",
+ service,ha->status));
+
+ if (service == SCREENSERVICE) {
+ msg = ha->pmsg;
+ TRACE(("len: %d, answer: %d, ext: %d, alen: %d\n",
+ msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
+ if (msg->msg_len > MSGLEN+1)
+ msg->msg_len = MSGLEN+1;
+ if (msg->msg_len)
+ if (!(msg->msg_answer && msg->msg_ext)) {
+ msg->msg_text[msg->msg_len] = '\0';
+ printk("%s",msg->msg_text);
+ }
+
+ if (msg->msg_ext && !msg->msg_answer) {
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_READ;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.su.msg.msg_handle= msg->msg_handle;
+ cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
+ + sizeof(ulong64);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 0;
+ }
+
+ if (msg->msg_answer && msg->msg_alen) {
+ /* default answers (getchar() not possible) */
+ if (msg->msg_alen == 1) {
+ msg->msg_alen = 0;
+ msg->msg_len = 1;
+ msg->msg_text[0] = 0;
+ } else {
+ msg->msg_alen -= 2;
+ msg->msg_len = 2;
+ msg->msg_text[0] = 1;
+ msg->msg_text[1] = 0;
+ }
+ msg->msg_ext = 0;
+ msg->msg_answer = 0;
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_WRITE;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.su.msg.msg_handle= msg->msg_handle;
+ cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
+ + sizeof(ulong64);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 0;
+ }
+ printk("\n");
+
+ } else {
+ b = virt_ctr ? NUMDATA(scp->device->host)->busnum : scp->device->channel;
+ t = scp->device->id;
+ if (scp->SCp.sent_command == -1 && b != ha->virt_bus) {
+ ha->raw[BUS_L2P(ha,b)].io_cnt[t]--;
+ }
+ /* cache or raw service */
+ if (ha->status == S_BSY) {
+ TRACE2(("Controller busy -> retry !\n"));
+ if (scp->SCp.sent_command == GDT_MOUNT)
+ scp->SCp.sent_command = GDT_CLUST_INFO;
+ /* retry */
+ return 2;
+ }
+ if (scp->SCp.Status == GDTH_MAP_SG)
+ pci_unmap_sg(ha->pdev,scp->request_buffer,
+ scp->use_sg,scp->SCp.Message);
+ else if (scp->SCp.Status == GDTH_MAP_SINGLE)
+ pci_unmap_page(ha->pdev,scp->SCp.dma_handle,
+ scp->request_bufflen,scp->SCp.Message);
+ if (scp->SCp.buffer) {
+ dma_addr_t addr;
+ addr = (dma_addr_t)(ulong32)scp->SCp.buffer;
+ if (scp->host_scribble)
+ addr += (dma_addr_t)((ulong64)(ulong32)scp->host_scribble << 32);
+ pci_unmap_page(ha->pdev,addr,16,PCI_DMA_FROMDEVICE);
+ }
+
+ if (ha->status == S_OK) {
+ scp->SCp.Status = S_OK;
+ scp->SCp.Message = ha->info;
+ if (scp->SCp.sent_command != -1) {
+ TRACE2(("gdth_sync_event(): special cmd 0x%x OK\n",
+ scp->SCp.sent_command));
+ /* special commands GDT_CLUST_INFO/GDT_MOUNT ? */
+ if (scp->SCp.sent_command == GDT_CLUST_INFO) {
+ ha->hdr[t].cluster_type = (unchar)ha->info;
+ if (!(ha->hdr[t].cluster_type &
+ CLUSTER_MOUNTED)) {
+ /* NOT MOUNTED -> MOUNT */
+ scp->SCp.sent_command = GDT_MOUNT;
+ if (ha->hdr[t].cluster_type &
+ CLUSTER_RESERVED) {
+ /* cluster drive RESERVED (on the other node) */
+ scp->SCp.phase = -2; /* reservation conflict */
+ }
+ } else {
+ scp->SCp.sent_command = -1;
+ }
+ } else {
+ if (scp->SCp.sent_command == GDT_MOUNT) {
+ ha->hdr[t].cluster_type |= CLUSTER_MOUNTED;
+ ha->hdr[t].media_changed = TRUE;
+ } else if (scp->SCp.sent_command == GDT_UNMOUNT) {
+ ha->hdr[t].cluster_type &= ~CLUSTER_MOUNTED;
+ ha->hdr[t].media_changed = TRUE;
+ }
+ scp->SCp.sent_command = -1;
+ }
+ /* retry */
+ scp->SCp.this_residual = HIGH_PRI;
+ return 2;
+ } else {
+ /* RESERVE/RELEASE ? */
+ if (scp->cmnd[0] == RESERVE) {
+ ha->hdr[t].cluster_type |= CLUSTER_RESERVED;
+ } else if (scp->cmnd[0] == RELEASE) {
+ ha->hdr[t].cluster_type &= ~CLUSTER_RESERVED;
+ }
+ scp->result = DID_OK << 16;
+ scp->sense_buffer[0] = 0;
+ }
+ } else {
+ scp->SCp.Status = ha->status;
+ scp->SCp.Message = ha->info;
+
+ if (scp->SCp.sent_command != -1) {
+ TRACE2(("gdth_sync_event(): special cmd 0x%x error 0x%x\n",
+ scp->SCp.sent_command, ha->status));
+ if (scp->SCp.sent_command == GDT_SCAN_START ||
+ scp->SCp.sent_command == GDT_SCAN_END) {
+ scp->SCp.sent_command = -1;
+ /* retry */
+ scp->SCp.this_residual = HIGH_PRI;
+ return 2;
+ }
+ memset((char*)scp->sense_buffer,0,16);
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = NOT_READY;
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ } else if (service == CACHESERVICE) {
+ if (ha->status == S_CACHE_UNKNOWN &&
+ (ha->hdr[t].cluster_type &
+ CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) {
+ /* bus reset -> force GDT_CLUST_INFO */
+ ha->hdr[t].cluster_type &= ~CLUSTER_RESERVED;
+ }
+ memset((char*)scp->sense_buffer,0,16);
+ if (ha->status == (ushort)S_CACHE_RESERV) {
+ scp->result = (DID_OK << 16) | (RESERVATION_CONFLICT << 1);
+ } else {
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = NOT_READY;
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ }
+ if (scp->done != gdth_scsi_done) {
+ ha->dvr.size = sizeof(ha->dvr.eu.sync);
+ ha->dvr.eu.sync.ionode = hanum;
+ ha->dvr.eu.sync.service = service;
+ ha->dvr.eu.sync.status = ha->status;
+ ha->dvr.eu.sync.info = ha->info;
+ ha->dvr.eu.sync.hostdrive = t;
+ if (ha->status >= 0x8000)
+ gdth_store_event(ha, ES_SYNC, 0, &ha->dvr);
+ else
+ gdth_store_event(ha, ES_SYNC, service, &ha->dvr);
+ }
+ } else {
+ /* sense buffer filled from controller firmware (DMA) */
+ if (ha->status != S_RAW_SCSI || ha->info >= 0x100) {
+ scp->result = DID_BAD_TARGET << 16;
+ } else {
+ scp->result = (DID_OK << 16) | ha->info;
+ }
+ }
+ }
+ if (!scp->SCp.have_data_in)
+ scp->SCp.have_data_in++;
+ else
+ return 1;
+ }
+
+ return 0;
+}
+
+static char *async_cache_tab[] = {
+/* 0*/ "\011\000\002\002\002\004\002\006\004"
+ "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 1*/ "\011\000\002\002\002\004\002\006\004"
+ "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 2*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu not ready",
+/* 3*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 4*/ "\005\000\002\006\004"
+ "GDT HA %u, mirror update on Host Drive %lu failed",
+/* 5*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu failed",
+/* 6*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 7*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu write protected",
+/* 8*/ "\005\000\002\006\004"
+ "GDT HA %u, media changed in Host Drive %lu",
+/* 9*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu is offline",
+/*10*/ "\005\000\002\006\004"
+ "GDT HA %u, media change of Mirror Drive %lu",
+/*11*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu is write protected",
+/*12*/ "\005\000\002\006\004"
+ "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!",
+/*13*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: Cache Drive %u failed",
+/*14*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: FAIL state entered",
+/*15*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: error",
+/*16*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u",
+/*17*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity build failed",
+/*18*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild failed",
+/*19*/ "\005\000\002\010\002"
+ "GDT HA %u, Test of Hot Fix %u failed",
+/*20*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive build finished successfully",
+/*21*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild finished successfully",
+/*22*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: Hot Fix %u activated",
+/*23*/ "\005\000\002\006\002"
+ "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error",
+/*24*/ "\005\000\002\010\002"
+ "GDT HA %u, mirror update on Cache Drive %u completed",
+/*25*/ "\005\000\002\010\002"
+ "GDT HA %u, mirror update on Cache Drive %lu failed",
+/*26*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild started",
+/*27*/ "\005\000\002\012\001"
+ "GDT HA %u, Fault bus %u: SHELF OK detected",
+/*28*/ "\005\000\002\012\001"
+ "GDT HA %u, Fault bus %u: SHELF not OK detected",
+/*29*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started",
+/*30*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: new disk detected",
+/*31*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: old disk detected",
+/*32*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is invalid",
+/*33*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: invalid device detected",
+/*34*/ "\011\000\002\012\001\013\001\006\004"
+ "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)",
+/*35*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: disk write protected",
+/*36*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: disk not available",
+/*37*/ "\007\000\002\012\001\006\004"
+ "GDT HA %u, Fault bus %u: swap detected (%lu)",
+/*38*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully",
+/*39*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug",
+/*40*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted",
+/*41*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started",
+/*42*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive build started",
+/*43*/ "\003\000\002"
+ "GDT HA %u, DRAM parity error detected",
+/*44*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: update started",
+/*45*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Mirror Drive %u: Hot Fix %u activated",
+/*46*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available",
+/*47*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available",
+/*48*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available",
+/*49*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available",
+/*50*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received",
+/*51*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand started",
+/*52*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand finished successfully",
+/*53*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand failed",
+/*54*/ "\003\000\002"
+ "GDT HA %u, CPU temperature critical",
+/*55*/ "\003\000\002"
+ "GDT HA %u, CPU temperature OK",
+/*56*/ "\005\000\002\006\004"
+ "GDT HA %u, Host drive %lu created",
+/*57*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand restarted",
+/*58*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand stopped",
+/*59*/ "\005\000\002\010\002"
+ "GDT HA %u, Mirror Drive %u: drive build quited",
+/*60*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity build quited",
+/*61*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild quited",
+/*62*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity verify started",
+/*63*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity verify done",
+/*64*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity verify failed",
+/*65*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity error detected",
+/*66*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity verify quited",
+/*67*/ "\005\000\002\006\002"
+ "GDT HA %u, Host Drive %u reserved",
+/*68*/ "\005\000\002\006\002"
+ "GDT HA %u, Host Drive %u mounted and released",
+/*69*/ "\005\000\002\006\002"
+ "GDT HA %u, Host Drive %u released",
+/*70*/ "\003\000\002"
+ "GDT HA %u, DRAM error detected and corrected with ECC",
+/*71*/ "\003\000\002"
+ "GDT HA %u, Uncorrectable DRAM error detected with ECC",
+/*72*/ "\011\000\002\012\001\013\001\014\001"
+ "GDT HA %u, SCSI bus %u, ID %u, LUN %u: reassigning block",
+/*73*/ "\005\000\002\006\002"
+ "GDT HA %u, Host drive %u resetted locally",
+/*74*/ "\005\000\002\006\002"
+ "GDT HA %u, Host drive %u resetted remotely",
+/*75*/ "\003\000\002"
+ "GDT HA %u, async. status 75 unknown",
+};
+
+
+static int gdth_async_event(int hanum)
+{
+ gdth_ha_str *ha;
+ gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ TRACE2(("gdth_async_event() ha %d serv %d\n",
+ hanum,ha->service));
+
+ if (ha->service == SCREENSERVICE) {
+ if (ha->status == MSG_REQUEST) {
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ cmd_index = gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_READ;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.su.msg.msg_handle= MSG_INV_HANDLE;
+ cmdp->u.screen.su.msg.msg_addr = ha->msg_phys;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr)
+ + sizeof(ulong64);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ if (ha->type == GDT_EISA)
+ printk("[EISA slot %d] ",(ushort)ha->brd_phys);
+ else if (ha->type == GDT_ISA)
+ printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys);
+ else
+ printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8),
+ (ushort)((ha->brd_phys>>3)&0x1f));
+ gdth_release_event(hanum);
+ }
+
+ } else {
+ if (ha->type == GDT_PCIMPR &&
+ (ha->fw_vers & 0xff) >= 0x1a) {
+ ha->dvr.size = 0;
+ ha->dvr.eu.async.ionode = hanum;
+ ha->dvr.eu.async.status = ha->status;
+ /* severity and event_string already set! */
+ } else {
+ ha->dvr.size = sizeof(ha->dvr.eu.async);
+ ha->dvr.eu.async.ionode = hanum;
+ ha->dvr.eu.async.service = ha->service;
+ ha->dvr.eu.async.status = ha->status;
+ ha->dvr.eu.async.info = ha->info;
+ *(ulong32 *)ha->dvr.eu.async.scsi_coord = ha->info2;
+ }
+ gdth_store_event( ha, ES_ASYNC, ha->service, &ha->dvr );
+ gdth_log_event( &ha->dvr, NULL );
+
+ /* new host drive from expand? */
+ if (ha->service == CACHESERVICE && ha->status == 56) {
+ TRACE2(("gdth_async_event(): new host drive %d created\n",
+ (ushort)ha->info));
+ /* gdth_analyse_hdrive(hanum, (ushort)ha->info); */
+ }
+ }
+ return 1;
+}
+
+static void gdth_log_event(gdth_evt_data *dvr, char *buffer)
+{
+ gdth_stackframe stack;
+ char *f = NULL;
+ int i,j;
+
+ TRACE2(("gdth_log_event()\n"));
+ if (dvr->size == 0) {
+ if (buffer == NULL) {
+ printk("Adapter %d: %s\n",dvr->eu.async.ionode,dvr->event_string);
+ } else {
+ sprintf(buffer,"Adapter %d: %s\n",
+ dvr->eu.async.ionode,dvr->event_string);
+ }
+ } else if (dvr->eu.async.service == CACHESERVICE &&
+ INDEX_OK(dvr->eu.async.status, async_cache_tab)) {
+ TRACE2(("GDT: Async. event cache service, event no.: %d\n",
+ dvr->eu.async.status));
+
+ f = async_cache_tab[dvr->eu.async.status];
+
+ /* i: parameter to push, j: stack element to fill */
+ for (j=0,i=1; i < f[0]; i+=2) {
+ switch (f[i+1]) {
+ case 4:
+ stack.b[j++] = *(ulong32*)&dvr->eu.stream[(int)f[i]];
+ break;
+ case 2:
+ stack.b[j++] = *(ushort*)&dvr->eu.stream[(int)f[i]];
+ break;
+ case 1:
+ stack.b[j++] = *(unchar*)&dvr->eu.stream[(int)f[i]];
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (buffer == NULL) {
+ printk(&f[(int)f[0]],stack);
+ printk("\n");
+ } else {
+ sprintf(buffer,&f[(int)f[0]],stack);
+ }
+
+ } else {
+ if (buffer == NULL) {
+ printk("GDT HA %u, Unknown async. event service %d event no. %d\n",
+ dvr->eu.async.ionode,dvr->eu.async.service,dvr->eu.async.status);
+ } else {
+ sprintf(buffer,"GDT HA %u, Unknown async. event service %d event no. %d",
+ dvr->eu.async.ionode,dvr->eu.async.service,dvr->eu.async.status);
+ }
+ }
+}
+
+#ifdef GDTH_STATISTICS
+void gdth_timeout(ulong data)
+{
+ ulong32 i;
+ Scsi_Cmnd *nscp;
+ gdth_ha_str *ha;
+ ulong flags;
+ int hanum = 0;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i)
+ if (ha->cmd_tab[i].cmnd != UNUSED_CMND)
+ ++act_stats;
+
+ for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++act_rq;
+
+ TRACE2(("gdth_to(): ints %d, ios %d, act_stats %d, act_rq %d\n",
+ act_ints, act_ios, act_stats, act_rq));
+ act_ints = act_ios = 0;
+
+ gdth_timer.expires = jiffies + 30 * HZ;
+ add_timer(&gdth_timer);
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+#endif
+
+void __init internal_setup(char *str,int *ints)
+{
+ int i, argc;
+ char *cur_str, *argv;
+
+ TRACE2(("internal_setup() str %s ints[0] %d\n",
+ str ? str:"NULL", ints ? ints[0]:0));
+
+ /* read irq[] from ints[] */
+ if (ints) {
+ argc = ints[0];
+ if (argc > 0) {
+ if (argc > MAXHA)
+ argc = MAXHA;
+ for (i = 0; i < argc; ++i)
+ irq[i] = ints[i+1];
+ }
+ }
+
+ /* analyse string */
+ argv = str;
+ while (argv && (cur_str = strchr(argv, ':'))) {
+ int val = 0, c = *++cur_str;
+
+ if (c == 'n' || c == 'N')
+ val = 0;
+ else if (c == 'y' || c == 'Y')
+ val = 1;
+ else
+ val = (int)simple_strtoul(cur_str, NULL, 0);
+
+ if (!strncmp(argv, "disable:", 8))
+ disable = val;
+ else if (!strncmp(argv, "reserve_mode:", 13))
+ reserve_mode = val;
+ else if (!strncmp(argv, "reverse_scan:", 13))
+ reverse_scan = val;
+ else if (!strncmp(argv, "hdr_channel:", 12))
+ hdr_channel = val;
+ else if (!strncmp(argv, "max_ids:", 8))
+ max_ids = val;
+ else if (!strncmp(argv, "rescan:", 7))
+ rescan = val;
+ else if (!strncmp(argv, "virt_ctr:", 9))
+ virt_ctr = val;
+ else if (!strncmp(argv, "shared_access:", 14))
+ shared_access = val;
+ else if (!strncmp(argv, "probe_eisa_isa:", 15))
+ probe_eisa_isa = val;
+ else if (!strncmp(argv, "reserve_list:", 13)) {
+ reserve_list[0] = val;
+ for (i = 1; i < MAX_RES_ARGS; i++) {
+ cur_str = strchr(cur_str, ',');
+ if (!cur_str)
+ break;
+ if (!isdigit((int)*++cur_str)) {
+ --cur_str;
+ break;
+ }
+ reserve_list[i] =
+ (int)simple_strtoul(cur_str, NULL, 0);
+ }
+ if (!cur_str)
+ break;
+ argv = ++cur_str;
+ continue;
+ }
+
+ if ((argv = strchr(argv, ',')))
+ ++argv;
+ }
+}
+
+int __init option_setup(char *str)
+{
+ int ints[MAXHA];
+ char *cur = str;
+ int i = 1;
+
+ TRACE2(("option_setup() str %s\n", str ? str:"NULL"));
+
+ while (cur && isdigit(*cur) && i <= MAXHA) {
+ ints[i++] = simple_strtoul(cur, NULL, 0);
+ if ((cur = strchr(cur, ',')) != NULL) cur++;
+ }
+
+ ints[0] = i - 1;
+ internal_setup(cur, ints);
+ return 1;
+}
+
+int __init gdth_detect(Scsi_Host_Template *shtp)
+{
+ struct Scsi_Host *shp;
+ gdth_pci_str pcistr[MAXHA];
+ gdth_ha_str *ha;
+ ulong32 isa_bios;
+ ushort eisa_slot;
+ int i,hanum,cnt,ctr,err;
+ unchar b;
+
+
+#ifdef DEBUG_GDTH
+ printk("GDT: This driver contains debugging information !! Trace level = %d\n",
+ DebugState);
+ printk(" Destination of debugging information: ");
+#ifdef __SERIAL__
+#ifdef __COM2__
+ printk("Serial port COM2\n");
+#else
+ printk("Serial port COM1\n");
+#endif
+#else
+ printk("Console\n");
+#endif
+ gdth_delay(3000);
+#endif
+
+ TRACE(("gdth_detect()\n"));
+
+ if (disable) {
+ printk("GDT-HA: Controller driver disabled from command line !\n");
+ return 0;
+ }
+
+ printk("GDT-HA: Storage RAID Controller Driver. Version: %s \n",GDTH_VERSION_STR);
+ /* initializations */
+ gdth_polling = TRUE; b = 0;
+ gdth_clear_events();
+
+ /* As default we do not probe for EISA or ISA controllers */
+ if (probe_eisa_isa) {
+ /* scanning for controllers, at first: ISA controller */
+ for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+ dma_addr_t scratch_dma_handle;
+ scratch_dma_handle = 0;
+
+ if (gdth_ctr_count >= MAXHA)
+ break;
+ if (gdth_search_isa(isa_bios)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ if (shp == NULL)
+ continue;
+
+ ha = HADATA(shp);
+ if (!gdth_init_isa(isa_bios,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+#ifdef __ia64__
+ break;
+#else
+ /* controller found and initialized */
+ printk("Configuring GDT-ISA HA at BIOS 0x%05X IRQ %u DRQ %u\n",
+ isa_bios,ha->irq,ha->drq);
+
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",ha)) {
+ printk("GDT-ISA: Unable to allocate IRQ\n");
+ scsi_unregister(shp);
+ continue;
+ }
+ if (request_dma(ha->drq,"gdth")) {
+ printk("GDT-ISA: Unable to allocate DMA channel\n");
+ free_irq(ha->irq,ha);
+ scsi_unregister(shp);
+ continue;
+ }
+ set_dma_mode(ha->drq,DMA_MODE_CASCADE);
+ enable_dma(ha->drq);
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+
+ ha->pccb = CMDDATA(shp);
+ ha->ccb_phys = 0L;
+ ha->pdev = NULL;
+ ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
+ &scratch_dma_handle);
+ ha->scratch_phys = scratch_dma_handle;
+ ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
+ &scratch_dma_handle);
+ ha->msg_phys = scratch_dma_handle;
+#ifdef INT_COAL
+ ha->coal_stat = (gdth_coal_status *)
+ pci_alloc_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, &scratch_dma_handle);
+ ha->coal_stat_phys = scratch_dma_handle;
+#endif
+
+ ha->scratch_busy = FALSE;
+ ha->req_first = NULL;
+ ha->tid_cnt = MAX_HDRIVES;
+ if (max_ids > 0 && max_ids < ha->tid_cnt)
+ ha->tid_cnt = max_ids;
+ for (i=0; i<GDTH_MAXCMDS; ++i)
+ ha->cmd_tab[i].cmnd = UNUSED_CMND;
+ ha->scan_mode = rescan ? 0x10 : 0;
+
+ if (ha->pscratch == NULL || ha->pmsg == NULL ||
+ !gdth_search_drives(hanum)) {
+ printk("GDT-ISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+
+#ifdef INT_COAL
+ if (ha->coal_stat)
+ pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, ha->coal_stat,
+ ha->coal_stat_phys);
+#endif
+ if (ha->pscratch)
+ pci_free_consistent(ha->pdev, GDTH_SCRATCH,
+ ha->pscratch, ha->scratch_phys);
+ if (ha->pmsg)
+ pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
+ ha->pmsg, ha->msg_phys);
+
+ free_irq(ha->irq,ha);
+ scsi_unregister(shp);
+ continue;
+ }
+ if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
+ hdr_channel = ha->bus_cnt;
+ ha->virt_bus = hdr_channel;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ shp->highmem_io = 0;
+#endif
+ if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT)
+ shp->max_cmd_len = 16;
+
+ shp->max_id = ha->tid_cnt;
+ shp->max_lun = MAXLUN;
+ shp->max_channel = virt_ctr ? 0 : ha->bus_cnt;
+ if (virt_ctr) {
+ virt_ctr = 1;
+ /* register addit. SCSI channels as virtual controllers */
+ for (b = 1; b < ha->bus_cnt + 1; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+ }
+
+ spin_lock_init(&ha->smp_lock);
+ gdth_enable_int(hanum);
+#endif /* !__ia64__ */
+ }
+ }
+
+ /* scanning for EISA controllers */
+ for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) {
+ dma_addr_t scratch_dma_handle;
+ scratch_dma_handle = 0;
+
+ if (gdth_ctr_count >= MAXHA)
+ break;
+ if (gdth_search_eisa(eisa_slot)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ if (shp == NULL)
+ continue;
+
+ ha = HADATA(shp);
+ if (!gdth_init_eisa(eisa_slot,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
+ eisa_slot>>12,ha->irq);
+
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",ha)) {
+ printk("GDT-EISA: Unable to allocate IRQ\n");
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+ TRACE2(("EISA detect Bus 0: hanum %d\n",
+ NUMDATA(shp)->hanum));
+
+ ha->pccb = CMDDATA(shp);
+ ha->ccb_phys = 0L;
+
+ ha->pdev = NULL;
+ ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
+ &scratch_dma_handle);
+ ha->scratch_phys = scratch_dma_handle;
+ ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
+ &scratch_dma_handle);
+ ha->msg_phys = scratch_dma_handle;
+#ifdef INT_COAL
+ ha->coal_stat = (gdth_coal_status *)
+ pci_alloc_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, &scratch_dma_handle);
+ ha->coal_stat_phys = scratch_dma_handle;
+#endif
+ ha->ccb_phys =
+ pci_map_single(ha->pdev,ha->pccb,
+ sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL);
+ ha->scratch_busy = FALSE;
+ ha->req_first = NULL;
+ ha->tid_cnt = MAX_HDRIVES;
+ if (max_ids > 0 && max_ids < ha->tid_cnt)
+ ha->tid_cnt = max_ids;
+ for (i=0; i<GDTH_MAXCMDS; ++i)
+ ha->cmd_tab[i].cmnd = UNUSED_CMND;
+ ha->scan_mode = rescan ? 0x10 : 0;
+
+ if (ha->pscratch == NULL || ha->pmsg == NULL ||
+ !gdth_search_drives(hanum)) {
+ printk("GDT-EISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+#ifdef INT_COAL
+ if (ha->coal_stat)
+ pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, ha->coal_stat,
+ ha->coal_stat_phys);
+#endif
+ if (ha->pscratch)
+ pci_free_consistent(ha->pdev, GDTH_SCRATCH,
+ ha->pscratch, ha->scratch_phys);
+ if (ha->pmsg)
+ pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
+ ha->pmsg, ha->msg_phys);
+ if (ha->ccb_phys)
+ pci_unmap_single(ha->pdev,ha->ccb_phys,
+ sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL);
+ free_irq(ha->irq,ha);
+ scsi_unregister(shp);
+ continue;
+ }
+ if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
+ hdr_channel = ha->bus_cnt;
+ ha->virt_bus = hdr_channel;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ shp->highmem_io = 0;
+#endif
+ if (ha->cache_feat & ha->raw_feat & ha->screen_feat & GDT_64BIT)
+ shp->max_cmd_len = 16;
+
+ shp->max_id = ha->tid_cnt;
+ shp->max_lun = MAXLUN;
+ shp->max_channel = virt_ctr ? 0 : ha->bus_cnt;
+ if (virt_ctr) {
+ virt_ctr = 1;
+ /* register addit. SCSI channels as virtual controllers */
+ for (b = 1; b < ha->bus_cnt + 1; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+ }
+
+ spin_lock_init(&ha->smp_lock);
+ gdth_enable_int(hanum);
+ }
+ }
+ }
+
+ /* scanning for PCI controllers */
+ cnt = gdth_search_pci(pcistr);
+ printk("GDT-HA: Found %d PCI Storage RAID Controllers\n",cnt);
+ gdth_sort_pci(pcistr,cnt);
+ for (ctr = 0; ctr < cnt; ++ctr) {
+ dma_addr_t scratch_dma_handle;
+ scratch_dma_handle = 0;
+
+ if (gdth_ctr_count >= MAXHA)
+ break;
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ if (shp == NULL)
+ continue;
+
+ ha = HADATA(shp);
+ if (!gdth_init_pci(&pcistr[ctr],ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
+ pcistr[ctr].bus,PCI_SLOT(pcistr[ctr].device_fn),ha->irq);
+
+ if (request_irq(ha->irq, gdth_interrupt,
+ SA_INTERRUPT|SA_SHIRQ, "gdth", ha))
+ {
+ printk("GDT-PCI: Unable to allocate IRQ\n");
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+
+ ha->pccb = CMDDATA(shp);
+ ha->ccb_phys = 0L;
+
+ ha->pscratch = pci_alloc_consistent(ha->pdev, GDTH_SCRATCH,
+ &scratch_dma_handle);
+ ha->scratch_phys = scratch_dma_handle;
+ ha->pmsg = pci_alloc_consistent(ha->pdev, sizeof(gdth_msg_str),
+ &scratch_dma_handle);
+ ha->msg_phys = scratch_dma_handle;
+#ifdef INT_COAL
+ ha->coal_stat = (gdth_coal_status *)
+ pci_alloc_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, &scratch_dma_handle);
+ ha->coal_stat_phys = scratch_dma_handle;
+#endif
+ ha->scratch_busy = FALSE;
+ ha->req_first = NULL;
+ ha->tid_cnt = pcistr[ctr].device_id >= 0x200 ? MAXID : MAX_HDRIVES;
+ if (max_ids > 0 && max_ids < ha->tid_cnt)
+ ha->tid_cnt = max_ids;
+ for (i=0; i<GDTH_MAXCMDS; ++i)
+ ha->cmd_tab[i].cmnd = UNUSED_CMND;
+ ha->scan_mode = rescan ? 0x10 : 0;
+
+ err = FALSE;
+ if (ha->pscratch == NULL || ha->pmsg == NULL ||
+ !gdth_search_drives(hanum)) {
+ err = TRUE;
+ } else {
+ if (hdr_channel < 0 || hdr_channel > ha->bus_cnt)
+ hdr_channel = ha->bus_cnt;
+ ha->virt_bus = hdr_channel;
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scsi_set_device(shp, &pcistr[ctr].pdev->dev);
+#else
+ scsi_set_pci_device(shp, pcistr[ctr].pdev);
+#endif
+ if (!(ha->cache_feat & ha->raw_feat & ha->screen_feat &GDT_64BIT)||
+ /* 64-bit DMA only supported from FW >= x.43 */
+ (!ha->dma64_support)) {
+ if (pci_set_dma_mask(pcistr[ctr].pdev, 0xffffffff)) {
+ printk(KERN_WARNING "GDT-PCI %d: Unable to set 32-bit DMA\n", hanum);
+ err = TRUE;
+ }
+ } else {
+ shp->max_cmd_len = 16;
+ if (!pci_set_dma_mask(pcistr[ctr].pdev, 0xffffffffffffffffULL)) {
+ printk("GDT-PCI %d: 64-bit DMA enabled\n", hanum);
+ } else if (pci_set_dma_mask(pcistr[ctr].pdev, 0xffffffff)) {
+ printk(KERN_WARNING "GDT-PCI %d: Unable to set 64/32-bit DMA\n", hanum);
+ err = TRUE;
+ }
+ }
+ }
+
+ if (err) {
+ printk("GDT-PCI %d: Error during device scan\n", hanum);
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+#ifdef INT_COAL
+ if (ha->coal_stat)
+ pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, ha->coal_stat,
+ ha->coal_stat_phys);
+#endif
+ if (ha->pscratch)
+ pci_free_consistent(ha->pdev, GDTH_SCRATCH,
+ ha->pscratch, ha->scratch_phys);
+ if (ha->pmsg)
+ pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
+ ha->pmsg, ha->msg_phys);
+ free_irq(ha->irq,ha);
+ scsi_unregister(shp);
+ continue;
+ }
+
+ shp->max_id = ha->tid_cnt;
+ shp->max_lun = MAXLUN;
+ shp->max_channel = virt_ctr ? 0 : ha->bus_cnt;
+ if (virt_ctr) {
+ virt_ctr = 1;
+ /* register addit. SCSI channels as virtual controllers */
+ for (b = 1; b < ha->bus_cnt + 1; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+ }
+
+ spin_lock_init(&ha->smp_lock);
+ gdth_enable_int(hanum);
+ }
+
+ TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count));
+ if (gdth_ctr_count > 0) {
+#ifdef GDTH_STATISTICS
+ TRACE2(("gdth_detect(): Initializing timer !\n"));
+ init_timer(&gdth_timer);
+ gdth_timer.expires = jiffies + HZ;
+ gdth_timer.data = 0L;
+ gdth_timer.function = gdth_timeout;
+ add_timer(&gdth_timer);
+#endif
+ major = register_chrdev(0,"gdth",&gdth_fops);
+ register_reboot_notifier(&gdth_notifier);
+ }
+ gdth_polling = FALSE;
+ return gdth_ctr_vcount;
+}
+
+
+int gdth_release(struct Scsi_Host *shp)
+{
+ int hanum;
+ gdth_ha_str *ha;
+
+ TRACE2(("gdth_release()\n"));
+ if (NUMDATA(shp)->busnum == 0) {
+ hanum = NUMDATA(shp)->hanum;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->sdev) {
+ scsi_free_host_dev(ha->sdev);
+ ha->sdev = NULL;
+ }
+ gdth_flush(hanum);
+
+ if (shp->irq) {
+ free_irq(shp->irq,ha);
+ }
+#ifndef __ia64__
+ if (shp->dma_channel != 0xff) {
+ free_dma(shp->dma_channel);
+ }
+#endif
+#ifdef INT_COAL
+ if (ha->coal_stat)
+ pci_free_consistent(ha->pdev, sizeof(gdth_coal_status) *
+ MAXOFFSETS, ha->coal_stat, ha->coal_stat_phys);
+#endif
+ if (ha->pscratch)
+ pci_free_consistent(ha->pdev, GDTH_SCRATCH,
+ ha->pscratch, ha->scratch_phys);
+ if (ha->pmsg)
+ pci_free_consistent(ha->pdev, sizeof(gdth_msg_str),
+ ha->pmsg, ha->msg_phys);
+ if (ha->ccb_phys)
+ pci_unmap_single(ha->pdev,ha->ccb_phys,
+ sizeof(gdth_cmd_str),PCI_DMA_BIDIRECTIONAL);
+ gdth_ctr_released++;
+ TRACE2(("gdth_release(): HA %d of %d\n",
+ gdth_ctr_released, gdth_ctr_count));
+
+ if (gdth_ctr_released == gdth_ctr_count) {
+#ifdef GDTH_STATISTICS
+ del_timer(&gdth_timer);
+#endif
+ unregister_chrdev(major,"gdth");
+ unregister_reboot_notifier(&gdth_notifier);
+ }
+ }
+
+ scsi_unregister(shp);
+ return 0;
+}
+
+
+static const char *gdth_ctr_name(int hanum)
+{
+ gdth_ha_str *ha;
+
+ TRACE2(("gdth_ctr_name()\n"));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->type == GDT_EISA) {
+ switch (ha->stype) {
+ case GDT3_ID:
+ return("GDT3000/3020");
+ case GDT3A_ID:
+ return("GDT3000A/3020A/3050A");
+ case GDT3B_ID:
+ return("GDT3000B/3010A");
+ }
+ } else if (ha->type == GDT_ISA) {
+ return("GDT2000/2020");
+ } else if (ha->type == GDT_PCI) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT60x0:
+ return("GDT6000/6020/6050");
+ case PCI_DEVICE_ID_VORTEX_GDT6000B:
+ return("GDT6000B/6010");
+ }
+ }
+ /* new controllers (GDT_PCINEW, GDT_PCIMPR, ..) use board_info IOCTL! */
+
+ return("");
+}
+
+const char *gdth_info(struct Scsi_Host *shp)
+{
+ int hanum;
+ gdth_ha_str *ha;
+
+ TRACE2(("gdth_info()\n"));
+ hanum = NUMDATA(shp)->hanum;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ return ((const char *)ha->binfo.type_string);
+}
+
+/* new error handling */
+int gdth_eh_abort(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_abort()\n"));
+ return FAILED;
+}
+
+int gdth_eh_device_reset(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_device_reset()\n"));
+ return FAILED;
+}
+
+int gdth_eh_bus_reset(Scsi_Cmnd *scp)
+{
+ int i, hanum;
+ gdth_ha_str *ha;
+ ulong flags;
+ Scsi_Cmnd *cmnd;
+ unchar b;
+
+ TRACE2(("gdth_eh_bus_reset()\n"));
+
+ hanum = NUMDATA(scp->device->host)->hanum;
+ b = virt_ctr ? NUMDATA(scp->device->host)->busnum : scp->device->channel;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ /* clear command tab */
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ for (i = 0; i < GDTH_MAXCMDS; ++i) {
+ cmnd = ha->cmd_tab[i].cmnd;
+ if (!SPECIAL_SCP(cmnd) && cmnd->device->channel == b)
+ ha->cmd_tab[i].cmnd = UNUSED_CMND;
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+
+ if (b == ha->virt_bus) {
+ /* host drives */
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (ha->hdr[i].present) {
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ gdth_polling = TRUE;
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ if (gdth_internal_cmd(hanum, CACHESERVICE,
+ GDT_CLUST_RESET, i, 0, 0))
+ ha->hdr[i].cluster_type &= ~CLUSTER_RESERVED;
+ gdth_polling = FALSE;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ }
+ }
+ } else {
+ /* raw devices */
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ for (i = 0; i < MAXID; ++i)
+ ha->raw[BUS_L2P(ha,b)].io_cnt[i] = 0;
+ gdth_polling = TRUE;
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS,
+ BUS_L2P(ha,b), 0, 0);
+ gdth_polling = FALSE;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ }
+ return SUCCESS;
+}
+
+int gdth_eh_host_reset(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_host_reset()\n"));
+ return FAILED;
+}
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+int gdth_bios_param(struct scsi_device *sdev,struct block_device *bdev,sector_t cap,int *ip)
+#else
+int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
+#endif
+{
+ unchar b, t;
+ int hanum;
+ gdth_ha_str *ha;
+ struct scsi_device *sd;
+ unsigned capacity;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ sd = sdev;
+ capacity = cap;
+#else
+ sd = disk->device;
+ capacity = disk->capacity;
+#endif
+ hanum = NUMDATA(sd->host)->hanum;
+ b = virt_ctr ? NUMDATA(sd->host)->busnum : sd->channel;
+ t = sd->id;
+ TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (b != ha->virt_bus || ha->hdr[t].heads == 0) {
+ /* raw device or host drive without mapping information */
+ TRACE2(("Evaluate mapping\n"));
+ gdth_eval_mapping(capacity,&ip[2],&ip[0],&ip[1]);
+ } else {
+ ip[0] = ha->hdr[t].heads;
+ ip[1] = ha->hdr[t].secs;
+ ip[2] = capacity / ip[0] / ip[1];
+ }
+
+ TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
+ ip[0],ip[1],ip[2]));
+ return 0;
+}
+
+
+int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
+{
+ int hanum;
+ int priority;
+
+ TRACE(("gdth_queuecommand() cmd 0x%x\n", scp->cmnd[0]));
+
+ scp->scsi_done = (void *)done;
+ scp->SCp.have_data_in = 1;
+ scp->SCp.phase = -1;
+ scp->SCp.sent_command = -1;
+ scp->SCp.Status = GDTH_MAP_NONE;
+ scp->SCp.buffer = (struct scatterlist *)NULL;
+
+ hanum = NUMDATA(scp->device->host)->hanum;
+#ifdef GDTH_STATISTICS
+ ++act_ios;
+#endif
+
+ priority = DEFAULT_PRI;
+ if (scp->done == gdth_scsi_done)
+ priority = scp->SCp.this_residual;
+ gdth_update_timeout(hanum, scp, scp->timeout_per_command * 6);
+ gdth_putq( hanum, scp, priority );
+ gdth_next( hanum );
+ return 0;
+}
+
+
+static int gdth_open(struct inode *inode, struct file *filep)
+{
+ gdth_ha_str *ha;
+ int i;
+
+ for (i = 0; i < gdth_ctr_count; i++) {
+ ha = HADATA(gdth_ctr_tab[i]);
+ if (!ha->sdev)
+ ha->sdev = scsi_get_host_dev(gdth_ctr_tab[i]);
+ }
+
+ TRACE(("gdth_open()\n"));
+ return 0;
+}
+
+static int gdth_close(struct inode *inode, struct file *filep)
+{
+ TRACE(("gdth_close()\n"));
+ return 0;
+}
+
+static int ioc_event(void __user *arg)
+{
+ gdth_ioctl_event evt;
+ gdth_ha_str *ha;
+ ulong flags;
+
+ if (copy_from_user(&evt, arg, sizeof(gdth_ioctl_event)) ||
+ evt.ionode >= gdth_ctr_count)
+ return -EFAULT;
+ ha = HADATA(gdth_ctr_tab[evt.ionode]);
+
+ if (evt.erase == 0xff) {
+ if (evt.event.event_source == ES_TEST)
+ evt.event.event_data.size=sizeof(evt.event.event_data.eu.test);
+ else if (evt.event.event_source == ES_DRIVER)
+ evt.event.event_data.size=sizeof(evt.event.event_data.eu.driver);
+ else if (evt.event.event_source == ES_SYNC)
+ evt.event.event_data.size=sizeof(evt.event.event_data.eu.sync);
+ else
+ evt.event.event_data.size=sizeof(evt.event.event_data.eu.async);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ gdth_store_event(ha, evt.event.event_source, evt.event.event_idx,
+ &evt.event.event_data);
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ } else if (evt.erase == 0xfe) {
+ gdth_clear_events();
+ } else if (evt.erase == 0) {
+ evt.handle = gdth_read_event(ha, evt.handle, &evt.event);
+ } else {
+ gdth_readapp_event(ha, evt.erase, &evt.event);
+ }
+ if (copy_to_user(arg, &evt, sizeof(gdth_ioctl_event)))
+ return -EFAULT;
+ return 0;
+}
+
+static int ioc_lockdrv(void __user *arg)
+{
+ gdth_ioctl_lockdrv ldrv;
+ unchar i, j;
+ ulong flags;
+ gdth_ha_str *ha;
+
+ if (copy_from_user(&ldrv, arg, sizeof(gdth_ioctl_lockdrv)) ||
+ ldrv.ionode >= gdth_ctr_count)
+ return -EFAULT;
+ ha = HADATA(gdth_ctr_tab[ldrv.ionode]);
+
+ for (i = 0; i < ldrv.drive_cnt && i < MAX_HDRIVES; ++i) {
+ j = ldrv.drives[i];
+ if (j >= MAX_HDRIVES || !ha->hdr[j].present)
+ continue;
+ if (ldrv.lock) {
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->hdr[j].lock = 1;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ gdth_wait_completion(ldrv.ionode, ha->bus_cnt, j);
+ gdth_stop_timeout(ldrv.ionode, ha->bus_cnt, j);
+ } else {
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->hdr[j].lock = 0;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ gdth_start_timeout(ldrv.ionode, ha->bus_cnt, j);
+ gdth_next(ldrv.ionode);
+ }
+ }
+ return 0;
+}
+
+static int ioc_resetdrv(void __user *arg, char *cmnd)
+{
+ gdth_ioctl_reset res;
+ gdth_cmd_str cmd;
+ int hanum;
+ gdth_ha_str *ha;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *srp;
+#else
+ Scsi_Cmnd *scp;
+#endif
+
+ if (copy_from_user(&res, arg, sizeof(gdth_ioctl_reset)) ||
+ res.ionode >= gdth_ctr_count || res.number >= MAX_HDRIVES)
+ return -EFAULT;
+ hanum = res.ionode;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (!ha->hdr[res.number].present)
+ return 0;
+ memset(&cmd, 0, sizeof(gdth_cmd_str));
+ cmd.Service = CACHESERVICE;
+ cmd.OpCode = GDT_CLUST_RESET;
+ if (ha->cache_feat & GDT_64BIT)
+ cmd.u.cache64.DeviceNo = res.number;
+ else
+ cmd.u.cache.DeviceNo = res.number;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ srp = scsi_allocate_request(ha->sdev, GFP_KERNEL);
+ if (!srp)
+ return -ENOMEM;
+ srp->sr_cmd_len = 12;
+ srp->sr_use_sg = 0;
+ gdth_do_req(srp, &cmd, cmnd, 30);
+ res.status = (ushort)srp->sr_command->SCp.Status;
+ scsi_release_request(srp);
+#else
+ scp = scsi_allocate_device(ha->sdev, 1, FALSE);
+ if (!scp)
+ return -ENOMEM;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+ gdth_do_cmd(scp, &cmd, cmnd, 30);
+ res.status = (ushort)scp->SCp.Status;
+ scsi_release_command(scp);
+#endif
+
+ if (copy_to_user(arg, &res, sizeof(gdth_ioctl_reset)))
+ return -EFAULT;
+ return 0;
+}
+
+static int ioc_general(void __user *arg, char *cmnd)
+{
+ gdth_ioctl_general gen;
+ char *buf = NULL;
+ ulong64 paddr;
+ int hanum;
+ gdth_ha_str *ha;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *srp;
+#else
+ Scsi_Cmnd *scp;
+#endif
+
+ if (copy_from_user(&gen, arg, sizeof(gdth_ioctl_general)) ||
+ gen.ionode >= gdth_ctr_count)
+ return -EFAULT;
+ hanum = gen.ionode;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (gen.data_len + gen.sense_len != 0) {
+ if (!(buf = gdth_ioctl_alloc(hanum, gen.data_len + gen.sense_len,
+ FALSE, &paddr)))
+ return -EFAULT;
+ if (copy_from_user(buf, arg + sizeof(gdth_ioctl_general),
+ gen.data_len + gen.sense_len)) {
+ gdth_ioctl_free(hanum, gen.data_len+gen.sense_len, buf, paddr);
+ return -EFAULT;
+ }
+
+ if (gen.command.OpCode == GDT_IOCTL) {
+ gen.command.u.ioctl.p_param = paddr;
+ } else if (gen.command.Service == CACHESERVICE) {
+ if (ha->cache_feat & GDT_64BIT) {
+ /* copy elements from 32-bit IOCTL structure */
+ gen.command.u.cache64.BlockCnt = gen.command.u.cache.BlockCnt;
+ gen.command.u.cache64.BlockNo = gen.command.u.cache.BlockNo;
+ gen.command.u.cache64.DeviceNo = gen.command.u.cache.DeviceNo;
+ /* addresses */
+ if (ha->cache_feat & SCATTER_GATHER) {
+ gen.command.u.cache64.DestAddr = (ulong64)-1;
+ gen.command.u.cache64.sg_canz = 1;
+ gen.command.u.cache64.sg_lst[0].sg_ptr = paddr;
+ gen.command.u.cache64.sg_lst[0].sg_len = gen.data_len;
+ gen.command.u.cache64.sg_lst[1].sg_len = 0;
+ } else {
+ gen.command.u.cache64.DestAddr = paddr;
+ gen.command.u.cache64.sg_canz = 0;
+ }
+ } else {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ gen.command.u.cache.DestAddr = 0xffffffff;
+ gen.command.u.cache.sg_canz = 1;
+ gen.command.u.cache.sg_lst[0].sg_ptr = (ulong32)paddr;
+ gen.command.u.cache.sg_lst[0].sg_len = gen.data_len;
+ gen.command.u.cache.sg_lst[1].sg_len = 0;
+ } else {
+ gen.command.u.cache.DestAddr = paddr;
+ gen.command.u.cache.sg_canz = 0;
+ }
+ }
+ } else if (gen.command.Service == SCSIRAWSERVICE) {
+ if (ha->raw_feat & GDT_64BIT) {
+ /* copy elements from 32-bit IOCTL structure */
+ char cmd[16];
+ gen.command.u.raw64.sense_len = gen.command.u.raw.sense_len;
+ gen.command.u.raw64.bus = gen.command.u.raw.bus;
+ gen.command.u.raw64.lun = gen.command.u.raw.lun;
+ gen.command.u.raw64.target = gen.command.u.raw.target;
+ memcpy(cmd, gen.command.u.raw.cmd, 16);
+ memcpy(gen.command.u.raw64.cmd, cmd, 16);
+ gen.command.u.raw64.clen = gen.command.u.raw.clen;
+ gen.command.u.raw64.sdlen = gen.command.u.raw.sdlen;
+ gen.command.u.raw64.direction = gen.command.u.raw.direction;
+ /* addresses */
+ if (ha->raw_feat & SCATTER_GATHER) {
+ gen.command.u.raw64.sdata = (ulong64)-1;
+ gen.command.u.raw64.sg_ranz = 1;
+ gen.command.u.raw64.sg_lst[0].sg_ptr = paddr;
+ gen.command.u.raw64.sg_lst[0].sg_len = gen.data_len;
+ gen.command.u.raw64.sg_lst[1].sg_len = 0;
+ } else {
+ gen.command.u.raw64.sdata = paddr;
+ gen.command.u.raw64.sg_ranz = 0;
+ }
+ gen.command.u.raw64.sense_data = paddr + gen.data_len;
+ } else {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ gen.command.u.raw.sdata = 0xffffffff;
+ gen.command.u.raw.sg_ranz = 1;
+ gen.command.u.raw.sg_lst[0].sg_ptr = (ulong32)paddr;
+ gen.command.u.raw.sg_lst[0].sg_len = gen.data_len;
+ gen.command.u.raw.sg_lst[1].sg_len = 0;
+ } else {
+ gen.command.u.raw.sdata = paddr;
+ gen.command.u.raw.sg_ranz = 0;
+ }
+ gen.command.u.raw.sense_data = (ulong32)paddr + gen.data_len;
+ }
+ } else {
+ gdth_ioctl_free(hanum, gen.data_len+gen.sense_len, buf, paddr);
+ return -EFAULT;
+ }
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ srp = scsi_allocate_request(ha->sdev, GFP_KERNEL);
+ if (!srp)
+ return -ENOMEM;
+ srp->sr_cmd_len = 12;
+ srp->sr_use_sg = 0;
+ gdth_do_req(srp, &gen.command, cmnd, gen.timeout);
+ gen.status = srp->sr_command->SCp.Status;
+ gen.info = srp->sr_command->SCp.Message;
+ scsi_release_request(srp);
+#else
+ scp = scsi_allocate_device(ha->sdev, 1, FALSE);
+ if (!scp)
+ return -ENOMEM;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+ gdth_do_cmd(scp, &gen.command, cmnd, gen.timeout);
+ gen.status = scp->SCp.Status;
+ gen.info = scp->SCp.Message;
+ scsi_release_command(scp);
+#endif
+
+ if (copy_to_user(arg + sizeof(gdth_ioctl_general), buf,
+ gen.data_len + gen.sense_len)) {
+ gdth_ioctl_free(hanum, gen.data_len+gen.sense_len, buf, paddr);
+ return -EFAULT;
+ }
+ if (copy_to_user(arg, &gen,
+ sizeof(gdth_ioctl_general) - sizeof(gdth_cmd_str))) {
+ gdth_ioctl_free(hanum, gen.data_len+gen.sense_len, buf, paddr);
+ return -EFAULT;
+ }
+ gdth_ioctl_free(hanum, gen.data_len+gen.sense_len, buf, paddr);
+ return 0;
+}
+
+static int ioc_hdrlist(void __user *arg, char *cmnd)
+{
+ gdth_ioctl_rescan *rsc;
+ gdth_cmd_str *cmd;
+ gdth_ha_str *ha;
+ unchar i;
+ int hanum, rc = -ENOMEM;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *srp;
+#else
+ Scsi_Cmnd *scp;
+#endif
+
+ rsc = kmalloc(sizeof(*rsc), GFP_KERNEL);
+ cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!rsc || !cmd)
+ goto free_fail;
+
+ if (copy_from_user(rsc, arg, sizeof(gdth_ioctl_rescan)) ||
+ rsc->ionode >= gdth_ctr_count) {
+ rc = -EFAULT;
+ goto free_fail;
+ }
+ hanum = rsc->ionode;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmd, 0, sizeof(gdth_cmd_str));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ srp = scsi_allocate_request(ha->sdev, GFP_KERNEL);
+ if (!srp)
+ goto free_fail;
+ srp->sr_cmd_len = 12;
+ srp->sr_use_sg = 0;
+#else
+ scp = scsi_allocate_device(ha->sdev, 1, FALSE);
+ if (!scp)
+ goto free_fail;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+#endif
+
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (!ha->hdr[i].present) {
+ rsc->hdr_list[i].bus = 0xff;
+ continue;
+ }
+ rsc->hdr_list[i].bus = ha->virt_bus;
+ rsc->hdr_list[i].target = i;
+ rsc->hdr_list[i].lun = 0;
+ rsc->hdr_list[i].cluster_type = ha->hdr[i].cluster_type;
+ if (ha->hdr[i].cluster_type & CLUSTER_DRIVE) {
+ cmd->Service = CACHESERVICE;
+ cmd->OpCode = GDT_CLUST_INFO;
+ if (ha->cache_feat & GDT_64BIT)
+ cmd->u.cache64.DeviceNo = i;
+ else
+ cmd->u.cache.DeviceNo = i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, cmd, cmnd, 30);
+ if (srp->sr_command->SCp.Status == S_OK)
+ rsc->hdr_list[i].cluster_type = srp->sr_command->SCp.Message;
+#else
+ gdth_do_cmd(scp, cmd, cmnd, 30);
+ if (scp->SCp.Status == S_OK)
+ rsc->hdr_list[i].cluster_type = scp->SCp.Message;
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scsi_release_request(srp);
+#else
+ scsi_release_command(scp);
+#endif
+
+ if (copy_to_user(arg, rsc, sizeof(gdth_ioctl_rescan)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+free_fail:
+ kfree(rsc);
+ kfree(cmd);
+ return rc;
+}
+
+static int ioc_rescan(void __user *arg, char *cmnd)
+{
+ gdth_ioctl_rescan *rsc;
+ gdth_cmd_str *cmd;
+ ushort i, status, hdr_cnt;
+ ulong32 info;
+ int hanum, cyls, hds, secs;
+ int rc = -ENOMEM;
+ ulong flags;
+ gdth_ha_str *ha;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *srp;
+#else
+ Scsi_Cmnd *scp;
+#endif
+
+ rsc = kmalloc(sizeof(*rsc), GFP_KERNEL);
+ cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd || !rsc)
+ goto free_fail;
+
+ if (copy_from_user(rsc, arg, sizeof(gdth_ioctl_rescan)) ||
+ rsc->ionode >= gdth_ctr_count) {
+ rc = -EFAULT;
+ goto free_fail;
+ }
+ hanum = rsc->ionode;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmd, 0, sizeof(gdth_cmd_str));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ srp = scsi_allocate_request(ha->sdev, GFP_KERNEL);
+ if (!srp)
+ goto free_fail;
+ srp->sr_cmd_len = 12;
+ srp->sr_use_sg = 0;
+#else
+ scp = scsi_allocate_device(ha->sdev, 1, FALSE);
+ if (!scp)
+ goto free_fail;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+#endif
+
+ if (rsc->flag == 0) {
+ /* old method: re-init. cache service */
+ cmd->Service = CACHESERVICE;
+ if (ha->cache_feat & GDT_64BIT) {
+ cmd->OpCode = GDT_X_INIT_HOST;
+ cmd->u.cache64.DeviceNo = LINUX_OS;
+ } else {
+ cmd->OpCode = GDT_INIT;
+ cmd->u.cache.DeviceNo = LINUX_OS;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, cmd, cmnd, 30);
+ status = (ushort)srp->sr_command->SCp.Status;
+ info = (ulong32)srp->sr_command->SCp.Message;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ gdth_do_cmd(scp, cmd, cmnd, 30);
+ status = (ushort)scp->SCp.Status;
+ info = (ulong32)scp->SCp.Message;
+#else
+ gdth_do_cmd(&scp, cmd, cmnd, 30);
+ status = (ushort)scp.SCp.Status;
+ info = (ulong32)scp.SCp.Message;
+#endif
+ i = 0;
+ hdr_cnt = (status == S_OK ? (ushort)info : 0);
+ } else {
+ i = rsc->hdr_no;
+ hdr_cnt = i + 1;
+ }
+
+ for (; i < hdr_cnt && i < MAX_HDRIVES; ++i) {
+ cmd->Service = CACHESERVICE;
+ cmd->OpCode = GDT_INFO;
+ if (ha->cache_feat & GDT_64BIT)
+ cmd->u.cache64.DeviceNo = i;
+ else
+ cmd->u.cache.DeviceNo = i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, cmd, cmnd, 30);
+ status = (ushort)srp->sr_command->SCp.Status;
+ info = (ulong32)srp->sr_command->SCp.Message;
+#else
+ gdth_do_cmd(scp, cmd, cmnd, 30);
+ status = (ushort)scp->SCp.Status;
+ info = (ulong32)scp->SCp.Message;
+#endif
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ rsc->hdr_list[i].bus = ha->virt_bus;
+ rsc->hdr_list[i].target = i;
+ rsc->hdr_list[i].lun = 0;
+ if (status != S_OK) {
+ ha->hdr[i].present = FALSE;
+ } else {
+ ha->hdr[i].present = TRUE;
+ ha->hdr[i].size = info;
+ /* evaluate mapping */
+ ha->hdr[i].size &= ~SECS32;
+ gdth_eval_mapping(ha->hdr[i].size,&cyls,&hds,&secs);
+ ha->hdr[i].heads = hds;
+ ha->hdr[i].secs = secs;
+ /* round size */
+ ha->hdr[i].size = cyls * hds * secs;
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ if (status != S_OK)
+ continue;
+
+ /* extended info, if GDT_64BIT, for drives > 2 TB */
+ /* but we need ha->info2, not yet stored in scp->SCp */
+
+ /* devtype, cluster info, R/W attribs */
+ cmd->Service = CACHESERVICE;
+ cmd->OpCode = GDT_DEVTYPE;
+ if (ha->cache_feat & GDT_64BIT)
+ cmd->u.cache64.DeviceNo = i;
+ else
+ cmd->u.cache.DeviceNo = i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, cmd, cmnd, 30);
+ status = (ushort)srp->sr_command->SCp.Status;
+ info = (ulong32)srp->sr_command->SCp.Message;
+#else
+ gdth_do_cmd(scp, cmd, cmnd, 30);
+ status = (ushort)scp->SCp.Status;
+ info = (ulong32)scp->SCp.Message;
+#endif
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->hdr[i].devtype = (status == S_OK ? (ushort)info : 0);
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+
+ cmd->Service = CACHESERVICE;
+ cmd->OpCode = GDT_CLUST_INFO;
+ if (ha->cache_feat & GDT_64BIT)
+ cmd->u.cache64.DeviceNo = i;
+ else
+ cmd->u.cache.DeviceNo = i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, cmd, cmnd, 30);
+ status = (ushort)srp->sr_command->SCp.Status;
+ info = (ulong32)srp->sr_command->SCp.Message;
+#else
+ gdth_do_cmd(scp, cmd, cmnd, 30);
+ status = (ushort)scp->SCp.Status;
+ info = (ulong32)scp->SCp.Message;
+#endif
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->hdr[i].cluster_type =
+ ((status == S_OK && !shared_access) ? (ushort)info : 0);
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ rsc->hdr_list[i].cluster_type = ha->hdr[i].cluster_type;
+
+ cmd->Service = CACHESERVICE;
+ cmd->OpCode = GDT_RW_ATTRIBS;
+ if (ha->cache_feat & GDT_64BIT)
+ cmd->u.cache64.DeviceNo = i;
+ else
+ cmd->u.cache.DeviceNo = i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, cmd, cmnd, 30);
+ status = (ushort)srp->sr_command->SCp.Status;
+ info = (ulong32)srp->sr_command->SCp.Message;
+#else
+ gdth_do_cmd(scp, cmd, cmnd, 30);
+ status = (ushort)scp->SCp.Status;
+ info = (ulong32)scp->SCp.Message;
+#endif
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->hdr[i].rw_attribs = (status == S_OK ? (ushort)info : 0);
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scsi_release_request(srp);
+#else
+ scsi_release_command(scp);
+#endif
+
+ if (copy_to_user(arg, rsc, sizeof(gdth_ioctl_rescan)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+free_fail:
+ kfree(rsc);
+ kfree(cmd);
+ return rc;
+}
+
+static int gdth_ioctl(struct inode *inode, struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ gdth_ha_str *ha;
+ Scsi_Cmnd *scp;
+ ulong flags;
+ char cmnd[MAX_COMMAND_SIZE];
+ void __user *argp = (void __user *)arg;
+
+ memset(cmnd, 0xff, 12);
+
+ TRACE(("gdth_ioctl() cmd 0x%x\n", cmd));
+
+ switch (cmd) {
+ case GDTIOCTL_CTRCNT:
+ {
+ int cnt = gdth_ctr_count;
+ if (put_user(cnt, (int __user *)argp))
+ return -EFAULT;
+ break;
+ }
+
+ case GDTIOCTL_DRVERS:
+ {
+ int ver = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
+ if (put_user(ver, (int __user *)argp))
+ return -EFAULT;
+ break;
+ }
+
+ case GDTIOCTL_OSVERS:
+ {
+ gdth_ioctl_osvers osv;
+
+ osv.version = (unchar)(LINUX_VERSION_CODE >> 16);
+ osv.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
+ osv.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+ if (copy_to_user(argp, &osv, sizeof(gdth_ioctl_osvers)))
+ return -EFAULT;
+ break;
+ }
+
+ case GDTIOCTL_CTRTYPE:
+ {
+ gdth_ioctl_ctrtype ctrt;
+
+ if (copy_from_user(&ctrt, argp, sizeof(gdth_ioctl_ctrtype)) ||
+ ctrt.ionode >= gdth_ctr_count)
+ return -EFAULT;
+ ha = HADATA(gdth_ctr_tab[ctrt.ionode]);
+ if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
+ ctrt.type = (unchar)((ha->stype>>20) - 0x10);
+ } else {
+ if (ha->type != GDT_PCIMPR) {
+ ctrt.type = (unchar)((ha->stype<<4) + 6);
+ } else {
+ ctrt.type =
+ (ha->oem_id == OEM_ID_INTEL ? 0xfd : 0xfe);
+ if (ha->stype >= 0x300)
+ ctrt.ext_type = 0x6000 | ha->subdevice_id;
+ else
+ ctrt.ext_type = 0x6000 | ha->stype;
+ }
+ ctrt.device_id = ha->stype;
+ ctrt.sub_device_id = ha->subdevice_id;
+ }
+ ctrt.info = ha->brd_phys;
+ ctrt.oem_id = ha->oem_id;
+ if (copy_to_user(argp, &ctrt, sizeof(gdth_ioctl_ctrtype)))
+ return -EFAULT;
+ break;
+ }
+
+ case GDTIOCTL_GENERAL:
+ return ioc_general(argp, cmnd);
+
+ case GDTIOCTL_EVENT:
+ return ioc_event(argp);
+
+ case GDTIOCTL_LOCKDRV:
+ return ioc_lockdrv(argp);
+
+ case GDTIOCTL_LOCKCHN:
+ {
+ gdth_ioctl_lockchn lchn;
+ unchar i, j;
+
+ if (copy_from_user(&lchn, argp, sizeof(gdth_ioctl_lockchn)) ||
+ lchn.ionode >= gdth_ctr_count)
+ return -EFAULT;
+ ha = HADATA(gdth_ctr_tab[lchn.ionode]);
+
+ i = lchn.channel;
+ if (i < ha->bus_cnt) {
+ if (lchn.lock) {
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->raw[i].lock = 1;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ for (j = 0; j < ha->tid_cnt; ++j) {
+ gdth_wait_completion(lchn.ionode, i, j);
+ gdth_stop_timeout(lchn.ionode, i, j);
+ }
+ } else {
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ ha->raw[i].lock = 0;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ for (j = 0; j < ha->tid_cnt; ++j) {
+ gdth_start_timeout(lchn.ionode, i, j);
+ gdth_next(lchn.ionode);
+ }
+ }
+ }
+ break;
+ }
+
+ case GDTIOCTL_RESCAN:
+ return ioc_rescan(argp, cmnd);
+
+ case GDTIOCTL_HDRLIST:
+ return ioc_hdrlist(argp, cmnd);
+
+ case GDTIOCTL_RESET_BUS:
+ {
+ gdth_ioctl_reset res;
+ int hanum, rval;
+
+ if (copy_from_user(&res, argp, sizeof(gdth_ioctl_reset)) ||
+ res.ionode >= gdth_ctr_count)
+ return -EFAULT;
+ hanum = res.ionode;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ /* Because we need a Scsi_Cmnd struct., we make a scsi_allocate device also for kernels >=2.6.x */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scp = scsi_get_command(ha->sdev, GFP_KERNEL);
+ if (!scp)
+ return -ENOMEM;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+ scp->device->channel = virt_ctr ? 0 : res.number;
+ rval = gdth_eh_bus_reset(scp);
+ res.status = (rval == SUCCESS ? S_OK : S_GENERR);
+ scsi_put_command(scp);
+#else
+ scp = scsi_allocate_device(ha->sdev, 1, FALSE);
+ if (!scp)
+ return -ENOMEM;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+ scp->channel = virt_ctr ? 0 : res.number;
+ rval = gdth_eh_bus_reset(scp);
+ res.status = (rval == SUCCESS ? S_OK : S_GENERR);
+ scsi_release_command(scp);
+#endif
+ if (copy_to_user(argp, &res, sizeof(gdth_ioctl_reset)))
+ return -EFAULT;
+ break;
+ }
+
+ case GDTIOCTL_RESET_DRV:
+ return ioc_resetdrv(argp, cmnd);
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+/* flush routine */
+static void gdth_flush(int hanum)
+{
+ int i;
+ gdth_ha_str *ha;
+ gdth_cmd_str gdtcmd;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *srp;
+#else
+ Scsi_Cmnd *scp;
+#endif
+ Scsi_Device *sdev;
+ char cmnd[MAX_COMMAND_SIZE];
+ memset(cmnd, 0xff, MAX_COMMAND_SIZE);
+
+ TRACE2(("gdth_flush() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+ srp = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!srp)
+ return;
+ srp->sr_cmd_len = 12;
+ srp->sr_use_sg = 0;
+#else
+ sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ if (!scp)
+ return;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+#endif
+
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (ha->hdr[i].present) {
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ if (ha->cache_feat & GDT_64BIT) {
+ gdtcmd.u.cache64.DeviceNo = i;
+ gdtcmd.u.cache64.BlockNo = 1;
+ gdtcmd.u.cache64.sg_canz = 0;
+ } else {
+ gdtcmd.u.cache.DeviceNo = i;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ }
+ TRACE2(("gdth_flush(): flush ha %d drive %d\n", hanum, i));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(srp, &gdtcmd, cmnd, 30);
+#else
+ gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+#endif
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scsi_release_request(srp);
+ scsi_free_host_dev(sdev);
+#else
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
+#endif
+}
+
+/* shutdown routine */
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+{
+ int hanum;
+#ifndef __alpha__
+ gdth_cmd_str gdtcmd;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *srp;
+ Scsi_Device *sdev;
+#else
+ Scsi_Cmnd *scp;
+ Scsi_Device *sdev;
+#endif
+ char cmnd[MAX_COMMAND_SIZE];
+#endif
+
+ TRACE2(("gdth_halt() event %d\n",(int)event));
+ if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+
+ printk("GDT-HA: Flushing all host drives .. ");
+ for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
+ gdth_flush(hanum);
+
+#ifndef __alpha__
+ /* controller reset */
+ memset(cmnd, 0xff, MAX_COMMAND_SIZE);
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_RESET;
+ TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+ srp = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!srp) {
+ unregister_reboot_notifier(&gdth_notifier);
+ return NOTIFY_OK;
+ }
+ srp->sr_cmd_len = 12;
+ srp->sr_use_sg = 0;
+ gdth_do_req(srp, &gdtcmd, cmnd, 10);
+ scsi_release_request(srp);
+ scsi_free_host_dev(sdev);
+#else
+ sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ if (!scp) {
+ unregister_reboot_notifier(&gdth_notifier);
+ return NOTIFY_OK;
+ }
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+ gdth_do_cmd(scp, &gdtcmd, cmnd, 10);
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
+#endif
+#endif
+ }
+ printk("Done.\n");
+
+#ifdef GDTH_STATISTICS
+ del_timer(&gdth_timer);
+#endif
+ unregister_reboot_notifier(&gdth_notifier);
+ return NOTIFY_OK;
+}
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "gdth",
+ .proc_info = gdth_proc_info,
+ .name = "GDT SCSI Disk Array Controller",
+ .detect = gdth_detect,
+ .release = gdth_release,
+ .info = gdth_info,
+ .queuecommand = gdth_queuecommand,
+ .eh_abort_handler = gdth_eh_abort,
+ .eh_device_reset_handler = gdth_eh_device_reset,
+ .eh_bus_reset_handler = gdth_eh_bus_reset,
+ .eh_host_reset_handler = gdth_eh_host_reset,
+ .bios_param = gdth_bios_param,
+ .can_queue = GDTH_MAXCMDS,
+ .this_id = -1,
+ .sg_tablesize = GDTH_MAXSG,
+ .cmd_per_lun = GDTH_MAXC_P_L,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ .use_new_eh_code = 1,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
+ .highmem_io = 1,
+#endif
+#endif
+};
+
+#include "scsi_module.c"
+#ifndef MODULE
+__setup("gdth=", option_setup);
+#endif
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
new file mode 100644
index 000000000000..bf269f05ea8e
--- /dev/null
+++ b/drivers/scsi/gdth.h
@@ -0,0 +1,1079 @@
+#ifndef _GDTH_H
+#define _GDTH_H
+
+/*
+ * Header file for the GDT Disk Array/Storage RAID controllers driver for Linux
+ *
+ * gdth.h Copyright (C) 1995-03 ICP vortex, Achim Leubner
+ * See gdth.c for further informations and
+ * below for supported controller types
+ *
+ * <achim_leubner@adaptec.com>
+ *
+ * $Id: gdth.h,v 1.57 2004/03/31 11:52:09 achim Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* defines, macros */
+
+/* driver version */
+#define GDTH_VERSION_STR "3.04"
+#define GDTH_VERSION 3
+#define GDTH_SUBVERSION 4
+
+/* protocol version */
+#define PROTOCOL_VERSION 1
+
+/* OEM IDs */
+#define OEM_ID_ICP 0x941c
+#define OEM_ID_INTEL 0x8000
+
+/* controller classes */
+#define GDT_ISA 0x01 /* ISA controller */
+#define GDT_EISA 0x02 /* EISA controller */
+#define GDT_PCI 0x03 /* PCI controller */
+#define GDT_PCINEW 0x04 /* new PCI controller */
+#define GDT_PCIMPR 0x05 /* PCI MPR controller */
+/* GDT_EISA, controller subtypes EISA */
+#define GDT3_ID 0x0130941c /* GDT3000/3020 */
+#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */
+#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */
+/* GDT_ISA */
+#define GDT2_ID 0x0120941c /* GDT2000/2020 */
+
+/* vendor ID, device IDs (PCI) */
+/* these defines should already exist in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_VORTEX
+#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */
+#endif
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
+/* GDT_PCI */
+#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */
+#define PCI_DEVICE_ID_VORTEX_GDT6000B 1 /* GDT6000B/6010 */
+/* GDT_PCINEW */
+#define PCI_DEVICE_ID_VORTEX_GDT6x10 2 /* GDT6110/6510 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x20 3 /* GDT6120/6520 */
+#define PCI_DEVICE_ID_VORTEX_GDT6530 4 /* GDT6530 */
+#define PCI_DEVICE_ID_VORTEX_GDT6550 5 /* GDT6550 */
+/* GDT_PCINEW, wide/ultra SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17 6 /* GDT6117/6517 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27 7 /* GDT6127/6527 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537 8 /* GDT6537 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557 9 /* GDT6557/6557-ECC */
+/* GDT_PCINEW, wide SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x15 10 /* GDT6115/6515 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x25 11 /* GDT6125/6525 */
+#define PCI_DEVICE_ID_VORTEX_GDT6535 12 /* GDT6535 */
+#define PCI_DEVICE_ID_VORTEX_GDT6555 13 /* GDT6555/6555-ECC */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP
+/* GDT_MPR, RP series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x100 /* GDT6117RP/GDT6517RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x101 /* GDT6127RP/GDT6527RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x102 /* GDT6537RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x103 /* GDT6557RP */
+/* GDT_MPR, RP series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x104 /* GDT6111RP/GDT6511RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x105 /* GDT6121RP/GDT6521RP */
+#endif
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RD
+/* GDT_MPR, RD series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RD 0x110 /* GDT6117RD/GDT6517RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RD 0x111 /* GDT6127RD/GDT6527RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RD 0x112 /* GDT6537RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RD 0x113 /* GDT6557RD */
+/* GDT_MPR, RD series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RD 0x114 /* GDT6111RD/GDT6511RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RD 0x115 /* GDT6121RD/GDT6521RD */
+/* GDT_MPR, RD series, wide/ultra2 SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x18RD 0x118 /* GDT6118RD/GDT6518RD/
+ GDT6618RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6x28RD 0x119 /* GDT6128RD/GDT6528RD/
+ GDT6628RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6x38RD 0x11A /* GDT6538RD/GDT6638RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6x58RD 0x11B /* GDT6558RD/GDT6658RD */
+/* GDT_MPR, RN series (64-bit PCI), wide/ultra2 SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT7x18RN 0x168 /* GDT7118RN/GDT7518RN/
+ GDT7618RN */
+#define PCI_DEVICE_ID_VORTEX_GDT7x28RN 0x169 /* GDT7128RN/GDT7528RN/
+ GDT7628RN */
+#define PCI_DEVICE_ID_VORTEX_GDT7x38RN 0x16A /* GDT7538RN/GDT7638RN */
+#define PCI_DEVICE_ID_VORTEX_GDT7x58RN 0x16B /* GDT7558RN/GDT7658RN */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x19RD
+/* GDT_MPR, RD series, Fibre Channel */
+#define PCI_DEVICE_ID_VORTEX_GDT6x19RD 0x210 /* GDT6519RD/GDT6619RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6x29RD 0x211 /* GDT6529RD/GDT6629RD */
+/* GDT_MPR, RN series (64-bit PCI), Fibre Channel */
+#define PCI_DEVICE_ID_VORTEX_GDT7x19RN 0x260 /* GDT7519RN/GDT7619RN */
+#define PCI_DEVICE_ID_VORTEX_GDT7x29RN 0x261 /* GDT7529RN/GDT7629RN */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDTMAXRP
+/* GDT_MPR, last device ID */
+#define PCI_DEVICE_ID_VORTEX_GDTMAXRP 0x2ff
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDTNEWRX
+/* new GDT Rx Controller */
+#define PCI_DEVICE_ID_VORTEX_GDTNEWRX 0x300
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDTNEWRX2
+/* new(2) GDT Rx Controller */
+#define PCI_DEVICE_ID_VORTEX_GDTNEWRX2 0x301
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_SRC
+/* Intel Storage RAID Controller */
+#define PCI_DEVICE_ID_INTEL_SRC 0x600
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_SRC_XSCALE
+/* Intel Storage RAID Controller */
+#define PCI_DEVICE_ID_INTEL_SRC_XSCALE 0x601
+#endif
+
+/* limits */
+#define GDTH_SCRATCH PAGE_SIZE /* 4KB scratch buffer */
+#define GDTH_MAXCMDS 120
+#define GDTH_MAXC_P_L 16 /* max. cmds per lun */
+#define GDTH_MAX_RAW 2 /* max. cmds per raw device */
+#define MAXOFFSETS 128
+#define MAXHA 16
+#define MAXID 127
+#define MAXLUN 8
+#define MAXBUS 6
+#define MAX_EVENTS 100 /* event buffer count */
+#define MAX_RES_ARGS 40 /* device reservation,
+ must be a multiple of 4 */
+#define MAXCYLS 1024
+#define HEADS 64
+#define SECS 32 /* mapping 64*32 */
+#define MEDHEADS 127
+#define MEDSECS 63 /* mapping 127*63 */
+#define BIGHEADS 255
+#define BIGSECS 63 /* mapping 255*63 */
+
+/* special command ptr. */
+#define UNUSED_CMND ((Scsi_Cmnd *)-1)
+#define INTERNAL_CMND ((Scsi_Cmnd *)-2)
+#define SCREEN_CMND ((Scsi_Cmnd *)-3)
+#define SPECIAL_SCP(p) (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND)
+
+/* controller services */
+#define SCSIRAWSERVICE 3
+#define CACHESERVICE 9
+#define SCREENSERVICE 11
+
+/* screenservice defines */
+#define MSG_INV_HANDLE -1 /* special message handle */
+#define MSGLEN 16 /* size of message text */
+#define MSG_SIZE 34 /* size of message structure */
+#define MSG_REQUEST 0 /* async. event: message */
+
+/* cacheservice defines */
+#define SECTOR_SIZE 0x200 /* always 512 bytes per sec. */
+
+/* DPMEM constants */
+#define DPMEM_MAGIC 0xC0FFEE11
+#define IC_HEADER_BYTES 48
+#define IC_QUEUE_BYTES 4
+#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+
+/* cluster_type constants */
+#define CLUSTER_DRIVE 1
+#define CLUSTER_MOUNTED 2
+#define CLUSTER_RESERVED 4
+#define CLUSTER_RESERVE_STATE (CLUSTER_DRIVE|CLUSTER_MOUNTED|CLUSTER_RESERVED)
+
+/* commands for all services, cache service */
+#define GDT_INIT 0 /* service initialization */
+#define GDT_READ 1 /* read command */
+#define GDT_WRITE 2 /* write command */
+#define GDT_INFO 3 /* information about devices */
+#define GDT_FLUSH 4 /* flush dirty cache buffers */
+#define GDT_IOCTL 5 /* ioctl command */
+#define GDT_DEVTYPE 9 /* additional information */
+#define GDT_MOUNT 10 /* mount cache device */
+#define GDT_UNMOUNT 11 /* unmount cache device */
+#define GDT_SET_FEAT 12 /* set feat. (scatter/gather) */
+#define GDT_GET_FEAT 13 /* get features */
+#define GDT_WRITE_THR 16 /* write through */
+#define GDT_READ_THR 17 /* read through */
+#define GDT_EXT_INFO 18 /* extended info */
+#define GDT_RESET 19 /* controller reset */
+#define GDT_RESERVE_DRV 20 /* reserve host drive */
+#define GDT_RELEASE_DRV 21 /* release host drive */
+#define GDT_CLUST_INFO 22 /* cluster info */
+#define GDT_RW_ATTRIBS 23 /* R/W attribs (write thru,..)*/
+#define GDT_CLUST_RESET 24 /* releases the cluster drives*/
+#define GDT_FREEZE_IO 25 /* freezes all IOs */
+#define GDT_UNFREEZE_IO 26 /* unfreezes all IOs */
+#define GDT_X_INIT_HOST 29 /* ext. init: 64 bit support */
+#define GDT_X_INFO 30 /* ext. info for drives>2TB */
+
+/* raw service commands */
+#define GDT_RESERVE 14 /* reserve dev. to raw serv. */
+#define GDT_RELEASE 15 /* release device */
+#define GDT_RESERVE_ALL 16 /* reserve all devices */
+#define GDT_RELEASE_ALL 17 /* release all devices */
+#define GDT_RESET_BUS 18 /* reset bus */
+#define GDT_SCAN_START 19 /* start device scan */
+#define GDT_SCAN_END 20 /* stop device scan */
+#define GDT_X_INIT_RAW 21 /* ext. init: 64 bit support */
+
+/* screen service commands */
+#define GDT_REALTIME 3 /* realtime clock to screens. */
+#define GDT_X_INIT_SCR 4 /* ext. init: 64 bit support */
+
+/* IOCTL command defines */
+#define SCSI_DR_INFO 0x00 /* SCSI drive info */
+#define SCSI_CHAN_CNT 0x05 /* SCSI channel count */
+#define SCSI_DR_LIST 0x06 /* SCSI drive list */
+#define SCSI_DEF_CNT 0x15 /* grown/primary defects */
+#define DSK_STATISTICS 0x4b /* SCSI disk statistics */
+#define IOCHAN_DESC 0x5d /* description of IO channel */
+#define IOCHAN_RAW_DESC 0x5e /* description of raw IO chn. */
+#define L_CTRL_PATTERN 0x20000000L /* SCSI IOCTL mask */
+#define ARRAY_INFO 0x12 /* array drive info */
+#define ARRAY_DRV_LIST 0x0f /* array drive list */
+#define ARRAY_DRV_LIST2 0x34 /* array drive list (new) */
+#define LA_CTRL_PATTERN 0x10000000L /* array IOCTL mask */
+#define CACHE_DRV_CNT 0x01 /* cache drive count */
+#define CACHE_DRV_LIST 0x02 /* cache drive list */
+#define CACHE_INFO 0x04 /* cache info */
+#define CACHE_CONFIG 0x05 /* cache configuration */
+#define CACHE_DRV_INFO 0x07 /* cache drive info */
+#define BOARD_FEATURES 0x15 /* controller features */
+#define BOARD_INFO 0x28 /* controller info */
+#define SET_PERF_MODES 0x82 /* set mode (coalescing,..) */
+#define GET_PERF_MODES 0x83 /* get mode */
+#define CACHE_READ_OEM_STRING_RECORD 0x84 /* read OEM string record */
+#define HOST_GET 0x10001L /* get host drive list */
+#define IO_CHANNEL 0x00020000L /* default IO channel */
+#define INVALID_CHANNEL 0x0000ffffL /* invalid channel */
+
+/* service errors */
+#define S_OK 1 /* no error */
+#define S_GENERR 6 /* general error */
+#define S_BSY 7 /* controller busy */
+#define S_CACHE_UNKNOWN 12 /* cache serv.: drive unknown */
+#define S_RAW_SCSI 12 /* raw serv.: target error */
+#define S_RAW_ILL 0xff /* raw serv.: illegal */
+#define S_NOFUNC -2 /* unknown function */
+#define S_CACHE_RESERV -24 /* cache: reserv. conflict */
+
+/* timeout values */
+#define INIT_RETRIES 100000 /* 100000 * 1ms = 100s */
+#define INIT_TIMEOUT 100000 /* 100000 * 1ms = 100s */
+#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */
+
+/* priorities */
+#define DEFAULT_PRI 0x20
+#define IOCTL_PRI 0x10
+#define HIGH_PRI 0x08
+
+/* data directions */
+#define GDTH_DATA_IN 0x01000000L /* data from target */
+#define GDTH_DATA_OUT 0x00000000L /* data to target */
+
+/* BMIC registers (EISA controllers) */
+#define ID0REG 0x0c80 /* board ID */
+#define EINTENABREG 0x0c89 /* interrupt enable */
+#define SEMA0REG 0x0c8a /* command semaphore */
+#define SEMA1REG 0x0c8b /* status semaphore */
+#define LDOORREG 0x0c8d /* local doorbell */
+#define EDENABREG 0x0c8e /* EISA system doorbell enab. */
+#define EDOORREG 0x0c8f /* EISA system doorbell */
+#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */
+#define EISAREG 0x0cc0 /* EISA configuration */
+
+/* DMA memory mappings */
+#define GDTH_MAP_NONE 0
+#define GDTH_MAP_SINGLE 1
+#define GDTH_MAP_SG 2
+#define GDTH_MAP_IOCTL 3
+
+/* other defines */
+#define LINUX_OS 8 /* used for cache optim. */
+#define SCATTER_GATHER 1 /* s/g feature */
+#define SECS32 0x1f /* round capacity */
+#define BIOS_ID_OFFS 0x10 /* offset contr-ID in ISABIOS */
+#define LOCALBOARD 0 /* board node always 0 */
+#define ASYNCINDEX 0 /* cmd index async. event */
+#define SPEZINDEX 1 /* cmd index unknown service */
+#define COALINDEX (GDTH_MAXCMDS + 2)
+
+/* features */
+#define SCATTER_GATHER 1 /* s/g feature */
+#define GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */
+#define GDT_64BIT 0x200 /* 64bit / drv>2TB support */
+
+#include "gdth_ioctl.h"
+
+/* screenservice message */
+typedef struct {
+ ulong32 msg_handle; /* message handle */
+ ulong32 msg_len; /* size of message */
+ ulong32 msg_alen; /* answer length */
+ unchar msg_answer; /* answer flag */
+ unchar msg_ext; /* more messages */
+ unchar msg_reserved[2];
+ char msg_text[MSGLEN+2]; /* the message text */
+} PACKED gdth_msg_str;
+
+
+/* IOCTL data structures */
+
+/* Status coalescing buffer for returning multiple requests per interrupt */
+typedef struct {
+ ulong32 status;
+ ulong32 ext_status;
+ ulong32 info0;
+ ulong32 info1;
+} PACKED gdth_coal_status;
+
+/* performance mode data structure */
+typedef struct {
+ ulong32 version; /* The version of this IOCTL structure. */
+ ulong32 st_mode; /* 0=dis., 1=st_buf_addr1 valid, 2=both */
+ ulong32 st_buff_addr1; /* physical address of status buffer 1 */
+ ulong32 st_buff_u_addr1; /* reserved for 64 bit addressing */
+ ulong32 st_buff_indx1; /* reserved command idx. for this buffer */
+ ulong32 st_buff_addr2; /* physical address of status buffer 1 */
+ ulong32 st_buff_u_addr2; /* reserved for 64 bit addressing */
+ ulong32 st_buff_indx2; /* reserved command idx. for this buffer */
+ ulong32 st_buff_size; /* size of each buffer in bytes */
+ ulong32 cmd_mode; /* 0 = mode disabled, 1 = cmd_buff_addr1 */
+ ulong32 cmd_buff_addr1; /* physical address of cmd buffer 1 */
+ ulong32 cmd_buff_u_addr1; /* reserved for 64 bit addressing */
+ ulong32 cmd_buff_indx1; /* cmd buf addr1 unique identifier */
+ ulong32 cmd_buff_addr2; /* physical address of cmd buffer 1 */
+ ulong32 cmd_buff_u_addr2; /* reserved for 64 bit addressing */
+ ulong32 cmd_buff_indx2; /* cmd buf addr1 unique identifier */
+ ulong32 cmd_buff_size; /* size of each cmd bufer in bytes */
+ ulong32 reserved1;
+ ulong32 reserved2;
+} PACKED gdth_perf_modes;
+
+/* SCSI drive info */
+typedef struct {
+ unchar vendor[8]; /* vendor string */
+ unchar product[16]; /* product string */
+ unchar revision[4]; /* revision */
+ ulong32 sy_rate; /* current rate for sync. tr. */
+ ulong32 sy_max_rate; /* max. rate for sync. tr. */
+ ulong32 no_ldrive; /* belongs to this log. drv.*/
+ ulong32 blkcnt; /* number of blocks */
+ ushort blksize; /* size of block in bytes */
+ unchar available; /* flag: access is available */
+ unchar init; /* medium is initialized */
+ unchar devtype; /* SCSI devicetype */
+ unchar rm_medium; /* medium is removable */
+ unchar wp_medium; /* medium is write protected */
+ unchar ansi; /* SCSI I/II or III? */
+ unchar protocol; /* same as ansi */
+ unchar sync; /* flag: sync. transfer enab. */
+ unchar disc; /* flag: disconnect enabled */
+ unchar queueing; /* flag: command queing enab. */
+ unchar cached; /* flag: caching enabled */
+ unchar target_id; /* target ID of device */
+ unchar lun; /* LUN id of device */
+ unchar orphan; /* flag: drive fragment */
+ ulong32 last_error; /* sense key or drive state */
+ ulong32 last_result; /* result of last command */
+ ulong32 check_errors; /* err. in last surface check */
+ unchar percent; /* progress for surface check */
+ unchar last_check; /* IOCTRL operation */
+ unchar res[2];
+ ulong32 flags; /* from 1.19/2.19: raw reserv.*/
+ unchar multi_bus; /* multi bus dev? (fibre ch.) */
+ unchar mb_status; /* status: available? */
+ unchar res2[2];
+ unchar mb_alt_status; /* status on second bus */
+ unchar mb_alt_bid; /* number of second bus */
+ unchar mb_alt_tid; /* target id on second bus */
+ unchar res3;
+ unchar fc_flag; /* from 1.22/2.22: info valid?*/
+ unchar res4;
+ ushort fc_frame_size; /* frame size (bytes) */
+ char wwn[8]; /* world wide name */
+} PACKED gdth_diskinfo_str;
+
+/* get SCSI channel count */
+typedef struct {
+ ulong32 channel_no; /* number of channel */
+ ulong32 drive_cnt; /* drive count */
+ unchar siop_id; /* SCSI processor ID */
+ unchar siop_state; /* SCSI processor state */
+} PACKED gdth_getch_str;
+
+/* get SCSI drive numbers */
+typedef struct {
+ ulong32 sc_no; /* SCSI channel */
+ ulong32 sc_cnt; /* sc_list[] elements */
+ ulong32 sc_list[MAXID]; /* minor device numbers */
+} PACKED gdth_drlist_str;
+
+/* get grown/primary defect count */
+typedef struct {
+ unchar sddc_type; /* 0x08: grown, 0x10: prim. */
+ unchar sddc_format; /* list entry format */
+ unchar sddc_len; /* list entry length */
+ unchar sddc_res;
+ ulong32 sddc_cnt; /* entry count */
+} PACKED gdth_defcnt_str;
+
+/* disk statistics */
+typedef struct {
+ ulong32 bid; /* SCSI channel */
+ ulong32 first; /* first SCSI disk */
+ ulong32 entries; /* number of elements */
+ ulong32 count; /* (R) number of init. el. */
+ ulong32 mon_time; /* time stamp */
+ struct {
+ unchar tid; /* target ID */
+ unchar lun; /* LUN */
+ unchar res[2];
+ ulong32 blk_size; /* block size in bytes */
+ ulong32 rd_count; /* bytes read */
+ ulong32 wr_count; /* bytes written */
+ ulong32 rd_blk_count; /* blocks read */
+ ulong32 wr_blk_count; /* blocks written */
+ ulong32 retries; /* retries */
+ ulong32 reassigns; /* reassigns */
+ } PACKED list[1];
+} PACKED gdth_dskstat_str;
+
+/* IO channel header */
+typedef struct {
+ ulong32 version; /* version (-1UL: newest) */
+ unchar list_entries; /* list entry count */
+ unchar first_chan; /* first channel number */
+ unchar last_chan; /* last channel number */
+ unchar chan_count; /* (R) channel count */
+ ulong32 list_offset; /* offset of list[0] */
+} PACKED gdth_iochan_header;
+
+/* get IO channel description */
+typedef struct {
+ gdth_iochan_header hdr;
+ struct {
+ ulong32 address; /* channel address */
+ unchar type; /* type (SCSI, FCAL) */
+ unchar local_no; /* local number */
+ ushort features; /* channel features */
+ } PACKED list[MAXBUS];
+} PACKED gdth_iochan_str;
+
+/* get raw IO channel description */
+typedef struct {
+ gdth_iochan_header hdr;
+ struct {
+ unchar proc_id; /* processor id */
+ unchar proc_defect; /* defect ? */
+ unchar reserved[2];
+ } PACKED list[MAXBUS];
+} PACKED gdth_raw_iochan_str;
+
+/* array drive component */
+typedef struct {
+ ulong32 al_controller; /* controller ID */
+ unchar al_cache_drive; /* cache drive number */
+ unchar al_status; /* cache drive state */
+ unchar al_res[2];
+} PACKED gdth_arraycomp_str;
+
+/* array drive information */
+typedef struct {
+ unchar ai_type; /* array type (RAID0,4,5) */
+ unchar ai_cache_drive_cnt; /* active cachedrives */
+ unchar ai_state; /* array drive state */
+ unchar ai_master_cd; /* master cachedrive */
+ ulong32 ai_master_controller; /* ID of master controller */
+ ulong32 ai_size; /* user capacity [sectors] */
+ ulong32 ai_striping_size; /* striping size [sectors] */
+ ulong32 ai_secsize; /* sector size [bytes] */
+ ulong32 ai_err_info; /* failed cache drive */
+ unchar ai_name[8]; /* name of the array drive */
+ unchar ai_controller_cnt; /* number of controllers */
+ unchar ai_removable; /* flag: removable */
+ unchar ai_write_protected; /* flag: write protected */
+ unchar ai_devtype; /* type: always direct access */
+ gdth_arraycomp_str ai_drives[35]; /* drive components: */
+ unchar ai_drive_entries; /* number of drive components */
+ unchar ai_protected; /* protection flag */
+ unchar ai_verify_state; /* state of a parity verify */
+ unchar ai_ext_state; /* extended array drive state */
+ unchar ai_expand_state; /* array expand state (>=2.18)*/
+ unchar ai_reserved[3];
+} PACKED gdth_arrayinf_str;
+
+/* get array drive list */
+typedef struct {
+ ulong32 controller_no; /* controller no. */
+ unchar cd_handle; /* master cachedrive */
+ unchar is_arrayd; /* Flag: is array drive? */
+ unchar is_master; /* Flag: is array master? */
+ unchar is_parity; /* Flag: is parity drive? */
+ unchar is_hotfix; /* Flag: is hotfix drive? */
+ unchar res[3];
+} PACKED gdth_alist_str;
+
+typedef struct {
+ ulong32 entries_avail; /* allocated entries */
+ ulong32 entries_init; /* returned entries */
+ ulong32 first_entry; /* first entry number */
+ ulong32 list_offset; /* offset of following list */
+ gdth_alist_str list[1]; /* list */
+} PACKED gdth_arcdl_str;
+
+/* cache info/config IOCTL */
+typedef struct {
+ ulong32 version; /* firmware version */
+ ushort state; /* cache state (on/off) */
+ ushort strategy; /* cache strategy */
+ ushort write_back; /* write back state (on/off) */
+ ushort block_size; /* cache block size */
+} PACKED gdth_cpar_str;
+
+typedef struct {
+ ulong32 csize; /* cache size */
+ ulong32 read_cnt; /* read/write counter */
+ ulong32 write_cnt;
+ ulong32 tr_hits; /* hits */
+ ulong32 sec_hits;
+ ulong32 sec_miss; /* misses */
+} PACKED gdth_cstat_str;
+
+typedef struct {
+ gdth_cpar_str cpar;
+ gdth_cstat_str cstat;
+} PACKED gdth_cinfo_str;
+
+/* cache drive info */
+typedef struct {
+ unchar cd_name[8]; /* cache drive name */
+ ulong32 cd_devtype; /* SCSI devicetype */
+ ulong32 cd_ldcnt; /* number of log. drives */
+ ulong32 cd_last_error; /* last error */
+ unchar cd_initialized; /* drive is initialized */
+ unchar cd_removable; /* media is removable */
+ unchar cd_write_protected; /* write protected */
+ unchar cd_flags; /* Pool Hot Fix? */
+ ulong32 ld_blkcnt; /* number of blocks */
+ ulong32 ld_blksize; /* blocksize */
+ ulong32 ld_dcnt; /* number of disks */
+ ulong32 ld_slave; /* log. drive index */
+ ulong32 ld_dtype; /* type of logical drive */
+ ulong32 ld_last_error; /* last error */
+ unchar ld_name[8]; /* log. drive name */
+ unchar ld_error; /* error */
+} PACKED gdth_cdrinfo_str;
+
+/* OEM string */
+typedef struct {
+ ulong32 ctl_version;
+ ulong32 file_major_version;
+ ulong32 file_minor_version;
+ ulong32 buffer_size;
+ ulong32 cpy_count;
+ ulong32 ext_error;
+ ulong32 oem_id;
+ ulong32 board_id;
+} PACKED gdth_oem_str_params;
+
+typedef struct {
+ unchar product_0_1_name[16];
+ unchar product_4_5_name[16];
+ unchar product_cluster_name[16];
+ unchar product_reserved[16];
+ unchar scsi_cluster_target_vendor_id[16];
+ unchar cluster_raid_fw_name[16];
+ unchar oem_brand_name[16];
+ unchar oem_raid_type[16];
+ unchar bios_type[13];
+ unchar bios_title[50];
+ unchar oem_company_name[37];
+ ulong32 pci_id_1;
+ ulong32 pci_id_2;
+ unchar validation_status[80];
+ unchar reserved_1[4];
+ unchar scsi_host_drive_inquiry_vendor_id[16];
+ unchar library_file_template[16];
+ unchar reserved_2[16];
+ unchar tool_name_1[32];
+ unchar tool_name_2[32];
+ unchar tool_name_3[32];
+ unchar oem_contact_1[84];
+ unchar oem_contact_2[84];
+ unchar oem_contact_3[84];
+} PACKED gdth_oem_str;
+
+typedef struct {
+ gdth_oem_str_params params;
+ gdth_oem_str text;
+} PACKED gdth_oem_str_ioctl;
+
+/* board features */
+typedef struct {
+ unchar chaining; /* Chaining supported */
+ unchar striping; /* Striping (RAID-0) supp. */
+ unchar mirroring; /* Mirroring (RAID-1) supp. */
+ unchar raid; /* RAID-4/5/10 supported */
+} PACKED gdth_bfeat_str;
+
+/* board info IOCTL */
+typedef struct {
+ ulong32 ser_no; /* serial no. */
+ unchar oem_id[2]; /* OEM ID */
+ ushort ep_flags; /* eprom flags */
+ ulong32 proc_id; /* processor ID */
+ ulong32 memsize; /* memory size (bytes) */
+ unchar mem_banks; /* memory banks */
+ unchar chan_type; /* channel type */
+ unchar chan_count; /* channel count */
+ unchar rdongle_pres; /* dongle present? */
+ ulong32 epr_fw_ver; /* (eprom) firmware version */
+ ulong32 upd_fw_ver; /* (update) firmware version */
+ ulong32 upd_revision; /* update revision */
+ char type_string[16]; /* controller name */
+ char raid_string[16]; /* RAID firmware name */
+ unchar update_pres; /* update present? */
+ unchar xor_pres; /* XOR engine present? */
+ unchar prom_type; /* ROM type (eprom/flash) */
+ unchar prom_count; /* number of ROM devices */
+ ulong32 dup_pres; /* duplexing module present? */
+ ulong32 chan_pres; /* number of expansion chn. */
+ ulong32 mem_pres; /* memory expansion inst. ? */
+ unchar ft_bus_system; /* fault bus supported? */
+ unchar subtype_valid; /* board_subtype valid? */
+ unchar board_subtype; /* subtype/hardware level */
+ unchar ramparity_pres; /* RAM parity check hardware? */
+} PACKED gdth_binfo_str;
+
+/* get host drive info */
+typedef struct {
+ char name[8]; /* host drive name */
+ ulong32 size; /* size (sectors) */
+ unchar host_drive; /* host drive number */
+ unchar log_drive; /* log. drive (master) */
+ unchar reserved;
+ unchar rw_attribs; /* r/w attribs */
+ ulong32 start_sec; /* start sector */
+} PACKED gdth_hentry_str;
+
+typedef struct {
+ ulong32 entries; /* entry count */
+ ulong32 offset; /* offset of entries */
+ unchar secs_p_head; /* sectors/head */
+ unchar heads_p_cyl; /* heads/cylinder */
+ unchar reserved;
+ unchar clust_drvtype; /* cluster drive type */
+ ulong32 location; /* controller number */
+ gdth_hentry_str entry[MAX_HDRIVES]; /* entries */
+} PACKED gdth_hget_str;
+
+
+/* DPRAM structures */
+
+/* interface area ISA/PCI */
+typedef struct {
+ unchar S_Cmd_Indx; /* special command */
+ unchar volatile S_Status; /* status special command */
+ ushort reserved1;
+ ulong32 S_Info[4]; /* add. info special command */
+ unchar volatile Sema0; /* command semaphore */
+ unchar reserved2[3];
+ unchar Cmd_Index; /* command number */
+ unchar reserved3[3];
+ ushort volatile Status; /* command status */
+ ushort Service; /* service(for async.events) */
+ ulong32 Info[2]; /* additional info */
+ struct {
+ ushort offset; /* command offs. in the DPRAM*/
+ ushort serv_id; /* service */
+ } PACKED comm_queue[MAXOFFSETS]; /* command queue */
+ ulong32 bios_reserved[2];
+ unchar gdt_dpr_cmd[1]; /* commands */
+} PACKED gdt_dpr_if;
+
+/* SRAM structure PCI controllers */
+typedef struct {
+ ulong32 magic; /* controller ID from BIOS */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding[9];
+ unchar os_used[16]; /* OS code per service */
+ unchar unused[28];
+ unchar fw_magic; /* contr. ID from firmware */
+} PACKED gdt_pci_sram;
+
+/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
+typedef struct {
+ unchar os_used[16]; /* OS code per service */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding;
+} PACKED gdt_eisa_sram;
+
+
+/* DPRAM ISA controllers */
+typedef struct {
+ union {
+ struct {
+ unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
+ ulong32 magic; /* controller (EISA) ID */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding[9];
+ unchar os_used[16]; /* OS code per service */
+ } PACKED dp_sram;
+ unchar bios_area[0x4000]; /* 16KB reserved for BIOS */
+ } bu;
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x3000]; /* 12KB for interface */
+ } u;
+ struct {
+ unchar memlock; /* write protection DPRAM */
+ unchar event; /* release event */
+ unchar irqen; /* board interrupts enable */
+ unchar irqdel; /* acknowledge board int. */
+ unchar volatile Sema1; /* status semaphore */
+ unchar rq; /* IRQ/DRQ configuration */
+ } PACKED io;
+} PACKED gdt2_dpram_str;
+
+/* DPRAM PCI controllers */
+typedef struct {
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0xff0-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+ struct {
+ unchar unused0[1];
+ unchar volatile Sema1; /* command semaphore */
+ unchar unused1[3];
+ unchar irqen; /* board interrupts enable */
+ unchar unused2[2];
+ unchar event; /* release event */
+ unchar unused3[3];
+ unchar irqdel; /* acknowledge board int. */
+ unchar unused4[3];
+ } PACKED io;
+} PACKED gdt6_dpram_str;
+
+/* PLX register structure (new PCI controllers) */
+typedef struct {
+ unchar cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
+ unchar unused1[0x3f];
+ unchar volatile sema0_reg; /* command semaphore */
+ unchar volatile sema1_reg; /* status semaphore */
+ unchar unused2[2];
+ ushort volatile status; /* command status */
+ ushort service; /* service */
+ ulong32 info[2]; /* additional info */
+ unchar unused3[0x10];
+ unchar ldoor_reg; /* PCI to local doorbell */
+ unchar unused4[3];
+ unchar volatile edoor_reg; /* local to PCI doorbell */
+ unchar unused5[3];
+ unchar control0; /* control0 register(unused) */
+ unchar control1; /* board interrupts enable */
+ unchar unused6[0x16];
+} PACKED gdt6c_plx_regs;
+
+/* DPRAM new PCI controllers */
+typedef struct {
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x4000-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+} PACKED gdt6c_dpram_str;
+
+/* i960 register structure (PCI MPR controllers) */
+typedef struct {
+ unchar unused1[16];
+ unchar volatile sema0_reg; /* command semaphore */
+ unchar unused2;
+ unchar volatile sema1_reg; /* status semaphore */
+ unchar unused3;
+ ushort volatile status; /* command status */
+ ushort service; /* service */
+ ulong32 info[2]; /* additional info */
+ unchar ldoor_reg; /* PCI to local doorbell */
+ unchar unused4[11];
+ unchar volatile edoor_reg; /* local to PCI doorbell */
+ unchar unused5[7];
+ unchar edoor_en_reg; /* board interrupts enable */
+ unchar unused6[27];
+ ulong32 unused7[939];
+ ulong32 severity;
+ char evt_str[256]; /* event string */
+} PACKED gdt6m_i960_regs;
+
+/* DPRAM PCI MPR controllers */
+typedef struct {
+ gdt6m_i960_regs i960r; /* 4KB i960 registers */
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x3000-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+} PACKED gdt6m_dpram_str;
+
+
+/* PCI resources */
+typedef struct {
+ struct pci_dev *pdev;
+ ushort vendor_id; /* vendor (ICP, Intel, ..) */
+ ushort device_id; /* device ID (0,..,9) */
+ ushort subdevice_id; /* sub device ID */
+ unchar bus; /* PCI bus */
+ unchar device_fn; /* PCI device/function no. */
+ ulong dpmem; /* DPRAM address */
+ ulong io; /* IO address */
+ ulong io_mm; /* IO address mem. mapped */
+ unchar irq; /* IRQ */
+} gdth_pci_str;
+
+
+/* controller information structure */
+typedef struct {
+ ushort oem_id; /* OEM */
+ ushort type; /* controller class */
+ ulong32 stype; /* subtype (PCI: device ID) */
+ ushort subdevice_id; /* sub device ID (PCI) */
+ ushort fw_vers; /* firmware version */
+ ushort cache_feat; /* feat. cache serv. (s/g,..)*/
+ ushort raw_feat; /* feat. raw service (s/g,..)*/
+ ushort screen_feat; /* feat. raw service (s/g,..)*/
+ ushort bmic; /* BMIC address (EISA) */
+ void __iomem *brd; /* DPRAM address */
+ ulong32 brd_phys; /* slot number/BIOS address */
+ gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
+ gdth_cmd_str *pccb; /* address command structure */
+ ulong32 ccb_phys; /* phys. address */
+#ifdef INT_COAL
+ gdth_coal_status *coal_stat; /* buffer for coalescing int.*/
+ ulong64 coal_stat_phys; /* phys. address */
+#endif
+ char *pscratch; /* scratch (DMA) buffer */
+ ulong64 scratch_phys; /* phys. address */
+ unchar scratch_busy; /* in use? */
+ unchar dma64_support; /* 64-bit DMA supported? */
+ gdth_msg_str *pmsg; /* message buffer */
+ ulong64 msg_phys; /* phys. address */
+ unchar scan_mode; /* current scan mode */
+ unchar irq; /* IRQ */
+ unchar drq; /* DRQ (ISA controllers) */
+ ushort status; /* command status */
+ ushort service; /* service/firmware ver./.. */
+ ulong32 info;
+ ulong32 info2; /* additional info */
+ Scsi_Cmnd *req_first; /* top of request queue */
+ struct {
+ unchar present; /* Flag: host drive present? */
+ unchar is_logdrv; /* Flag: log. drive (master)? */
+ unchar is_arraydrv; /* Flag: array drive? */
+ unchar is_master; /* Flag: array drive master? */
+ unchar is_parity; /* Flag: parity drive? */
+ unchar is_hotfix; /* Flag: hotfix drive? */
+ unchar master_no; /* number of master drive */
+ unchar lock; /* drive locked? (hot plug) */
+ unchar heads; /* mapping */
+ unchar secs;
+ ushort devtype; /* further information */
+ ulong64 size; /* capacity */
+ unchar ldr_no; /* log. drive no. */
+ unchar rw_attribs; /* r/w attributes */
+ unchar cluster_type; /* cluster properties */
+ unchar media_changed; /* Flag:MOUNT/UNMOUNT occured */
+ ulong32 start_sec; /* start sector */
+ } hdr[MAX_LDRIVES]; /* host drives */
+ struct {
+ unchar lock; /* channel locked? (hot plug) */
+ unchar pdev_cnt; /* physical device count */
+ unchar local_no; /* local channel number */
+ unchar io_cnt[MAXID]; /* current IO count */
+ ulong32 address; /* channel address */
+ ulong32 id_list[MAXID]; /* IDs of the phys. devices */
+ } raw[MAXBUS]; /* SCSI channels */
+ struct {
+ Scsi_Cmnd *cmnd; /* pending request */
+ ushort service; /* service */
+ } cmd_tab[GDTH_MAXCMDS]; /* table of pend. requests */
+ unchar bus_cnt; /* SCSI bus count */
+ unchar tid_cnt; /* Target ID count */
+ unchar bus_id[MAXBUS]; /* IOP IDs */
+ unchar virt_bus; /* number of virtual bus */
+ unchar more_proc; /* more /proc info supported */
+ ushort cmd_cnt; /* command count in DPRAM */
+ ushort cmd_len; /* length of actual command */
+ ushort cmd_offs_dpmem; /* actual offset in DPRAM */
+ ushort ic_all_size; /* sizeof DPRAM interf. area */
+ gdth_cpar_str cpar; /* controller cache par. */
+ gdth_bfeat_str bfeat; /* controller features */
+ gdth_binfo_str binfo; /* controller info */
+ gdth_evt_data dvr; /* event structure */
+ spinlock_t smp_lock;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ struct pci_dev *pdev;
+#endif
+ char oem_name[8];
+#ifdef GDTH_DMA_STATISTICS
+ ulong dma32_cnt, dma64_cnt; /* statistics: DMA buffer */
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ Scsi_Device *sdev;
+#else
+ Scsi_Device sdev;
+#endif
+} gdth_ha_str;
+
+/* structure for scsi_register(), SCSI bus != 0 */
+typedef struct {
+ ushort hanum;
+ ushort busnum;
+} gdth_num_str;
+
+/* structure for scsi_register() */
+typedef struct {
+ gdth_num_str numext; /* must be the first element */
+ gdth_ha_str haext;
+ gdth_cmd_str cmdext;
+} gdth_ext_str;
+
+
+/* INQUIRY data format */
+typedef struct {
+ unchar type_qual;
+ unchar modif_rmb;
+ unchar version;
+ unchar resp_aenc;
+ unchar add_length;
+ unchar reserved1;
+ unchar reserved2;
+ unchar misc;
+ unchar vendor[8];
+ unchar product[16];
+ unchar revision[4];
+} PACKED gdth_inq_data;
+
+/* READ_CAPACITY data format */
+typedef struct {
+ ulong32 last_block_no;
+ ulong32 block_length;
+} PACKED gdth_rdcap_data;
+
+/* READ_CAPACITY (16) data format */
+typedef struct {
+ ulong64 last_block_no;
+ ulong32 block_length;
+} PACKED gdth_rdcap16_data;
+
+/* REQUEST_SENSE data format */
+typedef struct {
+ unchar errorcode;
+ unchar segno;
+ unchar key;
+ ulong32 info;
+ unchar add_length;
+ ulong32 cmd_info;
+ unchar adsc;
+ unchar adsq;
+ unchar fruc;
+ unchar key_spec[3];
+} PACKED gdth_sense_data;
+
+/* MODE_SENSE data format */
+typedef struct {
+ struct {
+ unchar data_length;
+ unchar med_type;
+ unchar dev_par;
+ unchar bd_length;
+ } PACKED hd;
+ struct {
+ unchar dens_code;
+ unchar block_count[3];
+ unchar reserved;
+ unchar block_length[3];
+ } PACKED bd;
+} PACKED gdth_modep_data;
+
+/* stack frame */
+typedef struct {
+ ulong b[10]; /* 32/64 bit compiler ! */
+} PACKED gdth_stackframe;
+
+
+/* function prototyping */
+
+int gdth_detect(Scsi_Host_Template *);
+int gdth_release(struct Scsi_Host *);
+int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *));
+const char *gdth_info(struct Scsi_Host *);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+int gdth_bios_param(struct scsi_device *,struct block_device *,sector_t,int *);
+int gdth_proc_info(struct Scsi_Host *, char *,char **,off_t,int,int);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+int gdth_bios_param(Disk *,kdev_t,int *);
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+#else
+int gdth_bios_param(Disk *,kdev_t,int *);
+extern struct proc_dir_entry proc_scsi_gdth;
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+int gdth_abort(Scsi_Cmnd *);
+int gdth_reset(Scsi_Cmnd *,unsigned int);
+#define GDTH { proc_dir: &proc_scsi_gdth, \
+ proc_info: gdth_proc_info, \
+ name: "GDT SCSI Disk Array Controller",\
+ detect: gdth_detect, \
+ release: gdth_release, \
+ info: gdth_info, \
+ command: NULL, \
+ queuecommand: gdth_queuecommand, \
+ eh_abort_handler: gdth_eh_abort, \
+ eh_device_reset_handler: gdth_eh_device_reset, \
+ eh_bus_reset_handler: gdth_eh_bus_reset, \
+ eh_host_reset_handler: gdth_eh_host_reset, \
+ abort: gdth_abort, \
+ reset: gdth_reset, \
+ bios_param: gdth_bios_param, \
+ can_queue: GDTH_MAXCMDS, \
+ this_id: -1, \
+ sg_tablesize: GDTH_MAXSG, \
+ cmd_per_lun: GDTH_MAXC_P_L, \
+ present: 0, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 /* use new error code */ }
+#endif
+
+int gdth_eh_abort(Scsi_Cmnd *scp);
+int gdth_eh_device_reset(Scsi_Cmnd *scp);
+int gdth_eh_bus_reset(Scsi_Cmnd *scp);
+int gdth_eh_host_reset(Scsi_Cmnd *scp);
+
+#endif
diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
new file mode 100644
index 000000000000..783fae737f17
--- /dev/null
+++ b/drivers/scsi/gdth_ioctl.h
@@ -0,0 +1,347 @@
+#ifndef _GDTH_IOCTL_H
+#define _GDTH_IOCTL_H
+
+/* gdth_ioctl.h
+ * $Id: gdth_ioctl.h,v 1.14 2004/02/19 15:43:15 achim Exp $
+ */
+
+/* IOCTLs */
+#define GDTIOCTL_MASK ('J'<<8)
+#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */
+#define GDTIOCTL_HDRLIST (GDTIOCTL_MASK | 4) /* get host drive list */
+#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
+#define GDTIOCTL_SCSI (GDTIOCTL_MASK | 9) /* SCSI command */
+#define GDTIOCTL_RESET_BUS (GDTIOCTL_MASK |10) /* reset SCSI bus */
+#define GDTIOCTL_RESCAN (GDTIOCTL_MASK |11) /* rescan host drives */
+#define GDTIOCTL_RESET_DRV (GDTIOCTL_MASK |12) /* reset (remote) drv. res. */
+
+#define GDTIOCTL_MAGIC 0xaffe0004
+#define EVENT_SIZE 294
+#define GDTH_MAXSG 32 /* max. s/g elements */
+
+#define MAX_LDRIVES 255 /* max. log. drive count */
+#ifdef GDTH_IOCTL_PROC
+#define MAX_HDRIVES 100 /* max. host drive count */
+#else
+#define MAX_HDRIVES MAX_LDRIVES /* max. host drive count */
+#endif
+
+/* typedefs */
+#ifdef __KERNEL__
+typedef u32 ulong32;
+typedef u64 ulong64;
+#endif
+
+#define PACKED __attribute__((packed))
+
+/* scatter/gather element */
+typedef struct {
+ ulong32 sg_ptr; /* address */
+ ulong32 sg_len; /* length */
+} PACKED gdth_sg_str;
+
+/* scatter/gather element - 64bit addresses */
+typedef struct {
+ ulong64 sg_ptr; /* address */
+ ulong32 sg_len; /* length */
+} PACKED gdth_sg64_str;
+
+/* command structure */
+typedef struct {
+ ulong32 BoardNode; /* board node (always 0) */
+ ulong32 CommandIndex; /* command number */
+ ushort OpCode; /* the command (READ,..) */
+ union {
+ struct {
+ ushort DeviceNo; /* number of cache drive */
+ ulong32 BlockNo; /* block number */
+ ulong32 BlockCnt; /* block count */
+ ulong32 DestAddr; /* dest. addr. (if s/g: -1) */
+ ulong32 sg_canz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } PACKED cache; /* cache service cmd. str. */
+ struct {
+ ushort DeviceNo; /* number of cache drive */
+ ulong64 BlockNo; /* block number */
+ ulong32 BlockCnt; /* block count */
+ ulong64 DestAddr; /* dest. addr. (if s/g: -1) */
+ ulong32 sg_canz; /* s/g element count */
+ gdth_sg64_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } PACKED cache64; /* cache service cmd. str. */
+ struct {
+ ushort param_size; /* size of p_param buffer */
+ ulong32 subfunc; /* IOCTL function */
+ ulong32 channel; /* device */
+ ulong64 p_param; /* buffer */
+ } PACKED ioctl; /* IOCTL command structure */
+ struct {
+ ushort reserved;
+ union {
+ struct {
+ ulong32 msg_handle; /* message handle */
+ ulong64 msg_addr; /* message buffer address */
+ } PACKED msg;
+ unchar data[12]; /* buffer for rtc data, ... */
+ } su;
+ } PACKED screen; /* screen service cmd. str. */
+ struct {
+ ushort reserved;
+ ulong32 direction; /* data direction */
+ ulong32 mdisc_time; /* disc. time (0: no timeout)*/
+ ulong32 mcon_time; /* connect time(0: no to.) */
+ ulong32 sdata; /* dest. addr. (if s/g: -1) */
+ ulong32 sdlen; /* data length (bytes) */
+ ulong32 clen; /* SCSI cmd. length(6,10,12) */
+ unchar cmd[12]; /* SCSI command */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar bus; /* SCSI bus number */
+ unchar priority; /* only 0 used */
+ ulong32 sense_len; /* sense data length */
+ ulong32 sense_data; /* sense data addr. */
+ ulong32 link_p; /* linked cmds (not supp.) */
+ ulong32 sg_ranz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } PACKED raw; /* raw service cmd. struct. */
+ struct {
+ ushort reserved;
+ ulong32 direction; /* data direction */
+ ulong32 mdisc_time; /* disc. time (0: no timeout)*/
+ ulong32 mcon_time; /* connect time(0: no to.) */
+ ulong64 sdata; /* dest. addr. (if s/g: -1) */
+ ulong32 sdlen; /* data length (bytes) */
+ ulong32 clen; /* SCSI cmd. length(6,..,16) */
+ unchar cmd[16]; /* SCSI command */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar bus; /* SCSI bus number */
+ unchar priority; /* only 0 used */
+ ulong32 sense_len; /* sense data length */
+ ulong64 sense_data; /* sense data addr. */
+ ulong32 sg_ranz; /* s/g element count */
+ gdth_sg64_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } PACKED raw64; /* raw service cmd. struct. */
+ } u;
+ /* additional variables */
+ unchar Service; /* controller service */
+ unchar reserved;
+ ushort Status; /* command result */
+ ulong32 Info; /* additional information */
+ void *RequestBuffer; /* request buffer */
+} PACKED gdth_cmd_str;
+
+/* controller event structure */
+#define ES_ASYNC 1
+#define ES_DRIVER 2
+#define ES_TEST 3
+#define ES_SYNC 4
+typedef struct {
+ ushort size; /* size of structure */
+ union {
+ char stream[16];
+ struct {
+ ushort ionode;
+ ushort service;
+ ulong32 index;
+ } PACKED driver;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong32 info;
+ unchar scsi_coord[3];
+ } PACKED async;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong32 info;
+ ushort hostdrive;
+ unchar scsi_coord[3];
+ unchar sense_key;
+ } PACKED sync;
+ struct {
+ ulong32 l1, l2, l3, l4;
+ } PACKED test;
+ } eu;
+ ulong32 severity;
+ unchar event_string[256];
+} PACKED gdth_evt_data;
+
+typedef struct {
+ ulong32 first_stamp;
+ ulong32 last_stamp;
+ ushort same_count;
+ ushort event_source;
+ ushort event_idx;
+ unchar application;
+ unchar reserved;
+ gdth_evt_data event_data;
+} PACKED gdth_evt_str;
+
+
+#ifdef GDTH_IOCTL_PROC
+/* IOCTL structure (write) */
+typedef struct {
+ ulong32 magic; /* IOCTL magic */
+ ushort ioctl; /* IOCTL */
+ ushort ionode; /* controller number */
+ ushort service; /* controller service */
+ ushort timeout; /* timeout */
+ union {
+ struct {
+ unchar command[512]; /* controller command */
+ unchar data[1]; /* add. data */
+ } general;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar drive_cnt; /* drive count */
+ ushort drives[MAX_HDRIVES];/* drives */
+ } lockdrv;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar channel; /* channel */
+ } lockchn;
+ struct {
+ int erase; /* erase event ? */
+ int handle;
+ unchar evt[EVENT_SIZE]; /* event structure */
+ } event;
+ struct {
+ unchar bus; /* SCSI bus */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar cmd_len; /* command length */
+ unchar cmd[12]; /* SCSI command */
+ } scsi;
+ struct {
+ ushort hdr_no; /* host drive number */
+ unchar flag; /* old meth./add/remove */
+ } rescan;
+ } iu;
+} gdth_iowr_str;
+
+/* IOCTL structure (read) */
+typedef struct {
+ ulong32 size; /* buffer size */
+ ulong32 status; /* IOCTL error code */
+ union {
+ struct {
+ unchar data[1]; /* data */
+ } general;
+ struct {
+ ushort version; /* driver version */
+ } drvers;
+ struct {
+ unchar type; /* controller type */
+ ushort info; /* slot etc. */
+ ushort oem_id; /* OEM ID */
+ ushort bios_ver; /* not used */
+ ushort access; /* not used */
+ ushort ext_type; /* extended type */
+ ushort device_id; /* device ID */
+ ushort sub_device_id; /* sub device ID */
+ } ctrtype;
+ struct {
+ unchar version; /* OS version */
+ unchar subversion; /* OS subversion */
+ ushort revision; /* revision */
+ } osvers;
+ struct {
+ ushort count; /* controller count */
+ } ctrcnt;
+ struct {
+ int handle;
+ unchar evt[EVENT_SIZE]; /* event structure */
+ } event;
+ struct {
+ unchar bus; /* SCSI bus, 0xff: invalid */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar cluster_type; /* cluster properties */
+ } hdr_list[MAX_HDRIVES]; /* index is host drive number */
+ } iu;
+} gdth_iord_str;
+#endif
+
+/* GDTIOCTL_GENERAL */
+typedef struct {
+ ushort ionode; /* controller number */
+ ushort timeout; /* timeout */
+ ulong32 info; /* error info */
+ ushort status; /* status */
+ ulong data_len; /* data buffer size */
+ ulong sense_len; /* sense buffer size */
+ gdth_cmd_str command; /* command */
+} gdth_ioctl_general;
+
+/* GDTIOCTL_LOCKDRV */
+typedef struct {
+ ushort ionode; /* controller number */
+ unchar lock; /* lock/unlock */
+ unchar drive_cnt; /* drive count */
+ ushort drives[MAX_HDRIVES]; /* drives */
+} gdth_ioctl_lockdrv;
+
+/* GDTIOCTL_LOCKCHN */
+typedef struct {
+ ushort ionode; /* controller number */
+ unchar lock; /* lock/unlock */
+ unchar channel; /* channel */
+} gdth_ioctl_lockchn;
+
+/* GDTIOCTL_OSVERS */
+typedef struct {
+ unchar version; /* OS version */
+ unchar subversion; /* OS subversion */
+ ushort revision; /* revision */
+} gdth_ioctl_osvers;
+
+/* GDTIOCTL_CTRTYPE */
+typedef struct {
+ ushort ionode; /* controller number */
+ unchar type; /* controller type */
+ ushort info; /* slot etc. */
+ ushort oem_id; /* OEM ID */
+ ushort bios_ver; /* not used */
+ ushort access; /* not used */
+ ushort ext_type; /* extended type */
+ ushort device_id; /* device ID */
+ ushort sub_device_id; /* sub device ID */
+} gdth_ioctl_ctrtype;
+
+/* GDTIOCTL_EVENT */
+typedef struct {
+ ushort ionode;
+ int erase; /* erase event? */
+ int handle; /* event handle */
+ gdth_evt_str event;
+} gdth_ioctl_event;
+
+/* GDTIOCTL_RESCAN/GDTIOCTL_HDRLIST */
+typedef struct {
+ ushort ionode; /* controller number */
+ unchar flag; /* add/remove */
+ ushort hdr_no; /* drive no. */
+ struct {
+ unchar bus; /* SCSI bus */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar cluster_type; /* cluster properties */
+ } hdr_list[MAX_HDRIVES]; /* index is host drive number */
+} gdth_ioctl_rescan;
+
+/* GDTIOCTL_RESET_BUS/GDTIOCTL_RESET_DRV */
+typedef struct {
+ ushort ionode; /* controller number */
+ ushort number; /* bus/host drive number */
+ ushort status; /* status */
+} gdth_ioctl_reset;
+
+#endif
diff --git a/drivers/scsi/gdth_kcompat.h b/drivers/scsi/gdth_kcompat.h
new file mode 100644
index 000000000000..e6cf0edfa0ca
--- /dev/null
+++ b/drivers/scsi/gdth_kcompat.h
@@ -0,0 +1,21 @@
+
+
+#ifndef IRQ_HANDLED
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#endif
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef SERVICE_ACTION_IN
+#define SERVICE_ACTION_IN 0x9e
+#endif
+#ifndef READ_16
+#define READ_16 0x88
+#endif
+#ifndef WRITE_16
+#define WRITE_16 0x8a
+#endif
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
new file mode 100644
index 000000000000..1bd02f8d1e6a
--- /dev/null
+++ b/drivers/scsi/gdth_proc.c
@@ -0,0 +1,1030 @@
+/* gdth_proc.c
+ * $Id: gdth_proc.c,v 1.42 2004/03/05 15:50:20 achim Exp $
+ */
+
+#include <linux/completion.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+int gdth_proc_info(struct Scsi_Host *host, char *buffer,char **start,off_t offset,int length,
+ int inout)
+{
+ int hanum,busnum;
+
+ TRACE2(("gdth_proc_info() length %d offs %d inout %d\n",
+ length,(int)offset,inout));
+
+ hanum = NUMDATA(host)->hanum;
+ busnum= NUMDATA(host)->busnum;
+
+ if (inout)
+ return(gdth_set_info(buffer,length,host,hanum,busnum));
+ else
+ return(gdth_get_info(buffer,start,offset,length,host,hanum,busnum));
+}
+#else
+int gdth_proc_info(char *buffer,char **start,off_t offset,int length,int hostno,
+ int inout)
+{
+ int hanum,busnum,i;
+
+ TRACE2(("gdth_proc_info() length %d offs %d inout %d\n",
+ length,(int)offset,inout));
+
+ for (i = 0; i < gdth_ctr_vcount; ++i) {
+ if (gdth_ctr_vtab[i]->host_no == hostno)
+ break;
+ }
+ if (i == gdth_ctr_vcount)
+ return(-EINVAL);
+
+ hanum = NUMDATA(gdth_ctr_vtab[i])->hanum;
+ busnum= NUMDATA(gdth_ctr_vtab[i])->busnum;
+
+ if (inout)
+ return(gdth_set_info(buffer,length,gdth_ctr_vtab[i],hanum,busnum));
+ else
+ return(gdth_get_info(buffer,start,offset,length,
+ gdth_ctr_vtab[i],hanum,busnum));
+}
+#endif
+
+static int gdth_set_info(char *buffer,int length,struct Scsi_Host *host,
+ int hanum,int busnum)
+{
+ int ret_val = -EINVAL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *scp;
+ Scsi_Device *sdev;
+#else
+ Scsi_Cmnd *scp;
+ Scsi_Device *sdev;
+#endif
+ TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ sdev = scsi_get_host_dev(host);
+ scp = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!scp)
+ return -ENOMEM;
+ scp->sr_cmd_len = 12;
+ scp->sr_use_sg = 0;
+#else
+ sdev = scsi_get_host_dev(host);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ if (!scp)
+ return -ENOMEM;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+#endif
+
+ if (length >= 4) {
+ if (strncmp(buffer,"gdth",4) == 0) {
+ buffer += 5;
+ length -= 5;
+ ret_val = gdth_set_asc_info( buffer, length, hanum, scp );
+ }
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scsi_release_request(scp);
+ scsi_free_host_dev(sdev);
+#else
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
+#endif
+ return ret_val;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Request *scp)
+#else
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp)
+#endif
+{
+ int orig_length, drive, wb_mode;
+ int i, found;
+ gdth_ha_str *ha;
+ gdth_cmd_str gdtcmd;
+ gdth_cpar_str *pcpar;
+ ulong64 paddr;
+
+ char cmnd[MAX_COMMAND_SIZE];
+ memset(cmnd, 0xff, 12);
+ memset(&gdtcmd, 0, sizeof(gdth_cmd_str));
+
+ TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ orig_length = length + 5;
+ drive = -1;
+ wb_mode = 0;
+ found = FALSE;
+
+ if (length >= 5 && strncmp(buffer,"flush",5)==0) {
+ buffer += 6;
+ length -= 6;
+ if (length && *buffer>='0' && *buffer<='9') {
+ drive = (int)(*buffer-'0');
+ ++buffer; --length;
+ if (length && *buffer>='0' && *buffer<='9') {
+ drive = drive*10 + (int)(*buffer-'0');
+ ++buffer; --length;
+ }
+ printk("GDT: Flushing host drive %d .. ",drive);
+ } else {
+ printk("GDT: Flushing all host drives .. ");
+ }
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (ha->hdr[i].present) {
+ if (drive != -1 && i != drive)
+ continue;
+ found = TRUE;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ if (ha->cache_feat & GDT_64BIT) {
+ gdtcmd.u.cache64.DeviceNo = i;
+ gdtcmd.u.cache64.BlockNo = 1;
+ } else {
+ gdtcmd.u.cache.DeviceNo = i;
+ gdtcmd.u.cache.BlockNo = 1;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, &gdtcmd, cmnd, 30);
+#else
+ gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+#endif
+ }
+ }
+ if (!found)
+ printk("\nNo host drive found !\n");
+ else
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
+ buffer += 8;
+ length -= 8;
+ printk("GDT: Disabling write back permanently .. ");
+ wb_mode = 1;
+ } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
+ buffer += 7;
+ length -= 7;
+ printk("GDT: Enabling write back permanently .. ");
+ wb_mode = 2;
+ } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
+ buffer += 7;
+ length -= 7;
+ printk("GDT: Disabling write back commands .. ");
+ if (ha->cache_feat & GDT_WR_THROUGH) {
+ gdth_write_through = TRUE;
+ printk("Done.\n");
+ } else {
+ printk("Not supported !\n");
+ }
+ return(orig_length);
+ } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
+ buffer += 6;
+ length -= 6;
+ printk("GDT: Enabling write back commands .. ");
+ gdth_write_through = FALSE;
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ if (wb_mode) {
+ if (!gdth_ioctl_alloc(hanum, sizeof(gdth_cpar_str), TRUE, &paddr))
+ return(-EBUSY);
+ pcpar = (gdth_cpar_str *)ha->pscratch;
+ memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_IOCTL;
+ gdtcmd.u.ioctl.p_param = paddr;
+ gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
+ gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
+ gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
+ pcpar->write_back = wb_mode==1 ? 0:1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, &gdtcmd, cmnd, 30);
+#else
+ gdth_do_cmd(scp, &gdtcmd, cmnd, 30);
+#endif
+ gdth_ioctl_free(hanum, GDTH_SCRATCH, ha->pscratch, paddr);
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ printk("GDT: Unknown command: %s Length: %d\n",buffer,length);
+ return(-EINVAL);
+}
+
+static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
+ struct Scsi_Host *host,int hanum,int busnum)
+{
+ int size = 0,len = 0;
+ off_t begin = 0,pos = 0;
+ gdth_ha_str *ha;
+ int id, i, j, k, sec, flag;
+ int no_mdrv = 0, drv_no, is_mirr;
+ ulong32 cnt;
+ ulong64 paddr;
+ int rc = -ENOMEM;
+
+ gdth_cmd_str *gdtcmd;
+ gdth_evt_str *estr;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ Scsi_Request *scp;
+ Scsi_Device *sdev;
+#else
+ Scsi_Cmnd *scp;
+ Scsi_Device *sdev;
+#endif
+ char hrec[161];
+ struct timeval tv;
+
+ char *buf;
+ gdth_dskstat_str *pds;
+ gdth_diskinfo_str *pdi;
+ gdth_arrayinf_str *pai;
+ gdth_defcnt_str *pdef;
+ gdth_cdrinfo_str *pcdi;
+ gdth_hget_str *phg;
+ char cmnd[MAX_COMMAND_SIZE];
+
+ gdtcmd = kmalloc(sizeof(*gdtcmd), GFP_KERNEL);
+ estr = kmalloc(sizeof(*estr), GFP_KERNEL);
+ if (!gdtcmd || !estr)
+ goto free_fail;
+
+ memset(cmnd, 0xff, 12);
+ memset(gdtcmd, 0, sizeof(gdth_cmd_str));
+
+ TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ sdev = scsi_get_host_dev(host);
+ scp = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!scp)
+ goto free_fail;
+ scp->sr_cmd_len = 12;
+ scp->sr_use_sg = 0;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ sdev = scsi_get_host_dev(host);
+ scp = scsi_allocate_device(sdev, 1, FALSE);
+ if (!scp)
+ goto free_fail;
+ scp->cmd_len = 12;
+ scp->use_sg = 0;
+#else
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = scp.host = host;
+ sdev.id = scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+#endif
+
+
+ /* request is i.e. "cat /proc/scsi/gdth/0" */
+ /* format: %-15s\t%-10s\t%-15s\t%s */
+ /* driver parameters */
+ size = sprintf(buffer+len,"Driver Parameters:\n");
+ len += size; pos = begin + len;
+ if (reserve_list[0] == 0xff)
+ strcpy(hrec, "--");
+ else {
+ sprintf(hrec, "%d", reserve_list[0]);
+ for (i = 1; i < MAX_RES_ARGS; i++) {
+ if (reserve_list[i] == 0xff)
+ break;
+ sprintf(hrec,"%s,%d", hrec, reserve_list[i]);
+ }
+ }
+ size = sprintf(buffer+len,
+ " reserve_mode: \t%d \treserve_list: \t%s\n",
+ reserve_mode, hrec);
+ len += size; pos = begin + len;
+ size = sprintf(buffer+len,
+ " max_ids: \t%-3d \thdr_channel: \t%d\n",
+ max_ids, hdr_channel);
+ len += size; pos = begin + len;
+
+ /* controller information */
+ size = sprintf(buffer+len,"\nDisk Array Controller Information:\n");
+ len += size; pos = begin + len;
+ if (virt_ctr)
+ sprintf(hrec, "%s (Bus %d)", ha->binfo.type_string, busnum);
+ else
+ strcpy(hrec, ha->binfo.type_string);
+ size = sprintf(buffer+len,
+ " Number: \t%d \tName: \t%s\n",
+ hanum, hrec);
+ len += size; pos = begin + len;
+
+ if (ha->more_proc)
+ sprintf(hrec, "%d.%02d.%02d-%c%03X",
+ (unchar)(ha->binfo.upd_fw_ver>>24),
+ (unchar)(ha->binfo.upd_fw_ver>>16),
+ (unchar)(ha->binfo.upd_fw_ver),
+ ha->bfeat.raid ? 'R':'N',
+ ha->binfo.upd_revision);
+ else
+ sprintf(hrec, "%d.%02d", (unchar)(ha->cpar.version>>8),
+ (unchar)(ha->cpar.version));
+
+ size = sprintf(buffer+len,
+ " Driver Ver.: \t%-10s\tFirmware Ver.: \t%s\n",
+ GDTH_VERSION_STR, hrec);
+ len += size; pos = begin + len;
+
+ if (ha->more_proc) {
+ /* more information: 1. about controller */
+ size = sprintf(buffer+len,
+ " Serial No.: \t0x%8X\tCache RAM size:\t%d KB\n",
+ ha->binfo.ser_no, ha->binfo.memsize / 1024);
+ len += size; pos = begin + len;
+ }
+
+#ifdef GDTH_DMA_STATISTICS
+ /* controller statistics */
+ size = sprintf(buffer+len,"\nController Statistics:\n");
+ len += size; pos = begin + len;
+ size = sprintf(buffer+len,
+ " 32-bit DMA buffer:\t%lu\t64-bit DMA buffer:\t%lu\n",
+ ha->dma32_cnt, ha->dma64_cnt);
+ len += size; pos = begin + len;
+#endif
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ if (ha->more_proc) {
+ /* more information: 2. about physical devices */
+ size = sprintf(buffer+len,"\nPhysical Devices:");
+ len += size; pos = begin + len;
+ flag = FALSE;
+
+ buf = gdth_ioctl_alloc(hanum, GDTH_SCRATCH, FALSE, &paddr);
+ if (!buf)
+ goto stop_output;
+ for (i = 0; i < ha->bus_cnt; ++i) {
+ /* 2.a statistics (and retries/reassigns) */
+ TRACE2(("pdr_statistics() chn %d\n",i));
+ pds = (gdth_dskstat_str *)(buf + GDTH_SCRATCH/4);
+ gdtcmd->Service = CACHESERVICE;
+ gdtcmd->OpCode = GDT_IOCTL;
+ gdtcmd->u.ioctl.p_param = paddr + GDTH_SCRATCH/4;
+ gdtcmd->u.ioctl.param_size = 3*GDTH_SCRATCH/4;
+ gdtcmd->u.ioctl.subfunc = DSK_STATISTICS | L_CTRL_PATTERN;
+ gdtcmd->u.ioctl.channel = ha->raw[i].address | INVALID_CHANNEL;
+ pds->bid = ha->raw[i].local_no;
+ pds->first = 0;
+ pds->entries = ha->raw[i].pdev_cnt;
+ cnt = (3*GDTH_SCRATCH/4 - 5 * sizeof(ulong32)) /
+ sizeof(pds->list[0]);
+ if (pds->entries > cnt)
+ pds->entries = cnt;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, gdtcmd, cmnd, 30);
+ if (scp->sr_command->SCp.Status != S_OK)
+#else
+ gdth_do_cmd(scp, gdtcmd, cmnd, 30);
+ if (scp->SCp.Status != S_OK)
+#endif
+ {
+ pds->count = 0;
+ }
+
+ /* other IOCTLs must fit into area GDTH_SCRATCH/4 */
+ for (j = 0; j < ha->raw[i].pdev_cnt; ++j) {
+ /* 2.b drive info */
+ TRACE2(("scsi_drv_info() chn %d dev %d\n",
+ i, ha->raw[i].id_list[j]));
+ pdi = (gdth_diskinfo_str *)buf;
+ gdtcmd->Service = CACHESERVICE;
+ gdtcmd->OpCode = GDT_IOCTL;
+ gdtcmd->u.ioctl.p_param = paddr;
+ gdtcmd->u.ioctl.param_size = sizeof(gdth_diskinfo_str);
+ gdtcmd->u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN;
+ gdtcmd->u.ioctl.channel =
+ ha->raw[i].address | ha->raw[i].id_list[j];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, gdtcmd, cmnd, 30);
+ if (scp->sr_command->SCp.Status == S_OK)
+#else
+ gdth_do_cmd(scp, gdtcmd, cmnd, 30);
+ if (scp->SCp.Status == S_OK)
+#endif
+ {
+ strncpy(hrec,pdi->vendor,8);
+ strncpy(hrec+8,pdi->product,16);
+ strncpy(hrec+24,pdi->revision,4);
+ hrec[28] = 0;
+ size = sprintf(buffer+len,
+ "\n Chn/ID/LUN: \t%c/%02d/%d \tName: \t%s\n",
+ 'A'+i,pdi->target_id,pdi->lun,hrec);
+ len += size; pos = begin + len;
+ flag = TRUE;
+ pdi->no_ldrive &= 0xffff;
+ if (pdi->no_ldrive == 0xffff)
+ strcpy(hrec,"--");
+ else
+ sprintf(hrec,"%d",pdi->no_ldrive);
+ size = sprintf(buffer+len,
+ " Capacity [MB]:\t%-6d \tTo Log. Drive: \t%s\n",
+ pdi->blkcnt/(1024*1024/pdi->blksize),
+ hrec);
+ len += size; pos = begin + len;
+ } else {
+ pdi->devtype = 0xff;
+ }
+
+ if (pdi->devtype == 0) {
+ /* search retries/reassigns */
+ for (k = 0; k < pds->count; ++k) {
+ if (pds->list[k].tid == pdi->target_id &&
+ pds->list[k].lun == pdi->lun) {
+ size = sprintf(buffer+len,
+ " Retries: \t%-6d \tReassigns: \t%d\n",
+ pds->list[k].retries,
+ pds->list[k].reassigns);
+ len += size; pos = begin + len;
+ break;
+ }
+ }
+ /* 2.c grown defects */
+ TRACE2(("scsi_drv_defcnt() chn %d dev %d\n",
+ i, ha->raw[i].id_list[j]));
+ pdef = (gdth_defcnt_str *)buf;
+ gdtcmd->Service = CACHESERVICE;
+ gdtcmd->OpCode = GDT_IOCTL;
+ gdtcmd->u.ioctl.p_param = paddr;
+ gdtcmd->u.ioctl.param_size = sizeof(gdth_defcnt_str);
+ gdtcmd->u.ioctl.subfunc = SCSI_DEF_CNT | L_CTRL_PATTERN;
+ gdtcmd->u.ioctl.channel =
+ ha->raw[i].address | ha->raw[i].id_list[j];
+ pdef->sddc_type = 0x08;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, gdtcmd, cmnd, 30);
+ if (scp->sr_command->SCp.Status == S_OK)
+#else
+ gdth_do_cmd(scp, gdtcmd, cmnd, 30);
+ if (scp->SCp.Status == S_OK)
+#endif
+ {
+ size = sprintf(buffer+len,
+ " Grown Defects:\t%d\n",
+ pdef->sddc_cnt);
+ len += size; pos = begin + len;
+ }
+ }
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ }
+ gdth_ioctl_free(hanum, GDTH_SCRATCH, buf, paddr);
+
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
+
+ /* 3. about logical drives */
+ size = sprintf(buffer+len,"\nLogical Drives:");
+ len += size; pos = begin + len;
+ flag = FALSE;
+
+ buf = gdth_ioctl_alloc(hanum, GDTH_SCRATCH, FALSE, &paddr);
+ if (!buf)
+ goto stop_output;
+ for (i = 0; i < MAX_LDRIVES; ++i) {
+ if (!ha->hdr[i].is_logdrv)
+ continue;
+ drv_no = i;
+ j = k = 0;
+ is_mirr = FALSE;
+ do {
+ /* 3.a log. drive info */
+ TRACE2(("cache_drv_info() drive no %d\n",drv_no));
+ pcdi = (gdth_cdrinfo_str *)buf;
+ gdtcmd->Service = CACHESERVICE;
+ gdtcmd->OpCode = GDT_IOCTL;
+ gdtcmd->u.ioctl.p_param = paddr;
+ gdtcmd->u.ioctl.param_size = sizeof(gdth_cdrinfo_str);
+ gdtcmd->u.ioctl.subfunc = CACHE_DRV_INFO;
+ gdtcmd->u.ioctl.channel = drv_no;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, gdtcmd, cmnd, 30);
+ if (scp->sr_command->SCp.Status != S_OK)
+#else
+ gdth_do_cmd(scp, gdtcmd, cmnd, 30);
+ if (scp->SCp.Status != S_OK)
+#endif
+ {
+ break;
+ }
+ pcdi->ld_dtype >>= 16;
+ j++;
+ if (pcdi->ld_dtype > 2) {
+ strcpy(hrec, "missing");
+ } else if (pcdi->ld_error & 1) {
+ strcpy(hrec, "fault");
+ } else if (pcdi->ld_error & 2) {
+ strcpy(hrec, "invalid");
+ k++; j--;
+ } else {
+ strcpy(hrec, "ok");
+ }
+
+ if (drv_no == i) {
+ size = sprintf(buffer+len,
+ "\n Number: \t%-2d \tStatus: \t%s\n",
+ drv_no, hrec);
+ len += size; pos = begin + len;
+ flag = TRUE;
+ no_mdrv = pcdi->cd_ldcnt;
+ if (no_mdrv > 1 || pcdi->ld_slave != -1) {
+ is_mirr = TRUE;
+ strcpy(hrec, "RAID-1");
+ } else if (pcdi->ld_dtype == 0) {
+ strcpy(hrec, "Disk");
+ } else if (pcdi->ld_dtype == 1) {
+ strcpy(hrec, "RAID-0");
+ } else if (pcdi->ld_dtype == 2) {
+ strcpy(hrec, "Chain");
+ } else {
+ strcpy(hrec, "???");
+ }
+ size = sprintf(buffer+len,
+ " Capacity [MB]:\t%-6d \tType: \t%s\n",
+ pcdi->ld_blkcnt/(1024*1024/pcdi->ld_blksize),
+ hrec);
+ len += size; pos = begin + len;
+ } else {
+ size = sprintf(buffer+len,
+ " Slave Number: \t%-2d \tStatus: \t%s\n",
+ drv_no & 0x7fff, hrec);
+ len += size; pos = begin + len;
+ }
+ drv_no = pcdi->ld_slave;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ } while (drv_no != -1);
+
+ if (is_mirr) {
+ size = sprintf(buffer+len,
+ " Missing Drv.: \t%-2d \tInvalid Drv.: \t%d\n",
+ no_mdrv - j - k, k);
+ len += size; pos = begin + len;
+ }
+
+ if (!ha->hdr[i].is_arraydrv)
+ strcpy(hrec, "--");
+ else
+ sprintf(hrec, "%d", ha->hdr[i].master_no);
+ size = sprintf(buffer+len,
+ " To Array Drv.:\t%s\n", hrec);
+ len += size; pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ gdth_ioctl_free(hanum, GDTH_SCRATCH, buf, paddr);
+
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
+
+ /* 4. about array drives */
+ size = sprintf(buffer+len,"\nArray Drives:");
+ len += size; pos = begin + len;
+ flag = FALSE;
+
+ buf = gdth_ioctl_alloc(hanum, GDTH_SCRATCH, FALSE, &paddr);
+ if (!buf)
+ goto stop_output;
+ for (i = 0; i < MAX_LDRIVES; ++i) {
+ if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master))
+ continue;
+ /* 4.a array drive info */
+ TRACE2(("array_info() drive no %d\n",i));
+ pai = (gdth_arrayinf_str *)buf;
+ gdtcmd->Service = CACHESERVICE;
+ gdtcmd->OpCode = GDT_IOCTL;
+ gdtcmd->u.ioctl.p_param = paddr;
+ gdtcmd->u.ioctl.param_size = sizeof(gdth_arrayinf_str);
+ gdtcmd->u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN;
+ gdtcmd->u.ioctl.channel = i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, gdtcmd, cmnd, 30);
+ if (scp->sr_command->SCp.Status == S_OK)
+#else
+ gdth_do_cmd(scp, gdtcmd, cmnd, 30);
+ if (scp->SCp.Status == S_OK)
+#endif
+ {
+ if (pai->ai_state == 0)
+ strcpy(hrec, "idle");
+ else if (pai->ai_state == 2)
+ strcpy(hrec, "build");
+ else if (pai->ai_state == 4)
+ strcpy(hrec, "ready");
+ else if (pai->ai_state == 6)
+ strcpy(hrec, "fail");
+ else if (pai->ai_state == 8 || pai->ai_state == 10)
+ strcpy(hrec, "rebuild");
+ else
+ strcpy(hrec, "error");
+ if (pai->ai_ext_state & 0x10)
+ strcat(hrec, "/expand");
+ else if (pai->ai_ext_state & 0x1)
+ strcat(hrec, "/patch");
+ size = sprintf(buffer+len,
+ "\n Number: \t%-2d \tStatus: \t%s\n",
+ i,hrec);
+ len += size; pos = begin + len;
+ flag = TRUE;
+
+ if (pai->ai_type == 0)
+ strcpy(hrec, "RAID-0");
+ else if (pai->ai_type == 4)
+ strcpy(hrec, "RAID-4");
+ else if (pai->ai_type == 5)
+ strcpy(hrec, "RAID-5");
+ else
+ strcpy(hrec, "RAID-10");
+ size = sprintf(buffer+len,
+ " Capacity [MB]:\t%-6d \tType: \t%s\n",
+ pai->ai_size/(1024*1024/pai->ai_secsize),
+ hrec);
+ len += size; pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ }
+ gdth_ioctl_free(hanum, GDTH_SCRATCH, buf, paddr);
+
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
+
+ /* 5. about host drives */
+ size = sprintf(buffer+len,"\nHost Drives:");
+ len += size; pos = begin + len;
+ flag = FALSE;
+
+ buf = gdth_ioctl_alloc(hanum, sizeof(gdth_hget_str), FALSE, &paddr);
+ if (!buf)
+ goto stop_output;
+ for (i = 0; i < MAX_LDRIVES; ++i) {
+ if (!ha->hdr[i].is_logdrv ||
+ (ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master))
+ continue;
+ /* 5.a get host drive list */
+ TRACE2(("host_get() drv_no %d\n",i));
+ phg = (gdth_hget_str *)buf;
+ gdtcmd->Service = CACHESERVICE;
+ gdtcmd->OpCode = GDT_IOCTL;
+ gdtcmd->u.ioctl.p_param = paddr;
+ gdtcmd->u.ioctl.param_size = sizeof(gdth_hget_str);
+ gdtcmd->u.ioctl.subfunc = HOST_GET | LA_CTRL_PATTERN;
+ gdtcmd->u.ioctl.channel = i;
+ phg->entries = MAX_HDRIVES;
+ phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ gdth_do_req(scp, gdtcmd, cmnd, 30);
+ if (scp->sr_command->SCp.Status != S_OK)
+#else
+ gdth_do_cmd(scp, gdtcmd, cmnd, 30);
+ if (scp->SCp.Status != S_OK)
+#endif
+ {
+ ha->hdr[i].ldr_no = i;
+ ha->hdr[i].rw_attribs = 0;
+ ha->hdr[i].start_sec = 0;
+ } else {
+ for (j = 0; j < phg->entries; ++j) {
+ k = phg->entry[j].host_drive;
+ if (k >= MAX_LDRIVES)
+ continue;
+ ha->hdr[k].ldr_no = phg->entry[j].log_drive;
+ ha->hdr[k].rw_attribs = phg->entry[j].rw_attribs;
+ ha->hdr[k].start_sec = phg->entry[j].start_sec;
+ }
+ }
+ }
+ gdth_ioctl_free(hanum, sizeof(gdth_hget_str), buf, paddr);
+
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (!(ha->hdr[i].present))
+ continue;
+
+ size = sprintf(buffer+len,
+ "\n Number: \t%-2d \tArr/Log. Drive:\t%d\n",
+ i, ha->hdr[i].ldr_no);
+ len += size; pos = begin + len;
+ flag = TRUE;
+
+ size = sprintf(buffer+len,
+ " Capacity [MB]:\t%-6d \tStart Sector: \t%d\n",
+ (ulong32)(ha->hdr[i].size/2048), ha->hdr[i].start_sec);
+ len += size; pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
+ }
+
+ /* controller events */
+ size = sprintf(buffer+len,"\nController Events:\n");
+ len += size; pos = begin + len;
+
+ for (id = -1;;) {
+ id = gdth_read_event(ha, id, estr);
+ if (estr->event_source == 0)
+ break;
+ if (estr->event_data.eu.driver.ionode == hanum &&
+ estr->event_source == ES_ASYNC) {
+ gdth_log_event(&estr->event_data, hrec);
+ do_gettimeofday(&tv);
+ sec = (int)(tv.tv_sec - estr->first_stamp);
+ if (sec < 0) sec = 0;
+ size = sprintf(buffer+len," date- %02d:%02d:%02d\t%s\n",
+ sec/3600, sec%3600/60, sec%60, hrec);
+ len += size; pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ if (id == -1)
+ break;
+ }
+
+stop_output:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scsi_release_request(scp);
+ scsi_free_host_dev(sdev);
+#else
+ scsi_release_command(scp);
+ scsi_free_host_dev(sdev);
+#endif
+ *start = buffer +(offset-begin);
+ len -= (offset-begin);
+ if (len > length)
+ len = length;
+ TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
+ len,(int)pos,(int)begin,(int)offset,length,size));
+ rc = len;
+
+free_fail:
+ kfree(gdtcmd);
+ kfree(estr);
+ return rc;
+}
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static void gdth_do_req(Scsi_Request *scp, gdth_cmd_str *gdtcmd,
+ char *cmnd, int timeout)
+{
+ unsigned bufflen;
+ DECLARE_COMPLETION(wait);
+
+ TRACE2(("gdth_do_req()\n"));
+ if (gdtcmd != NULL) {
+ bufflen = sizeof(gdth_cmd_str);
+ } else {
+ bufflen = 0;
+ }
+ scp->sr_request->rq_status = RQ_SCSI_BUSY;
+ scp->sr_request->waiting = &wait;
+ scsi_do_req(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1);
+ wait_for_completion(&wait);
+}
+
+#else
+static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *gdtcmd,
+ char *cmnd, int timeout)
+{
+ unsigned bufflen;
+ DECLARE_COMPLETION(wait);
+
+ TRACE2(("gdth_do_cmd()\n"));
+ if (gdtcmd != NULL) {
+ scp->SCp.this_residual = IOCTL_PRI;
+ bufflen = sizeof(gdth_cmd_str);
+ } else {
+ scp->SCp.this_residual = DEFAULT_PRI;
+ bufflen = 0;
+ }
+
+ scp->request.rq_status = RQ_SCSI_BUSY;
+ scp->request.waiting = &wait;
+ scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1);
+ wait_for_completion(&wait);
+}
+#endif
+
+void gdth_scsi_done(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_scsi_done()\n"));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ scp->request->rq_status = RQ_SCSI_DONE;
+ if (scp->request->waiting != NULL)
+ complete(scp->request->waiting);
+#else
+ scp->request.rq_status = RQ_SCSI_DONE;
+ if (scp->request.waiting != NULL)
+ complete(scp->request.waiting);
+#endif
+}
+
+static char *gdth_ioctl_alloc(int hanum, int size, int scratch,
+ ulong64 *paddr)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ char *ret_val;
+
+ if (size == 0)
+ return NULL;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ if (!ha->scratch_busy && size <= GDTH_SCRATCH) {
+ ha->scratch_busy = TRUE;
+ ret_val = ha->pscratch;
+ *paddr = ha->scratch_phys;
+ } else if (scratch) {
+ ret_val = NULL;
+ } else {
+ dma_addr_t dma_addr;
+
+ ret_val = pci_alloc_consistent(ha->pdev, size, &dma_addr);
+ *paddr = dma_addr;
+ }
+
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ return ret_val;
+}
+
+static void gdth_ioctl_free(int hanum, int size, char *buf, ulong64 paddr)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ if (buf == ha->pscratch) {
+ ha->scratch_busy = FALSE;
+ } else {
+ pci_free_consistent(ha->pdev, size, buf, paddr);
+ }
+
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+
+#ifdef GDTH_IOCTL_PROC
+static int gdth_ioctl_check_bin(int hanum, ushort size)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ int ret_val;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ ret_val = FALSE;
+ if (ha->scratch_busy) {
+ if (((gdth_iord_str *)ha->pscratch)->size == (ulong32)size)
+ ret_val = TRUE;
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ return ret_val;
+}
+#endif
+
+static void gdth_wait_completion(int hanum, int busnum, int id)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ int i;
+ Scsi_Cmnd *scp;
+ unchar b, t;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ for (i = 0; i < GDTH_MAXCMDS; ++i) {
+ scp = ha->cmd_tab[i].cmnd;
+
+ b = virt_ctr ? NUMDATA(scp->device->host)->busnum : scp->device->channel;
+ t = scp->device->id;
+ if (!SPECIAL_SCP(scp) && t == (unchar)id &&
+ b == (unchar)busnum) {
+ scp->SCp.have_data_in = 0;
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+ while (!scp->SCp.have_data_in)
+ barrier();
+ spin_lock_irqsave(&ha->smp_lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+
+static void gdth_stop_timeout(int hanum, int busnum, int id)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ Scsi_Cmnd *scp;
+ unchar b, t;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+ b = virt_ctr ? NUMDATA(scp->device->host)->busnum : scp->device->channel;
+ t = scp->device->id;
+ if (t == (unchar)id && b == (unchar)busnum) {
+ TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
+ }
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+
+static void gdth_start_timeout(int hanum, int busnum, int id)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ Scsi_Cmnd *scp;
+ unchar b, t;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ spin_lock_irqsave(&ha->smp_lock, flags);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+ b = virt_ctr ? NUMDATA(scp->device->host)->busnum : scp->device->channel;
+ t = scp->device->id;
+ if (t == (unchar)id && b == (unchar)busnum) {
+ TRACE2(("gdth_start_timeout(): update_timeout()\n"));
+ gdth_update_timeout(hanum, scp, scp->SCp.buffers_residual);
+ }
+ }
+ spin_unlock_irqrestore(&ha->smp_lock, flags);
+}
+
+static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout)
+{
+ int oldto;
+
+ oldto = scp->timeout_per_command;
+ scp->timeout_per_command = timeout;
+
+ if (timeout == 0) {
+ del_timer(&scp->eh_timeout);
+ scp->eh_timeout.data = (unsigned long) NULL;
+ scp->eh_timeout.expires = 0;
+ } else {
+ if (scp->eh_timeout.data != (unsigned long) NULL)
+ del_timer(&scp->eh_timeout);
+ scp->eh_timeout.data = (unsigned long) scp;
+ scp->eh_timeout.expires = jiffies + timeout;
+ add_timer(&scp->eh_timeout);
+ }
+
+ return oldto;
+}
diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
new file mode 100644
index 000000000000..295e825e2c60
--- /dev/null
+++ b/drivers/scsi/gdth_proc.h
@@ -0,0 +1,34 @@
+#ifndef _GDTH_PROC_H
+#define _GDTH_PROC_H
+
+/* gdth_proc.h
+ * $Id: gdth_proc.h,v 1.16 2004/01/14 13:09:01 achim Exp $
+ */
+
+static int gdth_set_info(char *buffer,int length,struct Scsi_Host *host,
+ int hanum,int busnum);
+static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
+ struct Scsi_Host *host,int hanum,int busnum);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static void gdth_do_req(Scsi_Request *srp, gdth_cmd_str *cmd,
+ char *cmnd, int timeout);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Request *scp);
+#else
+static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd,
+ char *cmnd, int timeout);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp);
+#endif
+
+static char *gdth_ioctl_alloc(int hanum, int size, int scratch,
+ ulong64 *paddr);
+static void gdth_ioctl_free(int hanum, int size, char *buf, ulong64 paddr);
+static void gdth_wait_completion(int hanum, int busnum, int id);
+static void gdth_stop_timeout(int hanum, int busnum, int id);
+static void gdth_start_timeout(int hanum, int busnum, int id);
+static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout);
+
+void gdth_scsi_done(Scsi_Cmnd *scp);
+
+#endif
+
diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c
new file mode 100644
index 000000000000..30cbf73c7433
--- /dev/null
+++ b/drivers/scsi/gvp11.c
@@ -0,0 +1,387 @@
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <linux/spinlock.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "wd33c93.h"
+#include "gvp11.h"
+
+#include<linux/stat.h>
+
+#define DMA(ptr) ((gvp11_scsiregs *)((ptr)->base))
+#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata))
+
+static irqreturn_t gvp11_intr (int irq, void *_instance, struct pt_regs *fp)
+{
+ unsigned long flags;
+ unsigned int status;
+ struct Scsi_Host *instance = (struct Scsi_Host *)_instance;
+
+ status = DMA(instance)->CNTR;
+ if (!(status & GVP11_DMAC_INT_PENDING))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(instance->host_lock, flags);
+ wd33c93_intr(instance);
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int gvp11_xfer_mask = 0;
+
+void gvp11_setup (char *str, int *ints)
+{
+ gvp11_xfer_mask = ints[1];
+}
+
+static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
+{
+ unsigned short cntr = GVP11_DMAC_INT_ENABLE;
+ unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+ int bank_mask;
+ static int scsi_alloc_out_of_range = 0;
+
+ /* use bounce buffer if the physical address is bad */
+ if (addr & HDATA(cmd->device->host)->dma_xfer_mask ||
+ (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual)))
+ {
+ HDATA(cmd->device->host)->dma_bounce_len = (cmd->SCp.this_residual + 511)
+ & ~0x1ff;
+
+ if( !scsi_alloc_out_of_range ) {
+ HDATA(cmd->device->host)->dma_bounce_buffer =
+ kmalloc (HDATA(cmd->device->host)->dma_bounce_len, GFP_KERNEL);
+ HDATA(cmd->device->host)->dma_buffer_pool = BUF_SCSI_ALLOCED;
+ }
+
+ if (scsi_alloc_out_of_range ||
+ !HDATA(cmd->device->host)->dma_bounce_buffer) {
+ HDATA(cmd->device->host)->dma_bounce_buffer =
+ amiga_chip_alloc(HDATA(cmd->device->host)->dma_bounce_len,
+ "GVP II SCSI Bounce Buffer");
+
+ if(!HDATA(cmd->device->host)->dma_bounce_buffer)
+ {
+ HDATA(cmd->device->host)->dma_bounce_len = 0;
+ return 1;
+ }
+
+ HDATA(cmd->device->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
+ }
+
+ /* check if the address of the bounce buffer is OK */
+ addr = virt_to_bus(HDATA(cmd->device->host)->dma_bounce_buffer);
+
+ if (addr & HDATA(cmd->device->host)->dma_xfer_mask) {
+ /* fall back to Chip RAM if address out of range */
+ if( HDATA(cmd->device->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) {
+ kfree (HDATA(cmd->device->host)->dma_bounce_buffer);
+ scsi_alloc_out_of_range = 1;
+ } else {
+ amiga_chip_free (HDATA(cmd->device->host)->dma_bounce_buffer);
+ }
+
+ HDATA(cmd->device->host)->dma_bounce_buffer =
+ amiga_chip_alloc(HDATA(cmd->device->host)->dma_bounce_len,
+ "GVP II SCSI Bounce Buffer");
+
+ if(!HDATA(cmd->device->host)->dma_bounce_buffer)
+ {
+ HDATA(cmd->device->host)->dma_bounce_len = 0;
+ return 1;
+ }
+
+ addr = virt_to_bus(HDATA(cmd->device->host)->dma_bounce_buffer);
+ HDATA(cmd->device->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
+ }
+
+ if (!dir_in) {
+ /* copy to bounce buffer for a write */
+ memcpy (HDATA(cmd->device->host)->dma_bounce_buffer,
+ cmd->SCp.ptr, cmd->SCp.this_residual);
+ }
+ }
+
+ /* setup dma direction */
+ if (!dir_in)
+ cntr |= GVP11_DMAC_DIR_WRITE;
+
+ HDATA(cmd->device->host)->dma_dir = dir_in;
+ DMA(cmd->device->host)->CNTR = cntr;
+
+ /* setup DMA *physical* address */
+ DMA(cmd->device->host)->ACR = addr;
+
+ if (dir_in)
+ /* invalidate any cache */
+ cache_clear (addr, cmd->SCp.this_residual);
+ else
+ /* push any dirty cache */
+ cache_push (addr, cmd->SCp.this_residual);
+
+ if ((bank_mask = (~HDATA(cmd->device->host)->dma_xfer_mask >> 18) & 0x01c0))
+ DMA(cmd->device->host)->BANK = bank_mask & (addr >> 18);
+
+ /* start DMA */
+ DMA(cmd->device->host)->ST_DMA = 1;
+
+ /* return success */
+ return 0;
+}
+
+static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt,
+ int status)
+{
+ /* stop DMA */
+ DMA(instance)->SP_DMA = 1;
+ /* remove write bit from CONTROL bits */
+ DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE;
+
+ /* copy from a bounce buffer, if necessary */
+ if (status && HDATA(instance)->dma_bounce_buffer) {
+ if (HDATA(instance)->dma_dir && SCpnt)
+ memcpy (SCpnt->SCp.ptr,
+ HDATA(instance)->dma_bounce_buffer,
+ SCpnt->SCp.this_residual);
+
+ if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
+ kfree (HDATA(instance)->dma_bounce_buffer);
+ else
+ amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
+
+ HDATA(instance)->dma_bounce_buffer = NULL;
+ HDATA(instance)->dma_bounce_len = 0;
+ }
+}
+
+#define CHECK_WD33C93
+
+int __init gvp11_detect(Scsi_Host_Template *tpnt)
+{
+ static unsigned char called = 0;
+ struct Scsi_Host *instance;
+ unsigned long address;
+ unsigned int epc;
+ struct zorro_dev *z = NULL;
+ unsigned int default_dma_xfer_mask;
+ wd33c93_regs regs;
+ int num_gvp11 = 0;
+#ifdef CHECK_WD33C93
+ volatile unsigned char *sasr_3393, *scmd_3393;
+ unsigned char save_sasr;
+ unsigned char q, qq;
+#endif
+
+ if (!MACH_IS_AMIGA || called)
+ return 0;
+ called = 1;
+
+ tpnt->proc_name = "GVP11";
+ tpnt->proc_info = &wd33c93_proc_info;
+
+ while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
+ /*
+ * This should (hopefully) be the correct way to identify
+ * all the different GVP SCSI controllers (except for the
+ * SERIES I though).
+ */
+
+ if (z->id == ZORRO_PROD_GVP_COMBO_030_R3_SCSI ||
+ z->id == ZORRO_PROD_GVP_SERIES_II)
+ default_dma_xfer_mask = ~0x00ffffff;
+ else if (z->id == ZORRO_PROD_GVP_GFORCE_030_SCSI ||
+ z->id == ZORRO_PROD_GVP_A530_SCSI ||
+ z->id == ZORRO_PROD_GVP_COMBO_030_R4_SCSI)
+ default_dma_xfer_mask = ~0x01ffffff;
+ else if (z->id == ZORRO_PROD_GVP_A1291 ||
+ z->id == ZORRO_PROD_GVP_GFORCE_040_SCSI_1)
+ default_dma_xfer_mask = ~0x07ffffff;
+ else
+ continue;
+
+ /*
+ * Rumors state that some GVP ram boards use the same product
+ * code as the SCSI controllers. Therefore if the board-size
+ * is not 64KB we asume it is a ram board and bail out.
+ */
+ if (z->resource.end-z->resource.start != 0xffff)
+ continue;
+
+ address = z->resource.start;
+ if (!request_mem_region(address, 256, "wd33c93"))
+ continue;
+
+#ifdef CHECK_WD33C93
+
+ /*
+ * These darn GVP boards are a problem - it can be tough to tell
+ * whether or not they include a SCSI controller. This is the
+ * ultimate Yet-Another-GVP-Detection-Hack in that it actually
+ * probes for a WD33c93 chip: If we find one, it's extremely
+ * likely that this card supports SCSI, regardless of Product_
+ * Code, Board_Size, etc.
+ */
+
+ /* Get pointers to the presumed register locations and save contents */
+
+ sasr_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SASR);
+ scmd_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SCMD);
+ save_sasr = *sasr_3393;
+
+ /* First test the AuxStatus Reg */
+
+ q = *sasr_3393; /* read it */
+ if (q & 0x08) /* bit 3 should always be clear */
+ goto release;
+ *sasr_3393 = WD_AUXILIARY_STATUS; /* setup indirect address */
+ if (*sasr_3393 == WD_AUXILIARY_STATUS) { /* shouldn't retain the write */
+ *sasr_3393 = save_sasr; /* Oops - restore this byte */
+ goto release;
+ }
+ if (*sasr_3393 != q) { /* should still read the same */
+ *sasr_3393 = save_sasr; /* Oops - restore this byte */
+ goto release;
+ }
+ if (*scmd_3393 != q) /* and so should the image at 0x1f */
+ goto release;
+
+
+ /* Ok, we probably have a wd33c93, but let's check a few other places
+ * for good measure. Make sure that this works for both 'A and 'B
+ * chip versions.
+ */
+
+ *sasr_3393 = WD_SCSI_STATUS;
+ q = *scmd_3393;
+ *sasr_3393 = WD_SCSI_STATUS;
+ *scmd_3393 = ~q;
+ *sasr_3393 = WD_SCSI_STATUS;
+ qq = *scmd_3393;
+ *sasr_3393 = WD_SCSI_STATUS;
+ *scmd_3393 = q;
+ if (qq != q) /* should be read only */
+ goto release;
+ *sasr_3393 = 0x1e; /* this register is unimplemented */
+ q = *scmd_3393;
+ *sasr_3393 = 0x1e;
+ *scmd_3393 = ~q;
+ *sasr_3393 = 0x1e;
+ qq = *scmd_3393;
+ *sasr_3393 = 0x1e;
+ *scmd_3393 = q;
+ if (qq != q || qq != 0xff) /* should be read only, all 1's */
+ goto release;
+ *sasr_3393 = WD_TIMEOUT_PERIOD;
+ q = *scmd_3393;
+ *sasr_3393 = WD_TIMEOUT_PERIOD;
+ *scmd_3393 = ~q;
+ *sasr_3393 = WD_TIMEOUT_PERIOD;
+ qq = *scmd_3393;
+ *sasr_3393 = WD_TIMEOUT_PERIOD;
+ *scmd_3393 = q;
+ if (qq != (~q & 0xff)) /* should be read/write */
+ goto release;
+#endif
+
+ instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata));
+ if(instance == NULL)
+ goto release;
+ instance->base = ZTWO_VADDR(address);
+ instance->irq = IRQ_AMIGA_PORTS;
+ instance->unique_id = z->slotaddr;
+
+ if (gvp11_xfer_mask)
+ HDATA(instance)->dma_xfer_mask = gvp11_xfer_mask;
+ else
+ HDATA(instance)->dma_xfer_mask = default_dma_xfer_mask;
+
+
+ DMA(instance)->secret2 = 1;
+ DMA(instance)->secret1 = 0;
+ DMA(instance)->secret3 = 15;
+ while (DMA(instance)->CNTR & GVP11_DMAC_BUSY) ;
+ DMA(instance)->CNTR = 0;
+
+ DMA(instance)->BANK = 0;
+
+ epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000);
+
+ /*
+ * Check for 14MHz SCSI clock
+ */
+ regs.SASR = &(DMA(instance)->SASR);
+ regs.SCMD = &(DMA(instance)->SCMD);
+ wd33c93_init(instance, regs, dma_setup, dma_stop,
+ (epc & GVP_SCSICLKMASK) ? WD33C93_FS_8_10
+ : WD33C93_FS_12_15);
+
+ request_irq(IRQ_AMIGA_PORTS, gvp11_intr, SA_SHIRQ, "GVP11 SCSI",
+ instance);
+ DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE;
+ num_gvp11++;
+ continue;
+
+release:
+ release_mem_region(address, 256);
+ }
+
+ return num_gvp11;
+}
+
+static int gvp11_bus_reset(Scsi_Cmnd *cmd)
+{
+ /* FIXME perform bus-specific reset */
+ wd33c93_host_reset(cmd);
+ return SUCCESS;
+}
+
+
+#define HOSTS_C
+
+#include "gvp11.h"
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "GVP11",
+ .name = "GVP Series II SCSI",
+ .detect = gvp11_detect,
+ .release = gvp11_release,
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_bus_reset_handler = gvp11_bus_reset,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+int gvp11_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ DMA(instance)->CNTR = 0;
+ release_mem_region(ZTWO_PADDR(instance->base), 256);
+ free_irq(IRQ_AMIGA_PORTS, instance);
+ wd33c93_release();
+#endif
+ return 1;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/gvp11.h b/drivers/scsi/gvp11.h
new file mode 100644
index 000000000000..5148d9fada19
--- /dev/null
+++ b/drivers/scsi/gvp11.h
@@ -0,0 +1,63 @@
+#ifndef GVP11_H
+
+/* $Id: gvp11.h,v 1.4 1997/01/19 23:07:12 davem Exp $
+ *
+ * Header file for the GVP Series II SCSI controller for Linux
+ *
+ * Written and (C) 1993, Ralf Baechle, see gvp11.c for more info
+ * based on a2091.h (C) 1993 by Hamish Macdonald
+ *
+ */
+
+#include <linux/types.h>
+
+int gvp11_detect(Scsi_Host_Template *);
+int gvp11_release(struct Scsi_Host *);
+const char *wd33c93_info(void);
+int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd33c93_abort(Scsi_Cmnd *);
+int wd33c93_reset(Scsi_Cmnd *, unsigned int);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifndef HOSTS_C
+
+/*
+ * if the transfer address ANDed with this results in a non-zero
+ * result, then we can't use DMA.
+ */
+#define GVP11_XFER_MASK (0xff000001)
+
+typedef struct {
+ unsigned char pad1[64];
+ volatile unsigned short CNTR;
+ unsigned char pad2[31];
+ volatile unsigned char SASR;
+ unsigned char pad3;
+ volatile unsigned char SCMD;
+ unsigned char pad4[4];
+ volatile unsigned short BANK;
+ unsigned char pad5[6];
+ volatile unsigned long ACR;
+ volatile unsigned short secret1; /* store 0 here */
+ volatile unsigned short ST_DMA;
+ volatile unsigned short SP_DMA;
+ volatile unsigned short secret2; /* store 1 here */
+ volatile unsigned short secret3; /* store 15 here */
+} gvp11_scsiregs;
+
+/* bits in CNTR */
+#define GVP11_DMAC_BUSY (1<<0)
+#define GVP11_DMAC_INT_PENDING (1<<1)
+#define GVP11_DMAC_INT_ENABLE (1<<3)
+#define GVP11_DMAC_DIR_WRITE (1<<4)
+
+#endif /* else def HOSTS_C */
+
+#endif /* GVP11_H */
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
new file mode 100644
index 000000000000..ba347576d99b
--- /dev/null
+++ b/drivers/scsi/hosts.c
@@ -0,0 +1,462 @@
+/*
+ * hosts.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ * Copyright (C) 2002-2003 Christoph Hellwig
+ *
+ * mid to lowlevel SCSI driver interface
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
+ * Added QLOGIC QLA1280 SCSI controller kernel host support.
+ * August 4, 1999 Fred Lewis, Intel DuPont
+ *
+ * Updated to reflect the new initialization scheme for the higher
+ * level of scsi drivers (sd/sr/st)
+ * September 17, 2000 Torben Mathiasen <tmm@image.dk>
+ *
+ * Restructured scsi_host lists and associated functions.
+ * September 04, 2002 Mike Anderson (andmike@us.ibm.com)
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/transport_class.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+
+static int scsi_host_next_hn; /* host_no for next new host */
+
+
+static void scsi_host_cls_release(struct class_device *class_dev)
+{
+ put_device(&class_to_shost(class_dev)->shost_gendev);
+}
+
+static struct class shost_class = {
+ .name = "scsi_host",
+ .release = scsi_host_cls_release,
+};
+
+/**
+ * scsi_host_cancel - cancel outstanding IO to this host
+ * @shost: pointer to struct Scsi_Host
+ * recovery: recovery requested to run.
+ **/
+void scsi_host_cancel(struct Scsi_Host *shost, int recovery)
+{
+ struct scsi_device *sdev;
+
+ set_bit(SHOST_CANCEL, &shost->shost_state);
+ shost_for_each_device(sdev, shost) {
+ scsi_device_cancel(sdev, recovery);
+ }
+ wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY,
+ &shost->shost_state)));
+}
+
+/**
+ * scsi_remove_host - remove a scsi host
+ * @shost: a pointer to a scsi host to remove
+ **/
+void scsi_remove_host(struct Scsi_Host *shost)
+{
+ scsi_forget_host(shost);
+ scsi_host_cancel(shost, 0);
+ scsi_proc_host_rm(shost);
+
+ set_bit(SHOST_DEL, &shost->shost_state);
+
+ transport_unregister_device(&shost->shost_gendev);
+ class_device_unregister(&shost->shost_classdev);
+ device_del(&shost->shost_gendev);
+}
+EXPORT_SYMBOL(scsi_remove_host);
+
+/**
+ * scsi_add_host - add a scsi host
+ * @shost: scsi host pointer to add
+ * @dev: a struct device of type scsi class
+ *
+ * Return value:
+ * 0 on success / != 0 for error
+ **/
+int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
+{
+ struct scsi_host_template *sht = shost->hostt;
+ int error = -EINVAL;
+
+ printk(KERN_INFO "scsi%d : %s\n", shost->host_no,
+ sht->info ? sht->info(shost) : sht->name);
+
+ if (!shost->can_queue) {
+ printk(KERN_ERR "%s: can_queue = 0 no longer supported\n",
+ sht->name);
+ goto out;
+ }
+
+ if (!shost->shost_gendev.parent)
+ shost->shost_gendev.parent = dev ? dev : &platform_bus;
+
+ error = device_add(&shost->shost_gendev);
+ if (error)
+ goto out;
+
+ set_bit(SHOST_ADD, &shost->shost_state);
+ get_device(shost->shost_gendev.parent);
+
+ error = class_device_add(&shost->shost_classdev);
+ if (error)
+ goto out_del_gendev;
+
+ get_device(&shost->shost_gendev);
+
+ if (shost->transportt->host_size &&
+ (shost->shost_data = kmalloc(shost->transportt->host_size,
+ GFP_KERNEL)) == NULL)
+ goto out_del_classdev;
+
+ if (shost->transportt->create_work_queue) {
+ snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
+ shost->host_no);
+ shost->work_q = create_singlethread_workqueue(
+ shost->work_q_name);
+ if (!shost->work_q)
+ goto out_free_shost_data;
+ }
+
+ error = scsi_sysfs_add_host(shost);
+ if (error)
+ goto out_destroy_host;
+
+ scsi_proc_host_add(shost);
+ return error;
+
+ out_destroy_host:
+ if (shost->work_q)
+ destroy_workqueue(shost->work_q);
+ out_free_shost_data:
+ kfree(shost->shost_data);
+ out_del_classdev:
+ class_device_del(&shost->shost_classdev);
+ out_del_gendev:
+ device_del(&shost->shost_gendev);
+ out:
+ return error;
+}
+EXPORT_SYMBOL(scsi_add_host);
+
+static void scsi_host_dev_release(struct device *dev)
+{
+ struct Scsi_Host *shost = dev_to_shost(dev);
+ struct device *parent = dev->parent;
+
+ if (shost->ehandler) {
+ DECLARE_COMPLETION(sem);
+ shost->eh_notify = &sem;
+ shost->eh_kill = 1;
+ up(shost->eh_wait);
+ wait_for_completion(&sem);
+ shost->eh_notify = NULL;
+ }
+
+ if (shost->work_q)
+ destroy_workqueue(shost->work_q);
+
+ scsi_proc_hostdir_rm(shost->hostt);
+ scsi_destroy_command_freelist(shost);
+ kfree(shost->shost_data);
+
+ /*
+ * Some drivers (eg aha1542) do scsi_register()/scsi_unregister()
+ * during probing without performing a scsi_set_device() in between.
+ * In this case dev->parent is NULL.
+ */
+ if (parent)
+ put_device(parent);
+ kfree(shost);
+}
+
+/**
+ * scsi_host_alloc - register a scsi host adapter instance.
+ * @sht: pointer to scsi host template
+ * @privsize: extra bytes to allocate for driver
+ *
+ * Note:
+ * Allocate a new Scsi_Host and perform basic initialization.
+ * The host is not published to the scsi midlayer until scsi_add_host
+ * is called.
+ *
+ * Return value:
+ * Pointer to a new Scsi_Host
+ **/
+struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
+{
+ struct Scsi_Host *shost;
+ int gfp_mask = GFP_KERNEL, rval;
+ DECLARE_COMPLETION(complete);
+
+ if (sht->unchecked_isa_dma && privsize)
+ gfp_mask |= __GFP_DMA;
+
+ /* Check to see if this host has any error handling facilities */
+ if (!sht->eh_strategy_handler && !sht->eh_abort_handler &&
+ !sht->eh_device_reset_handler && !sht->eh_bus_reset_handler &&
+ !sht->eh_host_reset_handler) {
+ printk(KERN_ERR "ERROR: SCSI host `%s' has no error handling\n"
+ "ERROR: This is not a safe way to run your "
+ "SCSI host\n"
+ "ERROR: The error handling must be added to "
+ "this driver\n", sht->proc_name);
+ dump_stack();
+ }
+
+ shost = kmalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask);
+ if (!shost)
+ return NULL;
+ memset(shost, 0, sizeof(struct Scsi_Host) + privsize);
+
+ spin_lock_init(&shost->default_lock);
+ scsi_assign_lock(shost, &shost->default_lock);
+ INIT_LIST_HEAD(&shost->__devices);
+ INIT_LIST_HEAD(&shost->__targets);
+ INIT_LIST_HEAD(&shost->eh_cmd_q);
+ INIT_LIST_HEAD(&shost->starved_list);
+ init_waitqueue_head(&shost->host_wait);
+
+ init_MUTEX(&shost->scan_mutex);
+
+ shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */
+ shost->dma_channel = 0xff;
+
+ /* These three are default values which can be overridden */
+ shost->max_channel = 0;
+ shost->max_id = 8;
+ shost->max_lun = 8;
+
+ /* Give each shost a default transportt */
+ shost->transportt = &blank_transport_template;
+
+ /*
+ * All drivers right now should be able to handle 12 byte
+ * commands. Every so often there are requests for 16 byte
+ * commands, but individual low-level drivers need to certify that
+ * they actually do something sensible with such commands.
+ */
+ shost->max_cmd_len = 12;
+ shost->hostt = sht;
+ shost->this_id = sht->this_id;
+ shost->can_queue = sht->can_queue;
+ shost->sg_tablesize = sht->sg_tablesize;
+ shost->cmd_per_lun = sht->cmd_per_lun;
+ shost->unchecked_isa_dma = sht->unchecked_isa_dma;
+ shost->use_clustering = sht->use_clustering;
+ shost->ordered_flush = sht->ordered_flush;
+ shost->ordered_tag = sht->ordered_tag;
+
+ /*
+ * hosts/devices that do queueing must support ordered tags
+ */
+ if (shost->can_queue > 1 && shost->ordered_flush) {
+ printk(KERN_ERR "scsi: ordered flushes don't support queueing\n");
+ shost->ordered_flush = 0;
+ }
+
+ if (sht->max_host_blocked)
+ shost->max_host_blocked = sht->max_host_blocked;
+ else
+ shost->max_host_blocked = SCSI_DEFAULT_HOST_BLOCKED;
+
+ /*
+ * If the driver imposes no hard sector transfer limit, start at
+ * machine infinity initially.
+ */
+ if (sht->max_sectors)
+ shost->max_sectors = sht->max_sectors;
+ else
+ shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS;
+
+ /*
+ * assume a 4GB boundary, if not set
+ */
+ if (sht->dma_boundary)
+ shost->dma_boundary = sht->dma_boundary;
+ else
+ shost->dma_boundary = 0xffffffff;
+
+ rval = scsi_setup_command_freelist(shost);
+ if (rval)
+ goto fail_kfree;
+
+ device_initialize(&shost->shost_gendev);
+ snprintf(shost->shost_gendev.bus_id, BUS_ID_SIZE, "host%d",
+ shost->host_no);
+ shost->shost_gendev.release = scsi_host_dev_release;
+
+ class_device_initialize(&shost->shost_classdev);
+ shost->shost_classdev.dev = &shost->shost_gendev;
+ shost->shost_classdev.class = &shost_class;
+ snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d",
+ shost->host_no);
+
+ shost->eh_notify = &complete;
+ rval = kernel_thread(scsi_error_handler, shost, 0);
+ if (rval < 0)
+ goto fail_destroy_freelist;
+ wait_for_completion(&complete);
+ shost->eh_notify = NULL;
+
+ scsi_proc_hostdir_add(shost->hostt);
+ return shost;
+
+ fail_destroy_freelist:
+ scsi_destroy_command_freelist(shost);
+ fail_kfree:
+ kfree(shost);
+ return NULL;
+}
+EXPORT_SYMBOL(scsi_host_alloc);
+
+struct Scsi_Host *scsi_register(struct scsi_host_template *sht, int privsize)
+{
+ struct Scsi_Host *shost = scsi_host_alloc(sht, privsize);
+
+ if (!sht->detect) {
+ printk(KERN_WARNING "scsi_register() called on new-style "
+ "template for driver %s\n", sht->name);
+ dump_stack();
+ }
+
+ if (shost)
+ list_add_tail(&shost->sht_legacy_list, &sht->legacy_hosts);
+ return shost;
+}
+EXPORT_SYMBOL(scsi_register);
+
+void scsi_unregister(struct Scsi_Host *shost)
+{
+ list_del(&shost->sht_legacy_list);
+ scsi_host_put(shost);
+}
+EXPORT_SYMBOL(scsi_unregister);
+
+/**
+ * scsi_host_lookup - get a reference to a Scsi_Host by host no
+ *
+ * @hostnum: host number to locate
+ *
+ * Return value:
+ * A pointer to located Scsi_Host or NULL.
+ **/
+struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
+{
+ struct class *class = &shost_class;
+ struct class_device *cdev;
+ struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p;
+
+ down_read(&class->subsys.rwsem);
+ list_for_each_entry(cdev, &class->children, node) {
+ p = class_to_shost(cdev);
+ if (p->host_no == hostnum) {
+ shost = scsi_host_get(p);
+ break;
+ }
+ }
+ up_read(&class->subsys.rwsem);
+
+ return shost;
+}
+EXPORT_SYMBOL(scsi_host_lookup);
+
+/**
+ * scsi_host_get - inc a Scsi_Host ref count
+ * @shost: Pointer to Scsi_Host to inc.
+ **/
+struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
+{
+ if (test_bit(SHOST_DEL, &shost->shost_state) ||
+ !get_device(&shost->shost_gendev))
+ return NULL;
+ return shost;
+}
+EXPORT_SYMBOL(scsi_host_get);
+
+/**
+ * scsi_host_put - dec a Scsi_Host ref count
+ * @shost: Pointer to Scsi_Host to dec.
+ **/
+void scsi_host_put(struct Scsi_Host *shost)
+{
+ put_device(&shost->shost_gendev);
+}
+EXPORT_SYMBOL(scsi_host_put);
+
+int scsi_init_hosts(void)
+{
+ return class_register(&shost_class);
+}
+
+void scsi_exit_hosts(void)
+{
+ class_unregister(&shost_class);
+}
+
+int scsi_is_host_device(const struct device *dev)
+{
+ return dev->release == scsi_host_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_host_device);
+
+/**
+ * scsi_queue_work - Queue work to the Scsi_Host workqueue.
+ * @shost: Pointer to Scsi_Host.
+ * @work: Work to queue for execution.
+ *
+ * Return value:
+ * 0 on success / != 0 for error
+ **/
+int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)
+{
+ if (unlikely(!shost->work_q)) {
+ printk(KERN_ERR
+ "ERROR: Scsi host '%s' attempted to queue scsi-work, "
+ "when no workqueue created.\n", shost->hostt->name);
+ dump_stack();
+
+ return -EINVAL;
+ }
+
+ return queue_work(shost->work_q, work);
+}
+EXPORT_SYMBOL_GPL(scsi_queue_work);
+
+/**
+ * scsi_flush_work - Flush a Scsi_Host's workqueue.
+ * @shost: Pointer to Scsi_Host.
+ **/
+void scsi_flush_work(struct Scsi_Host *shost)
+{
+ if (!shost->work_q) {
+ printk(KERN_ERR
+ "ERROR: Scsi host '%s' attempted to flush scsi-work, "
+ "when no workqueue created.\n", shost->hostt->name);
+ dump_stack();
+ return;
+ }
+
+ flush_workqueue(shost->work_q);
+}
+EXPORT_SYMBOL_GPL(scsi_flush_work);
diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
new file mode 100644
index 000000000000..c27264bed5d4
--- /dev/null
+++ b/drivers/scsi/hosts.h
@@ -0,0 +1,2 @@
+#warning "This file is obsolete, please use <scsi/scsi_host.h> instead"
+#include <scsi/scsi_host.h>
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
new file mode 100644
index 000000000000..a3fdead9bce9
--- /dev/null
+++ b/drivers/scsi/ibmmca.c
@@ -0,0 +1,2491 @@
+/*
+ Low Level Linux Driver for the IBM Microchannel SCSI Subsystem for
+ Linux Kernel >= 2.4.0.
+ Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU
+ General Public License. Written by Martin Kolinek, December 1995.
+ Further development by: Chris Beauregard, Klaus Kudielka, Michael Lang
+ See the file Documentation/scsi/ibmmca.txt for a detailed description
+ of this driver, the commandline arguments and the history of its
+ development.
+ See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest
+ updates, info and ADF-files for adapters supported by this driver.
+
+ Alan Cox <alan@redhat.com>
+ Updated for Linux 2.5.45 to use the new error handler, cleaned up the
+ lock macros and did a few unavoidable locking tweaks, plus one locking
+ fix in the irq and completion path.
+
+ */
+
+#include <linux/config.h>
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45)
+#error "This driver works only with kernel 2.5.45 or higher!"
+#endif
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mca.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/mca-legacy.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "ibmmca.h"
+
+/* current version of this driver-source: */
+#define IBMMCA_SCSI_DRIVER_VERSION "4.0b-ac"
+
+/* driver configuration */
+#define IM_MAX_HOSTS 8 /* maximum number of host adapters */
+#define IM_RESET_DELAY 60 /* seconds allowed for a reset */
+
+/* driver debugging - #undef all for normal operation */
+/* if defined: count interrupts and ignore this special one: */
+#undef IM_DEBUG_TIMEOUT //50
+#define TIMEOUT_PUN 0
+#define TIMEOUT_LUN 0
+/* verbose interrupt: */
+#undef IM_DEBUG_INT
+/* verbose queuecommand: */
+#undef IM_DEBUG_CMD
+/* verbose queucommand for specific SCSI-device type: */
+#undef IM_DEBUG_CMD_SPEC_DEV
+/* verbose device probing */
+#undef IM_DEBUG_PROBE
+
+/* device type that shall be displayed on syslog (only during debugging): */
+#define IM_DEBUG_CMD_DEVICE TYPE_TAPE
+
+/* relative addresses of hardware registers on a subsystem */
+#define IM_CMD_REG(hi) (hosts[(hi)]->io_port) /*Command Interface, (4 bytes long) */
+#define IM_ATTN_REG(hi) (hosts[(hi)]->io_port+4) /*Attention (1 byte) */
+#define IM_CTR_REG(hi) (hosts[(hi)]->io_port+5) /*Basic Control (1 byte) */
+#define IM_INTR_REG(hi) (hosts[(hi)]->io_port+6) /*Interrupt Status (1 byte, r/o) */
+#define IM_STAT_REG(hi) (hosts[(hi)]->io_port+7) /*Basic Status (1 byte, read only) */
+
+/* basic I/O-port of first adapter */
+#define IM_IO_PORT 0x3540
+/* maximum number of hosts that can be found */
+#define IM_N_IO_PORT 8
+
+/*requests going into the upper nibble of the Attention register */
+/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
+#define IM_IMM_CMD 0x10 /*immediate command */
+#define IM_SCB 0x30 /*Subsystem Control Block command */
+#define IM_LONG_SCB 0x40 /*long Subsystem Control Block command */
+#define IM_EOI 0xe0 /*end-of-interrupt request */
+
+/*values for bits 7,1,0 of Basic Control reg. (bits 6-2 reserved) */
+#define IM_HW_RESET 0x80 /*hardware reset */
+#define IM_ENABLE_DMA 0x02 /*enable subsystem's busmaster DMA */
+#define IM_ENABLE_INTR 0x01 /*enable interrupts to the system */
+
+/*to interpret the upper nibble of Interrupt Status register */
+/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
+#define IM_SCB_CMD_COMPLETED 0x10
+#define IM_SCB_CMD_COMPLETED_WITH_RETRIES 0x50
+#define IM_LOOP_SCATTER_BUFFER_FULL 0x60
+#define IM_ADAPTER_HW_FAILURE 0x70
+#define IM_IMMEDIATE_CMD_COMPLETED 0xa0
+#define IM_CMD_COMPLETED_WITH_FAILURE 0xc0
+#define IM_CMD_ERROR 0xe0
+#define IM_SOFTWARE_SEQUENCING_ERROR 0xf0
+
+/*to interpret bits 3-0 of Basic Status register (bits 7-4 reserved) */
+#define IM_CMD_REG_FULL 0x08
+#define IM_CMD_REG_EMPTY 0x04
+#define IM_INTR_REQUEST 0x02
+#define IM_BUSY 0x01
+
+/*immediate commands (word written into low 2 bytes of command reg) */
+#define IM_RESET_IMM_CMD 0x0400
+#define IM_FEATURE_CTR_IMM_CMD 0x040c
+#define IM_DMA_PACING_IMM_CMD 0x040d
+#define IM_ASSIGN_IMM_CMD 0x040e
+#define IM_ABORT_IMM_CMD 0x040f
+#define IM_FORMAT_PREP_IMM_CMD 0x0417
+
+/*SCB (Subsystem Control Block) structure */
+struct im_scb {
+ unsigned short command; /*command word (read, etc.) */
+ unsigned short enable; /*enable word, modifies cmd */
+ union {
+ unsigned long log_blk_adr; /*block address on SCSI device */
+ unsigned char scsi_cmd_length; /*6,10,12, for other scsi cmd */
+ } u1;
+ unsigned long sys_buf_adr; /*physical system memory adr */
+ unsigned long sys_buf_length; /*size of sys mem buffer */
+ unsigned long tsb_adr; /*Termination Status Block adr */
+ unsigned long scb_chain_adr; /*optional SCB chain address */
+ union {
+ struct {
+ unsigned short count; /*block count, on SCSI device */
+ unsigned short length; /*block length, on SCSI device */
+ } blk;
+ unsigned char scsi_command[12]; /*other scsi command */
+ } u2;
+};
+
+/*structure scatter-gather element (for list of system memory areas) */
+struct im_sge {
+ void *address;
+ unsigned long byte_length;
+};
+
+/*structure returned by a get_pos_info command: */
+struct im_pos_info {
+ unsigned short pos_id; /* adapter id */
+ unsigned char pos_3a; /* pos 3 (if pos 6 = 0) */
+ unsigned char pos_2; /* pos 2 */
+ unsigned char int_level; /* interrupt level IRQ 11 or 14 */
+ unsigned char pos_4a; /* pos 4 (if pos 6 = 0) */
+ unsigned short connector_size; /* MCA connector size: 16 or 32 Bit */
+ unsigned char num_luns; /* number of supported luns per device */
+ unsigned char num_puns; /* number of supported puns */
+ unsigned char pacing_factor; /* pacing factor */
+ unsigned char num_ldns; /* number of ldns available */
+ unsigned char eoi_off; /* time EOI and interrupt inactive */
+ unsigned char max_busy; /* time between reset and busy on */
+ unsigned short cache_stat; /* ldn cachestat. Bit=1 = not cached */
+ unsigned short retry_stat; /* retry status of ldns. Bit=1=disabled */
+ unsigned char pos_4b; /* pos 4 (if pos 6 = 1) */
+ unsigned char pos_3b; /* pos 3 (if pos 6 = 1) */
+ unsigned char pos_6; /* pos 6 */
+ unsigned char pos_5; /* pos 5 */
+ unsigned short max_overlap; /* maximum overlapping requests */
+ unsigned short num_bus; /* number of SCSI-busses */
+};
+
+/*values for SCB command word */
+#define IM_NO_SYNCHRONOUS 0x0040 /*flag for any command */
+#define IM_NO_DISCONNECT 0x0080 /*flag for any command */
+#define IM_READ_DATA_CMD 0x1c01
+#define IM_WRITE_DATA_CMD 0x1c02
+#define IM_READ_VERIFY_CMD 0x1c03
+#define IM_WRITE_VERIFY_CMD 0x1c04
+#define IM_REQUEST_SENSE_CMD 0x1c08
+#define IM_READ_CAPACITY_CMD 0x1c09
+#define IM_DEVICE_INQUIRY_CMD 0x1c0b
+#define IM_READ_LOGICAL_CMD 0x1c2a
+#define IM_OTHER_SCSI_CMD_CMD 0x241f
+
+/* unused, but supported, SCB commands */
+#define IM_GET_COMMAND_COMPLETE_STATUS_CMD 0x1c07 /* command status */
+#define IM_GET_POS_INFO_CMD 0x1c0a /* returns neat stuff */
+#define IM_READ_PREFETCH_CMD 0x1c31 /* caching controller only */
+#define IM_FOMAT_UNIT_CMD 0x1c16 /* format unit */
+#define IM_REASSIGN_BLOCK_CMD 0x1c18 /* in case of error */
+
+/*values to set bits in the enable word of SCB */
+#define IM_READ_CONTROL 0x8000
+#define IM_REPORT_TSB_ONLY_ON_ERROR 0x4000
+#define IM_RETRY_ENABLE 0x2000
+#define IM_POINTER_TO_LIST 0x1000
+#define IM_SUPRESS_EXCEPTION_SHORT 0x0400
+#define IM_BYPASS_BUFFER 0x0200
+#define IM_CHAIN_ON_NO_ERROR 0x0001
+
+/*TSB (Termination Status Block) structure */
+struct im_tsb {
+ unsigned short end_status;
+ unsigned short reserved1;
+ unsigned long residual_byte_count;
+ unsigned long sg_list_element_adr;
+ unsigned short status_length;
+ unsigned char dev_status;
+ unsigned char cmd_status;
+ unsigned char dev_error;
+ unsigned char cmd_error;
+ unsigned short reserved2;
+ unsigned short reserved3;
+ unsigned short low_of_last_scb_adr;
+ unsigned short high_of_last_scb_adr;
+};
+
+/*subsystem uses interrupt request level 14 */
+#define IM_IRQ 14
+/*SCSI-2 F/W may evade to interrupt 11 */
+#define IM_IRQ_FW 11
+
+/* Model 95 has an additional alphanumeric display, which can be used
+ to display SCSI-activities. 8595 models do not have any disk led, which
+ makes this feature quite useful.
+ The regular PS/2 disk led is turned on/off by bits 6,7 of system
+ control port. */
+
+/* LED display-port (actually, last LED on display) */
+#define MOD95_LED_PORT 0x108
+/* system-control-register of PS/2s with diskindicator */
+#define PS2_SYS_CTR 0x92
+/* activity displaying methods */
+#define LED_DISP 1
+#define LED_ADISP 2
+#define LED_ACTIVITY 4
+/* failed intr */
+#define CMD_FAIL 255
+
+/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED
+ displays. ldn is no longer displayed here, because the ldn mapping is now
+ done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime
+ or during uptime in /proc/scsi/ibmmca/<host_no> in case of trouble,
+ interest, debugging or just for having fun. The left number gives the
+ host-adapter number and the right shows the accessed SCSI-ID. */
+
+/* display_mode is set by the ibmmcascsi= command line arg */
+static int display_mode = 0;
+/* set default adapter timeout */
+static unsigned int adapter_timeout = 45;
+/* for probing on feature-command: */
+static unsigned int global_command_error_excuse = 0;
+/* global setting by command line for adapter_speed */
+static int global_adapter_speed = 0; /* full speed by default */
+
+/* Panel / LED on, do it right for F/W addressin, too. adisplay will
+ * just ignore ids>7, as the panel has only 7 digits available */
+#define PS2_DISK_LED_ON(ad,id) { if (display_mode & LED_DISP) { if (id>9) \
+ outw((ad+48)|((id+55)<<8), MOD95_LED_PORT ); else \
+ outw((ad+48)|((id+48)<<8), MOD95_LED_PORT ); } else \
+ if (display_mode & LED_ADISP) { if (id<7) outb((char)(id+48),MOD95_LED_PORT+1+id); \
+ outb((char)(ad+48), MOD95_LED_PORT); } \
+ if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
+ outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); }
+
+/* Panel / LED off */
+/* bug fixed, Dec 15, 1997, where | was replaced by & here */
+#define PS2_DISK_LED_OFF() { if (display_mode & LED_DISP) \
+ outw(0x2020, MOD95_LED_PORT ); else if (display_mode & LED_ADISP) { \
+ outl(0x20202020,MOD95_LED_PORT); outl(0x20202020,MOD95_LED_PORT+4); } \
+ if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
+ outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); }
+
+/*list of supported subsystems */
+struct subsys_list_struct {
+ unsigned short mca_id;
+ char *description;
+};
+
+/* types of different supported hardware that goes to hostdata special */
+#define IBM_SCSI2_FW 0
+#define IBM_7568_WCACHE 1
+#define IBM_EXP_UNIT 2
+#define IBM_SCSI_WCACHE 3
+#define IBM_SCSI 4
+
+/* other special flags for hostdata structure */
+#define FORCED_DETECTION 100
+#define INTEGRATED_SCSI 101
+
+/* List of possible IBM-SCSI-adapters */
+static struct subsys_list_struct subsys_list[] = {
+ {0x8efc, "IBM SCSI-2 F/W Adapter"}, /* special = 0 */
+ {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/Cache"}, /* special = 1 */
+ {0x8ef8, "IBM Expansion Unit SCSI Controller"}, /* special = 2 */
+ {0x8eff, "IBM SCSI Adapter w/Cache"}, /* special = 3 */
+ {0x8efe, "IBM SCSI Adapter"}, /* special = 4 */
+};
+
+/* Max number of logical devices (can be up from 0 to 14). 15 is the address
+of the adapter itself. */
+#define MAX_LOG_DEV 15
+
+/*local data for a logical device */
+struct logical_device {
+ struct im_scb scb; /* SCSI-subsystem-control-block structure */
+ struct im_tsb tsb; /* SCSI command complete status block structure */
+ struct im_sge sge[16]; /* scatter gather list structure */
+ unsigned char buf[256]; /* SCSI command return data buffer */
+ Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */
+ int device_type; /* type of the SCSI-device. See include/scsi/scsi.h
+ for interpretation of the possible values */
+ int block_length; /* blocksize of a particular logical SCSI-device */
+ int cache_flag; /* 1 if this is uncached, 0 if cache is present for ldn */
+ int retry_flag; /* 1 if adapter retry is disabled, 0 if enabled */
+};
+
+/* statistics of the driver during operations (for proc_info) */
+struct Driver_Statistics {
+ /* SCSI statistics on the adapter */
+ int ldn_access[MAX_LOG_DEV + 1]; /* total accesses on a ldn */
+ int ldn_read_access[MAX_LOG_DEV + 1]; /* total read-access on a ldn */
+ int ldn_write_access[MAX_LOG_DEV + 1]; /* total write-access on a ldn */
+ int ldn_inquiry_access[MAX_LOG_DEV + 1]; /* total inquiries on a ldn */
+ int ldn_modeselect_access[MAX_LOG_DEV + 1]; /* total mode selects on ldn */
+ int scbs; /* short SCBs queued */
+ int long_scbs; /* long SCBs queued */
+ int total_accesses; /* total accesses on all ldns */
+ int total_interrupts; /* total interrupts (should be
+ same as total_accesses) */
+ int total_errors; /* command completed with error */
+ /* dynamical assignment statistics */
+ int total_scsi_devices; /* number of physical pun,lun */
+ int dyn_flag; /* flag showing dynamical mode */
+ int dynamical_assignments; /* number of remappings of ldns */
+ int ldn_assignments[MAX_LOG_DEV + 1]; /* number of remappings of each
+ ldn */
+};
+
+/* data structure for each host adapter */
+struct ibmmca_hostdata {
+ /* array of logical devices: */
+ struct logical_device _ld[MAX_LOG_DEV + 1];
+ /* array to convert (pun, lun) into logical device number: */
+ unsigned char _get_ldn[16][8];
+ /*array that contains the information about the physical SCSI-devices
+ attached to this host adapter: */
+ unsigned char _get_scsi[16][8];
+ /* used only when checking logical devices: */
+ int _local_checking_phase_flag;
+ /* report received interrupt: */
+ int _got_interrupt;
+ /* report termination-status of SCSI-command: */
+ int _stat_result;
+ /* reset status (used only when doing reset): */
+ int _reset_status;
+ /* code of the last SCSI command (needed for panic info): */
+ int _last_scsi_command[MAX_LOG_DEV + 1];
+ /* identifier of the last SCSI-command type */
+ int _last_scsi_type[MAX_LOG_DEV + 1];
+ /* last blockcount */
+ int _last_scsi_blockcount[MAX_LOG_DEV + 1];
+ /* last locgical block address */
+ unsigned long _last_scsi_logical_block[MAX_LOG_DEV + 1];
+ /* Counter that points on the next reassignable ldn for dynamical
+ remapping. The default value is 7, that is the first reassignable
+ number in the list at boottime: */
+ int _next_ldn;
+ /* Statistics-structure for this IBM-SCSI-host: */
+ struct Driver_Statistics _IBM_DS;
+ /* This hostadapters pos-registers pos2 until pos6 */
+ unsigned int _pos[8];
+ /* assign a special variable, that contains dedicated info about the
+ adaptertype */
+ int _special;
+ /* connector size on the MCA bus */
+ int _connector_size;
+ /* synchronous SCSI transfer rate bitpattern */
+ int _adapter_speed;
+};
+
+/* macros to access host data structure */
+#define subsystem_pun(hi) (hosts[(hi)]->this_id)
+#define subsystem_maxid(hi) (hosts[(hi)]->max_id)
+#define ld(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_ld)
+#define get_ldn(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_get_ldn)
+#define get_scsi(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_get_scsi)
+#define local_checking_phase_flag(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_local_checking_phase_flag)
+#define got_interrupt(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_got_interrupt)
+#define stat_result(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_stat_result)
+#define reset_status(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_reset_status)
+#define last_scsi_command(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_command)
+#define last_scsi_type(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_type)
+#define last_scsi_blockcount(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_blockcount)
+#define last_scsi_logical_block(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_logical_block)
+#define last_scsi_type(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_type)
+#define next_ldn(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_next_ldn)
+#define IBM_DS(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_IBM_DS)
+#define special(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_special)
+#define subsystem_connector_size(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_connector_size)
+#define adapter_speed(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_adapter_speed)
+#define pos2(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos[2])
+#define pos3(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos[3])
+#define pos4(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos[4])
+#define pos5(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos[5])
+#define pos6(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos[6])
+
+/* Define a arbitrary number as subsystem-marker-type. This number is, as
+ described in the ANSI-SCSI-standard, not occupied by other device-types. */
+#define TYPE_IBM_SCSI_ADAPTER 0x2F
+
+/* Define 0xFF for no device type, because this type is not defined within
+ the ANSI-SCSI-standard, therefore, it can be used and should not cause any
+ harm. */
+#define TYPE_NO_DEVICE 0xFF
+
+/* define medium-changer. If this is not defined previously, e.g. Linux
+ 2.0.x, define this type here. */
+#ifndef TYPE_MEDIUM_CHANGER
+#define TYPE_MEDIUM_CHANGER 0x08
+#endif
+
+/* define possible operations for the immediate_assign command */
+#define SET_LDN 0
+#define REMOVE_LDN 1
+
+/* ldn which is used to probe the SCSI devices */
+#define PROBE_LDN 0
+
+/* reset status flag contents */
+#define IM_RESET_NOT_IN_PROGRESS 0
+#define IM_RESET_IN_PROGRESS 1
+#define IM_RESET_FINISHED_OK 2
+#define IM_RESET_FINISHED_FAIL 3
+#define IM_RESET_NOT_IN_PROGRESS_NO_INT 4
+#define IM_RESET_FINISHED_OK_NO_INT 5
+
+/* define undefined SCSI-command */
+#define NO_SCSI 0xffff
+
+/*-----------------------------------------------------------------------*/
+
+/* if this is nonzero, ibmmcascsi option has been passed to the kernel */
+static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 };
+
+/* fill module-parameters only, when this define is present.
+ (that is kernel version 2.1.x) */
+#if defined(MODULE)
+static char *boot_options = NULL;
+module_param(boot_options, charp, 0);
+module_param_array(io_port, int, NULL, 0);
+module_param_array(scsi_id, int, NULL, 0);
+
+#if 0 /* FIXME: No longer exist? --RR */
+MODULE_PARM(display, "1i");
+MODULE_PARM(adisplay, "1i");
+MODULE_PARM(normal, "1i");
+MODULE_PARM(ansi, "1i");
+#endif
+#endif
+/*counter of concurrent disk read/writes, to turn on/off disk led */
+static int disk_rw_in_progress = 0;
+
+/* host information */
+static int found = 0;
+static struct Scsi_Host *hosts[IM_MAX_HOSTS + 1] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+static unsigned int pos[8]; /* whole pos register-line for diagnosis */
+/* Taking into account the additions, made by ZP Gu.
+ * This selects now the preset value from the configfile and
+ * offers the 'normal' commandline option to be accepted */
+#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD
+static char ibm_ansi_order = 1;
+#else
+static char ibm_ansi_order = 0;
+#endif
+
+static void issue_cmd(int, unsigned long, unsigned char);
+static void internal_done(Scsi_Cmnd * cmd);
+static void check_devices(int, int);
+static int immediate_assign(int, unsigned int, unsigned int, unsigned int, unsigned int);
+static int immediate_feature(int, unsigned int, unsigned int);
+#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
+static int immediate_reset(int, unsigned int);
+#endif
+static int device_inquiry(int, int);
+static int read_capacity(int, int);
+static int get_pos_info(int);
+static char *ti_p(int);
+static char *ti_l(int);
+static char *ibmrate(unsigned int, int);
+static int probe_display(int);
+static int probe_bus_mode(int);
+static int device_exists(int, int, int *, int *);
+static struct Scsi_Host *ibmmca_register(Scsi_Host_Template *, int, int, int, char *);
+static int option_setup(char *);
+/* local functions needed for proc_info */
+static int ldn_access_load(int, int);
+static int ldn_access_total_read_write(int);
+
+static irqreturn_t interrupt_handler(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ int host_index, ihost_index;
+ unsigned int intr_reg;
+ unsigned int cmd_result;
+ unsigned int ldn;
+ Scsi_Cmnd *cmd;
+ int lastSCSI;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock(dev->host_lock);
+ /* search for one adapter-response on shared interrupt */
+ for (host_index = 0; hosts[host_index] && !(inb(IM_STAT_REG(host_index)) & IM_INTR_REQUEST); host_index++);
+ /* return if some other device on this IRQ caused the interrupt */
+ if (!hosts[host_index]) {
+ spin_unlock(dev->host_lock);
+ return IRQ_NONE;
+ }
+
+ /* the reset-function already did all the job, even ints got
+ renabled on the subsystem, so just return */
+ if ((reset_status(host_index) == IM_RESET_NOT_IN_PROGRESS_NO_INT) || (reset_status(host_index) == IM_RESET_FINISHED_OK_NO_INT)) {
+ reset_status(host_index) = IM_RESET_NOT_IN_PROGRESS;
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+ }
+
+ /*must wait for attention reg not busy, then send EOI to subsystem */
+ while (1) {
+ if (!(inb(IM_STAT_REG(host_index)) & IM_BUSY))
+ break;
+ cpu_relax();
+ }
+ ihost_index = host_index;
+ /*get command result and logical device */
+ intr_reg = (unsigned char) (inb(IM_INTR_REG(ihost_index)));
+ cmd_result = intr_reg & 0xf0;
+ ldn = intr_reg & 0x0f;
+ /* get the last_scsi_command here */
+ lastSCSI = last_scsi_command(ihost_index)[ldn];
+ outb(IM_EOI | ldn, IM_ATTN_REG(ihost_index));
+
+ /*these should never happen (hw fails, or a local programming bug) */
+ if (!global_command_error_excuse) {
+ switch (cmd_result) {
+ /* Prevent from Ooopsing on error to show the real reason */
+ case IM_ADAPTER_HW_FAILURE:
+ case IM_SOFTWARE_SEQUENCING_ERROR:
+ case IM_CMD_ERROR:
+ printk(KERN_ERR "IBM MCA SCSI: Fatal Subsystem ERROR!\n");
+ printk(KERN_ERR " Last cmd=0x%x, ena=%x, len=", lastSCSI, ld(ihost_index)[ldn].scb.enable);
+ if (ld(ihost_index)[ldn].cmd)
+ printk("%ld/%ld,", (long) (ld(ihost_index)[ldn].cmd->request_bufflen), (long) (ld(ihost_index)[ldn].scb.sys_buf_length));
+ else
+ printk("none,");
+ if (ld(ihost_index)[ldn].cmd)
+ printk("Blocksize=%d", ld(ihost_index)[ldn].scb.u2.blk.length);
+ else
+ printk("Blocksize=none");
+ printk(", host=0x%x, ldn=0x%x\n", ihost_index, ldn);
+ if (ld(ihost_index)[ldn].cmd) {
+ printk(KERN_ERR "Blockcount=%d/%d\n", last_scsi_blockcount(ihost_index)[ldn], ld(ihost_index)[ldn].scb.u2.blk.count);
+ printk(KERN_ERR "Logical block=%lx/%lx\n", last_scsi_logical_block(ihost_index)[ldn], ld(ihost_index)[ldn].scb.u1.log_blk_adr);
+ }
+ printk(KERN_ERR "Reason given: %s\n", (cmd_result == IM_ADAPTER_HW_FAILURE) ? "HARDWARE FAILURE" : (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) ? "SOFTWARE SEQUENCING ERROR" : (cmd_result == IM_CMD_ERROR) ? "COMMAND ERROR" : "UNKNOWN");
+ /* if errors appear, enter this section to give detailed info */
+ printk(KERN_ERR "IBM MCA SCSI: Subsystem Error-Status follows:\n");
+ printk(KERN_ERR " Command Type................: %x\n", last_scsi_type(ihost_index)[ldn]);
+ printk(KERN_ERR " Attention Register..........: %x\n", inb(IM_ATTN_REG(ihost_index)));
+ printk(KERN_ERR " Basic Control Register......: %x\n", inb(IM_CTR_REG(ihost_index)));
+ printk(KERN_ERR " Interrupt Status Register...: %x\n", intr_reg);
+ printk(KERN_ERR " Basic Status Register.......: %x\n", inb(IM_STAT_REG(ihost_index)));
+ if ((last_scsi_type(ihost_index)[ldn] == IM_SCB) || (last_scsi_type(ihost_index)[ldn] == IM_LONG_SCB)) {
+ printk(KERN_ERR " SCB-Command.................: %x\n", ld(ihost_index)[ldn].scb.command);
+ printk(KERN_ERR " SCB-Enable..................: %x\n", ld(ihost_index)[ldn].scb.enable);
+ printk(KERN_ERR " SCB-logical block address...: %lx\n", ld(ihost_index)[ldn].scb.u1.log_blk_adr);
+ printk(KERN_ERR " SCB-system buffer address...: %lx\n", ld(ihost_index)[ldn].scb.sys_buf_adr);
+ printk(KERN_ERR " SCB-system buffer length....: %lx\n", ld(ihost_index)[ldn].scb.sys_buf_length);
+ printk(KERN_ERR " SCB-tsb address.............: %lx\n", ld(ihost_index)[ldn].scb.tsb_adr);
+ printk(KERN_ERR " SCB-Chain address...........: %lx\n", ld(ihost_index)[ldn].scb.scb_chain_adr);
+ printk(KERN_ERR " SCB-block count.............: %x\n", ld(ihost_index)[ldn].scb.u2.blk.count);
+ printk(KERN_ERR " SCB-block length............: %x\n", ld(ihost_index)[ldn].scb.u2.blk.length);
+ }
+ printk(KERN_ERR " Send this report to the maintainer.\n");
+ panic("IBM MCA SCSI: Fatal error message from the subsystem (0x%X,0x%X)!\n", lastSCSI, cmd_result);
+ break;
+ }
+ } else {
+ /* The command error handling is made silent, but we tell the
+ * calling function, that there is a reported error from the
+ * adapter. */
+ switch (cmd_result) {
+ case IM_ADAPTER_HW_FAILURE:
+ case IM_SOFTWARE_SEQUENCING_ERROR:
+ case IM_CMD_ERROR:
+ global_command_error_excuse = CMD_FAIL;
+ break;
+ default:
+ global_command_error_excuse = 0;
+ break;
+ }
+ }
+ /* if no panic appeared, increase the interrupt-counter */
+ IBM_DS(ihost_index).total_interrupts++;
+ /*only for local checking phase */
+ if (local_checking_phase_flag(ihost_index)) {
+ stat_result(ihost_index) = cmd_result;
+ got_interrupt(ihost_index) = 1;
+ reset_status(ihost_index) = IM_RESET_FINISHED_OK;
+ last_scsi_command(ihost_index)[ldn] = NO_SCSI;
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+ }
+ /* handling of commands coming from upper level of scsi driver */
+ if (last_scsi_type(ihost_index)[ldn] == IM_IMM_CMD) {
+ /* verify ldn, and may handle rare reset immediate command */
+ if ((reset_status(ihost_index) == IM_RESET_IN_PROGRESS) && (last_scsi_command(ihost_index)[ldn] == IM_RESET_IMM_CMD)) {
+ if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {
+ disk_rw_in_progress = 0;
+ PS2_DISK_LED_OFF();
+ reset_status(ihost_index) = IM_RESET_FINISHED_FAIL;
+ } else {
+ /*reset disk led counter, turn off disk led */
+ disk_rw_in_progress = 0;
+ PS2_DISK_LED_OFF();
+ reset_status(ihost_index) = IM_RESET_FINISHED_OK;
+ }
+ stat_result(ihost_index) = cmd_result;
+ last_scsi_command(ihost_index)[ldn] = NO_SCSI;
+ last_scsi_type(ihost_index)[ldn] = 0;
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+ } else if (last_scsi_command(ihost_index)[ldn] == IM_ABORT_IMM_CMD) {
+ /* react on SCSI abort command */
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Interrupt from SCSI-abort.\n");
+#endif
+ disk_rw_in_progress = 0;
+ PS2_DISK_LED_OFF();
+ cmd = ld(ihost_index)[ldn].cmd;
+ ld(ihost_index)[ldn].cmd = NULL;
+ if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
+ cmd->result = DID_NO_CONNECT << 16;
+ else
+ cmd->result = DID_ABORT << 16;
+ stat_result(ihost_index) = cmd_result;
+ last_scsi_command(ihost_index)[ldn] = NO_SCSI;
+ last_scsi_type(ihost_index)[ldn] = 0;
+ if (cmd->scsi_done)
+ (cmd->scsi_done) (cmd); /* should be the internal_done */
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+ } else {
+ disk_rw_in_progress = 0;
+ PS2_DISK_LED_OFF();
+ reset_status(ihost_index) = IM_RESET_FINISHED_OK;
+ stat_result(ihost_index) = cmd_result;
+ last_scsi_command(ihost_index)[ldn] = NO_SCSI;
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+ }
+ }
+ last_scsi_command(ihost_index)[ldn] = NO_SCSI;
+ last_scsi_type(ihost_index)[ldn] = 0;
+ cmd = ld(ihost_index)[ldn].cmd;
+ ld(ihost_index)[ldn].cmd = NULL;
+#ifdef IM_DEBUG_TIMEOUT
+ if (cmd) {
+ if ((cmd->target == TIMEOUT_PUN) && (cmd->device->lun == TIMEOUT_LUN)) {
+ printk("IBM MCA SCSI: Ignoring interrupt from pun=%x, lun=%x.\n", cmd->target, cmd->device->lun);
+ return IRQ_HANDLED;
+ }
+ }
+#endif
+ /*if no command structure, just return, else clear cmd */
+ if (!cmd)
+ {
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+ }
+
+#ifdef IM_DEBUG_INT
+ printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", cmd->cmnd[0], intr_reg, ld(ihost_index)[ldn].tsb.dev_status, ld(ihost_index)[ldn].tsb.cmd_status, ld(ihost_index)[ldn].tsb.dev_error, ld(ihost_index)[ldn].tsb.cmd_error);
+#endif
+ /*if this is end of media read/write, may turn off PS/2 disk led */
+ if ((ld(ihost_index)[ldn].device_type != TYPE_NO_LUN) && (ld(ihost_index)[ldn].device_type != TYPE_NO_DEVICE)) {
+ /* only access this, if there was a valid device addressed */
+ if (--disk_rw_in_progress == 0)
+ PS2_DISK_LED_OFF();
+ }
+
+ /* IBM describes the status-mask to be 0x1e, but this is not conform
+ * with SCSI-definition, I suppose, the reason for it is that IBM
+ * adapters do not support CMD_TERMINATED, TASK_SET_FULL and
+ * ACA_ACTIVE as returning statusbyte information. (ML) */
+ if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {
+ cmd->result = (unsigned char) (ld(ihost_index)[ldn].tsb.dev_status & 0x1e);
+ IBM_DS(ihost_index).total_errors++;
+ } else
+ cmd->result = 0;
+ /* write device status into cmd->result, and call done function */
+ if (lastSCSI == NO_SCSI) { /* unexpected interrupt :-( */
+ cmd->result |= DID_BAD_INTR << 16;
+ printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n");
+ } else /* things went right :-) */
+ cmd->result |= DID_OK << 16;
+ if (cmd->scsi_done)
+ (cmd->scsi_done) (cmd);
+ spin_unlock(dev->host_lock);
+ return IRQ_HANDLED;
+}
+
+static void issue_cmd(int host_index, unsigned long cmd_reg, unsigned char attn_reg)
+{
+ unsigned long flags;
+ /* must wait for attention reg not busy */
+ while (1) {
+ spin_lock_irqsave(hosts[host_index]->host_lock, flags);
+ if (!(inb(IM_STAT_REG(host_index)) & IM_BUSY))
+ break;
+ spin_unlock_irqrestore(hosts[host_index]->host_lock, flags);
+ }
+ /* write registers and enable system interrupts */
+ outl(cmd_reg, IM_CMD_REG(host_index));
+ outb(attn_reg, IM_ATTN_REG(host_index));
+ spin_unlock_irqrestore(hosts[host_index]->host_lock, flags);
+}
+
+static void internal_done(Scsi_Cmnd * cmd)
+{
+ cmd->SCp.Status++;
+ return;
+}
+
+/* SCSI-SCB-command for device_inquiry */
+static int device_inquiry(int host_index, int ldn)
+{
+ int retr;
+ struct im_scb *scb;
+ struct im_tsb *tsb;
+ unsigned char *buf;
+
+ scb = &(ld(host_index)[ldn].scb);
+ tsb = &(ld(host_index)[ldn].tsb);
+ buf = (unsigned char *) (&(ld(host_index)[ldn].buf));
+ ld(host_index)[ldn].tsb.dev_status = 0; /* prepare statusblock */
+ for (retr = 0; retr < 3; retr++) {
+ /* fill scb with inquiry command */
+ scb->command = IM_DEVICE_INQUIRY_CMD | IM_NO_DISCONNECT;
+ scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
+ last_scsi_command(host_index)[ldn] = IM_DEVICE_INQUIRY_CMD;
+ last_scsi_type(host_index)[ldn] = IM_SCB;
+ scb->sys_buf_adr = isa_virt_to_bus(buf);
+ scb->sys_buf_length = 255; /* maximum bufferlength gives max info */
+ scb->tsb_adr = isa_virt_to_bus(tsb);
+ /* issue scb to passed ldn, and busy wait for interrupt */
+ got_interrupt(host_index) = 0;
+ issue_cmd(host_index, isa_virt_to_bus(scb), IM_SCB | ldn);
+ while (!got_interrupt(host_index))
+ barrier();
+
+ /*if command succesful, break */
+ if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED) || (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
+ return 1;
+ }
+ /*if all three retries failed, return "no device at this ldn" */
+ if (retr >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+static int read_capacity(int host_index, int ldn)
+{
+ int retr;
+ struct im_scb *scb;
+ struct im_tsb *tsb;
+ unsigned char *buf;
+
+ scb = &(ld(host_index)[ldn].scb);
+ tsb = &(ld(host_index)[ldn].tsb);
+ buf = (unsigned char *) (&(ld(host_index)[ldn].buf));
+ ld(host_index)[ldn].tsb.dev_status = 0;
+ for (retr = 0; retr < 3; retr++) {
+ /*fill scb with read capacity command */
+ scb->command = IM_READ_CAPACITY_CMD;
+ scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
+ last_scsi_command(host_index)[ldn] = IM_READ_CAPACITY_CMD;
+ last_scsi_type(host_index)[ldn] = IM_SCB;
+ scb->sys_buf_adr = isa_virt_to_bus(buf);
+ scb->sys_buf_length = 8;
+ scb->tsb_adr = isa_virt_to_bus(tsb);
+ /*issue scb to passed ldn, and busy wait for interrupt */
+ got_interrupt(host_index) = 0;
+ issue_cmd(host_index, isa_virt_to_bus(scb), IM_SCB | ldn);
+ while (!got_interrupt(host_index))
+ barrier();
+
+ /*if got capacity, get block length and return one device found */
+ if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED) || (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
+ return 1;
+ }
+ /*if all three retries failed, return "no device at this ldn" */
+ if (retr >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+static int get_pos_info(int host_index)
+{
+ int retr;
+ struct im_scb *scb;
+ struct im_tsb *tsb;
+ unsigned char *buf;
+
+ scb = &(ld(host_index)[MAX_LOG_DEV].scb);
+ tsb = &(ld(host_index)[MAX_LOG_DEV].tsb);
+ buf = (unsigned char *) (&(ld(host_index)[MAX_LOG_DEV].buf));
+ ld(host_index)[MAX_LOG_DEV].tsb.dev_status = 0;
+ for (retr = 0; retr < 3; retr++) {
+ /*fill scb with get_pos_info command */
+ scb->command = IM_GET_POS_INFO_CMD;
+ scb->enable = IM_READ_CONTROL | IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
+ last_scsi_command(host_index)[MAX_LOG_DEV] = IM_GET_POS_INFO_CMD;
+ last_scsi_type(host_index)[MAX_LOG_DEV] = IM_SCB;
+ scb->sys_buf_adr = isa_virt_to_bus(buf);
+ if (special(host_index) == IBM_SCSI2_FW)
+ scb->sys_buf_length = 256; /* get all info from F/W adapter */
+ else
+ scb->sys_buf_length = 18; /* get exactly 18 bytes for other SCSI */
+ scb->tsb_adr = isa_virt_to_bus(tsb);
+ /*issue scb to ldn=15, and busy wait for interrupt */
+ got_interrupt(host_index) = 0;
+ issue_cmd(host_index, isa_virt_to_bus(scb), IM_SCB | MAX_LOG_DEV);
+
+ /* FIXME: timeout */
+ while (!got_interrupt(host_index))
+ barrier();
+
+ /*if got POS-stuff, get block length and return one device found */
+ if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED) || (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
+ return 1;
+ }
+ /* if all three retries failed, return "no device at this ldn" */
+ if (retr >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+/* SCSI-immediate-command for assign. This functions maps/unmaps specific
+ ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the
+ subsystem and for dynamical remapping od ldns. */
+static int immediate_assign(int host_index, unsigned int pun, unsigned int lun, unsigned int ldn, unsigned int operation)
+{
+ int retr;
+ unsigned long imm_cmd;
+
+ for (retr = 0; retr < 3; retr++) {
+ /* select mutation level of the SCSI-adapter */
+ switch (special(host_index)) {
+ case IBM_SCSI2_FW:
+ imm_cmd = (unsigned long) (IM_ASSIGN_IMM_CMD);
+ imm_cmd |= (unsigned long) ((lun & 7) << 24);
+ imm_cmd |= (unsigned long) ((operation & 1) << 23);
+ imm_cmd |= (unsigned long) ((pun & 7) << 20) | ((pun & 8) << 24);
+ imm_cmd |= (unsigned long) ((ldn & 15) << 16);
+ break;
+ default:
+ imm_cmd = inl(IM_CMD_REG(host_index));
+ imm_cmd &= (unsigned long) (0xF8000000); /* keep reserved bits */
+ imm_cmd |= (unsigned long) (IM_ASSIGN_IMM_CMD);
+ imm_cmd |= (unsigned long) ((lun & 7) << 24);
+ imm_cmd |= (unsigned long) ((operation & 1) << 23);
+ imm_cmd |= (unsigned long) ((pun & 7) << 20);
+ imm_cmd |= (unsigned long) ((ldn & 15) << 16);
+ break;
+ }
+ last_scsi_command(host_index)[MAX_LOG_DEV] = IM_ASSIGN_IMM_CMD;
+ last_scsi_type(host_index)[MAX_LOG_DEV] = IM_IMM_CMD;
+ got_interrupt(host_index) = 0;
+ issue_cmd(host_index, (unsigned long) (imm_cmd), IM_IMM_CMD | MAX_LOG_DEV);
+ while (!got_interrupt(host_index))
+ barrier();
+
+ /*if command succesful, break */
+ if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
+ return 1;
+ }
+ if (retr >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+static int immediate_feature(int host_index, unsigned int speed, unsigned int timeout)
+{
+ int retr;
+ unsigned long imm_cmd;
+
+ for (retr = 0; retr < 3; retr++) {
+ /* select mutation level of the SCSI-adapter */
+ imm_cmd = IM_FEATURE_CTR_IMM_CMD;
+ imm_cmd |= (unsigned long) ((speed & 0x7) << 29);
+ imm_cmd |= (unsigned long) ((timeout & 0x1fff) << 16);
+ last_scsi_command(host_index)[MAX_LOG_DEV] = IM_FEATURE_CTR_IMM_CMD;
+ last_scsi_type(host_index)[MAX_LOG_DEV] = IM_IMM_CMD;
+ got_interrupt(host_index) = 0;
+ /* we need to run into command errors in order to probe for the
+ * right speed! */
+ global_command_error_excuse = 1;
+ issue_cmd(host_index, (unsigned long) (imm_cmd), IM_IMM_CMD | MAX_LOG_DEV);
+
+ /* FIXME: timeout */
+ while (!got_interrupt(host_index))
+ barrier();
+ if (global_command_error_excuse == CMD_FAIL) {
+ global_command_error_excuse = 0;
+ return 2;
+ } else
+ global_command_error_excuse = 0;
+ /*if command succesful, break */
+ if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
+ return 1;
+ }
+ if (retr >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
+static int immediate_reset(int host_index, unsigned int ldn)
+{
+ int retries;
+ int ticks;
+ unsigned long imm_command;
+
+ for (retries = 0; retries < 3; retries++) {
+ imm_command = inl(IM_CMD_REG(host_index));
+ imm_command &= (unsigned long) (0xFFFF0000); /* keep reserved bits */
+ imm_command |= (unsigned long) (IM_RESET_IMM_CMD);
+ last_scsi_command(host_index)[ldn] = IM_RESET_IMM_CMD;
+ last_scsi_type(host_index)[ldn] = IM_IMM_CMD;
+ got_interrupt(host_index) = 0;
+ reset_status(host_index) = IM_RESET_IN_PROGRESS;
+ issue_cmd(host_index, (unsigned long) (imm_command), IM_IMM_CMD | ldn);
+ ticks = IM_RESET_DELAY * HZ;
+ while (reset_status(host_index) == IM_RESET_IN_PROGRESS && --ticks) {
+ udelay((1 + 999 / HZ) * 1000);
+ barrier();
+ }
+ /* if reset did not complete, just complain */
+ if (!ticks) {
+ printk(KERN_ERR "IBM MCA SCSI: reset did not complete within %d seconds.\n", IM_RESET_DELAY);
+ reset_status(host_index) = IM_RESET_FINISHED_OK;
+ /* did not work, finish */
+ return 1;
+ }
+ /*if command succesful, break */
+ if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
+ return 1;
+ }
+ if (retries >= 3)
+ return 0;
+ else
+ return 1;
+}
+#endif
+
+/* type-interpreter for physical device numbers */
+static char *ti_p(int dev)
+{
+ switch (dev) {
+ case TYPE_IBM_SCSI_ADAPTER:
+ return ("A");
+ case TYPE_DISK:
+ return ("D");
+ case TYPE_TAPE:
+ return ("T");
+ case TYPE_PROCESSOR:
+ return ("P");
+ case TYPE_WORM:
+ return ("W");
+ case TYPE_ROM:
+ return ("R");
+ case TYPE_SCANNER:
+ return ("S");
+ case TYPE_MOD:
+ return ("M");
+ case TYPE_MEDIUM_CHANGER:
+ return ("C");
+ case TYPE_NO_LUN:
+ return ("+"); /* show NO_LUN */
+ }
+ return ("-"); /* TYPE_NO_DEVICE and others */
+}
+
+/* interpreter for logical device numbers (ldn) */
+static char *ti_l(int val)
+{
+ const char hex[16] = "0123456789abcdef";
+ static char answer[2];
+
+ answer[1] = (char) (0x0);
+ if (val <= MAX_LOG_DEV)
+ answer[0] = hex[val];
+ else
+ answer[0] = '-';
+ return (char *) &answer;
+}
+
+/* transfers bitpattern of the feature command to values in MHz */
+static char *ibmrate(unsigned int speed, int i)
+{
+ switch (speed) {
+ case 0:
+ return i ? "5.00" : "10.00";
+ case 1:
+ return i ? "4.00" : "8.00";
+ case 2:
+ return i ? "3.33" : "6.66";
+ case 3:
+ return i ? "2.86" : "5.00";
+ case 4:
+ return i ? "2.50" : "4.00";
+ case 5:
+ return i ? "2.22" : "3.10";
+ case 6:
+ return i ? "2.00" : "2.50";
+ case 7:
+ return i ? "1.82" : "2.00";
+ }
+ return "---";
+}
+
+static int probe_display(int what)
+{
+ static int rotator = 0;
+ const char rotor[] = "|/-\\";
+
+ if (!(display_mode & LED_DISP))
+ return 0;
+ if (!what) {
+ outl(0x20202020, MOD95_LED_PORT);
+ outl(0x20202020, MOD95_LED_PORT + 4);
+ } else {
+ outb('S', MOD95_LED_PORT + 7);
+ outb('C', MOD95_LED_PORT + 6);
+ outb('S', MOD95_LED_PORT + 5);
+ outb('I', MOD95_LED_PORT + 4);
+ outb('i', MOD95_LED_PORT + 3);
+ outb('n', MOD95_LED_PORT + 2);
+ outb('i', MOD95_LED_PORT + 1);
+ outb((char) (rotor[rotator]), MOD95_LED_PORT);
+ rotator++;
+ if (rotator > 3)
+ rotator = 0;
+ }
+ return 0;
+}
+
+static int probe_bus_mode(int host_index)
+{
+ struct im_pos_info *info;
+ int num_bus = 0;
+ int ldn;
+
+ info = (struct im_pos_info *) (&(ld(host_index)[MAX_LOG_DEV].buf));
+ if (get_pos_info(host_index)) {
+ if (info->connector_size & 0xf000)
+ subsystem_connector_size(host_index) = 16;
+ else
+ subsystem_connector_size(host_index) = 32;
+ num_bus |= (info->pos_4b & 8) >> 3;
+ for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
+ if ((special(host_index) == IBM_SCSI_WCACHE) || (special(host_index) == IBM_7568_WCACHE)) {
+ if (!((info->cache_stat >> ldn) & 1))
+ ld(host_index)[ldn].cache_flag = 0;
+ }
+ if (!((info->retry_stat >> ldn) & 1))
+ ld(host_index)[ldn].retry_flag = 0;
+ }
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: SCSI-Cache bits: ");
+ for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
+ printk("%d", ld(host_index)[ldn].cache_flag);
+ }
+ printk("\nIBM MCA SCSI: SCSI-Retry bits: ");
+ for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
+ printk("%d", ld(host_index)[ldn].retry_flag);
+ }
+ printk("\n");
+#endif
+ }
+ return num_bus;
+}
+
+/* probing scsi devices */
+static void check_devices(int host_index, int adaptertype)
+{
+ int id, lun, ldn, ticks;
+ int count_devices; /* local counter for connected device */
+ int max_pun;
+ int num_bus;
+ int speedrun; /* local adapter_speed check variable */
+
+ /* assign default values to certain variables */
+ ticks = 0;
+ count_devices = 0;
+ IBM_DS(host_index).dyn_flag = 0; /* normally no need for dynamical ldn management */
+ IBM_DS(host_index).total_errors = 0; /* set errorcounter to 0 */
+ next_ldn(host_index) = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired' */
+
+ /* initialize the very important driver-informational arrays/structs */
+ memset(ld(host_index), 0, sizeof(ld(host_index)));
+ for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
+ last_scsi_command(host_index)[ldn] = NO_SCSI; /* emptify last SCSI-command storage */
+ last_scsi_type(host_index)[ldn] = 0;
+ ld(host_index)[ldn].cache_flag = 1;
+ ld(host_index)[ldn].retry_flag = 1;
+ }
+ memset(get_ldn(host_index), TYPE_NO_DEVICE, sizeof(get_ldn(host_index))); /* this is essential ! */
+ memset(get_scsi(host_index), TYPE_NO_DEVICE, sizeof(get_scsi(host_index))); /* this is essential ! */
+ for (lun = 0; lun < 8; lun++) {
+ /* mark the adapter at its pun on all luns */
+ get_scsi(host_index)[subsystem_pun(host_index)][lun] = TYPE_IBM_SCSI_ADAPTER;
+ get_ldn(host_index)[subsystem_pun(host_index)][lun] = MAX_LOG_DEV; /* make sure, the subsystem
+ ldn is active for all
+ luns. */
+ }
+ probe_display(0); /* Supercool display usage during SCSI-probing. */
+ /* This makes sense, when booting without any */
+ /* monitor connected on model XX95. */
+
+ /* STEP 1: */
+ adapter_speed(host_index) = global_adapter_speed;
+ speedrun = adapter_speed(host_index);
+ while (immediate_feature(host_index, speedrun, adapter_timeout) == 2) {
+ probe_display(1);
+ if (speedrun == 7)
+ panic("IBM MCA SCSI: Cannot set Synchronous-Transfer-Rate!\n");
+ speedrun++;
+ if (speedrun > 7)
+ speedrun = 7;
+ }
+ adapter_speed(host_index) = speedrun;
+ /* Get detailed information about the current adapter, necessary for
+ * device operations: */
+ num_bus = probe_bus_mode(host_index);
+
+ /* num_bus contains only valid data for the F/W adapter! */
+ if (adaptertype == IBM_SCSI2_FW) { /* F/W SCSI adapter: */
+ /* F/W adapter PUN-space extension evaluation: */
+ if (num_bus) {
+ printk(KERN_INFO "IBM MCA SCSI: Separate bus mode (wide-addressing enabled)\n");
+ subsystem_maxid(host_index) = 16;
+ } else {
+ printk(KERN_INFO "IBM MCA SCSI: Combined bus mode (wide-addressing disabled)\n");
+ subsystem_maxid(host_index) = 8;
+ }
+ printk(KERN_INFO "IBM MCA SCSI: Sync.-Rate (F/W: 20, Int.: 10, Ext.: %s) MBytes/s\n", ibmrate(speedrun, adaptertype));
+ } else /* all other IBM SCSI adapters: */
+ printk(KERN_INFO "IBM MCA SCSI: Synchronous-SCSI-Transfer-Rate: %s MBytes/s\n", ibmrate(speedrun, adaptertype));
+
+ /* assign correct PUN device space */
+ max_pun = subsystem_maxid(host_index);
+
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Current SCSI-host index: %d\n", host_index);
+ printk("IBM MCA SCSI: Removing default logical SCSI-device mapping.");
+#else
+ printk(KERN_INFO "IBM MCA SCSI: Dev. Order: %s, Mapping (takes <2min): ", (ibm_ansi_order) ? "ANSI" : "New");
+#endif
+ for (ldn = 0; ldn < MAX_LOG_DEV; ldn++) {
+ probe_display(1);
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ immediate_assign(host_index, 0, 0, ldn, REMOVE_LDN); /* remove ldn (wherever) */
+ }
+ lun = 0; /* default lun is 0 */
+#ifndef IM_DEBUG_PROBE
+ printk("cleared,");
+#endif
+ /* STEP 2: */
+#ifdef IM_DEBUG_PROBE
+ printk("\nIBM MCA SCSI: Scanning SCSI-devices.");
+#endif
+ for (id = 0; id < max_pun; id++)
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun = 0; lun < 8; lun++)
+#endif
+ {
+ probe_display(1);
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ if (id != subsystem_pun(host_index)) {
+ /* if pun is not the adapter: */
+ /* set ldn=0 to pun,lun */
+ immediate_assign(host_index, id, lun, PROBE_LDN, SET_LDN);
+ if (device_inquiry(host_index, PROBE_LDN)) { /* probe device */
+ get_scsi(host_index)[id][lun] = (unsigned char) (ld(host_index)[PROBE_LDN].buf[0]);
+ /* entry, even for NO_LUN */
+ if (ld(host_index)[PROBE_LDN].buf[0] != TYPE_NO_LUN)
+ count_devices++; /* a existing device is found */
+ }
+ /* remove ldn */
+ immediate_assign(host_index, id, lun, PROBE_LDN, REMOVE_LDN);
+ }
+ }
+#ifndef IM_DEBUG_PROBE
+ printk("scanned,");
+#endif
+ /* STEP 3: */
+#ifdef IM_DEBUG_PROBE
+ printk("\nIBM MCA SCSI: Mapping SCSI-devices.");
+#endif
+ ldn = 0;
+ lun = 0;
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun = 0; lun < 8 && ldn < MAX_LOG_DEV; lun++)
+#endif
+ for (id = 0; id < max_pun && ldn < MAX_LOG_DEV; id++) {
+ probe_display(1);
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ if (id != subsystem_pun(host_index)) {
+ if (get_scsi(host_index)[id][lun] != TYPE_NO_LUN && get_scsi(host_index)[id][lun] != TYPE_NO_DEVICE) {
+ /* Only map if accepted type. Always enter for
+ lun == 0 to get no gaps into ldn-mapping for ldn<7. */
+ immediate_assign(host_index, id, lun, ldn, SET_LDN);
+ get_ldn(host_index)[id][lun] = ldn; /* map ldn */
+ if (device_exists(host_index, ldn, &ld(host_index)[ldn].block_length, &ld(host_index)[ldn].device_type)) {
+#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
+ printk("resetting device at ldn=%x ... ", ldn);
+ immediate_reset(host_index, ldn);
+#endif
+ ldn++;
+ } else {
+ /* device vanished, probably because we don't know how to
+ * handle it or because it has problems */
+ if (lun > 0) {
+ /* remove mapping */
+ get_ldn(host_index)[id][lun] = TYPE_NO_DEVICE;
+ immediate_assign(host_index, 0, 0, ldn, REMOVE_LDN);
+ } else
+ ldn++;
+ }
+ } else if (lun == 0) {
+ /* map lun == 0, even if no device exists */
+ immediate_assign(host_index, id, lun, ldn, SET_LDN);
+ get_ldn(host_index)[id][lun] = ldn; /* map ldn */
+ ldn++;
+ }
+ }
+ }
+ /* STEP 4: */
+
+ /* map remaining ldns to non-existing devices */
+ for (lun = 1; lun < 8 && ldn < MAX_LOG_DEV; lun++)
+ for (id = 0; id < max_pun && ldn < MAX_LOG_DEV; id++) {
+ if (get_scsi(host_index)[id][lun] == TYPE_NO_LUN || get_scsi(host_index)[id][lun] == TYPE_NO_DEVICE) {
+ probe_display(1);
+ /* Map remaining ldns only to NON-existing pun,lun
+ combinations to make sure an inquiry will fail.
+ For MULTI_LUN, it is needed to avoid adapter autonome
+ SCSI-remapping. */
+ immediate_assign(host_index, id, lun, ldn, SET_LDN);
+ get_ldn(host_index)[id][lun] = ldn;
+ ldn++;
+ }
+ }
+#ifndef IM_DEBUG_PROBE
+ printk("mapped.");
+#endif
+ printk("\n");
+#ifdef IM_DEBUG_PROBE
+ if (ibm_ansi_order)
+ printk("IBM MCA SCSI: Device order: IBM/ANSI (pun=7 is first).\n");
+ else
+ printk("IBM MCA SCSI: Device order: New Industry Standard (pun=0 is first).\n");
+#endif
+
+#ifdef IM_DEBUG_PROBE
+ /* Show the physical and logical mapping during boot. */
+ printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n");
+ printk(" Physical SCSI-Device Map Logical SCSI-Device Map\n");
+ printk("ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n");
+ for (id = 0; id < max_pun; id++) {
+ printk("%2d ", id);
+ for (lun = 0; lun < 8; lun++)
+ printk("%2s ", ti_p(get_scsi(host_index)[id][lun]));
+ printk(" %2d ", id);
+ for (lun = 0; lun < 8; lun++)
+ printk("%2s ", ti_l(get_ldn(host_index)[id][lun]));
+ printk("\n");
+ }
+#endif
+
+ /* assign total number of found SCSI-devices to the statistics struct */
+ IBM_DS(host_index).total_scsi_devices = count_devices;
+
+ /* decide for output in /proc-filesystem, if the configuration of
+ SCSI-devices makes dynamical reassignment of devices necessary */
+ if (count_devices >= MAX_LOG_DEV)
+ IBM_DS(host_index).dyn_flag = 1; /* dynamical assignment is necessary */
+ else
+ IBM_DS(host_index).dyn_flag = 0; /* dynamical assignment is not necessary */
+
+ /* If no SCSI-devices are assigned, return 1 in order to cause message. */
+ if (ldn == 0)
+ printk("IBM MCA SCSI: Warning: No SCSI-devices found/assigned!\n");
+
+ /* reset the counters for statistics on the current adapter */
+ IBM_DS(host_index).scbs = 0;
+ IBM_DS(host_index).long_scbs = 0;
+ IBM_DS(host_index).total_accesses = 0;
+ IBM_DS(host_index).total_interrupts = 0;
+ IBM_DS(host_index).dynamical_assignments = 0;
+ memset(IBM_DS(host_index).ldn_access, 0x0, sizeof(IBM_DS(host_index).ldn_access));
+ memset(IBM_DS(host_index).ldn_read_access, 0x0, sizeof(IBM_DS(host_index).ldn_read_access));
+ memset(IBM_DS(host_index).ldn_write_access, 0x0, sizeof(IBM_DS(host_index).ldn_write_access));
+ memset(IBM_DS(host_index).ldn_inquiry_access, 0x0, sizeof(IBM_DS(host_index).ldn_inquiry_access));
+ memset(IBM_DS(host_index).ldn_modeselect_access, 0x0, sizeof(IBM_DS(host_index).ldn_modeselect_access));
+ memset(IBM_DS(host_index).ldn_assignments, 0x0, sizeof(IBM_DS(host_index).ldn_assignments));
+ probe_display(0);
+ return;
+}
+
+static int device_exists(int host_index, int ldn, int *block_length, int *device_type)
+{
+ unsigned char *buf;
+ /* if no valid device found, return immediately with 0 */
+ if (!(device_inquiry(host_index, ldn)))
+ return 0;
+ buf = (unsigned char *) (&(ld(host_index)[ldn].buf));
+ if (*buf == TYPE_ROM) {
+ *device_type = TYPE_ROM;
+ *block_length = 2048; /* (standard blocksize for yellow-/red-book) */
+ return 1;
+ }
+ if (*buf == TYPE_WORM) {
+ *device_type = TYPE_WORM;
+ *block_length = 2048;
+ return 1;
+ }
+ if (*buf == TYPE_DISK) {
+ *device_type = TYPE_DISK;
+ if (read_capacity(host_index, ldn)) {
+ *block_length = *(buf + 7) + (*(buf + 6) << 8) + (*(buf + 5) << 16) + (*(buf + 4) << 24);
+ return 1;
+ } else
+ return 0;
+ }
+ if (*buf == TYPE_MOD) {
+ *device_type = TYPE_MOD;
+ if (read_capacity(host_index, ldn)) {
+ *block_length = *(buf + 7) + (*(buf + 6) << 8) + (*(buf + 5) << 16) + (*(buf + 4) << 24);
+ return 1;
+ } else
+ return 0;
+ }
+ if (*buf == TYPE_TAPE) {
+ *device_type = TYPE_TAPE;
+ *block_length = 0; /* not in use (setting by mt and mtst in op.) */
+ return 1;
+ }
+ if (*buf == TYPE_PROCESSOR) {
+ *device_type = TYPE_PROCESSOR;
+ *block_length = 0; /* they set their stuff on drivers */
+ return 1;
+ }
+ if (*buf == TYPE_SCANNER) {
+ *device_type = TYPE_SCANNER;
+ *block_length = 0; /* they set their stuff on drivers */
+ return 1;
+ }
+ if (*buf == TYPE_MEDIUM_CHANGER) {
+ *device_type = TYPE_MEDIUM_CHANGER;
+ *block_length = 0; /* One never knows, what to expect on a medium
+ changer device. */
+ return 1;
+ }
+ return 0;
+}
+
+static void internal_ibmmca_scsi_setup(char *str, int *ints)
+{
+ int i, j, io_base, id_base;
+ char *token;
+
+ io_base = 0;
+ id_base = 0;
+ if (str) {
+ j = 0;
+ while ((token = strsep(&str, ",")) != NULL) {
+ if (!strcmp(token, "activity"))
+ display_mode |= LED_ACTIVITY;
+ if (!strcmp(token, "display"))
+ display_mode |= LED_DISP;
+ if (!strcmp(token, "adisplay"))
+ display_mode |= LED_ADISP;
+ if (!strcmp(token, "normal"))
+ ibm_ansi_order = 0;
+ if (!strcmp(token, "ansi"))
+ ibm_ansi_order = 1;
+ if (!strcmp(token, "fast"))
+ global_adapter_speed = 0;
+ if (!strcmp(token, "medium"))
+ global_adapter_speed = 4;
+ if (!strcmp(token, "slow"))
+ global_adapter_speed = 7;
+ if ((*token == '-') || (isdigit(*token))) {
+ if (!(j % 2) && (io_base < IM_MAX_HOSTS))
+ io_port[io_base++] = simple_strtoul(token, NULL, 0);
+ if ((j % 2) && (id_base < IM_MAX_HOSTS))
+ scsi_id[id_base++] = simple_strtoul(token, NULL, 0);
+ j++;
+ }
+ }
+ } else if (ints) {
+ for (i = 0; i < IM_MAX_HOSTS && 2 * i + 2 < ints[0]; i++) {
+ io_port[i] = ints[2 * i + 2];
+ scsi_id[i] = ints[2 * i + 2];
+ }
+ }
+ return;
+}
+
+static int ibmmca_getinfo(char *buf, int slot, void *dev_id)
+{
+ struct Scsi_Host *shpnt;
+ int len, speciale, connectore, k;
+ unsigned int pos[8];
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+
+ shpnt = dev; /* assign host-structure to local pointer */
+ len = 0; /* set filled text-buffer index to 0 */
+ /* get the _special contents of the hostdata structure */
+ speciale = ((struct ibmmca_hostdata *) shpnt->hostdata)->_special;
+ connectore = ((struct ibmmca_hostdata *) shpnt->hostdata)->_connector_size;
+ for (k = 2; k < 4; k++)
+ pos[k] = ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k];
+ if (speciale == FORCED_DETECTION) { /* forced detection */
+ len += sprintf(buf + len,
+ "Adapter category: forced detected\n" "***************************************\n" "*** Forced detected SCSI Adapter ***\n" "*** No chip-information available ***\n" "***************************************\n");
+ } else if (speciale == INTEGRATED_SCSI) {
+ /* if the integrated subsystem has been found automatically: */
+ len += sprintf(buf + len,
+ "Adapter category: integrated\n" "Chip revision level: %d\n" "Chip status: %s\n" "8 kByte NVRAM status: %s\n", ((pos[2] & 0xf0) >> 4), (pos[2] & 1) ? "enabled" : "disabled", (pos[2] & 2) ? "locked" : "accessible");
+ } else if ((speciale >= 0) && (speciale < (sizeof(subsys_list) / sizeof(struct subsys_list_struct)))) {
+ /* if the subsystem is a slot adapter */
+ len += sprintf(buf + len, "Adapter category: slot-card\n" "ROM Segment Address: ");
+ if ((pos[2] & 0xf0) == 0xf0)
+ len += sprintf(buf + len, "off\n");
+ else
+ len += sprintf(buf + len, "0x%x\n", ((pos[2] & 0xf0) << 13) + 0xc0000);
+ len += sprintf(buf + len, "Chip status: %s\n", (pos[2] & 1) ? "enabled" : "disabled");
+ len += sprintf(buf + len, "Adapter I/O Offset: 0x%x\n", ((pos[2] & 0x0e) << 2));
+ } else {
+ len += sprintf(buf + len, "Adapter category: unknown\n");
+ }
+ /* common subsystem information to write to the slotn file */
+ len += sprintf(buf + len, "Subsystem PUN: %d\n", shpnt->this_id);
+ len += sprintf(buf + len, "I/O base address range: 0x%x-0x%x\n", (unsigned int) (shpnt->io_port), (unsigned int) (shpnt->io_port + 7));
+ len += sprintf(buf + len, "MCA-slot size: %d bits", connectore);
+ /* Now make sure, the bufferlength is devidable by 4 to avoid
+ * paging problems of the buffer. */
+ while (len % sizeof(int) != (sizeof(int) - 1))
+ len += sprintf(buf + len, " ");
+ len += sprintf(buf + len, "\n");
+
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+
+ return len;
+}
+
+int ibmmca_detect(Scsi_Host_Template * scsi_template)
+{
+ struct Scsi_Host *shpnt;
+ int port, id, i, j, k, list_size, slot;
+ int devices_on_irq_11 = 0;
+ int devices_on_irq_14 = 0;
+ int IRQ14_registered = 0;
+ int IRQ11_registered = 0;
+
+ found = 0; /* make absolutely sure, that found is set to 0 */
+
+ /* First of all, print the version number of the driver. This is
+ * important to allow better user bugreports in case of already
+ * having problems with the MCA_bus probing. */
+ printk(KERN_INFO "IBM MCA SCSI: Version %s\n", IBMMCA_SCSI_DRIVER_VERSION);
+ /* if this is not MCA machine, return "nothing found" */
+ if (!MCA_bus) {
+ printk(KERN_INFO "IBM MCA SCSI: No Microchannel-bus present --> Aborting.\n" " This machine does not have any IBM MCA-bus\n" " or the MCA-Kernel-support is not enabled!\n");
+ return 0;
+ }
+
+#ifdef MODULE
+ /* If the driver is run as module, read from conf.modules or cmd-line */
+ if (boot_options)
+ option_setup(boot_options);
+#endif
+
+ /* get interrupt request level */
+ if (request_irq(IM_IRQ, interrupt_handler, SA_SHIRQ, "ibmmcascsi", hosts)) {
+ printk(KERN_ERR "IBM MCA SCSI: Unable to get shared IRQ %d.\n", IM_IRQ);
+ return 0;
+ } else
+ IRQ14_registered++;
+
+ /* if ibmmcascsi setup option was passed to kernel, return "found" */
+ for (i = 0; i < IM_MAX_HOSTS; i++)
+ if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8) {
+ printk("IBM MCA SCSI: forced detected SCSI Adapter, io=0x%x, scsi id=%d.\n", io_port[i], scsi_id[i]);
+ if ((shpnt = ibmmca_register(scsi_template, io_port[i], scsi_id[i], FORCED_DETECTION, "forced detected SCSI Adapter"))) {
+ for (k = 2; k < 7; k++)
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k] = 0;
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_special = FORCED_DETECTION;
+ mca_set_adapter_name(MCA_INTEGSCSI, "forced detected SCSI Adapter");
+ mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, shpnt);
+ mca_mark_as_used(MCA_INTEGSCSI);
+ devices_on_irq_14++;
+ }
+ }
+ if (found)
+ return found;
+
+ /* The POS2-register of all PS/2 model SCSI-subsystems has the following
+ * interpretation of bits:
+ * Bit 7 - 4 : Chip Revision ID (Release)
+ * Bit 3 - 2 : Reserved
+ * Bit 1 : 8k NVRAM Disabled
+ * Bit 0 : Chip Enable (EN-Signal)
+ * The POS3-register is interpreted as follows:
+ * Bit 7 - 5 : SCSI ID
+ * Bit 4 : Reserved = 0
+ * Bit 3 - 0 : Reserved = 0
+ * (taken from "IBM, PS/2 Hardware Interface Technical Reference, Common
+ * Interfaces (1991)").
+ * In short words, this means, that IBM PS/2 machines only support
+ * 1 single subsystem by default. The slot-adapters must have another
+ * configuration on pos2. Here, one has to assume the following
+ * things for POS2-register:
+ * Bit 7 - 4 : Chip Revision ID (Release)
+ * Bit 3 - 1 : port offset factor
+ * Bit 0 : Chip Enable (EN-Signal)
+ * As I found a patch here, setting the IO-registers to 0x3540 forced,
+ * as there was a 0x05 in POS2 on a model 56, I assume, that the
+ * port 0x3540 must be fix for integrated SCSI-controllers.
+ * Ok, this discovery leads to the following implementation: (M.Lang) */
+
+ /* first look for the IBM SCSI integrated subsystem on the motherboard */
+ for (j = 0; j < 8; j++) /* read the pos-information */
+ pos[j] = mca_read_stored_pos(MCA_INTEGSCSI, j);
+ /* pos2 = pos3 = 0xff if there is no integrated SCSI-subsystem present, but
+ * if we ignore the settings of all surrounding pos registers, it is not
+ * completely sufficient to only check pos2 and pos3. */
+ /* Therefore, now the following if statement is used to
+ * make sure, we see a real integrated onboard SCSI-interface and no
+ * internal system information, which gets mapped to some pos registers
+ * on models 95xx. */
+ if ((!pos[0] && !pos[1] && pos[2] > 0 && pos[3] > 0 && !pos[4] && !pos[5] && !pos[6] && !pos[7]) || (pos[0] == 0xff && pos[1] == 0xff && pos[2] < 0xff && pos[3] < 0xff && pos[4] == 0xff && pos[5] == 0xff && pos[6] == 0xff && pos[7] == 0xff)) {
+ if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */
+ port = IM_IO_PORT;
+ else { /* if disabled, no IRQs will be generated, as the chip won't
+ * listen to the incoming commands and will do really nothing,
+ * except for listening to the pos-register settings. If this
+ * happens, I need to hugely think about it, as one has to
+ * write something to the MCA-Bus pos register in order to
+ * enable the chip. Normally, IBM-SCSI won't pass the POST,
+ * when the chip is disabled (see IBM tech. ref.). */
+ port = IM_IO_PORT; /* anyway, set the portnumber and warn */
+ printk("IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n" " SCSI-operations may not work.\n");
+ }
+ id = (pos[3] & 0xe0) >> 5; /* this is correct and represents the PUN */
+ /* give detailed information on the subsystem. This helps me
+ * additionally during debugging and analyzing bug-reports. */
+ printk(KERN_INFO "IBM MCA SCSI: IBM Integrated SCSI Controller ffound, io=0x%x, scsi id=%d,\n", port, id);
+ printk(KERN_INFO " chip rev.=%d, 8K NVRAM=%s, subsystem=%s\n", ((pos[2] & 0xf0) >> 4), (pos[2] & 2) ? "locked" : "accessible", (pos[2] & 1) ? "enabled." : "disabled.");
+
+ /* register the found integrated SCSI-subsystem */
+ if ((shpnt = ibmmca_register(scsi_template, port, id, INTEGRATED_SCSI, "IBM Integrated SCSI Controller")))
+ {
+ for (k = 2; k < 7; k++)
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k] = pos[k];
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_special = INTEGRATED_SCSI;
+ mca_set_adapter_name(MCA_INTEGSCSI, "IBM Integrated SCSI Controller");
+ mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, shpnt);
+ mca_mark_as_used(MCA_INTEGSCSI);
+ devices_on_irq_14++;
+ }
+ }
+
+ /* now look for other adapters in MCA slots, */
+ /* determine the number of known IBM-SCSI-subsystem types */
+ /* see the pos[2] dependence to get the adapter port-offset. */
+ list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct);
+ for (i = 0; i < list_size; i++) {
+ /* scan each slot for a fitting adapter id */
+ slot = 0; /* start at slot 0 */
+ while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot))
+ != MCA_NOTFOUND) { /* scan through all slots */
+ for (j = 0; j < 8; j++) /* read the pos-information */
+ pos[j] = mca_read_stored_pos(slot, j);
+ if ((pos[2] & 1) == 1)
+ /* is the subsystem chip enabled ? */
+ /* (explanations see above) */
+ port = IM_IO_PORT + ((pos[2] & 0x0e) << 2);
+ else {
+ /* anyway, set the portnumber and warn */
+ port = IM_IO_PORT + ((pos[2] & 0x0e) << 2);
+ printk(KERN_WARNING "IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n");
+ printk(KERN_WARNING " SCSI-operations may not work.\n");
+ }
+ if ((i == IBM_SCSI2_FW) && (pos[6] != 0)) {
+ printk(KERN_ERR "IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n");
+ printk(KERN_ERR " Impossible to determine adapter PUN!\n");
+ printk(KERN_ERR " Guessing adapter PUN = 7.\n");
+ id = 7;
+ } else {
+ id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */
+ if (i == IBM_SCSI2_FW) {
+ id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit
+ * for F/W adapters */
+ }
+ }
+ if ((i == IBM_SCSI2_FW) && (pos[4] & 0x01) && (pos[6] == 0)) {
+ /* IRQ11 is used by SCSI-2 F/W Adapter/A */
+ printk(KERN_DEBUG "IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n");
+ /* get interrupt request level */
+ if (request_irq(IM_IRQ_FW, interrupt_handler, SA_SHIRQ, "ibmmcascsi", hosts)) {
+ printk(KERN_ERR "IBM MCA SCSI: Unable to get shared IRQ %d.\n", IM_IRQ_FW);
+ } else
+ IRQ11_registered++;
+ }
+ printk(KERN_INFO "IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d,\n", subsys_list[i].description, slot + 1, port, id);
+ if ((pos[2] & 0xf0) == 0xf0)
+ printk(KERN_DEBUG" ROM Addr.=off,");
+ else
+ printk(KERN_DEBUG " ROM Addr.=0x%x,", ((pos[2] & 0xf0) << 13) + 0xc0000);
+ printk(KERN_DEBUG " port-offset=0x%x, subsystem=%s\n", ((pos[2] & 0x0e) << 2), (pos[2] & 1) ? "enabled." : "disabled.");
+
+ /* register the hostadapter */
+ if ((shpnt = ibmmca_register(scsi_template, port, id, i, subsys_list[i].description))) {
+ for (k = 2; k < 8; k++)
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k] = pos[k];
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_special = i;
+ mca_set_adapter_name(slot, subsys_list[i].description);
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) ibmmca_getinfo, shpnt);
+ mca_mark_as_used(slot);
+ if ((i == IBM_SCSI2_FW) && (pos[4] & 0x01) && (pos[6] == 0))
+ devices_on_irq_11++;
+ else
+ devices_on_irq_14++;
+ }
+ slot++; /* advance to next slot */
+ } /* advance to next adapter id in the list of IBM-SCSI-subsystems */
+ }
+
+ /* now check for SCSI-adapters, mapped to the integrated SCSI
+ * area. E.g. a W/Cache in MCA-slot 9(!). Do the check correct here,
+ * as this is a known effect on some models 95xx. */
+ list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct);
+ for (i = 0; i < list_size; i++) {
+ /* scan each slot for a fitting adapter id */
+ slot = mca_find_adapter(subsys_list[i].mca_id, MCA_INTEGSCSI);
+ if (slot != MCA_NOTFOUND) { /* scan through all slots */
+ for (j = 0; j < 8; j++) /* read the pos-information */
+ pos[j] = mca_read_stored_pos(slot, j);
+ if ((pos[2] & 1) == 1) { /* is the subsystem chip enabled ? */
+ /* (explanations see above) */
+ port = IM_IO_PORT + ((pos[2] & 0x0e) << 2);
+ } else { /* anyway, set the portnumber and warn */
+ port = IM_IO_PORT + ((pos[2] & 0x0e) << 2);
+ printk(KERN_WARNING "IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n");
+ printk(KERN_WARNING " SCSI-operations may not work.\n");
+ }
+ if ((i == IBM_SCSI2_FW) && (pos[6] != 0)) {
+ printk(KERN_ERR "IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n");
+ printk(KERN_ERR " Impossible to determine adapter PUN!\n");
+ printk(KERN_ERR " Guessing adapter PUN = 7.\n");
+ id = 7;
+ } else {
+ id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */
+ if (i == IBM_SCSI2_FW)
+ id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit
+ * for F/W adapters */
+ }
+ if ((i == IBM_SCSI2_FW) && (pos[4] & 0x01) && (pos[6] == 0)) {
+ /* IRQ11 is used by SCSI-2 F/W Adapter/A */
+ printk(KERN_DEBUG "IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n");
+ /* get interrupt request level */
+ if (request_irq(IM_IRQ_FW, interrupt_handler, SA_SHIRQ, "ibmmcascsi", hosts))
+ printk(KERN_ERR "IBM MCA SCSI: Unable to get shared IRQ %d.\n", IM_IRQ_FW);
+ else
+ IRQ11_registered++;
+ }
+ printk(KERN_INFO "IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d,\n", subsys_list[i].description, slot + 1, port, id);
+ if ((pos[2] & 0xf0) == 0xf0)
+ printk(KERN_DEBUG " ROM Addr.=off,");
+ else
+ printk(KERN_DEBUG " ROM Addr.=0x%x,", ((pos[2] & 0xf0) << 13) + 0xc0000);
+ printk(KERN_DEBUG " port-offset=0x%x, subsystem=%s\n", ((pos[2] & 0x0e) << 2), (pos[2] & 1) ? "enabled." : "disabled.");
+
+ /* register the hostadapter */
+ if ((shpnt = ibmmca_register(scsi_template, port, id, i, subsys_list[i].description))) {
+ for (k = 2; k < 7; k++)
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k] = pos[k];
+ ((struct ibmmca_hostdata *) shpnt->hostdata)->_special = i;
+ mca_set_adapter_name(slot, subsys_list[i].description);
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) ibmmca_getinfo, shpnt);
+ mca_mark_as_used(slot);
+ if ((i == IBM_SCSI2_FW) && (pos[4] & 0x01) && (pos[6] == 0))
+ devices_on_irq_11++;
+ else
+ devices_on_irq_14++;
+ }
+ slot++; /* advance to next slot */
+ } /* advance to next adapter id in the list of IBM-SCSI-subsystems */
+ }
+ if (IRQ11_registered && !devices_on_irq_11)
+ free_irq(IM_IRQ_FW, hosts); /* no devices on IRQ 11 */
+ if (IRQ14_registered && !devices_on_irq_14)
+ free_irq(IM_IRQ, hosts); /* no devices on IRQ 14 */
+ if (!devices_on_irq_11 && !devices_on_irq_14)
+ printk(KERN_WARNING "IBM MCA SCSI: No IBM SCSI-subsystem adapter attached.\n");
+ return found; /* return the number of found SCSI hosts. Should be 1 or 0. */
+}
+
+static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, int adaptertype, char *hostname)
+{
+ struct Scsi_Host *shpnt;
+ int i, j;
+ unsigned int ctrl;
+
+ /* check I/O region */
+ if (!request_region(port, IM_N_IO_PORT, hostname)) {
+ printk(KERN_ERR "IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x (%d ports).\n", port, port + IM_N_IO_PORT - 1, IM_N_IO_PORT);
+ return NULL;
+ }
+
+ /* register host */
+ shpnt = scsi_register(scsi_template, sizeof(struct ibmmca_hostdata));
+ if (!shpnt) {
+ printk(KERN_ERR "IBM MCA SCSI: Unable to register host.\n");
+ release_region(port, IM_N_IO_PORT);
+ return NULL;
+ }
+
+ /* request I/O region */
+ hosts[found] = shpnt; /* add new found hostadapter to the list */
+ special(found) = adaptertype; /* important assignment or else crash! */
+ subsystem_connector_size(found) = 0; /* preset slot-size */
+ shpnt->irq = IM_IRQ; /* assign necessary stuff for the adapter */
+ shpnt->io_port = port;
+ shpnt->n_io_port = IM_N_IO_PORT;
+ shpnt->this_id = id;
+ shpnt->max_id = 8; /* 8 PUNs are default */
+ /* now, the SCSI-subsystem is connected to Linux */
+
+ ctrl = (unsigned int) (inb(IM_CTR_REG(found))); /* get control-register status */
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Control Register contents: %x, status: %x\n", ctrl, inb(IM_STAT_REG(found)));
+ printk("IBM MCA SCSI: This adapters' POS-registers: ");
+ for (i = 0; i < 8; i++)
+ printk("%x ", pos[i]);
+ printk("\n");
+#endif
+ reset_status(found) = IM_RESET_NOT_IN_PROGRESS;
+
+ for (i = 0; i < 16; i++) /* reset the tables */
+ for (j = 0; j < 8; j++)
+ get_ldn(found)[i][j] = MAX_LOG_DEV;
+
+ /* check which logical devices exist */
+ /* after this line, local interrupting is possible: */
+ local_checking_phase_flag(found) = 1;
+ check_devices(found, adaptertype); /* call by value, using the global variable hosts */
+ local_checking_phase_flag(found) = 0;
+ found++; /* now increase index to be prepared for next found subsystem */
+ /* an ibm mca subsystem has been detected */
+ return shpnt;
+}
+
+static int ibmmca_release(struct Scsi_Host *shpnt)
+{
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ if (!(--found))
+ free_irq(shpnt->irq, hosts);
+ return 0;
+}
+
+/* The following routine is the SCSI command queue for the midlevel driver */
+static int ibmmca_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ unsigned int ldn;
+ unsigned int scsi_cmd;
+ struct im_scb *scb;
+ struct Scsi_Host *shpnt;
+ int current_ldn;
+ int id, lun;
+ int target;
+ int host_index;
+ int max_pun;
+ int i;
+ struct scatterlist *sl;
+
+ shpnt = cmd->device->host;
+ /* search for the right hostadapter */
+ for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++);
+
+ if (!hosts[host_index]) { /* invalid hostadapter descriptor address */
+ cmd->result = DID_NO_CONNECT << 16;
+ if (done)
+ done(cmd);
+ return 0;
+ }
+ max_pun = subsystem_maxid(host_index);
+ if (ibm_ansi_order) {
+ target = max_pun - 1 - cmd->device->id;
+ if ((target <= subsystem_pun(host_index)) && (cmd->device->id <= subsystem_pun(host_index)))
+ target--;
+ else if ((target >= subsystem_pun(host_index)) && (cmd->device->id >= subsystem_pun(host_index)))
+ target++;
+ } else
+ target = cmd->device->id;
+
+ /* if (target,lun) is NO LUN or not existing at all, return error */
+ if ((get_scsi(host_index)[target][cmd->device->lun] == TYPE_NO_LUN) || (get_scsi(host_index)[target][cmd->device->lun] == TYPE_NO_DEVICE)) {
+ cmd->result = DID_NO_CONNECT << 16;
+ if (done)
+ done(cmd);
+ return 0;
+ }
+
+ /*if (target,lun) unassigned, do further checks... */
+ ldn = get_ldn(host_index)[target][cmd->device->lun];
+ if (ldn >= MAX_LOG_DEV) { /* on invalid ldn do special stuff */
+ if (ldn > MAX_LOG_DEV) { /* dynamical remapping if ldn unassigned */
+ current_ldn = next_ldn(host_index); /* stop-value for one circle */
+ while (ld(host_index)[next_ldn(host_index)].cmd) { /* search for a occupied, but not in */
+ /* command-processing ldn. */
+ next_ldn(host_index)++;
+ if (next_ldn(host_index) >= MAX_LOG_DEV)
+ next_ldn(host_index) = 7;
+ if (current_ldn == next_ldn(host_index)) { /* One circle done ? */
+ /* no non-processing ldn found */
+ printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n" " On ldn 7-14 SCSI-commands everywhere in progress.\n" " Reporting DID_NO_CONNECT for device (%d,%d).\n", target, cmd->device->lun);
+ cmd->result = DID_NO_CONNECT << 16; /* return no connect */
+ if (done)
+ done(cmd);
+ return 0;
+ }
+ }
+
+ /* unmap non-processing ldn */
+ for (id = 0; id < max_pun; id++)
+ for (lun = 0; lun < 8; lun++) {
+ if (get_ldn(host_index)[id][lun] == next_ldn(host_index)) {
+ get_ldn(host_index)[id][lun] = TYPE_NO_DEVICE;
+ get_scsi(host_index)[id][lun] = TYPE_NO_DEVICE;
+ /* unmap entry */
+ }
+ }
+ /* set reduced interrupt_handler-mode for checking */
+ local_checking_phase_flag(host_index) = 1;
+ /* map found ldn to pun,lun */
+ get_ldn(host_index)[target][cmd->device->lun] = next_ldn(host_index);
+ /* change ldn to the right value, that is now next_ldn */
+ ldn = next_ldn(host_index);
+ /* unassign all ldns (pun,lun,ldn does not matter for remove) */
+ immediate_assign(host_index, 0, 0, 0, REMOVE_LDN);
+ /* set only LDN for remapped device */
+ immediate_assign(host_index, target, cmd->device->lun, ldn, SET_LDN);
+ /* get device information for ld[ldn] */
+ if (device_exists(host_index, ldn, &ld(host_index)[ldn].block_length, &ld(host_index)[ldn].device_type)) {
+ ld(host_index)[ldn].cmd = NULL; /* To prevent panic set 0, because
+ devices that were not assigned,
+ should have nothing in progress. */
+ get_scsi(host_index)[target][cmd->device->lun] = ld(host_index)[ldn].device_type;
+ /* increase assignment counters for statistics in /proc */
+ IBM_DS(host_index).dynamical_assignments++;
+ IBM_DS(host_index).ldn_assignments[ldn]++;
+ } else
+ /* panic here, because a device, found at boottime has
+ vanished */
+ panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", ldn, target, cmd->device->lun);
+ /* unassign again all ldns (pun,lun,ldn does not matter for remove) */
+ immediate_assign(host_index, 0, 0, 0, REMOVE_LDN);
+ /* remap all ldns, as written in the pun/lun table */
+ lun = 0;
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun = 0; lun < 8; lun++)
+#endif
+ for (id = 0; id < max_pun; id++) {
+ if (get_ldn(host_index)[id][lun] <= MAX_LOG_DEV)
+ immediate_assign(host_index, id, lun, get_ldn(host_index)[id][lun], SET_LDN);
+ }
+ /* set back to normal interrupt_handling */
+ local_checking_phase_flag(host_index) = 0;
+#ifdef IM_DEBUG_PROBE
+ /* Information on syslog terminal */
+ printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", ldn, target, cmd->device->lun);
+#endif
+ /* increase next_ldn for next dynamical assignment */
+ next_ldn(host_index)++;
+ if (next_ldn(host_index) >= MAX_LOG_DEV)
+ next_ldn(host_index) = 7;
+ } else { /* wall against Linux accesses to the subsystem adapter */
+ cmd->result = DID_BAD_TARGET << 16;
+ if (done)
+ done(cmd);
+ return 0;
+ }
+ }
+
+ /*verify there is no command already in progress for this log dev */
+ if (ld(host_index)[ldn].cmd)
+ panic("IBM MCA SCSI: cmd already in progress for this ldn.\n");
+
+ /*save done in cmd, and save cmd for the interrupt handler */
+ cmd->scsi_done = done;
+ ld(host_index)[ldn].cmd = cmd;
+
+ /*fill scb information independent of the scsi command */
+ scb = &(ld(host_index)[ldn].scb);
+ ld(host_index)[ldn].tsb.dev_status = 0;
+ scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE;
+ scb->tsb_adr = isa_virt_to_bus(&(ld(host_index)[ldn].tsb));
+ scsi_cmd = cmd->cmnd[0];
+
+ if (cmd->use_sg) {
+ i = cmd->use_sg;
+ sl = (struct scatterlist *) (cmd->request_buffer);
+ if (i > 16)
+ panic("IBM MCA SCSI: scatter-gather list too long.\n");
+ while (--i >= 0) {
+ ld(host_index)[ldn].sge[i].address = (void *) (isa_page_to_bus(sl[i].page) + sl[i].offset);
+ ld(host_index)[ldn].sge[i].byte_length = sl[i].length;
+ }
+ scb->enable |= IM_POINTER_TO_LIST;
+ scb->sys_buf_adr = isa_virt_to_bus(&(ld(host_index)[ldn].sge[0]));
+ scb->sys_buf_length = cmd->use_sg * sizeof(struct im_sge);
+ } else {
+ scb->sys_buf_adr = isa_virt_to_bus(cmd->request_buffer);
+ /* recent Linux midlevel SCSI places 1024 byte for inquiry
+ * command. Far too much for old PS/2 hardware. */
+ switch (scsi_cmd) {
+ /* avoid command errors by setting bufferlengths to
+ * ANSI-standard. Beware of forcing it to 255,
+ * this could SEGV the kernel!!! */
+ case INQUIRY:
+ case REQUEST_SENSE:
+ case MODE_SENSE:
+ case MODE_SELECT:
+ if (cmd->request_bufflen > 255)
+ scb->sys_buf_length = 255;
+ else
+ scb->sys_buf_length = cmd->request_bufflen;
+ break;
+ case TEST_UNIT_READY:
+ scb->sys_buf_length = 0;
+ break;
+ default:
+ scb->sys_buf_length = cmd->request_bufflen;
+ break;
+ }
+ }
+ /*fill scb information dependent on scsi command */
+
+#ifdef IM_DEBUG_CMD
+ printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
+#endif
+
+ /* for specific device-type debugging: */
+#ifdef IM_DEBUG_CMD_SPEC_DEV
+ if (ld(host_index)[ldn].device_type == IM_DEBUG_CMD_DEVICE)
+ printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", ld(host_index)[ldn].device_type, scsi_cmd, ldn);
+#endif
+
+ /* for possible panics store current command */
+ last_scsi_command(host_index)[ldn] = scsi_cmd;
+ last_scsi_type(host_index)[ldn] = IM_SCB;
+ /* update statistical info */
+ IBM_DS(host_index).total_accesses++;
+ IBM_DS(host_index).ldn_access[ldn]++;
+
+ switch (scsi_cmd) {
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+ /* Distinguish between disk and other devices. Only disks (that are the
+ most frequently accessed devices) should be supported by the
+ IBM-SCSI-Subsystem commands. */
+ switch (ld(host_index)[ldn].device_type) {
+ case TYPE_DISK: /* for harddisks enter here ... */
+ case TYPE_MOD: /* ... try it also for MO-drives (send flames as */
+ /* you like, if this won't work.) */
+ if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12) {
+ /* read command preparations */
+ scb->enable |= IM_READ_CONTROL;
+ IBM_DS(host_index).ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
+ scb->command = IM_READ_DATA_CMD | IM_NO_DISCONNECT;
+ } else { /* write command preparations */
+ IBM_DS(host_index).ldn_write_access[ldn]++; /* increase write-count on ldn stat. */
+ scb->command = IM_WRITE_DATA_CMD | IM_NO_DISCONNECT;
+ }
+ if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) {
+ scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | (((unsigned) cmd->cmnd[2]) << 8) | ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
+ scb->u2.blk.count = (unsigned) cmd->cmnd[4];
+ } else {
+ scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | (((unsigned) cmd->cmnd[4]) << 8) | (((unsigned) cmd->cmnd[3]) << 16) | (((unsigned) cmd->cmnd[2]) << 24);
+ scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | (((unsigned) cmd->cmnd[7]) << 8);
+ }
+ last_scsi_logical_block(host_index)[ldn] = scb->u1.log_blk_adr;
+ last_scsi_blockcount(host_index)[ldn] = scb->u2.blk.count;
+ scb->u2.blk.length = ld(host_index)[ldn].block_length;
+ break;
+ /* for other devices, enter here. Other types are not known by
+ Linux! TYPE_NO_LUN is forbidden as valid device. */
+ case TYPE_ROM:
+ case TYPE_TAPE:
+ case TYPE_PROCESSOR:
+ case TYPE_WORM:
+ case TYPE_SCANNER:
+ case TYPE_MEDIUM_CHANGER:
+ /* If there is a sequential-device, IBM recommends to use
+ IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE.
+ This includes CD-ROM devices, too, due to the partial sequential
+ read capabilities. */
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12)
+ /* enable READ */
+ scb->enable |= IM_READ_CONTROL;
+ scb->enable |= IM_BYPASS_BUFFER;
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+ last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
+ /* Read/write on this non-disk devices is also displayworthy,
+ so flash-up the LED/display. */
+ break;
+ }
+ break;
+ case INQUIRY:
+ IBM_DS(host_index).ldn_inquiry_access[ldn]++;
+ scb->command = IM_DEVICE_INQUIRY_CMD;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
+ scb->u1.log_blk_adr = 0;
+ break;
+ case TEST_UNIT_READY:
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
+ scb->u1.log_blk_adr = 0;
+ scb->u1.scsi_cmd_length = 6;
+ memcpy(scb->u2.scsi_command, cmd->cmnd, 6);
+ last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
+ break;
+ case READ_CAPACITY:
+ /* the length of system memory buffer must be exactly 8 bytes */
+ scb->command = IM_READ_CAPACITY_CMD;
+ scb->enable |= IM_READ_CONTROL | IM_BYPASS_BUFFER;
+ if (scb->sys_buf_length > 8)
+ scb->sys_buf_length = 8;
+ break;
+ /* Commands that need read-only-mode (system <- device): */
+ case REQUEST_SENSE:
+ scb->command = IM_REQUEST_SENSE_CMD;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
+ break;
+ /* Commands that need write-only-mode (system -> device): */
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ IBM_DS(host_index).ldn_modeselect_access[ldn]++;
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ scb->enable |= IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; /*Select needs WRITE-enabled */
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+ last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
+ break;
+ /* For other commands, read-only is useful. Most other commands are
+ running without an input-data-block. */
+ default:
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+ last_scsi_type(host_index)[ldn] = IM_LONG_SCB;
+ break;
+ }
+ /*issue scb command, and return */
+ if (++disk_rw_in_progress == 1)
+ PS2_DISK_LED_ON(shpnt->host_no, target);
+
+ if (last_scsi_type(host_index)[ldn] == IM_LONG_SCB) {
+ issue_cmd(host_index, isa_virt_to_bus(scb), IM_LONG_SCB | ldn);
+ IBM_DS(host_index).long_scbs++;
+ } else {
+ issue_cmd(host_index, isa_virt_to_bus(scb), IM_SCB | ldn);
+ IBM_DS(host_index).scbs++;
+ }
+ return 0;
+}
+
+static int ibmmca_abort(Scsi_Cmnd * cmd)
+{
+ /* Abort does not work, as the adapter never generates an interrupt on
+ * whatever situation is simulated, even when really pending commands
+ * are running on the adapters' hardware ! */
+
+ struct Scsi_Host *shpnt;
+ unsigned int ldn;
+ void (*saved_done) (Scsi_Cmnd *);
+ int target;
+ int host_index;
+ int max_pun;
+ unsigned long imm_command;
+
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Abort subroutine called...\n");
+#endif
+
+ shpnt = cmd->device->host;
+ /* search for the right hostadapter */
+ for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++);
+
+ if (!hosts[host_index]) { /* invalid hostadapter descriptor address */
+ cmd->result = DID_NO_CONNECT << 16;
+ if (cmd->scsi_done)
+ (cmd->scsi_done) (cmd);
+ shpnt = cmd->device->host;
+#ifdef IM_DEBUG_PROBE
+ printk(KERN_DEBUG "IBM MCA SCSI: Abort adapter selection failed!\n");
+#endif
+ return SUCCESS;
+ }
+ max_pun = subsystem_maxid(host_index);
+ if (ibm_ansi_order) {
+ target = max_pun - 1 - cmd->device->id;
+ if ((target <= subsystem_pun(host_index)) && (cmd->device->id <= subsystem_pun(host_index)))
+ target--;
+ else if ((target >= subsystem_pun(host_index)) && (cmd->device->id >= subsystem_pun(host_index)))
+ target++;
+ } else
+ target = cmd->device->id;
+
+ /* get logical device number, and disable system interrupts */
+ printk(KERN_WARNING "IBM MCA SCSI: Sending abort to device pun=%d, lun=%d.\n", target, cmd->device->lun);
+ ldn = get_ldn(host_index)[target][cmd->device->lun];
+
+ /*if cmd for this ldn has already finished, no need to abort */
+ if (!ld(host_index)[ldn].cmd) {
+ return SUCCESS;
+ }
+
+ /* Clear ld.cmd, save done function, install internal done,
+ * send abort immediate command (this enables sys. interrupts),
+ * and wait until the interrupt arrives.
+ */
+ saved_done = cmd->scsi_done;
+ cmd->scsi_done = internal_done;
+ cmd->SCp.Status = 0;
+ last_scsi_command(host_index)[ldn] = IM_ABORT_IMM_CMD;
+ last_scsi_type(host_index)[ldn] = IM_IMM_CMD;
+ imm_command = inl(IM_CMD_REG(host_index));
+ imm_command &= (unsigned long) (0xffff0000); /* mask reserved stuff */
+ imm_command |= (unsigned long) (IM_ABORT_IMM_CMD);
+ /* must wait for attention reg not busy */
+ /* FIXME - timeout, politeness */
+ while (1) {
+ if (!(inb(IM_STAT_REG(host_index)) & IM_BUSY))
+ break;
+ }
+ /* write registers and enable system interrupts */
+ outl(imm_command, IM_CMD_REG(host_index));
+ outb(IM_IMM_CMD | ldn, IM_ATTN_REG(host_index));
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Abort queued to adapter...\n");
+#endif
+ spin_unlock_irq(shpnt->host_lock);
+ while (!cmd->SCp.Status)
+ yield();
+ spin_lock_irq(shpnt->host_lock);
+ cmd->scsi_done = saved_done;
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Abort returned with adapter response...\n");
+#endif
+
+ /*if abort went well, call saved done, then return success or error */
+ if (cmd->result == (DID_ABORT << 16))
+ {
+ cmd->result |= DID_ABORT << 16;
+ if (cmd->scsi_done)
+ (cmd->scsi_done) (cmd);
+ ld(host_index)[ldn].cmd = NULL;
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Abort finished with success.\n");
+#endif
+ return SUCCESS;
+ } else {
+ cmd->result |= DID_NO_CONNECT << 16;
+ if (cmd->scsi_done)
+ (cmd->scsi_done) (cmd);
+ ld(host_index)[ldn].cmd = NULL;
+#ifdef IM_DEBUG_PROBE
+ printk("IBM MCA SCSI: Abort failed.\n");
+#endif
+ return FAILED;
+ }
+}
+
+static int ibmmca_host_reset(Scsi_Cmnd * cmd)
+{
+ struct Scsi_Host *shpnt;
+ Scsi_Cmnd *cmd_aid;
+ int ticks, i;
+ int host_index;
+ unsigned long imm_command;
+
+ if (cmd == NULL)
+ BUG();
+
+ ticks = IM_RESET_DELAY * HZ;
+ shpnt = cmd->device->host;
+ /* search for the right hostadapter */
+ for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++);
+
+ if (!hosts[host_index]) /* invalid hostadapter descriptor address */
+ return FAILED;
+
+ if (local_checking_phase_flag(host_index)) {
+ printk(KERN_WARNING "IBM MCA SCSI: unable to reset while checking devices.\n");
+ return FAILED;
+ }
+
+ /* issue reset immediate command to subsystem, and wait for interrupt */
+ printk("IBM MCA SCSI: resetting all devices.\n");
+ reset_status(host_index) = IM_RESET_IN_PROGRESS;
+ last_scsi_command(host_index)[0xf] = IM_RESET_IMM_CMD;
+ last_scsi_type(host_index)[0xf] = IM_IMM_CMD;
+ imm_command = inl(IM_CMD_REG(host_index));
+ imm_command &= (unsigned long) (0xffff0000); /* mask reserved stuff */
+ imm_command |= (unsigned long) (IM_RESET_IMM_CMD);
+ /* must wait for attention reg not busy */
+ while (1) {
+ if (!(inb(IM_STAT_REG(host_index)) & IM_BUSY))
+ break;
+ spin_unlock_irq(shpnt->host_lock);
+ yield();
+ spin_lock_irq(shpnt->host_lock);
+ }
+ /*write registers and enable system interrupts */
+ outl(imm_command, IM_CMD_REG(host_index));
+ outb(IM_IMM_CMD | 0xf, IM_ATTN_REG(host_index));
+ /* wait for interrupt finished or intr_stat register to be set, as the
+ * interrupt will not be executed, while we are in here! */
+
+ /* FIXME: This is really really icky we so want a sleeping version of this ! */
+ while (reset_status(host_index) == IM_RESET_IN_PROGRESS && --ticks && ((inb(IM_INTR_REG(host_index)) & 0x8f) != 0x8f)) {
+ udelay((1 + 999 / HZ) * 1000);
+ barrier();
+ }
+ /* if reset did not complete, just return an error */
+ if (!ticks) {
+ printk(KERN_ERR "IBM MCA SCSI: reset did not complete within %d seconds.\n", IM_RESET_DELAY);
+ reset_status(host_index) = IM_RESET_FINISHED_FAIL;
+ return FAILED;
+ }
+
+ if ((inb(IM_INTR_REG(host_index)) & 0x8f) == 0x8f) {
+ /* analysis done by this routine and not by the intr-routine */
+ if (inb(IM_INTR_REG(host_index)) == 0xaf)
+ reset_status(host_index) = IM_RESET_FINISHED_OK_NO_INT;
+ else if (inb(IM_INTR_REG(host_index)) == 0xcf)
+ reset_status(host_index) = IM_RESET_FINISHED_FAIL;
+ else /* failed, 4get it */
+ reset_status(host_index) = IM_RESET_NOT_IN_PROGRESS_NO_INT;
+ outb(IM_EOI | 0xf, IM_ATTN_REG(host_index));
+ }
+
+ /* if reset failed, just return an error */
+ if (reset_status(host_index) == IM_RESET_FINISHED_FAIL) {
+ printk(KERN_ERR "IBM MCA SCSI: reset failed.\n");
+ return FAILED;
+ }
+
+ /* so reset finished ok - call outstanding done's, and return success */
+ printk(KERN_INFO "IBM MCA SCSI: Reset successfully completed.\n");
+ for (i = 0; i < MAX_LOG_DEV; i++) {
+ cmd_aid = ld(host_index)[i].cmd;
+ if (cmd_aid && cmd_aid->scsi_done) {
+ ld(host_index)[i].cmd = NULL;
+ cmd_aid->result = DID_RESET << 16;
+ }
+ }
+ return SUCCESS;
+}
+
+static int ibmmca_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *info)
+{
+ int size = capacity;
+ info[0] = 64;
+ info[1] = 32;
+ info[2] = size / (info[0] * info[1]);
+ if (info[2] >= 1024) {
+ info[0] = 128;
+ info[1] = 63;
+ info[2] = size / (info[0] * info[1]);
+ if (info[2] >= 1024) {
+ info[0] = 255;
+ info[1] = 63;
+ info[2] = size / (info[0] * info[1]);
+ if (info[2] >= 1024)
+ info[2] = 1023;
+ }
+ }
+ return 0;
+}
+
+/* calculate percentage of total accesses on a ldn */
+static int ldn_access_load(int host_index, int ldn)
+{
+ if (IBM_DS(host_index).total_accesses == 0)
+ return (0);
+ if (IBM_DS(host_index).ldn_access[ldn] == 0)
+ return (0);
+ return (IBM_DS(host_index).ldn_access[ldn] * 100) / IBM_DS(host_index).total_accesses;
+}
+
+/* calculate total amount of r/w-accesses */
+static int ldn_access_total_read_write(int host_index)
+{
+ int a;
+ int i;
+
+ a = 0;
+ for (i = 0; i <= MAX_LOG_DEV; i++)
+ a += IBM_DS(host_index).ldn_read_access[i] + IBM_DS(host_index).ldn_write_access[i];
+ return (a);
+}
+
+static int ldn_access_total_inquiry(int host_index)
+{
+ int a;
+ int i;
+
+ a = 0;
+ for (i = 0; i <= MAX_LOG_DEV; i++)
+ a += IBM_DS(host_index).ldn_inquiry_access[i];
+ return (a);
+}
+
+static int ldn_access_total_modeselect(int host_index)
+{
+ int a;
+ int i;
+
+ a = 0;
+ for (i = 0; i <= MAX_LOG_DEV; i++)
+ a += IBM_DS(host_index).ldn_modeselect_access[i];
+ return (a);
+}
+
+/* routine to display info in the proc-fs-structure (a deluxe feature) */
+static int ibmmca_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ int len = 0;
+ int i, id, lun, host_index;
+ unsigned long flags;
+ int max_pun;
+
+ for (i = 0; hosts[i] && hosts[i] != shpnt; i++);
+
+ spin_lock_irqsave(hosts[i]->host_lock, flags); /* Check it */
+ host_index = i;
+ if (!shpnt) {
+ len += sprintf(buffer + len, "\nIBM MCA SCSI: Can't find adapter for host number %d\n",
+ shpnt->host_no);
+ return len;
+ }
+ max_pun = subsystem_maxid(host_index);
+
+ len += sprintf(buffer + len, "\n IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n", IBMMCA_SCSI_DRIVER_VERSION);
+ len += sprintf(buffer + len, " SCSI Access-Statistics:\n");
+ len += sprintf(buffer + len, " Device Scanning Order....: %s\n", (ibm_ansi_order) ? "IBM/ANSI" : "New Industry Standard");
+#ifdef CONFIG_SCSI_MULTI_LUN
+ len += sprintf(buffer + len, " Multiple LUN probing.....: Yes\n");
+#else
+ len += sprintf(buffer + len, " Multiple LUN probing.....: No\n");
+#endif
+ len += sprintf(buffer + len, " This Hostnumber..........: %d\n", shpnt->host_no);
+ len += sprintf(buffer + len, " Base I/O-Port............: 0x%x\n", (unsigned int) (IM_CMD_REG(host_index)));
+ len += sprintf(buffer + len, " (Shared) IRQ.............: %d\n", IM_IRQ);
+ len += sprintf(buffer + len, " Total Interrupts.........: %d\n", IBM_DS(host_index).total_interrupts);
+ len += sprintf(buffer + len, " Total SCSI Accesses......: %d\n", IBM_DS(host_index).total_accesses);
+ len += sprintf(buffer + len, " Total short SCBs.........: %d\n", IBM_DS(host_index).scbs);
+ len += sprintf(buffer + len, " Total long SCBs..........: %d\n", IBM_DS(host_index).long_scbs);
+ len += sprintf(buffer + len, " Total SCSI READ/WRITE..: %d\n", ldn_access_total_read_write(host_index));
+ len += sprintf(buffer + len, " Total SCSI Inquiries...: %d\n", ldn_access_total_inquiry(host_index));
+ len += sprintf(buffer + len, " Total SCSI Modeselects.: %d\n", ldn_access_total_modeselect(host_index));
+ len += sprintf(buffer + len, " Total SCSI other cmds..: %d\n", IBM_DS(host_index).total_accesses - ldn_access_total_read_write(host_index)
+ - ldn_access_total_modeselect(host_index)
+ - ldn_access_total_inquiry(host_index));
+ len += sprintf(buffer + len, " Total SCSI command fails.: %d\n\n", IBM_DS(host_index).total_errors);
+ len += sprintf(buffer + len, " Logical-Device-Number (LDN) Access-Statistics:\n");
+ len += sprintf(buffer + len, " LDN | Accesses [%%] | READ | WRITE | ASSIGNMENTS\n");
+ len += sprintf(buffer + len, " -----|--------------|-----------|-----------|--------------\n");
+ for (i = 0; i <= MAX_LOG_DEV; i++)
+ len += sprintf(buffer + len, " %2X | %3d | %8d | %8d | %8d\n", i, ldn_access_load(host_index, i), IBM_DS(host_index).ldn_read_access[i], IBM_DS(host_index).ldn_write_access[i], IBM_DS(host_index).ldn_assignments[i]);
+ len += sprintf(buffer + len, " -----------------------------------------------------------\n\n");
+ len += sprintf(buffer + len, " Dynamical-LDN-Assignment-Statistics:\n");
+ len += sprintf(buffer + len, " Number of physical SCSI-devices..: %d (+ Adapter)\n", IBM_DS(host_index).total_scsi_devices);
+ len += sprintf(buffer + len, " Dynamical Assignment necessary...: %s\n", IBM_DS(host_index).dyn_flag ? "Yes" : "No ");
+ len += sprintf(buffer + len, " Next LDN to be assigned..........: 0x%x\n", next_ldn(host_index));
+ len += sprintf(buffer + len, " Dynamical assignments done yet...: %d\n", IBM_DS(host_index).dynamical_assignments);
+ len += sprintf(buffer + len, "\n Current SCSI-Device-Mapping:\n");
+ len += sprintf(buffer + len, " Physical SCSI-Device Map Logical SCSI-Device Map\n");
+ len += sprintf(buffer + len, " ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n");
+ for (id = 0; id < max_pun; id++) {
+ len += sprintf(buffer + len, " %2d ", id);
+ for (lun = 0; lun < 8; lun++)
+ len += sprintf(buffer + len, "%2s ", ti_p(get_scsi(host_index)[id][lun]));
+ len += sprintf(buffer + len, " %2d ", id);
+ for (lun = 0; lun < 8; lun++)
+ len += sprintf(buffer + len, "%2s ", ti_l(get_ldn(host_index)[id][lun]));
+ len += sprintf(buffer + len, "\n");
+ }
+
+ len += sprintf(buffer + len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n");
+ len += sprintf(buffer + len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n");
+ len += sprintf(buffer + len, " - = nothing found, nothing assigned or unprobed LUN)\n\n");
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ spin_unlock_irqrestore(shpnt->host_lock, flags);
+ return len;
+}
+
+static int option_setup(char *str)
+{
+ int ints[IM_MAX_HOSTS];
+ char *cur = str;
+ int i = 1;
+
+ while (cur && isdigit(*cur) && i <= IM_MAX_HOSTS) {
+ ints[i++] = simple_strtoul(cur, NULL, 0);
+ if ((cur = strchr(cur, ',')) != NULL)
+ cur++;
+ }
+ ints[0] = i - 1;
+ internal_ibmmca_scsi_setup(cur, ints);
+ return 0;
+}
+
+__setup("ibmmcascsi=", option_setup);
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "ibmmca",
+ .proc_info = ibmmca_proc_info,
+ .name = "IBM SCSI-Subsystem",
+ .detect = ibmmca_detect,
+ .release = ibmmca_release,
+ .queuecommand = ibmmca_queuecommand,
+ .eh_abort_handler = ibmmca_abort,
+ .eh_host_reset_handler = ibmmca_host_reset,
+ .bios_param = ibmmca_biosparam,
+ .can_queue = 16,
+ .this_id = 7,
+ .sg_tablesize = 16,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/ibmmca.h b/drivers/scsi/ibmmca.h
new file mode 100644
index 000000000000..6d68f603e9b8
--- /dev/null
+++ b/drivers/scsi/ibmmca.h
@@ -0,0 +1,21 @@
+/*
+ * Low Level Driver for the IBM Microchannel SCSI Subsystem
+ * (Headerfile, see Documentation/scsi/ibmmca.txt for description of the
+ * IBM MCA SCSI-driver.
+ * For use under the GNU General Public License within the Linux-kernel project.
+ * This include file works only correctly with kernel 2.4.0 or higher!!! */
+
+#ifndef _IBMMCA_H
+#define _IBMMCA_H
+
+/* Common forward declarations for all Linux-versions: */
+
+/* Interfaces to the midlevel Linux SCSI driver */
+static int ibmmca_detect (Scsi_Host_Template *);
+static int ibmmca_release (struct Scsi_Host *);
+static int ibmmca_queuecommand (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+static int ibmmca_abort (Scsi_Cmnd *);
+static int ibmmca_host_reset (Scsi_Cmnd *);
+static int ibmmca_biosparam (struct scsi_device *, struct block_device *, sector_t, int *);
+
+#endif /* _IBMMCA_H */
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
new file mode 100644
index 000000000000..4e247b6b8700
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o
+
+ibmvscsic-y += ibmvscsi.o
+ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o
+ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
new file mode 100644
index 000000000000..e89f76e5dd53
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -0,0 +1,1473 @@
+/* ------------------------------------------------------------
+ * ibmvscsi.c
+ * (C) Copyright IBM Corporation 1994, 2004
+ * Authors: Colin DeVilbiss (devilbis@us.ibm.com)
+ * Santiago Leon (santil@us.ibm.com)
+ * Dave Boutcher (sleddog@us.ibm.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
+ *
+ * ------------------------------------------------------------
+ * Emulation of a SCSI host adapter for Virtual I/O devices
+ *
+ * This driver supports the SCSI adapter implemented by the IBM
+ * Power5 firmware. That SCSI adapter is not a physical adapter,
+ * but allows Linux SCSI peripheral drivers to directly
+ * access devices in another logical partition on the physical system.
+ *
+ * The virtual adapter(s) are present in the open firmware device
+ * tree just like real adapters.
+ *
+ * One of the capabilities provided on these systems is the ability
+ * to DMA between partitions. The architecture states that for VSCSI,
+ * the server side is allowed to DMA to and from the client. The client
+ * is never trusted to DMA to or from the server directly.
+ *
+ * Messages are sent between partitions on a "Command/Response Queue"
+ * (CRQ), which is just a buffer of 16 byte entries in the receiver's
+ * Senders cannot access the buffer directly, but send messages by
+ * making a hypervisor call and passing in the 16 bytes. The hypervisor
+ * puts the message in the next 16 byte space in round-robbin fashion,
+ * turns on the high order bit of the message (the valid bit), and
+ * generates an interrupt to the receiver (if interrupts are turned on.)
+ * The receiver just turns off the valid bit when they have copied out
+ * the message.
+ *
+ * The VSCSI client builds a SCSI Remote Protocol (SRP) Information Unit
+ * (IU) (as defined in the T10 standard available at www.t10.org), gets
+ * a DMA address for the message, and sends it to the server as the
+ * payload of a CRQ message. The server DMAs the SRP IU and processes it,
+ * including doing any additional data transfers. When it is done, it
+ * DMAs the SRP response back to the same address as the request came from,
+ * and sends a CRQ message back to inform the client that the request has
+ * completed.
+ *
+ * Note that some of the underlying infrastructure is different between
+ * machines conforming to the "RS/6000 Platform Architecture" (RPA) and
+ * the older iSeries hypervisor models. To support both, some low level
+ * routines have been broken out into rpa_vscsi.c and iseries_vscsi.c.
+ * The Makefile should pick one, not two, not zero, of these.
+ *
+ * TODO: This is currently pretty tied to the IBM i/pSeries hypervisor
+ * interfaces. It would be really nice to abstract this above an RDMA
+ * layer.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <asm/vio.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include "ibmvscsi.h"
+
+/* The values below are somewhat arbitrary default values, but
+ * OS/400 will use 3 busses (disks, CDs, tapes, I think.)
+ * Note that there are 3 bits of channel value, 6 bits of id, and
+ * 5 bits of LUN.
+ */
+static int max_id = 64;
+static int max_channel = 3;
+static int init_timeout = 5;
+static int max_requests = 50;
+
+#define IBMVSCSI_VERSION "1.5.5"
+
+MODULE_DESCRIPTION("IBM Virtual SCSI");
+MODULE_AUTHOR("Dave Boutcher");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(IBMVSCSI_VERSION);
+
+module_param_named(max_id, max_id, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_id, "Largest ID value for each channel");
+module_param_named(max_channel, max_channel, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_channel, "Largest channel value");
+module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds");
+module_param_named(max_requests, max_requests, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter");
+
+/* ------------------------------------------------------------
+ * Routines for the event pool and event structs
+ */
+/**
+ * initialize_event_pool: - Allocates and initializes the event pool for a host
+ * @pool: event_pool to be initialized
+ * @size: Number of events in pool
+ * @hostdata: ibmvscsi_host_data who owns the event pool
+ *
+ * Returns zero on success.
+*/
+static int initialize_event_pool(struct event_pool *pool,
+ int size, struct ibmvscsi_host_data *hostdata)
+{
+ int i;
+
+ pool->size = size;
+ pool->next = 0;
+ pool->events = kmalloc(pool->size * sizeof(*pool->events), GFP_KERNEL);
+ if (!pool->events)
+ return -ENOMEM;
+ memset(pool->events, 0x00, pool->size * sizeof(*pool->events));
+
+ pool->iu_storage =
+ dma_alloc_coherent(hostdata->dev,
+ pool->size * sizeof(*pool->iu_storage),
+ &pool->iu_token, 0);
+ if (!pool->iu_storage) {
+ kfree(pool->events);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pool->size; ++i) {
+ struct srp_event_struct *evt = &pool->events[i];
+ memset(&evt->crq, 0x00, sizeof(evt->crq));
+ atomic_set(&evt->free, 1);
+ evt->crq.valid = 0x80;
+ evt->crq.IU_length = sizeof(*evt->xfer_iu);
+ evt->crq.IU_data_ptr = pool->iu_token +
+ sizeof(*evt->xfer_iu) * i;
+ evt->xfer_iu = pool->iu_storage + i;
+ evt->hostdata = hostdata;
+ }
+
+ return 0;
+}
+
+/**
+ * release_event_pool: - Frees memory of an event pool of a host
+ * @pool: event_pool to be released
+ * @hostdata: ibmvscsi_host_data who owns the even pool
+ *
+ * Returns zero on success.
+*/
+static void release_event_pool(struct event_pool *pool,
+ struct ibmvscsi_host_data *hostdata)
+{
+ int i, in_use = 0;
+ for (i = 0; i < pool->size; ++i)
+ if (atomic_read(&pool->events[i].free) != 1)
+ ++in_use;
+ if (in_use)
+ printk(KERN_WARNING
+ "ibmvscsi: releasing event pool with %d "
+ "events still in use?\n", in_use);
+ kfree(pool->events);
+ dma_free_coherent(hostdata->dev,
+ pool->size * sizeof(*pool->iu_storage),
+ pool->iu_storage, pool->iu_token);
+}
+
+/**
+ * valid_event_struct: - Determines if event is valid.
+ * @pool: event_pool that contains the event
+ * @evt: srp_event_struct to be checked for validity
+ *
+ * Returns zero if event is invalid, one otherwise.
+*/
+static int valid_event_struct(struct event_pool *pool,
+ struct srp_event_struct *evt)
+{
+ int index = evt - pool->events;
+ if (index < 0 || index >= pool->size) /* outside of bounds */
+ return 0;
+ if (evt != pool->events + index) /* unaligned */
+ return 0;
+ return 1;
+}
+
+/**
+ * ibmvscsi_free-event_struct: - Changes status of event to "free"
+ * @pool: event_pool that contains the event
+ * @evt: srp_event_struct to be modified
+ *
+*/
+static void free_event_struct(struct event_pool *pool,
+ struct srp_event_struct *evt)
+{
+ if (!valid_event_struct(pool, evt)) {
+ printk(KERN_ERR
+ "ibmvscsi: Freeing invalid event_struct %p "
+ "(not in pool %p)\n", evt, pool->events);
+ return;
+ }
+ if (atomic_inc_return(&evt->free) != 1) {
+ printk(KERN_ERR
+ "ibmvscsi: Freeing event_struct %p "
+ "which is not in use!\n", evt);
+ return;
+ }
+}
+
+/**
+ * get_evt_struct: - Gets the next free event in pool
+ * @pool: event_pool that contains the events to be searched
+ *
+ * Returns the next event in "free" state, and NULL if none are free.
+ * Note that no synchronization is done here, we assume the host_lock
+ * will syncrhonze things.
+*/
+static struct srp_event_struct *get_event_struct(struct event_pool *pool)
+{
+ int i;
+ int poolsize = pool->size;
+ int offset = pool->next;
+
+ for (i = 0; i < poolsize; i++) {
+ offset = (offset + 1) % poolsize;
+ if (!atomic_dec_if_positive(&pool->events[offset].free)) {
+ pool->next = offset;
+ return &pool->events[offset];
+ }
+ }
+
+ printk(KERN_ERR "ibmvscsi: found no event struct in pool!\n");
+ return NULL;
+}
+
+/**
+ * init_event_struct: Initialize fields in an event struct that are always
+ * required.
+ * @evt: The event
+ * @done: Routine to call when the event is responded to
+ * @format: SRP or MAD format
+ * @timeout: timeout value set in the CRQ
+ */
+static void init_event_struct(struct srp_event_struct *evt_struct,
+ void (*done) (struct srp_event_struct *),
+ u8 format,
+ int timeout)
+{
+ evt_struct->cmnd = NULL;
+ evt_struct->cmnd_done = NULL;
+ evt_struct->sync_srp = NULL;
+ evt_struct->crq.format = format;
+ evt_struct->crq.timeout = timeout;
+ evt_struct->done = done;
+}
+
+/* ------------------------------------------------------------
+ * Routines for receiving SCSI responses from the hosting partition
+ */
+
+/**
+ * set_srp_direction: Set the fields in the srp related to data
+ * direction and number of buffers based on the direction in
+ * the scsi_cmnd and the number of buffers
+ */
+static void set_srp_direction(struct scsi_cmnd *cmd,
+ struct srp_cmd *srp_cmd,
+ int numbuf)
+{
+ if (numbuf == 0)
+ return;
+
+ if (numbuf == 1) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ srp_cmd->data_out_format = SRP_DIRECT_BUFFER;
+ else
+ srp_cmd->data_in_format = SRP_DIRECT_BUFFER;
+ } else {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ srp_cmd->data_out_format = SRP_INDIRECT_BUFFER;
+ srp_cmd->data_out_count = numbuf;
+ } else {
+ srp_cmd->data_in_format = SRP_INDIRECT_BUFFER;
+ srp_cmd->data_in_count = numbuf;
+ }
+ }
+}
+
+/**
+ * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format
+ * @cmd: srp_cmd whose additional_data member will be unmapped
+ * @dev: device for which the memory is mapped
+ *
+*/
+static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev)
+{
+ int i;
+
+ if ((cmd->data_out_format == SRP_NO_BUFFER) &&
+ (cmd->data_in_format == SRP_NO_BUFFER))
+ return;
+ else if ((cmd->data_out_format == SRP_DIRECT_BUFFER) ||
+ (cmd->data_in_format == SRP_DIRECT_BUFFER)) {
+ struct memory_descriptor *data =
+ (struct memory_descriptor *)cmd->additional_data;
+ dma_unmap_single(dev, data->virtual_address, data->length,
+ DMA_BIDIRECTIONAL);
+ } else {
+ struct indirect_descriptor *indirect =
+ (struct indirect_descriptor *)cmd->additional_data;
+ int num_mapped = indirect->head.length /
+ sizeof(indirect->list[0]);
+ for (i = 0; i < num_mapped; ++i) {
+ struct memory_descriptor *data = &indirect->list[i];
+ dma_unmap_single(dev,
+ data->virtual_address,
+ data->length, DMA_BIDIRECTIONAL);
+ }
+ }
+}
+
+/**
+ * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields
+ * @cmd: Scsi_Cmnd with the scatterlist
+ * @srp_cmd: srp_cmd that contains the memory descriptor
+ * @dev: device for which to map dma memory
+ *
+ * Called by map_data_for_srp_cmd() when building srp cmd from scsi cmd.
+ * Returns 1 on success.
+*/
+static int map_sg_data(struct scsi_cmnd *cmd,
+ struct srp_cmd *srp_cmd, struct device *dev)
+{
+
+ int i, sg_mapped;
+ u64 total_length = 0;
+ struct scatterlist *sg = cmd->request_buffer;
+ struct memory_descriptor *data =
+ (struct memory_descriptor *)srp_cmd->additional_data;
+ struct indirect_descriptor *indirect =
+ (struct indirect_descriptor *)data;
+
+ sg_mapped = dma_map_sg(dev, sg, cmd->use_sg, DMA_BIDIRECTIONAL);
+
+ if (sg_mapped == 0)
+ return 0;
+
+ set_srp_direction(cmd, srp_cmd, sg_mapped);
+
+ /* special case; we can use a single direct descriptor */
+ if (sg_mapped == 1) {
+ data->virtual_address = sg_dma_address(&sg[0]);
+ data->length = sg_dma_len(&sg[0]);
+ data->memory_handle = 0;
+ return 1;
+ }
+
+ if (sg_mapped > MAX_INDIRECT_BUFS) {
+ printk(KERN_ERR
+ "ibmvscsi: More than %d mapped sg entries, got %d\n",
+ MAX_INDIRECT_BUFS, sg_mapped);
+ return 0;
+ }
+
+ indirect->head.virtual_address = 0;
+ indirect->head.length = sg_mapped * sizeof(indirect->list[0]);
+ indirect->head.memory_handle = 0;
+ for (i = 0; i < sg_mapped; ++i) {
+ struct memory_descriptor *descr = &indirect->list[i];
+ struct scatterlist *sg_entry = &sg[i];
+ descr->virtual_address = sg_dma_address(sg_entry);
+ descr->length = sg_dma_len(sg_entry);
+ descr->memory_handle = 0;
+ total_length += sg_dma_len(sg_entry);
+ }
+ indirect->total_length = total_length;
+
+ return 1;
+}
+
+/**
+ * map_single_data: - Maps memory and initializes memory decriptor fields
+ * @cmd: struct scsi_cmnd with the memory to be mapped
+ * @srp_cmd: srp_cmd that contains the memory descriptor
+ * @dev: device for which to map dma memory
+ *
+ * Called by map_data_for_srp_cmd() when building srp cmd from scsi cmd.
+ * Returns 1 on success.
+*/
+static int map_single_data(struct scsi_cmnd *cmd,
+ struct srp_cmd *srp_cmd, struct device *dev)
+{
+ struct memory_descriptor *data =
+ (struct memory_descriptor *)srp_cmd->additional_data;
+
+ data->virtual_address =
+ dma_map_single(dev, cmd->request_buffer,
+ cmd->request_bufflen,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(data->virtual_address)) {
+ printk(KERN_ERR
+ "ibmvscsi: Unable to map request_buffer for command!\n");
+ return 0;
+ }
+ data->length = cmd->request_bufflen;
+ data->memory_handle = 0;
+
+ set_srp_direction(cmd, srp_cmd, 1);
+
+ return 1;
+}
+
+/**
+ * map_data_for_srp_cmd: - Calls functions to map data for srp cmds
+ * @cmd: struct scsi_cmnd with the memory to be mapped
+ * @srp_cmd: srp_cmd that contains the memory descriptor
+ * @dev: dma device for which to map dma memory
+ *
+ * Called by scsi_cmd_to_srp_cmd() when converting scsi cmds to srp cmds
+ * Returns 1 on success.
+*/
+static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
+ struct srp_cmd *srp_cmd, struct device *dev)
+{
+ switch (cmd->sc_data_direction) {
+ case DMA_FROM_DEVICE:
+ case DMA_TO_DEVICE:
+ break;
+ case DMA_NONE:
+ return 1;
+ case DMA_BIDIRECTIONAL:
+ printk(KERN_ERR
+ "ibmvscsi: Can't map DMA_BIDIRECTIONAL to read/write\n");
+ return 0;
+ default:
+ printk(KERN_ERR
+ "ibmvscsi: Unknown data direction 0x%02x; can't map!\n",
+ cmd->sc_data_direction);
+ return 0;
+ }
+
+ if (!cmd->request_buffer)
+ return 1;
+ if (cmd->use_sg)
+ return map_sg_data(cmd, srp_cmd, dev);
+ return map_single_data(cmd, srp_cmd, dev);
+}
+
+/* ------------------------------------------------------------
+ * Routines for sending and receiving SRPs
+ */
+/**
+ * ibmvscsi_send_srp_event: - Transforms event to u64 array and calls send_crq()
+ * @evt_struct: evt_struct to be sent
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * Returns the value returned from ibmvscsi_send_crq(). (Zero for success)
+ * Note that this routine assumes that host_lock is held for synchronization
+*/
+static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
+ struct ibmvscsi_host_data *hostdata)
+{
+ struct scsi_cmnd *cmnd;
+ u64 *crq_as_u64 = (u64 *) &evt_struct->crq;
+ int rc;
+
+ /* If we have exhausted our request limit, just fail this request.
+ * Note that there are rare cases involving driver generated requests
+ * (such as task management requests) that the mid layer may think we
+ * can handle more requests (can_queue) when we actually can't
+ */
+ if ((evt_struct->crq.format == VIOSRP_SRP_FORMAT) &&
+ (atomic_dec_if_positive(&hostdata->request_limit) < 0)) {
+ /* See if the adapter is disabled */
+ if (atomic_read(&hostdata->request_limit) < 0)
+ goto send_error;
+
+ printk(KERN_WARNING
+ "ibmvscsi: Warning, request_limit exceeded\n");
+ unmap_cmd_data(&evt_struct->iu.srp.cmd,
+ hostdata->dev);
+ free_event_struct(&hostdata->pool, evt_struct);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ /* Copy the IU into the transfer area */
+ *evt_struct->xfer_iu = evt_struct->iu;
+ evt_struct->xfer_iu->srp.generic.tag = (u64)evt_struct;
+
+ /* Add this to the sent list. We need to do this
+ * before we actually send
+ * in case it comes back REALLY fast
+ */
+ list_add_tail(&evt_struct->list, &hostdata->sent);
+
+ if ((rc =
+ ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) {
+ list_del(&evt_struct->list);
+
+ printk(KERN_ERR "ibmvscsi: failed to send event struct rc %d\n",
+ rc);
+ goto send_error;
+ }
+
+ return 0;
+
+ send_error:
+ unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev);
+
+ if ((cmnd = evt_struct->cmnd) != NULL) {
+ cmnd->result = DID_ERROR << 16;
+ evt_struct->cmnd_done(cmnd);
+ } else if (evt_struct->done)
+ evt_struct->done(evt_struct);
+
+ free_event_struct(&hostdata->pool, evt_struct);
+ return 0;
+}
+
+/**
+ * handle_cmd_rsp: - Handle responses from commands
+ * @evt_struct: srp_event_struct to be handled
+ *
+ * Used as a callback by when sending scsi cmds.
+ * Gets called by ibmvscsi_handle_crq()
+*/
+static void handle_cmd_rsp(struct srp_event_struct *evt_struct)
+{
+ struct srp_rsp *rsp = &evt_struct->xfer_iu->srp.rsp;
+ struct scsi_cmnd *cmnd = evt_struct->cmnd;
+
+ if (unlikely(rsp->type != SRP_RSP_TYPE)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: bad SRP RSP type %d\n",
+ rsp->type);
+ }
+
+ if (cmnd) {
+ cmnd->result = rsp->status;
+ if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION)
+ memcpy(cmnd->sense_buffer,
+ rsp->sense_and_response_data,
+ rsp->sense_data_list_length);
+ unmap_cmd_data(&evt_struct->iu.srp.cmd,
+ evt_struct->hostdata->dev);
+
+ if (rsp->doover)
+ cmnd->resid = rsp->data_out_residual_count;
+ else if (rsp->diover)
+ cmnd->resid = rsp->data_in_residual_count;
+ }
+
+ if (evt_struct->cmnd_done)
+ evt_struct->cmnd_done(cmnd);
+}
+
+/**
+ * lun_from_dev: - Returns the lun of the scsi device
+ * @dev: struct scsi_device
+ *
+*/
+static inline u16 lun_from_dev(struct scsi_device *dev)
+{
+ return (0x2 << 14) | (dev->id << 8) | (dev->channel << 5) | dev->lun;
+}
+
+/**
+ * ibmvscsi_queue: - The queuecommand function of the scsi template
+ * @cmd: struct scsi_cmnd to be executed
+ * @done: Callback function to be called when cmd is completed
+*/
+static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct srp_cmd *srp_cmd;
+ struct srp_event_struct *evt_struct;
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)&cmnd->device->host->hostdata;
+ u16 lun = lun_from_dev(cmnd->device);
+
+ evt_struct = get_event_struct(&hostdata->pool);
+ if (!evt_struct)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ init_event_struct(evt_struct,
+ handle_cmd_rsp,
+ VIOSRP_SRP_FORMAT,
+ cmnd->timeout);
+
+ evt_struct->cmnd = cmnd;
+ evt_struct->cmnd_done = done;
+
+ /* Set up the actual SRP IU */
+ srp_cmd = &evt_struct->iu.srp.cmd;
+ memset(srp_cmd, 0x00, sizeof(*srp_cmd));
+ srp_cmd->type = SRP_CMD_TYPE;
+ memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(cmnd->cmnd));
+ srp_cmd->lun = ((u64) lun) << 48;
+
+ if (!map_data_for_srp_cmd(cmnd, srp_cmd, hostdata->dev)) {
+ printk(KERN_ERR "ibmvscsi: couldn't convert cmd to srp_cmd\n");
+ free_event_struct(&hostdata->pool, evt_struct);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ /* Fix up dma address of the buffer itself */
+ if ((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) ||
+ (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) {
+ struct indirect_descriptor *indirect =
+ (struct indirect_descriptor *)srp_cmd->additional_data;
+ indirect->head.virtual_address = evt_struct->crq.IU_data_ptr +
+ offsetof(struct srp_cmd, additional_data) +
+ offsetof(struct indirect_descriptor, list);
+ }
+
+ return ibmvscsi_send_srp_event(evt_struct, hostdata);
+}
+
+/* ------------------------------------------------------------
+ * Routines for driver initialization
+ */
+/**
+ * adapter_info_rsp: - Handle response to MAD adapter info request
+ * @evt_struct: srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending adapter_info. Gets called
+ * by ibmvscsi_handle_crq()
+*/
+static void adapter_info_rsp(struct srp_event_struct *evt_struct)
+{
+ struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+ dma_unmap_single(hostdata->dev,
+ evt_struct->iu.mad.adapter_info.buffer,
+ evt_struct->iu.mad.adapter_info.common.length,
+ DMA_BIDIRECTIONAL);
+
+ if (evt_struct->xfer_iu->mad.adapter_info.common.status) {
+ printk("ibmvscsi: error %d getting adapter info\n",
+ evt_struct->xfer_iu->mad.adapter_info.common.status);
+ } else {
+ printk("ibmvscsi: host srp version: %s, "
+ "host partition %s (%d), OS %d, max io %u\n",
+ hostdata->madapter_info.srp_version,
+ hostdata->madapter_info.partition_name,
+ hostdata->madapter_info.partition_number,
+ hostdata->madapter_info.os_type,
+ hostdata->madapter_info.port_max_txu[0]);
+
+ if (hostdata->madapter_info.port_max_txu[0])
+ hostdata->host->max_sectors =
+ hostdata->madapter_info.port_max_txu[0] >> 9;
+ }
+}
+
+/**
+ * send_mad_adapter_info: - Sends the mad adapter info request
+ * and stores the result so it can be retrieved with
+ * sysfs. We COULD consider causing a failure if the
+ * returned SRP version doesn't match ours.
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * Returns zero if successful.
+*/
+static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
+{
+ struct viosrp_adapter_info *req;
+ struct srp_event_struct *evt_struct;
+
+ memset(&hostdata->madapter_info, 0x00, sizeof(hostdata->madapter_info));
+
+ evt_struct = get_event_struct(&hostdata->pool);
+ if (!evt_struct) {
+ printk(KERN_ERR "ibmvscsi: couldn't allocate an event "
+ "for ADAPTER_INFO_REQ!\n");
+ return;
+ }
+
+ init_event_struct(evt_struct,
+ adapter_info_rsp,
+ VIOSRP_MAD_FORMAT,
+ init_timeout * HZ);
+
+ req = &evt_struct->iu.mad.adapter_info;
+ memset(req, 0x00, sizeof(*req));
+
+ req->common.type = VIOSRP_ADAPTER_INFO_TYPE;
+ req->common.length = sizeof(hostdata->madapter_info);
+ req->buffer = dma_map_single(hostdata->dev,
+ &hostdata->madapter_info,
+ sizeof(hostdata->madapter_info),
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(req->buffer)) {
+ printk(KERN_ERR
+ "ibmvscsi: Unable to map request_buffer "
+ "for adapter_info!\n");
+ free_event_struct(&hostdata->pool, evt_struct);
+ return;
+ }
+
+ if (ibmvscsi_send_srp_event(evt_struct, hostdata))
+ printk(KERN_ERR "ibmvscsi: couldn't send ADAPTER_INFO_REQ!\n");
+};
+
+/**
+ * login_rsp: - Handle response to SRP login request
+ * @evt_struct: srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending srp_login. Gets called
+ * by ibmvscsi_handle_crq()
+*/
+static void login_rsp(struct srp_event_struct *evt_struct)
+{
+ struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+ switch (evt_struct->xfer_iu->srp.generic.type) {
+ case SRP_LOGIN_RSP_TYPE: /* it worked! */
+ break;
+ case SRP_LOGIN_REJ_TYPE: /* refused! */
+ printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REQ rejected\n");
+ /* Login failed. */
+ atomic_set(&hostdata->request_limit, -1);
+ return;
+ default:
+ printk(KERN_ERR
+ "ibmvscsi: Invalid login response typecode 0x%02x!\n",
+ evt_struct->xfer_iu->srp.generic.type);
+ /* Login failed. */
+ atomic_set(&hostdata->request_limit, -1);
+ return;
+ }
+
+ printk(KERN_INFO "ibmvscsi: SRP_LOGIN succeeded\n");
+
+ if (evt_struct->xfer_iu->srp.login_rsp.request_limit_delta >
+ (max_requests - 2))
+ evt_struct->xfer_iu->srp.login_rsp.request_limit_delta =
+ max_requests - 2;
+
+ /* Now we know what the real request-limit is */
+ atomic_set(&hostdata->request_limit,
+ evt_struct->xfer_iu->srp.login_rsp.request_limit_delta);
+
+ hostdata->host->can_queue =
+ evt_struct->xfer_iu->srp.login_rsp.request_limit_delta - 2;
+
+ if (hostdata->host->can_queue < 1) {
+ printk(KERN_ERR "ibmvscsi: Invalid request_limit_delta\n");
+ return;
+ }
+
+ send_mad_adapter_info(hostdata);
+ return;
+}
+
+/**
+ * send_srp_login: - Sends the srp login
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * Returns zero if successful.
+*/
+static int send_srp_login(struct ibmvscsi_host_data *hostdata)
+{
+ int rc;
+ unsigned long flags;
+ struct srp_login_req *login;
+ struct srp_event_struct *evt_struct = get_event_struct(&hostdata->pool);
+ if (!evt_struct) {
+ printk(KERN_ERR
+ "ibmvscsi: couldn't allocate an event for login req!\n");
+ return FAILED;
+ }
+
+ init_event_struct(evt_struct,
+ login_rsp,
+ VIOSRP_SRP_FORMAT,
+ init_timeout * HZ);
+
+ login = &evt_struct->iu.srp.login_req;
+ login->type = SRP_LOGIN_REQ_TYPE;
+ login->max_requested_initiator_to_target_iulen = sizeof(union srp_iu);
+ login->required_buffer_formats = 0x0006;
+
+ /* Start out with a request limit of 1, since this is negotiated in
+ * the login request we are just sending
+ */
+ atomic_set(&hostdata->request_limit, 1);
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ rc = ibmvscsi_send_srp_event(evt_struct, hostdata);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ return rc;
+};
+
+/**
+ * sync_completion: Signal that a synchronous command has completed
+ * Note that after returning from this call, the evt_struct is freed.
+ * the caller waiting on this completion shouldn't touch the evt_struct
+ * again.
+ */
+static void sync_completion(struct srp_event_struct *evt_struct)
+{
+ /* copy the response back */
+ if (evt_struct->sync_srp)
+ *evt_struct->sync_srp = *evt_struct->xfer_iu;
+
+ complete(&evt_struct->comp);
+}
+
+/**
+ * ibmvscsi_abort: Abort a command...from scsi host template
+ * send this over to the server and wait synchronously for the response
+ */
+static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
+{
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)cmd->device->host->hostdata;
+ struct srp_tsk_mgmt *tsk_mgmt;
+ struct srp_event_struct *evt;
+ struct srp_event_struct *tmp_evt, *found_evt;
+ union viosrp_iu srp_rsp;
+ int rsp_rc;
+ u16 lun = lun_from_dev(cmd->device);
+
+ /* First, find this command in our sent list so we can figure
+ * out the correct tag
+ */
+ found_evt = NULL;
+ list_for_each_entry(tmp_evt, &hostdata->sent, list) {
+ if (tmp_evt->cmnd == cmd) {
+ found_evt = tmp_evt;
+ break;
+ }
+ }
+
+ if (!found_evt)
+ return FAILED;
+
+ evt = get_event_struct(&hostdata->pool);
+ if (evt == NULL) {
+ printk(KERN_ERR "ibmvscsi: failed to allocate abort event\n");
+ return FAILED;
+ }
+
+ init_event_struct(evt,
+ sync_completion,
+ VIOSRP_SRP_FORMAT,
+ init_timeout * HZ);
+
+ tsk_mgmt = &evt->iu.srp.tsk_mgmt;
+
+ /* Set up an abort SRP command */
+ memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
+ tsk_mgmt->type = SRP_TSK_MGMT_TYPE;
+ tsk_mgmt->lun = ((u64) lun) << 48;
+ tsk_mgmt->task_mgmt_flags = 0x01; /* ABORT TASK */
+ tsk_mgmt->managed_task_tag = (u64) found_evt;
+
+ printk(KERN_INFO "ibmvscsi: aborting command. lun 0x%lx, tag 0x%lx\n",
+ tsk_mgmt->lun, tsk_mgmt->managed_task_tag);
+
+ evt->sync_srp = &srp_rsp;
+ init_completion(&evt->comp);
+ if (ibmvscsi_send_srp_event(evt, hostdata) != 0) {
+ printk(KERN_ERR "ibmvscsi: failed to send abort() event\n");
+ return FAILED;
+ }
+
+ spin_unlock_irq(hostdata->host->host_lock);
+ wait_for_completion(&evt->comp);
+ spin_lock_irq(hostdata->host->host_lock);
+
+ /* make sure we got a good response */
+ if (unlikely(srp_rsp.srp.generic.type != SRP_RSP_TYPE)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: abort bad SRP RSP type %d\n",
+ srp_rsp.srp.generic.type);
+ return FAILED;
+ }
+
+ if (srp_rsp.srp.rsp.rspvalid)
+ rsp_rc = *((int *)srp_rsp.srp.rsp.sense_and_response_data);
+ else
+ rsp_rc = srp_rsp.srp.rsp.status;
+
+ if (rsp_rc) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: abort code %d for task tag 0x%lx\n",
+ rsp_rc,
+ tsk_mgmt->managed_task_tag);
+ return FAILED;
+ }
+
+ /* Because we dropped the spinlock above, it's possible
+ * The event is no longer in our list. Make sure it didn't
+ * complete while we were aborting
+ */
+ found_evt = NULL;
+ list_for_each_entry(tmp_evt, &hostdata->sent, list) {
+ if (tmp_evt->cmnd == cmd) {
+ found_evt = tmp_evt;
+ break;
+ }
+ }
+
+ if (found_evt == NULL) {
+ printk(KERN_INFO
+ "ibmvscsi: aborted task tag 0x%lx completed\n",
+ tsk_mgmt->managed_task_tag);
+ return SUCCESS;
+ }
+
+ printk(KERN_INFO
+ "ibmvscsi: successfully aborted task tag 0x%lx\n",
+ tsk_mgmt->managed_task_tag);
+
+ cmd->result = (DID_ABORT << 16);
+ list_del(&found_evt->list);
+ unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt->hostdata->dev);
+ free_event_struct(&found_evt->hostdata->pool, found_evt);
+ atomic_inc(&hostdata->request_limit);
+ return SUCCESS;
+}
+
+/**
+ * ibmvscsi_eh_device_reset_handler: Reset a single LUN...from scsi host
+ * template send this over to the server and wait synchronously for the
+ * response
+ */
+static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
+{
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)cmd->device->host->hostdata;
+
+ struct srp_tsk_mgmt *tsk_mgmt;
+ struct srp_event_struct *evt;
+ struct srp_event_struct *tmp_evt, *pos;
+ union viosrp_iu srp_rsp;
+ int rsp_rc;
+ u16 lun = lun_from_dev(cmd->device);
+
+ evt = get_event_struct(&hostdata->pool);
+ if (evt == NULL) {
+ printk(KERN_ERR "ibmvscsi: failed to allocate reset event\n");
+ return FAILED;
+ }
+
+ init_event_struct(evt,
+ sync_completion,
+ VIOSRP_SRP_FORMAT,
+ init_timeout * HZ);
+
+ tsk_mgmt = &evt->iu.srp.tsk_mgmt;
+
+ /* Set up a lun reset SRP command */
+ memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
+ tsk_mgmt->type = SRP_TSK_MGMT_TYPE;
+ tsk_mgmt->lun = ((u64) lun) << 48;
+ tsk_mgmt->task_mgmt_flags = 0x08; /* LUN RESET */
+
+ printk(KERN_INFO "ibmvscsi: resetting device. lun 0x%lx\n",
+ tsk_mgmt->lun);
+
+ evt->sync_srp = &srp_rsp;
+ init_completion(&evt->comp);
+ if (ibmvscsi_send_srp_event(evt, hostdata) != 0) {
+ printk(KERN_ERR "ibmvscsi: failed to send reset event\n");
+ return FAILED;
+ }
+
+ spin_unlock_irq(hostdata->host->host_lock);
+ wait_for_completion(&evt->comp);
+ spin_lock_irq(hostdata->host->host_lock);
+
+ /* make sure we got a good response */
+ if (unlikely(srp_rsp.srp.generic.type != SRP_RSP_TYPE)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: reset bad SRP RSP type %d\n",
+ srp_rsp.srp.generic.type);
+ return FAILED;
+ }
+
+ if (srp_rsp.srp.rsp.rspvalid)
+ rsp_rc = *((int *)srp_rsp.srp.rsp.sense_and_response_data);
+ else
+ rsp_rc = srp_rsp.srp.rsp.status;
+
+ if (rsp_rc) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: reset code %d for task tag 0x%lx\n",
+ rsp_rc,
+ tsk_mgmt->managed_task_tag);
+ return FAILED;
+ }
+
+ /* We need to find all commands for this LUN that have not yet been
+ * responded to, and fail them with DID_RESET
+ */
+ list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
+ if ((tmp_evt->cmnd) && (tmp_evt->cmnd->device == cmd->device)) {
+ if (tmp_evt->cmnd)
+ tmp_evt->cmnd->result = (DID_RESET << 16);
+ list_del(&tmp_evt->list);
+ unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt->hostdata->dev);
+ free_event_struct(&tmp_evt->hostdata->pool,
+ tmp_evt);
+ atomic_inc(&hostdata->request_limit);
+ if (tmp_evt->cmnd_done)
+ tmp_evt->cmnd_done(tmp_evt->cmnd);
+ else if (tmp_evt->done)
+ tmp_evt->done(tmp_evt);
+ }
+ }
+ return SUCCESS;
+}
+
+/**
+ * purge_requests: Our virtual adapter just shut down. purge any sent requests
+ * @hostdata: the adapter
+ */
+static void purge_requests(struct ibmvscsi_host_data *hostdata)
+{
+ struct srp_event_struct *tmp_evt, *pos;
+ unsigned long flags;
+
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
+ list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
+ list_del(&tmp_evt->list);
+ if (tmp_evt->cmnd) {
+ tmp_evt->cmnd->result = (DID_ERROR << 16);
+ unmap_cmd_data(&tmp_evt->iu.srp.cmd,
+ tmp_evt->hostdata->dev);
+ if (tmp_evt->cmnd_done)
+ tmp_evt->cmnd_done(tmp_evt->cmnd);
+ } else {
+ if (tmp_evt->done) {
+ tmp_evt->done(tmp_evt);
+ }
+ }
+ free_event_struct(&tmp_evt->hostdata->pool, tmp_evt);
+ }
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+}
+
+/**
+ * ibmvscsi_handle_crq: - Handles and frees received events in the CRQ
+ * @crq: Command/Response queue
+ * @hostdata: ibmvscsi_host_data of host
+ *
+*/
+void ibmvscsi_handle_crq(struct viosrp_crq *crq,
+ struct ibmvscsi_host_data *hostdata)
+{
+ unsigned long flags;
+ struct srp_event_struct *evt_struct =
+ (struct srp_event_struct *)crq->IU_data_ptr;
+ switch (crq->valid) {
+ case 0xC0: /* initialization */
+ switch (crq->format) {
+ case 0x01: /* Initialization message */
+ printk(KERN_INFO "ibmvscsi: partner initialized\n");
+ /* Send back a response */
+ if (ibmvscsi_send_crq(hostdata,
+ 0xC002000000000000LL, 0) == 0) {
+ /* Now login */
+ send_srp_login(hostdata);
+ } else {
+ printk(KERN_ERR
+ "ibmvscsi: Unable to send init rsp\n");
+ }
+
+ break;
+ case 0x02: /* Initialization response */
+ printk(KERN_INFO
+ "ibmvscsi: partner initialization complete\n");
+
+ /* Now login */
+ send_srp_login(hostdata);
+ break;
+ default:
+ printk(KERN_ERR "ibmvscsi: unknown crq message type\n");
+ }
+ return;
+ case 0xFF: /* Hypervisor telling us the connection is closed */
+ printk(KERN_INFO "ibmvscsi: Virtual adapter failed!\n");
+
+ atomic_set(&hostdata->request_limit, -1);
+ purge_requests(hostdata);
+ ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata);
+ return;
+ case 0x80: /* real payload */
+ break;
+ default:
+ printk(KERN_ERR
+ "ibmvscsi: got an invalid message type 0x%02x\n",
+ crq->valid);
+ return;
+ }
+
+ /* The only kind of payload CRQs we should get are responses to
+ * things we send. Make sure this response is to something we
+ * actually sent
+ */
+ if (!valid_event_struct(&hostdata->pool, evt_struct)) {
+ printk(KERN_ERR
+ "ibmvscsi: returned correlation_token 0x%p is invalid!\n",
+ (void *)crq->IU_data_ptr);
+ return;
+ }
+
+ if (atomic_read(&evt_struct->free)) {
+ printk(KERN_ERR
+ "ibmvscsi: received duplicate correlation_token 0x%p!\n",
+ (void *)crq->IU_data_ptr);
+ return;
+ }
+
+ if (crq->format == VIOSRP_SRP_FORMAT)
+ atomic_add(evt_struct->xfer_iu->srp.rsp.request_limit_delta,
+ &hostdata->request_limit);
+
+ if (evt_struct->done)
+ evt_struct->done(evt_struct);
+ else
+ printk(KERN_ERR
+ "ibmvscsi: returned done() is NULL; not running it!\n");
+
+ /*
+ * Lock the host_lock before messing with these structures, since we
+ * are running in a task context
+ */
+ spin_lock_irqsave(evt_struct->hostdata->host->host_lock, flags);
+ list_del(&evt_struct->list);
+ free_event_struct(&evt_struct->hostdata->pool, evt_struct);
+ spin_unlock_irqrestore(evt_struct->hostdata->host->host_lock, flags);
+}
+
+/**
+ * ibmvscsi_get_host_config: Send the command to the server to get host
+ * configuration data. The data is opaque to us.
+ */
+static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
+ unsigned char *buffer, int length)
+{
+ struct viosrp_host_config *host_config;
+ struct srp_event_struct *evt_struct;
+ int rc;
+
+ evt_struct = get_event_struct(&hostdata->pool);
+ if (!evt_struct) {
+ printk(KERN_ERR
+ "ibmvscsi: could't allocate event for HOST_CONFIG!\n");
+ return -1;
+ }
+
+ init_event_struct(evt_struct,
+ sync_completion,
+ VIOSRP_MAD_FORMAT,
+ init_timeout * HZ);
+
+ host_config = &evt_struct->iu.mad.host_config;
+
+ /* Set up a lun reset SRP command */
+ memset(host_config, 0x00, sizeof(*host_config));
+ host_config->common.type = VIOSRP_HOST_CONFIG_TYPE;
+ host_config->common.length = length;
+ host_config->buffer = dma_map_single(hostdata->dev, buffer, length,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(host_config->buffer)) {
+ printk(KERN_ERR
+ "ibmvscsi: dma_mapping error " "getting host config\n");
+ free_event_struct(&hostdata->pool, evt_struct);
+ return -1;
+ }
+
+ init_completion(&evt_struct->comp);
+ rc = ibmvscsi_send_srp_event(evt_struct, hostdata);
+ if (rc == 0) {
+ wait_for_completion(&evt_struct->comp);
+ dma_unmap_single(hostdata->dev, host_config->buffer,
+ length, DMA_BIDIRECTIONAL);
+ }
+
+ return rc;
+}
+
+/* ------------------------------------------------------------
+ * sysfs attributes
+ */
+static ssize_t show_host_srp_version(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)shost->hostdata;
+ int len;
+
+ len = snprintf(buf, PAGE_SIZE, "%s\n",
+ hostdata->madapter_info.srp_version);
+ return len;
+}
+
+static struct class_device_attribute ibmvscsi_host_srp_version = {
+ .attr = {
+ .name = "srp_version",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_srp_version,
+};
+
+static ssize_t show_host_partition_name(struct class_device *class_dev,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)shost->hostdata;
+ int len;
+
+ len = snprintf(buf, PAGE_SIZE, "%s\n",
+ hostdata->madapter_info.partition_name);
+ return len;
+}
+
+static struct class_device_attribute ibmvscsi_host_partition_name = {
+ .attr = {
+ .name = "partition_name",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_partition_name,
+};
+
+static ssize_t show_host_partition_number(struct class_device *class_dev,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)shost->hostdata;
+ int len;
+
+ len = snprintf(buf, PAGE_SIZE, "%d\n",
+ hostdata->madapter_info.partition_number);
+ return len;
+}
+
+static struct class_device_attribute ibmvscsi_host_partition_number = {
+ .attr = {
+ .name = "partition_number",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_partition_number,
+};
+
+static ssize_t show_host_mad_version(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)shost->hostdata;
+ int len;
+
+ len = snprintf(buf, PAGE_SIZE, "%d\n",
+ hostdata->madapter_info.mad_version);
+ return len;
+}
+
+static struct class_device_attribute ibmvscsi_host_mad_version = {
+ .attr = {
+ .name = "mad_version",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_mad_version,
+};
+
+static ssize_t show_host_os_type(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)shost->hostdata;
+ int len;
+
+ len = snprintf(buf, PAGE_SIZE, "%d\n", hostdata->madapter_info.os_type);
+ return len;
+}
+
+static struct class_device_attribute ibmvscsi_host_os_type = {
+ .attr = {
+ .name = "os_type",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_os_type,
+};
+
+static ssize_t show_host_config(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)shost->hostdata;
+
+ /* returns null-terminated host config data */
+ if (ibmvscsi_do_host_config(hostdata, buf, PAGE_SIZE) == 0)
+ return strlen(buf);
+ else
+ return 0;
+}
+
+static struct class_device_attribute ibmvscsi_host_config = {
+ .attr = {
+ .name = "config",
+ .mode = S_IRUGO,
+ },
+ .show = show_host_config,
+};
+
+static struct class_device_attribute *ibmvscsi_attrs[] = {
+ &ibmvscsi_host_srp_version,
+ &ibmvscsi_host_partition_name,
+ &ibmvscsi_host_partition_number,
+ &ibmvscsi_host_mad_version,
+ &ibmvscsi_host_os_type,
+ &ibmvscsi_host_config,
+ NULL
+};
+
+/* ------------------------------------------------------------
+ * SCSI driver registration
+ */
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .name = "IBM POWER Virtual SCSI Adapter " IBMVSCSI_VERSION,
+ .proc_name = "ibmvscsi",
+ .queuecommand = ibmvscsi_queuecommand,
+ .eh_abort_handler = ibmvscsi_eh_abort_handler,
+ .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler,
+ .cmd_per_lun = 16,
+ .can_queue = 1, /* Updated after SRP_LOGIN */
+ .this_id = -1,
+ .sg_tablesize = MAX_INDIRECT_BUFS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = ibmvscsi_attrs,
+};
+
+/**
+ * Called by bus code for each adapter
+ */
+static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
+{
+ struct ibmvscsi_host_data *hostdata;
+ struct Scsi_Host *host;
+ struct device *dev = &vdev->dev;
+ unsigned long wait_switch = 0;
+
+ vdev->dev.driver_data = NULL;
+
+ host = scsi_host_alloc(&driver_template, sizeof(*hostdata));
+ if (!host) {
+ printk(KERN_ERR "ibmvscsi: couldn't allocate host data\n");
+ goto scsi_host_alloc_failed;
+ }
+
+ hostdata = (struct ibmvscsi_host_data *)host->hostdata;
+ memset(hostdata, 0x00, sizeof(*hostdata));
+ INIT_LIST_HEAD(&hostdata->sent);
+ hostdata->host = host;
+ hostdata->dev = dev;
+ atomic_set(&hostdata->request_limit, -1);
+ hostdata->host->max_sectors = 32 * 8; /* default max I/O 32 pages */
+
+ if (ibmvscsi_init_crq_queue(&hostdata->queue, hostdata,
+ max_requests) != 0) {
+ printk(KERN_ERR "ibmvscsi: couldn't initialize crq\n");
+ goto init_crq_failed;
+ }
+ if (initialize_event_pool(&hostdata->pool, max_requests, hostdata) != 0) {
+ printk(KERN_ERR "ibmvscsi: couldn't initialize event pool\n");
+ goto init_pool_failed;
+ }
+
+ host->max_lun = 8;
+ host->max_id = max_id;
+ host->max_channel = max_channel;
+
+ if (scsi_add_host(hostdata->host, hostdata->dev))
+ goto add_host_failed;
+
+ /* Try to send an initialization message. Note that this is allowed
+ * to fail if the other end is not acive. In that case we don't
+ * want to scan
+ */
+ if (ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0) == 0) {
+ /*
+ * Wait around max init_timeout secs for the adapter to finish
+ * initializing. When we are done initializing, we will have a
+ * valid request_limit. We don't want Linux scanning before
+ * we are ready.
+ */
+ for (wait_switch = jiffies + (init_timeout * HZ);
+ time_before(jiffies, wait_switch) &&
+ atomic_read(&hostdata->request_limit) < 2;) {
+
+ msleep(10);
+ }
+
+ /* if we now have a valid request_limit, initiate a scan */
+ if (atomic_read(&hostdata->request_limit) > 0)
+ scsi_scan_host(host);
+ }
+
+ vdev->dev.driver_data = hostdata;
+ return 0;
+
+ add_host_failed:
+ release_event_pool(&hostdata->pool, hostdata);
+ init_pool_failed:
+ ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, max_requests);
+ init_crq_failed:
+ scsi_host_put(host);
+ scsi_host_alloc_failed:
+ return -1;
+}
+
+static int ibmvscsi_remove(struct vio_dev *vdev)
+{
+ struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data;
+ release_event_pool(&hostdata->pool, hostdata);
+ ibmvscsi_release_crq_queue(&hostdata->queue, hostdata,
+ max_requests);
+
+ scsi_remove_host(hostdata->host);
+ scsi_host_put(hostdata->host);
+
+ return 0;
+}
+
+/**
+ * ibmvscsi_device_table: Used by vio.c to match devices in the device tree we
+ * support.
+ */
+static struct vio_device_id ibmvscsi_device_table[] __devinitdata = {
+ {"vscsi", "IBM,v-scsi"},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table);
+static struct vio_driver ibmvscsi_driver = {
+ .name = "ibmvscsi",
+ .id_table = ibmvscsi_device_table,
+ .probe = ibmvscsi_probe,
+ .remove = ibmvscsi_remove
+};
+
+int __init ibmvscsi_module_init(void)
+{
+ return vio_register_driver(&ibmvscsi_driver);
+}
+
+void __exit ibmvscsi_module_exit(void)
+{
+ vio_unregister_driver(&ibmvscsi_driver);
+}
+
+module_init(ibmvscsi_module_init);
+module_exit(ibmvscsi_module_exit);
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h
new file mode 100644
index 000000000000..1030b703c30e
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.h
@@ -0,0 +1,109 @@
+/* ------------------------------------------------------------
+ * ibmvscsi.h
+ * (C) Copyright IBM Corporation 1994, 2003
+ * Authors: Colin DeVilbiss (devilbis@us.ibm.com)
+ * Santiago Leon (santil@us.ibm.com)
+ * Dave Boutcher (sleddog@us.ibm.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
+ *
+ * ------------------------------------------------------------
+ * Emulation of a SCSI host adapter for Virtual I/O devices
+ *
+ * This driver allows the Linux SCSI peripheral drivers to directly
+ * access devices in the hosting partition, either on an iSeries
+ * hypervisor system or a converged hypervisor system.
+ */
+#ifndef IBMVSCSI_H
+#define IBMVSCSI_H
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include "viosrp.h"
+
+struct scsi_cmnd;
+struct Scsi_Host;
+
+/* Number of indirect bufs...the list of these has to fit in the
+ * additional data of the srp_cmd struct along with the indirect
+ * descriptor
+ */
+#define MAX_INDIRECT_BUFS 10
+
+/* ------------------------------------------------------------
+ * Data Structures
+ */
+/* an RPA command/response transport queue */
+struct crq_queue {
+ struct viosrp_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ spinlock_t lock;
+};
+
+/* a unit of work for the hosting partition */
+struct srp_event_struct {
+ union viosrp_iu *xfer_iu;
+ struct scsi_cmnd *cmnd;
+ struct list_head list;
+ void (*done) (struct srp_event_struct *);
+ struct viosrp_crq crq;
+ struct ibmvscsi_host_data *hostdata;
+ atomic_t free;
+ union viosrp_iu iu;
+ void (*cmnd_done) (struct scsi_cmnd *);
+ struct completion comp;
+ union viosrp_iu *sync_srp;
+};
+
+/* a pool of event structs for use */
+struct event_pool {
+ struct srp_event_struct *events;
+ u32 size;
+ int next;
+ union viosrp_iu *iu_storage;
+ dma_addr_t iu_token;
+};
+
+/* all driver data associated with a host adapter */
+struct ibmvscsi_host_data {
+ atomic_t request_limit;
+ struct device *dev;
+ struct event_pool pool;
+ struct crq_queue queue;
+ struct tasklet_struct srp_task;
+ struct list_head sent;
+ struct Scsi_Host *host;
+ struct mad_adapter_info_data madapter_info;
+};
+
+/* routines for managing a command/response queue */
+int ibmvscsi_init_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests);
+void ibmvscsi_release_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests);
+void ibmvscsi_reset_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata);
+
+void ibmvscsi_handle_crq(struct viosrp_crq *crq,
+ struct ibmvscsi_host_data *hostdata);
+int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata,
+ u64 word1, u64 word2);
+
+#endif /* IBMVSCSI_H */
diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c
new file mode 100644
index 000000000000..e9202f2a8276
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/iseries_vscsi.c
@@ -0,0 +1,144 @@
+/* ------------------------------------------------------------
+ * iSeries_vscsi.c
+ * (C) Copyright IBM Corporation 1994, 2003
+ * Authors: Colin DeVilbiss (devilbis@us.ibm.com)
+ * Santiago Leon (santil@us.ibm.com)
+ * Dave Boutcher (sleddog@us.ibm.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
+ *
+ * ------------------------------------------------------------
+ * iSeries-specific functions of the SCSI host adapter for Virtual I/O devices
+ *
+ * This driver allows the Linux SCSI peripheral drivers to directly
+ * access devices in the hosting partition, either on an iSeries
+ * hypervisor system or a converged hypervisor system.
+ */
+
+#include <asm/iSeries/vio.h>
+#include <asm/iSeries/HvLpEvent.h>
+#include <asm/iSeries/HvTypes.h>
+#include <asm/iSeries/HvLpConfig.h>
+#include <asm/vio.h>
+#include <linux/device.h>
+#include "ibmvscsi.h"
+
+/* global variables */
+static struct ibmvscsi_host_data *single_host_data;
+
+/* ------------------------------------------------------------
+ * Routines for direct interpartition interaction
+ */
+struct srp_lp_event {
+ struct HvLpEvent lpevt; /* 0x00-0x17 */
+ u32 reserved1; /* 0x18-0x1B; unused */
+ u16 version; /* 0x1C-0x1D; unused */
+ u16 subtype_rc; /* 0x1E-0x1F; unused */
+ struct viosrp_crq crq; /* 0x20-0x3F */
+};
+
+/**
+ * standard interface for handling logical partition events.
+ */
+static void ibmvscsi_handle_event(struct HvLpEvent *lpevt)
+{
+ struct srp_lp_event *evt = (struct srp_lp_event *)lpevt;
+
+ if (!evt) {
+ printk(KERN_ERR "ibmvscsi: received null event\n");
+ return;
+ }
+
+ if (single_host_data == NULL) {
+ printk(KERN_ERR
+ "ibmvscsi: received event, no adapter present\n");
+ return;
+ }
+
+ ibmvscsi_handle_crq(&evt->crq, single_host_data);
+}
+
+/* ------------------------------------------------------------
+ * Routines for driver initialization
+ */
+int ibmvscsi_init_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests)
+{
+ int rc;
+
+ single_host_data = hostdata;
+ rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, 0);
+ if (rc < 0) {
+ printk("viopath_open failed with rc %d in open_event_path\n",
+ rc);
+ goto viopath_open_failed;
+ }
+
+ rc = vio_setHandler(viomajorsubtype_scsi, ibmvscsi_handle_event);
+ if (rc < 0) {
+ printk("vio_setHandler failed with rc %d in open_event_path\n",
+ rc);
+ goto vio_setHandler_failed;
+ }
+ return 0;
+
+ vio_setHandler_failed:
+ viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests);
+ viopath_open_failed:
+ return -1;
+}
+
+void ibmvscsi_release_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests)
+{
+ vio_clearHandler(viomajorsubtype_scsi);
+ viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests);
+}
+
+/**
+ * reset_crq_queue: - resets a crq after a failure
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * no-op for iSeries
+ */
+void ibmvscsi_reset_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata)
+{
+}
+
+/**
+ * ibmvscsi_send_crq: - Send a CRQ
+ * @hostdata: the adapter
+ * @word1: the first 64 bits of the data
+ * @word2: the second 64 bits of the data
+ */
+int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, u64 word1, u64 word2)
+{
+ single_host_data = hostdata;
+ return HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_scsi,
+ HvLpEvent_AckInd_NoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ 0,
+ VIOVERSION << 16, word1, word2, 0,
+ 0);
+}
diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c
new file mode 100644
index 000000000000..50cb909f314f
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c
@@ -0,0 +1,260 @@
+/* ------------------------------------------------------------
+ * rpa_vscsi.c
+ * (C) Copyright IBM Corporation 1994, 2003
+ * Authors: Colin DeVilbiss (devilbis@us.ibm.com)
+ * Santiago Leon (santil@us.ibm.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
+ *
+ * ------------------------------------------------------------
+ * RPA-specific functions of the SCSI host adapter for Virtual I/O devices
+ *
+ * This driver allows the Linux SCSI peripheral drivers to directly
+ * access devices in the hosting partition, either on an iSeries
+ * hypervisor system or a converged hypervisor system.
+ */
+
+#include <asm/vio.h>
+#include <asm/iommu.h>
+#include <asm/hvcall.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include "ibmvscsi.h"
+
+/* ------------------------------------------------------------
+ * Routines for managing the command/response queue
+ */
+/**
+ * ibmvscsi_handle_event: - Interrupt handler for crq events
+ * @irq: number of irq to handle, not used
+ * @dev_instance: ibmvscsi_host_data of host that received interrupt
+ * @regs: pt_regs with registers
+ *
+ * Disables interrupts and schedules srp_task
+ * Always returns IRQ_HANDLED
+ */
+static irqreturn_t ibmvscsi_handle_event(int irq,
+ void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct ibmvscsi_host_data *hostdata =
+ (struct ibmvscsi_host_data *)dev_instance;
+ vio_disable_interrupts(to_vio_dev(hostdata->dev));
+ tasklet_schedule(&hostdata->srp_task);
+ return IRQ_HANDLED;
+}
+
+/**
+ * release_crq_queue: - Deallocates data and unregisters CRQ
+ * @queue: crq_queue to initialize and register
+ * @host_data: ibmvscsi_host_data of host
+ *
+ * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
+ * the crq with the hypervisor.
+ */
+void ibmvscsi_release_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests)
+{
+ long rc;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+ free_irq(vdev->irq, (void *)hostdata);
+ tasklet_kill(&hostdata->srp_task);
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while ((rc == H_Busy) || (H_isLongBusy(rc)));
+ dma_unmap_single(hostdata->dev,
+ queue->msg_token,
+ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+ free_page((unsigned long)queue->msgs);
+}
+
+/**
+ * crq_queue_next_crq: - Returns the next entry in message queue
+ * @queue: crq_queue to use
+ *
+ * Returns pointer to next entry in queue, or NULL if there are no new
+ * entried in the CRQ.
+ */
+static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue)
+{
+ struct viosrp_crq *crq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ crq = &queue->msgs[queue->cur];
+ if (crq->valid & 0x80) {
+ if (++queue->cur == queue->size)
+ queue->cur = 0;
+ } else
+ crq = NULL;
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return crq;
+}
+
+/**
+ * ibmvscsi_send_crq: - Send a CRQ
+ * @hostdata: the adapter
+ * @word1: the first 64 bits of the data
+ * @word2: the second 64 bits of the data
+ */
+int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, u64 word1, u64 word2)
+{
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
+}
+
+/**
+ * ibmvscsi_task: - Process srps asynchronously
+ * @data: ibmvscsi_host_data of host
+ */
+static void ibmvscsi_task(void *data)
+{
+ struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+ struct viosrp_crq *crq;
+ int done = 0;
+
+ while (!done) {
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) {
+ ibmvscsi_handle_crq(crq, hostdata);
+ crq->valid = 0x00;
+ }
+
+ vio_enable_interrupts(vdev);
+ if ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) {
+ vio_disable_interrupts(vdev);
+ ibmvscsi_handle_crq(crq, hostdata);
+ crq->valid = 0x00;
+ } else {
+ done = 1;
+ }
+ }
+}
+
+/**
+ * initialize_crq_queue: - Initializes and registers CRQ with hypervisor
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ * Allocates a page for messages, maps it for dma, and registers
+ * the crq with the hypervisor.
+ * Returns zero on success.
+ */
+int ibmvscsi_init_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata,
+ int max_requests)
+{
+ int rc;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
+
+ if (!queue->msgs)
+ goto malloc_failed;
+ queue->size = PAGE_SIZE / sizeof(*queue->msgs);
+
+ queue->msg_token = dma_map_single(hostdata->dev, queue->msgs,
+ queue->size * sizeof(*queue->msgs),
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(queue->msg_token))
+ goto map_failed;
+
+ rc = plpar_hcall_norets(H_REG_CRQ,
+ vdev->unit_address,
+ queue->msg_token, PAGE_SIZE);
+ if (rc == 2) {
+ /* Adapter is good, but other end is not ready */
+ printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n");
+ } else if (rc != 0) {
+ printk(KERN_WARNING "ibmvscsi: Error %d opening adapter\n", rc);
+ goto reg_crq_failed;
+ }
+
+ if (request_irq(vdev->irq,
+ ibmvscsi_handle_event,
+ 0, "ibmvscsi", (void *)hostdata) != 0) {
+ printk(KERN_ERR "ibmvscsi: couldn't register irq 0x%x\n",
+ vdev->irq);
+ goto req_irq_failed;
+ }
+
+ rc = vio_enable_interrupts(vdev);
+ if (rc != 0) {
+ printk(KERN_ERR "ibmvscsi: Error %d enabling interrupts!!!\n",
+ rc);
+ goto req_irq_failed;
+ }
+
+ queue->cur = 0;
+ spin_lock_init(&queue->lock);
+
+ tasklet_init(&hostdata->srp_task, (void *)ibmvscsi_task,
+ (unsigned long)hostdata);
+
+ return 0;
+
+ req_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while ((rc == H_Busy) || (H_isLongBusy(rc)));
+ reg_crq_failed:
+ dma_unmap_single(hostdata->dev,
+ queue->msg_token,
+ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+ map_failed:
+ free_page((unsigned long)queue->msgs);
+ malloc_failed:
+ return -1;
+}
+
+/**
+ * reset_crq_queue: - resets a crq after a failure
+ * @queue: crq_queue to initialize and register
+ * @hostdata: ibmvscsi_host_data of host
+ *
+ */
+void ibmvscsi_reset_crq_queue(struct crq_queue *queue,
+ struct ibmvscsi_host_data *hostdata)
+{
+ int rc;
+ struct vio_dev *vdev = to_vio_dev(hostdata->dev);
+
+ /* Close the CRQ */
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while ((rc == H_Busy) || (H_isLongBusy(rc)));
+
+ /* Clean out the queue */
+ memset(queue->msgs, 0x00, PAGE_SIZE);
+ queue->cur = 0;
+
+ /* And re-open it again */
+ rc = plpar_hcall_norets(H_REG_CRQ,
+ vdev->unit_address,
+ queue->msg_token, PAGE_SIZE);
+ if (rc == 2) {
+ /* Adapter is good, but other end is not ready */
+ printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n");
+ } else if (rc != 0) {
+ printk(KERN_WARNING
+ "ibmvscsi: couldn't register crq--rc 0x%x\n", rc);
+ }
+}
diff --git a/drivers/scsi/ibmvscsi/srp.h b/drivers/scsi/ibmvscsi/srp.h
new file mode 100644
index 000000000000..e952c1cd9740
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/srp.h
@@ -0,0 +1,225 @@
+/*****************************************************************************/
+/* srp.h -- SCSI RDMA Protocol definitions */
+/* */
+/* Written By: Colin Devilbis, IBM Corporation */
+/* */
+/* Copyright (C) 2003 IBM Corporation */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* */
+/* */
+/* This file contains structures and definitions for the SCSI RDMA Protocol */
+/* (SRP) as defined in the T10 standard available at www.t10.org. This */
+/* file was based on the 16a version of the standard */
+/* */
+/*****************************************************************************/
+#ifndef SRP_H
+#define SRP_H
+
+#define PACKED __attribute__((packed))
+
+enum srp_types {
+ SRP_LOGIN_REQ_TYPE = 0x00,
+ SRP_LOGIN_RSP_TYPE = 0xC0,
+ SRP_LOGIN_REJ_TYPE = 0x80,
+ SRP_I_LOGOUT_TYPE = 0x03,
+ SRP_T_LOGOUT_TYPE = 0x80,
+ SRP_TSK_MGMT_TYPE = 0x01,
+ SRP_CMD_TYPE = 0x02,
+ SRP_RSP_TYPE = 0xC1,
+ SRP_CRED_REQ_TYPE = 0x81,
+ SRP_CRED_RSP_TYPE = 0x41,
+ SRP_AER_REQ_TYPE = 0x82,
+ SRP_AER_RSP_TYPE = 0x42
+};
+
+enum srp_descriptor_formats {
+ SRP_NO_BUFFER = 0x00,
+ SRP_DIRECT_BUFFER = 0x01,
+ SRP_INDIRECT_BUFFER = 0x02
+};
+
+struct memory_descriptor {
+ u64 virtual_address;
+ u32 memory_handle;
+ u32 length;
+};
+
+struct indirect_descriptor {
+ struct memory_descriptor head;
+ u32 total_length;
+ struct memory_descriptor list[1] PACKED;
+};
+
+struct srp_generic {
+ u8 type;
+ u8 reserved1[7];
+ u64 tag;
+};
+
+struct srp_login_req {
+ u8 type;
+ u8 reserved1[7];
+ u64 tag;
+ u32 max_requested_initiator_to_target_iulen;
+ u32 reserved2;
+ u16 required_buffer_formats;
+ u8 reserved3:6;
+ u8 multi_channel_action:2;
+ u8 reserved4;
+ u32 reserved5;
+ u8 initiator_port_identifier[16];
+ u8 target_port_identifier[16];
+};
+
+struct srp_login_rsp {
+ u8 type;
+ u8 reserved1[3];
+ u32 request_limit_delta;
+ u64 tag;
+ u32 max_initiator_to_target_iulen;
+ u32 max_target_to_initiator_iulen;
+ u16 supported_buffer_formats;
+ u8 reserved2:6;
+ u8 multi_channel_result:2;
+ u8 reserved3;
+ u8 reserved4[24];
+};
+
+struct srp_login_rej {
+ u8 type;
+ u8 reserved1[3];
+ u32 reason;
+ u64 tag;
+ u64 reserved2;
+ u16 supported_buffer_formats;
+ u8 reserved3[6];
+};
+
+struct srp_i_logout {
+ u8 type;
+ u8 reserved1[7];
+ u64 tag;
+};
+
+struct srp_t_logout {
+ u8 type;
+ u8 reserved1[3];
+ u32 reason;
+ u64 tag;
+};
+
+struct srp_tsk_mgmt {
+ u8 type;
+ u8 reserved1[7];
+ u64 tag;
+ u32 reserved2;
+ u64 lun PACKED;
+ u8 reserved3;
+ u8 reserved4;
+ u8 task_mgmt_flags;
+ u8 reserved5;
+ u64 managed_task_tag;
+ u64 reserved6;
+};
+
+struct srp_cmd {
+ u8 type;
+ u32 reserved1 PACKED;
+ u8 data_out_format:4;
+ u8 data_in_format:4;
+ u8 data_out_count;
+ u8 data_in_count;
+ u64 tag;
+ u32 reserved2;
+ u64 lun PACKED;
+ u8 reserved3;
+ u8 reserved4:5;
+ u8 task_attribute:3;
+ u8 reserved5;
+ u8 additional_cdb_len;
+ u8 cdb[16];
+ u8 additional_data[0x100 - 0x30];
+};
+
+struct srp_rsp {
+ u8 type;
+ u8 reserved1[3];
+ u32 request_limit_delta;
+ u64 tag;
+ u16 reserved2;
+ u8 reserved3:2;
+ u8 diunder:1;
+ u8 diover:1;
+ u8 dounder:1;
+ u8 doover:1;
+ u8 snsvalid:1;
+ u8 rspvalid:1;
+ u8 status;
+ u32 data_in_residual_count;
+ u32 data_out_residual_count;
+ u32 sense_data_list_length;
+ u32 response_data_list_length;
+ u8 sense_and_response_data[18];
+};
+
+struct srp_cred_req {
+ u8 type;
+ u8 reserved1[3];
+ u32 request_limit_delta;
+ u64 tag;
+};
+
+struct srp_cred_rsp {
+ u8 type;
+ u8 reserved1[7];
+ u64 tag;
+};
+
+struct srp_aer_req {
+ u8 type;
+ u8 reserved1[3];
+ u32 request_limit_delta;
+ u64 tag;
+ u32 reserved2;
+ u64 lun;
+ u32 sense_data_list_length;
+ u32 reserved3;
+ u8 sense_data[20];
+};
+
+struct srp_aer_rsp {
+ u8 type;
+ u8 reserved1[7];
+ u64 tag;
+};
+
+union srp_iu {
+ struct srp_generic generic;
+ struct srp_login_req login_req;
+ struct srp_login_rsp login_rsp;
+ struct srp_login_rej login_rej;
+ struct srp_i_logout i_logout;
+ struct srp_t_logout t_logout;
+ struct srp_tsk_mgmt tsk_mgmt;
+ struct srp_cmd cmd;
+ struct srp_rsp rsp;
+ struct srp_cred_req cred_req;
+ struct srp_cred_rsp cred_rsp;
+ struct srp_aer_req aer_req;
+ struct srp_aer_rsp aer_rsp;
+};
+
+#endif
diff --git a/drivers/scsi/ibmvscsi/viosrp.h b/drivers/scsi/ibmvscsi/viosrp.h
new file mode 100644
index 000000000000..6a6bba8a2f34
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/viosrp.h
@@ -0,0 +1,126 @@
+/*****************************************************************************/
+/* srp.h -- SCSI RDMA Protocol definitions */
+/* */
+/* Written By: Colin Devilbis, IBM Corporation */
+/* */
+/* Copyright (C) 2003 IBM Corporation */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* */
+/* */
+/* This file contains structures and definitions for IBM RPA (RS/6000 */
+/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */
+/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */
+/* commands between logical partitions. */
+/* */
+/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */
+/* between partitions. The definitions in this file are architected, */
+/* and cannot be changed without breaking compatibility with other versions */
+/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/
+/* between logical partitions */
+/*****************************************************************************/
+#ifndef VIOSRP_H
+#define VIOSRP_H
+#include "srp.h"
+
+enum viosrp_crq_formats {
+ VIOSRP_SRP_FORMAT = 0x01,
+ VIOSRP_MAD_FORMAT = 0x02,
+ VIOSRP_OS400_FORMAT = 0x03,
+ VIOSRP_AIX_FORMAT = 0x04,
+ VIOSRP_LINUX_FORMAT = 0x06,
+ VIOSRP_INLINE_FORMAT = 0x07
+};
+
+struct viosrp_crq {
+ u8 valid; /* used by RPA */
+ u8 format; /* SCSI vs out-of-band */
+ u8 reserved;
+ u8 status; /* non-scsi failure? (e.g. DMA failure) */
+ u16 timeout; /* in seconds */
+ u16 IU_length; /* in bytes */
+ u64 IU_data_ptr; /* the TCE for transferring data */
+};
+
+/* MADs are Management requests above and beyond the IUs defined in the SRP
+ * standard.
+ */
+enum viosrp_mad_types {
+ VIOSRP_EMPTY_IU_TYPE = 0x01,
+ VIOSRP_ERROR_LOG_TYPE = 0x02,
+ VIOSRP_ADAPTER_INFO_TYPE = 0x03,
+ VIOSRP_HOST_CONFIG_TYPE = 0x04
+};
+
+/*
+ * Common MAD header
+ */
+struct mad_common {
+ u32 type;
+ u16 status;
+ u16 length;
+ u64 tag;
+};
+
+/*
+ * All SRP (and MAD) requests normally flow from the
+ * client to the server. There is no way for the server to send
+ * an asynchronous message back to the client. The Empty IU is used
+ * to hang out a meaningless request to the server so that it can respond
+ * asynchrouously with something like a SCSI AER
+ */
+struct viosrp_empty_iu {
+ struct mad_common common;
+ u64 buffer;
+ u32 port;
+};
+
+struct viosrp_error_log {
+ struct mad_common common;
+ u64 buffer;
+};
+
+struct viosrp_adapter_info {
+ struct mad_common common;
+ u64 buffer;
+};
+
+struct viosrp_host_config {
+ struct mad_common common;
+ u64 buffer;
+};
+
+union mad_iu {
+ struct viosrp_empty_iu empty_iu;
+ struct viosrp_error_log error_log;
+ struct viosrp_adapter_info adapter_info;
+ struct viosrp_host_config host_config;
+};
+
+union viosrp_iu {
+ union srp_iu srp;
+ union mad_iu mad;
+};
+
+struct mad_adapter_info_data {
+ char srp_version[8];
+ char partition_name[96];
+ u32 partition_number;
+ u32 mad_version;
+ u32 os_type;
+ u32 port_max_txu[8]; /* per-port maximum transfer */
+};
+
+#endif
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
new file mode 100644
index 000000000000..2e2486b035dd
--- /dev/null
+++ b/drivers/scsi/ide-scsi.c
@@ -0,0 +1,1174 @@
+/*
+ * linux/drivers/scsi/ide-scsi.c Version 0.9 Jul 4, 1999
+ *
+ * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * Emulation of a SCSI host adapter for IDE ATAPI devices.
+ *
+ * With this driver, one can use the Linux SCSI drivers instead of the
+ * native IDE ATAPI drivers.
+ *
+ * Ver 0.1 Dec 3 96 Initial version.
+ * Ver 0.2 Jan 26 97 Fixed bug in cleanup_module() and added emulation
+ * of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks
+ * to Janos Farkas for pointing this out.
+ * Avoid using bitfields in structures for m68k.
+ * Added Scatter/Gather and DMA support.
+ * Ver 0.4 Dec 7 97 Add support for ATAPI PD/CD drives.
+ * Use variable timeout for each command.
+ * Ver 0.5 Jan 2 98 Fix previous PD/CD support.
+ * Allow disabling of SCSI-6 to SCSI-10 transformation.
+ * Ver 0.6 Jan 27 98 Allow disabling of SCSI command translation layer
+ * for access through /dev/sg.
+ * Fix MODE_SENSE_6/MODE_SELECT_6/INQUIRY translation.
+ * Ver 0.7 Dec 04 98 Ignore commands where lun != 0 to avoid multiple
+ * detection of devices with CONFIG_SCSI_MULTI_LUN
+ * Ver 0.8 Feb 05 99 Optical media need translation too. Reverse 0.7.
+ * Ver 0.9 Jul 04 99 Fix a bug in SG_SET_TRANSFORM.
+ * Ver 0.91 Jun 10 02 Fix "off by one" error in transforms
+ * Ver 0.92 Dec 31 02 Implement new SCSI mid level API
+ */
+
+#define IDESCSI_VERSION "0.92"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/slab.h>
+#include <linux/ide.h>
+#include <linux/scatterlist.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sg.h>
+
+#define IDESCSI_DEBUG_LOG 0
+
+typedef struct idescsi_pc_s {
+ u8 c[12]; /* Actual packet bytes */
+ int request_transfer; /* Bytes to transfer */
+ int actually_transferred; /* Bytes actually transferred */
+ int buffer_size; /* Size of our data buffer */
+ struct request *rq; /* The corresponding request */
+ u8 *buffer; /* Data buffer */
+ u8 *current_position; /* Pointer into the above buffer */
+ struct scatterlist *sg; /* Scatter gather table */
+ int b_count; /* Bytes transferred from current entry */
+ struct scsi_cmnd *scsi_cmd; /* SCSI command */
+ void (*done)(struct scsi_cmnd *); /* Scsi completion routine */
+ unsigned long flags; /* Status/Action flags */
+ unsigned long timeout; /* Command timeout */
+} idescsi_pc_t;
+
+/*
+ * Packet command status bits.
+ */
+#define PC_DMA_IN_PROGRESS 0 /* 1 while DMA in progress */
+#define PC_WRITING 1 /* Data direction */
+#define PC_TRANSFORM 2 /* transform SCSI commands */
+#define PC_TIMEDOUT 3 /* command timed out */
+#define PC_DMA_OK 4 /* Use DMA */
+
+/*
+ * SCSI command transformation layer
+ */
+#define IDESCSI_TRANSFORM 0 /* Enable/Disable transformation */
+#define IDESCSI_SG_TRANSFORM 1 /* /dev/sg transformation */
+
+/*
+ * Log flags
+ */
+#define IDESCSI_LOG_CMD 0 /* Log SCSI commands */
+
+typedef struct ide_scsi_obj {
+ ide_drive_t *drive;
+ ide_driver_t *driver;
+ struct gendisk *disk;
+ struct Scsi_Host *host;
+
+ idescsi_pc_t *pc; /* Current packet command */
+ unsigned long flags; /* Status/Action flags */
+ unsigned long transform; /* SCSI cmd translation layer */
+ unsigned long log; /* log flags */
+} idescsi_scsi_t;
+
+static DECLARE_MUTEX(idescsi_ref_sem);
+
+#define ide_scsi_g(disk) \
+ container_of((disk)->private_data, struct ide_scsi_obj, driver)
+
+static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk)
+{
+ struct ide_scsi_obj *scsi = NULL;
+
+ down(&idescsi_ref_sem);
+ scsi = ide_scsi_g(disk);
+ if (scsi)
+ scsi_host_get(scsi->host);
+ up(&idescsi_ref_sem);
+ return scsi;
+}
+
+static void ide_scsi_put(struct ide_scsi_obj *scsi)
+{
+ down(&idescsi_ref_sem);
+ scsi_host_put(scsi->host);
+ up(&idescsi_ref_sem);
+}
+
+static inline idescsi_scsi_t *scsihost_to_idescsi(struct Scsi_Host *host)
+{
+ return (idescsi_scsi_t*) (&host[1]);
+}
+
+static inline idescsi_scsi_t *drive_to_idescsi(ide_drive_t *ide_drive)
+{
+ return scsihost_to_idescsi(ide_drive->driver_data);
+}
+
+/*
+ * Per ATAPI device status bits.
+ */
+#define IDESCSI_DRQ_INTERRUPT 0 /* DRQ interrupt device */
+
+/*
+ * ide-scsi requests.
+ */
+#define IDESCSI_PC_RQ 90
+
+static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ (void) HWIF(drive)->INB(IDE_DATA_REG);
+}
+
+static void idescsi_output_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ HWIF(drive)->OUTB(0, IDE_DATA_REG);
+}
+
+/*
+ * PIO data transfer routines using the scatter gather table.
+ */
+static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+ int count;
+ char *buf;
+
+ while (bcount) {
+ if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+ printk (KERN_ERR "ide-scsi: scatter gather table too small, discarding data\n");
+ idescsi_discard_data (drive, bcount);
+ return;
+ }
+ count = min(pc->sg->length - pc->b_count, bcount);
+ buf = page_address(pc->sg->page) + pc->sg->offset;
+ drive->hwif->atapi_input_bytes(drive, buf + pc->b_count, count);
+ bcount -= count; pc->b_count += count;
+ if (pc->b_count == pc->sg->length) {
+ pc->sg++;
+ pc->b_count = 0;
+ }
+ }
+}
+
+static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+ int count;
+ char *buf;
+
+ while (bcount) {
+ if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+ printk (KERN_ERR "ide-scsi: scatter gather table too small, padding with zeros\n");
+ idescsi_output_zeros (drive, bcount);
+ return;
+ }
+ count = min(pc->sg->length - pc->b_count, bcount);
+ buf = page_address(pc->sg->page) + pc->sg->offset;
+ drive->hwif->atapi_output_bytes(drive, buf + pc->b_count, count);
+ bcount -= count; pc->b_count += count;
+ if (pc->b_count == pc->sg->length) {
+ pc->sg++;
+ pc->b_count = 0;
+ }
+ }
+}
+
+/*
+ * Most of the SCSI commands are supported directly by ATAPI devices.
+ * idescsi_transform_pc handles the few exceptions.
+ */
+static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+ u8 *c = pc->c, *scsi_buf = pc->buffer, *sc = pc->scsi_cmd->cmnd;
+ char *atapi_buf;
+
+ if (!test_bit(PC_TRANSFORM, &pc->flags))
+ return;
+ if (drive->media == ide_cdrom || drive->media == ide_optical) {
+ if (c[0] == READ_6 || c[0] == WRITE_6) {
+ c[8] = c[4]; c[5] = c[3]; c[4] = c[2];
+ c[3] = c[1] & 0x1f; c[2] = 0; c[1] &= 0xe0;
+ c[0] += (READ_10 - READ_6);
+ }
+ if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) {
+ unsigned short new_len;
+ if (!scsi_buf)
+ return;
+ if ((atapi_buf = kmalloc(pc->buffer_size + 4, GFP_ATOMIC)) == NULL)
+ return;
+ memset(atapi_buf, 0, pc->buffer_size + 4);
+ memset (c, 0, 12);
+ c[0] = sc[0] | 0x40;
+ c[1] = sc[1];
+ c[2] = sc[2];
+ new_len = sc[4] + 4;
+ c[8] = new_len;
+ c[7] = new_len >> 8;
+ c[9] = sc[5];
+ if (c[0] == MODE_SELECT_10) {
+ atapi_buf[1] = scsi_buf[0]; /* Mode data length */
+ atapi_buf[2] = scsi_buf[1]; /* Medium type */
+ atapi_buf[3] = scsi_buf[2]; /* Device specific parameter */
+ atapi_buf[7] = scsi_buf[3]; /* Block descriptor length */
+ memcpy(atapi_buf + 8, scsi_buf + 4, pc->buffer_size - 4);
+ }
+ pc->buffer = atapi_buf;
+ pc->request_transfer += 4;
+ pc->buffer_size += 4;
+ }
+ }
+}
+
+static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+ u8 *atapi_buf = pc->buffer;
+ u8 *sc = pc->scsi_cmd->cmnd;
+ u8 *scsi_buf = pc->scsi_cmd->request_buffer;
+
+ if (!test_bit(PC_TRANSFORM, &pc->flags))
+ return;
+ if (drive->media == ide_cdrom || drive->media == ide_optical) {
+ if (pc->c[0] == MODE_SENSE_10 && sc[0] == MODE_SENSE) {
+ scsi_buf[0] = atapi_buf[1]; /* Mode data length */
+ scsi_buf[1] = atapi_buf[2]; /* Medium type */
+ scsi_buf[2] = atapi_buf[3]; /* Device specific parameter */
+ scsi_buf[3] = atapi_buf[7]; /* Block descriptor length */
+ memcpy(scsi_buf + 4, atapi_buf + 8, pc->request_transfer - 8);
+ }
+ if (pc->c[0] == INQUIRY) {
+ scsi_buf[2] |= 2; /* ansi_revision */
+ scsi_buf[3] = (scsi_buf[3] & 0xf0) | 2; /* response data format */
+ }
+ }
+ if (atapi_buf && atapi_buf != scsi_buf)
+ kfree(atapi_buf);
+}
+
+static void hexdump(u8 *x, int len)
+{
+ int i;
+
+ printk("[ ");
+ for (i = 0; i < len; i++)
+ printk("%x ", x[i]);
+ printk("]\n");
+}
+
+static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_command)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+ idescsi_pc_t *pc;
+ struct request *rq;
+ u8 *buf;
+
+ /* stuff a sense request in front of our current request */
+ pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+ rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+ buf = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC);
+ if (pc == NULL || rq == NULL || buf == NULL) {
+ if (pc) kfree(pc);
+ if (rq) kfree(rq);
+ if (buf) kfree(buf);
+ return -ENOMEM;
+ }
+ memset (pc, 0, sizeof (idescsi_pc_t));
+ memset (buf, 0, SCSI_SENSE_BUFFERSIZE);
+ ide_init_drive_cmd(rq);
+ rq->special = (char *) pc;
+ pc->rq = rq;
+ pc->buffer = buf;
+ pc->c[0] = REQUEST_SENSE;
+ pc->c[4] = pc->request_transfer = pc->buffer_size = SCSI_SENSE_BUFFERSIZE;
+ rq->flags = REQ_SENSE;
+ pc->timeout = jiffies + WAIT_READY;
+ /* NOTE! Save the failed packet command in "rq->buffer" */
+ rq->buffer = (void *) failed_command->special;
+ pc->scsi_cmd = ((idescsi_pc_t *) failed_command->special)->scsi_cmd;
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) {
+ printk ("ide-scsi: %s: queue cmd = ", drive->name);
+ hexdump(pc->c, 6);
+ }
+ rq->rq_disk = scsi->disk;
+ return ide_do_drive_cmd(drive, rq, ide_preempt);
+}
+
+static int idescsi_end_request(ide_drive_t *, int, int);
+
+static ide_startstop_t
+idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
+{
+ if (HWIF(drive)->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT))
+ /* force an abort */
+ HWIF(drive)->OUTB(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);
+
+ rq->errors++;
+
+ idescsi_end_request(drive, 0, 0);
+
+ return ide_stopped;
+}
+
+static ide_startstop_t
+idescsi_atapi_abort(ide_drive_t *drive, struct request *rq)
+{
+#if IDESCSI_DEBUG_LOG
+ printk(KERN_WARNING "idescsi_atapi_abort called for %lu\n",
+ ((idescsi_pc_t *) rq->special)->scsi_cmd->serial_number);
+#endif
+ rq->errors |= ERROR_MAX;
+
+ idescsi_end_request(drive, 0, 0);
+
+ return ide_stopped;
+}
+
+static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+ struct request *rq = HWGROUP(drive)->rq;
+ idescsi_pc_t *pc = (idescsi_pc_t *) rq->special;
+ int log = test_bit(IDESCSI_LOG_CMD, &scsi->log);
+ struct Scsi_Host *host;
+ u8 *scsi_buf;
+ unsigned long flags;
+
+ if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) {
+ ide_end_request(drive, uptodate, nrsecs);
+ return 0;
+ }
+ ide_end_drive_cmd (drive, 0, 0);
+ if (rq->flags & REQ_SENSE) {
+ idescsi_pc_t *opc = (idescsi_pc_t *) rq->buffer;
+ if (log) {
+ printk ("ide-scsi: %s: wrap up check %lu, rst = ", drive->name, opc->scsi_cmd->serial_number);
+ hexdump(pc->buffer,16);
+ }
+ memcpy((void *) opc->scsi_cmd->sense_buffer, pc->buffer, SCSI_SENSE_BUFFERSIZE);
+ kfree(pc->buffer);
+ kfree(pc);
+ kfree(rq);
+ pc = opc;
+ rq = pc->rq;
+ pc->scsi_cmd->result = (CHECK_CONDITION << 1) |
+ ((test_bit(PC_TIMEDOUT, &pc->flags)?DID_TIME_OUT:DID_OK) << 16);
+ } else if (test_bit(PC_TIMEDOUT, &pc->flags)) {
+ if (log)
+ printk (KERN_WARNING "ide-scsi: %s: timed out for %lu\n",
+ drive->name, pc->scsi_cmd->serial_number);
+ pc->scsi_cmd->result = DID_TIME_OUT << 16;
+ } else if (rq->errors >= ERROR_MAX) {
+ pc->scsi_cmd->result = DID_ERROR << 16;
+ if (log)
+ printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+ } else if (rq->errors) {
+ if (log)
+ printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+ if (!idescsi_check_condition(drive, rq))
+ /* we started a request sense, so we'll be back, exit for now */
+ return 0;
+ pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+ } else {
+ pc->scsi_cmd->result = DID_OK << 16;
+ idescsi_transform_pc2 (drive, pc);
+ if (log) {
+ printk ("ide-scsi: %s: suc %lu", drive->name, pc->scsi_cmd->serial_number);
+ if (!test_bit(PC_WRITING, &pc->flags) && pc->actually_transferred && pc->actually_transferred <= 1024 && pc->buffer) {
+ printk(", rst = ");
+ scsi_buf = pc->scsi_cmd->request_buffer;
+ hexdump(scsi_buf, min_t(unsigned, 16, pc->scsi_cmd->request_bufflen));
+ } else printk("\n");
+ }
+ }
+ host = pc->scsi_cmd->device->host;
+ spin_lock_irqsave(host->host_lock, flags);
+ pc->done(pc->scsi_cmd);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ kfree(pc);
+ kfree(rq);
+ scsi->pc = NULL;
+ return 0;
+}
+
+static inline unsigned long get_timeout(idescsi_pc_t *pc)
+{
+ return max_t(unsigned long, WAIT_CMD, pc->timeout - jiffies);
+}
+
+static int idescsi_expiry(ide_drive_t *drive)
+{
+ idescsi_scsi_t *scsi = drive->driver_data;
+ idescsi_pc_t *pc = scsi->pc;
+
+#if IDESCSI_DEBUG_LOG
+ printk(KERN_WARNING "idescsi_expiry called for %lu at %lu\n", pc->scsi_cmd->serial_number, jiffies);
+#endif
+ set_bit(PC_TIMEDOUT, &pc->flags);
+
+ return 0; /* we do not want the ide subsystem to retry */
+}
+
+/*
+ * Our interrupt handler.
+ */
+static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+ idescsi_pc_t *pc=scsi->pc;
+ struct request *rq = pc->rq;
+ atapi_bcount_t bcount;
+ atapi_status_t status;
+ atapi_ireason_t ireason;
+ atapi_feature_t feature;
+
+ unsigned int temp;
+
+#if IDESCSI_DEBUG_LOG
+ printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n");
+#endif /* IDESCSI_DEBUG_LOG */
+
+ if (test_bit(PC_TIMEDOUT, &pc->flags)){
+#if IDESCSI_DEBUG_LOG
+ printk(KERN_WARNING "idescsi_pc_intr: got timed out packet %lu at %lu\n",
+ pc->scsi_cmd->serial_number, jiffies);
+#endif
+ /* end this request now - scsi should retry it*/
+ idescsi_end_request (drive, 1, 0);
+ return ide_stopped;
+ }
+ if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+#if IDESCSI_DEBUG_LOG
+ printk ("ide-scsi: %s: DMA complete\n", drive->name);
+#endif /* IDESCSI_DEBUG_LOG */
+ pc->actually_transferred=pc->request_transfer;
+ (void) HWIF(drive)->ide_dma_end(drive);
+ }
+
+ feature.all = 0;
+ /* Clear the interrupt */
+ status.all = HWIF(drive)->INB(IDE_STATUS_REG);
+
+ if (!status.b.drq) {
+ /* No more interrupts */
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
+ printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+ local_irq_enable();
+ if (status.b.check)
+ rq->errors++;
+ idescsi_end_request (drive, 1, 0);
+ return ide_stopped;
+ }
+ bcount.b.low = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+ bcount.b.high = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+ ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
+
+ if (ireason.b.cod) {
+ printk(KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
+ return ide_do_reset (drive);
+ }
+ if (ireason.b.io) {
+ temp = pc->actually_transferred + bcount.all;
+ if (temp > pc->request_transfer) {
+ if (temp > pc->buffer_size) {
+ printk(KERN_ERR "ide-scsi: The scsi wants to "
+ "send us more data than expected "
+ "- discarding data\n");
+ temp = pc->buffer_size - pc->actually_transferred;
+ if (temp) {
+ clear_bit(PC_WRITING, &pc->flags);
+ if (pc->sg)
+ idescsi_input_buffers(drive, pc, temp);
+ else
+ drive->hwif->atapi_input_bytes(drive, pc->current_position, temp);
+ printk(KERN_ERR "ide-scsi: transferred %d of %d bytes\n", temp, bcount.all);
+ }
+ pc->actually_transferred += temp;
+ pc->current_position += temp;
+ idescsi_discard_data(drive, bcount.all - temp);
+ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry);
+ return ide_started;
+ }
+#if IDESCSI_DEBUG_LOG
+ printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
+#endif /* IDESCSI_DEBUG_LOG */
+ }
+ }
+ if (ireason.b.io) {
+ clear_bit(PC_WRITING, &pc->flags);
+ if (pc->sg)
+ idescsi_input_buffers(drive, pc, bcount.all);
+ else
+ HWIF(drive)->atapi_input_bytes(drive, pc->current_position, bcount.all);
+ } else {
+ set_bit(PC_WRITING, &pc->flags);
+ if (pc->sg)
+ idescsi_output_buffers (drive, pc, bcount.all);
+ else
+ HWIF(drive)->atapi_output_bytes(drive, pc->current_position, bcount.all);
+ }
+ /* Update the current position */
+ pc->actually_transferred += bcount.all;
+ pc->current_position += bcount.all;
+
+ /* And set the interrupt handler again */
+ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry);
+ return ide_started;
+}
+
+static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+ idescsi_pc_t *pc = scsi->pc;
+ atapi_ireason_t ireason;
+ ide_startstop_t startstop;
+
+ if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+ printk(KERN_ERR "ide-scsi: Strange, packet command "
+ "initiated yet DRQ isn't asserted\n");
+ return startstop;
+ }
+ ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk(KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while "
+ "issuing a packet command\n");
+ return ide_do_reset (drive);
+ }
+ if (HWGROUP(drive)->handler != NULL)
+ BUG();
+ /* Set the interrupt routine */
+ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry);
+ /* Send the actual packet */
+ drive->hwif->atapi_output_bytes(drive, scsi->pc->c, 12);
+ if (test_bit (PC_DMA_OK, &pc->flags)) {
+ set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+ hwif->dma_start(drive);
+ }
+ return ide_started;
+}
+
+static inline int idescsi_set_direction(idescsi_pc_t *pc)
+{
+ switch (pc->c[0]) {
+ case READ_6: case READ_10: case READ_12:
+ clear_bit(PC_WRITING, &pc->flags);
+ return 0;
+ case WRITE_6: case WRITE_10: case WRITE_12:
+ set_bit(PC_WRITING, &pc->flags);
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static int idescsi_map_sg(ide_drive_t *drive, idescsi_pc_t *pc)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct scatterlist *sg, *scsi_sg;
+ int segments;
+
+ if (!pc->request_transfer || pc->request_transfer % 1024)
+ return 1;
+
+ if (idescsi_set_direction(pc))
+ return 1;
+
+ sg = hwif->sg_table;
+ scsi_sg = pc->scsi_cmd->request_buffer;
+ segments = pc->scsi_cmd->use_sg;
+
+ if (segments > hwif->sg_max_nents)
+ return 1;
+
+ if (!segments) {
+ hwif->sg_nents = 1;
+ sg_init_one(sg, pc->scsi_cmd->request_buffer, pc->request_transfer);
+ } else {
+ hwif->sg_nents = segments;
+ memcpy(sg, scsi_sg, sizeof(*sg) * segments);
+ }
+
+ return 0;
+}
+
+/*
+ * Issue a packet command
+ */
+static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+ ide_hwif_t *hwif = drive->hwif;
+ atapi_feature_t feature;
+ atapi_bcount_t bcount;
+
+ scsi->pc=pc; /* Set the current packet command */
+ pc->actually_transferred=0; /* We haven't transferred any data yet */
+ pc->current_position=pc->buffer;
+ bcount.all = min(pc->request_transfer, 63 * 1024); /* Request to transfer the entire buffer at once */
+
+ feature.all = 0;
+ if (drive->using_dma && !idescsi_map_sg(drive, pc)) {
+ hwif->sg_mapped = 1;
+ feature.b.dma = !hwif->dma_setup(drive);
+ hwif->sg_mapped = 0;
+ }
+
+ SELECT_DRIVE(drive);
+ if (IDE_CONTROL_REG)
+ HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
+
+ HWIF(drive)->OUTB(feature.all, IDE_FEATURE_REG);
+ HWIF(drive)->OUTB(bcount.b.high, IDE_BCOUNTH_REG);
+ HWIF(drive)->OUTB(bcount.b.low, IDE_BCOUNTL_REG);
+
+ if (feature.b.dma)
+ set_bit(PC_DMA_OK, &pc->flags);
+
+ if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
+ if (HWGROUP(drive)->handler != NULL)
+ BUG();
+ ide_set_handler(drive, &idescsi_transfer_pc,
+ get_timeout(pc), idescsi_expiry);
+ /* Issue the packet command */
+ HWIF(drive)->OUTB(WIN_PACKETCMD, IDE_COMMAND_REG);
+ return ide_started;
+ } else {
+ /* Issue the packet command */
+ HWIF(drive)->OUTB(WIN_PACKETCMD, IDE_COMMAND_REG);
+ return idescsi_transfer_pc(drive);
+ }
+}
+
+/*
+ * idescsi_do_request is our request handling function.
+ */
+static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block)
+{
+#if IDESCSI_DEBUG_LOG
+ printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %x, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd[0],rq->errors);
+ printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDESCSI_DEBUG_LOG */
+
+ if (rq->flags & (REQ_SPECIAL|REQ_SENSE)) {
+ return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special);
+ }
+ blk_dump_rq_flags(rq, "ide-scsi: unsup command");
+ idescsi_end_request (drive, 0, 0);
+ return ide_stopped;
+}
+
+static void idescsi_add_settings(ide_drive_t *drive)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+
+/*
+ * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function
+ */
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
+ ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
+ ide_add_setting(drive, "transform", SETTING_RW, -1, -1, TYPE_INT, 0, 3, 1, 1, &scsi->transform, NULL);
+ ide_add_setting(drive, "log", SETTING_RW, -1, -1, TYPE_INT, 0, 1, 1, 1, &scsi->log, NULL);
+}
+
+/*
+ * Driver initialization.
+ */
+static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi)
+{
+ DRIVER(drive)->busy++;
+ if (drive->id && (drive->id->config & 0x0060) == 0x20)
+ set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+ set_bit(IDESCSI_TRANSFORM, &scsi->transform);
+ clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
+#if IDESCSI_DEBUG_LOG
+ set_bit(IDESCSI_LOG_CMD, &scsi->log);
+#endif /* IDESCSI_DEBUG_LOG */
+ idescsi_add_settings(drive);
+ DRIVER(drive)->busy--;
+}
+
+static int idescsi_cleanup (ide_drive_t *drive)
+{
+ struct Scsi_Host *scsihost = drive->driver_data;
+ struct ide_scsi_obj *scsi = scsihost_to_idescsi(scsihost);
+ struct gendisk *g = scsi->disk;
+
+ if (ide_unregister_subdriver(drive))
+ return 1;
+
+ ide_unregister_region(g);
+
+ drive->driver_data = NULL;
+ g->private_data = NULL;
+ put_disk(g);
+
+ scsi_remove_host(scsihost);
+ ide_scsi_put(scsi);
+
+ return 0;
+}
+
+static int idescsi_attach(ide_drive_t *drive);
+
+#ifdef CONFIG_PROC_FS
+static ide_proc_entry_t idescsi_proc[] = {
+ { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL },
+ { NULL, 0, NULL, NULL }
+};
+#else
+# define idescsi_proc NULL
+#endif
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idescsi_driver = {
+ .owner = THIS_MODULE,
+ .name = "ide-scsi",
+ .version = IDESCSI_VERSION,
+ .media = ide_scsi,
+ .busy = 0,
+ .supports_dsc_overlap = 0,
+ .proc = idescsi_proc,
+ .attach = idescsi_attach,
+ .cleanup = idescsi_cleanup,
+ .do_request = idescsi_do_request,
+ .end_request = idescsi_end_request,
+ .error = idescsi_atapi_error,
+ .abort = idescsi_atapi_abort,
+ .drives = LIST_HEAD_INIT(idescsi_driver.drives),
+};
+
+static int idescsi_ide_open(struct inode *inode, struct file *filp)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct ide_scsi_obj *scsi;
+ ide_drive_t *drive;
+
+ if (!(scsi = ide_scsi_get(disk)))
+ return -ENXIO;
+
+ drive = scsi->drive;
+
+ drive->usage++;
+
+ return 0;
+}
+
+static int idescsi_ide_release(struct inode *inode, struct file *filp)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct ide_scsi_obj *scsi = ide_scsi_g(disk);
+ ide_drive_t *drive = scsi->drive;
+
+ drive->usage--;
+
+ ide_scsi_put(scsi);
+
+ return 0;
+}
+
+static int idescsi_ide_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct block_device *bdev = inode->i_bdev;
+ struct ide_scsi_obj *scsi = ide_scsi_g(bdev->bd_disk);
+ return generic_ide_ioctl(scsi->drive, file, bdev, cmd, arg);
+}
+
+static struct block_device_operations idescsi_ops = {
+ .owner = THIS_MODULE,
+ .open = idescsi_ide_open,
+ .release = idescsi_ide_release,
+ .ioctl = idescsi_ide_ioctl,
+};
+
+static int idescsi_attach(ide_drive_t *drive);
+
+static int idescsi_slave_configure(struct scsi_device * sdp)
+{
+ /* Configure detected device */
+ scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, sdp->host->cmd_per_lun);
+ return 0;
+}
+
+static const char *idescsi_info (struct Scsi_Host *host)
+{
+ return "SCSI host adapter emulation for IDE ATAPI devices";
+}
+
+static int idescsi_ioctl (struct scsi_device *dev, int cmd, void __user *arg)
+{
+ idescsi_scsi_t *scsi = scsihost_to_idescsi(dev->host);
+
+ if (cmd == SG_SET_TRANSFORM) {
+ if (arg)
+ set_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
+ else
+ clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
+ return 0;
+ } else if (cmd == SG_GET_TRANSFORM)
+ return put_user(test_bit(IDESCSI_SG_TRANSFORM, &scsi->transform), (int __user *) arg);
+ return -EINVAL;
+}
+
+static inline int should_transform(ide_drive_t *drive, struct scsi_cmnd *cmd)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+
+ /* this was a layering violation and we can't support it
+ anymore, sorry. */
+#if 0
+ struct gendisk *disk = cmd->request->rq_disk;
+
+ if (disk) {
+ struct Scsi_Device_Template **p = disk->private_data;
+ if (strcmp((*p)->scsi_driverfs_driver.name, "sg") == 0)
+ return test_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
+ }
+#endif
+ return test_bit(IDESCSI_TRANSFORM, &scsi->transform);
+}
+
+static int idescsi_queue (struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = cmd->device->host;
+ idescsi_scsi_t *scsi = scsihost_to_idescsi(host);
+ ide_drive_t *drive = scsi->drive;
+ struct request *rq = NULL;
+ idescsi_pc_t *pc = NULL;
+
+ if (!drive) {
+ printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->device->id);
+ goto abort;
+ }
+ scsi = drive_to_idescsi(drive);
+ pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+ rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+ if (rq == NULL || pc == NULL) {
+ printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
+ goto abort;
+ }
+
+ memset (pc->c, 0, 12);
+ pc->flags = 0;
+ pc->rq = rq;
+ memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
+ if (cmd->use_sg) {
+ pc->buffer = NULL;
+ pc->sg = cmd->request_buffer;
+ } else {
+ pc->buffer = cmd->request_buffer;
+ pc->sg = NULL;
+ }
+ pc->b_count = 0;
+ pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
+ pc->scsi_cmd = cmd;
+ pc->done = done;
+ pc->timeout = jiffies + cmd->timeout_per_command;
+
+ if (should_transform(drive, cmd))
+ set_bit(PC_TRANSFORM, &pc->flags);
+ idescsi_transform_pc1 (drive, pc);
+
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) {
+ printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number);
+ hexdump(cmd->cmnd, cmd->cmd_len);
+ if (memcmp(pc->c, cmd->cmnd, cmd->cmd_len)) {
+ printk ("ide-scsi: %s: que %lu, tsl = ", drive->name, cmd->serial_number);
+ hexdump(pc->c, 12);
+ }
+ }
+
+ ide_init_drive_cmd (rq);
+ rq->special = (char *) pc;
+ rq->flags = REQ_SPECIAL;
+ spin_unlock_irq(host->host_lock);
+ rq->rq_disk = scsi->disk;
+ (void) ide_do_drive_cmd (drive, rq, ide_end);
+ spin_lock_irq(host->host_lock);
+ return 0;
+abort:
+ if (pc) kfree (pc);
+ if (rq) kfree (rq);
+ cmd->result = DID_ERROR << 16;
+ done(cmd);
+ return 0;
+}
+
+static int idescsi_eh_abort (struct scsi_cmnd *cmd)
+{
+ idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host);
+ ide_drive_t *drive = scsi->drive;
+ int busy;
+ int ret = FAILED;
+
+ /* In idescsi_eh_abort we try to gently pry our command from the ide subsystem */
+
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
+ printk (KERN_WARNING "ide-scsi: abort called for %lu\n", cmd->serial_number);
+
+ if (!drive) {
+ printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_abort\n");
+ WARN_ON(1);
+ goto no_drive;
+ }
+
+ /* First give it some more time, how much is "right" is hard to say :-( */
+
+ busy = ide_wait_not_busy(HWIF(drive), 100); /* FIXME - uses mdelay which causes latency? */
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
+ printk (KERN_WARNING "ide-scsi: drive did%s become ready\n", busy?" not":"");
+
+ spin_lock_irq(&ide_lock);
+
+ /* If there is no pc running we're done (our interrupt took care of it) */
+ if (!scsi->pc) {
+ ret = SUCCESS;
+ goto ide_unlock;
+ }
+
+ /* It's somewhere in flight. Does ide subsystem agree? */
+ if (scsi->pc->scsi_cmd->serial_number == cmd->serial_number && !busy &&
+ elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != scsi->pc->rq) {
+ /*
+ * FIXME - not sure this condition can ever occur
+ */
+ printk (KERN_ERR "ide-scsi: cmd aborted!\n");
+
+ if (scsi->pc->rq->flags & REQ_SENSE)
+ kfree(scsi->pc->buffer);
+ kfree(scsi->pc->rq);
+ kfree(scsi->pc);
+ scsi->pc = NULL;
+
+ ret = SUCCESS;
+ }
+
+ide_unlock:
+ spin_unlock_irq(&ide_lock);
+no_drive:
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
+ printk (KERN_WARNING "ide-scsi: abort returns %s\n", ret == SUCCESS?"success":"failed");
+
+ return ret;
+}
+
+static int idescsi_eh_reset (struct scsi_cmnd *cmd)
+{
+ struct request *req;
+ idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host);
+ ide_drive_t *drive = scsi->drive;
+ int ready = 0;
+ int ret = SUCCESS;
+
+ /* In idescsi_eh_reset we forcefully remove the command from the ide subsystem and reset the device. */
+
+ if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
+ printk (KERN_WARNING "ide-scsi: reset called for %lu\n", cmd->serial_number);
+
+ if (!drive) {
+ printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_reset\n");
+ WARN_ON(1);
+ return FAILED;
+ }
+
+ spin_lock_irq(&ide_lock);
+
+ if (!scsi->pc || (req = scsi->pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) {
+ printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n");
+ spin_unlock(&ide_lock);
+ return FAILED;
+ }
+
+ /* kill current request */
+ blkdev_dequeue_request(req);
+ end_that_request_last(req);
+ if (req->flags & REQ_SENSE)
+ kfree(scsi->pc->buffer);
+ kfree(scsi->pc);
+ scsi->pc = NULL;
+ kfree(req);
+
+ /* now nuke the drive queue */
+ while ((req = elv_next_request(drive->queue))) {
+ blkdev_dequeue_request(req);
+ end_that_request_last(req);
+ }
+
+ HWGROUP(drive)->rq = NULL;
+ HWGROUP(drive)->handler = NULL;
+ HWGROUP(drive)->busy = 1; /* will set this to zero when ide reset finished */
+ spin_unlock_irq(&ide_lock);
+
+ ide_do_reset(drive);
+
+ /* ide_do_reset starts a polling handler which restarts itself every 50ms until the reset finishes */
+
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(cmd->device->host->host_lock);
+ schedule_timeout(HZ/20);
+ spin_lock_irq(cmd->device->host->host_lock);
+ } while ( HWGROUP(drive)->handler );
+
+ ready = drive_is_ready(drive);
+ HWGROUP(drive)->busy--;
+ if (!ready) {
+ printk (KERN_ERR "ide-scsi: reset failed!\n");
+ ret = FAILED;
+ }
+
+ return ret;
+}
+
+static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *parm)
+{
+ idescsi_scsi_t *idescsi = scsihost_to_idescsi(sdev->host);
+ ide_drive_t *drive = idescsi->drive;
+
+ if (drive->bios_cyl && drive->bios_head && drive->bios_sect) {
+ parm[0] = drive->bios_head;
+ parm[1] = drive->bios_sect;
+ parm[2] = drive->bios_cyl;
+ }
+ return 0;
+}
+
+static struct scsi_host_template idescsi_template = {
+ .module = THIS_MODULE,
+ .name = "idescsi",
+ .info = idescsi_info,
+ .slave_configure = idescsi_slave_configure,
+ .ioctl = idescsi_ioctl,
+ .queuecommand = idescsi_queue,
+ .eh_abort_handler = idescsi_eh_abort,
+ .eh_host_reset_handler = idescsi_eh_reset,
+ .bios_param = idescsi_bios,
+ .can_queue = 40,
+ .this_id = -1,
+ .sg_tablesize = 256,
+ .cmd_per_lun = 5,
+ .max_sectors = 128,
+ .use_clustering = DISABLE_CLUSTERING,
+ .emulated = 1,
+ .proc_name = "ide-scsi",
+};
+
+static int idescsi_attach(ide_drive_t *drive)
+{
+ idescsi_scsi_t *idescsi;
+ struct Scsi_Host *host;
+ struct gendisk *g;
+ static int warned;
+ int err = -ENOMEM;
+
+ if (!warned && drive->media == ide_cdrom) {
+ printk(KERN_WARNING "ide-scsi is deprecated for cd burning! Use ide-cd and give dev=/dev/hdX as device\n");
+ warned = 1;
+ }
+
+ if (!strstr("ide-scsi", drive->driver_req) ||
+ !drive->present ||
+ drive->media == ide_disk ||
+ !(host = scsi_host_alloc(&idescsi_template,sizeof(idescsi_scsi_t))))
+ return 1;
+
+ g = alloc_disk(1 << PARTN_BITS);
+ if (!g)
+ goto out_host_put;
+
+ ide_init_disk(g, drive);
+
+ host->max_id = 1;
+
+#if IDESCSI_DEBUG_LOG
+ if (drive->id->last_lun)
+ printk(KERN_NOTICE "%s: id->last_lun=%u\n", drive->name, drive->id->last_lun);
+#endif
+ if ((drive->id->last_lun & 0x7) != 7)
+ host->max_lun = (drive->id->last_lun & 0x7) + 1;
+ else
+ host->max_lun = 1;
+
+ drive->driver_data = host;
+ idescsi = scsihost_to_idescsi(host);
+ idescsi->drive = drive;
+ idescsi->driver = &idescsi_driver;
+ idescsi->host = host;
+ idescsi->disk = g;
+ g->private_data = &idescsi->driver;
+ err = ide_register_subdriver(drive, &idescsi_driver);
+ if (!err) {
+ idescsi_setup (drive, idescsi);
+ g->fops = &idescsi_ops;
+ ide_register_region(g);
+ err = scsi_add_host(host, &drive->gendev);
+ if (!err) {
+ scsi_scan_host(host);
+ return 0;
+ }
+ /* fall through on error */
+ ide_unregister_region(g);
+ ide_unregister_subdriver(drive);
+ }
+
+ put_disk(g);
+out_host_put:
+ scsi_host_put(host);
+ return err;
+}
+
+static int __init init_idescsi_module(void)
+{
+ return ide_register_driver(&idescsi_driver);
+}
+
+static void __exit exit_idescsi_module(void)
+{
+ ide_unregister_driver(&idescsi_driver);
+}
+
+module_init(init_idescsi_module);
+module_exit(exit_idescsi_module);
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c
new file mode 100644
index 000000000000..be7f2ca0183f
--- /dev/null
+++ b/drivers/scsi/imm.c
@@ -0,0 +1,1300 @@
+/* imm.c -- low level driver for the IOMEGA MatchMaker
+ * parallel port SCSI host adapter.
+ *
+ * (The IMM is the embedded controller in the ZIP Plus drive.)
+ *
+ * Current Maintainer: David Campbell (Perth, Western Australia)
+ * campbell@torque.net
+ *
+ * My unoffical company acronym list is 21 pages long:
+ * FLA: Four letter acronym with built in facility for
+ * future expansion to five letters.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/parport.h>
+#include <linux/workqueue.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+/* The following #define is to avoid a clash with hosts.c */
+#define IMM_PROBE_SPP 0x0001
+#define IMM_PROBE_PS2 0x0002
+#define IMM_PROBE_ECR 0x0010
+#define IMM_PROBE_EPP17 0x0100
+#define IMM_PROBE_EPP19 0x0200
+
+
+typedef struct {
+ struct pardevice *dev; /* Parport device entry */
+ int base; /* Actual port address */
+ int base_hi; /* Hi Base address for ECP-ISA chipset */
+ int mode; /* Transfer mode */
+ struct scsi_cmnd *cur_cmd; /* Current queued command */
+ struct work_struct imm_tq; /* Polling interrupt stuff */
+ unsigned long jstart; /* Jiffies at start */
+ unsigned failed:1; /* Failure flag */
+ unsigned dp:1; /* Data phase present */
+ unsigned rd:1; /* Read data in data phase */
+ unsigned wanted:1; /* Parport sharing busy flag */
+ wait_queue_head_t *waiting;
+ struct Scsi_Host *host;
+ struct list_head list;
+} imm_struct;
+
+static void imm_reset_pulse(unsigned int base);
+static int device_check(imm_struct *dev);
+
+#include "imm.h"
+
+static inline imm_struct *imm_dev(struct Scsi_Host *host)
+{
+ return *(imm_struct **)&host->hostdata;
+}
+
+static DEFINE_SPINLOCK(arbitration_lock);
+
+static void got_it(imm_struct *dev)
+{
+ dev->base = dev->dev->port->base;
+ if (dev->cur_cmd)
+ dev->cur_cmd->SCp.phase = 1;
+ else
+ wake_up(dev->waiting);
+}
+
+static void imm_wakeup(void *ref)
+{
+ imm_struct *dev = (imm_struct *) ref;
+ unsigned long flags;
+
+ spin_lock_irqsave(&arbitration_lock, flags);
+ if (dev->wanted) {
+ parport_claim(dev->dev);
+ got_it(dev);
+ dev->wanted = 0;
+ }
+ spin_unlock_irqrestore(&arbitration_lock, flags);
+}
+
+static int imm_pb_claim(imm_struct *dev)
+{
+ unsigned long flags;
+ int res = 1;
+ spin_lock_irqsave(&arbitration_lock, flags);
+ if (parport_claim(dev->dev) == 0) {
+ got_it(dev);
+ res = 0;
+ }
+ dev->wanted = res;
+ spin_unlock_irqrestore(&arbitration_lock, flags);
+ return res;
+}
+
+static void imm_pb_dismiss(imm_struct *dev)
+{
+ unsigned long flags;
+ int wanted;
+ spin_lock_irqsave(&arbitration_lock, flags);
+ wanted = dev->wanted;
+ dev->wanted = 0;
+ spin_unlock_irqrestore(&arbitration_lock, flags);
+ if (!wanted)
+ parport_release(dev->dev);
+}
+
+static inline void imm_pb_release(imm_struct *dev)
+{
+ parport_release(dev->dev);
+}
+
+/* This is to give the imm driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/imm/0 file.
+ * Very simple method really... (Too simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+static inline int imm_proc_write(imm_struct *dev, char *buffer, int length)
+{
+ unsigned long x;
+
+ if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ dev->mode = x;
+ return length;
+ }
+ printk("imm /proc: invalid variable\n");
+ return (-EINVAL);
+}
+
+static int imm_proc_info(struct Scsi_Host *host, char *buffer, char **start,
+ off_t offset, int length, int inout)
+{
+ imm_struct *dev = imm_dev(host);
+ int len = 0;
+
+ if (inout)
+ return imm_proc_write(dev, buffer, length);
+
+ len += sprintf(buffer + len, "Version : %s\n", IMM_VERSION);
+ len +=
+ sprintf(buffer + len, "Parport : %s\n",
+ dev->dev->port->name);
+ len +=
+ sprintf(buffer + len, "Mode : %s\n",
+ IMM_MODE_STRING[dev->mode]);
+
+ /* Request for beyond end of buffer */
+ if (offset > len)
+ return 0;
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+#if IMM_DEBUG > 0
+#define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\
+ y, __FUNCTION__, __LINE__); imm_fail_func(x,y);
+static inline void
+imm_fail_func(imm_struct *dev, int error_code)
+#else
+static inline void
+imm_fail(imm_struct *dev, int error_code)
+#endif
+{
+ /* If we fail a device then we trash status / message bytes */
+ if (dev->cur_cmd) {
+ dev->cur_cmd->result = error_code << 16;
+ dev->failed = 1;
+ }
+}
+
+/*
+ * Wait for the high bit to be set.
+ *
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts. We spin on
+ * the 0x80 ready bit.
+ */
+static unsigned char imm_wait(imm_struct *dev)
+{
+ int k;
+ unsigned short ppb = dev->base;
+ unsigned char r;
+
+ w_ctr(ppb, 0x0c);
+
+ k = IMM_SPIN_TMO;
+ do {
+ r = r_str(ppb);
+ k--;
+ udelay(1);
+ }
+ while (!(r & 0x80) && (k));
+
+ /*
+ * STR register (LPT base+1) to SCSI mapping:
+ *
+ * STR imm imm
+ * ===================================
+ * 0x80 S_REQ S_REQ
+ * 0x40 !S_BSY (????)
+ * 0x20 !S_CD !S_CD
+ * 0x10 !S_IO !S_IO
+ * 0x08 (????) !S_BSY
+ *
+ * imm imm meaning
+ * ==================================
+ * 0xf0 0xb8 Bit mask
+ * 0xc0 0x88 ZIP wants more data
+ * 0xd0 0x98 ZIP wants to send more data
+ * 0xe0 0xa8 ZIP is expecting SCSI command data
+ * 0xf0 0xb8 end of transfer, ZIP is sending status
+ */
+ w_ctr(ppb, 0x04);
+ if (k)
+ return (r & 0xb8);
+
+ /* Counter expired - Time out occurred */
+ imm_fail(dev, DID_TIME_OUT);
+ printk("imm timeout in imm_wait\n");
+ return 0; /* command timed out */
+}
+
+static int imm_negotiate(imm_struct * tmp)
+{
+ /*
+ * The following is supposedly the IEEE 1284-1994 negotiate
+ * sequence. I have yet to obtain a copy of the above standard
+ * so this is a bit of a guess...
+ *
+ * A fair chunk of this is based on the Linux parport implementation
+ * of IEEE 1284.
+ *
+ * Return 0 if data available
+ * 1 if no data available
+ */
+
+ unsigned short base = tmp->base;
+ unsigned char a, mode;
+
+ switch (tmp->mode) {
+ case IMM_NIBBLE:
+ mode = 0x00;
+ break;
+ case IMM_PS2:
+ mode = 0x01;
+ break;
+ default:
+ return 0;
+ }
+
+ w_ctr(base, 0x04);
+ udelay(5);
+ w_dtr(base, mode);
+ udelay(100);
+ w_ctr(base, 0x06);
+ udelay(5);
+ a = (r_str(base) & 0x20) ? 0 : 1;
+ udelay(5);
+ w_ctr(base, 0x07);
+ udelay(5);
+ w_ctr(base, 0x06);
+
+ if (a) {
+ printk
+ ("IMM: IEEE1284 negotiate indicates no data available.\n");
+ imm_fail(tmp, DID_ERROR);
+ }
+ return a;
+}
+
+/*
+ * Clear EPP timeout bit.
+ */
+static inline void epp_reset(unsigned short ppb)
+{
+ int i;
+
+ i = r_str(ppb);
+ w_str(ppb, i);
+ w_str(ppb, i & 0xfe);
+}
+
+/*
+ * Wait for empty ECP fifo (if we are in ECP fifo mode only)
+ */
+static inline void ecp_sync(imm_struct *dev)
+{
+ int i, ppb_hi = dev->base_hi;
+
+ if (ppb_hi == 0)
+ return;
+
+ if ((r_ecr(ppb_hi) & 0xe0) == 0x60) { /* mode 011 == ECP fifo mode */
+ for (i = 0; i < 100; i++) {
+ if (r_ecr(ppb_hi) & 0x01)
+ return;
+ udelay(5);
+ }
+ printk("imm: ECP sync failed as data still present in FIFO.\n");
+ }
+}
+
+static int imm_byte_out(unsigned short base, const char *buffer, int len)
+{
+ int i;
+
+ w_ctr(base, 0x4); /* apparently a sane mode */
+ for (i = len >> 1; i; i--) {
+ w_dtr(base, *buffer++);
+ w_ctr(base, 0x5); /* Drop STROBE low */
+ w_dtr(base, *buffer++);
+ w_ctr(base, 0x0); /* STROBE high + INIT low */
+ }
+ w_ctr(base, 0x4); /* apparently a sane mode */
+ return 1; /* All went well - we hope! */
+}
+
+static int imm_nibble_in(unsigned short base, char *buffer, int len)
+{
+ unsigned char l;
+ int i;
+
+ /*
+ * The following is based on documented timing signals
+ */
+ w_ctr(base, 0x4);
+ for (i = len; i; i--) {
+ w_ctr(base, 0x6);
+ l = (r_str(base) & 0xf0) >> 4;
+ w_ctr(base, 0x5);
+ *buffer++ = (r_str(base) & 0xf0) | l;
+ w_ctr(base, 0x4);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static int imm_byte_in(unsigned short base, char *buffer, int len)
+{
+ int i;
+
+ /*
+ * The following is based on documented timing signals
+ */
+ w_ctr(base, 0x4);
+ for (i = len; i; i--) {
+ w_ctr(base, 0x26);
+ *buffer++ = r_dtr(base);
+ w_ctr(base, 0x25);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static int imm_out(imm_struct *dev, char *buffer, int len)
+{
+ unsigned short ppb = dev->base;
+ int r = imm_wait(dev);
+
+ /*
+ * Make sure that:
+ * a) the SCSI bus is BUSY (device still listening)
+ * b) the device is listening
+ */
+ if ((r & 0x18) != 0x08) {
+ imm_fail(dev, DID_ERROR);
+ printk("IMM: returned SCSI status %2x\n", r);
+ return 0;
+ }
+ switch (dev->mode) {
+ case IMM_EPP_32:
+ case IMM_EPP_16:
+ case IMM_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x4);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+ if (!(((long) buffer | len) & 0x01))
+ outsw(ppb + 4, buffer, len >> 1);
+#else
+ if (!(((long) buffer | len) & 0x03))
+ outsl(ppb + 4, buffer, len >> 2);
+#endif
+ else
+ outsb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0xc);
+ r = !(r_str(ppb) & 0x01);
+ w_ctr(ppb, 0xc);
+ ecp_sync(dev);
+ break;
+
+ case IMM_NIBBLE:
+ case IMM_PS2:
+ /* 8 bit output, with a loop */
+ r = imm_byte_out(ppb, buffer, len);
+ break;
+
+ default:
+ printk("IMM: bug in imm_out()\n");
+ r = 0;
+ }
+ return r;
+}
+
+static int imm_in(imm_struct *dev, char *buffer, int len)
+{
+ unsigned short ppb = dev->base;
+ int r = imm_wait(dev);
+
+ /*
+ * Make sure that:
+ * a) the SCSI bus is BUSY (device still listening)
+ * b) the device is sending data
+ */
+ if ((r & 0x18) != 0x18) {
+ imm_fail(dev, DID_ERROR);
+ return 0;
+ }
+ switch (dev->mode) {
+ case IMM_NIBBLE:
+ /* 4 bit input, with a loop */
+ r = imm_nibble_in(ppb, buffer, len);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case IMM_PS2:
+ /* 8 bit input, with a loop */
+ r = imm_byte_in(ppb, buffer, len);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case IMM_EPP_32:
+ case IMM_EPP_16:
+ case IMM_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x24);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+ if (!(((long) buffer | len) & 0x01))
+ insw(ppb + 4, buffer, len >> 1);
+#else
+ if (!(((long) buffer | len) & 0x03))
+ insl(ppb + 4, buffer, len >> 2);
+#endif
+ else
+ insb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0x2c);
+ r = !(r_str(ppb) & 0x01);
+ w_ctr(ppb, 0x2c);
+ ecp_sync(dev);
+ break;
+
+ default:
+ printk("IMM: bug in imm_ins()\n");
+ r = 0;
+ break;
+ }
+ return r;
+}
+
+static int imm_cpp(unsigned short ppb, unsigned char b)
+{
+ /*
+ * Comments on udelay values refer to the
+ * Command Packet Protocol (CPP) timing diagram.
+ */
+
+ unsigned char s1, s2, s3;
+ w_ctr(ppb, 0x0c);
+ udelay(2); /* 1 usec - infinite */
+ w_dtr(ppb, 0xaa);
+ udelay(10); /* 7 usec - infinite */
+ w_dtr(ppb, 0x55);
+ udelay(10); /* 7 usec - infinite */
+ w_dtr(ppb, 0x00);
+ udelay(10); /* 7 usec - infinite */
+ w_dtr(ppb, 0xff);
+ udelay(10); /* 7 usec - infinite */
+ s1 = r_str(ppb) & 0xb8;
+ w_dtr(ppb, 0x87);
+ udelay(10); /* 7 usec - infinite */
+ s2 = r_str(ppb) & 0xb8;
+ w_dtr(ppb, 0x78);
+ udelay(10); /* 7 usec - infinite */
+ s3 = r_str(ppb) & 0x38;
+ /*
+ * Values for b are:
+ * 0000 00aa Assign address aa to current device
+ * 0010 00aa Select device aa in EPP Winbond mode
+ * 0010 10aa Select device aa in EPP mode
+ * 0011 xxxx Deselect all devices
+ * 0110 00aa Test device aa
+ * 1101 00aa Select device aa in ECP mode
+ * 1110 00aa Select device aa in Compatible mode
+ */
+ w_dtr(ppb, b);
+ udelay(2); /* 1 usec - infinite */
+ w_ctr(ppb, 0x0c);
+ udelay(10); /* 7 usec - infinite */
+ w_ctr(ppb, 0x0d);
+ udelay(2); /* 1 usec - infinite */
+ w_ctr(ppb, 0x0c);
+ udelay(10); /* 7 usec - infinite */
+ w_dtr(ppb, 0xff);
+ udelay(10); /* 7 usec - infinite */
+
+ /*
+ * The following table is electrical pin values.
+ * (BSY is inverted at the CTR register)
+ *
+ * BSY ACK POut SEL Fault
+ * S1 0 X 1 1 1
+ * S2 1 X 0 1 1
+ * S3 L X 1 1 S
+ *
+ * L => Last device in chain
+ * S => Selected
+ *
+ * Observered values for S1,S2,S3 are:
+ * Disconnect => f8/58/78
+ * Connect => f8/58/70
+ */
+ if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x30))
+ return 1; /* Connected */
+ if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x38))
+ return 0; /* Disconnected */
+
+ return -1; /* No device present */
+}
+
+static inline int imm_connect(imm_struct *dev, int flag)
+{
+ unsigned short ppb = dev->base;
+
+ imm_cpp(ppb, 0xe0); /* Select device 0 in compatible mode */
+ imm_cpp(ppb, 0x30); /* Disconnect all devices */
+
+ if ((dev->mode == IMM_EPP_8) ||
+ (dev->mode == IMM_EPP_16) ||
+ (dev->mode == IMM_EPP_32))
+ return imm_cpp(ppb, 0x28); /* Select device 0 in EPP mode */
+ return imm_cpp(ppb, 0xe0); /* Select device 0 in compatible mode */
+}
+
+static void imm_disconnect(imm_struct *dev)
+{
+ imm_cpp(dev->base, 0x30); /* Disconnect all devices */
+}
+
+static int imm_select(imm_struct *dev, int target)
+{
+ int k;
+ unsigned short ppb = dev->base;
+
+ /*
+ * Firstly we want to make sure there is nothing
+ * holding onto the SCSI bus.
+ */
+ w_ctr(ppb, 0xc);
+
+ k = IMM_SELECT_TMO;
+ do {
+ k--;
+ } while ((r_str(ppb) & 0x08) && (k));
+
+ if (!k)
+ return 0;
+
+ /*
+ * Now assert the SCSI ID (HOST and TARGET) on the data bus
+ */
+ w_ctr(ppb, 0x4);
+ w_dtr(ppb, 0x80 | (1 << target));
+ udelay(1);
+
+ /*
+ * Deassert SELIN first followed by STROBE
+ */
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xd);
+
+ /*
+ * ACK should drop low while SELIN is deasserted.
+ * FAULT should drop low when the SCSI device latches the bus.
+ */
+ k = IMM_SELECT_TMO;
+ do {
+ k--;
+ }
+ while (!(r_str(ppb) & 0x08) && (k));
+
+ /*
+ * Place the interface back into a sane state (status mode)
+ */
+ w_ctr(ppb, 0xc);
+ return (k) ? 1 : 0;
+}
+
+static int imm_init(imm_struct *dev)
+{
+ if (imm_connect(dev, 0) != 1)
+ return -EIO;
+ imm_reset_pulse(dev->base);
+ udelay(1000); /* Delay to allow devices to settle */
+ imm_disconnect(dev);
+ udelay(1000); /* Another delay to allow devices to settle */
+ return device_check(dev);
+}
+
+static inline int imm_send_command(struct scsi_cmnd *cmd)
+{
+ imm_struct *dev = imm_dev(cmd->device->host);
+ int k;
+
+ /* NOTE: IMM uses byte pairs */
+ for (k = 0; k < cmd->cmd_len; k += 2)
+ if (!imm_out(dev, &cmd->cmnd[k], 2))
+ return 0;
+ return 1;
+}
+
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ *
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int imm_completion(struct scsi_cmnd *cmd)
+{
+ /* Return codes:
+ * -1 Error
+ * 0 Told to schedule
+ * 1 Finished data transfer
+ */
+ imm_struct *dev = imm_dev(cmd->device->host);
+ unsigned short ppb = dev->base;
+ unsigned long start_jiffies = jiffies;
+
+ unsigned char r, v;
+ int fast, bulk, status;
+
+ v = cmd->cmnd[0];
+ bulk = ((v == READ_6) ||
+ (v == READ_10) || (v == WRITE_6) || (v == WRITE_10));
+
+ /*
+ * We only get here if the drive is ready to comunicate,
+ * hence no need for a full imm_wait.
+ */
+ w_ctr(ppb, 0x0c);
+ r = (r_str(ppb) & 0xb8);
+
+ /*
+ * while (device is not ready to send status byte)
+ * loop;
+ */
+ while (r != (unsigned char) 0xb8) {
+ /*
+ * If we have been running for more than a full timer tick
+ * then take a rest.
+ */
+ if (time_after(jiffies, start_jiffies + 1))
+ return 0;
+
+ /*
+ * FAIL if:
+ * a) Drive status is screwy (!ready && !present)
+ * b) Drive is requesting/sending more data than expected
+ */
+ if (((r & 0x88) != 0x88) || (cmd->SCp.this_residual <= 0)) {
+ imm_fail(dev, DID_ERROR);
+ return -1; /* ERROR_RETURN */
+ }
+ /* determine if we should use burst I/O */
+ if (dev->rd == 0) {
+ fast = (bulk
+ && (cmd->SCp.this_residual >=
+ IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 2;
+ status = imm_out(dev, cmd->SCp.ptr, fast);
+ } else {
+ fast = (bulk
+ && (cmd->SCp.this_residual >=
+ IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 1;
+ status = imm_in(dev, cmd->SCp.ptr, fast);
+ }
+
+ cmd->SCp.ptr += fast;
+ cmd->SCp.this_residual -= fast;
+
+ if (!status) {
+ imm_fail(dev, DID_BUS_BUSY);
+ return -1; /* ERROR_RETURN */
+ }
+ if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ /* if scatter/gather, advance to the next segment */
+ if (cmd->SCp.buffers_residual--) {
+ cmd->SCp.buffer++;
+ cmd->SCp.this_residual =
+ cmd->SCp.buffer->length;
+ cmd->SCp.ptr =
+ page_address(cmd->SCp.buffer->page) +
+ cmd->SCp.buffer->offset;
+
+ /*
+ * Make sure that we transfer even number of bytes
+ * otherwise it makes imm_byte_out() messy.
+ */
+ if (cmd->SCp.this_residual & 0x01)
+ cmd->SCp.this_residual++;
+ }
+ }
+ /* Now check to see if the drive is ready to comunicate */
+ w_ctr(ppb, 0x0c);
+ r = (r_str(ppb) & 0xb8);
+
+ /* If not, drop back down to the scheduler and wait a timer tick */
+ if (!(r & 0x80))
+ return 0;
+ }
+ return 1; /* FINISH_RETURN */
+}
+
+/*
+ * Since the IMM itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void imm_interrupt(void *data)
+{
+ imm_struct *dev = (imm_struct *) data;
+ struct scsi_cmnd *cmd = dev->cur_cmd;
+ struct Scsi_Host *host = cmd->device->host;
+ unsigned long flags;
+
+ if (!cmd) {
+ printk("IMM: bug in imm_interrupt\n");
+ return;
+ }
+ if (imm_engine(dev, cmd)) {
+ INIT_WORK(&dev->imm_tq, imm_interrupt, (void *) dev);
+ schedule_delayed_work(&dev->imm_tq, 1);
+ return;
+ }
+ /* Command must of completed hence it is safe to let go... */
+#if IMM_DEBUG > 0
+ switch ((cmd->result >> 16) & 0xff) {
+ case DID_OK:
+ break;
+ case DID_NO_CONNECT:
+ printk("imm: no device at SCSI ID %i\n", cmd->device->id);
+ break;
+ case DID_BUS_BUSY:
+ printk("imm: BUS BUSY - EPP timeout detected\n");
+ break;
+ case DID_TIME_OUT:
+ printk("imm: unknown timeout\n");
+ break;
+ case DID_ABORT:
+ printk("imm: told to abort\n");
+ break;
+ case DID_PARITY:
+ printk("imm: parity error (???)\n");
+ break;
+ case DID_ERROR:
+ printk("imm: internal driver error\n");
+ break;
+ case DID_RESET:
+ printk("imm: told to reset device\n");
+ break;
+ case DID_BAD_INTR:
+ printk("imm: bad interrupt (???)\n");
+ break;
+ default:
+ printk("imm: bad return code (%02x)\n",
+ (cmd->result >> 16) & 0xff);
+ }
+#endif
+
+ if (cmd->SCp.phase > 1)
+ imm_disconnect(dev);
+
+ imm_pb_dismiss(dev);
+
+ spin_lock_irqsave(host->host_lock, flags);
+ dev->cur_cmd = NULL;
+ cmd->scsi_done(cmd);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return;
+}
+
+static int imm_engine(imm_struct *dev, struct scsi_cmnd *cmd)
+{
+ unsigned short ppb = dev->base;
+ unsigned char l = 0, h = 0;
+ int retv, x;
+
+ /* First check for any errors that may have occurred
+ * Here we check for internal errors
+ */
+ if (dev->failed)
+ return 0;
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Phase 0 - Waiting for parport */
+ if (time_after(jiffies, dev->jstart + HZ)) {
+ /*
+ * We waited more than a second
+ * for parport to call us
+ */
+ imm_fail(dev, DID_BUS_BUSY);
+ return 0;
+ }
+ return 1; /* wait until imm_wakeup claims parport */
+ /* Phase 1 - Connected */
+ case 1:
+ imm_connect(dev, CONNECT_EPP_MAYBE);
+ cmd->SCp.phase++;
+
+ /* Phase 2 - We are now talking to the scsi bus */
+ case 2:
+ if (!imm_select(dev, cmd->device->id)) {
+ imm_fail(dev, DID_NO_CONNECT);
+ return 0;
+ }
+ cmd->SCp.phase++;
+
+ /* Phase 3 - Ready to accept a command */
+ case 3:
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ if (!imm_send_command(cmd))
+ return 0;
+ cmd->SCp.phase++;
+
+ /* Phase 4 - Setup scatter/gather buffers */
+ case 4:
+ if (cmd->use_sg) {
+ /* if many buffers are available, start filling the first */
+ cmd->SCp.buffer =
+ (struct scatterlist *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr =
+ page_address(cmd->SCp.buffer->page) +
+ cmd->SCp.buffer->offset;
+ } else {
+ /* else fill the only available buffer */
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.ptr = cmd->request_buffer;
+ }
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.phase++;
+ if (cmd->SCp.this_residual & 0x01)
+ cmd->SCp.this_residual++;
+ /* Phase 5 - Pre-Data transfer stage */
+ case 5:
+ /* Spin lock for BUSY */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ /* Require negotiation for read requests */
+ x = (r_str(ppb) & 0xb8);
+ dev->rd = (x & 0x10) ? 1 : 0;
+ dev->dp = (x & 0x20) ? 0 : 1;
+
+ if ((dev->dp) && (dev->rd))
+ if (imm_negotiate(dev))
+ return 0;
+ cmd->SCp.phase++;
+
+ /* Phase 6 - Data transfer stage */
+ case 6:
+ /* Spin lock for BUSY */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ if (dev->dp) {
+ retv = imm_completion(cmd);
+ if (retv == -1)
+ return 0;
+ if (retv == 0)
+ return 1;
+ }
+ cmd->SCp.phase++;
+
+ /* Phase 7 - Post data transfer stage */
+ case 7:
+ if ((dev->dp) && (dev->rd)) {
+ if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) {
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0x4);
+ }
+ }
+ cmd->SCp.phase++;
+
+ /* Phase 8 - Read status/message */
+ case 8:
+ /* Check for data overrun */
+ if (imm_wait(dev) != (unsigned char) 0xb8) {
+ imm_fail(dev, DID_ERROR);
+ return 0;
+ }
+ if (imm_negotiate(dev))
+ return 0;
+ if (imm_in(dev, &l, 1)) { /* read status byte */
+ /* Check for optional message byte */
+ if (imm_wait(dev) == (unsigned char) 0xb8)
+ imm_in(dev, &h, 1);
+ cmd->result = (DID_OK << 16) + (l & STATUS_MASK);
+ }
+ if ((dev->mode == IMM_NIBBLE) || (dev->mode == IMM_PS2)) {
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0x4);
+ }
+ return 0; /* Finished */
+ break;
+
+ default:
+ printk("imm: Invalid scsi phase\n");
+ }
+ return 0;
+}
+
+static int imm_queuecommand(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ imm_struct *dev = imm_dev(cmd->device->host);
+
+ if (dev->cur_cmd) {
+ printk("IMM: bug in imm_queuecommand\n");
+ return 0;
+ }
+ dev->failed = 0;
+ dev->jstart = jiffies;
+ dev->cur_cmd = cmd;
+ cmd->scsi_done = done;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0; /* bus free */
+
+ INIT_WORK(&dev->imm_tq, imm_interrupt, dev);
+ schedule_work(&dev->imm_tq);
+
+ imm_pb_claim(dev);
+
+ return 0;
+}
+
+/*
+ * Apparently the disk->capacity attribute is off by 1 sector
+ * for all disk drives. We add the one here, but it should really
+ * be done in sd.c. Even if it gets fixed there, this will still
+ * work.
+ */
+static int imm_biosparam(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int ip[])
+{
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]);
+ }
+ return 0;
+}
+
+static int imm_abort(struct scsi_cmnd *cmd)
+{
+ imm_struct *dev = imm_dev(cmd->device->host);
+ /*
+ * There is no method for aborting commands since Iomega
+ * have tied the SCSI_MESSAGE line high in the interface
+ */
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Do not have access to parport */
+ case 1: /* Have not connected to interface */
+ dev->cur_cmd = NULL; /* Forget the problem */
+ return SUCCESS;
+ break;
+ default: /* SCSI command sent, can not abort */
+ return FAILED;
+ break;
+ }
+}
+
+static void imm_reset_pulse(unsigned int base)
+{
+ w_ctr(base, 0x04);
+ w_dtr(base, 0x40);
+ udelay(1);
+ w_ctr(base, 0x0c);
+ w_ctr(base, 0x0d);
+ udelay(50);
+ w_ctr(base, 0x0c);
+ w_ctr(base, 0x04);
+}
+
+static int imm_reset(struct scsi_cmnd *cmd)
+{
+ imm_struct *dev = imm_dev(cmd->device->host);
+
+ if (cmd->SCp.phase)
+ imm_disconnect(dev);
+ dev->cur_cmd = NULL; /* Forget the problem */
+
+ imm_connect(dev, CONNECT_NORMAL);
+ imm_reset_pulse(dev->base);
+ udelay(1000); /* device settle delay */
+ imm_disconnect(dev);
+ udelay(1000); /* device settle delay */
+ return SUCCESS;
+}
+
+static int device_check(imm_struct *dev)
+{
+ /* This routine looks for a device and then attempts to use EPP
+ to send a command. If all goes as planned then EPP is available. */
+
+ static char cmd[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ int loop, old_mode, status, k, ppb = dev->base;
+ unsigned char l;
+
+ old_mode = dev->mode;
+ for (loop = 0; loop < 8; loop++) {
+ /* Attempt to use EPP for Test Unit Ready */
+ if ((ppb & 0x0007) == 0x0000)
+ dev->mode = IMM_EPP_32;
+
+ second_pass:
+ imm_connect(dev, CONNECT_EPP_MAYBE);
+ /* Select SCSI device */
+ if (!imm_select(dev, loop)) {
+ imm_disconnect(dev);
+ continue;
+ }
+ printk("imm: Found device at ID %i, Attempting to use %s\n",
+ loop, IMM_MODE_STRING[dev->mode]);
+
+ /* Send SCSI command */
+ status = 1;
+ w_ctr(ppb, 0x0c);
+ for (l = 0; (l < 3) && (status); l++)
+ status = imm_out(dev, &cmd[l << 1], 2);
+
+ if (!status) {
+ imm_disconnect(dev);
+ imm_connect(dev, CONNECT_EPP_MAYBE);
+ imm_reset_pulse(dev->base);
+ udelay(1000);
+ imm_disconnect(dev);
+ udelay(1000);
+ if (dev->mode == IMM_EPP_32) {
+ dev->mode = old_mode;
+ goto second_pass;
+ }
+ printk("imm: Unable to establish communication\n");
+ return -EIO;
+ }
+ w_ctr(ppb, 0x0c);
+
+ k = 1000000; /* 1 Second */
+ do {
+ l = r_str(ppb);
+ k--;
+ udelay(1);
+ } while (!(l & 0x80) && (k));
+
+ l &= 0xb8;
+
+ if (l != 0xb8) {
+ imm_disconnect(dev);
+ imm_connect(dev, CONNECT_EPP_MAYBE);
+ imm_reset_pulse(dev->base);
+ udelay(1000);
+ imm_disconnect(dev);
+ udelay(1000);
+ if (dev->mode == IMM_EPP_32) {
+ dev->mode = old_mode;
+ goto second_pass;
+ }
+ printk
+ ("imm: Unable to establish communication\n");
+ return -EIO;
+ }
+ imm_disconnect(dev);
+ printk
+ ("imm: Communication established at 0x%x with ID %i using %s\n",
+ ppb, loop, IMM_MODE_STRING[dev->mode]);
+ imm_connect(dev, CONNECT_EPP_MAYBE);
+ imm_reset_pulse(dev->base);
+ udelay(1000);
+ imm_disconnect(dev);
+ udelay(1000);
+ return 0;
+ }
+ printk("imm: No devices found\n");
+ return -ENODEV;
+}
+
+static int imm_adjust_queue(struct scsi_device *device)
+{
+ blk_queue_bounce_limit(device->request_queue, BLK_BOUNCE_HIGH);
+ return 0;
+}
+
+static struct scsi_host_template imm_template = {
+ .module = THIS_MODULE,
+ .proc_name = "imm",
+ .proc_info = imm_proc_info,
+ .name = "Iomega VPI2 (imm) interface",
+ .queuecommand = imm_queuecommand,
+ .eh_abort_handler = imm_abort,
+ .eh_bus_reset_handler = imm_reset,
+ .eh_host_reset_handler = imm_reset,
+ .bios_param = imm_biosparam,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .can_queue = 1,
+ .slave_alloc = imm_adjust_queue,
+ .unchecked_isa_dma = 1, /* imm cannot deal with highmem, so
+ * this is an easy trick to ensure
+ * all io pages for this host reside
+ * in low memory */
+};
+
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
+
+static LIST_HEAD(imm_hosts);
+
+static int __imm_attach(struct parport *pb)
+{
+ struct Scsi_Host *host;
+ imm_struct *dev;
+ DECLARE_WAIT_QUEUE_HEAD(waiting);
+ DEFINE_WAIT(wait);
+ int ports;
+ int modes, ppb;
+ int err = -ENOMEM;
+
+ init_waitqueue_head(&waiting);
+
+ dev = kmalloc(sizeof(imm_struct), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ memset(dev, 0, sizeof(imm_struct));
+
+ dev->base = -1;
+ dev->mode = IMM_AUTODETECT;
+ INIT_LIST_HEAD(&dev->list);
+
+ dev->dev = parport_register_device(pb, "imm", NULL, imm_wakeup,
+ NULL, 0, dev);
+
+ if (!dev->dev)
+ goto out;
+
+
+ /* Claim the bus so it remembers what we do to the control
+ * registers. [ CTR and ECP ]
+ */
+ err = -EBUSY;
+ dev->waiting = &waiting;
+ prepare_to_wait(&waiting, &wait, TASK_UNINTERRUPTIBLE);
+ if (imm_pb_claim(dev))
+ schedule_timeout(3 * HZ);
+ if (dev->wanted) {
+ printk(KERN_ERR "imm%d: failed to claim parport because "
+ "a pardevice is owning the port for too long "
+ "time!\n", pb->number);
+ imm_pb_dismiss(dev);
+ dev->waiting = NULL;
+ finish_wait(&waiting, &wait);
+ goto out1;
+ }
+ dev->waiting = NULL;
+ finish_wait(&waiting, &wait);
+ ppb = dev->base = dev->dev->port->base;
+ dev->base_hi = dev->dev->port->base_hi;
+ w_ctr(ppb, 0x0c);
+ modes = dev->dev->port->modes;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ dev->mode = IMM_NIBBLE;
+
+ if (modes & PARPORT_MODE_TRISTATE)
+ dev->mode = IMM_PS2;
+
+ /* Done configuration */
+
+ err = imm_init(dev);
+
+ imm_pb_release(dev);
+
+ if (err)
+ goto out1;
+
+ /* now the glue ... */
+ if (dev->mode == IMM_NIBBLE || dev->mode == IMM_PS2)
+ ports = 3;
+ else
+ ports = 8;
+
+ INIT_WORK(&dev->imm_tq, imm_interrupt, dev);
+
+ err = -ENOMEM;
+ host = scsi_host_alloc(&imm_template, sizeof(imm_struct *));
+ if (!host)
+ goto out1;
+ host->io_port = pb->base;
+ host->n_io_port = ports;
+ host->dma_channel = -1;
+ host->unique_id = pb->number;
+ *(imm_struct **)&host->hostdata = dev;
+ dev->host = host;
+ list_add_tail(&dev->list, &imm_hosts);
+ err = scsi_add_host(host, NULL);
+ if (err)
+ goto out2;
+ scsi_scan_host(host);
+ return 0;
+
+out2:
+ list_del_init(&dev->list);
+ scsi_host_put(host);
+out1:
+ parport_unregister_device(dev->dev);
+out:
+ kfree(dev);
+ return err;
+}
+
+static void imm_attach(struct parport *pb)
+{
+ __imm_attach(pb);
+}
+
+static void imm_detach(struct parport *pb)
+{
+ imm_struct *dev;
+ list_for_each_entry(dev, &imm_hosts, list) {
+ if (dev->dev->port == pb) {
+ list_del_init(&dev->list);
+ scsi_remove_host(dev->host);
+ scsi_host_put(dev->host);
+ parport_unregister_device(dev->dev);
+ kfree(dev);
+ break;
+ }
+ }
+}
+
+static struct parport_driver imm_driver = {
+ .name = "imm",
+ .attach = imm_attach,
+ .detach = imm_detach,
+};
+
+static int __init imm_driver_init(void)
+{
+ printk("imm: Version %s\n", IMM_VERSION);
+ return parport_register_driver(&imm_driver);
+}
+
+static void __exit imm_driver_exit(void)
+{
+ parport_unregister_driver(&imm_driver);
+}
+
+module_init(imm_driver_init);
+module_exit(imm_driver_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/imm.h b/drivers/scsi/imm.h
new file mode 100644
index 000000000000..dc3aebf0e365
--- /dev/null
+++ b/drivers/scsi/imm.h
@@ -0,0 +1,144 @@
+
+/* Driver for the Iomega MatchMaker parallel port SCSI HBA embedded in
+ * the Iomega ZIP Plus drive
+ *
+ * (c) 1998 David Campbell campbell@torque.net
+ *
+ * Please note that I live in Perth, Western Australia. GMT+0800
+ */
+
+#ifndef _IMM_H
+#define _IMM_H
+
+#define IMM_VERSION "2.05 (for Linux 2.4.0)"
+
+/*
+ * 10 Apr 1998 (Good Friday) - Received EN144302 by email from Iomega.
+ * Scarry thing is the level of support from one of their managers.
+ * The onus is now on us (the developers) to shut up and start coding.
+ * 11Apr98 [ 0.10 ]
+ *
+ * --- SNIP ---
+ *
+ * It manages to find the drive which is a good start. Writing data during
+ * data phase is known to be broken (due to requirements of two byte writes).
+ * Removing "Phase" debug messages.
+ *
+ * PS: Took four hours of coding after I bought a drive.
+ * ANZAC Day (Aus "War Veterans Holiday") 25Apr98 [ 0.14 ]
+ *
+ * Ten minutes later after a few fixes.... (LITERALLY!!!)
+ * Have mounted disk, copied file, dismounted disk, remount disk, diff file
+ * ----- It actually works!!! -----
+ * 25Apr98 [ 0.15 ]
+ *
+ * Twenty minutes of mucking around, rearanged the IEEE negotiate mechanism.
+ * Now have byte mode working (only EPP and ECP to go now... :=)
+ * 26Apr98 [ 0.16 ]
+ *
+ * Thirty minutes of further coding results in EPP working on my machine.
+ * 27Apr98 [ 0.17 ]
+ *
+ * Due to work commitments and inability to get a "true" ECP mode functioning
+ * I have decided to code the parport support into imm.
+ * 09Jun98 [ 0.18 ]
+ *
+ * Driver is now out of beta testing.
+ * Support for parport has been added.
+ * Now distributed with the ppa driver.
+ * 12Jun98 [ 2.00 ]
+ *
+ * Err.. It appears that imm-2.00 was broken....
+ * 18Jun98 [ 2.01 ]
+ *
+ * Patch applied to sync this against the Linux 2.1.x kernel code
+ * Included qboot_zip.sh
+ * 21Jun98 [ 2.02 ]
+ *
+ * Other clean ups include the follow changes:
+ * CONFIG_SCSI_PPA_HAVE_PEDANTIC => CONFIG_SCSI_IZIP_EPP16
+ * added CONFIG_SCSI_IZIP_SLOW_CTR option
+ * [2.03]
+ * Fix kernel panic on scsi timeout. 20Aug00 [2.04]
+ *
+ * Avoid io_request_lock problems.
+ * John Cavan <johncavan@home.com> 16Nov00 [2.05]
+ */
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <scsi/scsi_host.h>
+/* batteries not included :-) */
+
+/*
+ * modes in which the driver can operate
+ */
+#define IMM_AUTODETECT 0 /* Autodetect mode */
+#define IMM_NIBBLE 1 /* work in standard 4 bit mode */
+#define IMM_PS2 2 /* PS/2 byte mode */
+#define IMM_EPP_8 3 /* EPP mode, 8 bit */
+#define IMM_EPP_16 4 /* EPP mode, 16 bit */
+#define IMM_EPP_32 5 /* EPP mode, 32 bit */
+#define IMM_UNKNOWN 6 /* Just in case... */
+
+static char *IMM_MODE_STRING[] =
+{
+ [IMM_AUTODETECT] = "Autodetect",
+ [IMM_NIBBLE] = "SPP",
+ [IMM_PS2] = "PS/2",
+ [IMM_EPP_8] = "EPP 8 bit",
+ [IMM_EPP_16] = "EPP 16 bit",
+#ifdef CONFIG_SCSI_IZIP_EPP16
+ [IMM_EPP_32] = "EPP 16 bit",
+#else
+ [IMM_EPP_32] = "EPP 32 bit",
+#endif
+ [IMM_UNKNOWN] = "Unknown",
+};
+
+/* other options */
+#define IMM_BURST_SIZE 512 /* data burst size */
+#define IMM_SELECT_TMO 500 /* 500 how long to wait for target ? */
+#define IMM_SPIN_TMO 5000 /* 50000 imm_wait loop limiter */
+#define IMM_DEBUG 0 /* debugging option */
+#define IN_EPP_MODE(x) (x == IMM_EPP_8 || x == IMM_EPP_16 || x == IMM_EPP_32)
+
+/* args to imm_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL 0
+
+#define r_dtr(x) (unsigned char)inb((x))
+#define r_str(x) (unsigned char)inb((x)+1)
+#define r_ctr(x) (unsigned char)inb((x)+2)
+#define r_epp(x) (unsigned char)inb((x)+4)
+#define r_fifo(x) (unsigned char)inb((x)) /* x must be base_hi */
+ /* On PCI is: base+0x400 != base_hi */
+#define r_ecr(x) (unsigned char)inb((x)+2) /* x must be base_hi */
+
+#define w_dtr(x,y) outb(y, (x))
+#define w_str(x,y) outb(y, (x)+1)
+#define w_epp(x,y) outb(y, (x)+4)
+#define w_fifo(x,y) outb(y, (x)) /* x must be base_hi */
+#define w_ecr(x,y) outb(y, (x)+0x2) /* x must be base_hi */
+
+#ifdef CONFIG_SCSI_IZIP_SLOW_CTR
+#define w_ctr(x,y) outb_p(y, (x)+2)
+#else
+#define w_ctr(x,y) outb(y, (x)+2)
+#endif
+
+static int imm_engine(imm_struct *, struct scsi_cmnd *);
+
+#endif /* _IMM_H */
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
new file mode 100644
index 000000000000..0bb0369efb2d
--- /dev/null
+++ b/drivers/scsi/in2000.c
@@ -0,0 +1,2323 @@
+/*
+ * in2000.c - Linux device driver for the
+ * Always IN2000 ISA SCSI card.
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * john@geolog.com
+ * jshiffle@netcom.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * Drew Eckhardt's excellent 'Generic NCR5380' sources provided
+ * much of the inspiration and some of the code for this driver.
+ * The Linux IN2000 driver distributed in the Linux kernels through
+ * version 1.2.13 was an extremely valuable reference on the arcane
+ * (and still mysterious) workings of the IN2000's fifo. It also
+ * is where I lifted in2000_biosparam(), the gist of the card
+ * detection scheme, and other bits of code. Many thanks to the
+ * talented and courageous people who wrote, contributed to, and
+ * maintained that driver (including Brad McLean, Shaun Savage,
+ * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey,
+ * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric
+ * Youngdale). I should also mention the driver written by
+ * Hamish Macdonald for the (GASP!) Amiga A2091 card, included
+ * in the Linux-m68k distribution; it gave me a good initial
+ * understanding of the proper way to run a WD33c93 chip, and I
+ * ended up stealing lots of code from it.
+ *
+ * _This_ driver is (I feel) an improvement over the old one in
+ * several respects:
+ * - All problems relating to the data size of a SCSI request are
+ * gone (as far as I know). The old driver couldn't handle
+ * swapping to partitions because that involved 4k blocks, nor
+ * could it deal with the st.c tape driver unmodified, because
+ * that usually involved 4k - 32k blocks. The old driver never
+ * quite got away from a morbid dependence on 2k block sizes -
+ * which of course is the size of the card's fifo.
+ *
+ * - Target Disconnection/Reconnection is now supported. Any
+ * system with more than one device active on the SCSI bus
+ * will benefit from this. The driver defaults to what I'm
+ * calling 'adaptive disconnect' - meaning that each command
+ * is evaluated individually as to whether or not it should
+ * be run with the option to disconnect/reselect (if the
+ * device chooses), or as a "SCSI-bus-hog".
+ *
+ * - Synchronous data transfers are now supported. Because there
+ * are a few devices (and many improperly terminated systems)
+ * that choke when doing sync, the default is sync DISABLED
+ * for all devices. This faster protocol can (and should!)
+ * be enabled on selected devices via the command-line.
+ *
+ * - Runtime operating parameters can now be specified through
+ * either the LILO or the 'insmod' command line. For LILO do:
+ * "in2000=blah,blah,blah"
+ * and with insmod go like:
+ * "insmod /usr/src/linux/modules/in2000.o setup_strings=blah,blah"
+ * The defaults should be good for most people. See the comment
+ * for 'setup_strings' below for more details.
+ *
+ * - The old driver relied exclusively on what the Western Digital
+ * docs call "Combination Level 2 Commands", which are a great
+ * idea in that the CPU is relieved of a lot of interrupt
+ * overhead. However, by accepting a certain (user-settable)
+ * amount of additional interrupts, this driver achieves
+ * better control over the SCSI bus, and data transfers are
+ * almost as fast while being much easier to define, track,
+ * and debug.
+ *
+ * - You can force detection of a card whose BIOS has been disabled.
+ *
+ * - Multiple IN2000 cards might almost be supported. I've tried to
+ * keep it in mind, but have no way to test...
+ *
+ *
+ * TODO:
+ * tagged queuing. multiple cards.
+ *
+ *
+ * NOTE:
+ * When using this or any other SCSI driver as a module, you'll
+ * find that with the stock kernel, at most _two_ SCSI hard
+ * drives will be linked into the device list (ie, usable).
+ * If your IN2000 card has more than 2 disks on its bus, you
+ * might want to change the define of 'SD_EXTRA_DEVS' in the
+ * 'hosts.h' file from 2 to whatever is appropriate. It took
+ * me a while to track down this surprisingly obscure and
+ * undocumented little "feature".
+ *
+ *
+ * People with bug reports, wish-lists, complaints, comments,
+ * or improvements are asked to pah-leeez email me (John Shifflett)
+ * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get
+ * this thing into as good a shape as possible, and I'm positive
+ * there are lots of lurking bugs and "Stupid Places".
+ *
+ * Updated for Linux 2.5 by Alan Cox <alan@redhat.com>
+ * - Using new_eh handler
+ * - Hopefully got all the locking right again
+ * See "FIXME" notes for items that could do with more work
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#define IN2000_VERSION "1.33-2.5"
+#define IN2000_DATE "2002/11/03"
+
+#include "in2000.h"
+
+
+/*
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with a LILO or insmod command-line, those
+ * settings are combined with 'setup_args[]'. Note that LILO command-lines
+ * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix.
+ * The driver recognizes the following keywords (lower case required) and
+ * arguments:
+ *
+ * - ioport:addr -Where addr is IO address of a (usually ROM-less) card.
+ * - noreset -No optional args. Prevents SCSI bus reset at boot time.
+ * - nosync:x -x is a bitmask where the 1st 7 bits correspond with
+ * the 7 possible SCSI devices (bit 0 for device #0, etc).
+ * Set a bit to PREVENT sync negotiation on that device.
+ * The driver default is sync DISABLED on all devices.
+ * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer
+ * period. Default is 500; acceptable values are 250 - 1000.
+ * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them.
+ * x = 1 does 'adaptive' disconnects, which is the default
+ * and generally the best choice.
+ * - debug:x -If 'DEBUGGING_ON' is defined, x is a bitmask that causes
+ * various types of debug output to printed - see the DB_xxx
+ * defines in in2000.h
+ * - proc:x -If 'PROC_INTERFACE' is defined, x is a bitmask that
+ * determines how the /proc interface works and what it
+ * does - see the PR_xxx defines in in2000.h
+ *
+ * Syntax Notes:
+ * - Numeric arguments can be decimal or the '0x' form of hex notation. There
+ * _must_ be a colon between a keyword and its numeric argument, with no
+ * spaces.
+ * - Keywords are separated by commas, no spaces, in the standard kernel
+ * command-line manner.
+ * - A keyword in the 'nth' comma-separated command-line member will overwrite
+ * the 'nth' element of setup_args[]. A blank command-line member (in
+ * other words, a comma with no preceding keyword) will _not_ overwrite
+ * the corresponding setup_args[] element.
+ *
+ * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
+ * - in2000=ioport:0x220,noreset
+ * - in2000=period:250,disconnect:2,nosync:0x03
+ * - in2000=debug:0x1e
+ * - in2000=proc:3
+ */
+
+/* Normally, no defaults are specified... */
+static char *setup_args[] = { "", "", "", "", "", "", "", "", "" };
+
+/* filled in by 'insmod' */
+static char *setup_strings;
+
+module_param(setup_strings, charp, 0);
+
+static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
+{
+ write1_io(reg_num, IO_WD_ADDR);
+ return read1_io(IO_WD_DATA);
+}
+
+
+#define READ_AUX_STAT() read1_io(IO_WD_ASR)
+
+
+static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
+{
+ write1_io(reg_num, IO_WD_ADDR);
+ write1_io(value, IO_WD_DATA);
+}
+
+
+static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
+{
+/* while (READ_AUX_STAT() & ASR_CIP)
+ printk("|");*/
+ write1_io(WD_COMMAND, IO_WD_ADDR);
+ write1_io(cmd, IO_WD_DATA);
+}
+
+
+static uchar read_1_byte(struct IN2000_hostdata *hostdata)
+{
+ uchar asr, x = 0;
+
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393_cmd(hostdata, WD_CMD_TRANS_INFO | 0x80);
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ x = read_3393(hostdata, WD_DATA);
+ } while (!(asr & ASR_INT));
+ return x;
+}
+
+
+static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
+{
+ write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR);
+ write1_io((value >> 16), IO_WD_DATA);
+ write1_io((value >> 8), IO_WD_DATA);
+ write1_io(value, IO_WD_DATA);
+}
+
+
+static unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
+{
+ unsigned long value;
+
+ write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR);
+ value = read1_io(IO_WD_DATA) << 16;
+ value |= read1_io(IO_WD_DATA) << 8;
+ value |= read1_io(IO_WD_DATA);
+ return value;
+}
+
+
+/* The 33c93 needs to be told which direction a command transfers its
+ * data; we use this function to figure it out. Returns true if there
+ * will be a DATA_OUT phase with this command, false otherwise.
+ * (Thanks to Joerg Dorchain for the research and suggestion.)
+ */
+static int is_dir_out(Scsi_Cmnd * cmd)
+{
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_LONG:
+ case WRITE_SAME:
+ case WRITE_BUFFER:
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case COMPARE:
+ case COPY:
+ case COPY_VERIFY:
+ case SEARCH_EQUAL:
+ case SEARCH_HIGH:
+ case SEARCH_LOW:
+ case SEARCH_EQUAL_12:
+ case SEARCH_HIGH_12:
+ case SEARCH_LOW_12:
+ case FORMAT_UNIT:
+ case REASSIGN_BLOCKS:
+ case RESERVE:
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case LOG_SELECT:
+ case SEND_DIAGNOSTIC:
+ case CHANGE_DEFINITION:
+ case UPDATE_BLOCK:
+ case SET_WINDOW:
+ case MEDIUM_SCAN:
+ case SEND_VOLUME_TAG:
+ case 0xea:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+
+static struct sx_period sx_table[] = {
+ {1, 0x20},
+ {252, 0x20},
+ {376, 0x30},
+ {500, 0x40},
+ {624, 0x50},
+ {752, 0x60},
+ {876, 0x70},
+ {1000, 0x00},
+ {0, 0}
+};
+
+static int round_period(unsigned int period)
+{
+ int x;
+
+ for (x = 1; sx_table[x].period_ns; x++) {
+ if ((period <= sx_table[x - 0].period_ns) && (period > sx_table[x - 1].period_ns)) {
+ return x;
+ }
+ }
+ return 7;
+}
+
+static uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+ uchar result;
+
+ period *= 4; /* convert SDTR code to ns */
+ result = sx_table[round_period(period)].reg_value;
+ result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;
+ return result;
+}
+
+
+
+static void in2000_execute(struct Scsi_Host *instance);
+
+static int in2000_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ struct Scsi_Host *instance;
+ struct IN2000_hostdata *hostdata;
+ Scsi_Cmnd *tmp;
+
+ instance = cmd->device->host;
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+
+ DB(DB_QUEUE_COMMAND, printk("Q-%d-%02x-%ld(", cmd->device->id, cmd->cmnd[0], cmd->pid))
+
+/* Set up a few fields in the Scsi_Cmnd structure for our own use:
+ * - host_scribble is the pointer to the next cmd in the input queue
+ * - scsi_done points to the routine we call when a cmd is finished
+ * - result is what you'd expect
+ */
+ cmd->host_scribble = NULL;
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+/* We use the Scsi_Pointer structure that's included with each command
+ * as a scratchpad (as it's intended to be used!). The handy thing about
+ * the SCp.xxx fields is that they're always associated with a given
+ * cmd, and are preserved across disconnect-reselect. This means we
+ * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
+ * if we keep all the critical pointers and counters in SCp:
+ * - SCp.ptr is the pointer into the RAM buffer
+ * - SCp.this_residual is the size of that buffer
+ * - SCp.buffer points to the current scatter-gather buffer
+ * - SCp.buffers_residual tells us how many S.G. buffers there are
+ * - SCp.have_data_in helps keep track of >2048 byte transfers
+ * - SCp.sent_command is not used
+ * - SCp.phase records this command's SRCID_ER bit setting
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *) page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+ cmd->SCp.have_data_in = 0;
+
+/* We don't set SCp.phase here - that's done in in2000_execute() */
+
+/* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
+
+ cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
+
+/* We need to disable interrupts before messing with the input
+ * queue and calling in2000_execute().
+ */
+
+ /*
+ * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE
+ * commands are added to the head of the queue so that the desired
+ * sense data is not lost before REQUEST_SENSE executes.
+ */
+
+ if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ cmd->host_scribble = (uchar *) hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ } else { /* find the end of the queue */
+ for (tmp = (Scsi_Cmnd *) hostdata->input_Q; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);
+ tmp->host_scribble = (uchar *) cmd;
+ }
+
+/* We know that there's at least one command in 'input_Q' now.
+ * Go see if any of them are runnable!
+ */
+
+ in2000_execute(cmd->device->host);
+
+ DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->pid))
+ return 0;
+}
+
+
+
+/*
+ * This routine attempts to start a scsi command. If the host_card is
+ * already connected, we give up immediately. Otherwise, look through
+ * the input_Q, using the first command we find that's intended
+ * for a currently non-busy target/lun.
+ * Note that this function is always called with interrupts already
+ * disabled (either from in2000_queuecommand() or in2000_intr()).
+ */
+static void in2000_execute(struct Scsi_Host *instance)
+{
+ struct IN2000_hostdata *hostdata;
+ Scsi_Cmnd *cmd, *prev;
+ int i;
+ unsigned short *sp;
+ unsigned short f;
+ unsigned short flushbuf[16];
+
+
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+
+ DB(DB_EXECUTE, printk("EX("))
+
+ if (hostdata->selecting || hostdata->connected) {
+
+ DB(DB_EXECUTE, printk(")EX-0 "))
+
+ return;
+ }
+
+ /*
+ * Search through the input_Q for a command destined
+ * for an idle target/lun.
+ */
+
+ cmd = (Scsi_Cmnd *) hostdata->input_Q;
+ prev = NULL;
+ while (cmd) {
+ if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)))
+ break;
+ prev = cmd;
+ cmd = (Scsi_Cmnd *) cmd->host_scribble;
+ }
+
+ /* quit if queue empty or all possible targets are busy */
+
+ if (!cmd) {
+
+ DB(DB_EXECUTE, printk(")EX-1 "))
+
+ return;
+ }
+
+ /* remove command from queue */
+
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ else
+ hostdata->input_Q = (Scsi_Cmnd *) cmd->host_scribble;
+
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[cmd->device->id]++;
+#endif
+
+/*
+ * Start the selection process
+ */
+
+ if (is_dir_out(cmd))
+ write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id);
+ else
+ write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
+
+/* Now we need to figure out whether or not this command is a good
+ * candidate for disconnect/reselect. We guess to the best of our
+ * ability, based on a set of hierarchical rules. When several
+ * devices are operating simultaneously, disconnects are usually
+ * an advantage. In a single device system, or if only 1 device
+ * is being accessed, transfers usually go faster if disconnects
+ * are not allowed:
+ *
+ * + Commands should NEVER disconnect if hostdata->disconnect =
+ * DIS_NEVER (this holds for tape drives also), and ALWAYS
+ * disconnect if hostdata->disconnect = DIS_ALWAYS.
+ * + Tape drive commands should always be allowed to disconnect.
+ * + Disconnect should be allowed if disconnected_Q isn't empty.
+ * + Commands should NOT disconnect if input_Q is empty.
+ * + Disconnect should be allowed if there are commands in input_Q
+ * for a different target/lun. In this case, the other commands
+ * should be made disconnect-able, if not already.
+ *
+ * I know, I know - this code would flunk me out of any
+ * "C Programming 101" class ever offered. But it's easy
+ * to change around and experiment with for now.
+ */
+
+ cmd->SCp.phase = 0; /* assume no disconnect */
+ if (hostdata->disconnect == DIS_NEVER)
+ goto no;
+ if (hostdata->disconnect == DIS_ALWAYS)
+ goto yes;
+ if (cmd->device->type == 1) /* tape drive? */
+ goto yes;
+ if (hostdata->disconnected_Q) /* other commands disconnected? */
+ goto yes;
+ if (!(hostdata->input_Q)) /* input_Q empty? */
+ goto no;
+ for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) {
+ if ((prev->device->id != cmd->device->id) || (prev->device->lun != cmd->device->lun)) {
+ for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble)
+ prev->SCp.phase = 1;
+ goto yes;
+ }
+ }
+ goto no;
+
+ yes:
+ cmd->SCp.phase = 1;
+
+#ifdef PROC_STATISTICS
+ hostdata->disc_allowed_cnt[cmd->device->id]++;
+#endif
+
+ no:
+ write_3393(hostdata, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0));
+
+ write_3393(hostdata, WD_TARGET_LUN, cmd->device->lun);
+ write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]);
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+
+ if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) {
+
+ /*
+ * Do a 'Select-With-ATN' command. This will end with
+ * one of the following interrupts:
+ * CSR_RESEL_AM: failure - can try again later.
+ * CSR_TIMEOUT: failure - give up.
+ * CSR_SELECT: success - proceed.
+ */
+
+ hostdata->selecting = cmd;
+
+/* Every target has its own synchronous transfer setting, kept in
+ * the sync_xfer array, and a corresponding status byte in sync_stat[].
+ * Each target's sync_stat[] entry is initialized to SS_UNSET, and its
+ * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET
+ * means that the parameters are undetermined as yet, and that we
+ * need to send an SDTR message to this device after selection is
+ * complete. We set SS_FIRST to tell the interrupt routine to do so,
+ * unless we don't want to even _try_ synchronous transfers: In this
+ * case we set SS_SET to make the defaults final.
+ */
+ if (hostdata->sync_stat[cmd->device->id] == SS_UNSET) {
+ if (hostdata->sync_off & (1 << cmd->device->id))
+ hostdata->sync_stat[cmd->device->id] = SS_SET;
+ else
+ hostdata->sync_stat[cmd->device->id] = SS_FIRST;
+ }
+ hostdata->state = S_SELECTING;
+ write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN);
+ }
+
+ else {
+
+ /*
+ * Do a 'Select-With-ATN-Xfer' command. This will end with
+ * one of the following interrupts:
+ * CSR_RESEL_AM: failure - can try again later.
+ * CSR_TIMEOUT: failure - give up.
+ * anything else: success - proceed.
+ */
+
+ hostdata->connected = cmd;
+ write_3393(hostdata, WD_COMMAND_PHASE, 0);
+
+ /* copy command_descriptor_block into WD chip
+ * (take advantage of auto-incrementing)
+ */
+
+ write1_io(WD_CDB_1, IO_WD_ADDR);
+ for (i = 0; i < cmd->cmd_len; i++)
+ write1_io(cmd->cmnd[i], IO_WD_DATA);
+
+ /* The wd33c93 only knows about Group 0, 1, and 5 commands when
+ * it's doing a 'select-and-transfer'. To be safe, we write the
+ * size of the CDB into the OWN_ID register for every case. This
+ * way there won't be problems with vendor-unique, audio, etc.
+ */
+
+ write_3393(hostdata, WD_OWN_ID, cmd->cmd_len);
+
+ /* When doing a non-disconnect command, we can save ourselves a DATA
+ * phase interrupt later by setting everything up now. With writes we
+ * need to pre-fill the fifo; if there's room for the 32 flush bytes,
+ * put them in there too - that'll avoid a fifo interrupt. Reads are
+ * somewhat simpler.
+ * KLUDGE NOTE: It seems that you can't completely fill the fifo here:
+ * This results in the IO_FIFO_COUNT register rolling over to zero,
+ * and apparently the gate array logic sees this as empty, not full,
+ * so the 3393 chip is never signalled to start reading from the
+ * fifo. Or maybe it's seen as a permanent fifo interrupt condition.
+ * Regardless, we fix this by temporarily pretending that the fifo
+ * is 16 bytes smaller. (I see now that the old driver has a comment
+ * about "don't fill completely" in an analogous place - must be the
+ * same deal.) This results in CDROM, swap partitions, and tape drives
+ * needing an extra interrupt per write command - I think we can live
+ * with that!
+ */
+
+ if (!(cmd->SCp.phase)) {
+ write_3393_count(hostdata, cmd->SCp.this_residual);
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
+ write1_io(0, IO_FIFO_WRITE); /* clear fifo counter, write mode */
+
+ if (is_dir_out(cmd)) {
+ hostdata->fifo = FI_FIFO_WRITING;
+ if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16))
+ i = IN2000_FIFO_SIZE - 16;
+ cmd->SCp.have_data_in = i; /* this much data in fifo */
+ i >>= 1; /* Gulp. Assuming modulo 2. */
+ sp = (unsigned short *) cmd->SCp.ptr;
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_WRITE_IO
+
+ FAST_WRITE2_IO();
+#else
+ while (i--)
+ write2_io(*sp++, IO_FIFO);
+
+#endif
+
+ /* Is there room for the flush bytes? */
+
+ if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) {
+ sp = flushbuf;
+ i = 16;
+
+#ifdef FAST_WRITE_IO
+
+ FAST_WRITE2_IO();
+#else
+ while (i--)
+ write2_io(0, IO_FIFO);
+
+#endif
+
+ }
+ }
+
+ else {
+ write1_io(0, IO_FIFO_READ); /* put fifo in read mode */
+ hostdata->fifo = FI_FIFO_READING;
+ cmd->SCp.have_data_in = 0; /* nothing transferred yet */
+ }
+
+ } else {
+ write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */
+ }
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ }
+
+ /*
+ * Since the SCSI bus can handle only 1 connection at a time,
+ * we get out of here now. If the selection fails, or when
+ * the command disconnects, we'll come back to this routine
+ * to search the input_Q again...
+ */
+
+ DB(DB_EXECUTE, printk("%s%ld)EX-2 ", (cmd->SCp.phase) ? "d:" : "", cmd->pid))
+
+}
+
+
+
+static void transfer_pio(uchar * buf, int cnt, int data_in_dir, struct IN2000_hostdata *hostdata)
+{
+ uchar asr;
+
+ DB(DB_TRANSFER, printk("(%p,%d,%s)", buf, cnt, data_in_dir ? "in" : "out"))
+
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393_count(hostdata, cnt);
+ write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);
+ if (data_in_dir) {
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ *buf++ = read_3393(hostdata, WD_DATA);
+ } while (!(asr & ASR_INT));
+ } else {
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ write_3393(hostdata, WD_DATA, *buf++);
+ } while (!(asr & ASR_INT));
+ }
+
+ /* Note: we are returning with the interrupt UN-cleared.
+ * Since (presumably) an entire I/O operation has
+ * completed, the bus phase is probably different, and
+ * the interrupt routine will discover this when it
+ * responds to the uncleared int.
+ */
+
+}
+
+
+
+static void transfer_bytes(Scsi_Cmnd * cmd, int data_in_dir)
+{
+ struct IN2000_hostdata *hostdata;
+ unsigned short *sp;
+ unsigned short f;
+ int i;
+
+ hostdata = (struct IN2000_hostdata *) cmd->device->host->hostdata;
+
+/* Normally, you'd expect 'this_residual' to be non-zero here.
+ * In a series of scatter-gather transfers, however, this
+ * routine will usually be called with 'this_residual' equal
+ * to 0 and 'buffers_residual' non-zero. This means that a
+ * previous transfer completed, clearing 'this_residual', and
+ * now we need to setup the next scatter-gather buffer as the
+ * source or destination for THIS transfer.
+ */
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset;
+ }
+
+/* Set up hardware registers */
+
+ write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]);
+ write_3393_count(hostdata, cmd->SCp.this_residual);
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
+ write1_io(0, IO_FIFO_WRITE); /* zero counter, assume write */
+
+/* Reading is easy. Just issue the command and return - we'll
+ * get an interrupt later when we have actual data to worry about.
+ */
+
+ if (data_in_dir) {
+ write1_io(0, IO_FIFO_READ);
+ if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
+ write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else
+ write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);
+ hostdata->fifo = FI_FIFO_READING;
+ cmd->SCp.have_data_in = 0;
+ return;
+ }
+
+/* Writing is more involved - we'll start the WD chip and write as
+ * much data to the fifo as we can right now. Later interrupts will
+ * write any bytes that don't make it at this stage.
+ */
+
+ if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
+ write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else
+ write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);
+ hostdata->fifo = FI_FIFO_WRITING;
+ sp = (unsigned short *) cmd->SCp.ptr;
+
+ if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE)
+ i = IN2000_FIFO_SIZE;
+ cmd->SCp.have_data_in = i;
+ i >>= 1; /* Gulp. We assume this_residual is modulo 2 */
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_WRITE_IO
+
+ FAST_WRITE2_IO();
+#else
+ while (i--)
+ write2_io(*sp++, IO_FIFO);
+
+#endif
+
+}
+
+
+/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this
+ * function in order to work in an SMP environment. (I'd be surprised
+ * if the driver is ever used by anyone on a real multi-CPU motherboard,
+ * but it _does_ need to be able to compile and run in an SMP kernel.)
+ */
+
+static irqreturn_t in2000_intr(int irqnum, void *dev_id, struct pt_regs *ptregs)
+{
+ struct Scsi_Host *instance = dev_id;
+ struct IN2000_hostdata *hostdata;
+ Scsi_Cmnd *patch, *cmd;
+ uchar asr, sr, phs, id, lun, *ucp, msg;
+ int i, j;
+ unsigned long length;
+ unsigned short *sp;
+ unsigned short f;
+ unsigned long flags;
+
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+
+/* Get the spin_lock and disable further ints, for SMP */
+
+ spin_lock_irqsave(instance->host_lock, flags);
+
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt++;
+#endif
+
+/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
+ * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
+ * with a big logic array, so it's a little different than what you might
+ * expect). As far as I know, there's no reason that BOTH can't be active
+ * at the same time, but there's a problem: while we can read the 3393
+ * to tell if _it_ wants an interrupt, I don't know of a way to ask the
+ * fifo the same question. The best we can do is check the 3393 and if
+ * it _isn't_ the source of the interrupt, then we can be pretty sure
+ * that the fifo is the culprit.
+ * UPDATE: I have it on good authority (Bill Earnest) that bit 0 of the
+ * IO_FIFO_COUNT register mirrors the fifo interrupt state. I
+ * assume that bit clear means interrupt active. As it turns
+ * out, the driver really doesn't need to check for this after
+ * all, so my remarks above about a 'problem' can safely be
+ * ignored. The way the logic is set up, there's no advantage
+ * (that I can see) to worrying about it.
+ *
+ * It seems that the fifo interrupt signal is negated when we extract
+ * bytes during read or write bytes during write.
+ * - fifo will interrupt when data is moving from it to the 3393, and
+ * there are 31 (or less?) bytes left to go. This is sort of short-
+ * sighted: what if you don't WANT to do more? In any case, our
+ * response is to push more into the fifo - either actual data or
+ * dummy bytes if need be. Note that we apparently have to write at
+ * least 32 additional bytes to the fifo after an interrupt in order
+ * to get it to release the ones it was holding on to - writing fewer
+ * than 32 will result in another fifo int.
+ * UPDATE: Again, info from Bill Earnest makes this more understandable:
+ * 32 bytes = two counts of the fifo counter register. He tells
+ * me that the fifo interrupt is a non-latching signal derived
+ * from a straightforward boolean interpretation of the 7
+ * highest bits of the fifo counter and the fifo-read/fifo-write
+ * state. Who'd a thought?
+ */
+
+ write1_io(0, IO_LED_ON);
+ asr = READ_AUX_STAT();
+ if (!(asr & ASR_INT)) { /* no WD33c93 interrupt? */
+
+/* Ok. This is definitely a FIFO-only interrupt.
+ *
+ * If FI_FIFO_READING is set, there are up to 2048 bytes waiting to be read,
+ * maybe more to come from the SCSI bus. Read as many as we can out of the
+ * fifo and into memory at the location of SCp.ptr[SCp.have_data_in], and
+ * update have_data_in afterwards.
+ *
+ * If we have FI_FIFO_WRITING, the FIFO has almost run out of bytes to move
+ * into the WD3393 chip (I think the interrupt happens when there are 31
+ * bytes left, but it may be fewer...). The 3393 is still waiting, so we
+ * shove some more into the fifo, which gets things moving again. If the
+ * original SCSI command specified more than 2048 bytes, there may still
+ * be some of that data left: fine - use it (from SCp.ptr[SCp.have_data_in]).
+ * Don't forget to update have_data_in. If we've already written out the
+ * entire buffer, feed 32 dummy bytes to the fifo - they're needed to
+ * push out the remaining real data.
+ * (Big thanks to Bill Earnest for getting me out of the mud in here.)
+ */
+
+ cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */
+ CHECK_NULL(cmd, "fifo_int")
+
+ if (hostdata->fifo == FI_FIFO_READING) {
+
+ DB(DB_FIFO, printk("{R:%02x} ", read1_io(IO_FIFO_COUNT)))
+
+ sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i = read1_io(IO_FIFO_COUNT) & 0xfe;
+ i <<= 2; /* # of words waiting in the fifo */
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_READ_IO
+
+ FAST_READ2_IO();
+#else
+ while (i--)
+ *sp++ = read2_io(IO_FIFO);
+
+#endif
+
+ i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i <<= 1;
+ cmd->SCp.have_data_in += i;
+ }
+
+ else if (hostdata->fifo == FI_FIFO_WRITING) {
+
+ DB(DB_FIFO, printk("{W:%02x} ", read1_io(IO_FIFO_COUNT)))
+
+/* If all bytes have been written to the fifo, flush out the stragglers.
+ * Note that while writing 16 dummy words seems arbitrary, we don't
+ * have another choice that I can see. What we really want is to read
+ * the 3393 transfer count register (that would tell us how many bytes
+ * needed flushing), but the TRANSFER_INFO command hasn't completed
+ * yet (not enough bytes!) and that register won't be accessible. So,
+ * we use 16 words - a number obtained through trial and error.
+ * UPDATE: Bill says this is exactly what Always does, so there.
+ * More thanks due him for help in this section.
+ */
+ if (cmd->SCp.this_residual == cmd->SCp.have_data_in) {
+ i = 16;
+ while (i--) /* write 32 dummy bytes */
+ write2_io(0, IO_FIFO);
+ }
+
+/* If there are still bytes left in the SCSI buffer, write as many as we
+ * can out to the fifo.
+ */
+
+ else {
+ sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i = cmd->SCp.this_residual - cmd->SCp.have_data_in; /* bytes yet to go */
+ j = read1_io(IO_FIFO_COUNT) & 0xfe;
+ j <<= 2; /* how many words the fifo has room for */
+ if ((j << 1) > i)
+ j = (i >> 1);
+ while (j--)
+ write2_io(*sp++, IO_FIFO);
+
+ i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i <<= 1;
+ cmd->SCp.have_data_in += i;
+ }
+ }
+
+ else {
+ printk("*** Spurious FIFO interrupt ***");
+ }
+
+ write1_io(0, IO_LED_OFF);
+
+/* release the SMP spin_lock and restore irq state */
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+/* This interrupt was triggered by the WD33c93 chip. The fifo interrupt
+ * may also be asserted, but we don't bother to check it: we get more
+ * detailed info from FIFO_READING and FIFO_WRITING (see below).
+ */
+
+ cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */
+ sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear the interrupt */
+ phs = read_3393(hostdata, WD_COMMAND_PHASE);
+
+ if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) {
+ printk("\nNR:wd-intr-1\n");
+ write1_io(0, IO_LED_OFF);
+
+/* release the SMP spin_lock and restore irq state */
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ DB(DB_INTR, printk("{%02x:%02x-", asr, sr))
+
+/* After starting a FIFO-based transfer, the next _WD3393_ interrupt is
+ * guaranteed to be in response to the completion of the transfer.
+ * If we were reading, there's probably data in the fifo that needs
+ * to be copied into RAM - do that here. Also, we have to update
+ * 'this_residual' and 'ptr' based on the contents of the
+ * TRANSFER_COUNT register, in case the device decided to do an
+ * intermediate disconnect (a device may do this if it has to
+ * do a seek, or just to be nice and let other devices have
+ * some bus time during long transfers).
+ * After doing whatever is necessary with the fifo, we go on and
+ * service the WD3393 interrupt normally.
+ */
+ if (hostdata->fifo == FI_FIFO_READING) {
+
+/* buffer index = start-of-buffer + #-of-bytes-already-read */
+
+ sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in);
+
+/* bytes remaining in fifo = (total-wanted - #-not-got) - #-already-read */
+
+ i = (cmd->SCp.this_residual - read_3393_count(hostdata)) - cmd->SCp.have_data_in;
+ i >>= 1; /* Gulp. We assume this will always be modulo 2 */
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_READ_IO
+
+ FAST_READ2_IO();
+#else
+ while (i--)
+ *sp++ = read2_io(IO_FIFO);
+
+#endif
+
+ hostdata->fifo = FI_FIFO_UNUSED;
+ length = cmd->SCp.this_residual;
+ cmd->SCp.this_residual = read_3393_count(hostdata);
+ cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+
+ DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual))
+
+ }
+
+ else if (hostdata->fifo == FI_FIFO_WRITING) {
+ hostdata->fifo = FI_FIFO_UNUSED;
+ length = cmd->SCp.this_residual;
+ cmd->SCp.this_residual = read_3393_count(hostdata);
+ cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+
+ DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual))
+
+ }
+
+/* Respond to the specific WD3393 interrupt - there are quite a few! */
+
+ switch (sr) {
+
+ case CSR_TIMEOUT:
+ DB(DB_INTR, printk("TIMEOUT"))
+
+ if (hostdata->state == S_RUNNING_LEVEL2)
+ hostdata->connected = NULL;
+ else {
+ cmd = (Scsi_Cmnd *) hostdata->selecting; /* get a valid cmd */
+ CHECK_NULL(cmd, "csr_timeout")
+ hostdata->selecting = NULL;
+ }
+
+ cmd->result = DID_NO_CONNECT << 16;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ cmd->scsi_done(cmd);
+
+/* We are not connected to a target - check to see if there
+ * are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ break;
+
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+ case CSR_SELECT:
+ DB(DB_INTR, printk("SELECT"))
+ hostdata->connected = cmd = (Scsi_Cmnd *) hostdata->selecting;
+ CHECK_NULL(cmd, "csr_select")
+ hostdata->selecting = NULL;
+
+ /* construct an IDENTIFY message with correct disconnect bit */
+
+ hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun);
+ if (cmd->SCp.phase)
+ hostdata->outgoing_msg[0] |= 0x40;
+
+ if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) {
+#ifdef SYNC_DEBUG
+ printk(" sending SDTR ");
+#endif
+
+ hostdata->sync_stat[cmd->device->id] = SS_WAITING;
+
+ /* tack on a 2nd message to ask about synchronous transfers */
+
+ hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[2] = 3;
+ hostdata->outgoing_msg[3] = EXTENDED_SDTR;
+ hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4;
+ hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF;
+ hostdata->outgoing_len = 6;
+ } else
+ hostdata->outgoing_len = 1;
+
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_XFER_DONE | PHS_DATA_IN:
+ case CSR_UNEXP | PHS_DATA_IN:
+ case CSR_SRV_REQ | PHS_DATA_IN:
+ DB(DB_INTR, printk("IN-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual))
+ transfer_bytes(cmd, DATA_IN_DIR);
+ if (hostdata->state != S_RUNNING_LEVEL2)
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_XFER_DONE | PHS_DATA_OUT:
+ case CSR_UNEXP | PHS_DATA_OUT:
+ case CSR_SRV_REQ | PHS_DATA_OUT:
+ DB(DB_INTR, printk("OUT-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual))
+ transfer_bytes(cmd, DATA_OUT_DIR);
+ if (hostdata->state != S_RUNNING_LEVEL2)
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+ case CSR_XFER_DONE | PHS_COMMAND:
+ case CSR_UNEXP | PHS_COMMAND:
+ case CSR_SRV_REQ | PHS_COMMAND:
+ DB(DB_INTR, printk("CMND-%02x,%ld", cmd->cmnd[0], cmd->pid))
+ transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_XFER_DONE | PHS_STATUS:
+ case CSR_UNEXP | PHS_STATUS:
+ case CSR_SRV_REQ | PHS_STATUS:
+ DB(DB_INTR, printk("STATUS="))
+
+ cmd->SCp.Status = read_1_byte(hostdata);
+ DB(DB_INTR, printk("%02x", cmd->SCp.Status))
+ if (hostdata->level2 >= L2_BASIC) {
+ sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_3393(hostdata, WD_COMMAND_PHASE, 0x50);
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ } else {
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+
+ case CSR_XFER_DONE | PHS_MESS_IN:
+ case CSR_UNEXP | PHS_MESS_IN:
+ case CSR_SRV_REQ | PHS_MESS_IN:
+ DB(DB_INTR, printk("MSG_IN="))
+
+ msg = read_1_byte(hostdata);
+ sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */
+
+ hostdata->incoming_msg[hostdata->incoming_ptr] = msg;
+ if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE)
+ msg = EXTENDED_MESSAGE;
+ else
+ hostdata->incoming_ptr = 0;
+
+ cmd->SCp.Message = msg;
+ switch (msg) {
+
+ case COMMAND_COMPLETE:
+ DB(DB_INTR, printk("CCMP-%ld", cmd->pid))
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_PRE_CMP_DISC;
+ break;
+
+ case SAVE_POINTERS:
+ DB(DB_INTR, printk("SDP"))
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+
+ case RESTORE_POINTERS:
+ DB(DB_INTR, printk("RDP"))
+ if (hostdata->level2 >= L2_BASIC) {
+ write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else {
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+ case DISCONNECT:
+ DB(DB_INTR, printk("DIS"))
+ cmd->device->disconnect = 1;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_PRE_TMP_DISC;
+ break;
+
+ case MESSAGE_REJECT:
+ DB(DB_INTR, printk("REJ"))
+#ifdef SYNC_DEBUG
+ printk("-REJ-");
+#endif
+ if (hostdata->sync_stat[cmd->device->id] == SS_WAITING)
+ hostdata->sync_stat[cmd->device->id] = SS_SET;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+
+ case EXTENDED_MESSAGE:
+ DB(DB_INTR, printk("EXT"))
+
+ ucp = hostdata->incoming_msg;
+
+#ifdef SYNC_DEBUG
+ printk("%02x", ucp[hostdata->incoming_ptr]);
+#endif
+ /* Is this the last byte of the extended message? */
+
+ if ((hostdata->incoming_ptr >= 2) && (hostdata->incoming_ptr == (ucp[1] + 1))) {
+
+ switch (ucp[2]) { /* what's the EXTENDED code? */
+ case EXTENDED_SDTR:
+ id = calc_sync_xfer(ucp[3], ucp[4]);
+ if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) {
+
+/* A device has sent an unsolicited SDTR message; rather than go
+ * through the effort of decoding it and then figuring out what
+ * our reply should be, we're just gonna say that we have a
+ * synchronous fifo depth of 0. This will result in asynchronous
+ * transfers - not ideal but so much easier.
+ * Actually, this is OK because it assures us that if we don't
+ * specifically ask for sync transfers, we won't do any.
+ */
+
+ write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 3;
+ hostdata->outgoing_msg[2] = EXTENDED_SDTR;
+ hostdata->outgoing_msg[3] = hostdata->default_sx_per / 4;
+ hostdata->outgoing_msg[4] = 0;
+ hostdata->outgoing_len = 5;
+ hostdata->sync_xfer[cmd->device->id] = calc_sync_xfer(hostdata->default_sx_per / 4, 0);
+ } else {
+ hostdata->sync_xfer[cmd->device->id] = id;
+ }
+#ifdef SYNC_DEBUG
+ printk("sync_xfer=%02x", hostdata->sync_xfer[cmd->device->id]);
+#endif
+ hostdata->sync_stat[cmd->device->id] = SS_SET;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ case EXTENDED_WDTR:
+ write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("sending WDTR ");
+ hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 2;
+ hostdata->outgoing_msg[2] = EXTENDED_WDTR;
+ hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */
+ hostdata->outgoing_len = 4;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ default:
+ write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("Rejecting Unknown Extended Message(%02x). ", ucp[2]);
+ hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+ hostdata->outgoing_len = 1;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ }
+ hostdata->incoming_ptr = 0;
+ }
+
+ /* We need to read more MESS_IN bytes for the extended message */
+
+ else {
+ hostdata->incoming_ptr++;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+ default:
+ printk("Rejecting Unknown Message(%02x) ", msg);
+ write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+ hostdata->outgoing_len = 1;
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+ case CSR_SEL_XFER_DONE:
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
+ if (phs == 0x60) {
+ DB(DB_INTR, printk("SX-DONE-%ld", cmd->pid))
+ cmd->SCp.Message = COMMAND_COMPLETE;
+ lun = read_3393(hostdata, WD_TARGET_LUN);
+ DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+ cmd->SCp.Status = lun;
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ } else {
+ printk("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs, cmd->pid);
+ }
+ break;
+
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+ case CSR_SDP:
+ DB(DB_INTR, printk("SDP"))
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_3393(hostdata, WD_COMMAND_PHASE, 0x41);
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ break;
+
+
+ case CSR_XFER_DONE | PHS_MESS_OUT:
+ case CSR_UNEXP | PHS_MESS_OUT:
+ case CSR_SRV_REQ | PHS_MESS_OUT:
+ DB(DB_INTR, printk("MSG_OUT="))
+
+/* To get here, we've probably requested MESSAGE_OUT and have
+ * already put the correct bytes in outgoing_msg[] and filled
+ * in outgoing_len. We simply send them out to the SCSI bus.
+ * Sometimes we get MESSAGE_OUT phase when we're not expecting
+ * it - like when our SDTR message is rejected by a target. Some
+ * targets send the REJECT before receiving all of the extended
+ * message, and then seem to go back to MESSAGE_OUT for a byte
+ * or two. Not sure why, or if I'm doing something wrong to
+ * cause this to happen. Regardless, it seems that sending
+ * NOP messages in these situations results in no harm and
+ * makes everyone happy.
+ */
+ if (hostdata->outgoing_len == 0) {
+ hostdata->outgoing_len = 1;
+ hostdata->outgoing_msg[0] = NOP;
+ }
+ transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len, DATA_OUT_DIR, hostdata);
+ DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0]))
+ hostdata->outgoing_len = 0;
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_UNEXP_DISC:
+
+/* I think I've seen this after a request-sense that was in response
+ * to an error condition, but not sure. We certainly need to do
+ * something when we get this interrupt - the question is 'what?'.
+ * Let's think positively, and assume some command has finished
+ * in a legal manner (like a command that provokes a request-sense),
+ * so we treat it as a normal command-complete-disconnect.
+ */
+
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+
+/* release the SMP spin_lock and restore irq state */
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ return IRQ_HANDLED;
+ }
+ DB(DB_INTR, printk("UNEXP_DISC-%ld", cmd->pid))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ break;
+
+
+ case CSR_DISC:
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
+ DB(DB_INTR, printk("DISC-%ld", cmd->pid))
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+ }
+ switch (hostdata->state) {
+ case S_PRE_CMP_DISC:
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ DB(DB_INTR, printk(":%d", cmd->SCp.Status))
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+ break;
+ case S_PRE_TMP_DISC:
+ case S_RUNNING_LEVEL2:
+ cmd->host_scribble = (uchar *) hostdata->disconnected_Q;
+ hostdata->disconnected_Q = cmd;
+ hostdata->connected = NULL;
+ hostdata->state = S_UNCONNECTED;
+
+#ifdef PROC_STATISTICS
+ hostdata->disc_done_cnt[cmd->device->id]++;
+#endif
+
+ break;
+ default:
+ printk("*** Unexpected DISCONNECT interrupt! ***");
+ hostdata->state = S_UNCONNECTED;
+ }
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ break;
+
+
+ case CSR_RESEL_AM:
+ DB(DB_INTR, printk("RESEL"))
+
+ /* First we have to make sure this reselection didn't */
+ /* happen during Arbitration/Selection of some other device. */
+ /* If yes, put losing command back on top of input_Q. */
+ if (hostdata->level2 <= L2_NONE) {
+
+ if (hostdata->selecting) {
+ cmd = (Scsi_Cmnd *) hostdata->selecting;
+ hostdata->selecting = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ cmd->host_scribble = (uchar *) hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ }
+ }
+
+ else {
+
+ if (cmd) {
+ if (phs == 0x00) {
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ cmd->host_scribble = (uchar *) hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ } else {
+ printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---", asr, sr, phs);
+ while (1)
+ printk("\r");
+ }
+ }
+
+ }
+
+ /* OK - find out which device reselected us. */
+
+ id = read_3393(hostdata, WD_SOURCE_ID);
+ id &= SRCID_MASK;
+
+ /* and extract the lun from the ID message. (Note that we don't
+ * bother to check for a valid message here - I guess this is
+ * not the right way to go, but....)
+ */
+
+ lun = read_3393(hostdata, WD_DATA);
+ if (hostdata->level2 < L2_RESELECT)
+ write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
+ lun &= 7;
+
+ /* Now we look for the command that's reconnecting. */
+
+ cmd = (Scsi_Cmnd *) hostdata->disconnected_Q;
+ patch = NULL;
+ while (cmd) {
+ if (id == cmd->device->id && lun == cmd->device->lun)
+ break;
+ patch = cmd;
+ cmd = (Scsi_Cmnd *) cmd->host_scribble;
+ }
+
+ /* Hmm. Couldn't find a valid command.... What to do? */
+
+ if (!cmd) {
+ printk("---TROUBLE: target %d.%d not in disconnect queue---", id, lun);
+ break;
+ }
+
+ /* Ok, found the command - now start it up again. */
+
+ if (patch)
+ patch->host_scribble = cmd->host_scribble;
+ else
+ hostdata->disconnected_Q = (Scsi_Cmnd *) cmd->host_scribble;
+ hostdata->connected = cmd;
+
+ /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'
+ * because these things are preserved over a disconnect.
+ * But we DO need to fix the DPD bit so it's correct for this command.
+ */
+
+ if (is_dir_out(cmd))
+ write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id);
+ else
+ write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
+ if (hostdata->level2 >= L2_RESELECT) {
+ write_3393_count(hostdata, 0); /* we want a DATA_PHASE interrupt */
+ write_3393(hostdata, WD_COMMAND_PHASE, 0x45);
+ write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else
+ hostdata->state = S_CONNECTED;
+
+ DB(DB_INTR, printk("-%ld", cmd->pid))
+ break;
+
+ default:
+ printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs);
+ }
+
+ write1_io(0, IO_LED_OFF);
+
+ DB(DB_INTR, printk("} "))
+
+/* release the SMP spin_lock and restore irq state */
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+
+
+#define RESET_CARD 0
+#define RESET_CARD_AND_BUS 1
+#define B_FLAG 0x80
+
+/*
+ * Caller must hold instance lock!
+ */
+
+static int reset_hardware(struct Scsi_Host *instance, int type)
+{
+ struct IN2000_hostdata *hostdata;
+ int qt, x;
+
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+
+ write1_io(0, IO_LED_ON);
+ if (type == RESET_CARD_AND_BUS) {
+ write1_io(0, IO_CARD_RESET);
+ x = read1_io(IO_HARDWARE);
+ }
+ x = read_3393(hostdata, WD_SCSI_STATUS); /* clear any WD intrpt */
+ write_3393(hostdata, WD_OWN_ID, instance->this_id | OWNID_EAF | OWNID_RAF | OWNID_FS_8);
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, calc_sync_xfer(hostdata->default_sx_per / 4, DEFAULT_SX_OFF));
+
+ write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */
+ write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */
+ write_3393(hostdata, WD_COMMAND, WD_CMD_RESET);
+ /* FIXME: timeout ?? */
+ while (!(READ_AUX_STAT() & ASR_INT))
+ cpu_relax(); /* wait for RESET to complete */
+
+ x = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */
+
+ write_3393(hostdata, WD_QUEUE_TAG, 0xa5); /* any random number */
+ qt = read_3393(hostdata, WD_QUEUE_TAG);
+ if (qt == 0xa5) {
+ x |= B_FLAG;
+ write_3393(hostdata, WD_QUEUE_TAG, 0);
+ }
+ write_3393(hostdata, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write1_io(0, IO_LED_OFF);
+ return x;
+}
+
+
+
+static int in2000_bus_reset(Scsi_Cmnd * cmd)
+{
+ struct Scsi_Host *instance;
+ struct IN2000_hostdata *hostdata;
+ int x;
+
+ instance = cmd->device->host;
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+
+ printk(KERN_WARNING "scsi%d: Reset. ", instance->host_no);
+
+ /* do scsi-reset here */
+
+ reset_hardware(instance, RESET_CARD_AND_BUS);
+ for (x = 0; x < 8; x++) {
+ hostdata->busy[x] = 0;
+ hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
+ hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->fifo = FI_FIFO_UNUSED;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+
+ cmd->result = DID_RESET << 16;
+ return SUCCESS;
+}
+
+static int in2000_host_reset(Scsi_Cmnd * cmd)
+{
+ return FAILED;
+}
+
+static int in2000_device_reset(Scsi_Cmnd * cmd)
+{
+ return FAILED;
+}
+
+
+static int in2000_abort(Scsi_Cmnd * cmd)
+{
+ struct Scsi_Host *instance;
+ struct IN2000_hostdata *hostdata;
+ Scsi_Cmnd *tmp, *prev;
+ uchar sr, asr;
+ unsigned long timeout;
+
+ instance = cmd->device->host;
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+
+ printk(KERN_DEBUG "scsi%d: Abort-", instance->host_no);
+ printk("(asr=%02x,count=%ld,resid=%d,buf_resid=%d,have_data=%d,FC=%02x)- ", READ_AUX_STAT(), read_3393_count(hostdata), cmd->SCp.this_residual, cmd->SCp.buffers_residual, cmd->SCp.have_data_in, read1_io(IO_FIFO_COUNT));
+
+/*
+ * Case 1 : If the command hasn't been issued yet, we simply remove it
+ * from the inout_Q.
+ */
+
+ tmp = (Scsi_Cmnd *) hostdata->input_Q;
+ prev = NULL;
+ while (tmp) {
+ if (tmp == cmd) {
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ cmd->result = DID_ABORT << 16;
+ printk(KERN_WARNING "scsi%d: Abort - removing command %ld from input_Q. ", instance->host_no, cmd->pid);
+ cmd->scsi_done(cmd);
+ return SUCCESS;
+ }
+ prev = tmp;
+ tmp = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+
+/*
+ * Case 2 : If the command is connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected == cmd) {
+
+ printk(KERN_WARNING "scsi%d: Aborting connected command %ld - ", instance->host_no, cmd->pid);
+
+ printk("sending wd33c93 ABORT command - ");
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393_cmd(hostdata, WD_CMD_ABORT);
+
+/* Now we have to attempt to flush out the FIFO... */
+
+ printk("flushing fifo - ");
+ timeout = 1000000;
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ read_3393(hostdata, WD_DATA);
+ } while (!(asr & ASR_INT) && timeout-- > 0);
+ sr = read_3393(hostdata, WD_SCSI_STATUS);
+ printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ", asr, sr, read_3393_count(hostdata), timeout);
+
+ /*
+ * Abort command processed.
+ * Still connected.
+ * We must disconnect.
+ */
+
+ printk("sending wd33c93 DISCONNECT command - ");
+ write_3393_cmd(hostdata, WD_CMD_DISCONNECT);
+
+ timeout = 1000000;
+ asr = READ_AUX_STAT();
+ while ((asr & ASR_CIP) && timeout-- > 0)
+ asr = READ_AUX_STAT();
+ sr = read_3393(hostdata, WD_SCSI_STATUS);
+ printk("asr=%02x, sr=%02x.", asr, sr);
+
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->connected = NULL;
+ hostdata->state = S_UNCONNECTED;
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+
+ in2000_execute(instance);
+
+ return SUCCESS;
+ }
+
+/*
+ * Case 3: If the command is currently disconnected from the bus,
+ * we're not going to expend much effort here: Let's just return
+ * an ABORT_SNOOZE and hope for the best...
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_Q; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ printk(KERN_DEBUG "scsi%d: unable to abort disconnected command.\n", instance->host_no);
+ return FAILED;
+ }
+
+/*
+ * Case 4 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ in2000_execute(instance);
+
+ printk("scsi%d: warning : SCSI command probably completed successfully" " before abortion. ", instance->host_no);
+ return SUCCESS;
+}
+
+
+
+#define MAX_IN2000_HOSTS 3
+#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
+
+static void __init in2000_setup(char *str, int *ints)
+{
+ int i;
+ char *p1, *p2;
+
+ strlcpy(setup_buffer, str, SETUP_BUFFER_SIZE);
+ p1 = setup_buffer;
+ i = 0;
+ while (*p1 && (i < MAX_SETUP_ARGS)) {
+ p2 = strchr(p1, ',');
+ if (p2) {
+ *p2 = '\0';
+ if (p1 != p2)
+ setup_args[i] = p1;
+ p1 = p2 + 1;
+ i++;
+ } else {
+ setup_args[i] = p1;
+ break;
+ }
+ }
+ for (i = 0; i < MAX_SETUP_ARGS; i++)
+ setup_used[i] = 0;
+ done_setup = 1;
+}
+
+
+/* check_setup_args() returns index if key found, 0 if not
+ */
+
+static int __init check_setup_args(char *key, int *val, char *buf)
+{
+ int x;
+ char *cp;
+
+ for (x = 0; x < MAX_SETUP_ARGS; x++) {
+ if (setup_used[x])
+ continue;
+ if (!strncmp(setup_args[x], key, strlen(key)))
+ break;
+ }
+ if (x == MAX_SETUP_ARGS)
+ return 0;
+ setup_used[x] = 1;
+ cp = setup_args[x] + strlen(key);
+ *val = -1;
+ if (*cp != ':')
+ return ++x;
+ cp++;
+ if ((*cp >= '0') && (*cp <= '9')) {
+ *val = simple_strtoul(cp, NULL, 0);
+ }
+ return ++x;
+}
+
+
+
+/* The "correct" (ie portable) way to access memory-mapped hardware
+ * such as the IN2000 EPROM and dip switch is through the use of
+ * special macros declared in 'asm/io.h'. We use readb() and readl()
+ * when reading from the card's BIOS area in in2000_detect().
+ */
+static u32 bios_tab[] in2000__INITDATA = {
+ 0xc8000,
+ 0xd0000,
+ 0xd8000,
+ 0
+};
+
+static unsigned short base_tab[] in2000__INITDATA = {
+ 0x220,
+ 0x200,
+ 0x110,
+ 0x100,
+};
+
+static int int_tab[] in2000__INITDATA = {
+ 15,
+ 14,
+ 11,
+ 10
+};
+
+
+static int __init in2000_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *instance;
+ struct IN2000_hostdata *hostdata;
+ int detect_count;
+ int bios;
+ int x;
+ unsigned short base;
+ uchar switches;
+ uchar hrev;
+ unsigned long flags;
+ int val;
+ char buf[32];
+
+/* Thanks to help from Bill Earnest, probing for IN2000 cards is a
+ * pretty straightforward and fool-proof operation. There are 3
+ * possible locations for the IN2000 EPROM in memory space - if we
+ * find a BIOS signature, we can read the dip switch settings from
+ * the byte at BIOS+32 (shadowed in by logic on the card). From 2
+ * of the switch bits we get the card's address in IO space. There's
+ * an image of the dip switch there, also, so we have a way to back-
+ * check that this really is an IN2000 card. Very nifty. Use the
+ * 'ioport:xx' command-line parameter if your BIOS EPROM is absent
+ * or disabled.
+ */
+
+ if (!done_setup && setup_strings)
+ in2000_setup(setup_strings, NULL);
+
+ detect_count = 0;
+ for (bios = 0; bios_tab[bios]; bios++) {
+ if (check_setup_args("ioport", &val, buf)) {
+ base = val;
+ switches = ~inb(base + IO_SWITCHES) & 0xff;
+ printk("Forcing IN2000 detection at IOport 0x%x ", base);
+ bios = 2;
+ }
+/*
+ * There have been a couple of BIOS versions with different layouts
+ * for the obvious ID strings. We look for the 2 most common ones and
+ * hope that they cover all the cases...
+ */
+ else if (isa_readl(bios_tab[bios] + 0x10) == 0x41564f4e || isa_readl(bios_tab[bios] + 0x30) == 0x61776c41) {
+ printk("Found IN2000 BIOS at 0x%x ", (unsigned int) bios_tab[bios]);
+
+/* Read the switch image that's mapped into EPROM space */
+
+ switches = ~((isa_readb(bios_tab[bios] + 0x20) & 0xff));
+
+/* Find out where the IO space is */
+
+ x = switches & (SW_ADDR0 | SW_ADDR1);
+ base = base_tab[x];
+
+/* Check for the IN2000 signature in IO space. */
+
+ x = ~inb(base + IO_SWITCHES) & 0xff;
+ if (x != switches) {
+ printk("Bad IO signature: %02x vs %02x.\n", x, switches);
+ continue;
+ }
+ } else
+ continue;
+
+/* OK. We have a base address for the IO ports - run a few safety checks */
+
+ if (!(switches & SW_BIT7)) { /* I _think_ all cards do this */
+ printk("There is no IN-2000 SCSI card at IOport 0x%03x!\n", base);
+ continue;
+ }
+
+/* Let's assume any hardware version will work, although the driver
+ * has only been tested on 0x21, 0x22, 0x25, 0x26, and 0x27. We'll
+ * print out the rev number for reference later, but accept them all.
+ */
+
+ hrev = inb(base + IO_HARDWARE);
+
+ /* Bit 2 tells us if interrupts are disabled */
+ if (switches & SW_DISINT) {
+ printk("The IN-2000 SCSI card at IOport 0x%03x ", base);
+ printk("is not configured for interrupt operation!\n");
+ printk("This driver requires an interrupt: cancelling detection.\n");
+ continue;
+ }
+
+/* Ok. We accept that there's an IN2000 at ioaddr 'base'. Now
+ * initialize it.
+ */
+
+ tpnt->proc_name = "in2000";
+ instance = scsi_register(tpnt, sizeof(struct IN2000_hostdata));
+ if (instance == NULL)
+ continue;
+ detect_count++;
+ hostdata = (struct IN2000_hostdata *) instance->hostdata;
+ instance->io_port = hostdata->io_base = base;
+ hostdata->dip_switch = switches;
+ hostdata->hrev = hrev;
+
+ write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */
+ write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */
+ write1_io(0, IO_INTR_MASK); /* allow all ints */
+ x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT];
+ if (request_irq(x, in2000_intr, SA_INTERRUPT, "in2000", instance)) {
+ printk("in2000_detect: Unable to allocate IRQ.\n");
+ detect_count--;
+ continue;
+ }
+ instance->irq = x;
+ instance->n_io_port = 13;
+ request_region(base, 13, "in2000"); /* lock in this IO space for our use */
+
+ for (x = 0; x < 8; x++) {
+ hostdata->busy[x] = 0;
+ hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
+ hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[x] = 0;
+ hostdata->disc_allowed_cnt[x] = 0;
+ hostdata->disc_done_cnt[x] = 0;
+#endif
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->fifo = FI_FIFO_UNUSED;
+ hostdata->level2 = L2_BASIC;
+ hostdata->disconnect = DIS_ADAPTIVE;
+ hostdata->args = DEBUG_DEFAULTS;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+ hostdata->default_sx_per = DEFAULT_SX_PER;
+
+/* Older BIOS's had a 'sync on/off' switch - use its setting */
+
+ if (isa_readl(bios_tab[bios] + 0x10) == 0x41564f4e && (switches & SW_SYNC_DOS5))
+ hostdata->sync_off = 0x00; /* sync defaults to on */
+ else
+ hostdata->sync_off = 0xff; /* sync defaults to off */
+
+#ifdef PROC_INTERFACE
+ hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP;
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt = 0;
+#endif
+#endif
+
+ if (check_setup_args("nosync", &val, buf))
+ hostdata->sync_off = val;
+
+ if (check_setup_args("period", &val, buf))
+ hostdata->default_sx_per = sx_table[round_period((unsigned int) val)].period_ns;
+
+ if (check_setup_args("disconnect", &val, buf)) {
+ if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
+ hostdata->disconnect = val;
+ else
+ hostdata->disconnect = DIS_ADAPTIVE;
+ }
+
+ if (check_setup_args("noreset", &val, buf))
+ hostdata->args ^= A_NO_SCSI_RESET;
+
+ if (check_setup_args("level2", &val, buf))
+ hostdata->level2 = val;
+
+ if (check_setup_args("debug", &val, buf))
+ hostdata->args = (val & DB_MASK);
+
+#ifdef PROC_INTERFACE
+ if (check_setup_args("proc", &val, buf))
+ hostdata->proc = val;
+#endif
+
+
+ /* FIXME: not strictly needed I think but the called code expects
+ to be locked */
+ spin_lock_irqsave(instance->host_lock, flags);
+ x = reset_hardware(instance, (hostdata->args & A_NO_SCSI_RESET) ? RESET_CARD : RESET_CARD_AND_BUS);
+ spin_unlock_irqrestore(instance->host_lock, flags);
+
+ hostdata->microcode = read_3393(hostdata, WD_CDB_1);
+ if (x & 0x01) {
+ if (x & B_FLAG)
+ hostdata->chip = C_WD33C93B;
+ else
+ hostdata->chip = C_WD33C93A;
+ } else
+ hostdata->chip = C_WD33C93;
+
+ printk("dip_switch=%02x irq=%d ioport=%02x floppy=%s sync/DOS5=%s ", (switches & 0x7f), instance->irq, hostdata->io_base, (switches & SW_FLOPPY) ? "Yes" : "No", (switches & SW_SYNC_DOS5) ? "Yes" : "No");
+ printk("hardware_ver=%02x chip=%s microcode=%02x\n", hrev, (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip == C_WD33C93A) ? "WD33c93A" : (hostdata->chip == C_WD33C93B) ? "WD33c93B" : "unknown", hostdata->microcode);
+#ifdef DEBUGGING_ON
+ printk("setup_args = ");
+ for (x = 0; x < MAX_SETUP_ARGS; x++)
+ printk("%s,", setup_args[x]);
+ printk("\n");
+#endif
+ if (hostdata->sync_off == 0xff)
+ printk("Sync-transfer DISABLED on all devices: ENABLE from command-line\n");
+ printk("IN2000 driver version %s - %s\n", IN2000_VERSION, IN2000_DATE);
+ }
+
+ return detect_count;
+}
+
+static int in2000_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ return 0;
+}
+
+/* NOTE: I lifted this function straight out of the old driver,
+ * and have not tested it. Presumably it does what it's
+ * supposed to do...
+ */
+
+static int in2000_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *iinfo)
+{
+ int size;
+
+ size = capacity;
+ iinfo[0] = 64;
+ iinfo[1] = 32;
+ iinfo[2] = size >> 11;
+
+/* This should approximate the large drive handling that the DOS ASPI manager
+ uses. Drives very near the boundaries may not be handled correctly (i.e.
+ near 2.0 Gb and 4.0 Gb) */
+
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 64;
+ iinfo[1] = 63;
+ iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 128;
+ iinfo[1] = 63;
+ iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 255;
+ iinfo[1] = 63;
+ iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]);
+ }
+ return 0;
+}
+
+
+static int in2000_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off, int len, int in)
+{
+
+#ifdef PROC_INTERFACE
+
+ char *bp;
+ char tbuf[128];
+ unsigned long flags;
+ struct IN2000_hostdata *hd;
+ Scsi_Cmnd *cmd;
+ int x, i;
+ static int stop = 0;
+
+ hd = (struct IN2000_hostdata *) instance->hostdata;
+
+/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
+ * keywords (same format as command-line, but only ONE per read):
+ * debug
+ * disconnect
+ * period
+ * resync
+ * proc
+ */
+
+ if (in) {
+ buf[len] = '\0';
+ bp = buf;
+ if (!strncmp(bp, "debug:", 6)) {
+ bp += 6;
+ hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK;
+ } else if (!strncmp(bp, "disconnect:", 11)) {
+ bp += 11;
+ x = simple_strtoul(bp, NULL, 0);
+ if (x < DIS_NEVER || x > DIS_ALWAYS)
+ x = DIS_ADAPTIVE;
+ hd->disconnect = x;
+ } else if (!strncmp(bp, "period:", 7)) {
+ bp += 7;
+ x = simple_strtoul(bp, NULL, 0);
+ hd->default_sx_per = sx_table[round_period((unsigned int) x)].period_ns;
+ } else if (!strncmp(bp, "resync:", 7)) {
+ bp += 7;
+ x = simple_strtoul(bp, NULL, 0);
+ for (i = 0; i < 7; i++)
+ if (x & (1 << i))
+ hd->sync_stat[i] = SS_UNSET;
+ } else if (!strncmp(bp, "proc:", 5)) {
+ bp += 5;
+ hd->proc = simple_strtoul(bp, NULL, 0);
+ } else if (!strncmp(bp, "level2:", 7)) {
+ bp += 7;
+ hd->level2 = simple_strtoul(bp, NULL, 0);
+ }
+ return len;
+ }
+
+ spin_lock_irqsave(instance->host_lock, flags);
+ bp = buf;
+ *bp = '\0';
+ if (hd->proc & PR_VERSION) {
+ sprintf(tbuf, "\nVersion %s - %s. Compiled %s %s", IN2000_VERSION, IN2000_DATE, __DATE__, __TIME__);
+ strcat(bp, tbuf);
+ }
+ if (hd->proc & PR_INFO) {
+ sprintf(tbuf, "\ndip_switch=%02x: irq=%d io=%02x floppy=%s sync/DOS5=%s", (hd->dip_switch & 0x7f), instance->irq, hd->io_base, (hd->dip_switch & 0x40) ? "Yes" : "No", (hd->dip_switch & 0x20) ? "Yes" : "No");
+ strcat(bp, tbuf);
+ strcat(bp, "\nsync_xfer[] = ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%02x", hd->sync_xfer[x]);
+ strcat(bp, tbuf);
+ }
+ strcat(bp, "\nsync_stat[] = ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%02x", hd->sync_stat[x]);
+ strcat(bp, tbuf);
+ }
+ }
+#ifdef PROC_STATISTICS
+ if (hd->proc & PR_STATISTICS) {
+ strcat(bp, "\ncommands issued: ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%ld", hd->cmd_cnt[x]);
+ strcat(bp, tbuf);
+ }
+ strcat(bp, "\ndisconnects allowed:");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%ld", hd->disc_allowed_cnt[x]);
+ strcat(bp, tbuf);
+ }
+ strcat(bp, "\ndisconnects done: ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%ld", hd->disc_done_cnt[x]);
+ strcat(bp, tbuf);
+ }
+ sprintf(tbuf, "\ninterrupts: \t%ld", hd->int_cnt);
+ strcat(bp, tbuf);
+ }
+#endif
+ if (hd->proc & PR_CONNECTED) {
+ strcat(bp, "\nconnected: ");
+ if (hd->connected) {
+ cmd = (Scsi_Cmnd *) hd->connected;
+ sprintf(tbuf, " %ld-%d:%d(%02x)", cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ strcat(bp, tbuf);
+ }
+ }
+ if (hd->proc & PR_INPUTQ) {
+ strcat(bp, "\ninput_Q: ");
+ cmd = (Scsi_Cmnd *) hd->input_Q;
+ while (cmd) {
+ sprintf(tbuf, " %ld-%d:%d(%02x)", cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ strcat(bp, tbuf);
+ cmd = (Scsi_Cmnd *) cmd->host_scribble;
+ }
+ }
+ if (hd->proc & PR_DISCQ) {
+ strcat(bp, "\ndisconnected_Q:");
+ cmd = (Scsi_Cmnd *) hd->disconnected_Q;
+ while (cmd) {
+ sprintf(tbuf, " %ld-%d:%d(%02x)", cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ strcat(bp, tbuf);
+ cmd = (Scsi_Cmnd *) cmd->host_scribble;
+ }
+ }
+ if (hd->proc & PR_TEST) {
+ ; /* insert your own custom function here */
+ }
+ strcat(bp, "\n");
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ *start = buf;
+ if (stop) {
+ stop = 0;
+ return 0; /* return 0 to signal end-of-file */
+ }
+ if (off > 0x40000) /* ALWAYS stop after 256k bytes have been read */
+ stop = 1;
+ if (hd->proc & PR_STOP) /* stop every other time */
+ stop = 1;
+ return strlen(bp);
+
+#else /* PROC_INTERFACE */
+
+ return 0;
+
+#endif /* PROC_INTERFACE */
+
+}
+
+MODULE_LICENSE("GPL");
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "in2000",
+ .proc_info = in2000_proc_info,
+ .name = "Always IN2000",
+ .detect = in2000_detect,
+ .release = in2000_release,
+ .queuecommand = in2000_queuecommand,
+ .eh_abort_handler = in2000_abort,
+ .eh_bus_reset_handler = in2000_bus_reset,
+ .eh_device_reset_handler = in2000_device_reset,
+ .eh_host_reset_handler = in2000_host_reset,
+ .bios_param = in2000_biosparam,
+ .can_queue = IN2000_CAN_Q,
+ .this_id = IN2000_HOST_ID,
+ .sg_tablesize = IN2000_SG,
+ .cmd_per_lun = IN2000_CPL,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h
new file mode 100644
index 000000000000..019e45df3016
--- /dev/null
+++ b/drivers/scsi/in2000.h
@@ -0,0 +1,414 @@
+/*
+ * in2000.h - Linux device driver definitions for the
+ * Always IN2000 ISA SCSI card.
+ *
+ * IMPORTANT: This file is for version 1.33 - 26/Aug/1998
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * john@geolog.com
+ * jshiffle@netcom.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef IN2000_H
+#define IN2000_H
+
+#include <asm/io.h>
+
+#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */
+#ifdef PROC_INTERFACE
+#define PROC_STATISTICS /* add code for keeping various real time stats */
+#endif
+
+#define SYNC_DEBUG /* extra info on sync negotiation printed */
+#define DEBUGGING_ON /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */
+
+#ifdef __i386__
+#define FAST_READ_IO /* No problems with these on my machine */
+#define FAST_WRITE_IO
+#endif
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
+#else
+#define DB(f,a)
+#define CHECK_NULL(p,s)
+#endif
+
+#define uchar unsigned char
+
+#define read1_io(a) (inb(hostdata->io_base+(a)))
+#define read2_io(a) (inw(hostdata->io_base+(a)))
+#define write1_io(b,a) (outb((b),hostdata->io_base+(a)))
+#define write2_io(w,a) (outw((w),hostdata->io_base+(a)))
+
+#ifdef __i386__
+/* These inline assembly defines are derived from a patch
+ * sent to me by Bill Earnest. He's done a lot of very
+ * valuable thinking, testing, and coding during his effort
+ * to squeeze more speed out of this driver. I really think
+ * that we are doing IO at close to the maximum now with
+ * the fifo. (And yes, insw uses 'edi' while outsw uses
+ * 'esi'. Thanks Bill!)
+ */
+
+#define FAST_READ2_IO() \
+({ \
+int __dummy_1,__dummy_2; \
+ __asm__ __volatile__ ("\n \
+ cld \n \
+ orl %%ecx, %%ecx \n \
+ jz 1f \n \
+ rep \n \
+ insw (%%dx),%%es:(%%edi) \n \
+1: " \
+ : "=D" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2) /* output */ \
+ : "2" (f), "0" (sp), "1" (i) /* input */ \
+ ); /* trashed */ \
+})
+
+#define FAST_WRITE2_IO() \
+({ \
+int __dummy_1,__dummy_2; \
+ __asm__ __volatile__ ("\n \
+ cld \n \
+ orl %%ecx, %%ecx \n \
+ jz 1f \n \
+ rep \n \
+ outsw %%ds:(%%esi),(%%dx) \n \
+1: " \
+ : "=S" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2)/* output */ \
+ : "2" (f), "0" (sp), "1" (i) /* input */ \
+ ); /* trashed */ \
+})
+#endif
+
+/* IN2000 io_port offsets */
+#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */
+#define ASR_INT 0x80
+#define ASR_LCI 0x40
+#define ASR_BSY 0x20
+#define ASR_CIP 0x10
+#define ASR_PE 0x02
+#define ASR_DBR 0x01
+#define IO_WD_ADDR 0x00 /* W - 3393 address reg */
+#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */
+#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */
+#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */
+#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */
+#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */
+#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */
+#define IO_FIFO_READ 0x07 /* W - start fifo read */
+#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */
+#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */
+#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */
+#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */
+#define SW_DISINT 0x04 /* bit 2 true if ints disabled */
+#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */
+#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */
+#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */
+#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */
+#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */
+#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */
+#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */
+#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */
+#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */
+#define IMASK_WD 0x01 /* WD33c93 interrupt mask */
+#define IMASK_FIFO 0x02 /* FIFO interrupt mask */
+
+/* wd register names */
+#define WD_OWN_ID 0x00
+#define WD_CONTROL 0x01
+#define WD_TIMEOUT_PERIOD 0x02
+#define WD_CDB_1 0x03
+#define WD_CDB_2 0x04
+#define WD_CDB_3 0x05
+#define WD_CDB_4 0x06
+#define WD_CDB_5 0x07
+#define WD_CDB_6 0x08
+#define WD_CDB_7 0x09
+#define WD_CDB_8 0x0a
+#define WD_CDB_9 0x0b
+#define WD_CDB_10 0x0c
+#define WD_CDB_11 0x0d
+#define WD_CDB_12 0x0e
+#define WD_TARGET_LUN 0x0f
+#define WD_COMMAND_PHASE 0x10
+#define WD_SYNCHRONOUS_TRANSFER 0x11
+#define WD_TRANSFER_COUNT_MSB 0x12
+#define WD_TRANSFER_COUNT 0x13
+#define WD_TRANSFER_COUNT_LSB 0x14
+#define WD_DESTINATION_ID 0x15
+#define WD_SOURCE_ID 0x16
+#define WD_SCSI_STATUS 0x17
+#define WD_COMMAND 0x18
+#define WD_DATA 0x19
+#define WD_QUEUE_TAG 0x1a
+#define WD_AUXILIARY_STATUS 0x1f
+
+/* WD commands */
+#define WD_CMD_RESET 0x00
+#define WD_CMD_ABORT 0x01
+#define WD_CMD_ASSERT_ATN 0x02
+#define WD_CMD_NEGATE_ACK 0x03
+#define WD_CMD_DISCONNECT 0x04
+#define WD_CMD_RESELECT 0x05
+#define WD_CMD_SEL_ATN 0x06
+#define WD_CMD_SEL 0x07
+#define WD_CMD_SEL_ATN_XFER 0x08
+#define WD_CMD_SEL_XFER 0x09
+#define WD_CMD_RESEL_RECEIVE 0x0a
+#define WD_CMD_RESEL_SEND 0x0b
+#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
+#define WD_CMD_TRANS_ADDR 0x18
+#define WD_CMD_TRANS_INFO 0x20
+#define WD_CMD_TRANSFER_PAD 0x21
+#define WD_CMD_SBT_MODE 0x80
+
+/* SCSI Bus Phases */
+#define PHS_DATA_OUT 0x00
+#define PHS_DATA_IN 0x01
+#define PHS_COMMAND 0x02
+#define PHS_STATUS 0x03
+#define PHS_MESS_OUT 0x06
+#define PHS_MESS_IN 0x07
+
+/* Command Status Register definitions */
+
+ /* reset state interrupts */
+#define CSR_RESET 0x00
+#define CSR_RESET_AF 0x01
+
+ /* successful completion interrupts */
+#define CSR_RESELECT 0x10
+#define CSR_SELECT 0x11
+#define CSR_SEL_XFER_DONE 0x16
+#define CSR_XFER_DONE 0x18
+
+ /* paused or aborted interrupts */
+#define CSR_MSGIN 0x20
+#define CSR_SDP 0x21
+#define CSR_SEL_ABORT 0x22
+#define CSR_RESEL_ABORT 0x25
+#define CSR_RESEL_ABORT_AM 0x27
+#define CSR_ABORT 0x28
+
+ /* terminated interrupts */
+#define CSR_INVALID 0x40
+#define CSR_UNEXP_DISC 0x41
+#define CSR_TIMEOUT 0x42
+#define CSR_PARITY 0x43
+#define CSR_PARITY_ATN 0x44
+#define CSR_BAD_STATUS 0x45
+#define CSR_UNEXP 0x48
+
+ /* service required interrupts */
+#define CSR_RESEL 0x80
+#define CSR_RESEL_AM 0x81
+#define CSR_DISC 0x85
+#define CSR_SRV_REQ 0x88
+
+ /* Own ID/CDB Size register */
+#define OWNID_EAF 0x08
+#define OWNID_EHP 0x10
+#define OWNID_RAF 0x20
+#define OWNID_FS_8 0x00
+#define OWNID_FS_12 0x40
+#define OWNID_FS_16 0x80
+
+ /* Control register */
+#define CTRL_HSP 0x01
+#define CTRL_HA 0x02
+#define CTRL_IDI 0x04
+#define CTRL_EDI 0x08
+#define CTRL_HHP 0x10
+#define CTRL_POLLED 0x00
+#define CTRL_BURST 0x20
+#define CTRL_BUS 0x40
+#define CTRL_DMA 0x80
+
+ /* Timeout Period register */
+#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */
+
+ /* Synchronous Transfer Register */
+#define STR_FSS 0x80
+
+ /* Destination ID register */
+#define DSTID_DPD 0x40
+#define DATA_OUT_DIR 0
+#define DATA_IN_DIR 1
+#define DSTID_SCC 0x80
+
+ /* Source ID register */
+#define SRCID_MASK 0x07
+#define SRCID_SIV 0x08
+#define SRCID_DSP 0x20
+#define SRCID_ES 0x40
+#define SRCID_ER 0x80
+
+
+
+#define ILLEGAL_STATUS_BYTE 0xff
+
+
+#define DEFAULT_SX_PER 500 /* (ns) fairly safe */
+#define DEFAULT_SX_OFF 0 /* aka async */
+
+#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */
+#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */
+
+struct sx_period {
+ unsigned int period_ns;
+ uchar reg_value;
+ };
+
+
+struct IN2000_hostdata {
+ struct Scsi_Host *next;
+ uchar chip; /* what kind of wd33c93 chip? */
+ uchar microcode; /* microcode rev if 'B' */
+ unsigned short io_base; /* IO port base */
+ unsigned int dip_switch; /* dip switch settings */
+ unsigned int hrev; /* hardware revision of card */
+ volatile uchar busy[8]; /* index = target, bit = lun */
+ volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */
+ volatile Scsi_Cmnd *selecting; /* trying to select this command */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */
+ uchar state; /* what we are currently doing */
+ uchar fifo; /* what the FIFO is up to */
+ uchar level2; /* extent to which Level-2 commands are used */
+ uchar disconnect; /* disconnect/reselect policy */
+ unsigned int args; /* set from command-line argument */
+ uchar incoming_msg[8]; /* filled during message_in phase */
+ int incoming_ptr; /* mainly used with EXTENDED messages */
+ uchar outgoing_msg[8]; /* send this during next message_out */
+ int outgoing_len; /* length of outgoing message */
+ unsigned int default_sx_per; /* default transfer period for SCSI bus */
+ uchar sync_xfer[8]; /* sync_xfer reg settings per target */
+ uchar sync_stat[8]; /* status of sync negotiation per target */
+ uchar sync_off; /* bit mask: don't use sync with these targets */
+#ifdef PROC_INTERFACE
+ uchar proc; /* bit mask: what's in proc output */
+#ifdef PROC_STATISTICS
+ unsigned long cmd_cnt[8]; /* # of commands issued per target */
+ unsigned long int_cnt; /* # of interrupts serviced */
+ unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */
+ unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/
+#endif
+#endif
+ };
+
+
+/* defines for hostdata->chip */
+
+#define C_WD33C93 0
+#define C_WD33C93A 1
+#define C_WD33C93B 2
+#define C_UNKNOWN_CHIP 100
+
+/* defines for hostdata->state */
+
+#define S_UNCONNECTED 0
+#define S_SELECTING 1
+#define S_RUNNING_LEVEL2 2
+#define S_CONNECTED 3
+#define S_PRE_TMP_DISC 4
+#define S_PRE_CMP_DISC 5
+
+/* defines for hostdata->fifo */
+
+#define FI_FIFO_UNUSED 0
+#define FI_FIFO_READING 1
+#define FI_FIFO_WRITING 2
+
+/* defines for hostdata->level2 */
+/* NOTE: only the first 3 are trustworthy at this point -
+ * having trouble when more than 1 device is reading/writing
+ * at the same time...
+ */
+
+#define L2_NONE 0 /* no combination commands - we get lots of ints */
+#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */
+#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */
+#define L2_DATA 3 /* resume after DATA_IN/OUT ints */
+#define L2_MOST 4 /* resume after anything except a RESELECT int */
+#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */
+#define L2_ALL 6 /* always resume */
+
+/* defines for hostdata->disconnect */
+
+#define DIS_NEVER 0
+#define DIS_ADAPTIVE 1
+#define DIS_ALWAYS 2
+
+/* defines for hostdata->args */
+
+#define DB_TEST 1<<0
+#define DB_FIFO 1<<1
+#define DB_QUEUE_COMMAND 1<<2
+#define DB_EXECUTE 1<<3
+#define DB_INTR 1<<4
+#define DB_TRANSFER 1<<5
+#define DB_MASK 0x3f
+
+#define A_NO_SCSI_RESET 1<<15
+
+
+/* defines for hostdata->sync_xfer[] */
+
+#define SS_UNSET 0
+#define SS_FIRST 1
+#define SS_WAITING 2
+#define SS_SET 3
+
+/* defines for hostdata->proc */
+
+#define PR_VERSION 1<<0
+#define PR_INFO 1<<1
+#define PR_STATISTICS 1<<2
+#define PR_CONNECTED 1<<3
+#define PR_INPUTQ 1<<4
+#define PR_DISCQ 1<<5
+#define PR_TEST 1<<6
+#define PR_STOP 1<<7
+
+
+# include <linux/init.h>
+# include <linux/spinlock.h>
+# define in2000__INITFUNC(function) __initfunc(function)
+# define in2000__INIT __init
+# define in2000__INITDATA __initdata
+# define CLISPIN_LOCK(host,flags) spin_lock_irqsave(host->host_lock, flags)
+# define CLISPIN_UNLOCK(host,flags) spin_unlock_irqrestore(host->host_lock, \
+ flags)
+
+static int in2000_detect(Scsi_Host_Template *) in2000__INIT;
+static int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int in2000_abort(Scsi_Cmnd *);
+static void in2000_setup(char *, int *) in2000__INIT;
+static int in2000_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int *);
+static int in2000_host_reset(Scsi_Cmnd *);
+static int in2000_bus_reset(Scsi_Cmnd *);
+static int in2000_device_reset(Scsi_Cmnd *);
+
+
+#define IN2000_CAN_Q 16
+#define IN2000_SG SG_ALL
+#define IN2000_CPL 2
+#define IN2000_HOST_ID 7
+
+#endif /* IN2000_H */
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
new file mode 100644
index 000000000000..a7b74d8c53b9
--- /dev/null
+++ b/drivers/scsi/initio.c
@@ -0,0 +1,3184 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
+ * adapters
+ *
+ * 08/06/97 hc - v1.01h
+ * - Support inic-940 and inic-935
+ * 09/26/97 hc - v1.01i
+ * - Make correction from J.W. Schultz suggestion
+ * 10/13/97 hc - Support reset function
+ * 10/21/97 hc - v1.01j
+ * - Support 32 LUN (SCSI 3)
+ * 01/14/98 hc - v1.01k
+ * - Fix memory allocation problem
+ * 03/04/98 hc - v1.01l
+ * - Fix tape rewind which will hang the system problem
+ * - Set can_queue to tul_num_scb
+ * 06/25/98 hc - v1.01m
+ * - Get it work for kernel version >= 2.1.75
+ * - Dynamic assign SCSI bus reset holding time in init_tulip()
+ * 07/02/98 hc - v1.01n
+ * - Support 0002134A
+ * 08/07/98 hc - v1.01o
+ * - Change the tul_abort_srb routine to use scsi_done. <01>
+ * 09/07/98 hl - v1.02
+ * - Change the INI9100U define and proc_dir_entry to
+ * reflect the newer Kernel 2.1.118, but the v1.o1o
+ * should work with Kernel 2.1.118.
+ * 09/20/98 wh - v1.02a
+ * - Support Abort command.
+ * - Handle reset routine.
+ * 09/21/98 hl - v1.03
+ * - remove comments.
+ * 12/09/98 bv - v1.03a
+ * - Removed unused code
+ * 12/13/98 bv - v1.03b
+ * - Remove cli() locking for kernels >= 2.1.95. This uses
+ * spinlocks to serialize access to the pSRB_head and
+ * pSRB_tail members of the HCS structure.
+ * 09/01/99 bv - v1.03d
+ * - Fixed a deadlock problem in SMP.
+ * 21/01/99 bv - v1.03e
+ * - Add support for the Domex 3192U PCI SCSI
+ * This is a slightly modified patch by
+ * Brian Macy <bmacy@sunshinecomputing.com>
+ * 22/02/99 bv - v1.03f
+ * - Didn't detect the INIC-950 in 2.0.x correctly.
+ * Now fixed.
+ * 05/07/99 bv - v1.03g
+ * - Changed the assumption that HZ = 100
+ * 10/17/03 mc - v1.04
+ * - added new DMA API support
+ * 06/01/04 jmd - v1.04a
+ * - Re-add reset_bus support
+ **************************************************************************/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "initio.h"
+
+#define SENSE_SIZE 14
+
+#define i91u_MAXQUEUE 2
+#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"
+
+#define INI_VENDOR_ID 0x1101 /* Initio's PCI vendor ID */
+#define DMX_VENDOR_ID 0x134a /* Domex's PCI vendor ID */
+#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */
+#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */
+#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */
+#define I920_DEVICE_ID 0x0002 /* Initio's other product ID */
+
+#ifdef DEBUG_i91u
+static unsigned int i91u_debug = DEBUG_DEFAULT;
+#endif
+
+#define TULSZ(sz) (sizeof(sz) / sizeof(sz[0]))
+#define TUL_RDWORD(x,y) (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+typedef struct PCI_ID_Struc {
+ unsigned short vendor_id;
+ unsigned short device_id;
+} PCI_ID;
+
+static int tul_num_ch = 4; /* Maximum 4 adapters */
+static int tul_num_scb;
+static int tul_tag_enable = 1;
+static SCB *tul_scb;
+
+#ifdef DEBUG_i91u
+static int setup_debug = 0;
+#endif
+
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb);
+
+static const PCI_ID i91u_pci_devices[] = {
+ { INI_VENDOR_ID, I950_DEVICE_ID },
+ { INI_VENDOR_ID, I940_DEVICE_ID },
+ { INI_VENDOR_ID, I935_DEVICE_ID },
+ { INI_VENDOR_ID, I920_DEVICE_ID },
+ { DMX_VENDOR_ID, I920_DEVICE_ID },
+};
+
+#define DEBUG_INTERRUPT 0
+#define DEBUG_QUEUE 0
+#define DEBUG_STATE 0
+#define INT_DISC 0
+
+/*--- external functions --*/
+static void tul_se2_wait(void);
+
+/*--- forward refrence ---*/
+static SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun);
+static SCB *tul_find_done_scb(HCS * pCurHcb);
+
+static int tulip_main(HCS * pCurHcb);
+
+static int tul_next_state(HCS * pCurHcb);
+static int tul_state_1(HCS * pCurHcb);
+static int tul_state_2(HCS * pCurHcb);
+static int tul_state_3(HCS * pCurHcb);
+static int tul_state_4(HCS * pCurHcb);
+static int tul_state_5(HCS * pCurHcb);
+static int tul_state_6(HCS * pCurHcb);
+static int tul_state_7(HCS * pCurHcb);
+static int tul_xfer_data_in(HCS * pCurHcb);
+static int tul_xfer_data_out(HCS * pCurHcb);
+static int tul_xpad_in(HCS * pCurHcb);
+static int tul_xpad_out(HCS * pCurHcb);
+static int tul_status_msg(HCS * pCurHcb);
+
+static int tul_msgin(HCS * pCurHcb);
+static int tul_msgin_sync(HCS * pCurHcb);
+static int tul_msgin_accept(HCS * pCurHcb);
+static int tul_msgout_reject(HCS * pCurHcb);
+static int tul_msgin_extend(HCS * pCurHcb);
+
+static int tul_msgout_ide(HCS * pCurHcb);
+static int tul_msgout_abort_targ(HCS * pCurHcb);
+static int tul_msgout_abort_tag(HCS * pCurHcb);
+
+static int tul_bus_device_reset(HCS * pCurHcb);
+static void tul_select_atn(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb);
+static int int_tul_busfree(HCS * pCurHcb);
+int int_tul_scsi_rst(HCS * pCurHcb);
+static int int_tul_bad_seq(HCS * pCurHcb);
+static int int_tul_resel(HCS * pCurHcb);
+static int tul_sync_done(HCS * pCurHcb);
+static int wdtr_done(HCS * pCurHcb);
+static int wait_tulip(HCS * pCurHcb);
+static int tul_wait_done_disc(HCS * pCurHcb);
+static int tul_wait_disc(HCS * pCurHcb);
+static void tulip_scsi(HCS * pCurHcb);
+static int tul_post_scsi_rst(HCS * pCurHcb);
+
+static void tul_se2_ew_en(WORD CurBase);
+static void tul_se2_ew_ds(WORD CurBase);
+static int tul_se2_rd_all(WORD CurBase);
+static void tul_se2_update_all(WORD CurBase); /* setup default pattern */
+static void tul_read_eeprom(WORD CurBase);
+
+ /* ---- EXTERNAL VARIABLES ---- */
+HCS tul_hcs[MAX_SUPPORTED_ADAPTERS];
+ /* ---- INTERNAL VARIABLES ---- */
+static INI_ADPT_STRUCT i91u_adpt[MAX_SUPPORTED_ADAPTERS];
+
+/*NVRAM nvram, *nvramp = &nvram; */
+static NVRAM i91unvram;
+static NVRAM *i91unvramp;
+
+
+
+static UCHAR i91udftNvRam[64] =
+{
+/*----------- header -----------*/
+ 0x25, 0xc9, /* Signature */
+ 0x40, /* Size */
+ 0x01, /* Revision */
+ /* -- Host Adapter Structure -- */
+ 0x95, /* ModelByte0 */
+ 0x00, /* ModelByte1 */
+ 0x00, /* ModelInfo */
+ 0x01, /* NumOfCh */
+ NBC1_DEFAULT, /* BIOSConfig1 */
+ 0, /* BIOSConfig2 */
+ 0, /* HAConfig1 */
+ 0, /* HAConfig2 */
+ /* SCSI channel 0 and target Structure */
+ 7, /* SCSIid */
+ NCC1_DEFAULT, /* SCSIconfig1 */
+ 0, /* SCSIconfig2 */
+ 0x10, /* NumSCSItarget */
+
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+
+ /* SCSI channel 1 and target Structure */
+ 7, /* SCSIid */
+ NCC1_DEFAULT, /* SCSIconfig1 */
+ 0, /* SCSIconfig2 */
+ 0x10, /* NumSCSItarget */
+
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0}; /* - CheckSum - */
+
+
+static UCHAR tul_rate_tbl[8] = /* fast 20 */
+{
+ /* nanosecond devide by 4 */
+ 12, /* 50ns, 20M */
+ 18, /* 75ns, 13.3M */
+ 25, /* 100ns, 10M */
+ 31, /* 125ns, 8M */
+ 37, /* 150ns, 6.6M */
+ 43, /* 175ns, 5.7M */
+ 50, /* 200ns, 5M */
+ 62 /* 250ns, 4M */
+};
+
+static void tul_do_pause(unsigned amount)
+{ /* Pause for amount jiffies */
+ unsigned long the_time = jiffies + amount;
+
+ while (time_before_eq(jiffies, the_time));
+}
+
+/*-- forward reference --*/
+
+/*******************************************************************
+ Use memeory refresh time ~ 15us * 2
+********************************************************************/
+void tul_se2_wait(void)
+{
+#if 1
+ udelay(30);
+#else
+ UCHAR readByte;
+
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10) {
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10)
+ break;
+ }
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) != 0x10)
+ break;
+ }
+ } else {
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10)
+ break;
+ }
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) != 0x10)
+ break;
+ }
+ }
+#endif
+}
+
+
+/******************************************************************
+ Input: instruction for Serial E2PROM
+
+ EX: se2_rd(0 call se2_instr() to send address and read command
+
+ StartBit OP_Code Address Data
+ --------- -------- ------------------ -------
+ 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0
+
+ +-----------------------------------------------------
+ |
+ CS -----+
+ +--+ +--+ +--+ +--+ +--+
+ ^ | ^ | ^ | ^ | ^ |
+ | | | | | | | | | |
+ CLK -------+ +--+ +--+ +--+ +--+ +--
+ (leading edge trigger)
+
+ +--1-----1--+
+ | SB OP | OP A5 A4
+ DI ----+ +--0------------------
+ (address and cmd sent to nvram)
+
+ -------------------------------------------+
+ |
+ DO +---
+ (data sent from nvram)
+
+
+******************************************************************/
+void tul_se2_instr(WORD CurBase, UCHAR instr)
+{
+ int i;
+ UCHAR b;
+
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* cs+start bit */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK | SE2DO); /* +CLK */
+ tul_se2_wait();
+
+ for (i = 0; i < 8; i++) {
+ if (instr & 0x80)
+ b = SE2CS | SE2DO; /* -CLK+dataBit */
+ else
+ b = SE2CS; /* -CLK */
+ TUL_WR(CurBase + TUL_NVRAM, b);
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, b | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ instr <<= 1;
+ }
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ return;
+}
+
+
+/******************************************************************
+ Function name : tul_se2_ew_en
+ Description : Enable erase/write state of serial EEPROM
+******************************************************************/
+void tul_se2_ew_en(WORD CurBase)
+{
+ tul_se2_instr(CurBase, 0x30); /* EWEN */
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+ return;
+}
+
+
+/************************************************************************
+ Disable erase/write state of serial EEPROM
+*************************************************************************/
+void tul_se2_ew_ds(WORD CurBase)
+{
+ tul_se2_instr(CurBase, 0); /* EWDS */
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+ return;
+}
+
+
+/******************************************************************
+ Input :address of Serial E2PROM
+ Output :value stored in Serial E2PROM
+*******************************************************************/
+USHORT tul_se2_rd(WORD CurBase, ULONG adr)
+{
+ UCHAR instr, readByte;
+ USHORT readWord;
+ int i;
+
+ instr = (UCHAR) (adr | 0x80);
+ tul_se2_instr(CurBase, instr); /* READ INSTR */
+ readWord = 0;
+
+ for (i = 15; i >= 0; i--) {
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+
+ /* sample data after the following edge of clock */
+ readByte = TUL_RD(CurBase, TUL_NVRAM);
+ readByte &= SE2DI;
+ readWord += (readByte << i);
+ tul_se2_wait(); /* 6/20/95 */
+ }
+
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* no chip select */
+ tul_se2_wait();
+ return readWord;
+}
+
+
+/******************************************************************
+ Input: new value in Serial E2PROM, address of Serial E2PROM
+*******************************************************************/
+void tul_se2_wr(WORD CurBase, UCHAR adr, USHORT writeWord)
+{
+ UCHAR readByte;
+ UCHAR instr;
+ int i;
+
+ instr = (UCHAR) (adr | 0x40);
+ tul_se2_instr(CurBase, instr); /* WRITE INSTR */
+ for (i = 15; i >= 0; i--) {
+ if (writeWord & 0x8000)
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* -CLK+dataBit 1 */
+ else
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK+dataBit 0 */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ writeWord <<= 1;
+ }
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* +CS */
+ tul_se2_wait();
+
+ for (;;) {
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ if ((readByte = TUL_RD(CurBase, TUL_NVRAM)) & SE2DI)
+ break; /* write complete */
+ }
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+int tul_se2_rd_all(WORD CurBase)
+{
+ int i;
+ ULONG chksum = 0;
+ USHORT *np;
+
+ i91unvramp = &i91unvram;
+ np = (USHORT *) i91unvramp;
+ for (i = 0; i < 32; i++) {
+ *np++ = tul_se2_rd(CurBase, i);
+ }
+
+/*--------------------Is signature "ini" ok ? ----------------*/
+ if (i91unvramp->NVM_Signature != INI_SIGNATURE)
+ return -1;
+/*---------------------- Is ckecksum ok ? ----------------------*/
+ np = (USHORT *) i91unvramp;
+ for (i = 0; i < 31; i++)
+ chksum += *np++;
+ if (i91unvramp->NVM_CheckSum != (USHORT) chksum)
+ return -1;
+ return 1;
+}
+
+
+/***********************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+void tul_se2_update_all(WORD CurBase)
+{ /* setup default pattern */
+ int i;
+ ULONG chksum = 0;
+ USHORT *np, *np1;
+
+ i91unvramp = &i91unvram;
+ /* Calculate checksum first */
+ np = (USHORT *) i91udftNvRam;
+ for (i = 0; i < 31; i++)
+ chksum += *np++;
+ *np = (USHORT) chksum;
+ tul_se2_ew_en(CurBase); /* Enable write */
+
+ np = (USHORT *) i91udftNvRam;
+ np1 = (USHORT *) i91unvramp;
+ for (i = 0; i < 32; i++, np++, np1++) {
+ if (*np != *np1) {
+ tul_se2_wr(CurBase, i, *np);
+ }
+ }
+
+ tul_se2_ew_ds(CurBase); /* Disable write */
+ return;
+}
+
+/*************************************************************************
+ Function name : read_eeprom
+**************************************************************************/
+void tul_read_eeprom(WORD CurBase)
+{
+ UCHAR gctrl;
+
+ i91unvramp = &i91unvram;
+/*------Enable EEProm programming ---*/
+ gctrl = TUL_RD(CurBase, TUL_GCTRL);
+ TUL_WR(CurBase + TUL_GCTRL, gctrl | TUL_GCTRL_EEPROM_BIT);
+ if (tul_se2_rd_all(CurBase) != 1) {
+ tul_se2_update_all(CurBase); /* setup default pattern */
+ tul_se2_rd_all(CurBase); /* load again */
+ }
+/*------ Disable EEProm programming ---*/
+ gctrl = TUL_RD(CurBase, TUL_GCTRL);
+ TUL_WR(CurBase + TUL_GCTRL, gctrl & ~TUL_GCTRL_EEPROM_BIT);
+} /* read_eeprom */
+
+int Addi91u_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt,
+ BYTE bBus, BYTE bDevice)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {
+ if (i91u_adpt[i].ADPT_BIOS < wBIOS)
+ continue;
+ if (i91u_adpt[i].ADPT_BIOS == wBIOS) {
+ if (i91u_adpt[i].ADPT_BASE == wBASE) {
+ if (i91u_adpt[i].ADPT_Bus != 0xFF)
+ return 1;
+ } else if (i91u_adpt[i].ADPT_BASE < wBASE)
+ continue;
+ }
+ for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
+ i91u_adpt[j].ADPT_BASE = i91u_adpt[j - 1].ADPT_BASE;
+ i91u_adpt[j].ADPT_INTR = i91u_adpt[j - 1].ADPT_INTR;
+ i91u_adpt[j].ADPT_BIOS = i91u_adpt[j - 1].ADPT_BIOS;
+ i91u_adpt[j].ADPT_Bus = i91u_adpt[j - 1].ADPT_Bus;
+ i91u_adpt[j].ADPT_Device = i91u_adpt[j - 1].ADPT_Device;
+ }
+ i91u_adpt[i].ADPT_BASE = wBASE;
+ i91u_adpt[i].ADPT_INTR = bInterrupt;
+ i91u_adpt[i].ADPT_BIOS = wBIOS;
+ i91u_adpt[i].ADPT_Bus = bBus;
+ i91u_adpt[i].ADPT_Device = bDevice;
+ return 0;
+ }
+ return 1;
+}
+
+void init_i91uAdapter_table(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) { /* Initialize adapter structure */
+ i91u_adpt[i].ADPT_BIOS = 0xffff;
+ i91u_adpt[i].ADPT_BASE = 0xffff;
+ i91u_adpt[i].ADPT_INTR = 0xff;
+ i91u_adpt[i].ADPT_Bus = 0xff;
+ i91u_adpt[i].ADPT_Device = 0xff;
+ }
+ return;
+}
+
+void tul_stop_bm(HCS * pCurHcb)
+{
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+}
+
+/***************************************************************************/
+void get_tulipPCIConfig(HCS * pCurHcb, int ch_idx)
+{
+ pCurHcb->HCS_Base = i91u_adpt[ch_idx].ADPT_BASE; /* Supply base address */
+ pCurHcb->HCS_BIOS = i91u_adpt[ch_idx].ADPT_BIOS; /* Supply BIOS address */
+ pCurHcb->HCS_Intr = i91u_adpt[ch_idx].ADPT_INTR; /* Supply interrupt line */
+ return;
+}
+
+/***************************************************************************/
+int tul_reset_scsi(HCS * pCurHcb, int seconds)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_BUS);
+
+ while (!((pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt)) & TSS_SCSIRST_INT));
+ /* reset tulip chip */
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, 0);
+
+ /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
+ /* SONY 5200 tape drive won't work if only stall for 1 sec */
+ tul_do_pause(seconds * HZ);
+
+ TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+ return (SCSI_RESET_SUCCESS);
+}
+
+/***************************************************************************/
+int init_tulip(HCS * pCurHcb, SCB * scbp, int tul_num_scb, BYTE * pbBiosAdr, int seconds)
+{
+ int i;
+ BYTE *pwFlags;
+ BYTE *pbHeads;
+ SCB *pTmpScb, *pPrevScb = NULL;
+
+ pCurHcb->HCS_NumScbs = tul_num_scb;
+ pCurHcb->HCS_Semaph = 1;
+ spin_lock_init(&pCurHcb->HCS_SemaphLock);
+ pCurHcb->HCS_JSStatus0 = 0;
+ pCurHcb->HCS_Scb = scbp;
+ pCurHcb->HCS_NxtPend = scbp;
+ pCurHcb->HCS_NxtAvail = scbp;
+ for (i = 0, pTmpScb = scbp; i < tul_num_scb; i++, pTmpScb++) {
+ pTmpScb->SCB_TagId = i;
+ if (i != 0)
+ pPrevScb->SCB_NxtScb = pTmpScb;
+ pPrevScb = pTmpScb;
+ }
+ pPrevScb->SCB_NxtScb = NULL;
+ pCurHcb->HCS_ScbEnd = pTmpScb;
+ pCurHcb->HCS_FirstAvail = scbp;
+ pCurHcb->HCS_LastAvail = pPrevScb;
+ spin_lock_init(&pCurHcb->HCS_AvailLock);
+ pCurHcb->HCS_FirstPend = NULL;
+ pCurHcb->HCS_LastPend = NULL;
+ pCurHcb->HCS_FirstBusy = NULL;
+ pCurHcb->HCS_LastBusy = NULL;
+ pCurHcb->HCS_FirstDone = NULL;
+ pCurHcb->HCS_LastDone = NULL;
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+
+ tul_read_eeprom(pCurHcb->HCS_Base);
+/*---------- get H/A configuration -------------*/
+ if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
+ pCurHcb->HCS_MaxTar = 8;
+ else
+ pCurHcb->HCS_MaxTar = 16;
+
+ pCurHcb->HCS_Config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;
+
+ pCurHcb->HCS_SCSI_ID = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
+ pCurHcb->HCS_IdMask = ~(1 << pCurHcb->HCS_SCSI_ID);
+
+#if CHK_PARITY
+ /* Enable parity error response */
+ TUL_WR(pCurHcb->HCS_Base + TUL_PCMD, TUL_RD(pCurHcb->HCS_Base, TUL_PCMD) | 0x40);
+#endif
+
+ /* Mask all the interrupt */
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+
+ tul_stop_bm(pCurHcb);
+ /* --- Initialize the tulip --- */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_CHIP);
+
+ /* program HBA's SCSI ID */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId, pCurHcb->HCS_SCSI_ID << 4);
+
+ /* Enable Initiator Mode ,phase latch,alternate sync period mode,
+ disable SCSI reset */
+ if (pCurHcb->HCS_Config & HCC_EN_PAR)
+ pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
+ else
+ pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_SConf1);
+
+ /* Enable HW reselect */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, 0);
+
+ /* selection time out = 250 ms */
+ TUL_WR(pCurHcb->HCS_Base + TUL_STimeOut, 153);
+
+/*--------- Enable SCSI terminator -----*/
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, (pCurHcb->HCS_Config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)));
+ TUL_WR(pCurHcb->HCS_Base + TUL_GCTRL1,
+ ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE));
+
+ for (i = 0,
+ pwFlags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
+ pbHeads = pbBiosAdr + 0x180;
+ i < pCurHcb->HCS_MaxTar;
+ i++, pwFlags++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags = *pwFlags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ if (pCurHcb->HCS_Tcs[i].TCS_Flags & TCF_EN_255)
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+ else
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+ pCurHcb->HCS_Tcs[i].TCS_JS_Period = 0;
+ pCurHcb->HCS_Tcs[i].TCS_SConfig0 = pCurHcb->HCS_SConf1;
+ pCurHcb->HCS_Tcs[i].TCS_DrvHead = *pbHeads++;
+ if (pCurHcb->HCS_Tcs[i].TCS_DrvHead == 255)
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+ else
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+ pCurHcb->HCS_Tcs[i].TCS_DrvSector = *pbHeads++;
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;
+ pCurHcb->HCS_ActTags[i] = 0;
+ pCurHcb->HCS_MaxTags[i] = 0xFF;
+ } /* for */
+ printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
+ pCurHcb->HCS_Base, pCurHcb->HCS_Intr,
+ pCurHcb->HCS_BIOS, pCurHcb->HCS_SCSI_ID);
+/*------------------- reset SCSI Bus ---------------------------*/
+ if (pCurHcb->HCS_Config & HCC_SCSI_RESET) {
+ printk("i91u: Reset SCSI Bus ... \n");
+ tul_reset_scsi(pCurHcb, seconds);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCFG1, 0x17);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SIntEnable, 0xE9);
+ return (0);
+}
+
+/***************************************************************************/
+SCB *tul_alloc_scb(HCS * hcsp)
+{
+ SCB *pTmpScb;
+ ULONG flags;
+ spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+ if ((pTmpScb = hcsp->HCS_FirstAvail) != NULL) {
+#if DEBUG_QUEUE
+ printk("find scb at %08lx\n", (ULONG) pTmpScb);
+#endif
+ if ((hcsp->HCS_FirstAvail = pTmpScb->SCB_NxtScb) == NULL)
+ hcsp->HCS_LastAvail = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ pTmpScb->SCB_Status = SCB_RENT;
+ }
+ spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_release_scb(HCS * hcsp, SCB * scbp)
+{
+ ULONG flags;
+
+#if DEBUG_QUEUE
+ printk("Release SCB %lx; ", (ULONG) scbp);
+#endif
+ spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+ scbp->SCB_Srb = NULL;
+ scbp->SCB_Status = 0;
+ scbp->SCB_NxtScb = NULL;
+ if (hcsp->HCS_LastAvail != NULL) {
+ hcsp->HCS_LastAvail->SCB_NxtScb = scbp;
+ hcsp->HCS_LastAvail = scbp;
+ } else {
+ hcsp->HCS_FirstAvail = scbp;
+ hcsp->HCS_LastAvail = scbp;
+ }
+ spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+}
+
+/***************************************************************************/
+void tul_append_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("Append pend SCB %lx; ", (ULONG) scbp);
+#endif
+ scbp->SCB_Status = SCB_PEND;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastPend != NULL) {
+ pCurHcb->HCS_LastPend->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ } else {
+ pCurHcb->HCS_FirstPend = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ }
+}
+
+/***************************************************************************/
+void tul_push_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("Push pend SCB %lx; ", (ULONG) scbp);
+#endif
+ scbp->SCB_Status = SCB_PEND;
+ if ((scbp->SCB_NxtScb = pCurHcb->HCS_FirstPend) != NULL) {
+ pCurHcb->HCS_FirstPend = scbp;
+ } else {
+ pCurHcb->HCS_FirstPend = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_find_first_pend_scb(HCS * pCurHcb)
+{
+ SCB *pFirstPend;
+
+
+ pFirstPend = pCurHcb->HCS_FirstPend;
+ while (pFirstPend != NULL) {
+ if (pFirstPend->SCB_Opcode != ExecSCSI) {
+ return (pFirstPend);
+ }
+ if (pFirstPend->SCB_TagMsg == 0) {
+ if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] == 0) &&
+ !(pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+ return (pFirstPend);
+ }
+ } else {
+ if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] >=
+ pCurHcb->HCS_MaxTags[pFirstPend->SCB_Target]) |
+ (pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+ pFirstPend = pFirstPend->SCB_NxtScb;
+ continue;
+ }
+ return (pFirstPend);
+ }
+ pFirstPend = pFirstPend->SCB_NxtScb;
+ }
+
+
+ return (pFirstPend);
+}
+/***************************************************************************/
+SCB *tul_pop_pend_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+ if ((pTmpScb = pCurHcb->HCS_FirstPend) != NULL) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ }
+#if DEBUG_QUEUE
+ printk("Pop pend SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+
+/***************************************************************************/
+void tul_unlink_pend_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+ printk("unlink pend SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;
+ while (pTmpScb != NULL) {
+ if (pCurScb == pTmpScb) { /* Unlink this SCB */
+ if (pTmpScb == pCurHcb->HCS_FirstPend) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastPend)
+ pCurHcb->HCS_LastPend = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ return;
+}
+/***************************************************************************/
+void tul_append_busy_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("append busy SCB %lx; ", (ULONG) scbp);
+#endif
+ if (scbp->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[scbp->SCB_Target]++;
+ else
+ pCurHcb->HCS_Tcs[scbp->SCB_Target].TCS_Flags |= TCF_BUSY;
+ scbp->SCB_Status = SCB_BUSY;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastBusy != NULL) {
+ pCurHcb->HCS_LastBusy->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastBusy = scbp;
+ } else {
+ pCurHcb->HCS_FirstBusy = scbp;
+ pCurHcb->HCS_LastBusy = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_pop_busy_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+
+ if ((pTmpScb = pCurHcb->HCS_FirstBusy) != NULL) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ if (pTmpScb->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ else
+ pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+ }
+#if DEBUG_QUEUE
+ printk("Pop busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_unlink_busy_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+ printk("unlink busy SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+ while (pTmpScb != NULL) {
+ if (pCurScb == pTmpScb) { /* Unlink this SCB */
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+ if (pTmpScb->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ else
+ pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ return;
+}
+
+/***************************************************************************/
+SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun)
+{
+ SCB *pTmpScb, *pPrevScb;
+ WORD scbp_tarlun;
+
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+ while (pTmpScb != NULL) {
+ scbp_tarlun = (pTmpScb->SCB_Lun << 8) | (pTmpScb->SCB_Target);
+ if (scbp_tarlun == tarlun) { /* Unlink this SCB */
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+#if DEBUG_QUEUE
+ printk("find busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_append_done_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("append done SCB %lx; ", (ULONG) scbp);
+#endif
+
+ scbp->SCB_Status = SCB_DONE;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastDone != NULL) {
+ pCurHcb->HCS_LastDone->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastDone = scbp;
+ } else {
+ pCurHcb->HCS_FirstDone = scbp;
+ pCurHcb->HCS_LastDone = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_find_done_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+
+ if ((pTmpScb = pCurHcb->HCS_FirstDone) != NULL) {
+ if ((pCurHcb->HCS_FirstDone = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastDone = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ }
+#if DEBUG_QUEUE
+ printk("find done SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+int tul_abort_srb(HCS * pCurHcb, struct scsi_cmnd *srbp)
+{
+ ULONG flags;
+ SCB *pTmpScb, *pPrevScb;
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tulip_main(pCurHcb);
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ return SCSI_ABORT_SNOOZE;
+ }
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend; /* Check Pend queue */
+ while (pTmpScb != NULL) {
+ /* 07/27/98 */
+ if (pTmpScb->SCB_Srb == srbp) {
+ if (pTmpScb == pCurHcb->HCS_ActScb) {
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_ABORT_BUSY;
+ } else if (pTmpScb == pCurHcb->HCS_FirstPend) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastPend)
+ pCurHcb->HCS_LastPend = pPrevScb;
+ }
+ pTmpScb->SCB_HaStat = HOST_ABORTED;
+ pTmpScb->SCB_Flags |= SCF_DONE;
+ if (pTmpScb->SCB_Flags & SCF_POST)
+ (*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_ABORT_SUCCESS;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */
+ while (pTmpScb != NULL) {
+
+ if (pTmpScb->SCB_Srb == srbp) {
+
+ if (pTmpScb == pCurHcb->HCS_ActScb) {
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_ABORT_BUSY;
+ } else if (pTmpScb->SCB_TagMsg == 0) {
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_ABORT_BUSY;
+ } else {
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+
+
+ pTmpScb->SCB_HaStat = HOST_ABORTED;
+ pTmpScb->SCB_Flags |= SCF_DONE;
+ if (pTmpScb->SCB_Flags & SCF_POST)
+ (*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+}
+
+/***************************************************************************/
+int tul_bad_seq(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+
+ printk("tul_bad_seg c=%d\n", pCurHcb->HCS_Index);
+
+ if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+ tul_unlink_busy_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ pCurScb->SCB_TaStat = 0;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ tul_stop_bm(pCurHcb);
+
+ tul_reset_scsi(pCurHcb, 8); /* 7/29/98 */
+
+ return (tul_post_scsi_rst(pCurHcb));
+}
+
+/************************************************************************/
+int tul_device_reset(HCS * pCurHcb, struct scsi_cmnd *pSrb,
+ unsigned int target, unsigned int ResetFlags)
+{
+ ULONG flags;
+ SCB *pScb;
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) {
+
+ if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tulip_main(pCurHcb);
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ return SCSI_RESET_SNOOZE;
+ }
+ pScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */
+ while (pScb != NULL) {
+ if (pScb->SCB_Srb == pSrb)
+ break;
+ pScb = pScb->SCB_NxtScb;
+ }
+ if (pScb == NULL) {
+ printk("Unable to Reset - No SCB Found\n");
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ }
+ if ((pScb = tul_alloc_scb(pCurHcb)) == NULL) {
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ pScb->SCB_Opcode = BusDevRst;
+ pScb->SCB_Flags = SCF_POST;
+ pScb->SCB_Target = target;
+ pScb->SCB_Mode = 0;
+
+ pScb->SCB_Srb = NULL;
+ if (ResetFlags & SCSI_RESET_SYNCHRONOUS) {
+ pScb->SCB_Srb = pSrb;
+ }
+ tul_push_pend_scb(pCurHcb, pScb); /* push this SCB to Pending queue */
+
+ if (pCurHcb->HCS_Semaph == 1) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+ pCurHcb->HCS_Semaph = 0;
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tulip_main(pCurHcb);
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ }
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return SCSI_RESET_PENDING;
+}
+
+int tul_reset_scsi_bus(HCS * pCurHcb)
+{
+ ULONG flags;
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ pCurHcb->HCS_Semaph = 0;
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tul_stop_bm(pCurHcb);
+
+ tul_reset_scsi(pCurHcb, 2); /* 7/29/98 */
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+ tul_post_scsi_rst(pCurHcb);
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tulip_main(pCurHcb);
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET);
+}
+
+/************************************************************************/
+void tul_exec_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ ULONG flags;
+
+ pCurScb->SCB_Mode = 0;
+
+ pCurScb->SCB_SGIdx = 0;
+ pCurScb->SCB_SGMax = pCurScb->SCB_SGLen;
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tul_append_pend_scb(pCurHcb, pCurScb); /* Append this SCB to Pending queue */
+
+/* VVVVV 07/21/98 */
+ if (pCurHcb->HCS_Semaph == 1) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* disable Jasmin SCSI Int */
+ pCurHcb->HCS_Semaph = 0;
+
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+ tulip_main(pCurHcb);
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ }
+ spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+ return;
+}
+
+/***************************************************************************/
+int tul_isr(HCS * pCurHcb)
+{
+ /* Enter critical section */
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_Int) & TSS_INT_PENDING) {
+ if (pCurHcb->HCS_Semaph == 1) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+ /* Disable Tulip SCSI Int */
+ pCurHcb->HCS_Semaph = 0;
+
+ tulip_main(pCurHcb);
+
+ pCurHcb->HCS_Semaph = 1;
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/***************************************************************************/
+int tulip_main(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+
+ for (;;) {
+
+ tulip_scsi(pCurHcb); /* Call tulip_scsi */
+
+ while ((pCurScb = tul_find_done_scb(pCurHcb)) != NULL) { /* find done entry */
+ if (pCurScb->SCB_TaStat == INI_QUEUE_FULL) {
+ pCurHcb->HCS_MaxTags[pCurScb->SCB_Target] =
+ pCurHcb->HCS_ActTags[pCurScb->SCB_Target] - 1;
+ pCurScb->SCB_TaStat = 0;
+ tul_append_pend_scb(pCurHcb, pCurScb);
+ continue;
+ }
+ if (!(pCurScb->SCB_Mode & SCM_RSENS)) { /* not in auto req. sense mode */
+ if (pCurScb->SCB_TaStat == 2) {
+
+ /* clr sync. nego flag */
+
+ if (pCurScb->SCB_Flags & SCF_SENSE) {
+ BYTE len;
+ len = pCurScb->SCB_SenseLen;
+ if (len == 0)
+ len = 1;
+ pCurScb->SCB_BufLen = pCurScb->SCB_SenseLen;
+ pCurScb->SCB_BufPtr = pCurScb->SCB_SensePtr;
+ pCurScb->SCB_Flags &= ~(SCF_SG | SCF_DIR); /* for xfer_data_in */
+/* pCurScb->SCB_Flags |= SCF_NO_DCHK; */
+ /* so, we won't report worng direction in xfer_data_in,
+ and won't report HOST_DO_DU in state_6 */
+ pCurScb->SCB_Mode = SCM_RSENS;
+ pCurScb->SCB_Ident &= 0xBF; /* Disable Disconnect */
+ pCurScb->SCB_TagMsg = 0;
+ pCurScb->SCB_TaStat = 0;
+ pCurScb->SCB_CDBLen = 6;
+ pCurScb->SCB_CDB[0] = SCSICMD_RequestSense;
+ pCurScb->SCB_CDB[1] = 0;
+ pCurScb->SCB_CDB[2] = 0;
+ pCurScb->SCB_CDB[3] = 0;
+ pCurScb->SCB_CDB[4] = len;
+ pCurScb->SCB_CDB[5] = 0;
+ tul_push_pend_scb(pCurHcb, pCurScb);
+ break;
+ }
+ }
+ } else { /* in request sense mode */
+
+ if (pCurScb->SCB_TaStat == 2) { /* check contition status again after sending
+ requset sense cmd 0x3 */
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ }
+ pCurScb->SCB_TaStat = 2;
+ }
+ pCurScb->SCB_Flags |= SCF_DONE;
+ if (pCurScb->SCB_Flags & SCF_POST) {
+ (*pCurScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pCurScb);
+ }
+ } /* while */
+
+ /* find_active: */
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0) & TSS_INT_PENDING)
+ continue;
+
+ if (pCurHcb->HCS_ActScb) { /* return to OS and wait for xfer_done_ISR/Selected_ISR */
+ return 1; /* return to OS, enable interrupt */
+ }
+ /* Check pending SCB */
+ if (tul_find_first_pend_scb(pCurHcb) == NULL) {
+ return 1; /* return to OS, enable interrupt */
+ }
+ } /* End of for loop */
+ /* statement won't reach here */
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+void tulip_scsi(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ TCS *pCurTcb;
+
+ /* make sure to service interrupt asap */
+
+ if ((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0)) & TSS_INT_PENDING) {
+
+ pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+ pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* SCSI bus reset detected */
+ int_tul_scsi_rst(pCurHcb);
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) { /* if selected/reselected interrupt */
+ if (int_tul_resel(pCurHcb) == 0)
+ tul_next_state(pCurHcb);
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) {
+ int_tul_busfree(pCurHcb);
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ int_tul_busfree(pCurHcb); /* unexpected bus free or sel timeout */
+ return;
+ }
+ if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) { /* func complete or Bus service */
+ if ((pCurScb = pCurHcb->HCS_ActScb) != NULL)
+ tul_next_state(pCurHcb);
+ return;
+ }
+ }
+ if (pCurHcb->HCS_ActScb != NULL)
+ return;
+
+ if ((pCurScb = tul_find_first_pend_scb(pCurHcb)) == NULL)
+ return;
+
+ /* program HBA's SCSI ID & target SCSI ID */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId,
+ (pCurHcb->HCS_SCSI_ID << 4) | (pCurScb->SCB_Target & 0x0F));
+ if (pCurScb->SCB_Opcode == ExecSCSI) {
+ pCurTcb = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+
+ if (pCurScb->SCB_TagMsg)
+ pCurTcb->TCS_DrvFlags |= TCF_DRV_EN_TAG;
+ else
+ pCurTcb->TCS_DrvFlags &= ~TCF_DRV_EN_TAG;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+ if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { /* do wdtr negotiation */
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ } else {
+ if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync negotiation */
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ } else {
+ if (pCurScb->SCB_TagMsg)
+ tul_select_atn3(pCurHcb, pCurScb);
+ else
+ tul_select_atn(pCurHcb, pCurScb);
+ }
+ }
+ if (pCurScb->SCB_Flags & SCF_POLL) {
+ while (wait_tulip(pCurHcb) != -1) {
+ if (tul_next_state(pCurHcb) == -1)
+ break;
+ }
+ }
+ } else if (pCurScb->SCB_Opcode == BusDevRst) {
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ pCurScb->SCB_NxtStat = 8;
+ if (pCurScb->SCB_Flags & SCF_POLL) {
+ while (wait_tulip(pCurHcb) != -1) {
+ if (tul_next_state(pCurHcb) == -1)
+ break;
+ }
+ }
+ } else if (pCurScb->SCB_Opcode == AbortCmd) {
+ if (tul_abort_srb(pCurHcb, pCurScb->SCB_Srb) != 0) {
+
+
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+
+ tul_release_scb(pCurHcb, pCurScb);
+ } else {
+ pCurScb->SCB_Opcode = BusDevRst;
+ tul_select_atn_stop(pCurHcb, pCurScb);
+ pCurScb->SCB_NxtStat = 8;
+ }
+
+/* 08/03/98 */
+ } else {
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = 0x16; /* bad command */
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ return;
+}
+
+
+/***************************************************************************/
+int tul_next_state(HCS * pCurHcb)
+{
+ int next;
+
+ next = pCurHcb->HCS_ActScb->SCB_NxtStat;
+ for (;;) {
+ switch (next) {
+ case 1:
+ next = tul_state_1(pCurHcb);
+ break;
+ case 2:
+ next = tul_state_2(pCurHcb);
+ break;
+ case 3:
+ next = tul_state_3(pCurHcb);
+ break;
+ case 4:
+ next = tul_state_4(pCurHcb);
+ break;
+ case 5:
+ next = tul_state_5(pCurHcb);
+ break;
+ case 6:
+ next = tul_state_6(pCurHcb);
+ break;
+ case 7:
+ next = tul_state_7(pCurHcb);
+ break;
+ case 8:
+ return (tul_bus_device_reset(pCurHcb));
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ if (next <= 0)
+ return next;
+ }
+}
+
+
+/***************************************************************************/
+/* sTate after selection with attention & stop */
+int tul_state_1(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+ printk("-s1-");
+#endif
+
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ tul_append_busy_scb(pCurHcb, pCurScb);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+ /* ATN on */
+ if (pCurHcb->HCS_Phase == MSG_OUT) {
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, (TSC_EN_BUS_IN | TSC_HW_RESELECT));
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+
+ if (pCurScb->SCB_TagMsg) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+ }
+ if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
+
+ pCurTcb->TCS_Flags |= TCF_WDTR_DONE;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2); /* Extended msg length */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3); /* Sync request */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1); /* Start from 16 bits */
+ } else if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
+
+ pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3); /* extended msg length */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1); /* sync request */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET); /* REQ/ACK offset */
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+ return (3);
+}
+
+
+/***************************************************************************/
+/* state after selection with attention */
+/* state after selection with attention3 */
+int tul_state_2(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+ printk("-s2-");
+#endif
+
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ tul_append_busy_scb(pCurHcb, pCurScb);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+
+ if (pCurHcb->HCS_JSStatus1 & TSS_CMD_PH_CMP) {
+ return (4);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+ return (3);
+}
+
+/***************************************************************************/
+/* state before CDB xfer is done */
+int tul_state_3(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+ int i;
+
+#if DEBUG_STATE
+ printk("-s3-");
+#endif
+ for (;;) {
+ switch (pCurHcb->HCS_Phase) {
+ case CMD_OUT: /* Command out phase */
+ for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase == CMD_OUT) {
+ return (tul_bad_seq(pCurHcb));
+ }
+ return (4);
+
+ case MSG_IN: /* Message in phase */
+ pCurScb->SCB_NxtStat = 3;
+ if (tul_msgin(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case STATUS_IN: /* Status phase */
+ if (tul_status_msg(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case MSG_OUT: /* Message out phase */
+ if (pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+
+ } else {
+ pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3); /* ext. msg len */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1); /* sync request */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET); /* REQ/ACK offset */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7));
+
+ }
+ break;
+
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ }
+}
+
+
+/***************************************************************************/
+int tul_state_4(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+ printk("-s4-");
+#endif
+ if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_NO_XF) {
+ return (6); /* Go to state 6 */
+ }
+ for (;;) {
+ if (pCurScb->SCB_BufLen == 0)
+ return (6); /* Go to state 6 */
+
+ switch (pCurHcb->HCS_Phase) {
+
+ case STATUS_IN: /* Status phase */
+ if ((pCurScb->SCB_Flags & SCF_DIR) != 0) { /* if direction bit set then report data underrun */
+ pCurScb->SCB_HaStat = HOST_DO_DU;
+ }
+ if ((tul_status_msg(pCurHcb)) == -1)
+ return (-1);
+ break;
+
+ case MSG_IN: /* Message in phase */
+ pCurScb->SCB_NxtStat = 0x4;
+ if (tul_msgin(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case MSG_OUT: /* Message out phase */
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+ pCurScb->SCB_BufLen = 0;
+ pCurScb->SCB_HaStat = HOST_DO_DU;
+ if (tul_msgout_ide(pCurHcb) == -1)
+ return (-1);
+ return (6); /* Go to state 6 */
+ } else {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ }
+ break;
+
+ case DATA_IN: /* Data in phase */
+ return (tul_xfer_data_in(pCurHcb));
+
+ case DATA_OUT: /* Data out phase */
+ return (tul_xfer_data_out(pCurHcb));
+
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ }
+}
+
+
+/***************************************************************************/
+/* state after dma xfer done or phase change before xfer done */
+int tul_state_5(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ long cnt, xcnt; /* cannot use unsigned !! code: if (xcnt < 0) */
+
+#if DEBUG_STATE
+ printk("-s5-");
+#endif
+/*------ get remaining count -------*/
+
+ cnt = TUL_RDLONG(pCurHcb->HCS_Base, TUL_SCnt0) & 0x0FFFFFF;
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XCmd) & 0x20) {
+ /* ----------------------- DATA_IN ----------------------------- */
+ /* check scsi parity error */
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+ pCurScb->SCB_HaStat = HOST_DO_DU;
+ }
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* DMA xfer pending, Send STOP */
+ /* tell Hardware scsi xfer has been terminated */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, TUL_RD(pCurHcb->HCS_Base, TUL_XCtrl) | 0x80);
+ /* wait until DMA xfer not pending */
+ while (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND);
+ }
+ } else {
+/*-------- DATA OUT -----------*/
+ if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0) {
+ if (pCurHcb->HCS_ActTcs->TCS_JS_Period & TSC_WIDE_SCSI)
+ cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F) << 1;
+ else
+ cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F);
+ }
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+ }
+ if ((cnt == 1) && (pCurHcb->HCS_Phase == DATA_OUT)) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1) {
+ return (-1);
+ }
+ cnt = 0;
+ } else {
+ if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ }
+ }
+
+ if (cnt == 0) {
+ pCurScb->SCB_BufLen = 0;
+ return (6); /* Go to state 6 */
+ }
+ /* Update active data pointer */
+ xcnt = (long) pCurScb->SCB_BufLen - cnt; /* xcnt== bytes already xferred */
+ pCurScb->SCB_BufLen = (U32) cnt; /* cnt == bytes left to be xferred */
+ if (pCurScb->SCB_Flags & SCF_SG) {
+ register SG *sgp;
+ ULONG i;
+
+ sgp = &pCurScb->SCB_SGList[pCurScb->SCB_SGIdx];
+ for (i = pCurScb->SCB_SGIdx; i < pCurScb->SCB_SGMax; sgp++, i++) {
+ xcnt -= (long) sgp->SG_Len;
+ if (xcnt < 0) { /* this sgp xfer half done */
+ xcnt += (long) sgp->SG_Len; /* xcnt == bytes xferred in this sgp */
+ sgp->SG_Ptr += (U32) xcnt; /* new ptr to be xfer */
+ sgp->SG_Len -= (U32) xcnt; /* new len to be xfer */
+ pCurScb->SCB_BufPtr += ((U32) (i - pCurScb->SCB_SGIdx) << 3);
+ /* new SG table ptr */
+ pCurScb->SCB_SGLen = (BYTE) (pCurScb->SCB_SGMax - i);
+ /* new SG table len */
+ pCurScb->SCB_SGIdx = (WORD) i;
+ /* for next disc and come in this loop */
+ return (4); /* Go to state 4 */
+ }
+ /* else (xcnt >= 0 , i.e. this sgp already xferred */
+ } /* for */
+ return (6); /* Go to state 6 */
+ } else {
+ pCurScb->SCB_BufPtr += (U32) xcnt;
+ }
+ return (4); /* Go to state 4 */
+}
+
+/***************************************************************************/
+/* state after Data phase */
+int tul_state_6(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+ printk("-s6-");
+#endif
+ for (;;) {
+ switch (pCurHcb->HCS_Phase) {
+ case STATUS_IN: /* Status phase */
+ if ((tul_status_msg(pCurHcb)) == -1)
+ return (-1);
+ break;
+
+ case MSG_IN: /* Message in phase */
+ pCurScb->SCB_NxtStat = 6;
+ if ((tul_msgin(pCurHcb)) == -1)
+ return (-1);
+ break;
+
+ case MSG_OUT: /* Message out phase */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ break;
+
+ case DATA_IN: /* Data in phase */
+ return (tul_xpad_in(pCurHcb));
+
+ case DATA_OUT: /* Data out phase */
+ return (tul_xpad_out(pCurHcb));
+
+ default:
+ return (tul_bad_seq(pCurHcb));
+ }
+ }
+}
+
+/***************************************************************************/
+int tul_state_7(HCS * pCurHcb)
+{
+ int cnt, i;
+
+#if DEBUG_STATE
+ printk("-s7-");
+#endif
+ /* flush SCSI FIFO */
+ cnt = TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F;
+ if (cnt) {
+ for (i = 0; i < cnt; i++)
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ }
+ switch (pCurHcb->HCS_Phase) {
+ case DATA_IN: /* Data in phase */
+ case DATA_OUT: /* Data out phase */
+ return (tul_bad_seq(pCurHcb));
+ default:
+ return (6); /* Go to state 6 */
+ }
+}
+
+/***************************************************************************/
+int tul_xfer_data_in(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DOUT) {
+ return (6); /* wrong direction */
+ }
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_IN); /* 7/25/95 */
+
+ if (pCurScb->SCB_Flags & SCF_SG) { /* S/G xfer */
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_IN);
+ } else {
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_IN);
+ }
+ pCurScb->SCB_NxtStat = 0x5;
+ return (0); /* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xfer_data_out(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DIN) {
+ return (6); /* wrong direction */
+ }
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_OUT);
+
+ if (pCurScb->SCB_Flags & SCF_SG) { /* S/G xfer */
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_OUT);
+ } else {
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_OUT);
+ }
+
+ pCurScb->SCB_NxtStat = 0x5;
+ return (0); /* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xpad_in(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+ pCurScb->SCB_HaStat = HOST_DO_DU; /* over run */
+ }
+ for (;;) {
+ if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+ else
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1) {
+ return (-1);
+ }
+ if (pCurHcb->HCS_Phase != DATA_IN) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ return (6);
+ }
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ }
+}
+
+int tul_xpad_out(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+ if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+ pCurScb->SCB_HaStat = HOST_DO_DU; /* over run */
+ }
+ for (;;) {
+ if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+ else
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ if ((wait_tulip(pCurHcb)) == -1) {
+ return (-1);
+ }
+ if (pCurHcb->HCS_Phase != DATA_OUT) { /* Disable wide CPU to allow read 16 bits */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ return (6);
+ }
+ }
+}
+
+
+/***************************************************************************/
+int tul_status_msg(HCS * pCurHcb)
+{ /* status & MSG_IN */
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ BYTE msg;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_CMD_COMP);
+ if ((wait_tulip(pCurHcb)) == -1) {
+ return (-1);
+ }
+ /* get status */
+ pCurScb->SCB_TaStat = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+
+ if (pCurHcb->HCS_Phase == MSG_OUT) {
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+ } else {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ if (pCurHcb->HCS_Phase == MSG_IN) {
+ msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) { /* Parity error */
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_OUT)
+ return (tul_bad_seq(pCurHcb));
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ if (msg == 0) { /* Command complete */
+
+ if ((pCurScb->SCB_TaStat & 0x18) == 0x10) { /* No link support */
+ return (tul_bad_seq(pCurHcb));
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+ return tul_wait_done_disc(pCurHcb);
+
+ }
+ if ((msg == MSG_LINK_COMP) || (msg == MSG_LINK_FLAG)) {
+ if ((pCurScb->SCB_TaStat & 0x18) == 0x10)
+ return (tul_msgin_accept(pCurHcb));
+ }
+ }
+ return (tul_bad_seq(pCurHcb));
+}
+
+
+/***************************************************************************/
+/* scsi bus free */
+int int_tul_busfree(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+ if (pCurScb != NULL) {
+ if (pCurScb->SCB_Status & SCB_SELECT) { /* selection timeout */
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = HOST_SEL_TOUT;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ } else { /* Unexpected bus free */
+ tul_unlink_busy_scb(pCurHcb, pCurScb);
+ pCurScb->SCB_HaStat = HOST_BUS_FREE;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi bus reset */
+int int_tul_scsi_rst(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ int i;
+
+ /* if DMA xfer is pending, abort DMA xfer */
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & 0x01) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & 0x04) == 0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ }
+ /* Abort all active & disconnected scb */
+ while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+
+ /* clr sync nego. done flag */
+ for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ }
+ return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi reselection */
+int int_tul_resel(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ TCS *pCurTcb;
+ BYTE tag, msg = 0;
+ BYTE tar, lun;
+
+ if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+ if (pCurScb->SCB_Status & SCB_SELECT) { /* if waiting for selection complete */
+ pCurScb->SCB_Status &= ~SCB_SELECT;
+ }
+ pCurHcb->HCS_ActScb = NULL;
+ }
+ /* --------- get target id---------------------- */
+ tar = TUL_RD(pCurHcb->HCS_Base, TUL_SBusId);
+ /* ------ get LUN from Identify message----------- */
+ lun = TUL_RD(pCurHcb->HCS_Base, TUL_SIdent) & 0x0F;
+ /* 07/22/98 from 0x1F -> 0x0F */
+ pCurTcb = &pCurHcb->HCS_Tcs[tar];
+ pCurHcb->HCS_ActTcs = pCurTcb;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+
+
+ /* ------------- tag queueing ? ------------------- */
+ if (pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG) {
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_IN)
+ goto no_tag;
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1)
+ return (-1);
+ msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* Read Tag Message */
+
+ if ((msg < MSG_STAG) || (msg > MSG_OTAG)) /* Is simple Tag */
+ goto no_tag;
+
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+
+ if (pCurHcb->HCS_Phase != MSG_IN)
+ goto no_tag;
+
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1)
+ return (-1);
+ tag = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* Read Tag ID */
+ pCurScb = pCurHcb->HCS_Scb + tag;
+ if ((pCurScb->SCB_Target != tar) || (pCurScb->SCB_Lun != lun)) {
+ return tul_msgout_abort_tag(pCurHcb);
+ }
+ if (pCurScb->SCB_Status != SCB_BUSY) { /* 03/24/95 */
+ return tul_msgout_abort_tag(pCurHcb);
+ }
+ pCurHcb->HCS_ActScb = pCurScb;
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ } else { /* No tag */
+ no_tag:
+ if ((pCurScb = tul_find_busy_scb(pCurHcb, tar | (lun << 8))) == NULL) {
+ return tul_msgout_abort_targ(pCurHcb);
+ }
+ pCurHcb->HCS_ActScb = pCurScb;
+ if (!(pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG)) {
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+ }
+ }
+ return 0;
+}
+
+
+/***************************************************************************/
+int int_tul_bad_seq(HCS * pCurHcb)
+{ /* target wrong phase */
+ SCB *pCurScb;
+ int i;
+
+ tul_reset_scsi(pCurHcb, 10);
+
+ while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ }
+ return (-1);
+}
+
+
+/***************************************************************************/
+int tul_msgout_abort_targ(HCS * pCurHcb)
+{
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ if (tul_msgin_accept(pCurHcb) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_OUT)
+ return (tul_bad_seq(pCurHcb));
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+ return tul_wait_disc(pCurHcb);
+}
+
+/***************************************************************************/
+int tul_msgout_abort_tag(HCS * pCurHcb)
+{
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ if (tul_msgin_accept(pCurHcb) == -1)
+ return (-1);
+ if (pCurHcb->HCS_Phase != MSG_OUT)
+ return (tul_bad_seq(pCurHcb));
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT_TAG);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+ return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin(HCS * pCurHcb)
+{
+ TCS *pCurTcb;
+
+ for (;;) {
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if ((wait_tulip(pCurHcb)) == -1)
+ return (-1);
+
+ switch (TUL_RD(pCurHcb->HCS_Base, TUL_SFifo)) {
+ case MSG_DISC: /* Disconnect msg */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+
+ return tul_wait_disc(pCurHcb);
+
+ case MSG_SDP:
+ case MSG_RESTORE:
+ case MSG_NOP:
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ case MSG_REJ: /* Clear ATN first */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal,
+ (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+ pCurTcb = pCurHcb->HCS_ActTcs;
+ if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync nego */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ }
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ case MSG_EXTEND: /* extended msg */
+ tul_msgin_extend(pCurHcb);
+ break;
+
+ case MSG_IGNOREWIDE:
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ /* get */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if (wait_tulip(pCurHcb) == -1)
+ return -1;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0); /* put pad */
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* get IGNORE field */
+ TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* get pad */
+
+ tul_msgin_accept(pCurHcb);
+ break;
+
+ case MSG_COMP:
+ {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+ return tul_wait_done_disc(pCurHcb);
+ }
+ default:
+ tul_msgout_reject(pCurHcb);
+ break;
+ }
+ if (pCurHcb->HCS_Phase != MSG_IN)
+ return (pCurHcb->HCS_Phase);
+ }
+ /* statement won't reach here */
+}
+
+
+
+
+/***************************************************************************/
+int tul_msgout_reject(HCS * pCurHcb)
+{
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+ if ((tul_msgin_accept(pCurHcb)) == -1)
+ return (-1);
+
+ if (pCurHcb->HCS_Phase == MSG_OUT) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_REJ); /* Msg reject */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ return (pCurHcb->HCS_Phase);
+}
+
+
+
+/***************************************************************************/
+int tul_msgout_ide(HCS * pCurHcb)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_IDE); /* Initiator Detected Error */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+}
+
+
+/***************************************************************************/
+int tul_msgin_extend(HCS * pCurHcb)
+{
+ BYTE len, idx;
+
+ if (tul_msgin_accept(pCurHcb) != MSG_IN)
+ return (pCurHcb->HCS_Phase);
+
+ /* Get extended msg length */
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+
+ len = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ pCurHcb->HCS_Msg[0] = len;
+ for (idx = 1; len != 0; len--) {
+
+ if ((tul_msgin_accept(pCurHcb)) != MSG_IN)
+ return (pCurHcb->HCS_Phase);
+ TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+ if (wait_tulip(pCurHcb) == -1)
+ return (-1);
+ pCurHcb->HCS_Msg[idx++] = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+ }
+ if (pCurHcb->HCS_Msg[1] == 1) { /* if it's synchronous data transfer request */
+ if (pCurHcb->HCS_Msg[0] != 3) /* if length is not right */
+ return (tul_msgout_reject(pCurHcb));
+ if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_SYNC_NEGO) { /* Set OFFSET=0 to do async, nego back */
+ pCurHcb->HCS_Msg[3] = 0;
+ } else {
+ if ((tul_msgin_sync(pCurHcb) == 0) &&
+ (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SYNC_DONE)) {
+ tul_sync_done(pCurHcb);
+ return (tul_msgin_accept(pCurHcb));
+ }
+ }
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ if ((tul_msgin_accept(pCurHcb)) != MSG_OUT)
+ return (pCurHcb->HCS_Phase);
+ /* sync msg out */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+ tul_sync_done(pCurHcb);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[3]);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+ }
+ if ((pCurHcb->HCS_Msg[0] != 2) || (pCurHcb->HCS_Msg[1] != 3))
+ return (tul_msgout_reject(pCurHcb));
+ /* if it's WIDE DATA XFER REQ */
+ if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) {
+ pCurHcb->HCS_Msg[2] = 0;
+ } else {
+ if (pCurHcb->HCS_Msg[2] > 2) /* > 32 bits */
+ return (tul_msgout_reject(pCurHcb));
+ if (pCurHcb->HCS_Msg[2] == 2) { /* == 32 */
+ pCurHcb->HCS_Msg[2] = 1;
+ } else {
+ if ((pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) == 0) {
+ wdtr_done(pCurHcb);
+ if ((pCurHcb->HCS_ActTcs->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+ return (tul_msgin_accept(pCurHcb));
+ }
+ }
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+ if (tul_msgin_accept(pCurHcb) != MSG_OUT)
+ return (pCurHcb->HCS_Phase);
+ /* WDTR msg out */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+ return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_msgin_sync(HCS * pCurHcb)
+{
+ char default_period;
+
+ default_period = tul_rate_tbl[pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SCSI_RATE];
+ if (pCurHcb->HCS_Msg[3] > MAX_OFFSET) {
+ pCurHcb->HCS_Msg[3] = MAX_OFFSET;
+ if (pCurHcb->HCS_Msg[2] < default_period) {
+ pCurHcb->HCS_Msg[2] = default_period;
+ return 1;
+ }
+ if (pCurHcb->HCS_Msg[2] >= 59) { /* Change to async */
+ pCurHcb->HCS_Msg[3] = 0;
+ }
+ return 1;
+ }
+ /* offset requests asynchronous transfers ? */
+ if (pCurHcb->HCS_Msg[3] == 0) {
+ return 0;
+ }
+ if (pCurHcb->HCS_Msg[2] < default_period) {
+ pCurHcb->HCS_Msg[2] = default_period;
+ return 1;
+ }
+ if (pCurHcb->HCS_Msg[2] >= 59) {
+ pCurHcb->HCS_Msg[3] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+
+/***************************************************************************/
+int wdtr_done(HCS * pCurHcb)
+{
+ pCurHcb->HCS_ActTcs->TCS_Flags &= ~TCF_SYNC_DONE;
+ pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_WDTR_DONE;
+
+ pCurHcb->HCS_ActTcs->TCS_JS_Period = 0;
+ if (pCurHcb->HCS_Msg[2]) { /* if 16 bit */
+ pCurHcb->HCS_ActTcs->TCS_JS_Period |= TSC_WIDE_SCSI;
+ }
+ pCurHcb->HCS_ActTcs->TCS_SConfig0 &= ~TSC_ALT_PERIOD;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+ return 1;
+}
+
+/***************************************************************************/
+int tul_sync_done(HCS * pCurHcb)
+{
+ int i;
+
+ pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_SYNC_DONE;
+
+ if (pCurHcb->HCS_Msg[3]) {
+ pCurHcb->HCS_ActTcs->TCS_JS_Period |= pCurHcb->HCS_Msg[3];
+ for (i = 0; i < 8; i++) {
+ if (tul_rate_tbl[i] >= pCurHcb->HCS_Msg[2]) /* pick the big one */
+ break;
+ }
+ pCurHcb->HCS_ActTcs->TCS_JS_Period |= (i << 4);
+ pCurHcb->HCS_ActTcs->TCS_SConfig0 |= TSC_ALT_PERIOD;
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+ return (-1);
+}
+
+
+int tul_post_scsi_rst(HCS * pCurHcb)
+{
+ SCB *pCurScb;
+ TCS *pCurTcb;
+ int i;
+
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ pCurHcb->HCS_Flags = 0;
+
+ while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+ pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+ tul_append_done_scb(pCurHcb, pCurScb);
+ }
+ /* clear sync done flag */
+ pCurTcb = &pCurHcb->HCS_Tcs[0];
+ for (i = 0; i < pCurHcb->HCS_MaxTar; pCurTcb++, i++) {
+ pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ /* Initialize the sync. xfer register values to an asyn xfer */
+ pCurTcb->TCS_JS_Period = 0;
+ pCurTcb->TCS_SConfig0 = pCurHcb->HCS_SConf1;
+ pCurHcb->HCS_ActTags[0] = 0; /* 07/22/98 */
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY; /* 07/22/98 */
+ } /* for */
+
+ return (-1);
+}
+
+/***************************************************************************/
+void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb)
+{
+ pCurScb->SCB_Status |= SCB_SELECT;
+ pCurScb->SCB_NxtStat = 0x1;
+ pCurHcb->HCS_ActScb = pCurScb;
+ pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SELATNSTOP);
+ return;
+}
+
+
+/***************************************************************************/
+void tul_select_atn(HCS * pCurHcb, SCB * pCurScb)
+{
+ int i;
+
+ pCurScb->SCB_Status |= SCB_SELECT;
+ pCurScb->SCB_NxtStat = 0x2;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+ for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+ pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+ pCurHcb->HCS_ActScb = pCurScb;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN);
+ return;
+}
+
+/***************************************************************************/
+void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb)
+{
+ int i;
+
+ pCurScb->SCB_Status |= SCB_SELECT;
+ pCurScb->SCB_NxtStat = 0x2;
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+ for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+ pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+ pCurHcb->HCS_ActScb = pCurScb;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN3);
+ return;
+}
+
+/***************************************************************************/
+/* SCSI Bus Device Reset */
+int tul_bus_device_reset(HCS * pCurHcb)
+{
+ SCB *pCurScb = pCurHcb->HCS_ActScb;
+ TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+ SCB *pTmpScb, *pPrevScb;
+ BYTE tar;
+
+ if (pCurHcb->HCS_Phase != MSG_OUT) {
+ return (int_tul_bad_seq(pCurHcb)); /* Unexpected phase */
+ }
+ tul_unlink_pend_scb(pCurHcb, pCurScb);
+ tul_release_scb(pCurHcb, pCurScb);
+
+
+ tar = pCurScb->SCB_Target; /* target */
+ pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY);
+ /* clr sync. nego & WDTR flags 07/22/98 */
+
+ /* abort all SCB with same target */
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */
+ while (pTmpScb != NULL) {
+
+ if (pTmpScb->SCB_Target == tar) {
+ /* unlink it */
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_HaStat = HOST_ABORTED;
+ tul_append_done_scb(pCurHcb, pTmpScb);
+ }
+ /* Previous haven't change */
+ else {
+ pPrevScb = pTmpScb;
+ }
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_DEVRST);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+ return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin_accept(HCS * pCurHcb)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+ return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int wait_tulip(HCS * pCurHcb)
+{
+
+ while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+ & TSS_INT_PENDING));
+
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+ pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+ pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+
+ if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) { /* if SCSI bus reset detected */
+ return (int_tul_resel(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) { /* if selected/reselected timeout interrupt */
+ return (int_tul_busfree(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* if SCSI bus reset detected */
+ return (int_tul_scsi_rst(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ if (pCurHcb->HCS_Flags & HCF_EXPECT_DONE_DISC) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+ pCurHcb->HCS_ActScb->SCB_HaStat = 0;
+ tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ pCurHcb->HCS_Flags &= ~HCF_EXPECT_DONE_DISC;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ return (-1);
+ }
+ if (pCurHcb->HCS_Flags & HCF_EXPECT_DISC) {
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+ pCurHcb->HCS_Flags &= ~HCF_EXPECT_DISC;
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ return (-1);
+ }
+ return (int_tul_busfree(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) {
+ return (pCurHcb->HCS_Phase);
+ }
+ return (pCurHcb->HCS_Phase);
+}
+/***************************************************************************/
+int tul_wait_disc(HCS * pCurHcb)
+{
+
+ while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+ & TSS_INT_PENDING));
+
+
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* if SCSI bus reset detected */
+ return (int_tul_scsi_rst(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ pCurHcb->HCS_ActScb = NULL;
+ return (-1);
+ }
+ return (tul_bad_seq(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_wait_done_disc(HCS * pCurHcb)
+{
+
+
+ while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+ & TSS_INT_PENDING));
+
+ pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+
+ if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) { /* if SCSI bus reset detected */
+ return (int_tul_scsi_rst(pCurHcb));
+ }
+ if (pCurHcb->HCS_JSInt & TSS_DISC_INT) { /* BUS disconnection */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT); /* Enable HW reselect */
+ tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+
+ tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+ pCurHcb->HCS_ActScb = NULL;
+ return (-1);
+ }
+ return (tul_bad_seq(pCurHcb));
+}
+
+static irqreturn_t i91u_intr(int irqno, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *dev = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ tul_isr((HCS *)dev->base);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int tul_NewReturnNumberOfAdapters(void)
+{
+ struct pci_dev *pDev = NULL; /* Start from none */
+ int iAdapters = 0;
+ long dRegValue;
+ WORD wBIOS;
+ int i = 0;
+
+ init_i91uAdapter_table();
+
+ for (i = 0; i < TULSZ(i91u_pci_devices); i++)
+ {
+ while ((pDev = pci_find_device(i91u_pci_devices[i].vendor_id, i91u_pci_devices[i].device_id, pDev)) != NULL) {
+ if (pci_enable_device(pDev))
+ continue;
+ pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+ wBIOS = (UWORD) (dRegValue & 0xFF);
+ if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+ dRegValue = 0;
+ wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+ if (pci_set_dma_mask(pDev, 0xffffffff)) {
+ printk(KERN_WARNING
+ "i91u: Could not set 32 bit DMA mask\n");
+ continue;
+ }
+
+ if (Addi91u_into_Adapter_table(wBIOS,
+ (pDev->resource[0].start),
+ pDev->irq,
+ pDev->bus->number,
+ (pDev->devfn >> 3)
+ ) == 0)
+ iAdapters++;
+ }
+ }
+
+ return (iAdapters);
+}
+
+static int i91u_detect(struct scsi_host_template * tpnt)
+{
+ HCS *pHCB;
+ struct Scsi_Host *hreg;
+ unsigned long i; /* 01/14/98 */
+ int ok = 0, iAdapters;
+ ULONG dBiosAdr;
+ BYTE *pbBiosAdr;
+
+ /* Get total number of adapters in the motherboard */
+ iAdapters = tul_NewReturnNumberOfAdapters();
+ if (iAdapters == 0) /* If no tulip founded, return */
+ return (0);
+
+ tul_num_ch = (iAdapters > tul_num_ch) ? tul_num_ch : iAdapters;
+ /* Update actually channel number */
+ if (tul_tag_enable) { /* 1.01i */
+ tul_num_scb = MAX_TARGETS * i91u_MAXQUEUE;
+ } else {
+ tul_num_scb = MAX_TARGETS + 3; /* 1-tape, 1-CD_ROM, 1- extra */
+ } /* Update actually SCBs per adapter */
+
+ /* Get total memory needed for HCS */
+ i = tul_num_ch * sizeof(HCS);
+ memset((unsigned char *) &tul_hcs[0], 0, i); /* Initialize tul_hcs 0 */
+ /* Get total memory needed for SCB */
+
+ for (; tul_num_scb >= MAX_TARGETS + 3; tul_num_scb--) {
+ i = tul_num_ch * tul_num_scb * sizeof(SCB);
+ if ((tul_scb = (SCB *) kmalloc(i, GFP_ATOMIC | GFP_DMA)) != NULL)
+ break;
+ }
+ if (tul_scb == NULL) {
+ printk("i91u: SCB memory allocation error\n");
+ return (0);
+ }
+ memset((unsigned char *) tul_scb, 0, i);
+
+ for (i = 0, pHCB = &tul_hcs[0]; /* Get pointer for control block */
+ i < tul_num_ch;
+ i++, pHCB++) {
+ get_tulipPCIConfig(pHCB, i);
+
+ dBiosAdr = pHCB->HCS_BIOS;
+ dBiosAdr = (dBiosAdr << 4);
+
+ pbBiosAdr = phys_to_virt(dBiosAdr);
+
+ init_tulip(pHCB, tul_scb + (i * tul_num_scb), tul_num_scb, pbBiosAdr, 10);
+ request_region(pHCB->HCS_Base, 256, "i91u"); /* Register */
+
+ pHCB->HCS_Index = i; /* 7/29/98 */
+ hreg = scsi_register(tpnt, sizeof(HCS));
+ if(hreg == NULL) {
+ release_region(pHCB->HCS_Base, 256);
+ return 0;
+ }
+ hreg->io_port = pHCB->HCS_Base;
+ hreg->n_io_port = 0xff;
+ hreg->can_queue = tul_num_scb; /* 03/05/98 */
+ hreg->unique_id = pHCB->HCS_Base;
+ hreg->max_id = pHCB->HCS_MaxTar;
+ hreg->max_lun = 32; /* 10/21/97 */
+ hreg->irq = pHCB->HCS_Intr;
+ hreg->this_id = pHCB->HCS_SCSI_ID; /* Assign HCS index */
+ hreg->base = (unsigned long)pHCB;
+ hreg->sg_tablesize = TOTAL_SG_ENTRY; /* Maximun support is 32 */
+
+ /* Initial tulip chip */
+ ok = request_irq(pHCB->HCS_Intr, i91u_intr, SA_INTERRUPT | SA_SHIRQ, "i91u", hreg);
+ if (ok < 0) {
+ printk(KERN_WARNING "i91u: unable to request IRQ %d\n\n", pHCB->HCS_Intr);
+ return 0;
+ }
+ }
+
+ tpnt->this_id = -1;
+ tpnt->can_queue = 1;
+
+ return 1;
+}
+
+static void i91uBuildSCB(HCS * pHCB, SCB * pSCB, struct scsi_cmnd * SCpnt)
+{ /* Create corresponding SCB */
+ struct scatterlist *pSrbSG;
+ SG *pSG; /* Pointer to SG list */
+ int i;
+ long TotalLen;
+ dma_addr_t dma_addr;
+
+ pSCB->SCB_Post = i91uSCBPost; /* i91u's callback routine */
+ pSCB->SCB_Srb = SCpnt;
+ pSCB->SCB_Opcode = ExecSCSI;
+ pSCB->SCB_Flags = SCF_POST; /* After SCSI done, call post routine */
+ pSCB->SCB_Target = SCpnt->device->id;
+ pSCB->SCB_Lun = SCpnt->device->lun;
+ pSCB->SCB_Ident = SCpnt->device->lun | DISC_ALLOW;
+
+ pSCB->SCB_Flags |= SCF_SENSE; /* Turn on auto request sense */
+ dma_addr = dma_map_single(&pHCB->pci_dev->dev, SCpnt->sense_buffer,
+ SENSE_SIZE, DMA_FROM_DEVICE);
+ pSCB->SCB_SensePtr = cpu_to_le32((u32)dma_addr);
+ pSCB->SCB_SenseLen = cpu_to_le32(SENSE_SIZE);
+ SCpnt->SCp.ptr = (char *)(unsigned long)dma_addr;
+
+ pSCB->SCB_CDBLen = SCpnt->cmd_len;
+ pSCB->SCB_HaStat = 0;
+ pSCB->SCB_TaStat = 0;
+ memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, SCpnt->cmd_len);
+
+ if (SCpnt->device->tagged_supported) { /* Tag Support */
+ pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG; /* Do simple tag only */
+ } else {
+ pSCB->SCB_TagMsg = 0; /* No tag support */
+ }
+ /* todo handle map_sg error */
+ if (SCpnt->use_sg) {
+ dma_addr = dma_map_single(&pHCB->pci_dev->dev, &pSCB->SCB_SGList[0],
+ sizeof(struct SG_Struc) * TOTAL_SG_ENTRY,
+ DMA_BIDIRECTIONAL);
+ pSCB->SCB_BufPtr = cpu_to_le32((u32)dma_addr);
+ SCpnt->SCp.dma_handle = dma_addr;
+
+ pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+ pSCB->SCB_SGLen = dma_map_sg(&pHCB->pci_dev->dev, pSrbSG,
+ SCpnt->use_sg, SCpnt->sc_data_direction);
+
+ pSCB->SCB_Flags |= SCF_SG; /* Turn on SG list flag */
+ for (i = 0, TotalLen = 0, pSG = &pSCB->SCB_SGList[0]; /* 1.01g */
+ i < pSCB->SCB_SGLen; i++, pSG++, pSrbSG++) {
+ pSG->SG_Ptr = cpu_to_le32((u32)sg_dma_address(pSrbSG));
+ TotalLen += pSG->SG_Len = cpu_to_le32((u32)sg_dma_len(pSrbSG));
+ }
+
+ pSCB->SCB_BufLen = (SCpnt->request_bufflen > TotalLen) ?
+ TotalLen : SCpnt->request_bufflen;
+ } else if (SCpnt->request_bufflen) { /* Non SG */
+ dma_addr = dma_map_single(&pHCB->pci_dev->dev, SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ SCpnt->sc_data_direction);
+ SCpnt->SCp.dma_handle = dma_addr;
+ pSCB->SCB_BufPtr = cpu_to_le32((u32)dma_addr);
+ pSCB->SCB_BufLen = cpu_to_le32((u32)SCpnt->request_bufflen);
+ pSCB->SCB_SGLen = 0;
+ } else {
+ pSCB->SCB_BufLen = 0;
+ pSCB->SCB_SGLen = 0;
+ }
+}
+
+static int i91u_queuecommand(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ HCS *pHCB = (HCS *) cmd->device->host->base;
+ register SCB *pSCB;
+
+ cmd->scsi_done = done;
+
+ pSCB = tul_alloc_scb(pHCB);
+ if (!pSCB)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ i91uBuildSCB(pHCB, pSCB, cmd);
+ tul_exec_scb(pHCB, pSCB);
+ return 0;
+}
+
+#if 0 /* no new EH yet */
+/*
+ * Abort a queued command
+ * (commands that are on the bus can't be aborted easily)
+ */
+static int i91u_abort(struct scsi_cmnd * SCpnt)
+{
+ HCS *pHCB;
+
+ pHCB = (HCS *) SCpnt->device->host->base;
+ return tul_abort_srb(pHCB, SCpnt);
+}
+
+/*
+ * Reset registers, reset a hanging bus and
+ * kill active and disconnected commands for target w/o soft reset
+ */
+static int i91u_reset(struct scsi_cmnd * SCpnt, unsigned int reset_flags)
+{ /* I need Host Control Block Information */
+ HCS *pHCB;
+
+ pHCB = (HCS *) SCpnt->device->host->base;
+
+ if (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
+ return tul_reset_scsi_bus(pHCB);
+ else
+ return tul_device_reset(pHCB, SCpnt, SCpnt->device->id, reset_flags);
+}
+#endif
+
+static int i91u_bus_reset(struct scsi_cmnd * SCpnt)
+{
+ HCS *pHCB;
+
+ pHCB = (HCS *) SCpnt->device->host->base;
+ tul_reset_scsi(pHCB, 0);
+ return SUCCESS;
+}
+
+/*
+ * Return the "logical geometry"
+ */
+static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ HCS *pHcb; /* Point to Host adapter control block */
+ TCS *pTcb;
+
+ pHcb = (HCS *) sdev->host->base;
+ pTcb = &pHcb->HCS_Tcs[sdev->id];
+
+ if (pTcb->TCS_DrvHead) {
+ info_array[0] = pTcb->TCS_DrvHead;
+ info_array[1] = pTcb->TCS_DrvSector;
+ info_array[2] = (unsigned long)capacity / pTcb->TCS_DrvHead / pTcb->TCS_DrvSector;
+ } else {
+ if (pTcb->TCS_DrvFlags & TCF_DRV_255_63) {
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = (unsigned long)capacity / 255 / 63;
+ } else {
+ info_array[0] = 64;
+ info_array[1] = 32;
+ info_array[2] = (unsigned long)capacity >> 11;
+ }
+ }
+
+#if defined(DEBUG_BIOSPARAM)
+ if (i91u_debug & debug_biosparam) {
+ printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+ info_array[0], info_array[1], info_array[2]);
+ printk("WARNING: check, if the bios geometry is correct.\n");
+ }
+#endif
+
+ return 0;
+}
+
+static void i91u_unmap_cmnd(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd)
+{
+ /* auto sense buffer */
+ if (cmnd->SCp.ptr) {
+ dma_unmap_single(&pci_dev->dev,
+ (dma_addr_t)((unsigned long)cmnd->SCp.ptr),
+ SENSE_SIZE, DMA_FROM_DEVICE);
+ cmnd->SCp.ptr = NULL;
+ }
+
+ /* request buffer */
+ if (cmnd->use_sg) {
+ dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle,
+ sizeof(struct SG_Struc) * TOTAL_SG_ENTRY,
+ DMA_BIDIRECTIONAL);
+
+ dma_unmap_sg(&pci_dev->dev, cmnd->request_buffer,
+ cmnd->use_sg,
+ cmnd->sc_data_direction);
+ } else if (cmnd->request_bufflen) {
+ dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle,
+ cmnd->request_bufflen,
+ cmnd->sc_data_direction);
+ }
+}
+
+/*****************************************************************************
+ Function name : i91uSCBPost
+ Description : This is callback routine be called when tulip finish one
+ SCSI command.
+ Input : pHCB - Pointer to host adapter control block.
+ pSCB - Pointer to SCSI control block.
+ Output : None.
+ Return : None.
+*****************************************************************************/
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb)
+{
+ struct scsi_cmnd *pSRB; /* Pointer to SCSI request block */
+ HCS *pHCB;
+ SCB *pSCB;
+
+ pHCB = (HCS *) pHcb;
+ pSCB = (SCB *) pScb;
+ if ((pSRB = pSCB->SCB_Srb) == 0) {
+ printk("i91uSCBPost: SRB pointer is empty\n");
+
+ tul_release_scb(pHCB, pSCB); /* Release SCB for current channel */
+ return;
+ }
+ switch (pSCB->SCB_HaStat) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error interrupt generated */
+ pSCB->SCB_HaStat = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ pSCB->SCB_HaStat = DID_TIME_OUT;
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ pSCB->SCB_HaStat = DID_RESET;
+ break;
+
+ case 0x1a: /* SCB Aborted. 07/21/98 */
+ pSCB->SCB_HaStat = DID_ABORT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ than was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+ case 0x16: /* Invalid SCB Operation Code. */
+
+ default:
+ printk("ini9100u: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+ pSCB->SCB_HaStat = DID_ERROR; /* Couldn't find any better */
+ break;
+ }
+
+ pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+
+ if (pSRB == NULL) {
+ printk("pSRB is NULL\n");
+ }
+
+ i91u_unmap_cmnd(pHCB->pci_dev, pSRB);
+ pSRB->scsi_done(pSRB); /* Notify system DONE */
+
+ tul_release_scb(pHCB, pSCB); /* Release SCB for current channel */
+}
+
+/*
+ * Release ressources
+ */
+static int i91u_release(struct Scsi_Host *hreg)
+{
+ free_irq(hreg->irq, hreg);
+ release_region(hreg->io_port, 256);
+ return 0;
+}
+MODULE_LICENSE("Dual BSD/GPL");
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "INI9100U",
+ .name = i91u_REVID,
+ .detect = i91u_detect,
+ .release = i91u_release,
+ .queuecommand = i91u_queuecommand,
+// .abort = i91u_abort,
+// .reset = i91u_reset,
+ .eh_bus_reset_handler = i91u_bus_reset,
+ .bios_param = i91u_biosparam,
+ .can_queue = 1,
+ .this_id = 1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
+
diff --git a/drivers/scsi/initio.h b/drivers/scsi/initio.h
new file mode 100644
index 000000000000..df3ed7c1cee3
--- /dev/null
+++ b/drivers/scsi/initio.h
@@ -0,0 +1,739 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************/
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+
+#define ULONG unsigned long
+#define USHORT unsigned short
+#define UCHAR unsigned char
+#define BYTE unsigned char
+#define WORD unsigned short
+#define DWORD unsigned long
+#define UBYTE unsigned char
+#define UWORD unsigned short
+#define UDWORD unsigned long
+#define U32 u32
+
+#define TOTAL_SG_ENTRY 32
+#define MAX_SUPPORTED_ADAPTERS 8
+#define MAX_OFFSET 15
+#define MAX_TARGETS 16
+
+typedef struct {
+ unsigned short base;
+ unsigned short vec;
+} i91u_config;
+
+/***************************************/
+/* Tulip Configuration Register Set */
+/***************************************/
+#define TUL_PVID 0x00 /* Vendor ID */
+#define TUL_PDID 0x02 /* Device ID */
+#define TUL_PCMD 0x04 /* Command */
+#define TUL_PSTUS 0x06 /* Status */
+#define TUL_PRID 0x08 /* Revision number */
+#define TUL_PPI 0x09 /* Programming interface */
+#define TUL_PSC 0x0A /* Sub Class */
+#define TUL_PBC 0x0B /* Base Class */
+#define TUL_PCLS 0x0C /* Cache line size */
+#define TUL_PLTR 0x0D /* Latency timer */
+#define TUL_PHDT 0x0E /* Header type */
+#define TUL_PBIST 0x0F /* BIST */
+#define TUL_PBAD 0x10 /* Base address */
+#define TUL_PBAD1 0x14 /* Base address */
+#define TUL_PBAD2 0x18 /* Base address */
+#define TUL_PBAD3 0x1C /* Base address */
+#define TUL_PBAD4 0x20 /* Base address */
+#define TUL_PBAD5 0x24 /* Base address */
+#define TUL_PRSVD 0x28 /* Reserved */
+#define TUL_PRSVD1 0x2C /* Reserved */
+#define TUL_PRAD 0x30 /* Expansion ROM base address */
+#define TUL_PRSVD2 0x34 /* Reserved */
+#define TUL_PRSVD3 0x38 /* Reserved */
+#define TUL_PINTL 0x3C /* Interrupt line */
+#define TUL_PINTP 0x3D /* Interrupt pin */
+#define TUL_PIGNT 0x3E /* MIN_GNT */
+#define TUL_PMGNT 0x3F /* MAX_GNT */
+
+/************************/
+/* Jasmin Register Set */
+/************************/
+#define TUL_HACFG0 0x40 /* H/A Configuration Register 0 */
+#define TUL_HACFG1 0x41 /* H/A Configuration Register 1 */
+#define TUL_HACFG2 0x42 /* H/A Configuration Register 2 */
+
+#define TUL_SDCFG0 0x44 /* SCSI Device Configuration 0 */
+#define TUL_SDCFG1 0x45 /* SCSI Device Configuration 1 */
+#define TUL_SDCFG2 0x46 /* SCSI Device Configuration 2 */
+#define TUL_SDCFG3 0x47 /* SCSI Device Configuration 3 */
+
+#define TUL_GINTS 0x50 /* Global Interrupt Status Register */
+#define TUL_GIMSK 0x52 /* Global Interrupt MASK Register */
+#define TUL_GCTRL 0x54 /* Global Control Register */
+#define TUL_GCTRL_EEPROM_BIT 0x04
+#define TUL_GCTRL1 0x55 /* Global Control Register */
+#define TUL_DMACFG 0x5B /* DMA configuration */
+#define TUL_NVRAM 0x5D /* Non-volatile RAM port */
+
+#define TUL_SCnt0 0x80 /* 00 R/W Transfer Counter Low */
+#define TUL_SCnt1 0x81 /* 01 R/W Transfer Counter Mid */
+#define TUL_SCnt2 0x82 /* 02 R/W Transfer Count High */
+#define TUL_SFifoCnt 0x83 /* 03 R FIFO counter */
+#define TUL_SIntEnable 0x84 /* 03 W Interrupt enble */
+#define TUL_SInt 0x84 /* 04 R Interrupt Register */
+#define TUL_SCtrl0 0x85 /* 05 W Control 0 */
+#define TUL_SStatus0 0x85 /* 05 R Status 0 */
+#define TUL_SCtrl1 0x86 /* 06 W Control 1 */
+#define TUL_SStatus1 0x86 /* 06 R Status 1 */
+#define TUL_SConfig 0x87 /* 07 W Configuration */
+#define TUL_SStatus2 0x87 /* 07 R Status 2 */
+#define TUL_SPeriod 0x88 /* 08 W Sync. Transfer Period & Offset */
+#define TUL_SOffset 0x88 /* 08 R Offset */
+#define TUL_SScsiId 0x89 /* 09 W SCSI ID */
+#define TUL_SBusId 0x89 /* 09 R SCSI BUS ID */
+#define TUL_STimeOut 0x8A /* 0A W Sel/Resel Time Out Register */
+#define TUL_SIdent 0x8A /* 0A R Identify Message Register */
+#define TUL_SAvail 0x8A /* 0A R Availiable Counter Register */
+#define TUL_SData 0x8B /* 0B R/W SCSI data in/out */
+#define TUL_SFifo 0x8C /* 0C R/W FIFO */
+#define TUL_SSignal 0x90 /* 10 R/W SCSI signal in/out */
+#define TUL_SCmd 0x91 /* 11 R/W Command */
+#define TUL_STest0 0x92 /* 12 R/W Test0 */
+#define TUL_STest1 0x93 /* 13 R/W Test1 */
+#define TUL_SCFG1 0x94 /* 14 R/W Configuration */
+
+#define TUL_XAddH 0xC0 /*DMA Transfer Physical Address */
+#define TUL_XAddW 0xC8 /*DMA Current Transfer Physical Address */
+#define TUL_XCntH 0xD0 /*DMA Transfer Counter */
+#define TUL_XCntW 0xD4 /*DMA Current Transfer Counter */
+#define TUL_XCmd 0xD8 /*DMA Command Register */
+#define TUL_Int 0xDC /*Interrupt Register */
+#define TUL_XStatus 0xDD /*DMA status Register */
+#define TUL_Mask 0xE0 /*Interrupt Mask Register */
+#define TUL_XCtrl 0xE4 /*DMA Control Register */
+#define TUL_XCtrl1 0xE5 /*DMA Control Register 1 */
+#define TUL_XFifo 0xE8 /*DMA FIFO */
+
+#define TUL_WCtrl 0xF7 /*Bus master wait state control */
+#define TUL_DCtrl 0xFB /*DMA delay control */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Command register of Configuration Space Header */
+/*----------------------------------------------------------------------*/
+#define BUSMS 0x04 /* BUS MASTER Enable */
+#define IOSPA 0x01 /* IO Space Enable */
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip SCSI Command register */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_RESEL 0x80 /* Enable Reselection */
+#define TSC_CMD_COMP 0x84 /* Command Complete Sequence */
+#define TSC_SEL 0x01 /* Select Without ATN Sequence */
+#define TSC_SEL_ATN 0x11 /* Select With ATN Sequence */
+#define TSC_SEL_ATN_DMA 0x51 /* Select With ATN Sequence with DMA */
+#define TSC_SEL_ATN3 0x31 /* Select With ATN3 Sequence */
+#define TSC_SEL_ATNSTOP 0x12 /* Select With ATN and Stop Sequence */
+#define TSC_SELATNSTOP 0x1E /* Select With ATN and Stop Sequence */
+
+#define TSC_SEL_ATN_DIRECT_IN 0x95 /* Select With ATN Sequence */
+#define TSC_SEL_ATN_DIRECT_OUT 0x15 /* Select With ATN Sequence */
+#define TSC_SEL_ATN3_DIRECT_IN 0xB5 /* Select With ATN3 Sequence */
+#define TSC_SEL_ATN3_DIRECT_OUT 0x35 /* Select With ATN3 Sequence */
+#define TSC_XF_DMA_OUT_DIRECT 0x06 /* DMA Xfer Infomation out */
+#define TSC_XF_DMA_IN_DIRECT 0x86 /* DMA Xfer Infomation in */
+
+#define TSC_XF_DMA_OUT 0x43 /* DMA Xfer Infomation out */
+#define TSC_XF_DMA_IN 0xC3 /* DMA Xfer Infomation in */
+#define TSC_XF_FIFO_OUT 0x03 /* FIFO Xfer Infomation out */
+#define TSC_XF_FIFO_IN 0x83 /* FIFO Xfer Infomation in */
+
+#define TSC_MSG_ACCEPT 0x0F /* Message Accept */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 0 Register */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_SEQ 0x20 /* Reset sequence counter */
+#define TSC_FLUSH_FIFO 0x10 /* Flush FIFO */
+#define TSC_ABT_CMD 0x04 /* Abort command (sequence) */
+#define TSC_RST_CHIP 0x02 /* Reset SCSI Chip */
+#define TSC_RST_BUS 0x01 /* Reset SCSI Bus */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 1 Register */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_SCAM 0x80 /* Enable SCAM */
+#define TSC_TIMER 0x40 /* Select timeout unit */
+#define TSC_EN_SCSI2 0x20 /* SCSI-2 mode */
+#define TSC_PWDN 0x10 /* Power down mode */
+#define TSC_WIDE_CPU 0x08 /* Wide CPU */
+#define TSC_HW_RESELECT 0x04 /* Enable HW reselect */
+#define TSC_EN_BUS_OUT 0x02 /* Enable SCSI data bus out latch */
+#define TSC_EN_BUS_IN 0x01 /* Enable SCSI data bus in latch */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Configuration Register */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_LATCH 0x80 /* Enable phase latch */
+#define TSC_INITIATOR 0x40 /* Initiator mode */
+#define TSC_EN_SCSI_PAR 0x20 /* Enable SCSI parity */
+#define TSC_DMA_8BIT 0x10 /* Alternate dma 8-bits mode */
+#define TSC_DMA_16BIT 0x08 /* Alternate dma 16-bits mode */
+#define TSC_EN_WDACK 0x04 /* Enable DACK while wide SCSI xfer */
+#define TSC_ALT_PERIOD 0x02 /* Alternate sync period mode */
+#define TSC_DIS_SCSIRST 0x01 /* Disable SCSI bus reset us */
+
+#define TSC_INITDEFAULT (TSC_INITIATOR | TSC_EN_LATCH | TSC_ALT_PERIOD | TSC_DIS_SCSIRST)
+
+#define TSC_WIDE_SCSI 0x80 /* Enable Wide SCSI */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI signal Register */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_ACK 0x00 /* Release ACK signal */
+#define TSC_RST_ATN 0x00 /* Release ATN signal */
+#define TSC_RST_BSY 0x00 /* Release BSY signal */
+
+#define TSC_SET_ACK 0x40 /* ACK signal */
+#define TSC_SET_ATN 0x08 /* ATN signal */
+
+#define TSC_REQI 0x80 /* REQ signal */
+#define TSC_ACKI 0x40 /* ACK signal */
+#define TSC_BSYI 0x20 /* BSY signal */
+#define TSC_SELI 0x10 /* SEL signal */
+#define TSC_ATNI 0x08 /* ATN signal */
+#define TSC_MSGI 0x04 /* MSG signal */
+#define TSC_CDI 0x02 /* C/D signal */
+#define TSC_IOI 0x01 /* I/O signal */
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 0 Register */
+/*----------------------------------------------------------------------*/
+#define TSS_INT_PENDING 0x80 /* Interrupt pending */
+#define TSS_SEQ_ACTIVE 0x40 /* Sequencer active */
+#define TSS_XFER_CNT 0x20 /* Transfer counter zero */
+#define TSS_FIFO_EMPTY 0x10 /* FIFO empty */
+#define TSS_PAR_ERROR 0x08 /* SCSI parity error */
+#define TSS_PH_MASK 0x07 /* SCSI phase mask */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 1 Register */
+/*----------------------------------------------------------------------*/
+#define TSS_STATUS_RCV 0x08 /* Status received */
+#define TSS_MSG_SEND 0x40 /* Message sent */
+#define TSS_CMD_PH_CMP 0x20 /* command phase done */
+#define TSS_DATA_PH_CMP 0x10 /* Data phase done */
+#define TSS_STATUS_SEND 0x08 /* Status sent */
+#define TSS_XFER_CMP 0x04 /* Transfer completed */
+#define TSS_SEL_CMP 0x02 /* Selection completed */
+#define TSS_ARB_CMP 0x01 /* Arbitration completed */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 2 Register */
+/*----------------------------------------------------------------------*/
+#define TSS_CMD_ABTED 0x80 /* Command aborted */
+#define TSS_OFFSET_0 0x40 /* Offset counter zero */
+#define TSS_FIFO_FULL 0x20 /* FIFO full */
+#define TSS_TIMEOUT_0 0x10 /* Timeout counter zero */
+#define TSS_BUSY_RLS 0x08 /* Busy release */
+#define TSS_PH_MISMATCH 0x04 /* Phase mismatch */
+#define TSS_SCSI_BUS_EN 0x02 /* SCSI data bus enable */
+#define TSS_SCSIRST 0x01 /* SCSI bus reset in progress */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Interrupt Register */
+/*----------------------------------------------------------------------*/
+#define TSS_RESEL_INT 0x80 /* Reselected interrupt */
+#define TSS_SEL_TIMEOUT 0x40 /* Selected/reselected timeout */
+#define TSS_BUS_SERV 0x20
+#define TSS_SCSIRST_INT 0x10 /* SCSI bus reset detected */
+#define TSS_DISC_INT 0x08 /* Disconnected interrupt */
+#define TSS_SEL_INT 0x04 /* Select interrupt */
+#define TSS_SCAM_SEL 0x02 /* SCAM selected */
+#define TSS_FUNC_COMP 0x01
+
+/*----------------------------------------------------------------------*/
+/* SCSI Phase Codes. */
+/*----------------------------------------------------------------------*/
+#define DATA_OUT 0
+#define DATA_IN 1 /* 4 */
+#define CMD_OUT 2
+#define STATUS_IN 3 /* 6 */
+#define MSG_OUT 6 /* 3 */
+#define MSG_IN 7
+
+
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip xfer Command register */
+/*----------------------------------------------------------------------*/
+#define TAX_X_FORC 0x02
+#define TAX_X_ABT 0x04
+#define TAX_X_CLR_FIFO 0x08
+
+#define TAX_X_IN 0x21
+#define TAX_X_OUT 0x01
+#define TAX_SG_IN 0xA1
+#define TAX_SG_OUT 0x81
+
+/*----------------------------------------------------------------------*/
+/* Tulip Interrupt Register */
+/*----------------------------------------------------------------------*/
+#define XCMP 0x01
+#define FCMP 0x02
+#define XABT 0x04
+#define XERR 0x08
+#define SCMP 0x10
+#define IPEND 0x80
+
+/*----------------------------------------------------------------------*/
+/* Tulip DMA Status Register */
+/*----------------------------------------------------------------------*/
+#define XPEND 0x01 /* Transfer pending */
+#define FEMPTY 0x02 /* FIFO empty */
+
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_GCTRL */
+/*----------------------------------------------------------------------*/
+#define EXTSG 0x80
+#define EXTAD 0x60
+#define SEG4K 0x08
+#define EEPRG 0x04
+#define MRMUL 0x02
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_NVRAM */
+/*----------------------------------------------------------------------*/
+#define SE2CS 0x08
+#define SE2CLK 0x04
+#define SE2DO 0x02
+#define SE2DI 0x01
+
+
+/************************************************************************/
+/* Scatter-Gather Element Structure */
+/************************************************************************/
+typedef struct SG_Struc {
+ U32 SG_Ptr; /* Data Pointer */
+ U32 SG_Len; /* Data Length */
+} SG;
+
+/***********************************************************************
+ SCSI Control Block
+************************************************************************/
+typedef struct Scsi_Ctrl_Blk {
+ struct Scsi_Ctrl_Blk *SCB_NxtScb;
+ UBYTE SCB_Status; /*4 */
+ UBYTE SCB_NxtStat; /*5 */
+ UBYTE SCB_Mode; /*6 */
+ UBYTE SCB_Msgin; /*7 SCB_Res0 */
+ UWORD SCB_SGIdx; /*8 */
+ UWORD SCB_SGMax; /*A */
+#ifdef ALPHA
+ U32 SCB_Reserved[2]; /*C */
+#else
+ U32 SCB_Reserved[3]; /*C */
+#endif
+
+ U32 SCB_XferLen; /*18 Current xfer len */
+ U32 SCB_TotXLen; /*1C Total xfer len */
+ U32 SCB_PAddr; /*20 SCB phy. Addr. */
+
+ UBYTE SCB_Opcode; /*24 SCB command code */
+ UBYTE SCB_Flags; /*25 SCB Flags */
+ UBYTE SCB_Target; /*26 Target Id */
+ UBYTE SCB_Lun; /*27 Lun */
+ U32 SCB_BufPtr; /*28 Data Buffer Pointer */
+ U32 SCB_BufLen; /*2C Data Allocation Length */
+ UBYTE SCB_SGLen; /*30 SG list # */
+ UBYTE SCB_SenseLen; /*31 Sense Allocation Length */
+ UBYTE SCB_HaStat; /*32 */
+ UBYTE SCB_TaStat; /*33 */
+ UBYTE SCB_CDBLen; /*34 CDB Length */
+ UBYTE SCB_Ident; /*35 Identify */
+ UBYTE SCB_TagMsg; /*36 Tag Message */
+ UBYTE SCB_TagId; /*37 Queue Tag */
+ UBYTE SCB_CDB[12]; /*38 */
+ U32 SCB_SGPAddr; /*44 SG List/Sense Buf phy. Addr. */
+ U32 SCB_SensePtr; /*48 Sense data pointer */
+ void (*SCB_Post) (BYTE *, BYTE *); /*4C POST routine */
+ struct scsi_cmnd *SCB_Srb; /*50 SRB Pointer */
+ SG SCB_SGList[TOTAL_SG_ENTRY]; /*54 Start of SG list */
+} SCB;
+
+/* Bit Definition for SCB_Status */
+#define SCB_RENT 0x01
+#define SCB_PEND 0x02
+#define SCB_CONTIG 0x04 /* Contigent Allegiance */
+#define SCB_SELECT 0x08
+#define SCB_BUSY 0x10
+#define SCB_DONE 0x20
+
+
+/* Opcodes of SCB_Opcode */
+#define ExecSCSI 0x1
+#define BusDevRst 0x2
+#define AbortCmd 0x3
+
+
+/* Bit Definition for SCB_Mode */
+#define SCM_RSENS 0x01 /* request sense mode */
+
+
+/* Bit Definition for SCB_Flags */
+#define SCF_DONE 0x01
+#define SCF_POST 0x02
+#define SCF_SENSE 0x04
+#define SCF_DIR 0x18
+#define SCF_NO_DCHK 0x00
+#define SCF_DIN 0x08
+#define SCF_DOUT 0x10
+#define SCF_NO_XF 0x18
+#define SCF_WR_VF 0x20 /* Write verify turn on */
+#define SCF_POLL 0x40
+#define SCF_SG 0x80
+
+/* Error Codes for SCB_HaStat */
+#define HOST_SEL_TOUT 0x11
+#define HOST_DO_DU 0x12
+#define HOST_BUS_FREE 0x13
+#define HOST_BAD_PHAS 0x14
+#define HOST_INV_CMD 0x16
+#define HOST_ABORTED 0x1A /* 07/21/98 */
+#define HOST_SCSI_RST 0x1B
+#define HOST_DEV_RST 0x1C
+
+/* Error Codes for SCB_TaStat */
+#define TARGET_CHKCOND 0x02
+#define TARGET_BUSY 0x08
+#define INI_QUEUE_FULL 0x28
+
+/* SCSI MESSAGE */
+#define MSG_COMP 0x00
+#define MSG_EXTEND 0x01
+#define MSG_SDP 0x02
+#define MSG_RESTORE 0x03
+#define MSG_DISC 0x04
+#define MSG_IDE 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJ 0x07
+#define MSG_NOP 0x08
+#define MSG_PARITY 0x09
+#define MSG_LINK_COMP 0x0A
+#define MSG_LINK_FLAG 0x0B
+#define MSG_DEVRST 0x0C
+#define MSG_ABORT_TAG 0x0D
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG 0x20
+#define MSG_HTAG 0x21
+#define MSG_OTAG 0x22
+
+#define MSG_IGNOREWIDE 0x23
+
+#define MSG_IDENT 0x80
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+
+typedef struct Tar_Ctrl_Struc {
+ UWORD TCS_Flags; /* 0 */
+ UBYTE TCS_JS_Period; /* 2 */
+ UBYTE TCS_SConfig0; /* 3 */
+
+ UWORD TCS_DrvFlags; /* 4 */
+ UBYTE TCS_DrvHead; /* 6 */
+ UBYTE TCS_DrvSector; /* 7 */
+} TCS;
+
+/***********************************************************************
+ Target Device Control Structure
+**********************************************************************/
+
+/* Bit Definition for TCF_Flags */
+#define TCF_SCSI_RATE 0x0007
+#define TCF_EN_DISC 0x0008
+#define TCF_NO_SYNC_NEGO 0x0010
+#define TCF_NO_WDTR 0x0020
+#define TCF_EN_255 0x0040
+#define TCF_EN_START 0x0080
+#define TCF_WDTR_DONE 0x0100
+#define TCF_SYNC_DONE 0x0200
+#define TCF_BUSY 0x0400
+
+
+/* Bit Definition for TCF_DrvFlags */
+#define TCF_DRV_BUSY 0x01 /* Indicate target busy(driver) */
+#define TCF_DRV_EN_TAG 0x0800
+#define TCF_DRV_255_63 0x0400
+
+typedef struct I91u_Adpt_Struc {
+ UWORD ADPT_BIOS; /* 0 */
+ UWORD ADPT_BASE; /* 1 */
+ UBYTE ADPT_Bus; /* 2 */
+ UBYTE ADPT_Device; /* 3 */
+ UBYTE ADPT_INTR; /* 4 */
+} INI_ADPT_STRUCT;
+
+
+/***********************************************************************
+ Host Adapter Control Structure
+************************************************************************/
+typedef struct Ha_Ctrl_Struc {
+ UWORD HCS_Base; /* 00 */
+ UWORD HCS_BIOS; /* 02 */
+ UBYTE HCS_Intr; /* 04 */
+ UBYTE HCS_SCSI_ID; /* 05 */
+ UBYTE HCS_MaxTar; /* 06 */
+ UBYTE HCS_NumScbs; /* 07 */
+
+ UBYTE HCS_Flags; /* 08 */
+ UBYTE HCS_Index; /* 09 */
+ UBYTE HCS_HaId; /* 0A */
+ UBYTE HCS_Config; /* 0B */
+ UWORD HCS_IdMask; /* 0C */
+ UBYTE HCS_Semaph; /* 0E */
+ UBYTE HCS_Phase; /* 0F */
+ UBYTE HCS_JSStatus0; /* 10 */
+ UBYTE HCS_JSInt; /* 11 */
+ UBYTE HCS_JSStatus1; /* 12 */
+ UBYTE HCS_SConf1; /* 13 */
+
+ UBYTE HCS_Msg[8]; /* 14 */
+ SCB *HCS_NxtAvail; /* 1C */
+ SCB *HCS_Scb; /* 20 */
+ SCB *HCS_ScbEnd; /* 24 */
+ SCB *HCS_NxtPend; /* 28 */
+ SCB *HCS_NxtContig; /* 2C */
+ SCB *HCS_ActScb; /* 30 */
+ TCS *HCS_ActTcs; /* 34 */
+
+ SCB *HCS_FirstAvail; /* 38 */
+ SCB *HCS_LastAvail; /* 3C */
+ SCB *HCS_FirstPend; /* 40 */
+ SCB *HCS_LastPend; /* 44 */
+ SCB *HCS_FirstBusy; /* 48 */
+ SCB *HCS_LastBusy; /* 4C */
+ SCB *HCS_FirstDone; /* 50 */
+ SCB *HCS_LastDone; /* 54 */
+ UBYTE HCS_MaxTags[16]; /* 58 */
+ UBYTE HCS_ActTags[16]; /* 68 */
+ TCS HCS_Tcs[MAX_TARGETS]; /* 78 */
+ spinlock_t HCS_AvailLock;
+ spinlock_t HCS_SemaphLock;
+ struct pci_dev *pci_dev;
+} HCS;
+
+/* Bit Definition for HCB_Config */
+#define HCC_SCSI_RESET 0x01
+#define HCC_EN_PAR 0x02
+#define HCC_ACT_TERM1 0x04
+#define HCC_ACT_TERM2 0x08
+#define HCC_AUTO_TERM 0x10
+#define HCC_EN_PWR 0x80
+
+/* Bit Definition for HCB_Flags */
+#define HCF_EXPECT_DISC 0x01
+#define HCF_EXPECT_SELECT 0x02
+#define HCF_EXPECT_RESET 0x10
+#define HCF_EXPECT_DONE_DISC 0x20
+
+/******************************************************************
+ Serial EEProm
+*******************************************************************/
+
+typedef struct _NVRAM_SCSI { /* SCSI channel configuration */
+ UCHAR NVM_ChSCSIID; /* 0Ch -> Channel SCSI ID */
+ UCHAR NVM_ChConfig1; /* 0Dh -> Channel config 1 */
+ UCHAR NVM_ChConfig2; /* 0Eh -> Channel config 2 */
+ UCHAR NVM_NumOfTarg; /* 0Fh -> Number of SCSI target */
+ /* SCSI target configuration */
+ UCHAR NVM_Targ0Config; /* 10h -> Target 0 configuration */
+ UCHAR NVM_Targ1Config; /* 11h -> Target 1 configuration */
+ UCHAR NVM_Targ2Config; /* 12h -> Target 2 configuration */
+ UCHAR NVM_Targ3Config; /* 13h -> Target 3 configuration */
+ UCHAR NVM_Targ4Config; /* 14h -> Target 4 configuration */
+ UCHAR NVM_Targ5Config; /* 15h -> Target 5 configuration */
+ UCHAR NVM_Targ6Config; /* 16h -> Target 6 configuration */
+ UCHAR NVM_Targ7Config; /* 17h -> Target 7 configuration */
+ UCHAR NVM_Targ8Config; /* 18h -> Target 8 configuration */
+ UCHAR NVM_Targ9Config; /* 19h -> Target 9 configuration */
+ UCHAR NVM_TargAConfig; /* 1Ah -> Target A configuration */
+ UCHAR NVM_TargBConfig; /* 1Bh -> Target B configuration */
+ UCHAR NVM_TargCConfig; /* 1Ch -> Target C configuration */
+ UCHAR NVM_TargDConfig; /* 1Dh -> Target D configuration */
+ UCHAR NVM_TargEConfig; /* 1Eh -> Target E configuration */
+ UCHAR NVM_TargFConfig; /* 1Fh -> Target F configuration */
+} NVRAM_SCSI;
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+ USHORT NVM_Signature; /* 0,1: Signature */
+ UCHAR NVM_Size; /* 2: Size of data structure */
+ UCHAR NVM_Revision; /* 3: Revision of data structure */
+ /* ----Host Adapter Structure ---- */
+ UCHAR NVM_ModelByte0; /* 4: Model number (byte 0) */
+ UCHAR NVM_ModelByte1; /* 5: Model number (byte 1) */
+ UCHAR NVM_ModelInfo; /* 6: Model information */
+ UCHAR NVM_NumOfCh; /* 7: Number of SCSI channel */
+ UCHAR NVM_BIOSConfig1; /* 8: BIOS configuration 1 */
+ UCHAR NVM_BIOSConfig2; /* 9: BIOS configuration 2 */
+ UCHAR NVM_HAConfig1; /* A: Hoat adapter configuration 1 */
+ UCHAR NVM_HAConfig2; /* B: Hoat adapter configuration 2 */
+ NVRAM_SCSI NVM_SCSIInfo[2];
+ UCHAR NVM_reserved[10];
+ /* ---------- CheckSum ---------- */
+ USHORT NVM_CheckSum; /* 0x3E, 0x3F: Checksum of NVRam */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1 */
+#define NBC1_ENABLE 0x01 /* BIOS enable */
+#define NBC1_8DRIVE 0x02 /* Support more than 2 drives */
+#define NBC1_REMOVABLE 0x04 /* Support removable drive */
+#define NBC1_INT19 0x08 /* Intercept int 19h */
+#define NBC1_BIOSSCAN 0x10 /* Dynamic BIOS scan */
+#define NBC1_LUNSUPPORT 0x40 /* Support LUN */
+
+/* HA Configuration Byte 1 */
+#define NHC1_BOOTIDMASK 0x0F /* Boot ID number */
+#define NHC1_LUNMASK 0x70 /* Boot LUN number */
+#define NHC1_CHANMASK 0x80 /* Boot Channel number */
+
+/* Bit definition for nvram->SCSIconfig1 */
+#define NCC1_BUSRESET 0x01 /* Reset SCSI bus at power up */
+#define NCC1_PARITYCHK 0x02 /* SCSI parity enable */
+#define NCC1_ACTTERM1 0x04 /* Enable active terminator 1 */
+#define NCC1_ACTTERM2 0x08 /* Enable active terminator 2 */
+#define NCC1_AUTOTERM 0x10 /* Enable auto terminator */
+#define NCC1_PWRMGR 0x80 /* Enable power management */
+
+/* Bit definition for SCSI Target configuration byte */
+#define NTC_DISCONNECT 0x08 /* Enable SCSI disconnect */
+#define NTC_SYNC 0x10 /* SYNC_NEGO */
+#define NTC_NO_WDTR 0x20 /* SYNC_NEGO */
+#define NTC_1GIGA 0x40 /* 255 head / 63 sectors (64/32) */
+#define NTC_SPINUP 0x80 /* Start disk drive */
+
+/* Default NVRam values */
+#define INI_SIGNATURE 0xC925
+#define NBC1_DEFAULT (NBC1_ENABLE)
+#define NCC1_DEFAULT (NCC1_BUSRESET | NCC1_AUTOTERM | NCC1_PARITYCHK)
+#define NTC_DEFAULT (NTC_NO_WDTR | NTC_1GIGA | NTC_DISCONNECT)
+
+/* SCSI related definition */
+#define DISC_NOT_ALLOW 0x80 /* Disconnect is not allowed */
+#define DISC_ALLOW 0xC0 /* Disconnect is allowed */
+#define SCSICMD_RequestSense 0x03
+
+typedef struct _HCSinfo {
+ ULONG base;
+ UCHAR vec;
+ UCHAR bios; /* High byte of BIOS address */
+ USHORT BaseAndBios; /* high byte: pHcsInfo->bios,low byte:pHcsInfo->base */
+} HCSINFO;
+
+#define TUL_RD(x,y) (UCHAR)(inb( (int)((ULONG)(x+y)) ))
+#define TUL_RDLONG(x,y) (ULONG)(inl((int)((ULONG)(x+y)) ))
+#define TUL_WR( adr,data) outb( (UCHAR)(data), (int)(adr))
+#define TUL_WRSHORT(adr,data) outw( (UWORD)(data), (int)(adr))
+#define TUL_WRLONG( adr,data) outl( (ULONG)(data), (int)(adr))
+
+#define SCSI_ABORT_SNOOZE 0
+#define SCSI_ABORT_SUCCESS 1
+#define SCSI_ABORT_PENDING 2
+#define SCSI_ABORT_BUSY 3
+#define SCSI_ABORT_NOT_RUNNING 4
+#define SCSI_ABORT_ERROR 5
+
+#define SCSI_RESET_SNOOZE 0
+#define SCSI_RESET_PUNT 1
+#define SCSI_RESET_SUCCESS 2
+#define SCSI_RESET_PENDING 3
+#define SCSI_RESET_WAKEUP 4
+#define SCSI_RESET_NOT_RUNNING 5
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS 0x01
+#define SCSI_RESET_ASYNCHRONOUS 0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
+
+#define SCSI_RESET_BUS_RESET 0x100
+#define SCSI_RESET_HOST_RESET 0x200
+#define SCSI_RESET_ACTION 0xff
+
+extern void init_i91uAdapter_table(void);
+extern int Addi91u_into_Adapter_table(WORD, WORD, BYTE, BYTE, BYTE);
+extern int tul_ReturnNumberOfAdapters(void);
+extern void get_tulipPCIConfig(HCS * pHCB, int iChannel_index);
+extern int init_tulip(HCS * pHCB, SCB * pSCB, int tul_num_scb, BYTE * pbBiosAdr, int reset_time);
+extern SCB *tul_alloc_scb(HCS * pHCB);
+extern int tul_abort_srb(HCS * pHCB, struct scsi_cmnd * pSRB);
+extern void tul_exec_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_release_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_stop_bm(HCS * pHCB);
+extern int tul_reset_scsi(HCS * pCurHcb, int seconds);
+extern int tul_isr(HCS * pHCB);
+extern int tul_reset(HCS * pHCB, struct scsi_cmnd * pSRB, unsigned char target);
+extern int tul_reset_scsi_bus(HCS * pCurHcb);
+extern int tul_device_reset(HCS * pCurHcb, struct scsi_cmnd *pSrb,
+ unsigned int target, unsigned int ResetFlags);
+ /* ---- EXTERNAL VARIABLES ---- */
+extern HCS tul_hcs[];
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
new file mode 100644
index 000000000000..5441531c0d8e
--- /dev/null
+++ b/drivers/scsi/ipr.c
@@ -0,0 +1,6083 @@
+/*
+ * ipr.c -- driver for IBM Power Linux RAID adapters
+ *
+ * Written By: Brian King <brking@us.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) 2003, 2004 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Notes:
+ *
+ * This driver is used to control the following SCSI adapters:
+ *
+ * IBM iSeries: 5702, 5703, 2780, 5709, 570A, 570B
+ *
+ * IBM pSeries: PCI-X Dual Channel Ultra 320 SCSI RAID Adapter
+ * PCI-X Dual Channel Ultra 320 SCSI Adapter
+ * PCI-X Dual Channel Ultra 320 SCSI RAID Enablement Card
+ * Embedded SCSI adapter on p615 and p655 systems
+ *
+ * Supported Hardware Features:
+ * - Ultra 320 SCSI controller
+ * - PCI-X host interface
+ * - Embedded PowerPC RISC Processor and Hardware XOR DMA Engine
+ * - Non-Volatile Write Cache
+ * - Supports attachment of non-RAID disks, tape, and optical devices
+ * - RAID Levels 0, 5, 10
+ * - Hot spare
+ * - Background Parity Checking
+ * - Background Data Scrubbing
+ * - Ability to increase the capacity of an existing RAID 5 disk array
+ * by adding disks
+ *
+ * Driver Features:
+ * - Tagged command queuing
+ * - Adapter microcode download
+ * - PCI hot plug
+ * - SCSI device hot plug
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/processor.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_request.h>
+#include "ipr.h"
+
+/*
+ * Global Data
+ */
+static struct list_head ipr_ioa_head = LIST_HEAD_INIT(ipr_ioa_head);
+static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL;
+static unsigned int ipr_max_speed = 1;
+static int ipr_testmode = 0;
+static unsigned int ipr_fastfail = 0;
+static unsigned int ipr_transop_timeout = IPR_OPERATIONAL_TIMEOUT;
+static DEFINE_SPINLOCK(ipr_driver_lock);
+
+/* This table describes the differences between DMA controller chips */
+static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
+ { /* Gemstone and Citrine */
+ .mailbox = 0x0042C,
+ .cache_line_size = 0x20,
+ {
+ .set_interrupt_mask_reg = 0x0022C,
+ .clr_interrupt_mask_reg = 0x00230,
+ .sense_interrupt_mask_reg = 0x0022C,
+ .clr_interrupt_reg = 0x00228,
+ .sense_interrupt_reg = 0x00224,
+ .ioarrin_reg = 0x00404,
+ .sense_uproc_interrupt_reg = 0x00214,
+ .set_uproc_interrupt_reg = 0x00214,
+ .clr_uproc_interrupt_reg = 0x00218
+ }
+ },
+ { /* Snipe and Scamp */
+ .mailbox = 0x0052C,
+ .cache_line_size = 0x20,
+ {
+ .set_interrupt_mask_reg = 0x00288,
+ .clr_interrupt_mask_reg = 0x0028C,
+ .sense_interrupt_mask_reg = 0x00288,
+ .clr_interrupt_reg = 0x00284,
+ .sense_interrupt_reg = 0x00280,
+ .ioarrin_reg = 0x00504,
+ .sense_uproc_interrupt_reg = 0x00290,
+ .set_uproc_interrupt_reg = 0x00290,
+ .clr_uproc_interrupt_reg = 0x00294
+ }
+ },
+};
+
+static const struct ipr_chip_t ipr_chip[] = {
+ { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, &ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, &ipr_chip_cfg[1] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, &ipr_chip_cfg[1] }
+};
+
+static int ipr_max_bus_speeds [] = {
+ IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
+};
+
+MODULE_AUTHOR("Brian King <brking@us.ibm.com>");
+MODULE_DESCRIPTION("IBM Power RAID SCSI Adapter Driver");
+module_param_named(max_speed, ipr_max_speed, uint, 0);
+MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320");
+module_param_named(log_level, ipr_log_level, uint, 0);
+MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver");
+module_param_named(testmode, ipr_testmode, int, 0);
+MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations");
+module_param_named(fastfail, ipr_fastfail, int, 0);
+MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries");
+module_param_named(transop_timeout, ipr_transop_timeout, int, 0);
+MODULE_PARM_DESC(transop_timeout, "Time in seconds to wait for adapter to come operational (default: 300)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(IPR_DRIVER_VERSION);
+
+static const char *ipr_gpdd_dev_end_states[] = {
+ "Command complete",
+ "Terminated by host",
+ "Terminated by device reset",
+ "Terminated by bus reset",
+ "Unknown",
+ "Command not started"
+};
+
+static const char *ipr_gpdd_dev_bus_phases[] = {
+ "Bus free",
+ "Arbitration",
+ "Selection",
+ "Message out",
+ "Command",
+ "Message in",
+ "Data out",
+ "Data in",
+ "Status",
+ "Reselection",
+ "Unknown"
+};
+
+/* A constant array of IOASCs/URCs/Error Messages */
+static const
+struct ipr_error_table_t ipr_error_table[] = {
+ {0x00000000, 1, 1,
+ "8155: An unknown error was received"},
+ {0x00330000, 0, 0,
+ "Soft underlength error"},
+ {0x005A0000, 0, 0,
+ "Command to be cancelled not found"},
+ {0x00808000, 0, 0,
+ "Qualified success"},
+ {0x01080000, 1, 1,
+ "FFFE: Soft device bus error recovered by the IOA"},
+ {0x01170600, 0, 1,
+ "FFF9: Device sector reassign successful"},
+ {0x01170900, 0, 1,
+ "FFF7: Media error recovered by device rewrite procedures"},
+ {0x01180200, 0, 1,
+ "7001: IOA sector reassignment successful"},
+ {0x01180500, 0, 1,
+ "FFF9: Soft media error. Sector reassignment recommended"},
+ {0x01180600, 0, 1,
+ "FFF7: Media error recovered by IOA rewrite procedures"},
+ {0x01418000, 0, 1,
+ "FF3D: Soft PCI bus error recovered by the IOA"},
+ {0x01440000, 1, 1,
+ "FFF6: Device hardware error recovered by the IOA"},
+ {0x01448100, 0, 1,
+ "FFF6: Device hardware error recovered by the device"},
+ {0x01448200, 1, 1,
+ "FF3D: Soft IOA error recovered by the IOA"},
+ {0x01448300, 0, 1,
+ "FFFA: Undefined device response recovered by the IOA"},
+ {0x014A0000, 1, 1,
+ "FFF6: Device bus error, message or command phase"},
+ {0x015D0000, 0, 1,
+ "FFF6: Failure prediction threshold exceeded"},
+ {0x015D9200, 0, 1,
+ "8009: Impending cache battery pack failure"},
+ {0x02040400, 0, 0,
+ "34FF: Disk device format in progress"},
+ {0x023F0000, 0, 0,
+ "Synchronization required"},
+ {0x024E0000, 0, 0,
+ "No ready, IOA shutdown"},
+ {0x025A0000, 0, 0,
+ "Not ready, IOA has been shutdown"},
+ {0x02670100, 0, 1,
+ "3020: Storage subsystem configuration error"},
+ {0x03110B00, 0, 0,
+ "FFF5: Medium error, data unreadable, recommend reassign"},
+ {0x03110C00, 0, 0,
+ "7000: Medium error, data unreadable, do not reassign"},
+ {0x03310000, 0, 1,
+ "FFF3: Disk media format bad"},
+ {0x04050000, 0, 1,
+ "3002: Addressed device failed to respond to selection"},
+ {0x04080000, 1, 1,
+ "3100: Device bus error"},
+ {0x04080100, 0, 1,
+ "3109: IOA timed out a device command"},
+ {0x04088000, 0, 0,
+ "3120: SCSI bus is not operational"},
+ {0x04118000, 0, 1,
+ "9000: IOA reserved area data check"},
+ {0x04118100, 0, 1,
+ "9001: IOA reserved area invalid data pattern"},
+ {0x04118200, 0, 1,
+ "9002: IOA reserved area LRC error"},
+ {0x04320000, 0, 1,
+ "102E: Out of alternate sectors for disk storage"},
+ {0x04330000, 1, 1,
+ "FFF4: Data transfer underlength error"},
+ {0x04338000, 1, 1,
+ "FFF4: Data transfer overlength error"},
+ {0x043E0100, 0, 1,
+ "3400: Logical unit failure"},
+ {0x04408500, 0, 1,
+ "FFF4: Device microcode is corrupt"},
+ {0x04418000, 1, 1,
+ "8150: PCI bus error"},
+ {0x04430000, 1, 0,
+ "Unsupported device bus message received"},
+ {0x04440000, 1, 1,
+ "FFF4: Disk device problem"},
+ {0x04448200, 1, 1,
+ "8150: Permanent IOA failure"},
+ {0x04448300, 0, 1,
+ "3010: Disk device returned wrong response to IOA"},
+ {0x04448400, 0, 1,
+ "8151: IOA microcode error"},
+ {0x04448500, 0, 0,
+ "Device bus status error"},
+ {0x04448600, 0, 1,
+ "8157: IOA error requiring IOA reset to recover"},
+ {0x04490000, 0, 0,
+ "Message reject received from the device"},
+ {0x04449200, 0, 1,
+ "8008: A permanent cache battery pack failure occurred"},
+ {0x0444A000, 0, 1,
+ "9090: Disk unit has been modified after the last known status"},
+ {0x0444A200, 0, 1,
+ "9081: IOA detected device error"},
+ {0x0444A300, 0, 1,
+ "9082: IOA detected device error"},
+ {0x044A0000, 1, 1,
+ "3110: Device bus error, message or command phase"},
+ {0x04670400, 0, 1,
+ "9091: Incorrect hardware configuration change has been detected"},
+ {0x046E0000, 0, 1,
+ "FFF4: Command to logical unit failed"},
+ {0x05240000, 1, 0,
+ "Illegal request, invalid request type or request packet"},
+ {0x05250000, 0, 0,
+ "Illegal request, invalid resource handle"},
+ {0x05260000, 0, 0,
+ "Illegal request, invalid field in parameter list"},
+ {0x05260100, 0, 0,
+ "Illegal request, parameter not supported"},
+ {0x05260200, 0, 0,
+ "Illegal request, parameter value invalid"},
+ {0x052C0000, 0, 0,
+ "Illegal request, command sequence error"},
+ {0x06040500, 0, 1,
+ "9031: Array protection temporarily suspended, protection resuming"},
+ {0x06040600, 0, 1,
+ "9040: Array protection temporarily suspended, protection resuming"},
+ {0x06290000, 0, 1,
+ "FFFB: SCSI bus was reset"},
+ {0x06290500, 0, 0,
+ "FFFE: SCSI bus transition to single ended"},
+ {0x06290600, 0, 0,
+ "FFFE: SCSI bus transition to LVD"},
+ {0x06298000, 0, 1,
+ "FFFB: SCSI bus was reset by another initiator"},
+ {0x063F0300, 0, 1,
+ "3029: A device replacement has occurred"},
+ {0x064C8000, 0, 1,
+ "9051: IOA cache data exists for a missing or failed device"},
+ {0x06670100, 0, 1,
+ "9025: Disk unit is not supported at its physical location"},
+ {0x06670600, 0, 1,
+ "3020: IOA detected a SCSI bus configuration error"},
+ {0x06678000, 0, 1,
+ "3150: SCSI bus configuration error"},
+ {0x06690200, 0, 1,
+ "9041: Array protection temporarily suspended"},
+ {0x06698200, 0, 1,
+ "9042: Corrupt array parity detected on specified device"},
+ {0x066B0200, 0, 1,
+ "9030: Array no longer protected due to missing or failed disk unit"},
+ {0x066B8200, 0, 1,
+ "9032: Array exposed but still protected"},
+ {0x07270000, 0, 0,
+ "Failure due to other device"},
+ {0x07278000, 0, 1,
+ "9008: IOA does not support functions expected by devices"},
+ {0x07278100, 0, 1,
+ "9010: Cache data associated with attached devices cannot be found"},
+ {0x07278200, 0, 1,
+ "9011: Cache data belongs to devices other than those attached"},
+ {0x07278400, 0, 1,
+ "9020: Array missing 2 or more devices with only 1 device present"},
+ {0x07278500, 0, 1,
+ "9021: Array missing 2 or more devices with 2 or more devices present"},
+ {0x07278600, 0, 1,
+ "9022: Exposed array is missing a required device"},
+ {0x07278700, 0, 1,
+ "9023: Array member(s) not at required physical locations"},
+ {0x07278800, 0, 1,
+ "9024: Array not functional due to present hardware configuration"},
+ {0x07278900, 0, 1,
+ "9026: Array not functional due to present hardware configuration"},
+ {0x07278A00, 0, 1,
+ "9027: Array is missing a device and parity is out of sync"},
+ {0x07278B00, 0, 1,
+ "9028: Maximum number of arrays already exist"},
+ {0x07278C00, 0, 1,
+ "9050: Required cache data cannot be located for a disk unit"},
+ {0x07278D00, 0, 1,
+ "9052: Cache data exists for a device that has been modified"},
+ {0x07278F00, 0, 1,
+ "9054: IOA resources not available due to previous problems"},
+ {0x07279100, 0, 1,
+ "9092: Disk unit requires initialization before use"},
+ {0x07279200, 0, 1,
+ "9029: Incorrect hardware configuration change has been detected"},
+ {0x07279600, 0, 1,
+ "9060: One or more disk pairs are missing from an array"},
+ {0x07279700, 0, 1,
+ "9061: One or more disks are missing from an array"},
+ {0x07279800, 0, 1,
+ "9062: One or more disks are missing from an array"},
+ {0x07279900, 0, 1,
+ "9063: Maximum number of functional arrays has been exceeded"},
+ {0x0B260000, 0, 0,
+ "Aborted command, invalid descriptor"},
+ {0x0B5A0000, 0, 0,
+ "Command terminated by host"}
+};
+
+static const struct ipr_ses_table_entry ipr_ses_table[] = {
+ { "2104-DL1 ", "XXXXXXXXXXXXXXXX", 80 },
+ { "2104-TL1 ", "XXXXXXXXXXXXXXXX", 80 },
+ { "HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 7 slot */
+ { "HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 5 slot */
+ { "HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Bowtie */
+ { "HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* MartinFenning */
+ { "2104-DU3 ", "XXXXXXXXXXXXXXXX", 160 },
+ { "2104-TU3 ", "XXXXXXXXXXXXXXXX", 160 },
+ { "HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 },
+ { "HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 },
+ { "St V1S2 ", "XXXXXXXXXXXXXXXX", 160 },
+ { "HSBPD4M PU3SCSI", "XXXXXXX*XXXXXXXX", 160 },
+ { "VSBPD1H U3SCSI", "XXXXXXX*XXXXXXXX", 160 }
+};
+
+/*
+ * Function Prototypes
+ */
+static int ipr_reset_alert(struct ipr_cmnd *);
+static void ipr_process_ccn(struct ipr_cmnd *);
+static void ipr_process_error(struct ipr_cmnd *);
+static void ipr_reset_ioa_job(struct ipr_cmnd *);
+static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *,
+ enum ipr_shutdown_type);
+
+#ifdef CONFIG_SCSI_IPR_TRACE
+/**
+ * ipr_trc_hook - Add a trace entry to the driver trace
+ * @ipr_cmd: ipr command struct
+ * @type: trace type
+ * @add_data: additional data
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
+ u8 type, u32 add_data)
+{
+ struct ipr_trace_entry *trace_entry;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ trace_entry = &ioa_cfg->trace[ioa_cfg->trace_index++];
+ trace_entry->time = jiffies;
+ trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0];
+ trace_entry->type = type;
+ trace_entry->cmd_index = ipr_cmd->cmd_index;
+ trace_entry->res_handle = ipr_cmd->ioarcb.res_handle;
+ trace_entry->u.add_data = add_data;
+}
+#else
+#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0)
+#endif
+
+/**
+ * ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioasa *ioasa = &ipr_cmd->ioasa;
+
+ memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt));
+ ioarcb->write_data_transfer_length = 0;
+ ioarcb->read_data_transfer_length = 0;
+ ioarcb->write_ioadl_len = 0;
+ ioarcb->read_ioadl_len = 0;
+ ioasa->ioasc = 0;
+ ioasa->residual_data_len = 0;
+
+ ipr_cmd->scsi_cmd = NULL;
+ ipr_cmd->sense_buffer[0] = 0;
+ ipr_cmd->dma_use_sg = 0;
+}
+
+/**
+ * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd)
+{
+ ipr_reinit_ipr_cmnd(ipr_cmd);
+ ipr_cmd->u.scratch = 0;
+ ipr_cmd->sibling = NULL;
+ init_timer(&ipr_cmd->timer);
+}
+
+/**
+ * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * pointer to ipr command struct
+ **/
+static
+struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct ipr_cmnd *ipr_cmd;
+
+ ipr_cmd = list_entry(ioa_cfg->free_q.next, struct ipr_cmnd, queue);
+ list_del(&ipr_cmd->queue);
+ ipr_init_ipr_cmnd(ipr_cmd);
+
+ return ipr_cmd;
+}
+
+/**
+ * ipr_unmap_sglist - Unmap scatterlist if mapped
+ * @ioa_cfg: ioa config struct
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_unmap_sglist(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_cmnd *ipr_cmd)
+{
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+
+ if (ipr_cmd->dma_use_sg) {
+ if (scsi_cmd->use_sg > 0) {
+ pci_unmap_sg(ioa_cfg->pdev, scsi_cmd->request_buffer,
+ scsi_cmd->use_sg,
+ scsi_cmd->sc_data_direction);
+ } else {
+ pci_unmap_single(ioa_cfg->pdev, ipr_cmd->dma_handle,
+ scsi_cmd->request_bufflen,
+ scsi_cmd->sc_data_direction);
+ }
+ }
+}
+
+/**
+ * ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts
+ * @ioa_cfg: ioa config struct
+ * @clr_ints: interrupts to clear
+ *
+ * This function masks all interrupts on the adapter, then clears the
+ * interrupts specified in the mask
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
+ u32 clr_ints)
+{
+ volatile u32 int_reg;
+
+ /* Stop new interrupts */
+ ioa_cfg->allow_interrupts = 0;
+
+ /* Set interrupt mask to stop all new interrupts */
+ writel(~0, ioa_cfg->regs.set_interrupt_mask_reg);
+
+ /* Clear any pending interrupts */
+ writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+}
+
+/**
+ * ipr_save_pcix_cmd_reg - Save PCI-X command register
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 on success / -EIO on failure
+ **/
+static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
+
+ if (pcix_cmd_reg == 0) {
+ dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n");
+ return -EIO;
+ }
+
+ if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
+ &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
+ dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n");
+ return -EIO;
+ }
+
+ ioa_cfg->saved_pcix_cmd_reg |= PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO;
+ return 0;
+}
+
+/**
+ * ipr_set_pcix_cmd_reg - Setup PCI-X command register
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 on success / -EIO on failure
+ **/
+static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
+
+ if (pcix_cmd_reg) {
+ if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
+ ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
+ dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n");
+ return -EIO;
+ }
+ } else {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Failed to setup PCI-X command register\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ipr_scsi_eh_done - mid-layer done function for aborted ops
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is invoked by the interrupt handler for
+ * ops generated by the SCSI mid-layer which are being aborted.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+
+ scsi_cmd->result |= (DID_ERROR << 16);
+
+ ipr_unmap_sglist(ioa_cfg, ipr_cmd);
+ scsi_cmd->scsi_done(scsi_cmd);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+}
+
+/**
+ * ipr_fail_all_ops - Fails all outstanding ops.
+ * @ioa_cfg: ioa config struct
+ *
+ * This function fails all outstanding ops.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct ipr_cmnd *ipr_cmd, *temp;
+
+ ENTER;
+ list_for_each_entry_safe(ipr_cmd, temp, &ioa_cfg->pending_q, queue) {
+ list_del(&ipr_cmd->queue);
+
+ ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_IOA_WAS_RESET);
+ ipr_cmd->ioasa.ilid = cpu_to_be32(IPR_DRIVER_ILID);
+
+ if (ipr_cmd->scsi_cmd)
+ ipr_cmd->done = ipr_scsi_eh_done;
+
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET);
+ del_timer(&ipr_cmd->timer);
+ ipr_cmd->done(ipr_cmd);
+ }
+
+ LEAVE;
+}
+
+/**
+ * ipr_do_req - Send driver initiated requests.
+ * @ipr_cmd: ipr command struct
+ * @done: done function
+ * @timeout_func: timeout function
+ * @timeout: timeout value
+ *
+ * This function sends the specified command to the adapter with the
+ * timeout given. The done function is invoked on command completion.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_do_req(struct ipr_cmnd *ipr_cmd,
+ void (*done) (struct ipr_cmnd *),
+ void (*timeout_func) (struct ipr_cmnd *), u32 timeout)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+
+ ipr_cmd->done = done;
+
+ ipr_cmd->timer.data = (unsigned long) ipr_cmd;
+ ipr_cmd->timer.expires = jiffies + timeout;
+ ipr_cmd->timer.function = (void (*)(unsigned long))timeout_func;
+
+ add_timer(&ipr_cmd->timer);
+
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0);
+
+ mb();
+ writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr),
+ ioa_cfg->regs.ioarrin_reg);
+}
+
+/**
+ * ipr_internal_cmd_done - Op done function for an internally generated op.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is the op done function for an internally generated,
+ * blocking op. It simply wakes the sleeping thread.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd)
+{
+ if (ipr_cmd->sibling)
+ ipr_cmd->sibling = NULL;
+ else
+ complete(&ipr_cmd->completion);
+}
+
+/**
+ * ipr_send_blocking_cmd - Send command and sleep on its completion.
+ * @ipr_cmd: ipr command struct
+ * @timeout_func: function to invoke if command times out
+ * @timeout: timeout
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd,
+ void (*timeout_func) (struct ipr_cmnd *ipr_cmd),
+ u32 timeout)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ init_completion(&ipr_cmd->completion);
+ ipr_do_req(ipr_cmd, ipr_internal_cmd_done, timeout_func, timeout);
+
+ spin_unlock_irq(ioa_cfg->host->host_lock);
+ wait_for_completion(&ipr_cmd->completion);
+ spin_lock_irq(ioa_cfg->host->host_lock);
+}
+
+/**
+ * ipr_send_hcam - Send an HCAM to the adapter.
+ * @ioa_cfg: ioa config struct
+ * @type: HCAM type
+ * @hostrcb: hostrcb struct
+ *
+ * This function will send a Host Controlled Async command to the adapter.
+ * If HCAMs are currently not allowed to be issued to the adapter, it will
+ * place the hostrcb on the free queue.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
+ struct ipr_hostrcb *hostrcb)
+{
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioarcb *ioarcb;
+
+ if (ioa_cfg->allow_cmds) {
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+ list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q);
+
+ ipr_cmd->u.hostrcb = hostrcb;
+ ioarcb = &ipr_cmd->ioarcb;
+
+ ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_HCAM;
+ ioarcb->cmd_pkt.cdb[0] = IPR_HOST_CONTROLLED_ASYNC;
+ ioarcb->cmd_pkt.cdb[1] = type;
+ ioarcb->cmd_pkt.cdb[7] = (sizeof(hostrcb->hcam) >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[8] = sizeof(hostrcb->hcam) & 0xff;
+
+ ioarcb->read_data_transfer_length = cpu_to_be32(sizeof(hostrcb->hcam));
+ ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ipr_cmd->ioadl[0].flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | sizeof(hostrcb->hcam));
+ ipr_cmd->ioadl[0].address = cpu_to_be32(hostrcb->hostrcb_dma);
+
+ if (type == IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE)
+ ipr_cmd->done = ipr_process_ccn;
+ else
+ ipr_cmd->done = ipr_process_error;
+
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR);
+
+ mb();
+ writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr),
+ ioa_cfg->regs.ioarrin_reg);
+ } else {
+ list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
+ }
+}
+
+/**
+ * ipr_init_res_entry - Initialize a resource entry struct.
+ * @res: resource entry struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_init_res_entry(struct ipr_resource_entry *res)
+{
+ res->needs_sync_complete = 1;
+ res->in_erp = 0;
+ res->add_to_ml = 0;
+ res->del_from_ml = 0;
+ res->resetting_device = 0;
+ res->sdev = NULL;
+}
+
+/**
+ * ipr_handle_config_change - Handle a config change from the adapter
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ struct ipr_resource_entry *res = NULL;
+ struct ipr_config_table_entry *cfgte;
+ u32 is_ndn = 1;
+
+ cfgte = &hostrcb->hcam.u.ccn.cfgte;
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if (!memcmp(&res->cfgte.res_addr, &cfgte->res_addr,
+ sizeof(cfgte->res_addr))) {
+ is_ndn = 0;
+ break;
+ }
+ }
+
+ if (is_ndn) {
+ if (list_empty(&ioa_cfg->free_res_q)) {
+ ipr_send_hcam(ioa_cfg,
+ IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE,
+ hostrcb);
+ return;
+ }
+
+ res = list_entry(ioa_cfg->free_res_q.next,
+ struct ipr_resource_entry, queue);
+
+ list_del(&res->queue);
+ ipr_init_res_entry(res);
+ list_add_tail(&res->queue, &ioa_cfg->used_res_q);
+ }
+
+ memcpy(&res->cfgte, cfgte, sizeof(struct ipr_config_table_entry));
+
+ if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) {
+ if (res->sdev) {
+ res->sdev->hostdata = NULL;
+ res->del_from_ml = 1;
+ if (ioa_cfg->allow_ml_add_del)
+ schedule_work(&ioa_cfg->work_q);
+ } else
+ list_move_tail(&res->queue, &ioa_cfg->free_res_q);
+ } else if (!res->sdev) {
+ res->add_to_ml = 1;
+ if (ioa_cfg->allow_ml_add_del)
+ schedule_work(&ioa_cfg->work_q);
+ }
+
+ ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
+}
+
+/**
+ * ipr_process_ccn - Op done function for a CCN.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is the op done function for a configuration
+ * change notification host controlled async from the adapter.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ list_del(&hostrcb->queue);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+
+ if (ioasc) {
+ if (ioasc != IPR_IOASC_IOA_WAS_RESET)
+ dev_err(&ioa_cfg->pdev->dev,
+ "Host RCB failed with IOASC: 0x%08X\n", ioasc);
+
+ ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
+ } else {
+ ipr_handle_config_change(ioa_cfg, hostrcb);
+ }
+}
+
+/**
+ * ipr_log_vpd - Log the passed VPD to the error log.
+ * @vpids: vendor/product id struct
+ * @serial_num: serial number string
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_vpd(struct ipr_std_inq_vpids *vpids, u8 *serial_num)
+{
+ char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN
+ + IPR_SERIAL_NUM_LEN];
+
+ memcpy(buffer, vpids->vendor_id, IPR_VENDOR_ID_LEN);
+ memcpy(buffer + IPR_VENDOR_ID_LEN, vpids->product_id,
+ IPR_PROD_ID_LEN);
+ buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0';
+ ipr_err("Vendor/Product ID: %s\n", buffer);
+
+ memcpy(buffer, serial_num, IPR_SERIAL_NUM_LEN);
+ buffer[IPR_SERIAL_NUM_LEN] = '\0';
+ ipr_err(" Serial Number: %s\n", buffer);
+}
+
+/**
+ * ipr_log_cache_error - Log a cache error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_cache_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ struct ipr_hostrcb_type_02_error *error =
+ &hostrcb->hcam.u.error.u.type_02_error;
+
+ ipr_err("-----Current Configuration-----\n");
+ ipr_err("Cache Directory Card Information:\n");
+ ipr_log_vpd(&error->ioa_vpids, error->ioa_sn);
+ ipr_err("Adapter Card Information:\n");
+ ipr_log_vpd(&error->cfc_vpids, error->cfc_sn);
+
+ ipr_err("-----Expected Configuration-----\n");
+ ipr_err("Cache Directory Card Information:\n");
+ ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpids,
+ error->ioa_last_attached_to_cfc_sn);
+ ipr_err("Adapter Card Information:\n");
+ ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpids,
+ error->cfc_last_attached_to_ioa_sn);
+
+ ipr_err("Additional IOA Data: %08X %08X %08X\n",
+ be32_to_cpu(error->ioa_data[0]),
+ be32_to_cpu(error->ioa_data[1]),
+ be32_to_cpu(error->ioa_data[2]));
+}
+
+/**
+ * ipr_log_config_error - Log a configuration error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_config_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ int errors_logged, i;
+ struct ipr_hostrcb_device_data_entry *dev_entry;
+ struct ipr_hostrcb_type_03_error *error;
+
+ error = &hostrcb->hcam.u.error.u.type_03_error;
+ errors_logged = be32_to_cpu(error->errors_logged);
+
+ ipr_err("Device Errors Detected/Logged: %d/%d\n",
+ be32_to_cpu(error->errors_detected), errors_logged);
+
+ dev_entry = error->dev_entry;
+
+ for (i = 0; i < errors_logged; i++, dev_entry++) {
+ ipr_err_separator;
+
+ if (dev_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) {
+ ipr_err("Device %d: missing\n", i + 1);
+ } else {
+ ipr_err("Device %d: %d:%d:%d:%d\n", i + 1,
+ ioa_cfg->host->host_no, dev_entry->dev_res_addr.bus,
+ dev_entry->dev_res_addr.target, dev_entry->dev_res_addr.lun);
+ }
+ ipr_log_vpd(&dev_entry->dev_vpids, dev_entry->dev_sn);
+
+ ipr_err("-----New Device Information-----\n");
+ ipr_log_vpd(&dev_entry->new_dev_vpids, dev_entry->new_dev_sn);
+
+ ipr_err("Cache Directory Card Information:\n");
+ ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpids,
+ dev_entry->ioa_last_with_dev_sn);
+
+ ipr_err("Adapter Card Information:\n");
+ ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpids,
+ dev_entry->cfc_last_with_dev_sn);
+
+ ipr_err("Additional IOA Data: %08X %08X %08X %08X %08X\n",
+ be32_to_cpu(dev_entry->ioa_data[0]),
+ be32_to_cpu(dev_entry->ioa_data[1]),
+ be32_to_cpu(dev_entry->ioa_data[2]),
+ be32_to_cpu(dev_entry->ioa_data[3]),
+ be32_to_cpu(dev_entry->ioa_data[4]));
+ }
+}
+
+/**
+ * ipr_log_array_error - Log an array configuration error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ int i;
+ struct ipr_hostrcb_type_04_error *error;
+ struct ipr_hostrcb_array_data_entry *array_entry;
+ const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' };
+
+ error = &hostrcb->hcam.u.error.u.type_04_error;
+
+ ipr_err_separator;
+
+ ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n",
+ error->protection_level,
+ ioa_cfg->host->host_no,
+ error->last_func_vset_res_addr.bus,
+ error->last_func_vset_res_addr.target,
+ error->last_func_vset_res_addr.lun);
+
+ ipr_err_separator;
+
+ array_entry = error->array_member;
+
+ for (i = 0; i < 18; i++) {
+ if (!memcmp(array_entry->serial_num, zero_sn, IPR_SERIAL_NUM_LEN))
+ continue;
+
+ if (be32_to_cpu(error->exposed_mode_adn) == i) {
+ ipr_err("Exposed Array Member %d:\n", i);
+ } else {
+ ipr_err("Array Member %d:\n", i);
+ }
+
+ ipr_log_vpd(&array_entry->vpids, array_entry->serial_num);
+
+ if (array_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) {
+ ipr_err("Current Location: unknown\n");
+ } else {
+ ipr_err("Current Location: %d:%d:%d:%d\n",
+ ioa_cfg->host->host_no,
+ array_entry->dev_res_addr.bus,
+ array_entry->dev_res_addr.target,
+ array_entry->dev_res_addr.lun);
+ }
+
+ if (array_entry->dev_res_addr.bus >= IPR_MAX_NUM_BUSES) {
+ ipr_err("Expected Location: unknown\n");
+ } else {
+ ipr_err("Expected Location: %d:%d:%d:%d\n",
+ ioa_cfg->host->host_no,
+ array_entry->expected_dev_res_addr.bus,
+ array_entry->expected_dev_res_addr.target,
+ array_entry->expected_dev_res_addr.lun);
+ }
+
+ ipr_err_separator;
+
+ if (i == 9)
+ array_entry = error->array_member2;
+ else
+ array_entry++;
+ }
+}
+
+/**
+ * ipr_log_generic_error - Log an adapter error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_log_generic_error(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ int i;
+ int ioa_data_len = be32_to_cpu(hostrcb->hcam.length);
+
+ if (ioa_data_len == 0)
+ return;
+
+ ipr_err("IOA Error Data:\n");
+ ipr_err("Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
+
+ for (i = 0; i < ioa_data_len / 4; i += 4) {
+ ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
+ be32_to_cpu(hostrcb->hcam.u.raw.data[i]),
+ be32_to_cpu(hostrcb->hcam.u.raw.data[i+1]),
+ be32_to_cpu(hostrcb->hcam.u.raw.data[i+2]),
+ be32_to_cpu(hostrcb->hcam.u.raw.data[i+3]));
+ }
+}
+
+/**
+ * ipr_get_error - Find the specfied IOASC in the ipr_error_table.
+ * @ioasc: IOASC
+ *
+ * This function will return the index of into the ipr_error_table
+ * for the specified IOASC. If the IOASC is not in the table,
+ * 0 will be returned, which points to the entry used for unknown errors.
+ *
+ * Return value:
+ * index into the ipr_error_table
+ **/
+static u32 ipr_get_error(u32 ioasc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++)
+ if (ipr_error_table[i].ioasc == ioasc)
+ return i;
+
+ return 0;
+}
+
+/**
+ * ipr_handle_log_data - Log an adapter error.
+ * @ioa_cfg: ioa config struct
+ * @hostrcb: hostrcb struct
+ *
+ * This function logs an adapter error to the system.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_hostrcb *hostrcb)
+{
+ u32 ioasc;
+ int error_index;
+
+ if (hostrcb->hcam.notify_type != IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY)
+ return;
+
+ if (hostrcb->hcam.notifications_lost == IPR_HOST_RCB_NOTIFICATIONS_LOST)
+ dev_err(&ioa_cfg->pdev->dev, "Error notifications lost\n");
+
+ ioasc = be32_to_cpu(hostrcb->hcam.u.error.failing_dev_ioasc);
+
+ if (ioasc == IPR_IOASC_BUS_WAS_RESET ||
+ ioasc == IPR_IOASC_BUS_WAS_RESET_BY_OTHER) {
+ /* Tell the midlayer we had a bus reset so it will handle the UA properly */
+ scsi_report_bus_reset(ioa_cfg->host,
+ hostrcb->hcam.u.error.failing_dev_res_addr.bus);
+ }
+
+ error_index = ipr_get_error(ioasc);
+
+ if (!ipr_error_table[error_index].log_hcam)
+ return;
+
+ if (ipr_is_device(&hostrcb->hcam.u.error.failing_dev_res_addr)) {
+ ipr_res_err(ioa_cfg, hostrcb->hcam.u.error.failing_dev_res_addr,
+ "%s\n", ipr_error_table[error_index].error);
+ } else {
+ dev_err(&ioa_cfg->pdev->dev, "%s\n",
+ ipr_error_table[error_index].error);
+ }
+
+ /* Set indication we have logged an error */
+ ioa_cfg->errors_logged++;
+
+ if (ioa_cfg->log_level < IPR_DEFAULT_LOG_LEVEL)
+ return;
+
+ switch (hostrcb->hcam.overlay_id) {
+ case IPR_HOST_RCB_OVERLAY_ID_1:
+ ipr_log_generic_error(ioa_cfg, hostrcb);
+ break;
+ case IPR_HOST_RCB_OVERLAY_ID_2:
+ ipr_log_cache_error(ioa_cfg, hostrcb);
+ break;
+ case IPR_HOST_RCB_OVERLAY_ID_3:
+ ipr_log_config_error(ioa_cfg, hostrcb);
+ break;
+ case IPR_HOST_RCB_OVERLAY_ID_4:
+ case IPR_HOST_RCB_OVERLAY_ID_6:
+ ipr_log_array_error(ioa_cfg, hostrcb);
+ break;
+ case IPR_HOST_RCB_OVERLAY_ID_DEFAULT:
+ ipr_log_generic_error(ioa_cfg, hostrcb);
+ break;
+ default:
+ dev_err(&ioa_cfg->pdev->dev,
+ "Unknown error received. Overlay ID: %d\n",
+ hostrcb->hcam.overlay_id);
+ break;
+ }
+}
+
+/**
+ * ipr_process_error - Op done function for an adapter error log.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is the op done function for an error log host
+ * controlled async from the adapter. It will log the error and
+ * send the HCAM back to the adapter.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_process_error(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ list_del(&hostrcb->queue);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+
+ if (!ioasc) {
+ ipr_handle_log_data(ioa_cfg, hostrcb);
+ } else if (ioasc != IPR_IOASC_IOA_WAS_RESET) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Host RCB failed with IOASC: 0x%08X\n", ioasc);
+ }
+
+ ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb);
+}
+
+/**
+ * ipr_timeout - An internally generated op has timed out.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function blocks host requests and initiates an
+ * adapter reset.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_timeout(struct ipr_cmnd *ipr_cmd)
+{
+ unsigned long lock_flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ ioa_cfg->errors_logged++;
+ dev_err(&ioa_cfg->pdev->dev,
+ "Adapter being reset due to command timeout.\n");
+
+ if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
+ ioa_cfg->sdt_state = GET_DUMP;
+
+ if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd)
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
+}
+
+/**
+ * ipr_oper_timeout - Adapter timed out transitioning to operational
+ * @ipr_cmd: ipr command struct
+ *
+ * This function blocks host requests and initiates an
+ * adapter reset.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_oper_timeout(struct ipr_cmnd *ipr_cmd)
+{
+ unsigned long lock_flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ ioa_cfg->errors_logged++;
+ dev_err(&ioa_cfg->pdev->dev,
+ "Adapter timed out transitioning to operational.\n");
+
+ if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
+ ioa_cfg->sdt_state = GET_DUMP;
+
+ if (!ioa_cfg->in_reset_reload || ioa_cfg->reset_cmd == ipr_cmd) {
+ if (ipr_fastfail)
+ ioa_cfg->reset_retries += IPR_NUM_RESET_RELOAD_RETRIES;
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
+}
+
+/**
+ * ipr_reset_reload - Reset/Reload the IOA
+ * @ioa_cfg: ioa config struct
+ * @shutdown_type: shutdown type
+ *
+ * This function resets the adapter and re-initializes it.
+ * This function assumes that all new host commands have been stopped.
+ * Return value:
+ * SUCCESS / FAILED
+ **/
+static int ipr_reset_reload(struct ipr_ioa_cfg *ioa_cfg,
+ enum ipr_shutdown_type shutdown_type)
+{
+ if (!ioa_cfg->in_reset_reload)
+ ipr_initiate_ioa_reset(ioa_cfg, shutdown_type);
+
+ spin_unlock_irq(ioa_cfg->host->host_lock);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irq(ioa_cfg->host->host_lock);
+
+ /* If we got hit with a host reset while we were already resetting
+ the adapter for some reason, and the reset failed. */
+ if (ioa_cfg->ioa_is_dead) {
+ ipr_trace;
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * ipr_find_ses_entry - Find matching SES in SES table
+ * @res: resource entry struct of SES
+ *
+ * Return value:
+ * pointer to SES table entry / NULL on failure
+ **/
+static const struct ipr_ses_table_entry *
+ipr_find_ses_entry(struct ipr_resource_entry *res)
+{
+ int i, j, matches;
+ const struct ipr_ses_table_entry *ste = ipr_ses_table;
+
+ for (i = 0; i < ARRAY_SIZE(ipr_ses_table); i++, ste++) {
+ for (j = 0, matches = 0; j < IPR_PROD_ID_LEN; j++) {
+ if (ste->compare_product_id_byte[j] == 'X') {
+ if (res->cfgte.std_inq_data.vpids.product_id[j] == ste->product_id[j])
+ matches++;
+ else
+ break;
+ } else
+ matches++;
+ }
+
+ if (matches == IPR_PROD_ID_LEN)
+ return ste;
+ }
+
+ return NULL;
+}
+
+/**
+ * ipr_get_max_scsi_speed - Determine max SCSI speed for a given bus
+ * @ioa_cfg: ioa config struct
+ * @bus: SCSI bus
+ * @bus_width: bus width
+ *
+ * Return value:
+ * SCSI bus speed in units of 100KHz, 1600 is 160 MHz
+ * For a 2-byte wide SCSI bus, the maximum transfer speed is
+ * twice the maximum transfer rate (e.g. for a wide enabled bus,
+ * max 160MHz = max 320MB/sec).
+ **/
+static u32 ipr_get_max_scsi_speed(struct ipr_ioa_cfg *ioa_cfg, u8 bus, u8 bus_width)
+{
+ struct ipr_resource_entry *res;
+ const struct ipr_ses_table_entry *ste;
+ u32 max_xfer_rate = IPR_MAX_SCSI_RATE(bus_width);
+
+ /* Loop through each config table entry in the config table buffer */
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if (!(IPR_IS_SES_DEVICE(res->cfgte.std_inq_data)))
+ continue;
+
+ if (bus != res->cfgte.res_addr.bus)
+ continue;
+
+ if (!(ste = ipr_find_ses_entry(res)))
+ continue;
+
+ max_xfer_rate = (ste->max_bus_speed_limit * 10) / (bus_width / 8);
+ }
+
+ return max_xfer_rate;
+}
+
+/**
+ * ipr_wait_iodbg_ack - Wait for an IODEBUG ACK from the IOA
+ * @ioa_cfg: ioa config struct
+ * @max_delay: max delay in micro-seconds to wait
+ *
+ * Waits for an IODEBUG ACK from the IOA, doing busy looping.
+ *
+ * Return value:
+ * 0 on success / other on failure
+ **/
+static int ipr_wait_iodbg_ack(struct ipr_ioa_cfg *ioa_cfg, int max_delay)
+{
+ volatile u32 pcii_reg;
+ int delay = 1;
+
+ /* Read interrupt reg until IOA signals IO Debug Acknowledge */
+ while (delay < max_delay) {
+ pcii_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+
+ if (pcii_reg & IPR_PCII_IO_DEBUG_ACKNOWLEDGE)
+ return 0;
+
+ /* udelay cannot be used if delay is more than a few milliseconds */
+ if ((delay / 1000) > MAX_UDELAY_MS)
+ mdelay(delay / 1000);
+ else
+ udelay(delay);
+
+ delay += delay;
+ }
+ return -EIO;
+}
+
+/**
+ * ipr_get_ldump_data_section - Dump IOA memory
+ * @ioa_cfg: ioa config struct
+ * @start_addr: adapter address to dump
+ * @dest: destination kernel buffer
+ * @length_in_words: length to dump in 4 byte words
+ *
+ * Return value:
+ * 0 on success / -EIO on failure
+ **/
+static int ipr_get_ldump_data_section(struct ipr_ioa_cfg *ioa_cfg,
+ u32 start_addr,
+ __be32 *dest, u32 length_in_words)
+{
+ volatile u32 temp_pcii_reg;
+ int i, delay = 0;
+
+ /* Write IOA interrupt reg starting LDUMP state */
+ writel((IPR_UPROCI_RESET_ALERT | IPR_UPROCI_IO_DEBUG_ALERT),
+ ioa_cfg->regs.set_uproc_interrupt_reg);
+
+ /* Wait for IO debug acknowledge */
+ if (ipr_wait_iodbg_ack(ioa_cfg,
+ IPR_LDUMP_MAX_LONG_ACK_DELAY_IN_USEC)) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "IOA dump long data transfer timeout\n");
+ return -EIO;
+ }
+
+ /* Signal LDUMP interlocked - clear IO debug ack */
+ writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE,
+ ioa_cfg->regs.clr_interrupt_reg);
+
+ /* Write Mailbox with starting address */
+ writel(start_addr, ioa_cfg->ioa_mailbox);
+
+ /* Signal address valid - clear IOA Reset alert */
+ writel(IPR_UPROCI_RESET_ALERT,
+ ioa_cfg->regs.clr_uproc_interrupt_reg);
+
+ for (i = 0; i < length_in_words; i++) {
+ /* Wait for IO debug acknowledge */
+ if (ipr_wait_iodbg_ack(ioa_cfg,
+ IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC)) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "IOA dump short data transfer timeout\n");
+ return -EIO;
+ }
+
+ /* Read data from mailbox and increment destination pointer */
+ *dest = cpu_to_be32(readl(ioa_cfg->ioa_mailbox));
+ dest++;
+
+ /* For all but the last word of data, signal data received */
+ if (i < (length_in_words - 1)) {
+ /* Signal dump data received - Clear IO debug Ack */
+ writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE,
+ ioa_cfg->regs.clr_interrupt_reg);
+ }
+ }
+
+ /* Signal end of block transfer. Set reset alert then clear IO debug ack */
+ writel(IPR_UPROCI_RESET_ALERT,
+ ioa_cfg->regs.set_uproc_interrupt_reg);
+
+ writel(IPR_UPROCI_IO_DEBUG_ALERT,
+ ioa_cfg->regs.clr_uproc_interrupt_reg);
+
+ /* Signal dump data received - Clear IO debug Ack */
+ writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE,
+ ioa_cfg->regs.clr_interrupt_reg);
+
+ /* Wait for IOA to signal LDUMP exit - IOA reset alert will be cleared */
+ while (delay < IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC) {
+ temp_pcii_reg =
+ readl(ioa_cfg->regs.sense_uproc_interrupt_reg);
+
+ if (!(temp_pcii_reg & IPR_UPROCI_RESET_ALERT))
+ return 0;
+
+ udelay(10);
+ delay += 10;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SCSI_IPR_DUMP
+/**
+ * ipr_sdt_copy - Copy Smart Dump Table to kernel buffer
+ * @ioa_cfg: ioa config struct
+ * @pci_address: adapter address
+ * @length: length of data to copy
+ *
+ * Copy data from PCI adapter to kernel buffer.
+ * Note: length MUST be a 4 byte multiple
+ * Return value:
+ * 0 on success / other on failure
+ **/
+static int ipr_sdt_copy(struct ipr_ioa_cfg *ioa_cfg,
+ unsigned long pci_address, u32 length)
+{
+ int bytes_copied = 0;
+ int cur_len, rc, rem_len, rem_page_len;
+ __be32 *page;
+ unsigned long lock_flags = 0;
+ struct ipr_ioa_dump *ioa_dump = &ioa_cfg->dump->ioa_dump;
+
+ while (bytes_copied < length &&
+ (ioa_dump->hdr.len + bytes_copied) < IPR_MAX_IOA_DUMP_SIZE) {
+ if (ioa_dump->page_offset >= PAGE_SIZE ||
+ ioa_dump->page_offset == 0) {
+ page = (__be32 *)__get_free_page(GFP_ATOMIC);
+
+ if (!page) {
+ ipr_trace;
+ return bytes_copied;
+ }
+
+ ioa_dump->page_offset = 0;
+ ioa_dump->ioa_data[ioa_dump->next_page_index] = page;
+ ioa_dump->next_page_index++;
+ } else
+ page = ioa_dump->ioa_data[ioa_dump->next_page_index - 1];
+
+ rem_len = length - bytes_copied;
+ rem_page_len = PAGE_SIZE - ioa_dump->page_offset;
+ cur_len = min(rem_len, rem_page_len);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ioa_cfg->sdt_state == ABORT_DUMP) {
+ rc = -EIO;
+ } else {
+ rc = ipr_get_ldump_data_section(ioa_cfg,
+ pci_address + bytes_copied,
+ &page[ioa_dump->page_offset / 4],
+ (cur_len / sizeof(u32)));
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ if (!rc) {
+ ioa_dump->page_offset += cur_len;
+ bytes_copied += cur_len;
+ } else {
+ ipr_trace;
+ break;
+ }
+ schedule();
+ }
+
+ return bytes_copied;
+}
+
+/**
+ * ipr_init_dump_entry_hdr - Initialize a dump entry header.
+ * @hdr: dump entry header struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_init_dump_entry_hdr(struct ipr_dump_entry_header *hdr)
+{
+ hdr->eye_catcher = IPR_DUMP_EYE_CATCHER;
+ hdr->num_elems = 1;
+ hdr->offset = sizeof(*hdr);
+ hdr->status = IPR_DUMP_STATUS_SUCCESS;
+}
+
+/**
+ * ipr_dump_ioa_type_data - Fill in the adapter type in the dump.
+ * @ioa_cfg: ioa config struct
+ * @driver_dump: driver dump struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_dump_ioa_type_data(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_driver_dump *driver_dump)
+{
+ struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
+
+ ipr_init_dump_entry_hdr(&driver_dump->ioa_type_entry.hdr);
+ driver_dump->ioa_type_entry.hdr.len =
+ sizeof(struct ipr_dump_ioa_type_entry) -
+ sizeof(struct ipr_dump_entry_header);
+ driver_dump->ioa_type_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
+ driver_dump->ioa_type_entry.hdr.id = IPR_DUMP_DRIVER_TYPE_ID;
+ driver_dump->ioa_type_entry.type = ioa_cfg->type;
+ driver_dump->ioa_type_entry.fw_version = (ucode_vpd->major_release << 24) |
+ (ucode_vpd->card_type << 16) | (ucode_vpd->minor_release[0] << 8) |
+ ucode_vpd->minor_release[1];
+ driver_dump->hdr.num_entries++;
+}
+
+/**
+ * ipr_dump_version_data - Fill in the driver version in the dump.
+ * @ioa_cfg: ioa config struct
+ * @driver_dump: driver dump struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_dump_version_data(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_driver_dump *driver_dump)
+{
+ ipr_init_dump_entry_hdr(&driver_dump->version_entry.hdr);
+ driver_dump->version_entry.hdr.len =
+ sizeof(struct ipr_dump_version_entry) -
+ sizeof(struct ipr_dump_entry_header);
+ driver_dump->version_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII;
+ driver_dump->version_entry.hdr.id = IPR_DUMP_DRIVER_VERSION_ID;
+ strcpy(driver_dump->version_entry.version, IPR_DRIVER_VERSION);
+ driver_dump->hdr.num_entries++;
+}
+
+/**
+ * ipr_dump_trace_data - Fill in the IOA trace in the dump.
+ * @ioa_cfg: ioa config struct
+ * @driver_dump: driver dump struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_dump_trace_data(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_driver_dump *driver_dump)
+{
+ ipr_init_dump_entry_hdr(&driver_dump->trace_entry.hdr);
+ driver_dump->trace_entry.hdr.len =
+ sizeof(struct ipr_dump_trace_entry) -
+ sizeof(struct ipr_dump_entry_header);
+ driver_dump->trace_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
+ driver_dump->trace_entry.hdr.id = IPR_DUMP_TRACE_ID;
+ memcpy(driver_dump->trace_entry.trace, ioa_cfg->trace, IPR_TRACE_SIZE);
+ driver_dump->hdr.num_entries++;
+}
+
+/**
+ * ipr_dump_location_data - Fill in the IOA location in the dump.
+ * @ioa_cfg: ioa config struct
+ * @driver_dump: driver dump struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_dump_location_data(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_driver_dump *driver_dump)
+{
+ ipr_init_dump_entry_hdr(&driver_dump->location_entry.hdr);
+ driver_dump->location_entry.hdr.len =
+ sizeof(struct ipr_dump_location_entry) -
+ sizeof(struct ipr_dump_entry_header);
+ driver_dump->location_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII;
+ driver_dump->location_entry.hdr.id = IPR_DUMP_LOCATION_ID;
+ strcpy(driver_dump->location_entry.location, ioa_cfg->pdev->dev.bus_id);
+ driver_dump->hdr.num_entries++;
+}
+
+/**
+ * ipr_get_ioa_dump - Perform a dump of the driver and adapter.
+ * @ioa_cfg: ioa config struct
+ * @dump: dump struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
+{
+ unsigned long start_addr, sdt_word;
+ unsigned long lock_flags = 0;
+ struct ipr_driver_dump *driver_dump = &dump->driver_dump;
+ struct ipr_ioa_dump *ioa_dump = &dump->ioa_dump;
+ u32 num_entries, start_off, end_off;
+ u32 bytes_to_copy, bytes_copied, rc;
+ struct ipr_sdt *sdt;
+ int i;
+
+ ENTER;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (ioa_cfg->sdt_state != GET_DUMP) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ start_addr = readl(ioa_cfg->ioa_mailbox);
+
+ if (!ipr_sdt_is_fmt2(start_addr)) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Invalid dump table format: %lx\n", start_addr);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ dev_err(&ioa_cfg->pdev->dev, "Dump of IOA initiated\n");
+
+ driver_dump->hdr.eye_catcher = IPR_DUMP_EYE_CATCHER;
+
+ /* Initialize the overall dump header */
+ driver_dump->hdr.len = sizeof(struct ipr_driver_dump);
+ driver_dump->hdr.num_entries = 1;
+ driver_dump->hdr.first_entry_offset = sizeof(struct ipr_dump_header);
+ driver_dump->hdr.status = IPR_DUMP_STATUS_SUCCESS;
+ driver_dump->hdr.os = IPR_DUMP_OS_LINUX;
+ driver_dump->hdr.driver_name = IPR_DUMP_DRIVER_NAME;
+
+ ipr_dump_version_data(ioa_cfg, driver_dump);
+ ipr_dump_location_data(ioa_cfg, driver_dump);
+ ipr_dump_ioa_type_data(ioa_cfg, driver_dump);
+ ipr_dump_trace_data(ioa_cfg, driver_dump);
+
+ /* Update dump_header */
+ driver_dump->hdr.len += sizeof(struct ipr_dump_entry_header);
+
+ /* IOA Dump entry */
+ ipr_init_dump_entry_hdr(&ioa_dump->hdr);
+ ioa_dump->format = IPR_SDT_FMT2;
+ ioa_dump->hdr.len = 0;
+ ioa_dump->hdr.data_type = IPR_DUMP_DATA_TYPE_BINARY;
+ ioa_dump->hdr.id = IPR_DUMP_IOA_DUMP_ID;
+
+ /* First entries in sdt are actually a list of dump addresses and
+ lengths to gather the real dump data. sdt represents the pointer
+ to the ioa generated dump table. Dump data will be extracted based
+ on entries in this table */
+ sdt = &ioa_dump->sdt;
+
+ rc = ipr_get_ldump_data_section(ioa_cfg, start_addr, (__be32 *)sdt,
+ sizeof(struct ipr_sdt) / sizeof(__be32));
+
+ /* Smart Dump table is ready to use and the first entry is valid */
+ if (rc || (be32_to_cpu(sdt->hdr.state) != IPR_FMT2_SDT_READY_TO_USE)) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Dump of IOA failed. Dump table not valid: %d, %X.\n",
+ rc, be32_to_cpu(sdt->hdr.state));
+ driver_dump->hdr.status = IPR_DUMP_STATUS_FAILED;
+ ioa_cfg->sdt_state = DUMP_OBTAINED;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ num_entries = be32_to_cpu(sdt->hdr.num_entries_used);
+
+ if (num_entries > IPR_NUM_SDT_ENTRIES)
+ num_entries = IPR_NUM_SDT_ENTRIES;
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ for (i = 0; i < num_entries; i++) {
+ if (ioa_dump->hdr.len > IPR_MAX_IOA_DUMP_SIZE) {
+ driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS;
+ break;
+ }
+
+ if (sdt->entry[i].flags & IPR_SDT_VALID_ENTRY) {
+ sdt_word = be32_to_cpu(sdt->entry[i].bar_str_offset);
+ start_off = sdt_word & IPR_FMT2_MBX_ADDR_MASK;
+ end_off = be32_to_cpu(sdt->entry[i].end_offset);
+
+ if (ipr_sdt_is_fmt2(sdt_word) && sdt_word) {
+ bytes_to_copy = end_off - start_off;
+ if (bytes_to_copy > IPR_MAX_IOA_DUMP_SIZE) {
+ sdt->entry[i].flags &= ~IPR_SDT_VALID_ENTRY;
+ continue;
+ }
+
+ /* Copy data from adapter to driver buffers */
+ bytes_copied = ipr_sdt_copy(ioa_cfg, sdt_word,
+ bytes_to_copy);
+
+ ioa_dump->hdr.len += bytes_copied;
+
+ if (bytes_copied != bytes_to_copy) {
+ driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS;
+ break;
+ }
+ }
+ }
+ }
+
+ dev_err(&ioa_cfg->pdev->dev, "Dump of IOA completed.\n");
+
+ /* Update dump_header */
+ driver_dump->hdr.len += ioa_dump->hdr.len;
+ wmb();
+ ioa_cfg->sdt_state = DUMP_OBTAINED;
+ LEAVE;
+}
+
+#else
+#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0)
+#endif
+
+/**
+ * ipr_release_dump - Free adapter dump memory
+ * @kref: kref struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_release_dump(struct kref *kref)
+{
+ struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref);
+ struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg;
+ unsigned long lock_flags = 0;
+ int i;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ ioa_cfg->dump = NULL;
+ ioa_cfg->sdt_state = INACTIVE;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ for (i = 0; i < dump->ioa_dump.next_page_index; i++)
+ free_page((unsigned long) dump->ioa_dump.ioa_data[i]);
+
+ kfree(dump);
+ LEAVE;
+}
+
+/**
+ * ipr_worker_thread - Worker thread
+ * @data: ioa config struct
+ *
+ * Called at task level from a work thread. This function takes care
+ * of adding and removing device from the mid-layer as configuration
+ * changes are detected by the adapter.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_worker_thread(void *data)
+{
+ unsigned long lock_flags;
+ struct ipr_resource_entry *res;
+ struct scsi_device *sdev;
+ struct ipr_dump *dump;
+ struct ipr_ioa_cfg *ioa_cfg = data;
+ u8 bus, target, lun;
+ int did_work;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (ioa_cfg->sdt_state == GET_DUMP) {
+ dump = ioa_cfg->dump;
+ if (!dump) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+ kref_get(&dump->kref);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ ipr_get_ioa_dump(ioa_cfg, dump);
+ kref_put(&dump->kref, ipr_release_dump);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ioa_cfg->sdt_state == DUMP_OBTAINED)
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+restart:
+ do {
+ did_work = 0;
+ if (!ioa_cfg->allow_cmds || !ioa_cfg->allow_ml_add_del) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if (res->del_from_ml && res->sdev) {
+ did_work = 1;
+ sdev = res->sdev;
+ if (!scsi_device_get(sdev)) {
+ res->sdev = NULL;
+ list_move_tail(&res->queue, &ioa_cfg->free_res_q);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ }
+ break;
+ }
+ }
+ } while(did_work);
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if (res->add_to_ml) {
+ bus = res->cfgte.res_addr.bus;
+ target = res->cfgte.res_addr.target;
+ lun = res->cfgte.res_addr.lun;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ scsi_add_device(ioa_cfg->host, bus, target, lun);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ goto restart;
+ }
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ kobject_uevent(&ioa_cfg->host->shost_classdev.kobj, KOBJ_CHANGE, NULL);
+ LEAVE;
+}
+
+#ifdef CONFIG_SCSI_IPR_TRACE
+/**
+ * ipr_read_trace - Dump the adapter trace
+ * @kobj: kobject struct
+ * @buf: buffer
+ * @off: offset
+ * @count: buffer size
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_read_trace(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct class_device *cdev = container_of(kobj,struct class_device,kobj);
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long lock_flags = 0;
+ int size = IPR_TRACE_SIZE;
+ char *src = (char *)ioa_cfg->trace;
+
+ if (off > size)
+ return 0;
+ if (off + count > size) {
+ size -= off;
+ count = size;
+ }
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ memcpy(buf, &src[off], count);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return count;
+}
+
+static struct bin_attribute ipr_trace_attr = {
+ .attr = {
+ .name = "trace",
+ .mode = S_IRUGO,
+ },
+ .size = 0,
+ .read = ipr_read_trace,
+};
+#endif
+
+/**
+ * ipr_show_fw_version - Show the firmware version
+ * @class_dev: class device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_show_fw_version(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
+ unsigned long lock_flags = 0;
+ int len;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ len = snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X\n",
+ ucode_vpd->major_release, ucode_vpd->card_type,
+ ucode_vpd->minor_release[0],
+ ucode_vpd->minor_release[1]);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return len;
+}
+
+static struct class_device_attribute ipr_fw_version_attr = {
+ .attr = {
+ .name = "fw_version",
+ .mode = S_IRUGO,
+ },
+ .show = ipr_show_fw_version,
+};
+
+/**
+ * ipr_show_log_level - Show the adapter's error logging level
+ * @class_dev: class device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_show_log_level(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long lock_flags = 0;
+ int len;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ len = snprintf(buf, PAGE_SIZE, "%d\n", ioa_cfg->log_level);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return len;
+}
+
+/**
+ * ipr_store_log_level - Change the adapter's error logging level
+ * @class_dev: class device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_store_log_level(struct class_device *class_dev,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ ioa_cfg->log_level = simple_strtoul(buf, NULL, 10);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return strlen(buf);
+}
+
+static struct class_device_attribute ipr_log_level_attr = {
+ .attr = {
+ .name = "log_level",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ipr_show_log_level,
+ .store = ipr_store_log_level
+};
+
+/**
+ * ipr_store_diagnostics - IOA Diagnostics interface
+ * @class_dev: class_device struct
+ * @buf: buffer
+ * @count: buffer size
+ *
+ * This function will reset the adapter and wait a reasonable
+ * amount of time for any errors that the adapter might log.
+ *
+ * Return value:
+ * count on success / other on failure
+ **/
+static ssize_t ipr_store_diagnostics(struct class_device *class_dev,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long lock_flags = 0;
+ int rc = count;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ ioa_cfg->errors_logged = 0;
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL);
+
+ if (ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+
+ /* Wait for a second for any errors to be logged */
+ msleep(1000);
+ } else {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ioa_cfg->in_reset_reload || ioa_cfg->errors_logged)
+ rc = -EIO;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ return rc;
+}
+
+static struct class_device_attribute ipr_diagnostics_attr = {
+ .attr = {
+ .name = "run_diagnostics",
+ .mode = S_IWUSR,
+ },
+ .store = ipr_store_diagnostics
+};
+
+/**
+ * ipr_store_reset_adapter - Reset the adapter
+ * @class_dev: class_device struct
+ * @buf: buffer
+ * @count: buffer size
+ *
+ * This function will reset the adapter.
+ *
+ * Return value:
+ * count on success / other on failure
+ **/
+static ssize_t ipr_store_reset_adapter(struct class_device *class_dev,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ unsigned long lock_flags;
+ int result = count;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (!ioa_cfg->in_reset_reload)
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+
+ return result;
+}
+
+static struct class_device_attribute ipr_ioa_reset_attr = {
+ .attr = {
+ .name = "reset_host",
+ .mode = S_IWUSR,
+ },
+ .store = ipr_store_reset_adapter
+};
+
+/**
+ * ipr_alloc_ucode_buffer - Allocates a microcode download buffer
+ * @buf_len: buffer length
+ *
+ * Allocates a DMA'able buffer in chunks and assembles a scatter/gather
+ * list to use for microcode download
+ *
+ * Return value:
+ * pointer to sglist / NULL on failure
+ **/
+static struct ipr_sglist *ipr_alloc_ucode_buffer(int buf_len)
+{
+ int sg_size, order, bsize_elem, num_elem, i, j;
+ struct ipr_sglist *sglist;
+ struct scatterlist *scatterlist;
+ struct page *page;
+
+ /* Get the minimum size per scatter/gather element */
+ sg_size = buf_len / (IPR_MAX_SGLIST - 1);
+
+ /* Get the actual size per element */
+ order = get_order(sg_size);
+
+ /* Determine the actual number of bytes per element */
+ bsize_elem = PAGE_SIZE * (1 << order);
+
+ /* Determine the actual number of sg entries needed */
+ if (buf_len % bsize_elem)
+ num_elem = (buf_len / bsize_elem) + 1;
+ else
+ num_elem = buf_len / bsize_elem;
+
+ /* Allocate a scatter/gather list for the DMA */
+ sglist = kmalloc(sizeof(struct ipr_sglist) +
+ (sizeof(struct scatterlist) * (num_elem - 1)),
+ GFP_KERNEL);
+
+ if (sglist == NULL) {
+ ipr_trace;
+ return NULL;
+ }
+
+ memset(sglist, 0, sizeof(struct ipr_sglist) +
+ (sizeof(struct scatterlist) * (num_elem - 1)));
+
+ scatterlist = sglist->scatterlist;
+
+ sglist->order = order;
+ sglist->num_sg = num_elem;
+
+ /* Allocate a bunch of sg elements */
+ for (i = 0; i < num_elem; i++) {
+ page = alloc_pages(GFP_KERNEL, order);
+ if (!page) {
+ ipr_trace;
+
+ /* Free up what we already allocated */
+ for (j = i - 1; j >= 0; j--)
+ __free_pages(scatterlist[j].page, order);
+ kfree(sglist);
+ return NULL;
+ }
+
+ scatterlist[i].page = page;
+ }
+
+ return sglist;
+}
+
+/**
+ * ipr_free_ucode_buffer - Frees a microcode download buffer
+ * @p_dnld: scatter/gather list pointer
+ *
+ * Free a DMA'able ucode download buffer previously allocated with
+ * ipr_alloc_ucode_buffer
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_free_ucode_buffer(struct ipr_sglist *sglist)
+{
+ int i;
+
+ for (i = 0; i < sglist->num_sg; i++)
+ __free_pages(sglist->scatterlist[i].page, sglist->order);
+
+ kfree(sglist);
+}
+
+/**
+ * ipr_copy_ucode_buffer - Copy user buffer to kernel buffer
+ * @sglist: scatter/gather list pointer
+ * @buffer: buffer pointer
+ * @len: buffer length
+ *
+ * Copy a microcode image from a user buffer into a buffer allocated by
+ * ipr_alloc_ucode_buffer
+ *
+ * Return value:
+ * 0 on success / other on failure
+ **/
+static int ipr_copy_ucode_buffer(struct ipr_sglist *sglist,
+ u8 *buffer, u32 len)
+{
+ int bsize_elem, i, result = 0;
+ struct scatterlist *scatterlist;
+ void *kaddr;
+
+ /* Determine the actual number of bytes per element */
+ bsize_elem = PAGE_SIZE * (1 << sglist->order);
+
+ scatterlist = sglist->scatterlist;
+
+ for (i = 0; i < (len / bsize_elem); i++, buffer += bsize_elem) {
+ kaddr = kmap(scatterlist[i].page);
+ memcpy(kaddr, buffer, bsize_elem);
+ kunmap(scatterlist[i].page);
+
+ scatterlist[i].length = bsize_elem;
+
+ if (result != 0) {
+ ipr_trace;
+ return result;
+ }
+ }
+
+ if (len % bsize_elem) {
+ kaddr = kmap(scatterlist[i].page);
+ memcpy(kaddr, buffer, len % bsize_elem);
+ kunmap(scatterlist[i].page);
+
+ scatterlist[i].length = len % bsize_elem;
+ }
+
+ sglist->buffer_len = len;
+ return result;
+}
+
+/**
+ * ipr_map_ucode_buffer - Map a microcode download buffer
+ * @ipr_cmd: ipr command struct
+ * @sglist: scatter/gather list
+ * @len: total length of download buffer
+ *
+ * Maps a microcode download scatter/gather list for DMA and
+ * builds the IOADL.
+ *
+ * Return value:
+ * 0 on success / -EIO on failure
+ **/
+static int ipr_map_ucode_buffer(struct ipr_cmnd *ipr_cmd,
+ struct ipr_sglist *sglist, int len)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+ struct scatterlist *scatterlist = sglist->scatterlist;
+ int i;
+
+ ipr_cmd->dma_use_sg = pci_map_sg(ioa_cfg->pdev, scatterlist,
+ sglist->num_sg, DMA_TO_DEVICE);
+
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
+ ioarcb->write_data_transfer_length = cpu_to_be32(len);
+ ioarcb->write_ioadl_len =
+ cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
+
+ for (i = 0; i < ipr_cmd->dma_use_sg; i++) {
+ ioadl[i].flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_WRITE | sg_dma_len(&scatterlist[i]));
+ ioadl[i].address =
+ cpu_to_be32(sg_dma_address(&scatterlist[i]));
+ }
+
+ if (likely(ipr_cmd->dma_use_sg)) {
+ ioadl[i-1].flags_and_data_len |=
+ cpu_to_be32(IPR_IOADL_FLAGS_LAST);
+ }
+ else {
+ dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ipr_store_update_fw - Update the firmware on the adapter
+ * @class_dev: class_device struct
+ * @buf: buffer
+ * @count: buffer size
+ *
+ * This function will update the firmware on the adapter.
+ *
+ * Return value:
+ * count on success / other on failure
+ **/
+static ssize_t ipr_store_update_fw(struct class_device *class_dev,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ struct ipr_ucode_image_header *image_hdr;
+ const struct firmware *fw_entry;
+ struct ipr_sglist *sglist;
+ unsigned long lock_flags;
+ char fname[100];
+ char *src;
+ int len, result, dnld_size;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ len = snprintf(fname, 99, "%s", buf);
+ fname[len-1] = '\0';
+
+ if(request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
+ dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
+ return -EIO;
+ }
+
+ image_hdr = (struct ipr_ucode_image_header *)fw_entry->data;
+
+ if (be32_to_cpu(image_hdr->header_length) > fw_entry->size ||
+ (ioa_cfg->vpd_cbs->page3_data.card_type &&
+ ioa_cfg->vpd_cbs->page3_data.card_type != image_hdr->card_type)) {
+ dev_err(&ioa_cfg->pdev->dev, "Invalid microcode buffer\n");
+ release_firmware(fw_entry);
+ return -EINVAL;
+ }
+
+ src = (u8 *)image_hdr + be32_to_cpu(image_hdr->header_length);
+ dnld_size = fw_entry->size - be32_to_cpu(image_hdr->header_length);
+ sglist = ipr_alloc_ucode_buffer(dnld_size);
+
+ if (!sglist) {
+ dev_err(&ioa_cfg->pdev->dev, "Microcode buffer allocation failed\n");
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ result = ipr_copy_ucode_buffer(sglist, src, dnld_size);
+
+ if (result) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Microcode buffer copy to DMA buffer failed\n");
+ ipr_free_ucode_buffer(sglist);
+ release_firmware(fw_entry);
+ return result;
+ }
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (ioa_cfg->ucode_sglist) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ dev_err(&ioa_cfg->pdev->dev,
+ "Microcode download already in progress\n");
+ ipr_free_ucode_buffer(sglist);
+ release_firmware(fw_entry);
+ return -EIO;
+ }
+
+ ioa_cfg->ucode_sglist = sglist;
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NORMAL);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ ioa_cfg->ucode_sglist = NULL;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ ipr_free_ucode_buffer(sglist);
+ release_firmware(fw_entry);
+
+ return count;
+}
+
+static struct class_device_attribute ipr_update_fw_attr = {
+ .attr = {
+ .name = "update_fw",
+ .mode = S_IWUSR,
+ },
+ .store = ipr_store_update_fw
+};
+
+static struct class_device_attribute *ipr_ioa_attrs[] = {
+ &ipr_fw_version_attr,
+ &ipr_log_level_attr,
+ &ipr_diagnostics_attr,
+ &ipr_ioa_reset_attr,
+ &ipr_update_fw_attr,
+ NULL,
+};
+
+#ifdef CONFIG_SCSI_IPR_DUMP
+/**
+ * ipr_read_dump - Dump the adapter
+ * @kobj: kobject struct
+ * @buf: buffer
+ * @off: offset
+ * @count: buffer size
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_read_dump(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct class_device *cdev = container_of(kobj,struct class_device,kobj);
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ struct ipr_dump *dump;
+ unsigned long lock_flags = 0;
+ char *src;
+ int len;
+ size_t rc = count;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ dump = ioa_cfg->dump;
+
+ if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return 0;
+ }
+ kref_get(&dump->kref);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ if (off > dump->driver_dump.hdr.len) {
+ kref_put(&dump->kref, ipr_release_dump);
+ return 0;
+ }
+
+ if (off + count > dump->driver_dump.hdr.len) {
+ count = dump->driver_dump.hdr.len - off;
+ rc = count;
+ }
+
+ if (count && off < sizeof(dump->driver_dump)) {
+ if (off + count > sizeof(dump->driver_dump))
+ len = sizeof(dump->driver_dump) - off;
+ else
+ len = count;
+ src = (u8 *)&dump->driver_dump + off;
+ memcpy(buf, src, len);
+ buf += len;
+ off += len;
+ count -= len;
+ }
+
+ off -= sizeof(dump->driver_dump);
+
+ if (count && off < offsetof(struct ipr_ioa_dump, ioa_data)) {
+ if (off + count > offsetof(struct ipr_ioa_dump, ioa_data))
+ len = offsetof(struct ipr_ioa_dump, ioa_data) - off;
+ else
+ len = count;
+ src = (u8 *)&dump->ioa_dump + off;
+ memcpy(buf, src, len);
+ buf += len;
+ off += len;
+ count -= len;
+ }
+
+ off -= offsetof(struct ipr_ioa_dump, ioa_data);
+
+ while (count) {
+ if ((off & PAGE_MASK) != ((off + count) & PAGE_MASK))
+ len = PAGE_ALIGN(off) - off;
+ else
+ len = count;
+ src = (u8 *)dump->ioa_dump.ioa_data[(off & PAGE_MASK) >> PAGE_SHIFT];
+ src += off & ~PAGE_MASK;
+ memcpy(buf, src, len);
+ buf += len;
+ off += len;
+ count -= len;
+ }
+
+ kref_put(&dump->kref, ipr_release_dump);
+ return rc;
+}
+
+/**
+ * ipr_alloc_dump - Prepare for adapter dump
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 on success / other on failure
+ **/
+static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct ipr_dump *dump;
+ unsigned long lock_flags = 0;
+
+ ENTER;
+ dump = kmalloc(sizeof(struct ipr_dump), GFP_KERNEL);
+
+ if (!dump) {
+ ipr_err("Dump memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ memset(dump, 0, sizeof(struct ipr_dump));
+ kref_init(&dump->kref);
+ dump->ioa_cfg = ioa_cfg;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (INACTIVE != ioa_cfg->sdt_state) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ kfree(dump);
+ return 0;
+ }
+
+ ioa_cfg->dump = dump;
+ ioa_cfg->sdt_state = WAIT_FOR_DUMP;
+ if (ioa_cfg->ioa_is_dead && !ioa_cfg->dump_taken) {
+ ioa_cfg->dump_taken = 1;
+ schedule_work(&ioa_cfg->work_q);
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ LEAVE;
+ return 0;
+}
+
+/**
+ * ipr_free_dump - Free adapter dump memory
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 on success / other on failure
+ **/
+static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct ipr_dump *dump;
+ unsigned long lock_flags = 0;
+
+ ENTER;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ dump = ioa_cfg->dump;
+ if (!dump) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return 0;
+ }
+
+ ioa_cfg->dump = NULL;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ kref_put(&dump->kref, ipr_release_dump);
+
+ LEAVE;
+ return 0;
+}
+
+/**
+ * ipr_write_dump - Setup dump state of adapter
+ * @kobj: kobject struct
+ * @buf: buffer
+ * @off: offset
+ * @count: buffer size
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_write_dump(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct class_device *cdev = container_of(kobj,struct class_device,kobj);
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata;
+ int rc;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (buf[0] == '1')
+ rc = ipr_alloc_dump(ioa_cfg);
+ else if (buf[0] == '0')
+ rc = ipr_free_dump(ioa_cfg);
+ else
+ return -EINVAL;
+
+ if (rc)
+ return rc;
+ else
+ return count;
+}
+
+static struct bin_attribute ipr_dump_attr = {
+ .attr = {
+ .name = "dump",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = 0,
+ .read = ipr_read_dump,
+ .write = ipr_write_dump
+};
+#else
+static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; };
+#endif
+
+/**
+ * ipr_change_queue_depth - Change the device's queue depth
+ * @sdev: scsi device struct
+ * @qdepth: depth to set
+ *
+ * Return value:
+ * actual depth set
+ **/
+static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ return sdev->queue_depth;
+}
+
+/**
+ * ipr_change_queue_type - Change the device's queue type
+ * @dsev: scsi device struct
+ * @tag_type: type of tags to use
+ *
+ * Return value:
+ * actual queue type set
+ **/
+static int ipr_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = (struct ipr_resource_entry *)sdev->hostdata;
+
+ if (res) {
+ if (ipr_is_gscsi(res) && sdev->tagged_supported) {
+ /*
+ * We don't bother quiescing the device here since the
+ * adapter firmware does it for us.
+ */
+ scsi_set_tag_type(sdev, tag_type);
+
+ if (tag_type)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag_type = 0;
+ } else
+ tag_type = 0;
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return tag_type;
+}
+
+/**
+ * ipr_show_adapter_handle - Show the adapter's resource handle for this device
+ * @dev: device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ **/
+static ssize_t ipr_show_adapter_handle(struct device *dev, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+ ssize_t len = -ENXIO;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = (struct ipr_resource_entry *)sdev->hostdata;
+ if (res)
+ len = snprintf(buf, PAGE_SIZE, "%08X\n", res->cfgte.res_handle);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return len;
+}
+
+static struct device_attribute ipr_adapter_handle_attr = {
+ .attr = {
+ .name = "adapter_handle",
+ .mode = S_IRUSR,
+ },
+ .show = ipr_show_adapter_handle
+};
+
+static struct device_attribute *ipr_dev_attrs[] = {
+ &ipr_adapter_handle_attr,
+ NULL,
+};
+
+/**
+ * ipr_biosparam - Return the HSC mapping
+ * @sdev: scsi device struct
+ * @block_device: block device pointer
+ * @capacity: capacity of the device
+ * @parm: Array containing returned HSC values.
+ *
+ * This function generates the HSC parms that fdisk uses.
+ * We want to make sure we return something that places partitions
+ * on 4k boundaries for best performance with the IOA.
+ *
+ * Return value:
+ * 0 on success
+ **/
+static int ipr_biosparam(struct scsi_device *sdev,
+ struct block_device *block_device,
+ sector_t capacity, int *parm)
+{
+ int heads, sectors;
+ sector_t cylinders;
+
+ heads = 128;
+ sectors = 32;
+
+ cylinders = capacity;
+ sector_div(cylinders, (128 * 32));
+
+ /* return result */
+ parm[0] = heads;
+ parm[1] = sectors;
+ parm[2] = cylinders;
+
+ return 0;
+}
+
+/**
+ * ipr_slave_destroy - Unconfigure a SCSI device
+ * @sdev: scsi device struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_slave_destroy(struct scsi_device *sdev)
+{
+ struct ipr_resource_entry *res;
+ struct ipr_ioa_cfg *ioa_cfg;
+ unsigned long lock_flags = 0;
+
+ ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = (struct ipr_resource_entry *) sdev->hostdata;
+ if (res) {
+ sdev->hostdata = NULL;
+ res->sdev = NULL;
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+}
+
+/**
+ * ipr_slave_configure - Configure a SCSI device
+ * @sdev: scsi device struct
+ *
+ * This function configures the specified scsi device.
+ *
+ * Return value:
+ * 0 on success
+ **/
+static int ipr_slave_configure(struct scsi_device *sdev)
+{
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ res = sdev->hostdata;
+ if (res) {
+ if (ipr_is_af_dasd_device(res))
+ sdev->type = TYPE_RAID;
+ if (ipr_is_af_dasd_device(res) || ipr_is_ioa_resource(res))
+ sdev->scsi_level = 4;
+ if (ipr_is_vset_device(res)) {
+ sdev->timeout = IPR_VSET_RW_TIMEOUT;
+ blk_queue_max_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS);
+ }
+ if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data))
+ sdev->allow_restart = 1;
+ scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ }
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return 0;
+}
+
+/**
+ * ipr_slave_alloc - Prepare for commands to a device.
+ * @sdev: scsi device struct
+ *
+ * This function saves a pointer to the resource entry
+ * in the scsi device struct if the device exists. We
+ * can then use this pointer in ipr_queuecommand when
+ * handling new commands.
+ *
+ * Return value:
+ * 0 on success
+ **/
+static int ipr_slave_alloc(struct scsi_device *sdev)
+{
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata;
+ struct ipr_resource_entry *res;
+ unsigned long lock_flags;
+
+ sdev->hostdata = NULL;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if ((res->cfgte.res_addr.bus == sdev->channel) &&
+ (res->cfgte.res_addr.target == sdev->id) &&
+ (res->cfgte.res_addr.lun == sdev->lun)) {
+ res->sdev = sdev;
+ res->add_to_ml = 0;
+ res->in_erp = 0;
+ sdev->hostdata = res;
+ res->needs_sync_complete = 1;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+
+ return 0;
+}
+
+/**
+ * ipr_eh_host_reset - Reset the host adapter
+ * @scsi_cmd: scsi command struct
+ *
+ * Return value:
+ * SUCCESS / FAILED
+ **/
+static int ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg;
+ int rc;
+
+ ENTER;
+ ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
+
+ dev_err(&ioa_cfg->pdev->dev,
+ "Adapter being reset as a result of error recovery.\n");
+
+ if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
+ ioa_cfg->sdt_state = GET_DUMP;
+
+ rc = ipr_reset_reload(ioa_cfg, IPR_SHUTDOWN_ABBREV);
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ipr_eh_dev_reset - Reset the device
+ * @scsi_cmd: scsi command struct
+ *
+ * This function issues a device reset to the affected device.
+ * A LUN reset will be sent to the device first. If that does
+ * not work, a target reset will be sent.
+ *
+ * Return value:
+ * SUCCESS / FAILED
+ **/
+static int ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
+{
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioa_cfg *ioa_cfg;
+ struct ipr_resource_entry *res;
+ struct ipr_cmd_pkt *cmd_pkt;
+ u32 ioasc;
+
+ ENTER;
+ ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
+ res = scsi_cmd->device->hostdata;
+
+ if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res)))
+ return FAILED;
+
+ /*
+ * If we are currently going through reset/reload, return failed. This will force the
+ * mid-layer to call ipr_eh_host_reset, which will then go to sleep and wait for the
+ * reset to complete
+ */
+ if (ioa_cfg->in_reset_reload)
+ return FAILED;
+ if (ioa_cfg->ioa_is_dead)
+ return FAILED;
+
+ list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
+ if (ipr_cmd->ioarcb.res_handle == res->cfgte.res_handle) {
+ if (ipr_cmd->scsi_cmd)
+ ipr_cmd->done = ipr_scsi_eh_done;
+ }
+ }
+
+ res->resetting_device = 1;
+
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+
+ ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle;
+ cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
+ cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
+
+ ipr_sdev_err(scsi_cmd->device, "Resetting device\n");
+ ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
+
+ ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ res->resetting_device = 0;
+
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+
+ LEAVE;
+ return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+}
+
+/**
+ * ipr_bus_reset_done - Op done function for bus reset.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is the op done function for a bus reset
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_bus_reset_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_resource_entry *res;
+
+ ENTER;
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if (!memcmp(&res->cfgte.res_handle, &ipr_cmd->ioarcb.res_handle,
+ sizeof(res->cfgte.res_handle))) {
+ scsi_report_bus_reset(ioa_cfg->host, res->cfgte.res_addr.bus);
+ break;
+ }
+ }
+
+ /*
+ * If abort has not completed, indicate the reset has, else call the
+ * abort's done function to wake the sleeping eh thread
+ */
+ if (ipr_cmd->sibling->sibling)
+ ipr_cmd->sibling->sibling = NULL;
+ else
+ ipr_cmd->sibling->done(ipr_cmd->sibling);
+
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ LEAVE;
+}
+
+/**
+ * ipr_abort_timeout - An abort task has timed out
+ * @ipr_cmd: ipr command struct
+ *
+ * This function handles when an abort task times out. If this
+ * happens we issue a bus reset since we have resources tied
+ * up that must be freed before returning to the midlayer.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_cmnd *reset_cmd;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_cmd_pkt *cmd_pkt;
+ unsigned long lock_flags = 0;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ if (ipr_cmd->completion.done || ioa_cfg->in_reset_reload) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return;
+ }
+
+ ipr_sdev_err(ipr_cmd->u.sdev, "Abort timed out. Resetting bus\n");
+ reset_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ipr_cmd->sibling = reset_cmd;
+ reset_cmd->sibling = ipr_cmd;
+ reset_cmd->ioarcb.res_handle = ipr_cmd->ioarcb.res_handle;
+ cmd_pkt = &reset_cmd->ioarcb.cmd_pkt;
+ cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt->cdb[0] = IPR_RESET_DEVICE;
+ cmd_pkt->cdb[2] = IPR_RESET_TYPE_SELECT | IPR_BUS_RESET;
+
+ ipr_do_req(reset_cmd, ipr_bus_reset_done, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ LEAVE;
+}
+
+/**
+ * ipr_cancel_op - Cancel specified op
+ * @scsi_cmd: scsi command struct
+ *
+ * This function cancels specified op.
+ *
+ * Return value:
+ * SUCCESS / FAILED
+ **/
+static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
+{
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioa_cfg *ioa_cfg;
+ struct ipr_resource_entry *res;
+ struct ipr_cmd_pkt *cmd_pkt;
+ u32 ioasc;
+ int op_found = 0;
+
+ ENTER;
+ ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata;
+ res = scsi_cmd->device->hostdata;
+
+ if (!res || (!ipr_is_gscsi(res) && !ipr_is_vset_device(res)))
+ return FAILED;
+
+ list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) {
+ if (ipr_cmd->scsi_cmd == scsi_cmd) {
+ ipr_cmd->done = ipr_scsi_eh_done;
+ op_found = 1;
+ break;
+ }
+ }
+
+ if (!op_found)
+ return SUCCESS;
+
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle;
+ cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
+ cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS;
+ ipr_cmd->u.sdev = scsi_cmd->device;
+
+ ipr_sdev_err(scsi_cmd->device, "Aborting command: %02X\n", scsi_cmd->cmnd[0]);
+ ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_CANCEL_ALL_TIMEOUT);
+ ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ /*
+ * If the abort task timed out and we sent a bus reset, we will get
+ * one the following responses to the abort
+ */
+ if (ioasc == IPR_IOASC_BUS_WAS_RESET || ioasc == IPR_IOASC_SYNC_REQUIRED) {
+ ioasc = 0;
+ ipr_trace;
+ }
+
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ res->needs_sync_complete = 1;
+
+ LEAVE;
+ return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+}
+
+/**
+ * ipr_eh_abort - Abort a single op
+ * @scsi_cmd: scsi command struct
+ *
+ * Return value:
+ * SUCCESS / FAILED
+ **/
+static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg;
+
+ ENTER;
+ ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata;
+
+ /* If we are currently going through reset/reload, return failed. This will force the
+ mid-layer to call ipr_eh_host_reset, which will then go to sleep and wait for the
+ reset to complete */
+ if (ioa_cfg->in_reset_reload)
+ return FAILED;
+ if (ioa_cfg->ioa_is_dead)
+ return FAILED;
+ if (!scsi_cmd->device->hostdata)
+ return FAILED;
+
+ LEAVE;
+ return ipr_cancel_op(scsi_cmd);
+}
+
+/**
+ * ipr_handle_other_interrupt - Handle "other" interrupts
+ * @ioa_cfg: ioa config struct
+ * @int_reg: interrupt register
+ *
+ * Return value:
+ * IRQ_NONE / IRQ_HANDLED
+ **/
+static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
+ volatile u32 int_reg)
+{
+ irqreturn_t rc = IRQ_HANDLED;
+
+ if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
+ /* Mask the interrupt */
+ writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.set_interrupt_mask_reg);
+
+ /* Clear the interrupt */
+ writel(IPR_PCII_IOA_TRANS_TO_OPER, ioa_cfg->regs.clr_interrupt_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+
+ list_del(&ioa_cfg->reset_cmd->queue);
+ del_timer(&ioa_cfg->reset_cmd->timer);
+ ipr_reset_ioa_job(ioa_cfg->reset_cmd);
+ } else {
+ if (int_reg & IPR_PCII_IOA_UNIT_CHECKED)
+ ioa_cfg->ioa_unit_checked = 1;
+ else
+ dev_err(&ioa_cfg->pdev->dev,
+ "Permanent IOA failure. 0x%08X\n", int_reg);
+
+ if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
+ ioa_cfg->sdt_state = GET_DUMP;
+
+ ipr_mask_and_clear_interrupts(ioa_cfg, ~0);
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ }
+
+ return rc;
+}
+
+/**
+ * ipr_isr - Interrupt service routine
+ * @irq: irq number
+ * @devp: pointer to ioa config struct
+ * @regs: pt_regs struct
+ *
+ * Return value:
+ * IRQ_NONE / IRQ_HANDLED
+ **/
+static irqreturn_t ipr_isr(int irq, void *devp, struct pt_regs *regs)
+{
+ struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
+ unsigned long lock_flags = 0;
+ volatile u32 int_reg, int_mask_reg;
+ u32 ioasc;
+ u16 cmd_index;
+ struct ipr_cmnd *ipr_cmd;
+ irqreturn_t rc = IRQ_NONE;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ /* If interrupts are disabled, ignore the interrupt */
+ if (!ioa_cfg->allow_interrupts) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return IRQ_NONE;
+ }
+
+ int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
+
+ /* If an interrupt on the adapter did not occur, ignore it */
+ if (unlikely((int_reg & IPR_PCII_OPER_INTERRUPTS) == 0)) {
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return IRQ_NONE;
+ }
+
+ while (1) {
+ ipr_cmd = NULL;
+
+ while ((be32_to_cpu(*ioa_cfg->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) ==
+ ioa_cfg->toggle_bit) {
+
+ cmd_index = (be32_to_cpu(*ioa_cfg->hrrq_curr) &
+ IPR_HRRQ_REQ_RESP_HANDLE_MASK) >> IPR_HRRQ_REQ_RESP_HANDLE_SHIFT;
+
+ if (unlikely(cmd_index >= IPR_NUM_CMD_BLKS)) {
+ ioa_cfg->errors_logged++;
+ dev_err(&ioa_cfg->pdev->dev, "Invalid response handle from IOA\n");
+
+ if (WAIT_FOR_DUMP == ioa_cfg->sdt_state)
+ ioa_cfg->sdt_state = GET_DUMP;
+
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return IRQ_HANDLED;
+ }
+
+ ipr_cmd = ioa_cfg->ipr_cmnd_list[cmd_index];
+
+ ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, ioasc);
+
+ list_del(&ipr_cmd->queue);
+ del_timer(&ipr_cmd->timer);
+ ipr_cmd->done(ipr_cmd);
+
+ rc = IRQ_HANDLED;
+
+ if (ioa_cfg->hrrq_curr < ioa_cfg->hrrq_end) {
+ ioa_cfg->hrrq_curr++;
+ } else {
+ ioa_cfg->hrrq_curr = ioa_cfg->hrrq_start;
+ ioa_cfg->toggle_bit ^= 1u;
+ }
+ }
+
+ if (ipr_cmd != NULL) {
+ /* Clear the PCI interrupt */
+ writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
+ } else
+ break;
+ }
+
+ if (unlikely(rc == IRQ_NONE))
+ rc = ipr_handle_other_interrupt(ioa_cfg, int_reg);
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ return rc;
+}
+
+/**
+ * ipr_build_ioadl - Build a scatter/gather list and map the buffer
+ * @ioa_cfg: ioa config struct
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * 0 on success / -1 on failure
+ **/
+static int ipr_build_ioadl(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_cmnd *ipr_cmd)
+{
+ int i;
+ struct scatterlist *sglist;
+ u32 length;
+ u32 ioadl_flags = 0;
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+
+ length = scsi_cmd->request_bufflen;
+
+ if (length == 0)
+ return 0;
+
+ if (scsi_cmd->use_sg) {
+ ipr_cmd->dma_use_sg = pci_map_sg(ioa_cfg->pdev,
+ scsi_cmd->request_buffer,
+ scsi_cmd->use_sg,
+ scsi_cmd->sc_data_direction);
+
+ if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ ioadl_flags = IPR_IOADL_FLAGS_WRITE;
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
+ ioarcb->write_data_transfer_length = cpu_to_be32(length);
+ ioarcb->write_ioadl_len =
+ cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
+ } else if (scsi_cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ ioadl_flags = IPR_IOADL_FLAGS_READ;
+ ioarcb->read_data_transfer_length = cpu_to_be32(length);
+ ioarcb->read_ioadl_len =
+ cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg);
+ }
+
+ sglist = scsi_cmd->request_buffer;
+
+ for (i = 0; i < ipr_cmd->dma_use_sg; i++) {
+ ioadl[i].flags_and_data_len =
+ cpu_to_be32(ioadl_flags | sg_dma_len(&sglist[i]));
+ ioadl[i].address =
+ cpu_to_be32(sg_dma_address(&sglist[i]));
+ }
+
+ if (likely(ipr_cmd->dma_use_sg)) {
+ ioadl[i-1].flags_and_data_len |=
+ cpu_to_be32(IPR_IOADL_FLAGS_LAST);
+ return 0;
+ } else
+ dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ } else {
+ if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ ioadl_flags = IPR_IOADL_FLAGS_WRITE;
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
+ ioarcb->write_data_transfer_length = cpu_to_be32(length);
+ ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ } else if (scsi_cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ ioadl_flags = IPR_IOADL_FLAGS_READ;
+ ioarcb->read_data_transfer_length = cpu_to_be32(length);
+ ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ }
+
+ ipr_cmd->dma_handle = pci_map_single(ioa_cfg->pdev,
+ scsi_cmd->request_buffer, length,
+ scsi_cmd->sc_data_direction);
+
+ if (likely(!pci_dma_mapping_error(ipr_cmd->dma_handle))) {
+ ipr_cmd->dma_use_sg = 1;
+ ioadl[0].flags_and_data_len =
+ cpu_to_be32(ioadl_flags | length | IPR_IOADL_FLAGS_LAST);
+ ioadl[0].address = cpu_to_be32(ipr_cmd->dma_handle);
+ return 0;
+ } else
+ dev_err(&ioa_cfg->pdev->dev, "pci_map_single failed!\n");
+ }
+
+ return -1;
+}
+
+/**
+ * ipr_get_task_attributes - Translate SPI Q-Tag to task attributes
+ * @scsi_cmd: scsi command struct
+ *
+ * Return value:
+ * task attributes
+ **/
+static u8 ipr_get_task_attributes(struct scsi_cmnd *scsi_cmd)
+{
+ u8 tag[2];
+ u8 rc = IPR_FLAGS_LO_UNTAGGED_TASK;
+
+ if (scsi_populate_tag_msg(scsi_cmd, tag)) {
+ switch (tag[0]) {
+ case MSG_SIMPLE_TAG:
+ rc = IPR_FLAGS_LO_SIMPLE_TASK;
+ break;
+ case MSG_HEAD_TAG:
+ rc = IPR_FLAGS_LO_HEAD_OF_Q_TASK;
+ break;
+ case MSG_ORDERED_TAG:
+ rc = IPR_FLAGS_LO_ORDERED_TASK;
+ break;
+ };
+ }
+
+ return rc;
+}
+
+/**
+ * ipr_erp_done - Process completion of ERP for a device
+ * @ipr_cmd: ipr command struct
+ *
+ * This function copies the sense buffer into the scsi_cmd
+ * struct and pushes the scsi_done function.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_erp_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+ struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
+ scsi_cmd->result |= (DID_ERROR << 16);
+ ipr_sdev_err(scsi_cmd->device,
+ "Request Sense failed with IOASC: 0x%08X\n", ioasc);
+ } else {
+ memcpy(scsi_cmd->sense_buffer, ipr_cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE);
+ }
+
+ if (res) {
+ res->needs_sync_complete = 1;
+ res->in_erp = 0;
+ }
+ ipr_unmap_sglist(ioa_cfg, ipr_cmd);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ scsi_cmd->scsi_done(scsi_cmd);
+}
+
+/**
+ * ipr_reinit_ipr_cmnd_for_erp - Re-initialize a cmnd block to be used for ERP
+ * @ipr_cmd: ipr command struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_reinit_ipr_cmnd_for_erp(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioarcb *ioarcb;
+ struct ipr_ioasa *ioasa;
+
+ ioarcb = &ipr_cmd->ioarcb;
+ ioasa = &ipr_cmd->ioasa;
+
+ memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt));
+ ioarcb->write_data_transfer_length = 0;
+ ioarcb->read_data_transfer_length = 0;
+ ioarcb->write_ioadl_len = 0;
+ ioarcb->read_ioadl_len = 0;
+ ioasa->ioasc = 0;
+ ioasa->residual_data_len = 0;
+}
+
+/**
+ * ipr_erp_request_sense - Send request sense to a device
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a request sense to a device as a result
+ * of a check condition.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
+ ipr_erp_done(ipr_cmd);
+ return;
+ }
+
+ ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
+
+ cmd_pkt->request_type = IPR_RQTYPE_SCSICDB;
+ cmd_pkt->cdb[0] = REQUEST_SENSE;
+ cmd_pkt->cdb[4] = SCSI_SENSE_BUFFERSIZE;
+ cmd_pkt->flags_hi |= IPR_FLAGS_HI_SYNC_OVERRIDE;
+ cmd_pkt->flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
+ cmd_pkt->timeout = cpu_to_be16(IPR_REQUEST_SENSE_TIMEOUT / HZ);
+
+ ipr_cmd->ioadl[0].flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | SCSI_SENSE_BUFFERSIZE);
+ ipr_cmd->ioadl[0].address =
+ cpu_to_be32(ipr_cmd->sense_buffer_dma);
+
+ ipr_cmd->ioarcb.read_ioadl_len =
+ cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ipr_cmd->ioarcb.read_data_transfer_length =
+ cpu_to_be32(SCSI_SENSE_BUFFERSIZE);
+
+ ipr_do_req(ipr_cmd, ipr_erp_done, ipr_timeout,
+ IPR_REQUEST_SENSE_TIMEOUT * 2);
+}
+
+/**
+ * ipr_erp_cancel_all - Send cancel all to a device
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a cancel all to a device to clear the
+ * queue. If we are running TCQ on the device, QERR is set to 1,
+ * which means all outstanding ops have been dropped on the floor.
+ * Cancel all will return them to us.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_erp_cancel_all(struct ipr_cmnd *ipr_cmd)
+{
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+ struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
+ struct ipr_cmd_pkt *cmd_pkt;
+
+ res->in_erp = 1;
+
+ ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
+
+ if (!scsi_get_tag_type(scsi_cmd->device)) {
+ ipr_erp_request_sense(ipr_cmd);
+ return;
+ }
+
+ cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
+ cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
+ cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS;
+
+ ipr_do_req(ipr_cmd, ipr_erp_request_sense, ipr_timeout,
+ IPR_CANCEL_ALL_TIMEOUT);
+}
+
+/**
+ * ipr_dump_ioasa - Dump contents of IOASA
+ * @ioa_cfg: ioa config struct
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is invoked by the interrupt handler when ops
+ * fail. It will log the IOASA if appropriate. Only called
+ * for GPDD ops.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_dump_ioasa(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_cmnd *ipr_cmd)
+{
+ int i;
+ u16 data_len;
+ u32 ioasc;
+ struct ipr_ioasa *ioasa = &ipr_cmd->ioasa;
+ __be32 *ioasa_data = (__be32 *)ioasa;
+ int error_index;
+
+ ioasc = be32_to_cpu(ioasa->ioasc) & IPR_IOASC_IOASC_MASK;
+
+ if (0 == ioasc)
+ return;
+
+ if (ioa_cfg->log_level < IPR_DEFAULT_LOG_LEVEL)
+ return;
+
+ error_index = ipr_get_error(ioasc);
+
+ if (ioa_cfg->log_level < IPR_MAX_LOG_LEVEL) {
+ /* Don't log an error if the IOA already logged one */
+ if (ioasa->ilid != 0)
+ return;
+
+ if (ipr_error_table[error_index].log_ioasa == 0)
+ return;
+ }
+
+ ipr_sdev_err(ipr_cmd->scsi_cmd->device, "%s\n",
+ ipr_error_table[error_index].error);
+
+ if ((ioasa->u.gpdd.end_state <= ARRAY_SIZE(ipr_gpdd_dev_end_states)) &&
+ (ioasa->u.gpdd.bus_phase <= ARRAY_SIZE(ipr_gpdd_dev_bus_phases))) {
+ ipr_sdev_err(ipr_cmd->scsi_cmd->device,
+ "Device End state: %s Phase: %s\n",
+ ipr_gpdd_dev_end_states[ioasa->u.gpdd.end_state],
+ ipr_gpdd_dev_bus_phases[ioasa->u.gpdd.bus_phase]);
+ }
+
+ if (sizeof(struct ipr_ioasa) < be16_to_cpu(ioasa->ret_stat_len))
+ data_len = sizeof(struct ipr_ioasa);
+ else
+ data_len = be16_to_cpu(ioasa->ret_stat_len);
+
+ ipr_err("IOASA Dump:\n");
+
+ for (i = 0; i < data_len / 4; i += 4) {
+ ipr_err("%08X: %08X %08X %08X %08X\n", i*4,
+ be32_to_cpu(ioasa_data[i]),
+ be32_to_cpu(ioasa_data[i+1]),
+ be32_to_cpu(ioasa_data[i+2]),
+ be32_to_cpu(ioasa_data[i+3]));
+ }
+}
+
+/**
+ * ipr_gen_sense - Generate SCSI sense data from an IOASA
+ * @ioasa: IOASA
+ * @sense_buf: sense data buffer
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_gen_sense(struct ipr_cmnd *ipr_cmd)
+{
+ u32 failing_lba;
+ u8 *sense_buf = ipr_cmd->scsi_cmd->sense_buffer;
+ struct ipr_resource_entry *res = ipr_cmd->scsi_cmd->device->hostdata;
+ struct ipr_ioasa *ioasa = &ipr_cmd->ioasa;
+ u32 ioasc = be32_to_cpu(ioasa->ioasc);
+
+ memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
+
+ if (ioasc >= IPR_FIRST_DRIVER_IOASC)
+ return;
+
+ ipr_cmd->scsi_cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ if (ipr_is_vset_device(res) &&
+ ioasc == IPR_IOASC_MED_DO_NOT_REALLOC &&
+ ioasa->u.vset.failing_lba_hi != 0) {
+ sense_buf[0] = 0x72;
+ sense_buf[1] = IPR_IOASC_SENSE_KEY(ioasc);
+ sense_buf[2] = IPR_IOASC_SENSE_CODE(ioasc);
+ sense_buf[3] = IPR_IOASC_SENSE_QUAL(ioasc);
+
+ sense_buf[7] = 12;
+ sense_buf[8] = 0;
+ sense_buf[9] = 0x0A;
+ sense_buf[10] = 0x80;
+
+ failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_hi);
+
+ sense_buf[12] = (failing_lba & 0xff000000) >> 24;
+ sense_buf[13] = (failing_lba & 0x00ff0000) >> 16;
+ sense_buf[14] = (failing_lba & 0x0000ff00) >> 8;
+ sense_buf[15] = failing_lba & 0x000000ff;
+
+ failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_lo);
+
+ sense_buf[16] = (failing_lba & 0xff000000) >> 24;
+ sense_buf[17] = (failing_lba & 0x00ff0000) >> 16;
+ sense_buf[18] = (failing_lba & 0x0000ff00) >> 8;
+ sense_buf[19] = failing_lba & 0x000000ff;
+ } else {
+ sense_buf[0] = 0x70;
+ sense_buf[2] = IPR_IOASC_SENSE_KEY(ioasc);
+ sense_buf[12] = IPR_IOASC_SENSE_CODE(ioasc);
+ sense_buf[13] = IPR_IOASC_SENSE_QUAL(ioasc);
+
+ /* Illegal request */
+ if ((IPR_IOASC_SENSE_KEY(ioasc) == 0x05) &&
+ (be32_to_cpu(ioasa->ioasc_specific) & IPR_FIELD_POINTER_VALID)) {
+ sense_buf[7] = 10; /* additional length */
+
+ /* IOARCB was in error */
+ if (IPR_IOASC_SENSE_CODE(ioasc) == 0x24)
+ sense_buf[15] = 0xC0;
+ else /* Parameter data was invalid */
+ sense_buf[15] = 0x80;
+
+ sense_buf[16] =
+ ((IPR_FIELD_POINTER_MASK &
+ be32_to_cpu(ioasa->ioasc_specific)) >> 8) & 0xff;
+ sense_buf[17] =
+ (IPR_FIELD_POINTER_MASK &
+ be32_to_cpu(ioasa->ioasc_specific)) & 0xff;
+ } else {
+ if (ioasc == IPR_IOASC_MED_DO_NOT_REALLOC) {
+ if (ipr_is_vset_device(res))
+ failing_lba = be32_to_cpu(ioasa->u.vset.failing_lba_lo);
+ else
+ failing_lba = be32_to_cpu(ioasa->u.dasd.failing_lba);
+
+ sense_buf[0] |= 0x80; /* Or in the Valid bit */
+ sense_buf[3] = (failing_lba & 0xff000000) >> 24;
+ sense_buf[4] = (failing_lba & 0x00ff0000) >> 16;
+ sense_buf[5] = (failing_lba & 0x0000ff00) >> 8;
+ sense_buf[6] = failing_lba & 0x000000ff;
+ }
+
+ sense_buf[7] = 6; /* additional length */
+ }
+ }
+}
+
+/**
+ * ipr_erp_start - Process an error response for a SCSI op
+ * @ioa_cfg: ioa config struct
+ * @ipr_cmd: ipr command struct
+ *
+ * This function determines whether or not to initiate ERP
+ * on the affected device.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_cmnd *ipr_cmd)
+{
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+ struct ipr_resource_entry *res = scsi_cmd->device->hostdata;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ if (!res) {
+ ipr_scsi_eh_done(ipr_cmd);
+ return;
+ }
+
+ if (ipr_is_gscsi(res))
+ ipr_dump_ioasa(ioa_cfg, ipr_cmd);
+ else
+ ipr_gen_sense(ipr_cmd);
+
+ switch (ioasc & IPR_IOASC_IOASC_MASK) {
+ case IPR_IOASC_ABORTED_CMD_TERM_BY_HOST:
+ scsi_cmd->result |= (DID_IMM_RETRY << 16);
+ break;
+ case IPR_IOASC_IR_RESOURCE_HANDLE:
+ scsi_cmd->result |= (DID_NO_CONNECT << 16);
+ break;
+ case IPR_IOASC_HW_SEL_TIMEOUT:
+ scsi_cmd->result |= (DID_NO_CONNECT << 16);
+ res->needs_sync_complete = 1;
+ break;
+ case IPR_IOASC_SYNC_REQUIRED:
+ if (!res->in_erp)
+ res->needs_sync_complete = 1;
+ scsi_cmd->result |= (DID_IMM_RETRY << 16);
+ break;
+ case IPR_IOASC_MED_DO_NOT_REALLOC: /* prevent retries */
+ scsi_cmd->result |= (DID_PASSTHROUGH << 16);
+ break;
+ case IPR_IOASC_BUS_WAS_RESET:
+ case IPR_IOASC_BUS_WAS_RESET_BY_OTHER:
+ /*
+ * Report the bus reset and ask for a retry. The device
+ * will give CC/UA the next command.
+ */
+ if (!res->resetting_device)
+ scsi_report_bus_reset(ioa_cfg->host, scsi_cmd->device->channel);
+ scsi_cmd->result |= (DID_ERROR << 16);
+ res->needs_sync_complete = 1;
+ break;
+ case IPR_IOASC_HW_DEV_BUS_STATUS:
+ scsi_cmd->result |= IPR_IOASC_SENSE_STATUS(ioasc);
+ if (IPR_IOASC_SENSE_STATUS(ioasc) == SAM_STAT_CHECK_CONDITION) {
+ ipr_erp_cancel_all(ipr_cmd);
+ return;
+ }
+ res->needs_sync_complete = 1;
+ break;
+ case IPR_IOASC_NR_INIT_CMD_REQUIRED:
+ break;
+ default:
+ scsi_cmd->result |= (DID_ERROR << 16);
+ if (!ipr_is_vset_device(res))
+ res->needs_sync_complete = 1;
+ break;
+ }
+
+ ipr_unmap_sglist(ioa_cfg, ipr_cmd);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ scsi_cmd->scsi_done(scsi_cmd);
+}
+
+/**
+ * ipr_scsi_done - mid-layer done function
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is invoked by the interrupt handler for
+ * ops generated by the SCSI mid-layer
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_scsi_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ scsi_cmd->resid = be32_to_cpu(ipr_cmd->ioasa.residual_data_len);
+
+ if (likely(IPR_IOASC_SENSE_KEY(ioasc) == 0)) {
+ ipr_unmap_sglist(ioa_cfg, ipr_cmd);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ scsi_cmd->scsi_done(scsi_cmd);
+ } else
+ ipr_erp_start(ioa_cfg, ipr_cmd);
+}
+
+/**
+ * ipr_save_ioafp_mode_select - Save adapters mode select data
+ * @ioa_cfg: ioa config struct
+ * @scsi_cmd: scsi command struct
+ *
+ * This function saves mode select data for the adapter to
+ * use following an adapter reset.
+ *
+ * Return value:
+ * 0 on success / SCSI_MLQUEUE_HOST_BUSY on failure
+ **/
+static int ipr_save_ioafp_mode_select(struct ipr_ioa_cfg *ioa_cfg,
+ struct scsi_cmnd *scsi_cmd)
+{
+ if (!ioa_cfg->saved_mode_pages) {
+ ioa_cfg->saved_mode_pages = kmalloc(sizeof(struct ipr_mode_pages),
+ GFP_ATOMIC);
+ if (!ioa_cfg->saved_mode_pages) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "IOA mode select buffer allocation failed\n");
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ }
+
+ memcpy(ioa_cfg->saved_mode_pages, scsi_cmd->buffer, scsi_cmd->cmnd[4]);
+ ioa_cfg->saved_mode_page_len = scsi_cmd->cmnd[4];
+ return 0;
+}
+
+/**
+ * ipr_queuecommand - Queue a mid-layer request
+ * @scsi_cmd: scsi command struct
+ * @done: done function
+ *
+ * This function queues a request generated by the mid-layer.
+ *
+ * Return value:
+ * 0 on success
+ * SCSI_MLQUEUE_DEVICE_BUSY if device is busy
+ * SCSI_MLQUEUE_HOST_BUSY if host is busy
+ **/
+static int ipr_queuecommand(struct scsi_cmnd *scsi_cmd,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct ipr_ioa_cfg *ioa_cfg;
+ struct ipr_resource_entry *res;
+ struct ipr_ioarcb *ioarcb;
+ struct ipr_cmnd *ipr_cmd;
+ int rc = 0;
+
+ scsi_cmd->scsi_done = done;
+ ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata;
+ res = scsi_cmd->device->hostdata;
+ scsi_cmd->result = (DID_OK << 16);
+
+ /*
+ * We are currently blocking all devices due to a host reset
+ * We have told the host to stop giving us new requests, but
+ * ERP ops don't count. FIXME
+ */
+ if (unlikely(!ioa_cfg->allow_cmds && !ioa_cfg->ioa_is_dead))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ /*
+ * FIXME - Create scsi_set_host_offline interface
+ * and the ioa_is_dead check can be removed
+ */
+ if (unlikely(ioa_cfg->ioa_is_dead || !res)) {
+ memset(scsi_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ scsi_cmd->result = (DID_NO_CONNECT << 16);
+ scsi_cmd->scsi_done(scsi_cmd);
+ return 0;
+ }
+
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ioarcb = &ipr_cmd->ioarcb;
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+
+ memcpy(ioarcb->cmd_pkt.cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len);
+ ipr_cmd->scsi_cmd = scsi_cmd;
+ ioarcb->res_handle = res->cfgte.res_handle;
+ ipr_cmd->done = ipr_scsi_done;
+ ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_PHYS_LOC(res->cfgte.res_addr));
+
+ if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) {
+ if (scsi_cmd->underflow == 0)
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
+
+ if (res->needs_sync_complete) {
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_SYNC_COMPLETE;
+ res->needs_sync_complete = 0;
+ }
+
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR;
+ ioarcb->cmd_pkt.flags_lo |= ipr_get_task_attributes(scsi_cmd);
+ }
+
+ if (scsi_cmd->cmnd[0] >= 0xC0 &&
+ (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE))
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+
+ if (ipr_is_ioa_resource(res) && scsi_cmd->cmnd[0] == MODE_SELECT)
+ rc = ipr_save_ioafp_mode_select(ioa_cfg, scsi_cmd);
+
+ if (likely(rc == 0))
+ rc = ipr_build_ioadl(ioa_cfg, ipr_cmd);
+
+ if (likely(rc == 0)) {
+ mb();
+ writel(be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr),
+ ioa_cfg->regs.ioarrin_reg);
+ } else {
+ list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * ipr_info - Get information about the card/driver
+ * @scsi_host: scsi host struct
+ *
+ * Return value:
+ * pointer to buffer with description string
+ **/
+static const char * ipr_ioa_info(struct Scsi_Host *host)
+{
+ static char buffer[512];
+ struct ipr_ioa_cfg *ioa_cfg;
+ unsigned long lock_flags = 0;
+
+ ioa_cfg = (struct ipr_ioa_cfg *) host->hostdata;
+
+ spin_lock_irqsave(host->host_lock, lock_flags);
+ sprintf(buffer, "IBM %X Storage Adapter", ioa_cfg->type);
+ spin_unlock_irqrestore(host->host_lock, lock_flags);
+
+ return buffer;
+}
+
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .name = "IPR",
+ .info = ipr_ioa_info,
+ .queuecommand = ipr_queuecommand,
+ .eh_abort_handler = ipr_eh_abort,
+ .eh_device_reset_handler = ipr_eh_dev_reset,
+ .eh_host_reset_handler = ipr_eh_host_reset,
+ .slave_alloc = ipr_slave_alloc,
+ .slave_configure = ipr_slave_configure,
+ .slave_destroy = ipr_slave_destroy,
+ .change_queue_depth = ipr_change_queue_depth,
+ .change_queue_type = ipr_change_queue_type,
+ .bios_param = ipr_biosparam,
+ .can_queue = IPR_MAX_COMMANDS,
+ .this_id = -1,
+ .sg_tablesize = IPR_MAX_SGLIST,
+ .max_sectors = IPR_IOA_MAX_SECTORS,
+ .cmd_per_lun = IPR_MAX_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = ipr_ioa_attrs,
+ .sdev_attrs = ipr_dev_attrs,
+ .proc_name = IPR_NAME
+};
+
+#ifdef CONFIG_PPC_PSERIES
+static const u16 ipr_blocked_processors[] = {
+ PV_NORTHSTAR,
+ PV_PULSAR,
+ PV_POWER4,
+ PV_ICESTAR,
+ PV_SSTAR,
+ PV_POWER4p,
+ PV_630,
+ PV_630p
+};
+
+/**
+ * ipr_invalid_adapter - Determine if this adapter is supported on this hardware
+ * @ioa_cfg: ioa cfg struct
+ *
+ * Adapters that use Gemstone revision < 3.1 do not work reliably on
+ * certain pSeries hardware. This function determines if the given
+ * adapter is in one of these confgurations or not.
+ *
+ * Return value:
+ * 1 if adapter is not supported / 0 if adapter is supported
+ **/
+static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
+{
+ u8 rev_id;
+ int i;
+
+ if (ioa_cfg->type == 0x5702) {
+ if (pci_read_config_byte(ioa_cfg->pdev, PCI_REVISION_ID,
+ &rev_id) == PCIBIOS_SUCCESSFUL) {
+ if (rev_id < 4) {
+ for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++){
+ if (__is_processor(ipr_blocked_processors[i]))
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+#else
+#define ipr_invalid_adapter(ioa_cfg) 0
+#endif
+
+/**
+ * ipr_ioa_bringdown_done - IOA bring down completion.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function processes the completion of an adapter bring down.
+ * It wakes any reset sleepers.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioa_bringdown_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+ ioa_cfg->in_reset_reload = 0;
+ ioa_cfg->reset_retries = 0;
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ wake_up_all(&ioa_cfg->reset_wait_q);
+
+ spin_unlock_irq(ioa_cfg->host->host_lock);
+ scsi_unblock_requests(ioa_cfg->host);
+ spin_lock_irq(ioa_cfg->host->host_lock);
+ LEAVE;
+
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_ioa_reset_done - IOA reset completion.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function processes the completion of an adapter reset.
+ * It schedules any necessary mid-layer add/removes and
+ * wakes any reset sleepers.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_resource_entry *res;
+ struct ipr_hostrcb *hostrcb, *temp;
+ int i = 0;
+
+ ENTER;
+ ioa_cfg->in_reset_reload = 0;
+ ioa_cfg->allow_cmds = 1;
+ ioa_cfg->reset_cmd = NULL;
+
+ list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
+ if (ioa_cfg->allow_ml_add_del && (res->add_to_ml || res->del_from_ml)) {
+ ipr_trace;
+ break;
+ }
+ }
+ schedule_work(&ioa_cfg->work_q);
+
+ list_for_each_entry_safe(hostrcb, temp, &ioa_cfg->hostrcb_free_q, queue) {
+ list_del(&hostrcb->queue);
+ if (i++ < IPR_NUM_LOG_HCAMS)
+ ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb);
+ else
+ ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb);
+ }
+
+ dev_info(&ioa_cfg->pdev->dev, "IOA initialized.\n");
+
+ ioa_cfg->reset_retries = 0;
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ wake_up_all(&ioa_cfg->reset_wait_q);
+
+ spin_unlock_irq(ioa_cfg->host->host_lock);
+ scsi_unblock_requests(ioa_cfg->host);
+ spin_lock_irq(ioa_cfg->host->host_lock);
+
+ if (!ioa_cfg->allow_cmds)
+ scsi_block_requests(ioa_cfg->host);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_set_sup_dev_dflt - Initialize a Set Supported Device buffer
+ * @supported_dev: supported device struct
+ * @vpids: vendor product id struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_set_sup_dev_dflt(struct ipr_supported_device *supported_dev,
+ struct ipr_std_inq_vpids *vpids)
+{
+ memset(supported_dev, 0, sizeof(struct ipr_supported_device));
+ memcpy(&supported_dev->vpids, vpids, sizeof(struct ipr_std_inq_vpids));
+ supported_dev->num_records = 1;
+ supported_dev->data_length =
+ cpu_to_be16(sizeof(struct ipr_supported_device));
+ supported_dev->reserved = 0;
+}
+
+/**
+ * ipr_set_supported_devs - Send Set Supported Devices for a device
+ * @ipr_cmd: ipr command struct
+ *
+ * This function send a Set Supported Devices to the adapter
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_set_supported_devs(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_supported_device *supp_dev = &ioa_cfg->vpd_cbs->supp_dev;
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_resource_entry *res = ipr_cmd->u.res;
+
+ ipr_cmd->job_step = ipr_ioa_reset_done;
+
+ list_for_each_entry_continue(res, &ioa_cfg->used_res_q, queue) {
+ if (!ipr_is_af_dasd_device(res))
+ continue;
+
+ ipr_cmd->u.res = res;
+ ipr_set_sup_dev_dflt(supp_dev, &res->cfgte.std_inq_data.vpids);
+
+ ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+
+ ioarcb->cmd_pkt.cdb[0] = IPR_SET_SUPPORTED_DEVICES;
+ ioarcb->cmd_pkt.cdb[7] = (sizeof(struct ipr_supported_device) >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[8] = sizeof(struct ipr_supported_device) & 0xff;
+
+ ioadl->flags_and_data_len = cpu_to_be32(IPR_IOADL_FLAGS_WRITE_LAST |
+ sizeof(struct ipr_supported_device));
+ ioadl->address = cpu_to_be32(ioa_cfg->vpd_cbs_dma +
+ offsetof(struct ipr_misc_cbs, supp_dev));
+ ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ioarcb->write_data_transfer_length =
+ cpu_to_be32(sizeof(struct ipr_supported_device));
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
+ IPR_SET_SUP_DEVICE_TIMEOUT);
+
+ ipr_cmd->job_step = ipr_set_supported_devs;
+ return IPR_RC_JOB_RETURN;
+ }
+
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
+ * ipr_get_mode_page - Locate specified mode page
+ * @mode_pages: mode page buffer
+ * @page_code: page code to find
+ * @len: minimum required length for mode page
+ *
+ * Return value:
+ * pointer to mode page / NULL on failure
+ **/
+static void *ipr_get_mode_page(struct ipr_mode_pages *mode_pages,
+ u32 page_code, u32 len)
+{
+ struct ipr_mode_page_hdr *mode_hdr;
+ u32 page_length;
+ u32 length;
+
+ if (!mode_pages || (mode_pages->hdr.length == 0))
+ return NULL;
+
+ length = (mode_pages->hdr.length + 1) - 4 - mode_pages->hdr.block_desc_len;
+ mode_hdr = (struct ipr_mode_page_hdr *)
+ (mode_pages->data + mode_pages->hdr.block_desc_len);
+
+ while (length) {
+ if (IPR_GET_MODE_PAGE_CODE(mode_hdr) == page_code) {
+ if (mode_hdr->page_length >= (len - sizeof(struct ipr_mode_page_hdr)))
+ return mode_hdr;
+ break;
+ } else {
+ page_length = (sizeof(struct ipr_mode_page_hdr) +
+ mode_hdr->page_length);
+ length -= page_length;
+ mode_hdr = (struct ipr_mode_page_hdr *)
+ ((unsigned long)mode_hdr + page_length);
+ }
+ }
+ return NULL;
+}
+
+/**
+ * ipr_check_term_power - Check for term power errors
+ * @ioa_cfg: ioa config struct
+ * @mode_pages: IOAFP mode pages buffer
+ *
+ * Check the IOAFP's mode page 28 for term power errors
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_check_term_power(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_mode_pages *mode_pages)
+{
+ int i;
+ int entry_length;
+ struct ipr_dev_bus_entry *bus;
+ struct ipr_mode_page28 *mode_page;
+
+ mode_page = ipr_get_mode_page(mode_pages, 0x28,
+ sizeof(struct ipr_mode_page28));
+
+ entry_length = mode_page->entry_length;
+
+ bus = mode_page->bus;
+
+ for (i = 0; i < mode_page->num_entries; i++) {
+ if (bus->flags & IPR_SCSI_ATTR_NO_TERM_PWR) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Term power is absent on scsi bus %d\n",
+ bus->res_addr.bus);
+ }
+
+ bus = (struct ipr_dev_bus_entry *)((char *)bus + entry_length);
+ }
+}
+
+/**
+ * ipr_scsi_bus_speed_limit - Limit the SCSI speed based on SES table
+ * @ioa_cfg: ioa config struct
+ *
+ * Looks through the config table checking for SES devices. If
+ * the SES device is in the SES table indicating a maximum SCSI
+ * bus speed, the speed is limited for the bus.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg)
+{
+ u32 max_xfer_rate;
+ int i;
+
+ for (i = 0; i < IPR_MAX_NUM_BUSES; i++) {
+ max_xfer_rate = ipr_get_max_scsi_speed(ioa_cfg, i,
+ ioa_cfg->bus_attr[i].bus_width);
+
+ if (max_xfer_rate < ioa_cfg->bus_attr[i].max_xfer_rate)
+ ioa_cfg->bus_attr[i].max_xfer_rate = max_xfer_rate;
+ }
+}
+
+/**
+ * ipr_modify_ioafp_mode_page_28 - Modify IOAFP Mode Page 28
+ * @ioa_cfg: ioa config struct
+ * @mode_pages: mode page 28 buffer
+ *
+ * Updates mode page 28 based on driver configuration
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg,
+ struct ipr_mode_pages *mode_pages)
+{
+ int i, entry_length;
+ struct ipr_dev_bus_entry *bus;
+ struct ipr_bus_attributes *bus_attr;
+ struct ipr_mode_page28 *mode_page;
+
+ mode_page = ipr_get_mode_page(mode_pages, 0x28,
+ sizeof(struct ipr_mode_page28));
+
+ entry_length = mode_page->entry_length;
+
+ /* Loop for each device bus entry */
+ for (i = 0, bus = mode_page->bus;
+ i < mode_page->num_entries;
+ i++, bus = (struct ipr_dev_bus_entry *)((u8 *)bus + entry_length)) {
+ if (bus->res_addr.bus > IPR_MAX_NUM_BUSES) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Invalid resource address reported: 0x%08X\n",
+ IPR_GET_PHYS_LOC(bus->res_addr));
+ continue;
+ }
+
+ bus_attr = &ioa_cfg->bus_attr[i];
+ bus->extended_reset_delay = IPR_EXTENDED_RESET_DELAY;
+ bus->bus_width = bus_attr->bus_width;
+ bus->max_xfer_rate = cpu_to_be32(bus_attr->max_xfer_rate);
+ bus->flags &= ~IPR_SCSI_ATTR_QAS_MASK;
+ if (bus_attr->qas_enabled)
+ bus->flags |= IPR_SCSI_ATTR_ENABLE_QAS;
+ else
+ bus->flags |= IPR_SCSI_ATTR_DISABLE_QAS;
+ }
+}
+
+/**
+ * ipr_build_mode_select - Build a mode select command
+ * @ipr_cmd: ipr command struct
+ * @res_handle: resource handle to send command to
+ * @parm: Byte 2 of Mode Sense command
+ * @dma_addr: DMA buffer address
+ * @xfer_len: data transfer length
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_build_mode_select(struct ipr_cmnd *ipr_cmd,
+ __be32 res_handle, u8 parm, u32 dma_addr,
+ u8 xfer_len)
+{
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+
+ ioarcb->res_handle = res_handle;
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
+ ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ;
+ ioarcb->cmd_pkt.cdb[0] = MODE_SELECT;
+ ioarcb->cmd_pkt.cdb[1] = parm;
+ ioarcb->cmd_pkt.cdb[4] = xfer_len;
+
+ ioadl->flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_WRITE_LAST | xfer_len);
+ ioadl->address = cpu_to_be32(dma_addr);
+ ioarcb->write_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ioarcb->write_data_transfer_length = cpu_to_be32(xfer_len);
+}
+
+/**
+ * ipr_ioafp_mode_select_page28 - Issue Mode Select Page 28 to IOA
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sets up the SCSI bus attributes and sends
+ * a Mode Select for Page 28 to activate them.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_mode_select_page28(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_mode_pages *mode_pages = &ioa_cfg->vpd_cbs->mode_pages;
+ int length;
+
+ ENTER;
+ if (ioa_cfg->saved_mode_pages) {
+ memcpy(mode_pages, ioa_cfg->saved_mode_pages,
+ ioa_cfg->saved_mode_page_len);
+ length = ioa_cfg->saved_mode_page_len;
+ } else {
+ ipr_scsi_bus_speed_limit(ioa_cfg);
+ ipr_check_term_power(ioa_cfg, mode_pages);
+ ipr_modify_ioafp_mode_page_28(ioa_cfg, mode_pages);
+ length = mode_pages->hdr.length + 1;
+ mode_pages->hdr.length = 0;
+ }
+
+ ipr_build_mode_select(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE), 0x11,
+ ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, mode_pages),
+ length);
+
+ ipr_cmd->job_step = ipr_set_supported_devs;
+ ipr_cmd->u.res = list_entry(ioa_cfg->used_res_q.next,
+ struct ipr_resource_entry, queue);
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_build_mode_sense - Builds a mode sense command
+ * @ipr_cmd: ipr command struct
+ * @res: resource entry struct
+ * @parm: Byte 2 of mode sense command
+ * @dma_addr: DMA address of mode sense buffer
+ * @xfer_len: Size of DMA buffer
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_build_mode_sense(struct ipr_cmnd *ipr_cmd,
+ __be32 res_handle,
+ u8 parm, u32 dma_addr, u8 xfer_len)
+{
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+
+ ioarcb->res_handle = res_handle;
+ ioarcb->cmd_pkt.cdb[0] = MODE_SENSE;
+ ioarcb->cmd_pkt.cdb[2] = parm;
+ ioarcb->cmd_pkt.cdb[4] = xfer_len;
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
+
+ ioadl->flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | xfer_len);
+ ioadl->address = cpu_to_be32(dma_addr);
+ ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ioarcb->read_data_transfer_length = cpu_to_be32(xfer_len);
+}
+
+/**
+ * ipr_ioafp_mode_sense_page28 - Issue Mode Sense Page 28 to IOA
+ * @ipr_cmd: ipr command struct
+ *
+ * This function send a Page 28 mode sense to the IOA to
+ * retrieve SCSI bus attributes.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_mode_sense_page28(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+ ipr_build_mode_sense(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE),
+ 0x28, ioa_cfg->vpd_cbs_dma +
+ offsetof(struct ipr_misc_cbs, mode_pages),
+ sizeof(struct ipr_mode_pages));
+
+ ipr_cmd->job_step = ipr_ioafp_mode_select_page28;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_init_res_table - Initialize the resource table
+ * @ipr_cmd: ipr command struct
+ *
+ * This function looks through the existing resource table, comparing
+ * it with the config table. This function will take care of old/new
+ * devices and schedule adding/removing them from the mid-layer
+ * as appropriate.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE
+ **/
+static int ipr_init_res_table(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_resource_entry *res, *temp;
+ struct ipr_config_table_entry *cfgte;
+ int found, i;
+ LIST_HEAD(old_res);
+
+ ENTER;
+ if (ioa_cfg->cfg_table->hdr.flags & IPR_UCODE_DOWNLOAD_REQ)
+ dev_err(&ioa_cfg->pdev->dev, "Microcode download required\n");
+
+ list_for_each_entry_safe(res, temp, &ioa_cfg->used_res_q, queue)
+ list_move_tail(&res->queue, &old_res);
+
+ for (i = 0; i < ioa_cfg->cfg_table->hdr.num_entries; i++) {
+ cfgte = &ioa_cfg->cfg_table->dev[i];
+ found = 0;
+
+ list_for_each_entry_safe(res, temp, &old_res, queue) {
+ if (!memcmp(&res->cfgte.res_addr,
+ &cfgte->res_addr, sizeof(cfgte->res_addr))) {
+ list_move_tail(&res->queue, &ioa_cfg->used_res_q);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (list_empty(&ioa_cfg->free_res_q)) {
+ dev_err(&ioa_cfg->pdev->dev, "Too many devices attached\n");
+ break;
+ }
+
+ found = 1;
+ res = list_entry(ioa_cfg->free_res_q.next,
+ struct ipr_resource_entry, queue);
+ list_move_tail(&res->queue, &ioa_cfg->used_res_q);
+ ipr_init_res_entry(res);
+ res->add_to_ml = 1;
+ }
+
+ if (found)
+ memcpy(&res->cfgte, cfgte, sizeof(struct ipr_config_table_entry));
+ }
+
+ list_for_each_entry_safe(res, temp, &old_res, queue) {
+ if (res->sdev) {
+ res->del_from_ml = 1;
+ res->sdev->hostdata = NULL;
+ list_move_tail(&res->queue, &ioa_cfg->used_res_q);
+ } else {
+ list_move_tail(&res->queue, &ioa_cfg->free_res_q);
+ }
+ }
+
+ ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
+
+ LEAVE;
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
+ * ipr_ioafp_query_ioa_cfg - Send a Query IOA Config to the adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a Query IOA Configuration command
+ * to the adapter to retrieve the IOA configuration table.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_query_ioa_cfg(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+ struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
+
+ ENTER;
+ dev_info(&ioa_cfg->pdev->dev, "Adapter firmware version: %02X%02X%02X%02X\n",
+ ucode_vpd->major_release, ucode_vpd->card_type,
+ ucode_vpd->minor_release[0], ucode_vpd->minor_release[1]);
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+ ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+
+ ioarcb->cmd_pkt.cdb[0] = IPR_QUERY_IOA_CONFIG;
+ ioarcb->cmd_pkt.cdb[7] = (sizeof(struct ipr_config_table) >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[8] = sizeof(struct ipr_config_table) & 0xff;
+
+ ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ioarcb->read_data_transfer_length =
+ cpu_to_be32(sizeof(struct ipr_config_table));
+
+ ioadl->address = cpu_to_be32(ioa_cfg->cfg_table_dma);
+ ioadl->flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | sizeof(struct ipr_config_table));
+
+ ipr_cmd->job_step = ipr_init_res_table;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_ioafp_inquiry - Send an Inquiry to the adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * This utility function sends an inquiry to the adapter.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_ioafp_inquiry(struct ipr_cmnd *ipr_cmd, u8 flags, u8 page,
+ u32 dma_addr, u8 xfer_len)
+{
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+ struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
+
+ ENTER;
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
+ ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+
+ ioarcb->cmd_pkt.cdb[0] = INQUIRY;
+ ioarcb->cmd_pkt.cdb[1] = flags;
+ ioarcb->cmd_pkt.cdb[2] = page;
+ ioarcb->cmd_pkt.cdb[4] = xfer_len;
+
+ ioarcb->read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc));
+ ioarcb->read_data_transfer_length = cpu_to_be32(xfer_len);
+
+ ioadl->address = cpu_to_be32(dma_addr);
+ ioadl->flags_and_data_len =
+ cpu_to_be32(IPR_IOADL_FLAGS_READ_LAST | xfer_len);
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+ LEAVE;
+}
+
+/**
+ * ipr_ioafp_page3_inquiry - Send a Page 3 Inquiry to the adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a Page 3 inquiry to the adapter
+ * to retrieve software VPD information.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_page3_inquiry(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ char type[5];
+
+ ENTER;
+
+ /* Grab the type out of the VPD and store it away */
+ memcpy(type, ioa_cfg->vpd_cbs->ioa_vpd.std_inq_data.vpids.product_id, 4);
+ type[4] = '\0';
+ ioa_cfg->type = simple_strtoul((char *)type, NULL, 16);
+
+ ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg;
+
+ ipr_ioafp_inquiry(ipr_cmd, 1, 3,
+ ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, page3_data),
+ sizeof(struct ipr_inquiry_page3));
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_ioafp_std_inquiry - Send a Standard Inquiry to the adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function sends a standard inquiry to the adapter.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_std_inquiry(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ ENTER;
+ ipr_cmd->job_step = ipr_ioafp_page3_inquiry;
+
+ ipr_ioafp_inquiry(ipr_cmd, 0, 0,
+ ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, ioa_vpd),
+ sizeof(struct ipr_ioa_vpd));
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_ioafp_indentify_hrrq - Send Identify Host RRQ.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function send an Identify Host Request Response Queue
+ * command to establish the HRRQ with the adapter.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_indentify_hrrq(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
+
+ ENTER;
+ dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n");
+
+ ioarcb->cmd_pkt.cdb[0] = IPR_ID_HOST_RR_Q;
+ ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+
+ ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+ ioarcb->cmd_pkt.cdb[2] =
+ ((u32) ioa_cfg->host_rrq_dma >> 24) & 0xff;
+ ioarcb->cmd_pkt.cdb[3] =
+ ((u32) ioa_cfg->host_rrq_dma >> 16) & 0xff;
+ ioarcb->cmd_pkt.cdb[4] =
+ ((u32) ioa_cfg->host_rrq_dma >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[5] =
+ ((u32) ioa_cfg->host_rrq_dma) & 0xff;
+ ioarcb->cmd_pkt.cdb[7] =
+ ((sizeof(u32) * IPR_NUM_CMD_BLKS) >> 8) & 0xff;
+ ioarcb->cmd_pkt.cdb[8] =
+ (sizeof(u32) * IPR_NUM_CMD_BLKS) & 0xff;
+
+ ipr_cmd->job_step = ipr_ioafp_std_inquiry;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_reset_timer_done - Adapter reset timer function
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function is used in adapter reset processing
+ * for timing events. If the reset_cmd pointer in the IOA
+ * config struct is not this adapter's we are doing nested
+ * resets and fail_all_ops will take care of freeing the
+ * command block.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_reset_timer_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+
+ if (ioa_cfg->reset_cmd == ipr_cmd) {
+ list_del(&ipr_cmd->queue);
+ ipr_cmd->done(ipr_cmd);
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+}
+
+/**
+ * ipr_reset_start_timer - Start a timer for adapter reset job
+ * @ipr_cmd: ipr command struct
+ * @timeout: timeout value
+ *
+ * Description: This function is used in adapter reset processing
+ * for timing events. If the reset_cmd pointer in the IOA
+ * config struct is not this adapter's we are doing nested
+ * resets and fail_all_ops will take care of freeing the
+ * command block.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_reset_start_timer(struct ipr_cmnd *ipr_cmd,
+ unsigned long timeout)
+{
+ list_add_tail(&ipr_cmd->queue, &ipr_cmd->ioa_cfg->pending_q);
+ ipr_cmd->done = ipr_reset_ioa_job;
+
+ ipr_cmd->timer.data = (unsigned long) ipr_cmd;
+ ipr_cmd->timer.expires = jiffies + timeout;
+ ipr_cmd->timer.function = (void (*)(unsigned long))ipr_reset_timer_done;
+ add_timer(&ipr_cmd->timer);
+}
+
+/**
+ * ipr_init_ioa_mem - Initialize ioa_cfg control block
+ * @ioa_cfg: ioa cfg struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_init_ioa_mem(struct ipr_ioa_cfg *ioa_cfg)
+{
+ memset(ioa_cfg->host_rrq, 0, sizeof(u32) * IPR_NUM_CMD_BLKS);
+
+ /* Initialize Host RRQ pointers */
+ ioa_cfg->hrrq_start = ioa_cfg->host_rrq;
+ ioa_cfg->hrrq_end = &ioa_cfg->host_rrq[IPR_NUM_CMD_BLKS - 1];
+ ioa_cfg->hrrq_curr = ioa_cfg->hrrq_start;
+ ioa_cfg->toggle_bit = 1;
+
+ /* Zero out config table */
+ memset(ioa_cfg->cfg_table, 0, sizeof(struct ipr_config_table));
+}
+
+/**
+ * ipr_reset_enable_ioa - Enable the IOA following a reset.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function reinitializes some control blocks and
+ * enables destructive diagnostics on the adapter.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_enable_ioa(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ volatile u32 int_reg;
+
+ ENTER;
+ ipr_cmd->job_step = ipr_ioafp_indentify_hrrq;
+ ipr_init_ioa_mem(ioa_cfg);
+
+ ioa_cfg->allow_interrupts = 1;
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+
+ if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
+ writel((IPR_PCII_ERROR_INTERRUPTS | IPR_PCII_HRRQ_UPDATED),
+ ioa_cfg->regs.clr_interrupt_mask_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
+ return IPR_RC_JOB_CONTINUE;
+ }
+
+ /* Enable destructive diagnostics on IOA */
+ writel(IPR_DOORBELL, ioa_cfg->regs.set_uproc_interrupt_reg);
+
+ writel(IPR_PCII_OPER_INTERRUPTS, ioa_cfg->regs.clr_interrupt_mask_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
+
+ dev_info(&ioa_cfg->pdev->dev, "Initializing IOA.\n");
+
+ ipr_cmd->timer.data = (unsigned long) ipr_cmd;
+ ipr_cmd->timer.expires = jiffies + (ipr_transop_timeout * HZ);
+ ipr_cmd->timer.function = (void (*)(unsigned long))ipr_oper_timeout;
+ ipr_cmd->done = ipr_reset_ioa_job;
+ add_timer(&ipr_cmd->timer);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_reset_wait_for_dump - Wait for a dump to timeout.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function is invoked when an adapter dump has run out
+ * of processing time.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE
+ **/
+static int ipr_reset_wait_for_dump(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ if (ioa_cfg->sdt_state == GET_DUMP)
+ ioa_cfg->sdt_state = ABORT_DUMP;
+
+ ipr_cmd->job_step = ipr_reset_alert;
+
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
+ * ipr_unit_check_no_data - Log a unit check/no data error log
+ * @ioa_cfg: ioa config struct
+ *
+ * Logs an error indicating the adapter unit checked, but for some
+ * reason, we were unable to fetch the unit check buffer.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_unit_check_no_data(struct ipr_ioa_cfg *ioa_cfg)
+{
+ ioa_cfg->errors_logged++;
+ dev_err(&ioa_cfg->pdev->dev, "IOA unit check with no data\n");
+}
+
+/**
+ * ipr_get_unit_check_buffer - Get the unit check buffer from the IOA
+ * @ioa_cfg: ioa config struct
+ *
+ * Fetches the unit check buffer from the adapter by clocking the data
+ * through the mailbox register.
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg)
+{
+ unsigned long mailbox;
+ struct ipr_hostrcb *hostrcb;
+ struct ipr_uc_sdt sdt;
+ int rc, length;
+
+ mailbox = readl(ioa_cfg->ioa_mailbox);
+
+ if (!ipr_sdt_is_fmt2(mailbox)) {
+ ipr_unit_check_no_data(ioa_cfg);
+ return;
+ }
+
+ memset(&sdt, 0, sizeof(struct ipr_uc_sdt));
+ rc = ipr_get_ldump_data_section(ioa_cfg, mailbox, (__be32 *) &sdt,
+ (sizeof(struct ipr_uc_sdt)) / sizeof(__be32));
+
+ if (rc || (be32_to_cpu(sdt.hdr.state) != IPR_FMT2_SDT_READY_TO_USE) ||
+ !(sdt.entry[0].flags & IPR_SDT_VALID_ENTRY)) {
+ ipr_unit_check_no_data(ioa_cfg);
+ return;
+ }
+
+ /* Find length of the first sdt entry (UC buffer) */
+ length = (be32_to_cpu(sdt.entry[0].end_offset) -
+ be32_to_cpu(sdt.entry[0].bar_str_offset)) & IPR_FMT2_MBX_ADDR_MASK;
+
+ hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next,
+ struct ipr_hostrcb, queue);
+ list_del(&hostrcb->queue);
+ memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam));
+
+ rc = ipr_get_ldump_data_section(ioa_cfg,
+ be32_to_cpu(sdt.entry[0].bar_str_offset),
+ (__be32 *)&hostrcb->hcam,
+ min(length, (int)sizeof(hostrcb->hcam)) / sizeof(__be32));
+
+ if (!rc)
+ ipr_handle_log_data(ioa_cfg, hostrcb);
+ else
+ ipr_unit_check_no_data(ioa_cfg);
+
+ list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
+}
+
+/**
+ * ipr_reset_restore_cfg_space - Restore PCI config space.
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function restores the saved PCI config space of
+ * the adapter, fails all outstanding ops back to the callers, and
+ * fetches the dump/unit check if applicable to this reset.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int rc;
+
+ ENTER;
+ rc = pci_restore_state(ioa_cfg->pdev);
+
+ if (rc != PCIBIOS_SUCCESSFUL) {
+ ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR);
+ return IPR_RC_JOB_CONTINUE;
+ }
+
+ if (ipr_set_pcix_cmd_reg(ioa_cfg)) {
+ ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR);
+ return IPR_RC_JOB_CONTINUE;
+ }
+
+ ipr_fail_all_ops(ioa_cfg);
+
+ if (ioa_cfg->ioa_unit_checked) {
+ ioa_cfg->ioa_unit_checked = 0;
+ ipr_get_unit_check_buffer(ioa_cfg);
+ ipr_cmd->job_step = ipr_reset_alert;
+ ipr_reset_start_timer(ipr_cmd, 0);
+ return IPR_RC_JOB_RETURN;
+ }
+
+ if (ioa_cfg->in_ioa_bringdown) {
+ ipr_cmd->job_step = ipr_ioa_bringdown_done;
+ } else {
+ ipr_cmd->job_step = ipr_reset_enable_ioa;
+
+ if (GET_DUMP == ioa_cfg->sdt_state) {
+ ipr_reset_start_timer(ipr_cmd, IPR_DUMP_TIMEOUT);
+ ipr_cmd->job_step = ipr_reset_wait_for_dump;
+ schedule_work(&ioa_cfg->work_q);
+ return IPR_RC_JOB_RETURN;
+ }
+ }
+
+ ENTER;
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
+ * ipr_reset_start_bist - Run BIST on the adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function runs BIST on the adapter, then delays 2 seconds.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int rc;
+
+ ENTER;
+ rc = pci_write_config_byte(ioa_cfg->pdev, PCI_BIST, PCI_BIST_START);
+
+ if (rc != PCIBIOS_SUCCESSFUL) {
+ ipr_cmd->ioasa.ioasc = cpu_to_be32(IPR_IOASC_PCI_ACCESS_ERROR);
+ rc = IPR_RC_JOB_CONTINUE;
+ } else {
+ ipr_cmd->job_step = ipr_reset_restore_cfg_space;
+ ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
+ rc = IPR_RC_JOB_RETURN;
+ }
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ipr_reset_allowed - Query whether or not IOA can be reset
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 if reset not allowed / non-zero if reset is allowed
+ **/
+static int ipr_reset_allowed(struct ipr_ioa_cfg *ioa_cfg)
+{
+ volatile u32 temp_reg;
+
+ temp_reg = readl(ioa_cfg->regs.sense_interrupt_reg);
+ return ((temp_reg & IPR_PCII_CRITICAL_OPERATION) == 0);
+}
+
+/**
+ * ipr_reset_wait_to_start_bist - Wait for permission to reset IOA.
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function waits for adapter permission to run BIST,
+ * then runs BIST. If the adapter does not give permission after a
+ * reasonable time, we will reset the adapter anyway. The impact of
+ * resetting the adapter without warning the adapter is the risk of
+ * losing the persistent error log on the adapter. If the adapter is
+ * reset while it is writing to the flash on the adapter, the flash
+ * segment will have bad ECC and be zeroed.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_wait_to_start_bist(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ int rc = IPR_RC_JOB_RETURN;
+
+ if (!ipr_reset_allowed(ioa_cfg) && ipr_cmd->u.time_left) {
+ ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
+ ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT);
+ } else {
+ ipr_cmd->job_step = ipr_reset_start_bist;
+ rc = IPR_RC_JOB_CONTINUE;
+ }
+
+ return rc;
+}
+
+/**
+ * ipr_reset_alert_part2 - Alert the adapter of a pending reset
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function alerts the adapter that it will be reset.
+ * If memory space is not currently enabled, proceed directly
+ * to running BIST on the adapter. The timer must always be started
+ * so we guarantee we do not run BIST from ipr_isr.
+ *
+ * Return value:
+ * IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_alert(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ u16 cmd_reg;
+ int rc;
+
+ ENTER;
+ rc = pci_read_config_word(ioa_cfg->pdev, PCI_COMMAND, &cmd_reg);
+
+ if ((rc == PCIBIOS_SUCCESSFUL) && (cmd_reg & PCI_COMMAND_MEMORY)) {
+ ipr_mask_and_clear_interrupts(ioa_cfg, ~0);
+ writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.set_uproc_interrupt_reg);
+ ipr_cmd->job_step = ipr_reset_wait_to_start_bist;
+ } else {
+ ipr_cmd->job_step = ipr_reset_start_bist;
+ }
+
+ ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT;
+ ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_reset_ucode_download_done - Microcode download completion
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function unmaps the microcode download buffer.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE
+ **/
+static int ipr_reset_ucode_download_done(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_sglist *sglist = ioa_cfg->ucode_sglist;
+
+ pci_unmap_sg(ioa_cfg->pdev, sglist->scatterlist,
+ sglist->num_sg, DMA_TO_DEVICE);
+
+ ipr_cmd->job_step = ipr_reset_alert;
+ return IPR_RC_JOB_CONTINUE;
+}
+
+/**
+ * ipr_reset_ucode_download - Download microcode to the adapter
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function checks to see if it there is microcode
+ * to download to the adapter. If there is, a download is performed.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_ucode_download(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ struct ipr_sglist *sglist = ioa_cfg->ucode_sglist;
+
+ ENTER;
+ ipr_cmd->job_step = ipr_reset_alert;
+
+ if (!sglist)
+ return IPR_RC_JOB_CONTINUE;
+
+ ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+ ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_SCSICDB;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[0] = WRITE_BUFFER;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[1] = IPR_WR_BUF_DOWNLOAD_AND_SAVE;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[6] = (sglist->buffer_len & 0xff0000) >> 16;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[7] = (sglist->buffer_len & 0x00ff00) >> 8;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[8] = sglist->buffer_len & 0x0000ff;
+
+ if (ipr_map_ucode_buffer(ipr_cmd, sglist, sglist->buffer_len)) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "Failed to map microcode download buffer\n");
+ return IPR_RC_JOB_CONTINUE;
+ }
+
+ ipr_cmd->job_step = ipr_reset_ucode_download_done;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout,
+ IPR_WRITE_BUFFER_TIMEOUT);
+
+ LEAVE;
+ return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_reset_shutdown_ioa - Shutdown the adapter
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function issues an adapter shutdown of the
+ * specified type to the specified adapter as part of the
+ * adapter reset job.
+ *
+ * Return value:
+ * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_shutdown_ioa(struct ipr_cmnd *ipr_cmd)
+{
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+ enum ipr_shutdown_type shutdown_type = ipr_cmd->u.shutdown_type;
+ unsigned long timeout;
+ int rc = IPR_RC_JOB_CONTINUE;
+
+ ENTER;
+ if (shutdown_type != IPR_SHUTDOWN_NONE && !ioa_cfg->ioa_is_dead) {
+ ipr_cmd->ioarcb.res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE);
+ ipr_cmd->ioarcb.cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[0] = IPR_IOA_SHUTDOWN;
+ ipr_cmd->ioarcb.cmd_pkt.cdb[1] = shutdown_type;
+
+ if (shutdown_type == IPR_SHUTDOWN_ABBREV)
+ timeout = IPR_ABBREV_SHUTDOWN_TIMEOUT;
+ else if (shutdown_type == IPR_SHUTDOWN_PREPARE_FOR_NORMAL)
+ timeout = IPR_INTERNAL_TIMEOUT;
+ else
+ timeout = IPR_SHUTDOWN_TIMEOUT;
+
+ ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, timeout);
+
+ rc = IPR_RC_JOB_RETURN;
+ ipr_cmd->job_step = ipr_reset_ucode_download;
+ } else
+ ipr_cmd->job_step = ipr_reset_alert;
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ipr_reset_ioa_job - Adapter reset job
+ * @ipr_cmd: ipr command struct
+ *
+ * Description: This function is the job router for the adapter reset job.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
+{
+ u32 rc, ioasc;
+ unsigned long scratch = ipr_cmd->u.scratch;
+ struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+ do {
+ ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ if (ioa_cfg->reset_cmd != ipr_cmd) {
+ /*
+ * We are doing nested adapter resets and this is
+ * not the current reset job.
+ */
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ return;
+ }
+
+ if (IPR_IOASC_SENSE_KEY(ioasc)) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "0x%02X failed with IOASC: 0x%08X\n",
+ ipr_cmd->ioarcb.cmd_pkt.cdb[0], ioasc);
+
+ ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ return;
+ }
+
+ ipr_reinit_ipr_cmnd(ipr_cmd);
+ ipr_cmd->u.scratch = scratch;
+ rc = ipr_cmd->job_step(ipr_cmd);
+ } while(rc == IPR_RC_JOB_CONTINUE);
+}
+
+/**
+ * _ipr_initiate_ioa_reset - Initiate an adapter reset
+ * @ioa_cfg: ioa config struct
+ * @job_step: first job step of reset job
+ * @shutdown_type: shutdown type
+ *
+ * Description: This function will initiate the reset of the given adapter
+ * starting at the selected job step.
+ * If the caller needs to wait on the completion of the reset,
+ * the caller must sleep on the reset_wait_q.
+ *
+ * Return value:
+ * none
+ **/
+static void _ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
+ int (*job_step) (struct ipr_cmnd *),
+ enum ipr_shutdown_type shutdown_type)
+{
+ struct ipr_cmnd *ipr_cmd;
+
+ ioa_cfg->in_reset_reload = 1;
+ ioa_cfg->allow_cmds = 0;
+ scsi_block_requests(ioa_cfg->host);
+
+ ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
+ ioa_cfg->reset_cmd = ipr_cmd;
+ ipr_cmd->job_step = job_step;
+ ipr_cmd->u.shutdown_type = shutdown_type;
+
+ ipr_reset_ioa_job(ipr_cmd);
+}
+
+/**
+ * ipr_initiate_ioa_reset - Initiate an adapter reset
+ * @ioa_cfg: ioa config struct
+ * @shutdown_type: shutdown type
+ *
+ * Description: This function will initiate the reset of the given adapter.
+ * If the caller needs to wait on the completion of the reset,
+ * the caller must sleep on the reset_wait_q.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg,
+ enum ipr_shutdown_type shutdown_type)
+{
+ if (ioa_cfg->ioa_is_dead)
+ return;
+
+ if (ioa_cfg->in_reset_reload && ioa_cfg->sdt_state == GET_DUMP)
+ ioa_cfg->sdt_state = ABORT_DUMP;
+
+ if (ioa_cfg->reset_retries++ >= IPR_NUM_RESET_RELOAD_RETRIES) {
+ dev_err(&ioa_cfg->pdev->dev,
+ "IOA taken offline - error recovery failed\n");
+
+ ioa_cfg->reset_retries = 0;
+ ioa_cfg->ioa_is_dead = 1;
+
+ if (ioa_cfg->in_ioa_bringdown) {
+ ioa_cfg->reset_cmd = NULL;
+ ioa_cfg->in_reset_reload = 0;
+ ipr_fail_all_ops(ioa_cfg);
+ wake_up_all(&ioa_cfg->reset_wait_q);
+
+ spin_unlock_irq(ioa_cfg->host->host_lock);
+ scsi_unblock_requests(ioa_cfg->host);
+ spin_lock_irq(ioa_cfg->host->host_lock);
+ return;
+ } else {
+ ioa_cfg->in_ioa_bringdown = 1;
+ shutdown_type = IPR_SHUTDOWN_NONE;
+ }
+ }
+
+ _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_shutdown_ioa,
+ shutdown_type);
+}
+
+/**
+ * ipr_probe_ioa_part2 - Initializes IOAs found in ipr_probe_ioa(..)
+ * @ioa_cfg: ioa cfg struct
+ *
+ * Description: This is the second phase of adapter intialization
+ * This function takes care of initilizing the adapter to the point
+ * where it can accept new commands.
+
+ * Return value:
+ * 0 on sucess / -EIO on failure
+ **/
+static int __devinit ipr_probe_ioa_part2(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int rc = 0;
+ unsigned long host_lock_flags = 0;
+
+ ENTER;
+ spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
+ dev_dbg(&ioa_cfg->pdev->dev, "ioa_cfg adx: 0x%p\n", ioa_cfg);
+ _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_enable_ioa, IPR_SHUTDOWN_NONE);
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
+
+ if (ioa_cfg->ioa_is_dead) {
+ rc = -EIO;
+ } else if (ipr_invalid_adapter(ioa_cfg)) {
+ if (!ipr_testmode)
+ rc = -EIO;
+
+ dev_err(&ioa_cfg->pdev->dev,
+ "Adapter not supported in this hardware configuration.\n");
+ }
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ipr_free_cmd_blks - Frees command blocks allocated for an adapter
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int i;
+
+ for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
+ if (ioa_cfg->ipr_cmnd_list[i])
+ pci_pool_free(ioa_cfg->ipr_cmd_pool,
+ ioa_cfg->ipr_cmnd_list[i],
+ ioa_cfg->ipr_cmnd_list_dma[i]);
+
+ ioa_cfg->ipr_cmnd_list[i] = NULL;
+ }
+
+ if (ioa_cfg->ipr_cmd_pool)
+ pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
+
+ ioa_cfg->ipr_cmd_pool = NULL;
+}
+
+/**
+ * ipr_free_mem - Frees memory allocated for an adapter
+ * @ioa_cfg: ioa cfg struct
+ *
+ * Return value:
+ * nothing
+ **/
+static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int i;
+
+ kfree(ioa_cfg->res_entries);
+ pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_misc_cbs),
+ ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
+ ipr_free_cmd_blks(ioa_cfg);
+ pci_free_consistent(ioa_cfg->pdev, sizeof(u32) * IPR_NUM_CMD_BLKS,
+ ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma);
+ pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_config_table),
+ ioa_cfg->cfg_table,
+ ioa_cfg->cfg_table_dma);
+
+ for (i = 0; i < IPR_NUM_HCAMS; i++) {
+ pci_free_consistent(ioa_cfg->pdev,
+ sizeof(struct ipr_hostrcb),
+ ioa_cfg->hostrcb[i],
+ ioa_cfg->hostrcb_dma[i]);
+ }
+
+ ipr_free_dump(ioa_cfg);
+ kfree(ioa_cfg->saved_mode_pages);
+ kfree(ioa_cfg->trace);
+}
+
+/**
+ * ipr_free_all_resources - Free all allocated resources for an adapter.
+ * @ipr_cmd: ipr command struct
+ *
+ * This function frees all allocated resources for the
+ * specified adapter.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct pci_dev *pdev = ioa_cfg->pdev;
+
+ ENTER;
+ free_irq(pdev->irq, ioa_cfg);
+ iounmap(ioa_cfg->hdw_dma_regs);
+ pci_release_regions(pdev);
+ ipr_free_mem(ioa_cfg);
+ scsi_host_put(ioa_cfg->host);
+ pci_disable_device(pdev);
+ LEAVE;
+}
+
+/**
+ * ipr_alloc_cmd_blks - Allocate command blocks for an adapter
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 on success / -ENOMEM on allocation failure
+ **/
+static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct ipr_cmnd *ipr_cmd;
+ struct ipr_ioarcb *ioarcb;
+ dma_addr_t dma_addr;
+ int i;
+
+ ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
+ sizeof(struct ipr_cmnd), 8, 0);
+
+ if (!ioa_cfg->ipr_cmd_pool)
+ return -ENOMEM;
+
+ for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
+ ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, SLAB_KERNEL, &dma_addr);
+
+ if (!ipr_cmd) {
+ ipr_free_cmd_blks(ioa_cfg);
+ return -ENOMEM;
+ }
+
+ memset(ipr_cmd, 0, sizeof(*ipr_cmd));
+ ioa_cfg->ipr_cmnd_list[i] = ipr_cmd;
+ ioa_cfg->ipr_cmnd_list_dma[i] = dma_addr;
+
+ ioarcb = &ipr_cmd->ioarcb;
+ ioarcb->ioarcb_host_pci_addr = cpu_to_be32(dma_addr);
+ ioarcb->host_response_handle = cpu_to_be32(i << 2);
+ ioarcb->write_ioadl_addr =
+ cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, ioadl));
+ ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr;
+ ioarcb->ioasa_host_pci_addr =
+ cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, ioasa));
+ ioarcb->ioasa_len = cpu_to_be16(sizeof(struct ipr_ioasa));
+ ipr_cmd->cmd_index = i;
+ ipr_cmd->ioa_cfg = ioa_cfg;
+ ipr_cmd->sense_buffer_dma = dma_addr +
+ offsetof(struct ipr_cmnd, sense_buffer);
+
+ list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ }
+
+ return 0;
+}
+
+/**
+ * ipr_alloc_mem - Allocate memory for an adapter
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * 0 on success / non-zero for error
+ **/
+static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg)
+{
+ struct pci_dev *pdev = ioa_cfg->pdev;
+ int i, rc = -ENOMEM;
+
+ ENTER;
+ ioa_cfg->res_entries = kmalloc(sizeof(struct ipr_resource_entry) *
+ IPR_MAX_PHYSICAL_DEVS, GFP_KERNEL);
+
+ if (!ioa_cfg->res_entries)
+ goto out;
+
+ memset(ioa_cfg->res_entries, 0,
+ sizeof(struct ipr_resource_entry) * IPR_MAX_PHYSICAL_DEVS);
+
+ for (i = 0; i < IPR_MAX_PHYSICAL_DEVS; i++)
+ list_add_tail(&ioa_cfg->res_entries[i].queue, &ioa_cfg->free_res_q);
+
+ ioa_cfg->vpd_cbs = pci_alloc_consistent(ioa_cfg->pdev,
+ sizeof(struct ipr_misc_cbs),
+ &ioa_cfg->vpd_cbs_dma);
+
+ if (!ioa_cfg->vpd_cbs)
+ goto out_free_res_entries;
+
+ if (ipr_alloc_cmd_blks(ioa_cfg))
+ goto out_free_vpd_cbs;
+
+ ioa_cfg->host_rrq = pci_alloc_consistent(ioa_cfg->pdev,
+ sizeof(u32) * IPR_NUM_CMD_BLKS,
+ &ioa_cfg->host_rrq_dma);
+
+ if (!ioa_cfg->host_rrq)
+ goto out_ipr_free_cmd_blocks;
+
+ ioa_cfg->cfg_table = pci_alloc_consistent(ioa_cfg->pdev,
+ sizeof(struct ipr_config_table),
+ &ioa_cfg->cfg_table_dma);
+
+ if (!ioa_cfg->cfg_table)
+ goto out_free_host_rrq;
+
+ for (i = 0; i < IPR_NUM_HCAMS; i++) {
+ ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev,
+ sizeof(struct ipr_hostrcb),
+ &ioa_cfg->hostrcb_dma[i]);
+
+ if (!ioa_cfg->hostrcb[i])
+ goto out_free_hostrcb_dma;
+
+ ioa_cfg->hostrcb[i]->hostrcb_dma =
+ ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam);
+ list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q);
+ }
+
+ ioa_cfg->trace = kmalloc(sizeof(struct ipr_trace_entry) *
+ IPR_NUM_TRACE_ENTRIES, GFP_KERNEL);
+
+ if (!ioa_cfg->trace)
+ goto out_free_hostrcb_dma;
+
+ memset(ioa_cfg->trace, 0,
+ sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES);
+
+ rc = 0;
+out:
+ LEAVE;
+ return rc;
+
+out_free_hostrcb_dma:
+ while (i-- > 0) {
+ pci_free_consistent(pdev, sizeof(struct ipr_hostrcb),
+ ioa_cfg->hostrcb[i],
+ ioa_cfg->hostrcb_dma[i]);
+ }
+ pci_free_consistent(pdev, sizeof(struct ipr_config_table),
+ ioa_cfg->cfg_table, ioa_cfg->cfg_table_dma);
+out_free_host_rrq:
+ pci_free_consistent(pdev, sizeof(u32) * IPR_NUM_CMD_BLKS,
+ ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma);
+out_ipr_free_cmd_blocks:
+ ipr_free_cmd_blks(ioa_cfg);
+out_free_vpd_cbs:
+ pci_free_consistent(pdev, sizeof(struct ipr_misc_cbs),
+ ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
+out_free_res_entries:
+ kfree(ioa_cfg->res_entries);
+ goto out;
+}
+
+/**
+ * ipr_initialize_bus_attr - Initialize SCSI bus attributes to default values
+ * @ioa_cfg: ioa config struct
+ *
+ * Return value:
+ * none
+ **/
+static void __devinit ipr_initialize_bus_attr(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int i;
+
+ for (i = 0; i < IPR_MAX_NUM_BUSES; i++) {
+ ioa_cfg->bus_attr[i].bus = i;
+ ioa_cfg->bus_attr[i].qas_enabled = 0;
+ ioa_cfg->bus_attr[i].bus_width = IPR_DEFAULT_BUS_WIDTH;
+ if (ipr_max_speed < ARRAY_SIZE(ipr_max_bus_speeds))
+ ioa_cfg->bus_attr[i].max_xfer_rate = ipr_max_bus_speeds[ipr_max_speed];
+ else
+ ioa_cfg->bus_attr[i].max_xfer_rate = IPR_U160_SCSI_RATE;
+ }
+}
+
+/**
+ * ipr_init_ioa_cfg - Initialize IOA config struct
+ * @ioa_cfg: ioa config struct
+ * @host: scsi host struct
+ * @pdev: PCI dev struct
+ *
+ * Return value:
+ * none
+ **/
+static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
+ struct Scsi_Host *host, struct pci_dev *pdev)
+{
+ const struct ipr_interrupt_offsets *p;
+ struct ipr_interrupts *t;
+ void __iomem *base;
+
+ ioa_cfg->host = host;
+ ioa_cfg->pdev = pdev;
+ ioa_cfg->log_level = ipr_log_level;
+ sprintf(ioa_cfg->eye_catcher, IPR_EYECATCHER);
+ sprintf(ioa_cfg->trace_start, IPR_TRACE_START_LABEL);
+ sprintf(ioa_cfg->ipr_free_label, IPR_FREEQ_LABEL);
+ sprintf(ioa_cfg->ipr_pending_label, IPR_PENDQ_LABEL);
+ sprintf(ioa_cfg->cfg_table_start, IPR_CFG_TBL_START);
+ sprintf(ioa_cfg->resource_table_label, IPR_RES_TABLE_LABEL);
+ sprintf(ioa_cfg->ipr_hcam_label, IPR_HCAM_LABEL);
+ sprintf(ioa_cfg->ipr_cmd_label, IPR_CMD_LABEL);
+
+ INIT_LIST_HEAD(&ioa_cfg->free_q);
+ INIT_LIST_HEAD(&ioa_cfg->pending_q);
+ INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q);
+ INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q);
+ INIT_LIST_HEAD(&ioa_cfg->free_res_q);
+ INIT_LIST_HEAD(&ioa_cfg->used_res_q);
+ INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread, ioa_cfg);
+ init_waitqueue_head(&ioa_cfg->reset_wait_q);
+ ioa_cfg->sdt_state = INACTIVE;
+
+ ipr_initialize_bus_attr(ioa_cfg);
+
+ host->max_id = IPR_MAX_NUM_TARGETS_PER_BUS;
+ host->max_lun = IPR_MAX_NUM_LUNS_PER_TARGET;
+ host->max_channel = IPR_MAX_BUS_TO_SCAN;
+ host->unique_id = host->host_no;
+ host->max_cmd_len = IPR_MAX_CDB_LEN;
+ pci_set_drvdata(pdev, ioa_cfg);
+
+ p = &ioa_cfg->chip_cfg->regs;
+ t = &ioa_cfg->regs;
+ base = ioa_cfg->hdw_dma_regs;
+
+ t->set_interrupt_mask_reg = base + p->set_interrupt_mask_reg;
+ t->clr_interrupt_mask_reg = base + p->clr_interrupt_mask_reg;
+ t->sense_interrupt_mask_reg = base + p->sense_interrupt_mask_reg;
+ t->clr_interrupt_reg = base + p->clr_interrupt_reg;
+ t->sense_interrupt_reg = base + p->sense_interrupt_reg;
+ t->ioarrin_reg = base + p->ioarrin_reg;
+ t->sense_uproc_interrupt_reg = base + p->sense_uproc_interrupt_reg;
+ t->set_uproc_interrupt_reg = base + p->set_uproc_interrupt_reg;
+ t->clr_uproc_interrupt_reg = base + p->clr_uproc_interrupt_reg;
+}
+
+/**
+ * ipr_get_chip_cfg - Find adapter chip configuration
+ * @dev_id: PCI device id struct
+ *
+ * Return value:
+ * ptr to chip config on success / NULL on failure
+ **/
+static const struct ipr_chip_cfg_t * __devinit
+ipr_get_chip_cfg(const struct pci_device_id *dev_id)
+{
+ int i;
+
+ if (dev_id->driver_data)
+ return (const struct ipr_chip_cfg_t *)dev_id->driver_data;
+
+ for (i = 0; i < ARRAY_SIZE(ipr_chip); i++)
+ if (ipr_chip[i].vendor == dev_id->vendor &&
+ ipr_chip[i].device == dev_id->device)
+ return ipr_chip[i].cfg;
+ return NULL;
+}
+
+/**
+ * ipr_probe_ioa - Allocates memory and does first stage of initialization
+ * @pdev: PCI device struct
+ * @dev_id: PCI device id struct
+ *
+ * Return value:
+ * 0 on success / non-zero on failure
+ **/
+static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
+ const struct pci_device_id *dev_id)
+{
+ struct ipr_ioa_cfg *ioa_cfg;
+ struct Scsi_Host *host;
+ unsigned long ipr_regs_pci;
+ void __iomem *ipr_regs;
+ u32 rc = PCIBIOS_SUCCESSFUL;
+
+ ENTER;
+
+ if ((rc = pci_enable_device(pdev))) {
+ dev_err(&pdev->dev, "Cannot enable adapter\n");
+ goto out;
+ }
+
+ dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq);
+
+ host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg));
+
+ if (!host) {
+ dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n");
+ rc = -ENOMEM;
+ goto out_disable;
+ }
+
+ ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata;
+ memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg));
+
+ ioa_cfg->chip_cfg = ipr_get_chip_cfg(dev_id);
+
+ if (!ioa_cfg->chip_cfg) {
+ dev_err(&pdev->dev, "Unknown adapter chipset 0x%04X 0x%04X\n",
+ dev_id->vendor, dev_id->device);
+ goto out_scsi_host_put;
+ }
+
+ ipr_regs_pci = pci_resource_start(pdev, 0);
+
+ rc = pci_request_regions(pdev, IPR_NAME);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't register memory range of registers\n");
+ goto out_scsi_host_put;
+ }
+
+ ipr_regs = ioremap(ipr_regs_pci, pci_resource_len(pdev, 0));
+
+ if (!ipr_regs) {
+ dev_err(&pdev->dev,
+ "Couldn't map memory range of registers\n");
+ rc = -ENOMEM;
+ goto out_release_regions;
+ }
+
+ ioa_cfg->hdw_dma_regs = ipr_regs;
+ ioa_cfg->hdw_dma_regs_pci = ipr_regs_pci;
+ ioa_cfg->ioa_mailbox = ioa_cfg->chip_cfg->mailbox + ipr_regs;
+
+ ipr_init_ioa_cfg(ioa_cfg, host, pdev);
+
+ pci_set_master(pdev);
+
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
+ goto cleanup_nomem;
+ }
+
+ rc = pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+ ioa_cfg->chip_cfg->cache_line_size);
+
+ if (rc != PCIBIOS_SUCCESSFUL) {
+ dev_err(&pdev->dev, "Write of cache line size failed\n");
+ rc = -EIO;
+ goto cleanup_nomem;
+ }
+
+ /* Save away PCI config space for use following IOA reset */
+ rc = pci_save_state(pdev);
+
+ if (rc != PCIBIOS_SUCCESSFUL) {
+ dev_err(&pdev->dev, "Failed to save PCI config space\n");
+ rc = -EIO;
+ goto cleanup_nomem;
+ }
+
+ if ((rc = ipr_save_pcix_cmd_reg(ioa_cfg)))
+ goto cleanup_nomem;
+
+ if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg)))
+ goto cleanup_nomem;
+
+ rc = ipr_alloc_mem(ioa_cfg);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't allocate enough memory for device driver!\n");
+ goto cleanup_nomem;
+ }
+
+ ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
+ rc = request_irq(pdev->irq, ipr_isr, SA_SHIRQ, IPR_NAME, ioa_cfg);
+
+ if (rc) {
+ dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n",
+ pdev->irq, rc);
+ goto cleanup_nolog;
+ }
+
+ spin_lock(&ipr_driver_lock);
+ list_add_tail(&ioa_cfg->queue, &ipr_ioa_head);
+ spin_unlock(&ipr_driver_lock);
+
+ LEAVE;
+out:
+ return rc;
+
+cleanup_nolog:
+ ipr_free_mem(ioa_cfg);
+cleanup_nomem:
+ iounmap(ipr_regs);
+out_release_regions:
+ pci_release_regions(pdev);
+out_scsi_host_put:
+ scsi_host_put(host);
+out_disable:
+ pci_disable_device(pdev);
+ goto out;
+}
+
+/**
+ * ipr_scan_vsets - Scans for VSET devices
+ * @ioa_cfg: ioa config struct
+ *
+ * Description: Since the VSET resources do not follow SAM in that we can have
+ * sparse LUNs with no LUN 0, we have to scan for these ourselves.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_scan_vsets(struct ipr_ioa_cfg *ioa_cfg)
+{
+ int target, lun;
+
+ for (target = 0; target < IPR_MAX_NUM_TARGETS_PER_BUS; target++)
+ for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++ )
+ scsi_add_device(ioa_cfg->host, IPR_VSET_BUS, target, lun);
+}
+
+/**
+ * ipr_initiate_ioa_bringdown - Bring down an adapter
+ * @ioa_cfg: ioa config struct
+ * @shutdown_type: shutdown type
+ *
+ * Description: This function will initiate bringing down the adapter.
+ * This consists of issuing an IOA shutdown to the adapter
+ * to flush the cache, and running BIST.
+ * If the caller needs to wait on the completion of the reset,
+ * the caller must sleep on the reset_wait_q.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_initiate_ioa_bringdown(struct ipr_ioa_cfg *ioa_cfg,
+ enum ipr_shutdown_type shutdown_type)
+{
+ ENTER;
+ if (ioa_cfg->sdt_state == WAIT_FOR_DUMP)
+ ioa_cfg->sdt_state = ABORT_DUMP;
+ ioa_cfg->reset_retries = 0;
+ ioa_cfg->in_ioa_bringdown = 1;
+ ipr_initiate_ioa_reset(ioa_cfg, shutdown_type);
+ LEAVE;
+}
+
+/**
+ * __ipr_remove - Remove a single adapter
+ * @pdev: pci device struct
+ *
+ * Adapter hot plug remove entry point.
+ *
+ * Return value:
+ * none
+ **/
+static void __ipr_remove(struct pci_dev *pdev)
+{
+ unsigned long host_lock_flags = 0;
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+ ENTER;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
+ ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL);
+
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+ spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
+
+ spin_lock(&ipr_driver_lock);
+ list_del(&ioa_cfg->queue);
+ spin_unlock(&ipr_driver_lock);
+
+ if (ioa_cfg->sdt_state == ABORT_DUMP)
+ ioa_cfg->sdt_state = WAIT_FOR_DUMP;
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
+
+ ipr_free_all_resources(ioa_cfg);
+
+ LEAVE;
+}
+
+/**
+ * ipr_remove - IOA hot plug remove entry point
+ * @pdev: pci device struct
+ *
+ * Adapter hot plug remove entry point.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_remove(struct pci_dev *pdev)
+{
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
+
+ ENTER;
+
+ ioa_cfg->allow_cmds = 0;
+ flush_scheduled_work();
+ ipr_remove_trace_file(&ioa_cfg->host->shost_classdev.kobj,
+ &ipr_trace_attr);
+ ipr_remove_dump_file(&ioa_cfg->host->shost_classdev.kobj,
+ &ipr_dump_attr);
+ scsi_remove_host(ioa_cfg->host);
+
+ __ipr_remove(pdev);
+
+ LEAVE;
+}
+
+/**
+ * ipr_probe - Adapter hot plug add entry point
+ *
+ * Return value:
+ * 0 on success / non-zero on failure
+ **/
+static int __devinit ipr_probe(struct pci_dev *pdev,
+ const struct pci_device_id *dev_id)
+{
+ struct ipr_ioa_cfg *ioa_cfg;
+ int rc;
+
+ rc = ipr_probe_ioa(pdev, dev_id);
+
+ if (rc)
+ return rc;
+
+ ioa_cfg = pci_get_drvdata(pdev);
+ rc = ipr_probe_ioa_part2(ioa_cfg);
+
+ if (rc) {
+ __ipr_remove(pdev);
+ return rc;
+ }
+
+ rc = scsi_add_host(ioa_cfg->host, &pdev->dev);
+
+ if (rc) {
+ __ipr_remove(pdev);
+ return rc;
+ }
+
+ rc = ipr_create_trace_file(&ioa_cfg->host->shost_classdev.kobj,
+ &ipr_trace_attr);
+
+ if (rc) {
+ scsi_remove_host(ioa_cfg->host);
+ __ipr_remove(pdev);
+ return rc;
+ }
+
+ rc = ipr_create_dump_file(&ioa_cfg->host->shost_classdev.kobj,
+ &ipr_dump_attr);
+
+ if (rc) {
+ ipr_remove_trace_file(&ioa_cfg->host->shost_classdev.kobj,
+ &ipr_trace_attr);
+ scsi_remove_host(ioa_cfg->host);
+ __ipr_remove(pdev);
+ return rc;
+ }
+
+ scsi_scan_host(ioa_cfg->host);
+ ipr_scan_vsets(ioa_cfg);
+ scsi_add_device(ioa_cfg->host, IPR_IOA_BUS, IPR_IOA_TARGET, IPR_IOA_LUN);
+ ioa_cfg->allow_ml_add_del = 1;
+ schedule_work(&ioa_cfg->work_q);
+ return 0;
+}
+
+/**
+ * ipr_shutdown - Shutdown handler.
+ * @dev: device struct
+ *
+ * This function is invoked upon system shutdown/reboot. It will issue
+ * an adapter shutdown to the adapter to flush the write cache.
+ *
+ * Return value:
+ * none
+ **/
+static void ipr_shutdown(struct device *dev)
+{
+ struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(to_pci_dev(dev));
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
+ ipr_initiate_ioa_bringdown(ioa_cfg, IPR_SHUTDOWN_NORMAL);
+ spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
+}
+
+static struct pci_device_id ipr_pci_table[] __devinitdata = {
+ { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5702,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_5703,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573D,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573E,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571B,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_572E,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571A,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
+ { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571E,
+ 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, ipr_pci_table);
+
+static struct pci_driver ipr_driver = {
+ .name = IPR_NAME,
+ .id_table = ipr_pci_table,
+ .probe = ipr_probe,
+ .remove = ipr_remove,
+ .driver = {
+ .shutdown = ipr_shutdown,
+ },
+};
+
+/**
+ * ipr_init - Module entry point
+ *
+ * Return value:
+ * 0 on success / negative value on failure
+ **/
+static int __init ipr_init(void)
+{
+ ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n",
+ IPR_DRIVER_VERSION, IPR_DRIVER_DATE);
+
+ return pci_module_init(&ipr_driver);
+}
+
+/**
+ * ipr_exit - Module unload
+ *
+ * Module unload entry point.
+ *
+ * Return value:
+ * none
+ **/
+static void __exit ipr_exit(void)
+{
+ pci_unregister_driver(&ipr_driver);
+}
+
+module_init(ipr_init);
+module_exit(ipr_exit);
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
new file mode 100644
index 000000000000..446f4259285b
--- /dev/null
+++ b/drivers/scsi/ipr.h
@@ -0,0 +1,1261 @@
+/*
+ * ipr.h -- driver for IBM Power Linux RAID adapters
+ *
+ * Written By: Brian King <brking@us.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) 2003, 2004 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Alan Cox <alan@redhat.com> - Removed several careless u32/dma_addr_t errors
+ * that broke 64bit platforms.
+ */
+
+#ifndef _IPR_H
+#define _IPR_H
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+/*
+ * Literals
+ */
+#define IPR_DRIVER_VERSION "2.0.13"
+#define IPR_DRIVER_DATE "(February 21, 2005)"
+
+/*
+ * IPR_DBG_TRACE: Setting this to 1 will turn on some general function tracing
+ * resulting in a bunch of extra debugging printks to the console
+ *
+ * IPR_DEBUG: Setting this to 1 will turn on some error path tracing.
+ * Enables the ipr_trace macro.
+ */
+#ifdef IPR_DEBUG_ALL
+#define IPR_DEBUG 1
+#define IPR_DBG_TRACE 1
+#else
+#define IPR_DEBUG 0
+#define IPR_DBG_TRACE 0
+#endif
+
+/*
+ * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
+ * ops per device for devices not running tagged command queuing.
+ * This can be adjusted at runtime through sysfs device attributes.
+ */
+#define IPR_MAX_CMD_PER_LUN 6
+
+/*
+ * IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of
+ * ops the mid-layer can send to the adapter.
+ */
+#define IPR_NUM_BASE_CMD_BLKS 100
+
+#define IPR_SUBS_DEV_ID_2780 0x0264
+#define IPR_SUBS_DEV_ID_5702 0x0266
+#define IPR_SUBS_DEV_ID_5703 0x0278
+#define IPR_SUBS_DEV_ID_572E 0x028D
+#define IPR_SUBS_DEV_ID_573E 0x02D3
+#define IPR_SUBS_DEV_ID_573D 0x02D4
+#define IPR_SUBS_DEV_ID_571A 0x02C0
+#define IPR_SUBS_DEV_ID_571B 0x02BE
+#define IPR_SUBS_DEV_ID_571E 0x02BF
+
+#define IPR_NAME "ipr"
+
+/*
+ * Return codes
+ */
+#define IPR_RC_JOB_CONTINUE 1
+#define IPR_RC_JOB_RETURN 2
+
+/*
+ * IOASCs
+ */
+#define IPR_IOASC_NR_INIT_CMD_REQUIRED 0x02040200
+#define IPR_IOASC_SYNC_REQUIRED 0x023f0000
+#define IPR_IOASC_MED_DO_NOT_REALLOC 0x03110C00
+#define IPR_IOASC_HW_SEL_TIMEOUT 0x04050000
+#define IPR_IOASC_HW_DEV_BUS_STATUS 0x04448500
+#define IPR_IOASC_IOASC_MASK 0xFFFFFF00
+#define IPR_IOASC_SCSI_STATUS_MASK 0x000000FF
+#define IPR_IOASC_IR_RESOURCE_HANDLE 0x05250000
+#define IPR_IOASC_BUS_WAS_RESET 0x06290000
+#define IPR_IOASC_BUS_WAS_RESET_BY_OTHER 0x06298000
+#define IPR_IOASC_ABORTED_CMD_TERM_BY_HOST 0x0B5A0000
+
+#define IPR_FIRST_DRIVER_IOASC 0x10000000
+#define IPR_IOASC_IOA_WAS_RESET 0x10000001
+#define IPR_IOASC_PCI_ACCESS_ERROR 0x10000002
+
+#define IPR_NUM_LOG_HCAMS 2
+#define IPR_NUM_CFG_CHG_HCAMS 2
+#define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS)
+#define IPR_MAX_NUM_TARGETS_PER_BUS 0x10
+#define IPR_MAX_NUM_LUNS_PER_TARGET 256
+#define IPR_MAX_NUM_VSET_LUNS_PER_TARGET 8
+#define IPR_VSET_BUS 0xff
+#define IPR_IOA_BUS 0xff
+#define IPR_IOA_TARGET 0xff
+#define IPR_IOA_LUN 0xff
+#define IPR_MAX_NUM_BUSES 4
+#define IPR_MAX_BUS_TO_SCAN IPR_MAX_NUM_BUSES
+
+#define IPR_NUM_RESET_RELOAD_RETRIES 3
+
+/* We need resources for HCAMS, IOA reset, IOA bringdown, and ERP */
+#define IPR_NUM_INTERNAL_CMD_BLKS (IPR_NUM_HCAMS + \
+ ((IPR_NUM_RESET_RELOAD_RETRIES + 1) * 2) + 3)
+
+#define IPR_MAX_COMMANDS IPR_NUM_BASE_CMD_BLKS
+#define IPR_NUM_CMD_BLKS (IPR_NUM_BASE_CMD_BLKS + \
+ IPR_NUM_INTERNAL_CMD_BLKS)
+
+#define IPR_MAX_PHYSICAL_DEVS 192
+
+#define IPR_MAX_SGLIST 64
+#define IPR_IOA_MAX_SECTORS 32767
+#define IPR_VSET_MAX_SECTORS 512
+#define IPR_MAX_CDB_LEN 16
+
+#define IPR_DEFAULT_BUS_WIDTH 16
+#define IPR_80MBs_SCSI_RATE ((80 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8))
+#define IPR_U160_SCSI_RATE ((160 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8))
+#define IPR_U320_SCSI_RATE ((320 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8))
+#define IPR_MAX_SCSI_RATE(width) ((320 * 10) / ((width) / 8))
+
+#define IPR_IOA_RES_HANDLE 0xffffffff
+#define IPR_IOA_RES_ADDR 0x00ffffff
+
+/*
+ * Adapter Commands
+ */
+#define IPR_QUERY_RSRC_STATE 0xC2
+#define IPR_RESET_DEVICE 0xC3
+#define IPR_RESET_TYPE_SELECT 0x80
+#define IPR_LUN_RESET 0x40
+#define IPR_TARGET_RESET 0x20
+#define IPR_BUS_RESET 0x10
+#define IPR_ID_HOST_RR_Q 0xC4
+#define IPR_QUERY_IOA_CONFIG 0xC5
+#define IPR_CANCEL_ALL_REQUESTS 0xCE
+#define IPR_HOST_CONTROLLED_ASYNC 0xCF
+#define IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE 0x01
+#define IPR_HCAM_CDB_OP_CODE_LOG_DATA 0x02
+#define IPR_SET_SUPPORTED_DEVICES 0xFB
+#define IPR_IOA_SHUTDOWN 0xF7
+#define IPR_WR_BUF_DOWNLOAD_AND_SAVE 0x05
+
+/*
+ * Timeouts
+ */
+#define IPR_SHUTDOWN_TIMEOUT (ipr_fastfail ? 60 * HZ : 10 * 60 * HZ)
+#define IPR_VSET_RW_TIMEOUT (ipr_fastfail ? 30 * HZ : 2 * 60 * HZ)
+#define IPR_ABBREV_SHUTDOWN_TIMEOUT (10 * HZ)
+#define IPR_DEVICE_RESET_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
+#define IPR_CANCEL_ALL_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
+#define IPR_ABORT_TASK_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
+#define IPR_INTERNAL_TIMEOUT (ipr_fastfail ? 10 * HZ : 30 * HZ)
+#define IPR_WRITE_BUFFER_TIMEOUT (10 * 60 * HZ)
+#define IPR_SET_SUP_DEVICE_TIMEOUT (2 * 60 * HZ)
+#define IPR_REQUEST_SENSE_TIMEOUT (10 * HZ)
+#define IPR_OPERATIONAL_TIMEOUT (5 * 60)
+#define IPR_WAIT_FOR_RESET_TIMEOUT (2 * HZ)
+#define IPR_CHECK_FOR_RESET_TIMEOUT (HZ / 10)
+#define IPR_WAIT_FOR_BIST_TIMEOUT (2 * HZ)
+#define IPR_DUMP_TIMEOUT (15 * HZ)
+
+/*
+ * SCSI Literals
+ */
+#define IPR_VENDOR_ID_LEN 8
+#define IPR_PROD_ID_LEN 16
+#define IPR_SERIAL_NUM_LEN 8
+
+/*
+ * Hardware literals
+ */
+#define IPR_FMT2_MBX_ADDR_MASK 0x0fffffff
+#define IPR_FMT2_MBX_BAR_SEL_MASK 0xf0000000
+#define IPR_FMT2_MKR_BAR_SEL_SHIFT 28
+#define IPR_GET_FMT2_BAR_SEL(mbx) \
+(((mbx) & IPR_FMT2_MBX_BAR_SEL_MASK) >> IPR_FMT2_MKR_BAR_SEL_SHIFT)
+#define IPR_SDT_FMT2_BAR0_SEL 0x0
+#define IPR_SDT_FMT2_BAR1_SEL 0x1
+#define IPR_SDT_FMT2_BAR2_SEL 0x2
+#define IPR_SDT_FMT2_BAR3_SEL 0x3
+#define IPR_SDT_FMT2_BAR4_SEL 0x4
+#define IPR_SDT_FMT2_BAR5_SEL 0x5
+#define IPR_SDT_FMT2_EXP_ROM_SEL 0x8
+#define IPR_FMT2_SDT_READY_TO_USE 0xC4D4E3F2
+#define IPR_DOORBELL 0x82800000
+
+#define IPR_PCII_IOA_TRANS_TO_OPER (0x80000000 >> 0)
+#define IPR_PCII_IOARCB_XFER_FAILED (0x80000000 >> 3)
+#define IPR_PCII_IOA_UNIT_CHECKED (0x80000000 >> 4)
+#define IPR_PCII_NO_HOST_RRQ (0x80000000 >> 5)
+#define IPR_PCII_CRITICAL_OPERATION (0x80000000 >> 6)
+#define IPR_PCII_IO_DEBUG_ACKNOWLEDGE (0x80000000 >> 7)
+#define IPR_PCII_IOARRIN_LOST (0x80000000 >> 27)
+#define IPR_PCII_MMIO_ERROR (0x80000000 >> 28)
+#define IPR_PCII_PROC_ERR_STATE (0x80000000 >> 29)
+#define IPR_PCII_HRRQ_UPDATED (0x80000000 >> 30)
+#define IPR_PCII_CORE_ISSUED_RST_REQ (0x80000000 >> 31)
+
+#define IPR_PCII_ERROR_INTERRUPTS \
+(IPR_PCII_IOARCB_XFER_FAILED | IPR_PCII_IOA_UNIT_CHECKED | \
+IPR_PCII_NO_HOST_RRQ | IPR_PCII_IOARRIN_LOST | IPR_PCII_MMIO_ERROR)
+
+#define IPR_PCII_OPER_INTERRUPTS \
+(IPR_PCII_ERROR_INTERRUPTS | IPR_PCII_HRRQ_UPDATED | IPR_PCII_IOA_TRANS_TO_OPER)
+
+#define IPR_UPROCI_RESET_ALERT (0x80000000 >> 7)
+#define IPR_UPROCI_IO_DEBUG_ALERT (0x80000000 >> 9)
+
+#define IPR_LDUMP_MAX_LONG_ACK_DELAY_IN_USEC 200000 /* 200 ms */
+#define IPR_LDUMP_MAX_SHORT_ACK_DELAY_IN_USEC 200000 /* 200 ms */
+
+/*
+ * Dump literals
+ */
+#define IPR_MAX_IOA_DUMP_SIZE (4 * 1024 * 1024)
+#define IPR_NUM_SDT_ENTRIES 511
+#define IPR_MAX_NUM_DUMP_PAGES ((IPR_MAX_IOA_DUMP_SIZE / PAGE_SIZE) + 1)
+
+/*
+ * Misc literals
+ */
+#define IPR_NUM_IOADL_ENTRIES IPR_MAX_SGLIST
+
+/*
+ * Adapter interface types
+ */
+
+struct ipr_res_addr {
+ u8 reserved;
+ u8 bus;
+ u8 target;
+ u8 lun;
+#define IPR_GET_PHYS_LOC(res_addr) \
+ (((res_addr).bus << 16) | ((res_addr).target << 8) | (res_addr).lun)
+}__attribute__((packed, aligned (4)));
+
+struct ipr_std_inq_vpids {
+ u8 vendor_id[IPR_VENDOR_ID_LEN];
+ u8 product_id[IPR_PROD_ID_LEN];
+}__attribute__((packed));
+
+struct ipr_std_inq_data {
+ u8 peri_qual_dev_type;
+#define IPR_STD_INQ_PERI_QUAL(peri) ((peri) >> 5)
+#define IPR_STD_INQ_PERI_DEV_TYPE(peri) ((peri) & 0x1F)
+
+ u8 removeable_medium_rsvd;
+#define IPR_STD_INQ_REMOVEABLE_MEDIUM 0x80
+
+#define IPR_IS_DASD_DEVICE(std_inq) \
+((IPR_STD_INQ_PERI_DEV_TYPE((std_inq).peri_qual_dev_type) == TYPE_DISK) && \
+!(((std_inq).removeable_medium_rsvd) & IPR_STD_INQ_REMOVEABLE_MEDIUM))
+
+#define IPR_IS_SES_DEVICE(std_inq) \
+(IPR_STD_INQ_PERI_DEV_TYPE((std_inq).peri_qual_dev_type) == TYPE_ENCLOSURE)
+
+ u8 version;
+ u8 aen_naca_fmt;
+ u8 additional_len;
+ u8 sccs_rsvd;
+ u8 bq_enc_multi;
+ u8 sync_cmdq_flags;
+
+ struct ipr_std_inq_vpids vpids;
+
+ u8 ros_rsvd_ram_rsvd[4];
+
+ u8 serial_num[IPR_SERIAL_NUM_LEN];
+}__attribute__ ((packed));
+
+struct ipr_config_table_entry {
+ u8 service_level;
+ u8 array_id;
+ u8 flags;
+#define IPR_IS_IOA_RESOURCE 0x80
+#define IPR_IS_ARRAY_MEMBER 0x20
+#define IPR_IS_HOT_SPARE 0x10
+
+ u8 rsvd_subtype;
+#define IPR_RES_SUBTYPE(res) (((res)->cfgte.rsvd_subtype) & 0x0f)
+#define IPR_SUBTYPE_AF_DASD 0
+#define IPR_SUBTYPE_GENERIC_SCSI 1
+#define IPR_SUBTYPE_VOLUME_SET 2
+
+ struct ipr_res_addr res_addr;
+ __be32 res_handle;
+ __be32 reserved4[2];
+ struct ipr_std_inq_data std_inq_data;
+}__attribute__ ((packed, aligned (4)));
+
+struct ipr_config_table_hdr {
+ u8 num_entries;
+ u8 flags;
+#define IPR_UCODE_DOWNLOAD_REQ 0x10
+ __be16 reserved;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_config_table {
+ struct ipr_config_table_hdr hdr;
+ struct ipr_config_table_entry dev[IPR_MAX_PHYSICAL_DEVS];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_cfg_ch_not {
+ struct ipr_config_table_entry cfgte;
+ u8 reserved[936];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_supported_device {
+ __be16 data_length;
+ u8 reserved;
+ u8 num_records;
+ struct ipr_std_inq_vpids vpids;
+ u8 reserved2[16];
+}__attribute__((packed, aligned (4)));
+
+/* Command packet structure */
+struct ipr_cmd_pkt {
+ __be16 reserved; /* Reserved by IOA */
+ u8 request_type;
+#define IPR_RQTYPE_SCSICDB 0x00
+#define IPR_RQTYPE_IOACMD 0x01
+#define IPR_RQTYPE_HCAM 0x02
+
+ u8 luntar_luntrn;
+
+ u8 flags_hi;
+#define IPR_FLAGS_HI_WRITE_NOT_READ 0x80
+#define IPR_FLAGS_HI_NO_ULEN_CHK 0x20
+#define IPR_FLAGS_HI_SYNC_OVERRIDE 0x10
+#define IPR_FLAGS_HI_SYNC_COMPLETE 0x08
+#define IPR_FLAGS_HI_NO_LINK_DESC 0x04
+
+ u8 flags_lo;
+#define IPR_FLAGS_LO_ALIGNED_BFR 0x20
+#define IPR_FLAGS_LO_DELAY_AFTER_RST 0x10
+#define IPR_FLAGS_LO_UNTAGGED_TASK 0x00
+#define IPR_FLAGS_LO_SIMPLE_TASK 0x02
+#define IPR_FLAGS_LO_ORDERED_TASK 0x04
+#define IPR_FLAGS_LO_HEAD_OF_Q_TASK 0x06
+#define IPR_FLAGS_LO_ACA_TASK 0x08
+
+ u8 cdb[16];
+ __be16 timeout;
+}__attribute__ ((packed, aligned(4)));
+
+/* IOA Request Control Block 128 bytes */
+struct ipr_ioarcb {
+ __be32 ioarcb_host_pci_addr;
+ __be32 reserved;
+ __be32 res_handle;
+ __be32 host_response_handle;
+ __be32 reserved1;
+ __be32 reserved2;
+ __be32 reserved3;
+
+ __be32 write_data_transfer_length;
+ __be32 read_data_transfer_length;
+ __be32 write_ioadl_addr;
+ __be32 write_ioadl_len;
+ __be32 read_ioadl_addr;
+ __be32 read_ioadl_len;
+
+ __be32 ioasa_host_pci_addr;
+ __be16 ioasa_len;
+ __be16 reserved4;
+
+ struct ipr_cmd_pkt cmd_pkt;
+
+ __be32 add_cmd_parms_len;
+ __be32 add_cmd_parms[10];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_ioadl_desc {
+ __be32 flags_and_data_len;
+#define IPR_IOADL_FLAGS_MASK 0xff000000
+#define IPR_IOADL_GET_FLAGS(x) (be32_to_cpu(x) & IPR_IOADL_FLAGS_MASK)
+#define IPR_IOADL_DATA_LEN_MASK 0x00ffffff
+#define IPR_IOADL_GET_DATA_LEN(x) (be32_to_cpu(x) & IPR_IOADL_DATA_LEN_MASK)
+#define IPR_IOADL_FLAGS_READ 0x48000000
+#define IPR_IOADL_FLAGS_READ_LAST 0x49000000
+#define IPR_IOADL_FLAGS_WRITE 0x68000000
+#define IPR_IOADL_FLAGS_WRITE_LAST 0x69000000
+#define IPR_IOADL_FLAGS_LAST 0x01000000
+
+ __be32 address;
+}__attribute__((packed, aligned (8)));
+
+struct ipr_ioasa_vset {
+ __be32 failing_lba_hi;
+ __be32 failing_lba_lo;
+ __be32 ioa_data[22];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_ioasa_af_dasd {
+ __be32 failing_lba;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_ioasa_gpdd {
+ u8 end_state;
+ u8 bus_phase;
+ __be16 reserved;
+ __be32 ioa_data[23];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_ioasa_raw {
+ __be32 ioa_data[24];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_ioasa {
+ __be32 ioasc;
+#define IPR_IOASC_SENSE_KEY(ioasc) ((ioasc) >> 24)
+#define IPR_IOASC_SENSE_CODE(ioasc) (((ioasc) & 0x00ff0000) >> 16)
+#define IPR_IOASC_SENSE_QUAL(ioasc) (((ioasc) & 0x0000ff00) >> 8)
+#define IPR_IOASC_SENSE_STATUS(ioasc) ((ioasc) & 0x000000ff)
+
+ __be16 ret_stat_len; /* Length of the returned IOASA */
+
+ __be16 avail_stat_len; /* Total Length of status available. */
+
+ __be32 residual_data_len; /* number of bytes in the host data */
+ /* buffers that were not used by the IOARCB command. */
+
+ __be32 ilid;
+#define IPR_NO_ILID 0
+#define IPR_DRIVER_ILID 0xffffffff
+
+ __be32 fd_ioasc;
+
+ __be32 fd_phys_locator;
+
+ __be32 fd_res_handle;
+
+ __be32 ioasc_specific; /* status code specific field */
+#define IPR_IOASC_SPECIFIC_MASK 0x00ffffff
+#define IPR_FIELD_POINTER_VALID (0x80000000 >> 8)
+#define IPR_FIELD_POINTER_MASK 0x0000ffff
+
+ union {
+ struct ipr_ioasa_vset vset;
+ struct ipr_ioasa_af_dasd dasd;
+ struct ipr_ioasa_gpdd gpdd;
+ struct ipr_ioasa_raw raw;
+ } u;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_mode_parm_hdr {
+ u8 length;
+ u8 medium_type;
+ u8 device_spec_parms;
+ u8 block_desc_len;
+}__attribute__((packed));
+
+struct ipr_mode_pages {
+ struct ipr_mode_parm_hdr hdr;
+ u8 data[255 - sizeof(struct ipr_mode_parm_hdr)];
+}__attribute__((packed));
+
+struct ipr_mode_page_hdr {
+ u8 ps_page_code;
+#define IPR_MODE_PAGE_PS 0x80
+#define IPR_GET_MODE_PAGE_CODE(hdr) ((hdr)->ps_page_code & 0x3F)
+ u8 page_length;
+}__attribute__ ((packed));
+
+struct ipr_dev_bus_entry {
+ struct ipr_res_addr res_addr;
+ u8 flags;
+#define IPR_SCSI_ATTR_ENABLE_QAS 0x80
+#define IPR_SCSI_ATTR_DISABLE_QAS 0x40
+#define IPR_SCSI_ATTR_QAS_MASK 0xC0
+#define IPR_SCSI_ATTR_ENABLE_TM 0x20
+#define IPR_SCSI_ATTR_NO_TERM_PWR 0x10
+#define IPR_SCSI_ATTR_TM_SUPPORTED 0x08
+#define IPR_SCSI_ATTR_LVD_TO_SE_NOT_ALLOWED 0x04
+
+ u8 scsi_id;
+ u8 bus_width;
+ u8 extended_reset_delay;
+#define IPR_EXTENDED_RESET_DELAY 7
+
+ __be32 max_xfer_rate;
+
+ u8 spinup_delay;
+ u8 reserved3;
+ __be16 reserved4;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_mode_page28 {
+ struct ipr_mode_page_hdr hdr;
+ u8 num_entries;
+ u8 entry_length;
+ struct ipr_dev_bus_entry bus[0];
+}__attribute__((packed));
+
+struct ipr_ioa_vpd {
+ struct ipr_std_inq_data std_inq_data;
+ u8 ascii_part_num[12];
+ u8 reserved[40];
+ u8 ascii_plant_code[4];
+}__attribute__((packed));
+
+struct ipr_inquiry_page3 {
+ u8 peri_qual_dev_type;
+ u8 page_code;
+ u8 reserved1;
+ u8 page_length;
+ u8 ascii_len;
+ u8 reserved2[3];
+ u8 load_id[4];
+ u8 major_release;
+ u8 card_type;
+ u8 minor_release[2];
+ u8 ptf_number[4];
+ u8 patch_number[4];
+}__attribute__((packed));
+
+struct ipr_hostrcb_device_data_entry {
+ struct ipr_std_inq_vpids dev_vpids;
+ u8 dev_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_res_addr dev_res_addr;
+ struct ipr_std_inq_vpids new_dev_vpids;
+ u8 new_dev_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids ioa_last_with_dev_vpids;
+ u8 ioa_last_with_dev_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids cfc_last_with_dev_vpids;
+ u8 cfc_last_with_dev_sn[IPR_SERIAL_NUM_LEN];
+ __be32 ioa_data[5];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_array_data_entry {
+ struct ipr_std_inq_vpids vpids;
+ u8 serial_num[IPR_SERIAL_NUM_LEN];
+ struct ipr_res_addr expected_dev_res_addr;
+ struct ipr_res_addr dev_res_addr;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_type_ff_error {
+ __be32 ioa_data[246];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_type_01_error {
+ __be32 seek_counter;
+ __be32 read_counter;
+ u8 sense_data[32];
+ __be32 ioa_data[236];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_type_02_error {
+ struct ipr_std_inq_vpids ioa_vpids;
+ u8 ioa_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids cfc_vpids;
+ u8 cfc_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids ioa_last_attached_to_cfc_vpids;
+ u8 ioa_last_attached_to_cfc_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids cfc_last_attached_to_ioa_vpids;
+ u8 cfc_last_attached_to_ioa_sn[IPR_SERIAL_NUM_LEN];
+ __be32 ioa_data[3];
+ u8 reserved[844];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_type_03_error {
+ struct ipr_std_inq_vpids ioa_vpids;
+ u8 ioa_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids cfc_vpids;
+ u8 cfc_sn[IPR_SERIAL_NUM_LEN];
+ __be32 errors_detected;
+ __be32 errors_logged;
+ u8 ioa_data[12];
+ struct ipr_hostrcb_device_data_entry dev_entry[3];
+ u8 reserved[444];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_type_04_error {
+ struct ipr_std_inq_vpids ioa_vpids;
+ u8 ioa_sn[IPR_SERIAL_NUM_LEN];
+ struct ipr_std_inq_vpids cfc_vpids;
+ u8 cfc_sn[IPR_SERIAL_NUM_LEN];
+ u8 ioa_data[12];
+ struct ipr_hostrcb_array_data_entry array_member[10];
+ __be32 exposed_mode_adn;
+ __be32 array_id;
+ struct ipr_std_inq_vpids incomp_dev_vpids;
+ u8 incomp_dev_sn[IPR_SERIAL_NUM_LEN];
+ __be32 ioa_data2;
+ struct ipr_hostrcb_array_data_entry array_member2[8];
+ struct ipr_res_addr last_func_vset_res_addr;
+ u8 vset_serial_num[IPR_SERIAL_NUM_LEN];
+ u8 protection_level[8];
+ u8 reserved[124];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_error {
+ __be32 failing_dev_ioasc;
+ struct ipr_res_addr failing_dev_res_addr;
+ __be32 failing_dev_res_handle;
+ __be32 prc;
+ union {
+ struct ipr_hostrcb_type_ff_error type_ff_error;
+ struct ipr_hostrcb_type_01_error type_01_error;
+ struct ipr_hostrcb_type_02_error type_02_error;
+ struct ipr_hostrcb_type_03_error type_03_error;
+ struct ipr_hostrcb_type_04_error type_04_error;
+ } u;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb_raw {
+ __be32 data[sizeof(struct ipr_hostrcb_error)/sizeof(__be32)];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hcam {
+ u8 op_code;
+#define IPR_HOST_RCB_OP_CODE_CONFIG_CHANGE 0xE1
+#define IPR_HOST_RCB_OP_CODE_LOG_DATA 0xE2
+
+ u8 notify_type;
+#define IPR_HOST_RCB_NOTIF_TYPE_EXISTING_CHANGED 0x00
+#define IPR_HOST_RCB_NOTIF_TYPE_NEW_ENTRY 0x01
+#define IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY 0x02
+#define IPR_HOST_RCB_NOTIF_TYPE_ERROR_LOG_ENTRY 0x10
+#define IPR_HOST_RCB_NOTIF_TYPE_INFORMATION_ENTRY 0x11
+
+ u8 notifications_lost;
+#define IPR_HOST_RCB_NO_NOTIFICATIONS_LOST 0
+#define IPR_HOST_RCB_NOTIFICATIONS_LOST 0x80
+
+ u8 flags;
+#define IPR_HOSTRCB_INTERNAL_OPER 0x80
+#define IPR_HOSTRCB_ERR_RESP_SENT 0x40
+
+ u8 overlay_id;
+#define IPR_HOST_RCB_OVERLAY_ID_1 0x01
+#define IPR_HOST_RCB_OVERLAY_ID_2 0x02
+#define IPR_HOST_RCB_OVERLAY_ID_3 0x03
+#define IPR_HOST_RCB_OVERLAY_ID_4 0x04
+#define IPR_HOST_RCB_OVERLAY_ID_6 0x06
+#define IPR_HOST_RCB_OVERLAY_ID_DEFAULT 0xFF
+
+ u8 reserved1[3];
+ __be32 ilid;
+ __be32 time_since_last_ioa_reset;
+ __be32 reserved2;
+ __be32 length;
+
+ union {
+ struct ipr_hostrcb_error error;
+ struct ipr_hostrcb_cfg_ch_not ccn;
+ struct ipr_hostrcb_raw raw;
+ } u;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_hostrcb {
+ struct ipr_hcam hcam;
+ dma_addr_t hostrcb_dma;
+ struct list_head queue;
+};
+
+/* IPR smart dump table structures */
+struct ipr_sdt_entry {
+ __be32 bar_str_offset;
+ __be32 end_offset;
+ u8 entry_byte;
+ u8 reserved[3];
+
+ u8 flags;
+#define IPR_SDT_ENDIAN 0x80
+#define IPR_SDT_VALID_ENTRY 0x20
+
+ u8 resv;
+ __be16 priority;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_sdt_header {
+ __be32 state;
+ __be32 num_entries;
+ __be32 num_entries_used;
+ __be32 dump_size;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_sdt {
+ struct ipr_sdt_header hdr;
+ struct ipr_sdt_entry entry[IPR_NUM_SDT_ENTRIES];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_uc_sdt {
+ struct ipr_sdt_header hdr;
+ struct ipr_sdt_entry entry[1];
+}__attribute__((packed, aligned (4)));
+
+/*
+ * Driver types
+ */
+struct ipr_bus_attributes {
+ u8 bus;
+ u8 qas_enabled;
+ u8 bus_width;
+ u8 reserved;
+ u32 max_xfer_rate;
+};
+
+struct ipr_resource_entry {
+ struct ipr_config_table_entry cfgte;
+ u8 needs_sync_complete:1;
+ u8 in_erp:1;
+ u8 add_to_ml:1;
+ u8 del_from_ml:1;
+ u8 resetting_device:1;
+
+ struct scsi_device *sdev;
+ struct list_head queue;
+};
+
+struct ipr_resource_hdr {
+ u16 num_entries;
+ u16 reserved;
+};
+
+struct ipr_resource_table {
+ struct ipr_resource_hdr hdr;
+ struct ipr_resource_entry dev[IPR_MAX_PHYSICAL_DEVS];
+};
+
+struct ipr_misc_cbs {
+ struct ipr_ioa_vpd ioa_vpd;
+ struct ipr_inquiry_page3 page3_data;
+ struct ipr_mode_pages mode_pages;
+ struct ipr_supported_device supp_dev;
+};
+
+struct ipr_interrupt_offsets {
+ unsigned long set_interrupt_mask_reg;
+ unsigned long clr_interrupt_mask_reg;
+ unsigned long sense_interrupt_mask_reg;
+ unsigned long clr_interrupt_reg;
+
+ unsigned long sense_interrupt_reg;
+ unsigned long ioarrin_reg;
+ unsigned long sense_uproc_interrupt_reg;
+ unsigned long set_uproc_interrupt_reg;
+ unsigned long clr_uproc_interrupt_reg;
+};
+
+struct ipr_interrupts {
+ void __iomem *set_interrupt_mask_reg;
+ void __iomem *clr_interrupt_mask_reg;
+ void __iomem *sense_interrupt_mask_reg;
+ void __iomem *clr_interrupt_reg;
+
+ void __iomem *sense_interrupt_reg;
+ void __iomem *ioarrin_reg;
+ void __iomem *sense_uproc_interrupt_reg;
+ void __iomem *set_uproc_interrupt_reg;
+ void __iomem *clr_uproc_interrupt_reg;
+};
+
+struct ipr_chip_cfg_t {
+ u32 mailbox;
+ u8 cache_line_size;
+ struct ipr_interrupt_offsets regs;
+};
+
+struct ipr_chip_t {
+ u16 vendor;
+ u16 device;
+ const struct ipr_chip_cfg_t *cfg;
+};
+
+enum ipr_shutdown_type {
+ IPR_SHUTDOWN_NORMAL = 0x00,
+ IPR_SHUTDOWN_PREPARE_FOR_NORMAL = 0x40,
+ IPR_SHUTDOWN_ABBREV = 0x80,
+ IPR_SHUTDOWN_NONE = 0x100
+};
+
+struct ipr_trace_entry {
+ u32 time;
+
+ u8 op_code;
+ u8 type;
+#define IPR_TRACE_START 0x00
+#define IPR_TRACE_FINISH 0xff
+ u16 cmd_index;
+
+ __be32 res_handle;
+ union {
+ u32 ioasc;
+ u32 add_data;
+ u32 res_addr;
+ } u;
+};
+
+struct ipr_sglist {
+ u32 order;
+ u32 num_sg;
+ u32 buffer_len;
+ struct scatterlist scatterlist[1];
+};
+
+enum ipr_sdt_state {
+ INACTIVE,
+ WAIT_FOR_DUMP,
+ GET_DUMP,
+ ABORT_DUMP,
+ DUMP_OBTAINED
+};
+
+/* Per-controller data */
+struct ipr_ioa_cfg {
+ char eye_catcher[8];
+#define IPR_EYECATCHER "iprcfg"
+
+ struct list_head queue;
+
+ u8 allow_interrupts:1;
+ u8 in_reset_reload:1;
+ u8 in_ioa_bringdown:1;
+ u8 ioa_unit_checked:1;
+ u8 ioa_is_dead:1;
+ u8 dump_taken:1;
+ u8 allow_cmds:1;
+ u8 allow_ml_add_del:1;
+
+ u16 type; /* CCIN of the card */
+
+ u8 log_level;
+#define IPR_MAX_LOG_LEVEL 4
+#define IPR_DEFAULT_LOG_LEVEL 2
+
+#define IPR_NUM_TRACE_INDEX_BITS 8
+#define IPR_NUM_TRACE_ENTRIES (1 << IPR_NUM_TRACE_INDEX_BITS)
+#define IPR_TRACE_SIZE (sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES)
+ char trace_start[8];
+#define IPR_TRACE_START_LABEL "trace"
+ struct ipr_trace_entry *trace;
+ u32 trace_index:IPR_NUM_TRACE_INDEX_BITS;
+
+ /*
+ * Queue for free command blocks
+ */
+ char ipr_free_label[8];
+#define IPR_FREEQ_LABEL "free-q"
+ struct list_head free_q;
+
+ /*
+ * Queue for command blocks outstanding to the adapter
+ */
+ char ipr_pending_label[8];
+#define IPR_PENDQ_LABEL "pend-q"
+ struct list_head pending_q;
+
+ char cfg_table_start[8];
+#define IPR_CFG_TBL_START "cfg"
+ struct ipr_config_table *cfg_table;
+ dma_addr_t cfg_table_dma;
+
+ char resource_table_label[8];
+#define IPR_RES_TABLE_LABEL "res_tbl"
+ struct ipr_resource_entry *res_entries;
+ struct list_head free_res_q;
+ struct list_head used_res_q;
+
+ char ipr_hcam_label[8];
+#define IPR_HCAM_LABEL "hcams"
+ struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS];
+ dma_addr_t hostrcb_dma[IPR_NUM_HCAMS];
+ struct list_head hostrcb_free_q;
+ struct list_head hostrcb_pending_q;
+
+ __be32 *host_rrq;
+ dma_addr_t host_rrq_dma;
+#define IPR_HRRQ_REQ_RESP_HANDLE_MASK 0xfffffffc
+#define IPR_HRRQ_RESP_BIT_SET 0x00000002
+#define IPR_HRRQ_TOGGLE_BIT 0x00000001
+#define IPR_HRRQ_REQ_RESP_HANDLE_SHIFT 2
+ volatile __be32 *hrrq_start;
+ volatile __be32 *hrrq_end;
+ volatile __be32 *hrrq_curr;
+ volatile u32 toggle_bit;
+
+ struct ipr_bus_attributes bus_attr[IPR_MAX_NUM_BUSES];
+
+ const struct ipr_chip_cfg_t *chip_cfg;
+
+ void __iomem *hdw_dma_regs; /* iomapped PCI memory space */
+ unsigned long hdw_dma_regs_pci; /* raw PCI memory space */
+ void __iomem *ioa_mailbox;
+ struct ipr_interrupts regs;
+
+ u16 saved_pcix_cmd_reg;
+ u16 reset_retries;
+
+ u32 errors_logged;
+
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
+ struct ipr_sglist *ucode_sglist;
+ struct ipr_mode_pages *saved_mode_pages;
+ u8 saved_mode_page_len;
+
+ struct work_struct work_q;
+
+ wait_queue_head_t reset_wait_q;
+
+ struct ipr_dump *dump;
+ enum ipr_sdt_state sdt_state;
+
+ struct ipr_misc_cbs *vpd_cbs;
+ dma_addr_t vpd_cbs_dma;
+
+ struct pci_pool *ipr_cmd_pool;
+
+ struct ipr_cmnd *reset_cmd;
+
+ char ipr_cmd_label[8];
+#define IPR_CMD_LABEL "ipr_cmnd"
+ struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS];
+ u32 ipr_cmnd_list_dma[IPR_NUM_CMD_BLKS];
+};
+
+struct ipr_cmnd {
+ struct ipr_ioarcb ioarcb;
+ struct ipr_ioasa ioasa;
+ struct ipr_ioadl_desc ioadl[IPR_NUM_IOADL_ENTRIES];
+ struct list_head queue;
+ struct scsi_cmnd *scsi_cmd;
+ struct completion completion;
+ struct timer_list timer;
+ void (*done) (struct ipr_cmnd *);
+ int (*job_step) (struct ipr_cmnd *);
+ u16 cmd_index;
+ u8 sense_buffer[SCSI_SENSE_BUFFERSIZE];
+ dma_addr_t sense_buffer_dma;
+ unsigned short dma_use_sg;
+ dma_addr_t dma_handle;
+ struct ipr_cmnd *sibling;
+ union {
+ enum ipr_shutdown_type shutdown_type;
+ struct ipr_hostrcb *hostrcb;
+ unsigned long time_left;
+ unsigned long scratch;
+ struct ipr_resource_entry *res;
+ struct scsi_device *sdev;
+ } u;
+
+ struct ipr_ioa_cfg *ioa_cfg;
+};
+
+struct ipr_ses_table_entry {
+ char product_id[17];
+ char compare_product_id_byte[17];
+ u32 max_bus_speed_limit; /* MB/sec limit for this backplane */
+};
+
+struct ipr_dump_header {
+ u32 eye_catcher;
+#define IPR_DUMP_EYE_CATCHER 0xC5D4E3F2
+ u32 len;
+ u32 num_entries;
+ u32 first_entry_offset;
+ u32 status;
+#define IPR_DUMP_STATUS_SUCCESS 0
+#define IPR_DUMP_STATUS_QUAL_SUCCESS 2
+#define IPR_DUMP_STATUS_FAILED 0xffffffff
+ u32 os;
+#define IPR_DUMP_OS_LINUX 0x4C4E5558
+ u32 driver_name;
+#define IPR_DUMP_DRIVER_NAME 0x49505232
+}__attribute__((packed, aligned (4)));
+
+struct ipr_dump_entry_header {
+ u32 eye_catcher;
+#define IPR_DUMP_EYE_CATCHER 0xC5D4E3F2
+ u32 len;
+ u32 num_elems;
+ u32 offset;
+ u32 data_type;
+#define IPR_DUMP_DATA_TYPE_ASCII 0x41534349
+#define IPR_DUMP_DATA_TYPE_BINARY 0x42494E41
+ u32 id;
+#define IPR_DUMP_IOA_DUMP_ID 0x494F4131
+#define IPR_DUMP_LOCATION_ID 0x4C4F4341
+#define IPR_DUMP_TRACE_ID 0x54524143
+#define IPR_DUMP_DRIVER_VERSION_ID 0x44525652
+#define IPR_DUMP_DRIVER_TYPE_ID 0x54595045
+#define IPR_DUMP_IOA_CTRL_BLK 0x494F4342
+#define IPR_DUMP_PEND_OPS 0x414F5053
+ u32 status;
+}__attribute__((packed, aligned (4)));
+
+struct ipr_dump_location_entry {
+ struct ipr_dump_entry_header hdr;
+ u8 location[BUS_ID_SIZE];
+}__attribute__((packed));
+
+struct ipr_dump_trace_entry {
+ struct ipr_dump_entry_header hdr;
+ u32 trace[IPR_TRACE_SIZE / sizeof(u32)];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_dump_version_entry {
+ struct ipr_dump_entry_header hdr;
+ u8 version[sizeof(IPR_DRIVER_VERSION)];
+};
+
+struct ipr_dump_ioa_type_entry {
+ struct ipr_dump_entry_header hdr;
+ u32 type;
+ u32 fw_version;
+};
+
+struct ipr_driver_dump {
+ struct ipr_dump_header hdr;
+ struct ipr_dump_version_entry version_entry;
+ struct ipr_dump_location_entry location_entry;
+ struct ipr_dump_ioa_type_entry ioa_type_entry;
+ struct ipr_dump_trace_entry trace_entry;
+}__attribute__((packed));
+
+struct ipr_ioa_dump {
+ struct ipr_dump_entry_header hdr;
+ struct ipr_sdt sdt;
+ __be32 *ioa_data[IPR_MAX_NUM_DUMP_PAGES];
+ u32 reserved;
+ u32 next_page_index;
+ u32 page_offset;
+ u32 format;
+#define IPR_SDT_FMT2 2
+#define IPR_SDT_UNKNOWN 3
+}__attribute__((packed, aligned (4)));
+
+struct ipr_dump {
+ struct kref kref;
+ struct ipr_ioa_cfg *ioa_cfg;
+ struct ipr_driver_dump driver_dump;
+ struct ipr_ioa_dump ioa_dump;
+};
+
+struct ipr_error_table_t {
+ u32 ioasc;
+ int log_ioasa;
+ int log_hcam;
+ char *error;
+};
+
+struct ipr_software_inq_lid_info {
+ __be32 load_id;
+ __be32 timestamp[3];
+}__attribute__((packed, aligned (4)));
+
+struct ipr_ucode_image_header {
+ __be32 header_length;
+ __be32 lid_table_offset;
+ u8 major_release;
+ u8 card_type;
+ u8 minor_release[2];
+ u8 reserved[20];
+ char eyecatcher[16];
+ __be32 num_lids;
+ struct ipr_software_inq_lid_info lid[1];
+}__attribute__((packed, aligned (4)));
+
+/*
+ * Macros
+ */
+#if IPR_DEBUG
+#define IPR_DBG_CMD(CMD) do { CMD; } while (0)
+#else
+#define IPR_DBG_CMD(CMD)
+#endif
+
+#ifdef CONFIG_SCSI_IPR_TRACE
+#define ipr_create_trace_file(kobj, attr) sysfs_create_bin_file(kobj, attr)
+#define ipr_remove_trace_file(kobj, attr) sysfs_remove_bin_file(kobj, attr)
+#else
+#define ipr_create_trace_file(kobj, attr) 0
+#define ipr_remove_trace_file(kobj, attr) do { } while(0)
+#endif
+
+#ifdef CONFIG_SCSI_IPR_DUMP
+#define ipr_create_dump_file(kobj, attr) sysfs_create_bin_file(kobj, attr)
+#define ipr_remove_dump_file(kobj, attr) sysfs_remove_bin_file(kobj, attr)
+#else
+#define ipr_create_dump_file(kobj, attr) 0
+#define ipr_remove_dump_file(kobj, attr) do { } while(0)
+#endif
+
+/*
+ * Error logging macros
+ */
+#define ipr_err(...) printk(KERN_ERR IPR_NAME ": "__VA_ARGS__)
+#define ipr_info(...) printk(KERN_INFO IPR_NAME ": "__VA_ARGS__)
+#define ipr_crit(...) printk(KERN_CRIT IPR_NAME ": "__VA_ARGS__)
+#define ipr_warn(...) printk(KERN_WARNING IPR_NAME": "__VA_ARGS__)
+#define ipr_dbg(...) IPR_DBG_CMD(printk(KERN_INFO IPR_NAME ": "__VA_ARGS__))
+
+#define ipr_sdev_printk(level, sdev, fmt, ...) \
+ printk(level IPR_NAME ": %d:%d:%d:%d: " fmt, sdev->host->host_no, \
+ sdev->channel, sdev->id, sdev->lun, ##__VA_ARGS__)
+
+#define ipr_sdev_err(sdev, fmt, ...) \
+ ipr_sdev_printk(KERN_ERR, sdev, fmt, ##__VA_ARGS__)
+
+#define ipr_sdev_info(sdev, fmt, ...) \
+ ipr_sdev_printk(KERN_INFO, sdev, fmt, ##__VA_ARGS__)
+
+#define ipr_sdev_dbg(sdev, fmt, ...) \
+ IPR_DBG_CMD(ipr_sdev_printk(KERN_INFO, sdev, fmt, ##__VA_ARGS__))
+
+#define ipr_res_printk(level, ioa_cfg, res, fmt, ...) \
+ printk(level IPR_NAME ": %d:%d:%d:%d: " fmt, ioa_cfg->host->host_no, \
+ res.bus, res.target, res.lun, ##__VA_ARGS__)
+
+#define ipr_res_err(ioa_cfg, res, fmt, ...) \
+ ipr_res_printk(KERN_ERR, ioa_cfg, res, fmt, ##__VA_ARGS__)
+#define ipr_res_dbg(ioa_cfg, res, fmt, ...) \
+ IPR_DBG_CMD(ipr_res_printk(KERN_INFO, ioa_cfg, res, fmt, ##__VA_ARGS__))
+
+#define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\
+ __FILE__, __FUNCTION__, __LINE__)
+
+#if IPR_DBG_TRACE
+#define ENTER printk(KERN_INFO IPR_NAME": Entering %s\n", __FUNCTION__)
+#define LEAVE printk(KERN_INFO IPR_NAME": Leaving %s\n", __FUNCTION__)
+#else
+#define ENTER
+#define LEAVE
+#endif
+
+#define ipr_err_separator \
+ipr_err("----------------------------------------------------------\n")
+
+
+/*
+ * Inlines
+ */
+
+/**
+ * ipr_is_ioa_resource - Determine if a resource is the IOA
+ * @res: resource entry struct
+ *
+ * Return value:
+ * 1 if IOA / 0 if not IOA
+ **/
+static inline int ipr_is_ioa_resource(struct ipr_resource_entry *res)
+{
+ return (res->cfgte.flags & IPR_IS_IOA_RESOURCE) ? 1 : 0;
+}
+
+/**
+ * ipr_is_af_dasd_device - Determine if a resource is an AF DASD
+ * @res: resource entry struct
+ *
+ * Return value:
+ * 1 if AF DASD / 0 if not AF DASD
+ **/
+static inline int ipr_is_af_dasd_device(struct ipr_resource_entry *res)
+{
+ if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data) &&
+ !ipr_is_ioa_resource(res) &&
+ IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_AF_DASD)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * ipr_is_vset_device - Determine if a resource is a VSET
+ * @res: resource entry struct
+ *
+ * Return value:
+ * 1 if VSET / 0 if not VSET
+ **/
+static inline int ipr_is_vset_device(struct ipr_resource_entry *res)
+{
+ if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data) &&
+ !ipr_is_ioa_resource(res) &&
+ IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_VOLUME_SET)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * ipr_is_gscsi - Determine if a resource is a generic scsi resource
+ * @res: resource entry struct
+ *
+ * Return value:
+ * 1 if GSCSI / 0 if not GSCSI
+ **/
+static inline int ipr_is_gscsi(struct ipr_resource_entry *res)
+{
+ if (!ipr_is_ioa_resource(res) &&
+ IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_GENERIC_SCSI)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * ipr_is_device - Determine if resource address is that of a device
+ * @res_addr: resource address struct
+ *
+ * Return value:
+ * 1 if AF / 0 if not AF
+ **/
+static inline int ipr_is_device(struct ipr_res_addr *res_addr)
+{
+ if ((res_addr->bus < IPR_MAX_NUM_BUSES) &&
+ (res_addr->target < IPR_MAX_NUM_TARGETS_PER_BUS))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * ipr_sdt_is_fmt2 - Determine if a SDT address is in format 2
+ * @sdt_word: SDT address
+ *
+ * Return value:
+ * 1 if format 2 / 0 if not
+ **/
+static inline int ipr_sdt_is_fmt2(u32 sdt_word)
+{
+ u32 bar_sel = IPR_GET_FMT2_BAR_SEL(sdt_word);
+
+ switch (bar_sel) {
+ case IPR_SDT_FMT2_BAR0_SEL:
+ case IPR_SDT_FMT2_BAR1_SEL:
+ case IPR_SDT_FMT2_BAR2_SEL:
+ case IPR_SDT_FMT2_BAR3_SEL:
+ case IPR_SDT_FMT2_BAR4_SEL:
+ case IPR_SDT_FMT2_BAR5_SEL:
+ case IPR_SDT_FMT2_EXP_ROM_SEL:
+ return 1;
+ };
+
+ return 0;
+}
+
+#endif
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
new file mode 100644
index 000000000000..e46096da8db2
--- /dev/null
+++ b/drivers/scsi/ips.c
@@ -0,0 +1,7491 @@
+/*****************************************************************************/
+/* ips.c -- driver for the Adaptec / IBM ServeRAID controller */
+/* */
+/* Written By: Keith Mitchell, IBM Corporation */
+/* Jack Hammer, Adaptec, Inc. */
+/* David Jeffery, Adaptec, Inc. */
+/* */
+/* Copyright (C) 2000 IBM Corporation */
+/* Copyright (C) 2002,2003 Adaptec, Inc. */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* NO WARRANTY */
+/* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR */
+/* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT */
+/* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, */
+/* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is */
+/* solely responsible for determining the appropriateness of using and */
+/* distributing the Program and assumes all risks associated with its */
+/* exercise of rights under this Agreement, including but not limited to */
+/* the risks and costs of program errors, damage to or loss of data, */
+/* programs or equipment, and unavailability or interruption of operations. */
+/* */
+/* DISCLAIMER OF LIABILITY */
+/* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY */
+/* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL */
+/* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND */
+/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR */
+/* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE */
+/* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED */
+/* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* */
+/* Bugs/Comments/Suggestions about this driver should be mailed to: */
+/* ipslinux@adaptec.com */
+/* */
+/* For system support issues, contact your local IBM Customer support. */
+/* Directions to find IBM Customer Support for each country can be found at: */
+/* http://www.ibm.com/planetwide/ */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* Change Log */
+/* */
+/* 0.99.02 - Breakup commands that are bigger than 8 * the stripe size */
+/* 0.99.03 - Make interrupt routine handle all completed request on the */
+/* adapter not just the first one */
+/* - Make sure passthru commands get woken up if we run out of */
+/* SCBs */
+/* - Send all of the commands on the queue at once rather than */
+/* one at a time since the card will support it. */
+/* 0.99.04 - Fix race condition in the passthru mechanism -- this required */
+/* the interface to the utilities to change */
+/* - Fix error recovery code */
+/* 0.99.05 - Fix an oops when we get certain passthru commands */
+/* 1.00.00 - Initial Public Release */
+/* Functionally equivalent to 0.99.05 */
+/* 3.60.00 - Bump max commands to 128 for use with firmware 3.60 */
+/* - Change version to 3.60 to coincide with release numbering. */
+/* 3.60.01 - Remove bogus error check in passthru routine */
+/* 3.60.02 - Make DCDB direction based on lookup table */
+/* - Only allow one DCDB command to a SCSI ID at a time */
+/* 4.00.00 - Add support for ServeRAID 4 */
+/* 4.00.01 - Add support for First Failure Data Capture */
+/* 4.00.02 - Fix problem with PT DCDB with no buffer */
+/* 4.00.03 - Add alternative passthru interface */
+/* - Add ability to flash BIOS */
+/* 4.00.04 - Rename structures/constants to be prefixed with IPS_ */
+/* 4.00.05 - Remove wish_block from init routine */
+/* - Use linux/spinlock.h instead of asm/spinlock.h for kernels */
+/* 2.3.18 and later */
+/* - Sync with other changes from the 2.3 kernels */
+/* 4.00.06 - Fix timeout with initial FFDC command */
+/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@infradead.org> */
+/* 4.10.00 - Add support for ServeRAID 4M/4L */
+/* 4.10.13 - Fix for dynamic unload and proc file system */
+/* 4.20.03 - Rename version to coincide with new release schedules */
+/* Performance fixes */
+/* Fix truncation of /proc files with cat */
+/* Merge in changes through kernel 2.4.0test1ac21 */
+/* 4.20.13 - Fix some failure cases / reset code */
+/* - Hook into the reboot_notifier to flush the controller cache */
+/* 4.50.01 - Fix problem when there is a hole in logical drive numbering */
+/* 4.70.09 - Use a Common ( Large Buffer ) for Flashing from the JCRM CD */
+/* - Add IPSSEND Flash Support */
+/* - Set Sense Data for Unknown SCSI Command */
+/* - Use Slot Number from NVRAM Page 5 */
+/* - Restore caller's DCDB Structure */
+/* 4.70.12 - Corrective actions for bad controller ( during initialization )*/
+/* 4.70.13 - Don't Send CDB's if we already know the device is not present */
+/* - Don't release HA Lock in ips_next() until SC taken off queue */
+/* - Unregister SCSI device in ips_release() */
+/* 4.70.15 - Fix Breakup for very large ( non-SG ) requests in ips_done() */
+/* 4.71.00 - Change all memory allocations to not use GFP_DMA flag */
+/* Code Clean-Up for 2.4.x kernel */
+/* 4.72.00 - Allow for a Scatter-Gather Element to exceed MAX_XFER Size */
+/* 4.72.01 - I/O Mapped Memory release ( so "insmod ips" does not Fail ) */
+/* - Don't Issue Internal FFDC Command if there are Active Commands */
+/* - Close Window for getting too many IOCTL's active */
+/* 4.80.00 - Make ia64 Safe */
+/* 4.80.04 - Eliminate calls to strtok() if 2.4.x or greater */
+/* - Adjustments to Device Queue Depth */
+/* 4.80.14 - Take all semaphores off stack */
+/* - Clean Up New_IOCTL path */
+/* 4.80.20 - Set max_sectors in Scsi_Host structure ( if >= 2.4.7 kernel ) */
+/* - 5 second delay needed after resetting an i960 adapter */
+/* 4.80.26 - Clean up potential code problems ( Arjan's recommendations ) */
+/* 4.90.01 - Version Matching for FirmWare, BIOS, and Driver */
+/* 4.90.05 - Use New PCI Architecture to facilitate Hot Plug Development */
+/* 4.90.08 - Increase Delays in Flashing ( Trombone Only - 4H ) */
+/* 4.90.08 - Data Corruption if First Scatter Gather Element is > 64K */
+/* 4.90.11 - Don't actually RESET unless it's physically required */
+/* - Remove unused compile options */
+/* 5.00.01 - Sarasota ( 5i ) adapters must always be scanned first */
+/* - Get rid on IOCTL_NEW_COMMAND code */
+/* - Add Extended DCDB Commands for Tape Support in 5I */
+/* 5.10.12 - use pci_dma interfaces, update for 2.5 kernel changes */
+/* 5.10.15 - remove unused code (sem, macros, etc.) */
+/* 5.30.00 - use __devexit_p() */
+/* 6.00.00 - Add 6x Adapters and Battery Flash */
+/* 6.10.00 - Remove 1G Addressing Limitations */
+/* 6.11.xx - Get VersionInfo buffer off the stack ! DDTS 60401 */
+/* 6.11.xx - Make Logical Drive Info structure safe for DMA DDTS 60639 */
+/* 7.10.xx - Add highmem_io flag in SCSI Templete for 2.4 kernels */
+/* - Fix path/name for scsi_hosts.h include for 2.6 kernels */
+/* - Fix sort order of 7k */
+/* - Remove 3 unused "inline" functions */
+/*****************************************************************************/
+
+/*
+ * Conditional Compilation directives for this driver:
+ *
+ * IPS_DEBUG - Turn on debugging info
+ *
+ * Parameters:
+ *
+ * debug:<number> - Set debug level to <number>
+ * NOTE: only works when IPS_DEBUG compile directive is used.
+ * 1 - Normal debug messages
+ * 2 - Verbose debug messages
+ * 11 - Method trace (non interrupt)
+ * 12 - Method trace (includes interrupt)
+ *
+ * noi2o - Don't use I2O Queues (ServeRAID 4 only)
+ * nommap - Don't use memory mapped I/O
+ * ioctlsize - Initial size of the IOCTL buffer
+ */
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/page.h>
+#include <linux/stddef.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+
+#include <linux/blkdev.h>
+#include <linux/types.h>
+
+#include <scsi/sg.h>
+
+#include "scsi.h"
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
+#include "hosts.h"
+#else
+#include <scsi/scsi_host.h>
+#endif
+
+#include "ips.h"
+
+#include <linux/module.h>
+
+#include <linux/stat.h>
+#include <linux/config.h>
+
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <linux/smp.h>
+
+#ifdef MODULE
+static char *ips = NULL;
+module_param(ips, charp, 0);
+#endif
+
+/*
+ * DRIVER_VER
+ */
+#define IPS_VERSION_HIGH "7.10"
+#define IPS_VERSION_LOW ".18 "
+
+#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
+#warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
+#include <linux/blk.h>
+#include "sd.h"
+#define IPS_SG_ADDRESS(sg) ((sg)->address)
+#define IPS_LOCK_SAVE(lock,flags) spin_lock_irqsave(&io_request_lock,flags)
+#define IPS_UNLOCK_RESTORE(lock,flags) spin_unlock_irqrestore(&io_request_lock,flags)
+#ifndef __devexit_p
+#define __devexit_p(x) x
+#endif
+#else
+#define IPS_SG_ADDRESS(sg) (page_address((sg)->page) ? \
+ page_address((sg)->page)+(sg)->offset : NULL)
+#define IPS_LOCK_SAVE(lock,flags) do{spin_lock(lock);(void)flags;}while(0)
+#define IPS_UNLOCK_RESTORE(lock,flags) do{spin_unlock(lock);(void)flags;}while(0)
+#endif
+
+#define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
+ SCSI_DATA_NONE == scb->scsi_cmd->sc_data_direction) ? \
+ PCI_DMA_BIDIRECTIONAL : \
+ scsi_to_pci_dma_dir(scb->scsi_cmd->sc_data_direction))
+
+#ifdef IPS_DEBUG
+#define METHOD_TRACE(s, i) if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
+#define DEBUG(i, s) if (ips_debug >= i) printk(KERN_NOTICE s "\n");
+#define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
+#else
+#define METHOD_TRACE(s, i)
+#define DEBUG(i, s)
+#define DEBUG_VAR(i, s, v...)
+#endif
+
+/*
+ * Function prototypes
+ */
+static int ips_detect(Scsi_Host_Template *);
+static int ips_release(struct Scsi_Host *);
+static int ips_eh_abort(Scsi_Cmnd *);
+static int ips_eh_reset(Scsi_Cmnd *);
+static int ips_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+static const char *ips_info(struct Scsi_Host *);
+static irqreturn_t do_ipsintr(int, void *, struct pt_regs *);
+static int ips_hainit(ips_ha_t *);
+static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
+static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
+static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
+static int ips_online(ips_ha_t *, ips_scb_t *);
+static int ips_inquiry(ips_ha_t *, ips_scb_t *);
+static int ips_rdcap(ips_ha_t *, ips_scb_t *);
+static int ips_msense(ips_ha_t *, ips_scb_t *);
+static int ips_reqsen(ips_ha_t *, ips_scb_t *);
+static int ips_deallocatescbs(ips_ha_t *, int);
+static int ips_allocatescbs(ips_ha_t *);
+static int ips_reset_copperhead(ips_ha_t *);
+static int ips_reset_copperhead_memio(ips_ha_t *);
+static int ips_reset_morpheus(ips_ha_t *);
+static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
+static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
+static int ips_isintr_copperhead(ips_ha_t *);
+static int ips_isintr_copperhead_memio(ips_ha_t *);
+static int ips_isintr_morpheus(ips_ha_t *);
+static int ips_wait(ips_ha_t *, int, int);
+static int ips_write_driver_status(ips_ha_t *, int);
+static int ips_read_adapter_status(ips_ha_t *, int);
+static int ips_read_subsystem_parameters(ips_ha_t *, int);
+static int ips_read_config(ips_ha_t *, int);
+static int ips_clear_adapter(ips_ha_t *, int);
+static int ips_readwrite_page5(ips_ha_t *, int, int);
+static int ips_init_copperhead(ips_ha_t *);
+static int ips_init_copperhead_memio(ips_ha_t *);
+static int ips_init_morpheus(ips_ha_t *);
+static int ips_isinit_copperhead(ips_ha_t *);
+static int ips_isinit_copperhead_memio(ips_ha_t *);
+static int ips_isinit_morpheus(ips_ha_t *);
+static int ips_erase_bios(ips_ha_t *);
+static int ips_program_bios(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_verify_bios(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_erase_bios_memio(ips_ha_t *);
+static int ips_program_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_verify_bios_memio(ips_ha_t *, char *, uint32_t, uint32_t);
+static int ips_flash_copperhead(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static int ips_flash_bios(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static int ips_flash_firmware(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static void ips_free_flash_copperhead(ips_ha_t * ha);
+static void ips_get_bios_version(ips_ha_t *, int);
+static void ips_identify_controller(ips_ha_t *);
+static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
+static void ips_enable_int_copperhead(ips_ha_t *);
+static void ips_enable_int_copperhead_memio(ips_ha_t *);
+static void ips_enable_int_morpheus(ips_ha_t *);
+static int ips_intr_copperhead(ips_ha_t *);
+static int ips_intr_morpheus(ips_ha_t *);
+static void ips_next(ips_ha_t *, int);
+static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
+static void ipsintr_done(ips_ha_t *, struct ips_scb *);
+static void ips_done(ips_ha_t *, ips_scb_t *);
+static void ips_free(ips_ha_t *);
+static void ips_init_scb(ips_ha_t *, ips_scb_t *);
+static void ips_freescb(ips_ha_t *, ips_scb_t *);
+static void ips_setup_funclist(ips_ha_t *);
+static void ips_statinit(ips_ha_t *);
+static void ips_statinit_memio(ips_ha_t *);
+static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
+static void ips_ffdc_reset(ips_ha_t *, int);
+static void ips_ffdc_time(ips_ha_t *);
+static uint32_t ips_statupd_copperhead(ips_ha_t *);
+static uint32_t ips_statupd_copperhead_memio(ips_ha_t *);
+static uint32_t ips_statupd_morpheus(ips_ha_t *);
+static ips_scb_t *ips_getscb(ips_ha_t *);
+static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *);
+static void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *);
+static void ips_putq_copp_tail(ips_copp_queue_t *,
+ ips_copp_wait_item_t *);
+static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *);
+static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *);
+static Scsi_Cmnd *ips_removeq_wait_head(ips_wait_queue_t *);
+static Scsi_Cmnd *ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *);
+static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *,
+ ips_copp_wait_item_t *);
+static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *);
+
+static int ips_is_passthru(Scsi_Cmnd *);
+static int ips_make_passthru(ips_ha_t *, Scsi_Cmnd *, ips_scb_t *, int);
+static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *);
+static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *);
+static void ips_scmd_buf_write(Scsi_Cmnd * scmd, void *data,
+ unsigned int count);
+static void ips_scmd_buf_read(Scsi_Cmnd * scmd, void *data, unsigned int count);
+
+static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+static int ips_host_info(ips_ha_t *, char *, off_t, int);
+static void copy_mem_info(IPS_INFOSTR *, char *, int);
+static int copy_info(IPS_INFOSTR *, char *, ...);
+static int ips_get_version_info(ips_ha_t * ha, dma_addr_t, int intr);
+static void ips_version_check(ips_ha_t * ha, int intr);
+static int ips_abort_init(ips_ha_t * ha, int index);
+static int ips_init_phase2(int index);
+
+static int ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr);
+static int ips_register_scsi(int index);
+
+/*
+ * global variables
+ */
+static const char ips_name[] = "ips";
+static struct Scsi_Host *ips_sh[IPS_MAX_ADAPTERS]; /* Array of host controller structures */
+static ips_ha_t *ips_ha[IPS_MAX_ADAPTERS]; /* Array of HA structures */
+static unsigned int ips_next_controller;
+static unsigned int ips_num_controllers;
+static unsigned int ips_released_controllers;
+static int ips_hotplug;
+static int ips_cmd_timeout = 60;
+static int ips_reset_timeout = 60 * 5;
+static int ips_force_memio = 1; /* Always use Memory Mapped I/O */
+static int ips_force_i2o = 1; /* Always use I2O command delivery */
+static int ips_ioctlsize = IPS_IOCTL_SIZE; /* Size of the ioctl buffer */
+static int ips_cd_boot; /* Booting from Manager CD */
+static char *ips_FlashData = NULL; /* CD Boot - Flash Data Buffer */
+static dma_addr_t ips_flashbusaddr;
+static long ips_FlashDataInUse; /* CD Boot - Flash Data In Use Flag */
+static uint32_t MaxLiteCmds = 32; /* Max Active Cmds for a Lite Adapter */
+static Scsi_Host_Template ips_driver_template = {
+ .detect = ips_detect,
+ .release = ips_release,
+ .info = ips_info,
+ .queuecommand = ips_queue,
+ .eh_abort_handler = ips_eh_abort,
+ .eh_host_reset_handler = ips_eh_reset,
+ .proc_name = "ips",
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+ .proc_info = ips_proc_info,
+ .slave_configure = ips_slave_configure,
+#else
+ .proc_info = ips_proc24_info,
+ .select_queue_depths = ips_select_queue_depth,
+#endif
+ .bios_param = ips_biosparam,
+ .this_id = -1,
+ .sg_tablesize = IPS_MAX_SG,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ .use_new_eh_code = 1,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ .highmem_io = 1,
+#endif
+};
+
+static IPS_DEFINE_COMPAT_TABLE( Compatable ); /* Version Compatability Table */
+
+
+/* This table describes all ServeRAID Adapters */
+static struct pci_device_id ips_pci_table[] = {
+ { 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+ { 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+ { 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE( pci, ips_pci_table );
+
+static char ips_hot_plug_name[] = "ips";
+
+static int __devinit ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent);
+static void __devexit ips_remove_device(struct pci_dev *pci_dev);
+
+static struct pci_driver ips_pci_driver = {
+ .name = ips_hot_plug_name,
+ .id_table = ips_pci_table,
+ .probe = ips_insert_device,
+ .remove = __devexit_p(ips_remove_device),
+};
+
+
+/*
+ * Necessary forward function protoypes
+ */
+static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
+
+#define MAX_ADAPTER_NAME 15
+
+static char ips_adapter_name[][30] = {
+ "ServeRAID",
+ "ServeRAID II",
+ "ServeRAID on motherboard",
+ "ServeRAID on motherboard",
+ "ServeRAID 3H",
+ "ServeRAID 3L",
+ "ServeRAID 4H",
+ "ServeRAID 4M",
+ "ServeRAID 4L",
+ "ServeRAID 4Mx",
+ "ServeRAID 4Lx",
+ "ServeRAID 5i",
+ "ServeRAID 5i",
+ "ServeRAID 6M",
+ "ServeRAID 6i",
+ "ServeRAID 7t",
+ "ServeRAID 7k",
+ "ServeRAID 7M"
+};
+
+static struct notifier_block ips_notifier = {
+ ips_halt, NULL, 0
+};
+
+/*
+ * Direction table
+ */
+static char ips_command_direction[] = {
+ IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT,
+ IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK,
+ IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT,
+ IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT,
+ IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN,
+ IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
+ IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT,
+ IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT,
+ IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE,
+ IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK,
+ IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE,
+ IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT,
+ IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE,
+ IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK,
+ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK
+};
+
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_setup */
+/* */
+/* Routine Description: */
+/* */
+/* setup parameters to the driver */
+/* */
+/****************************************************************************/
+static int
+ips_setup(char *ips_str)
+{
+
+ int i;
+ char *key;
+ char *value;
+ IPS_OPTION options[] = {
+ {"noi2o", &ips_force_i2o, 0},
+ {"nommap", &ips_force_memio, 0},
+ {"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE},
+ {"cdboot", &ips_cd_boot, 0},
+ {"maxcmds", &MaxLiteCmds, 32},
+ };
+
+ /* Don't use strtok() anymore ( if 2.4 Kernel or beyond ) */
+ /* Search for value */
+ while ((key = strsep(&ips_str, ",."))) {
+ if (!*key)
+ continue;
+ value = strchr(key, ':');
+ if (value)
+ *value++ = '\0';
+ /*
+ * We now have key/value pairs.
+ * Update the variables
+ */
+ for (i = 0; i < (sizeof (options) / sizeof (options[0])); i++) {
+ if (strnicmp
+ (key, options[i].option_name,
+ strlen(options[i].option_name)) == 0) {
+ if (value)
+ *options[i].option_flag =
+ simple_strtoul(value, NULL, 0);
+ else
+ *options[i].option_flag =
+ options[i].option_value;
+ break;
+ }
+ }
+ }
+
+ return (1);
+}
+
+__setup("ips=", ips_setup);
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_detect */
+/* */
+/* Routine Description: */
+/* */
+/* Detect and initialize the driver */
+/* */
+/* NOTE: this routine is called under the io_request_lock spinlock */
+/* */
+/****************************************************************************/
+static int
+ips_detect(Scsi_Host_Template * SHT)
+{
+ int i;
+
+ METHOD_TRACE("ips_detect", 1);
+
+#ifdef MODULE
+ if (ips)
+ ips_setup(ips);
+#endif
+
+ for (i = 0; i < ips_num_controllers; i++) {
+ if (ips_register_scsi(i))
+ ips_free(ips_ha[i]);
+ ips_released_controllers++;
+ }
+ ips_hotplug = 1;
+ return (ips_num_controllers);
+}
+
+/****************************************************************************/
+/* configure the function pointers to use the functions that will work */
+/* with the found version of the adapter */
+/****************************************************************************/
+static void
+ips_setup_funclist(ips_ha_t * ha)
+{
+
+ /*
+ * Setup Functions
+ */
+ if (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha)) {
+ /* morpheus / marco / sebring */
+ ha->func.isintr = ips_isintr_morpheus;
+ ha->func.isinit = ips_isinit_morpheus;
+ ha->func.issue = ips_issue_i2o_memio;
+ ha->func.init = ips_init_morpheus;
+ ha->func.statupd = ips_statupd_morpheus;
+ ha->func.reset = ips_reset_morpheus;
+ ha->func.intr = ips_intr_morpheus;
+ ha->func.enableint = ips_enable_int_morpheus;
+ } else if (IPS_USE_MEMIO(ha)) {
+ /* copperhead w/MEMIO */
+ ha->func.isintr = ips_isintr_copperhead_memio;
+ ha->func.isinit = ips_isinit_copperhead_memio;
+ ha->func.init = ips_init_copperhead_memio;
+ ha->func.statupd = ips_statupd_copperhead_memio;
+ ha->func.statinit = ips_statinit_memio;
+ ha->func.reset = ips_reset_copperhead_memio;
+ ha->func.intr = ips_intr_copperhead;
+ ha->func.erasebios = ips_erase_bios_memio;
+ ha->func.programbios = ips_program_bios_memio;
+ ha->func.verifybios = ips_verify_bios_memio;
+ ha->func.enableint = ips_enable_int_copperhead_memio;
+ if (IPS_USE_I2O_DELIVER(ha))
+ ha->func.issue = ips_issue_i2o_memio;
+ else
+ ha->func.issue = ips_issue_copperhead_memio;
+ } else {
+ /* copperhead */
+ ha->func.isintr = ips_isintr_copperhead;
+ ha->func.isinit = ips_isinit_copperhead;
+ ha->func.init = ips_init_copperhead;
+ ha->func.statupd = ips_statupd_copperhead;
+ ha->func.statinit = ips_statinit;
+ ha->func.reset = ips_reset_copperhead;
+ ha->func.intr = ips_intr_copperhead;
+ ha->func.erasebios = ips_erase_bios;
+ ha->func.programbios = ips_program_bios;
+ ha->func.verifybios = ips_verify_bios;
+ ha->func.enableint = ips_enable_int_copperhead;
+
+ if (IPS_USE_I2O_DELIVER(ha))
+ ha->func.issue = ips_issue_i2o;
+ else
+ ha->func.issue = ips_issue_copperhead;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_release */
+/* */
+/* Routine Description: */
+/* */
+/* Remove a driver */
+/* */
+/****************************************************************************/
+static int
+ips_release(struct Scsi_Host *sh)
+{
+ ips_scb_t *scb;
+ ips_ha_t *ha;
+ int i;
+
+ METHOD_TRACE("ips_release", 1);
+
+ for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++) ;
+
+ if (i == IPS_MAX_ADAPTERS) {
+ printk(KERN_WARNING
+ "(%s) release, invalid Scsi_Host pointer.\n", ips_name);
+ BUG();
+ return (FALSE);
+ }
+
+ ha = IPS_HA(sh);
+
+ if (!ha)
+ return (FALSE);
+
+ /* flush the cache on the controller */
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FLUSH;
+
+ scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+ scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flush_cache.state = IPS_NORM_STATE;
+ scb->cmd.flush_cache.reserved = 0;
+ scb->cmd.flush_cache.reserved2 = 0;
+ scb->cmd.flush_cache.reserved3 = 0;
+ scb->cmd.flush_cache.reserved4 = 0;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
+
+ /* send command */
+ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Incomplete Flush.\n");
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Complete.\n");
+
+ ips_sh[i] = NULL;
+ ips_ha[i] = NULL;
+
+ /* free extra memory */
+ ips_free(ha);
+
+ /* Free I/O Region */
+ if (ha->io_addr)
+ release_region(ha->io_addr, ha->io_len);
+
+ /* free IRQ */
+ free_irq(ha->irq, ha);
+
+ IPS_REMOVE_HOST(sh);
+ scsi_host_put(sh);
+
+ ips_released_controllers++;
+
+ return (FALSE);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_halt */
+/* */
+/* Routine Description: */
+/* */
+/* Perform cleanup when the system reboots */
+/* */
+/****************************************************************************/
+static int
+ips_halt(struct notifier_block *nb, ulong event, void *buf)
+{
+ ips_scb_t *scb;
+ ips_ha_t *ha;
+ int i;
+
+ if ((event != SYS_RESTART) && (event != SYS_HALT) &&
+ (event != SYS_POWER_OFF))
+ return (NOTIFY_DONE);
+
+ for (i = 0; i < ips_next_controller; i++) {
+ ha = (ips_ha_t *) ips_ha[i];
+
+ if (!ha)
+ continue;
+
+ if (!ha->active)
+ continue;
+
+ /* flush the cache on the controller */
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FLUSH;
+
+ scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+ scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flush_cache.state = IPS_NORM_STATE;
+ scb->cmd.flush_cache.reserved = 0;
+ scb->cmd.flush_cache.reserved2 = 0;
+ scb->cmd.flush_cache.reserved3 = 0;
+ scb->cmd.flush_cache.reserved4 = 0;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n");
+
+ /* send command */
+ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) ==
+ IPS_FAILURE)
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Incomplete Flush.\n");
+ else
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Flushing Complete.\n");
+ }
+
+ return (NOTIFY_OK);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_eh_abort */
+/* */
+/* Routine Description: */
+/* */
+/* Abort a command (using the new error code stuff) */
+/* Note: this routine is called under the io_request_lock */
+/****************************************************************************/
+int
+ips_eh_abort(Scsi_Cmnd * SC)
+{
+ ips_ha_t *ha;
+ ips_copp_wait_item_t *item;
+ int ret;
+
+ METHOD_TRACE("ips_eh_abort", 1);
+
+ if (!SC)
+ return (FAILED);
+
+ ha = (ips_ha_t *) SC->device->host->hostdata;
+
+ if (!ha)
+ return (FAILED);
+
+ if (!ha->active)
+ return (FAILED);
+
+ if (SC->serial_number != SC->serial_number_at_timeout) {
+ /* HMM, looks like a bogus command */
+ DEBUG(1, "Abort called with bogus scsi command");
+
+ return (FAILED);
+ }
+
+ /* See if the command is on the copp queue */
+ item = ha->copp_waitlist.head;
+ while ((item) && (item->scsi_cmd != SC))
+ item = item->next;
+
+ if (item) {
+ /* Found it */
+ ips_removeq_copp(&ha->copp_waitlist, item);
+ ret = (SUCCESS);
+
+ /* See if the command is on the wait queue */
+ } else if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
+ /* command not sent yet */
+ ret = (SUCCESS);
+ } else {
+ /* command must have already been sent */
+ ret = (FAILED);
+ }
+ return ret;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_eh_reset */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller (with new eh error code) */
+/* */
+/* NOTE: this routine is called under the io_request_lock spinlock */
+/* */
+/****************************************************************************/
+static int
+ips_eh_reset(Scsi_Cmnd * SC)
+{
+ int ret;
+ int i;
+ ips_ha_t *ha;
+ ips_scb_t *scb;
+ ips_copp_wait_item_t *item;
+
+ METHOD_TRACE("ips_eh_reset", 1);
+
+#ifdef NO_IPS_RESET
+ return (FAILED);
+#else
+
+ if (!SC) {
+ DEBUG(1, "Reset called with NULL scsi command");
+
+ return (FAILED);
+ }
+
+ ha = (ips_ha_t *) SC->device->host->hostdata;
+
+ if (!ha) {
+ DEBUG(1, "Reset called with NULL ha struct");
+
+ return (FAILED);
+ }
+
+ if (!ha->active)
+ return (FAILED);
+
+ /* See if the command is on the copp queue */
+ item = ha->copp_waitlist.head;
+ while ((item) && (item->scsi_cmd != SC))
+ item = item->next;
+
+ if (item) {
+ /* Found it */
+ ips_removeq_copp(&ha->copp_waitlist, item);
+ return (SUCCESS);
+ }
+
+ /* See if the command is on the wait queue */
+ if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
+ /* command not sent yet */
+ return (SUCCESS);
+ }
+
+ /* An explanation for the casual observer: */
+ /* Part of the function of a RAID controller is automatic error */
+ /* detection and recovery. As such, the only problem that physically */
+ /* resetting an adapter will ever fix is when, for some reason, */
+ /* the driver is not successfully communicating with the adapter. */
+ /* Therefore, we will attempt to flush this adapter. If that succeeds, */
+ /* then there's no real purpose in a physical reset. This will complete */
+ /* much faster and avoids any problems that might be caused by a */
+ /* physical reset ( such as having to fail all the outstanding I/O's ). */
+
+ if (ha->ioctl_reset == 0) { /* IF Not an IOCTL Requested Reset */
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FLUSH;
+
+ scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+ scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flush_cache.state = IPS_NORM_STATE;
+ scb->cmd.flush_cache.reserved = 0;
+ scb->cmd.flush_cache.reserved2 = 0;
+ scb->cmd.flush_cache.reserved3 = 0;
+ scb->cmd.flush_cache.reserved4 = 0;
+
+ /* Attempt the flush command */
+ ret = ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_IORL);
+ if (ret == IPS_SUCCESS) {
+ IPS_PRINTK(KERN_NOTICE, ha->pcidev,
+ "Reset Request - Flushed Cache\n");
+ return (SUCCESS);
+ }
+ }
+
+ /* Either we can't communicate with the adapter or it's an IOCTL request */
+ /* from a utility. A physical reset is needed at this point. */
+
+ ha->ioctl_reset = 0; /* Reset the IOCTL Requested Reset Flag */
+
+ /*
+ * command must have already been sent
+ * reset the controller
+ */
+ IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Resetting controller.\n");
+ ret = (*ha->func.reset) (ha);
+
+ if (!ret) {
+ Scsi_Cmnd *scsi_cmd;
+
+ IPS_PRINTK(KERN_NOTICE, ha->pcidev,
+ "Controller reset failed - controller now offline.\n");
+
+ /* Now fail all of the active commands */
+ DEBUG_VAR(1, "(%s%d) Failing active commands",
+ ips_name, ha->host_num);
+
+ while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ ips_freescb(ha, scb);
+ }
+
+ /* Now fail all of the pending commands */
+ DEBUG_VAR(1, "(%s%d) Failing pending commands",
+ ips_name, ha->host_num);
+
+ while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+ scsi_cmd->result = DID_ERROR;
+ scsi_cmd->scsi_done(scsi_cmd);
+ }
+
+ ha->active = FALSE;
+ return (FAILED);
+ }
+
+ if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+ Scsi_Cmnd *scsi_cmd;
+
+ IPS_PRINTK(KERN_NOTICE, ha->pcidev,
+ "Controller reset failed - controller now offline.\n");
+
+ /* Now fail all of the active commands */
+ DEBUG_VAR(1, "(%s%d) Failing active commands",
+ ips_name, ha->host_num);
+
+ while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ ips_freescb(ha, scb);
+ }
+
+ /* Now fail all of the pending commands */
+ DEBUG_VAR(1, "(%s%d) Failing pending commands",
+ ips_name, ha->host_num);
+
+ while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+ scsi_cmd->result = DID_ERROR << 16;
+ scsi_cmd->scsi_done(scsi_cmd);
+ }
+
+ ha->active = FALSE;
+ return (FAILED);
+ }
+
+ /* FFDC */
+ if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) {
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ ha->last_ffdc = tv.tv_sec;
+ ha->reset_count++;
+ ips_ffdc_reset(ha, IPS_INTR_IORL);
+ }
+
+ /* Now fail all of the active commands */
+ DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num);
+
+ while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+ scb->scsi_cmd->result =
+ (DID_RESET << 16) | (SUGGEST_RETRY << 24);
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ ips_freescb(ha, scb);
+ }
+
+ /* Reset DCDB active command bits */
+ for (i = 1; i < ha->nbus; i++)
+ ha->dcdb_active[i - 1] = 0;
+
+ /* Reset the number of active IOCTLs */
+ ha->num_ioctl = 0;
+
+ ips_next(ha, IPS_INTR_IORL);
+
+ return (SUCCESS);
+#endif /* NO_IPS_RESET */
+
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_queue */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command to the controller */
+/* */
+/* NOTE: */
+/* Linux obtains io_request_lock before calling this function */
+/* */
+/****************************************************************************/
+static int
+ips_queue(Scsi_Cmnd * SC, void (*done) (Scsi_Cmnd *))
+{
+ ips_ha_t *ha;
+ ips_passthru_t *pt;
+
+ METHOD_TRACE("ips_queue", 1);
+
+ ha = (ips_ha_t *) SC->device->host->hostdata;
+
+ if (!ha)
+ return (1);
+
+ if (!ha->active)
+ return (DID_ERROR);
+
+ if (ips_is_passthru(SC)) {
+ if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) {
+ SC->result = DID_BUS_BUSY << 16;
+ done(SC);
+
+ return (0);
+ }
+ } else if (ha->scb_waitlist.count == IPS_MAX_QUEUE) {
+ SC->result = DID_BUS_BUSY << 16;
+ done(SC);
+
+ return (0);
+ }
+
+ SC->scsi_done = done;
+
+ DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ SC->cmnd[0],
+ SC->device->channel, SC->device->id, SC->device->lun);
+
+ /* Check for command to initiator IDs */
+ if ((SC->device->channel > 0)
+ && (SC->device->id == ha->ha_id[SC->device->channel])) {
+ SC->result = DID_NO_CONNECT << 16;
+ done(SC);
+
+ return (0);
+ }
+
+ if (ips_is_passthru(SC)) {
+
+ ips_copp_wait_item_t *scratch;
+
+ /* A Reset IOCTL is only sent by the boot CD in extreme cases. */
+ /* There can never be any system activity ( network or disk ), but check */
+ /* anyway just as a good practice. */
+ pt = (ips_passthru_t *) SC->request_buffer;
+ if ((pt->CoppCP.cmd.reset.op_code == IPS_CMD_RESET_CHANNEL) &&
+ (pt->CoppCP.cmd.reset.adapter_flag == 1)) {
+ if (ha->scb_activelist.count != 0) {
+ SC->result = DID_BUS_BUSY << 16;
+ done(SC);
+ return (0);
+ }
+ ha->ioctl_reset = 1; /* This reset request is from an IOCTL */
+ ips_eh_reset(SC);
+ SC->result = DID_OK << 16;
+ SC->scsi_done(SC);
+ return (0);
+ }
+
+ /* allocate space for the scribble */
+ scratch = kmalloc(sizeof (ips_copp_wait_item_t), GFP_ATOMIC);
+
+ if (!scratch) {
+ SC->result = DID_ERROR << 16;
+ done(SC);
+
+ return (0);
+ }
+
+ scratch->scsi_cmd = SC;
+ scratch->next = NULL;
+
+ ips_putq_copp_tail(&ha->copp_waitlist, scratch);
+ } else {
+ ips_putq_wait_tail(&ha->scb_waitlist, SC);
+ }
+
+ ips_next(ha, IPS_INTR_IORL);
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_biosparam */
+/* */
+/* Routine Description: */
+/* */
+/* Set bios geometry for the controller */
+/* */
+/****************************************************************************/
+static int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ips_biosparam(Disk * disk, kdev_t dev, int geom[])
+{
+ ips_ha_t *ha = (ips_ha_t *) disk->device->host->hostdata;
+ unsigned long capacity = disk->capacity;
+#else
+ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ ips_ha_t *ha = (ips_ha_t *) sdev->host->hostdata;
+#endif
+ int heads;
+ int sectors;
+ int cylinders;
+
+ METHOD_TRACE("ips_biosparam", 1);
+
+ if (!ha)
+ /* ?!?! host adater info invalid */
+ return (0);
+
+ if (!ha->active)
+ return (0);
+
+ if (!ips_read_adapter_status(ha, IPS_INTR_ON))
+ /* ?!?! Enquiry command failed */
+ return (0);
+
+ if ((capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) {
+ heads = IPS_NORM_HEADS;
+ sectors = IPS_NORM_SECTORS;
+ } else {
+ heads = IPS_COMP_HEADS;
+ sectors = IPS_COMP_SECTORS;
+ }
+
+ cylinders = (unsigned long) capacity / (heads * sectors);
+
+ DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
+ heads, sectors, cylinders);
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return (0);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+/* ips_proc24_info is a wrapper around ips_proc_info *
+ * for compatibility with the 2.4 scsi parameters */
+static int
+ips_proc24_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int func)
+{
+ int i;
+
+ for (i = 0; i < ips_next_controller; i++) {
+ if (ips_sh[i] && ips_sh[i]->host_no == hostno) {
+ return ips_proc_info(ips_sh[i], buffer, start,
+ offset, length, func);
+ }
+ }
+ return -EINVAL;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_select_queue_depth */
+/* */
+/* Routine Description: */
+/* */
+/* Select queue depths for the devices on the contoller */
+/* */
+/****************************************************************************/
+static void
+ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device * scsi_devs)
+{
+ Scsi_Device *device;
+ ips_ha_t *ha;
+ int count = 0;
+ int min;
+
+ ha = IPS_HA(host);
+ min = ha->max_cmds / 4;
+
+ for (device = scsi_devs; device; device = device->next) {
+ if (device->host == host) {
+ if ((device->channel == 0) && (device->type == 0))
+ count++;
+ }
+ }
+
+ for (device = scsi_devs; device; device = device->next) {
+ if (device->host == host) {
+ if ((device->channel == 0) && (device->type == 0)) {
+ device->queue_depth =
+ (ha->max_cmds - 1) / count;
+ if (device->queue_depth < min)
+ device->queue_depth = min;
+ } else {
+ device->queue_depth = 2;
+ }
+
+ if (device->queue_depth < 2)
+ device->queue_depth = 2;
+ }
+ }
+}
+
+#else
+/****************************************************************************/
+/* */
+/* Routine Name: ips_slave_configure */
+/* */
+/* Routine Description: */
+/* */
+/* Set queue depths on devices once scan is complete */
+/* */
+/****************************************************************************/
+static int
+ips_slave_configure(Scsi_Device * SDptr)
+{
+ ips_ha_t *ha;
+ int min;
+
+ ha = IPS_HA(SDptr->host);
+ if (SDptr->tagged_supported && SDptr->type == TYPE_DISK) {
+ min = ha->max_cmds / 2;
+ if (ha->enq->ucLogDriveCount <= 2)
+ min = ha->max_cmds - 1;
+ scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, min);
+ }
+ return 0;
+}
+#endif
+
+/****************************************************************************/
+/* */
+/* Routine Name: do_ipsintr */
+/* */
+/* Routine Description: */
+/* */
+/* Wrapper for the interrupt handler */
+/* */
+/****************************************************************************/
+static irqreturn_t
+do_ipsintr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ ips_ha_t *ha;
+ unsigned long cpu_flags;
+ struct Scsi_Host *host;
+ int irqstatus;
+
+ METHOD_TRACE("do_ipsintr", 2);
+
+ ha = (ips_ha_t *) dev_id;
+ if (!ha)
+ return IRQ_NONE;
+ host = ips_sh[ha->host_num];
+ /* interrupt during initialization */
+ if (!host) {
+ (*ha->func.intr) (ha);
+ return IRQ_HANDLED;
+ }
+
+ IPS_LOCK_SAVE(host->host_lock, cpu_flags);
+
+ if (!ha->active) {
+ IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
+ return IRQ_HANDLED;
+ }
+
+ irqstatus = (*ha->func.intr) (ha);
+
+ IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
+
+ /* start the next command */
+ ips_next(ha, IPS_INTR_ON);
+ return IRQ_RETVAL(irqstatus);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_intr_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Polling interrupt handler */
+/* */
+/* ASSUMES interrupts are disabled */
+/* */
+/****************************************************************************/
+int
+ips_intr_copperhead(ips_ha_t * ha)
+{
+ ips_stat_t *sp;
+ ips_scb_t *scb;
+ IPS_STATUS cstatus;
+ int intrstatus;
+
+ METHOD_TRACE("ips_intr", 2);
+
+ if (!ha)
+ return 0;
+
+ if (!ha->active)
+ return 0;
+
+ intrstatus = (*ha->func.isintr) (ha);
+
+ if (!intrstatus) {
+ /*
+ * Unexpected/Shared interrupt
+ */
+
+ return 0;
+ }
+
+ while (TRUE) {
+ sp = &ha->sp;
+
+ intrstatus = (*ha->func.isintr) (ha);
+
+ if (!intrstatus)
+ break;
+ else
+ cstatus.value = (*ha->func.statupd) (ha);
+
+ if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
+ /* Spurious Interupt ? */
+ continue;
+ }
+
+ ips_chkstatus(ha, &cstatus);
+ scb = (ips_scb_t *) sp->scb_addr;
+
+ /*
+ * use the callback function to finish things up
+ * NOTE: interrupts are OFF for this
+ */
+ (*scb->callback) (ha, scb);
+ } /* end while */
+ return 1;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_intr_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Polling interrupt handler */
+/* */
+/* ASSUMES interrupts are disabled */
+/* */
+/****************************************************************************/
+int
+ips_intr_morpheus(ips_ha_t * ha)
+{
+ ips_stat_t *sp;
+ ips_scb_t *scb;
+ IPS_STATUS cstatus;
+ int intrstatus;
+
+ METHOD_TRACE("ips_intr_morpheus", 2);
+
+ if (!ha)
+ return 0;
+
+ if (!ha->active)
+ return 0;
+
+ intrstatus = (*ha->func.isintr) (ha);
+
+ if (!intrstatus) {
+ /*
+ * Unexpected/Shared interrupt
+ */
+
+ return 0;
+ }
+
+ while (TRUE) {
+ sp = &ha->sp;
+
+ intrstatus = (*ha->func.isintr) (ha);
+
+ if (!intrstatus)
+ break;
+ else
+ cstatus.value = (*ha->func.statupd) (ha);
+
+ if (cstatus.value == 0xffffffff)
+ /* No more to process */
+ break;
+
+ if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Spurious interrupt; no ccb.\n");
+
+ continue;
+ }
+
+ ips_chkstatus(ha, &cstatus);
+ scb = (ips_scb_t *) sp->scb_addr;
+
+ /*
+ * use the callback function to finish things up
+ * NOTE: interrupts are OFF for this
+ */
+ (*scb->callback) (ha, scb);
+ } /* end while */
+ return 1;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_info */
+/* */
+/* Routine Description: */
+/* */
+/* Return info about the driver */
+/* */
+/****************************************************************************/
+static const char *
+ips_info(struct Scsi_Host *SH)
+{
+ static char buffer[256];
+ char *bp;
+ ips_ha_t *ha;
+
+ METHOD_TRACE("ips_info", 1);
+
+ ha = IPS_HA(SH);
+
+ if (!ha)
+ return (NULL);
+
+ bp = &buffer[0];
+ memset(bp, 0, sizeof (buffer));
+
+ sprintf(bp, "%s%s%s Build %d", "IBM PCI ServeRAID ",
+ IPS_VERSION_HIGH, IPS_VERSION_LOW, IPS_BUILD_IDENT);
+
+ if (ha->ad_type > 0 && ha->ad_type <= MAX_ADAPTER_NAME) {
+ strcat(bp, " <");
+ strcat(bp, ips_adapter_name[ha->ad_type - 1]);
+ strcat(bp, ">");
+ }
+
+ return (bp);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_proc_info */
+/* */
+/* Routine Description: */
+/* */
+/* The passthru interface for the driver */
+/* */
+/****************************************************************************/
+static int
+ips_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int func)
+{
+ int i;
+ int ret;
+ ips_ha_t *ha = NULL;
+
+ METHOD_TRACE("ips_proc_info", 1);
+
+ /* Find our host structure */
+ for (i = 0; i < ips_next_controller; i++) {
+ if (ips_sh[i]) {
+ if (ips_sh[i] == host) {
+ ha = (ips_ha_t *) ips_sh[i]->hostdata;
+ break;
+ }
+ }
+ }
+
+ if (!ha)
+ return (-EINVAL);
+
+ if (func) {
+ /* write */
+ return (0);
+ } else {
+ /* read */
+ if (start)
+ *start = buffer;
+
+ ret = ips_host_info(ha, buffer, offset, length);
+
+ return (ret);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Helper Functions */
+/*--------------------------------------------------------------------------*/
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_is_passthru */
+/* */
+/* Routine Description: */
+/* */
+/* Determine if the specified SCSI command is really a passthru command */
+/* */
+/****************************************************************************/
+static int
+ips_is_passthru(Scsi_Cmnd * SC)
+{
+ METHOD_TRACE("ips_is_passthru", 1);
+
+ if (!SC)
+ return (0);
+
+ if ((SC->cmnd[0] == IPS_IOCTL_COMMAND) &&
+ (SC->device->channel == 0) &&
+ (SC->device->id == IPS_ADAPTER_ID) &&
+ (SC->device->lun == 0) && SC->request_buffer) {
+ if ((!SC->use_sg) && SC->request_bufflen &&
+ (((char *) SC->request_buffer)[0] == 'C') &&
+ (((char *) SC->request_buffer)[1] == 'O') &&
+ (((char *) SC->request_buffer)[2] == 'P') &&
+ (((char *) SC->request_buffer)[3] == 'P'))
+ return 1;
+ else if (SC->use_sg) {
+ struct scatterlist *sg = SC->request_buffer;
+ char *buffer = IPS_SG_ADDRESS(sg);
+ if (buffer && buffer[0] == 'C' && buffer[1] == 'O' &&
+ buffer[2] == 'P' && buffer[3] == 'P')
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_alloc_passthru_buffer */
+/* */
+/* Routine Description: */
+/* allocate a buffer large enough for the ioctl data if the ioctl buffer */
+/* is too small or doesn't exist */
+/****************************************************************************/
+static int
+ips_alloc_passthru_buffer(ips_ha_t * ha, int length)
+{
+ void *bigger_buf;
+ dma_addr_t dma_busaddr;
+
+ if (ha->ioctl_data && length <= ha->ioctl_len)
+ return 0;
+ /* there is no buffer or it's not big enough, allocate a new one */
+ bigger_buf = pci_alloc_consistent(ha->pcidev, length, &dma_busaddr);
+ if (bigger_buf) {
+ /* free the old memory */
+ pci_free_consistent(ha->pcidev, ha->ioctl_len, ha->ioctl_data,
+ ha->ioctl_busaddr);
+ /* use the new memory */
+ ha->ioctl_data = (char *) bigger_buf;
+ ha->ioctl_len = length;
+ ha->ioctl_busaddr = dma_busaddr;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_make_passthru */
+/* */
+/* Routine Description: */
+/* */
+/* Make a passthru command out of the info in the Scsi block */
+/* */
+/****************************************************************************/
+static int
+ips_make_passthru(ips_ha_t * ha, Scsi_Cmnd * SC, ips_scb_t * scb, int intr)
+{
+ ips_passthru_t *pt;
+ int length = 0;
+ int ret;
+
+ METHOD_TRACE("ips_make_passthru", 1);
+
+ if (!SC->use_sg) {
+ length = SC->request_bufflen;
+ } else {
+ struct scatterlist *sg = SC->request_buffer;
+ int i;
+ for (i = 0; i < SC->use_sg; i++)
+ length += sg[i].length;
+ }
+ if (length < sizeof (ips_passthru_t)) {
+ /* wrong size */
+ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+ ips_name, ha->host_num);
+ return (IPS_FAILURE);
+ }
+ if (ips_alloc_passthru_buffer(ha, length)) {
+ /* allocation failure! If ha->ioctl_data exists, use it to return
+ some error codes. Return a failed command to the scsi layer. */
+ if (ha->ioctl_data) {
+ pt = (ips_passthru_t *) ha->ioctl_data;
+ ips_scmd_buf_read(SC, pt, sizeof (ips_passthru_t));
+ pt->BasicStatus = 0x0B;
+ pt->ExtendedStatus = 0x00;
+ ips_scmd_buf_write(SC, pt, sizeof (ips_passthru_t));
+ }
+ return IPS_FAILURE;
+ }
+ ha->ioctl_datasize = length;
+
+ ips_scmd_buf_read(SC, ha->ioctl_data, ha->ioctl_datasize);
+ pt = (ips_passthru_t *) ha->ioctl_data;
+
+ /*
+ * Some notes about the passthru interface used
+ *
+ * IF the scsi op_code == 0x0d then we assume
+ * that the data came along with/goes with the
+ * packet we received from the sg driver. In this
+ * case the CmdBSize field of the pt structure is
+ * used for the size of the buffer.
+ */
+
+ switch (pt->CoppCmd) {
+ case IPS_NUMCTRLS:
+ memcpy(ha->ioctl_data + sizeof (ips_passthru_t),
+ &ips_num_controllers, sizeof (int));
+ ips_scmd_buf_write(SC, ha->ioctl_data,
+ sizeof (ips_passthru_t) + sizeof (int));
+ SC->result = DID_OK << 16;
+
+ return (IPS_SUCCESS_IMM);
+
+ case IPS_COPPUSRCMD:
+ case IPS_COPPIOCCMD:
+ if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
+ if (length < (sizeof (ips_passthru_t) + pt->CmdBSize)) {
+ /* wrong size */
+ DEBUG_VAR(1,
+ "(%s%d) Passthru structure wrong size",
+ ips_name, ha->host_num);
+
+ return (IPS_FAILURE);
+ }
+
+ if (ha->device_id == IPS_DEVICEID_COPPERHEAD &&
+ pt->CoppCP.cmd.flashfw.op_code ==
+ IPS_CMD_RW_BIOSFW) {
+ ret = ips_flash_copperhead(ha, pt, scb);
+ ips_scmd_buf_write(SC, ha->ioctl_data,
+ sizeof (ips_passthru_t));
+ return ret;
+ }
+ if (ips_usrcmd(ha, pt, scb))
+ return (IPS_SUCCESS);
+ else
+ return (IPS_FAILURE);
+ }
+
+ break;
+
+ } /* end switch */
+
+ return (IPS_FAILURE);
+}
+
+/****************************************************************************/
+/* Routine Name: ips_flash_copperhead */
+/* Routine Description: */
+/* Flash the BIOS/FW on a Copperhead style controller */
+/****************************************************************************/
+static int
+ips_flash_copperhead(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
+{
+ int datasize;
+
+ /* Trombone is the only copperhead that can do packet flash, but only
+ * for firmware. No one said it had to make sence. */
+ if (IPS_IS_TROMBONE(ha) && pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE) {
+ if (ips_usrcmd(ha, pt, scb))
+ return IPS_SUCCESS;
+ else
+ return IPS_FAILURE;
+ }
+ pt->BasicStatus = 0x0B;
+ pt->ExtendedStatus = 0;
+ scb->scsi_cmd->result = DID_OK << 16;
+ /* IF it's OK to Use the "CD BOOT" Flash Buffer, then you can */
+ /* avoid allocating a huge buffer per adapter ( which can fail ). */
+ if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
+ pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) {
+ pt->BasicStatus = 0;
+ return ips_flash_bios(ha, pt, scb);
+ } else if (pt->CoppCP.cmd.flashfw.packet_num == 0) {
+ if (ips_FlashData && !test_and_set_bit(0, &ips_FlashDataInUse)){
+ ha->flash_data = ips_FlashData;
+ ha->flash_busaddr = ips_flashbusaddr;
+ ha->flash_len = PAGE_SIZE << 7;
+ ha->flash_datasize = 0;
+ } else if (!ha->flash_data) {
+ datasize = pt->CoppCP.cmd.flashfw.total_packets *
+ pt->CoppCP.cmd.flashfw.count;
+ ha->flash_data = pci_alloc_consistent(ha->pcidev,
+ datasize,
+ &ha->flash_busaddr);
+ if (!ha->flash_data){
+ printk(KERN_WARNING "Unable to allocate a flash buffer\n");
+ return IPS_FAILURE;
+ }
+ ha->flash_datasize = 0;
+ ha->flash_len = datasize;
+ } else
+ return IPS_FAILURE;
+ } else {
+ if (pt->CoppCP.cmd.flashfw.count + ha->flash_datasize >
+ ha->flash_len) {
+ ips_free_flash_copperhead(ha);
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "failed size sanity check\n");
+ return IPS_FAILURE;
+ }
+ }
+ if (!ha->flash_data)
+ return IPS_FAILURE;
+ pt->BasicStatus = 0;
+ memcpy(&ha->flash_data[ha->flash_datasize], pt + 1,
+ pt->CoppCP.cmd.flashfw.count);
+ ha->flash_datasize += pt->CoppCP.cmd.flashfw.count;
+ if (pt->CoppCP.cmd.flashfw.packet_num ==
+ pt->CoppCP.cmd.flashfw.total_packets - 1) {
+ if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE)
+ return ips_flash_bios(ha, pt, scb);
+ else if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE)
+ return ips_flash_firmware(ha, pt, scb);
+ }
+ return IPS_SUCCESS_IMM;
+}
+
+/****************************************************************************/
+/* Routine Name: ips_flash_bios */
+/* Routine Description: */
+/* flashes the bios of a copperhead adapter */
+/****************************************************************************/
+static int
+ips_flash_bios(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
+{
+
+ if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
+ pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_BIOS) {
+ if ((!ha->func.programbios) || (!ha->func.erasebios) ||
+ (!ha->func.verifybios))
+ goto error;
+ if ((*ha->func.erasebios) (ha)) {
+ DEBUG_VAR(1,
+ "(%s%d) flash bios failed - unable to erase flash",
+ ips_name, ha->host_num);
+ goto error;
+ } else
+ if ((*ha->func.programbios) (ha,
+ ha->flash_data +
+ IPS_BIOS_HEADER,
+ ha->flash_datasize -
+ IPS_BIOS_HEADER, 0)) {
+ DEBUG_VAR(1,
+ "(%s%d) flash bios failed - unable to flash",
+ ips_name, ha->host_num);
+ goto error;
+ } else
+ if ((*ha->func.verifybios) (ha,
+ ha->flash_data +
+ IPS_BIOS_HEADER,
+ ha->flash_datasize -
+ IPS_BIOS_HEADER, 0)) {
+ DEBUG_VAR(1,
+ "(%s%d) flash bios failed - unable to verify flash",
+ ips_name, ha->host_num);
+ goto error;
+ }
+ ips_free_flash_copperhead(ha);
+ return IPS_SUCCESS_IMM;
+ } else if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE &&
+ pt->CoppCP.cmd.flashfw.direction == IPS_ERASE_BIOS) {
+ if (!ha->func.erasebios)
+ goto error;
+ if ((*ha->func.erasebios) (ha)) {
+ DEBUG_VAR(1,
+ "(%s%d) flash bios failed - unable to erase flash",
+ ips_name, ha->host_num);
+ goto error;
+ }
+ return IPS_SUCCESS_IMM;
+ }
+ error:
+ pt->BasicStatus = 0x0B;
+ pt->ExtendedStatus = 0x00;
+ ips_free_flash_copperhead(ha);
+ return IPS_FAILURE;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_fill_scb_sg_single */
+/* */
+/* Routine Description: */
+/* Fill in a single scb sg_list element from an address */
+/* return a -1 if a breakup occurred */
+/****************************************************************************/
+static int
+ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr,
+ ips_scb_t * scb, int indx, unsigned int e_len)
+{
+
+ int ret_val = 0;
+
+ if ((scb->data_len + e_len) > ha->max_xfer) {
+ e_len = ha->max_xfer - scb->data_len;
+ scb->breakup = indx;
+ ++scb->sg_break;
+ ret_val = -1;
+ } else {
+ scb->breakup = 0;
+ scb->sg_break = 0;
+ }
+ if (IPS_USE_ENH_SGLIST(ha)) {
+ scb->sg_list.enh_list[indx].address_lo =
+ cpu_to_le32(pci_dma_lo32(busaddr));
+ scb->sg_list.enh_list[indx].address_hi =
+ cpu_to_le32(pci_dma_hi32(busaddr));
+ scb->sg_list.enh_list[indx].length = cpu_to_le32(e_len);
+ } else {
+ scb->sg_list.std_list[indx].address =
+ cpu_to_le32(pci_dma_lo32(busaddr));
+ scb->sg_list.std_list[indx].length = cpu_to_le32(e_len);
+ }
+
+ ++scb->sg_len;
+ scb->data_len += e_len;
+ return ret_val;
+}
+
+/****************************************************************************/
+/* Routine Name: ips_flash_firmware */
+/* Routine Description: */
+/* flashes the firmware of a copperhead adapter */
+/****************************************************************************/
+static int
+ips_flash_firmware(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
+{
+ IPS_SG_LIST sg_list;
+ uint32_t cmd_busaddr;
+
+ if (pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE &&
+ pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_FW) {
+ memset(&pt->CoppCP.cmd, 0, sizeof (IPS_HOST_COMMAND));
+ pt->CoppCP.cmd.flashfw.op_code = IPS_CMD_DOWNLOAD;
+ pt->CoppCP.cmd.flashfw.count = cpu_to_le32(ha->flash_datasize);
+ } else {
+ pt->BasicStatus = 0x0B;
+ pt->ExtendedStatus = 0x00;
+ ips_free_flash_copperhead(ha);
+ return IPS_FAILURE;
+ }
+ /* Save the S/G list pointer so it doesn't get clobbered */
+ sg_list.list = scb->sg_list.list;
+ cmd_busaddr = scb->scb_busaddr;
+ /* copy in the CP */
+ memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD));
+ /* FIX stuff that might be wrong */
+ scb->sg_list.list = sg_list.list;
+ scb->scb_busaddr = cmd_busaddr;
+ scb->bus = scb->scsi_cmd->device->channel;
+ scb->target_id = scb->scsi_cmd->device->id;
+ scb->lun = scb->scsi_cmd->device->lun;
+ scb->sg_len = 0;
+ scb->data_len = 0;
+ scb->flags = 0;
+ scb->op_code = 0;
+ scb->callback = ipsintr_done;
+ scb->timeout = ips_cmd_timeout;
+
+ scb->data_len = ha->flash_datasize;
+ scb->data_busaddr =
+ pci_map_single(ha->pcidev, ha->flash_data, scb->data_len,
+ IPS_DMA_DIR(scb));
+ scb->flags |= IPS_SCB_MAP_SINGLE;
+ scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flashfw.buffer_addr = cpu_to_le32(scb->data_busaddr);
+ if (pt->TimeOut)
+ scb->timeout = pt->TimeOut;
+ scb->scsi_cmd->result = DID_OK << 16;
+ return IPS_SUCCESS;
+}
+
+/****************************************************************************/
+/* Routine Name: ips_free_flash_copperhead */
+/* Routine Description: */
+/* release the memory resources used to hold the flash image */
+/****************************************************************************/
+static void
+ips_free_flash_copperhead(ips_ha_t * ha)
+{
+ if (ha->flash_data == ips_FlashData)
+ test_and_clear_bit(0, &ips_FlashDataInUse);
+ else if (ha->flash_data)
+ pci_free_consistent(ha->pcidev, ha->flash_len, ha->flash_data,
+ ha->flash_busaddr);
+ ha->flash_data = NULL;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_usrcmd */
+/* */
+/* Routine Description: */
+/* */
+/* Process a user command and make it ready to send */
+/* */
+/****************************************************************************/
+static int
+ips_usrcmd(ips_ha_t * ha, ips_passthru_t * pt, ips_scb_t * scb)
+{
+ IPS_SG_LIST sg_list;
+ uint32_t cmd_busaddr;
+
+ METHOD_TRACE("ips_usrcmd", 1);
+
+ if ((!scb) || (!pt) || (!ha))
+ return (0);
+
+ /* Save the S/G list pointer so it doesn't get clobbered */
+ sg_list.list = scb->sg_list.list;
+ cmd_busaddr = scb->scb_busaddr;
+ /* copy in the CP */
+ memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof (IPS_IOCTL_CMD));
+ memcpy(&scb->dcdb, &pt->CoppCP.dcdb, sizeof (IPS_DCDB_TABLE));
+
+ /* FIX stuff that might be wrong */
+ scb->sg_list.list = sg_list.list;
+ scb->scb_busaddr = cmd_busaddr;
+ scb->bus = scb->scsi_cmd->device->channel;
+ scb->target_id = scb->scsi_cmd->device->id;
+ scb->lun = scb->scsi_cmd->device->lun;
+ scb->sg_len = 0;
+ scb->data_len = 0;
+ scb->flags = 0;
+ scb->op_code = 0;
+ scb->callback = ipsintr_done;
+ scb->timeout = ips_cmd_timeout;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+
+ /* we don't support DCDB/READ/WRITE Scatter Gather */
+ if ((scb->cmd.basic_io.op_code == IPS_CMD_READ_SG) ||
+ (scb->cmd.basic_io.op_code == IPS_CMD_WRITE_SG) ||
+ (scb->cmd.basic_io.op_code == IPS_CMD_DCDB_SG))
+ return (0);
+
+ if (pt->CmdBSize) {
+ scb->data_len = pt->CmdBSize;
+ scb->data_busaddr = ha->ioctl_busaddr + sizeof (ips_passthru_t);
+ } else {
+ scb->data_busaddr = 0L;
+ }
+
+ if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
+ scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
+ (unsigned long) &scb->
+ dcdb -
+ (unsigned long) scb);
+
+ if (pt->CmdBSize) {
+ if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
+ scb->dcdb.buffer_pointer =
+ cpu_to_le32(scb->data_busaddr);
+ else
+ scb->cmd.basic_io.sg_addr =
+ cpu_to_le32(scb->data_busaddr);
+ }
+
+ /* set timeouts */
+ if (pt->TimeOut) {
+ scb->timeout = pt->TimeOut;
+
+ if (pt->TimeOut <= 10)
+ scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;
+ else if (pt->TimeOut <= 60)
+ scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;
+ else
+ scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;
+ }
+
+ /* assume success */
+ scb->scsi_cmd->result = DID_OK << 16;
+
+ /* success */
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_cleanup_passthru */
+/* */
+/* Routine Description: */
+/* */
+/* Cleanup after a passthru command */
+/* */
+/****************************************************************************/
+static void
+ips_cleanup_passthru(ips_ha_t * ha, ips_scb_t * scb)
+{
+ ips_passthru_t *pt;
+
+ METHOD_TRACE("ips_cleanup_passthru", 1);
+
+ if ((!scb) || (!scb->scsi_cmd) || (!scb->scsi_cmd->request_buffer)) {
+ DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru",
+ ips_name, ha->host_num);
+
+ return;
+ }
+ pt = (ips_passthru_t *) ha->ioctl_data;
+
+ /* Copy data back to the user */
+ if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) /* Copy DCDB Back to Caller's Area */
+ memcpy(&pt->CoppCP.dcdb, &scb->dcdb, sizeof (IPS_DCDB_TABLE));
+
+ pt->BasicStatus = scb->basic_status;
+ pt->ExtendedStatus = scb->extended_status;
+ pt->AdapterType = ha->ad_type;
+
+ if (ha->device_id == IPS_DEVICEID_COPPERHEAD &&
+ (scb->cmd.flashfw.op_code == IPS_CMD_DOWNLOAD ||
+ scb->cmd.flashfw.op_code == IPS_CMD_RW_BIOSFW))
+ ips_free_flash_copperhead(ha);
+
+ ips_scmd_buf_write(scb->scsi_cmd, ha->ioctl_data, ha->ioctl_datasize);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_host_info */
+/* */
+/* Routine Description: */
+/* */
+/* The passthru interface for the driver */
+/* */
+/****************************************************************************/
+static int
+ips_host_info(ips_ha_t * ha, char *ptr, off_t offset, int len)
+{
+ IPS_INFOSTR info;
+
+ METHOD_TRACE("ips_host_info", 1);
+
+ info.buffer = ptr;
+ info.length = len;
+ info.offset = offset;
+ info.pos = 0;
+ info.localpos = 0;
+
+ copy_info(&info, "\nIBM ServeRAID General Information:\n\n");
+
+ if ((le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) &&
+ (le16_to_cpu(ha->nvram->adapter_type) != 0))
+ copy_info(&info, "\tController Type : %s\n",
+ ips_adapter_name[ha->ad_type - 1]);
+ else
+ copy_info(&info,
+ "\tController Type : Unknown\n");
+
+ if (ha->io_addr)
+ copy_info(&info,
+ "\tIO region : 0x%lx (%d bytes)\n",
+ ha->io_addr, ha->io_len);
+
+ if (ha->mem_addr) {
+ copy_info(&info,
+ "\tMemory region : 0x%lx (%d bytes)\n",
+ ha->mem_addr, ha->mem_len);
+ copy_info(&info,
+ "\tShared memory address : 0x%lx\n",
+ ha->mem_ptr);
+ }
+
+ copy_info(&info, "\tIRQ number : %d\n", ha->irq);
+
+ /* For the Next 3 lines Check for Binary 0 at the end and don't include it if it's there. */
+ /* That keeps everything happy for "text" operations on the proc file. */
+
+ if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) {
+ if (ha->nvram->bios_low[3] == 0) {
+ copy_info(&info,
+ "\tBIOS Version : %c%c%c%c%c%c%c\n",
+ ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+ ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+ ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+ ha->nvram->bios_low[2]);
+
+ } else {
+ copy_info(&info,
+ "\tBIOS Version : %c%c%c%c%c%c%c%c\n",
+ ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+ ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+ ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+ ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
+ }
+
+ }
+
+ if (ha->enq->CodeBlkVersion[7] == 0) {
+ copy_info(&info,
+ "\tFirmware Version : %c%c%c%c%c%c%c\n",
+ ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
+ ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
+ ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
+ ha->enq->CodeBlkVersion[6]);
+ } else {
+ copy_info(&info,
+ "\tFirmware Version : %c%c%c%c%c%c%c%c\n",
+ ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1],
+ ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3],
+ ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5],
+ ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]);
+ }
+
+ if (ha->enq->BootBlkVersion[7] == 0) {
+ copy_info(&info,
+ "\tBoot Block Version : %c%c%c%c%c%c%c\n",
+ ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
+ ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
+ ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
+ ha->enq->BootBlkVersion[6]);
+ } else {
+ copy_info(&info,
+ "\tBoot Block Version : %c%c%c%c%c%c%c%c\n",
+ ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1],
+ ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3],
+ ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5],
+ ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]);
+ }
+
+ copy_info(&info, "\tDriver Version : %s%s\n",
+ IPS_VERSION_HIGH, IPS_VERSION_LOW);
+
+ copy_info(&info, "\tDriver Build : %d\n",
+ IPS_BUILD_IDENT);
+
+ copy_info(&info, "\tMax Physical Devices : %d\n",
+ ha->enq->ucMaxPhysicalDevices);
+ copy_info(&info, "\tMax Active Commands : %d\n",
+ ha->max_cmds);
+ copy_info(&info, "\tCurrent Queued Commands : %d\n",
+ ha->scb_waitlist.count);
+ copy_info(&info, "\tCurrent Active Commands : %d\n",
+ ha->scb_activelist.count - ha->num_ioctl);
+ copy_info(&info, "\tCurrent Queued PT Commands : %d\n",
+ ha->copp_waitlist.count);
+ copy_info(&info, "\tCurrent Active PT Commands : %d\n",
+ ha->num_ioctl);
+
+ copy_info(&info, "\n");
+
+ return (info.localpos);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: copy_mem_info */
+/* */
+/* Routine Description: */
+/* */
+/* Copy data into an IPS_INFOSTR structure */
+/* */
+/****************************************************************************/
+static void
+copy_mem_info(IPS_INFOSTR * info, char *data, int len)
+{
+ METHOD_TRACE("copy_mem_info", 1);
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ info->pos += (info->offset - info->pos);
+ }
+
+ if (info->localpos + len > info->length)
+ len = info->length - info->localpos;
+
+ if (len > 0) {
+ memcpy(info->buffer + info->localpos, data, len);
+ info->pos += len;
+ info->localpos += len;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: copy_info */
+/* */
+/* Routine Description: */
+/* */
+/* printf style wrapper for an info structure */
+/* */
+/****************************************************************************/
+static int
+copy_info(IPS_INFOSTR * info, char *fmt, ...)
+{
+ va_list args;
+ char buf[128];
+ int len;
+
+ METHOD_TRACE("copy_info", 1);
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+
+ return (len);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_identify_controller */
+/* */
+/* Routine Description: */
+/* */
+/* Identify this controller */
+/* */
+/****************************************************************************/
+static void
+ips_identify_controller(ips_ha_t * ha)
+{
+ METHOD_TRACE("ips_identify_controller", 1);
+
+ switch (ha->device_id) {
+ case IPS_DEVICEID_COPPERHEAD:
+ if (ha->revision_id <= IPS_REVID_SERVERAID) {
+ ha->ad_type = IPS_ADTYPE_SERVERAID;
+ } else if (ha->revision_id == IPS_REVID_SERVERAID2) {
+ ha->ad_type = IPS_ADTYPE_SERVERAID2;
+ } else if (ha->revision_id == IPS_REVID_NAVAJO) {
+ ha->ad_type = IPS_ADTYPE_NAVAJO;
+ } else if ((ha->revision_id == IPS_REVID_SERVERAID2)
+ && (ha->slot_num == 0)) {
+ ha->ad_type = IPS_ADTYPE_KIOWA;
+ } else if ((ha->revision_id >= IPS_REVID_CLARINETP1) &&
+ (ha->revision_id <= IPS_REVID_CLARINETP3)) {
+ if (ha->enq->ucMaxPhysicalDevices == 15)
+ ha->ad_type = IPS_ADTYPE_SERVERAID3L;
+ else
+ ha->ad_type = IPS_ADTYPE_SERVERAID3;
+ } else if ((ha->revision_id >= IPS_REVID_TROMBONE32) &&
+ (ha->revision_id <= IPS_REVID_TROMBONE64)) {
+ ha->ad_type = IPS_ADTYPE_SERVERAID4H;
+ }
+ break;
+
+ case IPS_DEVICEID_MORPHEUS:
+ switch (ha->subdevice_id) {
+ case IPS_SUBDEVICEID_4L:
+ ha->ad_type = IPS_ADTYPE_SERVERAID4L;
+ break;
+
+ case IPS_SUBDEVICEID_4M:
+ ha->ad_type = IPS_ADTYPE_SERVERAID4M;
+ break;
+
+ case IPS_SUBDEVICEID_4MX:
+ ha->ad_type = IPS_ADTYPE_SERVERAID4MX;
+ break;
+
+ case IPS_SUBDEVICEID_4LX:
+ ha->ad_type = IPS_ADTYPE_SERVERAID4LX;
+ break;
+
+ case IPS_SUBDEVICEID_5I2:
+ ha->ad_type = IPS_ADTYPE_SERVERAID5I2;
+ break;
+
+ case IPS_SUBDEVICEID_5I1:
+ ha->ad_type = IPS_ADTYPE_SERVERAID5I1;
+ break;
+ }
+
+ break;
+
+ case IPS_DEVICEID_MARCO:
+ switch (ha->subdevice_id) {
+ case IPS_SUBDEVICEID_6M:
+ ha->ad_type = IPS_ADTYPE_SERVERAID6M;
+ break;
+ case IPS_SUBDEVICEID_6I:
+ ha->ad_type = IPS_ADTYPE_SERVERAID6I;
+ break;
+ case IPS_SUBDEVICEID_7k:
+ ha->ad_type = IPS_ADTYPE_SERVERAID7k;
+ break;
+ case IPS_SUBDEVICEID_7M:
+ ha->ad_type = IPS_ADTYPE_SERVERAID7M;
+ break;
+ }
+ break;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_get_bios_version */
+/* */
+/* Routine Description: */
+/* */
+/* Get the BIOS revision number */
+/* */
+/****************************************************************************/
+static void
+ips_get_bios_version(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+ int ret;
+ uint8_t major;
+ uint8_t minor;
+ uint8_t subminor;
+ uint8_t *buffer;
+ char hexDigits[] =
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
+ 'D', 'E', 'F' };
+
+ METHOD_TRACE("ips_get_bios_version", 1);
+
+ major = 0;
+ minor = 0;
+
+ strncpy(ha->bios_version, " ?", 8);
+
+ if (ha->device_id == IPS_DEVICEID_COPPERHEAD) {
+ if (IPS_USE_MEMIO(ha)) {
+ /* Memory Mapped I/O */
+
+ /* test 1st byte */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
+ return;
+
+ writel(1, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
+ return;
+
+ /* Get Major version */
+ writel(0x1FF, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ major = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ /* Get Minor version */
+ writel(0x1FE, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+ minor = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ /* Get SubMinor version */
+ writel(0x1FD, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+ subminor = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ } else {
+ /* Programmed I/O */
+
+ /* test 1st byte */
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55)
+ return;
+
+ outl(cpu_to_le32(1), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA)
+ return;
+
+ /* Get Major version */
+ outl(cpu_to_le32(0x1FF), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ major = inb(ha->io_addr + IPS_REG_FLDP);
+
+ /* Get Minor version */
+ outl(cpu_to_le32(0x1FE), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ minor = inb(ha->io_addr + IPS_REG_FLDP);
+
+ /* Get SubMinor version */
+ outl(cpu_to_le32(0x1FD), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ subminor = inb(ha->io_addr + IPS_REG_FLDP);
+
+ }
+ } else {
+ /* Morpheus Family - Send Command to the card */
+
+ buffer = ha->ioctl_data;
+
+ memset(buffer, 0, 0x1000);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_RW_BIOSFW;
+
+ scb->cmd.flashfw.op_code = IPS_CMD_RW_BIOSFW;
+ scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flashfw.type = 1;
+ scb->cmd.flashfw.direction = 0;
+ scb->cmd.flashfw.count = cpu_to_le32(0x800);
+ scb->cmd.flashfw.total_packets = 1;
+ scb->cmd.flashfw.packet_num = 0;
+ scb->data_len = 0x1000;
+ scb->cmd.flashfw.buffer_addr = ha->ioctl_busaddr;
+
+ /* issue the command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_cmd_timeout,
+ intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
+ /* Error occurred */
+
+ return;
+ }
+
+ if ((buffer[0xC0] == 0x55) && (buffer[0xC1] == 0xAA)) {
+ major = buffer[0x1ff + 0xC0]; /* Offset 0x1ff after the header (0xc0) */
+ minor = buffer[0x1fe + 0xC0]; /* Offset 0x1fe after the header (0xc0) */
+ subminor = buffer[0x1fd + 0xC0]; /* Offset 0x1fd after the header (0xc0) */
+ } else {
+ return;
+ }
+ }
+
+ ha->bios_version[0] = hexDigits[(major & 0xF0) >> 4];
+ ha->bios_version[1] = '.';
+ ha->bios_version[2] = hexDigits[major & 0x0F];
+ ha->bios_version[3] = hexDigits[subminor];
+ ha->bios_version[4] = '.';
+ ha->bios_version[5] = hexDigits[(minor & 0xF0) >> 4];
+ ha->bios_version[6] = hexDigits[minor & 0x0F];
+ ha->bios_version[7] = 0;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_hainit */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize the controller */
+/* */
+/* NOTE: Assumes to be called from with a lock */
+/* */
+/****************************************************************************/
+static int
+ips_hainit(ips_ha_t * ha)
+{
+ int i;
+ struct timeval tv;
+
+ METHOD_TRACE("ips_hainit", 1);
+
+ if (!ha)
+ return (0);
+
+ if (ha->func.statinit)
+ (*ha->func.statinit) (ha);
+
+ if (ha->func.enableint)
+ (*ha->func.enableint) (ha);
+
+ /* Send FFDC */
+ ha->reset_count = 1;
+ do_gettimeofday(&tv);
+ ha->last_ffdc = tv.tv_sec;
+ ips_ffdc_reset(ha, IPS_INTR_IORL);
+
+ if (!ips_read_config(ha, IPS_INTR_IORL)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "unable to read config from controller.\n");
+
+ return (0);
+ }
+ /* end if */
+ if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "unable to read controller status.\n");
+
+ return (0);
+ }
+
+ /* Identify this controller */
+ ips_identify_controller(ha);
+
+ if (!ips_read_subsystem_parameters(ha, IPS_INTR_IORL)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "unable to read subsystem parameters.\n");
+
+ return (0);
+ }
+
+ /* write nvram user page 5 */
+ if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "unable to write driver info to controller.\n");
+
+ return (0);
+ }
+
+ /* If there are Logical Drives and a Reset Occurred, then an EraseStripeLock is Needed */
+ if ((ha->conf->ucLogDriveCount > 0) && (ha->requires_esl == 1))
+ ips_clear_adapter(ha, IPS_INTR_IORL);
+
+ /* set limits on SID, LUN, BUS */
+ ha->ntargets = IPS_MAX_TARGETS + 1;
+ ha->nlun = 1;
+ ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1;
+
+ switch (ha->conf->logical_drive[0].ucStripeSize) {
+ case 4:
+ ha->max_xfer = 0x10000;
+ break;
+
+ case 5:
+ ha->max_xfer = 0x20000;
+ break;
+
+ case 6:
+ ha->max_xfer = 0x40000;
+ break;
+
+ case 7:
+ default:
+ ha->max_xfer = 0x80000;
+ break;
+ }
+
+ /* setup max concurrent commands */
+ if (le32_to_cpu(ha->subsys->param[4]) & 0x1) {
+ /* Use the new method */
+ ha->max_cmds = ha->enq->ucConcurrentCmdCount;
+ } else {
+ /* use the old method */
+ switch (ha->conf->logical_drive[0].ucStripeSize) {
+ case 4:
+ ha->max_cmds = 32;
+ break;
+
+ case 5:
+ ha->max_cmds = 16;
+ break;
+
+ case 6:
+ ha->max_cmds = 8;
+ break;
+
+ case 7:
+ default:
+ ha->max_cmds = 4;
+ break;
+ }
+ }
+
+ /* Limit the Active Commands on a Lite Adapter */
+ if ((ha->ad_type == IPS_ADTYPE_SERVERAID3L) ||
+ (ha->ad_type == IPS_ADTYPE_SERVERAID4L) ||
+ (ha->ad_type == IPS_ADTYPE_SERVERAID4LX)) {
+ if ((ha->max_cmds > MaxLiteCmds) && (MaxLiteCmds))
+ ha->max_cmds = MaxLiteCmds;
+ }
+
+ /* set controller IDs */
+ ha->ha_id[0] = IPS_ADAPTER_ID;
+ for (i = 1; i < ha->nbus; i++) {
+ ha->ha_id[i] = ha->conf->init_id[i - 1] & 0x1f;
+ ha->dcdb_active[i - 1] = 0;
+ }
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_next */
+/* */
+/* Routine Description: */
+/* */
+/* Take the next command off the queue and send it to the controller */
+/* */
+/****************************************************************************/
+static void
+ips_next(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+ Scsi_Cmnd *SC;
+ Scsi_Cmnd *p;
+ Scsi_Cmnd *q;
+ ips_copp_wait_item_t *item;
+ int ret;
+ unsigned long cpu_flags = 0;
+ struct Scsi_Host *host;
+ METHOD_TRACE("ips_next", 1);
+
+ if (!ha)
+ return;
+ host = ips_sh[ha->host_num];
+ /*
+ * Block access to the queue function so
+ * this command won't time out
+ */
+ if (intr == IPS_INTR_ON)
+ IPS_LOCK_SAVE(host->host_lock, cpu_flags);
+
+ if ((ha->subsys->param[3] & 0x300000)
+ && (ha->scb_activelist.count == 0)) {
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ if (tv.tv_sec - ha->last_ffdc > IPS_SECS_8HOURS) {
+ ha->last_ffdc = tv.tv_sec;
+ ips_ffdc_time(ha);
+ }
+ }
+
+ /*
+ * Send passthru commands
+ * These have priority over normal I/O
+ * but shouldn't affect performance too much
+ * since we limit the number that can be active
+ * on the card at any one time
+ */
+ while ((ha->num_ioctl < IPS_MAX_IOCTL) &&
+ (ha->copp_waitlist.head) && (scb = ips_getscb(ha))) {
+
+ item = ips_removeq_copp_head(&ha->copp_waitlist);
+ ha->num_ioctl++;
+ if (intr == IPS_INTR_ON)
+ IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
+ scb->scsi_cmd = item->scsi_cmd;
+ kfree(item);
+
+ ret = ips_make_passthru(ha, scb->scsi_cmd, scb, intr);
+
+ if (intr == IPS_INTR_ON)
+ IPS_LOCK_SAVE(host->host_lock, cpu_flags);
+ switch (ret) {
+ case IPS_FAILURE:
+ if (scb->scsi_cmd) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ }
+
+ ips_freescb(ha, scb);
+ break;
+ case IPS_SUCCESS_IMM:
+ if (scb->scsi_cmd) {
+ scb->scsi_cmd->result = DID_OK << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ }
+
+ ips_freescb(ha, scb);
+ break;
+ default:
+ break;
+ } /* end case */
+
+ if (ret != IPS_SUCCESS) {
+ ha->num_ioctl--;
+ continue;
+ }
+
+ ret = ips_send_cmd(ha, scb);
+
+ if (ret == IPS_SUCCESS)
+ ips_putq_scb_head(&ha->scb_activelist, scb);
+ else
+ ha->num_ioctl--;
+
+ switch (ret) {
+ case IPS_FAILURE:
+ if (scb->scsi_cmd) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ }
+
+ ips_freescb(ha, scb);
+ break;
+ case IPS_SUCCESS_IMM:
+ ips_freescb(ha, scb);
+ break;
+ default:
+ break;
+ } /* end case */
+
+ }
+
+ /*
+ * Send "Normal" I/O commands
+ */
+
+ p = ha->scb_waitlist.head;
+ while ((p) && (scb = ips_getscb(ha))) {
+ if ((p->device->channel > 0)
+ && (ha->
+ dcdb_active[p->device->channel -
+ 1] & (1 << p->device->id))) {
+ ips_freescb(ha, scb);
+ p = (Scsi_Cmnd *) p->host_scribble;
+ continue;
+ }
+
+ q = p;
+ SC = ips_removeq_wait(&ha->scb_waitlist, q);
+
+ if (intr == IPS_INTR_ON)
+ IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags); /* Unlock HA after command is taken off queue */
+
+ SC->result = DID_OK;
+ SC->host_scribble = NULL;
+
+ memset(SC->sense_buffer, 0, sizeof (SC->sense_buffer));
+
+ scb->target_id = SC->device->id;
+ scb->lun = SC->device->lun;
+ scb->bus = SC->device->channel;
+ scb->scsi_cmd = SC;
+ scb->breakup = 0;
+ scb->data_len = 0;
+ scb->callback = ipsintr_done;
+ scb->timeout = ips_cmd_timeout;
+ memset(&scb->cmd, 0, 16);
+
+ /* copy in the CDB */
+ memcpy(scb->cdb, SC->cmnd, SC->cmd_len);
+
+ /* Now handle the data buffer */
+ if (SC->use_sg) {
+ struct scatterlist *sg;
+ int i;
+
+ sg = SC->request_buffer;
+ scb->sg_count = pci_map_sg(ha->pcidev, sg, SC->use_sg,
+ scsi_to_pci_dma_dir(SC->
+ sc_data_direction));
+ scb->flags |= IPS_SCB_MAP_SG;
+ for (i = 0; i < scb->sg_count; i++) {
+ if (ips_fill_scb_sg_single
+ (ha, sg_dma_address(&sg[i]), scb, i,
+ sg_dma_len(&sg[i])) < 0)
+ break;
+ }
+ scb->dcdb.transfer_length = scb->data_len;
+ } else {
+ if (SC->request_bufflen) {
+ scb->data_busaddr =
+ pci_map_single(ha->pcidev,
+ SC->request_buffer,
+ SC->request_bufflen,
+ scsi_to_pci_dma_dir(SC->
+ sc_data_direction));
+ scb->flags |= IPS_SCB_MAP_SINGLE;
+ ips_fill_scb_sg_single(ha, scb->data_busaddr,
+ scb, 0,
+ SC->request_bufflen);
+ scb->dcdb.transfer_length = scb->data_len;
+ } else {
+ scb->data_busaddr = 0L;
+ scb->sg_len = 0;
+ scb->data_len = 0;
+ scb->dcdb.transfer_length = 0;
+ }
+
+ }
+
+ scb->dcdb.cmd_attribute =
+ ips_command_direction[scb->scsi_cmd->cmnd[0]];
+
+ /* Allow a WRITE BUFFER Command to Have no Data */
+ /* This is Used by Tape Flash Utilites */
+ if ((scb->scsi_cmd->cmnd[0] == WRITE_BUFFER) && (scb->data_len == 0))
+ scb->dcdb.cmd_attribute = 0;
+
+ if (!(scb->dcdb.cmd_attribute & 0x3))
+ scb->dcdb.transfer_length = 0;
+
+ if (scb->data_len >= IPS_MAX_XFER) {
+ scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
+ scb->dcdb.transfer_length = 0;
+ }
+ if (intr == IPS_INTR_ON)
+ IPS_LOCK_SAVE(host->host_lock, cpu_flags);
+
+ ret = ips_send_cmd(ha, scb);
+
+ switch (ret) {
+ case IPS_SUCCESS:
+ ips_putq_scb_head(&ha->scb_activelist, scb);
+ break;
+ case IPS_FAILURE:
+ if (scb->scsi_cmd) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ }
+
+ if (scb->bus)
+ ha->dcdb_active[scb->bus - 1] &=
+ ~(1 << scb->target_id);
+
+ ips_freescb(ha, scb);
+ break;
+ case IPS_SUCCESS_IMM:
+ if (scb->scsi_cmd)
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+
+ if (scb->bus)
+ ha->dcdb_active[scb->bus - 1] &=
+ ~(1 << scb->target_id);
+
+ ips_freescb(ha, scb);
+ break;
+ default:
+ break;
+ } /* end case */
+
+ p = (Scsi_Cmnd *) p->host_scribble;
+
+ } /* end while */
+
+ if (intr == IPS_INTR_ON)
+ IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_putq_scb_head */
+/* */
+/* Routine Description: */
+/* */
+/* Add an item to the head of the queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static void
+ips_putq_scb_head(ips_scb_queue_t * queue, ips_scb_t * item)
+{
+ METHOD_TRACE("ips_putq_scb_head", 1);
+
+ if (!item)
+ return;
+
+ item->q_next = queue->head;
+ queue->head = item;
+
+ if (!queue->tail)
+ queue->tail = item;
+
+ queue->count++;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_removeq_scb_head */
+/* */
+/* Routine Description: */
+/* */
+/* Remove the head of the queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static ips_scb_t *
+ips_removeq_scb_head(ips_scb_queue_t * queue)
+{
+ ips_scb_t *item;
+
+ METHOD_TRACE("ips_removeq_scb_head", 1);
+
+ item = queue->head;
+
+ if (!item) {
+ return (NULL);
+ }
+
+ queue->head = item->q_next;
+ item->q_next = NULL;
+
+ if (queue->tail == item)
+ queue->tail = NULL;
+
+ queue->count--;
+
+ return (item);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_removeq_scb */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an item from a queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static ips_scb_t *
+ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item)
+{
+ ips_scb_t *p;
+
+ METHOD_TRACE("ips_removeq_scb", 1);
+
+ if (!item)
+ return (NULL);
+
+ if (item == queue->head) {
+ return (ips_removeq_scb_head(queue));
+ }
+
+ p = queue->head;
+
+ while ((p) && (item != p->q_next))
+ p = p->q_next;
+
+ if (p) {
+ /* found a match */
+ p->q_next = item->q_next;
+
+ if (!item->q_next)
+ queue->tail = p;
+
+ item->q_next = NULL;
+ queue->count--;
+
+ return (item);
+ }
+
+ return (NULL);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_putq_wait_tail */
+/* */
+/* Routine Description: */
+/* */
+/* Add an item to the tail of the queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static void
+ips_putq_wait_tail(ips_wait_queue_t * queue, Scsi_Cmnd * item)
+{
+ METHOD_TRACE("ips_putq_wait_tail", 1);
+
+ if (!item)
+ return;
+
+ item->host_scribble = NULL;
+
+ if (queue->tail)
+ queue->tail->host_scribble = (char *) item;
+
+ queue->tail = item;
+
+ if (!queue->head)
+ queue->head = item;
+
+ queue->count++;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_removeq_wait_head */
+/* */
+/* Routine Description: */
+/* */
+/* Remove the head of the queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static Scsi_Cmnd *
+ips_removeq_wait_head(ips_wait_queue_t * queue)
+{
+ Scsi_Cmnd *item;
+
+ METHOD_TRACE("ips_removeq_wait_head", 1);
+
+ item = queue->head;
+
+ if (!item) {
+ return (NULL);
+ }
+
+ queue->head = (Scsi_Cmnd *) item->host_scribble;
+ item->host_scribble = NULL;
+
+ if (queue->tail == item)
+ queue->tail = NULL;
+
+ queue->count--;
+
+ return (item);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_removeq_wait */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an item from a queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static Scsi_Cmnd *
+ips_removeq_wait(ips_wait_queue_t * queue, Scsi_Cmnd * item)
+{
+ Scsi_Cmnd *p;
+
+ METHOD_TRACE("ips_removeq_wait", 1);
+
+ if (!item)
+ return (NULL);
+
+ if (item == queue->head) {
+ return (ips_removeq_wait_head(queue));
+ }
+
+ p = queue->head;
+
+ while ((p) && (item != (Scsi_Cmnd *) p->host_scribble))
+ p = (Scsi_Cmnd *) p->host_scribble;
+
+ if (p) {
+ /* found a match */
+ p->host_scribble = item->host_scribble;
+
+ if (!item->host_scribble)
+ queue->tail = p;
+
+ item->host_scribble = NULL;
+ queue->count--;
+
+ return (item);
+ }
+
+ return (NULL);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_putq_copp_tail */
+/* */
+/* Routine Description: */
+/* */
+/* Add an item to the tail of the queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static void
+ips_putq_copp_tail(ips_copp_queue_t * queue, ips_copp_wait_item_t * item)
+{
+ METHOD_TRACE("ips_putq_copp_tail", 1);
+
+ if (!item)
+ return;
+
+ item->next = NULL;
+
+ if (queue->tail)
+ queue->tail->next = item;
+
+ queue->tail = item;
+
+ if (!queue->head)
+ queue->head = item;
+
+ queue->count++;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_removeq_copp_head */
+/* */
+/* Routine Description: */
+/* */
+/* Remove the head of the queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static ips_copp_wait_item_t *
+ips_removeq_copp_head(ips_copp_queue_t * queue)
+{
+ ips_copp_wait_item_t *item;
+
+ METHOD_TRACE("ips_removeq_copp_head", 1);
+
+ item = queue->head;
+
+ if (!item) {
+ return (NULL);
+ }
+
+ queue->head = item->next;
+ item->next = NULL;
+
+ if (queue->tail == item)
+ queue->tail = NULL;
+
+ queue->count--;
+
+ return (item);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_removeq_copp */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an item from a queue */
+/* */
+/* ASSUMED to be called from within the HA lock */
+/* */
+/****************************************************************************/
+static ips_copp_wait_item_t *
+ips_removeq_copp(ips_copp_queue_t * queue, ips_copp_wait_item_t * item)
+{
+ ips_copp_wait_item_t *p;
+
+ METHOD_TRACE("ips_removeq_copp", 1);
+
+ if (!item)
+ return (NULL);
+
+ if (item == queue->head) {
+ return (ips_removeq_copp_head(queue));
+ }
+
+ p = queue->head;
+
+ while ((p) && (item != p->next))
+ p = p->next;
+
+ if (p) {
+ /* found a match */
+ p->next = item->next;
+
+ if (!item->next)
+ queue->tail = p;
+
+ item->next = NULL;
+ queue->count--;
+
+ return (item);
+ }
+
+ return (NULL);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ipsintr_blocking */
+/* */
+/* Routine Description: */
+/* */
+/* Finalize an interrupt for internal commands */
+/* */
+/****************************************************************************/
+static void
+ipsintr_blocking(ips_ha_t * ha, ips_scb_t * scb)
+{
+ METHOD_TRACE("ipsintr_blocking", 2);
+
+ ips_freescb(ha, scb);
+ if ((ha->waitflag == TRUE) && (ha->cmd_in_progress == scb->cdb[0])) {
+ ha->waitflag = FALSE;
+
+ return;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ipsintr_done */
+/* */
+/* Routine Description: */
+/* */
+/* Finalize an interrupt for non-internal commands */
+/* */
+/****************************************************************************/
+static void
+ipsintr_done(ips_ha_t * ha, ips_scb_t * scb)
+{
+ METHOD_TRACE("ipsintr_done", 2);
+
+ if (!scb) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Spurious interrupt; scb NULL.\n");
+
+ return;
+ }
+
+ if (scb->scsi_cmd == NULL) {
+ /* unexpected interrupt */
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Spurious interrupt; scsi_cmd not set.\n");
+
+ return;
+ }
+
+ ips_done(ha, scb);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_done */
+/* */
+/* Routine Description: */
+/* */
+/* Do housekeeping on completed commands */
+/* ASSUMED to be called form within the request lock */
+/****************************************************************************/
+static void
+ips_done(ips_ha_t * ha, ips_scb_t * scb)
+{
+ int ret;
+
+ METHOD_TRACE("ips_done", 1);
+
+ if (!scb)
+ return;
+
+ if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) {
+ ips_cleanup_passthru(ha, scb);
+ ha->num_ioctl--;
+ } else {
+ /*
+ * Check to see if this command had too much
+ * data and had to be broke up. If so, queue
+ * the rest of the data and continue.
+ */
+ if ((scb->breakup) || (scb->sg_break)) {
+ /* we had a data breakup */
+ scb->data_len = 0;
+
+ if (scb->sg_count) {
+ /* S/G request */
+ struct scatterlist *sg;
+ int ips_sg_index = 0;
+ int sg_dma_index;
+
+ sg = scb->scsi_cmd->request_buffer;
+
+ /* Spin forward to last dma chunk */
+ sg_dma_index = scb->breakup;
+
+ /* Take care of possible partial on last chunk */
+ ips_fill_scb_sg_single(ha,
+ sg_dma_address(&sg
+ [sg_dma_index]),
+ scb, ips_sg_index++,
+ sg_dma_len(&sg
+ [sg_dma_index]));
+
+ for (; sg_dma_index < scb->sg_count;
+ sg_dma_index++) {
+ if (ips_fill_scb_sg_single
+ (ha,
+ sg_dma_address(&sg[sg_dma_index]),
+ scb, ips_sg_index++,
+ sg_dma_len(&sg[sg_dma_index])) < 0)
+ break;
+
+ }
+
+ } else {
+ /* Non S/G Request */
+ (void) ips_fill_scb_sg_single(ha,
+ scb->
+ data_busaddr +
+ (scb->sg_break *
+ ha->max_xfer),
+ scb, 0,
+ scb->scsi_cmd->
+ request_bufflen -
+ (scb->sg_break *
+ ha->max_xfer));
+ }
+
+ scb->dcdb.transfer_length = scb->data_len;
+ scb->dcdb.cmd_attribute |=
+ ips_command_direction[scb->scsi_cmd->cmnd[0]];
+
+ if (!(scb->dcdb.cmd_attribute & 0x3))
+ scb->dcdb.transfer_length = 0;
+
+ if (scb->data_len >= IPS_MAX_XFER) {
+ scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
+ scb->dcdb.transfer_length = 0;
+ }
+
+ ret = ips_send_cmd(ha, scb);
+
+ switch (ret) {
+ case IPS_FAILURE:
+ if (scb->scsi_cmd) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ }
+
+ ips_freescb(ha, scb);
+ break;
+ case IPS_SUCCESS_IMM:
+ if (scb->scsi_cmd) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ }
+
+ ips_freescb(ha, scb);
+ break;
+ default:
+ break;
+ } /* end case */
+
+ return;
+ }
+ } /* end if passthru */
+
+ if (scb->bus) {
+ ha->dcdb_active[scb->bus - 1] &= ~(1 << scb->target_id);
+ }
+
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+
+ ips_freescb(ha, scb);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_map_status */
+/* */
+/* Routine Description: */
+/* */
+/* Map Controller Error codes to Linux Error Codes */
+/* */
+/****************************************************************************/
+static int
+ips_map_status(ips_ha_t * ha, ips_scb_t * scb, ips_stat_t * sp)
+{
+ int errcode;
+ int device_error;
+ uint32_t transfer_len;
+ IPS_DCDB_TABLE_TAPE *tapeDCDB;
+
+ METHOD_TRACE("ips_map_status", 1);
+
+ if (scb->bus) {
+ DEBUG_VAR(2,
+ "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x",
+ ips_name, ha->host_num,
+ scb->scsi_cmd->device->channel,
+ scb->scsi_cmd->device->id, scb->scsi_cmd->device->lun,
+ scb->basic_status, scb->extended_status,
+ scb->extended_status ==
+ IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0,
+ scb->extended_status ==
+ IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0,
+ scb->extended_status ==
+ IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0);
+ }
+
+ /* default driver error */
+ errcode = DID_ERROR;
+ device_error = 0;
+
+ switch (scb->basic_status & IPS_GSC_STATUS_MASK) {
+ case IPS_CMD_TIMEOUT:
+ errcode = DID_TIME_OUT;
+ break;
+
+ case IPS_INVAL_OPCO:
+ case IPS_INVAL_CMD_BLK:
+ case IPS_INVAL_PARM_BLK:
+ case IPS_LD_ERROR:
+ case IPS_CMD_CMPLT_WERROR:
+ break;
+
+ case IPS_PHYS_DRV_ERROR:
+ switch (scb->extended_status) {
+ case IPS_ERR_SEL_TO:
+ if (scb->bus)
+ errcode = DID_NO_CONNECT;
+
+ break;
+
+ case IPS_ERR_OU_RUN:
+ if ((scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB) ||
+ (scb->cmd.dcdb.op_code ==
+ IPS_CMD_EXTENDED_DCDB_SG)) {
+ tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
+ transfer_len = tapeDCDB->transfer_length;
+ } else {
+ transfer_len =
+ (uint32_t) scb->dcdb.transfer_length;
+ }
+
+ if ((scb->bus) && (transfer_len < scb->data_len)) {
+ /* Underrun - set default to no error */
+ errcode = DID_OK;
+
+ /* Restrict access to physical DASD */
+ if ((scb->scsi_cmd->cmnd[0] == INQUIRY) &&
+ ((((char *) scb->scsi_cmd->
+ buffer)[0] & 0x1f) == TYPE_DISK)) {
+ /* underflow -- no error */
+ /* restrict access to physical DASD */
+ errcode = DID_TIME_OUT;
+ break;
+ }
+ } else
+ errcode = DID_ERROR;
+
+ break;
+
+ case IPS_ERR_RECOVERY:
+ /* don't fail recovered errors */
+ if (scb->bus)
+ errcode = DID_OK;
+
+ break;
+
+ case IPS_ERR_HOST_RESET:
+ case IPS_ERR_DEV_RESET:
+ errcode = DID_RESET;
+ break;
+
+ case IPS_ERR_CKCOND:
+ if (scb->bus) {
+ if ((scb->cmd.dcdb.op_code ==
+ IPS_CMD_EXTENDED_DCDB)
+ || (scb->cmd.dcdb.op_code ==
+ IPS_CMD_EXTENDED_DCDB_SG)) {
+ tapeDCDB =
+ (IPS_DCDB_TABLE_TAPE *) & scb->dcdb;
+ memcpy(scb->scsi_cmd->sense_buffer,
+ tapeDCDB->sense_info,
+ sizeof (scb->scsi_cmd->
+ sense_buffer));
+ } else {
+ memcpy(scb->scsi_cmd->sense_buffer,
+ scb->dcdb.sense_info,
+ sizeof (scb->scsi_cmd->
+ sense_buffer));
+ }
+ device_error = 2; /* check condition */
+ }
+
+ errcode = DID_OK;
+
+ break;
+
+ default:
+ errcode = DID_ERROR;
+ break;
+
+ } /* end switch */
+ } /* end switch */
+
+ scb->scsi_cmd->result = device_error | (errcode << 16);
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_send_wait */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command to the controller and wait for it to return */
+/* */
+/* The FFDC Time Stamp use this function for the callback, but doesn't */
+/* actually need to wait. */
+/****************************************************************************/
+static int
+ips_send_wait(ips_ha_t * ha, ips_scb_t * scb, int timeout, int intr)
+{
+ int ret;
+
+ METHOD_TRACE("ips_send_wait", 1);
+
+ if (intr != IPS_FFDC) { /* Won't be Waiting if this is a Time Stamp */
+ ha->waitflag = TRUE;
+ ha->cmd_in_progress = scb->cdb[0];
+ }
+ scb->callback = ipsintr_blocking;
+ ret = ips_send_cmd(ha, scb);
+
+ if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+ return (ret);
+
+ if (intr != IPS_FFDC) /* Don't Wait around if this is a Time Stamp */
+ ret = ips_wait(ha, timeout, intr);
+
+ return (ret);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_scmd_buf_write */
+/* */
+/* Routine Description: */
+/* Write data to Scsi_Cmnd request_buffer at proper offsets */
+/****************************************************************************/
+static void
+ips_scmd_buf_write(Scsi_Cmnd * scmd, void *data, unsigned
+ int count)
+{
+ if (scmd->use_sg) {
+ int i;
+ unsigned int min_cnt, xfer_cnt;
+ char *cdata = (char *) data;
+ struct scatterlist *sg = scmd->request_buffer;
+ for (i = 0, xfer_cnt = 0;
+ (i < scmd->use_sg) && (xfer_cnt < count); i++) {
+ if (!IPS_SG_ADDRESS(&sg[i]))
+ return;
+ min_cnt = min(count - xfer_cnt, sg[i].length);
+ memcpy(IPS_SG_ADDRESS(&sg[i]), &cdata[xfer_cnt],
+ min_cnt);
+ xfer_cnt += min_cnt;
+ }
+
+ } else {
+ unsigned int min_cnt = min(count, scmd->request_bufflen);
+ memcpy(scmd->request_buffer, data, min_cnt);
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_scmd_buf_read */
+/* */
+/* Routine Description: */
+/* Copy data from a Scsi_Cmnd to a new, linear buffer */
+/****************************************************************************/
+static void
+ips_scmd_buf_read(Scsi_Cmnd * scmd, void *data, unsigned
+ int count)
+{
+ if (scmd->use_sg) {
+ int i;
+ unsigned int min_cnt, xfer_cnt;
+ char *cdata = (char *) data;
+ struct scatterlist *sg = scmd->request_buffer;
+ for (i = 0, xfer_cnt = 0;
+ (i < scmd->use_sg) && (xfer_cnt < count); i++) {
+ if (!IPS_SG_ADDRESS(&sg[i]))
+ return;
+ min_cnt = min(count - xfer_cnt, sg[i].length);
+ memcpy(&cdata[xfer_cnt], IPS_SG_ADDRESS(&sg[i]),
+ min_cnt);
+ xfer_cnt += min_cnt;
+ }
+
+ } else {
+ unsigned int min_cnt = min(count, scmd->request_bufflen);
+ memcpy(data, scmd->request_buffer, min_cnt);
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_send_cmd */
+/* */
+/* Routine Description: */
+/* */
+/* Map SCSI commands to ServeRAID commands for logical drives */
+/* */
+/****************************************************************************/
+static int
+ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb)
+{
+ int ret;
+ char *sp;
+ int device_error;
+ IPS_DCDB_TABLE_TAPE *tapeDCDB;
+ int TimeOut;
+
+ METHOD_TRACE("ips_send_cmd", 1);
+
+ ret = IPS_SUCCESS;
+
+ if (!scb->scsi_cmd) {
+ /* internal command */
+
+ if (scb->bus > 0) {
+ /* Controller commands can't be issued */
+ /* to real devices -- fail them */
+ if ((ha->waitflag == TRUE) &&
+ (ha->cmd_in_progress == scb->cdb[0])) {
+ ha->waitflag = FALSE;
+ }
+
+ return (1);
+ }
+ } else if ((scb->bus == 0) && (!ips_is_passthru(scb->scsi_cmd))) {
+ /* command to logical bus -- interpret */
+ ret = IPS_SUCCESS_IMM;
+
+ switch (scb->scsi_cmd->cmnd[0]) {
+ case ALLOW_MEDIUM_REMOVAL:
+ case REZERO_UNIT:
+ case ERASE:
+ case WRITE_FILEMARKS:
+ case SPACE:
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ break;
+
+ case START_STOP:
+ scb->scsi_cmd->result = DID_OK << 16;
+
+ case TEST_UNIT_READY:
+ case INQUIRY:
+ if (scb->target_id == IPS_ADAPTER_ID) {
+ /*
+ * Either we have a TUR
+ * or we have a SCSI inquiry
+ */
+ if (scb->scsi_cmd->cmnd[0] == TEST_UNIT_READY)
+ scb->scsi_cmd->result = DID_OK << 16;
+
+ if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
+ IPS_SCSI_INQ_DATA inquiry;
+
+ memset(&inquiry, 0,
+ sizeof (IPS_SCSI_INQ_DATA));
+
+ inquiry.DeviceType =
+ IPS_SCSI_INQ_TYPE_PROCESSOR;
+ inquiry.DeviceTypeQualifier =
+ IPS_SCSI_INQ_LU_CONNECTED;
+ inquiry.Version = IPS_SCSI_INQ_REV2;
+ inquiry.ResponseDataFormat =
+ IPS_SCSI_INQ_RD_REV2;
+ inquiry.AdditionalLength = 31;
+ inquiry.Flags[0] =
+ IPS_SCSI_INQ_Address16;
+ inquiry.Flags[1] =
+ IPS_SCSI_INQ_WBus16 |
+ IPS_SCSI_INQ_Sync;
+ strncpy(inquiry.VendorId, "IBM ",
+ 8);
+ strncpy(inquiry.ProductId,
+ "SERVERAID ", 16);
+ strncpy(inquiry.ProductRevisionLevel,
+ "1.00", 4);
+
+ ips_scmd_buf_write(scb->scsi_cmd,
+ &inquiry,
+ sizeof (inquiry));
+
+ scb->scsi_cmd->result = DID_OK << 16;
+ }
+ } else {
+ scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
+ scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.logical_info.reserved = 0;
+ scb->cmd.logical_info.reserved2 = 0;
+ scb->data_len = sizeof (IPS_LD_INFO);
+ scb->data_busaddr = ha->logical_drive_info_dma_addr;
+ scb->flags = 0;
+ scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
+ ret = IPS_SUCCESS;
+ }
+
+ break;
+
+ case REQUEST_SENSE:
+ ips_reqsen(ha, scb);
+ scb->scsi_cmd->result = DID_OK << 16;
+ break;
+
+ case READ_6:
+ case WRITE_6:
+ if (!scb->sg_len) {
+ scb->cmd.basic_io.op_code =
+ (scb->scsi_cmd->cmnd[0] ==
+ READ_6) ? IPS_CMD_READ : IPS_CMD_WRITE;
+ scb->cmd.basic_io.enhanced_sg = 0;
+ scb->cmd.basic_io.sg_addr =
+ cpu_to_le32(scb->data_busaddr);
+ } else {
+ scb->cmd.basic_io.op_code =
+ (scb->scsi_cmd->cmnd[0] ==
+ READ_6) ? IPS_CMD_READ_SG :
+ IPS_CMD_WRITE_SG;
+ scb->cmd.basic_io.enhanced_sg =
+ IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
+ scb->cmd.basic_io.sg_addr =
+ cpu_to_le32(scb->sg_busaddr);
+ }
+
+ scb->cmd.basic_io.segment_4G = 0;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.basic_io.log_drv = scb->target_id;
+ scb->cmd.basic_io.sg_count = scb->sg_len;
+
+ if (scb->cmd.basic_io.lba)
+ scb->cmd.basic_io.lba =
+ cpu_to_le32(le32_to_cpu
+ (scb->cmd.basic_io.lba) +
+ le16_to_cpu(scb->cmd.basic_io.
+ sector_count));
+ else
+ scb->cmd.basic_io.lba =
+ (((scb->scsi_cmd->
+ cmnd[1] & 0x1f) << 16) | (scb->scsi_cmd->
+ cmnd[2] << 8) |
+ (scb->scsi_cmd->cmnd[3]));
+
+ scb->cmd.basic_io.sector_count =
+ cpu_to_le16(scb->data_len / IPS_BLKSIZE);
+
+ if (le16_to_cpu(scb->cmd.basic_io.sector_count) == 0)
+ scb->cmd.basic_io.sector_count =
+ cpu_to_le16(256);
+
+ ret = IPS_SUCCESS;
+ break;
+
+ case READ_10:
+ case WRITE_10:
+ if (!scb->sg_len) {
+ scb->cmd.basic_io.op_code =
+ (scb->scsi_cmd->cmnd[0] ==
+ READ_10) ? IPS_CMD_READ : IPS_CMD_WRITE;
+ scb->cmd.basic_io.enhanced_sg = 0;
+ scb->cmd.basic_io.sg_addr =
+ cpu_to_le32(scb->data_busaddr);
+ } else {
+ scb->cmd.basic_io.op_code =
+ (scb->scsi_cmd->cmnd[0] ==
+ READ_10) ? IPS_CMD_READ_SG :
+ IPS_CMD_WRITE_SG;
+ scb->cmd.basic_io.enhanced_sg =
+ IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
+ scb->cmd.basic_io.sg_addr =
+ cpu_to_le32(scb->sg_busaddr);
+ }
+
+ scb->cmd.basic_io.segment_4G = 0;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.basic_io.log_drv = scb->target_id;
+ scb->cmd.basic_io.sg_count = scb->sg_len;
+
+ if (scb->cmd.basic_io.lba)
+ scb->cmd.basic_io.lba =
+ cpu_to_le32(le32_to_cpu
+ (scb->cmd.basic_io.lba) +
+ le16_to_cpu(scb->cmd.basic_io.
+ sector_count));
+ else
+ scb->cmd.basic_io.lba =
+ ((scb->scsi_cmd->cmnd[2] << 24) | (scb->
+ scsi_cmd->
+ cmnd[3]
+ << 16) |
+ (scb->scsi_cmd->cmnd[4] << 8) | scb->
+ scsi_cmd->cmnd[5]);
+
+ scb->cmd.basic_io.sector_count =
+ cpu_to_le16(scb->data_len / IPS_BLKSIZE);
+
+ if (cpu_to_le16(scb->cmd.basic_io.sector_count) == 0) {
+ /*
+ * This is a null condition
+ * we don't have to do anything
+ * so just return
+ */
+ scb->scsi_cmd->result = DID_OK << 16;
+ } else
+ ret = IPS_SUCCESS;
+
+ break;
+
+ case RESERVE:
+ case RELEASE:
+ scb->scsi_cmd->result = DID_OK << 16;
+ break;
+
+ case MODE_SENSE:
+ scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.basic_io.segment_4G = 0;
+ scb->cmd.basic_io.enhanced_sg = 0;
+ scb->data_len = sizeof (*ha->enq);
+ scb->cmd.basic_io.sg_addr = ha->enq_busaddr;
+ ret = IPS_SUCCESS;
+ break;
+
+ case READ_CAPACITY:
+ scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
+ scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.logical_info.reserved = 0;
+ scb->cmd.logical_info.reserved2 = 0;
+ scb->cmd.logical_info.reserved3 = 0;
+ scb->data_len = sizeof (IPS_LD_INFO);
+ scb->data_busaddr = ha->logical_drive_info_dma_addr;
+ scb->flags = 0;
+ scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
+ ret = IPS_SUCCESS;
+ break;
+
+ case SEND_DIAGNOSTIC:
+ case REASSIGN_BLOCKS:
+ case FORMAT_UNIT:
+ case SEEK_10:
+ case VERIFY:
+ case READ_DEFECT_DATA:
+ case READ_BUFFER:
+ case WRITE_BUFFER:
+ scb->scsi_cmd->result = DID_OK << 16;
+ break;
+
+ default:
+ /* Set the Return Info to appear like the Command was */
+ /* attempted, a Check Condition occurred, and Sense */
+ /* Data indicating an Invalid CDB OpCode is returned. */
+ sp = (char *) scb->scsi_cmd->sense_buffer;
+ memset(sp, 0, sizeof (scb->scsi_cmd->sense_buffer));
+
+ sp[0] = 0x70; /* Error Code */
+ sp[2] = ILLEGAL_REQUEST; /* Sense Key 5 Illegal Req. */
+ sp[7] = 0x0A; /* Additional Sense Length */
+ sp[12] = 0x20; /* ASC = Invalid OpCode */
+ sp[13] = 0x00; /* ASCQ */
+
+ device_error = 2; /* Indicate Check Condition */
+ scb->scsi_cmd->result = device_error | (DID_OK << 16);
+ break;
+ } /* end switch */
+ }
+ /* end if */
+ if (ret == IPS_SUCCESS_IMM)
+ return (ret);
+
+ /* setup DCDB */
+ if (scb->bus > 0) {
+
+ /* If we already know the Device is Not there, no need to attempt a Command */
+ /* This also protects an NT FailOver Controller from getting CDB's sent to it */
+ if (ha->conf->dev[scb->bus - 1][scb->target_id].ucState == 0) {
+ scb->scsi_cmd->result = DID_NO_CONNECT << 16;
+ return (IPS_SUCCESS_IMM);
+ }
+
+ ha->dcdb_active[scb->bus - 1] |= (1 << scb->target_id);
+ scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
+ (unsigned long) &scb->
+ dcdb -
+ (unsigned long) scb);
+ scb->cmd.dcdb.reserved = 0;
+ scb->cmd.dcdb.reserved2 = 0;
+ scb->cmd.dcdb.reserved3 = 0;
+ scb->cmd.dcdb.segment_4G = 0;
+ scb->cmd.dcdb.enhanced_sg = 0;
+
+ TimeOut = scb->scsi_cmd->timeout_per_command;
+
+ if (ha->subsys->param[4] & 0x00100000) { /* If NEW Tape DCDB is Supported */
+ if (!scb->sg_len) {
+ scb->cmd.dcdb.op_code = IPS_CMD_EXTENDED_DCDB;
+ } else {
+ scb->cmd.dcdb.op_code =
+ IPS_CMD_EXTENDED_DCDB_SG;
+ scb->cmd.dcdb.enhanced_sg =
+ IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
+ }
+
+ tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb; /* Use Same Data Area as Old DCDB Struct */
+ tapeDCDB->device_address =
+ ((scb->bus - 1) << 4) | scb->target_id;
+ tapeDCDB->cmd_attribute |= IPS_DISCONNECT_ALLOWED;
+ tapeDCDB->cmd_attribute &= ~IPS_TRANSFER64K; /* Always Turn OFF 64K Size Flag */
+
+ if (TimeOut) {
+ if (TimeOut < (10 * HZ))
+ tapeDCDB->cmd_attribute |= IPS_TIMEOUT10; /* TimeOut is 10 Seconds */
+ else if (TimeOut < (60 * HZ))
+ tapeDCDB->cmd_attribute |= IPS_TIMEOUT60; /* TimeOut is 60 Seconds */
+ else if (TimeOut < (1200 * HZ))
+ tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M; /* TimeOut is 20 Minutes */
+ }
+
+ tapeDCDB->cdb_length = scb->scsi_cmd->cmd_len;
+ tapeDCDB->reserved_for_LUN = 0;
+ tapeDCDB->transfer_length = scb->data_len;
+ if (scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB_SG)
+ tapeDCDB->buffer_pointer =
+ cpu_to_le32(scb->sg_busaddr);
+ else
+ tapeDCDB->buffer_pointer =
+ cpu_to_le32(scb->data_busaddr);
+ tapeDCDB->sg_count = scb->sg_len;
+ tapeDCDB->sense_length = sizeof (tapeDCDB->sense_info);
+ tapeDCDB->scsi_status = 0;
+ tapeDCDB->reserved = 0;
+ memcpy(tapeDCDB->scsi_cdb, scb->scsi_cmd->cmnd,
+ scb->scsi_cmd->cmd_len);
+ } else {
+ if (!scb->sg_len) {
+ scb->cmd.dcdb.op_code = IPS_CMD_DCDB;
+ } else {
+ scb->cmd.dcdb.op_code = IPS_CMD_DCDB_SG;
+ scb->cmd.dcdb.enhanced_sg =
+ IPS_USE_ENH_SGLIST(ha) ? 0xFF : 0;
+ }
+
+ scb->dcdb.device_address =
+ ((scb->bus - 1) << 4) | scb->target_id;
+ scb->dcdb.cmd_attribute |= IPS_DISCONNECT_ALLOWED;
+
+ if (TimeOut) {
+ if (TimeOut < (10 * HZ))
+ scb->dcdb.cmd_attribute |= IPS_TIMEOUT10; /* TimeOut is 10 Seconds */
+ else if (TimeOut < (60 * HZ))
+ scb->dcdb.cmd_attribute |= IPS_TIMEOUT60; /* TimeOut is 60 Seconds */
+ else if (TimeOut < (1200 * HZ))
+ scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M; /* TimeOut is 20 Minutes */
+ }
+
+ scb->dcdb.transfer_length = scb->data_len;
+ if (scb->dcdb.cmd_attribute & IPS_TRANSFER64K)
+ scb->dcdb.transfer_length = 0;
+ if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB_SG)
+ scb->dcdb.buffer_pointer =
+ cpu_to_le32(scb->sg_busaddr);
+ else
+ scb->dcdb.buffer_pointer =
+ cpu_to_le32(scb->data_busaddr);
+ scb->dcdb.cdb_length = scb->scsi_cmd->cmd_len;
+ scb->dcdb.sense_length = sizeof (scb->dcdb.sense_info);
+ scb->dcdb.sg_count = scb->sg_len;
+ scb->dcdb.reserved = 0;
+ memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd,
+ scb->scsi_cmd->cmd_len);
+ scb->dcdb.scsi_status = 0;
+ scb->dcdb.reserved2[0] = 0;
+ scb->dcdb.reserved2[1] = 0;
+ scb->dcdb.reserved2[2] = 0;
+ }
+ }
+
+ return ((*ha->func.issue) (ha, scb));
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_chk_status */
+/* */
+/* Routine Description: */
+/* */
+/* Check the status of commands to logical drives */
+/* Assumed to be called with the HA lock */
+/****************************************************************************/
+static void
+ips_chkstatus(ips_ha_t * ha, IPS_STATUS * pstatus)
+{
+ ips_scb_t *scb;
+ ips_stat_t *sp;
+ uint8_t basic_status;
+ uint8_t ext_status;
+ int errcode;
+
+ METHOD_TRACE("ips_chkstatus", 1);
+
+ scb = &ha->scbs[pstatus->fields.command_id];
+ scb->basic_status = basic_status =
+ pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK;
+ scb->extended_status = ext_status = pstatus->fields.extended_status;
+
+ sp = &ha->sp;
+ sp->residue_len = 0;
+ sp->scb_addr = (void *) scb;
+
+ /* Remove the item from the active queue */
+ ips_removeq_scb(&ha->scb_activelist, scb);
+
+ if (!scb->scsi_cmd)
+ /* internal commands are handled in do_ipsintr */
+ return;
+
+ DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus, scb->target_id, scb->lun);
+
+ if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd)))
+ /* passthru - just returns the raw result */
+ return;
+
+ errcode = DID_OK;
+
+ if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) ||
+ ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) {
+
+ if (scb->bus == 0) {
+ if ((basic_status & IPS_GSC_STATUS_MASK) ==
+ IPS_CMD_RECOVERED_ERROR) {
+ DEBUG_VAR(1,
+ "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+ ips_name, ha->host_num,
+ scb->cmd.basic_io.op_code,
+ basic_status, ext_status);
+ }
+
+ switch (scb->scsi_cmd->cmnd[0]) {
+ case ALLOW_MEDIUM_REMOVAL:
+ case REZERO_UNIT:
+ case ERASE:
+ case WRITE_FILEMARKS:
+ case SPACE:
+ errcode = DID_ERROR;
+ break;
+
+ case START_STOP:
+ break;
+
+ case TEST_UNIT_READY:
+ if (!ips_online(ha, scb)) {
+ errcode = DID_TIME_OUT;
+ }
+ break;
+
+ case INQUIRY:
+ if (ips_online(ha, scb)) {
+ ips_inquiry(ha, scb);
+ } else {
+ errcode = DID_TIME_OUT;
+ }
+ break;
+
+ case REQUEST_SENSE:
+ ips_reqsen(ha, scb);
+ break;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case RESERVE:
+ case RELEASE:
+ break;
+
+ case MODE_SENSE:
+ if (!ips_online(ha, scb)
+ || !ips_msense(ha, scb)) {
+ errcode = DID_ERROR;
+ }
+ break;
+
+ case READ_CAPACITY:
+ if (ips_online(ha, scb))
+ ips_rdcap(ha, scb);
+ else {
+ errcode = DID_TIME_OUT;
+ }
+ break;
+
+ case SEND_DIAGNOSTIC:
+ case REASSIGN_BLOCKS:
+ break;
+
+ case FORMAT_UNIT:
+ errcode = DID_ERROR;
+ break;
+
+ case SEEK_10:
+ case VERIFY:
+ case READ_DEFECT_DATA:
+ case READ_BUFFER:
+ case WRITE_BUFFER:
+ break;
+
+ default:
+ errcode = DID_ERROR;
+ } /* end switch */
+
+ scb->scsi_cmd->result = errcode << 16;
+ } else { /* bus == 0 */
+ /* restrict access to physical drives */
+ if ((scb->scsi_cmd->cmnd[0] == INQUIRY) &&
+ ((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) ==
+ TYPE_DISK)) {
+
+ scb->scsi_cmd->result = DID_TIME_OUT << 16;
+ }
+ } /* else */
+ } else { /* recovered error / success */
+ if (scb->bus == 0) {
+ DEBUG_VAR(1,
+ "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+ ips_name, ha->host_num,
+ scb->cmd.basic_io.op_code, basic_status,
+ ext_status);
+ }
+
+ ips_map_status(ha, scb, sp);
+ } /* else */
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_online */
+/* */
+/* Routine Description: */
+/* */
+/* Determine if a logical drive is online */
+/* */
+/****************************************************************************/
+static int
+ips_online(ips_ha_t * ha, ips_scb_t * scb)
+{
+ METHOD_TRACE("ips_online", 1);
+
+ if (scb->target_id >= IPS_MAX_LD)
+ return (0);
+
+ if ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1) {
+ memset(ha->logical_drive_info, 0, sizeof (IPS_LD_INFO));
+ return (0);
+ }
+
+ if (ha->logical_drive_info->drive_info[scb->target_id].state !=
+ IPS_LD_OFFLINE
+ && ha->logical_drive_info->drive_info[scb->target_id].state !=
+ IPS_LD_FREE
+ && ha->logical_drive_info->drive_info[scb->target_id].state !=
+ IPS_LD_CRS
+ && ha->logical_drive_info->drive_info[scb->target_id].state !=
+ IPS_LD_SYS)
+ return (1);
+ else
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_inquiry */
+/* */
+/* Routine Description: */
+/* */
+/* Simulate an inquiry command to a logical drive */
+/* */
+/****************************************************************************/
+static int
+ips_inquiry(ips_ha_t * ha, ips_scb_t * scb)
+{
+ IPS_SCSI_INQ_DATA inquiry;
+
+ METHOD_TRACE("ips_inquiry", 1);
+
+ memset(&inquiry, 0, sizeof (IPS_SCSI_INQ_DATA));
+
+ inquiry.DeviceType = IPS_SCSI_INQ_TYPE_DASD;
+ inquiry.DeviceTypeQualifier = IPS_SCSI_INQ_LU_CONNECTED;
+ inquiry.Version = IPS_SCSI_INQ_REV2;
+ inquiry.ResponseDataFormat = IPS_SCSI_INQ_RD_REV2;
+ inquiry.AdditionalLength = 31;
+ inquiry.Flags[0] = IPS_SCSI_INQ_Address16;
+ inquiry.Flags[1] =
+ IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync | IPS_SCSI_INQ_CmdQue;
+ strncpy(inquiry.VendorId, "IBM ", 8);
+ strncpy(inquiry.ProductId, "SERVERAID ", 16);
+ strncpy(inquiry.ProductRevisionLevel, "1.00", 4);
+
+ ips_scmd_buf_write(scb->scsi_cmd, &inquiry, sizeof (inquiry));
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_rdcap */
+/* */
+/* Routine Description: */
+/* */
+/* Simulate a read capacity command to a logical drive */
+/* */
+/****************************************************************************/
+static int
+ips_rdcap(ips_ha_t * ha, ips_scb_t * scb)
+{
+ IPS_SCSI_CAPACITY cap;
+
+ METHOD_TRACE("ips_rdcap", 1);
+
+ if (scb->scsi_cmd->bufflen < 8)
+ return (0);
+
+ cap.lba =
+ cpu_to_be32(le32_to_cpu
+ (ha->logical_drive_info->
+ drive_info[scb->target_id].sector_count) - 1);
+ cap.len = cpu_to_be32((uint32_t) IPS_BLKSIZE);
+
+ ips_scmd_buf_write(scb->scsi_cmd, &cap, sizeof (cap));
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_msense */
+/* */
+/* Routine Description: */
+/* */
+/* Simulate a mode sense command to a logical drive */
+/* */
+/****************************************************************************/
+static int
+ips_msense(ips_ha_t * ha, ips_scb_t * scb)
+{
+ uint16_t heads;
+ uint16_t sectors;
+ uint32_t cylinders;
+ IPS_SCSI_MODE_PAGE_DATA mdata;
+
+ METHOD_TRACE("ips_msense", 1);
+
+ if (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) > 0x400000 &&
+ (ha->enq->ucMiscFlag & 0x8) == 0) {
+ heads = IPS_NORM_HEADS;
+ sectors = IPS_NORM_SECTORS;
+ } else {
+ heads = IPS_COMP_HEADS;
+ sectors = IPS_COMP_SECTORS;
+ }
+
+ cylinders =
+ (le32_to_cpu(ha->enq->ulDriveSize[scb->target_id]) -
+ 1) / (heads * sectors);
+
+ memset(&mdata, 0, sizeof (IPS_SCSI_MODE_PAGE_DATA));
+
+ mdata.hdr.BlockDescLength = 8;
+
+ switch (scb->scsi_cmd->cmnd[2] & 0x3f) {
+ case 0x03: /* page 3 */
+ mdata.pdata.pg3.PageCode = 3;
+ mdata.pdata.pg3.PageLength = sizeof (IPS_SCSI_MODE_PAGE3);
+ mdata.hdr.DataLength =
+ 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg3.PageLength;
+ mdata.pdata.pg3.TracksPerZone = 0;
+ mdata.pdata.pg3.AltSectorsPerZone = 0;
+ mdata.pdata.pg3.AltTracksPerZone = 0;
+ mdata.pdata.pg3.AltTracksPerVolume = 0;
+ mdata.pdata.pg3.SectorsPerTrack = cpu_to_be16(sectors);
+ mdata.pdata.pg3.BytesPerSector = cpu_to_be16(IPS_BLKSIZE);
+ mdata.pdata.pg3.Interleave = cpu_to_be16(1);
+ mdata.pdata.pg3.TrackSkew = 0;
+ mdata.pdata.pg3.CylinderSkew = 0;
+ mdata.pdata.pg3.flags = IPS_SCSI_MP3_SoftSector;
+ break;
+
+ case 0x4:
+ mdata.pdata.pg4.PageCode = 4;
+ mdata.pdata.pg4.PageLength = sizeof (IPS_SCSI_MODE_PAGE4);
+ mdata.hdr.DataLength =
+ 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg4.PageLength;
+ mdata.pdata.pg4.CylindersHigh =
+ cpu_to_be16((cylinders >> 8) & 0xFFFF);
+ mdata.pdata.pg4.CylindersLow = (cylinders & 0xFF);
+ mdata.pdata.pg4.Heads = heads;
+ mdata.pdata.pg4.WritePrecompHigh = 0;
+ mdata.pdata.pg4.WritePrecompLow = 0;
+ mdata.pdata.pg4.ReducedWriteCurrentHigh = 0;
+ mdata.pdata.pg4.ReducedWriteCurrentLow = 0;
+ mdata.pdata.pg4.StepRate = cpu_to_be16(1);
+ mdata.pdata.pg4.LandingZoneHigh = 0;
+ mdata.pdata.pg4.LandingZoneLow = 0;
+ mdata.pdata.pg4.flags = 0;
+ mdata.pdata.pg4.RotationalOffset = 0;
+ mdata.pdata.pg4.MediumRotationRate = 0;
+ break;
+ case 0x8:
+ mdata.pdata.pg8.PageCode = 8;
+ mdata.pdata.pg8.PageLength = sizeof (IPS_SCSI_MODE_PAGE8);
+ mdata.hdr.DataLength =
+ 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg8.PageLength;
+ /* everything else is left set to 0 */
+ break;
+
+ default:
+ return (0);
+ } /* end switch */
+
+ ips_scmd_buf_write(scb->scsi_cmd, &mdata, sizeof (mdata));
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reqsen */
+/* */
+/* Routine Description: */
+/* */
+/* Simulate a request sense command to a logical drive */
+/* */
+/****************************************************************************/
+static int
+ips_reqsen(ips_ha_t * ha, ips_scb_t * scb)
+{
+ IPS_SCSI_REQSEN reqsen;
+
+ METHOD_TRACE("ips_reqsen", 1);
+
+ memset(&reqsen, 0, sizeof (IPS_SCSI_REQSEN));
+
+ reqsen.ResponseCode =
+ IPS_SCSI_REQSEN_VALID | IPS_SCSI_REQSEN_CURRENT_ERR;
+ reqsen.AdditionalLength = 10;
+ reqsen.AdditionalSenseCode = IPS_SCSI_REQSEN_NO_SENSE;
+ reqsen.AdditionalSenseCodeQual = IPS_SCSI_REQSEN_NO_SENSE;
+
+ ips_scmd_buf_write(scb->scsi_cmd, &reqsen, sizeof (reqsen));
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_free */
+/* */
+/* Routine Description: */
+/* */
+/* Free any allocated space for this controller */
+/* */
+/****************************************************************************/
+static void
+ips_free(ips_ha_t * ha)
+{
+
+ METHOD_TRACE("ips_free", 1);
+
+ if (ha) {
+ if (ha->enq) {
+ pci_free_consistent(ha->pcidev, sizeof(IPS_ENQ),
+ ha->enq, ha->enq_busaddr);
+ ha->enq = NULL;
+ }
+
+ if (ha->conf) {
+ kfree(ha->conf);
+ ha->conf = NULL;
+ }
+
+ if (ha->adapt) {
+ pci_free_consistent(ha->pcidev,
+ sizeof (IPS_ADAPTER) +
+ sizeof (IPS_IO_CMD), ha->adapt,
+ ha->adapt->hw_status_start);
+ ha->adapt = NULL;
+ }
+
+ if (ha->logical_drive_info) {
+ pci_free_consistent(ha->pcidev,
+ sizeof (IPS_LD_INFO),
+ ha->logical_drive_info,
+ ha->logical_drive_info_dma_addr);
+ ha->logical_drive_info = NULL;
+ }
+
+ if (ha->nvram) {
+ kfree(ha->nvram);
+ ha->nvram = NULL;
+ }
+
+ if (ha->subsys) {
+ kfree(ha->subsys);
+ ha->subsys = NULL;
+ }
+
+ if (ha->ioctl_data) {
+ pci_free_consistent(ha->pcidev, ha->ioctl_len,
+ ha->ioctl_data, ha->ioctl_busaddr);
+ ha->ioctl_data = NULL;
+ ha->ioctl_datasize = 0;
+ ha->ioctl_len = 0;
+ }
+ ips_deallocatescbs(ha, ha->max_cmds);
+
+ /* free memory mapped (if applicable) */
+ if (ha->mem_ptr) {
+ iounmap(ha->ioremap_ptr);
+ ha->ioremap_ptr = NULL;
+ ha->mem_ptr = NULL;
+ }
+
+ if (ha->mem_addr)
+ release_mem_region(ha->mem_addr, ha->mem_len);
+ ha->mem_addr = 0;
+
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_deallocatescbs */
+/* */
+/* Routine Description: */
+/* */
+/* Free the command blocks */
+/* */
+/****************************************************************************/
+static int
+ips_deallocatescbs(ips_ha_t * ha, int cmds)
+{
+ if (ha->scbs) {
+ pci_free_consistent(ha->pcidev,
+ IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * cmds,
+ ha->scbs->sg_list.list,
+ ha->scbs->sg_busaddr);
+ pci_free_consistent(ha->pcidev, sizeof (ips_scb_t) * cmds,
+ ha->scbs, ha->scbs->scb_busaddr);
+ ha->scbs = NULL;
+ } /* end if */
+ return 1;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_allocatescbs */
+/* */
+/* Routine Description: */
+/* */
+/* Allocate the command blocks */
+/* */
+/****************************************************************************/
+static int
+ips_allocatescbs(ips_ha_t * ha)
+{
+ ips_scb_t *scb_p;
+ IPS_SG_LIST ips_sg;
+ int i;
+ dma_addr_t command_dma, sg_dma;
+
+ METHOD_TRACE("ips_allocatescbs", 1);
+
+ /* Allocate memory for the SCBs */
+ ha->scbs =
+ pci_alloc_consistent(ha->pcidev, ha->max_cmds * sizeof (ips_scb_t),
+ &command_dma);
+ if (ha->scbs == NULL)
+ return 0;
+ ips_sg.list =
+ pci_alloc_consistent(ha->pcidev,
+ IPS_SGLIST_SIZE(ha) * IPS_MAX_SG *
+ ha->max_cmds, &sg_dma);
+ if (ips_sg.list == NULL) {
+ pci_free_consistent(ha->pcidev,
+ ha->max_cmds * sizeof (ips_scb_t), ha->scbs,
+ command_dma);
+ return 0;
+ }
+
+ memset(ha->scbs, 0, ha->max_cmds * sizeof (ips_scb_t));
+
+ for (i = 0; i < ha->max_cmds; i++) {
+ scb_p = &ha->scbs[i];
+ scb_p->scb_busaddr = command_dma + sizeof (ips_scb_t) * i;
+ /* set up S/G list */
+ if (IPS_USE_ENH_SGLIST(ha)) {
+ scb_p->sg_list.enh_list =
+ ips_sg.enh_list + i * IPS_MAX_SG;
+ scb_p->sg_busaddr =
+ sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i;
+ } else {
+ scb_p->sg_list.std_list =
+ ips_sg.std_list + i * IPS_MAX_SG;
+ scb_p->sg_busaddr =
+ sg_dma + IPS_SGLIST_SIZE(ha) * IPS_MAX_SG * i;
+ }
+
+ /* add to the free list */
+ if (i < ha->max_cmds - 1) {
+ scb_p->q_next = ha->scb_freelist;
+ ha->scb_freelist = scb_p;
+ }
+ }
+
+ /* success */
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_scb */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a CCB to default values */
+/* */
+/****************************************************************************/
+static void
+ips_init_scb(ips_ha_t * ha, ips_scb_t * scb)
+{
+ IPS_SG_LIST sg_list;
+ uint32_t cmd_busaddr, sg_busaddr;
+ METHOD_TRACE("ips_init_scb", 1);
+
+ if (scb == NULL)
+ return;
+
+ sg_list.list = scb->sg_list.list;
+ cmd_busaddr = scb->scb_busaddr;
+ sg_busaddr = scb->sg_busaddr;
+ /* zero fill */
+ memset(scb, 0, sizeof (ips_scb_t));
+ memset(ha->dummy, 0, sizeof (IPS_IO_CMD));
+
+ /* Initialize dummy command bucket */
+ ha->dummy->op_code = 0xFF;
+ ha->dummy->ccsar = cpu_to_le32(ha->adapt->hw_status_start
+ + sizeof (IPS_ADAPTER));
+ ha->dummy->command_id = IPS_MAX_CMDS;
+
+ /* set bus address of scb */
+ scb->scb_busaddr = cmd_busaddr;
+ scb->sg_busaddr = sg_busaddr;
+ scb->sg_list.list = sg_list.list;
+
+ /* Neptune Fix */
+ scb->cmd.basic_io.cccr = cpu_to_le32((uint32_t) IPS_BIT_ILE);
+ scb->cmd.basic_io.ccsar = cpu_to_le32(ha->adapt->hw_status_start
+ + sizeof (IPS_ADAPTER));
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_get_scb */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a CCB to default values */
+/* */
+/* ASSUMED to be callled from within a lock */
+/* */
+/****************************************************************************/
+static ips_scb_t *
+ips_getscb(ips_ha_t * ha)
+{
+ ips_scb_t *scb;
+
+ METHOD_TRACE("ips_getscb", 1);
+
+ if ((scb = ha->scb_freelist) == NULL) {
+
+ return (NULL);
+ }
+
+ ha->scb_freelist = scb->q_next;
+ scb->flags = 0;
+ scb->q_next = NULL;
+
+ ips_init_scb(ha, scb);
+
+ return (scb);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_free_scb */
+/* */
+/* Routine Description: */
+/* */
+/* Return an unused CCB back to the free list */
+/* */
+/* ASSUMED to be called from within a lock */
+/* */
+/****************************************************************************/
+static void
+ips_freescb(ips_ha_t * ha, ips_scb_t * scb)
+{
+
+ METHOD_TRACE("ips_freescb", 1);
+ if (scb->flags & IPS_SCB_MAP_SG)
+ pci_unmap_sg(ha->pcidev, scb->scsi_cmd->request_buffer,
+ scb->scsi_cmd->use_sg, IPS_DMA_DIR(scb));
+ else if (scb->flags & IPS_SCB_MAP_SINGLE)
+ pci_unmap_single(ha->pcidev, scb->data_busaddr, scb->data_len,
+ IPS_DMA_DIR(scb));
+
+ /* check to make sure this is not our "special" scb */
+ if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
+ scb->q_next = ha->scb_freelist;
+ ha->scb_freelist = scb;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isinit_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Is controller initialized ? */
+/* */
+/****************************************************************************/
+static int
+ips_isinit_copperhead(ips_ha_t * ha)
+{
+ uint8_t scpr;
+ uint8_t isr;
+
+ METHOD_TRACE("ips_isinit_copperhead", 1);
+
+ isr = inb(ha->io_addr + IPS_REG_HISR);
+ scpr = inb(ha->io_addr + IPS_REG_SCPR);
+
+ if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+ return (0);
+ else
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isinit_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Is controller initialized ? */
+/* */
+/****************************************************************************/
+static int
+ips_isinit_copperhead_memio(ips_ha_t * ha)
+{
+ uint8_t isr = 0;
+ uint8_t scpr;
+
+ METHOD_TRACE("ips_is_init_copperhead_memio", 1);
+
+ isr = readb(ha->mem_ptr + IPS_REG_HISR);
+ scpr = readb(ha->mem_ptr + IPS_REG_SCPR);
+
+ if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+ return (0);
+ else
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isinit_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Is controller initialized ? */
+/* */
+/****************************************************************************/
+static int
+ips_isinit_morpheus(ips_ha_t * ha)
+{
+ uint32_t post;
+ uint32_t bits;
+
+ METHOD_TRACE("ips_is_init_morpheus", 1);
+
+ post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+ bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (post == 0)
+ return (0);
+ else if (bits & 0x3)
+ return (0);
+ else
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_enable_int_copperhead */
+/* */
+/* Routine Description: */
+/* Turn on interrupts */
+/* */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead(ips_ha_t * ha)
+{
+ METHOD_TRACE("ips_enable_int_copperhead", 1);
+
+ outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI);
+ inb(ha->io_addr + IPS_REG_HISR); /*Ensure PCI Posting Completes*/
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_enable_int_copperhead_memio */
+/* */
+/* Routine Description: */
+/* Turn on interrupts */
+/* */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead_memio(ips_ha_t * ha)
+{
+ METHOD_TRACE("ips_enable_int_copperhead_memio", 1);
+
+ writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+ readb(ha->mem_ptr + IPS_REG_HISR); /*Ensure PCI Posting Completes*/
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_enable_int_morpheus */
+/* */
+/* Routine Description: */
+/* Turn on interrupts */
+/* */
+/****************************************************************************/
+static void
+ips_enable_int_morpheus(ips_ha_t * ha)
+{
+ uint32_t Oimr;
+
+ METHOD_TRACE("ips_enable_int_morpheus", 1);
+
+ Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+ Oimr &= ~0x08;
+ writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+ readl(ha->mem_ptr + IPS_REG_I960_OIMR); /*Ensure PCI Posting Completes*/
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a copperhead controller */
+/* */
+/****************************************************************************/
+static int
+ips_init_copperhead(ips_ha_t * ha)
+{
+ uint8_t Isr;
+ uint8_t Cbsp;
+ uint8_t PostByte[IPS_MAX_POST_BYTES];
+ uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
+ int i, j;
+
+ METHOD_TRACE("ips_init_copperhead", 1);
+
+ for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+ for (j = 0; j < 45; j++) {
+ Isr = inb(ha->io_addr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (j >= 45)
+ /* error occurred */
+ return (0);
+
+ PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+ outb(Isr, ha->io_addr + IPS_REG_HISR);
+ }
+
+ if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "reset controller fails (post status %x %x).\n",
+ PostByte[0], PostByte[1]);
+
+ return (0);
+ }
+
+ for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+ for (j = 0; j < 240; j++) {
+ Isr = inb(ha->io_addr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (j >= 240)
+ /* error occurred */
+ return (0);
+
+ ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+ outb(Isr, ha->io_addr + IPS_REG_HISR);
+ }
+
+ for (i = 0; i < 240; i++) {
+ Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+
+ if ((Cbsp & IPS_BIT_OP) == 0)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 240)
+ /* reset failed */
+ return (0);
+
+ /* setup CCCR */
+ outl(cpu_to_le32(0x1010), ha->io_addr + IPS_REG_CCCR);
+
+ /* Enable busmastering */
+ outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ /* fix for anaconda64 */
+ outl(0, ha->io_addr + IPS_REG_NDAE);
+
+ /* Enable interrupts */
+ outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a copperhead controller with memory mapped I/O */
+/* */
+/****************************************************************************/
+static int
+ips_init_copperhead_memio(ips_ha_t * ha)
+{
+ uint8_t Isr = 0;
+ uint8_t Cbsp;
+ uint8_t PostByte[IPS_MAX_POST_BYTES];
+ uint8_t ConfigByte[IPS_MAX_CONFIG_BYTES];
+ int i, j;
+
+ METHOD_TRACE("ips_init_copperhead_memio", 1);
+
+ for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+ for (j = 0; j < 45; j++) {
+ Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (j >= 45)
+ /* error occurred */
+ return (0);
+
+ PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+ writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+ }
+
+ if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "reset controller fails (post status %x %x).\n",
+ PostByte[0], PostByte[1]);
+
+ return (0);
+ }
+
+ for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+ for (j = 0; j < 240; j++) {
+ Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (j >= 240)
+ /* error occurred */
+ return (0);
+
+ ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+ writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+ }
+
+ for (i = 0; i < 240; i++) {
+ Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP);
+
+ if ((Cbsp & IPS_BIT_OP) == 0)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 240)
+ /* error occurred */
+ return (0);
+
+ /* setup CCCR */
+ writel(0x1010, ha->mem_ptr + IPS_REG_CCCR);
+
+ /* Enable busmastering */
+ writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR);
+
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ /* fix for anaconda64 */
+ writel(0, ha->mem_ptr + IPS_REG_NDAE);
+
+ /* Enable interrupts */
+ writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+
+ /* if we get here then everything went OK */
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a morpheus controller */
+/* */
+/****************************************************************************/
+static int
+ips_init_morpheus(ips_ha_t * ha)
+{
+ uint32_t Post;
+ uint32_t Config;
+ uint32_t Isr;
+ uint32_t Oimr;
+ int i;
+
+ METHOD_TRACE("ips_init_morpheus", 1);
+
+ /* Wait up to 45 secs for Post */
+ for (i = 0; i < 45; i++) {
+ Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Isr & IPS_BIT_I960_MSG0I)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 45) {
+ /* error occurred */
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "timeout waiting for post.\n");
+
+ return (0);
+ }
+
+ Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+
+ if (Post == 0x4F00) { /* If Flashing the Battery PIC */
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Flashing Battery PIC, Please wait ...\n");
+
+ /* Clear the interrupt bit */
+ Isr = (uint32_t) IPS_BIT_I960_MSG0I;
+ writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ for (i = 0; i < 120; i++) { /* Wait Up to 2 Min. for Completion */
+ Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+ if (Post != 0x4F00)
+ break;
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 120) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "timeout waiting for Battery PIC Flash\n");
+ return (0);
+ }
+
+ }
+
+ /* Clear the interrupt bit */
+ Isr = (uint32_t) IPS_BIT_I960_MSG0I;
+ writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Post < (IPS_GOOD_POST_STATUS << 8)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "reset controller fails (post status %x).\n", Post);
+
+ return (0);
+ }
+
+ /* Wait up to 240 secs for config bytes */
+ for (i = 0; i < 240; i++) {
+ Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Isr & IPS_BIT_I960_MSG1I)
+ break;
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 240) {
+ /* error occurred */
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "timeout waiting for config.\n");
+
+ return (0);
+ }
+
+ Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
+
+ /* Clear interrupt bit */
+ Isr = (uint32_t) IPS_BIT_I960_MSG1I;
+ writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ /* Turn on the interrupts */
+ Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+ Oimr &= ~0x8;
+ writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+
+ /* if we get here then everything went OK */
+
+ /* Since we did a RESET, an EraseStripeLock may be needed */
+ if (Post == 0xEF10) {
+ if ((Config == 0x000F) || (Config == 0x0009))
+ ha->requires_esl = 1;
+ }
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reset_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_reset_copperhead(ips_ha_t * ha)
+{
+ int reset_counter;
+
+ METHOD_TRACE("ips_reset_copperhead", 1);
+
+ DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
+ ips_name, ha->host_num, ha->io_addr, ha->irq);
+
+ reset_counter = 0;
+
+ while (reset_counter < 2) {
+ reset_counter++;
+
+ outb(IPS_BIT_RST, ha->io_addr + IPS_REG_SCPR);
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+
+ outb(0, ha->io_addr + IPS_REG_SCPR);
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+
+ if ((*ha->func.init) (ha))
+ break;
+ else if (reset_counter >= 2) {
+
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reset_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_reset_copperhead_memio(ips_ha_t * ha)
+{
+ int reset_counter;
+
+ METHOD_TRACE("ips_reset_copperhead_memio", 1);
+
+ DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
+ ips_name, ha->host_num, ha->mem_addr, ha->irq);
+
+ reset_counter = 0;
+
+ while (reset_counter < 2) {
+ reset_counter++;
+
+ writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR);
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+
+ writeb(0, ha->mem_ptr + IPS_REG_SCPR);
+
+ /* Delay for 1 Second */
+ MDELAY(IPS_ONE_SEC);
+
+ if ((*ha->func.init) (ha))
+ break;
+ else if (reset_counter >= 2) {
+
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reset_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_reset_morpheus(ips_ha_t * ha)
+{
+ int reset_counter;
+ uint8_t junk;
+
+ METHOD_TRACE("ips_reset_morpheus", 1);
+
+ DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
+ ips_name, ha->host_num, ha->mem_addr, ha->irq);
+
+ reset_counter = 0;
+
+ while (reset_counter < 2) {
+ reset_counter++;
+
+ writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR);
+
+ /* Delay for 5 Seconds */
+ MDELAY(5 * IPS_ONE_SEC);
+
+ /* Do a PCI config read to wait for adapter */
+ pci_read_config_byte(ha->pcidev, 4, &junk);
+
+ if ((*ha->func.init) (ha))
+ break;
+ else if (reset_counter >= 2) {
+
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statinit */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize the status queues on the controller */
+/* */
+/****************************************************************************/
+static void
+ips_statinit(ips_ha_t * ha)
+{
+ uint32_t phys_status_start;
+
+ METHOD_TRACE("ips_statinit", 1);
+
+ ha->adapt->p_status_start = ha->adapt->status;
+ ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
+ ha->adapt->p_status_tail = ha->adapt->status;
+
+ phys_status_start = ha->adapt->hw_status_start;
+ outl(cpu_to_le32(phys_status_start), ha->io_addr + IPS_REG_SQSR);
+ outl(cpu_to_le32(phys_status_start + IPS_STATUS_Q_SIZE),
+ ha->io_addr + IPS_REG_SQER);
+ outl(cpu_to_le32(phys_status_start + IPS_STATUS_SIZE),
+ ha->io_addr + IPS_REG_SQHR);
+ outl(cpu_to_le32(phys_status_start), ha->io_addr + IPS_REG_SQTR);
+
+ ha->adapt->hw_status_tail = phys_status_start;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statinit_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize the status queues on the controller */
+/* */
+/****************************************************************************/
+static void
+ips_statinit_memio(ips_ha_t * ha)
+{
+ uint32_t phys_status_start;
+
+ METHOD_TRACE("ips_statinit_memio", 1);
+
+ ha->adapt->p_status_start = ha->adapt->status;
+ ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
+ ha->adapt->p_status_tail = ha->adapt->status;
+
+ phys_status_start = ha->adapt->hw_status_start;
+ writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
+ writel(phys_status_start + IPS_STATUS_Q_SIZE,
+ ha->mem_ptr + IPS_REG_SQER);
+ writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
+ writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
+
+ ha->adapt->hw_status_tail = phys_status_start;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statupd_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an element from the status queue */
+/* */
+/****************************************************************************/
+static uint32_t
+ips_statupd_copperhead(ips_ha_t * ha)
+{
+ METHOD_TRACE("ips_statupd_copperhead", 1);
+
+ if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
+ ha->adapt->p_status_tail++;
+ ha->adapt->hw_status_tail += sizeof (IPS_STATUS);
+ } else {
+ ha->adapt->p_status_tail = ha->adapt->p_status_start;
+ ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
+ }
+
+ outl(cpu_to_le32(ha->adapt->hw_status_tail),
+ ha->io_addr + IPS_REG_SQTR);
+
+ return (ha->adapt->p_status_tail->value);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statupd_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an element from the status queue */
+/* */
+/****************************************************************************/
+static uint32_t
+ips_statupd_copperhead_memio(ips_ha_t * ha)
+{
+ METHOD_TRACE("ips_statupd_copperhead_memio", 1);
+
+ if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
+ ha->adapt->p_status_tail++;
+ ha->adapt->hw_status_tail += sizeof (IPS_STATUS);
+ } else {
+ ha->adapt->p_status_tail = ha->adapt->p_status_start;
+ ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
+ }
+
+ writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR);
+
+ return (ha->adapt->p_status_tail->value);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statupd_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an element from the status queue */
+/* */
+/****************************************************************************/
+static uint32_t
+ips_statupd_morpheus(ips_ha_t * ha)
+{
+ uint32_t val;
+
+ METHOD_TRACE("ips_statupd_morpheus", 1);
+
+ val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ);
+
+ return (val);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_copperhead(ips_ha_t * ha, ips_scb_t * scb)
+{
+ uint32_t TimeOut;
+ uint32_t val;
+
+ METHOD_TRACE("ips_issue_copperhead", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus, scb->target_id, scb->lun);
+ } else {
+ DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d",
+ ips_name, ha->host_num, scb->cmd.basic_io.command_id);
+ }
+
+ TimeOut = 0;
+
+ while ((val =
+ le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & IPS_BIT_SEM) {
+ udelay(1000);
+
+ if (++TimeOut >= IPS_SEM_TIMEOUT) {
+ if (!(val & IPS_BIT_START_STOP))
+ break;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "ips_issue val [0x%x].\n", val);
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "ips_issue semaphore chk timeout.\n");
+
+ return (IPS_FAILURE);
+ } /* end if */
+ } /* end while */
+
+ outl(cpu_to_le32(scb->scb_busaddr), ha->io_addr + IPS_REG_CCSAR);
+ outw(cpu_to_le32(IPS_BIT_START_CMD), ha->io_addr + IPS_REG_CCCR);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_copperhead_memio(ips_ha_t * ha, ips_scb_t * scb)
+{
+ uint32_t TimeOut;
+ uint32_t val;
+
+ METHOD_TRACE("ips_issue_copperhead_memio", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus, scb->target_id, scb->lun);
+ } else {
+ DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+ ips_name, ha->host_num, scb->cmd.basic_io.command_id);
+ }
+
+ TimeOut = 0;
+
+ while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
+ udelay(1000);
+
+ if (++TimeOut >= IPS_SEM_TIMEOUT) {
+ if (!(val & IPS_BIT_START_STOP))
+ break;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "ips_issue val [0x%x].\n", val);
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "ips_issue semaphore chk timeout.\n");
+
+ return (IPS_FAILURE);
+ } /* end if */
+ } /* end while */
+
+ writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
+ writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_i2o */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_i2o(ips_ha_t * ha, ips_scb_t * scb)
+{
+
+ METHOD_TRACE("ips_issue_i2o", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus, scb->target_id, scb->lun);
+ } else {
+ DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+ ips_name, ha->host_num, scb->cmd.basic_io.command_id);
+ }
+
+ outl(cpu_to_le32(scb->scb_busaddr), ha->io_addr + IPS_REG_I2O_INMSGQ);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_i2o_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_i2o_memio(ips_ha_t * ha, ips_scb_t * scb)
+{
+
+ METHOD_TRACE("ips_issue_i2o_memio", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus, scb->target_id, scb->lun);
+ } else {
+ DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+ ips_name, ha->host_num, scb->cmd.basic_io.command_id);
+ }
+
+ writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isintr_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Test to see if an interrupt is for us */
+/* */
+/****************************************************************************/
+static int
+ips_isintr_copperhead(ips_ha_t * ha)
+{
+ uint8_t Isr;
+
+ METHOD_TRACE("ips_isintr_copperhead", 2);
+
+ Isr = inb(ha->io_addr + IPS_REG_HISR);
+
+ if (Isr == 0xFF)
+ /* ?!?! Nothing really there */
+ return (0);
+
+ if (Isr & IPS_BIT_SCE)
+ return (1);
+ else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
+ /* status queue overflow or GHI */
+ /* just clear the interrupt */
+ outb(Isr, ha->io_addr + IPS_REG_HISR);
+ }
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isintr_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Test to see if an interrupt is for us */
+/* */
+/****************************************************************************/
+static int
+ips_isintr_copperhead_memio(ips_ha_t * ha)
+{
+ uint8_t Isr;
+
+ METHOD_TRACE("ips_isintr_memio", 2);
+
+ Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+
+ if (Isr == 0xFF)
+ /* ?!?! Nothing really there */
+ return (0);
+
+ if (Isr & IPS_BIT_SCE)
+ return (1);
+ else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
+ /* status queue overflow or GHI */
+ /* just clear the interrupt */
+ writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+ }
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isintr_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Test to see if an interrupt is for us */
+/* */
+/****************************************************************************/
+static int
+ips_isintr_morpheus(ips_ha_t * ha)
+{
+ uint32_t Isr;
+
+ METHOD_TRACE("ips_isintr_morpheus", 2);
+
+ Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Isr & IPS_BIT_I2O_OPQI)
+ return (1);
+ else
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_wait */
+/* */
+/* Routine Description: */
+/* */
+/* Wait for a command to complete */
+/* */
+/****************************************************************************/
+static int
+ips_wait(ips_ha_t * ha, int time, int intr)
+{
+ int ret;
+ int done;
+
+ METHOD_TRACE("ips_wait", 1);
+
+ ret = IPS_FAILURE;
+ done = FALSE;
+
+ time *= IPS_ONE_SEC; /* convert seconds */
+
+ while ((time > 0) && (!done)) {
+ if (intr == IPS_INTR_ON) {
+ if (ha->waitflag == FALSE) {
+ ret = IPS_SUCCESS;
+ done = TRUE;
+ break;
+ }
+ } else if (intr == IPS_INTR_IORL) {
+ if (ha->waitflag == FALSE) {
+ /*
+ * controller generated an interrupt to
+ * acknowledge completion of the command
+ * and ips_intr() has serviced the interrupt.
+ */
+ ret = IPS_SUCCESS;
+ done = TRUE;
+ break;
+ }
+
+ /*
+ * NOTE: we already have the io_request_lock so
+ * even if we get an interrupt it won't get serviced
+ * until after we finish.
+ */
+
+ (*ha->func.intr) (ha);
+ }
+
+ /* This looks like a very evil loop, but it only does this during start-up */
+ udelay(1000);
+ time--;
+ }
+
+ return (ret);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_write_driver_status */
+/* */
+/* Routine Description: */
+/* */
+/* Write OS/Driver version to Page 5 of the nvram on the controller */
+/* */
+/****************************************************************************/
+static int
+ips_write_driver_status(ips_ha_t * ha, int intr)
+{
+ METHOD_TRACE("ips_write_driver_status", 1);
+
+ if (!ips_readwrite_page5(ha, FALSE, intr)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "unable to read NVRAM page 5.\n");
+
+ return (0);
+ }
+
+ /* check to make sure the page has a valid */
+ /* signature */
+ if (le32_to_cpu(ha->nvram->signature) != IPS_NVRAM_P5_SIG) {
+ DEBUG_VAR(1,
+ "(%s%d) NVRAM page 5 has an invalid signature: %X.",
+ ips_name, ha->host_num, ha->nvram->signature);
+ ha->nvram->signature = IPS_NVRAM_P5_SIG;
+ }
+
+ DEBUG_VAR(2,
+ "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
+ ips_name, ha->host_num, le16_to_cpu(ha->nvram->adapter_type),
+ ha->nvram->adapter_slot, ha->nvram->bios_high[0],
+ ha->nvram->bios_high[1], ha->nvram->bios_high[2],
+ ha->nvram->bios_high[3], ha->nvram->bios_low[0],
+ ha->nvram->bios_low[1], ha->nvram->bios_low[2],
+ ha->nvram->bios_low[3]);
+
+ ips_get_bios_version(ha, intr);
+
+ /* change values (as needed) */
+ ha->nvram->operating_system = IPS_OS_LINUX;
+ ha->nvram->adapter_type = ha->ad_type;
+ strncpy((char *) ha->nvram->driver_high, IPS_VERSION_HIGH, 4);
+ strncpy((char *) ha->nvram->driver_low, IPS_VERSION_LOW, 4);
+ strncpy((char *) ha->nvram->bios_high, ha->bios_version, 4);
+ strncpy((char *) ha->nvram->bios_low, ha->bios_version + 4, 4);
+
+ ips_version_check(ha, intr); /* Check BIOS/FW/Driver Versions */
+
+ /* now update the page */
+ if (!ips_readwrite_page5(ha, TRUE, intr)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "unable to write NVRAM page 5.\n");
+
+ return (0);
+ }
+
+ /* IF NVRAM Page 5 is OK, Use it for Slot Number Info Because Linux Doesn't Do Slots */
+ ha->slot_num = ha->nvram->adapter_slot;
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_read_adapter_status */
+/* */
+/* Routine Description: */
+/* */
+/* Do an Inquiry command to the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_read_adapter_status(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+ int ret;
+
+ METHOD_TRACE("ips_read_adapter_status", 1);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_ENQUIRY;
+
+ scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.basic_io.sg_count = 0;
+ scb->cmd.basic_io.lba = 0;
+ scb->cmd.basic_io.sector_count = 0;
+ scb->cmd.basic_io.log_drv = 0;
+ scb->data_len = sizeof (*ha->enq);
+ scb->cmd.basic_io.sg_addr = ha->enq_busaddr;
+
+ /* send command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
+ return (0);
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_read_subsystem_parameters */
+/* */
+/* Routine Description: */
+/* */
+/* Read subsystem parameters from the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_read_subsystem_parameters(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+ int ret;
+
+ METHOD_TRACE("ips_read_subsystem_parameters", 1);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_GET_SUBSYS;
+
+ scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.basic_io.sg_count = 0;
+ scb->cmd.basic_io.lba = 0;
+ scb->cmd.basic_io.sector_count = 0;
+ scb->cmd.basic_io.log_drv = 0;
+ scb->data_len = sizeof (*ha->subsys);
+ scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr;
+
+ /* send command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
+ return (0);
+
+ memcpy(ha->subsys, ha->ioctl_data, sizeof(*ha->subsys));
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_read_config */
+/* */
+/* Routine Description: */
+/* */
+/* Read the configuration on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_read_config(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+ int i;
+ int ret;
+
+ METHOD_TRACE("ips_read_config", 1);
+
+ /* set defaults for initiator IDs */
+ for (i = 0; i < 4; i++)
+ ha->conf->init_id[i] = 7;
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_READ_CONF;
+
+ scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF;
+ scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->data_len = sizeof (*ha->conf);
+ scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr;
+
+ /* send command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
+
+ memset(ha->conf, 0, sizeof (IPS_CONF));
+
+ /* reset initiator IDs */
+ for (i = 0; i < 4; i++)
+ ha->conf->init_id[i] = 7;
+
+ /* Allow Completed with Errors, so JCRM can access the Adapter to fix the problems */
+ if ((scb->basic_status & IPS_GSC_STATUS_MASK) ==
+ IPS_CMD_CMPLT_WERROR)
+ return (1);
+
+ return (0);
+ }
+
+ memcpy(ha->conf, ha->ioctl_data, sizeof(*ha->conf));
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_readwrite_page5 */
+/* */
+/* Routine Description: */
+/* */
+/* Read nvram page 5 from the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_readwrite_page5(ips_ha_t * ha, int write, int intr)
+{
+ ips_scb_t *scb;
+ int ret;
+
+ METHOD_TRACE("ips_readwrite_page5", 1);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_RW_NVRAM_PAGE;
+
+ scb->cmd.nvram.op_code = IPS_CMD_RW_NVRAM_PAGE;
+ scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.nvram.page = 5;
+ scb->cmd.nvram.write = write;
+ scb->cmd.nvram.reserved = 0;
+ scb->cmd.nvram.reserved2 = 0;
+ scb->data_len = sizeof (*ha->nvram);
+ scb->cmd.nvram.buffer_addr = ha->ioctl_busaddr;
+ if (write)
+ memcpy(ha->ioctl_data, ha->nvram, sizeof(*ha->nvram));
+
+ /* issue the command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) {
+
+ memset(ha->nvram, 0, sizeof (IPS_NVRAM_P5));
+
+ return (0);
+ }
+ if (!write)
+ memcpy(ha->nvram, ha->ioctl_data, sizeof(*ha->nvram));
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_clear_adapter */
+/* */
+/* Routine Description: */
+/* */
+/* Clear the stripe lock tables */
+/* */
+/****************************************************************************/
+static int
+ips_clear_adapter(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+ int ret;
+
+ METHOD_TRACE("ips_clear_adapter", 1);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_reset_timeout;
+ scb->cdb[0] = IPS_CMD_CONFIG_SYNC;
+
+ scb->cmd.config_sync.op_code = IPS_CMD_CONFIG_SYNC;
+ scb->cmd.config_sync.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.config_sync.channel = 0;
+ scb->cmd.config_sync.source_target = IPS_POCL;
+ scb->cmd.config_sync.reserved = 0;
+ scb->cmd.config_sync.reserved2 = 0;
+ scb->cmd.config_sync.reserved3 = 0;
+
+ /* issue command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
+ return (0);
+
+ /* send unlock stripe command */
+ ips_init_scb(ha, scb);
+
+ scb->cdb[0] = IPS_CMD_ERROR_TABLE;
+ scb->timeout = ips_reset_timeout;
+
+ scb->cmd.unlock_stripe.op_code = IPS_CMD_ERROR_TABLE;
+ scb->cmd.unlock_stripe.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.unlock_stripe.log_drv = 0;
+ scb->cmd.unlock_stripe.control = IPS_CSL;
+ scb->cmd.unlock_stripe.reserved = 0;
+ scb->cmd.unlock_stripe.reserved2 = 0;
+ scb->cmd.unlock_stripe.reserved3 = 0;
+
+ /* issue command */
+ if (((ret =
+ ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE)
+ || (ret == IPS_SUCCESS_IMM)
+ || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
+ return (0);
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_ffdc_reset */
+/* */
+/* Routine Description: */
+/* */
+/* FFDC: write reset info */
+/* */
+/****************************************************************************/
+static void
+ips_ffdc_reset(ips_ha_t * ha, int intr)
+{
+ ips_scb_t *scb;
+
+ METHOD_TRACE("ips_ffdc_reset", 1);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FFDC;
+ scb->cmd.ffdc.op_code = IPS_CMD_FFDC;
+ scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.ffdc.reset_count = ha->reset_count;
+ scb->cmd.ffdc.reset_type = 0x80;
+
+ /* convert time to what the card wants */
+ ips_fix_ffdc_time(ha, scb, ha->last_ffdc);
+
+ /* issue command */
+ ips_send_wait(ha, scb, ips_cmd_timeout, intr);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_ffdc_time */
+/* */
+/* Routine Description: */
+/* */
+/* FFDC: write time info */
+/* */
+/****************************************************************************/
+static void
+ips_ffdc_time(ips_ha_t * ha)
+{
+ ips_scb_t *scb;
+
+ METHOD_TRACE("ips_ffdc_time", 1);
+
+ DEBUG_VAR(1, "(%s%d) Sending time update.", ips_name, ha->host_num);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FFDC;
+ scb->cmd.ffdc.op_code = IPS_CMD_FFDC;
+ scb->cmd.ffdc.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.ffdc.reset_count = 0;
+ scb->cmd.ffdc.reset_type = 0;
+
+ /* convert time to what the card wants */
+ ips_fix_ffdc_time(ha, scb, ha->last_ffdc);
+
+ /* issue command */
+ ips_send_wait(ha, scb, ips_cmd_timeout, IPS_FFDC);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_fix_ffdc_time */
+/* */
+/* Routine Description: */
+/* Adjust time_t to what the card wants */
+/* */
+/****************************************************************************/
+static void
+ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time_t current_time)
+{
+ long days;
+ long rem;
+ int i;
+ int year;
+ int yleap;
+ int year_lengths[2] = { IPS_DAYS_NORMAL_YEAR, IPS_DAYS_LEAP_YEAR };
+ int month_lengths[12][2] = { {31, 31},
+ {28, 29},
+ {31, 31},
+ {30, 30},
+ {31, 31},
+ {30, 30},
+ {31, 31},
+ {31, 31},
+ {30, 30},
+ {31, 31},
+ {30, 30},
+ {31, 31}
+ };
+
+ METHOD_TRACE("ips_fix_ffdc_time", 1);
+
+ days = current_time / IPS_SECS_DAY;
+ rem = current_time % IPS_SECS_DAY;
+
+ scb->cmd.ffdc.hour = (rem / IPS_SECS_HOUR);
+ rem = rem % IPS_SECS_HOUR;
+ scb->cmd.ffdc.minute = (rem / IPS_SECS_MIN);
+ scb->cmd.ffdc.second = (rem % IPS_SECS_MIN);
+
+ year = IPS_EPOCH_YEAR;
+ while (days < 0 || days >= year_lengths[yleap = IPS_IS_LEAP_YEAR(year)]) {
+ int newy;
+
+ newy = year + (days / IPS_DAYS_NORMAL_YEAR);
+ if (days < 0)
+ --newy;
+ days -= (newy - year) * IPS_DAYS_NORMAL_YEAR +
+ IPS_NUM_LEAP_YEARS_THROUGH(newy - 1) -
+ IPS_NUM_LEAP_YEARS_THROUGH(year - 1);
+ year = newy;
+ }
+
+ scb->cmd.ffdc.yearH = year / 100;
+ scb->cmd.ffdc.yearL = year % 100;
+
+ for (i = 0; days >= month_lengths[i][yleap]; ++i)
+ days -= month_lengths[i][yleap];
+
+ scb->cmd.ffdc.month = i + 1;
+ scb->cmd.ffdc.day = days + 1;
+}
+
+/****************************************************************************
+ * BIOS Flash Routines *
+ ****************************************************************************/
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_erase_bios */
+/* */
+/* Routine Description: */
+/* Erase the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_erase_bios(ips_ha_t * ha)
+{
+ int timeout;
+ uint8_t status = 0;
+
+ METHOD_TRACE("ips_erase_bios", 1);
+
+ status = 0;
+
+ /* Clear the status register */
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ outb(0x50, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* Erase Setup */
+ outb(0x20, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* Erase Confirm */
+ outb(0xD0, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* Erase Status */
+ outb(0x70, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ timeout = 80000; /* 80 seconds */
+
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ udelay(25); /* 25 us */
+ }
+
+ status = inb(ha->io_addr + IPS_REG_FLDP);
+
+ if (status & 0x80)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ /* check for timeout */
+ if (timeout <= 0) {
+ /* timeout */
+
+ /* try to suspend the erase */
+ outb(0xB0, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* wait for 10 seconds */
+ timeout = 10000;
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ udelay(25); /* 25 us */
+ }
+
+ status = inb(ha->io_addr + IPS_REG_FLDP);
+
+ if (status & 0xC0)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ return (1);
+ }
+
+ /* check for valid VPP */
+ if (status & 0x08)
+ /* VPP failure */
+ return (1);
+
+ /* check for succesful flash */
+ if (status & 0x30)
+ /* sequence error */
+ return (1);
+
+ /* Otherwise, we were successful */
+ /* clear status */
+ outb(0x50, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* enable reads */
+ outb(0xFF, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_erase_bios_memio */
+/* */
+/* Routine Description: */
+/* Erase the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_erase_bios_memio(ips_ha_t * ha)
+{
+ int timeout;
+ uint8_t status;
+
+ METHOD_TRACE("ips_erase_bios_memio", 1);
+
+ status = 0;
+
+ /* Clear the status register */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* Erase Setup */
+ writeb(0x20, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* Erase Confirm */
+ writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* Erase Status */
+ writeb(0x70, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ timeout = 80000; /* 80 seconds */
+
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ udelay(25); /* 25 us */
+ }
+
+ status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ if (status & 0x80)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ /* check for timeout */
+ if (timeout <= 0) {
+ /* timeout */
+
+ /* try to suspend the erase */
+ writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* wait for 10 seconds */
+ timeout = 10000;
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ udelay(25); /* 25 us */
+ }
+
+ status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ if (status & 0xC0)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ return (1);
+ }
+
+ /* check for valid VPP */
+ if (status & 0x08)
+ /* VPP failure */
+ return (1);
+
+ /* check for succesful flash */
+ if (status & 0x30)
+ /* sequence error */
+ return (1);
+
+ /* Otherwise, we were successful */
+ /* clear status */
+ writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* enable reads */
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_program_bios */
+/* */
+/* Routine Description: */
+/* Program the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_program_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize,
+ uint32_t offset)
+{
+ int i;
+ int timeout;
+ uint8_t status = 0;
+
+ METHOD_TRACE("ips_program_bios", 1);
+
+ status = 0;
+
+ for (i = 0; i < buffersize; i++) {
+ /* write a byte */
+ outl(cpu_to_le32(i + offset), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ outb(0x40, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ outb(buffer[i], ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* wait up to one second */
+ timeout = 1000;
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ udelay(25); /* 25 us */
+ }
+
+ status = inb(ha->io_addr + IPS_REG_FLDP);
+
+ if (status & 0x80)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ /* timeout error */
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ outb(0xFF, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (1);
+ }
+
+ /* check the status */
+ if (status & 0x18) {
+ /* programming error */
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ outb(0xFF, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (1);
+ }
+ } /* end for */
+
+ /* Enable reading */
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ outb(0xFF, ha->io_addr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_program_bios_memio */
+/* */
+/* Routine Description: */
+/* Program the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_program_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize,
+ uint32_t offset)
+{
+ int i;
+ int timeout;
+ uint8_t status = 0;
+
+ METHOD_TRACE("ips_program_bios_memio", 1);
+
+ status = 0;
+
+ for (i = 0; i < buffersize; i++) {
+ /* write a byte */
+ writel(i + offset, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ writeb(0x40, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ /* wait up to one second */
+ timeout = 1000;
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ udelay(25); /* 25 us */
+ }
+
+ status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ if (status & 0x80)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ /* timeout error */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (1);
+ }
+
+ /* check the status */
+ if (status & 0x18) {
+ /* programming error */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (1);
+ }
+ } /* end for */
+
+ /* Enable reading */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_verify_bios */
+/* */
+/* Routine Description: */
+/* Verify the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_verify_bios(ips_ha_t * ha, char *buffer, uint32_t buffersize,
+ uint32_t offset)
+{
+ uint8_t checksum;
+ int i;
+
+ METHOD_TRACE("ips_verify_bios", 1);
+
+ /* test 1st byte */
+ outl(0, ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ if (inb(ha->io_addr + IPS_REG_FLDP) != 0x55)
+ return (1);
+
+ outl(cpu_to_le32(1), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+ if (inb(ha->io_addr + IPS_REG_FLDP) != 0xAA)
+ return (1);
+
+ checksum = 0xff;
+ for (i = 2; i < buffersize; i++) {
+
+ outl(cpu_to_le32(i + offset), ha->io_addr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ checksum = (uint8_t) checksum + inb(ha->io_addr + IPS_REG_FLDP);
+ }
+
+ if (checksum != 0)
+ /* failure */
+ return (1);
+ else
+ /* success */
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_verify_bios_memio */
+/* */
+/* Routine Description: */
+/* Verify the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_verify_bios_memio(ips_ha_t * ha, char *buffer, uint32_t buffersize,
+ uint32_t offset)
+{
+ uint8_t checksum;
+ int i;
+
+ METHOD_TRACE("ips_verify_bios_memio", 1);
+
+ /* test 1st byte */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
+ return (1);
+
+ writel(1, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+ if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
+ return (1);
+
+ checksum = 0xff;
+ for (i = 2; i < buffersize; i++) {
+
+ writel(i + offset, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ udelay(25); /* 25 us */
+
+ checksum =
+ (uint8_t) checksum + readb(ha->mem_ptr + IPS_REG_FLDP);
+ }
+
+ if (checksum != 0)
+ /* failure */
+ return (1);
+ else
+ /* success */
+ return (0);
+}
+
+/*---------------------------------------------------------------------------*/
+/* Routine Name: ips_version_check */
+/* */
+/* Dependencies: */
+/* Assumes that ips_read_adapter_status() is called first filling in */
+/* the data for SubSystem Parameters. */
+/* Called from ips_write_driver_status() so it also assumes NVRAM Page 5 */
+/* Data is available. */
+/* */
+/*---------------------------------------------------------------------------*/
+static void
+ips_version_check(ips_ha_t * ha, int intr)
+{
+ IPS_VERSION_DATA *VersionInfo;
+ uint8_t FirmwareVersion[IPS_COMPAT_ID_LENGTH + 1];
+ uint8_t BiosVersion[IPS_COMPAT_ID_LENGTH + 1];
+ int MatchError;
+ int rc;
+ char BiosString[10];
+ char FirmwareString[10];
+
+ METHOD_TRACE("ips_version_check", 1);
+
+ VersionInfo = ( IPS_VERSION_DATA * ) ha->ioctl_data;
+
+ memset(FirmwareVersion, 0, IPS_COMPAT_ID_LENGTH + 1);
+ memset(BiosVersion, 0, IPS_COMPAT_ID_LENGTH + 1);
+
+ /* Get the Compatible BIOS Version from NVRAM Page 5 */
+ memcpy(BiosVersion, ha->nvram->BiosCompatibilityID,
+ IPS_COMPAT_ID_LENGTH);
+
+ rc = IPS_FAILURE;
+ if (ha->subsys->param[4] & IPS_GET_VERSION_SUPPORT) { /* If Versioning is Supported */
+ /* Get the Version Info with a Get Version Command */
+ memset( VersionInfo, 0, sizeof (IPS_VERSION_DATA));
+ rc = ips_get_version_info(ha, ha->ioctl_busaddr, intr);
+ if (rc == IPS_SUCCESS)
+ memcpy(FirmwareVersion, VersionInfo->compatibilityId,
+ IPS_COMPAT_ID_LENGTH);
+ }
+
+ if (rc != IPS_SUCCESS) { /* If Data Not Obtainable from a GetVersion Command */
+ /* Get the Firmware Version from Enquiry Data */
+ memcpy(FirmwareVersion, ha->enq->CodeBlkVersion,
+ IPS_COMPAT_ID_LENGTH);
+ }
+
+ /* printk(KERN_WARNING "Adapter's BIOS Version = %s\n", BiosVersion); */
+ /* printk(KERN_WARNING "BIOS Compatible Version = %s\n", IPS_COMPAT_BIOS); */
+ /* printk(KERN_WARNING "Adapter's Firmware Version = %s\n", FirmwareVersion); */
+ /* printk(KERN_WARNING "Firmware Compatible Version = %s \n", Compatable[ ha->nvram->adapter_type ]); */
+
+ MatchError = 0;
+
+ if (strncmp
+ (FirmwareVersion, Compatable[ha->nvram->adapter_type],
+ IPS_COMPAT_ID_LENGTH) != 0)
+ MatchError = 1;
+
+ if (strncmp(BiosVersion, IPS_COMPAT_BIOS, IPS_COMPAT_ID_LENGTH) != 0)
+ MatchError = 1;
+
+ ha->nvram->versioning = 1; /* Indicate the Driver Supports Versioning */
+
+ if (MatchError) {
+ ha->nvram->version_mismatch = 1;
+ if (ips_cd_boot == 0) {
+ strncpy(&BiosString[0], ha->nvram->bios_high, 4);
+ strncpy(&BiosString[4], ha->nvram->bios_low, 4);
+ BiosString[8] = 0;
+
+ strncpy(&FirmwareString[0], ha->enq->CodeBlkVersion, 8);
+ FirmwareString[8] = 0;
+
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Warning ! ! ! ServeRAID Version Mismatch\n");
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Bios = %s, Firmware = %s, Device Driver = %s%s\n",
+ BiosString, FirmwareString, IPS_VERSION_HIGH,
+ IPS_VERSION_LOW);
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "These levels should match to avoid possible compatibility problems.\n");
+ }
+ } else {
+ ha->nvram->version_mismatch = 0;
+ }
+
+ return;
+}
+
+/*---------------------------------------------------------------------------*/
+/* Routine Name: ips_get_version_info */
+/* */
+/* Routine Description: */
+/* Issue an internal GETVERSION Command */
+/* */
+/* Return Value: */
+/* 0 if Successful, else non-zero */
+/*---------------------------------------------------------------------------*/
+static int
+ips_get_version_info(ips_ha_t * ha, dma_addr_t Buffer, int intr)
+{
+ ips_scb_t *scb;
+ int rc;
+
+ METHOD_TRACE("ips_get_version_info", 1);
+
+ scb = &ha->scbs[ha->max_cmds - 1];
+
+ ips_init_scb(ha, scb);
+
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_GET_VERSION_INFO;
+ scb->cmd.version_info.op_code = IPS_CMD_GET_VERSION_INFO;
+ scb->cmd.version_info.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.version_info.reserved = 0;
+ scb->cmd.version_info.count = sizeof (IPS_VERSION_DATA);
+ scb->cmd.version_info.reserved2 = 0;
+ scb->data_len = sizeof (IPS_VERSION_DATA);
+ scb->data_busaddr = Buffer;
+ scb->cmd.version_info.buffer_addr = Buffer;
+ scb->flags = 0;
+
+ /* issue command */
+ rc = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
+ return (rc);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_abort_init */
+/* */
+/* Routine Description: */
+/* cleanup routine for a failed adapter initialization */
+/****************************************************************************/
+static int
+ips_abort_init(ips_ha_t * ha, int index)
+{
+ ha->active = 0;
+ ips_free(ha);
+ ips_ha[index] = NULL;
+ ips_sh[index] = NULL;
+ return -1;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_shift_controllers */
+/* */
+/* Routine Description: */
+/* helper function for ordering adapters */
+/****************************************************************************/
+static void
+ips_shift_controllers(int lowindex, int highindex)
+{
+ ips_ha_t *ha_sav = ips_ha[highindex];
+ struct Scsi_Host *sh_sav = ips_sh[highindex];
+ int i;
+
+ for (i = highindex; i > lowindex; i--) {
+ ips_ha[i] = ips_ha[i - 1];
+ ips_sh[i] = ips_sh[i - 1];
+ ips_ha[i]->host_num = i;
+ }
+ ha_sav->host_num = lowindex;
+ ips_ha[lowindex] = ha_sav;
+ ips_sh[lowindex] = sh_sav;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_order_controllers */
+/* */
+/* Routine Description: */
+/* place controllers is the "proper" boot order */
+/****************************************************************************/
+static void
+ips_order_controllers(void)
+{
+ int i, j, tmp, position = 0;
+ IPS_NVRAM_P5 *nvram;
+ if (!ips_ha[0])
+ return;
+ nvram = ips_ha[0]->nvram;
+
+ if (nvram->adapter_order[0]) {
+ for (i = 1; i <= nvram->adapter_order[0]; i++) {
+ for (j = position; j < ips_num_controllers; j++) {
+ switch (ips_ha[j]->ad_type) {
+ case IPS_ADTYPE_SERVERAID6M:
+ case IPS_ADTYPE_SERVERAID7M:
+ if (nvram->adapter_order[i] == 'M') {
+ ips_shift_controllers(position,
+ j);
+ position++;
+ }
+ break;
+ case IPS_ADTYPE_SERVERAID4L:
+ case IPS_ADTYPE_SERVERAID4M:
+ case IPS_ADTYPE_SERVERAID4MX:
+ case IPS_ADTYPE_SERVERAID4LX:
+ if (nvram->adapter_order[i] == 'N') {
+ ips_shift_controllers(position,
+ j);
+ position++;
+ }
+ break;
+ case IPS_ADTYPE_SERVERAID6I:
+ case IPS_ADTYPE_SERVERAID5I2:
+ case IPS_ADTYPE_SERVERAID5I1:
+ case IPS_ADTYPE_SERVERAID7k:
+ if (nvram->adapter_order[i] == 'S') {
+ ips_shift_controllers(position,
+ j);
+ position++;
+ }
+ break;
+ case IPS_ADTYPE_SERVERAID:
+ case IPS_ADTYPE_SERVERAID2:
+ case IPS_ADTYPE_NAVAJO:
+ case IPS_ADTYPE_KIOWA:
+ case IPS_ADTYPE_SERVERAID3L:
+ case IPS_ADTYPE_SERVERAID3:
+ case IPS_ADTYPE_SERVERAID4H:
+ if (nvram->adapter_order[i] == 'A') {
+ ips_shift_controllers(position,
+ j);
+ position++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ /* if adapter_order[0], then ordering is complete */
+ return;
+ }
+ /* old bios, use older ordering */
+ tmp = 0;
+ for (i = position; i < ips_num_controllers; i++) {
+ if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I2 ||
+ ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID5I1) {
+ ips_shift_controllers(position, i);
+ position++;
+ tmp = 1;
+ }
+ }
+ /* if there were no 5I cards, then don't do any extra ordering */
+ if (!tmp)
+ return;
+ for (i = position; i < ips_num_controllers; i++) {
+ if (ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4L ||
+ ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4M ||
+ ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4LX ||
+ ips_ha[i]->ad_type == IPS_ADTYPE_SERVERAID4MX) {
+ ips_shift_controllers(position, i);
+ position++;
+ }
+ }
+
+ return;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_register_scsi */
+/* */
+/* Routine Description: */
+/* perform any registration and setup with the scsi layer */
+/****************************************************************************/
+static int
+ips_register_scsi(int index)
+{
+ struct Scsi_Host *sh;
+ ips_ha_t *ha, *oldha = ips_ha[index];
+ sh = scsi_host_alloc(&ips_driver_template, sizeof (ips_ha_t));
+ if (!sh) {
+ IPS_PRINTK(KERN_WARNING, oldha->pcidev,
+ "Unable to register controller with SCSI subsystem\n");
+ return -1;
+ }
+ ha = IPS_HA(sh);
+ memcpy(ha, oldha, sizeof (ips_ha_t));
+ free_irq(oldha->irq, oldha);
+ /* Install the interrupt handler with the new ha */
+ if (request_irq(ha->irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Unable to install interrupt handler\n");
+ scsi_host_put(sh);
+ return -1;
+ }
+
+ kfree(oldha);
+ ips_sh[index] = sh;
+ ips_ha[index] = ha;
+ IPS_SCSI_SET_DEVICE(sh, ha);
+
+ /* Store away needed values for later use */
+ sh->io_port = ha->io_addr;
+ sh->n_io_port = ha->io_addr ? 255 : 0;
+ sh->unique_id = (ha->io_addr) ? ha->io_addr : ha->mem_addr;
+ sh->irq = ha->irq;
+ sh->sg_tablesize = sh->hostt->sg_tablesize;
+ sh->can_queue = sh->hostt->can_queue;
+ sh->cmd_per_lun = sh->hostt->cmd_per_lun;
+ sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
+ sh->use_clustering = sh->hostt->use_clustering;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
+ sh->max_sectors = 128;
+#endif
+
+ sh->max_id = ha->ntargets;
+ sh->max_lun = ha->nlun;
+ sh->max_channel = ha->nbus - 1;
+ sh->can_queue = ha->max_cmds - 1;
+
+ IPS_ADD_HOST(sh, NULL);
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+/* Routine Name: ips_remove_device */
+/* */
+/* Routine Description: */
+/* Remove one Adapter ( Hot Plugging ) */
+/*---------------------------------------------------------------------------*/
+static void __devexit
+ips_remove_device(struct pci_dev *pci_dev)
+{
+ int i;
+ struct Scsi_Host *sh;
+ ips_ha_t *ha;
+
+ for (i = 0; i < IPS_MAX_ADAPTERS; i++) {
+ ha = ips_ha[i];
+ if (ha) {
+ if ((pci_dev->bus->number == ha->pcidev->bus->number) &&
+ (pci_dev->devfn == ha->pcidev->devfn)) {
+ sh = ips_sh[i];
+ ips_release(sh);
+ }
+ }
+ }
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_module_init */
+/* */
+/* Routine Description: */
+/* function called on module load */
+/****************************************************************************/
+static int __init
+ips_module_init(void)
+{
+ if (pci_module_init(&ips_pci_driver) < 0)
+ return -ENODEV;
+ ips_driver_template.module = THIS_MODULE;
+ ips_order_controllers();
+ if (IPS_REGISTER_HOSTS(&ips_driver_template)) {
+ pci_unregister_driver(&ips_pci_driver);
+ return -ENODEV;
+ }
+ register_reboot_notifier(&ips_notifier);
+ return 0;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_module_exit */
+/* */
+/* Routine Description: */
+/* function called on module unload */
+/****************************************************************************/
+static void __exit
+ips_module_exit(void)
+{
+ IPS_UNREGISTER_HOSTS(&ips_driver_template);
+ pci_unregister_driver(&ips_pci_driver);
+ unregister_reboot_notifier(&ips_notifier);
+}
+
+module_init(ips_module_init);
+module_exit(ips_module_exit);
+
+/*---------------------------------------------------------------------------*/
+/* Routine Name: ips_insert_device */
+/* */
+/* Routine Description: */
+/* Add One Adapter ( Hot Plug ) */
+/* */
+/* Return Value: */
+/* 0 if Successful, else non-zero */
+/*---------------------------------------------------------------------------*/
+static int __devinit
+ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent)
+{
+ int index;
+ int rc;
+
+ METHOD_TRACE("ips_insert_device", 1);
+ if (pci_enable_device(pci_dev))
+ return -1;
+
+ rc = ips_init_phase1(pci_dev, &index);
+ if (rc == SUCCESS)
+ rc = ips_init_phase2(index);
+
+ if (ips_hotplug)
+ if (ips_register_scsi(index)) {
+ ips_free(ips_ha[index]);
+ rc = -1;
+ }
+
+ if (rc == SUCCESS)
+ ips_num_controllers++;
+
+ ips_next_controller = ips_num_controllers;
+ return rc;
+}
+
+/*---------------------------------------------------------------------------*/
+/* Routine Name: ips_init_phase1 */
+/* */
+/* Routine Description: */
+/* Adapter Initialization */
+/* */
+/* Return Value: */
+/* 0 if Successful, else non-zero */
+/*---------------------------------------------------------------------------*/
+static int
+ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
+{
+ ips_ha_t *ha;
+ uint32_t io_addr;
+ uint32_t mem_addr;
+ uint32_t io_len;
+ uint32_t mem_len;
+ uint8_t revision_id;
+ uint8_t bus;
+ uint8_t func;
+ uint8_t irq;
+ uint16_t subdevice_id;
+ int j;
+ int index;
+ dma_addr_t dma_address;
+ char __iomem *ioremap_ptr;
+ char __iomem *mem_ptr;
+ uint32_t IsDead;
+
+ METHOD_TRACE("ips_init_phase1", 1);
+ index = IPS_MAX_ADAPTERS;
+ for (j = 0; j < IPS_MAX_ADAPTERS; j++) {
+ if (ips_ha[j] == 0) {
+ index = j;
+ break;
+ }
+ }
+
+ if (index >= IPS_MAX_ADAPTERS)
+ return -1;
+
+ /* stuff that we get in dev */
+ irq = pci_dev->irq;
+ bus = pci_dev->bus->number;
+ func = pci_dev->devfn;
+
+ /* Init MEM/IO addresses to 0 */
+ mem_addr = 0;
+ io_addr = 0;
+ mem_len = 0;
+ io_len = 0;
+
+ for (j = 0; j < 2; j++) {
+ if (!pci_resource_start(pci_dev, j))
+ break;
+
+ if (pci_resource_flags(pci_dev, j) & IORESOURCE_IO) {
+ io_addr = pci_resource_start(pci_dev, j);
+ io_len = pci_resource_len(pci_dev, j);
+ } else {
+ mem_addr = pci_resource_start(pci_dev, j);
+ mem_len = pci_resource_len(pci_dev, j);
+ }
+ }
+
+ /* setup memory mapped area (if applicable) */
+ if (mem_addr) {
+ uint32_t base;
+ uint32_t offs;
+
+ if (!request_mem_region(mem_addr, mem_len, "ips")) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Couldn't allocate IO Memory space %x len %d.\n",
+ mem_addr, mem_len);
+ return -1;
+ }
+
+ base = mem_addr & PAGE_MASK;
+ offs = mem_addr - base;
+ ioremap_ptr = ioremap(base, PAGE_SIZE);
+ mem_ptr = ioremap_ptr + offs;
+ } else {
+ ioremap_ptr = NULL;
+ mem_ptr = NULL;
+ }
+
+ /* setup I/O mapped area (if applicable) */
+ if (io_addr) {
+ if (!request_region(io_addr, io_len, "ips")) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Couldn't allocate IO space %x len %d.\n",
+ io_addr, io_len);
+ return -1;
+ }
+ }
+
+ /* get the revision ID */
+ if (pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id)) {
+ IPS_PRINTK(KERN_WARNING, pci_dev, "Can't get revision id.\n");
+ return -1;
+ }
+
+ subdevice_id = pci_dev->subsystem_device;
+
+ /* found a controller */
+ ha = kmalloc(sizeof (ips_ha_t), GFP_KERNEL);
+ if (ha == NULL) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate temporary ha struct\n");
+ return -1;
+ }
+
+ memset(ha, 0, sizeof (ips_ha_t));
+
+ ips_sh[index] = NULL;
+ ips_ha[index] = ha;
+ ha->active = 1;
+
+ /* Store info in HA structure */
+ ha->irq = irq;
+ ha->io_addr = io_addr;
+ ha->io_len = io_len;
+ ha->mem_addr = mem_addr;
+ ha->mem_len = mem_len;
+ ha->mem_ptr = mem_ptr;
+ ha->ioremap_ptr = ioremap_ptr;
+ ha->host_num = (uint32_t) index;
+ ha->revision_id = revision_id;
+ ha->slot_num = PCI_SLOT(pci_dev->devfn);
+ ha->device_id = pci_dev->device;
+ ha->subdevice_id = subdevice_id;
+ ha->pcidev = pci_dev;
+
+ /*
+ * Set the pci_dev's dma_mask. Not all adapters support 64bit
+ * addressing so don't enable it if the adapter can't support
+ * it! Also, don't use 64bit addressing if dma addresses
+ * are guaranteed to be < 4G.
+ */
+ if (IPS_ENABLE_DMA64 && IPS_HAS_ENH_SGLIST(ha) &&
+ !pci_set_dma_mask(ha->pcidev, 0xffffffffffffffffULL)) {
+ (ha)->flags |= IPS_HA_ENH_SG;
+ } else {
+ if (pci_set_dma_mask(ha->pcidev, 0xffffffffULL) != 0) {
+ printk(KERN_WARNING "Unable to set DMA Mask\n");
+ return ips_abort_init(ha, index);
+ }
+ }
+ if(ips_cd_boot && !ips_FlashData){
+ ips_FlashData = pci_alloc_consistent(pci_dev, PAGE_SIZE << 7,
+ &ips_flashbusaddr);
+ }
+
+ ha->enq = pci_alloc_consistent(pci_dev, sizeof (IPS_ENQ),
+ &ha->enq_busaddr);
+ if (!ha->enq) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate host inquiry structure\n");
+ return ips_abort_init(ha, index);
+ }
+
+ ha->adapt = pci_alloc_consistent(pci_dev, sizeof (IPS_ADAPTER) +
+ sizeof (IPS_IO_CMD), &dma_address);
+ if (!ha->adapt) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate host adapt & dummy structures\n");
+ return ips_abort_init(ha, index);
+ }
+ ha->adapt->hw_status_start = dma_address;
+ ha->dummy = (void *) (ha->adapt + 1);
+
+
+
+ ha->logical_drive_info = pci_alloc_consistent(pci_dev, sizeof (IPS_LD_INFO), &dma_address);
+ if (!ha->logical_drive_info) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate logical drive info structure\n");
+ return ips_abort_init(ha, index);
+ }
+ ha->logical_drive_info_dma_addr = dma_address;
+
+
+ ha->conf = kmalloc(sizeof (IPS_CONF), GFP_KERNEL);
+
+ if (!ha->conf) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate host conf structure\n");
+ return ips_abort_init(ha, index);
+ }
+
+ ha->nvram = kmalloc(sizeof (IPS_NVRAM_P5), GFP_KERNEL);
+
+ if (!ha->nvram) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate host NVRAM structure\n");
+ return ips_abort_init(ha, index);
+ }
+
+ ha->subsys = kmalloc(sizeof (IPS_SUBSYS), GFP_KERNEL);
+
+ if (!ha->subsys) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate host subsystem structure\n");
+ return ips_abort_init(ha, index);
+ }
+
+ /* the ioctl buffer is now used during adapter initialization, so its
+ * successful allocation is now required */
+ if (ips_ioctlsize < PAGE_SIZE)
+ ips_ioctlsize = PAGE_SIZE;
+
+ ha->ioctl_data = pci_alloc_consistent(pci_dev, ips_ioctlsize,
+ &ha->ioctl_busaddr);
+ ha->ioctl_len = ips_ioctlsize;
+ if (!ha->ioctl_data) {
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to allocate IOCTL data\n");
+ return ips_abort_init(ha, index);
+ }
+
+ /*
+ * Setup Functions
+ */
+ ips_setup_funclist(ha);
+
+ if ((IPS_IS_MORPHEUS(ha)) || (IPS_IS_MARCO(ha))) {
+ /* If Morpheus appears dead, reset it */
+ IsDead = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
+ if (IsDead == 0xDEADBEEF) {
+ ips_reset_morpheus(ha);
+ }
+ }
+
+ /*
+ * Initialize the card if it isn't already
+ */
+
+ if (!(*ha->func.isinit) (ha)) {
+ if (!(*ha->func.init) (ha)) {
+ /*
+ * Initialization failed
+ */
+ IPS_PRINTK(KERN_WARNING, pci_dev,
+ "Unable to initialize controller\n");
+ return ips_abort_init(ha, index);
+ }
+ }
+
+ *indexPtr = index;
+ return SUCCESS;
+}
+
+/*---------------------------------------------------------------------------*/
+/* Routine Name: ips_init_phase2 */
+/* */
+/* Routine Description: */
+/* Adapter Initialization Phase 2 */
+/* */
+/* Return Value: */
+/* 0 if Successful, else non-zero */
+/*---------------------------------------------------------------------------*/
+static int
+ips_init_phase2(int index)
+{
+ ips_ha_t *ha;
+
+ ha = ips_ha[index];
+
+ METHOD_TRACE("ips_init_phase2", 1);
+ if (!ha->active) {
+ ips_ha[index] = NULL;
+ return -1;
+ }
+
+ /* Install the interrupt handler */
+ if (request_irq(ha->irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Unable to install interrupt handler\n");
+ return ips_abort_init(ha, index);
+ }
+
+ /*
+ * Allocate a temporary SCB for initialization
+ */
+ ha->max_cmds = 1;
+ if (!ips_allocatescbs(ha)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Unable to allocate a CCB\n");
+ free_irq(ha->irq, ha);
+ return ips_abort_init(ha, index);
+ }
+
+ if (!ips_hainit(ha)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Unable to initialize controller\n");
+ free_irq(ha->irq, ha);
+ return ips_abort_init(ha, index);
+ }
+ /* Free the temporary SCB */
+ ips_deallocatescbs(ha, 1);
+
+ /* allocate CCBs */
+ if (!ips_allocatescbs(ha)) {
+ IPS_PRINTK(KERN_WARNING, ha->pcidev,
+ "Unable to allocate CCBs\n");
+ free_irq(ha->irq, ha);
+ return ips_abort_init(ha, index);
+ }
+
+ return SUCCESS;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
+MODULE_LICENSE("GPL");
+#endif
+
+MODULE_DESCRIPTION("IBM ServeRAID Adapter Driver " IPS_VER_STRING);
+
+#ifdef MODULE_VERSION
+MODULE_VERSION(IPS_VER_STRING);
+#endif
+
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h
new file mode 100644
index 000000000000..906a76158fa9
--- /dev/null
+++ b/drivers/scsi/ips.h
@@ -0,0 +1,1297 @@
+/*****************************************************************************/
+/* ips.h -- driver for the Adaptec / IBM ServeRAID controller */
+/* */
+/* Written By: Keith Mitchell, IBM Corporation */
+/* Jack Hammer, Adaptec, Inc. */
+/* David Jeffery, Adaptec, Inc. */
+/* */
+/* Copyright (C) 1999 IBM Corporation */
+/* Copyright (C) 2003 Adaptec, Inc. */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* NO WARRANTY */
+/* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR */
+/* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT */
+/* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, */
+/* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is */
+/* solely responsible for determining the appropriateness of using and */
+/* distributing the Program and assumes all risks associated with its */
+/* exercise of rights under this Agreement, including but not limited to */
+/* the risks and costs of program errors, damage to or loss of data, */
+/* programs or equipment, and unavailability or interruption of operations. */
+/* */
+/* DISCLAIMER OF LIABILITY */
+/* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY */
+/* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL */
+/* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND */
+/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR */
+/* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE */
+/* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED */
+/* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* */
+/* Bugs/Comments/Suggestions should be mailed to: */
+/* ipslinux@adaptec.com */
+/* */
+/*****************************************************************************/
+
+#ifndef _IPS_H_
+ #define _IPS_H_
+
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+
+ /*
+ * Some handy macros
+ */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) || defined CONFIG_HIGHIO
+ #define IPS_HIGHIO
+ #endif
+
+ #define IPS_HA(x) ((ips_ha_t *) x->hostdata)
+ #define IPS_COMMAND_ID(ha, scb) (int) (scb - ha->scbs)
+ #define IPS_IS_TROMBONE(ha) (((ha->device_id == IPS_DEVICEID_COPPERHEAD) && \
+ (ha->revision_id >= IPS_REVID_TROMBONE32) && \
+ (ha->revision_id <= IPS_REVID_TROMBONE64)) ? 1 : 0)
+ #define IPS_IS_CLARINET(ha) (((ha->device_id == IPS_DEVICEID_COPPERHEAD) && \
+ (ha->revision_id >= IPS_REVID_CLARINETP1) && \
+ (ha->revision_id <= IPS_REVID_CLARINETP3)) ? 1 : 0)
+ #define IPS_IS_MORPHEUS(ha) (ha->device_id == IPS_DEVICEID_MORPHEUS)
+ #define IPS_IS_MARCO(ha) (ha->device_id == IPS_DEVICEID_MARCO)
+ #define IPS_USE_I2O_DELIVER(ha) ((IPS_IS_MORPHEUS(ha) || \
+ (IPS_IS_TROMBONE(ha) && \
+ (ips_force_i2o))) ? 1 : 0)
+ #define IPS_USE_MEMIO(ha) ((IPS_IS_MORPHEUS(ha) || \
+ ((IPS_IS_TROMBONE(ha) || IPS_IS_CLARINET(ha)) && \
+ (ips_force_memio))) ? 1 : 0)
+
+ #define IPS_HAS_ENH_SGLIST(ha) (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha))
+ #define IPS_USE_ENH_SGLIST(ha) ((ha)->flags & IPS_HA_ENH_SG)
+ #define IPS_SGLIST_SIZE(ha) (IPS_USE_ENH_SGLIST(ha) ? \
+ sizeof(IPS_ENH_SG_LIST) : sizeof(IPS_STD_SG_LIST))
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+ #define pci_set_dma_mask(dev,mask) ( mask > 0xffffffff ? 1:0 )
+ #define scsi_set_pci_device(sh,dev) (0)
+ #endif
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+ #ifndef irqreturn_t
+ typedef void irqreturn_t;
+ #endif
+
+ #define IRQ_NONE
+ #define IRQ_HANDLED
+ #define IRQ_RETVAL(x)
+ #define IPS_REGISTER_HOSTS(SHT) scsi_register_module(MODULE_SCSI_HA,SHT)
+ #define IPS_UNREGISTER_HOSTS(SHT) scsi_unregister_module(MODULE_SCSI_HA,SHT)
+ #define IPS_ADD_HOST(shost,device)
+ #define IPS_REMOVE_HOST(shost)
+ #define IPS_SCSI_SET_DEVICE(sh,ha) scsi_set_pci_device(sh, (ha)->pcidev)
+ #define IPS_PRINTK(level, pcidev, format, arg...) \
+ printk(level "%s %s:" format , "ips" , \
+ (pcidev)->slot_name , ## arg)
+ #define scsi_host_alloc(sh,size) scsi_register(sh,size)
+ #define scsi_host_put(sh) scsi_unregister(sh)
+ #else
+ #define IPS_REGISTER_HOSTS(SHT) (!ips_detect(SHT))
+ #define IPS_UNREGISTER_HOSTS(SHT)
+ #define IPS_ADD_HOST(shost,device) do { scsi_add_host(shost,device); scsi_scan_host(shost); } while (0)
+ #define IPS_REMOVE_HOST(shost) scsi_remove_host(shost)
+ #define IPS_SCSI_SET_DEVICE(sh,ha) scsi_set_device(sh, &(ha)->pcidev->dev)
+ #define IPS_PRINTK(level, pcidev, format, arg...) \
+ dev_printk(level , &((pcidev)->dev) , format , ## arg)
+ #endif
+
+ #ifndef MDELAY
+ #define MDELAY mdelay
+ #endif
+
+ #ifndef min
+ #define min(x,y) ((x) < (y) ? x : y)
+ #endif
+
+ #define pci_dma_hi32(a) ((a >> 16) >> 16)
+ #define pci_dma_lo32(a) (a & 0xffffffff)
+
+ #if (BITS_PER_LONG > 32) || (defined CONFIG_HIGHMEM64G && defined IPS_HIGHIO)
+ #define IPS_ENABLE_DMA64 (1)
+ #else
+ #define IPS_ENABLE_DMA64 (0)
+ #endif
+
+ /*
+ * Adapter address map equates
+ */
+ #define IPS_REG_HISR 0x08 /* Host Interrupt Status Reg */
+ #define IPS_REG_CCSAR 0x10 /* Cmd Channel System Addr Reg */
+ #define IPS_REG_CCCR 0x14 /* Cmd Channel Control Reg */
+ #define IPS_REG_SQHR 0x20 /* Status Q Head Reg */
+ #define IPS_REG_SQTR 0x24 /* Status Q Tail Reg */
+ #define IPS_REG_SQER 0x28 /* Status Q End Reg */
+ #define IPS_REG_SQSR 0x2C /* Status Q Start Reg */
+ #define IPS_REG_SCPR 0x05 /* Subsystem control port reg */
+ #define IPS_REG_ISPR 0x06 /* interrupt status port reg */
+ #define IPS_REG_CBSP 0x07 /* CBSP register */
+ #define IPS_REG_FLAP 0x18 /* Flash address port */
+ #define IPS_REG_FLDP 0x1C /* Flash data port */
+ #define IPS_REG_NDAE 0x38 /* Anaconda 64 NDAE Register */
+ #define IPS_REG_I2O_INMSGQ 0x40 /* I2O Inbound Message Queue */
+ #define IPS_REG_I2O_OUTMSGQ 0x44 /* I2O Outbound Message Queue */
+ #define IPS_REG_I2O_HIR 0x30 /* I2O Interrupt Status */
+ #define IPS_REG_I960_IDR 0x20 /* i960 Inbound Doorbell */
+ #define IPS_REG_I960_MSG0 0x18 /* i960 Outbound Reg 0 */
+ #define IPS_REG_I960_MSG1 0x1C /* i960 Outbound Reg 1 */
+ #define IPS_REG_I960_OIMR 0x34 /* i960 Oubound Int Mask Reg */
+
+ /*
+ * Adapter register bit equates
+ */
+ #define IPS_BIT_GHI 0x04 /* HISR General Host Interrupt */
+ #define IPS_BIT_SQO 0x02 /* HISR Status Q Overflow */
+ #define IPS_BIT_SCE 0x01 /* HISR Status Channel Enqueue */
+ #define IPS_BIT_SEM 0x08 /* CCCR Semaphore Bit */
+ #define IPS_BIT_ILE 0x10 /* CCCR ILE Bit */
+ #define IPS_BIT_START_CMD 0x101A /* CCCR Start Command Channel */
+ #define IPS_BIT_START_STOP 0x0002 /* CCCR Start/Stop Bit */
+ #define IPS_BIT_RST 0x80 /* SCPR Reset Bit */
+ #define IPS_BIT_EBM 0x02 /* SCPR Enable Bus Master */
+ #define IPS_BIT_EI 0x80 /* HISR Enable Interrupts */
+ #define IPS_BIT_OP 0x01 /* OP bit in CBSP */
+ #define IPS_BIT_I2O_OPQI 0x08 /* General Host Interrupt */
+ #define IPS_BIT_I960_MSG0I 0x01 /* Message Register 0 Interrupt*/
+ #define IPS_BIT_I960_MSG1I 0x02 /* Message Register 1 Interrupt*/
+
+ /*
+ * Adapter Command ID Equates
+ */
+ #define IPS_CMD_GET_LD_INFO 0x19
+ #define IPS_CMD_GET_SUBSYS 0x40
+ #define IPS_CMD_READ_CONF 0x38
+ #define IPS_CMD_RW_NVRAM_PAGE 0xBC
+ #define IPS_CMD_READ 0x02
+ #define IPS_CMD_WRITE 0x03
+ #define IPS_CMD_FFDC 0xD7
+ #define IPS_CMD_ENQUIRY 0x05
+ #define IPS_CMD_FLUSH 0x0A
+ #define IPS_CMD_READ_SG 0x82
+ #define IPS_CMD_WRITE_SG 0x83
+ #define IPS_CMD_DCDB 0x04
+ #define IPS_CMD_DCDB_SG 0x84
+ #define IPS_CMD_EXTENDED_DCDB 0x95
+ #define IPS_CMD_EXTENDED_DCDB_SG 0x96
+ #define IPS_CMD_CONFIG_SYNC 0x58
+ #define IPS_CMD_ERROR_TABLE 0x17
+ #define IPS_CMD_DOWNLOAD 0x20
+ #define IPS_CMD_RW_BIOSFW 0x22
+ #define IPS_CMD_GET_VERSION_INFO 0xC6
+ #define IPS_CMD_RESET_CHANNEL 0x1A
+
+ /*
+ * Adapter Equates
+ */
+ #define IPS_CSL 0xFF
+ #define IPS_POCL 0x30
+ #define IPS_NORM_STATE 0x00
+ #define IPS_MAX_ADAPTER_TYPES 3
+ #define IPS_MAX_ADAPTERS 16
+ #define IPS_MAX_IOCTL 1
+ #define IPS_MAX_IOCTL_QUEUE 8
+ #define IPS_MAX_QUEUE 128
+ #define IPS_BLKSIZE 512
+ #define IPS_MAX_SG 17
+ #define IPS_MAX_LD 8
+ #define IPS_MAX_CHANNELS 4
+ #define IPS_MAX_TARGETS 15
+ #define IPS_MAX_CHUNKS 16
+ #define IPS_MAX_CMDS 128
+ #define IPS_MAX_XFER 0x10000
+ #define IPS_NVRAM_P5_SIG 0xFFDDBB99
+ #define IPS_MAX_POST_BYTES 0x02
+ #define IPS_MAX_CONFIG_BYTES 0x02
+ #define IPS_GOOD_POST_STATUS 0x80
+ #define IPS_SEM_TIMEOUT 2000
+ #define IPS_IOCTL_COMMAND 0x0D
+ #define IPS_INTR_ON 0
+ #define IPS_INTR_IORL 1
+ #define IPS_FFDC 99
+ #define IPS_ADAPTER_ID 0xF
+ #define IPS_VENDORID_IBM 0x1014
+ #define IPS_VENDORID_ADAPTEC 0x9005
+ #define IPS_DEVICEID_COPPERHEAD 0x002E
+ #define IPS_DEVICEID_MORPHEUS 0x01BD
+ #define IPS_DEVICEID_MARCO 0x0250
+ #define IPS_SUBDEVICEID_4M 0x01BE
+ #define IPS_SUBDEVICEID_4L 0x01BF
+ #define IPS_SUBDEVICEID_4MX 0x0208
+ #define IPS_SUBDEVICEID_4LX 0x020E
+ #define IPS_SUBDEVICEID_5I2 0x0259
+ #define IPS_SUBDEVICEID_5I1 0x0258
+ #define IPS_SUBDEVICEID_6M 0x0279
+ #define IPS_SUBDEVICEID_6I 0x028C
+ #define IPS_SUBDEVICEID_7k 0x028E
+ #define IPS_SUBDEVICEID_7M 0x028F
+ #define IPS_IOCTL_SIZE 8192
+ #define IPS_STATUS_SIZE 4
+ #define IPS_STATUS_Q_SIZE (IPS_MAX_CMDS+1) * IPS_STATUS_SIZE
+ #define IPS_IMAGE_SIZE 500 * 1024
+ #define IPS_MEMMAP_SIZE 128
+ #define IPS_ONE_MSEC 1
+ #define IPS_ONE_SEC 1000
+
+ /*
+ * Geometry Settings
+ */
+ #define IPS_COMP_HEADS 128
+ #define IPS_COMP_SECTORS 32
+ #define IPS_NORM_HEADS 254
+ #define IPS_NORM_SECTORS 63
+
+ /*
+ * Adapter Basic Status Codes
+ */
+ #define IPS_BASIC_STATUS_MASK 0xFF
+ #define IPS_GSC_STATUS_MASK 0x0F
+ #define IPS_CMD_SUCCESS 0x00
+ #define IPS_CMD_RECOVERED_ERROR 0x01
+ #define IPS_INVAL_OPCO 0x03
+ #define IPS_INVAL_CMD_BLK 0x04
+ #define IPS_INVAL_PARM_BLK 0x05
+ #define IPS_BUSY 0x08
+ #define IPS_CMD_CMPLT_WERROR 0x0C
+ #define IPS_LD_ERROR 0x0D
+ #define IPS_CMD_TIMEOUT 0x0E
+ #define IPS_PHYS_DRV_ERROR 0x0F
+
+ /*
+ * Adapter Extended Status Equates
+ */
+ #define IPS_ERR_SEL_TO 0xF0
+ #define IPS_ERR_OU_RUN 0xF2
+ #define IPS_ERR_HOST_RESET 0xF7
+ #define IPS_ERR_DEV_RESET 0xF8
+ #define IPS_ERR_RECOVERY 0xFC
+ #define IPS_ERR_CKCOND 0xFF
+
+ /*
+ * Operating System Defines
+ */
+ #define IPS_OS_WINDOWS_NT 0x01
+ #define IPS_OS_NETWARE 0x02
+ #define IPS_OS_OPENSERVER 0x03
+ #define IPS_OS_UNIXWARE 0x04
+ #define IPS_OS_SOLARIS 0x05
+ #define IPS_OS_OS2 0x06
+ #define IPS_OS_LINUX 0x07
+ #define IPS_OS_FREEBSD 0x08
+
+ /*
+ * Adapter Revision ID's
+ */
+ #define IPS_REVID_SERVERAID 0x02
+ #define IPS_REVID_NAVAJO 0x03
+ #define IPS_REVID_SERVERAID2 0x04
+ #define IPS_REVID_CLARINETP1 0x05
+ #define IPS_REVID_CLARINETP2 0x07
+ #define IPS_REVID_CLARINETP3 0x0D
+ #define IPS_REVID_TROMBONE32 0x0F
+ #define IPS_REVID_TROMBONE64 0x10
+
+ /*
+ * NVRAM Page 5 Adapter Defines
+ */
+ #define IPS_ADTYPE_SERVERAID 0x01
+ #define IPS_ADTYPE_SERVERAID2 0x02
+ #define IPS_ADTYPE_NAVAJO 0x03
+ #define IPS_ADTYPE_KIOWA 0x04
+ #define IPS_ADTYPE_SERVERAID3 0x05
+ #define IPS_ADTYPE_SERVERAID3L 0x06
+ #define IPS_ADTYPE_SERVERAID4H 0x07
+ #define IPS_ADTYPE_SERVERAID4M 0x08
+ #define IPS_ADTYPE_SERVERAID4L 0x09
+ #define IPS_ADTYPE_SERVERAID4MX 0x0A
+ #define IPS_ADTYPE_SERVERAID4LX 0x0B
+ #define IPS_ADTYPE_SERVERAID5I2 0x0C
+ #define IPS_ADTYPE_SERVERAID5I1 0x0D
+ #define IPS_ADTYPE_SERVERAID6M 0x0E
+ #define IPS_ADTYPE_SERVERAID6I 0x0F
+ #define IPS_ADTYPE_SERVERAID7t 0x10
+ #define IPS_ADTYPE_SERVERAID7k 0x11
+ #define IPS_ADTYPE_SERVERAID7M 0x12
+
+ /*
+ * Adapter Command/Status Packet Definitions
+ */
+ #define IPS_SUCCESS 0x01 /* Successfully completed */
+ #define IPS_SUCCESS_IMM 0x02 /* Success - Immediately */
+ #define IPS_FAILURE 0x04 /* Completed with Error */
+
+ /*
+ * Logical Drive Equates
+ */
+ #define IPS_LD_OFFLINE 0x02
+ #define IPS_LD_OKAY 0x03
+ #define IPS_LD_FREE 0x00
+ #define IPS_LD_SYS 0x06
+ #define IPS_LD_CRS 0x24
+
+ /*
+ * DCDB Table Equates
+ */
+ #define IPS_NO_DISCONNECT 0x00
+ #define IPS_DISCONNECT_ALLOWED 0x80
+ #define IPS_NO_AUTO_REQSEN 0x40
+ #define IPS_DATA_NONE 0x00
+ #define IPS_DATA_UNK 0x00
+ #define IPS_DATA_IN 0x01
+ #define IPS_DATA_OUT 0x02
+ #define IPS_TRANSFER64K 0x08
+ #define IPS_NOTIMEOUT 0x00
+ #define IPS_TIMEOUT10 0x10
+ #define IPS_TIMEOUT60 0x20
+ #define IPS_TIMEOUT20M 0x30
+
+ /*
+ * SCSI Inquiry Data Flags
+ */
+ #define IPS_SCSI_INQ_TYPE_DASD 0x00
+ #define IPS_SCSI_INQ_TYPE_PROCESSOR 0x03
+ #define IPS_SCSI_INQ_LU_CONNECTED 0x00
+ #define IPS_SCSI_INQ_RD_REV2 0x02
+ #define IPS_SCSI_INQ_REV2 0x02
+ #define IPS_SCSI_INQ_REV3 0x03
+ #define IPS_SCSI_INQ_Address16 0x01
+ #define IPS_SCSI_INQ_Address32 0x02
+ #define IPS_SCSI_INQ_MedChanger 0x08
+ #define IPS_SCSI_INQ_MultiPort 0x10
+ #define IPS_SCSI_INQ_EncServ 0x40
+ #define IPS_SCSI_INQ_SoftReset 0x01
+ #define IPS_SCSI_INQ_CmdQue 0x02
+ #define IPS_SCSI_INQ_Linked 0x08
+ #define IPS_SCSI_INQ_Sync 0x10
+ #define IPS_SCSI_INQ_WBus16 0x20
+ #define IPS_SCSI_INQ_WBus32 0x40
+ #define IPS_SCSI_INQ_RelAdr 0x80
+
+ /*
+ * SCSI Request Sense Data Flags
+ */
+ #define IPS_SCSI_REQSEN_VALID 0x80
+ #define IPS_SCSI_REQSEN_CURRENT_ERR 0x70
+ #define IPS_SCSI_REQSEN_NO_SENSE 0x00
+
+ /*
+ * SCSI Mode Page Equates
+ */
+ #define IPS_SCSI_MP3_SoftSector 0x01
+ #define IPS_SCSI_MP3_HardSector 0x02
+ #define IPS_SCSI_MP3_Removeable 0x04
+ #define IPS_SCSI_MP3_AllocateSurface 0x08
+
+ /*
+ * HA Flags
+ */
+
+ #define IPS_HA_ENH_SG 0x1
+
+ /*
+ * SCB Flags
+ */
+ #define IPS_SCB_MAP_SG 0x00008
+ #define IPS_SCB_MAP_SINGLE 0X00010
+
+ /*
+ * Passthru stuff
+ */
+ #define IPS_COPPUSRCMD (('C'<<8) | 65)
+ #define IPS_COPPIOCCMD (('C'<<8) | 66)
+ #define IPS_NUMCTRLS (('C'<<8) | 68)
+ #define IPS_CTRLINFO (('C'<<8) | 69)
+
+ /* flashing defines */
+ #define IPS_FW_IMAGE 0x00
+ #define IPS_BIOS_IMAGE 0x01
+ #define IPS_WRITE_FW 0x01
+ #define IPS_WRITE_BIOS 0x02
+ #define IPS_ERASE_BIOS 0x03
+ #define IPS_BIOS_HEADER 0xC0
+
+ /* time oriented stuff */
+ #define IPS_IS_LEAP_YEAR(y) (((y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0))) ? 1 : 0)
+ #define IPS_NUM_LEAP_YEARS_THROUGH(y) ((y) / 4 - (y) / 100 + (y) / 400)
+
+ #define IPS_SECS_MIN 60
+ #define IPS_SECS_HOUR 3600
+ #define IPS_SECS_8HOURS 28800
+ #define IPS_SECS_DAY 86400
+ #define IPS_DAYS_NORMAL_YEAR 365
+ #define IPS_DAYS_LEAP_YEAR 366
+ #define IPS_EPOCH_YEAR 1970
+
+ /*
+ * Scsi_Host Template
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ static int ips_proc24_info(char *, char **, off_t, int, int, int);
+ static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *);
+ static int ips_biosparam(Disk *disk, kdev_t dev, int geom[]);
+#else
+ static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+ static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[]);
+ static int ips_slave_configure(Scsi_Device *SDptr);
+#endif
+
+/*
+ * Raid Command Formats
+ */
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t log_drv;
+ uint8_t sg_count;
+ uint32_t lba;
+ uint32_t sg_addr;
+ uint16_t sector_count;
+ uint8_t segment_4G;
+ uint8_t enhanced_sg;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_IO_CMD, *PIPS_IO_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint16_t reserved;
+ uint32_t reserved2;
+ uint32_t buffer_addr;
+ uint32_t reserved3;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_LD_CMD, *PIPS_LD_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t reserved;
+ uint8_t reserved2;
+ uint32_t reserved3;
+ uint32_t buffer_addr;
+ uint32_t reserved4;
+} IPS_IOCTL_CMD, *PIPS_IOCTL_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t channel;
+ uint8_t reserved3;
+ uint8_t reserved4;
+ uint8_t reserved5;
+ uint8_t reserved6;
+ uint8_t reserved7;
+ uint8_t reserved8;
+ uint8_t reserved9;
+ uint8_t reserved10;
+ uint8_t reserved11;
+ uint8_t reserved12;
+ uint8_t reserved13;
+ uint8_t reserved14;
+ uint8_t adapter_flag;
+} IPS_RESET_CMD, *PIPS_RESET_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint16_t reserved;
+ uint32_t reserved2;
+ uint32_t dcdb_address;
+ uint16_t reserved3;
+ uint8_t segment_4G;
+ uint8_t enhanced_sg;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_DCDB_CMD, *PIPS_DCDB_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t channel;
+ uint8_t source_target;
+ uint32_t reserved;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_CS_CMD, *PIPS_CS_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t log_drv;
+ uint8_t control;
+ uint32_t reserved;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_US_CMD, *PIPS_US_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t reserved;
+ uint8_t state;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ uint32_t reserved4;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_FC_CMD, *PIPS_FC_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t reserved;
+ uint8_t desc;
+ uint32_t reserved2;
+ uint32_t buffer_addr;
+ uint32_t reserved3;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_STATUS_CMD, *PIPS_STATUS_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t page;
+ uint8_t write;
+ uint32_t reserved;
+ uint32_t buffer_addr;
+ uint32_t reserved2;
+ uint32_t ccsar;
+ uint32_t cccr;
+} IPS_NVRAM_CMD, *PIPS_NVRAM_CMD;
+
+typedef struct
+{
+ uint8_t op_code;
+ uint8_t command_id;
+ uint16_t reserved;
+ uint32_t count;
+ uint32_t buffer_addr;
+ uint32_t reserved2;
+} IPS_VERSION_INFO, *PIPS_VERSION_INFO;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t reset_count;
+ uint8_t reset_type;
+ uint8_t second;
+ uint8_t minute;
+ uint8_t hour;
+ uint8_t day;
+ uint8_t reserved1[4];
+ uint8_t month;
+ uint8_t yearH;
+ uint8_t yearL;
+ uint8_t reserved2;
+} IPS_FFDC_CMD, *PIPS_FFDC_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t type;
+ uint8_t direction;
+ uint32_t count;
+ uint32_t buffer_addr;
+ uint8_t total_packets;
+ uint8_t packet_num;
+ uint16_t reserved;
+} IPS_FLASHFW_CMD, *PIPS_FLASHFW_CMD;
+
+typedef struct {
+ uint8_t op_code;
+ uint8_t command_id;
+ uint8_t type;
+ uint8_t direction;
+ uint32_t count;
+ uint32_t buffer_addr;
+ uint32_t offset;
+} IPS_FLASHBIOS_CMD, *PIPS_FLASHBIOS_CMD;
+
+typedef union {
+ IPS_IO_CMD basic_io;
+ IPS_LD_CMD logical_info;
+ IPS_IOCTL_CMD ioctl_info;
+ IPS_DCDB_CMD dcdb;
+ IPS_CS_CMD config_sync;
+ IPS_US_CMD unlock_stripe;
+ IPS_FC_CMD flush_cache;
+ IPS_STATUS_CMD status;
+ IPS_NVRAM_CMD nvram;
+ IPS_FFDC_CMD ffdc;
+ IPS_FLASHFW_CMD flashfw;
+ IPS_FLASHBIOS_CMD flashbios;
+ IPS_VERSION_INFO version_info;
+ IPS_RESET_CMD reset;
+} IPS_HOST_COMMAND, *PIPS_HOST_COMMAND;
+
+typedef struct {
+ uint8_t logical_id;
+ uint8_t reserved;
+ uint8_t raid_level;
+ uint8_t state;
+ uint32_t sector_count;
+} IPS_DRIVE_INFO, *PIPS_DRIVE_INFO;
+
+typedef struct {
+ uint8_t no_of_log_drive;
+ uint8_t reserved[3];
+ IPS_DRIVE_INFO drive_info[IPS_MAX_LD];
+} IPS_LD_INFO, *PIPS_LD_INFO;
+
+typedef struct {
+ uint8_t device_address;
+ uint8_t cmd_attribute;
+ uint16_t transfer_length;
+ uint32_t buffer_pointer;
+ uint8_t cdb_length;
+ uint8_t sense_length;
+ uint8_t sg_count;
+ uint8_t reserved;
+ uint8_t scsi_cdb[12];
+ uint8_t sense_info[64];
+ uint8_t scsi_status;
+ uint8_t reserved2[3];
+} IPS_DCDB_TABLE, *PIPS_DCDB_TABLE;
+
+typedef struct {
+ uint8_t device_address;
+ uint8_t cmd_attribute;
+ uint8_t cdb_length;
+ uint8_t reserved_for_LUN;
+ uint32_t transfer_length;
+ uint32_t buffer_pointer;
+ uint16_t sg_count;
+ uint8_t sense_length;
+ uint8_t scsi_status;
+ uint32_t reserved;
+ uint8_t scsi_cdb[16];
+ uint8_t sense_info[56];
+} IPS_DCDB_TABLE_TAPE, *PIPS_DCDB_TABLE_TAPE;
+
+typedef union {
+ struct {
+ volatile uint8_t reserved;
+ volatile uint8_t command_id;
+ volatile uint8_t basic_status;
+ volatile uint8_t extended_status;
+ } fields;
+
+ volatile uint32_t value;
+} IPS_STATUS, *PIPS_STATUS;
+
+typedef struct {
+ IPS_STATUS status[IPS_MAX_CMDS + 1];
+ volatile PIPS_STATUS p_status_start;
+ volatile PIPS_STATUS p_status_end;
+ volatile PIPS_STATUS p_status_tail;
+ volatile uint32_t hw_status_start;
+ volatile uint32_t hw_status_tail;
+} IPS_ADAPTER, *PIPS_ADAPTER;
+
+typedef struct {
+ uint8_t ucLogDriveCount;
+ uint8_t ucMiscFlag;
+ uint8_t ucSLTFlag;
+ uint8_t ucBSTFlag;
+ uint8_t ucPwrChgCnt;
+ uint8_t ucWrongAdrCnt;
+ uint8_t ucUnidentCnt;
+ uint8_t ucNVramDevChgCnt;
+ uint8_t CodeBlkVersion[8];
+ uint8_t BootBlkVersion[8];
+ uint32_t ulDriveSize[IPS_MAX_LD];
+ uint8_t ucConcurrentCmdCount;
+ uint8_t ucMaxPhysicalDevices;
+ uint16_t usFlashRepgmCount;
+ uint8_t ucDefunctDiskCount;
+ uint8_t ucRebuildFlag;
+ uint8_t ucOfflineLogDrvCount;
+ uint8_t ucCriticalDrvCount;
+ uint16_t usConfigUpdateCount;
+ uint8_t ucBlkFlag;
+ uint8_t reserved;
+ uint16_t usAddrDeadDisk[IPS_MAX_CHANNELS * (IPS_MAX_TARGETS + 1)];
+} IPS_ENQ, *PIPS_ENQ;
+
+typedef struct {
+ uint8_t ucInitiator;
+ uint8_t ucParameters;
+ uint8_t ucMiscFlag;
+ uint8_t ucState;
+ uint32_t ulBlockCount;
+ uint8_t ucDeviceId[28];
+} IPS_DEVSTATE, *PIPS_DEVSTATE;
+
+typedef struct {
+ uint8_t ucChn;
+ uint8_t ucTgt;
+ uint16_t ucReserved;
+ uint32_t ulStartSect;
+ uint32_t ulNoOfSects;
+} IPS_CHUNK, *PIPS_CHUNK;
+
+typedef struct {
+ uint16_t ucUserField;
+ uint8_t ucState;
+ uint8_t ucRaidCacheParam;
+ uint8_t ucNoOfChunkUnits;
+ uint8_t ucStripeSize;
+ uint8_t ucParams;
+ uint8_t ucReserved;
+ uint32_t ulLogDrvSize;
+ IPS_CHUNK chunk[IPS_MAX_CHUNKS];
+} IPS_LD, *PIPS_LD;
+
+typedef struct {
+ uint8_t board_disc[8];
+ uint8_t processor[8];
+ uint8_t ucNoChanType;
+ uint8_t ucNoHostIntType;
+ uint8_t ucCompression;
+ uint8_t ucNvramType;
+ uint32_t ulNvramSize;
+} IPS_HARDWARE, *PIPS_HARDWARE;
+
+typedef struct {
+ uint8_t ucLogDriveCount;
+ uint8_t ucDateD;
+ uint8_t ucDateM;
+ uint8_t ucDateY;
+ uint8_t init_id[4];
+ uint8_t host_id[12];
+ uint8_t time_sign[8];
+ uint32_t UserOpt;
+ uint16_t user_field;
+ uint8_t ucRebuildRate;
+ uint8_t ucReserve;
+ IPS_HARDWARE hardware_disc;
+ IPS_LD logical_drive[IPS_MAX_LD];
+ IPS_DEVSTATE dev[IPS_MAX_CHANNELS][IPS_MAX_TARGETS+1];
+ uint8_t reserved[512];
+} IPS_CONF, *PIPS_CONF;
+
+typedef struct {
+ uint32_t signature;
+ uint8_t reserved1;
+ uint8_t adapter_slot;
+ uint16_t adapter_type;
+ uint8_t ctrl_bios[8];
+ uint8_t versioning; /* 1 = Versioning Supported, else 0 */
+ uint8_t version_mismatch; /* 1 = Versioning MisMatch, else 0 */
+ uint8_t reserved2;
+ uint8_t operating_system;
+ uint8_t driver_high[4];
+ uint8_t driver_low[4];
+ uint8_t BiosCompatibilityID[8];
+ uint8_t ReservedForOS2[8];
+ uint8_t bios_high[4]; /* Adapter's Flashed BIOS Version */
+ uint8_t bios_low[4];
+ uint8_t adapter_order[16]; /* BIOS Telling us the Sort Order */
+ uint8_t Filler[60];
+} IPS_NVRAM_P5, *PIPS_NVRAM_P5;
+
+/*--------------------------------------------------------------------------*/
+/* Data returned from a GetVersion Command */
+/*--------------------------------------------------------------------------*/
+
+ /* SubSystem Parameter[4] */
+#define IPS_GET_VERSION_SUPPORT 0x00018000 /* Mask for Versioning Support */
+
+typedef struct
+{
+ uint32_t revision;
+ uint8_t bootBlkVersion[32];
+ uint8_t bootBlkAttributes[4];
+ uint8_t codeBlkVersion[32];
+ uint8_t biosVersion[32];
+ uint8_t biosAttributes[4];
+ uint8_t compatibilityId[32];
+ uint8_t reserved[4];
+} IPS_VERSION_DATA;
+
+
+typedef struct _IPS_SUBSYS {
+ uint32_t param[128];
+} IPS_SUBSYS, *PIPS_SUBSYS;
+
+/**
+ ** SCSI Structures
+ **/
+
+/*
+ * Inquiry Data Format
+ */
+typedef struct {
+ uint8_t DeviceType;
+ uint8_t DeviceTypeQualifier;
+ uint8_t Version;
+ uint8_t ResponseDataFormat;
+ uint8_t AdditionalLength;
+ uint8_t Reserved;
+ uint8_t Flags[2];
+ uint8_t VendorId[8];
+ uint8_t ProductId[16];
+ uint8_t ProductRevisionLevel[4];
+ uint8_t Reserved2; /* Provides NULL terminator to name */
+} IPS_SCSI_INQ_DATA, *PIPS_SCSI_INQ_DATA;
+
+/*
+ * Read Capacity Data Format
+ */
+typedef struct {
+ uint32_t lba;
+ uint32_t len;
+} IPS_SCSI_CAPACITY;
+
+/*
+ * Request Sense Data Format
+ */
+typedef struct {
+ uint8_t ResponseCode;
+ uint8_t SegmentNumber;
+ uint8_t Flags;
+ uint8_t Information[4];
+ uint8_t AdditionalLength;
+ uint8_t CommandSpecific[4];
+ uint8_t AdditionalSenseCode;
+ uint8_t AdditionalSenseCodeQual;
+ uint8_t FRUCode;
+ uint8_t SenseKeySpecific[3];
+} IPS_SCSI_REQSEN;
+
+/*
+ * Sense Data Format - Page 3
+ */
+typedef struct {
+ uint8_t PageCode;
+ uint8_t PageLength;
+ uint16_t TracksPerZone;
+ uint16_t AltSectorsPerZone;
+ uint16_t AltTracksPerZone;
+ uint16_t AltTracksPerVolume;
+ uint16_t SectorsPerTrack;
+ uint16_t BytesPerSector;
+ uint16_t Interleave;
+ uint16_t TrackSkew;
+ uint16_t CylinderSkew;
+ uint8_t flags;
+ uint8_t reserved[3];
+} IPS_SCSI_MODE_PAGE3;
+
+/*
+ * Sense Data Format - Page 4
+ */
+typedef struct {
+ uint8_t PageCode;
+ uint8_t PageLength;
+ uint16_t CylindersHigh;
+ uint8_t CylindersLow;
+ uint8_t Heads;
+ uint16_t WritePrecompHigh;
+ uint8_t WritePrecompLow;
+ uint16_t ReducedWriteCurrentHigh;
+ uint8_t ReducedWriteCurrentLow;
+ uint16_t StepRate;
+ uint16_t LandingZoneHigh;
+ uint8_t LandingZoneLow;
+ uint8_t flags;
+ uint8_t RotationalOffset;
+ uint8_t Reserved;
+ uint16_t MediumRotationRate;
+ uint8_t Reserved2[2];
+} IPS_SCSI_MODE_PAGE4;
+
+/*
+ * Sense Data Format - Page 8
+ */
+typedef struct {
+ uint8_t PageCode;
+ uint8_t PageLength;
+ uint8_t flags;
+ uint8_t RetentPrio;
+ uint16_t DisPrefetchLen;
+ uint16_t MinPrefetchLen;
+ uint16_t MaxPrefetchLen;
+ uint16_t MaxPrefetchCeiling;
+} IPS_SCSI_MODE_PAGE8;
+
+/*
+ * Sense Data Format - Block Descriptor (DASD)
+ */
+typedef struct {
+ uint32_t NumberOfBlocks;
+ uint8_t DensityCode;
+ uint16_t BlockLengthHigh;
+ uint8_t BlockLengthLow;
+} IPS_SCSI_MODE_PAGE_BLKDESC;
+
+/*
+ * Sense Data Format - Mode Page Header
+ */
+typedef struct {
+ uint8_t DataLength;
+ uint8_t MediumType;
+ uint8_t Reserved;
+ uint8_t BlockDescLength;
+} IPS_SCSI_MODE_PAGE_HEADER;
+
+typedef struct {
+ IPS_SCSI_MODE_PAGE_HEADER hdr;
+ IPS_SCSI_MODE_PAGE_BLKDESC blkdesc;
+
+ union {
+ IPS_SCSI_MODE_PAGE3 pg3;
+ IPS_SCSI_MODE_PAGE4 pg4;
+ IPS_SCSI_MODE_PAGE8 pg8;
+ } pdata;
+} IPS_SCSI_MODE_PAGE_DATA;
+
+/*
+ * Scatter Gather list format
+ */
+typedef struct ips_sglist {
+ uint32_t address;
+ uint32_t length;
+} IPS_STD_SG_LIST;
+
+typedef struct ips_enh_sglist {
+ uint32_t address_lo;
+ uint32_t address_hi;
+ uint32_t length;
+ uint32_t reserved;
+} IPS_ENH_SG_LIST;
+
+typedef union {
+ void *list;
+ IPS_STD_SG_LIST *std_list;
+ IPS_ENH_SG_LIST *enh_list;
+} IPS_SG_LIST;
+
+typedef struct _IPS_INFOSTR {
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+ int localpos;
+} IPS_INFOSTR;
+
+typedef struct {
+ char *option_name;
+ int *option_flag;
+ int option_value;
+} IPS_OPTION;
+
+/*
+ * Status Info
+ */
+typedef struct ips_stat {
+ uint32_t residue_len;
+ void *scb_addr;
+ uint8_t padding[12 - sizeof(void *)];
+} ips_stat_t;
+
+/*
+ * SCB Queue Format
+ */
+typedef struct ips_scb_queue {
+ struct ips_scb *head;
+ struct ips_scb *tail;
+ int count;
+} ips_scb_queue_t;
+
+/*
+ * Wait queue_format
+ */
+typedef struct ips_wait_queue {
+ Scsi_Cmnd *head;
+ Scsi_Cmnd *tail;
+ int count;
+} ips_wait_queue_t;
+
+typedef struct ips_copp_wait_item {
+ Scsi_Cmnd *scsi_cmd;
+ struct ips_copp_wait_item *next;
+} ips_copp_wait_item_t;
+
+typedef struct ips_copp_queue {
+ struct ips_copp_wait_item *head;
+ struct ips_copp_wait_item *tail;
+ int count;
+} ips_copp_queue_t;
+
+/* forward decl for host structure */
+struct ips_ha;
+
+typedef struct {
+ int (*reset)(struct ips_ha *);
+ int (*issue)(struct ips_ha *, struct ips_scb *);
+ int (*isinit)(struct ips_ha *);
+ int (*isintr)(struct ips_ha *);
+ int (*init)(struct ips_ha *);
+ int (*erasebios)(struct ips_ha *);
+ int (*programbios)(struct ips_ha *, char *, uint32_t, uint32_t);
+ int (*verifybios)(struct ips_ha *, char *, uint32_t, uint32_t);
+ void (*statinit)(struct ips_ha *);
+ int (*intr)(struct ips_ha *);
+ void (*enableint)(struct ips_ha *);
+ uint32_t (*statupd)(struct ips_ha *);
+} ips_hw_func_t;
+
+typedef struct ips_ha {
+ uint8_t ha_id[IPS_MAX_CHANNELS+1];
+ uint32_t dcdb_active[IPS_MAX_CHANNELS];
+ uint32_t io_addr; /* Base I/O address */
+ uint8_t irq; /* IRQ for adapter */
+ uint8_t ntargets; /* Number of targets */
+ uint8_t nbus; /* Number of buses */
+ uint8_t nlun; /* Number of Luns */
+ uint16_t ad_type; /* Adapter type */
+ uint16_t host_num; /* Adapter number */
+ uint32_t max_xfer; /* Maximum Xfer size */
+ uint32_t max_cmds; /* Max concurrent commands */
+ uint32_t num_ioctl; /* Number of Ioctls */
+ ips_stat_t sp; /* Status packer pointer */
+ struct ips_scb *scbs; /* Array of all CCBS */
+ struct ips_scb *scb_freelist; /* SCB free list */
+ ips_wait_queue_t scb_waitlist; /* Pending SCB list */
+ ips_copp_queue_t copp_waitlist; /* Pending PT list */
+ ips_scb_queue_t scb_activelist; /* Active SCB list */
+ IPS_IO_CMD *dummy; /* dummy command */
+ IPS_ADAPTER *adapt; /* Adapter status area */
+ IPS_LD_INFO *logical_drive_info; /* Adapter Logical Drive Info */
+ dma_addr_t logical_drive_info_dma_addr; /* Logical Drive Info DMA Address */
+ IPS_ENQ *enq; /* Adapter Enquiry data */
+ IPS_CONF *conf; /* Adapter config data */
+ IPS_NVRAM_P5 *nvram; /* NVRAM page 5 data */
+ IPS_SUBSYS *subsys; /* Subsystem parameters */
+ char *ioctl_data; /* IOCTL data area */
+ uint32_t ioctl_datasize; /* IOCTL data size */
+ uint32_t cmd_in_progress; /* Current command in progress*/
+ int flags; /* */
+ uint8_t waitflag; /* are we waiting for cmd */
+ uint8_t active;
+ int ioctl_reset; /* IOCTL Requested Reset Flag */
+ uint16_t reset_count; /* number of resets */
+ time_t last_ffdc; /* last time we sent ffdc info*/
+ uint8_t revision_id; /* Revision level */
+ uint16_t device_id; /* PCI device ID */
+ uint8_t slot_num; /* PCI Slot Number */
+ uint16_t subdevice_id; /* Subsystem device ID */
+ int ioctl_len; /* size of ioctl buffer */
+ dma_addr_t ioctl_busaddr; /* dma address of ioctl buffer*/
+ uint8_t bios_version[8]; /* BIOS Revision */
+ uint32_t mem_addr; /* Memory mapped address */
+ uint32_t io_len; /* Size of IO Address */
+ uint32_t mem_len; /* Size of memory address */
+ char __iomem *mem_ptr; /* Memory mapped Ptr */
+ char __iomem *ioremap_ptr;/* ioremapped memory pointer */
+ ips_hw_func_t func; /* hw function pointers */
+ struct pci_dev *pcidev; /* PCI device handle */
+ char *flash_data; /* Save Area for flash data */
+ int flash_len; /* length of flash buffer */
+ u32 flash_datasize; /* Save Area for flash data size */
+ dma_addr_t flash_busaddr; /* dma address of flash buffer*/
+ dma_addr_t enq_busaddr; /* dma address of enq struct */
+ uint8_t requires_esl; /* Requires an EraseStripeLock */
+} ips_ha_t;
+
+typedef void (*ips_scb_callback) (ips_ha_t *, struct ips_scb *);
+
+/*
+ * SCB Format
+ */
+typedef struct ips_scb {
+ IPS_HOST_COMMAND cmd;
+ IPS_DCDB_TABLE dcdb;
+ uint8_t target_id;
+ uint8_t bus;
+ uint8_t lun;
+ uint8_t cdb[12];
+ uint32_t scb_busaddr;
+ uint32_t old_data_busaddr; // Obsolete, but kept for old utility compatibility
+ uint32_t timeout;
+ uint8_t basic_status;
+ uint8_t extended_status;
+ uint8_t breakup;
+ uint8_t sg_break;
+ uint32_t data_len;
+ uint32_t sg_len;
+ uint32_t flags;
+ uint32_t op_code;
+ IPS_SG_LIST sg_list;
+ Scsi_Cmnd *scsi_cmd;
+ struct ips_scb *q_next;
+ ips_scb_callback callback;
+ uint32_t sg_busaddr;
+ int sg_count;
+ dma_addr_t data_busaddr;
+} ips_scb_t;
+
+typedef struct ips_scb_pt {
+ IPS_HOST_COMMAND cmd;
+ IPS_DCDB_TABLE dcdb;
+ uint8_t target_id;
+ uint8_t bus;
+ uint8_t lun;
+ uint8_t cdb[12];
+ uint32_t scb_busaddr;
+ uint32_t data_busaddr;
+ uint32_t timeout;
+ uint8_t basic_status;
+ uint8_t extended_status;
+ uint16_t breakup;
+ uint32_t data_len;
+ uint32_t sg_len;
+ uint32_t flags;
+ uint32_t op_code;
+ IPS_SG_LIST *sg_list;
+ Scsi_Cmnd *scsi_cmd;
+ struct ips_scb *q_next;
+ ips_scb_callback callback;
+} ips_scb_pt_t;
+
+/*
+ * Passthru Command Format
+ */
+typedef struct {
+ uint8_t CoppID[4];
+ uint32_t CoppCmd;
+ uint32_t PtBuffer;
+ uint8_t *CmdBuffer;
+ uint32_t CmdBSize;
+ ips_scb_pt_t CoppCP;
+ uint32_t TimeOut;
+ uint8_t BasicStatus;
+ uint8_t ExtendedStatus;
+ uint8_t AdapterType;
+ uint8_t reserved;
+} ips_passthru_t;
+
+#endif
+
+/* The Version Information below gets created by SED during the build process. */
+/* Do not modify the next line; it's what SED is looking for to do the insert. */
+/* Version Info */
+/*************************************************************************
+*
+* VERSION.H -- version numbers and copyright notices in various formats
+*
+*************************************************************************/
+
+#define IPS_VER_MAJOR 7
+#define IPS_VER_MAJOR_STRING "7"
+#define IPS_VER_MINOR 10
+#define IPS_VER_MINOR_STRING "10"
+#define IPS_VER_BUILD 18
+#define IPS_VER_BUILD_STRING "18"
+#define IPS_VER_STRING "7.10.18"
+#define IPS_RELEASE_ID 0x00020000
+#define IPS_BUILD_IDENT 731
+#define IPS_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002. All Rights Reserved."
+#define IPS_ADAPTECCOPYRIGHT_STRING "(c) Copyright Adaptec, Inc. 2002 to 2004. All Rights Reserved."
+#define IPS_DELLCOPYRIGHT_STRING "(c) Copyright Dell 2004. All Rights Reserved."
+#define IPS_NT_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002."
+
+/* Version numbers for various adapters */
+#define IPS_VER_SERVERAID1 "2.25.01"
+#define IPS_VER_SERVERAID2 "2.88.13"
+#define IPS_VER_NAVAJO "2.88.13"
+#define IPS_VER_SERVERAID3 "6.10.24"
+#define IPS_VER_SERVERAID4H "7.10.11"
+#define IPS_VER_SERVERAID4MLx "7.10.18"
+#define IPS_VER_SARASOTA "7.10.18"
+#define IPS_VER_MARCO "7.10.18"
+#define IPS_VER_SEBRING "7.10.18"
+#define IPS_VER_KEYWEST "7.10.18"
+
+/* Compatability IDs for various adapters */
+#define IPS_COMPAT_UNKNOWN ""
+#define IPS_COMPAT_CURRENT "KW710"
+#define IPS_COMPAT_SERVERAID1 "2.25.01"
+#define IPS_COMPAT_SERVERAID2 "2.88.13"
+#define IPS_COMPAT_NAVAJO "2.88.13"
+#define IPS_COMPAT_KIOWA "2.88.13"
+#define IPS_COMPAT_SERVERAID3H "SB610"
+#define IPS_COMPAT_SERVERAID3L "SB610"
+#define IPS_COMPAT_SERVERAID4H "KW710"
+#define IPS_COMPAT_SERVERAID4M "KW710"
+#define IPS_COMPAT_SERVERAID4L "KW710"
+#define IPS_COMPAT_SERVERAID4Mx "KW710"
+#define IPS_COMPAT_SERVERAID4Lx "KW710"
+#define IPS_COMPAT_SARASOTA "KW710"
+#define IPS_COMPAT_MARCO "KW710"
+#define IPS_COMPAT_SEBRING "KW710"
+#define IPS_COMPAT_TAMPA "KW710"
+#define IPS_COMPAT_KEYWEST "KW710"
+#define IPS_COMPAT_BIOS "KW710"
+
+#define IPS_COMPAT_MAX_ADAPTER_TYPE 18
+#define IPS_COMPAT_ID_LENGTH 8
+
+#define IPS_DEFINE_COMPAT_TABLE(tablename) \
+ char tablename[IPS_COMPAT_MAX_ADAPTER_TYPE] [IPS_COMPAT_ID_LENGTH] = { \
+ IPS_COMPAT_UNKNOWN, \
+ IPS_COMPAT_SERVERAID1, \
+ IPS_COMPAT_SERVERAID2, \
+ IPS_COMPAT_NAVAJO, \
+ IPS_COMPAT_KIOWA, \
+ IPS_COMPAT_SERVERAID3H, \
+ IPS_COMPAT_SERVERAID3L, \
+ IPS_COMPAT_SERVERAID4H, \
+ IPS_COMPAT_SERVERAID4M, \
+ IPS_COMPAT_SERVERAID4L, \
+ IPS_COMPAT_SERVERAID4Mx, \
+ IPS_COMPAT_SERVERAID4Lx, \
+ IPS_COMPAT_SARASOTA, /* one-channel variety of SARASOTA */ \
+ IPS_COMPAT_SARASOTA, /* two-channel variety of SARASOTA */ \
+ IPS_COMPAT_MARCO, \
+ IPS_COMPAT_SEBRING, \
+ IPS_COMPAT_TAMPA, \
+ IPS_COMPAT_KEYWEST \
+ }
+
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/jazz_esp.c b/drivers/scsi/jazz_esp.c
new file mode 100644
index 000000000000..a642f736cf85
--- /dev/null
+++ b/drivers/scsi/jazz_esp.c
@@ -0,0 +1,329 @@
+/*
+ * jazz_esp.c: Driver for SCSI chip on Mips Magnum Boards (JAZZ architecture)
+ *
+ * Copyright (C) 1997 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * jazz_esp is based on David S. Miller's ESP driver and cyber_esp
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <asm/irq.h>
+#include <asm/jazz.h>
+#include <asm/jazzdma.h>
+#include <asm/dma.h>
+
+#include <asm/pgtable.h>
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, struct scsi_cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, struct scsi_cmnd *sp);
+static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, struct scsi_cmnd *sp);
+static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, struct scsi_cmnd *sp);
+static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, struct scsi_cmnd *sp);
+static void dma_advance_sg (struct scsi_cmnd *sp);
+static void dma_led_off(struct NCR_ESP *);
+static void dma_led_on(struct NCR_ESP *);
+
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are trasfered to the ESP chip
+ * via PIO.
+ */
+
+int jazz_esp_detect(Scsi_Host_Template *tpnt);
+static int jazz_esp_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "jazz_esp",
+ .proc_info = &esp_proc_info,
+ .name = "ESP 100/100a/200",
+ .detect = jazz_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = jazz_esp_release,
+ .info = esp_info,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+#include "scsi_module.c"
+
+/***************************************************************** Detection */
+static int jazz_esp_detect(struct scsi_host_template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct ConfigDev *esp_dev;
+
+ /*
+ * first assumption it is there:-)
+ */
+ if (1) {
+ esp_dev = 0;
+ esp = esp_allocate(tpnt, (void *) esp_dev);
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = 0;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+ esp->dma_led_off = &dma_led_off;
+ esp->dma_led_on = &dma_led_on;
+
+ /* virtual DMA functions */
+ esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
+ esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
+ esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one;
+ esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl;
+ esp->dma_advance_sg = &dma_advance_sg;
+
+
+ /* SCSI chip speed */
+ esp->cfreq = 40000000;
+
+ /*
+ * we don't give the address of DMA channel, but the number
+ * of DMA channel, so we can use the jazz DMA functions
+ *
+ */
+ esp->dregs = JAZZ_SCSI_DMA;
+
+ /* ESP register base */
+ esp->eregs = (struct ESP_regs *)(JAZZ_SCSI_BASE);
+
+ /* Set the command buffer */
+ esp->esp_command = (volatile unsigned char *)cmd_buffer;
+
+ /* get virtual dma address for command buffer */
+ esp->esp_command_dvma = vdma_alloc(CPHYSADDR(cmd_buffer), sizeof (cmd_buffer));
+
+ esp->irq = JAZZ_SCSI_IRQ;
+ request_irq(JAZZ_SCSI_IRQ, esp_intr, SA_INTERRUPT, "JAZZ SCSI",
+ esp->ehost);
+
+ /*
+ * FIXME, look if the scsi id is available from NVRAM
+ */
+ esp->scsi_id = 7;
+
+ /* Check for differential SCSI-bus */
+ /* What is this stuff? */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+ }
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, struct scsi_cmnd *sp)
+{
+ /*
+ * maximum DMA size is 1MB
+ */
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x100000)
+ sz = 0x100000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+
+ ESPLOG(("esp%d: dma -- enable <%08x> residue <%08x\n",
+ esp->esp_id, vdma_get_enable((int)esp->dregs), vdma_get_residue((int)esp->dregs)));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+ dma_cache_wback_inv ((unsigned long)phys_to_virt(vdma_log2phys(vaddress)), length);
+ vdma_disable ((int)esp->dregs);
+ vdma_set_mode ((int)esp->dregs, DMA_MODE_READ);
+ vdma_set_addr ((int)esp->dregs, vaddress);
+ vdma_set_count ((int)esp->dregs, length);
+ vdma_enable ((int)esp->dregs);
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+ dma_cache_wback_inv ((unsigned long)phys_to_virt(vdma_log2phys(vaddress)), length);
+ vdma_disable ((int)esp->dregs);
+ vdma_set_mode ((int)esp->dregs, DMA_MODE_WRITE);
+ vdma_set_addr ((int)esp->dregs, vaddress);
+ vdma_set_count ((int)esp->dregs, length);
+ vdma_enable ((int)esp->dregs);
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR);
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ int enable = vdma_get_enable((int)esp->dregs);
+
+ return (enable & R4030_CHNL_ENABLE);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /*
+ * On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, struct scsi_cmnd *sp)
+{
+ sp->SCp.have_data_in = vdma_alloc(CPHYSADDR(sp->SCp.buffer), sp->SCp.this_residual);
+ sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in);
+}
+
+static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, struct scsi_cmnd *sp)
+{
+ int sz = sp->SCp.buffers_residual;
+ struct scatterlist *sg = (struct scatterlist *) sp->SCp.buffer;
+
+ while (sz >= 0) {
+ sg[sz].dma_address = vdma_alloc(CPHYSADDR(page_address(sg[sz].page) + sg[sz].offset), sg[sz].length);
+ sz--;
+ }
+ sp->SCp.ptr=(char *)(sp->SCp.buffer->dma_address);
+}
+
+static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, struct scsi_cmnd *sp)
+{
+ vdma_free(sp->SCp.have_data_in);
+}
+
+static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, struct scsi_cmnd *sp)
+{
+ int sz = sp->use_sg - 1;
+ struct scatterlist *sg = (struct scatterlist *)sp->buffer;
+
+ while(sz >= 0) {
+ vdma_free(sg[sz].dma_address);
+ sz--;
+ }
+}
+
+static void dma_advance_sg (struct scsi_cmnd *sp)
+{
+ sp->SCp.ptr = (char *)(sp->SCp.buffer->dma_address);
+}
+
+#define JAZZ_HDC_LED 0xe000d100 /* FIXME, find correct address */
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+#if 0
+ *(unsigned char *)JAZZ_HDC_LED = 0;
+#endif
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+#if 0
+ *(unsigned char *)JAZZ_HDC_LED = 1;
+#endif
+}
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "jazz_esp",
+ .proc_info = esp_proc_info,
+ .name = "ESP 100/100a/200",
+ .detect = jazz_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = jazz_esp_release,
+ .info = esp_info,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c
new file mode 100644
index 000000000000..29f250c80b98
--- /dev/null
+++ b/drivers/scsi/lasi700.c
@@ -0,0 +1,189 @@
+/* -*- mode: c; c-basic-offset: 8 -*- */
+
+/* PARISC LASI driver for the 53c700 chip
+ *
+ * Copyright (C) 2001 by James.Bottomley@HansenPartnership.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+ */
+
+/*
+ * Many thanks to Richard Hirst <rhirst@linuxcare.com> for patiently
+ * debugging this driver on the parisc architecture and suggesting
+ * many improvements and bug fixes.
+ *
+ * Thanks also go to Linuxcare Inc. for providing several PARISC
+ * machines for me to debug the driver on.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/parisc-device.h>
+#include <asm/delay.h>
+
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "53c700.h"
+
+MODULE_AUTHOR("James Bottomley");
+MODULE_DESCRIPTION("lasi700 SCSI Driver");
+MODULE_LICENSE("GPL");
+
+#define LASI_700_SVERSION 0x00071
+#define LASI_710_SVERSION 0x00082
+
+#define LASI700_ID_TABLE { \
+ .hw_type = HPHW_FIO, \
+ .sversion = LASI_700_SVERSION, \
+ .hversion = HVERSION_ANY_ID, \
+ .hversion_rev = HVERSION_REV_ANY_ID, \
+}
+
+#define LASI710_ID_TABLE { \
+ .hw_type = HPHW_FIO, \
+ .sversion = LASI_710_SVERSION, \
+ .hversion = HVERSION_ANY_ID, \
+ .hversion_rev = HVERSION_REV_ANY_ID, \
+}
+
+#define LASI700_CLOCK 25
+#define LASI710_CLOCK 40
+#define LASI_SCSI_CORE_OFFSET 0x100
+
+static struct parisc_device_id lasi700_ids[] = {
+ LASI700_ID_TABLE,
+ LASI710_ID_TABLE,
+ { 0 }
+};
+
+static struct scsi_host_template lasi700_template = {
+ .name = "LASI SCSI 53c700",
+ .proc_name = "lasi700",
+ .this_id = 7,
+ .module = THIS_MODULE,
+};
+MODULE_DEVICE_TABLE(parisc, lasi700_ids);
+
+static int __init
+lasi700_probe(struct parisc_device *dev)
+{
+ unsigned long base = dev->hpa + LASI_SCSI_CORE_OFFSET;
+ struct NCR_700_Host_Parameters *hostdata;
+ struct Scsi_Host *host;
+
+ hostdata = kmalloc(sizeof(*hostdata), GFP_KERNEL);
+ if (!hostdata) {
+ printk(KERN_ERR "%s: Failed to allocate host data\n",
+ dev->dev.bus_id);
+ return -ENOMEM;
+ }
+ memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters));
+
+ hostdata->dev = &dev->dev;
+ dma_set_mask(&dev->dev, DMA_32BIT_MASK);
+ hostdata->base = ioremap(base, 0x100);
+ hostdata->differential = 0;
+
+ if (dev->id.sversion == LASI_700_SVERSION) {
+ hostdata->clock = LASI700_CLOCK;
+ hostdata->force_le_on_be = 1;
+ } else {
+ hostdata->clock = LASI710_CLOCK;
+ hostdata->force_le_on_be = 0;
+ hostdata->chip710 = 1;
+ hostdata->dmode_extra = DMODE_FC2;
+ }
+
+ NCR_700_set_mem_mapped(hostdata);
+
+ host = NCR_700_detect(&lasi700_template, hostdata, &dev->dev);
+ if (!host)
+ goto out_kfree;
+ host->this_id = 7;
+ host->irq = dev->irq;
+ if(request_irq(dev->irq, NCR_700_intr, SA_SHIRQ, "lasi700", host)) {
+ printk(KERN_ERR "lasi700: request_irq failed!\n");
+ goto out_put_host;
+ }
+
+ dev_set_drvdata(&dev->dev, host);
+ scsi_scan_host(host);
+
+ return 0;
+
+ out_put_host:
+ scsi_host_put(host);
+ out_kfree:
+ iounmap(hostdata->base);
+ kfree(hostdata);
+ return -ENODEV;
+}
+
+static int __exit
+lasi700_driver_remove(struct parisc_device *dev)
+{
+ struct Scsi_Host *host = dev_get_drvdata(&dev->dev);
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ scsi_remove_host(host);
+ NCR_700_release(host);
+ free_irq(host->irq, host);
+ iounmap(hostdata->base);
+ kfree(hostdata);
+
+ return 0;
+}
+
+static struct parisc_driver lasi700_driver = {
+ .name = "Lasi SCSI",
+ .id_table = lasi700_ids,
+ .probe = lasi700_probe,
+ .remove = __devexit_p(lasi700_driver_remove),
+};
+
+static int __init
+lasi700_init(void)
+{
+ return register_parisc_driver(&lasi700_driver);
+}
+
+static void __exit
+lasi700_exit(void)
+{
+ unregister_parisc_driver(&lasi700_driver);
+}
+
+module_init(lasi700_init);
+module_exit(lasi700_exit);
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
new file mode 100644
index 000000000000..0b5d3a5b7eda
--- /dev/null
+++ b/drivers/scsi/libata-core.c
@@ -0,0 +1,4024 @@
+/*
+ libata-core.c - helper library for ATA
+
+ Copyright 2003-2004 Red Hat, Inc. All rights reserved.
+ Copyright 2003-2004 Jeff Garzik
+
+ The contents of this file are subject to the Open
+ Software License version 1.1 that can be found at
+ http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ by reference.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the GNU General Public License version 2 (the "GPL") as distributed
+ in the kernel source COPYING file, in which case the provisions of
+ the GPL are applicable instead of the above. If you wish to allow
+ the use of your version of this file only under the terms of the
+ GPL and not to allow others to use your version of this file under
+ the OSL, indicate your decision by deleting the provisions above and
+ replace them with the notice and other provisions required by the GPL.
+ If you do not delete the provisions above, a recipient may use your
+ version of this file under either the OSL or the GPL.
+
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/suspend.h>
+#include <linux/workqueue.h>
+#include <scsi/scsi.h>
+#include "scsi.h"
+#include "scsi_priv.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+
+#include "libata.h"
+
+static unsigned int ata_busy_sleep (struct ata_port *ap,
+ unsigned long tmout_pat,
+ unsigned long tmout);
+static void ata_set_mode(struct ata_port *ap);
+static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev);
+static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift);
+static int fgb(u32 bitmap);
+static int ata_choose_xfer_mode(struct ata_port *ap,
+ u8 *xfer_mode_out,
+ unsigned int *xfer_shift_out);
+static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat);
+static void __ata_qc_complete(struct ata_queued_cmd *qc);
+
+static unsigned int ata_unique_id = 1;
+static struct workqueue_struct *ata_wq;
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("Library module for ATA devices");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+/**
+ * ata_tf_load - send taskfile registers to host controller
+ * @ap: Port to which output is sent
+ * @tf: ATA taskfile register set
+ *
+ * Outputs ATA taskfile to standard ATA host controller.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+static void ata_tf_load_pio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+ if (tf->ctl != ap->last_ctl) {
+ outb(tf->ctl, ioaddr->ctl_addr);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ outb(tf->hob_feature, ioaddr->feature_addr);
+ outb(tf->hob_nsect, ioaddr->nsect_addr);
+ outb(tf->hob_lbal, ioaddr->lbal_addr);
+ outb(tf->hob_lbam, ioaddr->lbam_addr);
+ outb(tf->hob_lbah, ioaddr->lbah_addr);
+ VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+ tf->hob_feature,
+ tf->hob_nsect,
+ tf->hob_lbal,
+ tf->hob_lbam,
+ tf->hob_lbah);
+ }
+
+ if (is_addr) {
+ outb(tf->feature, ioaddr->feature_addr);
+ outb(tf->nsect, ioaddr->nsect_addr);
+ outb(tf->lbal, ioaddr->lbal_addr);
+ outb(tf->lbam, ioaddr->lbam_addr);
+ outb(tf->lbah, ioaddr->lbah_addr);
+ VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+ tf->feature,
+ tf->nsect,
+ tf->lbal,
+ tf->lbam,
+ tf->lbah);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE) {
+ outb(tf->device, ioaddr->device_addr);
+ VPRINTK("device 0x%X\n", tf->device);
+ }
+
+ ata_wait_idle(ap);
+}
+
+/**
+ * ata_tf_load_mmio - send taskfile registers to host controller
+ * @ap: Port to which output is sent
+ * @tf: ATA taskfile register set
+ *
+ * Outputs ATA taskfile to standard ATA host controller using MMIO.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+static void ata_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+ if (tf->ctl != ap->last_ctl) {
+ writeb(tf->ctl, (void __iomem *) ap->ioaddr.ctl_addr);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ writeb(tf->hob_feature, (void __iomem *) ioaddr->feature_addr);
+ writeb(tf->hob_nsect, (void __iomem *) ioaddr->nsect_addr);
+ writeb(tf->hob_lbal, (void __iomem *) ioaddr->lbal_addr);
+ writeb(tf->hob_lbam, (void __iomem *) ioaddr->lbam_addr);
+ writeb(tf->hob_lbah, (void __iomem *) ioaddr->lbah_addr);
+ VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+ tf->hob_feature,
+ tf->hob_nsect,
+ tf->hob_lbal,
+ tf->hob_lbam,
+ tf->hob_lbah);
+ }
+
+ if (is_addr) {
+ writeb(tf->feature, (void __iomem *) ioaddr->feature_addr);
+ writeb(tf->nsect, (void __iomem *) ioaddr->nsect_addr);
+ writeb(tf->lbal, (void __iomem *) ioaddr->lbal_addr);
+ writeb(tf->lbam, (void __iomem *) ioaddr->lbam_addr);
+ writeb(tf->lbah, (void __iomem *) ioaddr->lbah_addr);
+ VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+ tf->feature,
+ tf->nsect,
+ tf->lbal,
+ tf->lbam,
+ tf->lbah);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE) {
+ writeb(tf->device, (void __iomem *) ioaddr->device_addr);
+ VPRINTK("device 0x%X\n", tf->device);
+ }
+
+ ata_wait_idle(ap);
+}
+
+void ata_tf_load(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ if (ap->flags & ATA_FLAG_MMIO)
+ ata_tf_load_mmio(ap, tf);
+ else
+ ata_tf_load_pio(ap, tf);
+}
+
+/**
+ * ata_exec_command - issue ATA command to host controller
+ * @ap: port to which command is being issued
+ * @tf: ATA taskfile register set
+ *
+ * Issues PIO/MMIO write to ATA command register, with proper
+ * synchronization with interrupt handler / other threads.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_exec_command_pio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
+
+ outb(tf->command, ap->ioaddr.command_addr);
+ ata_pause(ap);
+}
+
+
+/**
+ * ata_exec_command_mmio - issue ATA command to host controller
+ * @ap: port to which command is being issued
+ * @tf: ATA taskfile register set
+ *
+ * Issues MMIO write to ATA command register, with proper
+ * synchronization with interrupt handler / other threads.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
+
+ writeb(tf->command, (void __iomem *) ap->ioaddr.command_addr);
+ ata_pause(ap);
+}
+
+void ata_exec_command(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ if (ap->flags & ATA_FLAG_MMIO)
+ ata_exec_command_mmio(ap, tf);
+ else
+ ata_exec_command_pio(ap, tf);
+}
+
+/**
+ * ata_exec - issue ATA command to host controller
+ * @ap: port to which command is being issued
+ * @tf: ATA taskfile register set
+ *
+ * Issues PIO/MMIO write to ATA command register, with proper
+ * synchronization with interrupt handler / other threads.
+ *
+ * LOCKING:
+ * Obtains host_set lock.
+ */
+
+static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ unsigned long flags;
+
+ DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ ap->ops->exec_command(ap, tf);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+}
+
+/**
+ * ata_tf_to_host - issue ATA taskfile to host controller
+ * @ap: port to which command is being issued
+ * @tf: ATA taskfile register set
+ *
+ * Issues ATA taskfile register set to ATA host controller,
+ * with proper synchronization with interrupt handler and
+ * other threads.
+ *
+ * LOCKING:
+ * Obtains host_set lock.
+ */
+
+static void ata_tf_to_host(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ ap->ops->tf_load(ap, tf);
+
+ ata_exec(ap, tf);
+}
+
+/**
+ * ata_tf_to_host_nolock - issue ATA taskfile to host controller
+ * @ap: port to which command is being issued
+ * @tf: ATA taskfile register set
+ *
+ * Issues ATA taskfile register set to ATA host controller,
+ * with proper synchronization with interrupt handler and
+ * other threads.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ ap->ops->tf_load(ap, tf);
+ ap->ops->exec_command(ap, tf);
+}
+
+/**
+ * ata_tf_read - input device's ATA taskfile shadow registers
+ * @ap: Port from which input is read
+ * @tf: ATA taskfile register set for storing input
+ *
+ * Reads ATA taskfile registers for currently-selected device
+ * into @tf.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+static void ata_tf_read_pio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ tf->nsect = inb(ioaddr->nsect_addr);
+ tf->lbal = inb(ioaddr->lbal_addr);
+ tf->lbam = inb(ioaddr->lbam_addr);
+ tf->lbah = inb(ioaddr->lbah_addr);
+ tf->device = inb(ioaddr->device_addr);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ outb(tf->ctl | ATA_HOB, ioaddr->ctl_addr);
+ tf->hob_feature = inb(ioaddr->error_addr);
+ tf->hob_nsect = inb(ioaddr->nsect_addr);
+ tf->hob_lbal = inb(ioaddr->lbal_addr);
+ tf->hob_lbam = inb(ioaddr->lbam_addr);
+ tf->hob_lbah = inb(ioaddr->lbah_addr);
+ }
+}
+
+/**
+ * ata_tf_read_mmio - input device's ATA taskfile shadow registers
+ * @ap: Port from which input is read
+ * @tf: ATA taskfile register set for storing input
+ *
+ * Reads ATA taskfile registers for currently-selected device
+ * into @tf via MMIO.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+static void ata_tf_read_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ tf->nsect = readb((void __iomem *)ioaddr->nsect_addr);
+ tf->lbal = readb((void __iomem *)ioaddr->lbal_addr);
+ tf->lbam = readb((void __iomem *)ioaddr->lbam_addr);
+ tf->lbah = readb((void __iomem *)ioaddr->lbah_addr);
+ tf->device = readb((void __iomem *)ioaddr->device_addr);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ writeb(tf->ctl | ATA_HOB, (void __iomem *) ap->ioaddr.ctl_addr);
+ tf->hob_feature = readb((void __iomem *)ioaddr->error_addr);
+ tf->hob_nsect = readb((void __iomem *)ioaddr->nsect_addr);
+ tf->hob_lbal = readb((void __iomem *)ioaddr->lbal_addr);
+ tf->hob_lbam = readb((void __iomem *)ioaddr->lbam_addr);
+ tf->hob_lbah = readb((void __iomem *)ioaddr->lbah_addr);
+ }
+}
+
+void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ if (ap->flags & ATA_FLAG_MMIO)
+ ata_tf_read_mmio(ap, tf);
+ else
+ ata_tf_read_pio(ap, tf);
+}
+
+/**
+ * ata_check_status_pio - Read device status reg & clear interrupt
+ * @ap: port where the device is
+ *
+ * Reads ATA taskfile status register for currently-selected device
+ * and return it's value. This also clears pending interrupts
+ * from this device
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static u8 ata_check_status_pio(struct ata_port *ap)
+{
+ return inb(ap->ioaddr.status_addr);
+}
+
+/**
+ * ata_check_status_mmio - Read device status reg & clear interrupt
+ * @ap: port where the device is
+ *
+ * Reads ATA taskfile status register for currently-selected device
+ * via MMIO and return it's value. This also clears pending interrupts
+ * from this device
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static u8 ata_check_status_mmio(struct ata_port *ap)
+{
+ return readb((void __iomem *) ap->ioaddr.status_addr);
+}
+
+u8 ata_check_status(struct ata_port *ap)
+{
+ if (ap->flags & ATA_FLAG_MMIO)
+ return ata_check_status_mmio(ap);
+ return ata_check_status_pio(ap);
+}
+
+u8 ata_altstatus(struct ata_port *ap)
+{
+ if (ap->ops->check_altstatus)
+ return ap->ops->check_altstatus(ap);
+
+ if (ap->flags & ATA_FLAG_MMIO)
+ return readb((void __iomem *)ap->ioaddr.altstatus_addr);
+ return inb(ap->ioaddr.altstatus_addr);
+}
+
+u8 ata_chk_err(struct ata_port *ap)
+{
+ if (ap->ops->check_err)
+ return ap->ops->check_err(ap);
+
+ if (ap->flags & ATA_FLAG_MMIO) {
+ return readb((void __iomem *) ap->ioaddr.error_addr);
+ }
+ return inb(ap->ioaddr.error_addr);
+}
+
+/**
+ * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure
+ * @tf: Taskfile to convert
+ * @fis: Buffer into which data will output
+ * @pmp: Port multiplier port
+ *
+ * Converts a standard ATA taskfile to a Serial ATA
+ * FIS structure (Register - Host to Device).
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+void ata_tf_to_fis(struct ata_taskfile *tf, u8 *fis, u8 pmp)
+{
+ fis[0] = 0x27; /* Register - Host to Device FIS */
+ fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number,
+ bit 7 indicates Command FIS */
+ fis[2] = tf->command;
+ fis[3] = tf->feature;
+
+ fis[4] = tf->lbal;
+ fis[5] = tf->lbam;
+ fis[6] = tf->lbah;
+ fis[7] = tf->device;
+
+ fis[8] = tf->hob_lbal;
+ fis[9] = tf->hob_lbam;
+ fis[10] = tf->hob_lbah;
+ fis[11] = tf->hob_feature;
+
+ fis[12] = tf->nsect;
+ fis[13] = tf->hob_nsect;
+ fis[14] = 0;
+ fis[15] = tf->ctl;
+
+ fis[16] = 0;
+ fis[17] = 0;
+ fis[18] = 0;
+ fis[19] = 0;
+}
+
+/**
+ * ata_tf_from_fis - Convert SATA FIS to ATA taskfile
+ * @fis: Buffer from which data will be input
+ * @tf: Taskfile to output
+ *
+ * Converts a standard ATA taskfile to a Serial ATA
+ * FIS structure (Register - Host to Device).
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+
+void ata_tf_from_fis(u8 *fis, struct ata_taskfile *tf)
+{
+ tf->command = fis[2]; /* status */
+ tf->feature = fis[3]; /* error */
+
+ tf->lbal = fis[4];
+ tf->lbam = fis[5];
+ tf->lbah = fis[6];
+ tf->device = fis[7];
+
+ tf->hob_lbal = fis[8];
+ tf->hob_lbam = fis[9];
+ tf->hob_lbah = fis[10];
+
+ tf->nsect = fis[12];
+ tf->hob_nsect = fis[13];
+}
+
+/**
+ * ata_prot_to_cmd - determine which read/write opcodes to use
+ * @protocol: ATA_PROT_xxx taskfile protocol
+ * @lba48: true is lba48 is present
+ *
+ * Given necessary input, determine which read/write commands
+ * to use to transfer data.
+ *
+ * LOCKING:
+ * None.
+ */
+static int ata_prot_to_cmd(int protocol, int lba48)
+{
+ int rcmd = 0, wcmd = 0;
+
+ switch (protocol) {
+ case ATA_PROT_PIO:
+ if (lba48) {
+ rcmd = ATA_CMD_PIO_READ_EXT;
+ wcmd = ATA_CMD_PIO_WRITE_EXT;
+ } else {
+ rcmd = ATA_CMD_PIO_READ;
+ wcmd = ATA_CMD_PIO_WRITE;
+ }
+ break;
+
+ case ATA_PROT_DMA:
+ if (lba48) {
+ rcmd = ATA_CMD_READ_EXT;
+ wcmd = ATA_CMD_WRITE_EXT;
+ } else {
+ rcmd = ATA_CMD_READ;
+ wcmd = ATA_CMD_WRITE;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return rcmd | (wcmd << 8);
+}
+
+/**
+ * ata_dev_set_protocol - set taskfile protocol and r/w commands
+ * @dev: device to examine and configure
+ *
+ * Examine the device configuration, after we have
+ * read the identify-device page and configured the
+ * data transfer mode. Set internal state related to
+ * the ATA taskfile protocol (pio, pio mult, dma, etc.)
+ * and calculate the proper read/write commands to use.
+ *
+ * LOCKING:
+ * caller.
+ */
+static void ata_dev_set_protocol(struct ata_device *dev)
+{
+ int pio = (dev->flags & ATA_DFLAG_PIO);
+ int lba48 = (dev->flags & ATA_DFLAG_LBA48);
+ int proto, cmd;
+
+ if (pio)
+ proto = dev->xfer_protocol = ATA_PROT_PIO;
+ else
+ proto = dev->xfer_protocol = ATA_PROT_DMA;
+
+ cmd = ata_prot_to_cmd(proto, lba48);
+ if (cmd < 0)
+ BUG();
+
+ dev->read_cmd = cmd & 0xff;
+ dev->write_cmd = (cmd >> 8) & 0xff;
+}
+
+static const char * xfer_mode_str[] = {
+ "UDMA/16",
+ "UDMA/25",
+ "UDMA/33",
+ "UDMA/44",
+ "UDMA/66",
+ "UDMA/100",
+ "UDMA/133",
+ "UDMA7",
+ "MWDMA0",
+ "MWDMA1",
+ "MWDMA2",
+ "PIO0",
+ "PIO1",
+ "PIO2",
+ "PIO3",
+ "PIO4",
+};
+
+/**
+ * ata_udma_string - convert UDMA bit offset to string
+ * @mask: mask of bits supported; only highest bit counts.
+ *
+ * Determine string which represents the highest speed
+ * (highest bit in @udma_mask).
+ *
+ * LOCKING:
+ * None.
+ *
+ * RETURNS:
+ * Constant C string representing highest speed listed in
+ * @udma_mask, or the constant C string "<n/a>".
+ */
+
+static const char *ata_mode_string(unsigned int mask)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ if (mask & (1 << i))
+ goto out;
+ for (i = ATA_SHIFT_MWDMA + 2; i >= ATA_SHIFT_MWDMA; i--)
+ if (mask & (1 << i))
+ goto out;
+ for (i = ATA_SHIFT_PIO + 4; i >= ATA_SHIFT_PIO; i--)
+ if (mask & (1 << i))
+ goto out;
+
+ return "<n/a>";
+
+out:
+ return xfer_mode_str[i];
+}
+
+/**
+ * ata_pio_devchk - PATA device presence detection
+ * @ap: ATA channel to examine
+ * @device: Device to examine (starting at zero)
+ *
+ * This technique was originally described in
+ * Hale Landis's ATADRVR (www.ata-atapi.com), and
+ * later found its way into the ATA/ATAPI spec.
+ *
+ * Write a pattern to the ATA shadow registers,
+ * and if a device is present, it will respond by
+ * correctly storing and echoing back the
+ * ATA shadow register contents.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+static unsigned int ata_pio_devchk(struct ata_port *ap,
+ unsigned int device)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u8 nsect, lbal;
+
+ ap->ops->dev_select(ap, device);
+
+ outb(0x55, ioaddr->nsect_addr);
+ outb(0xaa, ioaddr->lbal_addr);
+
+ outb(0xaa, ioaddr->nsect_addr);
+ outb(0x55, ioaddr->lbal_addr);
+
+ outb(0x55, ioaddr->nsect_addr);
+ outb(0xaa, ioaddr->lbal_addr);
+
+ nsect = inb(ioaddr->nsect_addr);
+ lbal = inb(ioaddr->lbal_addr);
+
+ if ((nsect == 0x55) && (lbal == 0xaa))
+ return 1; /* we found a device */
+
+ return 0; /* nothing found */
+}
+
+/**
+ * ata_mmio_devchk - PATA device presence detection
+ * @ap: ATA channel to examine
+ * @device: Device to examine (starting at zero)
+ *
+ * This technique was originally described in
+ * Hale Landis's ATADRVR (www.ata-atapi.com), and
+ * later found its way into the ATA/ATAPI spec.
+ *
+ * Write a pattern to the ATA shadow registers,
+ * and if a device is present, it will respond by
+ * correctly storing and echoing back the
+ * ATA shadow register contents.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+static unsigned int ata_mmio_devchk(struct ata_port *ap,
+ unsigned int device)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u8 nsect, lbal;
+
+ ap->ops->dev_select(ap, device);
+
+ writeb(0x55, (void __iomem *) ioaddr->nsect_addr);
+ writeb(0xaa, (void __iomem *) ioaddr->lbal_addr);
+
+ writeb(0xaa, (void __iomem *) ioaddr->nsect_addr);
+ writeb(0x55, (void __iomem *) ioaddr->lbal_addr);
+
+ writeb(0x55, (void __iomem *) ioaddr->nsect_addr);
+ writeb(0xaa, (void __iomem *) ioaddr->lbal_addr);
+
+ nsect = readb((void __iomem *) ioaddr->nsect_addr);
+ lbal = readb((void __iomem *) ioaddr->lbal_addr);
+
+ if ((nsect == 0x55) && (lbal == 0xaa))
+ return 1; /* we found a device */
+
+ return 0; /* nothing found */
+}
+
+/**
+ * ata_devchk - PATA device presence detection
+ * @ap: ATA channel to examine
+ * @device: Device to examine (starting at zero)
+ *
+ * Dispatch ATA device presence detection, depending
+ * on whether we are using PIO or MMIO to talk to the
+ * ATA shadow registers.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+static unsigned int ata_devchk(struct ata_port *ap,
+ unsigned int device)
+{
+ if (ap->flags & ATA_FLAG_MMIO)
+ return ata_mmio_devchk(ap, device);
+ return ata_pio_devchk(ap, device);
+}
+
+/**
+ * ata_dev_classify - determine device type based on ATA-spec signature
+ * @tf: ATA taskfile register set for device to be identified
+ *
+ * Determine from taskfile register contents whether a device is
+ * ATA or ATAPI, as per "Signature and persistence" section
+ * of ATA/PI spec (volume 1, sect 5.14).
+ *
+ * LOCKING:
+ * None.
+ *
+ * RETURNS:
+ * Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN
+ * the event of failure.
+ */
+
+unsigned int ata_dev_classify(struct ata_taskfile *tf)
+{
+ /* Apple's open source Darwin code hints that some devices only
+ * put a proper signature into the LBA mid/high registers,
+ * So, we only check those. It's sufficient for uniqueness.
+ */
+
+ if (((tf->lbam == 0) && (tf->lbah == 0)) ||
+ ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) {
+ DPRINTK("found ATA device by sig\n");
+ return ATA_DEV_ATA;
+ }
+
+ if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) ||
+ ((tf->lbam == 0x69) && (tf->lbah == 0x96))) {
+ DPRINTK("found ATAPI device by sig\n");
+ return ATA_DEV_ATAPI;
+ }
+
+ DPRINTK("unknown device\n");
+ return ATA_DEV_UNKNOWN;
+}
+
+/**
+ * ata_dev_try_classify - Parse returned ATA device signature
+ * @ap: ATA channel to examine
+ * @device: Device to examine (starting at zero)
+ *
+ * After an event -- SRST, E.D.D., or SATA COMRESET -- occurs,
+ * an ATA/ATAPI-defined set of values is placed in the ATA
+ * shadow registers, indicating the results of device detection
+ * and diagnostics.
+ *
+ * Select the ATA device, and read the values from the ATA shadow
+ * registers. Then parse according to the Error register value,
+ * and the spec-defined values examined by ata_dev_classify().
+ *
+ * LOCKING:
+ * caller.
+ */
+
+static u8 ata_dev_try_classify(struct ata_port *ap, unsigned int device)
+{
+ struct ata_device *dev = &ap->device[device];
+ struct ata_taskfile tf;
+ unsigned int class;
+ u8 err;
+
+ ap->ops->dev_select(ap, device);
+
+ memset(&tf, 0, sizeof(tf));
+
+ err = ata_chk_err(ap);
+ ap->ops->tf_read(ap, &tf);
+
+ dev->class = ATA_DEV_NONE;
+
+ /* see if device passed diags */
+ if (err == 1)
+ /* do nothing */ ;
+ else if ((device == 0) && (err == 0x81))
+ /* do nothing */ ;
+ else
+ return err;
+
+ /* determine if device if ATA or ATAPI */
+ class = ata_dev_classify(&tf);
+ if (class == ATA_DEV_UNKNOWN)
+ return err;
+ if ((class == ATA_DEV_ATA) && (ata_chk_status(ap) == 0))
+ return err;
+
+ dev->class = class;
+
+ return err;
+}
+
+/**
+ * ata_dev_id_string - Convert IDENTIFY DEVICE page into string
+ * @id: IDENTIFY DEVICE results we will examine
+ * @s: string into which data is output
+ * @ofs: offset into identify device page
+ * @len: length of string to return. must be an even number.
+ *
+ * The strings in the IDENTIFY DEVICE page are broken up into
+ * 16-bit chunks. Run through the string, and output each
+ * 8-bit chunk linearly, regardless of platform.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+void ata_dev_id_string(u16 *id, unsigned char *s,
+ unsigned int ofs, unsigned int len)
+{
+ unsigned int c;
+
+ while (len > 0) {
+ c = id[ofs] >> 8;
+ *s = c;
+ s++;
+
+ c = id[ofs] & 0xff;
+ *s = c;
+ s++;
+
+ ofs++;
+ len -= 2;
+ }
+}
+
+void ata_noop_dev_select (struct ata_port *ap, unsigned int device)
+{
+}
+
+/**
+ * ata_std_dev_select - Select device 0/1 on ATA bus
+ * @ap: ATA channel to manipulate
+ * @device: ATA device (numbered from zero) to select
+ *
+ * Use the method defined in the ATA specification to
+ * make either device 0, or device 1, active on the
+ * ATA channel.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+void ata_std_dev_select (struct ata_port *ap, unsigned int device)
+{
+ u8 tmp;
+
+ if (device == 0)
+ tmp = ATA_DEVICE_OBS;
+ else
+ tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+ if (ap->flags & ATA_FLAG_MMIO) {
+ writeb(tmp, (void __iomem *) ap->ioaddr.device_addr);
+ } else {
+ outb(tmp, ap->ioaddr.device_addr);
+ }
+ ata_pause(ap); /* needed; also flushes, for mmio */
+}
+
+/**
+ * ata_dev_select - Select device 0/1 on ATA bus
+ * @ap: ATA channel to manipulate
+ * @device: ATA device (numbered from zero) to select
+ * @wait: non-zero to wait for Status register BSY bit to clear
+ * @can_sleep: non-zero if context allows sleeping
+ *
+ * Use the method defined in the ATA specification to
+ * make either device 0, or device 1, active on the
+ * ATA channel.
+ *
+ * This is a high-level version of ata_std_dev_select(),
+ * which additionally provides the services of inserting
+ * the proper pauses and status polling, where needed.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+void ata_dev_select(struct ata_port *ap, unsigned int device,
+ unsigned int wait, unsigned int can_sleep)
+{
+ VPRINTK("ENTER, ata%u: device %u, wait %u\n",
+ ap->id, device, wait);
+
+ if (wait)
+ ata_wait_idle(ap);
+
+ ap->ops->dev_select(ap, device);
+
+ if (wait) {
+ if (can_sleep && ap->device[device].class == ATA_DEV_ATAPI)
+ msleep(150);
+ ata_wait_idle(ap);
+ }
+}
+
+/**
+ * ata_dump_id - IDENTIFY DEVICE info debugging output
+ * @dev: Device whose IDENTIFY DEVICE page we will dump
+ *
+ * Dump selected 16-bit words from a detected device's
+ * IDENTIFY PAGE page.
+ *
+ * LOCKING:
+ * caller.
+ */
+
+static inline void ata_dump_id(struct ata_device *dev)
+{
+ DPRINTK("49==0x%04x "
+ "53==0x%04x "
+ "63==0x%04x "
+ "64==0x%04x "
+ "75==0x%04x \n",
+ dev->id[49],
+ dev->id[53],
+ dev->id[63],
+ dev->id[64],
+ dev->id[75]);
+ DPRINTK("80==0x%04x "
+ "81==0x%04x "
+ "82==0x%04x "
+ "83==0x%04x "
+ "84==0x%04x \n",
+ dev->id[80],
+ dev->id[81],
+ dev->id[82],
+ dev->id[83],
+ dev->id[84]);
+ DPRINTK("88==0x%04x "
+ "93==0x%04x\n",
+ dev->id[88],
+ dev->id[93]);
+}
+
+/**
+ * ata_dev_identify - obtain IDENTIFY x DEVICE page
+ * @ap: port on which device we wish to probe resides
+ * @device: device bus address, starting at zero
+ *
+ * Following bus reset, we issue the IDENTIFY [PACKET] DEVICE
+ * command, and read back the 512-byte device information page.
+ * The device information page is fed to us via the standard
+ * PIO-IN protocol, but we hand-code it here. (TODO: investigate
+ * using standard PIO-IN paths)
+ *
+ * After reading the device information page, we use several
+ * bits of information from it to initialize data structures
+ * that will be used during the lifetime of the ata_device.
+ * Other data from the info page is used to disqualify certain
+ * older ATA devices we do not wish to support.
+ *
+ * LOCKING:
+ * Inherited from caller. Some functions called by this function
+ * obtain the host_set lock.
+ */
+
+static void ata_dev_identify(struct ata_port *ap, unsigned int device)
+{
+ struct ata_device *dev = &ap->device[device];
+ unsigned int i;
+ u16 tmp;
+ unsigned long xfer_modes;
+ u8 status;
+ unsigned int using_edd;
+ DECLARE_COMPLETION(wait);
+ struct ata_queued_cmd *qc;
+ unsigned long flags;
+ int rc;
+
+ if (!ata_dev_present(dev)) {
+ DPRINTK("ENTER/EXIT (host %u, dev %u) -- nodev\n",
+ ap->id, device);
+ return;
+ }
+
+ if (ap->flags & (ATA_FLAG_SRST | ATA_FLAG_SATA_RESET))
+ using_edd = 0;
+ else
+ using_edd = 1;
+
+ DPRINTK("ENTER, host %u, dev %u\n", ap->id, device);
+
+ assert (dev->class == ATA_DEV_ATA || dev->class == ATA_DEV_ATAPI ||
+ dev->class == ATA_DEV_NONE);
+
+ ata_dev_select(ap, device, 1, 1); /* select device 0/1 */
+
+ qc = ata_qc_new_init(ap, dev);
+ BUG_ON(qc == NULL);
+
+ ata_sg_init_one(qc, dev->id, sizeof(dev->id));
+ qc->dma_dir = DMA_FROM_DEVICE;
+ qc->tf.protocol = ATA_PROT_PIO;
+ qc->nsect = 1;
+
+retry:
+ if (dev->class == ATA_DEV_ATA) {
+ qc->tf.command = ATA_CMD_ID_ATA;
+ DPRINTK("do ATA identify\n");
+ } else {
+ qc->tf.command = ATA_CMD_ID_ATAPI;
+ DPRINTK("do ATAPI identify\n");
+ }
+
+ qc->waiting = &wait;
+ qc->complete_fn = ata_qc_complete_noop;
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ rc = ata_qc_issue(qc);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ if (rc)
+ goto err_out;
+ else
+ wait_for_completion(&wait);
+
+ status = ata_chk_status(ap);
+ if (status & ATA_ERR) {
+ /*
+ * arg! EDD works for all test cases, but seems to return
+ * the ATA signature for some ATAPI devices. Until the
+ * reason for this is found and fixed, we fix up the mess
+ * here. If IDENTIFY DEVICE returns command aborted
+ * (as ATAPI devices do), then we issue an
+ * IDENTIFY PACKET DEVICE.
+ *
+ * ATA software reset (SRST, the default) does not appear
+ * to have this problem.
+ */
+ if ((using_edd) && (qc->tf.command == ATA_CMD_ID_ATA)) {
+ u8 err = ata_chk_err(ap);
+ if (err & ATA_ABORTED) {
+ dev->class = ATA_DEV_ATAPI;
+ qc->cursg = 0;
+ qc->cursg_ofs = 0;
+ qc->cursect = 0;
+ qc->nsect = 1;
+ goto retry;
+ }
+ }
+ goto err_out;
+ }
+
+ swap_buf_le16(dev->id, ATA_ID_WORDS);
+
+ /* print device capabilities */
+ printk(KERN_DEBUG "ata%u: dev %u cfg "
+ "49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x\n",
+ ap->id, device, dev->id[49],
+ dev->id[82], dev->id[83], dev->id[84],
+ dev->id[85], dev->id[86], dev->id[87],
+ dev->id[88]);
+
+ /*
+ * common ATA, ATAPI feature tests
+ */
+
+ /* we require LBA and DMA support (bits 8 & 9 of word 49) */
+ if (!ata_id_has_dma(dev->id) || !ata_id_has_lba(dev->id)) {
+ printk(KERN_DEBUG "ata%u: no dma/lba\n", ap->id);
+ goto err_out_nosup;
+ }
+
+ /* quick-n-dirty find max transfer mode; for printk only */
+ xfer_modes = dev->id[ATA_ID_UDMA_MODES];
+ if (!xfer_modes)
+ xfer_modes = (dev->id[ATA_ID_MWDMA_MODES]) << ATA_SHIFT_MWDMA;
+ if (!xfer_modes) {
+ xfer_modes = (dev->id[ATA_ID_PIO_MODES]) << (ATA_SHIFT_PIO + 3);
+ xfer_modes |= (0x7 << ATA_SHIFT_PIO);
+ }
+
+ ata_dump_id(dev);
+
+ /* ATA-specific feature tests */
+ if (dev->class == ATA_DEV_ATA) {
+ if (!ata_id_is_ata(dev->id)) /* sanity check */
+ goto err_out_nosup;
+
+ tmp = dev->id[ATA_ID_MAJOR_VER];
+ for (i = 14; i >= 1; i--)
+ if (tmp & (1 << i))
+ break;
+
+ /* we require at least ATA-3 */
+ if (i < 3) {
+ printk(KERN_DEBUG "ata%u: no ATA-3\n", ap->id);
+ goto err_out_nosup;
+ }
+
+ if (ata_id_has_lba48(dev->id)) {
+ dev->flags |= ATA_DFLAG_LBA48;
+ dev->n_sectors = ata_id_u64(dev->id, 100);
+ } else {
+ dev->n_sectors = ata_id_u32(dev->id, 60);
+ }
+
+ ap->host->max_cmd_len = 16;
+
+ /* print device info to dmesg */
+ printk(KERN_INFO "ata%u: dev %u ATA, max %s, %Lu sectors:%s\n",
+ ap->id, device,
+ ata_mode_string(xfer_modes),
+ (unsigned long long)dev->n_sectors,
+ dev->flags & ATA_DFLAG_LBA48 ? " lba48" : "");
+ }
+
+ /* ATAPI-specific feature tests */
+ else {
+ if (ata_id_is_ata(dev->id)) /* sanity check */
+ goto err_out_nosup;
+
+ rc = atapi_cdb_len(dev->id);
+ if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
+ printk(KERN_WARNING "ata%u: unsupported CDB len\n", ap->id);
+ goto err_out_nosup;
+ }
+ ap->cdb_len = (unsigned int) rc;
+ ap->host->max_cmd_len = (unsigned char) ap->cdb_len;
+
+ /* print device info to dmesg */
+ printk(KERN_INFO "ata%u: dev %u ATAPI, max %s\n",
+ ap->id, device,
+ ata_mode_string(xfer_modes));
+ }
+
+ DPRINTK("EXIT, drv_stat = 0x%x\n", ata_chk_status(ap));
+ return;
+
+err_out_nosup:
+ printk(KERN_WARNING "ata%u: dev %u not supported, ignoring\n",
+ ap->id, device);
+err_out:
+ dev->class++; /* converts ATA_DEV_xxx into ATA_DEV_xxx_UNSUP */
+ DPRINTK("EXIT, err\n");
+}
+
+/**
+ * ata_bus_probe - Reset and probe ATA bus
+ * @ap: Bus to probe
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static int ata_bus_probe(struct ata_port *ap)
+{
+ unsigned int i, found = 0;
+
+ ap->ops->phy_reset(ap);
+ if (ap->flags & ATA_FLAG_PORT_DISABLED)
+ goto err_out;
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ ata_dev_identify(ap, i);
+ if (ata_dev_present(&ap->device[i])) {
+ found = 1;
+ if (ap->ops->dev_config)
+ ap->ops->dev_config(ap, &ap->device[i]);
+ }
+ }
+
+ if ((!found) || (ap->flags & ATA_FLAG_PORT_DISABLED))
+ goto err_out_disable;
+
+ ata_set_mode(ap);
+ if (ap->flags & ATA_FLAG_PORT_DISABLED)
+ goto err_out_disable;
+
+ return 0;
+
+err_out_disable:
+ ap->ops->port_disable(ap);
+err_out:
+ return -1;
+}
+
+/**
+ * ata_port_probe -
+ * @ap:
+ *
+ * LOCKING:
+ */
+
+void ata_port_probe(struct ata_port *ap)
+{
+ ap->flags &= ~ATA_FLAG_PORT_DISABLED;
+}
+
+/**
+ * __sata_phy_reset -
+ * @ap:
+ *
+ * LOCKING:
+ *
+ */
+void __sata_phy_reset(struct ata_port *ap)
+{
+ u32 sstatus;
+ unsigned long timeout = jiffies + (HZ * 5);
+
+ if (ap->flags & ATA_FLAG_SATA_RESET) {
+ scr_write(ap, SCR_CONTROL, 0x301); /* issue phy wake/reset */
+ scr_read(ap, SCR_STATUS); /* dummy read; flush */
+ udelay(400); /* FIXME: a guess */
+ }
+ scr_write(ap, SCR_CONTROL, 0x300); /* issue phy wake/clear reset */
+
+ /* wait for phy to become ready, if necessary */
+ do {
+ msleep(200);
+ sstatus = scr_read(ap, SCR_STATUS);
+ if ((sstatus & 0xf) != 1)
+ break;
+ } while (time_before(jiffies, timeout));
+
+ /* TODO: phy layer with polling, timeouts, etc. */
+ if (sata_dev_present(ap))
+ ata_port_probe(ap);
+ else {
+ sstatus = scr_read(ap, SCR_STATUS);
+ printk(KERN_INFO "ata%u: no device found (phy stat %08x)\n",
+ ap->id, sstatus);
+ ata_port_disable(ap);
+ }
+
+ if (ap->flags & ATA_FLAG_PORT_DISABLED)
+ return;
+
+ if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
+ ata_port_disable(ap);
+ return;
+ }
+
+ ap->cbl = ATA_CBL_SATA;
+}
+
+/**
+ * __sata_phy_reset -
+ * @ap:
+ *
+ * LOCKING:
+ *
+ */
+void sata_phy_reset(struct ata_port *ap)
+{
+ __sata_phy_reset(ap);
+ if (ap->flags & ATA_FLAG_PORT_DISABLED)
+ return;
+ ata_bus_reset(ap);
+}
+
+/**
+ * ata_port_disable -
+ * @ap:
+ *
+ * LOCKING:
+ */
+
+void ata_port_disable(struct ata_port *ap)
+{
+ ap->device[0].class = ATA_DEV_NONE;
+ ap->device[1].class = ATA_DEV_NONE;
+ ap->flags |= ATA_FLAG_PORT_DISABLED;
+}
+
+static struct {
+ unsigned int shift;
+ u8 base;
+} xfer_mode_classes[] = {
+ { ATA_SHIFT_UDMA, XFER_UDMA_0 },
+ { ATA_SHIFT_MWDMA, XFER_MW_DMA_0 },
+ { ATA_SHIFT_PIO, XFER_PIO_0 },
+};
+
+static inline u8 base_from_shift(unsigned int shift)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xfer_mode_classes); i++)
+ if (xfer_mode_classes[i].shift == shift)
+ return xfer_mode_classes[i].base;
+
+ return 0xff;
+}
+
+static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+{
+ int ofs, idx;
+ u8 base;
+
+ if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
+ return;
+
+ if (dev->xfer_shift == ATA_SHIFT_PIO)
+ dev->flags |= ATA_DFLAG_PIO;
+
+ ata_dev_set_xfermode(ap, dev);
+
+ base = base_from_shift(dev->xfer_shift);
+ ofs = dev->xfer_mode - base;
+ idx = ofs + dev->xfer_shift;
+ WARN_ON(idx >= ARRAY_SIZE(xfer_mode_str));
+
+ DPRINTK("idx=%d xfer_shift=%u, xfer_mode=0x%x, base=0x%x, offset=%d\n",
+ idx, dev->xfer_shift, (int)dev->xfer_mode, (int)base, ofs);
+
+ printk(KERN_INFO "ata%u: dev %u configured for %s\n",
+ ap->id, dev->devno, xfer_mode_str[idx]);
+}
+
+static int ata_host_set_pio(struct ata_port *ap)
+{
+ unsigned int mask;
+ int x, i;
+ u8 base, xfer_mode;
+
+ mask = ata_get_mode_mask(ap, ATA_SHIFT_PIO);
+ x = fgb(mask);
+ if (x < 0) {
+ printk(KERN_WARNING "ata%u: no PIO support\n", ap->id);
+ return -1;
+ }
+
+ base = base_from_shift(ATA_SHIFT_PIO);
+ xfer_mode = base + x;
+
+ DPRINTK("base 0x%x xfer_mode 0x%x mask 0x%x x %d\n",
+ (int)base, (int)xfer_mode, mask, x);
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->device[i];
+ if (ata_dev_present(dev)) {
+ dev->pio_mode = xfer_mode;
+ dev->xfer_mode = xfer_mode;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ if (ap->ops->set_piomode)
+ ap->ops->set_piomode(ap, dev);
+ }
+ }
+
+ return 0;
+}
+
+static void ata_host_set_dma(struct ata_port *ap, u8 xfer_mode,
+ unsigned int xfer_shift)
+{
+ int i;
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++) {
+ struct ata_device *dev = &ap->device[i];
+ if (ata_dev_present(dev)) {
+ dev->dma_mode = xfer_mode;
+ dev->xfer_mode = xfer_mode;
+ dev->xfer_shift = xfer_shift;
+ if (ap->ops->set_dmamode)
+ ap->ops->set_dmamode(ap, dev);
+ }
+ }
+}
+
+/**
+ * ata_set_mode - Program timings and issue SET FEATURES - XFER
+ * @ap: port on which timings will be programmed
+ *
+ * LOCKING:
+ *
+ */
+static void ata_set_mode(struct ata_port *ap)
+{
+ unsigned int i, xfer_shift;
+ u8 xfer_mode;
+ int rc;
+
+ /* step 1: always set host PIO timings */
+ rc = ata_host_set_pio(ap);
+ if (rc)
+ goto err_out;
+
+ /* step 2: choose the best data xfer mode */
+ xfer_mode = xfer_shift = 0;
+ rc = ata_choose_xfer_mode(ap, &xfer_mode, &xfer_shift);
+ if (rc)
+ goto err_out;
+
+ /* step 3: if that xfer mode isn't PIO, set host DMA timings */
+ if (xfer_shift != ATA_SHIFT_PIO)
+ ata_host_set_dma(ap, xfer_mode, xfer_shift);
+
+ /* step 4: update devices' xfer mode */
+ ata_dev_set_mode(ap, &ap->device[0]);
+ ata_dev_set_mode(ap, &ap->device[1]);
+
+ if (ap->flags & ATA_FLAG_PORT_DISABLED)
+ return;
+
+ if (ap->ops->post_set_mode)
+ ap->ops->post_set_mode(ap);
+
+ for (i = 0; i < 2; i++) {
+ struct ata_device *dev = &ap->device[i];
+ ata_dev_set_protocol(dev);
+ }
+
+ return;
+
+err_out:
+ ata_port_disable(ap);
+}
+
+/**
+ * ata_busy_sleep - sleep until BSY clears, or timeout
+ * @ap: port containing status register to be polled
+ * @tmout_pat: impatience timeout
+ * @tmout: overall timeout
+ *
+ * LOCKING:
+ *
+ */
+
+static unsigned int ata_busy_sleep (struct ata_port *ap,
+ unsigned long tmout_pat,
+ unsigned long tmout)
+{
+ unsigned long timer_start, timeout;
+ u8 status;
+
+ status = ata_busy_wait(ap, ATA_BUSY, 300);
+ timer_start = jiffies;
+ timeout = timer_start + tmout_pat;
+ while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
+ msleep(50);
+ status = ata_busy_wait(ap, ATA_BUSY, 3);
+ }
+
+ if (status & ATA_BUSY)
+ printk(KERN_WARNING "ata%u is slow to respond, "
+ "please be patient\n", ap->id);
+
+ timeout = timer_start + tmout;
+ while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
+ msleep(50);
+ status = ata_chk_status(ap);
+ }
+
+ if (status & ATA_BUSY) {
+ printk(KERN_ERR "ata%u failed to respond (%lu secs)\n",
+ ap->id, tmout / HZ);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int dev0 = devmask & (1 << 0);
+ unsigned int dev1 = devmask & (1 << 1);
+ unsigned long timeout;
+
+ /* if device 0 was found in ata_devchk, wait for its
+ * BSY bit to clear
+ */
+ if (dev0)
+ ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+
+ /* if device 1 was found in ata_devchk, wait for
+ * register access, then wait for BSY to clear
+ */
+ timeout = jiffies + ATA_TMOUT_BOOT;
+ while (dev1) {
+ u8 nsect, lbal;
+
+ ap->ops->dev_select(ap, 1);
+ if (ap->flags & ATA_FLAG_MMIO) {
+ nsect = readb((void __iomem *) ioaddr->nsect_addr);
+ lbal = readb((void __iomem *) ioaddr->lbal_addr);
+ } else {
+ nsect = inb(ioaddr->nsect_addr);
+ lbal = inb(ioaddr->lbal_addr);
+ }
+ if ((nsect == 1) && (lbal == 1))
+ break;
+ if (time_after(jiffies, timeout)) {
+ dev1 = 0;
+ break;
+ }
+ msleep(50); /* give drive a breather */
+ }
+ if (dev1)
+ ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+
+ /* is all this really necessary? */
+ ap->ops->dev_select(ap, 0);
+ if (dev1)
+ ap->ops->dev_select(ap, 1);
+ if (dev0)
+ ap->ops->dev_select(ap, 0);
+}
+
+/**
+ * ata_bus_edd -
+ * @ap:
+ *
+ * LOCKING:
+ *
+ */
+
+static unsigned int ata_bus_edd(struct ata_port *ap)
+{
+ struct ata_taskfile tf;
+
+ /* set up execute-device-diag (bus reset) taskfile */
+ /* also, take interrupts to a known state (disabled) */
+ DPRINTK("execute-device-diag\n");
+ ata_tf_init(ap, &tf, 0);
+ tf.ctl |= ATA_NIEN;
+ tf.command = ATA_CMD_EDD;
+ tf.protocol = ATA_PROT_NODATA;
+
+ /* do bus reset */
+ ata_tf_to_host(ap, &tf);
+
+ /* spec says at least 2ms. but who knows with those
+ * crazy ATAPI devices...
+ */
+ msleep(150);
+
+ return ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+}
+
+static unsigned int ata_bus_softreset(struct ata_port *ap,
+ unsigned int devmask)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ DPRINTK("ata%u: bus reset via SRST\n", ap->id);
+
+ /* software reset. causes dev0 to be selected */
+ if (ap->flags & ATA_FLAG_MMIO) {
+ writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ writeb(ap->ctl | ATA_SRST, (void __iomem *) ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+ } else {
+ outb(ap->ctl, ioaddr->ctl_addr);
+ udelay(10);
+ outb(ap->ctl | ATA_SRST, ioaddr->ctl_addr);
+ udelay(10);
+ outb(ap->ctl, ioaddr->ctl_addr);
+ }
+
+ /* spec mandates ">= 2ms" before checking status.
+ * We wait 150ms, because that was the magic delay used for
+ * ATAPI devices in Hale Landis's ATADRVR, for the period of time
+ * between when the ATA command register is written, and then
+ * status is checked. Because waiting for "a while" before
+ * checking status is fine, post SRST, we perform this magic
+ * delay here as well.
+ */
+ msleep(150);
+
+ ata_bus_post_reset(ap, devmask);
+
+ return 0;
+}
+
+/**
+ * ata_bus_reset - reset host port and associated ATA channel
+ * @ap: port to reset
+ *
+ * This is typically the first time we actually start issuing
+ * commands to the ATA channel. We wait for BSY to clear, then
+ * issue EXECUTE DEVICE DIAGNOSTIC command, polling for its
+ * result. Determine what devices, if any, are on the channel
+ * by looking at the device 0/1 error register. Look at the signature
+ * stored in each device's taskfile registers, to determine if
+ * the device is ATA or ATAPI.
+ *
+ * LOCKING:
+ * Inherited from caller. Some functions called by this function
+ * obtain the host_set lock.
+ *
+ * SIDE EFFECTS:
+ * Sets ATA_FLAG_PORT_DISABLED if bus reset fails.
+ */
+
+void ata_bus_reset(struct ata_port *ap)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+ u8 err;
+ unsigned int dev0, dev1 = 0, rc = 0, devmask = 0;
+
+ DPRINTK("ENTER, host %u, port %u\n", ap->id, ap->port_no);
+
+ /* determine if device 0/1 are present */
+ if (ap->flags & ATA_FLAG_SATA_RESET)
+ dev0 = 1;
+ else {
+ dev0 = ata_devchk(ap, 0);
+ if (slave_possible)
+ dev1 = ata_devchk(ap, 1);
+ }
+
+ if (dev0)
+ devmask |= (1 << 0);
+ if (dev1)
+ devmask |= (1 << 1);
+
+ /* select device 0 again */
+ ap->ops->dev_select(ap, 0);
+
+ /* issue bus reset */
+ if (ap->flags & ATA_FLAG_SRST)
+ rc = ata_bus_softreset(ap, devmask);
+ else if ((ap->flags & ATA_FLAG_SATA_RESET) == 0) {
+ /* set up device control */
+ if (ap->flags & ATA_FLAG_MMIO)
+ writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+ else
+ outb(ap->ctl, ioaddr->ctl_addr);
+ rc = ata_bus_edd(ap);
+ }
+
+ if (rc)
+ goto err_out;
+
+ /*
+ * determine by signature whether we have ATA or ATAPI devices
+ */
+ err = ata_dev_try_classify(ap, 0);
+ if ((slave_possible) && (err != 0x81))
+ ata_dev_try_classify(ap, 1);
+
+ /* re-enable interrupts */
+ if (ap->ioaddr.ctl_addr) /* FIXME: hack. create a hook instead */
+ ata_irq_on(ap);
+
+ /* is double-select really necessary? */
+ if (ap->device[1].class != ATA_DEV_NONE)
+ ap->ops->dev_select(ap, 1);
+ if (ap->device[0].class != ATA_DEV_NONE)
+ ap->ops->dev_select(ap, 0);
+
+ /* if no devices were detected, disable this port */
+ if ((ap->device[0].class == ATA_DEV_NONE) &&
+ (ap->device[1].class == ATA_DEV_NONE))
+ goto err_out;
+
+ if (ap->flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST)) {
+ /* set up device control for ATA_FLAG_SATA_RESET */
+ if (ap->flags & ATA_FLAG_MMIO)
+ writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
+ else
+ outb(ap->ctl, ioaddr->ctl_addr);
+ }
+
+ DPRINTK("EXIT\n");
+ return;
+
+err_out:
+ printk(KERN_ERR "ata%u: disabling port\n", ap->id);
+ ap->ops->port_disable(ap);
+
+ DPRINTK("EXIT\n");
+}
+
+static void ata_pr_blacklisted(struct ata_port *ap, struct ata_device *dev)
+{
+ printk(KERN_WARNING "ata%u: dev %u is on DMA blacklist, disabling DMA\n",
+ ap->id, dev->devno);
+}
+
+static const char * ata_dma_blacklist [] = {
+ "WDC AC11000H",
+ "WDC AC22100H",
+ "WDC AC32500H",
+ "WDC AC33100H",
+ "WDC AC31600H",
+ "WDC AC32100H",
+ "WDC AC23200L",
+ "Compaq CRD-8241B",
+ "CRD-8400B",
+ "CRD-8480B",
+ "CRD-8482B",
+ "CRD-84",
+ "SanDisk SDP3B",
+ "SanDisk SDP3B-64",
+ "SANYO CD-ROM CRD",
+ "HITACHI CDR-8",
+ "HITACHI CDR-8335",
+ "HITACHI CDR-8435",
+ "Toshiba CD-ROM XM-6202B",
+ "CD-532E-A",
+ "E-IDE CD-ROM CR-840",
+ "CD-ROM Drive/F5A",
+ "WPI CDD-820",
+ "SAMSUNG CD-ROM SC-148C",
+ "SAMSUNG CD-ROM SC",
+ "SanDisk SDP3B-64",
+ "SAMSUNG CD-ROM SN-124",
+ "ATAPI CD-ROM DRIVE 40X MAXIMUM",
+ "_NEC DV5800A",
+};
+
+static int ata_dma_blacklisted(struct ata_port *ap, struct ata_device *dev)
+{
+ unsigned char model_num[40];
+ char *s;
+ unsigned int len;
+ int i;
+
+ ata_dev_id_string(dev->id, model_num, ATA_ID_PROD_OFS,
+ sizeof(model_num));
+ s = &model_num[0];
+ len = strnlen(s, sizeof(model_num));
+
+ /* ATAPI specifies that empty space is blank-filled; remove blanks */
+ while ((len > 0) && (s[len - 1] == ' ')) {
+ len--;
+ s[len] = 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ata_dma_blacklist); i++)
+ if (!strncmp(ata_dma_blacklist[i], s, len))
+ return 1;
+
+ return 0;
+}
+
+static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift)
+{
+ struct ata_device *master, *slave;
+ unsigned int mask;
+
+ master = &ap->device[0];
+ slave = &ap->device[1];
+
+ assert (ata_dev_present(master) || ata_dev_present(slave));
+
+ if (shift == ATA_SHIFT_UDMA) {
+ mask = ap->udma_mask;
+ if (ata_dev_present(master)) {
+ mask &= (master->id[ATA_ID_UDMA_MODES] & 0xff);
+ if (ata_dma_blacklisted(ap, master)) {
+ mask = 0;
+ ata_pr_blacklisted(ap, master);
+ }
+ }
+ if (ata_dev_present(slave)) {
+ mask &= (slave->id[ATA_ID_UDMA_MODES] & 0xff);
+ if (ata_dma_blacklisted(ap, slave)) {
+ mask = 0;
+ ata_pr_blacklisted(ap, slave);
+ }
+ }
+ }
+ else if (shift == ATA_SHIFT_MWDMA) {
+ mask = ap->mwdma_mask;
+ if (ata_dev_present(master)) {
+ mask &= (master->id[ATA_ID_MWDMA_MODES] & 0x07);
+ if (ata_dma_blacklisted(ap, master)) {
+ mask = 0;
+ ata_pr_blacklisted(ap, master);
+ }
+ }
+ if (ata_dev_present(slave)) {
+ mask &= (slave->id[ATA_ID_MWDMA_MODES] & 0x07);
+ if (ata_dma_blacklisted(ap, slave)) {
+ mask = 0;
+ ata_pr_blacklisted(ap, slave);
+ }
+ }
+ }
+ else if (shift == ATA_SHIFT_PIO) {
+ mask = ap->pio_mask;
+ if (ata_dev_present(master)) {
+ /* spec doesn't return explicit support for
+ * PIO0-2, so we fake it
+ */
+ u16 tmp_mode = master->id[ATA_ID_PIO_MODES] & 0x03;
+ tmp_mode <<= 3;
+ tmp_mode |= 0x7;
+ mask &= tmp_mode;
+ }
+ if (ata_dev_present(slave)) {
+ /* spec doesn't return explicit support for
+ * PIO0-2, so we fake it
+ */
+ u16 tmp_mode = slave->id[ATA_ID_PIO_MODES] & 0x03;
+ tmp_mode <<= 3;
+ tmp_mode |= 0x7;
+ mask &= tmp_mode;
+ }
+ }
+ else {
+ mask = 0xffffffff; /* shut up compiler warning */
+ BUG();
+ }
+
+ return mask;
+}
+
+/* find greatest bit */
+static int fgb(u32 bitmap)
+{
+ unsigned int i;
+ int x = -1;
+
+ for (i = 0; i < 32; i++)
+ if (bitmap & (1 << i))
+ x = i;
+
+ return x;
+}
+
+/**
+ * ata_choose_xfer_mode - attempt to find best transfer mode
+ * @ap: Port for which an xfer mode will be selected
+ * @xfer_mode_out: (output) SET FEATURES - XFER MODE code
+ * @xfer_shift_out: (output) bit shift that selects this mode
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ * Zero on success, negative on error.
+ */
+
+static int ata_choose_xfer_mode(struct ata_port *ap,
+ u8 *xfer_mode_out,
+ unsigned int *xfer_shift_out)
+{
+ unsigned int mask, shift;
+ int x, i;
+
+ for (i = 0; i < ARRAY_SIZE(xfer_mode_classes); i++) {
+ shift = xfer_mode_classes[i].shift;
+ mask = ata_get_mode_mask(ap, shift);
+
+ x = fgb(mask);
+ if (x >= 0) {
+ *xfer_mode_out = xfer_mode_classes[i].base + x;
+ *xfer_shift_out = shift;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * ata_dev_set_xfermode - Issue SET FEATURES - XFER MODE command
+ * @ap: Port associated with device @dev
+ * @dev: Device to which command will be sent
+ *
+ * LOCKING:
+ */
+
+static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev)
+{
+ DECLARE_COMPLETION(wait);
+ struct ata_queued_cmd *qc;
+ int rc;
+ unsigned long flags;
+
+ /* set up set-features taskfile */
+ DPRINTK("set features - xfer mode\n");
+
+ qc = ata_qc_new_init(ap, dev);
+ BUG_ON(qc == NULL);
+
+ qc->tf.command = ATA_CMD_SET_FEATURES;
+ qc->tf.feature = SETFEATURES_XFER;
+ qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ qc->tf.protocol = ATA_PROT_NODATA;
+ qc->tf.nsect = dev->xfer_mode;
+
+ qc->waiting = &wait;
+ qc->complete_fn = ata_qc_complete_noop;
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ rc = ata_qc_issue(qc);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ if (rc)
+ ata_port_disable(ap);
+ else
+ wait_for_completion(&wait);
+
+ DPRINTK("EXIT\n");
+}
+
+/**
+ * ata_sg_clean -
+ * @qc:
+ *
+ * LOCKING:
+ */
+
+static void ata_sg_clean(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct scatterlist *sg = qc->sg;
+ int dir = qc->dma_dir;
+
+ assert(qc->flags & ATA_QCFLAG_DMAMAP);
+ assert(sg != NULL);
+
+ if (qc->flags & ATA_QCFLAG_SINGLE)
+ assert(qc->n_elem == 1);
+
+ DPRINTK("unmapping %u sg elements\n", qc->n_elem);
+
+ if (qc->flags & ATA_QCFLAG_SG)
+ dma_unmap_sg(ap->host_set->dev, sg, qc->n_elem, dir);
+ else
+ dma_unmap_single(ap->host_set->dev, sg_dma_address(&sg[0]),
+ sg_dma_len(&sg[0]), dir);
+
+ qc->flags &= ~ATA_QCFLAG_DMAMAP;
+ qc->sg = NULL;
+}
+
+/**
+ * ata_fill_sg - Fill PCI IDE PRD table
+ * @qc: Metadata associated with taskfile to be transferred
+ *
+ * LOCKING:
+ *
+ */
+static void ata_fill_sg(struct ata_queued_cmd *qc)
+{
+ struct scatterlist *sg = qc->sg;
+ struct ata_port *ap = qc->ap;
+ unsigned int idx, nelem;
+
+ assert(sg != NULL);
+ assert(qc->n_elem > 0);
+
+ idx = 0;
+ for (nelem = qc->n_elem; nelem; nelem--,sg++) {
+ u32 addr, offset;
+ u32 sg_len, len;
+
+ /* determine if physical DMA addr spans 64K boundary.
+ * Note h/w doesn't support 64-bit, so we unconditionally
+ * truncate dma_addr_t to u32.
+ */
+ addr = (u32) sg_dma_address(sg);
+ sg_len = sg_dma_len(sg);
+
+ while (sg_len) {
+ offset = addr & 0xffff;
+ len = sg_len;
+ if ((offset + sg_len) > 0x10000)
+ len = 0x10000 - offset;
+
+ ap->prd[idx].addr = cpu_to_le32(addr);
+ ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+
+ idx++;
+ sg_len -= len;
+ addr += len;
+ }
+ }
+
+ if (idx)
+ ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+}
+/**
+ * ata_check_atapi_dma - Check whether ATAPI DMA can be supported
+ * @qc: Metadata associated with taskfile to check
+ *
+ * LOCKING:
+ * RETURNS: 0 when ATAPI DMA can be used
+ * nonzero otherwise
+ */
+int ata_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ int rc = 0; /* Assume ATAPI DMA is OK by default */
+
+ if (ap->ops->check_atapi_dma)
+ rc = ap->ops->check_atapi_dma(qc);
+
+ return rc;
+}
+/**
+ * ata_qc_prep - Prepare taskfile for submission
+ * @qc: Metadata associated with taskfile to be prepared
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+void ata_qc_prep(struct ata_queued_cmd *qc)
+{
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ ata_fill_sg(qc);
+}
+
+void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen)
+{
+ struct scatterlist *sg;
+
+ qc->flags |= ATA_QCFLAG_SINGLE;
+
+ memset(&qc->sgent, 0, sizeof(qc->sgent));
+ qc->sg = &qc->sgent;
+ qc->n_elem = 1;
+ qc->buf_virt = buf;
+
+ sg = qc->sg;
+ sg->page = virt_to_page(buf);
+ sg->offset = (unsigned long) buf & ~PAGE_MASK;
+ sg_dma_len(sg) = buflen;
+}
+
+void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg,
+ unsigned int n_elem)
+{
+ qc->flags |= ATA_QCFLAG_SG;
+ qc->sg = sg;
+ qc->n_elem = n_elem;
+}
+
+/**
+ * ata_sg_setup_one -
+ * @qc:
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ *
+ */
+
+static int ata_sg_setup_one(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ int dir = qc->dma_dir;
+ struct scatterlist *sg = qc->sg;
+ dma_addr_t dma_address;
+
+ dma_address = dma_map_single(ap->host_set->dev, qc->buf_virt,
+ sg_dma_len(sg), dir);
+ if (dma_mapping_error(dma_address))
+ return -1;
+
+ sg_dma_address(sg) = dma_address;
+
+ DPRINTK("mapped buffer of %d bytes for %s\n", sg_dma_len(sg),
+ qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+ return 0;
+}
+
+/**
+ * ata_sg_setup -
+ * @qc:
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ *
+ */
+
+static int ata_sg_setup(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct scatterlist *sg = qc->sg;
+ int n_elem, dir;
+
+ VPRINTK("ENTER, ata%u\n", ap->id);
+ assert(qc->flags & ATA_QCFLAG_SG);
+
+ dir = qc->dma_dir;
+ n_elem = dma_map_sg(ap->host_set->dev, sg, qc->n_elem, dir);
+ if (n_elem < 1)
+ return -1;
+
+ DPRINTK("%d sg elements mapped\n", n_elem);
+
+ qc->n_elem = n_elem;
+
+ return 0;
+}
+
+/**
+ * ata_pio_poll -
+ * @ap:
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ *
+ */
+
+static unsigned long ata_pio_poll(struct ata_port *ap)
+{
+ u8 status;
+ unsigned int poll_state = PIO_ST_UNKNOWN;
+ unsigned int reg_state = PIO_ST_UNKNOWN;
+ const unsigned int tmout_state = PIO_ST_TMOUT;
+
+ switch (ap->pio_task_state) {
+ case PIO_ST:
+ case PIO_ST_POLL:
+ poll_state = PIO_ST_POLL;
+ reg_state = PIO_ST;
+ break;
+ case PIO_ST_LAST:
+ case PIO_ST_LAST_POLL:
+ poll_state = PIO_ST_LAST_POLL;
+ reg_state = PIO_ST_LAST;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ status = ata_chk_status(ap);
+ if (status & ATA_BUSY) {
+ if (time_after(jiffies, ap->pio_task_timeout)) {
+ ap->pio_task_state = tmout_state;
+ return 0;
+ }
+ ap->pio_task_state = poll_state;
+ return ATA_SHORT_PAUSE;
+ }
+
+ ap->pio_task_state = reg_state;
+ return 0;
+}
+
+/**
+ * ata_pio_complete -
+ * @ap:
+ *
+ * LOCKING:
+ */
+
+static void ata_pio_complete (struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc;
+ u8 drv_stat;
+
+ /*
+ * This is purely hueristic. This is a fast path.
+ * Sometimes when we enter, BSY will be cleared in
+ * a chk-status or two. If not, the drive is probably seeking
+ * or something. Snooze for a couple msecs, then
+ * chk-status again. If still busy, fall back to
+ * PIO_ST_POLL state.
+ */
+ drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10);
+ if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+ msleep(2);
+ drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10);
+ if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
+ ap->pio_task_state = PIO_ST_LAST_POLL;
+ ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO;
+ return;
+ }
+ }
+
+ drv_stat = ata_wait_idle(ap);
+ if (!ata_ok(drv_stat)) {
+ ap->pio_task_state = PIO_ST_ERR;
+ return;
+ }
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ assert(qc != NULL);
+
+ ap->pio_task_state = PIO_ST_IDLE;
+
+ ata_irq_on(ap);
+
+ ata_qc_complete(qc, drv_stat);
+}
+
+void swap_buf_le16(u16 *buf, unsigned int buf_words)
+{
+#ifdef __BIG_ENDIAN
+ unsigned int i;
+
+ for (i = 0; i < buf_words; i++)
+ buf[i] = le16_to_cpu(buf[i]);
+#endif /* __BIG_ENDIAN */
+}
+
+static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf,
+ unsigned int buflen, int write_data)
+{
+ unsigned int i;
+ unsigned int words = buflen >> 1;
+ u16 *buf16 = (u16 *) buf;
+ void __iomem *mmio = (void __iomem *)ap->ioaddr.data_addr;
+
+ if (write_data) {
+ for (i = 0; i < words; i++)
+ writew(le16_to_cpu(buf16[i]), mmio);
+ } else {
+ for (i = 0; i < words; i++)
+ buf16[i] = cpu_to_le16(readw(mmio));
+ }
+}
+
+static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf,
+ unsigned int buflen, int write_data)
+{
+ unsigned int dwords = buflen >> 1;
+
+ if (write_data)
+ outsw(ap->ioaddr.data_addr, buf, dwords);
+ else
+ insw(ap->ioaddr.data_addr, buf, dwords);
+}
+
+static void ata_data_xfer(struct ata_port *ap, unsigned char *buf,
+ unsigned int buflen, int do_write)
+{
+ if (ap->flags & ATA_FLAG_MMIO)
+ ata_mmio_data_xfer(ap, buf, buflen, do_write);
+ else
+ ata_pio_data_xfer(ap, buf, buflen, do_write);
+}
+
+static void ata_pio_sector(struct ata_queued_cmd *qc)
+{
+ int do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
+ struct scatterlist *sg = qc->sg;
+ struct ata_port *ap = qc->ap;
+ struct page *page;
+ unsigned int offset;
+ unsigned char *buf;
+
+ if (qc->cursect == (qc->nsect - 1))
+ ap->pio_task_state = PIO_ST_LAST;
+
+ page = sg[qc->cursg].page;
+ offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE;
+
+ /* get the current page and offset */
+ page = nth_page(page, (offset >> PAGE_SHIFT));
+ offset %= PAGE_SIZE;
+
+ buf = kmap(page) + offset;
+
+ qc->cursect++;
+ qc->cursg_ofs++;
+
+ if ((qc->cursg_ofs * ATA_SECT_SIZE) == sg_dma_len(&sg[qc->cursg])) {
+ qc->cursg++;
+ qc->cursg_ofs = 0;
+ }
+
+ DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+ /* do the actual data transfer */
+ do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
+ ata_data_xfer(ap, buf, ATA_SECT_SIZE, do_write);
+
+ kunmap(page);
+}
+
+static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
+{
+ int do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
+ struct scatterlist *sg = qc->sg;
+ struct ata_port *ap = qc->ap;
+ struct page *page;
+ unsigned char *buf;
+ unsigned int offset, count;
+
+ if (qc->curbytes == qc->nbytes - bytes)
+ ap->pio_task_state = PIO_ST_LAST;
+
+next_sg:
+ sg = &qc->sg[qc->cursg];
+
+next_page:
+ page = sg->page;
+ offset = sg->offset + qc->cursg_ofs;
+
+ /* get the current page and offset */
+ page = nth_page(page, (offset >> PAGE_SHIFT));
+ offset %= PAGE_SIZE;
+
+ count = min(sg_dma_len(sg) - qc->cursg_ofs, bytes);
+
+ /* don't cross page boundaries */
+ count = min(count, (unsigned int)PAGE_SIZE - offset);
+
+ buf = kmap(page) + offset;
+
+ bytes -= count;
+ qc->curbytes += count;
+ qc->cursg_ofs += count;
+
+ if (qc->cursg_ofs == sg_dma_len(sg)) {
+ qc->cursg++;
+ qc->cursg_ofs = 0;
+ }
+
+ DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read");
+
+ /* do the actual data transfer */
+ ata_data_xfer(ap, buf, count, do_write);
+
+ kunmap(page);
+
+ if (bytes) {
+ if (qc->cursg_ofs < sg_dma_len(sg))
+ goto next_page;
+ goto next_sg;
+ }
+}
+
+static void atapi_pio_bytes(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ata_device *dev = qc->dev;
+ unsigned int ireason, bc_lo, bc_hi, bytes;
+ int i_write, do_write = (qc->tf.flags & ATA_TFLAG_WRITE) ? 1 : 0;
+
+ ap->ops->tf_read(ap, &qc->tf);
+ ireason = qc->tf.nsect;
+ bc_lo = qc->tf.lbam;
+ bc_hi = qc->tf.lbah;
+ bytes = (bc_hi << 8) | bc_lo;
+
+ /* shall be cleared to zero, indicating xfer of data */
+ if (ireason & (1 << 0))
+ goto err_out;
+
+ /* make sure transfer direction matches expected */
+ i_write = ((ireason & (1 << 1)) == 0) ? 1 : 0;
+ if (do_write != i_write)
+ goto err_out;
+
+ __atapi_pio_bytes(qc, bytes);
+
+ return;
+
+err_out:
+ printk(KERN_INFO "ata%u: dev %u: ATAPI check failed\n",
+ ap->id, dev->devno);
+ ap->pio_task_state = PIO_ST_ERR;
+}
+
+/**
+ * ata_pio_sector -
+ * @ap:
+ *
+ * LOCKING:
+ */
+
+static void ata_pio_block(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc;
+ u8 status;
+
+ /*
+ * This is purely hueristic. This is a fast path.
+ * Sometimes when we enter, BSY will be cleared in
+ * a chk-status or two. If not, the drive is probably seeking
+ * or something. Snooze for a couple msecs, then
+ * chk-status again. If still busy, fall back to
+ * PIO_ST_POLL state.
+ */
+ status = ata_busy_wait(ap, ATA_BUSY, 5);
+ if (status & ATA_BUSY) {
+ msleep(2);
+ status = ata_busy_wait(ap, ATA_BUSY, 10);
+ if (status & ATA_BUSY) {
+ ap->pio_task_state = PIO_ST_POLL;
+ ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO;
+ return;
+ }
+ }
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ assert(qc != NULL);
+
+ if (is_atapi_taskfile(&qc->tf)) {
+ /* no more data to transfer or unsupported ATAPI command */
+ if ((status & ATA_DRQ) == 0) {
+ ap->pio_task_state = PIO_ST_IDLE;
+
+ ata_irq_on(ap);
+
+ ata_qc_complete(qc, status);
+ return;
+ }
+
+ atapi_pio_bytes(qc);
+ } else {
+ /* handle BSY=0, DRQ=0 as error */
+ if ((status & ATA_DRQ) == 0) {
+ ap->pio_task_state = PIO_ST_ERR;
+ return;
+ }
+
+ ata_pio_sector(qc);
+ }
+}
+
+static void ata_pio_error(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc;
+ u8 drv_stat;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ assert(qc != NULL);
+
+ drv_stat = ata_chk_status(ap);
+ printk(KERN_WARNING "ata%u: PIO error, drv_stat 0x%x\n",
+ ap->id, drv_stat);
+
+ ap->pio_task_state = PIO_ST_IDLE;
+
+ ata_irq_on(ap);
+
+ ata_qc_complete(qc, drv_stat | ATA_ERR);
+}
+
+static void ata_pio_task(void *_data)
+{
+ struct ata_port *ap = _data;
+ unsigned long timeout = 0;
+
+ switch (ap->pio_task_state) {
+ case PIO_ST_IDLE:
+ return;
+
+ case PIO_ST:
+ ata_pio_block(ap);
+ break;
+
+ case PIO_ST_LAST:
+ ata_pio_complete(ap);
+ break;
+
+ case PIO_ST_POLL:
+ case PIO_ST_LAST_POLL:
+ timeout = ata_pio_poll(ap);
+ break;
+
+ case PIO_ST_TMOUT:
+ case PIO_ST_ERR:
+ ata_pio_error(ap);
+ return;
+ }
+
+ if (timeout)
+ queue_delayed_work(ata_wq, &ap->pio_task,
+ timeout);
+ else
+ queue_work(ata_wq, &ap->pio_task);
+}
+
+static void atapi_request_sense(struct ata_port *ap, struct ata_device *dev,
+ struct scsi_cmnd *cmd)
+{
+ DECLARE_COMPLETION(wait);
+ struct ata_queued_cmd *qc;
+ unsigned long flags;
+ int rc;
+
+ DPRINTK("ATAPI request sense\n");
+
+ qc = ata_qc_new_init(ap, dev);
+ BUG_ON(qc == NULL);
+
+ /* FIXME: is this needed? */
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer));
+ qc->dma_dir = DMA_FROM_DEVICE;
+
+ memset(&qc->cdb, 0, sizeof(ap->cdb_len));
+ qc->cdb[0] = REQUEST_SENSE;
+ qc->cdb[4] = SCSI_SENSE_BUFFERSIZE;
+
+ qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ qc->tf.command = ATA_CMD_PACKET;
+
+ qc->tf.protocol = ATA_PROT_ATAPI;
+ qc->tf.lbam = (8 * 1024) & 0xff;
+ qc->tf.lbah = (8 * 1024) >> 8;
+ qc->nbytes = SCSI_SENSE_BUFFERSIZE;
+
+ qc->waiting = &wait;
+ qc->complete_fn = ata_qc_complete_noop;
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ rc = ata_qc_issue(qc);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ if (rc)
+ ata_port_disable(ap);
+ else
+ wait_for_completion(&wait);
+
+ DPRINTK("EXIT\n");
+}
+
+/**
+ * ata_qc_timeout - Handle timeout of queued command
+ * @qc: Command that timed out
+ *
+ * Some part of the kernel (currently, only the SCSI layer)
+ * has noticed that the active command on port @ap has not
+ * completed after a specified length of time. Handle this
+ * condition by disabling DMA (if necessary) and completing
+ * transactions, with error if necessary.
+ *
+ * This also handles the case of the "lost interrupt", where
+ * for some reason (possibly hardware bug, possibly driver bug)
+ * an interrupt was not delivered to the driver, even though the
+ * transaction completed successfully.
+ *
+ * LOCKING:
+ */
+
+static void ata_qc_timeout(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ata_device *dev = qc->dev;
+ u8 host_stat = 0, drv_stat;
+
+ DPRINTK("ENTER\n");
+
+ /* FIXME: doesn't this conflict with timeout handling? */
+ if (qc->dev->class == ATA_DEV_ATAPI && qc->scsicmd) {
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ if (!scsi_eh_eflags_chk(cmd, SCSI_EH_CANCEL_CMD)) {
+
+ /* finish completing original command */
+ __ata_qc_complete(qc);
+
+ atapi_request_sense(ap, dev, cmd);
+
+ cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+ scsi_finish_command(cmd);
+
+ goto out;
+ }
+ }
+
+ /* hack alert! We cannot use the supplied completion
+ * function from inside the ->eh_strategy_handler() thread.
+ * libata is the only user of ->eh_strategy_handler() in
+ * any kernel, so the default scsi_done() assumes it is
+ * not being called from the SCSI EH.
+ */
+ qc->scsidone = scsi_finish_command;
+
+ switch (qc->tf.protocol) {
+
+ case ATA_PROT_DMA:
+ case ATA_PROT_ATAPI_DMA:
+ host_stat = ap->ops->bmdma_status(ap);
+
+ /* before we do anything else, clear DMA-Start bit */
+ ap->ops->bmdma_stop(ap);
+
+ /* fall through */
+
+ default:
+ ata_altstatus(ap);
+ drv_stat = ata_chk_status(ap);
+
+ /* ack bmdma irq events */
+ ap->ops->irq_clear(ap);
+
+ printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n",
+ ap->id, qc->tf.command, drv_stat, host_stat);
+
+ /* complete taskfile transaction */
+ ata_qc_complete(qc, drv_stat);
+ break;
+ }
+out:
+ DPRINTK("EXIT\n");
+}
+
+/**
+ * ata_eng_timeout - Handle timeout of queued command
+ * @ap: Port on which timed-out command is active
+ *
+ * Some part of the kernel (currently, only the SCSI layer)
+ * has noticed that the active command on port @ap has not
+ * completed after a specified length of time. Handle this
+ * condition by disabling DMA (if necessary) and completing
+ * transactions, with error if necessary.
+ *
+ * This also handles the case of the "lost interrupt", where
+ * for some reason (possibly hardware bug, possibly driver bug)
+ * an interrupt was not delivered to the driver, even though the
+ * transaction completed successfully.
+ *
+ * LOCKING:
+ * Inherited from SCSI layer (none, can sleep)
+ */
+
+void ata_eng_timeout(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc;
+
+ DPRINTK("ENTER\n");
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (!qc) {
+ printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+ ap->id);
+ goto out;
+ }
+
+ ata_qc_timeout(qc);
+
+out:
+ DPRINTK("EXIT\n");
+}
+
+/**
+ * ata_qc_new - Request an available ATA command, for queueing
+ * @ap: Port associated with device @dev
+ * @dev: Device from whom we request an available command structure
+ *
+ * LOCKING:
+ */
+
+static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc = NULL;
+ unsigned int i;
+
+ for (i = 0; i < ATA_MAX_QUEUE; i++)
+ if (!test_and_set_bit(i, &ap->qactive)) {
+ qc = ata_qc_from_tag(ap, i);
+ break;
+ }
+
+ if (qc)
+ qc->tag = i;
+
+ return qc;
+}
+
+/**
+ * ata_qc_new_init - Request an available ATA command, and initialize it
+ * @ap: Port associated with device @dev
+ * @dev: Device from whom we request an available command structure
+ *
+ * LOCKING:
+ */
+
+struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
+ struct ata_device *dev)
+{
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_new(ap);
+ if (qc) {
+ qc->sg = NULL;
+ qc->flags = 0;
+ qc->scsicmd = NULL;
+ qc->ap = ap;
+ qc->dev = dev;
+ qc->cursect = qc->cursg = qc->cursg_ofs = 0;
+ qc->nsect = 0;
+ qc->nbytes = qc->curbytes = 0;
+
+ ata_tf_init(ap, &qc->tf, dev->devno);
+
+ if (dev->flags & ATA_DFLAG_LBA48)
+ qc->tf.flags |= ATA_TFLAG_LBA48;
+ }
+
+ return qc;
+}
+
+static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ return 0;
+}
+
+static void __ata_qc_complete(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ unsigned int tag, do_clear = 0;
+
+ qc->flags = 0;
+ tag = qc->tag;
+ if (likely(ata_tag_valid(tag))) {
+ if (tag == ap->active_tag)
+ ap->active_tag = ATA_TAG_POISON;
+ qc->tag = ATA_TAG_POISON;
+ do_clear = 1;
+ }
+
+ if (qc->waiting) {
+ struct completion *waiting = qc->waiting;
+ qc->waiting = NULL;
+ complete(waiting);
+ }
+
+ if (likely(do_clear))
+ clear_bit(tag, &ap->qactive);
+}
+
+/**
+ * ata_qc_free - free unused ata_queued_cmd
+ * @qc: Command to complete
+ *
+ * Designed to free unused ata_queued_cmd object
+ * in case something prevents using it.
+ *
+ * LOCKING:
+ *
+ */
+void ata_qc_free(struct ata_queued_cmd *qc)
+{
+ assert(qc != NULL); /* ata_qc_from_tag _might_ return NULL */
+ assert(qc->waiting == NULL); /* nothing should be waiting */
+
+ __ata_qc_complete(qc);
+}
+
+/**
+ * ata_qc_complete - Complete an active ATA command
+ * @qc: Command to complete
+ * @drv_stat: ATA status register contents
+ *
+ * LOCKING:
+ *
+ */
+
+void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ int rc;
+
+ assert(qc != NULL); /* ata_qc_from_tag _might_ return NULL */
+ assert(qc->flags & ATA_QCFLAG_ACTIVE);
+
+ if (likely(qc->flags & ATA_QCFLAG_DMAMAP))
+ ata_sg_clean(qc);
+
+ /* call completion callback */
+ rc = qc->complete_fn(qc, drv_stat);
+
+ /* if callback indicates not to complete command (non-zero),
+ * return immediately
+ */
+ if (rc != 0)
+ return;
+
+ __ata_qc_complete(qc);
+
+ VPRINTK("EXIT\n");
+}
+
+static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_ATAPI_DMA:
+ return 1;
+
+ case ATA_PROT_ATAPI:
+ case ATA_PROT_PIO:
+ case ATA_PROT_PIO_MULT:
+ if (ap->flags & ATA_FLAG_PIO_DMA)
+ return 1;
+
+ /* fall through */
+
+ default:
+ return 0;
+ }
+
+ /* never reached */
+}
+
+/**
+ * ata_qc_issue - issue taskfile to device
+ * @qc: command to issue to device
+ *
+ * Prepare an ATA command to submission to device.
+ * This includes mapping the data into a DMA-able
+ * area, filling in the S/G table, and finally
+ * writing the taskfile to hardware, starting the command.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, negative on error.
+ */
+
+int ata_qc_issue(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ if (ata_should_dma_map(qc)) {
+ if (qc->flags & ATA_QCFLAG_SG) {
+ if (ata_sg_setup(qc))
+ goto err_out;
+ } else if (qc->flags & ATA_QCFLAG_SINGLE) {
+ if (ata_sg_setup_one(qc))
+ goto err_out;
+ }
+ } else {
+ qc->flags &= ~ATA_QCFLAG_DMAMAP;
+ }
+
+ ap->ops->qc_prep(qc);
+
+ qc->ap->active_tag = qc->tag;
+ qc->flags |= ATA_QCFLAG_ACTIVE;
+
+ return ap->ops->qc_issue(qc);
+
+err_out:
+ return -1;
+}
+
+/**
+ * ata_qc_issue_prot - issue taskfile to device in proto-dependent manner
+ * @qc: command to issue to device
+ *
+ * Using various libata functions and hooks, this function
+ * starts an ATA command. ATA commands are grouped into
+ * classes called "protocols", and issuing each type of protocol
+ * is slightly different.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, negative on error.
+ */
+
+int ata_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ ata_dev_select(ap, qc->dev->devno, 1, 0);
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_NODATA:
+ ata_tf_to_host_nolock(ap, &qc->tf);
+ break;
+
+ case ATA_PROT_DMA:
+ ap->ops->tf_load(ap, &qc->tf); /* load tf registers */
+ ap->ops->bmdma_setup(qc); /* set up bmdma */
+ ap->ops->bmdma_start(qc); /* initiate bmdma */
+ break;
+
+ case ATA_PROT_PIO: /* load tf registers, initiate polling pio */
+ ata_qc_set_polling(qc);
+ ata_tf_to_host_nolock(ap, &qc->tf);
+ ap->pio_task_state = PIO_ST;
+ queue_work(ata_wq, &ap->pio_task);
+ break;
+
+ case ATA_PROT_ATAPI:
+ ata_qc_set_polling(qc);
+ ata_tf_to_host_nolock(ap, &qc->tf);
+ queue_work(ata_wq, &ap->packet_task);
+ break;
+
+ case ATA_PROT_ATAPI_NODATA:
+ ata_tf_to_host_nolock(ap, &qc->tf);
+ queue_work(ata_wq, &ap->packet_task);
+ break;
+
+ case ATA_PROT_ATAPI_DMA:
+ ap->ops->tf_load(ap, &qc->tf); /* load tf registers */
+ ap->ops->bmdma_setup(qc); /* set up bmdma */
+ queue_work(ata_wq, &ap->packet_task);
+ break;
+
+ default:
+ WARN_ON(1);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * ata_bmdma_setup - Set up PCI IDE BMDMA transaction
+ * @qc: Info associated with this ATA transaction.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_setup_mmio (struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ u8 dmactl;
+ void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+
+ /* load PRD table addr. */
+ mb(); /* make sure PRD table writes are visible to controller */
+ writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS);
+
+ /* specify data direction, triple-check start bit is clear */
+ dmactl = readb(mmio + ATA_DMA_CMD);
+ dmactl &= ~(ATA_DMA_WR | ATA_DMA_START);
+ if (!rw)
+ dmactl |= ATA_DMA_WR;
+ writeb(dmactl, mmio + ATA_DMA_CMD);
+
+ /* issue r/w command */
+ ap->ops->exec_command(ap, &qc->tf);
+}
+
+/**
+ * ata_bmdma_start - Start a PCI IDE BMDMA transaction
+ * @qc: Info associated with this ATA transaction.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_start_mmio (struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+ u8 dmactl;
+
+ /* start host DMA transaction */
+ dmactl = readb(mmio + ATA_DMA_CMD);
+ writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD);
+
+ /* Strictly, one may wish to issue a readb() here, to
+ * flush the mmio write. However, control also passes
+ * to the hardware at this point, and it will interrupt
+ * us when we are to resume control. So, in effect,
+ * we don't care when the mmio write flushes.
+ * Further, a read of the DMA status register _immediately_
+ * following the write may not be what certain flaky hardware
+ * is expected, so I think it is best to not add a readb()
+ * without first all the MMIO ATA cards/mobos.
+ * Or maybe I'm just being paranoid.
+ */
+}
+
+/**
+ * ata_bmdma_setup_pio - Set up PCI IDE BMDMA transaction (PIO)
+ * @qc: Info associated with this ATA transaction.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_setup_pio (struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ u8 dmactl;
+
+ /* load PRD table addr. */
+ outl(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS);
+
+ /* specify data direction, triple-check start bit is clear */
+ dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+ dmactl &= ~(ATA_DMA_WR | ATA_DMA_START);
+ if (!rw)
+ dmactl |= ATA_DMA_WR;
+ outb(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+
+ /* issue r/w command */
+ ap->ops->exec_command(ap, &qc->tf);
+}
+
+/**
+ * ata_bmdma_start_pio - Start a PCI IDE BMDMA transaction (PIO)
+ * @qc: Info associated with this ATA transaction.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_bmdma_start_pio (struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ u8 dmactl;
+
+ /* start host DMA transaction */
+ dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+ outb(dmactl | ATA_DMA_START,
+ ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+}
+
+void ata_bmdma_start(struct ata_queued_cmd *qc)
+{
+ if (qc->ap->flags & ATA_FLAG_MMIO)
+ ata_bmdma_start_mmio(qc);
+ else
+ ata_bmdma_start_pio(qc);
+}
+
+void ata_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ if (qc->ap->flags & ATA_FLAG_MMIO)
+ ata_bmdma_setup_mmio(qc);
+ else
+ ata_bmdma_setup_pio(qc);
+}
+
+void ata_bmdma_irq_clear(struct ata_port *ap)
+{
+ if (ap->flags & ATA_FLAG_MMIO) {
+ void __iomem *mmio = ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS;
+ writeb(readb(mmio), mmio);
+ } else {
+ unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS;
+ outb(inb(addr), addr);
+ }
+
+}
+
+u8 ata_bmdma_status(struct ata_port *ap)
+{
+ u8 host_stat;
+ if (ap->flags & ATA_FLAG_MMIO) {
+ void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+ host_stat = readb(mmio + ATA_DMA_STATUS);
+ } else
+ host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);
+ return host_stat;
+}
+
+void ata_bmdma_stop(struct ata_port *ap)
+{
+ if (ap->flags & ATA_FLAG_MMIO) {
+ void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
+
+ /* clear start/stop bit */
+ writeb(readb(mmio + ATA_DMA_CMD) & ~ATA_DMA_START,
+ mmio + ATA_DMA_CMD);
+ } else {
+ /* clear start/stop bit */
+ outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START,
+ ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+ }
+
+ /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+ ata_altstatus(ap); /* dummy read */
+}
+
+/**
+ * ata_host_intr - Handle host interrupt for given (port, task)
+ * @ap: Port on which interrupt arrived (possibly...)
+ * @qc: Taskfile currently active in engine
+ *
+ * Handle host interrupt for given queued command. Currently,
+ * only DMA interrupts are handled. All other commands are
+ * handled via polling with interrupts disabled (nIEN bit).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * One if interrupt was handled, zero if not (shared irq).
+ */
+
+inline unsigned int ata_host_intr (struct ata_port *ap,
+ struct ata_queued_cmd *qc)
+{
+ u8 status, host_stat;
+
+ switch (qc->tf.protocol) {
+
+ case ATA_PROT_DMA:
+ case ATA_PROT_ATAPI_DMA:
+ case ATA_PROT_ATAPI:
+ /* check status of DMA engine */
+ host_stat = ap->ops->bmdma_status(ap);
+ VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
+
+ /* if it's not our irq... */
+ if (!(host_stat & ATA_DMA_INTR))
+ goto idle_irq;
+
+ /* before we do anything else, clear DMA-Start bit */
+ ap->ops->bmdma_stop(ap);
+
+ /* fall through */
+
+ case ATA_PROT_ATAPI_NODATA:
+ case ATA_PROT_NODATA:
+ /* check altstatus */
+ status = ata_altstatus(ap);
+ if (status & ATA_BUSY)
+ goto idle_irq;
+
+ /* check main status, clearing INTRQ */
+ status = ata_chk_status(ap);
+ if (unlikely(status & ATA_BUSY))
+ goto idle_irq;
+ DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
+ ap->id, qc->tf.protocol, status);
+
+ /* ack bmdma irq events */
+ ap->ops->irq_clear(ap);
+
+ /* complete taskfile transaction */
+ ata_qc_complete(qc, status);
+ break;
+
+ default:
+ goto idle_irq;
+ }
+
+ return 1; /* irq handled */
+
+idle_irq:
+ ap->stats.idle_irq++;
+
+#ifdef ATA_IRQ_TRAP
+ if ((ap->stats.idle_irq % 1000) == 0) {
+ handled = 1;
+ ata_irq_ack(ap, 0); /* debug trap */
+ printk(KERN_WARNING "ata%d: irq trap\n", ap->id);
+ }
+#endif
+ return 0; /* irq not handled */
+}
+
+/**
+ * ata_interrupt - Default ATA host interrupt handler
+ * @irq: irq line
+ * @dev_instance: pointer to our host information structure
+ * @regs: unused
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ *
+ */
+
+irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ unsigned int i;
+ unsigned int handled = 0;
+ unsigned long flags;
+
+ /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
+ spin_lock_irqsave(&host_set->lock, flags);
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap;
+
+ ap = host_set->ports[i];
+ if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN)))
+ handled |= ata_host_intr(ap, qc);
+ }
+ }
+
+ spin_unlock_irqrestore(&host_set->lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+
+/**
+ * atapi_packet_task - Write CDB bytes to hardware
+ * @_data: Port to which ATAPI device is attached.
+ *
+ * When device has indicated its readiness to accept
+ * a CDB, this function is called. Send the CDB.
+ * If DMA is to be performed, exit immediately.
+ * Otherwise, we are in polling mode, so poll
+ * status under operation succeeds or fails.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep)
+ */
+
+static void atapi_packet_task(void *_data)
+{
+ struct ata_port *ap = _data;
+ struct ata_queued_cmd *qc;
+ u8 status;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ assert(qc != NULL);
+ assert(qc->flags & ATA_QCFLAG_ACTIVE);
+
+ /* sleep-wait for BSY to clear */
+ DPRINTK("busy wait\n");
+ if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB))
+ goto err_out;
+
+ /* make sure DRQ is set */
+ status = ata_chk_status(ap);
+ if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)
+ goto err_out;
+
+ /* send SCSI cdb */
+ DPRINTK("send cdb\n");
+ assert(ap->cdb_len >= 12);
+ ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+
+ /* if we are DMA'ing, irq handler takes over from here */
+ if (qc->tf.protocol == ATA_PROT_ATAPI_DMA)
+ ap->ops->bmdma_start(qc); /* initiate bmdma */
+
+ /* non-data commands are also handled via irq */
+ else if (qc->tf.protocol == ATA_PROT_ATAPI_NODATA) {
+ /* do nothing */
+ }
+
+ /* PIO commands are handled by polling */
+ else {
+ ap->pio_task_state = PIO_ST;
+ queue_work(ata_wq, &ap->pio_task);
+ }
+
+ return;
+
+err_out:
+ ata_qc_complete(qc, ATA_ERR);
+}
+
+int ata_port_start (struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+
+ ap->prd = dma_alloc_coherent(dev, ATA_PRD_TBL_SZ, &ap->prd_dma, GFP_KERNEL);
+ if (!ap->prd)
+ return -ENOMEM;
+
+ DPRINTK("prd alloc, virt %p, dma %llx\n", ap->prd, (unsigned long long) ap->prd_dma);
+
+ return 0;
+}
+
+void ata_port_stop (struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+
+ dma_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma);
+}
+
+/**
+ * ata_host_remove - Unregister SCSI host structure with upper layers
+ * @ap: Port to unregister
+ * @do_unregister: 1 if we fully unregister, 0 to just stop the port
+ *
+ * LOCKING:
+ */
+
+static void ata_host_remove(struct ata_port *ap, unsigned int do_unregister)
+{
+ struct Scsi_Host *sh = ap->host;
+
+ DPRINTK("ENTER\n");
+
+ if (do_unregister)
+ scsi_remove_host(sh);
+
+ ap->ops->port_stop(ap);
+}
+
+/**
+ * ata_host_init - Initialize an ata_port structure
+ * @ap: Structure to initialize
+ * @host: associated SCSI mid-layer structure
+ * @host_set: Collection of hosts to which @ap belongs
+ * @ent: Probe information provided by low-level driver
+ * @port_no: Port number associated with this ata_port
+ *
+ * LOCKING:
+ *
+ */
+
+static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
+ struct ata_host_set *host_set,
+ struct ata_probe_ent *ent, unsigned int port_no)
+{
+ unsigned int i;
+
+ host->max_id = 16;
+ host->max_lun = 1;
+ host->max_channel = 1;
+ host->unique_id = ata_unique_id++;
+ host->max_cmd_len = 12;
+ scsi_set_device(host, ent->dev);
+ scsi_assign_lock(host, &host_set->lock);
+
+ ap->flags = ATA_FLAG_PORT_DISABLED;
+ ap->id = host->unique_id;
+ ap->host = host;
+ ap->ctl = ATA_DEVCTL_OBS;
+ ap->host_set = host_set;
+ ap->port_no = port_no;
+ ap->hard_port_no =
+ ent->legacy_mode ? ent->hard_port_no : port_no;
+ ap->pio_mask = ent->pio_mask;
+ ap->mwdma_mask = ent->mwdma_mask;
+ ap->udma_mask = ent->udma_mask;
+ ap->flags |= ent->host_flags;
+ ap->ops = ent->port_ops;
+ ap->cbl = ATA_CBL_NONE;
+ ap->active_tag = ATA_TAG_POISON;
+ ap->last_ctl = 0xFF;
+
+ INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
+ INIT_WORK(&ap->pio_task, ata_pio_task, ap);
+
+ for (i = 0; i < ATA_MAX_DEVICES; i++)
+ ap->device[i].devno = i;
+
+#ifdef ATA_IRQ_TRAP
+ ap->stats.unhandled_irq = 1;
+ ap->stats.idle_irq = 1;
+#endif
+
+ memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports));
+}
+
+/**
+ * ata_host_add - Attach low-level ATA driver to system
+ * @ent: Information provided by low-level driver
+ * @host_set: Collections of ports to which we add
+ * @port_no: Port number associated with this host
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ *
+ */
+
+static struct ata_port * ata_host_add(struct ata_probe_ent *ent,
+ struct ata_host_set *host_set,
+ unsigned int port_no)
+{
+ struct Scsi_Host *host;
+ struct ata_port *ap;
+ int rc;
+
+ DPRINTK("ENTER\n");
+ host = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
+ if (!host)
+ return NULL;
+
+ ap = (struct ata_port *) &host->hostdata[0];
+
+ ata_host_init(ap, host, host_set, ent, port_no);
+
+ rc = ap->ops->port_start(ap);
+ if (rc)
+ goto err_out;
+
+ return ap;
+
+err_out:
+ scsi_host_put(host);
+ return NULL;
+}
+
+/**
+ * ata_device_add -
+ * @ent:
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ *
+ */
+
+int ata_device_add(struct ata_probe_ent *ent)
+{
+ unsigned int count = 0, i;
+ struct device *dev = ent->dev;
+ struct ata_host_set *host_set;
+
+ DPRINTK("ENTER\n");
+ /* alloc a container for our list of ATA ports (buses) */
+ host_set = kmalloc(sizeof(struct ata_host_set) +
+ (ent->n_ports * sizeof(void *)), GFP_KERNEL);
+ if (!host_set)
+ return 0;
+ memset(host_set, 0, sizeof(struct ata_host_set) + (ent->n_ports * sizeof(void *)));
+ spin_lock_init(&host_set->lock);
+
+ host_set->dev = dev;
+ host_set->n_ports = ent->n_ports;
+ host_set->irq = ent->irq;
+ host_set->mmio_base = ent->mmio_base;
+ host_set->private_data = ent->private_data;
+ host_set->ops = ent->port_ops;
+
+ /* register each port bound to this device */
+ for (i = 0; i < ent->n_ports; i++) {
+ struct ata_port *ap;
+ unsigned long xfer_mode_mask;
+
+ ap = ata_host_add(ent, host_set, i);
+ if (!ap)
+ goto err_out;
+
+ host_set->ports[i] = ap;
+ xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
+ (ap->mwdma_mask << ATA_SHIFT_MWDMA) |
+ (ap->pio_mask << ATA_SHIFT_PIO);
+
+ /* print per-port info to dmesg */
+ printk(KERN_INFO "ata%u: %cATA max %s cmd 0x%lX ctl 0x%lX "
+ "bmdma 0x%lX irq %lu\n",
+ ap->id,
+ ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
+ ata_mode_string(xfer_mode_mask),
+ ap->ioaddr.cmd_addr,
+ ap->ioaddr.ctl_addr,
+ ap->ioaddr.bmdma_addr,
+ ent->irq);
+
+ ata_chk_status(ap);
+ host_set->ops->irq_clear(ap);
+ count++;
+ }
+
+ if (!count) {
+ kfree(host_set);
+ return 0;
+ }
+
+ /* obtain irq, that is shared between channels */
+ if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags,
+ DRV_NAME, host_set))
+ goto err_out;
+
+ /* perform each probe synchronously */
+ DPRINTK("probe begin\n");
+ for (i = 0; i < count; i++) {
+ struct ata_port *ap;
+ int rc;
+
+ ap = host_set->ports[i];
+
+ DPRINTK("ata%u: probe begin\n", ap->id);
+ rc = ata_bus_probe(ap);
+ DPRINTK("ata%u: probe end\n", ap->id);
+
+ if (rc) {
+ /* FIXME: do something useful here?
+ * Current libata behavior will
+ * tear down everything when
+ * the module is removed
+ * or the h/w is unplugged.
+ */
+ }
+
+ rc = scsi_add_host(ap->host, dev);
+ if (rc) {
+ printk(KERN_ERR "ata%u: scsi_add_host failed\n",
+ ap->id);
+ /* FIXME: do something useful here */
+ /* FIXME: handle unconditional calls to
+ * scsi_scan_host and ata_host_remove, below,
+ * at the very least
+ */
+ }
+ }
+
+ /* probes are done, now scan each port's disk(s) */
+ DPRINTK("probe begin\n");
+ for (i = 0; i < count; i++) {
+ struct ata_port *ap = host_set->ports[i];
+
+ scsi_scan_host(ap->host);
+ }
+
+ dev_set_drvdata(dev, host_set);
+
+ VPRINTK("EXIT, returning %u\n", ent->n_ports);
+ return ent->n_ports; /* success */
+
+err_out:
+ for (i = 0; i < count; i++) {
+ ata_host_remove(host_set->ports[i], 1);
+ scsi_host_put(host_set->ports[i]->host);
+ }
+ kfree(host_set);
+ VPRINTK("EXIT, returning 0\n");
+ return 0;
+}
+
+/**
+ * ata_scsi_release - SCSI layer callback hook for host unload
+ * @host: libata host to be unloaded
+ *
+ * Performs all duties necessary to shut down a libata port...
+ * Kill port kthread, disable port, and release resources.
+ *
+ * LOCKING:
+ * Inherited from SCSI layer.
+ *
+ * RETURNS:
+ * One.
+ */
+
+int ata_scsi_release(struct Scsi_Host *host)
+{
+ struct ata_port *ap = (struct ata_port *) &host->hostdata[0];
+
+ DPRINTK("ENTER\n");
+
+ ap->ops->port_disable(ap);
+ ata_host_remove(ap, 0);
+
+ DPRINTK("EXIT\n");
+ return 1;
+}
+
+/**
+ * ata_std_ports - initialize ioaddr with standard port offsets.
+ * @ioaddr: IO address structure to be initialized
+ */
+void ata_std_ports(struct ata_ioports *ioaddr)
+{
+ ioaddr->data_addr = ioaddr->cmd_addr + ATA_REG_DATA;
+ ioaddr->error_addr = ioaddr->cmd_addr + ATA_REG_ERR;
+ ioaddr->feature_addr = ioaddr->cmd_addr + ATA_REG_FEATURE;
+ ioaddr->nsect_addr = ioaddr->cmd_addr + ATA_REG_NSECT;
+ ioaddr->lbal_addr = ioaddr->cmd_addr + ATA_REG_LBAL;
+ ioaddr->lbam_addr = ioaddr->cmd_addr + ATA_REG_LBAM;
+ ioaddr->lbah_addr = ioaddr->cmd_addr + ATA_REG_LBAH;
+ ioaddr->device_addr = ioaddr->cmd_addr + ATA_REG_DEVICE;
+ ioaddr->status_addr = ioaddr->cmd_addr + ATA_REG_STATUS;
+ ioaddr->command_addr = ioaddr->cmd_addr + ATA_REG_CMD;
+}
+
+static struct ata_probe_ent *
+ata_probe_ent_alloc(struct device *dev, struct ata_port_info *port)
+{
+ struct ata_probe_ent *probe_ent;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (!probe_ent) {
+ printk(KERN_ERR DRV_NAME "(%s): out of memory\n",
+ kobject_name(&(dev->kobj)));
+ return NULL;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+
+ INIT_LIST_HEAD(&probe_ent->node);
+ probe_ent->dev = dev;
+
+ probe_ent->sht = port->sht;
+ probe_ent->host_flags = port->host_flags;
+ probe_ent->pio_mask = port->pio_mask;
+ probe_ent->mwdma_mask = port->mwdma_mask;
+ probe_ent->udma_mask = port->udma_mask;
+ probe_ent->port_ops = port->port_ops;
+
+ return probe_ent;
+}
+
+#ifdef CONFIG_PCI
+struct ata_probe_ent *
+ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port)
+{
+ struct ata_probe_ent *probe_ent =
+ ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
+ if (!probe_ent)
+ return NULL;
+
+ probe_ent->n_ports = 2;
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+
+ probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0);
+ probe_ent->port[0].altstatus_addr =
+ probe_ent->port[0].ctl_addr =
+ pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
+ probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4);
+
+ probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2);
+ probe_ent->port[1].altstatus_addr =
+ probe_ent->port[1].ctl_addr =
+ pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
+ probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8;
+
+ ata_std_ports(&probe_ent->port[0]);
+ ata_std_ports(&probe_ent->port[1]);
+
+ return probe_ent;
+}
+
+static struct ata_probe_ent *
+ata_pci_init_legacy_mode(struct pci_dev *pdev, struct ata_port_info **port,
+ struct ata_probe_ent **ppe2)
+{
+ struct ata_probe_ent *probe_ent, *probe_ent2;
+
+ probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
+ if (!probe_ent)
+ return NULL;
+ probe_ent2 = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[1]);
+ if (!probe_ent2) {
+ kfree(probe_ent);
+ return NULL;
+ }
+
+ probe_ent->n_ports = 1;
+ probe_ent->irq = 14;
+
+ probe_ent->hard_port_no = 0;
+ probe_ent->legacy_mode = 1;
+
+ probe_ent2->n_ports = 1;
+ probe_ent2->irq = 15;
+
+ probe_ent2->hard_port_no = 1;
+ probe_ent2->legacy_mode = 1;
+
+ probe_ent->port[0].cmd_addr = 0x1f0;
+ probe_ent->port[0].altstatus_addr =
+ probe_ent->port[0].ctl_addr = 0x3f6;
+ probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4);
+
+ probe_ent2->port[0].cmd_addr = 0x170;
+ probe_ent2->port[0].altstatus_addr =
+ probe_ent2->port[0].ctl_addr = 0x376;
+ probe_ent2->port[0].bmdma_addr = pci_resource_start(pdev, 4)+8;
+
+ ata_std_ports(&probe_ent->port[0]);
+ ata_std_ports(&probe_ent2->port[0]);
+
+ *ppe2 = probe_ent2;
+ return probe_ent;
+}
+
+/**
+ * ata_pci_init_one - Initialize/register PCI IDE host controller
+ * @pdev: Controller to be initialized
+ * @port_info: Information from low-level host driver
+ * @n_ports: Number of ports attached to host controller
+ *
+ * LOCKING:
+ * Inherited from PCI layer (may sleep).
+ *
+ * RETURNS:
+ *
+ */
+
+int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
+ unsigned int n_ports)
+{
+ struct ata_probe_ent *probe_ent, *probe_ent2 = NULL;
+ struct ata_port_info *port[2];
+ u8 tmp8, mask;
+ unsigned int legacy_mode = 0;
+ int disable_dev_on_err = 1;
+ int rc;
+
+ DPRINTK("ENTER\n");
+
+ port[0] = port_info[0];
+ if (n_ports > 1)
+ port[1] = port_info[1];
+ else
+ port[1] = port[0];
+
+ if ((port[0]->host_flags & ATA_FLAG_NO_LEGACY) == 0
+ && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+ /* TODO: support transitioning to native mode? */
+ pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
+ mask = (1 << 2) | (1 << 0);
+ if ((tmp8 & mask) != mask)
+ legacy_mode = (1 << 3);
+ }
+
+ /* FIXME... */
+ if ((!legacy_mode) && (n_ports > 1)) {
+ printk(KERN_ERR "ata: BUG: native mode, n_ports > 1\n");
+ return -EINVAL;
+ }
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ disable_dev_on_err = 0;
+ goto err_out;
+ }
+
+ if (legacy_mode) {
+ if (!request_region(0x1f0, 8, "libata")) {
+ struct resource *conflict, res;
+ res.start = 0x1f0;
+ res.end = 0x1f0 + 8 - 1;
+ conflict = ____request_resource(&ioport_resource, &res);
+ if (!strcmp(conflict->name, "libata"))
+ legacy_mode |= (1 << 0);
+ else {
+ disable_dev_on_err = 0;
+ printk(KERN_WARNING "ata: 0x1f0 IDE port busy\n");
+ }
+ } else
+ legacy_mode |= (1 << 0);
+
+ if (!request_region(0x170, 8, "libata")) {
+ struct resource *conflict, res;
+ res.start = 0x170;
+ res.end = 0x170 + 8 - 1;
+ conflict = ____request_resource(&ioport_resource, &res);
+ if (!strcmp(conflict->name, "libata"))
+ legacy_mode |= (1 << 1);
+ else {
+ disable_dev_on_err = 0;
+ printk(KERN_WARNING "ata: 0x170 IDE port busy\n");
+ }
+ } else
+ legacy_mode |= (1 << 1);
+ }
+
+ /* we have legacy mode, but all ports are unavailable */
+ if (legacy_mode == (1 << 3)) {
+ rc = -EBUSY;
+ goto err_out_regions;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ if (legacy_mode) {
+ probe_ent = ata_pci_init_legacy_mode(pdev, port, &probe_ent2);
+ } else
+ probe_ent = ata_pci_init_native_mode(pdev, port);
+ if (!probe_ent) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ pci_set_master(pdev);
+
+ /* FIXME: check ata_device_add return */
+ if (legacy_mode) {
+ if (legacy_mode & (1 << 0))
+ ata_device_add(probe_ent);
+ if (legacy_mode & (1 << 1))
+ ata_device_add(probe_ent2);
+ } else
+ ata_device_add(probe_ent);
+
+ kfree(probe_ent);
+ kfree(probe_ent2);
+
+ return 0;
+
+err_out_regions:
+ if (legacy_mode & (1 << 0))
+ release_region(0x1f0, 8);
+ if (legacy_mode & (1 << 1))
+ release_region(0x170, 8);
+ pci_release_regions(pdev);
+err_out:
+ if (disable_dev_on_err)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+/**
+ * ata_pci_remove_one - PCI layer callback for device removal
+ * @pdev: PCI device that was removed
+ *
+ * PCI layer indicates to libata via this hook that
+ * hot-unplug or module unload event has occured.
+ * Handle this by unregistering all objects associated
+ * with this PCI device. Free those objects. Then finally
+ * release PCI resources and disable device.
+ *
+ * LOCKING:
+ * Inherited from PCI layer (may sleep).
+ */
+
+void ata_pci_remove_one (struct pci_dev *pdev)
+{
+ struct device *dev = pci_dev_to_dev(pdev);
+ struct ata_host_set *host_set = dev_get_drvdata(dev);
+ struct ata_port *ap;
+ unsigned int i;
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+
+ scsi_remove_host(ap->host);
+ }
+
+ free_irq(host_set->irq, host_set);
+ if (host_set->ops->host_stop)
+ host_set->ops->host_stop(host_set);
+ if (host_set->mmio_base)
+ iounmap(host_set->mmio_base);
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+
+ ata_scsi_release(ap->host);
+
+ if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) {
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ if (ioaddr->cmd_addr == 0x1f0)
+ release_region(0x1f0, 8);
+ else if (ioaddr->cmd_addr == 0x170)
+ release_region(0x170, 8);
+ }
+
+ scsi_host_put(ap->host);
+ }
+
+ kfree(host_set);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ dev_set_drvdata(dev, NULL);
+}
+
+/* move to PCI subsystem */
+int pci_test_config_bits(struct pci_dev *pdev, struct pci_bits *bits)
+{
+ unsigned long tmp = 0;
+
+ switch (bits->width) {
+ case 1: {
+ u8 tmp8 = 0;
+ pci_read_config_byte(pdev, bits->reg, &tmp8);
+ tmp = tmp8;
+ break;
+ }
+ case 2: {
+ u16 tmp16 = 0;
+ pci_read_config_word(pdev, bits->reg, &tmp16);
+ tmp = tmp16;
+ break;
+ }
+ case 4: {
+ u32 tmp32 = 0;
+ pci_read_config_dword(pdev, bits->reg, &tmp32);
+ tmp = tmp32;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ tmp &= bits->mask;
+
+ return (tmp == bits->val) ? 1 : 0;
+}
+#endif /* CONFIG_PCI */
+
+
+/**
+ * ata_init -
+ *
+ * LOCKING:
+ *
+ * RETURNS:
+ *
+ */
+
+static int __init ata_init(void)
+{
+ ata_wq = create_workqueue("ata");
+ if (!ata_wq)
+ return -ENOMEM;
+
+ printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
+ return 0;
+}
+
+static void __exit ata_exit(void)
+{
+ destroy_workqueue(ata_wq);
+}
+
+module_init(ata_init);
+module_exit(ata_exit);
+
+/*
+ * libata is essentially a library of internal helper functions for
+ * low-level ATA host controller drivers. As such, the API/ABI is
+ * likely to change as new drivers are added and updated.
+ * Do not depend on ABI/API stability.
+ */
+
+EXPORT_SYMBOL_GPL(ata_std_bios_param);
+EXPORT_SYMBOL_GPL(ata_std_ports);
+EXPORT_SYMBOL_GPL(ata_device_add);
+EXPORT_SYMBOL_GPL(ata_sg_init);
+EXPORT_SYMBOL_GPL(ata_sg_init_one);
+EXPORT_SYMBOL_GPL(ata_qc_complete);
+EXPORT_SYMBOL_GPL(ata_qc_issue_prot);
+EXPORT_SYMBOL_GPL(ata_eng_timeout);
+EXPORT_SYMBOL_GPL(ata_tf_load);
+EXPORT_SYMBOL_GPL(ata_tf_read);
+EXPORT_SYMBOL_GPL(ata_noop_dev_select);
+EXPORT_SYMBOL_GPL(ata_std_dev_select);
+EXPORT_SYMBOL_GPL(ata_tf_to_fis);
+EXPORT_SYMBOL_GPL(ata_tf_from_fis);
+EXPORT_SYMBOL_GPL(ata_check_status);
+EXPORT_SYMBOL_GPL(ata_altstatus);
+EXPORT_SYMBOL_GPL(ata_chk_err);
+EXPORT_SYMBOL_GPL(ata_exec_command);
+EXPORT_SYMBOL_GPL(ata_port_start);
+EXPORT_SYMBOL_GPL(ata_port_stop);
+EXPORT_SYMBOL_GPL(ata_interrupt);
+EXPORT_SYMBOL_GPL(ata_qc_prep);
+EXPORT_SYMBOL_GPL(ata_bmdma_setup);
+EXPORT_SYMBOL_GPL(ata_bmdma_start);
+EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear);
+EXPORT_SYMBOL_GPL(ata_bmdma_status);
+EXPORT_SYMBOL_GPL(ata_bmdma_stop);
+EXPORT_SYMBOL_GPL(ata_port_probe);
+EXPORT_SYMBOL_GPL(sata_phy_reset);
+EXPORT_SYMBOL_GPL(__sata_phy_reset);
+EXPORT_SYMBOL_GPL(ata_bus_reset);
+EXPORT_SYMBOL_GPL(ata_port_disable);
+EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
+EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
+EXPORT_SYMBOL_GPL(ata_scsi_error);
+EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
+EXPORT_SYMBOL_GPL(ata_scsi_release);
+EXPORT_SYMBOL_GPL(ata_host_intr);
+EXPORT_SYMBOL_GPL(ata_dev_classify);
+EXPORT_SYMBOL_GPL(ata_dev_id_string);
+EXPORT_SYMBOL_GPL(ata_scsi_simulate);
+
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL_GPL(pci_test_config_bits);
+EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
+EXPORT_SYMBOL_GPL(ata_pci_init_one);
+EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+#endif /* CONFIG_PCI */
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
new file mode 100644
index 000000000000..4e5e54a1564b
--- /dev/null
+++ b/drivers/scsi/libata-scsi.c
@@ -0,0 +1,1593 @@
+/*
+ libata-scsi.c - helper library for ATA
+
+ Copyright 2003-2004 Red Hat, Inc. All rights reserved.
+ Copyright 2003-2004 Jeff Garzik
+
+ The contents of this file are subject to the Open
+ Software License version 1.1 that can be found at
+ http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ by reference.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the GNU General Public License version 2 (the "GPL") as distributed
+ in the kernel source COPYING file, in which case the provisions of
+ the GPL are applicable instead of the above. If you wish to allow
+ the use of your version of this file only under the terms of the
+ GPL and not to allow others to use your version of this file under
+ the OSL, indicate your decision by deleting the provisions above and
+ replace them with the notice and other provisions required by the GPL.
+ If you do not delete the provisions above, a recipient may use your
+ version of this file under either the OSL or the GPL.
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <scsi/scsi.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/uaccess.h>
+
+#include "libata.h"
+
+typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, u8 *scsicmd);
+static struct ata_device *
+ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev);
+
+
+/**
+ * ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd.
+ * @sdev: SCSI device for which BIOS geometry is to be determined
+ * @bdev: block device associated with @sdev
+ * @capacity: capacity of SCSI device
+ * @geom: location to which geometry will be output
+ *
+ * Generic bios head/sector/cylinder calculator
+ * used by sd. Most BIOSes nowadays expect a XXX/255/16 (CHS)
+ * mapping. Some situations may arise where the disk is not
+ * bootable if this is not used.
+ *
+ * LOCKING:
+ * Defined by the SCSI layer. We don't really care.
+ *
+ * RETURNS:
+ * Zero.
+ */
+int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ geom[0] = 255;
+ geom[1] = 63;
+ sector_div(capacity, 255*63);
+ geom[2] = capacity;
+
+ return 0;
+}
+
+int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
+{
+ struct ata_port *ap;
+ struct ata_device *dev;
+ int val = -EINVAL, rc = -EINVAL;
+
+ ap = (struct ata_port *) &scsidev->host->hostdata[0];
+ if (!ap)
+ goto out;
+
+ dev = ata_scsi_find_dev(ap, scsidev);
+ if (!dev) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ switch (cmd) {
+ case ATA_IOC_GET_IO32:
+ val = 0;
+ if (copy_to_user(arg, &val, 1))
+ return -EFAULT;
+ return 0;
+
+ case ATA_IOC_SET_IO32:
+ val = (unsigned long) arg;
+ if (val != 0)
+ return -EINVAL;
+ return 0;
+
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+out:
+ return rc;
+}
+
+/**
+ * ata_scsi_qc_new - acquire new ata_queued_cmd reference
+ * @ap: ATA port to which the new command is attached
+ * @dev: ATA device to which the new command is attached
+ * @cmd: SCSI command that originated this ATA command
+ * @done: SCSI command completion function
+ *
+ * Obtain a reference to an unused ata_queued_cmd structure,
+ * which is the basic libata structure representing a single
+ * ATA command sent to the hardware.
+ *
+ * If a command was available, fill in the SCSI-specific
+ * portions of the structure with information on the
+ * current command.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Command allocated, or %NULL if none available.
+ */
+struct ata_queued_cmd *ata_scsi_qc_new(struct ata_port *ap,
+ struct ata_device *dev,
+ struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_new_init(ap, dev);
+ if (qc) {
+ qc->scsicmd = cmd;
+ qc->scsidone = done;
+
+ if (cmd->use_sg) {
+ qc->sg = (struct scatterlist *) cmd->request_buffer;
+ qc->n_elem = cmd->use_sg;
+ } else {
+ qc->sg = &qc->sgent;
+ qc->n_elem = 1;
+ }
+ } else {
+ cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1);
+ done(cmd);
+ }
+
+ return qc;
+}
+
+/**
+ * ata_to_sense_error - convert ATA error to SCSI error
+ * @qc: Command that we are erroring out
+ * @drv_stat: value contained in ATA status register
+ *
+ * Converts an ATA error into a SCSI error. While we are at it
+ * we decode and dump the ATA error for the user so that they
+ * have some idea what really happened at the non make-believe
+ * layer.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+ u8 err = 0;
+ unsigned char *sb = cmd->sense_buffer;
+ /* Based on the 3ware driver translation table */
+ static unsigned char sense_table[][4] = {
+ /* BBD|ECC|ID|MAR */
+ {0xd1, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command
+ /* BBD|ECC|ID */
+ {0xd0, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command
+ /* ECC|MC|MARK */
+ {0x61, HARDWARE_ERROR, 0x00, 0x00}, // Device fault Hardware error
+ /* ICRC|ABRT */ /* NB: ICRC & !ABRT is BBD */
+ {0x84, ABORTED_COMMAND, 0x47, 0x00}, // Data CRC error SCSI parity error
+ /* MC|ID|ABRT|TRK0|MARK */
+ {0x37, NOT_READY, 0x04, 0x00}, // Unit offline Not ready
+ /* MCR|MARK */
+ {0x09, NOT_READY, 0x04, 0x00}, // Unrecovered disk error Not ready
+ /* Bad address mark */
+ {0x01, MEDIUM_ERROR, 0x13, 0x00}, // Address mark not found Address mark not found for data field
+ /* TRK0 */
+ {0x02, HARDWARE_ERROR, 0x00, 0x00}, // Track 0 not found Hardware error
+ /* Abort & !ICRC */
+ {0x04, ABORTED_COMMAND, 0x00, 0x00}, // Aborted command Aborted command
+ /* Media change request */
+ {0x08, NOT_READY, 0x04, 0x00}, // Media change request FIXME: faking offline
+ /* SRV */
+ {0x10, ABORTED_COMMAND, 0x14, 0x00}, // ID not found Recorded entity not found
+ /* Media change */
+ {0x08, NOT_READY, 0x04, 0x00}, // Media change FIXME: faking offline
+ /* ECC */
+ {0x40, MEDIUM_ERROR, 0x11, 0x04}, // Uncorrectable ECC error Unrecovered read error
+ /* BBD - block marked bad */
+ {0x80, MEDIUM_ERROR, 0x11, 0x04}, // Block marked bad Medium error, unrecovered read error
+ {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ };
+ static unsigned char stat_table[][4] = {
+ /* Must be first because BUSY means no other bits valid */
+ {0x80, ABORTED_COMMAND, 0x47, 0x00}, // Busy, fake parity for now
+ {0x20, HARDWARE_ERROR, 0x00, 0x00}, // Device fault
+ {0x08, ABORTED_COMMAND, 0x47, 0x00}, // Timed out in xfer, fake parity for now
+ {0x04, RECOVERED_ERROR, 0x11, 0x00}, // Recovered ECC error Medium error, recovered
+ {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ };
+ int i = 0;
+
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ /*
+ * Is this an error we can process/parse
+ */
+
+ if(drv_stat & ATA_ERR)
+ /* Read the err bits */
+ err = ata_chk_err(qc->ap);
+
+ /* Display the ATA level error info */
+
+ printk(KERN_WARNING "ata%u: status=0x%02x { ", qc->ap->id, drv_stat);
+ if(drv_stat & 0x80)
+ {
+ printk("Busy ");
+ err = 0; /* Data is not valid in this case */
+ }
+ else {
+ if(drv_stat & 0x40) printk("DriveReady ");
+ if(drv_stat & 0x20) printk("DeviceFault ");
+ if(drv_stat & 0x10) printk("SeekComplete ");
+ if(drv_stat & 0x08) printk("DataRequest ");
+ if(drv_stat & 0x04) printk("CorrectedError ");
+ if(drv_stat & 0x02) printk("Index ");
+ if(drv_stat & 0x01) printk("Error ");
+ }
+ printk("}\n");
+
+ if(err)
+ {
+ printk(KERN_WARNING "ata%u: error=0x%02x { ", qc->ap->id, err);
+ if(err & 0x04) printk("DriveStatusError ");
+ if(err & 0x80)
+ {
+ if(err & 0x04)
+ printk("BadCRC ");
+ else
+ printk("Sector ");
+ }
+ if(err & 0x40) printk("UncorrectableError ");
+ if(err & 0x10) printk("SectorIdNotFound ");
+ if(err & 0x02) printk("TrackZeroNotFound ");
+ if(err & 0x01) printk("AddrMarkNotFound ");
+ printk("}\n");
+
+ /* Should we dump sector info here too ?? */
+ }
+
+
+ /* Look for err */
+ while(sense_table[i][0] != 0xFF)
+ {
+ /* Look for best matches first */
+ if((sense_table[i][0] & err) == sense_table[i][0])
+ {
+ sb[0] = 0x70;
+ sb[2] = sense_table[i][1];
+ sb[7] = 0x0a;
+ sb[12] = sense_table[i][2];
+ sb[13] = sense_table[i][3];
+ return;
+ }
+ i++;
+ }
+ /* No immediate match */
+ if(err)
+ printk(KERN_DEBUG "ata%u: no sense translation for 0x%02x\n", qc->ap->id, err);
+
+ i = 0;
+ /* Fall back to interpreting status bits */
+ while(stat_table[i][0] != 0xFF)
+ {
+ if(stat_table[i][0] & drv_stat)
+ {
+ sb[0] = 0x70;
+ sb[2] = stat_table[i][1];
+ sb[7] = 0x0a;
+ sb[12] = stat_table[i][2];
+ sb[13] = stat_table[i][3];
+ return;
+ }
+ i++;
+ }
+ /* No error ?? */
+ printk(KERN_ERR "ata%u: called with no error (%02X)!\n", qc->ap->id, drv_stat);
+ /* additional-sense-code[-qualifier] */
+
+ sb[0] = 0x70;
+ sb[2] = MEDIUM_ERROR;
+ sb[7] = 0x0A;
+ if (cmd->sc_data_direction == SCSI_DATA_READ) {
+ sb[12] = 0x11; /* "unrecovered read error" */
+ sb[13] = 0x04;
+ } else {
+ sb[12] = 0x0C; /* "write error - */
+ sb[13] = 0x02; /* auto-reallocation failed" */
+ }
+}
+
+/**
+ * ata_scsi_slave_config - Set SCSI device attributes
+ * @sdev: SCSI device to examine
+ *
+ * This is called before we actually start reading
+ * and writing to the device, to configure certain
+ * SCSI mid-layer behaviors.
+ *
+ * LOCKING:
+ * Defined by SCSI layer. We don't really care.
+ */
+
+int ata_scsi_slave_config(struct scsi_device *sdev)
+{
+ sdev->use_10_for_rw = 1;
+ sdev->use_10_for_ms = 1;
+
+ blk_queue_max_phys_segments(sdev->request_queue, LIBATA_MAX_PRD);
+
+ if (sdev->id < ATA_MAX_DEVICES) {
+ struct ata_port *ap;
+ struct ata_device *dev;
+
+ ap = (struct ata_port *) &sdev->host->hostdata[0];
+ dev = &ap->device[sdev->id];
+
+ /* TODO: 1024 is an arbitrary number, not the
+ * hardware maximum. This should be increased to
+ * 65534 when Jens Axboe's patch for dynamically
+ * determining max_sectors is merged.
+ */
+ if ((dev->flags & ATA_DFLAG_LBA48) &&
+ ((dev->flags & ATA_DFLAG_LOCK_SECTORS) == 0)) {
+ sdev->host->max_sectors = 2048;
+ blk_queue_max_sectors(sdev->request_queue, 2048);
+ }
+ }
+
+ return 0; /* scsi layer doesn't check return value, sigh */
+}
+
+/**
+ * ata_scsi_error - SCSI layer error handler callback
+ * @host: SCSI host on which error occurred
+ *
+ * Handles SCSI-layer-thrown error events.
+ *
+ * LOCKING:
+ * Inherited from SCSI layer (none, can sleep)
+ *
+ * RETURNS:
+ * Zero.
+ */
+
+int ata_scsi_error(struct Scsi_Host *host)
+{
+ struct ata_port *ap;
+
+ DPRINTK("ENTER\n");
+
+ ap = (struct ata_port *) &host->hostdata[0];
+ ap->ops->eng_timeout(ap);
+
+ /* TODO: this is per-command; when queueing is supported
+ * this code will either change or move to a more
+ * appropriate place
+ */
+ host->host_failed--;
+
+ DPRINTK("EXIT\n");
+ return 0;
+}
+
+/**
+ * ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate (ignored)
+ *
+ * Sets up an ATA taskfile to issue FLUSH CACHE or
+ * FLUSH CACHE EXT.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+
+ tf->flags |= ATA_TFLAG_DEVICE;
+ tf->protocol = ATA_PROT_NODATA;
+
+ if ((tf->flags & ATA_TFLAG_LBA48) &&
+ (ata_id_has_flush_ext(qc->dev->id)))
+ tf->command = ATA_CMD_FLUSH_EXT;
+ else
+ tf->command = ATA_CMD_FLUSH;
+
+ return 0;
+}
+
+/**
+ * ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate
+ *
+ * Converts SCSI VERIFY command to an ATA READ VERIFY command.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;
+ u64 dev_sectors = qc->dev->n_sectors;
+ u64 sect = 0;
+ u32 n_sect = 0;
+
+ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf->protocol = ATA_PROT_NODATA;
+ tf->device |= ATA_LBA;
+
+ if (scsicmd[0] == VERIFY) {
+ sect |= ((u64)scsicmd[2]) << 24;
+ sect |= ((u64)scsicmd[3]) << 16;
+ sect |= ((u64)scsicmd[4]) << 8;
+ sect |= ((u64)scsicmd[5]);
+
+ n_sect |= ((u32)scsicmd[7]) << 8;
+ n_sect |= ((u32)scsicmd[8]);
+ }
+
+ else if (scsicmd[0] == VERIFY_16) {
+ sect |= ((u64)scsicmd[2]) << 56;
+ sect |= ((u64)scsicmd[3]) << 48;
+ sect |= ((u64)scsicmd[4]) << 40;
+ sect |= ((u64)scsicmd[5]) << 32;
+ sect |= ((u64)scsicmd[6]) << 24;
+ sect |= ((u64)scsicmd[7]) << 16;
+ sect |= ((u64)scsicmd[8]) << 8;
+ sect |= ((u64)scsicmd[9]);
+
+ n_sect |= ((u32)scsicmd[10]) << 24;
+ n_sect |= ((u32)scsicmd[11]) << 16;
+ n_sect |= ((u32)scsicmd[12]) << 8;
+ n_sect |= ((u32)scsicmd[13]);
+ }
+
+ else
+ return 1;
+
+ if (!n_sect)
+ return 1;
+ if (sect >= dev_sectors)
+ return 1;
+ if ((sect + n_sect) > dev_sectors)
+ return 1;
+ if (lba48) {
+ if (n_sect > (64 * 1024))
+ return 1;
+ } else {
+ if (n_sect > 256)
+ return 1;
+ }
+
+ if (lba48) {
+ tf->command = ATA_CMD_VERIFY_EXT;
+
+ tf->hob_nsect = (n_sect >> 8) & 0xff;
+
+ tf->hob_lbah = (sect >> 40) & 0xff;
+ tf->hob_lbam = (sect >> 32) & 0xff;
+ tf->hob_lbal = (sect >> 24) & 0xff;
+ } else {
+ tf->command = ATA_CMD_VERIFY;
+
+ tf->device |= (sect >> 24) & 0xf;
+ }
+
+ tf->nsect = n_sect & 0xff;
+
+ tf->lbah = (sect >> 16) & 0xff;
+ tf->lbam = (sect >> 8) & 0xff;
+ tf->lbal = sect & 0xff;
+
+ return 0;
+}
+
+/**
+ * ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one
+ * @qc: Storage for translated ATA taskfile
+ * @scsicmd: SCSI command to translate
+ *
+ * Converts any of six SCSI read/write commands into the
+ * ATA counterpart, including starting sector (LBA),
+ * sector count, and taking into account the device's LBA48
+ * support.
+ *
+ * Commands %READ_6, %READ_10, %READ_16, %WRITE_6, %WRITE_10, and
+ * %WRITE_16 are currently supported.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on error.
+ */
+
+static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;
+
+ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ tf->protocol = qc->dev->xfer_protocol;
+ tf->device |= ATA_LBA;
+
+ if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 ||
+ scsicmd[0] == READ_16) {
+ tf->command = qc->dev->read_cmd;
+ } else {
+ tf->command = qc->dev->write_cmd;
+ tf->flags |= ATA_TFLAG_WRITE;
+ }
+
+ if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) {
+ if (lba48) {
+ tf->hob_nsect = scsicmd[7];
+ tf->hob_lbal = scsicmd[2];
+
+ qc->nsect = ((unsigned int)scsicmd[7] << 8) |
+ scsicmd[8];
+ } else {
+ /* if we don't support LBA48 addressing, the request
+ * -may- be too large. */
+ if ((scsicmd[2] & 0xf0) || scsicmd[7])
+ return 1;
+
+ /* stores LBA27:24 in lower 4 bits of device reg */
+ tf->device |= scsicmd[2];
+
+ qc->nsect = scsicmd[8];
+ }
+
+ tf->nsect = scsicmd[8];
+ tf->lbal = scsicmd[5];
+ tf->lbam = scsicmd[4];
+ tf->lbah = scsicmd[3];
+
+ VPRINTK("ten-byte command\n");
+ return 0;
+ }
+
+ if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) {
+ qc->nsect = tf->nsect = scsicmd[4];
+ tf->lbal = scsicmd[3];
+ tf->lbam = scsicmd[2];
+ tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */
+
+ VPRINTK("six-byte command\n");
+ return 0;
+ }
+
+ if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) {
+ /* rule out impossible LBAs and sector counts */
+ if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11])
+ return 1;
+
+ if (lba48) {
+ tf->hob_nsect = scsicmd[12];
+ tf->hob_lbal = scsicmd[6];
+ tf->hob_lbam = scsicmd[5];
+ tf->hob_lbah = scsicmd[4];
+
+ qc->nsect = ((unsigned int)scsicmd[12] << 8) |
+ scsicmd[13];
+ } else {
+ /* once again, filter out impossible non-zero values */
+ if (scsicmd[4] || scsicmd[5] || scsicmd[12] ||
+ (scsicmd[6] & 0xf0))
+ return 1;
+
+ /* stores LBA27:24 in lower 4 bits of device reg */
+ tf->device |= scsicmd[6];
+
+ qc->nsect = scsicmd[13];
+ }
+
+ tf->nsect = scsicmd[13];
+ tf->lbal = scsicmd[9];
+ tf->lbam = scsicmd[8];
+ tf->lbah = scsicmd[7];
+
+ VPRINTK("sixteen-byte command\n");
+ return 0;
+ }
+
+ DPRINTK("no-byte command\n");
+ return 1;
+}
+
+static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ)))
+ ata_to_sense_error(qc, drv_stat);
+ else
+ cmd->result = SAM_STAT_GOOD;
+
+ qc->scsidone(cmd);
+
+ return 0;
+}
+
+/**
+ * ata_scsi_translate - Translate then issue SCSI command to ATA device
+ * @ap: ATA port to which the command is addressed
+ * @dev: ATA device to which the command is addressed
+ * @cmd: SCSI command to execute
+ * @done: SCSI command completion function
+ * @xlat_func: Actor which translates @cmd to an ATA taskfile
+ *
+ * Our ->queuecommand() function has decided that the SCSI
+ * command issued can be directly translated into an ATA
+ * command, rather than handled internally.
+ *
+ * This function sets up an ata_queued_cmd structure for the
+ * SCSI command, and sends that ata_queued_cmd to the hardware.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev,
+ struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *),
+ ata_xlat_func_t xlat_func)
+{
+ struct ata_queued_cmd *qc;
+ u8 *scsicmd = cmd->cmnd;
+
+ VPRINTK("ENTER\n");
+
+ qc = ata_scsi_qc_new(ap, dev, cmd, done);
+ if (!qc)
+ return;
+
+ /* data is present; dma-map it */
+ if (cmd->sc_data_direction == SCSI_DATA_READ ||
+ cmd->sc_data_direction == SCSI_DATA_WRITE) {
+ if (unlikely(cmd->request_bufflen < 1)) {
+ printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n",
+ ap->id, dev->devno);
+ goto err_out;
+ }
+
+ if (cmd->use_sg)
+ ata_sg_init(qc, cmd->request_buffer, cmd->use_sg);
+ else
+ ata_sg_init_one(qc, cmd->request_buffer,
+ cmd->request_bufflen);
+
+ qc->dma_dir = cmd->sc_data_direction;
+ }
+
+ qc->complete_fn = ata_scsi_qc_complete;
+
+ if (xlat_func(qc, scsicmd))
+ goto err_out;
+
+ /* select device, send command to hardware */
+ if (ata_qc_issue(qc))
+ goto err_out;
+
+ VPRINTK("EXIT\n");
+ return;
+
+err_out:
+ ata_qc_free(qc);
+ ata_bad_cdb(cmd, done);
+ DPRINTK("EXIT - badcmd\n");
+}
+
+/**
+ * ata_scsi_rbuf_get - Map response buffer.
+ * @cmd: SCSI command containing buffer to be mapped.
+ * @buf_out: Pointer to mapped area.
+ *
+ * Maps buffer contained within SCSI command @cmd.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Length of response buffer.
+ */
+
+static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out)
+{
+ u8 *buf;
+ unsigned int buflen;
+
+ if (cmd->use_sg) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *) cmd->request_buffer;
+ buf = kmap_atomic(sg->page, KM_USER0) + sg->offset;
+ buflen = sg->length;
+ } else {
+ buf = cmd->request_buffer;
+ buflen = cmd->request_bufflen;
+ }
+
+ *buf_out = buf;
+ return buflen;
+}
+
+/**
+ * ata_scsi_rbuf_put - Unmap response buffer.
+ * @cmd: SCSI command containing buffer to be unmapped.
+ * @buf: buffer to unmap
+ *
+ * Unmaps response buffer contained within @cmd.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf)
+{
+ if (cmd->use_sg) {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *) cmd->request_buffer;
+ kunmap_atomic(buf - sg->offset, KM_USER0);
+ }
+}
+
+/**
+ * ata_scsi_rbuf_fill - wrapper for SCSI command simulators
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @actor: Callback hook for desired SCSI command simulator
+ *
+ * Takes care of the hard work of simulating a SCSI command...
+ * Mapping the response buffer, calling the command's handler,
+ * and handling the handler's return value. This return value
+ * indicates whether the handler wishes the SCSI command to be
+ * completed successfully, or not.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
+ unsigned int (*actor) (struct ata_scsi_args *args,
+ u8 *rbuf, unsigned int buflen))
+{
+ u8 *rbuf;
+ unsigned int buflen, rc;
+ struct scsi_cmnd *cmd = args->cmd;
+
+ buflen = ata_scsi_rbuf_get(cmd, &rbuf);
+ memset(rbuf, 0, buflen);
+ rc = actor(args, rbuf, buflen);
+ ata_scsi_rbuf_put(cmd, rbuf);
+
+ if (rc)
+ ata_bad_cdb(cmd, args->done);
+ else {
+ cmd->result = SAM_STAT_GOOD;
+ args->done(cmd);
+ }
+}
+
+/**
+ * ata_scsiop_inq_std - Simulate INQUIRY command
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Returns standard device identification data associated
+ * with non-EVPD INQUIRY command output.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ u8 hdr[] = {
+ TYPE_DISK,
+ 0,
+ 0x5, /* claim SPC-3 version compatibility */
+ 2,
+ 95 - 4
+ };
+
+ /* set scsi removeable (RMB) bit per ata bit */
+ if (ata_id_removeable(args->id))
+ hdr[1] |= (1 << 7);
+
+ VPRINTK("ENTER\n");
+
+ memcpy(rbuf, hdr, sizeof(hdr));
+
+ if (buflen > 35) {
+ memcpy(&rbuf[8], "ATA ", 8);
+ ata_dev_id_string(args->id, &rbuf[16], ATA_ID_PROD_OFS, 16);
+ ata_dev_id_string(args->id, &rbuf[32], ATA_ID_FW_REV_OFS, 4);
+ if (rbuf[32] == 0 || rbuf[32] == ' ')
+ memcpy(&rbuf[32], "n/a ", 4);
+ }
+
+ if (buflen > 63) {
+ const u8 versions[] = {
+ 0x60, /* SAM-3 (no version claimed) */
+
+ 0x03,
+ 0x20, /* SBC-2 (no version claimed) */
+
+ 0x02,
+ 0x60 /* SPC-3 (no version claimed) */
+ };
+
+ memcpy(rbuf + 59, versions, sizeof(versions));
+ }
+
+ return 0;
+}
+
+/**
+ * ata_scsiop_inq_00 - Simulate INQUIRY EVPD page 0, list of pages
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Returns list of inquiry EVPD pages available.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ const u8 pages[] = {
+ 0x00, /* page 0x00, this page */
+ 0x80, /* page 0x80, unit serial no page */
+ 0x83 /* page 0x83, device ident page */
+ };
+ rbuf[3] = sizeof(pages); /* number of supported EVPD pages */
+
+ if (buflen > 6)
+ memcpy(rbuf + 4, pages, sizeof(pages));
+
+ return 0;
+}
+
+/**
+ * ata_scsiop_inq_80 - Simulate INQUIRY EVPD page 80, device serial number
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Returns ATA device serial number.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ const u8 hdr[] = {
+ 0,
+ 0x80, /* this page code */
+ 0,
+ ATA_SERNO_LEN, /* page len */
+ };
+ memcpy(rbuf, hdr, sizeof(hdr));
+
+ if (buflen > (ATA_SERNO_LEN + 4 - 1))
+ ata_dev_id_string(args->id, (unsigned char *) &rbuf[4],
+ ATA_ID_SERNO_OFS, ATA_SERNO_LEN);
+
+ return 0;
+}
+
+static const char *inq_83_str = "Linux ATA-SCSI simulator";
+
+/**
+ * ata_scsiop_inq_83 - Simulate INQUIRY EVPD page 83, device identity
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Returns device identification. Currently hardcoded to
+ * return "Linux ATA-SCSI simulator".
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ rbuf[1] = 0x83; /* this page code */
+ rbuf[3] = 4 + strlen(inq_83_str); /* page len */
+
+ /* our one and only identification descriptor (vendor-specific) */
+ if (buflen > (strlen(inq_83_str) + 4 + 4 - 1)) {
+ rbuf[4 + 0] = 2; /* code set: ASCII */
+ rbuf[4 + 3] = strlen(inq_83_str);
+ memcpy(rbuf + 4 + 4, inq_83_str, strlen(inq_83_str));
+ }
+
+ return 0;
+}
+
+/**
+ * ata_scsiop_noop -
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * No operation. Simply returns success to caller, to indicate
+ * that the caller should successfully complete this SCSI command.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ VPRINTK("ENTER\n");
+ return 0;
+}
+
+/**
+ * ata_msense_push - Push data onto MODE SENSE data output buffer
+ * @ptr_io: (input/output) Location to store more output data
+ * @last: End of output data buffer
+ * @buf: Pointer to BLOB being added to output buffer
+ * @buflen: Length of BLOB
+ *
+ * Store MODE SENSE data on an output buffer.
+ *
+ * LOCKING:
+ * None.
+ */
+
+static void ata_msense_push(u8 **ptr_io, const u8 *last,
+ const u8 *buf, unsigned int buflen)
+{
+ u8 *ptr = *ptr_io;
+
+ if ((ptr + buflen - 1) > last)
+ return;
+
+ memcpy(ptr, buf, buflen);
+
+ ptr += buflen;
+
+ *ptr_io = ptr;
+}
+
+/**
+ * ata_msense_caching - Simulate MODE SENSE caching info page
+ * @id: device IDENTIFY data
+ * @ptr_io: (input/output) Location to store more output data
+ * @last: End of output data buffer
+ *
+ * Generate a caching info page, which conditionally indicates
+ * write caching to the SCSI layer, depending on device
+ * capabilities.
+ *
+ * LOCKING:
+ * None.
+ */
+
+static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
+ const u8 *last)
+{
+ u8 page[] = {
+ 0x8, /* page code */
+ 0x12, /* page length */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 zeroes */
+ 0, 0, 0, 0, 0, 0, 0, 0 /* 8 zeroes */
+ };
+
+ if (ata_id_wcache_enabled(id))
+ page[2] |= (1 << 2); /* write cache enable */
+ if (!ata_id_rahead_enabled(id))
+ page[12] |= (1 << 5); /* disable read ahead */
+
+ ata_msense_push(ptr_io, last, page, sizeof(page));
+ return sizeof(page);
+}
+
+/**
+ * ata_msense_ctl_mode - Simulate MODE SENSE control mode page
+ * @dev: Device associated with this MODE SENSE command
+ * @ptr_io: (input/output) Location to store more output data
+ * @last: End of output data buffer
+ *
+ * Generate a generic MODE SENSE control mode page.
+ *
+ * LOCKING:
+ * None.
+ */
+
+static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)
+{
+ const u8 page[] = {0xa, 0xa, 6, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30};
+
+ /* byte 2: set the descriptor format sense data bit (bit 2)
+ * since we need to support returning this format for SAT
+ * commands and any SCSI commands against a 48b LBA device.
+ */
+
+ ata_msense_push(ptr_io, last, page, sizeof(page));
+ return sizeof(page);
+}
+
+/**
+ * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page
+ * @dev: Device associated with this MODE SENSE command
+ * @ptr_io: (input/output) Location to store more output data
+ * @last: End of output data buffer
+ *
+ * Generate a generic MODE SENSE r/w error recovery page.
+ *
+ * LOCKING:
+ * None.
+ */
+
+static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
+{
+ const u8 page[] = {
+ 0x1, /* page code */
+ 0xa, /* page length */
+ (1 << 7) | (1 << 6), /* note auto r/w reallocation */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */
+ };
+
+ ata_msense_push(ptr_io, last, page, sizeof(page));
+ return sizeof(page);
+}
+
+/**
+ * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Simulate MODE SENSE commands.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ u8 *scsicmd = args->cmd->cmnd, *p, *last;
+ unsigned int page_control, six_byte, output_len;
+
+ VPRINTK("ENTER\n");
+
+ six_byte = (scsicmd[0] == MODE_SENSE);
+
+ /* we only support saved and current values (which we treat
+ * in the same manner)
+ */
+ page_control = scsicmd[2] >> 6;
+ if ((page_control != 0) && (page_control != 3))
+ return 1;
+
+ if (six_byte)
+ output_len = 4;
+ else
+ output_len = 8;
+
+ p = rbuf + output_len;
+ last = rbuf + buflen - 1;
+
+ switch(scsicmd[2] & 0x3f) {
+ case 0x01: /* r/w error recovery */
+ output_len += ata_msense_rw_recovery(&p, last);
+ break;
+
+ case 0x08: /* caching */
+ output_len += ata_msense_caching(args->id, &p, last);
+ break;
+
+ case 0x0a: { /* control mode */
+ output_len += ata_msense_ctl_mode(&p, last);
+ break;
+ }
+
+ case 0x3f: /* all pages */
+ output_len += ata_msense_rw_recovery(&p, last);
+ output_len += ata_msense_caching(args->id, &p, last);
+ output_len += ata_msense_ctl_mode(&p, last);
+ break;
+
+ default: /* invalid page code */
+ return 1;
+ }
+
+ if (six_byte) {
+ output_len--;
+ rbuf[0] = output_len;
+ } else {
+ output_len -= 2;
+ rbuf[0] = output_len >> 8;
+ rbuf[1] = output_len;
+ }
+
+ return 0;
+}
+
+/**
+ * ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Simulate READ CAPACITY commands.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ u64 n_sectors;
+ u32 tmp;
+
+ VPRINTK("ENTER\n");
+
+ if (ata_id_has_lba48(args->id))
+ n_sectors = ata_id_u64(args->id, 100);
+ else
+ n_sectors = ata_id_u32(args->id, 60);
+ n_sectors--; /* ATA TotalUserSectors - 1 */
+
+ tmp = n_sectors; /* note: truncates, if lba48 */
+ if (args->cmd->cmnd[0] == READ_CAPACITY) {
+ /* sector count, 32-bit */
+ rbuf[0] = tmp >> (8 * 3);
+ rbuf[1] = tmp >> (8 * 2);
+ rbuf[2] = tmp >> (8 * 1);
+ rbuf[3] = tmp;
+
+ /* sector size */
+ tmp = ATA_SECT_SIZE;
+ rbuf[6] = tmp >> 8;
+ rbuf[7] = tmp;
+
+ } else {
+ /* sector count, 64-bit */
+ rbuf[2] = n_sectors >> (8 * 7);
+ rbuf[3] = n_sectors >> (8 * 6);
+ rbuf[4] = n_sectors >> (8 * 5);
+ rbuf[5] = n_sectors >> (8 * 4);
+ rbuf[6] = tmp >> (8 * 3);
+ rbuf[7] = tmp >> (8 * 2);
+ rbuf[8] = tmp >> (8 * 1);
+ rbuf[9] = tmp;
+
+ /* sector size */
+ tmp = ATA_SECT_SIZE;
+ rbuf[12] = tmp >> 8;
+ rbuf[13] = tmp;
+ }
+
+ return 0;
+}
+
+/**
+ * ata_scsiop_report_luns - Simulate REPORT LUNS command
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ * @buflen: Response buffer length.
+ *
+ * Simulate REPORT LUNS command.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen)
+{
+ VPRINTK("ENTER\n");
+ rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */
+
+ return 0;
+}
+
+/**
+ * ata_scsi_badcmd - End a SCSI request with an error
+ * @cmd: SCSI request to be handled
+ * @done: SCSI command completion function
+ * @asc: SCSI-defined additional sense code
+ * @ascq: SCSI-defined additional sense code qualifier
+ *
+ * Helper function that completes a SCSI command with
+ * %SAM_STAT_CHECK_CONDITION, with a sense key %ILLEGAL_REQUEST
+ * and the specified additional sense codes.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq)
+{
+ DPRINTK("ENTER\n");
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ cmd->sense_buffer[0] = 0x70;
+ cmd->sense_buffer[2] = ILLEGAL_REQUEST;
+ cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */
+ cmd->sense_buffer[12] = asc;
+ cmd->sense_buffer[13] = ascq;
+
+ done(cmd);
+}
+
+static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) {
+ DPRINTK("request check condition\n");
+
+ cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ qc->scsidone(cmd);
+
+ return 1;
+ } else {
+ u8 *scsicmd = cmd->cmnd;
+
+ if (scsicmd[0] == INQUIRY) {
+ u8 *buf = NULL;
+ unsigned int buflen;
+
+ buflen = ata_scsi_rbuf_get(cmd, &buf);
+ buf[2] = 0x5;
+ buf[3] = (buf[3] & 0xf0) | 2;
+ ata_scsi_rbuf_put(cmd, buf);
+ }
+ cmd->result = SAM_STAT_GOOD;
+ }
+
+ qc->scsidone(cmd);
+
+ return 0;
+}
+/**
+ * atapi_xlat - Initialize PACKET taskfile
+ * @qc: command structure to be initialized
+ * @scsicmd: SCSI CDB associated with this PACKET command
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Zero on success, non-zero on failure.
+ */
+
+static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+ struct ata_device *dev = qc->dev;
+ int using_pio = (dev->flags & ATA_DFLAG_PIO);
+ int nodata = (cmd->sc_data_direction == SCSI_DATA_NONE);
+
+ if (!using_pio)
+ /* Check whether ATAPI DMA is safe */
+ if (ata_check_atapi_dma(qc))
+ using_pio = 1;
+
+ memcpy(&qc->cdb, scsicmd, qc->ap->cdb_len);
+
+ qc->complete_fn = atapi_qc_complete;
+
+ qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+ if (cmd->sc_data_direction == SCSI_DATA_WRITE) {
+ qc->tf.flags |= ATA_TFLAG_WRITE;
+ DPRINTK("direction: write\n");
+ }
+
+ qc->tf.command = ATA_CMD_PACKET;
+
+ /* no data, or PIO data xfer */
+ if (using_pio || nodata) {
+ if (nodata)
+ qc->tf.protocol = ATA_PROT_ATAPI_NODATA;
+ else
+ qc->tf.protocol = ATA_PROT_ATAPI;
+ qc->tf.lbam = (8 * 1024) & 0xff;
+ qc->tf.lbah = (8 * 1024) >> 8;
+ }
+
+ /* DMA data xfer */
+ else {
+ qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+ qc->tf.feature |= ATAPI_PKT_DMA;
+
+#ifdef ATAPI_ENABLE_DMADIR
+ /* some SATA bridges need us to indicate data xfer direction */
+ if (cmd->sc_data_direction != SCSI_DATA_WRITE)
+ qc->tf.feature |= ATAPI_DMADIR;
+#endif
+ }
+
+ qc->nbytes = cmd->bufflen;
+
+ return 0;
+}
+
+/**
+ * ata_scsi_find_dev - lookup ata_device from scsi_cmnd
+ * @ap: ATA port to which the device is attached
+ * @scsidev: SCSI device from which we derive the ATA device
+ *
+ * Given various information provided in struct scsi_cmnd,
+ * map that onto an ATA bus, and using that mapping
+ * determine which ata_device is associated with the
+ * SCSI command to be sent.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * Associated ATA device, or %NULL if not found.
+ */
+
+static struct ata_device *
+ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev)
+{
+ struct ata_device *dev;
+
+ /* skip commands not addressed to targets we simulate */
+ if (likely(scsidev->id < ATA_MAX_DEVICES))
+ dev = &ap->device[scsidev->id];
+ else
+ return NULL;
+
+ if (unlikely((scsidev->channel != 0) ||
+ (scsidev->lun != 0)))
+ return NULL;
+
+ if (unlikely(!ata_dev_present(dev)))
+ return NULL;
+
+#ifndef ATA_ENABLE_ATAPI
+ if (unlikely(dev->class == ATA_DEV_ATAPI))
+ return NULL;
+#endif
+
+ return dev;
+}
+
+/**
+ * ata_get_xlat_func - check if SCSI to ATA translation is possible
+ * @dev: ATA device
+ * @cmd: SCSI command opcode to consider
+ *
+ * Look up the SCSI command given, and determine whether the
+ * SCSI command is to be translated or simulated.
+ *
+ * RETURNS:
+ * Pointer to translation function if possible, %NULL if not.
+ */
+
+static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
+{
+ switch (cmd) {
+ case READ_6:
+ case READ_10:
+ case READ_16:
+
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_16:
+ return ata_scsi_rw_xlat;
+
+ case SYNCHRONIZE_CACHE:
+ if (ata_try_flush_cache(dev))
+ return ata_scsi_flush_xlat;
+ break;
+
+ case VERIFY:
+ case VERIFY_16:
+ return ata_scsi_verify_xlat;
+ }
+
+ return NULL;
+}
+
+/**
+ * ata_scsi_dump_cdb - dump SCSI command contents to dmesg
+ * @ap: ATA port to which the command was being sent
+ * @cmd: SCSI command to dump
+ *
+ * Prints the contents of a SCSI command via printk().
+ */
+
+static inline void ata_scsi_dump_cdb(struct ata_port *ap,
+ struct scsi_cmnd *cmd)
+{
+#ifdef ATA_DEBUG
+ struct scsi_device *scsidev = cmd->device;
+ u8 *scsicmd = cmd->cmnd;
+
+ DPRINTK("CDB (%u:%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ap->id,
+ scsidev->channel, scsidev->id, scsidev->lun,
+ scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3],
+ scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7],
+ scsicmd[8]);
+#endif
+}
+
+/**
+ * ata_scsi_queuecmd - Issue SCSI cdb to libata-managed device
+ * @cmd: SCSI command to be sent
+ * @done: Completion function, called when command is complete
+ *
+ * In some cases, this function translates SCSI commands into
+ * ATA taskfiles, and queues the taskfiles to be sent to
+ * hardware. In other cases, this function simulates a
+ * SCSI device by evaluating and responding to certain
+ * SCSI commands. This creates the overall effect of
+ * ATA and ATAPI devices appearing as SCSI devices.
+ *
+ * LOCKING:
+ * Releases scsi-layer-held lock, and obtains host_set lock.
+ *
+ * RETURNS:
+ * Zero.
+ */
+
+int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct ata_port *ap;
+ struct ata_device *dev;
+ struct scsi_device *scsidev = cmd->device;
+
+ ap = (struct ata_port *) &scsidev->host->hostdata[0];
+
+ ata_scsi_dump_cdb(ap, cmd);
+
+ dev = ata_scsi_find_dev(ap, scsidev);
+ if (unlikely(!dev)) {
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ goto out_unlock;
+ }
+
+ if (dev->class == ATA_DEV_ATA) {
+ ata_xlat_func_t xlat_func = ata_get_xlat_func(dev,
+ cmd->cmnd[0]);
+
+ if (xlat_func)
+ ata_scsi_translate(ap, dev, cmd, done, xlat_func);
+ else
+ ata_scsi_simulate(dev->id, cmd, done);
+ } else
+ ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);
+
+out_unlock:
+ return 0;
+}
+
+/**
+ * ata_scsi_simulate - simulate SCSI command on ATA device
+ * @id: current IDENTIFY data for target device.
+ * @cmd: SCSI command being sent to device.
+ * @done: SCSI command completion function.
+ *
+ * Interprets and directly executes a select list of SCSI commands
+ * that can be handled internally.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+void ata_scsi_simulate(u16 *id,
+ struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct ata_scsi_args args;
+ u8 *scsicmd = cmd->cmnd;
+
+ args.id = id;
+ args.cmd = cmd;
+ args.done = done;
+
+ switch(scsicmd[0]) {
+ /* no-op's, complete with success */
+ case SYNCHRONIZE_CACHE:
+ case REZERO_UNIT:
+ case SEEK_6:
+ case SEEK_10:
+ case TEST_UNIT_READY:
+ case FORMAT_UNIT: /* FIXME: correct? */
+ case SEND_DIAGNOSTIC: /* FIXME: correct? */
+ ata_scsi_rbuf_fill(&args, ata_scsiop_noop);
+ break;
+
+ case INQUIRY:
+ if (scsicmd[1] & 2) /* is CmdDt set? */
+ ata_bad_cdb(cmd, done);
+ else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */
+ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
+ else if (scsicmd[2] == 0x00)
+ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00);
+ else if (scsicmd[2] == 0x80)
+ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80);
+ else if (scsicmd[2] == 0x83)
+ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83);
+ else
+ ata_bad_cdb(cmd, done);
+ break;
+
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense);
+ break;
+
+ case MODE_SELECT: /* unconditionally return */
+ case MODE_SELECT_10: /* bad-field-in-cdb */
+ ata_bad_cdb(cmd, done);
+ break;
+
+ case READ_CAPACITY:
+ ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
+ break;
+
+ case SERVICE_ACTION_IN:
+ if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
+ ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
+ else
+ ata_bad_cdb(cmd, done);
+ break;
+
+ case REPORT_LUNS:
+ ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns);
+ break;
+
+ /* mandantory commands we haven't implemented yet */
+ case REQUEST_SENSE:
+
+ /* all other commands */
+ default:
+ ata_bad_scsiop(cmd, done);
+ break;
+ }
+}
+
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
new file mode 100644
index 000000000000..6518226b8f87
--- /dev/null
+++ b/drivers/scsi/libata.h
@@ -0,0 +1,89 @@
+/*
+ libata.h - helper library for ATA
+
+ Copyright 2003-2004 Red Hat, Inc. All rights reserved.
+ Copyright 2003-2004 Jeff Garzik
+
+ The contents of this file are subject to the Open
+ Software License version 1.1 that can be found at
+ http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ by reference.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the GNU General Public License version 2 (the "GPL") as distributed
+ in the kernel source COPYING file, in which case the provisions of
+ the GPL are applicable instead of the above. If you wish to allow
+ the use of your version of this file only under the terms of the
+ GPL and not to allow others to use your version of this file under
+ the OSL, indicate your decision by deleting the provisions above and
+ replace them with the notice and other provisions required by the GPL.
+ If you do not delete the provisions above, a recipient may use your
+ version of this file under either the OSL or the GPL.
+
+ */
+
+#ifndef __LIBATA_H__
+#define __LIBATA_H__
+
+#define DRV_NAME "libata"
+#define DRV_VERSION "1.10" /* must be exactly four chars */
+
+struct ata_scsi_args {
+ u16 *id;
+ struct scsi_cmnd *cmd;
+ void (*done)(struct scsi_cmnd *);
+};
+
+/* libata-core.c */
+extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
+ struct ata_device *dev);
+extern void ata_qc_free(struct ata_queued_cmd *qc);
+extern int ata_qc_issue(struct ata_queued_cmd *qc);
+extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
+extern void ata_dev_select(struct ata_port *ap, unsigned int device,
+ unsigned int wait, unsigned int can_sleep);
+extern void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf);
+extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
+
+
+/* libata-scsi.c */
+extern void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat);
+extern int ata_scsi_error(struct Scsi_Host *host);
+extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+
+extern unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+
+extern unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern unsigned int ata_scsiop_sync_cache(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf,
+ unsigned int buflen);
+extern void ata_scsi_badcmd(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *),
+ u8 asc, u8 ascq);
+extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
+ unsigned int (*actor) (struct ata_scsi_args *args,
+ u8 *rbuf, unsigned int buflen));
+
+static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ ata_scsi_badcmd(cmd, done, 0x20, 0x00);
+}
+
+static inline void ata_bad_cdb(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ ata_scsi_badcmd(cmd, done, 0x24, 0x00);
+}
+
+#endif /* __LIBATA_H__ */
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
new file mode 100644
index 000000000000..3ef2a1443996
--- /dev/null
+++ b/drivers/scsi/mac53c94.c
@@ -0,0 +1,582 @@
+/*
+ * SCSI low-level driver for the 53c94 SCSI bus adaptor found
+ * on Power Macintosh computers, controlling the external SCSI chain.
+ * We assume the 53c94 is connected to a DBDMA (descriptor-based DMA)
+ * controller.
+ *
+ * Paul Mackerras, August 1996.
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/dbdma.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/pci-bridge.h>
+#include <asm/macio.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "mac53c94.h"
+
+enum fsc_phase {
+ idle,
+ selecting,
+ dataing,
+ completing,
+ busfreeing,
+};
+
+struct fsc_state {
+ struct mac53c94_regs __iomem *regs;
+ int intr;
+ struct dbdma_regs __iomem *dma;
+ int dmaintr;
+ int clk_freq;
+ struct Scsi_Host *host;
+ struct scsi_cmnd *request_q;
+ struct scsi_cmnd *request_qtail;
+ struct scsi_cmnd *current_req; /* req we're currently working on */
+ enum fsc_phase phase; /* what we're currently trying to do */
+ struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
+ void *dma_cmd_space;
+ struct pci_dev *pdev;
+ dma_addr_t dma_addr;
+ struct macio_dev *mdev;
+};
+
+static void mac53c94_init(struct fsc_state *);
+static void mac53c94_start(struct fsc_state *);
+static void mac53c94_interrupt(int, void *, struct pt_regs *);
+static irqreturn_t do_mac53c94_interrupt(int, void *, struct pt_regs *);
+static void cmd_done(struct fsc_state *, int result);
+static void set_dma_cmds(struct fsc_state *, struct scsi_cmnd *);
+
+
+static int mac53c94_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct fsc_state *state;
+
+#if 0
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ int i;
+ printk(KERN_DEBUG "mac53c94_queue %p: command is", cmd);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ printk(" %.2x", cmd->cmnd[i]);
+ printk("\n" KERN_DEBUG "use_sg=%d request_bufflen=%d request_buffer=%p\n",
+ cmd->use_sg, cmd->request_bufflen, cmd->request_buffer);
+ }
+#endif
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+
+ state = (struct fsc_state *) cmd->device->host->hostdata;
+
+ if (state->request_q == NULL)
+ state->request_q = cmd;
+ else
+ state->request_qtail->host_scribble = (void *) cmd;
+ state->request_qtail = cmd;
+
+ if (state->phase == idle)
+ mac53c94_start(state);
+
+ return 0;
+}
+
+static int mac53c94_abort(struct scsi_cmnd *cmd)
+{
+ return FAILED;
+}
+
+static int mac53c94_host_reset(struct scsi_cmnd *cmd)
+{
+ struct fsc_state *state = (struct fsc_state *) cmd->device->host->hostdata;
+ struct mac53c94_regs __iomem *regs = state->regs;
+ struct dbdma_regs __iomem *dma = state->dma;
+
+ writel((RUN|PAUSE|FLUSH|WAKE) << 16, &dma->control);
+ writeb(CMD_SCSI_RESET, &regs->command); /* assert RST */
+ udelay(100); /* leave it on for a while (>= 25us) */
+ writeb(CMD_RESET, &regs->command);
+ udelay(20);
+ mac53c94_init(state);
+ writeb(CMD_NOP, &regs->command);
+ return SUCCESS;
+}
+
+static void mac53c94_init(struct fsc_state *state)
+{
+ struct mac53c94_regs __iomem *regs = state->regs;
+ struct dbdma_regs __iomem *dma = state->dma;
+ int x;
+
+ writeb(state->host->this_id | CF1_PAR_ENABLE, &regs->config1);
+ writeb(TIMO_VAL(250), &regs->sel_timeout); /* 250ms */
+ writeb(CLKF_VAL(state->clk_freq), &regs->clk_factor);
+ writeb(CF2_FEATURE_EN, &regs->config2);
+ writeb(0, &regs->config3);
+ writeb(0, &regs->sync_period);
+ writeb(0, &regs->sync_offset);
+ x = readb(&regs->interrupt);
+ writel((RUN|PAUSE|FLUSH|WAKE) << 16, &dma->control);
+}
+
+/*
+ * Start the next command for a 53C94.
+ * Should be called with interrupts disabled.
+ */
+static void mac53c94_start(struct fsc_state *state)
+{
+ struct scsi_cmnd *cmd;
+ struct mac53c94_regs __iomem *regs = state->regs;
+ int i;
+
+ if (state->phase != idle || state->current_req != NULL)
+ panic("inappropriate mac53c94_start (state=%p)", state);
+ if (state->request_q == NULL)
+ return;
+ state->current_req = cmd = state->request_q;
+ state->request_q = (struct scsi_cmnd *) cmd->host_scribble;
+
+ /* Off we go */
+ writeb(0, &regs->count_lo);
+ writeb(0, &regs->count_mid);
+ writeb(0, &regs->count_hi);
+ writeb(CMD_NOP + CMD_DMA_MODE, &regs->command);
+ udelay(1);
+ writeb(CMD_FLUSH, &regs->command);
+ udelay(1);
+ writeb(cmd->device->id, &regs->dest_id);
+ writeb(0, &regs->sync_period);
+ writeb(0, &regs->sync_offset);
+
+ /* load the command into the FIFO */
+ for (i = 0; i < cmd->cmd_len; ++i)
+ writeb(cmd->cmnd[i], &regs->fifo);
+
+ /* do select without ATN XXX */
+ writeb(CMD_SELECT, &regs->command);
+ state->phase = selecting;
+
+ if (cmd->use_sg > 0 || cmd->request_bufflen != 0)
+ set_dma_cmds(state, cmd);
+}
+
+static irqreturn_t do_mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = ((struct fsc_state *) dev_id)->current_req->device->host;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ mac53c94_interrupt(irq, dev_id, ptregs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void mac53c94_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ struct fsc_state *state = (struct fsc_state *) dev_id;
+ struct mac53c94_regs __iomem *regs = state->regs;
+ struct dbdma_regs __iomem *dma = state->dma;
+ struct scsi_cmnd *cmd = state->current_req;
+ int nb, stat, seq, intr;
+ static int mac53c94_errors;
+
+ /*
+ * Apparently, reading the interrupt register unlatches
+ * the status and sequence step registers.
+ */
+ seq = readb(&regs->seqstep);
+ stat = readb(&regs->status);
+ intr = readb(&regs->interrupt);
+
+#if 0
+ printk(KERN_DEBUG "mac53c94_intr, intr=%x stat=%x seq=%x phase=%d\n",
+ intr, stat, seq, state->phase);
+#endif
+
+ if (intr & INTR_RESET) {
+ /* SCSI bus was reset */
+ printk(KERN_INFO "external SCSI bus reset detected\n");
+ writeb(CMD_NOP, &regs->command);
+ writel(RUN << 16, &dma->control); /* stop dma */
+ cmd_done(state, DID_RESET << 16);
+ return;
+ }
+ if (intr & INTR_ILL_CMD) {
+ printk(KERN_ERR "53c94: invalid cmd, intr=%x stat=%x seq=%x phase=%d\n",
+ intr, stat, seq, state->phase);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ if (stat & STAT_ERROR) {
+#if 0
+ /* XXX these seem to be harmless? */
+ printk("53c94: bad error, intr=%x stat=%x seq=%x phase=%d\n",
+ intr, stat, seq, state->phase);
+#endif
+ ++mac53c94_errors;
+ writeb(CMD_NOP + CMD_DMA_MODE, &regs->command);
+ }
+ if (cmd == 0) {
+ printk(KERN_DEBUG "53c94: interrupt with no command active?\n");
+ return;
+ }
+ if (stat & STAT_PARITY) {
+ printk(KERN_ERR "mac53c94: parity error\n");
+ cmd_done(state, DID_PARITY << 16);
+ return;
+ }
+ switch (state->phase) {
+ case selecting:
+ if (intr & INTR_DISCONNECT) {
+ /* selection timed out */
+ cmd_done(state, DID_BAD_TARGET << 16);
+ return;
+ }
+ if (intr != INTR_BUS_SERV + INTR_DONE) {
+ printk(KERN_DEBUG "got intr %x during selection\n", intr);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ if ((seq & SS_MASK) != SS_DONE) {
+ printk(KERN_DEBUG "seq step %x after command\n", seq);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ writeb(CMD_NOP, &regs->command);
+ /* set DMA controller going if any data to transfer */
+ if ((stat & (STAT_MSG|STAT_CD)) == 0
+ && (cmd->use_sg > 0 || cmd->request_bufflen != 0)) {
+ nb = cmd->SCp.this_residual;
+ if (nb > 0xfff0)
+ nb = 0xfff0;
+ cmd->SCp.this_residual -= nb;
+ writeb(nb, &regs->count_lo);
+ writeb(nb >> 8, &regs->count_mid);
+ writeb(CMD_DMA_MODE + CMD_NOP, &regs->command);
+ writel(virt_to_phys(state->dma_cmds), &dma->cmdptr);
+ writel((RUN << 16) | RUN, &dma->control);
+ writeb(CMD_DMA_MODE + CMD_XFER_DATA, &regs->command);
+ state->phase = dataing;
+ break;
+ } else if ((stat & STAT_PHASE) == STAT_CD + STAT_IO) {
+ /* up to status phase already */
+ writeb(CMD_I_COMPLETE, &regs->command);
+ state->phase = completing;
+ } else {
+ printk(KERN_DEBUG "in unexpected phase %x after cmd\n",
+ stat & STAT_PHASE);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ break;
+
+ case dataing:
+ if (intr != INTR_BUS_SERV) {
+ printk(KERN_DEBUG "got intr %x before status\n", intr);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ if (cmd->SCp.this_residual != 0
+ && (stat & (STAT_MSG|STAT_CD)) == 0) {
+ /* Set up the count regs to transfer more */
+ nb = cmd->SCp.this_residual;
+ if (nb > 0xfff0)
+ nb = 0xfff0;
+ cmd->SCp.this_residual -= nb;
+ writeb(nb, &regs->count_lo);
+ writeb(nb >> 8, &regs->count_mid);
+ writeb(CMD_DMA_MODE + CMD_NOP, &regs->command);
+ writeb(CMD_DMA_MODE + CMD_XFER_DATA, &regs->command);
+ break;
+ }
+ if ((stat & STAT_PHASE) != STAT_CD + STAT_IO) {
+ printk(KERN_DEBUG "intr %x before data xfer complete\n", intr);
+ }
+ writel(RUN << 16, &dma->control); /* stop dma */
+ if (cmd->use_sg != 0) {
+ pci_unmap_sg(state->pdev,
+ (struct scatterlist *)cmd->request_buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ } else {
+ pci_unmap_single(state->pdev, state->dma_addr,
+ cmd->request_bufflen, cmd->sc_data_direction);
+ }
+ /* should check dma status */
+ writeb(CMD_I_COMPLETE, &regs->command);
+ state->phase = completing;
+ break;
+ case completing:
+ if (intr != INTR_DONE) {
+ printk(KERN_DEBUG "got intr %x on completion\n", intr);
+ cmd_done(state, DID_ERROR << 16);
+ return;
+ }
+ cmd->SCp.Status = readb(&regs->fifo);
+ cmd->SCp.Message = readb(&regs->fifo);
+ cmd->result = CMD_ACCEPT_MSG;
+ writeb(CMD_ACCEPT_MSG, &regs->command);
+ state->phase = busfreeing;
+ break;
+ case busfreeing:
+ if (intr != INTR_DISCONNECT) {
+ printk(KERN_DEBUG "got intr %x when expected disconnect\n", intr);
+ }
+ cmd_done(state, (DID_OK << 16) + (cmd->SCp.Message << 8)
+ + cmd->SCp.Status);
+ break;
+ default:
+ printk(KERN_DEBUG "don't know about phase %d\n", state->phase);
+ }
+}
+
+static void cmd_done(struct fsc_state *state, int result)
+{
+ struct scsi_cmnd *cmd;
+
+ cmd = state->current_req;
+ if (cmd != 0) {
+ cmd->result = result;
+ (*cmd->scsi_done)(cmd);
+ state->current_req = NULL;
+ }
+ state->phase = idle;
+ mac53c94_start(state);
+}
+
+/*
+ * Set up DMA commands for transferring data.
+ */
+static void set_dma_cmds(struct fsc_state *state, struct scsi_cmnd *cmd)
+{
+ int i, dma_cmd, total;
+ struct scatterlist *scl;
+ struct dbdma_cmd *dcmds;
+ dma_addr_t dma_addr;
+ u32 dma_len;
+
+ dma_cmd = cmd->sc_data_direction == DMA_TO_DEVICE ?
+ OUTPUT_MORE : INPUT_MORE;
+ dcmds = state->dma_cmds;
+ if (cmd->use_sg > 0) {
+ int nseg;
+
+ total = 0;
+ scl = (struct scatterlist *) cmd->buffer;
+ nseg = pci_map_sg(state->pdev, scl, cmd->use_sg,
+ cmd->sc_data_direction);
+ for (i = 0; i < nseg; ++i) {
+ dma_addr = sg_dma_address(scl);
+ dma_len = sg_dma_len(scl);
+ if (dma_len > 0xffff)
+ panic("mac53c94: scatterlist element >= 64k");
+ total += dma_len;
+ st_le16(&dcmds->req_count, dma_len);
+ st_le16(&dcmds->command, dma_cmd);
+ st_le32(&dcmds->phy_addr, dma_addr);
+ dcmds->xfer_status = 0;
+ ++scl;
+ ++dcmds;
+ }
+ } else {
+ total = cmd->request_bufflen;
+ if (total > 0xffff)
+ panic("mac53c94: transfer size >= 64k");
+ dma_addr = pci_map_single(state->pdev, cmd->request_buffer,
+ total, cmd->sc_data_direction);
+ state->dma_addr = dma_addr;
+ st_le16(&dcmds->req_count, total);
+ st_le32(&dcmds->phy_addr, dma_addr);
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ }
+ dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
+ st_le16(&dcmds[-1].command, dma_cmd);
+ st_le16(&dcmds->command, DBDMA_STOP);
+ cmd->SCp.this_residual = total;
+}
+
+static struct scsi_host_template mac53c94_template = {
+ .proc_name = "53c94",
+ .name = "53C94",
+ .queuecommand = mac53c94_queue,
+ .eh_abort_handler = mac53c94_abort,
+ .eh_host_reset_handler = mac53c94_host_reset,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+static int mac53c94_probe(struct macio_dev *mdev, const struct of_match *match)
+{
+ struct device_node *node = macio_get_of_node(mdev);
+ struct pci_dev *pdev = macio_get_pci_dev(mdev);
+ struct fsc_state *state;
+ struct Scsi_Host *host;
+ void *dma_cmd_space;
+ unsigned char *clkprop;
+ int proplen;
+
+ if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) {
+ printk(KERN_ERR "mac53c94: expected 2 addrs and intrs (got %d/%d)\n",
+ node->n_addrs, node->n_intrs);
+ return -ENODEV;
+ }
+
+ if (macio_request_resources(mdev, "mac53c94") != 0) {
+ printk(KERN_ERR "mac53c94: unable to request memory resources");
+ return -EBUSY;
+ }
+
+ host = scsi_host_alloc(&mac53c94_template, sizeof(struct fsc_state));
+ if (host == NULL) {
+ printk(KERN_ERR "mac53c94: couldn't register host");
+ goto out_release;
+ }
+
+ state = (struct fsc_state *) host->hostdata;
+ macio_set_drvdata(mdev, state);
+ state->host = host;
+ state->pdev = pdev;
+ state->mdev = mdev;
+
+ state->regs = (struct mac53c94_regs __iomem *)
+ ioremap(macio_resource_start(mdev, 0), 0x1000);
+ state->intr = macio_irq(mdev, 0);
+ state->dma = (struct dbdma_regs __iomem *)
+ ioremap(macio_resource_start(mdev, 1), 0x1000);
+ state->dmaintr = macio_irq(mdev, 1);
+ if (state->regs == NULL || state->dma == NULL) {
+ printk(KERN_ERR "mac53c94: ioremap failed for %s\n",
+ node->full_name);
+ goto out_free;
+ }
+
+ clkprop = get_property(node, "clock-frequency", &proplen);
+ if (clkprop == NULL || proplen != sizeof(int)) {
+ printk(KERN_ERR "%s: can't get clock frequency, "
+ "assuming 25MHz\n", node->full_name);
+ state->clk_freq = 25000000;
+ } else
+ state->clk_freq = *(int *)clkprop;
+
+ /* Space for dma command list: +1 for stop command,
+ * +1 to allow for aligning.
+ * XXX FIXME: Use DMA consistent routines
+ */
+ dma_cmd_space = kmalloc((host->sg_tablesize + 2) *
+ sizeof(struct dbdma_cmd), GFP_KERNEL);
+ if (dma_cmd_space == 0) {
+ printk(KERN_ERR "mac53c94: couldn't allocate dma "
+ "command space for %s\n", node->full_name);
+ goto out_free;
+ }
+ state->dma_cmds = (struct dbdma_cmd *)DBDMA_ALIGN(dma_cmd_space);
+ memset(state->dma_cmds, 0, (host->sg_tablesize + 1)
+ * sizeof(struct dbdma_cmd));
+ state->dma_cmd_space = dma_cmd_space;
+
+ mac53c94_init(state);
+
+ if (request_irq(state->intr, do_mac53c94_interrupt, 0, "53C94", state)) {
+ printk(KERN_ERR "mac53C94: can't get irq %d for %s\n",
+ state->intr, node->full_name);
+ goto out_free_dma;
+ }
+
+ /* XXX FIXME: handle failure */
+ scsi_add_host(host, &mdev->ofdev.dev);
+ scsi_scan_host(host);
+
+ return 0;
+
+ out_free_dma:
+ kfree(state->dma_cmd_space);
+ out_free:
+ if (state->dma != NULL)
+ iounmap(state->dma);
+ if (state->regs != NULL)
+ iounmap(state->regs);
+ scsi_host_put(host);
+ out_release:
+ macio_release_resources(mdev);
+
+ return -ENODEV;
+}
+
+static int mac53c94_remove(struct macio_dev *mdev)
+{
+ struct fsc_state *fp = (struct fsc_state *)macio_get_drvdata(mdev);
+ struct Scsi_Host *host = fp->host;
+
+ scsi_remove_host(host);
+
+ free_irq(fp->intr, fp);
+
+ if (fp->regs)
+ iounmap((void *) fp->regs);
+ if (fp->dma)
+ iounmap((void *) fp->dma);
+ kfree(fp->dma_cmd_space);
+
+ scsi_host_put(host);
+
+ macio_release_resources(mdev);
+
+ return 0;
+}
+
+
+static struct of_match mac53c94_match[] =
+{
+ {
+ .name = "53c94",
+ .type = OF_ANY_MATCH,
+ .compatible = OF_ANY_MATCH
+ },
+ {},
+};
+
+static struct macio_driver mac53c94_driver =
+{
+ .name = "mac53c94",
+ .match_table = mac53c94_match,
+ .probe = mac53c94_probe,
+ .remove = mac53c94_remove,
+};
+
+
+static int __init init_mac53c94(void)
+{
+ return macio_register_driver(&mac53c94_driver);
+}
+
+static void __exit exit_mac53c94(void)
+{
+ return macio_unregister_driver(&mac53c94_driver);
+}
+
+module_init(init_mac53c94);
+module_exit(exit_mac53c94);
+
+MODULE_DESCRIPTION("PowerMac 53c94 SCSI driver");
+MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/mac53c94.h b/drivers/scsi/mac53c94.h
new file mode 100644
index 000000000000..1ad24e4f0a85
--- /dev/null
+++ b/drivers/scsi/mac53c94.h
@@ -0,0 +1,214 @@
+/*
+ * mac53c94.h: definitions for the driver for the 53c94 SCSI bus adaptor
+ * found on Power Macintosh computers, controlling the external SCSI chain.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#ifndef _MAC53C94_H
+#define _MAC53C94_H
+
+/*
+ * Registers in the 53C94 controller.
+ */
+
+struct mac53c94_regs {
+ unsigned char count_lo;
+ char pad0[15];
+ unsigned char count_mid;
+ char pad1[15];
+ unsigned char fifo;
+ char pad2[15];
+ unsigned char command;
+ char pad3[15];
+ unsigned char status;
+ char pad4[15];
+ unsigned char interrupt;
+ char pad5[15];
+ unsigned char seqstep;
+ char pad6[15];
+ unsigned char flags;
+ char pad7[15];
+ unsigned char config1;
+ char pad8[15];
+ unsigned char clk_factor;
+ char pad9[15];
+ unsigned char test;
+ char pad10[15];
+ unsigned char config2;
+ char pad11[15];
+ unsigned char config3;
+ char pad12[15];
+ unsigned char config4;
+ char pad13[15];
+ unsigned char count_hi;
+ char pad14[15];
+ unsigned char fifo_res;
+ char pad15[15];
+};
+
+/*
+ * Alternate functions for some registers.
+ */
+#define dest_id status
+#define sel_timeout interrupt
+#define sync_period seqstep
+#define sync_offset flags
+
+/*
+ * Bits in command register.
+ */
+#define CMD_DMA_MODE 0x80
+#define CMD_MODE_MASK 0x70
+#define CMD_MODE_INIT 0x10
+#define CMD_MODE_TARG 0x20
+#define CMD_MODE_DISC 0x40
+
+#define CMD_NOP 0
+#define CMD_FLUSH 1
+#define CMD_RESET 2
+#define CMD_SCSI_RESET 3
+
+#define CMD_XFER_DATA 0x10
+#define CMD_I_COMPLETE 0x11
+#define CMD_ACCEPT_MSG 0x12
+#define CMD_XFER_PAD 0x18
+#define CMD_SET_ATN 0x1a
+#define CMD_CLR_ATN 0x1b
+
+#define CMD_SEND_MSG 0x20
+#define CMD_SEND_STATUS 0x21
+#define CMD_SEND_DATA 0x22
+#define CMD_DISC_SEQ 0x23
+#define CMD_TERMINATE 0x24
+#define CMD_T_COMPLETE 0x25
+#define CMD_DISCONNECT 0x27
+#define CMD_RECV_MSG 0x28
+#define CMD_RECV_CDB 0x29
+#define CMD_RECV_DATA 0x2a
+#define CMD_RECV_CMD 0x2b
+#define CMD_ABORT_DMA 0x04
+
+#define CMD_RESELECT 0x40
+#define CMD_SELECT 0x41
+#define CMD_SELECT_ATN 0x42
+#define CMD_SELATN_STOP 0x43
+#define CMD_ENABLE_SEL 0x44
+#define CMD_DISABLE_SEL 0x45
+#define CMD_SEL_ATN3 0x46
+#define CMD_RESEL_ATN3 0x47
+
+/*
+ * Bits in status register.
+ */
+#define STAT_IRQ 0x80
+#define STAT_ERROR 0x40
+#define STAT_PARITY 0x20
+#define STAT_TC_ZERO 0x10
+#define STAT_DONE 0x08
+#define STAT_PHASE 0x07
+#define STAT_MSG 0x04
+#define STAT_CD 0x02
+#define STAT_IO 0x01
+
+/*
+ * Bits in interrupt register.
+ */
+#define INTR_RESET 0x80 /* SCSI bus was reset */
+#define INTR_ILL_CMD 0x40 /* illegal command */
+#define INTR_DISCONNECT 0x20 /* we got disconnected */
+#define INTR_BUS_SERV 0x10 /* bus service requested */
+#define INTR_DONE 0x08 /* function completed */
+#define INTR_RESELECTED 0x04 /* we were reselected */
+#define INTR_SEL_ATN 0x02 /* we were selected, ATN asserted */
+#define INTR_SELECT 0x01 /* we were selected, ATN negated */
+
+/*
+ * Encoding for the select timeout.
+ */
+#define TIMO_VAL(x) ((x) * 5000 / 7682)
+
+/*
+ * Bits in sequence step register.
+ */
+#define SS_MASK 7
+#define SS_ARB_SEL 0 /* Selection & arbitration complete */
+#define SS_MSG_SENT 1 /* One message byte sent */
+#define SS_NOT_CMD 2 /* Not in command phase */
+#define SS_PHASE_CHG 3 /* Early phase change, cmd bytes lost */
+#define SS_DONE 4 /* Command was sent OK */
+
+/*
+ * Encoding for sync transfer period.
+ */
+#define SYNCP_MASK 0x1f
+#define SYNCP_MIN 4
+#define SYNCP_MAX 31
+
+/*
+ * Bits in flags register.
+ */
+#define FLAGS_FIFO_LEV 0x1f
+#define FLAGS_SEQ_STEP 0xe0
+
+/*
+ * Encoding for sync offset.
+ */
+#define SYNCO_MASK 0x0f
+#define SYNCO_ASS_CTRL 0x30 /* REQ/ACK assertion control */
+#define SYNCO_NEG_CTRL 0xc0 /* REQ/ACK negation control */
+
+/*
+ * Bits in config1 register.
+ */
+#define CF1_SLOW_CABLE 0x80 /* Slow cable mode */
+#define CF1_NO_RES_REP 0x40 /* Disable SCSI reset reports */
+#define CF1_PAR_TEST 0x20 /* Parity test mode enable */
+#define CF1_PAR_ENABLE 0x10 /* Enable parity checks */
+#define CF1_TEST 0x08 /* Chip tests */
+#define CF1_MY_ID 0x07 /* Controller's address on bus */
+
+/*
+ * Encoding for clk_factor register.
+ */
+#define CLKF_MASK 7
+#define CLKF_VAL(freq) ((((freq) + 4999999) / 5000000) & CLKF_MASK)
+
+/*
+ * Bits in test mode register.
+ */
+#define TEST_TARGET 1 /* target test mode */
+#define TEST_INITIATOR 2 /* initiator test mode */
+#define TEST_TRISTATE 4 /* tristate (hi-z) test mode */
+
+/*
+ * Bits in config2 register.
+ */
+#define CF2_RFB 0x80
+#define CF2_FEATURE_EN 0x40 /* enable features / phase latch */
+#define CF2_BYTECTRL 0x20
+#define CF2_DREQ_HIZ 0x10
+#define CF2_SCSI2 0x08
+#define CF2_PAR_ABORT 0x04 /* bad parity target abort */
+#define CF2_REG_PARERR 0x02 /* register parity error */
+#define CF2_DMA_PARERR 0x01 /* DMA parity error */
+
+/*
+ * Bits in the config3 register.
+ */
+#define CF3_ID_MSG_CHK 0x80
+#define CF3_3B_MSGS 0x40
+#define CF3_CDB10 0x20
+#define CF3_FASTSCSI 0x10 /* enable fast SCSI support */
+#define CF3_FASTCLOCK 0x08
+#define CF3_SAVERESID 0x04
+#define CF3_ALT_DMA 0x02
+#define CF3_THRESH_8 0x01
+
+/*
+ * Bits in the config4 register.
+ */
+#define CF4_EAN 0x04
+#define CF4_TEST 0x02
+#define CF4_BBTE 0x01
+
+#endif /* _MAC53C94_H */
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
new file mode 100644
index 000000000000..c94c8db84651
--- /dev/null
+++ b/drivers/scsi/mac_esp.c
@@ -0,0 +1,754 @@
+/*
+ * 68k mac 53c9[46] scsi driver
+ *
+ * copyright (c) 1998, David Weis weisd3458@uni.edu
+ *
+ * debugging on Quadra 800 and 660AV Michael Schmitz, Dave Kilzer 7/98
+ *
+ * based loosely on cyber_esp.c
+ */
+
+/* these are unused for now */
+#define myreadl(addr) (*(volatile unsigned int *) (addr))
+#define mywritel(b, addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <asm/io.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+#include <asm/pgtable.h>
+
+#include <asm/macintosh.h>
+
+/* #define DEBUG_MAC_ESP */
+
+#define mac_turnon_irq(x) mac_enable_irq(x)
+#define mac_turnoff_irq(x) mac_disable_irq(x)
+
+extern void esp_handle(struct NCR_ESP *esp);
+extern void mac_esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+
+static int dma_bytes_sent(struct NCR_ESP * esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP * esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP * esp);
+static void dma_init_read(struct NCR_ESP * esp, char * vaddress, int length);
+static void dma_init_write(struct NCR_ESP * esp, char * vaddress, int length);
+static void dma_ints_off(struct NCR_ESP * esp);
+static void dma_ints_on(struct NCR_ESP * esp);
+static int dma_irq_p(struct NCR_ESP * esp);
+static int dma_irq_p_quick(struct NCR_ESP * esp);
+static void dma_led_off(struct NCR_ESP * esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP * esp, __u32 addr, int count, int write);
+static void dma_setup_quick(struct NCR_ESP * esp, __u32 addr, int count, int write);
+
+static int esp_dafb_dma_irq_p(struct NCR_ESP * espdev);
+static int esp_iosb_dma_irq_p(struct NCR_ESP * espdev);
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are transferred to the ESP chip
+ * via PIO.
+ */
+
+static int esp_initialized = 0;
+
+static int setup_num_esps = -1;
+static int setup_disconnect = -1;
+static int setup_nosync = -1;
+static int setup_can_queue = -1;
+static int setup_cmd_per_lun = -1;
+static int setup_sg_tablesize = -1;
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+#endif
+static int setup_hostid = -1;
+
+/*
+ * Experimental ESP inthandler; check macints.c to make sure dev_id is
+ * set up properly!
+ */
+
+void mac_esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *) dev_id;
+ int irq_p = 0;
+
+ /* Handle the one ESP interrupt showing at this IRQ level. */
+ if(((esp)->irq & 0xff) == irq) {
+ /*
+ * Debug ..
+ */
+ irq_p = esp->dma_irq_p(esp);
+ printk("mac_esp: irq_p %x current %p disconnected %p\n",
+ irq_p, esp->current_SC, esp->disconnected_SC);
+
+ /*
+ * Mac: if we're here, it's an ESP interrupt for sure!
+ */
+ if((esp->current_SC || esp->disconnected_SC)) {
+ esp->dma_ints_off(esp);
+
+ ESPIRQ(("I%d(", esp->esp_id));
+ esp_handle(esp);
+ ESPIRQ((")"));
+
+ esp->dma_ints_on(esp);
+ }
+ }
+}
+
+/*
+ * Debug hooks; use for playing with the interrupt flag testing and interrupt
+ * acknowledge on the various machines
+ */
+
+void scsi_esp_polled(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ if (esp_initialized == 0)
+ return;
+
+ mac_esp_intr(irq, dev_id, pregs);
+}
+
+void fake_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: got irq\n");
+#endif
+
+ mac_esp_intr(irq, dev_id, pregs);
+}
+
+irqreturn_t fake_drq(int irq, void *dev_id, struct pt_regs *pregs)
+{
+ printk("mac_esp: got drq\n");
+ return IRQ_HANDLED;
+}
+
+#define DRIVER_SETUP
+
+/*
+ * Function : mac_esp_setup(char *str)
+ *
+ * Purpose : booter command line initialization of the overrides array,
+ *
+ * Inputs : str - parameters, separated by commas.
+ *
+ * Currently unused in the new driver; need to add settable parameters to the
+ * detect function.
+ *
+ */
+
+static int __init mac_esp_setup(char *str) {
+#ifdef DRIVER_SETUP
+ /* Format of mac53c9x parameter is:
+ * mac53c9x=<num_esps>,<disconnect>,<nosync>,<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
+ * Negative values mean don't change.
+ */
+
+ char *this_opt;
+ long opt;
+
+ this_opt = strsep (&str, ",");
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ if (opt >= 0 && opt <= 2)
+ setup_num_esps = opt;
+ else if (opt > 2)
+ printk( "mac_esp_setup: invalid number of hosts %ld !\n", opt );
+
+ this_opt = strsep (&str, ",");
+ }
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ if (opt > 0)
+ setup_disconnect = opt;
+
+ this_opt = strsep (&str, ",");
+ }
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ if (opt >= 0)
+ setup_nosync = opt;
+
+ this_opt = strsep (&str, ",");
+ }
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ if (opt > 0)
+ setup_can_queue = opt;
+
+ this_opt = strsep (&str, ",");
+ }
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ if (opt > 0)
+ setup_cmd_per_lun = opt;
+
+ this_opt = strsep (&str, ",");
+ }
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ if (opt >= 0) {
+ setup_sg_tablesize = opt;
+ /* Must be <= SG_ALL (255) */
+ if (setup_sg_tablesize > SG_ALL)
+ setup_sg_tablesize = SG_ALL;
+ }
+
+ this_opt = strsep (&str, ",");
+ }
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+
+ /* Must be between 0 and 7 */
+ if (opt >= 0 && opt <= 7)
+ setup_hostid = opt;
+ else if (opt > 7)
+ printk( "mac_esp_setup: invalid host ID %ld !\n", opt);
+
+ this_opt = strsep (&str, ",");
+ }
+#ifdef SUPPORT_TAGS
+ if(this_opt) {
+ opt = simple_strtol( this_opt, NULL, 0 );
+ if (opt >= 0)
+ setup_use_tagged_queuing = !!opt;
+ }
+#endif
+#endif
+ return 1;
+}
+
+__setup("mac53c9x=", mac_esp_setup);
+
+
+/*
+ * ESP address 'detection'
+ */
+
+unsigned long get_base(int chip_num)
+{
+ /*
+ * using the chip_num and mac model, figure out where the
+ * chips are mapped
+ */
+
+ unsigned long io_base = 0x50f00000;
+ unsigned int second_offset = 0x402;
+ unsigned long scsi_loc = 0;
+
+ switch (macintosh_config->scsi_type) {
+
+ /* 950, 900, 700 */
+ case MAC_SCSI_QUADRA2:
+ scsi_loc = io_base + 0xf000 + ((chip_num == 0) ? 0 : second_offset);
+ break;
+
+ /* av's */
+ case MAC_SCSI_QUADRA3:
+ scsi_loc = io_base + 0x18000 + ((chip_num == 0) ? 0 : second_offset);
+ break;
+
+ /* most quadra/centris models are like this */
+ case MAC_SCSI_QUADRA:
+ scsi_loc = io_base + 0x10000;
+ break;
+
+ default:
+ printk("mac_esp: get_base: hit default!\n");
+ scsi_loc = io_base + 0x10000;
+ break;
+
+ } /* switch */
+
+ printk("mac_esp: io base at 0x%lx\n", scsi_loc);
+
+ return scsi_loc;
+}
+
+/*
+ * Model dependent ESP setup
+ */
+
+int mac_esp_detect(Scsi_Host_Template * tpnt)
+{
+ int quick = 0;
+ int chipnum, chipspresent = 0;
+#if 0
+ unsigned long timeout;
+#endif
+
+ if (esp_initialized > 0)
+ return -ENODEV;
+
+ /* what do we have in this machine... */
+ if (MACHW_PRESENT(MAC_SCSI_96)) {
+ chipspresent ++;
+ }
+
+ if (MACHW_PRESENT(MAC_SCSI_96_2)) {
+ chipspresent ++;
+ }
+
+ /* number of ESPs present ? */
+ if (setup_num_esps >= 0) {
+ if (chipspresent >= setup_num_esps)
+ chipspresent = setup_num_esps;
+ else
+ printk("mac_esp_detect: num_hosts detected %d setup %d \n",
+ chipspresent, setup_num_esps);
+ }
+
+ /* TODO: add disconnect / nosync flags */
+
+ /* setup variables */
+ tpnt->can_queue =
+ (setup_can_queue > 0) ? setup_can_queue : 7;
+ tpnt->cmd_per_lun =
+ (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : 1;
+ tpnt->sg_tablesize =
+ (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_ALL;
+
+ if (setup_hostid >= 0)
+ tpnt->this_id = setup_hostid;
+ else {
+ /* use 7 as default */
+ tpnt->this_id = 7;
+ }
+
+#ifdef SUPPORT_TAGS
+ if (setup_use_tagged_queuing < 0)
+ setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING;
+#endif
+
+ for (chipnum = 0; chipnum < chipspresent; chipnum ++) {
+ struct NCR_ESP * esp;
+
+ esp = esp_allocate(tpnt, (void *) NULL);
+ esp->eregs = (struct ESP_regs *) get_base(chipnum);
+
+ esp->dma_irq_p = &esp_dafb_dma_irq_p;
+ if (chipnum == 0) {
+
+ if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
+ /* most machines except those below :-) */
+ quick = 1;
+ esp->dma_irq_p = &esp_iosb_dma_irq_p;
+ } else if (macintosh_config->scsi_type == MAC_SCSI_QUADRA3) {
+ /* mostly av's */
+ quick = 0;
+ } else {
+ /* q950, 900, 700 */
+ quick = 1;
+ out_be32(0xf9800024, 0x1d1);
+ esp->dregs = (void *) 0xf9800024;
+ }
+
+ } else { /* chipnum */
+
+ quick = 1;
+ out_be32(0xf9800028, 0x1d1);
+ esp->dregs = (void *) 0xf9800028;
+
+ } /* chipnum == 0 */
+
+ /* use pio for command bytes; pio for message/data: TBI */
+ esp->do_pio_cmds = 1;
+
+ /* Set the command buffer */
+ esp->esp_command = (volatile unsigned char*) cmd_buffer;
+ esp->esp_command_dvma = (__u32) cmd_buffer;
+
+ /* various functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = NULL;
+ esp->dma_init_write = NULL;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+
+ esp->dma_ports_p = &dma_ports_p;
+
+
+ /* Optional functions */
+ esp->dma_barrier = NULL;
+ esp->dma_drain = NULL;
+ esp->dma_invalidate = NULL;
+ esp->dma_irq_entry = NULL;
+ esp->dma_irq_exit = NULL;
+ esp->dma_led_on = NULL;
+ esp->dma_led_off = NULL;
+ esp->dma_poll = NULL;
+ esp->dma_reset = NULL;
+
+ /* SCSI chip speed */
+ /* below esp->cfreq = 40000000; */
+
+
+ if (quick) {
+ /* 'quick' means there's handshake glue logic like in the 5380 case */
+ esp->dma_setup = &dma_setup_quick;
+ } else {
+ esp->dma_setup = &dma_setup;
+ }
+
+ if (chipnum == 0) {
+
+ esp->irq = IRQ_MAC_SCSI;
+
+ request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp->ehost);
+#if 0 /* conflicts with IOP ADB */
+ request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp->ehost);
+#endif
+
+ if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
+ esp->cfreq = 16500000;
+ } else {
+ esp->cfreq = 25000000;
+ }
+
+
+ } else { /* chipnum == 1 */
+
+ esp->irq = IRQ_MAC_SCSIDRQ;
+#if 0 /* conflicts with IOP ADB */
+ request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp->ehost);
+#endif
+
+ esp->cfreq = 25000000;
+
+ }
+
+ if (quick) {
+ printk("esp: using quick version\n");
+ }
+
+ printk("esp: addr at 0x%p\n", esp->eregs);
+
+ esp->scsi_id = 7;
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ } /* for chipnum */
+
+ if (chipspresent)
+ printk("\nmac_esp: %d esp controllers found\n", chipspresent);
+
+ esp_initialized = chipspresent;
+
+ return chipspresent;
+}
+
+static int mac_esp_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+/*
+ * I've been wondering what this is supposed to do, for some time. Talking
+ * to Allen Briggs: These machines have an extra register someplace where the
+ * DRQ pin of the ESP can be monitored. That isn't useful for determining
+ * anything else (such as reselect interrupt or other magic) though.
+ * Maybe make the semantics should be changed like
+ * if (esp->current_SC)
+ * ... check DRQ flag ...
+ * else
+ * ... disconnected, check pending VIA interrupt ...
+ *
+ * There's a problem with using the dabf flag or mac_irq_pending() here: both
+ * seem to return 1 even though no interrupt is currently pending, resulting
+ * in esp_exec_cmd() holding off the next command, and possibly infinite loops
+ * in esp_intr().
+ * Short term fix: just use esp_status & ESP_STAT_INTR here, as long as we
+ * use simple PIO. The DRQ status will be important when implementing pseudo
+ * DMA mode (set up ESP transfer count, return, do a batch of bytes in PIO or
+ * 'hardware handshake' mode upon DRQ).
+ * If you plan on changing this (i.e. to save the esp_status register access in
+ * favor of a VIA register access or a shadow register for the IFR), make sure
+ * to try a debug version of this first to monitor what registers would be a good
+ * indicator of the ESP interrupt.
+ */
+
+static int esp_dafb_dma_irq_p(struct NCR_ESP * esp)
+{
+ unsigned int ret;
+ int sreg = esp_read(esp->eregs->esp_status);
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: esp_dafb_dma_irq_p dafb %d irq %d\n",
+ readl(esp->dregs), mac_irq_pending(IRQ_MAC_SCSI));
+#endif
+
+ sreg &= ESP_STAT_INTR;
+
+ /*
+ * maybe working; this is essentially what's used for iosb_dma_irq_p
+ */
+ if (sreg)
+ return 1;
+ else
+ return 0;
+
+ /*
+ * didn't work ...
+ */
+#if 0
+ if (esp->current_SC)
+ ret = readl(esp->dregs) & 0x200;
+ else if (esp->disconnected_SC)
+ ret = 1; /* sreg ?? */
+ else
+ ret = mac_irq_pending(IRQ_MAC_SCSI);
+
+ return(ret);
+#endif
+
+}
+
+/*
+ * See above: testing mac_irq_pending always returned 8 (SCSI IRQ) regardless
+ * of the actual ESP status.
+ */
+
+static int esp_iosb_dma_irq_p(struct NCR_ESP * esp)
+{
+ int ret = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ);
+ int sreg = esp_read(esp->eregs->esp_status);
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n",
+ mac_irq_pending(IRQ_MAC_SCSIDRQ), mac_irq_pending(IRQ_MAC_SCSI),
+ sreg, esp->current_SC, esp->disconnected_SC);
+#endif
+
+ sreg &= ESP_STAT_INTR;
+
+ if (sreg)
+ return (sreg);
+ else
+ return 0;
+}
+
+/*
+ * This seems to be OK for PIO at least ... usually 0 after PIO.
+ */
+
+static int dma_bytes_sent(struct NCR_ESP * esp, int fifo_count)
+{
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma bytes sent = %x\n", fifo_count);
+#endif
+
+ return fifo_count;
+}
+
+/*
+ * dma_can_transfer is used to switch between DMA and PIO, if DMA (pseudo)
+ * is ever implemented. Returning 0 here will use PIO.
+ */
+
+static int dma_can_transfer(struct NCR_ESP * esp, Scsi_Cmnd * sp)
+{
+ unsigned long sz = sp->SCp.this_residual;
+#if 0 /* no DMA yet; make conditional */
+ if (sz > 0x10000000) {
+ sz = 0x10000000;
+ }
+ printk("mac_esp: dma can transfer = 0lx%x\n", sz);
+#else
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: pio to transfer = %ld\n", sz);
+#endif
+
+ sz = 0;
+#endif
+ return sz;
+}
+
+/*
+ * Not yet ...
+ */
+
+static void dma_dump_state(struct NCR_ESP * esp)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_dump_state: called\n");
+#endif
+#if 0
+ ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+ esp->esp_id, ((struct mac_dma_registers *)
+ (esp->dregs))->cond_reg));
+#endif
+}
+
+/*
+ * DMA setup: should be used to set up the ESP transfer count for pseudo
+ * DMA transfers; need a DRQ transfer function to do the actual transfer
+ */
+
+static void dma_init_read(struct NCR_ESP * esp, char * vaddress, int length)
+{
+ printk("mac_esp: dma_init_read\n");
+}
+
+
+static void dma_init_write(struct NCR_ESP * esp, char * vaddress, int length)
+{
+ printk("mac_esp: dma_init_write\n");
+}
+
+
+static void dma_ints_off(struct NCR_ESP * esp)
+{
+ mac_turnoff_irq(esp->irq);
+}
+
+
+static void dma_ints_on(struct NCR_ESP * esp)
+{
+ mac_turnon_irq(esp->irq);
+}
+
+/*
+ * generic dma_irq_p(), unused
+ */
+
+static int dma_irq_p(struct NCR_ESP * esp)
+{
+ int i = esp_read(esp->eregs->esp_status);
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_irq_p status %d\n", i);
+#endif
+
+ return (i & ESP_STAT_INTR);
+}
+
+static int dma_irq_p_quick(struct NCR_ESP * esp)
+{
+ /*
+ * Copied from iosb_dma_irq_p()
+ */
+ int ret = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ);
+ int sreg = esp_read(esp->eregs->esp_status);
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n",
+ mac_irq_pending(IRQ_MAC_SCSIDRQ), mac_irq_pending(IRQ_MAC_SCSI),
+ sreg, esp->current_SC, esp->disconnected_SC);
+#endif
+
+ sreg &= ESP_STAT_INTR;
+
+ if (sreg)
+ return (sreg);
+ else
+ return 0;
+
+}
+
+static void dma_led_off(struct NCR_ESP * esp)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_led_off: called\n");
+#endif
+}
+
+
+static void dma_led_on(struct NCR_ESP * esp)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_led_on: called\n");
+#endif
+}
+
+
+static int dma_ports_p(struct NCR_ESP * esp)
+{
+ return 0;
+}
+
+
+static void dma_setup(struct NCR_ESP * esp, __u32 addr, int count, int write)
+{
+
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_setup\n");
+#endif
+
+ if (write) {
+ dma_init_read(esp, (char *) addr, count);
+ } else {
+ dma_init_write(esp, (char *) addr, count);
+ }
+}
+
+
+static void dma_setup_quick(struct NCR_ESP * esp, __u32 addr, int count, int write)
+{
+#ifdef DEBUG_MAC_ESP
+ printk("mac_esp: dma_setup_quick\n");
+#endif
+}
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "mac_esp",
+ .name = "Mac 53C9x SCSI",
+ .detect = mac_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = mac_esp_release,
+ .info = esp_info,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c
new file mode 100644
index 000000000000..d5fd17ef74db
--- /dev/null
+++ b/drivers/scsi/mac_scsi.c
@@ -0,0 +1,605 @@
+/*
+ * Generic Macintosh NCR5380 driver
+ *
+ * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
+ *
+ * derived in part from:
+ */
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: mac_NCR5380.c,v $
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "mac_scsi.h"
+#include "NCR5380.h"
+
+#if 0
+#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION)
+#else
+#define NDEBUG (NDEBUG_ABORT)
+#endif
+
+#define RESET_BOOT
+#define DRIVER_SETUP
+
+#define ENABLE_IRQ() mac_enable_irq( IRQ_MAC_SCSI );
+#define DISABLE_IRQ() mac_disable_irq( IRQ_MAC_SCSI );
+
+extern void via_scsi_clear(void);
+
+#ifdef RESET_BOOT
+static void mac_scsi_reset_boot(struct Scsi_Host *instance);
+#endif
+
+static int setup_called = 0;
+static int setup_can_queue = -1;
+static int setup_cmd_per_lun = -1;
+static int setup_sg_tablesize = -1;
+static int setup_use_pdma = -1;
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+#endif
+static int setup_hostid = -1;
+
+/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
+ * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
+ * need ten times the standard value... */
+#define TOSHIBA_DELAY
+
+#ifdef TOSHIBA_DELAY
+#define AFTER_RESET_DELAY (5*HZ/2)
+#else
+#define AFTER_RESET_DELAY (HZ/2)
+#endif
+
+static volatile unsigned char *mac_scsi_regp = NULL;
+static volatile unsigned char *mac_scsi_drq = NULL;
+static volatile unsigned char *mac_scsi_nodrq = NULL;
+
+
+/*
+ * NCR 5380 register access functions
+ */
+
+#if 0
+/* Debug versions */
+#define CTRL(p,v) (*ctrl = (v))
+
+static char macscsi_read(struct Scsi_Host *instance, int reg)
+{
+ int iobase = instance->io_port;
+ int i;
+ int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+ CTRL(iobase, 0);
+ i = in_8(iobase + (reg<<4));
+ CTRL(iobase, 0x40);
+
+ return i;
+}
+
+static void macscsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+ int iobase = instance->io_port;
+ int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+ CTRL(iobase, 0);
+ out_8(iobase + (reg<<4), value);
+ CTRL(iobase, 0x40);
+}
+#else
+
+/* Fast versions */
+static __inline__ char macscsi_read(struct Scsi_Host *instance, int reg)
+{
+ return in_8(instance->io_port + (reg<<4));
+}
+
+static __inline__ void macscsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+ out_8(instance->io_port + (reg<<4), value);
+}
+#endif
+
+
+/*
+ * Function : mac_scsi_setup(char *str)
+ *
+ * Purpose : booter command line initialization of the overrides array,
+ *
+ * Inputs : str - comma delimited list of options
+ *
+ */
+
+static int __init mac_scsi_setup(char *str) {
+#ifdef DRIVER_SETUP
+ int ints[7];
+
+ (void)get_options( str, ARRAY_SIZE(ints), ints);
+
+ if (setup_called++ || ints[0] < 1 || ints[0] > 6) {
+ printk(KERN_WARNING "scsi: <mac5380>"
+ " Usage: mac5380=<can_queue>[,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>,<use_pdma>]\n");
+ printk(KERN_ALERT "scsi: <mac5380> Bad Penguin parameters?\n");
+ return 0;
+ }
+
+ if (ints[0] >= 1) {
+ if (ints[1] > 0)
+ /* no limits on this, just > 0 */
+ setup_can_queue = ints[1];
+ }
+ if (ints[0] >= 2) {
+ if (ints[2] > 0)
+ setup_cmd_per_lun = ints[2];
+ }
+ if (ints[0] >= 3) {
+ if (ints[3] >= 0) {
+ setup_sg_tablesize = ints[3];
+ /* Must be <= SG_ALL (255) */
+ if (setup_sg_tablesize > SG_ALL)
+ setup_sg_tablesize = SG_ALL;
+ }
+ }
+ if (ints[0] >= 4) {
+ /* Must be between 0 and 7 */
+ if (ints[4] >= 0 && ints[4] <= 7)
+ setup_hostid = ints[4];
+ else if (ints[4] > 7)
+ printk(KERN_WARNING "mac_scsi_setup: invalid host ID %d !\n", ints[4] );
+ }
+#ifdef SUPPORT_TAGS
+ if (ints[0] >= 5) {
+ if (ints[5] >= 0)
+ setup_use_tagged_queuing = !!ints[5];
+ }
+
+ if (ints[0] == 6) {
+ if (ints[6] >= 0)
+ setup_use_pdma = ints[6];
+ }
+#else
+ if (ints[0] == 5) {
+ if (ints[5] >= 0)
+ setup_use_pdma = ints[5];
+ }
+#endif /* SUPPORT_TAGS */
+
+#endif /* DRIVER_SETUP */
+ return 1;
+}
+
+__setup("mac5380=", mac_scsi_setup);
+
+/*
+ * If you want to find the instance with (k)gdb ...
+ */
+#if NDEBUG
+static struct Scsi_Host *default_instance;
+#endif
+
+/*
+ * Function : int macscsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes mac NCR5380 driver based on the
+ * command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int macscsi_detect(Scsi_Host_Template * tpnt)
+{
+ static int called = 0;
+ int flags = 0;
+ struct Scsi_Host *instance;
+
+ if (!MACH_IS_MAC || called)
+ return( 0 );
+
+ if (macintosh_config->scsi_type != MAC_SCSI_OLD)
+ return( 0 );
+
+ /* setup variables */
+ tpnt->can_queue =
+ (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
+ tpnt->cmd_per_lun =
+ (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
+ tpnt->sg_tablesize =
+ (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
+
+ if (setup_hostid >= 0)
+ tpnt->this_id = setup_hostid;
+ else {
+ /* use 7 as default */
+ tpnt->this_id = 7;
+ }
+
+#ifdef SUPPORT_TAGS
+ if (setup_use_tagged_queuing < 0)
+ setup_use_tagged_queuing = USE_TAGGED_QUEUING;
+#endif
+
+ /* Once we support multiple 5380s (e.g. DuoDock) we'll do
+ something different here */
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+#if NDEBUG
+ default_instance = instance;
+#endif
+
+ if (macintosh_config->ident == MAC_MODEL_IIFX) {
+ mac_scsi_regp = via1+0x8000;
+ mac_scsi_drq = via1+0xE000;
+ mac_scsi_nodrq = via1+0xC000;
+ /* The IIFX should be able to do true DMA, but pseudo-dma doesn't work */
+ flags = FLAG_NO_PSEUDO_DMA;
+ } else {
+ mac_scsi_regp = via1+0x10000;
+ mac_scsi_drq = via1+0x6000;
+ mac_scsi_nodrq = via1+0x12000;
+ }
+
+ if (! setup_use_pdma)
+ flags = FLAG_NO_PSEUDO_DMA;
+
+ instance->io_port = (unsigned long) mac_scsi_regp;
+ instance->irq = IRQ_MAC_SCSI;
+
+#ifdef RESET_BOOT
+ mac_scsi_reset_boot(instance);
+#endif
+
+ NCR5380_init(instance, flags);
+
+ instance->n_io_port = 255;
+
+ ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+
+ if (instance->irq != SCSI_IRQ_NONE)
+ if (request_irq(instance->irq, NCR5380_intr, IRQ_FLG_SLOW,
+ "ncr5380", instance)) {
+ printk(KERN_WARNING "scsi%d: IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+ }
+
+ printk(KERN_INFO "scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk (KERN_INFO "s disabled");
+ else
+ printk (KERN_INFO " %d", instance->irq);
+ printk(KERN_INFO " options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ instance->can_queue, instance->cmd_per_lun, MACSCSI_PUBLIC_RELEASE);
+ printk(KERN_INFO "\nscsi%d:", instance->host_no);
+ NCR5380_print_options(instance);
+ printk("\n");
+ called = 1;
+ return 1;
+}
+
+int macscsi_release (struct Scsi_Host *shpnt)
+{
+ if (shpnt->irq != SCSI_IRQ_NONE)
+ free_irq (shpnt->irq, NCR5380_intr);
+ NCR5380_exit(shpnt);
+
+ return 0;
+}
+
+#ifdef RESET_BOOT
+/*
+ * Our 'bus reset on boot' function
+ */
+
+static void mac_scsi_reset_boot(struct Scsi_Host *instance)
+{
+ unsigned long end;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ /*
+ * Do a SCSI reset to clean up the bus during initialization. No messing
+ * with the queues, interrupts, or locks necessary here.
+ */
+
+ printk(KERN_INFO "Macintosh SCSI: resetting the SCSI bus..." );
+
+ /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
+ mac_disable_irq(IRQ_MAC_SCSI);
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+ /* The min. reset hold time is 25us, so 40us should be enough */
+ udelay( 50 );
+ /* reset RST and interrupt */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
+ barrier();
+
+ /* switch on SCSI IRQ again */
+ mac_enable_irq(IRQ_MAC_SCSI);
+
+ printk(KERN_INFO " done\n" );
+}
+#endif
+
+const char * macscsi_info (struct Scsi_Host *spnt) {
+ return "";
+}
+
+/*
+ Pseudo-DMA: (Ove Edlund)
+ The code attempts to catch bus errors that occur if one for example
+ "trips over the cable".
+ XXX: Since bus errors in the PDMA routines never happen on my
+ computer, the bus error code is untested.
+ If the code works as intended, a bus error results in Pseudo-DMA
+ beeing disabled, meaning that the driver switches to slow handshake.
+ If bus errors are NOT extremely rare, this has to be changed.
+*/
+
+#define CP_IO_TO_MEM(s,d,len) \
+__asm__ __volatile__ \
+ (" cmp.w #4,%2\n" \
+ " bls 8f\n" \
+ " move.w %1,%%d0\n" \
+ " neg.b %%d0\n" \
+ " and.w #3,%%d0\n" \
+ " sub.w %%d0,%2\n" \
+ " bra 2f\n" \
+ " 1: move.b (%0),(%1)+\n" \
+ " 2: dbf %%d0,1b\n" \
+ " move.w %2,%%d0\n" \
+ " lsr.w #5,%%d0\n" \
+ " bra 4f\n" \
+ " 3: move.l (%0),(%1)+\n" \
+ "31: move.l (%0),(%1)+\n" \
+ "32: move.l (%0),(%1)+\n" \
+ "33: move.l (%0),(%1)+\n" \
+ "34: move.l (%0),(%1)+\n" \
+ "35: move.l (%0),(%1)+\n" \
+ "36: move.l (%0),(%1)+\n" \
+ "37: move.l (%0),(%1)+\n" \
+ " 4: dbf %%d0,3b\n" \
+ " move.w %2,%%d0\n" \
+ " lsr.w #2,%%d0\n" \
+ " and.w #7,%%d0\n" \
+ " bra 6f\n" \
+ " 5: move.l (%0),(%1)+\n" \
+ " 6: dbf %%d0,5b\n" \
+ " and.w #3,%2\n" \
+ " bra 8f\n" \
+ " 7: move.b (%0),(%1)+\n" \
+ " 8: dbf %2,7b\n" \
+ " moveq.l #0, %2\n" \
+ " 9: \n" \
+ ".section .fixup,\"ax\"\n" \
+ " .even\n" \
+ "90: moveq.l #1, %2\n" \
+ " jra 9b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b,90b\n" \
+ " .long 3b,90b\n" \
+ " .long 31b,90b\n" \
+ " .long 32b,90b\n" \
+ " .long 33b,90b\n" \
+ " .long 34b,90b\n" \
+ " .long 35b,90b\n" \
+ " .long 36b,90b\n" \
+ " .long 37b,90b\n" \
+ " .long 5b,90b\n" \
+ " .long 7b,90b\n" \
+ ".previous" \
+ : "=a"(s), "=a"(d), "=d"(len) \
+ : "0"(s), "1"(d), "2"(len) \
+ : "d0")
+
+
+static int macscsi_pread (struct Scsi_Host *instance,
+ unsigned char *dst, int len)
+{
+ unsigned char *d;
+ volatile unsigned char *s;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ s = mac_scsi_drq+0x60;
+ d = dst;
+
+/* These conditions are derived from MacOS */
+
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
+ && !(NCR5380_read(STATUS_REG) & SR_REQ))
+ ;
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
+ && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+ printk(KERN_ERR "Error in macscsi_pread\n");
+ return -1;
+ }
+
+ CP_IO_TO_MEM(s, d, len);
+
+ if (len != 0) {
+ printk(KERN_NOTICE "Bus error in macscsi_pread\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#define CP_MEM_TO_IO(s,d,len) \
+__asm__ __volatile__ \
+ (" cmp.w #4,%2\n" \
+ " bls 8f\n" \
+ " move.w %0,%%d0\n" \
+ " neg.b %%d0\n" \
+ " and.w #3,%%d0\n" \
+ " sub.w %%d0,%2\n" \
+ " bra 2f\n" \
+ " 1: move.b (%0)+,(%1)\n" \
+ " 2: dbf %%d0,1b\n" \
+ " move.w %2,%%d0\n" \
+ " lsr.w #5,%%d0\n" \
+ " bra 4f\n" \
+ " 3: move.l (%0)+,(%1)\n" \
+ "31: move.l (%0)+,(%1)\n" \
+ "32: move.l (%0)+,(%1)\n" \
+ "33: move.l (%0)+,(%1)\n" \
+ "34: move.l (%0)+,(%1)\n" \
+ "35: move.l (%0)+,(%1)\n" \
+ "36: move.l (%0)+,(%1)\n" \
+ "37: move.l (%0)+,(%1)\n" \
+ " 4: dbf %%d0,3b\n" \
+ " move.w %2,%%d0\n" \
+ " lsr.w #2,%%d0\n" \
+ " and.w #7,%%d0\n" \
+ " bra 6f\n" \
+ " 5: move.l (%0)+,(%1)\n" \
+ " 6: dbf %%d0,5b\n" \
+ " and.w #3,%2\n" \
+ " bra 8f\n" \
+ " 7: move.b (%0)+,(%1)\n" \
+ " 8: dbf %2,7b\n" \
+ " moveq.l #0, %2\n" \
+ " 9: \n" \
+ ".section .fixup,\"ax\"\n" \
+ " .even\n" \
+ "90: moveq.l #1, %2\n" \
+ " jra 9b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b,90b\n" \
+ " .long 3b,90b\n" \
+ " .long 31b,90b\n" \
+ " .long 32b,90b\n" \
+ " .long 33b,90b\n" \
+ " .long 34b,90b\n" \
+ " .long 35b,90b\n" \
+ " .long 36b,90b\n" \
+ " .long 37b,90b\n" \
+ " .long 5b,90b\n" \
+ " .long 7b,90b\n" \
+ ".previous" \
+ : "=a"(s), "=a"(d), "=d"(len) \
+ : "0"(s), "1"(d), "2"(len) \
+ : "d0")
+
+static int macscsi_pwrite (struct Scsi_Host *instance,
+ unsigned char *src, int len)
+{
+ unsigned char *s;
+ volatile unsigned char *d;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ s = src;
+ d = mac_scsi_drq;
+
+/* These conditions are derived from MacOS */
+
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
+ && (!(NCR5380_read(STATUS_REG) & SR_REQ)
+ || (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)))
+ ;
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) {
+ printk(KERN_ERR "Error in macscsi_pwrite\n");
+ return -1;
+ }
+
+ CP_MEM_TO_IO(s, d, len);
+
+ if (len != 0) {
+ printk(KERN_NOTICE "Bus error in macscsi_pwrite\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* These control the behaviour of the generic 5380 core */
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+#include "NCR5380.c"
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "Mac5380",
+ .proc_info = macscsi_proc_info,
+ .name = "Macintosh NCR5380 SCSI",
+ .detect = macscsi_detect,
+ .release = macscsi_release,
+ .info = macscsi_info,
+ .queuecommand = macscsi_queue_command,
+ .eh_abort_handler = macscsi_abort,
+ .eh_bus_reset_handler = macscsi_bus_reset,
+ .eh_device_reset_handler = macscsi_device_reset,
+ .eh_host_reset_handler = macscsi_host_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .unchecked_isa_dma = 0,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/mac_scsi.h b/drivers/scsi/mac_scsi.h
new file mode 100644
index 000000000000..23ab2c18a016
--- /dev/null
+++ b/drivers/scsi/mac_scsi.h
@@ -0,0 +1,85 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: cumana_NCR5380.h,v $
+ */
+
+#ifndef MAC_NCR5380_H
+#define MAC_NCR5380_H
+
+#define MACSCSI_PUBLIC_RELEASE 2
+
+#ifndef ASM
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifndef SG_TABLESIZE
+#define SG_TABLESIZE SG_NONE
+#endif
+
+#ifndef USE_TAGGED_QUEUING
+#define USE_TAGGED_QUEUING 0
+#endif
+
+#include <scsi/scsicam.h>
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ int port, ctrl
+
+#define NCR5380_local_declare() \
+ struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+ _instance = instance
+
+#define NCR5380_read(reg) macscsi_read(_instance, reg)
+#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value)
+
+#define NCR5380_pread macscsi_pread
+#define NCR5380_pwrite macscsi_pwrite
+
+#define NCR5380_intr macscsi_intr
+#define NCR5380_queue_command macscsi_queue_command
+#define NCR5380_abort macscsi_abort
+#define NCR5380_bus_reset macscsi_bus_reset
+#define NCR5380_device_reset macscsi_device_reset
+#define NCR5380_host_reset macscsi_host_reset
+#define NCR5380_proc_info macscsi_proc_info
+
+#define BOARD_NORMAL 0
+#define BOARD_NCR53C400 1
+
+#endif /* ndef HOSTS_C */
+#endif /* ndef ASM */
+#endif /* MAC_NCR5380_H */
+
diff --git a/drivers/scsi/mca_53c9x.c b/drivers/scsi/mca_53c9x.c
new file mode 100644
index 000000000000..194c75451faf
--- /dev/null
+++ b/drivers/scsi/mca_53c9x.c
@@ -0,0 +1,520 @@
+/* mca_53c9x.c: Driver for the SCSI adapter found on NCR 35xx
+ * (and maybe some other) Microchannel machines
+ *
+ * Code taken mostly from Cyberstorm SCSI drivers
+ * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk)
+ *
+ * Hacked to work with the NCR MCA stuff by Tymm Twillman (tymm@computer.org)
+ *
+ * The CyberStorm SCSI driver (and this driver) is based on David S. Miller's
+ * ESP driver * for the Sparc computers.
+ *
+ * Special thanks to Ken Stewart at Symbios (LSI) for helping with info on
+ * the 86C01. I was on the brink of going ga-ga...
+ *
+ * Also thanks to Jesper Skov for helping me with info on how the Amiga
+ * does things...
+ */
+
+/*
+ * This is currently only set up to use one 53c9x card at a time; it could be
+ * changed fairly easily to detect/use more than one, but I'm not too sure how
+ * many cards that use the 53c9x on MCA systems there are (if, in fact, there
+ * are cards that use them, other than the one built into some NCR systems)...
+ * If anyone requests this, I'll throw it in, otherwise it's not worth the
+ * effort.
+ */
+
+/*
+ * Info on the 86C01 MCA interface chip at the bottom, if you care enough to
+ * look.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mca.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mca-legacy.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/mca_dma.h>
+#include <asm/pgtable.h>
+
+/*
+ * From ibmmca.c (IBM scsi controller card driver) -- used for turning PS2 disk
+ * activity LED on and off
+ */
+
+#define PS2_SYS_CTR 0x92
+
+/* Ports the ncr's 53c94 can be put at; indexed by pos register value */
+
+#define MCA_53C9X_IO_PORTS { \
+ 0x0000, 0x0240, 0x0340, 0x0400, \
+ 0x0420, 0x3240, 0x8240, 0xA240, \
+ }
+
+/*
+ * Supposedly there were some cards put together with the 'c9x and 86c01. If
+ * they have different ID's from the ones on the 3500 series machines,
+ * you can add them here and hopefully things will work out.
+ */
+
+#define MCA_53C9X_IDS { \
+ 0x7F4C, \
+ 0x0000, \
+ }
+
+static int dma_bytes_sent(struct NCR_ESP *, int);
+static int dma_can_transfer(struct NCR_ESP *, Scsi_Cmnd *);
+static void dma_dump_state(struct NCR_ESP *);
+static void dma_init_read(struct NCR_ESP *, __u32, int);
+static void dma_init_write(struct NCR_ESP *, __u32, int);
+static void dma_ints_off(struct NCR_ESP *);
+static void dma_ints_on(struct NCR_ESP *);
+static int dma_irq_p(struct NCR_ESP *);
+static int dma_ports_p(struct NCR_ESP *);
+static void dma_setup(struct NCR_ESP *, __u32, int, int);
+static void dma_led_on(struct NCR_ESP *);
+static void dma_led_off(struct NCR_ESP *);
+
+/* This is where all commands are put before they are trasfered to the
+ * 53c9x via PIO.
+ */
+
+static volatile unsigned char cmd_buffer[16];
+
+/*
+ * We keep the structure that is used to access the registers on the 53c9x
+ * here.
+ */
+
+static struct ESP_regs eregs;
+
+/***************************************************************** Detection */
+static int mca_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ static int io_port_by_pos[] = MCA_53C9X_IO_PORTS;
+ int mca_53c9x_ids[] = MCA_53C9X_IDS;
+ int *id_to_check = mca_53c9x_ids;
+ int slot;
+ int pos[3];
+ unsigned int tmp_io_addr;
+ unsigned char tmp_byte;
+
+
+ if (!MCA_bus)
+ return 0;
+
+ while (*id_to_check) {
+ if ((slot = mca_find_adapter(*id_to_check, 0)) !=
+ MCA_NOTFOUND)
+ {
+ esp = esp_allocate(tpnt, (void *) NULL);
+
+ pos[0] = mca_read_stored_pos(slot, 2);
+ pos[1] = mca_read_stored_pos(slot, 3);
+ pos[2] = mca_read_stored_pos(slot, 4);
+
+ esp->eregs = &eregs;
+
+ /*
+ * IO port base is given in the first (non-ID) pos
+ * register, like so:
+ *
+ * Bits 3 2 1 IO base
+ * ----------------------------
+ * 0 0 0 <disabled>
+ * 0 0 1 0x0240
+ * 0 1 0 0x0340
+ * 0 1 1 0x0400
+ * 1 0 0 0x0420
+ * 1 0 1 0x3240
+ * 1 1 0 0x8240
+ * 1 1 1 0xA240
+ */
+
+ tmp_io_addr =
+ io_port_by_pos[(pos[0] & 0x0E) >> 1];
+
+ esp->eregs->io_addr = tmp_io_addr + 0x10;
+
+ if (esp->eregs->io_addr == 0x0000) {
+ printk("Adapter is disabled.\n");
+ break;
+ }
+
+ /*
+ * IRQ is specified in bits 4 and 5:
+ *
+ * Bits 4 5 IRQ
+ * -----------------------
+ * 0 0 3
+ * 0 1 5
+ * 1 0 7
+ * 1 1 9
+ */
+
+ esp->irq = ((pos[0] & 0x30) >> 3) + 3;
+
+ /*
+ * DMA channel is in the low 3 bits of the second
+ * POS register
+ */
+
+ esp->dma = pos[1] & 7;
+ esp->slot = slot;
+
+ if (request_irq(esp->irq, esp_intr, 0,
+ "NCR 53c9x SCSI", esp->ehost))
+ {
+ printk("Unable to request IRQ %d.\n", esp->irq);
+ esp_deallocate(esp);
+ scsi_unregister(esp->ehost);
+ return 0;
+ }
+
+ if (request_dma(esp->dma, "NCR 53c9x SCSI")) {
+ printk("Unable to request DMA channel %d.\n",
+ esp->dma);
+ free_irq(esp->irq, esp_intr);
+ esp_deallocate(esp);
+ scsi_unregister(esp->ehost);
+ return 0;
+ }
+
+ request_region(tmp_io_addr, 32, "NCR 53c9x SCSI");
+
+ /*
+ * 86C01 handles DMA, IO mode, from address
+ * (base + 0x0a)
+ */
+
+ mca_disable_dma(esp->dma);
+ mca_set_dma_io(esp->dma, tmp_io_addr + 0x0a);
+ mca_enable_dma(esp->dma);
+
+ /* Tell the 86C01 to give us interrupts */
+
+ tmp_byte = inb(tmp_io_addr + 0x02) | 0x40;
+ outb(tmp_byte, tmp_io_addr + 0x02);
+
+ /*
+ * Scsi ID -- general purpose register, hi
+ * 2 bits; add 4 to this number to get the
+ * ID
+ */
+
+ esp->scsi_id = ((pos[2] & 0xC0) >> 6) + 4;
+
+ /* Do command transfer with programmed I/O */
+
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+
+ esp->dma_barrier = NULL;
+ esp->dma_drain = NULL;
+ esp->dma_invalidate = NULL;
+ esp->dma_irq_entry = NULL;
+ esp->dma_irq_exit = NULL;
+ esp->dma_led_on = dma_led_on;
+ esp->dma_led_off = dma_led_off;
+ esp->dma_poll = NULL;
+ esp->dma_reset = NULL;
+
+ /* Set the command buffer */
+
+ esp->esp_command = (volatile unsigned char*)
+ cmd_buffer;
+ esp->esp_command_dvma = isa_virt_to_bus(cmd_buffer);
+
+ /* SCSI chip speed */
+
+ esp->cfreq = 25000000;
+
+ /* Differential SCSI? I think not. */
+
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk(" Adapter found in slot %2d: io port 0x%x "
+ "irq %d dma channel %d\n", slot + 1, tmp_io_addr,
+ esp->irq, esp->dma);
+
+ mca_set_adapter_name(slot, "NCR 53C9X SCSI Adapter");
+ mca_mark_as_used(slot);
+
+ break;
+ }
+
+ id_to_check++;
+ }
+
+ return esps_in_use;
+}
+
+
+/******************************************************************* Release */
+
+static int mca_esp_release(struct Scsi_Host *host)
+{
+ struct NCR_ESP *esp = (struct NCR_ESP *)host->hostdata;
+ unsigned char tmp_byte;
+
+ esp_deallocate(esp);
+ /*
+ * Tell the 86C01 to stop sending interrupts
+ */
+
+ tmp_byte = inb(esp->eregs->io_addr - 0x0E);
+ tmp_byte &= ~0x40;
+ outb(tmp_byte, esp->eregs->io_addr - 0x0E);
+
+ free_irq(esp->irq, esp_intr);
+ free_dma(esp->dma);
+
+ mca_mark_as_unused(esp->slot);
+
+ return 0;
+}
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Ask the 53c9x. It knows. */
+
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ /*
+ * The MCA dma channels can only do up to 128K bytes at a time.
+ * (16 bit mode)
+ */
+
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > 0x20000)
+ sz = 0x20000;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ /*
+ * Doesn't quite match up to the other drivers, but we do what we
+ * can.
+ */
+
+ ESPLOG(("esp%d: dma channel <%d>\n", esp->esp_id, esp->dma));
+ ESPLOG(("bytes left to dma: %d\n", mca_get_dma_residue(esp->dma)));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ unsigned long flags;
+
+
+ save_flags(flags);
+ cli();
+
+ mca_disable_dma(esp->dma);
+ mca_set_dma_mode(esp->dma, MCA_DMA_MODE_XFER | MCA_DMA_MODE_16 |
+ MCA_DMA_MODE_IO);
+ mca_set_dma_addr(esp->dma, addr);
+ mca_set_dma_count(esp->dma, length / 2); /* !!! */
+ mca_enable_dma(esp->dma);
+
+ restore_flags(flags);
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length)
+{
+ unsigned long flags;
+
+
+ save_flags(flags);
+ cli();
+
+ mca_disable_dma(esp->dma);
+ mca_set_dma_mode(esp->dma, MCA_DMA_MODE_XFER | MCA_DMA_MODE_WRITE |
+ MCA_DMA_MODE_16 | MCA_DMA_MODE_IO);
+ mca_set_dma_addr(esp->dma, addr);
+ mca_set_dma_count(esp->dma, length / 2); /* !!! */
+ mca_enable_dma(esp->dma);
+
+ restore_flags(flags);
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ /*
+ * Tell the 'C01 to shut up. All interrupts are routed through it.
+ */
+
+ outb(inb(esp->eregs->io_addr - 0x0E) & ~0x40,
+ esp->eregs->io_addr - 0x0E);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ /*
+ * Ok. You can speak again.
+ */
+
+ outb(inb(esp->eregs->io_addr - 0x0E) | 0x40,
+ esp->eregs->io_addr - 0x0E);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ /*
+ * DaveM says that this should return a "yes" if there is an interrupt
+ * or a DMA error occurred. I copied the Amiga driver's semantics,
+ * though, because it seems to work and we can't really tell if
+ * a DMA error happened. This gives the "yes" if the scsi chip
+ * is sending an interrupt and no DMA activity is taking place
+ */
+
+ return (!(inb(esp->eregs->io_addr - 0x04) & 1) &&
+ !(inb(esp->eregs->io_addr - 0x04) & 2) );
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ /*
+ * Check to see if interrupts are enabled on the 'C01 (in case abort
+ * is entered multiple times, so we only do the abort once)
+ */
+
+ return (inb(esp->eregs->io_addr - 0x0E) & 0x40) ? 1:0;
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ if(write){
+ dma_init_write(esp, addr, count);
+ } else {
+ dma_init_read(esp, addr, count);
+ }
+}
+
+/*
+ * These will not play nicely with other disk controllers that try to use the
+ * disk active LED... but what can you do? Don't answer that.
+ *
+ * Stolen shamelessly from ibmmca.c -- IBM Microchannel SCSI adapter driver
+ *
+ */
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+ outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR);
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+ outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR);
+}
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "mca_53c9x",
+ .name = "NCR 53c9x SCSI",
+ .detect = mca_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = mca_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .unchecked_isa_dma = 1,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+/*
+ * OK, here's the goods I promised. The NCR 86C01 is an MCA interface chip
+ * that handles enabling/diabling IRQ, dma interfacing, IO port selection
+ * and other fun stuff. It takes up 16 addresses, and the chip it is
+ * connnected to gets the following 16. Registers are as follows:
+ *
+ * Offsets 0-1 : Card ID
+ *
+ * Offset 2 : Mode enable register --
+ * Bit 7 : Data Word width (1 = 16, 0 = 8)
+ * Bit 6 : IRQ enable (1 = enabled)
+ * Bits 5,4 : IRQ select
+ * 0 0 : IRQ 3
+ * 0 1 : IRQ 5
+ * 1 0 : IRQ 7
+ * 1 1 : IRQ 9
+ * Bits 3-1 : Base Address
+ * 0 0 0 : <disabled>
+ * 0 0 1 : 0x0240
+ * 0 1 0 : 0x0340
+ * 0 1 1 : 0x0400
+ * 1 0 0 : 0x0420
+ * 1 0 1 : 0x3240
+ * 1 1 0 : 0x8240
+ * 1 1 1 : 0xA240
+ * Bit 0 : Card enable (1 = enabled)
+ *
+ * Offset 3 : DMA control register --
+ * Bit 7 : DMA enable (1 = enabled)
+ * Bits 6,5 : Preemt Count Select (transfers to complete after
+ * 'C01 has been preempted on MCA bus)
+ * 0 0 : 0
+ * 0 1 : 1
+ * 1 0 : 3
+ * 1 1 : 7
+ * (all these wacky numbers; I'm sure there's a reason somewhere)
+ * Bit 4 : Fairness enable (1 = fair bus priority)
+ * Bits 3-0 : Arbitration level (0-15 consecutive)
+ *
+ * Offset 4 : General purpose register
+ * Bits 7-3 : User definable (here, 7,6 are SCSI ID)
+ * Bits 2-0 : reserved
+ *
+ * Offset 10 : DMA decode register (used for IO based DMA; also can do
+ * PIO through this port)
+ *
+ * Offset 12 : Status
+ * Bits 7-2 : reserved
+ * Bit 1 : DMA pending (1 = pending)
+ * Bit 0 : IRQ pending (0 = pending)
+ *
+ * Exciting, huh?
+ *
+ */
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
new file mode 100644
index 000000000000..8d707b29027d
--- /dev/null
+++ b/drivers/scsi/megaraid.c
@@ -0,0 +1,5122 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright © 2002 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
+ * - fixes
+ * - speed-ups (list handling fixes, issued_list, optimizations.)
+ * - lots of cleanups.
+ *
+ * Copyright (c) 2003 Christoph Hellwig <hch@lst.de>
+ * - new-style, hotplug-aware pci probing and scsi registration
+ *
+ * Version : v2.00.3 (Feb 19, 2003) - Atul Mukker <Atul.Mukker@lsil.com>
+ *
+ * Description: Linux device driver for LSI Logic MegaRAID controller
+ *
+ * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490, 493
+ * 518, 520, 531, 532
+ *
+ * This driver is supported by LSI Logic, with assistance from Red Hat, Dell,
+ * and others. Please send updates to the mailing list
+ * linux-scsi@vger.kernel.org .
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/reboot.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <scsi/scsicam.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#include "megaraid.h"
+
+#define MEGARAID_MODULE_VERSION "2.00.3"
+
+MODULE_AUTHOR ("LSI Logic Corporation");
+MODULE_DESCRIPTION ("LSI Logic MegaRAID driver");
+MODULE_LICENSE ("GPL");
+MODULE_VERSION(MEGARAID_MODULE_VERSION);
+
+static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN;
+module_param(max_cmd_per_lun, uint, 0);
+MODULE_PARM_DESC(max_cmd_per_lun, "Maximum number of commands which can be issued to a single LUN (default=DEF_CMD_PER_LUN=63)");
+
+static unsigned short int max_sectors_per_io = MAX_SECTORS_PER_IO;
+module_param(max_sectors_per_io, ushort, 0);
+MODULE_PARM_DESC(max_sectors_per_io, "Maximum number of sectors per I/O request (default=MAX_SECTORS_PER_IO=128)");
+
+
+static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT;
+module_param(max_mbox_busy_wait, ushort, 0);
+MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)");
+
+#define RDINDOOR(adapter) readl((adapter)->base + 0x20)
+#define RDOUTDOOR(adapter) readl((adapter)->base + 0x2C)
+#define WRINDOOR(adapter,value) writel(value, (adapter)->base + 0x20)
+#define WROUTDOOR(adapter,value) writel(value, (adapter)->base + 0x2C)
+
+/*
+ * Global variables
+ */
+
+static int hba_count;
+static adapter_t *hba_soft_state[MAX_CONTROLLERS];
+static struct proc_dir_entry *mega_proc_dir_entry;
+
+/* For controller re-ordering */
+static struct mega_hbas mega_hbas[MAX_CONTROLLERS];
+
+/*
+ * The File Operations structure for the serial/ioctl interface of the driver
+ */
+static struct file_operations megadev_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = megadev_ioctl,
+ .open = megadev_open,
+};
+
+/*
+ * Array to structures for storing the information about the controllers. This
+ * information is sent to the user level applications, when they do an ioctl
+ * for this information.
+ */
+static struct mcontroller mcontroller[MAX_CONTROLLERS];
+
+/* The current driver version */
+static u32 driver_ver = 0x02000000;
+
+/* major number used by the device for character interface */
+static int major;
+
+#define IS_RAID_CH(hba, ch) (((hba)->mega_ch_class >> (ch)) & 0x01)
+
+
+/*
+ * Debug variable to print some diagnostic messages
+ */
+static int trace_level;
+
+/**
+ * mega_setup_mailbox()
+ * @adapter - pointer to our soft state
+ *
+ * Allocates a 8 byte aligned memory for the handshake mailbox.
+ */
+static int
+mega_setup_mailbox(adapter_t *adapter)
+{
+ unsigned long align;
+
+ adapter->una_mbox64 = pci_alloc_consistent(adapter->dev,
+ sizeof(mbox64_t), &adapter->una_mbox64_dma);
+
+ if( !adapter->una_mbox64 ) return -1;
+
+ adapter->mbox = &adapter->una_mbox64->mbox;
+
+ adapter->mbox = (mbox_t *)((((unsigned long) adapter->mbox) + 15) &
+ (~0UL ^ 0xFUL));
+
+ adapter->mbox64 = (mbox64_t *)(((unsigned long)adapter->mbox) - 8);
+
+ align = ((void *)adapter->mbox) - ((void *)&adapter->una_mbox64->mbox);
+
+ adapter->mbox_dma = adapter->una_mbox64_dma + 8 + align;
+
+ /*
+ * Register the mailbox if the controller is an io-mapped controller
+ */
+ if( adapter->flag & BOARD_IOMAP ) {
+
+ outb_p(adapter->mbox_dma & 0xFF,
+ adapter->host->io_port + MBOX_PORT0);
+
+ outb_p((adapter->mbox_dma >> 8) & 0xFF,
+ adapter->host->io_port + MBOX_PORT1);
+
+ outb_p((adapter->mbox_dma >> 16) & 0xFF,
+ adapter->host->io_port + MBOX_PORT2);
+
+ outb_p((adapter->mbox_dma >> 24) & 0xFF,
+ adapter->host->io_port + MBOX_PORT3);
+
+ outb_p(ENABLE_MBOX_BYTE,
+ adapter->host->io_port + ENABLE_MBOX_REGION);
+
+ irq_ack(adapter);
+
+ irq_enable(adapter);
+ }
+
+ return 0;
+}
+
+
+/*
+ * mega_query_adapter()
+ * @adapter - pointer to our soft state
+ *
+ * Issue the adapter inquiry commands to the controller and find out
+ * information and parameter about the devices attached
+ */
+static int
+mega_query_adapter(adapter_t *adapter)
+{
+ dma_addr_t prod_info_dma_handle;
+ mega_inquiry3 *inquiry3;
+ u8 raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ int retval;
+
+ /* Initialize adapter inquiry mailbox */
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+
+ /*
+ * Try to issue Inquiry3 command
+ * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and
+ * update enquiry3 structure
+ */
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ inquiry3 = (mega_inquiry3 *)adapter->mega_buffer;
+
+ raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
+ raw_mbox[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */
+ raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */
+
+ /* Issue a blocking command to the card */
+ if ((retval = issue_scb_block(adapter, raw_mbox))) {
+ /* the adapter does not support 40ld */
+
+ mraid_ext_inquiry *ext_inq;
+ mraid_inquiry *inq;
+ dma_addr_t dma_handle;
+
+ ext_inq = pci_alloc_consistent(adapter->dev,
+ sizeof(mraid_ext_inquiry), &dma_handle);
+
+ if( ext_inq == NULL ) return -1;
+
+ inq = &ext_inq->raid_inq;
+
+ mbox->m_out.xferaddr = (u32)dma_handle;
+
+ /*issue old 0x04 command to adapter */
+ mbox->m_out.cmd = MEGA_MBOXCMD_ADPEXTINQ;
+
+ issue_scb_block(adapter, raw_mbox);
+
+ /*
+ * update Enquiry3 and ProductInfo structures with
+ * mraid_inquiry structure
+ */
+ mega_8_to_40ld(inq, inquiry3,
+ (mega_product_info *)&adapter->product_info);
+
+ pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry),
+ ext_inq, dma_handle);
+
+ } else { /*adapter supports 40ld */
+ adapter->flag |= BOARD_40LD;
+
+ /*
+ * get product_info, which is static information and will be
+ * unchanged
+ */
+ prod_info_dma_handle = pci_map_single(adapter->dev, (void *)
+ &adapter->product_info,
+ sizeof(mega_product_info), PCI_DMA_FROMDEVICE);
+
+ mbox->m_out.xferaddr = prod_info_dma_handle;
+
+ raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
+ raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */
+
+ if ((retval = issue_scb_block(adapter, raw_mbox)))
+ printk(KERN_WARNING
+ "megaraid: Product_info cmd failed with error: %d\n",
+ retval);
+
+ pci_unmap_single(adapter->dev, prod_info_dma_handle,
+ sizeof(mega_product_info), PCI_DMA_FROMDEVICE);
+ }
+
+
+ /*
+ * kernel scans the channels from 0 to <= max_channel
+ */
+ adapter->host->max_channel =
+ adapter->product_info.nchannels + NVIRT_CHAN -1;
+
+ adapter->host->max_id = 16; /* max targets per channel */
+
+ adapter->host->max_lun = 7; /* Upto 7 luns for non disk devices */
+
+ adapter->host->cmd_per_lun = max_cmd_per_lun;
+
+ adapter->numldrv = inquiry3->num_ldrv;
+
+ adapter->max_cmds = adapter->product_info.max_commands;
+
+ if(adapter->max_cmds > MAX_COMMANDS)
+ adapter->max_cmds = MAX_COMMANDS;
+
+ adapter->host->can_queue = adapter->max_cmds - 1;
+
+ /*
+ * Get the maximum number of scatter-gather elements supported by this
+ * firmware
+ */
+ mega_get_max_sgl(adapter);
+
+ adapter->host->sg_tablesize = adapter->sglen;
+
+
+ /* use HP firmware and bios version encoding */
+ if (adapter->product_info.subsysvid == HP_SUBSYS_VID) {
+ sprintf (adapter->fw_version, "%c%d%d.%d%d",
+ adapter->product_info.fw_version[2],
+ adapter->product_info.fw_version[1] >> 8,
+ adapter->product_info.fw_version[1] & 0x0f,
+ adapter->product_info.fw_version[0] >> 8,
+ adapter->product_info.fw_version[0] & 0x0f);
+ sprintf (adapter->bios_version, "%c%d%d.%d%d",
+ adapter->product_info.bios_version[2],
+ adapter->product_info.bios_version[1] >> 8,
+ adapter->product_info.bios_version[1] & 0x0f,
+ adapter->product_info.bios_version[0] >> 8,
+ adapter->product_info.bios_version[0] & 0x0f);
+ } else {
+ memcpy(adapter->fw_version,
+ (char *)adapter->product_info.fw_version, 4);
+ adapter->fw_version[4] = 0;
+
+ memcpy(adapter->bios_version,
+ (char *)adapter->product_info.bios_version, 4);
+
+ adapter->bios_version[4] = 0;
+ }
+
+ printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n",
+ adapter->fw_version, adapter->bios_version, adapter->numldrv);
+
+ /*
+ * Do we support extended (>10 bytes) cdbs
+ */
+ adapter->support_ext_cdb = mega_support_ext_cdb(adapter);
+ if (adapter->support_ext_cdb)
+ printk(KERN_NOTICE "megaraid: supports extended CDBs.\n");
+
+
+ return 0;
+}
+
+/**
+ * mega_runpendq()
+ * @adapter - pointer to our soft state
+ *
+ * Runs through the list of pending requests.
+ */
+static inline void
+mega_runpendq(adapter_t *adapter)
+{
+ if(!list_empty(&adapter->pending_list))
+ __mega_runpendq(adapter);
+}
+
+/*
+ * megaraid_queue()
+ * @scmd - Issue this scsi command
+ * @done - the callback hook into the scsi mid-layer
+ *
+ * The command queuing entry point for the mid-layer.
+ */
+static int
+megaraid_queue(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *))
+{
+ adapter_t *adapter;
+ scb_t *scb;
+ int busy=0;
+
+ adapter = (adapter_t *)scmd->device->host->hostdata;
+
+ scmd->scsi_done = done;
+
+
+ /*
+ * Allocate and build a SCB request
+ * busy flag will be set if mega_build_cmd() command could not
+ * allocate scb. We will return non-zero status in that case.
+ * NOTE: scb can be null even though certain commands completed
+ * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, we would
+ * return 0 in that case.
+ */
+
+ scb = mega_build_cmd(adapter, scmd, &busy);
+
+ if(scb) {
+ scb->state |= SCB_PENDQ;
+ list_add_tail(&scb->list, &adapter->pending_list);
+
+ /*
+ * Check if the HBA is in quiescent state, e.g., during a
+ * delete logical drive opertion. If it is, don't run
+ * the pending_list.
+ */
+ if(atomic_read(&adapter->quiescent) == 0) {
+ mega_runpendq(adapter);
+ }
+ return 0;
+ }
+
+ return busy;
+}
+
+/**
+ * mega_allocate_scb()
+ * @adapter - pointer to our soft state
+ * @cmd - scsi command from the mid-layer
+ *
+ * Allocate a SCB structure. This is the central structure for controller
+ * commands.
+ */
+static inline scb_t *
+mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd)
+{
+ struct list_head *head = &adapter->free_list;
+ scb_t *scb;
+
+ /* Unlink command from Free List */
+ if( !list_empty(head) ) {
+
+ scb = list_entry(head->next, scb_t, list);
+
+ list_del_init(head->next);
+
+ scb->state = SCB_ACTIVE;
+ scb->cmd = cmd;
+ scb->dma_type = MEGA_DMA_TYPE_NONE;
+
+ return scb;
+ }
+
+ return NULL;
+}
+
+/**
+ * mega_get_ldrv_num()
+ * @adapter - pointer to our soft state
+ * @cmd - scsi mid layer command
+ * @channel - channel on the controller
+ *
+ * Calculate the logical drive number based on the information in scsi command
+ * and the channel number.
+ */
+static inline int
+mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel)
+{
+ int tgt;
+ int ldrv_num;
+
+ tgt = cmd->device->id;
+
+ if ( tgt > adapter->this_id )
+ tgt--; /* we do not get inquires for initiator id */
+
+ ldrv_num = (channel * 15) + tgt;
+
+
+ /*
+ * If we have a logical drive with boot enabled, project it first
+ */
+ if( adapter->boot_ldrv_enabled ) {
+ if( ldrv_num == 0 ) {
+ ldrv_num = adapter->boot_ldrv;
+ }
+ else {
+ if( ldrv_num <= adapter->boot_ldrv ) {
+ ldrv_num--;
+ }
+ }
+ }
+
+ /*
+ * If "delete logical drive" feature is enabled on this controller.
+ * Do only if at least one delete logical drive operation was done.
+ *
+ * Also, after logical drive deletion, instead of logical drive number,
+ * the value returned should be 0x80+logical drive id.
+ *
+ * These is valid only for IO commands.
+ */
+
+ if (adapter->support_random_del && adapter->read_ldidmap )
+ switch (cmd->cmnd[0]) {
+ case READ_6: /* fall through */
+ case WRITE_6: /* fall through */
+ case READ_10: /* fall through */
+ case WRITE_10:
+ ldrv_num += 0x80;
+ }
+
+ return ldrv_num;
+}
+
+/**
+ * mega_build_cmd()
+ * @adapter - pointer to our soft state
+ * @cmd - Prepare using this scsi command
+ * @busy - busy flag if no resources
+ *
+ * Prepares a command and scatter gather list for the controller. This routine
+ * also finds out if the commands is intended for a logical drive or a
+ * physical device and prepares the controller command accordingly.
+ *
+ * We also re-order the logical drives and physical devices based on their
+ * boot settings.
+ */
+static scb_t *
+mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
+{
+ mega_ext_passthru *epthru;
+ mega_passthru *pthru;
+ scb_t *scb;
+ mbox_t *mbox;
+ long seg;
+ char islogical;
+ int max_ldrv_num;
+ int channel = 0;
+ int target = 0;
+ int ldrv_num = 0; /* logical drive number */
+
+
+ /*
+ * filter the internal and ioctl commands
+ */
+ if((cmd->cmnd[0] == MEGA_INTERNAL_CMD)) {
+ return cmd->buffer;
+ }
+
+
+ /*
+ * We know what channels our logical drives are on - mega_find_card()
+ */
+ islogical = adapter->logdrv_chan[cmd->device->channel];
+
+ /*
+ * The theory: If physical drive is chosen for boot, all the physical
+ * devices are exported before the logical drives, otherwise physical
+ * devices are pushed after logical drives, in which case - Kernel sees
+ * the physical devices on virtual channel which is obviously converted
+ * to actual channel on the HBA.
+ */
+ if( adapter->boot_pdrv_enabled ) {
+ if( islogical ) {
+ /* logical channel */
+ channel = cmd->device->channel -
+ adapter->product_info.nchannels;
+ }
+ else {
+ /* this is physical channel */
+ channel = cmd->device->channel;
+ target = cmd->device->id;
+
+ /*
+ * boot from a physical disk, that disk needs to be
+ * exposed first IF both the channels are SCSI, then
+ * booting from the second channel is not allowed.
+ */
+ if( target == 0 ) {
+ target = adapter->boot_pdrv_tgt;
+ }
+ else if( target == adapter->boot_pdrv_tgt ) {
+ target = 0;
+ }
+ }
+ }
+ else {
+ if( islogical ) {
+ /* this is the logical channel */
+ channel = cmd->device->channel;
+ }
+ else {
+ /* physical channel */
+ channel = cmd->device->channel - NVIRT_CHAN;
+ target = cmd->device->id;
+ }
+ }
+
+
+ if(islogical) {
+
+ /* have just LUN 0 for each target on virtual channels */
+ if (cmd->device->lun) {
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ ldrv_num = mega_get_ldrv_num(adapter, cmd, channel);
+
+
+ max_ldrv_num = (adapter->flag & BOARD_40LD) ?
+ MAX_LOGICAL_DRIVES_40LD : MAX_LOGICAL_DRIVES_8LD;
+
+ /*
+ * max_ldrv_num increases by 0x80 if some logical drive was
+ * deleted.
+ */
+ if(adapter->read_ldidmap)
+ max_ldrv_num += 0x80;
+
+ if(ldrv_num > max_ldrv_num ) {
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ }
+ else {
+ if( cmd->device->lun > 7) {
+ /*
+ * Do not support lun >7 for physically accessed
+ * devices
+ */
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+ }
+
+ /*
+ *
+ * Logical drive commands
+ *
+ */
+ if(islogical) {
+ switch (cmd->cmnd[0]) {
+ case TEST_UNIT_READY:
+ memset(cmd->request_buffer, 0, cmd->request_bufflen);
+
+#if MEGA_HAVE_CLUSTERING
+ /*
+ * Do we support clustering and is the support enabled
+ * If no, return success always
+ */
+ if( !adapter->has_cluster ) {
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+
+ scb->raw_mbox[0] = MEGA_CLUSTER_CMD;
+ scb->raw_mbox[2] = MEGA_RESERVATION_STATUS;
+ scb->raw_mbox[3] = ldrv_num;
+
+ scb->dma_direction = PCI_DMA_NONE;
+
+ return scb;
+#else
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+#endif
+
+ case MODE_SENSE:
+ memset(cmd->request_buffer, 0, cmd->cmnd[4]);
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+
+ case READ_CAPACITY:
+ case INQUIRY:
+
+ if(!(adapter->flag & (1L << cmd->device->channel))) {
+
+ printk(KERN_NOTICE
+ "scsi%d: scanning scsi channel %d ",
+ adapter->host->host_no,
+ cmd->device->channel);
+ printk("for logical drives.\n");
+
+ adapter->flag |= (1L << cmd->device->channel);
+ }
+
+ /* Allocate a SCB and initialize passthru */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+ pthru = scb->pthru;
+
+ mbox = (mbox_t *)scb->raw_mbox;
+ memset(mbox, 0, sizeof(scb->raw_mbox));
+ memset(pthru, 0, sizeof(mega_passthru));
+
+ pthru->timeout = 0;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 1;
+ pthru->logdrv = ldrv_num;
+ pthru->cdblen = cmd->cmd_len;
+ memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len);
+
+ if( adapter->has_64bit_addr ) {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU64;
+ }
+ else {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU;
+ }
+
+ scb->dma_direction = PCI_DMA_FROMDEVICE;
+
+ pthru->numsgelements = mega_build_sglist(adapter, scb,
+ &pthru->dataxferaddr, &pthru->dataxferlen);
+
+ mbox->m_out.xferaddr = scb->pthru_dma_addr;
+
+ return scb;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+
+ /* Allocate a SCB and initialize mailbox */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+ mbox = (mbox_t *)scb->raw_mbox;
+
+ memset(mbox, 0, sizeof(scb->raw_mbox));
+ mbox->m_out.logdrv = ldrv_num;
+
+ /*
+ * A little hack: 2nd bit is zero for all scsi read
+ * commands and is set for all scsi write commands
+ */
+ if( adapter->has_64bit_addr ) {
+ mbox->m_out.cmd = (*cmd->cmnd & 0x02) ?
+ MEGA_MBOXCMD_LWRITE64:
+ MEGA_MBOXCMD_LREAD64 ;
+ }
+ else {
+ mbox->m_out.cmd = (*cmd->cmnd & 0x02) ?
+ MEGA_MBOXCMD_LWRITE:
+ MEGA_MBOXCMD_LREAD ;
+ }
+
+ /*
+ * 6-byte READ(0x08) or WRITE(0x0A) cdb
+ */
+ if( cmd->cmd_len == 6 ) {
+ mbox->m_out.numsectors = (u32) cmd->cmnd[4];
+ mbox->m_out.lba =
+ ((u32)cmd->cmnd[1] << 16) |
+ ((u32)cmd->cmnd[2] << 8) |
+ (u32)cmd->cmnd[3];
+
+ mbox->m_out.lba &= 0x1FFFFF;
+
+#if MEGA_HAVE_STATS
+ /*
+ * Take modulo 0x80, since the logical drive
+ * number increases by 0x80 when a logical
+ * drive was deleted
+ */
+ if (*cmd->cmnd == READ_6) {
+ adapter->nreads[ldrv_num%0x80]++;
+ adapter->nreadblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ } else {
+ adapter->nwrites[ldrv_num%0x80]++;
+ adapter->nwriteblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ }
+#endif
+ }
+
+ /*
+ * 10-byte READ(0x28) or WRITE(0x2A) cdb
+ */
+ if( cmd->cmd_len == 10 ) {
+ mbox->m_out.numsectors =
+ (u32)cmd->cmnd[8] |
+ ((u32)cmd->cmnd[7] << 8);
+ mbox->m_out.lba =
+ ((u32)cmd->cmnd[2] << 24) |
+ ((u32)cmd->cmnd[3] << 16) |
+ ((u32)cmd->cmnd[4] << 8) |
+ (u32)cmd->cmnd[5];
+
+#if MEGA_HAVE_STATS
+ if (*cmd->cmnd == READ_10) {
+ adapter->nreads[ldrv_num%0x80]++;
+ adapter->nreadblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ } else {
+ adapter->nwrites[ldrv_num%0x80]++;
+ adapter->nwriteblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ }
+#endif
+ }
+
+ /*
+ * 12-byte READ(0xA8) or WRITE(0xAA) cdb
+ */
+ if( cmd->cmd_len == 12 ) {
+ mbox->m_out.lba =
+ ((u32)cmd->cmnd[2] << 24) |
+ ((u32)cmd->cmnd[3] << 16) |
+ ((u32)cmd->cmnd[4] << 8) |
+ (u32)cmd->cmnd[5];
+
+ mbox->m_out.numsectors =
+ ((u32)cmd->cmnd[6] << 24) |
+ ((u32)cmd->cmnd[7] << 16) |
+ ((u32)cmd->cmnd[8] << 8) |
+ (u32)cmd->cmnd[9];
+
+#if MEGA_HAVE_STATS
+ if (*cmd->cmnd == READ_12) {
+ adapter->nreads[ldrv_num%0x80]++;
+ adapter->nreadblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ } else {
+ adapter->nwrites[ldrv_num%0x80]++;
+ adapter->nwriteblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ }
+#endif
+ }
+
+ /*
+ * If it is a read command
+ */
+ if( (*cmd->cmnd & 0x0F) == 0x08 ) {
+ scb->dma_direction = PCI_DMA_FROMDEVICE;
+ }
+ else {
+ scb->dma_direction = PCI_DMA_TODEVICE;
+ }
+
+ /* Calculate Scatter-Gather info */
+ mbox->m_out.numsgelements = mega_build_sglist(adapter, scb,
+ (u32 *)&mbox->m_out.xferaddr, (u32 *)&seg);
+
+ return scb;
+
+#if MEGA_HAVE_CLUSTERING
+ case RESERVE: /* Fall through */
+ case RELEASE:
+
+ /*
+ * Do we support clustering and is the support enabled
+ */
+ if( ! adapter->has_cluster ) {
+
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ /* Allocate a SCB and initialize mailbox */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+
+ scb->raw_mbox[0] = MEGA_CLUSTER_CMD;
+ scb->raw_mbox[2] = ( *cmd->cmnd == RESERVE ) ?
+ MEGA_RESERVE_LD : MEGA_RELEASE_LD;
+
+ scb->raw_mbox[3] = ldrv_num;
+
+ scb->dma_direction = PCI_DMA_NONE;
+
+ return scb;
+#endif
+
+ default:
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+ }
+
+ /*
+ * Passthru drive commands
+ */
+ else {
+ /* Allocate a SCB and initialize passthru */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+
+ mbox = (mbox_t *)scb->raw_mbox;
+ memset(mbox, 0, sizeof(scb->raw_mbox));
+
+ if( adapter->support_ext_cdb ) {
+
+ epthru = mega_prepare_extpassthru(adapter, scb, cmd,
+ channel, target);
+
+ mbox->m_out.cmd = MEGA_MBOXCMD_EXTPTHRU;
+
+ mbox->m_out.xferaddr = scb->epthru_dma_addr;
+
+ }
+ else {
+
+ pthru = mega_prepare_passthru(adapter, scb, cmd,
+ channel, target);
+
+ /* Initialize mailbox */
+ if( adapter->has_64bit_addr ) {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU64;
+ }
+ else {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU;
+ }
+
+ mbox->m_out.xferaddr = scb->pthru_dma_addr;
+
+ }
+ return scb;
+ }
+ return NULL;
+}
+
+
+/**
+ * mega_prepare_passthru()
+ * @adapter - pointer to our soft state
+ * @scb - our scsi control block
+ * @cmd - scsi command from the mid-layer
+ * @channel - actual channel on the controller
+ * @target - actual id on the controller.
+ *
+ * prepare a command for the scsi physical devices.
+ */
+static mega_passthru *
+mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
+ int channel, int target)
+{
+ mega_passthru *pthru;
+
+ pthru = scb->pthru;
+ memset(pthru, 0, sizeof (mega_passthru));
+
+ /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ pthru->timeout = 2;
+
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 0;
+
+ pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel;
+
+ pthru->target = (adapter->flag & BOARD_40LD) ?
+ (channel << 4) | target : target;
+
+ pthru->cdblen = cmd->cmd_len;
+ pthru->logdrv = cmd->device->lun;
+
+ memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len);
+
+ /* Not sure about the direction */
+ scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+
+ /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
+ switch (cmd->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ if(!(adapter->flag & (1L << cmd->device->channel))) {
+
+ printk(KERN_NOTICE
+ "scsi%d: scanning scsi channel %d [P%d] ",
+ adapter->host->host_no,
+ cmd->device->channel, channel);
+ printk("for physical devices.\n");
+
+ adapter->flag |= (1L << cmd->device->channel);
+ }
+ /* Fall through */
+ default:
+ pthru->numsgelements = mega_build_sglist(adapter, scb,
+ &pthru->dataxferaddr, &pthru->dataxferlen);
+ break;
+ }
+ return pthru;
+}
+
+
+/**
+ * mega_prepare_extpassthru()
+ * @adapter - pointer to our soft state
+ * @scb - our scsi control block
+ * @cmd - scsi command from the mid-layer
+ * @channel - actual channel on the controller
+ * @target - actual id on the controller.
+ *
+ * prepare a command for the scsi physical devices. This rountine prepares
+ * commands for devices which can take extended CDBs (>10 bytes)
+ */
+static mega_ext_passthru *
+mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
+ int channel, int target)
+{
+ mega_ext_passthru *epthru;
+
+ epthru = scb->epthru;
+ memset(epthru, 0, sizeof(mega_ext_passthru));
+
+ /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ epthru->timeout = 2;
+
+ epthru->ars = 1;
+ epthru->reqsenselen = 14;
+ epthru->islogical = 0;
+
+ epthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel;
+ epthru->target = (adapter->flag & BOARD_40LD) ?
+ (channel << 4) | target : target;
+
+ epthru->cdblen = cmd->cmd_len;
+ epthru->logdrv = cmd->device->lun;
+
+ memcpy(epthru->cdb, cmd->cmnd, cmd->cmd_len);
+
+ /* Not sure about the direction */
+ scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+
+ switch(cmd->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ if(!(adapter->flag & (1L << cmd->device->channel))) {
+
+ printk(KERN_NOTICE
+ "scsi%d: scanning scsi channel %d [P%d] ",
+ adapter->host->host_no,
+ cmd->device->channel, channel);
+ printk("for physical devices.\n");
+
+ adapter->flag |= (1L << cmd->device->channel);
+ }
+ /* Fall through */
+ default:
+ epthru->numsgelements = mega_build_sglist(adapter, scb,
+ &epthru->dataxferaddr, &epthru->dataxferlen);
+ break;
+ }
+
+ return epthru;
+}
+
+static void
+__mega_runpendq(adapter_t *adapter)
+{
+ scb_t *scb;
+ struct list_head *pos, *next;
+
+ /* Issue any pending commands to the card */
+ list_for_each_safe(pos, next, &adapter->pending_list) {
+
+ scb = list_entry(pos, scb_t, list);
+
+ if( !(scb->state & SCB_ISSUED) ) {
+
+ if( issue_scb(adapter, scb) != 0 )
+ return;
+ }
+ }
+
+ return;
+}
+
+
+/**
+ * issue_scb()
+ * @adapter - pointer to our soft state
+ * @scb - scsi control block
+ *
+ * Post a command to the card if the mailbox is available, otherwise return
+ * busy. We also take the scb from the pending list if the mailbox is
+ * available.
+ */
+static int
+issue_scb(adapter_t *adapter, scb_t *scb)
+{
+ volatile mbox64_t *mbox64 = adapter->mbox64;
+ volatile mbox_t *mbox = adapter->mbox;
+ unsigned int i = 0;
+
+ if(unlikely(mbox->m_in.busy)) {
+ do {
+ udelay(1);
+ i++;
+ } while( mbox->m_in.busy && (i < max_mbox_busy_wait) );
+
+ if(mbox->m_in.busy) return -1;
+ }
+
+ /* Copy mailbox data into host structure */
+ memcpy((char *)&mbox->m_out, (char *)scb->raw_mbox,
+ sizeof(struct mbox_out));
+
+ mbox->m_out.cmdid = scb->idx; /* Set cmdid */
+ mbox->m_in.busy = 1; /* Set busy */
+
+
+ /*
+ * Increment the pending queue counter
+ */
+ atomic_inc(&adapter->pend_cmds);
+
+ switch (mbox->m_out.cmd) {
+ case MEGA_MBOXCMD_LREAD64:
+ case MEGA_MBOXCMD_LWRITE64:
+ case MEGA_MBOXCMD_PASSTHRU64:
+ case MEGA_MBOXCMD_EXTPTHRU:
+ mbox64->xfer_segment_lo = mbox->m_out.xferaddr;
+ mbox64->xfer_segment_hi = 0;
+ mbox->m_out.xferaddr = 0xFFFFFFFF;
+ break;
+ default:
+ mbox64->xfer_segment_lo = 0;
+ mbox64->xfer_segment_hi = 0;
+ }
+
+ /*
+ * post the command
+ */
+ scb->state |= SCB_ISSUED;
+
+ if( likely(adapter->flag & BOARD_MEMMAP) ) {
+ mbox->m_in.poll = 0;
+ mbox->m_in.ack = 0;
+ WRINDOOR(adapter, adapter->mbox_dma | 0x1);
+ }
+ else {
+ irq_enable(adapter);
+ issue_command(adapter);
+ }
+
+ return 0;
+}
+
+/*
+ * Wait until the controller's mailbox is available
+ */
+static inline int
+mega_busywait_mbox (adapter_t *adapter)
+{
+ if (adapter->mbox->m_in.busy)
+ return __mega_busywait_mbox(adapter);
+ return 0;
+}
+
+/**
+ * issue_scb_block()
+ * @adapter - pointer to our soft state
+ * @raw_mbox - the mailbox
+ *
+ * Issue a scb in synchronous and non-interrupt mode
+ */
+static int
+issue_scb_block(adapter_t *adapter, u_char *raw_mbox)
+{
+ volatile mbox64_t *mbox64 = adapter->mbox64;
+ volatile mbox_t *mbox = adapter->mbox;
+ u8 byte;
+
+ /* Wait until mailbox is free */
+ if(mega_busywait_mbox (adapter))
+ goto bug_blocked_mailbox;
+
+ /* Copy mailbox data into host structure */
+ memcpy((char *) mbox, raw_mbox, sizeof(struct mbox_out));
+ mbox->m_out.cmdid = 0xFE;
+ mbox->m_in.busy = 1;
+
+ switch (raw_mbox[0]) {
+ case MEGA_MBOXCMD_LREAD64:
+ case MEGA_MBOXCMD_LWRITE64:
+ case MEGA_MBOXCMD_PASSTHRU64:
+ case MEGA_MBOXCMD_EXTPTHRU:
+ mbox64->xfer_segment_lo = mbox->m_out.xferaddr;
+ mbox64->xfer_segment_hi = 0;
+ mbox->m_out.xferaddr = 0xFFFFFFFF;
+ break;
+ default:
+ mbox64->xfer_segment_lo = 0;
+ mbox64->xfer_segment_hi = 0;
+ }
+
+ if( likely(adapter->flag & BOARD_MEMMAP) ) {
+ mbox->m_in.poll = 0;
+ mbox->m_in.ack = 0;
+ mbox->m_in.numstatus = 0xFF;
+ mbox->m_in.status = 0xFF;
+ WRINDOOR(adapter, adapter->mbox_dma | 0x1);
+
+ while((volatile u8)mbox->m_in.numstatus == 0xFF)
+ cpu_relax();
+
+ mbox->m_in.numstatus = 0xFF;
+
+ while( (volatile u8)mbox->m_in.poll != 0x77 )
+ cpu_relax();
+
+ mbox->m_in.poll = 0;
+ mbox->m_in.ack = 0x77;
+
+ WRINDOOR(adapter, adapter->mbox_dma | 0x2);
+
+ while(RDINDOOR(adapter) & 0x2)
+ cpu_relax();
+ }
+ else {
+ irq_disable(adapter);
+ issue_command(adapter);
+
+ while (!((byte = irq_state(adapter)) & INTR_VALID))
+ cpu_relax();
+
+ set_irq_state(adapter, byte);
+ irq_enable(adapter);
+ irq_ack(adapter);
+ }
+
+ return mbox->m_in.status;
+
+bug_blocked_mailbox:
+ printk(KERN_WARNING "megaraid: Blocked mailbox......!!\n");
+ udelay (1000);
+ return -1;
+}
+
+
+/**
+ * megaraid_isr_iomapped()
+ * @irq - irq
+ * @devp - pointer to our soft state
+ * @regs - unused
+ *
+ * Interrupt service routine for io-mapped controllers.
+ * Find out if our device is interrupting. If yes, acknowledge the interrupt
+ * and service the completed commands.
+ */
+static irqreturn_t
+megaraid_isr_iomapped(int irq, void *devp, struct pt_regs *regs)
+{
+ adapter_t *adapter = devp;
+ unsigned long flags;
+ u8 status;
+ u8 nstatus;
+ u8 completed[MAX_FIRMWARE_STATUS];
+ u8 byte;
+ int handled = 0;
+
+
+ /*
+ * loop till F/W has more commands for us to complete.
+ */
+ spin_lock_irqsave(&adapter->lock, flags);
+
+ do {
+ /* Check if a valid interrupt is pending */
+ byte = irq_state(adapter);
+ if( (byte & VALID_INTR_BYTE) == 0 ) {
+ /*
+ * No more pending commands
+ */
+ goto out_unlock;
+ }
+ set_irq_state(adapter, byte);
+
+ while((nstatus = (volatile u8)adapter->mbox->m_in.numstatus)
+ == 0xFF)
+ cpu_relax();
+ adapter->mbox->m_in.numstatus = 0xFF;
+
+ status = adapter->mbox->m_in.status;
+
+ /*
+ * decrement the pending queue counter
+ */
+ atomic_sub(nstatus, &adapter->pend_cmds);
+
+ memcpy(completed, (void *)adapter->mbox->m_in.completed,
+ nstatus);
+
+ /* Acknowledge interrupt */
+ irq_ack(adapter);
+
+ mega_cmd_done(adapter, completed, nstatus, status);
+
+ mega_rundoneq(adapter);
+
+ handled = 1;
+
+ /* Loop through any pending requests */
+ if(atomic_read(&adapter->quiescent) == 0) {
+ mega_runpendq(adapter);
+ }
+
+ } while(1);
+
+ out_unlock:
+
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/**
+ * megaraid_isr_memmapped()
+ * @irq - irq
+ * @devp - pointer to our soft state
+ * @regs - unused
+ *
+ * Interrupt service routine for memory-mapped controllers.
+ * Find out if our device is interrupting. If yes, acknowledge the interrupt
+ * and service the completed commands.
+ */
+static irqreturn_t
+megaraid_isr_memmapped(int irq, void *devp, struct pt_regs *regs)
+{
+ adapter_t *adapter = devp;
+ unsigned long flags;
+ u8 status;
+ u32 dword = 0;
+ u8 nstatus;
+ u8 completed[MAX_FIRMWARE_STATUS];
+ int handled = 0;
+
+
+ /*
+ * loop till F/W has more commands for us to complete.
+ */
+ spin_lock_irqsave(&adapter->lock, flags);
+
+ do {
+ /* Check if a valid interrupt is pending */
+ dword = RDOUTDOOR(adapter);
+ if(dword != 0x10001234) {
+ /*
+ * No more pending commands
+ */
+ goto out_unlock;
+ }
+ WROUTDOOR(adapter, 0x10001234);
+
+ while((nstatus = (volatile u8)adapter->mbox->m_in.numstatus)
+ == 0xFF) {
+ cpu_relax();
+ }
+ adapter->mbox->m_in.numstatus = 0xFF;
+
+ status = adapter->mbox->m_in.status;
+
+ /*
+ * decrement the pending queue counter
+ */
+ atomic_sub(nstatus, &adapter->pend_cmds);
+
+ memcpy(completed, (void *)adapter->mbox->m_in.completed,
+ nstatus);
+
+ /* Acknowledge interrupt */
+ WRINDOOR(adapter, 0x2);
+
+ handled = 1;
+
+ while( RDINDOOR(adapter) & 0x02 ) cpu_relax();
+
+ mega_cmd_done(adapter, completed, nstatus, status);
+
+ mega_rundoneq(adapter);
+
+ /* Loop through any pending requests */
+ if(atomic_read(&adapter->quiescent) == 0) {
+ mega_runpendq(adapter);
+ }
+
+ } while(1);
+
+ out_unlock:
+
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+/**
+ * mega_cmd_done()
+ * @adapter - pointer to our soft state
+ * @completed - array of ids of completed commands
+ * @nstatus - number of completed commands
+ * @status - status of the last command completed
+ *
+ * Complete the comamnds and call the scsi mid-layer callback hooks.
+ */
+static void
+mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
+{
+ mega_ext_passthru *epthru = NULL;
+ struct scatterlist *sgl;
+ Scsi_Cmnd *cmd = NULL;
+ mega_passthru *pthru = NULL;
+ mbox_t *mbox = NULL;
+ u8 c;
+ scb_t *scb;
+ int islogical;
+ int cmdid;
+ int i;
+
+ /*
+ * for all the commands completed, call the mid-layer callback routine
+ * and free the scb.
+ */
+ for( i = 0; i < nstatus; i++ ) {
+
+ cmdid = completed[i];
+
+ if( cmdid == CMDID_INT_CMDS ) { /* internal command */
+ scb = &adapter->int_scb;
+ cmd = scb->cmd;
+ mbox = (mbox_t *)scb->raw_mbox;
+
+ /*
+ * Internal command interface do not fire the extended
+ * passthru or 64-bit passthru
+ */
+ pthru = scb->pthru;
+
+ }
+ else {
+ scb = &adapter->scb_list[cmdid];
+
+ /*
+ * Make sure f/w has completed a valid command
+ */
+ if( !(scb->state & SCB_ISSUED) || scb->cmd == NULL ) {
+ printk(KERN_CRIT
+ "megaraid: invalid command ");
+ printk("Id %d, scb->state:%x, scsi cmd:%p\n",
+ cmdid, scb->state, scb->cmd);
+
+ continue;
+ }
+
+ /*
+ * Was a abort issued for this command
+ */
+ if( scb->state & SCB_ABORT ) {
+
+ printk(KERN_WARNING
+ "megaraid: aborted cmd %lx[%x] complete.\n",
+ scb->cmd->serial_number, scb->idx);
+
+ scb->cmd->result = (DID_ABORT << 16);
+
+ list_add_tail(SCSI_LIST(scb->cmd),
+ &adapter->completed_list);
+
+ mega_free_scb(adapter, scb);
+
+ continue;
+ }
+
+ /*
+ * Was a reset issued for this command
+ */
+ if( scb->state & SCB_RESET ) {
+
+ printk(KERN_WARNING
+ "megaraid: reset cmd %lx[%x] complete.\n",
+ scb->cmd->serial_number, scb->idx);
+
+ scb->cmd->result = (DID_RESET << 16);
+
+ list_add_tail(SCSI_LIST(scb->cmd),
+ &adapter->completed_list);
+
+ mega_free_scb (adapter, scb);
+
+ continue;
+ }
+
+ cmd = scb->cmd;
+ pthru = scb->pthru;
+ epthru = scb->epthru;
+ mbox = (mbox_t *)scb->raw_mbox;
+
+#if MEGA_HAVE_STATS
+ {
+
+ int logdrv = mbox->m_out.logdrv;
+
+ islogical = adapter->logdrv_chan[cmd->channel];
+ /*
+ * Maintain an error counter for the logical drive.
+ * Some application like SNMP agent need such
+ * statistics
+ */
+ if( status && islogical && (cmd->cmnd[0] == READ_6 ||
+ cmd->cmnd[0] == READ_10 ||
+ cmd->cmnd[0] == READ_12)) {
+ /*
+ * Logical drive number increases by 0x80 when
+ * a logical drive is deleted
+ */
+ adapter->rd_errors[logdrv%0x80]++;
+ }
+
+ if( status && islogical && (cmd->cmnd[0] == WRITE_6 ||
+ cmd->cmnd[0] == WRITE_10 ||
+ cmd->cmnd[0] == WRITE_12)) {
+ /*
+ * Logical drive number increases by 0x80 when
+ * a logical drive is deleted
+ */
+ adapter->wr_errors[logdrv%0x80]++;
+ }
+
+ }
+#endif
+ }
+
+ /*
+ * Do not return the presence of hard disk on the channel so,
+ * inquiry sent, and returned data==hard disk or removable
+ * hard disk and not logical, request should return failure! -
+ * PJ
+ */
+ islogical = adapter->logdrv_chan[cmd->device->channel];
+ if( cmd->cmnd[0] == INQUIRY && !islogical ) {
+
+ if( cmd->use_sg ) {
+ sgl = (struct scatterlist *)
+ cmd->request_buffer;
+
+ if( sgl->page ) {
+ c = *(unsigned char *)
+ page_address((&sgl[0])->page) +
+ (&sgl[0])->offset;
+ }
+ else {
+ printk(KERN_WARNING
+ "megaraid: invalid sg.\n");
+ c = 0;
+ }
+ }
+ else {
+ c = *(u8 *)cmd->request_buffer;
+ }
+
+ if(IS_RAID_CH(adapter, cmd->device->channel) &&
+ ((c & 0x1F ) == TYPE_DISK)) {
+ status = 0xF0;
+ }
+ }
+
+ /* clear result; otherwise, success returns corrupt value */
+ cmd->result = 0;
+
+ /* Convert MegaRAID status to Linux error code */
+ switch (status) {
+ case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */
+ cmd->result |= (DID_OK << 16);
+ break;
+
+ case 0x02: /* ERROR_ABORTED, i.e.
+ SCSI_STATUS_CHECK_CONDITION */
+
+ /* set sense_buffer and result fields */
+ if( mbox->m_out.cmd == MEGA_MBOXCMD_PASSTHRU ||
+ mbox->m_out.cmd == MEGA_MBOXCMD_PASSTHRU64 ) {
+
+ memcpy(cmd->sense_buffer, pthru->reqsensearea,
+ 14);
+
+ cmd->result = (DRIVER_SENSE << 24) |
+ (DID_OK << 16) |
+ (CHECK_CONDITION << 1);
+ }
+ else {
+ if (mbox->m_out.cmd == MEGA_MBOXCMD_EXTPTHRU) {
+
+ memcpy(cmd->sense_buffer,
+ epthru->reqsensearea, 14);
+
+ cmd->result = (DRIVER_SENSE << 24) |
+ (DID_OK << 16) |
+ (CHECK_CONDITION << 1);
+ } else {
+ cmd->sense_buffer[0] = 0x70;
+ cmd->sense_buffer[2] = ABORTED_COMMAND;
+ cmd->result |= (CHECK_CONDITION << 1);
+ }
+ }
+ break;
+
+ case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e.
+ SCSI_STATUS_BUSY */
+ cmd->result |= (DID_BUS_BUSY << 16) | status;
+ break;
+
+ default:
+#if MEGA_HAVE_CLUSTERING
+ /*
+ * If TEST_UNIT_READY fails, we know
+ * MEGA_RESERVATION_STATUS failed
+ */
+ if( cmd->cmnd[0] == TEST_UNIT_READY ) {
+ cmd->result |= (DID_ERROR << 16) |
+ (RESERVATION_CONFLICT << 1);
+ }
+ else
+ /*
+ * Error code returned is 1 if Reserve or Release
+ * failed or the input parameter is invalid
+ */
+ if( status == 1 &&
+ (cmd->cmnd[0] == RESERVE ||
+ cmd->cmnd[0] == RELEASE) ) {
+
+ cmd->result |= (DID_ERROR << 16) |
+ (RESERVATION_CONFLICT << 1);
+ }
+ else
+#endif
+ cmd->result |= (DID_BAD_TARGET << 16)|status;
+ }
+
+ /*
+ * Only free SCBs for the commands coming down from the
+ * mid-layer, not for which were issued internally
+ *
+ * For internal command, restore the status returned by the
+ * firmware so that user can interpret it.
+ */
+ if( cmdid == CMDID_INT_CMDS ) { /* internal command */
+ cmd->result = status;
+
+ /*
+ * Remove the internal command from the pending list
+ */
+ list_del_init(&scb->list);
+ scb->state = SCB_FREE;
+ }
+ else {
+ mega_free_scb(adapter, scb);
+ }
+
+ /* Add Scsi_Command to end of completed queue */
+ list_add_tail(SCSI_LIST(cmd), &adapter->completed_list);
+ }
+}
+
+
+/*
+ * mega_runpendq()
+ *
+ * Run through the list of completed requests and finish it
+ */
+static void
+mega_rundoneq (adapter_t *adapter)
+{
+ Scsi_Cmnd *cmd;
+ struct list_head *pos;
+
+ list_for_each(pos, &adapter->completed_list) {
+
+ Scsi_Pointer* spos = (Scsi_Pointer *)pos;
+
+ cmd = list_entry(spos, Scsi_Cmnd, SCp);
+ cmd->scsi_done(cmd);
+ }
+
+ INIT_LIST_HEAD(&adapter->completed_list);
+}
+
+
+/*
+ * Free a SCB structure
+ * Note: We assume the scsi commands associated with this scb is not free yet.
+ */
+static void
+mega_free_scb(adapter_t *adapter, scb_t *scb)
+{
+ switch( scb->dma_type ) {
+
+ case MEGA_DMA_TYPE_NONE:
+ break;
+
+ case MEGA_BULK_DATA:
+ pci_unmap_page(adapter->dev, scb->dma_h_bulkdata,
+ scb->cmd->request_bufflen, scb->dma_direction);
+ break;
+
+ case MEGA_SGLIST:
+ pci_unmap_sg(adapter->dev, scb->cmd->request_buffer,
+ scb->cmd->use_sg, scb->dma_direction);
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * Remove from the pending list
+ */
+ list_del_init(&scb->list);
+
+ /* Link the scb back into free list */
+ scb->state = SCB_FREE;
+ scb->cmd = NULL;
+
+ list_add(&scb->list, &adapter->free_list);
+}
+
+
+static int
+__mega_busywait_mbox (adapter_t *adapter)
+{
+ volatile mbox_t *mbox = adapter->mbox;
+ long counter;
+
+ for (counter = 0; counter < 10000; counter++) {
+ if (!mbox->m_in.busy)
+ return 0;
+ udelay(100); yield();
+ }
+ return -1; /* give up after 1 second */
+}
+
+/*
+ * Copies data to SGLIST
+ * Note: For 64 bit cards, we need a minimum of one SG element for read/write
+ */
+static int
+mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len)
+{
+ struct scatterlist *sgl;
+ struct page *page;
+ unsigned long offset;
+ Scsi_Cmnd *cmd;
+ int sgcnt;
+ int idx;
+
+ cmd = scb->cmd;
+
+ /* Scatter-gather not used */
+ if( !cmd->use_sg ) {
+
+ page = virt_to_page(cmd->request_buffer);
+ offset = offset_in_page(cmd->request_buffer);
+
+ scb->dma_h_bulkdata = pci_map_page(adapter->dev,
+ page, offset,
+ cmd->request_bufflen,
+ scb->dma_direction);
+ scb->dma_type = MEGA_BULK_DATA;
+
+ /*
+ * We need to handle special 64-bit commands that need a
+ * minimum of 1 SG
+ */
+ if( adapter->has_64bit_addr ) {
+ scb->sgl64[0].address = scb->dma_h_bulkdata;
+ scb->sgl64[0].length = cmd->request_bufflen;
+ *buf = (u32)scb->sgl_dma_addr;
+ *len = (u32)cmd->request_bufflen;
+ return 1;
+ }
+ else {
+ *buf = (u32)scb->dma_h_bulkdata;
+ *len = (u32)cmd->request_bufflen;
+ }
+ return 0;
+ }
+
+ sgl = (struct scatterlist *)cmd->request_buffer;
+
+ /*
+ * Copy Scatter-Gather list info into controller structure.
+ *
+ * The number of sg elements returned must not exceed our limit
+ */
+ sgcnt = pci_map_sg(adapter->dev, sgl, cmd->use_sg,
+ scb->dma_direction);
+
+ scb->dma_type = MEGA_SGLIST;
+
+ if( sgcnt > adapter->sglen ) BUG();
+
+ for( idx = 0; idx < sgcnt; idx++, sgl++ ) {
+
+ if( adapter->has_64bit_addr ) {
+ scb->sgl64[idx].address = sg_dma_address(sgl);
+ scb->sgl64[idx].length = sg_dma_len(sgl);
+ }
+ else {
+ scb->sgl[idx].address = sg_dma_address(sgl);
+ scb->sgl[idx].length = sg_dma_len(sgl);
+ }
+ }
+
+ /* Reset pointer and length fields */
+ *buf = scb->sgl_dma_addr;
+
+ /*
+ * For passthru command, dataxferlen must be set, even for commands
+ * with a sg list
+ */
+ *len = (u32)cmd->request_bufflen;
+
+ /* Return count of SG requests */
+ return sgcnt;
+}
+
+
+/*
+ * mega_8_to_40ld()
+ *
+ * takes all info in AdapterInquiry structure and puts it into ProductInfo and
+ * Enquiry3 structures for later use
+ */
+static void
+mega_8_to_40ld(mraid_inquiry *inquiry, mega_inquiry3 *enquiry3,
+ mega_product_info *product_info)
+{
+ int i;
+
+ product_info->max_commands = inquiry->adapter_info.max_commands;
+ enquiry3->rebuild_rate = inquiry->adapter_info.rebuild_rate;
+ product_info->nchannels = inquiry->adapter_info.nchannels;
+
+ for (i = 0; i < 4; i++) {
+ product_info->fw_version[i] =
+ inquiry->adapter_info.fw_version[i];
+
+ product_info->bios_version[i] =
+ inquiry->adapter_info.bios_version[i];
+ }
+ enquiry3->cache_flush_interval =
+ inquiry->adapter_info.cache_flush_interval;
+
+ product_info->dram_size = inquiry->adapter_info.dram_size;
+
+ enquiry3->num_ldrv = inquiry->logdrv_info.num_ldrv;
+
+ for (i = 0; i < MAX_LOGICAL_DRIVES_8LD; i++) {
+ enquiry3->ldrv_size[i] = inquiry->logdrv_info.ldrv_size[i];
+ enquiry3->ldrv_prop[i] = inquiry->logdrv_info.ldrv_prop[i];
+ enquiry3->ldrv_state[i] = inquiry->logdrv_info.ldrv_state[i];
+ }
+
+ for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++)
+ enquiry3->pdrv_state[i] = inquiry->pdrv_info.pdrv_state[i];
+}
+
+static inline void
+mega_free_sgl(adapter_t *adapter)
+{
+ scb_t *scb;
+ int i;
+
+ for(i = 0; i < adapter->max_cmds; i++) {
+
+ scb = &adapter->scb_list[i];
+
+ if( scb->sgl64 ) {
+ pci_free_consistent(adapter->dev,
+ sizeof(mega_sgl64) * adapter->sglen,
+ scb->sgl64,
+ scb->sgl_dma_addr);
+
+ scb->sgl64 = NULL;
+ }
+
+ if( scb->pthru ) {
+ pci_free_consistent(adapter->dev, sizeof(mega_passthru),
+ scb->pthru, scb->pthru_dma_addr);
+
+ scb->pthru = NULL;
+ }
+
+ if( scb->epthru ) {
+ pci_free_consistent(adapter->dev,
+ sizeof(mega_ext_passthru),
+ scb->epthru, scb->epthru_dma_addr);
+
+ scb->epthru = NULL;
+ }
+
+ }
+}
+
+
+/*
+ * Get information about the card/driver
+ */
+const char *
+megaraid_info(struct Scsi_Host *host)
+{
+ static char buffer[512];
+ adapter_t *adapter;
+
+ adapter = (adapter_t *)host->hostdata;
+
+ sprintf (buffer,
+ "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns",
+ adapter->fw_version, adapter->product_info.max_commands,
+ adapter->host->max_id, adapter->host->max_channel,
+ adapter->host->max_lun);
+ return buffer;
+}
+
+/*
+ * Abort a previous SCSI request. Only commands on the pending list can be
+ * aborted. All the commands issued to the F/W must complete.
+ */
+static int
+megaraid_abort(Scsi_Cmnd *cmd)
+{
+ adapter_t *adapter;
+ int rval;
+
+ adapter = (adapter_t *)cmd->device->host->hostdata;
+
+ rval = megaraid_abort_and_reset(adapter, cmd, SCB_ABORT);
+
+ /*
+ * This is required here to complete any completed requests
+ * to be communicated over to the mid layer.
+ */
+ mega_rundoneq(adapter);
+
+ return rval;
+}
+
+
+static int
+megaraid_reset(Scsi_Cmnd *cmd)
+{
+ adapter_t *adapter;
+ megacmd_t mc;
+ int rval;
+
+ adapter = (adapter_t *)cmd->device->host->hostdata;
+
+#if MEGA_HAVE_CLUSTERING
+ mc.cmd = MEGA_CLUSTER_CMD;
+ mc.opcode = MEGA_RESET_RESERVATIONS;
+
+ spin_unlock_irq(&adapter->lock);
+ if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) != 0 ) {
+ printk(KERN_WARNING
+ "megaraid: reservation reset failed.\n");
+ }
+ else {
+ printk(KERN_INFO "megaraid: reservation reset.\n");
+ }
+ spin_lock_irq(&adapter->lock);
+#endif
+
+ rval = megaraid_abort_and_reset(adapter, cmd, SCB_RESET);
+
+ /*
+ * This is required here to complete any completed requests
+ * to be communicated over to the mid layer.
+ */
+ mega_rundoneq(adapter);
+
+ return rval;
+}
+
+
+
+/**
+ * megaraid_abort_and_reset()
+ * @adapter - megaraid soft state
+ * @cmd - scsi command to be aborted or reset
+ * @aor - abort or reset flag
+ *
+ * Try to locate the scsi command in the pending queue. If found and is not
+ * issued to the controller, abort/reset it. Otherwise return failure
+ */
+static int
+megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
+{
+ struct list_head *pos, *next;
+ scb_t *scb;
+
+ printk(KERN_WARNING "megaraid: %s-%lx cmd=%x <c=%d t=%d l=%d>\n",
+ (aor == SCB_ABORT)? "ABORTING":"RESET", cmd->serial_number,
+ cmd->cmnd[0], cmd->device->channel,
+ cmd->device->id, cmd->device->lun);
+
+ if(list_empty(&adapter->pending_list))
+ return FALSE;
+
+ list_for_each_safe(pos, next, &adapter->pending_list) {
+
+ scb = list_entry(pos, scb_t, list);
+
+ if (scb->cmd == cmd) { /* Found command */
+
+ scb->state |= aor;
+
+ /*
+ * Check if this command has firmare owenership. If
+ * yes, we cannot reset this command. Whenever, f/w
+ * completes this command, we will return appropriate
+ * status from ISR.
+ */
+ if( scb->state & SCB_ISSUED ) {
+
+ printk(KERN_WARNING
+ "megaraid: %s-%lx[%x], fw owner.\n",
+ (aor==SCB_ABORT) ? "ABORTING":"RESET",
+ cmd->serial_number, scb->idx);
+
+ return FALSE;
+ }
+ else {
+
+ /*
+ * Not yet issued! Remove from the pending
+ * list
+ */
+ printk(KERN_WARNING
+ "megaraid: %s-%lx[%x], driver owner.\n",
+ (aor==SCB_ABORT) ? "ABORTING":"RESET",
+ cmd->serial_number, scb->idx);
+
+ mega_free_scb(adapter, scb);
+
+ if( aor == SCB_ABORT ) {
+ cmd->result = (DID_ABORT << 16);
+ }
+ else {
+ cmd->result = (DID_RESET << 16);
+ }
+
+ list_add_tail(SCSI_LIST(cmd),
+ &adapter->completed_list);
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static inline int
+make_local_pdev(adapter_t *adapter, struct pci_dev **pdev)
+{
+ *pdev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
+
+ if( *pdev == NULL ) return -1;
+
+ memcpy(*pdev, adapter->dev, sizeof(struct pci_dev));
+
+ if( pci_set_dma_mask(*pdev, 0xffffffff) != 0 ) {
+ kfree(*pdev);
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void
+free_local_pdev(struct pci_dev *pdev)
+{
+ kfree(pdev);
+}
+
+/**
+ * mega_allocate_inquiry()
+ * @dma_handle - handle returned for dma address
+ * @pdev - handle to pci device
+ *
+ * allocates memory for inquiry structure
+ */
+static inline void *
+mega_allocate_inquiry(dma_addr_t *dma_handle, struct pci_dev *pdev)
+{
+ return pci_alloc_consistent(pdev, sizeof(mega_inquiry3), dma_handle);
+}
+
+
+static inline void
+mega_free_inquiry(void *inquiry, dma_addr_t dma_handle, struct pci_dev *pdev)
+{
+ pci_free_consistent(pdev, sizeof(mega_inquiry3), inquiry, dma_handle);
+}
+
+
+#ifdef CONFIG_PROC_FS
+/* Following code handles /proc fs */
+
+#define CREATE_READ_PROC(string, func) create_proc_read_entry(string, \
+ S_IRUSR | S_IFREG, \
+ controller_proc_dir_entry, \
+ func, adapter)
+
+/**
+ * mega_create_proc_entry()
+ * @index - index in soft state array
+ * @parent - parent node for this /proc entry
+ *
+ * Creates /proc entries for our controllers.
+ */
+static void
+mega_create_proc_entry(int index, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *controller_proc_dir_entry = NULL;
+ u8 string[64] = { 0 };
+ adapter_t *adapter = hba_soft_state[index];
+
+ sprintf(string, "hba%d", adapter->host->host_no);
+
+ controller_proc_dir_entry =
+ adapter->controller_proc_dir_entry = proc_mkdir(string, parent);
+
+ if(!controller_proc_dir_entry) {
+ printk(KERN_WARNING "\nmegaraid: proc_mkdir failed\n");
+ return;
+ }
+ adapter->proc_read = CREATE_READ_PROC("config", proc_read_config);
+ adapter->proc_stat = CREATE_READ_PROC("stat", proc_read_stat);
+ adapter->proc_mbox = CREATE_READ_PROC("mailbox", proc_read_mbox);
+#if MEGA_HAVE_ENH_PROC
+ adapter->proc_rr = CREATE_READ_PROC("rebuild-rate", proc_rebuild_rate);
+ adapter->proc_battery = CREATE_READ_PROC("battery-status",
+ proc_battery);
+
+ /*
+ * Display each physical drive on its channel
+ */
+ adapter->proc_pdrvstat[0] = CREATE_READ_PROC("diskdrives-ch0",
+ proc_pdrv_ch0);
+ adapter->proc_pdrvstat[1] = CREATE_READ_PROC("diskdrives-ch1",
+ proc_pdrv_ch1);
+ adapter->proc_pdrvstat[2] = CREATE_READ_PROC("diskdrives-ch2",
+ proc_pdrv_ch2);
+ adapter->proc_pdrvstat[3] = CREATE_READ_PROC("diskdrives-ch3",
+ proc_pdrv_ch3);
+
+ /*
+ * Display a set of up to 10 logical drive through each of following
+ * /proc entries
+ */
+ adapter->proc_rdrvstat[0] = CREATE_READ_PROC("raiddrives-0-9",
+ proc_rdrv_10);
+ adapter->proc_rdrvstat[1] = CREATE_READ_PROC("raiddrives-10-19",
+ proc_rdrv_20);
+ adapter->proc_rdrvstat[2] = CREATE_READ_PROC("raiddrives-20-29",
+ proc_rdrv_30);
+ adapter->proc_rdrvstat[3] = CREATE_READ_PROC("raiddrives-30-39",
+ proc_rdrv_40);
+#endif
+}
+
+
+/**
+ * proc_read_config()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display configuration information about the controller.
+ */
+static int
+proc_read_config(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+
+ adapter_t *adapter = (adapter_t *)data;
+ int len = 0;
+
+ len += sprintf(page+len, "%s", MEGARAID_VERSION);
+
+ if(adapter->product_info.product_name[0])
+ len += sprintf(page+len, "%s\n",
+ adapter->product_info.product_name);
+
+ len += sprintf(page+len, "Controller Type: ");
+
+ if( adapter->flag & BOARD_MEMMAP ) {
+ len += sprintf(page+len,
+ "438/466/467/471/493/518/520/531/532\n");
+ }
+ else {
+ len += sprintf(page+len,
+ "418/428/434\n");
+ }
+
+ if(adapter->flag & BOARD_40LD) {
+ len += sprintf(page+len,
+ "Controller Supports 40 Logical Drives\n");
+ }
+
+ if(adapter->flag & BOARD_64BIT) {
+ len += sprintf(page+len,
+ "Controller capable of 64-bit memory addressing\n");
+ }
+ if( adapter->has_64bit_addr ) {
+ len += sprintf(page+len,
+ "Controller using 64-bit memory addressing\n");
+ }
+ else {
+ len += sprintf(page+len,
+ "Controller is not using 64-bit memory addressing\n");
+ }
+
+ len += sprintf(page+len, "Base = %08lx, Irq = %d, ", adapter->base,
+ adapter->host->irq);
+
+ len += sprintf(page+len, "Logical Drives = %d, Channels = %d\n",
+ adapter->numldrv, adapter->product_info.nchannels);
+
+ len += sprintf(page+len, "Version =%s:%s, DRAM = %dMb\n",
+ adapter->fw_version, adapter->bios_version,
+ adapter->product_info.dram_size);
+
+ len += sprintf(page+len,
+ "Controller Queue Depth = %d, Driver Queue Depth = %d\n",
+ adapter->product_info.max_commands, adapter->max_cmds);
+
+ len += sprintf(page+len, "support_ext_cdb = %d\n",
+ adapter->support_ext_cdb);
+ len += sprintf(page+len, "support_random_del = %d\n",
+ adapter->support_random_del);
+ len += sprintf(page+len, "boot_ldrv_enabled = %d\n",
+ adapter->boot_ldrv_enabled);
+ len += sprintf(page+len, "boot_ldrv = %d\n",
+ adapter->boot_ldrv);
+ len += sprintf(page+len, "boot_pdrv_enabled = %d\n",
+ adapter->boot_pdrv_enabled);
+ len += sprintf(page+len, "boot_pdrv_ch = %d\n",
+ adapter->boot_pdrv_ch);
+ len += sprintf(page+len, "boot_pdrv_tgt = %d\n",
+ adapter->boot_pdrv_tgt);
+ len += sprintf(page+len, "quiescent = %d\n",
+ atomic_read(&adapter->quiescent));
+ len += sprintf(page+len, "has_cluster = %d\n",
+ adapter->has_cluster);
+
+ len += sprintf(page+len, "\nModule Parameters:\n");
+ len += sprintf(page+len, "max_cmd_per_lun = %d\n",
+ max_cmd_per_lun);
+ len += sprintf(page+len, "max_sectors_per_io = %d\n",
+ max_sectors_per_io);
+
+ *eof = 1;
+
+ return len;
+}
+
+
+
+/**
+ * proc_read_stat()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Diaplay statistical information about the I/O activity.
+ */
+static int
+proc_read_stat(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter;
+ int len;
+ int i;
+
+ i = 0; /* avoid compilation warnings */
+ len = 0;
+ adapter = (adapter_t *)data;
+
+ len = sprintf(page, "Statistical Information for this controller\n");
+ len += sprintf(page+len, "pend_cmds = %d\n",
+ atomic_read(&adapter->pend_cmds));
+#if MEGA_HAVE_STATS
+ for(i = 0; i < adapter->numldrv; i++) {
+ len += sprintf(page+len, "Logical Drive %d:\n", i);
+
+ len += sprintf(page+len,
+ "\tReads Issued = %lu, Writes Issued = %lu\n",
+ adapter->nreads[i], adapter->nwrites[i]);
+
+ len += sprintf(page+len,
+ "\tSectors Read = %lu, Sectors Written = %lu\n",
+ adapter->nreadblocks[i], adapter->nwriteblocks[i]);
+
+ len += sprintf(page+len,
+ "\tRead errors = %lu, Write errors = %lu\n\n",
+ adapter->rd_errors[i], adapter->wr_errors[i]);
+ }
+#else
+ len += sprintf(page+len,
+ "IO and error counters not compiled in driver.\n");
+#endif
+
+ *eof = 1;
+
+ return len;
+}
+
+
+/**
+ * proc_read_mbox()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display mailbox information for the last command issued. This information
+ * is good for debugging.
+ */
+static int
+proc_read_mbox(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+
+ adapter_t *adapter = (adapter_t *)data;
+ volatile mbox_t *mbox = adapter->mbox;
+ int len = 0;
+
+ len = sprintf(page, "Contents of Mail Box Structure\n");
+ len += sprintf(page+len, " Fw Command = 0x%02x\n",
+ mbox->m_out.cmd);
+ len += sprintf(page+len, " Cmd Sequence = 0x%02x\n",
+ mbox->m_out.cmdid);
+ len += sprintf(page+len, " No of Sectors= %04d\n",
+ mbox->m_out.numsectors);
+ len += sprintf(page+len, " LBA = 0x%02x\n",
+ mbox->m_out.lba);
+ len += sprintf(page+len, " DTA = 0x%08x\n",
+ mbox->m_out.xferaddr);
+ len += sprintf(page+len, " Logical Drive= 0x%02x\n",
+ mbox->m_out.logdrv);
+ len += sprintf(page+len, " No of SG Elmt= 0x%02x\n",
+ mbox->m_out.numsgelements);
+ len += sprintf(page+len, " Busy = %01x\n",
+ mbox->m_in.busy);
+ len += sprintf(page+len, " Status = 0x%02x\n",
+ mbox->m_in.status);
+
+ *eof = 1;
+
+ return len;
+}
+
+
+/**
+ * proc_rebuild_rate()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display current rebuild rate
+ */
+static int
+proc_rebuild_rate(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+ dma_addr_t dma_handle;
+ caddr_t inquiry;
+ struct pci_dev *pdev;
+ int len = 0;
+
+ if( make_local_pdev(adapter, &pdev) != 0 ) {
+ *eof = 1;
+ return len;
+ }
+
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
+ free_local_pdev(pdev);
+ *eof = 1;
+ return len;
+ }
+
+ if( mega_adapinq(adapter, dma_handle) != 0 ) {
+
+ len = sprintf(page, "Adapter inquiry failed.\n");
+
+ printk(KERN_WARNING "megaraid: inquiry failed.\n");
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ free_local_pdev(pdev);
+
+ *eof = 1;
+
+ return len;
+ }
+
+ if( adapter->flag & BOARD_40LD ) {
+ len = sprintf(page, "Rebuild Rate: [%d%%]\n",
+ ((mega_inquiry3 *)inquiry)->rebuild_rate);
+ }
+ else {
+ len = sprintf(page, "Rebuild Rate: [%d%%]\n",
+ ((mraid_ext_inquiry *)
+ inquiry)->raid_inq.adapter_info.rebuild_rate);
+ }
+
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ free_local_pdev(pdev);
+
+ *eof = 1;
+
+ return len;
+}
+
+
+/**
+ * proc_battery()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display information about the battery module on the controller.
+ */
+static int
+proc_battery(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+ dma_addr_t dma_handle;
+ caddr_t inquiry;
+ struct pci_dev *pdev;
+ u8 battery_status = 0;
+ char str[256];
+ int len = 0;
+
+ if( make_local_pdev(adapter, &pdev) != 0 ) {
+ *eof = 1;
+ return len;
+ }
+
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
+ free_local_pdev(pdev);
+ *eof = 1;
+ return len;
+ }
+
+ if( mega_adapinq(adapter, dma_handle) != 0 ) {
+
+ len = sprintf(page, "Adapter inquiry failed.\n");
+
+ printk(KERN_WARNING "megaraid: inquiry failed.\n");
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ free_local_pdev(pdev);
+
+ *eof = 1;
+
+ return len;
+ }
+
+ if( adapter->flag & BOARD_40LD ) {
+ battery_status = ((mega_inquiry3 *)inquiry)->battery_status;
+ }
+ else {
+ battery_status = ((mraid_ext_inquiry *)inquiry)->
+ raid_inq.adapter_info.battery_status;
+ }
+
+ /*
+ * Decode the battery status
+ */
+ sprintf(str, "Battery Status:[%d]", battery_status);
+
+ if(battery_status == MEGA_BATT_CHARGE_DONE)
+ strcat(str, " Charge Done");
+
+ if(battery_status & MEGA_BATT_MODULE_MISSING)
+ strcat(str, " Module Missing");
+
+ if(battery_status & MEGA_BATT_LOW_VOLTAGE)
+ strcat(str, " Low Voltage");
+
+ if(battery_status & MEGA_BATT_TEMP_HIGH)
+ strcat(str, " Temperature High");
+
+ if(battery_status & MEGA_BATT_PACK_MISSING)
+ strcat(str, " Pack Missing");
+
+ if(battery_status & MEGA_BATT_CHARGE_INPROG)
+ strcat(str, " Charge In-progress");
+
+ if(battery_status & MEGA_BATT_CHARGE_FAIL)
+ strcat(str, " Charge Fail");
+
+ if(battery_status & MEGA_BATT_CYCLES_EXCEEDED)
+ strcat(str, " Cycles Exceeded");
+
+ len = sprintf(page, "%s\n", str);
+
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ free_local_pdev(pdev);
+
+ *eof = 1;
+
+ return len;
+}
+
+
+/**
+ * proc_pdrv_ch0()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display information about the physical drives on physical channel 0.
+ */
+static int
+proc_pdrv_ch0(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_pdrv(adapter, page, 0));
+}
+
+
+/**
+ * proc_pdrv_ch1()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display information about the physical drives on physical channel 1.
+ */
+static int
+proc_pdrv_ch1(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_pdrv(adapter, page, 1));
+}
+
+
+/**
+ * proc_pdrv_ch2()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display information about the physical drives on physical channel 2.
+ */
+static int
+proc_pdrv_ch2(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_pdrv(adapter, page, 2));
+}
+
+
+/**
+ * proc_pdrv_ch3()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display information about the physical drives on physical channel 3.
+ */
+static int
+proc_pdrv_ch3(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_pdrv(adapter, page, 3));
+}
+
+
+/**
+ * proc_pdrv()
+ * @page - buffer to write the data in
+ * @adapter - pointer to our soft state
+ *
+ * Display information about the physical drives.
+ */
+static int
+proc_pdrv(adapter_t *adapter, char *page, int channel)
+{
+ dma_addr_t dma_handle;
+ char *scsi_inq;
+ dma_addr_t scsi_inq_dma_handle;
+ caddr_t inquiry;
+ struct pci_dev *pdev;
+ u8 *pdrv_state;
+ u8 state;
+ int tgt;
+ int max_channels;
+ int len = 0;
+ char str[80];
+ int i;
+
+ if( make_local_pdev(adapter, &pdev) != 0 ) {
+ return len;
+ }
+
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
+ goto free_pdev;
+ }
+
+ if( mega_adapinq(adapter, dma_handle) != 0 ) {
+ len = sprintf(page, "Adapter inquiry failed.\n");
+
+ printk(KERN_WARNING "megaraid: inquiry failed.\n");
+
+ goto free_inquiry;
+ }
+
+
+ scsi_inq = pci_alloc_consistent(pdev, 256, &scsi_inq_dma_handle);
+
+ if( scsi_inq == NULL ) {
+ len = sprintf(page, "memory not available for scsi inq.\n");
+
+ goto free_inquiry;
+ }
+
+ if( adapter->flag & BOARD_40LD ) {
+ pdrv_state = ((mega_inquiry3 *)inquiry)->pdrv_state;
+ }
+ else {
+ pdrv_state = ((mraid_ext_inquiry *)inquiry)->
+ raid_inq.pdrv_info.pdrv_state;
+ }
+
+ max_channels = adapter->product_info.nchannels;
+
+ if( channel >= max_channels ) {
+ goto free_pci;
+ }
+
+ for( tgt = 0; tgt <= MAX_TARGET; tgt++ ) {
+
+ i = channel*16 + tgt;
+
+ state = *(pdrv_state + i);
+
+ switch( state & 0x0F ) {
+
+ case PDRV_ONLINE:
+ sprintf(str,
+ "Channel:%2d Id:%2d State: Online",
+ channel, tgt);
+ break;
+
+ case PDRV_FAILED:
+ sprintf(str,
+ "Channel:%2d Id:%2d State: Failed",
+ channel, tgt);
+ break;
+
+ case PDRV_RBLD:
+ sprintf(str,
+ "Channel:%2d Id:%2d State: Rebuild",
+ channel, tgt);
+ break;
+
+ case PDRV_HOTSPARE:
+ sprintf(str,
+ "Channel:%2d Id:%2d State: Hot spare",
+ channel, tgt);
+ break;
+
+ default:
+ sprintf(str,
+ "Channel:%2d Id:%2d State: Un-configured",
+ channel, tgt);
+ break;
+
+ }
+
+ /*
+ * This interface displays inquiries for disk drives
+ * only. Inquries for logical drives and non-disk
+ * devices are available through /proc/scsi/scsi
+ */
+ memset(scsi_inq, 0, 256);
+ if( mega_internal_dev_inquiry(adapter, channel, tgt,
+ scsi_inq_dma_handle) ||
+ (scsi_inq[0] & 0x1F) != TYPE_DISK ) {
+ continue;
+ }
+
+ /*
+ * Check for overflow. We print less than 240
+ * characters for inquiry
+ */
+ if( (len + 240) >= PAGE_SIZE ) break;
+
+ len += sprintf(page+len, "%s.\n", str);
+
+ len += mega_print_inquiry(page+len, scsi_inq);
+ }
+
+free_pci:
+ pci_free_consistent(pdev, 256, scsi_inq, scsi_inq_dma_handle);
+free_inquiry:
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+free_pdev:
+ free_local_pdev(pdev);
+
+ return len;
+}
+
+
+/*
+ * Display scsi inquiry
+ */
+static int
+mega_print_inquiry(char *page, char *scsi_inq)
+{
+ int len = 0;
+ int i;
+
+ len = sprintf(page, " Vendor: ");
+ for( i = 8; i < 16; i++ ) {
+ len += sprintf(page+len, "%c", scsi_inq[i]);
+ }
+
+ len += sprintf(page+len, " Model: ");
+
+ for( i = 16; i < 32; i++ ) {
+ len += sprintf(page+len, "%c", scsi_inq[i]);
+ }
+
+ len += sprintf(page+len, " Rev: ");
+
+ for( i = 32; i < 36; i++ ) {
+ len += sprintf(page+len, "%c", scsi_inq[i]);
+ }
+
+ len += sprintf(page+len, "\n");
+
+ i = scsi_inq[0] & 0x1f;
+
+ len += sprintf(page+len, " Type: %s ",
+ i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] :
+ "Unknown ");
+
+ len += sprintf(page+len,
+ " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07);
+
+ if( (scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1 )
+ len += sprintf(page+len, " CCS\n");
+ else
+ len += sprintf(page+len, "\n");
+
+ return len;
+}
+
+
+/**
+ * proc_rdrv_10()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_rdrv_10(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_rdrv(adapter, page, 0, 9));
+}
+
+
+/**
+ * proc_rdrv_20()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_rdrv_20(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_rdrv(adapter, page, 10, 19));
+}
+
+
+/**
+ * proc_rdrv_30()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_rdrv_30(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_rdrv(adapter, page, 20, 29));
+}
+
+
+/**
+ * proc_rdrv_40()
+ * @page - buffer to write the data in
+ * @start - where the actual data has been written in page
+ * @offset - same meaning as the read system call
+ * @count - same meaning as the read system call
+ * @eof - set if no more data needs to be returned
+ * @data - pointer to our soft state
+ *
+ * Display real time information about the logical drives 0 through 9.
+ */
+static int
+proc_rdrv_40(char *page, char **start, off_t offset, int count, int *eof,
+ void *data)
+{
+ adapter_t *adapter = (adapter_t *)data;
+
+ *eof = 1;
+
+ return (proc_rdrv(adapter, page, 30, 39));
+}
+
+
+/**
+ * proc_rdrv()
+ * @page - buffer to write the data in
+ * @adapter - pointer to our soft state
+ * @start - starting logical drive to display
+ * @end - ending logical drive to display
+ *
+ * We do not print the inquiry information since its already available through
+ * /proc/scsi/scsi interface
+ */
+static int
+proc_rdrv(adapter_t *adapter, char *page, int start, int end )
+{
+ dma_addr_t dma_handle;
+ logdrv_param *lparam;
+ megacmd_t mc;
+ char *disk_array;
+ dma_addr_t disk_array_dma_handle;
+ caddr_t inquiry;
+ struct pci_dev *pdev;
+ u8 *rdrv_state;
+ int num_ldrv;
+ u32 array_sz;
+ int len = 0;
+ int i;
+
+ if( make_local_pdev(adapter, &pdev) != 0 ) {
+ return len;
+ }
+
+ if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) {
+ free_local_pdev(pdev);
+ return len;
+ }
+
+ if( mega_adapinq(adapter, dma_handle) != 0 ) {
+
+ len = sprintf(page, "Adapter inquiry failed.\n");
+
+ printk(KERN_WARNING "megaraid: inquiry failed.\n");
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ free_local_pdev(pdev);
+
+ return len;
+ }
+
+ memset(&mc, 0, sizeof(megacmd_t));
+
+ if( adapter->flag & BOARD_40LD ) {
+ array_sz = sizeof(disk_array_40ld);
+
+ rdrv_state = ((mega_inquiry3 *)inquiry)->ldrv_state;
+
+ num_ldrv = ((mega_inquiry3 *)inquiry)->num_ldrv;
+ }
+ else {
+ array_sz = sizeof(disk_array_8ld);
+
+ rdrv_state = ((mraid_ext_inquiry *)inquiry)->
+ raid_inq.logdrv_info.ldrv_state;
+
+ num_ldrv = ((mraid_ext_inquiry *)inquiry)->
+ raid_inq.logdrv_info.num_ldrv;
+ }
+
+ disk_array = pci_alloc_consistent(pdev, array_sz,
+ &disk_array_dma_handle);
+
+ if( disk_array == NULL ) {
+ len = sprintf(page, "memory not available.\n");
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ free_local_pdev(pdev);
+
+ return len;
+ }
+
+ mc.xferaddr = (u32)disk_array_dma_handle;
+
+ if( adapter->flag & BOARD_40LD ) {
+ mc.cmd = FC_NEW_CONFIG;
+ mc.opcode = OP_DCMD_READ_CONFIG;
+
+ if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) ) {
+
+ len = sprintf(page, "40LD read config failed.\n");
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ pci_free_consistent(pdev, array_sz, disk_array,
+ disk_array_dma_handle);
+
+ free_local_pdev(pdev);
+
+ return len;
+ }
+
+ }
+ else {
+ mc.cmd = NEW_READ_CONFIG_8LD;
+
+ if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) ) {
+
+ mc.cmd = READ_CONFIG_8LD;
+
+ if( mega_internal_command(adapter, LOCK_INT, &mc,
+ NULL) ){
+
+ len = sprintf(page,
+ "8LD read config failed.\n");
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ pci_free_consistent(pdev, array_sz,
+ disk_array,
+ disk_array_dma_handle);
+
+ free_local_pdev(pdev);
+
+ return len;
+ }
+ }
+ }
+
+ for( i = start; i < ( (end+1 < num_ldrv) ? end+1 : num_ldrv ); i++ ) {
+
+ if( adapter->flag & BOARD_40LD ) {
+ lparam =
+ &((disk_array_40ld *)disk_array)->ldrv[i].lparam;
+ }
+ else {
+ lparam =
+ &((disk_array_8ld *)disk_array)->ldrv[i].lparam;
+ }
+
+ /*
+ * Check for overflow. We print less than 240 characters for
+ * information about each logical drive.
+ */
+ if( (len + 240) >= PAGE_SIZE ) break;
+
+ len += sprintf(page+len, "Logical drive:%2d:, ", i);
+
+ switch( rdrv_state[i] & 0x0F ) {
+ case RDRV_OFFLINE:
+ len += sprintf(page+len, "state: offline");
+ break;
+
+ case RDRV_DEGRADED:
+ len += sprintf(page+len, "state: degraded");
+ break;
+
+ case RDRV_OPTIMAL:
+ len += sprintf(page+len, "state: optimal");
+ break;
+
+ case RDRV_DELETED:
+ len += sprintf(page+len, "state: deleted");
+ break;
+
+ default:
+ len += sprintf(page+len, "state: unknown");
+ break;
+ }
+
+ /*
+ * Check if check consistency or initialization is going on
+ * for this logical drive.
+ */
+ if( (rdrv_state[i] & 0xF0) == 0x20 ) {
+ len += sprintf(page+len,
+ ", check-consistency in progress");
+ }
+ else if( (rdrv_state[i] & 0xF0) == 0x10 ) {
+ len += sprintf(page+len,
+ ", initialization in progress");
+ }
+
+ len += sprintf(page+len, "\n");
+
+ len += sprintf(page+len, "Span depth:%3d, ",
+ lparam->span_depth);
+
+ len += sprintf(page+len, "RAID level:%3d, ",
+ lparam->level);
+
+ len += sprintf(page+len, "Stripe size:%3d, ",
+ lparam->stripe_sz ? lparam->stripe_sz/2: 128);
+
+ len += sprintf(page+len, "Row size:%3d\n",
+ lparam->row_size);
+
+
+ len += sprintf(page+len, "Read Policy: ");
+
+ switch(lparam->read_ahead) {
+
+ case NO_READ_AHEAD:
+ len += sprintf(page+len, "No read ahead, ");
+ break;
+
+ case READ_AHEAD:
+ len += sprintf(page+len, "Read ahead, ");
+ break;
+
+ case ADAP_READ_AHEAD:
+ len += sprintf(page+len, "Adaptive, ");
+ break;
+
+ }
+
+ len += sprintf(page+len, "Write Policy: ");
+
+ switch(lparam->write_mode) {
+
+ case WRMODE_WRITE_THRU:
+ len += sprintf(page+len, "Write thru, ");
+ break;
+
+ case WRMODE_WRITE_BACK:
+ len += sprintf(page+len, "Write back, ");
+ break;
+ }
+
+ len += sprintf(page+len, "Cache Policy: ");
+
+ switch(lparam->direct_io) {
+
+ case CACHED_IO:
+ len += sprintf(page+len, "Cached IO\n\n");
+ break;
+
+ case DIRECT_IO:
+ len += sprintf(page+len, "Direct IO\n\n");
+ break;
+ }
+ }
+
+ mega_free_inquiry(inquiry, dma_handle, pdev);
+
+ pci_free_consistent(pdev, array_sz, disk_array,
+ disk_array_dma_handle);
+
+ free_local_pdev(pdev);
+
+ return len;
+}
+
+#endif
+
+
+/**
+ * megaraid_biosparam()
+ *
+ * Return the disk geometry for a particular disk
+ */
+static int
+megaraid_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ adapter_t *adapter;
+ unsigned char *bh;
+ int heads;
+ int sectors;
+ int cylinders;
+ int rval;
+
+ /* Get pointer to host config structure */
+ adapter = (adapter_t *)sdev->host->hostdata;
+
+ if (IS_RAID_CH(adapter, sdev->channel)) {
+ /* Default heads (64) & sectors (32) */
+ heads = 64;
+ sectors = 32;
+ cylinders = (ulong)capacity / (heads * sectors);
+
+ /*
+ * Handle extended translation size for logical drives
+ * > 1Gb
+ */
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = (ulong)capacity / (heads * sectors);
+ }
+
+ /* return result */
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ }
+ else {
+ bh = scsi_bios_ptable(bdev);
+
+ if( bh ) {
+ rval = scsi_partsize(bh, capacity,
+ &geom[2], &geom[0], &geom[1]);
+ kfree(bh);
+ if( rval != -1 )
+ return rval;
+ }
+
+ printk(KERN_INFO
+ "megaraid: invalid partition on this disk on channel %d\n",
+ sdev->channel);
+
+ /* Default heads (64) & sectors (32) */
+ heads = 64;
+ sectors = 32;
+ cylinders = (ulong)capacity / (heads * sectors);
+
+ /* Handle extended translation size for logical drives > 1Gb */
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = (ulong)capacity / (heads * sectors);
+ }
+
+ /* return result */
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ }
+
+ return 0;
+}
+
+/**
+ * mega_init_scb()
+ * @adapter - pointer to our soft state
+ *
+ * Allocate memory for the various pointers in the scb structures:
+ * scatter-gather list pointer, passthru and extended passthru structure
+ * pointers.
+ */
+static int
+mega_init_scb(adapter_t *adapter)
+{
+ scb_t *scb;
+ int i;
+
+ for( i = 0; i < adapter->max_cmds; i++ ) {
+
+ scb = &adapter->scb_list[i];
+
+ scb->sgl64 = NULL;
+ scb->sgl = NULL;
+ scb->pthru = NULL;
+ scb->epthru = NULL;
+ }
+
+ for( i = 0; i < adapter->max_cmds; i++ ) {
+
+ scb = &adapter->scb_list[i];
+
+ scb->idx = i;
+
+ scb->sgl64 = pci_alloc_consistent(adapter->dev,
+ sizeof(mega_sgl64) * adapter->sglen,
+ &scb->sgl_dma_addr);
+
+ scb->sgl = (mega_sglist *)scb->sgl64;
+
+ if( !scb->sgl ) {
+ printk(KERN_WARNING "RAID: Can't allocate sglist.\n");
+ mega_free_sgl(adapter);
+ return -1;
+ }
+
+ scb->pthru = pci_alloc_consistent(adapter->dev,
+ sizeof(mega_passthru),
+ &scb->pthru_dma_addr);
+
+ if( !scb->pthru ) {
+ printk(KERN_WARNING "RAID: Can't allocate passthru.\n");
+ mega_free_sgl(adapter);
+ return -1;
+ }
+
+ scb->epthru = pci_alloc_consistent(adapter->dev,
+ sizeof(mega_ext_passthru),
+ &scb->epthru_dma_addr);
+
+ if( !scb->epthru ) {
+ printk(KERN_WARNING
+ "Can't allocate extended passthru.\n");
+ mega_free_sgl(adapter);
+ return -1;
+ }
+
+
+ scb->dma_type = MEGA_DMA_TYPE_NONE;
+
+ /*
+ * Link to free list
+ * lock not required since we are loading the driver, so no
+ * commands possible right now.
+ */
+ scb->state = SCB_FREE;
+ scb->cmd = NULL;
+ list_add(&scb->list, &adapter->free_list);
+ }
+
+ return 0;
+}
+
+
+/**
+ * megadev_open()
+ * @inode - unused
+ * @filep - unused
+ *
+ * Routines for the character/ioctl interface to the driver. Find out if this
+ * is a valid open. If yes, increment the module use count so that it cannot
+ * be unloaded.
+ */
+static int
+megadev_open (struct inode *inode, struct file *filep)
+{
+ /*
+ * Only allow superuser to access private ioctl interface
+ */
+ if( !capable(CAP_SYS_ADMIN) ) return -EACCES;
+
+ return 0;
+}
+
+
+/**
+ * megadev_ioctl()
+ * @inode - Our device inode
+ * @filep - unused
+ * @cmd - ioctl command
+ * @arg - user buffer
+ *
+ * ioctl entry point for our private ioctl interface. We move the data in from
+ * the user space, prepare the command (if necessary, convert the old MIMD
+ * ioctl to new ioctl command), and issue a synchronous command to the
+ * controller.
+ */
+static int
+megadev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ adapter_t *adapter;
+ nitioctl_t uioc;
+ int adapno;
+ int rval;
+ mega_passthru __user *upthru; /* user address for passthru */
+ mega_passthru *pthru; /* copy user passthru here */
+ dma_addr_t pthru_dma_hndl;
+ void *data = NULL; /* data to be transferred */
+ dma_addr_t data_dma_hndl; /* dma handle for data xfer area */
+ megacmd_t mc;
+ megastat_t __user *ustats;
+ int num_ldrv;
+ u32 uxferaddr = 0;
+ struct pci_dev *pdev;
+
+ ustats = NULL; /* avoid compilation warnings */
+ num_ldrv = 0;
+
+ /*
+ * Make sure only USCSICMD are issued through this interface.
+ * MIMD application would still fire different command.
+ */
+ if( (_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD) ) {
+ return -EINVAL;
+ }
+
+ /*
+ * Check and convert a possible MIMD command to NIT command.
+ * mega_m_to_n() copies the data from the user space, so we do not
+ * have to do it here.
+ * NOTE: We will need some user address to copyout the data, therefore
+ * the inteface layer will also provide us with the required user
+ * addresses.
+ */
+ memset(&uioc, 0, sizeof(nitioctl_t));
+ if( (rval = mega_m_to_n( (void __user *)arg, &uioc)) != 0 )
+ return rval;
+
+
+ switch( uioc.opcode ) {
+
+ case GET_DRIVER_VER:
+ if( put_user(driver_ver, (u32 __user *)uioc.uioc_uaddr) )
+ return (-EFAULT);
+
+ break;
+
+ case GET_N_ADAP:
+ if( put_user(hba_count, (u32 __user *)uioc.uioc_uaddr) )
+ return (-EFAULT);
+
+ /*
+ * Shucks. MIMD interface returns a positive value for number
+ * of adapters. TODO: Change it to return 0 when there is no
+ * applicatio using mimd interface.
+ */
+ return hba_count;
+
+ case GET_ADAP_INFO:
+
+ /*
+ * Which adapter
+ */
+ if( (adapno = GETADAP(uioc.adapno)) >= hba_count )
+ return (-ENODEV);
+
+ if( copy_to_user(uioc.uioc_uaddr, mcontroller+adapno,
+ sizeof(struct mcontroller)) )
+ return (-EFAULT);
+ break;
+
+#if MEGA_HAVE_STATS
+
+ case GET_STATS:
+ /*
+ * Which adapter
+ */
+ if( (adapno = GETADAP(uioc.adapno)) >= hba_count )
+ return (-ENODEV);
+
+ adapter = hba_soft_state[adapno];
+
+ ustats = uioc.uioc_uaddr;
+
+ if( copy_from_user(&num_ldrv, &ustats->num_ldrv, sizeof(int)) )
+ return (-EFAULT);
+
+ /*
+ * Check for the validity of the logical drive number
+ */
+ if( num_ldrv >= MAX_LOGICAL_DRIVES_40LD ) return -EINVAL;
+
+ if( copy_to_user(ustats->nreads, adapter->nreads,
+ num_ldrv*sizeof(u32)) )
+ return -EFAULT;
+
+ if( copy_to_user(ustats->nreadblocks, adapter->nreadblocks,
+ num_ldrv*sizeof(u32)) )
+ return -EFAULT;
+
+ if( copy_to_user(ustats->nwrites, adapter->nwrites,
+ num_ldrv*sizeof(u32)) )
+ return -EFAULT;
+
+ if( copy_to_user(ustats->nwriteblocks, adapter->nwriteblocks,
+ num_ldrv*sizeof(u32)) )
+ return -EFAULT;
+
+ if( copy_to_user(ustats->rd_errors, adapter->rd_errors,
+ num_ldrv*sizeof(u32)) )
+ return -EFAULT;
+
+ if( copy_to_user(ustats->wr_errors, adapter->wr_errors,
+ num_ldrv*sizeof(u32)) )
+ return -EFAULT;
+
+ return 0;
+
+#endif
+ case MBOX_CMD:
+
+ /*
+ * Which adapter
+ */
+ if( (adapno = GETADAP(uioc.adapno)) >= hba_count )
+ return (-ENODEV);
+
+ adapter = hba_soft_state[adapno];
+
+ /*
+ * Deletion of logical drive is a special case. The adapter
+ * should be quiescent before this command is issued.
+ */
+ if( uioc.uioc_rmbox[0] == FC_DEL_LOGDRV &&
+ uioc.uioc_rmbox[2] == OP_DEL_LOGDRV ) {
+
+ /*
+ * Do we support this feature
+ */
+ if( !adapter->support_random_del ) {
+ printk(KERN_WARNING "megaraid: logdrv ");
+ printk("delete on non-supporting F/W.\n");
+
+ return (-EINVAL);
+ }
+
+ rval = mega_del_logdrv( adapter, uioc.uioc_rmbox[3] );
+
+ if( rval == 0 ) {
+ memset(&mc, 0, sizeof(megacmd_t));
+
+ mc.status = rval;
+
+ rval = mega_n_to_m((void __user *)arg, &mc);
+ }
+
+ return rval;
+ }
+ /*
+ * This interface only support the regular passthru commands.
+ * Reject extended passthru and 64-bit passthru
+ */
+ if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU64 ||
+ uioc.uioc_rmbox[0] == MEGA_MBOXCMD_EXTPTHRU ) {
+
+ printk(KERN_WARNING "megaraid: rejected passthru.\n");
+
+ return (-EINVAL);
+ }
+
+ /*
+ * For all internal commands, the buffer must be allocated in
+ * <4GB address range
+ */
+ if( make_local_pdev(adapter, &pdev) != 0 )
+ return -EIO;
+
+ /* Is it a passthru command or a DCMD */
+ if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU ) {
+ /* Passthru commands */
+
+ pthru = pci_alloc_consistent(pdev,
+ sizeof(mega_passthru),
+ &pthru_dma_hndl);
+
+ if( pthru == NULL ) {
+ free_local_pdev(pdev);
+ return (-ENOMEM);
+ }
+
+ /*
+ * The user passthru structure
+ */
+ upthru = (mega_passthru __user *)MBOX(uioc)->xferaddr;
+
+ /*
+ * Copy in the user passthru here.
+ */
+ if( copy_from_user(pthru, upthru,
+ sizeof(mega_passthru)) ) {
+
+ pci_free_consistent(pdev,
+ sizeof(mega_passthru), pthru,
+ pthru_dma_hndl);
+
+ free_local_pdev(pdev);
+
+ return (-EFAULT);
+ }
+
+ /*
+ * Is there a data transfer
+ */
+ if( pthru->dataxferlen ) {
+ data = pci_alloc_consistent(pdev,
+ pthru->dataxferlen,
+ &data_dma_hndl);
+
+ if( data == NULL ) {
+ pci_free_consistent(pdev,
+ sizeof(mega_passthru),
+ pthru,
+ pthru_dma_hndl);
+
+ free_local_pdev(pdev);
+
+ return (-ENOMEM);
+ }
+
+ /*
+ * Save the user address and point the kernel
+ * address at just allocated memory
+ */
+ uxferaddr = pthru->dataxferaddr;
+ pthru->dataxferaddr = data_dma_hndl;
+ }
+
+
+ /*
+ * Is data coming down-stream
+ */
+ if( pthru->dataxferlen && (uioc.flags & UIOC_WR) ) {
+ /*
+ * Get the user data
+ */
+ if( copy_from_user(data, (char __user *)uxferaddr,
+ pthru->dataxferlen) ) {
+ rval = (-EFAULT);
+ goto freemem_and_return;
+ }
+ }
+
+ memset(&mc, 0, sizeof(megacmd_t));
+
+ mc.cmd = MEGA_MBOXCMD_PASSTHRU;
+ mc.xferaddr = (u32)pthru_dma_hndl;
+
+ /*
+ * Issue the command
+ */
+ mega_internal_command(adapter, LOCK_INT, &mc, pthru);
+
+ rval = mega_n_to_m((void __user *)arg, &mc);
+
+ if( rval ) goto freemem_and_return;
+
+
+ /*
+ * Is data going up-stream
+ */
+ if( pthru->dataxferlen && (uioc.flags & UIOC_RD) ) {
+ if( copy_to_user((char __user *)uxferaddr, data,
+ pthru->dataxferlen) ) {
+ rval = (-EFAULT);
+ }
+ }
+
+ /*
+ * Send the request sense data also, irrespective of
+ * whether the user has asked for it or not.
+ */
+ copy_to_user(upthru->reqsensearea,
+ pthru->reqsensearea, 14);
+
+freemem_and_return:
+ if( pthru->dataxferlen ) {
+ pci_free_consistent(pdev,
+ pthru->dataxferlen, data,
+ data_dma_hndl);
+ }
+
+ pci_free_consistent(pdev, sizeof(mega_passthru),
+ pthru, pthru_dma_hndl);
+
+ free_local_pdev(pdev);
+
+ return rval;
+ }
+ else {
+ /* DCMD commands */
+
+ /*
+ * Is there a data transfer
+ */
+ if( uioc.xferlen ) {
+ data = pci_alloc_consistent(pdev,
+ uioc.xferlen, &data_dma_hndl);
+
+ if( data == NULL ) {
+ free_local_pdev(pdev);
+ return (-ENOMEM);
+ }
+
+ uxferaddr = MBOX(uioc)->xferaddr;
+ }
+
+ /*
+ * Is data coming down-stream
+ */
+ if( uioc.xferlen && (uioc.flags & UIOC_WR) ) {
+ /*
+ * Get the user data
+ */
+ if( copy_from_user(data, (char __user *)uxferaddr,
+ uioc.xferlen) ) {
+
+ pci_free_consistent(pdev,
+ uioc.xferlen,
+ data, data_dma_hndl);
+
+ free_local_pdev(pdev);
+
+ return (-EFAULT);
+ }
+ }
+
+ memcpy(&mc, MBOX(uioc), sizeof(megacmd_t));
+
+ mc.xferaddr = (u32)data_dma_hndl;
+
+ /*
+ * Issue the command
+ */
+ mega_internal_command(adapter, LOCK_INT, &mc, NULL);
+
+ rval = mega_n_to_m((void __user *)arg, &mc);
+
+ if( rval ) {
+ if( uioc.xferlen ) {
+ pci_free_consistent(pdev,
+ uioc.xferlen, data,
+ data_dma_hndl);
+ }
+
+ free_local_pdev(pdev);
+
+ return rval;
+ }
+
+ /*
+ * Is data going up-stream
+ */
+ if( uioc.xferlen && (uioc.flags & UIOC_RD) ) {
+ if( copy_to_user((char __user *)uxferaddr, data,
+ uioc.xferlen) ) {
+
+ rval = (-EFAULT);
+ }
+ }
+
+ if( uioc.xferlen ) {
+ pci_free_consistent(pdev,
+ uioc.xferlen, data,
+ data_dma_hndl);
+ }
+
+ free_local_pdev(pdev);
+
+ return rval;
+ }
+
+ default:
+ return (-EINVAL);
+ }
+
+ return 0;
+}
+
+/**
+ * mega_m_to_n()
+ * @arg - user address
+ * @uioc - new ioctl structure
+ *
+ * A thin layer to convert older mimd interface ioctl structure to NIT ioctl
+ * structure
+ *
+ * Converts the older mimd ioctl structure to newer NIT structure
+ */
+static int
+mega_m_to_n(void __user *arg, nitioctl_t *uioc)
+{
+ struct uioctl_t uioc_mimd;
+ char signature[8] = {0};
+ u8 opcode;
+ u8 subopcode;
+
+
+ /*
+ * check is the application conforms to NIT. We do not have to do much
+ * in that case.
+ * We exploit the fact that the signature is stored in the very
+ * begining of the structure.
+ */
+
+ if( copy_from_user(signature, arg, 7) )
+ return (-EFAULT);
+
+ if( memcmp(signature, "MEGANIT", 7) == 0 ) {
+
+ /*
+ * NOTE NOTE: The nit ioctl is still under flux because of
+ * change of mailbox definition, in HPE. No applications yet
+ * use this interface and let's not have applications use this
+ * interface till the new specifitions are in place.
+ */
+ return -EINVAL;
+#if 0
+ if( copy_from_user(uioc, arg, sizeof(nitioctl_t)) )
+ return (-EFAULT);
+ return 0;
+#endif
+ }
+
+ /*
+ * Else assume we have mimd uioctl_t as arg. Convert to nitioctl_t
+ *
+ * Get the user ioctl structure
+ */
+ if( copy_from_user(&uioc_mimd, arg, sizeof(struct uioctl_t)) )
+ return (-EFAULT);
+
+
+ /*
+ * Get the opcode and subopcode for the commands
+ */
+ opcode = uioc_mimd.ui.fcs.opcode;
+ subopcode = uioc_mimd.ui.fcs.subopcode;
+
+ switch (opcode) {
+ case 0x82:
+
+ switch (subopcode) {
+
+ case MEGAIOC_QDRVRVER: /* Query driver version */
+ uioc->opcode = GET_DRIVER_VER;
+ uioc->uioc_uaddr = uioc_mimd.data;
+ break;
+
+ case MEGAIOC_QNADAP: /* Get # of adapters */
+ uioc->opcode = GET_N_ADAP;
+ uioc->uioc_uaddr = uioc_mimd.data;
+ break;
+
+ case MEGAIOC_QADAPINFO: /* Get adapter information */
+ uioc->opcode = GET_ADAP_INFO;
+ uioc->adapno = uioc_mimd.ui.fcs.adapno;
+ uioc->uioc_uaddr = uioc_mimd.data;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+
+ break;
+
+
+ case 0x81:
+
+ uioc->opcode = MBOX_CMD;
+ uioc->adapno = uioc_mimd.ui.fcs.adapno;
+
+ memcpy(uioc->uioc_rmbox, uioc_mimd.mbox, 18);
+
+ uioc->xferlen = uioc_mimd.ui.fcs.length;
+
+ if( uioc_mimd.outlen ) uioc->flags = UIOC_RD;
+ if( uioc_mimd.inlen ) uioc->flags |= UIOC_WR;
+
+ break;
+
+ case 0x80:
+
+ uioc->opcode = MBOX_CMD;
+ uioc->adapno = uioc_mimd.ui.fcs.adapno;
+
+ memcpy(uioc->uioc_rmbox, uioc_mimd.mbox, 18);
+
+ /*
+ * Choose the xferlen bigger of input and output data
+ */
+ uioc->xferlen = uioc_mimd.outlen > uioc_mimd.inlen ?
+ uioc_mimd.outlen : uioc_mimd.inlen;
+
+ if( uioc_mimd.outlen ) uioc->flags = UIOC_RD;
+ if( uioc_mimd.inlen ) uioc->flags |= UIOC_WR;
+
+ break;
+
+ default:
+ return (-EINVAL);
+
+ }
+
+ return 0;
+}
+
+/*
+ * mega_n_to_m()
+ * @arg - user address
+ * @mc - mailbox command
+ *
+ * Updates the status information to the application, depending on application
+ * conforms to older mimd ioctl interface or newer NIT ioctl interface
+ */
+static int
+mega_n_to_m(void __user *arg, megacmd_t *mc)
+{
+ nitioctl_t __user *uiocp;
+ megacmd_t __user *umc;
+ mega_passthru __user *upthru;
+ struct uioctl_t __user *uioc_mimd;
+ char signature[8] = {0};
+
+ /*
+ * check is the application conforms to NIT.
+ */
+ if( copy_from_user(signature, arg, 7) )
+ return -EFAULT;
+
+ if( memcmp(signature, "MEGANIT", 7) == 0 ) {
+
+ uiocp = arg;
+
+ if( put_user(mc->status, (u8 __user *)&MBOX_P(uiocp)->status) )
+ return (-EFAULT);
+
+ if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) {
+
+ umc = MBOX_P(uiocp);
+
+ if (get_user(upthru, (mega_passthru __user * __user *)&umc->xferaddr))
+ return -EFAULT;
+
+ if( put_user(mc->status, (u8 __user *)&upthru->scsistatus))
+ return (-EFAULT);
+ }
+ }
+ else {
+ uioc_mimd = arg;
+
+ if( put_user(mc->status, (u8 __user *)&uioc_mimd->mbox[17]) )
+ return (-EFAULT);
+
+ if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) {
+
+ umc = (megacmd_t __user *)uioc_mimd->mbox;
+
+ if (get_user(upthru, (mega_passthru __user * __user *)&umc->xferaddr))
+ return (-EFAULT);
+
+ if( put_user(mc->status, (u8 __user *)&upthru->scsistatus) )
+ return (-EFAULT);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * MEGARAID 'FW' commands.
+ */
+
+/**
+ * mega_is_bios_enabled()
+ * @adapter - pointer to our soft state
+ *
+ * issue command to find out if the BIOS is enabled for this controller
+ */
+static int
+mega_is_bios_enabled(adapter_t *adapter)
+{
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ int ret;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ raw_mbox[0] = IS_BIOS_ENABLED;
+ raw_mbox[2] = GET_BIOS;
+
+
+ ret = issue_scb_block(adapter, raw_mbox);
+
+ return *(char *)adapter->mega_buffer;
+}
+
+
+/**
+ * mega_enum_raid_scsi()
+ * @adapter - pointer to our soft state
+ *
+ * Find out what channels are RAID/SCSI. This information is used to
+ * differentiate the virtual channels and physical channels and to support
+ * ROMB feature and non-disk devices.
+ */
+static void
+mega_enum_raid_scsi(adapter_t *adapter)
+{
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ int i;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+
+ /*
+ * issue command to find out what channels are raid/scsi
+ */
+ raw_mbox[0] = CHNL_CLASS;
+ raw_mbox[2] = GET_CHNL_CLASS;
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ /*
+ * Non-ROMB firmware fail this command, so all channels
+ * must be shown RAID
+ */
+ adapter->mega_ch_class = 0xFF;
+
+ if(!issue_scb_block(adapter, raw_mbox)) {
+ adapter->mega_ch_class = *((char *)adapter->mega_buffer);
+
+ }
+
+ for( i = 0; i < adapter->product_info.nchannels; i++ ) {
+ if( (adapter->mega_ch_class >> i) & 0x01 ) {
+ printk(KERN_INFO "megaraid: channel[%d] is raid.\n",
+ i);
+ }
+ else {
+ printk(KERN_INFO "megaraid: channel[%d] is scsi.\n",
+ i);
+ }
+ }
+
+ return;
+}
+
+
+/**
+ * mega_get_boot_drv()
+ * @adapter - pointer to our soft state
+ *
+ * Find out which device is the boot device. Note, any logical drive or any
+ * phyical device (e.g., a CDROM) can be designated as a boot device.
+ */
+static void
+mega_get_boot_drv(adapter_t *adapter)
+{
+ struct private_bios_data *prv_bios_data;
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ u16 cksum = 0;
+ u8 *cksum_p;
+ u8 boot_pdrv;
+ int i;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+
+ raw_mbox[0] = BIOS_PVT_DATA;
+ raw_mbox[2] = GET_BIOS_PVT_DATA;
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ adapter->boot_ldrv_enabled = 0;
+ adapter->boot_ldrv = 0;
+
+ adapter->boot_pdrv_enabled = 0;
+ adapter->boot_pdrv_ch = 0;
+ adapter->boot_pdrv_tgt = 0;
+
+ if(issue_scb_block(adapter, raw_mbox) == 0) {
+ prv_bios_data =
+ (struct private_bios_data *)adapter->mega_buffer;
+
+ cksum = 0;
+ cksum_p = (char *)prv_bios_data;
+ for (i = 0; i < 14; i++ ) {
+ cksum += (u16)(*cksum_p++);
+ }
+
+ if (prv_bios_data->cksum == (u16)(0-cksum) ) {
+
+ /*
+ * If MSB is set, a physical drive is set as boot
+ * device
+ */
+ if( prv_bios_data->boot_drv & 0x80 ) {
+ adapter->boot_pdrv_enabled = 1;
+ boot_pdrv = prv_bios_data->boot_drv & 0x7F;
+ adapter->boot_pdrv_ch = boot_pdrv / 16;
+ adapter->boot_pdrv_tgt = boot_pdrv % 16;
+ }
+ else {
+ adapter->boot_ldrv_enabled = 1;
+ adapter->boot_ldrv = prv_bios_data->boot_drv;
+ }
+ }
+ }
+
+}
+
+/**
+ * mega_support_random_del()
+ * @adapter - pointer to our soft state
+ *
+ * Find out if this controller supports random deletion and addition of
+ * logical drives
+ */
+static int
+mega_support_random_del(adapter_t *adapter)
+{
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ int rval;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+
+ /*
+ * issue command
+ */
+ raw_mbox[0] = FC_DEL_LOGDRV;
+ raw_mbox[2] = OP_SUP_DEL_LOGDRV;
+
+ rval = issue_scb_block(adapter, raw_mbox);
+
+ return !rval;
+}
+
+
+/**
+ * mega_support_ext_cdb()
+ * @adapter - pointer to our soft state
+ *
+ * Find out if this firmware support cdblen > 10
+ */
+static int
+mega_support_ext_cdb(adapter_t *adapter)
+{
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ int rval;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ /*
+ * issue command to find out if controller supports extended CDBs.
+ */
+ raw_mbox[0] = 0xA4;
+ raw_mbox[2] = 0x16;
+
+ rval = issue_scb_block(adapter, raw_mbox);
+
+ return !rval;
+}
+
+
+/**
+ * mega_del_logdrv()
+ * @adapter - pointer to our soft state
+ * @logdrv - logical drive to be deleted
+ *
+ * Delete the specified logical drive. It is the responsibility of the user
+ * app to let the OS know about this operation.
+ */
+static int
+mega_del_logdrv(adapter_t *adapter, int logdrv)
+{
+ unsigned long flags;
+ scb_t *scb;
+ int rval;
+
+ /*
+ * Stop sending commands to the controller, queue them internally.
+ * When deletion is complete, ISR will flush the queue.
+ */
+ atomic_set(&adapter->quiescent, 1);
+
+ /*
+ * Wait till all the issued commands are complete and there are no
+ * commands in the pending queue
+ */
+ while (atomic_read(&adapter->pend_cmds) > 0 ||
+ !list_empty(&adapter->pending_list))
+ msleep(1000); /* sleep for 1s */
+
+ rval = mega_do_del_logdrv(adapter, logdrv);
+
+ spin_lock_irqsave(&adapter->lock, flags);
+
+ /*
+ * If delete operation was successful, add 0x80 to the logical drive
+ * ids for commands in the pending queue.
+ */
+ if (adapter->read_ldidmap) {
+ struct list_head *pos;
+ list_for_each(pos, &adapter->pending_list) {
+ scb = list_entry(pos, scb_t, list);
+ if (scb->pthru->logdrv < 0x80 )
+ scb->pthru->logdrv += 0x80;
+ }
+ }
+
+ atomic_set(&adapter->quiescent, 0);
+
+ mega_runpendq(adapter);
+
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ return rval;
+}
+
+
+static int
+mega_do_del_logdrv(adapter_t *adapter, int logdrv)
+{
+ megacmd_t mc;
+ int rval;
+
+ memset( &mc, 0, sizeof(megacmd_t));
+
+ mc.cmd = FC_DEL_LOGDRV;
+ mc.opcode = OP_DEL_LOGDRV;
+ mc.subopcode = logdrv;
+
+ rval = mega_internal_command(adapter, LOCK_INT, &mc, NULL);
+
+ /* log this event */
+ if(rval) {
+ printk(KERN_WARNING "megaraid: Delete LD-%d failed.", logdrv);
+ return rval;
+ }
+
+ /*
+ * After deleting first logical drive, the logical drives must be
+ * addressed by adding 0x80 to the logical drive id.
+ */
+ adapter->read_ldidmap = 1;
+
+ return rval;
+}
+
+
+/**
+ * mega_get_max_sgl()
+ * @adapter - pointer to our soft state
+ *
+ * Find out the maximum number of scatter-gather elements supported by this
+ * version of the firmware
+ */
+static void
+mega_get_max_sgl(adapter_t *adapter)
+{
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(mbox, 0, sizeof(raw_mbox));
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ raw_mbox[0] = MAIN_MISC_OPCODE;
+ raw_mbox[2] = GET_MAX_SG_SUPPORT;
+
+
+ if( issue_scb_block(adapter, raw_mbox) ) {
+ /*
+ * f/w does not support this command. Choose the default value
+ */
+ adapter->sglen = MIN_SGLIST;
+ }
+ else {
+ adapter->sglen = *((char *)adapter->mega_buffer);
+
+ /*
+ * Make sure this is not more than the resources we are
+ * planning to allocate
+ */
+ if ( adapter->sglen > MAX_SGLIST )
+ adapter->sglen = MAX_SGLIST;
+ }
+
+ return;
+}
+
+
+/**
+ * mega_support_cluster()
+ * @adapter - pointer to our soft state
+ *
+ * Find out if this firmware support cluster calls.
+ */
+static int
+mega_support_cluster(adapter_t *adapter)
+{
+ unsigned char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset(mbox, 0, sizeof(raw_mbox));
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ /*
+ * Try to get the initiator id. This command will succeed iff the
+ * clustering is available on this HBA.
+ */
+ raw_mbox[0] = MEGA_GET_TARGET_ID;
+
+ if( issue_scb_block(adapter, raw_mbox) == 0 ) {
+
+ /*
+ * Cluster support available. Get the initiator target id.
+ * Tell our id to mid-layer too.
+ */
+ adapter->this_id = *(u32 *)adapter->mega_buffer;
+ adapter->host->this_id = adapter->this_id;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * mega_adapinq()
+ * @adapter - pointer to our soft state
+ * @dma_handle - DMA address of the buffer
+ *
+ * Issue internal comamnds while interrupts are available.
+ * We only issue direct mailbox commands from within the driver. ioctl()
+ * interface using these routines can issue passthru commands.
+ */
+static int
+mega_adapinq(adapter_t *adapter, dma_addr_t dma_handle)
+{
+ megacmd_t mc;
+
+ memset(&mc, 0, sizeof(megacmd_t));
+
+ if( adapter->flag & BOARD_40LD ) {
+ mc.cmd = FC_NEW_CONFIG;
+ mc.opcode = NC_SUBOP_ENQUIRY3;
+ mc.subopcode = ENQ3_GET_SOLICITED_FULL;
+ }
+ else {
+ mc.cmd = MEGA_MBOXCMD_ADPEXTINQ;
+ }
+
+ mc.xferaddr = (u32)dma_handle;
+
+ if ( mega_internal_command(adapter, LOCK_INT, &mc, NULL) != 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/** mega_internal_dev_inquiry()
+ * @adapter - pointer to our soft state
+ * @ch - channel for this device
+ * @tgt - ID of this device
+ * @buf_dma_handle - DMA address of the buffer
+ *
+ * Issue the scsi inquiry for the specified device.
+ */
+static int
+mega_internal_dev_inquiry(adapter_t *adapter, u8 ch, u8 tgt,
+ dma_addr_t buf_dma_handle)
+{
+ mega_passthru *pthru;
+ dma_addr_t pthru_dma_handle;
+ megacmd_t mc;
+ int rval;
+ struct pci_dev *pdev;
+
+
+ /*
+ * For all internal commands, the buffer must be allocated in <4GB
+ * address range
+ */
+ if( make_local_pdev(adapter, &pdev) != 0 ) return -1;
+
+ pthru = pci_alloc_consistent(pdev, sizeof(mega_passthru),
+ &pthru_dma_handle);
+
+ if( pthru == NULL ) {
+ free_local_pdev(pdev);
+ return -1;
+ }
+
+ pthru->timeout = 2;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 0;
+
+ pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : ch;
+
+ pthru->target = (adapter->flag & BOARD_40LD) ? (ch << 4)|tgt : tgt;
+
+ pthru->cdblen = 6;
+
+ pthru->cdb[0] = INQUIRY;
+ pthru->cdb[1] = 0;
+ pthru->cdb[2] = 0;
+ pthru->cdb[3] = 0;
+ pthru->cdb[4] = 255;
+ pthru->cdb[5] = 0;
+
+
+ pthru->dataxferaddr = (u32)buf_dma_handle;
+ pthru->dataxferlen = 256;
+
+ memset(&mc, 0, sizeof(megacmd_t));
+
+ mc.cmd = MEGA_MBOXCMD_PASSTHRU;
+ mc.xferaddr = (u32)pthru_dma_handle;
+
+ rval = mega_internal_command(adapter, LOCK_INT, &mc, pthru);
+
+ pci_free_consistent(pdev, sizeof(mega_passthru), pthru,
+ pthru_dma_handle);
+
+ free_local_pdev(pdev);
+
+ return rval;
+}
+
+
+/**
+ * mega_internal_command()
+ * @adapter - pointer to our soft state
+ * @ls - the scope of the exclusion lock.
+ * @mc - the mailbox command
+ * @pthru - Passthru structure for DCDB commands
+ *
+ * Issue the internal commands in interrupt mode.
+ * The last argument is the address of the passthru structure if the command
+ * to be fired is a passthru command
+ *
+ * lockscope specifies whether the caller has already acquired the lock. Of
+ * course, the caller must know which lock we are talking about.
+ *
+ * Note: parameter 'pthru' is null for non-passthru commands.
+ */
+static int
+mega_internal_command(adapter_t *adapter, lockscope_t ls, megacmd_t *mc,
+ mega_passthru *pthru )
+{
+ Scsi_Cmnd *scmd;
+ struct scsi_device *sdev;
+ unsigned long flags = 0;
+ scb_t *scb;
+ int rval;
+
+ /*
+ * The internal commands share one command id and hence are
+ * serialized. This is so because we want to reserve maximum number of
+ * available command ids for the I/O commands.
+ */
+ down(&adapter->int_mtx);
+
+ scb = &adapter->int_scb;
+ memset(scb, 0, sizeof(scb_t));
+
+ scmd = &adapter->int_scmd;
+ memset(scmd, 0, sizeof(Scsi_Cmnd));
+
+ sdev = kmalloc(sizeof(struct scsi_device), GFP_KERNEL);
+ memset(sdev, 0, sizeof(struct scsi_device));
+ scmd->device = sdev;
+
+ scmd->device->host = adapter->host;
+ scmd->buffer = (void *)scb;
+ scmd->cmnd[0] = MEGA_INTERNAL_CMD;
+
+ scb->state |= SCB_ACTIVE;
+ scb->cmd = scmd;
+
+ memcpy(scb->raw_mbox, mc, sizeof(megacmd_t));
+
+ /*
+ * Is it a passthru command
+ */
+ if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) {
+
+ scb->pthru = pthru;
+ }
+
+ scb->idx = CMDID_INT_CMDS;
+
+ scmd->state = 0;
+
+ /*
+ * Get the lock only if the caller has not acquired it already
+ */
+ if( ls == LOCK_INT ) spin_lock_irqsave(&adapter->lock, flags);
+
+ megaraid_queue(scmd, mega_internal_done);
+
+ if( ls == LOCK_INT ) spin_unlock_irqrestore(&adapter->lock, flags);
+
+ /*
+ * Wait till this command finishes. Do not use
+ * wait_event_interruptible(). It causes panic if CTRL-C is hit when
+ * dumping e.g., physical disk information through /proc interface.
+ */
+#if 0
+ wait_event_interruptible(adapter->int_waitq, scmd->state);
+#endif
+ wait_event(adapter->int_waitq, scmd->state);
+
+ rval = scmd->result;
+ mc->status = scmd->result;
+ kfree(sdev);
+
+ /*
+ * Print a debug message for all failed commands. Applications can use
+ * this information.
+ */
+ if( scmd->result && trace_level ) {
+ printk("megaraid: cmd [%x, %x, %x] status:[%x]\n",
+ mc->cmd, mc->opcode, mc->subopcode, scmd->result);
+ }
+
+ up(&adapter->int_mtx);
+
+ return rval;
+}
+
+
+/**
+ * mega_internal_done()
+ * @scmd - internal scsi command
+ *
+ * Callback routine for internal commands.
+ */
+static void
+mega_internal_done(Scsi_Cmnd *scmd)
+{
+ adapter_t *adapter;
+
+ adapter = (adapter_t *)scmd->device->host->hostdata;
+
+ scmd->state = 1; /* thread waiting for its command to complete */
+
+ /*
+ * See comment in mega_internal_command() routine for
+ * wait_event_interruptible()
+ */
+#if 0
+ wake_up_interruptible(&adapter->int_waitq);
+#endif
+ wake_up(&adapter->int_waitq);
+
+}
+
+
+static struct scsi_host_template megaraid_template = {
+ .module = THIS_MODULE,
+ .name = "MegaRAID",
+ .proc_name = "megaraid",
+ .info = megaraid_info,
+ .queuecommand = megaraid_queue,
+ .bios_param = megaraid_biosparam,
+ .max_sectors = MAX_SECTORS_PER_IO,
+ .can_queue = MAX_COMMANDS,
+ .this_id = DEFAULT_INITIATOR_ID,
+ .sg_tablesize = MAX_SGLIST,
+ .cmd_per_lun = DEF_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_abort_handler = megaraid_abort,
+ .eh_device_reset_handler = megaraid_reset,
+ .eh_bus_reset_handler = megaraid_reset,
+ .eh_host_reset_handler = megaraid_reset,
+};
+
+static int __devinit
+megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct Scsi_Host *host;
+ adapter_t *adapter;
+ unsigned long mega_baseport, tbase, flag = 0;
+ u16 subsysid, subsysvid;
+ u8 pci_bus, pci_dev_func;
+ int irq, i, j;
+ int error = -ENODEV;
+
+ if (pci_enable_device(pdev))
+ goto out;
+ pci_set_master(pdev);
+
+ pci_bus = pdev->bus->number;
+ pci_dev_func = pdev->devfn;
+
+ /*
+ * The megaraid3 stuff reports the ID of the Intel part which is not
+ * remotely specific to the megaraid
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
+ u16 magic;
+ /*
+ * Don't fall over the Compaq management cards using the same
+ * PCI identifier
+ */
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ &&
+ pdev->subsystem_device == 0xC000)
+ return -ENODEV;
+ /* Now check the magic signature byte */
+ pci_read_config_word(pdev, PCI_CONF_AMISIG, &magic);
+ if (magic != HBA_SIGNATURE_471 && magic != HBA_SIGNATURE)
+ return -ENODEV;
+ /* Ok it is probably a megaraid */
+ }
+
+ /*
+ * For these vendor and device ids, signature offsets are not
+ * valid and 64 bit is implicit
+ */
+ if (id->driver_data & BOARD_64BIT)
+ flag |= BOARD_64BIT;
+ else {
+ u32 magic64;
+
+ pci_read_config_dword(pdev, PCI_CONF_AMISIG64, &magic64);
+ if (magic64 == HBA_SIGNATURE_64BIT)
+ flag |= BOARD_64BIT;
+ }
+
+ subsysvid = pdev->subsystem_vendor;
+ subsysid = pdev->subsystem_device;
+
+ printk(KERN_NOTICE "megaraid: found 0x%4.04x:0x%4.04x:bus %d:",
+ id->vendor, id->device, pci_bus);
+
+ printk("slot %d:func %d\n",
+ PCI_SLOT(pci_dev_func), PCI_FUNC(pci_dev_func));
+
+ /* Read the base port and IRQ from PCI */
+ mega_baseport = pci_resource_start(pdev, 0);
+ irq = pdev->irq;
+
+ tbase = mega_baseport;
+ if (pci_resource_flags(pdev, 0) & IORESOURCE_MEM) {
+ flag |= BOARD_MEMMAP;
+
+ if (!request_mem_region(mega_baseport, 128, "megaraid")) {
+ printk(KERN_WARNING "megaraid: mem region busy!\n");
+ goto out_disable_device;
+ }
+
+ mega_baseport = (unsigned long)ioremap(mega_baseport, 128);
+ if (!mega_baseport) {
+ printk(KERN_WARNING
+ "megaraid: could not map hba memory\n");
+ goto out_release_region;
+ }
+ } else {
+ flag |= BOARD_IOMAP;
+ mega_baseport += 0x10;
+
+ if (!request_region(mega_baseport, 16, "megaraid"))
+ goto out_disable_device;
+ }
+
+ /* Initialize SCSI Host structure */
+ host = scsi_host_alloc(&megaraid_template, sizeof(adapter_t));
+ if (!host)
+ goto out_iounmap;
+
+ adapter = (adapter_t *)host->hostdata;
+ memset(adapter, 0, sizeof(adapter_t));
+
+ printk(KERN_NOTICE
+ "scsi%d:Found MegaRAID controller at 0x%lx, IRQ:%d\n",
+ host->host_no, mega_baseport, irq);
+
+ adapter->base = mega_baseport;
+
+ INIT_LIST_HEAD(&adapter->free_list);
+ INIT_LIST_HEAD(&adapter->pending_list);
+ INIT_LIST_HEAD(&adapter->completed_list);
+
+ adapter->flag = flag;
+ spin_lock_init(&adapter->lock);
+ scsi_assign_lock(host, &adapter->lock);
+
+ host->cmd_per_lun = max_cmd_per_lun;
+ host->max_sectors = max_sectors_per_io;
+
+ adapter->dev = pdev;
+ adapter->host = host;
+
+ adapter->host->irq = irq;
+
+ if (flag & BOARD_MEMMAP)
+ adapter->host->base = tbase;
+ else {
+ adapter->host->io_port = tbase;
+ adapter->host->n_io_port = 16;
+ }
+
+ adapter->host->unique_id = (pci_bus << 8) | pci_dev_func;
+
+ /*
+ * Allocate buffer to issue internal commands.
+ */
+ adapter->mega_buffer = pci_alloc_consistent(adapter->dev,
+ MEGA_BUFFER_SIZE, &adapter->buf_dma_handle);
+ if (!adapter->mega_buffer) {
+ printk(KERN_WARNING "megaraid: out of RAM.\n");
+ goto out_host_put;
+ }
+
+ adapter->scb_list = kmalloc(sizeof(scb_t) * MAX_COMMANDS, GFP_KERNEL);
+ if (!adapter->scb_list) {
+ printk(KERN_WARNING "megaraid: out of RAM.\n");
+ goto out_free_cmd_buffer;
+ }
+
+ if (request_irq(irq, (adapter->flag & BOARD_MEMMAP) ?
+ megaraid_isr_memmapped : megaraid_isr_iomapped,
+ SA_SHIRQ, "megaraid", adapter)) {
+ printk(KERN_WARNING
+ "megaraid: Couldn't register IRQ %d!\n", irq);
+ goto out_free_scb_list;
+ }
+
+ if (mega_setup_mailbox(adapter))
+ goto out_free_irq;
+
+ if (mega_query_adapter(adapter))
+ goto out_free_mbox;
+
+ /*
+ * Have checks for some buggy f/w
+ */
+ if ((subsysid == 0x1111) && (subsysvid == 0x1111)) {
+ /*
+ * Which firmware
+ */
+ if (!strcmp(adapter->fw_version, "3.00") ||
+ !strcmp(adapter->fw_version, "3.01")) {
+
+ printk( KERN_WARNING
+ "megaraid: Your card is a Dell PERC "
+ "2/SC RAID controller with "
+ "firmware\nmegaraid: 3.00 or 3.01. "
+ "This driver is known to have "
+ "corruption issues\nmegaraid: with "
+ "those firmware versions on this "
+ "specific card. In order\nmegaraid: "
+ "to protect your data, please upgrade "
+ "your firmware to version\nmegaraid: "
+ "3.10 or later, available from the "
+ "Dell Technical Support web\n"
+ "megaraid: site at\nhttp://support."
+ "dell.com/us/en/filelib/download/"
+ "index.asp?fileid=2940\n"
+ );
+ }
+ }
+
+ /*
+ * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with
+ * firmware H.01.07, H.01.08, and H.01.09 disable 64 bit
+ * support, since this firmware cannot handle 64 bit
+ * addressing
+ */
+ if ((subsysvid == HP_SUBSYS_VID) &&
+ ((subsysid == 0x60E7) || (subsysid == 0x60E8))) {
+ /*
+ * which firmware
+ */
+ if (!strcmp(adapter->fw_version, "H01.07") ||
+ !strcmp(adapter->fw_version, "H01.08") ||
+ !strcmp(adapter->fw_version, "H01.09") ) {
+ printk(KERN_WARNING
+ "megaraid: Firmware H.01.07, "
+ "H.01.08, and H.01.09 on 1M/2M "
+ "controllers\n"
+ "megaraid: do not support 64 bit "
+ "addressing.\nmegaraid: DISABLING "
+ "64 bit support.\n");
+ adapter->flag &= ~BOARD_64BIT;
+ }
+ }
+
+ if (mega_is_bios_enabled(adapter))
+ mega_hbas[hba_count].is_bios_enabled = 1;
+ mega_hbas[hba_count].hostdata_addr = adapter;
+
+ /*
+ * Find out which channel is raid and which is scsi. This is
+ * for ROMB support.
+ */
+ mega_enum_raid_scsi(adapter);
+
+ /*
+ * Find out if a logical drive is set as the boot drive. If
+ * there is one, will make that as the first logical drive.
+ * ROMB: Do we have to boot from a physical drive. Then all
+ * the physical drives would appear before the logical disks.
+ * Else, all the physical drives would be exported to the mid
+ * layer after logical drives.
+ */
+ mega_get_boot_drv(adapter);
+
+ if (adapter->boot_pdrv_enabled) {
+ j = adapter->product_info.nchannels;
+ for( i = 0; i < j; i++ )
+ adapter->logdrv_chan[i] = 0;
+ for( i = j; i < NVIRT_CHAN + j; i++ )
+ adapter->logdrv_chan[i] = 1;
+ } else {
+ for (i = 0; i < NVIRT_CHAN; i++)
+ adapter->logdrv_chan[i] = 1;
+ for (i = NVIRT_CHAN; i < MAX_CHANNELS+NVIRT_CHAN; i++)
+ adapter->logdrv_chan[i] = 0;
+ adapter->mega_ch_class <<= NVIRT_CHAN;
+ }
+
+ /*
+ * Do we support random deletion and addition of logical
+ * drives
+ */
+ adapter->read_ldidmap = 0; /* set it after first logdrv
+ delete cmd */
+ adapter->support_random_del = mega_support_random_del(adapter);
+
+ /* Initialize SCBs */
+ if (mega_init_scb(adapter))
+ goto out_free_mbox;
+
+ /*
+ * Reset the pending commands counter
+ */
+ atomic_set(&adapter->pend_cmds, 0);
+
+ /*
+ * Reset the adapter quiescent flag
+ */
+ atomic_set(&adapter->quiescent, 0);
+
+ hba_soft_state[hba_count] = adapter;
+
+ /*
+ * Fill in the structure which needs to be passed back to the
+ * application when it does an ioctl() for controller related
+ * information.
+ */
+ i = hba_count;
+
+ mcontroller[i].base = mega_baseport;
+ mcontroller[i].irq = irq;
+ mcontroller[i].numldrv = adapter->numldrv;
+ mcontroller[i].pcibus = pci_bus;
+ mcontroller[i].pcidev = id->device;
+ mcontroller[i].pcifun = PCI_FUNC (pci_dev_func);
+ mcontroller[i].pciid = -1;
+ mcontroller[i].pcivendor = id->vendor;
+ mcontroller[i].pcislot = PCI_SLOT(pci_dev_func);
+ mcontroller[i].uid = (pci_bus << 8) | pci_dev_func;
+
+
+ /* Set the Mode of addressing to 64 bit if we can */
+ if ((adapter->flag & BOARD_64BIT) && (sizeof(dma_addr_t) == 8)) {
+ pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
+ adapter->has_64bit_addr = 1;
+ } else {
+ pci_set_dma_mask(pdev, 0xffffffff);
+ adapter->has_64bit_addr = 0;
+ }
+
+ init_MUTEX(&adapter->int_mtx);
+ init_waitqueue_head(&adapter->int_waitq);
+
+ adapter->this_id = DEFAULT_INITIATOR_ID;
+ adapter->host->this_id = DEFAULT_INITIATOR_ID;
+
+#if MEGA_HAVE_CLUSTERING
+ /*
+ * Is cluster support enabled on this controller
+ * Note: In a cluster the HBAs ( the initiators ) will have
+ * different target IDs and we cannot assume it to be 7. Call
+ * to mega_support_cluster() will get the target ids also if
+ * the cluster support is available
+ */
+ adapter->has_cluster = mega_support_cluster(adapter);
+ if (adapter->has_cluster) {
+ printk(KERN_NOTICE
+ "megaraid: Cluster driver, initiator id:%d\n",
+ adapter->this_id);
+ }
+#endif
+
+ pci_set_drvdata(pdev, host);
+
+ mega_create_proc_entry(hba_count, mega_proc_dir_entry);
+
+ error = scsi_add_host(host, &pdev->dev);
+ if (error)
+ goto out_free_mbox;
+
+ scsi_scan_host(host);
+ hba_count++;
+ return 0;
+
+ out_free_mbox:
+ pci_free_consistent(adapter->dev, sizeof(mbox64_t),
+ adapter->una_mbox64, adapter->una_mbox64_dma);
+ out_free_irq:
+ free_irq(adapter->host->irq, adapter);
+ out_free_scb_list:
+ kfree(adapter->scb_list);
+ out_free_cmd_buffer:
+ pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE,
+ adapter->mega_buffer, adapter->buf_dma_handle);
+ out_host_put:
+ scsi_host_put(host);
+ out_iounmap:
+ if (flag & BOARD_MEMMAP)
+ iounmap((void *)mega_baseport);
+ out_release_region:
+ if (flag & BOARD_MEMMAP)
+ release_mem_region(tbase, 128);
+ else
+ release_region(mega_baseport, 16);
+ out_disable_device:
+ pci_disable_device(pdev);
+ out:
+ return error;
+}
+
+static void
+__megaraid_shutdown(adapter_t *adapter)
+{
+ u_char raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox = (mbox_t *)raw_mbox;
+ int i;
+
+ /* Flush adapter cache */
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ raw_mbox[0] = FLUSH_ADAPTER;
+
+ free_irq(adapter->host->irq, adapter);
+
+ /* Issue a blocking (interrupts disabled) command to the card */
+ issue_scb_block(adapter, raw_mbox);
+
+ /* Flush disks cache */
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+ raw_mbox[0] = FLUSH_SYSTEM;
+
+ /* Issue a blocking (interrupts disabled) command to the card */
+ issue_scb_block(adapter, raw_mbox);
+
+ if (atomic_read(&adapter->pend_cmds) > 0)
+ printk(KERN_WARNING "megaraid: pending commands!!\n");
+
+ /*
+ * Have a delibrate delay to make sure all the caches are
+ * actually flushed.
+ */
+ for (i = 0; i <= 10; i++)
+ mdelay(1000);
+}
+
+static void
+megaraid_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ adapter_t *adapter = (adapter_t *)host->hostdata;
+ char buf[12] = { 0 };
+
+ scsi_remove_host(host);
+
+ __megaraid_shutdown(adapter);
+
+ /* Free our resources */
+ if (adapter->flag & BOARD_MEMMAP) {
+ iounmap((void *)adapter->base);
+ release_mem_region(adapter->host->base, 128);
+ } else
+ release_region(adapter->base, 16);
+
+ mega_free_sgl(adapter);
+
+#ifdef CONFIG_PROC_FS
+ if (adapter->controller_proc_dir_entry) {
+ remove_proc_entry("stat", adapter->controller_proc_dir_entry);
+ remove_proc_entry("config",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("mailbox",
+ adapter->controller_proc_dir_entry);
+#if MEGA_HAVE_ENH_PROC
+ remove_proc_entry("rebuild-rate",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("battery-status",
+ adapter->controller_proc_dir_entry);
+
+ remove_proc_entry("diskdrives-ch0",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("diskdrives-ch1",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("diskdrives-ch2",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("diskdrives-ch3",
+ adapter->controller_proc_dir_entry);
+
+ remove_proc_entry("raiddrives-0-9",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("raiddrives-10-19",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("raiddrives-20-29",
+ adapter->controller_proc_dir_entry);
+ remove_proc_entry("raiddrives-30-39",
+ adapter->controller_proc_dir_entry);
+#endif
+ sprintf(buf, "hba%d", adapter->host->host_no);
+ remove_proc_entry(buf, mega_proc_dir_entry);
+ }
+#endif
+
+ pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE,
+ adapter->mega_buffer, adapter->buf_dma_handle);
+ kfree(adapter->scb_list);
+ pci_free_consistent(adapter->dev, sizeof(mbox64_t),
+ adapter->una_mbox64, adapter->una_mbox64_dma);
+
+ scsi_host_put(host);
+ pci_disable_device(pdev);
+
+ hba_count--;
+}
+
+static void
+megaraid_shutdown(struct device *dev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
+ adapter_t *adapter = (adapter_t *)host->hostdata;
+
+ __megaraid_shutdown(adapter);
+}
+
+static struct pci_device_id megaraid_pci_tbl[] = {
+ {PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DISCOVERY,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_PERC4_DI,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, BOARD_64BIT},
+ {PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_PERC4_QC_VERDE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, BOARD_64BIT},
+ {PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, megaraid_pci_tbl);
+
+static struct pci_driver megaraid_pci_driver = {
+ .name = "megaraid",
+ .id_table = megaraid_pci_tbl,
+ .probe = megaraid_probe_one,
+ .remove = __devexit_p(megaraid_remove_one),
+ .driver = {
+ .shutdown = megaraid_shutdown,
+ },
+};
+
+static int __init megaraid_init(void)
+{
+ int error;
+
+ if ((max_cmd_per_lun <= 0) || (max_cmd_per_lun > MAX_CMD_PER_LUN))
+ max_cmd_per_lun = MAX_CMD_PER_LUN;
+ if (max_mbox_busy_wait > MBOX_BUSY_WAIT)
+ max_mbox_busy_wait = MBOX_BUSY_WAIT;
+
+#ifdef CONFIG_PROC_FS
+ mega_proc_dir_entry = proc_mkdir("megaraid", &proc_root);
+ if (!mega_proc_dir_entry) {
+ printk(KERN_WARNING
+ "megaraid: failed to create megaraid root\n");
+ }
+#endif
+ error = pci_module_init(&megaraid_pci_driver);
+ if (error) {
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("megaraid", &proc_root);
+#endif
+ return error;
+ }
+
+ /*
+ * Register the driver as a character device, for applications
+ * to access it for ioctls.
+ * First argument (major) to register_chrdev implies a dynamic
+ * major number allocation.
+ */
+ major = register_chrdev(0, "megadev", &megadev_fops);
+ if (!major) {
+ printk(KERN_WARNING
+ "megaraid: failed to register char device\n");
+ }
+
+ return 0;
+}
+
+static void __exit megaraid_exit(void)
+{
+ /*
+ * Unregister the character device interface to the driver.
+ */
+ unregister_chrdev(major, "megadev");
+
+ pci_unregister_driver(&megaraid_pci_driver);
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("megaraid", &proc_root);
+#endif
+}
+
+module_init(megaraid_init);
+module_exit(megaraid_exit);
+
+/* vi: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
new file mode 100644
index 000000000000..e25c4de9edd9
--- /dev/null
+++ b/drivers/scsi/megaraid.h
@@ -0,0 +1,1071 @@
+#ifndef __MEGARAID_H__
+#define __MEGARAID_H__
+
+#include <linux/spinlock.h>
+
+
+#define MEGARAID_VERSION \
+ "v2.00.3 (Release Date: Wed Feb 19 08:51:30 EST 2003)\n"
+
+/*
+ * Driver features - change the values to enable or disable features in the
+ * driver.
+ */
+
+/*
+ * Comand coalescing - This feature allows the driver to be able to combine
+ * two or more commands and issue as one command in order to boost I/O
+ * performance. Useful if the nature of the I/O is sequential. It is not very
+ * useful for random natured I/Os.
+ */
+#define MEGA_HAVE_COALESCING 0
+
+/*
+ * Clustering support - Set this flag if you are planning to use the
+ * clustering services provided by the megaraid controllers and planning to
+ * setup a cluster
+ */
+#define MEGA_HAVE_CLUSTERING 1
+
+/*
+ * Driver statistics - Set this flag if you are interested in statics about
+ * number of I/O completed on each logical drive and how many interrupts
+ * generated. If enabled, this information is available through /proc
+ * interface and through the private ioctl. Setting this flag has a
+ * performance penalty.
+ */
+#define MEGA_HAVE_STATS 0
+
+/*
+ * Enhanced /proc interface - This feature will allow you to have a more
+ * detailed /proc interface for megaraid driver. E.g., a real time update of
+ * the status of the logical drives, battery status, physical drives etc.
+ */
+#define MEGA_HAVE_ENH_PROC 1
+
+#define MAX_DEV_TYPE 32
+
+#ifndef PCI_VENDOR_ID_LSI_LOGIC
+#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
+#endif
+
+#ifndef PCI_VENDOR_ID_AMI
+#define PCI_VENDOR_ID_AMI 0x101E
+#endif
+
+#ifndef PCI_VENDOR_ID_DELL
+#define PCI_VENDOR_ID_DELL 0x1028
+#endif
+
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif
+
+#ifndef PCI_DEVICE_ID_AMI_MEGARAID
+#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#endif
+
+#ifndef PCI_DEVICE_ID_AMI_MEGARAID2
+#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
+#endif
+
+#ifndef PCI_DEVICE_ID_AMI_MEGARAID3
+#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
+#endif
+
+#define PCI_DEVICE_ID_DISCOVERY 0x000E
+#define PCI_DEVICE_ID_PERC4_DI 0x000F
+#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407
+
+/* Sub-System Vendor IDs */
+#define AMI_SUBSYS_VID 0x101E
+#define DELL_SUBSYS_VID 0x1028
+#define HP_SUBSYS_VID 0x103C
+#define LSI_SUBSYS_VID 0x1000
+#define INTEL_SUBSYS_VID 0x8086
+
+#define HBA_SIGNATURE 0x3344
+#define HBA_SIGNATURE_471 0xCCCC
+#define HBA_SIGNATURE_64BIT 0x0299
+
+#define MBOX_BUSY_WAIT 10 /* wait for up to 10 usec for
+ mailbox to be free */
+#define DEFAULT_INITIATOR_ID 7
+
+#define MAX_SGLIST 64 /* max supported in f/w */
+#define MIN_SGLIST 26 /* guaranteed to support these many */
+#define MAX_COMMANDS 126
+#define CMDID_INT_CMDS MAX_COMMANDS+1 /* make sure CMDID_INT_CMDS
+ is less than max commands
+ supported by any f/w */
+
+#define MAX_CDB_LEN 10
+#define MAX_EXT_CDB_LEN 16 /* we support cdb length up to 16 */
+
+#define DEF_CMD_PER_LUN 63
+#define MAX_CMD_PER_LUN MAX_COMMANDS
+#define MAX_FIRMWARE_STATUS 46
+#define MAX_XFER_PER_CMD (64*1024)
+#define MAX_SECTORS_PER_IO 128
+
+#define MAX_LOGICAL_DRIVES_40LD 40
+#define FC_MAX_PHYSICAL_DEVICES 256
+#define MAX_LOGICAL_DRIVES_8LD 8
+#define MAX_CHANNELS 5
+#define MAX_TARGET 15
+#define MAX_PHYSICAL_DRIVES MAX_CHANNELS*MAX_TARGET
+#define MAX_ROW_SIZE_40LD 32
+#define MAX_ROW_SIZE_8LD 8
+#define MAX_SPAN_DEPTH 8
+
+#define NVIRT_CHAN 4 /* # of virtual channels to represent
+ up to 60 logical drives */
+struct mbox_out {
+ /* 0x0 */ u8 cmd;
+ /* 0x1 */ u8 cmdid;
+ /* 0x2 */ u16 numsectors;
+ /* 0x4 */ u32 lba;
+ /* 0x8 */ u32 xferaddr;
+ /* 0xC */ u8 logdrv;
+ /* 0xD */ u8 numsgelements;
+ /* 0xE */ u8 resvd;
+} __attribute__ ((packed));
+
+struct mbox_in {
+ /* 0xF */ volatile u8 busy;
+ /* 0x10 */ volatile u8 numstatus;
+ /* 0x11 */ volatile u8 status;
+ /* 0x12 */ volatile u8 completed[MAX_FIRMWARE_STATUS];
+ volatile u8 poll;
+ volatile u8 ack;
+} __attribute__ ((packed));
+
+typedef struct {
+ struct mbox_out m_out;
+ struct mbox_in m_in;
+} __attribute__ ((packed)) mbox_t;
+
+typedef struct {
+ u32 xfer_segment_lo;
+ u32 xfer_segment_hi;
+ mbox_t mbox;
+} __attribute__ ((packed)) mbox64_t;
+
+
+/*
+ * Passthru definitions
+ */
+#define MAX_REQ_SENSE_LEN 0x20
+
+typedef struct {
+ u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ u8 ars:1;
+ u8 reserved:3;
+ u8 islogical:1;
+ u8 logdrv; /* if islogical == 1 */
+ u8 channel; /* if islogical == 0 */
+ u8 target; /* if islogical == 0 */
+ u8 queuetag; /* unused */
+ u8 queueaction; /* unused */
+ u8 cdb[MAX_CDB_LEN];
+ u8 cdblen;
+ u8 reqsenselen;
+ u8 reqsensearea[MAX_REQ_SENSE_LEN];
+ u8 numsgelements;
+ u8 scsistatus;
+ u32 dataxferaddr;
+ u32 dataxferlen;
+} __attribute__ ((packed)) mega_passthru;
+
+
+/*
+ * Extended passthru: support CDB > 10 bytes
+ */
+typedef struct {
+ u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ u8 ars:1;
+ u8 rsvd1:1;
+ u8 cd_rom:1;
+ u8 rsvd2:1;
+ u8 islogical:1;
+ u8 logdrv; /* if islogical == 1 */
+ u8 channel; /* if islogical == 0 */
+ u8 target; /* if islogical == 0 */
+ u8 queuetag; /* unused */
+ u8 queueaction; /* unused */
+ u8 cdblen;
+ u8 rsvd3;
+ u8 cdb[MAX_EXT_CDB_LEN];
+ u8 numsgelements;
+ u8 status;
+ u8 reqsenselen;
+ u8 reqsensearea[MAX_REQ_SENSE_LEN];
+ u8 rsvd4;
+ u32 dataxferaddr;
+ u32 dataxferlen;
+} __attribute__ ((packed)) mega_ext_passthru;
+
+typedef struct {
+ u64 address;
+ u32 length;
+} __attribute__ ((packed)) mega_sgl64;
+
+typedef struct {
+ u32 address;
+ u32 length;
+} __attribute__ ((packed)) mega_sglist;
+
+
+/* Queued command data */
+typedef struct {
+ int idx;
+ u32 state;
+ struct list_head list;
+ u8 raw_mbox[66];
+ u32 dma_type;
+ u32 dma_direction;
+
+ Scsi_Cmnd *cmd;
+ dma_addr_t dma_h_bulkdata;
+ dma_addr_t dma_h_sgdata;
+
+ mega_sglist *sgl;
+ mega_sgl64 *sgl64;
+ dma_addr_t sgl_dma_addr;
+
+ mega_passthru *pthru;
+ dma_addr_t pthru_dma_addr;
+ mega_ext_passthru *epthru;
+ dma_addr_t epthru_dma_addr;
+} scb_t;
+
+/*
+ * Flags to follow the scb as it transitions between various stages
+ */
+#define SCB_FREE 0x0000 /* on the free list */
+#define SCB_ACTIVE 0x0001 /* off the free list */
+#define SCB_PENDQ 0x0002 /* on the pending queue */
+#define SCB_ISSUED 0x0004 /* issued - owner f/w */
+#define SCB_ABORT 0x0008 /* Got an abort for this one */
+#define SCB_RESET 0x0010 /* Got a reset for this one */
+
+/*
+ * Utilities declare this strcture size as 1024 bytes. So more fields can
+ * be added in future.
+ */
+typedef struct {
+ u32 data_size; /* current size in bytes (not including resvd) */
+
+ u32 config_signature;
+ /* Current value is 0x00282008
+ * 0x28=MAX_LOGICAL_DRIVES,
+ * 0x20=Number of stripes and
+ * 0x08=Number of spans */
+
+ u8 fw_version[16]; /* printable ASCI string */
+ u8 bios_version[16]; /* printable ASCI string */
+ u8 product_name[80]; /* printable ASCI string */
+
+ u8 max_commands; /* Max. concurrent commands supported */
+ u8 nchannels; /* Number of SCSI Channels detected */
+ u8 fc_loop_present; /* Number of Fibre Loops detected */
+ u8 mem_type; /* EDO, FPM, SDRAM etc */
+
+ u32 signature;
+ u16 dram_size; /* In terms of MB */
+ u16 subsysid;
+
+ u16 subsysvid;
+ u8 notify_counters;
+ u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */
+} __attribute__ ((packed)) mega_product_info;
+
+struct notify {
+ u32 global_counter; /* Any change increments this counter */
+
+ u8 param_counter; /* Indicates any params changed */
+ u8 param_id; /* Param modified - defined below */
+ u16 param_val; /* New val of last param modified */
+
+ u8 write_config_counter; /* write config occurred */
+ u8 write_config_rsvd[3];
+
+ u8 ldrv_op_counter; /* Indicates ldrv op started/completed */
+ u8 ldrv_opid; /* ldrv num */
+ u8 ldrv_opcmd; /* ldrv operation - defined below */
+ u8 ldrv_opstatus; /* status of the operation */
+
+ u8 ldrv_state_counter; /* Indicates change of ldrv state */
+ u8 ldrv_state_id; /* ldrv num */
+ u8 ldrv_state_new; /* New state */
+ u8 ldrv_state_old; /* old state */
+
+ u8 pdrv_state_counter; /* Indicates change of ldrv state */
+ u8 pdrv_state_id; /* pdrv id */
+ u8 pdrv_state_new; /* New state */
+ u8 pdrv_state_old; /* old state */
+
+ u8 pdrv_fmt_counter; /* Indicates pdrv format started/over */
+ u8 pdrv_fmt_id; /* pdrv id */
+ u8 pdrv_fmt_val; /* format started/over */
+ u8 pdrv_fmt_rsvd;
+
+ u8 targ_xfer_counter; /* Indicates SCSI-2 Xfer rate change */
+ u8 targ_xfer_id; /* pdrv Id */
+ u8 targ_xfer_val; /* new Xfer params of last pdrv */
+ u8 targ_xfer_rsvd;
+
+ u8 fcloop_id_chg_counter; /* Indicates loopid changed */
+ u8 fcloopid_pdrvid; /* pdrv id */
+ u8 fcloop_id0; /* loopid on fc loop 0 */
+ u8 fcloop_id1; /* loopid on fc loop 1 */
+
+ u8 fcloop_state_counter; /* Indicates loop state changed */
+ u8 fcloop_state0; /* state of fc loop 0 */
+ u8 fcloop_state1; /* state of fc loop 1 */
+ u8 fcloop_state_rsvd;
+} __attribute__ ((packed));
+
+#define MAX_NOTIFY_SIZE 0x80
+#define CUR_NOTIFY_SIZE sizeof(struct notify)
+
+typedef struct {
+ u32 data_size; /* current size in bytes (not including resvd) */
+
+ struct notify notify;
+
+ u8 notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE];
+
+ u8 rebuild_rate; /* Rebuild rate (0% - 100%) */
+ u8 cache_flush_interval; /* In terms of Seconds */
+ u8 sense_alert;
+ u8 drive_insert_count; /* drive insertion count */
+
+ u8 battery_status;
+ u8 num_ldrv; /* No. of Log Drives configured */
+ u8 recon_state[MAX_LOGICAL_DRIVES_40LD / 8]; /* State of
+ reconstruct */
+ u16 ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8]; /* logdrv
+ Status */
+
+ u32 ldrv_size[MAX_LOGICAL_DRIVES_40LD];/* Size of each log drv */
+ u8 ldrv_prop[MAX_LOGICAL_DRIVES_40LD];
+ u8 ldrv_state[MAX_LOGICAL_DRIVES_40LD];/* State of log drives */
+ u8 pdrv_state[FC_MAX_PHYSICAL_DEVICES];/* State of phys drvs. */
+ u16 pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16];
+
+ u8 targ_xfer[80]; /* phys device transfer rate */
+ u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */
+} __attribute__ ((packed)) mega_inquiry3;
+
+
+/* Structures */
+typedef struct {
+ u8 max_commands; /* Max concurrent commands supported */
+ u8 rebuild_rate; /* Rebuild rate - 0% thru 100% */
+ u8 max_targ_per_chan; /* Max targ per channel */
+ u8 nchannels; /* Number of channels on HBA */
+ u8 fw_version[4]; /* Firmware version */
+ u16 age_of_flash; /* Number of times FW has been flashed */
+ u8 chip_set_value; /* Contents of 0xC0000832 */
+ u8 dram_size; /* In MB */
+ u8 cache_flush_interval; /* in seconds */
+ u8 bios_version[4];
+ u8 board_type;
+ u8 sense_alert;
+ u8 write_config_count; /* Increase with every configuration
+ change */
+ u8 drive_inserted_count; /* Increase with every drive inserted
+ */
+ u8 inserted_drive; /* Channel:Id of inserted drive */
+ u8 battery_status; /*
+ * BIT 0: battery module missing
+ * BIT 1: VBAD
+ * BIT 2: temprature high
+ * BIT 3: battery pack missing
+ * BIT 4,5:
+ * 00 - charge complete
+ * 01 - fast charge in progress
+ * 10 - fast charge fail
+ * 11 - undefined
+ * Bit 6: counter > 1000
+ * Bit 7: Undefined
+ */
+ u8 dec_fault_bus_info;
+} __attribute__ ((packed)) mega_adp_info;
+
+
+typedef struct {
+ u8 num_ldrv; /* Number of logical drives configured */
+ u8 rsvd[3];
+ u32 ldrv_size[MAX_LOGICAL_DRIVES_8LD];
+ u8 ldrv_prop[MAX_LOGICAL_DRIVES_8LD];
+ u8 ldrv_state[MAX_LOGICAL_DRIVES_8LD];
+} __attribute__ ((packed)) mega_ldrv_info;
+
+typedef struct {
+ u8 pdrv_state[MAX_PHYSICAL_DRIVES];
+ u8 rsvd;
+} __attribute__ ((packed)) mega_pdrv_info;
+
+/* RAID inquiry: Mailbox command 0x05*/
+typedef struct {
+ mega_adp_info adapter_info;
+ mega_ldrv_info logdrv_info;
+ mega_pdrv_info pdrv_info;
+} __attribute__ ((packed)) mraid_inquiry;
+
+
+/* RAID extended inquiry: Mailbox command 0x04*/
+typedef struct {
+ mraid_inquiry raid_inq;
+ u16 phys_drv_format[MAX_CHANNELS];
+ u8 stack_attn;
+ u8 modem_status;
+ u8 rsvd[2];
+} __attribute__ ((packed)) mraid_ext_inquiry;
+
+
+typedef struct {
+ u8 channel;
+ u8 target;
+}__attribute__ ((packed)) adp_device;
+
+typedef struct {
+ u32 start_blk; /* starting block */
+ u32 num_blks; /* # of blocks */
+ adp_device device[MAX_ROW_SIZE_40LD];
+}__attribute__ ((packed)) adp_span_40ld;
+
+typedef struct {
+ u32 start_blk; /* starting block */
+ u32 num_blks; /* # of blocks */
+ adp_device device[MAX_ROW_SIZE_8LD];
+}__attribute__ ((packed)) adp_span_8ld;
+
+typedef struct {
+ u8 span_depth; /* Total # of spans */
+ u8 level; /* RAID level */
+ u8 read_ahead; /* read ahead, no read ahead, adaptive read
+ ahead */
+ u8 stripe_sz; /* Encoded stripe size */
+ u8 status; /* Status of the logical drive */
+ u8 write_mode; /* write mode, write_through/write_back */
+ u8 direct_io; /* direct io or through cache */
+ u8 row_size; /* Number of stripes in a row */
+} __attribute__ ((packed)) logdrv_param;
+
+typedef struct {
+ logdrv_param lparam;
+ adp_span_40ld span[MAX_SPAN_DEPTH];
+}__attribute__ ((packed)) logdrv_40ld;
+
+typedef struct {
+ logdrv_param lparam;
+ adp_span_8ld span[MAX_SPAN_DEPTH];
+}__attribute__ ((packed)) logdrv_8ld;
+
+typedef struct {
+ u8 type; /* Type of the device */
+ u8 cur_status; /* current status of the device */
+ u8 tag_depth; /* Level of tagging */
+ u8 sync_neg; /* sync negotiation - ENABLE or DISBALE */
+ u32 size; /* configurable size in terms of 512 byte
+ blocks */
+}__attribute__ ((packed)) phys_drv;
+
+typedef struct {
+ u8 nlog_drives; /* number of logical drives */
+ u8 resvd[3];
+ logdrv_40ld ldrv[MAX_LOGICAL_DRIVES_40LD];
+ phys_drv pdrv[MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_40ld;
+
+typedef struct {
+ u8 nlog_drives; /* number of logical drives */
+ u8 resvd[3];
+ logdrv_8ld ldrv[MAX_LOGICAL_DRIVES_8LD];
+ phys_drv pdrv[MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_8ld;
+
+
+/*
+ * User ioctl structure.
+ * This structure will be used for Traditional Method ioctl interface
+ * commands (0x80),Alternate Buffer Method (0x81) ioctl commands and the
+ * Driver ioctls.
+ * The Driver ioctl interface handles the commands at the driver level,
+ * without being sent to the card.
+ */
+/* system call imposed limit. Change accordingly */
+#define IOCTL_MAX_DATALEN 4096
+
+struct uioctl_t {
+ u32 inlen;
+ u32 outlen;
+ union {
+ u8 fca[16];
+ struct {
+ u8 opcode;
+ u8 subopcode;
+ u16 adapno;
+#if BITS_PER_LONG == 32
+ u8 *buffer;
+ u8 pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ u8 *buffer;
+#endif
+ u32 length;
+ } __attribute__ ((packed)) fcs;
+ } __attribute__ ((packed)) ui;
+ u8 mbox[18]; /* 16 bytes + 2 status bytes */
+ mega_passthru pthru;
+#if BITS_PER_LONG == 32
+ char __user *data; /* buffer <= 4096 for 0x80 commands */
+ char pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ char __user *data;
+#endif
+} __attribute__ ((packed));
+
+/*
+ * struct mcontroller is used to pass information about the controllers in the
+ * system. Its upto the application how to use the information. We are passing
+ * as much info about the cards as possible and useful. Before issuing the
+ * call to find information about the cards, the applicaiton needs to issue a
+ * ioctl first to find out the number of controllers in the system.
+ */
+#define MAX_CONTROLLERS 32
+
+struct mcontroller {
+ u64 base;
+ u8 irq;
+ u8 numldrv;
+ u8 pcibus;
+ u16 pcidev;
+ u8 pcifun;
+ u16 pciid;
+ u16 pcivendor;
+ u8 pcislot;
+ u32 uid;
+};
+
+/*
+ * mailbox structure used for internal commands
+ */
+typedef struct {
+ u8 cmd;
+ u8 cmdid;
+ u8 opcode;
+ u8 subopcode;
+ u32 lba;
+ u32 xferaddr;
+ u8 logdrv;
+ u8 rsvd[3];
+ u8 numstatus;
+ u8 status;
+} __attribute__ ((packed)) megacmd_t;
+
+/*
+ * Defines for Driver IOCTL interface
+ */
+#define MEGAIOC_MAGIC 'm'
+
+#define MEGAIOC_QNADAP 'm' /* Query # of adapters */
+#define MEGAIOC_QDRVRVER 'e' /* Query driver version */
+#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */
+#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) )
+#define GETADAP(mkadap) ( (mkadap) ^ MEGAIOC_MAGIC << 8 )
+
+/*
+ * Definition for the new ioctl interface (NIT)
+ */
+
+/*
+ * Vendor specific Group-7 commands
+ */
+#define VENDOR_SPECIFIC_COMMANDS 0xE0
+#define MEGA_INTERNAL_CMD VENDOR_SPECIFIC_COMMANDS + 0x01
+
+/*
+ * The ioctl command. No other command shall be used for this interface
+ */
+#define USCSICMD VENDOR_SPECIFIC_COMMANDS
+
+/*
+ * Data direction flags
+ */
+#define UIOC_RD 0x00001
+#define UIOC_WR 0x00002
+
+/*
+ * ioctl opcodes
+ */
+#define MBOX_CMD 0x00000 /* DCMD or passthru command */
+#define GET_DRIVER_VER 0x10000 /* Get driver version */
+#define GET_N_ADAP 0x20000 /* Get number of adapters */
+#define GET_ADAP_INFO 0x30000 /* Get information about a adapter */
+#define GET_CAP 0x40000 /* Get ioctl capabilities */
+#define GET_STATS 0x50000 /* Get statistics, including error info */
+
+
+/*
+ * The ioctl structure.
+ * MBOX macro converts a nitioctl_t structure to megacmd_t pointer and
+ * MBOX_P macro converts a nitioctl_t pointer to megacmd_t pointer.
+ */
+typedef struct {
+ char signature[8]; /* Must contain "MEGANIT" */
+ u32 opcode; /* opcode for the command */
+ u32 adapno; /* adapter number */
+ union {
+ u8 __raw_mbox[18];
+ void __user *__uaddr; /* xferaddr for non-mbox cmds */
+ }__ua;
+
+#define uioc_rmbox __ua.__raw_mbox
+#define MBOX(uioc) ((megacmd_t *)&((uioc).__ua.__raw_mbox[0]))
+#define MBOX_P(uioc) ((megacmd_t __user *)&((uioc)->__ua.__raw_mbox[0]))
+#define uioc_uaddr __ua.__uaddr
+
+ u32 xferlen; /* xferlen for DCMD and non-mbox
+ commands */
+ u32 flags; /* data direction flags */
+}nitioctl_t;
+
+
+/*
+ * I/O statistics for some applications like SNMP agent. The caller must
+ * provide the number of logical drives for which status should be reported.
+ */
+typedef struct {
+ int num_ldrv; /* Number for logical drives for which the
+ status should be reported. */
+ u32 nreads[MAX_LOGICAL_DRIVES_40LD]; /* number of reads for
+ each logical drive */
+ u32 nreadblocks[MAX_LOGICAL_DRIVES_40LD]; /* number of blocks
+ read for each logical
+ drive */
+ u32 nwrites[MAX_LOGICAL_DRIVES_40LD]; /* number of writes
+ for each logical
+ drive */
+ u32 nwriteblocks[MAX_LOGICAL_DRIVES_40LD]; /* number of blocks
+ writes for each
+ logical drive */
+ u32 rd_errors[MAX_LOGICAL_DRIVES_40LD]; /* number of read
+ errors for each
+ logical drive */
+ u32 wr_errors[MAX_LOGICAL_DRIVES_40LD]; /* number of write
+ errors for each
+ logical drive */
+}megastat_t;
+
+
+struct private_bios_data {
+ u8 geometry:4; /*
+ * bits 0-3 - BIOS geometry
+ * 0x0001 - 1GB
+ * 0x0010 - 2GB
+ * 0x1000 - 8GB
+ * Others values are invalid
+ */
+ u8 unused:4; /* bits 4-7 are unused */
+ u8 boot_drv; /*
+ * logical drive set as boot drive
+ * 0..7 - for 8LD cards
+ * 0..39 - for 40LD cards
+ */
+ u8 rsvd[12];
+ u16 cksum; /* 0-(sum of first 13 bytes of this structure) */
+} __attribute__ ((packed));
+
+
+
+
+/*
+ * Mailbox and firmware commands and subopcodes used in this driver.
+ */
+
+#define MEGA_MBOXCMD_LREAD 0x01
+#define MEGA_MBOXCMD_LWRITE 0x02
+#define MEGA_MBOXCMD_PASSTHRU 0x03
+#define MEGA_MBOXCMD_ADPEXTINQ 0x04
+#define MEGA_MBOXCMD_ADAPTERINQ 0x05
+#define MEGA_MBOXCMD_LREAD64 0xA7
+#define MEGA_MBOXCMD_LWRITE64 0xA8
+#define MEGA_MBOXCMD_PASSTHRU64 0xC3
+#define MEGA_MBOXCMD_EXTPTHRU 0xE3
+
+#define MAIN_MISC_OPCODE 0xA4 /* f/w misc opcode */
+#define GET_MAX_SG_SUPPORT 0x01 /* get max sg len supported by f/w */
+
+#define FC_NEW_CONFIG 0xA1
+#define NC_SUBOP_PRODUCT_INFO 0x0E
+#define NC_SUBOP_ENQUIRY3 0x0F
+#define ENQ3_GET_SOLICITED_FULL 0x02
+#define OP_DCMD_READ_CONFIG 0x04
+#define NEW_READ_CONFIG_8LD 0x67
+#define READ_CONFIG_8LD 0x07
+#define FLUSH_ADAPTER 0x0A
+#define FLUSH_SYSTEM 0xFE
+
+/*
+ * Command for random deletion of logical drives
+ */
+#define FC_DEL_LOGDRV 0xA4 /* f/w command */
+#define OP_SUP_DEL_LOGDRV 0x2A /* is feature supported */
+#define OP_GET_LDID_MAP 0x18 /* get ldid and logdrv number map */
+#define OP_DEL_LOGDRV 0x1C /* delete logical drive */
+
+/*
+ * BIOS commands
+ */
+#define IS_BIOS_ENABLED 0x62
+#define GET_BIOS 0x01
+#define CHNL_CLASS 0xA9
+#define GET_CHNL_CLASS 0x00
+#define SET_CHNL_CLASS 0x01
+#define CH_RAID 0x01
+#define CH_SCSI 0x00
+#define BIOS_PVT_DATA 0x40
+#define GET_BIOS_PVT_DATA 0x00
+
+
+/*
+ * Commands to support clustering
+ */
+#define MEGA_GET_TARGET_ID 0x7D
+#define MEGA_CLUSTER_OP 0x70
+#define MEGA_GET_CLUSTER_MODE 0x02
+#define MEGA_CLUSTER_CMD 0x6E
+#define MEGA_RESERVE_LD 0x01
+#define MEGA_RELEASE_LD 0x02
+#define MEGA_RESET_RESERVATIONS 0x03
+#define MEGA_RESERVATION_STATUS 0x04
+#define MEGA_RESERVE_PD 0x05
+#define MEGA_RELEASE_PD 0x06
+
+
+/*
+ * Module battery status
+ */
+#define MEGA_BATT_MODULE_MISSING 0x01
+#define MEGA_BATT_LOW_VOLTAGE 0x02
+#define MEGA_BATT_TEMP_HIGH 0x04
+#define MEGA_BATT_PACK_MISSING 0x08
+#define MEGA_BATT_CHARGE_MASK 0x30
+#define MEGA_BATT_CHARGE_DONE 0x00
+#define MEGA_BATT_CHARGE_INPROG 0x10
+#define MEGA_BATT_CHARGE_FAIL 0x20
+#define MEGA_BATT_CYCLES_EXCEEDED 0x40
+
+/*
+ * Physical drive states.
+ */
+#define PDRV_UNCNF 0
+#define PDRV_ONLINE 3
+#define PDRV_FAILED 4
+#define PDRV_RBLD 5
+#define PDRV_HOTSPARE 6
+
+
+/*
+ * Raid logical drive states.
+ */
+#define RDRV_OFFLINE 0
+#define RDRV_DEGRADED 1
+#define RDRV_OPTIMAL 2
+#define RDRV_DELETED 3
+
+/*
+ * Read, write and cache policies
+ */
+#define NO_READ_AHEAD 0
+#define READ_AHEAD 1
+#define ADAP_READ_AHEAD 2
+#define WRMODE_WRITE_THRU 0
+#define WRMODE_WRITE_BACK 1
+#define CACHED_IO 0
+#define DIRECT_IO 1
+
+
+#define SCSI_LIST(scp) ((struct list_head *)(&(scp)->SCp))
+
+/*
+ * Each controller's soft state
+ */
+typedef struct {
+ int this_id; /* our id, may set to different than 7 if
+ clustering is available */
+ u32 flag;
+
+ unsigned long base;
+
+ /* mbox64 with mbox not aligned on 16-byte boundry */
+ mbox64_t *una_mbox64;
+ dma_addr_t una_mbox64_dma;
+
+ volatile mbox64_t *mbox64;/* ptr to 64-bit mailbox */
+ volatile mbox_t *mbox; /* ptr to standard mailbox */
+ dma_addr_t mbox_dma;
+
+ struct pci_dev *dev;
+
+ struct list_head free_list;
+ struct list_head pending_list;
+ struct list_head completed_list;
+
+ struct Scsi_Host *host;
+
+#define MEGA_BUFFER_SIZE (2*1024)
+ u8 *mega_buffer;
+ dma_addr_t buf_dma_handle;
+
+ mega_product_info product_info;
+
+ u8 max_cmds;
+ scb_t *scb_list;
+
+ atomic_t pend_cmds; /* maintain a counter for pending
+ commands in firmware */
+
+#if MEGA_HAVE_STATS
+ u32 nreads[MAX_LOGICAL_DRIVES_40LD];
+ u32 nreadblocks[MAX_LOGICAL_DRIVES_40LD];
+ u32 nwrites[MAX_LOGICAL_DRIVES_40LD];
+ u32 nwriteblocks[MAX_LOGICAL_DRIVES_40LD];
+ u32 rd_errors[MAX_LOGICAL_DRIVES_40LD];
+ u32 wr_errors[MAX_LOGICAL_DRIVES_40LD];
+#endif
+
+ /* Host adapter parameters */
+ u8 numldrv;
+ u8 fw_version[7];
+ u8 bios_version[7];
+
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *controller_proc_dir_entry;
+ struct proc_dir_entry *proc_read;
+ struct proc_dir_entry *proc_stat;
+ struct proc_dir_entry *proc_mbox;
+
+#if MEGA_HAVE_ENH_PROC
+ struct proc_dir_entry *proc_rr;
+ struct proc_dir_entry *proc_battery;
+#define MAX_PROC_CHANNELS 4
+ struct proc_dir_entry *proc_pdrvstat[MAX_PROC_CHANNELS];
+ struct proc_dir_entry *proc_rdrvstat[MAX_PROC_CHANNELS];
+#endif
+
+#endif
+
+ int has_64bit_addr; /* are we using 64-bit addressing */
+ int support_ext_cdb;
+ int boot_ldrv_enabled;
+ int boot_ldrv;
+ int boot_pdrv_enabled; /* boot from physical drive */
+ int boot_pdrv_ch; /* boot physical drive channel */
+ int boot_pdrv_tgt; /* boot physical drive target */
+
+
+ int support_random_del; /* Do we support random deletion of
+ logdrvs */
+ int read_ldidmap; /* set after logical drive deltion. The
+ logical drive number must be read from the
+ map */
+ atomic_t quiescent; /* a stage reached when delete logical
+ drive needs to be done. Stop
+ sending requests to the hba till
+ delete operation is completed */
+ spinlock_t lock;
+
+ u8 logdrv_chan[MAX_CHANNELS+NVIRT_CHAN]; /* logical drive are on
+ what channels. */
+ int mega_ch_class;
+
+ u8 sglen; /* f/w supported scatter-gather list length */
+
+ scb_t int_scb;
+ Scsi_Cmnd int_scmd;
+ struct semaphore int_mtx; /* To synchronize the internal
+ commands */
+ wait_queue_head_t int_waitq; /* wait queue for internal
+ cmds */
+
+ int has_cluster; /* cluster support on this HBA */
+}adapter_t;
+
+
+struct mega_hbas {
+ int is_bios_enabled;
+ adapter_t *hostdata_addr;
+};
+
+
+/*
+ * For state flag. Do not use LSB(8 bits) which are
+ * reserved for storing info about channels.
+ */
+#define IN_ABORT 0x80000000L
+#define IN_RESET 0x40000000L
+#define BOARD_MEMMAP 0x20000000L
+#define BOARD_IOMAP 0x10000000L
+#define BOARD_40LD 0x08000000L
+#define BOARD_64BIT 0x04000000L
+
+#define INTR_VALID 0x40
+
+#define PCI_CONF_AMISIG 0xa0
+#define PCI_CONF_AMISIG64 0xa4
+
+
+#define MEGA_DMA_TYPE_NONE 0xFFFF
+#define MEGA_BULK_DATA 0x0001
+#define MEGA_SGLIST 0x0002
+
+/*
+ * lockscope definitions, callers can specify the lock scope with this data
+ * type. LOCK_INT would mean the caller has not acquired the lock before
+ * making the call and LOCK_EXT would mean otherwise.
+ */
+typedef enum { LOCK_INT, LOCK_EXT } lockscope_t;
+
+/*
+ * Parameters for the io-mapped controllers
+ */
+
+/* I/O Port offsets */
+#define CMD_PORT 0x00
+#define ACK_PORT 0x00
+#define TOGGLE_PORT 0x01
+#define INTR_PORT 0x0a
+
+#define MBOX_BUSY_PORT 0x00
+#define MBOX_PORT0 0x04
+#define MBOX_PORT1 0x05
+#define MBOX_PORT2 0x06
+#define MBOX_PORT3 0x07
+#define ENABLE_MBOX_REGION 0x0B
+
+/* I/O Port Values */
+#define ISSUE_BYTE 0x10
+#define ACK_BYTE 0x08
+#define ENABLE_INTR_BYTE 0xc0
+#define DISABLE_INTR_BYTE 0x00
+#define VALID_INTR_BYTE 0x40
+#define MBOX_BUSY_BYTE 0x10
+#define ENABLE_MBOX_BYTE 0x00
+
+
+/* Setup some port macros here */
+#define issue_command(adapter) \
+ outb_p(ISSUE_BYTE, (adapter)->base + CMD_PORT)
+
+#define irq_state(adapter) inb_p((adapter)->base + INTR_PORT)
+
+#define set_irq_state(adapter, value) \
+ outb_p((value), (adapter)->base + INTR_PORT)
+
+#define irq_ack(adapter) \
+ outb_p(ACK_BYTE, (adapter)->base + ACK_PORT)
+
+#define irq_enable(adapter) \
+ outb_p(ENABLE_INTR_BYTE, (adapter)->base + TOGGLE_PORT)
+
+#define irq_disable(adapter) \
+ outb_p(DISABLE_INTR_BYTE, (adapter)->base + TOGGLE_PORT)
+
+
+/*
+ * This is our SYSDEP area. All kernel specific detail should be placed here -
+ * as much as possible
+ */
+
+/*
+ * End of SYSDEP area
+ */
+
+const char *megaraid_info (struct Scsi_Host *);
+
+static int mega_query_adapter(adapter_t *);
+static int issue_scb(adapter_t *, scb_t *);
+static int mega_setup_mailbox(adapter_t *);
+
+static int megaraid_queue (Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+static scb_t * mega_build_cmd(adapter_t *, Scsi_Cmnd *, int *);
+static void __mega_runpendq(adapter_t *);
+static int issue_scb_block(adapter_t *, u_char *);
+
+static irqreturn_t megaraid_isr_memmapped(int, void *, struct pt_regs *);
+static irqreturn_t megaraid_isr_iomapped(int, void *, struct pt_regs *);
+
+static void mega_free_scb(adapter_t *, scb_t *);
+
+static int megaraid_abort(Scsi_Cmnd *);
+static int megaraid_reset(Scsi_Cmnd *);
+static int megaraid_abort_and_reset(adapter_t *, Scsi_Cmnd *, int);
+static int megaraid_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int []);
+static int mega_print_inquiry(char *, char *);
+
+static int mega_build_sglist (adapter_t *adapter, scb_t *scb,
+ u32 *buffer, u32 *length);
+static int __mega_busywait_mbox (adapter_t *);
+static void mega_rundoneq (adapter_t *);
+static void mega_cmd_done(adapter_t *, u8 [], int, int);
+static inline void mega_free_sgl (adapter_t *adapter);
+static void mega_8_to_40ld (mraid_inquiry *inquiry,
+ mega_inquiry3 *enquiry3, mega_product_info *);
+
+static int megadev_open (struct inode *, struct file *);
+static int megadev_ioctl (struct inode *, struct file *, unsigned int,
+ unsigned long);
+static int mega_m_to_n(void __user *, nitioctl_t *);
+static int mega_n_to_m(void __user *, megacmd_t *);
+
+static int mega_init_scb (adapter_t *);
+
+static int mega_is_bios_enabled (adapter_t *);
+
+#ifdef CONFIG_PROC_FS
+static void mega_create_proc_entry(int, struct proc_dir_entry *);
+static int proc_read_config(char *, char **, off_t, int, int *, void *);
+static int proc_read_stat(char *, char **, off_t, int, int *, void *);
+static int proc_read_mbox(char *, char **, off_t, int, int *, void *);
+static int proc_rebuild_rate(char *, char **, off_t, int, int *, void *);
+static int proc_battery(char *, char **, off_t, int, int *, void *);
+static int proc_pdrv_ch0(char *, char **, off_t, int, int *, void *);
+static int proc_pdrv_ch1(char *, char **, off_t, int, int *, void *);
+static int proc_pdrv_ch2(char *, char **, off_t, int, int *, void *);
+static int proc_pdrv_ch3(char *, char **, off_t, int, int *, void *);
+static int proc_pdrv(adapter_t *, char *, int);
+static int proc_rdrv_10(char *, char **, off_t, int, int *, void *);
+static int proc_rdrv_20(char *, char **, off_t, int, int *, void *);
+static int proc_rdrv_30(char *, char **, off_t, int, int *, void *);
+static int proc_rdrv_40(char *, char **, off_t, int, int *, void *);
+static int proc_rdrv(adapter_t *, char *, int, int);
+#endif
+
+static int mega_adapinq(adapter_t *, dma_addr_t);
+static int mega_internal_dev_inquiry(adapter_t *, u8, u8, dma_addr_t);
+
+static int mega_support_ext_cdb(adapter_t *);
+static mega_passthru* mega_prepare_passthru(adapter_t *, scb_t *,
+ Scsi_Cmnd *, int, int);
+static mega_ext_passthru* mega_prepare_extpassthru(adapter_t *,
+ scb_t *, Scsi_Cmnd *, int, int);
+static void mega_enum_raid_scsi(adapter_t *);
+static void mega_get_boot_drv(adapter_t *);
+static int mega_support_random_del(adapter_t *);
+static int mega_del_logdrv(adapter_t *, int);
+static int mega_do_del_logdrv(adapter_t *, int);
+static void mega_get_max_sgl(adapter_t *);
+static int mega_internal_command(adapter_t *, lockscope_t, megacmd_t *,
+ mega_passthru *);
+static void mega_internal_done(Scsi_Cmnd *);
+static int mega_support_cluster(adapter_t *);
+#endif
+
+/* vi: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid/Kconfig.megaraid b/drivers/scsi/megaraid/Kconfig.megaraid
new file mode 100644
index 000000000000..917d591d90b2
--- /dev/null
+++ b/drivers/scsi/megaraid/Kconfig.megaraid
@@ -0,0 +1,78 @@
+config MEGARAID_NEWGEN
+ bool "LSI Logic New Generation RAID Device Drivers"
+ depends on PCI && SCSI
+ help
+ LSI Logic RAID Device Drivers
+
+config MEGARAID_MM
+ tristate "LSI Logic Management Module (New Driver)"
+ depends on PCI && SCSI && MEGARAID_NEWGEN
+ help
+ Management Module provides ioctl, sysfs support for LSI Logic
+ RAID controllers.
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid_mm
+
+
+config MEGARAID_MAILBOX
+ tristate "LSI Logic MegaRAID Driver (New Driver)"
+ depends on PCI && SCSI && MEGARAID_MM
+ help
+ List of supported controllers
+
+ OEM Product Name VID :DID :SVID:SSID
+ --- ------------ ---- ---- ---- ----
+ Dell PERC3/QC 101E:1960:1028:0471
+ Dell PERC3/DC 101E:1960:1028:0493
+ Dell PERC3/SC 101E:1960:1028:0475
+ Dell PERC3/Di 1028:000E:1028:0123
+ Dell PERC4/SC 1000:1960:1028:0520
+ Dell PERC4/DC 1000:1960:1028:0518
+ Dell PERC4/QC 1000:0407:1028:0531
+ Dell PERC4/Di 1028:000F:1028:014A
+ Dell PERC 4e/Si 1028:0013:1028:016c
+ Dell PERC 4e/Di 1028:0013:1028:016d
+ Dell PERC 4e/Di 1028:0013:1028:016e
+ Dell PERC 4e/Di 1028:0013:1028:016f
+ Dell PERC 4e/Di 1028:0013:1028:0170
+ Dell PERC 4e/DC 1000:0408:1028:0002
+ Dell PERC 4e/SC 1000:0408:1028:0001
+ LSI MegaRAID SCSI 320-0 1000:1960:1000:A520
+ LSI MegaRAID SCSI 320-1 1000:1960:1000:0520
+ LSI MegaRAID SCSI 320-2 1000:1960:1000:0518
+ LSI MegaRAID SCSI 320-0X 1000:0407:1000:0530
+ LSI MegaRAID SCSI 320-2X 1000:0407:1000:0532
+ LSI MegaRAID SCSI 320-4X 1000:0407:1000:0531
+ LSI MegaRAID SCSI 320-1E 1000:0408:1000:0001
+ LSI MegaRAID SCSI 320-2E 1000:0408:1000:0002
+ LSI MegaRAID SATA 150-4 1000:1960:1000:4523
+ LSI MegaRAID SATA 150-6 1000:1960:1000:0523
+ LSI MegaRAID SATA 300-4X 1000:0409:1000:3004
+ LSI MegaRAID SATA 300-8X 1000:0409:1000:3008
+ INTEL RAID Controller SRCU42X 1000:0407:8086:0532
+ INTEL RAID Controller SRCS16 1000:1960:8086:0523
+ INTEL RAID Controller SRCU42E 1000:0408:8086:0002
+ INTEL RAID Controller SRCZCRX 1000:0407:8086:0530
+ INTEL RAID Controller SRCS28X 1000:0409:8086:3008
+ INTEL RAID Controller SROMBU42E 1000:0408:8086:3431
+ INTEL RAID Controller SROMBU42E 1000:0408:8086:3499
+ INTEL RAID Controller SRCU51L 1000:1960:8086:0520
+ FSC MegaRAID PCI Express ROMB 1000:0408:1734:1065
+ ACER MegaRAID ROMB-2E 1000:0408:1025:004D
+ NEC MegaRAID PCI Express ROMB 1000:0408:1033:8287
+
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid_mbox
+
+if MEGARAID_NEWGEN=n
+config MEGARAID_LEGACY
+ tristate "LSI Logic Legacy MegaRAID Driver"
+ depends on PCI && SCSI
+ help
+ This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490
+ and 467 SCSI host adapters. This driver also support the all U320
+ RAID controllers
+
+ To compile this driver as a module, choose M here: the
+ module will be called megaraid
+endif
diff --git a/drivers/scsi/megaraid/Makefile b/drivers/scsi/megaraid/Makefile
new file mode 100644
index 000000000000..6dd99f275722
--- /dev/null
+++ b/drivers/scsi/megaraid/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o
+obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o
diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h
new file mode 100644
index 000000000000..3052869f51f4
--- /dev/null
+++ b/drivers/scsi/megaraid/mbox_defs.h
@@ -0,0 +1,790 @@
+/*
+ *
+ * Linux MegaRAID Unified device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : mbox_defs.h
+ *
+ */
+#ifndef _MRAID_MBOX_DEFS_H_
+#define _MRAID_MBOX_DEFS_H_
+
+#include <linux/types.h>
+
+/*
+ * Commands and states for mailbox based controllers
+ */
+
+#define MBOXCMD_LREAD 0x01
+#define MBOXCMD_LWRITE 0x02
+#define MBOXCMD_PASSTHRU 0x03
+#define MBOXCMD_ADPEXTINQ 0x04
+#define MBOXCMD_ADAPTERINQ 0x05
+#define MBOXCMD_LREAD64 0xA7
+#define MBOXCMD_LWRITE64 0xA8
+#define MBOXCMD_PASSTHRU64 0xC3
+#define MBOXCMD_EXTPTHRU 0xE3
+
+#define MAIN_MISC_OPCODE 0xA4
+#define GET_MAX_SG_SUPPORT 0x01
+#define SUPPORT_EXT_CDB 0x16
+
+#define FC_NEW_CONFIG 0xA1
+#define NC_SUBOP_PRODUCT_INFO 0x0E
+#define NC_SUBOP_ENQUIRY3 0x0F
+#define ENQ3_GET_SOLICITED_FULL 0x02
+#define OP_DCMD_READ_CONFIG 0x04
+#define NEW_READ_CONFIG_8LD 0x67
+#define READ_CONFIG_8LD 0x07
+#define FLUSH_ADAPTER 0x0A
+#define FLUSH_SYSTEM 0xFE
+
+/*
+ * Command for random deletion of logical drives
+ */
+#define FC_DEL_LOGDRV 0xA4
+#define OP_SUP_DEL_LOGDRV 0x2A
+#define OP_GET_LDID_MAP 0x18
+#define OP_DEL_LOGDRV 0x1C
+
+/*
+ * BIOS commands
+ */
+#define IS_BIOS_ENABLED 0x62
+#define GET_BIOS 0x01
+#define CHNL_CLASS 0xA9
+#define GET_CHNL_CLASS 0x00
+#define SET_CHNL_CLASS 0x01
+#define CH_RAID 0x01
+#define CH_SCSI 0x00
+#define BIOS_PVT_DATA 0x40
+#define GET_BIOS_PVT_DATA 0x00
+
+
+/*
+ * Commands to support clustering
+ */
+#define GET_TARGET_ID 0x7D
+#define CLUSTER_OP 0x70
+#define GET_CLUSTER_MODE 0x02
+#define CLUSTER_CMD 0x6E
+#define RESERVE_LD 0x01
+#define RELEASE_LD 0x02
+#define RESET_RESERVATIONS 0x03
+#define RESERVATION_STATUS 0x04
+#define RESERVE_PD 0x05
+#define RELEASE_PD 0x06
+
+
+/*
+ * Module battery status
+ */
+#define BATTERY_MODULE_MISSING 0x01
+#define BATTERY_LOW_VOLTAGE 0x02
+#define BATTERY_TEMP_HIGH 0x04
+#define BATTERY_PACK_MISSING 0x08
+#define BATTERY_CHARGE_MASK 0x30
+#define BATTERY_CHARGE_DONE 0x00
+#define BATTERY_CHARGE_INPROG 0x10
+#define BATTERY_CHARGE_FAIL 0x20
+#define BATTERY_CYCLES_EXCEEDED 0x40
+
+/*
+ * Physical drive states.
+ */
+#define PDRV_UNCNF 0
+#define PDRV_ONLINE 3
+#define PDRV_FAILED 4
+#define PDRV_RBLD 5
+#define PDRV_HOTSPARE 6
+
+
+/*
+ * Raid logical drive states.
+ */
+#define RDRV_OFFLINE 0
+#define RDRV_DEGRADED 1
+#define RDRV_OPTIMAL 2
+#define RDRV_DELETED 3
+
+/*
+ * Read, write and cache policies
+ */
+#define NO_READ_AHEAD 0
+#define READ_AHEAD 1
+#define ADAP_READ_AHEAD 2
+#define WRMODE_WRITE_THRU 0
+#define WRMODE_WRITE_BACK 1
+#define CACHED_IO 0
+#define DIRECT_IO 1
+
+#define MAX_LOGICAL_DRIVES_8LD 8
+#define MAX_LOGICAL_DRIVES_40LD 40
+#define FC_MAX_PHYSICAL_DEVICES 256
+#define MAX_MBOX_CHANNELS 5
+#define MAX_MBOX_TARGET 15
+#define MBOX_MAX_PHYSICAL_DRIVES MAX_MBOX_CHANNELS*MAX_MBOX_TARGET
+#define MAX_ROW_SIZE_40LD 32
+#define MAX_ROW_SIZE_8LD 8
+#define SPAN_DEPTH_8_SPANS 8
+#define SPAN_DEPTH_4_SPANS 4
+#define MAX_REQ_SENSE_LEN 0x20
+
+
+
+/**
+ * struct mbox_t - Driver and f/w handshake structure.
+ * @cmd : firmware command
+ * @cmdid : command id
+ * @numsectors : number of sectors to be transferred
+ * @lba : Logical Block Address on LD
+ * @xferaddr : DMA address for data transfer
+ * @logdrv : logical drive number
+ * @numsge : number of scatter gather elements in sg list
+ * @resvd : reserved
+ * @busy : f/w busy, must wait to issue more commands.
+ * @numstatus : number of commands completed.
+ * @status : status of the commands completed
+ * @completed : array of completed command ids.
+ * @poll : poll and ack sequence
+ * @ack : poll and ack sequence
+ *
+ * The central handshake structure between the driver and the firmware. This
+ * structure must be allocated by the driver and aligned at 8-byte boundary.
+ */
+#define MBOX_MAX_FIRMWARE_STATUS 46
+typedef struct {
+ uint8_t cmd;
+ uint8_t cmdid;
+ uint16_t numsectors;
+ uint32_t lba;
+ uint32_t xferaddr;
+ uint8_t logdrv;
+ uint8_t numsge;
+ uint8_t resvd;
+ uint8_t busy;
+ uint8_t numstatus;
+ uint8_t status;
+ uint8_t completed[MBOX_MAX_FIRMWARE_STATUS];
+ uint8_t poll;
+ uint8_t ack;
+} __attribute__ ((packed)) mbox_t;
+
+
+/**
+ * mbox64_t - 64-bit extension for the mailbox
+ * @segment_lo : the low 32-bits of the address of the scatter-gather list
+ * @segment_hi : the upper 32-bits of the address of the scatter-gather list
+ * @mbox : 32-bit mailbox, whose xferadder field must be set to
+ * 0xFFFFFFFF
+ *
+ * This is the extension of the 32-bit mailbox to be able to perform DMA
+ * beyond 4GB address range.
+ */
+typedef struct {
+ uint32_t xferaddr_lo;
+ uint32_t xferaddr_hi;
+ mbox_t mbox32;
+} __attribute__ ((packed)) mbox64_t;
+
+/*
+ * mailbox structure used for internal commands
+ */
+typedef struct {
+ u8 cmd;
+ u8 cmdid;
+ u8 opcode;
+ u8 subopcode;
+ u32 lba;
+ u32 xferaddr;
+ u8 logdrv;
+ u8 rsvd[3];
+ u8 numstatus;
+ u8 status;
+} __attribute__ ((packed)) int_mbox_t;
+
+/**
+ * mraid_passthru_t - passthru structure to issue commands to physical devices
+ * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr
+ * @ars : set if ARS required after check condition
+ * @islogical : set if command meant for logical devices
+ * @logdrv : logical drive number if command for LD
+ * @channel : Channel on which physical device is located
+ * @target : SCSI target of the device
+ * @queuetag : unused
+ * @queueaction : unused
+ * @cdb : SCSI CDB
+ * @cdblen : length of the CDB
+ * @reqsenselen : amount of request sense data to be returned
+ * @reqsensearea : Sense information buffer
+ * @numsge : number of scatter-gather elements in the sg list
+ * @scsistatus : SCSI status of the command completed.
+ * @dataxferaddr : DMA data transfer address
+ * @dataxferlen : amount of the data to be transferred.
+ */
+typedef struct {
+ uint8_t timeout :3;
+ uint8_t ars :1;
+ uint8_t reserved :3;
+ uint8_t islogical :1;
+ uint8_t logdrv;
+ uint8_t channel;
+ uint8_t target;
+ uint8_t queuetag;
+ uint8_t queueaction;
+ uint8_t cdb[10];
+ uint8_t cdblen;
+ uint8_t reqsenselen;
+ uint8_t reqsensearea[MAX_REQ_SENSE_LEN];
+ uint8_t numsge;
+ uint8_t scsistatus;
+ uint32_t dataxferaddr;
+ uint32_t dataxferlen;
+} __attribute__ ((packed)) mraid_passthru_t;
+
+typedef struct {
+
+ uint32_t dataxferaddr_lo;
+ uint32_t dataxferaddr_hi;
+ mraid_passthru_t pthru32;
+
+} __attribute__ ((packed)) mega_passthru64_t;
+
+/**
+ * mraid_epassthru_t - passthru structure to issue commands to physical devices
+ * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr
+ * @ars : set if ARS required after check condition
+ * @rsvd1 : reserved field
+ * @cd_rom : (?)
+ * @rsvd2 : reserved field
+ * @islogical : set if command meant for logical devices
+ * @logdrv : logical drive number if command for LD
+ * @channel : Channel on which physical device is located
+ * @target : SCSI target of the device
+ * @queuetag : unused
+ * @queueaction : unused
+ * @cdblen : length of the CDB
+ * @rsvd3 : reserved field
+ * @cdb : SCSI CDB
+ * @numsge : number of scatter-gather elements in the sg list
+ * @status : SCSI status of the command completed.
+ * @reqsenselen : amount of request sense data to be returned
+ * @reqsensearea : Sense information buffer
+ * @rsvd4 : reserved field
+ * @dataxferaddr : DMA data transfer address
+ * @dataxferlen : amount of the data to be transferred.
+ */
+typedef struct {
+ uint8_t timeout :3;
+ uint8_t ars :1;
+ uint8_t rsvd1 :1;
+ uint8_t cd_rom :1;
+ uint8_t rsvd2 :1;
+ uint8_t islogical :1;
+ uint8_t logdrv;
+ uint8_t channel;
+ uint8_t target;
+ uint8_t queuetag;
+ uint8_t queueaction;
+ uint8_t cdblen;
+ uint8_t rsvd3;
+ uint8_t cdb[16];
+ uint8_t numsge;
+ uint8_t status;
+ uint8_t reqsenselen;
+ uint8_t reqsensearea[MAX_REQ_SENSE_LEN];
+ uint8_t rsvd4;
+ uint32_t dataxferaddr;
+ uint32_t dataxferlen;
+} __attribute__ ((packed)) mraid_epassthru_t;
+
+
+/**
+ * mraid_pinfo_t - product info, static information about the controller
+ * @data_size : current size in bytes (not including resvd)
+ * @config_signature : Current value is 0x00282008
+ * @fw_version : Firmware version
+ * @bios_version : version of the BIOS
+ * @product_name : Name given to the controller
+ * @max_commands : Maximum concurrent commands supported
+ * @nchannels : Number of SCSI Channels detected
+ * @fc_loop_present : Number of Fibre Loops detected
+ * @mem_type : EDO, FPM, SDRAM etc
+ * @signature :
+ * @dram_size : In terms of MB
+ * @subsysid : device PCI subsystem ID
+ * @subsysvid : device PCI subsystem vendor ID
+ * @notify_counters :
+ * @pad1k : 135 + 889 resvd = 1024 total size
+ *
+ * This structures holds the information about the controller which is not
+ * expected to change dynamically.
+ *
+ * The current value of config signature is 0x00282008:
+ * 0x28 = MAX_LOGICAL_DRIVES,
+ * 0x20 = Number of stripes and
+ * 0x08 = Number of spans
+ */
+typedef struct {
+ uint32_t data_size;
+ uint32_t config_signature;
+ uint8_t fw_version[16];
+ uint8_t bios_version[16];
+ uint8_t product_name[80];
+ uint8_t max_commands;
+ uint8_t nchannels;
+ uint8_t fc_loop_present;
+ uint8_t mem_type;
+ uint32_t signature;
+ uint16_t dram_size;
+ uint16_t subsysid;
+ uint16_t subsysvid;
+ uint8_t notify_counters;
+ uint8_t pad1k[889];
+} __attribute__ ((packed)) mraid_pinfo_t;
+
+
+/**
+ * mraid_notify_t - the notification structure
+ * @global_counter : Any change increments this counter
+ * @param_counter : Indicates any params changed
+ * @param_id : Param modified - defined below
+ * @param_val : New val of last param modified
+ * @write_config_counter : write config occurred
+ * @write_config_rsvd :
+ * @ldrv_op_counter : Indicates ldrv op started/completed
+ * @ldrv_opid : ldrv num
+ * @ldrv_opcmd : ldrv operation - defined below
+ * @ldrv_opstatus : status of the operation
+ * @ldrv_state_counter : Indicates change of ldrv state
+ * @ldrv_state_id : ldrv num
+ * @ldrv_state_new : New state
+ * @ldrv_state_old : old state
+ * @pdrv_state_counter : Indicates change of ldrv state
+ * @pdrv_state_id : pdrv id
+ * @pdrv_state_new : New state
+ * @pdrv_state_old : old state
+ * @pdrv_fmt_counter : Indicates pdrv format started/over
+ * @pdrv_fmt_id : pdrv id
+ * @pdrv_fmt_val : format started/over
+ * @pdrv_fmt_rsvd :
+ * @targ_xfer_counter : Indicates SCSI-2 Xfer rate change
+ * @targ_xfer_id : pdrv Id
+ * @targ_xfer_val : new Xfer params of last pdrv
+ * @targ_xfer_rsvd :
+ * @fcloop_id_chg_counter : Indicates loopid changed
+ * @fcloopid_pdrvid : pdrv id
+ * @fcloop_id0 : loopid on fc loop 0
+ * @fcloop_id1 : loopid on fc loop 1
+ * @fcloop_state_counter : Indicates loop state changed
+ * @fcloop_state0 : state of fc loop 0
+ * @fcloop_state1 : state of fc loop 1
+ * @fcloop_state_rsvd :
+ */
+typedef struct {
+ uint32_t global_counter;
+ uint8_t param_counter;
+ uint8_t param_id;
+ uint16_t param_val;
+ uint8_t write_config_counter;
+ uint8_t write_config_rsvd[3];
+ uint8_t ldrv_op_counter;
+ uint8_t ldrv_opid;
+ uint8_t ldrv_opcmd;
+ uint8_t ldrv_opstatus;
+ uint8_t ldrv_state_counter;
+ uint8_t ldrv_state_id;
+ uint8_t ldrv_state_new;
+ uint8_t ldrv_state_old;
+ uint8_t pdrv_state_counter;
+ uint8_t pdrv_state_id;
+ uint8_t pdrv_state_new;
+ uint8_t pdrv_state_old;
+ uint8_t pdrv_fmt_counter;
+ uint8_t pdrv_fmt_id;
+ uint8_t pdrv_fmt_val;
+ uint8_t pdrv_fmt_rsvd;
+ uint8_t targ_xfer_counter;
+ uint8_t targ_xfer_id;
+ uint8_t targ_xfer_val;
+ uint8_t targ_xfer_rsvd;
+ uint8_t fcloop_id_chg_counter;
+ uint8_t fcloopid_pdrvid;
+ uint8_t fcloop_id0;
+ uint8_t fcloop_id1;
+ uint8_t fcloop_state_counter;
+ uint8_t fcloop_state0;
+ uint8_t fcloop_state1;
+ uint8_t fcloop_state_rsvd;
+} __attribute__ ((packed)) mraid_notify_t;
+
+
+/**
+ * mraid_inquiry3_t - enquiry for device information
+ *
+ * @data_size : current size in bytes (not including resvd)
+ * @notify :
+ * @notify_rsvd :
+ * @rebuild_rate : rebuild rate (0% - 100%)
+ * @cache_flush_int : cache flush interval in seconds
+ * @sense_alert :
+ * @drive_insert_count : drive insertion count
+ * @battery_status :
+ * @num_ldrv : no. of Log Drives configured
+ * @recon_state : state of reconstruct
+ * @ldrv_op_status : logdrv Status
+ * @ldrv_size : size of each log drv
+ * @ldrv_prop :
+ * @ldrv_state : state of log drives
+ * @pdrv_state : state of phys drvs.
+ * @pdrv_format :
+ * @targ_xfer : phys device transfer rate
+ * @pad1k : 761 + 263reserved = 1024 bytes total size
+ */
+#define MAX_NOTIFY_SIZE 0x80
+#define CUR_NOTIFY_SIZE sizeof(mraid_notify_t)
+
+typedef struct {
+ uint32_t data_size;
+
+ mraid_notify_t notify;
+
+ uint8_t notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE];
+
+ uint8_t rebuild_rate;
+ uint8_t cache_flush_int;
+ uint8_t sense_alert;
+ uint8_t drive_insert_count;
+
+ uint8_t battery_status;
+ uint8_t num_ldrv;
+ uint8_t recon_state[MAX_LOGICAL_DRIVES_40LD / 8];
+ uint16_t ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8];
+
+ uint32_t ldrv_size[MAX_LOGICAL_DRIVES_40LD];
+ uint8_t ldrv_prop[MAX_LOGICAL_DRIVES_40LD];
+ uint8_t ldrv_state[MAX_LOGICAL_DRIVES_40LD];
+ uint8_t pdrv_state[FC_MAX_PHYSICAL_DEVICES];
+ uint16_t pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16];
+
+ uint8_t targ_xfer[80];
+ uint8_t pad1k[263];
+} __attribute__ ((packed)) mraid_inquiry3_t;
+
+
+/**
+ * mraid_adapinfo_t - information about the adapter
+ * @max_commands : max concurrent commands supported
+ * @rebuild_rate : rebuild rate - 0% thru 100%
+ * @max_targ_per_chan : max targ per channel
+ * @nchannels : number of channels on HBA
+ * @fw_version : firmware version
+ * @age_of_flash : number of times FW has been flashed
+ * @chip_set_value : contents of 0xC0000832
+ * @dram_size : in MB
+ * @cache_flush_interval : in seconds
+ * @bios_version :
+ * @board_type :
+ * @sense_alert :
+ * @write_config_count : increase with every configuration change
+ * @drive_inserted_count : increase with every drive inserted
+ * @inserted_drive : channel:Id of inserted drive
+ * @battery_status : bit 0: battery module missing
+ * bit 1: VBAD
+ * bit 2: temprature high
+ * bit 3: battery pack missing
+ * bit 4,5:
+ * 00 - charge complete
+ * 01 - fast charge in progress
+ * 10 - fast charge fail
+ * 11 - undefined
+ * bit 6: counter > 1000
+ * bit 7: Undefined
+ * @dec_fault_bus_info :
+ */
+typedef struct {
+ uint8_t max_commands;
+ uint8_t rebuild_rate;
+ uint8_t max_targ_per_chan;
+ uint8_t nchannels;
+ uint8_t fw_version[4];
+ uint16_t age_of_flash;
+ uint8_t chip_set_value;
+ uint8_t dram_size;
+ uint8_t cache_flush_interval;
+ uint8_t bios_version[4];
+ uint8_t board_type;
+ uint8_t sense_alert;
+ uint8_t write_config_count;
+ uint8_t battery_status;
+ uint8_t dec_fault_bus_info;
+} __attribute__ ((packed)) mraid_adapinfo_t;
+
+
+/**
+ * mraid_ldrv_info_t - information about the logical drives
+ * @nldrv : Number of logical drives configured
+ * @rsvd :
+ * @size : size of each logical drive
+ * @prop :
+ * @state : state of each logical drive
+ */
+typedef struct {
+ uint8_t nldrv;
+ uint8_t rsvd[3];
+ uint32_t size[MAX_LOGICAL_DRIVES_8LD];
+ uint8_t prop[MAX_LOGICAL_DRIVES_8LD];
+ uint8_t state[MAX_LOGICAL_DRIVES_8LD];
+} __attribute__ ((packed)) mraid_ldrv_info_t;
+
+
+/**
+ * mraid_pdrv_info_t - information about the physical drives
+ * @pdrv_state : state of each physical drive
+ */
+typedef struct {
+ uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES];
+ uint8_t rsvd;
+} __attribute__ ((packed)) mraid_pdrv_info_t;
+
+
+/**
+ * mraid_inquiry_t - RAID inquiry, mailbox command 0x05
+ * @mraid_adapinfo_t : adapter information
+ * @mraid_ldrv_info_t : logical drives information
+ * @mraid_pdrv_info_t : physical drives information
+ */
+typedef struct {
+ mraid_adapinfo_t adapter_info;
+ mraid_ldrv_info_t logdrv_info;
+ mraid_pdrv_info_t pdrv_info;
+} __attribute__ ((packed)) mraid_inquiry_t;
+
+
+/**
+ * mraid_extinq_t - RAID extended inquiry, mailbox command 0x04
+ *
+ * @raid_inq : raid inquiry
+ * @phys_drv_format :
+ * @stack_attn :
+ * @modem_status :
+ * @rsvd :
+ */
+typedef struct {
+ mraid_inquiry_t raid_inq;
+ uint16_t phys_drv_format[MAX_MBOX_CHANNELS];
+ uint8_t stack_attn;
+ uint8_t modem_status;
+ uint8_t rsvd[2];
+} __attribute__ ((packed)) mraid_extinq_t;
+
+
+/**
+ * adap_device_t - device information
+ * @channel : channel fpor the device
+ * @target : target ID of the device
+ */
+typedef struct {
+ uint8_t channel;
+ uint8_t target;
+}__attribute__ ((packed)) adap_device_t;
+
+
+/**
+ * adap_span_40ld_t - 40LD span
+ * @start_blk : starting block
+ * @num_blks : number of blocks
+ */
+typedef struct {
+ uint32_t start_blk;
+ uint32_t num_blks;
+ adap_device_t device[MAX_ROW_SIZE_40LD];
+}__attribute__ ((packed)) adap_span_40ld_t;
+
+
+/**
+ * adap_span_8ld_t - 8LD span
+ * @start_blk : starting block
+ * @num_blks : number of blocks
+ */
+typedef struct {
+ uint32_t start_blk;
+ uint32_t num_blks;
+ adap_device_t device[MAX_ROW_SIZE_8LD];
+}__attribute__ ((packed)) adap_span_8ld_t;
+
+
+/**
+ * logdrv_param_t - logical drives parameters
+ *
+ * @span_depth : total number of spans
+ * @level : RAID level
+ * @read_ahead : read ahead, no read ahead, adaptive read ahead
+ * @stripe_sz : encoded stripe size
+ * @status : status of the logical drive
+ * @write_mode : write mode, write_through/write_back
+ * @direct_io : direct io or through cache
+ * @row_size : number of stripes in a row
+ */
+typedef struct {
+ uint8_t span_depth;
+ uint8_t level;
+ uint8_t read_ahead;
+ uint8_t stripe_sz;
+ uint8_t status;
+ uint8_t write_mode;
+ uint8_t direct_io;
+ uint8_t row_size;
+} __attribute__ ((packed)) logdrv_param_t;
+
+
+/**
+ * logdrv_40ld_t - logical drive definition for 40LD controllers
+ * @lparam : logical drives parameters
+ * @span : span
+ */
+typedef struct {
+ logdrv_param_t lparam;
+ adap_span_40ld_t span[SPAN_DEPTH_8_SPANS];
+}__attribute__ ((packed)) logdrv_40ld_t;
+
+
+/**
+ * logdrv_8ld_span8_t - logical drive definition for 8LD controllers
+ * @lparam : logical drives parameters
+ * @span : span
+ *
+ * 8-LD logical drive with upto 8 spans
+ */
+typedef struct {
+ logdrv_param_t lparam;
+ adap_span_8ld_t span[SPAN_DEPTH_8_SPANS];
+}__attribute__ ((packed)) logdrv_8ld_span8_t;
+
+
+/**
+ * logdrv_8ld_span4_t - logical drive definition for 8LD controllers
+ * @lparam : logical drives parameters
+ * @span : span
+ *
+ * 8-LD logical drive with upto 4 spans
+ */
+typedef struct {
+ logdrv_param_t lparam;
+ adap_span_8ld_t span[SPAN_DEPTH_4_SPANS];
+}__attribute__ ((packed)) logdrv_8ld_span4_t;
+
+
+/**
+ * phys_drive_t - physical device information
+ * @type : Type of the device
+ * @cur_status : current status of the device
+ * @tag_depth : Level of tagging
+ * @sync_neg : sync negotiation - ENABLE or DISBALE
+ * @size : configurable size in terms of 512 byte
+ */
+typedef struct {
+ uint8_t type;
+ uint8_t cur_status;
+ uint8_t tag_depth;
+ uint8_t sync_neg;
+ uint32_t size;
+}__attribute__ ((packed)) phys_drive_t;
+
+
+/**
+ * disk_array_40ld_t - disk array for 40LD controllers
+ * @numldrv : number of logical drives
+ * @resvd :
+ * @ldrv : logical drives information
+ * @pdrv : physical drives information
+ */
+typedef struct {
+ uint8_t numldrv;
+ uint8_t resvd[3];
+ logdrv_40ld_t ldrv[MAX_LOGICAL_DRIVES_40LD];
+ phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_40ld_t;
+
+
+/**
+ * disk_array_8ld_span8_t - disk array for 8LD controllers
+ * @numldrv : number of logical drives
+ * @resvd :
+ * @ldrv : logical drives information
+ * @pdrv : physical drives information
+ *
+ * Disk array for 8LD logical drives with upto 8 spans
+ */
+typedef struct {
+ uint8_t numldrv;
+ uint8_t resvd[3];
+ logdrv_8ld_span8_t ldrv[MAX_LOGICAL_DRIVES_8LD];
+ phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_8ld_span8_t;
+
+
+/**
+ * disk_array_8ld_span4_t - disk array for 8LD controllers
+ * @numldrv : number of logical drives
+ * @resvd :
+ * @ldrv : logical drives information
+ * @pdrv : physical drives information
+ *
+ * Disk array for 8LD logical drives with upto 4 spans
+ */
+typedef struct {
+ uint8_t numldrv;
+ uint8_t resvd[3];
+ logdrv_8ld_span4_t ldrv[MAX_LOGICAL_DRIVES_8LD];
+ phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES];
+}__attribute__ ((packed)) disk_array_8ld_span4_t;
+
+
+/**
+ * private_bios_data - bios private data for boot devices
+ * @geometry : bits 0-3 - BIOS geometry, 0x0001 - 1GB, 0x0010 - 2GB,
+ * 0x1000 - 8GB, Others values are invalid
+ * @unused : bits 4-7 are unused
+ * @boot_drv : logical drive set as boot drive, 0..7 - for 8LD cards,
+ * 0..39 - for 40LD cards
+ * @cksum : 0-(sum of first 13 bytes of this structure)
+ */
+struct private_bios_data {
+ uint8_t geometry :4;
+ uint8_t unused :4;
+ uint8_t boot_drv;
+ uint8_t rsvd[12];
+ uint16_t cksum;
+} __attribute__ ((packed));
+
+
+/**
+ * mbox_sgl64 - 64-bit scatter list for mailbox based controllers
+ * @address : address of the buffer
+ * @length : data transfer length
+ */
+typedef struct {
+ uint64_t address;
+ uint32_t length;
+} __attribute__ ((packed)) mbox_sgl64;
+
+/**
+ * mbox_sgl32 - 32-bit scatter list for mailbox based controllers
+ * @address : address of the buffer
+ * @length : data transfer length
+ */
+typedef struct {
+ uint32_t address;
+ uint32_t length;
+} __attribute__ ((packed)) mbox_sgl32;
+
+#endif // _MRAID_MBOX_DEFS_H_
+
+/* vim: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h
new file mode 100644
index 000000000000..18969a4946b7
--- /dev/null
+++ b/drivers/scsi/megaraid/mega_common.h
@@ -0,0 +1,286 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : mega_common.h
+ *
+ * Libaray of common routine used by all low-level megaraid drivers
+ */
+
+#ifndef _MEGA_COMMON_H_
+#define _MEGA_COMMON_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/list.h>
+#include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <asm/semaphore.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+
+#define LSI_MAX_CHANNELS 16
+#define LSI_MAX_LOGICAL_DRIVES_64LD (64+1)
+
+
+/**
+ * scb_t - scsi command control block
+ * @param ccb : command control block for individual driver
+ * @param list : list of control blocks
+ * @param gp : general purpose field for LLDs
+ * @param sno : all SCBs have a serial number
+ * @param scp : associated scsi command
+ * @param state : current state of scb
+ * @param dma_dir : direction of data transfer
+ * @param dma_type : transfer with sg list, buffer, or no data transfer
+ * @param dev_channel : actual channel on the device
+ * @param dev_target : actual target on the device
+ * @param status : completion status
+ *
+ * This is our central data structure to issue commands the each driver.
+ * Driver specific data structures are maintained in the ccb field.
+ * scb provides a field 'gp', which can be used by LLD for its own purposes
+ *
+ * dev_channel and dev_target must be initialized with the actual channel and
+ * target on the controller.
+ */
+typedef struct {
+ caddr_t ccb;
+ struct list_head list;
+ unsigned long gp;
+ unsigned int sno;
+ struct scsi_cmnd *scp;
+ uint32_t state;
+ uint32_t dma_direction;
+ uint32_t dma_type;
+ uint16_t dev_channel;
+ uint16_t dev_target;
+ uint32_t status;
+} scb_t;
+
+/*
+ * SCB states as it transitions from one state to another
+ */
+#define SCB_FREE 0x0000 /* on the free list */
+#define SCB_ACTIVE 0x0001 /* off the free list */
+#define SCB_PENDQ 0x0002 /* on the pending queue */
+#define SCB_ISSUED 0x0004 /* issued - owner f/w */
+#define SCB_ABORT 0x0008 /* Got an abort for this one */
+#define SCB_RESET 0x0010 /* Got a reset for this one */
+
+/*
+ * DMA types for scb
+ */
+#define MRAID_DMA_NONE 0x0000 /* no data transfer for this command */
+#define MRAID_DMA_WSG 0x0001 /* data transfer using a sg list */
+#define MRAID_DMA_WBUF 0x0002 /* data transfer using a contiguous buffer */
+
+
+/**
+ * struct adapter_t - driver's initialization structure
+ * @param dpc_h : tasklet handle
+ * @param pdev : pci configuration pointer for kernel
+ * @param host : pointer to host structure of mid-layer
+ * @param host_lock : pointer to appropriate lock
+ * @param lock : synchronization lock for mid-layer and driver
+ * @param quiescent : driver is quiescent for now.
+ * @param outstanding_cmds : number of commands pending in the driver
+ * @param kscb_list : pointer to the bulk of SCBs pointers for IO
+ * @param kscb_pool : pool of free scbs for IO
+ * @param kscb_pool_lock : lock for pool of free scbs
+ * @param pend_list : pending commands list
+ * @param pend_list_lock : exlusion lock for pending commands list
+ * @param completed_list : list of completed commands
+ * @param completed_list_lock : exclusion lock for list of completed commands
+ * @param sglen : max sg elements supported
+ * @param device_ids : to convert kernel device addr to our devices.
+ * @param raid_device : raid adapter specific pointer
+ * @param max_channel : maximum channel number supported - inclusive
+ * @param max_target : max target supported - inclusive
+ * @param max_lun : max lun supported - inclusive
+ * @param unique_id : unique identifier for each adapter
+ * @param irq : IRQ for this adapter
+ * @param ito : internal timeout value, (-1) means no timeout
+ * @param ibuf : buffer to issue internal commands
+ * @param ibuf_dma_h : dma handle for the above buffer
+ * @param uscb_list : SCB pointers for user cmds, common mgmt module
+ * @param uscb_pool : pool of SCBs for user commands
+ * @param uscb_pool_lock : exclusion lock for these SCBs
+ * @param max_cmds : max outstanding commands
+ * @param fw_version : firmware version
+ * @param bios_version : bios version
+ * @param max_cdb_sz : biggest CDB size supported.
+ * @param ha : is high availability present - clustering
+ * @param init_id : initiator ID, the default value should be 7
+ * @param max_sectors : max sectors per request
+ * @param cmd_per_lun : max outstanding commands per LUN
+ * @param being_detached : set when unloading, no more mgmt calls
+ *
+ *
+ * mraid_setup_device_map() can be called anytime after the device map is
+ * available and MRAID_GET_DEVICE_MAP() can be called whenever the mapping is
+ * required, usually from LLD's queue entry point. The formar API sets up the
+ * MRAID_IS_LOGICAL(adapter_t *, struct scsi_cmnd *) to find out if the
+ * device in question is a logical drive.
+ *
+ * quiescent flag should be set by the driver if it is not accepting more
+ * commands
+ *
+ * NOTE: The fields of this structures are placed to minimize cache misses
+ */
+
+// amount of space required to store the bios and firmware version strings
+#define VERSION_SIZE 16
+
+typedef struct {
+ struct tasklet_struct dpc_h;
+ struct pci_dev *pdev;
+ struct Scsi_Host *host;
+ spinlock_t *host_lock;
+ spinlock_t lock;
+ uint8_t quiescent;
+ int outstanding_cmds;
+ scb_t *kscb_list;
+ struct list_head kscb_pool;
+ spinlock_t kscb_pool_lock;
+ struct list_head pend_list;
+ spinlock_t pend_list_lock;
+ struct list_head completed_list;
+ spinlock_t completed_list_lock;
+ uint16_t sglen;
+ int device_ids[LSI_MAX_CHANNELS]
+ [LSI_MAX_LOGICAL_DRIVES_64LD];
+ caddr_t raid_device;
+ uint8_t max_channel;
+ uint16_t max_target;
+ uint8_t max_lun;
+
+ uint32_t unique_id;
+ uint8_t irq;
+ uint8_t ito;
+ caddr_t ibuf;
+ dma_addr_t ibuf_dma_h;
+ scb_t *uscb_list;
+ struct list_head uscb_pool;
+ spinlock_t uscb_pool_lock;
+ int max_cmds;
+ uint8_t fw_version[VERSION_SIZE];
+ uint8_t bios_version[VERSION_SIZE];
+ uint8_t max_cdb_sz;
+ uint8_t ha;
+ uint16_t init_id;
+ uint16_t max_sectors;
+ uint16_t cmd_per_lun;
+ atomic_t being_detached;
+} adapter_t;
+
+#define SCSI_FREE_LIST_LOCK(adapter) (&adapter->kscb_pool_lock)
+#define USER_FREE_LIST_LOCK(adapter) (&adapter->uscb_pool_lock)
+#define PENDING_LIST_LOCK(adapter) (&adapter->pend_list_lock)
+#define COMPLETED_LIST_LOCK(adapter) (&adapter->completed_list_lock)
+
+
+// conversion from scsi command
+#define SCP2HOST(scp) (scp)->device->host // to host
+#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state
+#define SCP2CHANNEL(scp) (scp)->device->channel // to channel
+#define SCP2TARGET(scp) (scp)->device->id // to target
+#define SCP2LUN(scp) (scp)->device->lun // to LUN
+
+// generic macro to convert scsi command and host to controller's soft state
+#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0])
+#define SCP2ADAPTER(scp) (adapter_t *)SCSIHOST2ADAP(SCP2HOST(scp))
+
+
+/**
+ * MRAID_GET_DEVICE_MAP - device ids
+ * @param adp - Adapter's soft state
+ * @param scp - mid-layer scsi command pointer
+ * @param p_chan - physical channel on the controller
+ * @param target - target id of the device or logical drive number
+ * @param islogical - set if the command is for the logical drive
+ *
+ * Macro to retrieve information about device class, logical or physical and
+ * the corresponding physical channel and target or logical drive number
+ **/
+#define MRAID_IS_LOGICAL(adp, scp) \
+ (SCP2CHANNEL(scp) == (adp)->max_channel) ? 1 : 0
+
+#define MRAID_IS_LOGICAL_SDEV(adp, sdev) \
+ (sdev->channel == (adp)->max_channel) ? 1 : 0
+
+#define MRAID_GET_DEVICE_MAP(adp, scp, p_chan, target, islogical) \
+ /* \
+ * Is the request coming for the virtual channel \
+ */ \
+ islogical = MRAID_IS_LOGICAL(adp, scp); \
+ \
+ /* \
+ * Get an index into our table of drive ids mapping \
+ */ \
+ if (islogical) { \
+ p_chan = 0xFF; \
+ target = \
+ (adp)->device_ids[(adp)->max_channel][SCP2TARGET(scp)]; \
+ } \
+ else { \
+ p_chan = ((adp)->device_ids[SCP2CHANNEL(scp)] \
+ [SCP2TARGET(scp)] >> 8) & 0xFF; \
+ target = ((adp)->device_ids[SCP2CHANNEL(scp)] \
+ [SCP2TARGET(scp)] & 0xFF); \
+ }
+
+/*
+ * ### Helper routines ###
+ */
+#define LSI_DBGLVL mraid_debug_level // each LLD must define a global
+ // mraid_debug_level
+
+#ifdef DEBUG
+#if defined (_ASSERT_PANIC)
+#define ASSERT_ACTION panic
+#else
+#define ASSERT_ACTION printk
+#endif
+
+#define ASSERT(expression) \
+ if (!(expression)) { \
+ ASSERT_ACTION("assertion failed:(%s), file: %s, line: %d:%s\n", \
+ #expression, __FILE__, __LINE__, __FUNCTION__); \
+ }
+#else
+#define ASSERT(expression)
+#endif
+
+/*
+ * struct mraid_pci_blk - structure holds DMA memory block info
+ * @param vaddr : virtual address to a memory block
+ * @param dma_addr : DMA handle to a memory block
+ *
+ * This structure is filled up for the caller. It is the responsibilty of the
+ * caller to allocate this array big enough to store addresses for all
+ * requested elements
+ */
+struct mraid_pci_blk {
+ caddr_t vaddr;
+ dma_addr_t dma_addr;
+};
+
+#endif // _MEGA_COMMON_H_
+
+// vim: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/megaraid/megaraid_ioctl.h b/drivers/scsi/megaraid/megaraid_ioctl.h
new file mode 100644
index 000000000000..bdaee144a1c3
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_ioctl.h
@@ -0,0 +1,296 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_ioctl.h
+ *
+ * Definitions to interface with user level applications
+ */
+
+#ifndef _MEGARAID_IOCTL_H_
+#define _MEGARAID_IOCTL_H_
+
+#include <linux/types.h>
+#include <asm/semaphore.h>
+
+#include "mbox_defs.h"
+
+/**
+ * con_log() - console log routine
+ * @param level : indicates the severity of the message.
+ * @fparam mt : format string
+ *
+ * con_log displays the error messages on the console based on the current
+ * debug level. Also it attaches the appropriate kernel severity level with
+ * the message.
+ *
+ *
+ * consolge messages debug levels
+ */
+#define CL_ANN 0 /* print unconditionally, announcements */
+#define CL_DLEVEL1 1 /* debug level 1, informative */
+#define CL_DLEVEL2 2 /* debug level 2, verbose */
+#define CL_DLEVEL3 3 /* debug level 3, very verbose */
+
+#define con_log(level, fmt) if (LSI_DBGLVL >= level) printk fmt;
+
+/*
+ * Definitions & Declarations needed to use common management module
+ */
+
+#define MEGAIOC_MAGIC 'm'
+#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, mimd_t)
+
+#define MEGAIOC_QNADAP 'm' /* Query # of adapters */
+#define MEGAIOC_QDRVRVER 'e' /* Query driver version */
+#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */
+
+#define USCSICMD 0x80
+#define UIOC_RD 0x00001
+#define UIOC_WR 0x00002
+
+#define MBOX_CMD 0x00000
+#define GET_DRIVER_VER 0x10000
+#define GET_N_ADAP 0x20000
+#define GET_ADAP_INFO 0x30000
+#define GET_CAP 0x40000
+#define GET_STATS 0x50000
+#define GET_IOCTL_VERSION 0x01
+
+#define EXT_IOCTL_SIGN_SZ 16
+#define EXT_IOCTL_SIGN "$$_EXTD_IOCTL_$$"
+
+#define MBOX_LEGACY 0x00 /* ioctl has legacy mbox*/
+#define MBOX_HPE 0x01 /* ioctl has hpe mbox */
+
+#define APPTYPE_MIMD 0x00 /* old existing apps */
+#define APPTYPE_UIOC 0x01 /* new apps using uioc */
+
+#define IOCTL_ISSUE 0x00000001 /* Issue ioctl */
+#define IOCTL_ABORT 0x00000002 /* Abort previous ioctl */
+
+#define DRVRTYPE_MBOX 0x00000001 /* regular mbox driver */
+#define DRVRTYPE_HPE 0x00000002 /* new hpe driver */
+
+#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) )
+#define GETADAP(mkadap) ((mkadap) ^ MEGAIOC_MAGIC << 8)
+
+#define MAX_DMA_POOLS 5 /* 4k, 8k, 16k, 32k, 64k*/
+
+
+/**
+ * struct uioc_t - the common ioctl packet structure
+ *
+ * @signature : Must be "$$_EXTD_IOCTL_$$"
+ * @mb_type : Type of the mail box (MB_LEGACY or MB_HPE)
+ * @app_type : Type of the issuing application (existing or new)
+ * @opcode : Opcode of the command
+ * @adapno : Adapter number
+ * @cmdbuf : Pointer to buffer - can point to mbox or plain data buffer
+ * @xferlen : xferlen for DCMD and non mailbox commands
+ * @data_dir : Direction of the data transfer
+ * @status : Status from the driver
+ * @reserved : reserved bytes for future expansion
+ *
+ * @user_data : user data transfer address is saved in this
+ * @user_data_len: length of the data buffer sent by user app
+ * @user_pthru : user passthru address is saves in this (null if DCMD)
+ * @pthru32 : kernel address passthru (allocated per kioc)
+ * @pthru32_h : physicall address of @pthru32
+ * @list : for kioc free pool list maintenance
+ * @done : call back routine for llds to call when kioc is completed
+ * @buf_vaddr : dma pool buffer attached to kioc for data transfer
+ * @buf_paddr : physical address of the dma pool buffer
+ * @pool_index : index of the dma pool that @buf_vaddr is taken from
+ * @free_buf : indicates if buffer needs to be freed after kioc completes
+ *
+ * Note : All LSI drivers understand only this packet. Any other
+ * : format sent by applications would be converted to this.
+ */
+typedef struct uioc {
+
+/* User Apps: */
+
+ uint8_t signature[EXT_IOCTL_SIGN_SZ];
+ uint16_t mb_type;
+ uint16_t app_type;
+ uint32_t opcode;
+ uint32_t adapno;
+ uint64_t cmdbuf;
+ uint32_t xferlen;
+ uint32_t data_dir;
+ int32_t status;
+ uint8_t reserved[128];
+
+/* Driver Data: */
+ void __user * user_data;
+ uint32_t user_data_len;
+ mraid_passthru_t __user *user_pthru;
+
+ mraid_passthru_t *pthru32;
+ dma_addr_t pthru32_h;
+
+ struct list_head list;
+ void (*done)(struct uioc*);
+
+ caddr_t buf_vaddr;
+ dma_addr_t buf_paddr;
+ int8_t pool_index;
+ uint8_t free_buf;
+
+ uint8_t timedout;
+
+} __attribute__ ((aligned(1024),packed)) uioc_t;
+
+
+/**
+ * struct mraid_hba_info - information about the controller
+ *
+ * @param pci_vendor_id : PCI vendor id
+ * @param pci_device_id : PCI device id
+ * @param subsystem_vendor_id : PCI subsystem vendor id
+ * @param subsystem_device_id : PCI subsystem device id
+ * @param baseport : base port of hba memory
+ * @param pci_bus : PCI bus
+ * @param pci_dev_fn : PCI device/function values
+ * @param irq : interrupt vector for the device
+ *
+ * Extended information of 256 bytes about the controller. Align on the single
+ * byte boundary so that 32-bit applications can be run on 64-bit platform
+ * drivers withoug re-compilation.
+ * NOTE: reduce the number of reserved bytes whenever new field are added, so
+ * that total size of the structure remains 256 bytes.
+ */
+typedef struct mraid_hba_info {
+
+ uint16_t pci_vendor_id;
+ uint16_t pci_device_id;
+ uint16_t subsys_vendor_id;
+ uint16_t subsys_device_id;
+
+ uint64_t baseport;
+ uint8_t pci_bus;
+ uint8_t pci_dev_fn;
+ uint8_t pci_slot;
+ uint8_t irq;
+
+ uint32_t unique_id;
+ uint32_t host_no;
+
+ uint8_t num_ldrv;
+} __attribute__ ((aligned(256), packed)) mraid_hba_info_t;
+
+
+/**
+ * mcontroller : adapter info structure for old mimd_t apps
+ *
+ * @base : base address
+ * @irq : irq number
+ * @numldrv : number of logical drives
+ * @pcibus : pci bus
+ * @pcidev : pci device
+ * @pcifun : pci function
+ * @pciid : pci id
+ * @pcivendor : vendor id
+ * @pcislot : slot number
+ * @uid : unique id
+ */
+typedef struct mcontroller {
+
+ uint64_t base;
+ uint8_t irq;
+ uint8_t numldrv;
+ uint8_t pcibus;
+ uint16_t pcidev;
+ uint8_t pcifun;
+ uint16_t pciid;
+ uint16_t pcivendor;
+ uint8_t pcislot;
+ uint32_t uid;
+
+} __attribute__ ((packed)) mcontroller_t;
+
+
+/**
+ * mm_dmapool_t : Represents one dma pool with just one buffer
+ *
+ * @vaddr : Virtual address
+ * @paddr : DMA physicall address
+ * @bufsize : In KB - 4 = 4k, 8 = 8k etc.
+ * @handle : Handle to the dma pool
+ * @lock : lock to synchronize access to the pool
+ * @in_use : If pool already in use, attach new block
+ */
+typedef struct mm_dmapool {
+ caddr_t vaddr;
+ dma_addr_t paddr;
+ uint32_t buf_size;
+ struct dma_pool *handle;
+ spinlock_t lock;
+ uint8_t in_use;
+} mm_dmapool_t;
+
+
+/**
+ * mraid_mmadp_t: Structure that drivers pass during (un)registration
+ *
+ * @unique_id : Any unique id (usually PCI bus+dev+fn)
+ * @drvr_type : megaraid or hpe (DRVRTYPE_MBOX or DRVRTYPE_HPE)
+ * @drv_data : Driver specific; not touched by the common module
+ * @timeout : timeout for issued kiocs
+ * @max_kioc : Maximum ioctl packets acceptable by the lld
+ * @pdev : pci dev; used for allocating dma'ble memory
+ * @issue_uioc : Driver supplied routine to issue uioc_t commands
+ * : issue_uioc(drvr_data, kioc, ISSUE/ABORT, uioc_done)
+ * @quiescent : flag to indicate if ioctl can be issued to this adp
+ * @list : attach with the global list of adapters
+ * @kioc_list : block of mem for @max_kioc number of kiocs
+ * @kioc_pool : pool of free kiocs
+ * @kioc_pool_lock : protection for free pool
+ * @kioc_semaphore : so as not to exceed @max_kioc parallel ioctls
+ * @mbox_list : block of mem for @max_kioc number of mboxes
+ * @pthru_dma_pool : DMA pool to allocate passthru packets
+ * @dma_pool_list : array of dma pools
+ */
+
+typedef struct mraid_mmadp {
+
+/* Filled by driver */
+
+ uint32_t unique_id;
+ uint32_t drvr_type;
+ unsigned long drvr_data;
+ uint16_t timeout;
+ uint8_t max_kioc;
+
+ struct pci_dev *pdev;
+
+ int(*issue_uioc)(unsigned long, uioc_t *, uint32_t);
+
+/* Maintained by common module */
+ uint32_t quiescent;
+
+ struct list_head list;
+ uioc_t *kioc_list;
+ struct list_head kioc_pool;
+ spinlock_t kioc_pool_lock;
+ struct semaphore kioc_semaphore;
+
+ mbox64_t *mbox_list;
+ struct dma_pool *pthru_dma_pool;
+ mm_dmapool_t dma_pool_list[MAX_DMA_POOLS];
+
+} mraid_mmadp_t;
+
+int mraid_mm_register_adp(mraid_mmadp_t *);
+int mraid_mm_unregister_adp(uint32_t);
+uint32_t mraid_mm_adapter_app_handle(uint32_t);
+
+#endif /* _MEGARAID_IOCTL_H_ */
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
new file mode 100644
index 000000000000..138fa4815833
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -0,0 +1,4276 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mbox.c
+ * Version : v2.20.4.5 (Feb 03 2005)
+ *
+ * Authors:
+ * Atul Mukker <Atul.Mukker@lsil.com>
+ * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com>
+ * Manoj Jose <Manoj.Jose@lsil.com>
+ *
+ * List of supported controllers
+ *
+ * OEM Product Name VID DID SSVID SSID
+ * --- ------------ --- --- ---- ----
+ * Dell PERC3/QC 101E 1960 1028 0471
+ * Dell PERC3/DC 101E 1960 1028 0493
+ * Dell PERC3/SC 101E 1960 1028 0475
+ * Dell PERC3/Di 1028 1960 1028 0123
+ * Dell PERC4/SC 1000 1960 1028 0520
+ * Dell PERC4/DC 1000 1960 1028 0518
+ * Dell PERC4/QC 1000 0407 1028 0531
+ * Dell PERC4/Di 1028 000F 1028 014A
+ * Dell PERC 4e/Si 1028 0013 1028 016c
+ * Dell PERC 4e/Di 1028 0013 1028 016d
+ * Dell PERC 4e/Di 1028 0013 1028 016e
+ * Dell PERC 4e/Di 1028 0013 1028 016f
+ * Dell PERC 4e/Di 1028 0013 1028 0170
+ * Dell PERC 4e/DC 1000 0408 1028 0002
+ * Dell PERC 4e/SC 1000 0408 1028 0001
+ *
+ *
+ * LSI MegaRAID SCSI 320-0 1000 1960 1000 A520
+ * LSI MegaRAID SCSI 320-1 1000 1960 1000 0520
+ * LSI MegaRAID SCSI 320-2 1000 1960 1000 0518
+ * LSI MegaRAID SCSI 320-0X 1000 0407 1000 0530
+ * LSI MegaRAID SCSI 320-2X 1000 0407 1000 0532
+ * LSI MegaRAID SCSI 320-4X 1000 0407 1000 0531
+ * LSI MegaRAID SCSI 320-1E 1000 0408 1000 0001
+ * LSI MegaRAID SCSI 320-2E 1000 0408 1000 0002
+ * LSI MegaRAID SATA 150-4 1000 1960 1000 4523
+ * LSI MegaRAID SATA 150-6 1000 1960 1000 0523
+ * LSI MegaRAID SATA 300-4X 1000 0409 1000 3004
+ * LSI MegaRAID SATA 300-8X 1000 0409 1000 3008
+ *
+ * INTEL RAID Controller SRCU42X 1000 0407 8086 0532
+ * INTEL RAID Controller SRCS16 1000 1960 8086 0523
+ * INTEL RAID Controller SRCU42E 1000 0408 8086 0002
+ * INTEL RAID Controller SRCZCRX 1000 0407 8086 0530
+ * INTEL RAID Controller SRCS28X 1000 0409 8086 3008
+ * INTEL RAID Controller SROMBU42E 1000 0408 8086 3431
+ * INTEL RAID Controller SROMBU42E 1000 0408 8086 3499
+ * INTEL RAID Controller SRCU51L 1000 1960 8086 0520
+ *
+ * FSC MegaRAID PCI Express ROMB 1000 0408 1734 1065
+ *
+ * ACER MegaRAID ROMB-2E 1000 0408 1025 004D
+ *
+ * NEC MegaRAID PCI Express ROMB 1000 0408 1033 8287
+ *
+ * For history of changes, see Documentation/ChangeLog.megaraid
+ */
+
+#include "megaraid_mbox.h"
+
+static int megaraid_init(void);
+static void megaraid_exit(void);
+
+static int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *);
+static void megaraid_detach_one(struct pci_dev *);
+static void megaraid_mbox_shutdown(struct device *);
+
+static int megaraid_io_attach(adapter_t *);
+static void megaraid_io_detach(adapter_t *);
+
+static int megaraid_init_mbox(adapter_t *);
+static void megaraid_fini_mbox(adapter_t *);
+
+static int megaraid_alloc_cmd_packets(adapter_t *);
+static void megaraid_free_cmd_packets(adapter_t *);
+
+static int megaraid_mbox_setup_dma_pools(adapter_t *);
+static void megaraid_mbox_teardown_dma_pools(adapter_t *);
+
+static int megaraid_sysfs_alloc_resources(adapter_t *);
+static void megaraid_sysfs_free_resources(adapter_t *);
+
+static int megaraid_abort_handler(struct scsi_cmnd *);
+static int megaraid_reset_handler(struct scsi_cmnd *);
+
+static int mbox_post_sync_cmd(adapter_t *, uint8_t []);
+static int mbox_post_sync_cmd_fast(adapter_t *, uint8_t []);
+static int megaraid_busywait_mbox(mraid_device_t *);
+static int megaraid_mbox_product_info(adapter_t *);
+static int megaraid_mbox_extended_cdb(adapter_t *);
+static int megaraid_mbox_support_ha(adapter_t *, uint16_t *);
+static int megaraid_mbox_support_random_del(adapter_t *);
+static int megaraid_mbox_get_max_sg(adapter_t *);
+static void megaraid_mbox_enum_raid_scsi(adapter_t *);
+static void megaraid_mbox_flush_cache(adapter_t *);
+
+static void megaraid_mbox_display_scb(adapter_t *, scb_t *);
+static void megaraid_mbox_setup_device_map(adapter_t *);
+
+static int megaraid_queue_command(struct scsi_cmnd *,
+ void (*)(struct scsi_cmnd *));
+static scb_t *megaraid_mbox_build_cmd(adapter_t *, struct scsi_cmnd *, int *);
+static void megaraid_mbox_runpendq(adapter_t *, scb_t *);
+static void megaraid_mbox_prepare_pthru(adapter_t *, scb_t *,
+ struct scsi_cmnd *);
+static void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *,
+ struct scsi_cmnd *);
+
+static irqreturn_t megaraid_isr(int, void *, struct pt_regs *);
+
+static void megaraid_mbox_dpc(unsigned long);
+
+static ssize_t megaraid_sysfs_show_app_hndl(struct class_device *, char *);
+static ssize_t megaraid_sysfs_show_ldnum(struct device *, char *);
+
+static int megaraid_cmm_register(adapter_t *);
+static int megaraid_cmm_unregister(adapter_t *);
+static int megaraid_mbox_mm_handler(unsigned long, uioc_t *, uint32_t);
+static int megaraid_mbox_mm_command(adapter_t *, uioc_t *);
+static void megaraid_mbox_mm_done(adapter_t *, scb_t *);
+static int gather_hbainfo(adapter_t *, mraid_hba_info_t *);
+static int wait_till_fw_empty(adapter_t *);
+
+
+
+MODULE_AUTHOR("LSI Logic Corporation");
+MODULE_DESCRIPTION("LSI Logic MegaRAID Mailbox Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MEGARAID_VERSION);
+
+/*
+ * ### modules parameters for driver ###
+ */
+
+/**
+ * Set to enable driver to expose unconfigured disk to kernel
+ */
+static int megaraid_expose_unconf_disks = 0;
+module_param_named(unconf_disks, megaraid_expose_unconf_disks, int, 0);
+MODULE_PARM_DESC(unconf_disks,
+ "Set to expose unconfigured disks to kernel (default=0)");
+
+/**
+ * driver wait time if the adapter's mailbox is busy
+ */
+static unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT;
+module_param_named(busy_wait, max_mbox_busy_wait, int, 0);
+MODULE_PARM_DESC(busy_wait,
+ "Max wait for mailbox in microseconds if busy (default=10)");
+
+/**
+ * number of sectors per IO command
+ */
+static unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS;
+module_param_named(max_sectors, megaraid_max_sectors, int, 0);
+MODULE_PARM_DESC(max_sectors,
+ "Maximum number of sectors per IO command (default=128)");
+
+/**
+ * number of commands per logical unit
+ */
+static unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN;
+module_param_named(cmd_per_lun, megaraid_cmd_per_lun, int, 0);
+MODULE_PARM_DESC(cmd_per_lun,
+ "Maximum number of commands per logical unit (default=64)");
+
+
+/**
+ * Fast driver load option, skip scanning for physical devices during load.
+ * This would result in non-disk devices being skipped during driver load
+ * time. These can be later added though, using /proc/scsi/scsi
+ */
+static unsigned int megaraid_fast_load = 0;
+module_param_named(fast_load, megaraid_fast_load, int, 0);
+MODULE_PARM_DESC(fast_load,
+ "Faster loading of the driver, skips physical devices! (default=0)");
+
+
+/**
+ * mraid_debug level - threshold for amount of information to be displayed by
+ * the driver. This level can be changed through modules parameters, ioctl or
+ * sysfs/proc interface. By default, print the announcement messages only.
+ */
+int mraid_debug_level = CL_ANN;
+module_param_named(debug_level, mraid_debug_level, int, 0);
+MODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)");
+
+/*
+ * ### global data ###
+ */
+static uint8_t megaraid_mbox_version[8] =
+ { 0x02, 0x20, 0x04, 0x05, 2, 3, 20, 5 };
+
+
+/*
+ * PCI table for all supported controllers.
+ */
+static struct pci_device_id pci_id_table_g[] = {
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4_DI_DISCOVERY,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_DI_DISCOVERY,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4_SC,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_SC,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4_DC,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_DC,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4_QC,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_QC,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4_DI_EVERGLADES,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4_DI_EVERGLADES,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_SI_BIGBEND,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_SI_BIGBEND,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_KOBUK,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_KOBUK,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_CORVETTE,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_CORVETTE,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_EXPEDITION,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION,
+ },
+ {
+ PCI_VENDOR_ID_DELL,
+ PCI_DEVICE_ID_PERC4E_DI_GUADALUPE,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4E_DC_320_2E,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_DC_320_2E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_PERC4E_SC_320_1E,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC4E_SC_320_1E,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC3_QC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC3_DC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_DELL,
+ PCI_SUBSYS_ID_PERC3_SC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_AMI,
+ PCI_SUBSYS_ID_PERC3_SC,
+ },
+ {
+ PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3,
+ PCI_VENDOR_ID_AMI,
+ PCI_SUBSYS_ID_PERC3_DC,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_0,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_0,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_1,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_1,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_2,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_2,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_0x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_2x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_4x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_1E,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SCSI_320_2E,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_I4_133_RAID,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_I4_133_RAID,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_150_4,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_150_4,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_150_6,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_150_6,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_300_4x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_300_4x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_SATA_300_8x,
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_SUBSYS_ID_MEGARAID_SATA_300_8x,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCU42X,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCU42X,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCS16,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCS16,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCU42E,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCU42E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCZCRX,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCS28X,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCS28X,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK,
+ PCI_VENDOR_ID_INTEL,
+ PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB,
+ PCI_SUBSYS_ID_FSC,
+ PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E,
+ PCI_VENDOR_ID_AI,
+ PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E,
+ },
+ {
+ PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_MEGARAID_NEC_ROMB_2E,
+ PCI_VENDOR_ID_NEC,
+ PCI_SUBSYS_ID_MEGARAID_NEC_ROMB_2E,
+ },
+ {0} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, pci_id_table_g);
+
+
+static struct pci_driver megaraid_pci_driver_g = {
+ .name = "megaraid",
+ .id_table = pci_id_table_g,
+ .probe = megaraid_probe_one,
+ .remove = __devexit_p(megaraid_detach_one),
+ .driver = {
+ .shutdown = megaraid_mbox_shutdown,
+ }
+};
+
+
+
+// definitions for the device attributes for exporting logical drive number
+// for a scsi address (Host, Channel, Id, Lun)
+
+CLASS_DEVICE_ATTR(megaraid_mbox_app_hndl, S_IRUSR, megaraid_sysfs_show_app_hndl,
+ NULL);
+
+// Host template initializer for megaraid mbox sysfs device attributes
+static struct class_device_attribute *megaraid_shost_attrs[] = {
+ &class_device_attr_megaraid_mbox_app_hndl,
+ NULL,
+};
+
+
+DEVICE_ATTR(megaraid_mbox_ld, S_IRUSR, megaraid_sysfs_show_ldnum, NULL);
+
+// Host template initializer for megaraid mbox sysfs device attributes
+static struct device_attribute *megaraid_sdev_attrs[] = {
+ &dev_attr_megaraid_mbox_ld,
+ NULL,
+};
+
+
+/*
+ * Scsi host template for megaraid unified driver
+ */
+static struct scsi_host_template megaraid_template_g = {
+ .module = THIS_MODULE,
+ .name = "LSI Logic MegaRAID driver",
+ .proc_name = "megaraid",
+ .queuecommand = megaraid_queue_command,
+ .eh_abort_handler = megaraid_abort_handler,
+ .eh_device_reset_handler = megaraid_reset_handler,
+ .eh_bus_reset_handler = megaraid_reset_handler,
+ .eh_host_reset_handler = megaraid_reset_handler,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sdev_attrs = megaraid_sdev_attrs,
+ .shost_attrs = megaraid_shost_attrs,
+};
+
+
+/**
+ * megaraid_init - module load hook
+ *
+ * We register ourselves as hotplug enabled module and let PCI subsystem
+ * discover our adaters
+ **/
+static int __init
+megaraid_init(void)
+{
+ int rval;
+
+ // Announce the driver version
+ con_log(CL_ANN, (KERN_INFO "megaraid: %s %s\n", MEGARAID_VERSION,
+ MEGARAID_EXT_VERSION));
+
+ // check validity of module parameters
+ if (megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: max commands per lun reset to %d\n",
+ MBOX_MAX_SCSI_CMDS));
+
+ megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS;
+ }
+
+
+ // register as a PCI hot-plug driver module
+ if ((rval = pci_module_init(&megaraid_pci_driver_g))) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not register hotplug support.\n"));
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_exit - driver unload entry point
+ *
+ * We simply unwrap the megaraid_init routine here
+ */
+static void __exit
+megaraid_exit(void)
+{
+ con_log(CL_DLEVEL1, (KERN_NOTICE "megaraid: unloading framework\n"));
+
+ // unregister as PCI hotplug driver
+ pci_unregister_driver(&megaraid_pci_driver_g);
+
+ return;
+}
+
+
+/**
+ * megaraid_probe_one - PCI hotplug entry point
+ * @param pdev : handle to this controller's PCI configuration space
+ * @param id : pci device id of the class of controllers
+ *
+ * This routine should be called whenever a new adapter is detected by the
+ * PCI hotplug susbsytem.
+ **/
+static int __devinit
+megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ adapter_t *adapter;
+
+
+ // detected a new controller
+ con_log(CL_ANN, (KERN_INFO
+ "megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device));
+
+ con_log(CL_ANN, ("bus %d:slot %d:func %d\n", pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)));
+
+ if (pci_enable_device(pdev)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: pci_enable_device failed\n"));
+
+ return -ENODEV;
+ }
+
+ // Enable bus-mastering on this controller
+ pci_set_master(pdev);
+
+ // Allocate the per driver initialization structure
+ adapter = kmalloc(sizeof(adapter_t), GFP_KERNEL);
+
+ if (adapter == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__));
+
+ goto out_probe_one;
+ }
+ memset(adapter, 0, sizeof(adapter_t));
+
+
+ // set up PCI related soft state and other pre-known parameters
+ adapter->unique_id = pdev->bus->number << 8 | pdev->devfn;
+ adapter->irq = pdev->irq;
+ adapter->pdev = pdev;
+
+ atomic_set(&adapter->being_detached, 0);
+
+ // Setup the default DMA mask. This would be changed later on
+ // depending on hardware capabilities
+ if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFF) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: pci_set_dma_mask failed:%d\n", __LINE__));
+
+ goto out_free_adapter;
+ }
+
+
+ // Initialize the synchronization lock for kernel and LLD
+ spin_lock_init(&adapter->lock);
+ adapter->host_lock = &adapter->lock;
+
+
+ // Initialize the command queues: the list of free SCBs and the list
+ // of pending SCBs.
+ INIT_LIST_HEAD(&adapter->kscb_pool);
+ spin_lock_init(SCSI_FREE_LIST_LOCK(adapter));
+
+ INIT_LIST_HEAD(&adapter->pend_list);
+ spin_lock_init(PENDING_LIST_LOCK(adapter));
+
+ INIT_LIST_HEAD(&adapter->completed_list);
+ spin_lock_init(COMPLETED_LIST_LOCK(adapter));
+
+
+ // Start the mailbox based controller
+ if (megaraid_init_mbox(adapter) != 0) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: maibox adapter did not initialize\n"));
+
+ goto out_free_adapter;
+ }
+
+ // Register with LSI Common Management Module
+ if (megaraid_cmm_register(adapter) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not register with management module\n"));
+
+ goto out_fini_mbox;
+ }
+
+ // setup adapter handle in PCI soft state
+ pci_set_drvdata(pdev, adapter);
+
+ // attach with scsi mid-layer
+ if (megaraid_io_attach(adapter) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: io attach failed\n"));
+
+ goto out_cmm_unreg;
+ }
+
+ return 0;
+
+out_cmm_unreg:
+ pci_set_drvdata(pdev, NULL);
+ megaraid_cmm_unregister(adapter);
+out_fini_mbox:
+ megaraid_fini_mbox(adapter);
+out_free_adapter:
+ kfree(adapter);
+out_probe_one:
+ pci_disable_device(pdev);
+
+ return -ENODEV;
+}
+
+
+/**
+ * megaraid_detach_one - release the framework resources and call LLD release
+ * routine
+ * @param pdev : handle for our PCI cofiguration space
+ *
+ * This routine is called during driver unload. We free all the allocated
+ * resources and call the corresponding LLD so that it can also release all
+ * its resources.
+ *
+ * This routine is also called from the PCI hotplug system
+ **/
+static void
+megaraid_detach_one(struct pci_dev *pdev)
+{
+ adapter_t *adapter;
+ struct Scsi_Host *host;
+
+
+ // Start a rollback on this adapter
+ adapter = pci_get_drvdata(pdev);
+
+ if (!adapter) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x\n",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device));
+
+ return;
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x\n",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device));
+ }
+
+
+ host = adapter->host;
+
+ // do not allow any more requests from the management module for this
+ // adapter.
+ // FIXME: How do we account for the request which might still be
+ // pending with us?
+ atomic_set(&adapter->being_detached, 1);
+
+ // detach from the IO sub-system
+ megaraid_io_detach(adapter);
+
+ // reset the device state in the PCI structure. We check this
+ // condition when we enter here. If the device state is NULL,
+ // that would mean the device has already been removed
+ pci_set_drvdata(pdev, NULL);
+
+ // Unregister from common management module
+ //
+ // FIXME: this must return success or failure for conditions if there
+ // is a command pending with LLD or not.
+ megaraid_cmm_unregister(adapter);
+
+ // finalize the mailbox based controller and release all resources
+ megaraid_fini_mbox(adapter);
+
+ kfree(adapter);
+
+ scsi_host_put(host);
+
+ pci_disable_device(pdev);
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_shutdown - PCI shutdown for megaraid HBA
+ * @param device : generice driver model device
+ *
+ * Shutdown notification, perform flush cache
+ */
+static void
+megaraid_mbox_shutdown(struct device *device)
+{
+ adapter_t *adapter = pci_get_drvdata(to_pci_dev(device));
+ static int counter;
+
+ if (!adapter) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: null device in shutdown\n"));
+ return;
+ }
+
+ // flush caches now
+ con_log(CL_ANN, (KERN_INFO "megaraid: flushing adapter %d...",
+ counter++));
+
+ megaraid_mbox_flush_cache(adapter);
+
+ con_log(CL_ANN, ("done\n"));
+}
+
+
+/**
+ * megaraid_io_attach - attach a device with the IO subsystem
+ * @param adapter : controller's soft state
+ *
+ * Attach this device with the IO subsystem
+ **/
+static int
+megaraid_io_attach(adapter_t *adapter)
+{
+ struct Scsi_Host *host;
+
+ // Initialize SCSI Host structure
+ host = scsi_host_alloc(&megaraid_template_g, 8);
+ if (!host) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: scsi_register failed\n"));
+
+ return -1;
+ }
+
+ SCSIHOST2ADAP(host) = (caddr_t)adapter;
+ adapter->host = host;
+
+ // export the parameters required by the mid-layer
+ scsi_assign_lock(host, adapter->host_lock);
+ scsi_set_device(host, &adapter->pdev->dev);
+
+ host->irq = adapter->irq;
+ host->unique_id = adapter->unique_id;
+ host->can_queue = adapter->max_cmds;
+ host->this_id = adapter->init_id;
+ host->sg_tablesize = adapter->sglen;
+ host->max_sectors = adapter->max_sectors;
+ host->cmd_per_lun = adapter->cmd_per_lun;
+ host->max_channel = adapter->max_channel;
+ host->max_id = adapter->max_target;
+ host->max_lun = adapter->max_lun;
+
+
+ // notify mid-layer about the new controller
+ if (scsi_add_host(host, &adapter->pdev->dev)) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: scsi_add_host failed\n"));
+
+ scsi_host_put(host);
+
+ return -1;
+ }
+
+ scsi_scan_host(host);
+
+ return 0;
+}
+
+
+/**
+ * megaraid_io_detach - detach a device from the IO subsystem
+ * @param adapter : controller's soft state
+ *
+ * Detach this device from the IO subsystem
+ **/
+static void
+megaraid_io_detach(adapter_t *adapter)
+{
+ struct Scsi_Host *host;
+
+ con_log(CL_DLEVEL1, (KERN_INFO "megaraid: io detach\n"));
+
+ host = adapter->host;
+
+ scsi_remove_host(host);
+
+ return;
+}
+
+
+/*
+ * START: Mailbox Low Level Driver
+ *
+ * This is section specific to the single mailbox based controllers
+ */
+
+/**
+ * megaraid_init_mbox - initialize controller
+ * @param adapter - our soft state
+ *
+ * . Allocate 16-byte aligned mailbox memory for firmware handshake
+ * . Allocate controller's memory resources
+ * . Find out all initialization data
+ * . Allocate memory required for all the commands
+ * . Use internal library of FW routines, build up complete soft state
+ */
+static int __init
+megaraid_init_mbox(adapter_t *adapter)
+{
+ struct pci_dev *pdev;
+ mraid_device_t *raid_dev;
+ int i;
+
+
+ adapter->ito = MBOX_TIMEOUT;
+ pdev = adapter->pdev;
+
+ /*
+ * Allocate and initialize the init data structure for mailbox
+ * controllers
+ */
+ raid_dev = kmalloc(sizeof(mraid_device_t), GFP_KERNEL);
+ if (raid_dev == NULL) return -1;
+
+ memset(raid_dev, 0, sizeof(mraid_device_t));
+
+ /*
+ * Attach the adapter soft state to raid device soft state
+ */
+ adapter->raid_device = (caddr_t)raid_dev;
+ raid_dev->fast_load = megaraid_fast_load;
+
+
+ // our baseport
+ raid_dev->baseport = pci_resource_start(pdev, 0);
+
+ if (pci_request_regions(pdev, "MegaRAID: LSI Logic Corporation") != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: mem region busy\n"));
+
+ goto out_free_raid_dev;
+ }
+
+ raid_dev->baseaddr = ioremap_nocache(raid_dev->baseport, 128);
+
+ if (!raid_dev->baseaddr) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not map hba memory\n") );
+
+ goto out_release_regions;
+ }
+
+ //
+ // Setup the rest of the soft state using the library of FW routines
+ //
+
+ // request IRQ and register the interrupt service routine
+ if (request_irq(adapter->irq, megaraid_isr, SA_SHIRQ, "megaraid",
+ adapter)) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: Couldn't register IRQ %d!\n", adapter->irq));
+
+ goto out_iounmap;
+ }
+
+
+ // initialize the mutual exclusion lock for the mailbox
+ spin_lock_init(&raid_dev->mailbox_lock);
+
+ // allocate memory required for commands
+ if (megaraid_alloc_cmd_packets(adapter) != 0) {
+ goto out_free_irq;
+ }
+
+ // Product info
+ if (megaraid_mbox_product_info(adapter) != 0) {
+ goto out_alloc_cmds;
+ }
+
+ // Do we support extended CDBs
+ adapter->max_cdb_sz = 10;
+ if (megaraid_mbox_extended_cdb(adapter) == 0) {
+ adapter->max_cdb_sz = 16;
+ }
+
+ /*
+ * Do we support cluster environment, if we do, what is the initiator
+ * id.
+ * NOTE: In a non-cluster aware firmware environment, the LLD should
+ * return 7 as initiator id.
+ */
+ adapter->ha = 0;
+ adapter->init_id = -1;
+ if (megaraid_mbox_support_ha(adapter, &adapter->init_id) == 0) {
+ adapter->ha = 1;
+ }
+
+ /*
+ * Prepare the device ids array to have the mapping between the kernel
+ * device address and megaraid device address.
+ * We export the physical devices on their actual addresses. The
+ * logical drives are exported on a virtual SCSI channel
+ */
+ megaraid_mbox_setup_device_map(adapter);
+
+ // If the firmware supports random deletion, update the device id map
+ if (megaraid_mbox_support_random_del(adapter)) {
+
+ // Change the logical drives numbers in device_ids array one
+ // slot in device_ids is reserved for target id, that's why
+ // "<=" below
+ for (i = 0; i <= MAX_LOGICAL_DRIVES_40LD; i++) {
+ adapter->device_ids[adapter->max_channel][i] += 0x80;
+ }
+ adapter->device_ids[adapter->max_channel][adapter->init_id] =
+ 0xFF;
+
+ raid_dev->random_del_supported = 1;
+ }
+
+ /*
+ * find out the maximum number of scatter-gather elements supported by
+ * this firmware
+ */
+ adapter->sglen = megaraid_mbox_get_max_sg(adapter);
+
+ // enumerate RAID and SCSI channels so that all devices on SCSI
+ // channels can later be exported, including disk devices
+ megaraid_mbox_enum_raid_scsi(adapter);
+
+ /*
+ * Other parameters required by upper layer
+ *
+ * maximum number of sectors per IO command
+ */
+ adapter->max_sectors = megaraid_max_sectors;
+
+ /*
+ * number of queued commands per LUN.
+ */
+ adapter->cmd_per_lun = megaraid_cmd_per_lun;
+
+ /*
+ * Allocate resources required to issue FW calls, when sysfs is
+ * accessed
+ */
+ if (megaraid_sysfs_alloc_resources(adapter) != 0) {
+ goto out_alloc_cmds;
+ }
+
+ // Set the DMA mask to 64-bit. All supported controllers as capable of
+ // DMA in this range
+ if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFFFFFFFFFFULL) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: could not set DMA mask for 64-bit.\n"));
+
+ goto out_free_sysfs_res;
+ }
+
+ // setup tasklet for DPC
+ tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc,
+ (unsigned long)adapter);
+
+ con_log(CL_DLEVEL1, (KERN_INFO
+ "megaraid mbox hba successfully initialized\n"));
+
+ return 0;
+
+out_free_sysfs_res:
+ megaraid_sysfs_free_resources(adapter);
+out_alloc_cmds:
+ megaraid_free_cmd_packets(adapter);
+out_free_irq:
+ free_irq(adapter->irq, adapter);
+out_iounmap:
+ iounmap(raid_dev->baseaddr);
+out_release_regions:
+ pci_release_regions(pdev);
+out_free_raid_dev:
+ kfree(raid_dev);
+
+ return -1;
+}
+
+
+/**
+ * megaraid_fini_mbox - undo controller initialization
+ * @param adapter : our soft state
+ */
+static void
+megaraid_fini_mbox(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ // flush all caches
+ megaraid_mbox_flush_cache(adapter);
+
+ tasklet_kill(&adapter->dpc_h);
+
+ megaraid_sysfs_free_resources(adapter);
+
+ megaraid_free_cmd_packets(adapter);
+
+ free_irq(adapter->irq, adapter);
+
+ iounmap(raid_dev->baseaddr);
+
+ pci_release_regions(adapter->pdev);
+
+ kfree(raid_dev);
+
+ return;
+}
+
+
+/**
+ * megaraid_alloc_cmd_packets - allocate shared mailbox
+ * @param adapter : soft state of the raid controller
+ *
+ * Allocate and align the shared mailbox. This maibox is used to issue
+ * all the commands. For IO based controllers, the mailbox is also regsitered
+ * with the FW. Allocate memory for all commands as well.
+ * This is our big allocator
+ */
+static int
+megaraid_alloc_cmd_packets(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ struct pci_dev *pdev;
+ unsigned long align;
+ scb_t *scb;
+ mbox_ccb_t *ccb;
+ struct mraid_pci_blk *epthru_pci_blk;
+ struct mraid_pci_blk *sg_pci_blk;
+ struct mraid_pci_blk *mbox_pci_blk;
+ int i;
+
+ pdev = adapter->pdev;
+
+ /*
+ * Setup the mailbox
+ * Allocate the common 16-byte aligned memory for the handshake
+ * mailbox.
+ */
+ raid_dev->una_mbox64 = pci_alloc_consistent(adapter->pdev,
+ sizeof(mbox64_t), &raid_dev->una_mbox64_dma);
+
+ if (!raid_dev->una_mbox64) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ return -1;
+ }
+ memset(raid_dev->una_mbox64, 0, sizeof(mbox64_t));
+
+ /*
+ * Align the mailbox at 16-byte boundary
+ */
+ raid_dev->mbox = &raid_dev->una_mbox64->mbox32;
+
+ raid_dev->mbox = (mbox_t *)((((unsigned long)raid_dev->mbox) + 15) &
+ (~0UL ^ 0xFUL));
+
+ raid_dev->mbox64 = (mbox64_t *)(((unsigned long)raid_dev->mbox) - 8);
+
+ align = ((void *)raid_dev->mbox -
+ ((void *)&raid_dev->una_mbox64->mbox32));
+
+ raid_dev->mbox_dma = (unsigned long)raid_dev->una_mbox64_dma + 8 +
+ align;
+
+ // Allocate memory for commands issued internally
+ adapter->ibuf = pci_alloc_consistent(pdev, MBOX_IBUF_SIZE,
+ &adapter->ibuf_dma_h);
+ if (!adapter->ibuf) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ goto out_free_common_mbox;
+ }
+ memset(adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ // Allocate memory for our SCSI Command Blocks and their associated
+ // memory
+
+ /*
+ * Allocate memory for the base list of scb. Later allocate memory for
+ * CCBs and embedded components of each CCB and point the pointers in
+ * scb to the allocated components
+ * NOTE: The code to allocate SCB will be duplicated in all the LLD
+ * since the calling routine does not yet know the number of available
+ * commands.
+ */
+ adapter->kscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_SCSI_CMDS,
+ GFP_KERNEL);
+
+ if (adapter->kscb_list == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ goto out_free_ibuf;
+ }
+ memset(adapter->kscb_list, 0, sizeof(scb_t) * MBOX_MAX_SCSI_CMDS);
+
+ // memory allocation for our command packets
+ if (megaraid_mbox_setup_dma_pools(adapter) != 0) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ goto out_free_scb_list;
+ }
+
+ // Adjust the scb pointers and link in the free pool
+ epthru_pci_blk = raid_dev->epthru_pool;
+ sg_pci_blk = raid_dev->sg_pool;
+ mbox_pci_blk = raid_dev->mbox_pool;
+
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ scb = adapter->kscb_list + i;
+ ccb = raid_dev->ccb_list + i;
+
+ ccb->mbox = (mbox_t *)(mbox_pci_blk[i].vaddr + 16);
+ ccb->raw_mbox = (uint8_t *)ccb->mbox;
+ ccb->mbox64 = (mbox64_t *)(mbox_pci_blk[i].vaddr + 8);
+ ccb->mbox_dma_h = (unsigned long)mbox_pci_blk[i].dma_addr + 16;
+
+ // make sure the mailbox is aligned properly
+ if (ccb->mbox_dma_h & 0x0F) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid mbox: not aligned on 16-bytes\n"));
+
+ goto out_teardown_dma_pools;
+ }
+
+ ccb->epthru = (mraid_epassthru_t *)
+ epthru_pci_blk[i].vaddr;
+ ccb->epthru_dma_h = epthru_pci_blk[i].dma_addr;
+ ccb->pthru = (mraid_passthru_t *)ccb->epthru;
+ ccb->pthru_dma_h = ccb->epthru_dma_h;
+
+
+ ccb->sgl64 = (mbox_sgl64 *)sg_pci_blk[i].vaddr;
+ ccb->sgl_dma_h = sg_pci_blk[i].dma_addr;
+ ccb->sgl32 = (mbox_sgl32 *)ccb->sgl64;
+
+ scb->ccb = (caddr_t)ccb;
+ scb->gp = 0;
+
+ scb->sno = i; // command index
+
+ scb->scp = NULL;
+ scb->state = SCB_FREE;
+ scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_type = MRAID_DMA_NONE;
+ scb->dev_channel = -1;
+ scb->dev_target = -1;
+
+ // put scb in the free pool
+ list_add_tail(&scb->list, &adapter->kscb_pool);
+ }
+
+ return 0;
+
+out_teardown_dma_pools:
+ megaraid_mbox_teardown_dma_pools(adapter);
+out_free_scb_list:
+ kfree(adapter->kscb_list);
+out_free_ibuf:
+ pci_free_consistent(pdev, MBOX_IBUF_SIZE, (void *)adapter->ibuf,
+ adapter->ibuf_dma_h);
+out_free_common_mbox:
+ pci_free_consistent(adapter->pdev, sizeof(mbox64_t),
+ (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
+
+ return -1;
+}
+
+
+/**
+ * megaraid_free_cmd_packets - free memory
+ * @param adapter : soft state of the raid controller
+ *
+ * Release memory resources allocated for commands
+ */
+static void
+megaraid_free_cmd_packets(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ megaraid_mbox_teardown_dma_pools(adapter);
+
+ kfree(adapter->kscb_list);
+
+ pci_free_consistent(adapter->pdev, MBOX_IBUF_SIZE,
+ (void *)adapter->ibuf, adapter->ibuf_dma_h);
+
+ pci_free_consistent(adapter->pdev, sizeof(mbox64_t),
+ (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma);
+ return;
+}
+
+
+/**
+ * megaraid_mbox_setup_dma_pools - setup dma pool for command packets
+ * @param adapter : HBA soft state
+ *
+ * setup the dma pools for mailbox, passthru and extended passthru structures,
+ * and scatter-gather lists
+ */
+static int
+megaraid_mbox_setup_dma_pools(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ struct mraid_pci_blk *epthru_pci_blk;
+ struct mraid_pci_blk *sg_pci_blk;
+ struct mraid_pci_blk *mbox_pci_blk;
+ int i;
+
+
+
+ // Allocate memory for 16-bytes aligned mailboxes
+ raid_dev->mbox_pool_handle = pci_pool_create("megaraid mbox pool",
+ adapter->pdev,
+ sizeof(mbox64_t) + 16,
+ 16, 0);
+
+ if (raid_dev->mbox_pool_handle == NULL) {
+ goto fail_setup_dma_pool;
+ }
+
+ mbox_pci_blk = raid_dev->mbox_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ mbox_pci_blk[i].vaddr = pci_pool_alloc(
+ raid_dev->mbox_pool_handle,
+ GFP_KERNEL,
+ &mbox_pci_blk[i].dma_addr);
+ if (!mbox_pci_blk[i].vaddr) {
+ goto fail_setup_dma_pool;
+ }
+ }
+
+ /*
+ * Allocate memory for each embedded passthru strucuture pointer
+ * Request for a 128 bytes aligned structure for each passthru command
+ * structure
+ * Since passthru and extended passthru commands are exclusive, they
+ * share common memory pool. Passthru structures piggyback on memory
+ * allocted to extended passthru since passthru is smaller of the two
+ */
+ raid_dev->epthru_pool_handle = pci_pool_create("megaraid mbox pthru",
+ adapter->pdev, sizeof(mraid_epassthru_t), 128, 0);
+
+ if (raid_dev->epthru_pool_handle == NULL) {
+ goto fail_setup_dma_pool;
+ }
+
+ epthru_pci_blk = raid_dev->epthru_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ epthru_pci_blk[i].vaddr = pci_pool_alloc(
+ raid_dev->epthru_pool_handle,
+ GFP_KERNEL,
+ &epthru_pci_blk[i].dma_addr);
+ if (!epthru_pci_blk[i].vaddr) {
+ goto fail_setup_dma_pool;
+ }
+ }
+
+
+ // Allocate memory for each scatter-gather list. Request for 512 bytes
+ // alignment for each sg list
+ raid_dev->sg_pool_handle = pci_pool_create("megaraid mbox sg",
+ adapter->pdev,
+ sizeof(mbox_sgl64) * MBOX_MAX_SG_SIZE,
+ 512, 0);
+
+ if (raid_dev->sg_pool_handle == NULL) {
+ goto fail_setup_dma_pool;
+ }
+
+ sg_pci_blk = raid_dev->sg_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ sg_pci_blk[i].vaddr = pci_pool_alloc(
+ raid_dev->sg_pool_handle,
+ GFP_KERNEL,
+ &sg_pci_blk[i].dma_addr);
+ if (!sg_pci_blk[i].vaddr) {
+ goto fail_setup_dma_pool;
+ }
+ }
+
+ return 0;
+
+fail_setup_dma_pool:
+ megaraid_mbox_teardown_dma_pools(adapter);
+ return -1;
+}
+
+
+/**
+ * megaraid_mbox_teardown_dma_pools - teardown dma pools for command packets
+ * @param adapter : HBA soft state
+ *
+ * teardown the dma pool for mailbox, passthru and extended passthru
+ * structures, and scatter-gather lists
+ */
+static void
+megaraid_mbox_teardown_dma_pools(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ struct mraid_pci_blk *epthru_pci_blk;
+ struct mraid_pci_blk *sg_pci_blk;
+ struct mraid_pci_blk *mbox_pci_blk;
+ int i;
+
+
+ sg_pci_blk = raid_dev->sg_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS && sg_pci_blk[i].vaddr; i++) {
+ pci_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr,
+ sg_pci_blk[i].dma_addr);
+ }
+ if (raid_dev->sg_pool_handle)
+ pci_pool_destroy(raid_dev->sg_pool_handle);
+
+
+ epthru_pci_blk = raid_dev->epthru_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS && epthru_pci_blk[i].vaddr; i++) {
+ pci_pool_free(raid_dev->epthru_pool_handle,
+ epthru_pci_blk[i].vaddr, epthru_pci_blk[i].dma_addr);
+ }
+ if (raid_dev->epthru_pool_handle)
+ pci_pool_destroy(raid_dev->epthru_pool_handle);
+
+
+ mbox_pci_blk = raid_dev->mbox_pool;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS && mbox_pci_blk[i].vaddr; i++) {
+ pci_pool_free(raid_dev->mbox_pool_handle,
+ mbox_pci_blk[i].vaddr, mbox_pci_blk[i].dma_addr);
+ }
+ if (raid_dev->mbox_pool_handle)
+ pci_pool_destroy(raid_dev->mbox_pool_handle);
+
+ return;
+}
+
+
+/**
+ * megaraid_alloc_scb - detach and return a scb from the free list
+ * @adapter : controller's soft state
+ *
+ * return the scb from the head of the free list. NULL if there are none
+ * available
+ **/
+static inline scb_t *
+megaraid_alloc_scb(adapter_t *adapter, struct scsi_cmnd *scp)
+{
+ struct list_head *head = &adapter->kscb_pool;
+ scb_t *scb = NULL;
+ unsigned long flags;
+
+ // detach scb from free pool
+ spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ if (list_empty(head)) {
+ spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
+ return NULL;
+ }
+
+ scb = list_entry(head->next, scb_t, list);
+ list_del_init(&scb->list);
+
+ spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ scb->state = SCB_ACTIVE;
+ scb->scp = scp;
+ scb->dma_type = MRAID_DMA_NONE;
+
+ return scb;
+}
+
+
+/**
+ * megaraid_dealloc_scb - return the scb to the free pool
+ * @adapter : controller's soft state
+ * @scb : scb to be freed
+ *
+ * return the scb back to the free list of scbs. The caller must 'flush' the
+ * SCB before calling us. E.g., performing pci_unamp and/or pci_sync etc.
+ * NOTE NOTE: Make sure the scb is not on any list before calling this
+ * routine.
+ **/
+static inline void
+megaraid_dealloc_scb(adapter_t *adapter, scb_t *scb)
+{
+ unsigned long flags;
+
+ // put scb in the free pool
+ scb->state = SCB_FREE;
+ scb->scp = NULL;
+ spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ list_add(&scb->list, &adapter->kscb_pool);
+
+ spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags);
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_mksgl - make the scatter-gather list
+ * @adapter - controller's soft state
+ * @scb - scsi control block
+ *
+ * prepare the scatter-gather list
+ */
+static inline int
+megaraid_mbox_mksgl(adapter_t *adapter, scb_t *scb)
+{
+ struct scatterlist *sgl;
+ mbox_ccb_t *ccb;
+ struct page *page;
+ unsigned long offset;
+ struct scsi_cmnd *scp;
+ int sgcnt;
+ int i;
+
+
+ scp = scb->scp;
+ ccb = (mbox_ccb_t *)scb->ccb;
+
+ // no mapping required if no data to be transferred
+ if (!scp->request_buffer || !scp->request_bufflen)
+ return 0;
+
+ if (!scp->use_sg) { /* scatter-gather list not used */
+
+ page = virt_to_page(scp->request_buffer);
+
+ offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK);
+
+ ccb->buf_dma_h = pci_map_page(adapter->pdev, page, offset,
+ scp->request_bufflen,
+ scb->dma_direction);
+ scb->dma_type = MRAID_DMA_WBUF;
+
+ /*
+ * We need to handle special 64-bit commands that need a
+ * minimum of 1 SG
+ */
+ sgcnt = 1;
+ ccb->sgl64[0].address = ccb->buf_dma_h;
+ ccb->sgl64[0].length = scp->request_bufflen;
+
+ return sgcnt;
+ }
+
+ sgl = (struct scatterlist *)scp->request_buffer;
+
+ // The number of sg elements returned must not exceed our limit
+ sgcnt = pci_map_sg(adapter->pdev, sgl, scp->use_sg,
+ scb->dma_direction);
+
+ if (sgcnt > adapter->sglen) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid critical: too many sg elements:%d\n",
+ sgcnt));
+ BUG();
+ }
+
+ scb->dma_type = MRAID_DMA_WSG;
+
+ for (i = 0; i < sgcnt; i++, sgl++) {
+ ccb->sgl64[i].address = sg_dma_address(sgl);
+ ccb->sgl64[i].length = sg_dma_len(sgl);
+ }
+
+ // Return count of SG nodes
+ return sgcnt;
+}
+
+
+/**
+ * mbox_post_cmd - issue a mailbox command
+ * @adapter - controller's soft state
+ * @scb - command to be issued
+ *
+ * post the command to the controller if mailbox is availble.
+ */
+static inline int
+mbox_post_cmd(adapter_t *adapter, scb_t *scb)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ mbox_ccb_t *ccb;
+ unsigned long flags;
+ unsigned int i = 0;
+
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ mbox = raid_dev->mbox;
+ mbox64 = raid_dev->mbox64;
+
+ /*
+ * Check for busy mailbox. If it is, return failure - the caller
+ * should retry later.
+ */
+ spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags);
+
+ if (unlikely(mbox->busy)) {
+ do {
+ udelay(1);
+ i++;
+ rmb();
+ } while(mbox->busy && (i < max_mbox_busy_wait));
+
+ if (mbox->busy) {
+
+ spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
+
+ return -1;
+ }
+ }
+
+
+ // Copy this command's mailbox data into "adapter's" mailbox
+ memcpy((caddr_t)mbox64, (caddr_t)ccb->mbox64, 22);
+ mbox->cmdid = scb->sno;
+
+ adapter->outstanding_cmds++;
+
+ if (scb->dma_direction == PCI_DMA_TODEVICE) {
+ if (!scb->scp->use_sg) { // sg list not used
+ pci_dma_sync_single_for_device(adapter->pdev,
+ ccb->buf_dma_h,
+ scb->scp->request_bufflen,
+ PCI_DMA_TODEVICE);
+ }
+ else {
+ pci_dma_sync_sg_for_device(adapter->pdev,
+ scb->scp->request_buffer,
+ scb->scp->use_sg, PCI_DMA_TODEVICE);
+ }
+ }
+
+ mbox->busy = 1; // Set busy
+ mbox->poll = 0;
+ mbox->ack = 0;
+ wmb();
+
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
+
+ spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
+
+ return 0;
+}
+
+
+/**
+ * megaraid_queue_command - generic queue entry point for all LLDs
+ * @scp : pointer to the scsi command to be executed
+ * @done : callback routine to be called after the cmd has be completed
+ *
+ * Queue entry point for mailbox based controllers.
+ */
+static int
+megaraid_queue_command(struct scsi_cmnd *scp, void (* done)(struct scsi_cmnd *))
+{
+ adapter_t *adapter;
+ scb_t *scb;
+ int if_busy;
+
+ adapter = SCP2ADAPTER(scp);
+ scp->scsi_done = done;
+ scp->result = 0;
+
+ assert_spin_locked(adapter->host_lock);
+
+ spin_unlock(adapter->host_lock);
+
+ /*
+ * Allocate and build a SCB request
+ * if_busy flag will be set if megaraid_mbox_build_cmd() command could
+ * not allocate scb. We will return non-zero status in that case.
+ * NOTE: scb can be null even though certain commands completed
+ * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, it would
+ * return 0 in that case, and we would do the callback right away.
+ */
+ if_busy = 0;
+ scb = megaraid_mbox_build_cmd(adapter, scp, &if_busy);
+
+ if (scb) {
+ megaraid_mbox_runpendq(adapter, scb);
+ }
+
+ spin_lock(adapter->host_lock);
+
+ if (!scb) { // command already completed
+ done(scp);
+ return 0;
+ }
+
+ return if_busy;
+}
+
+
+/**
+ * megaraid_mbox_build_cmd - transform the mid-layer scsi command to megaraid
+ * firmware lingua
+ * @adapter - controller's soft state
+ * @scp - mid-layer scsi command pointer
+ * @busy - set if request could not be completed because of lack of
+ * resources
+ *
+ * convert the command issued by mid-layer to format understood by megaraid
+ * firmware. We also complete certain command without sending them to firmware
+ */
+static scb_t *
+megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy)
+{
+ mraid_device_t *rdev = ADAP2RAIDDEV(adapter);
+ int channel;
+ int target;
+ int islogical;
+ mbox_ccb_t *ccb;
+ mraid_passthru_t *pthru;
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ scb_t *scb;
+ char skip[] = "skipping";
+ char scan[] = "scanning";
+ char *ss;
+
+
+ /*
+ * Get the appropriate device map for the device this command is
+ * intended for
+ */
+ MRAID_GET_DEVICE_MAP(adapter, scp, channel, target, islogical);
+
+ /*
+ * Logical drive commands
+ */
+ if (islogical) {
+ switch (scp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ /*
+ * Do we support clustering and is the support enabled
+ * If no, return success always
+ */
+ if (!adapter->ha) {
+ scp->result = (DID_OK << 16);
+ return NULL;
+ }
+
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ scb->dma_direction = scp->sc_data_direction;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ ccb = (mbox_ccb_t *)scb->ccb;
+
+ /*
+ * The command id will be provided by the command
+ * issuance routine
+ */
+ ccb->raw_mbox[0] = CLUSTER_CMD;
+ ccb->raw_mbox[2] = RESERVATION_STATUS;
+ ccb->raw_mbox[3] = target;
+
+ return scb;
+
+ case MODE_SENSE:
+ if (scp->use_sg) {
+ struct scatterlist *sgl;
+ caddr_t vaddr;
+
+ sgl = (struct scatterlist *)scp->request_buffer;
+ if (sgl->page) {
+ vaddr = (caddr_t)
+ (page_address((&sgl[0])->page)
+ + (&sgl[0])->offset);
+
+ memset(vaddr, 0, scp->cmnd[4]);
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: invalid sg:%d\n",
+ __LINE__));
+ }
+ }
+ else {
+ memset(scp->request_buffer, 0, scp->cmnd[4]);
+ }
+ scp->result = (DID_OK << 16);
+ return NULL;
+
+ case INQUIRY:
+ /*
+ * Display the channel scan for logical drives
+ * Do not display scan for a channel if already done.
+ */
+ if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) {
+
+ con_log(CL_ANN, (KERN_INFO
+ "scsi[%d]: scanning scsi channel %d",
+ adapter->host->host_no,
+ SCP2CHANNEL(scp)));
+
+ con_log(CL_ANN, (
+ " [virtual] for logical drives\n"));
+
+ rdev->last_disp |= (1L << SCP2CHANNEL(scp));
+ }
+
+ /* Fall through */
+
+ case READ_CAPACITY:
+ /*
+ * Do not allow LUN > 0 for logical drives and
+ * requests for more than 40 logical drives
+ */
+ if (SCP2LUN(scp)) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+ if ((target % 0x80) >= MAX_LOGICAL_DRIVES_40LD) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+
+ /* Allocate a SCB and initialize passthru */
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ pthru = ccb->pthru;
+ mbox = ccb->mbox;
+ mbox64 = ccb->mbox64;
+
+ pthru->timeout = 0;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 1;
+ pthru->logdrv = target;
+ pthru->cdblen = scp->cmd_len;
+ memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
+
+ mbox->cmd = MBOXCMD_PASSTHRU64;
+ scb->dma_direction = scp->sc_data_direction;
+
+ pthru->dataxferlen = scp->request_bufflen;
+ pthru->dataxferaddr = ccb->sgl_dma_h;
+ pthru->numsge = megaraid_mbox_mksgl(adapter,
+ scb);
+
+ mbox->xferaddr = 0xFFFFFFFF;
+ mbox64->xferaddr_lo = (uint32_t )ccb->pthru_dma_h;
+ mbox64->xferaddr_hi = 0;
+
+ return scb;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+
+ /*
+ * Allocate a SCB and initialize mailbox
+ */
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ mbox = ccb->mbox;
+ mbox64 = ccb->mbox64;
+ mbox->logdrv = target;
+
+ /*
+ * A little HACK: 2nd bit is zero for all scsi read
+ * commands and is set for all scsi write commands
+ */
+ mbox->cmd = (scp->cmnd[0] & 0x02) ? MBOXCMD_LWRITE64:
+ MBOXCMD_LREAD64 ;
+
+ /*
+ * 6-byte READ(0x08) or WRITE(0x0A) cdb
+ */
+ if (scp->cmd_len == 6) {
+ mbox->numsectors = (uint32_t)scp->cmnd[4];
+ mbox->lba =
+ ((uint32_t)scp->cmnd[1] << 16) |
+ ((uint32_t)scp->cmnd[2] << 8) |
+ (uint32_t)scp->cmnd[3];
+
+ mbox->lba &= 0x1FFFFF;
+ }
+
+ /*
+ * 10-byte READ(0x28) or WRITE(0x2A) cdb
+ */
+ else if (scp->cmd_len == 10) {
+ mbox->numsectors =
+ (uint32_t)scp->cmnd[8] |
+ ((uint32_t)scp->cmnd[7] << 8);
+ mbox->lba =
+ ((uint32_t)scp->cmnd[2] << 24) |
+ ((uint32_t)scp->cmnd[3] << 16) |
+ ((uint32_t)scp->cmnd[4] << 8) |
+ (uint32_t)scp->cmnd[5];
+ }
+
+ /*
+ * 12-byte READ(0xA8) or WRITE(0xAA) cdb
+ */
+ else if (scp->cmd_len == 12) {
+ mbox->lba =
+ ((uint32_t)scp->cmnd[2] << 24) |
+ ((uint32_t)scp->cmnd[3] << 16) |
+ ((uint32_t)scp->cmnd[4] << 8) |
+ (uint32_t)scp->cmnd[5];
+
+ mbox->numsectors =
+ ((uint32_t)scp->cmnd[6] << 24) |
+ ((uint32_t)scp->cmnd[7] << 16) |
+ ((uint32_t)scp->cmnd[8] << 8) |
+ (uint32_t)scp->cmnd[9];
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: unsupported CDB length\n"));
+
+ megaraid_dealloc_scb(adapter, scb);
+
+ scp->result = (DID_ERROR << 16);
+ return NULL;
+ }
+
+ scb->dma_direction = scp->sc_data_direction;
+
+ // Calculate Scatter-Gather info
+ mbox64->xferaddr_lo = (uint32_t )ccb->sgl_dma_h;
+ mbox->numsge = megaraid_mbox_mksgl(adapter,
+ scb);
+ mbox->xferaddr = 0xFFFFFFFF;
+ mbox64->xferaddr_hi = 0;
+
+ return scb;
+
+ case RESERVE:
+ case RELEASE:
+ /*
+ * Do we support clustering and is the support enabled
+ */
+ if (!adapter->ha) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+ /*
+ * Allocate a SCB and initialize mailbox
+ */
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = 0xFF;
+ scb->dev_target = target;
+ ccb->raw_mbox[0] = CLUSTER_CMD;
+ ccb->raw_mbox[2] = (scp->cmnd[0] == RESERVE) ?
+ RESERVE_LD : RELEASE_LD;
+
+ ccb->raw_mbox[3] = target;
+ scb->dma_direction = scp->sc_data_direction;
+
+ return scb;
+
+ default:
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+ }
+ else { // Passthru device commands
+
+ // Do not allow access to target id > 15 or LUN > 7
+ if (target > 15 || SCP2LUN(scp) > 7) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+ // if fast load option was set and scan for last device is
+ // over, reset the fast_load flag so that during a possible
+ // next scan, devices can be made available
+ if (rdev->fast_load && (target == 15) &&
+ (SCP2CHANNEL(scp) == adapter->max_channel -1)) {
+
+ con_log(CL_ANN, (KERN_INFO
+ "megaraid[%d]: physical device scan re-enabled\n",
+ adapter->host->host_no));
+ rdev->fast_load = 0;
+ }
+
+ /*
+ * Display the channel scan for physical devices
+ */
+ if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) {
+
+ ss = rdev->fast_load ? skip : scan;
+
+ con_log(CL_ANN, (KERN_INFO
+ "scsi[%d]: %s scsi channel %d [Phy %d]",
+ adapter->host->host_no, ss, SCP2CHANNEL(scp),
+ channel));
+
+ con_log(CL_ANN, (
+ " for non-raid devices\n"));
+
+ rdev->last_disp |= (1L << SCP2CHANNEL(scp));
+ }
+
+ // disable channel sweep if fast load option given
+ if (rdev->fast_load) {
+ scp->result = (DID_BAD_TARGET << 16);
+ return NULL;
+ }
+
+ // Allocate a SCB and initialize passthru
+ if (!(scb = megaraid_alloc_scb(adapter, scp))) {
+ scp->result = (DID_ERROR << 16);
+ *busy = 1;
+ return NULL;
+ }
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scb->dev_channel = channel;
+ scb->dev_target = target;
+ scb->dma_direction = scp->sc_data_direction;
+ mbox = ccb->mbox;
+ mbox64 = ccb->mbox64;
+
+ // Does this firmware support extended CDBs
+ if (adapter->max_cdb_sz == 16) {
+ mbox->cmd = MBOXCMD_EXTPTHRU;
+
+ megaraid_mbox_prepare_epthru(adapter, scb, scp);
+
+ mbox64->xferaddr_lo = (uint32_t)ccb->epthru_dma_h;
+ mbox64->xferaddr_hi = 0;
+ mbox->xferaddr = 0xFFFFFFFF;
+ }
+ else {
+ mbox->cmd = MBOXCMD_PASSTHRU64;
+
+ megaraid_mbox_prepare_pthru(adapter, scb, scp);
+
+ mbox64->xferaddr_lo = (uint32_t)ccb->pthru_dma_h;
+ mbox64->xferaddr_hi = 0;
+ mbox->xferaddr = 0xFFFFFFFF;
+ }
+ return scb;
+ }
+
+ // NOT REACHED
+}
+
+
+/**
+ * megaraid_mbox_runpendq - execute commands queued in the pending queue
+ * @adapter : controller's soft state
+ * @scb : SCB to be queued in the pending list
+ *
+ * scan the pending list for commands which are not yet issued and try to
+ * post to the controller. The SCB can be a null pointer, which would indicate
+ * no SCB to be queue, just try to execute the ones in the pending list.
+ *
+ * NOTE: We do not actually traverse the pending list. The SCBs are plucked
+ * out from the head of the pending list. If it is successfully issued, the
+ * next SCB is at the head now.
+ */
+static void
+megaraid_mbox_runpendq(adapter_t *adapter, scb_t *scb_q)
+{
+ scb_t *scb;
+ unsigned long flags;
+
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+
+ if (scb_q) {
+ scb_q->state = SCB_PENDQ;
+ list_add_tail(&scb_q->list, &adapter->pend_list);
+ }
+
+ // if the adapter in not in quiescent mode, post the commands to FW
+ if (adapter->quiescent) {
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+ return;
+ }
+
+ while (!list_empty(&adapter->pend_list)) {
+
+ assert_spin_locked(PENDING_LIST_LOCK(adapter));
+
+ scb = list_entry(adapter->pend_list.next, scb_t, list);
+
+ // remove the scb from the pending list and try to
+ // issue. If we are unable to issue it, put back in
+ // the pending list and return
+
+ list_del_init(&scb->list);
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+ // if mailbox was busy, return SCB back to pending
+ // list. Make sure to add at the head, since that's
+ // where it would have been removed from
+
+ scb->state = SCB_ISSUED;
+
+ if (mbox_post_cmd(adapter, scb) != 0) {
+
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+
+ scb->state = SCB_PENDQ;
+
+ list_add(&scb->list, &adapter->pend_list);
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter),
+ flags);
+
+ return;
+ }
+
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+ }
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_prepare_pthru - prepare a command for physical devices
+ * @adapter - pointer to controller's soft state
+ * @scb - scsi control block
+ * @scp - scsi command from the mid-layer
+ *
+ * prepare a command for the scsi physical devices
+ */
+static void
+megaraid_mbox_prepare_pthru(adapter_t *adapter, scb_t *scb,
+ struct scsi_cmnd *scp)
+{
+ mbox_ccb_t *ccb;
+ mraid_passthru_t *pthru;
+ uint8_t channel;
+ uint8_t target;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ pthru = ccb->pthru;
+ channel = scb->dev_channel;
+ target = scb->dev_target;
+
+ // 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
+ pthru->timeout = 4;
+ pthru->ars = 1;
+ pthru->islogical = 0;
+ pthru->channel = 0;
+ pthru->target = (channel << 4) | target;
+ pthru->logdrv = SCP2LUN(scp);
+ pthru->reqsenselen = 14;
+ pthru->cdblen = scp->cmd_len;
+
+ memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
+
+ if (scp->request_bufflen) {
+ pthru->dataxferlen = scp->request_bufflen;
+ pthru->dataxferaddr = ccb->sgl_dma_h;
+ pthru->numsge = megaraid_mbox_mksgl(adapter, scb);
+ }
+ else {
+ pthru->dataxferaddr = 0;
+ pthru->dataxferlen = 0;
+ pthru->numsge = 0;
+ }
+ return;
+}
+
+
+/**
+ * megaraid_mbox_prepare_epthru - prepare a command for physical devices
+ * @adapter - pointer to controller's soft state
+ * @scb - scsi control block
+ * @scp - scsi command from the mid-layer
+ *
+ * prepare a command for the scsi physical devices. This rountine prepares
+ * commands for devices which can take extended CDBs (>10 bytes)
+ */
+static void
+megaraid_mbox_prepare_epthru(adapter_t *adapter, scb_t *scb,
+ struct scsi_cmnd *scp)
+{
+ mbox_ccb_t *ccb;
+ mraid_epassthru_t *epthru;
+ uint8_t channel;
+ uint8_t target;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ epthru = ccb->epthru;
+ channel = scb->dev_channel;
+ target = scb->dev_target;
+
+ // 0=6sec, 1=60sec, 2=10min, 3=3hrs, 4=NO timeout
+ epthru->timeout = 4;
+ epthru->ars = 1;
+ epthru->islogical = 0;
+ epthru->channel = 0;
+ epthru->target = (channel << 4) | target;
+ epthru->logdrv = SCP2LUN(scp);
+ epthru->reqsenselen = 14;
+ epthru->cdblen = scp->cmd_len;
+
+ memcpy(epthru->cdb, scp->cmnd, scp->cmd_len);
+
+ if (scp->request_bufflen) {
+ epthru->dataxferlen = scp->request_bufflen;
+ epthru->dataxferaddr = ccb->sgl_dma_h;
+ epthru->numsge = megaraid_mbox_mksgl(adapter, scb);
+ }
+ else {
+ epthru->dataxferaddr = 0;
+ epthru->dataxferlen = 0;
+ epthru->numsge = 0;
+ }
+ return;
+}
+
+
+/**
+ * megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs
+ * @adapter - controller's soft state
+ *
+ * Interrupt ackrowledgement sequence for memory mapped HBAs. Find out the
+ * completed command and put them on the completed list for later processing.
+ *
+ * Returns: 1 if the interrupt is valid, 0 otherwise
+ */
+static inline int
+megaraid_ack_sequence(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ scb_t *scb;
+ uint8_t nstatus;
+ uint8_t completed[MBOX_MAX_FIRMWARE_STATUS];
+ struct list_head clist;
+ int handled;
+ uint32_t dword;
+ unsigned long flags;
+ int i, j;
+
+
+ mbox = raid_dev->mbox;
+
+ // move the SCBs from the firmware completed array to our local list
+ INIT_LIST_HEAD(&clist);
+
+ // loop till F/W has more commands for us to complete
+ handled = 0;
+ spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags);
+ do {
+ /*
+ * Check if a valid interrupt is pending. If found, force the
+ * interrupt line low.
+ */
+ dword = RDOUTDOOR(raid_dev);
+ if (dword != 0x10001234) break;
+
+ handled = 1;
+
+ WROUTDOOR(raid_dev, 0x10001234);
+
+ nstatus = 0;
+ // wait for valid numstatus to post
+ for (i = 0; i < 0xFFFFF; i++) {
+ if (mbox->numstatus != 0xFF) {
+ nstatus = mbox->numstatus;
+ break;
+ }
+ rmb();
+ }
+ mbox->numstatus = 0xFF;
+
+ adapter->outstanding_cmds -= nstatus;
+
+ for (i = 0; i < nstatus; i++) {
+
+ // wait for valid command index to post
+ for (j = 0; j < 0xFFFFF; j++) {
+ if (mbox->completed[i] != 0xFF) break;
+ rmb();
+ }
+ completed[i] = mbox->completed[i];
+ mbox->completed[i] = 0xFF;
+
+ if (completed[i] == 0xFF) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid: command posting timed out\n"));
+
+ BUG();
+ continue;
+ }
+
+ // Get SCB associated with this command id
+ if (completed[i] >= MBOX_MAX_SCSI_CMDS) {
+ // a cmm command
+ scb = adapter->uscb_list + (completed[i] -
+ MBOX_MAX_SCSI_CMDS);
+ }
+ else {
+ // an os command
+ scb = adapter->kscb_list + completed[i];
+ }
+
+ scb->status = mbox->status;
+ list_add_tail(&scb->list, &clist);
+ }
+
+ // Acknowledge interrupt
+ WRINDOOR(raid_dev, 0x02);
+
+ } while(1);
+
+ spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags);
+
+
+ // put the completed commands in the completed list. DPC would
+ // complete these commands later
+ spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
+
+ list_splice(&clist, &adapter->completed_list);
+
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
+
+
+ // schedule the DPC if there is some work for it
+ if (handled)
+ tasklet_schedule(&adapter->dpc_h);
+
+ return handled;
+}
+
+
+/**
+ * megaraid_isr - isr for memory based mailbox based controllers
+ * @irq - irq
+ * @devp - pointer to our soft state
+ * @regs - unused
+ *
+ * Interrupt service routine for memory-mapped mailbox controllers.
+ */
+static irqreturn_t
+megaraid_isr(int irq, void *devp, struct pt_regs *regs)
+{
+ adapter_t *adapter = devp;
+ int handled;
+
+ handled = megaraid_ack_sequence(adapter);
+
+ /* Loop through any pending requests */
+ if (!adapter->quiescent) {
+ megaraid_mbox_runpendq(adapter, NULL);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/**
+ * megaraid_mbox_sync_scb - sync kernel buffers
+ * @adapter : controller's soft state
+ * @scb : pointer to the resource packet
+ *
+ * DMA sync if required.
+ */
+static inline void
+megaraid_mbox_sync_scb(adapter_t *adapter, scb_t *scb)
+{
+ mbox_ccb_t *ccb;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+
+ switch (scb->dma_type) {
+
+ case MRAID_DMA_WBUF:
+ if (scb->dma_direction == PCI_DMA_FROMDEVICE) {
+ pci_dma_sync_single_for_cpu(adapter->pdev,
+ ccb->buf_dma_h,
+ scb->scp->request_bufflen,
+ PCI_DMA_FROMDEVICE);
+ }
+
+ pci_unmap_page(adapter->pdev, ccb->buf_dma_h,
+ scb->scp->request_bufflen, scb->dma_direction);
+
+ break;
+
+ case MRAID_DMA_WSG:
+ if (scb->dma_direction == PCI_DMA_FROMDEVICE) {
+ pci_dma_sync_sg_for_cpu(adapter->pdev,
+ scb->scp->request_buffer,
+ scb->scp->use_sg, PCI_DMA_FROMDEVICE);
+ }
+
+ pci_unmap_sg(adapter->pdev, scb->scp->request_buffer,
+ scb->scp->use_sg, scb->dma_direction);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_dpc - the tasklet to complete the commands from completed list
+ * @devp : pointer to HBA soft state
+ *
+ * Pick up the commands from the completed list and send back to the owners.
+ * This is a reentrant function and does not assume any locks are held while
+ * it is being called.
+ */
+static void
+megaraid_mbox_dpc(unsigned long devp)
+{
+ adapter_t *adapter = (adapter_t *)devp;
+ mraid_device_t *raid_dev;
+ struct list_head clist;
+ struct scatterlist *sgl;
+ scb_t *scb;
+ scb_t *tmp;
+ struct scsi_cmnd *scp;
+ mraid_passthru_t *pthru;
+ mraid_epassthru_t *epthru;
+ mbox_ccb_t *ccb;
+ int islogical;
+ int pdev_index;
+ int pdev_state;
+ mbox_t *mbox;
+ unsigned long flags;
+ uint8_t c;
+ int status;
+
+
+ if (!adapter) return;
+
+ raid_dev = ADAP2RAIDDEV(adapter);
+
+ // move the SCBs from the completed list to our local list
+ INIT_LIST_HEAD(&clist);
+
+ spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
+
+ list_splice_init(&adapter->completed_list, &clist);
+
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
+
+
+ list_for_each_entry_safe(scb, tmp, &clist, list) {
+
+ status = scb->status;
+ scp = scb->scp;
+ ccb = (mbox_ccb_t *)scb->ccb;
+ pthru = ccb->pthru;
+ epthru = ccb->epthru;
+ mbox = ccb->mbox;
+
+ // Make sure f/w has completed a valid command
+ if (scb->state != SCB_ISSUED) {
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid critical err: invalid command %d:%d:%p\n",
+ scb->sno, scb->state, scp));
+ BUG();
+ continue; // Must never happen!
+ }
+
+ // check for the management command and complete it right away
+ if (scb->sno >= MBOX_MAX_SCSI_CMDS) {
+ scb->state = SCB_FREE;
+ scb->status = status;
+
+ // remove from local clist
+ list_del_init(&scb->list);
+
+ megaraid_mbox_mm_done(adapter, scb);
+
+ continue;
+ }
+
+ // Was an abort issued for this command earlier
+ if (scb->state & SCB_ABORT) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: aborted cmd %lx[%x] completed\n",
+ scp->serial_number, scb->sno));
+ }
+
+ /*
+ * If the inquiry came of a disk drive which is not part of
+ * any RAID array, expose it to the kernel. For this to be
+ * enabled, user must set the "megaraid_expose_unconf_disks"
+ * flag to 1 by specifying it on module parameter list.
+ * This would enable data migration off drives from other
+ * configurations.
+ */
+ islogical = MRAID_IS_LOGICAL(adapter, scp);
+ if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0
+ && IS_RAID_CH(raid_dev, scb->dev_channel)) {
+
+ if (scp->use_sg) {
+ sgl = (struct scatterlist *)
+ scp->request_buffer;
+
+ if (sgl->page) {
+ c = *(unsigned char *)
+ (page_address((&sgl[0])->page) +
+ (&sgl[0])->offset);
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: invalid sg:%d\n",
+ __LINE__));
+ c = 0;
+ }
+ }
+ else {
+ c = *(uint8_t *)scp->request_buffer;
+ }
+
+ if ((c & 0x1F ) == TYPE_DISK) {
+ pdev_index = (scb->dev_channel * 16) +
+ scb->dev_target;
+ pdev_state =
+ raid_dev->pdrv_state[pdev_index] & 0x0F;
+
+ if (pdev_state == PDRV_ONLINE ||
+ pdev_state == PDRV_FAILED ||
+ pdev_state == PDRV_RBLD ||
+ pdev_state == PDRV_HOTSPARE ||
+ megaraid_expose_unconf_disks == 0) {
+
+ status = 0xF0;
+ }
+ }
+ }
+
+ // Convert MegaRAID status to Linux error code
+ switch (status) {
+
+ case 0x00:
+
+ scp->result = (DID_OK << 16);
+ break;
+
+ case 0x02:
+
+ /* set sense_buffer and result fields */
+ if (mbox->cmd == MBOXCMD_PASSTHRU ||
+ mbox->cmd == MBOXCMD_PASSTHRU64) {
+
+ memcpy(scp->sense_buffer, pthru->reqsensearea,
+ 14);
+
+ scp->result = DRIVER_SENSE << 24 |
+ DID_OK << 16 | CHECK_CONDITION << 1;
+ }
+ else {
+ if (mbox->cmd == MBOXCMD_EXTPTHRU) {
+
+ memcpy(scp->sense_buffer,
+ epthru->reqsensearea, 14);
+
+ scp->result = DRIVER_SENSE << 24 |
+ DID_OK << 16 |
+ CHECK_CONDITION << 1;
+ } else {
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = ABORTED_COMMAND;
+ scp->result = CHECK_CONDITION << 1;
+ }
+ }
+ break;
+
+ case 0x08:
+
+ scp->result = DID_BUS_BUSY << 16 | status;
+ break;
+
+ default:
+
+ /*
+ * If TEST_UNIT_READY fails, we know RESERVATION_STATUS
+ * failed
+ */
+ if (scp->cmnd[0] == TEST_UNIT_READY) {
+ scp->result = DID_ERROR << 16 |
+ RESERVATION_CONFLICT << 1;
+ }
+ else
+ /*
+ * Error code returned is 1 if Reserve or Release
+ * failed or the input parameter is invalid
+ */
+ if (status == 1 && (scp->cmnd[0] == RESERVE ||
+ scp->cmnd[0] == RELEASE)) {
+
+ scp->result = DID_ERROR << 16 |
+ RESERVATION_CONFLICT << 1;
+ }
+ else {
+ scp->result = DID_BAD_TARGET << 16 | status;
+ }
+ }
+
+ // print a debug message for all failed commands
+ if (status) {
+ megaraid_mbox_display_scb(adapter, scb);
+ }
+
+ // Free our internal resources and call the mid-layer callback
+ // routine
+ megaraid_mbox_sync_scb(adapter, scb);
+
+ // remove from local clist
+ list_del_init(&scb->list);
+
+ // put back in free list
+ megaraid_dealloc_scb(adapter, scb);
+
+ // send the scsi packet back to kernel
+ spin_lock(adapter->host_lock);
+ scp->scsi_done(scp);
+ spin_unlock(adapter->host_lock);
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_abort_handler - abort the scsi command
+ * @scp : command to be aborted
+ *
+ * Abort a previous SCSI request. Only commands on the pending list can be
+ * aborted. All the commands issued to the F/W must complete.
+ **/
+static int
+megaraid_abort_handler(struct scsi_cmnd *scp)
+{
+ adapter_t *adapter;
+ mraid_device_t *raid_dev;
+ scb_t *scb;
+ scb_t *tmp;
+ int found;
+ unsigned long flags;
+ int i;
+
+
+ adapter = SCP2ADAPTER(scp);
+ raid_dev = ADAP2RAIDDEV(adapter);
+
+ assert_spin_locked(adapter->host_lock);
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: aborting-%ld cmd=%x <c=%d t=%d l=%d>\n",
+ scp->serial_number, scp->cmnd[0], SCP2CHANNEL(scp),
+ SCP2TARGET(scp), SCP2LUN(scp)));
+
+ // If FW has stopped responding, simply return failure
+ if (raid_dev->hw_error) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: hw error, not aborting\n"));
+ return FAILED;
+ }
+
+ // There might a race here, where the command was completed by the
+ // firmware and now it is on the completed list. Before we could
+ // complete the command to the kernel in dpc, the abort came.
+ // Find out if this is the case to avoid the race.
+ scb = NULL;
+ spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags);
+ list_for_each_entry_safe(scb, tmp, &adapter->completed_list, list) {
+
+ if (scb->scp == scp) { // Found command
+
+ list_del_init(&scb->list); // from completed list
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: %ld:%d[%d:%d], abort from completed list\n",
+ scp->serial_number, scb->sno,
+ scb->dev_channel, scb->dev_target));
+
+ scp->result = (DID_ABORT << 16);
+ scp->scsi_done(scp);
+
+ megaraid_dealloc_scb(adapter, scb);
+
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter),
+ flags);
+
+ return SUCCESS;
+ }
+ }
+ spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags);
+
+
+ // Find out if this command is still on the pending list. If it is and
+ // was never issued, abort and return success. If the command is owned
+ // by the firmware, we must wait for it to complete by the FW.
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+ list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) {
+
+ if (scb->scp == scp) { // Found command
+
+ list_del_init(&scb->list); // from pending list
+
+ ASSERT(!(scb->state & SCB_ISSUED));
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: %ld[%d:%d], driver owner\n",
+ scp->serial_number, scb->dev_channel,
+ scb->dev_target));
+
+ scp->result = (DID_ABORT << 16);
+ scp->scsi_done(scp);
+
+ megaraid_dealloc_scb(adapter, scb);
+
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter),
+ flags);
+
+ return SUCCESS;
+ }
+ }
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+
+ // Check do we even own this command, in which case this would be
+ // owned by the firmware. The only way to locate the FW scb is to
+ // traverse through the list of all SCB, since driver does not
+ // maintain these SCBs on any list
+ found = 0;
+ for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) {
+ scb = adapter->kscb_list + i;
+
+ if (scb->scp == scp) {
+
+ found = 1;
+
+ if (!(scb->state & SCB_ISSUED)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: %ld%d[%d:%d], invalid state\n",
+ scp->serial_number, scb->sno, scb->dev_channel,
+ scb->dev_target));
+ BUG();
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: %ld:%d[%d:%d], fw owner\n",
+ scp->serial_number, scb->sno, scb->dev_channel,
+ scb->dev_target));
+ }
+ }
+ }
+
+ if (!found) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid abort: scsi cmd:%ld, do now own\n",
+ scp->serial_number));
+
+ // FIXME: Should there be a callback for this command?
+ return SUCCESS;
+ }
+
+ // We cannot actually abort a command owned by firmware, return
+ // failure and wait for reset. In host reset handler, we will find out
+ // if the HBA is still live
+ return FAILED;
+}
+
+
+/**
+ * megaraid_reset_handler - device reset hadler for mailbox based driver
+ * @scp : reference command
+ *
+ * Reset handler for the mailbox based controller. First try to find out if
+ * the FW is still live, in which case the outstanding commands counter mut go
+ * down to 0. If that happens, also issue the reservation reset command to
+ * relinquish (possible) reservations on the logical drives connected to this
+ * host
+ **/
+static int
+megaraid_reset_handler(struct scsi_cmnd *scp)
+{
+ adapter_t *adapter;
+ scb_t *scb;
+ scb_t *tmp;
+ mraid_device_t *raid_dev;
+ unsigned long flags;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+ int recovery_window;
+ int recovering;
+ int i;
+
+ adapter = SCP2ADAPTER(scp);
+ raid_dev = ADAP2RAIDDEV(adapter);
+
+ assert_spin_locked(adapter->host_lock);
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: reseting the host...\n"));
+
+ // return failure if adapter is not responding
+ if (raid_dev->hw_error) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: hw error, cannot reset\n"));
+ return FAILED;
+ }
+
+
+ // Under exceptional conditions, FW can take up to 3 minutes to
+ // complete command processing. Wait for additional 2 minutes for the
+ // pending commands counter to go down to 0. If it doesn't, let the
+ // controller be marked offline
+ // Also, reset all the commands currently owned by the driver
+ spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags);
+ list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) {
+
+ list_del_init(&scb->list); // from pending list
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: %ld:%d[%d:%d], reset from pending list\n",
+ scp->serial_number, scb->sno,
+ scb->dev_channel, scb->dev_target));
+
+ scp->result = (DID_RESET << 16);
+ scp->scsi_done(scp);
+
+ megaraid_dealloc_scb(adapter, scb);
+ }
+ spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags);
+
+ if (adapter->outstanding_cmds) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: %d outstanding commands. Max wait %d sec\n",
+ adapter->outstanding_cmds, MBOX_RESET_WAIT));
+ }
+
+ spin_unlock(adapter->host_lock);
+
+ recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT;
+
+ recovering = adapter->outstanding_cmds;
+
+ for (i = 0; i < recovery_window && adapter->outstanding_cmds; i++) {
+
+ megaraid_ack_sequence(adapter);
+
+ // print a message once every 5 seconds only
+ if (!(i % 5)) {
+ con_log(CL_ANN, (
+ "megaraid mbox: Wait for %d commands to complete:%d\n",
+ adapter->outstanding_cmds,
+ MBOX_RESET_WAIT - i));
+ }
+
+ // bailout if no recovery happended in reset time
+ if ((i == MBOX_RESET_WAIT) &&
+ (recovering == adapter->outstanding_cmds)) {
+ break;
+ }
+
+ msleep(1000);
+ }
+
+ spin_lock(adapter->host_lock);
+
+ // If still outstanding commands, bail out
+ if (adapter->outstanding_cmds) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: critical hardware error!\n"));
+
+ raid_dev->hw_error = 1;
+
+ return FAILED;
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mbox: reset sequence completed sucessfully\n"));
+ }
+
+
+ // If the controller supports clustering, reset reservations
+ if (!adapter->ha) return SUCCESS;
+
+ // clear reservations if any
+ raw_mbox[0] = CLUSTER_CMD;
+ raw_mbox[2] = RESET_RESERVATIONS;
+
+ rval = SUCCESS;
+ if (mbox_post_sync_cmd_fast(adapter, raw_mbox) == 0) {
+ con_log(CL_ANN,
+ (KERN_INFO "megaraid: reservation reset\n"));
+ }
+ else {
+ rval = FAILED;
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: reservation reset failed\n"));
+ }
+
+ return rval;
+}
+
+
+/*
+ * START: internal commands library
+ *
+ * This section of the driver has the common routine used by the driver and
+ * also has all the FW routines
+ */
+
+/**
+ * mbox_post_sync_cmd() - blocking command to the mailbox based controllers
+ * @adapter - controller's soft state
+ * @raw_mbox - the mailbox
+ *
+ * Issue a scb in synchronous and non-interrupt mode for mailbox based
+ * controllers
+ */
+static int
+mbox_post_sync_cmd(adapter_t *adapter, uint8_t raw_mbox[])
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ uint8_t status;
+ int i;
+
+
+ mbox64 = raid_dev->mbox64;
+ mbox = raid_dev->mbox;
+
+ /*
+ * Wait until mailbox is free
+ */
+ if (megaraid_busywait_mbox(raid_dev) != 0)
+ goto blocked_mailbox;
+
+ /*
+ * Copy mailbox data into host structure
+ */
+ memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 16);
+ mbox->cmdid = 0xFE;
+ mbox->busy = 1;
+ mbox->poll = 0;
+ mbox->ack = 0;
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+
+ wmb();
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
+
+ // wait for maximum 1 second for status to post. If the status is not
+ // available within 1 second, assume FW is initializing and wait
+ // for an extended amount of time
+ if (mbox->numstatus == 0xFF) { // status not yet available
+ udelay(25);;
+
+ for (i = 0; mbox->numstatus == 0xFF && i < 1000; i++) {
+ rmb();
+ msleep(1);
+ }
+
+
+ if (i == 1000) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mailbox: wait for FW to boot "));
+
+ for (i = 0; (mbox->numstatus == 0xFF) &&
+ (i < MBOX_RESET_WAIT); i++) {
+ rmb();
+ con_log(CL_ANN, ("\b\b\b\b\b[%03d]",
+ MBOX_RESET_WAIT - i));
+ msleep(1000);
+ }
+
+ if (i == MBOX_RESET_WAIT) {
+
+ con_log(CL_ANN, (
+ "\nmegaraid mailbox: status not available\n"));
+
+ return -1;
+ }
+ con_log(CL_ANN, ("\b\b\b\b\b[ok] \n"));
+ }
+ }
+
+ // wait for maximum 1 second for poll semaphore
+ if (mbox->poll != 0x77) {
+ udelay(25);
+
+ for (i = 0; (mbox->poll != 0x77) && (i < 1000); i++) {
+ rmb();
+ msleep(1);
+ }
+
+ if (i == 1000) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: could not get poll semaphore\n"));
+ return -1;
+ }
+ }
+
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2);
+ wmb();
+
+ // wait for maximum 1 second for acknowledgement
+ if (RDINDOOR(raid_dev) & 0x2) {
+ udelay(25);
+
+ for (i = 0; (RDINDOOR(raid_dev) & 0x2) && (i < 1000); i++) {
+ rmb();
+ msleep(1);
+ }
+
+ if (i == 1000) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mailbox: could not acknowledge\n"));
+ return -1;
+ }
+ }
+ mbox->poll = 0;
+ mbox->ack = 0x77;
+
+ status = mbox->status;
+
+ // invalidate the completed command id array. After command
+ // completion, firmware would write the valid id.
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+ for (i = 0; i < MBOX_MAX_FIRMWARE_STATUS; i++) {
+ mbox->completed[i] = 0xFF;
+ }
+
+ return status;
+
+blocked_mailbox:
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: blocked mailbox\n") );
+ return -1;
+}
+
+
+/**
+ * mbox_post_sync_cmd_fast - blocking command to the mailbox based controllers
+ * @adapter - controller's soft state
+ * @raw_mbox - the mailbox
+ *
+ * Issue a scb in synchronous and non-interrupt mode for mailbox based
+ * controllers. This is a faster version of the synchronous command and
+ * therefore can be called in interrupt-context as well
+ */
+static int
+mbox_post_sync_cmd_fast(adapter_t *adapter, uint8_t raw_mbox[])
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ long i;
+
+
+ mbox = raid_dev->mbox;
+
+ // return immediately if the mailbox is busy
+ if (mbox->busy) return -1;
+
+ // Copy mailbox data into host structure
+ memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 14);
+ mbox->cmdid = 0xFE;
+ mbox->busy = 1;
+ mbox->poll = 0;
+ mbox->ack = 0;
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+
+ wmb();
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1);
+
+ for (i = 0; i < 0xFFFFF; i++) {
+ if (mbox->numstatus != 0xFF) break;
+ }
+
+ if (i == 0xFFFFF) {
+ // We may need to re-calibrate the counter
+ con_log(CL_ANN, (KERN_CRIT
+ "megaraid: fast sync command timed out\n"));
+ }
+
+ WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2);
+ wmb();
+
+ return mbox->status;
+}
+
+
+/**
+ * megaraid_busywait_mbox() - Wait until the controller's mailbox is available
+ * @raid_dev - RAID device (HBA) soft state
+ *
+ * wait until the controller's mailbox is available to accept more commands.
+ * wait for at most 1 second
+ */
+static int
+megaraid_busywait_mbox(mraid_device_t *raid_dev)
+{
+ mbox_t *mbox = raid_dev->mbox;
+ int i = 0;
+
+ if (mbox->busy) {
+ udelay(25);
+ for (i = 0; mbox->busy && i < 1000; i++)
+ msleep(1);
+ }
+
+ if (i < 1000) return 0;
+ else return -1;
+}
+
+
+/**
+ * megaraid_mbox_product_info - some static information about the controller
+ * @adapter - our soft state
+ *
+ * issue commands to the controller to grab some parameters required by our
+ * caller.
+ */
+static int
+megaraid_mbox_product_info(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ mraid_pinfo_t *pinfo;
+ dma_addr_t pinfo_dma_h;
+ mraid_inquiry3_t *mraid_inq3;
+ int i;
+
+
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+ mbox = (mbox_t *)raw_mbox;
+
+ /*
+ * Issue an ENQUIRY3 command to find out certain adapter parameters,
+ * e.g., max channels, max commands etc.
+ */
+ pinfo = pci_alloc_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ &pinfo_dma_h);
+
+ if (pinfo == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ return -1;
+ }
+ memset(pinfo, 0, sizeof(mraid_pinfo_t));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = FC_NEW_CONFIG;
+ raw_mbox[2] = NC_SUBOP_ENQUIRY3;
+ raw_mbox[3] = ENQ3_GET_SOLICITED_FULL;
+
+ // Issue the command
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid: Inquiry3 failed\n"));
+
+ pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ pinfo, pinfo_dma_h);
+
+ return -1;
+ }
+
+ /*
+ * Collect information about state of each physical drive
+ * attached to the controller. We will expose all the disks
+ * which are not part of RAID
+ */
+ mraid_inq3 = (mraid_inquiry3_t *)adapter->ibuf;
+ for (i = 0; i < MBOX_MAX_PHYSICAL_DRIVES; i++) {
+ raid_dev->pdrv_state[i] = mraid_inq3->pdrv_state[i];
+ }
+
+ /*
+ * Get product info for information like number of channels,
+ * maximum commands supported.
+ */
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+ mbox->xferaddr = (uint32_t)pinfo_dma_h;
+
+ raw_mbox[0] = FC_NEW_CONFIG;
+ raw_mbox[2] = NC_SUBOP_PRODUCT_INFO;
+
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: product info failed\n"));
+
+ pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t),
+ pinfo, pinfo_dma_h);
+
+ return -1;
+ }
+
+ /*
+ * Setup some parameters for host, as required by our caller
+ */
+ adapter->max_channel = pinfo->nchannels;
+
+ /*
+ * we will export all the logical drives on a single channel.
+ * Add 1 since inquires do not come for inititor ID
+ */
+ adapter->max_target = MAX_LOGICAL_DRIVES_40LD + 1;
+ adapter->max_lun = 8; // up to 8 LUNs for non-disk devices
+
+ /*
+ * These are the maximum outstanding commands for the scsi-layer
+ */
+ adapter->max_cmds = MBOX_MAX_SCSI_CMDS;
+
+ memset(adapter->fw_version, 0, VERSION_SIZE);
+ memset(adapter->bios_version, 0, VERSION_SIZE);
+
+ memcpy(adapter->fw_version, pinfo->fw_version, 4);
+ adapter->fw_version[4] = 0;
+
+ memcpy(adapter->bios_version, pinfo->bios_version, 4);
+ adapter->bios_version[4] = 0;
+
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: fw version:[%s] bios version:[%s]\n",
+ adapter->fw_version, adapter->bios_version));
+
+ pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), pinfo,
+ pinfo_dma_h);
+
+ return 0;
+}
+
+
+
+/**
+ * megaraid_mbox_extended_cdb - check for support for extended CDBs
+ * @adapter - soft state for the controller
+ *
+ * this routine check whether the controller in question supports extended
+ * ( > 10 bytes ) CDBs
+ */
+static int
+megaraid_mbox_extended_cdb(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = MAIN_MISC_OPCODE;
+ raw_mbox[2] = SUPPORT_EXT_CDB;
+
+ /*
+ * Issue the command
+ */
+ rval = 0;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+ rval = -1;
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_mbox_support_ha - Do we support clustering
+ * @adapter - soft state for the controller
+ * @init_id - ID of the initiator
+ *
+ * Determine if the firmware supports clustering and the ID of the initiator.
+ */
+static int
+megaraid_mbox_support_ha(adapter_t *adapter, uint16_t *init_id)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = GET_TARGET_ID;
+
+ // Issue the command
+ *init_id = 7;
+ rval = -1;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+
+ *init_id = *(uint8_t *)adapter->ibuf;
+
+ con_log(CL_ANN, (KERN_INFO
+ "megaraid: cluster firmware, initiator ID: %d\n",
+ *init_id));
+
+ rval = 0;
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_mbox_support_random_del - Do we support random deletion
+ * @adapter - soft state for the controller
+ *
+ * Determine if the firmware supports random deletion
+ * Return: 1 is operation supported, 0 otherwise
+ */
+static int
+megaraid_mbox_support_random_del(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int rval;
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ raw_mbox[0] = FC_DEL_LOGDRV;
+ raw_mbox[2] = OP_SUP_DEL_LOGDRV;
+
+ // Issue the command
+ rval = 0;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+
+ con_log(CL_DLEVEL1, ("megaraid: supports random deletion\n"));
+
+ rval = 1;
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_mbox_get_max_sg - maximum sg elements supported by the firmware
+ * @adapter - soft state for the controller
+ *
+ * Find out the maximum number of scatter-gather elements supported by the
+ * firmware
+ */
+static int
+megaraid_mbox_get_max_sg(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+ int nsg;
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = MAIN_MISC_OPCODE;
+ raw_mbox[2] = GET_MAX_SG_SUPPORT;
+
+ // Issue the command
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+ nsg = *(uint8_t *)adapter->ibuf;
+ }
+ else {
+ nsg = MBOX_DEFAULT_SG_SIZE;
+ }
+
+ if (nsg > MBOX_MAX_SG_SIZE) nsg = MBOX_MAX_SG_SIZE;
+
+ return nsg;
+}
+
+
+/**
+ * megaraid_mbox_enum_raid_scsi - enumerate the RAID and SCSI channels
+ * @adapter - soft state for the controller
+ *
+ * Enumerate the RAID and SCSI channels for ROMB platoforms so that channels
+ * can be exported as regular SCSI channels
+ */
+static void
+megaraid_mbox_enum_raid_scsi(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h;
+
+ memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE);
+
+ raw_mbox[0] = CHNL_CLASS;
+ raw_mbox[2] = GET_CHNL_CLASS;
+
+ // Issue the command. If the command fails, all channels are RAID
+ // channels
+ raid_dev->channel_class = 0xFF;
+ if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) {
+ raid_dev->channel_class = *(uint8_t *)adapter->ibuf;
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_flush_cache - flush adapter and disks cache
+ * @param adapter : soft state for the controller
+ *
+ * Flush adapter cache followed by disks cache
+ */
+static void
+megaraid_mbox_flush_cache(adapter_t *adapter)
+{
+ mbox_t *mbox;
+ uint8_t raw_mbox[sizeof(mbox_t)];
+
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((caddr_t)raw_mbox, 0, sizeof(mbox_t));
+
+ raw_mbox[0] = FLUSH_ADAPTER;
+
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+ con_log(CL_ANN, ("megaraid: flush adapter failed\n"));
+ }
+
+ raw_mbox[0] = FLUSH_SYSTEM;
+
+ if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) {
+ con_log(CL_ANN, ("megaraid: flush disks cache failed\n"));
+ }
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_display_scb - display SCB information, mostly debug purposes
+ * @param adapter : controllers' soft state
+ * @param scb : SCB to be displayed
+ * @param level : debug level for console print
+ *
+ * Diplay information about the given SCB iff the current debug level is
+ * verbose
+ */
+static void
+megaraid_mbox_display_scb(adapter_t *adapter, scb_t *scb)
+{
+ mbox_ccb_t *ccb;
+ struct scsi_cmnd *scp;
+ mbox_t *mbox;
+ int level;
+ int i;
+
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ scp = scb->scp;
+ mbox = ccb->mbox;
+
+ level = CL_DLEVEL3;
+
+ con_log(level, (KERN_NOTICE
+ "megaraid mailbox: status:%#x cmd:%#x id:%#x ", scb->status,
+ mbox->cmd, scb->sno));
+
+ con_log(level, ("sec:%#x lba:%#x addr:%#x ld:%d sg:%d\n",
+ mbox->numsectors, mbox->lba, mbox->xferaddr, mbox->logdrv,
+ mbox->numsge));
+
+ if (!scp) return;
+
+ con_log(level, (KERN_NOTICE "scsi cmnd: "));
+
+ for (i = 0; i < scp->cmd_len; i++) {
+ con_log(level, ("%#2.02x ", scp->cmnd[i]));
+ }
+
+ con_log(level, ("\n"));
+
+ return;
+}
+
+
+/**
+ * megaraid_mbox_setup_device_map - manage device ids
+ * @adapter : Driver's soft state
+ *
+ * Manange the device ids to have an appropraite mapping between the kernel
+ * scsi addresses and megaraid scsi and logical drive addresses. We export
+ * scsi devices on their actual addresses, whereas the logical drives are
+ * exported on a virtual scsi channel.
+ **/
+static void
+megaraid_mbox_setup_device_map(adapter_t *adapter)
+{
+ uint8_t c;
+ uint8_t t;
+
+ /*
+ * First fill the values on the logical drive channel
+ */
+ for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++)
+ adapter->device_ids[adapter->max_channel][t] =
+ (t < adapter->init_id) ? t : t - 1;
+
+ adapter->device_ids[adapter->max_channel][adapter->init_id] = 0xFF;
+
+ /*
+ * Fill the values on the physical devices channels
+ */
+ for (c = 0; c < adapter->max_channel; c++)
+ for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++)
+ adapter->device_ids[c][t] = (c << 8) | t;
+}
+
+
+/*
+ * END: internal commands library
+ */
+
+/*
+ * START: Interface for the common management module
+ *
+ * This is the module, which interfaces with the common mangement module to
+ * provide support for ioctl and sysfs
+ */
+
+/**
+ * megaraid_cmm_register - register with the mangement module
+ * @param adapter : HBA soft state
+ *
+ * Register with the management module, which allows applications to issue
+ * ioctl calls to the drivers. This interface is used by the management module
+ * to setup sysfs support as well.
+ */
+static int
+megaraid_cmm_register(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ mraid_mmadp_t adp;
+ scb_t *scb;
+ mbox_ccb_t *ccb;
+ int rval;
+ int i;
+
+ // Allocate memory for the base list of scb for management module.
+ adapter->uscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_USER_CMDS,
+ GFP_KERNEL);
+
+ if (adapter->uscb_list == NULL) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+ return -1;
+ }
+ memset(adapter->uscb_list, 0, sizeof(scb_t) * MBOX_MAX_USER_CMDS);
+
+
+ // Initialize the synchronization parameters for resources for
+ // commands for management module
+ INIT_LIST_HEAD(&adapter->uscb_pool);
+
+ spin_lock_init(USER_FREE_LIST_LOCK(adapter));
+
+
+
+ // link all the packets. Note, CCB for commands, coming from the
+ // commom management module, mailbox physical address are already
+ // setup by it. We just need placeholder for that in our local command
+ // control blocks
+ for (i = 0; i < MBOX_MAX_USER_CMDS; i++) {
+
+ scb = adapter->uscb_list + i;
+ ccb = raid_dev->uccb_list + i;
+
+ scb->ccb = (caddr_t)ccb;
+ ccb->mbox64 = raid_dev->umbox64 + i;
+ ccb->mbox = &ccb->mbox64->mbox32;
+ ccb->raw_mbox = (uint8_t *)ccb->mbox;
+
+ scb->gp = 0;
+
+ // COMMAND ID 0 - (MBOX_MAX_SCSI_CMDS-1) ARE RESERVED FOR
+ // COMMANDS COMING FROM IO SUBSYSTEM (MID-LAYER)
+ scb->sno = i + MBOX_MAX_SCSI_CMDS;
+
+ scb->scp = NULL;
+ scb->state = SCB_FREE;
+ scb->dma_direction = PCI_DMA_NONE;
+ scb->dma_type = MRAID_DMA_NONE;
+ scb->dev_channel = -1;
+ scb->dev_target = -1;
+
+ // put scb in the free pool
+ list_add_tail(&scb->list, &adapter->uscb_pool);
+ }
+
+ adp.unique_id = adapter->unique_id;
+ adp.drvr_type = DRVRTYPE_MBOX;
+ adp.drvr_data = (unsigned long)adapter;
+ adp.pdev = adapter->pdev;
+ adp.issue_uioc = megaraid_mbox_mm_handler;
+ adp.timeout = 300;
+ adp.max_kioc = MBOX_MAX_USER_CMDS;
+
+ if ((rval = mraid_mm_register_adp(&adp)) != 0) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: did not register with CMM\n"));
+
+ kfree(adapter->uscb_list);
+ }
+
+ return rval;
+}
+
+
+/**
+ * megaraid_cmm_unregister - un-register with the mangement module
+ * @param adapter : HBA soft state
+ *
+ * Un-register with the management module.
+ * FIXME: mgmt module must return failure for unregister if it has pending
+ * commands in LLD
+ */
+static int
+megaraid_cmm_unregister(adapter_t *adapter)
+{
+ kfree(adapter->uscb_list);
+ mraid_mm_unregister_adp(adapter->unique_id);
+ return 0;
+}
+
+
+/**
+ * megaraid_mbox_mm_handler - interface for CMM to issue commands to LLD
+ * @param drvr_data : LLD specific data
+ * @param kioc : CMM interface packet
+ * @param action : command action
+ *
+ * This routine is invoked whenever the Common Mangement Module (CMM) has a
+ * command for us. The 'action' parameter specifies if this is a new command
+ * or otherwise.
+ */
+static int
+megaraid_mbox_mm_handler(unsigned long drvr_data, uioc_t *kioc, uint32_t action)
+{
+ adapter_t *adapter;
+
+ if (action != IOCTL_ISSUE) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: unsupported management action:%#2x\n",
+ action));
+ return (-ENOTSUPP);
+ }
+
+ adapter = (adapter_t *)drvr_data;
+
+ // make sure this adapter is not being detached right now.
+ if (atomic_read(&adapter->being_detached)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: reject management request, detaching\n"));
+ return (-ENODEV);
+ }
+
+ switch (kioc->opcode) {
+
+ case GET_ADAP_INFO:
+
+ kioc->status = gather_hbainfo(adapter, (mraid_hba_info_t *)
+ (unsigned long)kioc->buf_vaddr);
+
+ kioc->done(kioc);
+
+ return kioc->status;
+
+ case MBOX_CMD:
+
+ return megaraid_mbox_mm_command(adapter, kioc);
+
+ default:
+ kioc->status = (-EINVAL);
+ kioc->done(kioc);
+ return (-EINVAL);
+ }
+
+ return 0; // not reached
+}
+
+/**
+ * megaraid_mbox_mm_command - issues commands routed through CMM
+ * @param adapter : HBA soft state
+ * @param kioc : management command packet
+ *
+ * Issues commands, which are routed through the management module.
+ */
+static int
+megaraid_mbox_mm_command(adapter_t *adapter, uioc_t *kioc)
+{
+ struct list_head *head = &adapter->uscb_pool;
+ mbox64_t *mbox64;
+ uint8_t *raw_mbox;
+ scb_t *scb;
+ mbox_ccb_t *ccb;
+ unsigned long flags;
+
+ // detach one scb from free pool
+ spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags);
+
+ if (list_empty(head)) { // should never happen because of CMM
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid mbox: bug in cmm handler, lost resources\n"));
+
+ spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
+
+ return (-EINVAL);
+ }
+
+ scb = list_entry(head->next, scb_t, list);
+ list_del_init(&scb->list);
+
+ spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
+
+ scb->state = SCB_ACTIVE;
+ scb->dma_type = MRAID_DMA_NONE;
+ scb->dma_direction = PCI_DMA_NONE;
+
+ ccb = (mbox_ccb_t *)scb->ccb;
+ mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
+ raw_mbox = (uint8_t *)&mbox64->mbox32;
+
+ memcpy(ccb->mbox64, mbox64, sizeof(mbox64_t));
+
+ scb->gp = (unsigned long)kioc;
+
+ /*
+ * If it is a logdrv random delete operation, we have to wait till
+ * there are no outstanding cmds at the fw and then issue it directly
+ */
+ if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) {
+
+ if (wait_till_fw_empty(adapter)) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mbox: LD delete, timed out\n"));
+
+ kioc->status = -ETIME;
+
+ scb->status = -1;
+
+ megaraid_mbox_mm_done(adapter, scb);
+
+ return (-ETIME);
+ }
+
+ INIT_LIST_HEAD(&scb->list);
+
+ scb->state = SCB_ISSUED;
+ if (mbox_post_cmd(adapter, scb) != 0) {
+
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid mbox: LD delete, mailbox busy\n"));
+
+ kioc->status = -EBUSY;
+
+ scb->status = -1;
+
+ megaraid_mbox_mm_done(adapter, scb);
+
+ return (-EBUSY);
+ }
+
+ return 0;
+ }
+
+ // put the command on the pending list and execute
+ megaraid_mbox_runpendq(adapter, scb);
+
+ return 0;
+}
+
+
+static int
+wait_till_fw_empty(adapter_t *adapter)
+{
+ unsigned long flags = 0;
+ int i;
+
+
+ /*
+ * Set the quiescent flag to stop issuing cmds to FW.
+ */
+ spin_lock_irqsave(adapter->host_lock, flags);
+ adapter->quiescent++;
+ spin_unlock_irqrestore(adapter->host_lock, flags);
+
+ /*
+ * Wait till there are no more cmds outstanding at FW. Try for at most
+ * 60 seconds
+ */
+ for (i = 0; i < 60 && adapter->outstanding_cmds; i++) {
+ con_log(CL_DLEVEL1, (KERN_INFO
+ "megaraid: FW has %d pending commands\n",
+ adapter->outstanding_cmds));
+
+ msleep(1000);
+ }
+
+ return adapter->outstanding_cmds;
+}
+
+
+/**
+ * megaraid_mbox_mm_done - callback for CMM commands
+ * @adapter : HBA soft state
+ * @scb : completed command
+ *
+ * Callback routine for internal commands originated from the management
+ * module.
+ */
+static void
+megaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb)
+{
+ uioc_t *kioc;
+ mbox64_t *mbox64;
+ uint8_t *raw_mbox;
+ unsigned long flags;
+
+ kioc = (uioc_t *)scb->gp;
+ kioc->status = 0;
+ mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
+ mbox64->mbox32.status = scb->status;
+ raw_mbox = (uint8_t *)&mbox64->mbox32;
+
+
+ // put scb in the free pool
+ scb->state = SCB_FREE;
+ scb->scp = NULL;
+
+ spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags);
+
+ list_add(&scb->list, &adapter->uscb_pool);
+
+ spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags);
+
+ // if a delete logical drive operation succeeded, restart the
+ // controller
+ if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) {
+
+ adapter->quiescent--;
+
+ megaraid_mbox_runpendq(adapter, NULL);
+ }
+
+ kioc->done(kioc);
+
+ return;
+}
+
+
+/**
+ * gather_hbainfo - HBA characteristics for the applications
+ * @param adapter : HBA soft state
+ * @param hinfo : pointer to the caller's host info strucuture
+ */
+static int
+gather_hbainfo(adapter_t *adapter, mraid_hba_info_t *hinfo)
+{
+ uint8_t dmajor;
+
+ dmajor = megaraid_mbox_version[0];
+
+ hinfo->pci_vendor_id = adapter->pdev->vendor;
+ hinfo->pci_device_id = adapter->pdev->device;
+ hinfo->subsys_vendor_id = adapter->pdev->subsystem_vendor;
+ hinfo->subsys_device_id = adapter->pdev->subsystem_device;
+
+ hinfo->pci_bus = adapter->pdev->bus->number;
+ hinfo->pci_dev_fn = adapter->pdev->devfn;
+ hinfo->pci_slot = PCI_SLOT(adapter->pdev->devfn);
+ hinfo->irq = adapter->host->irq;
+ hinfo->baseport = ADAP2RAIDDEV(adapter)->baseport;
+
+ hinfo->unique_id = (hinfo->pci_bus << 8) | adapter->pdev->devfn;
+ hinfo->host_no = adapter->host->host_no;
+
+ return 0;
+}
+
+/*
+ * END: Interface for the common management module
+ */
+
+
+
+/**
+ * megaraid_sysfs_alloc_resources - allocate sysfs related resources
+ *
+ * Allocate packets required to issue FW calls whenever the sysfs attributes
+ * are read. These attributes would require up-to-date information from the
+ * FW. Also set up resources for mutual exclusion to share these resources and
+ * the wait queue.
+ *
+ * @param adapter : controller's soft state
+ *
+ * @return 0 on success
+ * @return -ERROR_CODE on failure
+ */
+static int
+megaraid_sysfs_alloc_resources(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ int rval = 0;
+
+ raid_dev->sysfs_uioc = kmalloc(sizeof(uioc_t), GFP_KERNEL);
+
+ raid_dev->sysfs_mbox64 = kmalloc(sizeof(mbox64_t), GFP_KERNEL);
+
+ raid_dev->sysfs_buffer = pci_alloc_consistent(adapter->pdev,
+ PAGE_SIZE, &raid_dev->sysfs_buffer_dma);
+
+ if (!raid_dev->sysfs_uioc || !raid_dev->sysfs_mbox64 ||
+ !raid_dev->sysfs_buffer) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ rval = -ENOMEM;
+
+ megaraid_sysfs_free_resources(adapter);
+ }
+
+ sema_init(&raid_dev->sysfs_sem, 1);
+
+ init_waitqueue_head(&raid_dev->sysfs_wait_q);
+
+ return rval;
+}
+
+
+/**
+ * megaraid_sysfs_free_resources - free sysfs related resources
+ *
+ * Free packets allocated for sysfs FW commands
+ *
+ * @param adapter : controller's soft state
+ */
+static void
+megaraid_sysfs_free_resources(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ if (raid_dev->sysfs_uioc) kfree(raid_dev->sysfs_uioc);
+
+ if (raid_dev->sysfs_mbox64) kfree(raid_dev->sysfs_mbox64);
+
+ if (raid_dev->sysfs_buffer) {
+ pci_free_consistent(adapter->pdev, PAGE_SIZE,
+ raid_dev->sysfs_buffer, raid_dev->sysfs_buffer_dma);
+ }
+}
+
+
+/**
+ * megaraid_sysfs_get_ldmap_done - callback for get ldmap
+ *
+ * Callback routine called in the ISR/tasklet context for get ldmap call
+ *
+ * @param uioc : completed packet
+ */
+static void
+megaraid_sysfs_get_ldmap_done(uioc_t *uioc)
+{
+ adapter_t *adapter = (adapter_t *)uioc->buf_vaddr;
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ uioc->status = 0;
+
+ wake_up(&raid_dev->sysfs_wait_q);
+}
+
+
+/**
+ * megaraid_sysfs_get_ldmap_timeout - timeout handling for get ldmap
+ *
+ * Timeout routine to recover and return to application, in case the adapter
+ * has stopped responding. A timeout of 60 seconds for this command seem like
+ * a good value
+ *
+ * @param uioc : timed out packet
+ */
+static void
+megaraid_sysfs_get_ldmap_timeout(unsigned long data)
+{
+ uioc_t *uioc = (uioc_t *)data;
+ adapter_t *adapter = (adapter_t *)uioc->buf_vaddr;
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+
+ uioc->status = -ETIME;
+
+ wake_up(&raid_dev->sysfs_wait_q);
+}
+
+
+/**
+ * megaraid_sysfs_get_ldmap - get update logical drive map
+ *
+ * This routine will be called whenever user reads the logical drive
+ * attributes, go get the current logical drive mapping table from the
+ * firmware. We use the managment API's to issue commands to the controller.
+ *
+ * NOTE: The commands issuance functionality is not generalized and
+ * implemented in context of "get ld map" command only. If required, the
+ * command issuance logical can be trivially pulled out and implemented as a
+ * standalone libary. For now, this should suffice since there is no other
+ * user of this interface.
+ *
+ * @param adapter : controller's soft state
+ *
+ * @return 0 on success
+ * @return -1 on failure
+ */
+static int
+megaraid_sysfs_get_ldmap(adapter_t *adapter)
+{
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ uioc_t *uioc;
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ char *raw_mbox;
+ struct timer_list sysfs_timer;
+ struct timer_list *timerp;
+ caddr_t ldmap;
+ int rval = 0;
+
+ /*
+ * Allow only one read at a time to go through the sysfs attributes
+ */
+ down(&raid_dev->sysfs_sem);
+
+ uioc = raid_dev->sysfs_uioc;
+ mbox64 = raid_dev->sysfs_mbox64;
+ ldmap = raid_dev->sysfs_buffer;
+
+ memset(uioc, 0, sizeof(uioc_t));
+ memset(mbox64, 0, sizeof(mbox64_t));
+ memset(ldmap, 0, sizeof(raid_dev->curr_ldmap));
+
+ mbox = &mbox64->mbox32;
+ raw_mbox = (char *)mbox;
+ uioc->cmdbuf = (uint64_t)(unsigned long)mbox64;
+ uioc->buf_vaddr = (caddr_t)adapter;
+ uioc->status = -ENODATA;
+ uioc->done = megaraid_sysfs_get_ldmap_done;
+
+ /*
+ * Prepare the mailbox packet to get the current logical drive mapping
+ * table
+ */
+ mbox->xferaddr = (uint32_t)raid_dev->sysfs_buffer_dma;
+
+ raw_mbox[0] = FC_DEL_LOGDRV;
+ raw_mbox[2] = OP_GET_LDID_MAP;
+
+ /*
+ * Setup a timer to recover from a non-responding controller
+ */
+ timerp = &sysfs_timer;
+ init_timer(timerp);
+
+ timerp->function = megaraid_sysfs_get_ldmap_timeout;
+ timerp->data = (unsigned long)uioc;
+ timerp->expires = jiffies + 60 * HZ;
+
+ add_timer(timerp);
+
+ /*
+ * Send the command to the firmware
+ */
+ rval = megaraid_mbox_mm_command(adapter, uioc);
+
+ if (rval == 0) { // command successfully issued
+ wait_event(raid_dev->sysfs_wait_q, (uioc->status != -ENODATA));
+
+ /*
+ * Check if the command timed out
+ */
+ if (uioc->status == -ETIME) {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: sysfs get ld map timed out\n"));
+
+ rval = -ETIME;
+ }
+ else {
+ rval = mbox->status;
+ }
+
+ if (rval == 0) {
+ memcpy(raid_dev->curr_ldmap, ldmap,
+ sizeof(raid_dev->curr_ldmap));
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: get ld map failed with %x\n", rval));
+ }
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: could not issue ldmap command:%x\n", rval));
+ }
+
+
+ del_timer_sync(timerp);
+
+ up(&raid_dev->sysfs_sem);
+
+ return rval;
+}
+
+
+/**
+ * megaraid_sysfs_show_app_hndl - display application handle for this adapter
+ *
+ * Display the handle used by the applications while executing management
+ * tasks on the adapter. We invoke a management module API to get the adapter
+ * handle, since we do not interface with applications directly.
+ *
+ * @param cdev : class device object representation for the host
+ * @param buf : buffer to send data to
+ */
+static ssize_t
+megaraid_sysfs_show_app_hndl(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ adapter_t *adapter = (adapter_t *)SCSIHOST2ADAP(shost);
+ uint32_t app_hndl;
+
+ app_hndl = mraid_mm_adapter_app_handle(adapter->unique_id);
+
+ return snprintf(buf, 8, "%u\n", app_hndl);
+}
+
+
+/**
+ * megaraid_sysfs_show_ldnum - display the logical drive number for this device
+ *
+ * Display the logical drive number for the device in question, if it a valid
+ * logical drive. For physical devices, "-1" is returned
+ * The logical drive number is displayed in following format
+ *
+ * <SCSI ID> <LD NUM> <LD STICKY ID> <APP ADAPTER HANDLE>
+ * <int> <int> <int> <int>
+ *
+ * @param dev : device object representation for the scsi device
+ * @param buf : buffer to send data to
+ */
+static ssize_t
+megaraid_sysfs_show_ldnum(struct device *dev, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ adapter_t *adapter = (adapter_t *)SCSIHOST2ADAP(sdev->host);
+ mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter);
+ int scsi_id = -1;
+ int logical_drv = -1;
+ int ldid_map = -1;
+ uint32_t app_hndl = 0;
+ int mapped_sdev_id;
+ int rval;
+ int i;
+
+ if (raid_dev->random_del_supported &&
+ MRAID_IS_LOGICAL_SDEV(adapter, sdev)) {
+
+ rval = megaraid_sysfs_get_ldmap(adapter);
+ if (rval == 0) {
+
+ for (i = 0; i < MAX_LOGICAL_DRIVES_40LD; i++) {
+
+ mapped_sdev_id = sdev->id;
+
+ if (sdev->id > adapter->init_id) {
+ mapped_sdev_id -= 1;
+ }
+
+ if (raid_dev->curr_ldmap[i] == mapped_sdev_id) {
+
+ scsi_id = sdev->id;
+
+ logical_drv = i;
+
+ ldid_map = raid_dev->curr_ldmap[i];
+
+ app_hndl = mraid_mm_adapter_app_handle(
+ adapter->unique_id);
+
+ break;
+ }
+ }
+ }
+ else {
+ con_log(CL_ANN, (KERN_NOTICE
+ "megaraid: sysfs get ld map failed: %x\n",
+ rval));
+ }
+ }
+
+ return snprintf(buf, 36, "%d %d %d %d\n", scsi_id, logical_drv,
+ ldid_map, app_hndl);
+}
+
+
+/*
+ * END: Mailbox Low Level Driver
+ */
+module_init(megaraid_init);
+module_exit(megaraid_exit);
+
+/* vim: set ts=8 sw=8 tw=78 ai si: */
diff --git a/drivers/scsi/megaraid/megaraid_mbox.h b/drivers/scsi/megaraid/megaraid_mbox.h
new file mode 100644
index 000000000000..07510009d110
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mbox.h
@@ -0,0 +1,288 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mbox.h
+ */
+
+#ifndef _MEGARAID_H_
+#define _MEGARAID_H_
+
+
+#include "mega_common.h"
+#include "mbox_defs.h"
+#include "megaraid_ioctl.h"
+
+
+#define MEGARAID_VERSION "2.20.4.5"
+#define MEGARAID_EXT_VERSION "(Release Date: Thu Feb 03 12:27:22 EST 2005)"
+
+
+/*
+ * Define some PCI values here until they are put in the kernel
+ */
+#define PCI_DEVICE_ID_PERC4_DI_DISCOVERY 0x000E
+#define PCI_SUBSYS_ID_PERC4_DI_DISCOVERY 0x0123
+
+#define PCI_DEVICE_ID_PERC4_SC 0x1960
+#define PCI_SUBSYS_ID_PERC4_SC 0x0520
+
+#define PCI_DEVICE_ID_PERC4_DC 0x1960
+#define PCI_SUBSYS_ID_PERC4_DC 0x0518
+
+#define PCI_DEVICE_ID_PERC4_QC 0x0407
+#define PCI_SUBSYS_ID_PERC4_QC 0x0531
+
+#define PCI_DEVICE_ID_PERC4_DI_EVERGLADES 0x000F
+#define PCI_SUBSYS_ID_PERC4_DI_EVERGLADES 0x014A
+
+#define PCI_DEVICE_ID_PERC4E_SI_BIGBEND 0x0013
+#define PCI_SUBSYS_ID_PERC4E_SI_BIGBEND 0x016c
+
+#define PCI_DEVICE_ID_PERC4E_DI_KOBUK 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_KOBUK 0x016d
+
+#define PCI_DEVICE_ID_PERC4E_DI_CORVETTE 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_CORVETTE 0x016e
+
+#define PCI_DEVICE_ID_PERC4E_DI_EXPEDITION 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION 0x016f
+
+#define PCI_DEVICE_ID_PERC4E_DI_GUADALUPE 0x0013
+#define PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE 0x0170
+
+#define PCI_DEVICE_ID_PERC4E_DC_320_2E 0x0408
+#define PCI_SUBSYS_ID_PERC4E_DC_320_2E 0x0002
+
+#define PCI_DEVICE_ID_PERC4E_SC_320_1E 0x0408
+#define PCI_SUBSYS_ID_PERC4E_SC_320_1E 0x0001
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0 0xA520
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1 0x0520
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2 0x0518
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0x 0x0407
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x 0x0530
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2x 0x0407
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x 0x0532
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_4x 0x0407
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x 0x0531
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E 0x0001
+
+#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E 0x0002
+
+#define PCI_DEVICE_ID_MEGARAID_I4_133_RAID 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_I4_133_RAID 0x0522
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_150_4 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SATA_150_4 0x4523
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_150_6 0x1960
+#define PCI_SUBSYS_ID_MEGARAID_SATA_150_6 0x0523
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_300_4x 0x0409
+#define PCI_SUBSYS_ID_MEGARAID_SATA_300_4x 0x3004
+
+#define PCI_DEVICE_ID_MEGARAID_SATA_300_8x 0x0409
+#define PCI_SUBSYS_ID_MEGARAID_SATA_300_8x 0x3008
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCU42X 0x0407
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42X 0x0532
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCS16 0x1960
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCS16 0x0523
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCU42E 0x0408
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42E 0x0002
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCZCRX 0x0407
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX 0x0530
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCS28X 0x0409
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCS28X 0x3008
+
+#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF 0x0408
+#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF 0x3431
+
+#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH 0x0408
+#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH 0x3499
+
+#define PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x1960
+#define PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x0520
+
+#define PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x0408
+#define PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x1065
+
+#define PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E 0x004D
+
+#define PCI_SUBSYS_ID_PERC3_QC 0x0471
+#define PCI_SUBSYS_ID_PERC3_DC 0x0493
+#define PCI_SUBSYS_ID_PERC3_SC 0x0475
+
+#define PCI_DEVICE_ID_MEGARAID_NEC_ROMB_2E 0x0408
+#define PCI_SUBSYS_ID_MEGARAID_NEC_ROMB_2E 0x8287
+
+#ifndef PCI_SUBSYS_ID_FSC
+#define PCI_SUBSYS_ID_FSC 0x1734
+#endif
+
+#define MBOX_MAX_SCSI_CMDS 128 // number of cmds reserved for kernel
+#define MBOX_MAX_USER_CMDS 32 // number of cmds for applications
+#define MBOX_DEF_CMD_PER_LUN 64 // default commands per lun
+#define MBOX_DEFAULT_SG_SIZE 26 // default sg size supported by all fw
+#define MBOX_MAX_SG_SIZE 32 // maximum scatter-gather list size
+#define MBOX_MAX_SECTORS 128 // maximum sectors per IO
+#define MBOX_TIMEOUT 30 // timeout value for internal cmds
+#define MBOX_BUSY_WAIT 10 // max usec to wait for busy mailbox
+#define MBOX_RESET_WAIT 180 // wait these many seconds in reset
+#define MBOX_RESET_EXT_WAIT 120 // extended wait reset
+
+/*
+ * maximum transfer that can happen through the firmware commands issued
+ * internnaly from the driver.
+ */
+#define MBOX_IBUF_SIZE 4096
+
+
+/**
+ * mbox_ccb_t - command control block specific to mailbox based controllers
+ * @raw_mbox : raw mailbox pointer
+ * @mbox : mailbox
+ * @mbox64 : extended mailbox
+ * @mbox_dma_h : maibox dma address
+ * @sgl64 : 64-bit scatter-gather list
+ * @sgl32 : 32-bit scatter-gather list
+ * @sgl_dma_h : dma handle for the scatter-gather list
+ * @pthru : passthru structure
+ * @pthru_dma_h : dma handle for the passthru structure
+ * @epthru : extended passthru structure
+ * @epthru_dma_h : dma handle for extended passthru structure
+ * @buf_dma_h : dma handle for buffers w/o sg list
+ *
+ * command control block specific to the mailbox based controllers
+ */
+typedef struct {
+ uint8_t *raw_mbox;
+ mbox_t *mbox;
+ mbox64_t *mbox64;
+ dma_addr_t mbox_dma_h;
+ mbox_sgl64 *sgl64;
+ mbox_sgl32 *sgl32;
+ dma_addr_t sgl_dma_h;
+ mraid_passthru_t *pthru;
+ dma_addr_t pthru_dma_h;
+ mraid_epassthru_t *epthru;
+ dma_addr_t epthru_dma_h;
+ dma_addr_t buf_dma_h;
+} mbox_ccb_t;
+
+
+/**
+ * mraid_device_t - adapter soft state structure for mailbox controllers
+ * @param una_mbox64 : 64-bit mbox - unaligned
+ * @param una_mbox64_dma : mbox dma addr - unaligned
+ * @param mbox : 32-bit mbox - aligned
+ * @param mbox64 : 64-bit mbox - aligned
+ * @param mbox_dma : mbox dma addr - aligned
+ * @param mailbox_lock : exclusion lock for the mailbox
+ * @param baseport : base port of hba memory
+ * @param baseaddr : mapped addr of hba memory
+ * @param mbox_pool : pool of mailboxes
+ * @param mbox_pool_handle : handle for the mailbox pool memory
+ * @param epthru_pool : a pool for extended passthru commands
+ * @param epthru_pool_handle : handle to the pool above
+ * @param sg_pool : pool of scatter-gather lists for this driver
+ * @param sg_pool_handle : handle to the pool above
+ * @param ccb_list : list of our command control blocks
+ * @param uccb_list : list of cmd control blocks for mgmt module
+ * @param umbox64 : array of mailbox for user commands (cmm)
+ * @param pdrv_state : array for state of each physical drive.
+ * @param last_disp : flag used to show device scanning
+ * @param hw_error : set if FW not responding
+ * @param fast_load : If set, skip physical device scanning
+ * @channel_class : channel class, RAID or SCSI
+ * @sysfs_sem : semaphore to serialize access to sysfs res.
+ * @sysfs_uioc : management packet to issue FW calls from sysfs
+ * @sysfs_mbox64 : mailbox packet to issue FW calls from sysfs
+ * @sysfs_buffer : data buffer for FW commands issued from sysfs
+ * @sysfs_buffer_dma : DMA buffer for FW commands issued from sysfs
+ * @sysfs_wait_q : wait queue for sysfs operations
+ * @random_del_supported : set if the random deletion is supported
+ * @curr_ldmap : current LDID map
+ *
+ * Initialization structure for mailbox controllers: memory based and IO based
+ * All the fields in this structure are LLD specific and may be discovered at
+ * init() or start() time.
+ *
+ * NOTE: The fields of this structures are placed to minimize cache misses
+ */
+#define MAX_LD_EXTENDED64 64
+typedef struct {
+ mbox64_t *una_mbox64;
+ dma_addr_t una_mbox64_dma;
+ mbox_t *mbox;
+ mbox64_t *mbox64;
+ dma_addr_t mbox_dma;
+ spinlock_t mailbox_lock;
+ unsigned long baseport;
+ void __iomem * baseaddr;
+ struct mraid_pci_blk mbox_pool[MBOX_MAX_SCSI_CMDS];
+ struct dma_pool *mbox_pool_handle;
+ struct mraid_pci_blk epthru_pool[MBOX_MAX_SCSI_CMDS];
+ struct dma_pool *epthru_pool_handle;
+ struct mraid_pci_blk sg_pool[MBOX_MAX_SCSI_CMDS];
+ struct dma_pool *sg_pool_handle;
+ mbox_ccb_t ccb_list[MBOX_MAX_SCSI_CMDS];
+ mbox_ccb_t uccb_list[MBOX_MAX_USER_CMDS];
+ mbox64_t umbox64[MBOX_MAX_USER_CMDS];
+
+ uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES];
+ uint32_t last_disp;
+ int hw_error;
+ int fast_load;
+ uint8_t channel_class;
+ struct semaphore sysfs_sem;
+ uioc_t *sysfs_uioc;
+ mbox64_t *sysfs_mbox64;
+ caddr_t sysfs_buffer;
+ dma_addr_t sysfs_buffer_dma;
+ wait_queue_head_t sysfs_wait_q;
+ int random_del_supported;
+ uint16_t curr_ldmap[MAX_LD_EXTENDED64];
+} mraid_device_t;
+
+// route to raid device from adapter
+#define ADAP2RAIDDEV(adp) ((mraid_device_t *)((adp)->raid_device))
+
+#define MAILBOX_LOCK(rdev) (&(rdev)->mailbox_lock)
+
+// Find out if this channel is a RAID or SCSI
+#define IS_RAID_CH(rdev, ch) (((rdev)->channel_class >> (ch)) & 0x01)
+
+
+#define RDINDOOR(rdev) readl((rdev)->baseaddr + 0x20)
+#define RDOUTDOOR(rdev) readl((rdev)->baseaddr + 0x2C)
+#define WRINDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x20)
+#define WROUTDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x2C)
+
+#endif // _MEGARAID_H_
+
+// vim: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c
new file mode 100644
index 000000000000..9f1b550713ec
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mm.c
@@ -0,0 +1,1255 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mm.c
+ * Version : v2.20.2.5 (Jan 21 2005)
+ *
+ * Common management module
+ */
+
+#include "megaraid_mm.h"
+#include <linux/smp_lock.h>
+
+
+// Entry points for char node driver
+static int mraid_mm_open(struct inode *, struct file *);
+static int mraid_mm_ioctl(struct inode *, struct file *, uint, unsigned long);
+
+
+// routines to convert to and from the old the format
+static int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *);
+static int kioc_to_mimd(uioc_t *, mimd_t __user *);
+
+
+// Helper functions
+static int handle_drvrcmd(void __user *, uint8_t, int *);
+static int lld_ioctl(mraid_mmadp_t *, uioc_t *);
+static void ioctl_done(uioc_t *);
+static void lld_timedout(unsigned long);
+static void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *);
+static mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *);
+static uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *);
+static void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *);
+static int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int);
+static int mraid_mm_setup_dma_pools(mraid_mmadp_t *);
+static void mraid_mm_free_adp_resources(mraid_mmadp_t *);
+static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *);
+
+#ifdef CONFIG_COMPAT
+static long mraid_mm_compat_ioctl(struct file *, unsigned int, unsigned long);
+#endif
+
+MODULE_AUTHOR("LSI Logic Corporation");
+MODULE_DESCRIPTION("LSI Logic Management Module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LSI_COMMON_MOD_VERSION);
+
+static int dbglevel = CL_ANN;
+module_param_named(dlevel, dbglevel, int, 0);
+MODULE_PARM_DESC(dlevel, "Debug level (default=0)");
+
+EXPORT_SYMBOL(mraid_mm_register_adp);
+EXPORT_SYMBOL(mraid_mm_unregister_adp);
+EXPORT_SYMBOL(mraid_mm_adapter_app_handle);
+
+static int majorno;
+static uint32_t drvr_ver = 0x02200201;
+
+static int adapters_count_g;
+static struct list_head adapters_list_g;
+
+static wait_queue_head_t wait_q;
+
+static struct file_operations lsi_fops = {
+ .open = mraid_mm_open,
+ .ioctl = mraid_mm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = mraid_mm_compat_ioctl,
+#endif
+ .owner = THIS_MODULE,
+};
+
+/**
+ * mraid_mm_open - open routine for char node interface
+ * @inod : unused
+ * @filep : unused
+ *
+ * allow ioctl operations by apps only if they superuser privilege
+ */
+static int
+mraid_mm_open(struct inode *inode, struct file *filep)
+{
+ /*
+ * Only allow superuser to access private ioctl interface
+ */
+ if (!capable(CAP_SYS_ADMIN)) return (-EACCES);
+
+ return 0;
+}
+
+/**
+ * mraid_mm_ioctl - module entry-point for ioctls
+ * @inode : inode (ignored)
+ * @filep : file operations pointer (ignored)
+ * @cmd : ioctl command
+ * @arg : user ioctl packet
+ */
+static int
+mraid_mm_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ uioc_t *kioc;
+ char signature[EXT_IOCTL_SIGN_SZ] = {0};
+ int rval;
+ mraid_mmadp_t *adp;
+ uint8_t old_ioctl;
+ int drvrcmd_rval;
+ void __user *argp = (void __user *)arg;
+
+ /*
+ * Make sure only USCSICMD are issued through this interface.
+ * MIMD application would still fire different command.
+ */
+
+ if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) {
+ return (-EINVAL);
+ }
+
+ /*
+ * Look for signature to see if this is the new or old ioctl format.
+ */
+ if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: copy from usr addr failed\n"));
+ return (-EFAULT);
+ }
+
+ if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0)
+ old_ioctl = 0;
+ else
+ old_ioctl = 1;
+
+ /*
+ * At present, we don't support the new ioctl packet
+ */
+ if (!old_ioctl )
+ return (-EINVAL);
+
+ /*
+ * If it is a driver ioctl (as opposed to fw ioctls), then we can
+ * handle the command locally. rval > 0 means it is not a drvr cmd
+ */
+ rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval);
+
+ if (rval < 0)
+ return rval;
+ else if (rval == 0)
+ return drvrcmd_rval;
+
+ rval = 0;
+ if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) {
+ return rval;
+ }
+
+ /*
+ * Check if adapter can accept ioctl. We may have marked it offline
+ * if any previous kioc had timedout on this controller.
+ */
+ if (!adp->quiescent) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: controller cannot accept cmds due to "
+ "earlier errors\n" ));
+ return -EFAULT;
+ }
+
+ /*
+ * The following call will block till a kioc is available
+ */
+ kioc = mraid_mm_alloc_kioc(adp);
+
+ /*
+ * User sent the old mimd_t ioctl packet. Convert it to uioc_t.
+ */
+ if ((rval = mimd_to_kioc(argp, adp, kioc))) {
+ mraid_mm_dealloc_kioc(adp, kioc);
+ return rval;
+ }
+
+ kioc->done = ioctl_done;
+
+ /*
+ * Issue the IOCTL to the low level driver. After the IOCTL completes
+ * release the kioc if and only if it was _not_ timedout. If it was
+ * timedout, that means that resources are still with low level driver.
+ */
+ if ((rval = lld_ioctl(adp, kioc))) {
+
+ if (!kioc->timedout)
+ mraid_mm_dealloc_kioc(adp, kioc);
+
+ return rval;
+ }
+
+ /*
+ * Convert the kioc back to user space
+ */
+ rval = kioc_to_mimd(kioc, argp);
+
+ /*
+ * Return the kioc to free pool
+ */
+ mraid_mm_dealloc_kioc(adp, kioc);
+
+ return rval;
+}
+
+
+/**
+ * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet
+ * @umimd : User space mimd_t ioctl packet
+ * @adapter : pointer to the adapter (OUT)
+ */
+static mraid_mmadp_t *
+mraid_mm_get_adapter(mimd_t __user *umimd, int *rval)
+{
+ mraid_mmadp_t *adapter;
+ mimd_t mimd;
+ uint32_t adapno;
+ int iterator;
+
+
+ if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) {
+ *rval = -EFAULT;
+ return NULL;
+ }
+
+ adapno = GETADAP(mimd.ui.fcs.adapno);
+
+ if (adapno >= adapters_count_g) {
+ *rval = -ENODEV;
+ return NULL;
+ }
+
+ adapter = NULL;
+ iterator = 0;
+
+ list_for_each_entry(adapter, &adapters_list_g, list) {
+ if (iterator++ == adapno) break;
+ }
+
+ if (!adapter) {
+ *rval = -ENODEV;
+ return NULL;
+ }
+
+ return adapter;
+}
+
+/*
+ * handle_drvrcmd - This routine checks if the opcode is a driver
+ * cmd and if it is, handles it.
+ * @arg : packet sent by the user app
+ * @old_ioctl : mimd if 1; uioc otherwise
+ */
+static int
+handle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval)
+{
+ mimd_t __user *umimd;
+ mimd_t kmimd;
+ uint8_t opcode;
+ uint8_t subopcode;
+
+ if (old_ioctl)
+ goto old_packet;
+ else
+ goto new_packet;
+
+new_packet:
+ return (-ENOTSUPP);
+
+old_packet:
+ *rval = 0;
+ umimd = arg;
+
+ if (copy_from_user(&kmimd, umimd, sizeof(mimd_t)))
+ return (-EFAULT);
+
+ opcode = kmimd.ui.fcs.opcode;
+ subopcode = kmimd.ui.fcs.subopcode;
+
+ /*
+ * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or
+ * GET_NUMADP, then we can handle. Otherwise we should return 1 to
+ * indicate that we cannot handle this.
+ */
+ if (opcode != 0x82)
+ return 1;
+
+ switch (subopcode) {
+
+ case MEGAIOC_QDRVRVER:
+
+ if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t)))
+ return (-EFAULT);
+
+ return 0;
+
+ case MEGAIOC_QNADAP:
+
+ *rval = adapters_count_g;
+
+ if (copy_to_user(kmimd.data, &adapters_count_g,
+ sizeof(uint32_t)))
+ return (-EFAULT);
+
+ return 0;
+
+ default:
+ /* cannot handle */
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * mimd_to_kioc - Converter from old to new ioctl format
+ *
+ * @umimd : user space old MIMD IOCTL
+ * @kioc : kernel space new format IOCTL
+ *
+ * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The
+ * new packet is in kernel space so that driver can perform operations on it
+ * freely.
+ */
+
+static int
+mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc)
+{
+ mbox64_t *mbox64;
+ mbox_t *mbox;
+ mraid_passthru_t *pthru32;
+ uint32_t adapno;
+ uint8_t opcode;
+ uint8_t subopcode;
+ mimd_t mimd;
+
+ if (copy_from_user(&mimd, umimd, sizeof(mimd_t)))
+ return (-EFAULT);
+
+ /*
+ * Applications are not allowed to send extd pthru
+ */
+ if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) ||
+ (mimd.mbox[0] == MBOXCMD_EXTPTHRU))
+ return (-EINVAL);
+
+ opcode = mimd.ui.fcs.opcode;
+ subopcode = mimd.ui.fcs.subopcode;
+ adapno = GETADAP(mimd.ui.fcs.adapno);
+
+ if (adapno >= adapters_count_g)
+ return (-ENODEV);
+
+ kioc->adapno = adapno;
+ kioc->mb_type = MBOX_LEGACY;
+ kioc->app_type = APPTYPE_MIMD;
+
+ switch (opcode) {
+
+ case 0x82:
+
+ if (subopcode == MEGAIOC_QADAPINFO) {
+
+ kioc->opcode = GET_ADAP_INFO;
+ kioc->data_dir = UIOC_RD;
+ kioc->xferlen = sizeof(mraid_hba_info_t);
+
+ if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
+ return (-ENOMEM);
+ }
+ else {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: Invalid subop\n"));
+ return (-EINVAL);
+ }
+
+ break;
+
+ case 0x81:
+
+ kioc->opcode = MBOX_CMD;
+ kioc->xferlen = mimd.ui.fcs.length;
+ kioc->user_data_len = kioc->xferlen;
+ kioc->user_data = mimd.ui.fcs.buffer;
+
+ if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
+ return (-ENOMEM);
+
+ if (mimd.outlen) kioc->data_dir = UIOC_RD;
+ if (mimd.inlen) kioc->data_dir |= UIOC_WR;
+
+ break;
+
+ case 0x80:
+
+ kioc->opcode = MBOX_CMD;
+ kioc->xferlen = (mimd.outlen > mimd.inlen) ?
+ mimd.outlen : mimd.inlen;
+ kioc->user_data_len = kioc->xferlen;
+ kioc->user_data = mimd.data;
+
+ if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen))
+ return (-ENOMEM);
+
+ if (mimd.outlen) kioc->data_dir = UIOC_RD;
+ if (mimd.inlen) kioc->data_dir |= UIOC_WR;
+
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+
+ /*
+ * If driver command, nothing else to do
+ */
+ if (opcode == 0x82)
+ return 0;
+
+ /*
+ * This is a mailbox cmd; copy the mailbox from mimd
+ */
+ mbox64 = (mbox64_t *)((unsigned long)kioc->cmdbuf);
+ mbox = &mbox64->mbox32;
+ memcpy(mbox, mimd.mbox, 14);
+
+ if (mbox->cmd != MBOXCMD_PASSTHRU) { // regular DCMD
+
+ mbox->xferaddr = (uint32_t)kioc->buf_paddr;
+
+ if (kioc->data_dir & UIOC_WR) {
+ if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
+ kioc->xferlen)) {
+ return (-EFAULT);
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * This is a regular 32-bit pthru cmd; mbox points to pthru struct.
+ * Just like in above case, the beginning for memblk is treated as
+ * a mailbox. The passthru will begin at next 1K boundary. And the
+ * data will start 1K after that.
+ */
+ pthru32 = kioc->pthru32;
+ kioc->user_pthru = &umimd->pthru;
+ mbox->xferaddr = (uint32_t)kioc->pthru32_h;
+
+ if (copy_from_user(pthru32, kioc->user_pthru,
+ sizeof(mraid_passthru_t))) {
+ return (-EFAULT);
+ }
+
+ pthru32->dataxferaddr = kioc->buf_paddr;
+ if (kioc->data_dir & UIOC_WR) {
+ if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
+ pthru32->dataxferlen)) {
+ return (-EFAULT);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * mraid_mm_attch_buf - Attach a free dma buffer for required size
+ *
+ * @adp : Adapter softstate
+ * @kioc : kioc that the buffer needs to be attached to
+ * @xferlen : required length for buffer
+ *
+ * First we search for a pool with smallest buffer that is >= @xferlen. If
+ * that pool has no free buffer, we will try for the next bigger size. If none
+ * is available, we will try to allocate the smallest buffer that is >=
+ * @xferlen and attach it the pool.
+ */
+static int
+mraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen)
+{
+ mm_dmapool_t *pool;
+ int right_pool = -1;
+ unsigned long flags;
+ int i;
+
+ kioc->pool_index = -1;
+ kioc->buf_vaddr = NULL;
+ kioc->buf_paddr = 0;
+ kioc->free_buf = 0;
+
+ /*
+ * We need xferlen amount of memory. See if we can get it from our
+ * dma pools. If we don't get exact size, we will try bigger buffer
+ */
+
+ for (i = 0; i < MAX_DMA_POOLS; i++) {
+
+ pool = &adp->dma_pool_list[i];
+
+ if (xferlen > pool->buf_size)
+ continue;
+
+ if (right_pool == -1)
+ right_pool = i;
+
+ spin_lock_irqsave(&pool->lock, flags);
+
+ if (!pool->in_use) {
+
+ pool->in_use = 1;
+ kioc->pool_index = i;
+ kioc->buf_vaddr = pool->vaddr;
+ kioc->buf_paddr = pool->paddr;
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+ return 0;
+ }
+ else {
+ spin_unlock_irqrestore(&pool->lock, flags);
+ continue;
+ }
+ }
+
+ /*
+ * If xferlen doesn't match any of our pools, return error
+ */
+ if (right_pool == -1)
+ return -EINVAL;
+
+ /*
+ * We did not get any buffer from the preallocated pool. Let us try
+ * to allocate one new buffer. NOTE: This is a blocking call.
+ */
+ pool = &adp->dma_pool_list[right_pool];
+
+ spin_lock_irqsave(&pool->lock, flags);
+
+ kioc->pool_index = right_pool;
+ kioc->free_buf = 1;
+ kioc->buf_vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
+ &kioc->buf_paddr);
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ if (!kioc->buf_vaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * mraid_mm_alloc_kioc - Returns a uioc_t from free list
+ * @adp : Adapter softstate for this module
+ *
+ * The kioc_semaphore is initialized with number of kioc nodes in the
+ * free kioc pool. If the kioc pool is empty, this function blocks till
+ * a kioc becomes free.
+ */
+static uioc_t *
+mraid_mm_alloc_kioc(mraid_mmadp_t *adp)
+{
+ uioc_t *kioc;
+ struct list_head* head;
+ unsigned long flags;
+
+ down(&adp->kioc_semaphore);
+
+ spin_lock_irqsave(&adp->kioc_pool_lock, flags);
+
+ head = &adp->kioc_pool;
+
+ if (list_empty(head)) {
+ up(&adp->kioc_semaphore);
+ spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
+
+ con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n"));
+ return NULL;
+ }
+
+ kioc = list_entry(head->next, uioc_t, list);
+ list_del_init(&kioc->list);
+
+ spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
+
+ memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t));
+ memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t));
+
+ kioc->buf_vaddr = NULL;
+ kioc->buf_paddr = 0;
+ kioc->pool_index =-1;
+ kioc->free_buf = 0;
+ kioc->user_data = NULL;
+ kioc->user_data_len = 0;
+ kioc->user_pthru = NULL;
+ kioc->timedout = 0;
+
+ return kioc;
+}
+
+/**
+ * mraid_mm_dealloc_kioc - Return kioc to free pool
+ *
+ * @adp : Adapter softstate
+ * @kioc : uioc_t node to be returned to free pool
+ */
+static void
+mraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc)
+{
+ mm_dmapool_t *pool;
+ unsigned long flags;
+
+ if (kioc->pool_index != -1) {
+ pool = &adp->dma_pool_list[kioc->pool_index];
+
+ /* This routine may be called in non-isr context also */
+ spin_lock_irqsave(&pool->lock, flags);
+
+ /*
+ * While attaching the dma buffer, if we didn't get the
+ * required buffer from the pool, we would have allocated
+ * it at the run time and set the free_buf flag. We must
+ * free that buffer. Otherwise, just mark that the buffer is
+ * not in use
+ */
+ if (kioc->free_buf == 1)
+ pci_pool_free(pool->handle, kioc->buf_vaddr,
+ kioc->buf_paddr);
+ else
+ pool->in_use = 0;
+
+ spin_unlock_irqrestore(&pool->lock, flags);
+ }
+
+ /* Return the kioc to the free pool */
+ spin_lock_irqsave(&adp->kioc_pool_lock, flags);
+ list_add(&kioc->list, &adp->kioc_pool);
+ spin_unlock_irqrestore(&adp->kioc_pool_lock, flags);
+
+ /* increment the free kioc count */
+ up(&adp->kioc_semaphore);
+
+ return;
+}
+
+/**
+ * lld_ioctl - Routine to issue ioctl to low level drvr
+ *
+ * @adp : The adapter handle
+ * @kioc : The ioctl packet with kernel addresses
+ */
+static int
+lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc)
+{
+ int rval;
+ struct timer_list timer;
+ struct timer_list *tp = NULL;
+
+ kioc->status = -ENODATA;
+ rval = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE);
+
+ if (rval) return rval;
+
+ /*
+ * Start the timer
+ */
+ if (adp->timeout > 0) {
+ tp = &timer;
+ init_timer(tp);
+
+ tp->function = lld_timedout;
+ tp->data = (unsigned long)kioc;
+ tp->expires = jiffies + adp->timeout * HZ;
+
+ add_timer(tp);
+ }
+
+ /*
+ * Wait till the low level driver completes the ioctl. After this
+ * call, the ioctl either completed successfully or timedout.
+ */
+ wait_event(wait_q, (kioc->status != -ENODATA));
+ if (tp) {
+ del_timer_sync(tp);
+ }
+
+ /*
+ * If the command had timedout, we mark the controller offline
+ * before returning
+ */
+ if (kioc->timedout) {
+ adp->quiescent = 0;
+ }
+
+ return kioc->status;
+}
+
+
+/**
+ * ioctl_done - callback from the low level driver
+ *
+ * @kioc : completed ioctl packet
+ */
+static void
+ioctl_done(uioc_t *kioc)
+{
+ uint32_t adapno;
+ int iterator;
+ mraid_mmadp_t* adapter;
+
+ /*
+ * When the kioc returns from driver, make sure it still doesn't
+ * have ENODATA in status. Otherwise, driver will hang on wait_event
+ * forever
+ */
+ if (kioc->status == -ENODATA) {
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: lld didn't change status!\n"));
+
+ kioc->status = -EINVAL;
+ }
+
+ /*
+ * Check if this kioc was timedout before. If so, nobody is waiting
+ * on this kioc. We don't have to wake up anybody. Instead, we just
+ * have to free the kioc
+ */
+ if (kioc->timedout) {
+ iterator = 0;
+ adapter = NULL;
+ adapno = kioc->adapno;
+
+ con_log(CL_ANN, ( KERN_WARNING "megaraid cmm: completed "
+ "ioctl that was timedout before\n"));
+
+ list_for_each_entry(adapter, &adapters_list_g, list) {
+ if (iterator++ == adapno) break;
+ }
+
+ kioc->timedout = 0;
+
+ if (adapter) {
+ mraid_mm_dealloc_kioc( adapter, kioc );
+ }
+ }
+ else {
+ wake_up(&wait_q);
+ }
+}
+
+
+/*
+ * lld_timedout : callback from the expired timer
+ *
+ * @ptr : ioctl packet that timed out
+ */
+static void
+lld_timedout(unsigned long ptr)
+{
+ uioc_t *kioc = (uioc_t *)ptr;
+
+ kioc->status = -ETIME;
+ kioc->timedout = 1;
+
+ con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n"));
+
+ wake_up(&wait_q);
+}
+
+
+/**
+ * kioc_to_mimd : Converter from new back to old format
+ *
+ * @kioc : Kernel space IOCTL packet (successfully issued)
+ * @mimd : User space MIMD packet
+ */
+static int
+kioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd)
+{
+ mimd_t kmimd;
+ uint8_t opcode;
+ uint8_t subopcode;
+
+ mbox64_t *mbox64;
+ mraid_passthru_t __user *upthru32;
+ mraid_passthru_t *kpthru32;
+ mcontroller_t cinfo;
+ mraid_hba_info_t *hinfo;
+
+
+ if (copy_from_user(&kmimd, mimd, sizeof(mimd_t)))
+ return (-EFAULT);
+
+ opcode = kmimd.ui.fcs.opcode;
+ subopcode = kmimd.ui.fcs.subopcode;
+
+ if (opcode == 0x82) {
+ switch (subopcode) {
+
+ case MEGAIOC_QADAPINFO:
+
+ hinfo = (mraid_hba_info_t *)(unsigned long)
+ kioc->buf_vaddr;
+
+ hinfo_to_cinfo(hinfo, &cinfo);
+
+ if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo)))
+ return (-EFAULT);
+
+ return 0;
+
+ default:
+ return (-EINVAL);
+ }
+
+ return 0;
+ }
+
+ mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf;
+
+ if (kioc->user_pthru) {
+
+ upthru32 = kioc->user_pthru;
+ kpthru32 = kioc->pthru32;
+
+ if (copy_to_user(&upthru32->scsistatus,
+ &kpthru32->scsistatus,
+ sizeof(uint8_t))) {
+ return (-EFAULT);
+ }
+ }
+
+ if (kioc->user_data) {
+ if (copy_to_user(kioc->user_data, kioc->buf_vaddr,
+ kioc->user_data_len)) {
+ return (-EFAULT);
+ }
+ }
+
+ if (copy_to_user(&mimd->mbox[17],
+ &mbox64->mbox32.status, sizeof(uint8_t))) {
+ return (-EFAULT);
+ }
+
+ return 0;
+}
+
+
+/**
+ * hinfo_to_cinfo - Convert new format hba info into old format
+ *
+ * @hinfo : New format, more comprehensive adapter info
+ * @cinfo : Old format adapter info to support mimd_t apps
+ */
+static void
+hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo)
+{
+ if (!hinfo || !cinfo)
+ return;
+
+ cinfo->base = hinfo->baseport;
+ cinfo->irq = hinfo->irq;
+ cinfo->numldrv = hinfo->num_ldrv;
+ cinfo->pcibus = hinfo->pci_bus;
+ cinfo->pcidev = hinfo->pci_slot;
+ cinfo->pcifun = PCI_FUNC(hinfo->pci_dev_fn);
+ cinfo->pciid = hinfo->pci_device_id;
+ cinfo->pcivendor = hinfo->pci_vendor_id;
+ cinfo->pcislot = hinfo->pci_slot;
+ cinfo->uid = hinfo->unique_id;
+}
+
+
+/*
+ * mraid_mm_register_adp - Registration routine for low level drvrs
+ *
+ * @adp : Adapter objejct
+ */
+int
+mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
+{
+ mraid_mmadp_t *adapter;
+ mbox64_t *mbox_list;
+ uioc_t *kioc;
+ uint32_t rval;
+ int i;
+
+
+ if (lld_adp->drvr_type != DRVRTYPE_MBOX)
+ return (-EINVAL);
+
+ adapter = kmalloc(sizeof(mraid_mmadp_t), GFP_KERNEL);
+
+ if (!adapter) {
+ rval = -ENOMEM;
+ goto memalloc_error;
+ }
+
+ memset(adapter, 0, sizeof(mraid_mmadp_t));
+
+ adapter->unique_id = lld_adp->unique_id;
+ adapter->drvr_type = lld_adp->drvr_type;
+ adapter->drvr_data = lld_adp->drvr_data;
+ adapter->pdev = lld_adp->pdev;
+ adapter->issue_uioc = lld_adp->issue_uioc;
+ adapter->timeout = lld_adp->timeout;
+ adapter->max_kioc = lld_adp->max_kioc;
+ adapter->quiescent = 1;
+
+ /*
+ * Allocate single blocks of memory for all required kiocs,
+ * mailboxes and passthru structures.
+ */
+ adapter->kioc_list = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc,
+ GFP_KERNEL);
+ adapter->mbox_list = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc,
+ GFP_KERNEL);
+ adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool",
+ adapter->pdev,
+ sizeof(mraid_passthru_t),
+ 16, 0);
+
+ if (!adapter->kioc_list || !adapter->mbox_list ||
+ !adapter->pthru_dma_pool) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: out of memory, %s %d\n", __FUNCTION__,
+ __LINE__));
+
+ rval = (-ENOMEM);
+
+ goto memalloc_error;
+ }
+
+ /*
+ * Slice kioc_list and make a kioc_pool with the individiual kiocs
+ */
+ INIT_LIST_HEAD(&adapter->kioc_pool);
+ spin_lock_init(&adapter->kioc_pool_lock);
+ sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc);
+
+ mbox_list = (mbox64_t *)adapter->mbox_list;
+
+ for (i = 0; i < lld_adp->max_kioc; i++) {
+
+ kioc = adapter->kioc_list + i;
+ kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i);
+ kioc->pthru32 = pci_pool_alloc(adapter->pthru_dma_pool,
+ GFP_KERNEL, &kioc->pthru32_h);
+
+ if (!kioc->pthru32) {
+
+ con_log(CL_ANN, (KERN_WARNING
+ "megaraid cmm: out of memory, %s %d\n",
+ __FUNCTION__, __LINE__));
+
+ rval = (-ENOMEM);
+
+ goto pthru_dma_pool_error;
+ }
+
+ list_add_tail(&kioc->list, &adapter->kioc_pool);
+ }
+
+ // Setup the dma pools for data buffers
+ if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) {
+ goto dma_pool_error;
+ }
+
+ list_add_tail(&adapter->list, &adapters_list_g);
+
+ adapters_count_g++;
+
+ return 0;
+
+dma_pool_error:
+ /* Do nothing */
+
+pthru_dma_pool_error:
+
+ for (i = 0; i < lld_adp->max_kioc; i++) {
+ kioc = adapter->kioc_list + i;
+ if (kioc->pthru32) {
+ pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32,
+ kioc->pthru32_h);
+ }
+ }
+
+memalloc_error:
+
+ if (adapter->kioc_list)
+ kfree(adapter->kioc_list);
+
+ if (adapter->mbox_list)
+ kfree(adapter->mbox_list);
+
+ if (adapter->pthru_dma_pool)
+ pci_pool_destroy(adapter->pthru_dma_pool);
+
+ if (adapter)
+ kfree(adapter);
+
+ return rval;
+}
+
+
+/**
+ * mraid_mm_adapter_app_handle - return the application handle for this adapter
+ *
+ * For the given driver data, locate the adadpter in our global list and
+ * return the corresponding handle, which is also used by applications to
+ * uniquely identify an adapter.
+ *
+ * @param unique_id : adapter unique identifier
+ *
+ * @return adapter handle if found in the list
+ * @return 0 if adapter could not be located, should never happen though
+ */
+uint32_t
+mraid_mm_adapter_app_handle(uint32_t unique_id)
+{
+ mraid_mmadp_t *adapter;
+ mraid_mmadp_t *tmp;
+ int index = 0;
+
+ list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
+
+ if (adapter->unique_id == unique_id) {
+
+ return MKADAP(index);
+ }
+
+ index++;
+ }
+
+ return 0;
+}
+
+
+/**
+ * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter
+ *
+ * @adp : Adapter softstate
+ *
+ * We maintain a pool of dma buffers per each adapter. Each pool has one
+ * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers.
+ * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We
+ * dont' want to waste too much memory by allocating more buffers per each
+ * pool.
+ */
+static int
+mraid_mm_setup_dma_pools(mraid_mmadp_t *adp)
+{
+ mm_dmapool_t *pool;
+ int bufsize;
+ int i;
+
+ /*
+ * Create MAX_DMA_POOLS number of pools
+ */
+ bufsize = MRAID_MM_INIT_BUFF_SIZE;
+
+ for (i = 0; i < MAX_DMA_POOLS; i++){
+
+ pool = &adp->dma_pool_list[i];
+
+ pool->buf_size = bufsize;
+ spin_lock_init(&pool->lock);
+
+ pool->handle = pci_pool_create("megaraid mm data buffer",
+ adp->pdev, bufsize, 16, 0);
+
+ if (!pool->handle) {
+ goto dma_pool_setup_error;
+ }
+
+ pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL,
+ &pool->paddr);
+
+ if (!pool->vaddr)
+ goto dma_pool_setup_error;
+
+ bufsize = bufsize * 2;
+ }
+
+ return 0;
+
+dma_pool_setup_error:
+
+ mraid_mm_teardown_dma_pools(adp);
+ return (-ENOMEM);
+}
+
+
+/*
+ * mraid_mm_unregister_adp - Unregister routine for low level drivers
+ * Assume no outstanding ioctls to llds.
+ *
+ * @unique_id : UID of the adpater
+ */
+int
+mraid_mm_unregister_adp(uint32_t unique_id)
+{
+ mraid_mmadp_t *adapter;
+ mraid_mmadp_t *tmp;
+
+ list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) {
+
+
+ if (adapter->unique_id == unique_id) {
+
+ adapters_count_g--;
+
+ list_del_init(&adapter->list);
+
+ mraid_mm_free_adp_resources(adapter);
+
+ kfree(adapter);
+
+ con_log(CL_ANN, (
+ "megaraid cmm: Unregistered one adapter:%#x\n",
+ unique_id));
+
+ return 0;
+ }
+ }
+
+ return (-ENODEV);
+}
+
+/**
+ * mraid_mm_free_adp_resources - Free adapter softstate
+ *
+ * @adp : Adapter softstate
+ */
+static void
+mraid_mm_free_adp_resources(mraid_mmadp_t *adp)
+{
+ uioc_t *kioc;
+ int i;
+
+ mraid_mm_teardown_dma_pools(adp);
+
+ for (i = 0; i < adp->max_kioc; i++) {
+
+ kioc = adp->kioc_list + i;
+
+ pci_pool_free(adp->pthru_dma_pool, kioc->pthru32,
+ kioc->pthru32_h);
+ }
+
+ kfree(adp->kioc_list);
+
+ kfree(adp->mbox_list);
+
+ pci_pool_destroy(adp->pthru_dma_pool);
+
+
+ return;
+}
+
+
+/**
+ * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers
+ *
+ * @adp : Adapter softstate
+ */
+static void
+mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp)
+{
+ int i;
+ mm_dmapool_t *pool;
+
+ for (i = 0; i < MAX_DMA_POOLS; i++) {
+
+ pool = &adp->dma_pool_list[i];
+
+ if (pool->handle) {
+
+ if (pool->vaddr)
+ pci_pool_free(pool->handle, pool->vaddr,
+ pool->paddr);
+
+ pci_pool_destroy(pool->handle);
+ pool->handle = NULL;
+ }
+ }
+
+ return;
+}
+
+/**
+ * mraid_mm_init : Module entry point
+ */
+static int __init
+mraid_mm_init(void)
+{
+ // Announce the driver version
+ con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n",
+ LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION));
+
+ majorno = register_chrdev(0, "megadev", &lsi_fops);
+
+ if (majorno < 0) {
+ con_log(CL_ANN, ("megaraid cmm: cannot get major\n"));
+ return majorno;
+ }
+
+ init_waitqueue_head(&wait_q);
+
+ INIT_LIST_HEAD(&adapters_list_g);
+
+ return 0;
+}
+
+
+/**
+ * mraid_mm_compat_ioctl : 32bit to 64bit ioctl conversion routine
+ */
+#ifdef CONFIG_COMPAT
+static long
+mraid_mm_compat_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+ lock_kernel();
+ err = mraid_mm_ioctl(NULL, filep, cmd, arg);
+ unlock_kernel();
+ return err;
+}
+#endif
+
+/**
+ * mraid_mm_exit : Module exit point
+ */
+static void __exit
+mraid_mm_exit(void)
+{
+ con_log(CL_DLEVEL1 , ("exiting common mod\n"));
+
+ unregister_chrdev(majorno, "megadev");
+}
+
+module_init(mraid_mm_init);
+module_exit(mraid_mm_exit);
+
+/* vi: set ts=8 sw=8 tw=78: */
diff --git a/drivers/scsi/megaraid/megaraid_mm.h b/drivers/scsi/megaraid/megaraid_mm.h
new file mode 100644
index 000000000000..948a0012ab8c
--- /dev/null
+++ b/drivers/scsi/megaraid/megaraid_mm.h
@@ -0,0 +1,102 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright (c) 2003-2004 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FILE : megaraid_mm.h
+ */
+
+#ifndef MEGARAID_MM_H
+#define MEGARAID_MM_H
+
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/ioctl32.h>
+
+#include "mbox_defs.h"
+#include "megaraid_ioctl.h"
+
+
+#define LSI_COMMON_MOD_VERSION "2.20.2.5"
+#define LSI_COMMON_MOD_EXT_VERSION \
+ "(Release Date: Fri Jan 21 00:01:03 EST 2005)"
+
+
+#define LSI_DBGLVL dbglevel
+
+// The smallest dma pool
+#define MRAID_MM_INIT_BUFF_SIZE 4096
+
+/**
+ * mimd_t : Old style ioctl packet structure (deprecated)
+ *
+ * @inlen :
+ * @outlen :
+ * @fca :
+ * @opcode :
+ * @subopcode :
+ * @adapno :
+ * @buffer :
+ * @pad :
+ * @length :
+ * @mbox :
+ * @pthru :
+ * @data :
+ * @pad :
+ *
+ * Note : This structure is DEPRECATED. New applications must use
+ * : uioc_t structure instead. All new hba drivers use the new
+ * : format. If we get this mimd packet, we will convert it into
+ * : new uioc_t format and send it to the hba drivers.
+ */
+
+typedef struct mimd {
+
+ uint32_t inlen;
+ uint32_t outlen;
+
+ union {
+ uint8_t fca[16];
+ struct {
+ uint8_t opcode;
+ uint8_t subopcode;
+ uint16_t adapno;
+#if BITS_PER_LONG == 32
+ uint8_t __user *buffer;
+ uint8_t pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ uint8_t __user *buffer;
+#endif
+ uint32_t length;
+ } __attribute__ ((packed)) fcs;
+ } __attribute__ ((packed)) ui;
+
+ uint8_t mbox[18]; /* 16 bytes + 2 status bytes */
+ mraid_passthru_t pthru;
+
+#if BITS_PER_LONG == 32
+ char __user *data; /* buffer <= 4096 for 0x80 commands */
+ char pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ char __user *data;
+#endif
+
+} __attribute__ ((packed))mimd_t;
+
+#endif // MEGARAID_MM_H
+
+// vi: set ts=8 sw=8 tw=78:
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
new file mode 100644
index 000000000000..85f3a74ac421
--- /dev/null
+++ b/drivers/scsi/mesh.c
@@ -0,0 +1,2062 @@
+/*
+ * SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware)
+ * bus adaptor found on Power Macintosh computers.
+ * We assume the MESH is connected to a DBDMA (descriptor-based DMA)
+ * controller.
+ *
+ * Paul Mackerras, August 1996.
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * Apr. 21 2002 - BenH Rework bus reset code for new error handler
+ * Add delay after initial bus reset
+ * Add module parameters
+ *
+ * Sep. 27 2003 - BenH Move to new driver model, fix some write posting
+ * issues
+ * To do:
+ * - handle aborts correctly
+ * - retry arbitration if lost (unless higher levels do this for us)
+ * - power down the chip when no device is detected
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <asm/dbdma.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hydra.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#include <asm/macio.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "mesh.h"
+
+#if 1
+#undef KERN_DEBUG
+#define KERN_DEBUG KERN_WARNING
+#endif
+
+MODULE_AUTHOR("Paul Mackerras (paulus@samba.org)");
+MODULE_DESCRIPTION("PowerMac MESH SCSI driver");
+MODULE_LICENSE("GPL");
+
+static int sync_rate = CONFIG_SCSI_MESH_SYNC_RATE;
+static int sync_targets = 0xff;
+static int resel_targets = 0xff;
+static int debug_targets = 0; /* print debug for these targets */
+static int init_reset_delay = CONFIG_SCSI_MESH_RESET_DELAY_MS;
+
+module_param(sync_rate, int, 0);
+MODULE_PARM_DESC(sync_rate, "Synchronous rate (0..10, 0=async)");
+module_param(sync_targets, int, 0);
+MODULE_PARM_DESC(sync_targets, "Bitmask of targets allowed to set synchronous");
+module_param(resel_targets, int, 0);
+MODULE_PARM_DESC(resel_targets, "Bitmask of targets allowed to set disconnect");
+module_param(debug_targets, int, 0644);
+MODULE_PARM_DESC(debug_targets, "Bitmask of debugged targets");
+module_param(init_reset_delay, int, 0);
+MODULE_PARM_DESC(init_reset_delay, "Initial bus reset delay (0=no reset)");
+
+static int mesh_sync_period = 100;
+static int mesh_sync_offset = 0;
+static unsigned char use_active_neg = 0; /* bit mask for SEQ_ACTIVE_NEG if used */
+
+#define ALLOW_SYNC(tgt) ((sync_targets >> (tgt)) & 1)
+#define ALLOW_RESEL(tgt) ((resel_targets >> (tgt)) & 1)
+#define ALLOW_DEBUG(tgt) ((debug_targets >> (tgt)) & 1)
+#define DEBUG_TARGET(cmd) ((cmd) && ALLOW_DEBUG((cmd)->device->id))
+
+#undef MESH_DBG
+#define N_DBG_LOG 50
+#define N_DBG_SLOG 20
+#define NUM_DBG_EVENTS 13
+#undef DBG_USE_TB /* bombs on 601 */
+
+struct dbglog {
+ char *fmt;
+ u32 tb;
+ u8 phase;
+ u8 bs0;
+ u8 bs1;
+ u8 tgt;
+ int d;
+};
+
+enum mesh_phase {
+ idle,
+ arbitrating,
+ selecting,
+ commanding,
+ dataing,
+ statusing,
+ busfreeing,
+ disconnecting,
+ reselecting,
+ sleeping
+};
+
+enum msg_phase {
+ msg_none,
+ msg_out,
+ msg_out_xxx,
+ msg_out_last,
+ msg_in,
+ msg_in_bad,
+};
+
+enum sdtr_phase {
+ do_sdtr,
+ sdtr_sent,
+ sdtr_done
+};
+
+struct mesh_target {
+ enum sdtr_phase sdtr_state;
+ int sync_params;
+ int data_goes_out; /* guess as to data direction */
+ struct scsi_cmnd *current_req;
+ u32 saved_ptr;
+#ifdef MESH_DBG
+ int log_ix;
+ int n_log;
+ struct dbglog log[N_DBG_LOG];
+#endif
+};
+
+struct mesh_state {
+ volatile struct mesh_regs __iomem *mesh;
+ int meshintr;
+ volatile struct dbdma_regs __iomem *dma;
+ int dmaintr;
+ struct Scsi_Host *host;
+ struct mesh_state *next;
+ struct scsi_cmnd *request_q;
+ struct scsi_cmnd *request_qtail;
+ enum mesh_phase phase; /* what we're currently trying to do */
+ enum msg_phase msgphase;
+ int conn_tgt; /* target we're connected to */
+ struct scsi_cmnd *current_req; /* req we're currently working on */
+ int data_ptr;
+ int dma_started;
+ int dma_count;
+ int stat;
+ int aborting;
+ int expect_reply;
+ int n_msgin;
+ u8 msgin[16];
+ int n_msgout;
+ int last_n_msgout;
+ u8 msgout[16];
+ struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
+ dma_addr_t dma_cmd_bus;
+ void *dma_cmd_space;
+ int dma_cmd_size;
+ int clk_freq;
+ struct mesh_target tgts[8];
+ struct macio_dev *mdev;
+ struct pci_dev* pdev;
+#ifdef MESH_DBG
+ int log_ix;
+ int n_log;
+ struct dbglog log[N_DBG_SLOG];
+#endif
+};
+
+/*
+ * Driver is too messy, we need a few prototypes...
+ */
+static void mesh_done(struct mesh_state *ms, int start_next);
+static void mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs);
+static void cmd_complete(struct mesh_state *ms);
+static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd);
+static void halt_dma(struct mesh_state *ms);
+static void phase_mismatch(struct mesh_state *ms);
+
+
+/*
+ * Some debugging & logging routines
+ */
+
+#ifdef MESH_DBG
+
+static inline u32 readtb(void)
+{
+ u32 tb;
+
+#ifdef DBG_USE_TB
+ /* Beware: if you enable this, it will crash on 601s. */
+ asm ("mftb %0" : "=r" (tb) : );
+#else
+ tb = 0;
+#endif
+ return tb;
+}
+
+static void dlog(struct mesh_state *ms, char *fmt, int a)
+{
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ struct dbglog *tlp, *slp;
+
+ tlp = &tp->log[tp->log_ix];
+ slp = &ms->log[ms->log_ix];
+ tlp->fmt = fmt;
+ tlp->tb = readtb();
+ tlp->phase = (ms->msgphase << 4) + ms->phase;
+ tlp->bs0 = ms->mesh->bus_status0;
+ tlp->bs1 = ms->mesh->bus_status1;
+ tlp->tgt = ms->conn_tgt;
+ tlp->d = a;
+ *slp = *tlp;
+ if (++tp->log_ix >= N_DBG_LOG)
+ tp->log_ix = 0;
+ if (tp->n_log < N_DBG_LOG)
+ ++tp->n_log;
+ if (++ms->log_ix >= N_DBG_SLOG)
+ ms->log_ix = 0;
+ if (ms->n_log < N_DBG_SLOG)
+ ++ms->n_log;
+}
+
+static void dumplog(struct mesh_state *ms, int t)
+{
+ struct mesh_target *tp = &ms->tgts[t];
+ struct dbglog *lp;
+ int i;
+
+ if (tp->n_log == 0)
+ return;
+ i = tp->log_ix - tp->n_log;
+ if (i < 0)
+ i += N_DBG_LOG;
+ tp->n_log = 0;
+ do {
+ lp = &tp->log[i];
+ printk(KERN_DEBUG "mesh log %d: bs=%.2x%.2x ph=%.2x ",
+ t, lp->bs1, lp->bs0, lp->phase);
+#ifdef DBG_USE_TB
+ printk("tb=%10u ", lp->tb);
+#endif
+ printk(lp->fmt, lp->d);
+ printk("\n");
+ if (++i >= N_DBG_LOG)
+ i = 0;
+ } while (i != tp->log_ix);
+}
+
+static void dumpslog(struct mesh_state *ms)
+{
+ struct dbglog *lp;
+ int i;
+
+ if (ms->n_log == 0)
+ return;
+ i = ms->log_ix - ms->n_log;
+ if (i < 0)
+ i += N_DBG_SLOG;
+ ms->n_log = 0;
+ do {
+ lp = &ms->log[i];
+ printk(KERN_DEBUG "mesh log: bs=%.2x%.2x ph=%.2x t%d ",
+ lp->bs1, lp->bs0, lp->phase, lp->tgt);
+#ifdef DBG_USE_TB
+ printk("tb=%10u ", lp->tb);
+#endif
+ printk(lp->fmt, lp->d);
+ printk("\n");
+ if (++i >= N_DBG_SLOG)
+ i = 0;
+ } while (i != ms->log_ix);
+}
+
+#else
+
+static inline void dlog(struct mesh_state *ms, char *fmt, int a)
+{}
+static inline void dumplog(struct mesh_state *ms, int tgt)
+{}
+static inline void dumpslog(struct mesh_state *ms)
+{}
+
+#endif /* MESH_DBG */
+
+#define MKWORD(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+static void
+mesh_dump_regs(struct mesh_state *ms)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ volatile struct dbdma_regs __iomem *md = ms->dma;
+ int t;
+ struct mesh_target *tp;
+
+ printk(KERN_DEBUG "mesh: state at %p, regs at %p, dma at %p\n",
+ ms, mr, md);
+ printk(KERN_DEBUG " ct=%4x seq=%2x bs=%4x fc=%2x "
+ "exc=%2x err=%2x im=%2x int=%2x sp=%2x\n",
+ (mr->count_hi << 8) + mr->count_lo, mr->sequence,
+ (mr->bus_status1 << 8) + mr->bus_status0, mr->fifo_count,
+ mr->exception, mr->error, mr->intr_mask, mr->interrupt,
+ mr->sync_params);
+ while(in_8(&mr->fifo_count))
+ printk(KERN_DEBUG " fifo data=%.2x\n",in_8(&mr->fifo));
+ printk(KERN_DEBUG " dma stat=%x cmdptr=%x\n",
+ in_le32(&md->status), in_le32(&md->cmdptr));
+ printk(KERN_DEBUG " phase=%d msgphase=%d conn_tgt=%d data_ptr=%d\n",
+ ms->phase, ms->msgphase, ms->conn_tgt, ms->data_ptr);
+ printk(KERN_DEBUG " dma_st=%d dma_ct=%d n_msgout=%d\n",
+ ms->dma_started, ms->dma_count, ms->n_msgout);
+ for (t = 0; t < 8; ++t) {
+ tp = &ms->tgts[t];
+ if (tp->current_req == NULL)
+ continue;
+ printk(KERN_DEBUG " target %d: req=%p goes_out=%d saved_ptr=%d\n",
+ t, tp->current_req, tp->data_goes_out, tp->saved_ptr);
+ }
+}
+
+
+/*
+ * Flush write buffers on the bus path to the mesh
+ */
+static inline void mesh_flush_io(volatile struct mesh_regs __iomem *mr)
+{
+ (void)in_8(&mr->mesh_id);
+}
+
+
+/*
+ * Complete a SCSI command
+ */
+static void mesh_completed(struct mesh_state *ms, struct scsi_cmnd *cmd)
+{
+ (*cmd->scsi_done)(cmd);
+}
+
+
+/* Called with meshinterrupt disabled, initialize the chipset
+ * and eventually do the initial bus reset. The lock must not be
+ * held since we can schedule.
+ */
+static void mesh_init(struct mesh_state *ms)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ volatile struct dbdma_regs __iomem *md = ms->dma;
+
+ mesh_flush_io(mr);
+ udelay(100);
+
+ /* Reset controller */
+ out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
+ out_8(&mr->exception, 0xff); /* clear all exception bits */
+ out_8(&mr->error, 0xff); /* clear all error bits */
+ out_8(&mr->sequence, SEQ_RESETMESH);
+ mesh_flush_io(mr);
+ udelay(10);
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->source_id, ms->host->this_id);
+ out_8(&mr->sel_timeout, 25); /* 250ms */
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+
+ if (init_reset_delay) {
+ printk(KERN_INFO "mesh: performing initial bus reset...\n");
+
+ /* Reset bus */
+ out_8(&mr->bus_status1, BS1_RST); /* assert RST */
+ mesh_flush_io(mr);
+ udelay(30); /* leave it on for >= 25us */
+ out_8(&mr->bus_status1, 0); /* negate RST */
+ mesh_flush_io(mr);
+
+ /* Wait for bus to come back */
+ msleep(init_reset_delay);
+ }
+
+ /* Reconfigure controller */
+ out_8(&mr->interrupt, 0xff); /* clear all interrupt bits */
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+
+ ms->phase = idle;
+ ms->msgphase = msg_none;
+}
+
+
+static void mesh_start_cmd(struct mesh_state *ms, struct scsi_cmnd *cmd)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ int t, id;
+
+ id = cmd->device->id;
+ ms->current_req = cmd;
+ ms->tgts[id].data_goes_out = cmd->sc_data_direction == DMA_TO_DEVICE;
+ ms->tgts[id].current_req = cmd;
+
+#if 1
+ if (DEBUG_TARGET(cmd)) {
+ int i;
+ printk(KERN_DEBUG "mesh_start: %p ser=%lu tgt=%d cmd=",
+ cmd, cmd->serial_number, id);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ printk(" %x", cmd->cmnd[i]);
+ printk(" use_sg=%d buffer=%p bufflen=%u\n",
+ cmd->use_sg, cmd->request_buffer, cmd->request_bufflen);
+ }
+#endif
+ if (ms->dma_started)
+ panic("mesh: double DMA start !\n");
+
+ ms->phase = arbitrating;
+ ms->msgphase = msg_none;
+ ms->data_ptr = 0;
+ ms->dma_started = 0;
+ ms->n_msgout = 0;
+ ms->last_n_msgout = 0;
+ ms->expect_reply = 0;
+ ms->conn_tgt = id;
+ ms->tgts[id].saved_ptr = 0;
+ ms->stat = DID_OK;
+ ms->aborting = 0;
+#ifdef MESH_DBG
+ ms->tgts[id].n_log = 0;
+ dlog(ms, "start cmd=%x", (int) cmd);
+#endif
+
+ /* Off we go */
+ dlog(ms, "about to arb, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
+ out_8(&mr->interrupt, INT_CMDDONE);
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
+ udelay(1);
+
+ if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
+ /*
+ * Some other device has the bus or is arbitrating for it -
+ * probably a target which is about to reselect us.
+ */
+ dlog(ms, "busy b4 arb, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception,
+ mr->error, mr->fifo_count));
+ for (t = 100; t > 0; --t) {
+ if ((in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) == 0)
+ break;
+ if (in_8(&mr->interrupt) != 0) {
+ dlog(ms, "intr b4 arb, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception,
+ mr->error, mr->fifo_count));
+ mesh_interrupt(0, (void *)ms, NULL);
+ if (ms->phase != arbitrating)
+ return;
+ }
+ udelay(1);
+ }
+ if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
+ /* XXX should try again in a little while */
+ ms->stat = DID_BUS_BUSY;
+ ms->phase = idle;
+ mesh_done(ms, 0);
+ return;
+ }
+ }
+
+ /*
+ * Apparently the mesh has a bug where it will assert both its
+ * own bit and the target's bit on the bus during arbitration.
+ */
+ out_8(&mr->dest_id, mr->source_id);
+
+ /*
+ * There appears to be a race with reselection sometimes,
+ * where a target reselects us just as we issue the
+ * arbitrate command. It seems that then the arbitrate
+ * command just hangs waiting for the bus to be free
+ * without giving us a reselection exception.
+ * The only way I have found to get it to respond correctly
+ * is this: disable reselection before issuing the arbitrate
+ * command, then after issuing it, if it looks like a target
+ * is trying to reselect us, reset the mesh and then enable
+ * reselection.
+ */
+ out_8(&mr->sequence, SEQ_DISRESEL);
+ if (in_8(&mr->interrupt) != 0) {
+ dlog(ms, "intr after disresel, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception,
+ mr->error, mr->fifo_count));
+ mesh_interrupt(0, (void *)ms, NULL);
+ if (ms->phase != arbitrating)
+ return;
+ dlog(ms, "after intr after disresel, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception,
+ mr->error, mr->fifo_count));
+ }
+
+ out_8(&mr->sequence, SEQ_ARBITRATE);
+
+ for (t = 230; t > 0; --t) {
+ if (in_8(&mr->interrupt) != 0)
+ break;
+ udelay(1);
+ }
+ dlog(ms, "after arb, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
+ if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
+ && (in_8(&mr->bus_status0) & BS0_IO)) {
+ /* looks like a reselection - try resetting the mesh */
+ dlog(ms, "resel? after arb, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
+ out_8(&mr->sequence, SEQ_RESETMESH);
+ mesh_flush_io(mr);
+ udelay(10);
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
+ for (t = 10; t > 0 && in_8(&mr->interrupt) == 0; --t)
+ udelay(1);
+ dlog(ms, "tried reset after arb, intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
+#ifndef MESH_MULTIPLE_HOSTS
+ if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
+ && (in_8(&mr->bus_status0) & BS0_IO)) {
+ printk(KERN_ERR "mesh: controller not responding"
+ " to reselection!\n");
+ /*
+ * If this is a target reselecting us, and the
+ * mesh isn't responding, the higher levels of
+ * the scsi code will eventually time out and
+ * reset the bus.
+ */
+ }
+#endif
+ }
+}
+
+/*
+ * Start the next command for a MESH.
+ * Should be called with interrupts disabled.
+ */
+static void mesh_start(struct mesh_state *ms)
+{
+ struct scsi_cmnd *cmd, *prev, *next;
+
+ if (ms->phase != idle || ms->current_req != NULL) {
+ printk(KERN_ERR "inappropriate mesh_start (phase=%d, ms=%p)",
+ ms->phase, ms);
+ return;
+ }
+
+ while (ms->phase == idle) {
+ prev = NULL;
+ for (cmd = ms->request_q; ; cmd = (struct scsi_cmnd *) cmd->host_scribble) {
+ if (cmd == NULL)
+ return;
+ if (ms->tgts[cmd->device->id].current_req == NULL)
+ break;
+ prev = cmd;
+ }
+ next = (struct scsi_cmnd *) cmd->host_scribble;
+ if (prev == NULL)
+ ms->request_q = next;
+ else
+ prev->host_scribble = (void *) next;
+ if (next == NULL)
+ ms->request_qtail = prev;
+
+ mesh_start_cmd(ms, cmd);
+ }
+}
+
+static void mesh_done(struct mesh_state *ms, int start_next)
+{
+ struct scsi_cmnd *cmd;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ cmd = ms->current_req;
+ ms->current_req = NULL;
+ tp->current_req = NULL;
+ if (cmd) {
+ cmd->result = (ms->stat << 16) + cmd->SCp.Status;
+ if (ms->stat == DID_OK)
+ cmd->result += (cmd->SCp.Message << 8);
+ if (DEBUG_TARGET(cmd)) {
+ printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
+ cmd->result, ms->data_ptr, cmd->request_bufflen);
+ if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 3)
+ && cmd->request_buffer != 0) {
+ unsigned char *b = cmd->request_buffer;
+ printk(KERN_DEBUG "buffer = %x %x %x %x %x %x %x %x\n",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+ }
+ }
+ cmd->SCp.this_residual -= ms->data_ptr;
+ mesh_completed(ms, cmd);
+ }
+ if (start_next) {
+ out_8(&ms->mesh->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(ms->mesh);
+ udelay(1);
+ ms->phase = idle;
+ mesh_start(ms);
+ }
+}
+
+static inline void add_sdtr_msg(struct mesh_state *ms)
+{
+ int i = ms->n_msgout;
+
+ ms->msgout[i] = EXTENDED_MESSAGE;
+ ms->msgout[i+1] = 3;
+ ms->msgout[i+2] = EXTENDED_SDTR;
+ ms->msgout[i+3] = mesh_sync_period/4;
+ ms->msgout[i+4] = (ALLOW_SYNC(ms->conn_tgt)? mesh_sync_offset: 0);
+ ms->n_msgout = i + 5;
+}
+
+static void set_sdtr(struct mesh_state *ms, int period, int offset)
+{
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ int v, tr;
+
+ tp->sdtr_state = sdtr_done;
+ if (offset == 0) {
+ /* asynchronous */
+ if (SYNC_OFF(tp->sync_params))
+ printk(KERN_INFO "mesh: target %d now asynchronous\n",
+ ms->conn_tgt);
+ tp->sync_params = ASYNC_PARAMS;
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+ return;
+ }
+ /*
+ * We need to compute ceil(clk_freq * period / 500e6) - 2
+ * without incurring overflow.
+ */
+ v = (ms->clk_freq / 5000) * period;
+ if (v <= 250000) {
+ /* special case: sync_period == 5 * clk_period */
+ v = 0;
+ /* units of tr are 100kB/s */
+ tr = (ms->clk_freq + 250000) / 500000;
+ } else {
+ /* sync_period == (v + 2) * 2 * clk_period */
+ v = (v + 99999) / 100000 - 2;
+ if (v > 15)
+ v = 15; /* oops */
+ tr = ((ms->clk_freq / (v + 2)) + 199999) / 200000;
+ }
+ if (offset > 15)
+ offset = 15; /* can't happen */
+ tp->sync_params = SYNC_PARAMS(offset, v);
+ out_8(&mr->sync_params, tp->sync_params);
+ printk(KERN_INFO "mesh: target %d synchronous at %d.%d MB/s\n",
+ ms->conn_tgt, tr/10, tr%10);
+}
+
+static void start_phase(struct mesh_state *ms)
+{
+ int i, seq, nb;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ volatile struct dbdma_regs __iomem *md = ms->dma;
+ struct scsi_cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ dlog(ms, "start_phase nmo/exc/fc/seq = %.8x",
+ MKWORD(ms->n_msgout, mr->exception, mr->fifo_count, mr->sequence));
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
+ switch (ms->msgphase) {
+ case msg_none:
+ break;
+
+ case msg_in:
+ out_8(&mr->count_hi, 0);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGIN + seq);
+ ms->n_msgin = 0;
+ return;
+
+ case msg_out:
+ /*
+ * To make sure ATN drops before we assert ACK for
+ * the last byte of the message, we have to do the
+ * last byte specially.
+ */
+ if (ms->n_msgout <= 0) {
+ printk(KERN_ERR "mesh: msg_out but n_msgout=%d\n",
+ ms->n_msgout);
+ mesh_dump_regs(ms);
+ ms->msgphase = msg_none;
+ break;
+ }
+ if (ALLOW_DEBUG(ms->conn_tgt)) {
+ printk(KERN_DEBUG "mesh: sending %d msg bytes:",
+ ms->n_msgout);
+ for (i = 0; i < ms->n_msgout; ++i)
+ printk(" %x", ms->msgout[i]);
+ printk("\n");
+ }
+ dlog(ms, "msgout msg=%.8x", MKWORD(ms->n_msgout, ms->msgout[0],
+ ms->msgout[1], ms->msgout[2]));
+ out_8(&mr->count_hi, 0);
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ /*
+ * If ATN is not already asserted, we assert it, then
+ * issue a SEQ_MSGOUT to get the mesh to drop ACK.
+ */
+ if ((in_8(&mr->bus_status0) & BS0_ATN) == 0) {
+ dlog(ms, "bus0 was %.2x explictly asserting ATN", mr->bus_status0);
+ out_8(&mr->bus_status0, BS0_ATN); /* explicit ATN */
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + seq);
+ out_8(&mr->bus_status0, 0); /* release explicit ATN */
+ dlog(ms,"hace: after explicit ATN bus0=%.2x",mr->bus_status0);
+ }
+ if (ms->n_msgout == 1) {
+ /*
+ * We can't issue the SEQ_MSGOUT without ATN
+ * until the target has asserted REQ. The logic
+ * in cmd_complete handles both situations:
+ * REQ already asserted or not.
+ */
+ cmd_complete(ms);
+ } else {
+ out_8(&mr->count_lo, ms->n_msgout - 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + seq);
+ for (i = 0; i < ms->n_msgout - 1; ++i)
+ out_8(&mr->fifo, ms->msgout[i]);
+ }
+ return;
+
+ default:
+ printk(KERN_ERR "mesh bug: start_phase msgphase=%d\n",
+ ms->msgphase);
+ }
+
+ switch (ms->phase) {
+ case selecting:
+ out_8(&mr->dest_id, ms->conn_tgt);
+ out_8(&mr->sequence, SEQ_SELECT + SEQ_ATN);
+ break;
+ case commanding:
+ out_8(&mr->sync_params, tp->sync_params);
+ out_8(&mr->count_hi, 0);
+ if (cmd) {
+ out_8(&mr->count_lo, cmd->cmd_len);
+ out_8(&mr->sequence, SEQ_COMMAND + seq);
+ for (i = 0; i < cmd->cmd_len; ++i)
+ out_8(&mr->fifo, cmd->cmnd[i]);
+ } else {
+ out_8(&mr->count_lo, 6);
+ out_8(&mr->sequence, SEQ_COMMAND + seq);
+ for (i = 0; i < 6; ++i)
+ out_8(&mr->fifo, 0);
+ }
+ break;
+ case dataing:
+ /* transfer data, if any */
+ if (!ms->dma_started) {
+ set_dma_cmds(ms, cmd);
+ out_le32(&md->cmdptr, virt_to_phys(ms->dma_cmds));
+ out_le32(&md->control, (RUN << 16) | RUN);
+ ms->dma_started = 1;
+ }
+ nb = ms->dma_count;
+ if (nb > 0xfff0)
+ nb = 0xfff0;
+ ms->dma_count -= nb;
+ ms->data_ptr += nb;
+ out_8(&mr->count_lo, nb);
+ out_8(&mr->count_hi, nb >> 8);
+ out_8(&mr->sequence, (tp->data_goes_out?
+ SEQ_DATAOUT: SEQ_DATAIN) + SEQ_DMA_MODE + seq);
+ break;
+ case statusing:
+ out_8(&mr->count_hi, 0);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_STATUS + seq);
+ break;
+ case busfreeing:
+ case disconnecting:
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
+ udelay(1);
+ dlog(ms, "enbresel intr/exc/err/fc=%.8x",
+ MKWORD(mr->interrupt, mr->exception, mr->error,
+ mr->fifo_count));
+ out_8(&mr->sequence, SEQ_BUSFREE);
+ break;
+ default:
+ printk(KERN_ERR "mesh: start_phase called with phase=%d\n",
+ ms->phase);
+ dumpslog(ms);
+ }
+
+}
+
+static inline void get_msgin(struct mesh_state *ms)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ int i, n;
+
+ n = mr->fifo_count;
+ if (n != 0) {
+ i = ms->n_msgin;
+ ms->n_msgin = i + n;
+ for (; n > 0; --n)
+ ms->msgin[i++] = in_8(&mr->fifo);
+ }
+}
+
+static inline int msgin_length(struct mesh_state *ms)
+{
+ int b, n;
+
+ n = 1;
+ if (ms->n_msgin > 0) {
+ b = ms->msgin[0];
+ if (b == 1) {
+ /* extended message */
+ n = ms->n_msgin < 2? 2: ms->msgin[1] + 2;
+ } else if (0x20 <= b && b <= 0x2f) {
+ /* 2-byte message */
+ n = 2;
+ }
+ }
+ return n;
+}
+
+static void reselected(struct mesh_state *ms)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ struct scsi_cmnd *cmd;
+ struct mesh_target *tp;
+ int b, t, prev;
+
+ switch (ms->phase) {
+ case idle:
+ break;
+ case arbitrating:
+ if ((cmd = ms->current_req) != NULL) {
+ /* put the command back on the queue */
+ cmd->host_scribble = (void *) ms->request_q;
+ if (ms->request_q == NULL)
+ ms->request_qtail = cmd;
+ ms->request_q = cmd;
+ tp = &ms->tgts[cmd->device->id];
+ tp->current_req = NULL;
+ }
+ break;
+ case busfreeing:
+ ms->phase = reselecting;
+ mesh_done(ms, 0);
+ break;
+ case disconnecting:
+ break;
+ default:
+ printk(KERN_ERR "mesh: reselected in phase %d/%d tgt %d\n",
+ ms->msgphase, ms->phase, ms->conn_tgt);
+ dumplog(ms, ms->conn_tgt);
+ dumpslog(ms);
+ }
+
+ if (ms->dma_started) {
+ printk(KERN_ERR "mesh: reselected with DMA started !\n");
+ halt_dma(ms);
+ }
+ ms->current_req = NULL;
+ ms->phase = dataing;
+ ms->msgphase = msg_in;
+ ms->n_msgout = 0;
+ ms->last_n_msgout = 0;
+ prev = ms->conn_tgt;
+
+ /*
+ * We seem to get abortive reselections sometimes.
+ */
+ while ((in_8(&mr->bus_status1) & BS1_BSY) == 0) {
+ static int mesh_aborted_resels;
+ mesh_aborted_resels++;
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
+ udelay(5);
+ dlog(ms, "extra resel err/exc/fc = %.6x",
+ MKWORD(0, mr->error, mr->exception, mr->fifo_count));
+ }
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+
+ /*
+ * Find out who reselected us.
+ */
+ if (in_8(&mr->fifo_count) == 0) {
+ printk(KERN_ERR "mesh: reselection but nothing in fifo?\n");
+ ms->conn_tgt = ms->host->this_id;
+ goto bogus;
+ }
+ /* get the last byte in the fifo */
+ do {
+ b = in_8(&mr->fifo);
+ dlog(ms, "reseldata %x", b);
+ } while (in_8(&mr->fifo_count));
+ for (t = 0; t < 8; ++t)
+ if ((b & (1 << t)) != 0 && t != ms->host->this_id)
+ break;
+ if (b != (1 << t) + (1 << ms->host->this_id)) {
+ printk(KERN_ERR "mesh: bad reselection data %x\n", b);
+ ms->conn_tgt = ms->host->this_id;
+ goto bogus;
+ }
+
+
+ /*
+ * Set up to continue with that target's transfer.
+ */
+ ms->conn_tgt = t;
+ tp = &ms->tgts[t];
+ out_8(&mr->sync_params, tp->sync_params);
+ if (ALLOW_DEBUG(t)) {
+ printk(KERN_DEBUG "mesh: reselected by target %d\n", t);
+ printk(KERN_DEBUG "mesh: saved_ptr=%x goes_out=%d cmd=%p\n",
+ tp->saved_ptr, tp->data_goes_out, tp->current_req);
+ }
+ ms->current_req = tp->current_req;
+ if (tp->current_req == NULL) {
+ printk(KERN_ERR "mesh: reselected by tgt %d but no cmd!\n", t);
+ goto bogus;
+ }
+ ms->data_ptr = tp->saved_ptr;
+ dlog(ms, "resel prev tgt=%d", prev);
+ dlog(ms, "resel err/exc=%.4x", MKWORD(0, 0, mr->error, mr->exception));
+ start_phase(ms);
+ return;
+
+bogus:
+ dumplog(ms, ms->conn_tgt);
+ dumpslog(ms);
+ ms->data_ptr = 0;
+ ms->aborting = 1;
+ start_phase(ms);
+}
+
+static void do_abort(struct mesh_state *ms)
+{
+ ms->msgout[0] = ABORT;
+ ms->n_msgout = 1;
+ ms->aborting = 1;
+ ms->stat = DID_ABORT;
+ dlog(ms, "abort", 0);
+}
+
+static void handle_reset(struct mesh_state *ms)
+{
+ int tgt;
+ struct mesh_target *tp;
+ struct scsi_cmnd *cmd;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+
+ for (tgt = 0; tgt < 8; ++tgt) {
+ tp = &ms->tgts[tgt];
+ if ((cmd = tp->current_req) != NULL) {
+ cmd->result = DID_RESET << 16;
+ tp->current_req = NULL;
+ mesh_completed(ms, cmd);
+ }
+ ms->tgts[tgt].sdtr_state = do_sdtr;
+ ms->tgts[tgt].sync_params = ASYNC_PARAMS;
+ }
+ ms->current_req = NULL;
+ while ((cmd = ms->request_q) != NULL) {
+ ms->request_q = (struct scsi_cmnd *) cmd->host_scribble;
+ cmd->result = DID_RESET << 16;
+ mesh_completed(ms, cmd);
+ }
+ ms->phase = idle;
+ ms->msgphase = msg_none;
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+ out_8(&mr->sequence, SEQ_ENBRESEL);
+}
+
+static irqreturn_t do_mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = ((struct mesh_state *)dev_id)->host;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ mesh_interrupt(irq, dev_id, ptregs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void handle_error(struct mesh_state *ms)
+{
+ int err, exc, count;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+
+ err = in_8(&mr->error);
+ exc = in_8(&mr->exception);
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ dlog(ms, "error err/exc/fc/cl=%.8x",
+ MKWORD(err, exc, mr->fifo_count, mr->count_lo));
+ if (err & ERR_SCSIRESET) {
+ /* SCSI bus was reset */
+ printk(KERN_INFO "mesh: SCSI bus reset detected: "
+ "waiting for end...");
+ while ((in_8(&mr->bus_status1) & BS1_RST) != 0)
+ udelay(1);
+ printk("done\n");
+ handle_reset(ms);
+ /* request_q is empty, no point in mesh_start() */
+ return;
+ }
+ if (err & ERR_UNEXPDISC) {
+ /* Unexpected disconnect */
+ if (exc & EXC_RESELECTED) {
+ reselected(ms);
+ return;
+ }
+ if (!ms->aborting) {
+ printk(KERN_WARNING "mesh: target %d aborted\n",
+ ms->conn_tgt);
+ dumplog(ms, ms->conn_tgt);
+ dumpslog(ms);
+ }
+ out_8(&mr->interrupt, INT_CMDDONE);
+ ms->stat = DID_ABORT;
+ mesh_done(ms, 1);
+ return;
+ }
+ if (err & ERR_PARITY) {
+ if (ms->msgphase == msg_in) {
+ printk(KERN_ERR "mesh: msg parity error, target %d\n",
+ ms->conn_tgt);
+ ms->msgout[0] = MSG_PARITY_ERROR;
+ ms->n_msgout = 1;
+ ms->msgphase = msg_in_bad;
+ cmd_complete(ms);
+ return;
+ }
+ if (ms->stat == DID_OK) {
+ printk(KERN_ERR "mesh: parity error, target %d\n",
+ ms->conn_tgt);
+ ms->stat = DID_PARITY;
+ }
+ count = (mr->count_hi << 8) + mr->count_lo;
+ if (count == 0) {
+ cmd_complete(ms);
+ } else {
+ /* reissue the data transfer command */
+ out_8(&mr->sequence, mr->sequence);
+ }
+ return;
+ }
+ if (err & ERR_SEQERR) {
+ if (exc & EXC_RESELECTED) {
+ /* This can happen if we issue a command to
+ get the bus just after the target reselects us. */
+ static int mesh_resel_seqerr;
+ mesh_resel_seqerr++;
+ reselected(ms);
+ return;
+ }
+ if (exc == EXC_PHASEMM) {
+ static int mesh_phasemm_seqerr;
+ mesh_phasemm_seqerr++;
+ phase_mismatch(ms);
+ return;
+ }
+ printk(KERN_ERR "mesh: sequence error (err=%x exc=%x)\n",
+ err, exc);
+ } else {
+ printk(KERN_ERR "mesh: unknown error %x (exc=%x)\n", err, exc);
+ }
+ mesh_dump_regs(ms);
+ dumplog(ms, ms->conn_tgt);
+ if (ms->phase > selecting && (in_8(&mr->bus_status1) & BS1_BSY)) {
+ /* try to do what the target wants */
+ do_abort(ms);
+ phase_mismatch(ms);
+ return;
+ }
+ ms->stat = DID_ERROR;
+ mesh_done(ms, 1);
+}
+
+static void handle_exception(struct mesh_state *ms)
+{
+ int exc;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+
+ exc = in_8(&mr->exception);
+ out_8(&mr->interrupt, INT_EXCEPTION | INT_CMDDONE);
+ if (exc & EXC_RESELECTED) {
+ static int mesh_resel_exc;
+ mesh_resel_exc++;
+ reselected(ms);
+ } else if (exc == EXC_ARBLOST) {
+ printk(KERN_DEBUG "mesh: lost arbitration\n");
+ ms->stat = DID_BUS_BUSY;
+ mesh_done(ms, 1);
+ } else if (exc == EXC_SELTO) {
+ /* selection timed out */
+ ms->stat = DID_BAD_TARGET;
+ mesh_done(ms, 1);
+ } else if (exc == EXC_PHASEMM) {
+ /* target wants to do something different:
+ find out what it wants and do it. */
+ phase_mismatch(ms);
+ } else {
+ printk(KERN_ERR "mesh: can't cope with exception %x\n", exc);
+ mesh_dump_regs(ms);
+ dumplog(ms, ms->conn_tgt);
+ do_abort(ms);
+ phase_mismatch(ms);
+ }
+}
+
+static void handle_msgin(struct mesh_state *ms)
+{
+ int i, code;
+ struct scsi_cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+
+ if (ms->n_msgin == 0)
+ return;
+ code = ms->msgin[0];
+ if (ALLOW_DEBUG(ms->conn_tgt)) {
+ printk(KERN_DEBUG "got %d message bytes:", ms->n_msgin);
+ for (i = 0; i < ms->n_msgin; ++i)
+ printk(" %x", ms->msgin[i]);
+ printk("\n");
+ }
+ dlog(ms, "msgin msg=%.8x",
+ MKWORD(ms->n_msgin, code, ms->msgin[1], ms->msgin[2]));
+
+ ms->expect_reply = 0;
+ ms->n_msgout = 0;
+ if (ms->n_msgin < msgin_length(ms))
+ goto reject;
+ if (cmd)
+ cmd->SCp.Message = code;
+ switch (code) {
+ case COMMAND_COMPLETE:
+ break;
+ case EXTENDED_MESSAGE:
+ switch (ms->msgin[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ ms->data_ptr += (ms->msgin[3] << 24) + ms->msgin[6]
+ + (ms->msgin[4] << 16) + (ms->msgin[5] << 8);
+ break;
+ case EXTENDED_SDTR:
+ if (tp->sdtr_state != sdtr_sent) {
+ /* reply with an SDTR */
+ add_sdtr_msg(ms);
+ /* limit period to at least his value,
+ offset to no more than his */
+ if (ms->msgout[3] < ms->msgin[3])
+ ms->msgout[3] = ms->msgin[3];
+ if (ms->msgout[4] > ms->msgin[4])
+ ms->msgout[4] = ms->msgin[4];
+ set_sdtr(ms, ms->msgout[3], ms->msgout[4]);
+ ms->msgphase = msg_out;
+ } else {
+ set_sdtr(ms, ms->msgin[3], ms->msgin[4]);
+ }
+ break;
+ default:
+ goto reject;
+ }
+ break;
+ case SAVE_POINTERS:
+ tp->saved_ptr = ms->data_ptr;
+ break;
+ case RESTORE_POINTERS:
+ ms->data_ptr = tp->saved_ptr;
+ break;
+ case DISCONNECT:
+ ms->phase = disconnecting;
+ break;
+ case ABORT:
+ break;
+ case MESSAGE_REJECT:
+ if (tp->sdtr_state == sdtr_sent)
+ set_sdtr(ms, 0, 0);
+ break;
+ case NOP:
+ break;
+ default:
+ if (IDENTIFY_BASE <= code && code <= IDENTIFY_BASE + 7) {
+ if (cmd == NULL) {
+ do_abort(ms);
+ ms->msgphase = msg_out;
+ } else if (code != cmd->device->lun + IDENTIFY_BASE) {
+ printk(KERN_WARNING "mesh: lun mismatch "
+ "(%d != %d) on reselection from "
+ "target %d\n", code - IDENTIFY_BASE,
+ cmd->device->lun, ms->conn_tgt);
+ }
+ break;
+ }
+ goto reject;
+ }
+ return;
+
+ reject:
+ printk(KERN_WARNING "mesh: rejecting message from target %d:",
+ ms->conn_tgt);
+ for (i = 0; i < ms->n_msgin; ++i)
+ printk(" %x", ms->msgin[i]);
+ printk("\n");
+ ms->msgout[0] = MESSAGE_REJECT;
+ ms->n_msgout = 1;
+ ms->msgphase = msg_out;
+}
+
+/*
+ * Set up DMA commands for transferring data.
+ */
+static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd)
+{
+ int i, dma_cmd, total, off, dtot;
+ struct scatterlist *scl;
+ struct dbdma_cmd *dcmds;
+
+ dma_cmd = ms->tgts[ms->conn_tgt].data_goes_out?
+ OUTPUT_MORE: INPUT_MORE;
+ dcmds = ms->dma_cmds;
+ dtot = 0;
+ if (cmd) {
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ if (cmd->use_sg > 0) {
+ int nseg;
+ total = 0;
+ scl = (struct scatterlist *) cmd->buffer;
+ off = ms->data_ptr;
+ nseg = pci_map_sg(ms->pdev, scl, cmd->use_sg,
+ cmd->sc_data_direction);
+ for (i = 0; i <nseg; ++i, ++scl) {
+ u32 dma_addr = sg_dma_address(scl);
+ u32 dma_len = sg_dma_len(scl);
+
+ total += scl->length;
+ if (off >= dma_len) {
+ off -= dma_len;
+ continue;
+ }
+ if (dma_len > 0xffff)
+ panic("mesh: scatterlist element >= 64k");
+ st_le16(&dcmds->req_count, dma_len - off);
+ st_le16(&dcmds->command, dma_cmd);
+ st_le32(&dcmds->phy_addr, dma_addr + off);
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ dtot += dma_len - off;
+ off = 0;
+ }
+ } else if (ms->data_ptr < cmd->request_bufflen) {
+ dtot = cmd->request_bufflen - ms->data_ptr;
+ if (dtot > 0xffff)
+ panic("mesh: transfer size >= 64k");
+ st_le16(&dcmds->req_count, dtot);
+ /* XXX Use pci DMA API here ... */
+ st_le32(&dcmds->phy_addr,
+ virt_to_phys(cmd->request_buffer) + ms->data_ptr);
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ }
+ }
+ if (dtot == 0) {
+ /* Either the target has overrun our buffer,
+ or the caller didn't provide a buffer. */
+ static char mesh_extra_buf[64];
+
+ dtot = sizeof(mesh_extra_buf);
+ st_le16(&dcmds->req_count, dtot);
+ st_le32(&dcmds->phy_addr, virt_to_phys(mesh_extra_buf));
+ dcmds->xfer_status = 0;
+ ++dcmds;
+ }
+ dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
+ st_le16(&dcmds[-1].command, dma_cmd);
+ memset(dcmds, 0, sizeof(*dcmds));
+ st_le16(&dcmds->command, DBDMA_STOP);
+ ms->dma_count = dtot;
+}
+
+static void halt_dma(struct mesh_state *ms)
+{
+ volatile struct dbdma_regs __iomem *md = ms->dma;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ struct scsi_cmnd *cmd = ms->current_req;
+ int t, nb;
+
+ if (!ms->tgts[ms->conn_tgt].data_goes_out) {
+ /* wait a little while until the fifo drains */
+ t = 50;
+ while (t > 0 && in_8(&mr->fifo_count) != 0
+ && (in_le32(&md->status) & ACTIVE) != 0) {
+ --t;
+ udelay(1);
+ }
+ }
+ out_le32(&md->control, RUN << 16); /* turn off RUN bit */
+ nb = (mr->count_hi << 8) + mr->count_lo;
+ dlog(ms, "halt_dma fc/count=%.6x",
+ MKWORD(0, mr->fifo_count, 0, nb));
+ if (ms->tgts[ms->conn_tgt].data_goes_out)
+ nb += mr->fifo_count;
+ /* nb is the number of bytes not yet transferred
+ to/from the target. */
+ ms->data_ptr -= nb;
+ dlog(ms, "data_ptr %x", ms->data_ptr);
+ if (ms->data_ptr < 0) {
+ printk(KERN_ERR "mesh: halt_dma: data_ptr=%d (nb=%d, ms=%p)\n",
+ ms->data_ptr, nb, ms);
+ ms->data_ptr = 0;
+#ifdef MESH_DBG
+ dumplog(ms, ms->conn_tgt);
+ dumpslog(ms);
+#endif /* MESH_DBG */
+ } else if (cmd && cmd->request_bufflen != 0 &&
+ ms->data_ptr > cmd->request_bufflen) {
+ printk(KERN_DEBUG "mesh: target %d overrun, "
+ "data_ptr=%x total=%x goes_out=%d\n",
+ ms->conn_tgt, ms->data_ptr, cmd->request_bufflen,
+ ms->tgts[ms->conn_tgt].data_goes_out);
+ }
+ if (cmd->use_sg != 0) {
+ struct scatterlist *sg;
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(ms->pdev, sg, cmd->use_sg, cmd->sc_data_direction);
+ }
+ ms->dma_started = 0;
+}
+
+static void phase_mismatch(struct mesh_state *ms)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ int phase;
+
+ dlog(ms, "phasemm ch/cl/seq/fc=%.8x",
+ MKWORD(mr->count_hi, mr->count_lo, mr->sequence, mr->fifo_count));
+ phase = in_8(&mr->bus_status0) & BS0_PHASE;
+ if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {
+ /* output the last byte of the message, without ATN */
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
+ ms->msgphase = msg_out_last;
+ return;
+ }
+
+ if (ms->msgphase == msg_in) {
+ get_msgin(ms);
+ if (ms->n_msgin)
+ handle_msgin(ms);
+ }
+
+ if (ms->dma_started)
+ halt_dma(ms);
+ if (mr->fifo_count) {
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ }
+
+ ms->msgphase = msg_none;
+ switch (phase) {
+ case BP_DATAIN:
+ ms->tgts[ms->conn_tgt].data_goes_out = 0;
+ ms->phase = dataing;
+ break;
+ case BP_DATAOUT:
+ ms->tgts[ms->conn_tgt].data_goes_out = 1;
+ ms->phase = dataing;
+ break;
+ case BP_COMMAND:
+ ms->phase = commanding;
+ break;
+ case BP_STATUS:
+ ms->phase = statusing;
+ break;
+ case BP_MSGIN:
+ ms->msgphase = msg_in;
+ ms->n_msgin = 0;
+ break;
+ case BP_MSGOUT:
+ ms->msgphase = msg_out;
+ if (ms->n_msgout == 0) {
+ if (ms->aborting) {
+ do_abort(ms);
+ } else {
+ if (ms->last_n_msgout == 0) {
+ printk(KERN_DEBUG
+ "mesh: no msg to repeat\n");
+ ms->msgout[0] = NOP;
+ ms->last_n_msgout = 1;
+ }
+ ms->n_msgout = ms->last_n_msgout;
+ }
+ }
+ break;
+ default:
+ printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);
+ ms->stat = DID_ERROR;
+ mesh_done(ms, 1);
+ return;
+ }
+
+ start_phase(ms);
+}
+
+static void cmd_complete(struct mesh_state *ms)
+{
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ struct scsi_cmnd *cmd = ms->current_req;
+ struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
+ int seq, n, t;
+
+ dlog(ms, "cmd_complete fc=%x", mr->fifo_count);
+ seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
+ switch (ms->msgphase) {
+ case msg_out_xxx:
+ /* huh? we expected a phase mismatch */
+ ms->n_msgin = 0;
+ ms->msgphase = msg_in;
+ /* fall through */
+
+ case msg_in:
+ /* should have some message bytes in fifo */
+ get_msgin(ms);
+ n = msgin_length(ms);
+ if (ms->n_msgin < n) {
+ out_8(&mr->count_lo, n - ms->n_msgin);
+ out_8(&mr->sequence, SEQ_MSGIN + seq);
+ } else {
+ ms->msgphase = msg_none;
+ handle_msgin(ms);
+ start_phase(ms);
+ }
+ break;
+
+ case msg_in_bad:
+ out_8(&mr->sequence, SEQ_FLUSHFIFO);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGIN + SEQ_ATN + use_active_neg);
+ break;
+
+ case msg_out:
+ /*
+ * To get the right timing on ATN wrt ACK, we have
+ * to get the MESH to drop ACK, wait until REQ gets
+ * asserted, then drop ATN. To do this we first
+ * issue a SEQ_MSGOUT with ATN and wait for REQ,
+ * then change the command to a SEQ_MSGOUT w/o ATN.
+ * If we don't see REQ in a reasonable time, we
+ * change the command to SEQ_MSGIN with ATN,
+ * wait for the phase mismatch interrupt, then
+ * issue the SEQ_MSGOUT without ATN.
+ */
+ out_8(&mr->count_lo, 1);
+ out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);
+ t = 30; /* wait up to 30us */
+ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0 && --t >= 0)
+ udelay(1);
+ dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",
+ MKWORD(mr->error, mr->exception,
+ mr->fifo_count, mr->count_lo));
+ if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) {
+ /* whoops, target didn't do what we expected */
+ ms->last_n_msgout = ms->n_msgout;
+ ms->n_msgout = 0;
+ if (in_8(&mr->interrupt) & INT_ERROR) {
+ printk(KERN_ERR "mesh: error %x in msg_out\n",
+ in_8(&mr->error));
+ handle_error(ms);
+ return;
+ }
+ if (in_8(&mr->exception) != EXC_PHASEMM)
+ printk(KERN_ERR "mesh: exc %x in msg_out\n",
+ in_8(&mr->exception));
+ else
+ printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",
+ in_8(&mr->bus_status0));
+ handle_exception(ms);
+ return;
+ }
+ if (in_8(&mr->bus_status0) & BS0_REQ) {
+ out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
+ ms->msgphase = msg_out_last;
+ } else {
+ out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);
+ ms->msgphase = msg_out_xxx;
+ }
+ break;
+
+ case msg_out_last:
+ ms->last_n_msgout = ms->n_msgout;
+ ms->n_msgout = 0;
+ ms->msgphase = ms->expect_reply? msg_in: msg_none;
+ start_phase(ms);
+ break;
+
+ case msg_none:
+ switch (ms->phase) {
+ case idle:
+ printk(KERN_ERR "mesh: interrupt in idle phase?\n");
+ dumpslog(ms);
+ return;
+ case selecting:
+ dlog(ms, "Selecting phase at command completion",0);
+ ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),
+ (cmd? cmd->device->lun: 0));
+ ms->n_msgout = 1;
+ ms->expect_reply = 0;
+ if (ms->aborting) {
+ ms->msgout[0] = ABORT;
+ ms->n_msgout++;
+ } else if (tp->sdtr_state == do_sdtr) {
+ /* add SDTR message */
+ add_sdtr_msg(ms);
+ ms->expect_reply = 1;
+ tp->sdtr_state = sdtr_sent;
+ }
+ ms->msgphase = msg_out;
+ /*
+ * We need to wait for REQ before dropping ATN.
+ * We wait for at most 30us, then fall back to
+ * a scheme where we issue a SEQ_COMMAND with ATN,
+ * which will give us a phase mismatch interrupt
+ * when REQ does come, and then we send the message.
+ */
+ t = 230; /* wait up to 230us */
+ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0) {
+ if (--t < 0) {
+ dlog(ms, "impatient for req", ms->n_msgout);
+ ms->msgphase = msg_none;
+ break;
+ }
+ udelay(1);
+ }
+ break;
+ case dataing:
+ if (ms->dma_count != 0) {
+ start_phase(ms);
+ return;
+ }
+ /*
+ * We can get a phase mismatch here if the target
+ * changes to the status phase, even though we have
+ * had a command complete interrupt. Then, if we
+ * issue the SEQ_STATUS command, we'll get a sequence
+ * error interrupt. Which isn't so bad except that
+ * occasionally the mesh actually executes the
+ * SEQ_STATUS *as well as* giving us the sequence
+ * error and phase mismatch exception.
+ */
+ out_8(&mr->sequence, 0);
+ out_8(&mr->interrupt,
+ INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ halt_dma(ms);
+ break;
+ case statusing:
+ if (cmd) {
+ cmd->SCp.Status = mr->fifo;
+ if (DEBUG_TARGET(cmd))
+ printk(KERN_DEBUG "mesh: status is %x\n",
+ cmd->SCp.Status);
+ }
+ ms->msgphase = msg_in;
+ break;
+ case busfreeing:
+ mesh_done(ms, 1);
+ return;
+ case disconnecting:
+ ms->current_req = NULL;
+ ms->phase = idle;
+ mesh_start(ms);
+ return;
+ default:
+ break;
+ }
+ ++ms->phase;
+ start_phase(ms);
+ break;
+ }
+}
+
+
+/*
+ * Called by midlayer with host locked to queue a new
+ * request
+ */
+static int mesh_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct mesh_state *ms;
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+
+ ms = (struct mesh_state *) cmd->device->host->hostdata;
+
+ if (ms->request_q == NULL)
+ ms->request_q = cmd;
+ else
+ ms->request_qtail->host_scribble = (void *) cmd;
+ ms->request_qtail = cmd;
+
+ if (ms->phase == idle)
+ mesh_start(ms);
+
+ return 0;
+}
+
+/*
+ * Called to handle interrupts, either call by the interrupt
+ * handler (do_mesh_interrupt) or by other functions in
+ * exceptional circumstances
+ */
+static void mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+ struct mesh_state *ms = (struct mesh_state *) dev_id;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ int intr;
+
+#if 0
+ if (ALLOW_DEBUG(ms->conn_tgt))
+ printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x "
+ "phase=%d msgphase=%d\n", mr->bus_status0,
+ mr->interrupt, mr->exception, mr->error,
+ ms->phase, ms->msgphase);
+#endif
+ while ((intr = in_8(&mr->interrupt)) != 0) {
+ dlog(ms, "interrupt intr/err/exc/seq=%.8x",
+ MKWORD(intr, mr->error, mr->exception, mr->sequence));
+ if (intr & INT_ERROR) {
+ handle_error(ms);
+ } else if (intr & INT_EXCEPTION) {
+ handle_exception(ms);
+ } else if (intr & INT_CMDDONE) {
+ out_8(&mr->interrupt, INT_CMDDONE);
+ cmd_complete(ms);
+ }
+ }
+}
+
+/* Todo: here we can at least try to remove the command from the
+ * queue if it isn't connected yet, and for pending command, assert
+ * ATN until the bus gets freed.
+ */
+static int mesh_abort(struct scsi_cmnd *cmd)
+{
+ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
+
+ printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
+ mesh_dump_regs(ms);
+ dumplog(ms, cmd->device->id);
+ dumpslog(ms);
+ return FAILED;
+}
+
+/*
+ * Called by the midlayer with the lock held to reset the
+ * SCSI host and bus.
+ * The midlayer will wait for devices to come back, we don't need
+ * to do that ourselves
+ */
+static int mesh_host_reset(struct scsi_cmnd *cmd)
+{
+ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
+ volatile struct mesh_regs __iomem *mr = ms->mesh;
+ volatile struct dbdma_regs __iomem *md = ms->dma;
+
+ printk(KERN_DEBUG "mesh_host_reset\n");
+
+ /* Reset the controller & dbdma channel */
+ out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
+ out_8(&mr->exception, 0xff); /* clear all exception bits */
+ out_8(&mr->error, 0xff); /* clear all error bits */
+ out_8(&mr->sequence, SEQ_RESETMESH);
+ mesh_flush_io(mr);
+ udelay(1);
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->source_id, ms->host->this_id);
+ out_8(&mr->sel_timeout, 25); /* 250ms */
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+
+ /* Reset the bus */
+ out_8(&mr->bus_status1, BS1_RST); /* assert RST */
+ mesh_flush_io(mr);
+ udelay(30); /* leave it on for >= 25us */
+ out_8(&mr->bus_status1, 0); /* negate RST */
+
+ /* Complete pending commands */
+ handle_reset(ms);
+
+ return SUCCESS;
+}
+
+static void set_mesh_power(struct mesh_state *ms, int state)
+{
+ if (_machine != _MACH_Pmac)
+ return;
+ if (state) {
+ pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1);
+ msleep(200);
+ } else {
+ pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
+ msleep(10);
+ }
+}
+
+
+#ifdef CONFIG_PM
+static int mesh_suspend(struct macio_dev *mdev, u32 state)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ unsigned long flags;
+
+ if (state == mdev->ofdev.dev.power.power_state || state < 2)
+ return 0;
+
+ scsi_block_requests(ms->host);
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ while(ms->phase != idle) {
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+ msleep(10);
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ }
+ ms->phase = sleeping;
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+ disable_irq(ms->meshintr);
+ set_mesh_power(ms, 0);
+
+ mdev->ofdev.dev.power.power_state = state;
+
+ return 0;
+}
+
+static int mesh_resume(struct macio_dev *mdev)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ unsigned long flags;
+
+ if (mdev->ofdev.dev.power.power_state == 0)
+ return 0;
+
+ set_mesh_power(ms, 1);
+ mesh_init(ms);
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ mesh_start(ms);
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+ enable_irq(ms->meshintr);
+ scsi_unblock_requests(ms->host);
+
+ mdev->ofdev.dev.power.power_state = 0;
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/*
+ * If we leave drives set for synchronous transfers (especially
+ * CDROMs), and reboot to MacOS, it gets confused, poor thing.
+ * So, on reboot we reset the SCSI bus.
+ */
+static int mesh_shutdown(struct macio_dev *mdev)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ volatile struct mesh_regs __iomem *mr;
+ unsigned long flags;
+
+ printk(KERN_INFO "resetting MESH scsi bus(es)\n");
+ spin_lock_irqsave(ms->host->host_lock, flags);
+ mr = ms->mesh;
+ out_8(&mr->intr_mask, 0);
+ out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->bus_status1, BS1_RST);
+ mesh_flush_io(mr);
+ udelay(30);
+ out_8(&mr->bus_status1, 0);
+ spin_unlock_irqrestore(ms->host->host_lock, flags);
+
+ return 0;
+}
+
+static struct scsi_host_template mesh_template = {
+ .proc_name = "mesh",
+ .name = "MESH",
+ .queuecommand = mesh_queue,
+ .eh_abort_handler = mesh_abort,
+ .eh_host_reset_handler = mesh_host_reset,
+ .can_queue = 20,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 2,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+static int mesh_probe(struct macio_dev *mdev, const struct of_match *match)
+{
+ struct device_node *mesh = macio_get_of_node(mdev);
+ struct pci_dev* pdev = macio_get_pci_dev(mdev);
+ int tgt, *cfp, minper;
+ struct mesh_state *ms;
+ struct Scsi_Host *mesh_host;
+ void *dma_cmd_space;
+ dma_addr_t dma_cmd_bus;
+
+ switch (mdev->bus->chip->type) {
+ case macio_heathrow:
+ case macio_gatwick:
+ case macio_paddington:
+ use_active_neg = 0;
+ break;
+ default:
+ use_active_neg = SEQ_ACTIVE_NEG;
+ }
+
+ if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) {
+ printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
+ " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs);
+ return -ENODEV;
+ }
+
+ if (macio_request_resources(mdev, "mesh") != 0) {
+ printk(KERN_ERR "mesh: unable to request memory resources");
+ return -EBUSY;
+ }
+ mesh_host = scsi_host_alloc(&mesh_template, sizeof(struct mesh_state));
+ if (mesh_host == NULL) {
+ printk(KERN_ERR "mesh: couldn't register host");
+ goto out_release;
+ }
+
+ /* Old junk for root discovery, that will die ultimately */
+#if !defined(MODULE)
+ note_scsi_host(mesh, mesh_host);
+#endif
+
+ mesh_host->base = macio_resource_start(mdev, 0);
+ mesh_host->irq = macio_irq(mdev, 0);
+ ms = (struct mesh_state *) mesh_host->hostdata;
+ macio_set_drvdata(mdev, ms);
+ ms->host = mesh_host;
+ ms->mdev = mdev;
+ ms->pdev = pdev;
+
+ ms->mesh = ioremap(macio_resource_start(mdev, 0), 0x1000);
+ if (ms->mesh == NULL) {
+ printk(KERN_ERR "mesh: can't map registers\n");
+ goto out_free;
+ }
+ ms->dma = ioremap(macio_resource_start(mdev, 1), 0x1000);
+ if (ms->dma == NULL) {
+ printk(KERN_ERR "mesh: can't map registers\n");
+ iounmap(ms->mesh);
+ goto out_free;
+ }
+
+ ms->meshintr = macio_irq(mdev, 0);
+ ms->dmaintr = macio_irq(mdev, 1);
+
+ /* Space for dma command list: +1 for stop command,
+ * +1 to allow for aligning.
+ */
+ ms->dma_cmd_size = (mesh_host->sg_tablesize + 2) * sizeof(struct dbdma_cmd);
+
+ /* We use the PCI APIs for now until the generic one gets fixed
+ * enough or until we get some macio-specific versions
+ */
+ dma_cmd_space = pci_alloc_consistent(macio_get_pci_dev(mdev),
+ ms->dma_cmd_size,
+ &dma_cmd_bus);
+ if (dma_cmd_space == NULL) {
+ printk(KERN_ERR "mesh: can't allocate DMA table\n");
+ goto out_unmap;
+ }
+ memset(dma_cmd_space, 0, ms->dma_cmd_size);
+
+ ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
+ ms->dma_cmd_space = dma_cmd_space;
+ ms->dma_cmd_bus = dma_cmd_bus + ((unsigned long)ms->dma_cmds)
+ - (unsigned long)dma_cmd_space;
+ ms->current_req = NULL;
+ for (tgt = 0; tgt < 8; ++tgt) {
+ ms->tgts[tgt].sdtr_state = do_sdtr;
+ ms->tgts[tgt].sync_params = ASYNC_PARAMS;
+ ms->tgts[tgt].current_req = NULL;
+ }
+
+ if ((cfp = (int *) get_property(mesh, "clock-frequency", NULL)))
+ ms->clk_freq = *cfp;
+ else {
+ printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n");
+ ms->clk_freq = 50000000;
+ }
+
+ /* The maximum sync rate is clock / 5; increase
+ * mesh_sync_period if necessary.
+ */
+ minper = 1000000000 / (ms->clk_freq / 5); /* ns */
+ if (mesh_sync_period < minper)
+ mesh_sync_period = minper;
+
+ /* Power up the chip */
+ set_mesh_power(ms, 1);
+
+ /* Set it up */
+ mesh_init(ms);
+
+ /* XXX FIXME: error should be fatal */
+ if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms))
+ printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);
+
+ /* XXX FIXME: handle failure */
+ scsi_add_host(mesh_host, &mdev->ofdev.dev);
+ scsi_scan_host(mesh_host);
+
+ return 0;
+
+out_unmap:
+ iounmap(ms->dma);
+ iounmap(ms->mesh);
+out_free:
+ scsi_host_put(mesh_host);
+out_release:
+ macio_release_resources(mdev);
+
+ return -ENODEV;
+}
+
+static int mesh_remove(struct macio_dev *mdev)
+{
+ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
+ struct Scsi_Host *mesh_host = ms->host;
+
+ scsi_remove_host(mesh_host);
+
+ free_irq(ms->meshintr, ms);
+
+ /* Reset scsi bus */
+ mesh_shutdown(mdev);
+
+ /* Shut down chip & termination */
+ set_mesh_power(ms, 0);
+
+ /* Unmap registers & dma controller */
+ iounmap(ms->mesh);
+ iounmap(ms->dma);
+
+ /* Free DMA commands memory */
+ pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size,
+ ms->dma_cmd_space, ms->dma_cmd_bus);
+
+ /* Release memory resources */
+ macio_release_resources(mdev);
+
+ scsi_host_put(mesh_host);
+
+ return 0;
+}
+
+
+static struct of_match mesh_match[] =
+{
+ {
+ .name = "mesh",
+ .type = OF_ANY_MATCH,
+ .compatible = OF_ANY_MATCH
+ },
+ {
+ .name = OF_ANY_MATCH,
+ .type = "scsi",
+ .compatible = "chrp,mesh0"
+ },
+ {},
+};
+
+static struct macio_driver mesh_driver =
+{
+ .name = "mesh",
+ .match_table = mesh_match,
+ .probe = mesh_probe,
+ .remove = mesh_remove,
+ .shutdown = mesh_shutdown,
+#ifdef CONFIG_PM
+ .suspend = mesh_suspend,
+ .resume = mesh_resume,
+#endif
+};
+
+
+static int __init init_mesh(void)
+{
+
+ /* Calculate sync rate from module parameters */
+ if (sync_rate > 10)
+ sync_rate = 10;
+ if (sync_rate > 0) {
+ printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
+ mesh_sync_period = 1000 / sync_rate; /* ns */
+ mesh_sync_offset = 15;
+ } else
+ printk(KERN_INFO "mesh: configured for asynchronous\n");
+
+ return macio_register_driver(&mesh_driver);
+}
+
+static void __exit exit_mesh(void)
+{
+ return macio_unregister_driver(&mesh_driver);
+}
+
+module_init(init_mesh);
+module_exit(exit_mesh);
diff --git a/drivers/scsi/mesh.h b/drivers/scsi/mesh.h
new file mode 100644
index 000000000000..4fdb81fa55e2
--- /dev/null
+++ b/drivers/scsi/mesh.h
@@ -0,0 +1,127 @@
+/*
+ * mesh.h: definitions for the driver for the MESH SCSI bus adaptor
+ * (Macintosh Enhanced SCSI Hardware) found on Power Macintosh computers.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#ifndef _MESH_H
+#define _MESH_H
+
+/*
+ * Registers in the MESH controller.
+ */
+
+struct mesh_regs {
+ unsigned char count_lo;
+ char pad0[15];
+ unsigned char count_hi;
+ char pad1[15];
+ unsigned char fifo;
+ char pad2[15];
+ unsigned char sequence;
+ char pad3[15];
+ unsigned char bus_status0;
+ char pad4[15];
+ unsigned char bus_status1;
+ char pad5[15];
+ unsigned char fifo_count;
+ char pad6[15];
+ unsigned char exception;
+ char pad7[15];
+ unsigned char error;
+ char pad8[15];
+ unsigned char intr_mask;
+ char pad9[15];
+ unsigned char interrupt;
+ char pad10[15];
+ unsigned char source_id;
+ char pad11[15];
+ unsigned char dest_id;
+ char pad12[15];
+ unsigned char sync_params;
+ char pad13[15];
+ unsigned char mesh_id;
+ char pad14[15];
+ unsigned char sel_timeout;
+ char pad15[15];
+};
+
+/* Bits in the sequence register. */
+#define SEQ_DMA_MODE 0x80 /* use DMA for data transfer */
+#define SEQ_TARGET 0x40 /* put the controller into target mode */
+#define SEQ_ATN 0x20 /* assert ATN signal */
+#define SEQ_ACTIVE_NEG 0x10 /* use active negation on REQ/ACK */
+#define SEQ_CMD 0x0f /* command bits: */
+#define SEQ_ARBITRATE 1 /* get the bus */
+#define SEQ_SELECT 2 /* select a target */
+#define SEQ_COMMAND 3 /* send a command */
+#define SEQ_STATUS 4 /* receive status */
+#define SEQ_DATAOUT 5 /* send data */
+#define SEQ_DATAIN 6 /* receive data */
+#define SEQ_MSGOUT 7 /* send a message */
+#define SEQ_MSGIN 8 /* receive a message */
+#define SEQ_BUSFREE 9 /* look for bus free */
+#define SEQ_ENBPARITY 0x0a /* enable parity checking */
+#define SEQ_DISPARITY 0x0b /* disable parity checking */
+#define SEQ_ENBRESEL 0x0c /* enable reselection */
+#define SEQ_DISRESEL 0x0d /* disable reselection */
+#define SEQ_RESETMESH 0x0e /* reset the controller */
+#define SEQ_FLUSHFIFO 0x0f /* clear out the FIFO */
+
+/* Bits in the bus_status0 and bus_status1 registers:
+ these correspond directly to the SCSI bus control signals. */
+#define BS0_REQ 0x20
+#define BS0_ACK 0x10
+#define BS0_ATN 0x08
+#define BS0_MSG 0x04
+#define BS0_CD 0x02
+#define BS0_IO 0x01
+#define BS1_RST 0x80
+#define BS1_BSY 0x40
+#define BS1_SEL 0x20
+
+/* Bus phases defined by the bits in bus_status0 */
+#define BS0_PHASE (BS0_MSG+BS0_CD+BS0_IO)
+#define BP_DATAOUT 0
+#define BP_DATAIN BS0_IO
+#define BP_COMMAND BS0_CD
+#define BP_STATUS (BS0_CD+BS0_IO)
+#define BP_MSGOUT (BS0_MSG+BS0_CD)
+#define BP_MSGIN (BS0_MSG+BS0_CD+BS0_IO)
+
+/* Bits in the exception register. */
+#define EXC_SELWATN 0x20 /* (as target) we were selected with ATN */
+#define EXC_SELECTED 0x10 /* (as target) we were selected w/o ATN */
+#define EXC_RESELECTED 0x08 /* (as initiator) we were reselected */
+#define EXC_ARBLOST 0x04 /* we lost arbitration */
+#define EXC_PHASEMM 0x02 /* SCSI phase mismatch */
+#define EXC_SELTO 0x01 /* selection timeout */
+
+/* Bits in the error register */
+#define ERR_UNEXPDISC 0x40 /* target unexpectedly disconnected */
+#define ERR_SCSIRESET 0x20 /* SCSI bus got reset on us */
+#define ERR_SEQERR 0x10 /* we did something the chip didn't like */
+#define ERR_PARITY 0x01 /* parity error was detected */
+
+/* Bits in the interrupt and intr_mask registers */
+#define INT_ERROR 0x04 /* error interrupt */
+#define INT_EXCEPTION 0x02 /* exception interrupt */
+#define INT_CMDDONE 0x01 /* command done interrupt */
+
+/* Fields in the sync_params register */
+#define SYNC_OFF(x) ((x) >> 4) /* offset field */
+#define SYNC_PER(x) ((x) & 0xf) /* period field */
+#define SYNC_PARAMS(o, p) (((o) << 4) | (p))
+#define ASYNC_PARAMS 2 /* sync_params value for async xfers */
+
+/*
+ * Assuming a clock frequency of 50MHz:
+ *
+ * The transfer period with SYNC_PER(sync_params) == x
+ * is (x + 2) * 40ns, except that x == 0 gives 100ns.
+ *
+ * The units of the sel_timeout register are 10ms.
+ */
+
+
+#endif /* _MESH_H */
diff --git a/drivers/scsi/mvme147.c b/drivers/scsi/mvme147.c
new file mode 100644
index 000000000000..e73b33f293a0
--- /dev/null
+++ b/drivers/scsi/mvme147.c
@@ -0,0 +1,155 @@
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mvme147hw.h>
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "wd33c93.h"
+#include "mvme147.h"
+
+#include<linux/stat.h>
+
+#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata))
+
+static struct Scsi_Host *mvme147_host = NULL;
+
+static irqreturn_t mvme147_intr (int irq, void *dummy, struct pt_regs *fp)
+{
+ if (irq == MVME147_IRQ_SCSI_PORT)
+ wd33c93_intr (mvme147_host);
+ else
+ m147_pcc->dma_intr = 0x89; /* Ack and enable ints */
+ return IRQ_HANDLED;
+}
+
+static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
+{
+ unsigned char flags = 0x01;
+ unsigned long addr = virt_to_bus(cmd->SCp.ptr);
+
+ /* setup dma direction */
+ if (!dir_in)
+ flags |= 0x04;
+
+ /* remember direction */
+ HDATA(mvme147_host)->dma_dir = dir_in;
+
+ if (dir_in)
+ /* invalidate any cache */
+ cache_clear (addr, cmd->SCp.this_residual);
+ else
+ /* push any dirty cache */
+ cache_push (addr, cmd->SCp.this_residual);
+
+ /* start DMA */
+ m147_pcc->dma_bcr = cmd->SCp.this_residual | (1<<24);
+ m147_pcc->dma_dadr = addr;
+ m147_pcc->dma_cntrl = flags;
+
+ /* return success */
+ return 0;
+}
+
+static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt,
+ int status)
+{
+ m147_pcc->dma_cntrl = 0;
+}
+
+int mvme147_detect(Scsi_Host_Template *tpnt)
+{
+ static unsigned char called = 0;
+ wd33c93_regs regs;
+
+ if (!MACH_IS_MVME147 || called)
+ return 0;
+ called++;
+
+ tpnt->proc_name = "MVME147";
+ tpnt->proc_info = &wd33c93_proc_info;
+
+ mvme147_host = scsi_register (tpnt, sizeof(struct WD33C93_hostdata));
+ if (!mvme147_host)
+ goto err_out;
+
+ mvme147_host->base = 0xfffe4000;
+ mvme147_host->irq = MVME147_IRQ_SCSI_PORT;
+ regs.SASR = (volatile unsigned char *)0xfffe4000;
+ regs.SCMD = (volatile unsigned char *)0xfffe4001;
+ wd33c93_init(mvme147_host, regs, dma_setup, dma_stop, WD33C93_FS_8_10);
+
+ if (request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0, "MVME147 SCSI PORT", mvme147_intr))
+ goto err_unregister;
+ if (request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0, "MVME147 SCSI DMA", mvme147_intr))
+ goto err_free_irq;
+#if 0 /* Disabled; causes problems booting */
+ m147_pcc->scsi_interrupt = 0x10; /* Assert SCSI bus reset */
+ udelay(100);
+ m147_pcc->scsi_interrupt = 0x00; /* Negate SCSI bus reset */
+ udelay(2000);
+ m147_pcc->scsi_interrupt = 0x40; /* Clear bus reset interrupt */
+#endif
+ m147_pcc->scsi_interrupt = 0x09; /* Enable interrupt */
+
+ m147_pcc->dma_cntrl = 0x00; /* ensure DMA is stopped */
+ m147_pcc->dma_intr = 0x89; /* Ack and enable ints */
+
+ return 1;
+
+ err_free_irq:
+ free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr);
+ err_unregister:
+ wd33c93_release();
+ scsi_unregister(mvme147_host);
+ err_out:
+ return 0;
+}
+
+static int mvme147_bus_reset(Scsi_Cmnd *cmd)
+{
+ /* FIXME perform bus-specific reset */
+ wd33c93_host_reset(cmd);
+ return SUCCESS;
+}
+
+#define HOSTS_C
+
+#include "mvme147.h"
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "MVME147",
+ .name = "MVME147 built-in SCSI",
+ .detect = mvme147_detect,
+ .release = mvme147_release,
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_bus_reset_handler = mvme147_bus_reset,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+int mvme147_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ /* XXX Make sure DMA is stopped! */
+ wd33c93_release();
+ free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr);
+ free_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr);
+#endif
+ return 1;
+}
diff --git a/drivers/scsi/mvme147.h b/drivers/scsi/mvme147.h
new file mode 100644
index 000000000000..d8903f096182
--- /dev/null
+++ b/drivers/scsi/mvme147.h
@@ -0,0 +1,28 @@
+#ifndef MVME147_H
+
+/* $Id: mvme147.h,v 1.4 1997/01/19 23:07:10 davem Exp $
+ *
+ * Header file for the MVME147 built-in SCSI controller for Linux
+ *
+ * Written and (C) 1993, Hamish Macdonald, see mvme147.c for more info
+ *
+ */
+
+#include <linux/types.h>
+
+int mvme147_detect(Scsi_Host_Template *);
+int mvme147_release(struct Scsi_Host *);
+const char *wd33c93_info(void);
+int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd33c93_abort(Scsi_Cmnd *);
+int wd33c93_reset(Scsi_Cmnd *, unsigned int);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#endif /* MVME147_H */
diff --git a/drivers/scsi/mvme16x.c b/drivers/scsi/mvme16x.c
new file mode 100644
index 000000000000..b2d8d8ea1604
--- /dev/null
+++ b/drivers/scsi/mvme16x.c
@@ -0,0 +1,80 @@
+/*
+ * Detection routine for the NCR53c710 based MVME16x SCSI Controllers for Linux.
+ *
+ * Based on work by Alan Hourihane
+ */
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mvme16xhw.h>
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "53c7xx.h"
+#include "mvme16x.h"
+
+#include<linux/stat.h>
+
+
+int mvme16x_scsi_detect(Scsi_Host_Template *tpnt)
+{
+ static unsigned char called = 0;
+ int clock;
+ long long options;
+
+ if (!MACH_IS_MVME16x)
+ return 0;
+ if (mvme16x_config & MVME16x_CONFIG_NO_SCSICHIP) {
+ printk ("SCSI detection disabled, SCSI chip not present\n");
+ return 0;
+ }
+ if (called)
+ return 0;
+
+ tpnt->proc_name = "MVME16x";
+
+ options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
+
+ clock = 66000000; /* 66MHz SCSI Clock */
+
+ ncr53c7xx_init(tpnt, 0, 710, (unsigned long)0xfff47000,
+ 0, MVME16x_IRQ_SCSI, DMA_NONE,
+ options, clock);
+ called = 1;
+ return 1;
+}
+
+static int mvme16x_scsi_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "MVME16x NCR53c710 SCSI",
+ .detect = mvme16x_scsi_detect,
+ .release = mvme16x_scsi_release,
+ .queuecommand = NCR53c7xx_queue_command,
+ .abort = NCR53c7xx_abort,
+ .reset = NCR53c7xx_reset,
+ .can_queue = 24,
+ .this_id = 7,
+ .sg_tablesize = 63,
+ .cmd_per_lun = 3,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/mvme16x.h b/drivers/scsi/mvme16x.h
new file mode 100644
index 000000000000..25173c891d3c
--- /dev/null
+++ b/drivers/scsi/mvme16x.h
@@ -0,0 +1,24 @@
+#ifndef MVME16x_SCSI_H
+#define MVME16x_SCSI_H
+
+#include <linux/types.h>
+
+int mvme16x_scsi_detect(Scsi_Host_Template *);
+const char *NCR53c7x0_info(void);
+int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int NCR53c7xx_abort(Scsi_Cmnd *);
+int NCR53c7x0_release (struct Scsi_Host *);
+int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int);
+void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 3
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 24
+#endif
+
+#include <scsi/scsicam.h>
+
+#endif /* MVME16x_SCSI_H */
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
new file mode 100644
index 000000000000..7ae13236788e
--- /dev/null
+++ b/drivers/scsi/ncr53c8xx.c
@@ -0,0 +1,7986 @@
+/******************************************************************************
+** Device driver for the PCI-SCSI NCR538XX controller family.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You 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.
+**
+**-----------------------------------------------------------------------------
+**
+** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver
+** and is currently maintained by
+**
+** Gerard Roudier <groudier@free.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+** And has been ported to NetBSD by
+** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
+**
+**-----------------------------------------------------------------------------
+**
+** Brief history
+**
+** December 10 1995 by Gerard Roudier:
+** Initial port to Linux.
+**
+** June 23 1996 by Gerard Roudier:
+** Support for 64 bits architectures (Alpha).
+**
+** November 30 1996 by Gerard Roudier:
+** Support for Fast-20 scsi.
+** Support for large DMA fifo and 128 dwords bursting.
+**
+** February 27 1997 by Gerard Roudier:
+** Support for Fast-40 scsi.
+** Support for on-Board RAM.
+**
+** May 3 1997 by Gerard Roudier:
+** Full support for scsi scripts instructions pre-fetching.
+**
+** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+** Support for NvRAM detection and reading.
+**
+** August 18 1997 by Cort <cort@cs.nmt.edu>:
+** Support for Power/PC (Big Endian).
+**
+** June 20 1998 by Gerard Roudier
+** Support for up to 64 tags per lun.
+** O(1) everywhere (C and SCRIPTS) for normal cases.
+** Low PCI traffic for command handling when on-chip RAM is present.
+** Aggressive SCSI SCRIPTS optimizations.
+**
+*******************************************************************************
+*/
+
+/*
+** Supported SCSI-II features:
+** Synchronous negotiation
+** Wide negotiation (depends on the NCR Chip)
+** Enable disconnection
+** Tagged command queuing
+** Parity checking
+** Etc...
+**
+** Supported NCR/SYMBIOS chips:
+** 53C720 (Wide, Fast SCSI-2, intfly problems)
+*/
+
+/* Name and version of the driver */
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx-3.4.3g"
+
+#define SCSI_NCR_DEBUG_FLAGS (0)
+
+/*==========================================================
+**
+** Include files
+**
+**==========================================================
+*/
+
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "ncr53c8xx.h"
+
+#define NAME53C "ncr53c"
+#define NAME53C8XX "ncr53c8xx"
+
+#include "sym53c8xx_comm.h"
+
+
+/*==========================================================
+**
+** The CCB done queue uses an array of CCB virtual
+** addresses. Empty entries are flagged using the bogus
+** virtual address 0xffffffff.
+**
+** Since PCI ensures that only aligned DWORDs are accessed
+** atomically, 64 bit little-endian architecture requires
+** to test the high order DWORD of the entry to determine
+** if it is empty or valid.
+**
+** BTW, I will make things differently as soon as I will
+** have a better idea, but this is simple and should work.
+**
+**==========================================================
+*/
+
+#define SCSI_NCR_CCB_DONE_SUPPORT
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+
+#define MAX_DONE 24
+#define CCB_DONE_EMPTY 0xffffffffUL
+
+/* All 32 bit architectures */
+#if BITS_PER_LONG == 32
+#define CCB_DONE_VALID(cp) (((u_long) cp) != CCB_DONE_EMPTY)
+
+/* All > 32 bit (64 bit) architectures regardless endian-ness */
+#else
+#define CCB_DONE_VALID(cp) \
+ ((((u_long) cp) & 0xffffffff00000000ul) && \
+ (((u_long) cp) & 0xfffffffful) != CCB_DONE_EMPTY)
+#endif
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+
+/*==========================================================
+**
+** Configuration and Debugging
+**
+**==========================================================
+*/
+
+/*
+** SCSI address of this device.
+** The boot routines should have set it.
+** If not, use this.
+*/
+
+#ifndef SCSI_NCR_MYADDR
+#define SCSI_NCR_MYADDR (7)
+#endif
+
+/*
+** The maximum number of tags per logic unit.
+** Used only for disk devices that support tags.
+*/
+
+#ifndef SCSI_NCR_MAX_TAGS
+#define SCSI_NCR_MAX_TAGS (8)
+#endif
+
+/*
+** TAGS are actually limited to 64 tags/lun.
+** We need to deal with power of 2, for alignment constraints.
+*/
+#if SCSI_NCR_MAX_TAGS > 64
+#define MAX_TAGS (64)
+#else
+#define MAX_TAGS SCSI_NCR_MAX_TAGS
+#endif
+
+#define NO_TAG (255)
+
+/*
+** Choose appropriate type for tag bitmap.
+*/
+#if MAX_TAGS > 32
+typedef u64 tagmap_t;
+#else
+typedef u32 tagmap_t;
+#endif
+
+/*
+** Number of targets supported by the driver.
+** n permits target numbers 0..n-1.
+** Default is 16, meaning targets #0..#15.
+** #7 .. is myself.
+*/
+
+#ifdef SCSI_NCR_MAX_TARGET
+#define MAX_TARGET (SCSI_NCR_MAX_TARGET)
+#else
+#define MAX_TARGET (16)
+#endif
+
+/*
+** Number of logic units supported by the driver.
+** n enables logic unit numbers 0..n-1.
+** The common SCSI devices require only
+** one lun, so take 1 as the default.
+*/
+
+#ifdef SCSI_NCR_MAX_LUN
+#define MAX_LUN SCSI_NCR_MAX_LUN
+#else
+#define MAX_LUN (1)
+#endif
+
+/*
+** Asynchronous pre-scaler (ns). Shall be 40
+*/
+
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
+
+/*
+** The maximum number of jobs scheduled for starting.
+** There should be one slot per target, and one slot
+** for each tag of each target in use.
+** The calculation below is actually quite silly ...
+*/
+
+#ifdef SCSI_NCR_CAN_QUEUE
+#define MAX_START (SCSI_NCR_CAN_QUEUE + 4)
+#else
+#define MAX_START (MAX_TARGET + 7 * MAX_TAGS)
+#endif
+
+/*
+** We limit the max number of pending IO to 250.
+** since we donnot want to allocate more than 1
+** PAGE for 'scripth'.
+*/
+#if MAX_START > 250
+#undef MAX_START
+#define MAX_START 250
+#endif
+
+/*
+** The maximum number of segments a transfer is split into.
+** We support up to 127 segments for both read and write.
+** The data scripts are broken into 2 sub-scripts.
+** 80 (MAX_SCATTERL) segments are moved from a sub-script
+** in on-chip RAM. This makes data transfers shorter than
+** 80k (assuming 1k fs) as fast as possible.
+*/
+
+#define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
+
+#if (MAX_SCATTER > 80)
+#define MAX_SCATTERL 80
+#define MAX_SCATTERH (MAX_SCATTER - MAX_SCATTERL)
+#else
+#define MAX_SCATTERL (MAX_SCATTER-1)
+#define MAX_SCATTERH 1
+#endif
+
+/*
+** other
+*/
+
+#define NCR_SNOOP_TIMEOUT (1000000)
+
+/*
+** Other definitions
+*/
+
+#define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+/*==========================================================
+**
+** Command control block states.
+**
+**==========================================================
+*/
+
+#define HS_IDLE (0)
+#define HS_BUSY (1)
+#define HS_NEGOTIATE (2) /* sync/wide data transfer*/
+#define HS_DISCONNECT (3) /* Disconnected by target */
+
+#define HS_DONEMASK (0x80)
+#define HS_COMPLETE (4|HS_DONEMASK)
+#define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */
+#define HS_RESET (6|HS_DONEMASK) /* SCSI reset */
+#define HS_ABORTED (7|HS_DONEMASK) /* Transfer aborted */
+#define HS_TIMEOUT (8|HS_DONEMASK) /* Software timeout */
+#define HS_FAIL (9|HS_DONEMASK) /* SCSI or PCI bus errors */
+#define HS_UNEXPECTED (10|HS_DONEMASK)/* Unexpected disconnect */
+
+/*
+** Invalid host status values used by the SCRIPTS processor
+** when the nexus is not fully identified.
+** Shall never appear in a CCB.
+*/
+
+#define HS_INVALMASK (0x40)
+#define HS_SELECTING (0|HS_INVALMASK)
+#define HS_IN_RESELECT (1|HS_INVALMASK)
+#define HS_STARTING (2|HS_INVALMASK)
+
+/*
+** Flags set by the SCRIPT processor for commands
+** that have been skipped.
+*/
+#define HS_SKIPMASK (0x20)
+
+/*==========================================================
+**
+** Software Interrupt Codes
+**
+**==========================================================
+*/
+
+#define SIR_BAD_STATUS (1)
+#define SIR_XXXXXXXXXX (2)
+#define SIR_NEGO_SYNC (3)
+#define SIR_NEGO_WIDE (4)
+#define SIR_NEGO_FAILED (5)
+#define SIR_NEGO_PROTO (6)
+#define SIR_REJECT_RECEIVED (7)
+#define SIR_REJECT_SENT (8)
+#define SIR_IGN_RESIDUE (9)
+#define SIR_MISSING_SAVE (10)
+#define SIR_RESEL_NO_MSG_IN (11)
+#define SIR_RESEL_NO_IDENTIFY (12)
+#define SIR_RESEL_BAD_LUN (13)
+#define SIR_RESEL_BAD_TARGET (14)
+#define SIR_RESEL_BAD_I_T_L (15)
+#define SIR_RESEL_BAD_I_T_L_Q (16)
+#define SIR_DONE_OVERFLOW (17)
+#define SIR_INTFLY (18)
+#define SIR_MAX (18)
+
+/*==========================================================
+**
+** Extended error codes.
+** xerr_status field of struct ccb.
+**
+**==========================================================
+*/
+
+#define XE_OK (0)
+#define XE_EXTRA_DATA (1) /* unexpected data phase */
+#define XE_BAD_PHASE (2) /* illegal phase (4/5) */
+
+/*==========================================================
+**
+** Negotiation status.
+** nego_status field of struct ccb.
+**
+**==========================================================
+*/
+
+#define NS_NOCHANGE (0)
+#define NS_SYNC (1)
+#define NS_WIDE (2)
+#define NS_PPR (4)
+
+/*==========================================================
+**
+** Misc.
+**
+**==========================================================
+*/
+
+#define CCB_MAGIC (0xf2691ad2)
+
+/*==========================================================
+**
+** Declaration of structs.
+**
+**==========================================================
+*/
+
+static struct scsi_transport_template *ncr53c8xx_transport_template = NULL;
+
+struct tcb;
+struct lcb;
+struct ccb;
+struct ncb;
+struct script;
+
+struct link {
+ ncrcmd l_cmd;
+ ncrcmd l_paddr;
+};
+
+struct usrcmd {
+ u_long target;
+ u_long lun;
+ u_long data;
+ u_long cmd;
+};
+
+#define UC_SETSYNC 10
+#define UC_SETTAGS 11
+#define UC_SETDEBUG 12
+#define UC_SETORDER 13
+#define UC_SETWIDE 14
+#define UC_SETFLAG 15
+#define UC_SETVERBOSE 17
+
+#define UF_TRACE (0x01)
+#define UF_NODISC (0x02)
+#define UF_NOSCAN (0x04)
+
+/*========================================================================
+**
+** Declaration of structs: target control block
+**
+**========================================================================
+*/
+struct tcb {
+ /*----------------------------------------------------------------
+ ** During reselection the ncr jumps to this point with SFBR
+ ** set to the encoded target number with bit 7 set.
+ ** if it's not this target, jump to the next.
+ **
+ ** JUMP IF (SFBR != #target#), @(next tcb)
+ **----------------------------------------------------------------
+ */
+ struct link jump_tcb;
+
+ /*----------------------------------------------------------------
+ ** Load the actual values for the sxfer and the scntl3
+ ** register (sync/wide mode).
+ **
+ ** SCR_COPY (1), @(sval field of this tcb), @(sxfer register)
+ ** SCR_COPY (1), @(wval field of this tcb), @(scntl3 register)
+ **----------------------------------------------------------------
+ */
+ ncrcmd getscr[6];
+
+ /*----------------------------------------------------------------
+ ** Get the IDENTIFY message and load the LUN to SFBR.
+ **
+ ** CALL, <RESEL_LUN>
+ **----------------------------------------------------------------
+ */
+ struct link call_lun;
+
+ /*----------------------------------------------------------------
+ ** Now look for the right lun.
+ **
+ ** For i = 0 to 3
+ ** SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(first lcb mod. i)
+ **
+ ** Recent chips will prefetch the 4 JUMPS using only 1 burst.
+ ** It is kind of hashcoding.
+ **----------------------------------------------------------------
+ */
+ struct link jump_lcb[4]; /* JUMPs for reselection */
+ struct lcb * lp[MAX_LUN]; /* The lcb's of this tcb */
+
+ /*----------------------------------------------------------------
+ ** Pointer to the ccb used for negotiation.
+ ** Prevent from starting a negotiation for all queued commands
+ ** when tagged command queuing is enabled.
+ **----------------------------------------------------------------
+ */
+ struct ccb * nego_cp;
+
+ /*----------------------------------------------------------------
+ ** statistical data
+ **----------------------------------------------------------------
+ */
+ u_long transfers;
+ u_long bytes;
+
+ /*----------------------------------------------------------------
+ ** negotiation of wide and synch transfer and device quirks.
+ **----------------------------------------------------------------
+ */
+#ifdef SCSI_NCR_BIG_ENDIAN
+/*0*/ u16 period;
+/*2*/ u_char sval;
+/*3*/ u_char minsync;
+/*0*/ u_char wval;
+/*1*/ u_char widedone;
+/*2*/ u_char quirks;
+/*3*/ u_char maxoffs;
+#else
+/*0*/ u_char minsync;
+/*1*/ u_char sval;
+/*2*/ u16 period;
+/*0*/ u_char maxoffs;
+/*1*/ u_char quirks;
+/*2*/ u_char widedone;
+/*3*/ u_char wval;
+#endif
+
+ /* User settable limits and options. */
+ u_char usrsync;
+ u_char usrwide;
+ u_char usrtags;
+ u_char usrflag;
+ struct scsi_target *starget;
+};
+
+/*========================================================================
+**
+** Declaration of structs: lun control block
+**
+**========================================================================
+*/
+struct lcb {
+ /*----------------------------------------------------------------
+ ** During reselection the ncr jumps to this point
+ ** with SFBR set to the "Identify" message.
+ ** if it's not this lun, jump to the next.
+ **
+ ** JUMP IF (SFBR != #lun#), @(next lcb of this target)
+ **
+ ** It is this lun. Load TEMP with the nexus jumps table
+ ** address and jump to RESEL_TAG (or RESEL_NOTAG).
+ **
+ ** SCR_COPY (4), p_jump_ccb, TEMP,
+ ** SCR_JUMP, <RESEL_TAG>
+ **----------------------------------------------------------------
+ */
+ struct link jump_lcb;
+ ncrcmd load_jump_ccb[3];
+ struct link jump_tag;
+ ncrcmd p_jump_ccb; /* Jump table bus address */
+
+ /*----------------------------------------------------------------
+ ** Jump table used by the script processor to directly jump
+ ** to the CCB corresponding to the reselected nexus.
+ ** Address is allocated on 256 bytes boundary in order to
+ ** allow 8 bit calculation of the tag jump entry for up to
+ ** 64 possible tags.
+ **----------------------------------------------------------------
+ */
+ u32 jump_ccb_0; /* Default table if no tags */
+ u32 *jump_ccb; /* Virtual address */
+
+ /*----------------------------------------------------------------
+ ** CCB queue management.
+ **----------------------------------------------------------------
+ */
+ struct list_head free_ccbq; /* Queue of available CCBs */
+ struct list_head busy_ccbq; /* Queue of busy CCBs */
+ struct list_head wait_ccbq; /* Queue of waiting for IO CCBs */
+ struct list_head skip_ccbq; /* Queue of skipped CCBs */
+ u_char actccbs; /* Number of allocated CCBs */
+ u_char busyccbs; /* CCBs busy for this lun */
+ u_char queuedccbs; /* CCBs queued to the controller*/
+ u_char queuedepth; /* Queue depth for this lun */
+ u_char scdev_depth; /* SCSI device queue depth */
+ u_char maxnxs; /* Max possible nexuses */
+
+ /*----------------------------------------------------------------
+ ** Control of tagged command queuing.
+ ** Tags allocation is performed using a circular buffer.
+ ** This avoids using a loop for tag allocation.
+ **----------------------------------------------------------------
+ */
+ u_char ia_tag; /* Allocation index */
+ u_char if_tag; /* Freeing index */
+ u_char cb_tags[MAX_TAGS]; /* Circular tags buffer */
+ u_char usetags; /* Command queuing is active */
+ u_char maxtags; /* Max nr of tags asked by user */
+ u_char numtags; /* Current number of tags */
+
+ /*----------------------------------------------------------------
+ ** QUEUE FULL control and ORDERED tag control.
+ **----------------------------------------------------------------
+ */
+ /*----------------------------------------------------------------
+ ** QUEUE FULL and ORDERED tag control.
+ **----------------------------------------------------------------
+ */
+ u16 num_good; /* Nr of GOOD since QUEUE FULL */
+ tagmap_t tags_umap; /* Used tags bitmap */
+ tagmap_t tags_smap; /* Tags in use at 'tag_stime' */
+ u_long tags_stime; /* Last time we set smap=umap */
+ struct ccb * held_ccb; /* CCB held for QUEUE FULL */
+};
+
+/*========================================================================
+**
+** Declaration of structs: the launch script.
+**
+**========================================================================
+**
+** It is part of the CCB and is called by the scripts processor to
+** start or restart the data structure (nexus).
+** This 6 DWORDs mini script makes use of prefetching.
+**
+**------------------------------------------------------------------------
+*/
+struct launch {
+ /*----------------------------------------------------------------
+ ** SCR_COPY(4), @(p_phys), @(dsa register)
+ ** SCR_JUMP, @(scheduler_point)
+ **----------------------------------------------------------------
+ */
+ ncrcmd setup_dsa[3]; /* Copy 'phys' address to dsa */
+ struct link schedule; /* Jump to scheduler point */
+ ncrcmd p_phys; /* 'phys' header bus address */
+};
+
+/*========================================================================
+**
+** Declaration of structs: global HEADER.
+**
+**========================================================================
+**
+** This substructure is copied from the ccb to a global address after
+** selection (or reselection) and copied back before disconnect.
+**
+** These fields are accessible to the script processor.
+**
+**------------------------------------------------------------------------
+*/
+
+struct head {
+ /*----------------------------------------------------------------
+ ** Saved data pointer.
+ ** Points to the position in the script responsible for the
+ ** actual transfer transfer of data.
+ ** It's written after reception of a SAVE_DATA_POINTER message.
+ ** The goalpointer points after the last transfer command.
+ **----------------------------------------------------------------
+ */
+ u32 savep;
+ u32 lastp;
+ u32 goalp;
+
+ /*----------------------------------------------------------------
+ ** Alternate data pointer.
+ ** They are copied back to savep/lastp/goalp by the SCRIPTS
+ ** when the direction is unknown and the device claims data out.
+ **----------------------------------------------------------------
+ */
+ u32 wlastp;
+ u32 wgoalp;
+
+ /*----------------------------------------------------------------
+ ** The virtual address of the ccb containing this header.
+ **----------------------------------------------------------------
+ */
+ struct ccb * cp;
+
+ /*----------------------------------------------------------------
+ ** Status fields.
+ **----------------------------------------------------------------
+ */
+ u_char scr_st[4]; /* script status */
+ u_char status[4]; /* host status. must be the */
+ /* last DWORD of the header. */
+};
+
+/*
+** The status bytes are used by the host and the script processor.
+**
+** The byte corresponding to the host_status must be stored in the
+** last DWORD of the CCB header since it is used for command
+** completion (ncr_wakeup()). Doing so, we are sure that the header
+** has been entirely copied back to the CCB when the host_status is
+** seen complete by the CPU.
+**
+** The last four bytes (status[4]) are copied to the scratchb register
+** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect,
+** and copied back just after disconnecting.
+** Inside the script the XX_REG are used.
+**
+** The first four bytes (scr_st[4]) are used inside the script by
+** "COPY" commands.
+** Because source and destination must have the same alignment
+** in a DWORD, the fields HAVE to be at the choosen offsets.
+** xerr_st 0 (0x34) scratcha
+** sync_st 1 (0x05) sxfer
+** wide_st 3 (0x03) scntl3
+*/
+
+/*
+** Last four bytes (script)
+*/
+#define QU_REG scr0
+#define HS_REG scr1
+#define HS_PRT nc_scr1
+#define SS_REG scr2
+#define SS_PRT nc_scr2
+#define PS_REG scr3
+
+/*
+** Last four bytes (host)
+*/
+#ifdef SCSI_NCR_BIG_ENDIAN
+#define actualquirks phys.header.status[3]
+#define host_status phys.header.status[2]
+#define scsi_status phys.header.status[1]
+#define parity_status phys.header.status[0]
+#else
+#define actualquirks phys.header.status[0]
+#define host_status phys.header.status[1]
+#define scsi_status phys.header.status[2]
+#define parity_status phys.header.status[3]
+#endif
+
+/*
+** First four bytes (script)
+*/
+#define xerr_st header.scr_st[0]
+#define sync_st header.scr_st[1]
+#define nego_st header.scr_st[2]
+#define wide_st header.scr_st[3]
+
+/*
+** First four bytes (host)
+*/
+#define xerr_status phys.xerr_st
+#define nego_status phys.nego_st
+
+#if 0
+#define sync_status phys.sync_st
+#define wide_status phys.wide_st
+#endif
+
+/*==========================================================
+**
+** Declaration of structs: Data structure block
+**
+**==========================================================
+**
+** During execution of a ccb by the script processor,
+** the DSA (data structure address) register points
+** to this substructure of the ccb.
+** This substructure contains the header with
+** the script-processor-changable data and
+** data blocks for the indirect move commands.
+**
+**----------------------------------------------------------
+*/
+
+struct dsb {
+
+ /*
+ ** Header.
+ */
+
+ struct head header;
+
+ /*
+ ** Table data for Script
+ */
+
+ struct scr_tblsel select;
+ struct scr_tblmove smsg ;
+ struct scr_tblmove cmd ;
+ struct scr_tblmove sense ;
+ struct scr_tblmove data[MAX_SCATTER];
+};
+
+
+/*========================================================================
+**
+** Declaration of structs: Command control block.
+**
+**========================================================================
+*/
+struct ccb {
+ /*----------------------------------------------------------------
+ ** This is the data structure which is pointed by the DSA
+ ** register when it is executed by the script processor.
+ ** It must be the first entry because it contains the header
+ ** as first entry that must be cache line aligned.
+ **----------------------------------------------------------------
+ */
+ struct dsb phys;
+
+ /*----------------------------------------------------------------
+ ** Mini-script used at CCB execution start-up.
+ ** Load the DSA with the data structure address (phys) and
+ ** jump to SELECT. Jump to CANCEL if CCB is to be canceled.
+ **----------------------------------------------------------------
+ */
+ struct launch start;
+
+ /*----------------------------------------------------------------
+ ** Mini-script used at CCB relection to restart the nexus.
+ ** Load the DSA with the data structure address (phys) and
+ ** jump to RESEL_DSA. Jump to ABORT if CCB is to be aborted.
+ **----------------------------------------------------------------
+ */
+ struct launch restart;
+
+ /*----------------------------------------------------------------
+ ** If a data transfer phase is terminated too early
+ ** (after reception of a message (i.e. DISCONNECT)),
+ ** we have to prepare a mini script to transfer
+ ** the rest of the data.
+ **----------------------------------------------------------------
+ */
+ ncrcmd patch[8];
+
+ /*----------------------------------------------------------------
+ ** The general SCSI driver provides a
+ ** pointer to a control block.
+ **----------------------------------------------------------------
+ */
+ struct scsi_cmnd *cmd; /* SCSI command */
+ u_char cdb_buf[16]; /* Copy of CDB */
+ u_char sense_buf[64];
+ int data_len; /* Total data length */
+
+ /*----------------------------------------------------------------
+ ** Message areas.
+ ** We prepare a message to be sent after selection.
+ ** We may use a second one if the command is rescheduled
+ ** due to GETCC or QFULL.
+ ** Contents are IDENTIFY and SIMPLE_TAG.
+ ** While negotiating sync or wide transfer,
+ ** a SDTR or WDTR message is appended.
+ **----------------------------------------------------------------
+ */
+ u_char scsi_smsg [8];
+ u_char scsi_smsg2[8];
+
+ /*----------------------------------------------------------------
+ ** Other fields.
+ **----------------------------------------------------------------
+ */
+ u_long p_ccb; /* BUS address of this CCB */
+ u_char sensecmd[6]; /* Sense command */
+ u_char tag; /* Tag for this transfer */
+ /* 255 means no tag */
+ u_char target;
+ u_char lun;
+ u_char queued;
+ u_char auto_sense;
+ struct ccb * link_ccb; /* Host adapter CCB chain */
+ struct list_head link_ccbq; /* Link to unit CCB queue */
+ u32 startp; /* Initial data pointer */
+ u_long magic; /* Free / busy CCB flag */
+};
+
+#define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl))
+
+
+/*========================================================================
+**
+** Declaration of structs: NCR device descriptor
+**
+**========================================================================
+*/
+struct ncb {
+ /*----------------------------------------------------------------
+ ** The global header.
+ ** It is accessible to both the host and the script processor.
+ ** Must be cache line size aligned (32 for x86) in order to
+ ** allow cache line bursting when it is copied to/from CCB.
+ **----------------------------------------------------------------
+ */
+ struct head header;
+
+ /*----------------------------------------------------------------
+ ** CCBs management queues.
+ **----------------------------------------------------------------
+ */
+ struct scsi_cmnd *waiting_list; /* Commands waiting for a CCB */
+ /* when lcb is not allocated. */
+ struct scsi_cmnd *done_list; /* Commands waiting for done() */
+ /* callback to be invoked. */
+ spinlock_t smp_lock; /* Lock for SMP threading */
+
+ /*----------------------------------------------------------------
+ ** Chip and controller indentification.
+ **----------------------------------------------------------------
+ */
+ int unit; /* Unit number */
+ char inst_name[16]; /* ncb instance name */
+
+ /*----------------------------------------------------------------
+ ** Initial value of some IO register bits.
+ ** These values are assumed to have been set by BIOS, and may
+ ** be used for probing adapter implementation differences.
+ **----------------------------------------------------------------
+ */
+ u_char sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest0, sv_ctest3,
+ sv_ctest4, sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4;
+
+ /*----------------------------------------------------------------
+ ** Actual initial value of IO register bits used by the
+ ** driver. They are loaded at initialisation according to
+ ** features that are to be enabled.
+ **----------------------------------------------------------------
+ */
+ u_char rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest0, rv_ctest3,
+ rv_ctest4, rv_ctest5, rv_stest2;
+
+ /*----------------------------------------------------------------
+ ** Targets management.
+ ** During reselection the ncr jumps to jump_tcb.
+ ** The SFBR register is loaded with the encoded target id.
+ ** For i = 0 to 3
+ ** SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(next tcb mod. i)
+ **
+ ** Recent chips will prefetch the 4 JUMPS using only 1 burst.
+ ** It is kind of hashcoding.
+ **----------------------------------------------------------------
+ */
+ struct link jump_tcb[4]; /* JUMPs for reselection */
+ struct tcb target[MAX_TARGET]; /* Target data */
+
+ /*----------------------------------------------------------------
+ ** Virtual and physical bus addresses of the chip.
+ **----------------------------------------------------------------
+ */
+ void __iomem *vaddr; /* Virtual and bus address of */
+ unsigned long paddr; /* chip's IO registers. */
+ unsigned long paddr2; /* On-chip RAM bus address. */
+ volatile /* Pointer to volatile for */
+ struct ncr_reg __iomem *reg; /* memory mapped IO. */
+
+ /*----------------------------------------------------------------
+ ** SCRIPTS virtual and physical bus addresses.
+ ** 'script' is loaded in the on-chip RAM if present.
+ ** 'scripth' stays in main memory.
+ **----------------------------------------------------------------
+ */
+ struct script *script0; /* Copies of script and scripth */
+ struct scripth *scripth0; /* relocated for this ncb. */
+ struct scripth *scripth; /* Actual scripth virt. address */
+ u_long p_script; /* Actual script and scripth */
+ u_long p_scripth; /* bus addresses. */
+
+ /*----------------------------------------------------------------
+ ** General controller parameters and configuration.
+ **----------------------------------------------------------------
+ */
+ struct device *dev;
+ u_char revision_id; /* PCI device revision id */
+ u32 irq; /* IRQ level */
+ u32 features; /* Chip features map */
+ u_char myaddr; /* SCSI id of the adapter */
+ u_char maxburst; /* log base 2 of dwords burst */
+ u_char maxwide; /* Maximum transfer width */
+ u_char minsync; /* Minimum sync period factor */
+ u_char maxsync; /* Maximum sync period factor */
+ u_char maxoffs; /* Max scsi offset */
+ u_char multiplier; /* Clock multiplier (1,2,4) */
+ u_char clock_divn; /* Number of clock divisors */
+ u_long clock_khz; /* SCSI clock frequency in KHz */
+
+ /*----------------------------------------------------------------
+ ** Start queue management.
+ ** It is filled up by the host processor and accessed by the
+ ** SCRIPTS processor in order to start SCSI commands.
+ **----------------------------------------------------------------
+ */
+ u16 squeueput; /* Next free slot of the queue */
+ u16 actccbs; /* Number of allocated CCBs */
+ u16 queuedccbs; /* Number of CCBs in start queue*/
+ u16 queuedepth; /* Start queue depth */
+
+ /*----------------------------------------------------------------
+ ** Timeout handler.
+ **----------------------------------------------------------------
+ */
+ struct timer_list timer; /* Timer handler link header */
+ u_long lasttime;
+ u_long settle_time; /* Resetting the SCSI BUS */
+
+ /*----------------------------------------------------------------
+ ** Debugging and profiling.
+ **----------------------------------------------------------------
+ */
+ struct ncr_reg regdump; /* Register dump */
+ u_long regtime; /* Time it has been done */
+
+ /*----------------------------------------------------------------
+ ** Miscellaneous buffers accessed by the scripts-processor.
+ ** They shall be DWORD aligned, because they may be read or
+ ** written with a SCR_COPY script command.
+ **----------------------------------------------------------------
+ */
+ u_char msgout[8]; /* Buffer for MESSAGE OUT */
+ u_char msgin [8]; /* Buffer for MESSAGE IN */
+ u32 lastmsg; /* Last SCSI message sent */
+ u_char scratch; /* Scratch for SCSI receive */
+
+ /*----------------------------------------------------------------
+ ** Miscellaneous configuration and status parameters.
+ **----------------------------------------------------------------
+ */
+ u_char disc; /* Diconnection allowed */
+ u_char scsi_mode; /* Current SCSI BUS mode */
+ u_char order; /* Tag order to use */
+ u_char verbose; /* Verbosity for this controller*/
+ int ncr_cache; /* Used for cache test at init. */
+ u_long p_ncb; /* BUS address of this NCB */
+
+ /*----------------------------------------------------------------
+ ** Command completion handling.
+ **----------------------------------------------------------------
+ */
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+ struct ccb *(ccb_done[MAX_DONE]);
+ int ccb_done_ic;
+#endif
+ /*----------------------------------------------------------------
+ ** Fields that should be removed or changed.
+ **----------------------------------------------------------------
+ */
+ struct ccb *ccb; /* Global CCB */
+ struct usrcmd user; /* Command from user */
+ volatile u_char release_stage; /* Synchronisation stage on release */
+};
+
+#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl))
+#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl))
+
+/*==========================================================
+**
+**
+** Script for NCR-Processor.
+**
+** Use ncr_script_fill() to create the variable parts.
+** Use ncr_script_copy_and_bind() to make a copy and
+** bind to physical addresses.
+**
+**
+**==========================================================
+**
+** We have to know the offsets of all labels before
+** we reach them (for forward jumps).
+** Therefore we declare a struct here.
+** If you make changes inside the script,
+** DONT FORGET TO CHANGE THE LENGTHS HERE!
+**
+**----------------------------------------------------------
+*/
+
+/*
+** For HP Zalon/53c720 systems, the Zalon interface
+** between CPU and 53c720 does prefetches, which causes
+** problems with self modifying scripts. The problem
+** is overcome by calling a dummy subroutine after each
+** modification, to force a refetch of the script on
+** return from the subroutine.
+*/
+
+#ifdef CONFIG_NCR53C8XX_PREFETCH
+#define PREFETCH_FLUSH_CNT 2
+#define PREFETCH_FLUSH SCR_CALL, PADDRH (wait_dma),
+#else
+#define PREFETCH_FLUSH_CNT 0
+#define PREFETCH_FLUSH
+#endif
+
+/*
+** Script fragments which are loaded into the on-chip RAM
+** of 825A, 875 and 895 chips.
+*/
+struct script {
+ ncrcmd start [ 5];
+ ncrcmd startpos [ 1];
+ ncrcmd select [ 6];
+ ncrcmd select2 [ 9 + PREFETCH_FLUSH_CNT];
+ ncrcmd loadpos [ 4];
+ ncrcmd send_ident [ 9];
+ ncrcmd prepare [ 6];
+ ncrcmd prepare2 [ 7];
+ ncrcmd command [ 6];
+ ncrcmd dispatch [ 32];
+ ncrcmd clrack [ 4];
+ ncrcmd no_data [ 17];
+ ncrcmd status [ 8];
+ ncrcmd msg_in [ 2];
+ ncrcmd msg_in2 [ 16];
+ ncrcmd msg_bad [ 4];
+ ncrcmd setmsg [ 7];
+ ncrcmd cleanup [ 6];
+ ncrcmd complete [ 9];
+ ncrcmd cleanup_ok [ 8 + PREFETCH_FLUSH_CNT];
+ ncrcmd cleanup0 [ 1];
+#ifndef SCSI_NCR_CCB_DONE_SUPPORT
+ ncrcmd signal [ 12];
+#else
+ ncrcmd signal [ 9];
+ ncrcmd done_pos [ 1];
+ ncrcmd done_plug [ 2];
+ ncrcmd done_end [ 7];
+#endif
+ ncrcmd save_dp [ 7];
+ ncrcmd restore_dp [ 5];
+ ncrcmd disconnect [ 10];
+ ncrcmd msg_out [ 9];
+ ncrcmd msg_out_done [ 7];
+ ncrcmd idle [ 2];
+ ncrcmd reselect [ 8];
+ ncrcmd reselected [ 8];
+ ncrcmd resel_dsa [ 6 + PREFETCH_FLUSH_CNT];
+ ncrcmd loadpos1 [ 4];
+ ncrcmd resel_lun [ 6];
+ ncrcmd resel_tag [ 6];
+ ncrcmd jump_to_nexus [ 4 + PREFETCH_FLUSH_CNT];
+ ncrcmd nexus_indirect [ 4];
+ ncrcmd resel_notag [ 4];
+ ncrcmd data_in [MAX_SCATTERL * 4];
+ ncrcmd data_in2 [ 4];
+ ncrcmd data_out [MAX_SCATTERL * 4];
+ ncrcmd data_out2 [ 4];
+};
+
+/*
+** Script fragments which stay in main memory for all chips.
+*/
+struct scripth {
+ ncrcmd tryloop [MAX_START*2];
+ ncrcmd tryloop2 [ 2];
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+ ncrcmd done_queue [MAX_DONE*5];
+ ncrcmd done_queue2 [ 2];
+#endif
+ ncrcmd select_no_atn [ 8];
+ ncrcmd cancel [ 4];
+ ncrcmd skip [ 9 + PREFETCH_FLUSH_CNT];
+ ncrcmd skip2 [ 19];
+ ncrcmd par_err_data_in [ 6];
+ ncrcmd par_err_other [ 4];
+ ncrcmd msg_reject [ 8];
+ ncrcmd msg_ign_residue [ 24];
+ ncrcmd msg_extended [ 10];
+ ncrcmd msg_ext_2 [ 10];
+ ncrcmd msg_wdtr [ 14];
+ ncrcmd send_wdtr [ 7];
+ ncrcmd msg_ext_3 [ 10];
+ ncrcmd msg_sdtr [ 14];
+ ncrcmd send_sdtr [ 7];
+ ncrcmd nego_bad_phase [ 4];
+ ncrcmd msg_out_abort [ 10];
+ ncrcmd hdata_in [MAX_SCATTERH * 4];
+ ncrcmd hdata_in2 [ 2];
+ ncrcmd hdata_out [MAX_SCATTERH * 4];
+ ncrcmd hdata_out2 [ 2];
+ ncrcmd reset [ 4];
+ ncrcmd aborttag [ 4];
+ ncrcmd abort [ 2];
+ ncrcmd abort_resel [ 20];
+ ncrcmd resend_ident [ 4];
+ ncrcmd clratn_go_on [ 3];
+ ncrcmd nxtdsp_go_on [ 1];
+ ncrcmd sdata_in [ 8];
+ ncrcmd data_io [ 18];
+ ncrcmd bad_identify [ 12];
+ ncrcmd bad_i_t_l [ 4];
+ ncrcmd bad_i_t_l_q [ 4];
+ ncrcmd bad_target [ 8];
+ ncrcmd bad_status [ 8];
+ ncrcmd start_ram [ 4 + PREFETCH_FLUSH_CNT];
+ ncrcmd start_ram0 [ 4];
+ ncrcmd sto_restart [ 5];
+ ncrcmd wait_dma [ 2];
+ ncrcmd snooptest [ 9];
+ ncrcmd snoopend [ 2];
+};
+
+/*==========================================================
+**
+**
+** Function headers.
+**
+**
+**==========================================================
+*/
+
+static void ncr_alloc_ccb (struct ncb *np, u_char tn, u_char ln);
+static void ncr_complete (struct ncb *np, struct ccb *cp);
+static void ncr_exception (struct ncb *np);
+static void ncr_free_ccb (struct ncb *np, struct ccb *cp);
+static void ncr_init_ccb (struct ncb *np, struct ccb *cp);
+static void ncr_init_tcb (struct ncb *np, u_char tn);
+static struct lcb * ncr_alloc_lcb (struct ncb *np, u_char tn, u_char ln);
+static struct lcb * ncr_setup_lcb (struct ncb *np, struct scsi_device *sdev);
+static void ncr_getclock (struct ncb *np, int mult);
+static void ncr_selectclock (struct ncb *np, u_char scntl3);
+static struct ccb *ncr_get_ccb (struct ncb *np, struct scsi_cmnd *cmd);
+static void ncr_chip_reset (struct ncb *np, int delay);
+static void ncr_init (struct ncb *np, int reset, char * msg, u_long code);
+static int ncr_int_sbmc (struct ncb *np);
+static int ncr_int_par (struct ncb *np);
+static void ncr_int_ma (struct ncb *np);
+static void ncr_int_sir (struct ncb *np);
+static void ncr_int_sto (struct ncb *np);
+static void ncr_negotiate (struct ncb* np, struct tcb* tp);
+static int ncr_prepare_nego(struct ncb *np, struct ccb *cp, u_char *msgptr);
+
+static void ncr_script_copy_and_bind
+ (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len);
+static void ncr_script_fill (struct script * scr, struct scripth * scripth);
+static int ncr_scatter (struct ncb *np, struct ccb *cp, struct scsi_cmnd *cmd);
+static void ncr_getsync (struct ncb *np, u_char sfac, u_char *fakp, u_char *scntl3p);
+static void ncr_setsync (struct ncb *np, struct ccb *cp, u_char scntl3, u_char sxfer);
+static void ncr_setup_tags (struct ncb *np, struct scsi_device *sdev);
+static void ncr_setwide (struct ncb *np, struct ccb *cp, u_char wide, u_char ack);
+static int ncr_snooptest (struct ncb *np);
+static void ncr_timeout (struct ncb *np);
+static void ncr_wakeup (struct ncb *np, u_long code);
+static void ncr_wakeup_done (struct ncb *np);
+static void ncr_start_next_ccb (struct ncb *np, struct lcb * lp, int maxn);
+static void ncr_put_start_queue(struct ncb *np, struct ccb *cp);
+
+static void insert_into_waiting_list(struct ncb *np, struct scsi_cmnd *cmd);
+static struct scsi_cmnd *retrieve_from_waiting_list(int to_remove, struct ncb *np, struct scsi_cmnd *cmd);
+static void process_waiting_list(struct ncb *np, int sts);
+
+#define remove_from_waiting_list(np, cmd) \
+ retrieve_from_waiting_list(1, (np), (cmd))
+#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
+#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
+
+static inline char *ncr_name (struct ncb *np)
+{
+ return np->inst_name;
+}
+
+
+/*==========================================================
+**
+**
+** Scripts for NCR-Processor.
+**
+** Use ncr_script_bind for binding to physical addresses.
+**
+**
+**==========================================================
+**
+** NADDR generates a reference to a field of the controller data.
+** PADDR generates a reference to another part of the script.
+** RADDR generates a reference to a script processor register.
+** FADDR generates a reference to a script processor register
+** with offset.
+**
+**----------------------------------------------------------
+*/
+
+#define RELOC_SOFTC 0x40000000
+#define RELOC_LABEL 0x50000000
+#define RELOC_REGISTER 0x60000000
+#if 0
+#define RELOC_KVAR 0x70000000
+#endif
+#define RELOC_LABELH 0x80000000
+#define RELOC_MASK 0xf0000000
+
+#define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label))
+#define PADDR(label) (RELOC_LABEL | offsetof(struct script, label))
+#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label))
+#define RADDR(label) (RELOC_REGISTER | REG(label))
+#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
+#if 0
+#define KVAR(which) (RELOC_KVAR | (which))
+#endif
+
+#if 0
+#define SCRIPT_KVAR_JIFFIES (0)
+#define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES
+#define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES
+/*
+ * Kernel variables referenced in the scripts.
+ * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
+ */
+static void *script_kvars[] __initdata =
+ { (void *)&jiffies };
+#endif
+
+static struct script script0 __initdata = {
+/*--------------------------< START >-----------------------*/ {
+ /*
+ ** This NOP will be patched with LED ON
+ ** SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
+ ** Clear SIGP.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ /*
+ ** Then jump to a certain point in tryloop.
+ ** Due to the lack of indirect addressing the code
+ ** is self modifying here.
+ */
+ SCR_JUMP,
+}/*-------------------------< STARTPOS >--------------------*/,{
+ PADDRH(tryloop),
+
+}/*-------------------------< SELECT >----------------------*/,{
+ /*
+ ** DSA contains the address of a scheduled
+ ** data structure.
+ **
+ ** SCRATCHA contains the address of the script,
+ ** which starts the next entry.
+ **
+ ** Set Initiator mode.
+ **
+ ** (Target mode is left as an exercise for the reader)
+ */
+
+ SCR_CLR (SCR_TRG),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_SELECTING),
+ 0,
+
+ /*
+ ** And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
+ PADDR (reselect),
+
+}/*-------------------------< SELECT2 >----------------------*/,{
+ /*
+ ** Now there are 4 possibilities:
+ **
+ ** (1) The ncr loses arbitration.
+ ** This is ok, because it will try again,
+ ** when the bus becomes idle.
+ ** (But beware of the timeout function!)
+ **
+ ** (2) The ncr is reselected.
+ ** Then the script processor takes the jump
+ ** to the RESELECT label.
+ **
+ ** (3) The ncr wins arbitration.
+ ** Then it will execute SCRIPTS instruction until
+ ** the next instruction that checks SCSI phase.
+ ** Then will stop and wait for selection to be
+ ** complete or selection time-out to occur.
+ ** As a result the SCRIPTS instructions until
+ ** LOADPOS + 2 should be executed in parallel with
+ ** the SCSI core performing selection.
+ */
+
+ /*
+ ** The M_REJECT problem seems to be due to a selection
+ ** timing problem.
+ ** Wait immediately for the selection to complete.
+ ** (2.5x behaves so)
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ 0,
+
+ /*
+ ** Next time use the next slot.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ PADDR (startpos),
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can access it.
+ **
+ ** We patch the address part of a
+ ** COPY command with the DSA-register.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (loadpos),
+ /*
+ ** Flush script prefetch if required
+ */
+ PREFETCH_FLUSH
+ /*
+ ** then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+ /*
+ ** continued after the next label ...
+ */
+}/*-------------------------< LOADPOS >---------------------*/,{
+ 0,
+ NADDR (header),
+ /*
+ ** Wait for the next phase or the selection
+ ** to complete or time-out.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR (prepare),
+
+}/*-------------------------< SEND_IDENT >----------------------*/,{
+ /*
+ ** Selection complete.
+ ** Send the IDENTIFY and SIMPLE_TAG messages
+ ** (and the M_X_SYNC_REQ message)
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct dsb, smsg),
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDRH (resend_ident),
+ SCR_LOAD_REG (scratcha, 0x80),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (lastmsg),
+}/*-------------------------< PREPARE >----------------------*/,{
+ /*
+ ** load the savep (saved pointer) into
+ ** the TEMP register (actual pointer)
+ */
+ SCR_COPY (4),
+ NADDR (header.savep),
+ RADDR (temp),
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ NADDR (header.status),
+ RADDR (scr0),
+}/*-------------------------< PREPARE2 >---------------------*/,{
+ /*
+ ** Initialize the msgout buffer with a NOOP message.
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgout),
+#if 0
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgin),
+#endif
+ /*
+ ** Anticipate the COMMAND phase.
+ ** This is the normal case for initial selection.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)),
+ PADDR (dispatch),
+
+}/*-------------------------< COMMAND >--------------------*/,{
+ /*
+ ** ... and send the command
+ */
+ SCR_MOVE_TBL ^ SCR_COMMAND,
+ offsetof (struct dsb, cmd),
+ /*
+ ** If status is still HS_NEGOTIATE, negotiation failed.
+ ** We check this here, since we want to do that
+ ** only once.
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+
+}/*-----------------------< DISPATCH >----------------------*/,{
+ /*
+ ** MSG_IN is the only phase that shall be
+ ** entered at least once for each (re)selection.
+ ** So we test it first.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR (msg_in),
+
+ SCR_RETURN ^ IFTRUE (IF (SCR_DATA_OUT)),
+ 0,
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4.
+ ** Possible data corruption during Memory Write and Invalidate.
+ ** This work-around resets the addressing logic prior to the
+ ** start of the first MOVE of a DATA IN phase.
+ ** (See Documentation/scsi/ncr53c8xx.txt for more information)
+ */
+ SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+ 20,
+ SCR_COPY (4),
+ RADDR (scratcha),
+ RADDR (scratcha),
+ SCR_RETURN,
+ 0,
+ SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+ PADDR (status),
+ SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+ PADDR (command),
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+ PADDR (msg_out),
+ /*
+ ** Discard one illegal phase byte, if required.
+ */
+ SCR_LOAD_REG (scratcha, XE_BAD_PHASE),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (xerr_st),
+ SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+ NADDR (scratch),
+ SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+ NADDR (scratch),
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< CLRACK >----------------------*/,{
+ /*
+ ** Terminate possible pending message phase.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< NO_DATA >--------------------*/,{
+ /*
+ ** The target wants to tranfer too much data
+ ** or in the wrong direction.
+ ** Remember that in extended error.
+ */
+ SCR_LOAD_REG (scratcha, XE_EXTRA_DATA),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (xerr_st),
+ /*
+ ** Discard one data byte, if required.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_DATA_OUT,
+ NADDR (scratch),
+ SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_DATA_IN,
+ NADDR (scratch),
+ /*
+ ** .. and repeat as required.
+ */
+ SCR_CALL,
+ PADDR (dispatch),
+ SCR_JUMP,
+ PADDR (no_data),
+
+}/*-------------------------< STATUS >--------------------*/,{
+ /*
+ ** get the status
+ */
+ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ NADDR (scratch),
+ /*
+ ** save status to scsi_status.
+ ** mark as complete.
+ */
+ SCR_TO_REG (SS_REG),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+ 0,
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< MSG_IN >--------------------*/,{
+ /*
+ ** Get the first byte of the message
+ ** and save it to SCRATCHA.
+ **
+ ** The script processor doesn't negate the
+ ** ACK signal after this transfer.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[0]),
+}/*-------------------------< MSG_IN2 >--------------------*/,{
+ /*
+ ** Handle this message.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+ PADDR (complete),
+ SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+ PADDR (disconnect),
+ SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+ PADDR (save_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+ PADDR (restore_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+ PADDRH (msg_extended),
+ SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
+ PADDR (clrack),
+ SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
+ PADDRH (msg_reject),
+ SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
+ PADDRH (msg_ign_residue),
+ /*
+ ** Rest of the messages left as
+ ** an exercise ...
+ **
+ ** Unimplemented messages:
+ ** fall through to MSG_BAD.
+ */
+}/*-------------------------< MSG_BAD >------------------*/,{
+ /*
+ ** unimplemented message - reject it.
+ */
+ SCR_INT,
+ SIR_REJECT_SENT,
+ SCR_LOAD_REG (scratcha, M_REJECT),
+ 0,
+}/*-------------------------< SETMSG >----------------------*/,{
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgout),
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR (clrack),
+}/*-------------------------< CLEANUP >-------------------*/,{
+ /*
+ ** dsa: Pointer to ccb
+ ** or xxxxxxFF (no ccb)
+ **
+ ** HS_REG: Host-Status (<>0!)
+ */
+ SCR_FROM_REG (dsa),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (0xff)),
+ PADDR (start),
+ /*
+ ** dsa is valid.
+ ** complete the cleanup.
+ */
+ SCR_JUMP,
+ PADDR (cleanup_ok),
+
+}/*-------------------------< COMPLETE >-----------------*/,{
+ /*
+ ** Complete message.
+ **
+ ** Copy TEMP register to LASTP in header.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.lastp),
+ /*
+ ** When we terminate the cycle by clearing ACK,
+ ** the target may disconnect immediately.
+ **
+ ** We don't want to be told of an
+ ** "unexpected disconnect",
+ ** so we disable this feature.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ /*
+ ** Terminate cycle ...
+ */
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ ** ... and wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+}/*-------------------------< CLEANUP_OK >----------------*/,{
+ /*
+ ** Save host status to header.
+ */
+ SCR_COPY (4),
+ RADDR (scr0),
+ NADDR (header.status),
+ /*
+ ** and copy back the header to the ccb.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (cleanup0),
+ /*
+ ** Flush script prefetch if required
+ */
+ PREFETCH_FLUSH
+ SCR_COPY (sizeof (struct head)),
+ NADDR (header),
+}/*-------------------------< CLEANUP0 >--------------------*/,{
+ 0,
+}/*-------------------------< SIGNAL >----------------------*/,{
+ /*
+ ** if job not completed ...
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ /*
+ ** ... start the next command.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0, (HS_DONEMASK|HS_SKIPMASK))),
+ PADDR(start),
+ /*
+ ** If command resulted in not GOOD status,
+ ** call the C code if needed.
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+ PADDRH (bad_status),
+
+#ifndef SCSI_NCR_CCB_DONE_SUPPORT
+
+ /*
+ ** ... signal completion to the host
+ */
+ SCR_INT,
+ SIR_INTFLY,
+ /*
+ ** Auf zu neuen Schandtaten!
+ */
+ SCR_JUMP,
+ PADDR(start),
+
+#else /* defined SCSI_NCR_CCB_DONE_SUPPORT */
+
+ /*
+ ** ... signal completion to the host
+ */
+ SCR_JUMP,
+}/*------------------------< DONE_POS >---------------------*/,{
+ PADDRH (done_queue),
+}/*------------------------< DONE_PLUG >--------------------*/,{
+ SCR_INT,
+ SIR_DONE_OVERFLOW,
+}/*------------------------< DONE_END >---------------------*/,{
+ SCR_INT,
+ SIR_INTFLY,
+ SCR_COPY (4),
+ RADDR (temp),
+ PADDR (done_pos),
+ SCR_JUMP,
+ PADDR (start),
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+
+}/*-------------------------< SAVE_DP >------------------*/,{
+ /*
+ ** SAVE_DP message:
+ ** Copy TEMP register to SAVEP in header.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< RESTORE_DP >---------------*/,{
+ /*
+ ** RESTORE_DP message:
+ ** Copy SAVEP in header to TEMP register.
+ */
+ SCR_COPY (4),
+ NADDR (header.savep),
+ RADDR (temp),
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< DISCONNECT >---------------*/,{
+ /*
+ ** DISCONNECTing ...
+ **
+ ** disable the "unexpected disconnect" feature,
+ ** and remove the ACK signal.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ ** Wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** Status is: DISCONNECTED.
+ */
+ SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup_ok),
+
+}/*-------------------------< MSG_OUT >-------------------*/,{
+ /*
+ ** The target requests a message.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ NADDR (msgout),
+ NADDR (lastmsg),
+ /*
+ ** If it was no ABORT message ...
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
+ PADDRH (msg_out_abort),
+ /*
+ ** ... wait for the next phase
+ ** if it's a message out, send it again, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR (msg_out),
+}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+ /*
+ ** ... else clear the message ...
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (4),
+ RADDR (scratcha),
+ NADDR (msgout),
+ /*
+ ** ... and process the next phase
+ */
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< IDLE >------------------------*/,{
+ /*
+ ** Nothing to do?
+ ** Wait for reselect.
+ ** This NOP will be patched with LED OFF
+ ** SCR_REG_REG (gpreg, SCR_OR, 0x01)
+ */
+ SCR_NO_OP,
+ 0,
+}/*-------------------------< RESELECT >--------------------*/,{
+ /*
+ ** make the DSA invalid.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_CLR (SCR_TRG),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_IN_RESELECT),
+ 0,
+ /*
+ ** Sleep waiting for a reselection.
+ ** If SIGP is set, special treatment.
+ **
+ ** Zu allem bereit ..
+ */
+ SCR_WAIT_RESEL,
+ PADDR(start),
+}/*-------------------------< RESELECTED >------------------*/,{
+ /*
+ ** This NOP will be patched with LED ON
+ ** SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
+ ** ... zu nichts zu gebrauchen ?
+ **
+ ** load the target id into the SFBR
+ ** and jump to the control block.
+ **
+ ** Look at the declarations of
+ ** - struct ncb
+ ** - struct tcb
+ ** - struct lcb
+ ** - struct ccb
+ ** to understand what's going on.
+ */
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+ 0,
+ SCR_TO_REG (sdid),
+ 0,
+ SCR_JUMP,
+ NADDR (jump_tcb),
+
+}/*-------------------------< RESEL_DSA >-------------------*/,{
+ /*
+ ** Ack the IDENTIFY or TAG previously received.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can access it.
+ **
+ ** We patch the address part of a
+ ** COPY command with the DSA-register.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (loadpos1),
+ /*
+ ** Flush script prefetch if required
+ */
+ PREFETCH_FLUSH
+ /*
+ ** then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+ /*
+ ** continued after the next label ...
+ */
+
+}/*-------------------------< LOADPOS1 >-------------------*/,{
+ 0,
+ NADDR (header),
+ /*
+ ** The DSA contains the data structure address.
+ */
+ SCR_JUMP,
+ PADDR (prepare),
+
+}/*-------------------------< RESEL_LUN >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get an IDENTIFY message
+ ** Wait for a msg_in phase.
+ */
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ SIR_RESEL_NO_MSG_IN,
+ /*
+ ** message phase.
+ ** Read the data directly from the BUS DATA lines.
+ ** This helps to support very old SCSI devices that
+ ** may reselect without sending an IDENTIFY.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+ /*
+ ** It should be an Identify message.
+ */
+ SCR_RETURN,
+ 0,
+}/*-------------------------< RESEL_TAG >-------------------*/,{
+ /*
+ ** Read IDENTIFY + SIMPLE + TAG using a single MOVE.
+ ** Agressive optimization, is'nt it?
+ ** No need to test the SIMPLE TAG message, since the
+ ** driver only supports conformant devices for tags. ;-)
+ */
+ SCR_MOVE_ABS (3) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ /*
+ ** Read the TAG from the SIDL.
+ ** Still an aggressive optimization. ;-)
+ ** Compute the CCB indirect jump address which
+ ** is (#TAG*2 & 0xfc) due to tag numbering using
+ ** 1,3,5..MAXTAGS*2+1 actual values.
+ */
+ SCR_REG_SFBR (sidl, SCR_SHL, 0),
+ 0,
+ SCR_SFBR_REG (temp, SCR_AND, 0xfc),
+ 0,
+}/*-------------------------< JUMP_TO_NEXUS >-------------------*/,{
+ SCR_COPY_F (4),
+ RADDR (temp),
+ PADDR (nexus_indirect),
+ /*
+ ** Flush script prefetch if required
+ */
+ PREFETCH_FLUSH
+ SCR_COPY (4),
+}/*-------------------------< NEXUS_INDIRECT >-------------------*/,{
+ 0,
+ RADDR (temp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< RESEL_NOTAG >-------------------*/,{
+ /*
+ ** No tag expected.
+ ** Read an throw away the IDENTIFY.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_JUMP,
+ PADDR (jump_to_nexus),
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTERL parameter,
+** it is filled in at runtime.
+**
+** ##===========< i=0; i<MAX_SCATTERL >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** || PADDR (dispatch),
+** || SCR_MOVE_TBL ^ SCR_DATA_IN,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< DATA_IN2 >-------------------*/,{
+ SCR_CALL,
+ PADDR (dispatch),
+ SCR_JUMP,
+ PADDR (no_data),
+}/*-------------------------< DATA_OUT >--------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTERL parameter,
+** it is filled in at runtime.
+**
+** ##===========< i=0; i<MAX_SCATTERL >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+** || PADDR (dispatch),
+** || SCR_MOVE_TBL ^ SCR_DATA_OUT,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< DATA_OUT2 >-------------------*/,{
+ SCR_CALL,
+ PADDR (dispatch),
+ SCR_JUMP,
+ PADDR (no_data),
+}/*--------------------------------------------------------*/
+};
+
+static struct scripth scripth0 __initdata = {
+/*-------------------------< TRYLOOP >---------------------*/{
+/*
+** Start the next entry.
+** Called addresses point to the launch script in the CCB.
+** They are patched by the main processor.
+**
+** Because the size depends on the
+** #define MAX_START parameter, it is filled
+** in at runtime.
+**
+**-----------------------------------------------------------
+**
+** ##===========< I=0; i<MAX_START >===========
+** || SCR_CALL,
+** || PADDR (idle),
+** ##==========================================
+**
+**-----------------------------------------------------------
+*/
+0
+}/*------------------------< TRYLOOP2 >---------------------*/,{
+ SCR_JUMP,
+ PADDRH(tryloop),
+
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+
+}/*------------------------< DONE_QUEUE >-------------------*/,{
+/*
+** Copy the CCB address to the next done entry.
+** Because the size depends on the
+** #define MAX_DONE parameter, it is filled
+** in at runtime.
+**
+**-----------------------------------------------------------
+**
+** ##===========< I=0; i<MAX_DONE >===========
+** || SCR_COPY (sizeof(struct ccb *),
+** || NADDR (header.cp),
+** || NADDR (ccb_done[i]),
+** || SCR_CALL,
+** || PADDR (done_end),
+** ##==========================================
+**
+**-----------------------------------------------------------
+*/
+0
+}/*------------------------< DONE_QUEUE2 >------------------*/,{
+ SCR_JUMP,
+ PADDRH (done_queue),
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+}/*------------------------< SELECT_NO_ATN >-----------------*/,{
+ /*
+ ** Set Initiator mode.
+ ** And try to select this target without ATN.
+ */
+
+ SCR_CLR (SCR_TRG),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_SELECTING),
+ 0,
+ SCR_SEL_TBL ^ offsetof (struct dsb, select),
+ PADDR (reselect),
+ SCR_JUMP,
+ PADDR (select2),
+
+}/*-------------------------< CANCEL >------------------------*/,{
+
+ SCR_LOAD_REG (scratcha, HS_ABORTED),
+ 0,
+ SCR_JUMPR,
+ 8,
+}/*-------------------------< SKIP >------------------------*/,{
+ SCR_LOAD_REG (scratcha, 0),
+ 0,
+ /*
+ ** This entry has been canceled.
+ ** Next time use the next slot.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ PADDR (startpos),
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can access it.
+ **
+ ** We patch the address part of a
+ ** COPY command with the DSA-register.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDRH (skip2),
+ /*
+ ** Flush script prefetch if required
+ */
+ PREFETCH_FLUSH
+ /*
+ ** then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+ /*
+ ** continued after the next label ...
+ */
+}/*-------------------------< SKIP2 >---------------------*/,{
+ 0,
+ NADDR (header),
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ NADDR (header.status),
+ RADDR (scr0),
+ /*
+ ** Force host status.
+ */
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
+ 16,
+ SCR_REG_REG (HS_REG, SCR_OR, HS_SKIPMASK),
+ 0,
+ SCR_JUMPR,
+ 8,
+ SCR_TO_REG (HS_REG),
+ 0,
+ SCR_LOAD_REG (SS_REG, S_GOOD),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup_ok),
+
+},/*-------------------------< PAR_ERR_DATA_IN >---------------*/{
+ /*
+ ** Ignore all data in byte, until next phase
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDRH (par_err_other),
+ SCR_MOVE_ABS (1) ^ SCR_DATA_IN,
+ NADDR (scratch),
+ SCR_JUMPR,
+ -24,
+},/*-------------------------< PAR_ERR_OTHER >------------------*/{
+ /*
+ ** count it.
+ */
+ SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+ 0,
+ /*
+ ** jump to dispatcher.
+ */
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+ /*
+ ** If a negotiation was in progress,
+ ** negotiation failed.
+ ** Otherwise, let the C code print
+ ** some message.
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
+ SIR_REJECT_RECEIVED,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get residue size.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[1]),
+ /*
+ ** Size is 0 .. ignore message.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (0)),
+ PADDR (clrack),
+ /*
+ ** Size is not 1 .. have to interrupt.
+ */
+ SCR_JUMPR ^ IFFALSE (DATA (1)),
+ 40,
+ /*
+ ** Check for residue byte in swide register
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+ 16,
+ /*
+ ** There IS data in the swide register.
+ ** Discard it.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ SCR_JUMP,
+ PADDR (clrack),
+ /*
+ ** Load again the size to the sfbr register.
+ */
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_INT,
+ SIR_IGN_RESIDUE,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_EXTENDED >-------------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get length.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[1]),
+ /*
+ */
+ SCR_JUMP ^ IFTRUE (DATA (3)),
+ PADDRH (msg_ext_3),
+ SCR_JUMP ^ IFFALSE (DATA (2)),
+ PADDR (msg_bad),
+}/*-------------------------< MSG_EXT_2 >----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
+ PADDRH (msg_wdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+}/*-------------------------< MSG_WDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get data bus width
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT,
+ SIR_NEGO_WIDE,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDRH (nego_bad_phase),
+
+}/*-------------------------< SEND_WDTR >----------------*/,{
+ /*
+ ** Send the M_X_WIDE_REQ
+ */
+ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ NADDR (msgout),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< MSG_EXT_3 >----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
+ PADDRH (msg_sdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get period and offset
+ */
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT,
+ SIR_NEGO_SYNC,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDRH (nego_bad_phase),
+
+}/*-------------------------< SEND_SDTR >-------------*/,{
+ /*
+ ** Send the M_X_SYNC_REQ
+ */
+ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ NADDR (msgout),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< NEGO_BAD_PHASE >------------*/,{
+ SCR_INT,
+ SIR_NEGO_PROTO,
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
+ /*
+ ** After ABORT message,
+ **
+ ** expect an immediate disconnect, ...
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** ... and set the status to "ABORTED"
+ */
+ SCR_LOAD_REG (HS_REG, HS_ABORTED),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< HDATA_IN >-------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTERH parameter,
+** it is filled in at runtime.
+**
+** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >==
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** || PADDR (dispatch),
+** || SCR_MOVE_TBL ^ SCR_DATA_IN,
+** || offsetof (struct dsb, data[ i]),
+** ##===================================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< HDATA_IN2 >------------------*/,{
+ SCR_JUMP,
+ PADDR (data_in),
+
+}/*-------------------------< HDATA_OUT >-------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTERH parameter,
+** it is filled in at runtime.
+**
+** ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >==
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+** || PADDR (dispatch),
+** || SCR_MOVE_TBL ^ SCR_DATA_OUT,
+** || offsetof (struct dsb, data[ i]),
+** ##===================================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< HDATA_OUT2 >------------------*/,{
+ SCR_JUMP,
+ PADDR (data_out),
+
+}/*-------------------------< RESET >----------------------*/,{
+ /*
+ ** Send a M_RESET message if bad IDENTIFY
+ ** received on reselection.
+ */
+ SCR_LOAD_REG (scratcha, M_ABORT_TAG),
+ 0,
+ SCR_JUMP,
+ PADDRH (abort_resel),
+}/*-------------------------< ABORTTAG >-------------------*/,{
+ /*
+ ** Abort a wrong tag received on reselection.
+ */
+ SCR_LOAD_REG (scratcha, M_ABORT_TAG),
+ 0,
+ SCR_JUMP,
+ PADDRH (abort_resel),
+}/*-------------------------< ABORT >----------------------*/,{
+ /*
+ ** Abort a reselection when no active CCB.
+ */
+ SCR_LOAD_REG (scratcha, M_ABORT),
+ 0,
+}/*-------------------------< ABORT_RESEL >----------------*/,{
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgout),
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** and send it.
+ ** we expect an immediate disconnect
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ NADDR (msgout),
+ NADDR (lastmsg),
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ SCR_JUMP,
+ PADDR (start),
+}/*-------------------------< RESEND_IDENT >-------------------*/,{
+ /*
+ ** The target stays in MSG OUT phase after having acked
+ ** Identify [+ Tag [+ Extended message ]]. Targets shall
+ ** behave this way on parity error.
+ ** We must send it again all the messages.
+ */
+ SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the */
+ 0, /* 1rst ACK = 90 ns. Hope the NCR is'nt too fast */
+ SCR_JUMP,
+ PADDR (send_ident),
+}/*-------------------------< CLRATN_GO_ON >-------------------*/,{
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_JUMP,
+}/*-------------------------< NXTDSP_GO_ON >-------------------*/,{
+ 0,
+}/*-------------------------< SDATA_IN >-------------------*/,{
+ SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR (dispatch),
+ SCR_MOVE_TBL ^ SCR_DATA_IN,
+ offsetof (struct dsb, sense),
+ SCR_CALL,
+ PADDR (dispatch),
+ SCR_JUMP,
+ PADDR (no_data),
+}/*-------------------------< DATA_IO >--------------------*/,{
+ /*
+ ** We jump here if the data direction was unknown at the
+ ** time we had to queue the command to the scripts processor.
+ ** Pointers had been set as follow in this situation:
+ ** savep --> DATA_IO
+ ** lastp --> start pointer when DATA_IN
+ ** goalp --> goal pointer when DATA_IN
+ ** wlastp --> start pointer when DATA_OUT
+ ** wgoalp --> goal pointer when DATA_OUT
+ ** This script sets savep/lastp/goalp according to the
+ ** direction chosen by the target.
+ */
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ 32,
+ /*
+ ** Direction is DATA IN.
+ ** Warning: we jump here, even when phase is DATA OUT.
+ */
+ SCR_COPY (4),
+ NADDR (header.lastp),
+ NADDR (header.savep),
+
+ /*
+ ** Jump to the SCRIPTS according to actual direction.
+ */
+ SCR_COPY (4),
+ NADDR (header.savep),
+ RADDR (temp),
+ SCR_RETURN,
+ 0,
+ /*
+ ** Direction is DATA OUT.
+ */
+ SCR_COPY (4),
+ NADDR (header.wlastp),
+ NADDR (header.lastp),
+ SCR_COPY (4),
+ NADDR (header.wgoalp),
+ NADDR (header.goalp),
+ SCR_JUMPR,
+ -64,
+}/*-------------------------< BAD_IDENTIFY >---------------*/,{
+ /*
+ ** If message phase but not an IDENTIFY,
+ ** get some help from the C code.
+ ** Old SCSI device may behave so.
+ */
+ SCR_JUMPR ^ IFTRUE (MASK (0x80, 0x80)),
+ 16,
+ SCR_INT,
+ SIR_RESEL_NO_IDENTIFY,
+ SCR_JUMP,
+ PADDRH (reset),
+ /*
+ ** Message is an IDENTIFY, but lun is unknown.
+ ** Read the message, since we got it directly
+ ** from the SCSI BUS data lines.
+ ** Signal problem to C code for logging the event.
+ ** Send a M_ABORT to clear all pending tasks.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_LUN,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_JUMP,
+ PADDRH (abort),
+}/*-------------------------< BAD_I_T_L >------------------*/,{
+ /*
+ ** We donnot have a task for that I_T_L.
+ ** Signal problem to C code for logging the event.
+ ** Send a M_ABORT message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_I_T_L,
+ SCR_JUMP,
+ PADDRH (abort),
+}/*-------------------------< BAD_I_T_L_Q >----------------*/,{
+ /*
+ ** We donnot have a task that matches the tag.
+ ** Signal problem to C code for logging the event.
+ ** Send a M_ABORTTAG message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_I_T_L_Q,
+ SCR_JUMP,
+ PADDRH (aborttag),
+}/*-------------------------< BAD_TARGET >-----------------*/,{
+ /*
+ ** We donnot know the target that reselected us.
+ ** Grab the first message if any (IDENTIFY).
+ ** Signal problem to C code for logging the event.
+ ** M_RESET message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_TARGET,
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_JUMP,
+ PADDRH (reset),
+}/*-------------------------< BAD_STATUS >-----------------*/,{
+ /*
+ ** If command resulted in either QUEUE FULL,
+ ** CHECK CONDITION or COMMAND TERMINATED,
+ ** call the C code.
+ */
+ SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+ SIR_BAD_STATUS,
+ SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)),
+ SIR_BAD_STATUS,
+ SCR_INT ^ IFTRUE (DATA (S_TERMINATED)),
+ SIR_BAD_STATUS,
+ SCR_RETURN,
+ 0,
+}/*-------------------------< START_RAM >-------------------*/,{
+ /*
+ ** Load the script into on-chip RAM,
+ ** and jump to start point.
+ */
+ SCR_COPY_F (4),
+ RADDR (scratcha),
+ PADDRH (start_ram0),
+ /*
+ ** Flush script prefetch if required
+ */
+ PREFETCH_FLUSH
+ SCR_COPY (sizeof (struct script)),
+}/*-------------------------< START_RAM0 >--------------------*/,{
+ 0,
+ PADDR (start),
+ SCR_JUMP,
+ PADDR (start),
+}/*-------------------------< STO_RESTART >-------------------*/,{
+ /*
+ **
+ ** Repair start queue (e.g. next time use the next slot)
+ ** and jump to start point.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ PADDR (startpos),
+ SCR_JUMP,
+ PADDR (start),
+}/*-------------------------< WAIT_DMA >-------------------*/,{
+ /*
+ ** For HP Zalon/53c720 systems, the Zalon interface
+ ** between CPU and 53c720 does prefetches, which causes
+ ** problems with self modifying scripts. The problem
+ ** is overcome by calling a dummy subroutine after each
+ ** modification, to force a refetch of the script on
+ ** return from the subroutine.
+ */
+ SCR_RETURN,
+ 0,
+}/*-------------------------< SNOOPTEST >-------------------*/,{
+ /*
+ ** Read the variable.
+ */
+ SCR_COPY (4),
+ NADDR(ncr_cache),
+ RADDR (scratcha),
+ /*
+ ** Write the variable.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR(ncr_cache),
+ /*
+ ** Read back the variable.
+ */
+ SCR_COPY (4),
+ NADDR(ncr_cache),
+ RADDR (temp),
+}/*-------------------------< SNOOPEND >-------------------*/,{
+ /*
+ ** And stop.
+ */
+ SCR_INT,
+ 99,
+}/*--------------------------------------------------------*/
+};
+
+/*==========================================================
+**
+**
+** Fill in #define dependent parts of the script
+**
+**
+**==========================================================
+*/
+
+void __init ncr_script_fill (struct script * scr, struct scripth * scrh)
+{
+ int i;
+ ncrcmd *p;
+
+ p = scrh->tryloop;
+ for (i=0; i<MAX_START; i++) {
+ *p++ =SCR_CALL;
+ *p++ =PADDR (idle);
+ }
+
+ BUG_ON((u_long)p != (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
+
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+
+ p = scrh->done_queue;
+ for (i = 0; i<MAX_DONE; i++) {
+ *p++ =SCR_COPY (sizeof(struct ccb *));
+ *p++ =NADDR (header.cp);
+ *p++ =NADDR (ccb_done[i]);
+ *p++ =SCR_CALL;
+ *p++ =PADDR (done_end);
+ }
+
+ BUG_ON((u_long)p != (u_long)&scrh->done_queue+sizeof(scrh->done_queue));
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+
+ p = scrh->hdata_in;
+ for (i=0; i<MAX_SCATTERH; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+ *p++ =offsetof (struct dsb, data[i]);
+ }
+
+ BUG_ON((u_long)p != (u_long)&scrh->hdata_in + sizeof (scrh->hdata_in));
+
+ p = scr->data_in;
+ for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+ *p++ =offsetof (struct dsb, data[i]);
+ }
+
+ BUG_ON((u_long)p != (u_long)&scr->data_in + sizeof (scr->data_in));
+
+ p = scrh->hdata_out;
+ for (i=0; i<MAX_SCATTERH; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+ *p++ =offsetof (struct dsb, data[i]);
+ }
+
+ BUG_ON((u_long)p != (u_long)&scrh->hdata_out + sizeof (scrh->hdata_out));
+
+ p = scr->data_out;
+ for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+ *p++ =offsetof (struct dsb, data[i]);
+ }
+
+ BUG_ON((u_long) p != (u_long)&scr->data_out + sizeof (scr->data_out));
+}
+
+/*==========================================================
+**
+**
+** Copy and rebind a script.
+**
+**
+**==========================================================
+*/
+
+static void __init
+ncr_script_copy_and_bind (struct ncb *np, ncrcmd *src, ncrcmd *dst, int len)
+{
+ ncrcmd opcode, new, old, tmp1, tmp2;
+ ncrcmd *start, *end;
+ int relocs;
+ int opchanged = 0;
+
+ start = src;
+ end = src + len/4;
+
+ while (src < end) {
+
+ opcode = *src++;
+ *dst++ = cpu_to_scr(opcode);
+
+ /*
+ ** If we forget to change the length
+ ** in struct script, a field will be
+ ** padded with 0. This is an illegal
+ ** command.
+ */
+
+ if (opcode == 0) {
+ printk (KERN_ERR "%s: ERROR0 IN SCRIPT at %d.\n",
+ ncr_name(np), (int) (src-start-1));
+ mdelay(1000);
+ }
+
+ if (DEBUG_FLAGS & DEBUG_SCRIPT)
+ printk (KERN_DEBUG "%p: <%x>\n",
+ (src-1), (unsigned)opcode);
+
+ /*
+ ** We don't have to decode ALL commands
+ */
+ switch (opcode >> 28) {
+
+ case 0xc:
+ /*
+ ** COPY has TWO arguments.
+ */
+ relocs = 2;
+ tmp1 = src[0];
+#ifdef RELOC_KVAR
+ if ((tmp1 & RELOC_MASK) == RELOC_KVAR)
+ tmp1 = 0;
+#endif
+ tmp2 = src[1];
+#ifdef RELOC_KVAR
+ if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
+ tmp2 = 0;
+#endif
+ if ((tmp1 ^ tmp2) & 3) {
+ printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n",
+ ncr_name(np), (int) (src-start-1));
+ mdelay(1000);
+ }
+ /*
+ ** If PREFETCH feature not enabled, remove
+ ** the NO FLUSH bit if present.
+ */
+ if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
+ dst[-1] = cpu_to_scr(opcode & ~SCR_NO_FLUSH);
+ ++opchanged;
+ }
+ break;
+
+ case 0x0:
+ /*
+ ** MOVE (absolute address)
+ */
+ relocs = 1;
+ break;
+
+ case 0x8:
+ /*
+ ** JUMP / CALL
+ ** don't relocate if relative :-)
+ */
+ if (opcode & 0x00800000)
+ relocs = 0;
+ else
+ relocs = 1;
+ break;
+
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ relocs = 1;
+ break;
+
+ default:
+ relocs = 0;
+ break;
+ }
+
+ if (relocs) {
+ while (relocs--) {
+ old = *src++;
+
+ switch (old & RELOC_MASK) {
+ case RELOC_REGISTER:
+ new = (old & ~RELOC_MASK) + np->paddr;
+ break;
+ case RELOC_LABEL:
+ new = (old & ~RELOC_MASK) + np->p_script;
+ break;
+ case RELOC_LABELH:
+ new = (old & ~RELOC_MASK) + np->p_scripth;
+ break;
+ case RELOC_SOFTC:
+ new = (old & ~RELOC_MASK) + np->p_ncb;
+ break;
+#ifdef RELOC_KVAR
+ case RELOC_KVAR:
+ if (((old & ~RELOC_MASK) <
+ SCRIPT_KVAR_FIRST) ||
+ ((old & ~RELOC_MASK) >
+ SCRIPT_KVAR_LAST))
+ panic("ncr KVAR out of range");
+ new = vtophys(script_kvars[old &
+ ~RELOC_MASK]);
+ break;
+#endif
+ case 0:
+ /* Don't relocate a 0 address. */
+ if (old == 0) {
+ new = old;
+ break;
+ }
+ /* fall through */
+ default:
+ panic("ncr_script_copy_and_bind: weird relocation %x\n", old);
+ break;
+ }
+
+ *dst++ = cpu_to_scr(new);
+ }
+ } else
+ *dst++ = cpu_to_scr(*src++);
+
+ }
+}
+
+/*
+** Linux host data structure
+*/
+
+struct host_data {
+ struct ncb *ncb;
+};
+
+#define PRINT_ADDR(cmd, arg...) dev_info(&cmd->device->sdev_gendev , ## arg)
+
+static void ncr_print_msg(struct ccb *cp, char *label, u_char *msg)
+{
+ int i;
+ PRINT_ADDR(cp->cmd, "%s: ", label);
+
+ printk ("%x",*msg);
+ if (*msg == M_EXTENDED) {
+ for (i = 1; i < 8; i++) {
+ if (i - 1 > msg[1])
+ break;
+ printk ("-%x",msg[i]);
+ }
+ } else if ((*msg & 0xf0) == 0x20) {
+ printk ("-%x",msg[1]);
+ }
+
+ printk(".\n");
+}
+
+/*==========================================================
+**
+** NCR chip clock divisor table.
+** Divisors are multiplied by 10,000,000 in order to make
+** calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+ {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
+/*===============================================================
+**
+** Prepare io register values used by ncr_init() according
+** to selected and supported features.
+**
+** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128
+** transfers. 32,64,128 are only supported by 875 and 895 chips.
+** We use log base 2 (burst length) as internal code, with
+** value 0 meaning "burst disabled".
+**
+**===============================================================
+*/
+
+/*
+ * Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ * Burst code from io register bits. Burst enable is ctest0 for c720
+ */
+#define burst_code(dmode, ctest0) \
+ (ctest0) & 0x80 ? 0 : (((dmode) & 0xc0) >> 6) + 1
+
+/*
+ * Set initial io register bits from burst code.
+ */
+static inline void ncr_init_burst(struct ncb *np, u_char bc)
+{
+ u_char *be = &np->rv_ctest0;
+ *be &= ~0x80;
+ np->rv_dmode &= ~(0x3 << 6);
+ np->rv_ctest5 &= ~0x4;
+
+ if (!bc) {
+ *be |= 0x80;
+ } else {
+ --bc;
+ np->rv_dmode |= ((bc & 0x3) << 6);
+ np->rv_ctest5 |= (bc & 0x4);
+ }
+}
+
+static void __init ncr_prepare_setting(struct ncb *np)
+{
+ u_char burst_max;
+ u_long period;
+ int i;
+
+ /*
+ ** Save assumed BIOS setting
+ */
+
+ np->sv_scntl0 = INB(nc_scntl0) & 0x0a;
+ np->sv_scntl3 = INB(nc_scntl3) & 0x07;
+ np->sv_dmode = INB(nc_dmode) & 0xce;
+ np->sv_dcntl = INB(nc_dcntl) & 0xa8;
+ np->sv_ctest0 = INB(nc_ctest0) & 0x84;
+ np->sv_ctest3 = INB(nc_ctest3) & 0x01;
+ np->sv_ctest4 = INB(nc_ctest4) & 0x80;
+ np->sv_ctest5 = INB(nc_ctest5) & 0x24;
+ np->sv_gpcntl = INB(nc_gpcntl);
+ np->sv_stest2 = INB(nc_stest2) & 0x20;
+ np->sv_stest4 = INB(nc_stest4);
+
+ /*
+ ** Wide ?
+ */
+
+ np->maxwide = (np->features & FE_WIDE)? 1 : 0;
+
+ /*
+ * Guess the frequency of the chip's clock.
+ */
+ if (np->features & FE_ULTRA)
+ np->clock_khz = 80000;
+ else
+ np->clock_khz = 40000;
+
+ /*
+ * Get the clock multiplier factor.
+ */
+ if (np->features & FE_QUAD)
+ np->multiplier = 4;
+ else if (np->features & FE_DBLR)
+ np->multiplier = 2;
+ else
+ np->multiplier = 1;
+
+ /*
+ * Measure SCSI clock frequency for chips
+ * it may vary from assumed one.
+ */
+ if (np->features & FE_VARCLK)
+ ncr_getclock(np, np->multiplier);
+
+ /*
+ * Divisor to be used for async (timer pre-scaler).
+ */
+ i = np->clock_divn - 1;
+ while (--i >= 0) {
+ if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+ ++i;
+ break;
+ }
+ }
+ np->rv_scntl3 = i+1;
+
+ /*
+ * Minimum synchronous period factor supported by the chip.
+ * Btw, 'period' is in tenths of nanoseconds.
+ */
+
+ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+ if (period <= 250) np->minsync = 10;
+ else if (period <= 303) np->minsync = 11;
+ else if (period <= 500) np->minsync = 12;
+ else np->minsync = (period + 40 - 1) / 40;
+
+ /*
+ * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+ */
+
+ if (np->minsync < 25 && !(np->features & FE_ULTRA))
+ np->minsync = 25;
+
+ /*
+ * Maximum synchronous period factor supported by the chip.
+ */
+
+ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+ np->maxsync = period > 2540 ? 254 : period / 10;
+
+ /*
+ ** Prepare initial value of other IO registers
+ */
+#if defined SCSI_NCR_TRUST_BIOS_SETTING
+ np->rv_scntl0 = np->sv_scntl0;
+ np->rv_dmode = np->sv_dmode;
+ np->rv_dcntl = np->sv_dcntl;
+ np->rv_ctest0 = np->sv_ctest0;
+ np->rv_ctest3 = np->sv_ctest3;
+ np->rv_ctest4 = np->sv_ctest4;
+ np->rv_ctest5 = np->sv_ctest5;
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest0);
+#else
+
+ /*
+ ** Select burst length (dwords)
+ */
+ burst_max = driver_setup.burst_max;
+ if (burst_max == 255)
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest0);
+ if (burst_max > 7)
+ burst_max = 7;
+ if (burst_max > np->maxburst)
+ burst_max = np->maxburst;
+
+ /*
+ ** Select all supported special features
+ */
+ if (np->features & FE_ERL)
+ np->rv_dmode |= ERL; /* Enable Read Line */
+ if (np->features & FE_BOF)
+ np->rv_dmode |= BOF; /* Burst Opcode Fetch */
+ if (np->features & FE_ERMP)
+ np->rv_dmode |= ERMP; /* Enable Read Multiple */
+ if (np->features & FE_PFEN)
+ np->rv_dcntl |= PFEN; /* Prefetch Enable */
+ if (np->features & FE_CLSE)
+ np->rv_dcntl |= CLSE; /* Cache Line Size Enable */
+ if (np->features & FE_WRIE)
+ np->rv_ctest3 |= WRIE; /* Write and Invalidate */
+ if (np->features & FE_DFS)
+ np->rv_ctest5 |= DFS; /* Dma Fifo Size */
+ if (np->features & FE_MUX)
+ np->rv_ctest4 |= MUX; /* Host bus multiplex mode */
+ if (np->features & FE_EA)
+ np->rv_dcntl |= EA; /* Enable ACK */
+ if (np->features & FE_EHP)
+ np->rv_ctest0 |= EHP; /* Even host parity */
+
+ /*
+ ** Select some other
+ */
+ if (driver_setup.master_parity)
+ np->rv_ctest4 |= MPEE; /* Master parity checking */
+ if (driver_setup.scsi_parity)
+ np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */
+
+ /*
+ ** Get SCSI addr of host adapter (set by bios?).
+ */
+ if (np->myaddr == 255) {
+ np->myaddr = INB(nc_scid) & 0x07;
+ if (!np->myaddr)
+ np->myaddr = SCSI_NCR_MYADDR;
+ }
+
+#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
+
+ /*
+ * Prepare initial io register bits for burst length
+ */
+ ncr_init_burst(np, burst_max);
+
+ /*
+ ** Set SCSI BUS mode.
+ **
+ ** - ULTRA2 chips (895/895A/896) report the current
+ ** BUS mode through the STEST4 IO register.
+ ** - For previous generation chips (825/825A/875),
+ ** user has to tell us how to check against HVD,
+ ** since a 100% safe algorithm is not possible.
+ */
+ np->scsi_mode = SMODE_SE;
+ if (np->features & FE_DIFF) {
+ switch(driver_setup.diff_support) {
+ case 4: /* Trust previous settings if present, then GPIO3 */
+ if (np->sv_scntl3) {
+ if (np->sv_stest2 & 0x20)
+ np->scsi_mode = SMODE_HVD;
+ break;
+ }
+ case 3: /* SYMBIOS controllers report HVD through GPIO3 */
+ if (INB(nc_gpreg) & 0x08)
+ break;
+ case 2: /* Set HVD unconditionally */
+ np->scsi_mode = SMODE_HVD;
+ case 1: /* Trust previous settings for HVD */
+ if (np->sv_stest2 & 0x20)
+ np->scsi_mode = SMODE_HVD;
+ break;
+ default:/* Don't care about HVD */
+ break;
+ }
+ }
+ if (np->scsi_mode == SMODE_HVD)
+ np->rv_stest2 |= 0x20;
+
+ /*
+ ** Set LED support from SCRIPTS.
+ ** Ignore this feature for boards known to use a
+ ** specific GPIO wiring and for the 895A or 896
+ ** that drive the LED directly.
+ ** Also probe initial setting of GPIO0 as output.
+ */
+ if ((driver_setup.led_pin) &&
+ !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01))
+ np->features |= FE_LED0;
+
+ /*
+ ** Set irq mode.
+ */
+ switch(driver_setup.irqm & 3) {
+ case 2:
+ np->rv_dcntl |= IRQM;
+ break;
+ case 1:
+ np->rv_dcntl |= (np->sv_dcntl & IRQM);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ ** Configure targets according to driver setup.
+ ** Allow to override sync, wide and NOSCAN from
+ ** boot command line.
+ */
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ struct tcb *tp = &np->target[i];
+
+ tp->usrsync = driver_setup.default_sync;
+ tp->usrwide = driver_setup.max_wide;
+ tp->usrtags = MAX_TAGS;
+ tp->period = 0xffff;
+ if (!driver_setup.disconnection)
+ np->target[i].usrflag = UF_NODISC;
+ }
+
+ /*
+ ** Announce all that stuff to user.
+ */
+
+ printk(KERN_INFO "%s: ID %d, Fast-%d%s%s\n", ncr_name(np),
+ np->myaddr,
+ np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+ (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity",
+ (np->rv_stest2 & 0x20) ? ", Differential" : "");
+
+ if (bootverbose > 1) {
+ printk (KERN_INFO "%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+ np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+ printk (KERN_INFO "%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+ np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+ }
+
+ if (bootverbose && np->paddr2)
+ printk (KERN_INFO "%s: on-chip RAM at 0x%lx\n",
+ ncr_name(np), np->paddr2);
+}
+
+/*==========================================================
+**
+**
+** Done SCSI commands list management.
+**
+** We donnot enter the scsi_done() callback immediately
+** after a command has been seen as completed but we
+** insert it into a list which is flushed outside any kind
+** of driver critical section.
+** This allows to do minimal stuff under interrupt and
+** inside critical sections and to also avoid locking up
+** on recursive calls to driver entry points under SMP.
+** In fact, the only kernel point which is entered by the
+** driver with a driver lock set is kmalloc(GFP_ATOMIC)
+** that shall not reenter the driver under any circumstances,
+** AFAIK.
+**
+**==========================================================
+*/
+static inline void ncr_queue_done_cmd(struct ncb *np, struct scsi_cmnd *cmd)
+{
+ unmap_scsi_data(np, cmd);
+ cmd->host_scribble = (char *) np->done_list;
+ np->done_list = cmd;
+}
+
+static inline void ncr_flush_done_cmds(struct scsi_cmnd *lcmd)
+{
+ struct scsi_cmnd *cmd;
+
+ while (lcmd) {
+ cmd = lcmd;
+ lcmd = (struct scsi_cmnd *) cmd->host_scribble;
+ cmd->scsi_done(cmd);
+ }
+}
+
+/*==========================================================
+**
+**
+** Prepare the next negotiation message if needed.
+**
+** Fill in the part of message buffer that contains the
+** negotiation and the nego_status field of the CCB.
+** Returns the size of the message in bytes.
+**
+**
+**==========================================================
+*/
+
+
+static int ncr_prepare_nego(struct ncb *np, struct ccb *cp, u_char *msgptr)
+{
+ struct tcb *tp = &np->target[cp->target];
+ int msglen = 0;
+ int nego = 0;
+ struct scsi_target *starget = tp->starget;
+
+ /* negotiate wide transfers ? */
+ if (!tp->widedone) {
+ if (spi_support_wide(starget)) {
+ nego = NS_WIDE;
+ } else
+ tp->widedone=1;
+ }
+
+ /* negotiate synchronous transfers? */
+ if (!nego && !tp->period) {
+ if (spi_support_sync(starget)) {
+ nego = NS_SYNC;
+ } else {
+ tp->period =0xffff;
+ dev_info(&starget->dev, "target did not report SYNC.\n");
+ }
+ }
+
+ switch (nego) {
+ case NS_SYNC:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 3;
+ msgptr[msglen++] = M_X_SYNC_REQ;
+ msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
+ msgptr[msglen++] = tp->maxoffs;
+ break;
+ case NS_WIDE:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 2;
+ msgptr[msglen++] = M_X_WIDE_REQ;
+ msgptr[msglen++] = tp->usrwide;
+ break;
+ }
+
+ cp->nego_status = nego;
+
+ if (nego) {
+ tp->nego_cp = cp;
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ ncr_print_msg(cp, nego == NS_WIDE ?
+ "wide msgout":"sync_msgout", msgptr);
+ }
+ }
+
+ return msglen;
+}
+
+
+
+/*==========================================================
+**
+**
+** Start execution of a SCSI command.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd)
+{
+ struct scsi_device *sdev = cmd->device;
+ struct tcb *tp = &np->target[sdev->id];
+ struct lcb *lp = tp->lp[sdev->lun];
+ struct ccb *cp;
+
+ int segments;
+ u_char idmsg, *msgptr;
+ u32 msglen;
+ int direction;
+ u32 lastp, goalp;
+
+ /*---------------------------------------------
+ **
+ ** Some shortcuts ...
+ **
+ **---------------------------------------------
+ */
+ if ((sdev->id == np->myaddr ) ||
+ (sdev->id >= MAX_TARGET) ||
+ (sdev->lun >= MAX_LUN )) {
+ return(DID_BAD_TARGET);
+ }
+
+ /*---------------------------------------------
+ **
+ ** Complete the 1st TEST UNIT READY command
+ ** with error condition if the device is
+ ** flagged NOSCAN, in order to speed up
+ ** the boot.
+ **
+ **---------------------------------------------
+ */
+ if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12) &&
+ (tp->usrflag & UF_NOSCAN)) {
+ tp->usrflag &= ~UF_NOSCAN;
+ return DID_BAD_TARGET;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_TINY) {
+ PRINT_ADDR(cmd, "CMD=%x ", cmd->cmnd[0]);
+ }
+
+ /*---------------------------------------------------
+ **
+ ** Assign a ccb / bind cmd.
+ ** If resetting, shorten settle_time if necessary
+ ** in order to avoid spurious timeouts.
+ ** If resetting or no free ccb,
+ ** insert cmd into the waiting list.
+ **
+ **----------------------------------------------------
+ */
+ if (np->settle_time && cmd->timeout_per_command >= HZ) {
+ u_long tlimit = ktime_get(cmd->timeout_per_command - HZ);
+ if (ktime_dif(np->settle_time, tlimit) > 0)
+ np->settle_time = tlimit;
+ }
+
+ if (np->settle_time || !(cp=ncr_get_ccb (np, cmd))) {
+ insert_into_waiting_list(np, cmd);
+ return(DID_OK);
+ }
+ cp->cmd = cmd;
+
+ /*----------------------------------------------------
+ **
+ ** Build the identify / tag / sdtr message
+ **
+ **----------------------------------------------------
+ */
+
+ idmsg = M_IDENTIFY | sdev->lun;
+
+ if (cp ->tag != NO_TAG ||
+ (cp != np->ccb && np->disc && !(tp->usrflag & UF_NODISC)))
+ idmsg |= 0x40;
+
+ msgptr = cp->scsi_smsg;
+ msglen = 0;
+ msgptr[msglen++] = idmsg;
+
+ if (cp->tag != NO_TAG) {
+ char order = np->order;
+
+ /*
+ ** Force ordered tag if necessary to avoid timeouts
+ ** and to preserve interactivity.
+ */
+ if (lp && ktime_exp(lp->tags_stime)) {
+ if (lp->tags_smap) {
+ order = M_ORDERED_TAG;
+ if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){
+ PRINT_ADDR(cmd,
+ "ordered tag forced.\n");
+ }
+ }
+ lp->tags_stime = ktime_get(3*HZ);
+ lp->tags_smap = lp->tags_umap;
+ }
+
+ if (order == 0) {
+ /*
+ ** Ordered write ops, unordered read ops.
+ */
+ switch (cmd->cmnd[0]) {
+ case 0x08: /* READ_SMALL (6) */
+ case 0x28: /* READ_BIG (10) */
+ case 0xa8: /* READ_HUGE (12) */
+ order = M_SIMPLE_TAG;
+ break;
+ default:
+ order = M_ORDERED_TAG;
+ }
+ }
+ msgptr[msglen++] = order;
+ /*
+ ** Actual tags are numbered 1,3,5,..2*MAXTAGS+1,
+ ** since we may have to deal with devices that have
+ ** problems with #TAG 0 or too great #TAG numbers.
+ */
+ msgptr[msglen++] = (cp->tag << 1) + 1;
+ }
+
+ /*----------------------------------------------------
+ **
+ ** Build the data descriptors
+ **
+ **----------------------------------------------------
+ */
+
+ direction = cmd->sc_data_direction;
+ if (direction != DMA_NONE) {
+ segments = ncr_scatter(np, cp, cp->cmd);
+ if (segments < 0) {
+ ncr_free_ccb(np, cp);
+ return(DID_ERROR);
+ }
+ }
+ else {
+ cp->data_len = 0;
+ segments = 0;
+ }
+
+ /*---------------------------------------------------
+ **
+ ** negotiation required?
+ **
+ ** (nego_status is filled by ncr_prepare_nego())
+ **
+ **---------------------------------------------------
+ */
+
+ cp->nego_status = 0;
+
+ if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+ msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
+ }
+
+ /*----------------------------------------------------
+ **
+ ** Determine xfer direction.
+ **
+ **----------------------------------------------------
+ */
+ if (!cp->data_len)
+ direction = DMA_NONE;
+
+ /*
+ ** If data direction is BIDIRECTIONAL, speculate FROM_DEVICE
+ ** but prepare alternate pointers for TO_DEVICE in case
+ ** of our speculation will be just wrong.
+ ** SCRIPTS will swap values if needed.
+ */
+ switch(direction) {
+ case DMA_BIDIRECTIONAL:
+ case DMA_TO_DEVICE:
+ goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
+ if (segments <= MAX_SCATTERL)
+ lastp = goalp - 8 - (segments * 16);
+ else {
+ lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
+ lastp -= (segments - MAX_SCATTERL) * 16;
+ }
+ if (direction != DMA_BIDIRECTIONAL)
+ break;
+ cp->phys.header.wgoalp = cpu_to_scr(goalp);
+ cp->phys.header.wlastp = cpu_to_scr(lastp);
+ /* fall through */
+ case DMA_FROM_DEVICE:
+ goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
+ if (segments <= MAX_SCATTERL)
+ lastp = goalp - 8 - (segments * 16);
+ else {
+ lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
+ lastp -= (segments - MAX_SCATTERL) * 16;
+ }
+ break;
+ default:
+ case DMA_NONE:
+ lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
+ break;
+ }
+
+ /*
+ ** Set all pointers values needed by SCRIPTS.
+ ** If direction is unknown, start at data_io.
+ */
+ cp->phys.header.lastp = cpu_to_scr(lastp);
+ cp->phys.header.goalp = cpu_to_scr(goalp);
+
+ if (direction == DMA_BIDIRECTIONAL)
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
+ else
+ cp->phys.header.savep= cpu_to_scr(lastp);
+
+ /*
+ ** Save the initial data pointer in order to be able
+ ** to redo the command.
+ */
+ cp->startp = cp->phys.header.savep;
+
+ /*----------------------------------------------------
+ **
+ ** fill in ccb
+ **
+ **----------------------------------------------------
+ **
+ **
+ ** physical -> virtual backlink
+ ** Generic SCSI command
+ */
+
+ /*
+ ** Startqueue
+ */
+ cp->start.schedule.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+ cp->restart.schedule.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_dsa));
+ /*
+ ** select
+ */
+ cp->phys.select.sel_id = sdev->id;
+ cp->phys.select.sel_scntl3 = tp->wval;
+ cp->phys.select.sel_sxfer = tp->sval;
+ /*
+ ** message
+ */
+ cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
+ cp->phys.smsg.size = cpu_to_scr(msglen);
+
+ /*
+ ** command
+ */
+ memcpy(cp->cdb_buf, cmd->cmnd, min_t(int, cmd->cmd_len, sizeof(cp->cdb_buf)));
+ cp->phys.cmd.addr = cpu_to_scr(CCB_PHYS (cp, cdb_buf[0]));
+ cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
+
+ /*
+ ** status
+ */
+ cp->actualquirks = 0;
+ cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
+ cp->scsi_status = S_ILLEGAL;
+ cp->parity_status = 0;
+
+ cp->xerr_status = XE_OK;
+#if 0
+ cp->sync_status = tp->sval;
+ cp->wide_status = tp->wval;
+#endif
+
+ /*----------------------------------------------------
+ **
+ ** Critical region: start this job.
+ **
+ **----------------------------------------------------
+ */
+
+ /* activate this job. */
+ cp->magic = CCB_MAGIC;
+
+ /*
+ ** insert next CCBs into start queue.
+ ** 2 max at a time is enough to flush the CCB wait queue.
+ */
+ cp->auto_sense = 0;
+ if (lp)
+ ncr_start_next_ccb(np, lp, 2);
+ else
+ ncr_put_start_queue(np, cp);
+
+ /* Command is successfully queued. */
+
+ return DID_OK;
+}
+
+
+/*==========================================================
+**
+**
+** Insert a CCB into the start queue and wake up the
+** SCRIPTS processor.
+**
+**
+**==========================================================
+*/
+
+static void ncr_start_next_ccb(struct ncb *np, struct lcb *lp, int maxn)
+{
+ struct list_head *qp;
+ struct ccb *cp;
+
+ if (lp->held_ccb)
+ return;
+
+ while (maxn-- && lp->queuedccbs < lp->queuedepth) {
+ qp = ncr_list_pop(&lp->wait_ccbq);
+ if (!qp)
+ break;
+ ++lp->queuedccbs;
+ cp = list_entry(qp, struct ccb, link_ccbq);
+ list_add_tail(qp, &lp->busy_ccbq);
+ lp->jump_ccb[cp->tag == NO_TAG ? 0 : cp->tag] =
+ cpu_to_scr(CCB_PHYS (cp, restart));
+ ncr_put_start_queue(np, cp);
+ }
+}
+
+static void ncr_put_start_queue(struct ncb *np, struct ccb *cp)
+{
+ u16 qidx;
+
+ /*
+ ** insert into start queue.
+ */
+ if (!np->squeueput) np->squeueput = 1;
+ qidx = np->squeueput + 2;
+ if (qidx >= MAX_START + MAX_START) qidx = 1;
+
+ np->scripth->tryloop [qidx] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+ MEMORY_BARRIER();
+ np->scripth->tryloop [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, start));
+
+ np->squeueput = qidx;
+ ++np->queuedccbs;
+ cp->queued = 1;
+
+ if (DEBUG_FLAGS & DEBUG_QUEUE)
+ printk ("%s: queuepos=%d.\n", ncr_name (np), np->squeueput);
+
+ /*
+ ** Script processor may be waiting for reselect.
+ ** Wake it up.
+ */
+ MEMORY_BARRIER();
+ OUTB (nc_istat, SIGP);
+}
+
+
+static int ncr_reset_scsi_bus(struct ncb *np, int enab_int, int settle_delay)
+{
+ u32 term;
+ int retv = 0;
+
+ np->settle_time = ktime_get(settle_delay * HZ);
+
+ if (bootverbose > 1)
+ printk("%s: resetting, "
+ "command processing suspended for %d seconds\n",
+ ncr_name(np), settle_delay);
+
+ ncr_chip_reset(np, 100);
+ udelay(2000); /* The 895 needs time for the bus mode to settle */
+ if (enab_int)
+ OUTW (nc_sien, RST);
+ /*
+ ** Enable Tolerant, reset IRQD if present and
+ ** properly set IRQ mode, prior to resetting the bus.
+ */
+ OUTB (nc_stest3, TE);
+ OUTB (nc_scntl1, CRST);
+ udelay(200);
+
+ if (!driver_setup.bus_check)
+ goto out;
+ /*
+ ** Check for no terminators or SCSI bus shorts to ground.
+ ** Read SCSI data bus, data parity bits and control signals.
+ ** We are expecting RESET to be TRUE and other signals to be
+ ** FALSE.
+ */
+
+ term = INB(nc_sstat0);
+ term = ((term & 2) << 7) + ((term & 1) << 17); /* rst sdp0 */
+ term |= ((INB(nc_sstat2) & 0x01) << 26) | /* sdp1 */
+ ((INW(nc_sbdl) & 0xff) << 9) | /* d7-0 */
+ ((INW(nc_sbdl) & 0xff00) << 10) | /* d15-8 */
+ INB(nc_sbcl); /* req ack bsy sel atn msg cd io */
+
+ if (!(np->features & FE_WIDE))
+ term &= 0x3ffff;
+
+ if (term != (2<<7)) {
+ printk("%s: suspicious SCSI data while resetting the BUS.\n",
+ ncr_name(np));
+ printk("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+ "0x%lx, expecting 0x%lx\n",
+ ncr_name(np),
+ (np->features & FE_WIDE) ? "dp1,d15-8," : "",
+ (u_long)term, (u_long)(2<<7));
+ if (driver_setup.bus_check == 1)
+ retv = 1;
+ }
+out:
+ OUTB (nc_scntl1, 0);
+ return retv;
+}
+
+/*
+ * Start reset process.
+ * If reset in progress do nothing.
+ * The interrupt handler will reinitialize the chip.
+ * The timeout handler will wait for settle_time before
+ * clearing it and so resuming command processing.
+ */
+static void ncr_start_reset(struct ncb *np)
+{
+ if (!np->settle_time) {
+ ncr_reset_scsi_bus(np, 1, driver_setup.settle_delay);
+ }
+}
+
+/*==========================================================
+**
+**
+** Reset the SCSI BUS.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+static int ncr_reset_bus (struct ncb *np, struct scsi_cmnd *cmd, int sync_reset)
+{
+/* struct scsi_device *device = cmd->device; */
+ struct ccb *cp;
+ int found;
+
+/*
+ * Return immediately if reset is in progress.
+ */
+ if (np->settle_time) {
+ return FAILED;
+ }
+/*
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle
+ * delay of 2 seconds will be completed.
+ */
+ ncr_start_reset(np);
+/*
+ * First, look in the wakeup list
+ */
+ for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for the ccb of this command.
+ */
+ if (cp->host_status == HS_IDLE) continue;
+ if (cp->cmd == cmd) {
+ found = 1;
+ break;
+ }
+ }
+/*
+ * Then, look in the waiting list
+ */
+ if (!found && retrieve_from_waiting_list(0, np, cmd))
+ found = 1;
+/*
+ * Wake-up all awaiting commands with DID_RESET.
+ */
+ reset_waiting_list(np);
+/*
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
+ */
+ ncr_wakeup(np, HS_RESET);
+/*
+ * If the involved command was not in a driver queue, and the
+ * scsi driver told us reset is synchronous, and the command is not
+ * currently in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
+ */
+ if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
+ cmd->result = ScsiResult(DID_RESET, 0);
+ ncr_queue_done_cmd(np, cmd);
+ }
+
+ return SUCCESS;
+}
+
+#if 0 /* unused and broken.. */
+/*==========================================================
+**
+**
+** Abort an SCSI command.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+static int ncr_abort_command (struct ncb *np, struct scsi_cmnd *cmd)
+{
+/* struct scsi_device *device = cmd->device; */
+ struct ccb *cp;
+ int found;
+ int retv;
+
+/*
+ * First, look for the scsi command in the waiting list
+ */
+ if (remove_from_waiting_list(np, cmd)) {
+ cmd->result = ScsiResult(DID_ABORT, 0);
+ ncr_queue_done_cmd(np, cmd);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * Then, look in the wakeup list
+ */
+ for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for the ccb of this command.
+ */
+ if (cp->host_status == HS_IDLE) continue;
+ if (cp->cmd == cmd) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (np->settle_time) {
+ return SCSI_ABORT_SNOOZE;
+ }
+
+ /*
+ ** If the CCB is active, patch schedule jumps for the
+ ** script to abort the command.
+ */
+
+ switch(cp->host_status) {
+ case HS_BUSY:
+ case HS_NEGOTIATE:
+ printk ("%s: abort ccb=%p (cancel)\n", ncr_name (np), cp);
+ cp->start.schedule.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, cancel));
+ retv = SCSI_ABORT_PENDING;
+ break;
+ case HS_DISCONNECT:
+ cp->restart.schedule.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+ retv = SCSI_ABORT_PENDING;
+ break;
+ default:
+ retv = SCSI_ABORT_NOT_RUNNING;
+ break;
+
+ }
+
+ /*
+ ** If there are no requests, the script
+ ** processor will sleep on SEL_WAIT_RESEL.
+ ** Let's wake it up, since it may have to work.
+ */
+ OUTB (nc_istat, SIGP);
+
+ return retv;
+}
+#endif
+
+static void ncr_detach(struct ncb *np)
+{
+ struct ccb *cp;
+ struct tcb *tp;
+ struct lcb *lp;
+ int target, lun;
+ int i;
+ char inst_name[16];
+
+ /* Local copy so we don't access np after freeing it! */
+ strlcpy(inst_name, ncr_name(np), sizeof(inst_name));
+
+ printk("%s: releasing host resources\n", ncr_name(np));
+
+/*
+** Stop the ncr_timeout process
+** Set release_stage to 1 and wait that ncr_timeout() set it to 2.
+*/
+
+#ifdef DEBUG_NCR53C8XX
+ printk("%s: stopping the timer\n", ncr_name(np));
+#endif
+ np->release_stage = 1;
+ for (i = 50 ; i && np->release_stage != 2 ; i--)
+ mdelay(100);
+ if (np->release_stage != 2)
+ printk("%s: the timer seems to be already stopped\n", ncr_name(np));
+ else np->release_stage = 2;
+
+/*
+** Disable chip interrupts
+*/
+
+#ifdef DEBUG_NCR53C8XX
+ printk("%s: disabling chip interrupts\n", ncr_name(np));
+#endif
+ OUTW (nc_sien , 0);
+ OUTB (nc_dien , 0);
+
+ /*
+ ** Reset NCR chip
+ ** Restore bios setting for automatic clock detection.
+ */
+
+ printk("%s: resetting chip\n", ncr_name(np));
+ ncr_chip_reset(np, 100);
+
+ OUTB(nc_dmode, np->sv_dmode);
+ OUTB(nc_dcntl, np->sv_dcntl);
+ OUTB(nc_ctest0, np->sv_ctest0);
+ OUTB(nc_ctest3, np->sv_ctest3);
+ OUTB(nc_ctest4, np->sv_ctest4);
+ OUTB(nc_ctest5, np->sv_ctest5);
+ OUTB(nc_gpcntl, np->sv_gpcntl);
+ OUTB(nc_stest2, np->sv_stest2);
+
+ ncr_selectclock(np, np->sv_scntl3);
+
+ /*
+ ** Free allocated ccb(s)
+ */
+
+ while ((cp=np->ccb->link_ccb) != NULL) {
+ np->ccb->link_ccb = cp->link_ccb;
+ if (cp->host_status) {
+ printk("%s: shall free an active ccb (host_status=%d)\n",
+ ncr_name(np), cp->host_status);
+ }
+#ifdef DEBUG_NCR53C8XX
+ printk("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
+#endif
+ m_free_dma(cp, sizeof(*cp), "CCB");
+ }
+
+ /* Free allocated tp(s) */
+
+ for (target = 0; target < MAX_TARGET ; target++) {
+ tp=&np->target[target];
+ for (lun = 0 ; lun < MAX_LUN ; lun++) {
+ lp = tp->lp[lun];
+ if (lp) {
+#ifdef DEBUG_NCR53C8XX
+ printk("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
+#endif
+ if (lp->jump_ccb != &lp->jump_ccb_0)
+ m_free_dma(lp->jump_ccb,256,"JUMP_CCB");
+ m_free_dma(lp, sizeof(*lp), "LCB");
+ }
+ }
+ }
+
+ if (np->scripth0)
+ m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+ if (np->script0)
+ m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+ if (np->ccb)
+ m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+ m_free_dma(np, sizeof(struct ncb), "NCB");
+
+ printk("%s: host resources successfully released\n", inst_name);
+}
+
+/*==========================================================
+**
+**
+** Complete execution of a SCSI command.
+** Signal completion to the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+
+void ncr_complete (struct ncb *np, struct ccb *cp)
+{
+ struct scsi_cmnd *cmd;
+ struct tcb *tp;
+ struct lcb *lp;
+
+ /*
+ ** Sanity check
+ */
+
+ if (!cp || cp->magic != CCB_MAGIC || !cp->cmd)
+ return;
+
+ /*
+ ** Print minimal debug information.
+ */
+
+ if (DEBUG_FLAGS & DEBUG_TINY)
+ printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp,
+ cp->host_status,cp->scsi_status);
+
+ /*
+ ** Get command, target and lun pointers.
+ */
+
+ cmd = cp->cmd;
+ cp->cmd = NULL;
+ tp = &np->target[cmd->device->id];
+ lp = tp->lp[cmd->device->lun];
+
+ /*
+ ** We donnot queue more than 1 ccb per target
+ ** with negotiation at any time. If this ccb was
+ ** used for negotiation, clear this info in the tcb.
+ */
+
+ if (cp == tp->nego_cp)
+ tp->nego_cp = NULL;
+
+ /*
+ ** If auto-sense performed, change scsi status.
+ */
+ if (cp->auto_sense) {
+ cp->scsi_status = cp->auto_sense;
+ }
+
+ /*
+ ** If we were recovering from queue full or performing
+ ** auto-sense, requeue skipped CCBs to the wait queue.
+ */
+
+ if (lp && lp->held_ccb) {
+ if (cp == lp->held_ccb) {
+ list_splice_init(&lp->skip_ccbq, &lp->wait_ccbq);
+ lp->held_ccb = NULL;
+ }
+ }
+
+ /*
+ ** Check for parity errors.
+ */
+
+ if (cp->parity_status > 1) {
+ PRINT_ADDR(cmd, "%d parity error(s).\n",cp->parity_status);
+ }
+
+ /*
+ ** Check for extended errors.
+ */
+
+ if (cp->xerr_status != XE_OK) {
+ switch (cp->xerr_status) {
+ case XE_EXTRA_DATA:
+ PRINT_ADDR(cmd, "extraneous data discarded.\n");
+ break;
+ case XE_BAD_PHASE:
+ PRINT_ADDR(cmd, "invalid scsi phase (4/5).\n");
+ break;
+ default:
+ PRINT_ADDR(cmd, "extended error %d.\n",
+ cp->xerr_status);
+ break;
+ }
+ if (cp->host_status==HS_COMPLETE)
+ cp->host_status = HS_FAIL;
+ }
+
+ /*
+ ** Print out any error for debugging purpose.
+ */
+ if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
+ if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD) {
+ PRINT_ADDR(cmd, "ERROR: cmd=%x host_status=%x "
+ "scsi_status=%x\n", cmd->cmnd[0],
+ cp->host_status, cp->scsi_status);
+ }
+ }
+
+ /*
+ ** Check the status.
+ */
+ if ( (cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_GOOD ||
+ cp->scsi_status == S_COND_MET)) {
+ /*
+ * All went well (GOOD status).
+ * CONDITION MET status is returned on
+ * `Pre-Fetch' or `Search data' success.
+ */
+ cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+ /*
+ ** @RESID@
+ ** Could dig out the correct value for resid,
+ ** but it would be quite complicated.
+ */
+ /* if (cp->phys.header.lastp != cp->phys.header.goalp) */
+
+ /*
+ ** Allocate the lcb if not yet.
+ */
+ if (!lp)
+ ncr_alloc_lcb (np, cmd->device->id, cmd->device->lun);
+
+ tp->bytes += cp->data_len;
+ tp->transfers ++;
+
+ /*
+ ** If tags was reduced due to queue full,
+ ** increase tags if 1000 good status received.
+ */
+ if (lp && lp->usetags && lp->numtags < lp->maxtags) {
+ ++lp->num_good;
+ if (lp->num_good >= 1000) {
+ lp->num_good = 0;
+ ++lp->numtags;
+ ncr_setup_tags (np, cmd->device);
+ }
+ }
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_CHECK_COND)) {
+ /*
+ ** Check condition code
+ */
+ cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
+
+ /*
+ ** Copy back sense data to caller's buffer.
+ */
+ memcpy(cmd->sense_buffer, cp->sense_buf,
+ min(sizeof(cmd->sense_buffer), sizeof(cp->sense_buf)));
+
+ if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
+ u_char * p = (u_char*) & cmd->sense_buffer;
+ int i;
+ PRINT_ADDR(cmd, "sense data:");
+ for (i=0; i<14; i++) printk (" %x", *p++);
+ printk (".\n");
+ }
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_CONFLICT)) {
+ /*
+ ** Reservation Conflict condition code
+ */
+ cmd->result = ScsiResult(DID_OK, S_CONFLICT);
+
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_BUSY ||
+ cp->scsi_status == S_QUEUE_FULL)) {
+
+ /*
+ ** Target is busy.
+ */
+ cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+ } else if ((cp->host_status == HS_SEL_TIMEOUT)
+ || (cp->host_status == HS_TIMEOUT)) {
+
+ /*
+ ** No response
+ */
+ cmd->result = ScsiResult(DID_TIME_OUT, cp->scsi_status);
+
+ } else if (cp->host_status == HS_RESET) {
+
+ /*
+ ** SCSI bus reset
+ */
+ cmd->result = ScsiResult(DID_RESET, cp->scsi_status);
+
+ } else if (cp->host_status == HS_ABORTED) {
+
+ /*
+ ** Transfer aborted
+ */
+ cmd->result = ScsiResult(DID_ABORT, cp->scsi_status);
+
+ } else {
+
+ /*
+ ** Other protocol messes
+ */
+ PRINT_ADDR(cmd, "COMMAND FAILED (%x %x) @%p.\n",
+ cp->host_status, cp->scsi_status, cp);
+
+ cmd->result = ScsiResult(DID_ERROR, cp->scsi_status);
+ }
+
+ /*
+ ** trace output
+ */
+
+ if (tp->usrflag & UF_TRACE) {
+ u_char * p;
+ int i;
+ PRINT_ADDR(cmd, " CMD:");
+ p = (u_char*) &cmd->cmnd[0];
+ for (i=0; i<cmd->cmd_len; i++) printk (" %x", *p++);
+
+ if (cp->host_status==HS_COMPLETE) {
+ switch (cp->scsi_status) {
+ case S_GOOD:
+ printk (" GOOD");
+ break;
+ case S_CHECK_COND:
+ printk (" SENSE:");
+ p = (u_char*) &cmd->sense_buffer;
+ for (i=0; i<14; i++)
+ printk (" %x", *p++);
+ break;
+ default:
+ printk (" STAT: %x\n", cp->scsi_status);
+ break;
+ }
+ } else printk (" HOSTERROR: %x", cp->host_status);
+ printk ("\n");
+ }
+
+ /*
+ ** Free this ccb
+ */
+ ncr_free_ccb (np, cp);
+
+ /*
+ ** requeue awaiting scsi commands for this lun.
+ */
+ if (lp && lp->queuedccbs < lp->queuedepth &&
+ !list_empty(&lp->wait_ccbq))
+ ncr_start_next_ccb(np, lp, 2);
+
+ /*
+ ** requeue awaiting scsi commands for this controller.
+ */
+ if (np->waiting_list)
+ requeue_waiting_list(np);
+
+ /*
+ ** signal completion to generic driver.
+ */
+ ncr_queue_done_cmd(np, cmd);
+}
+
+/*==========================================================
+**
+**
+** Signal all (or one) control block done.
+**
+**
+**==========================================================
+*/
+
+/*
+** This CCB has been skipped by the NCR.
+** Queue it in the correponding unit queue.
+*/
+static void ncr_ccb_skipped(struct ncb *np, struct ccb *cp)
+{
+ struct tcb *tp = &np->target[cp->target];
+ struct lcb *lp = tp->lp[cp->lun];
+
+ if (lp && cp != np->ccb) {
+ cp->host_status &= ~HS_SKIPMASK;
+ cp->start.schedule.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+ list_del(&cp->link_ccbq);
+ list_add_tail(&cp->link_ccbq, &lp->skip_ccbq);
+ if (cp->queued) {
+ --lp->queuedccbs;
+ }
+ }
+ if (cp->queued) {
+ --np->queuedccbs;
+ cp->queued = 0;
+ }
+}
+
+/*
+** The NCR has completed CCBs.
+** Look at the DONE QUEUE if enabled, otherwise scan all CCBs
+*/
+void ncr_wakeup_done (struct ncb *np)
+{
+ struct ccb *cp;
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+ int i, j;
+
+ i = np->ccb_done_ic;
+ while (1) {
+ j = i+1;
+ if (j >= MAX_DONE)
+ j = 0;
+
+ cp = np->ccb_done[j];
+ if (!CCB_DONE_VALID(cp))
+ break;
+
+ np->ccb_done[j] = (struct ccb *)CCB_DONE_EMPTY;
+ np->scripth->done_queue[5*j + 4] =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, done_plug));
+ MEMORY_BARRIER();
+ np->scripth->done_queue[5*i + 4] =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, done_end));
+
+ if (cp->host_status & HS_DONEMASK)
+ ncr_complete (np, cp);
+ else if (cp->host_status & HS_SKIPMASK)
+ ncr_ccb_skipped (np, cp);
+
+ i = j;
+ }
+ np->ccb_done_ic = i;
+#else
+ cp = np->ccb;
+ while (cp) {
+ if (cp->host_status & HS_DONEMASK)
+ ncr_complete (np, cp);
+ else if (cp->host_status & HS_SKIPMASK)
+ ncr_ccb_skipped (np, cp);
+ cp = cp->link_ccb;
+ }
+#endif
+}
+
+/*
+** Complete all active CCBs.
+*/
+void ncr_wakeup (struct ncb *np, u_long code)
+{
+ struct ccb *cp = np->ccb;
+
+ while (cp) {
+ if (cp->host_status != HS_IDLE) {
+ cp->host_status = code;
+ ncr_complete (np, cp);
+ }
+ cp = cp->link_ccb;
+ }
+}
+
+/*
+** Reset ncr chip.
+*/
+
+/* Some initialisation must be done immediately following reset, for 53c720,
+ * at least. EA (dcntl bit 5) isn't set here as it is set once only in
+ * the _detect function.
+ */
+static void ncr_chip_reset(struct ncb *np, int delay)
+{
+ OUTB (nc_istat, SRST);
+ udelay(delay);
+ OUTB (nc_istat, 0 );
+
+ if (np->features & FE_EHP)
+ OUTB (nc_ctest0, EHP);
+ if (np->features & FE_MUX)
+ OUTB (nc_ctest4, MUX);
+}
+
+
+/*==========================================================
+**
+**
+** Start NCR chip.
+**
+**
+**==========================================================
+*/
+
+void ncr_init (struct ncb *np, int reset, char * msg, u_long code)
+{
+ int i;
+
+ /*
+ ** Reset chip if asked, otherwise just clear fifos.
+ */
+
+ if (reset) {
+ OUTB (nc_istat, SRST);
+ udelay(100);
+ }
+ else {
+ OUTB (nc_stest3, TE|CSF);
+ OUTONB (nc_ctest3, CLF);
+ }
+
+ /*
+ ** Message.
+ */
+
+ if (msg) printk (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
+
+ /*
+ ** Clear Start Queue
+ */
+ np->queuedepth = MAX_START - 1; /* 1 entry needed as end marker */
+ for (i = 1; i < MAX_START + MAX_START; i += 2)
+ np->scripth0->tryloop[i] =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+
+ /*
+ ** Start at first entry.
+ */
+ np->squeueput = 0;
+ np->script0->startpos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, tryloop));
+
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+ /*
+ ** Clear Done Queue
+ */
+ for (i = 0; i < MAX_DONE; i++) {
+ np->ccb_done[i] = (struct ccb *)CCB_DONE_EMPTY;
+ np->scripth0->done_queue[5*i + 4] =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, done_end));
+ }
+#endif
+
+ /*
+ ** Start at first entry.
+ */
+ np->script0->done_pos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np,done_queue));
+ np->ccb_done_ic = MAX_DONE-1;
+ np->scripth0->done_queue[5*(MAX_DONE-1) + 4] =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, done_plug));
+
+ /*
+ ** Wakeup all pending jobs.
+ */
+ ncr_wakeup (np, code);
+
+ /*
+ ** Init chip.
+ */
+
+ /*
+ ** Remove reset; big delay because the 895 needs time for the
+ ** bus mode to settle
+ */
+ ncr_chip_reset(np, 2000);
+
+ OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+ /* full arb., ena parity, par->ATN */
+ OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */
+
+ ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
+
+ OUTB (nc_scid , RRE|np->myaddr); /* Adapter SCSI address */
+ OUTW (nc_respid, 1ul<<np->myaddr); /* Id to respond to */
+ OUTB (nc_istat , SIGP ); /* Signal Process */
+ OUTB (nc_dmode , np->rv_dmode); /* Burst length, dma mode */
+ OUTB (nc_ctest5, np->rv_ctest5); /* Large fifo + large burst */
+
+ OUTB (nc_dcntl , NOCOM|np->rv_dcntl); /* Protect SFBR */
+ OUTB (nc_ctest0, np->rv_ctest0); /* 720: CDIS and EHP */
+ OUTB (nc_ctest3, np->rv_ctest3); /* Write and invalidate */
+ OUTB (nc_ctest4, np->rv_ctest4); /* Master parity checking */
+
+ OUTB (nc_stest2, EXT|np->rv_stest2); /* Extended Sreq/Sack filtering */
+ OUTB (nc_stest3, TE); /* TolerANT enable */
+ OUTB (nc_stime0, 0x0c ); /* HTH disabled STO 0.25 sec */
+
+ /*
+ ** Disable disconnects.
+ */
+
+ np->disc = 0;
+
+ /*
+ ** Enable GPIO0 pin for writing if LED support.
+ */
+
+ if (np->features & FE_LED0) {
+ OUTOFFB (nc_gpcntl, 0x01);
+ }
+
+ /*
+ ** enable ints
+ */
+
+ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST|PAR);
+ OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
+
+ /*
+ ** Fill in target structure.
+ ** Reinitialize usrsync.
+ ** Reinitialize usrwide.
+ ** Prepare sync negotiation according to actual SCSI bus mode.
+ */
+
+ for (i=0;i<MAX_TARGET;i++) {
+ struct tcb *tp = &np->target[i];
+
+ tp->sval = 0;
+ tp->wval = np->rv_scntl3;
+
+ if (tp->usrsync != 255) {
+ if (tp->usrsync <= np->maxsync) {
+ if (tp->usrsync < np->minsync) {
+ tp->usrsync = np->minsync;
+ }
+ }
+ else
+ tp->usrsync = 255;
+ }
+
+ if (tp->usrwide > np->maxwide)
+ tp->usrwide = np->maxwide;
+
+ }
+
+ /*
+ ** Start script processor.
+ */
+ if (np->paddr2) {
+ if (bootverbose)
+ printk ("%s: Downloading SCSI SCRIPTS.\n",
+ ncr_name(np));
+ OUTL (nc_scratcha, vtobus(np->script0));
+ OUTL_DSP (NCB_SCRIPTH_PHYS (np, start_ram));
+ }
+ else
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, start));
+}
+
+/*==========================================================
+**
+** Prepare the negotiation values for wide and
+** synchronous transfers.
+**
+**==========================================================
+*/
+
+static void ncr_negotiate (struct ncb* np, struct tcb* tp)
+{
+ /*
+ ** minsync unit is 4ns !
+ */
+
+ u_long minsync = tp->usrsync;
+
+ /*
+ ** SCSI bus mode limit
+ */
+
+ if (np->scsi_mode && np->scsi_mode == SMODE_SE) {
+ if (minsync < 12) minsync = 12;
+ }
+
+ /*
+ ** our limit ..
+ */
+
+ if (minsync < np->minsync)
+ minsync = np->minsync;
+
+ /*
+ ** divider limit
+ */
+
+ if (minsync > np->maxsync)
+ minsync = 255;
+
+ if (tp->maxoffs > np->maxoffs)
+ tp->maxoffs = np->maxoffs;
+
+ tp->minsync = minsync;
+ tp->maxoffs = (minsync<255 ? tp->maxoffs : 0);
+
+ /*
+ ** period=0: has to negotiate sync transfer
+ */
+
+ tp->period=0;
+
+ /*
+ ** widedone=0: has to negotiate wide transfer
+ */
+ tp->widedone=0;
+}
+
+/*==========================================================
+**
+** Get clock factor and sync divisor for a given
+** synchronous factor period.
+** Returns the clock factor (in sxfer) and scntl3
+** synchronous divisor field.
+**
+**==========================================================
+*/
+
+static void ncr_getsync(struct ncb *np, u_char sfac, u_char *fakp, u_char *scntl3p)
+{
+ u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */
+ int div = np->clock_divn; /* Number of divisors supported */
+ u_long fak; /* Sync factor in sxfer */
+ u_long per; /* Period in tenths of ns */
+ u_long kpc; /* (per * clk) */
+
+ /*
+ ** Compute the synchronous period in tenths of nano-seconds
+ */
+ if (sfac <= 10) per = 250;
+ else if (sfac == 11) per = 303;
+ else if (sfac == 12) per = 500;
+ else per = 40 * sfac;
+
+ /*
+ ** Look for the greatest clock divisor that allows an
+ ** input speed faster than the period.
+ */
+ kpc = per * clk;
+ while (--div >= 0)
+ if (kpc >= (div_10M[div] << 2)) break;
+
+ /*
+ ** Calculate the lowest clock factor that allows an output
+ ** speed not faster than the period.
+ */
+ fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0 /* This optimization does not seem very useful */
+
+ per = (fak * div_10M[div]) / clk;
+
+ /*
+ ** Why not to try the immediate lower divisor and to choose
+ ** the one that allows the fastest output speed ?
+ ** We don't want input speed too much greater than output speed.
+ */
+ if (div >= 1 && fak < 8) {
+ u_long fak2, per2;
+ fak2 = (kpc - 1) / div_10M[div-1] + 1;
+ per2 = (fak2 * div_10M[div-1]) / clk;
+ if (per2 < per && fak2 <= 8) {
+ fak = fak2;
+ per = per2;
+ --div;
+ }
+ }
+#endif
+
+ if (fak < 4) fak = 4; /* Should never happen, too bad ... */
+
+ /*
+ ** Compute and return sync parameters for the ncr
+ */
+ *fakp = fak - 4;
+ *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+}
+
+
+/*==========================================================
+**
+** Set actual values, sync status and patch all ccbs of
+** a target according to new sync/wide agreement.
+**
+**==========================================================
+*/
+
+static void ncr_set_sync_wide_status (struct ncb *np, u_char target)
+{
+ struct ccb *cp;
+ struct tcb *tp = &np->target[target];
+
+ /*
+ ** set actual value and sync_status
+ */
+ OUTB (nc_sxfer, tp->sval);
+ np->sync_st = tp->sval;
+ OUTB (nc_scntl3, tp->wval);
+ np->wide_st = tp->wval;
+
+ /*
+ ** patch ALL ccbs of this target.
+ */
+ for (cp = np->ccb; cp; cp = cp->link_ccb) {
+ if (!cp->cmd) continue;
+ if (cp->cmd->device->id != target) continue;
+#if 0
+ cp->sync_status = tp->sval;
+ cp->wide_status = tp->wval;
+#endif
+ cp->phys.select.sel_scntl3 = tp->wval;
+ cp->phys.select.sel_sxfer = tp->sval;
+ }
+}
+
+/*==========================================================
+**
+** Switch sync mode for current job and it's target
+**
+**==========================================================
+*/
+
+static void ncr_setsync (struct ncb *np, struct ccb *cp, u_char scntl3, u_char sxfer)
+{
+ struct scsi_cmnd *cmd = cp->cmd;
+ struct tcb *tp;
+ u_char target = INB (nc_sdid) & 0x0f;
+ u_char idiv;
+
+ BUG_ON(target != (cmd->device->id & 0xf));
+
+ tp = &np->target[target];
+
+ if (!scntl3 || !(sxfer & 0x1f))
+ scntl3 = np->rv_scntl3;
+ scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+
+ /*
+ ** Deduce the value of controller sync period from scntl3.
+ ** period is in tenths of nano-seconds.
+ */
+
+ idiv = ((scntl3 >> 4) & 0x7);
+ if ((sxfer & 0x1f) && idiv)
+ tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+ else
+ tp->period = 0xffff;
+
+ /* Stop there if sync parameters are unchanged */
+ if (tp->sval == sxfer && tp->wval == scntl3)
+ return;
+ tp->sval = sxfer;
+ tp->wval = scntl3;
+
+ if (sxfer & 0x01f) {
+ /* Disable extended Sreq/Sack filtering */
+ if (tp->period <= 2000)
+ OUTOFFB(nc_stest2, EXT);
+ }
+
+ spi_display_xfer_agreement(tp->starget);
+
+ /*
+ ** set actual value and sync_status
+ ** patch ALL ccbs of this target.
+ */
+ ncr_set_sync_wide_status(np, target);
+}
+
+/*==========================================================
+**
+** Switch wide mode for current job and it's target
+** SCSI specs say: a SCSI device that accepts a WDTR
+** message shall reset the synchronous agreement to
+** asynchronous mode.
+**
+**==========================================================
+*/
+
+static void ncr_setwide (struct ncb *np, struct ccb *cp, u_char wide, u_char ack)
+{
+ struct scsi_cmnd *cmd = cp->cmd;
+ u16 target = INB (nc_sdid) & 0x0f;
+ struct tcb *tp;
+ u_char scntl3;
+ u_char sxfer;
+
+ BUG_ON(target != (cmd->device->id & 0xf));
+
+ tp = &np->target[target];
+ tp->widedone = wide+1;
+ scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
+
+ sxfer = ack ? 0 : tp->sval;
+
+ /*
+ ** Stop there if sync/wide parameters are unchanged
+ */
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
+ tp->sval = sxfer;
+ tp->wval = scntl3;
+
+ /*
+ ** Bells and whistles ;-)
+ */
+ if (bootverbose >= 2) {
+ dev_info(&cmd->device->sdev_target->dev, "WIDE SCSI %sabled.\n",
+ (scntl3 & EWS) ? "en" : "dis");
+ }
+
+ /*
+ ** set actual value and sync_status
+ ** patch ALL ccbs of this target.
+ */
+ ncr_set_sync_wide_status(np, target);
+}
+
+/*==========================================================
+**
+** Switch tagged mode for a target.
+**
+**==========================================================
+*/
+
+static void ncr_setup_tags (struct ncb *np, struct scsi_device *sdev)
+{
+ unsigned char tn = sdev->id, ln = sdev->lun;
+ struct tcb *tp = &np->target[tn];
+ struct lcb *lp = tp->lp[ln];
+ u_char reqtags, maxdepth;
+
+ /*
+ ** Just in case ...
+ */
+ if ((!tp) || (!lp) || !sdev)
+ return;
+
+ /*
+ ** If SCSI device queue depth is not yet set, leave here.
+ */
+ if (!lp->scdev_depth)
+ return;
+
+ /*
+ ** Donnot allow more tags than the SCSI driver can queue
+ ** for this device.
+ ** Donnot allow more tags than we can handle.
+ */
+ maxdepth = lp->scdev_depth;
+ if (maxdepth > lp->maxnxs) maxdepth = lp->maxnxs;
+ if (lp->maxtags > maxdepth) lp->maxtags = maxdepth;
+ if (lp->numtags > maxdepth) lp->numtags = maxdepth;
+
+ /*
+ ** only devices conformant to ANSI Version >= 2
+ ** only devices capable of tagged commands
+ ** only if enabled by user ..
+ */
+ if (sdev->tagged_supported && lp->numtags > 1) {
+ reqtags = lp->numtags;
+ } else {
+ reqtags = 1;
+ }
+
+ /*
+ ** Update max number of tags
+ */
+ lp->numtags = reqtags;
+ if (lp->numtags > lp->maxtags)
+ lp->maxtags = lp->numtags;
+
+ /*
+ ** If we want to switch tag mode, we must wait
+ ** for no CCB to be active.
+ */
+ if (reqtags > 1 && lp->usetags) { /* Stay in tagged mode */
+ if (lp->queuedepth == reqtags) /* Already announced */
+ return;
+ lp->queuedepth = reqtags;
+ }
+ else if (reqtags <= 1 && !lp->usetags) { /* Stay in untagged mode */
+ lp->queuedepth = reqtags;
+ return;
+ }
+ else { /* Want to switch tag mode */
+ if (lp->busyccbs) /* If not yet safe, return */
+ return;
+ lp->queuedepth = reqtags;
+ lp->usetags = reqtags > 1 ? 1 : 0;
+ }
+
+ /*
+ ** Patch the lun mini-script, according to tag mode.
+ */
+ lp->jump_tag.l_paddr = lp->usetags?
+ cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_tag)) :
+ cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_notag));
+
+ /*
+ ** Announce change to user.
+ */
+ if (bootverbose) {
+ if (lp->usetags) {
+ dev_info(&sdev->sdev_gendev,
+ "tagged command queue depth set to %d\n",
+ reqtags);
+ } else {
+ dev_info(&sdev->sdev_gendev,
+ "tagged command queueing disabled\n");
+ }
+ }
+}
+
+/*==========================================================
+**
+**
+** ncr timeout handler.
+**
+**
+**==========================================================
+**
+** Misused to keep the driver running when
+** interrupts are not configured correctly.
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_timeout (struct ncb *np)
+{
+ u_long thistime = ktime_get(0);
+
+ /*
+ ** If release process in progress, let's go
+ ** Set the release stage from 1 to 2 to synchronize
+ ** with the release process.
+ */
+
+ if (np->release_stage) {
+ if (np->release_stage == 1) np->release_stage = 2;
+ return;
+ }
+
+ np->timer.expires = ktime_get(SCSI_NCR_TIMER_INTERVAL);
+ add_timer(&np->timer);
+
+ /*
+ ** If we are resetting the ncr, wait for settle_time before
+ ** clearing it. Then command processing will be resumed.
+ */
+ if (np->settle_time) {
+ if (np->settle_time <= thistime) {
+ if (bootverbose > 1)
+ printk("%s: command processing resumed\n", ncr_name(np));
+ np->settle_time = 0;
+ np->disc = 1;
+ requeue_waiting_list(np);
+ }
+ return;
+ }
+
+ /*
+ ** Since the generic scsi driver only allows us 0.5 second
+ ** to perform abort of a command, we must look at ccbs about
+ ** every 0.25 second.
+ */
+ if (np->lasttime + 4*HZ < thistime) {
+ /*
+ ** block ncr interrupts
+ */
+ np->lasttime = thistime;
+ }
+
+#ifdef SCSI_NCR_BROKEN_INTR
+ if (INB(nc_istat) & (INTF|SIP|DIP)) {
+
+ /*
+ ** Process pending interrupts.
+ */
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("{");
+ ncr_exception (np);
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("}");
+ }
+#endif /* SCSI_NCR_BROKEN_INTR */
+}
+
+/*==========================================================
+**
+** log message for real hard errors
+**
+** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+** exception register:
+** ds: dstat
+** si: sist
+**
+** SCSI bus lines:
+** so: control lines as driver by NCR.
+** si: control lines as seen by NCR.
+** sd: scsi data lines as seen by NCR.
+**
+** wide/fastmode:
+** sxfer: (see the manual)
+** scntl3: (see the manual)
+**
+** current script command:
+** dsp: script address (relative to start of script).
+** dbc: first word of script command.
+**
+** First 16 register of the chip:
+** r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(struct ncb *np, u16 sist, u_char dstat)
+{
+ u32 dsp;
+ int script_ofs;
+ int script_size;
+ char *script_name;
+ u_char *script_base;
+ int i;
+
+ dsp = INL (nc_dsp);
+
+ if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+ script_ofs = dsp - np->p_script;
+ script_size = sizeof(struct script);
+ script_base = (u_char *) np->script0;
+ script_name = "script";
+ }
+ else if (np->p_scripth < dsp &&
+ dsp <= np->p_scripth + sizeof(struct scripth)) {
+ script_ofs = dsp - np->p_scripth;
+ script_size = sizeof(struct scripth);
+ script_base = (u_char *) np->scripth0;
+ script_name = "scripth";
+ } else {
+ script_ofs = dsp;
+ script_size = 0;
+ script_base = NULL;
+ script_name = "mem";
+ }
+
+ printk ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
+ ncr_name (np), (unsigned)INB (nc_sdid)&0x0f, dstat, sist,
+ (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
+ (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
+ (unsigned)INL (nc_dbc));
+
+ if (((script_ofs & 3) == 0) &&
+ (unsigned)script_ofs < script_size) {
+ printk ("%s: script cmd = %08x\n", ncr_name(np),
+ scr_to_cpu((int) *(ncrcmd *)(script_base + script_ofs)));
+ }
+
+ printk ("%s: regdump:", ncr_name(np));
+ for (i=0; i<16;i++)
+ printk (" %02x", (unsigned)INB_OFF(i));
+ printk (".\n");
+}
+
+/*============================================================
+**
+** ncr chip exception handler.
+**
+**============================================================
+**
+** In normal cases, interrupt conditions occur one at a
+** time. The ncr is able to stack in some extra registers
+** other interrupts that will occurs after the first one.
+** But severall interrupts may occur at the same time.
+**
+** We probably should only try to deal with the normal
+** case, but it seems that multiple interrupts occur in
+** some cases that are not abnormal at all.
+**
+** The most frequent interrupt condition is Phase Mismatch.
+** We should want to service this interrupt quickly.
+** A SCSI parity error may be delivered at the same time.
+** The SIR interrupt is not very frequent in this driver,
+** since the INTFLY is likely used for command completion
+** signaling.
+** The Selection Timeout interrupt may be triggered with
+** IID and/or UDC.
+** The SBMC interrupt (SCSI Bus Mode Change) may probably
+** occur at any time.
+**
+** This handler try to deal as cleverly as possible with all
+** the above.
+**
+**============================================================
+*/
+
+void ncr_exception (struct ncb *np)
+{
+ u_char istat, dstat;
+ u16 sist;
+ int i;
+
+ /*
+ ** interrupt on the fly ?
+ ** Since the global header may be copied back to a CCB
+ ** using a posted PCI memory write, the last operation on
+ ** the istat register is a READ in order to flush posted
+ ** PCI write commands.
+ */
+ istat = INB (nc_istat);
+ if (istat & INTF) {
+ OUTB (nc_istat, (istat & SIGP) | INTF);
+ istat = INB (nc_istat);
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("F ");
+ ncr_wakeup_done (np);
+ }
+
+ if (!(istat & (SIP|DIP)))
+ return;
+
+ if (istat & CABRT)
+ OUTB (nc_istat, CABRT);
+
+ /*
+ ** Steinbach's Guideline for Systems Programming:
+ ** Never test for an error condition you don't know how to handle.
+ */
+
+ sist = (istat & SIP) ? INW (nc_sist) : 0;
+ dstat = (istat & DIP) ? INB (nc_dstat) : 0;
+
+ if (DEBUG_FLAGS & DEBUG_TINY)
+ printk ("<%d|%x:%x|%x:%x>",
+ (int)INB(nc_scr0),
+ dstat,sist,
+ (unsigned)INL(nc_dsp),
+ (unsigned)INL(nc_dbc));
+
+ /*========================================================
+ ** First, interrupts we want to service cleanly.
+ **
+ ** Phase mismatch is the most frequent interrupt, and
+ ** so we have to service it as quickly and as cleanly
+ ** as possible.
+ ** Programmed interrupts are rarely used in this driver,
+ ** but we must handle them cleanly anyway.
+ ** We try to deal with PAR and SBMC combined with
+ ** some other interrupt(s).
+ **=========================================================
+ */
+
+ if (!(sist & (STO|GEN|HTH|SGE|UDC|RST)) &&
+ !(dstat & (MDPE|BF|ABRT|IID))) {
+ if ((sist & SBMC) && ncr_int_sbmc (np))
+ return;
+ if ((sist & PAR) && ncr_int_par (np))
+ return;
+ if (sist & MA) {
+ ncr_int_ma (np);
+ return;
+ }
+ if (dstat & SIR) {
+ ncr_int_sir (np);
+ return;
+ }
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 2.
+ */
+ if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) {
+ printk( "%s: unknown interrupt(s) ignored, "
+ "ISTAT=%x DSTAT=%x SIST=%x\n",
+ ncr_name(np), istat, dstat, sist);
+ return;
+ }
+ OUTONB_STD ();
+ return;
+ }
+
+ /*========================================================
+ ** Now, interrupts that need some fixing up.
+ ** Order and multiple interrupts is so less important.
+ **
+ ** If SRST has been asserted, we just reset the chip.
+ **
+ ** Selection is intirely handled by the chip. If the
+ ** chip says STO, we trust it. Seems some other
+ ** interrupts may occur at the same time (UDC, IID), so
+ ** we ignore them. In any case we do enough fix-up
+ ** in the service routine.
+ ** We just exclude some fatal dma errors.
+ **=========================================================
+ */
+
+ if (sist & RST) {
+ ncr_init (np, 1, bootverbose ? "scsi reset" : NULL, HS_RESET);
+ return;
+ }
+
+ if ((sist & STO) &&
+ !(dstat & (MDPE|BF|ABRT))) {
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 1.
+ */
+ OUTONB (nc_ctest3, CLF);
+
+ ncr_int_sto (np);
+ return;
+ }
+
+ /*=========================================================
+ ** Now, interrupts we are not able to recover cleanly.
+ ** (At least for the moment).
+ **
+ ** Do the register dump.
+ ** Log message for real hard errors.
+ ** Clear all fifos.
+ ** For MDPE, BF, ABORT, IID, SGE and HTH we reset the
+ ** BUS and the chip.
+ ** We are more soft for UDC.
+ **=========================================================
+ */
+
+ if (ktime_exp(np->regtime)) {
+ np->regtime = ktime_get(10*HZ);
+ for (i = 0; i<sizeof(np->regdump); i++)
+ ((char*)&np->regdump)[i] = INB_OFF(i);
+ np->regdump.nc_dstat = dstat;
+ np->regdump.nc_sist = sist;
+ }
+
+ ncr_log_hard_error(np, sist, dstat);
+
+ printk ("%s: have to clear fifos.\n", ncr_name (np));
+ OUTB (nc_stest3, TE|CSF);
+ OUTONB (nc_ctest3, CLF);
+
+ if ((sist & (SGE)) ||
+ (dstat & (MDPE|BF|ABRT|IID))) {
+ ncr_start_reset(np);
+ return;
+ }
+
+ if (sist & HTH) {
+ printk ("%s: handshake timeout\n", ncr_name(np));
+ ncr_start_reset(np);
+ return;
+ }
+
+ if (sist & UDC) {
+ printk ("%s: unexpected disconnect\n", ncr_name(np));
+ OUTB (HS_PRT, HS_UNEXPECTED);
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, cleanup));
+ return;
+ }
+
+ /*=========================================================
+ ** We just miss the cause of the interrupt. :(
+ ** Print a message. The timeout will do the real work.
+ **=========================================================
+ */
+ printk ("%s: unknown interrupt\n", ncr_name(np));
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for selection timeout
+**
+**==========================================================
+**
+** There seems to be a bug in the 53c810.
+** Although a STO-Interrupt is pending,
+** it continues executing script commands.
+** But it will fail and interrupt (IID) on
+** the next instruction where it's looking
+** for a valid phase.
+**
+**----------------------------------------------------------
+*/
+
+void ncr_int_sto (struct ncb *np)
+{
+ u_long dsa;
+ struct ccb *cp;
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("T");
+
+ /*
+ ** look for ccb and set the status.
+ */
+
+ dsa = INL (nc_dsa);
+ cp = np->ccb;
+ while (cp && (CCB_PHYS (cp, phys) != dsa))
+ cp = cp->link_ccb;
+
+ if (cp) {
+ cp-> host_status = HS_SEL_TIMEOUT;
+ ncr_complete (np, cp);
+ }
+
+ /*
+ ** repair start queue and jump to start point.
+ */
+
+ OUTL_DSP (NCB_SCRIPTH_PHYS (np, sto_restart));
+ return;
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for SCSI bus mode change
+**
+**==========================================================
+**
+** spi2-r12 11.2.3 says a transceiver mode change must
+** generate a reset event and a device that detects a reset
+** event shall initiate a hard reset. It says also that a
+** device that detects a mode change shall set data transfer
+** mode to eight bit asynchronous, etc...
+** So, just resetting should be enough.
+**
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_sbmc (struct ncb *np)
+{
+ u_char scsi_mode = INB (nc_stest4) & SMODE;
+
+ if (scsi_mode != np->scsi_mode) {
+ printk("%s: SCSI bus mode change from %x to %x.\n",
+ ncr_name(np), np->scsi_mode, scsi_mode);
+
+ np->scsi_mode = scsi_mode;
+
+
+ /*
+ ** Suspend command processing for 1 second and
+ ** reinitialize all except the chip.
+ */
+ np->settle_time = ktime_get(1*HZ);
+ ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET);
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for SCSI parity error.
+**
+**==========================================================
+**
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_par (struct ncb *np)
+{
+ u_char hsts = INB (HS_PRT);
+ u32 dbc = INL (nc_dbc);
+ u_char sstat1 = INB (nc_sstat1);
+ int phase = -1;
+ int msg = -1;
+ u32 jmp;
+
+ printk("%s: SCSI parity error detected: SCR1=%d DBC=%x SSTAT1=%x\n",
+ ncr_name(np), hsts, dbc, sstat1);
+
+ /*
+ * Ignore the interrupt if the NCR is not connected
+ * to the SCSI bus, since the right work should have
+ * been done on unexpected disconnection handling.
+ */
+ if (!(INB (nc_scntl1) & ISCON))
+ return 0;
+
+ /*
+ * If the nexus is not clearly identified, reset the bus.
+ * We will try to do better later.
+ */
+ if (hsts & HS_INVALMASK)
+ goto reset_all;
+
+ /*
+ * If the SCSI parity error occurs in MSG IN phase, prepare a
+ * MSG PARITY message. Otherwise, prepare a INITIATOR DETECTED
+ * ERROR message and let the device decide to retry the command
+ * or to terminate with check condition. If we were in MSG IN
+ * phase waiting for the response of a negotiation, we will
+ * get SIR_NEGO_FAILED at dispatch.
+ */
+ if (!(dbc & 0xc0000000))
+ phase = (dbc >> 24) & 7;
+ if (phase == 7)
+ msg = M_PARITY;
+ else
+ msg = M_ID_ERROR;
+
+
+ /*
+ * If the NCR stopped on a MOVE ^ DATA_IN, we jump to a
+ * script that will ignore all data in bytes until phase
+ * change, since we are not sure the chip will wait the phase
+ * change prior to delivering the interrupt.
+ */
+ if (phase == 1)
+ jmp = NCB_SCRIPTH_PHYS (np, par_err_data_in);
+ else
+ jmp = NCB_SCRIPTH_PHYS (np, par_err_other);
+
+ OUTONB (nc_ctest3, CLF ); /* clear dma fifo */
+ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+
+ np->msgout[0] = msg;
+ OUTL_DSP (jmp);
+ return 1;
+
+reset_all:
+ ncr_start_reset(np);
+ return 1;
+}
+
+/*==========================================================
+**
+**
+** ncr chip exception handler for phase errors.
+**
+**
+**==========================================================
+**
+** We have to construct a new transfer descriptor,
+** to transfer the rest of the current block.
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_int_ma (struct ncb *np)
+{
+ u32 dbc;
+ u32 rest;
+ u32 dsp;
+ u32 dsa;
+ u32 nxtdsp;
+ u32 newtmp;
+ u32 *vdsp;
+ u32 oadr, olen;
+ u32 *tblp;
+ ncrcmd *newcmd;
+ u_char cmd, sbcl;
+ struct ccb *cp;
+
+ dsp = INL (nc_dsp);
+ dbc = INL (nc_dbc);
+ sbcl = INB (nc_sbcl);
+
+ cmd = dbc >> 24;
+ rest = dbc & 0xffffff;
+
+ /*
+ ** Take into account dma fifo and various buffers and latches,
+ ** only if the interrupted phase is an OUTPUT phase.
+ */
+
+ if ((cmd & 1) == 0) {
+ u_char ctest5, ss0, ss2;
+ u16 delta;
+
+ ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+ if (ctest5 & DFS)
+ delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+ else
+ delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+ /*
+ ** The data in the dma fifo has not been transferred to
+ ** the target -> add the amount to the rest
+ ** and clear the data.
+ ** Check the sstat2 register in case of wide transfer.
+ */
+
+ rest += delta;
+ ss0 = INB (nc_sstat0);
+ if (ss0 & OLF) rest++;
+ if (ss0 & ORF) rest++;
+ if (INB(nc_scntl3) & EWS) {
+ ss2 = INB (nc_sstat2);
+ if (ss2 & OLF1) rest++;
+ if (ss2 & ORF1) rest++;
+ }
+
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printk ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
+ (unsigned) rest, (unsigned) delta, ss0);
+
+ } else {
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printk ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
+ }
+
+ /*
+ ** Clear fifos.
+ */
+ OUTONB (nc_ctest3, CLF ); /* clear dma fifo */
+ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+
+ /*
+ ** locate matching cp.
+ ** if the interrupted phase is DATA IN or DATA OUT,
+ ** trust the global header.
+ */
+ dsa = INL (nc_dsa);
+ if (!(cmd & 6)) {
+ cp = np->header.cp;
+ if (CCB_PHYS(cp, phys) != dsa)
+ cp = NULL;
+ } else {
+ cp = np->ccb;
+ while (cp && (CCB_PHYS (cp, phys) != dsa))
+ cp = cp->link_ccb;
+ }
+
+ /*
+ ** try to find the interrupted script command,
+ ** and the address at which to continue.
+ */
+ vdsp = NULL;
+ nxtdsp = 0;
+ if (dsp > np->p_script &&
+ dsp <= np->p_script + sizeof(struct script)) {
+ vdsp = (u32 *)((char*)np->script0 + (dsp-np->p_script-8));
+ nxtdsp = dsp;
+ }
+ else if (dsp > np->p_scripth &&
+ dsp <= np->p_scripth + sizeof(struct scripth)) {
+ vdsp = (u32 *)((char*)np->scripth0 + (dsp-np->p_scripth-8));
+ nxtdsp = dsp;
+ }
+ else if (cp) {
+ if (dsp == CCB_PHYS (cp, patch[2])) {
+ vdsp = &cp->patch[0];
+ nxtdsp = scr_to_cpu(vdsp[3]);
+ }
+ else if (dsp == CCB_PHYS (cp, patch[6])) {
+ vdsp = &cp->patch[4];
+ nxtdsp = scr_to_cpu(vdsp[3]);
+ }
+ }
+
+ /*
+ ** log the information
+ */
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ printk ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
+ cp, np->header.cp,
+ (unsigned)dsp,
+ (unsigned)nxtdsp, vdsp, cmd);
+ }
+
+ /*
+ ** cp=0 means that the DSA does not point to a valid control
+ ** block. This should not happen since we donnot use multi-byte
+ ** move while we are being reselected ot after command complete.
+ ** We are not able to recover from such a phase error.
+ */
+ if (!cp) {
+ printk ("%s: SCSI phase error fixup: "
+ "CCB already dequeued (0x%08lx)\n",
+ ncr_name (np), (u_long) np->header.cp);
+ goto reset_all;
+ }
+
+ /*
+ ** get old startaddress and old length.
+ */
+
+ oadr = scr_to_cpu(vdsp[1]);
+
+ if (cmd & 0x10) { /* Table indirect */
+ tblp = (u32 *) ((char*) &cp->phys + oadr);
+ olen = scr_to_cpu(tblp[0]);
+ oadr = scr_to_cpu(tblp[1]);
+ } else {
+ tblp = (u32 *) 0;
+ olen = scr_to_cpu(vdsp[0]) & 0xffffff;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ printk ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
+ (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
+ tblp,
+ (unsigned) olen,
+ (unsigned) oadr);
+ }
+
+ /*
+ ** check cmd against assumed interrupted script command.
+ */
+
+ if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
+ PRINT_ADDR(cp->cmd, "internal error: cmd=%02x != %02x=(vdsp[0] "
+ ">> 24)\n", cmd, scr_to_cpu(vdsp[0]) >> 24);
+
+ goto reset_all;
+ }
+
+ /*
+ ** cp != np->header.cp means that the header of the CCB
+ ** currently being processed has not yet been copied to
+ ** the global header area. That may happen if the device did
+ ** not accept all our messages after having been selected.
+ */
+ if (cp != np->header.cp) {
+ printk ("%s: SCSI phase error fixup: "
+ "CCB address mismatch (0x%08lx != 0x%08lx)\n",
+ ncr_name (np), (u_long) cp, (u_long) np->header.cp);
+ }
+
+ /*
+ ** if old phase not dataphase, leave here.
+ */
+
+ if (cmd & 0x06) {
+ PRINT_ADDR(cp->cmd, "phase change %x-%x %d@%08x resid=%d.\n",
+ cmd&7, sbcl&7, (unsigned)olen,
+ (unsigned)oadr, (unsigned)rest);
+ goto unexpected_phase;
+ }
+
+ /*
+ ** choose the correct patch area.
+ ** if savep points to one, choose the other.
+ */
+
+ newcmd = cp->patch;
+ newtmp = CCB_PHYS (cp, patch);
+ if (newtmp == scr_to_cpu(cp->phys.header.savep)) {
+ newcmd = &cp->patch[4];
+ newtmp = CCB_PHYS (cp, patch[4]);
+ }
+
+ /*
+ ** fillin the commands
+ */
+
+ newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
+ newcmd[1] = cpu_to_scr(oadr + olen - rest);
+ newcmd[2] = cpu_to_scr(SCR_JUMP);
+ newcmd[3] = cpu_to_scr(nxtdsp);
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ PRINT_ADDR(cp->cmd, "newcmd[%d] %x %x %x %x.\n",
+ (int) (newcmd - cp->patch),
+ (unsigned)scr_to_cpu(newcmd[0]),
+ (unsigned)scr_to_cpu(newcmd[1]),
+ (unsigned)scr_to_cpu(newcmd[2]),
+ (unsigned)scr_to_cpu(newcmd[3]));
+ }
+ /*
+ ** fake the return address (to the patch).
+ ** and restart script processor at dispatcher.
+ */
+ OUTL (nc_temp, newtmp);
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, dispatch));
+ return;
+
+ /*
+ ** Unexpected phase changes that occurs when the current phase
+ ** is not a DATA IN or DATA OUT phase are due to error conditions.
+ ** Such event may only happen when the SCRIPTS is using a
+ ** multibyte SCSI MOVE.
+ **
+ ** Phase change Some possible cause
+ **
+ ** COMMAND --> MSG IN SCSI parity error detected by target.
+ ** COMMAND --> STATUS Bad command or refused by target.
+ ** MSG OUT --> MSG IN Message rejected by target.
+ ** MSG OUT --> COMMAND Bogus target that discards extended
+ ** negotiation messages.
+ **
+ ** The code below does not care of the new phase and so
+ ** trusts the target. Why to annoy it ?
+ ** If the interrupted phase is COMMAND phase, we restart at
+ ** dispatcher.
+ ** If a target does not get all the messages after selection,
+ ** the code assumes blindly that the target discards extended
+ ** messages and clears the negotiation status.
+ ** If the target does not want all our response to negotiation,
+ ** we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids
+ ** bloat for such a should_not_happen situation).
+ ** In all other situation, we reset the BUS.
+ ** Are these assumptions reasonnable ? (Wait and see ...)
+ */
+unexpected_phase:
+ dsp -= 8;
+ nxtdsp = 0;
+
+ switch (cmd & 7) {
+ case 2: /* COMMAND phase */
+ nxtdsp = NCB_SCRIPT_PHYS (np, dispatch);
+ break;
+#if 0
+ case 3: /* STATUS phase */
+ nxtdsp = NCB_SCRIPT_PHYS (np, dispatch);
+ break;
+#endif
+ case 6: /* MSG OUT phase */
+ np->scripth->nxtdsp_go_on[0] = cpu_to_scr(dsp + 8);
+ if (dsp == NCB_SCRIPT_PHYS (np, send_ident)) {
+ cp->host_status = HS_BUSY;
+ nxtdsp = NCB_SCRIPTH_PHYS (np, clratn_go_on);
+ }
+ else if (dsp == NCB_SCRIPTH_PHYS (np, send_wdtr) ||
+ dsp == NCB_SCRIPTH_PHYS (np, send_sdtr)) {
+ nxtdsp = NCB_SCRIPTH_PHYS (np, nego_bad_phase);
+ }
+ break;
+#if 0
+ case 7: /* MSG IN phase */
+ nxtdsp = NCB_SCRIPT_PHYS (np, clrack);
+ break;
+#endif
+ }
+
+ if (nxtdsp) {
+ OUTL_DSP (nxtdsp);
+ return;
+ }
+
+reset_all:
+ ncr_start_reset(np);
+}
+
+
+static void ncr_sir_to_redo(struct ncb *np, int num, struct ccb *cp)
+{
+ struct scsi_cmnd *cmd = cp->cmd;
+ struct tcb *tp = &np->target[cmd->device->id];
+ struct lcb *lp = tp->lp[cmd->device->lun];
+ struct list_head *qp;
+ struct ccb * cp2;
+ int disc_cnt = 0;
+ int busy_cnt = 0;
+ u32 startp;
+ u_char s_status = INB (SS_PRT);
+
+ /*
+ ** Let the SCRIPTS processor skip all not yet started CCBs,
+ ** and count disconnected CCBs. Since the busy queue is in
+ ** the same order as the chip start queue, disconnected CCBs
+ ** are before cp and busy ones after.
+ */
+ if (lp) {
+ qp = lp->busy_ccbq.prev;
+ while (qp != &lp->busy_ccbq) {
+ cp2 = list_entry(qp, struct ccb, link_ccbq);
+ qp = qp->prev;
+ ++busy_cnt;
+ if (cp2 == cp)
+ break;
+ cp2->start.schedule.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, skip));
+ }
+ lp->held_ccb = cp; /* Requeue when this one completes */
+ disc_cnt = lp->queuedccbs - busy_cnt;
+ }
+
+ switch(s_status) {
+ default: /* Just for safety, should never happen */
+ case S_QUEUE_FULL:
+ /*
+ ** Decrease number of tags to the number of
+ ** disconnected commands.
+ */
+ if (!lp)
+ goto out;
+ if (bootverbose >= 1) {
+ PRINT_ADDR(cmd, "QUEUE FULL! %d busy, %d disconnected "
+ "CCBs\n", busy_cnt, disc_cnt);
+ }
+ if (disc_cnt < lp->numtags) {
+ lp->numtags = disc_cnt > 2 ? disc_cnt : 2;
+ lp->num_good = 0;
+ ncr_setup_tags (np, cmd->device);
+ }
+ /*
+ ** Requeue the command to the start queue.
+ ** If any disconnected commands,
+ ** Clear SIGP.
+ ** Jump to reselect.
+ */
+ cp->phys.header.savep = cp->startp;
+ cp->host_status = HS_BUSY;
+ cp->scsi_status = S_ILLEGAL;
+
+ ncr_put_start_queue(np, cp);
+ if (disc_cnt)
+ INB (nc_ctest2); /* Clear SIGP */
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, reselect));
+ return;
+ case S_TERMINATED:
+ case S_CHECK_COND:
+ /*
+ ** If we were requesting sense, give up.
+ */
+ if (cp->auto_sense)
+ goto out;
+
+ /*
+ ** Device returned CHECK CONDITION status.
+ ** Prepare all needed data strutures for getting
+ ** sense data.
+ **
+ ** identify message
+ */
+ cp->scsi_smsg2[0] = IDENTIFY(0, cmd->device->lun);
+ cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
+ cp->phys.smsg.size = cpu_to_scr(1);
+
+ /*
+ ** sense command
+ */
+ cp->phys.cmd.addr = cpu_to_scr(CCB_PHYS (cp, sensecmd));
+ cp->phys.cmd.size = cpu_to_scr(6);
+
+ /*
+ ** patch requested size into sense command
+ */
+ cp->sensecmd[0] = 0x03;
+ cp->sensecmd[1] = cmd->device->lun << 5;
+ cp->sensecmd[4] = sizeof(cp->sense_buf);
+
+ /*
+ ** sense data
+ */
+ memset(cp->sense_buf, 0, sizeof(cp->sense_buf));
+ cp->phys.sense.addr = cpu_to_scr(CCB_PHYS(cp,sense_buf[0]));
+ cp->phys.sense.size = cpu_to_scr(sizeof(cp->sense_buf));
+
+ /*
+ ** requeue the command.
+ */
+ startp = cpu_to_scr(NCB_SCRIPTH_PHYS (np, sdata_in));
+
+ cp->phys.header.savep = startp;
+ cp->phys.header.goalp = startp + 24;
+ cp->phys.header.lastp = startp;
+ cp->phys.header.wgoalp = startp + 24;
+ cp->phys.header.wlastp = startp;
+
+ cp->host_status = HS_BUSY;
+ cp->scsi_status = S_ILLEGAL;
+ cp->auto_sense = s_status;
+
+ cp->start.schedule.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+
+ /*
+ ** Select without ATN for quirky devices.
+ */
+ if (cmd->device->select_no_atn)
+ cp->start.schedule.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, select_no_atn));
+
+ ncr_put_start_queue(np, cp);
+
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, start));
+ return;
+ }
+
+out:
+ OUTONB_STD ();
+ return;
+}
+
+
+/*==========================================================
+**
+**
+** ncr chip exception handler for programmed interrupts.
+**
+**
+**==========================================================
+*/
+
+void ncr_int_sir (struct ncb *np)
+{
+ u_char scntl3;
+ u_char chg, ofs, per, fak, wide;
+ u_char num = INB (nc_dsps);
+ struct ccb *cp=NULL;
+ u_long dsa = INL (nc_dsa);
+ u_char target = INB (nc_sdid) & 0x0f;
+ struct tcb *tp = &np->target[target];
+ struct scsi_target *starget = tp->starget;
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num);
+
+ switch (num) {
+ case SIR_INTFLY:
+ /*
+ ** This is used for HP Zalon/53c720 where INTFLY
+ ** operation is currently broken.
+ */
+ ncr_wakeup_done(np);
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+ OUTL(nc_dsp, NCB_SCRIPT_PHYS (np, done_end) + 8);
+#else
+ OUTL(nc_dsp, NCB_SCRIPT_PHYS (np, start));
+#endif
+ return;
+ case SIR_RESEL_NO_MSG_IN:
+ case SIR_RESEL_NO_IDENTIFY:
+ /*
+ ** If devices reselecting without sending an IDENTIFY
+ ** message still exist, this should help.
+ ** We just assume lun=0, 1 CCB, no tag.
+ */
+ if (tp->lp[0]) {
+ OUTL_DSP (scr_to_cpu(tp->lp[0]->jump_ccb[0]));
+ return;
+ }
+ case SIR_RESEL_BAD_TARGET: /* Will send a TARGET RESET message */
+ case SIR_RESEL_BAD_LUN: /* Will send a TARGET RESET message */
+ case SIR_RESEL_BAD_I_T_L_Q: /* Will send an ABORT TAG message */
+ case SIR_RESEL_BAD_I_T_L: /* Will send an ABORT message */
+ printk ("%s:%d: SIR %d, "
+ "incorrect nexus identification on reselection\n",
+ ncr_name (np), target, num);
+ goto out;
+ case SIR_DONE_OVERFLOW:
+ printk ("%s:%d: SIR %d, "
+ "CCB done queue overflow\n",
+ ncr_name (np), target, num);
+ goto out;
+ case SIR_BAD_STATUS:
+ cp = np->header.cp;
+ if (!cp || CCB_PHYS (cp, phys) != dsa)
+ goto out;
+ ncr_sir_to_redo(np, num, cp);
+ return;
+ default:
+ /*
+ ** lookup the ccb
+ */
+ cp = np->ccb;
+ while (cp && (CCB_PHYS (cp, phys) != dsa))
+ cp = cp->link_ccb;
+
+ BUG_ON(!cp);
+ BUG_ON(cp != np->header.cp);
+
+ if (!cp || cp != np->header.cp)
+ goto out;
+ }
+
+ switch (num) {
+/*-----------------------------------------------------------------------------
+**
+** Was Sie schon immer ueber transfermode negotiation wissen wollten ...
+**
+** We try to negotiate sync and wide transfer only after
+** a successful inquire command. We look at byte 7 of the
+** inquire data to determine the capabilities of the target.
+**
+** When we try to negotiate, we append the negotiation message
+** to the identify and (maybe) simple tag message.
+** The host status field is set to HS_NEGOTIATE to mark this
+** situation.
+**
+** If the target doesn't answer this message immidiately
+** (as required by the standard), the SIR_NEGO_FAIL interrupt
+** will be raised eventually.
+** The handler removes the HS_NEGOTIATE status, and sets the
+** negotiated value to the default (async / nowide).
+**
+** If we receive a matching answer immediately, we check it
+** for validity, and set the values.
+**
+** If we receive a Reject message immediately, we assume the
+** negotiation has failed, and fall back to standard values.
+**
+** If we receive a negotiation message while not in HS_NEGOTIATE
+** state, it's a target initiated negotiation. We prepare a
+** (hopefully) valid answer, set our parameters, and send back
+** this answer to the target.
+**
+** If the target doesn't fetch the answer (no message out phase),
+** we assume the negotiation has failed, and fall back to default
+** settings.
+**
+** When we set the values, we adjust them in all ccbs belonging
+** to this target, in the controller's register, and in the "phys"
+** field of the controller's struct ncb.
+**
+** Possible cases: hs sir msg_in value send goto
+** We try to negotiate:
+** -> target doesn't msgin NEG FAIL noop defa. - dispatch
+** -> target rejected our msg NEG FAIL reject defa. - dispatch
+** -> target answered (ok) NEG SYNC sdtr set - clrack
+** -> target answered (!ok) NEG SYNC sdtr defa. REJ--->msg_bad
+** -> target answered (ok) NEG WIDE wdtr set - clrack
+** -> target answered (!ok) NEG WIDE wdtr defa. REJ--->msg_bad
+** -> any other msgin NEG FAIL noop defa. - dispatch
+**
+** Target tries to negotiate:
+** -> incoming message --- SYNC sdtr set SDTR -
+** -> incoming message --- WIDE wdtr set WDTR -
+** We sent our answer:
+** -> target doesn't msgout --- PROTO ? defa. - dispatch
+**
+**-----------------------------------------------------------------------------
+*/
+
+ case SIR_NEGO_FAILED:
+ /*-------------------------------------------------------
+ **
+ ** Negotiation failed.
+ ** Target doesn't send an answer message,
+ ** or target rejected our message.
+ **
+ ** Remove negotiation request.
+ **
+ **-------------------------------------------------------
+ */
+ OUTB (HS_PRT, HS_BUSY);
+
+ /* fall through */
+
+ case SIR_NEGO_PROTO:
+ /*-------------------------------------------------------
+ **
+ ** Negotiation failed.
+ ** Target doesn't fetch the answer message.
+ **
+ **-------------------------------------------------------
+ */
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd, "negotiation failed sir=%x "
+ "status=%x.\n", num, cp->nego_status);
+ }
+
+ /*
+ ** any error in negotiation:
+ ** fall back to default mode.
+ */
+ switch (cp->nego_status) {
+
+ case NS_SYNC:
+ spi_period(starget) = 0;
+ spi_offset(starget) = 0;
+ ncr_setsync (np, cp, 0, 0xe0);
+ break;
+
+ case NS_WIDE:
+ spi_width(starget) = 0;
+ ncr_setwide (np, cp, 0, 0);
+ break;
+
+ }
+ np->msgin [0] = M_NOOP;
+ np->msgout[0] = M_NOOP;
+ cp->nego_status = 0;
+ break;
+
+ case SIR_NEGO_SYNC:
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ ncr_print_msg(cp, "sync msgin", np->msgin);
+ }
+
+ chg = 0;
+ per = np->msgin[3];
+ ofs = np->msgin[4];
+ if (ofs==0) per=255;
+
+ /*
+ ** if target sends SDTR message,
+ ** it CAN transfer synch.
+ */
+
+ if (ofs && starget)
+ spi_support_sync(starget) = 1;
+
+ /*
+ ** check values against driver limits.
+ */
+
+ if (per < np->minsync)
+ {chg = 1; per = np->minsync;}
+ if (per < tp->minsync)
+ {chg = 1; per = tp->minsync;}
+ if (ofs > tp->maxoffs)
+ {chg = 1; ofs = tp->maxoffs;}
+
+ /*
+ ** Check against controller limits.
+ */
+ fak = 7;
+ scntl3 = 0;
+ if (ofs != 0) {
+ ncr_getsync(np, per, &fak, &scntl3);
+ if (fak > 7) {
+ chg = 1;
+ ofs = 0;
+ }
+ }
+ if (ofs == 0) {
+ fak = 7;
+ per = 0;
+ scntl3 = 0;
+ tp->minsync = 0;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd, "sync: per=%d scntl3=0x%x ofs=%d "
+ "fak=%d chg=%d.\n", per, scntl3, ofs, fak, chg);
+ }
+
+ if (INB (HS_PRT) == HS_NEGOTIATE) {
+ OUTB (HS_PRT, HS_BUSY);
+ switch (cp->nego_status) {
+
+ case NS_SYNC:
+ /* This was an answer message */
+ if (chg) {
+ /* Answer wasn't acceptable. */
+ spi_period(starget) = 0;
+ spi_offset(starget) = 0;
+ ncr_setsync(np, cp, 0, 0xe0);
+ OUTL_DSP(NCB_SCRIPT_PHYS (np, msg_bad));
+ } else {
+ /* Answer is ok. */
+ spi_period(starget) = per;
+ spi_offset(starget) = ofs;
+ ncr_setsync(np, cp, scntl3, (fak<<5)|ofs);
+ OUTL_DSP(NCB_SCRIPT_PHYS (np, clrack));
+ }
+ return;
+
+ case NS_WIDE:
+ spi_width(starget) = 0;
+ ncr_setwide(np, cp, 0, 0);
+ break;
+ }
+ }
+
+ /*
+ ** It was a request. Set value and
+ ** prepare an answer message
+ */
+
+ spi_period(starget) = per;
+ spi_offset(starget) = ofs;
+ ncr_setsync(np, cp, scntl3, (fak<<5)|ofs);
+
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 3;
+ np->msgout[2] = M_X_SYNC_REQ;
+ np->msgout[3] = per;
+ np->msgout[4] = ofs;
+
+ cp->nego_status = NS_SYNC;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ ncr_print_msg(cp, "sync msgout", np->msgout);
+ }
+
+ if (!ofs) {
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad));
+ return;
+ }
+ np->msgin [0] = M_NOOP;
+
+ break;
+
+ case SIR_NEGO_WIDE:
+ /*
+ ** Wide request message received.
+ */
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ ncr_print_msg(cp, "wide msgin", np->msgin);
+ }
+
+ /*
+ ** get requested values.
+ */
+
+ chg = 0;
+ wide = np->msgin[3];
+
+ /*
+ ** if target sends WDTR message,
+ ** it CAN transfer wide.
+ */
+
+ if (wide && starget)
+ spi_support_wide(starget) = 1;
+
+ /*
+ ** check values against driver limits.
+ */
+
+ if (wide > tp->usrwide)
+ {chg = 1; wide = tp->usrwide;}
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd, "wide: wide=%d chg=%d.\n", wide,
+ chg);
+ }
+
+ if (INB (HS_PRT) == HS_NEGOTIATE) {
+ OUTB (HS_PRT, HS_BUSY);
+ switch (cp->nego_status) {
+
+ case NS_WIDE:
+ /*
+ ** This was an answer message
+ */
+ if (chg) {
+ /* Answer wasn't acceptable. */
+ spi_width(starget) = 0;
+ ncr_setwide(np, cp, 0, 1);
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad));
+ } else {
+ /* Answer is ok. */
+ spi_width(starget) = wide;
+ ncr_setwide(np, cp, wide, 1);
+ OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
+ }
+ return;
+
+ case NS_SYNC:
+ spi_period(starget) = 0;
+ spi_offset(starget) = 0;
+ ncr_setsync(np, cp, 0, 0xe0);
+ break;
+ }
+ }
+
+ /*
+ ** It was a request, set value and
+ ** prepare an answer message
+ */
+
+ spi_width(starget) = wide;
+ ncr_setwide(np, cp, wide, 1);
+
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 2;
+ np->msgout[2] = M_X_WIDE_REQ;
+ np->msgout[3] = wide;
+
+ np->msgin [0] = M_NOOP;
+
+ cp->nego_status = NS_WIDE;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ ncr_print_msg(cp, "wide msgout", np->msgin);
+ }
+ break;
+
+/*--------------------------------------------------------------------
+**
+** Processing of special messages
+**
+**--------------------------------------------------------------------
+*/
+
+ case SIR_REJECT_RECEIVED:
+ /*-----------------------------------------------
+ **
+ ** We received a M_REJECT message.
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd, "M_REJECT received (%x:%x).\n",
+ (unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
+ break;
+
+ case SIR_REJECT_SENT:
+ /*-----------------------------------------------
+ **
+ ** We received an unknown message
+ **
+ **-----------------------------------------------
+ */
+
+ ncr_print_msg(cp, "M_REJECT sent for", np->msgin);
+ break;
+
+/*--------------------------------------------------------------------
+**
+** Processing of special messages
+**
+**--------------------------------------------------------------------
+*/
+
+ case SIR_IGN_RESIDUE:
+ /*-----------------------------------------------
+ **
+ ** We received an IGNORE RESIDUE message,
+ ** which couldn't be handled by the script.
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd, "M_IGN_RESIDUE received, but not yet "
+ "implemented.\n");
+ break;
+#if 0
+ case SIR_MISSING_SAVE:
+ /*-----------------------------------------------
+ **
+ ** We received an DISCONNECT message,
+ ** but the datapointer wasn't saved before.
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd, "M_DISCONNECT received, but datapointer "
+ "not saved: data=%x save=%x goal=%x.\n",
+ (unsigned) INL (nc_temp),
+ (unsigned) scr_to_cpu(np->header.savep),
+ (unsigned) scr_to_cpu(np->header.goalp));
+ break;
+#endif
+ }
+
+out:
+ OUTONB_STD ();
+}
+
+/*==========================================================
+**
+**
+** Acquire a control block
+**
+**
+**==========================================================
+*/
+
+static struct ccb *ncr_get_ccb(struct ncb *np, struct scsi_cmnd *cmd)
+{
+ u_char tn = cmd->device->id;
+ u_char ln = cmd->device->lun;
+ struct tcb *tp = &np->target[tn];
+ struct lcb *lp = tp->lp[ln];
+ u_char tag = NO_TAG;
+ struct ccb *cp = NULL;
+
+ /*
+ ** Lun structure available ?
+ */
+ if (lp) {
+ struct list_head *qp;
+ /*
+ ** Keep from using more tags than we can handle.
+ */
+ if (lp->usetags && lp->busyccbs >= lp->maxnxs)
+ return NULL;
+
+ /*
+ ** Allocate a new CCB if needed.
+ */
+ if (list_empty(&lp->free_ccbq))
+ ncr_alloc_ccb(np, tn, ln);
+
+ /*
+ ** Look for free CCB
+ */
+ qp = ncr_list_pop(&lp->free_ccbq);
+ if (qp) {
+ cp = list_entry(qp, struct ccb, link_ccbq);
+ if (cp->magic) {
+ PRINT_ADDR(cmd, "ccb free list corrupted "
+ "(@%p)\n", cp);
+ cp = NULL;
+ } else {
+ list_add_tail(qp, &lp->wait_ccbq);
+ ++lp->busyccbs;
+ }
+ }
+
+ /*
+ ** If a CCB is available,
+ ** Get a tag for this nexus if required.
+ */
+ if (cp) {
+ if (lp->usetags)
+ tag = lp->cb_tags[lp->ia_tag];
+ }
+ else if (lp->actccbs > 0)
+ return NULL;
+ }
+
+ /*
+ ** if nothing available, take the default.
+ */
+ if (!cp)
+ cp = np->ccb;
+
+ /*
+ ** Wait until available.
+ */
+#if 0
+ while (cp->magic) {
+ if (flags & SCSI_NOSLEEP) break;
+ if (tsleep ((caddr_t)cp, PRIBIO|PCATCH, "ncr", 0))
+ break;
+ }
+#endif
+
+ if (cp->magic)
+ return NULL;
+
+ cp->magic = 1;
+
+ /*
+ ** Move to next available tag if tag used.
+ */
+ if (lp) {
+ if (tag != NO_TAG) {
+ ++lp->ia_tag;
+ if (lp->ia_tag == MAX_TAGS)
+ lp->ia_tag = 0;
+ lp->tags_umap |= (((tagmap_t) 1) << tag);
+ }
+ }
+
+ /*
+ ** Remember all informations needed to free this CCB.
+ */
+ cp->tag = tag;
+ cp->target = tn;
+ cp->lun = ln;
+
+ if (DEBUG_FLAGS & DEBUG_TAGS) {
+ PRINT_ADDR(cmd, "ccb @%p using tag %d.\n", cp, tag);
+ }
+
+ return cp;
+}
+
+/*==========================================================
+**
+**
+** Release one control block
+**
+**
+**==========================================================
+*/
+
+static void ncr_free_ccb (struct ncb *np, struct ccb *cp)
+{
+ struct tcb *tp = &np->target[cp->target];
+ struct lcb *lp = tp->lp[cp->lun];
+
+ if (DEBUG_FLAGS & DEBUG_TAGS) {
+ PRINT_ADDR(cp->cmd, "ccb @%p freeing tag %d.\n", cp, cp->tag);
+ }
+
+ /*
+ ** If lun control block available,
+ ** decrement active commands and increment credit,
+ ** free the tag if any and remove the JUMP for reselect.
+ */
+ if (lp) {
+ if (cp->tag != NO_TAG) {
+ lp->cb_tags[lp->if_tag++] = cp->tag;
+ if (lp->if_tag == MAX_TAGS)
+ lp->if_tag = 0;
+ lp->tags_umap &= ~(((tagmap_t) 1) << cp->tag);
+ lp->tags_smap &= lp->tags_umap;
+ lp->jump_ccb[cp->tag] =
+ cpu_to_scr(NCB_SCRIPTH_PHYS(np, bad_i_t_l_q));
+ } else {
+ lp->jump_ccb[0] =
+ cpu_to_scr(NCB_SCRIPTH_PHYS(np, bad_i_t_l));
+ }
+ }
+
+ /*
+ ** Make this CCB available.
+ */
+
+ if (lp) {
+ if (cp != np->ccb)
+ list_move(&cp->link_ccbq, &lp->free_ccbq);
+ --lp->busyccbs;
+ if (cp->queued) {
+ --lp->queuedccbs;
+ }
+ }
+ cp -> host_status = HS_IDLE;
+ cp -> magic = 0;
+ if (cp->queued) {
+ --np->queuedccbs;
+ cp->queued = 0;
+ }
+
+#if 0
+ if (cp == np->ccb)
+ wakeup ((caddr_t) cp);
+#endif
+}
+
+
+#define ncr_reg_bus_addr(r) (np->paddr + offsetof (struct ncr_reg, r))
+
+/*------------------------------------------------------------------------
+** Initialize the fixed part of a CCB structure.
+**------------------------------------------------------------------------
+**------------------------------------------------------------------------
+*/
+static void ncr_init_ccb(struct ncb *np, struct ccb *cp)
+{
+ ncrcmd copy_4 = np->features & FE_PFEN ? SCR_COPY(4) : SCR_COPY_F(4);
+
+ /*
+ ** Remember virtual and bus address of this ccb.
+ */
+ cp->p_ccb = vtobus(cp);
+ cp->phys.header.cp = cp;
+
+ /*
+ ** This allows list_del to work for the default ccb.
+ */
+ INIT_LIST_HEAD(&cp->link_ccbq);
+
+ /*
+ ** Initialyze the start and restart launch script.
+ **
+ ** COPY(4) @(...p_phys), @(dsa)
+ ** JUMP @(sched_point)
+ */
+ cp->start.setup_dsa[0] = cpu_to_scr(copy_4);
+ cp->start.setup_dsa[1] = cpu_to_scr(CCB_PHYS(cp, start.p_phys));
+ cp->start.setup_dsa[2] = cpu_to_scr(ncr_reg_bus_addr(nc_dsa));
+ cp->start.schedule.l_cmd = cpu_to_scr(SCR_JUMP);
+ cp->start.p_phys = cpu_to_scr(CCB_PHYS(cp, phys));
+
+ memcpy(&cp->restart, &cp->start, sizeof(cp->restart));
+
+ cp->start.schedule.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+ cp->restart.schedule.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+}
+
+
+/*------------------------------------------------------------------------
+** Allocate a CCB and initialize its fixed part.
+**------------------------------------------------------------------------
+**------------------------------------------------------------------------
+*/
+static void ncr_alloc_ccb(struct ncb *np, u_char tn, u_char ln)
+{
+ struct tcb *tp = &np->target[tn];
+ struct lcb *lp = tp->lp[ln];
+ struct ccb *cp = NULL;
+
+ /*
+ ** Allocate memory for this CCB.
+ */
+ cp = m_calloc_dma(sizeof(struct ccb), "CCB");
+ if (!cp)
+ return;
+
+ /*
+ ** Count it and initialyze it.
+ */
+ lp->actccbs++;
+ np->actccbs++;
+ memset(cp, 0, sizeof (*cp));
+ ncr_init_ccb(np, cp);
+
+ /*
+ ** Chain into wakeup list and free ccb queue and take it
+ ** into account for tagged commands.
+ */
+ cp->link_ccb = np->ccb->link_ccb;
+ np->ccb->link_ccb = cp;
+
+ list_add(&cp->link_ccbq, &lp->free_ccbq);
+}
+
+/*==========================================================
+**
+**
+** Allocation of resources for Targets/Luns/Tags.
+**
+**
+**==========================================================
+*/
+
+
+/*------------------------------------------------------------------------
+** Target control block initialisation.
+**------------------------------------------------------------------------
+** This data structure is fully initialized after a SCSI command
+** has been successfully completed for this target.
+** It contains a SCRIPT that is called on target reselection.
+**------------------------------------------------------------------------
+*/
+static void ncr_init_tcb (struct ncb *np, u_char tn)
+{
+ struct tcb *tp = &np->target[tn];
+ ncrcmd copy_1 = np->features & FE_PFEN ? SCR_COPY(1) : SCR_COPY_F(1);
+ int th = tn & 3;
+ int i;
+
+ /*
+ ** Jump to next tcb if SFBR does not match this target.
+ ** JUMP IF (SFBR != #target#), @(next tcb)
+ */
+ tp->jump_tcb.l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (0x80 + tn))));
+ tp->jump_tcb.l_paddr = np->jump_tcb[th].l_paddr;
+
+ /*
+ ** Load the synchronous transfer register.
+ ** COPY @(tp->sval), @(sxfer)
+ */
+ tp->getscr[0] = cpu_to_scr(copy_1);
+ tp->getscr[1] = cpu_to_scr(vtobus (&tp->sval));
+#ifdef SCSI_NCR_BIG_ENDIAN
+ tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer) ^ 3);
+#else
+ tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer));
+#endif
+
+ /*
+ ** Load the timing register.
+ ** COPY @(tp->wval), @(scntl3)
+ */
+ tp->getscr[3] = cpu_to_scr(copy_1);
+ tp->getscr[4] = cpu_to_scr(vtobus (&tp->wval));
+#ifdef SCSI_NCR_BIG_ENDIAN
+ tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3) ^ 3);
+#else
+ tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3));
+#endif
+
+ /*
+ ** Get the IDENTIFY message and the lun.
+ ** CALL @script(resel_lun)
+ */
+ tp->call_lun.l_cmd = cpu_to_scr(SCR_CALL);
+ tp->call_lun.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
+
+ /*
+ ** Look for the lun control block of this nexus.
+ ** For i = 0 to 3
+ ** JUMP ^ IFTRUE (MASK (i, 3)), @(next_lcb)
+ */
+ for (i = 0 ; i < 4 ; i++) {
+ tp->jump_lcb[i].l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFTRUE (MASK (i, 3))));
+ tp->jump_lcb[i].l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_identify));
+ }
+
+ /*
+ ** Link this target control block to the JUMP chain.
+ */
+ np->jump_tcb[th].l_paddr = cpu_to_scr(vtobus (&tp->jump_tcb));
+
+ /*
+ ** These assert's should be moved at driver initialisations.
+ */
+#ifdef SCSI_NCR_BIG_ENDIAN
+ BUG_ON(((offsetof(struct ncr_reg, nc_sxfer) ^
+ offsetof(struct tcb , sval )) &3) != 3);
+ BUG_ON(((offsetof(struct ncr_reg, nc_scntl3) ^
+ offsetof(struct tcb , wval )) &3) != 3);
+#else
+ BUG_ON(((offsetof(struct ncr_reg, nc_sxfer) ^
+ offsetof(struct tcb , sval )) &3) != 0);
+ BUG_ON(((offsetof(struct ncr_reg, nc_scntl3) ^
+ offsetof(struct tcb , wval )) &3) != 0);
+#endif
+}
+
+
+/*------------------------------------------------------------------------
+** Lun control block allocation and initialization.
+**------------------------------------------------------------------------
+** This data structure is allocated and initialized after a SCSI
+** command has been successfully completed for this target/lun.
+**------------------------------------------------------------------------
+*/
+static struct lcb *ncr_alloc_lcb (struct ncb *np, u_char tn, u_char ln)
+{
+ struct tcb *tp = &np->target[tn];
+ struct lcb *lp = tp->lp[ln];
+ ncrcmd copy_4 = np->features & FE_PFEN ? SCR_COPY(4) : SCR_COPY_F(4);
+ int lh = ln & 3;
+
+ /*
+ ** Already done, return.
+ */
+ if (lp)
+ return lp;
+
+ /*
+ ** Allocate the lcb.
+ */
+ lp = m_calloc_dma(sizeof(struct lcb), "LCB");
+ if (!lp)
+ goto fail;
+ memset(lp, 0, sizeof(*lp));
+ tp->lp[ln] = lp;
+
+ /*
+ ** Initialize the target control block if not yet.
+ */
+ if (!tp->jump_tcb.l_cmd)
+ ncr_init_tcb(np, tn);
+
+ /*
+ ** Initialize the CCB queue headers.
+ */
+ INIT_LIST_HEAD(&lp->free_ccbq);
+ INIT_LIST_HEAD(&lp->busy_ccbq);
+ INIT_LIST_HEAD(&lp->wait_ccbq);
+ INIT_LIST_HEAD(&lp->skip_ccbq);
+
+ /*
+ ** Set max CCBs to 1 and use the default 1 entry
+ ** jump table by default.
+ */
+ lp->maxnxs = 1;
+ lp->jump_ccb = &lp->jump_ccb_0;
+ lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
+
+ /*
+ ** Initilialyze the reselect script:
+ **
+ ** Jump to next lcb if SFBR does not match this lun.
+ ** Load TEMP with the CCB direct jump table bus address.
+ ** Get the SIMPLE TAG message and the tag.
+ **
+ ** JUMP IF (SFBR != #lun#), @(next lcb)
+ ** COPY @(lp->p_jump_ccb), @(temp)
+ ** JUMP @script(resel_notag)
+ */
+ lp->jump_lcb.l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFFALSE (MASK (0x80+ln, 0xff))));
+ lp->jump_lcb.l_paddr = tp->jump_lcb[lh].l_paddr;
+
+ lp->load_jump_ccb[0] = cpu_to_scr(copy_4);
+ lp->load_jump_ccb[1] = cpu_to_scr(vtobus (&lp->p_jump_ccb));
+ lp->load_jump_ccb[2] = cpu_to_scr(ncr_reg_bus_addr(nc_temp));
+
+ lp->jump_tag.l_cmd = cpu_to_scr(SCR_JUMP);
+ lp->jump_tag.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_notag));
+
+ /*
+ ** Link this lun control block to the JUMP chain.
+ */
+ tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtobus (&lp->jump_lcb));
+
+ /*
+ ** Initialize command queuing control.
+ */
+ lp->busyccbs = 1;
+ lp->queuedccbs = 1;
+ lp->queuedepth = 1;
+fail:
+ return lp;
+}
+
+
+/*------------------------------------------------------------------------
+** Lun control block setup on INQUIRY data received.
+**------------------------------------------------------------------------
+** We only support WIDE, SYNC for targets and CMDQ for logical units.
+** This setup is done on each INQUIRY since we are expecting user
+** will play with CHANGE DEFINITION commands. :-)
+**------------------------------------------------------------------------
+*/
+static struct lcb *ncr_setup_lcb (struct ncb *np, struct scsi_device *sdev)
+{
+ unsigned char tn = sdev->id, ln = sdev->lun;
+ struct tcb *tp = &np->target[tn];
+ struct lcb *lp = tp->lp[ln];
+
+ /* If no lcb, try to allocate it. */
+ if (!lp && !(lp = ncr_alloc_lcb(np, tn, ln)))
+ goto fail;
+
+ /*
+ ** If unit supports tagged commands, allocate the
+ ** CCB JUMP table if not yet.
+ */
+ if (sdev->tagged_supported && lp->jump_ccb == &lp->jump_ccb_0) {
+ int i;
+ lp->jump_ccb = m_calloc_dma(256, "JUMP_CCB");
+ if (!lp->jump_ccb) {
+ lp->jump_ccb = &lp->jump_ccb_0;
+ goto fail;
+ }
+ lp->p_jump_ccb = cpu_to_scr(vtobus(lp->jump_ccb));
+ for (i = 0 ; i < 64 ; i++)
+ lp->jump_ccb[i] =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
+ for (i = 0 ; i < MAX_TAGS ; i++)
+ lp->cb_tags[i] = i;
+ lp->maxnxs = MAX_TAGS;
+ lp->tags_stime = ktime_get(3*HZ);
+ ncr_setup_tags (np, sdev);
+ }
+
+
+fail:
+ return lp;
+}
+
+/*==========================================================
+**
+**
+** Build Scatter Gather Block
+**
+**
+**==========================================================
+**
+** The transfer area may be scattered among
+** several non adjacent physical pages.
+**
+** We may use MAX_SCATTER blocks.
+**
+**----------------------------------------------------------
+*/
+
+/*
+** We try to reduce the number of interrupts caused
+** by unexpected phase changes due to disconnects.
+** A typical harddisk may disconnect before ANY block.
+** If we wanted to avoid unexpected phase changes at all
+** we had to use a break point every 512 bytes.
+** Of course the number of scatter/gather blocks is
+** limited.
+** Under Linux, the scatter/gatter blocks are provided by
+** the generic driver. We just have to copy addresses and
+** sizes to the data segment array.
+*/
+
+static int ncr_scatter_no_sglist(struct ncb *np, struct ccb *cp, struct scsi_cmnd *cmd)
+{
+ struct scr_tblmove *data = &cp->phys.data[MAX_SCATTER - 1];
+ int segment;
+
+ cp->data_len = cmd->request_bufflen;
+
+ if (cmd->request_bufflen) {
+ dma_addr_t baddr = map_scsi_single_data(np, cmd);
+ if (baddr) {
+ ncr_build_sge(np, data, baddr, cmd->request_bufflen);
+ segment = 1;
+ } else {
+ segment = -2;
+ }
+ } else {
+ segment = 0;
+ }
+
+ return segment;
+}
+
+static int ncr_scatter(struct ncb *np, struct ccb *cp, struct scsi_cmnd *cmd)
+{
+ int segment = 0;
+ int use_sg = (int) cmd->use_sg;
+
+ cp->data_len = 0;
+
+ if (!use_sg)
+ segment = ncr_scatter_no_sglist(np, cp, cmd);
+ else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) {
+ struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+ struct scr_tblmove *data;
+
+ if (use_sg > MAX_SCATTER) {
+ unmap_scsi_data(np, cmd);
+ return -1;
+ }
+
+ data = &cp->phys.data[MAX_SCATTER - use_sg];
+
+ for (segment = 0; segment < use_sg; segment++) {
+ dma_addr_t baddr = sg_dma_address(&scatter[segment]);
+ unsigned int len = sg_dma_len(&scatter[segment]);
+
+ ncr_build_sge(np, &data[segment], baddr, len);
+ cp->data_len += len;
+ }
+ } else {
+ segment = -2;
+ }
+
+ return segment;
+}
+
+/*==========================================================
+**
+**
+** Test the bus snoop logic :-(
+**
+** Has to be called with interrupts disabled.
+**
+**
+**==========================================================
+*/
+
+static int __init ncr_regtest (struct ncb* np)
+{
+ register volatile u32 data;
+ /*
+ ** ncr registers may NOT be cached.
+ ** write 0xffffffff to a read only register area,
+ ** and try to read it back.
+ */
+ data = 0xffffffff;
+ OUTL_OFF(offsetof(struct ncr_reg, nc_dstat), data);
+ data = INL_OFF(offsetof(struct ncr_reg, nc_dstat));
+#if 1
+ if (data == 0xffffffff) {
+#else
+ if ((data & 0xe2f0fffd) != 0x02000080) {
+#endif
+ printk ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n",
+ (unsigned) data);
+ return (0x10);
+ }
+ return (0);
+}
+
+static int __init ncr_snooptest (struct ncb* np)
+{
+ u32 ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc;
+ int i, err=0;
+ if (np->reg) {
+ err |= ncr_regtest (np);
+ if (err)
+ return (err);
+ }
+
+ /* init */
+ pc = NCB_SCRIPTH_PHYS (np, snooptest);
+ host_wr = 1;
+ ncr_wr = 2;
+ /*
+ ** Set memory and register.
+ */
+ np->ncr_cache = cpu_to_scr(host_wr);
+ OUTL (nc_temp, ncr_wr);
+ /*
+ ** Start script (exchange values)
+ */
+ OUTL_DSP (pc);
+ /*
+ ** Wait 'til done (with timeout)
+ */
+ for (i=0; i<NCR_SNOOP_TIMEOUT; i++)
+ if (INB(nc_istat) & (INTF|SIP|DIP))
+ break;
+ /*
+ ** Save termination position.
+ */
+ pc = INL (nc_dsp);
+ /*
+ ** Read memory and register.
+ */
+ host_rd = scr_to_cpu(np->ncr_cache);
+ ncr_rd = INL (nc_scratcha);
+ ncr_bk = INL (nc_temp);
+ /*
+ ** Reset ncr chip
+ */
+ ncr_chip_reset(np, 100);
+ /*
+ ** check for timeout
+ */
+ if (i>=NCR_SNOOP_TIMEOUT) {
+ printk ("CACHE TEST FAILED: timeout.\n");
+ return (0x20);
+ }
+ /*
+ ** Check termination position.
+ */
+ if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) {
+ printk ("CACHE TEST FAILED: script execution failed.\n");
+ printk ("start=%08lx, pc=%08lx, end=%08lx\n",
+ (u_long) NCB_SCRIPTH_PHYS (np, snooptest), (u_long) pc,
+ (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
+ return (0x40);
+ }
+ /*
+ ** Show results.
+ */
+ if (host_wr != ncr_rd) {
+ printk ("CACHE TEST FAILED: host wrote %d, ncr read %d.\n",
+ (int) host_wr, (int) ncr_rd);
+ err |= 1;
+ }
+ if (host_rd != ncr_wr) {
+ printk ("CACHE TEST FAILED: ncr wrote %d, host read %d.\n",
+ (int) ncr_wr, (int) host_rd);
+ err |= 2;
+ }
+ if (ncr_bk != ncr_wr) {
+ printk ("CACHE TEST FAILED: ncr wrote %d, read back %d.\n",
+ (int) ncr_wr, (int) ncr_bk);
+ err |= 4;
+ }
+ return (err);
+}
+
+/*==========================================================
+**
+** Determine the ncr's clock frequency.
+** This is essential for the negotiation
+** of the synchronous transfer rate.
+**
+**==========================================================
+**
+** Note: we have to return the correct value.
+** THERE IS NO SAVE DEFAULT VALUE.
+**
+** Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+** 53C860 and 53C875 rev. 1 support fast20 transfers but
+** do not have a clock doubler and so are provided with a
+** 80 MHz clock. All other fast20 boards incorporate a doubler
+** and so should be delivered with a 40 MHz clock.
+** The future fast40 chips (895/895) use a 40 Mhz base clock
+** and provide a clock quadrupler (160 Mhz). The code below
+** tries to deal as cleverly as possible with all this stuff.
+**
+**----------------------------------------------------------
+*/
+
+/*
+ * Select NCR SCSI clock frequency
+ */
+static void ncr_selectclock(struct ncb *np, u_char scntl3)
+{
+ if (np->multiplier < 2) {
+ OUTB(nc_scntl3, scntl3);
+ return;
+ }
+
+ if (bootverbose >= 2)
+ printk ("%s: enabling clock multiplier\n", ncr_name(np));
+
+ OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */
+ if (np->multiplier > 2) { /* Poll bit 5 of stest4 for quadrupler */
+ int i = 20;
+ while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
+ udelay(20);
+ if (!i)
+ printk("%s: the chip cannot lock the frequency\n", ncr_name(np));
+ } else /* Wait 20 micro-seconds for doubler */
+ udelay(20);
+ OUTB(nc_stest3, HSC); /* Halt the scsi clock */
+ OUTB(nc_scntl3, scntl3);
+ OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */
+ OUTB(nc_stest3, 0x00); /* Restart scsi clock */
+}
+
+
+/*
+ * calculate NCR SCSI clock frequency (in KHz)
+ */
+static unsigned __init ncrgetfreq (struct ncb *np, int gen)
+{
+ unsigned ms = 0;
+ char count = 0;
+
+ /*
+ * Measure GEN timer delay in order
+ * to calculate SCSI clock frequency
+ *
+ * This code will never execute too
+ * many loop iterations (if DELAY is
+ * reasonably correct). It could get
+ * too low a delay (too high a freq.)
+ * if the CPU is slow executing the
+ * loop for some reason (an NMI, for
+ * example). For this reason we will
+ * if multiple measurements are to be
+ * performed trust the higher delay
+ * (lower frequency returned).
+ */
+ OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */
+ OUTW (nc_sien , 0); /* mask all scsi interrupts */
+ (void) INW (nc_sist); /* clear pending scsi interrupt */
+ OUTB (nc_dien , 0); /* mask all dma interrupts */
+ (void) INW (nc_sist); /* another one, just to be sure :) */
+ OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ OUTB (nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */
+ while (!(INW(nc_sist) & GEN) && ms++ < 100000) {
+ for (count = 0; count < 10; count ++)
+ udelay(100); /* count ms */
+ }
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ /*
+ * set prescaler to divide by whatever 0 means
+ * 0 ought to choose divide by 2, but appears
+ * to set divide by 3.5 mode in my 53c810 ...
+ */
+ OUTB (nc_scntl3, 0);
+
+ if (bootverbose >= 2)
+ printk ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
+ /*
+ * adjust for prescaler, and convert into KHz
+ */
+ return ms ? ((1 << gen) * 4340) / ms : 0;
+}
+
+/*
+ * Get/probe NCR SCSI clock frequency
+ */
+static void __init ncr_getclock (struct ncb *np, int mult)
+{
+ unsigned char scntl3 = INB(nc_scntl3);
+ unsigned char stest1 = INB(nc_stest1);
+ unsigned f1;
+
+ np->multiplier = 1;
+ f1 = 40000;
+
+ /*
+ ** True with 875 or 895 with clock multiplier selected
+ */
+ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+ if (bootverbose >= 2)
+ printk ("%s: clock multiplier found\n", ncr_name(np));
+ np->multiplier = mult;
+ }
+
+ /*
+ ** If multiplier not found or scntl3 not 7,5,3,
+ ** reset chip and get frequency from general purpose timer.
+ ** Otherwise trust scntl3 BIOS setting.
+ */
+ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+ unsigned f2;
+
+ ncr_chip_reset(np, 5);
+
+ (void) ncrgetfreq (np, 11); /* throw away first result */
+ f1 = ncrgetfreq (np, 11);
+ f2 = ncrgetfreq (np, 11);
+
+ if(bootverbose)
+ printk ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+
+ if (f1 > f2) f1 = f2; /* trust lower result */
+
+ if (f1 < 45000) f1 = 40000;
+ else if (f1 < 55000) f1 = 50000;
+ else f1 = 80000;
+
+ if (f1 < 80000 && mult > 1) {
+ if (bootverbose >= 2)
+ printk ("%s: clock multiplier assumed\n", ncr_name(np));
+ np->multiplier = mult;
+ }
+ } else {
+ if ((scntl3 & 7) == 3) f1 = 40000;
+ else if ((scntl3 & 7) == 5) f1 = 80000;
+ else f1 = 160000;
+
+ f1 /= np->multiplier;
+ }
+
+ /*
+ ** Compute controller synchronous parameters.
+ */
+ f1 *= np->multiplier;
+ np->clock_khz = f1;
+}
+
+/*===================== LINUX ENTRY POINTS SECTION ==========================*/
+
+static int ncr53c8xx_slave_alloc(struct scsi_device *device)
+{
+ struct Scsi_Host *host = device->host;
+ struct ncb *np = ((struct host_data *) host->hostdata)->ncb;
+ struct tcb *tp = &np->target[device->id];
+ tp->starget = device->sdev_target;
+
+ return 0;
+}
+
+static int ncr53c8xx_slave_configure(struct scsi_device *device)
+{
+ struct Scsi_Host *host = device->host;
+ struct ncb *np = ((struct host_data *) host->hostdata)->ncb;
+ struct tcb *tp = &np->target[device->id];
+ struct lcb *lp = tp->lp[device->lun];
+ int numtags, depth_to_use;
+
+ ncr_setup_lcb(np, device);
+
+ /*
+ ** Select queue depth from driver setup.
+ ** Donnot use more than configured by user.
+ ** Use at least 2.
+ ** Donnot use more than our maximum.
+ */
+ numtags = device_queue_depth(np->unit, device->id, device->lun);
+ if (numtags > tp->usrtags)
+ numtags = tp->usrtags;
+ if (!device->tagged_supported)
+ numtags = 1;
+ depth_to_use = numtags;
+ if (depth_to_use < 2)
+ depth_to_use = 2;
+ if (depth_to_use > MAX_TAGS)
+ depth_to_use = MAX_TAGS;
+
+ scsi_adjust_queue_depth(device,
+ (device->tagged_supported ?
+ MSG_SIMPLE_TAG : 0),
+ depth_to_use);
+
+ /*
+ ** Since the queue depth is not tunable under Linux,
+ ** we need to know this value in order not to
+ ** announce stupid things to user.
+ **
+ ** XXX(hch): As of Linux 2.6 it certainly _is_ tunable..
+ ** In fact we just tuned it, or did I miss
+ ** something important? :)
+ */
+ if (lp) {
+ lp->numtags = lp->maxtags = numtags;
+ lp->scdev_depth = depth_to_use;
+ }
+ ncr_setup_tags (np, device);
+
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx_select_queue_depth: host=%d, id=%d, lun=%d, depth=%d\n",
+ np->unit, device->id, device->lun, depth_to_use);
+#endif
+
+ if (spi_support_sync(device->sdev_target) &&
+ !spi_initial_dv(device->sdev_target))
+ spi_dv_device(device);
+ return 0;
+}
+
+static int ncr53c8xx_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
+{
+ struct ncb *np = ((struct host_data *) cmd->device->host->hostdata)->ncb;
+ unsigned long flags;
+ int sts;
+
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx_queue_command\n");
+#endif
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+ cmd->__data_mapped = 0;
+ cmd->__data_mapping = 0;
+
+ spin_lock_irqsave(&np->smp_lock, flags);
+
+ if ((sts = ncr_queue_command(np, cmd)) != DID_OK) {
+ cmd->result = ScsiResult(sts, 0);
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx : command not queued - result=%d\n", sts);
+#endif
+ }
+#ifdef DEBUG_NCR53C8XX
+ else
+printk("ncr53c8xx : command successfully queued\n");
+#endif
+
+ spin_unlock_irqrestore(&np->smp_lock, flags);
+
+ if (sts != DID_OK) {
+ unmap_scsi_data(np, cmd);
+ done(cmd);
+ sts = 0;
+ }
+
+ return sts;
+}
+
+irqreturn_t ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *shost = (struct Scsi_Host *)dev_id;
+ struct host_data *host_data = (struct host_data *)shost->hostdata;
+ struct ncb *np = host_data->ncb;
+ struct scsi_cmnd *done_list;
+
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx : interrupt received\n");
+#endif
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("[");
+
+ spin_lock_irqsave(&np->smp_lock, flags);
+ ncr_exception(np);
+ done_list = np->done_list;
+ np->done_list = NULL;
+ spin_unlock_irqrestore(&np->smp_lock, flags);
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printk ("]\n");
+
+ if (done_list)
+ ncr_flush_done_cmds(done_list);
+ return IRQ_HANDLED;
+}
+
+static void ncr53c8xx_timeout(unsigned long npref)
+{
+ struct ncb *np = (struct ncb *) npref;
+ unsigned long flags;
+ struct scsi_cmnd *done_list;
+
+ spin_lock_irqsave(&np->smp_lock, flags);
+ ncr_timeout(np);
+ done_list = np->done_list;
+ np->done_list = NULL;
+ spin_unlock_irqrestore(&np->smp_lock, flags);
+
+ if (done_list)
+ ncr_flush_done_cmds(done_list);
+}
+
+static int ncr53c8xx_bus_reset(struct scsi_cmnd *cmd)
+{
+ struct ncb *np = ((struct host_data *) cmd->device->host->hostdata)->ncb;
+ int sts;
+ unsigned long flags;
+ struct scsi_cmnd *done_list;
+
+ /*
+ * If the mid-level driver told us reset is synchronous, it seems
+ * that we must call the done() callback for the involved command,
+ * even if this command was not queued to the low-level driver,
+ * before returning SUCCESS.
+ */
+
+ spin_lock_irqsave(&np->smp_lock, flags);
+ sts = ncr_reset_bus(np, cmd, 1);
+
+ done_list = np->done_list;
+ np->done_list = NULL;
+ spin_unlock_irqrestore(&np->smp_lock, flags);
+
+ ncr_flush_done_cmds(done_list);
+
+ return sts;
+}
+
+#if 0 /* unused and broken */
+static int ncr53c8xx_abort(struct scsi_cmnd *cmd)
+{
+ struct ncb *np = ((struct host_data *) cmd->device->host->hostdata)->ncb;
+ int sts;
+ unsigned long flags;
+ struct scsi_cmnd *done_list;
+
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+ printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n",
+ cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout);
+#else
+ printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
+#endif
+
+ NCR_LOCK_NCB(np, flags);
+
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+ /*
+ * We have to just ignore abort requests in some situations.
+ */
+ if (cmd->serial_number != cmd->serial_number_at_timeout) {
+ sts = SCSI_ABORT_NOT_RUNNING;
+ goto out;
+ }
+#endif
+
+ sts = ncr_abort_command(np, cmd);
+out:
+ done_list = np->done_list;
+ np->done_list = NULL;
+ NCR_UNLOCK_NCB(np, flags);
+
+ ncr_flush_done_cmds(done_list);
+
+ return sts;
+}
+#endif
+
+
+/*
+** Scsi command waiting list management.
+**
+** It may happen that we cannot insert a scsi command into the start queue,
+** in the following circumstances.
+** Too few preallocated ccb(s),
+** maxtags < cmd_per_lun of the Linux host control block,
+** etc...
+** Such scsi commands are inserted into a waiting list.
+** When a scsi command complete, we try to requeue the commands of the
+** waiting list.
+*/
+
+#define next_wcmd host_scribble
+
+static void insert_into_waiting_list(struct ncb *np, struct scsi_cmnd *cmd)
+{
+ struct scsi_cmnd *wcmd;
+
+#ifdef DEBUG_WAITING_LIST
+ printk("%s: cmd %lx inserted into waiting list\n", ncr_name(np), (u_long) cmd);
+#endif
+ cmd->next_wcmd = NULL;
+ if (!(wcmd = np->waiting_list)) np->waiting_list = cmd;
+ else {
+ while ((wcmd->next_wcmd) != 0)
+ wcmd = (struct scsi_cmnd *) wcmd->next_wcmd;
+ wcmd->next_wcmd = (char *) cmd;
+ }
+}
+
+static struct scsi_cmnd *retrieve_from_waiting_list(int to_remove, struct ncb *np, struct scsi_cmnd *cmd)
+{
+ struct scsi_cmnd **pcmd = &np->waiting_list;
+
+ while (*pcmd) {
+ if (cmd == *pcmd) {
+ if (to_remove) {
+ *pcmd = (struct scsi_cmnd *) cmd->next_wcmd;
+ cmd->next_wcmd = NULL;
+ }
+#ifdef DEBUG_WAITING_LIST
+ printk("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
+#endif
+ return cmd;
+ }
+ pcmd = (struct scsi_cmnd **) &(*pcmd)->next_wcmd;
+ }
+ return NULL;
+}
+
+static void process_waiting_list(struct ncb *np, int sts)
+{
+ struct scsi_cmnd *waiting_list, *wcmd;
+
+ waiting_list = np->waiting_list;
+ np->waiting_list = NULL;
+
+#ifdef DEBUG_WAITING_LIST
+ if (waiting_list) printk("%s: waiting_list=%lx processing sts=%d\n", ncr_name(np), (u_long) waiting_list, sts);
+#endif
+ while ((wcmd = waiting_list) != 0) {
+ waiting_list = (struct scsi_cmnd *) wcmd->next_wcmd;
+ wcmd->next_wcmd = NULL;
+ if (sts == DID_OK) {
+#ifdef DEBUG_WAITING_LIST
+ printk("%s: cmd %lx trying to requeue\n", ncr_name(np), (u_long) wcmd);
+#endif
+ sts = ncr_queue_command(np, wcmd);
+ }
+ if (sts != DID_OK) {
+#ifdef DEBUG_WAITING_LIST
+ printk("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
+#endif
+ wcmd->result = ScsiResult(sts, 0);
+ ncr_queue_done_cmd(np, wcmd);
+ }
+ }
+}
+
+#undef next_wcmd
+
+static ssize_t show_ncr53c8xx_revision(struct class_device *dev, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(dev);
+ struct host_data *host_data = (struct host_data *)host->hostdata;
+
+ return snprintf(buf, 20, "0x%x\n", host_data->ncb->revision_id);
+}
+
+static struct class_device_attribute ncr53c8xx_revision_attr = {
+ .attr = { .name = "revision", .mode = S_IRUGO, },
+ .show = show_ncr53c8xx_revision,
+};
+
+static struct class_device_attribute *ncr53c8xx_host_attrs[] = {
+ &ncr53c8xx_revision_attr,
+ NULL
+};
+
+/*==========================================================
+**
+** Boot command line.
+**
+**==========================================================
+*/
+#ifdef MODULE
+char *ncr53c8xx; /* command line passed by insmod */
+module_param(ncr53c8xx, charp, 0);
+#endif
+
+static int __init ncr53c8xx_setup(char *str)
+{
+ return sym53c8xx__setup(str);
+}
+
+#ifndef MODULE
+__setup("ncr53c8xx=", ncr53c8xx_setup);
+#endif
+
+
+/*
+ * Host attach and initialisations.
+ *
+ * Allocate host data and ncb structure.
+ * Request IO region and remap MMIO region.
+ * Do chip initialization.
+ * If all is OK, install interrupt handling and
+ * start the timer daemon.
+ */
+struct Scsi_Host * __init ncr_attach(struct scsi_host_template *tpnt,
+ int unit, struct ncr_device *device)
+{
+ struct host_data *host_data;
+ struct ncb *np = NULL;
+ struct Scsi_Host *instance = NULL;
+ u_long flags = 0;
+ int i;
+
+ if (!tpnt->name)
+ tpnt->name = SCSI_NCR_DRIVER_NAME;
+ if (!tpnt->shost_attrs)
+ tpnt->shost_attrs = ncr53c8xx_host_attrs;
+
+ tpnt->queuecommand = ncr53c8xx_queue_command;
+ tpnt->slave_configure = ncr53c8xx_slave_configure;
+ tpnt->slave_alloc = ncr53c8xx_slave_alloc;
+ tpnt->eh_bus_reset_handler = ncr53c8xx_bus_reset;
+ tpnt->can_queue = SCSI_NCR_CAN_QUEUE;
+ tpnt->this_id = 7;
+ tpnt->sg_tablesize = SCSI_NCR_SG_TABLESIZE;
+ tpnt->cmd_per_lun = SCSI_NCR_CMD_PER_LUN;
+ tpnt->use_clustering = ENABLE_CLUSTERING;
+
+ if (device->differential)
+ driver_setup.diff_support = device->differential;
+
+ printk(KERN_INFO "ncr53c720-%d: rev 0x%x irq %d\n",
+ unit, device->chip.revision_id, device->slot.irq);
+
+ instance = scsi_host_alloc(tpnt, sizeof(*host_data));
+ if (!instance)
+ goto attach_error;
+ host_data = (struct host_data *) instance->hostdata;
+
+ np = __m_calloc_dma(device->dev, sizeof(struct ncb), "NCB");
+ if (!np)
+ goto attach_error;
+ spin_lock_init(&np->smp_lock);
+ np->dev = device->dev;
+ np->p_ncb = vtobus(np);
+ host_data->ncb = np;
+
+ np->ccb = m_calloc_dma(sizeof(struct ccb), "CCB");
+ if (!np->ccb)
+ goto attach_error;
+
+ /* Store input information in the host data structure. */
+ np->unit = unit;
+ np->verbose = driver_setup.verbose;
+ sprintf(np->inst_name, "ncr53c720-%d", np->unit);
+ np->revision_id = device->chip.revision_id;
+ np->features = device->chip.features;
+ np->clock_divn = device->chip.nr_divisor;
+ np->maxoffs = device->chip.offset_max;
+ np->maxburst = device->chip.burst_max;
+ np->myaddr = device->host_id;
+
+ /* Allocate SCRIPTS areas. */
+ np->script0 = m_calloc_dma(sizeof(struct script), "SCRIPT");
+ if (!np->script0)
+ goto attach_error;
+ np->scripth0 = m_calloc_dma(sizeof(struct scripth), "SCRIPTH");
+ if (!np->scripth0)
+ goto attach_error;
+
+ init_timer(&np->timer);
+ np->timer.data = (unsigned long) np;
+ np->timer.function = ncr53c8xx_timeout;
+
+ /* Try to map the controller chip to virtual and physical memory. */
+
+ np->paddr = device->slot.base;
+ np->paddr2 = (np->features & FE_RAM) ? device->slot.base_2 : 0;
+
+ if (device->slot.base_v)
+ np->vaddr = device->slot.base_v;
+ else
+ np->vaddr = ioremap(device->slot.base_c, 128);
+
+ if (!np->vaddr) {
+ printk(KERN_ERR
+ "%s: can't map memory mapped IO region\n",ncr_name(np));
+ goto attach_error;
+ } else {
+ if (bootverbose > 1)
+ printk(KERN_INFO
+ "%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+ }
+
+ /* Make the controller's registers available. Now the INB INW INL
+ * OUTB OUTW OUTL macros can be used safely.
+ */
+
+ np->reg = (struct ncr_reg __iomem *)np->vaddr;
+
+ /* Do chip dependent initialization. */
+ ncr_prepare_setting(np);
+
+ if (np->paddr2 && sizeof(struct script) > 4096) {
+ np->paddr2 = 0;
+ printk(KERN_WARNING "%s: script too large, NOT using on chip RAM.\n",
+ ncr_name(np));
+ }
+
+ instance->max_channel = 0;
+ instance->this_id = np->myaddr;
+ instance->max_id = np->maxwide ? 16 : 8;
+ instance->max_lun = SCSI_NCR_MAX_LUN;
+ instance->base = (unsigned long) np->reg;
+ instance->irq = device->slot.irq;
+ instance->unique_id = device->slot.base;
+ instance->dma_channel = 0;
+ instance->cmd_per_lun = MAX_TAGS;
+ instance->can_queue = (MAX_START-4);
+ /* This can happen if you forget to call ncr53c8xx_init from
+ * your module_init */
+ BUG_ON(!ncr53c8xx_transport_template);
+ instance->transportt = ncr53c8xx_transport_template;
+ scsi_set_device(instance, device->dev);
+
+ /* Patch script to physical addresses */
+ ncr_script_fill(&script0, &scripth0);
+
+ np->scripth = np->scripth0;
+ np->p_scripth = vtobus(np->scripth);
+ np->p_script = (np->paddr2) ? np->paddr2 : vtobus(np->script0);
+
+ ncr_script_copy_and_bind(np, (ncrcmd *) &script0,
+ (ncrcmd *) np->script0, sizeof(struct script));
+ ncr_script_copy_and_bind(np, (ncrcmd *) &scripth0,
+ (ncrcmd *) np->scripth0, sizeof(struct scripth));
+ np->ccb->p_ccb = vtobus (np->ccb);
+
+ /* Patch the script for LED support. */
+
+ if (np->features & FE_LED0) {
+ np->script0->idle[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_OR, 0x01));
+ np->script0->reselected[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+ np->script0->start[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+ }
+
+ /*
+ * Look for the target control block of this nexus.
+ * For i = 0 to 3
+ * JUMP ^ IFTRUE (MASK (i, 3)), @(next_lcb)
+ */
+ for (i = 0 ; i < 4 ; i++) {
+ np->jump_tcb[i].l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFTRUE (MASK (i, 3))));
+ np->jump_tcb[i].l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_target));
+ }
+
+ ncr_chip_reset(np, 100);
+
+ /* Now check the cache handling of the chipset. */
+
+ if (ncr_snooptest(np)) {
+ printk(KERN_ERR "CACHE INCORRECTLY CONFIGURED.\n");
+ goto attach_error;
+ }
+
+ /* Install the interrupt handler. */
+ np->irq = device->slot.irq;
+
+ /* Initialize the fixed part of the default ccb. */
+ ncr_init_ccb(np, np->ccb);
+
+ /*
+ * After SCSI devices have been opened, we cannot reset the bus
+ * safely, so we do it here. Interrupt handler does the real work.
+ * Process the reset exception if interrupts are not enabled yet.
+ * Then enable disconnects.
+ */
+ spin_lock_irqsave(&np->smp_lock, flags);
+ if (ncr_reset_scsi_bus(np, 0, driver_setup.settle_delay) != 0) {
+ printk(KERN_ERR "%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
+
+ spin_unlock_irqrestore(&np->smp_lock, flags);
+ goto attach_error;
+ }
+ ncr_exception(np);
+
+ np->disc = 1;
+
+ /*
+ * The middle-level SCSI driver does not wait for devices to settle.
+ * Wait synchronously if more than 2 seconds.
+ */
+ if (driver_setup.settle_delay > 2) {
+ printk(KERN_INFO "%s: waiting %d seconds for scsi devices to settle...\n",
+ ncr_name(np), driver_setup.settle_delay);
+ mdelay(1000 * driver_setup.settle_delay);
+ }
+
+ /* start the timeout daemon */
+ np->lasttime=0;
+ ncr_timeout (np);
+
+ /* use SIMPLE TAG messages by default */
+#ifdef SCSI_NCR_ALWAYS_SIMPLE_TAG
+ np->order = M_SIMPLE_TAG;
+#endif
+
+ spin_unlock_irqrestore(&np->smp_lock, flags);
+
+ return instance;
+
+ attach_error:
+ if (!instance)
+ return NULL;
+ printk(KERN_INFO "%s: detaching...\n", ncr_name(np));
+ if (!np)
+ goto unregister;
+ if (np->scripth0)
+ m_free_dma(np->scripth0, sizeof(struct scripth), "SCRIPTH");
+ if (np->script0)
+ m_free_dma(np->script0, sizeof(struct script), "SCRIPT");
+ if (np->ccb)
+ m_free_dma(np->ccb, sizeof(struct ccb), "CCB");
+ m_free_dma(np, sizeof(struct ncb), "NCB");
+ host_data->ncb = NULL;
+
+ unregister:
+ scsi_host_put(instance);
+
+ return NULL;
+}
+
+
+int ncr53c8xx_release(struct Scsi_Host *host)
+{
+ struct host_data *host_data;
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx: release\n");
+#endif
+ if (!host)
+ return 1;
+ host_data = (struct host_data *)host->hostdata;
+ if (host_data && host_data->ncb)
+ ncr_detach(host_data->ncb);
+ return 1;
+}
+
+static void ncr53c8xx_set_period(struct scsi_target *starget, int period)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ncb *np = ((struct host_data *)shost->hostdata)->ncb;
+ struct tcb *tp = &np->target[starget->id];
+
+ if (period > np->maxsync)
+ period = np->maxsync;
+ else if (period < np->minsync)
+ period = np->minsync;
+
+ tp->usrsync = period;
+
+ ncr_negotiate(np, tp);
+}
+
+static void ncr53c8xx_set_offset(struct scsi_target *starget, int offset)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ncb *np = ((struct host_data *)shost->hostdata)->ncb;
+ struct tcb *tp = &np->target[starget->id];
+
+ if (offset > np->maxoffs)
+ offset = np->maxoffs;
+ else if (offset < 0)
+ offset = 0;
+
+ tp->maxoffs = offset;
+
+ ncr_negotiate(np, tp);
+}
+
+static void ncr53c8xx_set_width(struct scsi_target *starget, int width)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ncb *np = ((struct host_data *)shost->hostdata)->ncb;
+ struct tcb *tp = &np->target[starget->id];
+
+ if (width > np->maxwide)
+ width = np->maxwide;
+ else if (width < 0)
+ width = 0;
+
+ tp->usrwide = width;
+
+ ncr_negotiate(np, tp);
+}
+
+static void ncr53c8xx_get_signalling(struct Scsi_Host *shost)
+{
+ struct ncb *np = ((struct host_data *)shost->hostdata)->ncb;
+ enum spi_signal_type type;
+
+ switch (np->scsi_mode) {
+ case SMODE_SE:
+ type = SPI_SIGNAL_SE;
+ break;
+ case SMODE_HVD:
+ type = SPI_SIGNAL_HVD;
+ break;
+ default:
+ type = SPI_SIGNAL_UNKNOWN;
+ break;
+ }
+ spi_signalling(shost) = type;
+}
+
+static struct spi_function_template ncr53c8xx_transport_functions = {
+ .set_period = ncr53c8xx_set_period,
+ .show_period = 1,
+ .set_offset = ncr53c8xx_set_offset,
+ .show_offset = 1,
+ .set_width = ncr53c8xx_set_width,
+ .show_width = 1,
+ .get_signalling = ncr53c8xx_get_signalling,
+};
+
+int __init ncr53c8xx_init(void)
+{
+ ncr53c8xx_transport_template = spi_attach_transport(&ncr53c8xx_transport_functions);
+ if (!ncr53c8xx_transport_template)
+ return -ENODEV;
+ return 0;
+}
+
+void ncr53c8xx_exit(void)
+{
+ spi_release_transport(ncr53c8xx_transport_template);
+}
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
new file mode 100644
index 000000000000..05c7b83cef09
--- /dev/null
+++ b/drivers/scsi/ncr53c8xx.h
@@ -0,0 +1,101 @@
+/******************************************************************************
+** Device driver for the PCI-SCSI NCR538XX controller family.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You 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.
+**
+**-----------------------------------------------------------------------------
+**
+** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver
+** and is currently maintained by
+**
+** Gerard Roudier <groudier@free.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+** And has been ported to NetBSD by
+** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
+**
+*******************************************************************************
+*/
+
+#ifndef NCR53C8XX_H
+#define NCR53C8XX_H
+
+#include <scsi/scsi_host.h>
+
+#include "sym53c8xx_defs.h"
+
+/*
+ Build a scatter/gather entry.
+ see sym53c8xx_2/sym_hipd.h for more detailed sym_build_sge()
+ implementation ;)
+ */
+
+#define ncr_build_sge(np, data, badd, len) \
+do { \
+ (data)->addr = cpu_to_scr(badd); \
+ (data)->size = cpu_to_scr(len); \
+} while (0)
+
+/*==========================================================
+**
+** Structures used by the detection routine to transmit
+** device configuration to the attach function.
+**
+**==========================================================
+*/
+struct ncr_slot {
+ u_long base;
+ u_long base_2;
+ u_long base_c;
+ u_long base_2_c;
+ void __iomem *base_v;
+ void __iomem *base_2_v;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ volatile struct ncr_reg __iomem *reg;
+};
+
+/*==========================================================
+**
+** Structure used by detection routine to save data on
+** each detected board for attach.
+**
+**==========================================================
+*/
+struct ncr_device {
+ struct device *dev;
+ struct ncr_slot slot;
+ struct ncr_chip chip;
+ u_char host_id;
+ u8 differential;
+};
+
+extern struct Scsi_Host *ncr_attach(struct scsi_host_template *tpnt, int unit, struct ncr_device *device);
+extern int ncr53c8xx_release(struct Scsi_Host *host);
+irqreturn_t ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
+extern int ncr53c8xx_init(void);
+extern void ncr53c8xx_exit(void);
+
+#endif /* NCR53C8XX_H */
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
new file mode 100644
index 000000000000..acfead1e9f17
--- /dev/null
+++ b/drivers/scsi/nsp32.c
@@ -0,0 +1,3585 @@
+/*
+ * NinjaSCSI-32Bi Cardbus, NinjaSCSI-32UDE PCI/CardBus SCSI driver
+ * Copyright (C) 2001, 2002, 2003
+ * YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+ * GOTO Masanori <gotom@debian.or.jp>, <gotom@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Revision History:
+ * 1.0: Initial Release.
+ * 1.1: Add /proc SDTR status.
+ * Remove obsolete error handler nsp32_reset.
+ * Some clean up.
+ * 1.2: PowerPC (big endian) support.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+# include <linux/blk.h>
+#endif
+
+#include "nsp32.h"
+
+
+/***********************************************************************
+ * Module parameters
+ */
+static int trans_mode = 0; /* default: BIOS */
+module_param (trans_mode, int, 0);
+MODULE_PARM_DESC(trans_mode, "transfer mode (0: BIOS(default) 1: Async 2: Ultra20M");
+#define ASYNC_MODE 1
+#define ULTRA20M_MODE 2
+
+static int auto_param = 0; /* default: ON */
+module_param (auto_param, bool, 0);
+MODULE_PARM_DESC(auto_param, "AutoParameter mode (0: ON(default) 1: OFF)");
+
+static int disc_priv = 1; /* default: OFF */
+module_param (disc_priv, bool, 0);
+MODULE_PARM_DESC(disc_priv, "disconnection privilege mode (0: ON 1: OFF(default))");
+
+MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>, GOTO Masanori <gotom@debian.or.jp>");
+MODULE_DESCRIPTION("Workbit NinjaSCSI-32Bi/UDE CardBus/PCI SCSI host bus adapter module");
+MODULE_LICENSE("GPL");
+
+static const char *nsp32_release_version = "1.2";
+
+
+/****************************************************************************
+ * Supported hardware
+ */
+static struct pci_device_id nsp32_pci_table[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_IODATA,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_IODATA,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_KME,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_KME,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_WBT,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_WORKBIT,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_WORKBIT_STANDARD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_WORKBIT,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_LOGITEC,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_LOGITEC,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_MELCO,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO_II,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_MELCO,
+ },
+ {0,0,},
+};
+MODULE_DEVICE_TABLE(pci, nsp32_pci_table);
+
+static nsp32_hw_data nsp32_data_base; /* probe <-> detect glue */
+
+
+/*
+ * Period/AckWidth speed conversion table
+ *
+ * Note: This period/ackwidth speed table must be in descending order.
+ */
+static nsp32_sync_table nsp32_sync_table_40M[] = {
+ /* {PNo, AW, SP, EP, SREQ smpl} Speed(MB/s) Period AckWidth */
+ {0x1, 0, 0x0c, 0x0c, SMPL_40M}, /* 20.0 : 50ns, 25ns */
+ {0x2, 0, 0x0d, 0x18, SMPL_40M}, /* 13.3 : 75ns, 25ns */
+ {0x3, 1, 0x19, 0x19, SMPL_40M}, /* 10.0 : 100ns, 50ns */
+ {0x4, 1, 0x1a, 0x1f, SMPL_20M}, /* 8.0 : 125ns, 50ns */
+ {0x5, 2, 0x20, 0x25, SMPL_20M}, /* 6.7 : 150ns, 75ns */
+ {0x6, 2, 0x26, 0x31, SMPL_20M}, /* 5.7 : 175ns, 75ns */
+ {0x7, 3, 0x32, 0x32, SMPL_20M}, /* 5.0 : 200ns, 100ns */
+ {0x8, 3, 0x33, 0x38, SMPL_10M}, /* 4.4 : 225ns, 100ns */
+ {0x9, 3, 0x39, 0x3e, SMPL_10M}, /* 4.0 : 250ns, 100ns */
+};
+
+static nsp32_sync_table nsp32_sync_table_20M[] = {
+ {0x1, 0, 0x19, 0x19, SMPL_40M}, /* 10.0 : 100ns, 50ns */
+ {0x2, 0, 0x1a, 0x25, SMPL_20M}, /* 6.7 : 150ns, 50ns */
+ {0x3, 1, 0x26, 0x32, SMPL_20M}, /* 5.0 : 200ns, 100ns */
+ {0x4, 1, 0x33, 0x3e, SMPL_10M}, /* 4.0 : 250ns, 100ns */
+ {0x5, 2, 0x3f, 0x4b, SMPL_10M}, /* 3.3 : 300ns, 150ns */
+ {0x6, 2, 0x4c, 0x57, SMPL_10M}, /* 2.8 : 350ns, 150ns */
+ {0x7, 3, 0x58, 0x64, SMPL_10M}, /* 2.5 : 400ns, 200ns */
+ {0x8, 3, 0x65, 0x70, SMPL_10M}, /* 2.2 : 450ns, 200ns */
+ {0x9, 3, 0x71, 0x7d, SMPL_10M}, /* 2.0 : 500ns, 200ns */
+};
+
+static nsp32_sync_table nsp32_sync_table_pci[] = {
+ {0x1, 0, 0x0c, 0x0f, SMPL_40M}, /* 16.6 : 60ns, 30ns */
+ {0x2, 0, 0x10, 0x16, SMPL_40M}, /* 11.1 : 90ns, 30ns */
+ {0x3, 1, 0x17, 0x1e, SMPL_20M}, /* 8.3 : 120ns, 60ns */
+ {0x4, 1, 0x1f, 0x25, SMPL_20M}, /* 6.7 : 150ns, 60ns */
+ {0x5, 2, 0x26, 0x2d, SMPL_20M}, /* 5.6 : 180ns, 90ns */
+ {0x6, 2, 0x2e, 0x34, SMPL_10M}, /* 4.8 : 210ns, 90ns */
+ {0x7, 3, 0x35, 0x3c, SMPL_10M}, /* 4.2 : 240ns, 120ns */
+ {0x8, 3, 0x3d, 0x43, SMPL_10M}, /* 3.7 : 270ns, 120ns */
+ {0x9, 3, 0x44, 0x4b, SMPL_10M}, /* 3.3 : 300ns, 120ns */
+};
+
+/*
+ * function declaration
+ */
+/* module entry point */
+static int __devinit nsp32_probe (struct pci_dev *, const struct pci_device_id *);
+static void __devexit nsp32_remove(struct pci_dev *);
+static int __init init_nsp32 (void);
+static void __exit exit_nsp32 (void);
+
+/* struct Scsi_Host_Template */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+static int nsp32_proc_info (struct Scsi_Host *, char *, char **, off_t, int, int);
+#else
+static int nsp32_proc_info (char *, char **, off_t, int, int, int);
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+static int nsp32_detect (struct pci_dev *pdev);
+#else
+static int nsp32_detect (Scsi_Host_Template *);
+#endif
+static int nsp32_queuecommand(struct scsi_cmnd *,
+ void (*done)(struct scsi_cmnd *));
+static const char *nsp32_info (struct Scsi_Host *);
+static int nsp32_release (struct Scsi_Host *);
+
+/* SCSI error handler */
+static int nsp32_eh_abort (struct scsi_cmnd *);
+static int nsp32_eh_bus_reset (struct scsi_cmnd *);
+static int nsp32_eh_host_reset(struct scsi_cmnd *);
+
+/* generate SCSI message */
+static void nsp32_build_identify(struct scsi_cmnd *);
+static void nsp32_build_nop (struct scsi_cmnd *);
+static void nsp32_build_reject (struct scsi_cmnd *);
+static void nsp32_build_sdtr (struct scsi_cmnd *, unsigned char, unsigned char);
+
+/* SCSI message handler */
+static int nsp32_busfree_occur(struct scsi_cmnd *, unsigned short);
+static void nsp32_msgout_occur (struct scsi_cmnd *);
+static void nsp32_msgin_occur (struct scsi_cmnd *, unsigned long, unsigned short);
+
+static int nsp32_setup_sg_table (struct scsi_cmnd *);
+static int nsp32_selection_autopara(struct scsi_cmnd *);
+static int nsp32_selection_autoscsi(struct scsi_cmnd *);
+static void nsp32_scsi_done (struct scsi_cmnd *);
+static int nsp32_arbitration (struct scsi_cmnd *, unsigned int);
+static int nsp32_reselection (struct scsi_cmnd *, unsigned char);
+static void nsp32_adjust_busfree (struct scsi_cmnd *, unsigned int);
+static void nsp32_restart_autoscsi (struct scsi_cmnd *, unsigned short);
+
+/* SCSI SDTR */
+static void nsp32_analyze_sdtr (struct scsi_cmnd *);
+static int nsp32_search_period_entry(nsp32_hw_data *, nsp32_target *, unsigned char);
+static void nsp32_set_async (nsp32_hw_data *, nsp32_target *);
+static void nsp32_set_max_sync (nsp32_hw_data *, nsp32_target *, unsigned char *, unsigned char *);
+static void nsp32_set_sync_entry (nsp32_hw_data *, nsp32_target *, int, unsigned char);
+
+/* SCSI bus status handler */
+static void nsp32_wait_req (nsp32_hw_data *, int);
+static void nsp32_wait_sack (nsp32_hw_data *, int);
+static void nsp32_sack_assert (nsp32_hw_data *);
+static void nsp32_sack_negate (nsp32_hw_data *);
+static void nsp32_do_bus_reset(nsp32_hw_data *);
+
+/* hardware interrupt handler */
+static irqreturn_t do_nsp32_isr(int, void *, struct pt_regs *);
+
+/* initialize hardware */
+static int nsp32hw_init(nsp32_hw_data *);
+
+/* EEPROM handler */
+static int nsp32_getprom_param (nsp32_hw_data *);
+static int nsp32_getprom_at24 (nsp32_hw_data *);
+static int nsp32_getprom_c16 (nsp32_hw_data *);
+static void nsp32_prom_start (nsp32_hw_data *);
+static void nsp32_prom_stop (nsp32_hw_data *);
+static int nsp32_prom_read (nsp32_hw_data *, int);
+static int nsp32_prom_read_bit (nsp32_hw_data *);
+static void nsp32_prom_write_bit(nsp32_hw_data *, int);
+static void nsp32_prom_set (nsp32_hw_data *, int, int);
+static int nsp32_prom_get (nsp32_hw_data *, int);
+
+/* debug/warning/info message */
+static void nsp32_message (const char *, int, char *, char *, ...);
+#ifdef NSP32_DEBUG
+static void nsp32_dmessage(const char *, int, int, char *, ...);
+#endif
+
+/*
+ * max_sectors is currently limited up to 128.
+ */
+static struct scsi_host_template nsp32_template = {
+ .proc_name = "nsp32",
+ .name = "Workbit NinjaSCSI-32Bi/UDE",
+ .proc_info = nsp32_proc_info,
+ .info = nsp32_info,
+ .queuecommand = nsp32_queuecommand,
+ .can_queue = 1,
+ .sg_tablesize = NSP32_SG_SIZE,
+ .max_sectors = 128,
+ .cmd_per_lun = 1,
+ .this_id = NSP32_HOST_SCSIID,
+ .use_clustering = DISABLE_CLUSTERING,
+ .eh_abort_handler = nsp32_eh_abort,
+/* .eh_device_reset_handler = NULL, */
+ .eh_bus_reset_handler = nsp32_eh_bus_reset,
+ .eh_host_reset_handler = nsp32_eh_host_reset,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74))
+ .detect = nsp32_detect,
+ .release = nsp32_release,
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2))
+ .use_new_eh_code = 1,
+#else
+/* .highmem_io = 1, */
+#endif
+};
+
+#include "nsp32_io.h"
+
+/***********************************************************************
+ * debug, error print
+ */
+#ifndef NSP32_DEBUG
+# define NSP32_DEBUG_MASK 0x000000
+# define nsp32_msg(type, args...) nsp32_message ("", 0, (type), args)
+# define nsp32_dbg(mask, args...) /* */
+#else
+# define NSP32_DEBUG_MASK 0xffffff
+# define nsp32_msg(type, args...) \
+ nsp32_message (__FUNCTION__, __LINE__, (type), args)
+# define nsp32_dbg(mask, args...) \
+ nsp32_dmessage(__FUNCTION__, __LINE__, (mask), args)
+#endif
+
+#define NSP32_DEBUG_QUEUECOMMAND BIT(0)
+#define NSP32_DEBUG_REGISTER BIT(1)
+#define NSP32_DEBUG_AUTOSCSI BIT(2)
+#define NSP32_DEBUG_INTR BIT(3)
+#define NSP32_DEBUG_SGLIST BIT(4)
+#define NSP32_DEBUG_BUSFREE BIT(5)
+#define NSP32_DEBUG_CDB_CONTENTS BIT(6)
+#define NSP32_DEBUG_RESELECTION BIT(7)
+#define NSP32_DEBUG_MSGINOCCUR BIT(8)
+#define NSP32_DEBUG_EEPROM BIT(9)
+#define NSP32_DEBUG_MSGOUTOCCUR BIT(10)
+#define NSP32_DEBUG_BUSRESET BIT(11)
+#define NSP32_DEBUG_RESTART BIT(12)
+#define NSP32_DEBUG_SYNC BIT(13)
+#define NSP32_DEBUG_WAIT BIT(14)
+#define NSP32_DEBUG_TARGETFLAG BIT(15)
+#define NSP32_DEBUG_PROC BIT(16)
+#define NSP32_DEBUG_INIT BIT(17)
+#define NSP32_SPECIAL_PRINT_REGISTER BIT(20)
+
+#define NSP32_DEBUG_BUF_LEN 100
+
+static void nsp32_message(const char *func, int line, char *type, char *fmt, ...)
+{
+ va_list args;
+ char buf[NSP32_DEBUG_BUF_LEN];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+#ifndef NSP32_DEBUG
+ printk("%snsp32: %s\n", type, buf);
+#else
+ printk("%snsp32: %s (%d): %s\n", type, func, line, buf);
+#endif
+}
+
+#ifdef NSP32_DEBUG
+static void nsp32_dmessage(const char *func, int line, int mask, char *fmt, ...)
+{
+ va_list args;
+ char buf[NSP32_DEBUG_BUF_LEN];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ if (mask & NSP32_DEBUG_MASK) {
+ printk("nsp32-debug: 0x%x %s (%d): %s\n", mask, func, line, buf);
+ }
+}
+#endif
+
+#ifdef NSP32_DEBUG
+# include "nsp32_debug.c"
+#else
+# define show_command(arg) /* */
+# define show_busphase(arg) /* */
+# define show_autophase(arg) /* */
+#endif
+
+/*
+ * IDENTIFY Message
+ */
+static void nsp32_build_identify(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+ int mode = FALSE;
+
+ /* XXX: Auto DiscPriv detection is progressing... */
+ if (disc_priv == 0) {
+ /* mode = TRUE; */
+ }
+
+ data->msgoutbuf[pos] = IDENTIFY(mode, SCpnt->device->lun); pos++;
+
+ data->msgout_len = pos;
+}
+
+/*
+ * SDTR Message Routine
+ */
+static void nsp32_build_sdtr(struct scsi_cmnd *SCpnt,
+ unsigned char period,
+ unsigned char offset)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+
+ data->msgoutbuf[pos] = EXTENDED_MESSAGE; pos++;
+ data->msgoutbuf[pos] = EXTENDED_SDTR_LEN; pos++;
+ data->msgoutbuf[pos] = EXTENDED_SDTR; pos++;
+ data->msgoutbuf[pos] = period; pos++;
+ data->msgoutbuf[pos] = offset; pos++;
+
+ data->msgout_len = pos;
+}
+
+/*
+ * No Operation Message
+ */
+static void nsp32_build_nop(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+
+ if (pos != 0) {
+ nsp32_msg(KERN_WARNING,
+ "Some messages are already contained!");
+ return;
+ }
+
+ data->msgoutbuf[pos] = NOP; pos++;
+ data->msgout_len = pos;
+}
+
+/*
+ * Reject Message
+ */
+static void nsp32_build_reject(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+
+ data->msgoutbuf[pos] = MESSAGE_REJECT; pos++;
+ data->msgout_len = pos;
+}
+
+/*
+ * timer
+ */
+#if 0
+static void nsp32_start_timer(struct scsi_cmnd *SCpnt, int time)
+{
+ unsigned int base = SCpnt->host->io_port;
+
+ nsp32_dbg(NSP32_DEBUG_INTR, "timer=%d", time);
+
+ if (time & (~TIMER_CNT_MASK)) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "timer set overflow");
+ }
+
+ nsp32_write2(base, TIMER_SET, time & TIMER_CNT_MASK);
+}
+#endif
+
+
+/*
+ * set SCSI command and other parameter to asic, and start selection phase
+ */
+static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned char target = SCpnt->device->id;
+ nsp32_autoparam *param = data->autoparam;
+ unsigned char phase;
+ int i, ret;
+ unsigned int msgout;
+ u16_le s;
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");
+
+ /*
+ * check bus free
+ */
+ phase = nsp32_read1(base, SCSI_BUS_MONITOR);
+ if (phase != BUSMON_BUS_FREE) {
+ nsp32_msg(KERN_WARNING, "bus busy");
+ show_busphase(phase & BUSMON_PHASE_MASK);
+ SCpnt->result = DID_BUS_BUSY << 16;
+ return FALSE;
+ }
+
+ /*
+ * message out
+ *
+ * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.
+ * over 3 messages needs another routine.
+ */
+ if (data->msgout_len == 0) {
+ nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");
+ SCpnt->result = DID_ERROR << 16;
+ return FALSE;
+ } else if (data->msgout_len > 0 && data->msgout_len <= 3) {
+ msgout = 0;
+ for (i = 0; i < data->msgout_len; i++) {
+ /*
+ * the sending order of the message is:
+ * MCNT 3: MSG#0 -> MSG#1 -> MSG#2
+ * MCNT 2: MSG#1 -> MSG#2
+ * MCNT 1: MSG#2
+ */
+ msgout >>= 8;
+ msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);
+ }
+ msgout |= MV_VALID; /* MV valid */
+ msgout |= (unsigned int)data->msgout_len; /* len */
+ } else {
+ /* data->msgout_len > 3 */
+ msgout = 0;
+ }
+
+ // nsp_dbg(NSP32_DEBUG_AUTOSCSI, "sel time out=0x%x\n", nsp32_read2(base, SEL_TIME_OUT));
+ // nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
+
+ /*
+ * setup asic parameter
+ */
+ memset(param, 0, sizeof(nsp32_autoparam));
+
+ /* cdb */
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ param->cdb[4 * i] = SCpnt->cmnd[i];
+ }
+
+ /* outgoing messages */
+ param->msgout = cpu_to_le32(msgout);
+
+ /* syncreg, ackwidth, target id, SREQ sampling rate */
+ param->syncreg = data->cur_target->syncreg;
+ param->ackwidth = data->cur_target->ackwidth;
+ param->target_id = BIT(host_id) | BIT(target);
+ param->sample_reg = data->cur_target->sample_reg;
+
+ // nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "sample rate=0x%x\n", data->cur_target->sample_reg);
+
+ /* command control */
+ param->command_control = cpu_to_le16(CLEAR_CDB_FIFO_POINTER |
+ AUTOSCSI_START |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 |
+ AUTO_ATN );
+
+
+ /* transfer control */
+ s = 0;
+ switch (data->trans_method) {
+ case NSP32_TRANSFER_BUSMASTER:
+ s |= BM_START;
+ break;
+ case NSP32_TRANSFER_MMIO:
+ s |= CB_MMIO_MODE;
+ break;
+ case NSP32_TRANSFER_PIO:
+ s |= CB_IO_MODE;
+ break;
+ default:
+ nsp32_msg(KERN_ERR, "unknown trans_method");
+ break;
+ }
+ /*
+ * OR-ed BLIEND_MODE, FIFO intr is decreased, instead of PCI bus waits.
+ * For bus master transfer, it's taken off.
+ */
+ s |= (TRANSFER_GO | ALL_COUNTER_CLR);
+ param->transfer_control = cpu_to_le16(s);
+
+ /* sg table addr */
+ param->sgt_pointer = cpu_to_le32(data->cur_lunt->sglun_paddr);
+
+ /*
+ * transfer parameter to ASIC
+ */
+ nsp32_write4(base, SGT_ADR, data->auto_paddr);
+ nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |
+ AUTO_PARAMETER );
+
+ /*
+ * Check arbitration
+ */
+ ret = nsp32_arbitration(SCpnt, base);
+
+ return ret;
+}
+
+
+/*
+ * Selection with AUTO SCSI (without AUTO PARAMETER)
+ */
+static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned char target = SCpnt->device->id;
+ unsigned char phase;
+ int status;
+ unsigned short command = 0;
+ unsigned int msgout = 0;
+ unsigned short execph;
+ int i;
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");
+
+ /*
+ * IRQ disable
+ */
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+
+ /*
+ * check bus line
+ */
+ phase = nsp32_read1(base, SCSI_BUS_MONITOR);
+ if(((phase & BUSMON_BSY) == 1) || (phase & BUSMON_SEL) == 1) {
+ nsp32_msg(KERN_WARNING, "bus busy");
+ SCpnt->result = DID_BUS_BUSY << 16;
+ status = 1;
+ goto out;
+ }
+
+ /*
+ * clear execph
+ */
+ execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);
+
+ /*
+ * clear FIFO counter to set CDBs
+ */
+ nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER);
+
+ /*
+ * set CDB0 - CDB15
+ */
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);
+ }
+ nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[0]);
+
+ /*
+ * set SCSIOUT LATCH(initiator)/TARGET(target) (OR-ed) ID
+ */
+ nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID, BIT(host_id) | BIT(target));
+
+ /*
+ * set SCSI MSGOUT REG
+ *
+ * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.
+ * over 3 messages needs another routine.
+ */
+ if (data->msgout_len == 0) {
+ nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");
+ SCpnt->result = DID_ERROR << 16;
+ status = 1;
+ goto out;
+ } else if (data->msgout_len > 0 && data->msgout_len <= 3) {
+ msgout = 0;
+ for (i = 0; i < data->msgout_len; i++) {
+ /*
+ * the sending order of the message is:
+ * MCNT 3: MSG#0 -> MSG#1 -> MSG#2
+ * MCNT 2: MSG#1 -> MSG#2
+ * MCNT 1: MSG#2
+ */
+ msgout >>= 8;
+ msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);
+ }
+ msgout |= MV_VALID; /* MV valid */
+ msgout |= (unsigned int)data->msgout_len; /* len */
+ nsp32_write4(base, SCSI_MSG_OUT, msgout);
+ } else {
+ /* data->msgout_len > 3 */
+ nsp32_write4(base, SCSI_MSG_OUT, 0);
+ }
+
+ /*
+ * set selection timeout(= 250ms)
+ */
+ nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
+
+ /*
+ * set SREQ hazard killer sampling rate
+ *
+ * TODO: sample_rate (BASE+0F) is 0 when internal clock = 40MHz.
+ * check other internal clock!
+ */
+ nsp32_write1(base, SREQ_SMPL_RATE, data->cur_target->sample_reg);
+
+ /*
+ * clear Arbit
+ */
+ nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+ /*
+ * set SYNCREG
+ * Don't set BM_START_ADR before setting this register.
+ */
+ nsp32_write1(base, SYNC_REG, data->cur_target->syncreg);
+
+ /*
+ * set ACKWIDTH
+ */
+ nsp32_write1(base, ACK_WIDTH, data->cur_target->ackwidth);
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
+ "syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",
+ nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),
+ nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgout_len=%d, msgout=0x%x",
+ data->msgout_len, msgout);
+
+ /*
+ * set SGT ADDR (physical address)
+ */
+ nsp32_write4(base, SGT_ADR, data->cur_lunt->sglun_paddr);
+
+ /*
+ * set TRANSFER CONTROL REG
+ */
+ command = 0;
+ command |= (TRANSFER_GO | ALL_COUNTER_CLR);
+ if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+ if (SCpnt->request_bufflen > 0) {
+ command |= BM_START;
+ }
+ } else if (data->trans_method & NSP32_TRANSFER_MMIO) {
+ command |= CB_MMIO_MODE;
+ } else if (data->trans_method & NSP32_TRANSFER_PIO) {
+ command |= CB_IO_MODE;
+ }
+ nsp32_write2(base, TRANSFER_CONTROL, command);
+
+ /*
+ * start AUTO SCSI, kick off arbitration
+ */
+ command = (CLEAR_CDB_FIFO_POINTER |
+ AUTOSCSI_START |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 |
+ AUTO_ATN );
+ nsp32_write2(base, COMMAND_CONTROL, command);
+
+ /*
+ * Check arbitration
+ */
+ status = nsp32_arbitration(SCpnt, base);
+
+ out:
+ /*
+ * IRQ enable
+ */
+ nsp32_write2(base, IRQ_CONTROL, 0);
+
+ return status;
+}
+
+
+/*
+ * Arbitration Status Check
+ *
+ * Note: Arbitration counter is waited during ARBIT_GO is not lifting.
+ * Using udelay(1) consumes CPU time and system time, but
+ * arbitration delay time is defined minimal 2.4us in SCSI
+ * specification, thus udelay works as coarse grained wait timer.
+ */
+static int nsp32_arbitration(struct scsi_cmnd *SCpnt, unsigned int base)
+{
+ unsigned char arbit;
+ int status = TRUE;
+ int time = 0;
+
+ do {
+ arbit = nsp32_read1(base, ARBIT_STATUS);
+ time++;
+ } while ((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&
+ (time <= ARBIT_TIMEOUT_TIME));
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
+ "arbit: 0x%x, delay time: %d", arbit, time);
+
+ if (arbit & ARBIT_WIN) {
+ /* Arbitration succeeded */
+ SCpnt->result = DID_OK << 16;
+ nsp32_index_write1(base, EXT_PORT, LED_ON); /* PCI LED on */
+ } else if (arbit & ARBIT_FAIL) {
+ /* Arbitration failed */
+ SCpnt->result = DID_BUS_BUSY << 16;
+ status = FALSE;
+ } else {
+ /*
+ * unknown error or ARBIT_GO timeout,
+ * something lock up! guess no connection.
+ */
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit timeout");
+ SCpnt->result = DID_NO_CONNECT << 16;
+ status = FALSE;
+ }
+
+ /*
+ * clear Arbit
+ */
+ nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+ return status;
+}
+
+
+/*
+ * reselection
+ *
+ * Note: This reselection routine is called from msgin_occur,
+ * reselection target id&lun must be already set.
+ * SCSI-2 says IDENTIFY implies RESTORE_POINTER operation.
+ */
+static int nsp32_reselection(struct scsi_cmnd *SCpnt, unsigned char newlun)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned char tmpid, newid;
+
+ nsp32_dbg(NSP32_DEBUG_RESELECTION, "enter");
+
+ /*
+ * calculate reselected SCSI ID
+ */
+ tmpid = nsp32_read1(base, RESELECT_ID);
+ tmpid &= (~BIT(host_id));
+ newid = 0;
+ while (tmpid) {
+ if (tmpid & 1) {
+ break;
+ }
+ tmpid >>= 1;
+ newid++;
+ }
+
+ /*
+ * If reselected New ID:LUN is not existed
+ * or current nexus is not existed, unexpected
+ * reselection is occurred. Send reject message.
+ */
+ if (newid >= ARRAY_SIZE(data->lunt) || newlun >= ARRAY_SIZE(data->lunt[0])) {
+ nsp32_msg(KERN_WARNING, "unknown id/lun");
+ return FALSE;
+ } else if(data->lunt[newid][newlun].SCpnt == NULL) {
+ nsp32_msg(KERN_WARNING, "no SCSI command is processing");
+ return FALSE;
+ }
+
+ data->cur_id = newid;
+ data->cur_lun = newlun;
+ data->cur_target = &(data->target[newid]);
+ data->cur_lunt = &(data->lunt[newid][newlun]);
+
+ /* reset SACK/SavedACK counter (or ALL clear?) */
+ nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+ return TRUE;
+}
+
+
+/*
+ * nsp32_setup_sg_table - build scatter gather list for transfer data
+ * with bus master.
+ *
+ * Note: NinjaSCSI-32Bi/UDE bus master can not transfer over 64KB at a time.
+ */
+static int nsp32_setup_sg_table(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ struct scatterlist *sgl;
+ nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;
+ int num, i;
+ u32_le l;
+
+ if (SCpnt->request_bufflen == 0) {
+ return TRUE;
+ }
+
+ if (sgt == NULL) {
+ nsp32_dbg(NSP32_DEBUG_SGLIST, "SGT == null");
+ return FALSE;
+ }
+
+ if (SCpnt->use_sg) {
+ sgl = (struct scatterlist *)SCpnt->request_buffer;
+ num = pci_map_sg(data->Pci, sgl, SCpnt->use_sg,
+ SCpnt->sc_data_direction);
+ for (i = 0; i < num; i++) {
+ /*
+ * Build nsp32_sglist, substitute sg dma addresses.
+ */
+ sgt[i].addr = cpu_to_le32(sg_dma_address(sgl));
+ sgt[i].len = cpu_to_le32(sg_dma_len(sgl));
+ sgl++;
+
+ if (le32_to_cpu(sgt[i].len) > 0x10000) {
+ nsp32_msg(KERN_ERR,
+ "can't transfer over 64KB at a time, size=0x%lx", le32_to_cpu(sgt[i].len));
+ return FALSE;
+ }
+ nsp32_dbg(NSP32_DEBUG_SGLIST,
+ "num 0x%x : addr 0x%lx len 0x%lx",
+ i,
+ le32_to_cpu(sgt[i].addr),
+ le32_to_cpu(sgt[i].len ));
+ }
+
+ /* set end mark */
+ l = le32_to_cpu(sgt[num-1].len);
+ sgt[num-1].len = cpu_to_le32(l | SGTEND);
+
+ } else {
+ SCpnt->SCp.have_data_in = pci_map_single(data->Pci,
+ SCpnt->request_buffer, SCpnt->request_bufflen,
+ SCpnt->sc_data_direction);
+
+ sgt[0].addr = cpu_to_le32(SCpnt->SCp.have_data_in);
+ sgt[0].len = cpu_to_le32(SCpnt->request_bufflen | SGTEND); /* set end mark */
+
+ if (SCpnt->request_bufflen > 0x10000) {
+ nsp32_msg(KERN_ERR,
+ "can't transfer over 64KB at a time, size=0x%lx", SCpnt->request_bufflen);
+ return FALSE;
+ }
+ nsp32_dbg(NSP32_DEBUG_SGLIST, "single : addr 0x%lx len=0x%lx",
+ le32_to_cpu(sgt[0].addr),
+ le32_to_cpu(sgt[0].len ));
+ }
+
+ return TRUE;
+}
+
+static int nsp32_queuecommand(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ nsp32_target *target;
+ nsp32_lunt *cur_lunt;
+ int ret;
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "
+ "use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",
+ SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0], SCpnt->cmd_len,
+ SCpnt->use_sg, SCpnt->request_buffer, SCpnt->request_bufflen);
+
+ if (data->CurrentSC != NULL) {
+ nsp32_msg(KERN_ERR, "Currentsc != NULL. Cancel this command request");
+ data->CurrentSC = NULL;
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ /* check target ID is not same as this initiator ID */
+ if (SCpnt->device->id == SCpnt->device->host->this_id) {
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "terget==host???");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ /* check target LUN is allowable value */
+ if (SCpnt->device->lun >= MAX_LUN) {
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "no more lun");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ show_command(SCpnt);
+
+ SCpnt->scsi_done = done;
+ data->CurrentSC = SCpnt;
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->resid = SCpnt->request_bufflen;
+
+ SCpnt->SCp.ptr = (char *) SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+
+ /* initialize data */
+ data->msgout_len = 0;
+ data->msgin_len = 0;
+ cur_lunt = &(data->lunt[SCpnt->device->id][SCpnt->device->lun]);
+ cur_lunt->SCpnt = SCpnt;
+ cur_lunt->save_datp = 0;
+ cur_lunt->msgin03 = FALSE;
+ data->cur_lunt = cur_lunt;
+ data->cur_id = SCpnt->device->id;
+ data->cur_lun = SCpnt->device->lun;
+
+ ret = nsp32_setup_sg_table(SCpnt);
+ if (ret == FALSE) {
+ nsp32_msg(KERN_ERR, "SGT fail");
+ SCpnt->result = DID_ERROR << 16;
+ nsp32_scsi_done(SCpnt);
+ return 0;
+ }
+
+ /* Build IDENTIFY */
+ nsp32_build_identify(SCpnt);
+
+ /*
+ * If target is the first time to transfer after the reset
+ * (target don't have SDTR_DONE and SDTR_INITIATOR), sync
+ * message SDTR is needed to do synchronous transfer.
+ */
+ target = &data->target[SCpnt->device->id];
+ data->cur_target = target;
+
+ if (!(target->sync_flag & (SDTR_DONE | SDTR_INITIATOR | SDTR_TARGET))) {
+ unsigned char period, offset;
+
+ if (trans_mode != ASYNC_MODE) {
+ nsp32_set_max_sync(data, target, &period, &offset);
+ nsp32_build_sdtr(SCpnt, period, offset);
+ target->sync_flag |= SDTR_INITIATOR;
+ } else {
+ nsp32_set_async(data, target);
+ target->sync_flag |= SDTR_DONE;
+ }
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "SDTR: entry: %d start_period: 0x%x offset: 0x%x\n",
+ target->limit_entry, period, offset);
+ } else if (target->sync_flag & SDTR_INITIATOR) {
+ /*
+ * It was negotiating SDTR with target, sending from the
+ * initiator, but there are no chance to remove this flag.
+ * Set async because we don't get proper negotiation.
+ */
+ nsp32_set_async(data, target);
+ target->sync_flag &= ~SDTR_INITIATOR;
+ target->sync_flag |= SDTR_DONE;
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "SDTR_INITIATOR: fall back to async");
+ } else if (target->sync_flag & SDTR_TARGET) {
+ /*
+ * It was negotiating SDTR with target, sending from target,
+ * but there are no chance to remove this flag. Set async
+ * because we don't get proper negotiation.
+ */
+ nsp32_set_async(data, target);
+ target->sync_flag &= ~SDTR_TARGET;
+ target->sync_flag |= SDTR_DONE;
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "Unknown SDTR from target is reached, fall back to async.");
+ }
+
+ nsp32_dbg(NSP32_DEBUG_TARGETFLAG,
+ "target: %d sync_flag: 0x%x syncreg: 0x%x ackwidth: 0x%x",
+ SCpnt->device->id, target->sync_flag, target->syncreg,
+ target->ackwidth);
+
+ /* Selection */
+ if (auto_param == 0) {
+ ret = nsp32_selection_autopara(SCpnt);
+ } else {
+ ret = nsp32_selection_autoscsi(SCpnt);
+ }
+
+ if (ret != TRUE) {
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "selection fail");
+ nsp32_scsi_done(SCpnt);
+ }
+
+ return 0;
+}
+
+/* initialize asic */
+static int nsp32hw_init(nsp32_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+ unsigned short irq_stat;
+ unsigned long lc_reg;
+ unsigned char power;
+
+ lc_reg = nsp32_index_read4(base, CFG_LATE_CACHE);
+ if ((lc_reg & 0xff00) == 0) {
+ lc_reg |= (0x20 << 8);
+ nsp32_index_write2(base, CFG_LATE_CACHE, lc_reg & 0xffff);
+ }
+
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+ nsp32_write2(base, TRANSFER_CONTROL, 0);
+ nsp32_write4(base, BM_CNT, 0);
+ nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
+
+ do {
+ irq_stat = nsp32_read2(base, IRQ_STATUS);
+ nsp32_dbg(NSP32_DEBUG_INIT, "irq_stat 0x%x", irq_stat);
+ } while (irq_stat & IRQSTATUS_ANY_IRQ);
+
+ /*
+ * Fill FIFO_FULL_SHLD, FIFO_EMPTY_SHLD. Below parameter is
+ * designated by specification.
+ */
+ if ((data->trans_method & NSP32_TRANSFER_PIO) ||
+ (data->trans_method & NSP32_TRANSFER_MMIO)) {
+ nsp32_index_write1(base, FIFO_FULL_SHLD_COUNT, 0x40);
+ nsp32_index_write1(base, FIFO_EMPTY_SHLD_COUNT, 0x40);
+ } else if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+ nsp32_index_write1(base, FIFO_FULL_SHLD_COUNT, 0x10);
+ nsp32_index_write1(base, FIFO_EMPTY_SHLD_COUNT, 0x60);
+ } else {
+ nsp32_dbg(NSP32_DEBUG_INIT, "unknown transfer mode");
+ }
+
+ nsp32_dbg(NSP32_DEBUG_INIT, "full 0x%x emp 0x%x",
+ nsp32_index_read1(base, FIFO_FULL_SHLD_COUNT),
+ nsp32_index_read1(base, FIFO_EMPTY_SHLD_COUNT));
+
+ nsp32_index_write1(base, CLOCK_DIV, data->clock);
+ nsp32_index_write1(base, BM_CYCLE, MEMRD_CMD1 | SGT_AUTO_PARA_MEMED_CMD);
+ nsp32_write1(base, PARITY_CONTROL, 0); /* parity check is disable */
+
+ /*
+ * initialize MISC_WRRD register
+ *
+ * Note: Designated parameters is obeyed as following:
+ * MISC_SCSI_DIRECTION_DETECTOR_SELECT: It must be set.
+ * MISC_MASTER_TERMINATION_SELECT: It must be set.
+ * MISC_BMREQ_NEGATE_TIMING_SEL: It should be set.
+ * MISC_AUTOSEL_TIMING_SEL: It should be set.
+ * MISC_BMSTOP_CHANGE2_NONDATA_PHASE: It should be set.
+ * MISC_DELAYED_BMSTART: It's selected for safety.
+ *
+ * Note: If MISC_BMSTOP_CHANGE2_NONDATA_PHASE is set, then
+ * we have to set TRANSFERCONTROL_BM_START as 0 and set
+ * appropriate value before restarting bus master transfer.
+ */
+ nsp32_index_write2(base, MISC_WR,
+ (SCSI_DIRECTION_DETECTOR_SELECT |
+ DELAYED_BMSTART |
+ MASTER_TERMINATION_SELECT |
+ BMREQ_NEGATE_TIMING_SEL |
+ AUTOSEL_TIMING_SEL |
+ BMSTOP_CHANGE2_NONDATA_PHASE));
+
+ nsp32_index_write1(base, TERM_PWR_CONTROL, 0);
+ power = nsp32_index_read1(base, TERM_PWR_CONTROL);
+ if (!(power & SENSE)) {
+ nsp32_msg(KERN_INFO, "term power on");
+ nsp32_index_write1(base, TERM_PWR_CONTROL, BPWR);
+ }
+
+ nsp32_write2(base, TIMER_SET, TIMER_STOP);
+ nsp32_write2(base, TIMER_SET, TIMER_STOP); /* Required 2 times */
+
+ nsp32_write1(base, SYNC_REG, 0);
+ nsp32_write1(base, ACK_WIDTH, 0);
+ nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
+
+ /*
+ * enable to select designated IRQ (except for
+ * IRQSELECT_SERR, IRQSELECT_PERR, IRQSELECT_BMCNTERR)
+ */
+ nsp32_index_write2(base, IRQ_SELECT, IRQSELECT_TIMER_IRQ |
+ IRQSELECT_SCSIRESET_IRQ |
+ IRQSELECT_FIFO_SHLD_IRQ |
+ IRQSELECT_RESELECT_IRQ |
+ IRQSELECT_PHASE_CHANGE_IRQ |
+ IRQSELECT_AUTO_SCSI_SEQ_IRQ |
+ // IRQSELECT_BMCNTERR_IRQ |
+ IRQSELECT_TARGET_ABORT_IRQ |
+ IRQSELECT_MASTER_ABORT_IRQ );
+ nsp32_write2(base, IRQ_CONTROL, 0);
+
+ /* PCI LED off */
+ nsp32_index_write1(base, EXT_PORT_DDR, LED_OFF);
+ nsp32_index_write1(base, EXT_PORT, LED_OFF);
+
+ return TRUE;
+}
+
+
+/* interrupt routine */
+static irqreturn_t do_nsp32_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ nsp32_hw_data *data = dev_id;
+ unsigned int base = data->BaseAddress;
+ struct scsi_cmnd *SCpnt = data->CurrentSC;
+ unsigned short auto_stat, irq_stat, trans_stat;
+ unsigned char busmon, busphase;
+ unsigned long flags;
+ int ret;
+ int handled = 0;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+ struct Scsi_Host *host = data->Host;
+ spin_lock_irqsave(host->host_lock, flags);
+#else
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+ /*
+ * IRQ check, then enable IRQ mask
+ */
+ irq_stat = nsp32_read2(base, IRQ_STATUS);
+ nsp32_dbg(NSP32_DEBUG_INTR,
+ "enter IRQ: %d, IRQstatus: 0x%x", irq, irq_stat);
+ /* is this interrupt comes from Ninja asic? */
+ if ((irq_stat & IRQSTATUS_ANY_IRQ) == 0) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "shared interrupt: irq other 0x%x", irq_stat);
+ goto out2;
+ }
+ handled = 1;
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+
+ busmon = nsp32_read1(base, SCSI_BUS_MONITOR);
+ busphase = busmon & BUSMON_PHASE_MASK;
+
+ trans_stat = nsp32_read2(base, TRANSFER_STATUS);
+ if ((irq_stat == 0xffff) && (trans_stat == 0xffff)) {
+ nsp32_msg(KERN_INFO, "card disconnect");
+ if (data->CurrentSC != NULL) {
+ nsp32_msg(KERN_INFO, "clean up current SCSI command");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ nsp32_scsi_done(SCpnt);
+ }
+ goto out;
+ }
+
+ /* Timer IRQ */
+ if (irq_stat & IRQSTATUS_TIMER_IRQ) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "timer stop");
+ nsp32_write2(base, TIMER_SET, TIMER_STOP);
+ goto out;
+ }
+
+ /* SCSI reset */
+ if (irq_stat & IRQSTATUS_SCSIRESET_IRQ) {
+ nsp32_msg(KERN_INFO, "detected someone do bus reset");
+ nsp32_do_bus_reset(data);
+ if (SCpnt != NULL) {
+ SCpnt->result = DID_RESET << 16;
+ nsp32_scsi_done(SCpnt);
+ }
+ goto out;
+ }
+
+ if (SCpnt == NULL) {
+ nsp32_msg(KERN_WARNING, "SCpnt==NULL this can't be happened");
+ nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x", irq_stat, trans_stat);
+ goto out;
+ }
+
+ /*
+ * AutoSCSI Interrupt.
+ * Note: This interrupt is occurred when AutoSCSI is finished. Then
+ * check SCSIEXECUTEPHASE, and do appropriate action. Each phases are
+ * recorded when AutoSCSI sequencer has been processed.
+ */
+ if(irq_stat & IRQSTATUS_AUTOSCSI_IRQ) {
+ /* getting SCSI executed phase */
+ auto_stat = nsp32_read2(base, SCSI_EXECUTE_PHASE);
+ nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
+
+ /* Selection Timeout, go busfree phase. */
+ if (auto_stat & SELECTION_TIMEOUT) {
+ nsp32_dbg(NSP32_DEBUG_INTR,
+ "selection timeout occurred");
+
+ SCpnt->result = DID_TIME_OUT << 16;
+ nsp32_scsi_done(SCpnt);
+ goto out;
+ }
+
+ if (auto_stat & MSGOUT_PHASE) {
+ /*
+ * MsgOut phase was processed.
+ * If MSG_IN_OCCUER is not set, then MsgOut phase is
+ * completed. Thus, msgout_len must reset. Otherwise,
+ * nothing to do here. If MSG_OUT_OCCUER is occurred,
+ * then we will encounter the condition and check.
+ */
+ if (!(auto_stat & MSG_IN_OCCUER) &&
+ (data->msgout_len <= 3)) {
+ /*
+ * !MSG_IN_OCCUER && msgout_len <=3
+ * ---> AutoSCSI with MSGOUTreg is processed.
+ */
+ data->msgout_len = 0;
+ };
+
+ nsp32_dbg(NSP32_DEBUG_INTR, "MsgOut phase processed");
+ }
+
+ if ((auto_stat & DATA_IN_PHASE) &&
+ (SCpnt->resid > 0) &&
+ ((nsp32_read2(base, FIFO_REST_CNT) & FIFO_REST_MASK) != 0)) {
+ printk( "auto+fifo\n");
+ //nsp32_pio_read(SCpnt);
+ }
+
+ if (auto_stat & (DATA_IN_PHASE | DATA_OUT_PHASE)) {
+ /* DATA_IN_PHASE/DATA_OUT_PHASE was processed. */
+ nsp32_dbg(NSP32_DEBUG_INTR,
+ "Data in/out phase processed");
+
+ /* read BMCNT, SGT pointer addr */
+ nsp32_dbg(NSP32_DEBUG_INTR, "BMCNT=0x%lx",
+ nsp32_read4(base, BM_CNT));
+ nsp32_dbg(NSP32_DEBUG_INTR, "addr=0x%lx",
+ nsp32_read4(base, SGT_ADR));
+ nsp32_dbg(NSP32_DEBUG_INTR, "SACK=0x%lx",
+ nsp32_read4(base, SACK_CNT));
+ nsp32_dbg(NSP32_DEBUG_INTR, "SSACK=0x%lx",
+ nsp32_read4(base, SAVED_SACK_CNT));
+
+ SCpnt->resid = 0; /* all data transfered! */
+ }
+
+ /*
+ * MsgIn Occur
+ */
+ if (auto_stat & MSG_IN_OCCUER) {
+ nsp32_msgin_occur(SCpnt, irq_stat, auto_stat);
+ }
+
+ /*
+ * MsgOut Occur
+ */
+ if (auto_stat & MSG_OUT_OCCUER) {
+ nsp32_msgout_occur(SCpnt);
+ }
+
+ /*
+ * Bus Free Occur
+ */
+ if (auto_stat & BUS_FREE_OCCUER) {
+ ret = nsp32_busfree_occur(SCpnt, auto_stat);
+ if (ret == TRUE) {
+ goto out;
+ }
+ }
+
+ if (auto_stat & STATUS_PHASE) {
+ /*
+ * Read CSB and substitute CSB for SCpnt->result
+ * to save status phase stutas byte.
+ * scsi error handler checks host_byte (DID_*:
+ * low level driver to indicate status), then checks
+ * status_byte (SCSI status byte).
+ */
+ SCpnt->result = (int)nsp32_read1(base, SCSI_CSB_IN);
+ }
+
+ if (auto_stat & ILLEGAL_PHASE) {
+ /* Illegal phase is detected. SACK is not back. */
+ nsp32_msg(KERN_WARNING,
+ "AUTO SCSI ILLEGAL PHASE OCCUR!!!!");
+
+ /* TODO: currently we don't have any action... bus reset? */
+
+ /*
+ * To send back SACK, assert, wait, and negate.
+ */
+ nsp32_sack_assert(data);
+ nsp32_wait_req(data, NEGATE);
+ nsp32_sack_negate(data);
+
+ }
+
+ if (auto_stat & COMMAND_PHASE) {
+ /* nothing to do */
+ nsp32_dbg(NSP32_DEBUG_INTR, "Command phase processed");
+ }
+
+ if (auto_stat & AUTOSCSI_BUSY) {
+ /* AutoSCSI is running */
+ }
+
+ show_autophase(auto_stat);
+ }
+
+ /* FIFO_SHLD_IRQ */
+ if (irq_stat & IRQSTATUS_FIFO_SHLD_IRQ) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "FIFO IRQ");
+
+ switch(busphase) {
+ case BUSPHASE_DATA_OUT:
+ nsp32_dbg(NSP32_DEBUG_INTR, "fifo/write");
+
+ //nsp32_pio_write(SCpnt);
+
+ break;
+
+ case BUSPHASE_DATA_IN:
+ nsp32_dbg(NSP32_DEBUG_INTR, "fifo/read");
+
+ //nsp32_pio_read(SCpnt);
+
+ break;
+
+ case BUSPHASE_STATUS:
+ nsp32_dbg(NSP32_DEBUG_INTR, "fifo/status");
+
+ SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
+
+ break;
+ default:
+ nsp32_dbg(NSP32_DEBUG_INTR, "fifo/other phase");
+ nsp32_dbg(NSP32_DEBUG_INTR, "irq_stat=0x%x trans_stat=0x%x", irq_stat, trans_stat);
+ show_busphase(busphase);
+ break;
+ }
+
+ goto out;
+ }
+
+ /* Phase Change IRQ */
+ if (irq_stat & IRQSTATUS_PHASE_CHANGE_IRQ) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "phase change IRQ");
+
+ switch(busphase) {
+ case BUSPHASE_MESSAGE_IN:
+ nsp32_dbg(NSP32_DEBUG_INTR, "phase chg/msg in");
+ nsp32_msgin_occur(SCpnt, irq_stat, 0);
+ break;
+ default:
+ nsp32_msg(KERN_WARNING, "phase chg/other phase?");
+ nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x\n",
+ irq_stat, trans_stat);
+ show_busphase(busphase);
+ break;
+ }
+ goto out;
+ }
+
+ /* PCI_IRQ */
+ if (irq_stat & IRQSTATUS_PCI_IRQ) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "PCI IRQ occurred");
+ /* Do nothing */
+ }
+
+ /* BMCNTERR_IRQ */
+ if (irq_stat & IRQSTATUS_BMCNTERR_IRQ) {
+ nsp32_msg(KERN_ERR, "Received unexpected BMCNTERR IRQ! ");
+ /*
+ * TODO: To be implemented improving bus master
+ * transfer reliablity when BMCNTERR is occurred in
+ * AutoSCSI phase described in specification.
+ */
+ }
+
+#if 0
+ nsp32_dbg(NSP32_DEBUG_INTR,
+ "irq_stat=0x%x trans_stat=0x%x", irq_stat, trans_stat);
+ show_busphase(busphase);
+#endif
+
+ out:
+ /* disable IRQ mask */
+ nsp32_write2(base, IRQ_CONTROL, 0);
+
+ out2:
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+ spin_unlock_irqrestore(host->host_lock, flags);
+#else
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+
+ nsp32_dbg(NSP32_DEBUG_INTR, "exit");
+
+ return IRQ_RETVAL(handled);
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { \
+ if(length > (pos - buffer)) { \
+ pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+ nsp32_dbg(NSP32_DEBUG_PROC, "buffer=0x%p pos=0x%p length=%d %d\n", buffer, pos, length, length - (pos - buffer));\
+ } \
+ } while(0)
+static int nsp32_proc_info(
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ struct Scsi_Host *host,
+#endif
+ char *buffer,
+ char **start,
+ off_t offset,
+ int length,
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ int hostno,
+#endif
+ int inout)
+{
+ char *pos = buffer;
+ int thislength;
+ unsigned long flags;
+ nsp32_hw_data *data;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ int hostno;
+#else
+ struct Scsi_Host *host;
+#endif
+ unsigned int base;
+ unsigned char mode_reg;
+ int id, speed;
+ long model;
+
+ /* Write is not supported, just return. */
+ if (inout == TRUE) {
+ return -EINVAL;
+ }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ hostno = host->host_no;
+#else
+ /* search this HBA host */
+ host = scsi_host_hn_get(hostno);
+ if (host == NULL) {
+ return -ESRCH;
+ }
+#endif
+ data = (nsp32_hw_data *)host->hostdata;
+ base = host->io_port;
+
+ SPRINTF("NinjaSCSI-32 status\n\n");
+ SPRINTF("Driver version: %s, $Revision: 1.33 $\n", nsp32_release_version);
+ SPRINTF("SCSI host No.: %d\n", hostno);
+ SPRINTF("IRQ: %d\n", host->irq);
+ SPRINTF("IO: 0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
+ SPRINTF("MMIO(virtual address): 0x%lx-0x%lx\n", host->base, host->base + data->MmioLength - 1);
+ SPRINTF("sg_tablesize: %d\n", host->sg_tablesize);
+ SPRINTF("Chip revision: 0x%x\n", (nsp32_read2(base, INDEX_REG) >> 8) & 0xff);
+
+ mode_reg = nsp32_index_read1(base, CHIP_MODE);
+ model = data->pci_devid->driver_data;
+
+#ifdef CONFIG_PM
+ SPRINTF("Power Management: %s\n", (mode_reg & OPTF) ? "yes" : "no");
+#endif
+ SPRINTF("OEM: %ld, %s\n", (mode_reg & (OEM0|OEM1)), nsp32_model[model]);
+
+ spin_lock_irqsave(&(data->Lock), flags);
+ SPRINTF("CurrentSC: 0x%p\n\n", data->CurrentSC);
+ spin_unlock_irqrestore(&(data->Lock), flags);
+
+
+ SPRINTF("SDTR status\n");
+ for (id = 0; id < ARRAY_SIZE(data->target); id++) {
+
+ SPRINTF("id %d: ", id);
+
+ if (id == host->this_id) {
+ SPRINTF("----- NinjaSCSI-32 host adapter\n");
+ continue;
+ }
+
+ if (data->target[id].sync_flag == SDTR_DONE) {
+ if (data->target[id].period == 0 &&
+ data->target[id].offset == ASYNC_OFFSET ) {
+ SPRINTF("async");
+ } else {
+ SPRINTF(" sync");
+ }
+ } else {
+ SPRINTF(" none");
+ }
+
+ if (data->target[id].period != 0) {
+
+ speed = 1000000 / (data->target[id].period * 4);
+
+ SPRINTF(" transfer %d.%dMB/s, offset %d",
+ speed / 1000,
+ speed % 1000,
+ data->target[id].offset
+ );
+ }
+ SPRINTF("\n");
+ }
+
+
+ thislength = pos - (buffer + offset);
+
+ if(thislength < 0) {
+ *start = NULL;
+ return 0;
+ }
+
+
+ thislength = min(thislength, length);
+ *start = buffer + offset;
+
+ return thislength;
+}
+#undef SPRINTF
+
+
+
+/*
+ * Reset parameters and call scsi_done for data->cur_lunt.
+ * Be careful setting SCpnt->result = DID_* before calling this function.
+ */
+static void nsp32_scsi_done(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+
+ /*
+ * unmap pci
+ */
+ if (SCpnt->request_bufflen == 0) {
+ goto skip;
+ }
+
+ if (SCpnt->use_sg) {
+ pci_unmap_sg(data->Pci,
+ (struct scatterlist *)SCpnt->buffer,
+ SCpnt->use_sg, SCpnt->sc_data_direction);
+ } else {
+ pci_unmap_single(data->Pci,
+ (u32)SCpnt->SCp.have_data_in,
+ SCpnt->request_bufflen,
+ SCpnt->sc_data_direction);
+ }
+
+ skip:
+ /*
+ * clear TRANSFERCONTROL_BM_START
+ */
+ nsp32_write2(base, TRANSFER_CONTROL, 0);
+ nsp32_write4(base, BM_CNT, 0);
+
+ /*
+ * call scsi_done
+ */
+ (*SCpnt->scsi_done)(SCpnt);
+
+ /*
+ * reset parameters
+ */
+ data->cur_lunt->SCpnt = NULL;
+ data->cur_lunt = NULL;
+ data->cur_target = NULL;
+ data->CurrentSC = NULL;
+}
+
+
+/*
+ * Bus Free Occur
+ *
+ * Current Phase is BUSFREE. AutoSCSI is automatically execute BUSFREE phase
+ * with ACK reply when below condition is matched:
+ * MsgIn 00: Command Complete.
+ * MsgIn 02: Save Data Pointer.
+ * MsgIn 04: Diconnect.
+ * In other case, unexpected BUSFREE is detected.
+ */
+static int nsp32_busfree_occur(struct scsi_cmnd *SCpnt, unsigned short execph)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+
+ nsp32_dbg(NSP32_DEBUG_BUSFREE, "enter execph=0x%x", execph);
+ show_autophase(execph);
+
+ nsp32_write4(base, BM_CNT, 0);
+ nsp32_write2(base, TRANSFER_CONTROL, 0);
+
+ /*
+ * MsgIn 02: Save Data Pointer
+ *
+ * VALID:
+ * Save Data Pointer is received. Adjust pointer.
+ *
+ * NO-VALID:
+ * SCSI-3 says if Save Data Pointer is not received, then we restart
+ * processing and we can't adjust any SCSI data pointer in next data
+ * phase.
+ */
+ if (execph & MSGIN_02_VALID) {
+ nsp32_dbg(NSP32_DEBUG_BUSFREE, "MsgIn02_Valid");
+
+ /*
+ * Check sack_cnt/saved_sack_cnt, then adjust sg table if
+ * needed.
+ */
+ if (!(execph & MSGIN_00_VALID) &&
+ ((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE))) {
+ unsigned int sacklen, s_sacklen;
+
+ /*
+ * Read SACK count and SAVEDSACK count, then compare.
+ */
+ sacklen = nsp32_read4(base, SACK_CNT );
+ s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);
+
+ /*
+ * If SAVEDSACKCNT == 0, it means SavedDataPointer is
+ * come after data transfering.
+ */
+ if (s_sacklen > 0) {
+ /*
+ * Comparing between sack and savedsack to
+ * check the condition of AutoMsgIn03.
+ *
+ * If they are same, set msgin03 == TRUE,
+ * COMMANDCONTROL_AUTO_MSGIN_03 is enabled at
+ * reselection. On the other hand, if they
+ * aren't same, set msgin03 == FALSE, and
+ * COMMANDCONTROL_AUTO_MSGIN_03 is disabled at
+ * reselection.
+ */
+ if (sacklen != s_sacklen) {
+ data->cur_lunt->msgin03 = FALSE;
+ } else {
+ data->cur_lunt->msgin03 = TRUE;
+ }
+
+ nsp32_adjust_busfree(SCpnt, s_sacklen);
+ }
+ }
+
+ /* This value has not substitude with valid value yet... */
+ //data->cur_lunt->save_datp = data->cur_datp;
+ } else {
+ /*
+ * no processing.
+ */
+ }
+
+ if (execph & MSGIN_03_VALID) {
+ /* MsgIn03 was valid to be processed. No need processing. */
+ }
+
+ /*
+ * target SDTR check
+ */
+ if (data->cur_target->sync_flag & SDTR_INITIATOR) {
+ /*
+ * SDTR negotiation pulled by the initiator has not
+ * finished yet. Fall back to ASYNC mode.
+ */
+ nsp32_set_async(data, data->cur_target);
+ data->cur_target->sync_flag &= ~SDTR_INITIATOR;
+ data->cur_target->sync_flag |= SDTR_DONE;
+ } else if (data->cur_target->sync_flag & SDTR_TARGET) {
+ /*
+ * SDTR negotiation pulled by the target has been
+ * negotiating.
+ */
+ if (execph & (MSGIN_00_VALID | MSGIN_04_VALID)) {
+ /*
+ * If valid message is received, then
+ * negotiation is succeeded.
+ */
+ } else {
+ /*
+ * On the contrary, if unexpected bus free is
+ * occurred, then negotiation is failed. Fall
+ * back to ASYNC mode.
+ */
+ nsp32_set_async(data, data->cur_target);
+ }
+ data->cur_target->sync_flag &= ~SDTR_TARGET;
+ data->cur_target->sync_flag |= SDTR_DONE;
+ }
+
+ /*
+ * It is always ensured by SCSI standard that initiator
+ * switches into Bus Free Phase after
+ * receiving message 00 (Command Complete), 04 (Disconnect).
+ * It's the reason that processing here is valid.
+ */
+ if (execph & MSGIN_00_VALID) {
+ /* MsgIn 00: Command Complete */
+ nsp32_dbg(NSP32_DEBUG_BUSFREE, "command complete");
+
+ SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
+ SCpnt->SCp.Message = 0;
+ nsp32_dbg(NSP32_DEBUG_BUSFREE,
+ "normal end stat=0x%x resid=0x%x\n",
+ SCpnt->SCp.Status, SCpnt->resid);
+ SCpnt->result = (DID_OK << 16) |
+ (SCpnt->SCp.Message << 8) |
+ (SCpnt->SCp.Status << 0);
+ nsp32_scsi_done(SCpnt);
+ /* All operation is done */
+ return TRUE;
+ } else if (execph & MSGIN_04_VALID) {
+ /* MsgIn 04: Disconnect */
+ SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
+ SCpnt->SCp.Message = 4;
+
+ nsp32_dbg(NSP32_DEBUG_BUSFREE, "disconnect");
+ return TRUE;
+ } else {
+ /* Unexpected bus free */
+ nsp32_msg(KERN_WARNING, "unexpected bus free occurred");
+
+ /* DID_ERROR? */
+ //SCpnt->result = (DID_OK << 16) | (SCpnt->SCp.Message << 8) | (SCpnt->SCp.Status << 0);
+ SCpnt->result = DID_ERROR << 16;
+ nsp32_scsi_done(SCpnt);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ * nsp32_adjust_busfree - adjusting SG table
+ *
+ * Note: This driver adjust the SG table using SCSI ACK
+ * counter instead of BMCNT counter!
+ */
+static void nsp32_adjust_busfree(struct scsi_cmnd *SCpnt, unsigned int s_sacklen)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int old_entry = data->cur_entry;
+ int new_entry;
+ int sg_num = data->cur_lunt->sg_num;
+ nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;
+ unsigned int restlen, sentlen;
+ u32_le len, addr;
+
+ nsp32_dbg(NSP32_DEBUG_SGLIST, "old resid=0x%x", SCpnt->resid);
+
+ /* adjust saved SACK count with 4 byte start address boundary */
+ s_sacklen -= le32_to_cpu(sgt[old_entry].addr) & 3;
+
+ /*
+ * calculate new_entry from sack count and each sgt[].len
+ * calculate the byte which is intent to send
+ */
+ sentlen = 0;
+ for (new_entry = old_entry; new_entry < sg_num; new_entry++) {
+ sentlen += (le32_to_cpu(sgt[new_entry].len) & ~SGTEND);
+ if (sentlen > s_sacklen) {
+ break;
+ }
+ }
+
+ /* all sgt is processed */
+ if (new_entry == sg_num) {
+ goto last;
+ }
+
+ if (sentlen == s_sacklen) {
+ /* XXX: confirm it's ok or not */
+ /* In this case, it's ok because we are at
+ the head element of the sg. restlen is correctly calculated. */
+ }
+
+ /* calculate the rest length for transfering */
+ restlen = sentlen - s_sacklen;
+
+ /* update adjusting current SG table entry */
+ len = le32_to_cpu(sgt[new_entry].len);
+ addr = le32_to_cpu(sgt[new_entry].addr);
+ addr += (len - restlen);
+ sgt[new_entry].addr = cpu_to_le32(addr);
+ sgt[new_entry].len = cpu_to_le32(restlen);
+
+ /* set cur_entry with new_entry */
+ data->cur_entry = new_entry;
+
+ return;
+
+ last:
+ if (SCpnt->resid < sentlen) {
+ nsp32_msg(KERN_ERR, "resid underflow");
+ }
+
+ SCpnt->resid -= sentlen;
+ nsp32_dbg(NSP32_DEBUG_SGLIST, "new resid=0x%x", SCpnt->resid);
+
+ /* update hostdata and lun */
+
+ return;
+}
+
+
+/*
+ * It's called MsgOut phase occur.
+ * NinjaSCSI-32Bi/UDE automatically processes up to 3 messages in
+ * message out phase. It, however, has more than 3 messages,
+ * HBA creates the interrupt and we have to process by hand.
+ */
+static void nsp32_msgout_occur(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+ //unsigned short command;
+ long new_sgtp;
+ int i;
+
+ nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
+ "enter: msgout_len: 0x%x", data->msgout_len);
+
+ /*
+ * If MsgOut phase is occurred without having any
+ * message, then No_Operation is sent (SCSI-2).
+ */
+ if (data->msgout_len == 0) {
+ nsp32_build_nop(SCpnt);
+ }
+
+ /*
+ * Set SGTP ADDR current entry for restarting AUTOSCSI,
+ * because SGTP is incremented next point.
+ * There is few statement in the specification...
+ */
+ new_sgtp = data->cur_lunt->sglun_paddr +
+ (data->cur_lunt->cur_entry * sizeof(nsp32_sgtable));
+
+ /*
+ * send messages
+ */
+ for (i = 0; i < data->msgout_len; i++) {
+ nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
+ "%d : 0x%x", i, data->msgoutbuf[i]);
+
+ /*
+ * Check REQ is asserted.
+ */
+ nsp32_wait_req(data, ASSERT);
+
+ if (i == (data->msgout_len - 1)) {
+ /*
+ * If the last message, set the AutoSCSI restart
+ * before send back the ack message. AutoSCSI
+ * restart automatically negate ATN signal.
+ */
+ //command = (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);
+ //nsp32_restart_autoscsi(SCpnt, command);
+ nsp32_write2(base, COMMAND_CONTROL,
+ (CLEAR_CDB_FIFO_POINTER |
+ AUTO_COMMAND_PHASE |
+ AUTOSCSI_RESTART |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 ));
+ }
+ /*
+ * Write data with SACK, then wait sack is
+ * automatically negated.
+ */
+ nsp32_write1(base, SCSI_DATA_WITH_ACK, data->msgoutbuf[i]);
+ nsp32_wait_sack(data, NEGATE);
+
+ nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "bus: 0x%x\n",
+ nsp32_read1(base, SCSI_BUS_MONITOR));
+ };
+
+ data->msgout_len = 0;
+
+ nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "exit");
+}
+
+/*
+ * Restart AutoSCSI
+ *
+ * Note: Restarting AutoSCSI needs set:
+ * SYNC_REG, ACK_WIDTH, SGT_ADR, TRANSFER_CONTROL
+ */
+static void nsp32_restart_autoscsi(struct scsi_cmnd *SCpnt, unsigned short command)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = data->BaseAddress;
+ unsigned short transfer = 0;
+
+ nsp32_dbg(NSP32_DEBUG_RESTART, "enter");
+
+ if (data->cur_target == NULL || data->cur_lunt == NULL) {
+ nsp32_msg(KERN_ERR, "Target or Lun is invalid");
+ }
+
+ /*
+ * set SYNC_REG
+ * Don't set BM_START_ADR before setting this register.
+ */
+ nsp32_write1(base, SYNC_REG, data->cur_target->syncreg);
+
+ /*
+ * set ACKWIDTH
+ */
+ nsp32_write1(base, ACK_WIDTH, data->cur_target->ackwidth);
+
+ /*
+ * set SREQ hazard killer sampling rate
+ */
+ nsp32_write1(base, SREQ_SMPL_RATE, data->cur_target->sample_reg);
+
+ /*
+ * set SGT ADDR (physical address)
+ */
+ nsp32_write4(base, SGT_ADR, data->cur_lunt->sglun_paddr);
+
+ /*
+ * set TRANSFER CONTROL REG
+ */
+ transfer = 0;
+ transfer |= (TRANSFER_GO | ALL_COUNTER_CLR);
+ if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+ if (SCpnt->request_bufflen > 0) {
+ transfer |= BM_START;
+ }
+ } else if (data->trans_method & NSP32_TRANSFER_MMIO) {
+ transfer |= CB_MMIO_MODE;
+ } else if (data->trans_method & NSP32_TRANSFER_PIO) {
+ transfer |= CB_IO_MODE;
+ }
+ nsp32_write2(base, TRANSFER_CONTROL, transfer);
+
+ /*
+ * restart AutoSCSI
+ *
+ * TODO: COMMANDCONTROL_AUTO_COMMAND_PHASE is needed ?
+ */
+ command |= (CLEAR_CDB_FIFO_POINTER |
+ AUTO_COMMAND_PHASE |
+ AUTOSCSI_RESTART );
+ nsp32_write2(base, COMMAND_CONTROL, command);
+
+ nsp32_dbg(NSP32_DEBUG_RESTART, "exit");
+}
+
+
+/*
+ * cannot run automatically message in occur
+ */
+static void nsp32_msgin_occur(struct scsi_cmnd *SCpnt,
+ unsigned long irq_status,
+ unsigned short execph)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned char msg;
+ unsigned char msgtype;
+ unsigned char newlun;
+ unsigned short command = 0;
+ int msgclear = TRUE;
+ long new_sgtp;
+ int ret;
+
+ /*
+ * read first message
+ * Use SCSIDATA_W_ACK instead of SCSIDATAIN, because the procedure
+ * of Message-In have to be processed before sending back SCSI ACK.
+ */
+ msg = nsp32_read1(base, SCSI_DATA_IN);
+ data->msginbuf[(unsigned char)data->msgin_len] = msg;
+ msgtype = data->msginbuf[0];
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR,
+ "enter: msglen: 0x%x msgin: 0x%x msgtype: 0x%x",
+ data->msgin_len, msg, msgtype);
+
+ /*
+ * TODO: We need checking whether bus phase is message in?
+ */
+
+ /*
+ * assert SCSI ACK
+ */
+ nsp32_sack_assert(data);
+
+ /*
+ * processing IDENTIFY
+ */
+ if (msgtype & 0x80) {
+ if (!(irq_status & IRQSTATUS_RESELECT_OCCUER)) {
+ /* Invalid (non reselect) phase */
+ goto reject;
+ }
+
+ newlun = msgtype & 0x1f; /* TODO: SPI-3 compliant? */
+ ret = nsp32_reselection(SCpnt, newlun);
+ if (ret == TRUE) {
+ goto restart;
+ } else {
+ goto reject;
+ }
+ }
+
+ /*
+ * processing messages except for IDENTIFY
+ *
+ * TODO: Messages are all SCSI-2 terminology. SCSI-3 compliance is TODO.
+ */
+ switch (msgtype) {
+ /*
+ * 1-byte message
+ */
+ case COMMAND_COMPLETE:
+ case DISCONNECT:
+ /*
+ * These messages should not be occurred.
+ * They should be processed on AutoSCSI sequencer.
+ */
+ nsp32_msg(KERN_WARNING,
+ "unexpected message of AutoSCSI MsgIn: 0x%x", msg);
+ break;
+
+ case RESTORE_POINTERS:
+ /*
+ * AutoMsgIn03 is disabled, and HBA gets this message.
+ */
+
+ if ((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE)) {
+ unsigned int s_sacklen;
+
+ s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);
+ if ((execph & MSGIN_02_VALID) && (s_sacklen > 0)) {
+ nsp32_adjust_busfree(SCpnt, s_sacklen);
+ } else {
+ /* No need to rewrite SGT */
+ }
+ }
+ data->cur_lunt->msgin03 = FALSE;
+
+ /* Update with the new value */
+
+ /* reset SACK/SavedACK counter (or ALL clear?) */
+ nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+ /*
+ * set new sg pointer
+ */
+ new_sgtp = data->cur_lunt->sglun_paddr +
+ (data->cur_lunt->cur_entry * sizeof(nsp32_sgtable));
+ nsp32_write4(base, SGT_ADR, new_sgtp);
+
+ break;
+
+ case SAVE_POINTERS:
+ /*
+ * These messages should not be occurred.
+ * They should be processed on AutoSCSI sequencer.
+ */
+ nsp32_msg (KERN_WARNING,
+ "unexpected message of AutoSCSI MsgIn: SAVE_POINTERS");
+
+ break;
+
+ case MESSAGE_REJECT:
+ /* If previous message_out is sending SDTR, and get
+ message_reject from target, SDTR negotiation is failed */
+ if (data->cur_target->sync_flag &
+ (SDTR_INITIATOR | SDTR_TARGET)) {
+ /*
+ * Current target is negotiating SDTR, but it's
+ * failed. Fall back to async transfer mode, and set
+ * SDTR_DONE.
+ */
+ nsp32_set_async(data, data->cur_target);
+ data->cur_target->sync_flag &= ~SDTR_INITIATOR;
+ data->cur_target->sync_flag |= SDTR_DONE;
+
+ }
+ break;
+
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* queue tag is not supported currently */
+ nsp32_msg (KERN_WARNING,
+ "unsupported message: 0x%x", msgtype);
+ break;
+
+ case INITIATE_RECOVERY:
+ /* staring ECA (Extended Contingent Allegiance) state. */
+ /* This message is declined in SPI2 or later. */
+
+ goto reject;
+
+ /*
+ * 2-byte message
+ */
+ case SIMPLE_QUEUE_TAG:
+ case 0x23:
+ /*
+ * 0x23: Ignore_Wide_Residue is not declared in scsi.h.
+ * No support is needed.
+ */
+ if (data->msgin_len >= 1) {
+ goto reject;
+ }
+
+ /* current position is 1-byte of 2 byte */
+ msgclear = FALSE;
+
+ break;
+
+ /*
+ * extended message
+ */
+ case EXTENDED_MESSAGE:
+ if (data->msgin_len < 1) {
+ /*
+ * Current position does not reach 2-byte
+ * (2-byte is extended message length).
+ */
+ msgclear = FALSE;
+ break;
+ }
+
+ if ((data->msginbuf[1] + 1) > data->msgin_len) {
+ /*
+ * Current extended message has msginbuf[1] + 2
+ * (msgin_len starts counting from 0, so buf[1] + 1).
+ * If current message position is not finished,
+ * continue receiving message.
+ */
+ msgclear = FALSE;
+ break;
+ }
+
+ /*
+ * Reach here means regular length of each type of
+ * extended messages.
+ */
+ switch (data->msginbuf[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ /* TODO */
+ goto reject; /* not implemented yet */
+ break;
+
+ case EXTENDED_SDTR:
+ /*
+ * Exchange this message between initiator and target.
+ */
+ if (data->msgin_len != EXTENDED_SDTR_LEN + 1) {
+ /*
+ * received inappropriate message.
+ */
+ goto reject;
+ break;
+ }
+
+ nsp32_analyze_sdtr(SCpnt);
+
+ break;
+
+ case EXTENDED_EXTENDED_IDENTIFY:
+ /* SCSI-I only, not supported. */
+ goto reject; /* not implemented yet */
+
+ break;
+
+ case EXTENDED_WDTR:
+ goto reject; /* not implemented yet */
+
+ break;
+
+ default:
+ goto reject;
+ }
+ break;
+
+ default:
+ goto reject;
+ }
+
+ restart:
+ if (msgclear == TRUE) {
+ data->msgin_len = 0;
+
+ /*
+ * If restarting AutoSCSI, but there are some message to out
+ * (msgout_len > 0), set AutoATN, and set SCSIMSGOUT as 0
+ * (MV_VALID = 0). When commandcontrol is written with
+ * AutoSCSI restart, at the same time MsgOutOccur should be
+ * happened (however, such situation is really possible...?).
+ */
+ if (data->msgout_len > 0) {
+ nsp32_write4(base, SCSI_MSG_OUT, 0);
+ command |= AUTO_ATN;
+ }
+
+ /*
+ * restart AutoSCSI
+ * If it's failed, COMMANDCONTROL_AUTO_COMMAND_PHASE is needed.
+ */
+ command |= (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);
+
+ /*
+ * If current msgin03 is TRUE, then flag on.
+ */
+ if (data->cur_lunt->msgin03 == TRUE) {
+ command |= AUTO_MSGIN_03;
+ }
+ data->cur_lunt->msgin03 = FALSE;
+ } else {
+ data->msgin_len++;
+ }
+
+ /*
+ * restart AutoSCSI
+ */
+ nsp32_restart_autoscsi(SCpnt, command);
+
+ /*
+ * wait SCSI REQ negate for REQ-ACK handshake
+ */
+ nsp32_wait_req(data, NEGATE);
+
+ /*
+ * negate SCSI ACK
+ */
+ nsp32_sack_negate(data);
+
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");
+
+ return;
+
+ reject:
+ nsp32_msg(KERN_WARNING,
+ "invalid or unsupported MessageIn, rejected. "
+ "current msg: 0x%x (len: 0x%x), processing msg: 0x%x",
+ msg, data->msgin_len, msgtype);
+ nsp32_build_reject(SCpnt);
+ data->msgin_len = 0;
+
+ goto restart;
+}
+
+/*
+ *
+ */
+static void nsp32_analyze_sdtr(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ nsp32_target *target = data->cur_target;
+ nsp32_sync_table *synct;
+ unsigned char get_period = data->msginbuf[3];
+ unsigned char get_offset = data->msginbuf[4];
+ int entry;
+ int syncnum;
+
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "enter");
+
+ synct = data->synct;
+ syncnum = data->syncnum;
+
+ /*
+ * If this inititor sent the SDTR message, then target responds SDTR,
+ * initiator SYNCREG, ACKWIDTH from SDTR parameter.
+ * Messages are not appropriate, then send back reject message.
+ * If initiator did not send the SDTR, but target sends SDTR,
+ * initiator calculator the appropriate parameter and send back SDTR.
+ */
+ if (target->sync_flag & SDTR_INITIATOR) {
+ /*
+ * Initiator sent SDTR, the target responds and
+ * send back negotiation SDTR.
+ */
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target responds SDTR");
+
+ target->sync_flag &= ~SDTR_INITIATOR;
+ target->sync_flag |= SDTR_DONE;
+
+ /*
+ * offset:
+ */
+ if (get_offset > SYNC_OFFSET) {
+ /*
+ * Negotiation is failed, the target send back
+ * unexpected offset value.
+ */
+ goto reject;
+ }
+
+ if (get_offset == ASYNC_OFFSET) {
+ /*
+ * Negotiation is succeeded, the target want
+ * to fall back into asynchronous transfer mode.
+ */
+ goto async;
+ }
+
+ /*
+ * period:
+ * Check whether sync period is too short. If too short,
+ * fall back to async mode. If it's ok, then investigate
+ * the received sync period. If sync period is acceptable
+ * between sync table start_period and end_period, then
+ * set this I_T nexus as sent offset and period.
+ * If it's not acceptable, send back reject and fall back
+ * to async mode.
+ */
+ if (get_period < data->synct[0].period_num) {
+ /*
+ * Negotiation is failed, the target send back
+ * unexpected period value.
+ */
+ goto reject;
+ }
+
+ entry = nsp32_search_period_entry(data, target, get_period);
+
+ if (entry < 0) {
+ /*
+ * Target want to use long period which is not
+ * acceptable NinjaSCSI-32Bi/UDE.
+ */
+ goto reject;
+ }
+
+ /*
+ * Set new sync table and offset in this I_T nexus.
+ */
+ nsp32_set_sync_entry(data, target, entry, get_offset);
+ } else {
+ /* Target send SDTR to initiator. */
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target send SDTR");
+
+ target->sync_flag |= SDTR_INITIATOR;
+
+ /* offset: */
+ if (get_offset > SYNC_OFFSET) {
+ /* send back as SYNC_OFFSET */
+ get_offset = SYNC_OFFSET;
+ }
+
+ /* period: */
+ if (get_period < data->synct[0].period_num) {
+ get_period = data->synct[0].period_num;
+ }
+
+ entry = nsp32_search_period_entry(data, target, get_period);
+
+ if (get_offset == ASYNC_OFFSET || entry < 0) {
+ nsp32_set_async(data, target);
+ nsp32_build_sdtr(SCpnt, 0, ASYNC_OFFSET);
+ } else {
+ nsp32_set_sync_entry(data, target, entry, get_offset);
+ nsp32_build_sdtr(SCpnt, get_period, get_offset);
+ }
+ }
+
+ target->period = get_period;
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");
+ return;
+
+ reject:
+ /*
+ * If the current message is unacceptable, send back to the target
+ * with reject message.
+ */
+ nsp32_build_reject(SCpnt);
+
+ async:
+ nsp32_set_async(data, target); /* set as ASYNC transfer mode */
+
+ target->period = 0;
+ nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit: set async");
+ return;
+}
+
+
+/*
+ * Search config entry number matched in sync_table from given
+ * target and speed period value. If failed to search, return negative value.
+ */
+static int nsp32_search_period_entry(nsp32_hw_data *data,
+ nsp32_target *target,
+ unsigned char period)
+{
+ int i;
+
+ if (target->limit_entry >= data->syncnum) {
+ nsp32_msg(KERN_ERR, "limit_entry exceeds syncnum!");
+ target->limit_entry = 0;
+ }
+
+ for (i = target->limit_entry; i < data->syncnum; i++) {
+ if (period >= data->synct[i].start_period &&
+ period <= data->synct[i].end_period) {
+ break;
+ }
+ }
+
+ /*
+ * Check given period value is over the sync_table value.
+ * If so, return max value.
+ */
+ if (i == data->syncnum) {
+ i = -1;
+ }
+
+ return i;
+}
+
+
+/*
+ * target <-> initiator use ASYNC transfer
+ */
+static void nsp32_set_async(nsp32_hw_data *data, nsp32_target *target)
+{
+ unsigned char period = data->synct[target->limit_entry].period_num;
+
+ target->offset = ASYNC_OFFSET;
+ target->period = 0;
+ target->syncreg = TO_SYNCREG(period, ASYNC_OFFSET);
+ target->ackwidth = 0;
+ target->sample_reg = 0;
+
+ nsp32_dbg(NSP32_DEBUG_SYNC, "set async");
+}
+
+
+/*
+ * target <-> initiator use maximum SYNC transfer
+ */
+static void nsp32_set_max_sync(nsp32_hw_data *data,
+ nsp32_target *target,
+ unsigned char *period,
+ unsigned char *offset)
+{
+ unsigned char period_num, ackwidth;
+
+ period_num = data->synct[target->limit_entry].period_num;
+ *period = data->synct[target->limit_entry].start_period;
+ ackwidth = data->synct[target->limit_entry].ackwidth;
+ *offset = SYNC_OFFSET;
+
+ target->syncreg = TO_SYNCREG(period_num, *offset);
+ target->ackwidth = ackwidth;
+ target->offset = *offset;
+ target->sample_reg = 0; /* disable SREQ sampling */
+}
+
+
+/*
+ * target <-> initiator use entry number speed
+ */
+static void nsp32_set_sync_entry(nsp32_hw_data *data,
+ nsp32_target *target,
+ int entry,
+ unsigned char offset)
+{
+ unsigned char period, ackwidth, sample_rate;
+
+ period = data->synct[entry].period_num;
+ ackwidth = data->synct[entry].ackwidth;
+ offset = offset;
+ sample_rate = data->synct[entry].sample_rate;
+
+ target->syncreg = TO_SYNCREG(period, offset);
+ target->ackwidth = ackwidth;
+ target->offset = offset;
+ target->sample_reg = sample_rate | SAMPLING_ENABLE;
+
+ nsp32_dbg(NSP32_DEBUG_SYNC, "set sync");
+}
+
+
+/*
+ * It waits until SCSI REQ becomes assertion or negation state.
+ *
+ * Note: If nsp32_msgin_occur is called, we asserts SCSI ACK. Then
+ * connected target responds SCSI REQ negation. We have to wait
+ * SCSI REQ becomes negation in order to negate SCSI ACK signal for
+ * REQ-ACK handshake.
+ */
+static void nsp32_wait_req(nsp32_hw_data *data, int state)
+{
+ unsigned int base = data->BaseAddress;
+ int wait_time = 0;
+ unsigned char bus, req_bit;
+
+ if (!((state == ASSERT) || (state == NEGATE))) {
+ nsp32_msg(KERN_ERR, "unknown state designation");
+ }
+ /* REQ is BIT(5) */
+ req_bit = (state == ASSERT ? BUSMON_REQ : 0);
+
+ do {
+ bus = nsp32_read1(base, SCSI_BUS_MONITOR);
+ if ((bus & BUSMON_REQ) == req_bit) {
+ nsp32_dbg(NSP32_DEBUG_WAIT,
+ "wait_time: %d", wait_time);
+ return;
+ }
+ udelay(1);
+ wait_time++;
+ } while (wait_time < REQSACK_TIMEOUT_TIME);
+
+ nsp32_msg(KERN_WARNING, "wait REQ timeout, req_bit: 0x%x", req_bit);
+}
+
+/*
+ * It waits until SCSI SACK becomes assertion or negation state.
+ */
+static void nsp32_wait_sack(nsp32_hw_data *data, int state)
+{
+ unsigned int base = data->BaseAddress;
+ int wait_time = 0;
+ unsigned char bus, ack_bit;
+
+ if (!((state == ASSERT) || (state == NEGATE))) {
+ nsp32_msg(KERN_ERR, "unknown state designation");
+ }
+ /* ACK is BIT(4) */
+ ack_bit = (state == ASSERT ? BUSMON_ACK : 0);
+
+ do {
+ bus = nsp32_read1(base, SCSI_BUS_MONITOR);
+ if ((bus & BUSMON_ACK) == ack_bit) {
+ nsp32_dbg(NSP32_DEBUG_WAIT,
+ "wait_time: %d", wait_time);
+ return;
+ }
+ udelay(1);
+ wait_time++;
+ } while (wait_time < REQSACK_TIMEOUT_TIME);
+
+ nsp32_msg(KERN_WARNING, "wait SACK timeout, ack_bit: 0x%x", ack_bit);
+}
+
+/*
+ * assert SCSI ACK
+ *
+ * Note: SCSI ACK assertion needs with ACKENB=1, AUTODIRECTION=1.
+ */
+static void nsp32_sack_assert(nsp32_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+ unsigned char busctrl;
+
+ busctrl = nsp32_read1(base, SCSI_BUS_CONTROL);
+ busctrl |= (BUSCTL_ACK | AUTODIRECTION | ACKENB);
+ nsp32_write1(base, SCSI_BUS_CONTROL, busctrl);
+}
+
+/*
+ * negate SCSI ACK
+ */
+static void nsp32_sack_negate(nsp32_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+ unsigned char busctrl;
+
+ busctrl = nsp32_read1(base, SCSI_BUS_CONTROL);
+ busctrl &= ~BUSCTL_ACK;
+ nsp32_write1(base, SCSI_BUS_CONTROL, busctrl);
+}
+
+
+
+/*
+ * Note: n_io_port is defined as 0x7f because I/O register port is
+ * assigned as:
+ * 0x800-0x8ff: memory mapped I/O port
+ * 0x900-0xbff: (map same 0x800-0x8ff I/O port image repeatedly)
+ * 0xc00-0xfff: CardBus status registers
+ */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+#define DETECT_OK 0
+#define DETECT_NG 1
+#define PCIDEV pdev
+static int nsp32_detect(struct pci_dev *pdev)
+#else
+#define DETECT_OK 1
+#define DETECT_NG 0
+#define PCIDEV (data->Pci)
+static int nsp32_detect(Scsi_Host_Template *sht)
+#endif
+{
+ struct Scsi_Host *host; /* registered host structure */
+ struct resource *res;
+ nsp32_hw_data *data;
+ int ret;
+ int i, j;
+
+ nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
+
+ /*
+ * register this HBA as SCSI device
+ */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ host = scsi_host_alloc(&nsp32_template, sizeof(nsp32_hw_data));
+#else
+ host = scsi_register(sht, sizeof(nsp32_hw_data));
+#endif
+ if (host == NULL) {
+ nsp32_msg (KERN_ERR, "failed to scsi register");
+ goto err;
+ }
+
+ /*
+ * set nsp32_hw_data
+ */
+ data = (nsp32_hw_data *)host->hostdata;
+
+ memcpy(data, &nsp32_data_base, sizeof(nsp32_hw_data));
+
+ host->irq = data->IrqNumber;
+ host->io_port = data->BaseAddress;
+ host->unique_id = data->BaseAddress;
+ host->n_io_port = data->NumAddress;
+ host->base = (unsigned long)data->MmioAddress;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,63))
+ scsi_set_device(host, &PCIDEV->dev);
+#else
+ scsi_set_pci_device(host, PCIDEV);
+#endif
+
+ data->Host = host;
+ spin_lock_init(&(data->Lock));
+
+ data->cur_lunt = NULL;
+ data->cur_target = NULL;
+
+ /*
+ * Bus master transfer mode is supported currently.
+ */
+ data->trans_method = NSP32_TRANSFER_BUSMASTER;
+
+ /*
+ * Set clock div, CLOCK_4 (HBA has own external clock, and
+ * dividing * 100ns/4).
+ * Currently CLOCK_4 has only tested, not for CLOCK_2/PCICLK yet.
+ */
+ data->clock = CLOCK_4;
+
+ /*
+ * Select appropriate nsp32_sync_table and set I_CLOCKDIV.
+ */
+ switch (data->clock) {
+ case CLOCK_4:
+ /* If data->clock is CLOCK_4, then select 40M sync table. */
+ data->synct = nsp32_sync_table_40M;
+ data->syncnum = ARRAY_SIZE(nsp32_sync_table_40M);
+ break;
+ case CLOCK_2:
+ /* If data->clock is CLOCK_2, then select 20M sync table. */
+ data->synct = nsp32_sync_table_20M;
+ data->syncnum = ARRAY_SIZE(nsp32_sync_table_20M);
+ break;
+ case PCICLK:
+ /* If data->clock is PCICLK, then select pci sync table. */
+ data->synct = nsp32_sync_table_pci;
+ data->syncnum = ARRAY_SIZE(nsp32_sync_table_pci);
+ break;
+ default:
+ nsp32_msg(KERN_WARNING,
+ "Invalid clock div is selected, set CLOCK_4.");
+ /* Use default value CLOCK_4 */
+ data->clock = CLOCK_4;
+ data->synct = nsp32_sync_table_40M;
+ data->syncnum = ARRAY_SIZE(nsp32_sync_table_40M);
+ }
+
+ /*
+ * setup nsp32_lunt
+ */
+
+ /*
+ * setup DMA
+ */
+ if (pci_set_dma_mask(PCIDEV, 0xffffffffUL) != 0) {
+ nsp32_msg (KERN_ERR, "failed to set PCI DMA mask");
+ goto scsi_unregister;
+ }
+
+ /*
+ * allocate autoparam DMA resource.
+ */
+ data->autoparam = pci_alloc_consistent(PCIDEV, sizeof(nsp32_autoparam), &(data->auto_paddr));
+ if (data->autoparam == NULL) {
+ nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
+ goto scsi_unregister;
+ }
+
+ /*
+ * allocate scatter-gather DMA resource.
+ */
+ data->sg_list = pci_alloc_consistent(PCIDEV, NSP32_SG_TABLE_SIZE,
+ &(data->sg_paddr));
+ if (data->sg_list == NULL) {
+ nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
+ goto free_autoparam;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->lunt); i++) {
+ for (j = 0; j < ARRAY_SIZE(data->lunt[0]); j++) {
+ int offset = i * ARRAY_SIZE(data->lunt[0]) + j;
+ nsp32_lunt tmp = {
+ .SCpnt = NULL,
+ .save_datp = 0,
+ .msgin03 = FALSE,
+ .sg_num = 0,
+ .cur_entry = 0,
+ .sglun = &(data->sg_list[offset]),
+ .sglun_paddr = data->sg_paddr + (offset * sizeof(nsp32_sglun)),
+ };
+
+ data->lunt[i][j] = tmp;
+ }
+ }
+
+ /*
+ * setup target
+ */
+ for (i = 0; i < ARRAY_SIZE(data->target); i++) {
+ nsp32_target *target = &(data->target[i]);
+
+ target->limit_entry = 0;
+ target->sync_flag = 0;
+ nsp32_set_async(data, target);
+ }
+
+ /*
+ * EEPROM check
+ */
+ ret = nsp32_getprom_param(data);
+ if (ret == FALSE) {
+ data->resettime = 3; /* default 3 */
+ }
+
+ /*
+ * setup HBA
+ */
+ nsp32hw_init(data);
+
+ snprintf(data->info_str, sizeof(data->info_str),
+ "NinjaSCSI-32Bi/UDE: irq %d, io 0x%lx+0x%x",
+ host->irq, host->io_port, host->n_io_port);
+
+ /*
+ * SCSI bus reset
+ *
+ * Note: It's important to reset SCSI bus in initialization phase.
+ * NinjaSCSI-32Bi/UDE HBA EEPROM seems to exchange SDTR when
+ * system is coming up, so SCSI devices connected to HBA is set as
+ * un-asynchronous mode. It brings the merit that this HBA is
+ * ready to start synchronous transfer without any preparation,
+ * but we are difficult to control transfer speed. In addition,
+ * it prevents device transfer speed from effecting EEPROM start-up
+ * SDTR. NinjaSCSI-32Bi/UDE has the feature if EEPROM is set as
+ * Auto Mode, then FAST-10M is selected when SCSI devices are
+ * connected same or more than 4 devices. It should be avoided
+ * depending on this specification. Thus, resetting the SCSI bus
+ * restores all connected SCSI devices to asynchronous mode, then
+ * this driver set SDTR safely later, and we can control all SCSI
+ * device transfer mode.
+ */
+ nsp32_do_bus_reset(data);
+
+ ret = request_irq(host->irq, do_nsp32_isr,
+ SA_SHIRQ | SA_SAMPLE_RANDOM, "nsp32", data);
+ if (ret < 0) {
+ nsp32_msg(KERN_ERR, "Unable to allocate IRQ for NinjaSCSI32 "
+ "SCSI PCI controller. Interrupt: %d", host->irq);
+ goto free_sg_list;
+ }
+
+ /*
+ * PCI IO register
+ */
+ res = request_region(host->io_port, host->n_io_port, "nsp32");
+ if (res == NULL) {
+ nsp32_msg(KERN_ERR,
+ "I/O region 0x%lx+0x%lx is already used",
+ data->BaseAddress, data->NumAddress);
+ goto free_irq;
+ }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ scsi_add_host (host, &PCIDEV->dev);
+ scsi_scan_host(host);
+#endif
+ pci_set_drvdata(PCIDEV, host);
+ return DETECT_OK;
+
+ free_irq:
+ free_irq(host->irq, data);
+
+ free_sg_list:
+ pci_free_consistent(PCIDEV, NSP32_SG_TABLE_SIZE,
+ data->sg_list, data->sg_paddr);
+
+ free_autoparam:
+ pci_free_consistent(PCIDEV, sizeof(nsp32_autoparam),
+ data->autoparam, data->auto_paddr);
+
+ scsi_unregister:
+ scsi_host_put(host);
+
+ err:
+ return DETECT_NG;
+}
+#undef DETECT_OK
+#undef DETECT_NG
+#undef PCIDEV
+
+static int nsp32_release(struct Scsi_Host *host)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
+
+ if (data->autoparam) {
+ pci_free_consistent(data->Pci, sizeof(nsp32_autoparam),
+ data->autoparam, data->auto_paddr);
+ }
+
+ if (data->sg_list) {
+ pci_free_consistent(data->Pci, NSP32_SG_TABLE_SIZE,
+ data->sg_list, data->sg_paddr);
+ }
+
+ if (host->irq) {
+ free_irq(host->irq, data);
+ }
+
+ if (host->io_port && host->n_io_port) {
+ release_region(host->io_port, host->n_io_port);
+ }
+
+ if (data->MmioAddress) {
+ iounmap(data->MmioAddress);
+ }
+
+ return 0;
+}
+
+static const char *nsp32_info(struct Scsi_Host *shpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)shpnt->hostdata;
+
+ return data->info_str;
+}
+
+
+/****************************************************************************
+ * error handler
+ */
+static int nsp32_eh_abort(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+
+ nsp32_msg(KERN_WARNING, "abort");
+
+ if (data->cur_lunt->SCpnt == NULL) {
+ nsp32_dbg(NSP32_DEBUG_BUSRESET, "abort failed");
+ return FAILED;
+ }
+
+ if (data->cur_target->sync_flag & (SDTR_INITIATOR | SDTR_TARGET)) {
+ /* reset SDTR negotiation */
+ data->cur_target->sync_flag = 0;
+ nsp32_set_async(data, data->cur_target);
+ }
+
+ nsp32_write2(base, TRANSFER_CONTROL, 0);
+ nsp32_write2(base, BM_CNT, 0);
+
+ SCpnt->result = DID_ABORT << 16;
+ nsp32_scsi_done(SCpnt);
+
+ nsp32_dbg(NSP32_DEBUG_BUSRESET, "abort success");
+ return SUCCESS;
+}
+
+static int nsp32_eh_bus_reset(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+
+ nsp32_msg(KERN_INFO, "Bus Reset");
+ nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
+
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+ nsp32_do_bus_reset(data);
+ nsp32_write2(base, IRQ_CONTROL, 0);
+
+ return SUCCESS; /* SCSI bus reset is succeeded at any time. */
+}
+
+static void nsp32_do_bus_reset(nsp32_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+ unsigned short intrdat;
+ int i;
+
+ nsp32_dbg(NSP32_DEBUG_BUSRESET, "in");
+
+ /*
+ * stop all transfer
+ * clear TRANSFERCONTROL_BM_START
+ * clear counter
+ */
+ nsp32_write2(base, TRANSFER_CONTROL, 0);
+ nsp32_write4(base, BM_CNT, 0);
+ nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+ /*
+ * fall back to asynchronous transfer mode
+ * initialize SDTR negotiation flag
+ */
+ for (i = 0; i < ARRAY_SIZE(data->target); i++) {
+ nsp32_target *target = &data->target[i];
+
+ target->sync_flag = 0;
+ nsp32_set_async(data, target);
+ }
+
+ /*
+ * reset SCSI bus
+ */
+ nsp32_write1(base, SCSI_BUS_CONTROL, BUSCTL_RST);
+ udelay(RESET_HOLD_TIME);
+ nsp32_write1(base, SCSI_BUS_CONTROL, 0);
+ for(i = 0; i < 5; i++) {
+ intrdat = nsp32_read2(base, IRQ_STATUS); /* dummy read */
+ nsp32_dbg(NSP32_DEBUG_BUSRESET, "irq:1: 0x%x", intrdat);
+ }
+
+ data->CurrentSC = NULL;
+}
+
+static int nsp32_eh_host_reset(struct scsi_cmnd *SCpnt)
+{
+ struct Scsi_Host *host = SCpnt->device->host;
+ unsigned int base = SCpnt->device->host->io_port;
+ nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
+
+ nsp32_msg(KERN_INFO, "Host Reset");
+ nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
+
+ nsp32hw_init(data);
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+ nsp32_do_bus_reset(data);
+ nsp32_write2(base, IRQ_CONTROL, 0);
+
+ return SUCCESS; /* Host reset is succeeded at any time. */
+}
+
+
+/**************************************************************************
+ * EEPROM handler
+ */
+
+/*
+ * getting EEPROM parameter
+ */
+static int nsp32_getprom_param(nsp32_hw_data *data)
+{
+ int vendor = data->pci_devid->vendor;
+ int device = data->pci_devid->device;
+ int ret, val, i;
+
+ /*
+ * EEPROM checking.
+ */
+ ret = nsp32_prom_read(data, 0x7e);
+ if (ret != 0x55) {
+ nsp32_msg(KERN_INFO, "No EEPROM detected: 0x%x", ret);
+ return FALSE;
+ }
+ ret = nsp32_prom_read(data, 0x7f);
+ if (ret != 0xaa) {
+ nsp32_msg(KERN_INFO, "Invalid number: 0x%x", ret);
+ return FALSE;
+ }
+
+ /*
+ * check EEPROM type
+ */
+ if (vendor == PCI_VENDOR_ID_WORKBIT &&
+ device == PCI_DEVICE_ID_WORKBIT_STANDARD) {
+ ret = nsp32_getprom_c16(data);
+ } else if (vendor == PCI_VENDOR_ID_WORKBIT &&
+ device == PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC) {
+ ret = nsp32_getprom_at24(data);
+ } else if (vendor == PCI_VENDOR_ID_WORKBIT &&
+ device == PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO ) {
+ ret = nsp32_getprom_at24(data);
+ } else {
+ nsp32_msg(KERN_WARNING, "Unknown EEPROM");
+ ret = FALSE;
+ }
+
+ /* for debug : SPROM data full checking */
+ for (i = 0; i <= 0x1f; i++) {
+ val = nsp32_prom_read(data, i);
+ nsp32_dbg(NSP32_DEBUG_EEPROM,
+ "rom address 0x%x : 0x%x", i, val);
+ }
+
+ return ret;
+}
+
+
+/*
+ * AT24C01A (Logitec: LHA-600S), AT24C02 (Melco Buffalo: IFC-USLP) data map:
+ *
+ * ROMADDR
+ * 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
+ * Value 0x0: ASYNC, 0x0c: Ultra-20M, 0x19: Fast-10M
+ * 0x07 : HBA Synchronous Transfer Period
+ * Value 0: AutoSync, 1: Manual Setting
+ * 0x08 - 0x0f : Not Used? (0x0)
+ * 0x10 : Bus Termination
+ * Value 0: Auto[ON], 1: ON, 2: OFF
+ * 0x11 : Not Used? (0)
+ * 0x12 : Bus Reset Delay Time (0x03)
+ * 0x13 : Bootable CD Support
+ * Value 0: Disable, 1: Enable
+ * 0x14 : Device Scan
+ * Bit 7 6 5 4 3 2 1 0
+ * | <----------------->
+ * | SCSI ID: Value 0: Skip, 1: YES
+ * |-> Value 0: ALL scan, Value 1: Manual
+ * 0x15 - 0x1b : Not Used? (0)
+ * 0x1c : Constant? (0x01) (clock div?)
+ * 0x1d - 0x7c : Not Used (0xff)
+ * 0x7d : Not Used? (0xff)
+ * 0x7e : Constant (0x55), Validity signature
+ * 0x7f : Constant (0xaa), Validity signature
+ */
+static int nsp32_getprom_at24(nsp32_hw_data *data)
+{
+ int ret, i;
+ int auto_sync;
+ nsp32_target *target;
+ int entry;
+
+ /*
+ * Reset time which is designated by EEPROM.
+ *
+ * TODO: Not used yet.
+ */
+ data->resettime = nsp32_prom_read(data, 0x12);
+
+ /*
+ * HBA Synchronous Transfer Period
+ *
+ * Note: auto_sync = 0: auto, 1: manual. Ninja SCSI HBA spec says
+ * that if auto_sync is 0 (auto), and connected SCSI devices are
+ * same or lower than 3, then transfer speed is set as ULTRA-20M.
+ * On the contrary if connected SCSI devices are same or higher
+ * than 4, then transfer speed is set as FAST-10M.
+ *
+ * I break this rule. The number of connected SCSI devices are
+ * only ignored. If auto_sync is 0 (auto), then transfer speed is
+ * forced as ULTRA-20M.
+ */
+ ret = nsp32_prom_read(data, 0x07);
+ switch (ret) {
+ case 0:
+ auto_sync = TRUE;
+ break;
+ case 1:
+ auto_sync = FALSE;
+ break;
+ default:
+ nsp32_msg(KERN_WARNING,
+ "Unsupported Auto Sync mode. Fall back to manual mode.");
+ auto_sync = TRUE;
+ }
+
+ if (trans_mode == ULTRA20M_MODE) {
+ auto_sync = TRUE;
+ }
+
+ /*
+ * each device Synchronous Transfer Period
+ */
+ for (i = 0; i < NSP32_HOST_SCSIID; i++) {
+ target = &data->target[i];
+ if (auto_sync == TRUE) {
+ target->limit_entry = 0; /* set as ULTRA20M */
+ } else {
+ ret = nsp32_prom_read(data, i);
+ entry = nsp32_search_period_entry(data, target, ret);
+ if (entry < 0) {
+ /* search failed... set maximum speed */
+ entry = 0;
+ }
+ target->limit_entry = entry;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * C16 110 (I-O Data: SC-NBD) data map:
+ *
+ * ROMADDR
+ * 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
+ * Value 0x0: 20MB/S, 0x1: 10MB/S, 0x2: 5MB/S, 0x3: ASYNC
+ * 0x07 : 0 (HBA Synchronous Transfer Period: Auto Sync)
+ * 0x08 - 0x0f : Not Used? (0x0)
+ * 0x10 : Transfer Mode
+ * Value 0: PIO, 1: Busmater
+ * 0x11 : Bus Reset Delay Time (0x00-0x20)
+ * 0x12 : Bus Termination
+ * Value 0: Disable, 1: Enable
+ * 0x13 - 0x19 : Disconnection
+ * Value 0: Disable, 1: Enable
+ * 0x1a - 0x7c : Not Used? (0)
+ * 0x7d : Not Used? (0xf8)
+ * 0x7e : Constant (0x55), Validity signature
+ * 0x7f : Constant (0xaa), Validity signature
+ */
+static int nsp32_getprom_c16(nsp32_hw_data *data)
+{
+ int ret, i;
+ nsp32_target *target;
+ int entry, val;
+
+ /*
+ * Reset time which is designated by EEPROM.
+ *
+ * TODO: Not used yet.
+ */
+ data->resettime = nsp32_prom_read(data, 0x11);
+
+ /*
+ * each device Synchronous Transfer Period
+ */
+ for (i = 0; i < NSP32_HOST_SCSIID; i++) {
+ target = &data->target[i];
+ ret = nsp32_prom_read(data, i);
+ switch (ret) {
+ case 0: /* 20MB/s */
+ val = 0x0c;
+ break;
+ case 1: /* 10MB/s */
+ val = 0x19;
+ break;
+ case 2: /* 5MB/s */
+ val = 0x32;
+ break;
+ case 3: /* ASYNC */
+ val = 0x00;
+ break;
+ default: /* default 20MB/s */
+ val = 0x0c;
+ break;
+ }
+ entry = nsp32_search_period_entry(data, target, val);
+ if (entry < 0 || trans_mode == ULTRA20M_MODE) {
+ /* search failed... set maximum speed */
+ entry = 0;
+ }
+ target->limit_entry = entry;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Atmel AT24C01A (drived in 5V) serial EEPROM routines
+ */
+static int nsp32_prom_read(nsp32_hw_data *data, int romaddr)
+{
+ int i, val;
+
+ /* start condition */
+ nsp32_prom_start(data);
+
+ /* device address */
+ nsp32_prom_write_bit(data, 1); /* 1 */
+ nsp32_prom_write_bit(data, 0); /* 0 */
+ nsp32_prom_write_bit(data, 1); /* 1 */
+ nsp32_prom_write_bit(data, 0); /* 0 */
+ nsp32_prom_write_bit(data, 0); /* A2: 0 (GND) */
+ nsp32_prom_write_bit(data, 0); /* A1: 0 (GND) */
+ nsp32_prom_write_bit(data, 0); /* A0: 0 (GND) */
+
+ /* R/W: W for dummy write */
+ nsp32_prom_write_bit(data, 0);
+
+ /* ack */
+ nsp32_prom_write_bit(data, 0);
+
+ /* word address */
+ for (i = 7; i >= 0; i--) {
+ nsp32_prom_write_bit(data, ((romaddr >> i) & 1));
+ }
+
+ /* ack */
+ nsp32_prom_write_bit(data, 0);
+
+ /* start condition */
+ nsp32_prom_start(data);
+
+ /* device address */
+ nsp32_prom_write_bit(data, 1); /* 1 */
+ nsp32_prom_write_bit(data, 0); /* 0 */
+ nsp32_prom_write_bit(data, 1); /* 1 */
+ nsp32_prom_write_bit(data, 0); /* 0 */
+ nsp32_prom_write_bit(data, 0); /* A2: 0 (GND) */
+ nsp32_prom_write_bit(data, 0); /* A1: 0 (GND) */
+ nsp32_prom_write_bit(data, 0); /* A0: 0 (GND) */
+
+ /* R/W: R */
+ nsp32_prom_write_bit(data, 1);
+
+ /* ack */
+ nsp32_prom_write_bit(data, 0);
+
+ /* data... */
+ val = 0;
+ for (i = 7; i >= 0; i--) {
+ val += (nsp32_prom_read_bit(data) << i);
+ }
+
+ /* no ack */
+ nsp32_prom_write_bit(data, 1);
+
+ /* stop condition */
+ nsp32_prom_stop(data);
+
+ return val;
+}
+
+static void nsp32_prom_set(nsp32_hw_data *data, int bit, int val)
+{
+ int base = data->BaseAddress;
+ int tmp;
+
+ tmp = nsp32_index_read1(base, SERIAL_ROM_CTL);
+
+ if (val == 0) {
+ tmp &= ~bit;
+ } else {
+ tmp |= bit;
+ }
+
+ nsp32_index_write1(base, SERIAL_ROM_CTL, tmp);
+
+ udelay(10);
+}
+
+static int nsp32_prom_get(nsp32_hw_data *data, int bit)
+{
+ int base = data->BaseAddress;
+ int tmp, ret;
+
+ if (bit != SDA) {
+ nsp32_msg(KERN_ERR, "return value is not appropriate");
+ return 0;
+ }
+
+
+ tmp = nsp32_index_read1(base, SERIAL_ROM_CTL) & bit;
+
+ if (tmp == 0) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ udelay(10);
+
+ return ret;
+}
+
+static void nsp32_prom_start (nsp32_hw_data *data)
+{
+ /* start condition */
+ nsp32_prom_set(data, SCL, 1);
+ nsp32_prom_set(data, SDA, 1);
+ nsp32_prom_set(data, ENA, 1); /* output mode */
+ nsp32_prom_set(data, SDA, 0); /* keeping SCL=1 and transiting
+ * SDA 1->0 is start condition */
+ nsp32_prom_set(data, SCL, 0);
+}
+
+static void nsp32_prom_stop (nsp32_hw_data *data)
+{
+ /* stop condition */
+ nsp32_prom_set(data, SCL, 1);
+ nsp32_prom_set(data, SDA, 0);
+ nsp32_prom_set(data, ENA, 1); /* output mode */
+ nsp32_prom_set(data, SDA, 1);
+ nsp32_prom_set(data, SCL, 0);
+}
+
+static void nsp32_prom_write_bit(nsp32_hw_data *data, int val)
+{
+ /* write */
+ nsp32_prom_set(data, SDA, val);
+ nsp32_prom_set(data, SCL, 1 );
+ nsp32_prom_set(data, SCL, 0 );
+}
+
+static int nsp32_prom_read_bit(nsp32_hw_data *data)
+{
+ int val;
+
+ /* read */
+ nsp32_prom_set(data, ENA, 0); /* input mode */
+ nsp32_prom_set(data, SCL, 1);
+
+ val = nsp32_prom_get(data, SDA);
+
+ nsp32_prom_set(data, SCL, 0);
+ nsp32_prom_set(data, ENA, 1); /* output mode */
+
+ return val;
+}
+
+
+/**************************************************************************
+ * Power Management
+ */
+#ifdef CONFIG_PM
+
+/* Device suspended */
+static int nsp32_suspend(struct pci_dev *pdev, u32 state)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+
+ nsp32_msg(KERN_INFO, "pci-suspend: pdev=0x%p, state=%ld, slot=%s, host=0x%p", pdev, state, pci_name(pdev), host);
+
+ pci_save_state (pdev);
+ pci_disable_device (pdev);
+ pci_set_power_state(pdev, state);
+
+ return 0;
+}
+
+/* Device woken up */
+static int nsp32_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
+ unsigned short reg;
+
+ nsp32_msg(KERN_INFO, "pci-resume: pdev=0x%p, slot=%s, host=0x%p", pdev, pci_name(pdev), host);
+
+ pci_set_power_state(pdev, 0);
+ pci_enable_wake (pdev, 0, 0);
+ pci_restore_state (pdev);
+
+ reg = nsp32_read2(data->BaseAddress, INDEX_REG);
+
+ nsp32_msg(KERN_INFO, "io=0x%x reg=0x%x", data->BaseAddress, reg);
+
+ if (reg == 0xffff) {
+ nsp32_msg(KERN_INFO, "missing device. abort resume.");
+ return 0;
+ }
+
+ nsp32hw_init (data);
+ nsp32_do_bus_reset(data);
+
+ nsp32_msg(KERN_INFO, "resume success");
+
+ return 0;
+}
+
+/* Enable wake event */
+static int nsp32_enable_wake(struct pci_dev *pdev, u32 state, int enable)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+
+ nsp32_msg(KERN_INFO, "pci-enable_wake: stub, pdev=0x%p, enable=%d, slot=%s, host=0x%p", pdev, enable, pci_name(pdev), host);
+
+ return 0;
+}
+#endif
+
+/************************************************************************
+ * PCI/Cardbus probe/remove routine
+ */
+static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int ret;
+ nsp32_hw_data *data = &nsp32_data_base;
+
+ nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ nsp32_msg(KERN_ERR, "failed to enable pci device");
+ return ret;
+ }
+
+ data->Pci = pdev;
+ data->pci_devid = id;
+ data->IrqNumber = pdev->irq;
+ data->BaseAddress = pci_resource_start(pdev, 0);
+ data->NumAddress = pci_resource_len (pdev, 0);
+ data->MmioAddress = ioremap_nocache(pci_resource_start(pdev, 1),
+ pci_resource_len (pdev, 1));
+ data->MmioLength = pci_resource_len (pdev, 1);
+
+ pci_set_master(pdev);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ ret = nsp32_detect(pdev);
+#else
+ ret = scsi_register_host(&nsp32_template);
+#endif
+
+ nsp32_msg(KERN_INFO, "irq: %i mmio: %p+0x%lx slot: %s model: %s",
+ pdev->irq,
+ data->MmioAddress, data->MmioLength,
+ pci_name(pdev),
+ nsp32_model[id->driver_data]);
+
+ nsp32_dbg(NSP32_DEBUG_REGISTER, "exit %d", ret);
+
+ return ret;
+}
+
+static void __devexit nsp32_remove(struct pci_dev *pdev)
+{
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+#endif
+
+ nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ scsi_remove_host(host);
+
+ nsp32_release(host);
+
+ scsi_host_put(host);
+#else
+ scsi_unregister_host(&nsp32_template);
+#endif
+}
+
+
+
+static struct pci_driver nsp32_driver = {
+ .name = "nsp32",
+ .id_table = nsp32_pci_table,
+ .probe = nsp32_probe,
+ .remove = __devexit_p(nsp32_remove),
+#ifdef CONFIG_PM
+ .suspend = nsp32_suspend,
+ .resume = nsp32_resume,
+ .enable_wake = nsp32_enable_wake,
+#endif
+};
+
+/*********************************************************************
+ * Moule entry point
+ */
+static int __init init_nsp32(void) {
+ nsp32_msg(KERN_INFO, "loading...");
+ return pci_module_init(&nsp32_driver);
+}
+
+static void __exit exit_nsp32(void) {
+ nsp32_msg(KERN_INFO, "unloading...");
+ pci_unregister_driver(&nsp32_driver);
+}
+
+module_init(init_nsp32);
+module_exit(exit_nsp32);
+
+/* end */
diff --git a/drivers/scsi/nsp32.h b/drivers/scsi/nsp32.h
new file mode 100644
index 000000000000..5664398fa0ad
--- /dev/null
+++ b/drivers/scsi/nsp32.h
@@ -0,0 +1,664 @@
+/*
+ * Workbit NinjaSCSI-32Bi/UDE PCI/CardBus SCSI Host Bus Adapter driver
+ * Basic data header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+*/
+
+#ifndef _NSP32_H
+#define _NSP32_H
+
+//#define NSP32_DEBUG 9
+
+/*
+ * VENDOR/DEVICE ID
+ */
+#define PCI_VENDOR_ID_IODATA 0x10fc
+#define PCI_VENDOR_ID_WORKBIT 0x1145
+
+#define PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II 0x0005
+#define PCI_DEVICE_ID_NINJASCSI_32BI_KME 0xf007
+#define PCI_DEVICE_ID_NINJASCSI_32BI_WBT 0x8007
+#define PCI_DEVICE_ID_WORKBIT_STANDARD 0xf010
+#define PCI_DEVICE_ID_WORKBIT_DUALEDGE 0xf011
+#define PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC 0xf012
+#define PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC 0xf013
+#define PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO 0xf015
+#define PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO_II 0x8009
+
+/*
+ * MODEL
+ */
+enum {
+ MODEL_IODATA = 0,
+ MODEL_KME = 1,
+ MODEL_WORKBIT = 2,
+ MODEL_LOGITEC = 3,
+ MODEL_PCI_WORKBIT = 4,
+ MODEL_PCI_LOGITEC = 5,
+ MODEL_PCI_MELCO = 6,
+};
+
+static char * nsp32_model[] = {
+ "I-O DATA CBSC-II CardBus card",
+ "KME SCSI CardBus card",
+ "Workbit duo SCSI CardBus card",
+ "Logitec CardBus card with external ROM",
+ "Workbit / I-O DATA PCI card",
+ "Logitec PCI card with external ROM",
+ "Melco CardBus/PCI card with external ROM",
+};
+
+
+/*
+ * SCSI Generic Definitions
+ */
+#define EXTENDED_SDTR_LEN 0x03
+
+/* Little Endian */
+typedef u32 u32_le;
+typedef u16 u16_le;
+
+/*
+ * MACRO
+ */
+#define BIT(x) (1UL << (x))
+
+/*
+ * BASIC Definitions
+ */
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+#define ASSERT 1
+#define NEGATE 0
+
+
+/*******************/
+/* normal register */
+/*******************/
+/*
+ * Don't access below register with Double Word:
+ * +00, +04, +08, +0c, +64, +80, +84, +88, +90, +c4, +c8, +cc, +d0.
+ */
+#define IRQ_CONTROL 0x00 /* BASE+00, W, W */
+#define IRQ_STATUS 0x00 /* BASE+00, W, R */
+# define IRQSTATUS_LATCHED_MSG BIT(0)
+# define IRQSTATUS_LATCHED_IO BIT(1)
+# define IRQSTATUS_LATCHED_CD BIT(2)
+# define IRQSTATUS_LATCHED_BUS_FREE BIT(3)
+# define IRQSTATUS_RESELECT_OCCUER BIT(4)
+# define IRQSTATUS_PHASE_CHANGE_IRQ BIT(5)
+# define IRQSTATUS_SCSIRESET_IRQ BIT(6)
+# define IRQSTATUS_TIMER_IRQ BIT(7)
+# define IRQSTATUS_FIFO_SHLD_IRQ BIT(8)
+# define IRQSTATUS_PCI_IRQ BIT(9)
+# define IRQSTATUS_BMCNTERR_IRQ BIT(10)
+# define IRQSTATUS_AUTOSCSI_IRQ BIT(11)
+# define PCI_IRQ_MASK BIT(12)
+# define TIMER_IRQ_MASK BIT(13)
+# define FIFO_IRQ_MASK BIT(14)
+# define SCSI_IRQ_MASK BIT(15)
+# define IRQ_CONTROL_ALL_IRQ_MASK (PCI_IRQ_MASK | \
+ TIMER_IRQ_MASK | \
+ FIFO_IRQ_MASK | \
+ SCSI_IRQ_MASK )
+# define IRQSTATUS_ANY_IRQ (IRQSTATUS_RESELECT_OCCUER | \
+ IRQSTATUS_PHASE_CHANGE_IRQ | \
+ IRQSTATUS_SCSIRESET_IRQ | \
+ IRQSTATUS_TIMER_IRQ | \
+ IRQSTATUS_FIFO_SHLD_IRQ | \
+ IRQSTATUS_PCI_IRQ | \
+ IRQSTATUS_BMCNTERR_IRQ | \
+ IRQSTATUS_AUTOSCSI_IRQ )
+
+#define TRANSFER_CONTROL 0x02 /* BASE+02, W, W */
+#define TRANSFER_STATUS 0x02 /* BASE+02, W, R */
+# define CB_MMIO_MODE BIT(0)
+# define CB_IO_MODE BIT(1)
+# define BM_TEST BIT(2)
+# define BM_TEST_DIR BIT(3)
+# define DUAL_EDGE_ENABLE BIT(4)
+# define NO_TRANSFER_TO_HOST BIT(5)
+# define TRANSFER_GO BIT(7)
+# define BLIEND_MODE BIT(8)
+# define BM_START BIT(9)
+# define ADVANCED_BM_WRITE BIT(10)
+# define BM_SINGLE_MODE BIT(11)
+# define FIFO_TRUE_FULL BIT(12)
+# define FIFO_TRUE_EMPTY BIT(13)
+# define ALL_COUNTER_CLR BIT(14)
+# define FIFOTEST BIT(15)
+
+#define INDEX_REG 0x04 /* BASE+04, Byte(R/W), Word(R) */
+
+#define TIMER_SET 0x06 /* BASE+06, W, R/W */
+# define TIMER_CNT_MASK (0xff)
+# define TIMER_STOP BIT(8)
+
+#define DATA_REG_LOW 0x08 /* BASE+08, LowW, R/W */
+#define DATA_REG_HI 0x0a /* BASE+0a, Hi-W, R/W */
+
+#define FIFO_REST_CNT 0x0c /* BASE+0c, W, R/W */
+# define FIFO_REST_MASK 0x1ff
+# define FIFO_EMPTY_SHLD_FLAG BIT(14)
+# define FIFO_FULL_SHLD_FLAG BIT(15)
+
+#define SREQ_SMPL_RATE 0x0f /* BASE+0f, B, R/W */
+# define SREQSMPLRATE_RATE0 BIT(0)
+# define SREQSMPLRATE_RATE1 BIT(1)
+# define SAMPLING_ENABLE BIT(2)
+# define SMPL_40M (0) /* 40MHz: 0-100ns/period */
+# define SMPL_20M (SREQSMPLRATE_RATE0) /* 20MHz: 100-200ns/period */
+# define SMPL_10M (SREQSMPLRATE_RATE1) /* 10Mhz: 200- ns/period */
+
+#define SCSI_BUS_CONTROL 0x10 /* BASE+10, B, R/W */
+# define BUSCTL_SEL BIT(0)
+# define BUSCTL_RST BIT(1)
+# define BUSCTL_DATAOUT_ENB BIT(2)
+# define BUSCTL_ATN BIT(3)
+# define BUSCTL_ACK BIT(4)
+# define BUSCTL_BSY BIT(5)
+# define AUTODIRECTION BIT(6)
+# define ACKENB BIT(7)
+
+#define CLR_COUNTER 0x12 /* BASE+12, B, W */
+# define ACK_COUNTER_CLR BIT(0)
+# define SREQ_COUNTER_CLR BIT(1)
+# define FIFO_HOST_POINTER_CLR BIT(2)
+# define FIFO_REST_COUNT_CLR BIT(3)
+# define BM_COUNTER_CLR BIT(4)
+# define SAVED_ACK_CLR BIT(5)
+# define CLRCOUNTER_ALLMASK (ACK_COUNTER_CLR | \
+ SREQ_COUNTER_CLR | \
+ FIFO_HOST_POINTER_CLR | \
+ FIFO_REST_COUNT_CLR | \
+ BM_COUNTER_CLR | \
+ SAVED_ACK_CLR )
+
+#define SCSI_BUS_MONITOR 0x12 /* BASE+12, B, R */
+# define BUSMON_MSG BIT(0)
+# define BUSMON_IO BIT(1)
+# define BUSMON_CD BIT(2)
+# define BUSMON_BSY BIT(3)
+# define BUSMON_ACK BIT(4)
+# define BUSMON_REQ BIT(5)
+# define BUSMON_SEL BIT(6)
+# define BUSMON_ATN BIT(7)
+
+#define COMMAND_DATA 0x14 /* BASE+14, B, R/W */
+
+#define PARITY_CONTROL 0x16 /* BASE+16, B, W */
+# define PARITY_CHECK_ENABLE BIT(0)
+# define PARITY_ERROR_CLEAR BIT(1)
+#define PARITY_STATUS 0x16 /* BASE+16, B, R */
+//# define PARITY_CHECK_ENABLE BIT(0)
+# define PARITY_ERROR_NORMAL BIT(1)
+# define PARITY_ERROR_LSB BIT(1)
+# define PARITY_ERROR_MSB BIT(2)
+
+#define RESELECT_ID 0x18 /* BASE+18, B, R */
+
+#define COMMAND_CONTROL 0x18 /* BASE+18, W, W */
+# define CLEAR_CDB_FIFO_POINTER BIT(0)
+# define AUTO_COMMAND_PHASE BIT(1)
+# define AUTOSCSI_START BIT(2)
+# define AUTOSCSI_RESTART BIT(3)
+# define AUTO_PARAMETER BIT(4)
+# define AUTO_ATN BIT(5)
+# define AUTO_MSGIN_00_OR_04 BIT(6)
+# define AUTO_MSGIN_02 BIT(7)
+# define AUTO_MSGIN_03 BIT(8)
+
+#define SET_ARBIT 0x1a /* BASE+1a, B, W */
+# define ARBIT_GO BIT(0)
+# define ARBIT_CLEAR BIT(1)
+
+#define ARBIT_STATUS 0x1a /* BASE+1a, B, R */
+//# define ARBIT_GO BIT(0)
+# define ARBIT_WIN BIT(1)
+# define ARBIT_FAIL BIT(2)
+# define AUTO_PARAMETER_VALID BIT(3)
+# define SGT_VALID BIT(4)
+
+#define SYNC_REG 0x1c /* BASE+1c, B, R/W */
+
+#define ACK_WIDTH 0x1d /* BASE+1d, B, R/W */
+
+#define SCSI_DATA_WITH_ACK 0x20 /* BASE+20, B, R/W */
+#define SCSI_OUT_LATCH_TARGET_ID 0x22 /* BASE+22, B, W */
+#define SCSI_DATA_IN 0x22 /* BASE+22, B, R */
+
+#define SCAM_CONTROL 0x24 /* BASE+24, B, W */
+#define SCAM_STATUS 0x24 /* BASE+24, B, R */
+# define SCAM_MSG BIT(0)
+# define SCAM_IO BIT(1)
+# define SCAM_CD BIT(2)
+# define SCAM_BSY BIT(3)
+# define SCAM_SEL BIT(4)
+# define SCAM_XFEROK BIT(5)
+
+#define SCAM_DATA 0x26 /* BASE+26, B, R/W */
+# define SD0 BIT(0)
+# define SD1 BIT(1)
+# define SD2 BIT(2)
+# define SD3 BIT(3)
+# define SD4 BIT(4)
+# define SD5 BIT(5)
+# define SD6 BIT(6)
+# define SD7 BIT(7)
+
+#define SACK_CNT 0x28 /* BASE+28, DW, R/W */
+#define SREQ_CNT 0x2c /* BASE+2c, DW, R/W */
+
+#define FIFO_DATA_LOW 0x30 /* BASE+30, B/W/DW, R/W */
+#define FIFO_DATA_HIGH 0x32 /* BASE+32, B/W, R/W */
+#define BM_START_ADR 0x34 /* BASE+34, DW, R/W */
+
+#define BM_CNT 0x38 /* BASE+38, DW, R/W */
+# define BM_COUNT_MASK 0x0001ffffUL
+# define SGTEND BIT(31) /* Last SGT marker */
+
+#define SGT_ADR 0x3c /* BASE+3c, DW, R/W */
+#define WAIT_REG 0x40 /* Bi only */
+
+#define SCSI_EXECUTE_PHASE 0x40 /* BASE+40, W, R */
+# define COMMAND_PHASE BIT(0)
+# define DATA_IN_PHASE BIT(1)
+# define DATA_OUT_PHASE BIT(2)
+# define MSGOUT_PHASE BIT(3)
+# define STATUS_PHASE BIT(4)
+# define ILLEGAL_PHASE BIT(5)
+# define BUS_FREE_OCCUER BIT(6)
+# define MSG_IN_OCCUER BIT(7)
+# define MSG_OUT_OCCUER BIT(8)
+# define SELECTION_TIMEOUT BIT(9)
+# define MSGIN_00_VALID BIT(10)
+# define MSGIN_02_VALID BIT(11)
+# define MSGIN_03_VALID BIT(12)
+# define MSGIN_04_VALID BIT(13)
+# define AUTOSCSI_BUSY BIT(15)
+
+#define SCSI_CSB_IN 0x42 /* BASE+42, B, R */
+
+#define SCSI_MSG_OUT 0x44 /* BASE+44, DW, R/W */
+# define MSGOUT_COUNT_MASK (BIT(0)|BIT(1))
+# define MV_VALID BIT(7)
+
+#define SEL_TIME_OUT 0x48 /* BASE+48, W, R/W */
+#define SAVED_SACK_CNT 0x4c /* BASE+4c, DW, R */
+
+#define HTOSDATADELAY 0x50 /* BASE+50, B, R/W */
+#define STOHDATADELAY 0x54 /* BASE+54, B, R/W */
+#define ACKSUMCHECKRD 0x58 /* BASE+58, W, R */
+#define REQSUMCHECKRD 0x5c /* BASE+5c, W, R */
+
+
+/********************/
+/* indexed register */
+/********************/
+
+#define CLOCK_DIV 0x00 /* BASE+08, IDX+00, B, R/W */
+# define CLOCK_2 BIT(0) /* MCLK/2 */
+# define CLOCK_4 BIT(1) /* MCLK/4 */
+# define PCICLK BIT(7) /* PCICLK (33MHz) */
+
+#define TERM_PWR_CONTROL 0x01 /* BASE+08, IDX+01, B, R/W */
+# define BPWR BIT(0)
+# define SENSE BIT(1) /* Read Only */
+
+#define EXT_PORT_DDR 0x02 /* BASE+08, IDX+02, B, R/W */
+#define EXT_PORT 0x03 /* BASE+08, IDX+03, B, R/W */
+# define LED_ON (0)
+# define LED_OFF BIT(0)
+
+#define IRQ_SELECT 0x04 /* BASE+08, IDX+04, W, R/W */
+# define IRQSELECT_RESELECT_IRQ BIT(0)
+# define IRQSELECT_PHASE_CHANGE_IRQ BIT(1)
+# define IRQSELECT_SCSIRESET_IRQ BIT(2)
+# define IRQSELECT_TIMER_IRQ BIT(3)
+# define IRQSELECT_FIFO_SHLD_IRQ BIT(4)
+# define IRQSELECT_TARGET_ABORT_IRQ BIT(5)
+# define IRQSELECT_MASTER_ABORT_IRQ BIT(6)
+# define IRQSELECT_SERR_IRQ BIT(7)
+# define IRQSELECT_PERR_IRQ BIT(8)
+# define IRQSELECT_BMCNTERR_IRQ BIT(9)
+# define IRQSELECT_AUTO_SCSI_SEQ_IRQ BIT(10)
+
+#define OLD_SCSI_PHASE 0x05 /* BASE+08, IDX+05, B, R */
+# define OLD_MSG BIT(0)
+# define OLD_IO BIT(1)
+# define OLD_CD BIT(2)
+# define OLD_BUSY BIT(3)
+
+#define FIFO_FULL_SHLD_COUNT 0x06 /* BASE+08, IDX+06, B, R/W */
+#define FIFO_EMPTY_SHLD_COUNT 0x07 /* BASE+08, IDX+07, B, R/W */
+
+#define EXP_ROM_CONTROL 0x08 /* BASE+08, IDX+08, B, R/W */ /* external ROM control */
+# define ROM_WRITE_ENB BIT(0)
+# define IO_ACCESS_ENB BIT(1)
+# define ROM_ADR_CLEAR BIT(2)
+
+#define EXP_ROM_ADR 0x09 /* BASE+08, IDX+09, W, R/W */
+
+#define EXP_ROM_DATA 0x0a /* BASE+08, IDX+0a, B, R/W */
+
+#define CHIP_MODE 0x0b /* BASE+08, IDX+0b, B, R */ /* NinjaSCSI-32Bi only */
+# define OEM0 BIT(1) /* OEM select */ /* 00=I-O DATA, 01=KME, 10=Workbit, 11=Ext ROM */
+# define OEM1 BIT(2) /* OEM select */
+# define OPTB BIT(3) /* KME mode select */
+# define OPTC BIT(4) /* KME mode select */
+# define OPTD BIT(5) /* KME mode select */
+# define OPTE BIT(6) /* KME mode select */
+# define OPTF BIT(7) /* Power management */
+
+#define MISC_WR 0x0c /* BASE+08, IDX+0c, W, R/W */
+#define MISC_RD 0x0c
+# define SCSI_DIRECTION_DETECTOR_SELECT BIT(0)
+# define SCSI2_HOST_DIRECTION_VALID BIT(1) /* Read only */
+# define HOST2_SCSI_DIRECTION_VALID BIT(2) /* Read only */
+# define DELAYED_BMSTART BIT(3)
+# define MASTER_TERMINATION_SELECT BIT(4)
+# define BMREQ_NEGATE_TIMING_SEL BIT(5)
+# define AUTOSEL_TIMING_SEL BIT(6)
+# define MISC_MABORT_MASK BIT(7)
+# define BMSTOP_CHANGE2_NONDATA_PHASE BIT(8)
+
+#define BM_CYCLE 0x0d /* BASE+08, IDX+0d, B, R/W */
+# define BM_CYCLE0 BIT(0)
+# define BM_CYCLE1 BIT(1)
+# define BM_FRAME_ASSERT_TIMING BIT(2)
+# define BM_IRDY_ASSERT_TIMING BIT(3)
+# define BM_SINGLE_BUS_MASTER BIT(4)
+# define MEMRD_CMD0 BIT(5)
+# define SGT_AUTO_PARA_MEMED_CMD BIT(6)
+# define MEMRD_CMD1 BIT(7)
+
+
+#define SREQ_EDGH 0x0e /* BASE+08, IDX+0e, B, W */
+# define SREQ_EDGH_SELECT BIT(0)
+
+#define UP_CNT 0x0f /* BASE+08, IDX+0f, B, W */
+# define REQCNT_UP BIT(0)
+# define ACKCNT_UP BIT(1)
+# define BMADR_UP BIT(4)
+# define BMCNT_UP BIT(5)
+# define SGT_CNT_UP BIT(7)
+
+#define CFG_CMD_STR 0x10 /* BASE+08, IDX+10, W, R */
+#define CFG_LATE_CACHE 0x11 /* BASE+08, IDX+11, W, R/W */
+#define CFG_BASE_ADR_1 0x12 /* BASE+08, IDX+12, W, R */
+#define CFG_BASE_ADR_2 0x13 /* BASE+08, IDX+13, W, R */
+#define CFG_INLINE 0x14 /* BASE+08, IDX+14, W, R */
+
+#define SERIAL_ROM_CTL 0x15 /* BASE+08, IDX+15, B, R */
+# define SCL BIT(0)
+# define ENA BIT(1)
+# define SDA BIT(2)
+
+#define FIFO_HST_POINTER 0x16 /* BASE+08, IDX+16, B, R/W */
+#define SREQ_DELAY 0x17 /* BASE+08, IDX+17, B, R/W */
+#define SACK_DELAY 0x18 /* BASE+08, IDX+18, B, R/W */
+#define SREQ_NOISE_CANCEL 0x19 /* BASE+08, IDX+19, B, R/W */
+#define SDP_NOISE_CANCEL 0x1a /* BASE+08, IDX+1a, B, R/W */
+#define DELAY_TEST 0x1b /* BASE+08, IDX+1b, B, R/W */
+#define SD0_NOISE_CANCEL 0x20 /* BASE+08, IDX+20, B, R/W */
+#define SD1_NOISE_CANCEL 0x21 /* BASE+08, IDX+21, B, R/W */
+#define SD2_NOISE_CANCEL 0x22 /* BASE+08, IDX+22, B, R/W */
+#define SD3_NOISE_CANCEL 0x23 /* BASE+08, IDX+23, B, R/W */
+#define SD4_NOISE_CANCEL 0x24 /* BASE+08, IDX+24, B, R/W */
+#define SD5_NOISE_CANCEL 0x25 /* BASE+08, IDX+25, B, R/W */
+#define SD6_NOISE_CANCEL 0x26 /* BASE+08, IDX+26, B, R/W */
+#define SD7_NOISE_CANCEL 0x27 /* BASE+08, IDX+27, B, R/W */
+
+
+/*
+ * Useful Bus Monitor status combinations.
+ */
+#define BUSMON_BUS_FREE 0
+#define BUSMON_COMMAND ( BUSMON_BSY | BUSMON_CD | BUSMON_REQ )
+#define BUSMON_MESSAGE_IN ( BUSMON_BSY | BUSMON_MSG | BUSMON_IO | BUSMON_CD | BUSMON_REQ )
+#define BUSMON_MESSAGE_OUT ( BUSMON_BSY | BUSMON_MSG | BUSMON_CD | BUSMON_REQ )
+#define BUSMON_DATA_IN ( BUSMON_BSY | BUSMON_IO | BUSMON_REQ )
+#define BUSMON_DATA_OUT ( BUSMON_BSY | BUSMON_REQ )
+#define BUSMON_STATUS ( BUSMON_BSY | BUSMON_IO | BUSMON_CD | BUSMON_REQ )
+#define BUSMON_RESELECT ( BUSMON_IO | BUSMON_SEL)
+#define BUSMON_PHASE_MASK ( BUSMON_MSG | BUSMON_IO | BUSMON_CD | BUSMON_SEL)
+
+#define BUSPHASE_COMMAND ( BUSMON_COMMAND & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_IN ( BUSMON_MESSAGE_IN & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_OUT ( BUSMON_MESSAGE_OUT & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_IN ( BUSMON_DATA_IN & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_OUT ( BUSMON_DATA_OUT & BUSMON_PHASE_MASK )
+#define BUSPHASE_STATUS ( BUSMON_STATUS & BUSMON_PHASE_MASK )
+#define BUSPHASE_SELECT ( BUSMON_SEL | BUSMON_IO )
+
+
+/************************************************************************
+ * structure for DMA/Scatter Gather list
+ */
+#define NSP32_SG_SIZE SG_ALL
+
+typedef struct _nsp32_sgtable {
+ /* values must be little endian */
+ u32_le addr; /* transfer address */
+ u32_le len; /* transfer length. BIT(31) is for SGT_END mark */
+} __attribute__ ((packed)) nsp32_sgtable;
+
+typedef struct _nsp32_sglun {
+ nsp32_sgtable sgt[NSP32_SG_SIZE+1]; /* SG table */
+} __attribute__ ((packed)) nsp32_sglun;
+#define NSP32_SG_TABLE_SIZE (sizeof(nsp32_sgtable) * NSP32_SG_SIZE * MAX_TARGET * MAX_LUN)
+
+/* Auto parameter mode memory map. */
+/* All values must be little endian. */
+typedef struct _nsp32_autoparam {
+ u8 cdb[4 * 0x10]; /* SCSI Command */
+ u32_le msgout; /* outgoing messages */
+ u8 syncreg; /* sync register value */
+ u8 ackwidth; /* ack width register value */
+ u8 target_id; /* target/host device id */
+ u8 sample_reg; /* hazard killer sampling rate */
+ u16_le command_control; /* command control register */
+ u16_le transfer_control; /* transfer control register */
+ u32_le sgt_pointer; /* SG table physical address for DMA */
+ u32_le dummy[2];
+} __attribute__ ((packed)) nsp32_autoparam; /* must be packed struct */
+
+/*
+ * host data structure
+ */
+/* message in/out buffer */
+#define MSGOUTBUF_MAX 20
+#define MSGINBUF_MAX 20
+
+/* flag for trans_method */
+#define NSP32_TRANSFER_BUSMASTER BIT(0)
+#define NSP32_TRANSFER_MMIO BIT(1) /* Not supported yet */
+#define NSP32_TRANSFER_PIO BIT(2) /* Not supported yet */
+
+
+/*
+ * structure for connected LUN dynamic data
+ *
+ * Note: Currently tagged queuing is disabled, each nsp32_lunt holds
+ * one SCSI command and one state.
+ */
+#define DISCPRIV_OK BIT(0) /* DISCPRIV Enable mode */
+#define MSGIN03 BIT(1) /* Auto Msg In 03 Flag */
+
+typedef struct _nsp32_lunt {
+ struct scsi_cmnd *SCpnt; /* Current Handling struct scsi_cmnd */
+ unsigned long save_datp; /* Save Data Pointer - saved position from initial address */
+ int msgin03; /* auto msg in 03 flag */
+ unsigned int sg_num; /* Total number of SG entries */
+ int cur_entry; /* Current SG entry number */
+ nsp32_sglun *sglun; /* sg table per lun */
+ dma_addr_t sglun_paddr; /* sglun physical address */
+} nsp32_lunt;
+
+
+/*
+ * SCSI TARGET/LUN definition
+ */
+#define NSP32_HOST_SCSIID 7 /* SCSI initiator is everytime defined as 7 */
+#define MAX_TARGET 8
+#define MAX_LUN 8 /* XXX: In SPI3, max number of LUN is 64. */
+
+
+typedef struct _nsp32_sync_table {
+ unsigned char period_num; /* period number */
+ unsigned char ackwidth; /* ack width designated by period */
+ unsigned char start_period; /* search range - start period */
+ unsigned char end_period; /* search range - end period */
+ unsigned char sample_rate; /* hazard killer parameter */
+} nsp32_sync_table;
+
+
+/*
+ * structure for target device static data
+ */
+/* flag for nsp32_target.sync_flag */
+#define SDTR_INITIATOR BIT(0) /* sending SDTR from initiator */
+#define SDTR_TARGET BIT(1) /* sending SDTR from target */
+#define SDTR_DONE BIT(2) /* exchanging SDTR has been processed */
+
+/* syncronous period value for nsp32_target.config_max */
+#define FAST5M 0x32
+#define FAST10M 0x19
+#define ULTRA20M 0x0c
+
+/* flag for nsp32_target.{sync_offset}, period */
+#define ASYNC_OFFSET 0 /* asynchronous transfer */
+#define SYNC_OFFSET 0xf /* synchronous transfer max offset */
+
+/* syncreg:
+ bit:07 06 05 04 03 02 01 00
+ ---PERIOD-- ---OFFSET-- */
+#define TO_SYNCREG(period, offset) (((period) & 0x0f) << 4 | ((offset) & 0x0f))
+
+typedef struct _nsp32_target {
+ unsigned char syncreg; /* value for SYNCREG */
+ unsigned char ackwidth; /* value for ACKWIDTH */
+ unsigned char period; /* sync period (0-255) */
+ unsigned char offset; /* sync offset (0-15) */
+ int sync_flag; /* SDTR_*, 0 */
+ int limit_entry; /* max speed limit entry designated
+ by EEPROM configuration */
+ unsigned char sample_reg; /* SREQ hazard killer register */
+} nsp32_target;
+
+typedef struct _nsp32_hw_data {
+ int IrqNumber;
+ int BaseAddress;
+ int NumAddress;
+ void __iomem *MmioAddress;
+#define NSP32_MMIO_OFFSET 0x0800
+ unsigned long MmioLength;
+
+ struct scsi_cmnd *CurrentSC;
+
+ struct pci_dev *Pci;
+ const struct pci_device_id *pci_devid;
+ struct Scsi_Host *Host;
+ spinlock_t Lock;
+
+ char info_str[100];
+
+ /* allocated memory region */
+ nsp32_sglun *sg_list; /* sglist virtuxal address */
+ dma_addr_t sg_paddr; /* physical address of hw_sg_table */
+ nsp32_autoparam *autoparam; /* auto parameter transfer region */
+ dma_addr_t auto_paddr; /* physical address of autoparam */
+ int cur_entry; /* current sgt entry */
+
+ /* target/LUN */
+ nsp32_lunt *cur_lunt; /* Current connected LUN table */
+ nsp32_lunt lunt[MAX_TARGET][MAX_LUN]; /* All LUN table */
+
+ nsp32_target *cur_target; /* Current connected SCSI ID */
+ nsp32_target target[MAX_TARGET]; /* SCSI ID */
+ int cur_id; /* Current connected target ID */
+ int cur_lun; /* Current connected target LUN */
+
+ /* behavior setting parameters */
+ int trans_method; /* transfer method flag */
+ int resettime; /* Reset time */
+ int clock; /* clock dividing flag */
+ nsp32_sync_table *synct; /* sync_table determined by clock */
+ int syncnum; /* the max number of synct element */
+
+ /* message buffer */
+ unsigned char msgoutbuf[MSGOUTBUF_MAX]; /* msgout buffer */
+ char msgout_len; /* msgoutbuf length */
+ unsigned char msginbuf [MSGINBUF_MAX]; /* megin buffer */
+ char msgin_len; /* msginbuf length */
+
+} nsp32_hw_data;
+
+/*
+ * TIME definition
+ */
+#define RESET_HOLD_TIME 10000 /* reset time in us (SCSI-2 says the
+ minimum is 25us) */
+#define SEL_TIMEOUT_TIME 10000 /* 250ms defined in SCSI specification
+ (25.6us/1unit) */
+#define ARBIT_TIMEOUT_TIME 100 /* 100us */
+#define REQSACK_TIMEOUT_TIME 10000 /* max wait time for REQ/SACK assertion
+ or negation, 10000us == 10ms */
+
+/**************************************************************************
+ * Compatibility functions
+ */
+
+/* for Kernel 2.4 */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+# define scsi_register_host(template) scsi_register_module(MODULE_SCSI_HA, template)
+# define scsi_unregister_host(template) scsi_unregister_module(MODULE_SCSI_HA, template)
+# define scsi_host_put(host) scsi_unregister(host)
+# define pci_name(pci_dev) ((pci_dev)->slot_name)
+
+typedef void irqreturn_t;
+# define IRQ_NONE /* */
+# define IRQ_HANDLED /* */
+# define IRQ_RETVAL(x) /* */
+
+/* This is ad-hoc version of scsi_host_get_next() */
+static inline struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *host)
+{
+ if (host == NULL) {
+ return scsi_hostlist;
+ } else {
+ return host->next;
+ }
+}
+
+/* This is ad-hoc version of scsi_host_hn_get() */
+static inline struct Scsi_Host *scsi_host_hn_get(unsigned short hostno)
+{
+ struct Scsi_Host *host;
+
+ for (host = scsi_host_get_next(NULL); host != NULL;
+ host = scsi_host_get_next(host)) {
+ if (host->host_no == hostno) {
+ break;
+ }
+ }
+
+ return host;
+}
+#endif
+
+#endif /* _NSP32_H */
+/* end */
diff --git a/drivers/scsi/nsp32_debug.c b/drivers/scsi/nsp32_debug.c
new file mode 100644
index 000000000000..ef3c59cbcff6
--- /dev/null
+++ b/drivers/scsi/nsp32_debug.c
@@ -0,0 +1,263 @@
+/*
+ * Workbit NinjaSCSI-32Bi/UDE PCI/CardBus SCSI Host Bus Adapter driver
+ * Debug routine
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License.
+ */
+
+/*
+ * Show the command data of a command
+ */
+static const char unknown[] = "UNKNOWN";
+
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */ unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
+/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long", "Write Long",
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", "Write Same",
+/* 42-48 */ "Read Sub-Ch(cd)", "Read TOC", "Read Header(cd)", "Play Audio(cd)", unknown, "Play Audio MSF(cd)", "Play Audio Track/Index(cd)",
+/* 49-4f */ "Play Track Relative(10)(cd)", unknown, "Pause/Resume(cd)", "Log Select", "Log Sense", unknown, unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP 0
+#define VENDOR_GROUP 1
+#define NOTEXT_GROUP 2
+
+static const char **commands[] = {
+ group_0_commands, group_1_commands, group_2_commands,
+ (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
+ (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP,
+ (const char **) VENDOR_GROUP
+};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcodek(unsigned char opcode)
+{
+ const char **table = commands[ group(opcode) ];
+
+ switch ((unsigned long) table) {
+ case RESERVED_GROUP:
+ printk("%s[%02x] ", reserved, opcode);
+ break;
+ case NOTEXT_GROUP:
+ printk("%s(notext)[%02x] ", unknown, opcode);
+ break;
+ case VENDOR_GROUP:
+ printk("%s[%02x] ", vendor, opcode);
+ break;
+ default:
+ if (table[opcode & 0x1f] != unknown)
+ printk("%s[%02x] ", table[opcode & 0x1f], opcode);
+ else
+ printk("%s[%02x] ", unknown, opcode);
+ break;
+ }
+}
+
+static void print_commandk (unsigned char *command)
+{
+ int i,s;
+// printk(KERN_DEBUG);
+ print_opcodek(command[0]);
+ /*printk(KERN_DEBUG "%s ", __FUNCTION__);*/
+ if ((command[0] >> 5) == 6 ||
+ (command[0] >> 5) == 7 ) {
+ s = 12; /* vender specific */
+ } else {
+ s = COMMAND_SIZE(command[0]);
+ }
+
+ for ( i = 1; i < s; ++i) {
+ printk("%02x ", command[i]);
+ }
+
+ switch (s) {
+ case 6:
+ printk("LBA=%d len=%d",
+ (((unsigned int)command[1] & 0x0f) << 16) |
+ ( (unsigned int)command[2] << 8) |
+ ( (unsigned int)command[3] ),
+ (unsigned int)command[4]
+ );
+ break;
+ case 10:
+ printk("LBA=%d len=%d",
+ ((unsigned int)command[2] << 24) |
+ ((unsigned int)command[3] << 16) |
+ ((unsigned int)command[4] << 8) |
+ ((unsigned int)command[5] ),
+ ((unsigned int)command[7] << 8) |
+ ((unsigned int)command[8] )
+ );
+ break;
+ case 12:
+ printk("LBA=%d len=%d",
+ ((unsigned int)command[2] << 24) |
+ ((unsigned int)command[3] << 16) |
+ ((unsigned int)command[4] << 8) |
+ ((unsigned int)command[5] ),
+ ((unsigned int)command[6] << 24) |
+ ((unsigned int)command[7] << 16) |
+ ((unsigned int)command[8] << 8) |
+ ((unsigned int)command[9] )
+ );
+ break;
+ default:
+ break;
+ }
+ printk("\n");
+}
+
+static void show_command(Scsi_Cmnd *SCpnt)
+{
+ print_commandk(SCpnt->cmnd);
+}
+
+static void show_busphase(unsigned char stat)
+{
+ switch(stat) {
+ case BUSPHASE_COMMAND:
+ printk( "BUSPHASE_COMMAND\n");
+ break;
+ case BUSPHASE_MESSAGE_IN:
+ printk( "BUSPHASE_MESSAGE_IN\n");
+ break;
+ case BUSPHASE_MESSAGE_OUT:
+ printk( "BUSPHASE_MESSAGE_OUT\n");
+ break;
+ case BUSPHASE_DATA_IN:
+ printk( "BUSPHASE_DATA_IN\n");
+ break;
+ case BUSPHASE_DATA_OUT:
+ printk( "BUSPHASE_DATA_OUT\n");
+ break;
+ case BUSPHASE_STATUS:
+ printk( "BUSPHASE_STATUS\n");
+ break;
+ case BUSPHASE_SELECT:
+ printk( "BUSPHASE_SELECT\n");
+ break;
+ default:
+ printk( "BUSPHASE_other: 0x%x\n", stat);
+ break;
+ }
+}
+
+static void show_autophase(unsigned short i)
+{
+ printk("auto: 0x%x,", i);
+
+ if(i & COMMAND_PHASE) {
+ printk(" cmd");
+ }
+ if(i & DATA_IN_PHASE) {
+ printk(" din");
+ }
+ if(i & DATA_OUT_PHASE) {
+ printk(" dout");
+ }
+ if(i & MSGOUT_PHASE) {
+ printk(" mout");
+ }
+ if(i & STATUS_PHASE) {
+ printk(" stat");
+ }
+ if(i & ILLEGAL_PHASE) {
+ printk(" ill");
+ }
+ if(i & BUS_FREE_OCCUER) {
+ printk(" bfree-o");
+ }
+ if(i & MSG_IN_OCCUER) {
+ printk(" min-o");
+ }
+ if(i & MSG_OUT_OCCUER) {
+ printk(" mout-o");
+ }
+ if(i & SELECTION_TIMEOUT) {
+ printk(" sel");
+ }
+ if(i & MSGIN_00_VALID) {
+ printk(" m0");
+ }
+ if(i & MSGIN_02_VALID) {
+ printk(" m2");
+ }
+ if(i & MSGIN_03_VALID) {
+ printk(" m3");
+ }
+ if(i & MSGIN_04_VALID) {
+ printk(" m4");
+ }
+ if(i & AUTOSCSI_BUSY) {
+ printk(" busy");
+ }
+
+ printk("\n");
+}
+
+static void nsp32_print_register(int base)
+{
+ if (!(NSP32_DEBUG_MASK & NSP32_SPECIAL_PRINT_REGISTER))
+ return;
+
+ printk("Phase=0x%x, ", nsp32_read1(base, SCSI_BUS_MONITOR));
+ printk("OldPhase=0x%x, ", nsp32_index_read1(base, OLD_SCSI_PHASE));
+ printk("syncreg=0x%x, ", nsp32_read1(base, SYNC_REG));
+ printk("ackwidth=0x%x, ", nsp32_read1(base, ACK_WIDTH));
+ printk("sgtpaddr=0x%lx, ", nsp32_read4(base, SGT_ADR));
+ printk("scsioutlatch=0x%x, ", nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
+ printk("msgout=0x%lx, ", nsp32_read4(base, SCSI_MSG_OUT));
+ printk("miscrd=0x%x, ", nsp32_index_read2(base, MISC_WR));
+ printk("seltimeout=0x%x, ", nsp32_read2(base, SEL_TIME_OUT));
+ printk("sreqrate=0x%x, ", nsp32_read1(base, SREQ_SMPL_RATE));
+ printk("transStatus=0x%x, ", nsp32_read2(base, TRANSFER_STATUS));
+ printk("reselectid=0x%x, ", nsp32_read2(base, COMMAND_CONTROL));
+ printk("arbit=0x%x, ", nsp32_read1(base, ARBIT_STATUS));
+ printk("BmStart=0x%lx, ", nsp32_read4(base, BM_START_ADR));
+ printk("BmCount=0x%lx, ", nsp32_read4(base, BM_CNT));
+ printk("SackCnt=0x%lx, ", nsp32_read4(base, SACK_CNT));
+ printk("SReqCnt=0x%lx, ", nsp32_read4(base, SREQ_CNT));
+ printk("SavedSackCnt=0x%lx, ", nsp32_read4(base, SAVED_SACK_CNT));
+ printk("ScsiBusControl=0x%x, ", nsp32_read1(base, SCSI_BUS_CONTROL));
+ printk("FifoRestCnt=0x%x, ", nsp32_read2(base, FIFO_REST_CNT));
+ printk("CdbIn=0x%x, ", nsp32_read1(base, SCSI_CSB_IN));
+ printk("\n");
+
+ if (0) {
+ printk("execph=0x%x, ", nsp32_read2(base, SCSI_EXECUTE_PHASE));
+ printk("IrqStatus=0x%x, ", nsp32_read2(base, IRQ_STATUS));
+ printk("\n");
+ }
+}
+
+/* end */
diff --git a/drivers/scsi/nsp32_io.h b/drivers/scsi/nsp32_io.h
new file mode 100644
index 000000000000..e3f3c27b01ef
--- /dev/null
+++ b/drivers/scsi/nsp32_io.h
@@ -0,0 +1,259 @@
+/*
+ * Workbit NinjaSCSI-32Bi/UDE PCI/CardBus SCSI Host Bus Adapter driver
+ * I/O routine
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License.
+ */
+
+#ifndef _NSP32_IO_H
+#define _NSP32_IO_H
+
+static inline void nsp32_write1(unsigned int base,
+ unsigned int index,
+ unsigned char val)
+{
+ outb(val, (base + index));
+}
+
+static inline unsigned char nsp32_read1(unsigned int base,
+ unsigned int index)
+{
+ return inb(base + index);
+}
+
+static inline void nsp32_write2(unsigned int base,
+ unsigned int index,
+ unsigned short val)
+{
+ outw(val, (base + index));
+}
+
+static inline unsigned short nsp32_read2(unsigned int base,
+ unsigned int index)
+{
+ return inw(base + index);
+}
+
+static inline void nsp32_write4(unsigned int base,
+ unsigned int index,
+ unsigned long val)
+{
+ outl(val, (base + index));
+}
+
+static inline unsigned long nsp32_read4(unsigned int base,
+ unsigned int index)
+{
+ return inl(base + index);
+}
+
+/*==============================================*/
+
+static inline void nsp32_mmio_write1(unsigned long base,
+ unsigned int index,
+ unsigned char val)
+{
+ volatile unsigned char *ptr;
+
+ ptr = (unsigned char *)(base + NSP32_MMIO_OFFSET + index);
+
+ writeb(val, ptr);
+}
+
+static inline unsigned char nsp32_mmio_read1(unsigned long base,
+ unsigned int index)
+{
+ volatile unsigned char *ptr;
+
+ ptr = (unsigned char *)(base + NSP32_MMIO_OFFSET + index);
+
+ return readb(ptr);
+}
+
+static inline void nsp32_mmio_write2(unsigned long base,
+ unsigned int index,
+ unsigned short val)
+{
+ volatile unsigned short *ptr;
+
+ ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + index);
+
+ writew(cpu_to_le16(val), ptr);
+}
+
+static inline unsigned short nsp32_mmio_read2(unsigned long base,
+ unsigned int index)
+{
+ volatile unsigned short *ptr;
+
+ ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + index);
+
+ return le16_to_cpu(readw(ptr));
+}
+
+static inline void nsp32_mmio_write4(unsigned long base,
+ unsigned int index,
+ unsigned long val)
+{
+ volatile unsigned long *ptr;
+
+ ptr = (unsigned long *)(base + NSP32_MMIO_OFFSET + index);
+
+ writel(cpu_to_le32(val), ptr);
+}
+
+static inline unsigned long nsp32_mmio_read4(unsigned long base,
+ unsigned int index)
+{
+ volatile unsigned long *ptr;
+
+ ptr = (unsigned long *)(base + NSP32_MMIO_OFFSET + index);
+
+ return le32_to_cpu(readl(ptr));
+}
+
+/*==============================================*/
+
+static inline unsigned char nsp32_index_read1(unsigned int base,
+ unsigned int reg)
+{
+ outb(reg, base + INDEX_REG);
+ return inb(base + DATA_REG_LOW);
+}
+
+static inline void nsp32_index_write1(unsigned int base,
+ unsigned int reg,
+ unsigned char val)
+{
+ outb(reg, base + INDEX_REG );
+ outb(val, base + DATA_REG_LOW);
+}
+
+static inline unsigned short nsp32_index_read2(unsigned int base,
+ unsigned int reg)
+{
+ outb(reg, base + INDEX_REG);
+ return inw(base + DATA_REG_LOW);
+}
+
+static inline void nsp32_index_write2(unsigned int base,
+ unsigned int reg,
+ unsigned short val)
+{
+ outb(reg, base + INDEX_REG );
+ outw(val, base + DATA_REG_LOW);
+}
+
+static inline unsigned long nsp32_index_read4(unsigned int base,
+ unsigned int reg)
+{
+ unsigned long h,l;
+
+ outb(reg, base + INDEX_REG);
+ l = inw(base + DATA_REG_LOW);
+ h = inw(base + DATA_REG_HI );
+
+ return ((h << 16) | l);
+}
+
+static inline void nsp32_index_write4(unsigned int base,
+ unsigned int reg,
+ unsigned long val)
+{
+ unsigned long h,l;
+
+ h = (val & 0xffff0000) >> 16;
+ l = (val & 0x0000ffff) >> 0;
+
+ outb(reg, base + INDEX_REG );
+ outw(l, base + DATA_REG_LOW);
+ outw(h, base + DATA_REG_HI );
+}
+
+/*==============================================*/
+
+static inline unsigned char nsp32_mmio_index_read1(unsigned long base,
+ unsigned int reg)
+{
+ volatile unsigned short *index_ptr, *data_ptr;
+
+ index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+ data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+ writeb(reg, index_ptr);
+ return readb(data_ptr);
+}
+
+static inline void nsp32_mmio_index_write1(unsigned long base,
+ unsigned int reg,
+ unsigned char val)
+{
+ volatile unsigned short *index_ptr, *data_ptr;
+
+ index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+ data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+ writeb(reg, index_ptr);
+ writeb(val, data_ptr );
+}
+
+static inline unsigned short nsp32_mmio_index_read2(unsigned long base,
+ unsigned int reg)
+{
+ volatile unsigned short *index_ptr, *data_ptr;
+
+ index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+ data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+ writeb(reg, index_ptr);
+ return le16_to_cpu(readw(data_ptr));
+}
+
+static inline void nsp32_mmio_index_write2(unsigned long base,
+ unsigned int reg,
+ unsigned short val)
+{
+ volatile unsigned short *index_ptr, *data_ptr;
+
+ index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+ data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+ writeb(reg, index_ptr);
+ writew(cpu_to_le16(val), data_ptr );
+}
+
+/*==============================================*/
+
+static inline void nsp32_multi_read4(unsigned int base,
+ unsigned int reg,
+ void *buf,
+ unsigned long count)
+{
+ insl(base + reg, buf, count);
+}
+
+static inline void nsp32_fifo_read(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ nsp32_multi_read4(base, FIFO_DATA_LOW, buf, count);
+}
+
+static inline void nsp32_multi_write4(unsigned int base,
+ unsigned int reg,
+ void *buf,
+ unsigned long count)
+{
+ outsl(base + reg, buf, count);
+}
+
+static inline void nsp32_fifo_write(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ nsp32_multi_write4(base, FIFO_DATA_LOW, buf, count);
+}
+
+#endif /* _NSP32_IO_H */
+/* end */
diff --git a/drivers/scsi/oktagon_esp.c b/drivers/scsi/oktagon_esp.c
new file mode 100644
index 000000000000..573d7ef93f08
--- /dev/null
+++ b/drivers/scsi/oktagon_esp.c
@@ -0,0 +1,609 @@
+/*
+ * Oktagon_esp.c -- Driver for bsc Oktagon
+ *
+ * Written by Carsten Pluntke 1998
+ *
+ * Based on cyber_esp.c
+ */
+
+#include <linux/config.h>
+
+#if defined(CONFIG_AMIGA) || defined(CONFIG_APUS)
+#define USE_BOTTOM_HALF
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/reboot.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#ifdef USE_BOTTOM_HALF
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#endif
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define OKTAGON_ESP_ADDR 0x03000
+#define OKTAGON_DMA_ADDR 0x01000
+
+
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static void dma_led_off(struct NCR_ESP *esp);
+static void dma_led_on(struct NCR_ESP *esp);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+
+static void dma_irq_exit(struct NCR_ESP *esp);
+static void dma_invalidate(struct NCR_ESP *esp);
+
+static void dma_mmu_get_scsi_one(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_mmu_get_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_mmu_release_scsi_one(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_mmu_release_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *);
+static void dma_advance_sg(Scsi_Cmnd *);
+static int oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x);
+
+#ifdef USE_BOTTOM_HALF
+static void dma_commit(void *opaque);
+
+long oktag_to_io(long *paddr, long *addr, long len);
+long oktag_from_io(long *addr, long *paddr, long len);
+
+static DECLARE_WORK(tq_fake_dma, dma_commit, NULL);
+
+#define DMA_MAXTRANSFER 0x8000
+
+#else
+
+/*
+ * No bottom half. Use transfer directly from IRQ. Find a narrow path
+ * between too much IRQ overhead and clogging the IRQ for too long.
+ */
+
+#define DMA_MAXTRANSFER 0x1000
+
+#endif
+
+static struct notifier_block oktagon_notifier = {
+ oktagon_notify_reboot,
+ NULL,
+ 0
+};
+
+static long *paddress;
+static long *address;
+static long len;
+static long dma_on;
+static int direction;
+static struct NCR_ESP *current_esp;
+
+
+static volatile unsigned char cmd_buffer[16];
+ /* This is where all commands are put
+ * before they are trasfered to the ESP chip
+ * via PIO.
+ */
+
+/***************************************************************** Detection */
+int oktagon_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct zorro_dev *z = NULL;
+ unsigned long address;
+ struct ESP_regs *eregs;
+
+ while ((z = zorro_find_device(ZORRO_PROD_BSC_OKTAGON_2008, z))) {
+ unsigned long board = z->resource.start;
+ if (request_mem_region(board+OKTAGON_ESP_ADDR,
+ sizeof(struct ESP_regs), "NCR53C9x")) {
+ /*
+ * It is a SCSI controller.
+ * Hardwire Host adapter to SCSI ID 7
+ */
+
+ address = (unsigned long)ZTWO_VADDR(board);
+ eregs = (struct ESP_regs *)(address + OKTAGON_ESP_ADDR);
+
+ /* This line was 5 lines lower */
+ esp = esp_allocate(tpnt, (void *)board+OKTAGON_ESP_ADDR);
+
+ /* we have to shift the registers only one bit for oktagon */
+ esp->shift = 1;
+
+ esp_write(eregs->esp_cfg1, (ESP_CONFIG1_PENABLE | 7));
+ udelay(5);
+ if (esp_read(eregs->esp_cfg1) != (ESP_CONFIG1_PENABLE | 7))
+ return 0; /* Bail out if address did not hold data */
+
+ /* Do command transfer with programmed I/O */
+ esp->do_pio_cmds = 1;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = 0;
+ esp->dma_drain = 0;
+ esp->dma_invalidate = &dma_invalidate;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = &dma_irq_exit;
+ esp->dma_led_on = &dma_led_on;
+ esp->dma_led_off = &dma_led_off;
+ esp->dma_poll = 0;
+ esp->dma_reset = 0;
+
+ esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
+ esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
+ esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one;
+ esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl;
+ esp->dma_advance_sg = &dma_advance_sg;
+
+ /* SCSI chip speed */
+ /* Looking at the quartz of the SCSI board... */
+ esp->cfreq = 25000000;
+
+ /* The DMA registers on the CyberStorm are mapped
+ * relative to the device (i.e. in the same Zorro
+ * I/O block).
+ */
+ esp->dregs = (void *)(address + OKTAGON_DMA_ADDR);
+
+ paddress = (long *) esp->dregs;
+
+ /* ESP register base */
+ esp->eregs = eregs;
+
+ /* Set the command buffer */
+ esp->esp_command = (volatile unsigned char*) cmd_buffer;
+
+ /* Yes, the virtual address. See below. */
+ esp->esp_command_dvma = (__u32) cmd_buffer;
+
+ esp->irq = IRQ_AMIGA_PORTS;
+ request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
+ "BSC Oktagon SCSI", esp->ehost);
+
+ /* Figure out our scsi ID on the bus */
+ esp->scsi_id = 7;
+
+ /* We don't have a differential SCSI-bus. */
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ printk("ESP_Oktagon Driver 1.1"
+#ifdef USE_BOTTOM_HALF
+ " [BOTTOM_HALF]"
+#else
+ " [IRQ]"
+#endif
+ " registered.\n");
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use);
+ esps_running = esps_in_use;
+ current_esp = esp;
+ register_reboot_notifier(&oktagon_notifier);
+ return esps_in_use;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * On certain configurations the SCSI equipment gets confused on reboot,
+ * so we have to reset it then.
+ */
+
+static int
+oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
+{
+ struct NCR_ESP *esp;
+
+ if((code == SYS_DOWN || code == SYS_HALT) && (esp = current_esp))
+ {
+ esp_bootup_reset(esp,esp->eregs);
+ udelay(500); /* Settle time. Maybe unnecessary. */
+ }
+ return NOTIFY_DONE;
+}
+
+
+
+#ifdef USE_BOTTOM_HALF
+
+
+/*
+ * The bsc Oktagon controller has no real DMA, so we have to do the 'DMA
+ * transfer' in the interrupt (Yikes!) or use a bottom half to not to clutter
+ * IRQ's for longer-than-good.
+ *
+ * FIXME
+ * BIG PROBLEM: 'len' is usually the buffer length, not the expected length
+ * of the data. So DMA may finish prematurely, further reads lead to
+ * 'machine check' on APUS systems (don't know about m68k systems, AmigaOS
+ * deliberately ignores the bus faults) and a normal copy-loop can't
+ * be exited prematurely just at the right moment by the dma_invalidate IRQ.
+ * So do it the hard way, write an own copier in assembler and
+ * catch the exception.
+ * -- Carsten
+ */
+
+
+static void dma_commit(void *opaque)
+{
+ long wait,len2,pos;
+ struct NCR_ESP *esp;
+
+ ESPDATA(("Transfer: %ld bytes, Address 0x%08lX, Direction: %d\n",
+ len,(long) address,direction));
+ dma_ints_off(current_esp);
+
+ pos = 0;
+ wait = 1;
+ if(direction) /* write? (memory to device) */
+ {
+ while(len > 0)
+ {
+ len2 = oktag_to_io(paddress, address+pos, len);
+ if(!len2)
+ {
+ if(wait > 1000)
+ {
+ printk("Expedited DMA exit (writing) %ld\n",len);
+ break;
+ }
+ mdelay(wait);
+ wait *= 2;
+ }
+ pos += len2;
+ len -= len2*sizeof(long);
+ }
+ } else {
+ while(len > 0)
+ {
+ len2 = oktag_from_io(address+pos, paddress, len);
+ if(!len2)
+ {
+ if(wait > 1000)
+ {
+ printk("Expedited DMA exit (reading) %ld\n",len);
+ break;
+ }
+ mdelay(wait);
+ wait *= 2;
+ }
+ pos += len2;
+ len -= len2*sizeof(long);
+ }
+ }
+
+ /* to make esp->shift work */
+ esp=current_esp;
+
+#if 0
+ len2 = (esp_read(current_esp->eregs->esp_tclow) & 0xff) |
+ ((esp_read(current_esp->eregs->esp_tcmed) & 0xff) << 8);
+
+ /*
+ * Uh uh. If you see this, len and transfer count registers were out of
+ * sync. That means really serious trouble.
+ */
+
+ if(len2)
+ printk("Eeeek!! Transfer count still %ld!\n",len2);
+#endif
+
+ /*
+ * Normally we just need to exit and wait for the interrupt to come.
+ * But at least one device (my Microtek ScanMaker 630) regularly mis-
+ * calculates the bytes it should send which is really ugly because
+ * it locks up the SCSI bus if not accounted for.
+ */
+
+ if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))
+ {
+ long len = 100;
+ long trash[10];
+
+ /*
+ * Interrupt bit was not set. Either the device is just plain lazy
+ * so we give it a 10 ms chance or...
+ */
+ while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)))
+ udelay(100);
+
+
+ if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))
+ {
+ /*
+ * So we think that the transfer count is out of sync. Since we
+ * have all we want we are happy and can ditch the trash.
+ */
+
+ len = DMA_MAXTRANSFER;
+
+ while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)))
+ oktag_from_io(trash,paddress,2);
+
+ if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))
+ {
+ /*
+ * Things really have gone wrong. If we leave the system in that
+ * state, the SCSI bus is locked forever. I hope that this will
+ * turn the system in a more or less running state.
+ */
+ printk("Device is bolixed, trying bus reset...\n");
+ esp_bootup_reset(current_esp,current_esp->eregs);
+ }
+ }
+ }
+
+ ESPDATA(("Transfer_finale: do_data_finale should come\n"));
+
+ len = 0;
+ dma_on = 0;
+ dma_ints_on(current_esp);
+}
+
+#endif
+
+/************************************************************* DMA Functions */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ /* Since the CyberStorm DMA is fully dedicated to the ESP chip,
+ * the number of bytes sent (to the ESP chip) equals the number
+ * of bytes in the FIFO - there is no buffering in the DMA controller.
+ * XXXX Do I read this right? It is from host to ESP, right?
+ */
+ return fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ unsigned long sz = sp->SCp.this_residual;
+ if(sz > DMA_MAXTRANSFER)
+ sz = DMA_MAXTRANSFER;
+ return sz;
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+}
+
+/*
+ * What the f$@& is this?
+ *
+ * Some SCSI devices (like my Microtek ScanMaker 630 scanner) want to transfer
+ * more data than requested. How much? Dunno. So ditch the bogus data into
+ * the sink, hoping the device will advance to the next phase sooner or later.
+ *
+ * -- Carsten
+ */
+
+static long oktag_eva_buffer[16]; /* The data sink */
+
+static void oktag_check_dma(void)
+{
+ struct NCR_ESP *esp;
+
+ esp=current_esp;
+ if(!len)
+ {
+ address = oktag_eva_buffer;
+ len = 2;
+ /* esp_do_data sets them to zero like len */
+ esp_write(current_esp->eregs->esp_tclow,2);
+ esp_write(current_esp->eregs->esp_tcmed,0);
+ }
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+ /* Zorro is noncached, everything else done using processor. */
+ /* cache_clear(addr, length); */
+
+ if(dma_on)
+ panic("dma_init_read while dma process is initialized/running!\n");
+ direction = 0;
+ address = (long *) vaddress;
+ current_esp = esp;
+ len = length;
+ oktag_check_dma();
+ dma_on = 1;
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+ /* cache_push(addr, length); */
+
+ if(dma_on)
+ panic("dma_init_write while dma process is initialized/running!\n");
+ direction = 1;
+ address = (long *) vaddress;
+ current_esp = esp;
+ len = length;
+ oktag_check_dma();
+ dma_on = 1;
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ disable_irq(esp->irq);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ enable_irq(esp->irq);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ /* It's important to check the DMA IRQ bit in the correct way! */
+ return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR);
+}
+
+static void dma_led_off(struct NCR_ESP *esp)
+{
+}
+
+static void dma_led_on(struct NCR_ESP *esp)
+{
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return ((custom.intenar) & IF_PORTS);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ /* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+ * so when (write) is true, it actually means READ!
+ */
+ if(write){
+ dma_init_read(esp, addr, count);
+ } else {
+ dma_init_write(esp, addr, count);
+ }
+}
+
+/*
+ * IRQ entry when DMA transfer is ready to be started
+ */
+
+static void dma_irq_exit(struct NCR_ESP *esp)
+{
+#ifdef USE_BOTTOM_HALF
+ if(dma_on)
+ {
+ schedule_work(&tq_fake_dma);
+ }
+#else
+ while(len && !dma_irq_p(esp))
+ {
+ if(direction)
+ *paddress = *address++;
+ else
+ *address++ = *paddress;
+ len -= (sizeof(long));
+ }
+ len = 0;
+ dma_on = 0;
+#endif
+}
+
+/*
+ * IRQ entry when DMA has just finished
+ */
+
+static void dma_invalidate(struct NCR_ESP *esp)
+{
+}
+
+/*
+ * Since the processor does the data transfer we have to use the custom
+ * mmu interface to pass the virtual address, not the physical.
+ */
+
+void dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ sp->SCp.ptr =
+ sp->request_buffer;
+}
+
+void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ sp->SCp.ptr = page_address(sp->SCp.buffer->page)+
+ sp->SCp.buffer->offset;
+}
+
+void dma_mmu_release_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+}
+
+void dma_mmu_release_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+}
+
+void dma_advance_sg(Scsi_Cmnd *sp)
+{
+ sp->SCp.ptr = page_address(sp->SCp.buffer->page)+
+ sp->SCp.buffer->offset;
+}
+
+
+#define HOSTS_C
+
+int oktagon_esp_release(struct Scsi_Host *instance)
+{
+#ifdef MODULE
+ unsigned long address = (unsigned long)((struct NCR_ESP *)instance->hostdata)->edev;
+ esp_release();
+ release_mem_region(address, sizeof(struct ESP_regs));
+ free_irq(IRQ_AMIGA_PORTS, esp_intr);
+ unregister_reboot_notifier(&oktagon_notifier);
+#endif
+ return 1;
+}
+
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "esp-oktagon",
+ .proc_info = &esp_proc_info,
+ .name = "BSC Oktagon SCSI",
+ .detect = oktagon_esp_detect,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .release = oktagon_esp_release,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/oktagon_io.S b/drivers/scsi/oktagon_io.S
new file mode 100644
index 000000000000..08ce8d80d8f5
--- /dev/null
+++ b/drivers/scsi/oktagon_io.S
@@ -0,0 +1,195 @@
+/* -*- mode: asm -*-
+ * Due to problems while transferring data I've put these routines as assembly
+ * code.
+ * Since I'm no PPC assembler guru, the code is just the assembler version of
+
+int oktag_to_io(long *paddr,long *addr,long len)
+{
+ long *addr2 = addr;
+ for(len=(len+sizeof(long)-1)/sizeof(long);len--;)
+ *paddr = *addr2++;
+ return addr2 - addr;
+}
+
+int oktag_from_io(long *addr,long *paddr,long len)
+{
+ long *addr2 = addr;
+ for(len=(len+sizeof(long)-1)/sizeof(long);len--;)
+ *addr2++ = *paddr;
+ return addr2 - addr;
+}
+
+ * assembled using gcc -O2 -S, with two exception catch points where data
+ * is moved to/from the IO register.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_APUS
+
+ .file "oktagon_io.c"
+
+gcc2_compiled.:
+/*
+ .section ".text"
+*/
+ .align 2
+ .globl oktag_to_io
+ .type oktag_to_io,@function
+oktag_to_io:
+ addi 5,5,3
+ srwi 5,5,2
+ cmpwi 1,5,0
+ mr 9,3
+ mr 3,4
+ addi 5,5,-1
+ bc 12,6,.L3
+.L5:
+ cmpwi 1,5,0
+ lwz 0,0(3)
+ addi 3,3,4
+ addi 5,5,-1
+exp1: stw 0,0(9)
+ bc 4,6,.L5
+.L3:
+ret1: subf 3,4,3
+ srawi 3,3,2
+ blr
+.Lfe1:
+ .size oktag_to_io,.Lfe1-oktag_to_io
+ .align 2
+ .globl oktag_from_io
+ .type oktag_from_io,@function
+oktag_from_io:
+ addi 5,5,3
+ srwi 5,5,2
+ cmpwi 1,5,0
+ mr 9,3
+ addi 5,5,-1
+ bc 12,6,.L9
+.L11:
+ cmpwi 1,5,0
+exp2: lwz 0,0(4)
+ addi 5,5,-1
+ stw 0,0(3)
+ addi 3,3,4
+ bc 4,6,.L11
+.L9:
+ret2: subf 3,9,3
+ srawi 3,3,2
+ blr
+.Lfe2:
+ .size oktag_from_io,.Lfe2-oktag_from_io
+ .ident "GCC: (GNU) egcs-2.90.29 980515 (egcs-1.0.3 release)"
+
+/*
+ * Exception table.
+ * Second longword shows where to jump when an exception at the addr the first
+ * longword is pointing to is caught.
+ */
+
+.section __ex_table,"a"
+ .align 2
+oktagon_except:
+ .long exp1,ret1
+ .long exp2,ret2
+
+#else
+
+/*
+The code which follows is for 680x0 based assembler and is meant for
+Linux/m68k. It was created by cross compiling the code using the
+instructions given above. I then added the four labels used in the
+exception handler table at the bottom of this file.
+- Kevin <kcozens@interlog.com>
+*/
+
+#ifdef CONFIG_AMIGA
+
+ .file "oktagon_io.c"
+ .version "01.01"
+gcc2_compiled.:
+.text
+ .align 2
+.globl oktag_to_io
+ .type oktag_to_io,@function
+oktag_to_io:
+ link.w %a6,#0
+ move.l %d2,-(%sp)
+ move.l 8(%a6),%a1
+ move.l 12(%a6),%d1
+ move.l %d1,%a0
+ move.l 16(%a6),%d0
+ addq.l #3,%d0
+ lsr.l #2,%d0
+ subq.l #1,%d0
+ moveq.l #-1,%d2
+ cmp.l %d0,%d2
+ jbeq .L3
+.L5:
+exp1:
+ move.l (%a0)+,(%a1)
+ dbra %d0,.L5
+ clr.w %d0
+ subq.l #1,%d0
+ jbcc .L5
+.L3:
+ret1:
+ move.l %a0,%d0
+ sub.l %d1,%d0
+ asr.l #2,%d0
+ move.l -4(%a6),%d2
+ unlk %a6
+ rts
+
+.Lfe1:
+ .size oktag_to_io,.Lfe1-oktag_to_io
+ .align 2
+.globl oktag_from_io
+ .type oktag_from_io,@function
+oktag_from_io:
+ link.w %a6,#0
+ move.l %d2,-(%sp)
+ move.l 8(%a6),%d1
+ move.l 12(%a6),%a1
+ move.l %d1,%a0
+ move.l 16(%a6),%d0
+ addq.l #3,%d0
+ lsr.l #2,%d0
+ subq.l #1,%d0
+ moveq.l #-1,%d2
+ cmp.l %d0,%d2
+ jbeq .L9
+.L11:
+exp2:
+ move.l (%a1),(%a0)+
+ dbra %d0,.L11
+ clr.w %d0
+ subq.l #1,%d0
+ jbcc .L11
+.L9:
+ret2:
+ move.l %a0,%d0
+ sub.l %d1,%d0
+ asr.l #2,%d0
+ move.l -4(%a6),%d2
+ unlk %a6
+ rts
+.Lfe2:
+ .size oktag_from_io,.Lfe2-oktag_from_io
+ .ident "GCC: (GNU) 2.7.2.1"
+
+/*
+ * Exception table.
+ * Second longword shows where to jump when an exception at the addr the first
+ * longword is pointing to is caught.
+ */
+
+.section __ex_table,"a"
+ .align 2
+oktagon_except:
+ .long exp1,ret1
+ .long exp2,ret2
+
+#endif
+#endif
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
new file mode 100644
index 000000000000..c585c7bef247
--- /dev/null
+++ b/drivers/scsi/osst.c
@@ -0,0 +1,5914 @@
+/*
+ SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+ file Documentation/scsi/st.txt for more information.
+
+ History:
+
+ OnStream SCSI Tape support (osst) cloned from st.c by
+ Willem Riede (osst@riede.org) Feb 2000
+ Fixes ... Kurt Garloff <garloff@suse.de> Mar 2000
+
+ Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
+ Contribution and ideas from several people including (in alphabetical
+ order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
+ Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+
+ Copyright 1992 - 2002 Kai Makisara / 2000 - 2004 Willem Riede
+ email osst@riede.org
+
+ $Header: /cvsroot/osst/Driver/osst.c,v 1.73 2005/01/01 21:13:34 wriede Exp $
+
+ Microscopic alterations - Rik Ling, 2000/12/21
+ Last st.c sync: Tue Oct 15 22:01:04 2002 by makisara
+ Some small formal changes - aeb, 950809
+*/
+
+static const char * cvsid = "$Id: osst.c,v 1.73 2005/01/01 21:13:34 wriede Exp $";
+static const char * osst_version = "0.99.3";
+
+/* The "failure to reconnect" firmware bug */
+#define OSST_FW_NEED_POLL_MIN 10601 /*(107A)*/
+#define OSST_FW_NEED_POLL_MAX 10704 /*(108D)*/
+#define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7)
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/moduleparam.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+
+/* The driver prints some debugging information on the console if DEBUG
+ is defined and non-zero. */
+#define DEBUG 0
+
+/* The message level for the debug messages is currently set to KERN_NOTICE
+ so that people can easily see the messages. Later when the debugging messages
+ in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
+#define OSST_DEB_MSG KERN_NOTICE
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
+
+#define ST_KILOBYTE 1024
+
+#include "st.h"
+#include "osst.h"
+#include "osst_options.h"
+#include "osst_detect.h"
+
+static int max_dev = 0;
+static int write_threshold_kbs = 0;
+static int max_sg_segs = 0;
+
+#ifdef MODULE
+MODULE_AUTHOR("Willem Riede");
+MODULE_DESCRIPTION("OnStream {DI-|FW-|SC-|USB}{30|50} Tape Driver");
+MODULE_LICENSE("GPL");
+
+module_param(max_dev, int, 0444);
+MODULE_PARM_DESC(max_dev, "Maximum number of OnStream Tape Drives to attach (4)");
+
+module_param(write_threshold_kbs, int, 0644);
+MODULE_PARM_DESC(write_threshold_kbs, "Asynchronous write threshold (KB; 32)");
+
+module_param(max_sg_segs, int, 0644);
+MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (9)");
+#else
+static struct osst_dev_parm {
+ char *name;
+ int *val;
+} parms[] __initdata = {
+ { "max_dev", &max_dev },
+ { "write_threshold_kbs", &write_threshold_kbs },
+ { "max_sg_segs", &max_sg_segs }
+};
+#endif
+
+static char *osst_formats[ST_NBR_MODES] ={"", "l", "m", "a"};
+
+/* Some default definitions have been moved to osst_options.h */
+#define OSST_BUFFER_SIZE (OSST_BUFFER_BLOCKS * ST_KILOBYTE)
+#define OSST_WRITE_THRESHOLD (OSST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE)
+
+/* The buffer size should fit into the 24 bits for length in the
+ 6-byte SCSI read and write commands. */
+#if OSST_BUFFER_SIZE >= (2 << 24 - 1)
+#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
+#endif
+
+#if DEBUG
+static int debugging = 1;
+/* uncomment define below to test error recovery */
+// #define OSST_INJECT_ERRORS 1
+#endif
+
+/* Do not retry! The drive firmware already retries when appropriate,
+ and when it tries to tell us something, we had better listen... */
+#define MAX_RETRIES 0
+
+#define NO_TAPE NOT_READY
+
+#define OSST_WAIT_POSITION_COMPLETE (HZ > 200 ? HZ / 200 : 1)
+#define OSST_WAIT_WRITE_COMPLETE (HZ / 12)
+#define OSST_WAIT_LONG_WRITE_COMPLETE (HZ / 2)
+
+#define OSST_TIMEOUT (200 * HZ)
+#define OSST_LONG_TIMEOUT (1800 * HZ)
+
+#define TAPE_NR(x) (iminor(x) & ~(-1 << ST_MODE_SHIFT))
+#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
+#define TAPE_REWIND(x) ((iminor(x) & 0x80) == 0)
+#define TAPE_IS_RAW(x) (TAPE_MODE(x) & (ST_NBR_MODES >> 1))
+
+/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
+ 24 bits) */
+#define SET_DENS_AND_BLK 0x10001
+
+static int osst_buffer_size = OSST_BUFFER_SIZE;
+static int osst_write_threshold = OSST_WRITE_THRESHOLD;
+static int osst_max_sg_segs = OSST_MAX_SG;
+static int osst_max_dev = OSST_MAX_TAPES;
+static int osst_nr_dev;
+
+static struct osst_tape **os_scsi_tapes = NULL;
+static DEFINE_RWLOCK(os_scsi_tapes_lock);
+
+static int modes_defined = 0;
+
+static struct osst_buffer *new_tape_buffer(int, int, int);
+static int enlarge_buffer(struct osst_buffer *, int);
+static void normalize_buffer(struct osst_buffer *);
+static int append_to_buffer(const char __user *, struct osst_buffer *, int);
+static int from_buffer(struct osst_buffer *, char __user *, int);
+static int osst_zero_buffer_tail(struct osst_buffer *);
+static int osst_copy_to_buffer(struct osst_buffer *, unsigned char *);
+static int osst_copy_from_buffer(struct osst_buffer *, unsigned char *);
+
+static int osst_probe(struct device *);
+static int osst_remove(struct device *);
+
+static struct scsi_driver osst_template = {
+ .owner = THIS_MODULE,
+ .gendrv = {
+ .name = "osst",
+ .probe = osst_probe,
+ .remove = osst_remove,
+ }
+};
+
+static int osst_int_ioctl(struct osst_tape *STp, struct scsi_request ** aSRpnt,
+ unsigned int cmd_in, unsigned long arg);
+
+static int osst_set_frame_position(struct osst_tape *STp, struct scsi_request ** aSRpnt, int frame, int skip);
+
+static int osst_get_frame_position(struct osst_tape *STp, struct scsi_request ** aSRpnt);
+
+static int osst_flush_write_buffer(struct osst_tape *STp, struct scsi_request ** aSRpnt);
+
+static int osst_write_error_recovery(struct osst_tape * STp, struct scsi_request ** aSRpnt, int pending);
+
+static inline char *tape_name(struct osst_tape *tape)
+{
+ return tape->drive->disk_name;
+}
+
+/* Routines that handle the interaction with mid-layer SCSI routines */
+
+/* Convert the result to success code */
+static int osst_chk_result(struct osst_tape * STp, struct scsi_request * SRpnt)
+{
+ char *name = tape_name(STp);
+ int result = SRpnt->sr_result;
+ unsigned char * sense = SRpnt->sr_sense_buffer, scode;
+#if DEBUG
+ const char *stp;
+#endif
+
+ if (!result) {
+ sense[0] = 0; /* We don't have sense data if this byte is zero */
+ return 0;
+ }
+ if ((driver_byte(result) & DRIVER_MASK) == DRIVER_SENSE)
+ scode = sense[2] & 0x0f;
+ else {
+ sense[0] = 0; /* We don't have sense data if this byte is zero */
+ scode = 0;
+ }
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
+ name, result,
+ SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2],
+ SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5],
+ SRpnt->sr_bufflen);
+ if (scode) printk(OSST_DEB_MSG "%s:D: Sense: %02x, ASC: %02x, ASCQ: %02x\n",
+ name, scode, sense[12], sense[13]);
+ if (driver_byte(result) & DRIVER_SENSE)
+ scsi_print_req_sense("osst ", SRpnt);
+ }
+ else
+#endif
+ if (!(driver_byte(result) & DRIVER_SENSE) ||
+ ((sense[0] & 0x70) == 0x70 &&
+ scode != NO_SENSE &&
+ scode != RECOVERED_ERROR &&
+/* scode != UNIT_ATTENTION && */
+ scode != BLANK_CHECK &&
+ scode != VOLUME_OVERFLOW &&
+ SRpnt->sr_cmnd[0] != MODE_SENSE &&
+ SRpnt->sr_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
+ if (driver_byte(result) & DRIVER_SENSE) {
+ printk(KERN_WARNING "%s:W: Command with sense data:\n", name);
+ scsi_print_req_sense("osst:", SRpnt);
+ }
+ else {
+ static int notyetprinted = 1;
+
+ printk(KERN_WARNING
+ "%s:W: Warning %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
+ name, result, suggestion(result), driver_byte(result) & DRIVER_MASK,
+ host_byte(result));
+ if (notyetprinted) {
+ notyetprinted = 0;
+ printk(KERN_INFO
+ "%s:I: This warning may be caused by your scsi controller,\n", name);
+ printk(KERN_INFO
+ "%s:I: it has been reported with some Buslogic cards.\n", name);
+ }
+ }
+ }
+ STp->pos_unknown |= STp->device->was_reset;
+
+ if ((sense[0] & 0x70) == 0x70 &&
+ scode == RECOVERED_ERROR) {
+ STp->recover_count++;
+ STp->recover_erreg++;
+#if DEBUG
+ if (debugging) {
+ if (SRpnt->sr_cmnd[0] == READ_6)
+ stp = "read";
+ else if (SRpnt->sr_cmnd[0] == WRITE_6)
+ stp = "write";
+ else
+ stp = "ioctl";
+ printk(OSST_DEB_MSG "%s:D: Recovered %s error (%d).\n", name, stp,
+ STp->recover_count);
+ }
+#endif
+ if ((sense[2] & 0xe0) == 0)
+ return 0;
+ }
+ return (-EIO);
+}
+
+
+/* Wakeup from interrupt */
+static void osst_sleep_done (struct scsi_cmnd * SCpnt)
+{
+ struct osst_tape * STp = container_of(SCpnt->request->rq_disk->private_data, struct osst_tape, driver);
+
+ if ((STp->buffer)->writing &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x40)) {
+ /* EOM at write-behind, has all been written? */
+ if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW)
+ STp->buffer->midlevel_result = SCpnt->result; /* Error */
+ else
+ STp->buffer->midlevel_result = INT_MAX; /* OK */
+ }
+ else
+ STp->buffer->midlevel_result = SCpnt->result;
+ SCpnt->request->rq_status = RQ_SCSI_DONE;
+ STp->buffer->last_SRpnt = SCpnt->sc_request;
+
+#if DEBUG
+ STp->write_pending = 0;
+#endif
+ complete(SCpnt->request->waiting);
+}
+
+
+/* Do the scsi command. Waits until command performed if do_wait is true.
+ Otherwise osst_write_behind_check() is used to check that the command
+ has finished. */
+static struct scsi_request * osst_do_scsi(struct scsi_request *SRpnt, struct osst_tape *STp,
+ unsigned char *cmd, int bytes, int direction, int timeout, int retries, int do_wait)
+{
+ unsigned char *bp;
+#ifdef OSST_INJECT_ERRORS
+ static int inject = 0;
+ static int repeat = 0;
+#endif
+ if (SRpnt == NULL) {
+ if ((SRpnt = scsi_allocate_request(STp->device, GFP_ATOMIC)) == NULL) {
+ printk(KERN_ERR "%s:E: Can't get SCSI request.\n", tape_name(STp));
+ if (signal_pending(current))
+ (STp->buffer)->syscall_result = (-EINTR);
+ else
+ (STp->buffer)->syscall_result = (-EBUSY);
+ return NULL;
+ }
+ }
+
+ init_completion(&STp->wait);
+ SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+ (STp->buffer)->use_sg : 0;
+ if (SRpnt->sr_use_sg) {
+ bp = (char *)&(STp->buffer->sg[0]);
+ if (STp->buffer->sg_segs < SRpnt->sr_use_sg)
+ SRpnt->sr_use_sg = STp->buffer->sg_segs;
+ }
+ else
+ bp = (STp->buffer)->b_data;
+ SRpnt->sr_data_direction = direction;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_request->waiting = &(STp->wait);
+ SRpnt->sr_request->rq_status = RQ_SCSI_BUSY;
+ SRpnt->sr_request->rq_disk = STp->drive;
+
+ scsi_do_req(SRpnt, (void *)cmd, bp, bytes, osst_sleep_done, timeout, retries);
+
+ if (do_wait) {
+ wait_for_completion(SRpnt->sr_request->waiting);
+ SRpnt->sr_request->waiting = NULL;
+ STp->buffer->syscall_result = osst_chk_result(STp, SRpnt);
+#ifdef OSST_INJECT_ERRORS
+ if (STp->buffer->syscall_result == 0 &&
+ cmd[0] == READ_6 &&
+ cmd[4] &&
+ ( (++ inject % 83) == 29 ||
+ (STp->first_frame_position == 240
+ /* or STp->read_error_frame to fail again on the block calculated above */ &&
+ ++repeat < 3))) {
+ printk(OSST_DEB_MSG "%s:D: Injecting read error\n", tape_name(STp));
+ STp->buffer->last_result_fatal = 1;
+ }
+#endif
+ }
+ return SRpnt;
+}
+
+
+/* Handle the write-behind checking (downs the semaphore) */
+static void osst_write_behind_check(struct osst_tape *STp)
+{
+ struct osst_buffer * STbuffer;
+
+ STbuffer = STp->buffer;
+
+#if DEBUG
+ if (STp->write_pending)
+ STp->nbr_waits++;
+ else
+ STp->nbr_finished++;
+#endif
+ wait_for_completion(&(STp->wait));
+ (STp->buffer)->last_SRpnt->sr_request->waiting = NULL;
+
+ STp->buffer->syscall_result = osst_chk_result(STp, STp->buffer->last_SRpnt);
+
+ if ((STp->buffer)->syscall_result)
+ (STp->buffer)->syscall_result =
+ osst_write_error_recovery(STp, &((STp->buffer)->last_SRpnt), 1);
+ else
+ STp->first_frame_position++;
+
+ scsi_release_request((STp->buffer)->last_SRpnt);
+
+ if (STbuffer->writing < STbuffer->buffer_bytes)
+ printk(KERN_WARNING "osst :A: write_behind_check: something left in buffer!\n");
+
+ STbuffer->buffer_bytes -= STbuffer->writing;
+ STbuffer->writing = 0;
+
+ return;
+}
+
+
+
+/* Onstream specific Routines */
+/*
+ * Initialize the OnStream AUX
+ */
+static void osst_init_aux(struct osst_tape * STp, int frame_type, int frame_seq_number,
+ int logical_blk_num, int blk_sz, int blk_cnt)
+{
+ os_aux_t *aux = STp->buffer->aux;
+ os_partition_t *par = &aux->partition;
+ os_dat_t *dat = &aux->dat;
+
+ if (STp->raw) return;
+
+ memset(aux, 0, sizeof(*aux));
+ aux->format_id = htonl(0);
+ memcpy(aux->application_sig, "LIN4", 4);
+ aux->hdwr = htonl(0);
+ aux->frame_type = frame_type;
+
+ switch (frame_type) {
+ case OS_FRAME_TYPE_HEADER:
+ aux->update_frame_cntr = htonl(STp->update_frame_cntr);
+ par->partition_num = OS_CONFIG_PARTITION;
+ par->par_desc_ver = OS_PARTITION_VERSION;
+ par->wrt_pass_cntr = htons(0xffff);
+ /* 0-4 = reserved, 5-9 = header, 2990-2994 = header, 2995-2999 = reserved */
+ par->first_frame_ppos = htonl(0);
+ par->last_frame_ppos = htonl(0xbb7);
+ aux->frame_seq_num = htonl(0);
+ aux->logical_blk_num_high = htonl(0);
+ aux->logical_blk_num = htonl(0);
+ aux->next_mark_ppos = htonl(STp->first_mark_ppos);
+ break;
+ case OS_FRAME_TYPE_DATA:
+ case OS_FRAME_TYPE_MARKER:
+ dat->dat_sz = 8;
+ dat->reserved1 = 0;
+ dat->entry_cnt = 1;
+ dat->reserved3 = 0;
+ dat->dat_list[0].blk_sz = htonl(blk_sz);
+ dat->dat_list[0].blk_cnt = htons(blk_cnt);
+ dat->dat_list[0].flags = frame_type==OS_FRAME_TYPE_MARKER?
+ OS_DAT_FLAGS_MARK:OS_DAT_FLAGS_DATA;
+ dat->dat_list[0].reserved = 0;
+ case OS_FRAME_TYPE_EOD:
+ aux->update_frame_cntr = htonl(0);
+ par->partition_num = OS_DATA_PARTITION;
+ par->par_desc_ver = OS_PARTITION_VERSION;
+ par->wrt_pass_cntr = htons(STp->wrt_pass_cntr);
+ par->first_frame_ppos = htonl(STp->first_data_ppos);
+ par->last_frame_ppos = htonl(STp->capacity);
+ aux->frame_seq_num = htonl(frame_seq_number);
+ aux->logical_blk_num_high = htonl(0);
+ aux->logical_blk_num = htonl(logical_blk_num);
+ break;
+ default: ; /* probably FILL */
+ }
+ aux->filemark_cnt = ntohl(STp->filemark_cnt);
+ aux->phys_fm = ntohl(0xffffffff);
+ aux->last_mark_ppos = ntohl(STp->last_mark_ppos);
+ aux->last_mark_lbn = ntohl(STp->last_mark_lbn);
+}
+
+/*
+ * Verify that we have the correct tape frame
+ */
+static int osst_verify_frame(struct osst_tape * STp, int frame_seq_number, int quiet)
+{
+ char * name = tape_name(STp);
+ os_aux_t * aux = STp->buffer->aux;
+ os_partition_t * par = &(aux->partition);
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ int blk_cnt, blk_sz, i;
+
+ if (STp->raw) {
+ if (STp->buffer->syscall_result) {
+ for (i=0; i < STp->buffer->sg_segs; i++)
+ memset(page_address(STp->buffer->sg[i].page),
+ 0, STp->buffer->sg[i].length);
+ strcpy(STp->buffer->b_data, "READ ERROR ON FRAME");
+ } else
+ STp->buffer->buffer_bytes = OS_FRAME_SIZE;
+ return 1;
+ }
+ if (STp->buffer->syscall_result) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, read error\n", name);
+#endif
+ return 0;
+ }
+ if (ntohl(aux->format_id) != 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, format_id %u\n", name, ntohl(aux->format_id));
+#endif
+ goto err_out;
+ }
+ if (memcmp(aux->application_sig, STp->application_sig, 4) != 0 &&
+ (memcmp(aux->application_sig, "LIN3", 4) != 0 || STp->linux_media_version != 4)) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, incorrect application signature\n", name);
+#endif
+ goto err_out;
+ }
+ if (par->partition_num != OS_DATA_PARTITION) {
+ if (!STp->linux_media || STp->linux_media_version != 2) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, partition num %d\n",
+ name, par->partition_num);
+#endif
+ goto err_out;
+ }
+ }
+ if (par->par_desc_ver != OS_PARTITION_VERSION) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, partition version %d\n", name, par->par_desc_ver);
+#endif
+ goto err_out;
+ }
+ if (ntohs(par->wrt_pass_cntr) != STp->wrt_pass_cntr) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, wrt_pass_cntr %d (expected %d)\n",
+ name, ntohs(par->wrt_pass_cntr), STp->wrt_pass_cntr);
+#endif
+ goto err_out;
+ }
+ if (aux->frame_type != OS_FRAME_TYPE_DATA &&
+ aux->frame_type != OS_FRAME_TYPE_EOD &&
+ aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ if (!quiet)
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, frame type %x\n", name, aux->frame_type);
+#endif
+ goto err_out;
+ }
+ if (aux->frame_type == OS_FRAME_TYPE_EOD &&
+ STp->first_frame_position < STp->eod_frame_ppos) {
+ printk(KERN_INFO "%s:I: Skipping premature EOD frame %d\n", name,
+ STp->first_frame_position);
+ goto err_out;
+ }
+ if (frame_seq_number != -1 && ntohl(aux->frame_seq_num) != frame_seq_number) {
+ if (!quiet)
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame, sequence number %u (expected %d)\n",
+ name, ntohl(aux->frame_seq_num), frame_seq_number);
+#endif
+ goto err_out;
+ }
+ if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
+ STps->eof = ST_FM_HIT;
+
+ i = ntohl(aux->filemark_cnt);
+ if (STp->header_cache != NULL && i < OS_FM_TAB_MAX && (i > STp->filemark_cnt ||
+ STp->first_frame_position - 1 != ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i]))) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: %s filemark %d at frame pos %d\n", name,
+ STp->header_cache->dat_fm_tab.fm_tab_ent[i] == 0?"Learned":"Corrected",
+ i, STp->first_frame_position - 1);
+#endif
+ STp->header_cache->dat_fm_tab.fm_tab_ent[i] = htonl(STp->first_frame_position - 1);
+ if (i >= STp->filemark_cnt)
+ STp->filemark_cnt = i+1;
+ }
+ }
+ if (aux->frame_type == OS_FRAME_TYPE_EOD) {
+ STps->eof = ST_EOD_1;
+ STp->frame_in_buffer = 1;
+ }
+ if (aux->frame_type == OS_FRAME_TYPE_DATA) {
+ blk_cnt = ntohs(aux->dat.dat_list[0].blk_cnt);
+ blk_sz = ntohl(aux->dat.dat_list[0].blk_sz);
+ STp->buffer->buffer_bytes = blk_cnt * blk_sz;
+ STp->buffer->read_pointer = 0;
+ STp->frame_in_buffer = 1;
+
+ /* See what block size was used to write file */
+ if (STp->block_size != blk_sz && blk_sz > 0) {
+ printk(KERN_INFO
+ "%s:I: File was written with block size %d%c, currently %d%c, adjusted to match.\n",
+ name, blk_sz<1024?blk_sz:blk_sz/1024,blk_sz<1024?'b':'k',
+ STp->block_size<1024?STp->block_size:STp->block_size/1024,
+ STp->block_size<1024?'b':'k');
+ STp->block_size = blk_sz;
+ STp->buffer->buffer_blocks = OS_DATA_SIZE / blk_sz;
+ }
+ STps->eof = ST_NOEOF;
+ }
+ STp->frame_seq_number = ntohl(aux->frame_seq_num);
+ STp->logical_blk_num = ntohl(aux->logical_blk_num);
+ return 1;
+
+err_out:
+ if (STp->read_error_frame == 0)
+ STp->read_error_frame = STp->first_frame_position - 1;
+ return 0;
+}
+
+/*
+ * Wait for the unit to become Ready
+ */
+static int osst_wait_ready(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ unsigned timeout, int initial_delay)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ unsigned long startwait = jiffies;
+#if DEBUG
+ int dbg = debugging;
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG "%s:D: Reached onstream wait ready\n", name);
+#endif
+
+ if (initial_delay > 0)
+ msleep(jiffies_to_msecs(initial_delay));
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+ if (!SRpnt) return (-EBUSY);
+
+ while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) &&
+ (( SRpnt->sr_sense_buffer[2] == 2 && SRpnt->sr_sense_buffer[12] == 4 &&
+ (SRpnt->sr_sense_buffer[13] == 1 || SRpnt->sr_sense_buffer[13] == 8) ) ||
+ ( SRpnt->sr_sense_buffer[2] == 6 && SRpnt->sr_sense_buffer[12] == 0x28 &&
+ SRpnt->sr_sense_buffer[13] == 0 ) )) {
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: Sleeping in onstream wait ready\n", name);
+ printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
+ debugging = 0;
+ }
+#endif
+ msleep(100);
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ }
+ *aSRpnt = SRpnt;
+#if DEBUG
+ debugging = dbg;
+#endif
+ if ( STp->buffer->syscall_result &&
+ osst_write_error_recovery(STp, aSRpnt, 0) ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Abnormal exit from onstream wait ready\n", name);
+ printk(OSST_DEB_MSG "%s:D: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", name,
+ STp->buffer->syscall_result, SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[2],
+ SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+#endif
+ return (-EIO);
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Normal exit from onstream wait ready\n", name);
+#endif
+ return 0;
+}
+
+/*
+ * Wait for a tape to be inserted in the unit
+ */
+static int osst_wait_for_medium(struct osst_tape * STp, struct scsi_request ** aSRpnt, unsigned timeout)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ unsigned long startwait = jiffies;
+#if DEBUG
+ int dbg = debugging;
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG "%s:D: Reached onstream wait for medium\n", name);
+#endif
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+ if (!SRpnt) return (-EBUSY);
+
+ while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) &&
+ SRpnt->sr_sense_buffer[2] == 2 && SRpnt->sr_sense_buffer[12] == 0x3a &&
+ SRpnt->sr_sense_buffer[13] == 0 ) {
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: Sleeping in onstream wait medium\n", name);
+ printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
+ debugging = 0;
+ }
+#endif
+ msleep(100);
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ }
+ *aSRpnt = SRpnt;
+#if DEBUG
+ debugging = dbg;
+#endif
+ if ( STp->buffer->syscall_result && SRpnt->sr_sense_buffer[2] != 2 &&
+ SRpnt->sr_sense_buffer[12] != 4 && SRpnt->sr_sense_buffer[13] == 1) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Abnormal exit from onstream wait medium\n", name);
+ printk(OSST_DEB_MSG "%s:D: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", name,
+ STp->buffer->syscall_result, SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[2],
+ SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+#endif
+ return 0;
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Normal exit from onstream wait medium\n", name);
+#endif
+ return 1;
+}
+
+static int osst_position_tape_and_confirm(struct osst_tape * STp, struct scsi_request ** aSRpnt, int frame)
+{
+ int retval;
+
+ osst_wait_ready(STp, aSRpnt, 15 * 60, 0); /* TODO - can this catch a write error? */
+ retval = osst_set_frame_position(STp, aSRpnt, frame, 0);
+ if (retval) return (retval);
+ osst_wait_ready(STp, aSRpnt, 15 * 60, OSST_WAIT_POSITION_COMPLETE);
+ return (osst_get_frame_position(STp, aSRpnt));
+}
+
+/*
+ * Wait for write(s) to complete
+ */
+static int osst_flush_drive_buffer(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ int result = 0;
+ int delay = OSST_WAIT_WRITE_COMPLETE;
+#if DEBUG
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG "%s:D: Reached onstream flush drive buffer (write filemark)\n", name);
+#endif
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_FILEMARKS;
+ cmd[1] = 1;
+
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+ if (!SRpnt) return (-EBUSY);
+ if (STp->buffer->syscall_result) {
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == 2 && SRpnt->sr_sense_buffer[12] == 4) {
+ if (SRpnt->sr_sense_buffer[13] == 8) {
+ delay = OSST_WAIT_LONG_WRITE_COMPLETE;
+ }
+ } else
+ result = osst_write_error_recovery(STp, aSRpnt, 0);
+ }
+ result |= osst_wait_ready(STp, aSRpnt, 5 * 60, delay);
+ STp->ps[STp->partition].rw = OS_WRITING_COMPLETE;
+
+ return (result);
+}
+
+#define OSST_POLL_PER_SEC 10
+static int osst_wait_frame(struct osst_tape * STp, struct scsi_request ** aSRpnt, int curr, int minlast, int to)
+{
+ unsigned long startwait = jiffies;
+ char * name = tape_name(STp);
+#if DEBUG
+ char notyetprinted = 1;
+#endif
+ if (minlast >= 0 && STp->ps[STp->partition].rw != ST_READING)
+ printk(KERN_ERR "%s:A: Waiting for frame without having initialized read!\n", name);
+
+ while (time_before (jiffies, startwait + to*HZ))
+ {
+ int result;
+ result = osst_get_frame_position(STp, aSRpnt);
+ if (result == -EIO)
+ if ((result = osst_write_error_recovery(STp, aSRpnt, 0)) == 0)
+ return 0; /* successful recovery leaves drive ready for frame */
+ if (result < 0) break;
+ if (STp->first_frame_position == curr &&
+ ((minlast < 0 &&
+ (signed)STp->last_frame_position > (signed)curr + minlast) ||
+ (minlast >= 0 && STp->cur_frames > minlast)
+ ) && result >= 0)
+ {
+#if DEBUG
+ if (debugging || jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC)
+ printk (OSST_DEB_MSG
+ "%s:D: Succ wait f fr %i (>%i): %i-%i %i (%i): %3li.%li s\n",
+ name, curr, curr+minlast, STp->first_frame_position,
+ STp->last_frame_position, STp->cur_frames,
+ result, (jiffies-startwait)/HZ,
+ (((jiffies-startwait)%HZ)*10)/HZ);
+#endif
+ return 0;
+ }
+#if DEBUG
+ if (jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC && notyetprinted)
+ {
+ printk (OSST_DEB_MSG "%s:D: Wait for frame %i (>%i): %i-%i %i (%i)\n",
+ name, curr, curr+minlast, STp->first_frame_position,
+ STp->last_frame_position, STp->cur_frames, result);
+ notyetprinted--;
+ }
+#endif
+ msleep(1000 / OSST_POLL_PER_SEC);
+ }
+#if DEBUG
+ printk (OSST_DEB_MSG "%s:D: Fail wait f fr %i (>%i): %i-%i %i: %3li.%li s\n",
+ name, curr, curr+minlast, STp->first_frame_position,
+ STp->last_frame_position, STp->cur_frames,
+ (jiffies-startwait)/HZ, (((jiffies-startwait)%HZ)*10)/HZ);
+#endif
+ return -EBUSY;
+}
+
+static int osst_recover_wait_frame(struct osst_tape * STp, struct scsi_request ** aSRpnt, int writing)
+{
+ struct scsi_request * SRpnt;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ unsigned long startwait = jiffies;
+ int retval = 1;
+ char * name = tape_name(STp);
+
+ if (writing) {
+ char mybuf[24];
+ char * olddata = STp->buffer->b_data;
+ int oldsize = STp->buffer->buffer_size;
+
+ /* write zero fm then read pos - if shows write error, try to recover - if no progress, wait */
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_FILEMARKS;
+ cmd[1] = 1;
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout,
+ MAX_RETRIES, 1);
+
+ while (retval && time_before (jiffies, startwait + 5*60*HZ)) {
+
+ if (STp->buffer->syscall_result && (SRpnt->sr_sense_buffer[2] & 0x0f) != 2) {
+
+ /* some failure - not just not-ready */
+ retval = osst_write_error_recovery(STp, aSRpnt, 0);
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout (HZ / OSST_POLL_PER_SEC);
+
+ STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = READ_POSITION;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 20, DMA_FROM_DEVICE, STp->timeout,
+ MAX_RETRIES, 1);
+
+ retval = ( STp->buffer->syscall_result || (STp->buffer)->b_data[15] > 25 );
+ STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+ }
+ if (retval)
+ printk(KERN_ERR "%s:E: Device did not succeed to write buffered data\n", name);
+ } else
+ /* TODO - figure out which error conditions can be handled */
+ if (STp->buffer->syscall_result)
+ printk(KERN_WARNING
+ "%s:W: Recover_wait_frame(read) cannot handle %02x:%02x:%02x\n", name,
+ (*aSRpnt)->sr_sense_buffer[ 2] & 0x0f,
+ (*aSRpnt)->sr_sense_buffer[12],
+ (*aSRpnt)->sr_sense_buffer[13]);
+
+ return retval;
+}
+
+/*
+ * Read the next OnStream tape frame at the current location
+ */
+static int osst_read_frame(struct osst_tape * STp, struct scsi_request ** aSRpnt, int timeout)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ int retval = 0;
+#if DEBUG
+ os_aux_t * aux = STp->buffer->aux;
+ char * name = tape_name(STp);
+#endif
+
+ if (STp->poll)
+ if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, 0, timeout))
+ retval = osst_recover_wait_frame(STp, aSRpnt, 0);
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = READ_6;
+ cmd[1] = 1;
+ cmd[4] = 1;
+
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Reading frame from OnStream tape\n", name);
+#endif
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_FROM_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+ if (!SRpnt)
+ return (-EBUSY);
+
+ if ((STp->buffer)->syscall_result) {
+ retval = 1;
+ if (STp->read_error_frame == 0) {
+ STp->read_error_frame = STp->first_frame_position;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Recording read error at %d\n", name, STp->read_error_frame);
+#endif
+ }
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ name,
+ SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[1],
+ SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3],
+ SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5],
+ SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7]);
+#endif
+ }
+ else
+ STp->first_frame_position++;
+#if DEBUG
+ if (debugging) {
+ char sig[8]; int i;
+ for (i=0;i<4;i++)
+ sig[i] = aux->application_sig[i]<32?'^':aux->application_sig[i];
+ sig[4] = '\0';
+ printk(OSST_DEB_MSG
+ "%s:D: AUX: %s UpdFrCt#%d Wpass#%d %s FrSeq#%d LogBlk#%d Qty=%d Sz=%d\n", name, sig,
+ ntohl(aux->update_frame_cntr), ntohs(aux->partition.wrt_pass_cntr),
+ aux->frame_type==1?"EOD":aux->frame_type==2?"MARK":
+ aux->frame_type==8?"HEADR":aux->frame_type==0x80?"DATA":"FILL",
+ ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num),
+ ntohs(aux->dat.dat_list[0].blk_cnt), ntohl(aux->dat.dat_list[0].blk_sz) );
+ if (aux->frame_type==2)
+ printk(OSST_DEB_MSG "%s:D: mark_cnt=%d, last_mark_ppos=%d, last_mark_lbn=%d\n", name,
+ ntohl(aux->filemark_cnt), ntohl(aux->last_mark_ppos), ntohl(aux->last_mark_lbn));
+ printk(OSST_DEB_MSG "%s:D: Exit read frame from OnStream tape with code %d\n", name, retval);
+ }
+#endif
+ return (retval);
+}
+
+static int osst_initiate_read(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ struct scsi_request * SRpnt ;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ int retval = 0;
+ char * name = tape_name(STp);
+
+ if (STps->rw != ST_READING) { /* Initialize read operation */
+ if (STps->rw == ST_WRITING || STp->dirty) {
+ STp->write_type = OS_WRITE_DATA;
+ osst_flush_write_buffer(STp, aSRpnt);
+ osst_flush_drive_buffer(STp, aSRpnt);
+ }
+ STps->rw = ST_READING;
+ STp->frame_in_buffer = 0;
+
+ /*
+ * Issue a read 0 command to get the OnStream drive
+ * read frames into its buffer.
+ */
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = READ_6;
+ cmd[1] = 1;
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Start Read Ahead on OnStream tape\n", name);
+#endif
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+ if ((retval = STp->buffer->syscall_result))
+ printk(KERN_WARNING "%s:W: Error starting read ahead\n", name);
+ }
+
+ return retval;
+}
+
+static int osst_get_logical_frame(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ int frame_seq_number, int quiet)
+{
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ char * name = tape_name(STp);
+ int cnt = 0,
+ bad = 0,
+ past = 0,
+ x,
+ position;
+
+ /*
+ * If we want just any frame (-1) and there is a frame in the buffer, return it
+ */
+ if (frame_seq_number == -1 && STp->frame_in_buffer) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Frame %d still in buffer\n", name, STp->frame_seq_number);
+#endif
+ return (STps->eof);
+ }
+ /*
+ * Search and wait for the next logical tape frame
+ */
+ while (1) {
+ if (cnt++ > 400) {
+ printk(KERN_ERR "%s:E: Couldn't find logical frame %d, aborting\n",
+ name, frame_seq_number);
+ if (STp->read_error_frame) {
+ osst_set_frame_position(STp, aSRpnt, STp->read_error_frame, 0);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Repositioning tape to bad frame %d\n",
+ name, STp->read_error_frame);
+#endif
+ STp->read_error_frame = 0;
+ STp->abort_count++;
+ }
+ return (-EIO);
+ }
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Looking for frame %d, attempt %d\n",
+ name, frame_seq_number, cnt);
+#endif
+ if ( osst_initiate_read(STp, aSRpnt)
+ || ( (!STp->frame_in_buffer) && osst_read_frame(STp, aSRpnt, 30) ) ) {
+ if (STp->raw)
+ return (-EIO);
+ position = osst_get_frame_position(STp, aSRpnt);
+ if (position >= 0xbae && position < 0xbb8)
+ position = 0xbb8;
+ else if (position > STp->eod_frame_ppos || ++bad == 10) {
+ position = STp->read_error_frame - 1;
+ bad = 0;
+ }
+ else {
+ position += 29;
+ cnt += 19;
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Bad frame detected, positioning tape to block %d\n",
+ name, position);
+#endif
+ osst_set_frame_position(STp, aSRpnt, position, 0);
+ continue;
+ }
+ if (osst_verify_frame(STp, frame_seq_number, quiet))
+ break;
+ if (osst_verify_frame(STp, -1, quiet)) {
+ x = ntohl(STp->buffer->aux->frame_seq_num);
+ if (STp->fast_open) {
+ printk(KERN_WARNING
+ "%s:W: Found logical frame %d instead of %d after fast open\n",
+ name, x, frame_seq_number);
+ STp->header_ok = 0;
+ STp->read_error_frame = 0;
+ return (-EIO);
+ }
+ if (x > frame_seq_number) {
+ if (++past > 3) {
+ /* positioning backwards did not bring us to the desired frame */
+ position = STp->read_error_frame - 1;
+ }
+ else {
+ position = osst_get_frame_position(STp, aSRpnt)
+ + frame_seq_number - x - 1;
+
+ if (STp->first_frame_position >= 3000 && position < 3000)
+ position -= 10;
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Found logical frame %d while looking for %d: back up %d\n",
+ name, x, frame_seq_number,
+ STp->first_frame_position - position);
+#endif
+ osst_set_frame_position(STp, aSRpnt, position, 0);
+ cnt += 10;
+ }
+ else
+ past = 0;
+ }
+ if (osst_get_frame_position(STp, aSRpnt) == 0xbaf) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping config partition\n", name);
+#endif
+ osst_set_frame_position(STp, aSRpnt, 0xbb8, 0);
+ cnt--;
+ }
+ STp->frame_in_buffer = 0;
+ }
+ if (cnt > 1) {
+ STp->recover_count++;
+ STp->recover_erreg++;
+ printk(KERN_WARNING "%s:I: Don't worry, Read error at position %d recovered\n",
+ name, STp->read_error_frame);
+ }
+ STp->read_count++;
+
+#if DEBUG
+ if (debugging || STps->eof)
+ printk(OSST_DEB_MSG
+ "%s:D: Exit get logical frame (%d=>%d) from OnStream tape with code %d\n",
+ name, frame_seq_number, STp->frame_seq_number, STps->eof);
+#endif
+ STp->fast_open = 0;
+ STp->read_error_frame = 0;
+ return (STps->eof);
+}
+
+static int osst_seek_logical_blk(struct osst_tape * STp, struct scsi_request ** aSRpnt, int logical_blk_num)
+{
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ char * name = tape_name(STp);
+ int retries = 0;
+ int frame_seq_estimate, ppos_estimate, move;
+
+ if (logical_blk_num < 0) logical_blk_num = 0;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Seeking logical block %d (now at %d, size %d%c)\n",
+ name, logical_blk_num, STp->logical_blk_num,
+ STp->block_size<1024?STp->block_size:STp->block_size/1024,
+ STp->block_size<1024?'b':'k');
+#endif
+ /* Do we know where we are? */
+ if (STps->drv_block >= 0) {
+ move = logical_blk_num - STp->logical_blk_num;
+ if (move < 0) move -= (OS_DATA_SIZE / STp->block_size) - 1;
+ move /= (OS_DATA_SIZE / STp->block_size);
+ frame_seq_estimate = STp->frame_seq_number + move;
+ } else
+ frame_seq_estimate = logical_blk_num * STp->block_size / OS_DATA_SIZE;
+
+ if (frame_seq_estimate < 2980) ppos_estimate = frame_seq_estimate + 10;
+ else ppos_estimate = frame_seq_estimate + 20;
+ while (++retries < 10) {
+ if (ppos_estimate > STp->eod_frame_ppos-2) {
+ frame_seq_estimate += STp->eod_frame_ppos - 2 - ppos_estimate;
+ ppos_estimate = STp->eod_frame_ppos - 2;
+ }
+ if (frame_seq_estimate < 0) {
+ frame_seq_estimate = 0;
+ ppos_estimate = 10;
+ }
+ osst_set_frame_position(STp, aSRpnt, ppos_estimate, 0);
+ if (osst_get_logical_frame(STp, aSRpnt, frame_seq_estimate, 1) >= 0) {
+ /* we've located the estimated frame, now does it have our block? */
+ if (logical_blk_num < STp->logical_blk_num ||
+ logical_blk_num >= STp->logical_blk_num + ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt)) {
+ if (STps->eof == ST_FM_HIT)
+ move = logical_blk_num < STp->logical_blk_num? -2 : 1;
+ else {
+ move = logical_blk_num - STp->logical_blk_num;
+ if (move < 0) move -= (OS_DATA_SIZE / STp->block_size) - 1;
+ move /= (OS_DATA_SIZE / STp->block_size);
+ }
+ if (!move) move = logical_blk_num > STp->logical_blk_num ? 1 : -1;
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Seek retry %d at ppos %d fsq %d (est %d) lbn %d (need %d) move %d\n",
+ name, retries, ppos_estimate, STp->frame_seq_number, frame_seq_estimate,
+ STp->logical_blk_num, logical_blk_num, move);
+#endif
+ frame_seq_estimate += move;
+ ppos_estimate += move;
+ continue;
+ } else {
+ STp->buffer->read_pointer = (logical_blk_num - STp->logical_blk_num) * STp->block_size;
+ STp->buffer->buffer_bytes -= STp->buffer->read_pointer;
+ STp->logical_blk_num = logical_blk_num;
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Seek success at ppos %d fsq %d in_buf %d, bytes %d, ptr %d*%d\n",
+ name, ppos_estimate, STp->frame_seq_number, STp->frame_in_buffer,
+ STp->buffer->buffer_bytes, STp->buffer->read_pointer / STp->block_size,
+ STp->block_size);
+#endif
+ STps->drv_file = ntohl(STp->buffer->aux->filemark_cnt);
+ if (STps->eof == ST_FM_HIT) {
+ STps->drv_file++;
+ STps->drv_block = 0;
+ } else {
+ STps->drv_block = ntohl(STp->buffer->aux->last_mark_lbn)?
+ STp->logical_blk_num -
+ (STps->drv_file ? ntohl(STp->buffer->aux->last_mark_lbn) + 1 : 0):
+ -1;
+ }
+ STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_NOEOF;
+ return 0;
+ }
+ }
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 1) < 0)
+ goto error;
+ /* we are not yet at the estimated frame, adjust our estimate of its physical position */
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Seek retry %d at ppos %d fsq %d (est %d) lbn %d (need %d)\n",
+ name, retries, ppos_estimate, STp->frame_seq_number, frame_seq_estimate,
+ STp->logical_blk_num, logical_blk_num);
+#endif
+ if (frame_seq_estimate != STp->frame_seq_number)
+ ppos_estimate += frame_seq_estimate - STp->frame_seq_number;
+ else
+ break;
+ }
+error:
+ printk(KERN_ERR "%s:E: Couldn't seek to logical block %d (at %d), %d retries\n",
+ name, logical_blk_num, STp->logical_blk_num, retries);
+ return (-EIO);
+}
+
+/* The values below are based on the OnStream frame payload size of 32K == 2**15,
+ * that is, OSST_FRAME_SHIFT + OSST_SECTOR_SHIFT must be 15. With a minimum block
+ * size of 512 bytes, we need to be able to resolve 32K/512 == 64 == 2**6 positions
+ * inside each frame. Finaly, OSST_SECTOR_MASK == 2**OSST_FRAME_SHIFT - 1.
+ */
+#define OSST_FRAME_SHIFT 6
+#define OSST_SECTOR_SHIFT 9
+#define OSST_SECTOR_MASK 0x03F
+
+static int osst_get_sector(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ int sector;
+#if DEBUG
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG
+ "%s:D: Positioned at ppos %d, frame %d, lbn %d, file %d, blk %d, %cptr %d, eof %d\n",
+ name, STp->first_frame_position, STp->frame_seq_number, STp->logical_blk_num,
+ STp->ps[STp->partition].drv_file, STp->ps[STp->partition].drv_block,
+ STp->ps[STp->partition].rw == ST_WRITING?'w':'r',
+ STp->ps[STp->partition].rw == ST_WRITING?STp->buffer->buffer_bytes:
+ STp->buffer->read_pointer, STp->ps[STp->partition].eof);
+#endif
+ /* do we know where we are inside a file? */
+ if (STp->ps[STp->partition].drv_block >= 0) {
+ sector = (STp->frame_in_buffer ? STp->first_frame_position-1 :
+ STp->first_frame_position) << OSST_FRAME_SHIFT;
+ if (STp->ps[STp->partition].rw == ST_WRITING)
+ sector |= (STp->buffer->buffer_bytes >> OSST_SECTOR_SHIFT) & OSST_SECTOR_MASK;
+ else
+ sector |= (STp->buffer->read_pointer >> OSST_SECTOR_SHIFT) & OSST_SECTOR_MASK;
+ } else {
+ sector = osst_get_frame_position(STp, aSRpnt);
+ if (sector > 0)
+ sector <<= OSST_FRAME_SHIFT;
+ }
+ return sector;
+}
+
+static int osst_seek_sector(struct osst_tape * STp, struct scsi_request ** aSRpnt, int sector)
+{
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ int frame = sector >> OSST_FRAME_SHIFT,
+ offset = (sector & OSST_SECTOR_MASK) << OSST_SECTOR_SHIFT,
+ r;
+#if DEBUG
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG "%s:D: Seeking sector %d in frame %d at offset %d\n",
+ name, sector, frame, offset);
+#endif
+ if (frame < 0 || frame >= STp->capacity) return (-ENXIO);
+
+ if (frame <= STp->first_data_ppos) {
+ STp->frame_seq_number = STp->logical_blk_num = STps->drv_file = STps->drv_block = 0;
+ return (osst_set_frame_position(STp, aSRpnt, frame, 0));
+ }
+ r = osst_set_frame_position(STp, aSRpnt, offset?frame:frame-1, 0);
+ if (r < 0) return r;
+
+ r = osst_get_logical_frame(STp, aSRpnt, -1, 1);
+ if (r < 0) return r;
+
+ if (osst_get_frame_position(STp, aSRpnt) != (offset?frame+1:frame)) return (-EIO);
+
+ if (offset) {
+ STp->logical_blk_num += offset / STp->block_size;
+ STp->buffer->read_pointer = offset;
+ STp->buffer->buffer_bytes -= offset;
+ } else {
+ STp->frame_seq_number++;
+ STp->frame_in_buffer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ STp->buffer->buffer_bytes = STp->buffer->read_pointer = 0;
+ }
+ STps->drv_file = ntohl(STp->buffer->aux->filemark_cnt);
+ if (STps->eof == ST_FM_HIT) {
+ STps->drv_file++;
+ STps->drv_block = 0;
+ } else {
+ STps->drv_block = ntohl(STp->buffer->aux->last_mark_lbn)?
+ STp->logical_blk_num -
+ (STps->drv_file ? ntohl(STp->buffer->aux->last_mark_lbn) + 1 : 0):
+ -1;
+ }
+ STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_NOEOF;
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Now positioned at ppos %d, frame %d, lbn %d, file %d, blk %d, rptr %d, eof %d\n",
+ name, STp->first_frame_position, STp->frame_seq_number, STp->logical_blk_num,
+ STps->drv_file, STps->drv_block, STp->buffer->read_pointer, STps->eof);
+#endif
+ return 0;
+}
+
+/*
+ * Read back the drive's internal buffer contents, as a part
+ * of the write error recovery mechanism for old OnStream
+ * firmware revisions.
+ * Precondition for this function to work: all frames in the
+ * drive's buffer must be of one type (DATA, MARK or EOD)!
+ */
+static int osst_read_back_buffer_and_rewrite(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ unsigned int frame, unsigned int skip, int pending)
+{
+ struct scsi_request * SRpnt = * aSRpnt;
+ unsigned char * buffer, * p;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ int flag, new_frame, i;
+ int nframes = STp->cur_frames;
+ int blks_per_frame = ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ int frame_seq_number = ntohl(STp->buffer->aux->frame_seq_num)
+ - (nframes + pending - 1);
+ int logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num)
+ - (nframes + pending - 1) * blks_per_frame;
+ char * name = tape_name(STp);
+ unsigned long startwait = jiffies;
+#if DEBUG
+ int dbg = debugging;
+#endif
+
+ if ((buffer = (unsigned char *)vmalloc((nframes + 1) * OS_DATA_SIZE)) == NULL)
+ return (-EIO);
+
+ printk(KERN_INFO "%s:I: Reading back %d frames from drive buffer%s\n",
+ name, nframes, pending?" and one that was pending":"");
+
+ osst_copy_from_buffer(STp->buffer, (p = &buffer[nframes * OS_DATA_SIZE]));
+#if DEBUG
+ if (pending && debugging)
+ printk(OSST_DEB_MSG "%s:D: Pending frame %d (lblk %d), data %02x %02x %02x %02x\n",
+ name, frame_seq_number + nframes,
+ logical_blk_num + nframes * blks_per_frame,
+ p[0], p[1], p[2], p[3]);
+#endif
+ for (i = 0, p = buffer; i < nframes; i++, p += OS_DATA_SIZE) {
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = 0x3C; /* Buffer Read */
+ cmd[1] = 6; /* Retrieve Faulty Block */
+ cmd[7] = 32768 >> 8;
+ cmd[8] = 32768 & 0xff;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, DMA_FROM_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+
+ if ((STp->buffer)->syscall_result || !SRpnt) {
+ printk(KERN_ERR "%s:E: Failed to read frame back from OnStream buffer\n", name);
+ vfree((void *)buffer);
+ *aSRpnt = SRpnt;
+ return (-EIO);
+ }
+ osst_copy_from_buffer(STp->buffer, p);
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Read back logical frame %d, data %02x %02x %02x %02x\n",
+ name, frame_seq_number + i, p[0], p[1], p[2], p[3]);
+#endif
+ }
+ *aSRpnt = SRpnt;
+ osst_get_frame_position(STp, aSRpnt);
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Frames left in buffer: %d\n", name, STp->cur_frames);
+#endif
+ /* Write synchronously so we can be sure we're OK again and don't have to recover recursively */
+ /* In the header we don't actually re-write the frames that fail, just the ones after them */
+
+ for (flag=1, new_frame=frame, p=buffer, i=0; i < nframes + pending; ) {
+
+ if (flag) {
+ if (STp->write_type == OS_WRITE_HEADER) {
+ i += skip;
+ p += skip * OS_DATA_SIZE;
+ }
+ else if (new_frame < 2990 && new_frame+skip+nframes+pending >= 2990)
+ new_frame = 3000-i;
+ else
+ new_frame += skip;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Position to frame %d, write fseq %d\n",
+ name, new_frame+i, frame_seq_number+i);
+#endif
+ osst_set_frame_position(STp, aSRpnt, new_frame + i, 0);
+ osst_wait_ready(STp, aSRpnt, 60, OSST_WAIT_POSITION_COMPLETE);
+ osst_get_frame_position(STp, aSRpnt);
+ SRpnt = * aSRpnt;
+
+ if (new_frame > frame + 1000) {
+ printk(KERN_ERR "%s:E: Failed to find writable tape media\n", name);
+ vfree((void *)buffer);
+ return (-EIO);
+ }
+ if ( i >= nframes + pending ) break;
+ flag = 0;
+ }
+ osst_copy_to_buffer(STp->buffer, p);
+ /*
+ * IMPORTANT: for error recovery to work, _never_ queue frames with mixed frame type!
+ */
+ osst_init_aux(STp, STp->buffer->aux->frame_type, frame_seq_number+i,
+ logical_blk_num + i*blks_per_frame,
+ ntohl(STp->buffer->aux->dat.dat_list[0].blk_sz), blks_per_frame);
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_6;
+ cmd[1] = 1;
+ cmd[4] = 1;
+
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG
+ "%s:D: About to write frame %d, seq %d, lbn %d, data %02x %02x %02x %02x\n",
+ name, new_frame+i, frame_seq_number+i, logical_blk_num + i*blks_per_frame,
+ p[0], p[1], p[2], p[3]);
+#endif
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, DMA_TO_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+
+ if (STp->buffer->syscall_result)
+ flag = 1;
+ else {
+ p += OS_DATA_SIZE; i++;
+
+ /* if we just sent the last frame, wait till all successfully written */
+ if ( i == nframes + pending ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Check re-write successful\n", name);
+#endif
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_FILEMARKS;
+ cmd[1] = 1;
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
+ STp->timeout, MAX_RETRIES, 1);
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: Sleeping in re-write wait ready\n", name);
+ printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
+ debugging = 0;
+ }
+#endif
+ flag = STp->buffer->syscall_result;
+ while ( !flag && time_before(jiffies, startwait + 60*HZ) ) {
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout,
+ MAX_RETRIES, 1);
+
+ if (SRpnt->sr_sense_buffer[2] == 2 && SRpnt->sr_sense_buffer[12] == 4 &&
+ (SRpnt->sr_sense_buffer[13] == 1 || SRpnt->sr_sense_buffer[13] == 8)) {
+ /* in the process of becoming ready */
+ msleep(100);
+ continue;
+ }
+ if (STp->buffer->syscall_result)
+ flag = 1;
+ break;
+ }
+#if DEBUG
+ debugging = dbg;
+ printk(OSST_DEB_MSG "%s:D: Wait re-write finished\n", name);
+#endif
+ }
+ }
+ *aSRpnt = SRpnt;
+ if (flag) {
+ if ((SRpnt->sr_sense_buffer[ 2] & 0x0f) == 13 &&
+ SRpnt->sr_sense_buffer[12] == 0 &&
+ SRpnt->sr_sense_buffer[13] == 2) {
+ printk(KERN_ERR "%s:E: Volume overflow in write error recovery\n", name);
+ vfree((void *)buffer);
+ return (-EIO); /* hit end of tape = fail */
+ }
+ i = ((SRpnt->sr_sense_buffer[3] << 24) |
+ (SRpnt->sr_sense_buffer[4] << 16) |
+ (SRpnt->sr_sense_buffer[5] << 8) |
+ SRpnt->sr_sense_buffer[6] ) - new_frame;
+ p = &buffer[i * OS_DATA_SIZE];
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Additional write error at %d\n", name, new_frame+i);
+#endif
+ osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: reported frame positions: host = %d, tape = %d, buffer = %d\n",
+ name, STp->first_frame_position, STp->last_frame_position, STp->cur_frames);
+#endif
+ }
+ }
+ if (flag) {
+ /* error recovery did not successfully complete */
+ printk(KERN_ERR "%s:D: Write error recovery failed in %s\n", name,
+ STp->write_type == OS_WRITE_HEADER?"header":"body");
+ }
+ if (!pending)
+ osst_copy_to_buffer(STp->buffer, p); /* so buffer content == at entry in all cases */
+ vfree((void *)buffer);
+ return 0;
+}
+
+static int osst_reposition_and_retry(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ unsigned int frame, unsigned int skip, int pending)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ char * name = tape_name(STp);
+ int expected = 0;
+ int attempts = 1000 / skip;
+ int flag = 1;
+ unsigned long startwait = jiffies;
+#if DEBUG
+ int dbg = debugging;
+#endif
+
+ while (attempts && time_before(jiffies, startwait + 60*HZ)) {
+ if (flag) {
+#if DEBUG
+ debugging = dbg;
+#endif
+ if (frame < 2990 && frame+skip+STp->cur_frames+pending >= 2990)
+ frame = 3000-skip;
+ expected = frame+skip+STp->cur_frames+pending;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Position to fppos %d, re-write from fseq %d\n",
+ name, frame+skip, STp->frame_seq_number-STp->cur_frames-pending);
+#endif
+ osst_set_frame_position(STp, aSRpnt, frame + skip, 1);
+ flag = 0;
+ attempts--;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ }
+ if (osst_get_frame_position(STp, aSRpnt) < 0) { /* additional write error */
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Addl error, host %d, tape %d, buffer %d\n",
+ name, STp->first_frame_position,
+ STp->last_frame_position, STp->cur_frames);
+#endif
+ frame = STp->last_frame_position;
+ flag = 1;
+ continue;
+ }
+ if (pending && STp->cur_frames < 50) {
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_6;
+ cmd[1] = 1;
+ cmd[4] = 1;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: About to write pending fseq %d at fppos %d\n",
+ name, STp->frame_seq_number-1, STp->first_frame_position);
+#endif
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_TO_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+
+ if (STp->buffer->syscall_result) { /* additional write error */
+ if ((SRpnt->sr_sense_buffer[ 2] & 0x0f) == 13 &&
+ SRpnt->sr_sense_buffer[12] == 0 &&
+ SRpnt->sr_sense_buffer[13] == 2) {
+ printk(KERN_ERR
+ "%s:E: Volume overflow in write error recovery\n",
+ name);
+ break; /* hit end of tape = fail */
+ }
+ flag = 1;
+ }
+ else
+ pending = 0;
+
+ continue;
+ }
+ if (STp->cur_frames == 0) {
+#if DEBUG
+ debugging = dbg;
+ printk(OSST_DEB_MSG "%s:D: Wait re-write finished\n", name);
+#endif
+ if (STp->first_frame_position != expected) {
+ printk(KERN_ERR "%s:A: Actual position %d - expected %d\n",
+ name, STp->first_frame_position, expected);
+ return (-EIO);
+ }
+ return 0;
+ }
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: Sleeping in re-write wait ready\n", name);
+ printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name);
+ debugging = 0;
+ }
+#endif
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ }
+ printk(KERN_ERR "%s:E: Failed to find valid tape media\n", name);
+#if DEBUG
+ debugging = dbg;
+#endif
+ return (-EIO);
+}
+
+/*
+ * Error recovery algorithm for the OnStream tape.
+ */
+
+static int osst_write_error_recovery(struct osst_tape * STp, struct scsi_request ** aSRpnt, int pending)
+{
+ struct scsi_request * SRpnt = * aSRpnt;
+ struct st_partstat * STps = & STp->ps[STp->partition];
+ char * name = tape_name(STp);
+ int retval = 0;
+ int rw_state;
+ unsigned int frame, skip;
+
+ rw_state = STps->rw;
+
+ if ((SRpnt->sr_sense_buffer[ 2] & 0x0f) != 3
+ || SRpnt->sr_sense_buffer[12] != 12
+ || SRpnt->sr_sense_buffer[13] != 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Write error recovery cannot handle %02x:%02x:%02x\n", name,
+ SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+#endif
+ return (-EIO);
+ }
+ frame = (SRpnt->sr_sense_buffer[3] << 24) |
+ (SRpnt->sr_sense_buffer[4] << 16) |
+ (SRpnt->sr_sense_buffer[5] << 8) |
+ SRpnt->sr_sense_buffer[6];
+ skip = SRpnt->sr_sense_buffer[9];
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Detected physical bad frame at %u, advised to skip %d\n", name, frame, skip);
+#endif
+ osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: reported frame positions: host = %d, tape = %d\n",
+ name, STp->first_frame_position, STp->last_frame_position);
+#endif
+ switch (STp->write_type) {
+ case OS_WRITE_DATA:
+ case OS_WRITE_EOD:
+ case OS_WRITE_NEW_MARK:
+ printk(KERN_WARNING
+ "%s:I: Relocating %d buffered logical frames from position %u to %u\n",
+ name, STp->cur_frames, frame, (frame + skip > 3000 && frame < 3000)?3000:frame + skip);
+ if (STp->os_fw_rev >= 10600)
+ retval = osst_reposition_and_retry(STp, aSRpnt, frame, skip, pending);
+ else
+ retval = osst_read_back_buffer_and_rewrite(STp, aSRpnt, frame, skip, pending);
+ printk(KERN_WARNING "%s:%s: %sWrite error%srecovered\n", name,
+ retval?"E" :"I",
+ retval?"" :"Don't worry, ",
+ retval?" not ":" ");
+ break;
+ case OS_WRITE_LAST_MARK:
+ printk(KERN_ERR "%s:E: Bad frame in update last marker, fatal\n", name);
+ osst_set_frame_position(STp, aSRpnt, frame + STp->cur_frames + pending, 0);
+ retval = -EIO;
+ break;
+ case OS_WRITE_HEADER:
+ printk(KERN_WARNING "%s:I: Bad frame in header partition, skipped\n", name);
+ retval = osst_read_back_buffer_and_rewrite(STp, aSRpnt, frame, 1, pending);
+ break;
+ default:
+ printk(KERN_INFO "%s:I: Bad frame in filler, ignored\n", name);
+ osst_set_frame_position(STp, aSRpnt, frame + STp->cur_frames + pending, 0);
+ }
+ osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Positioning complete, cur_frames %d, pos %d, tape pos %d\n",
+ name, STp->cur_frames, STp->first_frame_position, STp->last_frame_position);
+ printk(OSST_DEB_MSG "%s:D: next logical frame to write: %d\n", name, STp->logical_blk_num);
+#endif
+ if (retval == 0) {
+ STp->recover_count++;
+ STp->recover_erreg++;
+ } else
+ STp->abort_count++;
+
+ STps->rw = rw_state;
+ return retval;
+}
+
+static int osst_space_over_filemarks_backward(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ int mt_op, int mt_count)
+{
+ char * name = tape_name(STp);
+ int cnt;
+ int last_mark_ppos = -1;
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reached space_over_filemarks_backwards %d %d\n", name, mt_op, mt_count);
+#endif
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks_bwd\n", name);
+#endif
+ return -EIO;
+ }
+ if (STp->linux_media_version >= 4) {
+ /*
+ * direct lookup in header filemark list
+ */
+ cnt = ntohl(STp->buffer->aux->filemark_cnt);
+ if (STp->header_ok &&
+ STp->header_cache != NULL &&
+ (cnt - mt_count) >= 0 &&
+ (cnt - mt_count) < OS_FM_TAB_MAX &&
+ (cnt - mt_count) < STp->filemark_cnt &&
+ STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] == STp->buffer->aux->last_mark_ppos)
+
+ last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt - mt_count]);
+#if DEBUG
+ if (STp->header_cache == NULL || (cnt - mt_count) < 0 || (cnt - mt_count) >= OS_FM_TAB_MAX)
+ printk(OSST_DEB_MSG "%s:D: Filemark lookup fail due to %s\n", name,
+ STp->header_cache == NULL?"lack of header cache":"count out of range");
+ else
+ printk(OSST_DEB_MSG "%s:D: Filemark lookup: prev mark %d (%s), skip %d to %d\n",
+ name, cnt,
+ ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+ (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] ==
+ STp->buffer->aux->last_mark_ppos))?"match":"error",
+ mt_count, last_mark_ppos);
+#endif
+ if (last_mark_ppos > 10 && last_mark_ppos < STp->eod_frame_ppos) {
+ osst_position_tape_and_confirm(STp, aSRpnt, last_mark_ppos);
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Couldn't get logical blk num in space_filemarks\n", name);
+#endif
+ return (-EIO);
+ }
+ if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
+ name, last_mark_ppos);
+ return (-EIO);
+ }
+ goto found;
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reverting to scan filemark backwards\n", name);
+#endif
+ }
+ cnt = 0;
+ while (cnt != mt_count) {
+ last_mark_ppos = ntohl(STp->buffer->aux->last_mark_ppos);
+ if (last_mark_ppos == -1)
+ return (-EIO);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Positioning to last mark at %d\n", name, last_mark_ppos);
+#endif
+ osst_position_tape_and_confirm(STp, aSRpnt, last_mark_ppos);
+ cnt++;
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n", name);
+#endif
+ return (-EIO);
+ }
+ if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
+ name, last_mark_ppos);
+ return (-EIO);
+ }
+ }
+found:
+ if (mt_op == MTBSFM) {
+ STp->frame_seq_number++;
+ STp->frame_in_buffer = 0;
+ STp->buffer->buffer_bytes = 0;
+ STp->buffer->read_pointer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ }
+ return 0;
+}
+
+/*
+ * ADRL 1.1 compatible "slow" space filemarks fwd version
+ *
+ * Just scans for the filemark sequentially.
+ */
+static int osst_space_over_filemarks_forward_slow(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ int mt_op, int mt_count)
+{
+ int cnt = 0;
+#if DEBUG
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG "%s:D: Reached space_over_filemarks_forward_slow %d %d\n", name, mt_op, mt_count);
+#endif
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks_fwd\n", name);
+#endif
+ return (-EIO);
+ }
+ while (1) {
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n", name);
+#endif
+ return (-EIO);
+ }
+ if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
+ cnt++;
+ if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: space_fwd: EOD reached\n", name);
+#endif
+ if (STp->first_frame_position > STp->eod_frame_ppos+1) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: EOD position corrected (%d=>%d)\n",
+ name, STp->eod_frame_ppos, STp->first_frame_position-1);
+#endif
+ STp->eod_frame_ppos = STp->first_frame_position-1;
+ }
+ return (-EIO);
+ }
+ if (cnt == mt_count)
+ break;
+ STp->frame_in_buffer = 0;
+ }
+ if (mt_op == MTFSF) {
+ STp->frame_seq_number++;
+ STp->frame_in_buffer = 0;
+ STp->buffer->buffer_bytes = 0;
+ STp->buffer->read_pointer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ }
+ return 0;
+}
+
+/*
+ * Fast linux specific version of OnStream FSF
+ */
+static int osst_space_over_filemarks_forward_fast(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ int mt_op, int mt_count)
+{
+ char * name = tape_name(STp);
+ int cnt = 0,
+ next_mark_ppos = -1;
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reached space_over_filemarks_forward_fast %d %d\n", name, mt_op, mt_count);
+#endif
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks_fwd\n", name);
+#endif
+ return (-EIO);
+ }
+
+ if (STp->linux_media_version >= 4) {
+ /*
+ * direct lookup in header filemark list
+ */
+ cnt = ntohl(STp->buffer->aux->filemark_cnt) - 1;
+ if (STp->header_ok &&
+ STp->header_cache != NULL &&
+ (cnt + mt_count) < OS_FM_TAB_MAX &&
+ (cnt + mt_count) < STp->filemark_cnt &&
+ ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+ (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] == STp->buffer->aux->last_mark_ppos)))
+
+ next_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt + mt_count]);
+#if DEBUG
+ if (STp->header_cache == NULL || (cnt + mt_count) >= OS_FM_TAB_MAX)
+ printk(OSST_DEB_MSG "%s:D: Filemark lookup fail due to %s\n", name,
+ STp->header_cache == NULL?"lack of header cache":"count out of range");
+ else
+ printk(OSST_DEB_MSG "%s:D: Filemark lookup: prev mark %d (%s), skip %d to %d\n",
+ name, cnt,
+ ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+ (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] ==
+ STp->buffer->aux->last_mark_ppos))?"match":"error",
+ mt_count, next_mark_ppos);
+#endif
+ if (next_mark_ppos <= 10 || next_mark_ppos > STp->eod_frame_ppos) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reverting to slow filemark space\n", name);
+#endif
+ return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count);
+ } else {
+ osst_position_tape_and_confirm(STp, aSRpnt, next_mark_ppos);
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n",
+ name);
+#endif
+ return (-EIO);
+ }
+ if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
+ name, next_mark_ppos);
+ return (-EIO);
+ }
+ if (ntohl(STp->buffer->aux->filemark_cnt) != cnt + mt_count) {
+ printk(KERN_WARNING "%s:W: Expected to find marker %d at ppos %d, not %d\n",
+ name, cnt+mt_count, next_mark_ppos,
+ ntohl(STp->buffer->aux->filemark_cnt));
+ return (-EIO);
+ }
+ }
+ } else {
+ /*
+ * Find nearest (usually previous) marker, then jump from marker to marker
+ */
+ while (1) {
+ if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
+ break;
+ if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: space_fwd: EOD reached\n", name);
+#endif
+ return (-EIO);
+ }
+ if (ntohl(STp->buffer->aux->filemark_cnt) == 0) {
+ if (STp->first_mark_ppos == -1) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reverting to slow filemark space\n", name);
+#endif
+ return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count);
+ }
+ osst_position_tape_and_confirm(STp, aSRpnt, STp->first_mark_ppos);
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Couldn't get logical blk num in space_filemarks_fwd_fast\n",
+ name);
+#endif
+ return (-EIO);
+ }
+ if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_WARNING "%s:W: Expected to find filemark at %d\n",
+ name, STp->first_mark_ppos);
+ return (-EIO);
+ }
+ } else {
+ if (osst_space_over_filemarks_backward(STp, aSRpnt, MTBSF, 1) < 0)
+ return (-EIO);
+ mt_count++;
+ }
+ }
+ cnt++;
+ while (cnt != mt_count) {
+ next_mark_ppos = ntohl(STp->buffer->aux->next_mark_ppos);
+ if (!next_mark_ppos || next_mark_ppos > STp->eod_frame_ppos) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reverting to slow filemark space\n", name);
+#endif
+ return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count - cnt);
+ }
+#if DEBUG
+ else printk(OSST_DEB_MSG "%s:D: Positioning to next mark at %d\n", name, next_mark_ppos);
+#endif
+ osst_position_tape_and_confirm(STp, aSRpnt, next_mark_ppos);
+ cnt++;
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in space_filemarks\n",
+ name);
+#endif
+ return (-EIO);
+ }
+ if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+ printk(KERN_WARNING "%s:W: Expected to find marker at ppos %d, not found\n",
+ name, next_mark_ppos);
+ return (-EIO);
+ }
+ }
+ }
+ if (mt_op == MTFSF) {
+ STp->frame_seq_number++;
+ STp->frame_in_buffer = 0;
+ STp->buffer->buffer_bytes = 0;
+ STp->buffer->read_pointer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ }
+ return 0;
+}
+
+/*
+ * In debug mode, we want to see as many errors as possible
+ * to test the error recovery mechanism.
+ */
+#if DEBUG
+static void osst_set_retries(struct osst_tape * STp, struct scsi_request ** aSRpnt, int retries)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt = * aSRpnt;
+ char * name = tape_name(STp);
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = 0x10;
+ cmd[4] = NUMBER_RETRIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ (STp->buffer)->b_data[0] = cmd[4] - 1;
+ (STp->buffer)->b_data[1] = 0; /* Medium Type - ignoring */
+ (STp->buffer)->b_data[2] = 0; /* Reserved */
+ (STp->buffer)->b_data[3] = 0; /* Block Descriptor Length */
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = NUMBER_RETRIES_PAGE | (1 << 7);
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 2;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 4;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = retries;
+
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Setting number of retries on OnStream tape to %d\n", name, retries);
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
+ *aSRpnt = SRpnt;
+
+ if ((STp->buffer)->syscall_result)
+ printk (KERN_ERR "%s:D: Couldn't set retries to %d\n", name, retries);
+}
+#endif
+
+
+static int osst_write_filemark(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ int result;
+ int this_mark_ppos = STp->first_frame_position;
+ int this_mark_lbn = STp->logical_blk_num;
+#if DEBUG
+ char * name = tape_name(STp);
+#endif
+
+ if (STp->raw) return 0;
+
+ STp->write_type = OS_WRITE_NEW_MARK;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Writing Filemark %i at fppos %d (fseq %d, lblk %d)\n",
+ name, STp->filemark_cnt, this_mark_ppos, STp->frame_seq_number, this_mark_lbn);
+#endif
+ STp->dirty = 1;
+ result = osst_flush_write_buffer(STp, aSRpnt);
+ result |= osst_flush_drive_buffer(STp, aSRpnt);
+ STp->last_mark_ppos = this_mark_ppos;
+ STp->last_mark_lbn = this_mark_lbn;
+ if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX)
+ STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos);
+ if (STp->filemark_cnt++ == 0)
+ STp->first_mark_ppos = this_mark_ppos;
+ return result;
+}
+
+static int osst_write_eod(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ int result;
+#if DEBUG
+ char * name = tape_name(STp);
+#endif
+
+ if (STp->raw) return 0;
+
+ STp->write_type = OS_WRITE_EOD;
+ STp->eod_frame_ppos = STp->first_frame_position;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Writing EOD at fppos %d (fseq %d, lblk %d)\n", name,
+ STp->eod_frame_ppos, STp->frame_seq_number, STp->logical_blk_num);
+#endif
+ STp->dirty = 1;
+
+ result = osst_flush_write_buffer(STp, aSRpnt);
+ result |= osst_flush_drive_buffer(STp, aSRpnt);
+ STp->eod_frame_lfa = --(STp->frame_seq_number);
+ return result;
+}
+
+static int osst_write_filler(struct osst_tape * STp, struct scsi_request ** aSRpnt, int where, int count)
+{
+ char * name = tape_name(STp);
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reached onstream write filler group %d\n", name, where);
+#endif
+ osst_wait_ready(STp, aSRpnt, 60 * 5, 0);
+ osst_set_frame_position(STp, aSRpnt, where, 0);
+ STp->write_type = OS_WRITE_FILLER;
+ while (count--) {
+ memcpy(STp->buffer->b_data, "Filler", 6);
+ STp->buffer->buffer_bytes = 6;
+ STp->dirty = 1;
+ if (osst_flush_write_buffer(STp, aSRpnt)) {
+ printk(KERN_INFO "%s:I: Couldn't write filler frame\n", name);
+ return (-EIO);
+ }
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Exiting onstream write filler group\n", name);
+#endif
+ return osst_flush_drive_buffer(STp, aSRpnt);
+}
+
+static int __osst_write_header(struct osst_tape * STp, struct scsi_request ** aSRpnt, int where, int count)
+{
+ char * name = tape_name(STp);
+ int result;
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reached onstream write header group %d\n", name, where);
+#endif
+ osst_wait_ready(STp, aSRpnt, 60 * 5, 0);
+ osst_set_frame_position(STp, aSRpnt, where, 0);
+ STp->write_type = OS_WRITE_HEADER;
+ while (count--) {
+ osst_copy_to_buffer(STp->buffer, (unsigned char *)STp->header_cache);
+ STp->buffer->buffer_bytes = sizeof(os_header_t);
+ STp->dirty = 1;
+ if (osst_flush_write_buffer(STp, aSRpnt)) {
+ printk(KERN_INFO "%s:I: Couldn't write header frame\n", name);
+ return (-EIO);
+ }
+ }
+ result = osst_flush_drive_buffer(STp, aSRpnt);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Write onstream header group %s\n", name, result?"failed":"done");
+#endif
+ return result;
+}
+
+static int osst_write_header(struct osst_tape * STp, struct scsi_request ** aSRpnt, int locate_eod)
+{
+ os_header_t * header;
+ int result;
+ char * name = tape_name(STp);
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Writing tape header\n", name);
+#endif
+ if (STp->raw) return 0;
+
+ if (STp->header_cache == NULL) {
+ if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) {
+ printk(KERN_ERR "%s:E: Failed to allocate header cache\n", name);
+ return (-ENOMEM);
+ }
+ memset(STp->header_cache, 0, sizeof(os_header_t));
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Allocated and cleared memory for header cache\n", name);
+#endif
+ }
+ if (STp->header_ok) STp->update_frame_cntr++;
+ else STp->update_frame_cntr = 0;
+
+ header = STp->header_cache;
+ strcpy(header->ident_str, "ADR_SEQ");
+ header->major_rev = 1;
+ header->minor_rev = 4;
+ header->ext_trk_tb_off = htons(17192);
+ header->pt_par_num = 1;
+ header->partition[0].partition_num = OS_DATA_PARTITION;
+ header->partition[0].par_desc_ver = OS_PARTITION_VERSION;
+ header->partition[0].wrt_pass_cntr = htons(STp->wrt_pass_cntr);
+ header->partition[0].first_frame_ppos = htonl(STp->first_data_ppos);
+ header->partition[0].last_frame_ppos = htonl(STp->capacity);
+ header->partition[0].eod_frame_ppos = htonl(STp->eod_frame_ppos);
+ header->cfg_col_width = htonl(20);
+ header->dat_col_width = htonl(1500);
+ header->qfa_col_width = htonl(0);
+ header->ext_track_tb.nr_stream_part = 1;
+ header->ext_track_tb.et_ent_sz = 32;
+ header->ext_track_tb.dat_ext_trk_ey.et_part_num = 0;
+ header->ext_track_tb.dat_ext_trk_ey.fmt = 1;
+ header->ext_track_tb.dat_ext_trk_ey.fm_tab_off = htons(17736);
+ header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi = 0;
+ header->ext_track_tb.dat_ext_trk_ey.last_hlb = htonl(STp->eod_frame_lfa);
+ header->ext_track_tb.dat_ext_trk_ey.last_pp = htonl(STp->eod_frame_ppos);
+ header->dat_fm_tab.fm_part_num = 0;
+ header->dat_fm_tab.fm_tab_ent_sz = 4;
+ header->dat_fm_tab.fm_tab_ent_cnt = htons(STp->filemark_cnt<OS_FM_TAB_MAX?
+ STp->filemark_cnt:OS_FM_TAB_MAX);
+
+ result = __osst_write_header(STp, aSRpnt, 0xbae, 5);
+ if (STp->update_frame_cntr == 0)
+ osst_write_filler(STp, aSRpnt, 0xbb3, 5);
+ result &= __osst_write_header(STp, aSRpnt, 5, 5);
+
+ if (locate_eod) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Locating back to eod frame addr %d\n", name, STp->eod_frame_ppos);
+#endif
+ osst_set_frame_position(STp, aSRpnt, STp->eod_frame_ppos, 0);
+ }
+ if (result)
+ printk(KERN_ERR "%s:E: Write header failed\n", name);
+ else {
+ memcpy(STp->application_sig, "LIN4", 4);
+ STp->linux_media = 1;
+ STp->linux_media_version = 4;
+ STp->header_ok = 1;
+ }
+ return result;
+}
+
+static int osst_reset_header(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ if (STp->header_cache != NULL)
+ memset(STp->header_cache, 0, sizeof(os_header_t));
+
+ STp->logical_blk_num = STp->frame_seq_number = 0;
+ STp->frame_in_buffer = 0;
+ STp->eod_frame_ppos = STp->first_data_ppos = 0x0000000A;
+ STp->filemark_cnt = 0;
+ STp->first_mark_ppos = STp->last_mark_ppos = STp->last_mark_lbn = -1;
+ return osst_write_header(STp, aSRpnt, 1);
+}
+
+static int __osst_analyze_headers(struct osst_tape * STp, struct scsi_request ** aSRpnt, int ppos)
+{
+ char * name = tape_name(STp);
+ os_header_t * header;
+ os_aux_t * aux;
+ char id_string[8];
+ int linux_media_version,
+ update_frame_cntr;
+
+ if (STp->raw)
+ return 1;
+
+ if (ppos == 5 || ppos == 0xbae || STp->buffer->syscall_result) {
+ if (osst_set_frame_position(STp, aSRpnt, ppos, 0))
+ printk(KERN_WARNING "%s:W: Couldn't position tape\n", name);
+ osst_wait_ready(STp, aSRpnt, 60 * 15, 0);
+ if (osst_initiate_read (STp, aSRpnt)) {
+ printk(KERN_WARNING "%s:W: Couldn't initiate read\n", name);
+ return 0;
+ }
+ }
+ if (osst_read_frame(STp, aSRpnt, 180)) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't read header frame\n", name);
+#endif
+ return 0;
+ }
+ header = (os_header_t *) STp->buffer->b_data; /* warning: only first segment addressable */
+ aux = STp->buffer->aux;
+ if (aux->frame_type != OS_FRAME_TYPE_HEADER) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping non-header frame (%d)\n", name, ppos);
+#endif
+ return 0;
+ }
+ if (ntohl(aux->frame_seq_num) != 0 ||
+ ntohl(aux->logical_blk_num) != 0 ||
+ aux->partition.partition_num != OS_CONFIG_PARTITION ||
+ ntohl(aux->partition.first_frame_ppos) != 0 ||
+ ntohl(aux->partition.last_frame_ppos) != 0xbb7 ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Invalid header frame (%d,%d,%d,%d,%d)\n", name,
+ ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num),
+ aux->partition.partition_num, ntohl(aux->partition.first_frame_ppos),
+ ntohl(aux->partition.last_frame_ppos));
+#endif
+ return 0;
+ }
+ if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0 &&
+ strncmp(header->ident_str, "ADR-SEQ", 7) != 0) {
+ strlcpy(id_string, header->ident_str, 8);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Invalid header identification string %s\n", name, id_string);
+#endif
+ return 0;
+ }
+ update_frame_cntr = ntohl(aux->update_frame_cntr);
+ if (update_frame_cntr < STp->update_frame_cntr) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame %d with update_frame_counter %d<%d\n",
+ name, ppos, update_frame_cntr, STp->update_frame_cntr);
+#endif
+ return 0;
+ }
+ if (header->major_rev != 1 || header->minor_rev != 4 ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: %s revision %d.%d detected (1.4 supported)\n",
+ name, (header->major_rev != 1 || header->minor_rev < 2 ||
+ header->minor_rev > 4 )? "Invalid" : "Warning:",
+ header->major_rev, header->minor_rev);
+#endif
+ if (header->major_rev != 1 || header->minor_rev < 2 || header->minor_rev > 4)
+ return 0;
+ }
+#if DEBUG
+ if (header->pt_par_num != 1)
+ printk(KERN_INFO "%s:W: %d partitions defined, only one supported\n",
+ name, header->pt_par_num);
+#endif
+ memcpy(id_string, aux->application_sig, 4);
+ id_string[4] = 0;
+ if (memcmp(id_string, "LIN", 3) == 0) {
+ STp->linux_media = 1;
+ linux_media_version = id_string[3] - '0';
+ if (linux_media_version != 4)
+ printk(KERN_INFO "%s:I: Linux media version %d detected (current 4)\n",
+ name, linux_media_version);
+ } else {
+ printk(KERN_WARNING "%s:W: Non Linux media detected (%s)\n", name, id_string);
+ return 0;
+ }
+ if (linux_media_version < STp->linux_media_version) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping frame %d with linux_media_version %d\n",
+ name, ppos, linux_media_version);
+#endif
+ return 0;
+ }
+ if (linux_media_version > STp->linux_media_version) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Frame %d sets linux_media_version to %d\n",
+ name, ppos, linux_media_version);
+#endif
+ memcpy(STp->application_sig, id_string, 5);
+ STp->linux_media_version = linux_media_version;
+ STp->update_frame_cntr = -1;
+ }
+ if (update_frame_cntr > STp->update_frame_cntr) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Frame %d sets update_frame_counter to %d\n",
+ name, ppos, update_frame_cntr);
+#endif
+ if (STp->header_cache == NULL) {
+ if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) {
+ printk(KERN_ERR "%s:E: Failed to allocate header cache\n", name);
+ return 0;
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Allocated memory for header cache\n", name);
+#endif
+ }
+ osst_copy_from_buffer(STp->buffer, (unsigned char *)STp->header_cache);
+ header = STp->header_cache; /* further accesses from cached (full) copy */
+
+ STp->wrt_pass_cntr = ntohs(header->partition[0].wrt_pass_cntr);
+ STp->first_data_ppos = ntohl(header->partition[0].first_frame_ppos);
+ STp->eod_frame_ppos = ntohl(header->partition[0].eod_frame_ppos);
+ STp->eod_frame_lfa = ntohl(header->ext_track_tb.dat_ext_trk_ey.last_hlb);
+ STp->filemark_cnt = ntohl(aux->filemark_cnt);
+ STp->first_mark_ppos = ntohl(aux->next_mark_ppos);
+ STp->last_mark_ppos = ntohl(aux->last_mark_ppos);
+ STp->last_mark_lbn = ntohl(aux->last_mark_lbn);
+ STp->update_frame_cntr = update_frame_cntr;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Detected write pass %d, update frame counter %d, filemark counter %d\n",
+ name, STp->wrt_pass_cntr, STp->update_frame_cntr, STp->filemark_cnt);
+ printk(OSST_DEB_MSG "%s:D: first data frame on tape = %d, last = %d, eod frame = %d\n", name,
+ STp->first_data_ppos,
+ ntohl(header->partition[0].last_frame_ppos),
+ ntohl(header->partition[0].eod_frame_ppos));
+ printk(OSST_DEB_MSG "%s:D: first mark on tape = %d, last = %d, eod frame = %d\n",
+ name, STp->first_mark_ppos, STp->last_mark_ppos, STp->eod_frame_ppos);
+#endif
+ if (header->minor_rev < 4 && STp->linux_media_version == 4) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Moving filemark list to ADR 1.4 location\n", name);
+#endif
+ memcpy((void *)header->dat_fm_tab.fm_tab_ent,
+ (void *)header->old_filemark_list, sizeof(header->dat_fm_tab.fm_tab_ent));
+ memset((void *)header->old_filemark_list, 0, sizeof(header->old_filemark_list));
+ }
+ if (header->minor_rev == 4 &&
+ (header->ext_trk_tb_off != htons(17192) ||
+ header->partition[0].partition_num != OS_DATA_PARTITION ||
+ header->partition[0].par_desc_ver != OS_PARTITION_VERSION ||
+ header->partition[0].last_frame_ppos != htonl(STp->capacity) ||
+ header->cfg_col_width != htonl(20) ||
+ header->dat_col_width != htonl(1500) ||
+ header->qfa_col_width != htonl(0) ||
+ header->ext_track_tb.nr_stream_part != 1 ||
+ header->ext_track_tb.et_ent_sz != 32 ||
+ header->ext_track_tb.dat_ext_trk_ey.et_part_num != OS_DATA_PARTITION ||
+ header->ext_track_tb.dat_ext_trk_ey.fmt != 1 ||
+ header->ext_track_tb.dat_ext_trk_ey.fm_tab_off != htons(17736) ||
+ header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi != 0 ||
+ header->ext_track_tb.dat_ext_trk_ey.last_pp != htonl(STp->eod_frame_ppos) ||
+ header->dat_fm_tab.fm_part_num != OS_DATA_PARTITION ||
+ header->dat_fm_tab.fm_tab_ent_sz != 4 ||
+ header->dat_fm_tab.fm_tab_ent_cnt !=
+ htons(STp->filemark_cnt<OS_FM_TAB_MAX?STp->filemark_cnt:OS_FM_TAB_MAX)))
+ printk(KERN_WARNING "%s:W: Failed consistency check ADR 1.4 format\n", name);
+
+ }
+
+ return 1;
+}
+
+static int osst_analyze_headers(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ int position, ppos;
+ int first, last;
+ int valid = 0;
+ char * name = tape_name(STp);
+
+ position = osst_get_frame_position(STp, aSRpnt);
+
+ if (STp->raw) {
+ STp->header_ok = STp->linux_media = 1;
+ STp->linux_media_version = 0;
+ return 1;
+ }
+ STp->header_ok = STp->linux_media = STp->linux_media_version = 0;
+ STp->wrt_pass_cntr = STp->update_frame_cntr = -1;
+ STp->eod_frame_ppos = STp->first_data_ppos = -1;
+ STp->first_mark_ppos = STp->last_mark_ppos = STp->last_mark_lbn = -1;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reading header\n", name);
+#endif
+
+ /* optimization for speed - if we are positioned at ppos 10, read second group first */
+ /* TODO try the ADR 1.1 locations for the second group if we have no valid one yet... */
+
+ first = position==10?0xbae: 5;
+ last = position==10?0xbb3:10;
+
+ for (ppos = first; ppos < last; ppos++)
+ if (__osst_analyze_headers(STp, aSRpnt, ppos))
+ valid = 1;
+
+ first = position==10? 5:0xbae;
+ last = position==10?10:0xbb3;
+
+ for (ppos = first; ppos < last; ppos++)
+ if (__osst_analyze_headers(STp, aSRpnt, ppos))
+ valid = 1;
+
+ if (!valid) {
+ printk(KERN_ERR "%s:E: Failed to find valid ADRL header, new media?\n", name);
+ STp->eod_frame_ppos = STp->first_data_ppos = 0;
+ osst_set_frame_position(STp, aSRpnt, 10, 0);
+ return 0;
+ }
+ if (position <= STp->first_data_ppos) {
+ position = STp->first_data_ppos;
+ STp->ps[0].drv_file = STp->ps[0].drv_block = STp->frame_seq_number = STp->logical_blk_num = 0;
+ }
+ osst_set_frame_position(STp, aSRpnt, position, 0);
+ STp->header_ok = 1;
+
+ return 1;
+}
+
+static int osst_verify_position(struct osst_tape * STp, struct scsi_request ** aSRpnt)
+{
+ int frame_position = STp->first_frame_position;
+ int frame_seq_numbr = STp->frame_seq_number;
+ int logical_blk_num = STp->logical_blk_num;
+ int halfway_frame = STp->frame_in_buffer;
+ int read_pointer = STp->buffer->read_pointer;
+ int prev_mark_ppos = -1;
+ int actual_mark_ppos, i, n;
+#if DEBUG
+ char * name = tape_name(STp);
+
+ printk(OSST_DEB_MSG "%s:D: Verify that the tape is really the one we think before writing\n", name);
+#endif
+ osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't get logical blk num in verify_position\n", name);
+#endif
+ return (-EIO);
+ }
+ if (STp->linux_media_version >= 4) {
+ for (i=0; i<STp->filemark_cnt; i++)
+ if ((n=ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) < frame_position)
+ prev_mark_ppos = n;
+ } else
+ prev_mark_ppos = frame_position - 1; /* usually - we don't really know */
+ actual_mark_ppos = STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER ?
+ frame_position - 1 : ntohl(STp->buffer->aux->last_mark_ppos);
+ if (frame_position != STp->first_frame_position ||
+ frame_seq_numbr != STp->frame_seq_number + (halfway_frame?0:1) ||
+ prev_mark_ppos != actual_mark_ppos ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Block mismatch: fppos %d-%d, fseq %d-%d, mark %d-%d\n", name,
+ STp->first_frame_position, frame_position,
+ STp->frame_seq_number + (halfway_frame?0:1),
+ frame_seq_numbr, actual_mark_ppos, prev_mark_ppos);
+#endif
+ return (-EIO);
+ }
+ if (halfway_frame) {
+ /* prepare buffer for append and rewrite on top of original */
+ osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
+ STp->buffer->buffer_bytes = read_pointer;
+ STp->ps[STp->partition].rw = ST_WRITING;
+ STp->dirty = 1;
+ }
+ STp->frame_in_buffer = halfway_frame;
+ STp->frame_seq_number = frame_seq_numbr;
+ STp->logical_blk_num = logical_blk_num;
+ return 0;
+}
+
+/* Acc. to OnStream, the vers. numbering is the following:
+ * X.XX for released versions (X=digit),
+ * XXXY for unreleased versions (Y=letter)
+ * Ordering 1.05 < 106A < 106B < ... < 106a < ... < 1.06
+ * This fn makes monoton numbers out of this scheme ...
+ */
+static unsigned int osst_parse_firmware_rev (const char * str)
+{
+ if (str[1] == '.') {
+ return (str[0]-'0')*10000
+ +(str[2]-'0')*1000
+ +(str[3]-'0')*100;
+ } else {
+ return (str[0]-'0')*10000
+ +(str[1]-'0')*1000
+ +(str[2]-'0')*100 - 100
+ +(str[3]-'@');
+ }
+}
+
+/*
+ * Configure the OnStream SCII tape drive for default operation
+ */
+static int osst_configure_onstream(struct osst_tape *STp, struct scsi_request ** aSRpnt)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ char * name = tape_name(STp);
+ struct scsi_request * SRpnt = * aSRpnt;
+ osst_mode_parameter_header_t * header;
+ osst_block_size_page_t * bs;
+ osst_capabilities_page_t * cp;
+ osst_tape_paramtr_page_t * prm;
+ int drive_buffer_size;
+
+ if (STp->ready != ST_READY) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Not Ready\n", name);
+#endif
+ return (-EIO);
+ }
+
+ if (STp->os_fw_rev < 10600) {
+ printk(KERN_INFO "%s:I: Old OnStream firmware revision detected (%s),\n", name, STp->device->rev);
+ printk(KERN_INFO "%s:I: an upgrade to version 1.06 or above is recommended\n", name);
+ }
+
+ /*
+ * Configure 32.5KB (data+aux) frame size.
+ * Get the current frame size from the block size mode page
+ */
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = 8;
+ cmd[2] = BLOCK_SIZE_PAGE;
+ cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
+ if (SRpnt == NULL) {
+#if DEBUG
+ printk(OSST_DEB_MSG "osst :D: Busy\n");
+#endif
+ return (-EBUSY);
+ }
+ *aSRpnt = SRpnt;
+ if ((STp->buffer)->syscall_result != 0) {
+ printk (KERN_ERR "%s:E: Can't get tape block size mode page\n", name);
+ return (-EIO);
+ }
+
+ header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+ bs = (osst_block_size_page_t *) ((STp->buffer)->b_data + sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: 32KB play back: %s\n", name, bs->play32 ? "Yes" : "No");
+ printk(OSST_DEB_MSG "%s:D: 32.5KB play back: %s\n", name, bs->play32_5 ? "Yes" : "No");
+ printk(OSST_DEB_MSG "%s:D: 32KB record: %s\n", name, bs->record32 ? "Yes" : "No");
+ printk(OSST_DEB_MSG "%s:D: 32.5KB record: %s\n", name, bs->record32_5 ? "Yes" : "No");
+#endif
+
+ /*
+ * Configure default auto columns mode, 32.5KB transfer mode
+ */
+ bs->one = 1;
+ bs->play32 = 0;
+ bs->play32_5 = 1;
+ bs->record32 = 0;
+ bs->record32_5 = 1;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = 0x10;
+ cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
+ *aSRpnt = SRpnt;
+ if ((STp->buffer)->syscall_result != 0) {
+ printk (KERN_ERR "%s:E: Couldn't set tape block size mode page\n", name);
+ return (-EIO);
+ }
+
+#if DEBUG
+ printk(KERN_INFO "%s:D: Drive Block Size changed to 32.5K\n", name);
+ /*
+ * In debug mode, we want to see as many errors as possible
+ * to test the error recovery mechanism.
+ */
+ osst_set_retries(STp, aSRpnt, 0);
+ SRpnt = * aSRpnt;
+#endif
+
+ /*
+ * Set vendor name to 'LIN4' for "Linux support version 4".
+ */
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = 0x10;
+ cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ header->mode_data_length = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH - 1;
+ header->medium_type = 0; /* Medium Type - ignoring */
+ header->dsp = 0; /* Reserved */
+ header->bdl = 0; /* Block Descriptor Length */
+
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = VENDOR_IDENT_PAGE | (1 << 7);
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 6;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 'L';
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 'I';
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 4] = 'N';
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 5] = '4';
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 6] = 0;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 7] = 0;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
+ *aSRpnt = SRpnt;
+
+ if ((STp->buffer)->syscall_result != 0) {
+ printk (KERN_ERR "%s:E: Couldn't set vendor name to %s\n", name,
+ (char *) ((STp->buffer)->b_data + MODE_HEADER_LENGTH + 2));
+ return (-EIO);
+ }
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = 8;
+ cmd[2] = CAPABILITIES_PAGE;
+ cmd[4] = CAPABILITIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
+ *aSRpnt = SRpnt;
+
+ if ((STp->buffer)->syscall_result != 0) {
+ printk (KERN_ERR "%s:E: Can't get capabilities page\n", name);
+ return (-EIO);
+ }
+
+ header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+ cp = (osst_capabilities_page_t *) ((STp->buffer)->b_data +
+ sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+ drive_buffer_size = ntohs(cp->buffer_size) / 2;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = 8;
+ cmd[2] = TAPE_PARAMTR_PAGE;
+ cmd[4] = TAPE_PARAMTR_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
+ *aSRpnt = SRpnt;
+
+ if ((STp->buffer)->syscall_result != 0) {
+ printk (KERN_ERR "%s:E: Can't get tape parameter page\n", name);
+ return (-EIO);
+ }
+
+ header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+ prm = (osst_tape_paramtr_page_t *) ((STp->buffer)->b_data +
+ sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+ STp->density = prm->density;
+ STp->capacity = ntohs(prm->segtrk) * ntohs(prm->trks);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Density %d, tape length: %dMB, drive buffer size: %dKB\n",
+ name, STp->density, STp->capacity / 32, drive_buffer_size);
+#endif
+
+ return 0;
+
+}
+
+
+/* Step over EOF if it has been inadvertently crossed (ioctl not used because
+ it messes up the block number). */
+static int cross_eof(struct osst_tape *STp, struct scsi_request ** aSRpnt, int forward)
+{
+ int result;
+ char * name = tape_name(STp);
+
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Stepping over filemark %s.\n",
+ name, forward ? "forward" : "backward");
+#endif
+
+ if (forward) {
+ /* assumes that the filemark is already read by the drive, so this is low cost */
+ result = osst_space_over_filemarks_forward_slow(STp, aSRpnt, MTFSF, 1);
+ }
+ else
+ /* assumes this is only called if we just read the filemark! */
+ result = osst_seek_logical_blk(STp, aSRpnt, STp->logical_blk_num - 1);
+
+ if (result < 0)
+ printk(KERN_WARNING "%s:W: Stepping over filemark %s failed.\n",
+ name, forward ? "forward" : "backward");
+
+ return result;
+}
+
+
+/* Get the tape position. */
+
+static int osst_get_frame_position(struct osst_tape *STp, struct scsi_request ** aSRpnt)
+{
+ unsigned char scmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ int result = 0;
+ char * name = tape_name(STp);
+
+ /* KG: We want to be able to use it for checking Write Buffer availability
+ * and thus don't want to risk to overwrite anything. Exchange buffers ... */
+ char mybuf[24];
+ char * olddata = STp->buffer->b_data;
+ int oldsize = STp->buffer->buffer_size;
+
+ if (STp->ready != ST_READY) return (-EIO);
+
+ memset (scmd, 0, MAX_COMMAND_SIZE);
+ scmd[0] = READ_POSITION;
+
+ STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+ SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 20, DMA_FROM_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+ if (!SRpnt) {
+ STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+ return (-EBUSY);
+ }
+ *aSRpnt = SRpnt;
+
+ if (STp->buffer->syscall_result)
+ result = ((SRpnt->sr_sense_buffer[2] & 0x0f) == 3) ? -EIO : -EINVAL; /* 3: Write Error */
+
+ if (result == -EINVAL)
+ printk(KERN_ERR "%s:E: Can't read tape position.\n", name);
+ else {
+ if (result == -EIO) { /* re-read position - this needs to preserve media errors */
+ unsigned char mysense[16];
+ memcpy (mysense, SRpnt->sr_sense_buffer, 16);
+ memset (scmd, 0, MAX_COMMAND_SIZE);
+ scmd[0] = READ_POSITION;
+ STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+ SRpnt = osst_do_scsi(SRpnt, STp, scmd, 20, DMA_FROM_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reread position, reason=[%02x:%02x:%02x], result=[%s%02x:%02x:%02x]\n",
+ name, mysense[2], mysense[12], mysense[13], STp->buffer->syscall_result?"":"ok:",
+ SRpnt->sr_sense_buffer[2],SRpnt->sr_sense_buffer[12],SRpnt->sr_sense_buffer[13]);
+#endif
+ if (!STp->buffer->syscall_result)
+ memcpy (SRpnt->sr_sense_buffer, mysense, 16);
+ else
+ printk(KERN_WARNING "%s:W: Double error in get position\n", name);
+ }
+ STp->first_frame_position = ((STp->buffer)->b_data[4] << 24)
+ + ((STp->buffer)->b_data[5] << 16)
+ + ((STp->buffer)->b_data[6] << 8)
+ + (STp->buffer)->b_data[7];
+ STp->last_frame_position = ((STp->buffer)->b_data[ 8] << 24)
+ + ((STp->buffer)->b_data[ 9] << 16)
+ + ((STp->buffer)->b_data[10] << 8)
+ + (STp->buffer)->b_data[11];
+ STp->cur_frames = (STp->buffer)->b_data[15];
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: Drive Positions: host %d, tape %d%s, buffer %d\n", name,
+ STp->first_frame_position, STp->last_frame_position,
+ ((STp->buffer)->b_data[0]&0x80)?" (BOP)":
+ ((STp->buffer)->b_data[0]&0x40)?" (EOP)":"",
+ STp->cur_frames);
+ }
+#endif
+ if (STp->cur_frames == 0 && STp->first_frame_position != STp->last_frame_position) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Correcting read position %d, %d, %d\n", name,
+ STp->first_frame_position, STp->last_frame_position, STp->cur_frames);
+#endif
+ STp->first_frame_position = STp->last_frame_position;
+ }
+ }
+ STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+
+ return (result == 0 ? STp->first_frame_position : result);
+}
+
+
+/* Set the tape block */
+static int osst_set_frame_position(struct osst_tape *STp, struct scsi_request ** aSRpnt, int ppos, int skip)
+{
+ unsigned char scmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ struct st_partstat * STps;
+ int result = 0;
+ int pp = (ppos == 3000 && !skip)? 0 : ppos;
+ char * name = tape_name(STp);
+
+ if (STp->ready != ST_READY) return (-EIO);
+
+ STps = &(STp->ps[STp->partition]);
+
+ if (ppos < 0 || ppos > STp->capacity) {
+ printk(KERN_WARNING "%s:W: Reposition request %d out of range\n", name, ppos);
+ pp = ppos = ppos < 0 ? 0 : (STp->capacity - 1);
+ result = (-EINVAL);
+ }
+
+ do {
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Setting ppos to %d.\n", name, pp);
+#endif
+ memset (scmd, 0, MAX_COMMAND_SIZE);
+ scmd[0] = SEEK_10;
+ scmd[1] = 1;
+ scmd[3] = (pp >> 24);
+ scmd[4] = (pp >> 16);
+ scmd[5] = (pp >> 8);
+ scmd[6] = pp;
+ if (skip)
+ scmd[9] = 0x80;
+
+ SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 0, DMA_NONE, STp->long_timeout,
+ MAX_RETRIES, 1);
+ if (!SRpnt)
+ return (-EBUSY);
+ *aSRpnt = SRpnt;
+
+ if ((STp->buffer)->syscall_result != 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: SEEK command from %d to %d failed.\n",
+ name, STp->first_frame_position, pp);
+#endif
+ result = (-EIO);
+ }
+ if (pp != ppos)
+ osst_wait_ready(STp, aSRpnt, 5 * 60, OSST_WAIT_POSITION_COMPLETE);
+ } while ((pp != ppos) && (pp = ppos));
+ STp->first_frame_position = STp->last_frame_position = ppos;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->rw = ST_IDLE;
+ STp->frame_in_buffer = 0;
+ return result;
+}
+
+static int osst_write_trailer(struct osst_tape *STp, struct scsi_request ** aSRpnt, int leave_at_EOT)
+{
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ int result = 0;
+
+ if (STp->write_type != OS_WRITE_NEW_MARK) {
+ /* true unless the user wrote the filemark for us */
+ result = osst_flush_drive_buffer(STp, aSRpnt);
+ if (result < 0) goto out;
+ result = osst_write_filemark(STp, aSRpnt);
+ if (result < 0) goto out;
+
+ if (STps->drv_file >= 0)
+ STps->drv_file++ ;
+ STps->drv_block = 0;
+ }
+ result = osst_write_eod(STp, aSRpnt);
+ osst_write_header(STp, aSRpnt, leave_at_EOT);
+
+ STps->eof = ST_FM;
+out:
+ return result;
+}
+
+/* osst versions of st functions - augmented and stripped to suit OnStream only */
+
+/* Flush the write buffer (never need to write if variable blocksize). */
+static int osst_flush_write_buffer(struct osst_tape *STp, struct scsi_request ** aSRpnt)
+{
+ int offset, transfer, blks = 0;
+ int result = 0;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt = *aSRpnt;
+ struct st_partstat * STps;
+ char * name = tape_name(STp);
+
+ if ((STp->buffer)->writing) {
+ if (SRpnt == (STp->buffer)->last_SRpnt)
+#if DEBUG
+ { printk(OSST_DEB_MSG
+ "%s:D: aSRpnt points to scsi_request that write_behind_check will release -- cleared\n", name);
+#endif
+ *aSRpnt = SRpnt = NULL;
+#if DEBUG
+ } else if (SRpnt)
+ printk(OSST_DEB_MSG
+ "%s:D: aSRpnt does not point to scsi_request that write_behind_check will release -- strange\n", name);
+#endif
+ osst_write_behind_check(STp);
+ if ((STp->buffer)->syscall_result) {
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Async write error (flush) %x.\n",
+ name, (STp->buffer)->midlevel_result);
+#endif
+ if ((STp->buffer)->midlevel_result == INT_MAX)
+ return (-ENOSPC);
+ return (-EIO);
+ }
+ }
+
+ result = 0;
+ if (STp->dirty == 1) {
+
+ STp->write_count++;
+ STps = &(STp->ps[STp->partition]);
+ STps->rw = ST_WRITING;
+ offset = STp->buffer->buffer_bytes;
+ blks = (offset + STp->block_size - 1) / STp->block_size;
+ transfer = OS_FRAME_SIZE;
+
+ if (offset < OS_DATA_SIZE)
+ osst_zero_buffer_tail(STp->buffer);
+
+ if (STp->poll)
+ if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, -50, 120))
+ result = osst_recover_wait_frame(STp, aSRpnt, 1);
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_6;
+ cmd[1] = 1;
+ cmd[4] = 1;
+
+ switch (STp->write_type) {
+ case OS_WRITE_DATA:
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Writing %d blocks to frame %d, lblks %d-%d\n",
+ name, blks, STp->frame_seq_number,
+ STp->logical_blk_num - blks, STp->logical_blk_num - 1);
+#endif
+ osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->frame_seq_number++,
+ STp->logical_blk_num - blks, STp->block_size, blks);
+ break;
+ case OS_WRITE_EOD:
+ osst_init_aux(STp, OS_FRAME_TYPE_EOD, STp->frame_seq_number++,
+ STp->logical_blk_num, 0, 0);
+ break;
+ case OS_WRITE_NEW_MARK:
+ osst_init_aux(STp, OS_FRAME_TYPE_MARKER, STp->frame_seq_number++,
+ STp->logical_blk_num++, 0, blks=1);
+ break;
+ case OS_WRITE_HEADER:
+ osst_init_aux(STp, OS_FRAME_TYPE_HEADER, 0, 0, 0, blks=0);
+ break;
+ default: /* probably FILLER */
+ osst_init_aux(STp, OS_FRAME_TYPE_FILL, 0, 0, 0, 0);
+ }
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Flushing %d bytes, Transfering %d bytes in %d lblocks.\n",
+ name, offset, transfer, blks);
+#endif
+
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
+ STp->timeout, MAX_RETRIES, 1);
+ *aSRpnt = SRpnt;
+ if (!SRpnt)
+ return (-EBUSY);
+
+ if ((STp->buffer)->syscall_result != 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: write sense [0]=0x%02x [2]=%02x [12]=%02x [13]=%02x\n",
+ name, SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[2],
+ SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+#endif
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x40) && /* FIXME - SC-30 drive doesn't assert EOM bit */
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == NO_SENSE) {
+ STp->dirty = 0;
+ (STp->buffer)->buffer_bytes = 0;
+ result = (-ENOSPC);
+ }
+ else {
+ if (osst_write_error_recovery(STp, aSRpnt, 1)) {
+ printk(KERN_ERR "%s:E: Error on flush write.\n", name);
+ result = (-EIO);
+ }
+ }
+ STps->drv_block = (-1); /* FIXME - even if write recovery succeeds? */
+ }
+ else {
+ STp->first_frame_position++;
+ STp->dirty = 0;
+ (STp->buffer)->buffer_bytes = 0;
+ }
+ }
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Exit flush write buffer with code %d\n", name, result);
+#endif
+ return result;
+}
+
+
+/* Flush the tape buffer. The tape will be positioned correctly unless
+ seek_next is true. */
+static int osst_flush_buffer(struct osst_tape * STp, struct scsi_request ** aSRpnt, int seek_next)
+{
+ struct st_partstat * STps;
+ int backspace = 0, result = 0;
+#if DEBUG
+ char * name = tape_name(STp);
+#endif
+
+ /*
+ * If there was a bus reset, block further access
+ * to this device.
+ */
+ if( STp->pos_unknown)
+ return (-EIO);
+
+ if (STp->ready != ST_READY)
+ return 0;
+
+ STps = &(STp->ps[STp->partition]);
+ if (STps->rw == ST_WRITING || STp->dirty) { /* Writing */
+ STp->write_type = OS_WRITE_DATA;
+ return osst_flush_write_buffer(STp, aSRpnt);
+ }
+ if (STp->block_size == 0)
+ return 0;
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reached flush (read) buffer\n", name);
+#endif
+
+ if (!STp->can_bsr) {
+ backspace = ((STp->buffer)->buffer_bytes + (STp->buffer)->read_pointer) / STp->block_size -
+ ((STp->buffer)->read_pointer + STp->block_size - 1 ) / STp->block_size ;
+ (STp->buffer)->buffer_bytes = 0;
+ (STp->buffer)->read_pointer = 0;
+ STp->frame_in_buffer = 0; /* FIXME is this relevant w. OSST? */
+ }
+
+ if (!seek_next) {
+ if (STps->eof == ST_FM_HIT) {
+ result = cross_eof(STp, aSRpnt, 0); /* Back over the EOF hit */
+ if (!result)
+ STps->eof = ST_NOEOF;
+ else {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ }
+ }
+ if (!result && backspace > 0) /* TODO -- design and run a test case for this */
+ result = osst_seek_logical_blk(STp, aSRpnt, STp->logical_blk_num - backspace);
+ }
+ else if (STps->eof == ST_FM_HIT) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ }
+
+ return result;
+}
+
+static int osst_write_frame(struct osst_tape * STp, struct scsi_request ** aSRpnt, int synchronous)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt;
+ int blks;
+#if DEBUG
+ char * name = tape_name(STp);
+#endif
+
+ if ((!STp-> raw) && (STp->first_frame_position == 0xbae)) { /* _must_ preserve buffer! */
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Reaching config partition.\n", name);
+#endif
+ if (osst_flush_drive_buffer(STp, aSRpnt) < 0) {
+ return (-EIO);
+ }
+ /* error recovery may have bumped us past the header partition */
+ if (osst_get_frame_position(STp, aSRpnt) < 0xbb8) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Skipping over config partition.\n", name);
+#endif
+ osst_position_tape_and_confirm(STp, aSRpnt, 0xbb8);
+ }
+ }
+
+ if (STp->poll)
+ if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, -48, 120))
+ if (osst_recover_wait_frame(STp, aSRpnt, 1))
+ return (-EIO);
+
+// osst_build_stats(STp, &SRpnt);
+
+ STp->ps[STp->partition].rw = ST_WRITING;
+ STp->write_type = OS_WRITE_DATA;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_6;
+ cmd[1] = 1;
+ cmd[4] = 1; /* one frame at a time... */
+ blks = STp->buffer->buffer_bytes / STp->block_size;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Writing %d blocks to frame %d, lblks %d-%d\n", name, blks,
+ STp->frame_seq_number, STp->logical_blk_num - blks, STp->logical_blk_num - 1);
+#endif
+ osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->frame_seq_number++,
+ STp->logical_blk_num - blks, STp->block_size, blks);
+
+#if DEBUG
+ if (!synchronous)
+ STp->write_pending = 1;
+#endif
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_TO_DEVICE, STp->timeout,
+ MAX_RETRIES, synchronous);
+ if (!SRpnt)
+ return (-EBUSY);
+ *aSRpnt = SRpnt;
+
+ if (synchronous) {
+ if (STp->buffer->syscall_result != 0) {
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Error on write:\n", name);
+#endif
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x40)) {
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW)
+ return (-ENOSPC);
+ }
+ else {
+ if (osst_write_error_recovery(STp, aSRpnt, 1))
+ return (-EIO);
+ }
+ }
+ else
+ STp->first_frame_position++;
+ }
+
+ STp->write_count++;
+
+ return 0;
+}
+
+/* Lock or unlock the drive door. Don't use when struct scsi_request allocated. */
+static int do_door_lock(struct osst_tape * STp, int do_lock)
+{
+ int retval, cmd;
+
+ cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: %socking drive door.\n", tape_name(STp), do_lock ? "L" : "Unl");
+#endif
+ retval = scsi_ioctl(STp->device, cmd, NULL);
+ if (!retval) {
+ STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
+ }
+ else {
+ STp->door_locked = ST_LOCK_FAILS;
+ }
+ return retval;
+}
+
+/* Set the internal state after reset */
+static void reset_state(struct osst_tape *STp)
+{
+ int i;
+ struct st_partstat *STps;
+
+ STp->pos_unknown = 0;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = -1;
+ STps->drv_file = -1;
+ }
+}
+
+
+/* Entry points to osst */
+
+/* Write command */
+static ssize_t osst_write(struct file * filp, const char __user * buf, size_t count, loff_t *ppos)
+{
+ ssize_t total, retval = 0;
+ ssize_t i, do_count, blks, transfer;
+ int write_threshold;
+ int doing_write = 0;
+ const char __user * b_point;
+ struct scsi_request * SRpnt = NULL;
+ struct st_modedef * STm;
+ struct st_partstat * STps;
+ struct osst_tape * STp = filp->private_data;
+ char * name = tape_name(STp);
+
+
+ if (down_interruptible(&STp->lock))
+ return (-ERESTARTSYS);
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if( !scsi_block_when_processing_errors(STp->device) ) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ retval = (-ENOMEDIUM);
+ else
+ retval = (-EIO);
+ goto out;
+ }
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined) {
+ retval = (-ENXIO);
+ goto out;
+ }
+ if (count == 0)
+ goto out;
+
+ /*
+ * If there was a bus reset, block further access
+ * to this device.
+ */
+ if (STp->pos_unknown) {
+ retval = (-EIO);
+ goto out;
+ }
+
+#if DEBUG
+ if (!STp->in_use) {
+ printk(OSST_DEB_MSG "%s:D: Incorrect device.\n", name);
+ retval = (-EIO);
+ goto out;
+ }
+#endif
+
+ if (STp->write_prot) {
+ retval = (-EACCES);
+ goto out;
+ }
+
+ /* Write must be integral number of blocks */
+ if (STp->block_size != 0 && (count % STp->block_size) != 0) {
+ printk(KERN_ERR "%s:E: Write (%Zd bytes) not multiple of tape block size (%d%c).\n",
+ name, count, STp->block_size<1024?
+ STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k');
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ if (STp->first_frame_position >= STp->capacity - OSST_EOM_RESERVE) {
+ printk(KERN_ERR "%s:E: Write truncated at EOM early warning (frame %d).\n",
+ name, STp->first_frame_position);
+ retval = (-ENOSPC);
+ goto out;
+ }
+
+ if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED && !do_door_lock(STp, 1))
+ STp->door_locked = ST_LOCKED_AUTO;
+
+ STps = &(STp->ps[STp->partition]);
+
+ if (STps->rw == ST_READING) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Switching from read to write at file %d, block %d\n", name,
+ STps->drv_file, STps->drv_block);
+#endif
+ retval = osst_flush_buffer(STp, &SRpnt, 0);
+ if (retval)
+ goto out;
+ STps->rw = ST_IDLE;
+ }
+ if (STps->rw != ST_WRITING) {
+ /* Are we totally rewriting this tape? */
+ if (!STp->header_ok ||
+ (STp->first_frame_position == STp->first_data_ppos && STps->drv_block < 0) ||
+ (STps->drv_file == 0 && STps->drv_block == 0)) {
+ STp->wrt_pass_cntr++;
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Allocating next write pass counter: %d\n",
+ name, STp->wrt_pass_cntr);
+#endif
+ osst_reset_header(STp, &SRpnt);
+ STps->drv_file = STps->drv_block = 0;
+ }
+ /* Do we know where we'll be writing on the tape? */
+ else {
+ if ((STp->fast_open && osst_verify_position(STp, &SRpnt)) ||
+ STps->drv_file < 0 || STps->drv_block < 0) {
+ if (STp->first_frame_position == STp->eod_frame_ppos) { /* at EOD */
+ STps->drv_file = STp->filemark_cnt;
+ STps->drv_block = 0;
+ }
+ else {
+ /* We have no idea where the tape is positioned - give up */
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: Cannot write at indeterminate position.\n", name);
+#endif
+ retval = (-EIO);
+ goto out;
+ }
+ }
+ if ((STps->drv_file + STps->drv_block) > 0 && STps->drv_file < STp->filemark_cnt) {
+ STp->filemark_cnt = STps->drv_file;
+ STp->last_mark_ppos =
+ ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt-1]);
+ printk(KERN_WARNING
+ "%s:W: Overwriting file %d with old write pass counter %d\n",
+ name, STps->drv_file, STp->wrt_pass_cntr);
+ printk(KERN_WARNING
+ "%s:W: may lead to stale data being accepted on reading back!\n",
+ name);
+#if DEBUG
+ printk(OSST_DEB_MSG
+ "%s:D: resetting filemark count to %d and last mark ppos,lbn to %d,%d\n",
+ name, STp->filemark_cnt, STp->last_mark_ppos, STp->last_mark_lbn);
+#endif
+ }
+ }
+ STp->fast_open = 0;
+ }
+ if (!STp->header_ok) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Write cannot proceed without valid headers\n", name);
+#endif
+ retval = (-EIO);
+ goto out;
+ }
+
+ if ((STp->buffer)->writing) {
+if (SRpnt) printk(KERN_ERR "%s:A: Not supposed to have SRpnt at line %d\n", name, __LINE__);
+ osst_write_behind_check(STp);
+ if ((STp->buffer)->syscall_result) {
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Async write error (write) %x.\n", name,
+ (STp->buffer)->midlevel_result);
+#endif
+ if ((STp->buffer)->midlevel_result == INT_MAX)
+ STps->eof = ST_EOM_OK;
+ else
+ STps->eof = ST_EOM_ERROR;
+ }
+ }
+ if (STps->eof == ST_EOM_OK) {
+ retval = (-ENOSPC);
+ goto out;
+ }
+ else if (STps->eof == ST_EOM_ERROR) {
+ retval = (-EIO);
+ goto out;
+ }
+
+ /* Check the buffer readability in cases where copy_user might catch
+ the problems after some tape movement. */
+ if ((copy_from_user(&i, buf, 1) != 0 ||
+ copy_from_user(&i, buf + count - 1, 1) != 0)) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ if (!STm->do_buffer_writes) {
+ write_threshold = 1;
+ }
+ else
+ write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
+ if (!STm->do_async_writes)
+ write_threshold--;
+
+ total = count;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Writing %d bytes to file %d block %d lblk %d fseq %d fppos %d\n",
+ name, count, STps->drv_file, STps->drv_block,
+ STp->logical_blk_num, STp->frame_seq_number, STp->first_frame_position);
+#endif
+ b_point = buf;
+ while ((STp->buffer)->buffer_bytes + count > write_threshold)
+ {
+ doing_write = 1;
+ do_count = (STp->buffer)->buffer_blocks * STp->block_size -
+ (STp->buffer)->buffer_bytes;
+ if (do_count > count)
+ do_count = count;
+
+ i = append_to_buffer(b_point, STp->buffer, do_count);
+ if (i) {
+ retval = i;
+ goto out;
+ }
+
+ blks = do_count / STp->block_size;
+ STp->logical_blk_num += blks; /* logical_blk_num is incremented as data is moved from user */
+
+ i = osst_write_frame(STp, &SRpnt, 1);
+
+ if (i == (-ENOSPC)) {
+ transfer = STp->buffer->writing; /* FIXME -- check this logic */
+ if (transfer <= do_count) {
+ filp->f_pos += do_count - transfer;
+ count -= do_count - transfer;
+ if (STps->drv_block >= 0) {
+ STps->drv_block += (do_count - transfer) / STp->block_size;
+ }
+ STps->eof = ST_EOM_OK;
+ retval = (-ENOSPC); /* EOM within current request */
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: EOM with %d bytes unwritten.\n",
+ name, transfer);
+#endif
+ }
+ else {
+ STps->eof = ST_EOM_ERROR;
+ STps->drv_block = (-1); /* Too cautious? */
+ retval = (-EIO); /* EOM for old data */
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: EOM with lost data.\n", name);
+#endif
+ }
+ }
+ else
+ retval = i;
+
+ if (retval < 0) {
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ }
+ STp->buffer->buffer_bytes = 0;
+ STp->dirty = 0;
+ if (count < total)
+ retval = total - count;
+ goto out;
+ }
+
+ filp->f_pos += do_count;
+ b_point += do_count;
+ count -= do_count;
+ if (STps->drv_block >= 0) {
+ STps->drv_block += blks;
+ }
+ STp->buffer->buffer_bytes = 0;
+ STp->dirty = 0;
+ } /* end while write threshold exceeded */
+
+ if (count != 0) {
+ STp->dirty = 1;
+ i = append_to_buffer(b_point, STp->buffer, count);
+ if (i) {
+ retval = i;
+ goto out;
+ }
+ blks = count / STp->block_size;
+ STp->logical_blk_num += blks;
+ if (STps->drv_block >= 0) {
+ STps->drv_block += blks;
+ }
+ filp->f_pos += count;
+ count = 0;
+ }
+
+ if (doing_write && (STp->buffer)->syscall_result != 0) {
+ retval = (STp->buffer)->syscall_result;
+ goto out;
+ }
+
+ if (STm->do_async_writes && ((STp->buffer)->buffer_bytes >= STp->write_threshold)) {
+ /* Schedule an asynchronous write */
+ (STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
+ STp->block_size) * STp->block_size;
+ STp->dirty = !((STp->buffer)->writing ==
+ (STp->buffer)->buffer_bytes);
+
+ i = osst_write_frame(STp, &SRpnt, 0);
+ if (i < 0) {
+ retval = (-EIO);
+ goto out;
+ }
+ SRpnt = NULL; /* Prevent releasing this request! */
+ }
+ STps->at_sm &= (total == 0);
+ if (total > 0)
+ STps->eof = ST_NOEOF;
+
+ retval = total;
+
+out:
+ if (SRpnt != NULL) scsi_release_request(SRpnt);
+
+ up(&STp->lock);
+
+ return retval;
+}
+
+
+/* Read command */
+static ssize_t osst_read(struct file * filp, char __user * buf, size_t count, loff_t *ppos)
+{
+ ssize_t total, retval = 0;
+ ssize_t i, transfer;
+ int special;
+ struct st_modedef * STm;
+ struct st_partstat * STps;
+ struct scsi_request * SRpnt = NULL;
+ struct osst_tape * STp = filp->private_data;
+ char * name = tape_name(STp);
+
+
+ if (down_interruptible(&STp->lock))
+ return (-ERESTARTSYS);
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if( !scsi_block_when_processing_errors(STp->device) ) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ retval = (-ENOMEDIUM);
+ else
+ retval = (-EIO);
+ goto out;
+ }
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined) {
+ retval = (-ENXIO);
+ goto out;
+ }
+#if DEBUG
+ if (!STp->in_use) {
+ printk(OSST_DEB_MSG "%s:D: Incorrect device.\n", name);
+ retval = (-EIO);
+ goto out;
+ }
+#endif
+ /* Must have initialized medium */
+ if (!STp->header_ok) {
+ retval = (-EIO);
+ goto out;
+ }
+
+ if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED && !do_door_lock(STp, 1))
+ STp->door_locked = ST_LOCKED_AUTO;
+
+ STps = &(STp->ps[STp->partition]);
+ if (STps->rw == ST_WRITING) {
+ retval = osst_flush_buffer(STp, &SRpnt, 0);
+ if (retval)
+ goto out;
+ STps->rw = ST_IDLE;
+ /* FIXME -- this may leave the tape without EOD and up2date headers */
+ }
+
+ if ((count % STp->block_size) != 0) {
+ printk(KERN_WARNING
+ "%s:W: Read (%Zd bytes) not multiple of tape block size (%d%c).\n", name, count,
+ STp->block_size<1024?STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k');
+ }
+
+#if DEBUG
+ if (debugging && STps->eof != ST_NOEOF)
+ printk(OSST_DEB_MSG "%s:D: EOF/EOM flag up (%d). Bytes %d\n", name,
+ STps->eof, (STp->buffer)->buffer_bytes);
+#endif
+ if ((STp->buffer)->buffer_bytes == 0 &&
+ STps->eof >= ST_EOD_1) {
+ if (STps->eof < ST_EOD) {
+ STps->eof += 1;
+ retval = 0;
+ goto out;
+ }
+ retval = (-EIO); /* EOM or Blank Check */
+ goto out;
+ }
+
+ /* Check the buffer writability before any tape movement. Don't alter
+ buffer data. */
+ if (copy_from_user(&i, buf, 1) != 0 ||
+ copy_to_user (buf, &i, 1) != 0 ||
+ copy_from_user(&i, buf + count - 1, 1) != 0 ||
+ copy_to_user (buf + count - 1, &i, 1) != 0) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ /* Loop until enough data in buffer or a special condition found */
+ for (total = 0, special = 0; total < count - STp->block_size + 1 && !special; ) {
+
+ /* Get new data if the buffer is empty */
+ if ((STp->buffer)->buffer_bytes == 0) {
+ if (STps->eof == ST_FM_HIT)
+ break;
+ special = osst_get_logical_frame(STp, &SRpnt, STp->frame_seq_number, 0);
+ if (special < 0) { /* No need to continue read */
+ STp->frame_in_buffer = 0;
+ retval = special;
+ goto out;
+ }
+ }
+
+ /* Move the data from driver buffer to user buffer */
+ if ((STp->buffer)->buffer_bytes > 0) {
+#if DEBUG
+ if (debugging && STps->eof != ST_NOEOF)
+ printk(OSST_DEB_MSG "%s:D: EOF up (%d). Left %d, needed %d.\n", name,
+ STps->eof, (STp->buffer)->buffer_bytes, count - total);
+#endif
+ /* force multiple of block size, note block_size may have been adjusted */
+ transfer = (((STp->buffer)->buffer_bytes < count - total ?
+ (STp->buffer)->buffer_bytes : count - total)/
+ STp->block_size) * STp->block_size;
+
+ if (transfer == 0) {
+ printk(KERN_WARNING
+ "%s:W: Nothing can be transfered, requested %Zd, tape block size (%d%c).\n",
+ name, count, STp->block_size < 1024?
+ STp->block_size:STp->block_size/1024,
+ STp->block_size<1024?'b':'k');
+ break;
+ }
+ i = from_buffer(STp->buffer, buf, transfer);
+ if (i) {
+ retval = i;
+ goto out;
+ }
+ STp->logical_blk_num += transfer / STp->block_size;
+ STps->drv_block += transfer / STp->block_size;
+ filp->f_pos += transfer;
+ buf += transfer;
+ total += transfer;
+ }
+
+ if ((STp->buffer)->buffer_bytes == 0) {
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Finished with frame %d\n",
+ name, STp->frame_seq_number);
+#endif
+ STp->frame_in_buffer = 0;
+ STp->frame_seq_number++; /* frame to look for next time */
+ }
+ } /* for (total = 0, special = 0; total < count && !special; ) */
+
+ /* Change the eof state if no data from tape or buffer */
+ if (total == 0) {
+ if (STps->eof == ST_FM_HIT) {
+ STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD_2:ST_FM;
+ STps->drv_block = 0;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ }
+ else if (STps->eof == ST_EOD_1) {
+ STps->eof = ST_EOD_2;
+ if (STps->drv_block > 0 && STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ }
+ else if (STps->eof == ST_EOD_2)
+ STps->eof = ST_EOD;
+ }
+ else if (STps->eof == ST_FM)
+ STps->eof = ST_NOEOF;
+
+ retval = total;
+
+out:
+ if (SRpnt != NULL) scsi_release_request(SRpnt);
+
+ up(&STp->lock);
+
+ return retval;
+}
+
+
+/* Set the driver options */
+static void osst_log_options(struct osst_tape *STp, struct st_modedef *STm, char *name)
+{
+ printk(KERN_INFO
+"%s:I: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+ name, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+ STm->do_read_ahead);
+ printk(KERN_INFO
+"%s:I: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
+ name, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
+ printk(KERN_INFO
+"%s:I: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+ name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+ STp->scsi2_logical);
+ printk(KERN_INFO
+"%s:I: sysv: %d\n", name, STm->sysv);
+#if DEBUG
+ printk(KERN_INFO
+ "%s:D: debugging: %d\n",
+ name, debugging);
+#endif
+}
+
+
+static int osst_set_options(struct osst_tape *STp, long options)
+{
+ int value;
+ long code;
+ struct st_modedef * STm;
+ char * name = tape_name(STp);
+
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined) {
+ memcpy(STm, &(STp->modes[0]), sizeof(*STm));
+ modes_defined = 1;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Initialized mode %d definition from mode 0\n",
+ name, STp->current_mode);
+#endif
+ }
+
+ code = options & MT_ST_OPTIONS;
+ if (code == MT_ST_BOOLEANS) {
+ STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+ STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
+ STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
+ STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
+ STp->two_fm = (options & MT_ST_TWO_FM) != 0;
+ STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
+ STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
+ STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
+ STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
+ if ((STp->device)->scsi_level >= SCSI_2)
+ STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
+ STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
+ STm->sysv = (options & MT_ST_SYSV) != 0;
+#if DEBUG
+ debugging = (options & MT_ST_DEBUGGING) != 0;
+#endif
+ osst_log_options(STp, STm, name);
+ }
+ else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
+ value = (code == MT_ST_SETBOOLEANS);
+ if ((options & MT_ST_BUFFER_WRITES) != 0)
+ STm->do_buffer_writes = value;
+ if ((options & MT_ST_ASYNC_WRITES) != 0)
+ STm->do_async_writes = value;
+ if ((options & MT_ST_DEF_WRITES) != 0)
+ STm->defaults_for_writes = value;
+ if ((options & MT_ST_READ_AHEAD) != 0)
+ STm->do_read_ahead = value;
+ if ((options & MT_ST_TWO_FM) != 0)
+ STp->two_fm = value;
+ if ((options & MT_ST_FAST_MTEOM) != 0)
+ STp->fast_mteom = value;
+ if ((options & MT_ST_AUTO_LOCK) != 0)
+ STp->do_auto_lock = value;
+ if ((options & MT_ST_CAN_BSR) != 0)
+ STp->can_bsr = value;
+ if ((options & MT_ST_NO_BLKLIMS) != 0)
+ STp->omit_blklims = value;
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (options & MT_ST_CAN_PARTITIONS) != 0)
+ STp->can_partitions = value;
+ if ((options & MT_ST_SCSI2LOGICAL) != 0)
+ STp->scsi2_logical = value;
+ if ((options & MT_ST_SYSV) != 0)
+ STm->sysv = value;
+#if DEBUG
+ if ((options & MT_ST_DEBUGGING) != 0)
+ debugging = value;
+#endif
+ osst_log_options(STp, STm, name);
+ }
+ else if (code == MT_ST_WRITE_THRESHOLD) {
+ value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
+ if (value < 1 || value > osst_buffer_size) {
+ printk(KERN_WARNING "%s:W: Write threshold %d too small or too large.\n",
+ name, value);
+ return (-EIO);
+ }
+ STp->write_threshold = value;
+ printk(KERN_INFO "%s:I: Write threshold set to %d bytes.\n",
+ name, value);
+ }
+ else if (code == MT_ST_DEF_BLKSIZE) {
+ value = (options & ~MT_ST_OPTIONS);
+ if (value == ~MT_ST_OPTIONS) {
+ STm->default_blksize = (-1);
+ printk(KERN_INFO "%s:I: Default block size disabled.\n", name);
+ }
+ else {
+ if (value < 512 || value > OS_DATA_SIZE || OS_DATA_SIZE % value) {
+ printk(KERN_WARNING "%s:W: Default block size cannot be set to %d.\n",
+ name, value);
+ return (-EINVAL);
+ }
+ STm->default_blksize = value;
+ printk(KERN_INFO "%s:I: Default block size set to %d bytes.\n",
+ name, STm->default_blksize);
+ }
+ }
+ else if (code == MT_ST_TIMEOUTS) {
+ value = (options & ~MT_ST_OPTIONS);
+ if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
+ STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
+ printk(KERN_INFO "%s:I: Long timeout set to %d seconds.\n", name,
+ (value & ~MT_ST_SET_LONG_TIMEOUT));
+ }
+ else {
+ STp->timeout = value * HZ;
+ printk(KERN_INFO "%s:I: Normal timeout set to %d seconds.\n", name, value);
+ }
+ }
+ else if (code == MT_ST_DEF_OPTIONS) {
+ code = (options & ~MT_ST_CLEAR_DEFAULT);
+ value = (options & MT_ST_CLEAR_DEFAULT);
+ if (code == MT_ST_DEF_DENSITY) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STm->default_density = (-1);
+ printk(KERN_INFO "%s:I: Density default disabled.\n", name);
+ }
+ else {
+ STm->default_density = value & 0xff;
+ printk(KERN_INFO "%s:I: Density default set to %x\n",
+ name, STm->default_density);
+ }
+ }
+ else if (code == MT_ST_DEF_DRVBUFFER) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STp->default_drvbuffer = 0xff;
+ printk(KERN_INFO "%s:I: Drive buffer default disabled.\n", name);
+ }
+ else {
+ STp->default_drvbuffer = value & 7;
+ printk(KERN_INFO "%s:I: Drive buffer default set to %x\n",
+ name, STp->default_drvbuffer);
+ }
+ }
+ else if (code == MT_ST_DEF_COMPRESSION) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STm->default_compression = ST_DONT_TOUCH;
+ printk(KERN_INFO "%s:I: Compression default disabled.\n", name);
+ }
+ else {
+ STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+ printk(KERN_INFO "%s:I: Compression default set to %x\n",
+ name, (value & 1));
+ }
+ }
+ }
+ else
+ return (-EIO);
+
+ return 0;
+}
+
+
+/* Internal ioctl function */
+static int osst_int_ioctl(struct osst_tape * STp, struct scsi_request ** aSRpnt,
+ unsigned int cmd_in, unsigned long arg)
+{
+ int timeout;
+ long ltmp;
+ int i, ioctl_result;
+ int chg_eof = 1;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt = * aSRpnt;
+ struct st_partstat * STps;
+ int fileno, blkno, at_sm, frame_seq_numbr, logical_blk_num;
+ int datalen = 0, direction = DMA_NONE;
+ char * name = tape_name(STp);
+
+ if (STp->ready != ST_READY && cmd_in != MTLOAD) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
+ timeout = STp->long_timeout;
+ STps = &(STp->ps[STp->partition]);
+ fileno = STps->drv_file;
+ blkno = STps->drv_block;
+ at_sm = STps->at_sm;
+ frame_seq_numbr = STp->frame_seq_number;
+ logical_blk_num = STp->logical_blk_num;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ switch (cmd_in) {
+ case MTFSFM:
+ chg_eof = 0; /* Changed from the FSF after this */
+ case MTFSF:
+ if (STp->raw)
+ return (-EIO);
+ if (STp->linux_media)
+ ioctl_result = osst_space_over_filemarks_forward_fast(STp, &SRpnt, cmd_in, arg);
+ else
+ ioctl_result = osst_space_over_filemarks_forward_slow(STp, &SRpnt, cmd_in, arg);
+ if (fileno >= 0)
+ fileno += arg;
+ blkno = 0;
+ at_sm &= (arg == 0);
+ goto os_bypass;
+
+ case MTBSF:
+ chg_eof = 0; /* Changed from the FSF after this */
+ case MTBSFM:
+ if (STp->raw)
+ return (-EIO);
+ ioctl_result = osst_space_over_filemarks_backward(STp, &SRpnt, cmd_in, arg);
+ if (fileno >= 0)
+ fileno -= arg;
+ blkno = (-1); /* We can't know the block number */
+ at_sm &= (arg == 0);
+ goto os_bypass;
+
+ case MTFSR:
+ case MTBSR:
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Skipping %lu blocks %s from logical block %d\n",
+ name, arg, cmd_in==MTFSR?"forward":"backward", logical_blk_num);
+#endif
+ if (cmd_in == MTFSR) {
+ logical_blk_num += arg;
+ if (blkno >= 0) blkno += arg;
+ }
+ else {
+ logical_blk_num -= arg;
+ if (blkno >= 0) blkno -= arg;
+ }
+ ioctl_result = osst_seek_logical_blk(STp, &SRpnt, logical_blk_num);
+ fileno = STps->drv_file;
+ blkno = STps->drv_block;
+ at_sm &= (arg == 0);
+ goto os_bypass;
+
+ case MTFSS:
+ cmd[0] = SPACE;
+ cmd[1] = 0x04; /* Space Setmarks */ /* FIXME -- OS can't do this? */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Spacing tape forward %d setmarks.\n", name,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+ if (arg != 0) {
+ blkno = fileno = (-1);
+ at_sm = 1;
+ }
+ break;
+ case MTBSS:
+ cmd[0] = SPACE;
+ cmd[1] = 0x04; /* Space Setmarks */ /* FIXME -- OS can't do this? */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+#if DEBUG
+ if (debugging) {
+ if (cmd[2] & 0x80)
+ ltmp = 0xff000000;
+ ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+ printk(OSST_DEB_MSG "%s:D: Spacing tape backward %ld setmarks.\n",
+ name, (-ltmp));
+ }
+#endif
+ if (arg != 0) {
+ blkno = fileno = (-1);
+ at_sm = 1;
+ }
+ break;
+ case MTWEOF:
+ if ((STps->rw == ST_WRITING || STp->dirty) && !STp->pos_unknown) {
+ STp->write_type = OS_WRITE_DATA;
+ ioctl_result = osst_flush_write_buffer(STp, &SRpnt);
+ } else
+ ioctl_result = 0;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Writing %ld filemark(s).\n", name, arg);
+#endif
+ for (i=0; i<arg; i++)
+ ioctl_result |= osst_write_filemark(STp, &SRpnt);
+ if (fileno >= 0) fileno += arg;
+ if (blkno >= 0) blkno = 0;
+ goto os_bypass;
+
+ case MTWSM:
+ if (STp->write_prot)
+ return (-EACCES);
+ if (!STp->raw)
+ return 0;
+ cmd[0] = WRITE_FILEMARKS; /* FIXME -- need OS version */
+ if (cmd_in == MTWSM)
+ cmd[1] = 2;
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+ timeout = STp->timeout;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Writing %d setmark(s).\n", name,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+ if (fileno >= 0)
+ fileno += arg;
+ blkno = 0;
+ at_sm = (cmd_in == MTWSM);
+ break;
+ case MTOFFL:
+ case MTLOAD:
+ case MTUNLOAD:
+ case MTRETEN:
+ cmd[0] = START_STOP;
+ cmd[1] = 1; /* Don't wait for completion */
+ if (cmd_in == MTLOAD) {
+ if (STp->ready == ST_NO_TAPE)
+ cmd[4] = 4; /* open tray */
+ else
+ cmd[4] = 1; /* load */
+ }
+ if (cmd_in == MTRETEN)
+ cmd[4] = 3; /* retension then mount */
+ if (cmd_in == MTOFFL)
+ cmd[4] = 4; /* rewind then eject */
+ timeout = STp->timeout;
+#if DEBUG
+ if (debugging) {
+ switch (cmd_in) {
+ case MTUNLOAD:
+ printk(OSST_DEB_MSG "%s:D: Unloading tape.\n", name);
+ break;
+ case MTLOAD:
+ printk(OSST_DEB_MSG "%s:D: Loading tape.\n", name);
+ break;
+ case MTRETEN:
+ printk(OSST_DEB_MSG "%s:D: Retensioning tape.\n", name);
+ break;
+ case MTOFFL:
+ printk(OSST_DEB_MSG "%s:D: Ejecting tape.\n", name);
+ break;
+ }
+ }
+#endif
+ fileno = blkno = at_sm = frame_seq_numbr = logical_blk_num = 0 ;
+ break;
+ case MTNOP:
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: No-op on tape.\n", name);
+#endif
+ return 0; /* Should do something ? */
+ break;
+ case MTEOM:
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Spacing to end of recorded medium.\n", name);
+#endif
+ if ((osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos) < 0) ||
+ (osst_get_logical_frame(STp, &SRpnt, -1, 0) < 0)) {
+ ioctl_result = -EIO;
+ goto os_bypass;
+ }
+ if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_EOD) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: No EOD frame found where expected.\n", name);
+#endif
+ ioctl_result = -EIO;
+ goto os_bypass;
+ }
+ ioctl_result = osst_set_frame_position(STp, &SRpnt, STp->eod_frame_ppos, 0);
+ fileno = STp->filemark_cnt;
+ blkno = at_sm = 0;
+ goto os_bypass;
+
+ case MTERASE:
+ if (STp->write_prot)
+ return (-EACCES);
+ ioctl_result = osst_reset_header(STp, &SRpnt);
+ i = osst_write_eod(STp, &SRpnt);
+ if (i < ioctl_result) ioctl_result = i;
+ i = osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos);
+ if (i < ioctl_result) ioctl_result = i;
+ fileno = blkno = at_sm = 0 ;
+ goto os_bypass;
+
+ case MTREW:
+ cmd[0] = REZERO_UNIT; /* rewind */
+ cmd[1] = 1;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Rewinding tape, Immed=%d.\n", name, cmd[1]);
+#endif
+ fileno = blkno = at_sm = frame_seq_numbr = logical_blk_num = 0 ;
+ break;
+
+ case MTSETBLK: /* Set block length */
+ if ((STps->drv_block == 0 ) &&
+ !STp->dirty &&
+ ((STp->buffer)->buffer_bytes == 0) &&
+ ((arg & MT_ST_BLKSIZE_MASK) >= 512 ) &&
+ ((arg & MT_ST_BLKSIZE_MASK) <= OS_DATA_SIZE) &&
+ !(OS_DATA_SIZE % (arg & MT_ST_BLKSIZE_MASK)) ) {
+ /*
+ * Only allowed to change the block size if you opened the
+ * device at the beginning of a file before writing anything.
+ * Note, that when reading, changing block_size is futile,
+ * as the size used when writing overrides it.
+ */
+ STp->block_size = (arg & MT_ST_BLKSIZE_MASK);
+ printk(KERN_INFO "%s:I: Block size set to %d bytes.\n",
+ name, STp->block_size);
+ return 0;
+ }
+ case MTSETDENSITY: /* Set tape density */
+ case MTSETDRVBUFFER: /* Set drive buffering */
+ case SET_DENS_AND_BLK: /* Set density and block size */
+ chg_eof = 0;
+ if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
+ return (-EIO); /* Not allowed if data in buffer */
+ if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
+ (arg & MT_ST_BLKSIZE_MASK) != 0 &&
+ (arg & MT_ST_BLKSIZE_MASK) != STp->block_size ) {
+ printk(KERN_WARNING "%s:W: Illegal to set block size to %d%s.\n",
+ name, (int)(arg & MT_ST_BLKSIZE_MASK),
+ (OS_DATA_SIZE % (arg & MT_ST_BLKSIZE_MASK))?"":" now");
+ return (-EINVAL);
+ }
+ return 0; /* FIXME silently ignore if block size didn't change */
+
+ default:
+ return (-ENOSYS);
+ }
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, datalen, direction, timeout, MAX_RETRIES, 1);
+
+ ioctl_result = (STp->buffer)->syscall_result;
+
+ if (!SRpnt) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Couldn't exec scsi cmd for IOCTL\n", name);
+#endif
+ return ioctl_result;
+ }
+
+ if (!ioctl_result) { /* SCSI command successful */
+ STp->frame_seq_number = frame_seq_numbr;
+ STp->logical_blk_num = logical_blk_num;
+ }
+
+os_bypass:
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: IOCTL (%d) Result=%d\n", name, cmd_in, ioctl_result);
+#endif
+
+ if (!ioctl_result) { /* success */
+
+ if (cmd_in == MTFSFM) {
+ fileno--;
+ blkno--;
+ }
+ if (cmd_in == MTBSFM) {
+ fileno++;
+ blkno++;
+ }
+ STps->drv_block = blkno;
+ STps->drv_file = fileno;
+ STps->at_sm = at_sm;
+
+ if (cmd_in == MTEOM)
+ STps->eof = ST_EOD;
+ else if ((cmd_in == MTFSFM || cmd_in == MTBSF) && STps->eof == ST_FM_HIT) {
+ ioctl_result = osst_seek_logical_blk(STp, &SRpnt, STp->logical_blk_num-1);
+ STps->drv_block++;
+ STp->logical_blk_num++;
+ STp->frame_seq_number++;
+ STp->frame_in_buffer = 0;
+ STp->buffer->read_pointer = 0;
+ }
+ else if (cmd_in == MTFSF)
+ STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_FM;
+ else if (chg_eof)
+ STps->eof = ST_NOEOF;
+
+ if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
+ STp->rew_at_close = 0;
+ else if (cmd_in == MTLOAD) {
+ for (i=0; i < ST_NBR_PARTITIONS; i++) {
+ STp->ps[i].rw = ST_IDLE;
+ STp->ps[i].last_block_valid = 0;/* FIXME - where else is this field maintained? */
+ }
+ STp->partition = 0;
+ }
+
+ if (cmd_in == MTREW) {
+ ioctl_result = osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos);
+ if (ioctl_result > 0)
+ ioctl_result = 0;
+ }
+
+ } else if (cmd_in == MTBSF || cmd_in == MTBSFM ) {
+ if (osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos) < 0)
+ STps->drv_file = STps->drv_block = -1;
+ else
+ STps->drv_file = STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ } else if (cmd_in == MTFSF || cmd_in == MTFSFM) {
+ if (osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos) < 0)
+ STps->drv_file = STps->drv_block = -1;
+ else {
+ STps->drv_file = STp->filemark_cnt;
+ STps->drv_block = 0;
+ }
+ STps->eof = ST_EOD;
+ } else if (cmd_in == MTBSR || cmd_in == MTFSR || cmd_in == MTWEOF || cmd_in == MTEOM) {
+ STps->drv_file = STps->drv_block = (-1);
+ STps->eof = ST_NOEOF;
+ STp->header_ok = 0;
+ } else if (cmd_in == MTERASE) {
+ STp->header_ok = 0;
+ } else if (SRpnt) { /* SCSI command was not completely successful. */
+ if (SRpnt->sr_sense_buffer[2] & 0x40) {
+ STps->eof = ST_EOM_OK;
+ STps->drv_block = 0;
+ }
+ if (chg_eof)
+ STps->eof = ST_NOEOF;
+
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK)
+ STps->eof = ST_EOD;
+
+ if (cmd_in == MTLOAD && osst_wait_for_medium(STp, &SRpnt, 60))
+ ioctl_result = osst_wait_ready(STp, &SRpnt, 5 * 60, OSST_WAIT_POSITION_COMPLETE);
+ }
+ *aSRpnt = SRpnt;
+
+ return ioctl_result;
+}
+
+
+/* Open the device */
+static int os_scsi_tape_open(struct inode * inode, struct file * filp)
+{
+ unsigned short flags;
+ int i, b_size, new_session = 0, retval = 0;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request * SRpnt = NULL;
+ struct osst_tape * STp;
+ struct st_modedef * STm;
+ struct st_partstat * STps;
+ char * name;
+ int dev = TAPE_NR(inode);
+ int mode = TAPE_MODE(inode);
+
+ /*
+ * We really want to do nonseekable_open(inode, filp); here, but some
+ * versions of tar incorrectly call lseek on tapes and bail out if that
+ * fails. So we disallow pread() and pwrite(), but permit lseeks.
+ */
+ filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
+
+ write_lock(&os_scsi_tapes_lock);
+ if (dev >= osst_max_dev || os_scsi_tapes == NULL ||
+ (STp = os_scsi_tapes[dev]) == NULL || !STp->device) {
+ write_unlock(&os_scsi_tapes_lock);
+ return (-ENXIO);
+ }
+
+ name = tape_name(STp);
+
+ if (STp->in_use) {
+ write_unlock(&os_scsi_tapes_lock);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Device already in use.\n", name);
+#endif
+ return (-EBUSY);
+ }
+ if (scsi_device_get(STp->device)) {
+ write_unlock(&os_scsi_tapes_lock);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Failed scsi_device_get.\n", name);
+#endif
+ return (-ENXIO);
+ }
+ filp->private_data = STp;
+ STp->in_use = 1;
+ write_unlock(&os_scsi_tapes_lock);
+ STp->rew_at_close = TAPE_REWIND(inode);
+
+ if( !scsi_block_when_processing_errors(STp->device) ) {
+ return -ENXIO;
+ }
+
+ if (mode != STp->current_mode) {
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Mode change from %d to %d.\n",
+ name, STp->current_mode, mode);
+#endif
+ new_session = 1;
+ STp->current_mode = mode;
+ }
+ STm = &(STp->modes[STp->current_mode]);
+
+ flags = filp->f_flags;
+ STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
+
+ STp->raw = TAPE_IS_RAW(inode);
+ if (STp->raw)
+ STp->header_ok = 0;
+
+ /* Allocate data segments for this device's tape buffer */
+ if (!enlarge_buffer(STp->buffer, STp->restr_dma)) {
+ printk(KERN_ERR "%s:E: Unable to allocate memory segments for tape buffer.\n", name);
+ retval = (-EOVERFLOW);
+ goto err_out;
+ }
+ if (STp->buffer->buffer_size >= OS_FRAME_SIZE) {
+ for (i = 0, b_size = 0;
+ (i < STp->buffer->sg_segs) && ((b_size + STp->buffer->sg[i].length) <= OS_DATA_SIZE);
+ b_size += STp->buffer->sg[i++].length);
+ STp->buffer->aux = (os_aux_t *) (page_address(STp->buffer->sg[i].page) + OS_DATA_SIZE - b_size);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: b_data points to %p in segment 0 at %p\n", name,
+ STp->buffer->b_data, page_address(STp->buffer->sg[0].page));
+ printk(OSST_DEB_MSG "%s:D: AUX points to %p in segment %d at %p\n", name,
+ STp->buffer->aux, i, page_address(STp->buffer->sg[i].page));
+#endif
+ } else {
+ STp->buffer->aux = NULL; /* this had better never happen! */
+ printk(KERN_NOTICE "%s:A: Framesize %d too large for buffer.\n", name, OS_FRAME_SIZE);
+ retval = (-EIO);
+ goto err_out;
+ }
+ STp->buffer->writing = 0;
+ STp->buffer->syscall_result = 0;
+ STp->dirty = 0;
+ for (i=0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ }
+ STp->ready = ST_READY;
+#if DEBUG
+ STp->nbr_waits = STp->nbr_finished = 0;
+#endif
+
+ memset (cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(NULL, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1);
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result; /* FIXME - valid? */
+ goto err_out;
+ }
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY &&
+ SRpnt->sr_sense_buffer[12] == 4 ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Unit not ready, cause %x\n", name, SRpnt->sr_sense_buffer[13]);
+#endif
+ if (filp->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto err_out;
+ }
+ if (SRpnt->sr_sense_buffer[13] == 2) { /* initialize command required (LOAD) */
+ memset (cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = START_STOP;
+ cmd[1] = 1;
+ cmd[4] = 1;
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
+ STp->timeout, MAX_RETRIES, 1);
+ }
+ osst_wait_ready(STp, &SRpnt, (SRpnt->sr_sense_buffer[13]==1?15:3) * 60, 0);
+ }
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Unit wants attention\n", name);
+#endif
+ STp->header_ok = 0;
+
+ for (i=0; i < 10; i++) {
+
+ memset (cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
+ STp->timeout, MAX_RETRIES, 1);
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
+ (SRpnt->sr_sense_buffer[2] & 0x0f) != UNIT_ATTENTION)
+ break;
+ }
+
+ STp->pos_unknown = 0;
+ STp->partition = STp->new_partition = 0;
+ if (STp->can_partitions)
+ STp->nbr_partitions = 1; /* This guess will be updated later if necessary */
+ for (i=0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE; /* FIXME - seems to be redundant... */
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = 0;
+ STps->drv_file = 0 ;
+ }
+ new_session = 1;
+ STp->recover_count = 0;
+ STp->abort_count = 0;
+ }
+ /*
+ * if we have valid headers from before, and the drive/tape seem untouched,
+ * open without reconfiguring and re-reading the headers
+ */
+ if (!STp->buffer->syscall_result && STp->header_ok &&
+ !SRpnt->sr_result && SRpnt->sr_sense_buffer[0] == 0) {
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = 8;
+ cmd[2] = VENDOR_IDENT_PAGE;
+ cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, STp->timeout, 0, 1);
+
+ if (STp->buffer->syscall_result ||
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 2] != 'L' ||
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 3] != 'I' ||
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 4] != 'N' ||
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 5] != '4' ) {
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Signature was changed to %c%c%c%c\n", name,
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 2],
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 3],
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 4],
+ STp->buffer->b_data[MODE_HEADER_LENGTH + 5]);
+#endif
+ STp->header_ok = 0;
+ }
+ i = STp->first_frame_position;
+ if (STp->header_ok && i == osst_get_frame_position(STp, &SRpnt)) {
+ if (STp->door_locked == ST_UNLOCKED) {
+ if (do_door_lock(STp, 1))
+ printk(KERN_INFO "%s:I: Can't lock drive door\n", name);
+ else
+ STp->door_locked = ST_LOCKED_AUTO;
+ }
+ if (!STp->frame_in_buffer) {
+ STp->block_size = (STm->default_blksize > 0) ?
+ STm->default_blksize : OS_DATA_SIZE;
+ STp->buffer->buffer_bytes = STp->buffer->read_pointer = 0;
+ }
+ STp->buffer->buffer_blocks = OS_DATA_SIZE / STp->block_size;
+ STp->fast_open = 1;
+ scsi_release_request(SRpnt);
+ return 0;
+ }
+#if DEBUG
+ if (i != STp->first_frame_position)
+ printk(OSST_DEB_MSG "%s:D: Tape position changed from %d to %d\n",
+ name, i, STp->first_frame_position);
+#endif
+ STp->header_ok = 0;
+ }
+ STp->fast_open = 0;
+
+ if ((STp->buffer)->syscall_result != 0 && /* in all error conditions except no medium */
+ (SRpnt->sr_sense_buffer[2] != 2 || SRpnt->sr_sense_buffer[12] != 0x3A) ) {
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = 0x10;
+ cmd[4] = 4 + MODE_HEADER_LENGTH;
+
+ (STp->buffer)->b_data[0] = cmd[4] - 1;
+ (STp->buffer)->b_data[1] = 0; /* Medium Type - ignoring */
+ (STp->buffer)->b_data[2] = 0; /* Reserved */
+ (STp->buffer)->b_data[3] = 0; /* Block Descriptor Length */
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = 0x3f;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 1;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 2;
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 3;
+
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Applying soft reset\n", name);
+#endif
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, STp->timeout, 0, 1);
+
+ STp->header_ok = 0;
+
+ for (i=0; i < 10; i++) {
+
+ memset (cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
+ STp->timeout, MAX_RETRIES, 1);
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY)
+ break;
+
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) {
+ STp->pos_unknown = 0;
+ STp->partition = STp->new_partition = 0;
+ if (STp->can_partitions)
+ STp->nbr_partitions = 1; /* This guess will be updated later if necessary */
+ for (i=0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = 0;
+ STps->drv_file = 0 ;
+ }
+ new_session = 1;
+ }
+ }
+ }
+
+ if (osst_wait_ready(STp, &SRpnt, 15 * 60, 0)) /* FIXME - not allowed with NOBLOCK */
+ printk(KERN_INFO "%s:I: Device did not become Ready in open\n", name);
+
+ if ((STp->buffer)->syscall_result != 0) {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY &&
+ SRpnt->sr_sense_buffer[12] == 0x3a) { /* Check ASC */
+ STp->ready = ST_NO_TAPE;
+ } else
+ STp->ready = ST_NOT_READY;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ STp->density = 0; /* Clear the erroneous "residue" */
+ STp->write_prot = 0;
+ STp->block_size = 0;
+ STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
+ STp->partition = STp->new_partition = 0;
+ STp->door_locked = ST_UNLOCKED;
+ return 0;
+ }
+
+ osst_configure_onstream(STp, &SRpnt);
+
+ STp->block_size = STp->raw ? OS_FRAME_SIZE : (
+ (STm->default_blksize > 0) ? STm->default_blksize : OS_DATA_SIZE);
+ STp->buffer->buffer_blocks = STp->raw ? 1 : OS_DATA_SIZE / STp->block_size;
+ STp->buffer->buffer_bytes =
+ STp->buffer->read_pointer =
+ STp->frame_in_buffer = 0;
+
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Block size: %d, frame size: %d, buffer size: %d (%d blocks).\n",
+ name, STp->block_size, OS_FRAME_SIZE, (STp->buffer)->buffer_size,
+ (STp->buffer)->buffer_blocks);
+#endif
+
+ if (STp->drv_write_prot) {
+ STp->write_prot = 1;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Write protected\n", name);
+#endif
+ if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
+ retval = (-EROFS);
+ goto err_out;
+ }
+ }
+
+ if (new_session) { /* Change the drive parameters for the new mode */
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: New Session\n", name);
+#endif
+ STp->density_changed = STp->blksize_changed = 0;
+ STp->compression_changed = 0;
+ }
+
+ /*
+ * properly position the tape and check the ADR headers
+ */
+ if (STp->door_locked == ST_UNLOCKED) {
+ if (do_door_lock(STp, 1))
+ printk(KERN_INFO "%s:I: Can't lock drive door\n", name);
+ else
+ STp->door_locked = ST_LOCKED_AUTO;
+ }
+
+ osst_analyze_headers(STp, &SRpnt);
+
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+
+ return 0;
+
+err_out:
+ if (SRpnt != NULL)
+ scsi_release_request(SRpnt);
+ normalize_buffer(STp->buffer);
+ STp->header_ok = 0;
+ STp->in_use = 0;
+ scsi_device_put(STp->device);
+
+ return retval;
+}
+
+
+/* Flush the tape buffer before close */
+static int os_scsi_tape_flush(struct file * filp)
+{
+ int result = 0, result2;
+ struct osst_tape * STp = filp->private_data;
+ struct st_modedef * STm = &(STp->modes[STp->current_mode]);
+ struct st_partstat * STps = &(STp->ps[STp->partition]);
+ struct scsi_request * SRpnt = NULL;
+ char * name = tape_name(STp);
+
+ if (file_count(filp) > 1)
+ return 0;
+
+ if ((STps->rw == ST_WRITING || STp->dirty) && !STp->pos_unknown) {
+ STp->write_type = OS_WRITE_DATA;
+ result = osst_flush_write_buffer(STp, &SRpnt);
+ if (result != 0 && result != (-ENOSPC))
+ goto out;
+ }
+ if ( STps->rw >= ST_WRITING && !STp->pos_unknown) {
+
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG "%s:D: File length %ld bytes.\n",
+ name, (long)(filp->f_pos));
+ printk(OSST_DEB_MSG "%s:D: Async write waits %d, finished %d.\n",
+ name, STp->nbr_waits, STp->nbr_finished);
+ }
+#endif
+ result = osst_write_trailer(STp, &SRpnt, !(STp->rew_at_close));
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG "%s:D: Buffer flushed, %d EOF(s) written\n",
+ name, 1+STp->two_fm);
+#endif
+ }
+ else if (!STp->rew_at_close) {
+ STps = &(STp->ps[STp->partition]);
+ if (!STm->sysv || STps->rw != ST_READING) {
+ if (STp->can_bsr)
+ result = osst_flush_buffer(STp, &SRpnt, 0); /* this is the default path */
+ else if (STps->eof == ST_FM_HIT) {
+ result = cross_eof(STp, &SRpnt, 0);
+ if (result) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ }
+ else
+ STps->eof = ST_NOEOF;
+ }
+ }
+ else if ((STps->eof == ST_NOEOF &&
+ !(result = cross_eof(STp, &SRpnt, 1))) ||
+ STps->eof == ST_FM_HIT) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ }
+ }
+
+out:
+ if (STp->rew_at_close) {
+ result2 = osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos);
+ STps->drv_file = STps->drv_block = STp->frame_seq_number = STp->logical_blk_num = 0;
+ if (result == 0 && result2 < 0)
+ result = result2;
+ }
+ if (SRpnt) scsi_release_request(SRpnt);
+
+ if (STp->abort_count || STp->recover_count) {
+ printk(KERN_INFO "%s:I:", name);
+ if (STp->abort_count)
+ printk(" %d unrecovered errors", STp->abort_count);
+ if (STp->recover_count)
+ printk(" %d recovered errors", STp->recover_count);
+ if (STp->write_count)
+ printk(" in %d frames written", STp->write_count);
+ if (STp->read_count)
+ printk(" in %d frames read", STp->read_count);
+ printk("\n");
+ STp->recover_count = 0;
+ STp->abort_count = 0;
+ }
+ STp->write_count = 0;
+ STp->read_count = 0;
+
+ return result;
+}
+
+
+/* Close the device and release it */
+static int os_scsi_tape_close(struct inode * inode, struct file * filp)
+{
+ int result = 0;
+ struct osst_tape * STp = filp->private_data;
+
+ if (STp->door_locked == ST_LOCKED_AUTO)
+ do_door_lock(STp, 0);
+
+ if (STp->raw)
+ STp->header_ok = 0;
+
+ normalize_buffer(STp->buffer);
+ write_lock(&os_scsi_tapes_lock);
+ STp->in_use = 0;
+ write_unlock(&os_scsi_tapes_lock);
+
+ scsi_device_put(STp->device);
+
+ return result;
+}
+
+
+/* The ioctl command */
+static int osst_ioctl(struct inode * inode,struct file * file,
+ unsigned int cmd_in, unsigned long arg)
+{
+ int i, cmd_nr, cmd_type, retval = 0;
+ unsigned int blk;
+ struct st_modedef * STm;
+ struct st_partstat * STps;
+ struct scsi_request * SRpnt = NULL;
+ struct osst_tape * STp = file->private_data;
+ char * name = tape_name(STp);
+ void __user * p = (void __user *)arg;
+
+ if (down_interruptible(&STp->lock))
+ return -ERESTARTSYS;
+
+#if DEBUG
+ if (debugging && !STp->in_use) {
+ printk(OSST_DEB_MSG "%s:D: Incorrect device.\n", name);
+ retval = (-EIO);
+ goto out;
+ }
+#endif
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if( !scsi_block_when_processing_errors(STp->device) ) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ cmd_type = _IOC_TYPE(cmd_in);
+ cmd_nr = _IOC_NR(cmd_in);
+#if DEBUG
+ printk(OSST_DEB_MSG "%s:D: Ioctl %d,%d in %s mode\n", name,
+ cmd_type, cmd_nr, STp->raw?"raw":"normal");
+#endif
+ if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+ struct mtop mtc;
+ int auto_weof = 0;
+
+ if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ i = copy_from_user((char *) &mtc, p, sizeof(struct mtop));
+ if (i) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
+ printk(KERN_WARNING "%s:W: MTSETDRVBUFFER only allowed for root.\n", name);
+ retval = (-EPERM);
+ goto out;
+ }
+
+ if (!STm->defined && (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0)) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if (!STp->pos_unknown) {
+
+ if (STps->eof == ST_FM_HIT) {
+ if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) {
+ mtc.mt_count -= 1;
+ if (STps->drv_file >= 0)
+ STps->drv_file += 1;
+ }
+ else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
+ mtc.mt_count += 1;
+ if (STps->drv_file >= 0)
+ STps->drv_file += 1;
+ }
+ }
+
+ if (mtc.mt_op == MTSEEK) {
+ /* Old position must be restored if partition will be changed */
+ i = !STp->can_partitions || (STp->new_partition != STp->partition);
+ }
+ else {
+ i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+ mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
+ mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+ mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
+ mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
+ mtc.mt_op == MTCOMPRESSION;
+ }
+ i = osst_flush_buffer(STp, &SRpnt, i);
+ if (i < 0) {
+ retval = i;
+ goto out;
+ }
+ }
+ else {
+ /*
+ * If there was a bus reset, block further access
+ * to this device. If the user wants to rewind the tape,
+ * then reset the flag and allow access again.
+ */
+ if(mtc.mt_op != MTREW &&
+ mtc.mt_op != MTOFFL &&
+ mtc.mt_op != MTRETEN &&
+ mtc.mt_op != MTERASE &&
+ mtc.mt_op != MTSEEK &&
+ mtc.mt_op != MTEOM) {
+ retval = (-EIO);
+ goto out;
+ }
+ reset_state(STp);
+ /* remove this when the midlevel properly clears was_reset */
+ STp->device->was_reset = 0;
+ }
+
+ if (mtc.mt_op != MTCOMPRESSION && mtc.mt_op != MTLOCK &&
+ mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+ mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTSETDRVBUFFER &&
+ mtc.mt_op != MTMKPART && mtc.mt_op != MTSETPART &&
+ mtc.mt_op != MTWEOF && mtc.mt_op != MTWSM ) {
+
+ /*
+ * The user tells us to move to another position on the tape.
+ * If we were appending to the tape content, that would leave
+ * the tape without proper end, in that case write EOD and
+ * update the header to reflect its position.
+ */
+#if DEBUG
+ printk(KERN_WARNING "%s:D: auto_weod %s at ffp=%d,efp=%d,fsn=%d,lbn=%d,fn=%d,bn=%d\n", name,
+ STps->rw >= ST_WRITING ? "write" : STps->rw == ST_READING ? "read" : "idle",
+ STp->first_frame_position, STp->eod_frame_ppos, STp->frame_seq_number,
+ STp->logical_blk_num, STps->drv_file, STps->drv_block );
+#endif
+ if (STps->rw >= ST_WRITING && STp->first_frame_position >= STp->eod_frame_ppos) {
+ auto_weof = ((STp->write_type != OS_WRITE_NEW_MARK) &&
+ !(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL));
+ i = osst_write_trailer(STp, &SRpnt,
+ !(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL));
+#if DEBUG
+ printk(KERN_WARNING "%s:D: post trailer xeof=%d,ffp=%d,efp=%d,fsn=%d,lbn=%d,fn=%d,bn=%d\n",
+ name, auto_weof, STp->first_frame_position, STp->eod_frame_ppos,
+ STp->frame_seq_number, STp->logical_blk_num, STps->drv_file, STps->drv_block );
+#endif
+ if (i < 0) {
+ retval = i;
+ goto out;
+ }
+ }
+ STps->rw = ST_IDLE;
+ }
+
+ if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
+ do_door_lock(STp, 0); /* Ignore result! */
+
+ if (mtc.mt_op == MTSETDRVBUFFER &&
+ (mtc.mt_count & MT_ST_OPTIONS) != 0) {
+ retval = osst_set_options(STp, mtc.mt_count);
+ goto out;
+ }
+
+ if (mtc.mt_op == MTSETPART) {
+ if (mtc.mt_count >= STp->nbr_partitions)
+ retval = -EINVAL;
+ else {
+ STp->new_partition = mtc.mt_count;
+ retval = 0;
+ }
+ goto out;
+ }
+
+ if (mtc.mt_op == MTMKPART) {
+ if (!STp->can_partitions) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ if ((i = osst_int_ioctl(STp, &SRpnt, MTREW, 0)) < 0 /*||
+ (i = partition_tape(inode, mtc.mt_count)) < 0*/) {
+ retval = i;
+ goto out;
+ }
+ for (i=0; i < ST_NBR_PARTITIONS; i++) {
+ STp->ps[i].rw = ST_IDLE;
+ STp->ps[i].at_sm = 0;
+ STp->ps[i].last_block_valid = 0;
+ }
+ STp->partition = STp->new_partition = 0;
+ STp->nbr_partitions = 1; /* Bad guess ?-) */
+ STps->drv_block = STps->drv_file = 0;
+ retval = 0;
+ goto out;
+ }
+
+ if (mtc.mt_op == MTSEEK) {
+ if (STp->raw)
+ i = osst_set_frame_position(STp, &SRpnt, mtc.mt_count, 0);
+ else
+ i = osst_seek_sector(STp, &SRpnt, mtc.mt_count);
+ if (!STp->can_partitions)
+ STp->ps[0].rw = ST_IDLE;
+ retval = i;
+ goto out;
+ }
+
+ if (mtc.mt_op == MTLOCK || mtc.mt_op == MTUNLOCK) {
+ retval = do_door_lock(STp, (mtc.mt_op == MTLOCK));
+ goto out;
+ }
+
+ if (auto_weof)
+ cross_eof(STp, &SRpnt, 0);
+
+ if (mtc.mt_op == MTCOMPRESSION)
+ retval = -EINVAL; /* OnStream drives don't have compression hardware */
+ else
+ /* MTBSF MTBSFM MTBSR MTBSS MTEOM MTERASE MTFSF MTFSFB MTFSR MTFSS
+ * MTLOAD MTOFFL MTRESET MTRETEN MTREW MTUNLOAD MTWEOF MTWSM */
+ retval = osst_int_ioctl(STp, &SRpnt, mtc.mt_op, mtc.mt_count);
+ goto out;
+ }
+
+ if (!STm->defined) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if ((i = osst_flush_buffer(STp, &SRpnt, 0)) < 0) {
+ retval = i;
+ goto out;
+ }
+
+ if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+ struct mtget mt_status;
+
+ if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ mt_status.mt_type = MT_ISONSTREAM_SC;
+ mt_status.mt_erreg = STp->recover_erreg << MT_ST_SOFTERR_SHIFT;
+ mt_status.mt_dsreg =
+ ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+ ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+ mt_status.mt_blkno = STps->drv_block;
+ mt_status.mt_fileno = STps->drv_file;
+ if (STp->block_size != 0) {
+ if (STps->rw == ST_WRITING)
+ mt_status.mt_blkno += (STp->buffer)->buffer_bytes / STp->block_size;
+ else if (STps->rw == ST_READING)
+ mt_status.mt_blkno -= ((STp->buffer)->buffer_bytes +
+ STp->block_size - 1) / STp->block_size;
+ }
+
+ mt_status.mt_gstat = 0;
+ if (STp->drv_write_prot)
+ mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+ if (mt_status.mt_blkno == 0) {
+ if (mt_status.mt_fileno == 0)
+ mt_status.mt_gstat |= GMT_BOT(0xffffffff);
+ else
+ mt_status.mt_gstat |= GMT_EOF(0xffffffff);
+ }
+ mt_status.mt_resid = STp->partition;
+ if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
+ mt_status.mt_gstat |= GMT_EOT(0xffffffff);
+ else if (STps->eof >= ST_EOM_OK)
+ mt_status.mt_gstat |= GMT_EOD(0xffffffff);
+ if (STp->density == 1)
+ mt_status.mt_gstat |= GMT_D_800(0xffffffff);
+ else if (STp->density == 2)
+ mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
+ else if (STp->density == 3)
+ mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
+ if (STp->ready == ST_READY)
+ mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
+ if (STp->ready == ST_NO_TAPE)
+ mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
+ if (STps->at_sm)
+ mt_status.mt_gstat |= GMT_SM(0xffffffff);
+ if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) ||
+ STp->drv_buffer != 0)
+ mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+
+ i = copy_to_user(p, &mt_status, sizeof(struct mtget));
+ if (i) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ STp->recover_erreg = 0; /* Clear after read */
+ retval = 0;
+ goto out;
+ } /* End of MTIOCGET */
+
+ if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+ struct mtpos mt_pos;
+
+ if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ if (STp->raw)
+ blk = osst_get_frame_position(STp, &SRpnt);
+ else
+ blk = osst_get_sector(STp, &SRpnt);
+ if (blk < 0) {
+ retval = blk;
+ goto out;
+ }
+ mt_pos.mt_blkno = blk;
+ i = copy_to_user(p, &mt_pos, sizeof(struct mtpos));
+ if (i)
+ retval = -EFAULT;
+ goto out;
+ }
+ if (SRpnt) scsi_release_request(SRpnt);
+
+ up(&STp->lock);
+
+ return scsi_ioctl(STp->device, cmd_in, p);
+
+out:
+ if (SRpnt) scsi_release_request(SRpnt);
+
+ up(&STp->lock);
+
+ return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long osst_compat_ioctl(struct file * file, unsigned int cmd_in, unsigned long arg)
+{
+ struct osst_tape *STp = file->private_data;
+ struct scsi_device *sdev = STp->device;
+ int ret = -ENOIOCTLCMD;
+ if (sdev->host->hostt->compat_ioctl) {
+
+ ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
+
+ }
+ return ret;
+}
+#endif
+
+
+
+/* Memory handling routines */
+
+/* Try to allocate a new tape buffer skeleton. Caller must not hold os_scsi_tapes_lock */
+static struct osst_buffer * new_tape_buffer( int from_initialization, int need_dma, int max_sg )
+{
+ int i, priority;
+ struct osst_buffer *tb;
+
+ if (from_initialization)
+ priority = GFP_ATOMIC;
+ else
+ priority = GFP_KERNEL;
+
+ i = sizeof(struct osst_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist);
+ tb = (struct osst_buffer *)kmalloc(i, priority);
+ if (!tb) {
+ printk(KERN_NOTICE "osst :I: Can't allocate new tape buffer.\n");
+ return NULL;
+ }
+ memset(tb, 0, i);
+ tb->sg_segs = tb->orig_sg_segs = 0;
+ tb->use_sg = max_sg;
+ tb->in_use = 1;
+ tb->dma = need_dma;
+ tb->buffer_size = 0;
+#if DEBUG
+ if (debugging)
+ printk(OSST_DEB_MSG
+ "osst :D: Allocated tape buffer skeleton (%d bytes, %d segments, dma: %d).\n",
+ i, max_sg, need_dma);
+#endif
+ return tb;
+}
+
+/* Try to allocate a temporary (while a user has the device open) enlarged tape buffer */
+static int enlarge_buffer(struct osst_buffer *STbuffer, int need_dma)
+{
+ int segs, nbr, max_segs, b_size, priority, order, got;
+
+ if (STbuffer->buffer_size >= OS_FRAME_SIZE)
+ return 1;
+
+ if (STbuffer->sg_segs) {
+ printk(KERN_WARNING "osst :A: Buffer not previously normalized.\n");
+ normalize_buffer(STbuffer);
+ }
+ /* See how many segments we can use -- need at least two */
+ nbr = max_segs = STbuffer->use_sg;
+ if (nbr <= 2)
+ return 0;
+
+ priority = GFP_KERNEL /* | __GFP_NOWARN */;
+ if (need_dma)
+ priority |= GFP_DMA;
+
+ /* Try to allocate the first segment up to OS_DATA_SIZE and the others
+ big enough to reach the goal (code assumes no segments in place) */
+ for (b_size = OS_DATA_SIZE, order = OSST_FIRST_ORDER; b_size >= PAGE_SIZE; order--, b_size /= 2) {
+ STbuffer->sg[0].page = alloc_pages(priority, order);
+ STbuffer->sg[0].offset = 0;
+ if (STbuffer->sg[0].page != NULL) {
+ STbuffer->sg[0].length = b_size;
+ STbuffer->b_data = page_address(STbuffer->sg[0].page);
+ break;
+ }
+ }
+ if (STbuffer->sg[0].page == NULL) {
+ printk(KERN_NOTICE "osst :I: Can't allocate tape buffer main segment.\n");
+ return 0;
+ }
+ /* Got initial segment of 'bsize,order', continue with same size if possible, except for AUX */
+ for (segs=STbuffer->sg_segs=1, got=b_size;
+ segs < max_segs && got < OS_FRAME_SIZE; ) {
+ STbuffer->sg[segs].page =
+ alloc_pages(priority, (OS_FRAME_SIZE - got <= PAGE_SIZE) ? 0 : order);
+ STbuffer->sg[segs].offset = 0;
+ if (STbuffer->sg[segs].page == NULL) {
+ if (OS_FRAME_SIZE - got <= (max_segs - segs) * b_size / 2 && order) {
+ b_size /= 2; /* Large enough for the rest of the buffers */
+ order--;
+ continue;
+ }
+ printk(KERN_WARNING "osst :W: Failed to enlarge buffer to %d bytes.\n",
+ OS_FRAME_SIZE);
+#if DEBUG
+ STbuffer->buffer_size = got;
+#endif
+ normalize_buffer(STbuffer);
+ return 0;
+ }
+ STbuffer->sg[segs].length = (OS_FRAME_SIZE - got <= PAGE_SIZE / 2) ? (OS_FRAME_SIZE - got) : b_size;
+ got += STbuffer->sg[segs].length;
+ STbuffer->buffer_size = got;
+ STbuffer->sg_segs = ++segs;
+ }
+#if DEBUG
+ if (debugging) {
+ printk(OSST_DEB_MSG
+ "osst :D: Expanded tape buffer (%d bytes, %d->%d segments, dma: %d, at: %p).\n",
+ got, STbuffer->orig_sg_segs, STbuffer->sg_segs, need_dma, STbuffer->b_data);
+ printk(OSST_DEB_MSG
+ "osst :D: segment sizes: first %d at %p, last %d bytes at %p.\n",
+ STbuffer->sg[0].length, page_address(STbuffer->sg[0].page),
+ STbuffer->sg[segs-1].length, page_address(STbuffer->sg[segs-1].page));
+ }
+#endif
+
+ return 1;
+}
+
+
+/* Release the segments */
+static void normalize_buffer(struct osst_buffer *STbuffer)
+{
+ int i, order, b_size;
+
+ for (i=0; i < STbuffer->sg_segs; i++) {
+
+ for (b_size = PAGE_SIZE, order = 0;
+ b_size < STbuffer->sg[i].length;
+ b_size *= 2, order++);
+
+ __free_pages(STbuffer->sg[i].page, order);
+ STbuffer->buffer_size -= STbuffer->sg[i].length;
+ }
+#if DEBUG
+ if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
+ printk(OSST_DEB_MSG "osst :D: Buffer at %p normalized to %d bytes (segs %d).\n",
+ STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
+#endif
+ STbuffer->sg_segs = STbuffer->orig_sg_segs = 0;
+}
+
+
+/* Move data from the user buffer to the tape buffer. Returns zero (success) or
+ negative error code. */
+static int append_to_buffer(const char __user *ubp, struct osst_buffer *st_bp, int do_count)
+{
+ int i, cnt, res, offset;
+
+ for (i=0, offset=st_bp->buffer_bytes;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: Append_to_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count;
+ res = copy_from_user(page_address(st_bp->sg[i].page) + offset, ubp, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: Append_to_buffer overflow (left %d).\n",
+ do_count);
+ return (-EIO);
+ }
+ return 0;
+}
+
+
+/* Move data from the tape buffer to the user buffer. Returns zero (success) or
+ negative error code. */
+static int from_buffer(struct osst_buffer *st_bp, char __user *ubp, int do_count)
+{
+ int i, cnt, res, offset;
+
+ for (i=0, offset=st_bp->read_pointer;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: From_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count;
+ res = copy_to_user(ubp, page_address(st_bp->sg[i].page) + offset, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes -= cnt;
+ st_bp->read_pointer += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: From_buffer overflow (left %d).\n", do_count);
+ return (-EIO);
+ }
+ return 0;
+}
+
+/* Sets the tail of the buffer after fill point to zero.
+ Returns zero (success) or negative error code. */
+static int osst_zero_buffer_tail(struct osst_buffer *st_bp)
+{
+ int i, offset, do_count, cnt;
+
+ for (i = 0, offset = st_bp->buffer_bytes;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: Zero_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for (do_count = OS_DATA_SIZE - st_bp->buffer_bytes;
+ i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count ;
+ memset(page_address(st_bp->sg[i].page) + offset, 0, cnt);
+ do_count -= cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: Zero_buffer overflow (left %d).\n", do_count);
+ return (-EIO);
+ }
+ return 0;
+}
+
+/* Copy a osst 32K chunk of memory into the buffer.
+ Returns zero (success) or negative error code. */
+static int osst_copy_to_buffer(struct osst_buffer *st_bp, unsigned char *ptr)
+{
+ int i, cnt, do_count = OS_DATA_SIZE;
+
+ for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length < do_count ?
+ st_bp->sg[i].length : do_count ;
+ memcpy(page_address(st_bp->sg[i].page), ptr, cnt);
+ do_count -= cnt;
+ ptr += cnt;
+ }
+ if (do_count || i != st_bp->sg_segs-1) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: Copy_to_buffer overflow (left %d at sg %d).\n",
+ do_count, i);
+ return (-EIO);
+ }
+ return 0;
+}
+
+/* Copy a osst 32K chunk of memory from the buffer.
+ Returns zero (success) or negative error code. */
+static int osst_copy_from_buffer(struct osst_buffer *st_bp, unsigned char *ptr)
+{
+ int i, cnt, do_count = OS_DATA_SIZE;
+
+ for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length < do_count ?
+ st_bp->sg[i].length : do_count ;
+ memcpy(ptr, page_address(st_bp->sg[i].page), cnt);
+ do_count -= cnt;
+ ptr += cnt;
+ }
+ if (do_count || i != st_bp->sg_segs-1) { /* Should never happen */
+ printk(KERN_WARNING "osst :A: Copy_from_buffer overflow (left %d at sg %d).\n",
+ do_count, i);
+ return (-EIO);
+ }
+ return 0;
+}
+
+
+/* Module housekeeping */
+
+static void validate_options (void)
+{
+ if (max_dev > 0)
+ osst_max_dev = max_dev;
+ if (write_threshold_kbs > 0)
+ osst_write_threshold = write_threshold_kbs * ST_KILOBYTE;
+ if (osst_write_threshold > osst_buffer_size)
+ osst_write_threshold = osst_buffer_size;
+ if (max_sg_segs >= OSST_FIRST_SG)
+ osst_max_sg_segs = max_sg_segs;
+#if DEBUG
+ printk(OSST_DEB_MSG "osst :D: max tapes %d, write threshold %d, max s/g segs %d.\n",
+ osst_max_dev, osst_write_threshold, osst_max_sg_segs);
+#endif
+}
+
+#ifndef MODULE
+/* Set the boot options. Syntax: osst=xxx,yyy,...
+ where xxx is write threshold in 1024 byte blocks,
+ and yyy is number of s/g segments to use. */
+static int __init osst_setup (char *str)
+{
+ int i, ints[5];
+ char *stp;
+
+ stp = get_options(str, ARRAY_SIZE(ints), ints);
+
+ if (ints[0] > 0) {
+ for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++)
+ *parms[i].val = ints[i + 1];
+ } else {
+ while (stp != NULL) {
+ for (i = 0; i < ARRAY_SIZE(parms); i++) {
+ int len = strlen(parms[i].name);
+ if (!strncmp(stp, parms[i].name, len) &&
+ (*(stp + len) == ':' || *(stp + len) == '=')) {
+ *parms[i].val =
+ simple_strtoul(stp + len + 1, NULL, 0);
+ break;
+ }
+ }
+ if (i >= sizeof(parms) / sizeof(struct osst_dev_parm))
+ printk(KERN_INFO "osst :I: Illegal parameter in '%s'\n",
+ stp);
+ stp = strchr(stp, ',');
+ if (stp)
+ stp++;
+ }
+ }
+
+ return 1;
+}
+
+__setup("osst=", osst_setup);
+
+#endif
+
+static struct file_operations osst_fops = {
+ .owner = THIS_MODULE,
+ .read = osst_read,
+ .write = osst_write,
+ .ioctl = osst_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = osst_compat_ioctl,
+#endif
+ .open = os_scsi_tape_open,
+ .flush = os_scsi_tape_flush,
+ .release = os_scsi_tape_close,
+};
+
+static int osst_supports(struct scsi_device * SDp)
+{
+ struct osst_support_data {
+ char *vendor;
+ char *model;
+ char *rev;
+ char *driver_hint; /* Name of the correct driver, NULL if unknown */
+ };
+
+static struct osst_support_data support_list[] = {
+ /* {"XXX", "Yy-", "", NULL}, example */
+ SIGS_FROM_OSST,
+ {NULL, }};
+
+ struct osst_support_data *rp;
+
+ /* We are willing to drive OnStream SC-x0 as well as the
+ * * IDE, ParPort, FireWire, USB variants, if accessible by
+ * * emulation layer (ide-scsi, usb-storage, ...) */
+
+ for (rp=&(support_list[0]); rp->vendor != NULL; rp++)
+ if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
+ !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
+ !strncmp(rp->rev, SDp->rev, strlen(rp->rev)))
+ return 1;
+ return 0;
+}
+
+/*
+ * sysfs support for osst driver parameter information
+ */
+
+static ssize_t osst_version_show(struct device_driver *ddd, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", osst_version);
+}
+
+static DRIVER_ATTR(version, S_IRUGO, osst_version_show, NULL);
+
+static void osst_create_driverfs_files(struct device_driver *driverfs)
+{
+ driver_create_file(driverfs, &driver_attr_version);
+}
+
+static void osst_remove_driverfs_files(struct device_driver *driverfs)
+{
+ driver_remove_file(driverfs, &driver_attr_version);
+}
+
+/*
+ * sysfs support for accessing ADR header information
+ */
+
+static ssize_t osst_adr_rev_show(struct class_device *class_dev, char *buf)
+{
+ struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ ssize_t l = 0;
+
+ if (STp && STp->header_ok && STp->linux_media)
+ l = snprintf(buf, PAGE_SIZE, "%d.%d\n", STp->header_cache->major_rev, STp->header_cache->minor_rev);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(ADR_rev, S_IRUGO, osst_adr_rev_show, NULL);
+
+static ssize_t osst_linux_media_version_show(struct class_device *class_dev, char *buf)
+{
+ struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ ssize_t l = 0;
+
+ if (STp && STp->header_ok && STp->linux_media)
+ l = snprintf(buf, PAGE_SIZE, "LIN%d\n", STp->linux_media_version);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(media_version, S_IRUGO, osst_linux_media_version_show, NULL);
+
+static ssize_t osst_capacity_show(struct class_device *class_dev, char *buf)
+{
+ struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ ssize_t l = 0;
+
+ if (STp && STp->header_ok && STp->linux_media)
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STp->capacity);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(capacity, S_IRUGO, osst_capacity_show, NULL);
+
+static ssize_t osst_first_data_ppos_show(struct class_device *class_dev, char *buf)
+{
+ struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ ssize_t l = 0;
+
+ if (STp && STp->header_ok && STp->linux_media)
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STp->first_data_ppos);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(BOT_frame, S_IRUGO, osst_first_data_ppos_show, NULL);
+
+static ssize_t osst_eod_frame_ppos_show(struct class_device *class_dev, char *buf)
+{
+ struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ ssize_t l = 0;
+
+ if (STp && STp->header_ok && STp->linux_media)
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STp->eod_frame_ppos);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(EOD_frame, S_IRUGO, osst_eod_frame_ppos_show, NULL);
+
+static ssize_t osst_filemark_cnt_show(struct class_device *class_dev, char *buf)
+{
+ struct osst_tape * STp = (struct osst_tape *) class_get_devdata (class_dev);
+ ssize_t l = 0;
+
+ if (STp && STp->header_ok && STp->linux_media)
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STp->filemark_cnt);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(file_count, S_IRUGO, osst_filemark_cnt_show, NULL);
+
+static struct class_simple * osst_sysfs_class;
+
+static int osst_sysfs_valid = 0;
+
+static void osst_sysfs_init(void)
+{
+ osst_sysfs_class = class_simple_create(THIS_MODULE, "onstream_tape");
+ if ( IS_ERR(osst_sysfs_class) )
+ printk(KERN_WARNING "osst :W: Unable to register sysfs class\n");
+ else
+ osst_sysfs_valid = 1;
+}
+
+static void osst_sysfs_add(dev_t dev, struct device *device, struct osst_tape * STp, char * name)
+{
+ struct class_device *osst_class_member;
+
+ if (!osst_sysfs_valid) return;
+
+ osst_class_member = class_simple_device_add(osst_sysfs_class, dev, device, "%s", name);
+ if (IS_ERR(osst_class_member)) {
+ printk(KERN_WARNING "osst :W: Unable to add sysfs class member %s\n", name);
+ return;
+ }
+ class_set_devdata(osst_class_member, STp);
+ class_device_create_file(osst_class_member, &class_device_attr_ADR_rev);
+ class_device_create_file(osst_class_member, &class_device_attr_media_version);
+ class_device_create_file(osst_class_member, &class_device_attr_capacity);
+ class_device_create_file(osst_class_member, &class_device_attr_BOT_frame);
+ class_device_create_file(osst_class_member, &class_device_attr_EOD_frame);
+ class_device_create_file(osst_class_member, &class_device_attr_file_count);
+}
+
+static void osst_sysfs_destroy(dev_t dev)
+{
+ if (!osst_sysfs_valid) return;
+
+ class_simple_device_remove(dev);
+}
+
+static void osst_sysfs_cleanup(void)
+{
+ if (osst_sysfs_valid) {
+ class_simple_destroy(osst_sysfs_class);
+ osst_sysfs_valid = 0;
+ }
+}
+
+/*
+ * osst startup / cleanup code
+ */
+
+static int osst_probe(struct device *dev)
+{
+ struct scsi_device * SDp = to_scsi_device(dev);
+ struct osst_tape * tpnt;
+ struct st_modedef * STm;
+ struct st_partstat * STps;
+ struct osst_buffer * buffer;
+ struct gendisk * drive;
+ int i, mode, dev_num;
+
+ if (SDp->type != TYPE_TAPE || !osst_supports(SDp))
+ return -ENODEV;
+
+ drive = alloc_disk(1);
+ if (!drive) {
+ printk(KERN_ERR "osst :E: Out of memory. Device not attached.\n");
+ return -ENODEV;
+ }
+
+ /* if this is the first attach, build the infrastructure */
+ write_lock(&os_scsi_tapes_lock);
+ if (os_scsi_tapes == NULL) {
+ os_scsi_tapes =
+ (struct osst_tape **)kmalloc(osst_max_dev * sizeof(struct osst_tape *),
+ GFP_ATOMIC);
+ if (os_scsi_tapes == NULL) {
+ write_unlock(&os_scsi_tapes_lock);
+ printk(KERN_ERR "osst :E: Unable to allocate array for OnStream SCSI tapes.\n");
+ goto out_put_disk;
+ }
+ for (i=0; i < osst_max_dev; ++i) os_scsi_tapes[i] = NULL;
+ }
+
+ if (osst_nr_dev >= osst_max_dev) {
+ write_unlock(&os_scsi_tapes_lock);
+ printk(KERN_ERR "osst :E: Too many tape devices (max. %d).\n", osst_max_dev);
+ goto out_put_disk;
+ }
+
+ /* find a free minor number */
+ for (i=0; os_scsi_tapes[i] && i<osst_max_dev; i++);
+ if(i >= osst_max_dev) panic ("Scsi_devices corrupt (osst)");
+ dev_num = i;
+
+ /* allocate a struct osst_tape for this device */
+ tpnt = (struct osst_tape *)kmalloc(sizeof(struct osst_tape), GFP_ATOMIC);
+ if (tpnt == NULL) {
+ write_unlock(&os_scsi_tapes_lock);
+ printk(KERN_ERR "osst :E: Can't allocate device descriptor, device not attached.\n");
+ goto out_put_disk;
+ }
+ memset(tpnt, 0, sizeof(struct osst_tape));
+
+ /* allocate a buffer for this device */
+ i = SDp->host->sg_tablesize;
+ if (osst_max_sg_segs < i)
+ i = osst_max_sg_segs;
+ buffer = new_tape_buffer(1, SDp->host->unchecked_isa_dma, i);
+ if (buffer == NULL) {
+ write_unlock(&os_scsi_tapes_lock);
+ printk(KERN_ERR "osst :E: Unable to allocate a tape buffer, device not attached.\n");
+ kfree(tpnt);
+ goto out_put_disk;
+ }
+ os_scsi_tapes[dev_num] = tpnt;
+ tpnt->buffer = buffer;
+ tpnt->device = SDp;
+ drive->private_data = &tpnt->driver;
+ sprintf(drive->disk_name, "osst%d", dev_num);
+ tpnt->driver = &osst_template;
+ tpnt->drive = drive;
+ tpnt->in_use = 0;
+ tpnt->capacity = 0xfffff;
+ tpnt->dirty = 0;
+ tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
+ tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
+ tpnt->density = 0;
+ tpnt->do_auto_lock = OSST_AUTO_LOCK;
+ tpnt->can_bsr = OSST_IN_FILE_POS;
+ tpnt->can_partitions = 0;
+ tpnt->two_fm = OSST_TWO_FM;
+ tpnt->fast_mteom = OSST_FAST_MTEOM;
+ tpnt->scsi2_logical = OSST_SCSI2LOGICAL; /* FIXME */
+ tpnt->write_threshold = osst_write_threshold;
+ tpnt->default_drvbuffer = 0xff; /* No forced buffering */
+ tpnt->partition = 0;
+ tpnt->new_partition = 0;
+ tpnt->nbr_partitions = 0;
+ tpnt->min_block = 512;
+ tpnt->max_block = OS_DATA_SIZE;
+ tpnt->timeout = OSST_TIMEOUT;
+ tpnt->long_timeout = OSST_LONG_TIMEOUT;
+
+ /* Recognize OnStream tapes */
+ /* We don't need to test for OnStream, as this has been done in detect () */
+ tpnt->os_fw_rev = osst_parse_firmware_rev (SDp->rev);
+ tpnt->omit_blklims = 1;
+
+ tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) ||
+ (strncmp(SDp->model, "FW-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp);
+ tpnt->frame_in_buffer = 0;
+ tpnt->header_ok = 0;
+ tpnt->linux_media = 0;
+ tpnt->header_cache = NULL;
+
+ for (i=0; i < ST_NBR_MODES; i++) {
+ STm = &(tpnt->modes[i]);
+ STm->defined = 0;
+ STm->sysv = OSST_SYSV;
+ STm->defaults_for_writes = 0;
+ STm->do_async_writes = OSST_ASYNC_WRITES;
+ STm->do_buffer_writes = OSST_BUFFER_WRITES;
+ STm->do_read_ahead = OSST_READ_AHEAD;
+ STm->default_compression = ST_DONT_TOUCH;
+ STm->default_blksize = 512;
+ STm->default_density = (-1); /* No forced density */
+ }
+
+ for (i=0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(tpnt->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = (-1);
+ STps->drv_file = (-1);
+ }
+
+ tpnt->current_mode = 0;
+ tpnt->modes[0].defined = 1;
+ tpnt->modes[2].defined = 1;
+ tpnt->density_changed = tpnt->compression_changed = tpnt->blksize_changed = 0;
+
+ init_MUTEX(&tpnt->lock);
+ osst_nr_dev++;
+ write_unlock(&os_scsi_tapes_lock);
+ {
+ char name[8];
+ /* Rewind entry */
+ osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num), dev, tpnt, tape_name(tpnt));
+ /* No-rewind entry */
+ snprintf(name, 8, "%s%s", "n", tape_name(tpnt));
+ osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num + 128), dev, tpnt, name);
+ }
+ for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+ /* Rewind entry */
+ devfs_mk_cdev(MKDEV(OSST_MAJOR, dev_num + (mode << 5)),
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ "%s/ot%s", SDp->devfs_name, osst_formats[mode]);
+
+ /* No-rewind entry */
+ devfs_mk_cdev(MKDEV(OSST_MAJOR, dev_num + (mode << 5) + 128),
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ "%s/ot%sn", SDp->devfs_name, osst_formats[mode]);
+ }
+ drive->number = devfs_register_tape(SDp->devfs_name);
+
+ printk(KERN_INFO
+ "osst :I: Attached OnStream %.5s tape at scsi%d, channel %d, id %d, lun %d as %s\n",
+ SDp->model, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun, tape_name(tpnt));
+
+ return 0;
+
+out_put_disk:
+ put_disk(drive);
+ return -ENODEV;
+};
+
+static int osst_remove(struct device *dev)
+{
+ struct scsi_device * SDp = to_scsi_device(dev);
+ struct osst_tape * tpnt;
+ int i, mode;
+
+ if ((SDp->type != TYPE_TAPE) || (osst_nr_dev <= 0))
+ return 0;
+
+ write_lock(&os_scsi_tapes_lock);
+ for(i=0; i < osst_max_dev; i++) {
+ if((tpnt = os_scsi_tapes[i]) && (tpnt->device == SDp)) {
+ osst_sysfs_destroy(MKDEV(OSST_MAJOR, i));
+ osst_sysfs_destroy(MKDEV(OSST_MAJOR, i+128));
+ tpnt->device = NULL;
+ for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+ devfs_remove("%s/ot%s", SDp->devfs_name, osst_formats[mode]);
+ devfs_remove("%s/ot%sn", SDp->devfs_name, osst_formats[mode]);
+ }
+ devfs_unregister_tape(tpnt->drive->number);
+ put_disk(tpnt->drive);
+ os_scsi_tapes[i] = NULL;
+ osst_nr_dev--;
+ write_unlock(&os_scsi_tapes_lock);
+ if (tpnt->header_cache != NULL) vfree(tpnt->header_cache);
+ if (tpnt->buffer) {
+ normalize_buffer(tpnt->buffer);
+ kfree(tpnt->buffer);
+ }
+ kfree(tpnt);
+ return 0;
+ }
+ }
+ write_unlock(&os_scsi_tapes_lock);
+ return 0;
+}
+
+static int __init init_osst(void)
+{
+ printk(KERN_INFO "osst :I: Tape driver with OnStream support version %s\nosst :I: %s\n", osst_version, cvsid);
+
+ validate_options();
+ osst_sysfs_init();
+
+ if ((register_chrdev(OSST_MAJOR,"osst", &osst_fops) < 0) || scsi_register_driver(&osst_template.gendrv)) {
+ printk(KERN_ERR "osst :E: Unable to register major %d for OnStream tapes\n", OSST_MAJOR);
+ osst_sysfs_cleanup();
+ return 1;
+ }
+ osst_create_driverfs_files(&osst_template.gendrv);
+
+ return 0;
+}
+
+static void __exit exit_osst (void)
+{
+ int i;
+ struct osst_tape * STp;
+
+ osst_remove_driverfs_files(&osst_template.gendrv);
+ scsi_unregister_driver(&osst_template.gendrv);
+ unregister_chrdev(OSST_MAJOR, "osst");
+ osst_sysfs_cleanup();
+
+ if (os_scsi_tapes) {
+ for (i=0; i < osst_max_dev; ++i) {
+ if (!(STp = os_scsi_tapes[i])) continue;
+ /* This is defensive, supposed to happen during detach */
+ if (STp->header_cache)
+ vfree(STp->header_cache);
+ if (STp->buffer) {
+ normalize_buffer(STp->buffer);
+ kfree(STp->buffer);
+ }
+ put_disk(STp->drive);
+ kfree(STp);
+ }
+ kfree(os_scsi_tapes);
+ }
+ printk(KERN_INFO "osst :I: Unloaded.\n");
+}
+
+module_init(init_osst);
+module_exit(exit_osst);
diff --git a/drivers/scsi/osst.h b/drivers/scsi/osst.h
new file mode 100644
index 000000000000..b72e1c76f52c
--- /dev/null
+++ b/drivers/scsi/osst.h
@@ -0,0 +1,638 @@
+/*
+ * $Header: /cvsroot/osst/Driver/osst.h,v 1.16 2005/01/01 21:13:35 wriede Exp $
+ */
+
+#include <asm/byteorder.h>
+#include <linux/config.h>
+#include <linux/completion.h>
+
+/* FIXME - rename and use the following two types or delete them!
+ * and the types really should go to st.h anyway...
+ * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned response_format :4; /* Response Data Format */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+ u8 additional_length; /* Additional Length (total_length-4) */
+ u8 rsv5, rsv6, rsv7; /* Reserved */
+ u8 vendor_id[8]; /* Vendor Identification */
+ u8 product_id[16]; /* Product Identification */
+ u8 revision_level[4]; /* Revision Level */
+ u8 vendor_specific[20]; /* Vendor Specific - Optional */
+ u8 reserved56t95[40]; /* Reserved - Optional */
+ /* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ * READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+ unsigned reserved0_10 :2; /* Reserved */
+ unsigned bpu :1; /* Block Position Unknown */
+ unsigned reserved0_543 :3; /* Reserved */
+ unsigned eop :1; /* End Of Partition */
+ unsigned bop :1; /* Beginning Of Partition */
+ u8 partition; /* Partition Number */
+ u8 reserved2, reserved3; /* Reserved */
+ u32 first_block; /* First Block Location */
+ u32 last_block; /* Last Block Location (Optional) */
+ u8 reserved12; /* Reserved */
+ u8 blocks_in_buffer[3]; /* Blocks In Buffer - (Optional) */
+ u32 bytes_in_buffer; /* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ * Follows structures which are related to the SELECT SENSE / MODE SENSE
+ * packet commands.
+ */
+#define COMPRESSION_PAGE 0x0f
+#define COMPRESSION_PAGE_LENGTH 16
+
+#define CAPABILITIES_PAGE 0x2a
+#define CAPABILITIES_PAGE_LENGTH 20
+
+#define TAPE_PARAMTR_PAGE 0x2b
+#define TAPE_PARAMTR_PAGE_LENGTH 16
+
+#define NUMBER_RETRIES_PAGE 0x2f
+#define NUMBER_RETRIES_PAGE_LENGTH 4
+
+#define BLOCK_SIZE_PAGE 0x30
+#define BLOCK_SIZE_PAGE_LENGTH 4
+
+#define BUFFER_FILLING_PAGE 0x33
+#define BUFFER_FILLING_PAGE_LENGTH 4
+
+#define VENDOR_IDENT_PAGE 0x36
+#define VENDOR_IDENT_PAGE_LENGTH 8
+
+#define LOCATE_STATUS_PAGE 0x37
+#define LOCATE_STATUS_PAGE_LENGTH 0
+
+#define MODE_HEADER_LENGTH 4
+
+
+/*
+ * REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+ unsigned error_code :7; /* Current of deferred errors */
+ unsigned valid :1; /* The information field conforms to QIC-157C */
+ u8 reserved1 :8; /* Segment Number - Reserved */
+ unsigned sense_key :4; /* Sense Key */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned eom :1; /* End Of Medium */
+ unsigned filemark :1; /* Filemark */
+ u32 information __attribute__ ((packed));
+ u8 asl; /* Additional sense length (n-7) */
+ u32 command_specific; /* Additional command specific information */
+ u8 asc; /* Additional Sense Code */
+ u8 ascq; /* Additional Sense Code Qualifier */
+ u8 replaceable_unit_code; /* Field Replaceable Unit Code */
+ unsigned sk_specific1 :7; /* Sense Key Specific */
+ unsigned sksv :1; /* Sense Key Specific information is valid */
+ u8 sk_specific2; /* Sense Key Specific */
+ u8 sk_specific3; /* Sense Key Specific */
+ u8 pad[2]; /* Padding to 20 bytes */
+} idetape_request_sense_result_t;
+
+/*
+ * Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+ u8 mode_data_length; /* Length of the following data transfer */
+ u8 medium_type; /* Medium Type */
+ u8 dsp; /* Device Specific Parameter */
+ u8 bdl; /* Block Descriptor Length */
+} osst_mode_parameter_header_t;
+
+/*
+ * Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ * Support for block descriptors is optional.
+ */
+typedef struct {
+ u8 density_code; /* Medium density code */
+ u8 blocks[3]; /* Number of blocks */
+ u8 reserved4; /* Reserved */
+ u8 length[3]; /* Block Length */
+} osst_parameter_block_descriptor_t;
+
+/*
+ * The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1;
+ unsigned reserved0 :1; /* Reserved */
+ unsigned page_code :6; /* Page Code - Should be 0xf */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page Code - Should be 0xf */
+ unsigned reserved0 :1; /* Reserved */
+ unsigned ps :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 page_length; /* Page Length - Should be 14 */
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned dce :1; /* Data Compression Enable */
+ unsigned dcc :1; /* Data Compression Capable */
+ unsigned reserved2 :6; /* Reserved */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved2 :6; /* Reserved */
+ unsigned dcc :1; /* Data Compression Capable */
+ unsigned dce :1; /* Data Compression Enable */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned dde :1; /* Data Decompression Enable */
+ unsigned red :2; /* Report Exception on Decompression */
+ unsigned reserved3 :5; /* Reserved */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved3 :5; /* Reserved */
+ unsigned red :2; /* Report Exception on Decompression */
+ unsigned dde :1; /* Data Decompression Enable */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u32 ca; /* Compression Algorithm */
+ u32 da; /* Decompression Algorithm */
+ u8 reserved[4]; /* Reserved */
+} osst_data_compression_page_t;
+
+/*
+ * The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1;
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned page_code :6; /* Page Code - Should be 0x11 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page Code - Should be 0x11 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 page_length; /* Page Length - Should be 6 */
+ u8 map; /* Maximum Additional Partitions - Should be 0 */
+ u8 apd; /* Additional Partitions Defined - Should be 0 */
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned fdp :1; /* Fixed Data Partitions */
+ unsigned sdp :1; /* Should be 0 */
+ unsigned idp :1; /* Should be 0 */
+ unsigned psum :2; /* Should be 0 */
+ unsigned reserved4_012 :3; /* Reserved */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved4_012 :3; /* Reserved */
+ unsigned psum :2; /* Should be 0 */
+ unsigned idp :1; /* Should be 0 */
+ unsigned sdp :1; /* Should be 0 */
+ unsigned fdp :1; /* Fixed Data Partitions */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 mfr; /* Medium Format Recognition */
+ u8 reserved[2]; /* Reserved */
+} osst_medium_partition_page_t;
+
+/*
+ * Capabilities and Mechanical Status Page
+ */
+typedef struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved1_67 :2;
+ unsigned page_code :6; /* Page code - Should be 0x2a */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x2a */
+ unsigned reserved1_67 :2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 page_length; /* Page Length - Should be 0x12 */
+ u8 reserved2, reserved3;
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved4_67 :2;
+ unsigned sprev :1; /* Supports SPACE in the reverse direction */
+ unsigned reserved4_1234 :4;
+ unsigned ro :1; /* Read Only Mode */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned ro :1; /* Read Only Mode */
+ unsigned reserved4_1234 :4;
+ unsigned sprev :1; /* Supports SPACE in the reverse direction */
+ unsigned reserved4_67 :2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved5_67 :2;
+ unsigned qfa :1; /* Supports the QFA two partition formats */
+ unsigned reserved5_4 :1;
+ unsigned efmt :1; /* Supports ERASE command initiated formatting */
+ unsigned reserved5_012 :3;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved5_012 :3;
+ unsigned efmt :1; /* Supports ERASE command initiated formatting */
+ unsigned reserved5_4 :1;
+ unsigned qfa :1; /* Supports the QFA two partition formats */
+ unsigned reserved5_67 :2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned cmprs :1; /* Supports data compression */
+ unsigned ecc :1; /* Supports error correction */
+ unsigned reserved6_45 :2; /* Reserved */
+ unsigned eject :1; /* The device can eject the volume */
+ unsigned prevent :1; /* The device defaults in the prevent state after power up */
+ unsigned locked :1; /* The volume is locked */
+ unsigned lock :1; /* Supports locking the volume */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned lock :1; /* Supports locking the volume */
+ unsigned locked :1; /* The volume is locked */
+ unsigned prevent :1; /* The device defaults in the prevent state after power up */
+ unsigned eject :1; /* The device can eject the volume */
+ unsigned reserved6_45 :2; /* Reserved */
+ unsigned ecc :1; /* Supports error correction */
+ unsigned cmprs :1; /* Supports data compression */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */
+ /* transfers for slow buffer memory ??? */
+ /* Also 32768 block size in some cases */
+ unsigned reserved7_3_6 :4;
+ unsigned blk1024 :1; /* Supports 1024 bytes block size */
+ unsigned blk512 :1; /* Supports 512 bytes block size */
+ unsigned reserved7_0 :1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved7_0 :1;
+ unsigned blk512 :1; /* Supports 512 bytes block size */
+ unsigned blk1024 :1; /* Supports 1024 bytes block size */
+ unsigned reserved7_3_6 :4;
+ unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */
+ /* transfers for slow buffer memory ??? */
+ /* Also 32768 block size in some cases */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u16 max_speed; /* Maximum speed supported in KBps */
+ u8 reserved10, reserved11;
+ u16 ctl; /* Continuous Transfer Limit in blocks */
+ u16 speed; /* Current Speed, in KBps */
+ u16 buffer_size; /* Buffer Size, in 512 bytes */
+ u8 reserved18, reserved19;
+} osst_capabilities_page_t;
+
+/*
+ * Block Size Page
+ */
+typedef struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1;
+ unsigned reserved1_6 :1;
+ unsigned page_code :6; /* Page code - Should be 0x30 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x30 */
+ unsigned reserved1_6 :1;
+ unsigned ps :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 page_length; /* Page Length - Should be 2 */
+ u8 reserved2;
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned one :1;
+ unsigned reserved2_6 :1;
+ unsigned record32_5 :1;
+ unsigned record32 :1;
+ unsigned reserved2_23 :2;
+ unsigned play32_5 :1;
+ unsigned play32 :1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned play32 :1;
+ unsigned play32_5 :1;
+ unsigned reserved2_23 :2;
+ unsigned record32 :1;
+ unsigned record32_5 :1;
+ unsigned reserved2_6 :1;
+ unsigned one :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} osst_block_size_page_t;
+
+/*
+ * Tape Parameters Page
+ */
+typedef struct {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1;
+ unsigned reserved1_6 :1;
+ unsigned page_code :6; /* Page code - Should be 0x2b */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x2b */
+ unsigned reserved1_6 :1;
+ unsigned ps :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ u8 reserved2;
+ u8 density;
+ u8 reserved3,reserved4;
+ u16 segtrk;
+ u16 trks;
+ u8 reserved5,reserved6,reserved7,reserved8,reserved9,reserved10;
+} osst_tape_paramtr_page_t;
+
+/* OnStream definitions */
+
+#define OS_CONFIG_PARTITION (0xff)
+#define OS_DATA_PARTITION (0)
+#define OS_PARTITION_VERSION (1)
+
+/*
+ * partition
+ */
+typedef struct os_partition_s {
+ __u8 partition_num;
+ __u8 par_desc_ver;
+ __u16 wrt_pass_cntr;
+ __u32 first_frame_ppos;
+ __u32 last_frame_ppos;
+ __u32 eod_frame_ppos;
+} os_partition_t;
+
+/*
+ * DAT entry
+ */
+typedef struct os_dat_entry_s {
+ __u32 blk_sz;
+ __u16 blk_cnt;
+ __u8 flags;
+ __u8 reserved;
+} os_dat_entry_t;
+
+/*
+ * DAT
+ */
+#define OS_DAT_FLAGS_DATA (0xc)
+#define OS_DAT_FLAGS_MARK (0x1)
+
+typedef struct os_dat_s {
+ __u8 dat_sz;
+ __u8 reserved1;
+ __u8 entry_cnt;
+ __u8 reserved3;
+ os_dat_entry_t dat_list[16];
+} os_dat_t;
+
+/*
+ * Frame types
+ */
+#define OS_FRAME_TYPE_FILL (0)
+#define OS_FRAME_TYPE_EOD (1 << 0)
+#define OS_FRAME_TYPE_MARKER (1 << 1)
+#define OS_FRAME_TYPE_HEADER (1 << 3)
+#define OS_FRAME_TYPE_DATA (1 << 7)
+
+/*
+ * AUX
+ */
+typedef struct os_aux_s {
+ __u32 format_id; /* hardware compability AUX is based on */
+ char application_sig[4]; /* driver used to write this media */
+ __u32 hdwr; /* reserved */
+ __u32 update_frame_cntr; /* for configuration frame */
+ __u8 frame_type;
+ __u8 frame_type_reserved;
+ __u8 reserved_18_19[2];
+ os_partition_t partition;
+ __u8 reserved_36_43[8];
+ __u32 frame_seq_num;
+ __u32 logical_blk_num_high;
+ __u32 logical_blk_num;
+ os_dat_t dat;
+ __u8 reserved188_191[4];
+ __u32 filemark_cnt;
+ __u32 phys_fm;
+ __u32 last_mark_ppos;
+ __u8 reserved204_223[20];
+
+ /*
+ * __u8 app_specific[32];
+ *
+ * Linux specific fields:
+ */
+ __u32 next_mark_ppos; /* when known, points to next marker */
+ __u32 last_mark_lbn; /* storing log_blk_num of last mark is extends ADR spec */
+ __u8 linux_specific[24];
+
+ __u8 reserved_256_511[256];
+} os_aux_t;
+
+#define OS_FM_TAB_MAX 1024
+
+typedef struct os_fm_tab_s {
+ __u8 fm_part_num;
+ __u8 reserved_1;
+ __u8 fm_tab_ent_sz;
+ __u8 reserved_3;
+ __u16 fm_tab_ent_cnt;
+ __u8 reserved6_15[10];
+ __u32 fm_tab_ent[OS_FM_TAB_MAX];
+} os_fm_tab_t;
+
+typedef struct os_ext_trk_ey_s {
+ __u8 et_part_num;
+ __u8 fmt;
+ __u16 fm_tab_off;
+ __u8 reserved4_7[4];
+ __u32 last_hlb_hi;
+ __u32 last_hlb;
+ __u32 last_pp;
+ __u8 reserved20_31[12];
+} os_ext_trk_ey_t;
+
+typedef struct os_ext_trk_tb_s {
+ __u8 nr_stream_part;
+ __u8 reserved_1;
+ __u8 et_ent_sz;
+ __u8 reserved3_15[13];
+ os_ext_trk_ey_t dat_ext_trk_ey;
+ os_ext_trk_ey_t qfa_ext_trk_ey;
+} os_ext_trk_tb_t;
+
+typedef struct os_header_s {
+ char ident_str[8];
+ __u8 major_rev;
+ __u8 minor_rev;
+ __u16 ext_trk_tb_off;
+ __u8 reserved12_15[4];
+ __u8 pt_par_num;
+ __u8 pt_reserved1_3[3];
+ os_partition_t partition[16];
+ __u32 cfg_col_width;
+ __u32 dat_col_width;
+ __u32 qfa_col_width;
+ __u8 cartridge[16];
+ __u8 reserved304_511[208];
+ __u32 old_filemark_list[16680/4]; /* in ADR 1.4 __u8 track_table[16680] */
+ os_ext_trk_tb_t ext_track_tb;
+ __u8 reserved17272_17735[464];
+ os_fm_tab_t dat_fm_tab;
+ os_fm_tab_t qfa_fm_tab;
+ __u8 reserved25960_32767[6808];
+} os_header_t;
+
+
+/*
+ * OnStream ADRL frame
+ */
+#define OS_FRAME_SIZE (32 * 1024 + 512)
+#define OS_DATA_SIZE (32 * 1024)
+#define OS_AUX_SIZE (512)
+//#define OSST_MAX_SG 2
+
+/* The OnStream tape buffer descriptor. */
+struct osst_buffer {
+ unsigned char in_use;
+ unsigned char dma; /* DMA-able buffer */
+ int buffer_size;
+ int buffer_blocks;
+ int buffer_bytes;
+ int read_pointer;
+ int writing;
+ int midlevel_result;
+ int syscall_result;
+ struct scsi_request *last_SRpnt;
+ unsigned char *b_data;
+ os_aux_t *aux; /* onstream AUX structure at end of each block */
+ unsigned short use_sg; /* zero or number of s/g segments for this adapter */
+ unsigned short sg_segs; /* number of segments in s/g list */
+ unsigned short orig_sg_segs; /* number of segments allocated at first try */
+ struct scatterlist sg[1]; /* MUST BE last item */
+} ;
+
+/* The OnStream tape drive descriptor */
+struct osst_tape {
+ struct scsi_driver *driver;
+ unsigned capacity;
+ struct scsi_device *device;
+ struct semaphore lock; /* for serialization */
+ struct completion wait; /* for SCSI commands */
+ struct osst_buffer * buffer;
+
+ /* Drive characteristics */
+ unsigned char omit_blklims;
+ unsigned char do_auto_lock;
+ unsigned char can_bsr;
+ unsigned char can_partitions;
+ unsigned char two_fm;
+ unsigned char fast_mteom;
+ unsigned char restr_dma;
+ unsigned char scsi2_logical;
+ unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */
+ unsigned char pos_unknown; /* after reset position unknown */
+ int write_threshold;
+ int timeout; /* timeout for normal commands */
+ int long_timeout; /* timeout for commands known to take long time*/
+
+ /* Mode characteristics */
+ struct st_modedef modes[ST_NBR_MODES];
+ int current_mode;
+
+ /* Status variables */
+ int partition;
+ int new_partition;
+ int nbr_partitions; /* zero until partition support enabled */
+ struct st_partstat ps[ST_NBR_PARTITIONS];
+ unsigned char dirty;
+ unsigned char ready;
+ unsigned char write_prot;
+ unsigned char drv_write_prot;
+ unsigned char in_use;
+ unsigned char blksize_changed;
+ unsigned char density_changed;
+ unsigned char compression_changed;
+ unsigned char drv_buffer;
+ unsigned char density;
+ unsigned char door_locked;
+ unsigned char rew_at_close;
+ unsigned char inited;
+ int block_size;
+ int min_block;
+ int max_block;
+ int recover_count; /* from tape opening */
+ int abort_count;
+ int write_count;
+ int read_count;
+ int recover_erreg; /* from last status call */
+ /*
+ * OnStream specific data
+ */
+ int os_fw_rev; /* the firmware revision * 10000 */
+ unsigned char raw; /* flag OnStream raw access (32.5KB block size) */
+ unsigned char poll; /* flag that this drive needs polling (IDE|firmware) */
+ unsigned char frame_in_buffer; /* flag that the frame as per frame_seq_number
+ * has been read into STp->buffer and is valid */
+ int frame_seq_number; /* logical frame number */
+ int logical_blk_num; /* logical block number */
+ unsigned first_frame_position; /* physical frame to be transferred to/from host */
+ unsigned last_frame_position; /* physical frame to be transferd to/from tape */
+ int cur_frames; /* current number of frames in internal buffer */
+ int max_frames; /* max number of frames in internal buffer */
+ char application_sig[5]; /* application signature */
+ unsigned char fast_open; /* flag that reminds us we didn't check headers at open */
+ unsigned short wrt_pass_cntr; /* write pass counter */
+ int update_frame_cntr; /* update frame counter */
+ int onstream_write_error; /* write error recovery active */
+ int header_ok; /* header frame verified ok */
+ int linux_media; /* reading linux-specifc media */
+ int linux_media_version;
+ os_header_t * header_cache; /* cache is kept for filemark positions */
+ int filemark_cnt;
+ int first_mark_ppos;
+ int last_mark_ppos;
+ int last_mark_lbn; /* storing log_blk_num of last mark is extends ADR spec */
+ int first_data_ppos;
+ int eod_frame_ppos;
+ int eod_frame_lfa;
+ int write_type; /* used in write error recovery */
+ int read_error_frame; /* used in read error recovery */
+ unsigned long cmd_start_time;
+ unsigned long max_cmd_time;
+
+#if DEBUG
+ unsigned char write_pending;
+ int nbr_finished;
+ int nbr_waits;
+ unsigned char last_cmnd[6];
+ unsigned char last_sense[16];
+#endif
+ struct gendisk *drive;
+} ;
+
+/* Values of write_type */
+#define OS_WRITE_DATA 0
+#define OS_WRITE_EOD 1
+#define OS_WRITE_NEW_MARK 2
+#define OS_WRITE_LAST_MARK 3
+#define OS_WRITE_HEADER 4
+#define OS_WRITE_FILLER 5
+
+/* Additional rw state */
+#define OS_WRITING_COMPLETE 3
diff --git a/drivers/scsi/osst_detect.h b/drivers/scsi/osst_detect.h
new file mode 100644
index 000000000000..21717d0e6974
--- /dev/null
+++ b/drivers/scsi/osst_detect.h
@@ -0,0 +1,6 @@
+#define SIGS_FROM_OSST \
+ {"OnStream", "SC-", "", "osst"}, \
+ {"OnStream", "DI-", "", "osst"}, \
+ {"OnStream", "DP-", "", "osst"}, \
+ {"OnStream", "FW-", "", "osst"}, \
+ {"OnStream", "USB", "", "osst"}
diff --git a/drivers/scsi/osst_options.h b/drivers/scsi/osst_options.h
new file mode 100644
index 000000000000..ff1e610946ed
--- /dev/null
+++ b/drivers/scsi/osst_options.h
@@ -0,0 +1,106 @@
+/*
+ The compile-time configurable defaults for the Linux SCSI tape driver.
+
+ Copyright 1995 Kai Makisara.
+
+ Last modified: Wed Sep 2 21:24:07 1998 by root@home
+
+ Changed (and renamed) for OnStream SCSI drives garloff@suse.de
+ 2000-06-21
+
+ $Header: /cvsroot/osst/Driver/osst_options.h,v 1.6 2003/12/23 14:22:12 wriede Exp $
+*/
+
+#ifndef _OSST_OPTIONS_H
+#define _OSST_OPTIONS_H
+
+/* The minimum limit for the number of SCSI tape devices is determined by
+ OSST_MAX_TAPES. If the number of tape devices and the "slack" defined by
+ OSST_EXTRA_DEVS exceeds OSST_MAX_TAPES, the large number is used. */
+#define OSST_MAX_TAPES 4
+
+/* If OSST_IN_FILE_POS is nonzero, the driver positions the tape after the
+ record been read by the user program even if the tape has moved further
+ because of buffered reads. Should be set to zero to support also drives
+ that can't space backwards over records. NOTE: The tape will be
+ spaced backwards over an "accidentally" crossed filemark in any case. */
+#define OSST_IN_FILE_POS 1
+
+/* The tape driver buffer size in kilobytes. */
+/* Don't change, as this is the HW blocksize */
+#define OSST_BUFFER_BLOCKS 32
+
+/* The number of kilobytes of data in the buffer that triggers an
+ asynchronous write in fixed block mode. See also OSST_ASYNC_WRITES
+ below. */
+#define OSST_WRITE_THRESHOLD_BLOCKS 32
+
+/* OSST_EOM_RESERVE defines the number of frames are kept in reserve for
+ * * write error recovery when writing near end of medium. ENOSPC is returned
+ * * when write() is called and the tape write position is within this number
+ * * of blocks from the tape capacity. */
+#define OSST_EOM_RESERVE 300
+
+/* The maximum number of tape buffers the driver allocates. The number
+ is also constrained by the number of drives detected. Determines the
+ maximum number of concurrently active tape drives. */
+#define OSST_MAX_BUFFERS OSST_MAX_TAPES
+
+/* Maximum number of scatter/gather segments */
+/* Fit one buffer in pages and add one for the AUX header */
+#define OSST_MAX_SG (((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + 1)
+
+/* The number of scatter/gather segments to allocate at first try (must be
+ smaller or equal to the maximum). */
+#define OSST_FIRST_SG ((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE)
+
+/* The size of the first scatter/gather segments (determines the maximum block
+ size for SCSI adapters not supporting scatter/gather). The default is set
+ to try to allocate the buffer as one chunk. */
+#define OSST_FIRST_ORDER (15-PAGE_SHIFT)
+
+
+/* The following lines define defaults for properties that can be set
+ separately for each drive using the MTSTOPTIONS ioctl. */
+
+/* If OSST_TWO_FM is non-zero, the driver writes two filemarks after a
+ file being written. Some drives can't handle two filemarks at the
+ end of data. */
+#define OSST_TWO_FM 0
+
+/* If OSST_BUFFER_WRITES is non-zero, writes in fixed block mode are
+ buffered until the driver buffer is full or asynchronous write is
+ triggered. */
+#define OSST_BUFFER_WRITES 1
+
+/* If OSST_ASYNC_WRITES is non-zero, the SCSI write command may be started
+ without waiting for it to finish. May cause problems in multiple
+ tape backups. */
+#define OSST_ASYNC_WRITES 1
+
+/* If OSST_READ_AHEAD is non-zero, blocks are read ahead in fixed block
+ mode. */
+#define OSST_READ_AHEAD 1
+
+/* If OSST_AUTO_LOCK is non-zero, the drive door is locked at the first
+ read or write command after the device is opened. The door is opened
+ when the device is closed. */
+#define OSST_AUTO_LOCK 0
+
+/* If OSST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the
+ direct SCSI command. The file number status is lost but this method
+ is fast with some drives. Otherwise MTEOM is done by spacing over
+ files and the file number status is retained. */
+#define OSST_FAST_MTEOM 0
+
+/* If OSST_SCSI2LOGICAL is nonzero, the logical block addresses are used for
+ MTIOCPOS and MTSEEK by default. Vendor addresses are used if OSST_SCSI2LOGICAL
+ is zero. */
+#define OSST_SCSI2LOGICAL 0
+
+/* If OSST_SYSV is non-zero, the tape behaves according to the SYS V semantics.
+ The default is BSD semantics. */
+#define OSST_SYSV 0
+
+
+#endif
diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c
new file mode 100644
index 000000000000..e70dedb0d0a5
--- /dev/null
+++ b/drivers/scsi/pas16.c
@@ -0,0 +1,639 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+#define FOO
+#define UNSAFE /* Not unsafe for PAS16 -- use it */
+
+/*
+ * This driver adapted from Drew Eckhardt's Trantor T128 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * ( Based on T128 - DISTRIBUTION RELEASE 3. )
+ *
+ * Modified to work with the Pro Audio Spectrum/Studio 16
+ * by John Weidman.
+ *
+ *
+ * For more information, please consult
+ *
+ * Media Vision
+ * (510) 770-8600
+ * (800) 348-7116
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ * bytes at a time. Since interrupts are disabled by default during
+ * these transfers, we might need this to give reasonable interrupt
+ * service time if the transfer size gets too large.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This
+ * parameter comes from the NCR5380 code. It is NOT unsafe with
+ * the PAS16 and you should use it. If you don't you will have
+ * a problem with dropped characters during high speed
+ * communications during SCSI transfers. If you really don't
+ * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or
+ * twiddle with the transfer size in the high level code.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - There are many different models of
+ * the Pro Audio Spectrum/Studio 16, and I only have one of
+ * them, so this may require a little tweaking. An interrupt
+ * is triggered to autoprobe for the interrupt line. Note:
+ * with the newer model boards, the interrupt is set via
+ * software after reset using the default_irq for the
+ * current board number.
+ *
+ * 2. With command line overrides - pas16=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 3. With the PAS16_OVERRIDE compile time define. This is
+ * specified as an array of address, irq tuples. Ie, for
+ * one board at the default 0x388 address, IRQ10, I could say
+ * -DPAS16_OVERRIDE={{0x388, 10}}
+ * NOTE: Untested.
+ *
+ * 4. When included as a module, with arguments passed on the command line:
+ * pas16_irq=xx the interrupt
+ * pas16_addr=xx the port
+ * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5"
+ *
+ * Note that if the override methods are used, place holders must
+ * be specified for other boards in the system.
+ *
+ *
+ * Configuration notes :
+ * The current driver does not support interrupt sharing with the
+ * sound portion of the card. If you use the same irq for the
+ * scsi port and sound you will have problems. Either use
+ * a different irq for the scsi port or don't use interrupts
+ * for the scsi port.
+ *
+ * If you have problems with your card not being recognized, use
+ * the LILO command line override. Try to get it recognized without
+ * interrupts. Ie, for a board at the default 0x388 base port,
+ * boot: linux pas16=0x388,255
+ *
+ * SCSI_IRQ_NONE (255) should be specified for no interrupt,
+ * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden
+ * on the command line.
+ *
+ * (IRQ_AUTO == 254, SCSI_IRQ_NONE == 255 in NCR5380.h)
+ */
+
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "pas16.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+
+
+static int pas_maxi = 0;
+static int pas_wmaxi = 0;
+static unsigned short pas16_addr = 0;
+static int pas16_irq = 0;
+
+
+int scsi_irq_translate[] =
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
+
+/* The default_irqs array contains values used to set the irq into the
+ * board via software (as must be done on newer model boards without
+ * irq jumpers on the board). The first value in the array will be
+ * assigned to logical board 0, the next to board 1, etc.
+ */
+int default_irqs[] __initdata =
+ { PAS16_DEFAULT_BOARD_1_IRQ,
+ PAS16_DEFAULT_BOARD_2_IRQ,
+ PAS16_DEFAULT_BOARD_3_IRQ,
+ PAS16_DEFAULT_BOARD_4_IRQ
+ };
+
+static struct override {
+ unsigned short io_port;
+ int irq;
+} overrides
+#ifdef PAS16_OVERRIDE
+ [] __initdata = PAS16_OVERRIDE;
+#else
+ [4] __initdata = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO},
+ {0,IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned short io_port;
+ int noauto;
+} bases[] __initdata =
+ { {PAS16_DEFAULT_BASE_1, 0},
+ {PAS16_DEFAULT_BASE_2, 0},
+ {PAS16_DEFAULT_BASE_3, 0},
+ {PAS16_DEFAULT_BASE_4, 0}
+ };
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+unsigned short pas16_offset[ 8 ] =
+ {
+ 0x1c00, /* OUTPUT_DATA_REG */
+ 0x1c01, /* INITIATOR_COMMAND_REG */
+ 0x1c02, /* MODE_REG */
+ 0x1c03, /* TARGET_COMMAND_REG */
+ 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */
+ 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */
+ 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?)
+ * START_DMA_TARGET_RECEIVE_REG wo
+ */
+ 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro,
+ * START_DMA_INITIATOR_RECEIVE_REG wo
+ */
+ };
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+#if 1
+#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+
+/*
+ * Function : enable_board( int board_num, unsigned short port )
+ *
+ * Purpose : set address in new model board
+ *
+ * Inputs : board_num - logical board number 0-3, port - base address
+ *
+ */
+
+static void __init
+ enable_board( int board_num, unsigned short port )
+{
+ outb( 0xbc + board_num, MASTER_ADDRESS_PTR );
+ outb( port >> 2, MASTER_ADDRESS_PTR );
+}
+
+
+
+/*
+ * Function : init_board( unsigned short port, int irq )
+ *
+ * Purpose : Set the board up to handle the SCSI interface
+ *
+ * Inputs : port - base address of the board,
+ * irq - irq to assign to the SCSI port
+ * force_irq - set it even if it conflicts with sound driver
+ *
+ */
+
+static void __init
+ init_board( unsigned short io_port, int irq, int force_irq )
+{
+ unsigned int tmp;
+ unsigned int pas_irq_code;
+
+ /* Initialize the SCSI part of the board */
+
+ outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */
+ outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */
+ outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
+
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ /* Set the SCSI interrupt pointer without mucking up the sound
+ * interrupt pointer in the same byte.
+ */
+ pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0;
+ tmp = inb( io_port + IO_CONFIG_3 );
+
+ if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0
+ && !force_irq )
+ {
+ printk( "pas16: WARNING: Can't use same irq as sound "
+ "driver -- interrupts disabled\n" );
+ /* Set up the drive parameters, disable 5380 interrupts */
+ outb( 0x4d, io_port + SYS_CONFIG_4 );
+ }
+ else
+ {
+ tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 );
+ outb( tmp, io_port + IO_CONFIG_3 );
+
+ /* Set up the drive parameters and enable 5380 interrupts */
+ outb( 0x6d, io_port + SYS_CONFIG_4 );
+ }
+}
+
+
+/*
+ * Function : pas16_hw_detect( unsigned short board_num )
+ *
+ * Purpose : determine if a pas16 board is present
+ *
+ * Inputs : board_num - logical board number ( 0 - 3 )
+ *
+ * Returns : 0 if board not found, 1 if found.
+ */
+
+static int __init
+ pas16_hw_detect( unsigned short board_num )
+{
+ unsigned char board_rev, tmp;
+ unsigned short io_port = bases[ board_num ].io_port;
+
+ /* See if we can find a PAS16 board at the address associated
+ * with this logical board number.
+ */
+
+ /* First, attempt to take a newer model board out of reset and
+ * give it a base address. This shouldn't affect older boards.
+ */
+ enable_board( board_num, io_port );
+
+ /* Now see if it looks like a PAS16 board */
+ board_rev = inb( io_port + PCB_CONFIG );
+
+ if( board_rev == 0xff )
+ return 0;
+
+ tmp = board_rev ^ 0xe0;
+
+ outb( tmp, io_port + PCB_CONFIG );
+ tmp = inb( io_port + PCB_CONFIG );
+ outb( board_rev, io_port + PCB_CONFIG );
+
+ if( board_rev != tmp ) /* Not a PAS-16 */
+ return 0;
+
+ if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 )
+ return 0; /* return if no SCSI interface found */
+
+ /* Mediavision has some new model boards that return ID bits
+ * that indicate a SCSI interface, but they're not (LMS). We'll
+ * put in an additional test to try to weed them out.
+ */
+
+ outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
+ NCR5380_write( MODE_REG, 0x20 ); /* Is it really SCSI? */
+ if( NCR5380_read( MODE_REG ) != 0x20 ) /* Write to a reg. */
+ return 0; /* and try to read */
+ NCR5380_write( MODE_REG, 0x00 ); /* it back. */
+ if( NCR5380_read( MODE_REG ) != 0x00 )
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * Function : pas16_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void __init pas16_setup(char *str, int *ints)
+{
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("pas16_setup : usage pas16=io_port,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].io_port = (unsigned short) ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].io_port == (unsigned short) ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int pas16_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes PAS16 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int __init pas16_detect(Scsi_Host_Template * tpnt)
+{
+ static int current_override = 0;
+ static unsigned short current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned short io_port;
+ int count;
+
+ tpnt->proc_name = "pas16";
+ tpnt->proc_info = &pas16_proc_info;
+
+ if (pas16_addr != 0) {
+ overrides[0].io_port = pas16_addr;
+ /*
+ * This is how we avoid seeing more than
+ * one host adapter at the same I/O port.
+ * Cribbed shamelessly from pas16_setup().
+ */
+ for (count = 0; count < NO_BASES; ++count)
+ if (bases[count].io_port == pas16_addr) {
+ bases[count].noauto = 1;
+ break;
+ }
+ }
+ if (pas16_irq != 0)
+ overrides[0].irq = pas16_irq;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ io_port = 0;
+
+ if (overrides[current_override].io_port)
+ {
+ io_port = overrides[current_override].io_port;
+ enable_board( current_override, io_port );
+ init_board( io_port, overrides[current_override].irq, 1 );
+ }
+ else
+ for (; !io_port && (current_base < NO_BASES); ++current_base) {
+#if (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : probing io_port %04x\n", (unsigned int) bases[current_base].io_port);
+#endif
+ if ( !bases[current_base].noauto &&
+ pas16_hw_detect( current_base ) ){
+ io_port = bases[current_base].io_port;
+ init_board( io_port, default_irqs[ current_base ], 0 );
+#if (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : detected board.\n");
+#endif
+ }
+ }
+
+
+#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : io_port = %04x\n", (unsigned int) io_port);
+#endif
+
+ if (!io_port)
+ break;
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ if(instance == NULL)
+ break;
+
+ instance->io_port = io_port;
+
+ NCR5380_init(instance, 0);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS);
+
+ if (instance->irq != SCSI_IRQ_NONE)
+ if (request_irq(instance->irq, pas16_intr, SA_INTERRUPT, "pas16", instance)) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+ }
+
+ if (instance->irq == SCSI_IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ /* Disable 5380 interrupts, leave drive params the same */
+ outb( 0x4d, io_port + SYS_CONFIG_4 );
+ outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 );
+ }
+
+#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%04x", instance->host_no, (int)
+ instance->io_port);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int pas16_biosparam(Disk *disk, struct block_device *dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int * ip)
+{
+ int size = capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11; /* I think I have it as /(32*64) */
+ if( ip[2] > 1024 ) { /* yes, >, not >= */
+ ip[0]=255;
+ ip[1]=63;
+ ip[2]=size/(63*255);
+ if( ip[2] > 1023 ) /* yes >1023... */
+ ip[2] = 1023;
+ }
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
+ int len) {
+ register unsigned char *d = dst;
+ register unsigned short reg = (unsigned short) (instance->io_port +
+ P_DATA_REG_OFFSET);
+ register int i = len;
+ int ii = 0;
+
+ while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
+ ++ii;
+
+ insb( reg, d, i );
+
+ if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
+ outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
+ printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
+ instance->host_no);
+ return -1;
+ }
+ if (ii > pas_maxi)
+ pas_maxi = ii;
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
+ int len) {
+ register unsigned char *s = src;
+ register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
+ register int i = len;
+ int ii = 0;
+
+ while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
+ ++ii;
+
+ outsb( reg, s, i );
+
+ if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
+ outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
+ printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
+ instance->host_no);
+ return -1;
+ }
+ if (ii > pas_maxi)
+ pas_wmaxi = ii;
+ return 0;
+}
+
+#include "NCR5380.c"
+
+static int pas16_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ NCR5380_exit(shost);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "Pro Audio Spectrum-16 SCSI",
+ .detect = pas16_detect,
+ .release = pas16_release,
+ .queuecommand = pas16_queue_command,
+ .eh_abort_handler = pas16_abort,
+ .eh_bus_reset_handler = pas16_bus_reset,
+ .eh_device_reset_handler = pas16_device_reset,
+ .eh_host_reset_handler = pas16_host_reset,
+ .bios_param = pas16_biosparam,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
+
+#ifdef MODULE
+module_param(pas16_addr, ushort, 0);
+module_param(pas16_irq, int, 0);
+#endif
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h
new file mode 100644
index 000000000000..58d4d67aed24
--- /dev/null
+++ b/drivers/scsi/pas16.h
@@ -0,0 +1,179 @@
+/*
+ * This driver adapted from Drew Eckhardt's Trantor T128 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * ( Based on T128 - DISTRIBUTION RELEASE 3. )
+ *
+ * Modified to work with the Pro Audio Spectrum/Studio 16
+ * by John Weidman.
+ *
+ *
+ * For more information, please consult
+ *
+ * Media Vision
+ * (510) 770-8600
+ * (800) 348-7116
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+
+#ifndef PAS16_H
+#define PAS16_H
+
+#define PAS16_PUBLIC_RELEASE 3
+
+#define PDEBUG_INIT 0x1
+#define PDEBUG_TRANSFER 0x2
+
+#define PAS16_DEFAULT_BASE_1 0x388
+#define PAS16_DEFAULT_BASE_2 0x384
+#define PAS16_DEFAULT_BASE_3 0x38c
+#define PAS16_DEFAULT_BASE_4 0x288
+
+#define PAS16_DEFAULT_BOARD_1_IRQ 10
+#define PAS16_DEFAULT_BOARD_2_IRQ 12
+#define PAS16_DEFAULT_BOARD_3_IRQ 14
+#define PAS16_DEFAULT_BOARD_4_IRQ 15
+
+
+/*
+ * The Pro Audio Spectrum boards are I/O mapped. They use a Zilog 5380
+ * SCSI controller, which is the equivalent of NCR's 5380. "Pseudo-DMA"
+ * architecture is used, where a PAL drives the DMA signals on the 5380
+ * allowing fast, blind transfers with proper handshaking.
+ */
+
+
+/* The Time-out Counter register is used to safe-guard against a stuck
+ * bus (in the case of RDY driven handshake) or a stuck byte (if 16-Bit
+ * DMA conversion is used). The counter uses a 28.224MHz clock
+ * divided by 14 as its clock source. In the case of a stuck byte in
+ * the holding register, an interrupt is generated (and mixed with the
+ * one with the drive) using the CD-ROM interrupt pointer.
+ */
+
+#define P_TIMEOUT_COUNTER_REG 0x4000
+#define P_TC_DISABLE 0x80 /* Set to 0 to enable timeout int. */
+ /* Bits D6-D0 contain timeout count */
+
+
+#define P_TIMEOUT_STATUS_REG_OFFSET 0x4001
+#define P_TS_TIM 0x80 /* check timeout status */
+ /* Bits D6-D4 N/U */
+#define P_TS_ARM_DRQ_INT 0x08 /* Arm DRQ Int. When set high,
+ * the next rising edge will
+ * cause a CD-ROM interrupt.
+ * When set low, the interrupt
+ * will be cleared. There is
+ * no status available for
+ * this interrupt.
+ */
+#define P_TS_ENABLE_TO_ERR_INTERRUPT /* Enable timeout error int. */
+#define P_TS_ENABLE_WAIT /* Enable Wait */
+
+#define P_TS_CT 0x01 /* clear timeout. Note: writing
+ * to this register clears the
+ * timeout error int. or status
+ */
+
+
+/*
+ * The data register reads/writes to/from the 5380 in pseudo-DMA mode
+ */
+
+#define P_DATA_REG_OFFSET 0x5c00 /* rw */
+
+#define P_STATUS_REG_OFFSET 0x5c01 /* ro */
+#define P_ST_RDY 0x80 /* 5380 DDRQ Status */
+
+#define P_IRQ_STATUS 0x5c03
+#define P_IS_IRQ 0x80 /* DIRQ status */
+
+#define PCB_CONFIG 0x803
+#define MASTER_ADDRESS_PTR 0x9a01 /* Fixed position - no relo */
+#define SYS_CONFIG_4 0x8003
+#define WAIT_STATE 0xbc00
+#define OPERATION_MODE_1 0xec03
+#define IO_CONFIG_3 0xf002
+
+
+#ifndef ASM
+static int pas16_abort(Scsi_Cmnd *);
+static int pas16_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int*);
+static int pas16_detect(Scsi_Host_Template *);
+static int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int pas16_bus_reset(Scsi_Cmnd *);
+static int pas16_host_reset(Scsi_Cmnd *);
+static int pas16_device_reset(Scsi_Cmnd *);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ volatile unsigned short io_port
+
+#define NCR5380_local_declare() \
+ volatile unsigned short io_port
+
+#define NCR5380_setup(instance) \
+ io_port = (instance)->io_port
+
+#define PAS16_io_port(reg) ( io_port + pas16_offset[(reg)] )
+
+#if !(PDEBUG & PDEBUG_TRANSFER)
+#define NCR5380_read(reg) ( inb(PAS16_io_port(reg)) )
+#define NCR5380_write(reg, value) ( outb((value),PAS16_io_port(reg)) )
+#else
+#define NCR5380_read(reg) \
+ (((unsigned char) printk("scsi%d : read register %d at io_port %04x\n"\
+ , instance->hostno, (reg), PAS16_io_port(reg))), inb( PAS16_io_port(reg)) )
+
+#define NCR5380_write(reg, value) \
+ (printk("scsi%d : write %02x to register %d at io_port %04x\n", \
+ instance->hostno, (value), (reg), PAS16_io_port(reg)), \
+ outb( (value),PAS16_io_port(reg) ) )
+
+#endif
+
+
+#define NCR5380_intr pas16_intr
+#define do_NCR5380_intr do_pas16_intr
+#define NCR5380_queue_command pas16_queue_command
+#define NCR5380_abort pas16_abort
+#define NCR5380_device_reset pas16_device_reset
+#define NCR5380_bus_reset pas16_bus_reset
+#define NCR5380_host_reset pas16_host_reset
+#define NCR5380_proc_info pas16_proc_info
+
+/* 15 14 12 10 7 5 3
+ 1101 0100 1010 1000 */
+
+#define PAS16_IRQS 0xd4a8
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* PAS16_H */
diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
new file mode 100644
index 000000000000..60ce1cce9497
--- /dev/null
+++ b/drivers/scsi/pci2000.c
@@ -0,0 +1,834 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver for Linux.
+ *
+ * pci2000.c - Linux Host Driver for PCI-2000 IntelliCache SCSI Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ * http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ * tech@psidisk.com Technical Support
+ *
+ *
+ * Revisions 1.10 Jan-21-1999
+ * - Fixed sign on message to reflect proper controller name.
+ * - Added support for RAID status monitoring and control.
+ *
+ * Revisions 1.11 Mar-22-1999
+ * - Fixed control timeout to not lock up the entire system if
+ * controller goes offline completely.
+ *
+ * Revisions 1.12 Mar-26-1999
+ * - Fixed spinlock and PCI configuration.
+ *
+ * Revisions 1.20 Mar-27-2000
+ * - Added support for dynamic DMA
+ *
+ ****************************************************************************/
+#define PCI2000_VERSION "1.20"
+
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "pci2000.h"
+#include "psi_roy.h"
+
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+typedef struct
+ {
+ unsigned int address;
+ unsigned int length;
+ } SCATGATH, *PSCATGATH;
+
+typedef struct
+ {
+ Scsi_Cmnd *SCpnt;
+ PSCATGATH scatGath;
+ dma_addr_t scatGathDma;
+ UCHAR *cdb;
+ dma_addr_t cdbDma;
+ UCHAR tag;
+ } DEV2000, *PDEV2000;
+
+typedef struct
+ {
+ ULONG basePort;
+ ULONG mb0;
+ ULONG mb1;
+ ULONG mb2;
+ ULONG mb3;
+ ULONG mb4;
+ ULONG cmd;
+ ULONG tag;
+ ULONG irqOwned;
+ struct pci_dev *pdev;
+ DEV2000 dev[MAX_BUS][MAX_UNITS];
+ } ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+#define consistentLen (MAX_BUS * MAX_UNITS * (16 * sizeof (SCATGATH) + MAX_COMMAND_SIZE))
+
+
+static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter
+static int NumAdapters = 0;
+/****************************************************************
+ * Name: WaitReady :LOCAL
+ *
+ * Description: Wait for controller ready.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2000 padapter)
+ {
+ ULONG z;
+
+ for ( z = 0; z < (TIMEOUT_COMMAND * 4); z++ )
+ {
+ if ( !inb_p (padapter->cmd) )
+ return FALSE;
+ udelay (250);
+ };
+ return TRUE;
+ }
+/****************************************************************
+ * Name: WaitReadyLong :LOCAL
+ *
+ * Description: Wait for controller ready.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReadyLong (PADAPTER2000 padapter)
+ {
+ ULONG z;
+
+ for ( z = 0; z < (5000 * 4); z++ )
+ {
+ if ( !inb_p (padapter->cmd) )
+ return FALSE;
+ udelay (250);
+ };
+ return TRUE;
+ }
+/****************************************************************
+ * Name: OpDone :LOCAL
+ *
+ * Description: Clean up operation and issue done to caller.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * status - Caller status.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void OpDone (Scsi_Cmnd *SCpnt, ULONG status)
+ {
+ SCpnt->result = status;
+ SCpnt->scsi_done (SCpnt);
+ }
+/****************************************************************
+ * Name: Command :LOCAL
+ *
+ * Description: Issue queued command to the PCI-2000.
+ *
+ * Parameters: padapter - Pointer to adapter information structure.
+ * cmd - PCI-2000 command byte.
+ *
+ * Returns: Non-zero command tag if operation is accepted.
+ *
+ ****************************************************************/
+static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd)
+ {
+ outb_p (cmd, padapter->cmd);
+ if ( WaitReady (padapter) )
+ return 0;
+
+ if ( inw_p (padapter->mb0) )
+ return 0;
+
+ return inb_p (padapter->mb1);
+ }
+/****************************************************************
+ * Name: BuildSgList :LOCAL
+ *
+ * Description: Build the scatter gather list for controller.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * padapter - Pointer to adapter information structure.
+ * pdev - Pointer to adapter device structure.
+ *
+ * Returns: Non-zero in not scatter gather.
+ *
+ ****************************************************************/
+static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev)
+ {
+ int z;
+ int zc;
+ struct scatterlist *sg;
+
+ if ( SCpnt->use_sg )
+ {
+ sg = (struct scatterlist *)SCpnt->request_buffer;
+ zc = pci_map_sg (padapter->pdev, sg, SCpnt->use_sg, scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+ for ( z = 0; z < zc; z++ )
+ {
+ pdev->scatGath[z].address = cpu_to_le32 (sg_dma_address (sg));
+ pdev->scatGath[z].length = cpu_to_le32 (sg_dma_len (sg++));
+ }
+ outl (pdev->scatGathDma, padapter->mb2);
+ outl ((zc << 24) | SCpnt->request_bufflen, padapter->mb3);
+ return FALSE;
+ }
+ if ( !SCpnt->request_bufflen)
+ {
+ outl (0, padapter->mb2);
+ outl (0, padapter->mb3);
+ return TRUE;
+ }
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, SCpnt->request_bufflen, scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+ outl (SCpnt->SCp.have_data_in, padapter->mb2);
+ outl (SCpnt->request_bufflen, padapter->mb3);
+ return TRUE;
+ }
+/*********************************************************************
+ * Name: PsiRaidCmd
+ *
+ * Description: Execute a simple command.
+ *
+ * Parameters: padapter - Pointer to adapter control structure.
+ * cmd - Roy command byte.
+ *
+ * Returns: Return error status.
+ *
+ ********************************************************************/
+static int PsiRaidCmd (PADAPTER2000 padapter, char cmd)
+ {
+ if ( WaitReady (padapter) ) // test for command register ready
+ return DID_TIME_OUT;
+ outb_p (cmd, padapter->cmd); // issue command
+ if ( WaitReadyLong (padapter) ) // wait for adapter ready
+ return DID_TIME_OUT;
+ return DID_OK;
+ }
+/****************************************************************
+ * Name: Irq_Handler :LOCAL
+ *
+ * Description: Interrupt handler.
+ *
+ * Parameters: irq - Hardware IRQ number.
+ * dev_id -
+ * regs -
+ *
+ * Returns: TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static irqreturn_t Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+ {
+ struct Scsi_Host *shost = NULL; // Pointer to host data block
+ PADAPTER2000 padapter; // Pointer to adapter control structure
+ PDEV2000 pdev;
+ Scsi_Cmnd *SCpnt;
+ UCHAR tag = 0;
+ UCHAR tag0;
+ ULONG error;
+ int pun;
+ int bus;
+ int z;
+ unsigned long flags;
+ int handled = 0;
+
+ DEB(printk ("\npci2000 received interrupt "));
+ for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process
+ {
+ if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+ {
+ tag = inb_p (HOSTDATA(PsiHost[z])->tag);
+ if ( tag )
+ {
+ shost = PsiHost[z];
+ break;
+ }
+ }
+ }
+
+ if ( !shost )
+ {
+ DEB (printk ("\npci2000: not my interrupt"));
+ goto out;
+ }
+
+ handled = 1;
+ spin_lock_irqsave(shost->host_lock, flags);
+ padapter = HOSTDATA(shost);
+
+ tag0 = tag & 0x7F; // mask off the error bit
+ for ( bus = 0; bus < MAX_BUS; bus++ ) // scan the busses
+ {
+ for ( pun = 0; pun < MAX_UNITS; pun++ ) // scan the targets
+ {
+ pdev = &padapter->dev[bus][pun];
+ if ( !pdev->tag )
+ continue;
+ if ( pdev->tag == tag0 ) // is this it?
+ {
+ pdev->tag = 0;
+ SCpnt = pdev->SCpnt;
+ goto unmapProceed;
+ }
+ }
+ }
+
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+ goto irq_return; // done, but, with what?
+
+unmapProceed:;
+ if ( !bus )
+ {
+ switch ( SCpnt->cmnd[0] )
+ {
+ case SCSIOP_TEST_UNIT_READY:
+ pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, sizeof (SCpnt->sense_buffer), PCI_DMA_FROMDEVICE);
+ goto irqProceed;
+ case SCSIOP_READ_CAPACITY:
+ pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, 8, PCI_DMA_FROMDEVICE);
+ goto irqProceed;
+ case SCSIOP_VERIFY:
+ case SCSIOP_START_STOP_UNIT:
+ case SCSIOP_MEDIUM_REMOVAL:
+ goto irqProceed;
+ }
+ }
+ if ( SCpnt->SCp.have_data_in )
+ pci_unmap_single (padapter->pdev, SCpnt->SCp.have_data_in, SCpnt->request_bufflen, scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ else
+ {
+ if ( SCpnt->use_sg )
+ pci_unmap_sg (padapter->pdev, (struct scatterlist *)SCpnt->request_buffer, SCpnt->use_sg, scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ }
+
+irqProceed:;
+ if ( tag & ERR08_TAGGED ) // is there an error here?
+ {
+ if ( WaitReady (padapter) )
+ {
+ OpDone (SCpnt, DID_TIME_OUT << 16);
+ goto irq_return;
+ }
+
+ outb_p (tag0, padapter->mb0); // get real error code
+ outb_p (CMD_ERROR, padapter->cmd);
+ if ( WaitReady (padapter) ) // wait for controller to suck up the op
+ {
+ OpDone (SCpnt, DID_TIME_OUT << 16);
+ goto irq_return;
+ }
+
+ error = inl (padapter->mb0); // get error data
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+
+ DEB (printk ("status: %lX ", error));
+ if ( error == 0x00020002 ) // is this error a check condition?
+ {
+ if ( bus ) // are we doint SCSI commands?
+ {
+ OpDone (SCpnt, (DID_OK << 16) | 2);
+ goto irq_return;
+ }
+ if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY )
+ OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2); // test caller we have sense data too
+ else
+ OpDone (SCpnt, DID_ERROR << 16);
+ goto irq_return;
+ }
+ OpDone (SCpnt, DID_ERROR << 16);
+ goto irq_return;
+ }
+
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+ OpDone (SCpnt, DID_OK << 16);
+
+irq_return:
+ spin_unlock_irqrestore(shost->host_lock, flags);
+out:
+ return IRQ_RETVAL(handled);
+}
+/****************************************************************
+ * Name: Pci2000_QueueCommand
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * done - Pointer to done function to call.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER2000 padapter = HOSTDATA(SCpnt->device->host); // Pointer to adapter control structure
+ int rc = -1; // command return code
+ UCHAR bus = SCpnt->device->channel;
+ UCHAR pun = SCpnt->device->id;
+ UCHAR lun = SCpnt->device->lun;
+ UCHAR cmd;
+ PDEV2000 pdev = &padapter->dev[bus][pun];
+
+ if ( !done )
+ {
+ printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ SCpnt->scsi_done = done;
+ SCpnt->SCp.have_data_in = 0;
+ pdev->SCpnt = SCpnt; // Save this command data
+
+ if ( WaitReady (padapter) )
+ {
+ rc = DID_ERROR;
+ goto finished;
+ }
+
+ outw_p (pun | (lun << 8), padapter->mb0);
+
+ if ( bus )
+ {
+ DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+ DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command,
+ SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout));
+ outl (SCpnt->timeout_per_command, padapter->mb1);
+ outb_p (CMD_SCSI_TIMEOUT, padapter->cmd);
+ if ( WaitReady (padapter) )
+ {
+ rc = DID_ERROR;
+ goto finished;
+ }
+
+ outw_p (pun | (lun << 8), padapter->mb0);
+ outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2);
+ memcpy (pdev->cdb, cdb, MAX_COMMAND_SIZE);
+
+ outl (pdev->cdbDma, padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_SCSI_THRU;
+ else
+ cmd = CMD_SCSI_THRU_SG;
+ if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+ rc = DID_TIME_OUT;
+ goto finished;
+ }
+ else
+ {
+ if ( lun )
+ {
+ rc = DID_BAD_TARGET;
+ goto finished;
+ }
+ }
+
+ switch ( *cdb )
+ {
+ case SCSIOP_INQUIRY: // inquiry CDB
+ if ( cdb[2] == SC_MY_RAID )
+ {
+ switch ( cdb[3] )
+ {
+ case MY_SCSI_REBUILD:
+ OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_REBUILD) << 16);
+ return 0;
+ case MY_SCSI_ALARMMUTE:
+ OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_MUTE) << 16);
+ return 0;
+ case MY_SCSI_DEMOFAIL:
+ OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_FAIL) << 16);
+ return 0;
+ default:
+ if ( SCpnt->use_sg )
+ {
+ rc = DID_ERROR;
+ goto finished;
+ }
+ else
+ {
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, SCpnt->request_bufflen,
+ scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ outl (SCpnt->SCp.have_data_in, padapter->mb2);
+ }
+ outl (cdb[5], padapter->mb0);
+ outl (cdb[3], padapter->mb3);
+ cmd = CMD_DASD_RAID_RQ;
+ break;
+ }
+ break;
+ }
+
+ if ( SCpnt->use_sg )
+ {
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev,
+ ((struct scatterlist *)SCpnt->request_buffer)->address,
+ SCpnt->request_bufflen,
+ scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+ }
+ else
+ {
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ scsi_to_pci_dma_dir (SCpnt->sc_data_direction));
+ }
+ outl (SCpnt->SCp.have_data_in, padapter->mb2);
+ outl (SCpnt->request_bufflen, padapter->mb3);
+ cmd = CMD_DASD_SCSI_INQ;
+ break;
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->sense_buffer, sizeof (SCpnt->sense_buffer), PCI_DMA_FROMDEVICE);
+ outl (SCpnt->SCp.have_data_in, padapter->mb2);
+ outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+ cmd = CMD_TEST_READY;
+ break;
+
+ case SCSIOP_READ_CAPACITY: // read capacity CDB
+ if ( SCpnt->use_sg )
+ {
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, ((struct scatterlist *)(SCpnt->request_buffer))->address,
+ 8, PCI_DMA_FROMDEVICE);
+ }
+ else
+ SCpnt->SCp.have_data_in = pci_map_single (padapter->pdev, SCpnt->request_buffer, 8, PCI_DMA_FROMDEVICE);
+ outl (SCpnt->SCp.have_data_in, padapter->mb2);
+ outl (8, padapter->mb3);
+ cmd = CMD_DASD_CAP;
+ break;
+ case SCSIOP_VERIFY: // verify CDB
+ outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+ outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+ cmd = CMD_READ_SG;
+ break;
+ case SCSIOP_READ: // read10 CDB
+ outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+ outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_READ;
+ else
+ cmd = CMD_READ_SG;
+ break;
+ case SCSIOP_READ6: // read6 CDB
+ outw_p (cdb[4], padapter->mb0 + 2);
+ outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_READ;
+ else
+ cmd = CMD_READ_SG;
+ break;
+ case SCSIOP_WRITE: // write10 CDB
+ outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+ outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_WRITE;
+ else
+ cmd = CMD_WRITE_SG;
+ break;
+ case SCSIOP_WRITE6: // write6 CDB
+ outw_p (cdb[4], padapter->mb0 + 2);
+ outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_WRITE;
+ else
+ cmd = CMD_WRITE_SG;
+ break;
+ case SCSIOP_START_STOP_UNIT:
+ cmd = CMD_EJECT_MEDIA;
+ break;
+ case SCSIOP_MEDIUM_REMOVAL:
+ switch ( cdb[4] )
+ {
+ case 0:
+ cmd = CMD_UNLOCK_DOOR;
+ break;
+ case 1:
+ cmd = CMD_LOCK_DOOR;
+ break;
+ default:
+ cmd = 0;
+ break;
+ }
+ if ( cmd )
+ break;
+ default:
+ DEB (printk ("pci2000_queuecommand: Unsupported command %02X\n", *cdb));
+ OpDone (SCpnt, DID_ERROR << 16);
+ return 0;
+ }
+
+ if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+ rc = DID_TIME_OUT;
+finished:;
+ if ( rc != -1 )
+ OpDone (SCpnt, rc << 16);
+ return 0;
+ }
+/****************************************************************
+ * Name: Pci2000_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters installed.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+ {
+ int found = 0;
+ int installed = 0;
+ struct Scsi_Host *pshost;
+ PADAPTER2000 padapter;
+ int z, zz;
+ int setirq;
+ struct pci_dev *pdev = NULL;
+ UCHAR *consistent;
+ dma_addr_t consistentDma;
+
+ while ( (pdev = pci_find_device (VENDOR_PSI, DEVICE_ROY_1, pdev)) != NULL )
+ {
+ if (pci_enable_device(pdev))
+ continue;
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+ if(pshost == NULL)
+ continue;
+ padapter = HOSTDATA(pshost);
+
+ padapter->basePort = pci_resource_start (pdev, 1);
+ DEB (printk ("\nBase Regs = %#04X", padapter->basePort)); // get the base I/O port address
+ padapter->mb0 = padapter->basePort + RTR_MAILBOX; // get the 32 bit mail boxes
+ padapter->mb1 = padapter->basePort + RTR_MAILBOX + 4;
+ padapter->mb2 = padapter->basePort + RTR_MAILBOX + 8;
+ padapter->mb3 = padapter->basePort + RTR_MAILBOX + 12;
+ padapter->mb4 = padapter->basePort + RTR_MAILBOX + 16;
+ padapter->cmd = padapter->basePort + RTR_LOCAL_DOORBELL; // command register
+ padapter->tag = padapter->basePort + RTR_PCI_DOORBELL; // tag/response register
+ padapter->pdev = pdev;
+
+ if ( WaitReady (padapter) )
+ goto unregister;
+ outb_p (0x84, padapter->mb0);
+ outb_p (CMD_SPECIFY, padapter->cmd);
+ if ( WaitReady (padapter) )
+ goto unregister;
+
+ consistent = pci_alloc_consistent (pdev, consistentLen, &consistentDma);
+ if ( !consistent )
+ {
+ printk ("Unable to allocate DMA memory for PCI-2000 controller.\n");
+ goto unregister;
+ }
+
+ scsi_set_device(pshost, &pdev->dev);
+ pshost->irq = pdev->irq;
+ setirq = 1;
+ padapter->irqOwned = 0;
+ for ( z = 0; z < installed; z++ ) // scan for shared interrupts
+ {
+ if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses
+ setirq = 0;
+ }
+ if ( setirq ) // if not shared, posses
+ {
+ if ( request_irq (pshost->irq, Irq_Handler, SA_SHIRQ, "pci2000", padapter) < 0 )
+ {
+ if ( request_irq (pshost->irq, Irq_Handler, SA_INTERRUPT | SA_SHIRQ, "pci2000", padapter) < 0 )
+ {
+ printk ("Unable to allocate IRQ for PCI-2000 controller.\n");
+ pci_free_consistent (pdev, consistentLen, consistent, consistentDma);
+ goto unregister;
+ }
+ }
+ padapter->irqOwned = pshost->irq; // set IRQ as owned
+ }
+ PsiHost[installed] = pshost; // save SCSI_HOST pointer
+
+ pshost->io_port = padapter->basePort;
+ pshost->n_io_port = 0xFF;
+ pshost->unique_id = padapter->basePort;
+ pshost->max_id = 16;
+ pshost->max_channel = 1;
+
+ for ( zz = 0; zz < MAX_BUS; zz++ )
+ for ( z = 0; z < MAX_UNITS; z++ )
+ {
+ padapter->dev[zz][z].tag = 0;
+ padapter->dev[zz][z].scatGath = (PSCATGATH)consistent;
+ padapter->dev[zz][z].scatGathDma = consistentDma;
+ consistent += 16 * sizeof (SCATGATH);
+ consistentDma += 16 * sizeof (SCATGATH);
+ padapter->dev[zz][z].cdb = (UCHAR *)consistent;
+ padapter->dev[zz][z].cdbDma = consistentDma;
+ consistent += MAX_COMMAND_SIZE;
+ consistentDma += MAX_COMMAND_SIZE;
+ }
+
+ printk("\nPSI-2000 Intelligent Storage SCSI CONTROLLER: at I/O = %lX IRQ = %d\n", padapter->basePort, pshost->irq);
+ printk("Version %s, Compiled %s %s\n\n", PCI2000_VERSION, __DATE__, __TIME__);
+ found++;
+ if ( ++installed < MAXADAPTER )
+ continue;
+ break;
+unregister:;
+ scsi_unregister (pshost);
+ found++;
+ }
+ NumAdapters = installed;
+ return installed;
+ }
+/****************************************************************
+ * Name: Pci2000_Abort
+ *
+ * Description: Process the Abort command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Allways snooze.
+ *
+ ****************************************************************/
+int Pci2000_Abort (Scsi_Cmnd *SCpnt)
+ {
+ DEB (printk ("pci2000_abort\n"));
+ return SCSI_ABORT_SNOOZE;
+ }
+/****************************************************************
+ * Name: Pci2000_Reset
+ *
+ * Description: Process the Reset command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * flags - Flags about the reset command
+ *
+ * Returns: No active command at this time, so this means
+ * that each time we got some kind of response the
+ * last time through. Tell the mid-level code to
+ * request sense information in order to decide what
+ * to do next.
+ *
+ ****************************************************************/
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ return SCSI_RESET_PUNT;
+ }
+/****************************************************************
+ * Name: Pci2000_Release
+ *
+ * Description: Release resources allocated for a single each adapter.
+ *
+ * Parameters: pshost - Pointer to SCSI command structure.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Pci2000_Release (struct Scsi_Host *pshost)
+ {
+ PADAPTER2000 padapter = HOSTDATA (pshost);
+
+ if ( padapter->irqOwned )
+ free_irq (pshost->irq, padapter);
+ pci_free_consistent (padapter->pdev, consistentLen, padapter->dev[0][0].scatGath, padapter->dev[0][0].scatGathDma);
+ release_region (pshost->io_port, pshost->n_io_port);
+ scsi_unregister(pshost);
+ return 0;
+ }
+
+/****************************************************************
+ * Name: Pci2000_BiosParam
+ *
+ * Description: Process the biosparam request from the SCSI manager to
+ * return C/H/S data.
+ *
+ * Parameters: disk - Pointer to SCSI disk structure.
+ * dev - Major/minor number from kernel.
+ * geom - Pointer to integer array to place geometry data.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Pci2000_BiosParam (struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int geom[])
+ {
+ PADAPTER2000 padapter;
+
+ padapter = HOSTDATA(sdev->host);
+
+ if ( WaitReady (padapter) )
+ return 0;
+ outb_p (sdev->id, padapter->mb0);
+ outb_p (CMD_GET_PARMS, padapter->cmd);
+ if ( WaitReady (padapter) )
+ return 0;
+
+ geom[0] = inb_p (padapter->mb2 + 3);
+ geom[1] = inb_p (padapter->mb2 + 2);
+ geom[2] = inw_p (padapter->mb2);
+ return 0;
+ }
+
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "pci2000",
+ .name = "PCI-2000 SCSI Intelligent Disk Controller",
+ .detect = Pci2000_Detect,
+ .release = Pci2000_Release,
+ .queuecommand = Pci2000_QueueCommand,
+ .abort = Pci2000_Abort,
+ .reset = Pci2000_Reset,
+ .bios_param = Pci2000_BiosParam,
+ .can_queue = 16,
+ .this_id = -1,
+ .sg_tablesize = 16,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h
new file mode 100644
index 000000000000..c65afc964121
--- /dev/null
+++ b/drivers/scsi/pci2000.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver for Linux.
+ *
+ * pci2000.h - Linux Host Driver for PCI-2000 IntelliCache SCSI Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ * http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ * tech@psidisk.com Technical Support
+ *
+ ****************************************************************************/
+#ifndef _PCI2000_H
+#define _PCI2000_H
+
+#include <linux/types.h>
+
+#ifndef PSI_EIDE_SCSIOP
+#define PSI_EIDE_SCSIOP 1
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+#define LINUXVERSION(v,p,s) (((v)<<16) + ((p)<<8) + (s))
+
+/************************************************/
+/* definition of standard data types */
+/************************************************/
+#define CHAR char
+#define UCHAR unsigned char
+#define SHORT short
+#define USHORT unsigned short
+#define BOOL long
+#define LONG long
+#define ULONG unsigned long
+#define VOID void
+
+typedef CHAR *PCHAR;
+typedef UCHAR *PUCHAR;
+typedef SHORT *PSHORT;
+typedef USHORT *PUSHORT;
+typedef BOOL *PBOOL;
+typedef LONG *PLONG;
+typedef ULONG *PULONG;
+typedef VOID *PVOID;
+
+
+/************************************************/
+/* Misc. macros */
+/************************************************/
+#define ANY2SCSI(up, p) \
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up) \
+( (((long)*(((UCHAR *)up))) << 16) \
++ (((long)(((UCHAR *)up)[1])) << 8) \
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p) \
+((UCHAR *)up)[0] = ((long)(p)) >> 24; \
+((UCHAR *)up)[1] = ((long)(p)) >> 16; \
+((UCHAR *)up)[2] = ((long)(p)) >> 8; \
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up) \
+( (((long)(((UCHAR *)up)[0])) << 24) \
++ (((long)(((UCHAR *)up)[1])) << 16) \
++ (((long)(((UCHAR *)up)[2])) << 8) \
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/* SCSI CDB operation codes */
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_MODE_SELECT10 0x55
+#define SCSIOP_MODE_SENSE10 0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6
+#define SCSIOP_MECHANISM_STATUS 0xBD
+#define SCSIOP_READ_CD 0xBE
+
+// SCSI read capacity structure
+typedef struct _READ_CAPACITY_DATA
+ {
+ ULONG blks; /* total blocks (converted to little endian) */
+ ULONG blksiz; /* size of each (converted to little endian) */
+ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+ {
+ UCHAR DeviceType :5;
+ UCHAR DeviceTypeQualifier :3;
+ UCHAR DeviceTypeModifier :7;
+ UCHAR RemovableMedia :1;
+ UCHAR Versions;
+ UCHAR ResponseDataFormat;
+ UCHAR AdditionalLength;
+ UCHAR Reserved[2];
+ UCHAR SoftReset :1;
+ UCHAR CommandQueue :1;
+ UCHAR Reserved2 :1;
+ UCHAR LinkedCommands :1;
+ UCHAR Synchronous :1;
+ UCHAR Wide16Bit :1;
+ UCHAR Wide32Bit :1;
+ UCHAR RelativeAddressing :1;
+ UCHAR VendorId[8];
+ UCHAR ProductId[16];
+ UCHAR ProductRevisionLevel[4];
+ UCHAR VendorSpecific[20];
+ UCHAR Reserved3[40];
+ } INQUIRYDATA, *PINQUIRYDATA;
+
+#endif
+
+// function prototypes
+int Pci2000_Detect (Scsi_Host_Template *tpnt);
+int Pci2000_Command (Scsi_Cmnd *SCpnt);
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2000_Abort (Scsi_Cmnd *SCpnt);
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2000_Release (struct Scsi_Host *pshost);
+int Pci2000_BiosParam (struct scsi_device *sdev,
+ struct block_device *bdev,
+ sector_t capacity, int geom[]);
+
+#endif
diff --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c
new file mode 100644
index 000000000000..e395e4203154
--- /dev/null
+++ b/drivers/scsi/pci2220i.c
@@ -0,0 +1,2915 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2220I device driver for Linux.
+ *
+ * pci2220i.c - Linux Host Driver for PCI-2220I EIDE RAID Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ * http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ * tech@psidisk.com Technical Support
+ *
+ *
+ * Revisions 1.10 Mar-26-1999
+ * - Updated driver for RAID and hot reconstruct support.
+ *
+ * Revisions 1.11 Mar-26-1999
+ * - Fixed spinlock and PCI configuration.
+ *
+ * Revision 2.00 December-1-1999
+ * - Added code for the PCI-2240I controller
+ * - Added code for ATAPI devices.
+ * - Double buffer for scatter/gather support
+ *
+ * Revision 2.10 March-27-2000
+ * - Added support for dynamic DMA
+ *
+ ****************************************************************************/
+
+#error Convert me to understand page+offset based scatterlists
+
+//#define DEBUG 1
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/blkdev.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+
+#define PCI2220I_VERSION "2.10"
+#define READ_CMD IDE_CMD_READ_MULTIPLE
+#define WRITE_CMD IDE_CMD_WRITE_MULTIPLE
+#define MAX_BUS_MASTER_BLOCKS SECTORSXFER // This is the maximum we can bus master
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE() {int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE()
+#endif
+
+#define MAXADAPTER 4 // Increase this and the sizes of the arrays below, if you need more.
+
+
+typedef struct
+ {
+ UCHAR byte6; // device select register image
+ UCHAR spigot; // spigot number
+ UCHAR spigots[2]; // RAID spigots
+ UCHAR deviceID[2]; // device ID codes
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ USHORT spareword; // placeholder
+ ULONG blocks; // number of blocks on device
+ DISK_MIRROR DiskMirror[2]; // RAID status and control
+ ULONG lastsectorlba[2]; // last addressable sector on the drive
+ USHORT raid; // RAID active flag
+ USHORT mirrorRecon;
+ UCHAR reconOn;
+ USHORT reconCount;
+ USHORT reconIsStarting; // indicate hot reconstruct is starting
+ UCHAR cmdDrqInt; // flag for command interrupt
+ UCHAR packet; // command packet size in bytes
+ } OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+ {
+ USHORT bigD; // identity is a PCI-2240I if true, otherwise a PCI-2220I
+ USHORT atapi; // this interface is for ATAPI devices only
+ ULONG regDmaDesc; // address of the DMA discriptor register for direction of transfer
+ ULONG regDmaCmdStat; // Byte #1 of DMA command status register
+ ULONG regDmaAddrPci; // 32 bit register for PCI address of DMA
+ ULONG regDmaAddrLoc; // 32 bit register for local bus address of DMA
+ ULONG regDmaCount; // 32 bit register for DMA transfer count
+ ULONG regDmaMode; // 32 bit register for DMA mode control
+ ULONG regRemap; // 32 bit local space remap
+ ULONG regDesc; // 32 bit local region descriptor
+ ULONG regRange; // 32 bit local range
+ ULONG regIrqControl; // 16 bit Interrupt enable/disable and status
+ ULONG regScratchPad; // scratch pad I/O base address
+ ULONG regBase; // Base I/O register for data space
+ ULONG regData; // data register I/O address
+ ULONG regError; // error register I/O address
+ ULONG regSectCount; // sector count register I/O address
+ ULONG regLba0; // least significant byte of LBA
+ ULONG regLba8; // next least significant byte of LBA
+ ULONG regLba16; // next most significan byte of LBA
+ ULONG regLba24; // head and most 4 significant bits of LBA
+ ULONG regStatCmd; // status on read and command on write register
+ ULONG regStatSel; // board status on read and spigot select on write register
+ ULONG regFail; // fail bits control register
+ ULONG regAltStat; // alternate status and drive control register
+ ULONG basePort; // PLX base I/O port
+ USHORT timingMode; // timing mode currently set for adapter
+ USHORT timingPIO; // TRUE if PIO timing is active
+ struct pci_dev *pcidev;
+ ULONG timingAddress; // address to use on adapter for current timing mode
+ ULONG irqOwned; // owned IRQ or zero if shared
+ UCHAR numberOfDrives; // saved number of drives on this controller
+ UCHAR failRegister; // current inverted data in fail register
+ OUR_DEVICE device[BIGD_MAXDRIVES];
+ DISK_MIRROR *raidData[BIGD_MAXDRIVES];
+ ULONG startSector;
+ USHORT sectorCount;
+ ULONG readCount;
+ UCHAR *currentSgBuffer;
+ ULONG currentSgCount;
+ USHORT nextSg;
+ UCHAR cmd;
+ Scsi_Cmnd *SCpnt;
+ POUR_DEVICE pdev; // current device opearating on
+ USHORT devInReconIndex;
+ USHORT expectingIRQ;
+ USHORT reconOn; // Hot reconstruct is to be done.
+ USHORT reconPhase; // Hot reconstruct operation is in progress.
+ ULONG reconSize;
+ USHORT demoFail; // flag for RAID failure demonstration
+ USHORT survivor;
+ USHORT failinprog;
+ struct timer_list reconTimer;
+ struct timer_list timer;
+ UCHAR *kBuffer;
+ dma_addr_t kBufferDma;
+ UCHAR reqSense;
+ UCHAR atapiCdb[16];
+ UCHAR atapiSpecial;
+ } ADAPTER2220I, *PADAPTER2220I;
+
+#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata)
+
+#define RECON_PHASE_READY 0x01
+#define RECON_PHASE_COPY 0x02
+#define RECON_PHASE_UPDATE 0x03
+#define RECON_PHASE_LAST 0x04
+#define RECON_PHASE_END 0x07
+#define RECON_PHASE_MARKING 0x80
+#define RECON_PHASE_FAILOVER 0xFF
+
+static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter
+static int NumAdapters = 0;
+static int Installed = 0;
+static SETUP DaleSetup;
+static DISK_MIRROR DiskMirror[BIGD_MAXDRIVES];
+static ULONG ModeArray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE5};
+static ULONG ModeArray2[] = {BIGD_DATA_MODE2, BIGD_DATA_MODE3, BIGD_DATA_MODE4, BIGD_DATA_MODE5};
+
+static void ReconTimerExpiry (unsigned long data);
+
+/*******************************************************************************************************
+ * Name: Alarm
+ *
+ * Description: Sound the for the given device
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * device - Device number.
+ *
+ * Returns: Nothing.
+ *
+ ******************************************************************************************************/
+static void Alarm (PADAPTER2220I padapter, UCHAR device)
+ {
+ UCHAR zc;
+
+ if ( padapter->bigD )
+ {
+ zc = device | (FAIL_ANY | FAIL_AUDIBLE);
+ if ( padapter->failRegister & FAIL_ANY )
+ zc |= FAIL_MULTIPLE;
+
+ padapter->failRegister = zc;
+ outb_p (~zc, padapter->regFail);
+ }
+ else
+ outb_p (0x3C | (1 << device), padapter->regFail); // sound alarm and set fail light
+ }
+/****************************************************************
+ * Name: MuteAlarm :LOCAL
+ *
+ * Description: Mute the audible alarm.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static void MuteAlarm (PADAPTER2220I padapter)
+ {
+ UCHAR old;
+
+ if ( padapter->bigD )
+ {
+ padapter->failRegister &= ~FAIL_AUDIBLE;
+ outb_p (~padapter->failRegister, padapter->regFail);
+ }
+ else
+ {
+ old = (inb_p (padapter->regStatSel) >> 3) | (inb_p (padapter->regStatSel) & 0x83);
+ outb_p (old | 0x40, padapter->regFail);
+ }
+ }
+/****************************************************************
+ * Name: WaitReady :LOCAL
+ *
+ * Description: Wait for device ready.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2220I padapter)
+ {
+ ULONG z;
+ UCHAR status;
+
+ for ( z = 0; z < (TIMEOUT_READY * 4); z++ )
+ {
+ status = inb_p (padapter->regStatCmd);
+ if ( (status & (IDE_STATUS_DRDY | IDE_STATUS_BUSY)) == IDE_STATUS_DRDY )
+ return 0;
+ udelay (250);
+ }
+ return status;
+ }
+/****************************************************************
+ * Name: WaitReadyReset :LOCAL
+ *
+ * Description: Wait for device ready.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WaitReadyReset (PADAPTER2220I padapter)
+ {
+ ULONG z;
+ UCHAR status;
+
+ for ( z = 0; z < (125 * 16); z++ ) // wait up to 1/4 second
+ {
+ status = inb_p (padapter->regStatCmd);
+ if ( (status & (IDE_STATUS_DRDY | IDE_STATUS_BUSY)) == IDE_STATUS_DRDY )
+ {
+ DEB (printk ("\nPCI2220I: Reset took %ld mSec to be ready", z / 8));
+ return 0;
+ }
+ udelay (125);
+ }
+ DEB (printk ("\nPCI2220I: Reset took more than 2 Seconds to come ready, Disk Failure"));
+ return status;
+ }
+/****************************************************************
+ * Name: WaitDrq :LOCAL
+ *
+ * Description: Wait for device ready for data transfer.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WaitDrq (PADAPTER2220I padapter)
+ {
+ ULONG z;
+ UCHAR status;
+
+ for ( z = 0; z < (TIMEOUT_DRQ * 4); z++ )
+ {
+ status = inb_p (padapter->regStatCmd);
+ if ( status & IDE_STATUS_DRQ )
+ return 0;
+ udelay (250);
+ }
+ return status;
+ }
+/****************************************************************
+ * Name: AtapiWaitReady :LOCAL
+ *
+ * Description: Wait for device busy and DRQ to be cleared.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * msec - Number of milliseconds to wait.
+ *
+ * Returns: TRUE if drive does not clear busy in time.
+ *
+ ****************************************************************/
+static int AtapiWaitReady (PADAPTER2220I padapter, int msec)
+ {
+ int z;
+
+ for ( z = 0; z < (msec * 16); z++ )
+ {
+ if ( !(inb_p (padapter->regStatCmd) & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) )
+ return FALSE;
+ udelay (125);
+ }
+ return TRUE;
+ }
+/****************************************************************
+ * Name: AtapiWaitDrq :LOCAL
+ *
+ * Description: Wait for device ready for data transfer.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * msec - Number of milliseconds to wait.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int AtapiWaitDrq (PADAPTER2220I padapter, int msec)
+ {
+ ULONG z;
+
+ for ( z = 0; z < (msec * 16); z++ )
+ {
+ if ( inb_p (padapter->regStatCmd) & IDE_STATUS_DRQ )
+ return 0;
+ udelay (128);
+ }
+ return TRUE;
+ }
+/****************************************************************
+ * Name: HardReset :LOCAL
+ *
+ * Description: Wait for device ready for data transfer.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device.
+ * spigot - Spigot number.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int HardReset (PADAPTER2220I padapter, POUR_DEVICE pdev, UCHAR spigot)
+ {
+ DEB (printk ("\npci2220i:RESET spigot = %X devices = %d, %d", spigot, pdev->deviceID[0], pdev->deviceID[1]));
+ mdelay (100); // just wait 100 mSec to let drives flush
+ SelectSpigot (padapter, spigot | SEL_IRQ_OFF);
+
+ outb_p (0x0E, padapter->regAltStat); // reset the suvivor
+ udelay (100); // wait a little
+ outb_p (0x08, padapter->regAltStat); // clear the reset
+ udelay (100);
+
+ outb_p (0xA0, padapter->regLba24); // select the master drive
+ if ( WaitReadyReset (padapter) )
+ {
+ DEB (printk ("\npci2220i: master not ready after reset"));
+ return TRUE;
+ }
+ outb_p (0xB0, padapter->regLba24); // try the slave drive
+ if ( (inb_p (padapter->regStatCmd) & (IDE_STATUS_DRDY | IDE_STATUS_BUSY)) == IDE_STATUS_DRDY )
+ {
+ DEB (printk ("\nPCI2220I: initializing slave drive on spigot %X", spigot));
+ outb_p (SECTORSXFER, padapter->regSectCount);
+ WriteCommand (padapter, IDE_CMD_SET_MULTIPLE);
+ if ( WaitReady (padapter) )
+ {
+ DEB (printk ("\npci2220i: slave not ready after set multiple"));
+ return TRUE;
+ }
+ }
+
+ outb_p (0xA0, padapter->regLba24); // select the drive
+ outb_p (SECTORSXFER, padapter->regSectCount);
+ WriteCommand (padapter, IDE_CMD_SET_MULTIPLE);
+ if ( WaitReady (padapter) )
+ {
+ DEB (printk ("\npci2220i: master not ready after set multiple"));
+ return TRUE;
+ }
+ return FALSE;
+ }
+/****************************************************************
+ * Name: AtapiReset :LOCAL
+ *
+ * Description: Wait for device ready for data transfer.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device.
+ *
+ * Returns: TRUE if drive does not come ready.
+ *
+ ****************************************************************/
+static int AtapiReset (PADAPTER2220I padapter, POUR_DEVICE pdev)
+ {
+ SelectSpigot (padapter, pdev->spigot);
+ AtapiDevice (padapter, pdev->byte6);
+ AtapiCountLo (padapter, 0);
+ AtapiCountHi (padapter, 0);
+ WriteCommand (padapter, IDE_COMMAND_ATAPI_RESET);
+ udelay (125);
+ if ( AtapiWaitReady (padapter, 1000) )
+ return TRUE;
+ if ( inb_p (padapter->regStatCmd) || (inb_p (padapter->regLba8) != 0x14) || (inb_p (padapter->regLba16) != 0xEB) )
+ return TRUE;
+ return FALSE;
+ }
+/****************************************************************
+ * Name: WalkScatGath :LOCAL
+ *
+ * Description: Transfer data to/from scatter/gather buffers.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * datain - TRUE if data read.
+ * length - Number of bytes to transfer.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void WalkScatGath (PADAPTER2220I padapter, UCHAR datain, ULONG length)
+ {
+ ULONG count;
+ UCHAR *buffer = padapter->kBuffer;
+
+ while ( length )
+ {
+ count = ( length > padapter->currentSgCount ) ? padapter->currentSgCount : length;
+
+ if ( datain )
+ memcpy (padapter->currentSgBuffer, buffer, count);
+ else
+ memcpy (buffer, padapter->currentSgBuffer, count);
+
+ padapter->currentSgCount -= count;
+ if ( !padapter->currentSgCount )
+ {
+ if ( padapter->nextSg < padapter->SCpnt->use_sg )
+ {
+ padapter->currentSgBuffer = ((struct scatterlist *)padapter->SCpnt->request_buffer)[padapter->nextSg].address;
+ padapter->currentSgCount = ((struct scatterlist *)padapter->SCpnt->request_buffer)[padapter->nextSg].length;
+ padapter->nextSg++;
+ }
+ }
+ else
+ padapter->currentSgBuffer += count;
+
+ length -= count;
+ buffer += count;
+ }
+ }
+/****************************************************************
+ * Name: BusMaster :LOCAL
+ *
+ * Description: Do a bus master I/O.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * datain - TRUE if data read.
+ * irq - TRUE if bus master interrupt expected.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void BusMaster (PADAPTER2220I padapter, UCHAR datain, UCHAR irq)
+ {
+ ULONG zl;
+
+ zl = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+ padapter->sectorCount -= zl;
+ zl *= (ULONG)BYTES_PER_SECTOR;
+
+ if ( datain )
+ {
+ padapter->readCount = zl;
+ outb_p (8, padapter->regDmaDesc); // read operation
+ if ( padapter->bigD )
+ {
+ if ( irq && !padapter->sectorCount )
+ outb_p (0x0C, padapter->regDmaMode); // interrupt on
+ else
+ outb_p (0x08, padapter->regDmaMode); // no interrupt
+ }
+ else
+ {
+ if ( irq && !padapter->sectorCount )
+ outb_p (0x05, padapter->regDmaMode); // interrupt on
+ else
+ outb_p (0x01, padapter->regDmaMode); // no interrupt
+ }
+ }
+ else
+ {
+ outb_p (0x00, padapter->regDmaDesc); // write operation
+ if ( padapter->bigD )
+ outb_p (0x08, padapter->regDmaMode); // no interrupt
+ else
+ outb_p (0x01, padapter->regDmaMode); // no interrupt
+ WalkScatGath (padapter, FALSE, zl);
+ }
+
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (padapter->kBufferDma, padapter->regDmaAddrPci);
+ outl (zl, padapter->regDmaCount);
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ }
+/****************************************************************
+ * Name: AtapiBusMaster :LOCAL
+ *
+ * Description: Do a bus master I/O.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * datain - TRUE if data read.
+ * length - Number of bytes to transfer.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void AtapiBusMaster (PADAPTER2220I padapter, UCHAR datain, ULONG length)
+ {
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (padapter->kBufferDma, padapter->regDmaAddrPci);
+ outl (length, padapter->regDmaCount);
+ if ( datain )
+ {
+ if ( padapter->readCount )
+ WalkScatGath (padapter, TRUE, padapter->readCount);
+ outb_p (0x08, padapter->regDmaDesc); // read operation
+ outb_p (0x08, padapter->regDmaMode); // no interrupt
+ padapter->readCount = length;
+ }
+ else
+ {
+ outb_p (0x00, padapter->regDmaDesc); // write operation
+ outb_p (0x08, padapter->regDmaMode); // no interrupt
+ if ( !padapter->atapiSpecial )
+ WalkScatGath (padapter, FALSE, length);
+ }
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ }
+/****************************************************************
+ * Name: WriteData :LOCAL
+ *
+ * Description: Write data to device.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER2220I padapter)
+ {
+ ULONG zl;
+
+ if ( !WaitDrq (padapter) )
+ {
+ if ( padapter->timingPIO )
+ {
+ zl = (padapter->sectorCount > MAX_BUS_MASTER_BLOCKS) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+ WalkScatGath (padapter, FALSE, zl * BYTES_PER_SECTOR);
+ outsw (padapter->regData, padapter->kBuffer, zl * (BYTES_PER_SECTOR / 2));
+ padapter->sectorCount -= zl;
+ }
+ else
+ BusMaster (padapter, 0, 0);
+ return 0;
+ }
+ padapter->cmd = 0; // null out the command byte
+ return 1;
+ }
+/****************************************************************
+ * Name: WriteDataBoth :LOCAL
+ *
+ * Description: Write data to device.
+ *
+ * Parameters: padapter - Pointer to adapter structure.
+ * pdev - Pointer to device structure
+ *
+ * Returns: Index + 1 of drive not failed or zero for OK.
+ *
+ ****************************************************************/
+static int WriteDataBoth (PADAPTER2220I padapter, POUR_DEVICE pdev)
+ {
+ ULONG zl;
+ UCHAR status0, status1;
+
+ SelectSpigot (padapter, pdev->spigots[0]);
+ status0 = WaitDrq (padapter);
+ if ( !status0 )
+ {
+ SelectSpigot (padapter, pdev->spigots[1]);
+ status1 = WaitDrq (padapter);
+ if ( !status1 )
+ {
+ SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1] | padapter->bigD);
+ if ( padapter->timingPIO )
+ {
+ zl = (padapter->sectorCount > MAX_BUS_MASTER_BLOCKS) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+ WalkScatGath (padapter, FALSE, zl * BYTES_PER_SECTOR);
+ outsw (padapter->regData, padapter->kBuffer, zl * (BYTES_PER_SECTOR / 2));
+ padapter->sectorCount -= zl;
+ }
+ else
+ BusMaster (padapter, 0, 0);
+ return 0;
+ }
+ }
+ padapter->cmd = 0; // null out the command byte
+ if ( status0 )
+ return 2;
+ return 1;
+ }
+/****************************************************************
+ * Name: IdeCmd :LOCAL
+ *
+ * Description: Process an IDE command.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device.
+ *
+ * Returns: Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER2220I padapter, POUR_DEVICE pdev)
+ {
+ UCHAR status;
+
+ SelectSpigot (padapter, pdev->spigot | padapter->bigD); // select the spigot
+ outb_p (pdev->byte6 | ((UCHAR *)(&padapter->startSector))[3], padapter->regLba24); // select the drive
+ status = WaitReady (padapter);
+ if ( !status )
+ {
+ outb_p (padapter->sectorCount, padapter->regSectCount);
+ outb_p (((UCHAR *)(&padapter->startSector))[0], padapter->regLba0);
+ outb_p (((UCHAR *)(&padapter->startSector))[1], padapter->regLba8);
+ outb_p (((UCHAR *)(&padapter->startSector))[2], padapter->regLba16);
+ padapter->expectingIRQ = TRUE;
+ WriteCommand (padapter, padapter->cmd);
+ return 0;
+ }
+
+ padapter->cmd = 0; // null out the command byte
+ return status;
+ }
+/****************************************************************
+ * Name: IdeCmdBoth :LOCAL
+ *
+ * Description: Process an IDE command to both drivers.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device structure
+ *
+ * Returns: Index + 1 of drive not failed or zero for OK.
+ *
+ ****************************************************************/
+static UCHAR IdeCmdBoth (PADAPTER2220I padapter, POUR_DEVICE pdev)
+ {
+ UCHAR status0;
+ UCHAR status1;
+
+ SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1]); // select the spigots
+ outb_p (padapter->pdev->byte6 | ((UCHAR *)(&padapter->startSector))[3], padapter->regLba24);// select the drive
+ SelectSpigot (padapter, pdev->spigots[0]);
+ status0 = WaitReady (padapter);
+ if ( !status0 )
+ {
+ SelectSpigot (padapter, pdev->spigots[1]);
+ status1 = WaitReady (padapter);
+ if ( !status1 )
+ {
+ SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1] | padapter->bigD);
+ outb_p (padapter->sectorCount, padapter->regSectCount);
+ outb_p (((UCHAR *)(&padapter->startSector))[0], padapter->regLba0);
+ outb_p (((UCHAR *)(&padapter->startSector))[1], padapter->regLba8);
+ outb_p (((UCHAR *)(&padapter->startSector))[2], padapter->regLba16);
+ padapter->expectingIRQ = TRUE;
+ WriteCommand (padapter, padapter->cmd);
+ return 0;
+ }
+ }
+ padapter->cmd = 0; // null out the command byte
+ if ( status0 )
+ return 2;
+ return 1;
+ }
+/****************************************************************
+ * Name: OpDone :LOCAL
+ *
+ * Description: Complete an operatoin done sequence.
+ *
+ * Parameters: padapter - Pointer to host data block.
+ * spigot - Spigot select code.
+ * device - Device byte code.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void OpDone (PADAPTER2220I padapter, ULONG result)
+ {
+ Scsi_Cmnd *SCpnt = padapter->SCpnt;
+
+ if ( padapter->reconPhase )
+ {
+ padapter->reconPhase = 0;
+ if ( padapter->SCpnt )
+ {
+ Pci2220i_QueueCommand (SCpnt, SCpnt->scsi_done);
+ }
+ else
+ {
+ if ( padapter->reconOn )
+ {
+ ReconTimerExpiry ((unsigned long)padapter);
+ }
+ }
+ }
+ else
+ {
+ padapter->cmd = 0;
+ padapter->SCpnt = NULL;
+ padapter->pdev = NULL;
+ SCpnt->result = result;
+ SCpnt->scsi_done (SCpnt);
+ if ( padapter->reconOn && !padapter->reconTimer.data )
+ {
+ padapter->reconTimer.expires = jiffies + (HZ / 4); // start in 1/4 second
+ padapter->reconTimer.data = (unsigned long)padapter;
+ add_timer (&padapter->reconTimer);
+ }
+ }
+ }
+/****************************************************************
+ * Name: InlineIdentify :LOCAL
+ *
+ * Description: Do an intline inquiry on a drive.
+ *
+ * Parameters: padapter - Pointer to host data block.
+ * spigot - Spigot select code.
+ * device - Device byte code.
+ *
+ * Returns: Last addressable sector or zero if none.
+ *
+ ****************************************************************/
+static ULONG InlineIdentify (PADAPTER2220I padapter, UCHAR spigot, UCHAR device)
+ {
+ PIDENTIFY_DATA pid = (PIDENTIFY_DATA)padapter->kBuffer;
+
+ SelectSpigot (padapter, spigot | SEL_IRQ_OFF); // select the spigot
+ outb_p ((device << 4) | 0xA0, padapter->regLba24); // select the drive
+ if ( WaitReady (padapter) )
+ return 0;
+ WriteCommand (padapter, IDE_COMMAND_IDENTIFY);
+ if ( WaitDrq (padapter) )
+ return 0;
+ insw (padapter->regData, padapter->kBuffer, sizeof (IDENTIFY_DATA) >> 1);
+ return (pid->LBATotalSectors - 1);
+ }
+/****************************************************************
+ * Name: AtapiIdentify :LOCAL
+ *
+ * Description: Do an intline inquiry on a drive.
+ *
+ * Parameters: padapter - Pointer to host data block.
+ * pdev - Pointer to device table.
+ *
+ * Returns: TRUE on error.
+ *
+ ****************************************************************/
+static ULONG AtapiIdentify (PADAPTER2220I padapter, POUR_DEVICE pdev)
+ {
+ ATAPI_GENERAL_0 ag0;
+ USHORT zs;
+ int z;
+
+ AtapiDevice (padapter, pdev->byte6);
+ WriteCommand (padapter, IDE_COMMAND_ATAPI_IDENTIFY);
+ if ( AtapiWaitDrq (padapter, 3000) )
+ return TRUE;
+
+ *(USHORT *)&ag0 = inw_p (padapter->regData);
+ for ( z = 0; z < 255; z++ )
+ zs = inw_p (padapter->regData);
+
+ if ( ag0.ProtocolType == 2 )
+ {
+ if ( ag0.CmdDrqType == 1 )
+ pdev->cmdDrqInt = TRUE;
+ switch ( ag0.CmdPacketSize )
+ {
+ case 0:
+ pdev->packet = 6;
+ break;
+ case 1:
+ pdev->packet = 8;
+ break;
+ default:
+ pdev->packet = 6;
+ break;
+ }
+ return FALSE;
+ }
+ return TRUE;
+ }
+/****************************************************************
+ * Name: Atapi2Scsi
+ *
+ * Description: Convert ATAPI data to SCSI data.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+void Atapi2Scsi (PADAPTER2220I padapter, Scsi_Cmnd *SCpnt)
+ {
+ UCHAR *buff = padapter->currentSgBuffer;
+
+ switch ( SCpnt->cmnd[0] )
+ {
+ case SCSIOP_MODE_SENSE:
+ buff[0] = padapter->kBuffer[1];
+ buff[1] = padapter->kBuffer[2];
+ buff[2] = padapter->kBuffer[3];
+ buff[3] = padapter->kBuffer[7];
+ memcpy (&buff[4], &padapter->kBuffer[8], padapter->atapiCdb[8] - 8);
+ break;
+ case SCSIOP_INQUIRY:
+ padapter->kBuffer[2] = 2;
+ memcpy (buff, padapter->kBuffer, padapter->currentSgCount);
+ break;
+ default:
+ if ( padapter->readCount )
+ WalkScatGath (padapter, TRUE, padapter->readCount);
+ break;
+ }
+ }
+/****************************************************************
+ * Name: Scsi2Atapi
+ *
+ * Description: Convert SCSI packet command to Atapi packet command.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void Scsi2Atapi (PADAPTER2220I padapter, Scsi_Cmnd *SCpnt)
+ {
+ UCHAR *cdb = SCpnt->cmnd;
+ UCHAR *buff = padapter->currentSgBuffer;
+
+ switch (cdb[0])
+ {
+ case SCSIOP_READ6:
+ padapter->atapiCdb[0] = SCSIOP_READ;
+ padapter->atapiCdb[1] = cdb[1] & 0xE0;
+ padapter->atapiCdb[3] = cdb[1] & 0x1F;
+ padapter->atapiCdb[4] = cdb[2];
+ padapter->atapiCdb[5] = cdb[3];
+ padapter->atapiCdb[8] = cdb[4];
+ padapter->atapiCdb[9] = cdb[5];
+ break;
+ case SCSIOP_WRITE6:
+ padapter->atapiCdb[0] = SCSIOP_WRITE;
+ padapter->atapiCdb[1] = cdb[1] & 0xE0;
+ padapter->atapiCdb[3] = cdb[1] & 0x1F;
+ padapter->atapiCdb[4] = cdb[2];
+ padapter->atapiCdb[5] = cdb[3];
+ padapter->atapiCdb[8] = cdb[4];
+ padapter->atapiCdb[9] = cdb[5];
+ break;
+ case SCSIOP_MODE_SENSE:
+ padapter->atapiCdb[0] = SCSIOP_MODE_SENSE10;
+ padapter->atapiCdb[2] = cdb[2];
+ padapter->atapiCdb[8] = cdb[4] + 4;
+ break;
+
+ case SCSIOP_MODE_SELECT:
+ padapter->atapiSpecial = TRUE;
+ padapter->atapiCdb[0] = SCSIOP_MODE_SELECT10;
+ padapter->atapiCdb[1] = cdb[1] | 0x10;
+ memcpy (padapter->kBuffer, buff, 4);
+ padapter->kBuffer[4] = padapter->kBuffer[5] = 0;
+ padapter->kBuffer[6] = padapter->kBuffer[7] = 0;
+ memcpy (&padapter->kBuffer[8], &buff[4], cdb[4] - 4);
+ padapter->atapiCdb[8] = cdb[4] + 4;
+ break;
+ }
+ }
+/****************************************************************
+ * Name: AtapiSendCdb
+ *
+ * Description: Send the CDB packet to the device.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device.
+ * cdb - Pointer to 16 byte SCSI cdb.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void AtapiSendCdb (PADAPTER2220I padapter, POUR_DEVICE pdev, CHAR *cdb)
+ {
+ DEB (printk ("\nPCI2242I: CDB: %X %X %X %X %X %X %X %X %X %X %X %X", cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]));
+ outsw (padapter->regData, cdb, pdev->packet);
+ }
+/****************************************************************
+ * Name: AtapiRequestSense
+ *
+ * Description: Send the CDB packet to the device.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device.
+ * SCpnt - Pointer to SCSI command structure.
+ * pass - If true then this is the second pass to send cdb.
+ *
+ * Returns: TRUE on error.
+ *
+ ****************************************************************/
+static int AtapiRequestSense (PADAPTER2220I padapter, POUR_DEVICE pdev, Scsi_Cmnd *SCpnt, UCHAR pass)
+ {
+ UCHAR cdb[16] = {SCSIOP_REQUEST_SENSE,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0};
+
+ DEB (printk ("\nPCI2242I: AUTO REQUEST SENSE"));
+ cdb[4] = (UCHAR)(sizeof (SCpnt->sense_buffer));
+ if ( !pass )
+ {
+ padapter->reqSense = TRUE;
+ AtapiCountLo (padapter, cdb[4]);
+ AtapiCountHi (padapter, 0);
+ outb_p (0, padapter->regError);
+ WriteCommand (padapter, IDE_COMMAND_ATAPI_PACKET);
+ if ( pdev->cmdDrqInt )
+ return FALSE;
+
+ if ( AtapiWaitDrq (padapter, 500) )
+ return TRUE;
+ }
+ AtapiSendCdb (padapter, pdev, cdb);
+ return FALSE;
+ }
+/****************************************************************
+ * Name: InlineReadSignature :LOCAL
+ *
+ * Description: Do an inline read RAID sigature.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to device.
+ * index - index of data to read.
+ *
+ * Returns: Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR InlineReadSignature (PADAPTER2220I padapter, POUR_DEVICE pdev, int index)
+ {
+ UCHAR status;
+ ULONG zl = pdev->lastsectorlba[index];
+
+ SelectSpigot (padapter, pdev->spigots[index] | SEL_IRQ_OFF); // select the spigot without interrupts
+ outb_p (pdev->byte6 | ((UCHAR *)&zl)[3], padapter->regLba24);
+ status = WaitReady (padapter);
+ if ( !status )
+ {
+ outb_p (((UCHAR *)&zl)[2], padapter->regLba16);
+ outb_p (((UCHAR *)&zl)[1], padapter->regLba8);
+ outb_p (((UCHAR *)&zl)[0], padapter->regLba0);
+ outb_p (1, padapter->regSectCount);
+ WriteCommand (padapter, IDE_COMMAND_READ);
+ status = WaitDrq (padapter);
+ if ( !status )
+ {
+ insw (padapter->regData, padapter->kBuffer, BYTES_PER_SECTOR / 2);
+ ((ULONG *)(&pdev->DiskMirror[index]))[0] = ((ULONG *)(&padapter->kBuffer[DISK_MIRROR_POSITION]))[0];
+ ((ULONG *)(&pdev->DiskMirror[index]))[1] = ((ULONG *)(&padapter->kBuffer[DISK_MIRROR_POSITION]))[1];
+ // some drives assert DRQ before IRQ so let's make sure we clear the IRQ
+ WaitReady (padapter);
+ return 0;
+ }
+ }
+ return status;
+ }
+/****************************************************************
+ * Name: DecodeError :LOCAL
+ *
+ * Description: Decode and process device errors.
+ *
+ * Parameters: padapter - Pointer to adapter data.
+ * status - Status register code.
+ *
+ * Returns: The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (PADAPTER2220I padapter, UCHAR status)
+ {
+ UCHAR error;
+
+ padapter->expectingIRQ = 0;
+ if ( status & IDE_STATUS_WRITE_FAULT )
+ {
+ return DID_PARITY << 16;
+ }
+ if ( status & IDE_STATUS_BUSY )
+ return DID_BUS_BUSY << 16;
+
+ error = inb_p (padapter->regError);
+ DEB(printk ("\npci2220i error register: %x", error));
+ switch ( error )
+ {
+ case IDE_ERROR_AMNF:
+ case IDE_ERROR_TKONF:
+ case IDE_ERROR_ABRT:
+ case IDE_ERROR_IDFN:
+ case IDE_ERROR_UNC:
+ case IDE_ERROR_BBK:
+ default:
+ return DID_ERROR << 16;
+ }
+ return DID_ERROR << 16;
+ }
+/****************************************************************
+ * Name: StartTimer :LOCAL
+ *
+ * Description: Start the timer.
+ *
+ * Parameters: ipadapter - Pointer adapter data structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void StartTimer (PADAPTER2220I padapter)
+ {
+ padapter->timer.expires = jiffies + TIMEOUT_DATA;
+ add_timer (&padapter->timer);
+ }
+/****************************************************************
+ * Name: WriteSignature :LOCAL
+ *
+ * Description: Start the timer.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * pdev - Pointer to our device.
+ * spigot - Selected spigot.
+ * index - index of mirror signature on device.
+ *
+ * Returns: TRUE on any error.
+ *
+ ****************************************************************/
+static int WriteSignature (PADAPTER2220I padapter, POUR_DEVICE pdev, UCHAR spigot, int index)
+ {
+ ULONG zl;
+
+ SelectSpigot (padapter, spigot);
+ zl = pdev->lastsectorlba[index];
+ outb_p (pdev->byte6 | ((UCHAR *)&zl)[3], padapter->regLba24);
+ outb_p (((UCHAR *)&zl)[2], padapter->regLba16);
+ outb_p (((UCHAR *)&zl)[1], padapter->regLba8);
+ outb_p (((UCHAR *)&zl)[0], padapter->regLba0);
+ outb_p (1, padapter->regSectCount);
+
+ WriteCommand (padapter, IDE_COMMAND_WRITE);
+ if ( WaitDrq (padapter) )
+ return TRUE;
+ StartTimer (padapter);
+ padapter->expectingIRQ = TRUE;
+
+ ((ULONG *)(&padapter->kBuffer[DISK_MIRROR_POSITION]))[0] = ((ULONG *)(&pdev->DiskMirror[index]))[0];
+ ((ULONG *)(&padapter->kBuffer[DISK_MIRROR_POSITION]))[1] = ((ULONG *)(&pdev->DiskMirror[index]))[1];
+ outsw (padapter->regData, padapter->kBuffer, BYTES_PER_SECTOR / 2);
+ return FALSE;
+ }
+/*******************************************************************************************************
+ * Name: InitFailover
+ *
+ * Description: This is the beginning of the failover routine
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * padapter - Pointer adapter data structure.
+ * pdev - Pointer to our device.
+ *
+ * Returns: TRUE on error.
+ *
+ ******************************************************************************************************/
+static int InitFailover (PADAPTER2220I padapter, POUR_DEVICE pdev)
+ {
+ UCHAR spigot;
+
+ DEB (printk ("\npci2220i: Initialize failover process - survivor = %d", pdev->deviceID[padapter->survivor]));
+ pdev->raid = FALSE; //initializes system for non raid mode
+ pdev->reconOn = FALSE;
+ spigot = pdev->spigots[padapter->survivor];
+
+ if ( pdev->DiskMirror[padapter->survivor].status & UCBF_REBUILD )
+ {
+ DEB (printk ("\n failed, is survivor"));
+ return (TRUE);
+ }
+
+ if ( HardReset (padapter, pdev, spigot) )
+ {
+ DEB (printk ("\n failed, reset"));
+ return TRUE;
+ }
+
+ Alarm (padapter, pdev->deviceID[padapter->survivor ^ 1]);
+ pdev->DiskMirror[padapter->survivor].status = UCBF_MIRRORED | UCBF_SURVIVOR; //clear present status
+
+ if ( WriteSignature (padapter, pdev, spigot, padapter->survivor) )
+ {
+ DEB (printk ("\n failed, write signature"));
+ return TRUE;
+ }
+ padapter->failinprog = TRUE;
+ return FALSE;
+ }
+/****************************************************************
+ * Name: TimerExpiry :LOCAL
+ *
+ * Description: Timer expiry routine.
+ *
+ * Parameters: data - Pointer adapter data structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void TimerExpiry (unsigned long data)
+ {
+ PADAPTER2220I padapter = (PADAPTER2220I)data;
+ struct Scsi_Host *host = padapter->SCpnt->device->host;
+ POUR_DEVICE pdev = padapter->pdev;
+ UCHAR status = IDE_STATUS_BUSY;
+ UCHAR temp, temp1;
+ unsigned long flags;
+
+ /*
+ * Disable interrupts, if they aren't already disabled and acquire
+ * the I/O spinlock.
+ */
+ spin_lock_irqsave (host->host_lock, flags);
+ DEB (printk ("\nPCI2220I: Timeout expired "));
+
+ if ( padapter->failinprog )
+ {
+ DEB (printk ("in failover process"));
+ OpDone (padapter, DecodeError (padapter, inb_p (padapter->regStatCmd)));
+ goto timerExpiryDone;
+ }
+
+ while ( padapter->reconPhase )
+ {
+ DEB (printk ("in recon phase %X", padapter->reconPhase));
+ switch ( padapter->reconPhase )
+ {
+ case RECON_PHASE_MARKING:
+ case RECON_PHASE_LAST:
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 1 : 0;
+ DEB (printk ("\npci2220i: FAILURE 1"));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DID_ERROR << 16);
+ goto timerExpiryDone;
+
+ case RECON_PHASE_READY:
+ OpDone (padapter, DID_ERROR << 16);
+ goto timerExpiryDone;
+
+ case RECON_PHASE_COPY:
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 2"));
+ DEB (printk ("\n spig/stat = %X", inb_p (padapter->regStatSel));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DID_ERROR << 16);
+ goto timerExpiryDone;
+
+ case RECON_PHASE_UPDATE:
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 3")));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DID_ERROR << 16);
+ goto timerExpiryDone;
+
+ case RECON_PHASE_END:
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 4"));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DID_ERROR << 16);
+ goto timerExpiryDone;
+
+ default:
+ goto timerExpiryDone;
+ }
+ }
+
+ while ( padapter->cmd )
+ {
+ outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine
+ if ( pdev->raid )
+ {
+ if ( padapter->cmd == WRITE_CMD )
+ {
+ DEB (printk ("in RAID write operation"));
+ temp = ( pdev->spigot & (SEL_1 | SEL_2) ) ? SEL_1 : SEL_3;
+ if ( inb_p (padapter->regStatSel) & temp )
+ {
+ DEB (printk ("\npci2220i: Determined A OK"));
+ SelectSpigot (padapter, temp | SEL_IRQ_OFF); // Masking the interrupt during spigot select
+ temp = inb_p (padapter->regStatCmd);
+ }
+ else
+ temp = IDE_STATUS_BUSY;
+
+ temp1 = ( pdev->spigot & (SEL_1 | SEL_2) ) ? SEL_2 : SEL_4;
+ if ( inb (padapter->regStatSel) & temp1 )
+ {
+ DEB (printk ("\npci2220i: Determined B OK"));
+ SelectSpigot (padapter, temp1 | SEL_IRQ_OFF); // Masking the interrupt during spigot select
+ temp1 = inb_p (padapter->regStatCmd);
+ }
+ else
+ temp1 = IDE_STATUS_BUSY;
+
+ if ( (temp & IDE_STATUS_BUSY) || (temp1 & IDE_STATUS_BUSY) )
+ {
+ DEB (printk ("\npci2220i: Status A: %X B: %X", temp & 0xFF, temp1 & 0xFF));
+ if ( (temp & IDE_STATUS_BUSY) && (temp1 & IDE_STATUS_BUSY) )
+ {
+ status = temp;
+ break;
+ }
+ else
+ {
+ if ( temp & IDE_STATUS_BUSY )
+ padapter->survivor = 1;
+ else
+ padapter->survivor = 0;
+ if ( InitFailover (padapter, pdev) )
+ {
+ status = inb_p (padapter->regStatCmd);
+ break;
+ }
+ goto timerExpiryDone;
+ }
+ }
+ }
+ else
+ {
+ DEB (printk ("in RAID read operation"));
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 6"));
+ if ( InitFailover (padapter, pdev) )
+ {
+ status = inb_p (padapter->regStatCmd);
+ break;
+ }
+ goto timerExpiryDone;
+ }
+ }
+ else
+ {
+ DEB (printk ("in I/O operation"));
+ status = inb_p (padapter->regStatCmd);
+ }
+ break;
+ }
+
+ OpDone (padapter, DecodeError (padapter, status));
+
+timerExpiryDone:;
+ /*
+ * Release the I/O spinlock and restore the original flags
+ * which will enable interrupts if and only if they were
+ * enabled on entry.
+ */
+ spin_unlock_irqrestore (host->host_lock, flags);
+ }
+/****************************************************************
+ * Name: SetReconstruct :LOCAL
+ *
+ * Description: Set the reconstruct up.
+ *
+ * Parameters: pdev - Pointer to device structure.
+ * index - Mirror index number.
+ *
+ * Returns: Number of sectors on new disk required.
+ *
+ ****************************************************************/
+static LONG SetReconstruct (POUR_DEVICE pdev, int index)
+ {
+ pdev->DiskMirror[index].status = UCBF_MIRRORED; // setup the flags
+ pdev->DiskMirror[index ^ 1].status = UCBF_MIRRORED | UCBF_REBUILD;
+ pdev->DiskMirror[index ^ 1].reconstructPoint = 0; // start the reconstruct
+ pdev->reconCount = 1990; // mark target drive early
+ return pdev->DiskMirror[index].reconstructPoint;
+ }
+/****************************************************************
+ * Name: ReconTimerExpiry :LOCAL
+ *
+ * Description: Reconstruct timer expiry routine.
+ *
+ * Parameters: data - Pointer adapter data structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void ReconTimerExpiry (unsigned long data)
+ {
+ PADAPTER2220I padapter = (PADAPTER2220I)data;
+ struct Scsi_Host *host = padapter->SCpnt->device->host;
+ POUR_DEVICE pdev;
+ ULONG testsize = 0;
+ PIDENTIFY_DATA pid;
+ USHORT minmode;
+ ULONG zl;
+ UCHAR zc;
+ USHORT z;
+ unsigned long flags;
+
+ /*
+ * Disable interrupts, if they aren't already disabled and acquire
+ * the I/O spinlock.
+ */
+ spin_lock_irqsave(host->host_lock, flags);
+
+ if ( padapter->SCpnt )
+ goto reconTimerExpiry;
+
+ padapter->reconTimer.data = 0;
+ for ( z = padapter->devInReconIndex + 1; z < BIGD_MAXDRIVES; z++ )
+ {
+ if ( padapter->device[z].reconOn )
+ break;
+ }
+ if ( z < BIGD_MAXDRIVES )
+ pdev = &padapter->device[z];
+ else
+ {
+ for ( z = 0; z < BIGD_MAXDRIVES; z++ )
+ {
+ if ( padapter->device[z].reconOn )
+ break;
+ }
+ if ( z < BIGD_MAXDRIVES )
+ pdev = &padapter->device[z];
+ else
+ {
+ padapter->reconOn = FALSE;
+ goto reconTimerExpiry;
+ }
+ }
+
+ padapter->devInReconIndex = z;
+ pid = (PIDENTIFY_DATA)padapter->kBuffer;
+ padapter->pdev = pdev;
+ if ( pdev->reconIsStarting )
+ {
+ pdev->reconIsStarting = FALSE;
+ pdev->reconOn = FALSE;
+
+ while ( (pdev->DiskMirror[0].signature == SIGNATURE) && (pdev->DiskMirror[1].signature == SIGNATURE) &&
+ (pdev->DiskMirror[0].pairIdentifier == (pdev->DiskMirror[1].pairIdentifier ^ 1)) )
+ {
+ if ( (pdev->DiskMirror[0].status & UCBF_MATCHED) && (pdev->DiskMirror[1].status & UCBF_MATCHED) )
+ break;
+
+ if ( pdev->DiskMirror[0].status & UCBF_SURVIVOR ) // is first drive survivor?
+ testsize = SetReconstruct (pdev, 0);
+ else
+ if ( pdev->DiskMirror[1].status & UCBF_SURVIVOR ) // is second drive survivor?
+ testsize = SetReconstruct (pdev, 1);
+
+ if ( (pdev->DiskMirror[0].status & UCBF_REBUILD) || (pdev->DiskMirror[1].status & UCBF_REBUILD) )
+ {
+ if ( pdev->DiskMirror[0].status & UCBF_REBUILD )
+ pdev->mirrorRecon = 0;
+ else
+ pdev->mirrorRecon = 1;
+ pdev->reconOn = TRUE;
+ }
+ break;
+ }
+
+ if ( !pdev->reconOn )
+ goto reconTimerExpiry;
+
+ if ( padapter->bigD )
+ {
+ padapter->failRegister = 0;
+ outb_p (~padapter->failRegister, padapter->regFail);
+ }
+ else
+ {
+ zc = ((inb_p (padapter->regStatSel) >> 3) | inb_p (padapter->regStatSel)) & 0x83; // mute the alarm
+ outb_p (0xFF, padapter->regFail);
+ }
+
+ while ( 1 )
+ {
+ DEB (printk ("\npci2220i: hard reset issue"));
+ if ( HardReset (padapter, pdev, pdev->spigots[pdev->mirrorRecon]) )
+ {
+ DEB (printk ("\npci2220i: sub 1"));
+ break;
+ }
+
+ pdev->lastsectorlba[pdev->mirrorRecon] = InlineIdentify (padapter, pdev->spigots[pdev->mirrorRecon], pdev->deviceID[pdev->mirrorRecon] & 1);
+
+ if ( pdev->lastsectorlba[pdev->mirrorRecon] < testsize )
+ {
+ DEB (printk ("\npci2220i: sub 2 %ld %ld", pdev->lastsectorlba[pdev->mirrorRecon], testsize));
+ break;
+ }
+
+ // test LBA and multiper sector transfer compatibility
+ if (!pid->SupportLBA || (pid->NumSectorsPerInt < SECTORSXFER) || !pid->Valid_64_70 )
+ {
+ DEB (printk ("\npci2220i: sub 3"));
+ break;
+ }
+
+ // test PIO/bus matering mode compatibility
+ if ( (pid->MinPIOCycleWithoutFlow > 240) && !pid->SupportIORDYDisable && !padapter->timingPIO )
+ {
+ DEB (printk ("\npci2220i: sub 4"));
+ break;
+ }
+
+ if ( pid->MinPIOCycleWithoutFlow <= 120 ) // setup timing mode of drive
+ minmode = 5;
+ else
+ {
+ if ( pid->MinPIOCylceWithFlow <= 150 )
+ minmode = 4;
+ else
+ {
+ if ( pid->MinPIOCylceWithFlow <= 180 )
+ minmode = 3;
+ else
+ {
+ if ( pid->MinPIOCylceWithFlow <= 240 )
+ minmode = 2;
+ else
+ {
+ DEB (printk ("\npci2220i: sub 5"));
+ break;
+ }
+ }
+ }
+ }
+
+ if ( padapter->timingMode > minmode ) // set minimum timing mode
+ padapter->timingMode = minmode;
+ if ( padapter->timingMode >= 2 )
+ padapter->timingAddress = ModeArray[padapter->timingMode - 2];
+ else
+ padapter->timingPIO = TRUE;
+
+ padapter->reconOn = TRUE;
+ break;
+ }
+
+ if ( !pdev->reconOn )
+ {
+ padapter->survivor = pdev->mirrorRecon ^ 1;
+ padapter->reconPhase = RECON_PHASE_FAILOVER;
+ DEB (printk ("\npci2220i: FAILURE 7"));
+ InitFailover (padapter, pdev);
+ goto reconTimerExpiry;
+ }
+
+ pdev->raid = TRUE;
+
+ if ( WriteSignature (padapter, pdev, pdev->spigot, pdev->mirrorRecon ^ 1) )
+ goto reconTimerExpiry;
+ padapter->reconPhase = RECON_PHASE_MARKING;
+ goto reconTimerExpiry;
+ }
+
+ //**********************************
+ // reconstruct copy starts here
+ //**********************************
+ if ( pdev->reconCount++ > 2000 )
+ {
+ pdev->reconCount = 0;
+ if ( WriteSignature (padapter, pdev, pdev->spigots[pdev->mirrorRecon], pdev->mirrorRecon) )
+ {
+ padapter->survivor = pdev->mirrorRecon ^ 1;
+ padapter->reconPhase = RECON_PHASE_FAILOVER;
+ DEB (printk ("\npci2220i: FAILURE 8"));
+ InitFailover (padapter, pdev);
+ goto reconTimerExpiry;
+ }
+ padapter->reconPhase = RECON_PHASE_UPDATE;
+ goto reconTimerExpiry;
+ }
+
+ zl = pdev->DiskMirror[pdev->mirrorRecon].reconstructPoint;
+ padapter->reconSize = pdev->DiskMirror[pdev->mirrorRecon ^ 1].reconstructPoint - zl;
+ if ( padapter->reconSize > MAX_BUS_MASTER_BLOCKS )
+ padapter->reconSize = MAX_BUS_MASTER_BLOCKS;
+
+ if ( padapter->reconSize )
+ {
+ SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1]); // select the spigots
+ outb_p (pdev->byte6 | ((UCHAR *)(&zl))[3], padapter->regLba24); // select the drive
+ SelectSpigot (padapter, pdev->spigot);
+ if ( WaitReady (padapter) )
+ goto reconTimerExpiry;
+
+ SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon]);
+ if ( WaitReady (padapter) )
+ {
+ padapter->survivor = pdev->mirrorRecon ^ 1;
+ padapter->reconPhase = RECON_PHASE_FAILOVER;
+ DEB (printk ("\npci2220i: FAILURE 9"));
+ InitFailover (padapter, pdev);
+ goto reconTimerExpiry;
+ }
+
+ SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1]);
+ outb_p (padapter->reconSize & 0xFF, padapter->regSectCount);
+ outb_p (((UCHAR *)(&zl))[0], padapter->regLba0);
+ outb_p (((UCHAR *)(&zl))[1], padapter->regLba8);
+ outb_p (((UCHAR *)(&zl))[2], padapter->regLba16);
+ padapter->expectingIRQ = TRUE;
+ padapter->reconPhase = RECON_PHASE_READY;
+ SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon]);
+ WriteCommand (padapter, WRITE_CMD);
+ StartTimer (padapter);
+ SelectSpigot (padapter, pdev->spigot);
+ WriteCommand (padapter, READ_CMD);
+ goto reconTimerExpiry;
+ }
+
+ pdev->DiskMirror[pdev->mirrorRecon].status = UCBF_MIRRORED | UCBF_MATCHED;
+ pdev->DiskMirror[pdev->mirrorRecon ^ 1].status = UCBF_MIRRORED | UCBF_MATCHED;
+ if ( WriteSignature (padapter, pdev, pdev->spigot, pdev->mirrorRecon ^ 1) )
+ goto reconTimerExpiry;
+ padapter->reconPhase = RECON_PHASE_LAST;
+
+reconTimerExpiry:;
+ /*
+ * Release the I/O spinlock and restore the original flags
+ * which will enable interrupts if and only if they were
+ * enabled on entry.
+ */
+ spin_unlock_irqrestore(host->host_lock, flags);
+ }
+/****************************************************************
+ * Name: Irq_Handler :LOCAL
+ *
+ * Description: Interrupt handler.
+ *
+ * Parameters: irq - Hardware IRQ number.
+ * dev_id -
+ * regs -
+ *
+ * Returns: TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static irqreturn_t Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+ {
+ struct Scsi_Host *shost = NULL; // Pointer to host data block
+ PADAPTER2220I padapter; // Pointer to adapter control structure
+ POUR_DEVICE pdev;
+ Scsi_Cmnd *SCpnt;
+ UCHAR status;
+ UCHAR status1;
+ ATAPI_STATUS statusa;
+ ATAPI_REASON reasona;
+ ATAPI_ERROR errora;
+ int z;
+ ULONG zl;
+ unsigned long flags;
+ int handled = 0;
+
+// DEB (printk ("\npci2220i received interrupt\n"));
+
+ for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process
+ {
+ if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+ {
+ if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 )
+ {
+ shost = PsiHost[z];
+ break;
+ }
+ }
+ }
+
+ if ( !shost )
+ {
+ DEB (printk ("\npci2220i: not my interrupt"));
+ goto out;
+ }
+
+ handled = 1;
+ spin_lock_irqsave(shost->host_lock, flags);
+ padapter = HOSTDATA(shost);
+ pdev = padapter->pdev;
+ SCpnt = padapter->SCpnt;
+ outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine
+
+ if ( padapter->atapi && SCpnt )
+ {
+ *(char *)&statusa = inb_p (padapter->regStatCmd); // read the device status
+ *(char *)&reasona = inb_p (padapter->regSectCount); // read the device interrupt reason
+
+ if ( !statusa.bsy )
+ {
+ if ( statusa.drq ) // test for transfer phase
+ {
+ if ( !reasona.cod ) // test for data phase
+ {
+ z = (ULONG)inb_p (padapter->regLba8) | (ULONG)(inb_p (padapter->regLba16) << 8);
+ if ( padapter->reqSense )
+ insw (padapter->regData, SCpnt->sense_buffer, z / 2);
+ else
+ AtapiBusMaster (padapter, reasona.io, z);
+ goto irq_return;
+ }
+ if ( reasona.cod && !reasona.io ) // test for command packet phase
+ {
+ if ( padapter->reqSense )
+ AtapiRequestSense (padapter, pdev, SCpnt, TRUE);
+ else
+ AtapiSendCdb (padapter, pdev, padapter->atapiCdb);
+ goto irq_return;
+ }
+ }
+ else
+ {
+ if ( reasona.io && statusa.drdy ) // test for status phase
+ {
+ Atapi2Scsi (padapter, SCpnt);
+ if ( statusa.check )
+ {
+ *(UCHAR *)&errora = inb_p (padapter->regError); // read the device error
+ if ( errora.senseKey )
+ {
+ if ( padapter->reqSense || AtapiRequestSense (padapter, pdev, SCpnt, FALSE) )
+ OpDone (padapter, DID_ERROR << 16);
+ }
+ else
+ {
+ if ( errora.ili || errora.abort )
+ OpDone (padapter, DID_ERROR << 16);
+ else
+ OpDone (padapter, DID_OK << 16);
+ }
+ }
+ else
+ if ( padapter->reqSense )
+ {
+ DEB (printk ("PCI2242I: Sense codes - %X %X %X ", ((UCHAR *)SCpnt->sense_buffer)[0], ((UCHAR *)SCpnt->sense_buffer)[12], ((UCHAR *)SCpnt->sense_buffer)[13]));
+ OpDone (padapter, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2);
+ }
+ else
+ OpDone (padapter, DID_OK << 16);
+ }
+ }
+ }
+ goto irq_return;
+ }
+
+ if ( !padapter->expectingIRQ || !(SCpnt || padapter->reconPhase) )
+ {
+ DEB(printk ("\npci2220i Unsolicited interrupt\n"));
+ STOP_HERE ();
+ goto irq_return;
+ }
+ padapter->expectingIRQ = 0;
+
+ if ( padapter->failinprog )
+ {
+ DEB (printk ("\npci2220i interrupt failover complete"));
+ padapter->failinprog = FALSE;
+ status = inb_p (padapter->regStatCmd); // read the device status
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ DEB (printk ("\npci2220i: interrupt failover error from drive %X", status));
+ padapter->cmd = 0;
+ }
+ else
+ {
+ DEB (printk ("\npci2220i: restarting failed opertation."));
+ pdev->spigot = (padapter->survivor) ? pdev->spigots[1] : pdev->spigots[0];
+ del_timer (&padapter->timer);
+ if ( padapter->reconPhase )
+ OpDone (padapter, DID_OK << 16);
+ else
+ Pci2220i_QueueCommand (SCpnt, SCpnt->scsi_done);
+ goto irq_return;
+ }
+ }
+
+ if ( padapter->reconPhase )
+ {
+ switch ( padapter->reconPhase )
+ {
+ case RECON_PHASE_MARKING:
+ case RECON_PHASE_LAST:
+ status = inb_p (padapter->regStatCmd); // read the device status
+ del_timer (&padapter->timer);
+ if ( padapter->reconPhase == RECON_PHASE_LAST )
+ {
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 1 : 0;
+ DEB (printk ("\npci2220i: FAILURE 10"));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DecodeError (padapter, status));
+ goto irq_return;
+ }
+ if ( WriteSignature (padapter, pdev, pdev->spigots[pdev->mirrorRecon], pdev->mirrorRecon) )
+ {
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 11"));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DecodeError (padapter, status));
+ goto irq_return;
+ }
+ padapter->reconPhase = RECON_PHASE_END;
+ goto irq_return;
+ }
+ OpDone (padapter, DID_OK << 16);
+ goto irq_return;
+
+ case RECON_PHASE_READY:
+ status = inb_p (padapter->regStatCmd); // read the device status
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ del_timer (&padapter->timer);
+ OpDone (padapter, DecodeError (padapter, status));
+ goto irq_return;
+ }
+ SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon]);
+ if ( WaitDrq (padapter) )
+ {
+ del_timer (&padapter->timer);
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 12"));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DecodeError (padapter, status));
+ goto irq_return;
+ }
+ SelectSpigot (padapter, pdev->spigot | SEL_COPY | padapter->bigD);
+ padapter->reconPhase = RECON_PHASE_COPY;
+ padapter->expectingIRQ = TRUE;
+ if ( padapter->timingPIO )
+ {
+ insw (padapter->regData, padapter->kBuffer, padapter->reconSize * (BYTES_PER_SECTOR / 2));
+ }
+ else
+ {
+ if ( (padapter->timingMode > 3) )
+ {
+ if ( padapter->bigD )
+ outl (BIGD_DATA_MODE3, padapter->regDmaAddrLoc);
+ else
+ outl (DALE_DATA_MODE3, padapter->regDmaAddrLoc);
+ }
+ else
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (padapter->kBufferDma, padapter->regDmaAddrPci);
+ outl (padapter->reconSize * BYTES_PER_SECTOR, padapter->regDmaCount);
+ outb_p (8, padapter->regDmaDesc); // read operation
+ if ( padapter->bigD )
+ outb_p (8, padapter->regDmaMode); // no interrupt
+ else
+ outb_p (1, padapter->regDmaMode); // no interrupt
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ }
+ goto irq_return;
+
+ case RECON_PHASE_COPY:
+ pdev->DiskMirror[pdev->mirrorRecon].reconstructPoint += padapter->reconSize;
+
+ case RECON_PHASE_UPDATE:
+ SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon] | SEL_IRQ_OFF);
+ status = inb_p (padapter->regStatCmd); // read the device status
+ del_timer (&padapter->timer);
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 13"));
+ DEB (printk ("\n status register = %X error = %X", status, inb_p (padapter->regError)));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DecodeError (padapter, status));
+ goto irq_return;
+ }
+ OpDone (padapter, DID_OK << 16);
+ goto irq_return;
+
+ case RECON_PHASE_END:
+ status = inb_p (padapter->regStatCmd); // read the device status
+ del_timer (&padapter->timer);
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1;
+ DEB (printk ("\npci2220i: FAILURE 14"));
+ if ( InitFailover (padapter, pdev) )
+ OpDone (padapter, DecodeError (padapter, status));
+ goto irq_return;
+ }
+ pdev->reconOn = 0;
+ if ( padapter->bigD )
+ {
+ for ( z = 0; z < padapter->numberOfDrives; z++ )
+ {
+ if ( padapter->device[z].DiskMirror[0].status & UCBF_SURVIVOR )
+ {
+ Alarm (padapter, padapter->device[z].deviceID[0] ^ 2);
+ MuteAlarm (padapter);
+ }
+ if ( padapter->device[z].DiskMirror[1].status & UCBF_SURVIVOR )
+ {
+ Alarm (padapter, padapter->device[z].deviceID[1] ^ 2);
+ MuteAlarm (padapter);
+ }
+ }
+ }
+ OpDone (padapter, DID_OK << 16);
+ goto irq_return;
+
+ default:
+ goto irq_return;
+ }
+ }
+
+ switch ( padapter->cmd ) // decide how to handle the interrupt
+ {
+ case READ_CMD:
+ if ( padapter->sectorCount )
+ {
+ status = inb_p (padapter->regStatCmd); // read the device status
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ if ( pdev->raid )
+ {
+ padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 1 : 0;
+ del_timer (&padapter->timer);
+ DEB (printk ("\npci2220i: FAILURE 15"));
+ if ( !InitFailover (padapter, pdev) )
+ goto irq_return;
+ }
+ break;
+ }
+ if ( padapter->timingPIO )
+ {
+ insw (padapter->regData, padapter->kBuffer, padapter->readCount / 2);
+ padapter->sectorCount -= padapter->readCount / BYTES_PER_SECTOR;
+ WalkScatGath (padapter, TRUE, padapter->readCount);
+ if ( !padapter->sectorCount )
+ {
+ status = 0;
+ break;
+ }
+ }
+ else
+ {
+ if ( padapter->readCount )
+ WalkScatGath (padapter, TRUE, padapter->readCount);
+ BusMaster (padapter, 1, 1);
+ }
+ padapter->expectingIRQ = TRUE;
+ goto irq_return;
+ }
+ if ( padapter->readCount && !padapter->timingPIO )
+ WalkScatGath (padapter, TRUE, padapter->readCount);
+ status = 0;
+ break;
+
+ case WRITE_CMD:
+ if ( pdev->raid )
+ {
+ SelectSpigot (padapter, pdev->spigots[0] | SEL_IRQ_OFF);
+ status = inb_p (padapter->regStatCmd); // read the device status
+ SelectSpigot (padapter, pdev->spigots[1] | SEL_IRQ_OFF);
+ status1 = inb_p (padapter->regStatCmd); // read the device status
+ }
+ else
+ SelectSpigot (padapter, pdev->spigot | SEL_IRQ_OFF);
+ status = inb_p (padapter->regStatCmd); // read the device status
+ status1 = 0;
+
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ if ( pdev->raid && !(status1 & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT)) )
+ {
+ padapter->survivor = 1;
+ del_timer (&padapter->timer);
+ SelectSpigot (padapter, pdev->spigot | SEL_IRQ_OFF);
+ DEB (printk ("\npci2220i: FAILURE 16 status = %X error = %X", status, inb_p (padapter->regError)));
+ if ( !InitFailover (padapter, pdev) )
+ goto irq_return;
+ }
+ break;
+ }
+ if ( pdev->raid )
+ {
+ if ( status1 & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ {
+ padapter->survivor = 0;
+ del_timer (&padapter->timer);
+ DEB (printk ("\npci2220i: FAILURE 17 status = %X error = %X", status1, inb_p (padapter->regError)));
+ if ( !InitFailover (padapter, pdev) )
+ goto irq_return;
+ status = status1;
+ break;
+ }
+ if ( padapter->sectorCount )
+ {
+ status = WriteDataBoth (padapter, pdev);
+ if ( status )
+ {
+ padapter->survivor = status >> 1;
+ del_timer (&padapter->timer);
+ DEB (printk ("\npci2220i: FAILURE 18"));
+ if ( !InitFailover (padapter, pdev) )
+ goto irq_return;
+ SelectSpigot (padapter, pdev->spigots[status] | SEL_IRQ_OFF);
+ status = inb_p (padapter->regStatCmd); // read the device status
+ break;
+ }
+ padapter->expectingIRQ = TRUE;
+ goto irq_return;
+ }
+ status = 0;
+ break;
+ }
+ if ( padapter->sectorCount )
+ {
+ SelectSpigot (padapter, pdev->spigot | padapter->bigD);
+ status = WriteData (padapter);
+ if ( status )
+ break;
+ padapter->expectingIRQ = TRUE;
+ goto irq_return;
+ }
+ status = 0;
+ break;
+
+ case IDE_COMMAND_IDENTIFY:
+ {
+ PINQUIRYDATA pinquiryData = SCpnt->request_buffer;
+ PIDENTIFY_DATA pid = (PIDENTIFY_DATA)padapter->kBuffer;
+
+ status = inb_p (padapter->regStatCmd);
+ if ( status & IDE_STATUS_DRQ )
+ {
+ insw (padapter->regData, pid, sizeof (IDENTIFY_DATA) >> 1);
+
+ memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure.
+ pinquiryData->DeviceType = 0;
+ pinquiryData->Versions = 2;
+ pinquiryData->AdditionalLength = 35 - 4;
+
+ // Fill in vendor identification fields.
+ for ( z = 0; z < 20; z += 2 )
+ {
+ pinquiryData->VendorId[z] = ((UCHAR *)pid->ModelNumber)[z + 1];
+ pinquiryData->VendorId[z + 1] = ((UCHAR *)pid->ModelNumber)[z];
+ }
+
+ // Initialize unused portion of product id.
+ for ( z = 0; z < 4; z++ )
+ pinquiryData->ProductId[12 + z] = ' ';
+
+ // Move firmware revision from IDENTIFY data to
+ // product revision in INQUIRY data.
+ for ( z = 0; z < 4; z += 2 )
+ {
+ pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)pid->FirmwareRevision)[z + 1];
+ pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)pid->FirmwareRevision)[z];
+ }
+ if ( pdev == padapter->device )
+ *((USHORT *)(&pinquiryData->VendorSpecific)) = DEVICE_DALE_1;
+
+ status = 0;
+ }
+ break;
+ }
+
+ default:
+ status = 0;
+ break;
+ }
+
+ del_timer (&padapter->timer);
+ if ( status )
+ {
+ DEB (printk ("\npci2220i Interrupt handler return error"));
+ zl = DecodeError (padapter, status);
+ }
+ else
+ zl = DID_OK << 16;
+
+ OpDone (padapter, zl);
+irq_return:
+ spin_unlock_irqrestore(shost->host_lock, flags);
+out:
+ return IRQ_RETVAL(handled);
+}
+
+/****************************************************************
+ * Name: Pci2220i_QueueCommand
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * done - Pointer to done function to call.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER2220I padapter = HOSTDATA(SCpnt->device->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->device->id];// Pointer to device information
+ UCHAR rc; // command return code
+ int z;
+ PDEVICE_RAID1 pdr;
+
+ SCpnt->scsi_done = done;
+ padapter->SCpnt = SCpnt; // Save this command data
+ padapter->readCount = 0;
+
+ if ( SCpnt->use_sg )
+ {
+ padapter->currentSgBuffer = ((struct scatterlist *)SCpnt->request_buffer)[0].address;
+ padapter->currentSgCount = ((struct scatterlist *)SCpnt->request_buffer)[0].length;
+ }
+ else
+ {
+ padapter->currentSgBuffer = SCpnt->request_buffer;
+ padapter->currentSgCount = SCpnt->request_bufflen;
+ }
+ padapter->nextSg = 1;
+
+ if ( !done )
+ {
+ printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ if ( padapter->atapi )
+ {
+ UCHAR zlo, zhi;
+
+ DEB (printk ("\nPCI2242I: ID %d, LUN %d opcode %X ", SCpnt->device->id, SCpnt->device->lun, *cdb));
+ padapter->pdev = pdev;
+ if ( !pdev->byte6 || SCpnt->device->lun )
+ {
+ OpDone (padapter, DID_BAD_TARGET << 16);
+ return 0;
+ }
+
+ padapter->atapiSpecial = FALSE;
+ padapter->reqSense = FALSE;
+ memset (padapter->atapiCdb, 0, 16);
+ SelectSpigot (padapter, pdev->spigot); // select the spigot
+ AtapiDevice (padapter, pdev->byte6); // select the drive
+ if ( AtapiWaitReady (padapter, 100) )
+ {
+ OpDone (padapter, DID_NO_CONNECT << 16);
+ return 0;
+ }
+
+ switch ( cdb[0] )
+ {
+ case SCSIOP_MODE_SENSE:
+ case SCSIOP_MODE_SELECT:
+ Scsi2Atapi (padapter, SCpnt);
+ z = SCpnt->request_bufflen + 4;
+ break;
+ case SCSIOP_READ6:
+ case SCSIOP_WRITE6:
+ Scsi2Atapi (padapter, SCpnt);
+ z = SCpnt->request_bufflen;
+ break;
+ default:
+ memcpy (padapter->atapiCdb, cdb, SCpnt->cmd_len);
+ z = SCpnt->request_bufflen;
+ break;
+ }
+ if ( z > ATAPI_TRANSFER )
+ z = ATAPI_TRANSFER;
+ zlo = (UCHAR)(z & 0xFF);
+ zhi = (UCHAR)(z >> 8);
+
+ AtapiCountLo (padapter, zlo);
+ AtapiCountHi (padapter, zhi);
+ outb_p (0, padapter->regError);
+ WriteCommand (padapter, IDE_COMMAND_ATAPI_PACKET);
+ if ( pdev->cmdDrqInt )
+ return 0;
+
+ if ( AtapiWaitDrq (padapter, 500) )
+ {
+ OpDone (padapter, DID_ERROR << 16);
+ return 0;
+ }
+ AtapiSendCdb (padapter, pdev, padapter->atapiCdb);
+ return 0;
+ }
+
+ if ( padapter->reconPhase )
+ return 0;
+ if ( padapter->reconTimer.data )
+ {
+ del_timer (&padapter->reconTimer);
+ padapter->reconTimer.data = 0;
+ }
+
+ if ( (SCpnt->device->id >= padapter->numberOfDrives) || SCpnt->device->lun )
+ {
+ OpDone (padapter, DID_BAD_TARGET << 16);
+ return 0;
+ }
+
+ switch ( *cdb )
+ {
+ case SCSIOP_INQUIRY: // inquiry CDB
+ {
+ if ( cdb[2] == SC_MY_RAID )
+ {
+ switch ( cdb[3] )
+ {
+ case MY_SCSI_REBUILD:
+ for ( z = 0; z < padapter->numberOfDrives; z++ )
+ {
+ pdev = &padapter->device[z];
+ if ( ((pdev->DiskMirror[0].status & UCBF_SURVIVOR) && (pdev->DiskMirror[1].status & UCBF_MIRRORED)) ||
+ ((pdev->DiskMirror[1].status & UCBF_SURVIVOR) && (pdev->DiskMirror[0].status & UCBF_MIRRORED)) )
+ {
+ padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE;
+ }
+ }
+ OpDone (padapter, DID_OK << 16);
+ break;
+ case MY_SCSI_ALARMMUTE:
+ MuteAlarm (padapter);
+ OpDone (padapter, DID_OK << 16);
+ break;
+ case MY_SCSI_DEMOFAIL:
+ padapter->demoFail = TRUE;
+ OpDone (padapter, DID_OK << 16);
+ break;
+ default:
+ z = cdb[5]; // get index
+ pdr = (PDEVICE_RAID1)SCpnt->request_buffer;
+ if ( padapter->raidData[z] )
+ {
+ memcpy (&pdr->DiskRaid1, padapter->raidData[z], sizeof (DISK_MIRROR));
+ if ( padapter->raidData[z]->reconstructPoint > padapter->raidData[z ^ 2]->reconstructPoint )
+ pdr->TotalSectors = padapter->raidData[z]->reconstructPoint;
+ else
+ pdr->TotalSectors = padapter->raidData[z ^ 2]->reconstructPoint;
+ }
+ else
+ memset (pdr, 0, sizeof (DEVICE_RAID1));
+ OpDone (padapter, DID_OK << 16);
+ break;
+ }
+ return 0;
+ }
+ padapter->cmd = IDE_COMMAND_IDENTIFY;
+ break;
+ }
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ OpDone (padapter, DID_OK << 16);
+ return 0;
+ case SCSIOP_READ_CAPACITY: // read capctiy CDB
+ {
+ PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+ pdata->blksiz = 0x20000;
+ XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+ OpDone (padapter, DID_OK << 16);
+ return 0;
+ }
+ case SCSIOP_VERIFY: // verify CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+ padapter->cmd = IDE_COMMAND_VERIFY;
+ break;
+ case SCSIOP_READ: // read10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ padapter->cmd = READ_CMD;
+ break;
+ case SCSIOP_READ6: // read6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ padapter->cmd = READ_CMD;
+ break;
+ case SCSIOP_WRITE: // write10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ padapter->cmd = WRITE_CMD;
+ break;
+ case SCSIOP_WRITE6: // write6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ padapter->cmd = WRITE_CMD;
+ break;
+ default:
+ DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+ OpDone (padapter, DID_ERROR << 16);
+ return 0;
+ }
+
+ if ( padapter->reconPhase )
+ return 0;
+
+ padapter->pdev = pdev;
+
+ while ( padapter->demoFail )
+ {
+ pdev = padapter->pdev = &padapter->device[0];
+ padapter->demoFail = FALSE;
+ if ( !pdev->raid ||
+ (pdev->DiskMirror[0].status & UCBF_SURVIVOR) ||
+ (pdev->DiskMirror[1].status & UCBF_SURVIVOR) )
+ {
+ break;
+ }
+ if ( pdev->DiskMirror[0].status & UCBF_REBUILD )
+ padapter->survivor = 1;
+ else
+ padapter->survivor = 0;
+ DEB (printk ("\npci2220i: FAILURE 19"));
+ if ( InitFailover (padapter, pdev) )
+ break;
+ return 0;
+ }
+
+ StartTimer (padapter);
+ if ( pdev->raid && (padapter->cmd == WRITE_CMD) )
+ {
+ rc = IdeCmdBoth (padapter, pdev);
+ if ( !rc )
+ rc = WriteDataBoth (padapter, pdev);
+ if ( rc )
+ {
+ del_timer (&padapter->timer);
+ padapter->expectingIRQ = 0;
+ padapter->survivor = rc >> 1;
+ DEB (printk ("\npci2220i: FAILURE 20"));
+ if ( InitFailover (padapter, pdev) )
+ {
+ OpDone (padapter, DID_ERROR << 16);
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ rc = IdeCmd (padapter, pdev);
+ if ( (padapter->cmd == WRITE_CMD) && !rc )
+ rc = WriteData (padapter);
+ if ( rc )
+ {
+ del_timer (&padapter->timer);
+ padapter->expectingIRQ = 0;
+ if ( pdev->raid )
+ {
+ padapter->survivor = (pdev->spigot ^ 3) >> 1;
+ DEB (printk ("\npci2220i: FAILURE 21"));
+ if ( !InitFailover (padapter, pdev) )
+ return 0;
+ }
+ OpDone (padapter, DID_ERROR << 16);
+ return 0;
+ }
+ }
+ return 0;
+ }
+/****************************************************************
+ * Name: ReadFlash
+ *
+ * Description: Read information from controller Flash memory.
+ *
+ * Parameters: padapter - Pointer to host interface data structure.
+ * pdata - Pointer to data structures.
+ * base - base address in Flash.
+ * length - lenght of data space in bytes.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static VOID ReadFlash (PADAPTER2220I padapter, VOID *pdata, ULONG base, ULONG length)
+ {
+ ULONG oldremap;
+ UCHAR olddesc;
+ ULONG z;
+ UCHAR *pd = (UCHAR *)pdata;
+
+ oldremap = inl (padapter->regRemap); // save values to restore later
+ olddesc = inb_p (padapter->regDesc);
+
+ outl (base | 1, padapter->regRemap); // remap to Flash space as specified
+ outb_p (0x40, padapter->regDesc); // describe remap region as 8 bit
+ for ( z = 0; z < length; z++) // get "length" data count
+ *pd++ = inb_p (padapter->regBase + z); // read in the data
+
+ outl (oldremap, padapter->regRemap); // restore remap register values
+ outb_p (olddesc, padapter->regDesc);
+ }
+/****************************************************************
+ * Name: GetRegs
+ *
+ * Description: Initialize the regester information.
+ *
+ * Parameters: pshost - Pointer to SCSI host data structure.
+ * bigd - PCI-2240I identifier
+ * pcidev - Pointer to device data structure.
+ *
+ * Returns: TRUE if failure to install.
+ *
+ ****************************************************************/
+static USHORT GetRegs (struct Scsi_Host *pshost, BOOL bigd, struct pci_dev *pcidev)
+ {
+ PADAPTER2220I padapter;
+ int setirq;
+ int z;
+ USHORT zr, zl;
+ UCHAR *consistent;
+ dma_addr_t consistentDma;
+
+ padapter = HOSTDATA(pshost);
+ memset (padapter, 0, sizeof (ADAPTER2220I));
+ memset (&DaleSetup, 0, sizeof (DaleSetup));
+ memset (DiskMirror, 0, sizeof (DiskMirror));
+
+ zr = pci_resource_start (pcidev, 1);
+ zl = pci_resource_start (pcidev, 2);
+
+ padapter->basePort = zr;
+ padapter->regRemap = zr + RTR_LOCAL_REMAP; // 32 bit local space remap
+ padapter->regDesc = zr + RTR_REGIONS; // 32 bit local region descriptor
+ padapter->regRange = zr + RTR_LOCAL_RANGE; // 32 bit local range
+ padapter->regIrqControl = zr + RTR_INT_CONTROL_STATUS; // 16 bit interrupt control and status
+ padapter->regScratchPad = zr + RTR_MAILBOX; // 16 byte scratchpad I/O base address
+
+ padapter->regBase = zl;
+ padapter->regData = zl + REG_DATA; // data register I/O address
+ padapter->regError = zl + REG_ERROR; // error register I/O address
+ padapter->regSectCount = zl + REG_SECTOR_COUNT; // sector count register I/O address
+ padapter->regLba0 = zl + REG_LBA_0; // least significant byte of LBA
+ padapter->regLba8 = zl + REG_LBA_8; // next least significant byte of LBA
+ padapter->regLba16 = zl + REG_LBA_16; // next most significan byte of LBA
+ padapter->regLba24 = zl + REG_LBA_24; // head and most 4 significant bits of LBA
+ padapter->regStatCmd = zl + REG_STAT_CMD; // status on read and command on write register
+ padapter->regStatSel = zl + REG_STAT_SEL; // board status on read and spigot select on write register
+ padapter->regFail = zl + REG_FAIL;
+ padapter->regAltStat = zl + REG_ALT_STAT;
+ padapter->pcidev = pcidev;
+
+ if ( bigd )
+ {
+ padapter->regDmaDesc = zr + RTR_DMA0_DESC_PTR; // address of the DMA discriptor register for direction of transfer
+ padapter->regDmaCmdStat = zr + RTR_DMA_COMMAND_STATUS; // Byte #0 of DMA command status register
+ padapter->regDmaAddrPci = zr + RTR_DMA0_PCI_ADDR; // 32 bit register for PCI address of DMA
+ padapter->regDmaAddrLoc = zr + RTR_DMA0_LOCAL_ADDR; // 32 bit register for local bus address of DMA
+ padapter->regDmaCount = zr + RTR_DMA0_COUNT; // 32 bit register for DMA transfer count
+ padapter->regDmaMode = zr + RTR_DMA0_MODE + 1; // 32 bit register for DMA mode control
+ padapter->bigD = SEL_NEW_SPEED_1; // set spigot speed control bit
+ }
+ else
+ {
+ padapter->regDmaDesc = zl + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer
+ padapter->regDmaCmdStat = zl + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register
+ padapter->regDmaAddrPci = zl + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA
+ padapter->regDmaAddrLoc = zl + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA
+ padapter->regDmaCount = zl + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count
+ padapter->regDmaMode = zl + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control
+ }
+
+ padapter->numberOfDrives = inb_p (padapter->regScratchPad + BIGD_NUM_DRIVES);
+ if ( !bigd && !padapter->numberOfDrives ) // if no devices on this board
+ return TRUE;
+
+ pshost->irq = pcidev->irq;
+ setirq = 1;
+ for ( z = 0; z < Installed; z++ ) // scan for shared interrupts
+ {
+ if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses
+ setirq = 0;
+ }
+ if ( setirq ) // if not shared, posses
+ {
+ if ( request_irq (pshost->irq, Irq_Handler, SA_SHIRQ, "pci2220i", padapter) < 0 )
+ {
+ if ( request_irq (pshost->irq, Irq_Handler, SA_INTERRUPT | SA_SHIRQ, "pci2220i", padapter) < 0 )
+ {
+ printk ("Unable to allocate IRQ for PCI-2220I controller.\n");
+ return TRUE;
+ }
+ }
+ padapter->irqOwned = pshost->irq; // set IRQ as owned
+ }
+
+ if ( padapter->numberOfDrives )
+ consistent = pci_alloc_consistent (pcidev, SECTORSXFER * BYTES_PER_SECTOR, &consistentDma);
+ else
+ consistent = pci_alloc_consistent (pcidev, ATAPI_TRANSFER, &consistentDma);
+ if ( !consistent )
+ {
+ printk ("Unable to allocate DMA buffer for PCI-2220I controller.\n");
+ free_irq (pshost->irq, padapter);
+ return TRUE;
+ }
+ padapter->kBuffer = consistent;
+ padapter->kBufferDma = consistentDma;
+
+ PsiHost[Installed] = pshost; // save SCSI_HOST pointer
+ pshost->io_port = padapter->basePort;
+ pshost->n_io_port = 0xFF;
+ pshost->unique_id = padapter->regBase;
+
+ outb_p (0x01, padapter->regRange); // fix our range register because other drivers want to tromp on it
+
+ padapter->timingMode = inb_p (padapter->regScratchPad + DALE_TIMING_MODE);
+ if ( padapter->timingMode >= 2 )
+ {
+ if ( bigd )
+ padapter->timingAddress = ModeArray2[padapter->timingMode - 2];
+ else
+ padapter->timingAddress = ModeArray[padapter->timingMode - 2];
+ }
+ else
+ padapter->timingPIO = TRUE;
+
+ ReadFlash (padapter, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP));
+ ReadFlash (padapter, &DiskMirror, DALE_FLASH_RAID, sizeof (DiskMirror));
+
+ return FALSE;
+ }
+/****************************************************************
+ * Name: SetupFinish
+ *
+ * Description: Complete the driver initialization process for a card
+ *
+ * Parameters: padapter - Pointer to SCSI host data structure.
+ * str - Pointer to board type string.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+VOID SetupFinish (PADAPTER2220I padapter, char *str, int irq)
+ {
+ init_timer (&padapter->timer);
+ padapter->timer.function = TimerExpiry;
+ padapter->timer.data = (unsigned long)padapter;
+ init_timer (&padapter->reconTimer);
+ padapter->reconTimer.function = ReconTimerExpiry;
+ padapter->reconTimer.data = (unsigned long)padapter;
+ printk("\nPCI-%sI EIDE CONTROLLER: at I/O = %lX/%lX IRQ = %d\n", str, padapter->basePort, padapter->regBase, irq);
+ printk("Version %s, Compiled %s %s\n\n", PCI2220I_VERSION, __DATE__, __TIME__);
+ }
+/****************************************************************
+ * Name: Pci2220i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters installed.
+ *
+ ****************************************************************/
+int Pci2220i_Detect (Scsi_Host_Template *tpnt)
+ {
+ struct Scsi_Host *pshost;
+ PADAPTER2220I padapter;
+ POUR_DEVICE pdev;
+ int unit;
+ int z;
+ USHORT raidon;
+ UCHAR spigot1, spigot2;
+ UCHAR device;
+ struct pci_dev *pcidev = NULL;
+
+ while ( (pcidev = pci_find_device (VENDOR_PSI, DEVICE_DALE_1, pcidev)) != NULL )
+ {
+ if (pci_enable_device(pcidev))
+ continue;
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+ if(pshost==NULL)
+ continue;
+
+ padapter = HOSTDATA(pshost);
+
+ if ( GetRegs (pshost, FALSE, pcidev) )
+ goto unregister;
+
+ scsi_set_device(pshost, &pcidev->dev);
+ pshost->max_id = padapter->numberOfDrives;
+ for ( z = 0; z < padapter->numberOfDrives; z++ )
+ {
+ unit = inb_p (padapter->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F;
+ pdev = &padapter->device[z];
+ pdev->byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+ pdev->spigot = (UCHAR)(1 << (unit >> 1));
+ pdev->sectors = DaleSetup.setupDevice[unit].sectors;
+ pdev->heads = DaleSetup.setupDevice[unit].heads;
+ pdev->cylinders = DaleSetup.setupDevice[unit].cylinders;
+ pdev->blocks = DaleSetup.setupDevice[unit].blocks;
+
+ if ( !z )
+ {
+ DiskMirror[0].status = inb_p (padapter->regScratchPad + DALE_RAID_0_STATUS);
+ DiskMirror[1].status = inb_p (padapter->regScratchPad + DALE_RAID_1_STATUS);
+ if ( (DiskMirror[0].signature == SIGNATURE) && (DiskMirror[1].signature == SIGNATURE) &&
+ (DiskMirror[0].pairIdentifier == (DiskMirror[1].pairIdentifier ^ 1)) )
+ {
+ raidon = TRUE;
+ if ( unit > (unit ^ 2) )
+ unit = unit ^ 2;
+ }
+ else
+ raidon = FALSE;
+
+ memcpy (pdev->DiskMirror, DiskMirror, sizeof (DiskMirror));
+ padapter->raidData[0] = &pdev->DiskMirror[0];
+ padapter->raidData[2] = &pdev->DiskMirror[1];
+
+ spigot1 = spigot2 = FALSE;
+ pdev->spigots[0] = 1;
+ pdev->spigots[1] = 2;
+ pdev->lastsectorlba[0] = InlineIdentify (padapter, 1, 0);
+ pdev->lastsectorlba[1] = InlineIdentify (padapter, 2, 0);
+
+ if ( !(pdev->DiskMirror[1].status & UCBF_SURVIVOR) && pdev->lastsectorlba[0] )
+ spigot1 = TRUE;
+ if ( !(pdev->DiskMirror[0].status & UCBF_SURVIVOR) && pdev->lastsectorlba[1] )
+ spigot2 = TRUE;
+ if ( pdev->DiskMirror[0].status & DiskMirror[1].status & UCBF_SURVIVOR )
+ spigot1 = TRUE;
+
+ if ( spigot1 && (pdev->DiskMirror[0].status & UCBF_REBUILD) )
+ InlineReadSignature (padapter, pdev, 0);
+ if ( spigot2 && (pdev->DiskMirror[1].status & UCBF_REBUILD) )
+ InlineReadSignature (padapter, pdev, 1);
+
+ if ( spigot1 && spigot2 && raidon )
+ {
+ pdev->raid = 1;
+ if ( pdev->DiskMirror[0].status & UCBF_REBUILD )
+ pdev->spigot = 2;
+ else
+ pdev->spigot = 1;
+ if ( (pdev->DiskMirror[0].status & UCBF_REBUILD) || (pdev->DiskMirror[1].status & UCBF_REBUILD) )
+ padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE;
+ }
+ else
+ {
+ if ( spigot1 )
+ {
+ if ( pdev->DiskMirror[0].status & UCBF_REBUILD )
+ goto unregister;
+ pdev->DiskMirror[0].status = UCBF_MIRRORED | UCBF_SURVIVOR;
+ pdev->spigot = 1;
+ }
+ else
+ {
+ if ( pdev->DiskMirror[1].status & UCBF_REBUILD )
+ goto unregister;
+ pdev->DiskMirror[1].status = UCBF_MIRRORED | UCBF_SURVIVOR;
+ pdev->spigot = 2;
+ }
+ if ( DaleSetup.rebootRebuild && raidon )
+ padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE;
+ }
+
+ if ( raidon )
+ break;
+ }
+ }
+
+ SetupFinish (padapter, "2220", pshost->irq);
+
+ if ( ++Installed < MAXADAPTER )
+ continue;
+ break;
+unregister:;
+ scsi_unregister (pshost);
+ }
+
+ while ( (pcidev = pci_find_device (VENDOR_PSI, DEVICE_BIGD_1, pcidev)) != NULL )
+ {
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+ padapter = HOSTDATA(pshost);
+
+ if ( GetRegs (pshost, TRUE, pcidev) )
+ goto unregister1;
+
+ for ( z = 0; z < BIGD_MAXDRIVES; z++ )
+ DiskMirror[z].status = inb_p (padapter->regScratchPad + BIGD_RAID_0_STATUS + z);
+
+ scsi_set_pci_device(pshost, pcidev);
+ pshost->max_id = padapter->numberOfDrives;
+ padapter->failRegister = inb_p (padapter->regScratchPad + BIGD_ALARM_IMAGE);
+ for ( z = 0; z < padapter->numberOfDrives; z++ )
+ {
+ unit = inb_p (padapter->regScratchPad + BIGD_DEVICE_0 + z);
+ pdev = &padapter->device[z];
+ pdev->byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+ pdev->spigot = (UCHAR)(1 << (unit >> 1));
+ pdev->sectors = DaleSetup.setupDevice[unit].sectors;
+ pdev->heads = DaleSetup.setupDevice[unit].heads;
+ pdev->cylinders = DaleSetup.setupDevice[unit].cylinders;
+ pdev->blocks = DaleSetup.setupDevice[unit].blocks;
+
+ if ( (DiskMirror[unit].signature == SIGNATURE) && (DiskMirror[unit ^ 2].signature == SIGNATURE) &&
+ (DiskMirror[unit].pairIdentifier == (DiskMirror[unit ^ 2].pairIdentifier ^ 1)) )
+ {
+ raidon = TRUE;
+ if ( unit > (unit ^ 2) )
+ unit = unit ^ 2;
+ }
+ else
+ raidon = FALSE;
+
+ spigot1 = spigot2 = FALSE;
+ memcpy (&pdev->DiskMirror[0], &DiskMirror[unit], sizeof (DISK_MIRROR));
+ memcpy (&pdev->DiskMirror[1], &DiskMirror[unit ^ 2], sizeof (DISK_MIRROR));
+ padapter->raidData[unit] = &pdev->DiskMirror[0];
+ padapter->raidData[unit ^ 2] = &pdev->DiskMirror[1];
+ pdev->spigots[0] = 1 << (unit >> 1);
+ pdev->spigots[1] = 1 << ((unit ^ 2) >> 1);
+ pdev->deviceID[0] = unit;
+ pdev->deviceID[1] = unit ^ 2;
+ pdev->lastsectorlba[0] = InlineIdentify (padapter, pdev->spigots[0], unit & 1);
+ pdev->lastsectorlba[1] = InlineIdentify (padapter, pdev->spigots[1], unit & 1);
+
+ if ( !(pdev->DiskMirror[1].status & UCBF_SURVIVOR) && pdev->lastsectorlba[0] )
+ spigot1 = TRUE;
+ if ( !(pdev->DiskMirror[0].status & UCBF_SURVIVOR) && pdev->lastsectorlba[1] )
+ spigot2 = TRUE;
+ if ( pdev->DiskMirror[0].status & pdev->DiskMirror[1].status & UCBF_SURVIVOR )
+ spigot1 = TRUE;
+
+ if ( spigot1 && (pdev->DiskMirror[0].status & UCBF_REBUILD) )
+ InlineReadSignature (padapter, pdev, 0);
+ if ( spigot2 && (pdev->DiskMirror[1].status & UCBF_REBUILD) )
+ InlineReadSignature (padapter, pdev, 1);
+
+ if ( spigot1 && spigot2 && raidon )
+ {
+ pdev->raid = 1;
+ if ( pdev->DiskMirror[0].status & UCBF_REBUILD )
+ pdev->spigot = pdev->spigots[1];
+ else
+ pdev->spigot = pdev->spigots[0];
+ if ( (pdev->DiskMirror[0].status & UCBF_REBUILD) || (pdev->DiskMirror[1].status & UCBF_REBUILD) )
+ padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE;
+ }
+ else
+ {
+ if ( spigot1 )
+ {
+ if ( pdev->DiskMirror[0].status & UCBF_REBUILD )
+ goto unregister1;
+ pdev->DiskMirror[0].status = UCBF_MIRRORED | UCBF_SURVIVOR;
+ pdev->spigot = pdev->spigots[0];
+ }
+ else
+ {
+ if ( pdev->DiskMirror[1].status & UCBF_REBUILD )
+ goto unregister;
+ pdev->DiskMirror[1].status = UCBF_MIRRORED | UCBF_SURVIVOR;
+ pdev->spigot = pdev->spigots[1];
+ }
+ if ( DaleSetup.rebootRebuild && raidon )
+ padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE;
+ }
+ }
+
+ if ( !padapter->numberOfDrives ) // If no ATA devices then scan ATAPI
+ {
+ unit = 0;
+ for ( spigot1 = 0; spigot1 < 4; spigot1++ )
+ {
+ for ( device = 0; device < 2; device++ )
+ {
+ DEB (printk ("\nPCI2242I: scanning for ID %d ", (spigot1 * 2) + device));
+ pdev = &(padapter->device[(spigot1 * 2) + device]);
+ pdev->byte6 = 0x0A | (device << 4);
+ pdev->spigot = 1 << spigot1;
+ if ( !AtapiReset (padapter, pdev) )
+ {
+ DEB (printk (" Device found "));
+ if ( !AtapiIdentify (padapter, pdev) )
+ {
+ DEB (printk (" Device verified"));
+ unit++;
+ continue;
+ }
+ }
+ pdev->spigot = pdev->byte6 = 0;
+ }
+ }
+
+ if ( unit )
+ {
+ padapter->atapi = TRUE;
+ padapter->timingAddress = DALE_DATA_MODE3;
+ outw_p (0x0900, padapter->regIrqControl); // Turn our interrupts on
+ outw_p (0x0C41, padapter->regDmaMode - 1); // setup for 16 bits, ready enabled, done IRQ enabled, no incriment
+ outb_p (0xFF, padapter->regFail); // all fail lights and alarm off
+ pshost->max_id = 8;
+ }
+ }
+ SetupFinish (padapter, "2240", pshost->irq);
+
+ if ( ++Installed < MAXADAPTER )
+ continue;
+ break;
+unregister1:;
+ scsi_unregister (pshost);
+ }
+
+ NumAdapters = Installed;
+ return Installed;
+ }
+/****************************************************************
+ * Name: Pci2220i_Abort
+ *
+ * Description: Process the Abort command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Allways snooze.
+ *
+ ****************************************************************/
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt)
+ {
+ PADAPTER2220I padapter = HOSTDATA(SCpnt->device->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->device->id];// Pointer to device information
+
+ if ( !padapter->SCpnt )
+ return SCSI_ABORT_NOT_RUNNING;
+
+ if ( padapter->atapi )
+ {
+ if ( AtapiReset (padapter, pdev) )
+ return SCSI_ABORT_ERROR;
+ OpDone (padapter, DID_ABORT << 16);
+ return SCSI_ABORT_SUCCESS;
+ }
+ return SCSI_ABORT_SNOOZE;
+ }
+/****************************************************************
+ * Name: Pci2220i_Reset
+ *
+ * Description: Process the Reset command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * flags - Flags about the reset command
+ *
+ * Returns: No active command at this time, so this means
+ * that each time we got some kind of response the
+ * last time through. Tell the mid-level code to
+ * request sense information in order to decide what
+ * to do next.
+ *
+ ****************************************************************/
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ PADAPTER2220I padapter = HOSTDATA(SCpnt->device->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->device->id];// Pointer to device information
+
+ if ( padapter->atapi )
+ {
+ if ( AtapiReset (padapter, pdev) )
+ return SCSI_RESET_ERROR;
+ return SCSI_RESET_SUCCESS;
+ }
+ return SCSI_RESET_PUNT;
+ }
+/****************************************************************
+ * Name: Pci2220i_Release
+ *
+ * Description: Release resources allocated for a single each adapter.
+ *
+ * Parameters: pshost - Pointer to SCSI command structure.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Pci2220i_Release (struct Scsi_Host *pshost)
+ {
+ PADAPTER2220I padapter = HOSTDATA (pshost);
+ USHORT z;
+
+ if ( padapter->reconOn )
+ {
+ padapter->reconOn = FALSE; // shut down the hot reconstruct
+ if ( padapter->reconPhase )
+ mdelay (300);
+ if ( padapter->reconTimer.data ) // is the timer running?
+ {
+ del_timer (&padapter->reconTimer);
+ padapter->reconTimer.data = 0;
+ }
+ }
+
+ // save RAID status on the board
+ if ( padapter->bigD )
+ {
+ outb_p (padapter->failRegister, padapter->regScratchPad + BIGD_ALARM_IMAGE);
+ for ( z = 0; z < BIGD_MAXDRIVES; z++ )
+ {
+ if ( padapter->raidData )
+ outb_p (padapter->raidData[z]->status, padapter->regScratchPad + BIGD_RAID_0_STATUS + z);
+ else
+ outb_p (0, padapter->regScratchPad + BIGD_RAID_0_STATUS);
+ }
+ }
+ else
+ {
+ outb_p (padapter->device[0].DiskMirror[0].status, padapter->regScratchPad + DALE_RAID_0_STATUS);
+ outb_p (padapter->device[0].DiskMirror[1].status, padapter->regScratchPad + DALE_RAID_1_STATUS);
+ }
+
+ if ( padapter->irqOwned )
+ free_irq (pshost->irq, padapter);
+ release_region (pshost->io_port, pshost->n_io_port);
+ if ( padapter->numberOfDrives )
+ pci_free_consistent (padapter->pcidev, SECTORSXFER * BYTES_PER_SECTOR, padapter->kBuffer, padapter->kBufferDma);
+ else
+ pci_free_consistent (padapter->pcidev, ATAPI_TRANSFER, padapter->kBuffer, padapter->kBufferDma);
+ scsi_unregister(pshost);
+ return 0;
+ }
+
+/****************************************************************
+ * Name: Pci2220i_BiosParam
+ *
+ * Description: Process the biosparam request from the SCSI manager to
+ * return C/H/S data.
+ *
+ * Parameters: disk - Pointer to SCSI disk structure.
+ * dev - Major/minor number from kernel.
+ * geom - Pointer to integer array to place geometry data.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Pci2220i_BiosParam (struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int geom[])
+ {
+ POUR_DEVICE pdev;
+
+ if ( !(HOSTDATA(sdev->host))->atapi )
+ {
+ pdev = &(HOSTDATA(sdev->host)->device[sdev->id]);
+
+ geom[0] = pdev->heads;
+ geom[1] = pdev->sectors;
+ geom[2] = pdev->cylinders;
+ }
+ return 0;
+ }
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "pci2220i",
+ .name = "PCI-2220I/PCI-2240I",
+ .detect = Pci2220i_Detect,
+ .release = Pci2220i_Release,
+ .queuecommand = Pci2220i_QueueCommand,
+ .abort = Pci2220i_Abort,
+ .reset = Pci2220i_Reset,
+ .bios_param = Pci2220i_BiosParam,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h
new file mode 100644
index 000000000000..6926056c2aee
--- /dev/null
+++ b/drivers/scsi/pci2220i.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2220I device driver for Linux.
+ *
+ * pci2220i.h - Linux Host Driver for PCI-2220i EIDE Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ * http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ * tech@psidisk.com Technical Support
+ *
+ ****************************************************************************/
+#ifndef _PCI2220I_H
+#define _PCI2220I_H
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+#define LINUXVERSION(v,p,s) (((v)<<16) + ((p)<<8) + (s))
+
+// function prototypes
+int Pci2220i_Detect (Scsi_Host_Template *tpnt);
+int Pci2220i_Command (Scsi_Cmnd *SCpnt);
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt);
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2220i_Release (struct Scsi_Host *pshost);
+int Pci2220i_BiosParam (struct scsi_device *sdev,
+ struct block_device *dev,
+ sector_t capacity, int geom[]);
+#endif
diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig
new file mode 100644
index 000000000000..df52190f4d94
--- /dev/null
+++ b/drivers/scsi/pcmcia/Kconfig
@@ -0,0 +1,82 @@
+#
+# PCMCIA SCSI adapter configuration
+#
+
+menu "PCMCIA SCSI adapter support"
+ depends on SCSI!=n && PCMCIA!=n && MODULES
+
+config PCMCIA_AHA152X
+ tristate "Adaptec AHA152X PCMCIA support"
+ depends on m && !64BIT
+ help
+ Say Y here if you intend to attach this type of PCMCIA SCSI host
+ adapter to your computer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aha152x_cs.
+
+config PCMCIA_FDOMAIN
+ tristate "Future Domain PCMCIA support"
+ depends on m
+ help
+ Say Y here if you intend to attach this type of PCMCIA SCSI host
+ adapter to your computer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fdomain_cs.
+
+config PCMCIA_NINJA_SCSI
+ tristate "NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support"
+ depends on m && !64BIT
+ help
+ If you intend to attach this type of PCMCIA SCSI host adapter to
+ your computer, say Y here and read
+ <file:Documentation/scsi/NinjaSCSI.txt>.
+
+ Supported cards:
+
+ NinjaSCSI-3: (version string: "WBT","NinjaSCSI-3","R1.0")
+ IO-DATA PCSC-FP
+ ALPHA DATA AD-PCS201
+ CyQ've SFC-201
+ LOGITECH LPM-SCSI2E
+ Pioneer PCR-PR24's card
+ I-O DATA CDPS-PX24's card (PCSC-F)
+ Panasonic KXL-RW10AN CD-RW's card
+ etc.
+
+ NinjaSCSI-32Bit (in 16bit mode):
+ [Workbit (version string: "WORKBIT","UltraNinja-16","1")]
+ Jazz SCP050
+ [I-O DATA (OEM) (version string: "IO DATA","CBSC16 ","1")]
+ I-O DATA CBSC-II
+ [Kyusyu Matsushita Kotobuki (OEM)
+ (version string: "KME ","SCSI-CARD-001","1")]
+ KME KXL-820AN's card
+ HP M820e CDRW's card
+ etc.
+
+ To compile this driver as a module, choose M here: the
+ module will be called nsp_cs.
+
+config PCMCIA_QLOGIC
+ tristate "Qlogic PCMCIA support"
+ depends on m
+ help
+ Say Y here if you intend to attach this type of PCMCIA SCSI host
+ adapter to your computer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qlogic_cs.
+
+config PCMCIA_SYM53C500
+ tristate "Symbios 53c500 PCMCIA support"
+ depends on m
+ help
+ Say Y here if you have a New Media Bus Toaster or other PCMCIA
+ SCSI adapter based on the Symbios 53c500 controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sym53c500_cs.
+
+endmenu
diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
new file mode 100644
index 000000000000..eca379059db6
--- /dev/null
+++ b/drivers/scsi/pcmcia/Makefile
@@ -0,0 +1,13 @@
+
+EXTRA_CFLAGS += -Idrivers/scsi
+
+# 16-bit client drivers
+obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o
+obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o
+obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o
+obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
+obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
+
+aha152x_cs-objs := aha152x_stub.o aha152x_core.o
+fdomain_cs-objs := fdomain_stub.o fdomain_core.o
+qlogic_cs-objs := qlogic_stub.o
diff --git a/drivers/scsi/pcmcia/aha152x_core.c b/drivers/scsi/pcmcia/aha152x_core.c
new file mode 100644
index 000000000000..dba3716511c5
--- /dev/null
+++ b/drivers/scsi/pcmcia/aha152x_core.c
@@ -0,0 +1,3 @@
+#define PCMCIA 1
+#define AHA152X_STAT 1
+#include "aha152x.c"
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
new file mode 100644
index 000000000000..e60b4c0a8427
--- /dev/null
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -0,0 +1,343 @@
+/*======================================================================
+
+ A driver for Adaptec AHA152X-compatible PCMCIA SCSI cards.
+
+ This driver supports the Adaptec AHA-1460, the New Media Bus
+ Toaster, and the New Media Toast & Jam.
+
+ aha152x_cs.c 1.54 2000/06/12 21:27:25
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aha152x.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"aha152x_cs.c 1.54 2000/06/12 21:27:25 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* SCSI bus setup options */
+static int host_id = 7;
+static int reconnect = 1;
+static int parity = 1;
+static int synchronous = 1;
+static int reset_delay = 100;
+static int ext_trans = 0;
+
+module_param(host_id, int, 0);
+module_param(reconnect, int, 0);
+module_param(parity, int, 0);
+module_param(synchronous, int, 0);
+module_param(reset_delay, int, 0);
+module_param(ext_trans, int, 0);
+
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+} scsi_info_t;
+
+static void aha152x_release_cs(dev_link_t *link);
+static int aha152x_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static dev_link_t *aha152x_attach(void);
+static void aha152x_detach(dev_link_t *);
+
+static dev_link_t *dev_list;
+static dev_info_t dev_info = "aha152x_cs";
+
+static dev_link_t *aha152x_attach(void)
+{
+ scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int ret;
+
+ DEBUG(0, "aha152x_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link; link->priv = info;
+
+ link->io.NumPorts1 = 0x20;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.event_handler = &aha152x_event;
+ client_reg.EventMask =
+ CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ aha152x_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* aha152x_attach */
+
+/*====================================================================*/
+
+static void aha152x_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "aha152x_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ aha152x_release_cs(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ kfree(link->priv);
+
+} /* aha152x_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void aha152x_config_cs(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ scsi_info_t *info = link->priv;
+ struct aha152x_setup s;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ u_char tuple_data[64];
+ struct Scsi_Host *host;
+
+ DEBUG(0, "aha152x_config(0x%p)\n", link);
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.TupleData = tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ /* For New Media T&J, look for a SCSI window */
+ if (parse.cftable_entry.io.win[0].len >= 0x20)
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ else if ((parse.cftable_entry.io.nwin > 1) &&
+ (parse.cftable_entry.io.win[1].len >= 0x20))
+ link->io.BasePort1 = parse.cftable_entry.io.win[1].base;
+ if ((parse.cftable_entry.io.nwin > 0) &&
+ (link->io.BasePort1 < 0xffff)) {
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS) break;
+ }
+ next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /* Set configuration options for the aha152x driver */
+ memset(&s, 0, sizeof(s));
+ s.conf = "PCMCIA setup";
+ s.io_port = link->io.BasePort1;
+ s.irq = link->irq.AssignedIRQ;
+ s.scsiid = host_id;
+ s.reconnect = reconnect;
+ s.parity = parity;
+ s.synchronous = synchronous;
+ s.delay = reset_delay;
+ if (ext_trans)
+ s.ext_trans = ext_trans;
+
+ host = aha152x_probe_one(&s);
+ if (host == NULL) {
+ printk(KERN_INFO "aha152x_cs: no SCSI devices found\n");
+ goto cs_failed;
+ }
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ aha152x_release_cs(link);
+ return;
+}
+
+static void aha152x_release_cs(dev_link_t *link)
+{
+ scsi_info_t *info = link->priv;
+
+ aha152x_release(info->host);
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+}
+
+static int aha152x_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ scsi_info_t *info = link->priv;
+
+ DEBUG(0, "aha152x_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ aha152x_release_cs(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ aha152x_config_cs(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ Scsi_Cmnd tmp;
+ pcmcia_request_configuration(link->handle, &link->conf);
+ tmp.device->host = info->host;
+ aha152x_host_reset(&tmp);
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct pcmcia_driver aha152x_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "aha152x_cs",
+ },
+ .attach = aha152x_attach,
+ .detach = aha152x_detach,
+};
+
+static int __init init_aha152x_cs(void)
+{
+ return pcmcia_register_driver(&aha152x_cs_driver);
+}
+
+static void __exit exit_aha152x_cs(void)
+{
+ pcmcia_unregister_driver(&aha152x_cs_driver);
+ BUG_ON(dev_list != NULL);
+}
+
+module_init(init_aha152x_cs);
+module_exit(exit_aha152x_cs);
+
diff --git a/drivers/scsi/pcmcia/fdomain_core.c b/drivers/scsi/pcmcia/fdomain_core.c
new file mode 100644
index 000000000000..a48913791868
--- /dev/null
+++ b/drivers/scsi/pcmcia/fdomain_core.c
@@ -0,0 +1,2 @@
+#define PCMCIA 1
+#include "fdomain.c"
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
new file mode 100644
index 000000000000..3df7bc72e354
--- /dev/null
+++ b/drivers/scsi/pcmcia/fdomain_stub.c
@@ -0,0 +1,323 @@
+/*======================================================================
+
+ A driver for Future Domain-compatible PCMCIA SCSI cards
+
+ fdomain_cs.c 1.47 2001/10/13 00:08:52
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL"), in
+ which case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "fdomain.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+} scsi_info_t;
+
+
+static void fdomain_release(dev_link_t *link);
+static int fdomain_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static dev_link_t *fdomain_attach(void);
+static void fdomain_detach(dev_link_t *);
+
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "fdomain_cs";
+
+static dev_link_t *fdomain_attach(void)
+{
+ scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int ret;
+
+ DEBUG(0, "fdomain_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link; link->priv = info;
+ link->io.NumPorts1 = 0x10;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.event_handler = &fdomain_event;
+ client_reg.EventMask =
+ CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ fdomain_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* fdomain_attach */
+
+/*====================================================================*/
+
+static void fdomain_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "fdomain_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ fdomain_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ kfree(link->priv);
+
+} /* fdomain_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void fdomain_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ u_char tuple_data[64];
+ char str[16];
+ struct Scsi_Host *host;
+
+ DEBUG(0, "fdomain_config(0x%p)\n", link);
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.TupleData = tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS) break;
+ next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /* A bad hack... */
+ release_region(link->io.BasePort1, link->io.NumPorts1);
+
+ /* Set configuration options for the fdomain driver */
+ sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
+ fdomain_setup(str);
+
+ host = __fdomain_16x0_detect(&fdomain_driver_template);
+ if (!host) {
+ printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
+ goto cs_failed;
+ }
+
+ scsi_add_host(host, NULL); /* XXX handle failure */
+ scsi_scan_host(host);
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ fdomain_release(link);
+ return;
+
+} /* fdomain_config */
+
+/*====================================================================*/
+
+static void fdomain_release(dev_link_t *link)
+{
+ scsi_info_t *info = link->priv;
+
+ DEBUG(0, "fdomain_release(0x%p)\n", link);
+
+ scsi_remove_host(info->host);
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ scsi_unregister(info->host);
+
+ link->state &= ~DEV_CONFIG;
+}
+
+/*====================================================================*/
+
+static int fdomain_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+
+ DEBUG(1, "fdomain_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ fdomain_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ fdomain_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ fdomain_16x0_bus_reset(NULL);
+ }
+ break;
+ }
+ return 0;
+} /* fdomain_event */
+
+static struct pcmcia_driver fdomain_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "fdomain_cs",
+ },
+ .attach = fdomain_attach,
+ .detach = fdomain_detach,
+};
+
+static int __init init_fdomain_cs(void)
+{
+ return pcmcia_register_driver(&fdomain_cs_driver);
+}
+
+static void __exit exit_fdomain_cs(void)
+{
+ pcmcia_unregister_driver(&fdomain_cs_driver);
+ BUG_ON(dev_list != NULL);
+}
+
+module_init(init_fdomain_cs);
+module_exit(exit_fdomain_cs);
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
new file mode 100644
index 000000000000..496c412c8854
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -0,0 +1,2198 @@
+/*======================================================================
+
+ NinjaSCSI-3 / NinjaSCSI-32Bi PCMCIA SCSI host adapter card driver
+ By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+ Ver.2.8 Support 32bit MMIO mode
+ Support Synchronous Data Transfer Request (SDTR) mode
+ Ver.2.0 Support 32bit PIO mode
+ Ver.1.1.2 Fix for scatter list buffer exceeds
+ Ver.1.1 Support scatter list
+ Ver.0.1 Initial version
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License.
+
+======================================================================*/
+
+/***********************************************************************
+ This driver is for these PCcards.
+
+ I-O DATA PCSC-F (Workbit NinjaSCSI-3)
+ "WBT", "NinjaSCSI-3", "R1.0"
+ I-O DATA CBSC-II (Workbit NinjaSCSI-32Bi in 16bit mode)
+ "IO DATA", "CBSC16 ", "1"
+
+***********************************************************************/
+
+/* $Id: nsp_cs.c,v 1.23 2003/08/18 11:09:19 elca Exp $ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <../drivers/scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_ioctl.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include "nsp_cs.h"
+
+MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>");
+MODULE_DESCRIPTION("WorkBit NinjaSCSI-3 / NinjaSCSI-32Bi(16bit) PCMCIA SCSI host adapter module $Revision: 1.23 $");
+MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+#include "nsp_io.h"
+
+/*====================================================================*/
+/* Parameters that can be set with 'insmod' */
+
+static int nsp_burst_mode = BURST_MEM32;
+module_param(nsp_burst_mode, int, 0);
+MODULE_PARM_DESC(nsp_burst_mode, "Burst transfer mode (0=io8, 1=io32, 2=mem32(default))");
+
+/* Release IO ports after configuration? */
+static int free_ports = 0;
+module_param(free_ports, bool, 0);
+MODULE_PARM_DESC(free_ports, "Release IO ports after configuration? (default: 0 (=no))");
+
+/* /usr/src/linux/drivers/scsi/hosts.h */
+static Scsi_Host_Template nsp_driver_template = {
+ .proc_name = "nsp_cs",
+ .proc_info = nsp_proc_info,
+ .name = "WorkBit NinjaSCSI-3/32Bi(16bit)",
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+ .detect = nsp_detect_old,
+ .release = nsp_release_old,
+#endif
+ .info = nsp_info,
+ .queuecommand = nsp_queuecommand,
+/* .eh_strategy_handler = nsp_eh_strategy,*/
+/* .eh_abort_handler = nsp_eh_abort,*/
+/* .eh_device_reset_handler = nsp_eh_device_reset,*/
+ .eh_bus_reset_handler = nsp_eh_bus_reset,
+ .eh_host_reset_handler = nsp_eh_host_reset,
+ .can_queue = 1,
+ .this_id = NSP_INITIATOR_ID,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2))
+ .use_new_eh_code = 1,
+#endif
+};
+
+static dev_link_t *dev_list = NULL;
+static dev_info_t dev_info = {"nsp_cs"};
+
+static nsp_hw_data nsp_data_base; /* attach <-> detect glue */
+
+
+
+/*
+ * debug, error print
+ */
+#ifndef NSP_DEBUG
+# define NSP_DEBUG_MASK 0x000000
+# define nsp_msg(type, args...) nsp_cs_message("", 0, (type), args)
+# define nsp_dbg(mask, args...) /* */
+#else
+# define NSP_DEBUG_MASK 0xffffff
+# define nsp_msg(type, args...) \
+ nsp_cs_message (__FUNCTION__, __LINE__, (type), args)
+# define nsp_dbg(mask, args...) \
+ nsp_cs_dmessage(__FUNCTION__, __LINE__, (mask), args)
+#endif
+
+#define NSP_DEBUG_QUEUECOMMAND BIT(0)
+#define NSP_DEBUG_REGISTER BIT(1)
+#define NSP_DEBUG_AUTOSCSI BIT(2)
+#define NSP_DEBUG_INTR BIT(3)
+#define NSP_DEBUG_SGLIST BIT(4)
+#define NSP_DEBUG_BUSFREE BIT(5)
+#define NSP_DEBUG_CDB_CONTENTS BIT(6)
+#define NSP_DEBUG_RESELECTION BIT(7)
+#define NSP_DEBUG_MSGINOCCUR BIT(8)
+#define NSP_DEBUG_EEPROM BIT(9)
+#define NSP_DEBUG_MSGOUTOCCUR BIT(10)
+#define NSP_DEBUG_BUSRESET BIT(11)
+#define NSP_DEBUG_RESTART BIT(12)
+#define NSP_DEBUG_SYNC BIT(13)
+#define NSP_DEBUG_WAIT BIT(14)
+#define NSP_DEBUG_TARGETFLAG BIT(15)
+#define NSP_DEBUG_PROC BIT(16)
+#define NSP_DEBUG_INIT BIT(17)
+#define NSP_DEBUG_DATA_IO BIT(18)
+#define NSP_SPECIAL_PRINT_REGISTER BIT(20)
+
+#define NSP_DEBUG_BUF_LEN 150
+
+static void nsp_cs_message(const char *func, int line, char *type, char *fmt, ...)
+{
+ va_list args;
+ char buf[NSP_DEBUG_BUF_LEN];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+#ifndef NSP_DEBUG
+ printk("%snsp_cs: %s\n", type, buf);
+#else
+ printk("%snsp_cs: %s (%d): %s\n", type, func, line, buf);
+#endif
+}
+
+#ifdef NSP_DEBUG
+static void nsp_cs_dmessage(const char *func, int line, int mask, char *fmt, ...)
+{
+ va_list args;
+ char buf[NSP_DEBUG_BUF_LEN];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ if (mask & NSP_DEBUG_MASK) {
+ printk("nsp_cs-debug: 0x%x %s (%d): %s\n", mask, func, line, buf);
+ }
+}
+#endif
+
+/***********************************************************/
+
+/*====================================================
+ * Clenaup parameters and call done() functions.
+ * You must be set SCpnt->result before call this function.
+ */
+static void nsp_scsi_done(Scsi_Cmnd *SCpnt)
+{
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+ data->CurrentSC = NULL;
+
+ SCpnt->scsi_done(SCpnt);
+}
+
+static int nsp_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+#ifdef NSP_DEBUG
+ /*unsigned int host_id = SCpnt->device->host->this_id;*/
+ /*unsigned int base = SCpnt->device->host->io_port;*/
+ unsigned char target = SCpnt->device->id;
+#endif
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+ nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "SCpnt=0x%p target=%d lun=%d buff=0x%p bufflen=%d use_sg=%d",
+ SCpnt, target, SCpnt->device->lun, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->use_sg);
+ //nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "before CurrentSC=0x%p", data->CurrentSC);
+
+ SCpnt->scsi_done = done;
+
+ if (data->CurrentSC != NULL) {
+ nsp_msg(KERN_DEBUG, "CurrentSC!=NULL this can't be happen");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ nsp_scsi_done(SCpnt);
+ return 0;
+ }
+
+#if 0
+ /* XXX: pcmcia-cs generates SCSI command with "scsi_info" utility.
+ This makes kernel crash when suspending... */
+ if (data->ScsiInfo->stop != 0) {
+ nsp_msg(KERN_INFO, "suspending device. reject command.");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ nsp_scsi_done(SCpnt);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+#endif
+
+ show_command(SCpnt);
+
+ data->CurrentSC = SCpnt;
+
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->SCp.have_data_in = IO_UNKNOWN;
+ SCpnt->SCp.sent_command = 0;
+ SCpnt->SCp.phase = PH_UNDETERMINED;
+ SCpnt->resid = SCpnt->request_bufflen;
+
+ /* setup scratch area
+ SCp.ptr : buffer pointer
+ SCp.this_residual : buffer length
+ SCp.buffer : next buffer
+ SCp.buffers_residual : left buffers in list
+ SCp.phase : current state of the command */
+ if (SCpnt->use_sg) {
+ SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer;
+ SCpnt->SCp.ptr = BUFFER_ADDR;
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+ } else {
+ SCpnt->SCp.ptr = (char *) SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+ }
+
+ if (nsphw_start_selection(SCpnt) == FALSE) {
+ nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "selection fail");
+ SCpnt->result = DID_BUS_BUSY << 16;
+ nsp_scsi_done(SCpnt);
+ return 0;
+ }
+
+
+ //nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "out");
+#ifdef NSP_DEBUG
+ data->CmdId++;
+#endif
+ return 0;
+}
+
+/*
+ * setup PIO FIFO transfer mode and enable/disable to data out
+ */
+static void nsp_setup_fifo(nsp_hw_data *data, int enabled)
+{
+ unsigned int base = data->BaseAddress;
+ unsigned char transfer_mode_reg;
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "enabled=%d", enabled);
+
+ if (enabled != FALSE) {
+ transfer_mode_reg = TRANSFER_GO | BRAIND;
+ } else {
+ transfer_mode_reg = 0;
+ }
+
+ transfer_mode_reg |= data->TransferMode;
+
+ nsp_index_write(base, TRANSFERMODE, transfer_mode_reg);
+}
+
+static void nsphw_init_sync(nsp_hw_data *data)
+{
+ sync_data tmp_sync = { .SyncNegotiation = SYNC_NOT_YET,
+ .SyncPeriod = 0,
+ .SyncOffset = 0
+ };
+ int i;
+
+ /* setup sync data */
+ for ( i = 0; i < ARRAY_SIZE(data->Sync); i++ ) {
+ data->Sync[i] = tmp_sync;
+ }
+}
+
+/*
+ * Initialize Ninja hardware
+ */
+static int nsphw_init(nsp_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+
+ nsp_dbg(NSP_DEBUG_INIT, "in base=0x%x", base);
+
+ data->ScsiClockDiv = CLOCK_40M | FAST_20;
+ data->CurrentSC = NULL;
+ data->FifoCount = 0;
+ data->TransferMode = MODE_IO8;
+
+ nsphw_init_sync(data);
+
+ /* block all interrupts */
+ nsp_write(base, IRQCONTROL, IRQCONTROL_ALLMASK);
+
+ /* setup SCSI interface */
+ nsp_write(base, IFSELECT, IF_IFSEL);
+
+ nsp_index_write(base, SCSIIRQMODE, 0);
+
+ nsp_index_write(base, TRANSFERMODE, MODE_IO8);
+ nsp_index_write(base, CLOCKDIV, data->ScsiClockDiv);
+
+ nsp_index_write(base, PARITYCTRL, 0);
+ nsp_index_write(base, POINTERCLR, POINTER_CLEAR |
+ ACK_COUNTER_CLEAR |
+ REQ_COUNTER_CLEAR |
+ HOST_COUNTER_CLEAR);
+
+ /* setup fifo asic */
+ nsp_write(base, IFSELECT, IF_REGSEL);
+ nsp_index_write(base, TERMPWRCTRL, 0);
+ if ((nsp_index_read(base, OTHERCONTROL) & TPWR_SENSE) == 0) {
+ nsp_msg(KERN_INFO, "terminator power on");
+ nsp_index_write(base, TERMPWRCTRL, POWER_ON);
+ }
+
+ nsp_index_write(base, TIMERCOUNT, 0);
+ nsp_index_write(base, TIMERCOUNT, 0); /* requires 2 times!! */
+
+ nsp_index_write(base, SYNCREG, 0);
+ nsp_index_write(base, ACKWIDTH, 0);
+
+ /* enable interrupts and ack them */
+ nsp_index_write(base, SCSIIRQMODE, SCSI_PHASE_CHANGE_EI |
+ RESELECT_EI |
+ SCSI_RESET_IRQ_EI );
+ nsp_write(base, IRQCONTROL, IRQCONTROL_ALLCLEAR);
+
+ nsp_setup_fifo(data, FALSE);
+
+ return TRUE;
+}
+
+/*
+ * Start selection phase
+ */
+static int nsphw_start_selection(Scsi_Cmnd *SCpnt)
+{
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned char target = SCpnt->device->id;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ int time_out;
+ unsigned char phase, arbit;
+
+ //nsp_dbg(NSP_DEBUG_RESELECTION, "in");
+
+ phase = nsp_index_read(base, SCSIBUSMON);
+ if(phase != BUSMON_BUS_FREE) {
+ //nsp_dbg(NSP_DEBUG_RESELECTION, "bus busy");
+ return FALSE;
+ }
+
+ /* start arbitration */
+ //nsp_dbg(NSP_DEBUG_RESELECTION, "start arbit");
+ SCpnt->SCp.phase = PH_ARBSTART;
+ nsp_index_write(base, SETARBIT, ARBIT_GO);
+
+ time_out = 1000;
+ do {
+ /* XXX: what a stupid chip! */
+ arbit = nsp_index_read(base, ARBITSTATUS);
+ //nsp_dbg(NSP_DEBUG_RESELECTION, "arbit=%d, wait_count=%d", arbit, wait_count);
+ udelay(1); /* hold 1.2us */
+ } while((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&
+ (time_out-- != 0));
+
+ if (!(arbit & ARBIT_WIN)) {
+ //nsp_dbg(NSP_DEBUG_RESELECTION, "arbit fail");
+ nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR);
+ return FALSE;
+ }
+
+ /* assert select line */
+ //nsp_dbg(NSP_DEBUG_RESELECTION, "assert SEL line");
+ SCpnt->SCp.phase = PH_SELSTART;
+ udelay(3); /* wait 2.4us */
+ nsp_index_write(base, SCSIDATALATCH, BIT(host_id) | BIT(target));
+ nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_ATN);
+ udelay(2); /* wait >1.2us */
+ nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_DATAOUT_ENB | SCSI_ATN);
+ nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR);
+ /*udelay(1);*/ /* wait >90ns */
+ nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_DATAOUT_ENB | SCSI_ATN);
+
+ /* check selection timeout */
+ nsp_start_timer(SCpnt, 1000/51);
+ data->SelectionTimeOut = 1;
+
+ return TRUE;
+}
+
+struct nsp_sync_table {
+ unsigned int min_period;
+ unsigned int max_period;
+ unsigned int chip_period;
+ unsigned int ack_width;
+};
+
+static struct nsp_sync_table nsp_sync_table_40M[] = {
+ {0x0c, 0x0c, 0x1, 0}, /* 20MB 50ns*/
+ {0x19, 0x19, 0x3, 1}, /* 10MB 100ns*/
+ {0x1a, 0x25, 0x5, 2}, /* 7.5MB 150ns*/
+ {0x26, 0x32, 0x7, 3}, /* 5MB 200ns*/
+ { 0, 0, 0, 0},
+};
+
+static struct nsp_sync_table nsp_sync_table_20M[] = {
+ {0x19, 0x19, 0x1, 0}, /* 10MB 100ns*/
+ {0x1a, 0x25, 0x2, 0}, /* 7.5MB 150ns*/
+ {0x26, 0x32, 0x3, 1}, /* 5MB 200ns*/
+ { 0, 0, 0, 0},
+};
+
+/*
+ * setup synchronous data transfer mode
+ */
+static int nsp_analyze_sdtr(Scsi_Cmnd *SCpnt)
+{
+ unsigned char target = SCpnt->device->id;
+// unsigned char lun = SCpnt->device->lun;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ sync_data *sync = &(data->Sync[target]);
+ struct nsp_sync_table *sync_table;
+ unsigned int period, offset;
+ int i;
+
+
+ nsp_dbg(NSP_DEBUG_SYNC, "in");
+
+ period = sync->SyncPeriod;
+ offset = sync->SyncOffset;
+
+ nsp_dbg(NSP_DEBUG_SYNC, "period=0x%x, offset=0x%x", period, offset);
+
+ if ((data->ScsiClockDiv & (BIT(0)|BIT(1))) == CLOCK_20M) {
+ sync_table = nsp_sync_table_20M;
+ } else {
+ sync_table = nsp_sync_table_40M;
+ }
+
+ for ( i = 0; sync_table->max_period != 0; i++, sync_table++) {
+ if ( period >= sync_table->min_period &&
+ period <= sync_table->max_period ) {
+ break;
+ }
+ }
+
+ if (period != 0 && sync_table->max_period == 0) {
+ /*
+ * No proper period/offset found
+ */
+ nsp_dbg(NSP_DEBUG_SYNC, "no proper period/offset");
+
+ sync->SyncPeriod = 0;
+ sync->SyncOffset = 0;
+ sync->SyncRegister = 0;
+ sync->AckWidth = 0;
+
+ return FALSE;
+ }
+
+ sync->SyncRegister = (sync_table->chip_period << SYNCREG_PERIOD_SHIFT) |
+ (offset & SYNCREG_OFFSET_MASK);
+ sync->AckWidth = sync_table->ack_width;
+
+ nsp_dbg(NSP_DEBUG_SYNC, "sync_reg=0x%x, ack_width=0x%x", sync->SyncRegister, sync->AckWidth);
+
+ return TRUE;
+}
+
+
+/*
+ * start ninja hardware timer
+ */
+static void nsp_start_timer(Scsi_Cmnd *SCpnt, int time)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+ //nsp_dbg(NSP_DEBUG_INTR, "in SCpnt=0x%p, time=%d", SCpnt, time);
+ data->TimerCount = time;
+ nsp_index_write(base, TIMERCOUNT, time);
+}
+
+/*
+ * wait for bus phase change
+ */
+static int nsp_negate_signal(Scsi_Cmnd *SCpnt, unsigned char mask, char *str)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned char reg;
+ int time_out;
+
+ //nsp_dbg(NSP_DEBUG_INTR, "in");
+
+ time_out = 100;
+
+ do {
+ reg = nsp_index_read(base, SCSIBUSMON);
+ if (reg == 0xff) {
+ break;
+ }
+ } while ((time_out-- != 0) && (reg & mask) != 0);
+
+ if (time_out == 0) {
+ nsp_msg(KERN_DEBUG, " %s signal off timeut", str);
+ }
+
+ return 0;
+}
+
+/*
+ * expect Ninja Irq
+ */
+static int nsp_expect_signal(Scsi_Cmnd *SCpnt,
+ unsigned char current_phase,
+ unsigned char mask)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ int time_out;
+ unsigned char phase, i_src;
+
+ //nsp_dbg(NSP_DEBUG_INTR, "current_phase=0x%x, mask=0x%x", current_phase, mask);
+
+ time_out = 100;
+ do {
+ phase = nsp_index_read(base, SCSIBUSMON);
+ if (phase == 0xff) {
+ //nsp_dbg(NSP_DEBUG_INTR, "ret -1");
+ return -1;
+ }
+ i_src = nsp_read(base, IRQSTATUS);
+ if (i_src & IRQSTATUS_SCSI) {
+ //nsp_dbg(NSP_DEBUG_INTR, "ret 0 found scsi signal");
+ return 0;
+ }
+ if ((phase & mask) != 0 && (phase & BUSMON_PHASE_MASK) == current_phase) {
+ //nsp_dbg(NSP_DEBUG_INTR, "ret 1 phase=0x%x", phase);
+ return 1;
+ }
+ } while(time_out-- != 0);
+
+ //nsp_dbg(NSP_DEBUG_INTR, "timeout");
+ return -1;
+}
+
+/*
+ * transfer SCSI message
+ */
+static int nsp_xfer(Scsi_Cmnd *SCpnt, int phase)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ char *buf = data->MsgBuffer;
+ int len = min(MSGBUF_SIZE, data->MsgLen);
+ int ptr;
+ int ret;
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "in");
+ for (ptr = 0; len > 0; len--, ptr++) {
+
+ ret = nsp_expect_signal(SCpnt, phase, BUSMON_REQ);
+ if (ret <= 0) {
+ nsp_dbg(NSP_DEBUG_DATA_IO, "xfer quit");
+ return 0;
+ }
+
+ /* if last byte, negate ATN */
+ if (len == 1 && SCpnt->SCp.phase == PH_MSG_OUT) {
+ nsp_index_write(base, SCSIBUSCTRL, AUTODIRECTION | ACKENB);
+ }
+
+ /* read & write message */
+ if (phase & BUSMON_IO) {
+ nsp_dbg(NSP_DEBUG_DATA_IO, "read msg");
+ buf[ptr] = nsp_index_read(base, SCSIDATAWITHACK);
+ } else {
+ nsp_dbg(NSP_DEBUG_DATA_IO, "write msg");
+ nsp_index_write(base, SCSIDATAWITHACK, buf[ptr]);
+ }
+ nsp_negate_signal(SCpnt, BUSMON_ACK, "xfer<ack>");
+
+ }
+ return len;
+}
+
+/*
+ * get extra SCSI data from fifo
+ */
+static int nsp_dataphase_bypass(Scsi_Cmnd *SCpnt)
+{
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int count;
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "in");
+
+ if (SCpnt->SCp.have_data_in != IO_IN) {
+ return 0;
+ }
+
+ count = nsp_fifo_count(SCpnt);
+ if (data->FifoCount == count) {
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "not use bypass quirk");
+ return 0;
+ }
+
+ /*
+ * XXX: NSP_QUIRK
+ * data phase skip only occures in case of SCSI_LOW_READ
+ */
+ nsp_dbg(NSP_DEBUG_DATA_IO, "use bypass quirk");
+ SCpnt->SCp.phase = PH_DATA;
+ nsp_pio_read(SCpnt);
+ nsp_setup_fifo(data, FALSE);
+
+ return 0;
+}
+
+/*
+ * accept reselection
+ */
+static int nsp_reselected(Scsi_Cmnd *SCpnt)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ //nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ unsigned char bus_reg;
+ unsigned char id_reg, tmp;
+ int target;
+
+ nsp_dbg(NSP_DEBUG_RESELECTION, "in");
+
+ id_reg = nsp_index_read(base, RESELECTID);
+ tmp = id_reg & (~BIT(host_id));
+ target = 0;
+ while(tmp != 0) {
+ if (tmp & BIT(0)) {
+ break;
+ }
+ tmp >>= 1;
+ target++;
+ }
+
+ if (SCpnt->device->id != target) {
+ nsp_msg(KERN_ERR, "XXX: reselect ID must be %d in this implementation.", target);
+ }
+
+ nsp_negate_signal(SCpnt, BUSMON_SEL, "reselect<SEL>");
+
+ nsp_nexus(SCpnt);
+ bus_reg = nsp_index_read(base, SCSIBUSCTRL) & ~(SCSI_BSY | SCSI_ATN);
+ nsp_index_write(base, SCSIBUSCTRL, bus_reg);
+ nsp_index_write(base, SCSIBUSCTRL, bus_reg | AUTODIRECTION | ACKENB);
+
+ return TRUE;
+}
+
+/*
+ * count how many data transferd
+ */
+static int nsp_fifo_count(Scsi_Cmnd *SCpnt)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned int count;
+ unsigned int l, m, h, dummy;
+
+ nsp_index_write(base, POINTERCLR, POINTER_CLEAR | ACK_COUNTER);
+
+ l = nsp_index_read(base, TRANSFERCOUNT);
+ m = nsp_index_read(base, TRANSFERCOUNT);
+ h = nsp_index_read(base, TRANSFERCOUNT);
+ dummy = nsp_index_read(base, TRANSFERCOUNT); /* required this! */
+
+ count = (h << 16) | (m << 8) | (l << 0);
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "count=0x%x", count);
+
+ return count;
+}
+
+/* fifo size */
+#define RFIFO_CRIT 64
+#define WFIFO_CRIT 64
+
+/*
+ * read data in DATA IN phase
+ */
+static void nsp_pio_read(Scsi_Cmnd *SCpnt)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned long mmio_base = SCpnt->device->host->base;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ long time_out;
+ int ocount, res;
+ unsigned char stat, fifo_stat;
+
+ ocount = data->FifoCount;
+
+ nsp_dbg(NSP_DEBUG_DATA_IO, "in SCpnt=0x%p resid=%d ocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d",
+ SCpnt, SCpnt->resid, ocount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual, SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual);
+
+ time_out = 1000;
+
+ while ((time_out-- != 0) &&
+ (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0 ) ) {
+
+ stat = nsp_index_read(base, SCSIBUSMON);
+ stat &= BUSMON_PHASE_MASK;
+
+
+ res = nsp_fifo_count(SCpnt) - ocount;
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x ocount=0x%x res=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount, res);
+ if (res == 0) { /* if some data avilable ? */
+ if (stat == BUSPHASE_DATA_IN) { /* phase changed? */
+ //nsp_dbg(NSP_DEBUG_DATA_IO, " wait for data this=%d", SCpnt->SCp.this_residual);
+ continue;
+ } else {
+ nsp_dbg(NSP_DEBUG_DATA_IO, "phase changed stat=0x%x", stat);
+ break;
+ }
+ }
+
+ fifo_stat = nsp_read(base, FIFOSTATUS);
+ if ((fifo_stat & FIFOSTATUS_FULL_EMPTY) == 0 &&
+ stat == BUSPHASE_DATA_IN) {
+ continue;
+ }
+
+ res = min(res, SCpnt->SCp.this_residual);
+
+ switch (data->TransferMode) {
+ case MODE_IO32:
+ res &= ~(BIT(1)|BIT(0)); /* align 4 */
+ nsp_fifo32_read(base, SCpnt->SCp.ptr, res >> 2);
+ break;
+ case MODE_IO8:
+ nsp_fifo8_read (base, SCpnt->SCp.ptr, res );
+ break;
+
+ case MODE_MEM32:
+ res &= ~(BIT(1)|BIT(0)); /* align 4 */
+ nsp_mmio_fifo32_read(mmio_base, SCpnt->SCp.ptr, res >> 2);
+ break;
+
+ default:
+ nsp_dbg(NSP_DEBUG_DATA_IO, "unknown read mode");
+ return;
+ }
+
+ SCpnt->resid -= res;
+ SCpnt->SCp.ptr += res;
+ SCpnt->SCp.this_residual -= res;
+ ocount += res;
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this_residual=0x%x ocount=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount);
+
+ /* go to next scatter list if available */
+ if (SCpnt->SCp.this_residual == 0 &&
+ SCpnt->SCp.buffers_residual != 0 ) {
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "scatterlist next timeout=%d", time_out);
+ SCpnt->SCp.buffers_residual--;
+ SCpnt->SCp.buffer++;
+ SCpnt->SCp.ptr = BUFFER_ADDR;
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ time_out = 1000;
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "page: 0x%p, off: 0x%x", SCpnt->SCp.buffer->page, SCpnt->SCp.buffer->offset);
+ }
+ }
+
+ data->FifoCount = ocount;
+
+ if (time_out == 0) {
+ nsp_msg(KERN_DEBUG, "pio read timeout resid=%d this_residual=%d buffers_residual=%d",
+ SCpnt->resid, SCpnt->SCp.this_residual, SCpnt->SCp.buffers_residual);
+ }
+ nsp_dbg(NSP_DEBUG_DATA_IO, "read ocount=0x%x", ocount);
+ nsp_dbg(NSP_DEBUG_DATA_IO, "r cmd=%d resid=0x%x\n", data->CmdId, SCpnt->resid);
+}
+
+/*
+ * write data in DATA OUT phase
+ */
+static void nsp_pio_write(Scsi_Cmnd *SCpnt)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned long mmio_base = SCpnt->device->host->base;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ int time_out;
+ int ocount, res;
+ unsigned char stat;
+
+ ocount = data->FifoCount;
+
+ nsp_dbg(NSP_DEBUG_DATA_IO, "in fifocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d resid=0x%x",
+ data->FifoCount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual, SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual, SCpnt->resid);
+
+ time_out = 1000;
+
+ while ((time_out-- != 0) &&
+ (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0)) {
+ stat = nsp_index_read(base, SCSIBUSMON);
+ stat &= BUSMON_PHASE_MASK;
+
+ if (stat != BUSPHASE_DATA_OUT) {
+ res = ocount - nsp_fifo_count(SCpnt);
+
+ nsp_dbg(NSP_DEBUG_DATA_IO, "phase changed stat=0x%x, res=%d\n", stat, res);
+ /* Put back pointer */
+ SCpnt->resid += res;
+ SCpnt->SCp.ptr -= res;
+ SCpnt->SCp.this_residual += res;
+ ocount -= res;
+
+ break;
+ }
+
+ res = ocount - nsp_fifo_count(SCpnt);
+ if (res > 0) { /* write all data? */
+ nsp_dbg(NSP_DEBUG_DATA_IO, "wait for all data out. ocount=0x%x res=%d", ocount, res);
+ continue;
+ }
+
+ res = min(SCpnt->SCp.this_residual, WFIFO_CRIT);
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x res=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, res);
+ switch (data->TransferMode) {
+ case MODE_IO32:
+ res &= ~(BIT(1)|BIT(0)); /* align 4 */
+ nsp_fifo32_write(base, SCpnt->SCp.ptr, res >> 2);
+ break;
+ case MODE_IO8:
+ nsp_fifo8_write (base, SCpnt->SCp.ptr, res );
+ break;
+
+ case MODE_MEM32:
+ res &= ~(BIT(1)|BIT(0)); /* align 4 */
+ nsp_mmio_fifo32_write(mmio_base, SCpnt->SCp.ptr, res >> 2);
+ break;
+
+ default:
+ nsp_dbg(NSP_DEBUG_DATA_IO, "unknown write mode");
+ break;
+ }
+
+ SCpnt->resid -= res;
+ SCpnt->SCp.ptr += res;
+ SCpnt->SCp.this_residual -= res;
+ ocount += res;
+
+ /* go to next scatter list if available */
+ if (SCpnt->SCp.this_residual == 0 &&
+ SCpnt->SCp.buffers_residual != 0 ) {
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "scatterlist next");
+ SCpnt->SCp.buffers_residual--;
+ SCpnt->SCp.buffer++;
+ SCpnt->SCp.ptr = BUFFER_ADDR;
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ time_out = 1000;
+ }
+ }
+
+ data->FifoCount = ocount;
+
+ if (time_out == 0) {
+ nsp_msg(KERN_DEBUG, "pio write timeout resid=0x%x", SCpnt->resid);
+ }
+ nsp_dbg(NSP_DEBUG_DATA_IO, "write ocount=0x%x", ocount);
+ nsp_dbg(NSP_DEBUG_DATA_IO, "w cmd=%d resid=0x%x\n", data->CmdId, SCpnt->resid);
+}
+#undef RFIFO_CRIT
+#undef WFIFO_CRIT
+
+/*
+ * setup synchronous/asynchronous data transfer mode
+ */
+static int nsp_nexus(Scsi_Cmnd *SCpnt)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned char target = SCpnt->device->id;
+// unsigned char lun = SCpnt->device->lun;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ sync_data *sync = &(data->Sync[target]);
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "in SCpnt=0x%p", SCpnt);
+
+ /* setup synch transfer registers */
+ nsp_index_write(base, SYNCREG, sync->SyncRegister);
+ nsp_index_write(base, ACKWIDTH, sync->AckWidth);
+
+ if (SCpnt->use_sg == 0 ||
+ SCpnt->resid % 4 != 0 ||
+ SCpnt->resid <= PAGE_SIZE ) {
+ data->TransferMode = MODE_IO8;
+ } else if (nsp_burst_mode == BURST_MEM32) {
+ data->TransferMode = MODE_MEM32;
+ } else if (nsp_burst_mode == BURST_IO32) {
+ data->TransferMode = MODE_IO32;
+ } else {
+ data->TransferMode = MODE_IO8;
+ }
+
+ /* setup pdma fifo */
+ nsp_setup_fifo(data, TRUE);
+
+ /* clear ack counter */
+ data->FifoCount = 0;
+ nsp_index_write(base, POINTERCLR, POINTER_CLEAR |
+ ACK_COUNTER_CLEAR |
+ REQ_COUNTER_CLEAR |
+ HOST_COUNTER_CLEAR);
+
+ return 0;
+}
+
+#include "nsp_message.c"
+/*
+ * interrupt handler
+ */
+static irqreturn_t nspintr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int base;
+ unsigned char irq_status, irq_phase, phase;
+ Scsi_Cmnd *tmpSC;
+ unsigned char target, lun;
+ unsigned int *sync_neg;
+ int i, tmp;
+ nsp_hw_data *data;
+
+
+ //nsp_dbg(NSP_DEBUG_INTR, "dev_id=0x%p", dev_id);
+ //nsp_dbg(NSP_DEBUG_INTR, "host=0x%p", ((scsi_info_t *)dev_id)->host);
+
+ if ( dev_id != NULL &&
+ ((scsi_info_t *)dev_id)->host != NULL ) {
+ scsi_info_t *info = (scsi_info_t *)dev_id;
+
+ data = (nsp_hw_data *)info->host->hostdata;
+ } else {
+ nsp_dbg(NSP_DEBUG_INTR, "host data wrong");
+ return IRQ_NONE;
+ }
+
+ //nsp_dbg(NSP_DEBUG_INTR, "&nsp_data_base=0x%p, dev_id=0x%p", &nsp_data_base, dev_id);
+
+ base = data->BaseAddress;
+ //nsp_dbg(NSP_DEBUG_INTR, "base=0x%x", base);
+
+ /*
+ * interrupt check
+ */
+ nsp_write(base, IRQCONTROL, IRQCONTROL_IRQDISABLE);
+ irq_status = nsp_read(base, IRQSTATUS);
+ //nsp_dbg(NSP_DEBUG_INTR, "irq_status=0x%x", irq_status);
+ if ((irq_status == 0xff) || ((irq_status & IRQSTATUS_MASK) == 0)) {
+ nsp_write(base, IRQCONTROL, 0);
+ //nsp_dbg(NSP_DEBUG_INTR, "no irq/shared irq");
+ return IRQ_NONE;
+ }
+
+ /* XXX: IMPORTANT
+ * Do not read an irq_phase register if no scsi phase interrupt.
+ * Unless, you should lose a scsi phase interrupt.
+ */
+ phase = nsp_index_read(base, SCSIBUSMON);
+ if((irq_status & IRQSTATUS_SCSI) != 0) {
+ irq_phase = nsp_index_read(base, IRQPHASESENCE);
+ } else {
+ irq_phase = 0;
+ }
+
+ //nsp_dbg(NSP_DEBUG_INTR, "irq_phase=0x%x", irq_phase);
+
+ /*
+ * timer interrupt handler (scsi vs timer interrupts)
+ */
+ //nsp_dbg(NSP_DEBUG_INTR, "timercount=%d", data->TimerCount);
+ if (data->TimerCount != 0) {
+ //nsp_dbg(NSP_DEBUG_INTR, "stop timer");
+ nsp_index_write(base, TIMERCOUNT, 0);
+ nsp_index_write(base, TIMERCOUNT, 0);
+ data->TimerCount = 0;
+ }
+
+ if ((irq_status & IRQSTATUS_MASK) == IRQSTATUS_TIMER &&
+ data->SelectionTimeOut == 0) {
+ //nsp_dbg(NSP_DEBUG_INTR, "timer start");
+ nsp_write(base, IRQCONTROL, IRQCONTROL_TIMER_CLEAR);
+ return IRQ_HANDLED;
+ }
+
+ nsp_write(base, IRQCONTROL, IRQCONTROL_TIMER_CLEAR | IRQCONTROL_FIFO_CLEAR);
+
+ if ((irq_status & IRQSTATUS_SCSI) &&
+ (irq_phase & SCSI_RESET_IRQ)) {
+ nsp_msg(KERN_ERR, "bus reset (power off?)");
+
+ nsphw_init(data);
+ nsp_bus_reset(data);
+
+ if(data->CurrentSC != NULL) {
+ tmpSC = data->CurrentSC;
+ tmpSC->result = (DID_RESET << 16) |
+ ((tmpSC->SCp.Message & 0xff) << 8) |
+ ((tmpSC->SCp.Status & 0xff) << 0);
+ nsp_scsi_done(tmpSC);
+ }
+ return IRQ_HANDLED;
+ }
+
+ if (data->CurrentSC == NULL) {
+ nsp_msg(KERN_ERR, "CurrentSC==NULL irq_status=0x%x phase=0x%x irq_phase=0x%x this can't be happen. reset everything", irq_status, phase, irq_phase);
+ nsphw_init(data);
+ nsp_bus_reset(data);
+ return IRQ_HANDLED;
+ }
+
+ tmpSC = data->CurrentSC;
+ target = tmpSC->device->id;
+ lun = tmpSC->device->lun;
+ sync_neg = &(data->Sync[target].SyncNegotiation);
+
+ /*
+ * parse hardware SCSI irq reasons register
+ */
+ if (irq_status & IRQSTATUS_SCSI) {
+ if (irq_phase & RESELECT_IRQ) {
+ nsp_dbg(NSP_DEBUG_INTR, "reselect");
+ nsp_write(base, IRQCONTROL, IRQCONTROL_RESELECT_CLEAR);
+ if (nsp_reselected(tmpSC) != FALSE) {
+ return IRQ_HANDLED;
+ }
+ }
+
+ if ((irq_phase & (PHASE_CHANGE_IRQ | LATCHED_BUS_FREE)) == 0) {
+ return IRQ_HANDLED;
+ }
+ }
+
+ //show_phase(tmpSC);
+
+ switch(tmpSC->SCp.phase) {
+ case PH_SELSTART:
+ // *sync_neg = SYNC_NOT_YET;
+ if ((phase & BUSMON_BSY) == 0) {
+ //nsp_dbg(NSP_DEBUG_INTR, "selection count=%d", data->SelectionTimeOut);
+ if (data->SelectionTimeOut >= NSP_SELTIMEOUT) {
+ nsp_dbg(NSP_DEBUG_INTR, "selection time out");
+ data->SelectionTimeOut = 0;
+ nsp_index_write(base, SCSIBUSCTRL, 0);
+
+ tmpSC->result = DID_TIME_OUT << 16;
+ nsp_scsi_done(tmpSC);
+
+ return IRQ_HANDLED;
+ }
+ data->SelectionTimeOut += 1;
+ nsp_start_timer(tmpSC, 1000/51);
+ return IRQ_HANDLED;
+ }
+
+ /* attention assert */
+ //nsp_dbg(NSP_DEBUG_INTR, "attention assert");
+ data->SelectionTimeOut = 0;
+ tmpSC->SCp.phase = PH_SELECTED;
+ nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN);
+ udelay(1);
+ nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN | AUTODIRECTION | ACKENB);
+ return IRQ_HANDLED;
+
+ break;
+
+ case PH_RESELECT:
+ //nsp_dbg(NSP_DEBUG_INTR, "phase reselect");
+ // *sync_neg = SYNC_NOT_YET;
+ if ((phase & BUSMON_PHASE_MASK) != BUSPHASE_MESSAGE_IN) {
+
+ tmpSC->result = DID_ABORT << 16;
+ nsp_scsi_done(tmpSC);
+ return IRQ_HANDLED;
+ }
+ /* fall thru */
+ default:
+ if ((irq_status & (IRQSTATUS_SCSI | IRQSTATUS_FIFO)) == 0) {
+ return IRQ_HANDLED;
+ }
+ break;
+ }
+
+ /*
+ * SCSI sequencer
+ */
+ //nsp_dbg(NSP_DEBUG_INTR, "start scsi seq");
+
+ /* normal disconnect */
+ if (((tmpSC->SCp.phase == PH_MSG_IN) || (tmpSC->SCp.phase == PH_MSG_OUT)) &&
+ (irq_phase & LATCHED_BUS_FREE) != 0 ) {
+ nsp_dbg(NSP_DEBUG_INTR, "normal disconnect irq_status=0x%x, phase=0x%x, irq_phase=0x%x", irq_status, phase, irq_phase);
+
+ //*sync_neg = SYNC_NOT_YET;
+
+ if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) { /* all command complete and return status */
+ tmpSC->result = (DID_OK << 16) |
+ ((tmpSC->SCp.Message & 0xff) << 8) |
+ ((tmpSC->SCp.Status & 0xff) << 0);
+ nsp_dbg(NSP_DEBUG_INTR, "command complete result=0x%x", tmpSC->result);
+ nsp_scsi_done(tmpSC);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_HANDLED;
+ }
+
+
+ /* check unexpected bus free state */
+ if (phase == 0) {
+ nsp_msg(KERN_DEBUG, "unexpected bus free. irq_status=0x%x, phase=0x%x, irq_phase=0x%x", irq_status, phase, irq_phase);
+
+ *sync_neg = SYNC_NG;
+ tmpSC->result = DID_ERROR << 16;
+ nsp_scsi_done(tmpSC);
+ return IRQ_HANDLED;
+ }
+
+ switch (phase & BUSMON_PHASE_MASK) {
+ case BUSPHASE_COMMAND:
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_COMMAND");
+ if ((phase & BUSMON_REQ) == 0) {
+ nsp_dbg(NSP_DEBUG_INTR, "REQ == 0");
+ return IRQ_HANDLED;
+ }
+
+ tmpSC->SCp.phase = PH_COMMAND;
+
+ nsp_nexus(tmpSC);
+
+ /* write scsi command */
+ nsp_dbg(NSP_DEBUG_INTR, "cmd_len=%d", tmpSC->cmd_len);
+ nsp_index_write(base, COMMANDCTRL, CLEAR_COMMAND_POINTER);
+ for (i = 0; i < tmpSC->cmd_len; i++) {
+ nsp_index_write(base, COMMANDDATA, tmpSC->cmnd[i]);
+ }
+ nsp_index_write(base, COMMANDCTRL, CLEAR_COMMAND_POINTER | AUTO_COMMAND_GO);
+ break;
+
+ case BUSPHASE_DATA_OUT:
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_DATA_OUT");
+
+ tmpSC->SCp.phase = PH_DATA;
+ tmpSC->SCp.have_data_in = IO_OUT;
+
+ nsp_pio_write(tmpSC);
+
+ break;
+
+ case BUSPHASE_DATA_IN:
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_DATA_IN");
+
+ tmpSC->SCp.phase = PH_DATA;
+ tmpSC->SCp.have_data_in = IO_IN;
+
+ nsp_pio_read(tmpSC);
+
+ break;
+
+ case BUSPHASE_STATUS:
+ nsp_dataphase_bypass(tmpSC);
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_STATUS");
+
+ tmpSC->SCp.phase = PH_STATUS;
+
+ tmpSC->SCp.Status = nsp_index_read(base, SCSIDATAWITHACK);
+ nsp_dbg(NSP_DEBUG_INTR, "message=0x%x status=0x%x", tmpSC->SCp.Message, tmpSC->SCp.Status);
+
+ break;
+
+ case BUSPHASE_MESSAGE_OUT:
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_MESSAGE_OUT");
+ if ((phase & BUSMON_REQ) == 0) {
+ goto timer_out;
+ }
+
+ tmpSC->SCp.phase = PH_MSG_OUT;
+
+ //*sync_neg = SYNC_NOT_YET;
+
+ data->MsgLen = i = 0;
+ data->MsgBuffer[i] = IDENTIFY(TRUE, lun); i++;
+
+ if (*sync_neg == SYNC_NOT_YET) {
+ data->Sync[target].SyncPeriod = 0;
+ data->Sync[target].SyncOffset = 0;
+
+ /**/
+ data->MsgBuffer[i] = MSG_EXTENDED; i++;
+ data->MsgBuffer[i] = 3; i++;
+ data->MsgBuffer[i] = MSG_EXT_SDTR; i++;
+ data->MsgBuffer[i] = 0x0c; i++;
+ data->MsgBuffer[i] = 15; i++;
+ /**/
+ }
+ data->MsgLen = i;
+
+ nsp_analyze_sdtr(tmpSC);
+ show_message(data);
+ nsp_message_out(tmpSC);
+ break;
+
+ case BUSPHASE_MESSAGE_IN:
+ nsp_dataphase_bypass(tmpSC);
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_MESSAGE_IN");
+ if ((phase & BUSMON_REQ) == 0) {
+ goto timer_out;
+ }
+
+ tmpSC->SCp.phase = PH_MSG_IN;
+ nsp_message_in(tmpSC);
+
+ /**/
+ if (*sync_neg == SYNC_NOT_YET) {
+ //nsp_dbg(NSP_DEBUG_INTR, "sync target=%d,lun=%d",target,lun);
+
+ if (data->MsgLen >= 5 &&
+ data->MsgBuffer[0] == MSG_EXTENDED &&
+ data->MsgBuffer[1] == 3 &&
+ data->MsgBuffer[2] == MSG_EXT_SDTR ) {
+ data->Sync[target].SyncPeriod = data->MsgBuffer[3];
+ data->Sync[target].SyncOffset = data->MsgBuffer[4];
+ //nsp_dbg(NSP_DEBUG_INTR, "sync ok, %d %d", data->MsgBuffer[3], data->MsgBuffer[4]);
+ *sync_neg = SYNC_OK;
+ } else {
+ data->Sync[target].SyncPeriod = 0;
+ data->Sync[target].SyncOffset = 0;
+ *sync_neg = SYNC_NG;
+ }
+ nsp_analyze_sdtr(tmpSC);
+ }
+ /**/
+
+ /* search last messeage byte */
+ tmp = -1;
+ for (i = 0; i < data->MsgLen; i++) {
+ tmp = data->MsgBuffer[i];
+ if (data->MsgBuffer[i] == MSG_EXTENDED) {
+ i += (1 + data->MsgBuffer[i+1]);
+ }
+ }
+ tmpSC->SCp.Message = tmp;
+
+ nsp_dbg(NSP_DEBUG_INTR, "message=0x%x len=%d", tmpSC->SCp.Message, data->MsgLen);
+ show_message(data);
+
+ break;
+
+ case BUSPHASE_SELECT:
+ default:
+ nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE other");
+
+ break;
+ }
+
+ //nsp_dbg(NSP_DEBUG_INTR, "out");
+ return IRQ_HANDLED;
+
+timer_out:
+ nsp_start_timer(tmpSC, 1000/102);
+ return IRQ_HANDLED;
+}
+
+#ifdef NSP_DEBUG
+#include "nsp_debug.c"
+#endif /* NSP_DEBUG */
+
+/*----------------------------------------------------------------*/
+/* look for ninja3 card and init if found */
+/*----------------------------------------------------------------*/
+static struct Scsi_Host *nsp_detect(Scsi_Host_Template *sht)
+{
+ struct Scsi_Host *host; /* registered host structure */
+ nsp_hw_data *data_b = &nsp_data_base, *data;
+
+ nsp_dbg(NSP_DEBUG_INIT, "this_id=%d", sht->this_id);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ host = scsi_host_alloc(&nsp_driver_template, sizeof(nsp_hw_data));
+#else
+ host = scsi_register(sht, sizeof(nsp_hw_data));
+#endif
+ if (host == NULL) {
+ nsp_dbg(NSP_DEBUG_INIT, "host failed");
+ return NULL;
+ }
+
+ memcpy(host->hostdata, data_b, sizeof(nsp_hw_data));
+ data = (nsp_hw_data *)host->hostdata;
+ data->ScsiInfo->host = host;
+#ifdef NSP_DEBUG
+ data->CmdId = 0;
+#endif
+
+ nsp_dbg(NSP_DEBUG_INIT, "irq=%d,%d", data_b->IrqNumber, ((nsp_hw_data *)host->hostdata)->IrqNumber);
+
+ host->unique_id = data->BaseAddress;
+ host->io_port = data->BaseAddress;
+ host->n_io_port = data->NumAddress;
+ host->irq = data->IrqNumber;
+ host->base = data->MmioAddress;
+
+ spin_lock_init(&(data->Lock));
+
+ snprintf(data->nspinfo,
+ sizeof(data->nspinfo),
+ "NinjaSCSI-3/32Bi Driver $Revision: 1.23 $ IO:0x%04lx-0x%04lx MMIO(virt addr):0x%04lx IRQ:%02d",
+ host->io_port, host->io_port + host->n_io_port - 1,
+ host->base,
+ host->irq);
+ sht->name = data->nspinfo;
+
+ nsp_dbg(NSP_DEBUG_INIT, "end");
+
+
+ return host; /* detect done. */
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+static int nsp_detect_old(Scsi_Host_Template *sht)
+{
+ if (nsp_detect(sht) == NULL) {
+ return 0;
+ } else {
+ //MOD_INC_USE_COUNT;
+ return 1;
+ }
+}
+
+
+static int nsp_release_old(struct Scsi_Host *shpnt)
+{
+ //nsp_hw_data *data = (nsp_hw_data *)shpnt->hostdata;
+
+ /* PCMCIA Card Service dose same things below. */
+ /* So we do nothing. */
+ //if (shpnt->irq) {
+ // free_irq(shpnt->irq, data->ScsiInfo);
+ //}
+ //if (shpnt->io_port) {
+ // release_region(shpnt->io_port, shpnt->n_io_port);
+ //}
+
+ //MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* return info string */
+/*----------------------------------------------------------------*/
+static const char *nsp_info(struct Scsi_Host *shpnt)
+{
+ nsp_hw_data *data = (nsp_hw_data *)shpnt->hostdata;
+
+ return data->nspinfo;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { \
+ if(length > (pos - buffer)) { \
+ pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+ nsp_dbg(NSP_DEBUG_PROC, "buffer=0x%p pos=0x%p length=%d %d\n", buffer, pos, length, length - (pos - buffer));\
+ } \
+ } while(0)
+static int
+nsp_proc_info(
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ struct Scsi_Host *host,
+#endif
+ char *buffer,
+ char **start,
+ off_t offset,
+ int length,
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ int hostno,
+#endif
+ int inout)
+{
+ int id;
+ char *pos = buffer;
+ int thislength;
+ int speed;
+ unsigned long flags;
+ nsp_hw_data *data;
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ struct Scsi_Host *host;
+#else
+ int hostno;
+#endif
+ if (inout) {
+ return -EINVAL;
+ }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ hostno = host->host_no;
+#else
+ /* search this HBA host */
+ host = scsi_host_hn_get(hostno);
+ if (host == NULL) {
+ return -ESRCH;
+ }
+#endif
+ data = (nsp_hw_data *)host->hostdata;
+
+
+ SPRINTF("NinjaSCSI status\n\n");
+ SPRINTF("Driver version: $Revision: 1.23 $\n");
+ SPRINTF("SCSI host No.: %d\n", hostno);
+ SPRINTF("IRQ: %d\n", host->irq);
+ SPRINTF("IO: 0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
+ SPRINTF("MMIO(virtual address): 0x%lx-0x%lx\n", host->base, host->base + data->MmioLength - 1);
+ SPRINTF("sg_tablesize: %d\n", host->sg_tablesize);
+
+ SPRINTF("burst transfer mode: ");
+ switch (nsp_burst_mode) {
+ case BURST_IO8:
+ SPRINTF("io8");
+ break;
+ case BURST_IO32:
+ SPRINTF("io32");
+ break;
+ case BURST_MEM32:
+ SPRINTF("mem32");
+ break;
+ default:
+ SPRINTF("???");
+ break;
+ }
+ SPRINTF("\n");
+
+
+ spin_lock_irqsave(&(data->Lock), flags);
+ SPRINTF("CurrentSC: 0x%p\n\n", data->CurrentSC);
+ spin_unlock_irqrestore(&(data->Lock), flags);
+
+ SPRINTF("SDTR status\n");
+ for(id = 0; id < ARRAY_SIZE(data->Sync); id++) {
+
+ SPRINTF("id %d: ", id);
+
+ if (id == host->this_id) {
+ SPRINTF("----- NinjaSCSI-3 host adapter\n");
+ continue;
+ }
+
+ switch(data->Sync[id].SyncNegotiation) {
+ case SYNC_OK:
+ SPRINTF(" sync");
+ break;
+ case SYNC_NG:
+ SPRINTF("async");
+ break;
+ case SYNC_NOT_YET:
+ SPRINTF(" none");
+ break;
+ default:
+ SPRINTF("?????");
+ break;
+ }
+
+ if (data->Sync[id].SyncPeriod != 0) {
+ speed = 1000000 / (data->Sync[id].SyncPeriod * 4);
+
+ SPRINTF(" transfer %d.%dMB/s, offset %d",
+ speed / 1000,
+ speed % 1000,
+ data->Sync[id].SyncOffset
+ );
+ }
+ SPRINTF("\n");
+ }
+
+ thislength = pos - (buffer + offset);
+
+ if(thislength < 0) {
+ *start = NULL;
+ return 0;
+ }
+
+
+ thislength = min(thislength, length);
+ *start = buffer + offset;
+
+ return thislength;
+}
+#undef SPRINTF
+
+/*---------------------------------------------------------------*/
+/* error handler */
+/*---------------------------------------------------------------*/
+
+/*static int nsp_eh_strategy(struct Scsi_Host *Shost)
+{
+ return FAILED;
+}*/
+
+/*
+static int nsp_eh_abort(Scsi_Cmnd *SCpnt)
+{
+ nsp_dbg(NSP_DEBUG_BUSRESET, "SCpnt=0x%p", SCpnt);
+
+ return nsp_eh_bus_reset(SCpnt);
+}*/
+
+/*
+static int nsp_eh_device_reset(Scsi_Cmnd *SCpnt)
+{
+ nsp_dbg(NSP_DEBUG_BUSRESET, "%s: SCpnt=0x%p", SCpnt);
+
+ return FAILED;
+}*/
+
+static int nsp_bus_reset(nsp_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+ int i;
+
+ nsp_write(base, IRQCONTROL, IRQCONTROL_ALLMASK);
+
+ nsp_index_write(base, SCSIBUSCTRL, SCSI_RST);
+ mdelay(100); /* 100ms */
+ nsp_index_write(base, SCSIBUSCTRL, 0);
+ for(i = 0; i < 5; i++) {
+ nsp_index_read(base, IRQPHASESENCE); /* dummy read */
+ }
+
+ nsphw_init_sync(data);
+
+ nsp_write(base, IRQCONTROL, IRQCONTROL_ALLCLEAR);
+
+ return SUCCESS;
+}
+
+static int nsp_eh_bus_reset(Scsi_Cmnd *SCpnt)
+{
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+ nsp_dbg(NSP_DEBUG_BUSRESET, "SCpnt=0x%p", SCpnt);
+
+ return nsp_bus_reset(data);
+}
+
+static int nsp_eh_host_reset(Scsi_Cmnd *SCpnt)
+{
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+ nsp_dbg(NSP_DEBUG_BUSRESET, "in");
+
+ nsphw_init(data);
+
+ return SUCCESS;
+}
+
+
+/**********************************************************************
+ PCMCIA functions
+**********************************************************************/
+
+/*======================================================================
+ nsp_cs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+======================================================================*/
+static dev_link_t *nsp_cs_attach(void)
+{
+ scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int ret;
+ nsp_hw_data *data = &nsp_data_base;
+
+ nsp_dbg(NSP_DEBUG_INIT, "in");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (info == NULL) { return NULL; }
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ data->ScsiInfo = info;
+
+ nsp_dbg(NSP_DEBUG_INIT, "info=0x%p", info);
+
+ /* The io structure describes IO port mapping */
+ link->io.NumPorts1 = 0x10;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10; /* not used */
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ /* Interrupt handler */
+ link->irq.Handler = &nspintr;
+ link->irq.Instance = info;
+ link->irq.Attributes |= (SA_SHIRQ | SA_SAMPLE_RANDOM);
+
+ /* General socket configuration */
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME ;
+ client_reg.event_handler = &nsp_cs_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != CS_SUCCESS) {
+ cs_error(link->handle, RegisterClient, ret);
+ nsp_cs_detach(link);
+ return NULL;
+ }
+
+
+ nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link);
+ return link;
+} /* nsp_cs_attach */
+
+
+/*======================================================================
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+======================================================================*/
+static void nsp_cs_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ nsp_dbg(NSP_DEBUG_INIT, "in, link=0x%p", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) {
+ if (*linkp == link) {
+ break;
+ }
+ }
+ if (*linkp == NULL) {
+ return;
+ }
+
+ if (link->state & DEV_CONFIG)
+ nsp_cs_release(link);
+
+ /* Break the link with Card Services */
+ if (link->handle) {
+ pcmcia_deregister_client(link->handle);
+ }
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ kfree(link->priv);
+ link->priv = NULL;
+
+} /* nsp_cs_detach */
+
+
+/*======================================================================
+ nsp_cs_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ethernet device available to the system.
+======================================================================*/
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+/*====================================================================*/
+static void nsp_cs_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_ret, last_fn;
+ unsigned char tuple_data[64];
+ config_info_t conf;
+ win_req_t req;
+ memreq_t map;
+ cistpl_cftable_entry_t dflt = { 0 };
+ struct Scsi_Host *host;
+ nsp_hw_data *data = &nsp_data_base;
+#if !(LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
+ Scsi_Device *dev;
+ dev_node_t **tail, *node;
+#endif
+
+ nsp_dbg(NSP_DEBUG_INIT, "in");
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = tuple_data;
+ tuple.TupleDataMax = sizeof(tuple_data);
+ tuple.TupleOffset = 0;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ /* Look up the current Vcc */
+ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+ link->conf.Vcc = conf.Vcc;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { dflt = *cfg; }
+ if (cfg->index == 0) { goto next_entry; }
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ goto next_entry;
+ }
+ } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ } else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) {
+ link->conf.Vpp1 = link->conf.Vpp2 =
+ dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ }
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) {
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+ }
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link->handle, &link->io) != 0)
+ goto next_entry;
+ }
+
+ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+ req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+ req.Attributes |= WIN_ENABLE;
+ req.Base = mem->win[0].host_addr;
+ req.Size = mem->win[0].len;
+ if (req.Size < 0x1000) {
+ req.Size = 0x1000;
+ }
+ req.AccessSpeed = 0;
+ if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+ goto next_entry;
+ map.Page = 0; map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(link->win, &map) != 0)
+ goto next_entry;
+
+ data->MmioAddress = (unsigned long)ioremap_nocache(req.Base, req.Size);
+ data->MmioLength = req.Size;
+ }
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ nsp_dbg(NSP_DEBUG_INIT, "next");
+
+ if (link->io.NumPorts1) {
+ pcmcia_release_io(link->handle, &link->io);
+ }
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+ }
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ if (free_ports) {
+ if (link->io.BasePort1) {
+ release_region(link->io.BasePort1, link->io.NumPorts1);
+ }
+ if (link->io.BasePort2) {
+ release_region(link->io.BasePort2, link->io.NumPorts2);
+ }
+ }
+
+ /* Set port and IRQ */
+ data->BaseAddress = link->io.BasePort1;
+ data->NumAddress = link->io.NumPorts1;
+ data->IrqNumber = link->irq.AssignedIRQ;
+
+ nsp_dbg(NSP_DEBUG_INIT, "I/O[0x%x+0x%x] IRQ %d",
+ data->BaseAddress, data->NumAddress, data->IrqNumber);
+
+ if(nsphw_init(data) == FALSE) {
+ goto cs_failed;
+ }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2))
+ host = nsp_detect(&nsp_driver_template);
+#else
+ scsi_register_host(&nsp_driver_template);
+ for (host = scsi_host_get_next(NULL); host != NULL;
+ host = scsi_host_get_next(host)) {
+ if (host->hostt == &nsp_driver_template) {
+ break;
+ }
+ }
+#endif
+
+ if (host == NULL) {
+ nsp_dbg(NSP_DEBUG_INIT, "detect failed");
+ goto cs_failed;
+ }
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
+ scsi_add_host (host, NULL);
+ scsi_scan_host(host);
+
+ snprintf(info->node.dev_name, sizeof(info->node.dev_name), "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+#else
+ nsp_dbg(NSP_DEBUG_INIT, "GET_SCSI_INFO");
+ tail = &link->dev;
+ info->ndev = 0;
+
+ nsp_dbg(NSP_DEBUG_INIT, "host=0x%p", host);
+
+ for (dev = host->host_queue; dev != NULL; dev = dev->next) {
+ unsigned long id;
+ id = (dev->id & 0x0f) + ((dev->lun & 0x0f) << 4) +
+ ((dev->channel & 0x0f) << 8) +
+ ((dev->host->host_no & 0x0f) << 12);
+ node = &info->node[info->ndev];
+ node->minor = 0;
+ switch (dev->type) {
+ case TYPE_TAPE:
+ node->major = SCSI_TAPE_MAJOR;
+ snprintf(node->dev_name, sizeof(node->dev_name), "st#%04lx", id);
+ break;
+ case TYPE_DISK:
+ case TYPE_MOD:
+ node->major = SCSI_DISK0_MAJOR;
+ snprintf(node->dev_name, sizeof(node->dev_name), "sd#%04lx", id);
+ break;
+ case TYPE_ROM:
+ case TYPE_WORM:
+ node->major = SCSI_CDROM_MAJOR;
+ snprintf(node->dev_name, sizeof(node->dev_name), "sr#%04lx", id);
+ break;
+ default:
+ node->major = SCSI_GENERIC_MAJOR;
+ snprintf(node->dev_name, sizeof(node->dev_name), "sg#%04lx", id);
+ break;
+ }
+ *tail = node; tail = &node->next;
+ info->ndev++;
+ info->host = dev->host;
+ }
+
+ *tail = NULL;
+ if (info->ndev == 0) {
+ nsp_msg(KERN_INFO, "no SCSI devices found");
+ }
+ nsp_dbg(NSP_DEBUG_INIT, "host=0x%p", host);
+#endif
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "nsp_cs: index 0x%02x: Vcc %d.%d",
+ link->conf.ConfigIndex,
+ link->conf.Vcc/10, link->conf.Vcc%10);
+ if (link->conf.Vpp1) {
+ printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+ }
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ printk(", irq %d", link->irq.AssignedIRQ);
+ }
+ if (link->io.NumPorts1) {
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ }
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2+link->io.NumPorts2-1);
+ if (link->win)
+ printk(", mem 0x%06lx-0x%06lx", req.Base,
+ req.Base+req.Size-1);
+ printk("\n");
+
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+ cs_failed:
+ nsp_dbg(NSP_DEBUG_INIT, "config fail");
+ cs_error(link->handle, last_fn, last_ret);
+ nsp_cs_release(link);
+
+ return;
+} /* nsp_cs_config */
+#undef CS_CHECK
+
+
+/*======================================================================
+ After a card is removed, nsp_cs_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+======================================================================*/
+static void nsp_cs_release(dev_link_t *link)
+{
+ scsi_info_t *info = link->priv;
+ nsp_hw_data *data = NULL;
+
+ if (info->host == NULL) {
+ nsp_msg(KERN_DEBUG, "unexpected card release call.");
+ } else {
+ data = (nsp_hw_data *)info->host->hostdata;
+ }
+
+ nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link);
+
+ /* Unlink the device chain */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2))
+ if (info->host != NULL) {
+ scsi_remove_host(info->host);
+ }
+#else
+ scsi_unregister_host(&nsp_driver_template);
+#endif
+ link->dev = NULL;
+
+ if (link->win) {
+ if (data != NULL) {
+ iounmap((void *)(data->MmioAddress));
+ }
+ pcmcia_release_window(link->win);
+ }
+ pcmcia_release_configuration(link->handle);
+ if (link->io.NumPorts1) {
+ pcmcia_release_io(link->handle, &link->io);
+ }
+ if (link->irq.AssignedIRQ) {
+ pcmcia_release_irq(link->handle, &link->irq);
+ }
+ link->state &= ~DEV_CONFIG;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2))
+ if (info->host != NULL) {
+ scsi_host_put(info->host);
+ }
+#endif
+} /* nsp_cs_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received. A CARD_REMOVAL event
+ also sets some flags to discourage the net drivers from trying
+ to talk to the card any more.
+
+ When a CARD_REMOVAL event is received, we immediately set a flag
+ to block future accesses to this device. All the functions that
+ actually access the device should check this flag to make sure
+ the card is still present.
+
+======================================================================*/
+static int nsp_cs_event(event_t event,
+ int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ scsi_info_t *info = link->priv;
+ nsp_hw_data *data;
+
+ nsp_dbg(NSP_DEBUG_INIT, "in, event=0x%08x", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ nsp_dbg(NSP_DEBUG_INIT, "event: remove");
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG) {
+ ((scsi_info_t *)link->priv)->stop = 1;
+ nsp_cs_release(link);
+ }
+ break;
+
+ case CS_EVENT_CARD_INSERTION:
+ nsp_dbg(NSP_DEBUG_INIT, "event: insert");
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,68))
+ info->bus = args->bus;
+#endif
+ nsp_cs_config(link);
+ break;
+
+ case CS_EVENT_PM_SUSPEND:
+ nsp_dbg(NSP_DEBUG_INIT, "event: suspend");
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ /* Mark the device as stopped, to block IO until later */
+ nsp_dbg(NSP_DEBUG_INIT, "event: reset physical");
+
+ if (info->host != NULL) {
+ nsp_msg(KERN_INFO, "clear SDTR status");
+
+ data = (nsp_hw_data *)info->host->hostdata;
+
+ nsphw_init_sync(data);
+ }
+
+ info->stop = 1;
+ if (link->state & DEV_CONFIG) {
+ pcmcia_release_configuration(link->handle);
+ }
+ break;
+
+ case CS_EVENT_PM_RESUME:
+ nsp_dbg(NSP_DEBUG_INIT, "event: resume");
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ nsp_dbg(NSP_DEBUG_INIT, "event: reset");
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ }
+ info->stop = 0;
+
+ if (info->host != NULL) {
+ nsp_msg(KERN_INFO, "reset host and bus");
+
+ data = (nsp_hw_data *)info->host->hostdata;
+
+ nsphw_init (data);
+ nsp_bus_reset(data);
+ }
+
+ break;
+
+ default:
+ nsp_dbg(NSP_DEBUG_INIT, "event: unknown");
+ break;
+ }
+ nsp_dbg(NSP_DEBUG_INIT, "end");
+ return 0;
+} /* nsp_cs_event */
+
+/*======================================================================*
+ * module entry point
+ *====================================================================*/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
+static struct pcmcia_driver nsp_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "nsp_cs",
+ },
+ .attach = nsp_cs_attach,
+ .detach = nsp_cs_detach,
+};
+#endif
+
+static int __init nsp_cs_init(void)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
+ nsp_msg(KERN_INFO, "loading...");
+
+ return pcmcia_register_driver(&nsp_driver);
+#else
+ servinfo_t serv;
+
+ nsp_msg(KERN_INFO, "loading...");
+ pcmcia_get_card_services_info(&serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ nsp_msg(KERN_DEBUG, "Card Services release does not match!");
+ return -EINVAL;
+ }
+ register_pcmcia_driver(&dev_info, &nsp_cs_attach, &nsp_cs_detach);
+
+ nsp_dbg(NSP_DEBUG_INIT, "out");
+ return 0;
+#endif
+}
+
+static void __exit nsp_cs_exit(void)
+{
+ nsp_msg(KERN_INFO, "unloading...");
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
+ pcmcia_unregister_driver(&nsp_driver);
+ BUG_ON(dev_list != NULL);
+#else
+ unregister_pcmcia_driver(&dev_info);
+ /* XXX: this really needs to move into generic code.. */
+ while (dev_list != NULL) {
+ if (dev_list->state & DEV_CONFIG) {
+ nsp_cs_release(dev_list);
+ }
+ nsp_cs_detach(dev_list);
+ }
+#endif
+}
+
+
+module_init(nsp_cs_init)
+module_exit(nsp_cs_exit)
+
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_cs.h b/drivers/scsi/pcmcia/nsp_cs.h
new file mode 100644
index 000000000000..c201b52e063a
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_cs.h
@@ -0,0 +1,472 @@
+/*=======================================================/
+ Header file for nsp_cs.c
+ By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+ Ver.1.0 : Cut unused lines.
+ Ver 0.1 : Initial version.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License.
+
+=========================================================*/
+
+/* $Id: nsp_cs.h,v 1.19 2003/08/18 11:09:19 elca Exp $ */
+
+#ifndef __nsp_cs__
+#define __nsp_cs__
+
+/* for debugging */
+//#define NSP_DEBUG 9
+
+/*
+#define static
+#define inline
+*/
+
+/************************************
+ * Some useful macros...
+ */
+#define BIT(x) (1L << (x))
+
+/* SCSI initiator must be ID 7 */
+#define NSP_INITIATOR_ID 7
+
+#define NSP_SELTIMEOUT 200
+
+/***************************************************************************
+ * register definitions
+ ***************************************************************************/
+/*========================================================================
+ * base register
+ ========================================================================*/
+#define IRQCONTROL 0x00 /* R */
+# define IRQCONTROL_RESELECT_CLEAR BIT(0)
+# define IRQCONTROL_PHASE_CHANGE_CLEAR BIT(1)
+# define IRQCONTROL_TIMER_CLEAR BIT(2)
+# define IRQCONTROL_FIFO_CLEAR BIT(3)
+# define IRQCONTROL_ALLMASK 0xff
+# define IRQCONTROL_ALLCLEAR (IRQCONTROL_RESELECT_CLEAR | \
+ IRQCONTROL_PHASE_CHANGE_CLEAR | \
+ IRQCONTROL_TIMER_CLEAR | \
+ IRQCONTROL_FIFO_CLEAR )
+# define IRQCONTROL_IRQDISABLE 0xf0
+
+#define IRQSTATUS 0x00 /* W */
+# define IRQSTATUS_SCSI BIT(0)
+# define IRQSTATUS_TIMER BIT(2)
+# define IRQSTATUS_FIFO BIT(3)
+# define IRQSTATUS_MASK 0x0f
+
+#define IFSELECT 0x01 /* W */
+# define IF_IFSEL BIT(0)
+# define IF_REGSEL BIT(2)
+
+#define FIFOSTATUS 0x01 /* R */
+# define FIFOSTATUS_CHIP_REVISION_MASK 0x0f
+# define FIFOSTATUS_CHIP_ID_MASK 0x70
+# define FIFOSTATUS_FULL_EMPTY BIT(7)
+
+#define INDEXREG 0x02 /* R/W */
+#define DATAREG 0x03 /* R/W */
+#define FIFODATA 0x04 /* R/W */
+#define FIFODATA1 0x05 /* R/W */
+#define FIFODATA2 0x06 /* R/W */
+#define FIFODATA3 0x07 /* R/W */
+
+/*====================================================================
+ * indexed register
+ ====================================================================*/
+#define EXTBUSCTRL 0x10 /* R/W,deleted */
+
+#define CLOCKDIV 0x11 /* R/W */
+# define CLOCK_40M 0x02
+# define CLOCK_20M 0x01
+# define FAST_20 BIT(2)
+
+#define TERMPWRCTRL 0x13 /* R/W */
+# define POWER_ON BIT(0)
+
+#define SCSIIRQMODE 0x15 /* R/W */
+# define SCSI_PHASE_CHANGE_EI BIT(0)
+# define RESELECT_EI BIT(4)
+# define FIFO_IRQ_EI BIT(5)
+# define SCSI_RESET_IRQ_EI BIT(6)
+
+#define IRQPHASESENCE 0x16 /* R */
+# define LATCHED_MSG BIT(0)
+# define LATCHED_IO BIT(1)
+# define LATCHED_CD BIT(2)
+# define LATCHED_BUS_FREE BIT(3)
+# define PHASE_CHANGE_IRQ BIT(4)
+# define RESELECT_IRQ BIT(5)
+# define FIFO_IRQ BIT(6)
+# define SCSI_RESET_IRQ BIT(7)
+
+#define TIMERCOUNT 0x17 /* R/W */
+
+#define SCSIBUSCTRL 0x18 /* R/W */
+# define SCSI_SEL BIT(0)
+# define SCSI_RST BIT(1)
+# define SCSI_DATAOUT_ENB BIT(2)
+# define SCSI_ATN BIT(3)
+# define SCSI_ACK BIT(4)
+# define SCSI_BSY BIT(5)
+# define AUTODIRECTION BIT(6)
+# define ACKENB BIT(7)
+
+#define SCSIBUSMON 0x19 /* R */
+
+#define SETARBIT 0x1A /* W */
+# define ARBIT_GO BIT(0)
+# define ARBIT_FLAG_CLEAR BIT(1)
+
+#define ARBITSTATUS 0x1A /* R */
+/*# define ARBIT_GO BIT(0)*/
+# define ARBIT_WIN BIT(1)
+# define ARBIT_FAIL BIT(2)
+# define RESELECT_FLAG BIT(3)
+
+#define PARITYCTRL 0x1B /* W */
+#define PARITYSTATUS 0x1B /* R */
+
+#define COMMANDCTRL 0x1C /* W */
+# define CLEAR_COMMAND_POINTER BIT(0)
+# define AUTO_COMMAND_GO BIT(1)
+
+#define RESELECTID 0x1C /* R */
+#define COMMANDDATA 0x1D /* R/W */
+
+#define POINTERCLR 0x1E /* W */
+# define POINTER_CLEAR BIT(0)
+# define ACK_COUNTER_CLEAR BIT(1)
+# define REQ_COUNTER_CLEAR BIT(2)
+# define HOST_COUNTER_CLEAR BIT(3)
+# define READ_SOURCE (BIT(4) | BIT(5))
+# define ACK_COUNTER (0)
+# define REQ_COUNTER (BIT(4))
+# define HOST_COUNTER (BIT(5))
+
+#define TRANSFERCOUNT 0x1E /* R */
+
+#define TRANSFERMODE 0x20 /* R/W */
+# define MODE_MEM8 BIT(0)
+# define MODE_MEM32 BIT(1)
+# define MODE_ADR24 BIT(2)
+# define MODE_ADR32 BIT(3)
+# define MODE_IO8 BIT(4)
+# define MODE_IO32 BIT(5)
+# define TRANSFER_GO BIT(6)
+# define BRAIND BIT(7)
+
+#define SYNCREG 0x21 /* R/W */
+# define SYNCREG_OFFSET_MASK 0x0f
+# define SYNCREG_PERIOD_MASK 0xf0
+# define SYNCREG_PERIOD_SHIFT 4
+
+#define SCSIDATALATCH 0x22 /* W */
+#define SCSIDATAIN 0x22 /* R */
+#define SCSIDATAWITHACK 0x23 /* R/W */
+#define SCAMCONTROL 0x24 /* W */
+#define SCAMSTATUS 0x24 /* R */
+#define SCAMDATA 0x25 /* R/W */
+
+#define OTHERCONTROL 0x26 /* R/W */
+# define TPL_ROM_WRITE_EN BIT(0)
+# define TPWR_OUT BIT(1)
+# define TPWR_SENSE BIT(2)
+# define RA8_CONTROL BIT(3)
+
+#define ACKWIDTH 0x27 /* R/W */
+#define CLRTESTPNT 0x28 /* W */
+#define ACKCNTLD 0x29 /* W */
+#define REQCNTLD 0x2A /* W */
+#define HSTCNTLD 0x2B /* W */
+#define CHECKSUM 0x2C /* R/W */
+
+/************************************************************************
+ * Input status bit definitions.
+ ************************************************************************/
+#define S_MESSAGE BIT(0) /* Message line from SCSI bus */
+#define S_IO BIT(1) /* Input/Output line from SCSI bus */
+#define S_CD BIT(2) /* Command/Data line from SCSI bus */
+#define S_BUSY BIT(3) /* Busy line from SCSI bus */
+#define S_ACK BIT(4) /* Acknowlege line from SCSI bus */
+#define S_REQUEST BIT(5) /* Request line from SCSI bus */
+#define S_SELECT BIT(6) /* */
+#define S_ATN BIT(7) /* */
+
+/***********************************************************************
+ * Useful Bus Monitor status combinations.
+ ***********************************************************************/
+#define BUSMON_SEL S_SELECT
+#define BUSMON_BSY S_BUSY
+#define BUSMON_REQ S_REQUEST
+#define BUSMON_IO S_IO
+#define BUSMON_ACK S_ACK
+#define BUSMON_BUS_FREE 0
+#define BUSMON_COMMAND ( S_BUSY | S_CD | S_REQUEST )
+#define BUSMON_MESSAGE_IN ( S_BUSY | S_CD | S_IO | S_MESSAGE | S_REQUEST )
+#define BUSMON_MESSAGE_OUT ( S_BUSY | S_CD | S_MESSAGE | S_REQUEST )
+#define BUSMON_DATA_IN ( S_BUSY | S_IO | S_REQUEST )
+#define BUSMON_DATA_OUT ( S_BUSY | S_REQUEST )
+#define BUSMON_STATUS ( S_BUSY | S_CD | S_IO | S_REQUEST )
+#define BUSMON_SELECT ( S_IO | S_SELECT )
+#define BUSMON_RESELECT ( S_IO | S_SELECT )
+#define BUSMON_PHASE_MASK ( S_CD | S_IO | S_MESSAGE | S_SELECT )
+
+#define BUSPHASE_SELECT ( BUSMON_SELECT & BUSMON_PHASE_MASK )
+#define BUSPHASE_COMMAND ( BUSMON_COMMAND & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_IN ( BUSMON_MESSAGE_IN & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_OUT ( BUSMON_MESSAGE_OUT & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_IN ( BUSMON_DATA_IN & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_OUT ( BUSMON_DATA_OUT & BUSMON_PHASE_MASK )
+#define BUSPHASE_STATUS ( BUSMON_STATUS & BUSMON_PHASE_MASK )
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+ dev_link_t link;
+ struct Scsi_Host *host;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
+ dev_node_t node;
+#else
+ int ndev;
+ dev_node_t node[8];
+ struct bus_operations *bus;
+#endif
+ int stop;
+} scsi_info_t;
+
+
+/* synchronous transfer negotiation data */
+typedef struct _sync_data {
+ unsigned int SyncNegotiation;
+#define SYNC_NOT_YET 0
+#define SYNC_OK 1
+#define SYNC_NG 2
+
+ unsigned int SyncPeriod;
+ unsigned int SyncOffset;
+ unsigned char SyncRegister;
+ unsigned char AckWidth;
+} sync_data;
+
+typedef struct _nsp_hw_data {
+ unsigned int BaseAddress;
+ unsigned int NumAddress;
+ unsigned int IrqNumber;
+
+ unsigned long MmioAddress;
+#define NSP_MMIO_OFFSET 0x0800
+ unsigned long MmioLength;
+
+ unsigned char ScsiClockDiv;
+
+ unsigned char TransferMode;
+
+ int TimerCount;
+ int SelectionTimeOut;
+ Scsi_Cmnd *CurrentSC;
+ //int CurrnetTarget;
+
+ int FifoCount;
+
+#define MSGBUF_SIZE 20
+ unsigned char MsgBuffer[MSGBUF_SIZE];
+ int MsgLen;
+
+#define N_TARGET 8
+ sync_data Sync[N_TARGET];
+
+ char nspinfo[110]; /* description */
+ spinlock_t Lock;
+
+ scsi_info_t *ScsiInfo; /* attach <-> detect glue */
+
+
+#ifdef NSP_DEBUG
+ int CmdId; /* Accepted command serial number.
+ Used for debugging. */
+#endif
+} nsp_hw_data;
+
+
+/****************************************************************************
+ *
+ */
+
+/* Card service functions */
+static dev_link_t *nsp_cs_attach (void);
+static void nsp_cs_detach (dev_link_t *link);
+static void nsp_cs_release(dev_link_t *link);
+static void nsp_cs_config (dev_link_t *link);
+static int nsp_cs_event (event_t event, int priority, event_callback_args_t *args);
+
+/* Linux SCSI subsystem specific functions */
+static struct Scsi_Host *nsp_detect (Scsi_Host_Template *sht);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+static int nsp_detect_old (Scsi_Host_Template *sht);
+static int nsp_release_old(struct Scsi_Host *shpnt);
+#endif
+static const char *nsp_info (struct Scsi_Host *shpnt);
+static int nsp_proc_info (
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ struct Scsi_Host *host,
+#endif
+ char *buffer,
+ char **start,
+ off_t offset,
+ int length,
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+ int hostno,
+#endif
+ int inout);
+static int nsp_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *SCpnt));
+
+/* Error handler */
+/*static int nsp_eh_abort (Scsi_Cmnd *SCpnt);*/
+/*static int nsp_eh_device_reset(Scsi_Cmnd *SCpnt);*/
+static int nsp_eh_bus_reset (Scsi_Cmnd *SCpnt);
+static int nsp_eh_host_reset (Scsi_Cmnd *SCpnt);
+static int nsp_bus_reset (nsp_hw_data *data);
+
+/* */
+static int nsphw_init (nsp_hw_data *data);
+static int nsphw_start_selection(Scsi_Cmnd *SCpnt);
+static void nsp_start_timer (Scsi_Cmnd *SCpnt, int time);
+static int nsp_fifo_count (Scsi_Cmnd *SCpnt);
+static void nsp_pio_read (Scsi_Cmnd *SCpnt);
+static void nsp_pio_write (Scsi_Cmnd *SCpnt);
+static int nsp_nexus (Scsi_Cmnd *SCpnt);
+static void nsp_scsi_done (Scsi_Cmnd *SCpnt);
+static int nsp_analyze_sdtr (Scsi_Cmnd *SCpnt);
+static int nsp_negate_signal (Scsi_Cmnd *SCpnt, unsigned char mask, char *str);
+static int nsp_expect_signal (Scsi_Cmnd *SCpnt, unsigned char current_phase, unsigned char mask);
+static int nsp_xfer (Scsi_Cmnd *SCpnt, int phase);
+static int nsp_dataphase_bypass (Scsi_Cmnd *SCpnt);
+static int nsp_reselected (Scsi_Cmnd *SCpnt);
+static struct Scsi_Host *nsp_detect(Scsi_Host_Template *sht);
+
+/* Interrupt handler */
+//static irqreturn_t nspintr(int irq, void *dev_id, struct pt_regs *regs);
+
+/* Module entry point*/
+static int __init nsp_cs_init(void);
+static void __exit nsp_cs_exit(void);
+
+
+/* Debug */
+#ifdef NSP_DEBUG
+static void show_command (Scsi_Cmnd *SCpnt);
+static void show_phase (Scsi_Cmnd *SCpnt);
+static void show_busphase(unsigned char stat);
+static void show_message (nsp_hw_data *data);
+#else
+# define show_command(ptr) /* */
+# define show_phase(SCpnt) /* */
+# define show_busphase(stat) /* */
+# define show_message(data) /* */
+#endif
+
+/*
+ * SCSI phase
+ */
+enum _scsi_phase {
+ PH_UNDETERMINED ,
+ PH_ARBSTART ,
+ PH_SELSTART ,
+ PH_SELECTED ,
+ PH_COMMAND ,
+ PH_DATA ,
+ PH_STATUS ,
+ PH_MSG_IN ,
+ PH_MSG_OUT ,
+ PH_DISCONNECT ,
+ PH_RESELECT ,
+ PH_ABORT ,
+ PH_RESET
+};
+
+enum _data_in_out {
+ IO_UNKNOWN,
+ IO_IN,
+ IO_OUT
+};
+
+enum _burst_mode {
+ BURST_IO8 = 0,
+ BURST_IO32 = 1,
+ BURST_MEM32 = 2,
+};
+
+
+/**************************************************************************
+ * SCSI messaage
+ */
+#define MSG_COMMAND_COMPLETE 0x00
+#define MSG_EXTENDED 0x01
+#define MSG_ABORT 0x06
+#define MSG_NO_OPERATION 0x08
+#define MSG_BUS_DEVICE_RESET 0x0c
+
+#define MSG_EXT_SDTR 0x01
+
+
+/**************************************************************************
+ * Compatibility functions
+ */
+
+/* for Kernel 2.4 */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+# define scsi_register_host(template) scsi_register_module(MODULE_SCSI_HA, template)
+# define scsi_unregister_host(template) scsi_unregister_module(MODULE_SCSI_HA, template)
+# define scsi_host_put(host) scsi_unregister(host)
+
+typedef void irqreturn_t;
+# define IRQ_NONE /* */
+# define IRQ_HANDLED /* */
+# define IRQ_RETVAL(x) /* */
+
+/* This is ad-hoc version of scsi_host_get_next() */
+static inline struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *host)
+{
+ if (host == NULL) {
+ return scsi_hostlist;
+ } else {
+ return host->next;
+ }
+}
+
+/* This is ad-hoc version of scsi_host_hn_get() */
+static inline struct Scsi_Host *scsi_host_hn_get(unsigned short hostno)
+{
+ struct Scsi_Host *host;
+
+ for (host = scsi_host_get_next(NULL); host != NULL;
+ host = scsi_host_get_next(host)) {
+ if (host->host_no == hostno) {
+ break;
+ }
+ }
+
+ return host;
+}
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ pcmcia_report_error(handle, &err);
+}
+
+/* scatter-gather table */
+# define BUFFER_ADDR (SCpnt->SCp.buffer->address)
+#endif
+
+/* for Kernel 2.6 */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+/* scatter-gather table */
+# define BUFFER_ADDR ((char *)((unsigned int)(SCpnt->SCp.buffer->page) + SCpnt->SCp.buffer->offset))
+#endif
+
+#endif /*__nsp_cs__*/
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_debug.c b/drivers/scsi/pcmcia/nsp_debug.c
new file mode 100644
index 000000000000..62e5c60067fd
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_debug.c
@@ -0,0 +1,215 @@
+/*========================================================================
+ Debug routines for nsp_cs
+ By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License.
+=========================================================================*/
+
+/* $Id: nsp_debug.c,v 1.3 2003/07/26 14:21:09 elca Exp $ */
+
+/*
+ * Show the command data of a command
+ */
+static const char unknown[] = "UNKNOWN";
+
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */ unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
+/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long", "Write Long",
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", "Write Same",
+/* 42-48 */ "Read Sub-Ch(cd)", "Read TOC", "Read Header(cd)", "Play Audio(cd)", unknown, "Play Audio MSF(cd)", "Play Audio Track/Index(cd)",
+/* 49-4f */ "Play Track Relative(10)(cd)", unknown, "Pause/Resume(cd)", "Log Select", "Log Sense", unknown, unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP 0
+#define VENDOR_GROUP 1
+#define NOTEXT_GROUP 2
+
+static const char **commands[] = {
+ group_0_commands, group_1_commands, group_2_commands,
+ (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
+ (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP,
+ (const char **) VENDOR_GROUP
+};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcodek(unsigned char opcode)
+{
+ const char **table = commands[ group(opcode) ];
+
+ switch ((unsigned long) table) {
+ case RESERVED_GROUP:
+ printk("%s[%02x] ", reserved, opcode);
+ break;
+ case NOTEXT_GROUP:
+ printk("%s(notext)[%02x] ", unknown, opcode);
+ break;
+ case VENDOR_GROUP:
+ printk("%s[%02x] ", vendor, opcode);
+ break;
+ default:
+ if (table[opcode & 0x1f] != unknown)
+ printk("%s[%02x] ", table[opcode & 0x1f], opcode);
+ else
+ printk("%s[%02x] ", unknown, opcode);
+ break;
+ }
+}
+
+static void print_commandk (unsigned char *command)
+{
+ int i, s;
+ printk(KERN_DEBUG);
+ print_opcodek(command[0]);
+ /*printk(KERN_DEBUG "%s ", __FUNCTION__);*/
+ if ((command[0] >> 5) == 6 ||
+ (command[0] >> 5) == 7 ) {
+ s = 12; /* vender specific */
+ } else {
+ s = COMMAND_SIZE(command[0]);
+ }
+ for ( i = 1; i < s; ++i) {
+ printk("%02x ", command[i]);
+ }
+
+ switch (s) {
+ case 6:
+ printk("LBA=%d len=%d",
+ (((unsigned int)command[1] & 0x0f) << 16) |
+ ( (unsigned int)command[2] << 8) |
+ ( (unsigned int)command[3] ),
+ (unsigned int)command[4]
+ );
+ break;
+ case 10:
+ printk("LBA=%d len=%d",
+ ((unsigned int)command[2] << 24) |
+ ((unsigned int)command[3] << 16) |
+ ((unsigned int)command[4] << 8) |
+ ((unsigned int)command[5] ),
+ ((unsigned int)command[7] << 8) |
+ ((unsigned int)command[8] )
+ );
+ break;
+ case 12:
+ printk("LBA=%d len=%d",
+ ((unsigned int)command[2] << 24) |
+ ((unsigned int)command[3] << 16) |
+ ((unsigned int)command[4] << 8) |
+ ((unsigned int)command[5] ),
+ ((unsigned int)command[6] << 24) |
+ ((unsigned int)command[7] << 16) |
+ ((unsigned int)command[8] << 8) |
+ ((unsigned int)command[9] )
+ );
+ break;
+ default:
+ break;
+ }
+ printk("\n");
+}
+
+static void show_command(Scsi_Cmnd *SCpnt)
+{
+ print_commandk(SCpnt->cmnd);
+}
+
+static void show_phase(Scsi_Cmnd *SCpnt)
+{
+ int i = SCpnt->SCp.phase;
+
+ char *ph[] = {
+ "PH_UNDETERMINED",
+ "PH_ARBSTART",
+ "PH_SELSTART",
+ "PH_SELECTED",
+ "PH_COMMAND",
+ "PH_DATA",
+ "PH_STATUS",
+ "PH_MSG_IN",
+ "PH_MSG_OUT",
+ "PH_DISCONNECT",
+ "PH_RESELECT"
+ };
+
+ if ( i < PH_UNDETERMINED || i > PH_RESELECT ) {
+ printk(KERN_DEBUG "scsi phase: unknown(%d)\n", i);
+ return;
+ }
+
+ printk(KERN_DEBUG "scsi phase: %s\n", ph[i]);
+
+ return;
+}
+
+static void show_busphase(unsigned char stat)
+{
+ switch(stat) {
+ case BUSPHASE_COMMAND:
+ printk(KERN_DEBUG "BUSPHASE_COMMAND\n");
+ break;
+ case BUSPHASE_MESSAGE_IN:
+ printk(KERN_DEBUG "BUSPHASE_MESSAGE_IN\n");
+ break;
+ case BUSPHASE_MESSAGE_OUT:
+ printk(KERN_DEBUG "BUSPHASE_MESSAGE_OUT\n");
+ break;
+ case BUSPHASE_DATA_IN:
+ printk(KERN_DEBUG "BUSPHASE_DATA_IN\n");
+ break;
+ case BUSPHASE_DATA_OUT:
+ printk(KERN_DEBUG "BUSPHASE_DATA_OUT\n");
+ break;
+ case BUSPHASE_STATUS:
+ printk(KERN_DEBUG "BUSPHASE_STATUS\n");
+ break;
+ case BUSPHASE_SELECT:
+ printk(KERN_DEBUG "BUSPHASE_SELECT\n");
+ break;
+ default:
+ printk(KERN_DEBUG "BUSPHASE_other\n");
+ break;
+ }
+}
+
+static void show_message(nsp_hw_data *data)
+{
+ int i;
+
+ printk(KERN_DEBUG "msg:");
+ for(i=0; i < data->MsgLen; i++) {
+ printk(" %02x", data->MsgBuffer[i]);
+ }
+ printk("\n");
+}
+
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_io.h b/drivers/scsi/pcmcia/nsp_io.h
new file mode 100644
index 000000000000..3b8746f85b6c
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_io.h
@@ -0,0 +1,274 @@
+/*
+ NinjaSCSI I/O funtions
+ By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License.
+
+ */
+
+/* $Id: nsp_io.h,v 1.3 2003/08/04 21:15:26 elca Exp $ */
+
+#ifndef __NSP_IO_H__
+#define __NSP_IO_H__
+
+static inline void nsp_write(unsigned int base,
+ unsigned int index,
+ unsigned char val);
+static inline unsigned char nsp_read(unsigned int base,
+ unsigned int index);
+static inline void nsp_index_write(unsigned int BaseAddr,
+ unsigned int Register,
+ unsigned char Value);
+static inline unsigned char nsp_index_read(unsigned int BaseAddr,
+ unsigned int Register);
+
+/*******************************************************************
+ * Basic IO
+ */
+
+static inline void nsp_write(unsigned int base,
+ unsigned int index,
+ unsigned char val)
+{
+ outb(val, (base + index));
+}
+
+static inline unsigned char nsp_read(unsigned int base,
+ unsigned int index)
+{
+ return inb(base + index);
+}
+
+
+/**********************************************************************
+ * Indexed IO
+ */
+static inline unsigned char nsp_index_read(unsigned int BaseAddr,
+ unsigned int Register)
+{
+ outb(Register, BaseAddr + INDEXREG);
+ return inb(BaseAddr + DATAREG);
+}
+
+static inline void nsp_index_write(unsigned int BaseAddr,
+ unsigned int Register,
+ unsigned char Value)
+{
+ outb(Register, BaseAddr + INDEXREG);
+ outb(Value, BaseAddr + DATAREG);
+}
+
+/*********************************************************************
+ * fifo func
+ */
+
+/* read 8 bit FIFO */
+static inline void nsp_multi_read_1(unsigned int BaseAddr,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ insb(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo8_read(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ /*nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx", buf, count);*/
+ nsp_multi_read_1(base, FIFODATA, buf, count);
+}
+
+/*--------------------------------------------------------------*/
+
+/* read 16 bit FIFO */
+static inline void nsp_multi_read_2(unsigned int BaseAddr,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ insw(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo16_read(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*2", buf, count);
+ nsp_multi_read_2(base, FIFODATA, buf, count);
+}
+
+/*--------------------------------------------------------------*/
+
+/* read 32bit FIFO */
+static inline void nsp_multi_read_4(unsigned int BaseAddr,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ insl(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo32_read(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*4", buf, count);
+ nsp_multi_read_4(base, FIFODATA, buf, count);
+}
+
+/*----------------------------------------------------------*/
+
+/* write 8bit FIFO */
+static inline void nsp_multi_write_1(unsigned int BaseAddr,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ outsb(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo8_write(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ nsp_multi_write_1(base, FIFODATA, buf, count);
+}
+
+/*---------------------------------------------------------*/
+
+/* write 16bit FIFO */
+static inline void nsp_multi_write_2(unsigned int BaseAddr,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ outsw(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo16_write(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ nsp_multi_write_2(base, FIFODATA, buf, count);
+}
+
+/*---------------------------------------------------------*/
+
+/* write 32bit FIFO */
+static inline void nsp_multi_write_4(unsigned int BaseAddr,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ outsl(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo32_write(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ nsp_multi_write_4(base, FIFODATA, buf, count);
+}
+
+
+/*====================================================================*/
+
+static inline void nsp_mmio_write(unsigned long base,
+ unsigned int index,
+ unsigned char val)
+{
+ unsigned char *ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + index);
+
+ writeb(val, ptr);
+}
+
+static inline unsigned char nsp_mmio_read(unsigned long base,
+ unsigned int index)
+{
+ unsigned char *ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + index);
+
+ return readb(ptr);
+}
+
+/*-----------*/
+
+static inline unsigned char nsp_mmio_index_read(unsigned long base,
+ unsigned int reg)
+{
+ unsigned char *index_ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + INDEXREG);
+ unsigned char *data_ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + DATAREG);
+
+ writeb((unsigned char)reg, index_ptr);
+ return readb(data_ptr);
+}
+
+static inline void nsp_mmio_index_write(unsigned long base,
+ unsigned int reg,
+ unsigned char val)
+{
+ unsigned char *index_ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + INDEXREG);
+ unsigned char *data_ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + DATAREG);
+
+ writeb((unsigned char)reg, index_ptr);
+ writeb(val, data_ptr);
+}
+
+/* read 32bit FIFO */
+static inline void nsp_mmio_multi_read_4(unsigned long base,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ unsigned long *ptr = (unsigned long *)(base + Register);
+ unsigned long *tmp = (unsigned long *)buf;
+ int i;
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "base 0x%0lx ptr 0x%p",base,ptr);
+
+ for (i = 0; i < count; i++) {
+ *tmp = readl(ptr);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "<%d,%p,%p,%lx>", i, ptr, tmp, *tmp);
+ tmp++;
+ }
+}
+
+static inline void nsp_mmio_fifo32_read(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*4", buf, count);
+ nsp_mmio_multi_read_4(base, FIFODATA, buf, count);
+}
+
+static inline void nsp_mmio_multi_write_4(unsigned long base,
+ unsigned int Register,
+ void *buf,
+ unsigned long count)
+{
+ unsigned long *ptr = (unsigned long *)(base + Register);
+ unsigned long *tmp = (unsigned long *)buf;
+ int i;
+
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "base 0x%0lx ptr 0x%p",base,ptr);
+
+ for (i = 0; i < count; i++) {
+ writel(*tmp, ptr);
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "<%d,%p,%p,%lx>", i, ptr, tmp, *tmp);
+ tmp++;
+ }
+}
+
+static inline void nsp_mmio_fifo32_write(unsigned int base,
+ void *buf,
+ unsigned long count)
+{
+ //nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*4", buf, count);
+ nsp_mmio_multi_write_4(base, FIFODATA, buf, count);
+}
+
+
+
+#endif
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_message.c b/drivers/scsi/pcmcia/nsp_message.c
new file mode 100644
index 000000000000..d7057737ff34
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_message.c
@@ -0,0 +1,78 @@
+/*==========================================================================
+ NinjaSCSI-3 message handler
+ By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License.
+ */
+
+/* $Id: nsp_message.c,v 1.6 2003/07/26 14:21:09 elca Exp $ */
+
+static void nsp_message_in(Scsi_Cmnd *SCpnt)
+{
+ unsigned int base = SCpnt->device->host->io_port;
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ unsigned char data_reg, control_reg;
+ int ret, len;
+
+ /*
+ * XXX: NSP QUIRK
+ * NSP invoke interrupts only in the case of scsi phase changes,
+ * therefore we should poll the scsi phase here to catch
+ * the next "msg in" if exists (no scsi phase changes).
+ */
+ ret = 16;
+ len = 0;
+
+ nsp_dbg(NSP_DEBUG_MSGINOCCUR, "msgin loop");
+ do {
+ /* read data */
+ data_reg = nsp_index_read(base, SCSIDATAIN);
+
+ /* assert ACK */
+ control_reg = nsp_index_read(base, SCSIBUSCTRL);
+ control_reg |= SCSI_ACK;
+ nsp_index_write(base, SCSIBUSCTRL, control_reg);
+ nsp_negate_signal(SCpnt, BUSMON_REQ, "msgin<REQ>");
+
+ data->MsgBuffer[len] = data_reg; len++;
+
+ /* deassert ACK */
+ control_reg = nsp_index_read(base, SCSIBUSCTRL);
+ control_reg &= ~SCSI_ACK;
+ nsp_index_write(base, SCSIBUSCTRL, control_reg);
+
+ /* catch a next signal */
+ ret = nsp_expect_signal(SCpnt, BUSPHASE_MESSAGE_IN, BUSMON_REQ);
+ } while (ret > 0 && MSGBUF_SIZE > len);
+
+ data->MsgLen = len;
+
+}
+
+static void nsp_message_out(Scsi_Cmnd *SCpnt)
+{
+ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+ int ret = 1;
+ int len = data->MsgLen;
+
+ /*
+ * XXX: NSP QUIRK
+ * NSP invoke interrupts only in the case of scsi phase changes,
+ * therefore we should poll the scsi phase here to catch
+ * the next "msg out" if exists (no scsi phase changes).
+ */
+
+ nsp_dbg(NSP_DEBUG_MSGOUTOCCUR, "msgout loop");
+ do {
+ if (nsp_xfer(SCpnt, BUSPHASE_MESSAGE_OUT)) {
+ nsp_msg(KERN_DEBUG, "msgout: xfer short");
+ }
+
+ /* catch a next signal */
+ ret = nsp_expect_signal(SCpnt, BUSPHASE_MESSAGE_OUT, BUSMON_REQ);
+ } while (ret > 0 && len-- > 0);
+
+}
+
+/* end */
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
new file mode 100644
index 000000000000..4766bcd63692
--- /dev/null
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -0,0 +1,425 @@
+/*======================================================================
+
+ A driver for the Qlogic SCSI card
+
+ qlogic_cs.c 1.79 2000/06/12 21:27:26
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "../qlogicfas408.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ * drain
+ */
+#define INT_TYPE 0
+
+static char qlogic_name[] = "qlogic_cs";
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+static Scsi_Host_Template qlogicfas_driver_template = {
+ .module = THIS_MODULE,
+ .name = qlogic_name,
+ .proc_name = qlogic_name,
+ .info = qlogicfas408_info,
+ .queuecommand = qlogicfas408_queuecommand,
+ .eh_abort_handler = qlogicfas408_abort,
+ .eh_bus_reset_handler = qlogicfas408_bus_reset,
+ .eh_device_reset_handler= qlogicfas408_device_reset,
+ .eh_host_reset_handler = qlogicfas408_host_reset,
+ .bios_param = qlogicfas408_biosparam,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+ unsigned short manf_id;
+} scsi_info_t;
+
+static void qlogic_release(dev_link_t *link);
+static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
+
+static dev_link_t *qlogic_attach(void);
+static void qlogic_detach(dev_link_t *);
+
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "qlogic_cs";
+
+static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host,
+ dev_link_t *link, int qbase, int qlirq)
+{
+ int qltyp; /* type of chip */
+ int qinitid;
+ struct Scsi_Host *shost; /* registered host structure */
+ struct qlogicfas408_priv *priv;
+
+ qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
+ qinitid = host->this_id;
+ if (qinitid < 0)
+ qinitid = 7; /* if no ID, use 7 */
+
+ qlogicfas408_setup(qbase, qinitid, INT_TYPE);
+
+ host->name = qlogic_name;
+ shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
+ if (!shost)
+ goto err;
+ shost->io_port = qbase;
+ shost->n_io_port = 16;
+ shost->dma_channel = -1;
+ if (qlirq != -1)
+ shost->irq = qlirq;
+
+ priv = get_priv_by_host(shost);
+ priv->qlirq = qlirq;
+ priv->qbase = qbase;
+ priv->qinitid = qinitid;
+ priv->shost = shost;
+ priv->int_type = INT_TYPE;
+
+ if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
+ goto free_scsi_host;
+
+ sprintf(priv->qinfo,
+ "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
+ qltyp, qbase, qlirq, QL_TURBO_PDMA);
+
+ if (scsi_add_host(shost, NULL))
+ goto free_interrupt;
+
+ scsi_scan_host(shost);
+
+ return shost;
+
+free_interrupt:
+ free_irq(qlirq, shost);
+
+free_scsi_host:
+ scsi_host_put(shost);
+
+err:
+ return NULL;
+}
+static dev_link_t *qlogic_attach(void)
+{
+ scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int ret;
+
+ DEBUG(0, "qlogic_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.event_handler = &qlogic_event;
+ client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ qlogic_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* qlogic_attach */
+
+/*====================================================================*/
+
+static void qlogic_detach(dev_link_t * link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "qlogic_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ qlogic_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ kfree(link->priv);
+
+} /* qlogic_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void qlogic_config(dev_link_t * link)
+{
+ client_handle_t handle = link->handle;
+ scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ unsigned short tuple_data[32];
+ struct Scsi_Host *host;
+
+ DEBUG(0, "qlogic_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *) tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+ info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+ if (link->io.BasePort1 != 0) {
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+ next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
+ /* set ATAcmd */
+ outb(0xb4, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+
+ /* The KXL-810AN has a bigger IO port window */
+ if (link->io.NumPorts1 == 32)
+ host = qlogic_detect(&qlogicfas_driver_template, link,
+ link->io.BasePort1 + 16, link->irq.AssignedIRQ);
+ else
+ host = qlogic_detect(&qlogicfas_driver_template, link,
+ link->io.BasePort1, link->irq.AssignedIRQ);
+
+ if (!host) {
+ printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
+ goto out;
+ }
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+out:
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ link->dev = NULL;
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+ link->state &= ~DEV_CONFIG;
+ return;
+
+} /* qlogic_config */
+
+/*====================================================================*/
+
+static void qlogic_release(dev_link_t *link)
+{
+ scsi_info_t *info = link->priv;
+
+ DEBUG(0, "qlogic_release(0x%p)\n", link);
+
+ scsi_remove_host(info->host);
+ link->dev = NULL;
+
+ free_irq(link->irq.AssignedIRQ, info->host);
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ scsi_host_put(info->host);
+
+ link->state &= ~DEV_CONFIG;
+}
+
+/*====================================================================*/
+
+static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
+{
+ dev_link_t *link = args->client_data;
+
+ DEBUG(1, "qlogic_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ qlogic_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ qlogic_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ scsi_info_t *info = link->priv;
+ pcmcia_request_configuration(link->handle, &link->conf);
+ if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
+ outb(0x80, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+ /* Ugggglllyyyy!!! */
+ qlogicfas408_bus_reset(NULL);
+ }
+ break;
+ }
+ return 0;
+} /* qlogic_event */
+
+
+static struct pcmcia_driver qlogic_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "qlogic_cs",
+ },
+ .attach = qlogic_attach,
+ .detach = qlogic_detach,
+};
+
+static int __init init_qlogic_cs(void)
+{
+ return pcmcia_register_driver(&qlogic_cs_driver);
+}
+
+static void __exit exit_qlogic_cs(void)
+{
+ pcmcia_unregister_driver(&qlogic_cs_driver);
+ BUG_ON(dev_list != NULL);
+}
+
+MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
+MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
+MODULE_LICENSE("GPL");
+module_init(init_qlogic_cs);
+module_exit(exit_qlogic_cs);
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
new file mode 100644
index 000000000000..8457d0d7748a
--- /dev/null
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -0,0 +1,1022 @@
+/*
+* sym53c500_cs.c Bob Tracy (rct@frus.com)
+*
+* A rewrite of the pcmcia-cs add-on driver for newer (circa 1997)
+* New Media Bus Toaster PCMCIA SCSI cards using the Symbios Logic
+* 53c500 controller: intended for use with 2.6 and later kernels.
+* The pcmcia-cs add-on version of this driver is not supported
+* beyond 2.4. It consisted of three files with history/copyright
+* information as follows:
+*
+* SYM53C500.h
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at).
+* Adapted from NCR53c406a.h which is Copyrighted (C) 1994
+* Normunds Saumanis (normunds@rx.tech.swh.lv)
+*
+* SYM53C500.c
+* Bob Tracy (rct@frus.com)
+* Original driver by Tom Corner (tcorner@via.at) was adapted
+* from NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996
+* Normunds Saumanis (normunds@fi.ibm.com)
+*
+* sym53c500.c
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at) was adapted from a
+* driver for the Qlogic SCSI card written by
+* David Hinds (dhinds@allegro.stanford.edu).
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*/
+
+#define SYM53C500_DEBUG 0
+#define VERBOSE_SYM53C500_DEBUG 0
+
+/*
+* Set this to 0 if you encounter kernel lockups while transferring
+* data in PIO mode. Note this can be changed via "sysfs".
+*/
+#define USE_FAST_PIO 1
+
+/* =============== End of user configurable parameters ============== */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* ================================================================== */
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"sym53c500_cs.c 0.9c 2004/10/27 (Bob Tracy)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ================================================================== */
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#define C3_IMG 0x20 /* CDB */
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xa4 /* ? changed from b6= AA PI SIE POL */
+#define C7_IMG 0x80 /* added for SYM53C500 t. corner */
+
+/* Hardware Registers: offsets from io_port (base) */
+
+/* Control Register Set 0 */
+#define TC_LSB 0x00 /* transfer counter lsb */
+#define TC_MSB 0x01 /* transfer counter msb */
+#define SCSI_FIFO 0x02 /* scsi fifo register */
+#define CMD_REG 0x03 /* command register */
+#define STAT_REG 0x04 /* status register */
+#define DEST_ID 0x04 /* selection/reselection bus id */
+#define INT_REG 0x05 /* interrupt status register */
+#define SRTIMOUT 0x05 /* select/reselect timeout reg */
+#define SEQ_REG 0x06 /* sequence step register */
+#define SYNCPRD 0x06 /* synchronous transfer period */
+#define FIFO_FLAGS 0x07 /* indicates # of bytes in fifo */
+#define SYNCOFF 0x07 /* synchronous offset register */
+#define CONFIG1 0x08 /* configuration register */
+#define CLKCONV 0x09 /* clock conversion register */
+/* #define TESTREG 0x0A */ /* test mode register */
+#define CONFIG2 0x0B /* configuration 2 register */
+#define CONFIG3 0x0C /* configuration 3 register */
+#define CONFIG4 0x0D /* configuration 4 register */
+#define TC_HIGH 0x0E /* transfer counter high */
+/* #define FIFO_BOTTOM 0x0F */ /* reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/* #define JUMPER_SENSE 0x00 */ /* jumper sense port reg (r/w) */
+/* #define SRAM_PTR 0x01 */ /* SRAM address pointer reg (r/w) */
+/* #define SRAM_DATA 0x02 */ /* SRAM data register (r/w) */
+#define PIO_FIFO 0x04 /* PIO FIFO registers (r/w) */
+/* #define PIO_FIFO1 0x05 */ /* */
+/* #define PIO_FIFO2 0x06 */ /* */
+/* #define PIO_FIFO3 0x07 */ /* */
+#define PIO_STATUS 0x08 /* PIO status (r/w) */
+/* #define ATA_CMD 0x09 */ /* ATA command/status reg (r/w) */
+/* #define ATA_ERR 0x0A */ /* ATA features/error reg (r/w) */
+#define PIO_FLAG 0x0B /* PIO flag interrupt enable (r/w) */
+#define CONFIG5 0x09 /* configuration 5 register */
+/* #define SIGNATURE 0x0E */ /* signature register (r) */
+/* #define CONFIG6 0x0F */ /* configuration 6 register (r) */
+#define CONFIG7 0x0d
+
+/* select register set 0 */
+#define REG0(x) (outb(C4_IMG, (x) + CONFIG4))
+/* select register set 1 */
+#define REG1(x) outb(C7_IMG, (x) + CONFIG7); outb(C5_IMG, (x) + CONFIG5)
+
+#if SYM53C500_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_SYM53C500_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(x, count) \
+ outb(count & 0xff, (x) + TC_LSB); \
+ outb((count >> 8) & 0xff, (x) + TC_MSB); \
+ outb((count >> 16) & 0xff, (x) + TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/* ================================================================== */
+
+struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+ unsigned short manf_id;
+};
+
+/*
+* Repository for per-instance host data.
+*/
+struct sym53c500_data {
+ struct scsi_cmnd *current_SC;
+ int fast_pio;
+};
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* ================================================================== */
+
+/*
+* Global (within this module) variables other than
+* sym53c500_driver_template (the scsi_host_template).
+*/
+static dev_link_t *dev_list;
+static dev_info_t dev_info = "sym53c500_cs";
+
+/* ================================================================== */
+
+static void
+chip_init(int io_port)
+{
+ REG1(io_port);
+ outb(0x01, io_port + PIO_STATUS);
+ outb(0x00, io_port + PIO_FLAG);
+
+ outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */
+ outb(C3_IMG, io_port + CONFIG3);
+ outb(C2_IMG, io_port + CONFIG2);
+ outb(C1_IMG, io_port + CONFIG1);
+
+ outb(0x05, io_port + CLKCONV); /* clock conversion factor */
+ outb(0x9C, io_port + SRTIMOUT); /* Selection timeout */
+ outb(0x05, io_port + SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, io_port + SYNCOFF); /* synchronous mode */
+}
+
+static void
+SYM53C500_int_host_reset(int io_port)
+{
+ outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */
+ outb(CHIP_RESET, io_port + CMD_REG);
+ outb(SCSI_NOP, io_port + CMD_REG); /* required after reset */
+ outb(SCSI_RESET, io_port + CMD_REG);
+ chip_init(io_port);
+}
+
+static __inline__ int
+SYM53C500_pio_read(int fast_pio, int base, unsigned char *request, unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+
+ REG1(base);
+ while (reqlen) {
+ i = inb(base + PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch (i & 0x1e) {
+ default:
+ case 0x10: /* fifo empty */
+ len = 0;
+ break;
+ case 0x0:
+ len = 1;
+ break;
+ case 0x8: /* fifo 1/3 full */
+ len = 42;
+ break;
+ case 0xc: /* fifo 2/3 full */
+ len = 84;
+ break;
+ case 0xe: /* fifo full */
+ len = 128;
+ break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ insl(base + PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ *request++ = inb(base + PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static __inline__ int
+SYM53C500_pio_write(int fast_pio, int base, unsigned char *request, unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+
+ REG1(base);
+ while (reqlen && !(i & 0x40)) {
+ i = inb(base + PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch (i & 0x1e) {
+ case 0x10:
+ len = 128;
+ break;
+ case 0x0:
+ len = 84;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 1;
+ break;
+ default:
+ case 0xe:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ outsl(base + PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ outb(*request++, base + PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t
+SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+ int port_base = dev->io_port;
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)dev->hostdata;
+ struct scsi_cmnd *curSC = data->current_SC;
+ int fast_pio = data->fast_pio;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+
+ VDEB(printk("SYM53C500_intr called\n"));
+
+ REG1(port_base);
+ pio_status = inb(port_base + PIO_STATUS);
+ REG0(port_base);
+ status = inb(port_base + STAT_REG);
+ DEB(seq_reg = inb(port_base + SEQ_REG));
+ int_reg = inb(port_base + INT_REG);
+ DEB(fifo_size = inb(port_base + FIFO_FLAGS) & 0x1f);
+
+#if SYM53C500_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+ printk(", pio=%02x\n", pio_status);
+#endif /* SYM53C500_DEBUG */
+
+ if (int_reg & 0x80) { /* SCSI reset intr */
+ DEB(printk("SYM53C500: reset intr received\n"));
+ curSC->result = DID_RESET << 16;
+ goto idle_out;
+ }
+
+ if (pio_status & 0x80) {
+ printk("SYM53C500: Warning: PIO error!\n");
+ curSC->result = DID_ERROR << 16;
+ goto idle_out;
+ }
+
+ if (status & 0x20) { /* Parity error */
+ printk("SYM53C500: Warning: parity error!\n");
+ curSC->result = DID_PARITY << 16;
+ goto idle_out;
+ }
+
+ if (status & 0x40) { /* Gross error */
+ printk("SYM53C500: Warning: gross error!\n");
+ curSC->result = DID_ERROR << 16;
+ goto idle_out;
+ }
+
+ if (int_reg & 0x20) { /* Disconnect */
+ DEB(printk("SYM53C500: disconnect intr received\n"));
+ if (curSC->SCp.phase != message_in) { /* Unexpected disconnect */
+ curSC->result = DID_NO_CONNECT << 16;
+ } else { /* Command complete, return status and message */
+ curSC->result = (curSC->SCp.Status & 0xff)
+ | ((curSC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+ goto idle_out;
+ }
+
+ switch (status & 0x07) { /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ curSC->SCp.phase = data_out;
+ VDEB(printk("SYM53C500: Data-Out phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+ if (!curSC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_write(fast_pio, port_base, curSC->request_buffer, curSC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = curSC->use_sg;
+ sglist = curSC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_write(fast_pio, port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0(port_base);
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ curSC->SCp.phase = data_in;
+ VDEB(printk("SYM53C500: Data-In phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+ if (!curSC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_read(fast_pio, port_base, curSC->request_buffer, curSC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = curSC->use_sg;
+ sglist = curSC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_read(fast_pio, port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0(port_base);
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ curSC->SCp.phase = command_ph;
+ printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ curSC->SCp.phase = status_ph;
+ VDEB(printk("SYM53C500: Status phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ outb(INIT_CMD_COMPLETE, port_base + CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("SYM53C500: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("SYM53C500: Message-Out phase\n"));
+ curSC->SCp.phase = message_out;
+ outb(SET_ATN, port_base + CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, port_base + CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ VDEB(printk("SYM53C500: Message-In phase\n"));
+ curSC->SCp.phase = message_in;
+
+ curSC->SCp.Status = inb(port_base + SCSI_FIFO);
+ curSC->SCp.Message = inb(port_base + SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(port_base + FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n", curSC->SCp.Status, curSC->SCp.Message));
+
+ if (curSC->SCp.Message == SAVE_POINTERS || curSC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, port_base + CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, port_base + CMD_REG);
+ break;
+ }
+out:
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+
+idle_out:
+ curSC->SCp.phase = idle;
+ curSC->scsi_done(curSC);
+ goto out;
+}
+
+static void
+SYM53C500_release(dev_link_t *link)
+{
+ struct scsi_info_t *info = link->priv;
+ struct Scsi_Host *shost = info->host;
+
+ DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+
+ /*
+ * Do this before releasing/freeing resources.
+ */
+ scsi_remove_host(shost);
+
+ /*
+ * Interrupts getting hosed on card removal. Try
+ * the following code, mostly from qlogicfas.c.
+ */
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+ scsi_host_put(shost);
+} /* SYM53C500_release */
+
+static const char*
+SYM53C500_info(struct Scsi_Host *SChost)
+{
+ static char info_msg[256];
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SChost->hostdata;
+
+ DEB(printk("SYM53C500_info called\n"));
+ (void)snprintf(info_msg, sizeof(info_msg),
+ "SYM53C500 at 0x%lx, IRQ %d, %s PIO mode.",
+ SChost->io_port, SChost->irq, data->fast_pio ? "fast" : "slow");
+ return (info_msg);
+}
+
+static int
+SYM53C500_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int i;
+ int port_base = SCpnt->device->host->io_port;
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SCpnt->device->host->hostdata;
+
+ VDEB(printk("SYM53C500_queue called\n"));
+
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->request_bufflen));
+
+ VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+
+ data->current_SC = SCpnt;
+ data->current_SC->scsi_done = done;
+ data->current_SC->SCp.phase = command_ph;
+ data->current_SC->SCp.Status = 0;
+ data->current_SC->SCp.Message = 0;
+
+ /* We are locked here already by the mid layer */
+ REG0(port_base);
+ outb(SCpnt->device->id, port_base + DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, port_base + CMD_REG); /* reset the fifos */
+
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ outb(SCpnt->cmnd[i], port_base + SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, port_base + CMD_REG);
+
+ return 0;
+}
+
+static int
+SYM53C500_host_reset(struct scsi_cmnd *SCpnt)
+{
+ int port_base = SCpnt->device->host->io_port;
+
+ DEB(printk("SYM53C500_host_reset called\n"));
+ SYM53C500_int_host_reset(port_base);
+
+ return SUCCESS;
+}
+
+static int
+SYM53C500_biosparm(struct scsi_device *disk,
+ struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ int size;
+
+ DEB(printk("SYM53C500_biosparm called\n"));
+
+ size = capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size >> 11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255 * 63);
+ }
+ return 0;
+}
+
+static ssize_t
+SYM53C500_show_pio(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *SHp = class_to_shost(cdev);
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SHp->hostdata;
+
+ return snprintf(buf, 4, "%d\n", data->fast_pio);
+}
+
+static ssize_t
+SYM53C500_store_pio(struct class_device *cdev, const char *buf, size_t count)
+{
+ int pio;
+ struct Scsi_Host *SHp = class_to_shost(cdev);
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SHp->hostdata;
+
+ pio = simple_strtoul(buf, NULL, 0);
+ if (pio == 0 || pio == 1) {
+ data->fast_pio = pio;
+ return count;
+ }
+ else
+ return -EINVAL;
+}
+
+/*
+* SCSI HBA device attributes we want to
+* make available via sysfs.
+*/
+static struct class_device_attribute SYM53C500_pio_attr = {
+ .attr = {
+ .name = "fast_pio",
+ .mode = (S_IRUGO | S_IWUSR),
+ },
+ .show = SYM53C500_show_pio,
+ .store = SYM53C500_store_pio,
+};
+
+static struct class_device_attribute *SYM53C500_shost_attrs[] = {
+ &SYM53C500_pio_attr,
+ NULL,
+};
+
+/*
+* scsi_host_template initializer
+*/
+static struct scsi_host_template sym53c500_driver_template = {
+ .module = THIS_MODULE,
+ .name = "SYM53C500",
+ .info = SYM53C500_info,
+ .queuecommand = SYM53C500_queue,
+ .eh_host_reset_handler = SYM53C500_host_reset,
+ .bios_param = SYM53C500_biosparm,
+ .proc_name = "SYM53C500",
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = 32,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = SYM53C500_shost_attrs
+};
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void
+SYM53C500_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ int irq_level, port_base;
+ unsigned short tuple_data[32];
+ struct Scsi_Host *host;
+ struct scsi_host_template *tpnt = &sym53c500_driver_template;
+ struct sym53c500_data *data;
+
+ DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *)tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+ (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+ info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+
+ if (link->io.BasePort1 != 0) {
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /*
+ * That's the trouble with copying liberally from another driver.
+ * Some things probably aren't relevant, and I suspect this entire
+ * section dealing with manufacturer IDs can be scrapped. --rct
+ */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ /* set ATAcmd */
+ outb(0xb4, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+
+ /*
+ * irq_level == 0 implies tpnt->can_queue == 0, which
+ * is not supported in 2.6. Thus, only irq_level > 0
+ * will be allowed.
+ *
+ * Possible port_base values are as follows:
+ *
+ * 0x130, 0x230, 0x280, 0x290,
+ * 0x320, 0x330, 0x340, 0x350
+ */
+ port_base = link->io.BasePort1;
+ irq_level = link->irq.AssignedIRQ;
+
+ DEB(printk("SYM53C500: port_base=0x%x, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, USE_FAST_PIO);)
+
+ chip_init(port_base);
+
+ host = scsi_host_alloc(tpnt, sizeof(struct sym53c500_data));
+ if (!host) {
+ printk("SYM53C500: Unable to register host, giving up.\n");
+ goto err_release;
+ }
+
+ data = (struct sym53c500_data *)host->hostdata;
+
+ if (irq_level > 0) {
+ if (request_irq(irq_level, SYM53C500_intr, SA_SHIRQ, "SYM53C500", host)) {
+ printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
+ goto err_free_scsi;
+ }
+ DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
+ } else if (irq_level == 0) {
+ DEB(printk("SYM53C500: No interrupts detected\n"));
+ goto err_free_scsi;
+ } else {
+ DEB(printk("SYM53C500: Shouldn't get here!\n"));
+ goto err_free_scsi;
+ }
+
+ host->unique_id = port_base;
+ host->irq = irq_level;
+ host->io_port = port_base;
+ host->n_io_port = 0x10;
+ host->dma_channel = -1;
+
+ /*
+ * Note fast_pio is set to USE_FAST_PIO by
+ * default, but can be changed via "sysfs".
+ */
+ data->fast_pio = USE_FAST_PIO;
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ if (scsi_add_host(host, NULL))
+ goto err_free_irq;
+
+ scsi_scan_host(host);
+
+ goto out; /* SUCCESS */
+
+err_free_irq:
+ free_irq(irq_level, host);
+err_free_scsi:
+ scsi_host_put(host);
+err_release:
+ release_region(port_base, 0x10);
+ printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
+
+out:
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ SYM53C500_release(link);
+ return;
+} /* SYM53C500_config */
+
+static int
+SYM53C500_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct scsi_info_t *info = link->priv;
+
+ DEBUG(1, "SYM53C500_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ SYM53C500_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ /* See earlier comment about manufacturer IDs. */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ outb(0x80, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+ /*
+ * If things don't work after a "resume",
+ * this is a good place to start looking.
+ */
+ SYM53C500_int_host_reset(link->io.BasePort1);
+ }
+ break;
+ }
+ return 0;
+} /* SYM53C500_event */
+
+static void
+SYM53C500_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits. */
+ *linkp = link->next;
+ kfree(link->priv);
+ link->priv = NULL;
+} /* SYM53C500_detach */
+
+static dev_link_t *
+SYM53C500_attach(void)
+{
+ struct scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int ret;
+
+ DEBUG(0, "SYM53C500_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.event_handler = &SYM53C500_event;
+ client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ SYM53C500_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* SYM53C500_attach */
+
+MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
+MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
+MODULE_LICENSE("GPL");
+
+static struct pcmcia_driver sym53c500_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sym53c500_cs",
+ },
+ .attach = SYM53C500_attach,
+ .detach = SYM53C500_detach,
+};
+
+static int __init
+init_sym53c500_cs(void)
+{
+ return pcmcia_register_driver(&sym53c500_cs_driver);
+}
+
+static void __exit
+exit_sym53c500_cs(void)
+{
+ pcmcia_unregister_driver(&sym53c500_cs_driver);
+}
+
+module_init(init_sym53c500_cs);
+module_exit(exit_sym53c500_cs);
diff --git a/drivers/scsi/pluto.c b/drivers/scsi/pluto.c
new file mode 100644
index 000000000000..7bb0a2e56743
--- /dev/null
+++ b/drivers/scsi/pluto.c
@@ -0,0 +1,364 @@
+/* pluto.c: SparcSTORAGE Array SCSI host adapter driver.
+ *
+ * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "../fc4/fcp_impl.h"
+#include "pluto.h"
+
+#include <linux/module.h>
+
+/* #define PLUTO_DEBUG */
+
+#define pluto_printk printk ("PLUTO %s: ", fc->name); printk
+
+#ifdef PLUTO_DEBUG
+#define PLD(x) pluto_printk x;
+#define PLND(x) printk ("PLUTO: "); printk x;
+#else
+#define PLD(x)
+#define PLND(x)
+#endif
+
+static struct ctrl_inquiry {
+ struct Scsi_Host host;
+ struct pluto pluto;
+ Scsi_Cmnd cmd;
+ char inquiry[256];
+ fc_channel *fc;
+} *fcs __initdata = { 0 };
+static int fcscount __initdata = 0;
+static atomic_t fcss __initdata = ATOMIC_INIT(0);
+DECLARE_MUTEX_LOCKED(fc_sem);
+
+static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd);
+
+static void __init pluto_detect_timeout(unsigned long data)
+{
+ PLND(("Timeout\n"))
+ up(&fc_sem);
+}
+
+static void __init pluto_detect_done(Scsi_Cmnd *SCpnt)
+{
+ /* Do nothing */
+}
+
+static void __init pluto_detect_scsi_done(Scsi_Cmnd *SCpnt)
+{
+ SCpnt->request->rq_status = RQ_SCSI_DONE;
+ PLND(("Detect done %08lx\n", (long)SCpnt))
+ if (atomic_dec_and_test (&fcss))
+ up(&fc_sem);
+}
+
+int pluto_slave_configure(Scsi_Device *device)
+{
+ int depth_to_use;
+
+ if (device->tagged_supported)
+ depth_to_use = /* 254 */ 8;
+ else
+ depth_to_use = 2;
+
+ scsi_adjust_queue_depth(device,
+ (device->tagged_supported ?
+ MSG_SIMPLE_TAG : 0),
+ depth_to_use);
+
+ return 0;
+}
+
+/* Detect all SSAs attached to the machine.
+ To be fast, do it on all online FC channels at the same time. */
+int __init pluto_detect(Scsi_Host_Template *tpnt)
+{
+ int i, retry, nplutos;
+ fc_channel *fc;
+ Scsi_Device dev;
+ struct timer_list fc_timer =
+ TIMER_INITIALIZER(pluto_detect_timeout, 0, 0);
+
+ tpnt->proc_name = "pluto";
+ fcscount = 0;
+ for_each_online_fc_channel(fc) {
+ if (!fc->posmap)
+ fcscount++;
+ }
+ PLND(("%d channels online\n", fcscount))
+ if (!fcscount) {
+#if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KMOD)
+ request_module("soc");
+
+ for_each_online_fc_channel(fc) {
+ if (!fc->posmap)
+ fcscount++;
+ }
+ if (!fcscount)
+#endif
+ return 0;
+ }
+ fcs = (struct ctrl_inquiry *) kmalloc (sizeof (struct ctrl_inquiry) * fcscount, GFP_DMA);
+ if (!fcs) {
+ printk ("PLUTO: Not enough memory to probe\n");
+ return 0;
+ }
+
+ memset (fcs, 0, sizeof (struct ctrl_inquiry) * fcscount);
+ memset (&dev, 0, sizeof(dev));
+ atomic_set (&fcss, fcscount);
+
+ i = 0;
+ for_each_online_fc_channel(fc) {
+ Scsi_Cmnd *SCpnt;
+ struct Scsi_Host *host;
+ struct pluto *pluto;
+
+ if (i == fcscount) break;
+ if (fc->posmap) continue;
+
+ PLD(("trying to find SSA\n"))
+
+ /* If this is already registered to some other SCSI host, then it cannot be pluto */
+ if (fc->scsi_name[0]) continue;
+ memcpy (fc->scsi_name, "SSA", 4);
+
+ fcs[i].fc = fc;
+
+ fc->can_queue = PLUTO_CAN_QUEUE;
+ fc->rsp_size = 64;
+ fc->encode_addr = pluto_encode_addr;
+
+ fc->fcp_register(fc, TYPE_SCSI_FCP, 0);
+
+ SCpnt = &(fcs[i].cmd);
+ host = &(fcs[i].host);
+ pluto = (struct pluto *)host->hostdata;
+
+ pluto->fc = fc;
+
+ SCpnt->cmnd[0] = INQUIRY;
+ SCpnt->cmnd[4] = 255;
+
+ /* FC layer requires this, so that SCpnt->device->tagged_supported is initially 0 */
+ SCpnt->device = &dev;
+ dev.host = host;
+
+ SCpnt->cmd_len = COMMAND_SIZE(INQUIRY);
+
+ SCpnt->request->rq_status = RQ_SCSI_BUSY;
+
+ SCpnt->done = pluto_detect_done;
+ SCpnt->bufflen = 256;
+ SCpnt->buffer = fcs[i].inquiry;
+ SCpnt->request_bufflen = 256;
+ SCpnt->request_buffer = fcs[i].inquiry;
+ PLD(("set up %d %08lx\n", i, (long)SCpnt))
+ i++;
+ }
+
+ for (retry = 0; retry < 5; retry++) {
+ for (i = 0; i < fcscount; i++) {
+ if (!fcs[i].fc) break;
+ if (fcs[i].cmd.request->rq_status != RQ_SCSI_DONE) {
+ disable_irq(fcs[i].fc->irq);
+ PLND(("queuecommand %d %d\n", retry, i))
+ fcp_scsi_queuecommand (&(fcs[i].cmd),
+ pluto_detect_scsi_done);
+ enable_irq(fcs[i].fc->irq);
+ }
+ }
+
+ fc_timer.expires = jiffies + 10 * HZ;
+ add_timer(&fc_timer);
+
+ down(&fc_sem);
+ PLND(("Woken up\n"))
+ if (!atomic_read(&fcss))
+ break; /* All fc channels have answered us */
+ }
+ del_timer_sync(&fc_timer);
+
+ PLND(("Finished search\n"))
+ for (i = 0, nplutos = 0; i < fcscount; i++) {
+ Scsi_Cmnd *SCpnt;
+
+ if (!(fc = fcs[i].fc)) break;
+
+ SCpnt = &(fcs[i].cmd);
+
+ /* Let FC mid-level free allocated resources */
+ SCpnt->done (SCpnt);
+
+ if (!SCpnt->result) {
+ struct pluto_inquiry *inq;
+ struct pluto *pluto;
+ struct Scsi_Host *host;
+
+ inq = (struct pluto_inquiry *)fcs[i].inquiry;
+
+ if ((inq->dtype & 0x1f) == TYPE_PROCESSOR &&
+ !strncmp (inq->vendor_id, "SUN", 3) &&
+ !strncmp (inq->product_id, "SSA", 3)) {
+ char *p;
+ long *ages;
+
+ ages = kmalloc (((inq->channels + 1) * inq->targets) * sizeof(long), GFP_KERNEL);
+ if (!ages) continue;
+
+ host = scsi_register (tpnt, sizeof (struct pluto));
+ if(!host)
+ {
+ kfree(ages);
+ continue;
+ }
+
+ if (!try_module_get(fc->module)) {
+ kfree(ages);
+ scsi_unregister(host);
+ continue;
+ }
+
+ nplutos++;
+
+ pluto = (struct pluto *)host->hostdata;
+
+ host->max_id = inq->targets;
+ host->max_channel = inq->channels;
+ host->irq = fc->irq;
+
+ fc->channels = inq->channels + 1;
+ fc->targets = inq->targets;
+ fc->ages = ages;
+ memset (ages, 0, ((inq->channels + 1) * inq->targets) * sizeof(long));
+
+ pluto->fc = fc;
+ memcpy (pluto->rev_str, inq->revision, 4);
+ pluto->rev_str[4] = 0;
+ p = strchr (pluto->rev_str, ' ');
+ if (p) *p = 0;
+ memcpy (pluto->fw_rev_str, inq->fw_revision, 4);
+ pluto->fw_rev_str[4] = 0;
+ p = strchr (pluto->fw_rev_str, ' ');
+ if (p) *p = 0;
+ memcpy (pluto->serial_str, inq->serial, 12);
+ pluto->serial_str[12] = 0;
+ p = strchr (pluto->serial_str, ' ');
+ if (p) *p = 0;
+
+ PLD(("Found SSA rev %s fw rev %s serial %s %dx%d\n", pluto->rev_str, pluto->fw_rev_str, pluto->serial_str, host->max_channel, host->max_id))
+ } else
+ fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
+ } else
+ fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
+ }
+ kfree((char *)fcs);
+ if (nplutos)
+ printk ("PLUTO: Total of %d SparcSTORAGE Arrays found\n", nplutos);
+ return nplutos;
+}
+
+int pluto_release(struct Scsi_Host *host)
+{
+ struct pluto *pluto = (struct pluto *)host->hostdata;
+ fc_channel *fc = pluto->fc;
+
+ module_put(fc->module);
+
+ fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
+ PLND((" releasing pluto.\n"));
+ kfree (fc->ages);
+ PLND(("released pluto!\n"));
+ return 0;
+}
+
+const char *pluto_info(struct Scsi_Host *host)
+{
+ static char buf[128], *p;
+ struct pluto *pluto = (struct pluto *) host->hostdata;
+
+ sprintf(buf, "SUN SparcSTORAGE Array %s fw %s serial %s %dx%d on %s",
+ pluto->rev_str, pluto->fw_rev_str, pluto->serial_str,
+ host->max_channel, host->max_id, pluto->fc->name);
+#ifdef __sparc__
+ p = strchr(buf, 0);
+ sprintf(p, " PROM node %x", pluto->fc->dev->prom_node);
+#endif
+ return buf;
+}
+
+/* SSA uses this FC4S addressing:
+ switch (addr[0])
+ {
+ case 0: CONTROLLER - All of addr[1]..addr[3] has to be 0
+ case 1: SINGLE DISK - addr[1] channel, addr[2] id, addr[3] 0
+ case 2: DISK GROUP - ???
+ }
+
+ So that SCSI mid-layer can access to these, we reserve
+ channel 0 id 0 lun 0 for CONTROLLER
+ and channels 1 .. max_channel are normal single disks.
+ */
+static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd)
+{
+ PLND(("encode addr %d %d %d\n", SCpnt->device->channel, SCpnt->device->id, SCpnt->cmnd[1] & 0xe0))
+ /* We don't support LUNs - neither does SSA :) */
+ if (SCpnt->cmnd[1] & 0xe0)
+ return -EINVAL;
+ if (!SCpnt->device->channel) {
+ if (SCpnt->device->id)
+ return -EINVAL;
+ memset (addr, 0, 4 * sizeof(u16));
+ } else {
+ addr[0] = 1;
+ addr[1] = SCpnt->device->channel - 1;
+ addr[2] = SCpnt->device->id;
+ addr[3] = 0;
+ }
+ /* We're Point-to-Point, so target it to the default DID */
+ fcmd->did = fc->did;
+ PLND(("trying %04x%04x%04x%04x\n", addr[0], addr[1], addr[2], addr[3]))
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .name = "Sparc Storage Array 100/200",
+ .detect = pluto_detect,
+ .release = pluto_release,
+ .info = pluto_info,
+ .queuecommand = fcp_scsi_queuecommand,
+ .slave_configure = pluto_slave_configure,
+ .can_queue = PLUTO_CAN_QUEUE,
+ .this_id = -1,
+ .sg_tablesize = 1,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_abort_handler = fcp_scsi_abort,
+ .eh_device_reset_handler = fcp_scsi_dev_reset,
+ .eh_bus_reset_handler = fcp_scsi_bus_reset,
+ .eh_host_reset_handler = fcp_scsi_host_reset,
+};
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/pluto.h b/drivers/scsi/pluto.h
new file mode 100644
index 000000000000..beb844aafccd
--- /dev/null
+++ b/drivers/scsi/pluto.h
@@ -0,0 +1,47 @@
+/* pluto.h: SparcSTORAGE Array SCSI host adapter driver definitions.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#ifndef _PLUTO_H
+#define _PLUTO_H
+
+#include "../fc4/fcp_impl.h"
+
+struct pluto {
+ /* This must be first */
+ fc_channel *fc;
+ char rev_str[5];
+ char fw_rev_str[5];
+ char serial_str[13];
+};
+
+struct pluto_inquiry {
+ u8 dtype;
+ u8 removable:1, qualifier:7;
+ u8 iso:2, ecma:3, ansi:3;
+ u8 aenc:1, trmiop:1, :2, rdf:4;
+ u8 len;
+ u8 xxx1;
+ u8 xxx2;
+ u8 reladdr:1, wbus32:1, wbus16:1, sync:1, linked:1, :1, cmdque:1, softreset:1;
+ u8 vendor_id[8];
+ u8 product_id[16];
+ u8 revision[4];
+ u8 fw_revision[4];
+ u8 serial[12];
+ u8 xxx3[2];
+ u8 channels;
+ u8 targets;
+};
+
+/* This is the max number of outstanding SCSI commands per pluto */
+#define PLUTO_CAN_QUEUE 254
+
+int pluto_detect(Scsi_Host_Template *);
+int pluto_release(struct Scsi_Host *);
+const char * pluto_info(struct Scsi_Host *);
+int pluto_slave_configure(Scsi_Device *);
+
+#endif /* !(_PLUTO_H) */
+
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
new file mode 100644
index 000000000000..96b4522523d9
--- /dev/null
+++ b/drivers/scsi/ppa.c
@@ -0,0 +1,1150 @@
+/* ppa.c -- low level driver for the IOMEGA PPA3
+ * parallel port SCSI host adapter.
+ *
+ * (The PPA3 is the embedded controller in the ZIP drive.)
+ *
+ * (c) 1995,1996 Grant R. Guenther, grant@torque.net,
+ * under the terms of the GNU General Public License.
+ *
+ * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
+ * campbell@torque.net
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/parport.h>
+#include <linux/workqueue.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+
+static void ppa_reset_pulse(unsigned int base);
+
+typedef struct {
+ struct pardevice *dev; /* Parport device entry */
+ int base; /* Actual port address */
+ int mode; /* Transfer mode */
+ struct scsi_cmnd *cur_cmd; /* Current queued command */
+ struct work_struct ppa_tq; /* Polling interrupt stuff */
+ unsigned long jstart; /* Jiffies at start */
+ unsigned long recon_tmo; /* How many usecs to wait for reconnection (6th bit) */
+ unsigned int failed:1; /* Failure flag */
+ unsigned wanted:1; /* Parport sharing busy flag */
+ wait_queue_head_t *waiting;
+ struct Scsi_Host *host;
+ struct list_head list;
+} ppa_struct;
+
+#include "ppa.h"
+
+static inline ppa_struct *ppa_dev(struct Scsi_Host *host)
+{
+ return *(ppa_struct **)&host->hostdata;
+}
+
+static DEFINE_SPINLOCK(arbitration_lock);
+
+static void got_it(ppa_struct *dev)
+{
+ dev->base = dev->dev->port->base;
+ if (dev->cur_cmd)
+ dev->cur_cmd->SCp.phase = 1;
+ else
+ wake_up(dev->waiting);
+}
+
+static void ppa_wakeup(void *ref)
+{
+ ppa_struct *dev = (ppa_struct *) ref;
+ unsigned long flags;
+
+ spin_lock_irqsave(&arbitration_lock, flags);
+ if (dev->wanted) {
+ parport_claim(dev->dev);
+ got_it(dev);
+ dev->wanted = 0;
+ }
+ spin_unlock_irqrestore(&arbitration_lock, flags);
+ return;
+}
+
+static int ppa_pb_claim(ppa_struct *dev)
+{
+ unsigned long flags;
+ int res = 1;
+ spin_lock_irqsave(&arbitration_lock, flags);
+ if (parport_claim(dev->dev) == 0) {
+ got_it(dev);
+ res = 0;
+ }
+ dev->wanted = res;
+ spin_unlock_irqrestore(&arbitration_lock, flags);
+ return res;
+}
+
+static void ppa_pb_dismiss(ppa_struct *dev)
+{
+ unsigned long flags;
+ int wanted;
+ spin_lock_irqsave(&arbitration_lock, flags);
+ wanted = dev->wanted;
+ dev->wanted = 0;
+ spin_unlock_irqrestore(&arbitration_lock, flags);
+ if (!wanted)
+ parport_release(dev->dev);
+}
+
+static inline void ppa_pb_release(ppa_struct *dev)
+{
+ parport_release(dev->dev);
+}
+
+/*
+ * Start of Chipset kludges
+ */
+
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static inline int ppa_proc_write(ppa_struct *dev, char *buffer, int length)
+{
+ unsigned long x;
+
+ if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ dev->mode = x;
+ return length;
+ }
+ if ((length > 10) && (strncmp(buffer, "recon_tmo=", 10) == 0)) {
+ x = simple_strtoul(buffer + 10, NULL, 0);
+ dev->recon_tmo = x;
+ printk("ppa: recon_tmo set to %ld\n", x);
+ return length;
+ }
+ printk("ppa /proc: invalid variable\n");
+ return (-EINVAL);
+}
+
+static int ppa_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ int len = 0;
+ ppa_struct *dev = ppa_dev(host);
+
+ if (inout)
+ return ppa_proc_write(dev, buffer, length);
+
+ len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+ len +=
+ sprintf(buffer + len, "Parport : %s\n",
+ dev->dev->port->name);
+ len +=
+ sprintf(buffer + len, "Mode : %s\n",
+ PPA_MODE_STRING[dev->mode]);
+#if PPA_DEBUG > 0
+ len +=
+ sprintf(buffer + len, "recon_tmo : %lu\n", dev->recon_tmo);
+#endif
+
+ /* Request for beyond end of buffer */
+ if (offset > length)
+ return 0;
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+static int device_check(ppa_struct *dev);
+
+#if PPA_DEBUG > 0
+#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\
+ y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);
+static inline void ppa_fail_func(ppa_struct *dev, int error_code)
+#else
+static inline void ppa_fail(ppa_struct *dev, int error_code)
+#endif
+{
+ /* If we fail a device then we trash status / message bytes */
+ if (dev->cur_cmd) {
+ dev->cur_cmd->result = error_code << 16;
+ dev->failed = 1;
+ }
+}
+
+/*
+ * Wait for the high bit to be set.
+ *
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts. We spin on
+ * the 0x80 ready bit.
+ */
+static unsigned char ppa_wait(ppa_struct *dev)
+{
+ int k;
+ unsigned short ppb = dev->base;
+ unsigned char r;
+
+ k = PPA_SPIN_TMO;
+ /* Wait for bit 6 and 7 - PJC */
+ for (r = r_str(ppb); ((r & 0xc0) != 0xc0) && (k); k--) {
+ udelay(1);
+ r = r_str(ppb);
+ }
+
+ /*
+ * return some status information.
+ * Semantics: 0xc0 = ZIP wants more data
+ * 0xd0 = ZIP wants to send more data
+ * 0xe0 = ZIP is expecting SCSI command data
+ * 0xf0 = end of transfer, ZIP is sending status
+ */
+ if (k)
+ return (r & 0xf0);
+
+ /* Counter expired - Time out occurred */
+ ppa_fail(dev, DID_TIME_OUT);
+ printk("ppa timeout in ppa_wait\n");
+ return 0; /* command timed out */
+}
+
+/*
+ * Clear EPP Timeout Bit
+ */
+static inline void epp_reset(unsigned short ppb)
+{
+ int i;
+
+ i = r_str(ppb);
+ w_str(ppb, i);
+ w_str(ppb, i & 0xfe);
+}
+
+/*
+ * Wait for empty ECP fifo (if we are in ECP fifo mode only)
+ */
+static inline void ecp_sync(ppa_struct *dev)
+{
+ int i, ppb_hi = dev->dev->port->base_hi;
+
+ if (ppb_hi == 0)
+ return;
+
+ if ((r_ecr(ppb_hi) & 0xe0) == 0x60) { /* mode 011 == ECP fifo mode */
+ for (i = 0; i < 100; i++) {
+ if (r_ecr(ppb_hi) & 0x01)
+ return;
+ udelay(5);
+ }
+ printk("ppa: ECP sync failed as data still present in FIFO.\n");
+ }
+}
+
+static int ppa_byte_out(unsigned short base, const char *buffer, int len)
+{
+ int i;
+
+ for (i = len; i; i--) {
+ w_dtr(base, *buffer++);
+ w_ctr(base, 0xe);
+ w_ctr(base, 0xc);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static int ppa_byte_in(unsigned short base, char *buffer, int len)
+{
+ int i;
+
+ for (i = len; i; i--) {
+ *buffer++ = r_dtr(base);
+ w_ctr(base, 0x27);
+ w_ctr(base, 0x25);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static int ppa_nibble_in(unsigned short base, char *buffer, int len)
+{
+ for (; len; len--) {
+ unsigned char h;
+
+ w_ctr(base, 0x4);
+ h = r_str(base) & 0xf0;
+ w_ctr(base, 0x6);
+ *buffer++ = h | ((r_str(base) & 0xf0) >> 4);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static int ppa_out(ppa_struct *dev, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = dev->base;
+
+ r = ppa_wait(dev);
+
+ if ((r & 0x50) != 0x40) {
+ ppa_fail(dev, DID_ERROR);
+ return 0;
+ }
+ switch (dev->mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ /* 8 bit output, with a loop */
+ r = ppa_byte_out(ppb, buffer, len);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x4);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+ if (!(((long) buffer | len) & 0x01))
+ outsw(ppb + 4, buffer, len >> 1);
+#else
+ if (!(((long) buffer | len) & 0x03))
+ outsl(ppb + 4, buffer, len >> 2);
+#endif
+ else
+ outsb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0xc);
+ r = !(r_str(ppb) & 0x01);
+ w_ctr(ppb, 0xc);
+ ecp_sync(dev);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_out()\n");
+ r = 0;
+ }
+ return r;
+}
+
+static int ppa_in(ppa_struct *dev, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = dev->base;
+
+ r = ppa_wait(dev);
+
+ if ((r & 0x50) != 0x50) {
+ ppa_fail(dev, DID_ERROR);
+ return 0;
+ }
+ switch (dev->mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input, with a loop */
+ r = ppa_nibble_in(ppb, buffer, len);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_PS2:
+ /* 8 bit input, with a loop */
+ w_ctr(ppb, 0x25);
+ r = ppa_byte_in(ppb, buffer, len);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x24);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+ if (!(((long) buffer | len) & 0x01))
+ insw(ppb + 4, buffer, len >> 1);
+#else
+ if (!(((long) buffer | len) & 0x03))
+ insl(ppb + 4, buffer, len >> 2);
+#endif
+ else
+ insb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0x2c);
+ r = !(r_str(ppb) & 0x01);
+ w_ctr(ppb, 0x2c);
+ ecp_sync(dev);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_ins()\n");
+ r = 0;
+ break;
+ }
+ return r;
+}
+
+/* end of ppa_io.h */
+static inline void ppa_d_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+}
+
+static void ppa_disconnect(ppa_struct *dev)
+{
+ unsigned short ppb = dev->base;
+
+ ppa_d_pulse(ppb, 0);
+ ppa_d_pulse(ppb, 0x3c);
+ ppa_d_pulse(ppb, 0x20);
+ ppa_d_pulse(ppb, 0xf);
+}
+
+static inline void ppa_c_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0x6);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+}
+
+static inline void ppa_connect(ppa_struct *dev, int flag)
+{
+ unsigned short ppb = dev->base;
+
+ ppa_c_pulse(ppb, 0);
+ ppa_c_pulse(ppb, 0x3c);
+ ppa_c_pulse(ppb, 0x20);
+ if ((flag == CONNECT_EPP_MAYBE) && IN_EPP_MODE(dev->mode))
+ ppa_c_pulse(ppb, 0xcf);
+ else
+ ppa_c_pulse(ppb, 0x8f);
+}
+
+static int ppa_select(ppa_struct *dev, int target)
+{
+ int k;
+ unsigned short ppb = dev->base;
+
+ /*
+ * Bit 6 (0x40) is the device selected bit.
+ * First we must wait till the current device goes off line...
+ */
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ udelay(1);
+ } while ((r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ w_dtr(ppb, (1 << target));
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_dtr(ppb, 0x80); /* This is NOT the initator */
+ w_ctr(ppb, 0x8);
+
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ udelay(1);
+ }
+ while (!(r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ *
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ *
+ */
+static int ppa_init(ppa_struct *dev)
+{
+ int retv;
+ unsigned short ppb = dev->base;
+
+ ppa_disconnect(dev);
+ ppa_connect(dev, CONNECT_NORMAL);
+
+ retv = 2; /* Failed */
+
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
+
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ if (!retv)
+ ppa_reset_pulse(ppb);
+ udelay(1000); /* Allow devices to settle down */
+ ppa_disconnect(dev);
+ udelay(1000); /* Another delay to allow devices to settle */
+
+ if (retv)
+ return -EIO;
+
+ return device_check(dev);
+}
+
+static inline int ppa_send_command(struct scsi_cmnd *cmd)
+{
+ ppa_struct *dev = ppa_dev(cmd->device->host);
+ int k;
+
+ w_ctr(dev->base, 0x0c);
+
+ for (k = 0; k < cmd->cmd_len; k++)
+ if (!ppa_out(dev, &cmd->cmnd[k], 1))
+ return 0;
+ return 1;
+}
+
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ *
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int ppa_completion(struct scsi_cmnd *cmd)
+{
+ /* Return codes:
+ * -1 Error
+ * 0 Told to schedule
+ * 1 Finished data transfer
+ */
+ ppa_struct *dev = ppa_dev(cmd->device->host);
+ unsigned short ppb = dev->base;
+ unsigned long start_jiffies = jiffies;
+
+ unsigned char r, v;
+ int fast, bulk, status;
+
+ v = cmd->cmnd[0];
+ bulk = ((v == READ_6) ||
+ (v == READ_10) || (v == WRITE_6) || (v == WRITE_10));
+
+ /*
+ * We only get here if the drive is ready to comunicate,
+ * hence no need for a full ppa_wait.
+ */
+ r = (r_str(ppb) & 0xf0);
+
+ while (r != (unsigned char) 0xf0) {
+ /*
+ * If we have been running for more than a full timer tick
+ * then take a rest.
+ */
+ if (time_after(jiffies, start_jiffies + 1))
+ return 0;
+
+ if ((cmd->SCp.this_residual <= 0)) {
+ ppa_fail(dev, DID_ERROR);
+ return -1; /* ERROR_RETURN */
+ }
+
+ /* On some hardware we have SCSI disconnected (6th bit low)
+ * for about 100usecs. It is too expensive to wait a
+ * tick on every loop so we busy wait for no more than
+ * 500usecs to give the drive a chance first. We do not
+ * change things for "normal" hardware since generally
+ * the 6th bit is always high.
+ * This makes the CPU load higher on some hardware
+ * but otherwise we can not get more than 50K/secs
+ * on this problem hardware.
+ */
+ if ((r & 0xc0) != 0xc0) {
+ /* Wait for reconnection should be no more than
+ * jiffy/2 = 5ms = 5000 loops
+ */
+ unsigned long k = dev->recon_tmo;
+ for (; k && ((r = (r_str(ppb) & 0xf0)) & 0xc0) != 0xc0;
+ k--)
+ udelay(1);
+
+ if (!k)
+ return 0;
+ }
+
+ /* determine if we should use burst I/O */
+ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
+ ? PPA_BURST_SIZE : 1;
+
+ if (r == (unsigned char) 0xc0)
+ status = ppa_out(dev, cmd->SCp.ptr, fast);
+ else
+ status = ppa_in(dev, cmd->SCp.ptr, fast);
+
+ cmd->SCp.ptr += fast;
+ cmd->SCp.this_residual -= fast;
+
+ if (!status) {
+ ppa_fail(dev, DID_BUS_BUSY);
+ return -1; /* ERROR_RETURN */
+ }
+ if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ /* if scatter/gather, advance to the next segment */
+ if (cmd->SCp.buffers_residual--) {
+ cmd->SCp.buffer++;
+ cmd->SCp.this_residual =
+ cmd->SCp.buffer->length;
+ cmd->SCp.ptr =
+ page_address(cmd->SCp.buffer->page) +
+ cmd->SCp.buffer->offset;
+ }
+ }
+ /* Now check to see if the drive is ready to comunicate */
+ r = (r_str(ppb) & 0xf0);
+ /* If not, drop back down to the scheduler and wait a timer tick */
+ if (!(r & 0x80))
+ return 0;
+ }
+ return 1; /* FINISH_RETURN */
+}
+
+/*
+ * Since the PPA itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void ppa_interrupt(void *data)
+{
+ ppa_struct *dev = (ppa_struct *) data;
+ struct scsi_cmnd *cmd = dev->cur_cmd;
+
+ if (!cmd) {
+ printk("PPA: bug in ppa_interrupt\n");
+ return;
+ }
+ if (ppa_engine(dev, cmd)) {
+ dev->ppa_tq.data = (void *) dev;
+ schedule_delayed_work(&dev->ppa_tq, 1);
+ return;
+ }
+ /* Command must of completed hence it is safe to let go... */
+#if PPA_DEBUG > 0
+ switch ((cmd->result >> 16) & 0xff) {
+ case DID_OK:
+ break;
+ case DID_NO_CONNECT:
+ printk("ppa: no device at SCSI ID %i\n", cmd->device->target);
+ break;
+ case DID_BUS_BUSY:
+ printk("ppa: BUS BUSY - EPP timeout detected\n");
+ break;
+ case DID_TIME_OUT:
+ printk("ppa: unknown timeout\n");
+ break;
+ case DID_ABORT:
+ printk("ppa: told to abort\n");
+ break;
+ case DID_PARITY:
+ printk("ppa: parity error (???)\n");
+ break;
+ case DID_ERROR:
+ printk("ppa: internal driver error\n");
+ break;
+ case DID_RESET:
+ printk("ppa: told to reset device\n");
+ break;
+ case DID_BAD_INTR:
+ printk("ppa: bad interrupt (???)\n");
+ break;
+ default:
+ printk("ppa: bad return code (%02x)\n",
+ (cmd->result >> 16) & 0xff);
+ }
+#endif
+
+ if (cmd->SCp.phase > 1)
+ ppa_disconnect(dev);
+
+ ppa_pb_dismiss(dev);
+
+ dev->cur_cmd = NULL;
+
+ cmd->scsi_done(cmd);
+}
+
+static int ppa_engine(ppa_struct *dev, struct scsi_cmnd *cmd)
+{
+ unsigned short ppb = dev->base;
+ unsigned char l = 0, h = 0;
+ int retv;
+
+ /* First check for any errors that may of occurred
+ * Here we check for internal errors
+ */
+ if (dev->failed)
+ return 0;
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Phase 0 - Waiting for parport */
+ if (time_after(jiffies, dev->jstart + HZ)) {
+ /*
+ * We waited more than a second
+ * for parport to call us
+ */
+ ppa_fail(dev, DID_BUS_BUSY);
+ return 0;
+ }
+ return 1; /* wait until ppa_wakeup claims parport */
+ case 1: /* Phase 1 - Connected */
+ { /* Perform a sanity check for cable unplugged */
+ int retv = 2; /* Failed */
+
+ ppa_connect(dev, CONNECT_EPP_MAYBE);
+
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
+
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ if (retv) {
+ if ((jiffies - dev->jstart) > (1 * HZ)) {
+ printk
+ ("ppa: Parallel port cable is unplugged!!\n");
+ ppa_fail(dev, DID_BUS_BUSY);
+ return 0;
+ } else {
+ ppa_disconnect(dev);
+ return 1; /* Try again in a jiffy */
+ }
+ }
+ cmd->SCp.phase++;
+ }
+
+ case 2: /* Phase 2 - We are now talking to the scsi bus */
+ if (!ppa_select(dev, cmd->device->id)) {
+ ppa_fail(dev, DID_NO_CONNECT);
+ return 0;
+ }
+ cmd->SCp.phase++;
+
+ case 3: /* Phase 3 - Ready to accept a command */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ if (!ppa_send_command(cmd))
+ return 0;
+ cmd->SCp.phase++;
+
+ case 4: /* Phase 4 - Setup scatter/gather buffers */
+ if (cmd->use_sg) {
+ /* if many buffers are available, start filling the first */
+ cmd->SCp.buffer =
+ (struct scatterlist *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr =
+ page_address(cmd->SCp.buffer->page) +
+ cmd->SCp.buffer->offset;
+ } else {
+ /* else fill the only available buffer */
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.ptr = cmd->request_buffer;
+ }
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.phase++;
+
+ case 5: /* Phase 5 - Data transfer stage */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ retv = ppa_completion(cmd);
+ if (retv == -1)
+ return 0;
+ if (retv == 0)
+ return 1;
+ cmd->SCp.phase++;
+
+ case 6: /* Phase 6 - Read status/message */
+ cmd->result = DID_OK << 16;
+ /* Check for data overrun */
+ if (ppa_wait(dev) != (unsigned char) 0xf0) {
+ ppa_fail(dev, DID_ERROR);
+ return 0;
+ }
+ if (ppa_in(dev, &l, 1)) { /* read status byte */
+ /* Check for optional message byte */
+ if (ppa_wait(dev) == (unsigned char) 0xf0)
+ ppa_in(dev, &h, 1);
+ cmd->result =
+ (DID_OK << 16) + (h << 8) + (l & STATUS_MASK);
+ }
+ return 0; /* Finished */
+ break;
+
+ default:
+ printk("ppa: Invalid scsi phase\n");
+ }
+ return 0;
+}
+
+static int ppa_queuecommand(struct scsi_cmnd *cmd,
+ void (*done) (struct scsi_cmnd *))
+{
+ ppa_struct *dev = ppa_dev(cmd->device->host);
+
+ if (dev->cur_cmd) {
+ printk("PPA: bug in ppa_queuecommand\n");
+ return 0;
+ }
+ dev->failed = 0;
+ dev->jstart = jiffies;
+ dev->cur_cmd = cmd;
+ cmd->scsi_done = done;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0; /* bus free */
+
+ dev->ppa_tq.data = dev;
+ schedule_work(&dev->ppa_tq);
+
+ ppa_pb_claim(dev);
+
+ return 0;
+}
+
+/*
+ * Apparently the disk->capacity attribute is off by 1 sector
+ * for all disk drives. We add the one here, but it should really
+ * be done in sd.c. Even if it gets fixed there, this will still
+ * work.
+ */
+static int ppa_biosparam(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int ip[])
+{
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = ((unsigned long) capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
+}
+
+static int ppa_abort(struct scsi_cmnd *cmd)
+{
+ ppa_struct *dev = ppa_dev(cmd->device->host);
+ /*
+ * There is no method for aborting commands since Iomega
+ * have tied the SCSI_MESSAGE line high in the interface
+ */
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Do not have access to parport */
+ case 1: /* Have not connected to interface */
+ dev->cur_cmd = NULL; /* Forget the problem */
+ return SUCCESS;
+ break;
+ default: /* SCSI command sent, can not abort */
+ return FAILED;
+ break;
+ }
+}
+
+static void ppa_reset_pulse(unsigned int base)
+{
+ w_dtr(base, 0x40);
+ w_ctr(base, 0x8);
+ udelay(30);
+ w_ctr(base, 0xc);
+}
+
+static int ppa_reset(struct scsi_cmnd *cmd)
+{
+ ppa_struct *dev = ppa_dev(cmd->device->host);
+
+ if (cmd->SCp.phase)
+ ppa_disconnect(dev);
+ dev->cur_cmd = NULL; /* Forget the problem */
+
+ ppa_connect(dev, CONNECT_NORMAL);
+ ppa_reset_pulse(dev->base);
+ udelay(1000); /* device settle delay */
+ ppa_disconnect(dev);
+ udelay(1000); /* device settle delay */
+ return SUCCESS;
+}
+
+static int device_check(ppa_struct *dev)
+{
+ /* This routine looks for a device and then attempts to use EPP
+ to send a command. If all goes as planned then EPP is available. */
+
+ static char cmd[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ int loop, old_mode, status, k, ppb = dev->base;
+ unsigned char l;
+
+ old_mode = dev->mode;
+ for (loop = 0; loop < 8; loop++) {
+ /* Attempt to use EPP for Test Unit Ready */
+ if ((ppb & 0x0007) == 0x0000)
+ dev->mode = PPA_EPP_32;
+
+ second_pass:
+ ppa_connect(dev, CONNECT_EPP_MAYBE);
+ /* Select SCSI device */
+ if (!ppa_select(dev, loop)) {
+ ppa_disconnect(dev);
+ continue;
+ }
+ printk("ppa: Found device at ID %i, Attempting to use %s\n",
+ loop, PPA_MODE_STRING[dev->mode]);
+
+ /* Send SCSI command */
+ status = 1;
+ w_ctr(ppb, 0x0c);
+ for (l = 0; (l < 6) && (status); l++)
+ status = ppa_out(dev, cmd, 1);
+
+ if (!status) {
+ ppa_disconnect(dev);
+ ppa_connect(dev, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(dev);
+ udelay(1000);
+ if (dev->mode == PPA_EPP_32) {
+ dev->mode = old_mode;
+ goto second_pass;
+ }
+ return -EIO;
+ }
+ w_ctr(ppb, 0x0c);
+ k = 1000000; /* 1 Second */
+ do {
+ l = r_str(ppb);
+ k--;
+ udelay(1);
+ } while (!(l & 0x80) && (k));
+
+ l &= 0xf0;
+
+ if (l != 0xf0) {
+ ppa_disconnect(dev);
+ ppa_connect(dev, CONNECT_EPP_MAYBE);
+ ppa_reset_pulse(ppb);
+ udelay(1000);
+ ppa_disconnect(dev);
+ udelay(1000);
+ if (dev->mode == PPA_EPP_32) {
+ dev->mode = old_mode;
+ goto second_pass;
+ }
+ return -EIO;
+ }
+ ppa_disconnect(dev);
+ printk("ppa: Communication established with ID %i using %s\n",
+ loop, PPA_MODE_STRING[dev->mode]);
+ ppa_connect(dev, CONNECT_EPP_MAYBE);
+ ppa_reset_pulse(ppb);
+ udelay(1000);
+ ppa_disconnect(dev);
+ udelay(1000);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static struct scsi_host_template ppa_template = {
+ .module = THIS_MODULE,
+ .proc_name = "ppa",
+ .proc_info = ppa_proc_info,
+ .name = "Iomega VPI0 (ppa) interface",
+ .queuecommand = ppa_queuecommand,
+ .eh_abort_handler = ppa_abort,
+ .eh_bus_reset_handler = ppa_reset,
+ .eh_host_reset_handler = ppa_reset,
+ .bios_param = ppa_biosparam,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .can_queue = 1,
+};
+
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
+
+static LIST_HEAD(ppa_hosts);
+
+static int __ppa_attach(struct parport *pb)
+{
+ struct Scsi_Host *host;
+ DECLARE_WAIT_QUEUE_HEAD(waiting);
+ DEFINE_WAIT(wait);
+ ppa_struct *dev;
+ int ports;
+ int modes, ppb, ppb_hi;
+ int err = -ENOMEM;
+
+ dev = kmalloc(sizeof(ppa_struct), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ memset(dev, 0, sizeof(ppa_struct));
+ dev->base = -1;
+ dev->mode = PPA_AUTODETECT;
+ dev->recon_tmo = PPA_RECON_TMO;
+ init_waitqueue_head(&waiting);
+ dev->dev = parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+ NULL, 0, dev);
+
+ if (!dev->dev)
+ goto out;
+
+ /* Claim the bus so it remembers what we do to the control
+ * registers. [ CTR and ECP ]
+ */
+ err = -EBUSY;
+ dev->waiting = &waiting;
+ prepare_to_wait(&waiting, &wait, TASK_UNINTERRUPTIBLE);
+ if (ppa_pb_claim(dev))
+ schedule_timeout(3 * HZ);
+ if (dev->wanted) {
+ printk(KERN_ERR "ppa%d: failed to claim parport because "
+ "a pardevice is owning the port for too long "
+ "time!\n", pb->number);
+ ppa_pb_dismiss(dev);
+ dev->waiting = NULL;
+ finish_wait(&waiting, &wait);
+ goto out1;
+ }
+ dev->waiting = NULL;
+ finish_wait(&waiting, &wait);
+ ppb = dev->base = dev->dev->port->base;
+ ppb_hi = dev->dev->port->base_hi;
+ w_ctr(ppb, 0x0c);
+ modes = dev->dev->port->modes;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ dev->mode = PPA_NIBBLE;
+
+ if (modes & PARPORT_MODE_TRISTATE)
+ dev->mode = PPA_PS2;
+
+ if (modes & PARPORT_MODE_ECP) {
+ w_ecr(ppb_hi, 0x20);
+ dev->mode = PPA_PS2;
+ }
+ if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP))
+ w_ecr(ppb_hi, 0x80);
+
+ /* Done configuration */
+
+ err = ppa_init(dev);
+ ppa_pb_release(dev);
+
+ if (err)
+ goto out1;
+
+ /* now the glue ... */
+ if (dev->mode == PPA_NIBBLE || dev->mode == PPA_PS2)
+ ports = 3;
+ else
+ ports = 8;
+
+ INIT_WORK(&dev->ppa_tq, ppa_interrupt, dev);
+
+ err = -ENOMEM;
+ host = scsi_host_alloc(&ppa_template, sizeof(ppa_struct *));
+ if (!host)
+ goto out1;
+ host->io_port = pb->base;
+ host->n_io_port = ports;
+ host->dma_channel = -1;
+ host->unique_id = pb->number;
+ *(ppa_struct **)&host->hostdata = dev;
+ dev->host = host;
+ list_add_tail(&dev->list, &ppa_hosts);
+ err = scsi_add_host(host, NULL);
+ if (err)
+ goto out2;
+ scsi_scan_host(host);
+ return 0;
+out2:
+ list_del_init(&dev->list);
+ scsi_host_put(host);
+out1:
+ parport_unregister_device(dev->dev);
+out:
+ kfree(dev);
+ return err;
+}
+
+static void ppa_attach(struct parport *pb)
+{
+ __ppa_attach(pb);
+}
+
+static void ppa_detach(struct parport *pb)
+{
+ ppa_struct *dev;
+ list_for_each_entry(dev, &ppa_hosts, list) {
+ if (dev->dev->port == pb) {
+ list_del_init(&dev->list);
+ scsi_remove_host(dev->host);
+ scsi_host_put(dev->host);
+ parport_unregister_device(dev->dev);
+ kfree(dev);
+ break;
+ }
+ }
+}
+
+static struct parport_driver ppa_driver = {
+ .name = "ppa",
+ .attach = ppa_attach,
+ .detach = ppa_detach,
+};
+
+static int __init ppa_driver_init(void)
+{
+ printk("ppa: Version %s\n", PPA_VERSION);
+ return parport_register_driver(&ppa_driver);
+}
+
+static void __exit ppa_driver_exit(void)
+{
+ parport_unregister_driver(&ppa_driver);
+}
+
+module_init(ppa_driver_init);
+module_exit(ppa_driver_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h
new file mode 100644
index 000000000000..f6e1a1574bb8
--- /dev/null
+++ b/drivers/scsi/ppa.h
@@ -0,0 +1,151 @@
+/* Driver for the PPA3 parallel port SCSI HBA embedded in
+ * the Iomega ZIP drive
+ *
+ * (c) 1996 Grant R. Guenther grant@torque.net
+ * David Campbell campbell@torque.net
+ *
+ * All comments to David.
+ */
+
+#ifndef _PPA_H
+#define _PPA_H
+
+#define PPA_VERSION "2.07 (for Linux 2.4.x)"
+
+/*
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather. [0.26-athena]
+ *
+ * additional hacks by David Campbell
+ * in response to this driver "mis-behaving" on his machine.
+ * Fixed EPP to handle "software" changing of EPP port data direction.
+ * Chased down EPP timeouts
+ * Made this driver "kernel version friendly" [0.28-athena]
+ *
+ * [ Stuff removed ]
+ *
+ * Corrected ppa.h for 2.1.x kernels (>=2.1.85)
+ * Modified "Nat Semi Kludge" for extended chipsets
+ * [1.41]
+ *
+ * Fixed id_probe for EPP 1.9 chipsets (misdetected as EPP 1.7)
+ * [1.42]
+ *
+ * Development solely for 2.1.x kernels from now on!
+ * [2.00]
+ *
+ * Hack and slash at the init code (EPP device check routine)
+ * Added INSANE option.
+ * [2.01]
+ *
+ * Patch applied to sync against the 2.1.x kernel code
+ * Included qboot_zip.sh
+ * [2.02]
+ *
+ * Cleaned up the mess left by someone else trying to fix the
+ * asm section to keep egcc happy. The asm section no longer
+ * exists, the nibble code is *almost* as fast as the asm code
+ * providing it is compiled with egcc.
+ *
+ * Other clean ups include the follow changes:
+ * CONFIG_SCSI_PPA_HAVE_PEDANTIC => CONFIG_SCSI_IZIP_EPP16
+ * added CONFIG_SCSI_IZIP_SLOW_CTR option
+ * [2.03]
+ *
+ * Use ppa_wait() to check for ready AND connected status bits
+ * Add ppa_wait() calls to ppa_completion()
+ * by Peter Cherriman <pjc@ecs.soton.ac.uk> and
+ * Tim Waugh <twaugh@redhat.com>
+ * [2.04]
+ *
+ * Fix kernel panic on scsi timeout, 2000-08-18 [2.05]
+ *
+ * Avoid io_request_lock problems.
+ * John Cavan <johncavan@home.com> [2.06]
+ *
+ * Busy wait for connected status bit in ppa_completion()
+ * in order to cope with some hardware that has this bit low
+ * for short periods of time.
+ * Add udelay() to ppa_select()
+ * by Peter Cherriman <pjc@ecs.soton.ac.uk> and
+ * Oleg Makarenko <omakarenko@cyberplat.ru>
+ * [2.07]
+ */
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <scsi/scsi_host.h>
+/* batteries not included :-) */
+
+/*
+ * modes in which the driver can operate
+ */
+#define PPA_AUTODETECT 0 /* Autodetect mode */
+#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
+#define PPA_PS2 2 /* PS/2 byte mode */
+#define PPA_EPP_8 3 /* EPP mode, 8 bit */
+#define PPA_EPP_16 4 /* EPP mode, 16 bit */
+#define PPA_EPP_32 5 /* EPP mode, 32 bit */
+#define PPA_UNKNOWN 6 /* Just in case... */
+
+static char *PPA_MODE_STRING[] =
+{
+ "Autodetect",
+ "SPP",
+ "PS/2",
+ "EPP 8 bit",
+ "EPP 16 bit",
+#ifdef CONFIG_SCSI_IZIP_EPP16
+ "EPP 16 bit",
+#else
+ "EPP 32 bit",
+#endif
+ "Unknown"};
+
+/* other options */
+#define PPA_BURST_SIZE 512 /* data burst size */
+#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
+#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */
+#define PPA_RECON_TMO 500 /* scsi reconnection loop limiter */
+#define PPA_DEBUG 0 /* debugging option */
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL 0
+
+#define r_dtr(x) (unsigned char)inb((x))
+#define r_str(x) (unsigned char)inb((x)+1)
+#define r_ctr(x) (unsigned char)inb((x)+2)
+#define r_epp(x) (unsigned char)inb((x)+4)
+#define r_fifo(x) (unsigned char)inb((x)) /* x must be base_hi */
+ /* On PCI is base+0x400 != base_hi */
+#define r_ecr(x) (unsigned char)inb((x)+0x2) /* x must be base_hi */
+
+#define w_dtr(x,y) outb(y, (x))
+#define w_str(x,y) outb(y, (x)+1)
+#define w_epp(x,y) outb(y, (x)+4)
+#define w_fifo(x,y) outb(y, (x)) /* x must be base_hi */
+#define w_ecr(x,y) outb(y, (x)+0x2)/* x must be base_hi */
+
+#ifdef CONFIG_SCSI_IZIP_SLOW_CTR
+#define w_ctr(x,y) outb_p(y, (x)+2)
+#else
+#define w_ctr(x,y) outb(y, (x)+2)
+#endif
+
+static int ppa_engine(ppa_struct *, struct scsi_cmnd *);
+
+#endif /* _PPA_H */
diff --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c
new file mode 100644
index 000000000000..0f576d4ad0dd
--- /dev/null
+++ b/drivers/scsi/psi240i.c
@@ -0,0 +1,685 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi240i.c
+ *
+ * Description: SCSI driver for the PSI240I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXBOARDS 6 /* Increase this and the sizes of the arrays below, if you need more. */
+
+#define PORT_DATA 0
+#define PORT_ERROR 1
+#define PORT_SECTOR_COUNT 2
+#define PORT_LBA_0 3
+#define PORT_LBA_8 4
+#define PORT_LBA_16 5
+#define PORT_LBA_24 6
+#define PORT_STAT_CMD 7
+#define PORT_SEL_FAIL 8
+#define PORT_IRQ_STATUS 9
+#define PORT_ADDRESS 10
+#define PORT_FAIL 11
+#define PORT_ALT_STAT 12
+
+typedef struct
+ {
+ UCHAR device; // device code
+ UCHAR byte6; // device select register image
+ UCHAR spigot; // spigot number
+ UCHAR expectingIRQ; // flag for expecting and interrupt
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ USHORT spareword; // placeholder
+ ULONG blocks; // number of blocks on device
+ } OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+ {
+ USHORT ports[13];
+ OUR_DEVICE device[8];
+ Scsi_Cmnd *pSCmnd;
+ IDE_STRUCT ide;
+ ULONG startSector;
+ USHORT sectorCount;
+ Scsi_Cmnd *SCpnt;
+ VOID *buffer;
+ USHORT expectingIRQ;
+ } ADAPTER240I, *PADAPTER240I;
+
+#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata)
+
+static struct Scsi_Host *PsiHost[6] = {NULL,}; /* One for each IRQ level (10-15) */
+static IDENTIFY_DATA identifyData;
+static SETUP ChipSetup;
+
+static USHORT portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5};
+
+/****************************************************************
+ * Name: WriteData :LOCAL
+ *
+ * Description: Write data to device.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER240I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+
+ timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value
+ do {
+ if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+ {
+ outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256);
+ return 0;
+ }
+ } while ( time_after(timer, jiffies) ); // test for timeout
+
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ return 1;
+ }
+/****************************************************************
+ * Name: IdeCmd :LOCAL
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER240I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+ UCHAR status;
+
+ outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]); // select the spigot
+ outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive
+ timer = jiffies + TIMEOUT_READY; // calculate the timeout value
+ do {
+ status = inb_p (padapter->ports[PORT_STAT_CMD]);
+ if ( status & IDE_STATUS_DRDY )
+ {
+ outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+ outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+ outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+ outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+ padapter->expectingIRQ = 1;
+ outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+ if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+ return (WriteData (padapter));
+
+ return 0;
+ }
+ } while ( time_after(timer, jiffies) ); // test for timeout
+
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ return status;
+ }
+/****************************************************************
+ * Name: SetupTransfer :LOCAL
+ *
+ * Description: Setup a data transfer command.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * drive - Drive/head register upper nibble only.
+ *
+ * Returns: TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER240I padapter, UCHAR drive)
+ {
+ if ( padapter->sectorCount )
+ {
+ *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+ padapter->ide.ide.ide[6] |= drive;
+ padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+ padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer
+ padapter->startSector += padapter->ide.ide.ides.sectors;
+ return 0;
+ }
+ else
+ {
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ padapter->SCpnt = NULL;
+ return 1;
+ }
+ }
+/****************************************************************
+ * Name: DecodeError :LOCAL
+ *
+ * Description: Decode and process device errors.
+ *
+ * Parameters: pshost - Pointer to host data block.
+ * status - Status register code.
+ *
+ * Returns: The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+ {
+ PADAPTER240I padapter = HOSTDATA(pshost);
+ UCHAR error;
+
+ padapter->expectingIRQ = 0;
+ padapter->SCpnt = NULL;
+ if ( status & IDE_STATUS_WRITE_FAULT )
+ {
+ return DID_PARITY << 16;
+ }
+ if ( status & IDE_STATUS_BUSY )
+ return DID_BUS_BUSY << 16;
+
+ error = inb_p (padapter->ports[PORT_ERROR]);
+ DEB(printk ("\npsi240i error register: %x", error));
+ switch ( error )
+ {
+ case IDE_ERROR_AMNF:
+ case IDE_ERROR_TKONF:
+ case IDE_ERROR_ABRT:
+ case IDE_ERROR_IDFN:
+ case IDE_ERROR_UNC:
+ case IDE_ERROR_BBK:
+ default:
+ return DID_ERROR << 16;
+ }
+ return DID_ERROR << 16;
+ }
+/****************************************************************
+ * Name: Irq_Handler :LOCAL
+ *
+ * Description: Interrupt handler.
+ *
+ * Parameters: irq - Hardware IRQ number.
+ * dev_id -
+ * regs -
+ *
+ * Returns: TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+ {
+ struct Scsi_Host *shost; // Pointer to host data block
+ PADAPTER240I padapter; // Pointer to adapter control structure
+ USHORT *pports; // I/O port array
+ Scsi_Cmnd *SCpnt;
+ UCHAR status;
+ int z;
+
+ DEB(printk ("\npsi240i received interrupt\n"));
+
+ shost = PsiHost[irq - 10];
+ if ( !shost )
+ panic ("Splunge!");
+
+ padapter = HOSTDATA(shost);
+ pports = padapter->ports;
+ SCpnt = padapter->SCpnt;
+
+ if ( !padapter->expectingIRQ )
+ {
+ DEB(printk ("\npsi240i Unsolicited interrupt\n"));
+ return;
+ }
+ padapter->expectingIRQ = 0;
+
+ status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ goto irqerror;
+
+ DEB(printk ("\npsi240i processing interrupt"));
+ switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt
+ {
+ case IDE_CMD_READ_MULTIPLE:
+ if ( status & IDE_STATUS_DRQ )
+ {
+ insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256);
+ padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+ if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+ {
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ if ( !(status = IdeCmd (padapter)) )
+ return;
+ }
+ break;
+
+ case IDE_CMD_WRITE_MULTIPLE:
+ padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+ if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+ {
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ if ( !(status = IdeCmd (padapter)) )
+ return;
+ break;
+
+ case IDE_COMMAND_IDENTIFY:
+ {
+ PINQUIRYDATA pinquiryData = SCpnt->request_buffer;
+
+ if ( status & IDE_STATUS_DRQ )
+ {
+ insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+ memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure.
+ pinquiryData->DeviceType = 0;
+ pinquiryData->Versions = 2;
+ pinquiryData->AdditionalLength = 35 - 4;
+
+ // Fill in vendor identification fields.
+ for ( z = 0; z < 20; z += 2 )
+ {
+ pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+ pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+ }
+
+ // Initialize unused portion of product id.
+ for ( z = 0; z < 4; z++ )
+ pinquiryData->ProductId[12 + z] = ' ';
+
+ // Move firmware revision from IDENTIFY data to
+ // product revision in INQUIRY data.
+ for ( z = 0; z < 4; z += 2 )
+ {
+ pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+ pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+ }
+
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ break;
+ }
+
+ default:
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+
+irqerror:;
+ DEB(printk ("\npsi240i error Device Status: %X\n", status));
+ SCpnt->result = DecodeError (shost, status);
+ SCpnt->scsi_done (SCpnt);
+ }
+
+static irqreturn_t do_Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ Irq_Handler(irq, dev_id, regs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/****************************************************************
+ * Name: Psi240i_QueueCommand
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * done - Pointer to done function to call.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+static int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER240I padapter = HOSTDATA (SCpnt->device->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device [SCpnt->device->id];// Pointer to device information
+ UCHAR rc; // command return code
+
+ SCpnt->scsi_done = done;
+ padapter->ide.ide.ides.spigot = pdev->spigot;
+ padapter->buffer = SCpnt->request_buffer;
+ if (done)
+ {
+ if ( !pdev->device )
+ {
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done (SCpnt);
+ return 0;
+ }
+ }
+ else
+ {
+ printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ switch ( *cdb )
+ {
+ case SCSIOP_INQUIRY: // inquiry CDB
+ {
+ padapter->ide.ide.ide[6] = pdev->byte6;
+ padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+ break;
+ }
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ SCpnt->result = DID_OK << 16;
+ done (SCpnt);
+ return 0;
+
+ case SCSIOP_READ_CAPACITY: // read capctiy CDB
+ {
+ PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+ pdata->blksiz = 0x20000;
+ XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+ SCpnt->result = DID_OK << 16;
+ done (SCpnt);
+ return 0;
+ }
+
+ case SCSIOP_VERIFY: // verify CDB
+ *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+ padapter->ide.ide.ide[6] |= pdev->byte6;
+ padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+ padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+ break;
+
+ case SCSIOP_READ: // read10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+ break;
+
+ case SCSIOP_READ6: // read6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+ break;
+
+ case SCSIOP_WRITE: // write10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+ break;
+ case SCSIOP_WRITE6: // write6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+ break;
+
+ default:
+ DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+
+ padapter->SCpnt = SCpnt; // Save this command data
+
+ rc = IdeCmd (padapter);
+ if ( rc )
+ {
+ padapter->expectingIRQ = 0;
+ DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+ DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd));
+ return 0;
+ }
+
+/***************************************************************************
+ * Name: ReadChipMemory
+ *
+ * Description: Read information from controller memory.
+ *
+ * Parameters: psetup - Pointer to memory image of setup information.
+ * base - base address of memory.
+ * length - lenght of data space in bytes.
+ * port - I/O address of data port.
+ *
+ * Returns: Nothing.
+ *
+ **************************************************************************/
+static void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port)
+ {
+ USHORT z, zz;
+ UCHAR *pd = (UCHAR *)pdata;
+ outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup data port
+ zz = 0;
+ while ( zz < length )
+ {
+ outw_p (base, port + REG_ADDRESS); // setup address
+
+ for ( z = 0; z < 8; z++ )
+ {
+ if ( (zz + z) < length )
+ *pd++ = inb_p (port + z); // read data byte
+ }
+ zz += 8;
+ base += 8;
+ }
+ }
+/****************************************************************
+ * Name: Psi240i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters found.
+ *
+ ****************************************************************/
+static int Psi240i_Detect (Scsi_Host_Template *tpnt)
+ {
+ int board;
+ int count = 0;
+ int unit;
+ int z;
+ USHORT port, port_range = 16;
+ CHIP_CONFIG_N chipConfig;
+ CHIP_DEVICE_N chipDevice[8];
+ struct Scsi_Host *pshost;
+
+ for ( board = 0; board < MAXBOARDS; board++ ) // scan for I/O ports
+ {
+ pshost = NULL;
+ port = portAddr[board]; // get base address to test
+ if ( !request_region (port, port_range, "psi240i") )
+ continue;
+ if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us
+ goto host_init_failure;
+ outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup EEPROM/RAM access
+ outw (0, port + REG_ADDRESS); // setup EEPROM address zero
+ if ( inb_p (port) != 0x55 ) // test 1st byte
+ goto host_init_failure; // nope
+ if ( inb_p (port + 1) != 0xAA ) // test 2nd byte
+ goto host_init_failure; // nope
+
+ // at this point our board is found and can be accessed. Now we need to initialize
+ // our informatation and register with the kernel.
+
+
+ ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port);
+ ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port);
+ ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port);
+
+ if ( !chipConfig.numDrives ) // if no devices on this board
+ goto host_init_failure;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER240I));
+ if(pshost == NULL)
+ goto host_init_failure;
+
+ PsiHost[chipConfig.irq - 10] = pshost;
+ pshost->unique_id = port;
+ pshost->io_port = port;
+ pshost->n_io_port = 16; /* Number of bytes of I/O space used */
+ pshost->irq = chipConfig.irq;
+
+ for ( z = 0; z < 11; z++ ) // build regester address array
+ HOSTDATA(pshost)->ports[z] = port + z;
+ HOSTDATA(pshost)->ports[11] = port + REG_FAIL;
+ HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT;
+ DEB (printk ("\nPorts ="));
+ DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]););
+
+ for ( z = 0; z < chipConfig.numDrives; ++z )
+ {
+ unit = chipDevice[z].channel & 0x0F;
+ HOSTDATA(pshost)->device[unit].device = ChipSetup.setupDevice[unit].device;
+ HOSTDATA(pshost)->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+ HOSTDATA(pshost)->device[unit].spigot = (UCHAR)(1 << (unit >> 1));
+ HOSTDATA(pshost)->device[unit].sectors = ChipSetup.setupDevice[unit].sectors;
+ HOSTDATA(pshost)->device[unit].heads = ChipSetup.setupDevice[unit].heads;
+ HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders;
+ HOSTDATA(pshost)->device[unit].blocks = ChipSetup.setupDevice[unit].blocks;
+ DEB (printk ("\nHOSTDATA->device = %X", HOSTDATA(pshost)->device[unit].device));
+ DEB (printk ("\n byte6 = %X", HOSTDATA(pshost)->device[unit].byte6));
+ DEB (printk ("\n spigot = %X", HOSTDATA(pshost)->device[unit].spigot));
+ DEB (printk ("\n sectors = %X", HOSTDATA(pshost)->device[unit].sectors));
+ DEB (printk ("\n heads = %X", HOSTDATA(pshost)->device[unit].heads));
+ DEB (printk ("\n cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders));
+ DEB (printk ("\n blocks = %lX", HOSTDATA(pshost)->device[unit].blocks));
+ }
+
+ if ( request_irq (chipConfig.irq, do_Irq_Handler, 0, "psi240i", pshost) == 0 )
+ {
+ printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x IRQ = %d\n", port, chipConfig.irq);
+ printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+ count++;
+ continue;
+ }
+
+ printk ("Unable to allocate IRQ for PSI-240I controller.\n");
+
+host_init_failure:
+
+ release_region (port, port_range);
+ if (pshost)
+ scsi_unregister (pshost);
+
+ }
+ return count;
+ }
+
+static int Psi240i_Release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+/****************************************************************
+ * Name: Psi240i_BiosParam
+ *
+ * Description: Process the biosparam request from the SCSI manager to
+ * return C/H/S data.
+ *
+ * Parameters: disk - Pointer to SCSI disk structure.
+ * dev - Major/minor number from kernel.
+ * geom - Pointer to integer array to place geometry data.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+static int Psi240i_BiosParam (struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int geom[])
+ {
+ POUR_DEVICE pdev;
+
+ pdev = &(HOSTDATA(sdev->host)->device[sdev->id]);
+
+ geom[0] = pdev->heads;
+ geom[1] = pdev->sectors;
+ geom[2] = pdev->cylinders;
+ return 0;
+ }
+
+MODULE_LICENSE("GPL");
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "psi240i",
+ .name = "PSI-240I EIDE Disk Controller",
+ .detect = Psi240i_Detect,
+ .release = Psi240i_Release,
+ .queuecommand = Psi240i_QueueCommand,
+ .bios_param = Psi240i_BiosParam,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_NONE,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h
new file mode 100644
index 000000000000..6a598766df51
--- /dev/null
+++ b/drivers/scsi/psi240i.h
@@ -0,0 +1,315 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi240i.h
+ *
+ * Description: Header file for the SCSI driver for the PSI240I
+ * EIDE interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PSI240I_H
+#define _PSI240I_H
+
+#include <linux/types.h>
+
+#ifndef PSI_EIDE_SCSIOP
+#define PSI_EIDE_SCSIOP 1
+
+/************************************************/
+/* Some defines that we like */
+/************************************************/
+#define CHAR char
+#define UCHAR unsigned char
+#define SHORT short
+#define USHORT unsigned short
+#define BOOL unsigned short
+#define LONG long
+#define ULONG unsigned long
+#define VOID void
+
+/************************************************/
+/* Timeout konstants */
+/************************************************/
+#define TIMEOUT_READY 10 // 100 mSec
+#define TIMEOUT_DRQ 40 // 400 mSec
+
+/************************************************/
+/* Misc. macros */
+/************************************************/
+#define ANY2SCSI(up, p) \
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up) \
+( (((long)*(((UCHAR *)up))) << 16) \
++ (((long)(((UCHAR *)up)[1])) << 8) \
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p) \
+((UCHAR *)up)[0] = ((long)(p)) >> 24; \
+((UCHAR *)up)[1] = ((long)(p)) >> 16; \
+((UCHAR *)up)[2] = ((long)(p)) >> 8; \
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up) \
+( (((long)(((UCHAR *)up)[0])) << 24) \
++ (((long)(((UCHAR *)up)[1])) << 16) \
++ (((long)(((UCHAR *)up)[2])) << 8) \
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/* SCSI CDB operation codes */
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_MODE_SELECT10 0x55
+#define SCSIOP_MODE_SENSE10 0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6
+#define SCSIOP_MECHANISM_STATUS 0xBD
+#define SCSIOP_READ_CD 0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET 0x08
+#define IDE_COMMAND_READ 0x20
+#define IDE_COMMAND_WRITE 0x30
+#define IDE_COMMAND_RECALIBRATE 0x10
+#define IDE_COMMAND_SEEK 0x70
+#define IDE_COMMAND_SET_PARAMETERS 0x91
+#define IDE_COMMAND_VERIFY 0x40
+#define IDE_COMMAND_ATAPI_PACKET 0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1
+#define IDE_CMD_READ_MULTIPLE 0xC4
+#define IDE_CMD_WRITE_MULTIPLE 0xC5
+#define IDE_CMD_SET_MULTIPLE 0xC6
+#define IDE_COMMAND_WRITE_DMA 0xCA
+#define IDE_COMMAND_READ_DMA 0xC8
+#define IDE_COMMAND_IDENTIFY 0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR 0x01
+#define IDE_STATUS_INDEX 0x02
+#define IDE_STATUS_CORRECTED_ERROR 0x04
+#define IDE_STATUS_DRQ 0x08
+#define IDE_STATUS_DSC 0x10
+#define IDE_STATUS_WRITE_FAULT 0x20
+#define IDE_STATUS_DRDY 0x40
+#define IDE_STATUS_BUSY 0x80
+
+// IDE error definitions
+#define IDE_ERROR_AMNF 0x01
+#define IDE_ERROR_TKONF 0x02
+#define IDE_ERROR_ABRT 0x04
+#define IDE_ERROR_MCR 0x08
+#define IDE_ERROR_IDFN 0x10
+#define IDE_ERROR_MC 0x20
+#define IDE_ERROR_UNC 0x40
+#define IDE_ERROR_BBK 0x80
+
+// IDE interface structure
+typedef struct _IDE_STRUCT
+ {
+ union
+ {
+ UCHAR ide[9];
+ struct
+ {
+ USHORT data;
+ UCHAR sectors;
+ UCHAR lba[4];
+ UCHAR cmd;
+ UCHAR spigot;
+ } ides;
+ } ide;
+ } IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef struct _READ_CAPACITY_DATA
+ {
+ ULONG blks; /* total blocks (converted to little endian) */
+ ULONG blksiz; /* size of each (converted to little endian) */
+ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+#ifndef HOSTS_C
+
+typedef struct _INQUIRYDATA
+ {
+ UCHAR DeviceType :5;
+ UCHAR DeviceTypeQualifier :3;
+ UCHAR DeviceTypeModifier :7;
+ UCHAR RemovableMedia :1;
+ UCHAR Versions;
+ UCHAR ResponseDataFormat;
+ UCHAR AdditionalLength;
+ UCHAR Reserved[2];
+ UCHAR SoftReset :1;
+ UCHAR CommandQueue :1;
+ UCHAR Reserved2 :1;
+ UCHAR LinkedCommands :1;
+ UCHAR Synchronous :1;
+ UCHAR Wide16Bit :1;
+ UCHAR Wide32Bit :1;
+ UCHAR RelativeAddressing :1;
+ UCHAR VendorId[8];
+ UCHAR ProductId[16];
+ UCHAR ProductRevisionLevel[4];
+ UCHAR VendorSpecific[20];
+ UCHAR Reserved3[40];
+ } INQUIRYDATA, *PINQUIRYDATA;
+#endif
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+ {
+ USHORT GeneralConfiguration; // 00
+ USHORT NumberOfCylinders; // 02
+ USHORT Reserved1; // 04
+ USHORT NumberOfHeads; // 06
+ USHORT UnformattedBytesPerTrack; // 08
+ USHORT UnformattedBytesPerSector; // 0A
+ USHORT SectorsPerTrack; // 0C
+ USHORT VendorUnique1[3]; // 0E
+ USHORT SerialNumber[10]; // 14
+ USHORT BufferType; // 28
+ USHORT BufferSectorSize; // 2A
+ USHORT NumberOfEccBytes; // 2C
+ USHORT FirmwareRevision[4]; // 2E
+ USHORT ModelNumber[20]; // 36
+ UCHAR MaximumBlockTransfer; // 5E
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60
+ USHORT Capabilities; // 62
+ USHORT Reserved2; // 64
+ UCHAR VendorUnique3; // 66
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:1; // 6A
+ USHORT Reserved3:15;
+ USHORT NumberOfCurrentCylinders; // 6C
+ USHORT NumberOfCurrentHeads; // 6E
+ USHORT CurrentSectorsPerTrack; // 70
+ ULONG CurrentSectorCapacity; // 72
+ USHORT Reserved4[197]; // 76
+ } IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+ USHORT GeneralConfiguration; // 00
+ USHORT NumberOfCylinders; // 02
+ USHORT Reserved1; // 04
+ USHORT NumberOfHeads; // 06
+ USHORT UnformattedBytesPerTrack; // 08
+ USHORT UnformattedBytesPerSector; // 0A
+ USHORT SectorsPerTrack; // 0C
+ USHORT VendorUnique1[3]; // 0E
+ USHORT SerialNumber[10]; // 14
+ USHORT BufferType; // 28
+ USHORT BufferSectorSize; // 2A
+ USHORT NumberOfEccBytes; // 2C
+ USHORT FirmwareRevision[4]; // 2E
+ USHORT ModelNumber[20]; // 36
+ UCHAR MaximumBlockTransfer; // 5E
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60
+ USHORT Capabilities; // 62
+ USHORT Reserved2; // 64
+ UCHAR VendorUnique3; // 66
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:1; // 6A
+ USHORT Reserved3:15;
+ USHORT NumberOfCurrentCylinders; // 6C
+ USHORT NumberOfCurrentHeads; // 6E
+ USHORT CurrentSectorsPerTrack; // 70
+ ULONG CurrentSectorCapacity; // 72
+ } IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif // PSI_EIDE_SCSIOP
+
+// function prototypes
+int Psi240i_Command (Scsi_Cmnd *SCpnt);
+int Psi240i_Abort (Scsi_Cmnd *SCpnt);
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+#endif
diff --git a/drivers/scsi/psi_chip.h b/drivers/scsi/psi_chip.h
new file mode 100644
index 000000000000..224cf8f64c97
--- /dev/null
+++ b/drivers/scsi/psi_chip.h
@@ -0,0 +1,195 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi_chip.h
+ *
+ * Description: This file contains the interface defines and
+ * error codes.
+ *
+ *-M*************************************************************************/
+#ifndef PSI_CHIP
+#define PSI_CHIP
+
+/************************************************/
+/* Misc konstants */
+/************************************************/
+#define CHIP_MAXDRIVES 8
+
+/************************************************/
+/* Chip I/O addresses */
+/************************************************/
+#define CHIP_ADRS_0 0x0130
+#define CHIP_ADRS_1 0x0150
+#define CHIP_ADRS_2 0x0190
+#define CHIP_ADRS_3 0x0210
+#define CHIP_ADRS_4 0x0230
+#define CHIP_ADRS_5 0x0250
+
+/************************************************/
+/* EEPROM locations */
+/************************************************/
+#define CHIP_EEPROM_BIOS 0x0000 // BIOS base address
+#define CHIP_EEPROM_DATA 0x2000 // SETUP data base address
+#define CHIP_EEPROM_FACTORY 0x2400 // FACTORY data base address
+#define CHIP_EEPROM_SETUP 0x3000 // SETUP PROGRAM base address
+
+#define CHIP_EEPROM_SIZE 32768U // size of the entire EEPROM
+#define CHIP_EEPROM_BIOS_SIZE 8192 // size of the BIOS in bytes
+#define CHIP_EEPROM_DATA_SIZE 4096 // size of factory, setup, log data block in bytes
+#define CHIP_EEPROM_SETUP_SIZE 20480U // size of the setup program in bytes
+
+/************************************************/
+/* Chip Interrupts */
+/************************************************/
+#define CHIP_IRQ_10 0x72
+#define CHIP_IRQ_11 0x73
+#define CHIP_IRQ_12 0x74
+
+/************************************************/
+/* Chip Setup addresses */
+/************************************************/
+#define CHIP_SETUP_BASE 0x0000C000L
+
+/************************************************/
+/* Chip Register address offsets */
+/************************************************/
+#define REG_DATA 0x00
+#define REG_ERROR 0x01
+#define REG_SECTOR_COUNT 0x02
+#define REG_LBA_0 0x03
+#define REG_LBA_8 0x04
+#define REG_LBA_16 0x05
+#define REG_LBA_24 0x06
+#define REG_STAT_CMD 0x07
+#define REG_SEL_FAIL 0x08
+#define REG_IRQ_STATUS 0x09
+#define REG_ADDRESS 0x0A
+#define REG_FAIL 0x0C
+#define REG_ALT_STAT 0x0E
+#define REG_DRIVE_ADRS 0x0F
+
+/************************************************/
+/* Chip RAM locations */
+/************************************************/
+#define CHIP_DEVICE 0x8000
+#define CHIP_DEVICE_0 0x8000
+#define CHIP_DEVICE_1 0x8008
+#define CHIP_DEVICE_2 0x8010
+#define CHIP_DEVICE_3 0x8018
+#define CHIP_DEVICE_4 0x8020
+#define CHIP_DEVICE_5 0x8028
+#define CHIP_DEVICE_6 0x8030
+#define CHIP_DEVICE_7 0x8038
+typedef struct
+ {
+ UCHAR channel; // channel of this device (0-8).
+ UCHAR spt; // Sectors Per Track.
+ ULONG spc; // Sectors Per Cylinder.
+ } CHIP_DEVICE_N;
+
+#define CHIP_CONFIG 0x8100 // address of boards configuration.
+typedef struct
+ {
+ UCHAR irq; // interrupt request channel number
+ UCHAR numDrives; // Number of accessible drives
+ UCHAR fastFormat; // Boolean for fast format enable
+ } CHIP_CONFIG_N;
+
+#define CHIP_MAP 0x8108 // eight byte device type map.
+
+
+#define CHIP_RAID 0x8120 // array of RAID signature structures and LBA
+#define CHIP_RAID_1 0x8120
+#define CHIP_RAID_2 0x8130
+#define CHIP_RAID_3 0x8140
+#define CHIP_RAID_4 0x8150
+
+/************************************************/
+/* Chip Register Masks */
+/************************************************/
+#define CHIP_ID 0x7B
+#define SEL_RAM 0x8000
+#define MASK_FAIL 0x80
+
+/************************************************/
+/* Chip cable select bits */
+/************************************************/
+#define SECTORSXFER 8
+
+/************************************************/
+/* Chip cable select bits */
+/************************************************/
+#define SEL_NONE 0x00
+#define SEL_1 0x01
+#define SEL_2 0x02
+#define SEL_3 0x04
+#define SEL_4 0x08
+
+/************************************************/
+/* Programmable Interrupt Controller*/
+/************************************************/
+#define PIC1 0x20 // first 8259 base port address
+#define PIC2 0xA0 // second 8259 base port address
+#define INT_OCW1 1 // Operation Control Word 1: IRQ mask
+#define EOI 0x20 // non-specific end-of-interrupt
+
+/************************************************/
+/* Device/Geometry controls */
+/************************************************/
+#define GEOMETRY_NONE 0x0 // No device
+#define GEOMETRY_AUTO 0x1 // Geometry set automatically
+#define GEOMETRY_USER 0x2 // User supplied geometry
+
+#define DEVICE_NONE 0x0 // No device present
+#define DEVICE_INACTIVE 0x1 // device present but not registered active
+#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...)
+#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device
+#define DEVICE_DASD_LBA 0x4 // LBA compatible device
+
+/************************************************/
+/* Setup Structure Definitions */
+/************************************************/
+typedef struct // device setup parameters
+ {
+ UCHAR geometryControl; // geometry control flags
+ UCHAR device; // device code
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ ULONG blocks; // number of blocks on device
+ USHORT spare1;
+ USHORT spare2;
+ } SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct // master setup structure
+ {
+ USHORT startupDelay;
+ USHORT promptBIOS;
+ USHORT fastFormat;
+ USHORT spare2;
+ USHORT spare3;
+ USHORT spare4;
+ USHORT spare5;
+ USHORT spare6;
+ SETUP_DEVICE setupDevice[8];
+ } SETUP, *PSETUP;
+
+#endif
+
diff --git a/drivers/scsi/psi_dale.h b/drivers/scsi/psi_dale.h
new file mode 100644
index 000000000000..d672e3b01982
--- /dev/null
+++ b/drivers/scsi/psi_dale.h
@@ -0,0 +1,564 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2220I device driver for Linux.
+ *
+ * psi_dalei.h - Linux Host Driver for PCI-2220i EIDE Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ * http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ * tech@psidisk.com Technical Support
+ *
+ ****************************************************************************/
+
+/************************************************/
+/* Some defines that we like */
+/************************************************/
+#define CHAR char
+#define UCHAR unsigned char
+#define SHORT short
+#define USHORT unsigned short
+#define BOOL unsigned short
+#define LONG long
+#define ULONG unsigned long
+#define VOID void
+
+/************************************************/
+/* Dale PCI setup */
+/************************************************/
+#define VENDOR_PSI 0x1256
+#define DEVICE_DALE_1 0x4401 /* 'D1' */
+#define DEVICE_BIGD_1 0x4201 /* 'B1' */
+#define DEVICE_BIGD_2 0x4202 /* 'B2' */
+
+/************************************************/
+/* Misc konstants */
+/************************************************/
+#define DALE_MAXDRIVES 4
+#define BIGD_MAXDRIVES 8
+#define SECTORSXFER 8
+#define ATAPI_TRANSFER 8192
+#define BYTES_PER_SECTOR 512
+#define DEFAULT_TIMING_MODE 5
+
+/************************************************/
+/* EEPROM locations */
+/************************************************/
+#define DALE_FLASH_PAGE_SIZE 128 // number of bytes per page
+#define DALE_FLASH_SIZE 65536L
+
+#define DALE_FLASH_BIOS 0x00080000L // BIOS base address
+#define DALE_FLASH_SETUP 0x00088000L // SETUP PROGRAM base address offset from BIOS
+#define DALE_FLASH_RAID 0x00088400L // RAID signature storage
+#define DALE_FLASH_FACTORY 0x00089000L // FACTORY data base address offset from BIOS
+
+#define DALE_FLASH_BIOS_SIZE 32768U // size of FLASH BIOS REGION
+
+/************************************************/
+/* DALE Register address offsets */
+/************************************************/
+#define REG_DATA 0x80
+#define REG_ERROR 0x84
+#define REG_SECTOR_COUNT 0x88
+#define REG_LBA_0 0x8C
+#define REG_LBA_8 0x90
+#define REG_LBA_16 0x94
+#define REG_LBA_24 0x98
+#define REG_STAT_CMD 0x9C
+#define REG_STAT_SEL 0xA0
+#define REG_FAIL 0xB0
+#define REG_ALT_STAT 0xB8
+#define REG_DRIVE_ADRS 0xBC
+
+#define DALE_DATA_SLOW 0x00040000L
+#define DALE_DATA_MODE2 0x00040000L
+#define DALE_DATA_MODE3 0x00050000L
+#define DALE_DATA_MODE4 0x00060000L
+#define DALE_DATA_MODE5 0x00070000L
+
+#define BIGD_DATA_SLOW 0x00000000L
+#define BIGD_DATA_MODE0 0x00000000L
+#define BIGD_DATA_MODE2 0x00000000L
+#define BIGD_DATA_MODE3 0x00000008L
+#define BIGD_DATA_MODE4 0x00000010L
+#define BIGD_DATA_MODE5 0x00000020L
+
+#define RTR_LOCAL_RANGE 0x000
+#define RTR_LOCAL_REMAP 0x004
+#define RTR_EXP_RANGE 0x010
+#define RTR_EXP_REMAP 0x014
+#define RTR_REGIONS 0x018
+#define RTR_DM_MASK 0x01C
+#define RTR_DM_LOCAL_BASE 0x020
+#define RTR_DM_IO_BASE 0x024
+#define RTR_DM_PCI_REMAP 0x028
+#define RTR_DM_IO_CONFIG 0x02C
+#define RTR_MAILBOX 0x040
+#define RTR_LOCAL_DOORBELL 0x060
+#define RTR_PCI_DOORBELL 0x064
+#define RTR_INT_CONTROL_STATUS 0x068
+#define RTR_EEPROM_CONTROL_STATUS 0x06C
+
+#define RTR_DMA0_MODE 0x0080
+#define RTR_DMA0_PCI_ADDR 0x0084
+#define RTR_DMA0_LOCAL_ADDR 0x0088
+#define RTR_DMA0_COUNT 0x008C
+#define RTR_DMA0_DESC_PTR 0x0090
+#define RTR_DMA1_MODE 0x0094
+#define RTR_DMA1_PCI_ADDR 0x0098
+#define RTR_DMA1_LOCAL_ADDR 0x009C
+#define RTR_DMA1_COUNT 0x00A0
+#define RTR_DMA1_DESC_PTR 0x00A4
+#define RTR_DMA_COMMAND_STATUS 0x00A8
+#define RTR_DMA_ARB0 0x00AC
+#define RTR_DMA_ARB1 0x00B0
+
+#define RTL_DMA0_MODE 0x00
+#define RTL_DMA0_PCI_ADDR 0x04
+#define RTL_DMA0_LOCAL_ADDR 0x08
+#define RTL_DMA0_COUNT 0x0C
+#define RTL_DMA0_DESC_PTR 0x10
+#define RTL_DMA1_MODE 0x14
+#define RTL_DMA1_PCI_ADDR 0x18
+#define RTL_DMA1_LOCAL_ADDR 0x1C
+#define RTL_DMA1_COUNT 0x20
+#define RTL_DMA1_DESC_PTR 0x24
+#define RTL_DMA_COMMAND_STATUS 0x28
+#define RTL_DMA_ARB0 0x2C
+#define RTL_DMA_ARB1 0x30
+
+/************************************************/
+/* Dale Scratchpad locations */
+/************************************************/
+#define DALE_CHANNEL_DEVICE_0 0 // device channel locations
+#define DALE_CHANNEL_DEVICE_1 1
+#define DALE_CHANNEL_DEVICE_2 2
+#define DALE_CHANNEL_DEVICE_3 3
+
+#define DALE_SCRATCH_DEVICE_0 4 // device type codes
+#define DALE_SCRATCH_DEVICE_1 5
+#define DALE_SCRATCH_DEVICE_2 6
+#define DALE_SCRATCH_DEVICE_3 7
+
+#define DALE_RAID_0_STATUS 8
+#define DALE_RAID_1_STATUS 9
+
+#define DALE_TIMING_MODE 12 // bus master timing mode (2, 3, 4, 5)
+#define DALE_NUM_DRIVES 13 // number of addressable drives on this board
+#define DALE_RAID_ON 14 // RAID status On
+#define DALE_LAST_ERROR 15 // Last error code from BIOS
+
+/************************************************/
+/* BigD Scratchpad locations */
+/************************************************/
+#define BIGD_DEVICE_0 0 // device channel locations
+#define BIGD_DEVICE_1 1
+#define BIGD_DEVICE_2 2
+#define BIGD_DEVICE_3 3
+
+#define BIGD_DEVICE_4 4 // device type codes
+#define BIGD_DEVICE_5 5
+#define BIGD_DEVICE_6 6
+#define BIGD_DEVICE_7 7
+
+#define BIGD_ALARM_IMAGE 11 // ~image of alarm fail register
+#define BIGD_TIMING_MODE 12 // bus master timing mode (2, 3, 4, 5)
+#define BIGD_NUM_DRIVES 13 // number of addressable drives on this board
+#define BIGD_RAID_ON 14 // RAID status is on for the whole board
+#define BIGD_LAST_ERROR 15 // Last error code from BIOS
+
+#define BIGD_RAID_0_STATUS 16
+#define BIGD_RAID_1_STATUS 17
+#define BIGD_RAID_2_STATUS 18
+#define BIGD_RAID_3_STATUS 19
+#define BIGD_RAID_4_STATUS 20
+#define BIGD_RAID_5_STATUS 21
+#define BIGD_RAID_6_STATUS 22
+#define BIGD_RAID_7_STATUS 23
+
+/************************************************/
+/* Dale cable select bits */
+/************************************************/
+#define SEL_NONE 0x00
+#define SEL_1 0x01
+#define SEL_2 0x02
+#define SEL_3 0x04
+#define SEL_4 0x08
+#define SEL_NEW_SPEED_1 0x20
+#define SEL_COPY 0x40
+#define SEL_IRQ_OFF 0x80
+
+/************************************************/
+/* Device/Geometry controls */
+/************************************************/
+#define GEOMETRY_NONE 0x0 // No device
+#define GEOMETRY_SET 0x1 // Geometry set
+#define GEOMETRY_LBA 0x2 // Geometry set in default LBA mode
+#define GEOMETRY_PHOENIX 0x3 // Geometry set in Pheonix BIOS compatibility mode
+
+#define DEVICE_NONE 0x0 // No device present
+#define DEVICE_INACTIVE 0x1 // device present but not registered active
+#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...)
+#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device
+#define DEVICE_DASD_LBA 0x4 // LBA compatible device
+
+/************************************************/
+/* BigD fail register bits */
+/************************************************/
+#define FAIL_NONE 0x00
+#define FAIL_0 0x01
+#define FAIL_1 0x02
+#define FAIL_2 0x04
+#define FAIL_MULTIPLE 0x08
+#define FAIL_GOOD 0x20
+#define FAIL_AUDIBLE 0x40
+#define FAIL_ANY 0x80
+
+/************************************************/
+/* Setup Structure Definitions */
+/************************************************/
+typedef struct // device setup parameters
+ {
+ UCHAR geometryControl; // geometry control flags
+ UCHAR device; // device code
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ ULONG blocks; // number of blocks on device
+ ULONG realCapacity; // number of real blocks on this device for drive changed testing
+ } SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct // master setup structure
+ {
+ USHORT startupDelay;
+ BOOL promptBIOS;
+ BOOL fastFormat;
+ BOOL shareInterrupt;
+ BOOL rebootRebuild;
+ USHORT timingMode;
+ USHORT spare5;
+ USHORT spare6;
+ SETUP_DEVICE setupDevice[BIGD_MAXDRIVES];
+ } SETUP, *PSETUP;
+
+/************************************************/
+/* RAID Structure Definitions */
+/************************************************/
+typedef struct
+ {
+ UCHAR signature; // 0x55 our mirror signature
+ UCHAR status; // current status bits
+ UCHAR pairIdentifier; // unique identifier for pair
+ ULONG reconstructPoint; // recontruction point for hot reconstruct
+ } DISK_MIRROR;
+
+typedef struct DEVICE_RAID1
+ {
+ long TotalSectors;
+ DISK_MIRROR DiskRaid1;
+ } DEVICE_RAID1, *PDEVICE_RAID1;
+
+#define DISK_MIRROR_POSITION 0x01A8
+#define SIGNATURE 0x55
+
+#define MASK_SERIAL_NUMBER 0x0FFE // mask for serial number matching
+#define MASK_SERIAL_UNIT 0x0001 // mask for unit portion of serial number
+
+// Status bits
+#define UCBF_MIRRORED 0x0010 // drive has a pair
+#define UCBF_MATCHED 0x0020 // drive pair is matched
+#define UCBF_SURVIVOR 0x0040 // this unit is a survivor of a pair
+#define UCBF_REBUILD 0x0080 // rebuild in progress on this device
+
+// SCSI controls for RAID
+#define SC_MY_RAID 0xBF // our special CDB command byte for Win95... interface
+#define MY_SCSI_QUERY1 0x32 // byte 1 subcommand to query driver for RAID 1 informatation
+#define MY_SCSI_REBUILD 0x40 // byte 1 subcommand to reconstruct a mirrored pair
+#define MY_SCSI_DEMOFAIL 0x54 // byte 1 subcommand for RAID failure demonstration
+#define MY_SCSI_ALARMMUTE 0x60 // byte 1 subcommand to mute any alarm currently on
+
+/************************************************/
+/* Timeout konstants */
+/************************************************/
+#define TIMEOUT_READY 100 // 100 mSec
+#define TIMEOUT_DRQ 300 // 300 mSec
+#define TIMEOUT_DATA (3 * HZ) // 3 seconds
+
+/************************************************/
+/* Misc. macros */
+/************************************************/
+#define ANY2SCSI(up, p) \
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up) \
+( (((long)*(((UCHAR *)up))) << 16) \
++ (((long)(((UCHAR *)up)[1])) << 8) \
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p) \
+((UCHAR *)up)[0] = ((long)(p)) >> 24; \
+((UCHAR *)up)[1] = ((long)(p)) >> 16; \
+((UCHAR *)up)[2] = ((long)(p)) >> 8; \
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up) \
+( (((long)(((UCHAR *)up)[0])) << 24) \
++ (((long)(((UCHAR *)up)[1])) << 16) \
++ (((long)(((UCHAR *)up)[2])) << 8) \
++ ((long)(((UCHAR *)up)[3])) )
+
+#define SelectSpigot(padapter,spigot) outb_p (spigot, padapter->regStatSel)
+#define WriteCommand(padapter,cmd) outb_p (cmd, padapter->regStatCmd)
+#define AtapiDevice(padapter,b) outb_p (b, padapter->regLba24);
+#define AtapiCountLo(padapter,b) outb_p (b, padapter->regLba8)
+#define AtapiCountHi(padapter,b) outb_p (b, padapter->regLba16)
+
+/************************************************/
+/* SCSI CDB operation codes */
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_MODE_SELECT10 0x55
+#define SCSIOP_MODE_SENSE10 0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6
+#define SCSIOP_MECHANISM_STATUS 0xBD
+#define SCSIOP_READ_CD 0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET 0x08
+#define IDE_COMMAND_READ 0x20
+#define IDE_COMMAND_WRITE 0x30
+#define IDE_COMMAND_RECALIBRATE 0x10
+#define IDE_COMMAND_SEEK 0x70
+#define IDE_COMMAND_SET_PARAMETERS 0x91
+#define IDE_COMMAND_VERIFY 0x40
+#define IDE_COMMAND_ATAPI_PACKET 0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1
+#define IDE_CMD_READ_MULTIPLE 0xC4
+#define IDE_CMD_WRITE_MULTIPLE 0xC5
+#define IDE_CMD_SET_MULTIPLE 0xC6
+#define IDE_COMMAND_IDENTIFY 0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR 0x01
+#define IDE_STATUS_INDEX 0x02
+#define IDE_STATUS_CORRECTED_ERROR 0x04
+#define IDE_STATUS_DRQ 0x08
+#define IDE_STATUS_DSC 0x10
+#define IDE_STATUS_WRITE_FAULT 0x20
+#define IDE_STATUS_DRDY 0x40
+#define IDE_STATUS_BUSY 0x80
+
+typedef struct _ATAPI_STATUS
+ {
+ CHAR check :1;
+ CHAR reserved1 :1;
+ CHAR corr :1;
+ CHAR drq :1;
+ CHAR dsc :1;
+ CHAR reserved2 :1;
+ CHAR drdy :1;
+ CHAR bsy :1;
+ } ATAPI_STATUS;
+
+typedef struct _ATAPI_REASON
+ {
+ CHAR cod :1;
+ CHAR io :1;
+ CHAR reserved1 :6;
+ } ATAPI_REASON;
+
+typedef struct _ATAPI_ERROR
+ {
+ CHAR ili :1;
+ CHAR eom :1;
+ CHAR abort :1;
+ CHAR mcr :1;
+ CHAR senseKey :4;
+ } ATAPI_ERROR;
+
+// IDE error definitions
+#define IDE_ERROR_AMNF 0x01
+#define IDE_ERROR_TKONF 0x02
+#define IDE_ERROR_ABRT 0x04
+#define IDE_ERROR_MCR 0x08
+#define IDE_ERROR_IDFN 0x10
+#define IDE_ERROR_MC 0x20
+#define IDE_ERROR_UNC 0x40
+#define IDE_ERROR_BBK 0x80
+
+// SCSI read capacity structure
+typedef struct _READ_CAPACITY_DATA
+ {
+ ULONG blks; /* total blocks (converted to little endian) */
+ ULONG blksiz; /* size of each (converted to little endian) */
+ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+ {
+ UCHAR DeviceType :5;
+ UCHAR DeviceTypeQualifier :3;
+ UCHAR DeviceTypeModifier :7;
+ UCHAR RemovableMedia :1;
+ UCHAR Versions;
+ UCHAR ResponseDataFormat;
+ UCHAR AdditionalLength;
+ UCHAR Reserved[2];
+ UCHAR SoftReset :1;
+ UCHAR CommandQueue :1;
+ UCHAR Reserved2 :1;
+ UCHAR LinkedCommands :1;
+ UCHAR Synchronous :1;
+ UCHAR Wide16Bit :1;
+ UCHAR Wide32Bit :1;
+ UCHAR RelativeAddressing :1;
+ UCHAR VendorId[8];
+ UCHAR ProductId[16];
+ UCHAR ProductRevisionLevel[4];
+ UCHAR VendorSpecific[20];
+ UCHAR Reserved3[40];
+ } INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+#pragma pack (1)
+typedef struct _IDENTIFY_DATA
+ {
+ USHORT GeneralConfiguration; // 0
+ USHORT NumberOfCylinders; // 1
+ USHORT Reserved1; // 2
+ USHORT NumberOfHeads; // 3
+ USHORT UnformattedBytesPerTrack; // 4
+ USHORT UnformattedBytesPerSector; // 5
+ USHORT SectorsPerTrack; // 6
+ USHORT NumBytesISG; // 7 Byte Len - inter-sector gap
+ USHORT NumBytesSync; // 8 - sync field
+ USHORT NumWordsVUS; // 9 Len - Vendor Unique Info
+ USHORT SerialNumber[10]; // 10
+ USHORT BufferType; // 20
+ USHORT BufferSectorSize; // 21
+ USHORT NumberOfEccBytes; // 22
+ USHORT FirmwareRevision[4]; // 23
+ USHORT ModelNumber[20]; // 27
+ USHORT NumSectorsPerInt :8; // 47 Multiple Mode - Sec/Blk
+ USHORT Reserved2 :8; // 47
+ USHORT DoubleWordMode; // 48 flag for double word mode capable
+ USHORT VendorUnique1 :8; // 49
+ USHORT SupportDMA :1; // 49 DMA supported
+ USHORT SupportLBA :1; // 49 LBA supported
+ USHORT SupportIORDYDisable :1; // 49 IORDY can be disabled
+ USHORT SupportIORDY :1; // 49 IORDY supported
+ USHORT ReservedPsuedoDMA :1; // 49 reserved for pseudo DMA mode support
+ USHORT Reserved3 :3; // 49
+ USHORT Reserved4; // 50
+ USHORT Reserved5 :8; // 51 Transfer Cycle Timing - PIO
+ USHORT PIOCycleTime :8; // 51 Transfer Cycle Timing - PIO
+ USHORT Reserved6 :8; // 52 - DMA
+ USHORT DMACycleTime :8; // 52 - DMA
+ USHORT Valid_54_58 :1; // 53 words 54 - 58 are valid
+ USHORT Valid_64_70 :1; // 53 words 64 - 70 are valid
+ USHORT Reserved7 :14; // 53
+ USHORT LogNumCyl; // 54 Current Translation - Num Cyl
+ USHORT LogNumHeads; // 55 Num Heads
+ USHORT LogSectorsPerTrack; // 56 Sec/Trk
+ ULONG LogTotalSectors; // 57 Total Sec
+ USHORT CurrentNumSecPerInt :8; // 59 current setting for number of sectors per interrupt
+ USHORT ValidNumSecPerInt :1; // 59 Current setting is valid for number of sectors per interrupt
+ USHORT Reserved8 :7; // 59
+ ULONG LBATotalSectors; // 60 LBA Mode - Sectors
+ USHORT DMASWordFlags; // 62
+ USHORT DMAMWordFlags; // 63
+ USHORT AdvancedPIOSupport :8; // 64 Flow control PIO transfer modes supported
+ USHORT Reserved9 :8; // 64
+ USHORT MinMultiDMACycle; // 65 minimum multiword DMA transfer cycle time per word
+ USHORT RecomendDMACycle; // 66 Manufacturer's recommende multiword DMA transfer cycle time
+ USHORT MinPIOCycleWithoutFlow; // 67 Minimum PIO transfer cycle time without flow control
+ USHORT MinPIOCylceWithFlow; // 68 Minimum PIO transfer cycle time with IORDY flow control
+ USHORT ReservedSpace[256-69]; // 69
+ } IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// ATAPI configuration bits
+typedef struct _ATAPI_GENERAL_0
+ {
+ USHORT CmdPacketSize :2; // Command packet size
+ USHORT Reserved1 :3;
+ USHORT CmdDrqType :2;
+ USHORT Removable :1;
+ USHORT DeviceType :5;
+ USHORT Reserved2 :1;
+ USHORT ProtocolType :2;
+ } ATAPI_GENERAL_0;
+
+#pragma pack ()
diff --git a/drivers/scsi/psi_roy.h b/drivers/scsi/psi_roy.h
new file mode 100644
index 000000000000..c55b9c04c32a
--- /dev/null
+++ b/drivers/scsi/psi_roy.h
@@ -0,0 +1,331 @@
+/****************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver for Linux.
+ *
+ * psi_roy.h - Linux Host Driver for PCI-2000 IntelliCache SCSI Adapters
+ *
+ * Copyright (c) 1997-1999 Perceptive Solutions, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * Technical updates and product information at:
+ * http://www.psidisk.com
+ *
+ * Please send questions, comments, bug reports to:
+ * tech@psidisk.com Technical Support
+ *
+ ****************************************************************************/
+
+#ifndef ROY_HOST
+#define ROY_HOST
+
+/************************************************/
+/* PCI setup */
+/************************************************/
+#define VENDOR_PSI 0x1256
+#define DEVICE_ROY_1 0x5201 /* 'R1' */
+
+/************************************************/
+/* controller constants */
+/************************************************/
+#define MAXADAPTER 4 // Increase this and the sizes of the arrays below, if you need more.
+#define MAX_BUS 2
+#define MAX_UNITS 16
+#define TIMEOUT_COMMAND 400 // number of milliSecondos for command busy timeout
+
+/************************************************/
+/* I/O address offsets */
+/************************************************/
+#define RTR_MAILBOX 0x040
+#define RTR_LOCAL_DOORBELL 0x060
+#define RTR_PCI_DOORBELL 0x064
+
+/************************************************/
+/* */
+/* Host command codes */
+/* */
+/************************************************/
+#define CMD_READ_CHS 0x01 /* read sectors as specified (CHS mode) */
+#define CMD_READ 0x02 /* read sectors as specified (RBA mode) */
+#define CMD_READ_SG 0x03 /* read sectors using scatter/gather list */
+#define CMD_WRITE_CHS 0x04 /* write sectors as specified (CHS mode) */
+#define CMD_WRITE 0x05 /* write sectors as specified (RBA mode) */
+#define CMD_WRITE_SG 0x06 /* write sectors using scatter/gather list (LBA mode) */
+#define CMD_READ_CHS_SG 0x07 /* read sectors using scatter/gather list (CHS mode) */
+#define CMD_WRITE_CHS_SG 0x08 /* write sectors using scatter/gather list (CHS mode) */
+#define CMD_VERIFY_CHS 0x09 /* verify data on sectors as specified (CHS mode) */
+#define CMD_VERIFY 0x0A /* verify data on sectors as specified (RBA mode) */
+#define CMD_DASD_CDB 0x0B /* process CDB for a DASD device */
+#define CMD_DASD_CDB_SG 0x0C /* process CDB for a DASD device with scatter/gather */
+
+#define CMD_READ_ABS 0x10 /* read absolute disk */
+#define CMD_WRITE_ABS 0x11 /* write absolute disk */
+#define CMD_VERIFY_ABS 0x12 /* verify absolute disk */
+#define CMD_TEST_READY 0x13 /* test unit ready and return status code */
+#define CMD_LOCK_DOOR 0x14 /* lock device door */
+#define CMD_UNLOCK_DOOR 0x15 /* unlock device door */
+#define CMD_EJECT_MEDIA 0x16 /* eject the media */
+#define CMD_UPDATE_CAP 0x17 /* update capacity information */
+#define CMD_TEST_PRIV 0x18 /* test and setup private format media */
+
+
+#define CMD_SCSI_THRU 0x30 /* SCSI pass through CDB */
+#define CMD_SCSI_THRU_SG 0x31 /* SCSI pass through CDB with scatter/gather */
+#define CMD_SCSI_REQ_SENSE 0x32 /* SCSI pass through request sense after check condition */
+
+#define CMD_DASD_RAID_RQ 0x35 /* request DASD RAID drive data */
+#define CMD_DASD_RAID_RQ0 0x31 /* byte 1 subcommand to query for RAID 0 informatation */
+#define CMD_DASD_RAID_RQ1 0x32 /* byte 1 subcommand to query for RAID 1 informatation */
+#define CMD_DASD_RAID_RQ5 0x33 /* byte 1 subcommand to query for RAID 5 informatation */
+
+#define CMD_DASD_SCSI_INQ 0x36 /* do DASD inquire and return in SCSI format */
+#define CMD_DASD_CAP 0x37 /* read DASD capacity */
+#define CMD_DASD_INQ 0x38 /* do DASD inquire for type data and return SCSI/EIDE inquiry */
+#define CMD_SCSI_INQ 0x39 /* do SCSI inquire */
+#define CMD_READ_SETUP 0x3A /* Get setup structures from controller */
+#define CMD_WRITE_SETUP 0x3B /* Put setup structures in controller and burn in flash */
+#define CMD_READ_CONFIG 0x3C /* Get the entire configuration and setup structures */
+#define CMD_WRITE_CONFIG 0x3D /* Put the entire configuration and setup structures in flash */
+
+#define CMD_TEXT_DEVICE 0x3E /* obtain device text */
+#define CMD_TEXT_SIGNON 0x3F /* get sign on banner */
+
+#define CMD_QUEUE 0x40 /* any command below this generates a queue tag interrupt to host*/
+
+#define CMD_PREFETCH 0x40 /* prefetch sectors as specified */
+#define CMD_TEST_WRITE 0x41 /* Test a device for write protect */
+#define CMD_LAST_STATUS 0x42 /* get last command status and error data*/
+#define CMD_ABORT 0x43 /* abort command as specified */
+#define CMD_ERROR 0x44 /* fetch error code from a tagged op */
+#define CMD_DONE 0x45 /* done with operation */
+#define CMD_DIAGNOSTICS 0x46 /* execute controller diagnostics and wait for results */
+#define CMD_FEATURE_MODE 0x47 /* feature mode control word */
+#define CMD_DASD_INQUIRE 0x48 /* inquire as to DASD SCSI device (32 possible) */
+#define CMD_FEATURE_QUERY 0x49 /* query the feature control word */
+#define CMD_DASD_EJECT 0x4A /* Eject removable media for DASD type */
+#define CMD_DASD_LOCK 0x4B /* Lock removable media for DASD type */
+#define CMD_DASD_TYPE 0x4C /* obtain DASD device type */
+#define CMD_NUM_DEV 0x4D /* obtain the number of devices connected to the controller */
+#define CMD_GET_PARMS 0x4E /* obtain device parameters */
+#define CMD_SPECIFY 0x4F /* specify operating system for scatter/gather operations */
+
+#define CMD_RAID_GET_DEV 0x50 /* read RAID device geometry */
+#define CMD_RAID_READ 0x51 /* read RAID 1 parameter block */
+#define CMD_RAID_WRITE 0x52 /* write RAID 1 parameter block */
+#define CMD_RAID_LITEUP 0x53 /* Light up the drive light for identification */
+#define CMD_RAID_REBUILD 0x54 /* issue a RAID 1 pair rebuild */
+#define CMD_RAID_MUTE 0x55 /* mute RAID failure alarm */
+#define CMD_RAID_FAIL 0x56 /* induce a RAID failure */
+#define CMD_RAID_STATUS 0x57 /* get status of RAID pair */
+#define CMD_RAID_STOP 0x58 /* stop any reconstruct in progress */
+#define CMD_RAID_START 0x59 /* start reconstruct */
+#define CMD_RAID0_READ 0x5A /* read RAID 0 parameter block */
+#define CMD_RAID0_WRITE 0x5B /* write RAID 0 parameter block */
+#define CMD_RAID5_READ 0x5C /* read RAID 5 parameter block */
+#define CMD_RAID5_WRITE 0x5D /* write RAID 5 parameter block */
+
+#define CMD_ERASE_TABLES 0x5F /* erase partition table and RAID signatutures */
+
+#define CMD_SCSI_GET 0x60 /* get SCSI pass through devices */
+#define CMD_SCSI_TIMEOUT 0x61 /* set SCSI pass through timeout */
+#define CMD_SCSI_ERROR 0x62 /* get SCSI pass through request sense length and residual data count */
+#define CMD_GET_SPARMS 0x63 /* get SCSI bus and user parms */
+#define CMD_SCSI_ABORT 0x64 /* abort by setting time-out to zero */
+
+#define CMD_CHIRP_CHIRP 0x77 /* make a chirp chirp sound */
+#define CMD_GET_LAST_DONE 0x78 /* get tag of last done in progress */
+#define CMD_GET_FEATURES 0x79 /* get feature code and ESN */
+#define CMD_CLEAR_CACHE 0x7A /* Clear cache on specified device */
+#define CMD_BIOS_TEST 0x7B /* Test whether or not to load BIOS */
+#define CMD_WAIT_FLUSH 0x7C /* wait for cache flushed and invalidate read cache */
+#define CMD_RESET_BUS 0x7D /* reset the SCSI bus */
+#define CMD_STARTUP_QRY 0x7E /* startup in progress query */
+#define CMD_RESET 0x7F /* reset the controller */
+
+#define CMD_RESTART_RESET 0x80 /* reload and restart the controller at any reset issued */
+#define CMD_SOFT_RESET 0x81 /* do a soft reset NOW! */
+
+/************************************************/
+/* */
+/* Host return errors */
+/* */
+/************************************************/
+#define ERR08_TAGGED 0x80 /* doorbell error ored with tag */
+
+#define ERR16_NONE 0x0000 /* no errors */
+#define ERR16_SC_COND_MET 0x0004 /* SCSI status - Condition Met */
+#define ERR16_CMD 0x0101 /* command error */
+#define ERR16_SC_CHECK_COND 0x0002 /* SCSI status - Check Condition */
+#define ERR16_CMD_NOT 0x0201 /* command not supported */
+#define ERR16_NO_DEVICE 0x0301 /* invalid device selection */
+#define ERR16_SECTOR 0x0202 /* bad sector */
+#define ERR16_PROTECT 0x0303 /* write protected */
+#define ERR16_NOSECTOR 0x0404 /* sector not found */
+#define ERR16_MEDIA 0x0C0C /* invalid media */
+#define ERR16_CONTROL 0x2020 /* controller error */
+#define ERR16_CONTROL_DMA 0x2120 /* controller DMA engine error */
+#define ERR16_NO_ALARM 0x2220 /* alarm is not active */
+#define ERR16_OP_BUSY 0x2320 /* operation busy */
+#define ERR16_SEEK 0x4040 /* seek failure */
+#define ERR16_DEVICE_FAIL 0x4140 /* device has failed */
+#define ERR16_TIMEOUT 0x8080 /* timeout error */
+#define ERR16_DEV_NOT_READY 0xAAAA /* drive not ready */
+#define ERR16_UNDEFINED 0xBBBB /* undefined error */
+#define ERR16_WRITE_FAULT 0xCCCC /* write fault */
+#define ERR16_INVALID_DEV 0x4001 /* invalid device access */
+#define ERR16_DEVICE_BUSY 0x4002 /* device is busy */
+#define ERR16_MEMORY 0x4003 /* device pass thru requires too much memory */
+#define ERR16_NO_FEATURE 0x40FA /* feature no implemented */
+#define ERR16_NOTAG 0x40FD /* no tag space available */
+#define ERR16_NOT_READY 0x40FE /* controller not ready error */
+#define ERR16_SETUP_FLASH 0x5050 /* error when writing setup to flash memory */
+#define ERR16_SETUP_SIZE 0x5051 /* setup block size error */
+#define ERR16_SENSE 0xFFFF /* sense opereration failed */
+#define ERR16_SC_BUSY 0x0008 /* SCSI status - Busy */
+#define ERR16_SC_RES_CONFL 0x0018 /* SCSI status - Reservation Conflict */
+#define ERR16_SC_CMD_TERM 0x0022 /* SCSI status - Command Terminated */
+#define ERR16_SC_OTHER 0x00FF /* SCSI status - not recognized (any value masked) */
+#define ERR16_MEDIA_CHANGED 0x8001 /* devices media has been changed */
+
+#define ERR32_NONE 0x00000000 /* no errors */
+#define ERR32_SC_COND_MET 0x00000004 /* SCSI status - Condition Met */
+#define ERR32_CMD 0x00010101 /* command error */
+#define ERR32_SC_CHECK_COND 0x00020002 /* SCSI status - Check Condition */
+#define ERR32_CMD_NOT 0x00030201 /* command not supported */
+#define ERR32_NO_DEVICE 0x00040301 /* invalid device selection */
+#define ERR32_SECTOR 0x00050202 /* bad sector */
+#define ERR32_PROTECT 0x00060303 /* write protected */
+#define ERR32_NOSECTOR 0x00070404 /* sector not found */
+#define ERR32_MEDIA 0x00080C0C /* invalid media */
+#define ERR32_CONTROL 0x00092020 /* controller error */
+#define ERR32_CONTROL_DMA 0x000A2120 /* Controller DMA error */
+#define ERR32_NO_ALARM 0x000B2220 /* alarm is not active */
+#define ERR32_OP_BUSY 0x000C2320 /* operation busy */
+#define ERR32_SEEK 0x000D4040 /* seek failure */
+#define ERR32_DEVICE_FAIL 0x000E4140 /* device has failed */
+#define ERR32_TIMEOUT 0x000F8080 /* timeout error */
+#define ERR32_DEV_NOT_READY 0x0010AAAA /* drive not ready */
+#define ERR32_UNDEFINED 0x0011BBBB /* undefined error */
+#define ERR32_WRITE_FAULT 0x0012CCCC /* write fault */
+#define ERR32_INVALID_DEV 0x00134001 /* invalid device access */
+#define ERR32_DEVICE_BUSY 0x00144002 /* device is busy */
+#define ERR32_MEMORY 0x00154003 /* device pass thru requires too much memory */
+#define ERR32_NO_FEATURE 0x001640FA /* feature no implemented */
+#define ERR32_NOTAG 0x001740FD /* no tag space available */
+#define ERR32_NOT_READY 0x001840FE /* controller not ready error */
+#define ERR32_SETUP_FLASH 0x00195050 /* error when writing setup to flash memory */
+#define ERR32_SETUP_SIZE 0x001A5051 /* setup block size error */
+#define ERR32_SENSE 0x001BFFFF /* sense opereration failed */
+#define ERR32_SC_BUSY 0x001C0008 /* SCSI status - Busy */
+#define ERR32_SC_RES_CONFL 0x001D0018 /* SCSI status - Reservation Conflict */
+#define ERR32_SC_CMD_TERM 0x001E0022 /* SCSI status - Command Terminated */
+#define ERR32_SC_OTHER 0x001F00FF /* SCSI status - not recognized (any value masked) */
+#define ERR32_MEDIA_CHANGED 0x00208001 /* devices media has been changed */
+
+/************************************************/
+/* */
+/* Host Operating System specification codes */
+/* */
+/************************************************/
+#define SPEC_INTERRUPT 0x80 /* specification requires host interrupt */
+#define SPEC_BACKWARD_SG 0x40 /* specification requires scatter/gather items reversed */
+#define SPEC_DOS_BLOCK 0x01 /* DOS DASD blocking on pass through */
+#define SPEC_OS2_V3 0x02 /* OS/2 Warp */
+#define SPCE_SCO_3242 0x04 /* SCO 3.4.2.2 */
+#define SPEC_QNX_4X 0x05 /* QNX 4.XX */
+#define SPEC_NOVELL_NWPA 0x08 /* Novell NWPA scatter/gather support */
+
+/************************************************/
+/* */
+/* Inquire structures */
+/* */
+/************************************************/
+typedef struct _CNT_SCSI_INQ
+ {
+ UCHAR devt; /* 00: device type */
+ UCHAR devtm; /* 01: device type modifier */
+ UCHAR svers; /* 02: SCSI version */
+ UCHAR rfmt; /* 03: response data format */
+ UCHAR adlen; /* 04: additional length of data */
+ UCHAR res1; /* 05: */
+ UCHAR res2; /* 06: */
+ UCHAR fncs; /* 07: functional capabilities */
+ UCHAR vid[8]; /* 08: vendor ID */
+ UCHAR pid[16]; /* 10: product ID */
+ UCHAR rev[4]; /* 20: product revision */
+ } CNT_SCSI_INQ;
+
+typedef struct _CNT_IDE_INQ
+ {
+ USHORT GeneralConfiguration; /* 00 */
+ USHORT NumberOfCylinders; /* 02 */
+ USHORT Reserved1; /* 04 */
+ USHORT NumberOfHeads; /* 06 */
+ USHORT UnformattedBytesPerTrack; /* 08 */
+ USHORT UnformattedBytesPerSector; /* 0A */
+ USHORT SectorsPerTrack; /* 0C */
+ USHORT VendorUnique1[3]; /* 0E */
+ USHORT SerialNumber[10]; /* 14 */
+ USHORT BufferType; /* 28 */
+ USHORT BufferSectorSize; /* 2A */
+ USHORT NumberOfEccBytes; /* 2C */
+ USHORT FirmwareRevision[4]; /* 2E */
+ USHORT ModelNumber[20]; /* 36 */
+ UCHAR MaximumBlockTransfer; /* 5E */
+ UCHAR VendorUnique2; /* 5F */
+ USHORT DoubleWordIo; /* 60 */
+ USHORT Capabilities; /* 62 */
+ USHORT Reserved2; /* 64 */
+ UCHAR VendorUnique3; /* 66 */
+ UCHAR PioCycleTimingMode; /* 67 */
+ UCHAR VendorUnique4; /* 68 */
+ UCHAR DmaCycleTimingMode; /* 69 */
+ USHORT TranslationFieldsValid; /* 6A */
+ USHORT NumberOfCurrentCylinders; /* 6C */
+ USHORT NumberOfCurrentHeads; /* 6E */
+ USHORT CurrentSectorsPerTrack; /* 70 */
+ ULONG CurrentSectorCapacity; /* 72 */
+ } CNT_IDE_INQ;
+
+typedef struct _DASD_INQUIRE
+ {
+ ULONG type; /* 0 = SCSI, 1 = IDE */
+ union
+ {
+ CNT_SCSI_INQ scsi; /* SCSI inquire data */
+ CNT_IDE_INQ ide; /* IDE inquire data */
+ } inq;
+ } DASD_INQUIRE;
+
+/************************************************/
+/* */
+/* Device Codes */
+/* */
+/************************************************/
+#define DEVC_DASD 0x00 /* Direct-access Storage Device */
+#define DEVC_SEQACESS 0x01 /* Sequential-access device */
+#define DEVC_PRINTER 0x02 /* Printer device */
+#define DEVC_PROCESSOR 0x03 /* Processor device */
+#define DEVC_WRITEONCE 0x04 /* Write-once device */
+#define DEVC_CDROM 0x05 /* CD-ROM device */
+#define DEVC_SCANNER 0x06 /* Scanner device */
+#define DEVC_OPTICAL 0x07 /* Optical memory device */
+#define DEVC_MEDCHGR 0x08 /* Medium changer device */
+#define DEVC_DASD_REMOVABLE 0x80 /* Direct-access storage device, Removable */
+#define DEVC_NONE 0xFF /* no device */
+
+// SCSI controls for RAID
+#define SC_MY_RAID 0xBF // our special CDB command byte for Win95... interface
+#define MY_SCSI_QUERY0 0x31 // byte 1 subcommand to query driver for RAID 0 informatation
+#define MY_SCSI_QUERY1 0x32 // byte 1 subcommand to query driver for RAID 1 informatation
+#define MY_SCSI_QUERY5 0x33 // byte 1 subcommand to query driver for RAID 5 informatation
+#define MY_SCSI_REBUILD 0x40 // byte 1 subcommand to reconstruct a mirrored pair
+#define MY_SCSI_DEMOFAIL 0x54 // byte 1 subcommand for RAID failure demonstration
+#define MY_SCSI_ALARMMUTE 0x60 // byte 1 subcommand to mute any alarm currently on
+
+
+#endif
+
diff --git a/drivers/scsi/ql1040_fw.h b/drivers/scsi/ql1040_fw.h
new file mode 100644
index 000000000000..89d8e09ec387
--- /dev/null
+++ b/drivers/scsi/ql1040_fw.h
@@ -0,0 +1,2099 @@
+/**************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * Copyright (C) 2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *************************************************************************/
+
+/************************************************************************
+ * *
+ * --- ISP1040 Initiator/Target Firmware --- *
+ * 32 LUN Support *
+ * *
+ ************************************************************************
+ */
+
+/*
+ * Firmware Version 7.65.00 (14:17 Jul 20, 1999)
+ */
+
+static unsigned char firmware_version[] = {7,65,0};
+
+#define FW_VERSION_STRING "7.65.0"
+
+static unsigned short risc_code_addr01 = 0x1000 ;
+
+static unsigned short risc_code01[] = {
+ 0x0078, 0x103a, 0x0000, 0x4057, 0x0000, 0x2043, 0x4f50, 0x5952,
+ 0x4947, 0x4854, 0x2031, 0x3939, 0x3520, 0x514c, 0x4f47, 0x4943,
+ 0x2043, 0x4f52, 0x504f, 0x5241, 0x5449, 0x4f4e, 0x2049, 0x5350,
+ 0x3130, 0x3230, 0x2049, 0x2f54, 0x2046, 0x6972, 0x6d77, 0x6172,
+ 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030, 0x372e, 0x3635,
+ 0x2020, 0x2043, 0x7573, 0x746f, 0x6d65, 0x7220, 0x4e6f, 0x2e20,
+ 0x3030, 0x2050, 0x726f, 0x6475, 0x6374, 0x204e, 0x6f2e, 0x2020,
+ 0x3031, 0x2024, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x0048,
+ 0x1045, 0x0038, 0x104b, 0x0078, 0x1047, 0x0028, 0x104b, 0x20b9,
+ 0x1212, 0x0078, 0x104d, 0x20b9, 0x2222, 0x20c1, 0x0008, 0x2071,
+ 0x0010, 0x70c3, 0x0004, 0x20c9, 0x77ff, 0x2089, 0x1186, 0x70c7,
+ 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0007, 0x3f00,
+ 0x70d6, 0x20c1, 0x0008, 0x2019, 0x0000, 0x2009, 0xfeff, 0x2100,
+ 0x200b, 0xa5a5, 0xa1ec, 0x7fff, 0x2d64, 0x206b, 0x0a0a, 0xaddc,
+ 0x3fff, 0x2b54, 0x205b, 0x5050, 0x2114, 0xa286, 0xa5a5, 0x0040,
+ 0x10bf, 0xa386, 0x000f, 0x0040, 0x1085, 0x2c6a, 0x2a5a, 0x20c1,
+ 0x0000, 0x2019, 0x000f, 0x0078, 0x1065, 0x2c6a, 0x2a5a, 0x20c1,
+ 0x0008, 0x2009, 0x7fff, 0x2148, 0x2944, 0x204b, 0x0a0a, 0xa9bc,
+ 0x3fff, 0x2734, 0x203b, 0x5050, 0x2114, 0xa286, 0x0a0a, 0x0040,
+ 0x10a9, 0x284a, 0x263a, 0x20c1, 0x0004, 0x2009, 0x3fff, 0x2134,
+ 0x200b, 0x5050, 0x2114, 0xa286, 0x5050, 0x0040, 0x10aa, 0x0078,
+ 0x118e, 0x284a, 0x263a, 0x98c0, 0xa188, 0x1000, 0x212c, 0x200b,
+ 0xa5a5, 0x2114, 0xa286, 0xa5a5, 0x0040, 0x10bc, 0x250a, 0xa18a,
+ 0x1000, 0x98c1, 0x0078, 0x10c1, 0x250a, 0x0078, 0x10c1, 0x2c6a,
+ 0x2a5a, 0x2130, 0xa18a, 0x0040, 0x2128, 0xa1a2, 0x5100, 0x8424,
+ 0x8424, 0x8424, 0x8424, 0x8424, 0x8424, 0xa192, 0x7800, 0x2009,
+ 0x0000, 0x2001, 0x0031, 0x1078, 0x1cba, 0x2218, 0x2079, 0x5100,
+ 0x2fa0, 0x2408, 0x2011, 0x0000, 0x20a9, 0x0040, 0x42a4, 0x8109,
+ 0x00c0, 0x10dc, 0x7ef2, 0x8528, 0x7de6, 0x7cea, 0x7bee, 0x7883,
+ 0x0000, 0x2031, 0x0030, 0x78cf, 0x0101, 0x780b, 0x0002, 0x780f,
+ 0x0002, 0x784f, 0x0003, 0x2069, 0x5140, 0x2001, 0x04fd, 0x2004,
+ 0xa082, 0x0005, 0x0048, 0x1104, 0x0038, 0x1100, 0x0078, 0x1108,
+ 0x681b, 0x003c, 0x0078, 0x110a, 0x00a8, 0x1108, 0x681b, 0x003c,
+ 0x681b, 0x0028, 0x6807, 0x0007, 0x680b, 0x00fa, 0x680f, 0x0008,
+ 0x6813, 0x0005, 0x6823, 0x0000, 0x6827, 0x0006, 0x6817, 0x0008,
+ 0x682b, 0x0000, 0x681f, 0x0019, 0x2069, 0x5380, 0x2011, 0x0020,
+ 0x2009, 0x0010, 0x680b, 0x080c, 0x680f, 0x0019, 0x6803, 0xfd00,
+ 0x6807, 0x0018, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, 0xa290, 0x0004,
+ 0x8109, 0x00c0, 0x1122, 0x2069, 0x5400, 0x2009, 0x0002, 0x20a9,
+ 0x0100, 0x6837, 0x0000, 0x680b, 0x0040, 0x7bf0, 0xa386, 0xfeff,
+ 0x00c0, 0x1148, 0x6817, 0x0100, 0x681f, 0x0064, 0x0078, 0x114c,
+ 0x6817, 0x0064, 0x681f, 0x0002, 0xade8, 0x0010, 0x0070, 0x1152,
+ 0x0078, 0x1139, 0x8109, 0x00c0, 0x1137, 0x1078, 0x220a, 0x1078,
+ 0x482c, 0x1078, 0x1963, 0x1078, 0x4d22, 0x3200, 0xa085, 0x000d,
+ 0x2090, 0x70c3, 0x0000, 0x0090, 0x116c, 0x70c0, 0xa086, 0x0002,
+ 0x00c0, 0x116c, 0x1078, 0x1284, 0x1078, 0x1196, 0x78cc, 0xa005,
+ 0x00c0, 0x117a, 0x1078, 0x1ce3, 0x0010, 0x1180, 0x0068, 0x1180,
+ 0x1078, 0x20e9, 0x0010, 0x1180, 0x0068, 0x1180, 0x1078, 0x1a48,
+ 0x00e0, 0x116c, 0x1078, 0x4ba9, 0x0078, 0x116c, 0x118e, 0x1190,
+ 0x240b, 0x240b, 0x48ad, 0x48ad, 0x240b, 0x240b, 0x0078, 0x118e,
+ 0x0078, 0x1190, 0x0078, 0x1192, 0x0078, 0x1194, 0x0068, 0x1201,
+ 0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1201, 0x7814,
+ 0xa005, 0x00c0, 0x11a7, 0x0010, 0x1202, 0x0078, 0x1201, 0x2009,
+ 0x515b, 0x2104, 0xa005, 0x00c0, 0x1201, 0x2009, 0x5164, 0x200b,
+ 0x0000, 0x7914, 0xa186, 0x0042, 0x00c0, 0x11cc, 0x7816, 0x2009,
+ 0x5162, 0x2164, 0x200b, 0x0000, 0x6018, 0x70c6, 0x6014, 0x70ca,
+ 0x611c, 0xa18c, 0xff00, 0x6020, 0xa084, 0x00ff, 0xa105, 0x70ce,
+ 0x1078, 0x1948, 0x0078, 0x11ff, 0x7814, 0xa086, 0x0018, 0x00c0,
+ 0x11d3, 0x1078, 0x165a, 0x7817, 0x0000, 0x2009, 0x5162, 0x2104,
+ 0xa065, 0x0040, 0x11ef, 0x0c7e, 0x609c, 0x2060, 0x1078, 0x19b3,
+ 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009, 0x000c, 0x6007,
+ 0x0103, 0x1078, 0x1924, 0x00c0, 0x11fb, 0x1078, 0x1948, 0x2009,
+ 0x5162, 0x200b, 0x0000, 0x2009, 0x515c, 0x2104, 0x200b, 0x0000,
+ 0xa005, 0x0040, 0x11ff, 0x2001, 0x4005, 0x0078, 0x1286, 0x0078,
+ 0x1284, 0x007c, 0x70c3, 0x0000, 0x70c7, 0x0000, 0x70cb, 0x0000,
+ 0x70cf, 0x0000, 0x70c0, 0xa0bc, 0xffc0, 0x00c0, 0x1252, 0x2038,
+ 0x0079, 0x1212, 0x1284, 0x12e5, 0x12a9, 0x12fe, 0x130d, 0x1313,
+ 0x12a0, 0x1748, 0x1317, 0x1298, 0x12ad, 0x12af, 0x12b1, 0x12b3,
+ 0x174d, 0x1298, 0x1329, 0x1360, 0x1672, 0x1742, 0x12b5, 0x1591,
+ 0x15ad, 0x15c9, 0x15f4, 0x154a, 0x1558, 0x156c, 0x1580, 0x13df,
+ 0x1298, 0x138d, 0x1393, 0x1398, 0x139d, 0x13a3, 0x13a8, 0x13ad,
+ 0x13b2, 0x13b7, 0x13bb, 0x13d0, 0x13dc, 0x1298, 0x1298, 0x1298,
+ 0x1298, 0x13eb, 0x13f4, 0x1403, 0x1429, 0x1433, 0x143a, 0x1480,
+ 0x148f, 0x149e, 0x14b0, 0x152a, 0x153a, 0x1298, 0x1298, 0x1298,
+ 0x1298, 0x153f, 0xa0bc, 0xffa0, 0x00c0, 0x1298, 0x2038, 0xa084,
+ 0x001f, 0x0079, 0x125b, 0x1786, 0x1789, 0x1799, 0x1298, 0x1298,
+ 0x18df, 0x18fc, 0x1298, 0x1298, 0x1298, 0x1900, 0x1908, 0x1298,
+ 0x1298, 0x1298, 0x1298, 0x12db, 0x12f4, 0x131f, 0x1356, 0x1668,
+ 0x1764, 0x1778, 0x1298, 0x1829, 0x190e, 0x18bb, 0x18c5, 0x18c9,
+ 0x18d7, 0x1298, 0x1298, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0078,
+ 0x1286, 0x73ce, 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x0068,
+ 0x1287, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x00e0,
+ 0x128f, 0x00e0, 0x1291, 0x0068, 0x1291, 0x2091, 0x4080, 0x007c,
+ 0x70c3, 0x4001, 0x0078, 0x1287, 0x70c3, 0x4006, 0x0078, 0x1287,
+ 0x2099, 0x0041, 0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0078,
+ 0x1284, 0x70c4, 0x70c3, 0x0004, 0x007a, 0x0078, 0x1284, 0x0078,
+ 0x1284, 0x0078, 0x1284, 0x0078, 0x1284, 0x2091, 0x8000, 0x70c3,
+ 0x0000, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3,
+ 0x0007, 0x3f00, 0x70d6, 0x2079, 0x0000, 0x781b, 0x0001, 0x2031,
+ 0x0030, 0x2059, 0x1000, 0x2029, 0x0457, 0x2051, 0x0470, 0x2061,
+ 0x0472, 0x20b9, 0xffff, 0x20c1, 0x0000, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x0078, 0x0455, 0x1078, 0x1b53, 0x00c0, 0x129c, 0x75d8,
+ 0x74dc, 0x75da, 0x74de, 0x0078, 0x12e8, 0x2029, 0x0000, 0x2520,
+ 0x71d0, 0x73c8, 0x72cc, 0x70c4, 0x1078, 0x1a8d, 0x0040, 0x1284,
+ 0x70c3, 0x4002, 0x0078, 0x1284, 0x1078, 0x1b53, 0x00c0, 0x129c,
+ 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x1301, 0x2029, 0x0000,
+ 0x2520, 0x71d0, 0x73c8, 0x72cc, 0x70c4, 0x1078, 0x1aed, 0x0040,
+ 0x1284, 0x70c3, 0x4002, 0x0078, 0x1284, 0x71c4, 0x70c8, 0x2114,
+ 0x200a, 0x0078, 0x1282, 0x71c4, 0x2114, 0x0078, 0x1282, 0x70c7,
+ 0x0007, 0x70cb, 0x0041, 0x70cf, 0x0000, 0x0078, 0x1284, 0x1078,
+ 0x1b53, 0x00c0, 0x129c, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078,
+ 0x132c, 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d0,
+ 0x70c6, 0x72ca, 0x73ce, 0x74d2, 0xa005, 0x0040, 0x1350, 0x8001,
+ 0x7892, 0xa084, 0xfc00, 0x0040, 0x1345, 0x78cc, 0xa085, 0x0001,
+ 0x78ce, 0x2001, 0x4005, 0x0078, 0x1286, 0x7a9a, 0x7b9e, 0x7da2,
+ 0x7ea6, 0x7c96, 0x78cc, 0xa084, 0xfffc, 0x78ce, 0x0078, 0x1354,
+ 0x78cc, 0xa085, 0x0001, 0x78ce, 0x0078, 0x1284, 0x1078, 0x1b53,
+ 0x00c0, 0x129c, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078, 0x1363,
+ 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d4, 0x70c6,
+ 0x72ca, 0x73ce, 0x74d6, 0xa005, 0x0040, 0x1387, 0x8001, 0x78ae,
+ 0xa084, 0xfc00, 0x0040, 0x137c, 0x78cc, 0xa085, 0x0100, 0x78ce,
+ 0x2001, 0x4005, 0x0078, 0x1286, 0x7ab6, 0x7bba, 0x7dbe, 0x7ec2,
+ 0x7cb2, 0x78cc, 0xa084, 0xfcff, 0x78ce, 0x0078, 0x138b, 0x78cc,
+ 0xa085, 0x0100, 0x78ce, 0x0078, 0x1284, 0x2009, 0x5161, 0x210c,
+ 0x7aec, 0x0078, 0x1282, 0x2009, 0x5141, 0x210c, 0x0078, 0x1283,
+ 0x2009, 0x5142, 0x210c, 0x0078, 0x1283, 0x2061, 0x5140, 0x610c,
+ 0x6210, 0x0078, 0x1282, 0x2009, 0x5145, 0x210c, 0x0078, 0x1283,
+ 0x2009, 0x5146, 0x210c, 0x0078, 0x1283, 0x2009, 0x5148, 0x210c,
+ 0x0078, 0x1283, 0x2009, 0x5149, 0x210c, 0x0078, 0x1283, 0x7908,
+ 0x7a0c, 0x0078, 0x1282, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa0e8, 0x5380, 0x6a00, 0x6804, 0xa084, 0x0008,
+ 0x0040, 0x13cd, 0x6b08, 0x0078, 0x13ce, 0x6b0c, 0x0078, 0x1281,
+ 0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6b1c, 0x6a14, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x1281, 0x794c, 0x0078, 0x1283, 0x77c4,
+ 0x1078, 0x1973, 0x2091, 0x8000, 0x6908, 0x6a18, 0x6b10, 0x2091,
+ 0x8001, 0x0078, 0x1281, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x127c,
+ 0x1078, 0x22e2, 0x0078, 0x1281, 0x71c4, 0xa182, 0x0010, 0x00c8,
+ 0x127c, 0x2011, 0x5141, 0x2204, 0x007e, 0x2112, 0x1078, 0x229b,
+ 0x017f, 0x0078, 0x1283, 0x71c4, 0x2011, 0x1421, 0x20a9, 0x0008,
+ 0x2204, 0xa106, 0x0040, 0x1413, 0x8210, 0x0070, 0x1411, 0x0078,
+ 0x1408, 0x0078, 0x127c, 0xa292, 0x1421, 0x027e, 0x2011, 0x5142,
+ 0x2204, 0x2112, 0x017f, 0x007e, 0x1078, 0x22a7, 0x017f, 0x0078,
+ 0x1283, 0x03e8, 0x00fa, 0x01f4, 0x02ee, 0x0064, 0x0019, 0x0032,
+ 0x004b, 0x2061, 0x5140, 0x610c, 0x6210, 0x70c4, 0x600e, 0x70c8,
+ 0x6012, 0x0078, 0x1282, 0x2061, 0x5140, 0x6114, 0x70c4, 0x6016,
+ 0x0078, 0x1283, 0x2061, 0x5140, 0x71c4, 0x2011, 0x0004, 0x601f,
+ 0x0019, 0x2019, 0x1212, 0xa186, 0x0028, 0x0040, 0x145b, 0x2011,
+ 0x0005, 0x601f, 0x0019, 0x2019, 0x1212, 0xa186, 0x0032, 0x0040,
+ 0x145b, 0x2011, 0x0006, 0x601f, 0x000c, 0x2019, 0x2222, 0xa186,
+ 0x003c, 0x00c0, 0x127c, 0x6018, 0x007e, 0x611a, 0x7800, 0xa084,
+ 0x0001, 0x00c0, 0x1476, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+ 0x0048, 0x146e, 0x0038, 0x1472, 0x0078, 0x1476, 0x0028, 0x1472,
+ 0x0078, 0x1476, 0x2019, 0x2222, 0x0078, 0x1478, 0x2019, 0x1212,
+ 0x23b8, 0x1078, 0x22b8, 0x1078, 0x4d22, 0x017f, 0x0078, 0x1283,
+ 0x71c4, 0xa184, 0xffcf, 0x00c0, 0x127c, 0x2011, 0x5148, 0x2204,
+ 0x2112, 0x007e, 0x1078, 0x22da, 0x017f, 0x0078, 0x1283, 0x71c4,
+ 0xa182, 0x0010, 0x00c8, 0x127c, 0x2011, 0x5149, 0x2204, 0x007e,
+ 0x2112, 0x1078, 0x22c9, 0x017f, 0x0078, 0x1283, 0x71c4, 0x72c8,
+ 0xa184, 0xfffd, 0x00c0, 0x127b, 0xa284, 0xfffd, 0x00c0, 0x127b,
+ 0x2100, 0x7908, 0x780a, 0x2200, 0x7a0c, 0x780e, 0x0078, 0x1282,
+ 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8,
+ 0x5380, 0x2019, 0x0000, 0x72c8, 0xa284, 0x0080, 0x0040, 0x14c6,
+ 0x6c14, 0x84ff, 0x00c0, 0x14c6, 0x6817, 0x0040, 0xa284, 0x0040,
+ 0x0040, 0x14d0, 0x6c10, 0x84ff, 0x00c0, 0x14d0, 0x6813, 0x0001,
+ 0x6800, 0x007e, 0xa226, 0x0040, 0x14f3, 0x6a02, 0xa484, 0x2000,
+ 0x0040, 0x14dc, 0xa39d, 0x0010, 0xa484, 0x1000, 0x0040, 0x14e2,
+ 0xa39d, 0x0008, 0xa484, 0x4000, 0x0040, 0x14f3, 0x810f, 0xa284,
+ 0x4000, 0x0040, 0x14ef, 0x1078, 0x22fc, 0x0078, 0x14f3, 0x1078,
+ 0x22ee, 0x0078, 0x14f3, 0x72cc, 0x6808, 0xa206, 0x0040, 0x1522,
+ 0xa2a4, 0x00ff, 0x2061, 0x5140, 0x6118, 0xa186, 0x0028, 0x0040,
+ 0x1509, 0xa186, 0x0032, 0x0040, 0x150f, 0xa186, 0x003c, 0x0040,
+ 0x1515, 0xa482, 0x0064, 0x0048, 0x151f, 0x0078, 0x1519, 0xa482,
+ 0x0050, 0x0048, 0x151f, 0x0078, 0x1519, 0xa482, 0x0043, 0x0048,
+ 0x151f, 0x71c4, 0x71c6, 0x027f, 0x72ca, 0x0078, 0x127d, 0x6a0a,
+ 0xa39d, 0x000a, 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c, 0x71c4,
+ 0x0078, 0x1281, 0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6a14,
+ 0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e, 0x2708,
+ 0x0078, 0x1281, 0x70c4, 0x794c, 0x784e, 0x0078, 0x1283, 0x71c4,
+ 0x72c8, 0x73cc, 0xa182, 0x0010, 0x00c8, 0x127c, 0x1078, 0x230a,
+ 0x0078, 0x1281, 0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6a08,
+ 0xa295, 0x0002, 0x6a0a, 0x2091, 0x8001, 0x2708, 0x0078, 0x1282,
+ 0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6a08, 0xa294, 0xfff9,
+ 0x6a0a, 0x6804, 0xa005, 0x0040, 0x1567, 0x1078, 0x21d2, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x1282, 0x77c4, 0x1078, 0x1973, 0x2091,
+ 0x8000, 0x6a08, 0xa295, 0x0004, 0x6a0a, 0x6804, 0xa005, 0x0040,
+ 0x157b, 0x1078, 0x21d2, 0x2091, 0x8001, 0x2708, 0x0078, 0x1282,
+ 0x77c4, 0x2041, 0x0001, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091,
+ 0x8000, 0x1078, 0x1980, 0x2091, 0x8001, 0x2708, 0x6a08, 0x0078,
+ 0x1282, 0x77c4, 0x72c8, 0x73cc, 0x77c6, 0x72ca, 0x73ce, 0x1078,
+ 0x19e1, 0x00c0, 0x15a9, 0x6818, 0xa005, 0x0040, 0x15a9, 0x2708,
+ 0x1078, 0x231a, 0x00c0, 0x15a9, 0x7817, 0x0015, 0x2091, 0x8001,
+ 0x007c, 0x2091, 0x8001, 0x0078, 0x1284, 0x77c4, 0x77c6, 0x2041,
+ 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078,
+ 0x1980, 0x2061, 0x5140, 0x606f, 0x0003, 0x6782, 0x6093, 0x000f,
+ 0x6073, 0x0000, 0x7817, 0x0016, 0x1078, 0x21d2, 0x2091, 0x8001,
+ 0x007c, 0x77c8, 0x77ca, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2091,
+ 0x8000, 0x2061, 0x5140, 0x606f, 0x0002, 0x6073, 0x0000, 0x6782,
+ 0x6093, 0x000f, 0x7817, 0x0017, 0x1078, 0x21d2, 0x2091, 0x8001,
+ 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010, 0x2091, 0x8000,
+ 0x1078, 0x1980, 0x70c8, 0x6836, 0x8738, 0xa784, 0x001f, 0x00c0,
+ 0x15e8, 0x2091, 0x8001, 0x007c, 0x78cc, 0xa084, 0x0003, 0x00c0,
+ 0x1618, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051,
+ 0x0008, 0x1078, 0x1973, 0x2091, 0x8000, 0x6808, 0xa80d, 0x690a,
+ 0x2091, 0x8001, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1601, 0xa7bc,
+ 0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1601,
+ 0x2091, 0x8000, 0x2069, 0x0100, 0x6830, 0xa084, 0x0040, 0x0040,
+ 0x1641, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0004,
+ 0x0040, 0x162e, 0x0070, 0x162e, 0x0078, 0x1625, 0x684b, 0x0009,
+ 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040, 0x163b, 0x0070,
+ 0x163b, 0x0078, 0x1632, 0x20a9, 0x00fa, 0x0070, 0x1641, 0x0078,
+ 0x163d, 0x2079, 0x5100, 0x7817, 0x0018, 0x2061, 0x5140, 0x606f,
+ 0x0001, 0x6073, 0x0000, 0x6093, 0x000f, 0x78cc, 0xa085, 0x0002,
+ 0x78ce, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b, 0x0048, 0x2091,
+ 0x8001, 0x007c, 0x78cc, 0xa084, 0xfffd, 0x78ce, 0xa084, 0x0001,
+ 0x00c0, 0x1664, 0x1078, 0x1a2b, 0x71c4, 0x71c6, 0x794a, 0x007c,
+ 0x1078, 0x1b53, 0x00c0, 0x129c, 0x75d8, 0x74dc, 0x75da, 0x74de,
+ 0x0078, 0x1675, 0x2029, 0x0000, 0x2520, 0x71c4, 0x73c8, 0x72cc,
+ 0x71c6, 0x73ca, 0x72ce, 0x2079, 0x5100, 0x2091, 0x8000, 0x1078,
+ 0x192e, 0x2091, 0x8001, 0x0040, 0x172c, 0x20a9, 0x0005, 0x20a1,
+ 0x5118, 0x2091, 0x8000, 0x41a1, 0x2091, 0x8001, 0x2009, 0x0020,
+ 0x1078, 0x1929, 0x0040, 0x1698, 0x1078, 0x1948, 0x0078, 0x172c,
+ 0x6004, 0xa084, 0xff00, 0x8007, 0x8009, 0x0040, 0x16fb, 0x0c7e,
+ 0x2c68, 0x2091, 0x8000, 0x1078, 0x192e, 0x2091, 0x8001, 0x0040,
+ 0x16cc, 0x2c00, 0x689e, 0x8109, 0x00c0, 0x16a0, 0x609f, 0x0000,
+ 0x0c7f, 0x0c7e, 0x7218, 0x731c, 0x7420, 0x7524, 0x2c68, 0x689c,
+ 0xa065, 0x0040, 0x16fa, 0x2009, 0x0020, 0x1078, 0x1929, 0x00c0,
+ 0x16e3, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0002, 0x00c0, 0x16cc,
+ 0x2d00, 0x6002, 0x0078, 0x16b2, 0x0c7f, 0x0c7e, 0x609c, 0x2060,
+ 0x1078, 0x19b3, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009,
+ 0x000c, 0x6008, 0xa085, 0x0200, 0x600a, 0x1078, 0x1924, 0x1078,
+ 0x1948, 0x0078, 0x172c, 0x0c7f, 0x0c7e, 0x609c, 0x2060, 0x1078,
+ 0x19b3, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009, 0x000c,
+ 0x6007, 0x0103, 0x601b, 0x0003, 0x1078, 0x1924, 0x1078, 0x1948,
+ 0x0078, 0x172c, 0x0c7f, 0x74c4, 0x73c8, 0x72cc, 0x6014, 0x2091,
+ 0x8000, 0x7817, 0x0012, 0x0e7e, 0x2071, 0x5140, 0x706f, 0x0005,
+ 0x7073, 0x0000, 0x7376, 0x727a, 0x747e, 0x7082, 0x7087, 0x0000,
+ 0x2c00, 0x708a, 0x708f, 0x0000, 0xa02e, 0x2530, 0x611c, 0x61a2,
+ 0xa184, 0x0060, 0x0040, 0x171e, 0x1078, 0x47c2, 0x0e7f, 0x6596,
+ 0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000, 0x60b3, 0x0000, 0x1078,
+ 0x21d2, 0x2091, 0x8001, 0x007c, 0x70c3, 0x4005, 0x0078, 0x1287,
+ 0x20a9, 0x0005, 0x2099, 0x5118, 0x2091, 0x8000, 0x530a, 0x2091,
+ 0x8001, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+ 0x0000, 0x007c, 0x71c4, 0x70c7, 0x0000, 0x7906, 0x0078, 0x1284,
+ 0x71c4, 0x71c6, 0x2168, 0x0078, 0x174f, 0x2069, 0x1000, 0x690c,
+ 0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x00c0, 0x1751, 0xa285,
+ 0x0000, 0x00c0, 0x175f, 0x70c3, 0x4000, 0x0078, 0x1761, 0x70c3,
+ 0x4003, 0x70ca, 0x0078, 0x1287, 0x2011, 0x5167, 0x220c, 0x70c4,
+ 0x8003, 0x0048, 0x1771, 0x1078, 0x3b7f, 0xa184, 0x7fff, 0x0078,
+ 0x1775, 0x1078, 0x3b72, 0xa185, 0x8000, 0x2012, 0x0078, 0x1283,
+ 0x71c4, 0x1078, 0x3b69, 0x6100, 0x2001, 0x5167, 0x2004, 0xa084,
+ 0x8000, 0xa10d, 0x6204, 0x6308, 0x0078, 0x1281, 0x79e4, 0x0078,
+ 0x1283, 0x71c4, 0x71c6, 0x2198, 0x20a1, 0x0042, 0x20a9, 0x0004,
+ 0x53a3, 0x21a0, 0x2099, 0x0042, 0x20a9, 0x0004, 0x53a3, 0x0078,
+ 0x1284, 0x70c4, 0x2068, 0x2079, 0x5100, 0x2091, 0x8000, 0x1078,
+ 0x192e, 0x2091, 0x8001, 0x0040, 0x1825, 0x6007, 0x0001, 0x600b,
+ 0x0000, 0x602b, 0x0000, 0x601b, 0x0006, 0x6a10, 0xa28c, 0x000f,
+ 0xa284, 0x00f0, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0x6016,
+ 0xa284, 0x0800, 0x0040, 0x17c0, 0x601b, 0x000a, 0x0078, 0x17c6,
+ 0xa284, 0x1000, 0x0040, 0x17c6, 0x601b, 0x000c, 0xa284, 0x0300,
+ 0x0040, 0x17cf, 0x602b, 0x0001, 0x8004, 0x8004, 0x8004, 0xa085,
+ 0x0001, 0x601e, 0x6023, 0x0000, 0x6027, 0x0000, 0xa284, 0x0400,
+ 0x0040, 0x17dc, 0x602b, 0x0000, 0x20a9, 0x0006, 0xac80, 0x000b,
+ 0x20a0, 0xad80, 0x0005, 0x2098, 0x53a3, 0xa284, 0x0300, 0x00c0,
+ 0x17f1, 0x6046, 0x604a, 0x604e, 0x6052, 0x6096, 0x609a, 0x0078,
+ 0x17fb, 0x6800, 0x6046, 0x6804, 0x604a, 0x6e08, 0x664e, 0x6d0c,
+ 0x6552, 0x6596, 0x669a, 0x6014, 0x2091, 0x8000, 0x7817, 0x0042,
+ 0x2c08, 0x2061, 0x5140, 0x606f, 0x0005, 0x6073, 0x0000, 0x6077,
+ 0x0000, 0x607b, 0x0000, 0x607f, 0x0000, 0x6082, 0x618a, 0xa284,
+ 0x0400, 0x608e, 0x2091, 0x8001, 0x0e7e, 0x2071, 0x0020, 0x7007,
+ 0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x0e7f, 0x2091, 0x8000,
+ 0x1078, 0x21d2, 0x2091, 0x8001, 0x007c, 0x70c3, 0x4005, 0x0078,
+ 0x1287, 0x0c7e, 0x0d7e, 0x0e7e, 0x0f7e, 0x2091, 0x8000, 0x2071,
+ 0x5140, 0x2079, 0x0100, 0x2061, 0x0010, 0x70a0, 0xa06d, 0x0040,
+ 0x18b1, 0x6a04, 0xa294, 0x00ff, 0xa286, 0x0007, 0x0040, 0x1844,
+ 0xa286, 0x000f, 0x00c0, 0x18b1, 0x691c, 0xa184, 0x0080, 0x00c0,
+ 0x18b1, 0x6824, 0xa18c, 0xff00, 0xa085, 0x0019, 0x6826, 0x71b0,
+ 0x81ff, 0x0040, 0x1867, 0x0d7e, 0x2069, 0x0020, 0x6807, 0x0010,
+ 0x6908, 0x6808, 0xa106, 0x00c0, 0x1858, 0x690c, 0x680c, 0xa106,
+ 0x00c0, 0x185d, 0xa184, 0x00ff, 0x00c0, 0x185d, 0x0d7f, 0x78b8,
+ 0xa084, 0x801f, 0x00c0, 0x1867, 0x7848, 0xa085, 0x000c, 0x784a,
+ 0x71b0, 0x81ff, 0x0040, 0x188a, 0x70b3, 0x0000, 0x0d7e, 0x2069,
+ 0x0020, 0x6807, 0x0018, 0x6804, 0xa084, 0x0008, 0x00c0, 0x187b,
+ 0x6807, 0x0008, 0x6804, 0xa084, 0x0008, 0x00c0, 0x1882, 0x6807,
+ 0x0002, 0x0d7f, 0x61c4, 0x62c8, 0x63cc, 0x61c6, 0x62ca, 0x63ce,
+ 0x0e7e, 0x2071, 0x5100, 0x7266, 0x736a, 0xae80, 0x0019, 0x0e7f,
+ 0x7848, 0xa084, 0x000c, 0x00c0, 0x1898, 0x1078, 0x46db, 0x78a3,
+ 0x0000, 0x7858, 0xa084, 0xedff, 0x785a, 0x70b4, 0xa080, 0x00df,
+ 0x781a, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x2091, 0x8001, 0x0078,
+ 0x1284, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x2091, 0x8001, 0x2001,
+ 0x4005, 0x0078, 0x1286, 0x7980, 0x71c6, 0x71c4, 0xa182, 0x0003,
+ 0x00c8, 0x127c, 0x7982, 0x0078, 0x1284, 0x7980, 0x71c6, 0x0078,
+ 0x1284, 0x7974, 0x71c6, 0x71c4, 0x7976, 0x7978, 0x71ca, 0x71c8,
+ 0x797a, 0x797c, 0x71ce, 0x71cc, 0x797e, 0x0078, 0x1284, 0x7974,
+ 0x71c6, 0x7978, 0x71ca, 0x797c, 0x71ce, 0x0078, 0x1284, 0x7900,
+ 0x71c6, 0x71c4, 0x7902, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+ 0x0048, 0x18ee, 0x0038, 0x18f0, 0x0078, 0x18fa, 0x00a8, 0x18fa,
+ 0xa18c, 0x0001, 0x00c0, 0x18f8, 0x20b9, 0x2222, 0x0078, 0x18fa,
+ 0x20b9, 0x1212, 0x0078, 0x1284, 0x7900, 0x71c6, 0x0078, 0x1284,
+ 0x2009, 0x5174, 0x2104, 0x70c6, 0x70c4, 0x200a, 0x0078, 0x1284,
+ 0x2009, 0x5174, 0x2104, 0x70c6, 0x0078, 0x1284, 0x71c4, 0x8107,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8, 0x5380, 0x6a14,
+ 0xd2b4, 0x0040, 0x191f, 0x2011, 0x0001, 0x0078, 0x1921, 0x2011,
+ 0x0000, 0x6b0c, 0x0078, 0x1281, 0xac80, 0x0001, 0x1078, 0x1b0f,
+ 0x007c, 0xac80, 0x0001, 0x1078, 0x1aaf, 0x007c, 0x7850, 0xa065,
+ 0x0040, 0x1936, 0x2c04, 0x7852, 0x2063, 0x0000, 0x007c, 0x0f7e,
+ 0x2079, 0x5100, 0x7850, 0xa06d, 0x0040, 0x1946, 0x2d04, 0x7852,
+ 0x6803, 0x0000, 0x6807, 0x0000, 0x680b, 0x0000, 0x0f7f, 0x007c,
+ 0x2091, 0x8000, 0x0f7e, 0x2079, 0x5100, 0x7850, 0x2062, 0x2c00,
+ 0xa005, 0x00c0, 0x1955, 0x1078, 0x23eb, 0x7852, 0x0f7f, 0x2091,
+ 0x8001, 0x007c, 0x0f7e, 0x2079, 0x5100, 0x7850, 0x206a, 0x2d00,
+ 0x7852, 0x0f7f, 0x007c, 0x2011, 0x7800, 0x7a52, 0x7bec, 0x8319,
+ 0x0040, 0x1970, 0xa280, 0x0031, 0x2012, 0x2010, 0x0078, 0x1967,
+ 0x2013, 0x0000, 0x007c, 0xa784, 0x0f00, 0x800b, 0xa784, 0x001f,
+ 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e8, 0x5400, 0x007c,
+ 0x1078, 0x1973, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808, 0xa084,
+ 0xffef, 0xa80d, 0x690a, 0x2009, 0x5152, 0x210c, 0x6804, 0xa005,
+ 0x0040, 0x19b2, 0xa116, 0x00c0, 0x199d, 0x2060, 0x6000, 0x6806,
+ 0x017e, 0x200b, 0x0000, 0x0078, 0x19a0, 0x2009, 0x0000, 0x017e,
+ 0x6804, 0xa065, 0x0040, 0x19af, 0x6000, 0x6806, 0x1078, 0x19c0,
+ 0x1078, 0x1c5f, 0x6810, 0x8001, 0x6812, 0x00c0, 0x19a0, 0x017f,
+ 0x6902, 0x6906, 0x007c, 0xa065, 0x0040, 0x19bf, 0x609c, 0x609f,
+ 0x0000, 0x2008, 0x1078, 0x1948, 0x2100, 0x0078, 0x19b3, 0x007c,
+ 0x6007, 0x0103, 0x608f, 0x0000, 0x20a9, 0x001c, 0xac80, 0x0005,
+ 0x20a0, 0x2001, 0x0000, 0x40a4, 0x6828, 0x601a, 0x682c, 0x6022,
+ 0x007c, 0x0e7e, 0x2071, 0x5140, 0x704c, 0xa08c, 0x0200, 0x00c0,
+ 0x19df, 0xa088, 0x5180, 0x2d0a, 0x8000, 0x704e, 0xa006, 0x0e7f,
+ 0x007c, 0x1078, 0x1973, 0x2091, 0x8000, 0x6804, 0x781e, 0xa065,
+ 0x0040, 0x1a2a, 0x0078, 0x19f2, 0x2c00, 0x781e, 0x6000, 0xa065,
+ 0x0040, 0x1a2a, 0x600c, 0xa306, 0x00c0, 0x19ec, 0x6010, 0xa206,
+ 0x00c0, 0x19ec, 0x2c28, 0x2001, 0x5152, 0x2004, 0xac06, 0x00c0,
+ 0x1a03, 0x0078, 0x1a28, 0x6804, 0xac06, 0x00c0, 0x1a10, 0x6000,
+ 0xa065, 0x6806, 0x00c0, 0x1a1a, 0x6803, 0x0000, 0x0078, 0x1a1a,
+ 0x6400, 0x781c, 0x2060, 0x6402, 0xa486, 0x0000, 0x00c0, 0x1a1a,
+ 0x2c00, 0x6802, 0x2560, 0x1078, 0x19c0, 0x601b, 0x0005, 0x6023,
+ 0x0020, 0x1078, 0x1c5f, 0x6810, 0x8001, 0x1050, 0x23eb, 0x6812,
+ 0xa085, 0xffff, 0x007c, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049,
+ 0x0004, 0x2051, 0x0008, 0x2091, 0x8000, 0x1078, 0x1980, 0x8738,
+ 0xa784, 0x001f, 0x00c0, 0x1a35, 0xa7bc, 0xff00, 0x873f, 0x8738,
+ 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1a35, 0x2091, 0x8001, 0x007c,
+ 0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1a59, 0x2091,
+ 0x8000, 0x78e0, 0x78e3, 0x0000, 0x2091, 0x8001, 0xa005, 0x00c0,
+ 0x1a5a, 0x007c, 0xa08c, 0xfff0, 0x0040, 0x1a60, 0x1078, 0x23eb,
+ 0x0079, 0x1a62, 0x1a72, 0x1a75, 0x1a7b, 0x1a7f, 0x1a73, 0x1a83,
+ 0x1a89, 0x1a73, 0x1a73, 0x1c29, 0x1c4d, 0x1c51, 0x1a73, 0x1a73,
+ 0x1a73, 0x1a73, 0x007c, 0x1078, 0x23eb, 0x1078, 0x1a2b, 0x2001,
+ 0x8001, 0x0078, 0x1c57, 0x2001, 0x8003, 0x0078, 0x1c57, 0x2001,
+ 0x8004, 0x0078, 0x1c57, 0x1078, 0x1a2b, 0x2001, 0x8006, 0x0078,
+ 0x1c57, 0x2001, 0x8007, 0x0078, 0x1c57, 0x2030, 0x2138, 0xa782,
+ 0x0021, 0x0048, 0x1a95, 0x2009, 0x0020, 0x2600, 0x1078, 0x1aaf,
+ 0x00c0, 0x1aae, 0xa7ba, 0x0020, 0x0048, 0x1aad, 0x0040, 0x1aad,
+ 0x2708, 0xa6b0, 0x0020, 0xa290, 0x0040, 0xa399, 0x0000, 0xa4a1,
+ 0x0000, 0xa5a9, 0x0000, 0x0078, 0x1a8f, 0xa006, 0x007c, 0x81ff,
+ 0x0040, 0x1aea, 0x2099, 0x0030, 0x20a0, 0x700c, 0xa084, 0x00ff,
+ 0x0040, 0x1ac1, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0,
+ 0x1abc, 0x21a8, 0x7017, 0x0000, 0x810b, 0x7112, 0x721a, 0x731e,
+ 0x7422, 0x7526, 0x780c, 0xa085, 0x0001, 0x7002, 0x7007, 0x0001,
+ 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8, 0x1ade, 0x2009,
+ 0x0022, 0x2104, 0xa084, 0x4000, 0x00c0, 0x1ad0, 0x7008, 0x800b,
+ 0x00c8, 0x1ad0, 0x7007, 0x0002, 0xa08c, 0x01e0, 0x00c0, 0x1aea,
+ 0x53a5, 0xa006, 0x7003, 0x0000, 0x007c, 0x2030, 0x2138, 0xa782,
+ 0x0021, 0x0048, 0x1af5, 0x2009, 0x0020, 0x2600, 0x1078, 0x1b0f,
+ 0x00c0, 0x1b0e, 0xa7ba, 0x0020, 0x0048, 0x1b0d, 0x0040, 0x1b0d,
+ 0x2708, 0xa6b0, 0x0020, 0xa290, 0x0040, 0xa399, 0x0000, 0xa4a1,
+ 0x0000, 0xa5a9, 0x0000, 0x0078, 0x1aef, 0xa006, 0x007c, 0x81ff,
+ 0x0040, 0x1b50, 0x2098, 0x20a1, 0x0030, 0x700c, 0xa084, 0x00ff,
+ 0x0040, 0x1b21, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0,
+ 0x1b1c, 0x21a8, 0x7017, 0x0000, 0x810b, 0x7112, 0x721a, 0x731e,
+ 0x7422, 0x7526, 0x780c, 0xa085, 0x0000, 0x7002, 0x53a6, 0x7007,
+ 0x0001, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8, 0x1b3f,
+ 0x2009, 0x0022, 0x2104, 0xa084, 0x4000, 0x00c0, 0x1b31, 0x7010,
+ 0xa084, 0xf000, 0x0040, 0x1b48, 0x7007, 0x0008, 0x0078, 0x1b4c,
+ 0x7108, 0x8103, 0x00c8, 0x1b31, 0x7007, 0x0002, 0xa184, 0x01e0,
+ 0x7003, 0x0000, 0x007c, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0004,
+ 0x00c8, 0x1b5c, 0x0078, 0x1b5f, 0xa006, 0x0078, 0x1b61, 0xa085,
+ 0x0001, 0x007c, 0x0e7e, 0x2071, 0x5100, 0x2d08, 0x7058, 0x6802,
+ 0xa005, 0x00c0, 0x1b6c, 0x715e, 0x715a, 0x0e7f, 0x007c, 0x2c08,
+ 0x7858, 0x6002, 0xa005, 0x00c0, 0x1b76, 0x795e, 0x795a, 0x007c,
+ 0x2091, 0x8000, 0x6003, 0x0000, 0x2c08, 0x785c, 0xa065, 0x00c0,
+ 0x1b84, 0x795a, 0x0078, 0x1b85, 0x6102, 0x795e, 0x2091, 0x8001,
+ 0x1078, 0x21ef, 0x007c, 0x0e7e, 0x2071, 0x5100, 0x7058, 0xa06d,
+ 0x0040, 0x1b99, 0x6800, 0x705a, 0xa005, 0x00c0, 0x1b98, 0x705e,
+ 0x8dff, 0x0e7f, 0x007c, 0x0d7e, 0x0c7e, 0x0f7e, 0x2079, 0x5100,
+ 0xaf80, 0x0016, 0x2060, 0x6000, 0xa005, 0x0040, 0x1bc9, 0x2068,
+ 0x6814, 0xa306, 0x00c0, 0x1bb2, 0x6828, 0xa084, 0x00ff, 0xa406,
+ 0x0040, 0x1bb5, 0x2d60, 0x0078, 0x1ba3, 0x6800, 0xa005, 0x6002,
+ 0x00c0, 0x1bc1, 0xaf80, 0x0016, 0xac06, 0x0040, 0x1bc0, 0x2c00,
+ 0x785e, 0x0d7e, 0x689c, 0xa005, 0x0040, 0x1bc8, 0x1078, 0x19b3,
+ 0x007f, 0x0f7f, 0x0c7f, 0x0d7f, 0xa005, 0x007c, 0x0d7e, 0x0c7e,
+ 0x0f7e, 0x2079, 0x5100, 0xaf80, 0x0016, 0x2060, 0x6000, 0xa005,
+ 0x0040, 0x1bf8, 0x2068, 0x6814, 0xa084, 0x00ff, 0xa306, 0x0040,
+ 0x1be4, 0x2d60, 0x0078, 0x1bd6, 0x6800, 0xa005, 0x6002, 0x00c0,
+ 0x1bf0, 0xaf80, 0x0016, 0xac06, 0x0040, 0x1bef, 0x2c00, 0x785e,
+ 0x0d7e, 0x689c, 0xa005, 0x0040, 0x1bf7, 0x1078, 0x19b3, 0x007f,
+ 0x0f7f, 0x0c7f, 0x0d7f, 0xa005, 0x007c, 0x0d7e, 0x0c7e, 0x0f7e,
+ 0x2079, 0x5100, 0xaf80, 0x0016, 0x2060, 0x6000, 0xa06d, 0x0040,
+ 0x1c24, 0x6814, 0xa306, 0x0040, 0x1c10, 0x2d60, 0x0078, 0x1c05,
+ 0x6800, 0xa005, 0x6002, 0x00c0, 0x1c1c, 0xaf80, 0x0016, 0xac06,
+ 0x0040, 0x1c1b, 0x2c00, 0x785e, 0x0d7e, 0x689c, 0xa005, 0x0040,
+ 0x1c23, 0x1078, 0x19b3, 0x007f, 0x0f7f, 0x0c7f, 0x0d7f, 0xa005,
+ 0x007c, 0x2091, 0x8000, 0x2069, 0x5140, 0x6800, 0xa086, 0x0000,
+ 0x0040, 0x1c37, 0x2091, 0x8001, 0x78e3, 0x0009, 0x007c, 0x6880,
+ 0xa0bc, 0xff00, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010,
+ 0x1078, 0x1980, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1c40, 0x2091,
+ 0x8001, 0x2001, 0x800a, 0x0078, 0x1c57, 0x2001, 0x800c, 0x0078,
+ 0x1c57, 0x1078, 0x1a2b, 0x2001, 0x800d, 0x0078, 0x1c57, 0x70c2,
+ 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x4080, 0x007c, 0x6004,
+ 0x2c08, 0x2063, 0x0000, 0x7884, 0x8000, 0x7886, 0x7888, 0xa005,
+ 0x798a, 0x0040, 0x1c6e, 0x2c02, 0x0078, 0x1c6f, 0x798e, 0x007c,
+ 0x6807, 0x0103, 0x0c7e, 0x2061, 0x5100, 0x2d08, 0x206b, 0x0000,
+ 0x6084, 0x8000, 0x6086, 0x6088, 0xa005, 0x618a, 0x0040, 0x1c83,
+ 0x2d02, 0x0078, 0x1c84, 0x618e, 0x0c7f, 0x007c, 0x1078, 0x1c97,
+ 0x0040, 0x1c96, 0x0c7e, 0x609c, 0xa065, 0x0040, 0x1c91, 0x1078,
+ 0x19b3, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1948, 0x007c, 0x788c,
+ 0xa065, 0x0040, 0x1ca9, 0x2091, 0x8000, 0x7884, 0x8001, 0x7886,
+ 0x2c04, 0x788e, 0xa005, 0x00c0, 0x1ca7, 0x788a, 0x8000, 0x2091,
+ 0x8001, 0x007c, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, 0x818e,
+ 0x00c8, 0x1cb3, 0xa200, 0x0070, 0x1cb7, 0x0078, 0x1cae, 0x8086,
+ 0x818e, 0x007c, 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x1cdd,
+ 0xa11a, 0x00c8, 0x1cdd, 0x8213, 0x818d, 0x0048, 0x1cce, 0xa11a,
+ 0x00c8, 0x1ccf, 0x0070, 0x1cd5, 0x0078, 0x1cc3, 0xa11a, 0x2308,
+ 0x8210, 0x0070, 0x1cd5, 0x0078, 0x1cc3, 0x007e, 0x3200, 0xa084,
+ 0xf7ff, 0x2080, 0x007f, 0x157f, 0x007c, 0x007e, 0x3200, 0xa085,
+ 0x0800, 0x0078, 0x1cd9, 0x7994, 0x70d0, 0xa106, 0x0040, 0x1d51,
+ 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0, 0x1d51,
+ 0x7008, 0x7208, 0xa206, 0x00c0, 0x1d51, 0xa286, 0x0008, 0x00c0,
+ 0x1d51, 0x2071, 0x0010, 0x1078, 0x192e, 0x0040, 0x1d51, 0x7a9c,
+ 0x7b98, 0x7ca4, 0x7da0, 0xa184, 0xff00, 0x0040, 0x1d1f, 0x2031,
+ 0x0000, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x810b,
+ 0x86b5, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x2100, 0xa210, 0x2600,
+ 0xa319, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x0078, 0x1d29, 0x8107,
+ 0x8004, 0x8004, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+ 0x0000, 0x2009, 0x0020, 0x1078, 0x1929, 0x2091, 0x8001, 0x0040,
+ 0x1d48, 0x1078, 0x1948, 0x78a8, 0x8000, 0x78aa, 0xa086, 0x0002,
+ 0x00c0, 0x1d51, 0x2091, 0x8000, 0x78e3, 0x0002, 0x78ab, 0x0000,
+ 0x78cc, 0xa085, 0x0003, 0x78ce, 0x2091, 0x8001, 0x0078, 0x1d51,
+ 0x78ab, 0x0000, 0x1078, 0x20ac, 0x6004, 0xa084, 0x000f, 0x0079,
+ 0x1d56, 0x2071, 0x0010, 0x2091, 0x8001, 0x007c, 0x1d66, 0x1d88,
+ 0x1dae, 0x1d66, 0x1dcb, 0x1d75, 0x1f2c, 0x1f47, 0x1d66, 0x1d82,
+ 0x1da8, 0x1e13, 0x1e82, 0x1ed2, 0x1ee4, 0x1f43, 0x2039, 0x0400,
+ 0x78dc, 0xa705, 0x78de, 0x6008, 0xa705, 0x600a, 0x1078, 0x1fc7,
+ 0x609c, 0x78da, 0x1078, 0x2094, 0x007c, 0x78dc, 0xa084, 0x0100,
+ 0x0040, 0x1d7c, 0x0078, 0x1d66, 0x601c, 0xa085, 0x0080, 0x601e,
+ 0x0078, 0x1d8f, 0x1078, 0x1b53, 0x00c0, 0x1d66, 0x1078, 0x20c6,
+ 0x78dc, 0xa084, 0x0100, 0x0040, 0x1d8f, 0x0078, 0x1d66, 0x78df,
+ 0x0000, 0x6004, 0x8007, 0xa084, 0x00ff, 0x78d2, 0x8001, 0x609f,
+ 0x0000, 0x0040, 0x1da5, 0x1078, 0x1fc7, 0x0040, 0x1da5, 0x78dc,
+ 0xa085, 0x0100, 0x78de, 0x0078, 0x1da7, 0x1078, 0x1feb, 0x007c,
+ 0x1078, 0x1b53, 0x00c0, 0x1d66, 0x1078, 0x20c2, 0x78dc, 0xa08c,
+ 0x0e00, 0x00c0, 0x1db7, 0xa084, 0x0100, 0x00c0, 0x1db9, 0x0078,
+ 0x1d66, 0x1078, 0x1fc7, 0x00c0, 0x1dca, 0x6104, 0xa18c, 0x00ff,
+ 0xa186, 0x0007, 0x0040, 0x1f84, 0xa186, 0x000f, 0x0040, 0x1f84,
+ 0x1078, 0x1feb, 0x007c, 0x78dc, 0xa084, 0x0100, 0x0040, 0x1dd2,
+ 0x0078, 0x1d66, 0x78df, 0x0000, 0x6714, 0x2011, 0x0001, 0x20a9,
+ 0x0001, 0x6018, 0xa084, 0x00ff, 0xa005, 0x0040, 0x1df5, 0x2011,
+ 0x0001, 0xa7bc, 0xff00, 0x20a9, 0x0020, 0xa08e, 0x0001, 0x0040,
+ 0x1df5, 0x2039, 0x0000, 0x2011, 0x0002, 0x20a9, 0x0100, 0xa08e,
+ 0x0002, 0x0040, 0x1df5, 0x0078, 0x1e10, 0x1078, 0x1973, 0x2091,
+ 0x8000, 0x682b, 0x0000, 0x682f, 0x0000, 0x6808, 0xa084, 0xffde,
+ 0x680a, 0xade8, 0x0010, 0x2091, 0x8001, 0x0070, 0x1e09, 0x0078,
+ 0x1df7, 0x8211, 0x0040, 0x1e10, 0x20a9, 0x0100, 0x0078, 0x1df7,
+ 0x1078, 0x1948, 0x007c, 0x2001, 0x5167, 0x2004, 0xa084, 0x8000,
+ 0x0040, 0x1fac, 0x6114, 0x1078, 0x20e3, 0x6900, 0xa184, 0x0001,
+ 0x0040, 0x1e34, 0x6028, 0xa084, 0x00ff, 0x00c0, 0x1fa4, 0x6800,
+ 0xa084, 0x0001, 0x0040, 0x1fac, 0x6803, 0x0000, 0x680b, 0x0000,
+ 0x6807, 0x0000, 0x0078, 0x1fb4, 0x2011, 0x0001, 0x6020, 0xd0f4,
+ 0x0040, 0x1e3c, 0xa295, 0x0002, 0xd0c4, 0x0040, 0x1e41, 0xa295,
+ 0x0008, 0xd0cc, 0x0040, 0x1e46, 0xa295, 0x0400, 0x601c, 0xa084,
+ 0x0002, 0x0040, 0x1e4d, 0xa295, 0x0004, 0x602c, 0xa08c, 0x00ff,
+ 0xa182, 0x0002, 0x0048, 0x1fb0, 0xa182, 0x001b, 0x00c8, 0x1fb0,
+ 0x0040, 0x1fb0, 0x690e, 0x602c, 0x8007, 0xa08c, 0x00ff, 0xa182,
+ 0x0002, 0x0048, 0x1fb0, 0xa182, 0x001b, 0x00c8, 0x1fb0, 0x0040,
+ 0x1fb0, 0x6912, 0x6030, 0xa005, 0x00c0, 0x1e70, 0x2001, 0x001e,
+ 0x8000, 0x6816, 0x6028, 0xa084, 0x00ff, 0x0040, 0x1fac, 0x6806,
+ 0x6028, 0x8007, 0xa084, 0x00ff, 0x0040, 0x1fac, 0x680a, 0x6a02,
+ 0x0078, 0x1fb4, 0x2001, 0x5167, 0x2004, 0xa084, 0x8000, 0x0040,
+ 0x1fac, 0x6114, 0x1078, 0x20e3, 0x2091, 0x8000, 0x6a04, 0x6b08,
+ 0x6418, 0xa484, 0x0003, 0x0040, 0x1ea8, 0x6128, 0xa18c, 0x00ff,
+ 0x8001, 0x00c0, 0x1ea1, 0x2100, 0xa210, 0x0048, 0x1ece, 0x0078,
+ 0x1ea8, 0x8001, 0x00c0, 0x1ece, 0x2100, 0xa212, 0x0048, 0x1ece,
+ 0xa484, 0x000c, 0x0040, 0x1ec2, 0x6128, 0x810f, 0xa18c, 0x00ff,
+ 0xa082, 0x0004, 0x00c0, 0x1eba, 0x2100, 0xa318, 0x0048, 0x1ece,
+ 0x0078, 0x1ec2, 0xa082, 0x0004, 0x00c0, 0x1ece, 0x2100, 0xa31a,
+ 0x0048, 0x1ece, 0x6030, 0xa005, 0x0040, 0x1ec8, 0x8000, 0x6816,
+ 0x6a06, 0x6b0a, 0x2091, 0x8001, 0x0078, 0x1fb4, 0x2091, 0x8001,
+ 0x0078, 0x1fb0, 0x6114, 0x1078, 0x20e3, 0x2091, 0x8000, 0x6b08,
+ 0x8318, 0x0048, 0x1ee0, 0x6b0a, 0x2091, 0x8001, 0x0078, 0x1fc3,
+ 0x2091, 0x8001, 0x0078, 0x1fb0, 0x6024, 0x8007, 0xa084, 0x00ff,
+ 0x0040, 0x1f02, 0xa086, 0x0080, 0x00c0, 0x1f2a, 0x20a9, 0x0008,
+ 0x2069, 0x7510, 0x2091, 0x8000, 0x6800, 0xa084, 0xfcff, 0x6802,
+ 0xade8, 0x0008, 0x0070, 0x1efe, 0x0078, 0x1ef4, 0x2091, 0x8001,
+ 0x0078, 0x1fb4, 0x6028, 0xa015, 0x0040, 0x1f2a, 0x6114, 0x1078,
+ 0x20e3, 0x0d7e, 0xade8, 0x0007, 0x2091, 0x8000, 0x6800, 0xa00d,
+ 0x0040, 0x1f27, 0xa206, 0x0040, 0x1f18, 0x2168, 0x0078, 0x1f0e,
+ 0x0c7e, 0x2160, 0x6000, 0x6802, 0x1078, 0x1948, 0x0c7f, 0x0d7f,
+ 0x6808, 0x8000, 0x680a, 0x2091, 0x8001, 0x0078, 0x1fc3, 0x2091,
+ 0x8001, 0x0d7f, 0x0078, 0x1fac, 0x6114, 0x1078, 0x20e3, 0x6800,
+ 0xa084, 0x0001, 0x0040, 0x1f9c, 0x2091, 0x8000, 0x6a04, 0x8210,
+ 0x0048, 0x1f3f, 0x6a06, 0x2091, 0x8001, 0x0078, 0x1fc3, 0x2091,
+ 0x8001, 0x0078, 0x1fb0, 0x1078, 0x1b53, 0x00c0, 0x1d66, 0x6114,
+ 0x1078, 0x20e3, 0x60be, 0x60bb, 0x0000, 0x6900, 0xa184, 0x0008,
+ 0x0040, 0x1f56, 0x6020, 0xa085, 0x0100, 0x6022, 0xa184, 0x0001,
+ 0x0040, 0x1fac, 0xa184, 0x0100, 0x00c0, 0x1f98, 0xa184, 0x0200,
+ 0x00c0, 0x1f94, 0x681c, 0xa005, 0x00c0, 0x1fa0, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x000f, 0x00c0, 0x1f6f, 0x1078, 0x20c6, 0x78df,
+ 0x0000, 0x6004, 0x8007, 0xa084, 0x00ff, 0x78d2, 0x8001, 0x609f,
+ 0x0000, 0x0040, 0x1f84, 0x1078, 0x1fc7, 0x0040, 0x1f84, 0x78dc,
+ 0xa085, 0x0100, 0x78de, 0x007c, 0x78d7, 0x0000, 0x78db, 0x0000,
+ 0x6024, 0xa084, 0xff00, 0x6026, 0x1078, 0x39de, 0x0040, 0x1ce3,
+ 0x1078, 0x1b78, 0x0078, 0x1ce3, 0x2009, 0x0017, 0x0078, 0x1fb6,
+ 0x2009, 0x000e, 0x0078, 0x1fb6, 0x2009, 0x0007, 0x0078, 0x1fb6,
+ 0x2009, 0x0035, 0x0078, 0x1fb6, 0x2009, 0x003e, 0x0078, 0x1fb6,
+ 0x2009, 0x0004, 0x0078, 0x1fb6, 0x2009, 0x0006, 0x0078, 0x1fb6,
+ 0x2009, 0x0016, 0x0078, 0x1fb6, 0x2009, 0x0001, 0x6024, 0xa084,
+ 0xff00, 0xa105, 0x6026, 0x2091, 0x8000, 0x1078, 0x1c5f, 0x2091,
+ 0x8001, 0x0078, 0x1ce3, 0x1078, 0x1948, 0x0078, 0x1ce3, 0x78d4,
+ 0xa06d, 0x00c0, 0x1fd2, 0x2c00, 0x78d6, 0x78da, 0x609f, 0x0000,
+ 0x0078, 0x1fde, 0x2c00, 0x689e, 0x609f, 0x0000, 0x78d6, 0x2d00,
+ 0x6002, 0x78d8, 0xad06, 0x00c0, 0x1fde, 0x6002, 0x78d0, 0x8001,
+ 0x78d2, 0x00c0, 0x1fea, 0x78dc, 0xa084, 0xfeff, 0x78de, 0x78d8,
+ 0x2060, 0xa006, 0x007c, 0xa02e, 0x2530, 0x611c, 0x61a2, 0xa184,
+ 0xe1ff, 0x601e, 0xa184, 0x0060, 0x0040, 0x1ffa, 0x0e7e, 0x1078,
+ 0x47c2, 0x0e7f, 0x6596, 0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000,
+ 0x60b3, 0x0000, 0x6714, 0x1078, 0x1973, 0x2091, 0x8000, 0x60a0,
+ 0xa084, 0x8000, 0x00c0, 0x2021, 0x6808, 0xa084, 0x0001, 0x0040,
+ 0x2021, 0x2091, 0x8001, 0x1078, 0x19c0, 0x2091, 0x8000, 0x1078,
+ 0x1c5f, 0x2091, 0x8001, 0x78d7, 0x0000, 0x78db, 0x0000, 0x0078,
+ 0x2093, 0x6024, 0xa096, 0x0001, 0x00c0, 0x2028, 0x8000, 0x6026,
+ 0x6a10, 0x6814, 0x2091, 0x8001, 0xa202, 0x0048, 0x2037, 0x0040,
+ 0x2037, 0x2039, 0x0200, 0x1078, 0x2094, 0x0078, 0x2093, 0x2c08,
+ 0x2091, 0x8000, 0x60a0, 0xa084, 0x8000, 0x0040, 0x2064, 0x6800,
+ 0xa065, 0x0040, 0x2069, 0x6a04, 0x0e7e, 0x2071, 0x5140, 0x7000,
+ 0xa084, 0x0001, 0x0040, 0x205e, 0x7048, 0xa206, 0x00c0, 0x205e,
+ 0x6b04, 0x231c, 0x2160, 0x6302, 0x2300, 0xa005, 0x00c0, 0x2059,
+ 0x6902, 0x2260, 0x6102, 0x0e7f, 0x0078, 0x2070, 0x2160, 0x6202,
+ 0x6906, 0x0e7f, 0x0078, 0x2070, 0x6800, 0xa065, 0x0040, 0x2069,
+ 0x6102, 0x6902, 0x00c0, 0x206d, 0x6906, 0x2160, 0x6003, 0x0000,
+ 0x2160, 0x60a0, 0xa084, 0x8000, 0x0040, 0x207a, 0x6808, 0xa084,
+ 0xfffc, 0x680a, 0x6810, 0x8000, 0x6812, 0x2091, 0x8001, 0x6808,
+ 0xa08c, 0x0040, 0x0040, 0x2089, 0xa086, 0x0040, 0x680a, 0x1078,
+ 0x19d1, 0x2091, 0x8000, 0x1078, 0x21d2, 0x2091, 0x8001, 0x78db,
+ 0x0000, 0x78d7, 0x0000, 0x007c, 0x6008, 0xa705, 0x600a, 0x2091,
+ 0x8000, 0x1078, 0x1c5f, 0x2091, 0x8001, 0x78d8, 0xa065, 0x0040,
+ 0x20a7, 0x609c, 0x78da, 0x609f, 0x0000, 0x0078, 0x2097, 0x78d7,
+ 0x0000, 0x78db, 0x0000, 0x007c, 0x7990, 0x7894, 0x8000, 0xa10a,
+ 0x00c8, 0x20b3, 0xa006, 0x7896, 0x70d2, 0x7804, 0xa005, 0x0040,
+ 0x20c1, 0x8001, 0x7806, 0x00c0, 0x20c1, 0x0068, 0x20c1, 0x2091,
+ 0x4080, 0x007c, 0x2039, 0x20da, 0x0078, 0x20c8, 0x2039, 0x20e0,
+ 0x2704, 0xa005, 0x0040, 0x20d9, 0xac00, 0x2068, 0x6b08, 0x6c0c,
+ 0x6910, 0x6a14, 0x690a, 0x6a0e, 0x6b12, 0x6c16, 0x8738, 0x0078,
+ 0x20c8, 0x007c, 0x0003, 0x0009, 0x000f, 0x0015, 0x001b, 0x0000,
+ 0x0015, 0x001b, 0x0000, 0x0c7e, 0x1078, 0x3b69, 0x2c68, 0x0c7f,
+ 0x007c, 0x0010, 0x215a, 0x0068, 0x215a, 0x2029, 0x0000, 0x78cb,
+ 0x0000, 0x788c, 0xa065, 0x0040, 0x2153, 0x2009, 0x5174, 0x2104,
+ 0xa084, 0x0001, 0x0040, 0x2121, 0x6004, 0xa086, 0x0103, 0x00c0,
+ 0x2121, 0x6018, 0xa005, 0x00c0, 0x2121, 0x6014, 0xa005, 0x00c0,
+ 0x2121, 0x0d7e, 0x2069, 0x0000, 0x6818, 0xa084, 0x0001, 0x00c0,
+ 0x2120, 0x600c, 0x70c6, 0x6010, 0x70ca, 0x70c3, 0x8020, 0x681b,
+ 0x0001, 0x2091, 0x4080, 0x0d7f, 0x1078, 0x1c86, 0x0078, 0x2158,
+ 0x0d7f, 0x1078, 0x215b, 0x0040, 0x2153, 0x6204, 0xa294, 0x00ff,
+ 0xa296, 0x0003, 0x0040, 0x2133, 0x6204, 0xa296, 0x0110, 0x00c0,
+ 0x2141, 0x78cb, 0x0001, 0x6204, 0xa294, 0xff00, 0x8217, 0x8211,
+ 0x0040, 0x2141, 0x85ff, 0x00c0, 0x2153, 0x8210, 0xa202, 0x00c8,
+ 0x2153, 0x057e, 0x1078, 0x216a, 0x057f, 0x0040, 0x214e, 0x78e0,
+ 0xa086, 0x0003, 0x0040, 0x2153, 0x0078, 0x2141, 0x8528, 0x78c8,
+ 0xa005, 0x0040, 0x20f1, 0x85ff, 0x0040, 0x215a, 0x2091, 0x4080,
+ 0x78b0, 0x70d6, 0x007c, 0x7bac, 0x79b0, 0x70d4, 0xa102, 0x00c0,
+ 0x2164, 0x2300, 0xa005, 0x007c, 0x0048, 0x2168, 0xa302, 0x007c,
+ 0x8002, 0x007c, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8,
+ 0x2184, 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0,
+ 0x21b9, 0x7008, 0x7208, 0xa206, 0x00c0, 0x21b9, 0xa286, 0x0008,
+ 0x00c0, 0x21b9, 0x2071, 0x0010, 0x1078, 0x21be, 0x2009, 0x0020,
+ 0x6004, 0xa086, 0x0103, 0x00c0, 0x2193, 0x6028, 0xa005, 0x00c0,
+ 0x2193, 0x2009, 0x000c, 0x1078, 0x1924, 0x0040, 0x21ac, 0x78c4,
+ 0x8000, 0x78c6, 0xa086, 0x0002, 0x00c0, 0x21b9, 0x2091, 0x8000,
+ 0x78e3, 0x0003, 0x78c7, 0x0000, 0x78cc, 0xa085, 0x0300, 0x78ce,
+ 0x2091, 0x8001, 0x0078, 0x21b9, 0x78c7, 0x0000, 0x1078, 0x1c86,
+ 0x79ac, 0x78b0, 0x8000, 0xa10a, 0x00c8, 0x21b7, 0xa006, 0x78b2,
+ 0xa006, 0x2071, 0x0010, 0x2091, 0x8001, 0x007c, 0x8107, 0x8004,
+ 0x8004, 0x7ab8, 0x7bb4, 0x7cc0, 0x7dbc, 0xa210, 0xa399, 0x0000,
+ 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x007c, 0x2009, 0x515b, 0x2091,
+ 0x8000, 0x200a, 0x0f7e, 0x0e7e, 0x2071, 0x5140, 0x7000, 0xa086,
+ 0x0000, 0x00c0, 0x21ec, 0x2009, 0x5112, 0x2104, 0xa005, 0x00c0,
+ 0x21ec, 0x2079, 0x0100, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x21ec,
+ 0x0018, 0x21ec, 0x781b, 0x004b, 0x0e7f, 0x0f7f, 0x007c, 0x0f7e,
+ 0x0e7e, 0x2071, 0x5140, 0x2091, 0x8000, 0x7000, 0xa086, 0x0000,
+ 0x00c0, 0x2205, 0x2079, 0x0100, 0x7830, 0xa084, 0x00c0, 0x00c0,
+ 0x2205, 0x0018, 0x2205, 0x781b, 0x004d, 0x2091, 0x8001, 0x0e7f,
+ 0x0f7f, 0x007c, 0x127e, 0x2091, 0x2300, 0x2071, 0x5140, 0x2079,
+ 0x0100, 0x784b, 0x000f, 0x0098, 0x2218, 0x7838, 0x0078, 0x2211,
+ 0x20a9, 0x0040, 0x7800, 0xa082, 0x0004, 0x0048, 0x2221, 0x20a9,
+ 0x0060, 0x789b, 0x0000, 0x78af, 0x0000, 0x78af, 0x0000, 0x0070,
+ 0x222b, 0x0078, 0x2223, 0x7800, 0xa082, 0x0004, 0x0048, 0x223a,
+ 0x70b7, 0x0096, 0x2019, 0x4ee7, 0x1078, 0x2276, 0x702f, 0x8001,
+ 0x0078, 0x2246, 0x70b7, 0x0000, 0x2019, 0x4d5f, 0x1078, 0x2276,
+ 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f, 0x8000, 0x7003, 0x0000,
+ 0x1078, 0x237f, 0x7004, 0xa084, 0x000f, 0x017e, 0x2009, 0x04fd,
+ 0x210c, 0xa18a, 0x0005, 0x0048, 0x225b, 0x0038, 0x2261, 0xa085,
+ 0x6280, 0x0078, 0x2263, 0x0028, 0x2261, 0xa085, 0x6280, 0x0078,
+ 0x2263, 0xa085, 0x62c0, 0x017f, 0x7806, 0x780f, 0xb204, 0x7843,
+ 0x00d8, 0x7853, 0x0080, 0x780b, 0x0008, 0x7047, 0x0008, 0x7053,
+ 0x517f, 0x704f, 0x0000, 0x127f, 0x2000, 0x007c, 0x137e, 0x147e,
+ 0x157e, 0x047e, 0x20a1, 0x012b, 0x2304, 0xa005, 0x789a, 0x0040,
+ 0x2296, 0x8318, 0x2324, 0x8318, 0x2398, 0x24a8, 0xa484, 0xff00,
+ 0x0040, 0x228e, 0xa482, 0x0100, 0x20a9, 0x0100, 0x2020, 0x53a6,
+ 0xa005, 0x00c0, 0x2285, 0x3318, 0x0078, 0x227c, 0x047f, 0x157f,
+ 0x147f, 0x137f, 0x007c, 0xa18c, 0x000f, 0x2011, 0x0101, 0x2204,
+ 0xa084, 0xfff0, 0xa105, 0x2012, 0x1078, 0x237f, 0x007c, 0x2011,
+ 0x0101, 0x20a9, 0x0009, 0x810b, 0x0070, 0x22b0, 0x0078, 0x22ab,
+ 0xa18c, 0x0e00, 0x2204, 0xa084, 0xf1ff, 0xa105, 0x2012, 0x007c,
+ 0x2009, 0x0101, 0x20a9, 0x0005, 0x8213, 0x0070, 0x22c1, 0x0078,
+ 0x22bc, 0xa294, 0x00e0, 0x2104, 0xa084, 0xff1f, 0xa205, 0x200a,
+ 0x007c, 0x2011, 0x0101, 0x20a9, 0x000c, 0x810b, 0x0070, 0x22d2,
+ 0x0078, 0x22cd, 0xa18c, 0xf000, 0x2204, 0xa084, 0x0fff, 0xa105,
+ 0x2012, 0x007c, 0x2011, 0x0102, 0x2204, 0xa084, 0xffcf, 0xa105,
+ 0x2012, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e, 0x2061,
+ 0x0100, 0x609a, 0x62ac, 0x63ac, 0x0c7f, 0x007c, 0x8103, 0x8003,
+ 0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa084,
+ 0xffdf, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0022,
+ 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa085, 0x0020, 0x60ae,
+ 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e, 0x2061,
+ 0x0100, 0x609a, 0x60a4, 0x62ae, 0x2010, 0x60a4, 0x63ae, 0x2018,
+ 0x0c7f, 0x007c, 0x2091, 0x8000, 0x0c7e, 0x0e7e, 0x6818, 0xa005,
+ 0x0040, 0x235d, 0x2061, 0x7500, 0x1078, 0x2365, 0x0040, 0x2349,
+ 0x20a9, 0x0000, 0x2061, 0x7400, 0x0c7e, 0x1078, 0x2365, 0x0040,
+ 0x2339, 0x0c7f, 0x8c60, 0x0070, 0x2337, 0x0078, 0x232c, 0x0078,
+ 0x235d, 0x007f, 0xa082, 0x7400, 0x2071, 0x5140, 0x7086, 0x7182,
+ 0x2001, 0x0004, 0x706e, 0x7093, 0x000f, 0x1078, 0x21cd, 0x0078,
+ 0x2359, 0x60c0, 0xa005, 0x00c0, 0x235d, 0x2071, 0x5140, 0x7182,
+ 0x2c00, 0x708a, 0x2001, 0x0006, 0x706e, 0x7093, 0x000f, 0x1078,
+ 0x21cd, 0x2001, 0x0000, 0x0078, 0x235f, 0x2001, 0x0001, 0x2091,
+ 0x8001, 0xa005, 0x0e7f, 0x0c7f, 0x007c, 0x2c04, 0xa005, 0x0040,
+ 0x237c, 0x2060, 0x600c, 0xa306, 0x00c0, 0x2379, 0x6010, 0xa206,
+ 0x00c0, 0x2379, 0x6014, 0xa106, 0x00c0, 0x2379, 0xa006, 0x0078,
+ 0x237e, 0x6000, 0x0078, 0x2366, 0xa085, 0x0001, 0x007c, 0x2011,
+ 0x5141, 0x220c, 0xa18c, 0x000f, 0x2011, 0x013b, 0x2204, 0xa084,
+ 0x0100, 0x0040, 0x2395, 0x2021, 0xff04, 0x2122, 0x810b, 0x810b,
+ 0x810b, 0x810b, 0xa18d, 0x0f00, 0x2104, 0x007c, 0x0e7e, 0x68e4,
+ 0xa08c, 0x0020, 0x0040, 0x23e9, 0xa084, 0x0006, 0x00c0, 0x23e9,
+ 0x6014, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0f0,
+ 0x5380, 0x7004, 0xa084, 0x000a, 0x00c0, 0x23e9, 0x7108, 0xa194,
+ 0xff00, 0x0040, 0x23e9, 0xa18c, 0x00ff, 0x2001, 0x000c, 0xa106,
+ 0x0040, 0x23d0, 0x2001, 0x0012, 0xa106, 0x0040, 0x23d4, 0x2001,
+ 0x0014, 0xa106, 0x0040, 0x23d8, 0x2001, 0x0019, 0xa106, 0x0040,
+ 0x23dc, 0x2001, 0x0032, 0xa106, 0x0040, 0x23e0, 0x0078, 0x23e4,
+ 0x2009, 0x0012, 0x0078, 0x23e6, 0x2009, 0x0014, 0x0078, 0x23e6,
+ 0x2009, 0x0019, 0x0078, 0x23e6, 0x2009, 0x0020, 0x0078, 0x23e6,
+ 0x2009, 0x003f, 0x0078, 0x23e6, 0x2011, 0x0000, 0x2100, 0xa205,
+ 0x700a, 0x0e7f, 0x007c, 0x0068, 0x23eb, 0x2091, 0x8000, 0x2071,
+ 0x0000, 0x007e, 0x7018, 0xa084, 0x0001, 0x00c0, 0x23f2, 0x007f,
+ 0x2071, 0x0010, 0x70ca, 0x007f, 0x70c6, 0x70c3, 0x8002, 0x70db,
+ 0x0741, 0x70df, 0x0000, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091,
+ 0x4080, 0x0078, 0x2409, 0x107e, 0x007e, 0x127e, 0x2091, 0x2300,
+ 0x7f3c, 0x7e58, 0x7c30, 0x7d38, 0x77c2, 0x74c6, 0x76ca, 0x75ce,
+ 0xa594, 0x003f, 0xa49c, 0x0003, 0xa484, 0x000f, 0x0079, 0x2420,
+ 0x2432, 0x2432, 0x2432, 0x276c, 0x393b, 0x2430, 0x2461, 0x246b,
+ 0x2430, 0x2430, 0x2430, 0x2430, 0x2430, 0x2430, 0x2430, 0x2430,
+ 0x1078, 0x23eb, 0x8507, 0xa084, 0x001f, 0x0079, 0x2437, 0x2475,
+ 0x276c, 0x2926, 0x2a23, 0x2a4b, 0x2ced, 0x2f98, 0x2fdb, 0x3026,
+ 0x30ab, 0x3163, 0x320c, 0x2461, 0x2848, 0x2f6d, 0x2457, 0x3cc8,
+ 0x3ce8, 0x3eae, 0x3eba, 0x3f8f, 0x2457, 0x2457, 0x4062, 0x4066,
+ 0x3cc6, 0x2457, 0x3e19, 0x2457, 0x3b8c, 0x246b, 0x2457, 0x1078,
+ 0x23eb, 0x0018, 0x2410, 0x127f, 0x2091, 0x8001, 0x007f, 0x107f,
+ 0x007c, 0x2019, 0x4e3b, 0x1078, 0x2276, 0x702f, 0x0001, 0x781b,
+ 0x004f, 0x0078, 0x2459, 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f,
+ 0x8000, 0x781b, 0x00d0, 0x0078, 0x2459, 0x7242, 0x2009, 0x510f,
+ 0x200b, 0x0000, 0xa584, 0x0001, 0x00c0, 0x3ba0, 0x0040, 0x2492,
+ 0x1078, 0x23eb, 0x7003, 0x0000, 0x704b, 0x0000, 0x7043, 0x0000,
+ 0x7037, 0x0000, 0x1078, 0x3912, 0x0018, 0x2410, 0x2009, 0x510f,
+ 0x200b, 0x0000, 0x7068, 0xa005, 0x00c0, 0x255d, 0x706c, 0xa084,
+ 0x0007, 0x0079, 0x249b, 0x2594, 0x24a3, 0x24af, 0x24cc, 0x24ee,
+ 0x253b, 0x2514, 0x24a3, 0x1078, 0x38fa, 0x2009, 0x0048, 0x1078,
+ 0x2e39, 0x00c0, 0x24ad, 0x7003, 0x0004, 0x0078, 0x2459, 0x1078,
+ 0x38fa, 0x00c0, 0x24ca, 0x7080, 0x8007, 0x7882, 0x789b, 0x0010,
+ 0x78ab, 0x000c, 0x789b, 0x0060, 0x78ab, 0x0001, 0x785b, 0x0004,
+ 0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x24ca, 0x7003, 0x0004,
+ 0x7093, 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa, 0x00c0, 0x24ec,
+ 0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f, 0xa18d,
+ 0x00c0, 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002,
+ 0x785b, 0x0004, 0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x24ec,
+ 0x7003, 0x0004, 0x7093, 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa,
+ 0x00c0, 0x2512, 0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c,
+ 0x001f, 0xa18d, 0x00c0, 0x79aa, 0x78ab, 0x0020, 0x7184, 0x79aa,
+ 0x78ab, 0x000d, 0x789b, 0x0060, 0x78ab, 0x0004, 0x785b, 0x0004,
+ 0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x2512, 0x7003, 0x0004,
+ 0x7093, 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa, 0x00c0, 0x2539,
+ 0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f, 0xa18d,
+ 0x00c0, 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002,
+ 0x785b, 0x0004, 0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x2539,
+ 0x7088, 0x708b, 0x0000, 0x2068, 0x704a, 0x7003, 0x0002, 0x7093,
+ 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa, 0x00c0, 0x2459, 0x7088,
+ 0x2068, 0x6f14, 0x1078, 0x37ef, 0x2c50, 0x1078, 0x39ac, 0x789b,
+ 0x0010, 0x6814, 0xa084, 0x001f, 0xa085, 0x0080, 0x78aa, 0x6e1c,
+ 0x2041, 0x0001, 0x708c, 0xa084, 0x0400, 0x2001, 0x0004, 0x0040,
+ 0x255b, 0x2001, 0x0006, 0x0078, 0x267c, 0x1078, 0x38fa, 0x00c0,
+ 0x2459, 0x789b, 0x0010, 0x7068, 0x2068, 0x6f14, 0x1078, 0x37ef,
+ 0x2c50, 0x1078, 0x39ac, 0x6008, 0xa085, 0x0010, 0x600a, 0x6824,
+ 0xa005, 0x0040, 0x257b, 0xa082, 0x0006, 0x0048, 0x2579, 0x0078,
+ 0x257b, 0x6827, 0x0005, 0x6b14, 0xa39c, 0x001f, 0xa39d, 0x00c0,
+ 0x7058, 0xa084, 0x8000, 0x0040, 0x2589, 0xa684, 0x0001, 0x0040,
+ 0x258b, 0xa39c, 0xffbf, 0x7baa, 0x2031, 0x0020, 0x2041, 0x0001,
+ 0x2001, 0x0003, 0x0078, 0x267c, 0x0018, 0x2410, 0x744c, 0xa485,
+ 0x0000, 0x0040, 0x25ae, 0xa080, 0x5180, 0x2030, 0x7150, 0x8108,
+ 0xa12a, 0x0048, 0x25a5, 0x2009, 0x5180, 0x2164, 0x6504, 0x85ff,
+ 0x00c0, 0x25bf, 0x8421, 0x00c0, 0x259f, 0x7152, 0x7003, 0x0000,
+ 0x704b, 0x0000, 0x7040, 0xa005, 0x0040, 0x3ba0, 0x0078, 0x2459,
+ 0x764c, 0xa6b0, 0x5180, 0x7150, 0x2600, 0x0078, 0x25aa, 0x7152,
+ 0x2568, 0x2558, 0x754a, 0x2c50, 0x6034, 0xa085, 0x0000, 0x00c0,
+ 0x25bc, 0x6708, 0x773a, 0xa784, 0x033f, 0x0040, 0x25f5, 0xa784,
+ 0x0021, 0x00c0, 0x25bc, 0xa784, 0x0002, 0x0040, 0x25de, 0xa784,
+ 0x0004, 0x0040, 0x25bc, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0008,
+ 0x00c0, 0x25bc, 0xa784, 0x0010, 0x00c0, 0x25bc, 0xa784, 0x0200,
+ 0x00c0, 0x25bc, 0xa784, 0x0100, 0x0040, 0x25f5, 0x6018, 0xa005,
+ 0x00c0, 0x25bc, 0xa7bc, 0xfeff, 0x670a, 0x6823, 0x0000, 0x6e1c,
+ 0xa684, 0x000e, 0x6118, 0x0040, 0x2605, 0x601c, 0xa102, 0x0048,
+ 0x2608, 0x0040, 0x2608, 0x0078, 0x25b8, 0x81ff, 0x00c0, 0x25b8,
+ 0x68c3, 0x0000, 0xa784, 0x0080, 0x00c0, 0x2610, 0x700c, 0x6022,
+ 0xa7bc, 0xff7f, 0x670a, 0x1078, 0x39ac, 0x0018, 0x2410, 0x789b,
+ 0x0010, 0xa046, 0x1078, 0x38fa, 0x00c0, 0x2459, 0x6b14, 0xa39c,
+ 0x001f, 0xa39d, 0x00c0, 0x7058, 0xa084, 0x8000, 0x0040, 0x262c,
+ 0xa684, 0x0001, 0x0040, 0x262e, 0xa39c, 0xffbf, 0xa684, 0x0010,
+ 0x0040, 0x2634, 0xa39d, 0x0020, 0x7baa, 0x8840, 0xa684, 0x000e,
+ 0x00c0, 0x263f, 0xa7bd, 0x0010, 0x670a, 0x0078, 0x267a, 0x7158,
+ 0xa18c, 0x0800, 0x0040, 0x3401, 0x2011, 0x0020, 0xa684, 0x0008,
+ 0x00c0, 0x2650, 0x8210, 0xa684, 0x0002, 0x00c0, 0x2650, 0x8210,
+ 0x7aaa, 0x8840, 0x1078, 0x3912, 0x6a14, 0x610c, 0x8108, 0xa18c,
+ 0x00ff, 0xa1e0, 0x7400, 0x2c64, 0x8cff, 0x0040, 0x2671, 0x6014,
+ 0xa206, 0x00c0, 0x265b, 0x60b8, 0x8001, 0x60ba, 0x00c0, 0x2656,
+ 0x0c7e, 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078,
+ 0x2594, 0x1078, 0x38fa, 0x00c0, 0x2459, 0x2a60, 0x610e, 0x79aa,
+ 0x8840, 0x7132, 0x2001, 0x0001, 0x007e, 0x715c, 0xa184, 0x0018,
+ 0x0040, 0x2697, 0xa184, 0x0010, 0x0040, 0x268a, 0x1078, 0x3604,
+ 0x00c0, 0x26ba, 0xa184, 0x0008, 0x0040, 0x2697, 0x69a0, 0xa184,
+ 0x0600, 0x00c0, 0x2697, 0x1078, 0x34f1, 0x0078, 0x26ba, 0x69a0,
+ 0xa184, 0x0800, 0x0040, 0x26ae, 0x0c7e, 0x027e, 0x2960, 0x6000,
+ 0xa085, 0x2000, 0x6002, 0x6104, 0xa18d, 0x0010, 0x6106, 0x027f,
+ 0x0c7f, 0x1078, 0x3604, 0x00c0, 0x26ba, 0x69a0, 0xa184, 0x0200,
+ 0x0040, 0x26b6, 0x1078, 0x3540, 0x0078, 0x26ba, 0xa184, 0x0400,
+ 0x00c0, 0x2693, 0x69a0, 0xa184, 0x1000, 0x0040, 0x26c5, 0x6914,
+ 0xa18c, 0xff00, 0x810f, 0x1078, 0x22ee, 0x007f, 0x7002, 0xa68c,
+ 0x00e0, 0xa684, 0x0060, 0x0040, 0x26d3, 0xa086, 0x0060, 0x00c0,
+ 0x26d3, 0xa18d, 0x4000, 0x88ff, 0x0040, 0x26d8, 0xa18d, 0x0004,
+ 0x795a, 0x69b6, 0x789b, 0x0060, 0x2800, 0x78aa, 0x789b, 0x0061,
+ 0x6818, 0xa08d, 0x8000, 0xa084, 0x7fff, 0x691a, 0xa68c, 0x0080,
+ 0x0040, 0x26f7, 0x7097, 0x0000, 0xa08a, 0x000d, 0x0050, 0x26f5,
+ 0xa08a, 0x000c, 0x7196, 0x2001, 0x000c, 0x800c, 0x719a, 0x78aa,
+ 0x8008, 0x810c, 0x0040, 0x3407, 0xa18c, 0x00f8, 0x00c0, 0x3407,
+ 0x157e, 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000,
+ 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f,
+ 0x6814, 0x8007, 0x7882, 0x6d94, 0x7dd6, 0x7dde, 0x6e98, 0x7ed2,
+ 0x7eda, 0x1078, 0x38fa, 0x00c0, 0x272e, 0x702c, 0x8003, 0x0048,
+ 0x2727, 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f, 0x8000, 0x7830,
+ 0xa084, 0x00c0, 0x00c0, 0x272e, 0x0098, 0x2736, 0x6008, 0xa084,
+ 0xffef, 0x600a, 0x1078, 0x3912, 0x0078, 0x2482, 0x7200, 0xa284,
+ 0x0007, 0xa086, 0x0001, 0x00c0, 0x2743, 0x781b, 0x004f, 0x1078,
+ 0x3912, 0x0078, 0x2754, 0x6ab4, 0xa295, 0x2000, 0x7a5a, 0x781b,
+ 0x004f, 0x1078, 0x3912, 0x7200, 0x2500, 0xa605, 0x0040, 0x2754,
+ 0xa284, 0x0007, 0x1079, 0x2762, 0xad80, 0x0009, 0x7036, 0xa284,
+ 0x0007, 0xa086, 0x0001, 0x00c0, 0x2459, 0x6018, 0x8000, 0x601a,
+ 0x0078, 0x2459, 0x276a, 0x4a3a, 0x4a3a, 0x4a29, 0x4a3a, 0x276a,
+ 0x4a29, 0x276a, 0x1078, 0x23eb, 0x1078, 0x38fa, 0x0f7e, 0x2079,
+ 0x5100, 0x78cc, 0x0f7f, 0xa084, 0x0001, 0x0040, 0x2790, 0x706c,
+ 0xa086, 0x0001, 0x00c0, 0x277f, 0x706e, 0x0078, 0x2823, 0x706c,
+ 0xa086, 0x0005, 0x00c0, 0x278e, 0x7088, 0x2068, 0x681b, 0x0004,
+ 0x6817, 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x706f, 0x0000,
+ 0x2011, 0x0004, 0x716c, 0xa186, 0x0001, 0x0040, 0x27b1, 0xa186,
+ 0x0007, 0x00c0, 0x27a1, 0x2009, 0x5138, 0x200b, 0x0005, 0x0078,
+ 0x27b1, 0x2009, 0x5113, 0x2104, 0x2009, 0x5112, 0x200a, 0x2009,
+ 0x5138, 0x200b, 0x0001, 0x706f, 0x0000, 0x7073, 0x0001, 0x0078,
+ 0x27b3, 0x706f, 0x0000, 0x1078, 0x4776, 0x157e, 0x20a9, 0x0010,
+ 0x2039, 0x0000, 0x1078, 0x36e2, 0xa7b8, 0x0100, 0x0070, 0x27c2,
+ 0x0078, 0x27ba, 0x157f, 0x7000, 0x0079, 0x27c6, 0x27f4, 0x27db,
+ 0x27db, 0x27ce, 0x27f4, 0x27f4, 0x27f4, 0x27f4, 0x2021, 0x515a,
+ 0x2404, 0xa005, 0x0040, 0x27f4, 0xad06, 0x00c0, 0x27db, 0x6800,
+ 0x2022, 0x0078, 0x27eb, 0x6820, 0xa084, 0x0001, 0x00c0, 0x27e7,
+ 0x6f14, 0x1078, 0x37ef, 0x1078, 0x33d8, 0x0078, 0x27eb, 0x7060,
+ 0x2060, 0x6800, 0x6002, 0x6a1a, 0x6817, 0x0000, 0x6820, 0xa085,
+ 0x0008, 0x6822, 0x1078, 0x1c70, 0x2021, 0x7500, 0x1078, 0x2830,
+ 0x2021, 0x515a, 0x1078, 0x2830, 0x157e, 0x20a9, 0x0000, 0x2021,
+ 0x7400, 0x1078, 0x2830, 0x8420, 0x0070, 0x2808, 0x0078, 0x2801,
+ 0x2061, 0x5400, 0x2021, 0x0002, 0x20a9, 0x0100, 0x6018, 0x6110,
+ 0x81ff, 0x0040, 0x2817, 0xa102, 0x0050, 0x2817, 0x6012, 0x601b,
+ 0x0000, 0xace0, 0x0010, 0x0070, 0x281f, 0x0078, 0x280e, 0x8421,
+ 0x00c0, 0x280c, 0x157f, 0x709c, 0xa084, 0x8000, 0x0040, 0x282a,
+ 0x1078, 0x3a00, 0x7003, 0x0000, 0x704b, 0x0000, 0x0078, 0x2459,
+ 0x047e, 0x2404, 0xa005, 0x0040, 0x2844, 0x2068, 0x6800, 0x007e,
+ 0x6a1a, 0x6817, 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x1078,
+ 0x1c70, 0x007f, 0x0078, 0x2832, 0x047f, 0x2023, 0x0000, 0x007c,
+ 0xa282, 0x0003, 0x0050, 0x284e, 0x1078, 0x23eb, 0x2300, 0x0079,
+ 0x2851, 0x2854, 0x28c7, 0x28e4, 0xa282, 0x0002, 0x0040, 0x285a,
+ 0x1078, 0x23eb, 0x706c, 0x706f, 0x0000, 0x7093, 0x0000, 0x0079,
+ 0x2861, 0x2869, 0x2869, 0x286b, 0x289f, 0x340d, 0x2869, 0x289f,
+ 0x2869, 0x1078, 0x23eb, 0x7780, 0x1078, 0x36e2, 0x7780, 0xa7bc,
+ 0x0f00, 0x1078, 0x37ef, 0x6018, 0xa005, 0x0040, 0x2896, 0x2021,
+ 0x7500, 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x28ff, 0x0040,
+ 0x2896, 0x157e, 0x20a9, 0x0000, 0x2021, 0x7400, 0x047e, 0x2009,
+ 0x0004, 0x2011, 0x0010, 0x1078, 0x28ff, 0x047f, 0x0040, 0x2895,
+ 0x8420, 0x0070, 0x2895, 0x0078, 0x2886, 0x157f, 0x8738, 0xa784,
+ 0x001f, 0x00c0, 0x2871, 0x0078, 0x2482, 0x0078, 0x2482, 0x7780,
+ 0x1078, 0x37ef, 0x6018, 0xa005, 0x0040, 0x28c5, 0x2021, 0x7500,
+ 0x2009, 0x0005, 0x2011, 0x0020, 0x1078, 0x28ff, 0x0040, 0x28c5,
+ 0x157e, 0x20a9, 0x0000, 0x2021, 0x7400, 0x047e, 0x2009, 0x0005,
+ 0x2011, 0x0020, 0x1078, 0x28ff, 0x047f, 0x0040, 0x28c4, 0x8420,
+ 0x0070, 0x28c4, 0x0078, 0x28b5, 0x157f, 0x0078, 0x2482, 0x2200,
+ 0x0079, 0x28ca, 0x28cd, 0x28cf, 0x28cf, 0x1078, 0x23eb, 0x2009,
+ 0x0012, 0x706c, 0xa086, 0x0002, 0x0040, 0x28d8, 0x2009, 0x000e,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x28de, 0x691a, 0x706f, 0x0000,
+ 0x7073, 0x0001, 0x0078, 0x3888, 0x2200, 0x0079, 0x28e7, 0x28ec,
+ 0x28cf, 0x28ea, 0x1078, 0x23eb, 0x1078, 0x4776, 0x7000, 0xa086,
+ 0x0001, 0x00c0, 0x339d, 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x3390, 0x0040, 0x339d, 0x0078, 0x2594, 0x2404,
+ 0xa005, 0x0040, 0x2922, 0x2068, 0x2d04, 0x007e, 0x6814, 0xa706,
+ 0x0040, 0x290e, 0x2d20, 0x007f, 0x0078, 0x2900, 0x007f, 0x2022,
+ 0x691a, 0x6817, 0x0000, 0x6820, 0xa205, 0x6822, 0x1078, 0x1c70,
+ 0x6010, 0x8001, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078,
+ 0x33ee, 0x007c, 0xa085, 0x0001, 0x0078, 0x2921, 0x2300, 0x0079,
+ 0x2929, 0x292e, 0x292c, 0x29c7, 0x1078, 0x23eb, 0x78ec, 0xa084,
+ 0x0001, 0x00c0, 0x2942, 0x7000, 0xa086, 0x0004, 0x00c0, 0x293a,
+ 0x0078, 0x2965, 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef, 0x600a,
+ 0x0078, 0x339d, 0x78e4, 0xa005, 0x00d0, 0x2965, 0x0018, 0x2459,
+ 0x2008, 0xa084, 0x0030, 0x00c0, 0x2951, 0x781b, 0x004f, 0x0078,
+ 0x2459, 0x78ec, 0xa084, 0x0003, 0x0040, 0x294d, 0x2100, 0xa084,
+ 0x0007, 0x0079, 0x295b, 0x299e, 0x29a9, 0x298f, 0x2963, 0x38ed,
+ 0x38ed, 0x2963, 0x29b8, 0x1078, 0x23eb, 0x7000, 0xa086, 0x0004,
+ 0x00c0, 0x297f, 0x706c, 0xa086, 0x0002, 0x00c0, 0x2975, 0x2011,
+ 0x0002, 0x2019, 0x0000, 0x0078, 0x2848, 0x706c, 0xa086, 0x0006,
+ 0x0040, 0x296f, 0x706c, 0xa086, 0x0004, 0x0040, 0x296f, 0x79e4,
+ 0xa184, 0x0030, 0x0040, 0x2989, 0x78ec, 0xa084, 0x0003, 0x00c0,
+ 0x298b, 0x0078, 0x2f6d, 0x2001, 0x0003, 0x0078, 0x2d01, 0x6818,
+ 0xa084, 0x8000, 0x0040, 0x2996, 0x681b, 0x001d, 0x1078, 0x36c1,
+ 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x29a5, 0x681b, 0x001d, 0x1078, 0x36c1, 0x0078,
+ 0x38b8, 0x6818, 0xa084, 0x8000, 0x0040, 0x29b0, 0x681b, 0x001d,
+ 0x1078, 0x36c1, 0x782b, 0x3008, 0x781b, 0x00cd, 0x0078, 0x2459,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x29bf, 0x681b, 0x001d, 0x1078,
+ 0x36c1, 0x782b, 0x3008, 0x781b, 0x008e, 0x0078, 0x2459, 0xa584,
+ 0x000f, 0x00c0, 0x29e4, 0x7000, 0x0079, 0x29ce, 0x2482, 0x29d8,
+ 0x29d6, 0x339d, 0x339d, 0x339d, 0x339d, 0x29d6, 0x1078, 0x23eb,
+ 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x3390,
+ 0x0040, 0x339d, 0x0078, 0x2594, 0x78e4, 0xa005, 0x00d0, 0x2965,
+ 0x0018, 0x2965, 0x2008, 0xa084, 0x0030, 0x00c0, 0x29f3, 0x781b,
+ 0x004f, 0x0078, 0x2459, 0x78ec, 0xa084, 0x0003, 0x0040, 0x29ef,
+ 0x2100, 0xa184, 0x0007, 0x0079, 0x29fd, 0x2a0f, 0x2a13, 0x2a07,
+ 0x2a05, 0x38ed, 0x38ed, 0x2a05, 0x38e3, 0x1078, 0x23eb, 0x1078,
+ 0x36c9, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x1078,
+ 0x36c9, 0x0078, 0x38b8, 0x1078, 0x36c9, 0x782b, 0x3008, 0x781b,
+ 0x00cd, 0x0078, 0x2459, 0x1078, 0x36c9, 0x782b, 0x3008, 0x781b,
+ 0x008e, 0x0078, 0x2459, 0x2300, 0x0079, 0x2a26, 0x2a2b, 0x2a29,
+ 0x2a2d, 0x1078, 0x23eb, 0x0078, 0x30ab, 0x681b, 0x0008, 0x78a3,
+ 0x0000, 0x79e4, 0xa184, 0x0030, 0x0040, 0x30ab, 0x78ec, 0xa084,
+ 0x0003, 0x0040, 0x30ab, 0xa184, 0x0007, 0x0079, 0x2a3f, 0x2a47,
+ 0x2a13, 0x298f, 0x3888, 0x38ed, 0x38ed, 0x2a47, 0x38e3, 0x1078,
+ 0x389c, 0x0078, 0x2459, 0xa282, 0x0005, 0x0050, 0x2a51, 0x1078,
+ 0x23eb, 0x2300, 0x0079, 0x2a54, 0x2a57, 0x2cae, 0x2cbc, 0x2200,
+ 0x0079, 0x2a5a, 0x2a74, 0x2a61, 0x2a74, 0x2a5f, 0x2c93, 0x1078,
+ 0x23eb, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa082, 0x0020,
+ 0x0048, 0x369d, 0xa08a, 0x0004, 0x00c8, 0x369d, 0x0079, 0x2a70,
+ 0x369d, 0x369d, 0x369d, 0x364b, 0x789b, 0x0018, 0x79a8, 0xa184,
+ 0x0080, 0x0040, 0x2a85, 0x0078, 0x369d, 0x7000, 0xa005, 0x00c0,
+ 0x2a7b, 0x2011, 0x0004, 0x0078, 0x321f, 0xa184, 0x00ff, 0xa08a,
+ 0x0010, 0x00c8, 0x369d, 0x0079, 0x2a8d, 0x2a9f, 0x2a9d, 0x2ab7,
+ 0x2abb, 0x2b78, 0x369d, 0x369d, 0x2b7a, 0x369d, 0x369d, 0x2c8f,
+ 0x2c8f, 0x369d, 0x369d, 0x369d, 0x2c91, 0x1078, 0x23eb, 0xa684,
+ 0x1000, 0x0040, 0x2aac, 0x2001, 0x0500, 0x8000, 0x8000, 0x783a,
+ 0x781b, 0x008c, 0x0078, 0x2459, 0x6818, 0xa084, 0x8000, 0x0040,
+ 0x2ab5, 0x681b, 0x001d, 0x0078, 0x2aa3, 0x0078, 0x3888, 0x681b,
+ 0x001d, 0x0078, 0x36ad, 0x6920, 0x6922, 0xa684, 0x1800, 0x00c0,
+ 0x2afc, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2b04, 0x6818, 0xa086,
+ 0x0008, 0x00c0, 0x2acd, 0x681b, 0x0000, 0xa684, 0x0400, 0x0040,
+ 0x2b74, 0xa684, 0x0080, 0x0040, 0x2af8, 0x7097, 0x0000, 0x6818,
+ 0xa084, 0x003f, 0xa08a, 0x000d, 0x0050, 0x2af8, 0xa08a, 0x000c,
+ 0x7196, 0x2001, 0x000c, 0x800c, 0x719a, 0x789b, 0x0061, 0x78aa,
+ 0x157e, 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000,
+ 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f,
+ 0x781b, 0x0058, 0x0078, 0x2459, 0xa684, 0x1000, 0x0040, 0x2b04,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0xa684, 0x0060, 0x0040, 0x2b70,
+ 0xa684, 0x0800, 0x0040, 0x2b70, 0xa684, 0x8000, 0x00c0, 0x2b12,
+ 0x0078, 0x2b2c, 0xa6b4, 0x7fff, 0x7e5a, 0x6eb6, 0x789b, 0x0076,
+ 0x7aac, 0x79ac, 0x78ac, 0x801b, 0x00c8, 0x2b1f, 0x8000, 0xa084,
+ 0x003f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100, 0xa302, 0x68b2,
+ 0x6b94, 0x2200, 0xa303, 0x68ae, 0xa684, 0x4000, 0x0040, 0x2b34,
+ 0xa6b4, 0xbfff, 0x7e5a, 0x6eb6, 0x7000, 0xa086, 0x0003, 0x00c0,
+ 0x2b41, 0x1078, 0x482c, 0x1078, 0x4a29, 0x781b, 0x0064, 0x0078,
+ 0x2459, 0xa006, 0x1078, 0x4b30, 0x6ab0, 0x69ac, 0x6c98, 0x6b94,
+ 0x2200, 0xa105, 0x0040, 0x2b50, 0x2200, 0xa422, 0x2100, 0xa31b,
+ 0x6caa, 0x7cd2, 0x7cda, 0x6ba6, 0x7bd6, 0x7bde, 0x2300, 0xa405,
+ 0x00c0, 0x2b62, 0xa6b5, 0x4000, 0x7e5a, 0x6eb6, 0x781b, 0x0064,
+ 0x0078, 0x2459, 0x781b, 0x0064, 0x2200, 0xa115, 0x00c0, 0x2b6c,
+ 0x1078, 0x4a3a, 0x0078, 0x2459, 0x1078, 0x4a85, 0x0078, 0x2459,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0x781b, 0x0058, 0x0078, 0x2459,
+ 0x1078, 0x23eb, 0x0078, 0x2bdb, 0x6920, 0xa184, 0x0100, 0x0040,
+ 0x2b92, 0xa18c, 0xfeff, 0x6922, 0x0c7e, 0x7054, 0x2060, 0x6000,
+ 0xa084, 0xefff, 0x6002, 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f,
+ 0x0078, 0x2bca, 0xa184, 0x0200, 0x0040, 0x2bca, 0xa18c, 0xfdff,
+ 0x6922, 0x0c7e, 0x7054, 0x2060, 0x6000, 0xa084, 0xdfff, 0x6002,
+ 0x6004, 0xa084, 0xffef, 0x6006, 0x2008, 0x2c48, 0x0c7f, 0xa184,
+ 0x0008, 0x0040, 0x2bca, 0x1078, 0x37eb, 0x1078, 0x34f1, 0x88ff,
+ 0x0040, 0x2bca, 0x789b, 0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5,
+ 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2bc4, 0x782b, 0x3008,
+ 0x781b, 0x0056, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b, 0x0065,
+ 0x0078, 0x2459, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x2bd3, 0x781b,
+ 0x0058, 0x0078, 0x2459, 0x781b, 0x0065, 0x0078, 0x2459, 0x0078,
+ 0x36a5, 0x0078, 0x36a5, 0x2019, 0x0000, 0x7990, 0xa18c, 0x0007,
+ 0x00c0, 0x2be9, 0x6820, 0xa084, 0x0100, 0x0040, 0x2bd9, 0x2009,
+ 0x0008, 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001,
+ 0x00c0, 0x2c20, 0x2300, 0x7ca8, 0xa400, 0x2018, 0xa102, 0x0040,
+ 0x2c18, 0x0048, 0x2bfd, 0x0078, 0x2c1a, 0xa380, 0x0002, 0xa102,
+ 0x00c8, 0x2c18, 0x6920, 0xa18c, 0xfcff, 0x6922, 0x0c7e, 0x7054,
+ 0x2060, 0x6000, 0xa084, 0xefef, 0x6002, 0x6004, 0xa084, 0xffe5,
+ 0x6006, 0x0c7f, 0x7e58, 0xa6b4, 0xfffb, 0x7e5a, 0x0078, 0x2bcb,
+ 0x0078, 0x2b7c, 0x24a8, 0x7aa8, 0x00f0, 0x2c1a, 0x0078, 0x2beb,
+ 0xa284, 0x00f0, 0xa086, 0x0020, 0x00c0, 0x2c80, 0x8318, 0x8318,
+ 0x2300, 0xa102, 0x0040, 0x2c30, 0x0048, 0x2c30, 0x0078, 0x2c7d,
+ 0xa286, 0x0023, 0x0040, 0x2bd9, 0x681c, 0xa084, 0xfff1, 0x681e,
+ 0x7e58, 0xa684, 0xfff1, 0xa085, 0x0010, 0x2030, 0x7e5a, 0x6008,
+ 0xa085, 0x0010, 0x600a, 0x0c7e, 0x7054, 0x2060, 0x6004, 0x2008,
+ 0x2c48, 0x0c7f, 0xa184, 0x0010, 0x0040, 0x2c54, 0x1078, 0x37eb,
+ 0x1078, 0x3604, 0x0078, 0x2c63, 0x0c7e, 0x7054, 0x2060, 0x6004,
+ 0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2bca, 0x1078,
+ 0x37eb, 0x1078, 0x34f1, 0x88ff, 0x0040, 0x2bca, 0x789b, 0x0060,
+ 0x2800, 0x78aa, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0,
+ 0x2c77, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x782b,
+ 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x7aa8, 0x0078, 0x2beb,
+ 0x8318, 0x2300, 0xa102, 0x0040, 0x2c89, 0x0048, 0x2c89, 0x0078,
+ 0x2beb, 0xa284, 0x0080, 0x00c0, 0x36ad, 0x0078, 0x36a5, 0x0078,
+ 0x36ad, 0x0078, 0x369d, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff,
+ 0xa08e, 0x0001, 0x0040, 0x2c9e, 0x1078, 0x23eb, 0x7aa8, 0xa294,
+ 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004, 0x00c8, 0x369d,
+ 0x0079, 0x2caa, 0x369d, 0x343e, 0x369d, 0x3599, 0xa282, 0x0000,
+ 0x00c0, 0x2cb4, 0x1078, 0x23eb, 0x1078, 0x36c1, 0x782b, 0x3008,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0xa282, 0x0003, 0x00c0, 0x2cc2,
+ 0x1078, 0x23eb, 0xa484, 0x8000, 0x00c0, 0x2ce5, 0x706c, 0xa005,
+ 0x0040, 0x2ccc, 0x1078, 0x23eb, 0x6f14, 0x7782, 0xa7bc, 0x0f00,
+ 0x1078, 0x37ef, 0x6008, 0xa085, 0x0021, 0x600a, 0x8738, 0xa784,
+ 0x001f, 0x00c0, 0x2cd0, 0x1078, 0x36c5, 0x706f, 0x0002, 0x2009,
+ 0x5138, 0x200b, 0x0009, 0x0078, 0x2ce7, 0x1078, 0x36d1, 0x782b,
+ 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0xa282, 0x0004, 0x0050,
+ 0x2cf3, 0x1078, 0x23eb, 0x2300, 0x0079, 0x2cf6, 0x2cf9, 0x2de2,
+ 0x2e15, 0xa286, 0x0003, 0x0040, 0x2cff, 0x1078, 0x23eb, 0x2001,
+ 0x0000, 0x007e, 0x68c0, 0xa005, 0x0040, 0x2d08, 0x7003, 0x0003,
+ 0x68a0, 0xa084, 0x2000, 0x0040, 0x2d11, 0x6008, 0xa085, 0x0002,
+ 0x600a, 0x007f, 0x703e, 0x7000, 0xa084, 0x0007, 0x0079, 0x2d18,
+ 0x2482, 0x2d22, 0x2d22, 0x2f17, 0x2f53, 0x2482, 0x2f53, 0x2d20,
+ 0x1078, 0x23eb, 0xa684, 0x1000, 0x00c0, 0x2d2a, 0x1078, 0x4776,
+ 0x0040, 0x2dbc, 0x7868, 0xa08c, 0x00ff, 0x0040, 0x2d72, 0xa186,
+ 0x0008, 0x00c0, 0x2d41, 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x3390, 0x0040, 0x2d72, 0x1078, 0x4776, 0x0078,
+ 0x2d59, 0xa186, 0x0028, 0x00c0, 0x2d72, 0x1078, 0x4776, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x6018, 0xa005, 0x0040, 0x2d59, 0x8001,
+ 0x601a, 0xa005, 0x0040, 0x2d59, 0x8001, 0xa005, 0x0040, 0x2d59,
+ 0x601e, 0x6820, 0xa084, 0x0001, 0x0040, 0x2482, 0x6820, 0xa084,
+ 0xfffe, 0x6822, 0x7060, 0x0c7e, 0x2060, 0x6800, 0x6002, 0x0c7f,
+ 0x6004, 0x6802, 0xa005, 0x2d00, 0x00c0, 0x2d6f, 0x6002, 0x6006,
+ 0x0078, 0x2482, 0x017e, 0x1078, 0x2e46, 0x017f, 0xa684, 0xdf00,
+ 0x681e, 0x682b, 0x0000, 0x6f14, 0x81ff, 0x0040, 0x2dbc, 0xa186,
+ 0x0002, 0x00c0, 0x2dbc, 0xa684, 0x0800, 0x00c0, 0x2d8f, 0xa684,
+ 0x0060, 0x0040, 0x2d8f, 0x78d8, 0x7adc, 0x682e, 0x6a32, 0x6820,
+ 0xa084, 0x0800, 0x00c0, 0x2dbc, 0x8717, 0xa294, 0x000f, 0x8213,
+ 0x8213, 0x8213, 0xa290, 0x5380, 0xa290, 0x0000, 0x221c, 0xa384,
+ 0x0100, 0x00c0, 0x2da5, 0x0078, 0x2dab, 0x8210, 0x2204, 0xa085,
+ 0x0018, 0x2012, 0x8211, 0xa384, 0x0400, 0x0040, 0x2db8, 0x68a0,
+ 0xa084, 0x0100, 0x00c0, 0x2db8, 0x1078, 0x2eca, 0x0078, 0x2482,
+ 0x6008, 0xa085, 0x0002, 0x600a, 0x6916, 0x6818, 0xa084, 0x8000,
+ 0x0040, 0x2dc4, 0x703c, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x1078,
+ 0x33df, 0x1078, 0x33ee, 0x00c0, 0x2dd1, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2dda, 0x1078, 0x33d8,
+ 0x0078, 0x2dde, 0x7060, 0x2060, 0x6800, 0x6002, 0x1078, 0x1c70,
+ 0x0078, 0x2482, 0xa282, 0x0004, 0x0048, 0x2de8, 0x1078, 0x23eb,
+ 0x2200, 0x0079, 0x2deb, 0x2de6, 0x2def, 0x2dfc, 0x2def, 0x7000,
+ 0xa086, 0x0005, 0x0040, 0x2df8, 0x1078, 0x36c1, 0x782b, 0x3008,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0x7890, 0x8007, 0x8001, 0xa084,
+ 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c, 0x00ff, 0xa186,
+ 0x0003, 0x0040, 0x2e11, 0xa186, 0x0000, 0x0040, 0x2e11, 0x0078,
+ 0x369d, 0x781b, 0x0065, 0x0078, 0x2459, 0x6820, 0xa085, 0x0004,
+ 0x6822, 0x82ff, 0x00c0, 0x2e20, 0x1078, 0x36c1, 0x0078, 0x2e27,
+ 0x8211, 0x0040, 0x2e25, 0x1078, 0x23eb, 0x1078, 0x36d1, 0x782b,
+ 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x702c, 0x8003, 0x0048,
+ 0x2e37, 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f, 0x8000, 0x1078,
+ 0x3912, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2e43, 0x0018, 0x2e43,
+ 0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, 0xa684, 0x0060,
+ 0x00c0, 0x2e50, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2ec9,
+ 0xa684, 0x0800, 0x00c0, 0x2e72, 0x68b4, 0xa084, 0x4800, 0xa635,
+ 0xa684, 0x0800, 0x00c0, 0x2e72, 0x6998, 0x6a94, 0x692e, 0x6a32,
+ 0x703c, 0xa005, 0x00c0, 0x2e6a, 0x2200, 0xa105, 0x0040, 0x2e71,
+ 0x703f, 0x0015, 0x7000, 0xa086, 0x0006, 0x0040, 0x2e71, 0x1078,
+ 0x4776, 0x007c, 0xa684, 0x0020, 0x0040, 0x2e94, 0xa684, 0x4000,
+ 0x0040, 0x2e80, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2e6a,
+ 0x68b4, 0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2e7a,
+ 0x703c, 0xa005, 0x00c0, 0x2e8e, 0x703f, 0x0015, 0x79d8, 0x7adc,
+ 0x692e, 0x6a32, 0x0078, 0x2e6a, 0xa684, 0x4000, 0x0040, 0x2e9e,
+ 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2e6a, 0x68b4, 0xa084,
+ 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2e98, 0x703c, 0xa005,
+ 0x00c0, 0x2eac, 0x703f, 0x0015, 0x79d8, 0x7adc, 0x78d0, 0x80fb,
+ 0x00c8, 0x2eb3, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+ 0x692e, 0x6a32, 0x2100, 0xa205, 0x00c0, 0x2ec0, 0x0078, 0x2e6a,
+ 0x7000, 0xa086, 0x0006, 0x0040, 0x2ec9, 0x1078, 0x4b30, 0x0078,
+ 0x2e6a, 0x007c, 0x6008, 0xa085, 0x0200, 0x600a, 0xa384, 0x0200,
+ 0x0040, 0x2ed6, 0x6008, 0xa085, 0x0002, 0x600a, 0x681b, 0x0006,
+ 0x688f, 0x0000, 0x6893, 0x0000, 0x6a30, 0x692c, 0x6a3e, 0x6942,
+ 0x682f, 0x0003, 0x6833, 0x0000, 0x6837, 0x0020, 0x6897, 0x0000,
+ 0x689b, 0x0020, 0x68b3, 0x0000, 0x68af, 0x0000, 0x7000, 0x0079,
+ 0x2ef1, 0x2482, 0x2efb, 0x2f04, 0x2ef9, 0x2ef9, 0x2ef9, 0x2ef9,
+ 0x2ef9, 0x1078, 0x23eb, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2f04,
+ 0x1078, 0x33d8, 0x0078, 0x2f0a, 0x7060, 0x2c50, 0x2060, 0x6800,
+ 0x6002, 0x2a60, 0x2021, 0x515a, 0x2404, 0xa005, 0x0040, 0x2f13,
+ 0x2020, 0x0078, 0x2f0c, 0x2d22, 0x206b, 0x0000, 0x007c, 0x1078,
+ 0x33df, 0x1078, 0x33ee, 0x6008, 0xa084, 0xfdff, 0x600a, 0x682b,
+ 0x0000, 0x789b, 0x000e, 0x6f14, 0x6817, 0x0002, 0x1078, 0x4b78,
+ 0xa684, 0x0800, 0x0040, 0x2f30, 0x691c, 0xa18d, 0x2000, 0x691e,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x2f40, 0x7868, 0xa08c, 0x00ff,
+ 0x0040, 0x2f3e, 0x681b, 0x001e, 0x0078, 0x2f40, 0x681b, 0x0000,
+ 0x2021, 0x515a, 0x2404, 0xad06, 0x0040, 0x2f47, 0x7460, 0x6800,
+ 0x2022, 0x68c3, 0x0000, 0x6a3c, 0x6940, 0x6a32, 0x692e, 0x1078,
+ 0x1c70, 0x0078, 0x2482, 0x1078, 0x2e46, 0x682b, 0x0000, 0x2001,
+ 0x000e, 0x6f14, 0x1078, 0x3918, 0xa08c, 0x00ff, 0x6916, 0x6818,
+ 0xa084, 0x8000, 0x0040, 0x2f66, 0x703c, 0x681a, 0xa68c, 0xdf00,
+ 0x691e, 0x706f, 0x0000, 0x0078, 0x2482, 0x7000, 0xa005, 0x00c0,
+ 0x2f73, 0x0078, 0x2482, 0xa006, 0x1078, 0x4776, 0x6817, 0x0000,
+ 0x681b, 0x0014, 0xa68c, 0xdf00, 0x691e, 0x682b, 0x0000, 0x6820,
+ 0xa085, 0x00ff, 0x6822, 0x7000, 0x0079, 0x2f86, 0x2482, 0x2f90,
+ 0x2f90, 0x2f92, 0x2f92, 0x2f92, 0x2f92, 0x2f8e, 0x1078, 0x23eb,
+ 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef, 0x600a, 0x0078, 0x33a8,
+ 0x2300, 0x0079, 0x2f9b, 0x2f9e, 0x2fa0, 0x2fd9, 0x1078, 0x23eb,
+ 0x7000, 0x0079, 0x2fa3, 0x2482, 0x2fad, 0x2fad, 0x2fc8, 0x2fad,
+ 0x2fd5, 0x2fc8, 0x2fab, 0x1078, 0x23eb, 0xa684, 0x0060, 0xa086,
+ 0x0060, 0x00c0, 0x2fc4, 0xa6b4, 0xffdf, 0xa6b4, 0xbfff, 0xa6b5,
+ 0x2000, 0x7e5a, 0x681c, 0xa084, 0xffdf, 0x681e, 0x1078, 0x4776,
+ 0x1078, 0x4a3a, 0x0078, 0x3888, 0xa684, 0x2000, 0x0040, 0x2fb7,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x2fd5, 0x681b, 0x0015, 0xa684,
+ 0x4000, 0x0040, 0x2fd5, 0x681b, 0x0007, 0x1078, 0x389c, 0x0078,
+ 0x2459, 0x1078, 0x23eb, 0x2300, 0x0079, 0x2fde, 0x2fe1, 0x2fe3,
+ 0x3016, 0x1078, 0x23eb, 0x7000, 0x0079, 0x2fe6, 0x2482, 0x2ff0,
+ 0x2ff0, 0x300b, 0x2ff0, 0x3012, 0x300b, 0x2fee, 0x1078, 0x23eb,
+ 0xa684, 0x0060, 0xa086, 0x0060, 0x00c0, 0x3007, 0xa6b4, 0xffbf,
+ 0xa6b4, 0xbfff, 0xa6b5, 0x2000, 0x7e5a, 0x681c, 0xa084, 0xffbf,
+ 0x681e, 0x1078, 0x4776, 0x1078, 0x4a3a, 0x0078, 0x3888, 0xa684,
+ 0x2000, 0x0040, 0x2ffa, 0x6818, 0xa084, 0x8000, 0x0040, 0x3012,
+ 0x681b, 0x0007, 0x781b, 0x00cd, 0x0078, 0x2459, 0x6820, 0xa085,
+ 0x0004, 0x6822, 0x1078, 0x3853, 0xa6b5, 0x0800, 0x1078, 0x36c1,
+ 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x2300, 0x0079,
+ 0x3029, 0x302c, 0x302e, 0x3030, 0x1078, 0x23eb, 0x0078, 0x36ad,
+ 0xa684, 0x0400, 0x00c0, 0x3059, 0x79e4, 0xa184, 0x0020, 0x0040,
+ 0x3040, 0x78ec, 0xa084, 0x0003, 0x0040, 0x3040, 0x782b, 0x3009,
+ 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x79e4,
+ 0xa184, 0x0020, 0x0040, 0x3051, 0x78ec, 0xa084, 0x0003, 0x00c0,
+ 0x3055, 0x2001, 0x0014, 0x0078, 0x2d01, 0xa184, 0x0007, 0x0079,
+ 0x3091, 0x7a90, 0xa294, 0x0007, 0x789b, 0x0060, 0x79a8, 0x81ff,
+ 0x0040, 0x308f, 0x789b, 0x0010, 0x7ba8, 0xa384, 0x0001, 0x00c0,
+ 0x3080, 0x7ba8, 0x7ba8, 0xa386, 0x0001, 0x00c0, 0x3073, 0x2009,
+ 0xfff7, 0x0078, 0x3079, 0xa386, 0x0003, 0x00c0, 0x3080, 0x2009,
+ 0xffef, 0x0c7e, 0x7054, 0x2060, 0x6004, 0xa104, 0x6006, 0x0c7f,
+ 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x782b,
+ 0x3009, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x6922, 0x0078,
+ 0x3888, 0x299e, 0x29a9, 0x309b, 0x30a3, 0x3099, 0x3099, 0x3888,
+ 0x3888, 0x1078, 0x23eb, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff,
+ 0x6922, 0x0078, 0x3892, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff,
+ 0x6922, 0x0078, 0x3888, 0x79e4, 0xa184, 0x0030, 0x0040, 0x30b5,
+ 0x78ec, 0xa084, 0x0003, 0x00c0, 0x30dc, 0x7000, 0xa086, 0x0004,
+ 0x00c0, 0x30cf, 0x706c, 0xa086, 0x0002, 0x00c0, 0x30c5, 0x2011,
+ 0x0002, 0x2019, 0x0000, 0x0078, 0x2848, 0x706c, 0xa086, 0x0006,
+ 0x0040, 0x30bf, 0x706c, 0xa086, 0x0004, 0x0040, 0x30bf, 0x7000,
+ 0xa086, 0x0000, 0x0040, 0x2459, 0x6818, 0xa085, 0x8000, 0x681a,
+ 0x2001, 0x0014, 0x0078, 0x2d01, 0xa184, 0x0007, 0x0079, 0x30e0,
+ 0x3888, 0x3888, 0x30e8, 0x3888, 0x38ed, 0x38ed, 0x3888, 0x3888,
+ 0xa684, 0x0080, 0x0040, 0x3117, 0x7194, 0x81ff, 0x0040, 0x3117,
+ 0xa182, 0x000d, 0x00d0, 0x30f8, 0x7097, 0x0000, 0x0078, 0x30fd,
+ 0xa182, 0x000c, 0x7096, 0x2009, 0x000c, 0x789b, 0x0061, 0x79aa,
+ 0x157e, 0x137e, 0x147e, 0x7098, 0x8114, 0xa210, 0x729a, 0xa080,
+ 0x000b, 0xad00, 0x2098, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8108,
+ 0x81ac, 0x53a6, 0x147f, 0x137f, 0x157f, 0x0078, 0x3892, 0xa684,
+ 0x0400, 0x00c0, 0x3158, 0x6820, 0xa084, 0x0001, 0x0040, 0x3892,
+ 0xa68c, 0x0060, 0xa684, 0x0060, 0x0040, 0x312c, 0xa086, 0x0060,
+ 0x00c0, 0x312c, 0xa18d, 0x4000, 0xa18c, 0xfffb, 0x795a, 0x69b6,
+ 0x789b, 0x0060, 0x78ab, 0x0000, 0x789b, 0x0061, 0x6818, 0xa085,
+ 0x8000, 0x681a, 0x78aa, 0x8008, 0x810c, 0x0040, 0x3407, 0xa18c,
+ 0x00f8, 0x00c0, 0x3407, 0x157e, 0x137e, 0x147e, 0x20a1, 0x012b,
+ 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6,
+ 0x147f, 0x137f, 0x157f, 0x6814, 0x8007, 0x7882, 0x0078, 0x3892,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x315f, 0x681b, 0x0008, 0x781b,
+ 0x00c3, 0x0078, 0x2459, 0x2300, 0x0079, 0x3166, 0x316b, 0x320a,
+ 0x3169, 0x1078, 0x23eb, 0x7000, 0xa084, 0x0007, 0x0079, 0x3170,
+ 0x2482, 0x317a, 0x31af, 0x3185, 0x3178, 0x2482, 0x3178, 0x3178,
+ 0x1078, 0x23eb, 0x681c, 0xa084, 0x2000, 0x0040, 0x3193, 0x6008,
+ 0xa085, 0x0002, 0x600a, 0x0078, 0x3193, 0x68c0, 0xa005, 0x00c0,
+ 0x31af, 0x6920, 0xa18d, 0x0001, 0x6922, 0x68c3, 0x0001, 0x6800,
+ 0x706a, 0x0078, 0x31a9, 0x6920, 0xa18d, 0x0001, 0x6922, 0x6800,
+ 0x6006, 0xa005, 0x00c0, 0x319d, 0x6002, 0x681c, 0xa084, 0x000e,
+ 0x0040, 0x31a9, 0x7014, 0x68ba, 0x7130, 0xa188, 0x7400, 0x0078,
+ 0x31ab, 0x2009, 0x7500, 0x2104, 0x6802, 0x2d0a, 0x7162, 0x6eb6,
+ 0xa684, 0x0060, 0x0040, 0x3208, 0xa684, 0x0800, 0x00c0, 0x31c3,
+ 0xa684, 0x7fff, 0x68b6, 0x6894, 0x68a6, 0x6898, 0x68aa, 0x1078,
+ 0x4776, 0x0078, 0x3208, 0xa684, 0x0020, 0x0040, 0x31d8, 0x68c0,
+ 0xa005, 0x0040, 0x31cf, 0x1078, 0x4b78, 0x0078, 0x31d2, 0xa006,
+ 0x1078, 0x4b30, 0x79d8, 0x7adc, 0x69aa, 0x6aa6, 0x0078, 0x31de,
+ 0x1078, 0x37fc, 0x69aa, 0x6aa6, 0x1078, 0x4b30, 0xa684, 0x8000,
+ 0x0040, 0x3208, 0xa684, 0x7fff, 0x68b6, 0x2001, 0x0076, 0x1078,
+ 0x3918, 0x2010, 0x2001, 0x0078, 0x1078, 0x3918, 0x2008, 0xa684,
+ 0x0020, 0x00c0, 0x3200, 0x2001, 0x007a, 0x1078, 0x3918, 0x801b,
+ 0x00c8, 0x31fb, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+ 0x6b98, 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae,
+ 0x0078, 0x2482, 0x0078, 0x36ad, 0x7037, 0x0000, 0xa282, 0x0006,
+ 0x0050, 0x3214, 0x1078, 0x23eb, 0x7000, 0xa084, 0x0007, 0x10c0,
+ 0x39be, 0x2300, 0x0079, 0x321c, 0x321f, 0x3248, 0x325c, 0x2200,
+ 0x0079, 0x3222, 0x3246, 0x36ad, 0x3228, 0x3246, 0x3278, 0x32ba,
+ 0x7003, 0x0005, 0x2001, 0x7610, 0x2068, 0x704a, 0x157e, 0x20a9,
+ 0x0031, 0x2003, 0x0000, 0x8000, 0x0070, 0x3238, 0x0078, 0x3231,
+ 0x157f, 0xad80, 0x0009, 0x7036, 0x6817, 0x0000, 0x68b7, 0x0700,
+ 0x6823, 0x0800, 0x6827, 0x0003, 0x0078, 0x369d, 0x1078, 0x23eb,
+ 0x7003, 0x0005, 0x2001, 0x7610, 0x2068, 0x704a, 0xad80, 0x0009,
+ 0x7036, 0x2200, 0x0079, 0x3254, 0x36ad, 0x325a, 0x325a, 0x3278,
+ 0x325a, 0x36ad, 0x1078, 0x23eb, 0x7003, 0x0005, 0x2001, 0x7610,
+ 0x2068, 0x704a, 0xad80, 0x0009, 0x7036, 0x2200, 0x0079, 0x3268,
+ 0x3270, 0x326e, 0x326e, 0x3270, 0x326e, 0x3270, 0x1078, 0x23eb,
+ 0x1078, 0x36d1, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2459,
+ 0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8,
+ 0xa484, 0x001f, 0xa215, 0x2069, 0x7500, 0x2d04, 0x2d08, 0x7162,
+ 0x2068, 0xa005, 0x0040, 0x3293, 0x6814, 0xa206, 0x0040, 0x32af,
+ 0x6800, 0x0078, 0x3286, 0x7003, 0x0005, 0x2001, 0x7610, 0x2068,
+ 0x704a, 0x7036, 0x157e, 0x20a9, 0x0031, 0x2003, 0x0000, 0x8000,
+ 0x0070, 0x32a4, 0x0078, 0x329d, 0x157f, 0xad80, 0x0009, 0x7036,
+ 0x6a16, 0x68b7, 0x0700, 0x6823, 0x0800, 0x6827, 0x0003, 0x6eb4,
+ 0x7e5a, 0x6820, 0xa084, 0x0c00, 0x0040, 0x3309, 0x1078, 0x36c9,
+ 0x0078, 0x3309, 0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b,
+ 0x0018, 0x7ca8, 0xa484, 0x001f, 0xa215, 0x79a8, 0x79a8, 0xa18c,
+ 0x00ff, 0xa1e8, 0x7400, 0x2d04, 0x2d08, 0x7162, 0x2068, 0xa005,
+ 0x0040, 0x32d9, 0x6814, 0xa206, 0x0040, 0x32f4, 0x6800, 0x0078,
+ 0x32cc, 0x7003, 0x0005, 0x2001, 0x7610, 0x2068, 0x704a, 0x157e,
+ 0x20a9, 0x0031, 0x2003, 0x0000, 0x8000, 0x0070, 0x32e9, 0x0078,
+ 0x32e2, 0x157f, 0xad80, 0x0009, 0x7036, 0x6a16, 0x68b7, 0x0700,
+ 0x6823, 0x0800, 0x6827, 0x0003, 0x6eb4, 0x7e5a, 0x6820, 0xa084,
+ 0x0c00, 0x0040, 0x3309, 0xa084, 0x0800, 0x0040, 0x3303, 0x1078,
+ 0x36cd, 0x0078, 0x3309, 0x1078, 0x36c9, 0x708b, 0x0000, 0x0078,
+ 0x3309, 0x027e, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+ 0xa080, 0x5380, 0x2060, 0x7056, 0x6000, 0x705a, 0x6004, 0x705e,
+ 0xa684, 0x0060, 0x0040, 0x3361, 0x6b98, 0x6c94, 0x69ac, 0x68b0,
+ 0xa105, 0x00c0, 0x3343, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0xa6b4,
+ 0xb7ff, 0x7e5a, 0xa684, 0x0060, 0xa086, 0x0060, 0x0040, 0x3361,
+ 0x68c0, 0xa005, 0x0040, 0x333c, 0x7003, 0x0003, 0x682b, 0x0000,
+ 0x1078, 0x4a29, 0x0078, 0x333e, 0x1078, 0x4a3a, 0xa6b5, 0x2000,
+ 0x7e5a, 0x0078, 0x3361, 0x68b0, 0xa31a, 0x2100, 0xa423, 0x2400,
+ 0xa305, 0x0040, 0x3361, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0x68b0,
+ 0xa6b4, 0xbfff, 0x7e5a, 0x007e, 0x68c0, 0xa005, 0x007f, 0x0040,
+ 0x335f, 0x7003, 0x0003, 0x1078, 0x4a29, 0x0078, 0x3361, 0x1078,
+ 0x4a85, 0x077f, 0x1078, 0x37ef, 0x2009, 0x0065, 0xa684, 0x0004,
+ 0x0040, 0x3382, 0x78e4, 0xa084, 0x0030, 0x0040, 0x337a, 0x78ec,
+ 0xa084, 0x0003, 0x0040, 0x337a, 0x782b, 0x3008, 0x2009, 0x0065,
+ 0x0078, 0x3382, 0x0f7e, 0x2079, 0x5100, 0x1078, 0x4776, 0x0f7f,
+ 0x0040, 0x2482, 0x791a, 0x2d00, 0x704a, 0x8207, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa080, 0x5380, 0x2048, 0x0078, 0x2459,
+ 0x6020, 0xa005, 0x0040, 0x339c, 0x8001, 0x6022, 0x6008, 0xa085,
+ 0x0008, 0x600a, 0x7010, 0x6026, 0x007c, 0xa006, 0x1078, 0x4776,
+ 0x6817, 0x0000, 0x681b, 0x0001, 0x6823, 0x0040, 0x681f, 0x0100,
+ 0x7000, 0xa084, 0x0007, 0x0079, 0x33ad, 0x2482, 0x33b7, 0x33b7,
+ 0x33d4, 0x33bf, 0x33bd, 0x33bf, 0x33b5, 0x1078, 0x23eb, 0x1078,
+ 0x33df, 0x1078, 0x33d8, 0x1078, 0x1c70, 0x0078, 0x2482, 0x706c,
+ 0x706f, 0x0000, 0x7093, 0x0000, 0x0079, 0x33c6, 0x33d0, 0x33d0,
+ 0x33ce, 0x33ce, 0x33ce, 0x33d0, 0x33ce, 0x33d0, 0x0079, 0x2861,
+ 0x706f, 0x0000, 0x0078, 0x2482, 0x681b, 0x0000, 0x0078, 0x2f17,
+ 0x6800, 0xa005, 0x00c0, 0x33dd, 0x6002, 0x6006, 0x007c, 0x6010,
+ 0xa005, 0x0040, 0x33e8, 0x8001, 0x00d0, 0x33e8, 0x1078, 0x23eb,
+ 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x007c, 0x6018, 0xa005,
+ 0x0040, 0x33f4, 0x8001, 0x601a, 0x007c, 0x1078, 0x3912, 0x681b,
+ 0x0018, 0x0078, 0x342b, 0x1078, 0x3912, 0x681b, 0x0019, 0x0078,
+ 0x342b, 0x1078, 0x3912, 0x681b, 0x001a, 0x0078, 0x342b, 0x1078,
+ 0x3912, 0x681b, 0x0003, 0x0078, 0x342b, 0x7780, 0x1078, 0x37ef,
+ 0x7184, 0xa18c, 0x00ff, 0xa1e8, 0x7400, 0x2d04, 0x2d08, 0x2068,
+ 0xa005, 0x00c0, 0x341d, 0x0078, 0x2482, 0x6814, 0x7280, 0xa206,
+ 0x0040, 0x3425, 0x6800, 0x0078, 0x3416, 0x6800, 0x200a, 0x681b,
+ 0x0005, 0x708b, 0x0000, 0x1078, 0x33df, 0x6820, 0xa084, 0x0001,
+ 0x00c0, 0x3434, 0x1078, 0x33d8, 0x1078, 0x33ee, 0x681f, 0x0000,
+ 0x6823, 0x0020, 0x1078, 0x1c70, 0x0078, 0x2482, 0xa282, 0x0003,
+ 0x00c0, 0x369d, 0x7da8, 0xa5ac, 0x00ff, 0x7ca8, 0xa4a4, 0x00ff,
+ 0x6920, 0xa18d, 0x0080, 0x6922, 0xa184, 0x0100, 0x0040, 0x34a2,
+ 0xa18c, 0xfeff, 0x6922, 0xa4a4, 0x00ff, 0x0040, 0x348c, 0xa482,
+ 0x000c, 0x0048, 0x345f, 0x0040, 0x345f, 0x2021, 0x000c, 0x852b,
+ 0x852b, 0x1078, 0x3760, 0x0040, 0x3469, 0x1078, 0x355b, 0x0078,
+ 0x3495, 0x1078, 0x371b, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5,
+ 0x6006, 0x1078, 0x3586, 0x0c7f, 0x6920, 0xa18d, 0x0100, 0x6922,
+ 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x3486,
+ 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x782b, 0x3008,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0x0c7e, 0x2960, 0x6004, 0xa084,
+ 0xfff5, 0x6006, 0x1078, 0x3586, 0x0c7f, 0x7e58, 0xa684, 0x0400,
+ 0x00c0, 0x349e, 0x781b, 0x0058, 0x0078, 0x2459, 0x781b, 0x0065,
+ 0x0078, 0x2459, 0x0c7e, 0x7054, 0x2060, 0x6100, 0xa18c, 0x1000,
+ 0x0040, 0x34e2, 0x6208, 0x8217, 0xa294, 0x00ff, 0xa282, 0x000c,
+ 0x0048, 0x34b6, 0x0040, 0x34b6, 0x2011, 0x000c, 0x2400, 0xa202,
+ 0x00c8, 0x34bb, 0x2220, 0x6208, 0xa294, 0x00ff, 0x7018, 0xa086,
+ 0x0028, 0x00c0, 0x34cb, 0xa282, 0x0019, 0x00c8, 0x34d1, 0x2011,
+ 0x0019, 0x0078, 0x34d1, 0xa282, 0x000c, 0x00c8, 0x34d1, 0x2011,
+ 0x000c, 0x2200, 0xa502, 0x00c8, 0x34d6, 0x2228, 0x1078, 0x371f,
+ 0x852b, 0x852b, 0x1078, 0x3760, 0x0040, 0x34e2, 0x1078, 0x355b,
+ 0x0078, 0x34e6, 0x1078, 0x371b, 0x1078, 0x3586, 0x7858, 0xa085,
+ 0x0004, 0x785a, 0x0c7f, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078,
+ 0x2459, 0x0c7e, 0x2960, 0x6000, 0xa084, 0x1000, 0x00c0, 0x3509,
+ 0x6010, 0xa084, 0x000f, 0x00c0, 0x3503, 0x6104, 0xa18c, 0xfff5,
+ 0x6106, 0x0c7f, 0x007c, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078,
+ 0x3530, 0x68a0, 0xa084, 0x0200, 0x00c0, 0x3503, 0x6208, 0xa294,
+ 0x00ff, 0x7018, 0xa086, 0x0028, 0x00c0, 0x351e, 0xa282, 0x0019,
+ 0x00c8, 0x3524, 0x2011, 0x0019, 0x0078, 0x3524, 0xa282, 0x000c,
+ 0x00c8, 0x3524, 0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff,
+ 0xa382, 0x000c, 0x0048, 0x3530, 0x0040, 0x3530, 0x2019, 0x000c,
+ 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa,
+ 0xa8c0, 0x0005, 0x6820, 0xa085, 0x0100, 0x6822, 0x0c7f, 0x007c,
+ 0x0c7e, 0x2960, 0xa18c, 0xfff5, 0x6106, 0x2011, 0x0032, 0x2019,
+ 0x0000, 0x0078, 0x354b, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab,
+ 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005, 0x6820, 0xa085, 0x0100,
+ 0x6822, 0x0c7f, 0x007c, 0x0c7e, 0x7154, 0x2160, 0x1078, 0x3562,
+ 0x0c7f, 0x007c, 0x2008, 0xa084, 0xfff0, 0xa425, 0x7c86, 0x6018,
+ 0x789a, 0x7cae, 0x6412, 0x78a4, 0xa084, 0xfff8, 0xa18c, 0x0007,
+ 0xa105, 0x78a6, 0x6016, 0x788a, 0xa4a4, 0x000f, 0x8427, 0x8204,
+ 0x8004, 0xa084, 0x00ff, 0xa405, 0x600e, 0x78ec, 0xd08c, 0x00c0,
+ 0x3585, 0x6004, 0xa084, 0xfff5, 0x6006, 0x007c, 0x0c7e, 0x7054,
+ 0x2060, 0x1078, 0x358d, 0x0c7f, 0x007c, 0x6018, 0x789a, 0x78a4,
+ 0xa084, 0xfff0, 0x78a6, 0x6012, 0x7884, 0xa084, 0xfff0, 0x7886,
+ 0x007c, 0xa282, 0x0002, 0x00c0, 0x369d, 0x7aa8, 0x6920, 0xa18d,
+ 0x0080, 0x6922, 0xa184, 0x0200, 0x0040, 0x35e2, 0xa18c, 0xfdff,
+ 0x6922, 0xa294, 0x00ff, 0xa282, 0x0002, 0x00c8, 0x369d, 0x1078,
+ 0x362b, 0x1078, 0x3586, 0xa980, 0x0001, 0x200c, 0x1078, 0x37eb,
+ 0x1078, 0x34f1, 0x88ff, 0x0040, 0x35d5, 0x789b, 0x0060, 0x2800,
+ 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0,
+ 0x35cf, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x782b,
+ 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x7e58, 0xa684, 0x0400,
+ 0x00c0, 0x35de, 0x781b, 0x0058, 0x0078, 0x2459, 0x781b, 0x0065,
+ 0x0078, 0x2459, 0xa282, 0x0002, 0x00c8, 0x35ea, 0xa284, 0x0001,
+ 0x0040, 0x35f4, 0x7154, 0xa188, 0x0000, 0x210c, 0xa18c, 0x2000,
+ 0x00c0, 0x35f4, 0x2011, 0x0000, 0x1078, 0x370d, 0x1078, 0x362b,
+ 0x1078, 0x3586, 0x7858, 0xa085, 0x0004, 0x785a, 0x782b, 0x3008,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0x0c7e, 0x027e, 0x2960, 0x6000,
+ 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, 0x361b, 0x6014, 0xa084,
+ 0x0040, 0x00c0, 0x3619, 0xa18c, 0xffef, 0x6106, 0xa006, 0x0078,
+ 0x3628, 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab,
+ 0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x6820, 0xa085, 0x0200, 0x6822,
+ 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7054, 0x2060, 0x1078, 0x3632,
+ 0x0c7f, 0x007c, 0x82ff, 0x0040, 0x3637, 0x2011, 0x0040, 0x6018,
+ 0xa080, 0x0002, 0x789a, 0x78a4, 0xa084, 0xffbf, 0xa205, 0x78a6,
+ 0x788a, 0x6016, 0x78ec, 0xd08c, 0x00c0, 0x364a, 0x6004, 0xa084,
+ 0xffef, 0x6006, 0x007c, 0x007e, 0x7000, 0xa086, 0x0003, 0x0040,
+ 0x3654, 0x007f, 0x0078, 0x3657, 0x007f, 0x0078, 0x3699, 0xa684,
+ 0x0020, 0x0040, 0x3699, 0x7888, 0xa084, 0x0040, 0x0040, 0x3699,
+ 0x7bb8, 0xa384, 0x003f, 0x831b, 0x00c8, 0x3667, 0x8000, 0xa005,
+ 0x0040, 0x367d, 0x831b, 0x00c8, 0x3670, 0x8001, 0x0040, 0x3695,
+ 0xa684, 0x4000, 0x0040, 0x367d, 0x78b8, 0x801b, 0x00c8, 0x3679,
+ 0x8000, 0xa084, 0x003f, 0x00c0, 0x3695, 0xa6b4, 0xbfff, 0x7e5a,
+ 0x79d8, 0x7adc, 0x2001, 0x0001, 0xa108, 0x00c8, 0x3689, 0xa291,
+ 0x0000, 0x79d2, 0x79da, 0x7ad6, 0x7ade, 0x1078, 0x4b30, 0x781b,
+ 0x0064, 0x1078, 0x49b5, 0x0078, 0x2459, 0x781b, 0x0064, 0x0078,
+ 0x2459, 0x781b, 0x0065, 0x0078, 0x2459, 0x1078, 0x36d5, 0x782b,
+ 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x1078, 0x36c1, 0x782b,
+ 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x6827, 0x0002, 0x1078,
+ 0x36c9, 0x78e4, 0xa084, 0x0030, 0x0040, 0x2482, 0x78ec, 0xa084,
+ 0x0003, 0x0040, 0x2482, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078,
+ 0x2459, 0x2001, 0x0005, 0x0078, 0x36d7, 0x2001, 0x000c, 0x0078,
+ 0x36d7, 0x2001, 0x0006, 0x0078, 0x36d7, 0x2001, 0x000d, 0x0078,
+ 0x36d7, 0x2001, 0x0009, 0x0078, 0x36d7, 0x2001, 0x0007, 0x789b,
+ 0x0010, 0x78aa, 0x789b, 0x0060, 0x78ab, 0x0001, 0xa6b5, 0x0004,
+ 0x7e5a, 0x007c, 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b,
+ 0x8703, 0xa0e0, 0x5380, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184,
+ 0x000f, 0x0040, 0x36fb, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004,
+ 0xa085, 0x0008, 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184,
+ 0x0040, 0x0040, 0x370b, 0xa184, 0xffbf, 0x78a6, 0x6016, 0x6004,
+ 0xa085, 0x0010, 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab,
+ 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060,
+ 0x78ab, 0x0004, 0x007c, 0x2021, 0x0000, 0x2029, 0x0032, 0x789b,
+ 0x0010, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa,
+ 0x7caa, 0x789b, 0x0060, 0x78ab, 0x0005, 0x007c, 0x157e, 0x8007,
+ 0xa084, 0x00ff, 0x8003, 0x8003, 0xa080, 0x0020, 0x789a, 0x79a4,
+ 0xa18c, 0xfff0, 0x2001, 0x5146, 0x2004, 0xa082, 0x0028, 0x0040,
+ 0x3749, 0x2021, 0x37d2, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078,
+ 0x374f, 0x2021, 0x37de, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011,
+ 0x0064, 0x2404, 0xa084, 0xfff0, 0xa106, 0x0040, 0x375e, 0x8420,
+ 0x2300, 0xa210, 0x0070, 0x375e, 0x0078, 0x3751, 0x157f, 0x007c,
+ 0x157e, 0x2009, 0x5146, 0x210c, 0xa182, 0x0032, 0x0048, 0x3774,
+ 0x0040, 0x3778, 0x2009, 0x37c4, 0x2019, 0x0011, 0x20a9, 0x000e,
+ 0x2011, 0x0032, 0x0078, 0x378a, 0xa182, 0x0028, 0x0040, 0x3782,
+ 0x2009, 0x37d2, 0x2019, 0x0014, 0x20a9, 0x000c, 0x2011, 0x0064,
+ 0x0078, 0x378a, 0x2009, 0x37de, 0x2019, 0x0019, 0x20a9, 0x000d,
+ 0x2011, 0x0064, 0x2200, 0xa502, 0x0040, 0x379a, 0x0048, 0x379a,
+ 0x8108, 0x2300, 0xa210, 0x0070, 0x3797, 0x0078, 0x378a, 0x157f,
+ 0xa006, 0x007c, 0x157f, 0xa582, 0x0064, 0x00c8, 0x37a9, 0x7808,
+ 0xa085, 0x0070, 0x780a, 0x7044, 0xa085, 0x0070, 0x7046, 0x0078,
+ 0x37a9, 0x78ec, 0xa084, 0x0300, 0x0040, 0x37b1, 0x2104, 0x0078,
+ 0x37c2, 0x2104, 0xa09e, 0x1102, 0x00c0, 0x37c2, 0x2001, 0x04fd,
+ 0x2004, 0xa082, 0x0005, 0x0048, 0x37c1, 0x2001, 0x1201, 0x0078,
+ 0x37c2, 0x2104, 0xa005, 0x007c, 0x1102, 0x3002, 0x3202, 0x4203,
+ 0x4403, 0x5404, 0x5604, 0x6605, 0x6805, 0x7806, 0x7a06, 0x0c07,
+ 0x0c07, 0x0e07, 0x3202, 0x4202, 0x5202, 0x6202, 0x7202, 0x6605,
+ 0x7605, 0x7805, 0x7a05, 0x7c05, 0x7e05, 0x7f05, 0x2202, 0x3202,
+ 0x4202, 0x5202, 0x5404, 0x6404, 0x7404, 0x7604, 0x7804, 0x7a04,
+ 0x7c04, 0x7e04, 0x7f04, 0x789b, 0x0010, 0xa046, 0x007c, 0xa784,
+ 0x0f00, 0x800b, 0xa784, 0x001f, 0x8003, 0x8003, 0x8003, 0x8003,
+ 0xa105, 0xa0e0, 0x5400, 0x007c, 0x79d8, 0x7adc, 0x78d0, 0x801b,
+ 0x00c8, 0x3803, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+ 0x007c, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x5140, 0x2091, 0x8000,
+ 0x2104, 0x0079, 0x3813, 0x3849, 0x381d, 0x381d, 0x381d, 0x381d,
+ 0x381d, 0x381d, 0x384d, 0x1078, 0x23eb, 0x784b, 0x0004, 0x7848,
+ 0xa084, 0x0004, 0x00c0, 0x381f, 0x784b, 0x0008, 0x7848, 0xa084,
+ 0x0008, 0x00c0, 0x3826, 0x68b4, 0xa085, 0x4000, 0x68b6, 0x7858,
+ 0xa085, 0x4000, 0x785a, 0x7830, 0xa084, 0x0080, 0x00c0, 0x3849,
+ 0x0018, 0x3849, 0x681c, 0xa084, 0x0020, 0x00c0, 0x3847, 0x0e7e,
+ 0x2071, 0x5140, 0x1078, 0x389c, 0x0e7f, 0x0078, 0x3849, 0x781b,
+ 0x00cd, 0x2091, 0x8001, 0x0f7f, 0x007c, 0x70b3, 0x0000, 0x1078,
+ 0x3a76, 0x0078, 0x3849, 0x0c7e, 0x6814, 0x8007, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa0e0, 0x5380, 0x6004, 0xa084, 0x000a,
+ 0x00c0, 0x3886, 0x6108, 0xa194, 0xff00, 0x0040, 0x3886, 0xa18c,
+ 0x00ff, 0x2001, 0x0019, 0xa106, 0x0040, 0x3875, 0x2001, 0x0032,
+ 0xa106, 0x0040, 0x3879, 0x0078, 0x387d, 0x2009, 0x0020, 0x0078,
+ 0x387f, 0x2009, 0x003f, 0x0078, 0x387f, 0x2011, 0x0000, 0x2100,
+ 0xa205, 0x600a, 0x6004, 0xa085, 0x0002, 0x6006, 0x0c7f, 0x007c,
+ 0x781b, 0x0065, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b, 0x0065,
+ 0x0078, 0x2459, 0x781b, 0x0058, 0x0078, 0x2459, 0x782b, 0x3008,
+ 0x781b, 0x0056, 0x0078, 0x2459, 0x2009, 0x5120, 0x210c, 0xa186,
+ 0x0000, 0x0040, 0x38b0, 0xa186, 0x0001, 0x0040, 0x38b3, 0x2009,
+ 0x5138, 0x200b, 0x000b, 0x706f, 0x0001, 0x781b, 0x0048, 0x007c,
+ 0x781b, 0x00c7, 0x007c, 0x2009, 0x5138, 0x200b, 0x000a, 0x007c,
+ 0x2009, 0x5120, 0x210c, 0xa186, 0x0000, 0x0040, 0x38d3, 0xa186,
+ 0x0001, 0x0040, 0x38cd, 0x2009, 0x5138, 0x200b, 0x000b, 0x706f,
+ 0x0001, 0x781b, 0x0048, 0x0078, 0x2459, 0x2009, 0x5138, 0x200b,
+ 0x000a, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b, 0x00c7, 0x0078,
+ 0x2459, 0x781b, 0x00cd, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b,
+ 0x00cd, 0x0078, 0x2459, 0x781b, 0x008e, 0x0078, 0x2459, 0x782b,
+ 0x3008, 0x781b, 0x008e, 0x0078, 0x2459, 0x6818, 0xa084, 0x8000,
+ 0x0040, 0x38f4, 0x681b, 0x001d, 0x706f, 0x0001, 0x781b, 0x0048,
+ 0x0078, 0x2459, 0x007e, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3910,
+ 0x7808, 0xa084, 0xfffc, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005,
+ 0x78ec, 0xa084, 0x0021, 0x0040, 0x3910, 0x7044, 0x780a, 0xa005,
+ 0x007f, 0x007c, 0x7044, 0xa085, 0x0002, 0x7046, 0x780a, 0x007c,
+ 0x007e, 0x7830, 0xa084, 0x0040, 0x00c0, 0x3919, 0x0098, 0x3924,
+ 0x007f, 0x789a, 0x78ac, 0x007c, 0x7808, 0xa084, 0xfffd, 0x780a,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021, 0x0040,
+ 0x3933, 0x0098, 0x3931, 0x007f, 0x789a, 0x78ac, 0x007e, 0x7044,
+ 0x780a, 0x007f, 0x007c, 0x78ec, 0xa084, 0x0002, 0x00c0, 0x4760,
+ 0xa784, 0x007d, 0x00c0, 0x3947, 0x2700, 0x1078, 0x23eb, 0xa784,
+ 0x0001, 0x00c0, 0x2f6d, 0xa784, 0x0070, 0x0040, 0x3957, 0x0c7e,
+ 0x2d60, 0x2f68, 0x1078, 0x2396, 0x2d78, 0x2c68, 0x0c7f, 0xa784,
+ 0x0008, 0x0040, 0x3964, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003,
+ 0x0040, 0x2482, 0x0078, 0x3888, 0xa784, 0x0004, 0x0040, 0x3997,
+ 0x78b8, 0xa084, 0x4001, 0x0040, 0x3997, 0x784b, 0x0008, 0x78ec,
+ 0xa084, 0x0003, 0x0040, 0x2482, 0x78e4, 0xa084, 0x0007, 0xa086,
+ 0x0001, 0x00c0, 0x3997, 0x78c0, 0xa085, 0x4800, 0x2030, 0x7e5a,
+ 0x781b, 0x00cd, 0x0078, 0x2459, 0x784b, 0x0008, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x3993, 0x681b, 0x0015, 0xa684, 0x4000, 0x0040,
+ 0x3993, 0x681b, 0x0007, 0x1078, 0x389c, 0x0078, 0x2459, 0x681b,
+ 0x0003, 0x7858, 0xa084, 0x3f00, 0x681e, 0x682f, 0x0000, 0x6833,
+ 0x0000, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x2965,
+ 0x0018, 0x2459, 0x0078, 0x36a5, 0x6b14, 0x8307, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa080, 0x5380, 0x2060, 0x2048, 0x7056,
+ 0x6000, 0x705a, 0x6004, 0x705e, 0x2a60, 0x007c, 0x0079, 0x39c0,
+ 0x39c8, 0x39c9, 0x39c8, 0x39cb, 0x39c8, 0x39c8, 0x39c8, 0x39d0,
+ 0x007c, 0x1078, 0x33ee, 0x1078, 0x4776, 0x7038, 0x600a, 0x007c,
+ 0x70a0, 0xa005, 0x0040, 0x39dd, 0x2068, 0x1078, 0x1b62, 0x1078,
+ 0x46f8, 0x1078, 0x46ff, 0x70a3, 0x0000, 0x007c, 0x0e7e, 0x2091,
+ 0x8000, 0x2071, 0x5140, 0x7000, 0xa086, 0x0007, 0x00c0, 0x39f4,
+ 0x6110, 0x70bc, 0xa106, 0x00c0, 0x39f4, 0x0e7f, 0x1078, 0x1b6f,
+ 0x1078, 0x39fa, 0xa006, 0x007c, 0x2091, 0x8001, 0x0e7f, 0xa085,
+ 0x0001, 0x007c, 0x0f7e, 0x0e7e, 0x2071, 0x5140, 0x0078, 0x21fa,
+ 0x785b, 0x0000, 0x70af, 0x000e, 0x2009, 0x0100, 0x017e, 0x70a0,
+ 0xa06d, 0x0040, 0x3a0f, 0x70a3, 0x0000, 0x0078, 0x3a15, 0x70b3,
+ 0x0000, 0x1078, 0x1b8b, 0x0040, 0x3a1b, 0x70ac, 0x6826, 0x1078,
+ 0x3af8, 0x0078, 0x3a0f, 0x017f, 0x157e, 0x0c7e, 0x0d7e, 0x20a9,
+ 0x0008, 0x2061, 0x7510, 0x6000, 0xa105, 0x6002, 0x601c, 0xa06d,
+ 0x0040, 0x3a33, 0x6800, 0x601e, 0x1078, 0x195a, 0x6008, 0x8000,
+ 0x600a, 0x0078, 0x3a26, 0x6018, 0xa06d, 0x0040, 0x3a3d, 0x6800,
+ 0x601a, 0x1078, 0x195a, 0x0078, 0x3a33, 0xace0, 0x0008, 0x0070,
+ 0x3a43, 0x0078, 0x3a23, 0x709c, 0xa084, 0x8000, 0x0040, 0x3a4a,
+ 0x1078, 0x3b72, 0x0d7f, 0x0c7f, 0x157f, 0x007c, 0x127e, 0x2091,
+ 0x2300, 0x6804, 0xa084, 0x000f, 0x0079, 0x3a56, 0x3a66, 0x3a66,
+ 0x3a66, 0x3a66, 0x3a66, 0x3a66, 0x3a68, 0x3a6e, 0x3a66, 0x3a66,
+ 0x3a66, 0x3a66, 0x3a66, 0x3a70, 0x3a66, 0x3a68, 0x1078, 0x23eb,
+ 0x1078, 0x44d0, 0x1078, 0x195a, 0x0078, 0x3a74, 0x6827, 0x000b,
+ 0x1078, 0x44d0, 0x1078, 0x3af8, 0x127f, 0x007c, 0x127e, 0x2091,
+ 0x2300, 0x0098, 0x3a92, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3a92,
+ 0x0d7e, 0x1078, 0x4708, 0x2d00, 0x682e, 0x2009, 0x0004, 0x2001,
+ 0x0000, 0x6827, 0x0084, 0x1078, 0x46c1, 0x1078, 0x3af8, 0x0d7f,
+ 0x0078, 0x3ac6, 0x7948, 0xa185, 0x4000, 0x784a, 0x0098, 0x3a9b,
+ 0x794a, 0x0078, 0x3a80, 0x7828, 0xa086, 0x1834, 0x00c0, 0x3aa4,
+ 0xa185, 0x0004, 0x0078, 0x3aab, 0x7828, 0xa086, 0x1814, 0x00c0,
+ 0x3a98, 0xa185, 0x000c, 0x784a, 0x789b, 0x000e, 0x78ab, 0x0002,
+ 0x7858, 0xa084, 0x00ff, 0xa085, 0x0400, 0x785a, 0x70b4, 0xa080,
+ 0x0091, 0x781a, 0x6827, 0x0284, 0x682c, 0x6836, 0x6830, 0x683a,
+ 0x2009, 0x0004, 0x2001, 0x0000, 0x1078, 0x46c1, 0x127f, 0x007c,
+ 0x0d7e, 0x6b14, 0x1078, 0x1bfd, 0x0040, 0x3ad5, 0x2068, 0x6827,
+ 0x0002, 0x1078, 0x3af8, 0x0078, 0x3aca, 0x0d7f, 0x007c, 0x0d7e,
+ 0x6b14, 0x6c28, 0xa4a4, 0x00ff, 0x1078, 0x1b9b, 0x0040, 0x3ae5,
+ 0x2068, 0x6827, 0x0002, 0x1078, 0x3af8, 0x0d7f, 0x007c, 0x0d7e,
+ 0x6b14, 0xa39c, 0x00ff, 0x1078, 0x1bce, 0x0040, 0x3af6, 0x2068,
+ 0x6827, 0x0002, 0x1078, 0x3af8, 0x0078, 0x3aeb, 0x0d7f, 0x007c,
+ 0x0c7e, 0x6914, 0x1078, 0x3b69, 0x6904, 0xa18c, 0x00ff, 0xa186,
+ 0x0006, 0x0040, 0x3b13, 0xa186, 0x000d, 0x0040, 0x3b32, 0xa186,
+ 0x0017, 0x00c0, 0x3b0f, 0x1078, 0x195a, 0x0078, 0x3b11, 0x1078,
+ 0x1c72, 0x0c7f, 0x007c, 0x6004, 0x8001, 0x0048, 0x3b30, 0x6006,
+ 0x2009, 0x0000, 0xa684, 0x0001, 0x00c0, 0x3b20, 0xa18d, 0x8000,
+ 0xa684, 0x0004, 0x0040, 0x3b26, 0xa18d, 0x0002, 0x691e, 0x6823,
+ 0x0000, 0x7104, 0x810f, 0x6818, 0xa105, 0x681a, 0x0078, 0x3b0f,
+ 0x1078, 0x23eb, 0x6018, 0xa005, 0x00c0, 0x3b41, 0x6008, 0x8001,
+ 0x0048, 0x3b41, 0x600a, 0x601c, 0x6802, 0x2d00, 0x601e, 0x0078,
+ 0x3b57, 0xac88, 0x0006, 0x2104, 0xa005, 0x0040, 0x3b4a, 0x2008,
+ 0x0078, 0x3b43, 0x6802, 0x2d0a, 0x6008, 0x8001, 0x0048, 0x3b11,
+ 0x600a, 0x6018, 0x2068, 0x6800, 0x601a, 0x0078, 0x3b3b, 0x157e,
+ 0x137e, 0x147e, 0x0c7e, 0x0d7e, 0x1078, 0x1937, 0x2da0, 0x137f,
+ 0x20a9, 0x0031, 0x53a3, 0x0c7f, 0x147f, 0x137f, 0x157f, 0x0078,
+ 0x3b0f, 0xa184, 0x001f, 0x8003, 0x8003, 0x8003, 0xa080, 0x7510,
+ 0x2060, 0x007c, 0x2019, 0x5151, 0x2304, 0xa085, 0x0001, 0x201a,
+ 0x2019, 0x0102, 0x2304, 0xa085, 0x0001, 0x201a, 0x007c, 0x2019,
+ 0x5151, 0x2304, 0xa084, 0xfffe, 0x201a, 0x2019, 0x0102, 0x2304,
+ 0xa084, 0xfffe, 0x201a, 0x007c, 0x7990, 0xa18c, 0xfff8, 0x7992,
+ 0x70b4, 0xa080, 0x00dd, 0x781a, 0x0078, 0x2459, 0x70a3, 0x0000,
+ 0x7003, 0x0000, 0x7043, 0x0001, 0x7037, 0x0000, 0x0018, 0x2410,
+ 0x1078, 0x1b8b, 0x0040, 0x3bc7, 0x2009, 0x510f, 0x200b, 0x0000,
+ 0x68bc, 0x2060, 0x6100, 0xa184, 0x0300, 0x0040, 0x3bbb, 0x6827,
+ 0x000e, 0xa084, 0x0200, 0x0040, 0x3bb7, 0x6827, 0x0017, 0x1078,
+ 0x3af8, 0x0078, 0x3b96, 0x7000, 0xa086, 0x0007, 0x00c0, 0x3c29,
+ 0x2d00, 0x70a2, 0xad80, 0x000f, 0x7036, 0x0078, 0x3bce, 0x7040,
+ 0xa086, 0x0001, 0x0040, 0x2492, 0x0078, 0x2459, 0x2031, 0x0000,
+ 0x691c, 0xa184, 0x0002, 0x0040, 0x3bd7, 0xa6b5, 0x0004, 0xa184,
+ 0x00c0, 0x8003, 0x8003, 0x8007, 0xa080, 0x3cc2, 0x2004, 0xa635,
+ 0x6820, 0xa084, 0x0400, 0x0040, 0x3bef, 0x789b, 0x0018, 0x78ab,
+ 0x0003, 0x789b, 0x0081, 0x78ab, 0x0001, 0xa6b5, 0x1000, 0x6820,
+ 0xa084, 0x8000, 0x00c0, 0x3bfd, 0x681c, 0xa084, 0x8000, 0x00c0,
+ 0x3c04, 0xa6b5, 0x0800, 0x0078, 0x3c04, 0xa6b5, 0x0400, 0x789b,
+ 0x000e, 0x6824, 0x8007, 0x78aa, 0x6820, 0xa084, 0x0100, 0x0040,
+ 0x3c0b, 0xa6b5, 0x4000, 0xa684, 0x0200, 0x0040, 0x3c25, 0x682c,
+ 0x78d2, 0x6830, 0x78d6, 0xa684, 0x0100, 0x0040, 0x3c23, 0x682c,
+ 0xa084, 0x0001, 0x0040, 0x3c23, 0x7888, 0xa084, 0x0040, 0x0040,
+ 0x3c23, 0xa6b5, 0x8000, 0x1078, 0x46f0, 0x7e5a, 0x6eb6, 0x0078,
+ 0x4727, 0x1078, 0x38fa, 0x00c0, 0x3cbc, 0x702c, 0x8004, 0x0048,
+ 0x3c37, 0x2019, 0x4e3b, 0x1078, 0x2276, 0x702f, 0x0001, 0x2041,
+ 0x0001, 0x2031, 0x1000, 0x789b, 0x0018, 0x6814, 0xa084, 0x001f,
+ 0xa085, 0x0080, 0x78aa, 0x691c, 0xa184, 0x0002, 0x0040, 0x3c50,
+ 0xa6b5, 0x0004, 0x78ab, 0x0020, 0x6828, 0x78aa, 0xa8c0, 0x0002,
+ 0x681c, 0xd0f4, 0x0040, 0x3c59, 0x2c50, 0x1078, 0x39ac, 0x1078,
+ 0x45ff, 0x6820, 0xa084, 0x8000, 0x0040, 0x3c67, 0xa6b5, 0x0400,
+ 0x789b, 0x000e, 0x6824, 0x8007, 0x78aa, 0x0078, 0x3c6e, 0x681c,
+ 0xa084, 0x8000, 0x00c0, 0x3c6e, 0xa6b5, 0x0800, 0x6820, 0xa084,
+ 0x0100, 0x0040, 0x3c75, 0xa6b5, 0x4000, 0x681c, 0xa084, 0x00c0,
+ 0x8003, 0x8003, 0x8007, 0xa080, 0x3cc2, 0x2004, 0xa635, 0xa684,
+ 0x0100, 0x0040, 0x3c8f, 0x682c, 0xa084, 0x0001, 0x0040, 0x3c8f,
+ 0x7888, 0xa084, 0x0040, 0x0040, 0x3c8f, 0xa6b5, 0x8000, 0x789b,
+ 0x007e, 0x7eae, 0x6eb6, 0x6814, 0x8007, 0x78aa, 0x7882, 0x2810,
+ 0x7aaa, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3cbc, 0x0018, 0x3cbc,
+ 0x70b4, 0xa080, 0x00e2, 0x781a, 0x1078, 0x3912, 0xa684, 0x0200,
+ 0x0040, 0x3cb0, 0x682c, 0x78d2, 0x6830, 0x78d6, 0x1078, 0x46f0,
+ 0x2d00, 0x70a2, 0x704a, 0x6810, 0x70be, 0x7003, 0x0007, 0xad80,
+ 0x000f, 0x7036, 0x0078, 0x2459, 0x1078, 0x1b62, 0x1078, 0x3912,
+ 0x0078, 0x2459, 0x0000, 0x0300, 0x0200, 0x0000, 0x1078, 0x23eb,
+ 0x2300, 0x0079, 0x3ccb, 0x3cce, 0x3cce, 0x3cd0, 0x1078, 0x23eb,
+ 0x1078, 0x46ff, 0x6924, 0xa184, 0x00ff, 0xa086, 0x000a, 0x0040,
+ 0x3ce2, 0xa184, 0xff00, 0xa085, 0x000a, 0x6826, 0x1078, 0x1b62,
+ 0x0078, 0x3b96, 0x2001, 0x000a, 0x1078, 0x4691, 0x0078, 0x3b96,
+ 0xa282, 0x0005, 0x0050, 0x3cee, 0x1078, 0x23eb, 0x7000, 0xa084,
+ 0x0007, 0x10c0, 0x39be, 0x1078, 0x1937, 0x00c0, 0x3d0d, 0xa684,
+ 0x0004, 0x0040, 0x3cff, 0x2001, 0x2800, 0x0078, 0x3d01, 0x2001,
+ 0x0800, 0x71b4, 0xa188, 0x0091, 0x789b, 0x000e, 0x78aa, 0x2031,
+ 0x0400, 0x7e5a, 0x791a, 0x0078, 0x2459, 0x6807, 0x0106, 0x680b,
+ 0x0000, 0x689f, 0x0000, 0x6827, 0x0000, 0xa386, 0x0002, 0x00c0,
+ 0x3d2e, 0xa286, 0x0002, 0x00c0, 0x3d2e, 0x78a0, 0xa005, 0x00c0,
+ 0x3d2e, 0xa484, 0x8000, 0x00c0, 0x3d2e, 0x78e4, 0xa084, 0x0008,
+ 0x0040, 0x3d2e, 0xa6b5, 0x0008, 0x2019, 0x0000, 0x1078, 0x411e,
+ 0x2d00, 0x70a2, 0x704a, 0x7003, 0x0007, 0x7037, 0x0000, 0x6824,
+ 0xa084, 0x0080, 0x0040, 0x3d40, 0x1078, 0x41d0, 0x0078, 0x2459,
+ 0x2300, 0x0079, 0x3d43, 0x3d46, 0x3dc7, 0x3de6, 0x2200, 0x0079,
+ 0x3d49, 0x3d4e, 0x3d5e, 0x3d84, 0x3d90, 0x3db3, 0x2029, 0x0001,
+ 0xa026, 0x2011, 0x0000, 0x1078, 0x42f1, 0x0079, 0x3d57, 0x3d5c,
+ 0x2459, 0x3b96, 0x3d5c, 0x3d5c, 0x1078, 0x23eb, 0x7990, 0xa18c,
+ 0x0007, 0x00c0, 0x3d65, 0x2009, 0x0008, 0x2011, 0x0001, 0xa684,
+ 0x0004, 0x0040, 0x3d6d, 0x2011, 0x0003, 0x2220, 0xa12a, 0x2011,
+ 0x0001, 0x1078, 0x42f1, 0x0079, 0x3d75, 0x3d7a, 0x2459, 0x3b96,
+ 0x3d82, 0x3d7c, 0x0078, 0x472d, 0x70ab, 0x3d80, 0x0078, 0x2459,
+ 0x0078, 0x3d7a, 0x1078, 0x23eb, 0xa684, 0x0010, 0x0040, 0x3d8e,
+ 0x1078, 0x419f, 0x0040, 0x3d8e, 0x0078, 0x2459, 0x0078, 0x420c,
+ 0x6000, 0xa084, 0x0002, 0x0040, 0x3dad, 0x70b4, 0xa080, 0x00d2,
+ 0x781a, 0x0d7e, 0x1078, 0x4708, 0x2d00, 0x682e, 0x6827, 0x0000,
+ 0x1078, 0x3af8, 0x0d7f, 0x1078, 0x195a, 0x7003, 0x0000, 0x7037,
+ 0x0000, 0x704b, 0x0000, 0x0078, 0x3b96, 0xa684, 0x0004, 0x00c0,
+ 0x3db3, 0x0078, 0x472d, 0x6000, 0xa084, 0x0004, 0x00c0, 0x3dc5,
+ 0x6000, 0xa084, 0x0001, 0x0040, 0x3dc5, 0x70ab, 0x3dc5, 0x2001,
+ 0x0007, 0x1078, 0x4689, 0x0078, 0x4733, 0x0078, 0x472d, 0x2200,
+ 0x0079, 0x3dca, 0x3dcf, 0x3dcf, 0x3dcf, 0x3dd1, 0x3dcf, 0x1078,
+ 0x23eb, 0x70a7, 0x3dd5, 0x0078, 0x4739, 0x2011, 0x0018, 0x1078,
+ 0x42eb, 0x0079, 0x3ddb, 0x3de0, 0x2459, 0x3b96, 0x3de2, 0x3de4,
+ 0x1078, 0x23eb, 0x1078, 0x23eb, 0x1078, 0x23eb, 0x2200, 0x0079,
+ 0x3de9, 0x3dee, 0x3df0, 0x3df0, 0x3dee, 0x3dee, 0x1078, 0x23eb,
+ 0x78e4, 0xa084, 0x0008, 0x0040, 0x3e05, 0x70a7, 0x3df9, 0x0078,
+ 0x4739, 0x2011, 0x0004, 0x1078, 0x42eb, 0x0079, 0x3dff, 0x3e05,
+ 0x2459, 0x3b96, 0x3e05, 0x3e0f, 0x3e13, 0x70ab, 0x3e0d, 0x2001,
+ 0x0003, 0x1078, 0x4689, 0x0078, 0x4733, 0x0078, 0x472d, 0x70ab,
+ 0x3e05, 0x0078, 0x2459, 0x70ab, 0x3e17, 0x0078, 0x2459, 0x0078,
+ 0x3e0d, 0xa282, 0x0003, 0x0050, 0x3e1f, 0x1078, 0x23eb, 0xa386,
+ 0x0002, 0x00c0, 0x3e38, 0xa286, 0x0002, 0x00c0, 0x3e3e, 0x78a0,
+ 0xa005, 0x00c0, 0x3e3e, 0xa484, 0x8000, 0x00c0, 0x3e3e, 0x78e4,
+ 0xa084, 0x0008, 0x0040, 0x3e38, 0xa6b5, 0x0008, 0x2019, 0x0000,
+ 0xa684, 0x0008, 0x0040, 0x3e3e, 0x1078, 0x417c, 0x6810, 0x70be,
+ 0x7003, 0x0007, 0x2300, 0x0079, 0x3e45, 0x3e48, 0x3e75, 0x3e7d,
+ 0x2200, 0x0079, 0x3e4b, 0x3e50, 0x3e4e, 0x3e69, 0x1078, 0x23eb,
+ 0x7990, 0xa1ac, 0x0007, 0xa026, 0x2011, 0x0001, 0x1078, 0x42f1,
+ 0x0079, 0x3e5a, 0x3e5f, 0x2459, 0x3b96, 0x3e67, 0x3e61, 0x0078,
+ 0x472d, 0x70ab, 0x3e65, 0x0078, 0x2459, 0x0078, 0x3e5f, 0x1078,
+ 0x23eb, 0xa684, 0x0010, 0x0040, 0x3e73, 0x1078, 0x419f, 0x0040,
+ 0x3e73, 0x0078, 0x2459, 0x0078, 0x420c, 0x2200, 0x0079, 0x3e78,
+ 0x3e7b, 0x3e7b, 0x3e7b, 0x1078, 0x23eb, 0x2200, 0x0079, 0x3e80,
+ 0x3e83, 0x3e85, 0x3e85, 0x1078, 0x23eb, 0x78e4, 0xa084, 0x0008,
+ 0x0040, 0x3e9a, 0x70a7, 0x3e8e, 0x0078, 0x4739, 0x2011, 0x0004,
+ 0x1078, 0x42eb, 0x0079, 0x3e94, 0x3e9a, 0x2459, 0x3b96, 0x3e9a,
+ 0x3ea4, 0x3ea8, 0x70ab, 0x3ea2, 0x2001, 0x0003, 0x1078, 0x4689,
+ 0x0078, 0x4733, 0x0078, 0x472d, 0x70ab, 0x3e9a, 0x0078, 0x2459,
+ 0x70ab, 0x3eac, 0x0078, 0x2459, 0x0078, 0x3ea2, 0x2300, 0x0079,
+ 0x3eb1, 0x3eb6, 0x3eb8, 0x3eb4, 0x1078, 0x23eb, 0x70a4, 0x007a,
+ 0x70a4, 0x007a, 0xa282, 0x0002, 0x0050, 0x3ec0, 0x1078, 0x23eb,
+ 0xa684, 0x0200, 0x0040, 0x3eca, 0x1078, 0x46f8, 0x1078, 0x42d3,
+ 0x1078, 0x46ff, 0x2300, 0x0079, 0x3ecd, 0x3ed0, 0x3ef4, 0x3f5a,
+ 0xa286, 0x0001, 0x0040, 0x3ed6, 0x1078, 0x23eb, 0xa684, 0x0200,
+ 0x0040, 0x3ede, 0x1078, 0x46f8, 0x1078, 0x46ff, 0x2001, 0x0001,
+ 0x1078, 0x4691, 0x78b8, 0xa084, 0xc001, 0x0040, 0x3ef0, 0x7848,
+ 0xa085, 0x0008, 0x784a, 0x7848, 0xa084, 0x0008, 0x00c0, 0x3eeb,
+ 0x7003, 0x0000, 0x0078, 0x3b96, 0x2200, 0x0079, 0x3ef7, 0x3ef9,
+ 0x3f2a, 0x70a7, 0x3efd, 0x0078, 0x4739, 0x2011, 0x000d, 0x1078,
+ 0x42eb, 0x0079, 0x3f03, 0x3f0a, 0x2459, 0x3b96, 0x3f12, 0x3f1a,
+ 0x3f20, 0x3f22, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+ 0x0078, 0x4727, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+ 0x0078, 0x4727, 0x70ab, 0x3f1e, 0x0078, 0x2459, 0x0078, 0x3f0a,
+ 0x1078, 0x23eb, 0x70ab, 0x3f26, 0x0078, 0x2459, 0x1078, 0x473f,
+ 0x0078, 0x2459, 0x70a7, 0x3f2e, 0x0078, 0x4739, 0x2011, 0x0012,
+ 0x1078, 0x42eb, 0x0079, 0x3f34, 0x3f3a, 0x2459, 0x3b96, 0x3f46,
+ 0x3f4e, 0x3f54, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+ 0x70b4, 0xa080, 0x00a6, 0x781a, 0x0078, 0x2459, 0xa6b4, 0x00ff,
+ 0xa6b5, 0x0400, 0x6eb6, 0x7e5a, 0x0078, 0x4727, 0x70ab, 0x3f52,
+ 0x0078, 0x2459, 0x0078, 0x3f3a, 0x70ab, 0x3f58, 0x0078, 0x2459,
+ 0x0078, 0x3f46, 0xa286, 0x0001, 0x0040, 0x3f60, 0x1078, 0x23eb,
+ 0x70a7, 0x3f64, 0x0078, 0x4739, 0x2011, 0x0015, 0x1078, 0x42eb,
+ 0x0079, 0x3f6a, 0x3f6f, 0x2459, 0x3b96, 0x3f7d, 0x3f89, 0xa6b4,
+ 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a, 0x783b, 0x1301, 0x70b4,
+ 0xa080, 0x00b4, 0x781a, 0x0078, 0x2459, 0xa6b4, 0x00ff, 0xa6b5,
+ 0x0400, 0x6eb6, 0x7e5a, 0x70b4, 0xa080, 0x00a6, 0x781a, 0x0078,
+ 0x2459, 0x70ab, 0x3f8d, 0x0078, 0x2459, 0x0078, 0x3f6f, 0xa282,
+ 0x0003, 0x0050, 0x3f95, 0x1078, 0x23eb, 0x2300, 0x0079, 0x3f98,
+ 0x3f9b, 0x3fd2, 0x402d, 0xa286, 0x0001, 0x0040, 0x3fa1, 0x1078,
+ 0x23eb, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x3fae,
+ 0x1078, 0x3af8, 0x7003, 0x0000, 0x0078, 0x3b96, 0x683b, 0x0000,
+ 0x6837, 0x0000, 0xa684, 0x0200, 0x0040, 0x3fbc, 0x1078, 0x46f8,
+ 0x1078, 0x42d3, 0x1078, 0x46ff, 0x2001, 0x0001, 0x1078, 0x4691,
+ 0x78b8, 0xa084, 0xc001, 0x0040, 0x3fce, 0x7848, 0xa085, 0x0008,
+ 0x784a, 0x7848, 0xa084, 0x0008, 0x00c0, 0x3fc9, 0x7003, 0x0000,
+ 0x0078, 0x3b96, 0x2200, 0x0079, 0x3fd5, 0x3fd7, 0x4008, 0x70a7,
+ 0x3fdb, 0x0078, 0x4739, 0x2011, 0x000d, 0x1078, 0x42eb, 0x0079,
+ 0x3fe1, 0x3fe8, 0x2459, 0x3b96, 0x3ff0, 0x3ff8, 0x3ffe, 0x4000,
+ 0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727,
+ 0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727,
+ 0x70ab, 0x3ffc, 0x0078, 0x2459, 0x0078, 0x3fe8, 0x1078, 0x23eb,
+ 0x70ab, 0x4004, 0x0078, 0x2459, 0x1078, 0x473f, 0x0078, 0x2459,
+ 0x70a7, 0x400c, 0x0078, 0x4739, 0x2011, 0x0005, 0x1078, 0x42eb,
+ 0x0079, 0x4012, 0x4017, 0x2459, 0x3b96, 0x401f, 0x4027, 0xa6b4,
+ 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727, 0xa6b4,
+ 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727, 0x70ab,
+ 0x402b, 0x0078, 0x2459, 0x0078, 0x4017, 0xa286, 0x0001, 0x0040,
+ 0x4033, 0x1078, 0x23eb, 0x70a7, 0x4037, 0x0078, 0x4739, 0x2011,
+ 0x0006, 0x1078, 0x42eb, 0x0079, 0x403d, 0x4042, 0x2459, 0x3b96,
+ 0x4048, 0x4052, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727,
+ 0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0xa6b5, 0x4000, 0x7e5a,
+ 0x0078, 0x4727, 0x70ab, 0x4056, 0x0078, 0x2459, 0x0078, 0x4042,
+ 0x2300, 0x0079, 0x405b, 0x4060, 0x405e, 0x405e, 0x1078, 0x23eb,
+ 0x1078, 0x23eb, 0x2300, 0x71a8, 0xa005, 0x017a, 0x6810, 0x70be,
+ 0xa282, 0x0003, 0x0050, 0x406e, 0x1078, 0x23eb, 0x2300, 0x0079,
+ 0x4071, 0x4074, 0x4082, 0x40a4, 0xa684, 0x0200, 0x0040, 0x407c,
+ 0x1078, 0x46f8, 0x1078, 0x46ff, 0x2001, 0x0001, 0x1078, 0x4691,
+ 0x0078, 0x2459, 0xa296, 0x0002, 0x0040, 0x408b, 0x82ff, 0x0040,
+ 0x408b, 0x1078, 0x23eb, 0x70a7, 0x408f, 0x0078, 0x4739, 0x2011,
+ 0x0018, 0x1078, 0x42eb, 0x0079, 0x4095, 0x409a, 0x2459, 0x3b96,
+ 0x409c, 0x409e, 0x0078, 0x4727, 0x0078, 0x4727, 0x70ab, 0x40a2,
+ 0x0078, 0x2459, 0x0078, 0x409a, 0x2200, 0x0079, 0x40a7, 0x40a9,
+ 0x40c2, 0x70a7, 0x40ad, 0x0078, 0x4739, 0x2011, 0x0017, 0x1078,
+ 0x42eb, 0x0079, 0x40b3, 0x40b8, 0x2459, 0x3b96, 0x40ba, 0x40bc,
+ 0x0078, 0x4727, 0x0078, 0x4727, 0x70ab, 0x40c0, 0x0078, 0x2459,
+ 0x0078, 0x40b8, 0xa484, 0x8000, 0x00c0, 0x410c, 0xa684, 0x0100,
+ 0x0040, 0x40d6, 0x1078, 0x46f8, 0x1078, 0x42d3, 0x1078, 0x46ff,
+ 0x7848, 0xa085, 0x000c, 0x784a, 0x0078, 0x40da, 0x78d8, 0x78d2,
+ 0x78dc, 0x78d6, 0xa6b4, 0xefff, 0x7e5a, 0x70a7, 0x40e1, 0x0078,
+ 0x4739, 0x2011, 0x000d, 0x1078, 0x42eb, 0x0079, 0x40e7, 0x40ee,
+ 0x2459, 0x3b96, 0x40ee, 0x40fc, 0x4102, 0x4104, 0xa684, 0x0100,
+ 0x0040, 0x40fa, 0x1078, 0x46b6, 0x682c, 0x78d2, 0x6830, 0x78d6,
+ 0x1078, 0x46f0, 0x0078, 0x4727, 0x70ab, 0x4100, 0x0078, 0x2459,
+ 0x0078, 0x40ee, 0x1078, 0x23eb, 0x70ab, 0x4108, 0x0078, 0x2459,
+ 0x1078, 0x473f, 0x0078, 0x2459, 0x1078, 0x46ff, 0x70ab, 0x4116,
+ 0x2001, 0x0003, 0x1078, 0x4689, 0x0078, 0x4733, 0x1078, 0x46f0,
+ 0x682c, 0x78d2, 0x6830, 0x78d6, 0x0078, 0x4727, 0x70b8, 0x6812,
+ 0x70be, 0x8000, 0x70ba, 0x681b, 0x0000, 0xa684, 0x0008, 0x0040,
+ 0x4141, 0x157e, 0x137e, 0x147e, 0x7890, 0x8004, 0x8004, 0x8004,
+ 0x8004, 0xa084, 0x000f, 0x681a, 0x80ac, 0x789b, 0x0000, 0xaf80,
+ 0x002b, 0x2098, 0xad80, 0x000b, 0x20a0, 0x53a5, 0x147f, 0x137f,
+ 0x157f, 0xa6c4, 0x0f00, 0xa684, 0x0002, 0x00c0, 0x4150, 0x692c,
+ 0x810d, 0x810d, 0x810d, 0xa184, 0x0007, 0x2008, 0x0078, 0x415f,
+ 0x789b, 0x0010, 0x79ac, 0xa184, 0x0020, 0x0040, 0x415f, 0x017e,
+ 0x2009, 0x0005, 0x2001, 0x3d00, 0x1078, 0x46c1, 0x017f, 0xa184,
+ 0x001f, 0xa805, 0x6816, 0x1078, 0x3b69, 0x68be, 0xa684, 0x0004,
+ 0x0040, 0x4170, 0xa18c, 0xff00, 0x78a8, 0xa084, 0x00ff, 0xa105,
+ 0x682a, 0xa6b4, 0x00ff, 0x6000, 0xa084, 0x0008, 0x0040, 0x417a,
+ 0xa6b5, 0x4000, 0x6eb6, 0x007c, 0x157e, 0x137e, 0x147e, 0x6918,
+ 0x7890, 0x8004, 0x8004, 0x8004, 0x8004, 0xa084, 0x000f, 0x007e,
+ 0xa100, 0x681a, 0x007f, 0x8000, 0x8004, 0x0040, 0x419b, 0x20a8,
+ 0x8104, 0xa080, 0x000b, 0xad00, 0x20a0, 0x789b, 0x0000, 0xaf80,
+ 0x002b, 0x2098, 0x53a5, 0x147f, 0x137f, 0x157f, 0x007c, 0x682c,
+ 0xa084, 0x0020, 0x00c0, 0x41a7, 0x620c, 0x0078, 0x41a8, 0x6210,
+ 0x6b18, 0x2300, 0xa202, 0x0040, 0x41c8, 0x2018, 0xa382, 0x000e,
+ 0x0048, 0x41b8, 0x0040, 0x41b8, 0x2019, 0x000e, 0x0078, 0x41bc,
+ 0x7858, 0xa084, 0xffef, 0x785a, 0x783b, 0x1b01, 0x7893, 0x0000,
+ 0x7ba2, 0x70b4, 0xa080, 0x008e, 0x781a, 0xa085, 0x0001, 0x007c,
+ 0x7858, 0xa084, 0xffef, 0x785a, 0x7893, 0x0000, 0xa006, 0x007c,
+ 0x6904, 0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x41dd, 0xa196,
+ 0x000f, 0x0040, 0x41dd, 0x6807, 0x0117, 0x6914, 0x1078, 0x3b69,
+ 0x6100, 0x8104, 0x00c8, 0x41f8, 0x601c, 0xa005, 0x0040, 0x41ec,
+ 0x2001, 0x0800, 0x0078, 0x41fa, 0x0d7e, 0x6824, 0x007e, 0x1078,
+ 0x4708, 0x007f, 0x6826, 0x2d00, 0x682e, 0x1078, 0x3af8, 0x0d7f,
+ 0x2001, 0x0200, 0x6826, 0x8007, 0x789b, 0x000e, 0x78aa, 0x6820,
+ 0xa085, 0x8000, 0x6822, 0x2031, 0x0400, 0x6eb6, 0x7e5a, 0x71b4,
+ 0xa188, 0x0091, 0x791a, 0x007c, 0xa6c4, 0x0f00, 0xa684, 0x0002,
+ 0x00c0, 0x4220, 0x692c, 0x810d, 0x810d, 0x810d, 0xa184, 0x0007,
+ 0x2008, 0xa805, 0x6816, 0x1078, 0x3b69, 0x68be, 0x0078, 0x4223,
+ 0x6914, 0x1078, 0x3b69, 0x6100, 0x8104, 0x00c8, 0x4280, 0xa184,
+ 0x0300, 0x0040, 0x422f, 0x6807, 0x0117, 0x0078, 0x424d, 0x6004,
+ 0xa005, 0x00c0, 0x4256, 0x6807, 0x0117, 0x601c, 0xa005, 0x00c0,
+ 0x4243, 0x0d7e, 0x1078, 0x4708, 0x6827, 0x0034, 0x2d00, 0x682e,
+ 0x1078, 0x3af8, 0x0d7f, 0xa684, 0x0004, 0x0040, 0x424d, 0x2031,
+ 0x0400, 0x2001, 0x2800, 0x0078, 0x4251, 0x2031, 0x0400, 0x2001,
+ 0x0800, 0x71b4, 0xa188, 0x0091, 0x0078, 0x42ae, 0x6018, 0xa005,
+ 0x00c0, 0x4243, 0x601c, 0xa005, 0x00c0, 0x4243, 0x689f, 0x0000,
+ 0x6827, 0x003d, 0xa684, 0x0001, 0x0040, 0x42bc, 0xd694, 0x00c0,
+ 0x4279, 0x6100, 0xd1d4, 0x0040, 0x4279, 0x692c, 0x81ff, 0x0040,
+ 0x42bc, 0xa186, 0x0003, 0x0040, 0x42bc, 0xa186, 0x0012, 0x0040,
+ 0x42bc, 0xa6b5, 0x0800, 0x71b4, 0xa188, 0x00af, 0x0078, 0x42b7,
+ 0x6807, 0x0117, 0x2031, 0x0400, 0x692c, 0xa18c, 0x00ff, 0xa186,
+ 0x0012, 0x00c0, 0x4291, 0x2001, 0x42c9, 0x2009, 0x0001, 0x0078,
+ 0x42a2, 0xa186, 0x0003, 0x00c0, 0x429b, 0x2001, 0x42ca, 0x2009,
+ 0x0012, 0x0078, 0x42a2, 0x2001, 0x0200, 0x71b4, 0xa188, 0x0091,
+ 0x0078, 0x42ae, 0x1078, 0x46db, 0x78a3, 0x0000, 0x681c, 0xa085,
+ 0x0040, 0x681e, 0x71b4, 0xa188, 0x00df, 0xa006, 0x6826, 0x8007,
+ 0x789b, 0x000e, 0x78aa, 0x6820, 0xa085, 0x8000, 0x6822, 0x6eb6,
+ 0x7e5a, 0x791a, 0x0078, 0x2459, 0x6eb6, 0x1078, 0x3af8, 0x6810,
+ 0x70be, 0x7003, 0x0007, 0x70a3, 0x0000, 0x704b, 0x0000, 0x0078,
+ 0x2459, 0x0023, 0x0070, 0x0005, 0x0000, 0x0a00, 0x0000, 0x0000,
+ 0x0025, 0x0000, 0x0000, 0x683b, 0x0000, 0x6837, 0x0000, 0xa684,
+ 0x0200, 0x0040, 0x42ea, 0x78b8, 0xa08c, 0x001f, 0xa084, 0x8000,
+ 0x0040, 0x42e3, 0x8108, 0x78d8, 0xa100, 0x6836, 0x78dc, 0xa081,
+ 0x0000, 0x683a, 0x007c, 0x7990, 0x810f, 0xa5ac, 0x0007, 0x2021,
+ 0x0000, 0xa480, 0x0010, 0x789a, 0x79a8, 0xa18c, 0x00ff, 0xa184,
+ 0x0080, 0x00c0, 0x4319, 0xa182, 0x0020, 0x00c8, 0x4337, 0xa182,
+ 0x0012, 0x00c8, 0x467b, 0x2100, 0x1079, 0x4307, 0x007c, 0x467b,
+ 0x44e8, 0x467b, 0x467b, 0x4344, 0x4347, 0x4381, 0x43b7, 0x43eb,
+ 0x43ee, 0x467b, 0x467b, 0x43a2, 0x4412, 0x444c, 0x467b, 0x467b,
+ 0x4473, 0xa184, 0x0020, 0x00c0, 0x44a7, 0xa18c, 0x001f, 0x6814,
+ 0xa084, 0x001f, 0xa106, 0x0040, 0x4334, 0x70b4, 0xa080, 0x00d2,
+ 0x781a, 0x2001, 0x0014, 0x1078, 0x4691, 0x1078, 0x46ff, 0x7003,
+ 0x0000, 0x2001, 0x0002, 0x007c, 0x2001, 0x0000, 0x007c, 0xa182,
+ 0x0024, 0x00c8, 0x467b, 0xa184, 0x0003, 0x1079, 0x4307, 0x007c,
+ 0x467b, 0x467b, 0x467b, 0x467b, 0x1078, 0x467b, 0x007c, 0x2200,
+ 0x0079, 0x434a, 0x4476, 0x4476, 0x436e, 0x436e, 0x436e, 0x436e,
+ 0x436e, 0x436e, 0x436e, 0x436e, 0x436c, 0x436e, 0x4363, 0x436e,
+ 0x436e, 0x436e, 0x436e, 0x436e, 0x4376, 0x4379, 0x4476, 0x4379,
+ 0x436e, 0x436e, 0x436e, 0x0c7e, 0x077e, 0x6f14, 0x1078, 0x36e2,
+ 0x077f, 0x0c7f, 0x0078, 0x436e, 0x1078, 0x458b, 0x6827, 0x02b3,
+ 0x2009, 0x000b, 0x2001, 0x4800, 0x0078, 0x44aa, 0x1078, 0x4670,
+ 0x007c, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4800, 0x0078,
+ 0x4492, 0x2d58, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0,
+ 0x438b, 0x6807, 0x0117, 0x6827, 0x0002, 0x1078, 0x4708, 0x6827,
+ 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3ac8, 0x1078,
+ 0x44d0, 0x2b68, 0x1078, 0x3af8, 0x0d7f, 0x1078, 0x3af8, 0x2001,
+ 0x0002, 0x007c, 0x1078, 0x44d0, 0x2001, 0x0017, 0x1078, 0x4691,
+ 0x70a3, 0x0000, 0x2009, 0x5138, 0x200b, 0x0006, 0x70af, 0x0017,
+ 0x2009, 0x0200, 0x1078, 0x3a06, 0x2001, 0x0001, 0x007c, 0x2200,
+ 0x0079, 0x43ba, 0x4476, 0x44a7, 0x44a7, 0x44a7, 0x43db, 0x44b7,
+ 0x43e3, 0x44b7, 0x44b7, 0x44ba, 0x44ba, 0x44bf, 0x44bf, 0x43d3,
+ 0x43d3, 0x44a7, 0x44a7, 0x44b7, 0x44a7, 0x43e3, 0x4476, 0x43e3,
+ 0x43e3, 0x43e3, 0x43e3, 0x6827, 0x0084, 0x2009, 0x000b, 0x2001,
+ 0x4300, 0x0078, 0x44c9, 0x6827, 0x000d, 0x2009, 0x000b, 0x2001,
+ 0x4300, 0x0078, 0x44aa, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001,
+ 0x4300, 0x0078, 0x4492, 0x2001, 0x0000, 0x007c, 0x2200, 0x0079,
+ 0x43f1, 0x4476, 0x440a, 0x440a, 0x440a, 0x440a, 0x44b7, 0x44b7,
+ 0x44b7, 0x44b7, 0x44b7, 0x44b7, 0x44b7, 0x44b7, 0x440a, 0x440a,
+ 0x440a, 0x440a, 0x44b7, 0x440a, 0x440a, 0x44b7, 0x44b7, 0x44b7,
+ 0x44b7, 0x4476, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4300,
+ 0x0078, 0x4492, 0xa684, 0x0004, 0x00c0, 0x4426, 0x6804, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x00c0, 0x467b, 0x1078, 0x44d0, 0x6807,
+ 0x0117, 0x1078, 0x3af8, 0x2001, 0x0002, 0x007c, 0x6000, 0xa084,
+ 0x0004, 0x0040, 0x467b, 0x2d58, 0x6804, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x00c0, 0x4435, 0x6807, 0x0117, 0x6827, 0x0002, 0x1078,
+ 0x4708, 0x6827, 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078,
+ 0x3ad7, 0x1078, 0x44d0, 0x2b68, 0x1078, 0x3af8, 0x0d7f, 0x1078,
+ 0x3af8, 0x2001, 0x0002, 0x007c, 0x6000, 0xa084, 0x0004, 0x0040,
+ 0x467b, 0x2d58, 0x6a04, 0xa294, 0x00ff, 0xa286, 0x0006, 0x00c0,
+ 0x445b, 0x6807, 0x0117, 0x6827, 0x0002, 0x2d58, 0x1078, 0x4708,
+ 0x6827, 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3ae7,
+ 0x1078, 0x44d0, 0x2b68, 0x1078, 0x3af8, 0x0d7f, 0x1078, 0x3af8,
+ 0x2001, 0x0002, 0x007c, 0x1078, 0x467b, 0x007c, 0x70b4, 0xa080,
+ 0x00d2, 0x781a, 0x2001, 0x0001, 0x1078, 0x4691, 0x1078, 0x46ff,
+ 0x7003, 0x0000, 0x2001, 0x0002, 0x007c, 0x1078, 0x46c1, 0x1078,
+ 0x46f8, 0x1078, 0x42d3, 0x1078, 0x41d0, 0x1078, 0x46ff, 0x2001,
+ 0x0001, 0x007c, 0x1078, 0x46c1, 0x1078, 0x46f8, 0x1078, 0x42d3,
+ 0x70b4, 0xa080, 0x00d2, 0x781a, 0x2001, 0x0013, 0x1078, 0x4691,
+ 0x1078, 0x46ff, 0x7003, 0x0000, 0x2001, 0x0002, 0x007c, 0x1078,
+ 0x467b, 0x007c, 0x1078, 0x46c1, 0x1078, 0x46f8, 0x1078, 0x42d3,
+ 0x1078, 0x41d0, 0x1078, 0x46ff, 0x2001, 0x0001, 0x007c, 0x2001,
+ 0x0003, 0x007c, 0x1078, 0x458b, 0x2001, 0x0000, 0x007c, 0x0c7e,
+ 0x077e, 0x6f14, 0x1078, 0x36e2, 0x077f, 0x0c7f, 0x2001, 0x0000,
+ 0x007c, 0x1078, 0x46c1, 0x1078, 0x467b, 0x2001, 0x0006, 0x007c,
+ 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x44db, 0xa186,
+ 0x000f, 0x00c0, 0x44df, 0x1078, 0x46f8, 0x1078, 0x42d3, 0x70b4,
+ 0xa080, 0x00d2, 0x781a, 0x1078, 0x46ff, 0x7003, 0x0000, 0x007c,
+ 0x7aa8, 0xa294, 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004,
+ 0x00c8, 0x467b, 0x1079, 0x44f5, 0x007c, 0x467b, 0x44f9, 0x467b,
+ 0x4592, 0xa282, 0x0003, 0x0040, 0x4500, 0x1078, 0x467b, 0x007c,
+ 0x7da8, 0xa5ac, 0x00ff, 0x7ca8, 0xa4a4, 0x00ff, 0x69b8, 0xa184,
+ 0x0100, 0x0040, 0x453f, 0xa18c, 0xfeff, 0x69ba, 0x78a0, 0xa005,
+ 0x00c0, 0x453f, 0xa4a4, 0x00ff, 0x0040, 0x4533, 0xa482, 0x000c,
+ 0x0040, 0x451c, 0x00c8, 0x4526, 0x852b, 0x852b, 0x1078, 0x3760,
+ 0x0040, 0x4526, 0x1078, 0x355b, 0x0078, 0x4535, 0x1078, 0x465d,
+ 0x1078, 0x3586, 0x69b8, 0xa18d, 0x0100, 0x69ba, 0xa6b5, 0x1000,
+ 0x7e5a, 0x0078, 0x4538, 0x1078, 0x3586, 0xa6b4, 0xefff, 0x7e5a,
+ 0x70b4, 0xa080, 0x0091, 0x781a, 0x2001, 0x0001, 0x007c, 0x0c7e,
+ 0x1078, 0x457f, 0x6200, 0xd2e4, 0x0040, 0x4570, 0x6208, 0x8217,
+ 0xa294, 0x00ff, 0xa282, 0x000c, 0x0048, 0x4552, 0x0040, 0x4552,
+ 0x2011, 0x000c, 0x2400, 0xa202, 0x00c8, 0x4557, 0x2220, 0x6208,
+ 0xa294, 0x00ff, 0x701c, 0xa202, 0x00c8, 0x455f, 0x721c, 0x2200,
+ 0xa502, 0x00c8, 0x4564, 0x2228, 0x1078, 0x4661, 0x852b, 0x852b,
+ 0x1078, 0x3760, 0x0040, 0x4570, 0x1078, 0x3562, 0x0078, 0x4574,
+ 0x1078, 0x465d, 0x1078, 0x358d, 0xa6b5, 0x1000, 0x7e5a, 0x70b4,
+ 0xa080, 0x00be, 0x781a, 0x2001, 0x0004, 0x0c7f, 0x007c, 0x007e,
+ 0x6814, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0,
+ 0x5380, 0x007f, 0x007c, 0x0c7e, 0x1078, 0x457f, 0x1078, 0x358d,
+ 0x0c7f, 0x007c, 0xa282, 0x0002, 0x00c0, 0x467b, 0x7aa8, 0xa294,
+ 0x00ff, 0x69b8, 0xa184, 0x0200, 0x0040, 0x45c9, 0xa18c, 0xfdff,
+ 0x69ba, 0x78a0, 0xa005, 0x00c0, 0x45c9, 0xa282, 0x0002, 0x00c8,
+ 0x369d, 0x1078, 0x4627, 0x1078, 0x362b, 0x1078, 0x3586, 0xa684,
+ 0x0100, 0x0040, 0x45bf, 0x682c, 0xa084, 0x0001, 0x0040, 0x45bf,
+ 0xc6fc, 0x7888, 0xa084, 0x0040, 0x0040, 0x45bf, 0xc6fd, 0xa6b5,
+ 0x1000, 0x7e5a, 0x70b4, 0xa080, 0x0091, 0x781a, 0x2001, 0x0001,
+ 0x007c, 0x0c7e, 0x1078, 0x457f, 0xa284, 0xfffe, 0x0040, 0x45d4,
+ 0x2011, 0x0001, 0x0078, 0x45d8, 0xa284, 0x0001, 0x0040, 0x45de,
+ 0x6100, 0xd1ec, 0x00c0, 0x45de, 0x2011, 0x0000, 0x1078, 0x4619,
+ 0x1078, 0x3632, 0x1078, 0x358d, 0xa684, 0x0100, 0x0040, 0x45f4,
+ 0x682c, 0xa084, 0x0001, 0x0040, 0x45f4, 0xc6fc, 0x7888, 0xa084,
+ 0x0040, 0x0040, 0x45f4, 0xc6fd, 0xa6b5, 0x1000, 0x7e5a, 0x70b4,
+ 0xa080, 0x00be, 0x781a, 0x2001, 0x0004, 0x0c7f, 0x007c, 0x0c7e,
+ 0x2960, 0x6000, 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, 0x460a,
+ 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003,
+ 0x7aaa, 0xa8c0, 0x0004, 0x68b8, 0xa085, 0x0200, 0x68ba, 0x0c7f,
+ 0x007c, 0x789b, 0x0018, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab,
+ 0x0003, 0x7aaa, 0x789b, 0x0081, 0x78ab, 0x0004, 0x007c, 0x0c7e,
+ 0x7054, 0x2060, 0x6000, 0xa084, 0x1000, 0x00c0, 0x4635, 0x2029,
+ 0x0032, 0x2021, 0x0000, 0x0078, 0x4655, 0x6508, 0xa5ac, 0x00ff,
+ 0x7018, 0xa086, 0x0028, 0x00c0, 0x4645, 0xa582, 0x0019, 0x00c8,
+ 0x464b, 0x2029, 0x0019, 0x0078, 0x464b, 0xa582, 0x000c, 0x00c8,
+ 0x464b, 0x2029, 0x000c, 0x6408, 0x8427, 0xa4a4, 0x00ff, 0xa482,
+ 0x000c, 0x0048, 0x4655, 0x2021, 0x000c, 0x1078, 0x4661, 0x68b8,
+ 0xa085, 0x0100, 0x68ba, 0x0c7f, 0x007c, 0x2021, 0x0000, 0x2029,
+ 0x0032, 0x789b, 0x0018, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab,
+ 0x0001, 0x7daa, 0x7caa, 0x789b, 0x0081, 0x78ab, 0x0005, 0x007c,
+ 0x2001, 0x0003, 0x1078, 0x4689, 0x70b4, 0xa080, 0x00be, 0x781a,
+ 0x2001, 0x0005, 0x007c, 0x2001, 0x0007, 0x1078, 0x4689, 0xa6b5,
+ 0x1000, 0x7e5a, 0x70b4, 0xa080, 0x00be, 0x781a, 0x2001, 0x0004,
+ 0x007c, 0x789b, 0x0018, 0x78aa, 0x789b, 0x0081, 0x78ab, 0x0001,
+ 0x007c, 0x6904, 0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x469f,
+ 0xa196, 0x000f, 0x0040, 0x469f, 0x1078, 0x195a, 0x007c, 0x6924,
+ 0xa194, 0x003f, 0x00c0, 0x46a8, 0xa18c, 0xffc0, 0xa105, 0x6826,
+ 0x1078, 0x3af8, 0x691c, 0xa184, 0x0100, 0x0040, 0x46b5, 0x6914,
+ 0x1078, 0x3b69, 0x6204, 0x8210, 0x6206, 0x007c, 0x692c, 0x6834,
+ 0x682e, 0xa112, 0x6930, 0x6838, 0x6832, 0xa11b, 0xa200, 0xa301,
+ 0x007c, 0x0c7e, 0xade0, 0x0018, 0x6003, 0x0070, 0x6106, 0x600b,
+ 0x0000, 0x600f, 0x0a00, 0x6013, 0x0000, 0x6017, 0x0000, 0x8007,
+ 0x601a, 0x601f, 0x0000, 0x6023, 0x0000, 0x0c7f, 0x6824, 0xa085,
+ 0x0080, 0x6826, 0x007c, 0x157e, 0x137e, 0x147e, 0x2098, 0xaf80,
+ 0x002d, 0x20a0, 0x81ac, 0x0040, 0x46e6, 0x53a6, 0xa184, 0x0001,
+ 0x0040, 0x46ec, 0x3304, 0x78be, 0x147f, 0x137f, 0x157f, 0x007c,
+ 0x70b0, 0xa005, 0x10c0, 0x23eb, 0x70b3, 0x8000, 0x0078, 0x4a3a,
+ 0x71b0, 0x81ff, 0x0040, 0x46fe, 0x1078, 0x4b30, 0x007c, 0x71b0,
+ 0x81ff, 0x0040, 0x4707, 0x70b3, 0x0000, 0x1078, 0x4776, 0x007c,
+ 0x0c7e, 0x0d7e, 0x1078, 0x1937, 0x0c7f, 0x157e, 0x137e, 0x147e,
+ 0x2da0, 0x2c98, 0x20a9, 0x0031, 0x53a3, 0x147f, 0x137f, 0x157f,
+ 0x6807, 0x010d, 0x680b, 0x0000, 0x7004, 0x8007, 0x681a, 0x6823,
+ 0x0000, 0x681f, 0x0000, 0x689f, 0x0000, 0x0c7f, 0x007c, 0x70b4,
+ 0xa080, 0x0091, 0x781a, 0x0078, 0x2459, 0x70b4, 0xa080, 0x0081,
+ 0x781a, 0x0078, 0x2459, 0x70b4, 0xa080, 0x00be, 0x781a, 0x0078,
+ 0x2459, 0x70b4, 0xa080, 0x00c8, 0x781a, 0x0078, 0x2459, 0x6904,
+ 0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x474c, 0xa196, 0x000f,
+ 0x0040, 0x474c, 0x6807, 0x0117, 0x2001, 0x0200, 0x6826, 0x8007,
+ 0x789b, 0x000e, 0x78aa, 0x6820, 0xa085, 0x8000, 0x6822, 0x2031,
+ 0x0400, 0x6eb6, 0x7e5a, 0x71b4, 0xa188, 0x0091, 0x791a, 0x007c,
+ 0x1078, 0x46ff, 0x7848, 0xa085, 0x000c, 0x784a, 0x70b4, 0xa080,
+ 0x00d2, 0x781a, 0x2009, 0x000b, 0x2001, 0x4400, 0x1078, 0x46c1,
+ 0x2001, 0x0013, 0x1078, 0x4691, 0x0078, 0x3b96, 0x127e, 0x2091,
+ 0x2200, 0x2049, 0x4776, 0x7000, 0x7204, 0xa205, 0x720c, 0xa215,
+ 0x7008, 0xa084, 0xfff7, 0xa205, 0x0040, 0x4788, 0x0078, 0x478d,
+ 0x7003, 0x0000, 0x127f, 0x2000, 0x007c, 0x7000, 0xa084, 0x0001,
+ 0x00c0, 0x47bb, 0x7108, 0x8103, 0x00c8, 0x479a, 0x1078, 0x48bd,
+ 0x0078, 0x4792, 0x700c, 0xa08c, 0x00ff, 0x0040, 0x47bb, 0x7004,
+ 0x8004, 0x00c8, 0x47b2, 0x7014, 0xa005, 0x00c0, 0x47ae, 0x7010,
+ 0xa005, 0x0040, 0x47b2, 0xa102, 0x00c8, 0x4792, 0x7007, 0x0010,
+ 0x0078, 0x47bb, 0x8aff, 0x0040, 0x47bb, 0x1078, 0x4b07, 0x00c0,
+ 0x47b5, 0x0040, 0x4792, 0x1078, 0x4846, 0x7003, 0x0000, 0x127f,
+ 0x2000, 0x007c, 0x017e, 0x6104, 0xa18c, 0x00ff, 0xa186, 0x0007,
+ 0x0040, 0x47ce, 0xa18e, 0x000f, 0x00c0, 0x47d1, 0x6040, 0x0078,
+ 0x47d2, 0x6428, 0x017f, 0x84ff, 0x0040, 0x47fc, 0x2c70, 0x7004,
+ 0xa0bc, 0x000f, 0xa7b8, 0x480c, 0x273c, 0x87fb, 0x00c0, 0x47ea,
+ 0x0048, 0x47e4, 0x1078, 0x23eb, 0x609c, 0xa075, 0x0040, 0x47fc,
+ 0x0078, 0x47d7, 0x2704, 0xae68, 0x6808, 0xa630, 0x680c, 0xa529,
+ 0x8421, 0x0040, 0x47fc, 0x8738, 0x2704, 0xa005, 0x00c0, 0x47eb,
+ 0x709c, 0xa075, 0x00c0, 0x47d7, 0x007c, 0x0000, 0x0005, 0x0009,
+ 0x000d, 0x0011, 0x0015, 0x0019, 0x001d, 0x0000, 0x0003, 0x0009,
+ 0x000f, 0x0015, 0x001b, 0x0000, 0x0000, 0x4801, 0x47fe, 0x0000,
+ 0x0000, 0x8000, 0x0000, 0x4801, 0x0000, 0x4809, 0x4806, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x4809, 0x0000, 0x4804, 0x4804, 0x0000,
+ 0x0000, 0x8000, 0x0000, 0x4804, 0x0000, 0x480a, 0x480a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x480a, 0x127e, 0x2091, 0x2200, 0x2079,
+ 0x5100, 0x2071, 0x0010, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003,
+ 0x0000, 0x2071, 0x0020, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003,
+ 0x0000, 0x2049, 0x0000, 0x127f, 0x2000, 0x007c, 0x2049, 0x4846,
+ 0x2019, 0x0000, 0x7004, 0x8004, 0x00c8, 0x4899, 0x7007, 0x0012,
+ 0x7108, 0x7008, 0xa106, 0x00c0, 0x4850, 0xa184, 0x01e0, 0x0040,
+ 0x485b, 0x1078, 0x23eb, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+ 0x00c8, 0x4866, 0xa184, 0x4000, 0x00c0, 0x4850, 0xa19c, 0x300c,
+ 0xa386, 0x2004, 0x0040, 0x4874, 0xa386, 0x0008, 0x0040, 0x487f,
+ 0xa386, 0x200c, 0x00c0, 0x4850, 0x7200, 0x8204, 0x0048, 0x487f,
+ 0x730c, 0xa384, 0x00ff, 0x0040, 0x487f, 0x1078, 0x23eb, 0x7007,
+ 0x0012, 0x7000, 0xa084, 0x0001, 0x00c0, 0x4899, 0x7008, 0xa084,
+ 0x01e0, 0x00c0, 0x4899, 0x7310, 0x7014, 0xa305, 0x0040, 0x4899,
+ 0x710c, 0xa184, 0x0300, 0x00c0, 0x4899, 0xa184, 0x00ff, 0x00c0,
+ 0x4846, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004, 0xa084, 0x0008,
+ 0x00c0, 0x489d, 0x7007, 0x0012, 0x7108, 0x8103, 0x0048, 0x48a2,
+ 0x7003, 0x0000, 0x2049, 0x0000, 0x007c, 0x107e, 0x007e, 0x127e,
+ 0x157e, 0x2091, 0x2200, 0x7108, 0x1078, 0x48bd, 0x157f, 0x127f,
+ 0x2091, 0x8001, 0x007f, 0x107f, 0x007c, 0x7204, 0x7500, 0x730c,
+ 0xa384, 0x0300, 0x00c0, 0x48e4, 0xa184, 0x01e0, 0x00c0, 0x4908,
+ 0x7108, 0xa184, 0x01e0, 0x00c0, 0x4908, 0x2001, 0x04fd, 0x2004,
+ 0xa082, 0x0005, 0x00c8, 0x48d8, 0xa184, 0x4000, 0x00c0, 0x48c8,
+ 0xa184, 0x0007, 0x0079, 0x48dc, 0x48e6, 0x48f8, 0x48e4, 0x48f8,
+ 0x48e4, 0x4944, 0x48e4, 0x4942, 0x1078, 0x23eb, 0x7004, 0xa084,
+ 0x0010, 0xa085, 0x0002, 0x7006, 0x8aff, 0x00c0, 0x48f3, 0x2049,
+ 0x0000, 0x0078, 0x48f7, 0x1078, 0x4b07, 0x00c0, 0x48f3, 0x007c,
+ 0x7004, 0xa084, 0x0010, 0xa085, 0x0002, 0x7006, 0x8aff, 0x00c0,
+ 0x4903, 0x0078, 0x4907, 0x1078, 0x4b07, 0x00c0, 0x4903, 0x007c,
+ 0x7007, 0x0012, 0x7108, 0x00e0, 0x490b, 0x2091, 0x6000, 0x00e0,
+ 0x490f, 0x2091, 0x6000, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004,
+ 0xa084, 0x0008, 0x00c0, 0x4917, 0x7007, 0x0012, 0x7108, 0x8103,
+ 0x0048, 0x491c, 0x7003, 0x0000, 0x7000, 0xa005, 0x00c0, 0x4930,
+ 0x7004, 0xa005, 0x00c0, 0x4930, 0x700c, 0xa005, 0x0040, 0x4932,
+ 0x0078, 0x4913, 0x2049, 0x0000, 0x1078, 0x3809, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x493d, 0x681b, 0x0002, 0x007c, 0x1078, 0x23eb,
+ 0x1078, 0x23eb, 0x1078, 0x49a0, 0x7210, 0x7114, 0x700c, 0xa09c,
+ 0x00ff, 0x2800, 0xa300, 0xa211, 0xa189, 0x0000, 0x1078, 0x49a0,
+ 0x2704, 0x2c58, 0xac60, 0x6308, 0x2200, 0xa322, 0x630c, 0x2100,
+ 0xa31b, 0x2400, 0xa305, 0x0040, 0x4967, 0x00c8, 0x4967, 0x8412,
+ 0x8210, 0x830a, 0xa189, 0x0000, 0x2b60, 0x0078, 0x494e, 0x2b60,
+ 0x8a07, 0x007e, 0x6004, 0xa084, 0x0008, 0x0040, 0x4973, 0xa7ba,
+ 0x4806, 0x0078, 0x4975, 0xa7ba, 0x47fe, 0x007f, 0xa73d, 0x2c00,
+ 0x6886, 0x6f8a, 0x6c92, 0x6b8e, 0x7007, 0x0012, 0x1078, 0x4846,
+ 0x007c, 0x8738, 0x2704, 0xa005, 0x00c0, 0x4994, 0x609c, 0xa005,
+ 0x0040, 0x499d, 0x2060, 0x6004, 0xa084, 0x000f, 0xa080, 0x480c,
+ 0x203c, 0x87fb, 0x1040, 0x23eb, 0x8a51, 0x0040, 0x499c, 0x7008,
+ 0xa084, 0x0003, 0xa086, 0x0003, 0x007c, 0x2051, 0x0000, 0x007c,
+ 0x8a50, 0x8739, 0x2704, 0xa004, 0x00c0, 0x49b4, 0x6000, 0xa064,
+ 0x00c0, 0x49ab, 0x2d60, 0x6004, 0xa084, 0x000f, 0xa080, 0x481c,
+ 0x203c, 0x87fb, 0x1040, 0x23eb, 0x007c, 0x127e, 0x0d7e, 0x2091,
+ 0x2200, 0x0d7f, 0x6884, 0x2060, 0x6888, 0x6b8c, 0x6c90, 0x8057,
+ 0xaad4, 0x00ff, 0xa084, 0x00ff, 0x007e, 0x6804, 0xa084, 0x0008,
+ 0x007f, 0x0040, 0x49cf, 0xa0b8, 0x4806, 0x0078, 0x49d1, 0xa0b8,
+ 0x47fe, 0x7e08, 0xa6b5, 0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186,
+ 0x0007, 0x0040, 0x49df, 0xa18e, 0x000f, 0x00c0, 0x49e8, 0x681c,
+ 0xa084, 0x0040, 0x0040, 0x49ef, 0xa6b5, 0x0001, 0x0078, 0x49ef,
+ 0x681c, 0xa084, 0x0040, 0x0040, 0x49ef, 0xa6b5, 0x0001, 0x7007,
+ 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x49f1, 0x2400, 0xa305,
+ 0x00c0, 0x49fc, 0x0078, 0x4a22, 0x2c58, 0x2704, 0x6104, 0xac60,
+ 0x6000, 0xa400, 0x701a, 0x6004, 0xa301, 0x701e, 0xa184, 0x0008,
+ 0x0040, 0x4a12, 0x6010, 0xa081, 0x0000, 0x7022, 0x6014, 0xa081,
+ 0x0000, 0x7026, 0x6208, 0x2400, 0xa202, 0x7012, 0x620c, 0x2300,
+ 0xa203, 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078, 0x4981,
+ 0x0078, 0x4a24, 0x1078, 0x4b07, 0x00c0, 0x4a22, 0x127f, 0x2000,
+ 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x7007, 0x0004,
+ 0x7004, 0xa084, 0x0004, 0x00c0, 0x4a30, 0x7003, 0x0008, 0x127f,
+ 0x2000, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049,
+ 0x4a3a, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x4a43,
+ 0x7e08, 0xa6b5, 0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007,
+ 0x0040, 0x4a56, 0xa18e, 0x000f, 0x00c0, 0x4a61, 0x681c, 0xa084,
+ 0x0040, 0x0040, 0x4a5d, 0xa6b5, 0x0001, 0x6840, 0x2050, 0x0078,
+ 0x4a6a, 0x681c, 0xa084, 0x0020, 0x00c0, 0x4a68, 0xa6b5, 0x0001,
+ 0x6828, 0x2050, 0x2d60, 0x6004, 0xa0bc, 0x000f, 0xa7b8, 0x480c,
+ 0x273c, 0x87fb, 0x00c0, 0x4a7e, 0x0048, 0x4a78, 0x1078, 0x23eb,
+ 0x689c, 0xa065, 0x0040, 0x4a82, 0x0078, 0x4a6b, 0x1078, 0x4b07,
+ 0x00c0, 0x4a7e, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e, 0x017e,
+ 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x037f, 0x047f, 0x7e08, 0xa6b5,
+ 0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4a9c,
+ 0xa18e, 0x000f, 0x00c0, 0x4aa5, 0x681c, 0xa084, 0x0040, 0x0040,
+ 0x4aac, 0xa6b5, 0x0001, 0x0078, 0x4aac, 0x681c, 0xa084, 0x0040,
+ 0x0040, 0x4aac, 0xa6b5, 0x0001, 0x2049, 0x4a85, 0x017e, 0x6904,
+ 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4aba, 0xa18e, 0x000f,
+ 0x00c0, 0x4abd, 0x6840, 0x0078, 0x4abe, 0x6828, 0x017f, 0xa055,
+ 0x0040, 0x4b04, 0x2d70, 0x2e60, 0x7004, 0xa0bc, 0x000f, 0xa7b8,
+ 0x480c, 0x273c, 0x87fb, 0x00c0, 0x4ad8, 0x0048, 0x4ad1, 0x1078,
+ 0x23eb, 0x709c, 0xa075, 0x2060, 0x0040, 0x4b04, 0x0078, 0x4ac4,
+ 0x2704, 0xae68, 0x6808, 0xa422, 0x680c, 0xa31b, 0x0048, 0x4af1,
+ 0x8a51, 0x00c0, 0x4ae5, 0x1078, 0x23eb, 0x8738, 0x2704, 0xa005,
+ 0x00c0, 0x4ad9, 0x709c, 0xa075, 0x2060, 0x0040, 0x4b04, 0x0078,
+ 0x4ac4, 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x6908, 0x2400,
+ 0xa122, 0x690c, 0x2300, 0xa11b, 0x00c8, 0x4b00, 0x1078, 0x23eb,
+ 0x2071, 0x0020, 0x0078, 0x49ef, 0x127f, 0x2000, 0x007c, 0x7008,
+ 0xa084, 0x0003, 0xa086, 0x0003, 0x0040, 0x4b2f, 0x2704, 0xac08,
+ 0x2104, 0x701a, 0x8108, 0x2104, 0x701e, 0x8108, 0x2104, 0x7012,
+ 0x8108, 0x2104, 0x7016, 0x6004, 0xa084, 0x0008, 0x0040, 0x4b26,
+ 0x8108, 0x2104, 0x7022, 0x8108, 0x2104, 0x7026, 0x7602, 0x7004,
+ 0xa084, 0x0010, 0xa085, 0x0001, 0x7006, 0x1078, 0x4981, 0x007c,
+ 0x127e, 0x007e, 0x0d7e, 0x2091, 0x2200, 0x2049, 0x4b30, 0x0d7f,
+ 0x087f, 0x7108, 0xa184, 0x0003, 0x00c0, 0x4b5a, 0x017e, 0x6904,
+ 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4b4a, 0xa18e, 0x000f,
+ 0x00c0, 0x4b4d, 0x6840, 0x0078, 0x4b4e, 0x6828, 0x017f, 0xa005,
+ 0x0040, 0x4b68, 0x0078, 0x478d, 0x0020, 0x4b5a, 0x1078, 0x4944,
+ 0x0078, 0x4b68, 0x00a0, 0x4b61, 0x7108, 0x1078, 0x48bd, 0x0078,
+ 0x4b39, 0x7007, 0x0010, 0x00a0, 0x4b63, 0x7108, 0x1078, 0x48bd,
+ 0x7008, 0xa086, 0x0008, 0x00c0, 0x4b39, 0x7000, 0xa005, 0x00c0,
+ 0x4b39, 0x7003, 0x0000, 0x2049, 0x0000, 0x127f, 0x2000, 0x007c,
+ 0x127e, 0x147e, 0x137e, 0x157e, 0x0c7e, 0x0d7e, 0x2091, 0x2200,
+ 0x0d7f, 0x2049, 0x4b78, 0xad80, 0x0011, 0x20a0, 0x2099, 0x0031,
+ 0x700c, 0xa084, 0x00ff, 0x682a, 0x7007, 0x0008, 0x7007, 0x0002,
+ 0x7003, 0x0001, 0x0040, 0x4b97, 0x8000, 0x80ac, 0x53a5, 0x7007,
+ 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x4b99, 0x0c7f, 0x2049,
+ 0x0000, 0x7003, 0x0000, 0x157f, 0x137f, 0x147f, 0x127f, 0x2000,
+ 0x007c, 0x2091, 0x6000, 0x2091, 0x8000, 0x78cc, 0xa005, 0x0040,
+ 0x4bc0, 0x7994, 0x70d0, 0xa106, 0x00c0, 0x4bc0, 0x7804, 0xa005,
+ 0x0040, 0x4bc0, 0x7807, 0x0000, 0x0068, 0x4bc0, 0x2091, 0x4080,
+ 0x7820, 0x8001, 0x7822, 0x00c0, 0x4c1b, 0x7824, 0x7822, 0x2069,
+ 0x5140, 0x6800, 0xa084, 0x0007, 0x0040, 0x4bde, 0xa086, 0x0002,
+ 0x0040, 0x4bde, 0x6834, 0xa00d, 0x0040, 0x4bde, 0x2104, 0xa005,
+ 0x0040, 0x4bde, 0x8001, 0x200a, 0x0040, 0x4cc3, 0x7848, 0xa005,
+ 0x0040, 0x4bec, 0x8001, 0x784a, 0x00c0, 0x4bec, 0x2009, 0x0102,
+ 0x6844, 0x200a, 0x1078, 0x21d2, 0x6890, 0xa005, 0x0040, 0x4bf8,
+ 0x8001, 0x6892, 0x00c0, 0x4bf8, 0x686f, 0x0000, 0x6873, 0x0001,
+ 0x2061, 0x5400, 0x20a9, 0x0100, 0x2009, 0x0002, 0x6034, 0xa005,
+ 0x0040, 0x4c0e, 0x8001, 0x6036, 0x00c0, 0x4c0e, 0x6010, 0xa005,
+ 0x0040, 0x4c0e, 0x017e, 0x1078, 0x21d2, 0x017f, 0xace0, 0x0010,
+ 0x0070, 0x4c14, 0x0078, 0x4bfe, 0x8109, 0x0040, 0x4c1b, 0x20a9,
+ 0x0100, 0x0078, 0x4bfe, 0x1078, 0x4c28, 0x1078, 0x4c4d, 0x2009,
+ 0x5151, 0x2104, 0x2009, 0x0102, 0x200a, 0x2091, 0x8001, 0x007c,
+ 0x7834, 0x8001, 0x7836, 0x00c0, 0x4c4c, 0x7838, 0x7836, 0x2091,
+ 0x8000, 0x7844, 0xa005, 0x00c0, 0x4c37, 0x2001, 0x0101, 0x8001,
+ 0x7846, 0xa080, 0x7400, 0x2040, 0x2004, 0xa065, 0x0040, 0x4c4c,
+ 0x6024, 0xa005, 0x0040, 0x4c48, 0x8001, 0x6026, 0x0040, 0x4c7c,
+ 0x6000, 0x2c40, 0x0078, 0x4c3d, 0x007c, 0x7828, 0x8001, 0x782a,
+ 0x00c0, 0x4c7b, 0x782c, 0x782a, 0x7830, 0xa005, 0x00c0, 0x4c5a,
+ 0x2001, 0x0200, 0x8001, 0x7832, 0x8003, 0x8003, 0x8003, 0x8003,
+ 0xa090, 0x5400, 0xa298, 0x0002, 0x2304, 0xa084, 0x0008, 0x0040,
+ 0x4c7b, 0xa290, 0x0009, 0x2204, 0xa005, 0x0040, 0x4c73, 0x8001,
+ 0x2012, 0x00c0, 0x4c7b, 0x2304, 0xa084, 0xfff7, 0xa085, 0x0080,
+ 0x201a, 0x1078, 0x21d2, 0x007c, 0x2069, 0x5140, 0x6800, 0xa005,
+ 0x0040, 0x4c86, 0x6848, 0xac06, 0x0040, 0x4cc3, 0x601b, 0x0006,
+ 0x60b4, 0xa084, 0x3f00, 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085,
+ 0x0060, 0x6022, 0x6000, 0x2042, 0x6714, 0x6f82, 0x1078, 0x1973,
+ 0x6818, 0xa005, 0x0040, 0x4c9e, 0x8001, 0x681a, 0x6808, 0xa084,
+ 0xffef, 0x680a, 0x6810, 0x8001, 0x00d0, 0x4ca8, 0x1078, 0x23eb,
+ 0x6812, 0x602f, 0x0000, 0x6033, 0x0000, 0x2c68, 0x1078, 0x1c70,
+ 0x2069, 0x5140, 0x7944, 0xa184, 0x0100, 0x2001, 0x0006, 0x686e,
+ 0x00c0, 0x4cbe, 0x6986, 0x2001, 0x0004, 0x686e, 0x1078, 0x21cd,
+ 0x2091, 0x8001, 0x007c, 0x2069, 0x0100, 0x2009, 0x5140, 0x2104,
+ 0xa084, 0x0007, 0x0040, 0x4d1f, 0xa086, 0x0007, 0x00c0, 0x4cd9,
+ 0x0d7e, 0x2009, 0x5152, 0x216c, 0x1078, 0x3a4e, 0x0d7f, 0x0078,
+ 0x4d1f, 0x2009, 0x5152, 0x2164, 0x1078, 0x2396, 0x601b, 0x0006,
+ 0x6858, 0xa084, 0x3f00, 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085,
+ 0x0048, 0x6022, 0x602f, 0x0000, 0x6033, 0x0000, 0x6830, 0xa084,
+ 0x0040, 0x0040, 0x4d13, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848,
+ 0xa084, 0x0004, 0x0040, 0x4d00, 0x0070, 0x4d00, 0x0078, 0x4cf7,
+ 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040,
+ 0x4d0d, 0x0070, 0x4d0d, 0x0078, 0x4d04, 0x20a9, 0x00fa, 0x0070,
+ 0x4d13, 0x0078, 0x4d0f, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b,
+ 0x0048, 0x2009, 0x515b, 0x200b, 0x0007, 0x784c, 0x784a, 0x2091,
+ 0x8001, 0x007c, 0x2079, 0x5100, 0x1078, 0x4d4d, 0x1078, 0x4d31,
+ 0x1078, 0x4d3f, 0x7833, 0x0000, 0x7847, 0x0000, 0x784b, 0x0000,
+ 0x007c, 0x2019, 0x0003, 0x2011, 0x5146, 0x2204, 0xa086, 0x003c,
+ 0x0040, 0x4d3c, 0x2019, 0x0002, 0x7b2a, 0x7b2e, 0x007c, 0x2019,
+ 0x0039, 0x2011, 0x5146, 0x2204, 0xa086, 0x003c, 0x0040, 0x4d4a,
+ 0x2019, 0x0027, 0x7b36, 0x7b3a, 0x007c, 0x2019, 0x3971, 0x2011,
+ 0x5146, 0x2204, 0xa086, 0x003c, 0x0040, 0x4d58, 0x2019, 0x2626,
+ 0x7b22, 0x7b26, 0x783f, 0x0000, 0x7843, 0x000a, 0x007c, 0x0020,
+ 0x002b, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0014, 0x0014, 0x9849, 0x0014, 0x0014, 0x0014,
+ 0x0014, 0x0014, 0x0014, 0x0014, 0x0080, 0x000f, 0x0000, 0x0201,
+ 0x0604, 0x0c08, 0x2120, 0x4022, 0xf880, 0x0018, 0x300b, 0xa201,
+ 0x0014, 0xa200, 0x0014, 0xa200, 0x0214, 0x0000, 0x006c, 0x0002,
+ 0x0014, 0x98d0, 0x009e, 0x0096, 0xa202, 0x8838, 0x3806, 0x8839,
+ 0x20c3, 0x0864, 0x9884, 0x28c1, 0x9cb1, 0xa203, 0x300c, 0x2846,
+ 0x8161, 0x846a, 0x8300, 0x1856, 0x883a, 0x9865, 0x28f2, 0x9c90,
+ 0x9858, 0x300c, 0x28e1, 0x9c90, 0x2802, 0xa206, 0x64c3, 0x282d,
+ 0xa207, 0x64a0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7824, 0x68c1,
+ 0x7864, 0x883e, 0x9878, 0x8576, 0x8677, 0x206b, 0x28c1, 0x9cb1,
+ 0x2044, 0x2103, 0x20a2, 0x2081, 0x9865, 0xa209, 0x2901, 0x988c,
+ 0x0014, 0xa205, 0xa300, 0x1872, 0x879a, 0x883c, 0x1fe2, 0xc601,
+ 0xa20a, 0x856e, 0x0704, 0x9c90, 0x0014, 0xa204, 0xa300, 0x3009,
+ 0x19e2, 0xf868, 0x8176, 0x86eb, 0x85eb, 0x872e, 0x87a9, 0x883f,
+ 0x08e6, 0x9890, 0xf881, 0x988b, 0xc801, 0x0014, 0xf8c1, 0x0016,
+ 0x85b2, 0x80f0, 0x9532, 0xfb02, 0x1de2, 0x0014, 0x8532, 0xf241,
+ 0x0014, 0x1de2, 0x84a8, 0xd7a0, 0x1fe6, 0x0014, 0xa208, 0x6043,
+ 0x8008, 0x1dc1, 0x0016, 0x8300, 0x8160, 0x842a, 0xf041, 0x3008,
+ 0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011, 0x20d5, 0x8822, 0x0016,
+ 0x8000, 0x2847, 0x1011, 0x98c3, 0x8000, 0xa000, 0x2802, 0x1011,
+ 0x98c9, 0x9865, 0x283e, 0x1011, 0x98cd, 0xa20b, 0x0017, 0x300c,
+ 0xa300, 0x1de2, 0xdb81, 0x0014, 0x0210, 0x98da, 0x0014, 0x26e0,
+ 0x873a, 0xfb02, 0x19f2, 0x1fe2, 0x0014, 0xa20d, 0x3806, 0x0210,
+ 0x9cb6, 0x0704, 0x0000, 0x006c, 0x0002, 0x984f, 0x0014, 0x009e,
+ 0x00a5, 0x0017, 0x60ff, 0x300c, 0x8720, 0xa211, 0x9cd5, 0x8772,
+ 0x8837, 0x2101, 0x987a, 0x10d2, 0x78e2, 0x9cd8, 0x9859, 0xd984,
+ 0xf0e2, 0xf0a1, 0x98d2, 0x0014, 0x8831, 0xd166, 0x8830, 0x800f,
+ 0x9401, 0xb520, 0xc802, 0x8820, 0x987a, 0x2301, 0x987a, 0x10d2,
+ 0x78e4, 0x9cd8, 0x8821, 0x8820, 0x9859, 0xf123, 0xf142, 0xf101,
+ 0x98cb, 0x10d2, 0x70f6, 0x8832, 0x8203, 0x870c, 0xd99e, 0x6001,
+ 0x0014, 0x6845, 0x0214, 0xa21b, 0x9cd5, 0x2001, 0x98ca, 0x8201,
+ 0x1852, 0xd184, 0xd163, 0x8834, 0x8001, 0x988d, 0x3027, 0x84a8,
+ 0x1a56, 0x8833, 0x0014, 0xa218, 0x6981, 0x9cc1, 0x692a, 0x6902,
+ 0x1834, 0x989d, 0x1a14, 0x8010, 0x8592, 0x8026, 0x84b9, 0x7021,
+ 0x0014, 0xa300, 0x69e1, 0x9caa, 0x694c, 0xa213, 0x9cba, 0x1462,
+ 0xa213, 0x8000, 0x16e1, 0x98b4, 0x8023, 0x16e1, 0x8001, 0x10f1,
+ 0x0016, 0x6968, 0xa214, 0x9cba, 0x8004, 0x16e1, 0x0101, 0x300a,
+ 0x8827, 0x0014, 0x9cba, 0x0014, 0x61c2, 0x8002, 0x14e1, 0x0016,
+ 0xa217, 0x9cc1, 0x0014, 0xa300, 0x8181, 0x842a, 0x84a8, 0x1ce6,
+ 0x882c, 0x0016, 0xa212, 0x9cd5, 0x10d2, 0x70e4, 0x0004, 0x8007,
+ 0x9424, 0xcc1a, 0x9cd8, 0x98ca, 0x8827, 0x300a, 0x0013, 0x8000,
+ 0x84a4, 0x0016, 0x11c2, 0x211e, 0x870e, 0xa21d, 0x0014, 0x878e,
+ 0x0016, 0xa21c, 0x1035, 0x9891, 0xa210, 0xa000, 0x8010, 0x8592,
+ 0x853b, 0xd044, 0x8022, 0x3807, 0x84bb, 0x98ef, 0x8021, 0x3807,
+ 0x84b9, 0x300c, 0x817e, 0x872b, 0x8772, 0x9891, 0x0000, 0x0020,
+ 0x002b, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0014, 0x0014, 0x9849, 0x0014, 0x0014, 0x98e5,
+ 0x98d0, 0x0014, 0x0014, 0x0014, 0x0080, 0x013f, 0x0000, 0x0201,
+ 0x0604, 0x0c08, 0x2120, 0x4022, 0xf880, 0x0018, 0x300b, 0xa201,
+ 0x0014, 0xa200, 0x0014, 0xa200, 0x0214, 0xa202, 0x8838, 0x3806,
+ 0x8839, 0x20c3, 0x0864, 0xa82e, 0x28c1, 0x9cb1, 0xa203, 0x300c,
+ 0x2846, 0x8161, 0x846a, 0x8300, 0x1856, 0x883a, 0xa804, 0x28f2,
+ 0x9c90, 0xa8f4, 0x300c, 0x28e1, 0x9c90, 0x2802, 0xa206, 0x64c3,
+ 0x282d, 0xa207, 0x64a0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7824,
+ 0x68c1, 0x7864, 0x883e, 0xa802, 0x8576, 0x8677, 0x206b, 0x28c1,
+ 0x9cb1, 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8e5, 0xa209, 0x2901,
+ 0xa809, 0x0014, 0xa205, 0xa300, 0x1872, 0x879a, 0x883c, 0x1fe2,
+ 0xc601, 0xa20a, 0x856e, 0x0704, 0x9c90, 0x0014, 0xa204, 0xa300,
+ 0x3009, 0x19e2, 0xf868, 0x8176, 0x86eb, 0x85eb, 0x872e, 0x87a9,
+ 0x883f, 0x08e6, 0xa8f3, 0xf881, 0xa8ec, 0xc801, 0x0014, 0xf8c1,
+ 0x0016, 0x85b2, 0x80f0, 0x9532, 0xfb02, 0x1de2, 0x0014, 0x8532,
+ 0xf241, 0x0014, 0x1de2, 0x84a8, 0xd7a0, 0x1fe6, 0x0014, 0xa208,
+ 0x6043, 0x8008, 0x1dc1, 0x0016, 0x8300, 0x8160, 0x842a, 0xf041,
+ 0x3008, 0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011, 0x20d5, 0x8822,
+ 0x0016, 0x8000, 0x2847, 0x1011, 0xa8fc, 0x8000, 0xa000, 0x2802,
+ 0x1011, 0xa8fd, 0xa898, 0x283e, 0x1011, 0xa8fd, 0xa20b, 0x0017,
+ 0x300c, 0xa300, 0x1de2, 0xdb81, 0x0014, 0x0210, 0xa801, 0x0014,
+ 0x26e0, 0x873a, 0xfb02, 0x19f2, 0x1fe2, 0x0014, 0xa20d, 0x3806,
+ 0x0210, 0x9cb6, 0x0704, 0x0017, 0x60ff, 0x300c, 0x8720, 0xa211,
+ 0x9d6b, 0x8772, 0x8837, 0x2101, 0xa821, 0x10d2, 0x78e2, 0x9d6e,
+ 0xa8fc, 0xd984, 0xf0e2, 0xf0a1, 0xa871, 0x0014, 0x8831, 0xd166,
+ 0x8830, 0x800f, 0x9401, 0xb520, 0xc802, 0x8820, 0xa80f, 0x2301,
+ 0xa80d, 0x10d2, 0x78e4, 0x9d6e, 0x8821, 0x8820, 0xa8e6, 0xf123,
+ 0xf142, 0xf101, 0xa854, 0x10d2, 0x70f6, 0x8832, 0x8203, 0x870c,
+ 0xd99e, 0x6001, 0x0014, 0x6845, 0x0214, 0xa21b, 0x9d6b, 0x2001,
+ 0xa845, 0x8201, 0x1852, 0xd184, 0xd163, 0x8834, 0x8001, 0xa801,
+ 0x3027, 0x84a8, 0x1a56, 0x8833, 0x0014, 0xa218, 0x6981, 0x9d57,
+ 0x692a, 0x6902, 0x1834, 0xa805, 0x1a14, 0x8010, 0x8592, 0x8026,
+ 0x84b9, 0x7021, 0x0014, 0xa300, 0x69e1, 0x9d40, 0x694c, 0xa213,
+ 0x9d50, 0x1462, 0xa213, 0x8000, 0x16e1, 0xa80a, 0x8023, 0x16e1,
+ 0x8001, 0x10f1, 0x0016, 0x6968, 0xa214, 0x9d50, 0x8004, 0x16e1,
+ 0x0101, 0x300a, 0x8827, 0x0014, 0x9d50, 0x0014, 0x61c2, 0x8002,
+ 0x14e1, 0x0016, 0xa217, 0x9d57, 0x0014, 0xa300, 0x8181, 0x842a,
+ 0x84a8, 0x1ce6, 0x882c, 0x0016, 0xa212, 0x9d6b, 0x10d2, 0x70e4,
+ 0x0004, 0x8007, 0x9424, 0xcc1a, 0x9d6e, 0xa8f8, 0x8827, 0x300a,
+ 0x0013, 0x8000, 0x84a4, 0x0016, 0x11c2, 0x211e, 0x870e, 0xa21d,
+ 0x0014, 0x878e, 0x0016, 0xa21c, 0x1035, 0xa8af, 0xa210, 0x3807,
+ 0x300c, 0x817e, 0x872b, 0x8772, 0xa8a8, 0x0000, 0xdf21
+};
+static unsigned short risc_code_length01 = 0x4057;
+
diff --git a/drivers/scsi/ql12160_fw.h b/drivers/scsi/ql12160_fw.h
new file mode 100644
index 000000000000..9db6a208c9f8
--- /dev/null
+++ b/drivers/scsi/ql12160_fw.h
@@ -0,0 +1,1781 @@
+/*****************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP12160 device driver for Linux 2.2.x and 2.4.x
+ * Copyright (C) 2002 Qlogic Corporation (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *****************************************************************************/
+
+/************************************************************************
+ * --- ISP12160A Initiator Firmware --- *
+ * 32 LUN Support *
+ ************************************************************************/
+
+/*
+ * Firmware Version 10.04.32 (12:03 May 09, 2001)
+ */
+
+#ifdef UNIQUE_FW_NAME
+static unsigned char fw12160i_version_str[] = {10,4,32};
+#else
+static unsigned char firmware_version[] = {10,4,32};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw12160i_VERSION_STRING "10.04.32"
+#else
+#define FW_VERSION_STRING "10.04.32"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+static unsigned short fw12160i_addr01 = 0x1000;
+#else
+static unsigned short risc_code_addr01 = 0x1000;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+static unsigned short fw12160i_code01[] = {
+#else
+static unsigned short risc_code01[] = {
+#endif
+ 0x0804, 0x1041, 0x0000, 0x35e6, 0x0000, 0x2043, 0x4f50, 0x5952,
+ 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932, 0x2c31,
+ 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749, 0x4320,
+ 0x434f, 0x5250, 0x4f52, 0x4154, 0x494f, 0x4e00, 0x2049, 0x5350,
+ 0x3132, 0x3136, 0x2046, 0x6972, 0x6d77, 0x6172, 0x6520, 0x2056,
+ 0x6572, 0x7369, 0x6f6e, 0x2031, 0x302e, 0x3034, 0x2020, 0x2043,
+ 0x7573, 0x746f, 0x6d65, 0x7220, 0x4e6f, 0x2e20, 0x3030, 0x2050,
+ 0x726f, 0x6475, 0x6374, 0x204e, 0x6f2e, 0x2020, 0x3030, 0x2020,
+ 0x2400, 0x20c9, 0x8fff, 0x2071, 0x0200, 0x70a0, 0x70a2, 0x2001,
+ 0x01ff, 0x2004, 0xd0fc, 0x1120, 0x2071, 0x0100, 0x70a0, 0x70a2,
+ 0x20c1, 0x0020, 0x2089, 0x1221, 0x2071, 0x0010, 0x70c3, 0x0004,
+ 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x000a,
+ 0x2001, 0x04fd, 0x2004, 0x70d6, 0x2009, 0xfeff, 0x2130, 0x2128,
+ 0xa1a2, 0x4600, 0x8424, 0x8424, 0x8424, 0x8424, 0x8424, 0x8424,
+ 0xa192, 0x9000, 0x2009, 0x0000, 0x2001, 0x0032, 0x080c, 0x1de8,
+ 0x2218, 0x2079, 0x4600, 0x2fa0, 0x2408, 0x2011, 0x0000, 0x20a9,
+ 0x0040, 0x42a4, 0x8109, 0x1dd8, 0x2009, 0xff00, 0x3400, 0xa102,
+ 0x0218, 0x0110, 0x20a8, 0x42a4, 0x781b, 0x0064, 0x7814, 0xc0cd,
+ 0xc0d5, 0x7816, 0x2071, 0x0200, 0x00d6, 0x2069, 0x4640, 0x080c,
+ 0x459a, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1130, 0x2069, 0x4680,
+ 0x2071, 0x0100, 0x080c, 0x459a, 0x7814, 0xc0d4, 0x7816, 0x00de,
+ 0x7eca, 0x7cc2, 0x7bc6, 0x7867, 0x0000, 0x7800, 0xc08d, 0x7802,
+ 0x2031, 0x0030, 0x78af, 0x0101, 0x7823, 0x0002, 0x7827, 0x0002,
+ 0x2009, 0x0002, 0x2069, 0x4640, 0x681b, 0x0003, 0x6823, 0x0007,
+ 0x6827, 0x00fa, 0x682b, 0x0008, 0x682f, 0x0028, 0x6837, 0x0006,
+ 0x6833, 0x0008, 0x683b, 0x0000, 0x8109, 0x0500, 0x68cf, 0x000a,
+ 0x68bf, 0x46c0, 0x2079, 0x4600, 0x68d3, 0x762d, 0x68c3, 0x4bc0,
+ 0x68c7, 0x4ac0, 0x68cb, 0x8bc0, 0x68a7, 0x8e44, 0x68ab, 0x8e49,
+ 0x68af, 0x8e44, 0x68b3, 0x8e44, 0x68a3, 0x0001, 0x2001, 0x01ff,
+ 0x2004, 0xd0fc, 0x11c8, 0x2069, 0x4680, 0x0870, 0x68cf, 0x000a,
+ 0x68bf, 0x48c0, 0x68d3, 0x7839, 0x68c3, 0x6bc0, 0x68c7, 0x4b40,
+ 0x68cb, 0x8cd0, 0x68a7, 0x8e49, 0x68ab, 0x8e4e, 0x68af, 0x8e49,
+ 0x68b3, 0x8e49, 0x68a3, 0x0001, 0x00e6, 0x2069, 0x4ac0, 0x2071,
+ 0x0200, 0x70ec, 0xd0e4, 0x2019, 0x1809, 0x2021, 0x0009, 0x1120,
+ 0x2019, 0x180c, 0x2021, 0x000c, 0x080c, 0x1d58, 0x2001, 0x01ff,
+ 0x2004, 0xd0fc, 0x1188, 0x2069, 0x4b40, 0x2071, 0x0100, 0x70ec,
+ 0xd0e4, 0x2019, 0x1809, 0x2021, 0x0009, 0x1120, 0x2019, 0x180c,
+ 0x2021, 0x000c, 0x080c, 0x1d58, 0x00ee, 0x2011, 0x0002, 0x2069,
+ 0x4bc0, 0x2009, 0x0002, 0x20a9, 0x0100, 0x6837, 0x0000, 0x680b,
+ 0x0040, 0x7bc8, 0xa386, 0xfeff, 0x1128, 0x6817, 0x0100, 0x681f,
+ 0x0064, 0x0020, 0x6817, 0x0064, 0x681f, 0x0002, 0xade8, 0x0010,
+ 0x1f04, 0x1135, 0x8109, 0x1d38, 0x2001, 0x01ff, 0x2004, 0xd0fc,
+ 0x1128, 0x8211, 0x0118, 0x2069, 0x6bc0, 0x08d8, 0x080c, 0x22cf,
+ 0x080c, 0x4015, 0x080c, 0x1b6d, 0x080c, 0x4553, 0x2091, 0x2200,
+ 0x2079, 0x4600, 0x2071, 0x0050, 0x2091, 0x2400, 0x2079, 0x4600,
+ 0x2071, 0x0020, 0x2091, 0x2600, 0x2079, 0x0200, 0x2071, 0x4640,
+ 0x2091, 0x2800, 0x2079, 0x0100, 0x2071, 0x4680, 0x2091, 0x2000,
+ 0x2079, 0x4600, 0x2071, 0x0010, 0x3200, 0xa085, 0x303d, 0x2090,
+ 0x2071, 0x0010, 0x70c3, 0x0000, 0x1004, 0x118c, 0x70c0, 0xa086,
+ 0x0002, 0x1110, 0x080c, 0x13ba, 0x2039, 0x0000, 0x080c, 0x12ab,
+ 0x78ac, 0xa005, 0x1180, 0x0e04, 0x119a, 0x786c, 0xa065, 0x0110,
+ 0x080c, 0x207a, 0x080c, 0x1e09, 0x0e04, 0x11af, 0x786c, 0xa065,
+ 0x0110, 0x080c, 0x207a, 0x0e04, 0x11af, 0x2009, 0x4647, 0x2011,
+ 0x4687, 0x2104, 0x220c, 0xa105, 0x0110, 0x080c, 0x1c7c, 0x2071,
+ 0x4640, 0x70a0, 0xa005, 0x01e8, 0x744c, 0xa485, 0x0000, 0x01c8,
+ 0x2079, 0x0200, 0x2091, 0x8000, 0x72d0, 0xa28c, 0x303d, 0x2190,
+ 0x080c, 0x2720, 0x2091, 0x8000, 0x2091, 0x303d, 0x0e04, 0x11d1,
+ 0x2079, 0x4600, 0x786c, 0xa065, 0x0120, 0x2071, 0x0010, 0x080c,
+ 0x207a, 0x1d04, 0x11d9, 0x2079, 0x4600, 0x2071, 0x0010, 0x080c,
+ 0x4370, 0x2071, 0x4680, 0x70a0, 0xa005, 0x0188, 0x704c, 0xa025,
+ 0x0170, 0x2079, 0x0100, 0x2091, 0x8000, 0x72d0, 0xa28c, 0x303d,
+ 0x2190, 0x080c, 0x2720, 0x2091, 0x8000, 0x2091, 0x303d, 0x2079,
+ 0x4600, 0x2071, 0x0010, 0x0e04, 0x11fa, 0x786c, 0xa065, 0x0110,
+ 0x080c, 0x207a, 0x1d04, 0x118e, 0x080c, 0x4370, 0x0804, 0x118e,
+ 0x3c00, 0xa084, 0x0007, 0x0002, 0x120c, 0x120c, 0x120e, 0x120e,
+ 0x1213, 0x1213, 0x1218, 0x1218, 0x080c, 0x254c, 0x2091, 0x2400,
+ 0x080c, 0x40ad, 0x0005, 0x2091, 0x2200, 0x080c, 0x40ad, 0x0005,
+ 0x2091, 0x2200, 0x080c, 0x40ad, 0x2091, 0x2400, 0x080c, 0x40ad,
+ 0x0005, 0x1241, 0x1241, 0x1242, 0x1242, 0x124d, 0x124d, 0x124d,
+ 0x124d, 0x1256, 0x1256, 0x1261, 0x1261, 0x124d, 0x124d, 0x124d,
+ 0x124d, 0x1270, 0x1270, 0x1270, 0x1270, 0x1270, 0x1270, 0x1270,
+ 0x1270, 0x1270, 0x1270, 0x1270, 0x1270, 0x1270, 0x1270, 0x1270,
+ 0x1270, 0x0cf8, 0x0006, 0x0106, 0x0126, 0x2091, 0x2800, 0x080c,
+ 0x2569, 0x012e, 0x010e, 0x000e, 0x000d, 0x0006, 0x0106, 0x0126,
+ 0x080c, 0x1200, 0x012e, 0x010e, 0x000e, 0x000d, 0x0006, 0x0106,
+ 0x0126, 0x2091, 0x2600, 0x080c, 0x2569, 0x012e, 0x010e, 0x000e,
+ 0x000d, 0x0006, 0x0106, 0x0126, 0x2091, 0x2600, 0x080c, 0x2569,
+ 0x2091, 0x2800, 0x080c, 0x2569, 0x012e, 0x010e, 0x000e, 0x000d,
+ 0x0006, 0x0106, 0x0126, 0x00d6, 0x00e6, 0x00f6, 0x2079, 0x4600,
+ 0x2071, 0x0200, 0x2069, 0x4640, 0x3d00, 0xd08c, 0x0130, 0x70ec,
+ 0xa084, 0x1c00, 0x78e2, 0x080c, 0x459a, 0x3d00, 0xd084, 0x0150,
+ 0x2069, 0x4680, 0x2071, 0x0100, 0x70ec, 0xa084, 0x1c00, 0x78e6,
+ 0x080c, 0x459a, 0x080c, 0x24fd, 0x00fe, 0x00ee, 0x00de, 0x012e,
+ 0x010e, 0x000e, 0x000d, 0x7008, 0x800b, 0x1240, 0x7007, 0x0002,
+ 0xa08c, 0x01e0, 0x1120, 0xd09c, 0x0108, 0x0887, 0x0897, 0x70c3,
+ 0x4002, 0x0804, 0x13bd, 0x0e04, 0x131e, 0x2061, 0x0000, 0x6018,
+ 0xd084, 0x1904, 0x131e, 0x7828, 0xa005, 0x1120, 0x0004, 0x131f,
+ 0x0804, 0x131e, 0xd0fc, 0x0130, 0x0006, 0x080c, 0x1b0a, 0x000e,
+ 0x0150, 0x0028, 0x0006, 0x080c, 0x1aff, 0x000e, 0x0120, 0x2001,
+ 0x4007, 0x0804, 0x13bc, 0x7910, 0xd0fc, 0x1128, 0x2061, 0x4640,
+ 0xc19c, 0xc7fc, 0x0020, 0x2061, 0x4680, 0xc19d, 0xc7fd, 0x6060,
+ 0xa005, 0x1904, 0x131e, 0x7912, 0x607e, 0x7828, 0xc0fc, 0xa086,
+ 0x0018, 0x1120, 0x00c6, 0x080c, 0x1916, 0x00ce, 0x782b, 0x0000,
+ 0x6078, 0xa065, 0x01e0, 0x00c6, 0x609c, 0x080c, 0x1bd4, 0x00ce,
+ 0x609f, 0x0000, 0x080c, 0x1a41, 0x2009, 0x0018, 0x6087, 0x0103,
+ 0x7810, 0x0006, 0x84ff, 0x1110, 0x85ff, 0x0110, 0xc0c5, 0x7812,
+ 0x080c, 0x1b15, 0x000e, 0x7812, 0x1198, 0x080c, 0x1b60, 0x7810,
+ 0xd09c, 0x1118, 0x2061, 0x4640, 0x0020, 0x2061, 0x4680, 0xc09c,
+ 0x7812, 0x607b, 0x0000, 0x60d0, 0xd0c4, 0x0130, 0xc0c4, 0x60d2,
+ 0x2001, 0x4005, 0x0804, 0x13bc, 0x0804, 0x13ba, 0x0005, 0xa006,
+ 0x70c2, 0x70c6, 0x70ca, 0x70ce, 0x70da, 0x70c0, 0xa03d, 0xa08a,
+ 0x0040, 0x1a04, 0x136c, 0x0002, 0x13ba, 0x1408, 0x13d6, 0x143c,
+ 0x1470, 0x1470, 0x13ce, 0x1a59, 0x147a, 0x13c8, 0x13da, 0x13db,
+ 0x13dc, 0x13dd, 0x1a5d, 0x13c8, 0x1487, 0x14db, 0x1931, 0x1a53,
+ 0x13de, 0x17ba, 0x17f0, 0x1822, 0x1868, 0x1777, 0x1784, 0x1797,
+ 0x17a9, 0x15b0, 0x13c8, 0x150d, 0x1518, 0x1526, 0x1534, 0x154b,
+ 0x1559, 0x155c, 0x156a, 0x1578, 0x1582, 0x1596, 0x15a2, 0x13c8,
+ 0x13c8, 0x13c8, 0x13c8, 0x15bd, 0x15ce, 0x15e8, 0x161c, 0x1645,
+ 0x1657, 0x165a, 0x1685, 0x16be, 0x16d0, 0x1745, 0x1755, 0x13c8,
+ 0x13c8, 0x13c8, 0x13c8, 0x1767, 0x2100, 0xa08a, 0x0040, 0x1a04,
+ 0x13c8, 0x0002, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x1a7f,
+ 0x1a85, 0x13c8, 0x13c8, 0x13c8, 0x1a89, 0x1ac9, 0x13c8, 0x13c8,
+ 0x13c8, 0x13c8, 0x1403, 0x146b, 0x1482, 0x14d6, 0x192c, 0x13c8,
+ 0x13c8, 0x18fb, 0x13c8, 0x1acd, 0x1a71, 0x1a7b, 0x13c8, 0x13c8,
+ 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8,
+ 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8,
+ 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8,
+ 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8, 0x13c8,
+ 0x13c8, 0x13c8, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0028, 0x73ce,
+ 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x0e04, 0x13bd, 0x2061,
+ 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x0005,
+ 0x70c3, 0x4001, 0x0c90, 0x70c3, 0x4006, 0x0c78, 0x2099, 0x0041,
+ 0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0c20, 0x70c4, 0x70c3,
+ 0x0004, 0x0807, 0x08f8, 0x08f0, 0x08e8, 0x08e0, 0x2091, 0x8000,
+ 0x70c3, 0x0004, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020,
+ 0x70d3, 0x000a, 0x2001, 0x0004, 0x70d6, 0x2079, 0x0000, 0x781b,
+ 0x0001, 0x2031, 0x0030, 0x2059, 0x1000, 0x2029, 0x041a, 0x2051,
+ 0x0445, 0x2061, 0x0447, 0x20c1, 0x0020, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x0804, 0x0418, 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0018,
+ 0x2029, 0x0000, 0x2520, 0x71d0, 0x72c8, 0x73cc, 0x70c4, 0x20a0,
+ 0x2099, 0x0030, 0x7003, 0x0001, 0x7007, 0x0006, 0x731a, 0x721e,
+ 0x7422, 0x7526, 0x2021, 0x0040, 0x81ff, 0x0904, 0x13ba, 0xa182,
+ 0x0040, 0x1210, 0x2120, 0xa006, 0x2008, 0x8403, 0x7012, 0x7007,
+ 0x0004, 0x7007, 0x0001, 0x7008, 0xd0fc, 0x0de8, 0x7007, 0x0002,
+ 0xa084, 0x01e0, 0x0120, 0x70c3, 0x4002, 0x0804, 0x13bd, 0x24a8,
+ 0x53a5, 0x0c10, 0x0804, 0x13ba, 0x2029, 0x0000, 0x2520, 0x71d0,
+ 0x72c8, 0x73cc, 0x70c4, 0x2098, 0x20a1, 0x0030, 0x7003, 0x0000,
+ 0x7007, 0x0006, 0x731a, 0x721e, 0x7422, 0x7526, 0x2021, 0x0040,
+ 0x7007, 0x0006, 0x81ff, 0x0904, 0x13ba, 0xa182, 0x0040, 0x1210,
+ 0x2120, 0xa006, 0x2008, 0x8403, 0x7012, 0x24a8, 0x53a6, 0x7007,
+ 0x0001, 0x7008, 0xd0fc, 0x0de8, 0xa084, 0x01e0, 0x0d48, 0x70c3,
+ 0x4002, 0x0804, 0x13bd, 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0878,
+ 0x71c4, 0x70c8, 0x2114, 0xa79e, 0x0004, 0x1108, 0x200a, 0x72ca,
+ 0x0804, 0x13b9, 0x70c7, 0x000a, 0x70cb, 0x0004, 0x70cf, 0x0020,
+ 0x0804, 0x13ba, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0018, 0x2029,
+ 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d0, 0x70c6, 0x72ca,
+ 0x73ce, 0x74d2, 0xa005, 0x05e8, 0xa40a, 0x0108, 0x1240, 0x8001,
+ 0x7872, 0xa084, 0xfc00, 0x0138, 0x78ac, 0xc085, 0x78ae, 0x2001,
+ 0x4005, 0x0804, 0x13bc, 0x7b7e, 0x7a7a, 0x7e86, 0x7d82, 0x7c76,
+ 0xa48c, 0xff00, 0x0170, 0x8407, 0x8004, 0x8004, 0x810c, 0x810c,
+ 0x810f, 0xa118, 0xa291, 0x0000, 0xa6b1, 0x0000, 0xa581, 0x0000,
+ 0x0050, 0x8407, 0x8004, 0x8004, 0xa318, 0xa291, 0x0000, 0xa6b1,
+ 0x0000, 0xa581, 0x0000, 0x731a, 0x721e, 0x7622, 0x7026, 0xa605,
+ 0x0118, 0x7a10, 0xc2c5, 0x7a12, 0x78ac, 0xa084, 0xfffc, 0x78ae,
+ 0x0018, 0x78ac, 0xc085, 0x78ae, 0x0804, 0x13ba, 0x75d8, 0x76dc,
+ 0x75da, 0x76de, 0x0018, 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8,
+ 0x73cc, 0x74d4, 0x70c6, 0x72ca, 0x73ce, 0x74d6, 0xa005, 0x0500,
+ 0xa40a, 0x0110, 0x1a04, 0x13bc, 0x8001, 0x7892, 0xa084, 0xfc00,
+ 0x0138, 0x78ac, 0xc0c5, 0x78ae, 0x2001, 0x4005, 0x0804, 0x13bc,
+ 0x7a9a, 0x7b9e, 0x7da2, 0x7ea6, 0x2600, 0xa505, 0x0118, 0x7a10,
+ 0xc2c5, 0x7a12, 0x7c96, 0x78ac, 0xa084, 0xfcff, 0x78ae, 0x0018,
+ 0x78ac, 0xc0c5, 0x78ae, 0x0804, 0x13ba, 0x2009, 0x0000, 0x786c,
+ 0xa065, 0x0118, 0x8108, 0x6000, 0x0cd8, 0x7ac4, 0x0804, 0x13b8,
+ 0x2009, 0x4648, 0x210c, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904,
+ 0x13b9, 0x2011, 0x4688, 0x2214, 0x0804, 0x13b8, 0x2009, 0x4649,
+ 0x210c, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904, 0x13b9, 0x2011,
+ 0x4689, 0x2214, 0x0804, 0x13b8, 0x2061, 0x4640, 0x6128, 0x622c,
+ 0x8214, 0x8214, 0x8214, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1148,
+ 0x2061, 0x4680, 0x6328, 0x73da, 0x632c, 0x831c, 0x831c, 0x831c,
+ 0x73de, 0x0804, 0x13b8, 0x2009, 0x464c, 0x210c, 0x2001, 0x01ff,
+ 0x2004, 0xd0fc, 0x1904, 0x13b9, 0x2011, 0x468c, 0x2214, 0x0804,
+ 0x13b8, 0x7918, 0x0804, 0x13b9, 0x2009, 0x0202, 0x210c, 0x2001,
+ 0x01ff, 0x2004, 0xd0fc, 0x1904, 0x13b9, 0x2011, 0x0102, 0x2214,
+ 0x0804, 0x13b8, 0x2009, 0x464d, 0x210c, 0x2001, 0x01ff, 0x2004,
+ 0xd0fc, 0x1904, 0x13b9, 0x2011, 0x468d, 0x2214, 0x0804, 0x13b8,
+ 0x7920, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904, 0x13b9, 0x7a24,
+ 0x0804, 0x13b8, 0x2011, 0x4b40, 0x71c4, 0xd1fc, 0x1110, 0x2011,
+ 0x4ac0, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa268,
+ 0x6a00, 0x6b08, 0x6c1c, 0x74da, 0x0804, 0x13b7, 0x77c4, 0x080c,
+ 0x1b7b, 0x2091, 0x8000, 0x6b1c, 0x6a14, 0x2091, 0x8001, 0x2708,
+ 0x0804, 0x13b7, 0x2061, 0x4640, 0x6118, 0x2001, 0x01ff, 0x2004,
+ 0xd0fc, 0x1904, 0x13b9, 0x2061, 0x4680, 0x6218, 0x0804, 0x13b8,
+ 0x77c4, 0x080c, 0x1b7b, 0x2091, 0x8000, 0x6908, 0x6a18, 0x6b10,
+ 0x77da, 0x2091, 0x8001, 0x0804, 0x13b7, 0x71c4, 0x2110, 0xa294,
+ 0x000f, 0xa282, 0x0010, 0x1a04, 0x13b3, 0x080c, 0x238b, 0xa384,
+ 0x4000, 0x0110, 0xa295, 0x0020, 0x0804, 0x13b7, 0x71c4, 0x2100,
+ 0xc0bc, 0xa082, 0x0010, 0x1a04, 0x13b3, 0xd1bc, 0x1120, 0x2011,
+ 0x4648, 0x2204, 0x0020, 0x2011, 0x4688, 0x2204, 0xc0bd, 0x0006,
+ 0x2100, 0xc0bc, 0x2012, 0x080c, 0x2331, 0x001e, 0x0804, 0x13b9,
+ 0x71c4, 0x2021, 0x4649, 0x2404, 0x70c6, 0x2019, 0x0000, 0x0030,
+ 0x71c8, 0x2021, 0x4689, 0x2404, 0x70ca, 0xc3fd, 0x2011, 0x1614,
+ 0x20a9, 0x0008, 0x2204, 0xa106, 0x0138, 0x8210, 0x1f04, 0x15fa,
+ 0x71c4, 0x72c8, 0x0804, 0x13b2, 0xa292, 0x1614, 0x0026, 0x2122,
+ 0x001e, 0x080c, 0x2343, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1110,
+ 0xd3fc, 0x09f0, 0x0804, 0x13ba, 0x03e8, 0x00fa, 0x01f4, 0x02ee,
+ 0x0004, 0x0001, 0x0002, 0x0003, 0x2061, 0x4640, 0x6128, 0x622c,
+ 0x8214, 0x8214, 0x8214, 0x70c4, 0x602a, 0x70c8, 0x8003, 0x8003,
+ 0x8003, 0x602e, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x11a0, 0x0026,
+ 0x0016, 0x2061, 0x4680, 0x6128, 0x622c, 0x8214, 0x8214, 0x8214,
+ 0x70d8, 0x602a, 0x70dc, 0x8003, 0x8003, 0x8003, 0x602e, 0x71da,
+ 0x72de, 0x001e, 0x002e, 0x0804, 0x13b8, 0x2061, 0x4640, 0x6130,
+ 0x70c4, 0x6032, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904, 0x13b9,
+ 0x2061, 0x4680, 0x6230, 0x70c8, 0x6032, 0x0804, 0x13b8, 0x7918,
+ 0x0804, 0x13b9, 0x71c4, 0xa184, 0xf0cf, 0x0148, 0x2001, 0x01ff,
+ 0x2004, 0xd0fc, 0x1904, 0x13b3, 0x72c8, 0x0804, 0x13b2, 0x0006,
+ 0x2019, 0x0000, 0x080c, 0x237f, 0x2001, 0x01ff, 0x2004, 0xd0fc,
+ 0x0118, 0x001e, 0x0804, 0x13b9, 0x71c8, 0xa184, 0xf0cf, 0x0128,
+ 0x0006, 0x2110, 0x71c4, 0x0804, 0x13b2, 0x0006, 0xc3fd, 0x080c,
+ 0x237f, 0x002e, 0x001e, 0x0804, 0x13b8, 0x71c4, 0xa182, 0x0010,
+ 0x0248, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904, 0x13b3, 0x72c8,
+ 0x0804, 0x13b2, 0x2011, 0x464d, 0x2204, 0x0006, 0x8104, 0x1208,
+ 0x8108, 0x2112, 0x2019, 0x0000, 0x080c, 0x236c, 0x2001, 0x01ff,
+ 0x2004, 0xd0fc, 0x0118, 0x001e, 0x0804, 0x13b9, 0x71c8, 0xa182,
+ 0x0010, 0x0228, 0x0006, 0x2110, 0x71c4, 0x0804, 0x13b2, 0x2011,
+ 0x468d, 0x2204, 0x0006, 0x8104, 0x1208, 0x8108, 0x2112, 0xc3fd,
+ 0x080c, 0x236c, 0x002e, 0x001e, 0x0804, 0x13b8, 0x71c4, 0x72c8,
+ 0xa184, 0xfffd, 0x1904, 0x13b2, 0xa284, 0xfffd, 0x1904, 0x13b2,
+ 0x2100, 0x7920, 0x7822, 0x2200, 0x7a24, 0x7826, 0x0804, 0x13b8,
+ 0x2011, 0x4b40, 0x71c4, 0xd1fc, 0x1110, 0x2011, 0x4ac0, 0x8107,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa268, 0x72c8, 0x73cc,
+ 0x74d8, 0x71c6, 0x6800, 0x70ca, 0x73ce, 0x74da, 0x2091, 0x8000,
+ 0x6a02, 0xd2ac, 0x1118, 0x2021, 0x0000, 0x0090, 0xa484, 0x00ff,
+ 0xa082, 0x0002, 0x1a04, 0x1741, 0x843f, 0xa7bc, 0x00ff, 0x0140,
+ 0xa786, 0x0002, 0x1904, 0x1741, 0xa484, 0x00ff, 0x0904, 0x1741,
+ 0x2061, 0x0200, 0xd1fc, 0x0110, 0x2061, 0x0100, 0x2029, 0x0009,
+ 0x2031, 0x0062, 0x843f, 0xa7bc, 0x00ff, 0x0130, 0x8307, 0xa084,
+ 0x00ff, 0x1110, 0xa73d, 0x1138, 0x2041, 0x0019, 0xa384, 0x00ff,
+ 0xa082, 0x001a, 0x0210, 0xa4a4, 0x00ff, 0x8307, 0xa084, 0x00ff,
+ 0x0188, 0xa842, 0x02f0, 0xa086, 0x0010, 0x1120, 0xa39c, 0x00ff,
+ 0xa39d, 0x0f00, 0xa3bc, 0x00ff, 0x2500, 0xa702, 0x0290, 0x2600,
+ 0xa702, 0x1278, 0x2039, 0x003a, 0x6804, 0xa705, 0x6806, 0x6b0a,
+ 0x6b0c, 0x73ce, 0x681c, 0x70da, 0x6c1e, 0x2091, 0x8001, 0x0804,
+ 0x13ba, 0x2091, 0x8001, 0x0804, 0x13b4, 0x77c4, 0x080c, 0x1b7b,
+ 0x2091, 0x8000, 0x6a14, 0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816,
+ 0x70cc, 0x681e, 0x2708, 0x0804, 0x13b7, 0x70c4, 0x2061, 0x4640,
+ 0x6118, 0x601a, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904, 0x13b9,
+ 0x70c8, 0x2061, 0x4680, 0x6218, 0x601a, 0x0804, 0x13b8, 0x71c4,
+ 0x72c8, 0x73cc, 0xa182, 0x0010, 0x1a04, 0x13b3, 0x080c, 0x23af,
+ 0xa384, 0x4000, 0x0110, 0xa295, 0x0020, 0x0804, 0x13b7, 0x77c4,
+ 0x080c, 0x1b7b, 0x2091, 0x8000, 0x6a08, 0xc28d, 0x6a0a, 0x2091,
+ 0x8001, 0x2708, 0x0804, 0x13b8, 0x77c4, 0x080c, 0x1b7b, 0x2091,
+ 0x8000, 0x6a08, 0xa294, 0xfff9, 0x6a0a, 0x6804, 0xa005, 0x0110,
+ 0x080c, 0x22ae, 0x2091, 0x8001, 0x2708, 0x0804, 0x13b8, 0x77c4,
+ 0x080c, 0x1b7b, 0x2091, 0x8000, 0x6a08, 0xc295, 0x6a0a, 0x6804,
+ 0xa005, 0x0110, 0x080c, 0x22ae, 0x2091, 0x8001, 0x2708, 0x0804,
+ 0x13b8, 0x77c4, 0x2041, 0x0001, 0x2049, 0x0005, 0x2051, 0x0020,
+ 0x2091, 0x8000, 0x080c, 0x1b93, 0x2091, 0x8001, 0x2708, 0x6a08,
+ 0x0804, 0x13b8, 0x77c4, 0xd7fc, 0x0128, 0x080c, 0x1b0a, 0x0138,
+ 0x0804, 0x13bc, 0x080c, 0x1aff, 0x0110, 0x0804, 0x13bc, 0x73c8,
+ 0x72cc, 0x77c6, 0x73ca, 0x72ce, 0x080c, 0x1c0b, 0x11e8, 0x6818,
+ 0xa005, 0x01a0, 0x2708, 0x0076, 0x080c, 0x23ce, 0x007e, 0x1170,
+ 0x2001, 0x0015, 0xd7fc, 0x1118, 0x2061, 0x4640, 0x0018, 0xc0fd,
+ 0x2061, 0x4680, 0x782a, 0x2091, 0x8001, 0x0005, 0x2091, 0x8001,
+ 0x2001, 0x4005, 0x0804, 0x13bc, 0x2091, 0x8001, 0x0804, 0x13ba,
+ 0x77c4, 0xd7fc, 0x0128, 0x080c, 0x1b0a, 0x0138, 0x0804, 0x13bc,
+ 0x080c, 0x1aff, 0x0110, 0x0804, 0x13bc, 0x77c6, 0x2041, 0x0021,
+ 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x080c, 0x1b93,
+ 0x2009, 0x0016, 0xd7fc, 0x1118, 0x2061, 0x4640, 0x0018, 0x2061,
+ 0x4680, 0xc1fd, 0x6063, 0x0003, 0x607b, 0x0000, 0x6772, 0x607f,
+ 0x000f, 0x792a, 0x61d0, 0xc1c4, 0x61d2, 0x080c, 0x22ae, 0x2091,
+ 0x8001, 0x0005, 0x77c8, 0x77ca, 0x77c4, 0x77c6, 0xd7fc, 0x0128,
+ 0x080c, 0x1b0a, 0x0138, 0x0804, 0x13bc, 0x080c, 0x1aff, 0x0110,
+ 0x0804, 0x13bc, 0xa7bc, 0xff00, 0x2091, 0x8000, 0x2009, 0x0017,
+ 0xd7fc, 0x1118, 0x2061, 0x4640, 0x0018, 0x2061, 0x4680, 0xc1fd,
+ 0x607b, 0x0000, 0x6063, 0x0002, 0x6772, 0x607f, 0x000f, 0x792a,
+ 0x61d0, 0xc1c4, 0x61d2, 0x080c, 0x22ae, 0x2091, 0x8001, 0x2041,
+ 0x0021, 0x2049, 0x0005, 0x2051, 0x0030, 0x2091, 0x8000, 0x70c8,
+ 0xa005, 0x0118, 0x60d0, 0xc0fd, 0x60d2, 0x080c, 0x1b93, 0x70c8,
+ 0x6836, 0x8738, 0xa784, 0x001f, 0x1dc0, 0x2091, 0x8001, 0x0005,
+ 0x2019, 0x0000, 0x72c8, 0xd284, 0x0128, 0x080c, 0x1b0a, 0x0138,
+ 0x0804, 0x13bc, 0x080c, 0x1aff, 0x0110, 0x0804, 0x13bc, 0x72c8,
+ 0x72ca, 0x78ac, 0xa084, 0x0003, 0x1508, 0x2039, 0x0000, 0xd284,
+ 0x0108, 0xc7fd, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008,
+ 0x080c, 0x1b7b, 0x2091, 0x8000, 0x6808, 0xc0d4, 0xa80d, 0x690a,
+ 0x2091, 0x8001, 0x8738, 0xa784, 0x001f, 0x1d90, 0xa7bc, 0xff00,
+ 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x1d50, 0x2091, 0x8000,
+ 0x72c8, 0x2069, 0x0100, 0xd284, 0x1110, 0x2069, 0x0200, 0x6808,
+ 0xa084, 0xfffd, 0x680a, 0x6830, 0xd0b4, 0x01b0, 0x684b, 0x0004,
+ 0x20a9, 0x0014, 0x6848, 0xd094, 0x0110, 0x1f04, 0x18b2, 0x684b,
+ 0x0009, 0x20a9, 0x0014, 0x6848, 0xd084, 0x0110, 0x1f04, 0x18bb,
+ 0x20a9, 0x00fa, 0x1f04, 0x18c2, 0x2079, 0x4600, 0x2009, 0x0018,
+ 0x72c8, 0xd284, 0x1118, 0x2061, 0x4640, 0x0018, 0x2061, 0x4680,
+ 0xc1fd, 0x607b, 0x0000, 0x792a, 0x6063, 0x0001, 0x607f, 0x000f,
+ 0x60a3, 0x0000, 0x60a4, 0x60ae, 0x60b2, 0x60d0, 0xd0b4, 0x0160,
+ 0xc0b4, 0x60d2, 0x00c6, 0x60b4, 0xa065, 0x6008, 0xc0d4, 0x600a,
+ 0x6018, 0x8001, 0x601a, 0x00ce, 0x60d0, 0xa084, 0x7eff, 0x60d2,
+ 0x78ac, 0xc08d, 0x78ae, 0x83ff, 0x0108, 0x0005, 0x681b, 0x0054,
+ 0x2091, 0x8001, 0x0005, 0x73cc, 0x080c, 0x186a, 0x69ec, 0x6a48,
+ 0xa185, 0x1800, 0x684a, 0xa185, 0x0040, 0x68ee, 0x73cc, 0x2021,
+ 0x0004, 0x20a9, 0x09ff, 0x1f04, 0x190b, 0x8421, 0x1dd0, 0x8319,
+ 0x1db0, 0x69ee, 0x6a4a, 0x2091, 0x8001, 0x0005, 0xd7fc, 0x1118,
+ 0x2069, 0x4640, 0x0010, 0x2069, 0x4680, 0x71c4, 0x71c6, 0x6916,
+ 0x81ff, 0x1110, 0x68a3, 0x0001, 0x78ac, 0xc08c, 0x78ae, 0xd084,
+ 0x1110, 0x080c, 0x1c5b, 0x0005, 0x75d8, 0x74dc, 0x75da, 0x74de,
+ 0x0010, 0xa02e, 0x2520, 0x71c4, 0x73c8, 0x72cc, 0x71c6, 0x73ca,
+ 0x72ce, 0x2079, 0x4600, 0x7dde, 0x7cda, 0x7bd6, 0x7ad2, 0x080c,
+ 0x1b58, 0x0904, 0x1a3d, 0x20a9, 0x0005, 0x20a1, 0x4614, 0x2091,
+ 0x8000, 0x41a1, 0x2091, 0x8001, 0x2009, 0x0040, 0x080c, 0x1d24,
+ 0x0120, 0x080c, 0x1b60, 0x0804, 0x1a3d, 0x6004, 0xa08c, 0x00ff,
+ 0xa18e, 0x0009, 0x1120, 0x0006, 0x080c, 0x205f, 0x000e, 0xa084,
+ 0xff00, 0x8007, 0x8009, 0x0904, 0x19e1, 0x00c6, 0x2c68, 0x080c,
+ 0x1b58, 0x05a8, 0x2c00, 0x689e, 0x8109, 0x1dc0, 0x609f, 0x0000,
+ 0x00ce, 0x00c6, 0x7ddc, 0x7cd8, 0x7bd4, 0x7ad0, 0xa290, 0x0040,
+ 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x7dde, 0x7cda,
+ 0x7bd6, 0x7ad2, 0x2c68, 0x689c, 0xa065, 0x0904, 0x19e0, 0x2009,
+ 0x0040, 0x080c, 0x1d24, 0x15a0, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0002, 0x1168, 0x6004, 0xa084, 0x00ff, 0xa086, 0x000a, 0x1120,
+ 0x0016, 0x080c, 0x205c, 0x001e, 0x2d00, 0x6002, 0x0898, 0x00ce,
+ 0x00c6, 0x609c, 0x080c, 0x1bd4, 0x00ce, 0x609f, 0x0000, 0x080c,
+ 0x1a41, 0x2009, 0x0018, 0x6008, 0xc0cd, 0x600a, 0x6004, 0x6086,
+ 0x7810, 0x0006, 0x84ff, 0x1110, 0x85ff, 0x0110, 0xc0c5, 0x7812,
+ 0x080c, 0x1b15, 0x000e, 0x7812, 0x080c, 0x1b60, 0x0804, 0x1a3d,
+ 0x00ce, 0x00c6, 0x609c, 0x080c, 0x1bd4, 0x00ce, 0x609f, 0x0000,
+ 0x080c, 0x1a41, 0x2009, 0x0018, 0x6087, 0x0103, 0x601b, 0x0003,
+ 0x7810, 0x0006, 0x84ff, 0x1110, 0x85ff, 0x0110, 0xc0c5, 0x7812,
+ 0x080c, 0x1b15, 0x000e, 0x7812, 0x080c, 0x1b60, 0x0804, 0x1a3d,
+ 0x00ce, 0x6114, 0xd1fc, 0x0120, 0x080c, 0x1b0a, 0x01f0, 0x0018,
+ 0x080c, 0x1aff, 0x01d0, 0x080c, 0x1a41, 0x2009, 0x0018, 0x6087,
+ 0x0103, 0x601b, 0x0021, 0x7810, 0x0006, 0x84ff, 0x1110, 0x85ff,
+ 0x0110, 0xc0c5, 0x7812, 0x080c, 0x1b15, 0x000e, 0x7812, 0x080c,
+ 0x1b60, 0x2001, 0x4007, 0x0804, 0x13bc, 0x74c4, 0x73c8, 0x72cc,
+ 0x6014, 0x2091, 0x8000, 0x00e6, 0x2009, 0x0012, 0xd0fc, 0x1118,
+ 0x2071, 0x4640, 0x0018, 0x2071, 0x4680, 0xc1fd, 0x792a, 0x7063,
+ 0x0005, 0x71d0, 0xc1c4, 0x71d2, 0x7366, 0x726a, 0x746e, 0x7072,
+ 0x7077, 0x0000, 0x2c00, 0x707a, 0xa02e, 0x2530, 0x611c, 0xa184,
+ 0x0060, 0x0110, 0x080c, 0x3fc1, 0x00ee, 0x6596, 0x65a6, 0x669a,
+ 0x66aa, 0x60af, 0x0000, 0x60b3, 0x0000, 0x6714, 0x6023, 0x0000,
+ 0x080c, 0x22ae, 0x2091, 0x8001, 0x0005, 0x70c3, 0x4005, 0x0804,
+ 0x13bd, 0x20a9, 0x0005, 0x2099, 0x4614, 0x2091, 0x8000, 0x530a,
+ 0x2091, 0x8001, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000,
+ 0xa5a9, 0x0000, 0x0005, 0x71c4, 0x70c7, 0x0000, 0x791e, 0x0804,
+ 0x13ba, 0x71c4, 0x71c6, 0x2168, 0x0010, 0x2069, 0x1000, 0x690c,
+ 0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x1dd8, 0xa285, 0x0000,
+ 0x1118, 0x70c3, 0x4000, 0x0010, 0x70c3, 0x4003, 0x70ca, 0x0804,
+ 0x13bd, 0x7964, 0x71c6, 0x71c4, 0xa182, 0x0003, 0x1a04, 0x13b3,
+ 0x7966, 0x0804, 0x13ba, 0x7964, 0x71c6, 0x0804, 0x13ba, 0x7900,
+ 0x71c6, 0x71c4, 0x7902, 0x0804, 0x13ba, 0x7900, 0x71c6, 0x0804,
+ 0x13ba, 0x70c4, 0x2011, 0x0000, 0xa08c, 0x000d, 0x0160, 0x810c,
+ 0x0230, 0x8210, 0x810c, 0x810c, 0x0210, 0x8210, 0x810c, 0x81ff,
+ 0x1904, 0x13b4, 0x8210, 0x7a0e, 0xd28c, 0x0538, 0x7910, 0xc1cd,
+ 0x7912, 0x2009, 0x0021, 0x2019, 0x0003, 0xd284, 0x01c0, 0x8108,
+ 0x2019, 0x0041, 0x2011, 0x8e4e, 0x2312, 0x2019, 0x0042, 0x8210,
+ 0x2312, 0x2019, 0x0043, 0x8210, 0x2312, 0x2019, 0x0046, 0x8210,
+ 0x2312, 0x2019, 0x0047, 0x8210, 0x2312, 0x2019, 0x0006, 0x2011,
+ 0x8e53, 0x2112, 0x2011, 0x8e73, 0x2312, 0x7904, 0x7806, 0x0804,
+ 0x13b9, 0x7804, 0x70c6, 0x0804, 0x13ba, 0x71c4, 0xd1fc, 0x1118,
+ 0x2011, 0x4ac0, 0x0010, 0x2011, 0x4b40, 0x8107, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa268, 0x2011, 0x0000, 0x6814, 0xd0fc,
+ 0x0110, 0xa295, 0x0200, 0xd0b4, 0x0110, 0xa295, 0x0001, 0x6b0c,
+ 0x6800, 0x70da, 0x0804, 0x13b7, 0x7814, 0xd0f4, 0x0130, 0x2001,
+ 0x4007, 0x70db, 0x0000, 0xa005, 0x0048, 0xd0fc, 0x0130, 0x2001,
+ 0x4007, 0x70db, 0x0001, 0xa005, 0x0008, 0xa006, 0x0005, 0x7814,
+ 0xd0f4, 0x0130, 0x2001, 0x4007, 0x70db, 0x0000, 0xa005, 0x0008,
+ 0xa006, 0x0005, 0x7814, 0xd0fc, 0x0130, 0x2001, 0x4007, 0x70db,
+ 0x0001, 0xa005, 0x0008, 0xa006, 0x0005, 0x7112, 0x721a, 0x731e,
+ 0x7810, 0xd0c4, 0x0110, 0x7422, 0x7526, 0xac80, 0x0001, 0x8108,
+ 0x810c, 0x81a9, 0x8098, 0x20a1, 0x0030, 0x7003, 0x0000, 0x6084,
+ 0x20a2, 0x53a6, 0x7007, 0x0001, 0x7974, 0xa184, 0xff00, 0x0140,
+ 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007, 0xa100, 0x0018,
+ 0x8107, 0x8004, 0x8004, 0x797c, 0xa108, 0x7a78, 0xa006, 0xa211,
+ 0x7d10, 0xd5c4, 0x0120, 0x7b84, 0xa319, 0x7c80, 0xa421, 0x7008,
+ 0xd0fc, 0x0de8, 0x7003, 0x0001, 0x7007, 0x0006, 0x711a, 0x721e,
+ 0x7d10, 0xd5c4, 0x0110, 0x7322, 0x7426, 0xa084, 0x01e0, 0x0005,
+ 0x7848, 0xa065, 0x0120, 0x2c04, 0x784a, 0x2063, 0x0000, 0x0005,
+ 0x00f6, 0x2079, 0x4600, 0x7848, 0x2062, 0x2c00, 0xa005, 0x1110,
+ 0x080c, 0x254c, 0x784a, 0x00fe, 0x0005, 0x2011, 0x9000, 0x7a4a,
+ 0x7bc4, 0x8319, 0x0128, 0xa280, 0x0032, 0x2012, 0x2010, 0x0cc8,
+ 0x2013, 0x0000, 0x0005, 0x0016, 0x0026, 0xd7fc, 0x1118, 0x2011,
+ 0x4bc0, 0x0010, 0x2011, 0x6bc0, 0xa784, 0x0f00, 0x800b, 0xa784,
+ 0x001f, 0x0120, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa268,
+ 0x002e, 0x001e, 0x0005, 0x0c39, 0x2900, 0x682a, 0x2a00, 0x682e,
+ 0x6808, 0xa084, 0xf9ef, 0xa80d, 0x690a, 0x00e6, 0xd7fc, 0x1128,
+ 0x2009, 0x4652, 0x2071, 0x4640, 0x0020, 0x2009, 0x4692, 0x2071,
+ 0x4680, 0x210c, 0x6804, 0xa005, 0x0148, 0xa116, 0x1138, 0x2060,
+ 0x6000, 0x6806, 0x0016, 0x200b, 0x0000, 0x0018, 0x2009, 0x0000,
+ 0x0016, 0x6804, 0xa065, 0x0178, 0x6000, 0x6806, 0x0421, 0x080c,
+ 0x1d95, 0x6810, 0x7908, 0x8109, 0x790a, 0x8001, 0x6812, 0x1d88,
+ 0x7910, 0xc1a5, 0x7912, 0x001e, 0x6902, 0x6906, 0x2d00, 0x2060,
+ 0x080c, 0x2693, 0x00ee, 0x0005, 0xa065, 0x0160, 0x2008, 0x609c,
+ 0xa005, 0x0128, 0x2062, 0x609f, 0x0000, 0xa065, 0x0cc0, 0x7848,
+ 0x794a, 0x2062, 0x0005, 0x6007, 0x0103, 0x608f, 0x0000, 0x20a9,
+ 0x001c, 0xac80, 0x0005, 0x20a0, 0x2001, 0x0000, 0x40a4, 0x6828,
+ 0x601a, 0x682c, 0x6022, 0x0005, 0x00e6, 0xd7fc, 0x1128, 0x2071,
+ 0x4640, 0x2031, 0x46c0, 0x0020, 0x2071, 0x4680, 0x2031, 0x48c0,
+ 0x704c, 0xa08c, 0x0200, 0x1128, 0xa608, 0x2d0a, 0x8000, 0x704e,
+ 0xa006, 0x00ee, 0x0005, 0x00f6, 0xd7fc, 0x1118, 0x2079, 0x4640,
+ 0x0010, 0x2079, 0x4680, 0x080c, 0x1b7b, 0x2091, 0x8000, 0x6804,
+ 0x780a, 0xa065, 0x05f0, 0x0030, 0x2c00, 0x780a, 0x2060, 0x6000,
+ 0xa065, 0x05b8, 0x6010, 0xa306, 0x1db8, 0x600c, 0xa206, 0x1da0,
+ 0x2c28, 0x7848, 0xac06, 0x1108, 0x0448, 0x6804, 0xac06, 0x1140,
+ 0x6000, 0x2060, 0x6806, 0xa005, 0x1118, 0x6803, 0x0000, 0x0048,
+ 0x6400, 0x7808, 0x2060, 0x6402, 0xa486, 0x0000, 0x1110, 0x2c00,
+ 0x6802, 0x2560, 0x080c, 0x1be3, 0x601b, 0x0005, 0x6023, 0x0020,
+ 0x00fe, 0x080c, 0x1d95, 0x00f6, 0x7908, 0x8109, 0x790a, 0x6810,
+ 0x8001, 0x6812, 0x1118, 0x7810, 0xc0a5, 0x7812, 0x2001, 0xffff,
+ 0xa005, 0x00fe, 0x0005, 0x0076, 0x2700, 0x2039, 0x0000, 0xd0fc,
+ 0x0108, 0xc7fd, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008,
+ 0x2091, 0x8000, 0x080c, 0x1b93, 0x8738, 0xa784, 0x001f, 0x1dd0,
+ 0xa7bc, 0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x1d90,
+ 0x2091, 0x8001, 0x007e, 0x0005, 0x786c, 0x2009, 0x8e74, 0x210c,
+ 0xa10d, 0x0118, 0xa065, 0x0804, 0x207a, 0x2061, 0x0000, 0x6018,
+ 0xd084, 0x11b8, 0x7810, 0xd08c, 0x0130, 0xc08c, 0x7812, 0xc7fc,
+ 0x2069, 0x4640, 0x0028, 0xc08d, 0x7812, 0x2069, 0x4680, 0xc7fd,
+ 0x2091, 0x8000, 0x681c, 0x681f, 0x0000, 0x2091, 0x8001, 0xa005,
+ 0x1108, 0x0005, 0xa08c, 0xfff0, 0x0110, 0x080c, 0x254c, 0x0002,
+ 0x1cb8, 0x1cbb, 0x1cc1, 0x1cc5, 0x1cb9, 0x1cc9, 0x1cb9, 0x1cb9,
+ 0x1cb9, 0x1ccf, 0x1cfb, 0x1cfe, 0x1d03, 0x1d0c, 0x1cb9, 0x1cb9,
+ 0x0005, 0x080c, 0x254c, 0x080c, 0x1c5b, 0x2001, 0x8001, 0x0804,
+ 0x1d15, 0x2001, 0x8003, 0x0804, 0x1d15, 0x2001, 0x8004, 0x0804,
+ 0x1d15, 0x080c, 0x1c5b, 0x2001, 0x8006, 0x0804, 0x1d15, 0x2091,
+ 0x8000, 0x0076, 0xd7fc, 0x1128, 0x2069, 0x4640, 0x2039, 0x0009,
+ 0x0020, 0x2069, 0x4680, 0x2039, 0x0009, 0x6800, 0xa086, 0x0000,
+ 0x0128, 0x000e, 0x6f1e, 0x2091, 0x8001, 0x0005, 0x6870, 0x007e,
+ 0xa0bc, 0xff00, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010,
+ 0x080c, 0x1b93, 0x8738, 0xa784, 0x001f, 0x1dd0, 0x2091, 0x8001,
+ 0x2001, 0x800a, 0x00d0, 0x2001, 0x800c, 0x00b8, 0x080c, 0x1c5b,
+ 0x2001, 0x800d, 0x0090, 0xd7fc, 0x0110, 0x78e4, 0x0008, 0x78e0,
+ 0x70c6, 0x2001, 0x800e, 0x0048, 0xd7fc, 0x0110, 0x78ec, 0x0008,
+ 0x78e8, 0x70c6, 0x2001, 0x800f, 0x0000, 0x70c2, 0xd7fc, 0x1118,
+ 0x70db, 0x0000, 0x0010, 0x70db, 0x0001, 0x2061, 0x0000, 0x601b,
+ 0x0001, 0x2091, 0x4080, 0x0005, 0xac80, 0x0001, 0x81ff, 0x0518,
+ 0x2099, 0x0030, 0x20a0, 0x700c, 0xa084, 0x07ff, 0x0100, 0x7018,
+ 0x0006, 0x701c, 0x0006, 0x7020, 0x0006, 0x7024, 0x0006, 0x7112,
+ 0x81ac, 0x721a, 0x731e, 0x7422, 0x7526, 0x7003, 0x0001, 0x7007,
+ 0x0001, 0x7008, 0x800b, 0x1ee8, 0x7007, 0x0002, 0xa08c, 0x01e0,
+ 0x1110, 0x53a5, 0xa006, 0x7003, 0x0000, 0x7007, 0x0004, 0x000e,
+ 0x7026, 0x000e, 0x7022, 0x000e, 0x701e, 0x000e, 0x701a, 0x0005,
+ 0x2011, 0x0020, 0x2009, 0x0010, 0x6b0a, 0x6c0e, 0x681f, 0x0201,
+ 0x6803, 0xfd20, 0x6807, 0x0038, 0x6a1a, 0x2d00, 0xa0e8, 0x0008,
+ 0xa290, 0x0004, 0x8109, 0x1d80, 0x0005, 0x70ec, 0xd0dc, 0x1520,
+ 0x2029, 0x0001, 0x7814, 0xd0cc, 0x1160, 0x70ec, 0xd0e4, 0x2019,
+ 0x0c0a, 0x2021, 0x000a, 0x1120, 0x2019, 0x0c0c, 0x2021, 0x000c,
+ 0x0070, 0x70ec, 0xd0e4, 0x1128, 0x2019, 0x180c, 0x2021, 0x000c,
+ 0x0030, 0x2019, 0x1809, 0x2021, 0x0009, 0xa5ad, 0x0200, 0x6b0a,
+ 0x6c0e, 0x6d1e, 0x6807, 0x0038, 0x0005, 0x6004, 0x6086, 0x2c08,
+ 0x2063, 0x0000, 0x7868, 0xa005, 0x796a, 0x0110, 0x2c02, 0x0008,
+ 0x796e, 0x0005, 0x00c6, 0x2061, 0x4600, 0x6887, 0x0103, 0x2d08,
+ 0x206b, 0x0000, 0x6068, 0xa005, 0x616a, 0x0110, 0x2d02, 0x0008,
+ 0x616e, 0x00ce, 0x0005, 0x2091, 0x8000, 0x2c04, 0x786e, 0xa005,
+ 0x1108, 0x786a, 0x2091, 0x8001, 0x609c, 0xa005, 0x0188, 0x00c6,
+ 0x2060, 0x2008, 0x609c, 0xa005, 0x0138, 0x2062, 0x609f, 0x0000,
+ 0xa065, 0x609c, 0xa005, 0x1dc8, 0x7848, 0x794a, 0x2062, 0x00ce,
+ 0x7848, 0x2062, 0x609f, 0x0000, 0xac85, 0x0000, 0x1110, 0x080c,
+ 0x254c, 0x784a, 0x0005, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086,
+ 0x818e, 0x1208, 0xa200, 0x1f04, 0x1ddf, 0x8086, 0x818e, 0x0005,
+ 0x0156, 0x20a9, 0x0010, 0xa005, 0x01b8, 0xa11a, 0x12a8, 0x8213,
+ 0x818d, 0x0228, 0xa11a, 0x1220, 0x1f04, 0x1def, 0x0028, 0xa11a,
+ 0x2308, 0x8210, 0x1f04, 0x1def, 0x0006, 0x3200, 0xa084, 0xefff,
+ 0x2080, 0x000e, 0x015e, 0x0005, 0x0006, 0x3200, 0xa085, 0x1000,
+ 0x0cb8, 0x7d74, 0x70d0, 0xa506, 0x0904, 0x1ebd, 0x7810, 0x2050,
+ 0x080c, 0x1b58, 0x0904, 0x1ebd, 0xa046, 0x7970, 0x2500, 0x8000,
+ 0xa112, 0x2009, 0x0040, 0x1208, 0x0030, 0x72d0, 0xa206, 0x0118,
+ 0x8840, 0x2009, 0x0080, 0x00c6, 0x7112, 0x7007, 0x0001, 0x2099,
+ 0x0030, 0x20a9, 0x0020, 0xac80, 0x0001, 0x20a0, 0x2061, 0x0000,
+ 0x88ff, 0x0110, 0x080c, 0x1b58, 0x7008, 0xd0fc, 0x0de8, 0x7007,
+ 0x0002, 0x2091, 0x8001, 0xa08c, 0x01e0, 0x1538, 0x53a5, 0x8cff,
+ 0x1120, 0x88ff, 0x0904, 0x1eaa, 0x0050, 0x2c00, 0x788e, 0x20a9,
+ 0x0020, 0xac80, 0x0001, 0x20a0, 0x53a5, 0x0804, 0x1eaa, 0xa046,
+ 0x7218, 0x731c, 0xdac4, 0x0110, 0x7420, 0x7524, 0xa292, 0x0040,
+ 0xa39b, 0x0000, 0xa4a3, 0x0000, 0xa5ab, 0x0000, 0x721a, 0x731e,
+ 0xdac4, 0x0118, 0x7422, 0x7526, 0xa006, 0x7007, 0x0004, 0x0904,
+ 0x1eaa, 0x8cff, 0x0110, 0x080c, 0x1b60, 0x00ce, 0x080c, 0x1b60,
+ 0xa046, 0x7888, 0x8000, 0x788a, 0xa086, 0x0002, 0x01c0, 0x7a7c,
+ 0x7b78, 0xdac4, 0x0110, 0x7c84, 0x7d80, 0x7974, 0x8107, 0x8004,
+ 0x8004, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000,
+ 0x721a, 0x731e, 0xdac4, 0x0588, 0x7422, 0x7526, 0x0470, 0x6014,
+ 0xd0fc, 0x1118, 0x2069, 0x4640, 0x0010, 0x2069, 0x4680, 0x2091,
+ 0x8000, 0x681f, 0x0002, 0x88ff, 0x0120, 0xa046, 0x788c, 0x2060,
+ 0x0c70, 0x788b, 0x0000, 0x78ac, 0xa085, 0x0003, 0x78ae, 0x2091,
+ 0x8001, 0x0098, 0x00ce, 0x788b, 0x0000, 0x080c, 0x2035, 0x6004,
+ 0xa084, 0x000f, 0x0059, 0x88ff, 0x0130, 0x788c, 0x2060, 0x6004,
+ 0xa084, 0x000f, 0x0019, 0x0804, 0x1e09, 0x0005, 0x0002, 0x1ecf,
+ 0x1eea, 0x1f03, 0x1ecf, 0x1f10, 0x1ee0, 0x1ecf, 0x1ecf, 0x1ecf,
+ 0x1ee8, 0x1f01, 0x1ecf, 0x1ecf, 0x1ecf, 0x1ecf, 0x1ecf, 0x2039,
+ 0x0400, 0x78bc, 0xa705, 0x78be, 0x6008, 0xa705, 0x600a, 0x080c,
+ 0x1f4c, 0x609c, 0x78ba, 0x609f, 0x0000, 0x080c, 0x2021, 0x0005,
+ 0x78bc, 0xd0c4, 0x0108, 0x0c58, 0x601c, 0xc0bd, 0x601e, 0x0030,
+ 0x080c, 0x205f, 0x78bc, 0xd0c4, 0x0108, 0x0c08, 0x78bf, 0x0000,
+ 0x6004, 0x8007, 0xa084, 0x00ff, 0x78b2, 0x8001, 0x0138, 0x080c,
+ 0x1f4c, 0x0120, 0x78bc, 0xc0c5, 0x78be, 0x0010, 0x0804, 0x1f67,
+ 0x0005, 0x080c, 0x205c, 0x78bc, 0xa08c, 0x0e00, 0x1110, 0xd0c4,
+ 0x1108, 0x0828, 0x080c, 0x1f4c, 0x1110, 0x0804, 0x1f67, 0x0005,
+ 0x78bc, 0xd0c4, 0x0110, 0x0804, 0x1ecf, 0x78bf, 0x0000, 0x6714,
+ 0x2011, 0x0001, 0x22a8, 0x6018, 0xa084, 0x00ff, 0xa005, 0x0188,
+ 0xa7bc, 0xff00, 0x20a9, 0x0020, 0xa08e, 0x0001, 0x0150, 0xa7bc,
+ 0x8000, 0x2011, 0x0002, 0x20a9, 0x0100, 0xa08e, 0x0002, 0x0108,
+ 0x00c0, 0x080c, 0x1b7b, 0x2d00, 0x2091, 0x8000, 0x682b, 0x0000,
+ 0x682f, 0x0000, 0x6808, 0xa084, 0xffde, 0x680a, 0xade8, 0x0010,
+ 0x2091, 0x8001, 0x1f04, 0x1f34, 0x8211, 0x0118, 0x20a9, 0x0100,
+ 0x0c58, 0x080c, 0x1b60, 0x0005, 0x609f, 0x0000, 0x78b4, 0xa06d,
+ 0x2c00, 0x78b6, 0x1110, 0x78ba, 0x0038, 0x689e, 0x2d00, 0x6002,
+ 0x78b8, 0xad06, 0x1108, 0x6002, 0x78b0, 0x8001, 0x78b2, 0x1130,
+ 0x78bc, 0xc0c4, 0x78be, 0x78b8, 0x2060, 0xa006, 0x0005, 0x00e6,
+ 0xa02e, 0x2530, 0x7dba, 0x7db6, 0x65ae, 0x65b2, 0x601c, 0x60a2,
+ 0x2048, 0xa984, 0xe1ff, 0x601e, 0xa984, 0x0060, 0x0110, 0x080c,
+ 0x3fc1, 0x6596, 0x65a6, 0x669a, 0x66aa, 0x6714, 0x2071, 0x4680,
+ 0xd7fc, 0x1110, 0x2071, 0x4640, 0xa784, 0x0f00, 0x800b, 0xa784,
+ 0x001f, 0x0120, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0x71c0,
+ 0xa168, 0x2700, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+ 0x71c4, 0xa100, 0x60c2, 0x2091, 0x8000, 0x7814, 0xd0c4, 0x0138,
+ 0xd7fc, 0x1118, 0xd0f4, 0x1140, 0x0010, 0xd0fc, 0x1128, 0x6e08,
+ 0xd684, 0x01f0, 0xd9fc, 0x11e0, 0x2091, 0x8001, 0x080c, 0x1be3,
+ 0x2091, 0x8000, 0x080c, 0x1d95, 0x2091, 0x8001, 0x7814, 0xd0c4,
+ 0x0904, 0x201f, 0xd7fc, 0x1120, 0xd0f4, 0x1130, 0x0804, 0x201f,
+ 0xd0fc, 0x1110, 0x0804, 0x201f, 0x601b, 0x0021, 0x0804, 0x201f,
+ 0x6024, 0xa096, 0x0001, 0x1110, 0x8000, 0x6026, 0x6a10, 0x6814,
+ 0xa202, 0x0268, 0x0160, 0x2091, 0x8001, 0x2039, 0x0200, 0x609c,
+ 0x78ba, 0x609f, 0x0000, 0x080c, 0x2021, 0x0804, 0x201f, 0x2c08,
+ 0xd9fc, 0x01f0, 0x6800, 0xa065, 0x01d8, 0x6a04, 0x7000, 0xa084,
+ 0x0002, 0x0168, 0x7048, 0xa206, 0x1150, 0x6b04, 0x2160, 0x2304,
+ 0x6002, 0xa005, 0x1108, 0x6902, 0x2260, 0x6102, 0x0098, 0x2d00,
+ 0x2060, 0x080c, 0x2693, 0x6e08, 0x2160, 0x6202, 0x6906, 0x0050,
+ 0x6800, 0x6902, 0xa065, 0x0110, 0x6102, 0x0008, 0x6906, 0x2160,
+ 0x6003, 0x0000, 0x2160, 0xd9fc, 0x0118, 0xa6b4, 0xfffc, 0x6e0a,
+ 0x6810, 0x7d08, 0x8528, 0x7d0a, 0x8000, 0x6812, 0x2091, 0x8001,
+ 0xd6b4, 0x0128, 0xa6b6, 0x0040, 0x6e0a, 0x080c, 0x1bf4, 0x00ee,
+ 0x0005, 0x6008, 0xa705, 0x600a, 0x2091, 0x8000, 0x080c, 0x1d95,
+ 0x2091, 0x8001, 0x78b8, 0xa065, 0x0128, 0x609c, 0x78ba, 0x609f,
+ 0x0000, 0x0c78, 0x78b6, 0x78ba, 0x0005, 0x7970, 0x7874, 0x2818,
+ 0xd384, 0x0118, 0x8000, 0xa112, 0x0220, 0x8000, 0xa112, 0x1278,
+ 0xc384, 0x7a7c, 0x721a, 0x7a78, 0x721e, 0xdac4, 0x0120, 0x7a84,
+ 0x7222, 0x7a80, 0x7226, 0xa006, 0xd384, 0x0108, 0x8000, 0x7876,
+ 0x70d2, 0x781c, 0xa005, 0x0138, 0x8001, 0x781e, 0x1120, 0x0e04,
+ 0x205b, 0x2091, 0x4080, 0x0005, 0x2039, 0x2071, 0x0010, 0x2039,
+ 0x2077, 0x2704, 0xa005, 0x0160, 0xac00, 0x2068, 0x6908, 0x6810,
+ 0x6912, 0x680a, 0x690c, 0x6814, 0x6916, 0x680e, 0x8738, 0x0c88,
+ 0x0005, 0x0003, 0x0009, 0x000f, 0x0015, 0x001b, 0x0000, 0x0015,
+ 0x001b, 0x0000, 0x2041, 0x0000, 0x780c, 0x0002, 0x2223, 0x21fe,
+ 0x2082, 0x20f2, 0x2039, 0x8e74, 0x2734, 0x7d10, 0x00c0, 0x6084,
+ 0xa086, 0x0103, 0x1904, 0x20dc, 0x6114, 0x6018, 0xa105, 0x0120,
+ 0x86ff, 0x11d8, 0x0804, 0x20dc, 0x8603, 0xa080, 0x8e55, 0x620c,
+ 0x2202, 0x8000, 0x6210, 0x2202, 0x080c, 0x1db3, 0x8630, 0xa68e,
+ 0x000f, 0x0904, 0x215d, 0x786c, 0xa065, 0x1d08, 0x7808, 0xa602,
+ 0x1220, 0xd5ac, 0x1110, 0x263a, 0x0005, 0xa682, 0x0003, 0x1a04,
+ 0x215d, 0x2091, 0x8000, 0x2069, 0x0000, 0x6818, 0xd084, 0x11f8,
+ 0x2011, 0x8e55, 0x2204, 0x70c6, 0x8210, 0x2204, 0x70ca, 0xd684,
+ 0x1130, 0x8210, 0x2204, 0x70da, 0x8210, 0x2204, 0x70de, 0xa685,
+ 0x8020, 0x70c2, 0x681b, 0x0001, 0x2091, 0x4080, 0x7810, 0xa084,
+ 0xffcf, 0x7812, 0x2091, 0x8001, 0x203b, 0x0000, 0x0005, 0x7810,
+ 0xc0ad, 0x7812, 0x0804, 0x215d, 0x263a, 0x080c, 0x2229, 0x1904,
+ 0x2245, 0x786c, 0xa065, 0x1904, 0x2087, 0x2091, 0x8000, 0x7810,
+ 0xa084, 0xffcf, 0x86ff, 0x0108, 0xc0ad, 0x7812, 0x2091, 0x8001,
+ 0x0804, 0x2245, 0x2039, 0x8e74, 0x2734, 0x7d10, 0x00a0, 0x6084,
+ 0xa086, 0x0103, 0x1904, 0x2147, 0x6114, 0x6018, 0xa105, 0x0120,
+ 0x86ff, 0x11b8, 0x0804, 0x2147, 0xa680, 0x8e55, 0x620c, 0x2202,
+ 0x080c, 0x1db3, 0x8630, 0xa68e, 0x001e, 0x0904, 0x215d, 0x786c,
+ 0xa065, 0x1d28, 0x7808, 0xa602, 0x1220, 0xd5ac, 0x1110, 0x263a,
+ 0x0005, 0xa682, 0x0006, 0x1a04, 0x215d, 0x2091, 0x8000, 0x2069,
+ 0x0000, 0x6818, 0xd084, 0x11f8, 0x2011, 0x8e55, 0x2009, 0x8e4e,
+ 0x26a8, 0x211c, 0x2204, 0x201a, 0x8108, 0x8210, 0x1f04, 0x2129,
+ 0xa685, 0x8030, 0x70c2, 0x681b, 0x0001, 0x2091, 0x4080, 0x7810,
+ 0xa084, 0xffcf, 0x7812, 0x2091, 0x8001, 0xa006, 0x2009, 0x8e75,
+ 0x200a, 0x203a, 0x0005, 0x7810, 0xc0ad, 0x7812, 0x00b0, 0x263a,
+ 0x080c, 0x2229, 0x1904, 0x2245, 0x786c, 0xa065, 0x1904, 0x20f7,
+ 0x2091, 0x8000, 0x7810, 0xa084, 0xffcf, 0x86ff, 0x0108, 0xc0ad,
+ 0x7812, 0x2091, 0x8001, 0x0804, 0x2245, 0x2091, 0x8000, 0x7007,
+ 0x0004, 0x7994, 0x70d4, 0xa102, 0x0228, 0x0168, 0x7b90, 0xa302,
+ 0x1150, 0x0010, 0x8002, 0x1138, 0x263a, 0x7810, 0xc0ad, 0x7812,
+ 0x2091, 0x8001, 0x0005, 0xa184, 0xff00, 0x0140, 0x810f, 0x810c,
+ 0x810c, 0x8004, 0x8004, 0x8007, 0xa100, 0x0018, 0x8107, 0x8004,
+ 0x8004, 0x7a9c, 0xa210, 0x721a, 0x7a98, 0xa006, 0xa211, 0x721e,
+ 0xd4c4, 0x0130, 0x7aa4, 0xa211, 0x7222, 0x7aa0, 0xa211, 0x7226,
+ 0x20a1, 0x0030, 0x7003, 0x0000, 0x2009, 0x8e54, 0x260a, 0x8109,
+ 0x2198, 0x2104, 0xd084, 0x0108, 0x8633, 0xa6b0, 0x0002, 0x26a8,
+ 0x53a6, 0x8603, 0x7012, 0x7007, 0x0001, 0x7990, 0x7894, 0x8000,
+ 0xa10a, 0x1208, 0xa006, 0x2028, 0x7974, 0xa184, 0xff00, 0x0140,
+ 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007, 0xa100, 0x0018,
+ 0x8107, 0x8004, 0x8004, 0x797c, 0xa108, 0x7a78, 0xa006, 0xa211,
+ 0xd4c4, 0x0120, 0x7b84, 0xa319, 0x7c80, 0xa421, 0x7008, 0xd0fc,
+ 0x0de8, 0xa084, 0x01e0, 0x01d0, 0x7d10, 0x2031, 0x8e54, 0x2634,
+ 0x78a8, 0x8000, 0x78aa, 0xd08c, 0x1138, 0x7007, 0x0006, 0x7004,
+ 0xd094, 0x1de8, 0x0804, 0x215f, 0x2069, 0x4647, 0x206b, 0x0003,
+ 0x78ac, 0xa085, 0x0300, 0x78ae, 0xa006, 0x0048, 0x2030, 0x75d6,
+ 0x2091, 0x4080, 0x7d96, 0x7d10, 0xa5ac, 0xffcf, 0x7d12, 0x2091,
+ 0x8001, 0x78aa, 0x7007, 0x0006, 0x263a, 0x7003, 0x0001, 0x711a,
+ 0x721e, 0xd5c4, 0x0110, 0x7322, 0x7426, 0x0005, 0x6084, 0xa086,
+ 0x0103, 0x11d8, 0x6114, 0x6018, 0xa105, 0x11b8, 0x2069, 0x0000,
+ 0x6818, 0xd084, 0x1190, 0x600c, 0x70c6, 0x6010, 0x70ca, 0x70c3,
+ 0x8020, 0x681b, 0x0001, 0x2091, 0x4080, 0x080c, 0x1db3, 0x0e04,
+ 0x221c, 0x786c, 0xa065, 0x1d10, 0x0005, 0x0059, 0x1530, 0x786c,
+ 0xa065, 0x19e0, 0x0410, 0x0029, 0x1500, 0x786c, 0xa065, 0x1dd8,
+ 0x00e0, 0x6084, 0xa086, 0x0103, 0x1168, 0x6018, 0xc0fc, 0x601a,
+ 0xa086, 0x0004, 0x1138, 0x7804, 0xd0a4, 0x0120, 0x080c, 0x1db3,
+ 0xa006, 0x0005, 0x0079, 0x1118, 0xa085, 0x0001, 0x0005, 0x00b9,
+ 0x1110, 0x2041, 0x0001, 0x7d10, 0x0005, 0x88ff, 0x0110, 0x2091,
+ 0x4080, 0x0005, 0x7b90, 0x7994, 0x70d4, 0xa102, 0x1118, 0xa385,
+ 0x0000, 0x0005, 0x0210, 0xa302, 0x0005, 0x8002, 0x0005, 0xa184,
+ 0xff00, 0x0140, 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007,
+ 0xa100, 0x0018, 0x8107, 0x8004, 0x8004, 0x7a9c, 0x7b98, 0x7ca4,
+ 0x7da0, 0xa210, 0xa006, 0xa319, 0xa421, 0xa529, 0x2009, 0x0018,
+ 0x6028, 0xa005, 0x0110, 0x2009, 0x0040, 0x080c, 0x1b15, 0x01d0,
+ 0x78a8, 0x8000, 0x78aa, 0xd08c, 0x1510, 0x6014, 0xd0fc, 0x1118,
+ 0x2069, 0x4640, 0x0010, 0x2069, 0x4680, 0x2091, 0x8000, 0x681f,
+ 0x0003, 0x78ab, 0x0000, 0x78ac, 0xa085, 0x0300, 0x78ae, 0x2091,
+ 0x8001, 0x0068, 0x78ab, 0x0000, 0x080c, 0x1db3, 0x7990, 0x7894,
+ 0x8000, 0xa10a, 0x1208, 0xa006, 0x7896, 0x70d6, 0xa006, 0x2071,
+ 0x0010, 0x2091, 0x8001, 0x0005, 0xd7fc, 0x1118, 0x2009, 0x4658,
+ 0x0010, 0x2009, 0x4698, 0x2091, 0x8000, 0x200a, 0x00f6, 0x2009,
+ 0x4680, 0x2079, 0x0100, 0xd7fc, 0x1120, 0x2009, 0x4640, 0x2079,
+ 0x0200, 0x2104, 0xa086, 0x0000, 0x1180, 0xd7fc, 0x1118, 0x2009,
+ 0x4645, 0x0010, 0x2009, 0x4685, 0x2104, 0xa005, 0x1130, 0x7830,
+ 0xa084, 0x00c0, 0x1110, 0x781b, 0x0052, 0x00fe, 0x0005, 0x2009,
+ 0x0002, 0x2069, 0x4600, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1904,
+ 0x2324, 0x2071, 0x4680, 0x2079, 0x0100, 0x2021, 0x48bf, 0x784b,
+ 0x000f, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x0118, 0x2019, 0x3e0f,
+ 0x0030, 0x20a1, 0x012b, 0x2019, 0x3e0f, 0xd184, 0x0110, 0x20a1,
+ 0x022b, 0x2304, 0xa005, 0x0140, 0x789a, 0x8318, 0x23ac, 0x8318,
+ 0x2398, 0x53a6, 0x3318, 0x0ca8, 0x789b, 0x0000, 0x789b, 0x0020,
+ 0x20a9, 0x0010, 0x78af, 0x0000, 0x78af, 0x2020, 0x1f04, 0x2302,
+ 0x7003, 0x0000, 0x0016, 0xd18c, 0x2009, 0x0000, 0x0108, 0xc1bd,
+ 0x080c, 0x2443, 0x001e, 0x7020, 0xa084, 0x000f, 0xa085, 0x6300,
+ 0x7806, 0x780f, 0x9000, 0x7843, 0x00d8, 0x7853, 0x0090, 0x780b,
+ 0x2f08, 0x7452, 0x704f, 0x0000, 0x8109, 0x0140, 0x2071, 0x4640,
+ 0x2079, 0x0200, 0x2021, 0x46bf, 0x0804, 0x22df, 0x080c, 0x24fd,
+ 0x0005, 0x0016, 0x2011, 0x0101, 0xd1bc, 0x1110, 0x2011, 0x0201,
+ 0xa18c, 0x000f, 0x2204, 0xa084, 0xfff0, 0xa105, 0x2012, 0x001e,
+ 0x080c, 0x2443, 0x0005, 0x2011, 0x0101, 0xd3fc, 0x1110, 0x2011,
+ 0x0201, 0x20a9, 0x0009, 0x810b, 0x1f04, 0x234b, 0xa18c, 0x0e00,
+ 0x2204, 0xa084, 0xf1ff, 0xa105, 0x2012, 0x0005, 0x2019, 0x0002,
+ 0x2009, 0x0101, 0x20a9, 0x0005, 0x8213, 0x1f04, 0x235c, 0xa294,
+ 0x00e0, 0x2104, 0xa084, 0xff1f, 0xa205, 0x200a, 0x8319, 0x0118,
+ 0x2009, 0x0201, 0x0c78, 0x0005, 0x2011, 0x0101, 0xd3fc, 0x1110,
+ 0x2011, 0x0201, 0x20a9, 0x000c, 0x810b, 0x1f04, 0x2374, 0xa18c,
+ 0xf000, 0x2204, 0xa084, 0x0fff, 0xa105, 0x2012, 0x0005, 0x2011,
+ 0x0102, 0xd3fc, 0x1110, 0x2011, 0x0202, 0x2204, 0xa084, 0xf0cf,
+ 0xa105, 0x2012, 0x0005, 0x00c6, 0x2061, 0x0100, 0xd1bc, 0x1110,
+ 0x2061, 0x0200, 0xc1bc, 0x8103, 0x8003, 0xa080, 0x0020, 0x609a,
+ 0x62ac, 0x63ac, 0x00ce, 0x0005, 0x00c6, 0x2061, 0x0100, 0xd1bc,
+ 0x1110, 0x2061, 0x0200, 0xc1bc, 0x8103, 0x8003, 0xa080, 0x0022,
+ 0x609a, 0x60a4, 0xa084, 0xffdf, 0x60ae, 0x00ce, 0x0005, 0x00c6,
+ 0x2061, 0x0100, 0xd1bc, 0x1110, 0x2061, 0x0200, 0xc1bc, 0x8103,
+ 0x8003, 0xa080, 0x0020, 0x609a, 0x60a4, 0xa28c, 0x0020, 0x0118,
+ 0xc2ac, 0xa39d, 0x4000, 0xc3ec, 0xd3b4, 0x1108, 0xc3ed, 0x62ae,
+ 0x2010, 0x60a4, 0x63ae, 0x2018, 0x00ce, 0x0005, 0x2091, 0x8000,
+ 0x00c6, 0x00e6, 0x6818, 0xa005, 0x0904, 0x2427, 0xd1fc, 0x0118,
+ 0x2061, 0x8dd0, 0x0010, 0x2061, 0x8cc0, 0x080c, 0x242f, 0x0560,
+ 0x20a9, 0x0101, 0xd1fc, 0x0118, 0x2061, 0x8cd0, 0x0010, 0x2061,
+ 0x8bc0, 0x00c6, 0x080c, 0x242f, 0x0128, 0x00ce, 0x8c60, 0x1f04,
+ 0x23e9, 0x04a8, 0x000e, 0xd1fc, 0x0128, 0xa082, 0x8cd0, 0x2071,
+ 0x4680, 0x0020, 0xa082, 0x8bc0, 0x2071, 0x4640, 0x7076, 0x7172,
+ 0x2138, 0x2001, 0x0004, 0x7062, 0x707f, 0x000f, 0x71d0, 0xc1c4,
+ 0x71d2, 0x080c, 0x22a4, 0x00c0, 0xd1fc, 0x1118, 0x2071, 0x4640,
+ 0x0010, 0x2071, 0x4680, 0x6020, 0xc0dd, 0x6022, 0x7172, 0x2138,
+ 0x2c00, 0x707a, 0x2001, 0x0006, 0x7062, 0x707f, 0x000f, 0x71d0,
+ 0xc1c4, 0x71d2, 0x080c, 0x22a4, 0x2001, 0x0000, 0x0010, 0x2001,
+ 0x0001, 0x2091, 0x8001, 0xa005, 0x00ee, 0x00ce, 0x0005, 0x2c04,
+ 0xa005, 0x0170, 0x2060, 0x6010, 0xa306, 0x1140, 0x600c, 0xa206,
+ 0x1128, 0x6014, 0xa106, 0x1110, 0xa006, 0x0020, 0x6000, 0x0c80,
+ 0xa085, 0x0001, 0x0005, 0x00f6, 0x00e6, 0x0016, 0x2079, 0x4680,
+ 0x2071, 0x0100, 0xd1bc, 0x1120, 0x2079, 0x4640, 0x2071, 0x0200,
+ 0x7920, 0xa18c, 0x000f, 0x70ec, 0xd0c4, 0x1110, 0x001e, 0x0060,
+ 0x810b, 0x810b, 0x810b, 0x810b, 0x000e, 0xa18d, 0x0800, 0xd0bc,
+ 0x1110, 0xa18d, 0x0f00, 0x2104, 0x00ee, 0x00fe, 0x0005, 0x2001,
+ 0x4601, 0x2004, 0xd0ac, 0x1138, 0x68e4, 0xd0ac, 0x0120, 0xa084,
+ 0x0006, 0x1108, 0x0009, 0x0005, 0x6014, 0x00e6, 0x0036, 0x2018,
+ 0x2071, 0x4b40, 0xd0fc, 0x1110, 0x2071, 0x4ac0, 0x8007, 0xa084,
+ 0x000f, 0x8003, 0x8003, 0x8003, 0xae70, 0x7004, 0xa084, 0x000a,
+ 0x1904, 0x24fa, 0x7108, 0xa194, 0xff00, 0x0904, 0x24fa, 0xa18c,
+ 0x00ff, 0x701c, 0xa084, 0xff00, 0x01c0, 0x7004, 0xa085, 0x003a,
+ 0x7006, 0x2001, 0x0009, 0xa102, 0x16d8, 0x2001, 0x000a, 0xa102,
+ 0x16d0, 0x2001, 0x000c, 0xa102, 0x16c8, 0x701c, 0xa084, 0x00ff,
+ 0x701e, 0x7004, 0xa084, 0xffdf, 0x7006, 0x2001, 0x000a, 0xa106,
+ 0x01a8, 0x2001, 0x000c, 0xa106, 0x01a0, 0x2001, 0x0012, 0xa106,
+ 0x0198, 0x2001, 0x0014, 0xa106, 0x0190, 0x2001, 0x0019, 0xa106,
+ 0x0188, 0x2001, 0x0032, 0xa106, 0x0180, 0x00d8, 0x2009, 0x000c,
+ 0x00d0, 0x2009, 0x0012, 0x00b8, 0x2009, 0x0014, 0x00a0, 0x2009,
+ 0x0019, 0x0088, 0x2009, 0x0020, 0x0070, 0x2009, 0x003f, 0x0058,
+ 0x2009, 0x000a, 0x0040, 0x2009, 0x000c, 0x0028, 0x2009, 0x0019,
+ 0x0010, 0x2011, 0x0000, 0x2100, 0xa205, 0x700a, 0x7004, 0xa085,
+ 0x000a, 0x7006, 0x2071, 0x4600, 0x7004, 0xd0bc, 0x0158, 0xd3fc,
+ 0x1120, 0x73ea, 0x2071, 0x4640, 0x0018, 0x73ee, 0x2071, 0x4680,
+ 0x701f, 0x000d, 0x003e, 0x00ee, 0x0005, 0x2001, 0x01ff, 0x2004,
+ 0xd0fc, 0x11d0, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x12a0,
+ 0x2071, 0x0200, 0x71ec, 0xa18c, 0x1c00, 0x810f, 0x810c, 0x810c,
+ 0x2079, 0x0100, 0x78ec, 0xa084, 0x1c00, 0x8007, 0x8004, 0x8004,
+ 0xa105, 0xa08a, 0x0007, 0x0208, 0x0005, 0x0002, 0x254b, 0x2532,
+ 0x254b, 0x2532, 0x2525, 0x253f, 0x2525, 0x7008, 0xa084, 0xc3ff,
+ 0xa085, 0x3000, 0x700a, 0x7808, 0xa084, 0xc3ff, 0xa085, 0x3000,
+ 0x780a, 0x0005, 0x7008, 0xa084, 0xc3ff, 0xa085, 0x2000, 0x700a,
+ 0x7808, 0xa084, 0xc3ff, 0xa085, 0x2000, 0x780a, 0x0005, 0x7008,
+ 0xa084, 0xc3ff, 0xa085, 0x0c00, 0x700a, 0x7808, 0xa084, 0xc3ff,
+ 0xa085, 0x0c00, 0x780a, 0x0005, 0x0e04, 0x254c, 0x2091, 0x8000,
+ 0x2071, 0x0000, 0x0006, 0x7018, 0xd084, 0x1de8, 0x000e, 0x2071,
+ 0x0010, 0x70ca, 0x000e, 0x70c6, 0x70c3, 0x8002, 0x70db, 0x0a04,
+ 0x70df, 0x0020, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080,
+ 0x0cf8, 0x7f3c, 0x7e58, 0x7c30, 0x7d38, 0x78a0, 0x708a, 0x758e,
+ 0x7492, 0x7696, 0x779a, 0xa594, 0x003f, 0xd4f4, 0x0138, 0xd7bc,
+ 0x1128, 0xa784, 0x007d, 0x1904, 0x3c74, 0x0871, 0xa49c, 0x000f,
+ 0xa382, 0x0004, 0x0320, 0xa3a6, 0x0007, 0x1930, 0x2418, 0x8507,
+ 0xa084, 0x000f, 0x0002, 0x2b49, 0x2c34, 0x2c72, 0x2ed8, 0x3256,
+ 0x32ad, 0x3353, 0x33e2, 0x34b6, 0x3588, 0x259e, 0x259b, 0x2970,
+ 0x2a56, 0x322a, 0x259b, 0x080c, 0x254c, 0x0005, 0xa006, 0x0038,
+ 0x7808, 0xc08d, 0x780a, 0xa006, 0x7002, 0x704a, 0x7042, 0x70ce,
+ 0x705c, 0xa005, 0x1904, 0x26ec, 0x7060, 0xa084, 0x0007, 0x0002,
+ 0x25b8, 0x2626, 0x262e, 0x2637, 0x2640, 0x26d2, 0x2649, 0x2626,
+ 0x7830, 0xd0bc, 0x1d10, 0x71d0, 0xd1bc, 0x19f8, 0xd1b4, 0x1904,
+ 0x2603, 0x70a0, 0xa086, 0x0001, 0x09c0, 0x70b0, 0xa06d, 0x6800,
+ 0xa065, 0xa055, 0x789b, 0x0080, 0x6b0c, 0x7baa, 0x6808, 0xa045,
+ 0x6d10, 0x6804, 0xa06d, 0xa05d, 0xa886, 0x0001, 0x0118, 0x69bc,
+ 0x7daa, 0x79aa, 0x68c0, 0xa04d, 0x6e1c, 0x2001, 0x0010, 0x0804,
+ 0x281f, 0x705c, 0xa005, 0x1904, 0x259d, 0x00c6, 0x00d6, 0x70b0,
+ 0xa06d, 0x6800, 0xa065, 0xa055, 0x789b, 0x0080, 0x6b0c, 0x7baa,
+ 0x6808, 0xa045, 0x6d10, 0x6804, 0xa06d, 0xa05d, 0xa886, 0x0001,
+ 0x0118, 0x69bc, 0x7daa, 0x79aa, 0x68c0, 0xa04d, 0x6e1c, 0x2001,
+ 0x0020, 0x0804, 0x281f, 0x080c, 0x3c33, 0x1904, 0x259d, 0x781b,
+ 0x0068, 0x70b8, 0xa06d, 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de,
+ 0x6898, 0x78d2, 0x78da, 0x7808, 0xc08d, 0x780a, 0x68bc, 0x703e,
+ 0xc1b4, 0x71d2, 0x70b4, 0xa065, 0x68c0, 0x7056, 0x7003, 0x0002,
+ 0x2d00, 0x704a, 0xad80, 0x0009, 0x7042, 0x0005, 0x080c, 0x3c33,
+ 0x1120, 0x781b, 0x0054, 0x7003, 0x0004, 0x0005, 0x080c, 0x3c33,
+ 0x1128, 0x2011, 0x000c, 0x0419, 0x7003, 0x0004, 0x0005, 0x080c,
+ 0x3c33, 0x1128, 0x2011, 0x0006, 0x00d1, 0x7003, 0x0004, 0x0005,
+ 0x080c, 0x3c33, 0x1128, 0x2011, 0x000d, 0x0089, 0x7003, 0x0004,
+ 0x0005, 0x080c, 0x3c33, 0x1150, 0x2011, 0x0006, 0x0041, 0x7078,
+ 0x707b, 0x0000, 0x2068, 0x704a, 0x7003, 0x0004, 0x0005, 0x7170,
+ 0xc1fc, 0x8107, 0x7882, 0x789b, 0x0080, 0xa286, 0x000c, 0x1120,
+ 0x7aaa, 0x2001, 0x0001, 0x0098, 0xa18c, 0x001f, 0xa18d, 0x00c0,
+ 0x79aa, 0xa286, 0x000d, 0x0120, 0x7aaa, 0x2001, 0x0002, 0x0038,
+ 0x78ab, 0x0020, 0x7174, 0x79aa, 0x7aaa, 0x2001, 0x0004, 0x789b,
+ 0x0060, 0x78aa, 0x785b, 0x0004, 0x781b, 0x0113, 0x080c, 0x3c46,
+ 0x707f, 0x000f, 0x70d0, 0xd0b4, 0x0168, 0xc0b4, 0x70d2, 0x00c6,
+ 0x70b4, 0xa065, 0x6008, 0xa084, 0xfbef, 0x600a, 0x6018, 0x8001,
+ 0x601a, 0x00ce, 0x0005, 0x7014, 0xa005, 0x1138, 0x70d0, 0xd0b4,
+ 0x0128, 0x70b4, 0xac06, 0x1110, 0x0c29, 0x0005, 0x0016, 0x71a0,
+ 0xa186, 0x0001, 0x0528, 0x00d6, 0x0026, 0x2100, 0x2011, 0x0001,
+ 0xa212, 0x70b0, 0x2068, 0x6800, 0xac06, 0x0120, 0x8211, 0x01b0,
+ 0x00c9, 0x0cc8, 0x00c6, 0x2100, 0x2011, 0x0001, 0xa212, 0x70b0,
+ 0x2068, 0x6800, 0x2060, 0x6008, 0xa084, 0xfbef, 0x600a, 0x8211,
+ 0x0110, 0x0041, 0x0cb0, 0x70a3, 0x0001, 0x00ce, 0x002e, 0x00de,
+ 0x001e, 0x0005, 0xade8, 0x0005, 0x70a8, 0xad06, 0x1110, 0x70a4,
+ 0x2068, 0x0005, 0x080c, 0x3c33, 0x1904, 0x259d, 0x7078, 0x2068,
+ 0x7770, 0x080c, 0x3b6f, 0x2c50, 0x080c, 0x3cce, 0x789b, 0x0080,
+ 0x6814, 0xa084, 0x001f, 0xc0bd, 0x78aa, 0x6e1c, 0x2041, 0x0001,
+ 0x2001, 0x0004, 0x0804, 0x2824, 0x080c, 0x3c33, 0x1904, 0x259d,
+ 0x789b, 0x0080, 0x705c, 0x2068, 0x6f14, 0x70d0, 0xd0b4, 0x0168,
+ 0xc0b4, 0x70d2, 0x00c6, 0x70b4, 0xa065, 0x6008, 0xa084, 0xfbef,
+ 0x600a, 0x6018, 0x8001, 0x601a, 0x00ce, 0x080c, 0x3b6f, 0x2c50,
+ 0x080c, 0x3cce, 0x6824, 0xa005, 0x0130, 0xa082, 0x0006, 0x0208,
+ 0x0010, 0x6827, 0x0005, 0x6814, 0xa084, 0x001f, 0xc0bd, 0x78aa,
+ 0x2031, 0x0020, 0x2041, 0x0001, 0x2001, 0x0003, 0x0804, 0x2824,
+ 0xc28d, 0x72d2, 0x72bc, 0xa200, 0xa015, 0x7150, 0x8108, 0xa12a,
+ 0x0208, 0x71bc, 0x2164, 0x6504, 0x85ff, 0x1170, 0x7152, 0x8421,
+ 0x1da8, 0x70d0, 0xd08c, 0x0128, 0x70cc, 0xa005, 0x1110, 0x70cf,
+ 0x000a, 0x0005, 0x2200, 0x0c90, 0x70d0, 0xc08c, 0x70d2, 0x70cf,
+ 0x0000, 0x6034, 0xa005, 0x1db0, 0x6708, 0xa784, 0x073f, 0x01d0,
+ 0xd7d4, 0x1d80, 0xa784, 0x0021, 0x1d68, 0xa784, 0x0002, 0x0130,
+ 0xa784, 0x0004, 0x0d38, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0218,
+ 0x1d08, 0xa784, 0x0100, 0x0130, 0x6018, 0xa005, 0x19d8, 0xa7bc,
+ 0xfeff, 0x670a, 0x2568, 0x6823, 0x0000, 0x6e1c, 0xa684, 0x000e,
+ 0x6318, 0x0128, 0x601c, 0xa302, 0x0220, 0x0118, 0x0858, 0x83ff,
+ 0x1948, 0x2d58, 0x2c50, 0x7152, 0xd7bc, 0x1120, 0x7028, 0x6022,
+ 0x603a, 0x0010, 0xc7bc, 0x670a, 0x68c0, 0xa065, 0xa04d, 0x6100,
+ 0x2a60, 0x2041, 0x0001, 0x6b14, 0xa39c, 0x001f, 0xa39d, 0x00c0,
+ 0xd1fc, 0x0110, 0xd684, 0x0110, 0xa39c, 0xffbf, 0xd6a4, 0x0110,
+ 0xa39d, 0x0020, 0xa684, 0x000e, 0x1904, 0x27d6, 0xc7a5, 0x670a,
+ 0x2c00, 0x68c6, 0x77a0, 0xa786, 0x0001, 0x1178, 0x70d0, 0xd0b4,
+ 0x1160, 0x7000, 0xa082, 0x0002, 0x1240, 0x7830, 0xd0bc, 0x1128,
+ 0x789b, 0x0080, 0x7baa, 0x0804, 0x281d, 0x8739, 0x77a2, 0x2750,
+ 0x77ac, 0xa7b0, 0x0005, 0x70a8, 0xa606, 0x1108, 0x76a4, 0x76ae,
+ 0x2c3a, 0x8738, 0x2d3a, 0x8738, 0x283a, 0x8738, 0x233a, 0x8738,
+ 0x253a, 0x7830, 0xd0bc, 0x0150, 0x2091, 0x8000, 0x2091, 0x303d,
+ 0x70d0, 0xa084, 0x303d, 0x2091, 0x8000, 0x2090, 0xaad5, 0x0000,
+ 0x0120, 0x8421, 0x2200, 0x1904, 0x2725, 0x0005, 0xd1dc, 0x0904,
+ 0x37ce, 0x2029, 0x0020, 0xd69c, 0x1120, 0x8528, 0xd68c, 0x1108,
+ 0x8528, 0x8840, 0x6f14, 0x610c, 0x8108, 0xa18c, 0x00ff, 0x70c8,
+ 0xa160, 0x2c64, 0x8cff, 0x0188, 0x6014, 0xa706, 0x1dd0, 0x60b8,
+ 0x8001, 0x60ba, 0x1d88, 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a,
+ 0x2200, 0x8421, 0x1904, 0x2725, 0x0005, 0x2a60, 0x610e, 0x69be,
+ 0x2c00, 0x68c6, 0x8840, 0x6008, 0xc0d5, 0x600a, 0x77a0, 0xa786,
+ 0x0001, 0x1904, 0x27ad, 0x70d0, 0xd0b4, 0x1904, 0x27ad, 0x7000,
+ 0xa082, 0x0002, 0x1a04, 0x27ad, 0x7830, 0xd0bc, 0x1904, 0x27ad,
+ 0x789b, 0x0080, 0x7baa, 0x7daa, 0x79aa, 0x2001, 0x0002, 0x0006,
+ 0x6018, 0x8000, 0x601a, 0x0008, 0x0006, 0x2960, 0x6104, 0x2a60,
+ 0x080c, 0x3ce1, 0x1590, 0xa184, 0x0018, 0x0180, 0xa184, 0x0010,
+ 0x0118, 0x080c, 0x3977, 0x1548, 0xa184, 0x0008, 0x0138, 0x69a0,
+ 0xa184, 0x0600, 0x1118, 0x080c, 0x3895, 0x00f8, 0x69a0, 0xa184,
+ 0x1e00, 0x0528, 0xa184, 0x0800, 0x0178, 0x00c6, 0x2960, 0x6000,
+ 0xa085, 0x2000, 0x6002, 0x6104, 0xa18d, 0x0010, 0x6106, 0x00ce,
+ 0x080c, 0x3977, 0x1150, 0x69a0, 0xa184, 0x0200, 0x0118, 0x080c,
+ 0x38da, 0x0018, 0xa184, 0x0400, 0x19f0, 0x69a0, 0xa184, 0x1000,
+ 0x0130, 0x6914, 0xa18c, 0xff00, 0x810f, 0x080c, 0x239c, 0x002e,
+ 0xa68c, 0x00e0, 0xa684, 0x0060, 0x0128, 0xa086, 0x0060, 0x1110,
+ 0xa18d, 0x4000, 0xa18d, 0x0104, 0x69b6, 0x789b, 0x0060, 0x2800,
+ 0x78aa, 0x6818, 0xc0fd, 0x681a, 0xd6bc, 0x0168, 0xc0fc, 0x7083,
+ 0x0000, 0xa08a, 0x000d, 0x0328, 0xa08a, 0x000c, 0x7182, 0x2001,
+ 0x000c, 0x800c, 0x7186, 0x78aa, 0x3518, 0x3340, 0x3428, 0x8000,
+ 0x80ac, 0xaf80, 0x002b, 0x20a0, 0x789b, 0x0000, 0xad80, 0x000b,
+ 0x2098, 0x53a6, 0x23a8, 0x2898, 0x25a0, 0xa286, 0x0020, 0x1508,
+ 0x70d0, 0xc0b5, 0x70d2, 0x2c00, 0x70b6, 0x2d00, 0x70ba, 0x6814,
+ 0xc0fc, 0x8007, 0x7882, 0xa286, 0x0002, 0x0904, 0x28f5, 0x70a0,
+ 0x8000, 0x70a2, 0x74b0, 0xa498, 0x0005, 0x70a8, 0xa306, 0x1108,
+ 0x73a4, 0x73b2, 0xa286, 0x0010, 0x0904, 0x259d, 0x00de, 0x00ce,
+ 0x0005, 0x7000, 0xa005, 0x19e0, 0xa286, 0x0002, 0x1904, 0x290c,
+ 0x080c, 0x3c33, 0x19a8, 0x6814, 0xc0fc, 0x8007, 0x7882, 0x2091,
+ 0x8000, 0x781b, 0x0068, 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de,
+ 0x6898, 0x78d2, 0x78da, 0x2091, 0x8001, 0x7808, 0xc08d, 0x780a,
+ 0x0126, 0x00d6, 0x00c6, 0x70d0, 0xa084, 0x2e00, 0x2090, 0x00ce,
+ 0x00de, 0x012e, 0x2900, 0x7056, 0x68bc, 0x703e, 0x7003, 0x0002,
+ 0x2d00, 0x704a, 0xad80, 0x0009, 0x7042, 0x7830, 0xd0bc, 0x0140,
+ 0x2091, 0x303d, 0x70d0, 0xa084, 0x303d, 0x2091, 0x8000, 0x2090,
+ 0x70a0, 0xa005, 0x1108, 0x0005, 0x8421, 0x0de8, 0x724c, 0x70bc,
+ 0xa200, 0xa015, 0x0804, 0x2725, 0xa286, 0x0010, 0x1560, 0x080c,
+ 0x3c33, 0x1904, 0x28a0, 0x6814, 0xc0fc, 0x8007, 0x7882, 0x781b,
+ 0x0068, 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2,
+ 0x78da, 0x7808, 0xc08d, 0x780a, 0x70a0, 0x8000, 0x70a2, 0x74b0,
+ 0xa490, 0x0005, 0x70a8, 0xa206, 0x1108, 0x72a4, 0x72b2, 0x2900,
+ 0x7056, 0x68bc, 0x703e, 0x7003, 0x0002, 0x2d00, 0x704a, 0xad80,
+ 0x0009, 0x7042, 0x0005, 0x6bb4, 0xa39d, 0x2000, 0x7b5a, 0x6814,
+ 0xc0fc, 0x8007, 0x7882, 0x6b94, 0x7bd6, 0x7bde, 0x6e98, 0x7ed2,
+ 0x7eda, 0x781b, 0x0068, 0x2900, 0x7056, 0x7202, 0x7808, 0xc08d,
+ 0x780a, 0x2300, 0xa605, 0x0170, 0x70d0, 0xa084, 0x2e00, 0xa086,
+ 0x2600, 0x1118, 0x2009, 0x0000, 0x0010, 0x2009, 0x0001, 0xa284,
+ 0x000f, 0x0023, 0xad80, 0x0009, 0x7042, 0x0005, 0x296e, 0x41d9,
+ 0x41d9, 0x41c7, 0x41d9, 0x296e, 0x296e, 0x296e, 0x080c, 0x254c,
+ 0x7808, 0xa084, 0xfffd, 0x780a, 0x00f6, 0x2079, 0x4600, 0x78ac,
+ 0x00fe, 0xd084, 0x01b0, 0x7060, 0xa086, 0x0001, 0x0904, 0x2a32,
+ 0x7060, 0xa086, 0x0005, 0x1158, 0x7078, 0x2068, 0x681b, 0x0004,
+ 0x6817, 0x0000, 0x6820, 0xa084, 0x00ff, 0xc09d, 0x6822, 0x7063,
+ 0x0000, 0x70a3, 0x0000, 0x70a4, 0x70ae, 0x70b2, 0x080c, 0x2682,
+ 0x0156, 0x2011, 0x0004, 0x7160, 0xa186, 0x0001, 0x0160, 0xa186,
+ 0x0007, 0x1118, 0x701f, 0x0005, 0x0030, 0x701f, 0x0001, 0x70d0,
+ 0xc0c5, 0x70d2, 0x0000, 0x2001, 0x460a, 0x2004, 0xa084, 0x00ff,
+ 0xa086, 0x0018, 0x0130, 0x7018, 0x7016, 0xa005, 0x1110, 0x70a3,
+ 0x0001, 0x0066, 0x080c, 0x3f26, 0x20a9, 0x0010, 0x2039, 0x0000,
+ 0x080c, 0x3a66, 0xa7b8, 0x0100, 0x1f04, 0x29c0, 0x006e, 0x7000,
+ 0x0002, 0x29fd, 0x29db, 0x29db, 0x29d3, 0x29fd, 0x29fd, 0x29fd,
+ 0x29d1, 0x080c, 0x254c, 0x705c, 0xa005, 0x0538, 0xad06, 0x1118,
+ 0x6800, 0x705e, 0x0080, 0x6820, 0xd084, 0x1148, 0x6f14, 0x080c,
+ 0x3b6f, 0x6008, 0xc0d4, 0x600a, 0x080c, 0x37a4, 0x0020, 0x7058,
+ 0x2060, 0x6800, 0x6002, 0xa684, 0x5f00, 0x681e, 0x6818, 0xd0fc,
+ 0x0108, 0x6a1a, 0x6817, 0x0000, 0x682b, 0x0000, 0x6820, 0xa084,
+ 0x00ff, 0xc09d, 0x6822, 0x080c, 0x1da2, 0x2011, 0x0004, 0x74c8,
+ 0xa4a0, 0x0100, 0x04b1, 0xaea0, 0x0017, 0x0499, 0x20a9, 0x0101,
+ 0x74c8, 0x0479, 0x8420, 0x1f04, 0x2a09, 0x70c0, 0x2060, 0x2021,
+ 0x0002, 0x20a9, 0x0100, 0x6110, 0x81ff, 0x0198, 0x6018, 0x0016,
+ 0x0006, 0x2011, 0x4602, 0x220c, 0xa102, 0x2012, 0x000e, 0x001e,
+ 0xa102, 0x0338, 0x6012, 0x1128, 0x2011, 0x4604, 0x2204, 0xc0a5,
+ 0x2012, 0x601b, 0x0000, 0xace0, 0x0010, 0x1f04, 0x2a13, 0x8421,
+ 0x1d00, 0x015e, 0x7063, 0x0000, 0x7003, 0x0000, 0x704b, 0x0000,
+ 0x0005, 0x0046, 0x2404, 0xa005, 0x01a8, 0x2068, 0x6800, 0x0006,
+ 0x6a1a, 0x6817, 0x0000, 0x682b, 0x0000, 0x68b4, 0xa084, 0x5f00,
+ 0x681e, 0x6820, 0xa084, 0x00ff, 0xc09d, 0x6822, 0x080c, 0x1da2,
+ 0x000e, 0x0c48, 0x004e, 0x2023, 0x0000, 0x0005, 0xa282, 0x0003,
+ 0x0310, 0x080c, 0x254c, 0x2300, 0x0002, 0x2a60, 0x2add, 0x2af7,
+ 0xa282, 0x0002, 0x0110, 0x080c, 0x254c, 0x7060, 0x7063, 0x0000,
+ 0x707f, 0x0000, 0x0022, 0x77d0, 0xc7c5, 0x77d2, 0x0002, 0x2a77,
+ 0x2a77, 0x2a79, 0x2ab1, 0x37d8, 0x2a77, 0x2ab1, 0x2a77, 0x080c,
+ 0x254c, 0x7770, 0x080c, 0x3a66, 0x7770, 0xa7bc, 0x8f00, 0x080c,
+ 0x3b6f, 0x6018, 0xa005, 0x0528, 0xd7fc, 0x1118, 0x2021, 0x8cc0,
+ 0x0010, 0x2021, 0x8dd0, 0x2009, 0x0005, 0x2011, 0x0010, 0x080c,
+ 0x2b11, 0x01b8, 0x0156, 0x20a9, 0x0101, 0xd7fc, 0x1118, 0x2021,
+ 0x8bc0, 0x0010, 0x2021, 0x8cd0, 0x0046, 0x2009, 0x0005, 0x2011,
+ 0x0010, 0x080c, 0x2b11, 0x004e, 0x0118, 0x8420, 0x1f04, 0x2a9c,
+ 0x015e, 0x8738, 0xa784, 0x001f, 0x1990, 0x0804, 0x25a0, 0x0804,
+ 0x25a0, 0x7770, 0x080c, 0x3b6f, 0x6018, 0xa005, 0x0520, 0xd7fc,
+ 0x1118, 0x2021, 0x8cc0, 0x0010, 0x2021, 0x8dd0, 0x2009, 0x0005,
+ 0x2011, 0x0020, 0x080c, 0x2b11, 0x01b0, 0x0156, 0x20a9, 0x0101,
+ 0xd7fc, 0x1118, 0x2021, 0x8bc0, 0x0010, 0x2021, 0x8cd0, 0x0046,
+ 0x2009, 0x0005, 0x2011, 0x0020, 0x04e1, 0x004e, 0x0118, 0x8420,
+ 0x1f04, 0x2acf, 0x015e, 0x0804, 0x25a0, 0x2200, 0x0002, 0x2ae2,
+ 0x2ae4, 0x2ae4, 0x080c, 0x254c, 0x2009, 0x0012, 0x7060, 0xa086,
+ 0x0002, 0x0110, 0x2009, 0x000e, 0x6818, 0xd0fc, 0x0108, 0x691a,
+ 0x7063, 0x0000, 0x70d0, 0xc0c5, 0x70d2, 0x0804, 0x3be5, 0x2200,
+ 0x0002, 0x2afe, 0x2ae4, 0x2afc, 0x080c, 0x254c, 0x080c, 0x3f26,
+ 0x7000, 0xa086, 0x0002, 0x1904, 0x375d, 0x080c, 0x37be, 0x6008,
+ 0xa084, 0xfbef, 0x600a, 0x080c, 0x374f, 0x0904, 0x375d, 0x0804,
+ 0x25a0, 0x2404, 0xa005, 0x0590, 0x2068, 0x2d04, 0x0006, 0x6814,
+ 0xa706, 0x0118, 0x2d20, 0x000e, 0x0ca8, 0x000e, 0x2022, 0x691a,
+ 0x6817, 0x0000, 0x682b, 0x0000, 0x68b4, 0xa084, 0x5f00, 0x681e,
+ 0x6820, 0xa084, 0x00ff, 0xa205, 0x6822, 0x080c, 0x1da2, 0x2021,
+ 0x4602, 0x241c, 0x8319, 0x2322, 0x6010, 0x8001, 0x6012, 0x1128,
+ 0x2021, 0x4604, 0x2404, 0xc0a5, 0x2022, 0x6008, 0xa084, 0xf9ef,
+ 0x600a, 0x080c, 0x269e, 0x080c, 0x37be, 0x0005, 0xa085, 0x0001,
+ 0x0ce0, 0x2300, 0x0002, 0x2b50, 0x2b4e, 0x2bcb, 0x080c, 0x254c,
+ 0x78e4, 0xa005, 0x17b0, 0x3208, 0xa18c, 0x0800, 0x0118, 0x0104,
+ 0x259d, 0x0010, 0x0304, 0x259d, 0x2008, 0xa084, 0x0030, 0x1110,
+ 0x0804, 0x322a, 0x78ec, 0xa084, 0x0003, 0x0dd0, 0x7884, 0xd0fc,
+ 0x1118, 0xa184, 0x0007, 0x0090, 0xa184, 0x0007, 0xa086, 0x0004,
+ 0x1118, 0x2001, 0x0000, 0x0050, 0xa184, 0x0007, 0xa086, 0x0005,
+ 0x0118, 0xa184, 0x0007, 0x0010, 0x2001, 0x0001, 0x0002, 0x2bae,
+ 0x2bb7, 0x2ba4, 0x2b87, 0x3c29, 0x3c29, 0x2b87, 0x2bc1, 0x080c,
+ 0x254c, 0x7000, 0xa086, 0x0004, 0x1190, 0x7060, 0xa086, 0x0002,
+ 0x1130, 0x2011, 0x0002, 0x2019, 0x0000, 0x0804, 0x2a56, 0x7060,
+ 0xa086, 0x0006, 0x0db0, 0x7060, 0xa086, 0x0004, 0x0d90, 0x79e4,
+ 0x2001, 0x0003, 0x0804, 0x2f18, 0x6818, 0xd0fc, 0x0110, 0x681b,
+ 0x001d, 0x080c, 0x3a3c, 0x781b, 0x006e, 0x0005, 0x6818, 0xd0fc,
+ 0x0110, 0x681b, 0x001d, 0x080c, 0x3a3c, 0x0804, 0x3c07, 0x6818,
+ 0xd0fc, 0x0110, 0x681b, 0x001d, 0x080c, 0x3a3c, 0x781b, 0x00fa,
+ 0x0005, 0x6818, 0xd0fc, 0x0110, 0x681b, 0x001d, 0x080c, 0x3a3c,
+ 0x781b, 0x00cb, 0x0005, 0xa584, 0x000f, 0x11c0, 0x7000, 0x0002,
+ 0x25a0, 0x2bd8, 0x2bda, 0x375d, 0x375d, 0x375d, 0x2bd8, 0x2bd8,
+ 0x080c, 0x254c, 0x080c, 0x37be, 0x6008, 0xa084, 0xfbef, 0x600a,
+ 0x080c, 0x374f, 0x0904, 0x375d, 0x0804, 0x25a0, 0x78e4, 0xa005,
+ 0x1b04, 0x2b89, 0x3208, 0xa18c, 0x0800, 0x0118, 0x0104, 0x2b89,
+ 0x0010, 0x0304, 0x2b89, 0x2008, 0xa084, 0x0030, 0x1118, 0x781b,
+ 0x0068, 0x0005, 0x78ec, 0xa084, 0x0003, 0x0dc8, 0x7884, 0xd0fc,
+ 0x1118, 0xa184, 0x0007, 0x0090, 0xa184, 0x0007, 0xa086, 0x0004,
+ 0x1118, 0x2001, 0x0000, 0x0050, 0xa184, 0x0007, 0xa086, 0x0005,
+ 0x0118, 0xa184, 0x0007, 0x0010, 0x2001, 0x0001, 0x0002, 0x2c26,
+ 0x2c2a, 0x2c21, 0x2c1f, 0x3c29, 0x3c29, 0x2c1f, 0x3c23, 0x080c,
+ 0x254c, 0x080c, 0x3a42, 0x781b, 0x006e, 0x0005, 0x080c, 0x3a42,
+ 0x0804, 0x3c07, 0x080c, 0x3a42, 0x781b, 0x00fa, 0x0005, 0x080c,
+ 0x3a42, 0x781b, 0x00cb, 0x0005, 0x2300, 0x0002, 0x2c3b, 0x2c39,
+ 0x2c3d, 0x080c, 0x254c, 0x0804, 0x33e2, 0x681b, 0x0016, 0x78a3,
+ 0x0000, 0x79e4, 0xa184, 0x0030, 0x0904, 0x33e2, 0x78ec, 0xa084,
+ 0x0003, 0x0904, 0x33e2, 0xa184, 0x0100, 0x0d98, 0x7884, 0xd0fc,
+ 0x1118, 0xa184, 0x0007, 0x0090, 0xa184, 0x0007, 0xa086, 0x0004,
+ 0x1118, 0x2001, 0x0000, 0x0050, 0xa184, 0x0007, 0xa086, 0x0005,
+ 0x0118, 0xa184, 0x0007, 0x0010, 0x2001, 0x0001, 0x0002, 0x2c6f,
+ 0x2c2a, 0x2ba4, 0x3be5, 0x3c29, 0x3c29, 0x3be5, 0x3c23, 0x080c,
+ 0x3bf1, 0x0005, 0xa282, 0x0005, 0x0310, 0x080c, 0x254c, 0x7898,
+ 0x2040, 0x2300, 0x0002, 0x2c7e, 0x2ea8, 0x2eb2, 0x2200, 0x0002,
+ 0x2c9a, 0x2c87, 0x2c9a, 0x2c85, 0x2e8a, 0x080c, 0x254c, 0x789b,
+ 0x0018, 0x78a8, 0x2010, 0xa084, 0x00ff, 0xa082, 0x0020, 0x0a04,
+ 0x3a0b, 0xa08a, 0x0004, 0x1a04, 0x3a0b, 0x0002, 0x3a0b, 0x3a0b,
+ 0x3a0b, 0x39c1, 0x789b, 0x0018, 0x79a8, 0xa184, 0x0080, 0x0148,
+ 0x0804, 0x3a0b, 0x7000, 0xa005, 0x1dd8, 0x2011, 0x0004, 0x0804,
+ 0x3594, 0xa184, 0x00ff, 0xa08a, 0x0010, 0x1a04, 0x3a0b, 0x0002,
+ 0x2cc2, 0x2cc0, 0x2cd4, 0x2cd8, 0x2d86, 0x3a0b, 0x3a0b, 0x2d88,
+ 0x3a0b, 0x3a0b, 0x2e86, 0x2e86, 0x3a0b, 0x3a0b, 0x3a0b, 0x2e88,
+ 0x080c, 0x254c, 0xd6e4, 0x0140, 0x2001, 0x0300, 0x8000, 0x8000,
+ 0x783a, 0x781b, 0x00c7, 0x0005, 0x6818, 0xd0fc, 0x0118, 0x681b,
+ 0x001d, 0x0c90, 0x0804, 0x3be5, 0x681b, 0x001d, 0x0804, 0x3a36,
+ 0x6920, 0x6922, 0xa684, 0x1800, 0x1904, 0x2d29, 0x6820, 0xd084,
+ 0x1904, 0x2d31, 0x6818, 0xa086, 0x0008, 0x1110, 0x681b, 0x0000,
+ 0xd6d4, 0x0568, 0xd6bc, 0x0558, 0x7083, 0x0000, 0x6818, 0xa084,
+ 0x003f, 0xa08a, 0x000d, 0x0718, 0xa08a, 0x000c, 0x7182, 0x2001,
+ 0x000c, 0x800c, 0x7186, 0x789b, 0x0061, 0x78aa, 0x0156, 0x0136,
+ 0x0146, 0x0016, 0x3208, 0xa18c, 0x0600, 0x0118, 0x20a1, 0x022b,
+ 0x0010, 0x20a1, 0x012b, 0x001e, 0x789b, 0x0000, 0x8000, 0x80ac,
+ 0xad80, 0x000b, 0x2098, 0x53a6, 0x014e, 0x013e, 0x015e, 0x6038,
+ 0xa005, 0x1150, 0x681c, 0xa084, 0x000e, 0x0904, 0x3a36, 0x080c,
+ 0x3a48, 0x782b, 0x3008, 0x0010, 0x8001, 0x603a, 0x781b, 0x0071,
+ 0x0005, 0xd6e4, 0x0130, 0x781b, 0x0083, 0x0005, 0x781b, 0x0083,
+ 0x0005, 0xa684, 0x0060, 0x0dd0, 0xd6dc, 0x0dc0, 0xd6fc, 0x01a0,
+ 0xc6fc, 0x7e5a, 0x6eb6, 0x7adc, 0x79d8, 0x78d0, 0x8007, 0xa084,
+ 0x007f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100, 0xa302, 0x68b2,
+ 0x6b94, 0x2200, 0xa303, 0x68ae, 0xd6f4, 0x0118, 0xc6f4, 0x7e5a,
+ 0x6eb6, 0x7000, 0xa086, 0x0003, 0x1148, 0x0006, 0x080c, 0x3f26,
+ 0x080c, 0x41d9, 0x000e, 0x781b, 0x0080, 0x0005, 0xa006, 0x080c,
+ 0x42b5, 0x6ab0, 0x69ac, 0x6c98, 0x6b94, 0x2200, 0xa105, 0x0120,
+ 0x2200, 0xa422, 0x2100, 0xa31b, 0x6caa, 0x7cd2, 0x7cda, 0x6ba6,
+ 0x7bd6, 0x7bde, 0x2300, 0xa405, 0x1130, 0xc6f5, 0x7e5a, 0x6eb6,
+ 0x781b, 0x0080, 0x0005, 0x781b, 0x0080, 0x2200, 0xa115, 0x1118,
+ 0x080c, 0x41d9, 0x0005, 0x080c, 0x4206, 0x0005, 0x080c, 0x254c,
+ 0x0804, 0x2e1c, 0x00c6, 0x7054, 0x2060, 0x6920, 0xa18c, 0xecff,
+ 0x6922, 0x6000, 0xa084, 0xcfdf, 0x6002, 0x080c, 0x38f4, 0xa006,
+ 0x2040, 0x2038, 0x080c, 0x399c, 0x0804, 0x2e10, 0x00c6, 0x7054,
+ 0x2060, 0x2c48, 0x7aa8, 0xa294, 0x00ff, 0xa286, 0x0004, 0x11d8,
+ 0x6920, 0xd1e4, 0x1170, 0x2039, 0x0000, 0x2041, 0x0000, 0x2031,
+ 0x0000, 0xa006, 0x2010, 0x080c, 0x38f7, 0x080c, 0x399c, 0x0804,
+ 0x2e10, 0xa18c, 0xecff, 0x6922, 0x6104, 0xa18c, 0xffdd, 0x6106,
+ 0x6000, 0xc0ac, 0x6002, 0xa286, 0x0003, 0x01d0, 0x6104, 0xa184,
+ 0x0010, 0x0548, 0x080c, 0x3b6b, 0x080c, 0x3977, 0x88ff, 0x0518,
+ 0x00ce, 0x789b, 0x0060, 0x2800, 0x78aa, 0x7e58, 0xc695, 0x7e5a,
+ 0xd6d4, 0x1118, 0x781b, 0x006e, 0x0005, 0x781b, 0x0082, 0x0005,
+ 0x6920, 0xd1cc, 0x0130, 0xa18c, 0xfdff, 0x6922, 0x6000, 0xc0ec,
+ 0x6002, 0x2039, 0x0000, 0x2041, 0x0000, 0x2031, 0x0000, 0xa006,
+ 0x2010, 0x080c, 0x399c, 0xa286, 0x0001, 0x0158, 0x6104, 0xa184,
+ 0x0008, 0x01b0, 0x080c, 0x3b6b, 0x080c, 0x3895, 0x88ff, 0x1980,
+ 0x0078, 0x6920, 0xd1c4, 0x0130, 0xa18c, 0xfeff, 0x6922, 0x6000,
+ 0xc0e4, 0x6002, 0x2031, 0x0000, 0xa006, 0x2010, 0x080c, 0x38f7,
+ 0x00ce, 0x7e58, 0xd6d4, 0x1118, 0x781b, 0x0071, 0x0005, 0x781b,
+ 0x0083, 0x0005, 0x0804, 0x3a32, 0x2808, 0x789b, 0x0080, 0x2019,
+ 0x0080, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001, 0x11b8, 0x2300,
+ 0xa102, 0xa086, 0x0001, 0x0904, 0x2d8a, 0x7ca8, 0xa4a4, 0x00ff,
+ 0xa480, 0x0002, 0xa300, 0x2018, 0xa102, 0x0a04, 0x2d9e, 0x0904,
+ 0x2d9e, 0x24a8, 0x7aa8, 0x1f04, 0x2e3a, 0x0c18, 0xa284, 0x00f0,
+ 0xa082, 0x0020, 0x06b8, 0x2200, 0xa082, 0x0021, 0x1698, 0x7aa8,
+ 0x8318, 0x8318, 0x2100, 0xa302, 0x0aa0, 0xa286, 0x0023, 0x0950,
+ 0x681c, 0xa084, 0xfff1, 0x681e, 0x7e58, 0xa684, 0xfff1, 0xc0a5,
+ 0x2030, 0x7e5a, 0x6008, 0xc0a5, 0x600a, 0x78a0, 0x8001, 0x0904,
+ 0x2e10, 0x20a8, 0x7998, 0x789b, 0x0060, 0x78aa, 0x2011, 0x0080,
+ 0x799a, 0x78a8, 0x7998, 0x7a9a, 0x78aa, 0x7a98, 0x1f04, 0x2e68,
+ 0xc695, 0x7e5a, 0xd6d4, 0x1118, 0x781b, 0x006e, 0x0005, 0x781b,
+ 0x0082, 0x0005, 0x8318, 0x2100, 0xa302, 0x0a04, 0x2e21, 0xa284,
+ 0x0080, 0x1904, 0x3a36, 0x78a0, 0xa005, 0x08c8, 0x0804, 0x3a36,
+ 0x0804, 0x3a0b, 0x7054, 0xa04d, 0x789b, 0x0018, 0x78a8, 0xa084,
+ 0x00ff, 0xa08e, 0x0001, 0x0110, 0x080c, 0x254c, 0x7aa8, 0xa294,
+ 0x00ff, 0x784b, 0x0008, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0005,
+ 0x1a04, 0x3a0b, 0x0002, 0x3a0b, 0x380c, 0x3a0b, 0x3927, 0x3d31,
+ 0xa282, 0x0000, 0x1110, 0x080c, 0x254c, 0x080c, 0x3a3c, 0x781b,
+ 0x0082, 0x0005, 0xa282, 0x0003, 0x1110, 0x080c, 0x254c, 0xd4fc,
+ 0x11d0, 0x7060, 0xa005, 0x0110, 0x080c, 0x254c, 0x6f14, 0x7772,
+ 0xa7bc, 0x8f00, 0x080c, 0x3b6f, 0x6008, 0xa085, 0x0021, 0x600a,
+ 0x8738, 0xa784, 0x001f, 0x1db0, 0x080c, 0x3a3f, 0x7063, 0x0002,
+ 0x701f, 0x0009, 0x0010, 0x080c, 0x3a4b, 0x781b, 0x0082, 0x0005,
+ 0xa282, 0x0004, 0x0310, 0x080c, 0x254c, 0x2300, 0x0002, 0x2ee2,
+ 0x3078, 0x30b4, 0xa286, 0x0003, 0x0598, 0x7200, 0x7cd8, 0x7ddc,
+ 0x7fd0, 0x71d0, 0xd1b4, 0x0528, 0xd1bc, 0x1518, 0x2001, 0x4601,
+ 0x2004, 0xd0c4, 0x11f0, 0x7868, 0xa084, 0x00ff, 0x11d0, 0xa282,
+ 0x0002, 0x12b8, 0x00d6, 0x783b, 0x8300, 0x781b, 0x0059, 0x70b8,
+ 0xa06d, 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2,
+ 0x78da, 0xc1b4, 0x71d2, 0x7003, 0x0030, 0x00de, 0x2001, 0x0000,
+ 0x0058, 0x783b, 0x1300, 0x781b, 0x0057, 0x2001, 0x0000, 0x0020,
+ 0x7200, 0x7cd8, 0x7ddc, 0x7fd0, 0x7046, 0x68a0, 0xd0ec, 0x0118,
+ 0x6008, 0xc08d, 0x600a, 0xa284, 0x000f, 0x0002, 0x3059, 0x2f33,
+ 0x2f30, 0x3184, 0x320f, 0x25a0, 0x2f2e, 0x2f2e, 0x080c, 0x254c,
+ 0x6008, 0xc0d4, 0x600a, 0xd6e4, 0x0120, 0x7044, 0xa086, 0x0014,
+ 0x11e8, 0x080c, 0x3f26, 0x2009, 0x0000, 0x6818, 0xd0fc, 0x0108,
+ 0x7044, 0xa086, 0x0014, 0x0168, 0x6818, 0xa086, 0x0008, 0x1904,
+ 0x301b, 0x7858, 0xd09c, 0x0904, 0x301b, 0x6820, 0xd0ac, 0x0904,
+ 0x301b, 0x681b, 0x0014, 0x2009, 0x0002, 0x04a8, 0x7868, 0xa08c,
+ 0x00ff, 0x0588, 0xa186, 0x0008, 0x1158, 0x6008, 0xc0a4, 0x600a,
+ 0x080c, 0x374f, 0x0540, 0x080c, 0x37be, 0x080c, 0x3f26, 0x0060,
+ 0xa186, 0x0028, 0x1500, 0x6018, 0xa005, 0x0d78, 0x8001, 0x0d68,
+ 0x8001, 0x0d58, 0x601e, 0x0c48, 0x6820, 0xd084, 0x0904, 0x25a0,
+ 0xc084, 0x6822, 0x080c, 0x2693, 0x7058, 0x00c6, 0x2060, 0x6800,
+ 0x6002, 0x00ce, 0x6004, 0x6802, 0xa005, 0x2d00, 0x1108, 0x6002,
+ 0x6006, 0x0804, 0x25a0, 0x0016, 0x81ff, 0x15f0, 0x7000, 0xa086,
+ 0x0030, 0x05d0, 0x71d0, 0xd1bc, 0x15b8, 0xd1b4, 0x11e8, 0x705c,
+ 0xa005, 0x1590, 0x70a0, 0xa086, 0x0001, 0x0570, 0x7003, 0x0000,
+ 0x0046, 0x0056, 0x0076, 0x0066, 0x00c6, 0x00d6, 0x080c, 0x25c5,
+ 0x00de, 0x00ce, 0x006e, 0x007e, 0x005e, 0x004e, 0x71d0, 0xd1b4,
+ 0x11d8, 0x7003, 0x0040, 0x00c0, 0x080c, 0x3c33, 0x11a8, 0x781b,
+ 0x0068, 0x00d6, 0x70b8, 0xa06d, 0x68b4, 0x785a, 0x6894, 0x78d6,
+ 0x78de, 0x6898, 0x78d2, 0x78da, 0xc1b4, 0x71d2, 0x7003, 0x0030,
+ 0x7808, 0xc08d, 0x780a, 0x00de, 0x080c, 0x30dc, 0x001e, 0x81ff,
+ 0x0904, 0x301b, 0xa684, 0xdf00, 0x681e, 0x682b, 0x0000, 0x6f14,
+ 0xa186, 0x0002, 0x1904, 0x301c, 0x6818, 0xa086, 0x0014, 0x1130,
+ 0x2008, 0xd6e4, 0x0118, 0x7868, 0xa08c, 0x00ff, 0x080c, 0x3a55,
+ 0x080c, 0x269e, 0x6820, 0xd0dc, 0x1578, 0x8717, 0xa294, 0x000f,
+ 0x8213, 0x8213, 0x8213, 0xb284, 0x0600, 0x0118, 0xa290, 0x4ac0,
+ 0x0010, 0xa290, 0x4b40, 0xa290, 0x0000, 0x221c, 0xd3c4, 0x0170,
+ 0x6820, 0xd0e4, 0x0128, 0xa084, 0xefff, 0x6822, 0xc3ac, 0x2312,
+ 0x8210, 0x2204, 0xa085, 0x0038, 0x2012, 0x8211, 0xd3d4, 0x0138,
+ 0x68a0, 0xd0c4, 0x1120, 0x080c, 0x3144, 0x0804, 0x25a0, 0x6008,
+ 0xc08d, 0x600a, 0x0008, 0x692a, 0x6916, 0x6818, 0xd0fc, 0x0110,
+ 0x7044, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x6410, 0x84ff, 0x0168,
+ 0x2009, 0x4602, 0x2104, 0x8001, 0x200a, 0x8421, 0x6412, 0x1128,
+ 0x2021, 0x4604, 0x2404, 0xc0a5, 0x2022, 0x6018, 0xa005, 0x0118,
+ 0x8001, 0x601a, 0x1118, 0x6008, 0xc0a4, 0x600a, 0x6820, 0xd084,
+ 0x1130, 0x6800, 0xa005, 0x1108, 0x6002, 0x6006, 0x0020, 0x7058,
+ 0x2060, 0x6800, 0x6002, 0x2061, 0x4600, 0x6887, 0x0103, 0x2d08,
+ 0x206b, 0x0000, 0x6068, 0xa005, 0x616a, 0x0110, 0x2d02, 0x0008,
+ 0x616e, 0x7200, 0xa286, 0x0030, 0x0158, 0xa286, 0x0040, 0x1904,
+ 0x25a0, 0x7003, 0x0002, 0x7048, 0x2068, 0x68c4, 0x2060, 0x0005,
+ 0x7003, 0x0002, 0x70b8, 0xa06d, 0x68bc, 0x703e, 0x70b4, 0xa065,
+ 0x68c0, 0x7056, 0x2d00, 0x704a, 0xad80, 0x0009, 0x7042, 0x0005,
+ 0xa282, 0x0004, 0x0210, 0x080c, 0x254c, 0x2200, 0x0002, 0x3083,
+ 0x3092, 0x309e, 0x3092, 0xa586, 0x1300, 0x0160, 0xa586, 0x8300,
+ 0x1d90, 0x7003, 0x0000, 0x6018, 0x8001, 0x601a, 0x6008, 0xa084,
+ 0xfbef, 0x600a, 0x7000, 0xa086, 0x0005, 0x0128, 0x080c, 0x3a3c,
+ 0x781b, 0x0082, 0x0005, 0x781b, 0x0083, 0x0005, 0x7890, 0x8007,
+ 0x8001, 0xa084, 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c,
+ 0x00ff, 0xa186, 0x0003, 0x0128, 0xa186, 0x0000, 0x0110, 0x0804,
+ 0x3a0b, 0x781b, 0x0083, 0x0005, 0x6820, 0xc095, 0x6822, 0x82ff,
+ 0x1118, 0x080c, 0x3a3c, 0x0030, 0x8211, 0x0110, 0x080c, 0x254c,
+ 0x080c, 0x3a4b, 0x781b, 0x0082, 0x0005, 0x080c, 0x3c46, 0x7830,
+ 0xa084, 0x00c0, 0x1170, 0x0016, 0x3208, 0xa18c, 0x0800, 0x001e,
+ 0x0118, 0x0104, 0x30d9, 0x0010, 0x0304, 0x30d9, 0x791a, 0xa006,
+ 0x0005, 0xa085, 0x0001, 0x0005, 0xa684, 0x0060, 0x1130, 0x682f,
+ 0x0000, 0x6833, 0x0000, 0x0804, 0x3143, 0xd6dc, 0x1198, 0x68b4,
+ 0xd0dc, 0x1180, 0x6998, 0x6a94, 0x692e, 0x6a32, 0x7044, 0xa005,
+ 0x1130, 0x2200, 0xa105, 0x0904, 0x3f26, 0x7047, 0x0015, 0x0804,
+ 0x3f26, 0x0005, 0xd6ac, 0x01f0, 0xd6f4, 0x0130, 0x682f, 0x0000,
+ 0x6833, 0x0000, 0x0804, 0x3f26, 0x68b4, 0xa084, 0x4000, 0xa635,
+ 0xd6f4, 0x1da0, 0x7044, 0xa005, 0x1110, 0x7047, 0x0015, 0xd6dc,
+ 0x1128, 0x68b4, 0xd0dc, 0x0110, 0x6ca8, 0x6da4, 0x6c2e, 0x6d32,
+ 0x0804, 0x3f26, 0xd6f4, 0x0130, 0x682f, 0x0000, 0x6833, 0x0000,
+ 0x0804, 0x3f26, 0x68b4, 0xa084, 0x4800, 0xa635, 0xd6f4, 0x1da0,
+ 0x7044, 0xa005, 0x1110, 0x7047, 0x0015, 0x2408, 0x2510, 0x2700,
+ 0x8007, 0xa084, 0x007f, 0xa108, 0xa291, 0x0000, 0x692e, 0x6a32,
+ 0x2100, 0xa205, 0x1110, 0x0804, 0x3f26, 0x7000, 0xa086, 0x0006,
+ 0x0110, 0x0804, 0x3f26, 0x0005, 0x6946, 0x6008, 0xc0cd, 0xd3cc,
+ 0x0108, 0xc08d, 0x600a, 0x6818, 0x683a, 0x681b, 0x0006, 0x688f,
+ 0x0000, 0x6893, 0x0000, 0x6a30, 0x692c, 0x6a3e, 0x6942, 0x682f,
+ 0x0003, 0x6833, 0x0000, 0x6837, 0x0020, 0x6897, 0x0000, 0x689b,
+ 0x0020, 0x7000, 0x0002, 0x25a0, 0x3173, 0x316d, 0x316b, 0x316b,
+ 0x316b, 0x316b, 0x316b, 0x080c, 0x254c, 0x6820, 0xd084, 0x1118,
+ 0x080c, 0x37a4, 0x0030, 0x7058, 0x2c50, 0x2060, 0x6800, 0x6002,
+ 0x2a60, 0xaea0, 0x0017, 0x2404, 0xa005, 0x0110, 0x2020, 0x0cd8,
+ 0x2d22, 0x206b, 0x0000, 0x0005, 0x080c, 0x37aa, 0x080c, 0x37be,
+ 0x6008, 0xc0cc, 0x600a, 0x682b, 0x0000, 0x789b, 0x000e, 0x6f14,
+ 0x6938, 0x691a, 0x6944, 0x6916, 0x2009, 0x0000, 0xae86, 0x4640,
+ 0x0110, 0x2009, 0x0001, 0x080c, 0x42ec, 0xd6dc, 0x01c8, 0x691c,
+ 0xc1ed, 0x691e, 0x6828, 0xa082, 0x000e, 0x0290, 0x6848, 0xa084,
+ 0x000f, 0xa086, 0x000b, 0x1160, 0x685c, 0xa086, 0x0047, 0x1140,
+ 0x2001, 0x4601, 0x2004, 0xd0ac, 0x1118, 0x2700, 0x080c, 0x2475,
+ 0x6818, 0xd0fc, 0x0140, 0x681b, 0x0000, 0x7868, 0xa08c, 0x00ff,
+ 0x0110, 0x681b, 0x001e, 0xaea0, 0x0017, 0x6800, 0x2022, 0x6a3c,
+ 0x6940, 0x6a32, 0x692e, 0x68c0, 0x2060, 0x6000, 0xd0a4, 0x0580,
+ 0x2041, 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x00d6, 0x00f6,
+ 0x0156, 0x0146, 0x2079, 0x4600, 0x080c, 0x1b93, 0x014e, 0x015e,
+ 0x00fe, 0x70c8, 0x2010, 0x2009, 0x0101, 0x0026, 0x2204, 0xa06d,
+ 0x0140, 0x6814, 0xa706, 0x0110, 0x6800, 0x0cc8, 0x6820, 0xc0d5,
+ 0x6822, 0x002e, 0x8210, 0x8109, 0x1d80, 0x00de, 0x7063, 0x0003,
+ 0x707b, 0x0000, 0x7772, 0x707f, 0x000f, 0x71d0, 0xc1c4, 0x71d2,
+ 0x6818, 0xa086, 0x0002, 0x1138, 0x6817, 0x0000, 0x682b, 0x0000,
+ 0x681c, 0xc0ec, 0x681e, 0x080c, 0x1da2, 0x0804, 0x25a0, 0x7cd8,
+ 0x7ddc, 0x7fd0, 0x080c, 0x30dc, 0x682b, 0x0000, 0x789b, 0x000e,
+ 0x6f14, 0x080c, 0x3c4a, 0xa08c, 0x00ff, 0x6916, 0x6818, 0xd0fc,
+ 0x0110, 0x7044, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x7063, 0x0000,
+ 0x0804, 0x25a0, 0x7000, 0xa005, 0x1110, 0x0804, 0x25a0, 0xa006,
+ 0x080c, 0x3f26, 0x6920, 0xd1ac, 0x1110, 0x681b, 0x0014, 0xa68c,
+ 0xdf00, 0x691e, 0x682b, 0x0000, 0x6820, 0xa084, 0x00ff, 0x6822,
+ 0x7000, 0x0002, 0x25a0, 0x324c, 0x324c, 0x324f, 0x324f, 0x324f,
+ 0x324a, 0x324a, 0x080c, 0x254c, 0x6818, 0x0804, 0x2f18, 0x6008,
+ 0xc0a4, 0x600a, 0x6817, 0x0000, 0x0804, 0x3772, 0x2300, 0x0002,
+ 0x325b, 0x325d, 0x32ab, 0x080c, 0x254c, 0xd6fc, 0x1904, 0x2d38,
+ 0x7000, 0xa00d, 0x0002, 0x25a0, 0x326d, 0x326d, 0x3297, 0x326d,
+ 0x32a8, 0x326b, 0x326b, 0x080c, 0x254c, 0xa684, 0x0060, 0x0538,
+ 0xa086, 0x0060, 0x1510, 0xc6ac, 0xc6f4, 0xc6ed, 0x7e5a, 0x6eb6,
+ 0x681c, 0xc0ac, 0x681e, 0xa186, 0x0002, 0x0148, 0x080c, 0x3f26,
+ 0x69ac, 0x68b0, 0xa115, 0x0118, 0x080c, 0x4206, 0x0010, 0x080c,
+ 0x41d9, 0x781b, 0x0083, 0x71d0, 0xd1b4, 0x1904, 0x259d, 0x70a0,
+ 0xa086, 0x0001, 0x1904, 0x25e1, 0x0005, 0xd6ec, 0x09f0, 0x6818,
+ 0xd0fc, 0x0170, 0xd6f4, 0x1130, 0x681b, 0x0015, 0x781b, 0x0083,
+ 0x0804, 0x259d, 0x681b, 0x0007, 0x682f, 0x0000, 0x6833, 0x0000,
+ 0x080c, 0x3bf1, 0x0005, 0x080c, 0x254c, 0x2300, 0x0002, 0x32b4,
+ 0x32d6, 0x332e, 0x080c, 0x254c, 0x7000, 0x0002, 0x32be, 0x32c0,
+ 0x32c7, 0x32be, 0x32be, 0x32be, 0x32be, 0x32be, 0x080c, 0x254c,
+ 0x69ac, 0x68b0, 0xa115, 0x0118, 0x080c, 0x4206, 0x0010, 0x080c,
+ 0x41d9, 0x681c, 0xc0b4, 0x681e, 0x70d0, 0xd0b4, 0x1904, 0x259d,
+ 0x70a0, 0xa086, 0x0001, 0x1904, 0x25e1, 0x0005, 0xd6fc, 0x1904,
+ 0x331e, 0x7000, 0xa00d, 0x0002, 0x25a0, 0x32ec, 0x32e6, 0x3316,
+ 0x32ec, 0x331b, 0x32e4, 0x32e4, 0x080c, 0x254c, 0x6894, 0x78d6,
+ 0x78de, 0x6898, 0x78d2, 0x78da, 0xa684, 0x0060, 0x0538, 0xa086,
+ 0x0060, 0x1510, 0xa6b4, 0xbfbf, 0xc6ed, 0x7e5a, 0x6eb6, 0xa186,
+ 0x0002, 0x0148, 0x080c, 0x3f26, 0x69ac, 0x68b0, 0xa115, 0x0118,
+ 0x080c, 0x4206, 0x0010, 0x080c, 0x41d9, 0x781b, 0x0083, 0x681c,
+ 0xc0b4, 0x681e, 0x71d0, 0xd1b4, 0x1904, 0x259d, 0x70a0, 0xa086,
+ 0x0001, 0x1904, 0x25e1, 0x0005, 0xd6ec, 0x09f0, 0x6818, 0xd0fc,
+ 0x0110, 0x681b, 0x0007, 0x781b, 0x00fb, 0x0005, 0xc6fc, 0x7e5a,
+ 0x7adc, 0x79d8, 0x6b98, 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200,
+ 0xa303, 0x68ae, 0x79d2, 0x781b, 0x0083, 0x0005, 0xd6dc, 0x0130,
+ 0x782b, 0x3009, 0x781b, 0x0083, 0x0804, 0x259d, 0x7884, 0xc0ac,
+ 0x7886, 0x78e4, 0xa084, 0x0008, 0x1150, 0xa484, 0x0200, 0x0108,
+ 0xc6f5, 0xc6dd, 0x7e5a, 0x781b, 0x0083, 0x0804, 0x259d, 0x6820,
+ 0xc095, 0x6822, 0x080c, 0x3bdc, 0xc6dd, 0x080c, 0x3a3c, 0x781b,
+ 0x0082, 0x0804, 0x259d, 0x2300, 0x0002, 0x3358, 0x335a, 0x335c,
+ 0x080c, 0x254c, 0x0804, 0x3a36, 0x7d98, 0xd6d4, 0x15a8, 0x79e4,
+ 0xd1ac, 0x0130, 0x78ec, 0xa084, 0x0003, 0x0110, 0x782b, 0x3009,
+ 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x7d9a,
+ 0x79e4, 0xd1ac, 0x0120, 0x78ec, 0xa084, 0x0003, 0x1120, 0x2001,
+ 0x0014, 0x0804, 0x2f18, 0x7884, 0xd0fc, 0x1118, 0xa184, 0x0007,
+ 0x0090, 0xa184, 0x0007, 0xa086, 0x0004, 0x1118, 0x2001, 0x0000,
+ 0x0050, 0xa184, 0x0007, 0xa086, 0x0005, 0x0118, 0xa184, 0x0007,
+ 0x0010, 0x2001, 0x0001, 0x04c2, 0x7a90, 0xa294, 0x0007, 0x789b,
+ 0x0060, 0x79a8, 0x81ff, 0x0568, 0x789b, 0x0080, 0x7ba8, 0xa384,
+ 0x0001, 0x11d0, 0x7ba8, 0x7ba8, 0xa386, 0x0004, 0x1118, 0x2009,
+ 0xffdf, 0x0058, 0xa386, 0x0001, 0x1118, 0x2009, 0xfff7, 0x0028,
+ 0xa386, 0x0003, 0x1148, 0x2009, 0xffef, 0x00c6, 0x7054, 0x2060,
+ 0x6004, 0xa104, 0x6006, 0x00ce, 0x789b, 0x0060, 0x78ab, 0x0000,
+ 0xa684, 0xfffb, 0x785a, 0x782b, 0x3009, 0x6920, 0xa18c, 0xecff,
+ 0x6922, 0x7d9a, 0x0804, 0x3be5, 0x2bae, 0x2bb7, 0x33d6, 0x33dc,
+ 0x33d4, 0x33d4, 0x3be5, 0x3be5, 0x080c, 0x254c, 0x6920, 0xa18c,
+ 0xfcff, 0x6922, 0x0804, 0x3beb, 0x6920, 0xa18c, 0xfcff, 0x6922,
+ 0x0804, 0x3be5, 0x79e4, 0xa184, 0x0030, 0x0120, 0x78ec, 0xa084,
+ 0x0003, 0x1570, 0x7000, 0xa086, 0x0004, 0x1190, 0x7060, 0xa086,
+ 0x0002, 0x1130, 0x2011, 0x0002, 0x2019, 0x0000, 0x0804, 0x2a56,
+ 0x7060, 0xa086, 0x0006, 0x0db0, 0x7060, 0xa086, 0x0004, 0x0d90,
+ 0x7000, 0xa086, 0x0000, 0x0904, 0x259d, 0x6920, 0xa184, 0x0420,
+ 0x0128, 0xc1d4, 0x6922, 0x6818, 0x0804, 0x2f18, 0x6818, 0xa08e,
+ 0x0002, 0x0120, 0xc0fd, 0x681a, 0x2001, 0x0014, 0x0804, 0x2f18,
+ 0x7884, 0xd0fc, 0x1118, 0xa184, 0x0007, 0x0090, 0xa184, 0x0007,
+ 0xa086, 0x0004, 0x1118, 0x2001, 0x0000, 0x0050, 0xa184, 0x0007,
+ 0xa086, 0x0005, 0x0118, 0xa184, 0x0007, 0x0010, 0x2001, 0x0001,
+ 0x0002, 0x3be5, 0x3be5, 0x3439, 0x3be5, 0x3c29, 0x3c29, 0x3be5,
+ 0x3be5, 0xd6bc, 0x0570, 0x7180, 0x81ff, 0x0558, 0xa182, 0x000d,
+ 0x1318, 0x7083, 0x0000, 0x0028, 0xa182, 0x000c, 0x7082, 0x2009,
+ 0x000c, 0x789b, 0x0061, 0x79aa, 0x0156, 0x0136, 0x0146, 0x7084,
+ 0x8114, 0xa210, 0x7286, 0xa080, 0x000b, 0xad00, 0x2098, 0xb284,
+ 0x0600, 0x0118, 0x20a1, 0x022b, 0x0010, 0x20a1, 0x012b, 0x789b,
+ 0x0000, 0x8108, 0x81ac, 0x53a6, 0x014e, 0x013e, 0x015e, 0x0804,
+ 0x3beb, 0xd6d4, 0x1904, 0x34ac, 0x6820, 0xd084, 0x0904, 0x3beb,
+ 0xa68c, 0x0060, 0xa684, 0x0060, 0x0120, 0xa086, 0x0060, 0x1108,
+ 0xc1f5, 0xc194, 0x795a, 0x69b6, 0x789b, 0x0060, 0x78ab, 0x0000,
+ 0x789b, 0x0061, 0x6818, 0xc0fd, 0x681a, 0x78aa, 0x8008, 0x810c,
+ 0x0904, 0x37d3, 0xa18c, 0x00f8, 0x1904, 0x37d3, 0x0156, 0x0136,
+ 0x0146, 0x0016, 0x20a1, 0x012b, 0x3208, 0xa18c, 0x0600, 0x0110,
+ 0x20a1, 0x022b, 0x001e, 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80,
+ 0x000b, 0x2098, 0x53a6, 0x014e, 0x013e, 0x015e, 0x6814, 0xc0fc,
+ 0x8007, 0x7882, 0x0804, 0x3beb, 0x6818, 0xd0fc, 0x0110, 0x681b,
+ 0x0008, 0x080c, 0x3a3c, 0x781b, 0x00ed, 0x0005, 0x2300, 0x0002,
+ 0x34bd, 0x357a, 0x34bb, 0x080c, 0x254c, 0x7cd8, 0x7ddc, 0x7fd0,
+ 0x82ff, 0x1528, 0x7200, 0xa286, 0x0003, 0x0904, 0x2ee6, 0x71d0,
+ 0xd1bc, 0x11f8, 0xd1b4, 0x01e8, 0x2001, 0x4601, 0x2004, 0xd0c4,
+ 0x11c0, 0x00d6, 0x783b, 0x8800, 0x781b, 0x0059, 0x70b8, 0xa06d,
+ 0x68b4, 0xc0a5, 0x785a, 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2,
+ 0x78da, 0xc1b4, 0x71d2, 0x7003, 0x0030, 0x00de, 0x0030, 0x7200,
+ 0x0020, 0x783b, 0x1800, 0x781b, 0x0057, 0xa284, 0x000f, 0x0002,
+ 0x3565, 0x3522, 0x34fa, 0x2f15, 0x34f8, 0x3565, 0x34f8, 0x34f8,
+ 0x080c, 0x254c, 0x681c, 0xd0ec, 0x0118, 0x6008, 0xc08d, 0x600a,
+ 0x6920, 0xc185, 0x6922, 0x6800, 0x6006, 0xa005, 0x1108, 0x6002,
+ 0x6008, 0xc0d4, 0x600a, 0x681c, 0xa084, 0x000e, 0x1120, 0x71c8,
+ 0xa188, 0x0100, 0x0028, 0x7030, 0x68ba, 0x713c, 0x70c8, 0xa108,
+ 0x2104, 0x6802, 0x2d0a, 0x715a, 0xd6dc, 0x1120, 0xc6fc, 0x6eb6,
+ 0x0804, 0x3565, 0x6eb6, 0xa684, 0x0060, 0x1120, 0xa684, 0x7fff,
+ 0x68b6, 0x04d8, 0xd6dc, 0x1150, 0xa684, 0x7fff, 0x68b6, 0x6894,
+ 0x68a6, 0x6898, 0x68aa, 0x080c, 0x3f26, 0x0478, 0xd6ac, 0x0140,
+ 0xa006, 0x080c, 0x3f26, 0x2408, 0x2510, 0x69aa, 0x6aa6, 0x0068,
+ 0x2408, 0x2510, 0x2700, 0x8007, 0xa084, 0x007f, 0xa108, 0xa291,
+ 0x0000, 0x69aa, 0x6aa6, 0x080c, 0x3f26, 0xd6fc, 0x01b0, 0xa684,
+ 0x7fff, 0x68b6, 0x2510, 0x2408, 0xd6ac, 0x1138, 0x2700, 0x8007,
+ 0xa084, 0x007f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100, 0xa302,
+ 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae, 0x7000, 0xa086, 0x0030,
+ 0x1904, 0x25a0, 0x7003, 0x0002, 0x70b8, 0xa06d, 0x68bc, 0x703e,
+ 0x70b4, 0xa065, 0x68c0, 0x7056, 0x2d00, 0x704a, 0xad80, 0x0009,
+ 0x7042, 0x0005, 0xa586, 0x8800, 0x1148, 0x7003, 0x0000, 0x6018,
+ 0x8001, 0x601a, 0x6008, 0xa084, 0xfbef, 0x600a, 0x0804, 0x3a36,
+ 0x7043, 0x0000, 0xa282, 0x0006, 0x0310, 0x080c, 0x254c, 0x2300,
+ 0x0002, 0x3594, 0x35a5, 0x35af, 0x2200, 0x0002, 0x359c, 0x3a36,
+ 0x359e, 0x359c, 0x35e0, 0x362e, 0x080c, 0x254c, 0x7a80, 0xa294,
+ 0x0f00, 0x080c, 0x3682, 0x0804, 0x3a0b, 0x00c1, 0x0002, 0x3a36,
+ 0x35ad, 0x35ad, 0x35e0, 0x35ad, 0x3a36, 0x080c, 0x254c, 0x0071,
+ 0x0002, 0x35b9, 0x35b7, 0x35b7, 0x35b9, 0x35b7, 0x35b9, 0x080c,
+ 0x254c, 0x080c, 0x3a4b, 0x781b, 0x0082, 0x0005, 0x7000, 0xa086,
+ 0x0002, 0x1150, 0x080c, 0x37be, 0x0010, 0x080c, 0x3f26, 0x6008,
+ 0xa084, 0xfbef, 0x600a, 0x0020, 0x7000, 0xa086, 0x0003, 0x0da8,
+ 0x7003, 0x0005, 0x2001, 0x8de0, 0xae8e, 0x4640, 0x0110, 0x2001,
+ 0x8e12, 0x2068, 0x704a, 0xad80, 0x0009, 0x7042, 0x2200, 0x0005,
+ 0x7000, 0xa086, 0x0002, 0x1158, 0x70d0, 0xc0b5, 0x70d2, 0x2c00,
+ 0x70b6, 0x2d00, 0x70ba, 0x0038, 0x080c, 0x3f26, 0x0020, 0x7000,
+ 0xa086, 0x0003, 0x0dc8, 0x7003, 0x0001, 0x7a80, 0xa294, 0x0f00,
+ 0x789b, 0x0018, 0x7ca8, 0xa484, 0x001f, 0xa215, 0x2069, 0x8cc0,
+ 0xb284, 0x0600, 0x1118, 0xc2fd, 0x2069, 0x8dd0, 0x2d04, 0x2d08,
+ 0x715a, 0xa06d, 0x0128, 0x6814, 0xa206, 0x0120, 0x6800, 0x0cb8,
+ 0x080c, 0x3682, 0x6eb4, 0x7e5a, 0x6920, 0xa184, 0x0c00, 0x0904,
+ 0x36a8, 0x7060, 0xa086, 0x0006, 0x1128, 0x7070, 0xa206, 0x1110,
+ 0x7062, 0x707a, 0x681b, 0x0005, 0xc1ad, 0x681b, 0x0005, 0xc1ad,
+ 0xc1d4, 0x6922, 0x080c, 0x3a42, 0x0804, 0x36a8, 0x7200, 0xa286,
+ 0x0002, 0x1158, 0x70d0, 0xc0b5, 0x70d2, 0x2c00, 0x70b6, 0x2d00,
+ 0x70ba, 0x0030, 0x080c, 0x3f26, 0x0018, 0xa286, 0x0003, 0x0dd0,
+ 0x7003, 0x0001, 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8,
+ 0xa484, 0x001f, 0xa215, 0xae86, 0x4640, 0x0108, 0xc2fd, 0x79a8,
+ 0x79a8, 0xa18c, 0x00ff, 0x2118, 0x70c8, 0xa168, 0x2d04, 0x2d08,
+ 0x715a, 0xa06d, 0x0128, 0x6814, 0xa206, 0x0118, 0x6800, 0x0cb8,
+ 0x0409, 0x6eb4, 0x6920, 0xa184, 0x0c00, 0x0904, 0x36a8, 0xd0dc,
+ 0x0178, 0x7060, 0xa086, 0x0004, 0x1140, 0x7070, 0xa206, 0x1128,
+ 0x7074, 0xa306, 0x1110, 0x7062, 0x707a, 0x080c, 0x3a48, 0x0480,
+ 0x681b, 0x0005, 0xc1ad, 0xc1d4, 0x6922, 0x080c, 0x3a42, 0x707b,
+ 0x0000, 0x0430, 0x7003, 0x0005, 0xb284, 0x0600, 0x0118, 0x2001,
+ 0x8de0, 0x0010, 0x2001, 0x8e12, 0x2068, 0x704a, 0x0156, 0x20a9,
+ 0x0032, 0x2003, 0x0000, 0x8000, 0x1f04, 0x3691, 0x015e, 0xb284,
+ 0x0600, 0x0110, 0xc2fc, 0x0008, 0xc2fd, 0x6a16, 0xad80, 0x0009,
+ 0x7042, 0x68b7, 0x0700, 0x6823, 0x0800, 0x6827, 0x0003, 0x0005,
+ 0xc6ec, 0xa6ac, 0x0060, 0x0904, 0x36ef, 0x6b98, 0x6c94, 0x69ac,
+ 0x68b0, 0xa105, 0x11e0, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0xa586,
+ 0x0060, 0x05c8, 0xd6f4, 0x1108, 0xc6ed, 0xa6b4, 0xb7ff, 0x7e5a,
+ 0x2009, 0x0083, 0xd69c, 0x0128, 0x2009, 0x0082, 0x2019, 0x0000,
+ 0x2320, 0x791a, 0xd6ec, 0x0588, 0x080c, 0x41d9, 0x0470, 0x68b0,
+ 0xa31a, 0x2100, 0xa423, 0x2400, 0xa305, 0x01f8, 0x7bd2, 0x7bda,
+ 0x7cd6, 0x7cde, 0x68b0, 0xd6f4, 0x1108, 0xc6ed, 0xc6f4, 0x7e5a,
+ 0x2011, 0x0083, 0xd69c, 0x0128, 0x2011, 0x0082, 0x2019, 0x0000,
+ 0x2320, 0x7a1a, 0xd6ec, 0x0188, 0x080c, 0x4206, 0x0070, 0x2019,
+ 0x0000, 0x2320, 0x0010, 0xa6b4, 0xb7ff, 0x7e5a, 0x2009, 0x0083,
+ 0xd69c, 0x0110, 0x2009, 0x0082, 0x791a, 0x68c0, 0x7056, 0x2d00,
+ 0x704a, 0x68c4, 0x2060, 0x71d0, 0x2001, 0x4601, 0x2004, 0xd0c4,
+ 0x15c8, 0x70d4, 0xa02d, 0x01b8, 0xd1bc, 0x0548, 0x7a80, 0xa294,
+ 0x0f00, 0x70d8, 0xa206, 0x0118, 0x78e0, 0xa504, 0x1558, 0x70d6,
+ 0xc1bc, 0x71d2, 0x0438, 0x2031, 0x0001, 0x852c, 0x0218, 0x8633,
+ 0x8210, 0x0cd8, 0x0005, 0x7de0, 0xa594, 0xff00, 0x0130, 0x2011,
+ 0x0008, 0x852f, 0x0c81, 0x8637, 0x0008, 0x0c69, 0x8217, 0x7880,
+ 0xa084, 0x0f00, 0xa206, 0x0170, 0x72da, 0x76d6, 0x0058, 0x7a80,
+ 0xa294, 0x0f00, 0x70d8, 0xa236, 0x0dc0, 0x78e0, 0xa534, 0x0da8,
+ 0xc1bd, 0x71d2, 0xd1b4, 0x1904, 0x259d, 0x2300, 0xa405, 0x0904,
+ 0x259d, 0x70a0, 0xa086, 0x0001, 0x1904, 0x25e1, 0x0005, 0x6020,
+ 0xa005, 0x0150, 0x8001, 0x6022, 0x6008, 0xa085, 0x0008, 0x600a,
+ 0x700f, 0x0100, 0x702c, 0x6026, 0x0005, 0xa006, 0x080c, 0x3f26,
+ 0x7000, 0xa086, 0x0002, 0x0120, 0x7060, 0xa086, 0x0005, 0x1150,
+ 0x682b, 0x0000, 0x6817, 0x0000, 0x681b, 0x0001, 0x6823, 0x0040,
+ 0x681f, 0x0100, 0x7000, 0xa084, 0x000f, 0x0002, 0x25a0, 0x3783,
+ 0x3780, 0x37a0, 0x378c, 0x25a0, 0x377e, 0x377e, 0x080c, 0x254c,
+ 0x0449, 0x0411, 0x0028, 0x0431, 0x7058, 0x2060, 0x6800, 0x6002,
+ 0x080c, 0x1da2, 0x0804, 0x25a0, 0x7060, 0x7063, 0x0000, 0x707f,
+ 0x0000, 0x0002, 0x379c, 0x379c, 0x379a, 0x379a, 0x379a, 0x379c,
+ 0x379a, 0x379c, 0x0804, 0x2a6b, 0x7063, 0x0000, 0x0804, 0x25a0,
+ 0x681b, 0x0000, 0x0804, 0x3184, 0x6800, 0xa005, 0x1108, 0x6002,
+ 0x6006, 0x0005, 0x6410, 0x84ff, 0x0168, 0x2009, 0x4602, 0x2104,
+ 0x8001, 0x200a, 0x8421, 0x6412, 0x1128, 0x2021, 0x4604, 0x2404,
+ 0xc0a5, 0x2022, 0x6008, 0xc0a4, 0x600a, 0x0005, 0x6018, 0xa005,
+ 0x0110, 0x8001, 0x601a, 0x0005, 0x080c, 0x3c46, 0x681b, 0x0018,
+ 0x0490, 0x080c, 0x3c46, 0x681b, 0x0019, 0x0468, 0x080c, 0x3c46,
+ 0x681b, 0x001a, 0x0440, 0x080c, 0x3c46, 0x681b, 0x0003, 0x0418,
+ 0x7770, 0x080c, 0x3b6f, 0x7174, 0xa18c, 0x00ff, 0x3210, 0xa294,
+ 0x0600, 0x0118, 0xa1e8, 0x8bc0, 0x0010, 0xa1e8, 0x8cd0, 0x2d04,
+ 0x2d08, 0x2068, 0xa005, 0x1118, 0x707a, 0x0804, 0x25a0, 0x6814,
+ 0x7270, 0xa206, 0x0110, 0x6800, 0x0c98, 0x6800, 0x200a, 0x681b,
+ 0x0005, 0x707b, 0x0000, 0x080c, 0x37aa, 0x6820, 0xd084, 0x1110,
+ 0x080c, 0x37a4, 0x080c, 0x37be, 0x681f, 0x0000, 0x6823, 0x0020,
+ 0x080c, 0x1da2, 0x0804, 0x25a0, 0xa282, 0x0003, 0x1904, 0x3a10,
+ 0x7da8, 0xa5ac, 0x00ff, 0x7ea8, 0xa6b4, 0x00ff, 0x6920, 0xc1bd,
+ 0x6922, 0xd1c4, 0x05b0, 0xc1c4, 0x6922, 0xa6b4, 0x00ff, 0x0530,
+ 0xa682, 0x0018, 0x0218, 0x0110, 0x2031, 0x0018, 0xa686, 0x0010,
+ 0x1108, 0x8630, 0x852b, 0x852b, 0x2041, 0x0000, 0x080c, 0x3ac9,
+ 0x0118, 0x080c, 0x38f7, 0x00a0, 0x080c, 0x3a95, 0x080c, 0x38f4,
+ 0x6920, 0xc1c5, 0x6922, 0x7e58, 0xc695, 0x7e5a, 0xd6d4, 0x1118,
+ 0x781b, 0x006e, 0x0005, 0x781b, 0x0082, 0x0005, 0x080c, 0x38f4,
+ 0x7e58, 0xd6d4, 0x1118, 0x781b, 0x0071, 0x0005, 0x781b, 0x0083,
+ 0x0005, 0x00c6, 0x7054, 0x2060, 0x6100, 0xd1e4, 0x0598, 0x6208,
+ 0x8217, 0xa294, 0x00ff, 0xa282, 0x0018, 0x0218, 0x0110, 0x2011,
+ 0x0018, 0x2600, 0xa202, 0x1208, 0x2230, 0xa686, 0x0010, 0x1108,
+ 0x8630, 0x6208, 0xa294, 0x00ff, 0x78ec, 0xd0e4, 0x0130, 0xa282,
+ 0x000a, 0x1240, 0x2011, 0x000a, 0x0028, 0xa282, 0x000c, 0x1210,
+ 0x2011, 0x000c, 0x2200, 0xa502, 0x1208, 0x2228, 0x080c, 0x3a99,
+ 0x852b, 0x852b, 0x2041, 0x0000, 0x080c, 0x3ac9, 0x0118, 0x080c,
+ 0x38f7, 0x0020, 0x080c, 0x3a95, 0x080c, 0x38f4, 0x7858, 0xc095,
+ 0x785a, 0x00ce, 0x781b, 0x0082, 0x0005, 0x00c6, 0x2960, 0x6000,
+ 0xd0e4, 0x1188, 0xd0b4, 0x1150, 0x6010, 0xa084, 0x000f, 0x1130,
+ 0x6104, 0xa18c, 0xfff5, 0x6106, 0x00ce, 0x0005, 0x2011, 0x0032,
+ 0x2019, 0x0000, 0x00f0, 0x68a0, 0xd0cc, 0x1dc0, 0x6208, 0xa294,
+ 0x00ff, 0x78ec, 0xd0e4, 0x0130, 0xa282, 0x000b, 0x1218, 0x2011,
+ 0x000a, 0x0028, 0xa282, 0x000c, 0x1210, 0x2011, 0x000c, 0x6308,
+ 0x831f, 0xa39c, 0x00ff, 0xa382, 0x0018, 0x0218, 0x0110, 0x2019,
+ 0x0018, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa,
+ 0x7baa, 0xa8c0, 0x0005, 0x6820, 0xc0c5, 0x6822, 0x080c, 0x3a55,
+ 0x00ce, 0x0005, 0x00c6, 0x2960, 0x6104, 0xa18c, 0xfff5, 0x6106,
+ 0x2011, 0x0032, 0x2019, 0x0000, 0x0000, 0x78ab, 0x0001, 0x78ab,
+ 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005, 0x6820,
+ 0xc0c5, 0x6822, 0x00ce, 0x0005, 0xa006, 0x2030, 0x2010, 0x00c6,
+ 0x7154, 0x2160, 0x2018, 0x2008, 0xa084, 0xffe0, 0xa635, 0x7e86,
+ 0x6018, 0x789a, 0x7eae, 0x6612, 0x78a4, 0xa084, 0x7770, 0xa18c,
+ 0x000f, 0xa105, 0x2029, 0x4605, 0x252c, 0xd5cc, 0x0140, 0xd3a4,
+ 0x0110, 0xa085, 0x0800, 0xd3fc, 0x0110, 0xa085, 0x8080, 0x78a6,
+ 0x6016, 0x788a, 0xa6b4, 0x001f, 0x8637, 0x8204, 0x8004, 0xa605,
+ 0x600e, 0x6004, 0xa084, 0xffd5, 0x6006, 0x00ce, 0x0005, 0xa282,
+ 0x0002, 0x1904, 0x3a1a, 0x7aa8, 0x6920, 0xc1bd, 0x6922, 0xd1cc,
+ 0x0568, 0xc1cc, 0x6922, 0xa294, 0x00ff, 0xa282, 0x0002, 0x1a04,
+ 0x3a0b, 0x080c, 0x399e, 0x080c, 0x38f4, 0xa980, 0x0001, 0x200c,
+ 0x080c, 0x3b6b, 0x080c, 0x3895, 0x88ff, 0x0178, 0x789b, 0x0060,
+ 0x2800, 0x78aa, 0x7e58, 0xc695, 0x7e5a, 0xd6d4, 0x1118, 0x781b,
+ 0x006e, 0x0005, 0x781b, 0x0082, 0x0005, 0x7e58, 0xd6d4, 0x1118,
+ 0x781b, 0x0071, 0x0005, 0x781b, 0x0083, 0x0005, 0xa282, 0x0002,
+ 0x1218, 0xa284, 0x0001, 0x0140, 0x7154, 0xa188, 0x0000, 0x210c,
+ 0xd1ec, 0x1110, 0x2011, 0x0000, 0x080c, 0x3a87, 0x0479, 0x080c,
+ 0x38f4, 0x7858, 0xc095, 0x785a, 0x781b, 0x0082, 0x0005, 0x00c6,
+ 0x0026, 0x2960, 0x6000, 0x2011, 0x0001, 0xd0ec, 0x1158, 0xd0bc,
+ 0x1138, 0x6014, 0xd0b4, 0x1120, 0xc1a4, 0x6106, 0xa006, 0x0088,
+ 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003,
+ 0x7aaa, 0xa8c0, 0x0004, 0x080c, 0x3a55, 0x6820, 0xa085, 0x0200,
+ 0x6822, 0x002e, 0x00ce, 0x0005, 0x8807, 0xa715, 0x00c6, 0x2009,
+ 0x0000, 0x7054, 0x2060, 0x82ff, 0x0110, 0x2009, 0x0040, 0x6018,
+ 0xa080, 0x0002, 0x789a, 0x78a4, 0xa084, 0xff9f, 0xa105, 0xc0ec,
+ 0xd0b4, 0x1108, 0xc0ed, 0x6100, 0xd1f4, 0x0110, 0xa085, 0x0020,
+ 0x78a6, 0x6016, 0x788a, 0x6004, 0xa084, 0xffef, 0x6006, 0x00ce,
+ 0x0005, 0x0006, 0x7000, 0xa086, 0x0003, 0x0110, 0x000e, 0x0010,
+ 0x000e, 0x0488, 0xd6ac, 0x0578, 0x7888, 0xa084, 0x0040, 0x0558,
+ 0x7bb8, 0x8307, 0xa084, 0x007f, 0x1508, 0x8207, 0xa084, 0x00ff,
+ 0xa09e, 0x0001, 0x1904, 0x3a32, 0xd6f4, 0x11d0, 0x79d8, 0x7adc,
+ 0xa108, 0xa291, 0x0000, 0x79d2, 0x79da, 0x7ad6, 0x7ade, 0x080c,
+ 0x42b5, 0x781b, 0x0080, 0xb284, 0x0600, 0x0118, 0x2001, 0x0000,
+ 0x0010, 0x2001, 0x0001, 0x080c, 0x4172, 0x0005, 0x080c, 0x254c,
+ 0x781b, 0x0080, 0x0005, 0x781b, 0x0083, 0x0005, 0x2039, 0x0000,
+ 0x2041, 0x0000, 0x2031, 0x0000, 0xa006, 0x2010, 0x080c, 0x38f7,
+ 0x080c, 0x399c, 0x7e58, 0x080c, 0x3a4e, 0x781b, 0x0082, 0x0005,
+ 0x0cd1, 0x6820, 0xc0c4, 0x6822, 0x00c6, 0x7054, 0x2060, 0x080c,
+ 0x3921, 0x00b0, 0x0c81, 0x6820, 0xc0cc, 0x6822, 0x00c6, 0x7054,
+ 0x2060, 0x080c, 0x39bb, 0x0060, 0x0c31, 0x6820, 0xa084, 0xecff,
+ 0x6822, 0x00c6, 0x7054, 0x2060, 0x6004, 0xa084, 0xffc5, 0x6006,
+ 0x00ce, 0x0005, 0x0049, 0x781b, 0x0082, 0x0005, 0x6827, 0x0002,
+ 0x0049, 0x781b, 0x0082, 0x0005, 0x2001, 0x0005, 0x0088, 0x2001,
+ 0x000c, 0x0070, 0x6820, 0xc0d5, 0x6822, 0x2001, 0x0006, 0x0040,
+ 0x2001, 0x000d, 0x0028, 0x2001, 0x0009, 0x0010, 0x2001, 0x0007,
+ 0x789b, 0x007e, 0x78aa, 0xc69d, 0x7e5a, 0x70d0, 0xd0b4, 0x0168,
+ 0xc0b4, 0x70d2, 0x00c6, 0x70b4, 0xa065, 0x6008, 0xa084, 0xfbef,
+ 0x600a, 0x6018, 0x8001, 0x601a, 0x00ce, 0x0005, 0x0076, 0x873f,
+ 0xa7bc, 0x000f, 0x873b, 0x873b, 0x8703, 0xa0e0, 0x4ac0, 0xae8e,
+ 0x4640, 0x0110, 0xa0e0, 0x4b40, 0xa7b8, 0x0020, 0x7f9a, 0x79a4,
+ 0xa184, 0x7fe0, 0x78ae, 0x6012, 0x79a4, 0xa184, 0x773f, 0x78a6,
+ 0x6016, 0x6004, 0xa085, 0x0038, 0x6006, 0x007e, 0x0005, 0x789b,
+ 0x0080, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa,
+ 0x789b, 0x0060, 0x78ab, 0x0004, 0x0800, 0x2031, 0x0000, 0x2029,
+ 0x0032, 0x789b, 0x0080, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab,
+ 0x0001, 0x7daa, 0x7eaa, 0x789b, 0x0060, 0x78ab, 0x0005, 0x0804,
+ 0x3a55, 0x0156, 0x8007, 0xa084, 0x00ff, 0x8003, 0x8003, 0xa080,
+ 0x0020, 0x789a, 0x79a4, 0xa18c, 0xffe0, 0x2021, 0x3b54, 0x2019,
+ 0x0011, 0x20a9, 0x000e, 0x2011, 0x0032, 0x2404, 0xa084, 0xffe0,
+ 0xa106, 0x0128, 0x8420, 0x2300, 0xa210, 0x1f04, 0x3abd, 0x015e,
+ 0x0005, 0x0156, 0x04f8, 0x2021, 0x3b62, 0x20a9, 0x0009, 0x2011,
+ 0x0029, 0xa582, 0x0028, 0x0550, 0x8420, 0x95a9, 0x2011, 0x0033,
+ 0xa582, 0x0033, 0x0618, 0x8420, 0x95a9, 0x2019, 0x000a, 0x2011,
+ 0x0065, 0x2200, 0xa502, 0x02d0, 0x8420, 0x2300, 0xa210, 0x1f04,
+ 0x3ae1, 0x015e, 0x0088, 0x2021, 0x3b54, 0x2019, 0x0011, 0x20a9,
+ 0x000e, 0x2011, 0x0033, 0x2200, 0xa502, 0x0240, 0x8420, 0x2300,
+ 0xa210, 0x1f04, 0x3af3, 0x015e, 0xa006, 0x0005, 0x8211, 0x015e,
+ 0xa582, 0x0064, 0x1220, 0x7808, 0xa085, 0x0070, 0x780a, 0x2404,
+ 0xa005, 0x0005, 0xa886, 0x0002, 0x01e8, 0x2021, 0x3b40, 0x20a9,
+ 0x000d, 0x2011, 0x0028, 0xa582, 0x0028, 0x0d48, 0x8420, 0x2019,
+ 0x0019, 0x2011, 0x0033, 0x2200, 0xa502, 0x0e00, 0x8420, 0x2300,
+ 0xa210, 0x1f04, 0x3b1b, 0x015e, 0x2011, 0x0184, 0xa582, 0x0185,
+ 0x0ab0, 0x0890, 0x2021, 0x3b4f, 0x20a9, 0x0003, 0x2011, 0x0024,
+ 0xa586, 0x0024, 0x0960, 0x8420, 0x2011, 0x0028, 0xa586, 0x0028,
+ 0x0930, 0x8420, 0x2019, 0x0019, 0x2011, 0x0033, 0x0804, 0x3af3,
+ 0x1021, 0x2202, 0x3403, 0x4604, 0x5805, 0x6a06, 0x7c07, 0x4610,
+ 0x4612, 0x5812, 0x5a12, 0x6a14, 0x6c14, 0x6e14, 0x7e17, 0x9021,
+ 0xb002, 0xe204, 0xe210, 0xe210, 0x1209, 0x3002, 0x3202, 0x4203,
+ 0x4403, 0x5404, 0x5604, 0x6605, 0x6805, 0x7806, 0x7a06, 0x0c07,
+ 0x0c07, 0x0e07, 0x10e1, 0x330a, 0x5805, 0x5a05, 0x6a06, 0x6c06,
+ 0x7c07, 0x7e07, 0x0e00, 0x789b, 0x0080, 0xa046, 0x0005, 0xa784,
+ 0x0f00, 0x800b, 0xa784, 0x001f, 0x8003, 0x8003, 0x8003, 0x8003,
+ 0xa105, 0xd7fc, 0x0118, 0xa0e0, 0x6bc0, 0x0010, 0xa0e0, 0x4bc0,
+ 0x0005, 0x00e6, 0x00f6, 0xd084, 0x0138, 0x2079, 0x0100, 0x2009,
+ 0x4680, 0x2071, 0x4680, 0x0030, 0x2009, 0x4640, 0x2079, 0x0200,
+ 0x2071, 0x4640, 0x2091, 0x8000, 0x2104, 0xa084, 0x000f, 0x0002,
+ 0x3ba2, 0x3ba2, 0x3ba2, 0x3ba2, 0x3ba2, 0x3ba2, 0x3ba0, 0x3ba0,
+ 0x080c, 0x254c, 0x69b4, 0xc1f5, 0xa18c, 0xff9f, 0x69b6, 0xa005,
+ 0x0580, 0x7858, 0xa084, 0xff9f, 0xa085, 0x6000, 0x785a, 0x7828,
+ 0xa086, 0x1814, 0x1530, 0x784b, 0x0004, 0x7848, 0xa084, 0x0004,
+ 0x1de0, 0x784b, 0x0008, 0x7848, 0xa084, 0x0008, 0x1de0, 0x7830,
+ 0xd0bc, 0x11b8, 0xb284, 0x0800, 0x0118, 0x0104, 0x3bd9, 0x0010,
+ 0x0304, 0x3bd9, 0x79e4, 0xa184, 0x0030, 0x0158, 0x78ec, 0xa084,
+ 0x0003, 0x0138, 0x681c, 0xd0ac, 0x1110, 0x00d9, 0x0010, 0x781b,
+ 0x00fb, 0x00fe, 0x00ee, 0x0005, 0x2001, 0x4601, 0x2004, 0xd0ac,
+ 0x1118, 0x6814, 0x080c, 0x2475, 0x0005, 0x781b, 0x0083, 0x0005,
+ 0x781b, 0x0082, 0x0005, 0x781b, 0x0071, 0x0005, 0x781b, 0x006e,
+ 0x0005, 0x2009, 0x4619, 0x210c, 0xa186, 0x0000, 0x0150, 0xa186,
+ 0x0001, 0x0150, 0x701f, 0x000b, 0x7063, 0x0001, 0x781b, 0x0054,
+ 0x0005, 0x781b, 0x00f3, 0x0005, 0x701f, 0x000a, 0x0005, 0x2009,
+ 0x4619, 0x210c, 0xa186, 0x0000, 0x0168, 0xa186, 0x0001, 0x0138,
+ 0x701f, 0x000b, 0x7063, 0x0001, 0x781b, 0x0054, 0x0005, 0x701f,
+ 0x000a, 0x0005, 0x781b, 0x00f2, 0x0005, 0x781b, 0x00fb, 0x0005,
+ 0x781b, 0x00fa, 0x0005, 0x781b, 0x00cc, 0x0005, 0x781b, 0x00cb,
+ 0x0005, 0x6818, 0xd0fc, 0x0110, 0x681b, 0x001d, 0x7063, 0x0001,
+ 0x781b, 0x0054, 0x0005, 0x7830, 0xa084, 0x00c0, 0x1170, 0x7808,
+ 0xc08c, 0x780a, 0xe000, 0xe000, 0xe000, 0xe000, 0x78ec, 0xa084,
+ 0x0021, 0x0118, 0x7808, 0xc08d, 0x780a, 0x0005, 0x7808, 0xc08d,
+ 0x780a, 0x0005, 0x7830, 0xa084, 0x0040, 0x1de0, 0xb284, 0x0800,
+ 0x0118, 0x1104, 0x3c58, 0x0010, 0x1304, 0x3c58, 0x78ac, 0x0005,
+ 0x7808, 0xa084, 0xfffd, 0x780a, 0xe000, 0xe000, 0xe000, 0xe000,
+ 0x78ec, 0xa084, 0x0021, 0x0140, 0xb284, 0x0800, 0x0118, 0x1104,
+ 0x3c67, 0x0010, 0x1304, 0x3c6a, 0x78ac, 0x0006, 0x7808, 0xa085,
+ 0x0002, 0x780a, 0x000e, 0x0005, 0xa784, 0x0001, 0x1904, 0x322a,
+ 0xa784, 0x0070, 0x0140, 0x00c6, 0x2d60, 0x2f68, 0x080c, 0x2467,
+ 0x2d78, 0x2c68, 0x00ce, 0xa784, 0x0008, 0x0148, 0x784b, 0x0008,
+ 0x78ec, 0xa084, 0x0003, 0x0904, 0x322a, 0x0804, 0x3be5, 0xa784,
+ 0x0004, 0x01c8, 0x78b8, 0xa084, 0x8000, 0x01a8, 0x784b, 0x0008,
+ 0x78ec, 0xa084, 0x0003, 0x0904, 0x322a, 0x78e4, 0xa084, 0x0007,
+ 0xa086, 0x0001, 0x1140, 0x78c0, 0xa685, 0x4800, 0x2030, 0x7e5a,
+ 0x781b, 0x00fb, 0x0005, 0xa784, 0x0080, 0x0140, 0x7884, 0xd0fc,
+ 0x0128, 0x080c, 0x3a32, 0x681b, 0x0022, 0x0005, 0x681b, 0x0003,
+ 0x7858, 0xa084, 0x5f00, 0x681e, 0x682f, 0x0000, 0x6833, 0x0000,
+ 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003, 0x0904, 0x2b89, 0xb284,
+ 0x0800, 0x0110, 0x0104, 0x259d, 0x0304, 0x259d, 0x6b14, 0x8307,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xd3fc, 0x0118, 0xa080,
+ 0x4b40, 0x0010, 0xa080, 0x4ac0, 0x2060, 0x2048, 0x7056, 0x2a60,
+ 0x0005, 0x00c6, 0x2960, 0x6000, 0xd0ac, 0x0904, 0x3d2f, 0x68a0,
+ 0xd1ac, 0x1120, 0xa084, 0x0e00, 0x0904, 0x3d2d, 0x6108, 0x8117,
+ 0xa18c, 0x00ff, 0x631c, 0x832f, 0xd0dc, 0x0110, 0xa39d, 0x0001,
+ 0xd0cc, 0x11c8, 0xa584, 0x00ff, 0x0138, 0x78ec, 0xd0e4, 0x0110,
+ 0x8213, 0x00b8, 0x2029, 0x0000, 0xa182, 0x000c, 0x1290, 0x78ec,
+ 0xd0e4, 0x1118, 0x2009, 0x000c, 0x0060, 0xa182, 0x000b, 0x1248,
+ 0x2009, 0x000a, 0x0030, 0x2009, 0x0032, 0x2011, 0x0000, 0x2029,
+ 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0006, 0x78ab, 0x0004, 0x79aa,
+ 0x78ab, 0x0000, 0x7aaa, 0x7baa, 0x7daa, 0xa8c0, 0x0008, 0x6820,
+ 0xa085, 0x1000, 0x6822, 0x080c, 0x3a55, 0xa085, 0x0001, 0x00ce,
+ 0x0005, 0xa282, 0x0006, 0x1904, 0x3a24, 0x7da8, 0x7eac, 0x8637,
+ 0xa5ac, 0x00ff, 0xa6b4, 0x00ff, 0x7fac, 0x8747, 0xa7bc, 0x00ff,
+ 0xa8c4, 0x00ff, 0x6920, 0xc1bd, 0x6922, 0xd1e4, 0x0904, 0x3da3,
+ 0xa18c, 0xecff, 0x6922, 0xa782, 0x0002, 0x1a04, 0x39fe, 0xa6b4,
+ 0x00ff, 0x0904, 0x3da0, 0xa682, 0x0031, 0x1a04, 0x39fe, 0xa582,
+ 0x0009, 0x0a04, 0x39fe, 0xa882, 0x0003, 0x1a04, 0x39fe, 0xa886,
+ 0x0002, 0x01d0, 0xa886, 0x0000, 0x1904, 0x39fe, 0x2001, 0x000c,
+ 0x79ec, 0xd1e4, 0x0110, 0x2001, 0x000a, 0xa502, 0x1290, 0x080c,
+ 0x39fe, 0x00c6, 0x2960, 0x6004, 0xa085, 0x001a, 0x6006, 0x6000,
+ 0xc0ac, 0x6002, 0x00ce, 0x0005, 0xa786, 0x0000, 0x0904, 0x39fe,
+ 0x8634, 0xa682, 0x0018, 0x0228, 0x0120, 0x2031, 0x0018, 0x0804,
+ 0x3df1, 0xa686, 0x0010, 0x1108, 0x8630, 0x852b, 0x852b, 0x080c,
+ 0x3ac9, 0x0904, 0x39fe, 0x080c, 0x38f7, 0x080c, 0x399c, 0x7e58,
+ 0xd6d4, 0x1118, 0x781b, 0x0071, 0x0005, 0x781b, 0x0083, 0x0005,
+ 0x080c, 0x38f4, 0x0c90, 0xa886, 0x0002, 0x1108, 0x8634, 0x7154,
+ 0xa188, 0x0000, 0x210c, 0xd1ac, 0x0904, 0x39fe, 0xd1ec, 0x1120,
+ 0x2039, 0x0000, 0x2041, 0x0000, 0xd1e4, 0x1120, 0x2031, 0x0000,
+ 0x2041, 0x0000, 0xa782, 0x0002, 0x12c8, 0x621c, 0xa284, 0x00ff,
+ 0xa706, 0x0110, 0x2039, 0x0000, 0xa605, 0x0190, 0x6108, 0x811f,
+ 0xa39c, 0x00ff, 0x0168, 0xa302, 0x1208, 0x2330, 0x8807, 0xa705,
+ 0xa086, 0x0201, 0x0160, 0xa886, 0x0000, 0x0168, 0x2039, 0x0000,
+ 0x2041, 0x0000, 0x2031, 0x0000, 0xa006, 0x2010, 0x0070, 0xa284,
+ 0xff00, 0x1108, 0x2040, 0xa184, 0x00ff, 0xa502, 0x0108, 0x2128,
+ 0x852b, 0x852b, 0x080c, 0x3ac9, 0x0d58, 0x080c, 0x38f7, 0x080c,
+ 0x399c, 0x789b, 0x0080, 0x78ab, 0x0001, 0x78ab, 0x0006, 0x78ab,
+ 0x0004, 0x7daa, 0x78ab, 0x0000, 0x7eaa, 0x7faa, 0x2800, 0x78aa,
+ 0x789b, 0x0060, 0x78ab, 0x0008, 0x6820, 0xc0e5, 0x6822, 0x080c,
+ 0x3a55, 0x7858, 0xc095, 0x785a, 0x781b, 0x0082, 0x0005, 0x0020,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0062, 0x0009, 0x0014, 0x0014, 0x9855, 0x984d, 0x0014,
+ 0x9911, 0x98ff, 0x0014, 0x0014, 0x0090, 0x00e7, 0x0100, 0x0402,
+ 0x2008, 0xf880, 0x0018, 0x0017, 0x840f, 0xd8c1, 0x0014, 0x0016,
+ 0xa20a, 0x0014, 0x300b, 0xa20c, 0x0014, 0x2500, 0x0013, 0x2500,
+ 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
+ 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0xa200, 0x3806,
+ 0x8839, 0x20c4, 0x0864, 0xa850, 0x3008, 0x28c1, 0x9d18, 0xa201,
+ 0x300c, 0x2847, 0x8161, 0x846a, 0x8000, 0x84a4, 0x1856, 0x883a,
+ 0xa808, 0x28e2, 0x9cce, 0xa8f3, 0x0864, 0xa83e, 0x300c, 0xa801,
+ 0x3008, 0x28e1, 0x9cce, 0x28a2, 0x7163, 0xa831, 0x2021, 0xa818,
+ 0xa205, 0x870c, 0xd8de, 0x64a0, 0x6de0, 0x6fc0, 0x67a4, 0x6c80,
+ 0x0212, 0xa205, 0x883d, 0x882b, 0x1814, 0x883b, 0x7027, 0x85f2,
+ 0xa737, 0xa532, 0xf003, 0x8576, 0x8677, 0xa813, 0x883e, 0xa811,
+ 0x2882, 0x7162, 0xa814, 0x280a, 0xa204, 0x64c0, 0x6de0, 0x67a0,
+ 0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677, 0xa802, 0x7861,
+ 0x883e, 0x206a, 0x28c1, 0x9d18, 0x2042, 0x2101, 0xa8ca, 0x2902,
+ 0xa20e, 0xa80b, 0xa207, 0x0014, 0xa203, 0x8000, 0x85a4, 0x1872,
+ 0x879a, 0x883c, 0x1fe2, 0xf601, 0xa208, 0x856e, 0x7121, 0x0014,
+ 0x0704, 0x3008, 0x9cce, 0x0014, 0xa202, 0x8000, 0x85a4, 0x3009,
+ 0x84a8, 0x19e2, 0xf844, 0x856e, 0x883f, 0x08e6, 0xa8f5, 0xf861,
+ 0xa8eb, 0xf801, 0x0014, 0xf881, 0x0016, 0x85b2, 0x80f0, 0x9532,
+ 0xfaa2, 0x1de2, 0x0014, 0x8532, 0xf221, 0x0014, 0x1de2, 0x84a8,
+ 0xd6e0, 0x1fe6, 0x0014, 0x3008, 0x8000, 0x2849, 0x1011, 0xa8fc,
+ 0x3008, 0x8000, 0xa000, 0x2081, 0x2802, 0x1011, 0xa8fc, 0xa889,
+ 0x3008, 0x20a1, 0x283c, 0x1011, 0xa8fc, 0xa209, 0x0017, 0x300c,
+ 0x8000, 0x85a4, 0x1de2, 0xdac1, 0x0014, 0x0210, 0xa801, 0x0014,
+ 0x26e0, 0x873a, 0xfaa3, 0x19f2, 0x26e0, 0x18f2, 0x0014, 0xa20b,
+ 0x0014, 0xa20d, 0x3806, 0x0210, 0x9d22, 0x0704, 0xa206, 0x6865,
+ 0x817e, 0x842a, 0x1dc1, 0x8823, 0x0016, 0x6042, 0x8008, 0xa8fa,
+ 0x8160, 0x842a, 0x8180, 0xf021, 0x3008, 0x84a8, 0x11d7, 0x7042,
+ 0x20dd, 0x0011, 0x20d5, 0x8822, 0x0016, 0x0000, 0x0126, 0x70d0,
+ 0xa084, 0x4c00, 0x8004, 0x2090, 0x7204, 0x7008, 0xc09c, 0xa205,
+ 0x11a0, 0x720c, 0x82ff, 0x0128, 0x8aff, 0x1178, 0x7200, 0xd284,
+ 0x1160, 0x7804, 0xd0cc, 0x0110, 0x080c, 0x4328, 0x7007, 0x0008,
+ 0x7003, 0x0008, 0x012e, 0x2000, 0x0005, 0x7000, 0xa084, 0x0003,
+ 0x7002, 0xc69c, 0xd084, 0x0588, 0x7108, 0xe000, 0x7008, 0xa106,
+ 0x1dd8, 0xa184, 0x0003, 0x0904, 0x3fa2, 0xa184, 0x01e0, 0x1904,
+ 0x3fa2, 0xd1f4, 0x1d88, 0xa184, 0x3000, 0xa086, 0x1000, 0x0d60,
+ 0x2011, 0x0180, 0x710c, 0x8211, 0x0130, 0x7008, 0xd0f4, 0x1d20,
+ 0x700c, 0xa106, 0x0dc0, 0x7007, 0x0012, 0x7108, 0xe000, 0x7008,
+ 0xa106, 0x1dd8, 0xa184, 0x0003, 0x0568, 0xd194, 0x0db0, 0xd1f4,
+ 0x0548, 0x7007, 0x0002, 0x0880, 0x0428, 0x7108, 0xd1fc, 0x0130,
+ 0x080c, 0x40ae, 0x8aff, 0x0904, 0x3f2c, 0x0cb8, 0x700c, 0xa08c,
+ 0x07ff, 0x01e8, 0x7004, 0xd084, 0x0178, 0x7014, 0xa005, 0x1148,
+ 0x7010, 0x7310, 0xa306, 0x1de0, 0x2300, 0xa005, 0x0128, 0xa102,
+ 0x1e20, 0x7007, 0x0010, 0x0030, 0x8aff, 0x0148, 0x080c, 0x426b,
+ 0x1de8, 0x09d8, 0x080c, 0x4034, 0x012e, 0x2000, 0x0005, 0x7204,
+ 0x7108, 0xc19c, 0x8103, 0x1218, 0x7007, 0x0002, 0x0cc0, 0xa205,
+ 0x1d88, 0x7007, 0x0008, 0x7003, 0x0008, 0x0006, 0x2001, 0x4601,
+ 0x2004, 0xd0cc, 0x0110, 0x080c, 0x4328, 0x000e, 0x012e, 0x2000,
+ 0x0005, 0x6428, 0x84ff, 0x0508, 0x2c70, 0x7004, 0xa0bc, 0x000f,
+ 0xa7b8, 0x3ff5, 0x273c, 0x87fb, 0x1148, 0x0210, 0x080c, 0x254c,
+ 0x609c, 0xa075, 0x0190, 0x0c88, 0x2039, 0x3fea, 0x2704, 0xae68,
+ 0x6808, 0xa630, 0x680c, 0xa529, 0x8421, 0x0138, 0x8738, 0x2704,
+ 0xa005, 0x1da8, 0x709c, 0xa075, 0x1d00, 0x0005, 0x0000, 0x0005,
+ 0x0009, 0x000d, 0x0011, 0x0015, 0x0019, 0x001d, 0x0000, 0x0003,
+ 0x0009, 0x000f, 0x0015, 0x001b, 0x0000, 0x0000, 0x3fea, 0x3fe7,
+ 0x0000, 0x0000, 0x8000, 0x0000, 0x3fea, 0x0000, 0x3ff2, 0x3fef,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x3ff2, 0x0000, 0x3fed, 0x3fed,
+ 0x0000, 0x0000, 0x8000, 0x0000, 0x3fed, 0x0000, 0x3ff3, 0x3ff3,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x3ff3, 0x2079, 0x4600, 0x2071,
+ 0x0010, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003, 0x0001, 0x2009,
+ 0x0002, 0x2071, 0x0050, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003,
+ 0x0000, 0x2001, 0x01ff, 0x2004, 0xd0fc, 0x1128, 0x8109, 0x0118,
+ 0x2071, 0x0020, 0x0c80, 0x0005, 0x7004, 0x8004, 0x1a04, 0x408a,
+ 0x7108, 0x7008, 0xa106, 0x1de0, 0xa184, 0x01e0, 0x0120, 0x080c,
+ 0x40e6, 0x0804, 0x40aa, 0x7007, 0x0012, 0x2019, 0x0000, 0x7108,
+ 0x7008, 0xa106, 0x1de0, 0xa184, 0x01e0, 0x0120, 0x080c, 0x40e6,
+ 0x0804, 0x40aa, 0xa19c, 0x300c, 0xa386, 0x2004, 0x0190, 0xa386,
+ 0x0008, 0x01c0, 0x7004, 0xd084, 0x1148, 0x7108, 0x7008, 0xa106,
+ 0x1de0, 0xa184, 0x0003, 0x0110, 0x0804, 0x40e6, 0xa386, 0x200c,
+ 0x19f0, 0x7200, 0x8204, 0x0230, 0x730c, 0xa384, 0x07ff, 0x0110,
+ 0x080c, 0x254c, 0x7108, 0x7008, 0xa106, 0x1de0, 0xa184, 0x01e0,
+ 0x0118, 0x080c, 0x40e6, 0x0470, 0x7007, 0x0012, 0x7000, 0xd084,
+ 0x1148, 0x7310, 0x7014, 0xa305, 0x0128, 0x710c, 0xa184, 0x07ff,
+ 0x1904, 0x4034, 0x7108, 0x7008, 0xa106, 0x1de0, 0xa184, 0x01e0,
+ 0x0118, 0x080c, 0x40e6, 0x00b0, 0x7007, 0x0012, 0x7007, 0x0008,
+ 0x7004, 0xd09c, 0x1de8, 0x7108, 0x7008, 0xa106, 0x1de0, 0xa184,
+ 0x01e0, 0x0118, 0x080c, 0x40e6, 0x0028, 0x7007, 0x0012, 0x7108,
+ 0x8103, 0x0e88, 0x7003, 0x0008, 0x0005, 0x7108, 0xa184, 0x01e0,
+ 0x15a8, 0x7108, 0xa184, 0x01e0, 0x1588, 0xa184, 0x0007, 0x0002,
+ 0x40c2, 0x40d0, 0x40c0, 0x40d0, 0x40c0, 0x4120, 0x40c0, 0x411e,
+ 0x080c, 0x254c, 0x7004, 0xa084, 0x0010, 0xc08d, 0x7006, 0x8aff,
+ 0x1118, 0x2049, 0x0000, 0x0005, 0x080c, 0x426b, 0x1de8, 0x0005,
+ 0x7004, 0xa084, 0x0010, 0xc08d, 0x7006, 0x7004, 0xd084, 0x1140,
+ 0x7108, 0x7008, 0xa106, 0x1de0, 0xa184, 0x0003, 0x0108, 0x0030,
+ 0x8aff, 0x0118, 0x080c, 0x426b, 0x1de8, 0x0005, 0x7007, 0x0012,
+ 0x7108, 0x1d04, 0x40e9, 0x2091, 0x6000, 0x1d04, 0x40ed, 0x2091,
+ 0x6000, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004, 0xd09c, 0x1de8,
+ 0x7007, 0x0012, 0x7108, 0xd1fc, 0x1dd8, 0x7003, 0x0000, 0x7000,
+ 0xa005, 0x1130, 0x7004, 0xa005, 0x1118, 0x700c, 0xa005, 0x0108,
+ 0x0c40, 0x2049, 0x0000, 0xb284, 0x0200, 0x0118, 0x2001, 0x0000,
+ 0x0010, 0x2001, 0x0001, 0x080c, 0x3b81, 0x681b, 0x0002, 0x2051,
+ 0x0000, 0x0005, 0x080c, 0x254c, 0x080c, 0x254c, 0x080c, 0x415f,
+ 0x7210, 0x7114, 0x700c, 0xa09c, 0x07ff, 0x2800, 0xa300, 0xa211,
+ 0xa189, 0x0000, 0x04a1, 0x2704, 0x2c58, 0xac60, 0x6308, 0x2200,
+ 0xa322, 0x630c, 0x2100, 0xa31b, 0x2400, 0xa305, 0x0140, 0x1238,
+ 0x8412, 0x8210, 0x830a, 0xa189, 0x0000, 0x2b60, 0x0c58, 0x2b60,
+ 0x8a07, 0x0006, 0x6004, 0xd09c, 0x0118, 0xa7ba, 0x3fef, 0x0010,
+ 0xa7ba, 0x3fe7, 0x000e, 0xa73d, 0x2c00, 0x6886, 0x6f8a, 0x6c92,
+ 0x6b8e, 0x7108, 0x7008, 0xa106, 0x1de0, 0xa184, 0x01e0, 0x0110,
+ 0x080c, 0x40e6, 0x7007, 0x0012, 0x080c, 0x4034, 0x0005, 0x8a50,
+ 0x8739, 0x2704, 0xa004, 0x1168, 0x6000, 0xa064, 0x1108, 0x2d60,
+ 0x6004, 0xa084, 0x000f, 0xa080, 0x4005, 0x203c, 0x87fb, 0x090c,
+ 0x254c, 0x0005, 0x0126, 0x00d6, 0x70d0, 0xa084, 0x4c00, 0x8004,
+ 0x2090, 0x00de, 0x6884, 0x2060, 0x6888, 0x6b8c, 0x6c90, 0x8057,
+ 0xaad4, 0x00ff, 0xa084, 0x00ff, 0x0006, 0x6804, 0xa084, 0x0008,
+ 0x000e, 0x0118, 0xa0b8, 0x3fef, 0x0010, 0xa0b8, 0x3fe7, 0xb284,
+ 0x0200, 0x0110, 0x7e20, 0x0008, 0x7e24, 0xa6b5, 0x000c, 0x681c,
+ 0xd0b4, 0x0108, 0xc685, 0x2400, 0xa305, 0x0518, 0x2c58, 0x2704,
+ 0x6104, 0xac60, 0x6000, 0xa400, 0x701a, 0x6004, 0xa301, 0x701e,
+ 0xd19c, 0x0140, 0x6010, 0xa081, 0x0000, 0x7022, 0x6014, 0xa081,
+ 0x0000, 0x7026, 0x6208, 0x2400, 0xa202, 0x7012, 0x620c, 0x2300,
+ 0xa203, 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x080c, 0x4292,
+ 0x0010, 0x080c, 0x426b, 0x1de8, 0x012e, 0x2000, 0x0005, 0x0126,
+ 0x00d6, 0x70d0, 0xa084, 0x4c00, 0x8004, 0x2090, 0x00de, 0x7007,
+ 0x0004, 0x7004, 0xd094, 0x1de8, 0x7003, 0x0008, 0x012e, 0x2000,
+ 0x0005, 0x0126, 0x00d6, 0x70d0, 0xa084, 0x4c00, 0x8004, 0x2090,
+ 0x00de, 0x7e20, 0xb284, 0x0200, 0x1108, 0x7e24, 0xa6b5, 0x000c,
+ 0x681c, 0xd0ac, 0x1118, 0xc685, 0x7003, 0x0000, 0x6828, 0x2050,
+ 0x2d60, 0x6004, 0xa0bc, 0x000f, 0xa7b8, 0x3ff5, 0x273c, 0x87fb,
+ 0x1138, 0x0210, 0x080c, 0x254c, 0x689c, 0xa065, 0x0120, 0x0c88,
+ 0x080c, 0x426b, 0x1de8, 0x012e, 0x2000, 0x0005, 0x0126, 0x0006,
+ 0x0016, 0x00d6, 0x70d0, 0xa084, 0x4c00, 0x8004, 0x2090, 0x7e20,
+ 0xb284, 0x0200, 0x1108, 0x7e24, 0x00de, 0x003e, 0x004e, 0xa6b5,
+ 0x000c, 0x681c, 0xd0b4, 0x0128, 0xc685, 0x7003, 0x0000, 0x7007,
+ 0x0004, 0x2049, 0x4206, 0x6828, 0xa055, 0x00d6, 0x0904, 0x4267,
+ 0x2d70, 0x2e60, 0x7004, 0xa0bc, 0x000f, 0xa7b8, 0x3ff5, 0x273c,
+ 0x87fb, 0x1140, 0x0210, 0x080c, 0x254c, 0x709c, 0xa075, 0x2060,
+ 0x0570, 0x0c80, 0x2704, 0xae68, 0x6808, 0xa422, 0x680c, 0xa31b,
+ 0x0268, 0x8a51, 0x1110, 0x080c, 0x254c, 0x8738, 0x2704, 0xa005,
+ 0x1d90, 0x709c, 0xa075, 0x2060, 0x01d0, 0x08e0, 0x8422, 0x8420,
+ 0x831a, 0xa399, 0x0000, 0x6908, 0x2400, 0xa122, 0x690c, 0x2300,
+ 0xa11b, 0x1210, 0x080c, 0x254c, 0xb284, 0x0200, 0x0118, 0x2071,
+ 0x0050, 0x0010, 0x2071, 0x0020, 0x00de, 0x0804, 0x419b, 0x00de,
+ 0x012e, 0x2000, 0x0005, 0x7008, 0x0006, 0xa084, 0x01e0, 0x000e,
+ 0x0110, 0xa006, 0x0005, 0xa084, 0x0003, 0xa086, 0x0003, 0x1108,
+ 0x0005, 0x2704, 0xac78, 0x7800, 0x701a, 0x7804, 0x701e, 0x7808,
+ 0x7012, 0x780c, 0x7016, 0x6004, 0xd09c, 0x0120, 0x7810, 0x7022,
+ 0x7814, 0x7026, 0x7602, 0x7004, 0xa084, 0x0010, 0xc085, 0x7006,
+ 0x2079, 0x4600, 0x8a51, 0x01e8, 0x8738, 0x2704, 0xa005, 0x1168,
+ 0x609c, 0xa005, 0x01b8, 0x2060, 0x6004, 0xa084, 0x000f, 0xa080,
+ 0x3ff5, 0x203c, 0x87fb, 0x090c, 0x254c, 0x7008, 0x0006, 0xa084,
+ 0x01e0, 0x000e, 0x0110, 0xa006, 0x0028, 0xa084, 0x0003, 0xa086,
+ 0x0003, 0x0005, 0x2051, 0x0000, 0x0005, 0x0126, 0x0006, 0x00d6,
+ 0x70d0, 0xa084, 0x4c00, 0x8004, 0x2090, 0x00de, 0x008e, 0x7108,
+ 0xa184, 0x0003, 0x1128, 0x6828, 0xa005, 0x0178, 0x0804, 0x3f45,
+ 0x7108, 0xd1fc, 0x0118, 0x080c, 0x40ae, 0x0c88, 0x7007, 0x0010,
+ 0x7108, 0xd1fc, 0x0de8, 0x080c, 0x40ae, 0x7008, 0xa086, 0x0008,
+ 0x1d30, 0x7000, 0xa005, 0x1d18, 0x7003, 0x0000, 0x2049, 0x0000,
+ 0x0006, 0x2001, 0x4601, 0x2004, 0xd0cc, 0x0110, 0x080c, 0x4328,
+ 0x000e, 0x012e, 0x2000, 0x0005, 0x0126, 0x0146, 0x0136, 0x0156,
+ 0x00c6, 0x00d6, 0x70d0, 0xa084, 0x4c00, 0x8004, 0x2090, 0x00de,
+ 0x2049, 0x42ec, 0xad80, 0x0011, 0x20a0, 0xb284, 0x0200, 0x0118,
+ 0x2099, 0x0032, 0x0010, 0x2099, 0x0031, 0x700c, 0xa084, 0x07ff,
+ 0x682a, 0x7007, 0x0008, 0x7007, 0x0002, 0x7003, 0x0001, 0x0118,
+ 0x8000, 0x80ac, 0x53a5, 0x700c, 0xa084, 0x07ff, 0x0130, 0x7007,
+ 0x0004, 0x7004, 0xa084, 0x0004, 0x1de0, 0x00ce, 0x2049, 0x0000,
+ 0x7003, 0x0000, 0x015e, 0x013e, 0x014e, 0x012e, 0x2000, 0x0005,
+ 0x6814, 0xd0fc, 0x0904, 0x436b, 0x7000, 0xd084, 0x05e0, 0x7e24,
+ 0xa6b5, 0x0004, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x1de0,
+ 0x7118, 0x0016, 0x711c, 0x0016, 0x7120, 0x0016, 0x7124, 0x0016,
+ 0x701b, 0x0000, 0x701f, 0x3fff, 0x7023, 0x0000, 0x7027, 0x0000,
+ 0x7013, 0x0004, 0x7017, 0x0000, 0x7602, 0x7007, 0x0001, 0x2001,
+ 0xffff, 0x2009, 0x0031, 0x200a, 0x200a, 0x7108, 0x7008, 0xa106,
+ 0x1de0, 0xd1fc, 0x0dd0, 0x002e, 0x7226, 0x002e, 0x7222, 0x002e,
+ 0x721e, 0x002e, 0x721a, 0x7007, 0x0002, 0x7008, 0xa086, 0x0008,
+ 0x0110, 0x0804, 0x40e6, 0x7007, 0x0004, 0x7003, 0x0000, 0x0005,
+ 0x2091, 0x8000, 0x2091, 0x6000, 0x78ac, 0xa005, 0x1168, 0x7974,
+ 0x70d0, 0xa106, 0x1148, 0x781c, 0xa005, 0x0130, 0x781f, 0x0000,
+ 0x0e04, 0x4384, 0x2091, 0x4080, 0x2069, 0x4680, 0xd7fc, 0x1110,
+ 0x2069, 0x4640, 0x6800, 0xa084, 0x000f, 0x1198, 0x68d0, 0xd0b4,
+ 0x0180, 0xd0bc, 0x1170, 0x00f6, 0x2079, 0x0100, 0xd7fc, 0x1110,
+ 0x2079, 0x0200, 0x7830, 0xa084, 0x00c0, 0x1110, 0x080c, 0x22ae,
+ 0x00fe, 0x7830, 0x8001, 0x7832, 0x1904, 0x440b, 0x7834, 0x7832,
+ 0x2061, 0x6bc0, 0x2069, 0x4680, 0xc7fd, 0x68cc, 0xa005, 0x0128,
+ 0x8001, 0x68ce, 0x1110, 0x080c, 0x4577, 0x6800, 0xa084, 0x000f,
+ 0x0168, 0xa086, 0x0001, 0x0150, 0x6840, 0xa00d, 0x0138, 0x2104,
+ 0xa005, 0x0120, 0x8001, 0x200a, 0x0904, 0x4514, 0x6814, 0xa005,
+ 0x01a8, 0x8001, 0x6816, 0x1190, 0x68a3, 0x0001, 0x00f6, 0xd7fc,
+ 0x1118, 0x2079, 0x0200, 0x0010, 0x2079, 0x0100, 0x080c, 0x3c46,
+ 0x00fe, 0x6860, 0xa005, 0x0110, 0x080c, 0x22ae, 0x687c, 0xa005,
+ 0x0140, 0x8001, 0x687e, 0x1128, 0x6863, 0x0000, 0x68d0, 0xc0c5,
+ 0x68d2, 0x68d0, 0xd0fc, 0x01b0, 0xc0fc, 0x68d2, 0x20a9, 0x0200,
+ 0x6034, 0xa005, 0x0158, 0x8001, 0x6036, 0x68d0, 0xc0fd, 0x68d2,
+ 0x1128, 0x6010, 0xa005, 0x0110, 0x080c, 0x22ae, 0xace0, 0x0010,
+ 0x1f04, 0x43f0, 0xd7fc, 0x0138, 0x2061, 0x4bc0, 0x2069, 0x4640,
+ 0xc7fc, 0x0804, 0x43ad, 0x0459, 0x7838, 0x8001, 0x783a, 0x11a0,
+ 0x783c, 0x783a, 0x2061, 0x4bc0, 0x2069, 0x4640, 0xc7fc, 0x680c,
+ 0xa005, 0x0110, 0x080c, 0x4487, 0xd7fc, 0x1130, 0x2061, 0x6bc0,
+ 0x2069, 0x4680, 0xc7fd, 0x0c98, 0x7810, 0xd0cc, 0x0168, 0xd0ac,
+ 0x1120, 0xd0a4, 0x0148, 0xc0ad, 0x7812, 0x2091, 0x8001, 0x0e04,
+ 0x4433, 0x080c, 0x207a, 0x0005, 0x2091, 0x8001, 0x0005, 0x7840,
+ 0x8001, 0x7842, 0x1904, 0x4486, 0x7844, 0x7842, 0x2069, 0x4640,
+ 0xc7fc, 0x2079, 0x0200, 0x68d4, 0xa005, 0x0138, 0x7de0, 0xa504,
+ 0x1120, 0x68d6, 0x68d0, 0xc0bc, 0x68d2, 0x2079, 0x4600, 0x6810,
+ 0xa005, 0x1110, 0x2001, 0x0101, 0x8001, 0x6812, 0xd7fc, 0x0118,
+ 0xa080, 0x8cd0, 0x0010, 0xa080, 0x8bc0, 0x2040, 0x2004, 0xa065,
+ 0x01e0, 0x6024, 0xa005, 0x01b0, 0x8001, 0x6026, 0x1198, 0x6800,
+ 0xa005, 0x0130, 0x6848, 0xac06, 0x1118, 0x080c, 0x4514, 0x0068,
+ 0x6860, 0xa005, 0x0118, 0x6027, 0x0001, 0x0020, 0x080c, 0x44c8,
+ 0x2804, 0x0c28, 0x6000, 0x2c40, 0x0c10, 0xd7fc, 0x1138, 0x2069,
+ 0x4680, 0xc7fd, 0x2079, 0x0100, 0x0804, 0x4443, 0x0005, 0x2009,
+ 0x0000, 0x20a9, 0x0200, 0x6008, 0xd09c, 0x0558, 0x6024, 0xa005,
+ 0x0118, 0x8001, 0x6026, 0x0418, 0x6008, 0xc09c, 0xd084, 0x1110,
+ 0xd0ac, 0x01c0, 0x600a, 0x6004, 0xa005, 0x01d8, 0x00d6, 0x00c6,
+ 0x0016, 0x2068, 0x6010, 0x8001, 0x6012, 0x080c, 0x37a4, 0x2d00,
+ 0x2c68, 0x2060, 0x080c, 0x1be3, 0x080c, 0x1d95, 0x001e, 0x00ce,
+ 0x00de, 0x0038, 0xc0bd, 0x600a, 0xa18d, 0x0001, 0x0010, 0xa18d,
+ 0x0100, 0xace0, 0x0010, 0x1f04, 0x448b, 0xa184, 0x0001, 0x0130,
+ 0xa18c, 0xfffe, 0x690e, 0x080c, 0x22ae, 0x0008, 0x690e, 0x0005,
+ 0x2c00, 0x687a, 0x6714, 0x6f72, 0x6017, 0x0000, 0x602b, 0x0000,
+ 0x601b, 0x0006, 0x60b4, 0xa084, 0x5f00, 0x601e, 0x6020, 0xa084,
+ 0x00ff, 0xa085, 0x0060, 0x6022, 0x6000, 0x2042, 0x6858, 0xac06,
+ 0x1110, 0x2800, 0x685a, 0x080c, 0x1b7b, 0x6818, 0xa005, 0x0110,
+ 0x8001, 0x681a, 0x6808, 0xc0a4, 0x680a, 0x6810, 0x7908, 0x8109,
+ 0x790a, 0x8001, 0x1310, 0x080c, 0x254c, 0x6812, 0x1118, 0x7910,
+ 0xc1a5, 0x7912, 0x602f, 0x0000, 0x6033, 0x0000, 0x2c68, 0x080c,
+ 0x1da2, 0xd7fc, 0x1118, 0x2069, 0x4640, 0x0010, 0x2069, 0x4680,
+ 0x6910, 0xa184, 0x0100, 0x2001, 0x0006, 0x1118, 0x6976, 0x2001,
+ 0x0004, 0x080c, 0x22a4, 0x0005, 0x00d6, 0x6948, 0x2160, 0xd7fc,
+ 0x1118, 0x2069, 0x0200, 0x0010, 0x2069, 0x0100, 0x080c, 0x2467,
+ 0x601b, 0x0006, 0x6858, 0xa084, 0x5f00, 0x601e, 0x6020, 0xa084,
+ 0x00ff, 0xa085, 0x0048, 0x6022, 0x602f, 0x0000, 0x6033, 0x0000,
+ 0x6808, 0xa084, 0xfffd, 0x680a, 0x6830, 0xd0b4, 0x01b0, 0x684b,
+ 0x0004, 0x20a9, 0x0014, 0x6848, 0xd094, 0x0110, 0x1f04, 0x453b,
+ 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xd084, 0x0110, 0x1f04,
+ 0x4544, 0x20a9, 0x00fa, 0x1f04, 0x454b, 0x681b, 0x0054, 0x00de,
+ 0x6863, 0x0007, 0x0005, 0x2079, 0x4600, 0x00e1, 0x0089, 0x00a9,
+ 0x2009, 0x0002, 0x2069, 0x4680, 0x680f, 0x0000, 0x6813, 0x0000,
+ 0x6817, 0x0000, 0x8109, 0x0118, 0x2069, 0x4640, 0x0ca8, 0x0005,
+ 0x2019, 0x00a3, 0x7b3a, 0x7b3e, 0x0005, 0x2019, 0x0033, 0x7b42,
+ 0x7b46, 0x0005, 0x2019, 0x32dd, 0x7b32, 0x7b36, 0x0005, 0x6a4c,
+ 0xa285, 0x0000, 0x01f0, 0x6950, 0x6bbc, 0xa300, 0x00c6, 0x2164,
+ 0x6304, 0x83ff, 0x1138, 0x8211, 0x0148, 0x8108, 0xa11a, 0x0eb8,
+ 0x69bc, 0x0ca8, 0x68cf, 0x000a, 0x00ce, 0x0005, 0x694c, 0x6abc,
+ 0x2264, 0x6008, 0xc0b5, 0x600a, 0x8210, 0x8109, 0x1dc8, 0x694e,
+ 0x00ce, 0x0005, 0x1d04, 0x459a, 0x2091, 0x6000, 0x1d04, 0x459e,
+ 0x2091, 0x6000, 0x70ec, 0xd0dc, 0x1118, 0xd0d4, 0x0190, 0x0098,
+ 0xae8e, 0x0100, 0x0138, 0x7814, 0xc0f5, 0xc0c5, 0x7816, 0xd0d4,
+ 0x1578, 0x0458, 0x7814, 0xc0fd, 0xc0c5, 0x7816, 0xd0d4, 0x1540,
+ 0x0420, 0xd0e4, 0x0538, 0x1d04, 0x45bb, 0x2091, 0x6000, 0x2009,
+ 0x000c, 0x1d04, 0x45c1, 0x2091, 0x6000, 0x8109, 0x1dd0, 0x70e4,
+ 0xa084, 0x01ff, 0xa086, 0x01ff, 0x1110, 0x70ec, 0x08c8, 0xae8e,
+ 0x0100, 0x0128, 0x7814, 0xc0f4, 0xd0fc, 0x1130, 0x0020, 0x7814,
+ 0xc0fc, 0xd0f4, 0x1108, 0xc0c4, 0x7816, 0x7804, 0xd08c, 0x0110,
+ 0x681f, 0x000c, 0x70a0, 0x70a2, 0x0005, 0x7c12
+};
+#ifdef UNIQUE_FW_NAME
+static unsigned short fw12160i_length01 = 0x35e6;
+#else
+static unsigned short risc_code_length01 = 0x35e6;
+#endif
diff --git a/drivers/scsi/ql1280_fw.h b/drivers/scsi/ql1280_fw.h
new file mode 100644
index 000000000000..2621e99a4311
--- /dev/null
+++ b/drivers/scsi/ql1280_fw.h
@@ -0,0 +1,2017 @@
+/*****************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP1280/ device driver for Linux 2.2.x and 2.4.x
+ * Copyright (C) 2001 Qlogic Corporation (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *****************************************************************************/
+
+/************************************************************************
+ * --- ISP1240/1080/1280 Initiator Firmware --- *
+ * 32 LUN Support *
+ ************************************************************************/
+
+
+/*
+ * Firmware Version 8.15.00 (14:35 Aug 22, 2000)
+ */
+
+#ifdef UNIQUE_FW_NAME
+static unsigned char fw1280ei_version_str[] = {8,15,0};
+#else
+static unsigned char firmware_version[] = {8,15,0};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw1280ei_VERSION_STRING "8.15.00"
+#else
+#define FW_VERSION_STRING "8.15.00"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+static unsigned short fw1280ei_addr01 = 0x1000;
+#else
+static unsigned short risc_code_addr01 = 0x1000;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+static unsigned short fw1280ei_code01[] = {
+#else
+static unsigned short risc_code01[] = {
+#endif
+ 0x0078, 0x1041, 0x0000, 0x3d3b, 0x0000, 0x2043, 0x4f50, 0x5952,
+ 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932, 0x2c31,
+ 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749, 0x4320,
+ 0x434f, 0x5250, 0x4f52, 0x4154, 0x494f, 0x4e00, 0x2049, 0x5350,
+ 0x3132, 0x3430, 0x2046, 0x6972, 0x6d77, 0x6172, 0x6520, 0x2056,
+ 0x6572, 0x7369, 0x6f6e, 0x2030, 0x382e, 0x3135, 0x2020, 0x2043,
+ 0x7573, 0x746f, 0x6d65, 0x7220, 0x4e6f, 0x2e20, 0x3030, 0x2050,
+ 0x726f, 0x6475, 0x6374, 0x204e, 0x6f2e, 0x2020, 0x3030, 0x2020,
+ 0x2400, 0x20c9, 0x97ff, 0x2001, 0x04fc, 0x2004, 0xa086, 0x1080,
+ 0x00c0, 0x1054, 0x2071, 0x0100, 0x70a0, 0x70a2, 0x20c1, 0x0010,
+ 0x2089, 0x1374, 0x0078, 0x106d, 0x2001, 0x04fc, 0x2004, 0xa086,
+ 0x1280, 0x00c0, 0x1069, 0x2071, 0x0200, 0x70a0, 0x70a2, 0x2071,
+ 0x0100, 0x70a0, 0x70a2, 0x20c1, 0x0010, 0x2089, 0x13f8, 0x0078,
+ 0x106d, 0x20c1, 0x0020, 0x2089, 0x131c, 0x2071, 0x0010, 0x70c3,
+ 0x0004, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3,
+ 0x0008, 0x2001, 0x04fe, 0x70d6, 0x20c1, 0x0021, 0x2019, 0x0000,
+ 0x2009, 0xfeff, 0x2100, 0x200b, 0xa5a5, 0xa1ec, 0x7fff, 0x2d64,
+ 0x206b, 0x0a0a, 0xaddc, 0x3fff, 0x2b54, 0x205b, 0x5050, 0x2114,
+ 0xa286, 0xa5a5, 0x0040, 0x10a4, 0xa386, 0x000f, 0x0040, 0x10a0,
+ 0x2c6a, 0x2a5a, 0x20c1, 0x0020, 0x2019, 0x000f, 0x0078, 0x1080,
+ 0x2c6a, 0x2a5a, 0x0078, 0x10a2, 0x2c6a, 0x2a5a, 0x2130, 0x2128,
+ 0xa1a2, 0x4e00, 0x8424, 0x8424, 0x8424, 0x8424, 0x8424, 0x8424,
+ 0xa192, 0x9800, 0x2009, 0x0000, 0x2001, 0x0032, 0x1078, 0x207a,
+ 0x2218, 0x2079, 0x4e00, 0x2fa0, 0x2408, 0x2011, 0x0000, 0x20a9,
+ 0x0040, 0x42a4, 0x8109, 0x00c0, 0x10bf, 0x2009, 0xff00, 0x3400,
+ 0xa102, 0x0048, 0x10cf, 0x0040, 0x10cf, 0x20a8, 0x42a4, 0x2001,
+ 0x04fc, 0x2004, 0xa086, 0x1080, 0x00c0, 0x10e5, 0x2071, 0x0100,
+ 0x0d7e, 0x2069, 0x4e40, 0x1078, 0x4cdd, 0x0d7f, 0x7810, 0xc0ed,
+ 0x7812, 0x781b, 0x0064, 0x0078, 0x110a, 0x2001, 0x04fc, 0x2004,
+ 0xa086, 0x1280, 0x00c0, 0x1105, 0x7814, 0xc0ed, 0xc0d5, 0x7816,
+ 0x781b, 0x0064, 0x2071, 0x0200, 0x0d7e, 0x2069, 0x4e40, 0x1078,
+ 0x4cdd, 0x2069, 0x4e80, 0x2071, 0x0100, 0x1078, 0x4cdd, 0x7814,
+ 0xc0d4, 0x7816, 0x0d7f, 0x0078, 0x110a, 0x7814, 0xc0e5, 0x7816,
+ 0x781b, 0x003c, 0x7eca, 0x7cc2, 0x7bc6, 0x7867, 0x0000, 0x7800,
+ 0xc08d, 0x7802, 0x2031, 0x0030, 0x78af, 0x0101, 0x7823, 0x0002,
+ 0x7827, 0x0002, 0x2009, 0x0002, 0x2069, 0x4e40, 0x681b, 0x0003,
+ 0x6823, 0x0007, 0x6827, 0x00fa, 0x682b, 0x0008, 0x682f, 0x0028,
+ 0x6837, 0x0000, 0x683b, 0x0006, 0x6833, 0x0008, 0x683f, 0x0000,
+ 0x8109, 0x0040, 0x115e, 0x68d3, 0x000a, 0x68c3, 0x4ec0, 0x2079,
+ 0x4e00, 0x7814, 0xd0e4, 0x00c0, 0x1144, 0xd0ec, 0x00c0, 0x1148,
+ 0x68d7, 0x7329, 0x0078, 0x114a, 0x68d7, 0x730d, 0x0078, 0x114a,
+ 0x68d7, 0x732d, 0x68c7, 0x53c0, 0x68cb, 0x52c0, 0x68cf, 0x93c0,
+ 0x68ab, 0x9644, 0x68af, 0x9649, 0x68b3, 0x9644, 0x68b7, 0x9644,
+ 0x68a7, 0x0001, 0x2069, 0x4e80, 0x0078, 0x111e, 0x68d3, 0x000a,
+ 0x68c3, 0x50c0, 0x7814, 0xd0e4, 0x00c0, 0x116a, 0x68d7, 0x7439,
+ 0x0078, 0x116c, 0x68d7, 0x7419, 0x68c7, 0x73c0, 0x68cb, 0x5340,
+ 0x68cf, 0x94d0, 0x68ab, 0x9649, 0x68af, 0x964e, 0x68b3, 0x9649,
+ 0x68b7, 0x9649, 0x68a7, 0x0001, 0x7810, 0xd0ec, 0x00c0, 0x11c2,
+ 0x7814, 0xd0e4, 0x00c0, 0x11b4, 0x0e7e, 0x2069, 0x52c0, 0x2071,
+ 0x0200, 0x70ec, 0xd0e4, 0x00c0, 0x1195, 0x2019, 0x0c0c, 0x2021,
+ 0x000c, 0x1078, 0x2009, 0x0078, 0x119b, 0x2019, 0x0c0a, 0x2021,
+ 0x000a, 0x1078, 0x2009, 0x2069, 0x5340, 0x2071, 0x0100, 0x70ec,
+ 0xd0e4, 0x00c0, 0x11ab, 0x2019, 0x0c0c, 0x2021, 0x000c, 0x1078,
+ 0x2009, 0x0078, 0x11b1, 0x2019, 0x0c0a, 0x2021, 0x000a, 0x1078,
+ 0x2009, 0x0e7f, 0x0078, 0x11db, 0x2019, 0x0c0c, 0x2021, 0x000c,
+ 0x2069, 0x52c0, 0x1078, 0x2009, 0x2069, 0x5340, 0x1078, 0x2009,
+ 0x0078, 0x11db, 0x2069, 0x52c0, 0x0e7e, 0x2071, 0x0100, 0x70ec,
+ 0xd0e4, 0x00c0, 0x11d4, 0x2019, 0x0c0c, 0x2021, 0x000c, 0x1078,
+ 0x2009, 0x0e7f, 0x0078, 0x11db, 0x2019, 0x0c0a, 0x2021, 0x000a,
+ 0x1078, 0x2009, 0x0e7f, 0x2011, 0x0002, 0x2069, 0x53c0, 0x2009,
+ 0x0002, 0x20a9, 0x0100, 0x6837, 0x0000, 0x680b, 0x0040, 0x7bc8,
+ 0xa386, 0xfeff, 0x00c0, 0x11f2, 0x6817, 0x0100, 0x681f, 0x0064,
+ 0x0078, 0x11f6, 0x6817, 0x0064, 0x681f, 0x0002, 0xade8, 0x0010,
+ 0x00f0, 0x11e3, 0x8109, 0x00c0, 0x11e1, 0x8211, 0x0040, 0x1204,
+ 0x2069, 0x73c0, 0x0078, 0x11df, 0x1078, 0x265b, 0x1078, 0x468e,
+ 0x1078, 0x1dd4, 0x1078, 0x4c6f, 0x2091, 0x2100, 0x2079, 0x4e00,
+ 0x7810, 0xd0ec, 0x0040, 0x1218, 0x2071, 0x0020, 0x0078, 0x121a,
+ 0x2071, 0x0050, 0x2091, 0x2200, 0x2079, 0x4e00, 0x2071, 0x0020,
+ 0x2091, 0x2300, 0x2079, 0x4e00, 0x7810, 0xd0ec, 0x0040, 0x122c,
+ 0x2079, 0x0100, 0x0078, 0x122e, 0x2079, 0x0200, 0x2071, 0x4e40,
+ 0x2091, 0x2400, 0x2079, 0x0100, 0x2071, 0x4e80, 0x2091, 0x2000,
+ 0x2079, 0x4e00, 0x2071, 0x0010, 0x3200, 0xa085, 0x303d, 0x2090,
+ 0x2071, 0x0010, 0x70c3, 0x0000, 0x0090, 0x124d, 0x70c0, 0xa086,
+ 0x0002, 0x00c0, 0x124d, 0x1078, 0x15ba, 0x2039, 0x0000, 0x7810,
+ 0xd0ec, 0x00c0, 0x12cf, 0x1078, 0x148e, 0x78ac, 0xa005, 0x00c0,
+ 0x126b, 0x0068, 0x1261, 0x786c, 0xa065, 0x0040, 0x1261, 0x1078,
+ 0x2395, 0x1078, 0x20a1, 0x0068, 0x1278, 0x786c, 0xa065, 0x0040,
+ 0x126b, 0x1078, 0x2395, 0x0068, 0x1278, 0x2009, 0x4e47, 0x2011,
+ 0x4e87, 0x2104, 0x220c, 0xa105, 0x0040, 0x1278, 0x1078, 0x1f0a,
+ 0x2071, 0x4e40, 0x70a4, 0xa005, 0x0040, 0x129d, 0x7450, 0xa485,
+ 0x0000, 0x0040, 0x129d, 0x2079, 0x0200, 0x2091, 0x8000, 0x72d4,
+ 0xa28c, 0x303d, 0x2190, 0x1078, 0x2b6a, 0x2091, 0x8000, 0x2091,
+ 0x303d, 0x0068, 0x129d, 0x2079, 0x4e00, 0x786c, 0xa065, 0x0040,
+ 0x129d, 0x2071, 0x0010, 0x1078, 0x2395, 0x00e0, 0x12a5, 0x2079,
+ 0x4e00, 0x2071, 0x0010, 0x1078, 0x4a43, 0x2071, 0x4e80, 0x70a4,
+ 0xa005, 0x0040, 0x12bd, 0x7050, 0xa025, 0x0040, 0x12bd, 0x2079,
+ 0x0100, 0x2091, 0x8000, 0x72d4, 0xa28c, 0x303d, 0x2190, 0x1078,
+ 0x2b6a, 0x2091, 0x8000, 0x2091, 0x303d, 0x2079, 0x4e00, 0x2071,
+ 0x0010, 0x0068, 0x12c9, 0x786c, 0xa065, 0x0040, 0x12c9, 0x1078,
+ 0x2395, 0x00e0, 0x1253, 0x1078, 0x4a43, 0x0078, 0x1253, 0x1078,
+ 0x148e, 0x78ac, 0xa005, 0x00c0, 0x12e7, 0x0068, 0x12dd, 0x786c,
+ 0xa065, 0x0040, 0x12dd, 0x1078, 0x2395, 0x1078, 0x20a1, 0x0068,
+ 0x12f1, 0x786c, 0xa065, 0x0040, 0x12e7, 0x1078, 0x2395, 0x0068,
+ 0x12f1, 0x2009, 0x4e47, 0x2104, 0xa005, 0x0040, 0x12f1, 0x1078,
+ 0x1f0a, 0x2071, 0x4e40, 0x70a4, 0xa005, 0x0040, 0x130c, 0x7450,
+ 0xa485, 0x0000, 0x0040, 0x130c, 0x2079, 0x0100, 0x2091, 0x8000,
+ 0x72d4, 0xa28c, 0x303d, 0x2190, 0x1078, 0x2b6a, 0x2091, 0x8000,
+ 0x2091, 0x303d, 0x2079, 0x4e00, 0x2071, 0x0010, 0x0068, 0x1316,
+ 0x786c, 0xa065, 0x0040, 0x1316, 0x1078, 0x2395, 0x00e0, 0x12cf,
+ 0x1078, 0x4a43, 0x0078, 0x12cf, 0x133c, 0x133c, 0x133e, 0x133e,
+ 0x134b, 0x134b, 0x134b, 0x134b, 0x1356, 0x1356, 0x1363, 0x1363,
+ 0x134b, 0x134b, 0x134b, 0x134b, 0x133c, 0x133c, 0x133e, 0x133e,
+ 0x134b, 0x134b, 0x134b, 0x134b, 0x1356, 0x1356, 0x1363, 0x1363,
+ 0x134b, 0x134b, 0x134b, 0x134b, 0x0078, 0x133c, 0x007e, 0x107e,
+ 0x127e, 0x2091, 0x2400, 0x1078, 0x298a, 0x127f, 0x107f, 0x007f,
+ 0x2091, 0x8001, 0x007c, 0x007e, 0x107e, 0x127e, 0x1078, 0x13c8,
+ 0x127f, 0x107f, 0x007f, 0x2091, 0x8001, 0x007c, 0x007e, 0x107e,
+ 0x127e, 0x2091, 0x2300, 0x1078, 0x298a, 0x127f, 0x107f, 0x007f,
+ 0x2091, 0x8001, 0x007c, 0x007e, 0x107e, 0x127e, 0x2091, 0x2300,
+ 0x1078, 0x298a, 0x2091, 0x2400, 0x1078, 0x298a, 0x127f, 0x107f,
+ 0x007f, 0x2091, 0x8001, 0x007c, 0x1394, 0x1394, 0x1396, 0x1396,
+ 0x13a3, 0x13a3, 0x13a3, 0x13a3, 0x13ae, 0x13ae, 0x1396, 0x1396,
+ 0x13a3, 0x13a3, 0x13a3, 0x13a3, 0x13af, 0x13af, 0x13af, 0x13af,
+ 0x13af, 0x13af, 0x13af, 0x13af, 0x13af, 0x13af, 0x13af, 0x13af,
+ 0x13af, 0x13af, 0x13af, 0x13af, 0x0078, 0x1394, 0x007e, 0x107e,
+ 0x127e, 0x2091, 0x2300, 0x1078, 0x298a, 0x127f, 0x107f, 0x007f,
+ 0x2091, 0x8001, 0x007c, 0x007e, 0x107e, 0x127e, 0x1078, 0x13d5,
+ 0x127f, 0x107f, 0x007f, 0x2091, 0x8001, 0x007c, 0x007c, 0x107e,
+ 0x127e, 0x0d7e, 0x0e7e, 0x0f7e, 0x007e, 0x2071, 0x0100, 0x2069,
+ 0x4e40, 0x2079, 0x4e00, 0x70ec, 0xa084, 0x1c00, 0x78e2, 0x1078,
+ 0x4cdd, 0x007f, 0x0f7f, 0x0e7f, 0x0d7f, 0x127f, 0x107f, 0x007c,
+ 0x3c00, 0xa084, 0x0007, 0x0079, 0x13cd, 0x13de, 0x13de, 0x13e0,
+ 0x13e0, 0x13e5, 0x13e5, 0x13ea, 0x13ea, 0x3c00, 0xa084, 0x0003,
+ 0x0079, 0x13da, 0x13de, 0x13de, 0x13f3, 0x13f3, 0x1078, 0x296b,
+ 0x2091, 0x2200, 0x1078, 0x4768, 0x007c, 0x2091, 0x2100, 0x1078,
+ 0x4768, 0x007c, 0x2091, 0x2100, 0x1078, 0x4768, 0x2091, 0x2200,
+ 0x1078, 0x4768, 0x007c, 0x2091, 0x2100, 0x1078, 0x4768, 0x007c,
+ 0x1418, 0x1418, 0x141a, 0x141a, 0x1427, 0x1427, 0x1427, 0x1427,
+ 0x1432, 0x1432, 0x143f, 0x143f, 0x1427, 0x1427, 0x1427, 0x1427,
+ 0x1450, 0x1450, 0x1450, 0x1450, 0x1450, 0x1450, 0x1450, 0x1450,
+ 0x1450, 0x1450, 0x1450, 0x1450, 0x1450, 0x1450, 0x1450, 0x1450,
+ 0x0078, 0x1418, 0x007e, 0x107e, 0x127e, 0x2091, 0x2400, 0x1078,
+ 0x298a, 0x127f, 0x107f, 0x007f, 0x2091, 0x8001, 0x007c, 0x007e,
+ 0x107e, 0x127e, 0x1078, 0x13c8, 0x127f, 0x107f, 0x007f, 0x2091,
+ 0x8001, 0x007c, 0x007e, 0x107e, 0x127e, 0x2091, 0x2300, 0x1078,
+ 0x298a, 0x127f, 0x107f, 0x007f, 0x2091, 0x8001, 0x007c, 0x007e,
+ 0x107e, 0x127e, 0x2091, 0x2300, 0x1078, 0x298a, 0x2091, 0x2400,
+ 0x1078, 0x298a, 0x127f, 0x107f, 0x007f, 0x2091, 0x8001, 0x007c,
+ 0x007e, 0x107e, 0x127e, 0x0d7e, 0x0e7e, 0x0f7e, 0x2079, 0x4e00,
+ 0x2071, 0x0200, 0x2069, 0x4e40, 0x3d00, 0xd08c, 0x0040, 0x1466,
+ 0x70ec, 0xa084, 0x1c00, 0x78e2, 0x1078, 0x4cdd, 0x3d00, 0xd084,
+ 0x0040, 0x1474, 0x2069, 0x4e80, 0x2071, 0x0100, 0x70ec, 0xa084,
+ 0x1c00, 0x78e6, 0x1078, 0x4cdd, 0x0f7f, 0x0e7f, 0x0d7f, 0x127f,
+ 0x107f, 0x007f, 0x007c, 0x7008, 0x800b, 0x00c8, 0x1489, 0x7007,
+ 0x0002, 0xa08c, 0x01e0, 0x00c0, 0x148a, 0xd09c, 0x0040, 0x1489,
+ 0x087a, 0x097a, 0x70c3, 0x4002, 0x0078, 0x15bd, 0x0068, 0x1513,
+ 0x2061, 0x0000, 0x6018, 0xd084, 0x00c0, 0x1513, 0x7828, 0xa005,
+ 0x00c0, 0x149e, 0x0010, 0x1514, 0x0078, 0x1513, 0x7910, 0xd1f4,
+ 0x0040, 0x14a6, 0x2001, 0x4007, 0x0078, 0x15bc, 0x7914, 0xd1ec,
+ 0x0040, 0x14c1, 0xd0fc, 0x0040, 0x14b7, 0x007e, 0x1078, 0x1d64,
+ 0x007f, 0x0040, 0x14c1, 0x2001, 0x4007, 0x0078, 0x15bc, 0x007e,
+ 0x1078, 0x1d54, 0x007f, 0x0040, 0x14c1, 0x2001, 0x4007, 0x0078,
+ 0x15bc, 0x7910, 0xd0fc, 0x00c0, 0x14cb, 0x2061, 0x4e40, 0xc19c,
+ 0xc7fc, 0x0078, 0x14cf, 0x2061, 0x4e80, 0xc19d, 0xc7fd, 0x6064,
+ 0xa005, 0x00c0, 0x1513, 0x7912, 0x6083, 0x0000, 0x7828, 0xc0fc,
+ 0xa086, 0x0018, 0x00c0, 0x14e0, 0x0c7e, 0x1078, 0x1b5b, 0x0c7f,
+ 0x782b, 0x0000, 0x607c, 0xa065, 0x0040, 0x14f9, 0x0c7e, 0x609c,
+ 0x1078, 0x1e49, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1c84, 0x2009,
+ 0x0018, 0x6087, 0x0103, 0x1078, 0x1d74, 0x00c0, 0x150d, 0x1078,
+ 0x1dc6, 0x7810, 0xd09c, 0x00c0, 0x1501, 0x2061, 0x4e40, 0x0078,
+ 0x1505, 0x2061, 0x4e80, 0xc09c, 0x7812, 0x607f, 0x0000, 0x60d4,
+ 0xd0dc, 0x0040, 0x1511, 0xc0dc, 0x60d6, 0x2001, 0x4005, 0x0078,
+ 0x15bc, 0x0078, 0x15ba, 0x007c, 0x7810, 0xd0f4, 0x0040, 0x151c,
+ 0x2001, 0x4007, 0x0078, 0x15bc, 0xa006, 0x70c2, 0x70c6, 0x70ca,
+ 0x70ce, 0x70da, 0x70c0, 0xa03d, 0xa08a, 0x0040, 0x00c8, 0x152a,
+ 0x0079, 0x1531, 0x2100, 0xa08a, 0x0040, 0x00c8, 0x15c8, 0x0079,
+ 0x1571, 0x15ba, 0x1610, 0x15d9, 0x1648, 0x1680, 0x1680, 0x15d0,
+ 0x1c9c, 0x168b, 0x15c8, 0x15dd, 0x15df, 0x15e1, 0x15e3, 0x1ca1,
+ 0x15c8, 0x1699, 0x16f6, 0x1b7b, 0x1c96, 0x15e5, 0x19c0, 0x1a02,
+ 0x1a3d, 0x1a8e, 0x197b, 0x1988, 0x199c, 0x19af, 0x17cb, 0x15c8,
+ 0x172d, 0x173a, 0x1746, 0x1752, 0x1768, 0x1774, 0x1777, 0x1783,
+ 0x178f, 0x1797, 0x17b3, 0x17bf, 0x15c8, 0x15c8, 0x15c8, 0x15c8,
+ 0x17d8, 0x17ea, 0x1806, 0x183c, 0x1864, 0x1874, 0x1877, 0x18a8,
+ 0x18d9, 0x18eb, 0x194a, 0x195a, 0x15c8, 0x15c8, 0x15c8, 0x15c8,
+ 0x196a, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x1cc6, 0x1ccc,
+ 0x15c8, 0x15c8, 0x15c8, 0x1cd0, 0x1d15, 0x15c8, 0x15c8, 0x15c8,
+ 0x15c8, 0x160a, 0x167a, 0x1693, 0x16f0, 0x1b75, 0x15c8, 0x15c8,
+ 0x1b3e, 0x15c8, 0x1d19, 0x1cb8, 0x1cc2, 0x15c8, 0x15c8, 0x15c8,
+ 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8,
+ 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8,
+ 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8,
+ 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8, 0x15c8,
+ 0x15c8, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0078, 0x15bc, 0x73ce,
+ 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x0068, 0x15bd, 0x2061,
+ 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x007c,
+ 0x70c3, 0x4001, 0x0078, 0x15bd, 0x70c3, 0x4006, 0x0078, 0x15bd,
+ 0x2099, 0x0041, 0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0078,
+ 0x15ba, 0x70c4, 0x70c3, 0x0004, 0x007a, 0x0078, 0x15ba, 0x0078,
+ 0x15ba, 0x0078, 0x15ba, 0x0078, 0x15ba, 0x2091, 0x8000, 0x70c3,
+ 0x0004, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3,
+ 0x0008, 0x2001, 0x000f, 0x70d6, 0x2079, 0x0000, 0x781b, 0x0001,
+ 0x2031, 0x0030, 0x2059, 0x1000, 0x2029, 0x041a, 0x2051, 0x0445,
+ 0x2061, 0x0447, 0x20c1, 0x0020, 0x2091, 0x5000, 0x2091, 0x4080,
+ 0x0078, 0x0418, 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x1613,
+ 0x2029, 0x0000, 0x2520, 0x71d0, 0x72c8, 0x73cc, 0x70c4, 0x20a0,
+ 0x2099, 0x0030, 0x7003, 0x0001, 0x7007, 0x0006, 0x731a, 0x721e,
+ 0x7422, 0x7526, 0x2021, 0x0040, 0x81ff, 0x0040, 0x15ba, 0xa182,
+ 0x0040, 0x00c8, 0x162d, 0x2120, 0xa006, 0x2008, 0x8403, 0x7012,
+ 0x7007, 0x0004, 0x7007, 0x0001, 0x7008, 0xd0fc, 0x0040, 0x1634,
+ 0x7007, 0x0002, 0xa084, 0x01e0, 0x0040, 0x1642, 0x70c3, 0x4002,
+ 0x0078, 0x15bd, 0x24a8, 0x53a5, 0x0078, 0x1624, 0x0078, 0x15ba,
+ 0x2029, 0x0000, 0x2520, 0x71d0, 0x72c8, 0x73cc, 0x70c4, 0x2098,
+ 0x20a1, 0x0030, 0x7003, 0x0000, 0x7007, 0x0006, 0x731a, 0x721e,
+ 0x7422, 0x7526, 0x2021, 0x0040, 0x7007, 0x0006, 0x81ff, 0x0040,
+ 0x15ba, 0xa182, 0x0040, 0x00c8, 0x1667, 0x2120, 0xa006, 0x2008,
+ 0x8403, 0x7012, 0x24a8, 0x53a6, 0x7007, 0x0001, 0x7008, 0xd0fc,
+ 0x0040, 0x166e, 0xa084, 0x01e0, 0x0040, 0x165c, 0x70c3, 0x4002,
+ 0x0078, 0x15bd, 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x164b,
+ 0x71c4, 0x70c8, 0x2114, 0xa79e, 0x0004, 0x00c0, 0x1688, 0x200a,
+ 0x72ca, 0x0078, 0x15b9, 0x70c7, 0x0008, 0x70cb, 0x000f, 0x70cf,
+ 0x0000, 0x0078, 0x15ba, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078,
+ 0x169c, 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d0,
+ 0x70c6, 0x72ca, 0x73ce, 0x74d2, 0xa005, 0x0040, 0x16eb, 0xa40a,
+ 0x0040, 0x16ac, 0x00c8, 0x16b5, 0x8001, 0x7872, 0xa084, 0xfc00,
+ 0x0040, 0x16b9, 0x78ac, 0xc085, 0x78ae, 0x2001, 0x4005, 0x0078,
+ 0x15bc, 0x7b7e, 0x7a7a, 0x7e86, 0x7d82, 0x7c76, 0xa48c, 0xff00,
+ 0x0040, 0x16d1, 0x8407, 0x8004, 0x8004, 0x810c, 0x810c, 0x810f,
+ 0xa118, 0xa291, 0x0000, 0xa6b1, 0x0000, 0xa581, 0x0000, 0x0078,
+ 0x16db, 0x8407, 0x8004, 0x8004, 0xa318, 0xa291, 0x0000, 0xa6b1,
+ 0x0000, 0xa581, 0x0000, 0x731a, 0x721e, 0x7622, 0x7026, 0xa605,
+ 0x0040, 0x16e5, 0x7a10, 0xc2c5, 0x7a12, 0x78ac, 0xa084, 0xfffc,
+ 0x78ae, 0x0078, 0x16ee, 0x78ac, 0xc085, 0x78ae, 0x0078, 0x15ba,
+ 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078, 0x16f9, 0x2029, 0x0000,
+ 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d4, 0x70c6, 0x72ca, 0x73ce,
+ 0x74d6, 0xa005, 0x0040, 0x1728, 0xa40a, 0x0040, 0x1709, 0x00c8,
+ 0x15bc, 0x8001, 0x7892, 0xa084, 0xfc00, 0x0040, 0x1716, 0x78ac,
+ 0xc0c5, 0x78ae, 0x2001, 0x4005, 0x0078, 0x15bc, 0x7a9a, 0x7b9e,
+ 0x7da2, 0x7ea6, 0x2600, 0xa505, 0x0040, 0x1721, 0x7a10, 0xc2c5,
+ 0x7a12, 0x7c96, 0x78ac, 0xa084, 0xfcff, 0x78ae, 0x0078, 0x172b,
+ 0x78ac, 0xc0c5, 0x78ae, 0x0078, 0x15ba, 0x2009, 0x0000, 0x786c,
+ 0xa065, 0x0040, 0x1737, 0x8108, 0x6000, 0x0078, 0x1730, 0x7ac4,
+ 0x0078, 0x15b8, 0x2009, 0x4e48, 0x210c, 0x7810, 0xd0ec, 0x00c0,
+ 0x15b9, 0x2011, 0x4e88, 0x2214, 0x0078, 0x15b8, 0x2009, 0x4e49,
+ 0x210c, 0x7810, 0xd0ec, 0x00c0, 0x15b9, 0x2011, 0x4e89, 0x2214,
+ 0x0078, 0x15b8, 0x2061, 0x4e40, 0x6128, 0x622c, 0x8214, 0x8214,
+ 0x8214, 0x7810, 0xd0ec, 0x00c0, 0x1766, 0x2061, 0x4e80, 0x6328,
+ 0x73da, 0x632c, 0x831c, 0x831c, 0x831c, 0x73de, 0x0078, 0x15b8,
+ 0x2009, 0x4e4c, 0x210c, 0x7810, 0xd0ec, 0x00c0, 0x15b9, 0x2011,
+ 0x4e8c, 0x2214, 0x0078, 0x15b8, 0x7918, 0x0078, 0x15b9, 0x2009,
+ 0x4e4d, 0x210c, 0x7810, 0xd0ec, 0x00c0, 0x15b9, 0x2011, 0x4e8d,
+ 0x2214, 0x0078, 0x15b8, 0x2009, 0x4e4e, 0x210c, 0x7810, 0xd0ec,
+ 0x00c0, 0x15b9, 0x2011, 0x4e8e, 0x2214, 0x0078, 0x15b8, 0x7920,
+ 0x7810, 0xd0ec, 0x00c0, 0x15b9, 0x7a24, 0x0078, 0x15b8, 0x71c4,
+ 0xd1fc, 0x00c0, 0x179f, 0x2011, 0x52c0, 0x0078, 0x17a1, 0x2011,
+ 0x5340, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa268,
+ 0x6a00, 0x6804, 0xd09c, 0x0040, 0x17b0, 0x6b08, 0x0078, 0x17b1,
+ 0x6b0c, 0x0078, 0x15b7, 0x77c4, 0x1078, 0x1de4, 0x2091, 0x8000,
+ 0x6b1c, 0x6a14, 0x2091, 0x8001, 0x2708, 0x0078, 0x15b7, 0x2061,
+ 0x4e40, 0x6118, 0x7810, 0xd0ec, 0x00c0, 0x15b9, 0x2061, 0x4e80,
+ 0x6218, 0x0078, 0x15b8, 0x77c4, 0x1078, 0x1de4, 0x2091, 0x8000,
+ 0x6908, 0x6a18, 0x6b10, 0x77da, 0x2091, 0x8001, 0x0078, 0x15b7,
+ 0x71c4, 0x2110, 0xa294, 0x000f, 0xa282, 0x0010, 0x00c8, 0x15b2,
+ 0x1078, 0x277f, 0xa384, 0x4000, 0x0040, 0x17e8, 0xa295, 0x0020,
+ 0x0078, 0x15b7, 0x71c4, 0x2100, 0xc0bc, 0xa082, 0x0010, 0x00c8,
+ 0x15b2, 0xd1bc, 0x00c0, 0x17f9, 0x2011, 0x4e48, 0x2204, 0x0078,
+ 0x17fd, 0x2011, 0x4e88, 0x2204, 0xc0bd, 0x007e, 0x2100, 0xc0bc,
+ 0x2012, 0x1078, 0x26dc, 0x017f, 0x0078, 0x15b9, 0x71c4, 0x2021,
+ 0x4e49, 0x2404, 0x70c6, 0x2019, 0x0000, 0x0078, 0x1815, 0x71c8,
+ 0x2021, 0x4e89, 0x2404, 0x70ca, 0xc3fd, 0x2011, 0x1834, 0x20a9,
+ 0x0008, 0x2204, 0xa106, 0x0040, 0x1824, 0x8210, 0x00f0, 0x1819,
+ 0x71c4, 0x72c8, 0x0078, 0x15b1, 0xa292, 0x1834, 0x027e, 0x2122,
+ 0x017f, 0x1078, 0x26fd, 0x7810, 0xd0ec, 0x00c0, 0x1832, 0xd3fc,
+ 0x0040, 0x180f, 0x0078, 0x15ba, 0x03e8, 0x00fa, 0x01f4, 0x02ee,
+ 0x0004, 0x0001, 0x0002, 0x0003, 0x2061, 0x4e40, 0x6128, 0x622c,
+ 0x8214, 0x8214, 0x8214, 0x70c4, 0x602a, 0x70c8, 0x8003, 0x8003,
+ 0x8003, 0x602e, 0x7810, 0xd0ec, 0x00c0, 0x1862, 0x027e, 0x017e,
+ 0x2061, 0x4e80, 0x6128, 0x622c, 0x8214, 0x8214, 0x8214, 0x70d8,
+ 0x602a, 0x70dc, 0x8003, 0x8003, 0x8003, 0x602e, 0x71da, 0x72de,
+ 0x017f, 0x027f, 0x0078, 0x15b8, 0x2061, 0x4e40, 0x6130, 0x70c4,
+ 0x6032, 0x7810, 0xd0ec, 0x00c0, 0x15b9, 0x2061, 0x4e80, 0x6230,
+ 0x70c8, 0x6032, 0x0078, 0x15b8, 0x7918, 0x0078, 0x15b9, 0x71c4,
+ 0xa184, 0xffcf, 0x0040, 0x1883, 0x7810, 0xd0ec, 0x00c0, 0x15b2,
+ 0x72c8, 0x0078, 0x15b1, 0x2011, 0x4e4d, 0x2204, 0x2112, 0x007e,
+ 0x2019, 0x0000, 0x1078, 0x2764, 0x7810, 0xd0ec, 0x0040, 0x1893,
+ 0x017f, 0x0078, 0x15b9, 0x71c8, 0xa184, 0xffcf, 0x0040, 0x189c,
+ 0x2110, 0x71c4, 0x0078, 0x15b1, 0x2011, 0x4e8d, 0x2204, 0x2112,
+ 0x007e, 0xc3fd, 0x1078, 0x2764, 0x027f, 0x017f, 0x0078, 0x15b8,
+ 0x71c4, 0xa182, 0x0010, 0x0048, 0x18b4, 0x7810, 0xd0ec, 0x00c0,
+ 0x15b2, 0x72c8, 0x0078, 0x15b1, 0x2011, 0x4e4e, 0x2204, 0x007e,
+ 0x2112, 0x2019, 0x0000, 0x1078, 0x2742, 0x7810, 0xd0ec, 0x0040,
+ 0x18c4, 0x017f, 0x0078, 0x15b9, 0x71c8, 0xa182, 0x0010, 0x0048,
+ 0x18cd, 0x2110, 0x71c4, 0x0078, 0x15b1, 0x2011, 0x4e8e, 0x2204,
+ 0x007e, 0x2112, 0xc3fd, 0x1078, 0x2742, 0x027f, 0x017f, 0x0078,
+ 0x15b8, 0x71c4, 0x72c8, 0xa184, 0xfffd, 0x00c0, 0x15b1, 0xa284,
+ 0xfffd, 0x00c0, 0x15b1, 0x2100, 0x7920, 0x7822, 0x2200, 0x7a24,
+ 0x7826, 0x0078, 0x15b8, 0x71c4, 0xd1fc, 0x00c0, 0x18f3, 0x2011,
+ 0x52c0, 0x0078, 0x18f5, 0x2011, 0x5340, 0x8107, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa268, 0x2019, 0x0000, 0x72c8, 0x2091,
+ 0x8000, 0x6800, 0x007e, 0xa226, 0x0040, 0x191e, 0x6a02, 0xd4ec,
+ 0x0040, 0x190b, 0xc3a5, 0xd4e4, 0x0040, 0x190f, 0xc39d, 0xd4f4,
+ 0x0040, 0x191e, 0x810f, 0xd2f4, 0x0040, 0x191a, 0x1078, 0x27c1,
+ 0x0078, 0x191e, 0x1078, 0x279f, 0x0078, 0x191e, 0x72cc, 0x6808,
+ 0xa206, 0x0040, 0x1940, 0xa2a4, 0x00ff, 0x7814, 0xd0e4, 0x00c0,
+ 0x1931, 0xa482, 0x0028, 0x0048, 0x193d, 0x0040, 0x193d, 0x0078,
+ 0x1935, 0xa482, 0x0043, 0x0048, 0x193d, 0x71c4, 0x71c6, 0x027f,
+ 0x72ca, 0x2091, 0x8001, 0x0078, 0x15b3, 0x6a0a, 0xa39d, 0x000a,
+ 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c, 0x71c4, 0x2091, 0x8001,
+ 0x0078, 0x15b7, 0x77c4, 0x1078, 0x1de4, 0x2091, 0x8000, 0x6a14,
+ 0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e, 0x2708,
+ 0x0078, 0x15b7, 0x70c4, 0x2061, 0x4e40, 0x6118, 0x601a, 0x7810,
+ 0xd0ec, 0x00c0, 0x15b9, 0x70c8, 0x2061, 0x4e80, 0x6218, 0x601a,
+ 0x0078, 0x15b8, 0x71c4, 0x72c8, 0x73cc, 0xa182, 0x0010, 0x00c8,
+ 0x15b2, 0x1078, 0x27e3, 0xa384, 0x4000, 0x0040, 0x1979, 0xa295,
+ 0x0020, 0x0078, 0x15b7, 0x77c4, 0x1078, 0x1de4, 0x2091, 0x8000,
+ 0x6a08, 0xc28d, 0x6a0a, 0x2091, 0x8001, 0x2708, 0x0078, 0x15b8,
+ 0x77c4, 0x1078, 0x1de4, 0x2091, 0x8000, 0x6a08, 0xa294, 0xfff9,
+ 0x6a0a, 0x6804, 0xa005, 0x0040, 0x1997, 0x1078, 0x2628, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x15b8, 0x77c4, 0x1078, 0x1de4, 0x2091,
+ 0x8000, 0x6a08, 0xc295, 0x6a0a, 0x6804, 0xa005, 0x0040, 0x19aa,
+ 0x1078, 0x2628, 0x2091, 0x8001, 0x2708, 0x0078, 0x15b8, 0x77c4,
+ 0x2041, 0x0001, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000,
+ 0x1078, 0x1dff, 0x2091, 0x8001, 0x2708, 0x6a08, 0x0078, 0x15b8,
+ 0x77c4, 0x7814, 0xd0e4, 0x00c0, 0x19d4, 0xd7fc, 0x0040, 0x19ce,
+ 0x1078, 0x1d64, 0x0040, 0x19d4, 0x0078, 0x15bc, 0x1078, 0x1d54,
+ 0x0040, 0x19d4, 0x0078, 0x15bc, 0x73c8, 0x72cc, 0x77c6, 0x73ca,
+ 0x72ce, 0x1078, 0x1e86, 0x00c0, 0x19fe, 0x6818, 0xa005, 0x0040,
+ 0x19f8, 0x2708, 0x077e, 0x1078, 0x2813, 0x077f, 0x00c0, 0x19f8,
+ 0x2001, 0x0015, 0xd7fc, 0x00c0, 0x19f1, 0x2061, 0x4e40, 0x0078,
+ 0x19f4, 0xc0fd, 0x2061, 0x4e80, 0x782a, 0x2091, 0x8001, 0x007c,
+ 0x2091, 0x8001, 0x2001, 0x4005, 0x0078, 0x15bc, 0x2091, 0x8001,
+ 0x0078, 0x15ba, 0x77c4, 0x7814, 0xd0e4, 0x00c0, 0x1a16, 0xd7fc,
+ 0x0040, 0x1a10, 0x1078, 0x1d64, 0x0040, 0x1a16, 0x0078, 0x15bc,
+ 0x1078, 0x1d54, 0x0040, 0x1a16, 0x0078, 0x15bc, 0x77c6, 0x2041,
+ 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078,
+ 0x1dff, 0x2009, 0x0016, 0xd7fc, 0x00c0, 0x1a2a, 0x2061, 0x4e40,
+ 0x0078, 0x1a2d, 0x2061, 0x4e80, 0xc1fd, 0x6067, 0x0003, 0x607f,
+ 0x0000, 0x6776, 0x6083, 0x000f, 0x792a, 0x61d4, 0xc1dc, 0x61d6,
+ 0x1078, 0x2628, 0x2091, 0x8001, 0x007c, 0x77c8, 0x77ca, 0x77c4,
+ 0x77c6, 0x7814, 0xd0e4, 0x00c0, 0x1a54, 0xd7fc, 0x0040, 0x1a4e,
+ 0x1078, 0x1d64, 0x0040, 0x1a54, 0x0078, 0x15bc, 0x1078, 0x1d54,
+ 0x0040, 0x1a54, 0x0078, 0x15bc, 0xa7bc, 0xff00, 0x2091, 0x8000,
+ 0x2009, 0x0017, 0xd7fc, 0x00c0, 0x1a61, 0x2061, 0x4e40, 0x0078,
+ 0x1a64, 0x2061, 0x4e80, 0xc1fd, 0x607f, 0x0000, 0x6067, 0x0002,
+ 0x6776, 0x6083, 0x000f, 0x792a, 0x61d4, 0xc1dc, 0x61d6, 0x1078,
+ 0x2628, 0x2091, 0x8001, 0x2041, 0x0021, 0x2049, 0x0005, 0x2051,
+ 0x0010, 0x2091, 0x8000, 0x70c8, 0xa005, 0x0040, 0x1a82, 0x60d4,
+ 0xc0fd, 0x60d6, 0x1078, 0x1dff, 0x70c8, 0x6836, 0x8738, 0xa784,
+ 0x001f, 0x00c0, 0x1a82, 0x2091, 0x8001, 0x007c, 0x2019, 0x0000,
+ 0x7814, 0xd0e4, 0x00c0, 0x1aa4, 0x72c8, 0xd284, 0x0040, 0x1a9e,
+ 0x1078, 0x1d64, 0x0040, 0x1aa4, 0x0078, 0x15bc, 0x1078, 0x1d54,
+ 0x0040, 0x1aa4, 0x0078, 0x15bc, 0x72c8, 0x72ca, 0x78ac, 0xa084,
+ 0x0003, 0x00c0, 0x1acf, 0x2039, 0x0000, 0xd284, 0x0040, 0x1ab1,
+ 0xc7fd, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008, 0x1078,
+ 0x1de4, 0x2091, 0x8000, 0x6808, 0xc0d4, 0xa80d, 0x690a, 0x2091,
+ 0x8001, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1ab7, 0xa7bc, 0xff00,
+ 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1ab7, 0x2091,
+ 0x8000, 0x72c8, 0xd284, 0x00c0, 0x1ae1, 0x7810, 0xd0ec, 0x0040,
+ 0x1add, 0x2069, 0x0100, 0x0078, 0x1ae3, 0x2069, 0x0200, 0x0078,
+ 0x1ae3, 0x2069, 0x0100, 0x6808, 0xa084, 0xfffd, 0x680a, 0x6830,
+ 0xd0b4, 0x0040, 0x1b03, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848,
+ 0xd094, 0x0040, 0x1af5, 0x00f0, 0x1aef, 0x684b, 0x0009, 0x20a9,
+ 0x0014, 0x6848, 0xd084, 0x0040, 0x1aff, 0x00f0, 0x1af9, 0x20a9,
+ 0x00fa, 0x00f0, 0x1b01, 0x2079, 0x4e00, 0x2009, 0x0018, 0x72c8,
+ 0xd284, 0x00c0, 0x1b0f, 0x2061, 0x4e40, 0x0078, 0x1b12, 0x2061,
+ 0x4e80, 0xc1fd, 0x607f, 0x0000, 0x792a, 0x6067, 0x0001, 0x6083,
+ 0x000f, 0x60a7, 0x0000, 0x60a8, 0x60b2, 0x60b6, 0x60d4, 0xd0b4,
+ 0x0040, 0x1b2e, 0xc0b4, 0x60d6, 0x0c7e, 0x60b8, 0xa065, 0x6008,
+ 0xc0d4, 0x600a, 0x6018, 0x8001, 0x601a, 0x0c7f, 0x60d4, 0xa084,
+ 0x77ff, 0x60d6, 0x78ac, 0xc08d, 0x78ae, 0x83ff, 0x0040, 0x1b39,
+ 0x007c, 0x681b, 0x0047, 0x2091, 0x8001, 0x007c, 0x73cc, 0x1078,
+ 0x1a90, 0x69ec, 0x6a48, 0xa185, 0x1800, 0x684a, 0xa185, 0x0040,
+ 0x68ee, 0x73cc, 0x2021, 0x0004, 0x20a9, 0x09ff, 0x00f0, 0x1b4e,
+ 0x8421, 0x00c0, 0x1b4c, 0x8319, 0x00c0, 0x1b4a, 0x69ee, 0x6a4a,
+ 0x2091, 0x8001, 0x007c, 0xd7fc, 0x00c0, 0x1b62, 0x2069, 0x4e40,
+ 0x0078, 0x1b64, 0x2069, 0x4e80, 0x71c4, 0x71c6, 0x6916, 0x81ff,
+ 0x00c0, 0x1b6c, 0x68a7, 0x0001, 0x78ac, 0xc08c, 0x78ae, 0xd084,
+ 0x00c0, 0x1b74, 0x1078, 0x1ee6, 0x007c, 0x75d8, 0x74dc, 0x75da,
+ 0x74de, 0x0078, 0x1b7e, 0x2029, 0x0000, 0x2520, 0x71c4, 0x73c8,
+ 0x72cc, 0x71c6, 0x73ca, 0x72ce, 0x2079, 0x4e00, 0x7dde, 0x7cda,
+ 0x7bd6, 0x7ad2, 0x1078, 0x1dbd, 0x0040, 0x1c80, 0x20a9, 0x0005,
+ 0x20a1, 0x4e14, 0x2091, 0x8000, 0x41a1, 0x2091, 0x8001, 0x2009,
+ 0x0040, 0x1078, 0x1fd1, 0x0040, 0x1ba1, 0x1078, 0x1dc6, 0x0078,
+ 0x1c80, 0x6004, 0xa08c, 0x00ff, 0xa18e, 0x0009, 0x00c0, 0x1bac,
+ 0x007e, 0x1078, 0x2378, 0x007f, 0xa084, 0xff00, 0x8007, 0x8009,
+ 0x0040, 0x1c20, 0x0c7e, 0x2c68, 0x1078, 0x1dbd, 0x0040, 0x1bf2,
+ 0x2c00, 0x689e, 0x8109, 0x00c0, 0x1bb3, 0x609f, 0x0000, 0x0c7f,
+ 0x0c7e, 0x7ddc, 0x7cd8, 0x7bd4, 0x7ad0, 0xa290, 0x0040, 0xa399,
+ 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x7dde, 0x7cda, 0x7bd6,
+ 0x7ad2, 0x2c68, 0x689c, 0xa065, 0x0040, 0x1c1f, 0x2009, 0x0040,
+ 0x1078, 0x1fd1, 0x00c0, 0x1c09, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0002, 0x00c0, 0x1bf2, 0x6004, 0xa084, 0x00ff, 0xa086, 0x000a,
+ 0x00c0, 0x1bee, 0x017e, 0x1078, 0x2374, 0x017f, 0x2d00, 0x6002,
+ 0x0078, 0x1bc1, 0x0c7f, 0x0c7e, 0x609c, 0x1078, 0x1e49, 0x0c7f,
+ 0x609f, 0x0000, 0x1078, 0x1c84, 0x2009, 0x0018, 0x6008, 0xc0cd,
+ 0x600a, 0x6004, 0x6086, 0x1078, 0x1d74, 0x1078, 0x1dc6, 0x0078,
+ 0x1c80, 0x0c7f, 0x0c7e, 0x609c, 0x1078, 0x1e49, 0x0c7f, 0x609f,
+ 0x0000, 0x1078, 0x1c84, 0x2009, 0x0018, 0x6087, 0x0103, 0x601b,
+ 0x0003, 0x1078, 0x1d74, 0x1078, 0x1dc6, 0x0078, 0x1c80, 0x0c7f,
+ 0x7814, 0xd0e4, 0x00c0, 0x1c45, 0x6114, 0xd1fc, 0x0040, 0x1c2e,
+ 0x1078, 0x1d64, 0x0040, 0x1c45, 0x0078, 0x1c32, 0x1078, 0x1d54,
+ 0x0040, 0x1c45, 0x2029, 0x0000, 0x2520, 0x2009, 0x0018, 0x73c8,
+ 0x72cc, 0x6087, 0x0103, 0x601b, 0x0021, 0x1078, 0x1d74, 0x1078,
+ 0x1dc6, 0x2001, 0x4007, 0x0078, 0x15bc, 0x74c4, 0x73c8, 0x72cc,
+ 0x6014, 0x2091, 0x8000, 0x0e7e, 0x2009, 0x0012, 0xd0fc, 0x00c0,
+ 0x1c55, 0x2071, 0x4e40, 0x0078, 0x1c58, 0x2071, 0x4e80, 0xc1fd,
+ 0x792a, 0x7067, 0x0005, 0x71d4, 0xc1dc, 0x71d6, 0x736a, 0x726e,
+ 0x7472, 0x7076, 0x707b, 0x0000, 0x2c00, 0x707e, 0xa02e, 0x2530,
+ 0x611c, 0xa184, 0x0060, 0x0040, 0x1c6f, 0x1078, 0x4632, 0x0e7f,
+ 0x6596, 0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000, 0x60b3, 0x0000,
+ 0x6714, 0x6023, 0x0000, 0x1078, 0x2628, 0x2091, 0x8001, 0x007c,
+ 0x70c3, 0x4005, 0x0078, 0x15bd, 0x20a9, 0x0005, 0x2099, 0x4e14,
+ 0x2091, 0x8000, 0x530a, 0x2091, 0x8001, 0x2100, 0xa210, 0xa399,
+ 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x007c, 0x71c4, 0x70c7,
+ 0x0000, 0x791e, 0x0078, 0x15ba, 0x71c4, 0x71c6, 0x2168, 0x0078,
+ 0x1ca3, 0x2069, 0x1000, 0x690c, 0xa016, 0x2d04, 0xa210, 0x8d68,
+ 0x8109, 0x00c0, 0x1ca5, 0xa285, 0x0000, 0x00c0, 0x1cb3, 0x70c3,
+ 0x4000, 0x0078, 0x1cb5, 0x70c3, 0x4003, 0x70ca, 0x0078, 0x15bd,
+ 0x7964, 0x71c6, 0x71c4, 0xa182, 0x0003, 0x00c8, 0x15b2, 0x7966,
+ 0x0078, 0x15ba, 0x7964, 0x71c6, 0x0078, 0x15ba, 0x7900, 0x71c6,
+ 0x71c4, 0x7902, 0x0078, 0x15ba, 0x7900, 0x71c6, 0x0078, 0x15ba,
+ 0x70c4, 0x2011, 0x0000, 0xa08c, 0x000d, 0x0040, 0x1ce5, 0x810c,
+ 0x0048, 0x1ce1, 0x8210, 0x810c, 0x810c, 0x0048, 0x1ce1, 0x8210,
+ 0x810c, 0x81ff, 0x00c0, 0x15b3, 0x8210, 0x7a0e, 0xd28c, 0x0040,
+ 0x1d11, 0x7910, 0xc1cd, 0x7912, 0x2009, 0x0021, 0x2019, 0x0003,
+ 0xd284, 0x0040, 0x1d0b, 0x8108, 0x2019, 0x0041, 0x2011, 0x964e,
+ 0x2312, 0x2019, 0x0042, 0x8210, 0x2312, 0x2019, 0x0043, 0x8210,
+ 0x2312, 0x2019, 0x0046, 0x8210, 0x2312, 0x2019, 0x0047, 0x8210,
+ 0x2312, 0x2019, 0x0006, 0x2011, 0x9653, 0x2112, 0x2011, 0x9673,
+ 0x2312, 0x7904, 0x7806, 0x0078, 0x15b9, 0x7804, 0x70c6, 0x0078,
+ 0x15ba, 0x71c4, 0xd1fc, 0x00c0, 0x1d21, 0x2011, 0x52c0, 0x0078,
+ 0x1d23, 0x2011, 0x5340, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003,
+ 0x8003, 0xa268, 0x6a14, 0xd2b4, 0x0040, 0x1d32, 0x2011, 0x0001,
+ 0x0078, 0x1d34, 0x2011, 0x0000, 0x6b0c, 0x6800, 0x70da, 0x0078,
+ 0x15b7, 0x017e, 0x7814, 0xd0f4, 0x0040, 0x1d46, 0x2001, 0x4007,
+ 0x70db, 0x0000, 0xa18d, 0x0001, 0x0078, 0x1d52, 0xd0fc, 0x0040,
+ 0x1d51, 0x2001, 0x4007, 0x70db, 0x0001, 0xa18d, 0x0001, 0x0078,
+ 0x1d52, 0xa006, 0x017f, 0x007c, 0x017e, 0x7814, 0xd0f4, 0x0040,
+ 0x1d61, 0x2001, 0x4007, 0x70db, 0x0000, 0xa18d, 0x0001, 0x0078,
+ 0x1d62, 0xa006, 0x017f, 0x007c, 0x017e, 0x7814, 0xd0fc, 0x0040,
+ 0x1d71, 0x2001, 0x4007, 0x70db, 0x0001, 0xa18d, 0x0001, 0x0078,
+ 0x1d72, 0xa006, 0x017f, 0x007c, 0x7112, 0x721a, 0x731e, 0x7810,
+ 0xd0c4, 0x0040, 0x1d7d, 0x7422, 0x7526, 0xac80, 0x0001, 0x8108,
+ 0x810c, 0x81a9, 0x8098, 0x20a1, 0x0030, 0x7003, 0x0000, 0x6084,
+ 0x20a2, 0x53a6, 0x7007, 0x0001, 0x7974, 0xa184, 0xff00, 0x0040,
+ 0x1d9a, 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007, 0xa100,
+ 0x0078, 0x1d9d, 0x8107, 0x8004, 0x8004, 0x797c, 0xa108, 0x7a78,
+ 0xa006, 0xa211, 0x7d10, 0xd5c4, 0x0040, 0x1daa, 0x7b84, 0xa319,
+ 0x7c80, 0xa421, 0x7008, 0xd0fc, 0x0040, 0x1daa, 0x7003, 0x0001,
+ 0x7007, 0x0006, 0x711a, 0x721e, 0x7d10, 0xd5c4, 0x0040, 0x1dba,
+ 0x7322, 0x7426, 0xa084, 0x01e0, 0x007c, 0x7848, 0xa065, 0x0040,
+ 0x1dc5, 0x2c04, 0x784a, 0x2063, 0x0000, 0x007c, 0x0f7e, 0x2079,
+ 0x4e00, 0x7848, 0x2062, 0x2c00, 0xa005, 0x00c0, 0x1dd1, 0x1078,
+ 0x296b, 0x784a, 0x0f7f, 0x007c, 0x2011, 0x9800, 0x7a4a, 0x7bc4,
+ 0x8319, 0x0040, 0x1de1, 0xa280, 0x0032, 0x2012, 0x2010, 0x0078,
+ 0x1dd8, 0x2013, 0x0000, 0x007c, 0x017e, 0x027e, 0xd7fc, 0x00c0,
+ 0x1ded, 0x2011, 0x53c0, 0x0078, 0x1def, 0x2011, 0x73c0, 0xa784,
+ 0x0f00, 0x800b, 0xa784, 0x001f, 0x0040, 0x1dfa, 0x8003, 0x8003,
+ 0x8003, 0x8003, 0xa105, 0xa268, 0x027f, 0x017f, 0x007c, 0x1078,
+ 0x1de4, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808, 0xa084, 0xf9ef,
+ 0xa80d, 0x690a, 0x0e7e, 0xd7fc, 0x00c0, 0x1e14, 0x2009, 0x4e53,
+ 0x2071, 0x4e40, 0x0078, 0x1e18, 0x2009, 0x4e93, 0x2071, 0x4e80,
+ 0x210c, 0x6804, 0xa005, 0x0040, 0x1e28, 0xa116, 0x00c0, 0x1e28,
+ 0x2060, 0x6000, 0x6806, 0x017e, 0x200b, 0x0000, 0x0078, 0x1e2b,
+ 0x2009, 0x0000, 0x017e, 0x6804, 0xa065, 0x0040, 0x1e40, 0x6000,
+ 0x6806, 0x1078, 0x1e5b, 0x1078, 0x201d, 0x6810, 0x7908, 0x8109,
+ 0x790a, 0x8001, 0x6812, 0x00c0, 0x1e2b, 0x7910, 0xc1a5, 0x7912,
+ 0x017f, 0x6902, 0x6906, 0x2d00, 0x2060, 0x1078, 0x2acc, 0x0e7f,
+ 0x007c, 0xa065, 0x0040, 0x1e5a, 0x2008, 0x609c, 0xa005, 0x0040,
+ 0x1e57, 0x2062, 0x609f, 0x0000, 0xa065, 0x0078, 0x1e4d, 0x7848,
+ 0x794a, 0x2062, 0x007c, 0x6007, 0x0103, 0x608f, 0x0000, 0x20a9,
+ 0x001c, 0xac80, 0x0005, 0x20a0, 0x2001, 0x0000, 0x40a4, 0x6828,
+ 0x601a, 0x682c, 0x6022, 0x007c, 0x0e7e, 0xd7fc, 0x00c0, 0x1e76,
+ 0x2071, 0x4e40, 0x2031, 0x4ec0, 0x0078, 0x1e7a, 0x2071, 0x4e80,
+ 0x2031, 0x50c0, 0x7050, 0xa08c, 0x0200, 0x00c0, 0x1e84, 0xa608,
+ 0x2d0a, 0x8000, 0x7052, 0xa006, 0x0e7f, 0x007c, 0x0f7e, 0xd7fc,
+ 0x00c0, 0x1e8e, 0x2079, 0x4e40, 0x0078, 0x1e90, 0x2079, 0x4e80,
+ 0x1078, 0x1de4, 0x2091, 0x8000, 0x6804, 0x780a, 0xa065, 0x0040,
+ 0x1ee4, 0x0078, 0x1ea2, 0x2c00, 0x780a, 0x2060, 0x6000, 0xa065,
+ 0x0040, 0x1ee4, 0x6010, 0xa306, 0x00c0, 0x1e9b, 0x600c, 0xa206,
+ 0x00c0, 0x1e9b, 0x2c28, 0x784c, 0xac06, 0x00c0, 0x1eb1, 0x0078,
+ 0x1ee1, 0x6804, 0xac06, 0x00c0, 0x1ebf, 0x6000, 0x2060, 0x6806,
+ 0xa005, 0x00c0, 0x1ebf, 0x6803, 0x0000, 0x0078, 0x1ec9, 0x6400,
+ 0x7808, 0x2060, 0x6402, 0xa486, 0x0000, 0x00c0, 0x1ec9, 0x2c00,
+ 0x6802, 0x2560, 0x0f7f, 0x1078, 0x1e5b, 0x0f7e, 0x601b, 0x0005,
+ 0x6023, 0x0020, 0x0f7f, 0x1078, 0x201d, 0x0f7e, 0x7908, 0x8109,
+ 0x790a, 0x6810, 0x8001, 0x6812, 0x00c0, 0x1ee1, 0x7810, 0xc0a5,
+ 0x7812, 0x2001, 0xffff, 0xa005, 0x0f7f, 0x007c, 0x077e, 0x2700,
+ 0x2039, 0x0000, 0xd0fc, 0x0040, 0x1eee, 0xc7fd, 0x2041, 0x0021,
+ 0x2049, 0x0004, 0x2051, 0x0008, 0x2091, 0x8000, 0x1078, 0x1dff,
+ 0x8738, 0xa784, 0x001f, 0x00c0, 0x1ef6, 0xa7bc, 0xff00, 0x873f,
+ 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1ef6, 0x2091, 0x8001,
+ 0x077f, 0x007c, 0x786c, 0x2009, 0x9674, 0x210c, 0xa10d, 0x0040,
+ 0x1f14, 0xa065, 0x0078, 0x2395, 0x2061, 0x0000, 0x6018, 0xd084,
+ 0x00c0, 0x1f34, 0x7810, 0xd08c, 0x0040, 0x1f25, 0xc08c, 0x7812,
+ 0xc7fc, 0x2069, 0x4e40, 0x0078, 0x1f2a, 0xc08d, 0x7812, 0x2069,
+ 0x4e80, 0xc7fd, 0x2091, 0x8000, 0x681c, 0x681f, 0x0000, 0x2091,
+ 0x8001, 0xa005, 0x00c0, 0x1f35, 0x007c, 0xa08c, 0xfff0, 0x0040,
+ 0x1f3b, 0x1078, 0x296b, 0x0079, 0x1f3d, 0x1f4d, 0x1f50, 0x1f56,
+ 0x1f5a, 0x1f4e, 0x1f5e, 0x1f4e, 0x1f4e, 0x1f4e, 0x1f64, 0x1f95,
+ 0x1f99, 0x1f9f, 0x1fb4, 0x1f4e, 0x1f4e, 0x007c, 0x1078, 0x296b,
+ 0x1078, 0x1ee6, 0x2001, 0x8001, 0x0078, 0x1fc0, 0x2001, 0x8003,
+ 0x0078, 0x1fc0, 0x2001, 0x8004, 0x0078, 0x1fc0, 0x1078, 0x1ee6,
+ 0x2001, 0x8006, 0x0078, 0x1fc0, 0x2091, 0x8000, 0x077e, 0xd7fc,
+ 0x00c0, 0x1f70, 0x2069, 0x4e40, 0x2039, 0x0009, 0x0078, 0x1f74,
+ 0x2069, 0x4e80, 0x2039, 0x0009, 0x6800, 0xa086, 0x0000, 0x0040,
+ 0x1f7e, 0x007f, 0x6f1e, 0x2091, 0x8001, 0x007c, 0x6874, 0x077f,
+ 0xa0bc, 0xff00, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010,
+ 0x1078, 0x1dff, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1f88, 0x2091,
+ 0x8001, 0x2001, 0x800a, 0x0078, 0x1fc0, 0x2001, 0x800c, 0x0078,
+ 0x1fc0, 0x1078, 0x1ee6, 0x2001, 0x800d, 0x0078, 0x1fc0, 0x7814,
+ 0xd0e4, 0x00c0, 0x1fb2, 0xd0ec, 0x0040, 0x1fac, 0xd7fc, 0x0040,
+ 0x1fac, 0x78e4, 0x0078, 0x1fad, 0x78e0, 0x70c6, 0x2001, 0x800e,
+ 0x0078, 0x1fc0, 0x0078, 0x1f4e, 0xd7fc, 0x0040, 0x1fba, 0x78ec,
+ 0x0078, 0x1fbb, 0x78e8, 0x70c6, 0x2001, 0x800f, 0x0078, 0x1fc0,
+ 0x70c2, 0xd7fc, 0x00c0, 0x1fc8, 0x70db, 0x0000, 0x0078, 0x1fca,
+ 0x70db, 0x0001, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x4080,
+ 0x007c, 0xac80, 0x0001, 0x81ff, 0x0040, 0x1ffc, 0x2099, 0x0030,
+ 0x20a0, 0x700c, 0xa084, 0x03ff, 0x0040, 0x1fde, 0x7018, 0x007e,
+ 0x701c, 0x007e, 0x7020, 0x007e, 0x7024, 0x007e, 0x7112, 0x81ac,
+ 0x721a, 0x731e, 0x7422, 0x7526, 0x7003, 0x0001, 0x7007, 0x0001,
+ 0x7008, 0x800b, 0x00c8, 0x1ff0, 0x7007, 0x0002, 0xa08c, 0x01e0,
+ 0x00c0, 0x1ffc, 0x53a5, 0xa006, 0x7003, 0x0000, 0x7007, 0x0004,
+ 0x007f, 0x7026, 0x007f, 0x7022, 0x007f, 0x701e, 0x007f, 0x701a,
+ 0x007c, 0x2011, 0x0020, 0x2009, 0x0010, 0x6b0a, 0x6c0e, 0x6803,
+ 0xfd00, 0x6807, 0x0018, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, 0xa290,
+ 0x0004, 0x8109, 0x00c0, 0x200d, 0x007c, 0x6004, 0x6086, 0x2c08,
+ 0x2063, 0x0000, 0x7868, 0xa005, 0x796a, 0x0040, 0x202a, 0x2c02,
+ 0x0078, 0x202b, 0x796e, 0x007c, 0x0c7e, 0x2061, 0x4e00, 0x6887,
+ 0x0103, 0x2d08, 0x206b, 0x0000, 0x6068, 0xa005, 0x616a, 0x0040,
+ 0x203c, 0x2d02, 0x0078, 0x203d, 0x616e, 0x0c7f, 0x007c, 0x2091,
+ 0x8000, 0x2c04, 0x786e, 0xa005, 0x00c0, 0x2047, 0x786a, 0x2091,
+ 0x8001, 0x609c, 0xa005, 0x0040, 0x2060, 0x0c7e, 0x2060, 0x2008,
+ 0x609c, 0xa005, 0x0040, 0x205c, 0x2062, 0x609f, 0x0000, 0xa065,
+ 0x609c, 0xa005, 0x00c0, 0x2054, 0x7848, 0x794a, 0x2062, 0x0c7f,
+ 0x7848, 0x2062, 0x609f, 0x0000, 0xac85, 0x0000, 0x00c0, 0x206a,
+ 0x1078, 0x296b, 0x784a, 0x007c, 0x20a9, 0x0010, 0xa006, 0x8004,
+ 0x8086, 0x818e, 0x00c8, 0x2075, 0xa200, 0x00f0, 0x2070, 0x8086,
+ 0x818e, 0x007c, 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x209b,
+ 0xa11a, 0x00c8, 0x209b, 0x8213, 0x818d, 0x0048, 0x208e, 0xa11a,
+ 0x00c8, 0x208f, 0x00f0, 0x2083, 0x0078, 0x2093, 0xa11a, 0x2308,
+ 0x8210, 0x00f0, 0x2083, 0x007e, 0x3200, 0xa084, 0xf7ff, 0x2080,
+ 0x007f, 0x157f, 0x007c, 0x007e, 0x3200, 0xa085, 0x0800, 0x0078,
+ 0x2097, 0x7d74, 0x70d0, 0xa506, 0x0040, 0x2187, 0x7810, 0x2050,
+ 0x7800, 0xd08c, 0x0040, 0x20c3, 0xdaec, 0x0040, 0x20c3, 0x0e7e,
+ 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0, 0x20c0,
+ 0x7008, 0x0e7f, 0xa086, 0x0008, 0x0040, 0x20c3, 0x0078, 0x2187,
+ 0x0e7f, 0x0078, 0x2187, 0x1078, 0x1dbd, 0x0040, 0x2187, 0xa046,
+ 0x7970, 0x2500, 0x8000, 0xa112, 0x2009, 0x0040, 0x00c8, 0x20d2,
+ 0x0078, 0x20d9, 0x72d0, 0xa206, 0x0040, 0x20d9, 0x8840, 0x2009,
+ 0x0080, 0x0c7e, 0x7112, 0x7007, 0x0001, 0x2099, 0x0030, 0x20a9,
+ 0x0020, 0xac80, 0x0001, 0x20a0, 0x2061, 0x0000, 0x88ff, 0x0040,
+ 0x20eb, 0x1078, 0x1dbd, 0x7008, 0xd0fc, 0x0040, 0x20eb, 0x7007,
+ 0x0002, 0x2091, 0x8001, 0xa08c, 0x01e0, 0x00c0, 0x2122, 0x53a5,
+ 0x8cff, 0x00c0, 0x2100, 0x88ff, 0x0040, 0x2171, 0x0078, 0x210a,
+ 0x2c00, 0x788e, 0x20a9, 0x0020, 0xac80, 0x0001, 0x20a0, 0x53a5,
+ 0x0078, 0x2171, 0xa046, 0x7218, 0x731c, 0xdac4, 0x0040, 0x2112,
+ 0x7420, 0x7524, 0xa292, 0x0040, 0xa39b, 0x0000, 0xa4a3, 0x0000,
+ 0xa5ab, 0x0000, 0x721a, 0x731e, 0xdac4, 0x0040, 0x2122, 0x7422,
+ 0x7526, 0xa006, 0x7007, 0x0004, 0x0040, 0x2171, 0x8cff, 0x0040,
+ 0x212b, 0x1078, 0x1dc6, 0x0c7f, 0x1078, 0x1dc6, 0xa046, 0x7888,
+ 0x8000, 0x788a, 0xa086, 0x0002, 0x0040, 0x2151, 0x7a7c, 0x7b78,
+ 0xdac4, 0x0040, 0x213d, 0x7c84, 0x7d80, 0x7974, 0x8107, 0x8004,
+ 0x8004, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000,
+ 0x721a, 0x731e, 0xdac4, 0x0040, 0x2187, 0x7422, 0x7526, 0x0078,
+ 0x2187, 0x6014, 0xd0fc, 0x00c0, 0x2159, 0x2069, 0x4e40, 0x0078,
+ 0x215b, 0x2069, 0x4e80, 0x2091, 0x8000, 0x681f, 0x0002, 0x88ff,
+ 0x0040, 0x2167, 0xa046, 0x788c, 0x2060, 0x0078, 0x2151, 0x788b,
+ 0x0000, 0x78ac, 0xa085, 0x0003, 0x78ae, 0x2091, 0x8001, 0x0078,
+ 0x2187, 0x0c7f, 0x788b, 0x0000, 0x1078, 0x2346, 0x6004, 0xa084,
+ 0x000f, 0x1078, 0x2188, 0x88ff, 0x0040, 0x2185, 0x788c, 0x2060,
+ 0x6004, 0xa084, 0x000f, 0x1078, 0x2188, 0x0078, 0x20a1, 0x007c,
+ 0x0079, 0x218a, 0x219a, 0x21b8, 0x21d6, 0x219a, 0x21e7, 0x21ab,
+ 0x219a, 0x219a, 0x219a, 0x21b6, 0x21d4, 0x219a, 0x219a, 0x219a,
+ 0x219a, 0x219a, 0x2039, 0x0400, 0x78bc, 0xa705, 0x78be, 0x6008,
+ 0xa705, 0x600a, 0x1078, 0x222a, 0x609c, 0x78ba, 0x609f, 0x0000,
+ 0x1078, 0x2330, 0x007c, 0x78bc, 0xd0c4, 0x0040, 0x21b1, 0x0078,
+ 0x219a, 0x601c, 0xc0bd, 0x601e, 0x0078, 0x21be, 0x1078, 0x2378,
+ 0x78bc, 0xd0c4, 0x0040, 0x21be, 0x0078, 0x219a, 0x78bf, 0x0000,
+ 0x6004, 0x8007, 0xa084, 0x00ff, 0x78b2, 0x8001, 0x0040, 0x21d1,
+ 0x1078, 0x222a, 0x0040, 0x21d1, 0x78bc, 0xc0c5, 0x78be, 0x0078,
+ 0x21d3, 0x0078, 0x2249, 0x007c, 0x1078, 0x2374, 0x78bc, 0xa08c,
+ 0x0e00, 0x00c0, 0x21de, 0xd0c4, 0x00c0, 0x21e0, 0x0078, 0x219a,
+ 0x1078, 0x222a, 0x00c0, 0x21e6, 0x0078, 0x2249, 0x007c, 0x78bc,
+ 0xd0c4, 0x0040, 0x21ed, 0x0078, 0x219a, 0x78bf, 0x0000, 0x6714,
+ 0x2011, 0x0001, 0x22a8, 0x6018, 0xa084, 0x00ff, 0xa005, 0x0040,
+ 0x220d, 0xa7bc, 0xff00, 0x20a9, 0x0020, 0xa08e, 0x0001, 0x0040,
+ 0x220d, 0xa7bc, 0x8000, 0x2011, 0x0002, 0x20a9, 0x0100, 0xa08e,
+ 0x0002, 0x0040, 0x220d, 0x0078, 0x2227, 0x1078, 0x1de4, 0x2d00,
+ 0x2091, 0x8000, 0x682b, 0x0000, 0x682f, 0x0000, 0x6808, 0xa084,
+ 0xffde, 0x680a, 0xade8, 0x0010, 0x2091, 0x8001, 0x00f0, 0x2210,
+ 0x8211, 0x0040, 0x2227, 0x20a9, 0x0100, 0x0078, 0x2210, 0x1078,
+ 0x1dc6, 0x007c, 0x609f, 0x0000, 0x78b4, 0xa06d, 0x2c00, 0x78b6,
+ 0x00c0, 0x2235, 0x78ba, 0x0078, 0x223d, 0x689e, 0x2d00, 0x6002,
+ 0x78b8, 0xad06, 0x00c0, 0x223d, 0x6002, 0x78b0, 0x8001, 0x78b2,
+ 0x00c0, 0x2248, 0x78bc, 0xc0c4, 0x78be, 0x78b8, 0x2060, 0xa006,
+ 0x007c, 0x0e7e, 0xa02e, 0x2530, 0x7dba, 0x7db6, 0x65ae, 0x65b2,
+ 0x601c, 0x60a2, 0x2048, 0xa984, 0xe1ff, 0x601e, 0xa984, 0x0060,
+ 0x0040, 0x225c, 0x1078, 0x4632, 0x6596, 0x65a6, 0x669a, 0x66aa,
+ 0x6714, 0x2071, 0x4e80, 0xd7fc, 0x00c0, 0x2268, 0x2071, 0x4e40,
+ 0xa784, 0x0f00, 0x800b, 0xa784, 0x001f, 0x0040, 0x2273, 0x8003,
+ 0x8003, 0x8003, 0x8003, 0xa105, 0x71c4, 0xa168, 0x2700, 0x8007,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0x71c8, 0xa100, 0x60c2,
+ 0x2091, 0x8000, 0x7814, 0xd0c4, 0x0040, 0x2298, 0xd0ec, 0x0040,
+ 0x2294, 0xd7fc, 0x00c0, 0x2291, 0xd0f4, 0x00c0, 0x229f, 0x0078,
+ 0x2298, 0xd0fc, 0x00c0, 0x229f, 0x7810, 0xd0f4, 0x00c0, 0x229f,
+ 0x6e08, 0xd684, 0x0040, 0x22c9, 0xd9fc, 0x00c0, 0x22c9, 0x2091,
+ 0x8001, 0x1078, 0x1e5b, 0x2091, 0x8000, 0x1078, 0x201d, 0x2091,
+ 0x8001, 0x7814, 0xd0e4, 0x00c0, 0x232e, 0x7814, 0xd0c4, 0x0040,
+ 0x232e, 0xd0ec, 0x0040, 0x22c1, 0xd7fc, 0x00c0, 0x22bc, 0xd0f4,
+ 0x00c0, 0x22c5, 0x0078, 0x232e, 0xd0fc, 0x00c0, 0x22c5, 0x0078,
+ 0x232e, 0x7810, 0xd0f4, 0x0040, 0x232e, 0x601b, 0x0021, 0x0078,
+ 0x232e, 0x6024, 0xa096, 0x0001, 0x00c0, 0x22d0, 0x8000, 0x6026,
+ 0x6a10, 0x6814, 0xa202, 0x0048, 0x22e3, 0x0040, 0x22e3, 0x2091,
+ 0x8001, 0x2039, 0x0200, 0x609c, 0x78ba, 0x609f, 0x0000, 0x1078,
+ 0x2330, 0x0078, 0x232e, 0x2c08, 0xd9fc, 0x0040, 0x230b, 0x6800,
+ 0xa065, 0x0040, 0x230b, 0x6a04, 0x7000, 0xa084, 0x0002, 0x0040,
+ 0x2301, 0x704c, 0xa206, 0x00c0, 0x2301, 0x6b04, 0x2160, 0x2304,
+ 0x6002, 0xa005, 0x00c0, 0x22fd, 0x6902, 0x2260, 0x6102, 0x0078,
+ 0x2317, 0x2d00, 0x2060, 0x1078, 0x2acc, 0x6e08, 0x2160, 0x6202,
+ 0x6906, 0x0078, 0x2317, 0x6800, 0x6902, 0xa065, 0x0040, 0x2313,
+ 0x6102, 0x0078, 0x2314, 0x6906, 0x2160, 0x6003, 0x0000, 0x2160,
+ 0xd9fc, 0x0040, 0x231e, 0xa6b4, 0xfffc, 0x6e0a, 0x6810, 0x7d08,
+ 0x8528, 0x7d0a, 0x8000, 0x6812, 0x2091, 0x8001, 0xd6b4, 0x0040,
+ 0x232e, 0xa6b6, 0x0040, 0x6e0a, 0x1078, 0x1e6c, 0x0e7f, 0x007c,
+ 0x6008, 0xa705, 0x600a, 0x2091, 0x8000, 0x1078, 0x201d, 0x2091,
+ 0x8001, 0x78b8, 0xa065, 0x0040, 0x2343, 0x609c, 0x78ba, 0x609f,
+ 0x0000, 0x0078, 0x2330, 0x78b6, 0x78ba, 0x007c, 0x7970, 0x7874,
+ 0x2818, 0xd384, 0x0040, 0x2350, 0x8000, 0xa112, 0x0048, 0x2355,
+ 0x8000, 0xa112, 0x00c8, 0x2365, 0xc384, 0x7a7c, 0x721a, 0x7a78,
+ 0x721e, 0xdac4, 0x0040, 0x2360, 0x7a84, 0x7222, 0x7a80, 0x7226,
+ 0xa006, 0xd384, 0x0040, 0x2365, 0x8000, 0x7876, 0x70d2, 0x781c,
+ 0xa005, 0x0040, 0x2373, 0x8001, 0x781e, 0x00c0, 0x2373, 0x0068,
+ 0x2373, 0x2091, 0x4080, 0x007c, 0x2039, 0x238c, 0x0078, 0x237a,
+ 0x2039, 0x2392, 0x2704, 0xa005, 0x0040, 0x238b, 0xac00, 0x2068,
+ 0x6908, 0x6810, 0x6912, 0x680a, 0x690c, 0x6814, 0x6916, 0x680e,
+ 0x8738, 0x0078, 0x237a, 0x007c, 0x0003, 0x0009, 0x000f, 0x0015,
+ 0x001b, 0x0000, 0x0015, 0x001b, 0x0000, 0x2041, 0x0000, 0x780c,
+ 0x0079, 0x239a, 0x256c, 0x253f, 0x239e, 0x2417, 0x2039, 0x9674,
+ 0x2734, 0x7d10, 0x0078, 0x23be, 0x6084, 0xa086, 0x0103, 0x00c0,
+ 0x2400, 0x6114, 0x6018, 0xa105, 0x0040, 0x23b3, 0x86ff, 0x00c0,
+ 0x23cf, 0x0078, 0x2400, 0x8603, 0xa080, 0x9655, 0x620c, 0x2202,
+ 0x8000, 0x6210, 0x2202, 0x1078, 0x203f, 0x8630, 0xa68e, 0x000f,
+ 0x0040, 0x248b, 0x786c, 0xa065, 0x00c0, 0x23a4, 0x7808, 0xa602,
+ 0x00c8, 0x23cf, 0xd5ac, 0x00c0, 0x23cf, 0x263a, 0x007c, 0xa682,
+ 0x0003, 0x00c8, 0x248b, 0x2091, 0x8000, 0x2069, 0x0000, 0x6818,
+ 0xd084, 0x00c0, 0x23fb, 0x2011, 0x9655, 0x2204, 0x70c6, 0x8210,
+ 0x2204, 0x70ca, 0xd684, 0x00c0, 0x23eb, 0x8210, 0x2204, 0x70da,
+ 0x8210, 0x2204, 0x70de, 0xa685, 0x8020, 0x70c2, 0x681b, 0x0001,
+ 0x2091, 0x4080, 0x7810, 0xa084, 0xffcf, 0x7812, 0x2091, 0x8001,
+ 0x203b, 0x0000, 0x007c, 0x7810, 0xc0ad, 0x7812, 0x0078, 0x248b,
+ 0x263a, 0x1078, 0x2576, 0x00c0, 0x2599, 0x786c, 0xa065, 0x00c0,
+ 0x23a4, 0x2091, 0x8000, 0x7810, 0xa084, 0xffcf, 0x86ff, 0x0040,
+ 0x2412, 0xc0ad, 0x7812, 0x2091, 0x8001, 0x0078, 0x2599, 0x2039,
+ 0x9674, 0x2734, 0x7d10, 0x0078, 0x2433, 0x6084, 0xa086, 0x0103,
+ 0x00c0, 0x2474, 0x6114, 0x6018, 0xa105, 0x0040, 0x242c, 0x86ff,
+ 0x00c0, 0x2444, 0x0078, 0x2474, 0xa680, 0x9655, 0x620c, 0x2202,
+ 0x1078, 0x203f, 0x8630, 0xa68e, 0x001e, 0x0040, 0x248b, 0x786c,
+ 0xa065, 0x00c0, 0x241d, 0x7808, 0xa602, 0x00c8, 0x2444, 0xd5ac,
+ 0x00c0, 0x2444, 0x263a, 0x007c, 0xa682, 0x0006, 0x00c8, 0x248b,
+ 0x2091, 0x8000, 0x2069, 0x0000, 0x6818, 0xd084, 0x00c0, 0x246f,
+ 0x2011, 0x9655, 0x2009, 0x964e, 0x26a8, 0x211c, 0x2204, 0x201a,
+ 0x8108, 0x8210, 0x00f0, 0x2455, 0xa685, 0x8030, 0x70c2, 0x681b,
+ 0x0001, 0x2091, 0x4080, 0x7810, 0xa084, 0xffcf, 0x7812, 0x2091,
+ 0x8001, 0xa006, 0x2009, 0x9675, 0x200a, 0x203a, 0x007c, 0x7810,
+ 0xc0ad, 0x7812, 0x0078, 0x248b, 0x263a, 0x1078, 0x2576, 0x00c0,
+ 0x2599, 0x786c, 0xa065, 0x00c0, 0x241d, 0x2091, 0x8000, 0x7810,
+ 0xa084, 0xffcf, 0x86ff, 0x0040, 0x2486, 0xc0ad, 0x7812, 0x2091,
+ 0x8001, 0x0078, 0x2599, 0x2091, 0x8000, 0x7007, 0x0004, 0x7994,
+ 0x70d4, 0xa102, 0x0048, 0x249c, 0x0040, 0x24a6, 0x7b90, 0xa302,
+ 0x00c0, 0x24a6, 0x0078, 0x249f, 0x8002, 0x00c0, 0x24a6, 0x263a,
+ 0x7810, 0xc0ad, 0x7812, 0x2091, 0x8001, 0x007c, 0xa184, 0xff00,
+ 0x0040, 0x24b3, 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007,
+ 0xa100, 0x0078, 0x24b6, 0x8107, 0x8004, 0x8004, 0x7a9c, 0xa210,
+ 0x721a, 0x7a98, 0xa006, 0xa211, 0x721e, 0xd4c4, 0x0040, 0x24c6,
+ 0x7aa4, 0xa211, 0x7222, 0x7aa0, 0xa211, 0x7226, 0x20a1, 0x0030,
+ 0x7003, 0x0000, 0x2009, 0x9654, 0x260a, 0x8109, 0x2198, 0x2104,
+ 0xd084, 0x0040, 0x24d4, 0x8633, 0xa6b0, 0x0002, 0x26a8, 0x53a6,
+ 0x8603, 0x7012, 0x7007, 0x0001, 0x7990, 0x7894, 0x8000, 0xa10a,
+ 0x00c8, 0x24e3, 0xa006, 0x2028, 0x7974, 0xa184, 0xff00, 0x0040,
+ 0x24f2, 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007, 0xa100,
+ 0x0078, 0x24f5, 0x8107, 0x8004, 0x8004, 0x797c, 0xa108, 0x7a78,
+ 0xa006, 0xa211, 0xd4c4, 0x0040, 0x2501, 0x7b84, 0xa319, 0x7c80,
+ 0xa421, 0x7008, 0xd0fc, 0x0040, 0x2501, 0xa084, 0x01e0, 0x0040,
+ 0x2526, 0x7d10, 0x2031, 0x9654, 0x2634, 0x78a8, 0x8000, 0x78aa,
+ 0xd08c, 0x00c0, 0x251b, 0x7007, 0x0006, 0x7004, 0xd094, 0x00c0,
+ 0x2515, 0x0078, 0x248d, 0x2069, 0x4e47, 0x206b, 0x0003, 0x78ac,
+ 0xa085, 0x0300, 0x78ae, 0xa006, 0x0078, 0x252f, 0x2030, 0x75d6,
+ 0x2091, 0x4080, 0x7d96, 0x7d10, 0xa5ac, 0xffcf, 0x7d12, 0x2091,
+ 0x8001, 0x78aa, 0x7007, 0x0006, 0x263a, 0x7003, 0x0001, 0x711a,
+ 0x721e, 0xd5c4, 0x0040, 0x253e, 0x7322, 0x7426, 0x007c, 0x6084,
+ 0xa086, 0x0103, 0x00c0, 0x2562, 0x6114, 0x6018, 0xa105, 0x00c0,
+ 0x2562, 0x2069, 0x0000, 0x6818, 0xd084, 0x00c0, 0x2562, 0x600c,
+ 0x70c6, 0x6010, 0x70ca, 0x70c3, 0x8020, 0x681b, 0x0001, 0x2091,
+ 0x4080, 0x1078, 0x203f, 0x0068, 0x2561, 0x786c, 0xa065, 0x00c0,
+ 0x253f, 0x007c, 0x1078, 0x2576, 0x00c0, 0x2599, 0x786c, 0xa065,
+ 0x00c0, 0x253f, 0x0078, 0x2599, 0x1078, 0x2576, 0x00c0, 0x2599,
+ 0x786c, 0xa065, 0x00c0, 0x256c, 0x0078, 0x2599, 0x6084, 0xa086,
+ 0x0103, 0x00c0, 0x258a, 0x6018, 0xc0fc, 0x601a, 0xa086, 0x0004,
+ 0x00c0, 0x258a, 0x7804, 0xd0a4, 0x0040, 0x258a, 0x1078, 0x203f,
+ 0xa006, 0x007c, 0x1078, 0x259f, 0x00c0, 0x2591, 0xa085, 0x0001,
+ 0x007c, 0x1078, 0x25ae, 0x00c0, 0x2597, 0x2041, 0x0001, 0x7d10,
+ 0x007c, 0x88ff, 0x0040, 0x259e, 0x2091, 0x4080, 0x007c, 0x7b90,
+ 0x7994, 0x70d4, 0xa102, 0x00c0, 0x25a8, 0xa385, 0x0000, 0x007c,
+ 0x0048, 0x25ac, 0xa302, 0x007c, 0x8002, 0x007c, 0x7810, 0xd0ec,
+ 0x0040, 0x25c6, 0x0e7e, 0x2091, 0x8000, 0x2071, 0x0020, 0x7004,
+ 0xa005, 0x00c0, 0x25c3, 0x7008, 0x0e7f, 0xa086, 0x0008, 0x0040,
+ 0x25c6, 0x0078, 0x2617, 0x0e7f, 0x0078, 0x2617, 0xa184, 0xff00,
+ 0x0040, 0x25d3, 0x810f, 0x810c, 0x810c, 0x8004, 0x8004, 0x8007,
+ 0xa100, 0x0078, 0x25d6, 0x8107, 0x8004, 0x8004, 0x7a9c, 0x7b98,
+ 0x7ca4, 0x7da0, 0xa210, 0xa006, 0xa319, 0xa421, 0xa529, 0x2009,
+ 0x0018, 0x6028, 0xa005, 0x0040, 0x25e7, 0x2009, 0x0040, 0x1078,
+ 0x1d74, 0x0040, 0x2609, 0x78a8, 0x8000, 0x78aa, 0xd08c, 0x00c0,
+ 0x2617, 0x6014, 0xd0fc, 0x00c0, 0x25f9, 0x2069, 0x4e40, 0x0078,
+ 0x25fb, 0x2069, 0x4e80, 0x2091, 0x8000, 0x681f, 0x0003, 0x78ab,
+ 0x0000, 0x78ac, 0xa085, 0x0300, 0x78ae, 0x2091, 0x8001, 0x0078,
+ 0x2617, 0x78ab, 0x0000, 0x1078, 0x203f, 0x7990, 0x7894, 0x8000,
+ 0xa10a, 0x00c8, 0x2614, 0xa006, 0x7896, 0x70d6, 0xa006, 0x2071,
+ 0x0010, 0x2091, 0x8001, 0x007c, 0xd7fc, 0x00c0, 0x2623, 0x2009,
+ 0x4e59, 0x0078, 0x2625, 0x2009, 0x4e99, 0x2091, 0x8000, 0x200a,
+ 0x0f7e, 0xd7fc, 0x00c0, 0x263c, 0x2009, 0x4e40, 0x2001, 0x4e04,
+ 0x2004, 0xd0ec, 0x0040, 0x2638, 0x2079, 0x0100, 0x0078, 0x2640,
+ 0x2079, 0x0200, 0x0078, 0x2640, 0x2009, 0x4e80, 0x2079, 0x0100,
+ 0x2104, 0xa086, 0x0000, 0x00c0, 0x2659, 0xd7fc, 0x00c0, 0x264c,
+ 0x2009, 0x4e45, 0x0078, 0x264e, 0x2009, 0x4e85, 0x2104, 0xa005,
+ 0x00c0, 0x2659, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2659, 0x781b,
+ 0x0045, 0x0f7f, 0x007c, 0x2009, 0x0002, 0x2069, 0x4e00, 0x6810,
+ 0xd0ec, 0x00c0, 0x26c8, 0x2071, 0x4e80, 0x2079, 0x0100, 0x2021,
+ 0x50bf, 0x784b, 0x000f, 0x2019, 0x4457, 0xd184, 0x0040, 0x267c,
+ 0x6810, 0xd0ec, 0x0040, 0x2678, 0x20a1, 0x012b, 0x0078, 0x267e,
+ 0x20a1, 0x022b, 0x0078, 0x267e, 0x20a1, 0x012b, 0x2304, 0xa005,
+ 0x0040, 0x268b, 0x789a, 0x8318, 0x23ac, 0x8318, 0x2398, 0x53a6,
+ 0x3318, 0x0078, 0x267e, 0x789b, 0x0020, 0x20a9, 0x0010, 0x6814,
+ 0xd0e4, 0x0040, 0x269b, 0x78af, 0x0000, 0x78af, 0x9020, 0x00f0,
+ 0x2693, 0x0078, 0x26a1, 0x78af, 0x0000, 0x78af, 0x8020, 0x00f0,
+ 0x269b, 0x7003, 0x0000, 0x017e, 0xd18c, 0x2009, 0x0000, 0x0040,
+ 0x26aa, 0xc1bd, 0x1078, 0x289b, 0x017f, 0x7020, 0xa084, 0x000f,
+ 0x007e, 0x6814, 0xd0e4, 0x007f, 0x00c0, 0x26ba, 0xa085, 0x6340,
+ 0x0078, 0x26bc, 0xa085, 0x62c0, 0x7806, 0x780f, 0x9200, 0x7843,
+ 0x00d8, 0x7853, 0x0080, 0x780b, 0x0008, 0x7456, 0x7053, 0x0000,
+ 0x8109, 0x0040, 0x26db, 0x2071, 0x4e40, 0x6810, 0xd0ec, 0x0040,
+ 0x26d5, 0x2079, 0x0100, 0x0078, 0x26d7, 0x2079, 0x0200, 0x2021,
+ 0x4ebf, 0x0078, 0x2669, 0x007c, 0x017e, 0xd1bc, 0x00c0, 0x26f0,
+ 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040, 0x26ec,
+ 0x2011, 0x0101, 0x0078, 0x26f2, 0x2011, 0x0201, 0x0078, 0x26f2,
+ 0x2011, 0x0101, 0xa18c, 0x000f, 0x2204, 0xa084, 0xfff0, 0xa105,
+ 0x2012, 0x017f, 0x1078, 0x289b, 0x007c, 0xd3fc, 0x00c0, 0x2710,
+ 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040, 0x270c,
+ 0x2011, 0x0101, 0x0078, 0x2712, 0x2011, 0x0201, 0x0078, 0x2712,
+ 0x2011, 0x0101, 0x20a9, 0x0009, 0x810b, 0x00f0, 0x2714, 0xa18c,
+ 0x0e00, 0x2204, 0xa084, 0xf1ff, 0xa105, 0x2012, 0x007c, 0x2019,
+ 0x0002, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x0040, 0x272c, 0x8319,
+ 0x2009, 0x0101, 0x0078, 0x272e, 0x2009, 0x0101, 0x20a9, 0x0005,
+ 0x8213, 0x00f0, 0x2730, 0xa294, 0x00e0, 0x2104, 0xa084, 0xff1f,
+ 0xa205, 0x200a, 0x8319, 0x0040, 0x2741, 0x2009, 0x0201, 0x0078,
+ 0x272e, 0x007c, 0xd3fc, 0x00c0, 0x2755, 0x007e, 0x2001, 0x4e04,
+ 0x2004, 0xd0ec, 0x007f, 0x0040, 0x2751, 0x2011, 0x0101, 0x0078,
+ 0x2757, 0x2011, 0x0201, 0x0078, 0x2757, 0x2011, 0x0101, 0x20a9,
+ 0x000c, 0x810b, 0x00f0, 0x2759, 0xa18c, 0xf000, 0x2204, 0xa084,
+ 0x0fff, 0xa105, 0x2012, 0x007c, 0xd3fc, 0x00c0, 0x2777, 0x007e,
+ 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040, 0x2773, 0x2011,
+ 0x0102, 0x0078, 0x2779, 0x2011, 0x0202, 0x0078, 0x2779, 0x2011,
+ 0x0102, 0x2204, 0xa084, 0xffcf, 0xa105, 0x2012, 0x007c, 0x0c7e,
+ 0xd1bc, 0x00c0, 0x2793, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec,
+ 0x007f, 0x0040, 0x278f, 0x2061, 0x0100, 0x0078, 0x2795, 0x2061,
+ 0x0200, 0x0078, 0x2795, 0x2061, 0x0100, 0xc1bc, 0x8103, 0x8003,
+ 0xa080, 0x0020, 0x609a, 0x62ac, 0x63ac, 0x0c7f, 0x007c, 0x0c7e,
+ 0xd1bc, 0x00c0, 0x27b3, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec,
+ 0x007f, 0x0040, 0x27af, 0x2061, 0x0100, 0x0078, 0x27b5, 0x2061,
+ 0x0200, 0x0078, 0x27b5, 0x2061, 0x0100, 0xc1bc, 0x8103, 0x8003,
+ 0xa080, 0x0022, 0x609a, 0x60a4, 0xa084, 0xffdf, 0x60ae, 0x0c7f,
+ 0x007c, 0x0c7e, 0xd1bc, 0x00c0, 0x27d5, 0x007e, 0x2001, 0x4e04,
+ 0x2004, 0xd0ec, 0x007f, 0x0040, 0x27d1, 0x2061, 0x0100, 0x0078,
+ 0x27d7, 0x2061, 0x0200, 0x0078, 0x27d7, 0x2061, 0x0100, 0xc1bc,
+ 0x8103, 0x8003, 0xa080, 0x0022, 0x609a, 0x60a4, 0xa085, 0x0020,
+ 0x60ae, 0x0c7f, 0x007c, 0x0c7e, 0xd1bc, 0x00c0, 0x27f7, 0x007e,
+ 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040, 0x27f3, 0x2061,
+ 0x0100, 0x0078, 0x27f9, 0x2061, 0x0200, 0x0078, 0x27f9, 0x2061,
+ 0x0100, 0xc1bc, 0x8103, 0x8003, 0xa080, 0x0020, 0x609a, 0x60a4,
+ 0xa28c, 0x0020, 0x0040, 0x2807, 0xc2ac, 0xa39d, 0x4000, 0xc3fc,
+ 0xd3b4, 0x00c0, 0x280c, 0xc3fd, 0x62ae, 0x2010, 0x60a4, 0x63ae,
+ 0x2018, 0x0c7f, 0x007c, 0x2091, 0x8000, 0x0c7e, 0x0e7e, 0x6818,
+ 0xa005, 0x0040, 0x2879, 0xd1fc, 0x0040, 0x2822, 0x2061, 0x95d0,
+ 0x0078, 0x2824, 0x2061, 0x94c0, 0x1078, 0x2881, 0x0040, 0x285b,
+ 0x20a9, 0x0101, 0xd1fc, 0x0040, 0x2831, 0x2061, 0x94d0, 0x0078,
+ 0x2833, 0x2061, 0x93c0, 0x0c7e, 0x1078, 0x2881, 0x0040, 0x283e,
+ 0x0c7f, 0x8c60, 0x00f0, 0x2833, 0x0078, 0x2879, 0x007f, 0xd1fc,
+ 0x0040, 0x2848, 0xa082, 0x94d0, 0x2071, 0x4e80, 0x0078, 0x284c,
+ 0xa082, 0x93c0, 0x2071, 0x4e40, 0x707a, 0x7176, 0x2138, 0x2001,
+ 0x0004, 0x7066, 0x7083, 0x000f, 0x71d4, 0xc1dc, 0x71d6, 0x1078,
+ 0x261c, 0x0078, 0x2875, 0xd1fc, 0x00c0, 0x2862, 0x2071, 0x4e40,
+ 0x0078, 0x2864, 0x2071, 0x4e80, 0x6020, 0xc0dd, 0x6022, 0x7176,
+ 0x2138, 0x2c00, 0x707e, 0x2001, 0x0006, 0x7066, 0x7083, 0x000f,
+ 0x71d4, 0xc1dc, 0x71d6, 0x1078, 0x261c, 0x2001, 0x0000, 0x0078,
+ 0x287b, 0x2001, 0x0001, 0x2091, 0x8001, 0xa005, 0x0e7f, 0x0c7f,
+ 0x007c, 0x2c04, 0xa005, 0x0040, 0x2898, 0x2060, 0x6010, 0xa306,
+ 0x00c0, 0x2895, 0x600c, 0xa206, 0x00c0, 0x2895, 0x6014, 0xa106,
+ 0x00c0, 0x2895, 0xa006, 0x0078, 0x289a, 0x6000, 0x0078, 0x2882,
+ 0xa085, 0x0001, 0x007c, 0x0f7e, 0x0e7e, 0x017e, 0xd1bc, 0x00c0,
+ 0x28b3, 0x2079, 0x4e40, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec,
+ 0x007f, 0x0040, 0x28af, 0x2071, 0x0100, 0x0078, 0x28b7, 0x2071,
+ 0x0200, 0x0078, 0x28b7, 0x2079, 0x4e80, 0x2071, 0x0100, 0x7920,
+ 0xa18c, 0x000f, 0x70ec, 0xd0c4, 0x00c0, 0x28c1, 0x017f, 0x0078,
+ 0x28dc, 0x810b, 0x810b, 0x810b, 0x810b, 0x007f, 0xd0bc, 0x00c0,
+ 0x28d9, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040,
+ 0x28d5, 0xa18d, 0x0f00, 0x0078, 0x28db, 0xa18d, 0x0f00, 0x0078,
+ 0x28db, 0xa18d, 0x0800, 0x2104, 0x0e7f, 0x0f7f, 0x007c, 0x0e7e,
+ 0x2001, 0x4e01, 0x2004, 0xd0ac, 0x00c0, 0x295c, 0x68e4, 0xd0ac,
+ 0x0040, 0x295c, 0xa084, 0x0006, 0x00c0, 0x295c, 0x6014, 0xd0fc,
+ 0x00c0, 0x28f6, 0x2071, 0x52c0, 0x0078, 0x28f8, 0x2071, 0x5340,
+ 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xae70, 0x7004,
+ 0xa084, 0x000a, 0x00c0, 0x295c, 0x7108, 0xa194, 0xff00, 0x0040,
+ 0x295c, 0xa18c, 0x00ff, 0x2001, 0x000a, 0xa106, 0x0040, 0x292b,
+ 0x2001, 0x000c, 0xa106, 0x0040, 0x292f, 0x2001, 0x0012, 0xa106,
+ 0x0040, 0x2933, 0x2001, 0x0014, 0xa106, 0x0040, 0x2937, 0x2001,
+ 0x0019, 0xa106, 0x0040, 0x293b, 0x2001, 0x0032, 0xa106, 0x0040,
+ 0x293f, 0x0078, 0x2943, 0x2009, 0x000c, 0x0078, 0x2945, 0x2009,
+ 0x0012, 0x0078, 0x2945, 0x2009, 0x0014, 0x0078, 0x2945, 0x2009,
+ 0x0019, 0x0078, 0x2945, 0x2009, 0x0020, 0x0078, 0x2945, 0x2009,
+ 0x003f, 0x0078, 0x2945, 0x2011, 0x0000, 0x2100, 0xa205, 0x700a,
+ 0x2071, 0x4e00, 0x7004, 0xd0bc, 0x0040, 0x295c, 0x6014, 0xd0fc,
+ 0x00c0, 0x2957, 0x70ea, 0x2071, 0x4e40, 0x0078, 0x295a, 0x70ee,
+ 0x2071, 0x4e80, 0x701f, 0x000d, 0x0e7f, 0x007c, 0x2001, 0x4e05,
+ 0x2004, 0xd0e4, 0x00c0, 0x296a, 0x7804, 0xa084, 0xff1f, 0xa085,
+ 0x6340, 0x7806, 0x007c, 0x0068, 0x296b, 0x2091, 0x8000, 0x2071,
+ 0x0000, 0x007e, 0x7018, 0xd084, 0x00c0, 0x2972, 0x007f, 0x2071,
+ 0x0010, 0x70ca, 0x007f, 0x70c6, 0x70c3, 0x8002, 0x70db, 0x080f,
+ 0x70df, 0x0000, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080,
+ 0x0078, 0x2988, 0x7f3c, 0x7e58, 0x7c30, 0x7d38, 0x78a0, 0x708e,
+ 0x7592, 0x7496, 0x769a, 0x779e, 0xa594, 0x003f, 0xd4f4, 0x0040,
+ 0x299f, 0xa784, 0x007d, 0x00c0, 0x43cd, 0x1078, 0x296b, 0xa49c,
+ 0x000f, 0xa382, 0x0004, 0x0050, 0x29aa, 0xa3a6, 0x0007, 0x00c0,
+ 0x296b, 0x2418, 0x8507, 0xa084, 0x000f, 0x0079, 0x29af, 0x3028,
+ 0x3119, 0x3144, 0x33b6, 0x379f, 0x3819, 0x38ce, 0x395f, 0x3a4d,
+ 0x3b3c, 0x29c2, 0x29bf, 0x2df9, 0x2f1c, 0x3770, 0x29bf, 0x1078,
+ 0x296b, 0x007c, 0xa006, 0x0078, 0x29cc, 0x7808, 0xc08d, 0x780a,
+ 0xa006, 0x7002, 0x704e, 0x7046, 0x70d2, 0x7060, 0xa005, 0x00c0,
+ 0x2b32, 0x7064, 0xa084, 0x0007, 0x0079, 0x29d6, 0x29de, 0x2a51,
+ 0x2a5a, 0x2a65, 0x2a70, 0x2b18, 0x2a7b, 0x2a51, 0x7830, 0xd0bc,
+ 0x00c0, 0x29c1, 0x71d4, 0xd1bc, 0x00c0, 0x29c1, 0xd1b4, 0x00c0,
+ 0x2a2e, 0x70a4, 0xa086, 0x0001, 0x0040, 0x29c1, 0x70b4, 0xa06d,
+ 0x6800, 0xa065, 0xa055, 0x789b, 0x0010, 0x6b0c, 0x7baa, 0x6808,
+ 0xa045, 0x6d10, 0x6804, 0xa06d, 0xa05d, 0xa886, 0x0001, 0x0040,
+ 0x2a04, 0x69bc, 0x7daa, 0x79aa, 0x68c0, 0xa04d, 0x6e1c, 0x2001,
+ 0x0010, 0x0078, 0x2c8c, 0x7060, 0xa005, 0x00c0, 0x29c1, 0x0c7e,
+ 0x0d7e, 0x70b4, 0xa06d, 0x6800, 0xa065, 0xa055, 0x789b, 0x0010,
+ 0x6b0c, 0x7baa, 0x6808, 0xa045, 0x6d10, 0x6804, 0xa06d, 0xa05d,
+ 0xa886, 0x0001, 0x0040, 0x2a27, 0x69bc, 0x7daa, 0x79aa, 0x68c0,
+ 0xa04d, 0x6e1c, 0x2001, 0x0020, 0x0078, 0x2c8c, 0x1078, 0x4360,
+ 0x00c0, 0x29c1, 0x781b, 0x005b, 0x70bc, 0xa06d, 0x68b4, 0x785a,
+ 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2, 0x78da, 0x7808, 0xc08d,
+ 0x780a, 0x68bc, 0x7042, 0xc1b4, 0x71d6, 0x70b8, 0xa065, 0x68c0,
+ 0x705a, 0x7003, 0x0002, 0x2d00, 0x704e, 0xad80, 0x0009, 0x7046,
+ 0x007c, 0x1078, 0x4360, 0x00c0, 0x2a59, 0x781b, 0x0047, 0x7003,
+ 0x0004, 0x007c, 0x1078, 0x4360, 0x00c0, 0x2a64, 0x2011, 0x000c,
+ 0x1078, 0x2a8b, 0x7003, 0x0004, 0x007c, 0x1078, 0x4360, 0x00c0,
+ 0x2a6f, 0x2011, 0x0006, 0x1078, 0x2a8b, 0x7003, 0x0004, 0x007c,
+ 0x1078, 0x4360, 0x00c0, 0x2a7a, 0x2011, 0x000d, 0x1078, 0x2a8b,
+ 0x7003, 0x0004, 0x007c, 0x1078, 0x4360, 0x00c0, 0x2a8a, 0x2011,
+ 0x0006, 0x1078, 0x2a8b, 0x707c, 0x707f, 0x0000, 0x2068, 0x704e,
+ 0x7003, 0x0001, 0x007c, 0x7174, 0xc1fc, 0x8107, 0x7882, 0x789b,
+ 0x0010, 0xa286, 0x000c, 0x00c0, 0x2a9a, 0x7aaa, 0x2001, 0x0001,
+ 0x0078, 0x2aaf, 0xa18c, 0x001f, 0xa18d, 0x00c0, 0x79aa, 0xa286,
+ 0x000d, 0x0040, 0x2aa8, 0x7aaa, 0x2001, 0x0002, 0x0078, 0x2aaf,
+ 0x78ab, 0x0020, 0x7178, 0x79aa, 0x7aaa, 0x2001, 0x0004, 0x789b,
+ 0x0060, 0x78aa, 0x785b, 0x0004, 0x781b, 0x0116, 0x1078, 0x4383,
+ 0x7083, 0x000f, 0x70d4, 0xd0b4, 0x0040, 0x2acb, 0xc0b4, 0x70d6,
+ 0x0c7e, 0x70b8, 0xa065, 0x6008, 0xa084, 0xfbef, 0x600a, 0x6018,
+ 0x8001, 0x601a, 0x0c7f, 0x007c, 0x7014, 0xa005, 0x00c0, 0x2ada,
+ 0x70d4, 0xd0b4, 0x0040, 0x2adb, 0x70b8, 0xac06, 0x00c0, 0x2adb,
+ 0x1078, 0x2aba, 0x007c, 0x017e, 0x71a4, 0xa186, 0x0001, 0x0040,
+ 0x2b0d, 0x0d7e, 0x027e, 0x2100, 0x2011, 0x0001, 0xa212, 0x70b4,
+ 0x2068, 0x6800, 0xac06, 0x0040, 0x2af4, 0x8211, 0x0040, 0x2b0b,
+ 0x1078, 0x2b0f, 0x0078, 0x2ae9, 0x0c7e, 0x2100, 0x2011, 0x0001,
+ 0xa212, 0x70b4, 0x2068, 0x6800, 0x2060, 0x6008, 0xa084, 0xfbef,
+ 0x600a, 0x8211, 0x0040, 0x2b08, 0x1078, 0x2b0f, 0x0078, 0x2afb,
+ 0x70a7, 0x0001, 0x0c7f, 0x027f, 0x0d7f, 0x017f, 0x007c, 0xade8,
+ 0x0005, 0x70ac, 0xad06, 0x00c0, 0x2b17, 0x70a8, 0x2068, 0x007c,
+ 0x1078, 0x4360, 0x00c0, 0x29c1, 0x707c, 0x2068, 0x7774, 0x1078,
+ 0x41fe, 0x2c50, 0x1078, 0x4442, 0x789b, 0x0010, 0x6814, 0xa084,
+ 0x001f, 0xc0bd, 0x78aa, 0x6e1c, 0x2041, 0x0001, 0x2001, 0x0004,
+ 0x0078, 0x2c92, 0x1078, 0x4360, 0x00c0, 0x29c1, 0x789b, 0x0010,
+ 0x7060, 0x2068, 0x6f14, 0x70d4, 0xd0b4, 0x0040, 0x2b4c, 0xc0b4,
+ 0x70d6, 0x0c7e, 0x70b8, 0xa065, 0x6008, 0xa084, 0xfbef, 0x600a,
+ 0x6018, 0x8001, 0x601a, 0x0c7f, 0x1078, 0x41fe, 0x2c50, 0x1078,
+ 0x4442, 0x6824, 0xa005, 0x0040, 0x2b5d, 0xa082, 0x0006, 0x0048,
+ 0x2b5b, 0x0078, 0x2b5d, 0x6827, 0x0005, 0x6814, 0xa084, 0x001f,
+ 0xc0bd, 0x78aa, 0x2031, 0x0020, 0x2041, 0x0001, 0x2001, 0x0003,
+ 0x0078, 0x2c92, 0xc28d, 0x72d6, 0x72c0, 0xa200, 0xa015, 0x7154,
+ 0x8108, 0xa12a, 0x0048, 0x2b75, 0x71c0, 0x2164, 0x6504, 0x85ff,
+ 0x00c0, 0x2b8c, 0x7156, 0x8421, 0x00c0, 0x2b70, 0x70d4, 0xd08c,
+ 0x0040, 0x2b88, 0x70d0, 0xa005, 0x00c0, 0x2b88, 0x70d3, 0x000a,
+ 0x007c, 0x2200, 0x0078, 0x2b7a, 0x70d4, 0xc08c, 0x70d6, 0x70d3,
+ 0x0000, 0x6034, 0xa005, 0x00c0, 0x2b89, 0x6708, 0xa784, 0x073f,
+ 0x0040, 0x2bbb, 0xd7d4, 0x00c0, 0x2b89, 0xa784, 0x0021, 0x00c0,
+ 0x2b89, 0xa784, 0x0002, 0x0040, 0x2bac, 0xa784, 0x0004, 0x0040,
+ 0x2b89, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0218, 0x00c0, 0x2b89,
+ 0xa784, 0x0100, 0x0040, 0x2bbb, 0x6018, 0xa005, 0x00c0, 0x2b89,
+ 0xa7bc, 0xfeff, 0x670a, 0x2568, 0x6823, 0x0000, 0x6e1c, 0xa684,
+ 0x000e, 0x6318, 0x0040, 0x2bcc, 0x601c, 0xa302, 0x0048, 0x2bcf,
+ 0x0040, 0x2bcf, 0x0078, 0x2b89, 0x83ff, 0x00c0, 0x2b89, 0x2d58,
+ 0x2c50, 0x7156, 0xd7bc, 0x00c0, 0x2bd8, 0x7028, 0x6022, 0x603a,
+ 0xc7bc, 0x670a, 0x68c0, 0xa065, 0xa04d, 0x6100, 0x2a60, 0x2041,
+ 0x0001, 0x6b14, 0xa39c, 0x001f, 0xa39d, 0x00c0, 0xd1fc, 0x0040,
+ 0x2bec, 0xd684, 0x0040, 0x2bee, 0xa39c, 0xffbf, 0xd6a4, 0x0040,
+ 0x2bf3, 0xa39d, 0x0020, 0xa684, 0x000e, 0x00c0, 0x2c3e, 0xc7a5,
+ 0x670a, 0x2c00, 0x68c6, 0x77a4, 0xa786, 0x0001, 0x00c0, 0x2c12,
+ 0x70d4, 0xd0b4, 0x00c0, 0x2c12, 0x7000, 0xa082, 0x0002, 0x00c8,
+ 0x2c12, 0x7830, 0xd0bc, 0x00c0, 0x2c12, 0x789b, 0x0010, 0x7baa,
+ 0x0078, 0x2c8a, 0x8739, 0x77a6, 0x2750, 0x77b0, 0xa7b0, 0x0005,
+ 0x70ac, 0xa606, 0x00c0, 0x2c1d, 0x76a8, 0x76b2, 0x2c3a, 0x8738,
+ 0x2d3a, 0x8738, 0x283a, 0x8738, 0x233a, 0x8738, 0x253a, 0x7830,
+ 0xd0bc, 0x0040, 0x2c35, 0x2091, 0x8000, 0x2091, 0x303d, 0x70d4,
+ 0xa084, 0x303d, 0x2091, 0x8000, 0x2090, 0xaad5, 0x0000, 0x0040,
+ 0x2c3d, 0x8421, 0x2200, 0x00c0, 0x2b6f, 0x007c, 0xd1dc, 0x0040,
+ 0x3e00, 0x2029, 0x0020, 0xd69c, 0x00c0, 0x2c4b, 0x8528, 0xd68c,
+ 0x00c0, 0x2c4b, 0x8528, 0x8840, 0x6f14, 0x610c, 0x8108, 0xa18c,
+ 0x00ff, 0x70cc, 0xa160, 0x2c64, 0x8cff, 0x0040, 0x2c6a, 0x6014,
+ 0xa706, 0x00c0, 0x2c53, 0x60b8, 0x8001, 0x60ba, 0x00c0, 0x2c4e,
+ 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a, 0x2200, 0x8421, 0x00c0,
+ 0x2b6f, 0x007c, 0x2a60, 0x610e, 0x69be, 0x2c00, 0x68c6, 0x8840,
+ 0x6008, 0xc0d5, 0x600a, 0x77a4, 0xa786, 0x0001, 0x00c0, 0x2c12,
+ 0x70d4, 0xd0b4, 0x00c0, 0x2c12, 0x7000, 0xa082, 0x0002, 0x00c8,
+ 0x2c12, 0x7830, 0xd0bc, 0x00c0, 0x2c12, 0x789b, 0x0010, 0x7baa,
+ 0x7daa, 0x79aa, 0x2001, 0x0002, 0x007e, 0x6018, 0x8000, 0x601a,
+ 0x0078, 0x2c93, 0x007e, 0x2960, 0x6104, 0x2a60, 0xa184, 0x0018,
+ 0x0040, 0x2caf, 0xa184, 0x0010, 0x0040, 0x2ca2, 0x1078, 0x4011,
+ 0x00c0, 0x2cd4, 0xa184, 0x0008, 0x0040, 0x2caf, 0x69a0, 0xa184,
+ 0x0600, 0x00c0, 0x2caf, 0x1078, 0x3ef5, 0x0078, 0x2cd4, 0x69a0,
+ 0xa184, 0x1e00, 0x0040, 0x2cdf, 0xa184, 0x0800, 0x0040, 0x2cc8,
+ 0x0c7e, 0x2960, 0x6000, 0xa085, 0x2000, 0x6002, 0x6104, 0xa18d,
+ 0x0010, 0x6106, 0x0c7f, 0x1078, 0x4011, 0x00c0, 0x2cd4, 0x69a0,
+ 0xa184, 0x0200, 0x0040, 0x2cd0, 0x1078, 0x3f54, 0x0078, 0x2cd4,
+ 0xa184, 0x0400, 0x00c0, 0x2cab, 0x69a0, 0xa184, 0x1000, 0x0040,
+ 0x2cdf, 0x6914, 0xa18c, 0xff00, 0x810f, 0x1078, 0x279f, 0x027f,
+ 0xa68c, 0x00e0, 0xa684, 0x0060, 0x0040, 0x2cec, 0xa086, 0x0060,
+ 0x00c0, 0x2cec, 0xa18d, 0x4000, 0xa18d, 0x0104, 0x69b6, 0x789b,
+ 0x0060, 0x2800, 0x78aa, 0x6818, 0xc0fd, 0x681a, 0xd6bc, 0x0040,
+ 0x2d07, 0xc0fc, 0x7087, 0x0000, 0xa08a, 0x000d, 0x0050, 0x2d05,
+ 0xa08a, 0x000c, 0x7186, 0x2001, 0x000c, 0x800c, 0x718a, 0x78aa,
+ 0x3518, 0x3340, 0x3428, 0x8000, 0x80ac, 0xaf80, 0x002b, 0x20a0,
+ 0x789b, 0x0000, 0xad80, 0x000b, 0x2098, 0x53a6, 0x23a8, 0x2898,
+ 0x25a0, 0xa286, 0x0020, 0x00c0, 0x2d3f, 0x70d4, 0xc0b5, 0x70d6,
+ 0x2c00, 0x70ba, 0x2d00, 0x70be, 0x6814, 0xc0fc, 0x8007, 0x7882,
+ 0xa286, 0x0002, 0x0040, 0x2d75, 0x70a4, 0x8000, 0x70a6, 0x74b4,
+ 0xa498, 0x0005, 0x70ac, 0xa306, 0x00c0, 0x2d37, 0x73a8, 0x73b6,
+ 0xa286, 0x0010, 0x0040, 0x29c1, 0x0d7f, 0x0c7f, 0x007c, 0x7000,
+ 0xa005, 0x00c0, 0x2d1d, 0xa286, 0x0002, 0x00c0, 0x2d8f, 0x1078,
+ 0x4360, 0x00c0, 0x2d1d, 0x6814, 0xc0fc, 0x8007, 0x7882, 0x2091,
+ 0x8000, 0x781b, 0x005b, 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de,
+ 0x6898, 0x78d2, 0x78da, 0x2091, 0x8001, 0x7808, 0xc08d, 0x780a,
+ 0x127e, 0x0d7e, 0x0c7e, 0x70d4, 0xa084, 0x2700, 0x2090, 0x0c7f,
+ 0x0d7f, 0x127f, 0x2900, 0x705a, 0x68bc, 0x7042, 0x7003, 0x0002,
+ 0x2d00, 0x704e, 0xad80, 0x0009, 0x7046, 0x7830, 0xd0bc, 0x0040,
+ 0x2d81, 0x2091, 0x303d, 0x70d4, 0xa084, 0x303d, 0x2091, 0x8000,
+ 0x2090, 0x70a4, 0xa005, 0x00c0, 0x2d86, 0x007c, 0x8421, 0x0040,
+ 0x2d85, 0x7250, 0x70c0, 0xa200, 0xa015, 0x0078, 0x2b6f, 0xa286,
+ 0x0010, 0x00c0, 0x2dc0, 0x1078, 0x4360, 0x00c0, 0x2d1d, 0x6814,
+ 0xc0fc, 0x8007, 0x7882, 0x781b, 0x005b, 0x68b4, 0x785a, 0x6894,
+ 0x78d6, 0x78de, 0x6898, 0x78d2, 0x78da, 0x7808, 0xc08d, 0x780a,
+ 0x70a4, 0x8000, 0x70a6, 0x74b4, 0xa490, 0x0005, 0x70ac, 0xa206,
+ 0x00c0, 0x2db3, 0x72a8, 0x72b6, 0x2900, 0x705a, 0x68bc, 0x7042,
+ 0x7003, 0x0002, 0x2d00, 0x704e, 0xad80, 0x0009, 0x7046, 0x007c,
+ 0x6bb4, 0xa39d, 0x2000, 0x7b5a, 0x6814, 0xc0fc, 0x8007, 0x7882,
+ 0x6b94, 0x7bd6, 0x7bde, 0x6e98, 0x7ed2, 0x7eda, 0x781b, 0x005b,
+ 0x2900, 0x705a, 0x7202, 0x7808, 0xc08d, 0x780a, 0x2300, 0xa605,
+ 0x0040, 0x2deb, 0x70d4, 0xa084, 0x2700, 0xa086, 0x2300, 0x00c0,
+ 0x2de5, 0x2009, 0x0000, 0x0078, 0x2de7, 0x2009, 0x0001, 0xa284,
+ 0x000f, 0x1079, 0x2def, 0xad80, 0x0009, 0x7046, 0x007c, 0x2df7,
+ 0x48bd, 0x48bd, 0x48aa, 0x48bd, 0x2df7, 0x2df7, 0x2df7, 0x1078,
+ 0x296b, 0x7808, 0xa084, 0xfffd, 0x780a, 0x1078, 0x295e, 0x0f7e,
+ 0x2079, 0x4e00, 0x78ac, 0x0f7f, 0xd084, 0x0040, 0x2e21, 0x7064,
+ 0xa086, 0x0001, 0x00c0, 0x2e0f, 0x7066, 0x0078, 0x2ef8, 0x7064,
+ 0xa086, 0x0005, 0x00c0, 0x2e1f, 0x707c, 0x2068, 0x681b, 0x0004,
+ 0x6817, 0x0000, 0x6820, 0xa084, 0x00ff, 0xc09d, 0x6822, 0x7067,
+ 0x0000, 0x70a7, 0x0000, 0x70a8, 0x70b2, 0x70b6, 0x1078, 0x2aba,
+ 0x157e, 0x2011, 0x0004, 0x7164, 0xa186, 0x0001, 0x0040, 0x2e41,
+ 0xa186, 0x0007, 0x00c0, 0x2e38, 0x701f, 0x0005, 0x0078, 0x2e41,
+ 0x701f, 0x0001, 0x7067, 0x0000, 0x70d4, 0xc0dd, 0x70d6, 0x0078,
+ 0x2e43, 0x7067, 0x0000, 0x2001, 0x4e0a, 0x2004, 0xa084, 0x00ff,
+ 0xa086, 0x0018, 0x0040, 0x2e53, 0x7018, 0x7016, 0xa005, 0x00c0,
+ 0x2e53, 0x70a7, 0x0001, 0x067e, 0x1078, 0x4586, 0x20a9, 0x0010,
+ 0x2039, 0x0000, 0x1078, 0x40f8, 0xa7b8, 0x0100, 0x00f0, 0x2e5a,
+ 0x067f, 0x7000, 0x0079, 0x2e64, 0x2e9e, 0x2e79, 0x2e79, 0x2e6e,
+ 0x2e9e, 0x2e9e, 0x2e9e, 0x2e6c, 0x1078, 0x296b, 0x7060, 0xa005,
+ 0x0040, 0x2e9e, 0xad06, 0x00c0, 0x2e79, 0x6800, 0x7062, 0x0078,
+ 0x2e8b, 0x6820, 0xd084, 0x00c0, 0x2e87, 0x6f14, 0x1078, 0x41fe,
+ 0x6008, 0xc0d4, 0x600a, 0x1078, 0x3dd0, 0x0078, 0x2e8b, 0x705c,
+ 0x2060, 0x6800, 0x6002, 0xa684, 0x5f00, 0x681e, 0x6818, 0xd0fc,
+ 0x0040, 0x2e93, 0x6a1a, 0x6817, 0x0000, 0x682b, 0x0000, 0x6820,
+ 0xa084, 0x00ff, 0xc09d, 0x6822, 0x1078, 0x202c, 0xb284, 0x0400,
+ 0x0040, 0x2ea6, 0x2021, 0x95d0, 0x0078, 0x2ea8, 0x2021, 0x94c0,
+ 0x1078, 0x2efd, 0xb284, 0x0400, 0x0040, 0x2eb2, 0x2021, 0x4e98,
+ 0x0078, 0x2eb4, 0x2021, 0x4e58, 0x1078, 0x2efd, 0x20a9, 0x0101,
+ 0xb284, 0x0400, 0x0040, 0x2ec0, 0x2021, 0x94d0, 0x0078, 0x2ec2,
+ 0x2021, 0x93c0, 0x1078, 0x2efd, 0x8420, 0x00f0, 0x2ec2, 0xb284,
+ 0x0300, 0x0040, 0x2ecf, 0x2061, 0x53c0, 0x0078, 0x2ed1, 0x2061,
+ 0x73c0, 0x2021, 0x0002, 0x20a9, 0x0100, 0x6110, 0x81ff, 0x0040,
+ 0x2eee, 0x6018, 0x017e, 0x007e, 0x2011, 0x4e02, 0x220c, 0xa102,
+ 0x2012, 0x007f, 0x017f, 0xa102, 0x0050, 0x2eee, 0x6012, 0x00c0,
+ 0x2eee, 0x2011, 0x4e04, 0x2204, 0xc0a5, 0x2012, 0x601b, 0x0000,
+ 0xace0, 0x0010, 0x00f0, 0x2ed5, 0x8421, 0x00c0, 0x2ed3, 0x157f,
+ 0x7003, 0x0000, 0x704f, 0x0000, 0x007c, 0x047e, 0x2404, 0xa005,
+ 0x0040, 0x2f18, 0x2068, 0x6800, 0x007e, 0x6a1a, 0x6817, 0x0000,
+ 0x682b, 0x0000, 0x68b4, 0xa084, 0x5f00, 0x681e, 0x6820, 0xa084,
+ 0x00ff, 0xc09d, 0x6822, 0x1078, 0x202c, 0x007f, 0x0078, 0x2eff,
+ 0x047f, 0x2023, 0x0000, 0x007c, 0xa282, 0x0003, 0x0050, 0x2f22,
+ 0x1078, 0x296b, 0x2300, 0x0079, 0x2f25, 0x2f28, 0x2fb3, 0x2fd0,
+ 0xa282, 0x0002, 0x0040, 0x2f2e, 0x1078, 0x296b, 0x7064, 0x7067,
+ 0x0000, 0x7083, 0x0000, 0x0079, 0x2f35, 0x2f3d, 0x2f3d, 0x2f3f,
+ 0x2f7f, 0x3e0c, 0x2f3d, 0x2f7f, 0x2f3d, 0x1078, 0x296b, 0x7774,
+ 0x1078, 0x40f8, 0x7774, 0xa7bc, 0x8f00, 0x1078, 0x41fe, 0x6018,
+ 0xa005, 0x0040, 0x2f76, 0xd7fc, 0x00c0, 0x2f52, 0x2021, 0x94c0,
+ 0x0078, 0x2f54, 0x2021, 0x95d0, 0x2009, 0x0005, 0x2011, 0x0010,
+ 0x1078, 0x2feb, 0x0040, 0x2f76, 0x157e, 0x20a9, 0x0101, 0xd7fc,
+ 0x00c0, 0x2f66, 0x2021, 0x93c0, 0x0078, 0x2f68, 0x2021, 0x94d0,
+ 0x047e, 0x2009, 0x0005, 0x2011, 0x0010, 0x1078, 0x2feb, 0x047f,
+ 0x0040, 0x2f75, 0x8420, 0x00f0, 0x2f68, 0x157f, 0x8738, 0xa784,
+ 0x001f, 0x00c0, 0x2f45, 0x0078, 0x29c5, 0x0078, 0x29c5, 0x7774,
+ 0x1078, 0x41fe, 0x6018, 0xa005, 0x0040, 0x2fb1, 0xd7fc, 0x00c0,
+ 0x2f8d, 0x2021, 0x94c0, 0x0078, 0x2f8f, 0x2021, 0x95d0, 0x2009,
+ 0x0005, 0x2011, 0x0020, 0x1078, 0x2feb, 0x0040, 0x2fb1, 0x157e,
+ 0x20a9, 0x0101, 0xd7fc, 0x00c0, 0x2fa1, 0x2021, 0x93c0, 0x0078,
+ 0x2fa3, 0x2021, 0x94d0, 0x047e, 0x2009, 0x0005, 0x2011, 0x0020,
+ 0x1078, 0x2feb, 0x047f, 0x0040, 0x2fb0, 0x8420, 0x00f0, 0x2fa3,
+ 0x157f, 0x0078, 0x29c5, 0x2200, 0x0079, 0x2fb6, 0x2fb9, 0x2fbb,
+ 0x2fbb, 0x1078, 0x296b, 0x2009, 0x0012, 0x7064, 0xa086, 0x0002,
+ 0x0040, 0x2fc4, 0x2009, 0x000e, 0x6818, 0xd0fc, 0x0040, 0x2fc9,
+ 0x691a, 0x7067, 0x0000, 0x70d4, 0xc0dd, 0x70d6, 0x0078, 0x430d,
+ 0x2200, 0x0079, 0x2fd3, 0x2fd8, 0x2fbb, 0x2fd6, 0x1078, 0x296b,
+ 0x1078, 0x4586, 0x7000, 0xa086, 0x0002, 0x00c0, 0x3d7e, 0x1078,
+ 0x3ded, 0x6008, 0xa084, 0xfbef, 0x600a, 0x1078, 0x3d6f, 0x0040,
+ 0x3d7e, 0x0078, 0x29c5, 0x2404, 0xa005, 0x0040, 0x3024, 0x2068,
+ 0x2d04, 0x007e, 0x6814, 0xa706, 0x0040, 0x2ffa, 0x2d20, 0x007f,
+ 0x0078, 0x2fec, 0x007f, 0x2022, 0x691a, 0x6817, 0x0000, 0x682b,
+ 0x0000, 0x68b4, 0xa084, 0x5f00, 0x681e, 0x6820, 0xa084, 0x00ff,
+ 0xa205, 0x6822, 0x1078, 0x202c, 0x2021, 0x4e02, 0x241c, 0x8319,
+ 0x2322, 0x6010, 0x8001, 0x6012, 0x00c0, 0x301b, 0x2021, 0x4e04,
+ 0x2404, 0xc0a5, 0x2022, 0x6008, 0xa084, 0xf9ef, 0x600a, 0x1078,
+ 0x2adb, 0x1078, 0x3ded, 0x007c, 0xa085, 0x0001, 0x0078, 0x3023,
+ 0x2300, 0x0079, 0x302b, 0x3030, 0x302e, 0x30b0, 0x1078, 0x296b,
+ 0x78e4, 0xa005, 0x00d0, 0x3066, 0x3208, 0x007e, 0x2001, 0x4e04,
+ 0x2004, 0xd0ec, 0x007f, 0x0040, 0x3041, 0xa18c, 0x0300, 0x0078,
+ 0x3043, 0xa18c, 0x0400, 0x0040, 0x3049, 0x0018, 0x29c1, 0x0078,
+ 0x304b, 0x0028, 0x29c1, 0x2008, 0xa084, 0x0030, 0x00c0, 0x3052,
+ 0x0078, 0x3770, 0x78ec, 0xa084, 0x0003, 0x0040, 0x3050, 0x2100,
+ 0xa084, 0x0007, 0x0079, 0x305c, 0x3090, 0x309a, 0x3085, 0x3064,
+ 0x4355, 0x4355, 0x3064, 0x30a5, 0x1078, 0x296b, 0x7000, 0xa086,
+ 0x0004, 0x00c0, 0x3080, 0x7064, 0xa086, 0x0002, 0x00c0, 0x3076,
+ 0x2011, 0x0002, 0x2019, 0x0000, 0x0078, 0x2f1c, 0x7064, 0xa086,
+ 0x0006, 0x0040, 0x3070, 0x7064, 0xa086, 0x0004, 0x0040, 0x3070,
+ 0x79e4, 0x2001, 0x0003, 0x0078, 0x33fa, 0x6818, 0xd0fc, 0x0040,
+ 0x308b, 0x681b, 0x001d, 0x1078, 0x40c8, 0x781b, 0x0064, 0x007c,
+ 0x6818, 0xd0fc, 0x0040, 0x3096, 0x681b, 0x001d, 0x1078, 0x40c8,
+ 0x0078, 0x4331, 0x6818, 0xd0fc, 0x0040, 0x30a0, 0x681b, 0x001d,
+ 0x1078, 0x40c8, 0x781b, 0x00f8, 0x007c, 0x6818, 0xd0fc, 0x0040,
+ 0x30ab, 0x681b, 0x001d, 0x1078, 0x40c8, 0x781b, 0x00c8, 0x007c,
+ 0xa584, 0x000f, 0x00c0, 0x30cf, 0x1078, 0x295e, 0x7000, 0x0079,
+ 0x30b9, 0x29c5, 0x30c1, 0x30c3, 0x3d7e, 0x3d7e, 0x3d7e, 0x30c1,
+ 0x30c1, 0x1078, 0x296b, 0x1078, 0x3ded, 0x6008, 0xa084, 0xfbef,
+ 0x600a, 0x1078, 0x3d6f, 0x0040, 0x3d7e, 0x0078, 0x29c5, 0x78e4,
+ 0xa005, 0x00d0, 0x3066, 0x3208, 0x007e, 0x2001, 0x4e04, 0x2004,
+ 0xd0ec, 0x007f, 0x0040, 0x30e0, 0xa18c, 0x0300, 0x0078, 0x30e2,
+ 0xa18c, 0x0400, 0x0040, 0x30e8, 0x0018, 0x3066, 0x0078, 0x30ea,
+ 0x0028, 0x3066, 0x2008, 0xa084, 0x0030, 0x00c0, 0x30f2, 0x781b,
+ 0x005b, 0x007c, 0x78ec, 0xa084, 0x0003, 0x0040, 0x30ef, 0x2100,
+ 0xa184, 0x0007, 0x0079, 0x30fc, 0x310b, 0x310f, 0x3106, 0x3104,
+ 0x4355, 0x4355, 0x3104, 0x434f, 0x1078, 0x296b, 0x1078, 0x40d0,
+ 0x781b, 0x0064, 0x007c, 0x1078, 0x40d0, 0x0078, 0x4331, 0x1078,
+ 0x40d0, 0x781b, 0x00f8, 0x007c, 0x1078, 0x40d0, 0x781b, 0x00c8,
+ 0x007c, 0x2300, 0x0079, 0x311c, 0x3121, 0x311f, 0x3123, 0x1078,
+ 0x296b, 0x0078, 0x395f, 0x681b, 0x0016, 0x78a3, 0x0000, 0x79e4,
+ 0xa184, 0x0030, 0x0040, 0x395f, 0x78ec, 0xa084, 0x0003, 0x0040,
+ 0x395f, 0xa184, 0x0100, 0x0040, 0x3127, 0xa184, 0x0007, 0x0079,
+ 0x3139, 0x3141, 0x310f, 0x3085, 0x430d, 0x4355, 0x4355, 0x430d,
+ 0x434f, 0x1078, 0x4319, 0x007c, 0xa282, 0x0005, 0x0050, 0x314a,
+ 0x1078, 0x296b, 0x2300, 0x0079, 0x314d, 0x3150, 0x3380, 0x338b,
+ 0x2200, 0x0079, 0x3153, 0x316d, 0x315a, 0x316d, 0x3158, 0x3363,
+ 0x1078, 0x296b, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa082,
+ 0x0020, 0x0048, 0x40b7, 0xa08a, 0x0004, 0x00c8, 0x40b7, 0x0079,
+ 0x3169, 0x40b7, 0x40b7, 0x40b7, 0x4061, 0x789b, 0x0018, 0x79a8,
+ 0xa184, 0x0080, 0x0040, 0x317e, 0x0078, 0x40b7, 0x7000, 0xa005,
+ 0x00c0, 0x3174, 0x2011, 0x0004, 0x0078, 0x3b4a, 0xa184, 0x00ff,
+ 0xa08a, 0x0010, 0x00c8, 0x40b7, 0x0079, 0x3186, 0x3198, 0x3196,
+ 0x31ad, 0x31b1, 0x3284, 0x40b7, 0x40b7, 0x3286, 0x40b7, 0x40b7,
+ 0x335f, 0x335f, 0x40b7, 0x40b7, 0x40b7, 0x3361, 0x1078, 0x296b,
+ 0xd6e4, 0x0040, 0x31a3, 0x2001, 0x0300, 0x8000, 0x8000, 0x783a,
+ 0x781b, 0x00c3, 0x007c, 0x6818, 0xd0fc, 0x0040, 0x31ab, 0x681b,
+ 0x001d, 0x0078, 0x319b, 0x0078, 0x430d, 0x681b, 0x001d, 0x0078,
+ 0x40c1, 0x6920, 0x6922, 0xa684, 0x1800, 0x00c0, 0x3216, 0x6820,
+ 0xd084, 0x00c0, 0x321c, 0x6818, 0xa086, 0x0008, 0x00c0, 0x31c2,
+ 0x681b, 0x0000, 0xd6d4, 0x0040, 0x3281, 0xd6bc, 0x0040, 0x3202,
+ 0x7087, 0x0000, 0x6818, 0xa084, 0x003f, 0xa08a, 0x000d, 0x0050,
+ 0x3202, 0xa08a, 0x000c, 0x7186, 0x2001, 0x000c, 0x800c, 0x718a,
+ 0x789b, 0x0061, 0x78aa, 0x157e, 0x137e, 0x147e, 0x017e, 0x3208,
+ 0xa18c, 0x0300, 0x0040, 0x31f4, 0x007e, 0x2001, 0x4e04, 0x2004,
+ 0xd0ec, 0x007f, 0x0040, 0x31f0, 0x20a1, 0x012b, 0x0078, 0x31f6,
+ 0x20a1, 0x022b, 0x0078, 0x31f6, 0x20a1, 0x012b, 0x017f, 0x789b,
+ 0x0000, 0x8000, 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f,
+ 0x137f, 0x157f, 0x6038, 0xa005, 0x00c0, 0x3211, 0x681c, 0xa084,
+ 0x000e, 0x0040, 0x40c1, 0x1078, 0x40d7, 0x782b, 0x3008, 0x0078,
+ 0x3213, 0x8001, 0x603a, 0x781b, 0x0067, 0x007c, 0xd6e4, 0x0040,
+ 0x321c, 0x781b, 0x0079, 0x007c, 0xa684, 0x0060, 0x0040, 0x327e,
+ 0xd6dc, 0x0040, 0x327e, 0xd6fc, 0x00c0, 0x3228, 0x0078, 0x323f,
+ 0xc6fc, 0x7e5a, 0x6eb6, 0x7adc, 0x79d8, 0x78d0, 0x801b, 0x00c8,
+ 0x3232, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x6b98,
+ 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae, 0xd6f4,
+ 0x0040, 0x3245, 0xc6f4, 0x7e5a, 0x6eb6, 0x7000, 0xa086, 0x0003,
+ 0x00c0, 0x3253, 0x007e, 0x1078, 0x4586, 0x1078, 0x48bd, 0x007f,
+ 0x781b, 0x0076, 0x007c, 0xa006, 0x1078, 0x49c3, 0x6ab0, 0x69ac,
+ 0x6c98, 0x6b94, 0x2200, 0xa105, 0x0040, 0x3262, 0x2200, 0xa422,
+ 0x2100, 0xa31b, 0x6caa, 0x7cd2, 0x7cda, 0x6ba6, 0x7bd6, 0x7bde,
+ 0x2300, 0xa405, 0x00c0, 0x3272, 0xc6f5, 0x7e5a, 0x6eb6, 0x781b,
+ 0x0076, 0x007c, 0x781b, 0x0076, 0x2200, 0xa115, 0x00c0, 0x327b,
+ 0x1078, 0x48bd, 0x007c, 0x1078, 0x48f5, 0x007c, 0x781b, 0x0079,
+ 0x007c, 0x781b, 0x0067, 0x007c, 0x1078, 0x296b, 0x0078, 0x32d2,
+ 0x6920, 0xd1c4, 0x0040, 0x329b, 0xc1c4, 0x6922, 0x0c7e, 0x7058,
+ 0x2060, 0x6000, 0xc0e4, 0x6002, 0x6004, 0xa084, 0xfff5, 0x6006,
+ 0x0c7f, 0x0078, 0x32c6, 0xd1cc, 0x0040, 0x32c6, 0xc1cc, 0x6922,
+ 0x0c7e, 0x7058, 0x2060, 0x6000, 0xc0ec, 0x6002, 0x6004, 0xc0a4,
+ 0x6006, 0x2008, 0x2c48, 0x0c7f, 0xd19c, 0x0040, 0x32c6, 0x1078,
+ 0x41fa, 0x1078, 0x3ef5, 0x88ff, 0x0040, 0x32c6, 0x789b, 0x0060,
+ 0x2800, 0x78aa, 0x7e58, 0xc695, 0x7e5a, 0xd6d4, 0x00c0, 0x32c3,
+ 0x781b, 0x0064, 0x007c, 0x781b, 0x0078, 0x007c, 0x7e58, 0xd6d4,
+ 0x00c0, 0x32cd, 0x781b, 0x0067, 0x007c, 0x781b, 0x0079, 0x007c,
+ 0x0078, 0x40bc, 0x2019, 0x0000, 0x7990, 0xa18c, 0x0007, 0x00c0,
+ 0x32e0, 0x6820, 0xa084, 0x0100, 0x0040, 0x32d0, 0x2009, 0x0008,
+ 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001, 0x00c0,
+ 0x32fc, 0x2300, 0x7ca8, 0xa400, 0x2018, 0xa102, 0x0040, 0x32f4,
+ 0x0048, 0x32f4, 0x0078, 0x32f6, 0x0078, 0x3288, 0x24a8, 0x7aa8,
+ 0x00f0, 0x32f6, 0x0078, 0x32e2, 0xa284, 0x00f0, 0xa086, 0x0020,
+ 0x00c0, 0x3350, 0x8318, 0x8318, 0x2300, 0xa102, 0x0040, 0x330c,
+ 0x0048, 0x330c, 0x0078, 0x334d, 0xa286, 0x0023, 0x0040, 0x32d0,
+ 0x681c, 0xa084, 0xfff1, 0x681e, 0x7e58, 0xa684, 0xfff1, 0xc0a5,
+ 0x2030, 0x7e5a, 0x6008, 0xc0a5, 0x600a, 0x0c7e, 0x7058, 0x2060,
+ 0x6004, 0x2008, 0x2c48, 0x0c7f, 0xd1a4, 0x0040, 0x332d, 0x1078,
+ 0x41fa, 0x1078, 0x4011, 0x0078, 0x333b, 0x0c7e, 0x7058, 0x2060,
+ 0x6004, 0x2008, 0x2c48, 0x0c7f, 0xd19c, 0x0040, 0x32c6, 0x1078,
+ 0x41fa, 0x1078, 0x3ef5, 0x88ff, 0x0040, 0x32c6, 0x789b, 0x0060,
+ 0x2800, 0x78aa, 0xc695, 0x7e5a, 0xd6d4, 0x00c0, 0x334a, 0x781b,
+ 0x0064, 0x007c, 0x781b, 0x0078, 0x007c, 0x7aa8, 0x0078, 0x32e2,
+ 0x8318, 0x2300, 0xa102, 0x0040, 0x3359, 0x0048, 0x3359, 0x0078,
+ 0x32e2, 0xa284, 0x0080, 0x00c0, 0x40c1, 0x0078, 0x40bc, 0x0078,
+ 0x40c1, 0x0078, 0x40b7, 0x7058, 0xa04d, 0x789b, 0x0018, 0x78a8,
+ 0xa084, 0x00ff, 0xa08e, 0x0001, 0x0040, 0x3370, 0x1078, 0x296b,
+ 0x7aa8, 0xa294, 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004,
+ 0x00c8, 0x40b7, 0x0079, 0x337c, 0x40b7, 0x3e46, 0x40b7, 0x3fb9,
+ 0xa282, 0x0000, 0x00c0, 0x3386, 0x1078, 0x296b, 0x1078, 0x40c8,
+ 0x781b, 0x0078, 0x007c, 0xa282, 0x0003, 0x00c0, 0x3391, 0x1078,
+ 0x296b, 0xd4fc, 0x00c0, 0x33b1, 0x7064, 0xa005, 0x0040, 0x339a,
+ 0x1078, 0x296b, 0x6f14, 0x7776, 0xa7bc, 0x8f00, 0x1078, 0x41fe,
+ 0x6008, 0xa085, 0x0021, 0x600a, 0x8738, 0xa784, 0x001f, 0x00c0,
+ 0x339e, 0x1078, 0x40cc, 0x7067, 0x0002, 0x701f, 0x0009, 0x0078,
+ 0x33b3, 0x1078, 0x40db, 0x781b, 0x0078, 0x007c, 0xa282, 0x0004,
+ 0x0050, 0x33bc, 0x1078, 0x296b, 0x2300, 0x0079, 0x33bf, 0x33c2,
+ 0x3582, 0x35c5, 0xa286, 0x0003, 0x0040, 0x33fa, 0x7200, 0x7cd8,
+ 0x7ddc, 0x7fd0, 0x71d4, 0xd1bc, 0x00c0, 0x33f2, 0xd1b4, 0x0040,
+ 0x33f2, 0x7868, 0xa084, 0x00ff, 0x00c0, 0x33f2, 0xa282, 0x0002,
+ 0x00c8, 0x33f2, 0x0d7e, 0x783b, 0x8300, 0x781b, 0x004c, 0x70bc,
+ 0xa06d, 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2,
+ 0x78da, 0xc1b4, 0x71d6, 0x7003, 0x0030, 0x0d7f, 0x2001, 0x0000,
+ 0x0078, 0x33fe, 0x783b, 0x1300, 0x781b, 0x004a, 0x2001, 0x0000,
+ 0x0078, 0x33fe, 0x7200, 0x7cd8, 0x7ddc, 0x7fd0, 0x704a, 0x68a0,
+ 0xd0ec, 0x0040, 0x3406, 0x6008, 0xc08d, 0x600a, 0xa284, 0x000f,
+ 0x0079, 0x340a, 0x3562, 0x3417, 0x3414, 0x36c8, 0x3754, 0x29c5,
+ 0x3412, 0x3412, 0x1078, 0x296b, 0x6008, 0xc0d4, 0x600a, 0xd6e4,
+ 0x0040, 0x341f, 0x7048, 0xa086, 0x0014, 0x00c0, 0x343f, 0x1078,
+ 0x4586, 0x2009, 0x0000, 0x6818, 0xd0fc, 0x0040, 0x3428, 0x7048,
+ 0xa086, 0x0014, 0x0040, 0x3439, 0x6818, 0xa086, 0x0008, 0x00c0,
+ 0x351a, 0x7858, 0xd09c, 0x0040, 0x351a, 0x6820, 0xd0ac, 0x0040,
+ 0x351a, 0x681b, 0x0014, 0x2009, 0x0002, 0x0078, 0x347e, 0x7868,
+ 0xa08c, 0x00ff, 0x0040, 0x347e, 0xa186, 0x0008, 0x00c0, 0x3455,
+ 0x6008, 0xc0a4, 0x600a, 0x1078, 0x3d6f, 0x0040, 0x347e, 0x1078,
+ 0x3ded, 0x1078, 0x4586, 0x0078, 0x3466, 0xa186, 0x0028, 0x00c0,
+ 0x347e, 0x6018, 0xa005, 0x0040, 0x3448, 0x8001, 0x0040, 0x3448,
+ 0x8001, 0x0040, 0x3448, 0x601e, 0x0078, 0x3448, 0x6820, 0xd084,
+ 0x0040, 0x29c5, 0xc084, 0x6822, 0x1078, 0x2acc, 0x705c, 0x0c7e,
+ 0x2060, 0x6800, 0x6002, 0x0c7f, 0x6004, 0x6802, 0xa005, 0x2d00,
+ 0x00c0, 0x347b, 0x6002, 0x6006, 0x0078, 0x29c5, 0x017e, 0x81ff,
+ 0x00c0, 0x34c8, 0x7000, 0xa086, 0x0030, 0x0040, 0x34c8, 0x71d4,
+ 0xd1bc, 0x00c0, 0x34c8, 0xd1b4, 0x00c0, 0x34af, 0x7060, 0xa005,
+ 0x00c0, 0x34c8, 0x70a4, 0xa086, 0x0001, 0x0040, 0x34c8, 0x7003,
+ 0x0000, 0x047e, 0x057e, 0x077e, 0x067e, 0x0c7e, 0x0d7e, 0x1078,
+ 0x29ee, 0x0d7f, 0x0c7f, 0x067f, 0x077f, 0x057f, 0x047f, 0x71d4,
+ 0xd1b4, 0x00c0, 0x34c8, 0x7003, 0x0040, 0x0078, 0x34c8, 0x1078,
+ 0x4360, 0x00c0, 0x34c8, 0x781b, 0x005b, 0x0d7e, 0x70bc, 0xa06d,
+ 0x68b4, 0x785a, 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2, 0x78da,
+ 0xc1b4, 0x71d6, 0x7003, 0x0030, 0x7808, 0xc08d, 0x780a, 0x0d7f,
+ 0x1078, 0x35ff, 0x017f, 0x81ff, 0x0040, 0x351a, 0xa684, 0xdf00,
+ 0x681e, 0x682b, 0x0000, 0x6f14, 0xa186, 0x0002, 0x00c0, 0x351b,
+ 0x6818, 0xa086, 0x0014, 0x00c0, 0x34e4, 0x2008, 0xd6e4, 0x0040,
+ 0x34e4, 0x7868, 0xa08c, 0x00ff, 0x1078, 0x2aba, 0x1078, 0x2adb,
+ 0x6820, 0xd0dc, 0x00c0, 0x351b, 0x8717, 0xa294, 0x000f, 0x8213,
+ 0x8213, 0x8213, 0xb284, 0x0300, 0x0040, 0x34fa, 0xa290, 0x52c0,
+ 0x0078, 0x34fc, 0xa290, 0x5340, 0xa290, 0x0000, 0x221c, 0xd3c4,
+ 0x00c0, 0x3504, 0x0078, 0x350a, 0x8210, 0x2204, 0xa085, 0x0018,
+ 0x2012, 0x8211, 0xd3d4, 0x0040, 0x3515, 0x68a0, 0xd0c4, 0x00c0,
+ 0x3515, 0x1078, 0x3679, 0x0078, 0x29c5, 0x6008, 0xc08d, 0x600a,
+ 0x0078, 0x351b, 0x692a, 0x6916, 0x6818, 0xd0fc, 0x0040, 0x3522,
+ 0x7048, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x6410, 0x84ff, 0x0040,
+ 0x3537, 0x2009, 0x4e02, 0x2104, 0x8001, 0x200a, 0x8421, 0x6412,
+ 0x00c0, 0x3537, 0x2021, 0x4e04, 0x2404, 0xc0a5, 0x2022, 0x6018,
+ 0xa005, 0x0040, 0x353f, 0x8001, 0x601a, 0x00c0, 0x3542, 0x6008,
+ 0xc0a4, 0x600a, 0x6820, 0xd084, 0x00c0, 0x354e, 0x6800, 0xa005,
+ 0x00c0, 0x354b, 0x6002, 0x6006, 0x0078, 0x3552, 0x705c, 0x2060,
+ 0x6800, 0x6002, 0x2061, 0x4e00, 0x6887, 0x0103, 0x2d08, 0x206b,
+ 0x0000, 0x6068, 0xa005, 0x616a, 0x0040, 0x3561, 0x2d02, 0x0078,
+ 0x3562, 0x616e, 0x7200, 0xa286, 0x0030, 0x0040, 0x3572, 0xa286,
+ 0x0040, 0x00c0, 0x29c5, 0x7003, 0x0002, 0x704c, 0x2068, 0x68c4,
+ 0x2060, 0x007c, 0x7003, 0x0002, 0x70bc, 0xa06d, 0x68bc, 0x7042,
+ 0x70b8, 0xa065, 0x68c0, 0x705a, 0x2d00, 0x704e, 0xad80, 0x0009,
+ 0x7046, 0x007c, 0xa282, 0x0004, 0x0048, 0x3588, 0x1078, 0x296b,
+ 0x2200, 0x0079, 0x358b, 0x358f, 0x35a0, 0x35ad, 0x35a0, 0xa586,
+ 0x1300, 0x0040, 0x35a0, 0xa586, 0x8300, 0x00c0, 0x3586, 0x7003,
+ 0x0000, 0x6018, 0x8001, 0x601a, 0x6008, 0xa084, 0xfbef, 0x600a,
+ 0x7000, 0xa086, 0x0005, 0x0040, 0x35aa, 0x1078, 0x40c8, 0x781b,
+ 0x0078, 0x007c, 0x781b, 0x0079, 0x007c, 0x7890, 0x8007, 0x8001,
+ 0xa084, 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c, 0x00ff,
+ 0xa186, 0x0003, 0x0040, 0x35c2, 0xa186, 0x0000, 0x0040, 0x35c2,
+ 0x0078, 0x40b7, 0x781b, 0x0079, 0x007c, 0x6820, 0xc095, 0x6822,
+ 0x82ff, 0x00c0, 0x35cf, 0x1078, 0x40c8, 0x0078, 0x35d6, 0x8211,
+ 0x0040, 0x35d4, 0x1078, 0x296b, 0x1078, 0x40db, 0x781b, 0x0078,
+ 0x007c, 0x1078, 0x4383, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x35fc,
+ 0x017e, 0x3208, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f,
+ 0x0040, 0x35ee, 0xa18c, 0x0300, 0x0078, 0x35f0, 0xa18c, 0x0400,
+ 0x017f, 0x0040, 0x35f7, 0x0018, 0x35fc, 0x0078, 0x35f9, 0x0028,
+ 0x35fc, 0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, 0xa684,
+ 0x0060, 0x00c0, 0x3609, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078,
+ 0x3678, 0xd6dc, 0x00c0, 0x3621, 0x68b4, 0xd0dc, 0x00c0, 0x3621,
+ 0x6998, 0x6a94, 0x692e, 0x6a32, 0x7048, 0xa005, 0x00c0, 0x361e,
+ 0x2200, 0xa105, 0x0040, 0x4586, 0x704b, 0x0015, 0x0078, 0x4586,
+ 0x007c, 0xd6ac, 0x0040, 0x3647, 0xd6f4, 0x0040, 0x362d, 0x682f,
+ 0x0000, 0x6833, 0x0000, 0x0078, 0x4586, 0x68b4, 0xa084, 0x4000,
+ 0xa635, 0xd6f4, 0x00c0, 0x3627, 0x7048, 0xa005, 0x00c0, 0x363a,
+ 0x704b, 0x0015, 0xd6dc, 0x00c0, 0x3643, 0x68b4, 0xd0dc, 0x0040,
+ 0x3643, 0x6ca8, 0x6da4, 0x6c2e, 0x6d32, 0x0078, 0x4586, 0xd6f4,
+ 0x0040, 0x3650, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x4586,
+ 0x68b4, 0xa084, 0x4800, 0xa635, 0xd6f4, 0x00c0, 0x364a, 0x7048,
+ 0xa005, 0x00c0, 0x365d, 0x704b, 0x0015, 0x2408, 0x2510, 0x2700,
+ 0x80fb, 0x00c8, 0x3664, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291,
+ 0x0000, 0x692e, 0x6a32, 0x2100, 0xa205, 0x00c0, 0x3671, 0x0078,
+ 0x4586, 0x7000, 0xa086, 0x0006, 0x0040, 0x3678, 0x0078, 0x4586,
+ 0x007c, 0x6946, 0x6008, 0xc0cd, 0xd3cc, 0x0040, 0x3680, 0xc08d,
+ 0x600a, 0x6818, 0x683a, 0x681b, 0x0006, 0x688f, 0x0000, 0x6893,
+ 0x0000, 0x6a30, 0x692c, 0x6a3e, 0x6942, 0x682f, 0x0003, 0x6833,
+ 0x0000, 0x6837, 0x0020, 0x6897, 0x0000, 0x689b, 0x0020, 0x7000,
+ 0x0079, 0x369a, 0x29c5, 0x36ac, 0x36a4, 0x36a2, 0x36a2, 0x36a2,
+ 0x36a2, 0x36a2, 0x1078, 0x296b, 0x6820, 0xd084, 0x00c0, 0x36ac,
+ 0x1078, 0x3dd0, 0x0078, 0x36b2, 0x705c, 0x2c50, 0x2060, 0x6800,
+ 0x6002, 0x2a60, 0x3208, 0xa18c, 0x0300, 0x0040, 0x36bb, 0x2021,
+ 0x4e58, 0x0078, 0x36bd, 0x2021, 0x4e98, 0x2404, 0xa005, 0x0040,
+ 0x36c4, 0x2020, 0x0078, 0x36bd, 0x2d22, 0x206b, 0x0000, 0x007c,
+ 0x1078, 0x3dd7, 0x1078, 0x3ded, 0x6008, 0xc0cc, 0x600a, 0x682b,
+ 0x0000, 0x789b, 0x000e, 0x6f14, 0x6938, 0x691a, 0x6944, 0x6916,
+ 0x3208, 0xa18c, 0x0300, 0x0040, 0x36e1, 0x2009, 0x0000, 0x0078,
+ 0x36e3, 0x2009, 0x0001, 0x1078, 0x49f8, 0xd6dc, 0x0040, 0x36eb,
+ 0x691c, 0xc1ed, 0x691e, 0x6818, 0xd0fc, 0x0040, 0x36fa, 0x7868,
+ 0xa08c, 0x00ff, 0x0040, 0x36f8, 0x681b, 0x001e, 0x0078, 0x36fa,
+ 0x681b, 0x0000, 0xb284, 0x0300, 0x00c0, 0x3702, 0x2021, 0x4e98,
+ 0x0078, 0x3704, 0x2021, 0x4e58, 0x6800, 0x2022, 0x6a3c, 0x6940,
+ 0x6a32, 0x692e, 0x68c0, 0x2060, 0x6000, 0xd0a4, 0x0040, 0x3744,
+ 0x2041, 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x0d7e, 0x0f7e,
+ 0x157e, 0x147e, 0x2079, 0x4e00, 0x1078, 0x1dff, 0x147f, 0x157f,
+ 0x0f7f, 0x70cc, 0x2010, 0x2009, 0x0101, 0x027e, 0x2204, 0xa06d,
+ 0x0040, 0x3734, 0x6814, 0xa706, 0x0040, 0x3731, 0x6800, 0x0078,
+ 0x3727, 0x6820, 0xc0d5, 0x6822, 0x027f, 0x8210, 0x8109, 0x00c0,
+ 0x3725, 0x0d7f, 0x7067, 0x0003, 0x707f, 0x0000, 0x7776, 0x7083,
+ 0x000f, 0x71d4, 0xc1dc, 0x71d6, 0x6818, 0xa086, 0x0002, 0x00c0,
+ 0x3750, 0x6817, 0x0000, 0x682b, 0x0000, 0x681c, 0xc0ec, 0x681e,
+ 0x1078, 0x202c, 0x0078, 0x29c5, 0x7cd8, 0x7ddc, 0x7fd0, 0x1078,
+ 0x35ff, 0x682b, 0x0000, 0x789b, 0x000e, 0x6f14, 0x1078, 0x4387,
+ 0xa08c, 0x00ff, 0x6916, 0x6818, 0xd0fc, 0x0040, 0x3769, 0x7048,
+ 0x681a, 0xa68c, 0xdf00, 0x691e, 0x7067, 0x0000, 0x0078, 0x29c5,
+ 0x7000, 0xa005, 0x00c0, 0x3776, 0x0078, 0x29c5, 0xa006, 0x1078,
+ 0x4586, 0x6920, 0xd1ac, 0x00c0, 0x377f, 0x681b, 0x0014, 0xa68c,
+ 0xdf00, 0x691e, 0x682b, 0x0000, 0x6820, 0xa084, 0x00ff, 0x6822,
+ 0x7000, 0x0079, 0x378b, 0x29c5, 0x3795, 0x3795, 0x3798, 0x3798,
+ 0x3798, 0x3793, 0x3793, 0x1078, 0x296b, 0x6818, 0x0078, 0x33fa,
+ 0x6008, 0xc0a4, 0x600a, 0x6817, 0x0000, 0x0078, 0x3d95, 0x2300,
+ 0x0079, 0x37a2, 0x37a5, 0x37a7, 0x3817, 0x1078, 0x296b, 0xd6fc,
+ 0x00c0, 0x37fe, 0x7000, 0xa00d, 0x0079, 0x37ae, 0x29c5, 0x37b8,
+ 0x37b8, 0x37e8, 0x37b8, 0x37fb, 0x37b6, 0x37b6, 0x1078, 0x296b,
+ 0xa684, 0x0060, 0x0040, 0x37e8, 0xa086, 0x0060, 0x00c0, 0x37e5,
+ 0xc6ac, 0xc6f4, 0xc6ed, 0x7e5a, 0x6eb6, 0x681c, 0xc0ac, 0x681e,
+ 0xa186, 0x0002, 0x0040, 0x37d7, 0x1078, 0x4586, 0x69ac, 0x68b0,
+ 0xa115, 0x0040, 0x37d7, 0x1078, 0x48f5, 0x0078, 0x37d9, 0x1078,
+ 0x48bd, 0x781b, 0x0079, 0x71d4, 0xd1b4, 0x00c0, 0x29c1, 0x70a4,
+ 0xa086, 0x0001, 0x00c0, 0x2a0b, 0x007c, 0xd6ec, 0x0040, 0x37c2,
+ 0x6818, 0xd0fc, 0x0040, 0x37fb, 0xd6f4, 0x00c0, 0x37f5, 0x681b,
+ 0x0015, 0x781b, 0x0079, 0x0078, 0x29c1, 0x681b, 0x0007, 0x682f,
+ 0x0000, 0x6833, 0x0000, 0x1078, 0x4319, 0x007c, 0xc6fc, 0x7e5a,
+ 0x7adc, 0x79d8, 0x78d0, 0x801b, 0x00c8, 0x3807, 0x8000, 0xa084,
+ 0x003f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100, 0xa302, 0x68b2,
+ 0x6b94, 0x2200, 0xa303, 0x68ae, 0x781b, 0x0079, 0x007c, 0x1078,
+ 0x296b, 0x2300, 0x0079, 0x381c, 0x3821, 0x3846, 0x38a6, 0x1078,
+ 0x296b, 0x7000, 0x0079, 0x3824, 0x382c, 0x382e, 0x3837, 0x382c,
+ 0x382c, 0x382c, 0x382c, 0x382c, 0x1078, 0x296b, 0x69ac, 0x68b0,
+ 0xa115, 0x0040, 0x3837, 0x1078, 0x48f5, 0x0078, 0x3839, 0x1078,
+ 0x48bd, 0x681c, 0xc0b4, 0x681e, 0x70d4, 0xd0b4, 0x00c0, 0x29c1,
+ 0x70a4, 0xa086, 0x0001, 0x00c0, 0x2a0b, 0x007c, 0xd6fc, 0x00c0,
+ 0x3896, 0x7000, 0xa00d, 0x0079, 0x384d, 0x29c5, 0x385d, 0x3857,
+ 0x388d, 0x385d, 0x3893, 0x3855, 0x3855, 0x1078, 0x296b, 0x6894,
+ 0x78d6, 0x78de, 0x6898, 0x78d2, 0x78da, 0xa684, 0x0060, 0x0040,
+ 0x388d, 0xa086, 0x0060, 0x00c0, 0x388a, 0xa6b4, 0xbfbf, 0xc6ed,
+ 0x7e5a, 0x6eb6, 0xa186, 0x0002, 0x0040, 0x3879, 0x1078, 0x4586,
+ 0x69ac, 0x68b0, 0xa115, 0x0040, 0x3879, 0x1078, 0x48f5, 0x0078,
+ 0x387b, 0x1078, 0x48bd, 0x781b, 0x0079, 0x681c, 0xc0b4, 0x681e,
+ 0x71d4, 0xd1b4, 0x00c0, 0x29c1, 0x70a4, 0xa086, 0x0001, 0x00c0,
+ 0x2a0b, 0x007c, 0xd6ec, 0x0040, 0x3867, 0x6818, 0xd0fc, 0x0040,
+ 0x3893, 0x681b, 0x0007, 0x781b, 0x00f9, 0x007c, 0xc6fc, 0x7e5a,
+ 0x7adc, 0x79d8, 0x6b98, 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200,
+ 0xa303, 0x68ae, 0x79d2, 0x781b, 0x0079, 0x007c, 0xd6dc, 0x0040,
+ 0x38af, 0x782b, 0x3009, 0x781b, 0x0079, 0x0078, 0x29c1, 0x7884,
+ 0xc0ac, 0x7886, 0x78e4, 0xa084, 0x0008, 0x00c0, 0x38c2, 0xa484,
+ 0x0200, 0x0040, 0x38bc, 0xc6f5, 0xc6dd, 0x7e5a, 0x781b, 0x0079,
+ 0x0078, 0x29c1, 0x6820, 0xc095, 0x6822, 0x1078, 0x4292, 0xc6dd,
+ 0x1078, 0x40c8, 0x781b, 0x0078, 0x0078, 0x29c1, 0x2300, 0x0079,
+ 0x38d1, 0x38d4, 0x38d6, 0x38d8, 0x1078, 0x296b, 0x0078, 0x40c1,
+ 0xd6d4, 0x00c0, 0x3913, 0x79e4, 0xd1ac, 0x0040, 0x38e6, 0x78ec,
+ 0xa084, 0x0003, 0x0040, 0x38e6, 0x782b, 0x3009, 0x789b, 0x0060,
+ 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x79e4, 0xd1ac, 0x0040,
+ 0x38f6, 0x78ec, 0xa084, 0x0003, 0x00c0, 0x390f, 0x2001, 0x4e04,
+ 0x2004, 0xd0e4, 0x00c0, 0x390b, 0x6820, 0xd0c4, 0x0040, 0x390b,
+ 0x0c7e, 0x7058, 0x2060, 0x6004, 0xc09d, 0x6006, 0x6008, 0xa084,
+ 0x00ff, 0x600a, 0x0c7f, 0x2001, 0x0014, 0x0078, 0x33fa, 0xa184,
+ 0x0007, 0x0079, 0x3949, 0x7a90, 0xa294, 0x0007, 0x789b, 0x0060,
+ 0x79a8, 0x81ff, 0x0040, 0x3947, 0x789b, 0x0010, 0x7ba8, 0xa384,
+ 0x0001, 0x00c0, 0x393a, 0x7ba8, 0x7ba8, 0xa386, 0x0001, 0x00c0,
+ 0x392d, 0x2009, 0xfff7, 0x0078, 0x3933, 0xa386, 0x0003, 0x00c0,
+ 0x393a, 0x2009, 0xffef, 0x0c7e, 0x7058, 0x2060, 0x6004, 0xa104,
+ 0x6006, 0x0c7f, 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb,
+ 0x785a, 0x782b, 0x3009, 0x6920, 0xa18c, 0xfcff, 0x6922, 0x0078,
+ 0x430d, 0x3090, 0x309a, 0x3953, 0x3959, 0x3951, 0x3951, 0x430d,
+ 0x430d, 0x1078, 0x296b, 0x6920, 0xa18c, 0xfcff, 0x6922, 0x0078,
+ 0x4313, 0x6920, 0xa18c, 0xfcff, 0x6922, 0x0078, 0x430d, 0x79e4,
+ 0xa184, 0x0030, 0x0040, 0x3969, 0x78ec, 0xa084, 0x0003, 0x00c0,
+ 0x399d, 0x7000, 0xa086, 0x0004, 0x00c0, 0x3983, 0x7064, 0xa086,
+ 0x0002, 0x00c0, 0x3979, 0x2011, 0x0002, 0x2019, 0x0000, 0x0078,
+ 0x2f1c, 0x7064, 0xa086, 0x0006, 0x0040, 0x3973, 0x7064, 0xa086,
+ 0x0004, 0x0040, 0x3973, 0x7000, 0xa086, 0x0000, 0x0040, 0x29c1,
+ 0x6920, 0xa184, 0x0420, 0x0040, 0x3992, 0xc1d4, 0x6922, 0x6818,
+ 0x0078, 0x33fa, 0x6818, 0xa08e, 0x0002, 0x0040, 0x399b, 0xc0fd,
+ 0x681a, 0x2001, 0x0014, 0x0078, 0x33fa, 0xa184, 0x0007, 0x0079,
+ 0x39a1, 0x430d, 0x430d, 0x39a9, 0x430d, 0x4355, 0x4355, 0x430d,
+ 0x430d, 0xd6bc, 0x0040, 0x39eb, 0x7184, 0x81ff, 0x0040, 0x39eb,
+ 0xa182, 0x000d, 0x00d0, 0x39b8, 0x7087, 0x0000, 0x0078, 0x39bd,
+ 0xa182, 0x000c, 0x7086, 0x2009, 0x000c, 0x789b, 0x0061, 0x79aa,
+ 0x157e, 0x137e, 0x147e, 0x7088, 0x8114, 0xa210, 0x728a, 0xa080,
+ 0x000b, 0xad00, 0x2098, 0xb284, 0x0300, 0x0040, 0x39df, 0x007e,
+ 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040, 0x39db, 0x20a1,
+ 0x012b, 0x0078, 0x39e1, 0x20a1, 0x022b, 0x0078, 0x39e1, 0x20a1,
+ 0x012b, 0x789b, 0x0000, 0x8108, 0x81ac, 0x53a6, 0x147f, 0x137f,
+ 0x157f, 0x0078, 0x4313, 0xd6d4, 0x00c0, 0x3a3f, 0x6820, 0xd084,
+ 0x0040, 0x4313, 0xa68c, 0x0060, 0xa684, 0x0060, 0x0040, 0x39fd,
+ 0xa086, 0x0060, 0x00c0, 0x39fd, 0xc1f5, 0xc194, 0x795a, 0x69b6,
+ 0x789b, 0x0060, 0x78ab, 0x0000, 0x789b, 0x0061, 0x6818, 0xc0fd,
+ 0x681a, 0x78aa, 0x8008, 0x810c, 0x0040, 0x3e06, 0xa18c, 0x00f8,
+ 0x00c0, 0x3e06, 0x157e, 0x137e, 0x147e, 0x017e, 0x3208, 0xa18c,
+ 0x0300, 0x0040, 0x3a2b, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec,
+ 0x007f, 0x0040, 0x3a27, 0x20a1, 0x012b, 0x0078, 0x3a2d, 0x20a1,
+ 0x022b, 0x0078, 0x3a2d, 0x20a1, 0x012b, 0x017f, 0x789b, 0x0000,
+ 0x8000, 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f,
+ 0x157f, 0x6814, 0xc0fc, 0x8007, 0x7882, 0x0078, 0x4313, 0x6818,
+ 0xd0fc, 0x0040, 0x3a45, 0x681b, 0x0008, 0x6820, 0xc0ad, 0x6822,
+ 0x1078, 0x40d0, 0x781b, 0x00ea, 0x007c, 0x2300, 0x0079, 0x3a50,
+ 0x3a55, 0x3b2d, 0x3a53, 0x1078, 0x296b, 0x7cd8, 0x7ddc, 0x7fd0,
+ 0x82ff, 0x00c0, 0x3a7e, 0x7200, 0xa286, 0x0003, 0x0040, 0x33c7,
+ 0x71d4, 0xd1bc, 0x00c0, 0x3a81, 0xd1b4, 0x0040, 0x3a81, 0x0d7e,
+ 0x783b, 0x8800, 0x781b, 0x004c, 0x70bc, 0xa06d, 0x68b4, 0xc0a5,
+ 0x785a, 0x6894, 0x78d6, 0x78de, 0x6898, 0x78d2, 0x78da, 0xc1b4,
+ 0x71d6, 0x7003, 0x0030, 0x0d7f, 0x0078, 0x3a85, 0x7200, 0x0078,
+ 0x3a85, 0x783b, 0x1800, 0x781b, 0x004a, 0xa284, 0x000f, 0x0079,
+ 0x3a89, 0x3b18, 0x3ac7, 0x3a93, 0x33f6, 0x3a91, 0x3b18, 0x3a91,
+ 0x3a91, 0x1078, 0x296b, 0x681c, 0xd0ec, 0x0040, 0x3a9a, 0x6008,
+ 0xc08d, 0x600a, 0x6920, 0xc185, 0x6922, 0x6800, 0x6006, 0xa005,
+ 0x00c0, 0x3aa3, 0x6002, 0x6008, 0xc0d4, 0x600a, 0x681c, 0xa084,
+ 0x000e, 0x00c0, 0x3ab7, 0xb284, 0x0300, 0x0040, 0x3ab3, 0x2009,
+ 0x94c0, 0x0078, 0x3abc, 0x2009, 0x95d0, 0x0078, 0x3abc, 0x7030,
+ 0x68ba, 0x7140, 0x70cc, 0xa108, 0x2104, 0x6802, 0x2d0a, 0x715e,
+ 0xd6dc, 0x00c0, 0x3ac7, 0xc6fc, 0x6eb6, 0x0078, 0x3b18, 0x6eb6,
+ 0xa684, 0x0060, 0x00c0, 0x3ad1, 0xa684, 0x7fff, 0x68b6, 0x0078,
+ 0x3b18, 0xd6dc, 0x00c0, 0x3adf, 0xa684, 0x7fff, 0x68b6, 0x6894,
+ 0x68a6, 0x6898, 0x68aa, 0x1078, 0x4586, 0x0078, 0x3b18, 0xd6ac,
+ 0x0040, 0x3aeb, 0xa006, 0x1078, 0x4586, 0x2408, 0x2510, 0x69aa,
+ 0x6aa6, 0x0078, 0x3afb, 0x2408, 0x2510, 0x2700, 0x801b, 0x00c8,
+ 0x3af2, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x69aa,
+ 0x6aa6, 0x1078, 0x4586, 0xd6fc, 0x0040, 0x3b18, 0xa684, 0x7fff,
+ 0x68b6, 0x2510, 0x2408, 0xd6ac, 0x00c0, 0x3b10, 0x2700, 0x801b,
+ 0x00c8, 0x3b0b, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+ 0x6b98, 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae,
+ 0x7000, 0xa086, 0x0030, 0x00c0, 0x29c5, 0x7003, 0x0002, 0x70bc,
+ 0xa06d, 0x68bc, 0x7042, 0x70b8, 0xa065, 0x68c0, 0x705a, 0x2d00,
+ 0x704e, 0xad80, 0x0009, 0x7046, 0x007c, 0xa586, 0x8800, 0x00c0,
+ 0x3b3a, 0x7003, 0x0000, 0x6018, 0x8001, 0x601a, 0x6008, 0xa084,
+ 0xfbef, 0x600a, 0x0078, 0x40c1, 0x7047, 0x0000, 0xa282, 0x0006,
+ 0x0050, 0x3b44, 0x1078, 0x296b, 0x2300, 0x0079, 0x3b47, 0x3b4a,
+ 0x3b5c, 0x3b68, 0x2200, 0x0079, 0x3b4d, 0x3b53, 0x40c1, 0x3b55,
+ 0x3b53, 0x3ba2, 0x3bf7, 0x1078, 0x296b, 0x7a80, 0xa294, 0x0f00,
+ 0x1078, 0x3c81, 0x0078, 0x40b7, 0x1078, 0x3b79, 0x0079, 0x3b60,
+ 0x40c1, 0x3b66, 0x3b66, 0x3ba2, 0x3b66, 0x40c1, 0x1078, 0x296b,
+ 0x1078, 0x3b79, 0x0079, 0x3b6c, 0x3b74, 0x3b72, 0x3b72, 0x3b74,
+ 0x3b72, 0x3b74, 0x1078, 0x296b, 0x1078, 0x40db, 0x781b, 0x0078,
+ 0x007c, 0x7000, 0xa086, 0x0002, 0x00c0, 0x3b8a, 0x1078, 0x3ded,
+ 0x0078, 0x3b84, 0x1078, 0x4586, 0x6008, 0xa084, 0xfbef, 0x600a,
+ 0x0078, 0x3b8f, 0x7000, 0xa086, 0x0003, 0x0040, 0x3b82, 0x7003,
+ 0x0005, 0xb284, 0x0300, 0x0040, 0x3b99, 0x2001, 0x95e0, 0x0078,
+ 0x3b9b, 0x2001, 0x9612, 0x2068, 0x704e, 0xad80, 0x0009, 0x7046,
+ 0x2200, 0x007c, 0x7000, 0xa086, 0x0002, 0x00c0, 0x3bb4, 0x70d4,
+ 0xc0b5, 0x70d6, 0x2c00, 0x70ba, 0x2d00, 0x70be, 0x0078, 0x3bb9,
+ 0x1078, 0x4586, 0x0078, 0x3bb9, 0x7000, 0xa086, 0x0003, 0x0040,
+ 0x3bb0, 0x7003, 0x0001, 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018,
+ 0x7ca8, 0xa484, 0x001f, 0xa215, 0x2069, 0x94c0, 0xb284, 0x0300,
+ 0x00c0, 0x3bcd, 0xc2fd, 0x2069, 0x95d0, 0x2d04, 0x2d08, 0x715e,
+ 0xa06d, 0x0040, 0x3bda, 0x6814, 0xa206, 0x0040, 0x3bdc, 0x6800,
+ 0x0078, 0x3bce, 0x1078, 0x3c81, 0x6eb4, 0x7e5a, 0x6920, 0xa184,
+ 0x0c00, 0x0040, 0x3cab, 0x7064, 0xa086, 0x0006, 0x00c0, 0x3bee,
+ 0x7074, 0xa206, 0x00c0, 0x3bee, 0x7066, 0x707e, 0x681b, 0x0005,
+ 0xc1ad, 0xc1d4, 0x6922, 0x1078, 0x40d0, 0x0078, 0x3cab, 0x7200,
+ 0xa286, 0x0002, 0x00c0, 0x3c09, 0x70d4, 0xc0b5, 0x70d6, 0x2c00,
+ 0x70ba, 0x2d00, 0x70be, 0x0078, 0x3c0d, 0x1078, 0x4586, 0x0078,
+ 0x3c0d, 0xa286, 0x0003, 0x0040, 0x3c05, 0x7003, 0x0001, 0x7a80,
+ 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x001f, 0xa215,
+ 0xb284, 0x0300, 0x00c0, 0x3c1d, 0xc2fd, 0x79a8, 0x79a8, 0xa18c,
+ 0x00ff, 0x2118, 0x70cc, 0xa168, 0x2d04, 0x2d08, 0x715e, 0xa06d,
+ 0x0040, 0x3c31, 0x6814, 0xa206, 0x0040, 0x3c5a, 0x6800, 0x0078,
+ 0x3c25, 0x7003, 0x0005, 0xb284, 0x0300, 0x0040, 0x3c3b, 0x2001,
+ 0x95e0, 0x0078, 0x3c3d, 0x2001, 0x9612, 0x2068, 0x704e, 0x157e,
+ 0x20a9, 0x0032, 0x2003, 0x0000, 0x8000, 0x00f0, 0x3c42, 0x157f,
+ 0xb284, 0x0300, 0x0040, 0x3c4f, 0xc2fc, 0x0078, 0x3c50, 0xc2fd,
+ 0x6a16, 0xad80, 0x0009, 0x7046, 0x68b7, 0x0700, 0x6823, 0x0800,
+ 0x6827, 0x0003, 0x6eb4, 0x6920, 0xa184, 0x0c00, 0x0040, 0x3cab,
+ 0xd0dc, 0x0040, 0x3c76, 0x7064, 0xa086, 0x0004, 0x00c0, 0x3c72,
+ 0x7074, 0xa206, 0x00c0, 0x3c72, 0x7078, 0xa306, 0x00c0, 0x3c72,
+ 0x7066, 0x707e, 0x1078, 0x40d7, 0x0078, 0x3cab, 0x681b, 0x0005,
+ 0xc1ad, 0xc1d4, 0x6922, 0x1078, 0x40d0, 0x707f, 0x0000, 0x0078,
+ 0x3cab, 0x7003, 0x0005, 0xb284, 0x0300, 0x0040, 0x3c8b, 0x2001,
+ 0x95e0, 0x0078, 0x3c8d, 0x2001, 0x9612, 0x2068, 0x704e, 0x157e,
+ 0x20a9, 0x0032, 0x2003, 0x0000, 0x8000, 0x00f0, 0x3c92, 0x157f,
+ 0xb284, 0x0300, 0x0040, 0x3c9f, 0xc2fc, 0x0078, 0x3ca0, 0xc2fd,
+ 0x6a16, 0xad80, 0x0009, 0x7046, 0x68b7, 0x0700, 0x6823, 0x0800,
+ 0x6827, 0x0003, 0x007c, 0xc6ec, 0xa6ac, 0x0060, 0x0040, 0x3cfd,
+ 0x6b98, 0x6c94, 0x69ac, 0x68b0, 0xa105, 0x00c0, 0x3cd8, 0x7bd2,
+ 0x7bda, 0x7cd6, 0x7cde, 0xa586, 0x0060, 0x0040, 0x3d02, 0xd6f4,
+ 0x00c0, 0x3cc3, 0xc6ed, 0xa6b4, 0xb7ff, 0x7e5a, 0x2009, 0x0079,
+ 0xd69c, 0x0040, 0x3cd0, 0x2009, 0x0078, 0x2019, 0x0000, 0x2320,
+ 0x791a, 0xd6ec, 0x0040, 0x3d0d, 0x1078, 0x48bd, 0x0078, 0x3d0d,
+ 0x68b0, 0xa31a, 0x2100, 0xa423, 0x2400, 0xa305, 0x0040, 0x3d04,
+ 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0x68b0, 0xd6f4, 0x00c0, 0x3ce9,
+ 0xc6ed, 0xc6f4, 0x7e5a, 0x2011, 0x0079, 0xd69c, 0x0040, 0x3cf5,
+ 0x2011, 0x0078, 0x2019, 0x0000, 0x2320, 0x7a1a, 0xd6ec, 0x0040,
+ 0x3d0d, 0x1078, 0x48f5, 0x0078, 0x3d0d, 0x2019, 0x0000, 0x2320,
+ 0x0078, 0x3d04, 0xa6b4, 0xb7ff, 0x7e5a, 0x2009, 0x0079, 0xd69c,
+ 0x0040, 0x3d0c, 0x2009, 0x0078, 0x791a, 0x68c0, 0x705a, 0x2d00,
+ 0x704e, 0x68c4, 0x2060, 0x71d4, 0x2001, 0x4e01, 0x2004, 0xd0c4,
+ 0x00c0, 0x3d62, 0x70d8, 0xa02d, 0x0040, 0x3d3b, 0xd1bc, 0x0040,
+ 0x3d55, 0x7a80, 0xa294, 0x0f00, 0x70dc, 0xa206, 0x0040, 0x3d2c,
+ 0x78e0, 0xa504, 0x00c0, 0x3d62, 0x70da, 0xc1bc, 0x71d6, 0x0078,
+ 0x3d62, 0x2031, 0x0001, 0x852c, 0x0048, 0x3d3a, 0x8633, 0x8210,
+ 0x0078, 0x3d33, 0x007c, 0x7de0, 0xa594, 0xff00, 0x0040, 0x3d48,
+ 0x2011, 0x0008, 0x852f, 0x1078, 0x3d31, 0x8637, 0x0078, 0x3d4a,
+ 0x1078, 0x3d31, 0x8217, 0x7880, 0xa084, 0x0f00, 0xa206, 0x0040,
+ 0x3d62, 0x72de, 0x76da, 0x0078, 0x3d62, 0x7a80, 0xa294, 0x0f00,
+ 0x70dc, 0xa236, 0x0040, 0x3d52, 0x78e0, 0xa534, 0x0040, 0x3d52,
+ 0xc1bd, 0x71d6, 0xd1b4, 0x00c0, 0x29c1, 0x2300, 0xa405, 0x0040,
+ 0x29c1, 0x70a4, 0xa086, 0x0001, 0x00c0, 0x2a0b, 0x007c, 0x6020,
+ 0xa005, 0x0040, 0x3d7d, 0x8001, 0x6022, 0x6008, 0xa085, 0x0008,
+ 0x600a, 0x700f, 0x0100, 0x702c, 0x6026, 0x007c, 0xa006, 0x1078,
+ 0x4586, 0x7000, 0xa086, 0x0002, 0x0040, 0x3d8b, 0x7064, 0xa086,
+ 0x0005, 0x00c0, 0x3d95, 0x682b, 0x0000, 0x6817, 0x0000, 0x681b,
+ 0x0001, 0x6823, 0x0040, 0x681f, 0x0100, 0x7000, 0xa084, 0x000f,
+ 0x0079, 0x3d9a, 0x29c5, 0x3daa, 0x3da4, 0x3dcc, 0x3db4, 0x29c5,
+ 0x3da2, 0x3da2, 0x1078, 0x296b, 0x1078, 0x3dd7, 0x1078, 0x3dd0,
+ 0x0078, 0x3db0, 0x1078, 0x3dd7, 0x705c, 0x2060, 0x6800, 0x6002,
+ 0x1078, 0x202c, 0x0078, 0x29c5, 0x7064, 0x7067, 0x0000, 0x7083,
+ 0x0000, 0x0079, 0x3dbb, 0x3dc8, 0x3dc8, 0x3dc3, 0x3dc3, 0x3dc3,
+ 0x3dc8, 0x3dc3, 0x3dc8, 0x77d4, 0xc7dd, 0x77d6, 0x0079, 0x2f35,
+ 0x7067, 0x0000, 0x0078, 0x29c5, 0x681b, 0x0000, 0x0078, 0x36c8,
+ 0x6800, 0xa005, 0x00c0, 0x3dd5, 0x6002, 0x6006, 0x007c, 0x6410,
+ 0x84ff, 0x0040, 0x3de9, 0x2009, 0x4e02, 0x2104, 0x8001, 0x200a,
+ 0x8421, 0x6412, 0x00c0, 0x3de9, 0x2021, 0x4e04, 0x2404, 0xc0a5,
+ 0x2022, 0x6008, 0xc0a4, 0x600a, 0x007c, 0x6018, 0xa005, 0x0040,
+ 0x3df3, 0x8001, 0x601a, 0x007c, 0x1078, 0x4383, 0x681b, 0x0018,
+ 0x0078, 0x3e34, 0x1078, 0x4383, 0x681b, 0x0019, 0x0078, 0x3e34,
+ 0x1078, 0x4383, 0x681b, 0x001a, 0x0078, 0x3e34, 0x1078, 0x4383,
+ 0x681b, 0x0003, 0x0078, 0x3e34, 0x7774, 0x1078, 0x41fe, 0x7178,
+ 0xa18c, 0x00ff, 0x3210, 0xa294, 0x0300, 0x0040, 0x3e1b, 0xa1e8,
+ 0x93c0, 0x0078, 0x3e1d, 0xa1e8, 0x94d0, 0x2d04, 0x2d08, 0x2068,
+ 0xa005, 0x00c0, 0x3e26, 0x707e, 0x0078, 0x29c5, 0x6814, 0x7274,
+ 0xa206, 0x0040, 0x3e2e, 0x6800, 0x0078, 0x3e1e, 0x6800, 0x200a,
+ 0x681b, 0x0005, 0x707f, 0x0000, 0x1078, 0x3dd7, 0x6820, 0xd084,
+ 0x00c0, 0x3e3c, 0x1078, 0x3dd0, 0x1078, 0x3ded, 0x681f, 0x0000,
+ 0x6823, 0x0020, 0x1078, 0x202c, 0x0078, 0x29c5, 0xa282, 0x0003,
+ 0x00c0, 0x40b7, 0x7da8, 0xa5ac, 0x00ff, 0x7e5a, 0x7ea8, 0xa6b4,
+ 0x00ff, 0x6920, 0xc1bd, 0x6922, 0xd1c4, 0x0040, 0x3ea1, 0xc1c4,
+ 0x6922, 0xa6b4, 0x00ff, 0x0040, 0x3e8e, 0xa682, 0x000c, 0x0048,
+ 0x3e65, 0x0040, 0x3e65, 0x2031, 0x000c, 0x2500, 0xa086, 0x000a,
+ 0x0040, 0x3e6c, 0x852b, 0x852b, 0x1078, 0x4190, 0x0040, 0x3e74,
+ 0x1078, 0x3f6f, 0x0078, 0x3e97, 0x1078, 0x414b, 0x0c7e, 0x2960,
+ 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, 0x3fa5, 0x0c7f, 0x6920,
+ 0xc1c5, 0x6922, 0x7e58, 0xc695, 0x7e5a, 0xd6d4, 0x00c0, 0x3e8b,
+ 0x781b, 0x0064, 0x007c, 0x781b, 0x0078, 0x007c, 0x0c7e, 0x2960,
+ 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, 0x3fa5, 0x0c7f, 0x7e58,
+ 0xd6d4, 0x00c0, 0x3e9e, 0x781b, 0x0067, 0x007c, 0x781b, 0x0079,
+ 0x007c, 0x0c7e, 0x7058, 0x2060, 0x6100, 0xd1e4, 0x0040, 0x3eea,
+ 0x6208, 0x8217, 0xa294, 0x00ff, 0xa282, 0x000c, 0x0048, 0x3eb4,
+ 0x0040, 0x3eb4, 0x2011, 0x000c, 0x2600, 0xa202, 0x00c8, 0x3eb9,
+ 0x2230, 0x6208, 0xa294, 0x00ff, 0x2001, 0x4e05, 0x2004, 0xd0e4,
+ 0x00c0, 0x3ece, 0x78ec, 0xd0e4, 0x0040, 0x3ece, 0xa282, 0x000a,
+ 0x00c8, 0x3ed4, 0x2011, 0x000a, 0x0078, 0x3ed4, 0xa282, 0x000c,
+ 0x00c8, 0x3ed4, 0x2011, 0x000c, 0x2200, 0xa502, 0x00c8, 0x3ed9,
+ 0x2228, 0x1078, 0x414f, 0x2500, 0xa086, 0x000a, 0x0040, 0x3ee2,
+ 0x852b, 0x852b, 0x1078, 0x4190, 0x0040, 0x3eea, 0x1078, 0x3f6f,
+ 0x0078, 0x3eee, 0x1078, 0x414b, 0x1078, 0x3fa5, 0x7858, 0xc095,
+ 0x785a, 0x0c7f, 0x781b, 0x0078, 0x007c, 0x0c7e, 0x2960, 0x6000,
+ 0xd0e4, 0x00c0, 0x3f0b, 0xa084, 0x0040, 0x00c0, 0x3f05, 0x6104,
+ 0xa18c, 0xfff5, 0x6106, 0x0c7f, 0x007c, 0x2011, 0x0032, 0x2019,
+ 0x0000, 0x0078, 0x3f36, 0x68a0, 0xd0cc, 0x00c0, 0x3f05, 0x6208,
+ 0xa294, 0x00ff, 0x2001, 0x4e05, 0x2004, 0xd0e4, 0x00c0, 0x3f24,
+ 0x78ec, 0xd0e4, 0x0040, 0x3f24, 0xa282, 0x000b, 0x00c8, 0x3f24,
+ 0x2011, 0x000a, 0x0078, 0x3f2a, 0xa282, 0x000c, 0x00c8, 0x3f2a,
+ 0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff, 0xa382, 0x000c,
+ 0x0048, 0x3f36, 0x0040, 0x3f36, 0x2019, 0x000c, 0x78ab, 0x0001,
+ 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005,
+ 0x6820, 0xc0c5, 0x6822, 0x70d4, 0xd0b4, 0x0040, 0x3f52, 0xc0b4,
+ 0x70d6, 0x70b8, 0xa065, 0x6008, 0xa084, 0xfbef, 0x600a, 0x6018,
+ 0x8001, 0x601a, 0x0c7f, 0x007c, 0x0c7e, 0x2960, 0x6104, 0xa18c,
+ 0xfff5, 0x6106, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078, 0x3f60,
+ 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa,
+ 0xa8c0, 0x0005, 0x6820, 0xc0c5, 0x6822, 0x0c7f, 0x007c, 0x0c7e,
+ 0x7158, 0x2160, 0x2018, 0xa08c, 0x0020, 0x0040, 0x3f78, 0xc0ac,
+ 0x2008, 0xa084, 0xfff0, 0xa635, 0x7e86, 0x6018, 0x789a, 0x7eae,
+ 0x6612, 0x78a4, 0xa084, 0xfff0, 0xa18c, 0x000f, 0xa105, 0xc0f4,
+ 0xa39c, 0x0020, 0x0040, 0x3f8e, 0xa085, 0x4000, 0xc0fc, 0xd0b4,
+ 0x00c0, 0x3f93, 0xc0fd, 0x78a6, 0x6016, 0x788a, 0xa6b4, 0x000f,
+ 0x8637, 0x8204, 0x8004, 0xa084, 0x00ff, 0xa605, 0x600e, 0x6004,
+ 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x007c, 0x0c7e, 0x7058, 0x2060,
+ 0x6018, 0x789a, 0x78a4, 0xa084, 0xfff0, 0x78a6, 0x6012, 0x7884,
+ 0xa084, 0xfff0, 0x7886, 0x600c, 0xa084, 0x00ff, 0x600e, 0x0c7f,
+ 0x007c, 0xa282, 0x0002, 0x00c0, 0x40b7, 0x7aa8, 0x6920, 0xc1bd,
+ 0x6922, 0xd1cc, 0x0040, 0x3ff4, 0xc1cc, 0x6922, 0xa294, 0x00ff,
+ 0xa282, 0x0002, 0x00c8, 0x40b7, 0x1078, 0x4044, 0x1078, 0x3fa5,
+ 0xa980, 0x0001, 0x200c, 0x1078, 0x41fa, 0x1078, 0x3ef5, 0x88ff,
+ 0x0040, 0x3fea, 0x789b, 0x0060, 0x2800, 0x78aa, 0x7e58, 0xc695,
+ 0x7e5a, 0xd6d4, 0x00c0, 0x3fe7, 0x781b, 0x0064, 0x007c, 0x781b,
+ 0x0078, 0x007c, 0x7e58, 0xd6d4, 0x00c0, 0x3ff1, 0x781b, 0x0067,
+ 0x007c, 0x781b, 0x0079, 0x007c, 0xa282, 0x0002, 0x00c8, 0x3ffc,
+ 0xa284, 0x0001, 0x0040, 0x4005, 0x7158, 0xa188, 0x0000, 0x210c,
+ 0xd1ec, 0x00c0, 0x4005, 0x2011, 0x0000, 0x1078, 0x412c, 0x1078,
+ 0x4044, 0x1078, 0x3fa5, 0x7858, 0xc095, 0x785a, 0x781b, 0x0078,
+ 0x007c, 0x0c7e, 0x027e, 0x2960, 0x6000, 0x2011, 0x0001, 0xd0ec,
+ 0x00c0, 0x4025, 0xa084, 0x0080, 0x00c0, 0x4023, 0xc1a4, 0x6106,
+ 0xa006, 0x0078, 0x4041, 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab,
+ 0x0002, 0x78ab, 0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x70d4, 0xd0b4,
+ 0x0040, 0x403d, 0xc0b4, 0x70d6, 0x70b8, 0xa065, 0x6008, 0xa084,
+ 0xfbef, 0x600a, 0x6018, 0x8001, 0x601a, 0x6820, 0xa085, 0x0200,
+ 0x6822, 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7058, 0x2060, 0x82ff,
+ 0x0040, 0x404c, 0x2011, 0x0040, 0x6018, 0xa080, 0x0002, 0x789a,
+ 0x78a4, 0xa084, 0xffbf, 0xa205, 0xc0fc, 0xd0b4, 0x00c0, 0x4059,
+ 0xc0fd, 0x78a6, 0x6016, 0x788a, 0x6004, 0xc0a4, 0x6006, 0x0c7f,
+ 0x007c, 0x007e, 0x7000, 0xa086, 0x0003, 0x0040, 0x406a, 0x007f,
+ 0x0078, 0x406d, 0x007f, 0x0078, 0x40b4, 0xd6ac, 0x0040, 0x40b4,
+ 0x7888, 0xa084, 0x0040, 0x0040, 0x40b4, 0x7bb8, 0xa384, 0x003f,
+ 0x831b, 0x00c8, 0x407c, 0x8000, 0xa005, 0x0040, 0x4091, 0x831b,
+ 0x00c8, 0x4085, 0x8001, 0x0040, 0x40b1, 0xd6f4, 0x0040, 0x4091,
+ 0x78b8, 0x801b, 0x00c8, 0x408d, 0x8000, 0xa084, 0x003f, 0x00c0,
+ 0x40b1, 0xc6f4, 0x7e5a, 0x79d8, 0x7adc, 0x2001, 0x0001, 0xa108,
+ 0x00c8, 0x409c, 0xa291, 0x0000, 0x79d2, 0x79da, 0x7ad6, 0x7ade,
+ 0x1078, 0x49c3, 0x781b, 0x0076, 0xb284, 0x0300, 0x0040, 0x40ac,
+ 0x2001, 0x0000, 0x0078, 0x40ae, 0x2001, 0x0001, 0x1078, 0x484b,
+ 0x007c, 0x781b, 0x0076, 0x007c, 0x781b, 0x0079, 0x007c, 0x1078,
+ 0x40df, 0x781b, 0x0078, 0x007c, 0x1078, 0x40c8, 0x781b, 0x0078,
+ 0x007c, 0x6827, 0x0002, 0x1078, 0x40d0, 0x781b, 0x0078, 0x007c,
+ 0x2001, 0x0005, 0x0078, 0x40e1, 0x2001, 0x000c, 0x0078, 0x40e1,
+ 0x6820, 0xc0d5, 0x6822, 0x2001, 0x0006, 0x0078, 0x40e1, 0x2001,
+ 0x000d, 0x0078, 0x40e1, 0x2001, 0x0009, 0x0078, 0x40e1, 0x2001,
+ 0x0007, 0x789b, 0x007e, 0x78aa, 0xc69d, 0x7e5a, 0x70d4, 0xd0b4,
+ 0x0040, 0x40f7, 0xc0b4, 0x70d6, 0x0c7e, 0x70b8, 0xa065, 0x6008,
+ 0xa084, 0xfbef, 0x600a, 0x6018, 0x8001, 0x601a, 0x0c7f, 0x007c,
+ 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b, 0x8703, 0x017e,
+ 0xb28c, 0x0300, 0x0040, 0x4108, 0xa0e0, 0x52c0, 0x0078, 0x410a,
+ 0xa0e0, 0x5340, 0x017f, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184,
+ 0x000f, 0x0040, 0x411a, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004,
+ 0xc09d, 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184, 0x0040,
+ 0x0040, 0x412a, 0xa184, 0xffbf, 0xc0fd, 0x78a6, 0x6016, 0x6004,
+ 0xc0a5, 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab, 0x0001,
+ 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060, 0x78ab,
+ 0x0004, 0x70d4, 0xd0b4, 0x0040, 0x414a, 0xc0b4, 0x70d6, 0x0c7e,
+ 0x70b8, 0xa065, 0x6008, 0xa084, 0xfbef, 0x600a, 0x6018, 0x8001,
+ 0x601a, 0x0c7f, 0x007c, 0x2031, 0x0000, 0x2029, 0x0032, 0x789b,
+ 0x0010, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa,
+ 0x7eaa, 0x789b, 0x0060, 0x78ab, 0x0005, 0x70d4, 0xd0b4, 0x0040,
+ 0x416e, 0xc0b4, 0x70d6, 0x0c7e, 0x70b8, 0xa065, 0x6008, 0xa084,
+ 0xfbef, 0x600a, 0x6018, 0x8001, 0x601a, 0x0c7f, 0x007c, 0x157e,
+ 0x8007, 0xa084, 0x00ff, 0x8003, 0x8003, 0xa080, 0x0020, 0x789a,
+ 0x79a4, 0xa18c, 0xfff0, 0x2021, 0x41e3, 0x2019, 0x0011, 0x20a9,
+ 0x000e, 0x2011, 0x0032, 0x2404, 0xa084, 0xfff0, 0xa106, 0x0040,
+ 0x418e, 0x8420, 0x2300, 0xa210, 0x00f0, 0x4183, 0x157f, 0x007c,
+ 0x157e, 0x2001, 0x4e05, 0x2004, 0xd0e4, 0x00c0, 0x41c1, 0x2021,
+ 0x41f1, 0x20a9, 0x0009, 0x2011, 0x0028, 0xa582, 0x0019, 0x0040,
+ 0x41d7, 0x0048, 0x41d7, 0x8420, 0x95a9, 0x2011, 0x0032, 0xa582,
+ 0x0032, 0x0040, 0x41d7, 0x0048, 0x41d7, 0x8420, 0x95a9, 0x2019,
+ 0x000a, 0x2011, 0x0064, 0x2200, 0xa502, 0x0040, 0x41d7, 0x0048,
+ 0x41d7, 0x8420, 0x2300, 0xa210, 0x00f0, 0x41b3, 0x157f, 0x0078,
+ 0x41d5, 0x2021, 0x41e3, 0x2019, 0x0011, 0x20a9, 0x000e, 0x2011,
+ 0x0032, 0x2200, 0xa502, 0x0040, 0x41d7, 0x0048, 0x41d7, 0x8420,
+ 0x2300, 0xa210, 0x00f0, 0x41c9, 0x157f, 0xa006, 0x007c, 0x157f,
+ 0xa582, 0x0064, 0x00c8, 0x41e0, 0x7808, 0xa085, 0x0070, 0x780a,
+ 0x2404, 0xa005, 0x007c, 0x1209, 0x3002, 0x3202, 0x4203, 0x4403,
+ 0x5404, 0x5604, 0x6605, 0x6805, 0x7806, 0x7a06, 0x0c07, 0x0c07,
+ 0x0e07, 0x10e1, 0x330a, 0x5805, 0x5a05, 0x6a06, 0x6c06, 0x7c07,
+ 0x7e07, 0x0e00, 0x789b, 0x0010, 0xa046, 0x007c, 0xa784, 0x0f00,
+ 0x800b, 0xa784, 0x001f, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105,
+ 0xd7fc, 0x0040, 0x420f, 0xa0e0, 0x73c0, 0x0078, 0x4211, 0xa0e0,
+ 0x53c0, 0x007c, 0x0e7e, 0x0f7e, 0xd084, 0x0040, 0x421f, 0x2079,
+ 0x0100, 0x2009, 0x4e80, 0x2071, 0x4e80, 0x0078, 0x422f, 0x2009,
+ 0x4e40, 0x2071, 0x4e40, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x0040,
+ 0x422d, 0x2079, 0x0100, 0x0078, 0x422f, 0x2079, 0x0200, 0x2091,
+ 0x8000, 0x2104, 0xa084, 0x000f, 0x0079, 0x4236, 0x4240, 0x4240,
+ 0x4240, 0x4240, 0x4240, 0x4240, 0x423e, 0x423e, 0x1078, 0x296b,
+ 0x69b4, 0xc1f5, 0xa18c, 0xff9f, 0x69b6, 0xa005, 0x0040, 0x428f,
+ 0x7858, 0xa084, 0xff9f, 0xa085, 0x6000, 0x785a, 0x7828, 0xa086,
+ 0x1814, 0x00c0, 0x428f, 0x784b, 0x0004, 0x7848, 0xa084, 0x0004,
+ 0x00c0, 0x4255, 0x784b, 0x0008, 0x7848, 0xa084, 0x0008, 0x00c0,
+ 0x425c, 0x7830, 0xd0bc, 0x00c0, 0x428f, 0x007e, 0x2001, 0x4e04,
+ 0x2004, 0xd0ec, 0x007f, 0x0040, 0x4271, 0xb284, 0x0300, 0x0078,
+ 0x4273, 0xb284, 0x0400, 0x0040, 0x4279, 0x0018, 0x428f, 0x0078,
+ 0x427b, 0x0028, 0x428f, 0x79e4, 0xa184, 0x0030, 0x0040, 0x428f,
+ 0x78ec, 0xa084, 0x0003, 0x0040, 0x428f, 0x681c, 0xd0ac, 0x00c0,
+ 0x428d, 0x1078, 0x4319, 0x0078, 0x428f, 0x781b, 0x00f9, 0x0f7f,
+ 0x0e7f, 0x007c, 0x0c7e, 0x2001, 0x4e01, 0x2004, 0xd0ac, 0x00c0,
+ 0x430b, 0x6814, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+ 0xb28c, 0x0300, 0x0040, 0x42a8, 0xa0e0, 0x52c0, 0x0078, 0x42aa,
+ 0xa0e0, 0x5340, 0x6004, 0xa084, 0x000a, 0x00c0, 0x430b, 0x6108,
+ 0xa194, 0xff00, 0x0040, 0x430b, 0xa18c, 0x00ff, 0x2001, 0x000a,
+ 0xa106, 0x0040, 0x42d6, 0x2001, 0x000c, 0xa106, 0x0040, 0x42da,
+ 0x2001, 0x0012, 0xa106, 0x0040, 0x42de, 0x2001, 0x0014, 0xa106,
+ 0x0040, 0x42e2, 0x2001, 0x0019, 0xa106, 0x0040, 0x42e6, 0x2001,
+ 0x0032, 0xa106, 0x0040, 0x42ea, 0x0078, 0x42ee, 0x2009, 0x000c,
+ 0x0078, 0x42f0, 0x2009, 0x0012, 0x0078, 0x42f0, 0x2009, 0x0014,
+ 0x0078, 0x42f0, 0x2009, 0x0019, 0x0078, 0x42f0, 0x2009, 0x0020,
+ 0x0078, 0x42f0, 0x2009, 0x003f, 0x0078, 0x42f0, 0x2011, 0x0000,
+ 0x2100, 0xa205, 0x600a, 0x6004, 0xa085, 0x0002, 0x6006, 0x2061,
+ 0x4e00, 0x6004, 0xd0bc, 0x0040, 0x430b, 0x6814, 0xd0fc, 0x00c0,
+ 0x4306, 0x60ea, 0x2061, 0x4e40, 0x0078, 0x4309, 0x60ee, 0x2061,
+ 0x4e80, 0x601f, 0x800f, 0x0c7f, 0x007c, 0x781b, 0x0079, 0x007c,
+ 0x781b, 0x0078, 0x007c, 0x781b, 0x0067, 0x007c, 0x781b, 0x0064,
+ 0x007c, 0x2009, 0x4e19, 0x210c, 0xa186, 0x0000, 0x0040, 0x432b,
+ 0xa186, 0x0001, 0x0040, 0x432e, 0x701f, 0x000b, 0x7067, 0x0001,
+ 0x781b, 0x0047, 0x007c, 0x781b, 0x00f0, 0x007c, 0x701f, 0x000a,
+ 0x007c, 0x2009, 0x4e19, 0x210c, 0xa186, 0x0000, 0x0040, 0x4346,
+ 0xa186, 0x0001, 0x0040, 0x4343, 0x701f, 0x000b, 0x7067, 0x0001,
+ 0x781b, 0x0047, 0x007c, 0x701f, 0x000a, 0x007c, 0x781b, 0x00ef,
+ 0x007c, 0x781b, 0x00f9, 0x007c, 0x781b, 0x00f8, 0x007c, 0x781b,
+ 0x00c9, 0x007c, 0x781b, 0x00c8, 0x007c, 0x6818, 0xd0fc, 0x0040,
+ 0x435b, 0x681b, 0x001d, 0x7067, 0x0001, 0x781b, 0x0047, 0x007c,
+ 0x7830, 0xa084, 0x00c0, 0x00c0, 0x4382, 0x7808, 0xc08c, 0x780a,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021, 0x00c0,
+ 0x437f, 0x2001, 0x4e05, 0x2004, 0xd0e4, 0x00c0, 0x437d, 0x7804,
+ 0xa084, 0xff1f, 0xa085, 0x00e0, 0x7806, 0xa006, 0x007c, 0x7808,
+ 0xc08d, 0x780a, 0x007c, 0x7808, 0xc08d, 0x780a, 0x007c, 0x7830,
+ 0xa084, 0x0040, 0x00c0, 0x4387, 0x2001, 0x4e04, 0x2004, 0xd0ec,
+ 0x0040, 0x4396, 0xb284, 0x0300, 0x0078, 0x4398, 0xb284, 0x0400,
+ 0x0040, 0x439e, 0x0098, 0x43a2, 0x0078, 0x43a0, 0x00a8, 0x43a2,
+ 0x78ac, 0x007c, 0x7808, 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021, 0x0040, 0x43c5, 0x007e,
+ 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f, 0x0040, 0x43bb, 0xb284,
+ 0x0300, 0x0078, 0x43bd, 0xb284, 0x0400, 0x0040, 0x43c3, 0x0098,
+ 0x43bf, 0x0078, 0x43c5, 0x00a8, 0x43c3, 0x78ac, 0x007e, 0x7808,
+ 0xa085, 0x0002, 0x780a, 0x007f, 0x007c, 0xa784, 0x0001, 0x00c0,
+ 0x3770, 0xa784, 0x0070, 0x0040, 0x43dd, 0x0c7e, 0x2d60, 0x2f68,
+ 0x1078, 0x28df, 0x2d78, 0x2c68, 0x0c7f, 0xa784, 0x0008, 0x0040,
+ 0x43ea, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x3770,
+ 0x0078, 0x430d, 0xa784, 0x0004, 0x0040, 0x4419, 0x78b8, 0xa084,
+ 0x4001, 0x0040, 0x4419, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003,
+ 0x0040, 0x3770, 0x78e4, 0xa084, 0x0007, 0xa086, 0x0001, 0x00c0,
+ 0x4419, 0x78c0, 0xa685, 0x4800, 0x2030, 0x7e5a, 0x781b, 0x00f9,
+ 0x007c, 0x784b, 0x0008, 0x6818, 0xd0fc, 0x0040, 0x4416, 0x681b,
+ 0x0015, 0xd6f4, 0x0040, 0x4416, 0x681b, 0x0007, 0x1078, 0x4319,
+ 0x007c, 0x681b, 0x0003, 0x7858, 0xa084, 0x3f00, 0x681e, 0x682f,
+ 0x0000, 0x6833, 0x0000, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003,
+ 0x0040, 0x3066, 0x007e, 0x2001, 0x4e04, 0x2004, 0xd0ec, 0x007f,
+ 0x0040, 0x4436, 0xb284, 0x0300, 0x0078, 0x4438, 0xb284, 0x0400,
+ 0x0040, 0x443e, 0x0018, 0x29c1, 0x0078, 0x4440, 0x0028, 0x29c1,
+ 0x0078, 0x40bc, 0x6b14, 0x8307, 0xa084, 0x000f, 0x8003, 0x8003,
+ 0x8003, 0xd3fc, 0x0040, 0x4450, 0xa080, 0x5340, 0x0078, 0x4452,
+ 0xa080, 0x52c0, 0x2060, 0x2048, 0x705a, 0x2a60, 0x007c, 0x0020,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0062, 0x0009, 0x0014, 0x0014, 0x9848, 0x0014, 0x0014,
+ 0x9914, 0x98fd, 0x0014, 0x0014, 0x0080, 0x00ff, 0x0100, 0x0402,
+ 0x2008, 0xf880, 0x0018, 0xa20a, 0x0014, 0x300b, 0xa20c, 0x0014,
+ 0x2500, 0x0013, 0x2500, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
+ 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
+ 0x0010, 0xa200, 0x3806, 0x7102, 0x805f, 0x9481, 0x8839, 0x20c4,
+ 0x0864, 0xa856, 0x3008, 0x28c1, 0x9d1b, 0xa201, 0x300c, 0x2847,
+ 0x8161, 0x846a, 0x8000, 0x84a4, 0x1856, 0x883a, 0xa808, 0x28e2,
+ 0x9ccb, 0xa8f3, 0x0864, 0xa844, 0x300c, 0xa801, 0x3008, 0x28e1,
+ 0x9ccb, 0x2021, 0xa81d, 0xa205, 0x870c, 0xd8de, 0x64a0, 0x6de0,
+ 0x6fc0, 0x63a4, 0x6c80, 0x0212, 0xa205, 0x883d, 0x7942, 0x8020,
+ 0xa4a1, 0x882b, 0x1814, 0x883b, 0x80df, 0x94a1, 0x7027, 0x85f2,
+ 0xa737, 0xa532, 0xf003, 0x8576, 0x8677, 0xa816, 0x883e, 0xa814,
+ 0x2001, 0xa812, 0xa204, 0x64c0, 0x6de0, 0x67a0, 0x6fc0, 0x7942,
+ 0x8020, 0xa4a1, 0x1814, 0x80df, 0x94a1, 0x883b, 0x7023, 0x8576,
+ 0x8677, 0xa802, 0x7861, 0x883e, 0x206b, 0x28c1, 0x9d1b, 0x2044,
+ 0x2103, 0x20a2, 0x2081, 0xa8c3, 0xa207, 0x0904, 0xa20e, 0xa809,
+ 0xa203, 0x8000, 0x85a4, 0x1872, 0x879a, 0x883c, 0x1fe2, 0xf601,
+ 0xa208, 0x856e, 0x866f, 0x7161, 0x0014, 0x0704, 0x3008, 0x9ccb,
+ 0x0014, 0xa202, 0x8000, 0x85a4, 0x3009, 0x84a8, 0x19e2, 0xf844,
+ 0x856e, 0x883f, 0x08e6, 0xa8f5, 0xf861, 0xa8ea, 0xf801, 0x0014,
+ 0xf881, 0x0016, 0x85b2, 0x80f0, 0x9532, 0xfaa2, 0x1de2, 0x0014,
+ 0x8532, 0xf221, 0x0014, 0x1de2, 0x84a8, 0xd6e0, 0x1fe6, 0x0014,
+ 0x3008, 0x8000, 0x284a, 0x1011, 0xa8fc, 0x3008, 0x9d33, 0x8000,
+ 0xa000, 0x2802, 0x1011, 0xa8fd, 0x9d39, 0xa8bd, 0x3008, 0x9d33,
+ 0x283b, 0x1011, 0xa8fd, 0xa209, 0x7102, 0x805f, 0x9481, 0x0017,
+ 0x300c, 0xa209, 0x8000, 0x85a4, 0x1de2, 0xa209, 0xdac1, 0x0014,
+ 0x0210, 0xa801, 0x0014, 0x26e0, 0x873a, 0xfaa3, 0x19f2, 0x26e0,
+ 0x18f2, 0x0014, 0xa20b, 0x0014, 0xa20d, 0x3806, 0x0210, 0x9d25,
+ 0x0704, 0xa206, 0x6865, 0x817e, 0x842a, 0x1dc1, 0x8823, 0x0016,
+ 0x6042, 0x8008, 0xa8fa, 0x8000, 0x84a4, 0x8160, 0x842a, 0xf021,
+ 0x3008, 0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011, 0x20d4, 0x8822,
+ 0x0016, 0x7944, 0x8421, 0xa020, 0xa532, 0x84a1, 0x0016, 0x7944,
+ 0x8421, 0xa0df, 0x9532, 0x84a1, 0x0016, 0x0000, 0x127e, 0x70d4,
+ 0xa084, 0x4600, 0x8004, 0x2090, 0x7204, 0x7008, 0xc09c, 0xa205,
+ 0x00c0, 0x45a2, 0x720c, 0x82ff, 0x0040, 0x459d, 0x8aff, 0x00c0,
+ 0x45a2, 0x7200, 0xd284, 0x00c0, 0x45a2, 0x7003, 0x0008, 0x127f,
+ 0x2000, 0x007c, 0x7000, 0xa084, 0x0003, 0x7002, 0xc69c, 0xd084,
+ 0x0040, 0x45e5, 0x7108, 0x0005, 0x7008, 0xa106, 0x00c0, 0x45aa,
+ 0xa184, 0x0003, 0x0040, 0x4616, 0xa184, 0x01e0, 0x00c0, 0x4616,
+ 0xd1f4, 0x00c0, 0x45aa, 0xa184, 0x3000, 0xa086, 0x1000, 0x0040,
+ 0x45aa, 0x2011, 0x0180, 0x710c, 0x8211, 0x0040, 0x45cf, 0x7008,
+ 0xd0f4, 0x00c0, 0x45aa, 0x700c, 0xa106, 0x0040, 0x45c4, 0x7007,
+ 0x0012, 0x7108, 0x0005, 0x7008, 0xa106, 0x00c0, 0x45d1, 0xa184,
+ 0x0003, 0x0040, 0x4616, 0xd194, 0x0040, 0x45d1, 0xd1f4, 0x0040,
+ 0x4616, 0x7007, 0x0002, 0x0078, 0x45aa, 0x7108, 0xd1fc, 0x0040,
+ 0x45f0, 0x1078, 0x4769, 0x8aff, 0x0040, 0x458c, 0x0078, 0x45e5,
+ 0x700c, 0xa08c, 0x03ff, 0x0040, 0x461b, 0x7004, 0xd084, 0x0040,
+ 0x460d, 0x7014, 0xa005, 0x00c0, 0x4609, 0x7010, 0x7310, 0xa306,
+ 0x00c0, 0x45fd, 0x2300, 0xa005, 0x0040, 0x460d, 0xa102, 0x00c8,
+ 0x45e5, 0x7007, 0x0010, 0x0078, 0x4616, 0x8aff, 0x0040, 0x461b,
+ 0x1078, 0x4970, 0x00c0, 0x4610, 0x0040, 0x45e5, 0x1078, 0x46b4,
+ 0x127f, 0x2000, 0x007c, 0x7204, 0x7108, 0xc19c, 0x8103, 0x00c8,
+ 0x462a, 0x7007, 0x0002, 0x0078, 0x461b, 0x7003, 0x0008, 0x127f,
+ 0x2000, 0x007c, 0xa205, 0x00c0, 0x4616, 0x7003, 0x0008, 0x127f,
+ 0x2000, 0x007c, 0x6428, 0x84ff, 0x0040, 0x465e, 0x2c70, 0x7004,
+ 0xa0bc, 0x000f, 0xa7b8, 0x466e, 0x273c, 0x87fb, 0x00c0, 0x464c,
+ 0x0048, 0x4644, 0x1078, 0x296b, 0x609c, 0xa075, 0x0040, 0x465e,
+ 0x0078, 0x4637, 0x2039, 0x4663, 0x2704, 0xae68, 0x6808, 0xa630,
+ 0x680c, 0xa529, 0x8421, 0x0040, 0x465e, 0x8738, 0x2704, 0xa005,
+ 0x00c0, 0x464d, 0x709c, 0xa075, 0x00c0, 0x4637, 0x007c, 0x0000,
+ 0x0005, 0x0009, 0x000d, 0x0011, 0x0015, 0x0019, 0x001d, 0x0000,
+ 0x0003, 0x0009, 0x000f, 0x0015, 0x001b, 0x0000, 0x0000, 0x4663,
+ 0x4660, 0x0000, 0x0000, 0x8000, 0x0000, 0x4663, 0x0000, 0x466b,
+ 0x4668, 0x0000, 0x0000, 0x0000, 0x0000, 0x466b, 0x0000, 0x4666,
+ 0x4666, 0x0000, 0x0000, 0x8000, 0x0000, 0x4666, 0x0000, 0x466c,
+ 0x466c, 0x0000, 0x0000, 0x0000, 0x0000, 0x466c, 0x2079, 0x4e00,
+ 0x2071, 0x0010, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003, 0x0001,
+ 0x7810, 0xd0ec, 0x0040, 0x46a2, 0x2009, 0x0001, 0x2071, 0x0020,
+ 0x0078, 0x46a6, 0x2009, 0x0002, 0x2071, 0x0050, 0x7007, 0x000a,
+ 0x7007, 0x0002, 0x7003, 0x0000, 0x8109, 0x0040, 0x46b3, 0x2071,
+ 0x0020, 0x0078, 0x46a6, 0x007c, 0x7004, 0x8004, 0x00c8, 0x473d,
+ 0x7108, 0x7008, 0xa106, 0x00c0, 0x46b8, 0xa184, 0x01e0, 0x0040,
+ 0x46c5, 0x1078, 0x47ac, 0x0078, 0x4765, 0x7007, 0x0012, 0x2019,
+ 0x0000, 0x7108, 0x7008, 0xa106, 0x00c0, 0x46c9, 0xa184, 0x01e0,
+ 0x0040, 0x46d6, 0x1078, 0x47ac, 0x0078, 0x4765, 0x7810, 0xd0ec,
+ 0x0040, 0x46f0, 0x2001, 0x04fd, 0x2004, 0xa086, 0x0003, 0x00c0,
+ 0x46f4, 0xa184, 0x4000, 0x0040, 0x46f8, 0xa382, 0x0003, 0x00c8,
+ 0x46f8, 0xa184, 0x0004, 0x0040, 0x46c9, 0x8318, 0x0078, 0x46c9,
+ 0x7814, 0xd0ec, 0x00c0, 0x46f8, 0xa184, 0x4000, 0x00c0, 0x46c9,
+ 0xa19c, 0x300c, 0xa386, 0x2004, 0x0040, 0x4715, 0xa386, 0x0008,
+ 0x0040, 0x4720, 0x7004, 0xd084, 0x00c0, 0x4711, 0x7108, 0x7008,
+ 0xa106, 0x00c0, 0x4706, 0xa184, 0x0003, 0x0040, 0x4711, 0x0078,
+ 0x47ac, 0xa386, 0x200c, 0x00c0, 0x46c9, 0x7200, 0x8204, 0x0048,
+ 0x4720, 0x730c, 0xa384, 0x03ff, 0x0040, 0x4720, 0x1078, 0x296b,
+ 0x7108, 0x7008, 0xa106, 0x00c0, 0x4720, 0xa184, 0x01e0, 0x0040,
+ 0x472d, 0x1078, 0x47ac, 0x0078, 0x4765, 0x7007, 0x0012, 0x7000,
+ 0xd084, 0x00c0, 0x473d, 0x7310, 0x7014, 0xa305, 0x0040, 0x473d,
+ 0x710c, 0xa184, 0x03ff, 0x00c0, 0x46b4, 0x7108, 0x7008, 0xa106,
+ 0x00c0, 0x473d, 0xa184, 0x01e0, 0x0040, 0x474a, 0x1078, 0x47ac,
+ 0x0078, 0x4765, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004, 0xd09c,
+ 0x00c0, 0x474e, 0x7108, 0x7008, 0xa106, 0x00c0, 0x4752, 0xa184,
+ 0x01e0, 0x0040, 0x475f, 0x1078, 0x47ac, 0x0078, 0x4765, 0x7007,
+ 0x0012, 0x7108, 0x8103, 0x0048, 0x4752, 0x7003, 0x0008, 0x007c,
+ 0x7108, 0xa184, 0x01e0, 0x00c0, 0x47ac, 0x7108, 0xa184, 0x01e0,
+ 0x00c0, 0x47ac, 0xa184, 0x0007, 0x0079, 0x4776, 0x4780, 0x4790,
+ 0x477e, 0x4790, 0x477e, 0x47ee, 0x477e, 0x47ec, 0x1078, 0x296b,
+ 0x7004, 0xa084, 0x0010, 0xc08d, 0x7006, 0x8aff, 0x00c0, 0x478b,
+ 0x2049, 0x0000, 0x007c, 0x1078, 0x4970, 0x00c0, 0x478b, 0x007c,
+ 0x7004, 0xa084, 0x0010, 0xc08d, 0x7006, 0x7004, 0xd084, 0x00c0,
+ 0x47a4, 0x7108, 0x7008, 0xa106, 0x00c0, 0x4799, 0xa184, 0x0003,
+ 0x0040, 0x47a4, 0x0078, 0x47ac, 0x8aff, 0x0040, 0x47ab, 0x1078,
+ 0x4970, 0x00c0, 0x47a7, 0x007c, 0x7007, 0x0012, 0x7108, 0x00e0,
+ 0x47af, 0x2091, 0x6000, 0x00e0, 0x47b3, 0x2091, 0x6000, 0x7007,
+ 0x0012, 0x7007, 0x0008, 0x7004, 0xd09c, 0x00c0, 0x47bb, 0x7007,
+ 0x0012, 0x7108, 0xd1fc, 0x00c0, 0x47bf, 0x7003, 0x0000, 0x7000,
+ 0xa005, 0x00c0, 0x47d3, 0x7004, 0xa005, 0x00c0, 0x47d3, 0x700c,
+ 0xa005, 0x0040, 0x47d5, 0x0078, 0x47b7, 0x2049, 0x0000, 0xb284,
+ 0x0100, 0x0040, 0x47df, 0x2001, 0x0000, 0x0078, 0x47e1, 0x2001,
+ 0x0001, 0x1078, 0x4212, 0x681b, 0x0002, 0x2051, 0x0000, 0x007c,
+ 0x1078, 0x296b, 0x1078, 0x296b, 0x1078, 0x4836, 0x7210, 0x7114,
+ 0x700c, 0xa09c, 0x03ff, 0x2800, 0xa300, 0xa211, 0xa189, 0x0000,
+ 0x1078, 0x4836, 0x2704, 0x2c58, 0xac60, 0x6308, 0x2200, 0xa322,
+ 0x630c, 0x2100, 0xa31b, 0x2400, 0xa305, 0x0040, 0x4811, 0x00c8,
+ 0x4811, 0x8412, 0x8210, 0x830a, 0xa189, 0x0000, 0x2b60, 0x0078,
+ 0x47f8, 0x2b60, 0x8a07, 0x007e, 0x6004, 0xa084, 0x0008, 0x0040,
+ 0x481d, 0xa7ba, 0x4668, 0x0078, 0x481f, 0xa7ba, 0x4660, 0x007f,
+ 0xa73d, 0x2c00, 0x6886, 0x6f8a, 0x6c92, 0x6b8e, 0x7108, 0x7008,
+ 0xa106, 0x00c0, 0x4826, 0xa184, 0x01e0, 0x0040, 0x4831, 0x1078,
+ 0x47ac, 0x7007, 0x0012, 0x1078, 0x46b4, 0x007c, 0x8a50, 0x8739,
+ 0x2704, 0xa004, 0x00c0, 0x484a, 0x6000, 0xa064, 0x00c0, 0x4841,
+ 0x2d60, 0x6004, 0xa084, 0x000f, 0xa080, 0x467e, 0x203c, 0x87fb,
+ 0x1040, 0x296b, 0x007c, 0x127e, 0x0d7e, 0x70d4, 0xa084, 0x4600,
+ 0x8004, 0x2090, 0x0d7f, 0x6884, 0x2060, 0x6888, 0x6b8c, 0x6c90,
+ 0x8057, 0xaad4, 0x00ff, 0xa084, 0x00ff, 0x007e, 0x6804, 0xa084,
+ 0x0008, 0x007f, 0x0040, 0x4868, 0xa0b8, 0x4668, 0x0078, 0x486a,
+ 0xa0b8, 0x4660, 0xb284, 0x0100, 0x0040, 0x4871, 0x7e20, 0x0078,
+ 0x4872, 0x7e24, 0xa6b5, 0x000c, 0x681c, 0xd0b4, 0x0040, 0x4879,
+ 0xc685, 0x2400, 0xa305, 0x0040, 0x48a3, 0x2c58, 0x2704, 0x6104,
+ 0xac60, 0x6000, 0xa400, 0x701a, 0x6004, 0xa301, 0x701e, 0xa184,
+ 0x0008, 0x0040, 0x4893, 0x6010, 0xa081, 0x0000, 0x7022, 0x6014,
+ 0xa081, 0x0000, 0x7026, 0x6208, 0x2400, 0xa202, 0x7012, 0x620c,
+ 0x2300, 0xa203, 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078,
+ 0x499b, 0x0078, 0x48a5, 0x1078, 0x4970, 0x00c0, 0x48a3, 0x127f,
+ 0x2000, 0x007c, 0x127e, 0x0d7e, 0x70d4, 0xa084, 0x4600, 0x8004,
+ 0x2090, 0x0d7f, 0x7007, 0x0004, 0x7004, 0xd094, 0x00c0, 0x48b4,
+ 0x7003, 0x0008, 0x127f, 0x2000, 0x007c, 0x127e, 0x0d7e, 0x70d4,
+ 0xa084, 0x4600, 0x8004, 0x007e, 0x2090, 0x007f, 0x0d7f, 0x7e20,
+ 0xb284, 0x0100, 0x00c0, 0x48cd, 0x7e24, 0xa6b5, 0x000c, 0x681c,
+ 0xd0ac, 0x00c0, 0x48d8, 0xc685, 0x7003, 0x0000, 0x7007, 0x0004,
+ 0x6828, 0x2050, 0x2d60, 0x6004, 0xa0bc, 0x000f, 0xa7b8, 0x466e,
+ 0x273c, 0x87fb, 0x00c0, 0x48ee, 0x0048, 0x48e8, 0x1078, 0x296b,
+ 0x689c, 0xa065, 0x0040, 0x48f2, 0x0078, 0x48db, 0x1078, 0x4970,
+ 0x00c0, 0x48ee, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e, 0x017e,
+ 0x0d7e, 0x70d4, 0xa084, 0x4600, 0x8004, 0x007e, 0x2090, 0x007f,
+ 0x7e20, 0xb284, 0x0100, 0x00c0, 0x4906, 0x7e24, 0x0d7f, 0x037f,
+ 0x047f, 0xa6b5, 0x000c, 0x681c, 0xd0b4, 0x0040, 0x4914, 0xc685,
+ 0x7003, 0x0000, 0x7007, 0x0004, 0x2049, 0x48f5, 0x6828, 0xa055,
+ 0x0d7e, 0x0040, 0x496c, 0x2d70, 0x2e60, 0x7004, 0xa0bc, 0x000f,
+ 0xa7b8, 0x466e, 0x273c, 0x87fb, 0x00c0, 0x4931, 0x0048, 0x492a,
+ 0x1078, 0x296b, 0x709c, 0xa075, 0x2060, 0x0040, 0x496c, 0x0078,
+ 0x491d, 0x2704, 0xae68, 0x6808, 0xa422, 0x680c, 0xa31b, 0x0048,
+ 0x494a, 0x8a51, 0x00c0, 0x493e, 0x1078, 0x296b, 0x8738, 0x2704,
+ 0xa005, 0x00c0, 0x4932, 0x709c, 0xa075, 0x2060, 0x0040, 0x496c,
+ 0x0078, 0x491d, 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x6908,
+ 0x2400, 0xa122, 0x690c, 0x2300, 0xa11b, 0x00c8, 0x4959, 0x1078,
+ 0x296b, 0xb284, 0x0100, 0x0040, 0x4967, 0x2001, 0x4e04, 0x2004,
+ 0xd0ec, 0x00c0, 0x4967, 0x2071, 0x0050, 0x0078, 0x4969, 0x2071,
+ 0x0020, 0x0d7f, 0x0078, 0x4879, 0x0d7f, 0x127f, 0x2000, 0x007c,
+ 0x7008, 0x007e, 0xa084, 0x01e0, 0x007f, 0x0040, 0x4979, 0xa006,
+ 0x007c, 0xa084, 0x0003, 0xa086, 0x0003, 0x00c0, 0x4980, 0x007c,
+ 0x2704, 0xac78, 0x7800, 0x701a, 0x7804, 0x701e, 0x7808, 0x7012,
+ 0x780c, 0x7016, 0x6004, 0xa084, 0x0008, 0x0040, 0x4993, 0x7810,
+ 0x7022, 0x7814, 0x7026, 0x7602, 0x7004, 0xa084, 0x0010, 0xc085,
+ 0x7006, 0x2079, 0x4e00, 0x8a51, 0x0040, 0x49bf, 0x8738, 0x2704,
+ 0xa005, 0x00c0, 0x49b1, 0x609c, 0xa005, 0x0040, 0x49c0, 0x2060,
+ 0x6004, 0xa084, 0x000f, 0xa080, 0x466e, 0x203c, 0x87fb, 0x1040,
+ 0x296b, 0x7008, 0x007e, 0xa084, 0x01e0, 0x007f, 0x0040, 0x49bb,
+ 0xa006, 0x0078, 0x49c0, 0xa084, 0x0003, 0xa086, 0x0003, 0x007c,
+ 0x2051, 0x0000, 0x007c, 0x127e, 0x007e, 0x0d7e, 0x70d4, 0xa084,
+ 0x4600, 0x8004, 0x2090, 0x0d7f, 0x087f, 0x7108, 0xa184, 0x0003,
+ 0x00c0, 0x49d8, 0x6828, 0xa005, 0x0040, 0x49e8, 0x0078, 0x45a2,
+ 0x7108, 0xd1fc, 0x0040, 0x49e0, 0x1078, 0x4769, 0x0078, 0x49cd,
+ 0x7007, 0x0010, 0x7108, 0xd1fc, 0x0040, 0x49e2, 0x1078, 0x4769,
+ 0x7008, 0xa086, 0x0008, 0x00c0, 0x49cd, 0x7000, 0xa005, 0x00c0,
+ 0x49cd, 0x7003, 0x0000, 0x2049, 0x0000, 0x127f, 0x2000, 0x007c,
+ 0x127e, 0x147e, 0x137e, 0x157e, 0x0c7e, 0x0d7e, 0x70d4, 0xa084,
+ 0x4600, 0x8004, 0x2090, 0x0d7f, 0x2049, 0x49f8, 0xad80, 0x0011,
+ 0x20a0, 0xb284, 0x0100, 0x0040, 0x4a1b, 0x2001, 0x4e04, 0x2004,
+ 0xd0ec, 0x0040, 0x4a17, 0x2099, 0x0031, 0x0078, 0x4a1d, 0x2099,
+ 0x0032, 0x0078, 0x4a1d, 0x2099, 0x0031, 0x700c, 0xa084, 0x03ff,
+ 0x682a, 0x7007, 0x0008, 0x7007, 0x0002, 0x7003, 0x0001, 0x0040,
+ 0x4a2c, 0x8000, 0x80ac, 0x53a5, 0x700c, 0xa084, 0x03ff, 0x0040,
+ 0x4a38, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x4a33,
+ 0x0c7f, 0x2049, 0x0000, 0x7003, 0x0000, 0x157f, 0x137f, 0x147f,
+ 0x127f, 0x2000, 0x007c, 0x2091, 0x8000, 0x2091, 0x6000, 0x78ac,
+ 0xa005, 0x00c0, 0x4a5a, 0x7974, 0x70d0, 0xa106, 0x00c0, 0x4a5a,
+ 0x781c, 0xa005, 0x0040, 0x4a5a, 0x781f, 0x0000, 0x0068, 0x4a5a,
+ 0x2091, 0x4080, 0x7830, 0x8001, 0x7832, 0x00c0, 0x4ae2, 0x7834,
+ 0x7832, 0x7810, 0xd0ec, 0x00c0, 0x4adb, 0x2061, 0x73c0, 0x2069,
+ 0x4e80, 0xc7fd, 0x68d0, 0xa005, 0x0040, 0x4a74, 0x8001, 0x68d2,
+ 0x00c0, 0x4a74, 0x1078, 0x4cb0, 0x6800, 0xa084, 0x000f, 0x0040,
+ 0x4a89, 0xa086, 0x0001, 0x0040, 0x4a89, 0x6844, 0xa00d, 0x0040,
+ 0x4a89, 0x2104, 0xa005, 0x0040, 0x4a89, 0x8001, 0x200a, 0x0040,
+ 0x4c23, 0x6814, 0xa005, 0x0040, 0x4aae, 0x8001, 0x6816, 0x00c0,
+ 0x4aae, 0x68a7, 0x0001, 0x0f7e, 0xd7fc, 0x00c0, 0x4aa3, 0x7810,
+ 0xd0ec, 0x0040, 0x4a9f, 0x2079, 0x0100, 0x0078, 0x4aa5, 0x2079,
+ 0x0200, 0x0078, 0x4aa5, 0x2079, 0x0100, 0x1078, 0x4383, 0x0f7f,
+ 0x6864, 0xa005, 0x0040, 0x4aae, 0x1078, 0x2628, 0x6880, 0xa005,
+ 0x0040, 0x4abb, 0x8001, 0x6882, 0x00c0, 0x4abb, 0x6867, 0x0000,
+ 0x68d4, 0xc0dd, 0x68d6, 0x68d4, 0xd0fc, 0x0040, 0x4ad8, 0xc0fc,
+ 0x68d6, 0x20a9, 0x0200, 0x6034, 0xa005, 0x0040, 0x4ad4, 0x8001,
+ 0x6036, 0x68d4, 0xc0fd, 0x68d6, 0x00c0, 0x4ad4, 0x6010, 0xa005,
+ 0x0040, 0x4ad4, 0x1078, 0x2628, 0xace0, 0x0010, 0x00f0, 0x4ac3,
+ 0xd7fc, 0x0040, 0x4ae2, 0x2061, 0x53c0, 0x2069, 0x4e40, 0xc7fc,
+ 0x0078, 0x4a6a, 0x1078, 0x4b1e, 0x7838, 0x8001, 0x783a, 0x00c0,
+ 0x4b04, 0x783c, 0x783a, 0x2061, 0x53c0, 0x2069, 0x4e40, 0xc7fc,
+ 0x680c, 0xa005, 0x0040, 0x4af6, 0x1078, 0x4b88, 0xd7fc, 0x00c0,
+ 0x4b04, 0x7810, 0xd0ec, 0x00c0, 0x4b04, 0x2061, 0x73c0, 0x2069,
+ 0x4e80, 0xc7fd, 0x0078, 0x4af0, 0x7814, 0xd0e4, 0x00c0, 0x4b08,
+ 0x7810, 0xd0cc, 0x0040, 0x4b1b, 0xd0ac, 0x00c0, 0x4b14, 0xd0a4,
+ 0x0040, 0x4b1b, 0xc0ad, 0x7812, 0x2091, 0x8001, 0x0068, 0x4b1a,
+ 0x1078, 0x2395, 0x007c, 0x2091, 0x8001, 0x007c, 0x7840, 0x8001,
+ 0x7842, 0x00c0, 0x4b87, 0x7844, 0x7842, 0x2069, 0x4e40, 0xc7fc,
+ 0x7810, 0x2079, 0x0200, 0xd0ec, 0x0040, 0x4b30, 0x2079, 0x0100,
+ 0x68d8, 0xa005, 0x0040, 0x4b3c, 0x7de0, 0xa504, 0x00c0, 0x4b3c,
+ 0x68da, 0x68d4, 0xc0bc, 0x68d6, 0x2079, 0x4e00, 0x6810, 0xa005,
+ 0x00c0, 0x4b44, 0x2001, 0x0101, 0x8001, 0x6812, 0xd7fc, 0x0040,
+ 0x4b4d, 0xa080, 0x94d0, 0x0078, 0x4b4f, 0xa080, 0x93c0, 0x2040,
+ 0x2004, 0xa065, 0x0040, 0x4b79, 0x6024, 0xa005, 0x0040, 0x4b75,
+ 0x8001, 0x6026, 0x00c0, 0x4b75, 0x6800, 0xa005, 0x0040, 0x4b68,
+ 0x684c, 0xac06, 0x00c0, 0x4b68, 0x1078, 0x4c23, 0x0078, 0x4b79,
+ 0x6864, 0xa005, 0x0040, 0x4b70, 0x6027, 0x0001, 0x0078, 0x4b75,
+ 0x1078, 0x4bd6, 0x2804, 0x0078, 0x4b51, 0x6000, 0x2c40, 0x0078,
+ 0x4b51, 0xd7fc, 0x00c0, 0x4b87, 0x7810, 0xd0ec, 0x00c0, 0x4b87,
+ 0x2069, 0x4e80, 0xc7fd, 0x2079, 0x0100, 0x0078, 0x4b30, 0x007c,
+ 0x2009, 0x0000, 0x20a9, 0x0200, 0x6008, 0xd09c, 0x0040, 0x4bc2,
+ 0x6024, 0xa005, 0x0040, 0x4b98, 0x8001, 0x6026, 0x0078, 0x4bc0,
+ 0x6008, 0xc09c, 0xd084, 0x00c0, 0x4ba0, 0xd0ac, 0x0040, 0x4bba,
+ 0x600a, 0x6004, 0xa005, 0x0040, 0x4bc2, 0x0d7e, 0x0c7e, 0x017e,
+ 0x2068, 0x6010, 0x8001, 0x6012, 0x1078, 0x3dd0, 0x2d00, 0x2c68,
+ 0x2060, 0x1078, 0x1e5b, 0x1078, 0x201d, 0x017f, 0x0c7f, 0x0d7f,
+ 0x0078, 0x4bc2, 0xc0bd, 0x600a, 0xa18d, 0x0001, 0x0078, 0x4bc2,
+ 0xa18d, 0x0100, 0xace0, 0x0010, 0x00f0, 0x4b8c, 0xa184, 0x0001,
+ 0x0040, 0x4bd1, 0xa18c, 0xfffe, 0x690e, 0x1078, 0x2628, 0x0078,
+ 0x4bd2, 0x690e, 0x007c, 0x00c0, 0x4bd2, 0x786c, 0x2c00, 0x687e,
+ 0x6714, 0x6f76, 0x6017, 0x0000, 0x602b, 0x0000, 0x601b, 0x0006,
+ 0x60b4, 0xa084, 0x3f00, 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085,
+ 0x0060, 0x6022, 0x6000, 0x2042, 0x1078, 0x1de4, 0x6818, 0xa005,
+ 0x0040, 0x4bf4, 0x8001, 0x681a, 0x6808, 0xc0a4, 0x680a, 0x6810,
+ 0x7908, 0x8109, 0x790a, 0x8001, 0x00d0, 0x4c00, 0x1078, 0x296b,
+ 0x6812, 0x00c0, 0x4c06, 0x7910, 0xc1a5, 0x7912, 0x602f, 0x0000,
+ 0x6033, 0x0000, 0x2c68, 0x1078, 0x202c, 0xd7fc, 0x00c0, 0x4c14,
+ 0x2069, 0x4e40, 0x0078, 0x4c16, 0x2069, 0x4e80, 0x6910, 0xa184,
+ 0x0100, 0x2001, 0x0006, 0x00c0, 0x4c20, 0x697a, 0x2001, 0x0004,
+ 0x1078, 0x261c, 0x007c, 0x0d7e, 0x694c, 0x2160, 0xd7fc, 0x00c0,
+ 0x4c35, 0x7810, 0xd0ec, 0x0040, 0x4c31, 0x2069, 0x0100, 0x0078,
+ 0x4c37, 0x2069, 0x0200, 0x0078, 0x4c37, 0x2069, 0x0100, 0x1078,
+ 0x28df, 0x601b, 0x0006, 0x6858, 0xa084, 0x3f00, 0x601e, 0x6020,
+ 0xa084, 0x00ff, 0xa085, 0x0048, 0x6022, 0x602f, 0x0000, 0x6033,
+ 0x0000, 0x6808, 0xa084, 0xfffd, 0x680a, 0x6830, 0xd0b4, 0x0040,
+ 0x4c69, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848, 0xd094, 0x0040,
+ 0x4c5b, 0x00f0, 0x4c55, 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848,
+ 0xd084, 0x0040, 0x4c65, 0x00f0, 0x4c5f, 0x20a9, 0x00fa, 0x00f0,
+ 0x4c67, 0x681b, 0x0047, 0x0d7f, 0x6867, 0x0007, 0x007c, 0x2079,
+ 0x4e00, 0x1078, 0x4ca3, 0x1078, 0x4c89, 0x1078, 0x4c96, 0x2009,
+ 0x0002, 0x2069, 0x4e80, 0x680f, 0x0000, 0x6813, 0x0000, 0x6817,
+ 0x0000, 0x8109, 0x0040, 0x4c88, 0x2069, 0x4e40, 0x0078, 0x4c7b,
+ 0x007c, 0x7810, 0xd0ec, 0x0040, 0x4c91, 0x2019, 0x00cc, 0x0078,
+ 0x4c93, 0x2019, 0x007b, 0x7b3a, 0x7b3e, 0x007c, 0x7814, 0xd0e4,
+ 0x00c0, 0x4c9e, 0x2019, 0x0040, 0x0078, 0x4ca0, 0x2019, 0x0026,
+ 0x7b42, 0x7b46, 0x007c, 0x7814, 0xd0e4, 0x00c0, 0x4cab, 0x2019,
+ 0x3f94, 0x0078, 0x4cad, 0x2019, 0x2624, 0x7b32, 0x7b36, 0x007c,
+ 0x6a50, 0xa285, 0x0000, 0x0040, 0x4cdc, 0x6954, 0x6bc0, 0xa300,
+ 0x0c7e, 0x2164, 0x6304, 0x83ff, 0x00c0, 0x4cc8, 0x8211, 0x0040,
+ 0x4ccc, 0x8108, 0xa11a, 0x0048, 0x4cb9, 0x69c0, 0x0078, 0x4cb9,
+ 0x68d3, 0x000a, 0x0c7f, 0x007c, 0x6950, 0x6ac0, 0x2264, 0x602b,
+ 0x0000, 0x602f, 0x0000, 0x6008, 0xc0b5, 0x600a, 0x8210, 0x8109,
+ 0x00c0, 0x4cce, 0x6952, 0x0c7f, 0x007c, 0x00e0, 0x4cdd, 0x2091,
+ 0x6000, 0x00e0, 0x4ce1, 0x2091, 0x6000, 0x70ec, 0xd0dc, 0x00c0,
+ 0x4cee, 0xd0d4, 0x0040, 0x4d17, 0x0078, 0x4d1a, 0x2008, 0x7810,
+ 0xd0ec, 0x0040, 0x4d01, 0xd1c4, 0x00c0, 0x4d39, 0x7814, 0xc0c5,
+ 0x7816, 0x7810, 0xc0f5, 0x7812, 0xd0ec, 0x0040, 0x4d35, 0x0078,
+ 0x4d31, 0xae8e, 0x0100, 0x0040, 0x4d0e, 0x7814, 0xc0f5, 0xc0c5,
+ 0x7816, 0xd0d4, 0x00c0, 0x4d35, 0x0078, 0x4d31, 0x7814, 0xc0fd,
+ 0xc0c5, 0x7816, 0xd0d4, 0x00c0, 0x4d35, 0x0078, 0x4d31, 0xd0e4,
+ 0x0040, 0x4d37, 0x00e0, 0x4d1a, 0x2091, 0x6000, 0x2009, 0x000c,
+ 0x00e0, 0x4d20, 0x2091, 0x6000, 0x8109, 0x00c0, 0x4d20, 0x70e4,
+ 0xa084, 0x01ff, 0xa086, 0x01ff, 0x00c0, 0x4d31, 0x70ec, 0x0078,
+ 0x4cee, 0x7804, 0xd08c, 0x0040, 0x4d37, 0x681f, 0x000c, 0x70a0,
+ 0x70a2, 0x007c, 0x205b
+};
+#ifdef UNIQUE_FW_NAME
+static unsigned short fw1280ei_length01 = 0x3d3b;
+#else
+static unsigned short risc_code_length01 = 0x3d3b;
+#endif
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
new file mode 100644
index 000000000000..4ad280814990
--- /dev/null
+++ b/drivers/scsi/qla1280.c
@@ -0,0 +1,4863 @@
+/******************************************************************************
+* QLOGIC LINUX SOFTWARE
+*
+* QLogic QLA1280 (Ultra2) and QLA12160 (Ultra3) SCSI driver
+* Copyright (C) 2000 Qlogic Corporation (www.qlogic.com)
+* Copyright (C) 2001-2004 Jes Sorensen, Wild Open Source Inc.
+* Copyright (C) 2003-2004 Christoph Hellwig
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+******************************************************************************/
+#define QLA1280_VERSION "3.25"
+/*****************************************************************************
+ Revision History:
+ Rev 3.25.1, February 10, 2005 Christoph Hellwig
+ - use pci_map_single to map non-S/G requests
+ - remove qla1280_proc_info
+ Rev 3.25, September 28, 2004, Christoph Hellwig
+ - add support for ISP1020/1040
+ - don't include "scsi.h" anymore for 2.6.x
+ Rev 3.24.4 June 7, 2004 Christoph Hellwig
+ - restructure firmware loading, cleanup initialization code
+ - prepare support for ISP1020/1040 chips
+ Rev 3.24.3 January 19, 2004, Jes Sorensen
+ - Handle PCI DMA mask settings correctly
+ - Correct order of error handling in probe_one, free_irq should not
+ be called if request_irq failed
+ Rev 3.24.2 January 19, 2004, James Bottomley & Andrew Vasquez
+ - Big endian fixes (James)
+ - Remove bogus IOCB content on zero data transfer commands (Andrew)
+ Rev 3.24.1 January 5, 2004, Jes Sorensen
+ - Initialize completion queue to avoid OOPS on probe
+ - Handle interrupts during mailbox testing
+ Rev 3.24 November 17, 2003, Christoph Hellwig
+ - use struct list_head for completion queue
+ - avoid old Scsi_FOO typedefs
+ - cleanup 2.4 compat glue a bit
+ - use <scsi/scsi_*.h> headers on 2.6 instead of "scsi.h"
+ - make initialization for memory mapped vs port I/O more similar
+ - remove broken pci config space manipulation
+ - kill more cruft
+ - this is an almost perfect 2.6 scsi driver now! ;)
+ Rev 3.23.39 December 17, 2003, Jes Sorensen
+ - Delete completion queue from srb if mailbox command failed to
+ to avoid qla1280_done completeting qla1280_error_action's
+ obsolete context
+ - Reduce arguments for qla1280_done
+ Rev 3.23.38 October 18, 2003, Christoph Hellwig
+ - Convert to new-style hotplugable driver for 2.6
+ - Fix missing scsi_unregister/scsi_host_put on HBA removal
+ - Kill some more cruft
+ Rev 3.23.37 October 1, 2003, Jes Sorensen
+ - Make MMIO depend on CONFIG_X86_VISWS instead of yet another
+ random CONFIG option
+ - Clean up locking in probe path
+ Rev 3.23.36 October 1, 2003, Christoph Hellwig
+ - queuecommand only ever receives new commands - clear flags
+ - Reintegrate lost fixes from Linux 2.5
+ Rev 3.23.35 August 14, 2003, Jes Sorensen
+ - Build against 2.6
+ Rev 3.23.34 July 23, 2003, Jes Sorensen
+ - Remove pointless TRUE/FALSE macros
+ - Clean up vchan handling
+ Rev 3.23.33 July 3, 2003, Jes Sorensen
+ - Don't define register access macros before define determining MMIO.
+ This just happend to work out on ia64 but not elsewhere.
+ - Don't try and read from the card while it is in reset as
+ it won't respond and causes an MCA
+ Rev 3.23.32 June 23, 2003, Jes Sorensen
+ - Basic support for boot time arguments
+ Rev 3.23.31 June 8, 2003, Jes Sorensen
+ - Reduce boot time messages
+ Rev 3.23.30 June 6, 2003, Jes Sorensen
+ - Do not enable sync/wide/ppr before it has been determined
+ that the target device actually supports it
+ - Enable DMA arbitration for multi channel controllers
+ Rev 3.23.29 June 3, 2003, Jes Sorensen
+ - Port to 2.5.69
+ Rev 3.23.28 June 3, 2003, Jes Sorensen
+ - Eliminate duplicate marker commands on bus resets
+ - Handle outstanding commands appropriately on bus/device resets
+ Rev 3.23.27 May 28, 2003, Jes Sorensen
+ - Remove bogus input queue code, let the Linux SCSI layer do the work
+ - Clean up NVRAM handling, only read it once from the card
+ - Add a number of missing default nvram parameters
+ Rev 3.23.26 Beta May 28, 2003, Jes Sorensen
+ - Use completion queue for mailbox commands instead of busy wait
+ Rev 3.23.25 Beta May 27, 2003, James Bottomley
+ - Migrate to use new error handling code
+ Rev 3.23.24 Beta May 21, 2003, James Bottomley
+ - Big endian support
+ - Cleanup data direction code
+ Rev 3.23.23 Beta May 12, 2003, Jes Sorensen
+ - Switch to using MMIO instead of PIO
+ Rev 3.23.22 Beta April 15, 2003, Jes Sorensen
+ - Fix PCI parity problem with 12160 during reset.
+ Rev 3.23.21 Beta April 14, 2003, Jes Sorensen
+ - Use pci_map_page()/pci_unmap_page() instead of map_single version.
+ Rev 3.23.20 Beta April 9, 2003, Jes Sorensen
+ - Remove < 2.4.x support
+ - Introduce HOST_LOCK to make the spin lock changes portable.
+ - Remove a bunch of idiotic and unnecessary typedef's
+ - Kill all leftovers of target-mode support which never worked anyway
+ Rev 3.23.19 Beta April 11, 2002, Linus Torvalds
+ - Do qla1280_pci_config() before calling request_irq() and
+ request_region()
+ - Use pci_dma_hi32() to handle upper word of DMA addresses instead
+ of large shifts
+ - Hand correct arguments to free_irq() in case of failure
+ Rev 3.23.18 Beta April 11, 2002, Jes Sorensen
+ - Run source through Lindent and clean up the output
+ Rev 3.23.17 Beta April 11, 2002, Jes Sorensen
+ - Update SCSI firmware to qla1280 v8.15.00 and qla12160 v10.04.32
+ Rev 3.23.16 Beta March 19, 2002, Jes Sorensen
+ - Rely on mailbox commands generating interrupts - do not
+ run qla1280_isr() from ql1280_mailbox_command()
+ - Remove device_reg_t
+ - Integrate ql12160_set_target_parameters() with 1280 version
+ - Make qla1280_setup() non static
+ - Do not call qla1280_check_for_dead_scsi_bus() on every I/O request
+ sent to the card - this command pauses the firmare!!!
+ Rev 3.23.15 Beta March 19, 2002, Jes Sorensen
+ - Clean up qla1280.h - remove obsolete QL_DEBUG_LEVEL_x definitions
+ - Remove a pile of pointless and confusing (srb_t **) and
+ (scsi_lu_t *) typecasts
+ - Explicit mark that we do not use the new error handling (for now)
+ - Remove scsi_qla_host_t and use 'struct' instead
+ - Remove in_abort, watchdog_enabled, dpc, dpc_sched, bios_enabled,
+ pci_64bit_slot flags which weren't used for anything anyway
+ - Grab host->host_lock while calling qla1280_isr() from abort()
+ - Use spin_lock()/spin_unlock() in qla1280_intr_handler() - we
+ do not need to save/restore flags in the interrupt handler
+ - Enable interrupts early (before any mailbox access) in preparation
+ for cleaning up the mailbox handling
+ Rev 3.23.14 Beta March 14, 2002, Jes Sorensen
+ - Further cleanups. Remove all trace of QL_DEBUG_LEVEL_x and replace
+ it with proper use of dprintk().
+ - Make qla1280_print_scsi_cmd() and qla1280_dump_buffer() both take
+ a debug level argument to determine if data is to be printed
+ - Add KERN_* info to printk()
+ Rev 3.23.13 Beta March 14, 2002, Jes Sorensen
+ - Significant cosmetic cleanups
+ - Change debug code to use dprintk() and remove #if mess
+ Rev 3.23.12 Beta March 13, 2002, Jes Sorensen
+ - More cosmetic cleanups, fix places treating return as function
+ - use cpu_relax() in qla1280_debounce_register()
+ Rev 3.23.11 Beta March 13, 2002, Jes Sorensen
+ - Make it compile under 2.5.5
+ Rev 3.23.10 Beta October 1, 2001, Jes Sorensen
+ - Do no typecast short * to long * in QL1280BoardTbl, this
+ broke miserably on big endian boxes
+ Rev 3.23.9 Beta September 30, 2001, Jes Sorensen
+ - Remove pre 2.2 hack for checking for reentrance in interrupt handler
+ - Make data types used to receive from SCSI_{BUS,TCN,LUN}_32
+ unsigned int to match the types from struct scsi_cmnd
+ Rev 3.23.8 Beta September 29, 2001, Jes Sorensen
+ - Remove bogus timer_t typedef from qla1280.h
+ - Remove obsolete pre 2.2 PCI setup code, use proper #define's
+ for PCI_ values, call pci_set_master()
+ - Fix memleak of qla1280_buffer on module unload
+ - Only compile module parsing code #ifdef MODULE - should be
+ changed to use individual MODULE_PARM's later
+ - Remove dummy_buffer that was never modified nor printed
+ - ENTER()/LEAVE() are noops unless QL_DEBUG_LEVEL_3, hence remove
+ #ifdef QL_DEBUG_LEVEL_3/#endif around ENTER()/LEAVE() calls
+ - Remove \r from print statements, this is Linux, not DOS
+ - Remove obsolete QLA1280_{SCSILU,INTR,RING}_{LOCK,UNLOCK}
+ dummy macros
+ - Remove C++ compile hack in header file as Linux driver are not
+ supposed to be compiled as C++
+ - Kill MS_64BITS macro as it makes the code more readable
+ - Remove unnecessary flags.in_interrupts bit
+ Rev 3.23.7 Beta August 20, 2001, Jes Sorensen
+ - Dont' check for set flags on q->q_flag one by one in qla1280_next()
+ - Check whether the interrupt was generated by the QLA1280 before
+ doing any processing
+ - qla1280_status_entry(): Only zero out part of sense_buffer that
+ is not being copied into
+ - Remove more superflouous typecasts
+ - qla1280_32bit_start_scsi() replace home-brew memcpy() with memcpy()
+ Rev 3.23.6 Beta August 20, 2001, Tony Luck, Intel
+ - Don't walk the entire list in qla1280_putq_t() just to directly
+ grab the pointer to the last element afterwards
+ Rev 3.23.5 Beta August 9, 2001, Jes Sorensen
+ - Don't use SA_INTERRUPT, it's use is deprecated for this kinda driver
+ Rev 3.23.4 Beta August 8, 2001, Jes Sorensen
+ - Set dev->max_sectors to 1024
+ Rev 3.23.3 Beta August 6, 2001, Jes Sorensen
+ - Provide compat macros for pci_enable_device(), pci_find_subsys()
+ and scsi_set_pci_device()
+ - Call scsi_set_pci_device() for all devices
+ - Reduce size of kernel version dependent device probe code
+ - Move duplicate probe/init code to separate function
+ - Handle error if qla1280_mem_alloc() fails
+ - Kill OFFSET() macro and use Linux's PCI definitions instead
+ - Kill private structure defining PCI config space (struct config_reg)
+ - Only allocate I/O port region if not in MMIO mode
+ - Remove duplicate (unused) sanity check of sife of srb_t
+ Rev 3.23.2 Beta August 6, 2001, Jes Sorensen
+ - Change home-brew memset() implementations to use memset()
+ - Remove all references to COMTRACE() - accessing a PC's COM2 serial
+ port directly is not legal under Linux.
+ Rev 3.23.1 Beta April 24, 2001, Jes Sorensen
+ - Remove pre 2.2 kernel support
+ - clean up 64 bit DMA setting to use 2.4 API (provide backwards compat)
+ - Fix MMIO access to use readl/writel instead of directly
+ dereferencing pointers
+ - Nuke MSDOS debugging code
+ - Change true/false data types to int from uint8_t
+ - Use int for counters instead of uint8_t etc.
+ - Clean up size & byte order conversion macro usage
+ Rev 3.23 Beta January 11, 2001 BN Qlogic
+ - Added check of device_id when handling non
+ QLA12160s during detect().
+ Rev 3.22 Beta January 5, 2001 BN Qlogic
+ - Changed queue_task() to schedule_task()
+ for kernels 2.4.0 and higher.
+ Note: 2.4.0-testxx kernels released prior to
+ the actual 2.4.0 kernel release on January 2001
+ will get compile/link errors with schedule_task().
+ Please update your kernel to released 2.4.0 level,
+ or comment lines in this file flagged with 3.22
+ to resolve compile/link error of schedule_task().
+ - Added -DCONFIG_SMP in addition to -D__SMP__
+ in Makefile for 2.4.0 builds of driver as module.
+ Rev 3.21 Beta January 4, 2001 BN Qlogic
+ - Changed criteria of 64/32 Bit mode of HBA
+ operation according to BITS_PER_LONG rather
+ than HBA's NVRAM setting of >4Gig memory bit;
+ so that the HBA auto-configures without the need
+ to setup each system individually.
+ Rev 3.20 Beta December 5, 2000 BN Qlogic
+ - Added priority handling to IA-64 onboard SCSI
+ ISP12160 chip for kernels greater than 2.3.18.
+ - Added irqrestore for qla1280_intr_handler.
+ - Enabled /proc/scsi/qla1280 interface.
+ - Clear /proc/scsi/qla1280 counters in detect().
+ Rev 3.19 Beta October 13, 2000 BN Qlogic
+ - Declare driver_template for new kernel
+ (2.4.0 and greater) scsi initialization scheme.
+ - Update /proc/scsi entry for 2.3.18 kernels and
+ above as qla1280
+ Rev 3.18 Beta October 10, 2000 BN Qlogic
+ - Changed scan order of adapters to map
+ the QLA12160 followed by the QLA1280.
+ Rev 3.17 Beta September 18, 2000 BN Qlogic
+ - Removed warnings for 32 bit 2.4.x compiles
+ - Corrected declared size for request and response
+ DMA addresses that are kept in each ha
+ Rev. 3.16 Beta August 25, 2000 BN Qlogic
+ - Corrected 64 bit addressing issue on IA-64
+ where the upper 32 bits were not properly
+ passed to the RISC engine.
+ Rev. 3.15 Beta August 22, 2000 BN Qlogic
+ - Modified qla1280_setup_chip to properly load
+ ISP firmware for greater that 4 Gig memory on IA-64
+ Rev. 3.14 Beta August 16, 2000 BN Qlogic
+ - Added setting of dma_mask to full 64 bit
+ if flags.enable_64bit_addressing is set in NVRAM
+ Rev. 3.13 Beta August 16, 2000 BN Qlogic
+ - Use new PCI DMA mapping APIs for 2.4.x kernel
+ Rev. 3.12 July 18, 2000 Redhat & BN Qlogic
+ - Added check of pci_enable_device to detect() for 2.3.x
+ - Use pci_resource_start() instead of
+ pdev->resource[0].start in detect() for 2.3.x
+ - Updated driver version
+ Rev. 3.11 July 14, 2000 BN Qlogic
+ - Updated SCSI Firmware to following versions:
+ qla1x80: 8.13.08
+ qla1x160: 10.04.08
+ - Updated driver version to 3.11
+ Rev. 3.10 June 23, 2000 BN Qlogic
+ - Added filtering of AMI SubSys Vendor ID devices
+ Rev. 3.9
+ - DEBUG_QLA1280 undefined and new version BN Qlogic
+ Rev. 3.08b May 9, 2000 MD Dell
+ - Added logic to check against AMI subsystem vendor ID
+ Rev. 3.08 May 4, 2000 DG Qlogic
+ - Added logic to check for PCI subsystem ID.
+ Rev. 3.07 Apr 24, 2000 DG & BN Qlogic
+ - Updated SCSI Firmware to following versions:
+ qla12160: 10.01.19
+ qla1280: 8.09.00
+ Rev. 3.06 Apr 12, 2000 DG & BN Qlogic
+ - Internal revision; not released
+ Rev. 3.05 Mar 28, 2000 DG & BN Qlogic
+ - Edit correction for virt_to_bus and PROC.
+ Rev. 3.04 Mar 28, 2000 DG & BN Qlogic
+ - Merge changes from ia64 port.
+ Rev. 3.03 Mar 28, 2000 BN Qlogic
+ - Increase version to reflect new code drop with compile fix
+ of issue with inclusion of linux/spinlock for 2.3 kernels
+ Rev. 3.02 Mar 15, 2000 BN Qlogic
+ - Merge qla1280_proc_info from 2.10 code base
+ Rev. 3.01 Feb 10, 2000 BN Qlogic
+ - Corrected code to compile on a 2.2.x kernel.
+ Rev. 3.00 Jan 17, 2000 DG Qlogic
+ - Added 64-bit support.
+ Rev. 2.07 Nov 9, 1999 DG Qlogic
+ - Added new routine to set target parameters for ISP12160.
+ Rev. 2.06 Sept 10, 1999 DG Qlogic
+ - Added support for ISP12160 Ultra 3 chip.
+ Rev. 2.03 August 3, 1999 Fred Lewis, Intel DuPont
+ - Modified code to remove errors generated when compiling with
+ Cygnus IA64 Compiler.
+ - Changed conversion of pointers to unsigned longs instead of integers.
+ - Changed type of I/O port variables from uint32_t to unsigned long.
+ - Modified OFFSET macro to work with 64-bit as well as 32-bit.
+ - Changed sprintf and printk format specifiers for pointers to %p.
+ - Changed some int to long type casts where needed in sprintf & printk.
+ - Added l modifiers to sprintf and printk format specifiers for longs.
+ - Removed unused local variables.
+ Rev. 1.20 June 8, 1999 DG, Qlogic
+ Changes to support RedHat release 6.0 (kernel 2.2.5).
+ - Added SCSI exclusive access lock (io_request_lock) when accessing
+ the adapter.
+ - Added changes for the new LINUX interface template. Some new error
+ handling routines have been added to the template, but for now we
+ will use the old ones.
+ - Initial Beta Release.
+*****************************************************************************/
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <asm/processor.h>
+#include <asm/types.h>
+#include <asm/system.h>
+
+#if LINUX_VERSION_CODE >= 0x020545
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#else
+#include <linux/blk.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "sd.h"
+#endif
+
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+#include <asm/sn/io.h>
+#endif
+
+#if LINUX_VERSION_CODE < 0x020407
+#error "Kernels older than 2.4.7 are no longer supported"
+#endif
+
+
+/*
+ * Compile time Options:
+ * 0 - Disable and 1 - Enable
+ */
+#define DEBUG_QLA1280_INTR 0
+#define DEBUG_PRINT_NVRAM 0
+#define DEBUG_QLA1280 0
+
+/*
+ * The SGI VISWS is broken and doesn't support MMIO ;-(
+ */
+#ifdef CONFIG_X86_VISWS
+#define MEMORY_MAPPED_IO 0
+#else
+#define MEMORY_MAPPED_IO 1
+#endif
+
+#define UNIQUE_FW_NAME
+#include "qla1280.h"
+#include "ql12160_fw.h" /* ISP RISC codes */
+#include "ql1280_fw.h"
+#include "ql1040_fw.h"
+
+
+/*
+ * Missing PCI ID's
+ */
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP1080
+#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080
+#endif
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP1240
+#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240
+#endif
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP1280
+#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280
+#endif
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP10160
+#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016
+#endif
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP12160
+#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216
+#endif
+
+#ifndef PCI_VENDOR_ID_AMI
+#define PCI_VENDOR_ID_AMI 0x101e
+#endif
+
+#ifndef BITS_PER_LONG
+#error "BITS_PER_LONG not defined!"
+#endif
+#if (BITS_PER_LONG == 64) || defined CONFIG_HIGHMEM
+#define QLA_64BIT_PTR 1
+#endif
+
+#ifdef QLA_64BIT_PTR
+#define pci_dma_hi32(a) ((a >> 16) >> 16)
+#else
+#define pci_dma_hi32(a) 0
+#endif
+#define pci_dma_lo32(a) (a & 0xffffffff)
+
+#define NVRAM_DELAY() udelay(500) /* 2 microseconds */
+
+#if LINUX_VERSION_CODE < 0x020500
+#define HOST_LOCK &io_request_lock
+#define irqreturn_t void
+#define IRQ_RETVAL(foo)
+#define MSG_ORDERED_TAG 1
+
+#define DMA_BIDIRECTIONAL SCSI_DATA_UNKNOWN
+#define DMA_TO_DEVICE SCSI_DATA_WRITE
+#define DMA_FROM_DEVICE SCSI_DATA_READ
+#define DMA_NONE SCSI_DATA_NONE
+
+#ifndef HAVE_SECTOR_T
+typedef unsigned int sector_t;
+#endif
+
+static inline void
+scsi_adjust_queue_depth(struct scsi_device *device, int tag, int depth)
+{
+ if (tag) {
+ device->tagged_queue = tag;
+ device->current_tag = 0;
+ }
+ device->queue_depth = depth;
+}
+static inline struct Scsi_Host *scsi_host_alloc(Scsi_Host_Template *t, size_t s)
+{
+ return scsi_register(t, s);
+}
+static inline void scsi_host_put(struct Scsi_Host *h)
+{
+ scsi_unregister(h);
+}
+#else
+#define HOST_LOCK ha->host->host_lock
+#endif
+#if LINUX_VERSION_CODE < 0x020600
+#define DEV_SIMPLE_TAGS(device) device->tagged_queue
+/*
+ * Hack around that qla1280_remove_one is called from
+ * qla1280_release in 2.4
+ */
+#undef __devexit
+#define __devexit
+#else
+#define DEV_SIMPLE_TAGS(device) device->simple_tags
+#endif
+#if defined(__ia64__) && !defined(ia64_platform_is)
+#define ia64_platform_is(foo) (!strcmp(x, platform_name))
+#endif
+
+
+#define IS_ISP1040(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1020)
+#define IS_ISP1x40(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1020 || \
+ ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1240)
+#define IS_ISP1x160(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP10160 || \
+ ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP12160)
+
+
+static int qla1280_probe_one(struct pci_dev *, const struct pci_device_id *);
+static void qla1280_remove_one(struct pci_dev *);
+
+/*
+ * QLogic Driver Support Function Prototypes.
+ */
+static void qla1280_done(struct scsi_qla_host *);
+#if LINUX_VERSION_CODE < 0x020545
+static void qla1280_get_target_options(struct scsi_cmnd *, struct scsi_qla_host *);
+#endif
+static int qla1280_get_token(char *);
+static int qla1280_setup(char *s) __init;
+
+/*
+ * QLogic ISP1280 Hardware Support Function Prototypes.
+ */
+static int qla1280_load_firmware(struct scsi_qla_host *);
+static int qla1280_init_rings(struct scsi_qla_host *);
+static int qla1280_nvram_config(struct scsi_qla_host *);
+static int qla1280_mailbox_command(struct scsi_qla_host *,
+ uint8_t, uint16_t *);
+static int qla1280_bus_reset(struct scsi_qla_host *, int);
+static int qla1280_device_reset(struct scsi_qla_host *, int, int);
+static int qla1280_abort_device(struct scsi_qla_host *, int, int, int);
+static int qla1280_abort_command(struct scsi_qla_host *, struct srb *, int);
+static int qla1280_abort_isp(struct scsi_qla_host *);
+#ifdef QLA_64BIT_PTR
+static int qla1280_64bit_start_scsi(struct scsi_qla_host *, struct srb *);
+#else
+static int qla1280_32bit_start_scsi(struct scsi_qla_host *, struct srb *);
+#endif
+static void qla1280_nv_write(struct scsi_qla_host *, uint16_t);
+static void qla1280_poll(struct scsi_qla_host *);
+static void qla1280_reset_adapter(struct scsi_qla_host *);
+static void qla1280_marker(struct scsi_qla_host *, int, int, int, u8);
+static void qla1280_isp_cmd(struct scsi_qla_host *);
+static void qla1280_isr(struct scsi_qla_host *, struct list_head *);
+static void qla1280_rst_aen(struct scsi_qla_host *);
+static void qla1280_status_entry(struct scsi_qla_host *, struct response *,
+ struct list_head *);
+static void qla1280_error_entry(struct scsi_qla_host *, struct response *,
+ struct list_head *);
+static uint16_t qla1280_get_nvram_word(struct scsi_qla_host *, uint32_t);
+static uint16_t qla1280_nvram_request(struct scsi_qla_host *, uint32_t);
+static uint16_t qla1280_debounce_register(volatile uint16_t __iomem *);
+static request_t *qla1280_req_pkt(struct scsi_qla_host *);
+static int qla1280_check_for_dead_scsi_bus(struct scsi_qla_host *,
+ unsigned int);
+static void qla1280_get_target_parameters(struct scsi_qla_host *,
+ struct scsi_device *);
+static int qla1280_set_target_parameters(struct scsi_qla_host *, int, int);
+
+
+static struct qla_driver_setup driver_setup;
+
+/*
+ * convert scsi data direction to request_t control flags
+ */
+static inline uint16_t
+qla1280_data_direction(struct scsi_cmnd *cmnd)
+{
+ switch(cmnd->sc_data_direction) {
+ case DMA_FROM_DEVICE:
+ return BIT_5;
+ case DMA_TO_DEVICE:
+ return BIT_6;
+ case DMA_BIDIRECTIONAL:
+ return BIT_5 | BIT_6;
+ /*
+ * We could BUG() on default here if one of the four cases aren't
+ * met, but then again if we receive something like that from the
+ * SCSI layer we have more serious problems. This shuts up GCC.
+ */
+ case DMA_NONE:
+ default:
+ return 0;
+ }
+}
+
+#if DEBUG_QLA1280
+static void __qla1280_print_scsi_cmd(struct scsi_cmnd * cmd);
+static void __qla1280_dump_buffer(char *, int);
+#endif
+
+
+/*
+ * insmod needs to find the variable and make it point to something
+ */
+#ifdef MODULE
+static char *qla1280;
+
+/* insmod qla1280 options=verbose" */
+module_param(qla1280, charp, 0);
+#else
+__setup("qla1280=", qla1280_setup);
+#endif
+
+
+/*
+ * We use the scsi_pointer structure that's included with each scsi_command
+ * to overlay our struct srb over it. qla1280_init() checks that a srb is not
+ * bigger than a scsi_pointer.
+ */
+
+#define CMD_SP(Cmnd) &Cmnd->SCp
+#define CMD_CDBLEN(Cmnd) Cmnd->cmd_len
+#define CMD_CDBP(Cmnd) Cmnd->cmnd
+#define CMD_SNSP(Cmnd) Cmnd->sense_buffer
+#define CMD_SNSLEN(Cmnd) sizeof(Cmnd->sense_buffer)
+#define CMD_RESULT(Cmnd) Cmnd->result
+#define CMD_HANDLE(Cmnd) Cmnd->host_scribble
+#if LINUX_VERSION_CODE < 0x020545
+#define CMD_REQUEST(Cmnd) Cmnd->request.cmd
+#else
+#define CMD_REQUEST(Cmnd) Cmnd->request->cmd
+#endif
+
+#define CMD_HOST(Cmnd) Cmnd->device->host
+#define SCSI_BUS_32(Cmnd) Cmnd->device->channel
+#define SCSI_TCN_32(Cmnd) Cmnd->device->id
+#define SCSI_LUN_32(Cmnd) Cmnd->device->lun
+
+
+/*****************************************/
+/* ISP Boards supported by this driver */
+/*****************************************/
+
+struct qla_boards {
+ unsigned char name[9]; /* Board ID String */
+ int numPorts; /* Number of SCSI ports */
+ unsigned short *fwcode; /* pointer to FW array */
+ unsigned short *fwlen; /* number of words in array */
+ unsigned short *fwstart; /* start address for F/W */
+ unsigned char *fwver; /* Ptr to F/W version array */
+};
+
+/* NOTE: the last argument in each entry is used to index ql1280_board_tbl */
+static struct pci_device_id qla1280_pci_tbl[] = {
+ {PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP12160,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#ifdef CONFIG_SCSI_QLOGIC_1280_1040
+ {PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1020,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
+#endif
+ {PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1080,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
+ {PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1240,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
+ {PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1280,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+ {PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP10160,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, qla1280_pci_tbl);
+
+static struct qla_boards ql1280_board_tbl[] = {
+ /* Name , Number of ports, FW details */
+ {"QLA12160", 2, &fw12160i_code01[0], &fw12160i_length01,
+ &fw12160i_addr01, &fw12160i_version_str[0]},
+ {"QLA1040", 1, &risc_code01[0], &risc_code_length01,
+ &risc_code_addr01, &firmware_version[0]},
+ {"QLA1080", 1, &fw1280ei_code01[0], &fw1280ei_length01,
+ &fw1280ei_addr01, &fw1280ei_version_str[0]},
+ {"QLA1240", 2, &fw1280ei_code01[0], &fw1280ei_length01,
+ &fw1280ei_addr01, &fw1280ei_version_str[0]},
+ {"QLA1280", 2, &fw1280ei_code01[0], &fw1280ei_length01,
+ &fw1280ei_addr01, &fw1280ei_version_str[0]},
+ {"QLA10160", 1, &fw12160i_code01[0], &fw12160i_length01,
+ &fw12160i_addr01, &fw12160i_version_str[0]},
+ {" ", 0}
+};
+
+static int qla1280_verbose = 1;
+
+#if DEBUG_QLA1280
+static int ql_debug_level = 1;
+#define dprintk(level, format, a...) \
+ do { if (ql_debug_level >= level) printk(KERN_ERR format, ##a); } while(0)
+#define qla1280_dump_buffer(level, buf, size) \
+ if (ql_debug_level >= level) __qla1280_dump_buffer(buf, size)
+#define qla1280_print_scsi_cmd(level, cmd) \
+ if (ql_debug_level >= level) __qla1280_print_scsi_cmd(cmd)
+#else
+#define ql_debug_level 0
+#define dprintk(level, format, a...) do{}while(0)
+#define qla1280_dump_buffer(a, b, c) do{}while(0)
+#define qla1280_print_scsi_cmd(a, b) do{}while(0)
+#endif
+
+#define ENTER(x) dprintk(3, "qla1280 : Entering %s()\n", x);
+#define LEAVE(x) dprintk(3, "qla1280 : Leaving %s()\n", x);
+#define ENTER_INTR(x) dprintk(4, "qla1280 : Entering %s()\n", x);
+#define LEAVE_INTR(x) dprintk(4, "qla1280 : Leaving %s()\n", x);
+
+
+static int qla1280_read_nvram(struct scsi_qla_host *ha)
+{
+ uint16_t *wptr;
+ uint8_t chksum;
+ int cnt, i;
+ struct nvram *nv;
+
+ ENTER("qla1280_read_nvram");
+
+ if (driver_setup.no_nvram)
+ return 1;
+
+ printk(KERN_INFO "scsi(%ld): Reading NVRAM\n", ha->host_no);
+
+ wptr = (uint16_t *)&ha->nvram;
+ nv = &ha->nvram;
+ chksum = 0;
+ for (cnt = 0; cnt < 3; cnt++) {
+ *wptr = qla1280_get_nvram_word(ha, cnt);
+ chksum += *wptr & 0xff;
+ chksum += (*wptr >> 8) & 0xff;
+ wptr++;
+ }
+
+ if (nv->id0 != 'I' || nv->id1 != 'S' ||
+ nv->id2 != 'P' || nv->id3 != ' ' || nv->version < 1) {
+ dprintk(2, "Invalid nvram ID or version!\n");
+ chksum = 1;
+ } else {
+ for (; cnt < sizeof(struct nvram); cnt++) {
+ *wptr = qla1280_get_nvram_word(ha, cnt);
+ chksum += *wptr & 0xff;
+ chksum += (*wptr >> 8) & 0xff;
+ wptr++;
+ }
+ }
+
+ dprintk(3, "qla1280_read_nvram: NVRAM Magic ID= %c %c %c %02x"
+ " version %i\n", nv->id0, nv->id1, nv->id2, nv->id3,
+ nv->version);
+
+
+ if (chksum) {
+ if (!driver_setup.no_nvram)
+ printk(KERN_WARNING "scsi(%ld): Unable to identify or "
+ "validate NVRAM checksum, using default "
+ "settings\n", ha->host_no);
+ ha->nvram_valid = 0;
+ } else
+ ha->nvram_valid = 1;
+
+ /* The firmware interface is, um, interesting, in that the
+ * actual firmware image on the chip is little endian, thus,
+ * the process of taking that image to the CPU would end up
+ * little endian. However, the firmare interface requires it
+ * to be read a word (two bytes) at a time.
+ *
+ * The net result of this would be that the word (and
+ * doubleword) quantites in the firmware would be correct, but
+ * the bytes would be pairwise reversed. Since most of the
+ * firmware quantites are, in fact, bytes, we do an extra
+ * le16_to_cpu() in the firmware read routine.
+ *
+ * The upshot of all this is that the bytes in the firmware
+ * are in the correct places, but the 16 and 32 bit quantites
+ * are still in little endian format. We fix that up below by
+ * doing extra reverses on them */
+ nv->isp_parameter = cpu_to_le16(nv->isp_parameter);
+ nv->firmware_feature.w = cpu_to_le16(nv->firmware_feature.w);
+ for(i = 0; i < MAX_BUSES; i++) {
+ nv->bus[i].selection_timeout = cpu_to_le16(nv->bus[i].selection_timeout);
+ nv->bus[i].max_queue_depth = cpu_to_le16(nv->bus[i].max_queue_depth);
+ }
+ dprintk(1, "qla1280_read_nvram: Completed Reading NVRAM\n");
+ LEAVE("qla1280_read_nvram");
+
+ return chksum;
+}
+
+/**************************************************************************
+ * qla1280_info
+ * Return a string describing the driver.
+ **************************************************************************/
+static const char *
+qla1280_info(struct Scsi_Host *host)
+{
+ static char qla1280_scsi_name_buffer[125];
+ char *bp;
+ struct scsi_qla_host *ha;
+ struct qla_boards *bdp;
+
+ bp = &qla1280_scsi_name_buffer[0];
+ ha = (struct scsi_qla_host *)host->hostdata;
+ bdp = &ql1280_board_tbl[ha->devnum];
+ memset(bp, 0, sizeof(qla1280_scsi_name_buffer));
+
+ sprintf (bp,
+ "QLogic %s PCI to SCSI Host Adapter\n"
+ " Firmware version: %2d.%02d.%02d, Driver version %s",
+ &bdp->name[0], bdp->fwver[0], bdp->fwver[1], bdp->fwver[2],
+ QLA1280_VERSION);
+ return bp;
+}
+
+/**************************************************************************
+ * qla1200_queuecommand
+ * Queue a command to the controller.
+ *
+ * Note:
+ * The mid-level driver tries to ensures that queuecommand never gets invoked
+ * concurrently with itself or the interrupt handler (although the
+ * interrupt handler may call this routine as part of request-completion
+ * handling). Unfortunely, it sometimes calls the scheduler in interrupt
+ * context which is a big NO! NO!.
+ **************************************************************************/
+static int
+qla1280_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct scsi_qla_host *ha = (struct scsi_qla_host *)host->hostdata;
+ struct srb *sp = (struct srb *)&cmd->SCp;
+ int status;
+
+ cmd->scsi_done = fn;
+ sp->cmd = cmd;
+ sp->flags = 0;
+
+ qla1280_print_scsi_cmd(5, cmd);
+
+#ifdef QLA_64BIT_PTR
+ /*
+ * Using 64 bit commands if the PCI bridge doesn't support it is a
+ * bit wasteful, however this should really only happen if one's
+ * PCI controller is completely broken, like the BCM1250. For
+ * sane hardware this is not an issue.
+ */
+ status = qla1280_64bit_start_scsi(ha, sp);
+#else
+ status = qla1280_32bit_start_scsi(ha, sp);
+#endif
+ return status;
+}
+
+enum action {
+ ABORT_COMMAND,
+ ABORT_DEVICE,
+ DEVICE_RESET,
+ BUS_RESET,
+ ADAPTER_RESET,
+ FAIL
+};
+
+/* timer action for error action processor */
+static void qla1280_error_wait_timeout(unsigned long __data)
+{
+ struct scsi_cmnd *cmd = (struct scsi_cmnd *)__data;
+ struct srb *sp = (struct srb *)CMD_SP(cmd);
+
+ complete(sp->wait);
+}
+
+static void qla1280_mailbox_timeout(unsigned long __data)
+{
+ struct scsi_qla_host *ha = (struct scsi_qla_host *)__data;
+ struct device_reg __iomem *reg;
+ reg = ha->iobase;
+
+ ha->mailbox_out[0] = RD_REG_WORD(&reg->mailbox0);
+ printk(KERN_ERR "scsi(%ld): mailbox timed out, mailbox0 %04x, "
+ "ictrl %04x, istatus %04x\n", ha->host_no, ha->mailbox_out[0],
+ RD_REG_WORD(&reg->ictrl), RD_REG_WORD(&reg->istatus));
+ complete(ha->mailbox_wait);
+}
+
+/**************************************************************************
+ * qla1200_error_action
+ * The function will attempt to perform a specified error action and
+ * wait for the results (or time out).
+ *
+ * Input:
+ * cmd = Linux SCSI command packet of the command that cause the
+ * bus reset.
+ * action = error action to take (see action_t)
+ *
+ * Returns:
+ * SUCCESS or FAILED
+ *
+ * Note:
+ * Resetting the bus always succeeds - is has to, otherwise the
+ * kernel will panic! Try a surgical technique - sending a BUS
+ * DEVICE RESET message - on the offending target before pulling
+ * the SCSI bus reset line.
+ **************************************************************************/
+static int
+qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
+{
+ struct scsi_qla_host *ha;
+ int bus, target, lun;
+ struct srb *sp;
+ uint16_t data;
+ unsigned char *handle;
+ int result, i;
+ DECLARE_COMPLETION(wait);
+ struct timer_list timer;
+
+ ha = (struct scsi_qla_host *)(CMD_HOST(cmd)->hostdata);
+
+ dprintk(4, "error_action %i, istatus 0x%04x\n", action,
+ RD_REG_WORD(&ha->iobase->istatus));
+
+ dprintk(4, "host_cmd 0x%04x, ictrl 0x%04x, jiffies %li\n",
+ RD_REG_WORD(&ha->iobase->host_cmd),
+ RD_REG_WORD(&ha->iobase->ictrl), jiffies);
+
+ ENTER("qla1280_error_action");
+ if (qla1280_verbose)
+ printk(KERN_INFO "scsi(%li): Resetting Cmnd=0x%p, "
+ "Handle=0x%p, action=0x%x\n",
+ ha->host_no, cmd, CMD_HANDLE(cmd), action);
+
+ if (cmd == NULL) {
+ printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL "
+ "si_Cmnd pointer, failing.\n");
+ LEAVE("qla1280_error_action");
+ return FAILED;
+ }
+
+ ha = (struct scsi_qla_host *)cmd->device->host->hostdata;
+ sp = (struct srb *)CMD_SP(cmd);
+ handle = CMD_HANDLE(cmd);
+
+ /* Check for pending interrupts. */
+ data = qla1280_debounce_register(&ha->iobase->istatus);
+ /*
+ * The io_request_lock is held when the reset handler is called, hence
+ * the interrupt handler cannot be running in parallel as it also
+ * grabs the lock. /Jes
+ */
+ if (data & RISC_INT)
+ qla1280_isr(ha, &ha->done_q);
+
+ /*
+ * Determine the suggested action that the mid-level driver wants
+ * us to perform.
+ */
+ if (handle == (unsigned char *)INVALID_HANDLE || handle == NULL) {
+ if(action == ABORT_COMMAND) {
+ /* we never got this command */
+ printk(KERN_INFO "qla1280: Aborting a NULL handle\n");
+ return SUCCESS; /* no action - we don't have command */
+ }
+ } else {
+ sp->wait = &wait;
+ }
+
+ bus = SCSI_BUS_32(cmd);
+ target = SCSI_TCN_32(cmd);
+ lun = SCSI_LUN_32(cmd);
+
+ /* Overloading result. Here it means the success or fail of the
+ * *issue* of the action. When we return from the routine, it must
+ * mean the actual success or fail of the action */
+ result = FAILED;
+ switch (action) {
+ case FAIL:
+ break;
+
+ case ABORT_COMMAND:
+ if ((sp->flags & SRB_ABORT_PENDING)) {
+ printk(KERN_WARNING
+ "scsi(): Command has a pending abort "
+ "message - ABORT_PENDING.\n");
+ /* This should technically be impossible since we
+ * now wait for abort completion */
+ break;
+ }
+
+ for (i = 0; i < MAX_OUTSTANDING_COMMANDS; i++) {
+ if (sp == ha->outstanding_cmds[i]) {
+ dprintk(1, "qla1280: RISC aborting command\n");
+ if (qla1280_abort_command(ha, sp, i) == 0)
+ result = SUCCESS;
+ else {
+ /*
+ * Since we don't know what might
+ * have happend to the command, it
+ * is unsafe to remove it from the
+ * device's queue at this point.
+ * Wait and let the escalation
+ * process take care of it.
+ */
+ printk(KERN_WARNING
+ "scsi(%li:%i:%i:%i): Unable"
+ " to abort command!\n",
+ ha->host_no, bus, target, lun);
+ }
+ }
+ }
+ break;
+
+ case ABORT_DEVICE:
+ ha->flags.in_reset = 1;
+ if (qla1280_verbose)
+ printk(KERN_INFO
+ "scsi(%ld:%d:%d:%d): Queueing abort device "
+ "command.\n", ha->host_no, bus, target, lun);
+ if (qla1280_abort_device(ha, bus, target, lun) == 0)
+ result = SUCCESS;
+ break;
+
+ case DEVICE_RESET:
+ if (qla1280_verbose)
+ printk(KERN_INFO
+ "scsi(%ld:%d:%d:%d): Queueing device reset "
+ "command.\n", ha->host_no, bus, target, lun);
+ ha->flags.in_reset = 1;
+ if (qla1280_device_reset(ha, bus, target) == 0)
+ result = SUCCESS;
+ break;
+
+ case BUS_RESET:
+ if (qla1280_verbose)
+ printk(KERN_INFO "qla1280(%ld:%d): Issuing BUS "
+ "DEVICE RESET\n", ha->host_no, bus);
+ ha->flags.in_reset = 1;
+ if (qla1280_bus_reset(ha, bus == 0))
+ result = SUCCESS;
+
+ break;
+
+ case ADAPTER_RESET:
+ default:
+ if (qla1280_verbose) {
+ printk(KERN_INFO
+ "scsi(%ld): Issued ADAPTER RESET\n",
+ ha->host_no);
+ printk(KERN_INFO "scsi(%ld): I/O processing will "
+ "continue automatically\n", ha->host_no);
+ }
+ ha->flags.reset_active = 1;
+ /*
+ * We restarted all of the commands automatically, so the
+ * mid-level code can expect completions momentitarily.
+ */
+ if (qla1280_abort_isp(ha) == 0)
+ result = SUCCESS;
+
+ ha->flags.reset_active = 0;
+ }
+
+ if (!list_empty(&ha->done_q))
+ qla1280_done(ha);
+ ha->flags.in_reset = 0;
+
+ /* If we didn't manage to issue the action, or we have no
+ * command to wait for, exit here */
+ if (result == FAILED || handle == NULL ||
+ handle == (unsigned char *)INVALID_HANDLE) {
+ /*
+ * Clear completion queue to avoid qla1280_done() trying
+ * to complete the command at a later stage after we
+ * have exited the current context
+ */
+ sp->wait = NULL;
+ goto leave;
+ }
+
+ /* set up a timer just in case we're really jammed */
+ init_timer(&timer);
+ timer.expires = jiffies + 4*HZ;
+ timer.data = (unsigned long)cmd;
+ timer.function = qla1280_error_wait_timeout;
+ add_timer(&timer);
+
+ /* wait for the action to complete (or the timer to expire) */
+ spin_unlock_irq(HOST_LOCK);
+ wait_for_completion(&wait);
+ del_timer_sync(&timer);
+ spin_lock_irq(HOST_LOCK);
+ sp->wait = NULL;
+
+ /* the only action we might get a fail for is abort */
+ if (action == ABORT_COMMAND) {
+ if(sp->flags & SRB_ABORTED)
+ result = SUCCESS;
+ else
+ result = FAILED;
+ }
+
+ leave:
+ dprintk(1, "RESET returning %d\n", result);
+
+ LEAVE("qla1280_error_action");
+ return result;
+}
+
+/**************************************************************************
+ * qla1280_abort
+ * Abort the specified SCSI command(s).
+ **************************************************************************/
+static int
+qla1280_eh_abort(struct scsi_cmnd * cmd)
+{
+ return qla1280_error_action(cmd, ABORT_COMMAND);
+}
+
+/**************************************************************************
+ * qla1280_device_reset
+ * Reset the specified SCSI device
+ **************************************************************************/
+static int
+qla1280_eh_device_reset(struct scsi_cmnd *cmd)
+{
+ return qla1280_error_action(cmd, DEVICE_RESET);
+}
+
+/**************************************************************************
+ * qla1280_bus_reset
+ * Reset the specified bus.
+ **************************************************************************/
+static int
+qla1280_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+ return qla1280_error_action(cmd, BUS_RESET);
+}
+
+/**************************************************************************
+ * qla1280_adapter_reset
+ * Reset the specified adapter (both channels)
+ **************************************************************************/
+static int
+qla1280_eh_adapter_reset(struct scsi_cmnd *cmd)
+{
+ return qla1280_error_action(cmd, ADAPTER_RESET);
+}
+
+static int
+qla1280_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ int heads, sectors, cylinders;
+
+ heads = 64;
+ sectors = 32;
+ cylinders = (unsigned long)capacity / (heads * sectors);
+ if (cylinders > 1024) {
+ heads = 255;
+ sectors = 63;
+ cylinders = (unsigned long)capacity / (heads * sectors);
+ /* if (cylinders > 1023)
+ cylinders = 1023; */
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < 0x020600
+static int
+qla1280_detect(Scsi_Host_Template *template)
+{
+ struct pci_device_id *id = &qla1280_pci_tbl[0];
+ struct pci_dev *pdev = NULL;
+ int num_hosts = 0;
+
+ if (sizeof(struct srb) > sizeof(Scsi_Pointer)) {
+ printk(KERN_WARNING
+ "qla1280: struct srb too big, aborting\n");
+ return 0;
+ }
+
+ if ((DMA_BIDIRECTIONAL != PCI_DMA_BIDIRECTIONAL) ||
+ (DMA_TO_DEVICE != PCI_DMA_TODEVICE) ||
+ (DMA_FROM_DEVICE != PCI_DMA_FROMDEVICE) ||
+ (DMA_NONE != PCI_DMA_NONE)) {
+ printk(KERN_WARNING
+ "qla1280: dma direction bits don't match\n");
+ return 0;
+ }
+
+#ifdef MODULE
+ /*
+ * If we are called as a module, the qla1280 pointer may not be null
+ * and it would point to our bootup string, just like on the lilo
+ * command line. IF not NULL, then process this config string with
+ * qla1280_setup
+ *
+ * Boot time Options
+ * To add options at boot time add a line to your lilo.conf file like:
+ * append="qla1280=verbose,max_tags:{{255,255,255,255},{255,255,255,255}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ */
+ if (qla1280)
+ qla1280_setup(qla1280);
+#endif
+
+ /* First Initialize QLA12160 on PCI Bus 1 Dev 2 */
+ while ((pdev = pci_find_device(id->vendor, id->device, pdev))) {
+ if (pdev->bus->number == 1 && PCI_SLOT(pdev->devfn) == 2) {
+ if (!qla1280_probe_one(pdev, id))
+ num_hosts++;
+ }
+ }
+
+ pdev = NULL;
+ /* Try and find each different type of adapter we support */
+ for (id = &qla1280_pci_tbl[0]; id->device; id++) {
+ while ((pdev = pci_find_device(id->vendor, id->device, pdev))) {
+ /*
+ * skip QLA12160 already initialized on
+ * PCI Bus 1 Dev 2 since we already initialized
+ * and presented it
+ */
+ if (id->device == PCI_DEVICE_ID_QLOGIC_ISP12160 &&
+ pdev->bus->number == 1 &&
+ PCI_SLOT(pdev->devfn) == 2)
+ continue;
+
+ if (!qla1280_probe_one(pdev, id))
+ num_hosts++;
+ }
+ }
+
+ return num_hosts;
+}
+
+/*
+ * This looks a bit ugly as we could just pass down host to
+ * qla1280_remove_one, but I want to keep qla1280_release purely a wrapper
+ * around pci_driver::remove as used from 2.6 onwards.
+ */
+static int
+qla1280_release(struct Scsi_Host *host)
+{
+ struct scsi_qla_host *ha = (struct scsi_qla_host *)host->hostdata;
+
+ qla1280_remove_one(ha->pdev);
+ return 0;
+}
+
+static int
+qla1280_biosparam_old(Disk * disk, kdev_t dev, int geom[])
+{
+ return qla1280_biosparam(disk->device, NULL, disk->capacity, geom);
+}
+#endif
+
+/**************************************************************************
+ * qla1280_intr_handler
+ * Handles the H/W interrupt
+ **************************************************************************/
+static irqreturn_t
+qla1280_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct scsi_qla_host *ha;
+ struct device_reg __iomem *reg;
+ u16 data;
+ int handled = 0;
+
+ ENTER_INTR ("qla1280_intr_handler");
+ ha = (struct scsi_qla_host *)dev_id;
+
+ spin_lock(HOST_LOCK);
+
+ ha->isr_count++;
+ reg = ha->iobase;
+
+ WRT_REG_WORD(&reg->ictrl, 0); /* disable our interrupt. */
+
+ data = qla1280_debounce_register(&reg->istatus);
+ /* Check for pending interrupts. */
+ if (data & RISC_INT) {
+ qla1280_isr(ha, &ha->done_q);
+ handled = 1;
+ }
+ if (!list_empty(&ha->done_q))
+ qla1280_done(ha);
+
+ spin_unlock(HOST_LOCK);
+
+ /* enable our interrupt. */
+ WRT_REG_WORD(&reg->ictrl, (ISP_EN_INT | ISP_EN_RISC));
+
+ LEAVE_INTR("qla1280_intr_handler");
+ return IRQ_RETVAL(handled);
+}
+
+
+static int
+qla1280_set_target_parameters(struct scsi_qla_host *ha, int bus, int target)
+{
+ uint8_t mr;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct nvram *nv;
+ int status;
+
+ nv = &ha->nvram;
+
+ mr = BIT_3 | BIT_2 | BIT_1 | BIT_0;
+
+ /* Set Target Parameters. */
+ mb[0] = MBC_SET_TARGET_PARAMETERS;
+ mb[1] = (uint16_t) (bus ? target | BIT_7 : target);
+ mb[1] <<= 8;
+
+ mb[2] = (nv->bus[bus].target[target].parameter.c << 8);
+
+ if (IS_ISP1x160(ha)) {
+ mb[2] |= nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr << 5;
+ mb[3] = (nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8) |
+ nv->bus[bus].target[target].sync_period;
+ mb[6] = (nv->bus[bus].target[target].ppr_1x160.flags.ppr_options << 8) |
+ nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width;
+ mr |= BIT_6;
+ } else {
+ mb[3] = (nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8) |
+ nv->bus[bus].target[target].sync_period;
+ }
+
+ status = qla1280_mailbox_command(ha, mr, &mb[0]);
+
+ if (status)
+ printk(KERN_WARNING "scsi(%ld:%i:%i): "
+ "qla1280_set_target_parameters() failed\n",
+ ha->host_no, bus, target);
+ return status;
+}
+
+
+/**************************************************************************
+ * qla1280_slave_configure
+ *
+ * Description:
+ * Determines the queue depth for a given device. There are two ways
+ * a queue depth can be obtained for a tagged queueing device. One
+ * way is the default queue depth which is determined by whether
+ * If it is defined, then it is used
+ * as the default queue depth. Otherwise, we use either 4 or 8 as the
+ * default queue depth (dependent on the number of hardware SCBs).
+ **************************************************************************/
+static int
+qla1280_slave_configure(struct scsi_device *device)
+{
+ struct scsi_qla_host *ha;
+ int default_depth = 3;
+ int bus = device->channel;
+ int target = device->id;
+ int status = 0;
+ struct nvram *nv;
+ unsigned long flags;
+
+ ha = (struct scsi_qla_host *)device->host->hostdata;
+ nv = &ha->nvram;
+
+ if (qla1280_check_for_dead_scsi_bus(ha, bus))
+ return 1;
+
+ if (device->tagged_supported &&
+ (ha->bus_settings[bus].qtag_enables & (BIT_0 << target))) {
+ scsi_adjust_queue_depth(device, MSG_ORDERED_TAG,
+ ha->bus_settings[bus].hiwat);
+ } else {
+ scsi_adjust_queue_depth(device, 0, default_depth);
+ }
+
+#if LINUX_VERSION_CODE > 0x020500
+ nv->bus[bus].target[target].parameter.f.enable_sync = device->sdtr;
+ nv->bus[bus].target[target].parameter.f.enable_wide = device->wdtr;
+ nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr = device->ppr;
+#endif
+
+ if (driver_setup.no_sync ||
+ (driver_setup.sync_mask &&
+ (~driver_setup.sync_mask & (1 << target))))
+ nv->bus[bus].target[target].parameter.f.enable_sync = 0;
+ if (driver_setup.no_wide ||
+ (driver_setup.wide_mask &&
+ (~driver_setup.wide_mask & (1 << target))))
+ nv->bus[bus].target[target].parameter.f.enable_wide = 0;
+ if (IS_ISP1x160(ha)) {
+ if (driver_setup.no_ppr ||
+ (driver_setup.ppr_mask &&
+ (~driver_setup.ppr_mask & (1 << target))))
+ nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 0;
+ }
+
+ spin_lock_irqsave(HOST_LOCK, flags);
+ if (nv->bus[bus].target[target].parameter.f.enable_sync)
+ status = qla1280_set_target_parameters(ha, bus, target);
+ qla1280_get_target_parameters(ha, device);
+ spin_unlock_irqrestore(HOST_LOCK, flags);
+ return status;
+}
+
+#if LINUX_VERSION_CODE < 0x020545
+/**************************************************************************
+ * qla1280_select_queue_depth
+ *
+ * Sets the queue depth for each SCSI device hanging off the input
+ * host adapter. We use a queue depth of 2 for devices that do not
+ * support tagged queueing.
+ **************************************************************************/
+static void
+qla1280_select_queue_depth(struct Scsi_Host *host, struct scsi_device *sdev_q)
+{
+ struct scsi_qla_host *ha = (struct scsi_qla_host *)host->hostdata;
+ struct scsi_device *sdev;
+
+ ENTER("qla1280_select_queue_depth");
+ for (sdev = sdev_q; sdev; sdev = sdev->next)
+ if (sdev->host == host)
+ qla1280_slave_configure(sdev);
+
+ if (sdev_q)
+ qla1280_check_for_dead_scsi_bus(ha, sdev_q->channel);
+ LEAVE("qla1280_select_queue_depth");
+}
+#endif
+
+/*
+ * qla1280_done
+ * Process completed commands.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * done_q = done queue.
+ */
+static void
+qla1280_done(struct scsi_qla_host *ha)
+{
+ struct srb *sp;
+ struct list_head *done_q;
+ int bus, target, lun;
+ struct scsi_cmnd *cmd;
+
+ ENTER("qla1280_done");
+
+ done_q = &ha->done_q;
+
+ while (!list_empty(done_q)) {
+ sp = list_entry(done_q->next, struct srb, list);
+
+ list_del(&sp->list);
+
+ cmd = sp->cmd;
+ bus = SCSI_BUS_32(cmd);
+ target = SCSI_TCN_32(cmd);
+ lun = SCSI_LUN_32(cmd);
+
+ switch ((CMD_RESULT(cmd) >> 16)) {
+ case DID_RESET:
+ /* Issue marker command. */
+ qla1280_marker(ha, bus, target, 0, MK_SYNC_ID);
+ break;
+ case DID_ABORT:
+ sp->flags &= ~SRB_ABORT_PENDING;
+ sp->flags |= SRB_ABORTED;
+ if (sp->flags & SRB_TIMEOUT)
+ CMD_RESULT(sp->cmd) = DID_TIME_OUT << 16;
+ break;
+ default:
+ break;
+ }
+
+ /* Release memory used for this I/O */
+ if (cmd->use_sg) {
+ pci_unmap_sg(ha->pdev, cmd->request_buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ } else if (cmd->request_bufflen) {
+ pci_unmap_single(ha->pdev, sp->saved_dma_handle,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+ }
+
+ /* Call the mid-level driver interrupt handler */
+ CMD_HANDLE(sp->cmd) = (unsigned char *)INVALID_HANDLE;
+ ha->actthreads--;
+
+#if LINUX_VERSION_CODE < 0x020500
+ if (cmd->cmnd[0] == INQUIRY)
+ qla1280_get_target_options(cmd, ha);
+#endif
+ (*(cmd)->scsi_done)(cmd);
+
+ if(sp->wait != NULL)
+ complete(sp->wait);
+ }
+ LEAVE("qla1280_done");
+}
+
+/*
+ * Translates a ISP error to a Linux SCSI error
+ */
+static int
+qla1280_return_status(struct response * sts, struct scsi_cmnd *cp)
+{
+ int host_status = DID_ERROR;
+ uint16_t comp_status = le16_to_cpu(sts->comp_status);
+ uint16_t state_flags = le16_to_cpu(sts->state_flags);
+ uint16_t residual_length = le16_to_cpu(sts->residual_length);
+ uint16_t scsi_status = le16_to_cpu(sts->scsi_status);
+#if DEBUG_QLA1280_INTR
+ static char *reason[] = {
+ "DID_OK",
+ "DID_NO_CONNECT",
+ "DID_BUS_BUSY",
+ "DID_TIME_OUT",
+ "DID_BAD_TARGET",
+ "DID_ABORT",
+ "DID_PARITY",
+ "DID_ERROR",
+ "DID_RESET",
+ "DID_BAD_INTR"
+ };
+#endif /* DEBUG_QLA1280_INTR */
+
+ ENTER("qla1280_return_status");
+
+#if DEBUG_QLA1280_INTR
+ /*
+ dprintk(1, "qla1280_return_status: compl status = 0x%04x\n",
+ comp_status);
+ */
+#endif
+
+ switch (comp_status) {
+ case CS_COMPLETE:
+ host_status = DID_OK;
+ break;
+
+ case CS_INCOMPLETE:
+ if (!(state_flags & SF_GOT_BUS))
+ host_status = DID_NO_CONNECT;
+ else if (!(state_flags & SF_GOT_TARGET))
+ host_status = DID_BAD_TARGET;
+ else if (!(state_flags & SF_SENT_CDB))
+ host_status = DID_ERROR;
+ else if (!(state_flags & SF_TRANSFERRED_DATA))
+ host_status = DID_ERROR;
+ else if (!(state_flags & SF_GOT_STATUS))
+ host_status = DID_ERROR;
+ else if (!(state_flags & SF_GOT_SENSE))
+ host_status = DID_ERROR;
+ break;
+
+ case CS_RESET:
+ host_status = DID_RESET;
+ break;
+
+ case CS_ABORTED:
+ host_status = DID_ABORT;
+ break;
+
+ case CS_TIMEOUT:
+ host_status = DID_TIME_OUT;
+ break;
+
+ case CS_DATA_OVERRUN:
+ dprintk(2, "Data overrun 0x%x\n", residual_length);
+ dprintk(2, "qla1280_isr: response packet data\n");
+ qla1280_dump_buffer(2, (char *)sts, RESPONSE_ENTRY_SIZE);
+ host_status = DID_ERROR;
+ break;
+
+ case CS_DATA_UNDERRUN:
+ if ((cp->request_bufflen - residual_length) <
+ cp->underflow) {
+ printk(KERN_WARNING
+ "scsi: Underflow detected - retrying "
+ "command.\n");
+ host_status = DID_ERROR;
+ } else
+ host_status = DID_OK;
+ break;
+
+ default:
+ host_status = DID_ERROR;
+ break;
+ }
+
+#if DEBUG_QLA1280_INTR
+ dprintk(1, "qla1280 ISP status: host status (%s) scsi status %x\n",
+ reason[host_status], scsi_status);
+#endif
+
+ LEAVE("qla1280_return_status");
+
+ return (scsi_status & 0xff) | (host_status << 16);
+}
+
+/****************************************************************************/
+/* QLogic ISP1280 Hardware Support Functions. */
+/****************************************************************************/
+
+ /*
+ * qla2100_enable_intrs
+ * qla2100_disable_intrs
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * None
+ */
+static inline void
+qla1280_enable_intrs(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg;
+
+ reg = ha->iobase;
+ /* enable risc and host interrupts */
+ WRT_REG_WORD(&reg->ictrl, (ISP_EN_INT | ISP_EN_RISC));
+ RD_REG_WORD(&reg->ictrl); /* PCI Posted Write flush */
+ ha->flags.ints_enabled = 1;
+}
+
+static inline void
+qla1280_disable_intrs(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg;
+
+ reg = ha->iobase;
+ /* disable risc and host interrupts */
+ WRT_REG_WORD(&reg->ictrl, 0);
+ RD_REG_WORD(&reg->ictrl); /* PCI Posted Write flush */
+ ha->flags.ints_enabled = 0;
+}
+
+/*
+ * qla1280_initialize_adapter
+ * Initialize board.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int __devinit
+qla1280_initialize_adapter(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg;
+ int status;
+ int bus;
+#if LINUX_VERSION_CODE > 0x020500
+ unsigned long flags;
+#endif
+
+ ENTER("qla1280_initialize_adapter");
+
+ /* Clear adapter flags. */
+ ha->flags.online = 0;
+ ha->flags.disable_host_adapter = 0;
+ ha->flags.reset_active = 0;
+ ha->flags.abort_isp_active = 0;
+
+ ha->flags.ints_enabled = 0;
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+ if (ia64_platform_is("sn2")) {
+ printk(KERN_INFO "scsi(%li): Enabling SN2 PCI DMA "
+ "dual channel lockup workaround\n", ha->host_no);
+ ha->flags.use_pci_vchannel = 1;
+ driver_setup.no_nvram = 1;
+ }
+#endif
+
+ /* TODO: implement support for the 1040 nvram format */
+ if (IS_ISP1040(ha))
+ driver_setup.no_nvram = 1;
+
+ dprintk(1, "Configure PCI space for adapter...\n");
+
+ reg = ha->iobase;
+
+ /* Insure mailbox registers are free. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->host_cmd, HC_CLR_RISC_INT);
+ WRT_REG_WORD(&reg->host_cmd, HC_CLR_HOST_INT);
+ RD_REG_WORD(&reg->host_cmd);
+
+ if (qla1280_read_nvram(ha)) {
+ dprintk(2, "qla1280_initialize_adapter: failed to read "
+ "NVRAM\n");
+ }
+
+#if LINUX_VERSION_CODE >= 0x020500
+ /*
+ * It's necessary to grab the spin here as qla1280_mailbox_command
+ * needs to be able to drop the lock unconditionally to wait
+ * for completion.
+ * In 2.4 ->detect is called with the io_request_lock held.
+ */
+ spin_lock_irqsave(HOST_LOCK, flags);
+#endif
+
+ status = qla1280_load_firmware(ha);
+ if (status) {
+ printk(KERN_ERR "scsi(%li): initialize: pci probe failed!\n",
+ ha->host_no);
+ goto out;
+ }
+
+ /* Setup adapter based on NVRAM parameters. */
+ dprintk(1, "scsi(%ld): Configure NVRAM parameters\n", ha->host_no);
+ qla1280_nvram_config(ha);
+
+ if (ha->flags.disable_host_adapter) {
+ status = 1;
+ goto out;
+ }
+
+ status = qla1280_init_rings(ha);
+ if (status)
+ goto out;
+
+ /* Issue SCSI reset, if we can't reset twice then bus is dead */
+ for (bus = 0; bus < ha->ports; bus++) {
+ if (!ha->bus_settings[bus].disable_scsi_reset &&
+ qla1280_bus_reset(ha, bus) &&
+ qla1280_bus_reset(ha, bus))
+ ha->bus_settings[bus].scsi_bus_dead = 1;
+ }
+
+ ha->flags.online = 1;
+ out:
+#if LINUX_VERSION_CODE >= 0x020500
+ spin_unlock_irqrestore(HOST_LOCK, flags);
+#endif
+ if (status)
+ dprintk(2, "qla1280_initialize_adapter: **** FAILED ****\n");
+
+ LEAVE("qla1280_initialize_adapter");
+ return status;
+}
+
+
+/*
+ * ISP Firmware Test
+ * Checks if present version of RISC firmware is older than
+ * driver firmware.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = firmware does not need to be loaded.
+ */
+static int
+qla1280_isp_firmware(struct scsi_qla_host *ha)
+{
+ struct nvram *nv = (struct nvram *) ha->response_ring;
+ int status = 0; /* dg 2/27 always loads RISC */
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+
+ ENTER("qla1280_isp_firmware");
+
+ dprintk(1, "scsi(%li): Determining if RISC is loaded\n", ha->host_no);
+
+ /* Bad NVRAM data, load RISC code. */
+ if (!ha->nvram_valid) {
+ ha->flags.disable_risc_code_load = 0;
+ } else
+ ha->flags.disable_risc_code_load =
+ nv->cntr_flags_1.disable_loading_risc_code;
+
+ if (ha->flags.disable_risc_code_load) {
+ dprintk(3, "qla1280_isp_firmware: Telling RISC to verify "
+ "checksum of loaded BIOS code.\n");
+
+ /* Verify checksum of loaded RISC code. */
+ mb[0] = MBC_VERIFY_CHECKSUM;
+ /* mb[1] = ql12_risc_code_addr01; */
+ mb[1] = *ql1280_board_tbl[ha->devnum].fwstart;
+
+ if (!(status =
+ qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]))) {
+ /* Start firmware execution. */
+ dprintk(3, "qla1280_isp_firmware: Startng F/W "
+ "execution.\n");
+
+ mb[0] = MBC_EXECUTE_FIRMWARE;
+ /* mb[1] = ql12_risc_code_addr01; */
+ mb[1] = *ql1280_board_tbl[ha->devnum].fwstart;
+ qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+ } else
+ printk(KERN_INFO "qla1280: RISC checksum failed.\n");
+ } else {
+ dprintk(1, "qla1280: NVRAM configured to load RISC load.\n");
+ status = 1;
+ }
+
+ if (status)
+ dprintk(2, "qla1280_isp_firmware: **** Load RISC code ****\n");
+
+ LEAVE("qla1280_isp_firmware");
+ return status;
+}
+
+/*
+ * Chip diagnostics
+ * Test chip for proper operation.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ */
+static int
+qla1280_chip_diag(struct scsi_qla_host *ha)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct device_reg __iomem *reg = ha->iobase;
+ int status = 0;
+ int cnt;
+ uint16_t data;
+ dprintk(3, "qla1280_chip_diag: testing device at 0x%p \n", &reg->id_l);
+
+ dprintk(1, "scsi(%ld): Verifying chip\n", ha->host_no);
+
+ /* Soft reset chip and wait for it to finish. */
+ WRT_REG_WORD(&reg->ictrl, ISP_RESET);
+
+ /*
+ * We can't do a traditional PCI write flush here by reading
+ * back the register. The card will not respond once the reset
+ * is in action and we end up with a machine check exception
+ * instead. Nothing to do but wait and hope for the best.
+ * A portable pci_write_flush(pdev) call would be very useful here.
+ */
+ udelay(20);
+ data = qla1280_debounce_register(&reg->ictrl);
+ /*
+ * Yet another QLogic gem ;-(
+ */
+ for (cnt = 1000000; cnt && data & ISP_RESET; cnt--) {
+ udelay(5);
+ data = RD_REG_WORD(&reg->ictrl);
+ }
+
+ if (!cnt)
+ goto fail;
+
+ /* Reset register cleared by chip reset. */
+ dprintk(3, "qla1280_chip_diag: reset register cleared by chip reset\n");
+
+ WRT_REG_WORD(&reg->cfg_1, 0);
+
+ /* Reset RISC and disable BIOS which
+ allows RISC to execute out of RAM. */
+ WRT_REG_WORD(&reg->host_cmd, HC_RESET_RISC |
+ HC_RELEASE_RISC | HC_DISABLE_BIOS);
+
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ data = qla1280_debounce_register(&reg->mailbox0);
+
+ /*
+ * I *LOVE* this code!
+ */
+ for (cnt = 1000000; cnt && data == MBS_BUSY; cnt--) {
+ udelay(5);
+ data = RD_REG_WORD(&reg->mailbox0);
+ }
+
+ if (!cnt)
+ goto fail;
+
+ /* Check product ID of chip */
+ dprintk(3, "qla1280_chip_diag: Checking product ID of chip\n");
+
+ if (RD_REG_WORD(&reg->mailbox1) != PROD_ID_1 ||
+ (RD_REG_WORD(&reg->mailbox2) != PROD_ID_2 &&
+ RD_REG_WORD(&reg->mailbox2) != PROD_ID_2a) ||
+ RD_REG_WORD(&reg->mailbox3) != PROD_ID_3 ||
+ RD_REG_WORD(&reg->mailbox4) != PROD_ID_4) {
+ printk(KERN_INFO "qla1280: Wrong product ID = "
+ "0x%x,0x%x,0x%x,0x%x\n",
+ RD_REG_WORD(&reg->mailbox1),
+ RD_REG_WORD(&reg->mailbox2),
+ RD_REG_WORD(&reg->mailbox3),
+ RD_REG_WORD(&reg->mailbox4));
+ goto fail;
+ }
+
+ /*
+ * Enable ints early!!!
+ */
+ qla1280_enable_intrs(ha);
+
+ dprintk(1, "qla1280_chip_diag: Checking mailboxes of chip\n");
+ /* Wrap Incoming Mailboxes Test. */
+ mb[0] = MBC_MAILBOX_REGISTER_TEST;
+ mb[1] = 0xAAAA;
+ mb[2] = 0x5555;
+ mb[3] = 0xAA55;
+ mb[4] = 0x55AA;
+ mb[5] = 0xA5A5;
+ mb[6] = 0x5A5A;
+ mb[7] = 0x2525;
+
+ status = qla1280_mailbox_command(ha, 0xff, mb);
+ if (status)
+ goto fail;
+
+ if (mb[1] != 0xAAAA || mb[2] != 0x5555 || mb[3] != 0xAA55 ||
+ mb[4] != 0x55AA || mb[5] != 0xA5A5 || mb[6] != 0x5A5A ||
+ mb[7] != 0x2525) {
+ printk(KERN_INFO "qla1280: Failed mbox check\n");
+ goto fail;
+ }
+
+ dprintk(3, "qla1280_chip_diag: exiting normally\n");
+ return 0;
+ fail:
+ dprintk(2, "qla1280_chip_diag: **** FAILED ****\n");
+ return status;
+}
+
+static int
+qla1280_load_firmware_pio(struct scsi_qla_host *ha)
+{
+ uint16_t risc_address, *risc_code_address, risc_code_size;
+ uint16_t mb[MAILBOX_REGISTER_COUNT], i;
+ int err;
+
+ /* Load RISC code. */
+ risc_address = *ql1280_board_tbl[ha->devnum].fwstart;
+ risc_code_address = ql1280_board_tbl[ha->devnum].fwcode;
+ risc_code_size = *ql1280_board_tbl[ha->devnum].fwlen;
+
+ for (i = 0; i < risc_code_size; i++) {
+ mb[0] = MBC_WRITE_RAM_WORD;
+ mb[1] = risc_address + i;
+ mb[2] = risc_code_address[i];
+
+ err = qla1280_mailbox_command(ha, BIT_0 | BIT_1 | BIT_2, mb);
+ if (err) {
+ printk(KERN_ERR "scsi(%li): Failed to load firmware\n",
+ ha->host_no);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+#define DUMP_IT_BACK 0 /* for debug of RISC loading */
+static int
+qla1280_load_firmware_dma(struct scsi_qla_host *ha)
+{
+ uint16_t risc_address, *risc_code_address, risc_code_size;
+ uint16_t mb[MAILBOX_REGISTER_COUNT], cnt;
+ int err = 0, num, i;
+#if DUMP_IT_BACK
+ uint8_t *sp, *tbuf;
+ dma_addr_t p_tbuf;
+
+ tbuf = pci_alloc_consistent(ha->pdev, 8000, &p_tbuf);
+ if (!tbuf)
+ return -ENOMEM;
+#endif
+
+ /* Load RISC code. */
+ risc_address = *ql1280_board_tbl[ha->devnum].fwstart;
+ risc_code_address = ql1280_board_tbl[ha->devnum].fwcode;
+ risc_code_size = *ql1280_board_tbl[ha->devnum].fwlen;
+
+ dprintk(1, "%s: DMA RISC code (%i) words\n",
+ __FUNCTION__, risc_code_size);
+
+ num = 0;
+ while (risc_code_size > 0) {
+ int warn __attribute__((unused)) = 0;
+
+ cnt = 2000 >> 1;
+
+ if (cnt > risc_code_size)
+ cnt = risc_code_size;
+
+ dprintk(2, "qla1280_setup_chip: loading risc @ =(0x%p),"
+ "%d,%d(0x%x)\n",
+ risc_code_address, cnt, num, risc_address);
+ for(i = 0; i < cnt; i++)
+ ((uint16_t *)ha->request_ring)[i] =
+ cpu_to_le16(risc_code_address[i]);
+
+ mb[0] = MBC_LOAD_RAM;
+ mb[1] = risc_address;
+ mb[4] = cnt;
+ mb[3] = ha->request_dma & 0xffff;
+ mb[2] = (ha->request_dma >> 16) & 0xffff;
+ mb[7] = pci_dma_hi32(ha->request_dma) & 0xffff;
+ mb[6] = pci_dma_hi32(ha->request_dma) >> 16;
+ dprintk(2, "%s: op=%d 0x%p = 0x%4x,0x%4x,0x%4x,0x%4x\n",
+ __FUNCTION__, mb[0],
+ (void *)(long)ha->request_dma,
+ mb[6], mb[7], mb[2], mb[3]);
+ err = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | BIT_2 |
+ BIT_1 | BIT_0, mb);
+ if (err) {
+ printk(KERN_ERR "scsi(%li): Failed to load partial "
+ "segment of f\n", ha->host_no);
+ goto out;
+ }
+
+#if DUMP_IT_BACK
+ mb[0] = MBC_DUMP_RAM;
+ mb[1] = risc_address;
+ mb[4] = cnt;
+ mb[3] = p_tbuf & 0xffff;
+ mb[2] = (p_tbuf >> 16) & 0xffff;
+ mb[7] = pci_dma_hi32(p_tbuf) & 0xffff;
+ mb[6] = pci_dma_hi32(p_tbuf) >> 16;
+
+ err = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | BIT_2 |
+ BIT_1 | BIT_0, mb);
+ if (err) {
+ printk(KERN_ERR
+ "Failed to dump partial segment of f/w\n");
+ goto out;
+ }
+ sp = (uint8_t *)ha->request_ring;
+ for (i = 0; i < (cnt << 1); i++) {
+ if (tbuf[i] != sp[i] && warn++ < 10) {
+ printk(KERN_ERR "%s: FW compare error @ "
+ "byte(0x%x) loop#=%x\n",
+ __FUNCTION__, i, num);
+ printk(KERN_ERR "%s: FWbyte=%x "
+ "FWfromChip=%x\n",
+ __FUNCTION__, sp[i], tbuf[i]);
+ /*break; */
+ }
+ }
+#endif
+ risc_address += cnt;
+ risc_code_size = risc_code_size - cnt;
+ risc_code_address = risc_code_address + cnt;
+ num++;
+ }
+
+ out:
+#if DUMP_IT_BACK
+ pci_free_consistent(ha->pdev, 8000, tbuf, p_tbuf);
+#endif
+ return err;
+}
+
+static int
+qla1280_start_firmware(struct scsi_qla_host *ha)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int err;
+
+ dprintk(1, "%s: Verifying checksum of loaded RISC code.\n",
+ __FUNCTION__);
+
+ /* Verify checksum of loaded RISC code. */
+ mb[0] = MBC_VERIFY_CHECKSUM;
+ /* mb[1] = ql12_risc_code_addr01; */
+ mb[1] = *ql1280_board_tbl[ha->devnum].fwstart;
+ err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb);
+ if (err) {
+ printk(KERN_ERR "scsi(%li): Failed checksum\n", ha->host_no);
+ return err;
+ }
+
+ /* Start firmware execution. */
+ dprintk(1, "%s: start firmware running.\n", __FUNCTION__);
+ mb[0] = MBC_EXECUTE_FIRMWARE;
+ mb[1] = *ql1280_board_tbl[ha->devnum].fwstart;
+ err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+ if (err) {
+ printk(KERN_ERR "scsi(%li): Failed to start firmware\n",
+ ha->host_no);
+ }
+
+ return err;
+}
+
+static int
+qla1280_load_firmware(struct scsi_qla_host *ha)
+{
+ int err = -ENODEV;
+
+ /* If firmware needs to be loaded */
+ if (!qla1280_isp_firmware(ha)) {
+ printk(KERN_ERR "scsi(%li): isp_firmware() failed!\n",
+ ha->host_no);
+ goto out;
+ }
+
+ err = qla1280_chip_diag(ha);
+ if (err)
+ goto out;
+ if (IS_ISP1040(ha))
+ err = qla1280_load_firmware_pio(ha);
+ else
+ err = qla1280_load_firmware_dma(ha);
+ if (err)
+ goto out;
+ err = qla1280_start_firmware(ha);
+ out:
+ return err;
+}
+
+/*
+ * Initialize rings
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * ha->request_ring = request ring virtual address
+ * ha->response_ring = response ring virtual address
+ * ha->request_dma = request ring physical address
+ * ha->response_dma = response ring physical address
+ *
+ * Returns:
+ * 0 = success.
+ */
+static int
+qla1280_init_rings(struct scsi_qla_host *ha)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int status = 0;
+
+ ENTER("qla1280_init_rings");
+
+ /* Clear outstanding commands array. */
+ memset(ha->outstanding_cmds, 0,
+ sizeof(struct srb *) * MAX_OUTSTANDING_COMMANDS);
+
+ /* Initialize request queue. */
+ ha->request_ring_ptr = ha->request_ring;
+ ha->req_ring_index = 0;
+ ha->req_q_cnt = REQUEST_ENTRY_CNT;
+ /* mb[0] = MBC_INIT_REQUEST_QUEUE; */
+ mb[0] = MBC_INIT_REQUEST_QUEUE_A64;
+ mb[1] = REQUEST_ENTRY_CNT;
+ mb[3] = ha->request_dma & 0xffff;
+ mb[2] = (ha->request_dma >> 16) & 0xffff;
+ mb[4] = 0;
+ mb[7] = pci_dma_hi32(ha->request_dma) & 0xffff;
+ mb[6] = pci_dma_hi32(ha->request_dma) >> 16;
+ if (!(status = qla1280_mailbox_command(ha, BIT_7 | BIT_6 | BIT_4 |
+ BIT_3 | BIT_2 | BIT_1 | BIT_0,
+ &mb[0]))) {
+ /* Initialize response queue. */
+ ha->response_ring_ptr = ha->response_ring;
+ ha->rsp_ring_index = 0;
+ /* mb[0] = MBC_INIT_RESPONSE_QUEUE; */
+ mb[0] = MBC_INIT_RESPONSE_QUEUE_A64;
+ mb[1] = RESPONSE_ENTRY_CNT;
+ mb[3] = ha->response_dma & 0xffff;
+ mb[2] = (ha->response_dma >> 16) & 0xffff;
+ mb[5] = 0;
+ mb[7] = pci_dma_hi32(ha->response_dma) & 0xffff;
+ mb[6] = pci_dma_hi32(ha->response_dma) >> 16;
+ status = qla1280_mailbox_command(ha, BIT_7 | BIT_6 | BIT_5 |
+ BIT_3 | BIT_2 | BIT_1 | BIT_0,
+ &mb[0]);
+ }
+
+ if (status)
+ dprintk(2, "qla1280_init_rings: **** FAILED ****\n");
+
+ LEAVE("qla1280_init_rings");
+ return status;
+}
+
+static void
+qla1280_print_settings(struct nvram *nv)
+{
+ dprintk(1, "qla1280 : initiator scsi id bus[0]=%d\n",
+ nv->bus[0].config_1.initiator_id);
+ dprintk(1, "qla1280 : initiator scsi id bus[1]=%d\n",
+ nv->bus[1].config_1.initiator_id);
+
+ dprintk(1, "qla1280 : bus reset delay[0]=%d\n",
+ nv->bus[0].bus_reset_delay);
+ dprintk(1, "qla1280 : bus reset delay[1]=%d\n",
+ nv->bus[1].bus_reset_delay);
+
+ dprintk(1, "qla1280 : retry count[0]=%d\n", nv->bus[0].retry_count);
+ dprintk(1, "qla1280 : retry delay[0]=%d\n", nv->bus[0].retry_delay);
+ dprintk(1, "qla1280 : retry count[1]=%d\n", nv->bus[1].retry_count);
+ dprintk(1, "qla1280 : retry delay[1]=%d\n", nv->bus[1].retry_delay);
+
+ dprintk(1, "qla1280 : async data setup time[0]=%d\n",
+ nv->bus[0].config_2.async_data_setup_time);
+ dprintk(1, "qla1280 : async data setup time[1]=%d\n",
+ nv->bus[1].config_2.async_data_setup_time);
+
+ dprintk(1, "qla1280 : req/ack active negation[0]=%d\n",
+ nv->bus[0].config_2.req_ack_active_negation);
+ dprintk(1, "qla1280 : req/ack active negation[1]=%d\n",
+ nv->bus[1].config_2.req_ack_active_negation);
+
+ dprintk(1, "qla1280 : data line active negation[0]=%d\n",
+ nv->bus[0].config_2.data_line_active_negation);
+ dprintk(1, "qla1280 : data line active negation[1]=%d\n",
+ nv->bus[1].config_2.data_line_active_negation);
+
+ dprintk(1, "qla1280 : disable loading risc code=%d\n",
+ nv->cntr_flags_1.disable_loading_risc_code);
+
+ dprintk(1, "qla1280 : enable 64bit addressing=%d\n",
+ nv->cntr_flags_1.enable_64bit_addressing);
+
+ dprintk(1, "qla1280 : selection timeout limit[0]=%d\n",
+ nv->bus[0].selection_timeout);
+ dprintk(1, "qla1280 : selection timeout limit[1]=%d\n",
+ nv->bus[1].selection_timeout);
+
+ dprintk(1, "qla1280 : max queue depth[0]=%d\n",
+ nv->bus[0].max_queue_depth);
+ dprintk(1, "qla1280 : max queue depth[1]=%d\n",
+ nv->bus[1].max_queue_depth);
+}
+
+static void
+qla1280_set_target_defaults(struct scsi_qla_host *ha, int bus, int target)
+{
+ struct nvram *nv = &ha->nvram;
+
+ nv->bus[bus].target[target].parameter.f.renegotiate_on_error = 1;
+ nv->bus[bus].target[target].parameter.f.auto_request_sense = 1;
+ nv->bus[bus].target[target].parameter.f.tag_queuing = 1;
+ nv->bus[bus].target[target].parameter.f.enable_sync = 1;
+#if 1 /* Some SCSI Processors do not seem to like this */
+ nv->bus[bus].target[target].parameter.f.enable_wide = 1;
+#endif
+ nv->bus[bus].target[target].parameter.f.parity_checking = 1;
+ nv->bus[bus].target[target].parameter.f.disconnect_allowed = 1;
+ nv->bus[bus].target[target].execution_throttle =
+ nv->bus[bus].max_queue_depth - 1;
+
+ if (IS_ISP1x160(ha)) {
+ nv->bus[bus].target[target].flags.flags1x160.device_enable = 1;
+ nv->bus[bus].target[target].flags.flags1x160.sync_offset = 0x0e;
+ nv->bus[bus].target[target].sync_period = 9;
+ nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 1;
+ nv->bus[bus].target[target].ppr_1x160.flags.ppr_options = 2;
+ nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width = 1;
+ } else {
+ nv->bus[bus].target[target].flags.flags1x80.device_enable = 1;
+ nv->bus[bus].target[target].flags.flags1x80.sync_offset = 12;
+ nv->bus[bus].target[target].sync_period = 10;
+ }
+}
+
+static void
+qla1280_set_defaults(struct scsi_qla_host *ha)
+{
+ struct nvram *nv = &ha->nvram;
+ int bus, target;
+
+ dprintk(1, "Using defaults for NVRAM: \n");
+ memset(nv, 0, sizeof(struct nvram));
+
+ /* nv->cntr_flags_1.disable_loading_risc_code = 1; */
+ nv->firmware_feature.f.enable_fast_posting = 1;
+ nv->firmware_feature.f.disable_synchronous_backoff = 1;
+ nv->termination.f.scsi_bus_0_control = 3;
+ nv->termination.f.scsi_bus_1_control = 3;
+ nv->termination.f.auto_term_support = 1;
+
+ /*
+ * Set default FIFO magic - What appropriate values would be here
+ * is unknown. This is what I have found testing with 12160s.
+ *
+ * Now, I would love the magic decoder ring for this one, the
+ * header file provided by QLogic seems to be bogus or incomplete
+ * at best.
+ */
+ nv->isp_config.c = ISP_CFG1_BENAB|ISP_CFG1_F128;
+ if (IS_ISP1x160(ha))
+ nv->isp_parameter = 0x01; /* fast memory enable */
+
+ for (bus = 0; bus < MAX_BUSES; bus++) {
+ nv->bus[bus].config_1.initiator_id = 7;
+ nv->bus[bus].config_2.req_ack_active_negation = 1;
+ nv->bus[bus].config_2.data_line_active_negation = 1;
+ nv->bus[bus].selection_timeout = 250;
+ nv->bus[bus].max_queue_depth = 256;
+
+ if (IS_ISP1040(ha)) {
+ nv->bus[bus].bus_reset_delay = 3;
+ nv->bus[bus].config_2.async_data_setup_time = 6;
+ nv->bus[bus].retry_delay = 1;
+ } else {
+ nv->bus[bus].bus_reset_delay = 5;
+ nv->bus[bus].config_2.async_data_setup_time = 8;
+ }
+
+ for (target = 0; target < MAX_TARGETS; target++)
+ qla1280_set_target_defaults(ha, bus, target);
+ }
+}
+
+static int
+qla1280_config_target(struct scsi_qla_host *ha, int bus, int target)
+{
+ struct nvram *nv = &ha->nvram;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int status, lun;
+
+ /* Set Target Parameters. */
+ mb[0] = MBC_SET_TARGET_PARAMETERS;
+ mb[1] = (uint16_t) (bus ? target | BIT_7 : target);
+ mb[1] <<= 8;
+
+ /*
+ * Do not enable wide, sync, and ppr for the initial
+ * INQUIRY run. We enable this later if we determine
+ * the target actually supports it.
+ */
+ nv->bus[bus].target[target].parameter.f.
+ auto_request_sense = 1;
+ nv->bus[bus].target[target].parameter.f.
+ stop_queue_on_check = 0;
+
+ if (IS_ISP1x160(ha))
+ nv->bus[bus].target[target].ppr_1x160.
+ flags.enable_ppr = 0;
+
+ /*
+ * No sync, wide, etc. while probing
+ */
+ mb[2] = (nv->bus[bus].target[target].parameter.c << 8) &
+ ~(TP_SYNC /*| TP_WIDE | TP_PPR*/);
+
+ if (IS_ISP1x160(ha))
+ mb[3] = nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8;
+ else
+ mb[3] = nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8;
+ mb[3] |= nv->bus[bus].target[target].sync_period;
+
+ status = qla1280_mailbox_command(ha, BIT_3 | BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ /* Save Tag queuing enable flag. */
+ mb[0] = BIT_0 << target;
+ if (nv->bus[bus].target[target].parameter.f.tag_queuing)
+ ha->bus_settings[bus].qtag_enables |= mb[0];
+
+ /* Save Device enable flag. */
+ if (IS_ISP1x160(ha)) {
+ if (nv->bus[bus].target[target].flags.flags1x160.device_enable)
+ ha->bus_settings[bus].device_enables |= mb[0];
+ ha->bus_settings[bus].lun_disables |= 0;
+ } else {
+ if (nv->bus[bus].target[target].flags.flags1x80.device_enable)
+ ha->bus_settings[bus].device_enables |= mb[0];
+ /* Save LUN disable flag. */
+ if (nv->bus[bus].target[target].flags.flags1x80.lun_disable)
+ ha->bus_settings[bus].lun_disables |= mb[0];
+ }
+
+ /* Set Device Queue Parameters. */
+ for (lun = 0; lun < MAX_LUNS; lun++) {
+ mb[0] = MBC_SET_DEVICE_QUEUE;
+ mb[1] = (uint16_t)(bus ? target | BIT_7 : target);
+ mb[1] = mb[1] << 8 | lun;
+ mb[2] = nv->bus[bus].max_queue_depth;
+ mb[3] = nv->bus[bus].target[target].execution_throttle;
+ status |= qla1280_mailbox_command(ha, 0x0f, &mb[0]);
+ }
+
+ return status;
+}
+
+static int
+qla1280_config_bus(struct scsi_qla_host *ha, int bus)
+{
+ struct nvram *nv = &ha->nvram;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int target, status;
+
+ /* SCSI Reset Disable. */
+ ha->bus_settings[bus].disable_scsi_reset =
+ nv->bus[bus].config_1.scsi_reset_disable;
+
+ /* Initiator ID. */
+ ha->bus_settings[bus].id = nv->bus[bus].config_1.initiator_id;
+ mb[0] = MBC_SET_INITIATOR_ID;
+ mb[1] = bus ? ha->bus_settings[bus].id | BIT_7 :
+ ha->bus_settings[bus].id;
+ status = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+
+ /* Reset Delay. */
+ ha->bus_settings[bus].bus_reset_delay =
+ nv->bus[bus].bus_reset_delay;
+
+ /* Command queue depth per device. */
+ ha->bus_settings[bus].hiwat = nv->bus[bus].max_queue_depth - 1;
+
+ /* Set target parameters. */
+ for (target = 0; target < MAX_TARGETS; target++)
+ status |= qla1280_config_target(ha, bus, target);
+
+ return status;
+}
+
+static int
+qla1280_nvram_config(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ struct nvram *nv = &ha->nvram;
+ int bus, target, status = 0;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ uint16_t mask;
+
+ ENTER("qla1280_nvram_config");
+
+ if (ha->nvram_valid) {
+ /* Always force AUTO sense for LINUX SCSI */
+ for (bus = 0; bus < MAX_BUSES; bus++)
+ for (target = 0; target < MAX_TARGETS; target++) {
+ nv->bus[bus].target[target].parameter.f.
+ auto_request_sense = 1;
+ }
+ } else {
+ qla1280_set_defaults(ha);
+ }
+
+ qla1280_print_settings(nv);
+
+ /* Disable RISC load of firmware. */
+ ha->flags.disable_risc_code_load =
+ nv->cntr_flags_1.disable_loading_risc_code;
+
+ if (IS_ISP1040(ha)) {
+ uint16_t hwrev, cfg1, cdma_conf, ddma_conf;
+
+ hwrev = RD_REG_WORD(&reg->cfg_0) & ISP_CFG0_HWMSK;
+
+ cfg1 = RD_REG_WORD(&reg->cfg_1);
+ cdma_conf = RD_REG_WORD(&reg->cdma_cfg);
+ ddma_conf = RD_REG_WORD(&reg->ddma_cfg);
+
+ /* Busted fifo, says mjacob. */
+ if (hwrev == ISP_CFG0_1040A)
+ WRT_REG_WORD(&reg->cfg_1, cfg1 | ISP_CFG1_F64);
+ else
+ WRT_REG_WORD(&reg->cfg_1, cfg1 | ISP_CFG1_F64 | ISP_CFG1_BENAB);
+
+ WRT_REG_WORD(&reg->cdma_cfg, cdma_conf | CDMA_CONF_BENAB);
+ WRT_REG_WORD(&reg->ddma_cfg, cdma_conf | DDMA_CONF_BENAB);
+ } else {
+ /* Set ISP hardware DMA burst */
+ mb[0] = nv->isp_config.c;
+ /* Enable DMA arbitration on dual channel controllers */
+ if (ha->ports > 1)
+ mb[0] |= BIT_13;
+ WRT_REG_WORD(&reg->cfg_1, mb[0]);
+
+ /* Set SCSI termination. */
+ WRT_REG_WORD(&reg->gpio_enable, (BIT_3 + BIT_2 + BIT_1 + BIT_0));
+ mb[0] = nv->termination.c & (BIT_3 + BIT_2 + BIT_1 + BIT_0);
+ WRT_REG_WORD(&reg->gpio_data, mb[0]);
+ }
+
+ /* ISP parameter word. */
+ mb[0] = MBC_SET_SYSTEM_PARAMETER;
+ mb[1] = nv->isp_parameter;
+ status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+
+ if (IS_ISP1x40(ha)) {
+ /* clock rate - for qla1240 and older, only */
+ mb[0] = MBC_SET_CLOCK_RATE;
+ mb[1] = 40;
+ status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb);
+ }
+
+ /* Firmware feature word. */
+ mb[0] = MBC_SET_FIRMWARE_FEATURES;
+ mask = BIT_5 | BIT_1 | BIT_0;
+ mb[1] = le16_to_cpu(nv->firmware_feature.w) & (mask);
+#if defined(CONFIG_IA64_GENERIC) || defined (CONFIG_IA64_SGI_SN2)
+ if (ia64_platform_is("sn2")) {
+ printk(KERN_INFO "scsi(%li): Enabling SN2 PCI DMA "
+ "workaround\n", ha->host_no);
+ mb[1] |= BIT_9;
+ }
+#endif
+ status |= qla1280_mailbox_command(ha, mask, &mb[0]);
+
+ /* Retry count and delay. */
+ mb[0] = MBC_SET_RETRY_COUNT;
+ mb[1] = nv->bus[0].retry_count;
+ mb[2] = nv->bus[0].retry_delay;
+ mb[6] = nv->bus[1].retry_count;
+ mb[7] = nv->bus[1].retry_delay;
+ status |= qla1280_mailbox_command(ha, BIT_7 | BIT_6 | BIT_2 |
+ BIT_1 | BIT_0, &mb[0]);
+
+ /* ASYNC data setup time. */
+ mb[0] = MBC_SET_ASYNC_DATA_SETUP;
+ mb[1] = nv->bus[0].config_2.async_data_setup_time;
+ mb[2] = nv->bus[1].config_2.async_data_setup_time;
+ status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ /* Active negation states. */
+ mb[0] = MBC_SET_ACTIVE_NEGATION;
+ mb[1] = 0;
+ if (nv->bus[0].config_2.req_ack_active_negation)
+ mb[1] |= BIT_5;
+ if (nv->bus[0].config_2.data_line_active_negation)
+ mb[1] |= BIT_4;
+ mb[2] = 0;
+ if (nv->bus[1].config_2.req_ack_active_negation)
+ mb[2] |= BIT_5;
+ if (nv->bus[1].config_2.data_line_active_negation)
+ mb[2] |= BIT_4;
+ status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ mb[0] = MBC_SET_DATA_OVERRUN_RECOVERY;
+ mb[1] = 2; /* Reset SCSI bus and return all outstanding IO */
+ status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+
+ /* thingy */
+ mb[0] = MBC_SET_PCI_CONTROL;
+ mb[1] = 2; /* Data DMA Channel Burst Enable */
+ mb[2] = 2; /* Command DMA Channel Burst Enable */
+ status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ mb[0] = MBC_SET_TAG_AGE_LIMIT;
+ mb[1] = 8;
+ status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+
+ /* Selection timeout. */
+ mb[0] = MBC_SET_SELECTION_TIMEOUT;
+ mb[1] = nv->bus[0].selection_timeout;
+ mb[2] = nv->bus[1].selection_timeout;
+ status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ for (bus = 0; bus < ha->ports; bus++)
+ status |= qla1280_config_bus(ha, bus);
+
+ if (status)
+ dprintk(2, "qla1280_nvram_config: **** FAILED ****\n");
+
+ LEAVE("qla1280_nvram_config");
+ return status;
+}
+
+/*
+ * Get NVRAM data word
+ * Calculates word position in NVRAM and calls request routine to
+ * get the word from NVRAM.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * address = NVRAM word address.
+ *
+ * Returns:
+ * data word.
+ */
+static uint16_t
+qla1280_get_nvram_word(struct scsi_qla_host *ha, uint32_t address)
+{
+ uint32_t nv_cmd;
+ uint16_t data;
+
+ nv_cmd = address << 16;
+ nv_cmd |= NV_READ_OP;
+
+ data = le16_to_cpu(qla1280_nvram_request(ha, nv_cmd));
+
+ dprintk(8, "qla1280_get_nvram_word: exiting normally NVRAM data = "
+ "0x%x", data);
+
+ return data;
+}
+
+/*
+ * NVRAM request
+ * Sends read command to NVRAM and gets data from NVRAM.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * nv_cmd = Bit 26 = start bit
+ * Bit 25, 24 = opcode
+ * Bit 23-16 = address
+ * Bit 15-0 = write data
+ *
+ * Returns:
+ * data word.
+ */
+static uint16_t
+qla1280_nvram_request(struct scsi_qla_host *ha, uint32_t nv_cmd)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ int cnt;
+ uint16_t data = 0;
+ uint16_t reg_data;
+
+ /* Send command to NVRAM. */
+
+ nv_cmd <<= 5;
+ for (cnt = 0; cnt < 11; cnt++) {
+ if (nv_cmd & BIT_31)
+ qla1280_nv_write(ha, NV_DATA_OUT);
+ else
+ qla1280_nv_write(ha, 0);
+ nv_cmd <<= 1;
+ }
+
+ /* Read data from NVRAM. */
+
+ for (cnt = 0; cnt < 16; cnt++) {
+ WRT_REG_WORD(&reg->nvram, (NV_SELECT | NV_CLOCK));
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ NVRAM_DELAY();
+ data <<= 1;
+ reg_data = RD_REG_WORD(&reg->nvram);
+ if (reg_data & NV_DATA_IN)
+ data |= BIT_0;
+ WRT_REG_WORD(&reg->nvram, NV_SELECT);
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ NVRAM_DELAY();
+ }
+
+ /* Deselect chip. */
+
+ WRT_REG_WORD(&reg->nvram, NV_DESELECT);
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ NVRAM_DELAY();
+
+ return data;
+}
+
+static void
+qla1280_nv_write(struct scsi_qla_host *ha, uint16_t data)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+
+ WRT_REG_WORD(&reg->nvram, data | NV_SELECT);
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NV_SELECT | NV_CLOCK);
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NV_SELECT);
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+ NVRAM_DELAY();
+}
+
+/*
+ * Mailbox Command
+ * Issue mailbox command and waits for completion.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * mr = mailbox registers to load.
+ * mb = data pointer for mailbox registers.
+ *
+ * Output:
+ * mb[MAILBOX_REGISTER_COUNT] = returned mailbox data.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int
+qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+#if 0
+ LIST_HEAD(done_q);
+#endif
+ int status = 0;
+ int cnt;
+ uint16_t *optr, *iptr;
+ uint16_t __iomem *mptr;
+ uint16_t data;
+ DECLARE_COMPLETION(wait);
+ struct timer_list timer;
+
+ ENTER("qla1280_mailbox_command");
+
+ if (ha->mailbox_wait) {
+ printk(KERN_ERR "Warning mailbox wait already in use!\n");
+ }
+ ha->mailbox_wait = &wait;
+
+ /*
+ * We really should start out by verifying that the mailbox is
+ * available before starting sending the command data
+ */
+ /* Load mailbox registers. */
+ mptr = (uint16_t __iomem *) &reg->mailbox0;
+ iptr = mb;
+ for (cnt = 0; cnt < MAILBOX_REGISTER_COUNT; cnt++) {
+ if (mr & BIT_0) {
+ WRT_REG_WORD(mptr, (*iptr));
+ }
+
+ mr >>= 1;
+ mptr++;
+ iptr++;
+ }
+
+ /* Issue set host interrupt command. */
+
+ /* set up a timer just in case we're really jammed */
+ init_timer(&timer);
+ timer.expires = jiffies + 20*HZ;
+ timer.data = (unsigned long)ha;
+ timer.function = qla1280_mailbox_timeout;
+ add_timer(&timer);
+
+ spin_unlock_irq(HOST_LOCK);
+ WRT_REG_WORD(&reg->host_cmd, HC_SET_HOST_INT);
+ data = qla1280_debounce_register(&reg->istatus);
+
+ wait_for_completion(&wait);
+ del_timer_sync(&timer);
+
+ spin_lock_irq(HOST_LOCK);
+
+ ha->mailbox_wait = NULL;
+
+ /* Check for mailbox command timeout. */
+ if (ha->mailbox_out[0] != MBS_CMD_CMP) {
+ printk(KERN_WARNING "qla1280_mailbox_command: Command failed, "
+ "mailbox0 = 0x%04x, mailbox_out0 = 0x%04x, istatus = "
+ "0x%04x\n",
+ mb[0], ha->mailbox_out[0], RD_REG_WORD(&reg->istatus));
+ printk(KERN_WARNING "m0 %04x, m1 %04x, m2 %04x, m3 %04x\n",
+ RD_REG_WORD(&reg->mailbox0), RD_REG_WORD(&reg->mailbox1),
+ RD_REG_WORD(&reg->mailbox2), RD_REG_WORD(&reg->mailbox3));
+ printk(KERN_WARNING "m4 %04x, m5 %04x, m6 %04x, m7 %04x\n",
+ RD_REG_WORD(&reg->mailbox4), RD_REG_WORD(&reg->mailbox5),
+ RD_REG_WORD(&reg->mailbox6), RD_REG_WORD(&reg->mailbox7));
+ status = 1;
+ }
+
+ /* Load return mailbox registers. */
+ optr = mb;
+ iptr = (uint16_t *) &ha->mailbox_out[0];
+ mr = MAILBOX_REGISTER_COUNT;
+ memcpy(optr, iptr, MAILBOX_REGISTER_COUNT * sizeof(uint16_t));
+
+#if 0
+ /* Go check for any response interrupts pending. */
+ qla1280_isr(ha, &done_q);
+#endif
+
+ if (ha->flags.reset_marker)
+ qla1280_rst_aen(ha);
+
+#if 0
+ if (!list_empty(&done_q))
+ qla1280_done(ha, &done_q);
+#endif
+
+ if (status)
+ dprintk(2, "qla1280_mailbox_command: **** FAILED, mailbox0 = "
+ "0x%x ****\n", mb[0]);
+
+ LEAVE("qla1280_mailbox_command");
+ return status;
+}
+
+/*
+ * qla1280_poll
+ * Polls ISP for interrupts.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ */
+static void
+qla1280_poll(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ uint16_t data;
+ LIST_HEAD(done_q);
+
+ /* ENTER("qla1280_poll"); */
+
+ /* Check for pending interrupts. */
+ data = RD_REG_WORD(&reg->istatus);
+ if (data & RISC_INT)
+ qla1280_isr(ha, &done_q);
+
+ if (!ha->mailbox_wait) {
+ if (ha->flags.reset_marker)
+ qla1280_rst_aen(ha);
+ }
+
+ if (!list_empty(&done_q))
+ qla1280_done(ha);
+
+ /* LEAVE("qla1280_poll"); */
+}
+
+/*
+ * qla1280_bus_reset
+ * Issue SCSI bus reset.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * bus = SCSI bus number.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int
+qla1280_bus_reset(struct scsi_qla_host *ha, int bus)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ uint16_t reset_delay;
+ int status;
+
+ dprintk(3, "qla1280_bus_reset: entered\n");
+
+ if (qla1280_verbose)
+ printk(KERN_INFO "scsi(%li:%i): Resetting SCSI BUS\n",
+ ha->host_no, bus);
+
+ reset_delay = ha->bus_settings[bus].bus_reset_delay;
+ mb[0] = MBC_BUS_RESET;
+ mb[1] = reset_delay;
+ mb[2] = (uint16_t) bus;
+ status = qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ if (status) {
+ if (ha->bus_settings[bus].failed_reset_count > 2)
+ ha->bus_settings[bus].scsi_bus_dead = 1;
+ ha->bus_settings[bus].failed_reset_count++;
+ } else {
+ spin_unlock_irq(HOST_LOCK);
+ schedule_timeout(reset_delay * HZ);
+ spin_lock_irq(HOST_LOCK);
+
+ ha->bus_settings[bus].scsi_bus_dead = 0;
+ ha->bus_settings[bus].failed_reset_count = 0;
+ ha->bus_settings[bus].reset_marker = 0;
+ /* Issue marker command. */
+ qla1280_marker(ha, bus, 0, 0, MK_SYNC_ALL);
+ }
+
+ /*
+ * We should probably call qla1280_set_target_parameters()
+ * here as well for all devices on the bus.
+ */
+
+ if (status)
+ dprintk(2, "qla1280_bus_reset: **** FAILED ****\n");
+ else
+ dprintk(3, "qla1280_bus_reset: exiting normally\n");
+
+ return status;
+}
+
+/*
+ * qla1280_device_reset
+ * Issue bus device reset message to the target.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * bus = SCSI BUS number.
+ * target = SCSI ID.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int
+qla1280_device_reset(struct scsi_qla_host *ha, int bus, int target)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int status;
+
+ ENTER("qla1280_device_reset");
+
+ mb[0] = MBC_ABORT_TARGET;
+ mb[1] = (bus ? (target | BIT_7) : target) << 8;
+ mb[2] = 1;
+ status = qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]);
+
+ /* Issue marker command. */
+ qla1280_marker(ha, bus, target, 0, MK_SYNC_ID);
+
+ if (status)
+ dprintk(2, "qla1280_device_reset: **** FAILED ****\n");
+
+ LEAVE("qla1280_device_reset");
+ return status;
+}
+
+/*
+ * qla1280_abort_device
+ * Issue an abort message to the device
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * bus = SCSI BUS.
+ * target = SCSI ID.
+ * lun = SCSI LUN.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int
+qla1280_abort_device(struct scsi_qla_host *ha, int bus, int target, int lun)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int status;
+
+ ENTER("qla1280_abort_device");
+
+ mb[0] = MBC_ABORT_DEVICE;
+ mb[1] = (bus ? target | BIT_7 : target) << 8 | lun;
+ status = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
+
+ /* Issue marker command. */
+ qla1280_marker(ha, bus, target, lun, MK_SYNC_ID_LUN);
+
+ if (status)
+ dprintk(2, "qla1280_abort_device: **** FAILED ****\n");
+
+ LEAVE("qla1280_abort_device");
+ return status;
+}
+
+/*
+ * qla1280_abort_command
+ * Abort command aborts a specified IOCB.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = SB structure pointer.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int
+qla1280_abort_command(struct scsi_qla_host *ha, struct srb * sp, int handle)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ unsigned int bus, target, lun;
+ int status;
+
+ ENTER("qla1280_abort_command");
+
+ bus = SCSI_BUS_32(sp->cmd);
+ target = SCSI_TCN_32(sp->cmd);
+ lun = SCSI_LUN_32(sp->cmd);
+
+ sp->flags |= SRB_ABORT_PENDING;
+
+ mb[0] = MBC_ABORT_COMMAND;
+ mb[1] = (bus ? target | BIT_7 : target) << 8 | lun;
+ mb[2] = handle >> 16;
+ mb[3] = handle & 0xffff;
+ status = qla1280_mailbox_command(ha, 0x0f, &mb[0]);
+
+ if (status) {
+ dprintk(2, "qla1280_abort_command: **** FAILED ****\n");
+ sp->flags &= ~SRB_ABORT_PENDING;
+ }
+
+
+ LEAVE("qla1280_abort_command");
+ return status;
+}
+
+/*
+ * qla1280_reset_adapter
+ * Reset adapter.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ */
+static void
+qla1280_reset_adapter(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+
+ ENTER("qla1280_reset_adapter");
+
+ /* Disable ISP chip */
+ ha->flags.online = 0;
+ WRT_REG_WORD(&reg->ictrl, ISP_RESET);
+ WRT_REG_WORD(&reg->host_cmd,
+ HC_RESET_RISC | HC_RELEASE_RISC | HC_DISABLE_BIOS);
+ RD_REG_WORD(&reg->id_l); /* Flush PCI write */
+
+ LEAVE("qla1280_reset_adapter");
+}
+
+/*
+ * Issue marker command.
+ * Function issues marker IOCB.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * bus = SCSI BUS number
+ * id = SCSI ID
+ * lun = SCSI LUN
+ * type = marker modifier
+ */
+static void
+qla1280_marker(struct scsi_qla_host *ha, int bus, int id, int lun, u8 type)
+{
+ struct mrk_entry *pkt;
+
+ ENTER("qla1280_marker");
+
+ /* Get request packet. */
+ if ((pkt = (struct mrk_entry *) qla1280_req_pkt(ha))) {
+ pkt->entry_type = MARKER_TYPE;
+ pkt->lun = (uint8_t) lun;
+ pkt->target = (uint8_t) (bus ? (id | BIT_7) : id);
+ pkt->modifier = type;
+ pkt->entry_status = 0;
+
+ /* Issue command to ISP */
+ qla1280_isp_cmd(ha);
+ }
+
+ LEAVE("qla1280_marker");
+}
+
+
+/*
+ * qla1280_64bit_start_scsi
+ * The start SCSI is responsible for building request packets on
+ * request ring and modifying ISP input pointer.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = SB structure pointer.
+ *
+ * Returns:
+ * 0 = success, was able to issue command.
+ */
+#ifdef QLA_64BIT_PTR
+static int
+qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ struct scsi_cmnd *cmd = sp->cmd;
+ cmd_a64_entry_t *pkt;
+ struct scatterlist *sg = NULL;
+ u32 *dword_ptr;
+ dma_addr_t dma_handle;
+ int status = 0;
+ int cnt;
+ int req_cnt;
+ u16 seg_cnt;
+ u8 dir;
+
+ ENTER("qla1280_64bit_start_scsi:");
+
+ /* Calculate number of entries and segments required. */
+ req_cnt = 1;
+ if (cmd->use_sg) {
+ sg = (struct scatterlist *) cmd->request_buffer;
+ seg_cnt = pci_map_sg(ha->pdev, sg, cmd->use_sg,
+ cmd->sc_data_direction);
+
+ if (seg_cnt > 2) {
+ req_cnt += (seg_cnt - 2) / 5;
+ if ((seg_cnt - 2) % 5)
+ req_cnt++;
+ }
+ } else if (cmd->request_bufflen) { /* If data transfer. */
+ seg_cnt = 1;
+ } else {
+ seg_cnt = 0;
+ }
+
+ if ((req_cnt + 2) >= ha->req_q_cnt) {
+ /* Calculate number of free request entries. */
+ cnt = RD_REG_WORD(&reg->mailbox4);
+ if (ha->req_ring_index < cnt)
+ ha->req_q_cnt = cnt - ha->req_ring_index;
+ else
+ ha->req_q_cnt =
+ REQUEST_ENTRY_CNT - (ha->req_ring_index - cnt);
+ }
+
+ /* If room for request in request ring. */
+ if ((req_cnt + 2) >= ha->req_q_cnt) {
+ status = 1;
+ dprintk(2, "qla1280_64bit_start_scsi: in-ptr=0x%x req_q_cnt="
+ "0x%xreq_cnt=0x%x", ha->req_ring_index, ha->req_q_cnt,
+ req_cnt);
+ goto out;
+ }
+
+ /* Check for room in outstanding command list. */
+ for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS &&
+ ha->outstanding_cmds[cnt] != 0; cnt++);
+
+ if (cnt >= MAX_OUTSTANDING_COMMANDS) {
+ status = 1;
+ dprintk(2, "qla1280_64bit_start_scsi: NO ROOM IN "
+ "OUTSTANDING ARRAY, req_q_cnt=0x%x", ha->req_q_cnt);
+ goto out;
+ }
+
+ ha->outstanding_cmds[cnt] = sp;
+ ha->req_q_cnt -= req_cnt;
+ CMD_HANDLE(sp->cmd) = (unsigned char *)(unsigned long)(cnt + 1);
+
+ dprintk(2, "64bit_start: cmd=%p sp=%p CDB=%xm, handle %lx\n", cmd, sp,
+ cmd->cmnd[0], (long)CMD_HANDLE(sp->cmd));
+ dprintk(2, " bus %i, target %i, lun %i\n",
+ SCSI_BUS_32(cmd), SCSI_TCN_32(cmd), SCSI_LUN_32(cmd));
+ qla1280_dump_buffer(2, cmd->cmnd, MAX_COMMAND_SIZE);
+
+ /*
+ * Build command packet.
+ */
+ pkt = (cmd_a64_entry_t *) ha->request_ring_ptr;
+
+ pkt->entry_type = COMMAND_A64_TYPE;
+ pkt->entry_count = (uint8_t) req_cnt;
+ pkt->sys_define = (uint8_t) ha->req_ring_index;
+ pkt->entry_status = 0;
+ pkt->handle = cpu_to_le32(cnt);
+
+ /* Zero out remaining portion of packet. */
+ memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8));
+
+ /* Set ISP command timeout. */
+ pkt->timeout = cpu_to_le16(30);
+
+ /* Set device target ID and LUN */
+ pkt->lun = SCSI_LUN_32(cmd);
+ pkt->target = SCSI_BUS_32(cmd) ?
+ (SCSI_TCN_32(cmd) | BIT_7) : SCSI_TCN_32(cmd);
+
+ /* Enable simple tag queuing if device supports it. */
+ if (DEV_SIMPLE_TAGS(cmd->device))
+ pkt->control_flags |= cpu_to_le16(BIT_3);
+
+ /* Load SCSI command packet. */
+ pkt->cdb_len = cpu_to_le16(CMD_CDBLEN(cmd));
+ memcpy(pkt->scsi_cdb, &(CMD_CDBP(cmd)), CMD_CDBLEN(cmd));
+ /* dprintk(1, "Build packet for command[0]=0x%x\n",pkt->scsi_cdb[0]); */
+
+ /* Set transfer direction. */
+ dir = qla1280_data_direction(cmd);
+ pkt->control_flags |= cpu_to_le16(dir);
+
+ /* Set total data segment count. */
+ pkt->dseg_count = cpu_to_le16(seg_cnt);
+
+ /*
+ * Load data segments.
+ */
+ if (seg_cnt) { /* If data transfer. */
+ /* Setup packet address segment pointer. */
+ dword_ptr = (u32 *)&pkt->dseg_0_address;
+
+ if (cmd->use_sg) { /* If scatter gather */
+ /* Load command entry data segments. */
+ for (cnt = 0; cnt < 2 && seg_cnt; cnt++, seg_cnt--) {
+ dma_handle = sg_dma_address(sg);
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+ if (ha->flags.use_pci_vchannel)
+ sn_pci_set_vchan(ha->pdev,
+ (unsigned long *)&dma_handle,
+ SCSI_BUS_32(cmd));
+#endif
+ *dword_ptr++ =
+ cpu_to_le32(pci_dma_lo32(dma_handle));
+ *dword_ptr++ =
+ cpu_to_le32(pci_dma_hi32(dma_handle));
+ *dword_ptr++ = cpu_to_le32(sg_dma_len(sg));
+ sg++;
+ dprintk(3, "S/G Segment phys_addr=%x %x, len=0x%x\n",
+ cpu_to_le32(pci_dma_hi32(dma_handle)),
+ cpu_to_le32(pci_dma_lo32(dma_handle)),
+ cpu_to_le32(sg_dma_len(sg)));
+ }
+ dprintk(5, "qla1280_64bit_start_scsi: Scatter/gather "
+ "command packet data - b %i, t %i, l %i \n",
+ SCSI_BUS_32(cmd), SCSI_TCN_32(cmd),
+ SCSI_LUN_32(cmd));
+ qla1280_dump_buffer(5, (char *)pkt,
+ REQUEST_ENTRY_SIZE);
+
+ /*
+ * Build continuation packets.
+ */
+ dprintk(3, "S/G Building Continuation...seg_cnt=0x%x "
+ "remains\n", seg_cnt);
+
+ while (seg_cnt > 0) {
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr =
+ ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ pkt = (cmd_a64_entry_t *)ha->request_ring_ptr;
+
+ /* Zero out packet. */
+ memset(pkt, 0, REQUEST_ENTRY_SIZE);
+
+ /* Load packet defaults. */
+ ((struct cont_a64_entry *) pkt)->entry_type =
+ CONTINUE_A64_TYPE;
+ ((struct cont_a64_entry *) pkt)->entry_count = 1;
+ ((struct cont_a64_entry *) pkt)->sys_define =
+ (uint8_t)ha->req_ring_index;
+ /* Setup packet address segment pointer. */
+ dword_ptr =
+ (u32 *)&((struct cont_a64_entry *) pkt)->dseg_0_address;
+
+ /* Load continuation entry data segments. */
+ for (cnt = 0; cnt < 5 && seg_cnt;
+ cnt++, seg_cnt--) {
+ dma_handle = sg_dma_address(sg);
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+ if (ha->flags.use_pci_vchannel)
+ sn_pci_set_vchan(ha->pdev,
+ (unsigned long *)&dma_handle,
+ SCSI_BUS_32(cmd));
+#endif
+ *dword_ptr++ =
+ cpu_to_le32(pci_dma_lo32(dma_handle));
+ *dword_ptr++ =
+ cpu_to_le32(pci_dma_hi32(dma_handle));
+ *dword_ptr++ =
+ cpu_to_le32(sg_dma_len(sg));
+ dprintk(3, "S/G Segment Cont. phys_addr=%x %x, len=0x%x\n",
+ cpu_to_le32(pci_dma_hi32(dma_handle)),
+ cpu_to_le32(pci_dma_lo32(dma_handle)),
+ cpu_to_le32(sg_dma_len(sg)));
+ sg++;
+ }
+ dprintk(5, "qla1280_64bit_start_scsi: "
+ "continuation packet data - b %i, t "
+ "%i, l %i \n", SCSI_BUS_32(cmd),
+ SCSI_TCN_32(cmd), SCSI_LUN_32(cmd));
+ qla1280_dump_buffer(5, (char *)pkt,
+ REQUEST_ENTRY_SIZE);
+ }
+ } else { /* No scatter gather data transfer */
+ dma_handle = pci_map_single(ha->pdev,
+ cmd->request_buffer,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+
+ sp->saved_dma_handle = dma_handle;
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+ if (ha->flags.use_pci_vchannel)
+ sn_pci_set_vchan(ha->pdev,
+ (unsigned long *)&dma_handle,
+ SCSI_BUS_32(cmd));
+#endif
+ *dword_ptr++ = cpu_to_le32(pci_dma_lo32(dma_handle));
+ *dword_ptr++ = cpu_to_le32(pci_dma_hi32(dma_handle));
+ *dword_ptr = cpu_to_le32(cmd->request_bufflen);
+
+ dprintk(5, "qla1280_64bit_start_scsi: No scatter/"
+ "gather command packet data - b %i, t %i, "
+ "l %i \n", SCSI_BUS_32(cmd), SCSI_TCN_32(cmd),
+ SCSI_LUN_32(cmd));
+ qla1280_dump_buffer(5, (char *)pkt,
+ REQUEST_ENTRY_SIZE);
+ }
+ } else { /* No data transfer */
+ dprintk(5, "qla1280_64bit_start_scsi: No data, command "
+ "packet data - b %i, t %i, l %i \n",
+ SCSI_BUS_32(cmd), SCSI_TCN_32(cmd), SCSI_LUN_32(cmd));
+ qla1280_dump_buffer(5, (char *)pkt, REQUEST_ENTRY_SIZE);
+ }
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ /* Set chip new ring index. */
+ dprintk(2,
+ "qla1280_64bit_start_scsi: Wakeup RISC for pending command\n");
+ sp->flags |= SRB_SENT;
+ ha->actthreads++;
+ WRT_REG_WORD(&reg->mailbox4, ha->req_ring_index);
+ /* Enforce mmio write ordering; see comment in qla1280_isp_cmd(). */
+ mmiowb();
+
+ out:
+ if (status)
+ dprintk(2, "qla1280_64bit_start_scsi: **** FAILED ****\n");
+ else
+ dprintk(3, "qla1280_64bit_start_scsi: exiting normally\n");
+
+ return status;
+}
+#else /* !QLA_64BIT_PTR */
+
+/*
+ * qla1280_32bit_start_scsi
+ * The start SCSI is responsible for building request packets on
+ * request ring and modifying ISP input pointer.
+ *
+ * The Qlogic firmware interface allows every queue slot to have a SCSI
+ * command and up to 4 scatter/gather (SG) entries. If we need more
+ * than 4 SG entries, then continuation entries are used that can
+ * hold another 7 entries each. The start routine determines if there
+ * is eought empty slots then build the combination of requests to
+ * fulfill the OS request.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = SCSI Request Block structure pointer.
+ *
+ * Returns:
+ * 0 = success, was able to issue command.
+ */
+static int
+qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ struct scsi_cmnd *cmd = sp->cmd;
+ struct cmd_entry *pkt;
+ struct scatterlist *sg = NULL;
+ uint32_t *dword_ptr;
+ int status = 0;
+ int cnt;
+ int req_cnt;
+ uint16_t seg_cnt;
+ dma_addr_t dma_handle;
+ u8 dir;
+
+ ENTER("qla1280_32bit_start_scsi");
+
+ dprintk(1, "32bit_start: cmd=%p sp=%p CDB=%x\n", cmd, sp,
+ cmd->cmnd[0]);
+
+ /* Calculate number of entries and segments required. */
+ req_cnt = 1;
+ if (cmd->use_sg) {
+ /*
+ * We must build an SG list in adapter format, as the kernel's
+ * SG list cannot be used directly because of data field size
+ * (__alpha__) differences and the kernel SG list uses virtual
+ * addresses where we need physical addresses.
+ */
+ sg = (struct scatterlist *) cmd->request_buffer;
+ seg_cnt = pci_map_sg(ha->pdev, sg, cmd->use_sg,
+ cmd->sc_data_direction);
+
+ /*
+ * if greater than four sg entries then we need to allocate
+ * continuation entries
+ */
+ if (seg_cnt > 4) {
+ req_cnt += (seg_cnt - 4) / 7;
+ if ((seg_cnt - 4) % 7)
+ req_cnt++;
+ }
+ dprintk(3, "S/G Transfer cmd=%p seg_cnt=0x%x, req_cnt=%x\n",
+ cmd, seg_cnt, req_cnt);
+ } else if (cmd->request_bufflen) { /* If data transfer. */
+ dprintk(3, "No S/G transfer t=%x cmd=%p len=%x CDB=%x\n",
+ SCSI_TCN_32(cmd), cmd, cmd->request_bufflen,
+ cmd->cmnd[0]);
+ seg_cnt = 1;
+ } else {
+ /* dprintk(1, "No data transfer \n"); */
+ seg_cnt = 0;
+ }
+
+ if ((req_cnt + 2) >= ha->req_q_cnt) {
+ /* Calculate number of free request entries. */
+ cnt = RD_REG_WORD(&reg->mailbox4);
+ if (ha->req_ring_index < cnt)
+ ha->req_q_cnt = cnt - ha->req_ring_index;
+ else
+ ha->req_q_cnt =
+ REQUEST_ENTRY_CNT - (ha->req_ring_index - cnt);
+ }
+
+ dprintk(3, "Number of free entries=(%d) seg_cnt=0x%x\n",
+ ha->req_q_cnt, seg_cnt);
+ /* If room for request in request ring. */
+ if ((req_cnt + 2) >= ha->req_q_cnt) {
+ status = 1;
+ dprintk(2, "qla1280_32bit_start_scsi: in-ptr=0x%x, "
+ "req_q_cnt=0x%x, req_cnt=0x%x", ha->req_ring_index,
+ ha->req_q_cnt, req_cnt);
+ goto out;
+ }
+
+ /* Check for empty slot in outstanding command list. */
+ for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS &&
+ (ha->outstanding_cmds[cnt] != 0); cnt++) ;
+
+ if (cnt >= MAX_OUTSTANDING_COMMANDS) {
+ status = 1;
+ dprintk(2, "qla1280_32bit_start_scsi: NO ROOM IN OUTSTANDING "
+ "ARRAY, req_q_cnt=0x%x\n", ha->req_q_cnt);
+ goto out;
+ }
+
+ CMD_HANDLE(sp->cmd) = (unsigned char *) (unsigned long)(cnt + 1);
+ ha->outstanding_cmds[cnt] = sp;
+ ha->req_q_cnt -= req_cnt;
+
+ /*
+ * Build command packet.
+ */
+ pkt = (struct cmd_entry *) ha->request_ring_ptr;
+
+ pkt->entry_type = COMMAND_TYPE;
+ pkt->entry_count = (uint8_t) req_cnt;
+ pkt->sys_define = (uint8_t) ha->req_ring_index;
+ pkt->entry_status = 0;
+ pkt->handle = cpu_to_le32(cnt);
+
+ /* Zero out remaining portion of packet. */
+ memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8));
+
+ /* Set ISP command timeout. */
+ pkt->timeout = cpu_to_le16(30);
+
+ /* Set device target ID and LUN */
+ pkt->lun = SCSI_LUN_32(cmd);
+ pkt->target = SCSI_BUS_32(cmd) ?
+ (SCSI_TCN_32(cmd) | BIT_7) : SCSI_TCN_32(cmd);
+
+ /* Enable simple tag queuing if device supports it. */
+ if (DEV_SIMPLE_TAGS(cmd->device))
+ pkt->control_flags |= cpu_to_le16(BIT_3);
+
+ /* Load SCSI command packet. */
+ pkt->cdb_len = cpu_to_le16(CMD_CDBLEN(cmd));
+ memcpy(pkt->scsi_cdb, &(CMD_CDBP(cmd)), CMD_CDBLEN(cmd));
+
+ /*dprintk(1, "Build packet for command[0]=0x%x\n",pkt->scsi_cdb[0]); */
+ /* Set transfer direction. */
+ dir = qla1280_data_direction(cmd);
+ pkt->control_flags |= cpu_to_le16(dir);
+
+ /* Set total data segment count. */
+ pkt->dseg_count = cpu_to_le16(seg_cnt);
+
+ /*
+ * Load data segments.
+ */
+ if (seg_cnt) {
+ /* Setup packet address segment pointer. */
+ dword_ptr = &pkt->dseg_0_address;
+
+ if (cmd->use_sg) { /* If scatter gather */
+ dprintk(3, "Building S/G data segments..\n");
+ qla1280_dump_buffer(1, (char *)sg, 4 * 16);
+
+ /* Load command entry data segments. */
+ for (cnt = 0; cnt < 4 && seg_cnt; cnt++, seg_cnt--) {
+ *dword_ptr++ =
+ cpu_to_le32(pci_dma_lo32(sg_dma_address(sg)));
+ *dword_ptr++ =
+ cpu_to_le32(sg_dma_len(sg));
+ dprintk(3, "S/G Segment phys_addr=0x%lx, len=0x%x\n",
+ (pci_dma_lo32(sg_dma_address(sg))),
+ (sg_dma_len(sg)));
+ sg++;
+ }
+ /*
+ * Build continuation packets.
+ */
+ dprintk(3, "S/G Building Continuation"
+ "...seg_cnt=0x%x remains\n", seg_cnt);
+ while (seg_cnt > 0) {
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr =
+ ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ pkt = (struct cmd_entry *)ha->request_ring_ptr;
+
+ /* Zero out packet. */
+ memset(pkt, 0, REQUEST_ENTRY_SIZE);
+
+ /* Load packet defaults. */
+ ((struct cont_entry *) pkt)->
+ entry_type = CONTINUE_TYPE;
+ ((struct cont_entry *) pkt)->entry_count = 1;
+
+ ((struct cont_entry *) pkt)->sys_define =
+ (uint8_t) ha->req_ring_index;
+
+ /* Setup packet address segment pointer. */
+ dword_ptr =
+ &((struct cont_entry *) pkt)->dseg_0_address;
+
+ /* Load continuation entry data segments. */
+ for (cnt = 0; cnt < 7 && seg_cnt;
+ cnt++, seg_cnt--) {
+ *dword_ptr++ =
+ cpu_to_le32(pci_dma_lo32(sg_dma_address(sg)));
+ *dword_ptr++ =
+ cpu_to_le32(sg_dma_len(sg));
+ dprintk(1,
+ "S/G Segment Cont. phys_addr=0x%x, "
+ "len=0x%x\n",
+ cpu_to_le32(pci_dma_lo32(sg_dma_address(sg))),
+ cpu_to_le32(sg_dma_len(sg)));
+ sg++;
+ }
+ dprintk(5, "qla1280_32bit_start_scsi: "
+ "continuation packet data - "
+ "scsi(%i:%i:%i)\n", SCSI_BUS_32(cmd),
+ SCSI_TCN_32(cmd), SCSI_LUN_32(cmd));
+ qla1280_dump_buffer(5, (char *)pkt,
+ REQUEST_ENTRY_SIZE);
+ }
+ } else { /* No S/G data transfer */
+ dma_handle = pci_map_single(ha->pdev,
+ cmd->request_buffer,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+ sp->saved_dma_handle = dma_handle;
+
+ *dword_ptr++ = cpu_to_le32(pci_dma_lo32(dma_handle));
+ *dword_ptr = cpu_to_le32(cmd->request_bufflen);
+ }
+ } else { /* No data transfer at all */
+ dprintk(5, "qla1280_32bit_start_scsi: No data, command "
+ "packet data - \n");
+ qla1280_dump_buffer(5, (char *)pkt, REQUEST_ENTRY_SIZE);
+ }
+ dprintk(5, "qla1280_32bit_start_scsi: First IOCB block:\n");
+ qla1280_dump_buffer(5, (char *)ha->request_ring_ptr,
+ REQUEST_ENTRY_SIZE);
+
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ /* Set chip new ring index. */
+ dprintk(2, "qla1280_32bit_start_scsi: Wakeup RISC "
+ "for pending command\n");
+ sp->flags |= SRB_SENT;
+ ha->actthreads++;
+ WRT_REG_WORD(&reg->mailbox4, ha->req_ring_index);
+ /* Enforce mmio write ordering; see comment in qla1280_isp_cmd(). */
+ mmiowb();
+
+out:
+ if (status)
+ dprintk(2, "qla1280_32bit_start_scsi: **** FAILED ****\n");
+
+ LEAVE("qla1280_32bit_start_scsi");
+
+ return status;
+}
+#endif
+
+/*
+ * qla1280_req_pkt
+ * Function is responsible for locking ring and
+ * getting a zeroed out request packet.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = failed to get slot.
+ */
+static request_t *
+qla1280_req_pkt(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ request_t *pkt = NULL;
+ int cnt;
+ uint32_t timer;
+
+ ENTER("qla1280_req_pkt");
+
+ /*
+ * This can be called from interrupt context, damn it!!!
+ */
+ /* Wait for 30 seconds for slot. */
+ for (timer = 15000000; timer; timer--) {
+ if (ha->req_q_cnt > 0) {
+ /* Calculate number of free request entries. */
+ cnt = RD_REG_WORD(&reg->mailbox4);
+ if (ha->req_ring_index < cnt)
+ ha->req_q_cnt = cnt - ha->req_ring_index;
+ else
+ ha->req_q_cnt =
+ REQUEST_ENTRY_CNT - (ha->req_ring_index - cnt);
+ }
+
+ /* Found empty request ring slot? */
+ if (ha->req_q_cnt > 0) {
+ ha->req_q_cnt--;
+ pkt = ha->request_ring_ptr;
+
+ /* Zero out packet. */
+ memset(pkt, 0, REQUEST_ENTRY_SIZE);
+
+ /*
+ * How can this be right when we have a ring
+ * size of 512???
+ */
+ /* Set system defined field. */
+ pkt->sys_define = (uint8_t) ha->req_ring_index;
+
+ /* Set entry count. */
+ pkt->entry_count = 1;
+
+ break;
+ }
+
+ udelay(2); /* 10 */
+
+ /* Check for pending interrupts. */
+ qla1280_poll(ha);
+ }
+
+ if (!pkt)
+ dprintk(2, "qla1280_req_pkt: **** FAILED ****\n");
+ else
+ dprintk(3, "qla1280_req_pkt: exiting normally\n");
+
+ return pkt;
+}
+
+/*
+ * qla1280_isp_cmd
+ * Function is responsible for modifying ISP input pointer.
+ * Releases ring lock.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ */
+static void
+qla1280_isp_cmd(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+
+ ENTER("qla1280_isp_cmd");
+
+ dprintk(5, "qla1280_isp_cmd: IOCB data:\n");
+ qla1280_dump_buffer(5, (char *)ha->request_ring_ptr,
+ REQUEST_ENTRY_SIZE);
+
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ /*
+ * Update request index to mailbox4 (Request Queue In).
+ * The mmiowb() ensures that this write is ordered with writes by other
+ * CPUs. Without the mmiowb(), it is possible for the following:
+ * CPUA posts write of index 5 to mailbox4
+ * CPUA releases host lock
+ * CPUB acquires host lock
+ * CPUB posts write of index 6 to mailbox4
+ * On PCI bus, order reverses and write of 6 posts, then index 5,
+ * causing chip to issue full queue of stale commands
+ * The mmiowb() prevents future writes from crossing the barrier.
+ * See Documentation/DocBook/deviceiobook.tmpl for more information.
+ */
+ WRT_REG_WORD(&reg->mailbox4, ha->req_ring_index);
+ mmiowb();
+
+ LEAVE("qla1280_isp_cmd");
+}
+
+/****************************************************************************/
+/* Interrupt Service Routine. */
+/****************************************************************************/
+
+/****************************************************************************
+ * qla1280_isr
+ * Calls I/O done on command completion.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * done_q = done queue.
+ ****************************************************************************/
+static void
+qla1280_isr(struct scsi_qla_host *ha, struct list_head *done_q)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ struct response *pkt;
+ struct srb *sp = NULL;
+ uint16_t mailbox[MAILBOX_REGISTER_COUNT];
+ uint16_t *wptr;
+ uint32_t index;
+ u16 istatus;
+
+ ENTER("qla1280_isr");
+
+ istatus = RD_REG_WORD(&reg->istatus);
+ if (!(istatus & (RISC_INT | PCI_INT)))
+ return;
+
+ /* Save mailbox register 5 */
+ mailbox[5] = RD_REG_WORD(&reg->mailbox5);
+
+ /* Check for mailbox interrupt. */
+
+ mailbox[0] = RD_REG_WORD_dmasync(&reg->semaphore);
+
+ if (mailbox[0] & BIT_0) {
+ /* Get mailbox data. */
+ /* dprintk(1, "qla1280_isr: In Get mailbox data \n"); */
+
+ wptr = &mailbox[0];
+ *wptr++ = RD_REG_WORD(&reg->mailbox0);
+ *wptr++ = RD_REG_WORD(&reg->mailbox1);
+ *wptr = RD_REG_WORD(&reg->mailbox2);
+ if (mailbox[0] != MBA_SCSI_COMPLETION) {
+ wptr++;
+ *wptr++ = RD_REG_WORD(&reg->mailbox3);
+ *wptr++ = RD_REG_WORD(&reg->mailbox4);
+ wptr++;
+ *wptr++ = RD_REG_WORD(&reg->mailbox6);
+ *wptr = RD_REG_WORD(&reg->mailbox7);
+ }
+
+ /* Release mailbox registers. */
+
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->host_cmd, HC_CLR_RISC_INT);
+
+ dprintk(5, "qla1280_isr: mailbox interrupt mailbox[0] = 0x%x",
+ mailbox[0]);
+
+ /* Handle asynchronous event */
+ switch (mailbox[0]) {
+ case MBA_SCSI_COMPLETION: /* Response completion */
+ dprintk(5, "qla1280_isr: mailbox SCSI response "
+ "completion\n");
+
+ if (ha->flags.online) {
+ /* Get outstanding command index. */
+ index = mailbox[2] << 16 | mailbox[1];
+
+ /* Validate handle. */
+ if (index < MAX_OUTSTANDING_COMMANDS)
+ sp = ha->outstanding_cmds[index];
+ else
+ sp = NULL;
+
+ if (sp) {
+ /* Free outstanding command slot. */
+ ha->outstanding_cmds[index] = NULL;
+
+ /* Save ISP completion status */
+ CMD_RESULT(sp->cmd) = 0;
+
+ /* Place block on done queue */
+ list_add_tail(&sp->list, done_q);
+ } else {
+ /*
+ * If we get here we have a real problem!
+ */
+ printk(KERN_WARNING
+ "qla1280: ISP invalid handle");
+ }
+ }
+ break;
+
+ case MBA_BUS_RESET: /* SCSI Bus Reset */
+ ha->flags.reset_marker = 1;
+ index = mailbox[6] & BIT_0;
+ ha->bus_settings[index].reset_marker = 1;
+
+ printk(KERN_DEBUG "qla1280_isr(): index %i "
+ "asynchronous BUS_RESET\n", index);
+ break;
+
+ case MBA_SYSTEM_ERR: /* System Error */
+ printk(KERN_WARNING
+ "qla1280: ISP System Error - mbx1=%xh, mbx2="
+ "%xh, mbx3=%xh\n", mailbox[1], mailbox[2],
+ mailbox[3]);
+ break;
+
+ case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
+ printk(KERN_WARNING
+ "qla1280: ISP Request Transfer Error\n");
+ break;
+
+ case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
+ printk(KERN_WARNING
+ "qla1280: ISP Response Transfer Error\n");
+ break;
+
+ case MBA_WAKEUP_THRES: /* Request Queue Wake-up */
+ dprintk(2, "qla1280_isr: asynchronous WAKEUP_THRES\n");
+ break;
+
+ case MBA_TIMEOUT_RESET: /* Execution Timeout Reset */
+ dprintk(2,
+ "qla1280_isr: asynchronous TIMEOUT_RESET\n");
+ break;
+
+ case MBA_DEVICE_RESET: /* Bus Device Reset */
+ printk(KERN_INFO "qla1280_isr(): asynchronous "
+ "BUS_DEVICE_RESET\n");
+
+ ha->flags.reset_marker = 1;
+ index = mailbox[6] & BIT_0;
+ ha->bus_settings[index].reset_marker = 1;
+ break;
+
+ case MBA_BUS_MODE_CHANGE:
+ dprintk(2,
+ "qla1280_isr: asynchronous BUS_MODE_CHANGE\n");
+ break;
+
+ default:
+ /* dprintk(1, "qla1280_isr: default case of switch MB \n"); */
+ if (mailbox[0] < MBA_ASYNC_EVENT) {
+ wptr = &mailbox[0];
+ memcpy((uint16_t *) ha->mailbox_out, wptr,
+ MAILBOX_REGISTER_COUNT *
+ sizeof(uint16_t));
+
+ if(ha->mailbox_wait != NULL)
+ complete(ha->mailbox_wait);
+ }
+ break;
+ }
+ } else {
+ WRT_REG_WORD(&reg->host_cmd, HC_CLR_RISC_INT);
+ }
+
+ /*
+ * We will receive interrupts during mailbox testing prior to
+ * the card being marked online, hence the double check.
+ */
+ if (!(ha->flags.online && !ha->mailbox_wait)) {
+ dprintk(2, "qla1280_isr: Response pointer Error\n");
+ goto out;
+ }
+
+ if (mailbox[5] >= RESPONSE_ENTRY_CNT)
+ goto out;
+
+ while (ha->rsp_ring_index != mailbox[5]) {
+ pkt = ha->response_ring_ptr;
+
+ dprintk(5, "qla1280_isr: ha->rsp_ring_index = 0x%x, mailbox[5]"
+ " = 0x%x\n", ha->rsp_ring_index, mailbox[5]);
+ dprintk(5,"qla1280_isr: response packet data\n");
+ qla1280_dump_buffer(5, (char *)pkt, RESPONSE_ENTRY_SIZE);
+
+ if (pkt->entry_type == STATUS_TYPE) {
+ if ((le16_to_cpu(pkt->scsi_status) & 0xff)
+ || pkt->comp_status || pkt->entry_status) {
+ dprintk(2, "qla1280_isr: ha->rsp_ring_index = "
+ "0x%x mailbox[5] = 0x%x, comp_status "
+ "= 0x%x, scsi_status = 0x%x\n",
+ ha->rsp_ring_index, mailbox[5],
+ le16_to_cpu(pkt->comp_status),
+ le16_to_cpu(pkt->scsi_status));
+ }
+ } else {
+ dprintk(2, "qla1280_isr: ha->rsp_ring_index = "
+ "0x%x, mailbox[5] = 0x%x\n",
+ ha->rsp_ring_index, mailbox[5]);
+ dprintk(2, "qla1280_isr: response packet data\n");
+ qla1280_dump_buffer(2, (char *)pkt,
+ RESPONSE_ENTRY_SIZE);
+ }
+
+ if (pkt->entry_type == STATUS_TYPE || pkt->entry_status) {
+ dprintk(2, "status: Cmd %p, handle %i\n",
+ ha->outstanding_cmds[pkt->handle]->cmd,
+ pkt->handle);
+ if (pkt->entry_type == STATUS_TYPE)
+ qla1280_status_entry(ha, pkt, done_q);
+ else
+ qla1280_error_entry(ha, pkt, done_q);
+ /* Adjust ring index. */
+ ha->rsp_ring_index++;
+ if (ha->rsp_ring_index == RESPONSE_ENTRY_CNT) {
+ ha->rsp_ring_index = 0;
+ ha->response_ring_ptr = ha->response_ring;
+ } else
+ ha->response_ring_ptr++;
+ WRT_REG_WORD(&reg->mailbox5, ha->rsp_ring_index);
+ }
+ }
+
+ out:
+ LEAVE("qla1280_isr");
+}
+
+/*
+ * qla1280_rst_aen
+ * Processes asynchronous reset.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ */
+static void
+qla1280_rst_aen(struct scsi_qla_host *ha)
+{
+ uint8_t bus;
+
+ ENTER("qla1280_rst_aen");
+
+ if (ha->flags.online && !ha->flags.reset_active &&
+ !ha->flags.abort_isp_active) {
+ ha->flags.reset_active = 1;
+ while (ha->flags.reset_marker) {
+ /* Issue marker command. */
+ ha->flags.reset_marker = 0;
+ for (bus = 0; bus < ha->ports &&
+ !ha->flags.reset_marker; bus++) {
+ if (ha->bus_settings[bus].reset_marker) {
+ ha->bus_settings[bus].reset_marker = 0;
+ qla1280_marker(ha, bus, 0, 0,
+ MK_SYNC_ALL);
+ }
+ }
+ }
+ }
+
+ LEAVE("qla1280_rst_aen");
+}
+
+
+#if LINUX_VERSION_CODE < 0x020500
+/*
+ *
+ */
+static void
+qla1280_get_target_options(struct scsi_cmnd *cmd, struct scsi_qla_host *ha)
+{
+ unsigned char *result;
+ struct nvram *n;
+ int bus, target, lun;
+
+ bus = SCSI_BUS_32(cmd);
+ target = SCSI_TCN_32(cmd);
+ lun = SCSI_LUN_32(cmd);
+
+ /*
+ * Make sure to not touch anything if someone is using the
+ * sg interface.
+ */
+ if (cmd->use_sg || (CMD_RESULT(cmd) >> 16) != DID_OK || lun)
+ return;
+
+ result = cmd->request_buffer;
+ n = &ha->nvram;
+
+ n->bus[bus].target[target].parameter.f.enable_wide = 0;
+ n->bus[bus].target[target].parameter.f.enable_sync = 0;
+ n->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 0;
+
+ if (result[7] & 0x60)
+ n->bus[bus].target[target].parameter.f.enable_wide = 1;
+ if (result[7] & 0x10)
+ n->bus[bus].target[target].parameter.f.enable_sync = 1;
+ if ((result[2] >= 3) && (result[4] + 5 > 56) &&
+ (result[56] & 0x4))
+ n->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 1;
+
+ dprintk(2, "get_target_options(): wide %i, sync %i, ppr %i\n",
+ n->bus[bus].target[target].parameter.f.enable_wide,
+ n->bus[bus].target[target].parameter.f.enable_sync,
+ n->bus[bus].target[target].ppr_1x160.flags.enable_ppr);
+}
+#endif
+
+/*
+ * qla1280_status_entry
+ * Processes received ISP status entry.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * pkt = entry pointer.
+ * done_q = done queue.
+ */
+static void
+qla1280_status_entry(struct scsi_qla_host *ha, struct response *pkt,
+ struct list_head *done_q)
+{
+ unsigned int bus, target, lun;
+ int sense_sz;
+ struct srb *sp;
+ struct scsi_cmnd *cmd;
+ uint32_t handle = le32_to_cpu(pkt->handle);
+ uint16_t scsi_status = le16_to_cpu(pkt->scsi_status);
+ uint16_t comp_status = le16_to_cpu(pkt->comp_status);
+
+ ENTER("qla1280_status_entry");
+
+ /* Validate handle. */
+ if (handle < MAX_OUTSTANDING_COMMANDS)
+ sp = ha->outstanding_cmds[handle];
+ else
+ sp = NULL;
+
+ if (!sp) {
+ printk(KERN_WARNING "qla1280: Status Entry invalid handle\n");
+ goto out;
+ }
+
+ /* Free outstanding command slot. */
+ ha->outstanding_cmds[handle] = NULL;
+
+ cmd = sp->cmd;
+
+ /* Generate LU queue on cntrl, target, LUN */
+ bus = SCSI_BUS_32(cmd);
+ target = SCSI_TCN_32(cmd);
+ lun = SCSI_LUN_32(cmd);
+
+ if (comp_status || scsi_status) {
+ dprintk(3, "scsi: comp_status = 0x%x, scsi_status = "
+ "0x%x, handle = 0x%x\n", comp_status,
+ scsi_status, handle);
+ }
+
+ /* Target busy */
+ if (scsi_status & SS_BUSY_CONDITION &&
+ scsi_status != SS_RESERVE_CONFLICT) {
+ CMD_RESULT(cmd) =
+ DID_BUS_BUSY << 16 | (scsi_status & 0xff);
+ } else {
+
+ /* Save ISP completion status */
+ CMD_RESULT(cmd) = qla1280_return_status(pkt, cmd);
+
+ if (scsi_status & SS_CHECK_CONDITION) {
+ if (comp_status != CS_ARS_FAILED) {
+ uint16_t req_sense_length =
+ le16_to_cpu(pkt->req_sense_length);
+ if (req_sense_length < CMD_SNSLEN(cmd))
+ sense_sz = req_sense_length;
+ else
+ /*
+ * scsi_cmnd->sense_buffer is
+ * 64 bytes, why only copy 63?
+ * This looks wrong! /Jes
+ */
+ sense_sz = CMD_SNSLEN(cmd) - 1;
+
+ memcpy(cmd->sense_buffer,
+ &pkt->req_sense_data, sense_sz);
+ } else
+ sense_sz = 0;
+ memset(cmd->sense_buffer + sense_sz, 0,
+ sizeof(cmd->sense_buffer) - sense_sz);
+
+ dprintk(2, "qla1280_status_entry: Check "
+ "condition Sense data, b %i, t %i, "
+ "l %i\n", bus, target, lun);
+ if (sense_sz)
+ qla1280_dump_buffer(2,
+ (char *)cmd->sense_buffer,
+ sense_sz);
+ }
+ }
+
+ /* Place command on done queue. */
+ list_add_tail(&sp->list, done_q);
+ out:
+ LEAVE("qla1280_status_entry");
+}
+
+/*
+ * qla1280_error_entry
+ * Processes error entry.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * pkt = entry pointer.
+ * done_q = done queue.
+ */
+static void
+qla1280_error_entry(struct scsi_qla_host *ha, struct response *pkt,
+ struct list_head *done_q)
+{
+ struct srb *sp;
+ uint32_t handle = le32_to_cpu(pkt->handle);
+
+ ENTER("qla1280_error_entry");
+
+ if (pkt->entry_status & BIT_3)
+ dprintk(2, "qla1280_error_entry: BAD PAYLOAD flag error\n");
+ else if (pkt->entry_status & BIT_2)
+ dprintk(2, "qla1280_error_entry: BAD HEADER flag error\n");
+ else if (pkt->entry_status & BIT_1)
+ dprintk(2, "qla1280_error_entry: FULL flag error\n");
+ else
+ dprintk(2, "qla1280_error_entry: UNKNOWN flag error\n");
+
+ /* Validate handle. */
+ if (handle < MAX_OUTSTANDING_COMMANDS)
+ sp = ha->outstanding_cmds[handle];
+ else
+ sp = NULL;
+
+ if (sp) {
+ /* Free outstanding command slot. */
+ ha->outstanding_cmds[handle] = NULL;
+
+ /* Bad payload or header */
+ if (pkt->entry_status & (BIT_3 + BIT_2)) {
+ /* Bad payload or header, set error status. */
+ /* CMD_RESULT(sp->cmd) = CS_BAD_PAYLOAD; */
+ CMD_RESULT(sp->cmd) = DID_ERROR << 16;
+ } else if (pkt->entry_status & BIT_1) { /* FULL flag */
+ CMD_RESULT(sp->cmd) = DID_BUS_BUSY << 16;
+ } else {
+ /* Set error status. */
+ CMD_RESULT(sp->cmd) = DID_ERROR << 16;
+ }
+
+ /* Place command on done queue. */
+ list_add_tail(&sp->list, done_q);
+ }
+#ifdef QLA_64BIT_PTR
+ else if (pkt->entry_type == COMMAND_A64_TYPE) {
+ printk(KERN_WARNING "!qla1280: Error Entry invalid handle");
+ }
+#endif
+
+ LEAVE("qla1280_error_entry");
+}
+
+/*
+ * qla1280_abort_isp
+ * Resets ISP and aborts all outstanding commands.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success
+ */
+static int
+qla1280_abort_isp(struct scsi_qla_host *ha)
+{
+ struct device_reg __iomem *reg = ha->iobase;
+ struct srb *sp;
+ int status = 0;
+ int cnt;
+ int bus;
+
+ ENTER("qla1280_abort_isp");
+
+ if (ha->flags.abort_isp_active || !ha->flags.online)
+ goto out;
+
+ ha->flags.abort_isp_active = 1;
+
+ /* Disable ISP interrupts. */
+ qla1280_disable_intrs(ha);
+ WRT_REG_WORD(&reg->host_cmd, HC_PAUSE_RISC);
+ RD_REG_WORD(&reg->id_l);
+
+ printk(KERN_INFO "scsi(%li): dequeuing outstanding commands\n",
+ ha->host_no);
+ /* Dequeue all commands in outstanding command list. */
+ for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ struct scsi_cmnd *cmd;
+ sp = ha->outstanding_cmds[cnt];
+ if (sp) {
+
+ cmd = sp->cmd;
+ CMD_RESULT(cmd) = DID_RESET << 16;
+
+ sp->cmd = NULL;
+ ha->outstanding_cmds[cnt] = NULL;
+
+ (*cmd->scsi_done)(cmd);
+
+ sp->flags = 0;
+ }
+ }
+
+ status = qla1280_load_firmware(ha);
+ if (status)
+ goto out;
+
+ /* Setup adapter based on NVRAM parameters. */
+ qla1280_nvram_config (ha);
+
+ status = qla1280_init_rings(ha);
+ if (status)
+ goto out;
+
+ /* Issue SCSI reset. */
+ for (bus = 0; bus < ha->ports; bus++)
+ qla1280_bus_reset(ha, bus);
+
+ ha->flags.abort_isp_active = 0;
+ out:
+ if (status) {
+ printk(KERN_WARNING
+ "qla1280: ISP error recovery failed, board disabled");
+ qla1280_reset_adapter(ha);
+ dprintk(2, "qla1280_abort_isp: **** FAILED ****\n");
+ }
+
+ LEAVE("qla1280_abort_isp");
+ return status;
+}
+
+
+/*
+ * qla1280_debounce_register
+ * Debounce register.
+ *
+ * Input:
+ * port = register address.
+ *
+ * Returns:
+ * register value.
+ */
+static u16
+qla1280_debounce_register(volatile u16 __iomem * addr)
+{
+ volatile u16 ret;
+ volatile u16 ret2;
+
+ ret = RD_REG_WORD(addr);
+ ret2 = RD_REG_WORD(addr);
+
+ if (ret == ret2)
+ return ret;
+
+ do {
+ cpu_relax();
+ ret = RD_REG_WORD(addr);
+ ret2 = RD_REG_WORD(addr);
+ } while (ret != ret2);
+
+ return ret;
+}
+
+
+/************************************************************************
+ * qla1280_check_for_dead_scsi_bus *
+ * *
+ * This routine checks for a dead SCSI bus *
+ ************************************************************************/
+#define SET_SXP_BANK 0x0100
+#define SCSI_PHASE_INVALID 0x87FF
+static int
+qla1280_check_for_dead_scsi_bus(struct scsi_qla_host *ha, unsigned int bus)
+{
+ uint16_t config_reg, scsi_control;
+ struct device_reg __iomem *reg = ha->iobase;
+
+ if (ha->bus_settings[bus].scsi_bus_dead) {
+ WRT_REG_WORD(&reg->host_cmd, HC_PAUSE_RISC);
+ config_reg = RD_REG_WORD(&reg->cfg_1);
+ WRT_REG_WORD(&reg->cfg_1, SET_SXP_BANK);
+ scsi_control = RD_REG_WORD(&reg->scsiControlPins);
+ WRT_REG_WORD(&reg->cfg_1, config_reg);
+ WRT_REG_WORD(&reg->host_cmd, HC_RELEASE_RISC);
+
+ if (scsi_control == SCSI_PHASE_INVALID) {
+ ha->bus_settings[bus].scsi_bus_dead = 1;
+#if 0
+ CMD_RESULT(cp) = DID_NO_CONNECT << 16;
+ CMD_HANDLE(cp) = INVALID_HANDLE;
+ /* ha->actthreads--; */
+
+ (*(cp)->scsi_done)(cp);
+#endif
+ return 1; /* bus is dead */
+ } else {
+ ha->bus_settings[bus].scsi_bus_dead = 0;
+ ha->bus_settings[bus].failed_reset_count = 0;
+ }
+ }
+ return 0; /* bus is not dead */
+}
+
+static void
+qla1280_get_target_parameters(struct scsi_qla_host *ha,
+ struct scsi_device *device)
+{
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ int bus, target, lun;
+
+ bus = device->channel;
+ target = device->id;
+ lun = device->lun;
+
+
+ mb[0] = MBC_GET_TARGET_PARAMETERS;
+ mb[1] = (uint16_t) (bus ? target | BIT_7 : target);
+ mb[1] <<= 8;
+ qla1280_mailbox_command(ha, BIT_6 | BIT_3 | BIT_2 | BIT_1 | BIT_0,
+ &mb[0]);
+
+ printk(KERN_INFO "scsi(%li:%d:%d:%d):", ha->host_no, bus, target, lun);
+
+ if (mb[3] != 0) {
+ printk(" Sync: period %d, offset %d",
+ (mb[3] & 0xff), (mb[3] >> 8));
+ if (mb[2] & BIT_13)
+ printk(", Wide");
+ if ((mb[2] & BIT_5) && ((mb[6] >> 8) & 0xff) >= 2)
+ printk(", DT");
+ } else
+ printk(" Async");
+
+ if (DEV_SIMPLE_TAGS(device))
+ printk(", Tagged queuing: depth %d", device->queue_depth);
+ printk("\n");
+}
+
+
+#if DEBUG_QLA1280
+static void
+__qla1280_dump_buffer(char *b, int size)
+{
+ int cnt;
+ u8 c;
+
+ printk(KERN_DEBUG " 0 1 2 3 4 5 6 7 8 9 Ah "
+ "Bh Ch Dh Eh Fh\n");
+ printk(KERN_DEBUG "---------------------------------------------"
+ "------------------\n");
+
+ for (cnt = 0; cnt < size;) {
+ c = *b++;
+
+ printk("0x%02x", c);
+ cnt++;
+ if (!(cnt % 16))
+ printk("\n");
+ else
+ printk(" ");
+ }
+ if (cnt % 16)
+ printk("\n");
+}
+
+/**************************************************************************
+ * ql1280_print_scsi_cmd
+ *
+ **************************************************************************/
+static void
+__qla1280_print_scsi_cmd(struct scsi_cmnd *cmd)
+{
+ struct scsi_qla_host *ha;
+ struct Scsi_Host *host = CMD_HOST(cmd);
+ struct srb *sp;
+ /* struct scatterlist *sg; */
+
+ int i;
+ ha = (struct scsi_qla_host *)host->hostdata;
+
+ sp = (struct srb *)CMD_SP(cmd);
+ printk("SCSI Command @= 0x%p, Handle=0x%p\n", cmd, CMD_HANDLE(cmd));
+ printk(" chan=%d, target = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
+ SCSI_BUS_32(cmd), SCSI_TCN_32(cmd), SCSI_LUN_32(cmd),
+ CMD_CDBLEN(cmd));
+ printk(" CDB = ");
+ for (i = 0; i < cmd->cmd_len; i++) {
+ printk("0x%02x ", cmd->cmnd[i]);
+ }
+ printk(" seg_cnt =%d\n", cmd->use_sg);
+ printk(" request buffer=0x%p, request buffer len=0x%x\n",
+ cmd->request_buffer, cmd->request_bufflen);
+ /* if (cmd->use_sg)
+ {
+ sg = (struct scatterlist *) cmd->request_buffer;
+ printk(" SG buffer: \n");
+ qla1280_dump_buffer(1, (char *)sg, (cmd->use_sg*sizeof(struct scatterlist)));
+ } */
+ printk(" tag=%d, transfersize=0x%x \n",
+ cmd->tag, cmd->transfersize);
+ printk(" Pid=%li, SP=0x%p\n", cmd->pid, CMD_SP(cmd));
+ printk(" underflow size = 0x%x, direction=0x%x\n",
+ cmd->underflow, cmd->sc_data_direction);
+}
+
+/**************************************************************************
+ * ql1280_dump_device
+ *
+ **************************************************************************/
+static void
+ql1280_dump_device(struct scsi_qla_host *ha)
+{
+
+ struct scsi_cmnd *cp;
+ struct srb *sp;
+ int i;
+
+ printk(KERN_DEBUG "Outstanding Commands on controller:\n");
+
+ for (i = 0; i < MAX_OUTSTANDING_COMMANDS; i++) {
+ if ((sp = ha->outstanding_cmds[i]) == NULL)
+ continue;
+ if ((cp = sp->cmd) == NULL)
+ continue;
+ qla1280_print_scsi_cmd(1, cp);
+ }
+}
+#endif
+
+
+enum tokens {
+ TOKEN_NVRAM,
+ TOKEN_SYNC,
+ TOKEN_WIDE,
+ TOKEN_PPR,
+ TOKEN_VERBOSE,
+ TOKEN_DEBUG,
+};
+
+struct setup_tokens {
+ char *token;
+ int val;
+};
+
+static struct setup_tokens setup_token[] __initdata =
+{
+ { "nvram", TOKEN_NVRAM },
+ { "sync", TOKEN_SYNC },
+ { "wide", TOKEN_WIDE },
+ { "ppr", TOKEN_PPR },
+ { "verbose", TOKEN_VERBOSE },
+ { "debug", TOKEN_DEBUG },
+};
+
+
+/**************************************************************************
+ * qla1280_setup
+ *
+ * Handle boot parameters. This really needs to be changed so one
+ * can specify per adapter parameters.
+ **************************************************************************/
+static int __init
+qla1280_setup(char *s)
+{
+ char *cp, *ptr;
+ unsigned long val;
+ int toke;
+
+ cp = s;
+
+ while (cp && (ptr = strchr(cp, ':'))) {
+ ptr++;
+ if (!strcmp(ptr, "yes")) {
+ val = 0x10000;
+ ptr += 3;
+ } else if (!strcmp(ptr, "no")) {
+ val = 0;
+ ptr += 2;
+ } else
+ val = simple_strtoul(ptr, &ptr, 0);
+
+ switch ((toke = qla1280_get_token(cp))) {
+ case TOKEN_NVRAM:
+ if (!val)
+ driver_setup.no_nvram = 1;
+ break;
+ case TOKEN_SYNC:
+ if (!val)
+ driver_setup.no_sync = 1;
+ else if (val != 0x10000)
+ driver_setup.sync_mask = val;
+ break;
+ case TOKEN_WIDE:
+ if (!val)
+ driver_setup.no_wide = 1;
+ else if (val != 0x10000)
+ driver_setup.wide_mask = val;
+ break;
+ case TOKEN_PPR:
+ if (!val)
+ driver_setup.no_ppr = 1;
+ else if (val != 0x10000)
+ driver_setup.ppr_mask = val;
+ break;
+ case TOKEN_VERBOSE:
+ qla1280_verbose = val;
+ break;
+ default:
+ printk(KERN_INFO "qla1280: unknown boot option %s\n",
+ cp);
+ }
+
+ cp = strchr(ptr, ';');
+ if (cp)
+ cp++;
+ else {
+ break;
+ }
+ }
+ return 1;
+}
+
+
+static int
+qla1280_get_token(char *str)
+{
+ char *sep;
+ long ret = -1;
+ int i, len;
+
+ len = sizeof(setup_token)/sizeof(struct setup_tokens);
+
+ sep = strchr(str, ':');
+
+ if (sep) {
+ for (i = 0; i < len; i++){
+
+ if (!strncmp(setup_token[i].token, str, (sep - str))) {
+ ret = setup_token[i].val;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+#if LINUX_VERSION_CODE >= 0x020600
+static struct scsi_host_template qla1280_driver_template = {
+ .module = THIS_MODULE,
+ .proc_name = "qla1280",
+ .name = "Qlogic ISP 1280/12160",
+ .info = qla1280_info,
+ .slave_configure = qla1280_slave_configure,
+ .queuecommand = qla1280_queuecommand,
+ .eh_abort_handler = qla1280_eh_abort,
+ .eh_device_reset_handler= qla1280_eh_device_reset,
+ .eh_bus_reset_handler = qla1280_eh_bus_reset,
+ .eh_host_reset_handler = qla1280_eh_adapter_reset,
+ .bios_param = qla1280_biosparam,
+ .can_queue = 0xfffff,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#else
+static Scsi_Host_Template qla1280_driver_template = {
+ .proc_name = "qla1280",
+ .name = "Qlogic ISP 1280/12160",
+ .detect = qla1280_detect,
+ .release = qla1280_release,
+ .info = qla1280_info,
+ .queuecommand = qla1280_queuecommand,
+ .eh_abort_handler = qla1280_eh_abort,
+ .eh_device_reset_handler= qla1280_eh_device_reset,
+ .eh_bus_reset_handler = qla1280_eh_bus_reset,
+ .eh_host_reset_handler = qla1280_eh_adapter_reset,
+ .bios_param = qla1280_biosparam_old,
+ .can_queue = 0xfffff,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+ .use_new_eh_code = 1,
+};
+#endif
+
+static int __devinit
+qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int devnum = id->driver_data;
+ struct qla_boards *bdp = &ql1280_board_tbl[devnum];
+ struct Scsi_Host *host;
+ struct scsi_qla_host *ha;
+ int error = -ENODEV;
+
+ /* Bypass all AMI SUBSYS VENDOR IDs */
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_AMI) {
+ printk(KERN_INFO
+ "qla1280: Skipping AMI SubSys Vendor ID Chip\n");
+ goto error;
+ }
+
+ printk(KERN_INFO "qla1280: %s found on PCI bus %i, dev %i\n",
+ bdp->name, pdev->bus->number, PCI_SLOT(pdev->devfn));
+
+ if (pci_enable_device(pdev)) {
+ printk(KERN_WARNING
+ "qla1280: Failed to enabled pci device, aborting.\n");
+ goto error;
+ }
+
+ pci_set_master(pdev);
+
+ error = -ENOMEM;
+ host = scsi_host_alloc(&qla1280_driver_template, sizeof(*ha));
+ if (!host) {
+ printk(KERN_WARNING
+ "qla1280: Failed to register host, aborting.\n");
+ goto error_disable_device;
+ }
+
+ ha = (struct scsi_qla_host *)host->hostdata;
+ memset(ha, 0, sizeof(struct scsi_qla_host));
+
+ ha->pdev = pdev;
+ ha->devnum = devnum; /* specifies microcode load address */
+
+#ifdef QLA_64BIT_PTR
+ if (pci_set_dma_mask(ha->pdev, (dma_addr_t) ~ 0ULL)) {
+ if (pci_set_dma_mask(ha->pdev, 0xffffffff)) {
+ printk(KERN_WARNING "scsi(%li): Unable to set a "
+ " suitable DMA mask - aboring\n", ha->host_no);
+ error = -ENODEV;
+ goto error_free_irq;
+ }
+ } else
+ dprintk(2, "scsi(%li): 64 Bit PCI Addressing Enabled\n",
+ ha->host_no);
+#else
+ if (pci_set_dma_mask(ha->pdev, 0xffffffff)) {
+ printk(KERN_WARNING "scsi(%li): Unable to set a "
+ " suitable DMA mask - aboring\n", ha->host_no);
+ error = -ENODEV;
+ goto error_free_irq;
+ }
+#endif
+
+ ha->request_ring = pci_alloc_consistent(ha->pdev,
+ ((REQUEST_ENTRY_CNT + 1) * (sizeof(request_t))),
+ &ha->request_dma);
+ if (!ha->request_ring) {
+ printk(KERN_INFO "qla1280: Failed to get request memory\n");
+ goto error_put_host;
+ }
+
+ ha->response_ring = pci_alloc_consistent(ha->pdev,
+ ((RESPONSE_ENTRY_CNT + 1) * (sizeof(struct response))),
+ &ha->response_dma);
+ if (!ha->response_ring) {
+ printk(KERN_INFO "qla1280: Failed to get response memory\n");
+ goto error_free_request_ring;
+ }
+
+ ha->ports = bdp->numPorts;
+
+ ha->host = host;
+ ha->host_no = host->host_no;
+
+ host->irq = pdev->irq;
+ host->max_channel = bdp->numPorts - 1;
+ host->max_lun = MAX_LUNS - 1;
+ host->max_id = MAX_TARGETS;
+ host->max_sectors = 1024;
+ host->unique_id = host->host_no;
+
+#if LINUX_VERSION_CODE < 0x020545
+ host->select_queue_depths = qla1280_select_queue_depth;
+#endif
+
+ error = -ENODEV;
+
+#if MEMORY_MAPPED_IO
+ ha->mmpbase = ioremap(pci_resource_start(ha->pdev, 1),
+ pci_resource_len(ha->pdev, 1));
+ if (!ha->mmpbase) {
+ printk(KERN_INFO "qla1280: Unable to map I/O memory\n");
+ goto error_free_response_ring;
+ }
+
+ host->base = (unsigned long)ha->mmpbase;
+ ha->iobase = (struct device_reg __iomem *)ha->mmpbase;
+#else
+ host->io_port = pci_resource_start(ha->pdev, 0);
+ if (!request_region(host->io_port, 0xff, "qla1280")) {
+ printk(KERN_INFO "qla1280: Failed to reserve i/o region "
+ "0x%04lx-0x%04lx - already in use\n",
+ host->io_port, host->io_port + 0xff);
+ goto error_free_response_ring;
+ }
+
+ ha->iobase = (struct device_reg *)host->io_port;
+#endif
+
+ INIT_LIST_HEAD(&ha->done_q);
+
+ /* Disable ISP interrupts. */
+ qla1280_disable_intrs(ha);
+
+ if (request_irq(pdev->irq, qla1280_intr_handler, SA_SHIRQ,
+ "qla1280", ha)) {
+ printk("qla1280 : Failed to reserve interrupt %d already "
+ "in use\n", pdev->irq);
+ goto error_release_region;
+ }
+
+ /* load the F/W, read paramaters, and init the H/W */
+ if (qla1280_initialize_adapter(ha)) {
+ printk(KERN_INFO "qla1x160: Failed to initialize adapter\n");
+ goto error_free_irq;
+ }
+
+ /* set our host ID (need to do something about our two IDs) */
+ host->this_id = ha->bus_settings[0].id;
+
+ pci_set_drvdata(pdev, host);
+
+#if LINUX_VERSION_CODE >= 0x020600
+ error = scsi_add_host(host, &pdev->dev);
+ if (error)
+ goto error_disable_adapter;
+ scsi_scan_host(host);
+#else
+ scsi_set_pci_device(host, pdev);
+#endif
+
+ return 0;
+
+#if LINUX_VERSION_CODE >= 0x020600
+ error_disable_adapter:
+ WRT_REG_WORD(&ha->iobase->ictrl, 0);
+#endif
+ error_free_irq:
+ free_irq(pdev->irq, ha);
+ error_release_region:
+#if MEMORY_MAPPED_IO
+ iounmap(ha->mmpbase);
+#else
+ release_region(host->io_port, 0xff);
+#endif
+ error_free_response_ring:
+ pci_free_consistent(ha->pdev,
+ ((RESPONSE_ENTRY_CNT + 1) * (sizeof(struct response))),
+ ha->response_ring, ha->response_dma);
+ error_free_request_ring:
+ pci_free_consistent(ha->pdev,
+ ((REQUEST_ENTRY_CNT + 1) * (sizeof(request_t))),
+ ha->request_ring, ha->request_dma);
+ error_put_host:
+ scsi_host_put(host);
+ error_disable_device:
+ pci_disable_device(pdev);
+ error:
+ return error;
+}
+
+
+static void __devexit
+qla1280_remove_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct scsi_qla_host *ha = (struct scsi_qla_host *)host->hostdata;
+
+#if LINUX_VERSION_CODE >= 0x020600
+ scsi_remove_host(host);
+#endif
+
+ WRT_REG_WORD(&ha->iobase->ictrl, 0);
+
+ free_irq(pdev->irq, ha);
+
+#if MEMORY_MAPPED_IO
+ iounmap(ha->mmpbase);
+#else
+ release_region(host->io_port, 0xff);
+#endif
+
+ pci_free_consistent(ha->pdev,
+ ((REQUEST_ENTRY_CNT + 1) * (sizeof(request_t))),
+ ha->request_ring, ha->request_dma);
+ pci_free_consistent(ha->pdev,
+ ((RESPONSE_ENTRY_CNT + 1) * (sizeof(struct response))),
+ ha->response_ring, ha->response_dma);
+
+ pci_disable_device(pdev);
+
+ scsi_host_put(host);
+}
+
+#if LINUX_VERSION_CODE >= 0x020600
+static struct pci_driver qla1280_pci_driver = {
+ .name = "qla1280",
+ .id_table = qla1280_pci_tbl,
+ .probe = qla1280_probe_one,
+ .remove = __devexit_p(qla1280_remove_one),
+};
+
+static int __init
+qla1280_init(void)
+{
+ if (sizeof(struct srb) > sizeof(struct scsi_pointer)) {
+ printk(KERN_WARNING
+ "qla1280: struct srb too big, aborting\n");
+ return -EINVAL;
+ }
+
+#ifdef MODULE
+ /*
+ * If we are called as a module, the qla1280 pointer may not be null
+ * and it would point to our bootup string, just like on the lilo
+ * command line. IF not NULL, then process this config string with
+ * qla1280_setup
+ *
+ * Boot time Options
+ * To add options at boot time add a line to your lilo.conf file like:
+ * append="qla1280=verbose,max_tags:{{255,255,255,255},{255,255,255,255}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ */
+ if (qla1280)
+ qla1280_setup(qla1280);
+#endif
+
+ return pci_module_init(&qla1280_pci_driver);
+}
+
+static void __exit
+qla1280_exit(void)
+{
+ pci_unregister_driver(&qla1280_pci_driver);
+}
+
+module_init(qla1280_init);
+module_exit(qla1280_exit);
+
+#else
+# define driver_template qla1280_driver_template
+# include "scsi_module.c"
+#endif
+
+MODULE_AUTHOR("Qlogic & Jes Sorensen");
+MODULE_DESCRIPTION("Qlogic ISP SCSI (qla1x80/qla1x160) driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA1280_VERSION);
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/scsi/qla1280.h b/drivers/scsi/qla1280.h
new file mode 100644
index 000000000000..d245ae07518e
--- /dev/null
+++ b/drivers/scsi/qla1280.h
@@ -0,0 +1,1098 @@
+/******************************************************************************
+* QLOGIC LINUX SOFTWARE
+*
+* QLogic ISP1280 (Ultra2) /12160 (Ultra3) SCSI driver
+* Copyright (C) 2000 Qlogic Corporation
+* (www.qlogic.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, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+******************************************************************************/
+
+#ifndef _QLA1280_H
+#define _QLA1280_H
+
+/*
+ * Data bit definitions.
+ */
+#define BIT_0 0x1
+#define BIT_1 0x2
+#define BIT_2 0x4
+#define BIT_3 0x8
+#define BIT_4 0x10
+#define BIT_5 0x20
+#define BIT_6 0x40
+#define BIT_7 0x80
+#define BIT_8 0x100
+#define BIT_9 0x200
+#define BIT_10 0x400
+#define BIT_11 0x800
+#define BIT_12 0x1000
+#define BIT_13 0x2000
+#define BIT_14 0x4000
+#define BIT_15 0x8000
+#define BIT_16 0x10000
+#define BIT_17 0x20000
+#define BIT_18 0x40000
+#define BIT_19 0x80000
+#define BIT_20 0x100000
+#define BIT_21 0x200000
+#define BIT_22 0x400000
+#define BIT_23 0x800000
+#define BIT_24 0x1000000
+#define BIT_25 0x2000000
+#define BIT_26 0x4000000
+#define BIT_27 0x8000000
+#define BIT_28 0x10000000
+#define BIT_29 0x20000000
+#define BIT_30 0x40000000
+#define BIT_31 0x80000000
+
+#if MEMORY_MAPPED_IO
+#define RD_REG_WORD(addr) readw_relaxed(addr)
+#define RD_REG_WORD_dmasync(addr) readw(addr)
+#define WRT_REG_WORD(addr, data) writew(data, addr)
+#else /* MEMORY_MAPPED_IO */
+#define RD_REG_WORD(addr) inw((unsigned long)addr)
+#define RD_REG_WORD_dmasync(addr) RD_REG_WORD(addr)
+#define WRT_REG_WORD(addr, data) outw(data, (unsigned long)addr)
+#endif /* MEMORY_MAPPED_IO */
+
+/*
+ * Host adapter default definitions.
+ */
+#define MAX_BUSES 2 /* 2 */
+#define MAX_B_BITS 1
+
+#define MAX_TARGETS 16 /* 16 */
+#define MAX_T_BITS 4 /* 4 */
+
+#define MAX_LUNS 8 /* 32 */
+#define MAX_L_BITS 3 /* 5 */
+
+/*
+ * Watchdog time quantum
+ */
+#define QLA1280_WDG_TIME_QUANTUM 5 /* In seconds */
+
+/* Command retry count (0-65535) */
+#define COMMAND_RETRY_COUNT 255
+
+/* Maximum outstanding commands in ISP queues */
+#define MAX_OUTSTANDING_COMMANDS 512
+#define INVALID_HANDLE (MAX_OUTSTANDING_COMMANDS + 2)
+
+/* ISP request and response entry counts (37-65535) */
+#define REQUEST_ENTRY_CNT 256 /* Number of request entries. */
+#define RESPONSE_ENTRY_CNT 16 /* Number of response entries. */
+
+/* Number of segments 1 - 65535 */
+#define SG_SEGMENTS 32 /* Cmd entry + 6 continuations */
+
+/*
+ * SCSI Request Block structure (sp) that is placed
+ * on cmd->SCp location of every I/O
+ */
+struct srb {
+ struct list_head list; /* (8/16) LU queue */
+ struct scsi_cmnd *cmd; /* (4/8) SCSI command block */
+ /* NOTE: the sp->cmd will be NULL when this completion is
+ * called, so you should know the scsi_cmnd when using this */
+ struct completion *wait;
+ dma_addr_t saved_dma_handle; /* for unmap of single transfers */
+ uint8_t flags; /* (1) Status flags. */
+ uint8_t dir; /* direction of transfer */
+};
+
+/*
+ * SRB flag definitions
+ */
+#define SRB_TIMEOUT (1 << 0) /* Command timed out */
+#define SRB_SENT (1 << 1) /* Command sent to ISP */
+#define SRB_ABORT_PENDING (1 << 2) /* Command abort sent to device */
+#define SRB_ABORTED (1 << 3) /* Command aborted command already */
+
+/*
+ * ISP I/O Register Set structure definitions.
+ */
+struct device_reg {
+ uint16_t id_l; /* ID low */
+ uint16_t id_h; /* ID high */
+ uint16_t cfg_0; /* Configuration 0 */
+#define ISP_CFG0_HWMSK 0x000f /* Hardware revision mask */
+#define ISP_CFG0_1020 BIT_0 /* ISP1020 */
+#define ISP_CFG0_1020A BIT_1 /* ISP1020A */
+#define ISP_CFG0_1040 BIT_2 /* ISP1040 */
+#define ISP_CFG0_1040A BIT_3 /* ISP1040A */
+#define ISP_CFG0_1040B BIT_4 /* ISP1040B */
+#define ISP_CFG0_1040C BIT_5 /* ISP1040C */
+ uint16_t cfg_1; /* Configuration 1 */
+#define ISP_CFG1_F128 BIT_6 /* 128-byte FIFO threshold */
+#define ISP_CFG1_F64 BIT_4|BIT_5 /* 128-byte FIFO threshold */
+#define ISP_CFG1_F32 BIT_5 /* 128-byte FIFO threshold */
+#define ISP_CFG1_F16 BIT_4 /* 128-byte FIFO threshold */
+#define ISP_CFG1_BENAB BIT_2 /* Global Bus burst enable */
+#define ISP_CFG1_SXP BIT_0 /* SXP register select */
+ uint16_t ictrl; /* Interface control */
+#define ISP_RESET BIT_0 /* ISP soft reset */
+#define ISP_EN_INT BIT_1 /* ISP enable interrupts. */
+#define ISP_EN_RISC BIT_2 /* ISP enable RISC interrupts. */
+#define ISP_FLASH_ENABLE BIT_8 /* Flash BIOS Read/Write enable */
+#define ISP_FLASH_UPPER BIT_9 /* Flash upper bank select */
+ uint16_t istatus; /* Interface status */
+#define PCI_64BIT_SLOT BIT_14 /* PCI 64-bit slot indicator. */
+#define RISC_INT BIT_2 /* RISC interrupt */
+#define PCI_INT BIT_1 /* PCI interrupt */
+ uint16_t semaphore; /* Semaphore */
+ uint16_t nvram; /* NVRAM register. */
+#define NV_DESELECT 0
+#define NV_CLOCK BIT_0
+#define NV_SELECT BIT_1
+#define NV_DATA_OUT BIT_2
+#define NV_DATA_IN BIT_3
+ uint16_t flash_data; /* Flash BIOS data */
+ uint16_t flash_address; /* Flash BIOS address */
+
+ uint16_t unused_1[0x06];
+
+ /* cdma_* and ddma_* are 1040 only */
+ uint16_t cdma_cfg;
+#define CDMA_CONF_SENAB BIT_3 /* SXP to DMA Data enable */
+#define CDMA_CONF_RIRQ BIT_2 /* RISC interrupt enable */
+#define CDMA_CONF_BENAB BIT_1 /* Bus burst enable */
+#define CDMA_CONF_DIR BIT_0 /* DMA direction (0=fifo->host 1=host->fifo) */
+ uint16_t cdma_ctrl;
+ uint16_t cdma_status;
+ uint16_t cdma_fifo_status;
+ uint16_t cdma_count;
+ uint16_t cdma_reserved;
+ uint16_t cdma_address_count_0;
+ uint16_t cdma_address_count_1;
+ uint16_t cdma_address_count_2;
+ uint16_t cdma_address_count_3;
+
+ uint16_t unused_2[0x06];
+
+ uint16_t ddma_cfg;
+#define DDMA_CONF_SENAB BIT_3 /* SXP to DMA Data enable */
+#define DDMA_CONF_RIRQ BIT_2 /* RISC interrupt enable */
+#define DDMA_CONF_BENAB BIT_1 /* Bus burst enable */
+#define DDMA_CONF_DIR BIT_0 /* DMA direction (0=fifo->host 1=host->fifo) */
+ uint16_t ddma_ctrl;
+ uint16_t ddma_status;
+ uint16_t ddma_fifo_status;
+ uint16_t ddma_xfer_count_low;
+ uint16_t ddma_xfer_count_high;
+ uint16_t ddma_addr_count_0;
+ uint16_t ddma_addr_count_1;
+ uint16_t ddma_addr_count_2;
+ uint16_t ddma_addr_count_3;
+
+ uint16_t unused_3[0x0e];
+
+ uint16_t mailbox0; /* Mailbox 0 */
+ uint16_t mailbox1; /* Mailbox 1 */
+ uint16_t mailbox2; /* Mailbox 2 */
+ uint16_t mailbox3; /* Mailbox 3 */
+ uint16_t mailbox4; /* Mailbox 4 */
+ uint16_t mailbox5; /* Mailbox 5 */
+ uint16_t mailbox6; /* Mailbox 6 */
+ uint16_t mailbox7; /* Mailbox 7 */
+
+ uint16_t unused_4[0x20];/* 0x80-0xbf Gap */
+
+ uint16_t host_cmd; /* Host command and control */
+#define HOST_INT BIT_7 /* host interrupt bit */
+#define BIOS_ENABLE BIT_0
+
+ uint16_t unused_5[0x5]; /* 0xc2-0xcb Gap */
+
+ uint16_t gpio_data;
+ uint16_t gpio_enable;
+
+ uint16_t unused_6[0x11]; /* d0-f0 */
+ uint16_t scsiControlPins; /* f2 */
+};
+
+#define MAILBOX_REGISTER_COUNT 8
+
+/*
+ * ISP product identification definitions in mailboxes after reset.
+ */
+#define PROD_ID_1 0x4953
+#define PROD_ID_2 0x0000
+#define PROD_ID_2a 0x5020
+#define PROD_ID_3 0x2020
+#define PROD_ID_4 0x1
+
+/*
+ * ISP host command and control register command definitions
+ */
+#define HC_RESET_RISC 0x1000 /* Reset RISC */
+#define HC_PAUSE_RISC 0x2000 /* Pause RISC */
+#define HC_RELEASE_RISC 0x3000 /* Release RISC from reset. */
+#define HC_SET_HOST_INT 0x5000 /* Set host interrupt */
+#define HC_CLR_HOST_INT 0x6000 /* Clear HOST interrupt */
+#define HC_CLR_RISC_INT 0x7000 /* Clear RISC interrupt */
+#define HC_DISABLE_BIOS 0x9000 /* Disable BIOS. */
+
+/*
+ * ISP mailbox Self-Test status codes
+ */
+#define MBS_FRM_ALIVE 0 /* Firmware Alive. */
+#define MBS_CHKSUM_ERR 1 /* Checksum Error. */
+#define MBS_SHADOW_LD_ERR 2 /* Shadow Load Error. */
+#define MBS_BUSY 4 /* Busy. */
+
+/*
+ * ISP mailbox command complete status codes
+ */
+#define MBS_CMD_CMP 0x4000 /* Command Complete. */
+#define MBS_INV_CMD 0x4001 /* Invalid Command. */
+#define MBS_HOST_INF_ERR 0x4002 /* Host Interface Error. */
+#define MBS_TEST_FAILED 0x4003 /* Test Failed. */
+#define MBS_CMD_ERR 0x4005 /* Command Error. */
+#define MBS_CMD_PARAM_ERR 0x4006 /* Command Parameter Error. */
+
+/*
+ * ISP mailbox asynchronous event status codes
+ */
+#define MBA_ASYNC_EVENT 0x8000 /* Asynchronous event. */
+#define MBA_BUS_RESET 0x8001 /* SCSI Bus Reset. */
+#define MBA_SYSTEM_ERR 0x8002 /* System Error. */
+#define MBA_REQ_TRANSFER_ERR 0x8003 /* Request Transfer Error. */
+#define MBA_RSP_TRANSFER_ERR 0x8004 /* Response Transfer Error. */
+#define MBA_WAKEUP_THRES 0x8005 /* Request Queue Wake-up. */
+#define MBA_TIMEOUT_RESET 0x8006 /* Execution Timeout Reset. */
+#define MBA_DEVICE_RESET 0x8007 /* Bus Device Reset. */
+#define MBA_BUS_MODE_CHANGE 0x800E /* SCSI bus mode transition. */
+#define MBA_SCSI_COMPLETION 0x8020 /* Completion response. */
+
+/*
+ * ISP mailbox commands
+ */
+#define MBC_NOP 0 /* No Operation */
+#define MBC_LOAD_RAM 1 /* Load RAM */
+#define MBC_EXECUTE_FIRMWARE 2 /* Execute firmware */
+#define MBC_DUMP_RAM 3 /* Dump RAM contents */
+#define MBC_WRITE_RAM_WORD 4 /* Write ram word */
+#define MBC_READ_RAM_WORD 5 /* Read ram word */
+#define MBC_MAILBOX_REGISTER_TEST 6 /* Wrap incoming mailboxes */
+#define MBC_VERIFY_CHECKSUM 7 /* Verify checksum */
+#define MBC_ABOUT_FIRMWARE 8 /* Get firmware revision */
+#define MBC_INIT_REQUEST_QUEUE 0x10 /* Initialize request queue */
+#define MBC_INIT_RESPONSE_QUEUE 0x11 /* Initialize response queue */
+#define MBC_EXECUTE_IOCB 0x12 /* Execute IOCB command */
+#define MBC_ABORT_COMMAND 0x15 /* Abort IOCB command */
+#define MBC_ABORT_DEVICE 0x16 /* Abort device (ID/LUN) */
+#define MBC_ABORT_TARGET 0x17 /* Abort target (ID) */
+#define MBC_BUS_RESET 0x18 /* SCSI bus reset */
+#define MBC_GET_RETRY_COUNT 0x22 /* Get retry count and delay */
+#define MBC_GET_TARGET_PARAMETERS 0x28 /* Get target parameters */
+#define MBC_SET_INITIATOR_ID 0x30 /* Set initiator SCSI ID */
+#define MBC_SET_SELECTION_TIMEOUT 0x31 /* Set selection timeout */
+#define MBC_SET_RETRY_COUNT 0x32 /* Set retry count and delay */
+#define MBC_SET_TAG_AGE_LIMIT 0x33 /* Set tag age limit */
+#define MBC_SET_CLOCK_RATE 0x34 /* Set clock rate */
+#define MBC_SET_ACTIVE_NEGATION 0x35 /* Set active negation state */
+#define MBC_SET_ASYNC_DATA_SETUP 0x36 /* Set async data setup time */
+#define MBC_SET_PCI_CONTROL 0x37 /* Set BUS control parameters */
+#define MBC_SET_TARGET_PARAMETERS 0x38 /* Set target parameters */
+#define MBC_SET_DEVICE_QUEUE 0x39 /* Set device queue parameters */
+#define MBC_SET_RESET_DELAY_PARAMETERS 0x3A /* Set reset delay parameters */
+#define MBC_SET_SYSTEM_PARAMETER 0x45 /* Set system parameter word */
+#define MBC_SET_FIRMWARE_FEATURES 0x4A /* Set firmware feature word */
+#define MBC_INIT_REQUEST_QUEUE_A64 0x52 /* Initialize request queue A64 */
+#define MBC_INIT_RESPONSE_QUEUE_A64 0x53 /* Initialize response q A64 */
+#define MBC_ENABLE_TARGET_MODE 0x55 /* Enable target mode */
+#define MBC_SET_DATA_OVERRUN_RECOVERY 0x5A /* Set data overrun recovery mode */
+
+/*
+ * ISP Get/Set Target Parameters mailbox command control flags.
+ */
+#define TP_PPR BIT_5 /* PPR */
+#define TP_RENEGOTIATE BIT_8 /* Renegotiate on error. */
+#define TP_STOP_QUEUE BIT_9 /* Stop que on check condition */
+#define TP_AUTO_REQUEST_SENSE BIT_10 /* Automatic request sense. */
+#define TP_TAGGED_QUEUE BIT_11 /* Tagged queuing. */
+#define TP_SYNC BIT_12 /* Synchronous data transfers. */
+#define TP_WIDE BIT_13 /* Wide data transfers. */
+#define TP_PARITY BIT_14 /* Parity checking. */
+#define TP_DISCONNECT BIT_15 /* Disconnect privilege. */
+
+/*
+ * NVRAM Command values.
+ */
+#define NV_START_BIT BIT_2
+#define NV_WRITE_OP (BIT_26 | BIT_24)
+#define NV_READ_OP (BIT_26 | BIT_25)
+#define NV_ERASE_OP (BIT_26 | BIT_25 | BIT_24)
+#define NV_MASK_OP (BIT_26 | BIT_25 | BIT_24)
+#define NV_DELAY_COUNT 10
+
+/*
+ * QLogic ISP1280/ISP12160 NVRAM structure definition.
+ */
+struct nvram {
+ uint8_t id0; /* 0 */
+ uint8_t id1; /* 1 */
+ uint8_t id2; /* 2 */
+ uint8_t id3; /* 3 */
+ uint8_t version; /* 4 */
+
+ struct {
+ uint8_t bios_configuration_mode:2;
+ uint8_t bios_disable:1;
+ uint8_t selectable_scsi_boot_enable:1;
+ uint8_t cd_rom_boot_enable:1;
+ uint8_t disable_loading_risc_code:1;
+ uint8_t enable_64bit_addressing:1;
+ uint8_t unused_7:1;
+ } cntr_flags_1; /* 5 */
+
+ struct {
+ uint8_t boot_lun_number:5;
+ uint8_t scsi_bus_number:1;
+ uint8_t unused_6:1;
+ uint8_t unused_7:1;
+ } cntr_flags_2l; /* 7 */
+
+ struct {
+ uint8_t boot_target_number:4;
+ uint8_t unused_12:1;
+ uint8_t unused_13:1;
+ uint8_t unused_14:1;
+ uint8_t unused_15:1;
+ } cntr_flags_2h; /* 8 */
+
+ uint16_t unused_8; /* 8, 9 */
+ uint16_t unused_10; /* 10, 11 */
+ uint16_t unused_12; /* 12, 13 */
+ uint16_t unused_14; /* 14, 15 */
+
+ union {
+ uint8_t c;
+ struct {
+ uint8_t reserved:2;
+ uint8_t burst_enable:1;
+ uint8_t reserved_1:1;
+ uint8_t fifo_threshold:4;
+ } f;
+ } isp_config; /* 16 */
+
+ /* Termination
+ * 0 = Disable, 1 = high only, 3 = Auto term
+ */
+ union {
+ uint8_t c;
+ struct {
+ uint8_t scsi_bus_1_control:2;
+ uint8_t scsi_bus_0_control:2;
+ uint8_t unused_0:1;
+ uint8_t unused_1:1;
+ uint8_t unused_2:1;
+ uint8_t auto_term_support:1;
+ } f;
+ } termination; /* 17 */
+
+ uint16_t isp_parameter; /* 18, 19 */
+
+ union {
+ uint16_t w;
+ struct {
+ uint16_t enable_fast_posting:1;
+ uint16_t report_lvd_bus_transition:1;
+ uint16_t unused_2:1;
+ uint16_t unused_3:1;
+ uint16_t disable_iosbs_with_bus_reset_status:1;
+ uint16_t disable_synchronous_backoff:1;
+ uint16_t unused_6:1;
+ uint16_t synchronous_backoff_reporting:1;
+ uint16_t disable_reselection_fairness:1;
+ uint16_t unused_9:1;
+ uint16_t unused_10:1;
+ uint16_t unused_11:1;
+ uint16_t unused_12:1;
+ uint16_t unused_13:1;
+ uint16_t unused_14:1;
+ uint16_t unused_15:1;
+ } f;
+ } firmware_feature; /* 20, 21 */
+
+ uint16_t unused_22; /* 22, 23 */
+
+ struct {
+ struct {
+ uint8_t initiator_id:4;
+ uint8_t scsi_reset_disable:1;
+ uint8_t scsi_bus_size:1;
+ uint8_t scsi_bus_type:1;
+ uint8_t unused_7:1;
+ } config_1; /* 24 */
+
+ uint8_t bus_reset_delay; /* 25 */
+ uint8_t retry_count; /* 26 */
+ uint8_t retry_delay; /* 27 */
+
+ struct {
+ uint8_t async_data_setup_time:4;
+ uint8_t req_ack_active_negation:1;
+ uint8_t data_line_active_negation:1;
+ uint8_t unused_6:1;
+ uint8_t unused_7:1;
+ } config_2; /* 28 */
+
+ uint8_t unused_29; /* 29 */
+
+ uint16_t selection_timeout; /* 30, 31 */
+ uint16_t max_queue_depth; /* 32, 33 */
+
+ uint16_t unused_34; /* 34, 35 */
+ uint16_t unused_36; /* 36, 37 */
+ uint16_t unused_38; /* 38, 39 */
+
+ struct {
+ union {
+ uint8_t c;
+ struct {
+ uint8_t renegotiate_on_error:1;
+ uint8_t stop_queue_on_check:1;
+ uint8_t auto_request_sense:1;
+ uint8_t tag_queuing:1;
+ uint8_t enable_sync:1;
+ uint8_t enable_wide:1;
+ uint8_t parity_checking:1;
+ uint8_t disconnect_allowed:1;
+ } f;
+ } parameter; /* 40 */
+
+ uint8_t execution_throttle; /* 41 */
+ uint8_t sync_period; /* 42 */
+
+ union { /* 43 */
+ uint8_t flags_43;
+ struct {
+ uint8_t sync_offset:4;
+ uint8_t device_enable:1;
+ uint8_t lun_disable:1;
+ uint8_t unused_6:1;
+ uint8_t unused_7:1;
+ } flags1x80;
+ struct {
+ uint8_t sync_offset:5;
+ uint8_t device_enable:1;
+ uint8_t unused_6:1;
+ uint8_t unused_7:1;
+ } flags1x160;
+ } flags;
+ union { /* PPR flags for the 1x160 controllers */
+ uint8_t unused_44;
+ struct {
+ uint8_t ppr_options:4;
+ uint8_t ppr_bus_width:2;
+ uint8_t unused_8:1;
+ uint8_t enable_ppr:1;
+ } flags; /* 44 */
+ } ppr_1x160;
+ uint8_t unused_45; /* 45 */
+ } target[MAX_TARGETS];
+ } bus[MAX_BUSES];
+
+ uint16_t unused_248; /* 248, 249 */
+
+ uint16_t subsystem_id[2]; /* 250, 251, 252, 253 */
+
+ union { /* 254 */
+ uint8_t unused_254;
+ uint8_t system_id_pointer;
+ } sysid_1x160;
+
+ uint8_t chksum; /* 255 */
+};
+
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define MAX_CMDSZ 12 /* SCSI maximum CDB size. */
+struct cmd_entry {
+ uint8_t entry_type; /* Entry type. */
+#define COMMAND_TYPE 1 /* Command entry */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ uint8_t lun; /* SCSI LUN */
+ uint8_t target; /* SCSI ID */
+ uint16_t cdb_len; /* SCSI command length. */
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+};
+
+/*
+ * ISP queue - continuation entry structure definition.
+ */
+struct cont_entry {
+ uint8_t entry_type; /* Entry type. */
+#define CONTINUE_TYPE 2 /* Continuation entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved; /* Reserved */
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+ uint32_t dseg_4_address; /* Data segment 4 address. */
+ uint32_t dseg_4_length; /* Data segment 4 length. */
+ uint32_t dseg_5_address; /* Data segment 5 address. */
+ uint32_t dseg_5_length; /* Data segment 5 length. */
+ uint32_t dseg_6_address; /* Data segment 6 address. */
+ uint32_t dseg_6_length; /* Data segment 6 length. */
+};
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+struct response {
+ uint8_t entry_type; /* Entry type. */
+#define STATUS_TYPE 3 /* Status entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+#define RF_CONT BIT_0 /* Continuation. */
+#define RF_FULL BIT_1 /* Full */
+#define RF_BAD_HEADER BIT_2 /* Bad header. */
+#define RF_BAD_PAYLOAD BIT_3 /* Bad payload. */
+ uint32_t handle; /* System handle. */
+ uint16_t scsi_status; /* SCSI status. */
+ uint16_t comp_status; /* Completion status. */
+ uint16_t state_flags; /* State flags. */
+#define SF_TRANSFER_CMPL BIT_14 /* Transfer Complete. */
+#define SF_GOT_SENSE BIT_13 /* Got Sense */
+#define SF_GOT_STATUS BIT_12 /* Got Status */
+#define SF_TRANSFERRED_DATA BIT_11 /* Transferred data */
+#define SF_SENT_CDB BIT_10 /* Send CDB */
+#define SF_GOT_TARGET BIT_9 /* */
+#define SF_GOT_BUS BIT_8 /* */
+ uint16_t status_flags; /* Status flags. */
+ uint16_t time; /* Time. */
+ uint16_t req_sense_length; /* Request sense data length. */
+ uint32_t residual_length; /* Residual transfer length. */
+ uint16_t reserved[4];
+ uint8_t req_sense_data[32]; /* Request sense data. */
+};
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+struct mrk_entry {
+ uint8_t entry_type; /* Entry type. */
+#define MARKER_TYPE 4 /* Marker entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved;
+ uint8_t lun; /* SCSI LUN */
+ uint8_t target; /* SCSI ID */
+ uint8_t modifier; /* Modifier (7-0). */
+#define MK_SYNC_ID_LUN 0 /* Synchronize ID/LUN */
+#define MK_SYNC_ID 1 /* Synchronize ID */
+#define MK_SYNC_ALL 2 /* Synchronize all ID/LUN */
+ uint8_t reserved_1[53];
+};
+
+/*
+ * ISP queue - extended command entry structure definition.
+ *
+ * Unused by the driver!
+ */
+struct ecmd_entry {
+ uint8_t entry_type; /* Entry type. */
+#define EXTENDED_CMD_TYPE 5 /* Extended command entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ uint8_t lun; /* SCSI LUN */
+ uint8_t target; /* SCSI ID */
+ uint16_t cdb_len; /* SCSI command length. */
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[88]; /* SCSI command words. */
+};
+
+/*
+ * ISP queue - 64-Bit addressing, command entry structure definition.
+ */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+#define COMMAND_A64_TYPE 9 /* Command A64 entry */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ uint8_t lun; /* SCSI LUN */
+ uint8_t target; /* SCSI ID */
+ uint16_t cdb_len; /* SCSI command length. */
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t reserved_1[2]; /* unused */
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+} cmd_a64_entry_t, request_t;
+
+/*
+ * ISP queue - 64-Bit addressing, continuation entry structure definition.
+ */
+struct cont_a64_entry {
+ uint8_t entry_type; /* Entry type. */
+#define CONTINUE_A64_TYPE 0xA /* Continuation A64 entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address[2]; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address[2]; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+ uint32_t dseg_4_address[2]; /* Data segment 4 address. */
+ uint32_t dseg_4_length; /* Data segment 4 length. */
+};
+
+/*
+ * ISP queue - enable LUN entry structure definition.
+ */
+struct elun_entry {
+ uint8_t entry_type; /* Entry type. */
+#define ENABLE_LUN_TYPE 0xB /* Enable LUN entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status not used. */
+ uint32_t reserved_2;
+ uint16_t lun; /* Bit 15 is bus number. */
+ uint16_t reserved_4;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t reserved_5;
+ uint8_t command_count; /* Number of ATIOs allocated. */
+ uint8_t immed_notify_count; /* Number of Immediate Notify */
+ /* entries allocated. */
+ uint8_t group_6_length; /* SCSI CDB length for group 6 */
+ /* commands (2-26). */
+ uint8_t group_7_length; /* SCSI CDB length for group 7 */
+ /* commands (2-26). */
+ uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
+ uint16_t reserved_6[20];
+};
+
+/*
+ * ISP queue - modify LUN entry structure definition.
+ *
+ * Unused by the driver!
+ */
+struct modify_lun_entry {
+ uint8_t entry_type; /* Entry type. */
+#define MODIFY_LUN_TYPE 0xC /* Modify LUN entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun; /* SCSI LUN */
+ uint8_t reserved_3;
+ uint8_t operators;
+ uint8_t reserved_4;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t reserved_5;
+ uint8_t command_count; /* Number of ATIOs allocated. */
+ uint8_t immed_notify_count; /* Number of Immediate Notify */
+ /* entries allocated. */
+ uint16_t reserved_6;
+ uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
+ uint16_t reserved_7[20];
+};
+
+/*
+ * ISP queue - immediate notify entry structure definition.
+ */
+struct notify_entry {
+ uint8_t entry_type; /* Entry type. */
+#define IMMED_NOTIFY_TYPE 0xD /* Immediate notify entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun;
+ uint8_t initiator_id;
+ uint8_t reserved_3;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t reserved_4;
+ uint8_t tag_value; /* Received queue tag message value */
+ uint8_t tag_type; /* Received queue tag message type */
+ /* entries allocated. */
+ uint16_t seq_id;
+ uint8_t scsi_msg[8]; /* SCSI message not handled by ISP */
+ uint16_t reserved_5[8];
+ uint8_t sense_data[18];
+};
+
+/*
+ * ISP queue - notify acknowledge entry structure definition.
+ */
+struct nack_entry {
+ uint8_t entry_type; /* Entry type. */
+#define NOTIFY_ACK_TYPE 0xE /* Notify acknowledge entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun;
+ uint8_t initiator_id;
+ uint8_t reserved_3;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t event;
+ uint16_t seq_id;
+ uint16_t reserved_4[22];
+};
+
+/*
+ * ISP queue - Accept Target I/O (ATIO) entry structure definition.
+ */
+struct atio_entry {
+ uint8_t entry_type; /* Entry type. */
+#define ACCEPT_TGT_IO_TYPE 6 /* Accept target I/O entry. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun;
+ uint8_t initiator_id;
+ uint8_t cdb_len;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t scsi_status;
+ uint8_t tag_value; /* Received queue tag message value */
+ uint8_t tag_type; /* Received queue tag message type */
+ uint8_t cdb[26];
+ uint8_t sense_data[18];
+};
+
+/*
+ * ISP queue - Continue Target I/O (CTIO) entry structure definition.
+ */
+struct ctio_entry {
+ uint8_t entry_type; /* Entry type. */
+#define CONTINUE_TGT_IO_TYPE 7 /* CTIO entry */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun; /* SCSI LUN */
+ uint8_t initiator_id;
+ uint8_t reserved_3;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t scsi_status;
+ uint8_t tag_value; /* Received queue tag message value */
+ uint8_t tag_type; /* Received queue tag message type */
+ uint32_t transfer_length;
+ uint32_t residual;
+ uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
+ uint16_t dseg_count; /* Data segment count. */
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+};
+
+/*
+ * ISP queue - CTIO returned entry structure definition.
+ */
+struct ctio_ret_entry {
+ uint8_t entry_type; /* Entry type. */
+#define CTIO_RET_TYPE 7 /* CTIO return entry */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun; /* SCSI LUN */
+ uint8_t initiator_id;
+ uint8_t reserved_3;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t scsi_status;
+ uint8_t tag_value; /* Received queue tag message value */
+ uint8_t tag_type; /* Received queue tag message type */
+ uint32_t transfer_length;
+ uint32_t residual;
+ uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
+ uint16_t dseg_count; /* Data segment count. */
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint16_t dseg_1_length; /* Data segment 1 length. */
+ uint8_t sense_data[18];
+};
+
+/*
+ * ISP queue - CTIO A64 entry structure definition.
+ */
+struct ctio_a64_entry {
+ uint8_t entry_type; /* Entry type. */
+#define CTIO_A64_TYPE 0xF /* CTIO A64 entry */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun; /* SCSI LUN */
+ uint8_t initiator_id;
+ uint8_t reserved_3;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t scsi_status;
+ uint8_t tag_value; /* Received queue tag message value */
+ uint8_t tag_type; /* Received queue tag message type */
+ uint32_t transfer_length;
+ uint32_t residual;
+ uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
+ uint16_t dseg_count; /* Data segment count. */
+ uint32_t reserved_4[2];
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+};
+
+/*
+ * ISP queue - CTIO returned entry structure definition.
+ */
+struct ctio_a64_ret_entry {
+ uint8_t entry_type; /* Entry type. */
+#define CTIO_A64_RET_TYPE 0xF /* CTIO A64 returned entry */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t reserved_1;
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved_2;
+ uint8_t lun; /* SCSI LUN */
+ uint8_t initiator_id;
+ uint8_t reserved_3;
+ uint8_t target_id;
+ uint32_t option_flags;
+ uint8_t status;
+ uint8_t scsi_status;
+ uint8_t tag_value; /* Received queue tag message value */
+ uint8_t tag_type; /* Received queue tag message type */
+ uint32_t transfer_length;
+ uint32_t residual;
+ uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
+ uint16_t dseg_count; /* Data segment count. */
+ uint16_t reserved_4[7];
+ uint8_t sense_data[18];
+};
+
+/*
+ * ISP request and response queue entry sizes
+ */
+#define RESPONSE_ENTRY_SIZE (sizeof(struct response))
+#define REQUEST_ENTRY_SIZE (sizeof(request_t))
+
+/*
+ * ISP status entry - completion status definitions.
+ */
+#define CS_COMPLETE 0x0 /* No errors */
+#define CS_INCOMPLETE 0x1 /* Incomplete transfer of cmd. */
+#define CS_DMA 0x2 /* A DMA direction error. */
+#define CS_TRANSPORT 0x3 /* Transport error. */
+#define CS_RESET 0x4 /* SCSI bus reset occurred */
+#define CS_ABORTED 0x5 /* System aborted command. */
+#define CS_TIMEOUT 0x6 /* Timeout error. */
+#define CS_DATA_OVERRUN 0x7 /* Data overrun. */
+#define CS_COMMAND_OVERRUN 0x8 /* Command Overrun. */
+#define CS_STATUS_OVERRUN 0x9 /* Status Overrun. */
+#define CS_BAD_MSG 0xA /* Bad msg after status phase. */
+#define CS_NO_MSG_OUT 0xB /* No msg out after selection. */
+#define CS_EXTENDED_ID 0xC /* Extended ID failed. */
+#define CS_IDE_MSG 0xD /* Target rejected IDE msg. */
+#define CS_ABORT_MSG 0xE /* Target rejected abort msg. */
+#define CS_REJECT_MSG 0xF /* Target rejected reject msg. */
+#define CS_NOP_MSG 0x10 /* Target rejected NOP msg. */
+#define CS_PARITY_MSG 0x11 /* Target rejected parity msg. */
+#define CS_DEV_RESET_MSG 0x12 /* Target rejected dev rst msg. */
+#define CS_ID_MSG 0x13 /* Target rejected ID msg. */
+#define CS_FREE 0x14 /* Unexpected bus free. */
+#define CS_DATA_UNDERRUN 0x15 /* Data Underrun. */
+#define CS_TRANACTION_1 0x18 /* Transaction error 1 */
+#define CS_TRANACTION_2 0x19 /* Transaction error 2 */
+#define CS_TRANACTION_3 0x1a /* Transaction error 3 */
+#define CS_INV_ENTRY_TYPE 0x1b /* Invalid entry type */
+#define CS_DEV_QUEUE_FULL 0x1c /* Device queue full */
+#define CS_PHASED_SKIPPED 0x1d /* SCSI phase skipped */
+#define CS_ARS_FAILED 0x1e /* ARS failed */
+#define CS_LVD_BUS_ERROR 0x21 /* LVD bus error */
+#define CS_BAD_PAYLOAD 0x80 /* Driver defined */
+#define CS_UNKNOWN 0x81 /* Driver defined */
+#define CS_RETRY 0x82 /* Driver defined */
+
+/*
+ * ISP status entry - SCSI status byte bit definitions.
+ */
+#define SS_CHECK_CONDITION BIT_1
+#define SS_CONDITION_MET BIT_2
+#define SS_BUSY_CONDITION BIT_3
+#define SS_RESERVE_CONFLICT (BIT_4 | BIT_3)
+
+/*
+ * ISP target entries - Option flags bit definitions.
+ */
+#define OF_ENABLE_TAG BIT_1 /* Tagged queue action enable */
+#define OF_DATA_IN BIT_6 /* Data in to initiator */
+ /* (data from target to initiator) */
+#define OF_DATA_OUT BIT_7 /* Data out from initiator */
+ /* (data from initiator to target) */
+#define OF_NO_DATA (BIT_7 | BIT_6)
+#define OF_DISC_DISABLED BIT_15 /* Disconnects disabled */
+#define OF_DISABLE_SDP BIT_24 /* Disable sending save data ptr */
+#define OF_SEND_RDP BIT_26 /* Send restore data pointers msg */
+#define OF_FORCE_DISC BIT_30 /* Disconnects mandatory */
+#define OF_SSTS BIT_31 /* Send SCSI status */
+
+
+/*
+ * BUS parameters/settings structure - UNUSED
+ */
+struct bus_param {
+ uint8_t id; /* Host adapter SCSI id */
+ uint8_t bus_reset_delay; /* SCSI bus reset delay. */
+ uint8_t failed_reset_count; /* number of time reset failed */
+ uint8_t unused;
+ uint16_t device_enables; /* Device enable bits. */
+ uint16_t lun_disables; /* LUN disable bits. */
+ uint16_t qtag_enables; /* Tag queue enables. */
+ uint16_t hiwat; /* High water mark per device. */
+ uint8_t reset_marker:1;
+ uint8_t disable_scsi_reset:1;
+ uint8_t scsi_bus_dead:1; /* SCSI Bus is Dead, when 5 back to back resets failed */
+};
+
+
+struct qla_driver_setup {
+ uint32_t no_sync:1;
+ uint32_t no_wide:1;
+ uint32_t no_ppr:1;
+ uint32_t no_nvram:1;
+ uint16_t sync_mask;
+ uint16_t wide_mask;
+ uint16_t ppr_mask;
+};
+
+
+/*
+ * Linux Host Adapter structure
+ */
+struct scsi_qla_host {
+ /* Linux adapter configuration data */
+ struct Scsi_Host *host; /* pointer to host data */
+ struct scsi_qla_host *next;
+ struct device_reg __iomem *iobase; /* Base Memory-mapped I/O address */
+
+ unsigned char __iomem *mmpbase; /* memory mapped address */
+ unsigned long host_no;
+ struct pci_dev *pdev;
+ uint8_t devnum;
+ uint8_t revision;
+ uint8_t ports;
+
+ unsigned long actthreads;
+ unsigned long isr_count; /* Interrupt count */
+ unsigned long spurious_int;
+
+ /* Outstandings ISP commands. */
+ struct srb *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
+
+ /* BUS configuration data */
+ struct bus_param bus_settings[MAX_BUSES];
+
+ /* Received ISP mailbox data. */
+ volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT];
+
+ dma_addr_t request_dma; /* Physical Address */
+ request_t *request_ring; /* Base virtual address */
+ request_t *request_ring_ptr; /* Current address. */
+ uint16_t req_ring_index; /* Current index. */
+ uint16_t req_q_cnt; /* Number of available entries. */
+
+ dma_addr_t response_dma; /* Physical address. */
+ struct response *response_ring; /* Base virtual address */
+ struct response *response_ring_ptr; /* Current address. */
+ uint16_t rsp_ring_index; /* Current index. */
+
+ struct list_head done_q; /* Done queue */
+
+ struct completion *mailbox_wait;
+
+ volatile struct {
+ uint32_t online:1; /* 0 */
+ uint32_t reset_marker:1; /* 1 */
+ uint32_t disable_host_adapter:1; /* 2 */
+ uint32_t reset_active:1; /* 3 */
+ uint32_t abort_isp_active:1; /* 4 */
+ uint32_t disable_risc_code_load:1; /* 5 */
+ uint32_t enable_64bit_addressing:1; /* 6 */
+ uint32_t in_reset:1; /* 7 */
+ uint32_t ints_enabled:1;
+ uint32_t ignore_nvram:1;
+#ifdef __ia64__
+ uint32_t use_pci_vchannel:1;
+#endif
+ } flags;
+
+ struct nvram nvram;
+ int nvram_valid;
+};
+
+#endif /* _QLA1280_H */
diff --git a/drivers/scsi/qla2xxx/Kconfig b/drivers/scsi/qla2xxx/Kconfig
new file mode 100644
index 000000000000..6c73b84c6e64
--- /dev/null
+++ b/drivers/scsi/qla2xxx/Kconfig
@@ -0,0 +1,41 @@
+config SCSI_QLA2XXX
+ tristate
+ default (SCSI && PCI)
+ depends on SCSI && PCI
+
+config SCSI_QLA21XX
+ tristate "QLogic ISP2100 host adapter family support"
+ depends on SCSI_QLA2XXX
+ select SCSI_FC_ATTRS
+ ---help---
+ This driver supports the QLogic 21xx (ISP2100) host adapter family.
+
+config SCSI_QLA22XX
+ tristate "QLogic ISP2200 host adapter family support"
+ depends on SCSI_QLA2XXX
+ select SCSI_FC_ATTRS
+ ---help---
+ This driver supports the QLogic 22xx (ISP2200) host adapter family.
+
+config SCSI_QLA2300
+ tristate "QLogic ISP2300 host adapter family support"
+ depends on SCSI_QLA2XXX
+ select SCSI_FC_ATTRS
+ ---help---
+ This driver supports the QLogic 2300 (ISP2300 and ISP2312) host
+ adapter family.
+
+config SCSI_QLA2322
+ tristate "QLogic ISP2322 host adapter family support"
+ depends on SCSI_QLA2XXX
+ select SCSI_FC_ATTRS
+ ---help---
+ This driver supports the QLogic 2322 (ISP2322) host adapter family.
+
+config SCSI_QLA6312
+ tristate "QLogic ISP63xx host adapter family support"
+ depends on SCSI_QLA2XXX
+ select SCSI_FC_ATTRS
+ ---help---
+ This driver supports the QLogic 63xx (ISP6312 and ISP6322) host
+ adapter family.
diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile
new file mode 100644
index 000000000000..f7a247defba6
--- /dev/null
+++ b/drivers/scsi/qla2xxx/Makefile
@@ -0,0 +1,16 @@
+EXTRA_CFLAGS += -DUNIQUE_FW_NAME
+
+qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
+ qla_dbg.o qla_sup.o qla_rscn.o
+
+qla2100-y := ql2100.o ql2100_fw.o
+qla2200-y := ql2200.o ql2200_fw.o
+qla2300-y := ql2300.o ql2300_fw.o
+qla2322-y := ql2322.o ql2322_fw.o
+qla6312-y := ql6312.o ql6312_fw.o
+
+obj-$(CONFIG_SCSI_QLA21XX) += qla2xxx.o qla2100.o
+obj-$(CONFIG_SCSI_QLA22XX) += qla2xxx.o qla2200.o
+obj-$(CONFIG_SCSI_QLA2300) += qla2xxx.o qla2300.o
+obj-$(CONFIG_SCSI_QLA2322) += qla2xxx.o qla2322.o
+obj-$(CONFIG_SCSI_QLA6312) += qla2xxx.o qla6312.o
diff --git a/drivers/scsi/qla2xxx/ql2100.c b/drivers/scsi/qla2xxx/ql2100.c
new file mode 100644
index 000000000000..ea136a61af40
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2100.c
@@ -0,0 +1,92 @@
+/*
+ * QLogic ISP2100 device driver for Linux 2.6.x
+ * Copyright (C) 2003 Christoph Hellwig.
+ * Copyright (C) 2003-2004 QLogic Corporation (www.qlogic.com)
+ *
+ * Released under GPL v2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "qla_def.h"
+
+static char qla_driver_name[] = "qla2100";
+
+extern unsigned char fw2100tp_version[];
+extern unsigned char fw2100tp_version_str[];
+extern unsigned short fw2100tp_addr01;
+extern unsigned short fw2100tp_code01[];
+extern unsigned short fw2100tp_length01;
+
+static struct qla_fw_info qla_fw_tbl[] = {
+ {
+ .addressing = FW_INFO_ADDR_NORMAL,
+ .fwcode = &fw2100tp_code01[0],
+ .fwlen = &fw2100tp_length01,
+ .fwstart = &fw2100tp_addr01,
+ },
+
+ { FW_INFO_ADDR_NOMORE, },
+};
+
+static struct qla_board_info qla_board_tbl = {
+ .drv_name = qla_driver_name,
+
+ .isp_name = "ISP2100",
+ .fw_info = qla_fw_tbl,
+};
+
+static struct pci_device_id qla2100_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP2100,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl,
+ },
+
+ {0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla2100_pci_tbl);
+
+static int __devinit
+qla2100_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ return qla2x00_probe_one(pdev,
+ (struct qla_board_info *)id->driver_data);
+}
+
+static void __devexit
+qla2100_remove_one(struct pci_dev *pdev)
+{
+ qla2x00_remove_one(pdev);
+}
+
+static struct pci_driver qla2100_pci_driver = {
+ .name = "qla2100",
+ .id_table = qla2100_pci_tbl,
+ .probe = qla2100_probe_one,
+ .remove = __devexit_p(qla2100_remove_one),
+};
+
+static int __init
+qla2100_init(void)
+{
+ return pci_module_init(&qla2100_pci_driver);
+}
+
+static void __exit
+qla2100_exit(void)
+{
+ pci_unregister_driver(&qla2100_pci_driver);
+}
+
+module_init(qla2100_init);
+module_exit(qla2100_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic ISP21xx FC-SCSI Host Bus Adapter driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff --git a/drivers/scsi/qla2xxx/ql2100_fw.c b/drivers/scsi/qla2xxx/ql2100_fw.c
new file mode 100644
index 000000000000..e72f9f1a11ef
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2100_fw.c
@@ -0,0 +1,4858 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *************************************************************************/
+
+/*
+ * Firmware Version 1.19.24 (14:02 Jul 16, 2002)
+ */
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2100tp_version = 1*1024+19;
+#else
+unsigned short risc_code_version = 1*1024+19;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned char fw2100tp_version_str[] = {1,19,24};
+#else
+unsigned char firmware_version[] = {1,19,24};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw2100tp_VERSION_STRING "1.19.24"
+#else
+#define FW_VERSION_STRING "1.19.24"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2100tp_addr01 = 0x1000 ;
+#else
+unsigned short risc_code_addr01 = 0x1000 ;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2100tp_code01[] = {
+#else
+unsigned short risc_code01[] = {
+#endif
+ 0x0078, 0x102d, 0x0000, 0x95f1, 0x0000, 0x0001, 0x0013, 0x0018,
+ 0x0017, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2032, 0x3030,
+ 0x3120, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3231, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x312e, 0x3139, 0x2020, 0x2020, 0x2400, 0x2091, 0x2000, 0x20c1,
+ 0x0021, 0x2039, 0xffff, 0x2019, 0xaaaa, 0x2760, 0x2069, 0x7fff,
+ 0x20c1, 0x0020, 0x2c2c, 0x2d34, 0x2762, 0x236a, 0x2c24, 0x2d04,
+ 0x266a, 0x2562, 0xa406, 0x00c0, 0x1052, 0x20c1, 0x0021, 0x2c2c,
+ 0x2362, 0x2c04, 0x2562, 0xa306, 0x0040, 0x1052, 0x20c1, 0x0020,
+ 0x2039, 0x8fff, 0x20a1, 0xad00, 0x2708, 0x810d, 0x810d, 0x810d,
+ 0x810d, 0xa18c, 0x000f, 0x2001, 0x000a, 0xa112, 0xa00e, 0x21a8,
+ 0x41a4, 0x3400, 0x8211, 0x00c0, 0x105f, 0x2708, 0x3400, 0xa102,
+ 0x0040, 0x106f, 0x0048, 0x106f, 0x20a8, 0xa00e, 0x41a4, 0x20a1,
+ 0xa5f1, 0x2009, 0x0000, 0x20a9, 0x070f, 0x41a4, 0x3400, 0x20c9,
+ 0xaaff, 0x2059, 0x0000, 0x2b78, 0x7823, 0x0004, 0x2089, 0x25c7,
+ 0x2051, 0xa600, 0x2a70, 0x7762, 0xa786, 0x8fff, 0x0040, 0x1092,
+ 0x705f, 0xcd00, 0x705b, 0xccf1, 0x7067, 0x0200, 0x706b, 0x0200,
+ 0x0078, 0x109a, 0x705b, 0xbd01, 0x7067, 0x0100, 0x706b, 0x0100,
+ 0x705f, 0xbd00, 0x1078, 0x12df, 0x1078, 0x13ca, 0x1078, 0x1577,
+ 0x1078, 0x1ce9, 0x1078, 0x42ec, 0x1078, 0x76bf, 0x1078, 0x1355,
+ 0x1078, 0x2ac0, 0x1078, 0x4e93, 0x1078, 0x49a3, 0x1078, 0x594a,
+ 0x1078, 0x2263, 0x1078, 0x5c43, 0x1078, 0x5485, 0x1078, 0x2162,
+ 0x1078, 0x2240, 0x2091, 0x3009, 0x7823, 0x0000, 0x0090, 0x10cf,
+ 0x7820, 0xa086, 0x0002, 0x00c0, 0x10cf, 0x7823, 0x4000, 0x0068,
+ 0x10c7, 0x781b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x2a70,
+ 0x7003, 0x0000, 0x2001, 0x017f, 0x2003, 0x0000, 0x2a70, 0x7000,
+ 0xa08e, 0x0003, 0x00c0, 0x10ef, 0x1078, 0x365e, 0x1078, 0x2ae8,
+ 0x1078, 0x4ee3, 0x1078, 0x4b66, 0x2009, 0x0100, 0x2104, 0xa082,
+ 0x0002, 0x0048, 0x10f3, 0x1078, 0x5966, 0x0078, 0x10d6, 0x1079,
+ 0x10f7, 0x0078, 0x10dc, 0x1078, 0x7197, 0x0078, 0x10eb, 0x1101,
+ 0x1102, 0x11be, 0x10ff, 0x1246, 0x12dc, 0x12dd, 0x12de, 0x1078,
+ 0x1332, 0x007c, 0x127e, 0x0f7e, 0x2091, 0x8000, 0x7000, 0xa086,
+ 0x0001, 0x00c0, 0x1198, 0x1078, 0x3aec, 0x2079, 0x0100, 0x7844,
+ 0xa005, 0x00c0, 0x1198, 0x2011, 0x41dc, 0x1078, 0x5a45, 0x1078,
+ 0x1adf, 0x780f, 0x00ff, 0x7840, 0xa084, 0xfffb, 0x7842, 0x2011,
+ 0x8010, 0x73c4, 0x1078, 0x361b, 0x2001, 0xffff, 0x1078, 0x5ae6,
+ 0x723c, 0xc284, 0x723e, 0x2001, 0xa60c, 0x2014, 0xc2ac, 0x2202,
+ 0x1078, 0x6f9f, 0x2011, 0x0004, 0x1078, 0x8d1b, 0x1078, 0x489e,
+ 0x1078, 0x42d4, 0x0040, 0x1144, 0x7087, 0x0001, 0x70bf, 0x0000,
+ 0x1078, 0x3c9e, 0x0078, 0x1198, 0x1078, 0x4967, 0x0040, 0x114d,
+ 0x7a0c, 0xc2b4, 0x7a0e, 0x0078, 0x1159, 0x1078, 0x90a6, 0x70cc,
+ 0xd09c, 0x00c0, 0x1159, 0x7098, 0xa005, 0x0040, 0x1159, 0x1078,
+ 0x42b8, 0x70d7, 0x0000, 0x70d3, 0x0000, 0x72cc, 0x2079, 0xa652,
+ 0x7804, 0xd0ac, 0x0040, 0x1165, 0xc295, 0x72ce, 0xa296, 0x0004,
+ 0x0040, 0x1186, 0x2011, 0x0001, 0x1078, 0x8d1b, 0x7093, 0x0000,
+ 0x7097, 0xffff, 0x7003, 0x0002, 0x0f7f, 0x1078, 0x2677, 0x2011,
+ 0x0005, 0x1078, 0x70e0, 0x1078, 0x62d1, 0x0c7e, 0x2061, 0x0100,
+ 0x60e3, 0x0008, 0x0c7f, 0x127f, 0x0078, 0x119a, 0x7093, 0x0000,
+ 0x7097, 0xffff, 0x7003, 0x0002, 0x2011, 0x0005, 0x1078, 0x70e0,
+ 0x1078, 0x62d1, 0x0c7e, 0x2061, 0x0100, 0x60e3, 0x0008, 0x0c7f,
+ 0x0f7f, 0x127f, 0x007c, 0x0c7e, 0x20a9, 0x0082, 0x2009, 0x007e,
+ 0x017e, 0x027e, 0x037e, 0x2110, 0x027e, 0x2019, 0x0029, 0x1078,
+ 0x73d0, 0x027f, 0x1078, 0xa4f1, 0x037f, 0x027f, 0x017f, 0x1078,
+ 0x298e, 0x8108, 0x00f0, 0x11a0, 0x0c7f, 0x706f, 0x0000, 0x7070,
+ 0xa084, 0x00ff, 0x7072, 0x709b, 0x0000, 0x007c, 0x127e, 0x2091,
+ 0x8000, 0x7000, 0xa086, 0x0002, 0x00c0, 0x1244, 0x7094, 0xa086,
+ 0xffff, 0x0040, 0x11d1, 0x1078, 0x2677, 0x1078, 0x62d1, 0x0078,
+ 0x1244, 0x70cc, 0xd09c, 0x0040, 0x11fd, 0xd084, 0x0040, 0x11fd,
+ 0x0f7e, 0x2079, 0x0100, 0x790c, 0xc1b5, 0x790e, 0x0f7f, 0xd08c,
+ 0x0040, 0x11fd, 0x70d0, 0xa086, 0xffff, 0x0040, 0x11f9, 0x1078,
+ 0x27f7, 0x1078, 0x62d1, 0x70cc, 0xd094, 0x00c0, 0x1244, 0x2011,
+ 0x0001, 0x2019, 0x0000, 0x1078, 0x282f, 0x1078, 0x62d1, 0x0078,
+ 0x1244, 0x70d4, 0xa005, 0x00c0, 0x1244, 0x7090, 0xa005, 0x00c0,
+ 0x1244, 0x1078, 0x4967, 0x00c0, 0x1244, 0x2001, 0xa653, 0x2004,
+ 0xd0ac, 0x0040, 0x1227, 0x157e, 0x0c7e, 0x20a9, 0x007f, 0x2009,
+ 0x0000, 0x017e, 0x1078, 0x45c4, 0x00c0, 0x121a, 0x6000, 0xd0ec,
+ 0x00c0, 0x1222, 0x017f, 0x8108, 0x00f0, 0x1211, 0x0c7f, 0x157f,
+ 0x0078, 0x1227, 0x017f, 0x0c7f, 0x157f, 0x0078, 0x1244, 0x7003,
+ 0x0003, 0x7097, 0xffff, 0x2001, 0x0000, 0x1078, 0x24e8, 0x1078,
+ 0x3699, 0x2001, 0xa8b2, 0x2004, 0xa086, 0x0005, 0x00c0, 0x123c,
+ 0x2011, 0x0000, 0x1078, 0x70e0, 0x2011, 0x0000, 0x1078, 0x70ea,
+ 0x1078, 0x62d1, 0x1078, 0x639b, 0x127f, 0x007c, 0x017e, 0x0f7e,
+ 0x127e, 0x2091, 0x8000, 0x2079, 0x0100, 0x2009, 0x00f7, 0x1078,
+ 0x42a1, 0x7940, 0xa18c, 0x0010, 0x7942, 0x7924, 0xd1b4, 0x0040,
+ 0x125b, 0x7827, 0x0040, 0xd19c, 0x0040, 0x1260, 0x7827, 0x0008,
+ 0x007e, 0x037e, 0x157e, 0xa006, 0x1078, 0x5ae6, 0x7900, 0xa18a,
+ 0x0003, 0x0050, 0x1289, 0x7954, 0xd1ac, 0x00c0, 0x1289, 0x2009,
+ 0x00f8, 0x1078, 0x42a1, 0x7843, 0x0090, 0x7843, 0x0010, 0x20a9,
+ 0x09c4, 0x7820, 0xd09c, 0x00c0, 0x1281, 0x7824, 0xd0ac, 0x00c0,
+ 0x12ca, 0x00f0, 0x1279, 0x2001, 0x0001, 0x1078, 0x24e8, 0x0078,
+ 0x12d5, 0x7853, 0x0000, 0x782f, 0x0020, 0x20a9, 0x0050, 0x00e0,
+ 0x128f, 0x2091, 0x6000, 0x00f0, 0x128f, 0x7853, 0x0400, 0x782f,
+ 0x0000, 0x2009, 0x00f8, 0x1078, 0x42a1, 0x20a9, 0x000e, 0x0005,
+ 0x00f0, 0x129f, 0x7853, 0x1400, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x2019, 0x61a8, 0x7854, 0x0005, 0x0005, 0xd08c, 0x0040, 0x12b4,
+ 0x7824, 0xd0ac, 0x00c0, 0x12ca, 0x8319, 0x00c0, 0x12aa, 0x2009,
+ 0xa632, 0x2104, 0x8000, 0x200a, 0xa084, 0xfff0, 0x0040, 0x12c4,
+ 0x200b, 0x0000, 0x1078, 0x2588, 0x2001, 0x0001, 0x1078, 0x24e8,
+ 0x0078, 0x12d3, 0x2001, 0xa632, 0x2003, 0x0000, 0x7828, 0xc09d,
+ 0x782a, 0x7827, 0x0048, 0x7853, 0x0400, 0x157f, 0x037f, 0x007f,
+ 0x127f, 0x0f7f, 0x017f, 0x007c, 0x007c, 0x007c, 0x007c, 0x2a70,
+ 0x2061, 0xa8ad, 0x2063, 0x0001, 0x6007, 0x0013, 0x600b, 0x0018,
+ 0x600f, 0x0017, 0x2009, 0x0100, 0x2104, 0xa082, 0x0002, 0x0048,
+ 0x12f5, 0x7053, 0xffff, 0x0078, 0x12f7, 0x7053, 0x0000, 0x7057,
+ 0xffff, 0x706f, 0x0000, 0x7073, 0x0000, 0x1078, 0x90a6, 0x2061,
+ 0xa88d, 0x6003, 0x0909, 0x6007, 0x0000, 0x600b, 0x8800, 0x600f,
+ 0x0200, 0x6013, 0x00ff, 0x6017, 0x0003, 0x601b, 0x0000, 0x601f,
+ 0x07d0, 0x2061, 0xa895, 0x6003, 0x8000, 0x6007, 0x0000, 0x600b,
+ 0x0000, 0x600f, 0x0200, 0x6013, 0x00ff, 0x6017, 0x0000, 0x601b,
+ 0x0001, 0x601f, 0x0000, 0x2061, 0xa8a5, 0x6003, 0x514c, 0x6007,
+ 0x4f47, 0x600b, 0x4943, 0x600f, 0x2020, 0x2001, 0xa626, 0x2003,
+ 0x0000, 0x007c, 0x2091, 0x8000, 0x0068, 0x1334, 0x007e, 0x017e,
+ 0x2079, 0x0000, 0x7818, 0xd084, 0x00c0, 0x133a, 0x017f, 0x792e,
+ 0x007f, 0x782a, 0x007f, 0x7826, 0x3900, 0x783a, 0x7823, 0x8002,
+ 0x781b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x2079, 0xa600,
+ 0x7803, 0x0005, 0x0078, 0x1352, 0x007c, 0x2071, 0xa600, 0x715c,
+ 0x712e, 0x2021, 0x0001, 0xa190, 0x002d, 0xa298, 0x002d, 0x0048,
+ 0x136b, 0x7060, 0xa302, 0x00c8, 0x136b, 0x220a, 0x2208, 0x2310,
+ 0x8420, 0x0078, 0x135d, 0x200b, 0x0000, 0x74aa, 0x74ae, 0x007c,
+ 0x0e7e, 0x127e, 0x2091, 0x8000, 0x2071, 0xa600, 0x70ac, 0xa0ea,
+ 0x0010, 0x00c8, 0x137e, 0xa06e, 0x0078, 0x1388, 0x8001, 0x70ae,
+ 0x702c, 0x2068, 0x2d04, 0x702e, 0x206b, 0x0000, 0x6807, 0x0000,
+ 0x127f, 0x0e7f, 0x007c, 0x0e7e, 0x2071, 0xa600, 0x127e, 0x2091,
+ 0x8000, 0x70ac, 0x8001, 0x00c8, 0x1398, 0xa06e, 0x0078, 0x13a1,
+ 0x70ae, 0x702c, 0x2068, 0x2d04, 0x702e, 0x206b, 0x0000, 0x6807,
+ 0x0000, 0x127f, 0x0e7f, 0x007c, 0x0e7e, 0x127e, 0x2091, 0x8000,
+ 0x2071, 0xa600, 0x702c, 0x206a, 0x2d00, 0x702e, 0x70ac, 0x8000,
+ 0x70ae, 0x127f, 0x0e7f, 0x007c, 0x8dff, 0x0040, 0x13c0, 0x6804,
+ 0x6807, 0x0000, 0x007e, 0x1078, 0x13a4, 0x0d7f, 0x0078, 0x13b4,
+ 0x007c, 0x0e7e, 0x2071, 0xa600, 0x70ac, 0xa08a, 0x0010, 0xa00d,
+ 0x0e7f, 0x007c, 0x0e7e, 0x2071, 0xa8d6, 0x7007, 0x0000, 0x701b,
+ 0x0000, 0x701f, 0x0000, 0x2071, 0x0000, 0x7010, 0xa085, 0x8004,
+ 0x7012, 0x0e7f, 0x007c, 0x127e, 0x2091, 0x8000, 0x0e7e, 0x2270,
+ 0x700b, 0x0000, 0x2071, 0xa8d6, 0x7018, 0xa088, 0xa8df, 0x220a,
+ 0x8000, 0xa084, 0x0007, 0x701a, 0x7004, 0xa005, 0x00c0, 0x13f6,
+ 0x0f7e, 0x2079, 0x0010, 0x1078, 0x1408, 0x0f7f, 0x0e7f, 0x127f,
+ 0x007c, 0x0e7e, 0x2071, 0xa8d6, 0x7004, 0xa005, 0x00c0, 0x1406,
+ 0x0f7e, 0x2079, 0x0010, 0x1078, 0x1408, 0x0f7f, 0x0e7f, 0x007c,
+ 0x7000, 0x0079, 0x140b, 0x140f, 0x1479, 0x1496, 0x1496, 0x7018,
+ 0x711c, 0xa106, 0x00c0, 0x1417, 0x7007, 0x0000, 0x007c, 0x0d7e,
+ 0xa180, 0xa8df, 0x2004, 0x700a, 0x2068, 0x8108, 0xa18c, 0x0007,
+ 0x711e, 0x7803, 0x0026, 0x6824, 0x7832, 0x6828, 0x7836, 0x682c,
+ 0x783a, 0x6830, 0x783e, 0x6810, 0x700e, 0x680c, 0x7016, 0x6804,
+ 0x0d7f, 0xd084, 0x0040, 0x1439, 0x7007, 0x0001, 0x1078, 0x143e,
+ 0x007c, 0x7007, 0x0002, 0x1078, 0x1454, 0x007c, 0x017e, 0x027e,
+ 0x710c, 0x2011, 0x0040, 0xa182, 0x0040, 0x00c8, 0x1449, 0x2110,
+ 0xa006, 0x700e, 0x7212, 0x8203, 0x7822, 0x7803, 0x0020, 0x7803,
+ 0x0041, 0x027f, 0x017f, 0x007c, 0x017e, 0x027e, 0x137e, 0x147e,
+ 0x157e, 0x7014, 0x2098, 0x20a1, 0x0014, 0x7803, 0x0026, 0x710c,
+ 0x2011, 0x0040, 0xa182, 0x0040, 0x00c8, 0x1468, 0x2110, 0xa006,
+ 0x700e, 0x22a8, 0x53a6, 0x8203, 0x7822, 0x7803, 0x0020, 0x3300,
+ 0x7016, 0x7803, 0x0001, 0x157f, 0x147f, 0x137f, 0x027f, 0x017f,
+ 0x007c, 0x137e, 0x147e, 0x157e, 0x2099, 0xa6fa, 0x20a1, 0x0018,
+ 0x20a9, 0x0008, 0x53a3, 0x7803, 0x0020, 0x127e, 0x2091, 0x8000,
+ 0x7803, 0x0041, 0x7007, 0x0003, 0x7000, 0xc084, 0x7002, 0x700b,
+ 0xa6f5, 0x127f, 0x157f, 0x147f, 0x137f, 0x007c, 0x137e, 0x147e,
+ 0x157e, 0x2001, 0xa729, 0x209c, 0x20a1, 0x0014, 0x7803, 0x0026,
+ 0x2001, 0xa72a, 0x20ac, 0x53a6, 0x2099, 0xa72b, 0x20a1, 0x0018,
+ 0x20a9, 0x0008, 0x53a3, 0x7803, 0x0020, 0x127e, 0x2091, 0x8000,
+ 0x7803, 0x0001, 0x7007, 0x0004, 0x7000, 0xc08c, 0x7002, 0x700b,
+ 0xa726, 0x127f, 0x157f, 0x147f, 0x137f, 0x007c, 0x017e, 0x0e7e,
+ 0x2071, 0xa8d6, 0x0f7e, 0x2079, 0x0010, 0x7904, 0x7803, 0x0002,
+ 0xd1fc, 0x0040, 0x14d0, 0xa18c, 0x0700, 0x7004, 0x1079, 0x14d4,
+ 0x0f7f, 0x0e7f, 0x017f, 0x007c, 0x1408, 0x14dc, 0x1509, 0x1531,
+ 0x1564, 0x14da, 0x0078, 0x14da, 0xa18c, 0x0700, 0x00c0, 0x1502,
+ 0x137e, 0x147e, 0x157e, 0x7014, 0x20a0, 0x2099, 0x0014, 0x7803,
+ 0x0040, 0x7010, 0x20a8, 0x53a5, 0x3400, 0x7016, 0x157f, 0x147f,
+ 0x137f, 0x700c, 0xa005, 0x0040, 0x151e, 0x1078, 0x143e, 0x007c,
+ 0x7008, 0xa080, 0x0002, 0x2003, 0x0100, 0x7007, 0x0000, 0x1078,
+ 0x1408, 0x007c, 0x7008, 0xa080, 0x0002, 0x2003, 0x0200, 0x0078,
+ 0x14fd, 0xa18c, 0x0700, 0x00c0, 0x1514, 0x700c, 0xa005, 0x0040,
+ 0x151e, 0x1078, 0x1454, 0x007c, 0x7008, 0xa080, 0x0002, 0x2003,
+ 0x0200, 0x7007, 0x0000, 0x1078, 0x1408, 0x007c, 0x0d7e, 0x7008,
+ 0x2068, 0x7830, 0x6826, 0x7834, 0x682a, 0x7838, 0x682e, 0x783c,
+ 0x6832, 0x680b, 0x0100, 0x0d7f, 0x7007, 0x0000, 0x1078, 0x1408,
+ 0x007c, 0xa18c, 0x0700, 0x00c0, 0x155e, 0x137e, 0x147e, 0x157e,
+ 0x2001, 0xa6f8, 0x2004, 0xa080, 0x000d, 0x20a0, 0x2099, 0x0014,
+ 0x7803, 0x0040, 0x20a9, 0x0020, 0x53a5, 0x2001, 0xa6fa, 0x2004,
+ 0xd0bc, 0x0040, 0x1554, 0x2001, 0xa703, 0x2004, 0xa080, 0x000d,
+ 0x20a0, 0x20a9, 0x0020, 0x53a5, 0x157f, 0x147f, 0x137f, 0x7007,
+ 0x0000, 0x1078, 0x4f8c, 0x1078, 0x1408, 0x007c, 0x2011, 0x8003,
+ 0x1078, 0x361b, 0x0078, 0x1562, 0xa18c, 0x0700, 0x00c0, 0x1571,
+ 0x2001, 0xa728, 0x2003, 0x0100, 0x7007, 0x0000, 0x1078, 0x1408,
+ 0x007c, 0x2011, 0x8004, 0x1078, 0x361b, 0x0078, 0x1575, 0x127e,
+ 0x2091, 0x2100, 0x2079, 0x0030, 0x2071, 0xa8e7, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0x700f, 0xa8ed, 0x7013, 0xa8ed, 0x780f, 0x0076,
+ 0x7803, 0x0004, 0x127f, 0x007c, 0x6934, 0xa184, 0x0007, 0x0079,
+ 0x1591, 0x1599, 0x15df, 0x1599, 0x1599, 0x1599, 0x15c4, 0x15a8,
+ 0x159d, 0xa085, 0x0001, 0x0078, 0x15f9, 0x684c, 0xd0bc, 0x0040,
+ 0x1599, 0x6860, 0x682e, 0x685c, 0x682a, 0x6858, 0x0078, 0x15e7,
+ 0xa18c, 0x00ff, 0xa186, 0x001e, 0x00c0, 0x1599, 0x684c, 0xd0bc,
+ 0x0040, 0x1599, 0x6860, 0x682e, 0x685c, 0x682a, 0x6804, 0x681a,
+ 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080, 0x206a, 0x2004,
+ 0x6832, 0x6858, 0x0078, 0x15ef, 0xa18c, 0x00ff, 0xa186, 0x0015,
+ 0x00c0, 0x1599, 0x684c, 0xd0ac, 0x0040, 0x1599, 0x6804, 0x681a,
+ 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080, 0x206a, 0x2004,
+ 0x6832, 0xa006, 0x682e, 0x682a, 0x6858, 0x0078, 0x15ef, 0x684c,
+ 0xd0ac, 0x0040, 0x1599, 0xa006, 0x682e, 0x682a, 0x6858, 0xa18c,
+ 0x000f, 0xa188, 0x206a, 0x210c, 0x6932, 0x2d08, 0x691a, 0x6826,
+ 0x684c, 0xc0dd, 0x684e, 0xa006, 0x680a, 0x697c, 0x6912, 0x6980,
+ 0x6916, 0x007c, 0x20e1, 0x0007, 0x20e1, 0x2000, 0x2001, 0x020a,
+ 0x2004, 0x82ff, 0x0040, 0x161c, 0xa280, 0x0004, 0x0d7e, 0x206c,
+ 0x684c, 0xd0dc, 0x00c0, 0x1618, 0x1078, 0x158c, 0x0040, 0x1618,
+ 0x0d7f, 0xa280, 0x0000, 0x2003, 0x0002, 0xa016, 0x0078, 0x161c,
+ 0x6808, 0x8000, 0x680a, 0x0d7f, 0x127e, 0x047e, 0x037e, 0x027e,
+ 0x2091, 0x2100, 0x027f, 0x037f, 0x047f, 0x7000, 0xa005, 0x00c0,
+ 0x1630, 0x7206, 0x2001, 0x1651, 0x007e, 0x2260, 0x0078, 0x17e0,
+ 0x710c, 0x220a, 0x8108, 0x230a, 0x8108, 0x240a, 0x8108, 0xa182,
+ 0xa908, 0x0048, 0x163d, 0x2009, 0xa8ed, 0x710e, 0x7010, 0xa102,
+ 0xa082, 0x0009, 0x0040, 0x1648, 0xa080, 0x001b, 0x00c0, 0x164b,
+ 0x2009, 0x0138, 0x200a, 0x7000, 0xa005, 0x00c0, 0x1651, 0x1078,
+ 0x17c1, 0x127f, 0x007c, 0x127e, 0x027e, 0x037e, 0x0c7e, 0x007e,
+ 0x2091, 0x2100, 0x007f, 0x047f, 0x037f, 0x027f, 0x0d7e, 0x0c7e,
+ 0x2460, 0x6110, 0x2168, 0x6a62, 0x6b5e, 0xa005, 0x0040, 0x16dd,
+ 0x6808, 0xa005, 0x0040, 0x174a, 0x7000, 0xa005, 0x00c0, 0x1672,
+ 0x0078, 0x16d2, 0x700c, 0x7110, 0xa106, 0x00c0, 0x1753, 0x7004,
+ 0xa406, 0x00c0, 0x16d2, 0x2001, 0x0005, 0x2004, 0xd08c, 0x0040,
+ 0x168f, 0x047e, 0x1078, 0x1913, 0x047f, 0x2460, 0x6010, 0xa080,
+ 0x0002, 0x2004, 0xa005, 0x0040, 0x174a, 0x0078, 0x166c, 0x2001,
+ 0x0207, 0x2004, 0xd09c, 0x00c0, 0x167b, 0x7804, 0xa084, 0x6000,
+ 0x0040, 0x16a0, 0xa086, 0x6000, 0x0040, 0x16a0, 0x0078, 0x167b,
+ 0x7100, 0xa186, 0x0002, 0x00c0, 0x16c0, 0x0e7e, 0x2b68, 0x6818,
+ 0x2060, 0x1078, 0x203f, 0x2804, 0xac70, 0x6034, 0xd09c, 0x00c0,
+ 0x16b5, 0x7108, 0x720c, 0x0078, 0x16b7, 0x7110, 0x7214, 0x6810,
+ 0xa100, 0x6812, 0x6814, 0xa201, 0x6816, 0x0e7f, 0x0078, 0x16c4,
+ 0xa186, 0x0001, 0x00c0, 0x16cc, 0x7820, 0x6910, 0xa100, 0x6812,
+ 0x7824, 0x6914, 0xa101, 0x6816, 0x7803, 0x0004, 0x7003, 0x0000,
+ 0x7004, 0x2060, 0x6100, 0xa18e, 0x0004, 0x00c0, 0x1753, 0x2009,
+ 0x0048, 0x1078, 0x775c, 0x0078, 0x1753, 0x6808, 0xa005, 0x0040,
+ 0x174a, 0x7000, 0xa005, 0x00c0, 0x16e7, 0x0078, 0x174a, 0x700c,
+ 0x7110, 0xa106, 0x00c0, 0x16f0, 0x7004, 0xa406, 0x00c0, 0x174a,
+ 0x2001, 0x0005, 0x2004, 0xd08c, 0x0040, 0x1704, 0x047e, 0x1078,
+ 0x1913, 0x047f, 0x2460, 0x6010, 0xa080, 0x0002, 0x2004, 0xa005,
+ 0x0040, 0x174a, 0x0078, 0x16e1, 0x2001, 0x0207, 0x2004, 0xd09c,
+ 0x00c0, 0x16f0, 0x2001, 0x0005, 0x2004, 0xd08c, 0x00c0, 0x16f6,
+ 0x7804, 0xa084, 0x6000, 0x0040, 0x171b, 0xa086, 0x6000, 0x0040,
+ 0x171b, 0x0078, 0x16f0, 0x7007, 0x0000, 0xa016, 0x2218, 0x7000,
+ 0xa08e, 0x0001, 0x0040, 0x173c, 0xa08e, 0x0002, 0x00c0, 0x174a,
+ 0x0c7e, 0x0e7e, 0x6818, 0x2060, 0x1078, 0x203f, 0x2804, 0xac70,
+ 0x6034, 0xd09c, 0x00c0, 0x1738, 0x7308, 0x720c, 0x0078, 0x173a,
+ 0x7310, 0x7214, 0x0e7f, 0x0c7f, 0x7820, 0xa318, 0x7824, 0xa211,
+ 0x6810, 0xa300, 0x6812, 0x6814, 0xa201, 0x6816, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0x6100, 0xa18e, 0x0004, 0x00c0, 0x1753, 0x2009,
+ 0x0048, 0x1078, 0x775c, 0x0c7f, 0x0d7f, 0x127f, 0x007c, 0x0f7e,
+ 0x0e7e, 0x027e, 0x037e, 0x047e, 0x057e, 0x2071, 0xa8e7, 0x7000,
+ 0xa086, 0x0000, 0x0040, 0x17ba, 0x7004, 0xac06, 0x00c0, 0x17ab,
+ 0x2079, 0x0030, 0x7000, 0xa086, 0x0003, 0x0040, 0x17ab, 0x7804,
+ 0xd0fc, 0x00c0, 0x17a7, 0x20e1, 0x6000, 0x2011, 0x0032, 0x2001,
+ 0x0208, 0x200c, 0x2001, 0x0209, 0x2004, 0xa106, 0x00c0, 0x176f,
+ 0x8211, 0x00c0, 0x1777, 0x7804, 0xd0fc, 0x00c0, 0x17a7, 0x1078,
+ 0x1b22, 0x027e, 0x057e, 0x7803, 0x0004, 0x7804, 0xd0ac, 0x00c0,
+ 0x178d, 0x7803, 0x0002, 0x7803, 0x0009, 0x7003, 0x0003, 0x7007,
+ 0x0000, 0x057f, 0x027f, 0x2001, 0x015d, 0x2003, 0x0000, 0x2001,
+ 0x0160, 0x2502, 0x2001, 0x0138, 0x2202, 0x0078, 0x17ab, 0x1078,
+ 0x1913, 0x0078, 0x175f, 0x157e, 0x20a9, 0x0009, 0x2009, 0xa8ed,
+ 0x2104, 0xac06, 0x00c0, 0x17b5, 0x200a, 0xa188, 0x0003, 0x00f0,
+ 0x17b0, 0x157f, 0x057f, 0x047f, 0x037f, 0x027f, 0x0e7f, 0x0f7f,
+ 0x007c, 0x700c, 0x7110, 0xa106, 0x00c0, 0x17c9, 0x7003, 0x0000,
+ 0x007c, 0x2104, 0x7006, 0x2060, 0x8108, 0x211c, 0x8108, 0x2124,
+ 0x8108, 0xa182, 0xa908, 0x0048, 0x17d7, 0x2009, 0xa8ed, 0x7112,
+ 0x700c, 0xa106, 0x00c0, 0x17e0, 0x2001, 0x0138, 0x2003, 0x0008,
+ 0x8cff, 0x00c0, 0x17e7, 0x1078, 0x1b4d, 0x0078, 0x1854, 0x6010,
+ 0x2068, 0x2d58, 0x6828, 0xa406, 0x00c0, 0x17f2, 0x682c, 0xa306,
+ 0x0040, 0x182f, 0x601c, 0xa086, 0x0008, 0x0040, 0x182f, 0x6024,
+ 0xd0f4, 0x00c0, 0x181c, 0xd0d4, 0x0040, 0x1818, 0x6038, 0xa402,
+ 0x6034, 0xa303, 0x0040, 0x1806, 0x00c8, 0x1818, 0x643a, 0x6336,
+ 0x6c2a, 0x6b2e, 0x047e, 0x037e, 0x2400, 0x6c7c, 0xa402, 0x6812,
+ 0x2300, 0x6b80, 0xa303, 0x6816, 0x037f, 0x047f, 0x0078, 0x181c,
+ 0x1078, 0x9053, 0x0040, 0x17e3, 0x2001, 0xa674, 0x2004, 0xd0b4,
+ 0x00c0, 0x182b, 0x6018, 0x2004, 0xd0bc, 0x00c0, 0x182b, 0x6817,
+ 0x7fff, 0x6813, 0xffff, 0x1078, 0x208a, 0x00c0, 0x17e3, 0x0c7e,
+ 0x7004, 0x2060, 0x6024, 0xc0d4, 0x6026, 0x0c7f, 0x684c, 0xd0f4,
+ 0x0040, 0x1840, 0x6817, 0xffff, 0x6813, 0xffff, 0x0078, 0x17e3,
+ 0x6824, 0x2050, 0x6818, 0x2060, 0x6830, 0x2040, 0x6034, 0xa0cc,
+ 0x000f, 0x2009, 0x0011, 0x1078, 0x1855, 0x0040, 0x1853, 0x2009,
+ 0x0001, 0x1078, 0x1855, 0x2d58, 0x007c, 0x8aff, 0x0040, 0x18ec,
+ 0xa03e, 0x2730, 0x6850, 0xd0fc, 0x00c0, 0x1877, 0xd0f4, 0x00c0,
+ 0x1887, 0x0d7e, 0x2804, 0xac68, 0x2900, 0x0079, 0x1867, 0x18ce,
+ 0x188e, 0x188e, 0x18ce, 0x18ce, 0x18c6, 0x18ce, 0x188e, 0x18ce,
+ 0x1894, 0x1894, 0x18ce, 0x18ce, 0x18ce, 0x18bd, 0x1894, 0xc0fc,
+ 0x6852, 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0x0d7e, 0xd99c, 0x0040,
+ 0x18d1, 0x2804, 0xac68, 0x6f08, 0x6e0c, 0x0078, 0x18d1, 0xc0f4,
+ 0x6852, 0x6b6c, 0x6a70, 0x0d7e, 0x0078, 0x18d8, 0x6b08, 0x6a0c,
+ 0x6d00, 0x6c04, 0x0078, 0x18d1, 0x7b0c, 0xd3bc, 0x0040, 0x18b5,
+ 0x7004, 0x0e7e, 0x2070, 0x701c, 0x0e7f, 0xa086, 0x0008, 0x00c0,
+ 0x18b5, 0x7b08, 0xa39c, 0x0fff, 0x2d20, 0x0d7f, 0x0d7e, 0x6a14,
+ 0x82ff, 0x00c0, 0x18b0, 0x6810, 0xa302, 0x0048, 0x18b0, 0x6b10,
+ 0x2011, 0x0000, 0x2468, 0x0078, 0x18b7, 0x6b10, 0x6a14, 0x6d00,
+ 0x6c04, 0x6f08, 0x6e0c, 0x0078, 0x18d1, 0x0d7f, 0x0d7e, 0x6834,
+ 0xa084, 0x00ff, 0xa086, 0x001e, 0x00c0, 0x18ce, 0x0d7f, 0x1078,
+ 0x2026, 0x00c0, 0x1855, 0xa00e, 0x0078, 0x18ec, 0x0d7f, 0x1078,
+ 0x1332, 0x7b22, 0x7a26, 0x7d32, 0x7c36, 0x7f3a, 0x7e3e, 0x7902,
+ 0x7000, 0x8000, 0x7002, 0x0d7f, 0x6828, 0xa300, 0x682a, 0x682c,
+ 0xa201, 0x682e, 0x2300, 0x6b10, 0xa302, 0x6812, 0x2200, 0x6a14,
+ 0xa203, 0x6816, 0x1078, 0x2026, 0x007c, 0x1078, 0x1332, 0x1078,
+ 0x1c97, 0x7004, 0x2060, 0x0d7e, 0x6010, 0x2068, 0x7003, 0x0000,
+ 0x1078, 0x1af4, 0x1078, 0x8d06, 0x0040, 0x190c, 0x6808, 0x8001,
+ 0x680a, 0x697c, 0x6912, 0x6980, 0x6916, 0x682b, 0xffff, 0x682f,
+ 0xffff, 0x6850, 0xc0bd, 0x6852, 0x0d7f, 0x1078, 0x8a01, 0x0078,
+ 0x1adb, 0x1078, 0x1332, 0x127e, 0x2091, 0x2100, 0x007e, 0x017e,
+ 0x2b68, 0x6818, 0x2060, 0x7904, 0x7803, 0x0002, 0xa184, 0x0700,
+ 0x00c0, 0x18ef, 0xa184, 0x0003, 0xa086, 0x0003, 0x0040, 0x1911,
+ 0x7000, 0x0079, 0x192b, 0x1933, 0x1935, 0x1a34, 0x1ab2, 0x1ac9,
+ 0x1933, 0x1933, 0x1933, 0x1078, 0x1332, 0x8001, 0x7002, 0xa184,
+ 0x0880, 0x00c0, 0x194a, 0x8aff, 0x0040, 0x19d4, 0x2009, 0x0001,
+ 0x1078, 0x1855, 0x0040, 0x1adb, 0x2009, 0x0001, 0x1078, 0x1855,
+ 0x0078, 0x1adb, 0x7803, 0x0004, 0x7003, 0x0000, 0xd1bc, 0x00c0,
+ 0x19b2, 0x027e, 0x037e, 0x017e, 0x7808, 0xd0ec, 0x00c0, 0x1962,
+ 0x7c20, 0x7d24, 0x7e30, 0x7f34, 0x7803, 0x0009, 0x7003, 0x0004,
+ 0x0078, 0x1964, 0x1078, 0x1bd7, 0x017f, 0xd194, 0x0040, 0x196b,
+ 0x8aff, 0x0040, 0x19a1, 0x6b28, 0x6a2c, 0x2400, 0x686e, 0xa31a,
+ 0x2500, 0x6872, 0xa213, 0x6b2a, 0x6a2e, 0x0c7e, 0x7004, 0x2060,
+ 0x6024, 0xd0f4, 0x00c0, 0x197e, 0x633a, 0x6236, 0x0c7f, 0x2400,
+ 0x6910, 0xa100, 0x6812, 0x2500, 0x6914, 0xa101, 0x6816, 0x037f,
+ 0x027f, 0x2600, 0x681e, 0x2700, 0x6822, 0x1078, 0x203f, 0x2a00,
+ 0x6826, 0x2c00, 0x681a, 0x2800, 0x6832, 0x6850, 0xc0fd, 0x6852,
+ 0x6808, 0x8001, 0x680a, 0x00c0, 0x19a7, 0x684c, 0xd0e4, 0x0040,
+ 0x19a7, 0x7004, 0x2060, 0x2009, 0x0048, 0x1078, 0x775c, 0x7000,
+ 0xa086, 0x0004, 0x0040, 0x1adb, 0x7003, 0x0000, 0x1078, 0x17c1,
+ 0x0078, 0x1adb, 0x057e, 0x7d0c, 0xd5bc, 0x00c0, 0x19b9, 0x1078,
+ 0xa57e, 0x057f, 0x1078, 0x1af4, 0x0f7e, 0x7004, 0x2078, 0x1078,
+ 0x4963, 0x0040, 0x19c6, 0x7824, 0xc0f5, 0x7826, 0x0f7f, 0x682b,
+ 0xffff, 0x682f, 0xffff, 0x6808, 0x8001, 0x680a, 0x697c, 0x6912,
+ 0x6980, 0x6916, 0x0078, 0x1adb, 0x7004, 0x0c7e, 0x2060, 0x6024,
+ 0x0c7f, 0xd0f4, 0x0040, 0x19e1, 0x6808, 0x8001, 0x680a, 0x0078,
+ 0x19f5, 0x684c, 0xc0f5, 0x684e, 0x7814, 0xa005, 0x00c0, 0x19f9,
+ 0x7003, 0x0000, 0x6808, 0x8001, 0x680a, 0x00c0, 0x19f5, 0x7004,
+ 0x2060, 0x2009, 0x0048, 0x1078, 0x775c, 0x1078, 0x17c1, 0x0078,
+ 0x1adb, 0x7814, 0x6910, 0xa102, 0x6812, 0x6914, 0xa183, 0x0000,
+ 0x6816, 0x7814, 0x7908, 0xa18c, 0x0fff, 0xa192, 0x0841, 0x00c8,
+ 0x18ef, 0xa188, 0x0007, 0x8114, 0x8214, 0x8214, 0xa10a, 0x8104,
+ 0x8004, 0x8004, 0xa20a, 0x810b, 0x810b, 0x810b, 0x1078, 0x1b5e,
+ 0x7803, 0x0004, 0x780f, 0xffff, 0x7803, 0x0001, 0x7804, 0xd0fc,
+ 0x0040, 0x1a1e, 0x7803, 0x0002, 0x7803, 0x0004, 0x780f, 0x0076,
+ 0x7004, 0x7007, 0x0000, 0x2060, 0x2009, 0x0048, 0x1078, 0x775c,
+ 0x1078, 0x1b92, 0x0040, 0x19f5, 0x8001, 0x7002, 0xd194, 0x0040,
+ 0x1a46, 0x7804, 0xd0fc, 0x00c0, 0x191b, 0x8aff, 0x0040, 0x1adb,
+ 0x2009, 0x0001, 0x1078, 0x1855, 0x0078, 0x1adb, 0xa184, 0x0880,
+ 0x00c0, 0x1a53, 0x8aff, 0x0040, 0x1adb, 0x2009, 0x0001, 0x1078,
+ 0x1855, 0x0078, 0x1adb, 0x7803, 0x0004, 0x7003, 0x0000, 0xd1bc,
+ 0x00c0, 0x1a93, 0x027e, 0x037e, 0x7808, 0xd0ec, 0x00c0, 0x1a66,
+ 0x7803, 0x0009, 0x7003, 0x0004, 0x0078, 0x1a68, 0x1078, 0x1bd7,
+ 0x6b28, 0x6a2c, 0x1078, 0x203f, 0x0d7e, 0x0f7e, 0x2d78, 0x2804,
+ 0xac68, 0x6034, 0xd09c, 0x00c0, 0x1a83, 0x6808, 0x2008, 0xa31a,
+ 0x680c, 0xa213, 0x7810, 0xa100, 0x7812, 0x690c, 0x7814, 0xa101,
+ 0x7816, 0x0078, 0x1a8f, 0x6810, 0x2008, 0xa31a, 0x6814, 0xa213,
+ 0x7810, 0xa100, 0x7812, 0x6914, 0x7814, 0xa101, 0x7816, 0x0f7f,
+ 0x0d7f, 0x0078, 0x196d, 0x057e, 0x7d0c, 0x1078, 0xa57e, 0x057f,
+ 0x1078, 0x1af4, 0x0f7e, 0x7004, 0x2078, 0x1078, 0x4963, 0x0040,
+ 0x1aa4, 0x7824, 0xc0f5, 0x7826, 0x0f7f, 0x682b, 0xffff, 0x682f,
+ 0xffff, 0x6808, 0x8001, 0x680a, 0x697c, 0x6912, 0x6980, 0x6916,
+ 0x0078, 0x1adb, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0xa00d,
+ 0x0040, 0x1ac5, 0x6808, 0x8001, 0x680a, 0x00c0, 0x1ac5, 0x7004,
+ 0x2060, 0x2009, 0x0048, 0x1078, 0x775c, 0x1078, 0x17c1, 0x0078,
+ 0x1adb, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0x2060, 0x6010,
+ 0xa005, 0x0040, 0x1ac5, 0x2068, 0x6808, 0x8000, 0x680a, 0x6c28,
+ 0x6b2c, 0x1078, 0x17e0, 0x017f, 0x007f, 0x127f, 0x007c, 0x127e,
+ 0x2091, 0x2100, 0x7000, 0xa086, 0x0003, 0x00c0, 0x1af2, 0x700c,
+ 0x7110, 0xa106, 0x0040, 0x1af2, 0x20e1, 0x9028, 0x700f, 0xa8ed,
+ 0x7013, 0xa8ed, 0x127f, 0x007c, 0x0c7e, 0x1078, 0x1b22, 0x20e1,
+ 0x9028, 0x700c, 0x7110, 0xa106, 0x0040, 0x1b19, 0x2104, 0xa005,
+ 0x0040, 0x1b08, 0x2060, 0x6010, 0x2060, 0x6008, 0x8001, 0x600a,
+ 0xa188, 0x0003, 0xa182, 0xa908, 0x0048, 0x1b10, 0x2009, 0xa8ed,
+ 0x7112, 0x700c, 0xa106, 0x00c0, 0x1af9, 0x2011, 0x0008, 0x0078,
+ 0x1af9, 0x2001, 0x015d, 0x2003, 0x0000, 0x2001, 0x0138, 0x2202,
+ 0x0c7f, 0x007c, 0x2001, 0x0138, 0x2014, 0x2003, 0x0000, 0x2021,
+ 0xb015, 0x2001, 0x0141, 0x201c, 0xd3dc, 0x00c0, 0x1b3f, 0x2001,
+ 0x0109, 0x201c, 0xa39c, 0x0048, 0x00c0, 0x1b3f, 0x2001, 0x0111,
+ 0x201c, 0x83ff, 0x00c0, 0x1b3f, 0x8421, 0x00c0, 0x1b29, 0x007c,
+ 0x2011, 0x0201, 0x2009, 0x003c, 0x2204, 0xa005, 0x00c0, 0x1b4c,
+ 0x8109, 0x00c0, 0x1b44, 0x007c, 0x007c, 0x1078, 0x1b40, 0x0040,
+ 0x1b55, 0x780c, 0xd0a4, 0x0040, 0x1b5b, 0x1078, 0x1af4, 0xa085,
+ 0x0001, 0x0078, 0x1b5d, 0x1078, 0x1b92, 0x007c, 0x0e7e, 0x2071,
+ 0x0200, 0x7808, 0xa084, 0xf000, 0xa10d, 0x1078, 0x1b22, 0x2019,
+ 0x5000, 0x8319, 0x0040, 0x1b7c, 0x2001, 0xa908, 0x2004, 0xa086,
+ 0x0000, 0x0040, 0x1b7c, 0x2001, 0x0021, 0xd0fc, 0x0040, 0x1b69,
+ 0x1078, 0x1eaa, 0x0078, 0x1b67, 0x20e1, 0x7000, 0x7324, 0x7420,
+ 0x7028, 0x7028, 0x7426, 0x7037, 0x0001, 0x810f, 0x712e, 0x702f,
+ 0x0100, 0x7037, 0x0008, 0x7326, 0x7422, 0x2001, 0x0138, 0x2202,
+ 0x0e7f, 0x007c, 0x027e, 0x2001, 0x015d, 0x2001, 0x0000, 0x7908,
+ 0xa18c, 0x0fff, 0xa182, 0x0ffd, 0x0048, 0x1ba0, 0x2009, 0x0000,
+ 0xa190, 0x0007, 0xa294, 0x1ff8, 0x8214, 0x8214, 0x8214, 0x2001,
+ 0x020a, 0x82ff, 0x0040, 0x1bb5, 0x20e1, 0x6000, 0x200c, 0x200c,
+ 0x200c, 0x200c, 0x8211, 0x00c0, 0x1bae, 0x20e1, 0x7000, 0x200c,
+ 0x200c, 0x7003, 0x0000, 0x20e1, 0x6000, 0x2001, 0x0208, 0x200c,
+ 0x2001, 0x0209, 0x2004, 0xa106, 0x0040, 0x1bd4, 0x1078, 0x1b40,
+ 0x0040, 0x1bd2, 0x7908, 0xd1ec, 0x00c0, 0x1bd4, 0x790c, 0xd1a4,
+ 0x0040, 0x1b97, 0x1078, 0x1af4, 0xa006, 0x027f, 0x007c, 0x7c20,
+ 0x7d24, 0x7e30, 0x7f34, 0x700c, 0x7110, 0xa106, 0x0040, 0x1c69,
+ 0x7004, 0x017e, 0x210c, 0xa106, 0x017f, 0x0040, 0x1c69, 0x0d7e,
+ 0x0c7e, 0x216c, 0x2d00, 0xa005, 0x0040, 0x1c67, 0x681c, 0xa086,
+ 0x0008, 0x0040, 0x1c67, 0x6824, 0xd0d4, 0x00c0, 0x1c67, 0x6810,
+ 0x2068, 0x6850, 0xd0fc, 0x0040, 0x1c29, 0x8108, 0x2104, 0x6b2c,
+ 0xa306, 0x00c0, 0x1c67, 0x8108, 0x2104, 0x6a28, 0xa206, 0x00c0,
+ 0x1c67, 0x6850, 0xc0fc, 0xc0f5, 0x6852, 0x686c, 0x7822, 0x6870,
+ 0x7826, 0x681c, 0x7832, 0x6820, 0x7836, 0x6818, 0x2060, 0x6034,
+ 0xd09c, 0x0040, 0x1c24, 0x6830, 0x2004, 0xac68, 0x6808, 0x783a,
+ 0x680c, 0x783e, 0x0078, 0x1c65, 0xa006, 0x783a, 0x783e, 0x0078,
+ 0x1c65, 0x8108, 0x2104, 0xa005, 0x00c0, 0x1c67, 0x6b2c, 0xa306,
+ 0x00c0, 0x1c67, 0x8108, 0x2104, 0xa005, 0x00c0, 0x1c67, 0x6a28,
+ 0xa206, 0x00c0, 0x1c67, 0x6850, 0xc0f5, 0x6852, 0x6830, 0x2004,
+ 0x6918, 0xa160, 0xa180, 0x000d, 0x2004, 0xd09c, 0x00c0, 0x1c57,
+ 0x6008, 0x7822, 0x686e, 0x600c, 0x7826, 0x6872, 0x6000, 0x7832,
+ 0x6004, 0x7836, 0xa006, 0x783a, 0x783e, 0x0078, 0x1c65, 0x6010,
+ 0x7822, 0x686e, 0x6014, 0x7826, 0x6872, 0x6000, 0x7832, 0x6004,
+ 0x7836, 0x6008, 0x783a, 0x600c, 0x783e, 0x7803, 0x0011, 0x0c7f,
+ 0x0d7f, 0x007c, 0x0f7e, 0x0e7e, 0x017e, 0x027e, 0x2071, 0xa8e7,
+ 0x2079, 0x0030, 0x2011, 0x0050, 0x7000, 0xa086, 0x0000, 0x0040,
+ 0x1c92, 0x8211, 0x0040, 0x1c90, 0x2001, 0x0005, 0x2004, 0xd08c,
+ 0x0040, 0x1c79, 0x7904, 0xa18c, 0x0780, 0x017e, 0x1078, 0x1913,
+ 0x017f, 0x81ff, 0x00c0, 0x1c90, 0x2011, 0x0050, 0x0078, 0x1c74,
+ 0xa085, 0x0001, 0x027f, 0x017f, 0x0e7f, 0x0f7f, 0x007c, 0x7803,
+ 0x0004, 0x2009, 0x0064, 0x7804, 0xd0ac, 0x0040, 0x1ce8, 0x8109,
+ 0x00c0, 0x1c9b, 0x2009, 0x0100, 0x210c, 0xa18a, 0x0003, 0x1048,
+ 0x1332, 0x1078, 0x1fca, 0x0e7e, 0x0f7e, 0x2071, 0xa8d6, 0x2079,
+ 0x0010, 0x7004, 0xa086, 0x0000, 0x0040, 0x1ce0, 0x7800, 0x007e,
+ 0x7820, 0x007e, 0x7830, 0x007e, 0x7834, 0x007e, 0x7838, 0x007e,
+ 0x783c, 0x007e, 0x7803, 0x0004, 0x7823, 0x0000, 0x0005, 0x0005,
+ 0x2079, 0x0030, 0x7804, 0xd0ac, 0x10c0, 0x1332, 0x2079, 0x0010,
+ 0x007f, 0x783e, 0x007f, 0x783a, 0x007f, 0x7836, 0x007f, 0x7832,
+ 0x007f, 0x7822, 0x007f, 0x7802, 0x0f7f, 0x0e7f, 0x0078, 0x1ce6,
+ 0x0f7f, 0x0e7f, 0x7804, 0xd0ac, 0x10c0, 0x1332, 0x1078, 0x639b,
+ 0x007c, 0x0e7e, 0x2071, 0xa908, 0x7003, 0x0000, 0x0e7f, 0x007c,
+ 0x0d7e, 0xa280, 0x0004, 0x206c, 0x694c, 0xd1dc, 0x00c0, 0x1d6b,
+ 0x6934, 0xa184, 0x0007, 0x0079, 0x1cfd, 0x1d05, 0x1d56, 0x1d05,
+ 0x1d05, 0x1d05, 0x1d3b, 0x1d18, 0x1d07, 0x1078, 0x1332, 0x684c,
+ 0xd0b4, 0x0040, 0x1e79, 0x6860, 0x682e, 0x6816, 0x685c, 0x682a,
+ 0x6812, 0x687c, 0x680a, 0x6880, 0x680e, 0x6958, 0x0078, 0x1d5e,
+ 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e, 0x00c0, 0x1d05, 0x684c,
+ 0xd0b4, 0x0040, 0x1e79, 0x6860, 0x682e, 0x6816, 0x685c, 0x682a,
+ 0x6812, 0x687c, 0x680a, 0x6880, 0x680e, 0x6804, 0x681a, 0xa080,
+ 0x000d, 0x2004, 0xa084, 0x000f, 0xa080, 0x206a, 0x2004, 0x6832,
+ 0x6958, 0x0078, 0x1d67, 0xa18c, 0x00ff, 0xa186, 0x0015, 0x00c0,
+ 0x1d6b, 0x684c, 0xd0b4, 0x0040, 0x1e79, 0x6804, 0x681a, 0xa080,
+ 0x000d, 0x2004, 0xa084, 0x000f, 0xa080, 0x206a, 0x2004, 0x6832,
+ 0x6958, 0xa006, 0x682e, 0x682a, 0x0078, 0x1d67, 0x684c, 0xd0b4,
+ 0x0040, 0x18ed, 0x6958, 0xa006, 0x682e, 0x682a, 0x2d00, 0x681a,
+ 0x6834, 0xa084, 0x000f, 0xa080, 0x206a, 0x2004, 0x6832, 0x6926,
+ 0x684c, 0xc0dd, 0x684e, 0x0d7f, 0x007c, 0x0f7e, 0x2079, 0x0020,
+ 0x7804, 0xd0fc, 0x10c0, 0x1eaa, 0x0e7e, 0x0d7e, 0x2071, 0xa908,
+ 0x7000, 0xa005, 0x00c0, 0x1df0, 0x0c7e, 0x7206, 0xa280, 0x0004,
+ 0x205c, 0x7004, 0x2068, 0x7803, 0x0004, 0x6818, 0x0d7e, 0x2068,
+ 0x686c, 0x7812, 0x6890, 0x0f7e, 0x20e1, 0x9040, 0x2079, 0x0200,
+ 0x781a, 0x2079, 0x0100, 0x8004, 0x78d6, 0x0f7f, 0x0d7f, 0x2b68,
+ 0x6824, 0x2050, 0x6818, 0x2060, 0x6830, 0x2040, 0x6034, 0xa0cc,
+ 0x000f, 0x6908, 0x2001, 0x04fd, 0x2004, 0xa086, 0x0007, 0x0040,
+ 0x1db2, 0xa184, 0x0007, 0x0040, 0x1db2, 0x017e, 0x2009, 0x0008,
+ 0xa102, 0x017f, 0xa108, 0x791a, 0x7116, 0x701e, 0x680c, 0xa081,
+ 0x0000, 0x781e, 0x701a, 0xa006, 0x700e, 0x7012, 0x7004, 0x692c,
+ 0x6814, 0xa106, 0x00c0, 0x1dc9, 0x6928, 0x6810, 0xa106, 0x0040,
+ 0x1dd6, 0x037e, 0x047e, 0x6b14, 0x6c10, 0x1078, 0x208a, 0x047f,
+ 0x037f, 0x0040, 0x1dd6, 0x0c7f, 0x0078, 0x1df0, 0x8aff, 0x00c0,
+ 0x1dde, 0x0c7f, 0xa085, 0x0001, 0x0078, 0x1df0, 0x127e, 0x2091,
+ 0x8000, 0x2079, 0x0020, 0x2009, 0x0001, 0x1078, 0x1df4, 0x0040,
+ 0x1ded, 0x2009, 0x0001, 0x1078, 0x1df4, 0x127f, 0x0c7f, 0xa006,
+ 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x077e, 0x067e, 0x057e, 0x047e,
+ 0x037e, 0x027e, 0x8aff, 0x0040, 0x1e72, 0x700c, 0x7214, 0xa23a,
+ 0x7010, 0x7218, 0xa203, 0x0048, 0x1e71, 0xa705, 0x0040, 0x1e71,
+ 0xa03e, 0x2730, 0x6850, 0xd0fc, 0x00c0, 0x1e24, 0x0d7e, 0x2804,
+ 0xac68, 0x2900, 0x0079, 0x1e14, 0x1e53, 0x1e34, 0x1e34, 0x1e53,
+ 0x1e53, 0x1e4b, 0x1e53, 0x1e34, 0x1e53, 0x1e3a, 0x1e3a, 0x1e53,
+ 0x1e53, 0x1e53, 0x1e42, 0x1e3a, 0xc0fc, 0x6852, 0x6b6c, 0x6a70,
+ 0x6d1c, 0x6c20, 0xd99c, 0x0040, 0x1e57, 0x0d7e, 0x2804, 0xac68,
+ 0x6f08, 0x6e0c, 0x0078, 0x1e56, 0x6b08, 0x6a0c, 0x6d00, 0x6c04,
+ 0x0078, 0x1e56, 0x6b10, 0x6a14, 0x6d00, 0x6c04, 0x6f08, 0x6e0c,
+ 0x0078, 0x1e56, 0x0d7f, 0x0d7e, 0x6834, 0xa084, 0x00ff, 0xa086,
+ 0x001e, 0x00c0, 0x1e53, 0x0d7f, 0x1078, 0x2026, 0x00c0, 0x1dfa,
+ 0xa00e, 0x0078, 0x1e72, 0x0d7f, 0x1078, 0x1332, 0x0d7f, 0x7b22,
+ 0x7a26, 0x7d32, 0x7c36, 0x7f3a, 0x7e3e, 0x7902, 0x7000, 0x8000,
+ 0x7002, 0x6828, 0xa300, 0x682a, 0x682c, 0xa201, 0x682e, 0x700c,
+ 0xa300, 0x700e, 0x7010, 0xa201, 0x7012, 0x1078, 0x2026, 0x0078,
+ 0x1e72, 0xa006, 0x027f, 0x037f, 0x047f, 0x057f, 0x067f, 0x077f,
+ 0x007c, 0x1078, 0x1332, 0x027e, 0x2001, 0x0105, 0x2003, 0x0010,
+ 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0x2060,
+ 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040, 0x1e92, 0x6850,
+ 0xc0bd, 0x6852, 0x0d7f, 0x0c7e, 0x1078, 0x8a01, 0x0c7f, 0x2001,
+ 0xa8c0, 0x2004, 0xac06, 0x00c0, 0x1ea7, 0x20e1, 0x9040, 0x1078,
+ 0x738a, 0x2011, 0x0000, 0x1078, 0x70ea, 0x1078, 0x639b, 0x027f,
+ 0x0078, 0x1f76, 0x127e, 0x2091, 0x2200, 0x007e, 0x017e, 0x0f7e,
+ 0x0e7e, 0x0d7e, 0x0c7e, 0x2079, 0x0020, 0x2071, 0xa908, 0x2b68,
+ 0x6818, 0x2060, 0x7904, 0x7803, 0x0002, 0xa184, 0x0700, 0x00c0,
+ 0x1e7b, 0x7000, 0x0079, 0x1ec4, 0x1f76, 0x1ec8, 0x1f43, 0x1f74,
+ 0x8001, 0x7002, 0xd19c, 0x00c0, 0x1edc, 0x8aff, 0x0040, 0x1efb,
+ 0x2009, 0x0001, 0x1078, 0x1df4, 0x0040, 0x1f76, 0x2009, 0x0001,
+ 0x1078, 0x1df4, 0x0078, 0x1f76, 0x7803, 0x0004, 0xd194, 0x0040,
+ 0x1eec, 0x6850, 0xc0fc, 0x6852, 0x8aff, 0x00c0, 0x1ef1, 0x684c,
+ 0xc0f5, 0x684e, 0x0078, 0x1ef1, 0x1078, 0x203f, 0x6850, 0xc0fd,
+ 0x6852, 0x2a00, 0x6826, 0x2c00, 0x681a, 0x2800, 0x6832, 0x7003,
+ 0x0000, 0x0078, 0x1f76, 0x711c, 0x81ff, 0x0040, 0x1f11, 0x7918,
+ 0x7922, 0x7827, 0x0000, 0x7803, 0x0001, 0x7000, 0x8000, 0x7002,
+ 0x700c, 0xa100, 0x700e, 0x7010, 0xa081, 0x0000, 0x7012, 0x0078,
+ 0x1f76, 0x0f7e, 0x027e, 0x781c, 0x007e, 0x7818, 0x007e, 0x2079,
+ 0x0100, 0x7a14, 0xa284, 0x0004, 0xa085, 0x0012, 0x7816, 0x037e,
+ 0x2019, 0x1000, 0x8319, 0x1040, 0x1332, 0x7820, 0xd0bc, 0x00c0,
+ 0x1f22, 0x037f, 0x79c8, 0x007f, 0xa102, 0x017f, 0x007e, 0x017e,
+ 0x79c4, 0x007f, 0xa103, 0x78c6, 0x007f, 0x78ca, 0xa284, 0x0004,
+ 0xa085, 0x0012, 0x7816, 0x027f, 0x0f7f, 0x7803, 0x0008, 0x7003,
+ 0x0000, 0x0078, 0x1f76, 0x8001, 0x7002, 0xd194, 0x0040, 0x1f58,
+ 0x7804, 0xd0fc, 0x00c0, 0x1eba, 0xd19c, 0x00c0, 0x1f72, 0x8aff,
+ 0x0040, 0x1f76, 0x2009, 0x0001, 0x1078, 0x1df4, 0x0078, 0x1f76,
+ 0x027e, 0x037e, 0x6b28, 0x6a2c, 0x1078, 0x203f, 0x0d7e, 0x2804,
+ 0xac68, 0x6034, 0xd09c, 0x00c0, 0x1f6b, 0x6808, 0xa31a, 0x680c,
+ 0xa213, 0x0078, 0x1f6f, 0x6810, 0xa31a, 0x6814, 0xa213, 0x0d7f,
+ 0x0078, 0x1eec, 0x0078, 0x1eec, 0x1078, 0x1332, 0x0c7f, 0x0d7f,
+ 0x0e7f, 0x0f7f, 0x017f, 0x007f, 0x127f, 0x007c, 0x0f7e, 0x0e7e,
+ 0x2071, 0xa908, 0x7000, 0xa086, 0x0000, 0x0040, 0x1fc7, 0x2079,
+ 0x0020, 0x017e, 0x2009, 0x0207, 0x210c, 0xd194, 0x0040, 0x1fa4,
+ 0x2009, 0x020c, 0x210c, 0xa184, 0x0003, 0x0040, 0x1fa4, 0x1078,
+ 0xa5d2, 0x2001, 0x0133, 0x2004, 0xa005, 0x1040, 0x1332, 0x20e1,
+ 0x9040, 0x2001, 0x020c, 0x2102, 0x2009, 0x0206, 0x2104, 0x2009,
+ 0x0203, 0x210c, 0xa106, 0x00c0, 0x1faf, 0x20e1, 0x9040, 0x7804,
+ 0xd0fc, 0x0040, 0x1f8a, 0x1078, 0x1eaa, 0x7000, 0xa086, 0x0000,
+ 0x00c0, 0x1f8a, 0x017f, 0x7803, 0x0004, 0x7804, 0xd0ac, 0x00c0,
+ 0x1fbd, 0x20e1, 0x9040, 0x7803, 0x0002, 0x7003, 0x0000, 0x0e7f,
+ 0x0f7f, 0x007c, 0x027e, 0x0c7e, 0x0d7e, 0x0e7e, 0x0f7e, 0x2071,
+ 0xa908, 0x2079, 0x0020, 0x7000, 0xa086, 0x0000, 0x0040, 0x2003,
+ 0x7004, 0x2060, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040, 0x1fed,
+ 0x6850, 0xc0b5, 0x6852, 0x680c, 0x7a1c, 0xa206, 0x00c0, 0x1fed,
+ 0x6808, 0x7a18, 0xa206, 0x0040, 0x2009, 0x2001, 0x0105, 0x2003,
+ 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004,
+ 0x2060, 0x1078, 0x8a01, 0x20e1, 0x9040, 0x1078, 0x738a, 0x2011,
+ 0x0000, 0x1078, 0x70ea, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x027f,
+ 0x007c, 0x6810, 0x6a14, 0xa205, 0x00c0, 0x1fed, 0x684c, 0xc0dc,
+ 0x684e, 0x2c10, 0x1078, 0x1cf0, 0x2001, 0x0105, 0x2003, 0x0010,
+ 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003, 0x0000, 0x2069, 0xa8b1,
+ 0x6833, 0x0000, 0x683f, 0x0000, 0x0078, 0x2003, 0x8840, 0x2804,
+ 0xa005, 0x00c0, 0x203a, 0x6004, 0xa005, 0x0040, 0x203c, 0x681a,
+ 0x2060, 0x6034, 0xa084, 0x000f, 0xa080, 0x206a, 0x2044, 0x88ff,
+ 0x1040, 0x1332, 0x8a51, 0x007c, 0x2051, 0x0000, 0x007c, 0x8a50,
+ 0x8841, 0x2804, 0xa005, 0x00c0, 0x2059, 0x2c00, 0xad06, 0x0040,
+ 0x204e, 0x6000, 0xa005, 0x00c0, 0x204e, 0x2d00, 0x2060, 0x681a,
+ 0x6034, 0xa084, 0x000f, 0xa080, 0x207a, 0x2044, 0x88ff, 0x1040,
+ 0x1332, 0x007c, 0x0000, 0x0011, 0x0015, 0x0019, 0x001d, 0x0021,
+ 0x0025, 0x0029, 0x0000, 0x000f, 0x0015, 0x001b, 0x0021, 0x0027,
+ 0x0000, 0x0000, 0x0000, 0x205f, 0x205b, 0x0000, 0x0000, 0x2069,
+ 0x0000, 0x205f, 0x0000, 0x2066, 0x2063, 0x0000, 0x0000, 0x0000,
+ 0x2069, 0x2066, 0x0000, 0x2061, 0x2061, 0x0000, 0x0000, 0x2069,
+ 0x0000, 0x2061, 0x0000, 0x2067, 0x2067, 0x0000, 0x0000, 0x0000,
+ 0x2069, 0x2067, 0x0a7e, 0x097e, 0x087e, 0x6b2e, 0x6c2a, 0x6858,
+ 0xa055, 0x0040, 0x212d, 0x2d60, 0x6034, 0xa0cc, 0x000f, 0xa9c0,
+ 0x206a, 0xa986, 0x0007, 0x0040, 0x20a5, 0xa986, 0x000e, 0x0040,
+ 0x20a5, 0xa986, 0x000f, 0x00c0, 0x20a9, 0x605c, 0xa422, 0x6060,
+ 0xa31a, 0x2804, 0xa045, 0x00c0, 0x20b7, 0x0050, 0x20b1, 0x0078,
+ 0x212d, 0x6004, 0xa065, 0x0040, 0x212d, 0x0078, 0x2094, 0x2804,
+ 0xa005, 0x0040, 0x20d5, 0xac68, 0xd99c, 0x00c0, 0x20c5, 0x6808,
+ 0xa422, 0x680c, 0xa31b, 0x0078, 0x20c9, 0x6810, 0xa422, 0x6814,
+ 0xa31b, 0x0048, 0x20f4, 0x2300, 0xa405, 0x0040, 0x20db, 0x8a51,
+ 0x0040, 0x212d, 0x8840, 0x0078, 0x20b7, 0x6004, 0xa065, 0x0040,
+ 0x212d, 0x0078, 0x2094, 0x8a51, 0x0040, 0x212d, 0x8840, 0x2804,
+ 0xa005, 0x00c0, 0x20ee, 0x6004, 0xa065, 0x0040, 0x212d, 0x6034,
+ 0xa0cc, 0x000f, 0xa9c0, 0x206a, 0x2804, 0x2040, 0x2b68, 0x6850,
+ 0xc0fc, 0x6852, 0x0078, 0x2121, 0x8422, 0x8420, 0x831a, 0xa399,
+ 0x0000, 0x0d7e, 0x2b68, 0x6c6e, 0x6b72, 0x0d7f, 0xd99c, 0x00c0,
+ 0x210f, 0x6908, 0x2400, 0xa122, 0x690c, 0x2300, 0xa11b, 0x1048,
+ 0x1332, 0x6800, 0xa420, 0x6804, 0xa319, 0x0078, 0x211b, 0x6910,
+ 0x2400, 0xa122, 0x6914, 0x2300, 0xa11b, 0x1048, 0x1332, 0x6800,
+ 0xa420, 0x6804, 0xa319, 0x2b68, 0x6c1e, 0x6b22, 0x6850, 0xc0fd,
+ 0x6852, 0x2c00, 0x681a, 0x2800, 0x6832, 0x2a00, 0x6826, 0x007f,
+ 0x007f, 0x007f, 0xa006, 0x0078, 0x2132, 0x087f, 0x097f, 0x0a7f,
+ 0xa085, 0x0001, 0x007c, 0x2001, 0x0005, 0x2004, 0xa084, 0x0007,
+ 0x0079, 0x213a, 0x2142, 0x2143, 0x2146, 0x2149, 0x214e, 0x2151,
+ 0x2156, 0x215b, 0x007c, 0x1078, 0x1eaa, 0x007c, 0x1078, 0x1913,
+ 0x007c, 0x1078, 0x1913, 0x1078, 0x1eaa, 0x007c, 0x1078, 0x14be,
+ 0x007c, 0x1078, 0x1eaa, 0x1078, 0x14be, 0x007c, 0x1078, 0x1913,
+ 0x1078, 0x14be, 0x007c, 0x1078, 0x1913, 0x1078, 0x1eaa, 0x1078,
+ 0x14be, 0x007c, 0x127e, 0x2091, 0x2300, 0x2079, 0x0200, 0x2071,
+ 0xab80, 0x2069, 0xa600, 0x2009, 0x0004, 0x7912, 0x7817, 0x0004,
+ 0x1078, 0x251f, 0x781b, 0x0002, 0x20e1, 0x8700, 0x127f, 0x007c,
+ 0x127e, 0x2091, 0x2300, 0x781c, 0xa084, 0x0007, 0x0079, 0x2180,
+ 0x21a4, 0x2188, 0x218c, 0x2190, 0x2196, 0x219a, 0x219e, 0x21a2,
+ 0x1078, 0x548e, 0x0078, 0x21a4, 0x1078, 0x54da, 0x0078, 0x21a4,
+ 0x1078, 0x548e, 0x1078, 0x54da, 0x0078, 0x21a4, 0x1078, 0x21a6,
+ 0x0078, 0x21a4, 0x1078, 0x21a6, 0x0078, 0x21a4, 0x1078, 0x21a6,
+ 0x0078, 0x21a4, 0x1078, 0x21a6, 0x127f, 0x007c, 0x007e, 0x017e,
+ 0x027e, 0x1078, 0xa5d2, 0x7930, 0xa184, 0x0003, 0x0040, 0x21c9,
+ 0x2001, 0xa8c0, 0x2004, 0xa005, 0x0040, 0x21c5, 0x2001, 0x0133,
+ 0x2004, 0xa005, 0x1040, 0x1332, 0x0c7e, 0x2001, 0xa8c0, 0x2064,
+ 0x1078, 0x8a01, 0x0c7f, 0x0078, 0x21f2, 0x20e1, 0x9040, 0x0078,
+ 0x21f2, 0xa184, 0x0030, 0x0040, 0x21da, 0x6a00, 0xa286, 0x0003,
+ 0x00c0, 0x21d4, 0x0078, 0x21d6, 0x1078, 0x4224, 0x20e1, 0x9010,
+ 0x0078, 0x21f2, 0xa184, 0x00c0, 0x0040, 0x21ec, 0x0e7e, 0x037e,
+ 0x047e, 0x057e, 0x2071, 0xa8e7, 0x1078, 0x1af4, 0x057f, 0x047f,
+ 0x037f, 0x0e7f, 0x0078, 0x21f2, 0xa184, 0x0300, 0x0040, 0x21f2,
+ 0x20e1, 0x9020, 0x7932, 0x027f, 0x017f, 0x007f, 0x007c, 0x017e,
+ 0x0e7e, 0x0f7e, 0x2071, 0xa600, 0x7128, 0x2001, 0xa890, 0x2102,
+ 0x2001, 0xa898, 0x2102, 0xa182, 0x0211, 0x00c8, 0x220b, 0x2009,
+ 0x0008, 0x0078, 0x2235, 0xa182, 0x0259, 0x00c8, 0x2213, 0x2009,
+ 0x0007, 0x0078, 0x2235, 0xa182, 0x02c1, 0x00c8, 0x221b, 0x2009,
+ 0x0006, 0x0078, 0x2235, 0xa182, 0x0349, 0x00c8, 0x2223, 0x2009,
+ 0x0005, 0x0078, 0x2235, 0xa182, 0x0421, 0x00c8, 0x222b, 0x2009,
+ 0x0004, 0x0078, 0x2235, 0xa182, 0x0581, 0x00c8, 0x2233, 0x2009,
+ 0x0003, 0x0078, 0x2235, 0x2009, 0x0002, 0x2079, 0x0200, 0x7912,
+ 0x7817, 0x0004, 0x1078, 0x251f, 0x0f7f, 0x0e7f, 0x017f, 0x007c,
+ 0x127e, 0x2091, 0x2200, 0x2061, 0x0100, 0x2071, 0xa600, 0x6024,
+ 0x6026, 0x6053, 0x0030, 0x6033, 0x00ef, 0x60e7, 0x0000, 0x60eb,
+ 0x00ef, 0x60e3, 0x0008, 0x604b, 0xf7f7, 0x6043, 0x0000, 0x602f,
+ 0x0080, 0x602f, 0x0000, 0x6007, 0x0eaf, 0x600f, 0x00ff, 0x602b,
+ 0x002f, 0x127f, 0x007c, 0x2001, 0xa630, 0x2003, 0x0000, 0x2001,
+ 0xa62f, 0x2003, 0x0001, 0x007c, 0x127e, 0x2091, 0x2200, 0x007e,
+ 0x017e, 0x027e, 0x6124, 0xa184, 0x002c, 0x00c0, 0x227b, 0xa184,
+ 0x0007, 0x0079, 0x2281, 0xa195, 0x0004, 0xa284, 0x0007, 0x0079,
+ 0x2281, 0x22ad, 0x2289, 0x228d, 0x2291, 0x2297, 0x229b, 0x22a1,
+ 0x22a7, 0x1078, 0x5c56, 0x0078, 0x22ad, 0x1078, 0x5d45, 0x0078,
+ 0x22ad, 0x1078, 0x5d45, 0x1078, 0x5c56, 0x0078, 0x22ad, 0x1078,
+ 0x22b2, 0x0078, 0x22ad, 0x1078, 0x5c56, 0x1078, 0x22b2, 0x0078,
+ 0x22ad, 0x1078, 0x5d45, 0x1078, 0x22b2, 0x0078, 0x22ad, 0x1078,
+ 0x5d45, 0x1078, 0x5c56, 0x1078, 0x22b2, 0x027f, 0x017f, 0x007f,
+ 0x127f, 0x007c, 0x6124, 0xd1ac, 0x0040, 0x23ac, 0x017e, 0x047e,
+ 0x0c7e, 0x644c, 0xa486, 0xf0f0, 0x00c0, 0x22c5, 0x2061, 0x0100,
+ 0x644a, 0x6043, 0x0090, 0x6043, 0x0010, 0x74c6, 0xa48c, 0xff00,
+ 0x7034, 0xd084, 0x0040, 0x22dd, 0xa186, 0xf800, 0x00c0, 0x22dd,
+ 0x703c, 0xd084, 0x00c0, 0x22dd, 0xc085, 0x703e, 0x037e, 0x2418,
+ 0x2011, 0x8016, 0x1078, 0x361b, 0x037f, 0xa196, 0xff00, 0x0040,
+ 0x231f, 0x6030, 0xa084, 0x00ff, 0x810f, 0xa116, 0x0040, 0x231f,
+ 0x7130, 0xd184, 0x00c0, 0x231f, 0x2011, 0xa653, 0x2214, 0xd2ec,
+ 0x0040, 0x22fa, 0xc18d, 0x7132, 0x2011, 0xa653, 0x2214, 0xd2ac,
+ 0x00c0, 0x231f, 0x6240, 0xa294, 0x0010, 0x0040, 0x2306, 0x6248,
+ 0xa294, 0xff00, 0xa296, 0xff00, 0x0040, 0x231f, 0x7030, 0xd08c,
+ 0x0040, 0x2371, 0x7034, 0xd08c, 0x00c0, 0x2316, 0x2001, 0xa60c,
+ 0x200c, 0xd1ac, 0x00c0, 0x2371, 0xc1ad, 0x2102, 0x037e, 0x73c4,
+ 0x2011, 0x8013, 0x1078, 0x361b, 0x037f, 0x0078, 0x2371, 0x7034,
+ 0xd08c, 0x00c0, 0x232b, 0x2001, 0xa60c, 0x200c, 0xd1ac, 0x00c0,
+ 0x2371, 0xc1ad, 0x2102, 0x037e, 0x73c4, 0x2011, 0x8013, 0x1078,
+ 0x361b, 0x037f, 0x7130, 0xc185, 0x7132, 0x2011, 0xa653, 0x220c,
+ 0xd1a4, 0x0040, 0x2355, 0x017e, 0x2009, 0x0001, 0x2011, 0x0100,
+ 0x1078, 0x5bf1, 0x2019, 0x000e, 0x1078, 0xa195, 0xa484, 0x00ff,
+ 0xa080, 0x29c0, 0x200c, 0xa18c, 0xff00, 0x810f, 0x8127, 0xa006,
+ 0x2009, 0x000e, 0x1078, 0xa21d, 0x017f, 0xd1ac, 0x00c0, 0x2362,
+ 0x017e, 0x2009, 0x0000, 0x2019, 0x0004, 0x1078, 0x284f, 0x017f,
+ 0x0078, 0x2371, 0x157e, 0x20a9, 0x007f, 0x2009, 0x0000, 0x1078,
+ 0x45c4, 0x00c0, 0x236d, 0x1078, 0x42f8, 0x8108, 0x00f0, 0x2367,
+ 0x157f, 0x0c7f, 0x047f, 0x0f7e, 0x2079, 0xa8c4, 0x783c, 0xa086,
+ 0x0000, 0x0040, 0x2383, 0x6027, 0x0004, 0x783f, 0x0000, 0x2079,
+ 0x0140, 0x7803, 0x0000, 0x0f7f, 0x2011, 0x0003, 0x1078, 0x70e0,
+ 0x2011, 0x0002, 0x1078, 0x70ea, 0x1078, 0x6fc4, 0x037e, 0x2019,
+ 0x0000, 0x1078, 0x7058, 0x037f, 0x60e3, 0x0000, 0x017f, 0x2001,
+ 0xa600, 0x2014, 0xa296, 0x0004, 0x00c0, 0x23a4, 0xd19c, 0x00c0,
+ 0x23ac, 0x6228, 0xc29d, 0x622a, 0x2003, 0x0001, 0x2001, 0xa622,
+ 0x2003, 0x0000, 0x6027, 0x0020, 0xd194, 0x0040, 0x2490, 0x0f7e,
+ 0x2079, 0xa8c4, 0x783c, 0xa086, 0x0001, 0x00c0, 0x23d0, 0x017e,
+ 0x6027, 0x0004, 0x783f, 0x0000, 0x2079, 0x0140, 0x7803, 0x1000,
+ 0x7803, 0x0000, 0x2079, 0xa8b1, 0x7807, 0x0000, 0x7833, 0x0000,
+ 0x1078, 0x62d1, 0x1078, 0x639b, 0x017f, 0x0f7f, 0x0078, 0x2490,
+ 0x0f7f, 0x017e, 0x3900, 0xa082, 0xa9e3, 0x00c8, 0x23db, 0x017e,
+ 0x1078, 0x747a, 0x017f, 0x6220, 0xd2b4, 0x0040, 0x2446, 0x1078,
+ 0x5acb, 0x1078, 0x6e0f, 0x6027, 0x0004, 0x0f7e, 0x2019, 0xa8ba,
+ 0x2304, 0xa07d, 0x0040, 0x241c, 0x7804, 0xa086, 0x0032, 0x00c0,
+ 0x241c, 0x0d7e, 0x0c7e, 0x0e7e, 0x2069, 0x0140, 0x618c, 0x6288,
+ 0x7818, 0x608e, 0x7808, 0x608a, 0x6043, 0x0002, 0x2001, 0x0003,
+ 0x8001, 0x00c0, 0x2400, 0x6043, 0x0000, 0x6803, 0x1000, 0x6803,
+ 0x0000, 0x618e, 0x628a, 0x1078, 0x61cd, 0x1078, 0x62d1, 0x7810,
+ 0x2070, 0x7037, 0x0103, 0x2f60, 0x1078, 0x772d, 0x0e7f, 0x0c7f,
+ 0x0d7f, 0x0f7f, 0x017f, 0x007c, 0x0f7f, 0x0d7e, 0x2069, 0x0140,
+ 0x6804, 0xa084, 0x4000, 0x0040, 0x2429, 0x6803, 0x1000, 0x6803,
+ 0x0000, 0x0d7f, 0x0c7e, 0x2061, 0xa8b1, 0x6028, 0xa09a, 0x00c8,
+ 0x00c8, 0x2439, 0x8000, 0x602a, 0x0c7f, 0x1078, 0x6e01, 0x0078,
+ 0x248f, 0x2019, 0xa8ba, 0x2304, 0xa065, 0x0040, 0x2443, 0x2009,
+ 0x0027, 0x1078, 0x775c, 0x0c7f, 0x0078, 0x248f, 0xd2bc, 0x0040,
+ 0x248f, 0x1078, 0x5ad8, 0x6017, 0x0010, 0x6027, 0x0004, 0x0d7e,
+ 0x2069, 0x0140, 0x6804, 0xa084, 0x4000, 0x0040, 0x245b, 0x6803,
+ 0x1000, 0x6803, 0x0000, 0x0d7f, 0x0c7e, 0x2061, 0xa8b1, 0x6044,
+ 0xa09a, 0x00c8, 0x00c8, 0x247e, 0x8000, 0x6046, 0x603c, 0x0c7f,
+ 0xa005, 0x0040, 0x248f, 0x2009, 0x07d0, 0x1078, 0x5ad0, 0xa080,
+ 0x0007, 0x2004, 0xa086, 0x0006, 0x00c0, 0x247a, 0x6017, 0x0012,
+ 0x0078, 0x248f, 0x6017, 0x0016, 0x0078, 0x248f, 0x037e, 0x2019,
+ 0x0001, 0x1078, 0x7058, 0x037f, 0x2019, 0xa8c0, 0x2304, 0xa065,
+ 0x0040, 0x248e, 0x2009, 0x004f, 0x1078, 0x775c, 0x0c7f, 0x017f,
+ 0xd19c, 0x0040, 0x24e4, 0x7034, 0xd0ac, 0x00c0, 0x24c1, 0x017e,
+ 0x157e, 0x6027, 0x0008, 0x602f, 0x0020, 0x20a9, 0x000a, 0x00f0,
+ 0x249f, 0x602f, 0x0000, 0x6150, 0xa185, 0x1400, 0x6052, 0x20a9,
+ 0x0320, 0x00e0, 0x24a9, 0x2091, 0x6000, 0x6020, 0xd09c, 0x00c0,
+ 0x24b8, 0x157f, 0x6152, 0x017f, 0x6027, 0x0008, 0x0078, 0x24e4,
+ 0x1078, 0x2577, 0x00f0, 0x24a9, 0x157f, 0x6152, 0x017f, 0x6027,
+ 0x0008, 0x017e, 0x6028, 0xc09c, 0x602a, 0x2011, 0x0003, 0x1078,
+ 0x70e0, 0x2011, 0x0002, 0x1078, 0x70ea, 0x1078, 0x6fc4, 0x037e,
+ 0x2019, 0x0000, 0x1078, 0x7058, 0x037f, 0x60e3, 0x0000, 0x1078,
+ 0xa5ad, 0x1078, 0xa5cb, 0x2001, 0xa600, 0x2003, 0x0004, 0x6027,
+ 0x0008, 0x1078, 0x1246, 0x017f, 0xa18c, 0xffd0, 0x6126, 0x007c,
+ 0x007e, 0x017e, 0x027e, 0x0e7e, 0x0f7e, 0x127e, 0x2091, 0x8000,
+ 0x2071, 0xa600, 0x71bc, 0x70be, 0xa116, 0x0040, 0x2518, 0x81ff,
+ 0x0040, 0x2500, 0x2011, 0x8011, 0x1078, 0x361b, 0x0078, 0x2518,
+ 0x2011, 0x8012, 0x1078, 0x361b, 0x2001, 0xa672, 0x2004, 0xd0fc,
+ 0x00c0, 0x2518, 0x037e, 0x0c7e, 0x1078, 0x6f9f, 0x2061, 0x0100,
+ 0x2019, 0x0028, 0x2009, 0x0000, 0x1078, 0x284f, 0x0c7f, 0x037f,
+ 0x127f, 0x0f7f, 0x0e7f, 0x027f, 0x017f, 0x007f, 0x007c, 0x0c7e,
+ 0x0f7e, 0x007e, 0x027e, 0x2061, 0x0100, 0xa190, 0x253b, 0x2204,
+ 0x60f2, 0x2011, 0x2548, 0x6000, 0xa082, 0x0003, 0x00c8, 0x2534,
+ 0x2001, 0x00ff, 0x0078, 0x2535, 0x2204, 0x60ee, 0x027f, 0x007f,
+ 0x0f7f, 0x0c7f, 0x007c, 0x0840, 0x0840, 0x0840, 0x0580, 0x0420,
+ 0x0348, 0x02c0, 0x0258, 0x0210, 0x01a8, 0x01a8, 0x01a8, 0x01a8,
+ 0x0140, 0x00f8, 0x00d0, 0x00b0, 0x00a0, 0x2028, 0xa18c, 0x00ff,
+ 0x2130, 0xa094, 0xff00, 0x00c0, 0x2558, 0x81ff, 0x0040, 0x255c,
+ 0x1078, 0x5761, 0x0078, 0x2563, 0xa080, 0x29c0, 0x200c, 0xa18c,
+ 0xff00, 0x810f, 0xa006, 0x007c, 0xa080, 0x29c0, 0x200c, 0xa18c,
+ 0x00ff, 0x007c, 0x0c7e, 0x2061, 0xa600, 0x6030, 0x0040, 0x2573,
+ 0xc09d, 0x0078, 0x2574, 0xc09c, 0x6032, 0x0c7f, 0x007c, 0x007e,
+ 0x157e, 0x0f7e, 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854, 0xd08c,
+ 0x00c0, 0x2584, 0x00f0, 0x257e, 0x0f7f, 0x157f, 0x007f, 0x007c,
+ 0x0c7e, 0x007e, 0x2061, 0x0100, 0x6030, 0x007e, 0x6048, 0x007e,
+ 0x60e4, 0x007e, 0x60e8, 0x007e, 0x6050, 0x007e, 0x60f0, 0x007e,
+ 0x60ec, 0x007e, 0x600c, 0x007e, 0x6004, 0x007e, 0x6028, 0x007e,
+ 0x60e0, 0x007e, 0x602f, 0x0100, 0x602f, 0x0000, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x602f, 0x0040, 0x602f, 0x0000, 0x007f, 0x60e2,
+ 0x007f, 0x602a, 0x007f, 0x6006, 0x007f, 0x600e, 0x007f, 0x60ee,
+ 0x007f, 0x60f2, 0x007f, 0x6052, 0x007f, 0x60ea, 0x007f, 0x60e6,
+ 0x007f, 0x604a, 0x007f, 0x6032, 0x007f, 0x0c7f, 0x007c, 0x25e7,
+ 0x25eb, 0x25ef, 0x25f5, 0x25fb, 0x2601, 0x2607, 0x260f, 0x2617,
+ 0x261d, 0x2623, 0x262b, 0x2633, 0x263b, 0x2643, 0x264d, 0x2657,
+ 0x2657, 0x2657, 0x2657, 0x2657, 0x2657, 0x2657, 0x2657, 0x2657,
+ 0x2657, 0x2657, 0x2657, 0x2657, 0x2657, 0x2657, 0x2657, 0x107e,
+ 0x007e, 0x0078, 0x2670, 0x107e, 0x007e, 0x0078, 0x2670, 0x107e,
+ 0x007e, 0x1078, 0x226c, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078,
+ 0x226c, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078, 0x2133, 0x0078,
+ 0x2670, 0x107e, 0x007e, 0x1078, 0x2133, 0x0078, 0x2670, 0x107e,
+ 0x007e, 0x1078, 0x226c, 0x1078, 0x2133, 0x0078, 0x2670, 0x107e,
+ 0x007e, 0x1078, 0x226c, 0x1078, 0x2133, 0x0078, 0x2670, 0x107e,
+ 0x007e, 0x1078, 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078,
+ 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078, 0x226c, 0x1078,
+ 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078, 0x226c, 0x1078,
+ 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078, 0x2133, 0x1078,
+ 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078, 0x2133, 0x1078,
+ 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078, 0x226c, 0x1078,
+ 0x2133, 0x1078, 0x2178, 0x0078, 0x2670, 0x107e, 0x007e, 0x1078,
+ 0x226c, 0x1078, 0x2133, 0x1078, 0x2178, 0x0078, 0x2670, 0x0005,
+ 0x0078, 0x2657, 0xb084, 0x003c, 0x8004, 0x8004, 0x0079, 0x2660,
+ 0x2670, 0x25ed, 0x25f1, 0x25f7, 0x25fd, 0x2603, 0x2609, 0x2611,
+ 0x2619, 0x261f, 0x2625, 0x262d, 0x2635, 0x263d, 0x2645, 0x264f,
+ 0x0008, 0x265a, 0x007f, 0x107f, 0x2091, 0x8001, 0x007c, 0x0c7e,
+ 0x027e, 0x047e, 0x2021, 0x0000, 0x1078, 0x4967, 0x00c0, 0x2772,
+ 0x70cc, 0xd09c, 0x0040, 0x268e, 0xd084, 0x00c0, 0x268e, 0xd0bc,
+ 0x00c0, 0x2772, 0x1078, 0x2776, 0x0078, 0x2772, 0xd0cc, 0x00c0,
+ 0x2772, 0xd094, 0x0040, 0x2698, 0x7097, 0xffff, 0x0078, 0x2772,
+ 0x2001, 0x010c, 0x203c, 0x7284, 0xd284, 0x0040, 0x2701, 0xd28c,
+ 0x00c0, 0x2701, 0x037e, 0x7394, 0xa38e, 0xffff, 0x0040, 0x26ab,
+ 0x83ff, 0x00c0, 0x26ad, 0x2019, 0x0001, 0x8314, 0xa2e0, 0xacc0,
+ 0x2c04, 0xa38c, 0x0001, 0x0040, 0x26ba, 0xa084, 0xff00, 0x8007,
+ 0x0078, 0x26bc, 0xa084, 0x00ff, 0xa70e, 0x0040, 0x26f6, 0xa08e,
+ 0x0000, 0x0040, 0x26f6, 0xa08e, 0x00ff, 0x00c0, 0x26d3, 0x7230,
+ 0xd284, 0x00c0, 0x26fc, 0x7284, 0xc28d, 0x7286, 0x7097, 0xffff,
+ 0x037f, 0x0078, 0x2701, 0x2009, 0x0000, 0x1078, 0x254d, 0x1078,
+ 0x455c, 0x00c0, 0x26f9, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006,
+ 0x00c0, 0x26f0, 0x7030, 0xd08c, 0x0040, 0x26ea, 0x6000, 0xd0bc,
+ 0x0040, 0x26f0, 0x1078, 0x278c, 0x0040, 0x26f9, 0x0078, 0x26f6,
+ 0x1078, 0x28c4, 0x1078, 0x27b9, 0x0040, 0x26f9, 0x8318, 0x0078,
+ 0x26ad, 0x7396, 0x0078, 0x26fe, 0x7097, 0xffff, 0x037f, 0x0078,
+ 0x2772, 0xa780, 0x29c0, 0x203c, 0xa7bc, 0xff00, 0x873f, 0x2041,
+ 0x007e, 0x7094, 0xa096, 0xffff, 0x00c0, 0x2713, 0x2009, 0x0000,
+ 0x28a8, 0x0078, 0x271f, 0xa812, 0x0048, 0x271b, 0x2008, 0xa802,
+ 0x20a8, 0x0078, 0x271f, 0x7097, 0xffff, 0x0078, 0x2772, 0x2700,
+ 0x157e, 0x017e, 0xa106, 0x0040, 0x2766, 0xc484, 0x1078, 0x45c4,
+ 0x0040, 0x2730, 0x1078, 0x455c, 0x00c0, 0x276f, 0x0078, 0x2731,
+ 0xc485, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x2740,
+ 0x7030, 0xd08c, 0x0040, 0x275e, 0x6000, 0xd0bc, 0x00c0, 0x275e,
+ 0x7284, 0xd28c, 0x0040, 0x2756, 0x6004, 0xa084, 0x00ff, 0xa082,
+ 0x0006, 0x0048, 0x2766, 0xd484, 0x00c0, 0x2752, 0x1078, 0x457f,
+ 0x0078, 0x2754, 0x1078, 0x298e, 0x0078, 0x2766, 0x1078, 0x28c4,
+ 0x1078, 0x27b9, 0x0040, 0x276f, 0x0078, 0x2766, 0x1078, 0x2959,
+ 0x0040, 0x2766, 0x1078, 0x278c, 0x0040, 0x276f, 0x017f, 0x8108,
+ 0x157f, 0x00f0, 0x271f, 0x7097, 0xffff, 0x0078, 0x2772, 0x017f,
+ 0x157f, 0x7196, 0x047f, 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x017e,
+ 0x7097, 0x0001, 0x2009, 0x007e, 0x1078, 0x455c, 0x00c0, 0x2789,
+ 0x1078, 0x28c4, 0x1078, 0x27b9, 0x0040, 0x2789, 0x70cc, 0xc0bd,
+ 0x70ce, 0x017f, 0x0c7f, 0x007c, 0x017e, 0x077e, 0x0d7e, 0x0c7e,
+ 0x2c68, 0x2001, 0xa657, 0x2004, 0xa084, 0x00ff, 0x6842, 0x1078,
+ 0x76c7, 0x0040, 0x27b4, 0x2d00, 0x601a, 0x601f, 0x0001, 0x2001,
+ 0x0000, 0x1078, 0x44ee, 0x2001, 0x0000, 0x1078, 0x4502, 0x127e,
+ 0x2091, 0x8000, 0x7090, 0x8000, 0x7092, 0x127f, 0x2009, 0x0004,
+ 0x1078, 0x775c, 0xa085, 0x0001, 0x0c7f, 0x0d7f, 0x077f, 0x017f,
+ 0x007c, 0x017e, 0x077e, 0x0d7e, 0x0c7e, 0x2c68, 0x2001, 0xa657,
+ 0x2004, 0xa084, 0x00ff, 0x6842, 0x1078, 0x9187, 0x0040, 0x27f2,
+ 0x2d00, 0x601a, 0x6800, 0xc0c4, 0x6802, 0x68a0, 0xa086, 0x007e,
+ 0x0040, 0x27db, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0,
+ 0x27db, 0x1078, 0x2880, 0x601f, 0x0001, 0x2001, 0x0000, 0x1078,
+ 0x44ee, 0x2001, 0x0002, 0x1078, 0x4502, 0x127e, 0x2091, 0x8000,
+ 0x7090, 0x8000, 0x7092, 0x127f, 0x2009, 0x0002, 0x1078, 0x775c,
+ 0xa085, 0x0001, 0x0c7f, 0x0d7f, 0x077f, 0x017f, 0x007c, 0x0c7e,
+ 0x027e, 0x2009, 0x0080, 0x1078, 0x455c, 0x00c0, 0x2805, 0x1078,
+ 0x2808, 0x0040, 0x2805, 0x70d3, 0xffff, 0x027f, 0x0c7f, 0x007c,
+ 0x017e, 0x077e, 0x0d7e, 0x0c7e, 0x2c68, 0x1078, 0x76c7, 0x0040,
+ 0x282a, 0x2d00, 0x601a, 0x601f, 0x0001, 0x2001, 0x0000, 0x1078,
+ 0x44ee, 0x2001, 0x0002, 0x1078, 0x4502, 0x127e, 0x2091, 0x8000,
+ 0x70d4, 0x8000, 0x70d6, 0x127f, 0x2009, 0x0002, 0x1078, 0x775c,
+ 0xa085, 0x0001, 0x0c7f, 0x0d7f, 0x077f, 0x017f, 0x007c, 0x0c7e,
+ 0x0d7e, 0x127e, 0x2091, 0x8000, 0x2009, 0x007f, 0x1078, 0x455c,
+ 0x00c0, 0x284b, 0x2c68, 0x1078, 0x76c7, 0x0040, 0x284b, 0x2d00,
+ 0x601a, 0x6312, 0x601f, 0x0001, 0x620a, 0x2009, 0x0022, 0x1078,
+ 0x775c, 0xa085, 0x0001, 0x127f, 0x0d7f, 0x0c7f, 0x007c, 0x0e7e,
+ 0x0c7e, 0x067e, 0x037e, 0x027e, 0x1078, 0x5f0e, 0x1078, 0x5eae,
+ 0x1078, 0x8068, 0x2130, 0x81ff, 0x0040, 0x2864, 0x20a9, 0x007e,
+ 0x2009, 0x0000, 0x0078, 0x2868, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0x017e, 0x1078, 0x45c4, 0x00c0, 0x2871, 0x1078, 0x47e9, 0x1078,
+ 0x42f8, 0x017f, 0x8108, 0x00f0, 0x2868, 0x86ff, 0x00c0, 0x287a,
+ 0x1078, 0x119b, 0x027f, 0x037f, 0x067f, 0x0c7f, 0x0e7f, 0x007c,
+ 0x0e7e, 0x0c7e, 0x037e, 0x027e, 0x017e, 0x6218, 0x2270, 0x72a0,
+ 0x027e, 0x2019, 0x0029, 0x1078, 0x5f01, 0x077e, 0x2039, 0x0000,
+ 0x1078, 0x5e0a, 0x2c08, 0x1078, 0x9f8b, 0x077f, 0x017f, 0x2e60,
+ 0x1078, 0x47e9, 0x6210, 0x6314, 0x1078, 0x42f8, 0x6212, 0x6316,
+ 0x017f, 0x027f, 0x037f, 0x0c7f, 0x0e7f, 0x007c, 0x0e7e, 0x007e,
+ 0x6018, 0xa080, 0x0028, 0x2004, 0xd0bc, 0x00c0, 0x28ba, 0x2071,
+ 0xa600, 0x7090, 0xa005, 0x0040, 0x28b7, 0x8001, 0x7092, 0x007f,
+ 0x0e7f, 0x007c, 0x2071, 0xa600, 0x70d4, 0xa005, 0x0040, 0x28b7,
+ 0x8001, 0x70d6, 0x0078, 0x28b7, 0x6000, 0xc08c, 0x6002, 0x007c,
+ 0x0f7e, 0x0e7e, 0x0c7e, 0x037e, 0x027e, 0x017e, 0x157e, 0x2178,
+ 0x81ff, 0x00c0, 0x28d7, 0x20a9, 0x0001, 0x0078, 0x28f2, 0x2001,
+ 0xa653, 0x2004, 0xd0c4, 0x0040, 0x28ee, 0xd0a4, 0x0040, 0x28ee,
+ 0x047e, 0x6018, 0xa080, 0x0028, 0x2024, 0xa4a4, 0x00ff, 0x8427,
+ 0xa006, 0x2009, 0x002d, 0x1078, 0xa21d, 0x047f, 0x20a9, 0x00ff,
+ 0x2011, 0x0000, 0x027e, 0xa28e, 0x007e, 0x0040, 0x2936, 0xa28e,
+ 0x007f, 0x0040, 0x2936, 0xa28e, 0x0080, 0x0040, 0x2936, 0xa288,
+ 0xa735, 0x210c, 0x81ff, 0x0040, 0x2936, 0x8fff, 0x1040, 0x2942,
+ 0x0c7e, 0x2160, 0x2001, 0x0001, 0x1078, 0x4972, 0x0c7f, 0x2019,
+ 0x0029, 0x1078, 0x5f01, 0x077e, 0x2039, 0x0000, 0x1078, 0x5e0a,
+ 0x0c7e, 0x027e, 0x2160, 0x6204, 0xa294, 0x00ff, 0xa286, 0x0006,
+ 0x00c0, 0x2926, 0x6007, 0x0404, 0x0078, 0x292b, 0x2001, 0x0004,
+ 0x8007, 0xa215, 0x6206, 0x027f, 0x0c7f, 0x017e, 0x2c08, 0x1078,
+ 0x9f8b, 0x017f, 0x077f, 0x2160, 0x1078, 0x47e9, 0x027f, 0x8210,
+ 0x00f0, 0x28f2, 0x157f, 0x017f, 0x027f, 0x037f, 0x0c7f, 0x0e7f,
+ 0x0f7f, 0x007c, 0x047e, 0x027e, 0x017e, 0x2001, 0xa653, 0x2004,
+ 0xd0c4, 0x0040, 0x2955, 0xd0a4, 0x0040, 0x2955, 0xa006, 0x2220,
+ 0x8427, 0x2009, 0x0029, 0x1078, 0xa21d, 0x017f, 0x027f, 0x047f,
+ 0x007c, 0x017e, 0x027e, 0x037e, 0x0c7e, 0x7284, 0x82ff, 0x0040,
+ 0x2987, 0xa290, 0xa653, 0x2214, 0xd2ac, 0x00c0, 0x2987, 0x2100,
+ 0x1078, 0x2564, 0x81ff, 0x0040, 0x2989, 0x2019, 0x0001, 0x8314,
+ 0xa2e0, 0xacc0, 0x2c04, 0xd384, 0x0040, 0x297b, 0xa084, 0xff00,
+ 0x8007, 0x0078, 0x297d, 0xa084, 0x00ff, 0xa116, 0x0040, 0x2989,
+ 0xa096, 0x00ff, 0x0040, 0x2987, 0x8318, 0x0078, 0x296f, 0xa085,
+ 0x0001, 0x0c7f, 0x037f, 0x027f, 0x017f, 0x007c, 0x017e, 0x0c7e,
+ 0x127e, 0x2091, 0x8000, 0x017e, 0x027e, 0x037e, 0x2110, 0x027e,
+ 0x2019, 0x0029, 0x1078, 0x73d0, 0x027f, 0x1078, 0xa4f1, 0x037f,
+ 0x027f, 0x017f, 0xa180, 0xa735, 0x2004, 0xa065, 0x0040, 0x29b7,
+ 0x017e, 0x0c7e, 0x1078, 0x9187, 0x017f, 0x1040, 0x1332, 0x611a,
+ 0x1078, 0x2880, 0x1078, 0x772d, 0x017f, 0x1078, 0x457f, 0x127f,
+ 0x0c7f, 0x017f, 0x007c, 0x2001, 0xa633, 0x2004, 0xd0cc, 0x007c,
+ 0x7eef, 0x7de8, 0x7ce4, 0x80e2, 0x7be1, 0x80e0, 0x80dc, 0x80da,
+ 0x7ad9, 0x80d6, 0x80d5, 0x80d4, 0x80d3, 0x80d2, 0x80d1, 0x79ce,
+ 0x78cd, 0x80cc, 0x80cb, 0x80ca, 0x80c9, 0x80c7, 0x80c6, 0x77c5,
+ 0x76c3, 0x80bc, 0x80ba, 0x75b9, 0x80b6, 0x74b5, 0x73b4, 0x72b3,
+ 0x80b2, 0x80b1, 0x80ae, 0x71ad, 0x80ac, 0x70ab, 0x6faa, 0x6ea9,
+ 0x80a7, 0x6da6, 0x6ca5, 0x6ba3, 0x6a9f, 0x699e, 0x689d, 0x809b,
+ 0x8098, 0x6797, 0x6690, 0x658f, 0x6488, 0x6384, 0x6282, 0x8081,
+ 0x8080, 0x617c, 0x607a, 0x8079, 0x5f76, 0x8075, 0x8074, 0x8073,
+ 0x8072, 0x8071, 0x806e, 0x5e6d, 0x806c, 0x5d6b, 0x5c6a, 0x5b69,
+ 0x8067, 0x5a66, 0x5965, 0x5863, 0x575c, 0x565a, 0x5559, 0x8056,
+ 0x8055, 0x5454, 0x5353, 0x5252, 0x5151, 0x504e, 0x4f4d, 0x804c,
+ 0x804b, 0x4e4a, 0x4d49, 0x8047, 0x4c46, 0x8045, 0x8043, 0x803c,
+ 0x803a, 0x8039, 0x8036, 0x4b35, 0x8034, 0x4a33, 0x4932, 0x4831,
+ 0x802e, 0x472d, 0x462c, 0x452b, 0x442a, 0x4329, 0x4227, 0x8026,
+ 0x8025, 0x4123, 0x401f, 0x3f1e, 0x3e1d, 0x3d1b, 0x3c18, 0x8017,
+ 0x8010, 0x3b0f, 0x3a08, 0x8004, 0x3902, 0x8001, 0x8000, 0x8000,
+ 0x3800, 0x3700, 0x3600, 0x8000, 0x3500, 0x8000, 0x8000, 0x8000,
+ 0x3400, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x3300,
+ 0x3200, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x3100,
+ 0x3000, 0x8000, 0x8000, 0x2f00, 0x8000, 0x2e00, 0x2d00, 0x2c00,
+ 0x8000, 0x8000, 0x8000, 0x2b00, 0x8000, 0x2a00, 0x2900, 0x2800,
+ 0x8000, 0x2700, 0x2600, 0x2500, 0x2400, 0x2300, 0x2200, 0x8000,
+ 0x8000, 0x2100, 0x2000, 0x1f00, 0x1e00, 0x1d00, 0x1c00, 0x8000,
+ 0x8000, 0x1b00, 0x1a00, 0x8000, 0x1900, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x1800, 0x8000, 0x1700, 0x1600, 0x1500,
+ 0x8000, 0x1400, 0x1300, 0x1200, 0x1100, 0x1000, 0x0f00, 0x8000,
+ 0x8000, 0x0e00, 0x0d00, 0x0c00, 0x0b00, 0x0a00, 0x0900, 0x8000,
+ 0x8000, 0x0800, 0x0700, 0x8000, 0x0600, 0x8000, 0x8000, 0x8000,
+ 0x0500, 0x0400, 0x0300, 0x8000, 0x0200, 0x8000, 0x8000, 0x8000,
+ 0x0100, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x2071, 0xa682, 0x7003, 0x0002, 0xa006, 0x7012, 0x7016, 0x703a,
+ 0x703e, 0x7033, 0xa692, 0x7037, 0xa692, 0x7007, 0x0001, 0x2061,
+ 0xa6d2, 0x6003, 0x0002, 0x007c, 0x0090, 0x2ae7, 0x0068, 0x2ae7,
+ 0x2071, 0xa682, 0x2b78, 0x7818, 0xd084, 0x00c0, 0x2ae7, 0x2a60,
+ 0x7820, 0xa08e, 0x0069, 0x00c0, 0x2bd7, 0x0079, 0x2b6b, 0x007c,
+ 0x2071, 0xa682, 0x7004, 0x0079, 0x2aed, 0x2af1, 0x2af2, 0x2afc,
+ 0x2b0e, 0x007c, 0x0090, 0x2afb, 0x0068, 0x2afb, 0x2b78, 0x7818,
+ 0xd084, 0x0040, 0x2b1a, 0x007c, 0x2b78, 0x2061, 0xa6d2, 0x6008,
+ 0xa08e, 0x0100, 0x0040, 0x2b09, 0xa086, 0x0200, 0x0040, 0x2bcf,
+ 0x007c, 0x7014, 0x2068, 0x2a60, 0x7018, 0x007a, 0x7010, 0x2068,
+ 0x6834, 0xa086, 0x0103, 0x0040, 0x2b16, 0x007c, 0x2a60, 0x2b78,
+ 0x7018, 0x007a, 0x2a60, 0x7820, 0xa08a, 0x0040, 0x00c8, 0x2b23,
+ 0x61bc, 0x0079, 0x2b2b, 0x2100, 0xa08a, 0x003f, 0x00c8, 0x2bcb,
+ 0x61bc, 0x0079, 0x2b6b, 0x2bad, 0x2bdf, 0x2be7, 0x2beb, 0x2bf3,
+ 0x2bf9, 0x2bfd, 0x2c09, 0x2c0d, 0x2c17, 0x2c1b, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2c1f, 0x2bcb, 0x2c2f, 0x2c46, 0x2c5d, 0x2cdd, 0x2ce2,
+ 0x2d0f, 0x2d69, 0x2d7a, 0x2d98, 0x2dd9, 0x2de3, 0x2df0, 0x2e03,
+ 0x2e22, 0x2e2b, 0x2e68, 0x2e6e, 0x2bcb, 0x2e8a, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x2bcb, 0x2e91, 0x2e9b, 0x2bcb, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb, 0x2ea3, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x2bcb, 0x2eb5, 0x2ece, 0x2bcb, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x2bcb, 0x2ee0, 0x2f37, 0x2f95, 0x2fa9, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x398e, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x2bcb, 0x2c17, 0x2c1b, 0x2fc0, 0x2bcb, 0x2fcd,
+ 0x3a26, 0x3a83, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb,
+ 0x2bcb, 0x2bcb, 0x2bcb, 0x301a, 0x314f, 0x316b, 0x3177, 0x31da,
+ 0x3233, 0x323e, 0x327d, 0x328c, 0x329b, 0x329e, 0x2fd1, 0x32c2,
+ 0x331e, 0x332b, 0x343c, 0x356f, 0x3599, 0x36a6, 0x2bcb, 0x36b6,
+ 0x36f0, 0x37bf, 0x2bcb, 0x2bcb, 0x2bcb, 0x2bcb, 0x3827, 0x3843,
+ 0x38bd, 0x3977, 0x713c, 0x0078, 0x2bad, 0x2021, 0x4000, 0x1078,
+ 0x35f5, 0x127e, 0x2091, 0x8000, 0x0068, 0x2bba, 0x7818, 0xd084,
+ 0x0040, 0x2bbd, 0x127f, 0x0078, 0x2bb1, 0x7c22, 0x7926, 0x7a2a,
+ 0x7b2e, 0x781b, 0x0001, 0x2091, 0x4080, 0x7007, 0x0001, 0x2091,
+ 0x5000, 0x127f, 0x007c, 0x2021, 0x4001, 0x0078, 0x2baf, 0x2021,
+ 0x4002, 0x0078, 0x2baf, 0x2021, 0x4003, 0x0078, 0x2baf, 0x2021,
+ 0x4005, 0x0078, 0x2baf, 0x2021, 0x4006, 0x0078, 0x2baf, 0xa02e,
+ 0x2520, 0x7b28, 0x7a2c, 0x7824, 0x7930, 0x0078, 0x3604, 0x7823,
+ 0x0004, 0x7824, 0x007a, 0xa02e, 0x2520, 0x7b28, 0x7a2c, 0x7824,
+ 0x7930, 0x0078, 0x3608, 0x7924, 0x7828, 0x2114, 0x200a, 0x0078,
+ 0x2bad, 0x7924, 0x2114, 0x0078, 0x2bad, 0x2099, 0x0009, 0x20a1,
+ 0x0009, 0x20a9, 0x0007, 0x53a3, 0x7924, 0x7a28, 0x7b2c, 0x0078,
+ 0x2bad, 0x7824, 0x2060, 0x0078, 0x2c21, 0x2009, 0x0001, 0x2011,
+ 0x0013, 0x2019, 0x0018, 0x783b, 0x0017, 0x0078, 0x2bad, 0x7d38,
+ 0x7c3c, 0x0078, 0x2be1, 0x7d38, 0x7c3c, 0x0078, 0x2bed, 0x2061,
+ 0x1000, 0x610c, 0xa006, 0x2c14, 0xa200, 0x8c60, 0x8109, 0x00c0,
+ 0x2c23, 0x2010, 0xa005, 0x0040, 0x2bad, 0x0078, 0x2bd3, 0x2069,
+ 0xa652, 0x7824, 0x7930, 0xa11a, 0x00c8, 0x2bdb, 0x8019, 0x0040,
+ 0x2bdb, 0x684a, 0x6942, 0x782c, 0x6852, 0x7828, 0x6856, 0xa006,
+ 0x685a, 0x685e, 0x1078, 0x4eae, 0x0078, 0x2bad, 0x2069, 0xa652,
+ 0x7824, 0x7934, 0xa11a, 0x00c8, 0x2bdb, 0x8019, 0x0040, 0x2bdb,
+ 0x684e, 0x6946, 0x782c, 0x6862, 0x7828, 0x6866, 0xa006, 0x686a,
+ 0x686e, 0x1078, 0x4a3e, 0x0078, 0x2bad, 0xa02e, 0x2520, 0x81ff,
+ 0x00c0, 0x2bd7, 0x7924, 0x7b28, 0x7a2c, 0x20a9, 0x0005, 0x20a1,
+ 0xa689, 0x41a1, 0x1078, 0x35ba, 0x0040, 0x2bd7, 0x2009, 0x0020,
+ 0x1078, 0x3604, 0x701b, 0x2c75, 0x007c, 0x6834, 0x2008, 0xa084,
+ 0x00ff, 0xa096, 0x0011, 0x0040, 0x2c85, 0xa096, 0x0019, 0x0040,
+ 0x2c85, 0xa096, 0x0015, 0x00c0, 0x2bd7, 0x810f, 0xa18c, 0x00ff,
+ 0x0040, 0x2bd7, 0x710e, 0x700c, 0x8001, 0x0040, 0x2cb6, 0x700e,
+ 0x1078, 0x35ba, 0x0040, 0x2bd7, 0x2009, 0x0020, 0x2061, 0xa6d2,
+ 0x6224, 0x6328, 0x642c, 0x6530, 0xa290, 0x0040, 0xa399, 0x0000,
+ 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x1078, 0x3604, 0x701b, 0x2ca9,
+ 0x007c, 0x6834, 0xa084, 0x00ff, 0xa096, 0x0002, 0x0040, 0x2cb4,
+ 0xa096, 0x000a, 0x00c0, 0x2bd7, 0x0078, 0x2c8b, 0x7010, 0x2068,
+ 0x6838, 0xc0fd, 0x683a, 0x1078, 0x4431, 0x00c0, 0x2cc4, 0x7007,
+ 0x0003, 0x701b, 0x2cc6, 0x007c, 0x1078, 0x4b51, 0x127e, 0x2091,
+ 0x8000, 0x20a9, 0x0005, 0x2099, 0xa689, 0x530a, 0x2100, 0xa210,
+ 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0xad80, 0x000d,
+ 0x2009, 0x0020, 0x127f, 0x0078, 0x3608, 0x61a4, 0x7824, 0x60a6,
+ 0x0078, 0x2bad, 0x2091, 0x8000, 0x7823, 0x4000, 0x7827, 0x4953,
+ 0x782b, 0x5020, 0x782f, 0x2020, 0x2009, 0x017f, 0x2104, 0x7832,
+ 0x3f00, 0x7836, 0x2061, 0x0100, 0x6200, 0x2061, 0x0200, 0x603c,
+ 0x8007, 0xa205, 0x783a, 0x2009, 0x04fd, 0x2104, 0x783e, 0x781b,
+ 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x2071, 0x0010, 0x20c1,
+ 0x00f0, 0xa08a, 0x0003, 0x00c8, 0x0427, 0x0078, 0x0423, 0x81ff,
+ 0x00c0, 0x2bd7, 0x7924, 0x810f, 0xa18c, 0x00ff, 0x1078, 0x45c4,
+ 0x00c0, 0x2bdb, 0x7e38, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0048,
+ 0x2d23, 0x0078, 0x2bdb, 0x7c28, 0x7d2c, 0x1078, 0x47a4, 0xd28c,
+ 0x00c0, 0x2d2e, 0x1078, 0x4736, 0x0078, 0x2d30, 0x1078, 0x4772,
+ 0x00c0, 0x2d5a, 0x2061, 0xad00, 0x127e, 0x2091, 0x8000, 0x6000,
+ 0xa086, 0x0000, 0x0040, 0x2d48, 0x6010, 0xa06d, 0x0040, 0x2d48,
+ 0x683c, 0xa406, 0x00c0, 0x2d48, 0x6840, 0xa506, 0x0040, 0x2d53,
+ 0x127f, 0xace0, 0x0010, 0x2001, 0xa616, 0x2004, 0xac02, 0x00c8,
+ 0x2bd7, 0x0078, 0x2d34, 0x1078, 0x8a01, 0x127f, 0x0040, 0x2bd7,
+ 0x0078, 0x2bad, 0xa00e, 0x2001, 0x0005, 0x1078, 0x4b51, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x8f85, 0x1078, 0x4a73, 0x127f, 0x0078,
+ 0x2bad, 0x81ff, 0x00c0, 0x2bd7, 0x1078, 0x35d2, 0x0040, 0x2bdb,
+ 0x1078, 0x4673, 0x0040, 0x2bd7, 0x1078, 0x47b2, 0x0040, 0x2bd7,
+ 0x0078, 0x2bad, 0x81ff, 0x00c0, 0x2bd7, 0x1078, 0x35e4, 0x0040,
+ 0x2bdb, 0x1078, 0x482f, 0x0040, 0x2bd7, 0x2019, 0x0005, 0x1078,
+ 0x47d3, 0x0040, 0x2bd7, 0x7828, 0xa08a, 0x1000, 0x00c8, 0x2bdb,
+ 0x8003, 0x800b, 0x810b, 0xa108, 0x1078, 0x5a52, 0x0078, 0x2bad,
+ 0x127e, 0x2091, 0x8000, 0x81ff, 0x0040, 0x2da2, 0x2009, 0x0001,
+ 0x0078, 0x2dd3, 0x2029, 0x00ff, 0x6450, 0x2400, 0xa506, 0x0040,
+ 0x2dcd, 0x2508, 0x1078, 0x45c4, 0x00c0, 0x2dcd, 0x1078, 0x482f,
+ 0x00c0, 0x2db8, 0x2009, 0x0002, 0x62ac, 0x2518, 0x0078, 0x2dd3,
+ 0x2019, 0x0004, 0x1078, 0x47d3, 0x00c0, 0x2dc2, 0x2009, 0x0006,
+ 0x0078, 0x2dd3, 0x7824, 0xa08a, 0x1000, 0x00c8, 0x2dd6, 0x8003,
+ 0x800b, 0x810b, 0xa108, 0x1078, 0x5a52, 0x8529, 0x00c8, 0x2da5,
+ 0x127f, 0x0078, 0x2bad, 0x127f, 0x0078, 0x2bd7, 0x127f, 0x0078,
+ 0x2bdb, 0x1078, 0x35d2, 0x0040, 0x2bdb, 0x1078, 0x46e7, 0x1078,
+ 0x47a4, 0x0078, 0x2bad, 0x81ff, 0x00c0, 0x2bd7, 0x1078, 0x35d2,
+ 0x0040, 0x2bdb, 0x1078, 0x46d6, 0x1078, 0x47a4, 0x0078, 0x2bad,
+ 0x81ff, 0x00c0, 0x2bd7, 0x1078, 0x35d2, 0x0040, 0x2bdb, 0x1078,
+ 0x4775, 0x0040, 0x2bd7, 0x1078, 0x4484, 0x1078, 0x472f, 0x1078,
+ 0x47a4, 0x0078, 0x2bad, 0x1078, 0x35d2, 0x0040, 0x2bdb, 0x1078,
+ 0x4673, 0x0040, 0x2bd7, 0x62a0, 0x2019, 0x0005, 0x0c7e, 0x1078,
+ 0x47e9, 0x0c7f, 0x1078, 0x5f01, 0x077e, 0x2039, 0x0000, 0x1078,
+ 0x5e0a, 0x2009, 0x0000, 0x1078, 0x9f8b, 0x077f, 0x1078, 0x47a4,
+ 0x0078, 0x2bad, 0x1078, 0x35d2, 0x0040, 0x2bdb, 0x1078, 0x47a4,
+ 0x2208, 0x0078, 0x2bad, 0x157e, 0x0d7e, 0x0e7e, 0x2069, 0xa714,
+ 0x6810, 0x6914, 0xa10a, 0x00c8, 0x2e37, 0x2009, 0x0000, 0x6816,
+ 0x2011, 0x0000, 0x2019, 0x0000, 0x20a9, 0x00ff, 0x2069, 0xa735,
+ 0x2d04, 0xa075, 0x0040, 0x2e4c, 0x704c, 0x1078, 0x2e56, 0xa210,
+ 0x7080, 0x1078, 0x2e56, 0xa318, 0x8d68, 0x00f0, 0x2e40, 0x2300,
+ 0xa218, 0x0e7f, 0x0d7f, 0x157f, 0x0078, 0x2bad, 0x0f7e, 0x017e,
+ 0xa07d, 0x0040, 0x2e65, 0x2001, 0x0000, 0x8000, 0x2f0c, 0x81ff,
+ 0x0040, 0x2e65, 0x2178, 0x0078, 0x2e5d, 0x017f, 0x0f7f, 0x007c,
+ 0x2069, 0xa714, 0x6910, 0x62a8, 0x0078, 0x2bad, 0x81ff, 0x00c0,
+ 0x2bd7, 0x6150, 0xa190, 0x29c0, 0x2214, 0xa294, 0x00ff, 0x6070,
+ 0xa084, 0xff00, 0xa215, 0x636c, 0x67cc, 0xd79c, 0x0040, 0x2e84,
+ 0x2031, 0x0001, 0x0078, 0x2e86, 0x2031, 0x0000, 0x7e3a, 0x7f3e,
+ 0x0078, 0x2bad, 0x6140, 0x6244, 0x2019, 0xa8a2, 0x231c, 0x0078,
+ 0x2bad, 0x127e, 0x2091, 0x8000, 0x6134, 0x6338, 0xa006, 0x2010,
+ 0x127f, 0x0078, 0x2bad, 0x1078, 0x35e4, 0x0040, 0x2bdb, 0x6244,
+ 0x6338, 0x0078, 0x2bad, 0x6140, 0x6244, 0x7824, 0x6042, 0x7b28,
+ 0x6346, 0x2069, 0xa652, 0x831f, 0xa305, 0x6816, 0x782c, 0x2069,
+ 0xa8a2, 0x2d1c, 0x206a, 0x0078, 0x2bad, 0x017e, 0x127e, 0x2091,
+ 0x8000, 0x7824, 0x6036, 0xd094, 0x0040, 0x2ec8, 0x7828, 0xa085,
+ 0x0001, 0x2009, 0xa8ab, 0x200a, 0x2001, 0xffff, 0x1078, 0x5ae6,
+ 0x782c, 0x603a, 0x127f, 0x017f, 0x0078, 0x2bad, 0x1078, 0x35e4,
+ 0x0040, 0x2bdb, 0x7828, 0xa00d, 0x0040, 0x2bdb, 0x782c, 0xa005,
+ 0x0040, 0x2bdb, 0x6244, 0x6146, 0x6338, 0x603a, 0x0078, 0x2bad,
+ 0x2001, 0xa600, 0x2004, 0xa086, 0x0003, 0x00c0, 0x2bd7, 0x0c7e,
+ 0x2061, 0x0100, 0x7924, 0x810f, 0xa18c, 0x00ff, 0xa196, 0x00ff,
+ 0x00c0, 0x2ef7, 0x6030, 0xa085, 0xff00, 0x0078, 0x2f06, 0xa182,
+ 0x007f, 0x00c8, 0x2f30, 0xa188, 0x29c0, 0x210c, 0xa18c, 0x00ff,
+ 0x6030, 0xa116, 0x0040, 0x2f30, 0x810f, 0xa105, 0x127e, 0x2091,
+ 0x8000, 0x007e, 0x1078, 0x76c7, 0x007f, 0x0040, 0x2f2c, 0x601a,
+ 0x600b, 0xbc09, 0x601f, 0x0001, 0x1078, 0x35ba, 0x0040, 0x2f33,
+ 0x6837, 0x0000, 0x7007, 0x0003, 0x6833, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x701b, 0x2f8e, 0x2d00, 0x6012, 0x2009, 0x0032, 0x1078,
+ 0x775c, 0x127f, 0x0c7f, 0x007c, 0x127f, 0x0c7f, 0x0078, 0x2bd7,
+ 0x0c7f, 0x0078, 0x2bdb, 0x1078, 0x772d, 0x0078, 0x2f2c, 0x2001,
+ 0xa600, 0x2004, 0xa086, 0x0003, 0x00c0, 0x2bd7, 0x0c7e, 0x2061,
+ 0x0100, 0x7924, 0x810f, 0xa18c, 0x00ff, 0xa196, 0x00ff, 0x00c0,
+ 0x2f4e, 0x6030, 0xa085, 0xff00, 0x0078, 0x2f5d, 0xa182, 0x007f,
+ 0x00c8, 0x2f87, 0xa188, 0x29c0, 0x210c, 0xa18c, 0x00ff, 0x6030,
+ 0xa116, 0x0040, 0x2f87, 0x810f, 0xa105, 0x127e, 0x2091, 0x8000,
+ 0x007e, 0x1078, 0x76c7, 0x007f, 0x0040, 0x2f83, 0x601a, 0x600b,
+ 0xbc05, 0x601f, 0x0001, 0x1078, 0x35ba, 0x0040, 0x2f8a, 0x6837,
+ 0x0000, 0x7007, 0x0003, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a,
+ 0x701b, 0x2f8e, 0x2d00, 0x6012, 0x2009, 0x0032, 0x1078, 0x775c,
+ 0x127f, 0x0c7f, 0x007c, 0x127f, 0x0c7f, 0x0078, 0x2bd7, 0x0c7f,
+ 0x0078, 0x2bdb, 0x1078, 0x772d, 0x0078, 0x2f83, 0x6830, 0xa086,
+ 0x0100, 0x0040, 0x2bd7, 0x0078, 0x2bad, 0x2061, 0xa933, 0x127e,
+ 0x2091, 0x8000, 0x6000, 0xd084, 0x0040, 0x2fa6, 0x6104, 0x6208,
+ 0x2019, 0xa612, 0x231c, 0x127f, 0x0078, 0x2bad, 0x127f, 0x0078,
+ 0x2bdb, 0x81ff, 0x00c0, 0x2bd7, 0x127e, 0x2091, 0x8000, 0x6248,
+ 0x6064, 0xa202, 0x0048, 0x2fbd, 0xa085, 0x0001, 0x1078, 0x256a,
+ 0x1078, 0x3c9e, 0x127f, 0x0078, 0x2bad, 0x127f, 0x0078, 0x2bdb,
+ 0x127e, 0x2091, 0x8000, 0x20a9, 0x0012, 0x2001, 0xa640, 0x20a0,
+ 0xa006, 0x40a4, 0x127f, 0x0078, 0x2bad, 0x7d38, 0x7c3c, 0x0078,
+ 0x2c5f, 0x7824, 0xa09c, 0x00ff, 0xa39a, 0x0003, 0x00c8, 0x2bd7,
+ 0x6250, 0xa084, 0xff00, 0x8007, 0xa206, 0x00c0, 0x2fe9, 0x2001,
+ 0xa640, 0x2009, 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0078,
+ 0x3608, 0x81ff, 0x00c0, 0x2bd7, 0x1078, 0x35e4, 0x0040, 0x2bdb,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x2bd7, 0x0c7e,
+ 0x1078, 0x35ba, 0x0c7f, 0x0040, 0x2bd7, 0x6837, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x1078, 0x8e4a, 0x0040, 0x2bd7, 0x7007, 0x0003,
+ 0x701b, 0x300b, 0x007c, 0x6830, 0xa086, 0x0100, 0x0040, 0x2bd7,
+ 0xad80, 0x000e, 0x2009, 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x0078, 0x3608, 0x1078, 0x35ba, 0x0040, 0x2bd7, 0x1078, 0x42dd,
+ 0x2009, 0x001c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x3604,
+ 0x701b, 0x302b, 0x007c, 0xade8, 0x000d, 0x6800, 0xa005, 0x0040,
+ 0x2bdb, 0x6804, 0xd0ac, 0x0040, 0x3038, 0xd0a4, 0x0040, 0x2bdb,
+ 0xd094, 0x0040, 0x3043, 0x0c7e, 0x2061, 0x0100, 0x6104, 0xa18c,
+ 0xffdf, 0x6106, 0x0c7f, 0xd08c, 0x0040, 0x304e, 0x0c7e, 0x2061,
+ 0x0100, 0x6104, 0xa18d, 0x0010, 0x6106, 0x0c7f, 0x2009, 0x0100,
+ 0x210c, 0xa18a, 0x0002, 0x0048, 0x3063, 0xd084, 0x0040, 0x3063,
+ 0x6a28, 0xa28a, 0x007f, 0x00c8, 0x2bdb, 0xa288, 0x29c0, 0x210c,
+ 0xa18c, 0x00ff, 0x6156, 0xd0dc, 0x0040, 0x306c, 0x6828, 0xa08a,
+ 0x007f, 0x00c8, 0x2bdb, 0x6052, 0x6808, 0xa08a, 0x0100, 0x0048,
+ 0x2bdb, 0xa08a, 0x0841, 0x00c8, 0x2bdb, 0xa084, 0x0007, 0x00c0,
+ 0x2bdb, 0x680c, 0xa005, 0x0040, 0x2bdb, 0x6810, 0xa005, 0x0040,
+ 0x2bdb, 0x6848, 0x6940, 0xa10a, 0x00c8, 0x2bdb, 0x8001, 0x0040,
+ 0x2bdb, 0x684c, 0x6944, 0xa10a, 0x00c8, 0x2bdb, 0x8001, 0x0040,
+ 0x2bdb, 0x6804, 0xd0fc, 0x0040, 0x30c2, 0x1078, 0x35ba, 0x0040,
+ 0x2bd7, 0x2009, 0x0014, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0xa290,
+ 0x0038, 0xa399, 0x0000, 0x1078, 0x3604, 0x701b, 0x30a8, 0x007c,
+ 0xade8, 0x000d, 0x20a9, 0x0014, 0x2d98, 0x2069, 0xa66e, 0x2da0,
+ 0x53a3, 0x7010, 0xa0e8, 0x000d, 0x2001, 0xa672, 0x200c, 0xd1e4,
+ 0x0040, 0x30c2, 0x0c7e, 0x2061, 0x0100, 0x6004, 0xa085, 0x0b00,
+ 0x6006, 0x0c7f, 0x20a9, 0x001c, 0x2d98, 0x2069, 0xa652, 0x2da0,
+ 0x53a3, 0x6814, 0xa08c, 0x00ff, 0x6142, 0x8007, 0xa084, 0x00ff,
+ 0x6046, 0x1078, 0x4eae, 0x1078, 0x49ce, 0x1078, 0x4a3e, 0x6000,
+ 0xa086, 0x0000, 0x00c0, 0x314d, 0x6808, 0x602a, 0x1078, 0x21f7,
+ 0x6818, 0x691c, 0x6a20, 0x6b24, 0x8007, 0x810f, 0x8217, 0x831f,
+ 0x6016, 0x611a, 0x621e, 0x6322, 0x6c04, 0xd4f4, 0x0040, 0x30fa,
+ 0x6830, 0x6934, 0x6a38, 0x6b3c, 0x8007, 0x810f, 0x8217, 0x831f,
+ 0x0078, 0x30fc, 0xa084, 0xf0ff, 0x6006, 0x610a, 0x620e, 0x6312,
+ 0x1078, 0x5b19, 0x6904, 0xd1fc, 0x0040, 0x312f, 0x0c7e, 0x2009,
+ 0x0000, 0x20a9, 0x0001, 0x6b70, 0xd384, 0x0040, 0x312c, 0x0078,
+ 0x3116, 0x839d, 0x00c8, 0x312c, 0x3508, 0x8109, 0x1078, 0x5480,
+ 0x6878, 0x6016, 0x6874, 0x2008, 0xa084, 0xff00, 0x8007, 0x600a,
+ 0xa184, 0x00ff, 0x6006, 0x8108, 0x00c0, 0x312a, 0x6003, 0x0003,
+ 0x0078, 0x312c, 0x6003, 0x0001, 0x00f0, 0x3111, 0x0c7f, 0x0c7e,
+ 0x2061, 0x0100, 0x602f, 0x0040, 0x602f, 0x0000, 0x0c7f, 0x1078,
+ 0x3819, 0x0040, 0x313d, 0x1078, 0x256a, 0x60c0, 0xa005, 0x0040,
+ 0x3149, 0x6003, 0x0001, 0x2091, 0x301d, 0x1078, 0x4224, 0x0078,
+ 0x314d, 0x6003, 0x0004, 0x2091, 0x301d, 0x0078, 0x2bad, 0x6000,
+ 0xa086, 0x0000, 0x0040, 0x2bd7, 0x2069, 0xa652, 0x7830, 0x6842,
+ 0x7834, 0x6846, 0x6804, 0xd0fc, 0x0040, 0x3162, 0x2009, 0x0030,
+ 0x0078, 0x3164, 0x2009, 0x001c, 0x2d00, 0x7a2c, 0x7b28, 0x7c3c,
+ 0x7d38, 0x0078, 0x3608, 0xa006, 0x1078, 0x256a, 0x81ff, 0x00c0,
+ 0x2bd7, 0x1078, 0x42dd, 0x1078, 0x4224, 0x0078, 0x2bad, 0x81ff,
+ 0x00c0, 0x2bd7, 0x6184, 0x81ff, 0x0040, 0x3191, 0x703f, 0x0000,
+ 0x2001, 0xacc0, 0x2009, 0x0040, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x3608, 0x701b, 0x2baa, 0x127f,
+ 0x007c, 0x703f, 0x0001, 0x0d7e, 0x2069, 0xacc0, 0x20a9, 0x0040,
+ 0x20a1, 0xacc0, 0x2019, 0xffff, 0x43a4, 0x6550, 0xa588, 0x29c0,
+ 0x210c, 0xa18c, 0x00ff, 0x216a, 0xa00e, 0x2011, 0x0002, 0x2100,
+ 0xa506, 0x0040, 0x31c3, 0x1078, 0x45c4, 0x00c0, 0x31c3, 0x6014,
+ 0x821c, 0x0048, 0x31bb, 0xa398, 0xacc0, 0xa085, 0xff00, 0x8007,
+ 0x201a, 0x0078, 0x31c2, 0xa398, 0xacc0, 0x2324, 0xa4a4, 0xff00,
+ 0xa405, 0x201a, 0x8210, 0x8108, 0xa182, 0x0080, 0x00c8, 0x31ca,
+ 0x0078, 0x31a7, 0x8201, 0x8007, 0x2d0c, 0xa105, 0x206a, 0x0d7f,
+ 0x20a9, 0x0040, 0x20a1, 0xacc0, 0x2099, 0xacc0, 0x1078, 0x4281,
+ 0x0078, 0x3180, 0x1078, 0x35e4, 0x0040, 0x2bdb, 0x0c7e, 0x1078,
+ 0x35ba, 0x0c7f, 0x00c0, 0x31e8, 0x2009, 0x0002, 0x0078, 0x2bd7,
+ 0x2001, 0xa653, 0x2004, 0xd0b4, 0x0040, 0x320f, 0x6000, 0xd08c,
+ 0x00c0, 0x320f, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0,
+ 0x320f, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x1078, 0x8e9e,
+ 0x00c0, 0x3206, 0x2009, 0x0003, 0x0078, 0x2bd7, 0x7007, 0x0003,
+ 0x701b, 0x320b, 0x007c, 0x1078, 0x35e4, 0x0040, 0x2bdb, 0x20a9,
+ 0x002b, 0x2c98, 0xade8, 0x0002, 0x2da0, 0x53a3, 0x20a9, 0x0004,
+ 0xac80, 0x0006, 0x2098, 0xad80, 0x0006, 0x20a0, 0x1078, 0x4281,
+ 0x20a9, 0x0004, 0xac80, 0x000a, 0x2098, 0xad80, 0x000a, 0x20a0,
+ 0x1078, 0x4281, 0x2d00, 0x2009, 0x002b, 0x7a2c, 0x7b28, 0x7c3c,
+ 0x7d38, 0x0078, 0x3608, 0x81ff, 0x00c0, 0x2bd7, 0x1078, 0x35d2,
+ 0x0040, 0x2bdb, 0x1078, 0x47bd, 0x0078, 0x2bad, 0x81ff, 0x00c0,
+ 0x2bd7, 0x7828, 0xa08a, 0x1000, 0x00c8, 0x2bdb, 0x1078, 0x35e4,
+ 0x0040, 0x2bdb, 0x1078, 0x482f, 0x0040, 0x2bd7, 0x2019, 0x0004,
+ 0x1078, 0x47d3, 0x7924, 0x810f, 0x7a28, 0x1078, 0x3259, 0x0078,
+ 0x2bad, 0xa186, 0x00ff, 0x0040, 0x3261, 0x1078, 0x3271, 0x0078,
+ 0x3270, 0x2029, 0x007e, 0x2061, 0xa600, 0x6450, 0x2400, 0xa506,
+ 0x0040, 0x326d, 0x2508, 0x1078, 0x3271, 0x8529, 0x00c8, 0x3266,
+ 0x007c, 0x1078, 0x45c4, 0x00c0, 0x327c, 0x2200, 0x8003, 0x800b,
+ 0x810b, 0xa108, 0x1078, 0x5a52, 0x007c, 0x81ff, 0x00c0, 0x2bd7,
+ 0x1078, 0x35d2, 0x0040, 0x2bdb, 0x1078, 0x4673, 0x0040, 0x2bd7,
+ 0x1078, 0x47c8, 0x0078, 0x2bad, 0x81ff, 0x00c0, 0x2bd7, 0x1078,
+ 0x35d2, 0x0040, 0x2bdb, 0x1078, 0x4673, 0x0040, 0x2bd7, 0x1078,
+ 0x47b2, 0x0078, 0x2bad, 0x6100, 0x0078, 0x2bad, 0x1078, 0x35e4,
+ 0x0040, 0x2bdb, 0x2001, 0xa600, 0x2004, 0xa086, 0x0003, 0x00c0,
+ 0x2bd7, 0x0d7e, 0xace8, 0x000a, 0x7924, 0xd184, 0x0040, 0x32b2,
+ 0xace8, 0x0006, 0x680c, 0x8007, 0x783e, 0x6808, 0x8007, 0x783a,
+ 0x6b04, 0x831f, 0x6a00, 0x8217, 0x0d7f, 0x6100, 0xa18c, 0x0200,
+ 0x0078, 0x2bad, 0xa006, 0x1078, 0x256a, 0x7824, 0xa084, 0x00ff,
+ 0xa086, 0x00ff, 0x0040, 0x32cf, 0x81ff, 0x00c0, 0x2bd7, 0x1078,
+ 0x42dd, 0x7828, 0xa08a, 0x1000, 0x00c8, 0x2bdb, 0x7924, 0xa18c,
+ 0xff00, 0x810f, 0xa186, 0x00ff, 0x0040, 0x32e5, 0xa182, 0x007f,
+ 0x00c8, 0x2bdb, 0x2100, 0x1078, 0x2564, 0x027e, 0x0c7e, 0x127e,
+ 0x2091, 0x8000, 0x2061, 0xa8c4, 0x601b, 0x0000, 0x601f, 0x0000,
+ 0x2011, 0x0003, 0x1078, 0x70e0, 0x2011, 0x0002, 0x1078, 0x70ea,
+ 0x1078, 0x6fc4, 0x037e, 0x2019, 0x0000, 0x1078, 0x7058, 0x037f,
+ 0x2061, 0x0100, 0x6030, 0xa084, 0x00ff, 0x810f, 0xa105, 0x604a,
+ 0x6043, 0x0090, 0x6043, 0x0010, 0x2009, 0x002d, 0x2011, 0x4259,
+ 0x1078, 0x5add, 0x7924, 0xa18c, 0xff00, 0x810f, 0x7a28, 0x1078,
+ 0x3259, 0x127f, 0x0c7f, 0x027f, 0x0078, 0x2bad, 0x7924, 0xa18c,
+ 0xff00, 0x810f, 0x0c7e, 0x1078, 0x455c, 0x2c08, 0x0c7f, 0x00c0,
+ 0x2bdb, 0x0078, 0x2bad, 0x81ff, 0x0040, 0x3332, 0x2009, 0x0001,
+ 0x0078, 0x2bd7, 0x60cc, 0xd09c, 0x00c0, 0x333a, 0x2009, 0x0005,
+ 0x0078, 0x2bd7, 0x1078, 0x35ba, 0x00c0, 0x3342, 0x2009, 0x0002,
+ 0x0078, 0x2bd7, 0x7924, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078,
+ 0x3604, 0x701b, 0x334c, 0x007c, 0x2009, 0x0080, 0x1078, 0x45c4,
+ 0x00c0, 0x3359, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x0040,
+ 0x335d, 0x2021, 0x400a, 0x0078, 0x2baf, 0x0d7e, 0xade8, 0x000d,
+ 0x6900, 0x6a08, 0x6b0c, 0x6c10, 0x6d14, 0x6e18, 0x6820, 0xa0be,
+ 0x0100, 0x0040, 0x33d0, 0xa0be, 0x0112, 0x0040, 0x33d0, 0xa0be,
+ 0x0113, 0x0040, 0x33d0, 0xa0be, 0x0114, 0x0040, 0x33d0, 0xa0be,
+ 0x0117, 0x0040, 0x33d0, 0xa0be, 0x011a, 0x0040, 0x33d0, 0xa0be,
+ 0x0121, 0x0040, 0x33c6, 0xa0be, 0x0131, 0x0040, 0x33c6, 0xa0be,
+ 0x0171, 0x0040, 0x33d0, 0xa0be, 0x0173, 0x0040, 0x33d0, 0xa0be,
+ 0x01a1, 0x00c0, 0x3398, 0x6830, 0x8007, 0x6832, 0x0078, 0x33d6,
+ 0xa0be, 0x0212, 0x0040, 0x33cc, 0xa0be, 0x0213, 0x0040, 0x33cc,
+ 0xa0be, 0x0214, 0x0040, 0x33be, 0xa0be, 0x0217, 0x0040, 0x33b8,
+ 0xa0be, 0x021a, 0x00c0, 0x33b1, 0x6838, 0x8007, 0x683a, 0x0078,
+ 0x33d0, 0xa0be, 0x0300, 0x0040, 0x33d0, 0x0d7f, 0x0078, 0x2bdb,
+ 0xad80, 0x0010, 0x20a9, 0x0007, 0x1078, 0x3418, 0xad80, 0x000e,
+ 0x20a9, 0x0001, 0x1078, 0x3418, 0x0078, 0x33d0, 0xad80, 0x000c,
+ 0x1078, 0x3426, 0x0078, 0x33d6, 0xad80, 0x000e, 0x1078, 0x3426,
+ 0xad80, 0x000c, 0x20a9, 0x0001, 0x1078, 0x3418, 0x0c7e, 0x1078,
+ 0x35ba, 0x0040, 0x3409, 0x6838, 0xc0fd, 0x683a, 0x6837, 0x0119,
+ 0x6853, 0x0000, 0x684f, 0x0020, 0x685b, 0x0001, 0x810b, 0x697e,
+ 0x6883, 0x0000, 0x6a86, 0x6b8a, 0x6c8e, 0x6d92, 0x6996, 0x689b,
+ 0x0000, 0x0c7f, 0x0d7f, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a,
+ 0x6823, 0x0000, 0x6804, 0x2068, 0x1078, 0x8e66, 0x00c0, 0x3404,
+ 0x2009, 0x0003, 0x0078, 0x2bd7, 0x7007, 0x0003, 0x701b, 0x340f,
+ 0x007c, 0x0c7f, 0x0d7f, 0x2009, 0x0002, 0x0078, 0x2bd7, 0x6820,
+ 0xa086, 0x8001, 0x00c0, 0x2bad, 0x2009, 0x0004, 0x0078, 0x2bd7,
+ 0x017e, 0x2008, 0x2044, 0x8000, 0x204c, 0x8000, 0x290a, 0x8108,
+ 0x280a, 0x8108, 0x00f0, 0x341a, 0x017f, 0x007c, 0x017e, 0x0a7e,
+ 0x0b7e, 0x2008, 0x2044, 0x8000, 0x204c, 0x8000, 0x2054, 0x8000,
+ 0x205c, 0x2b0a, 0x8108, 0x2a0a, 0x8108, 0x290a, 0x8108, 0x280a,
+ 0x0b7f, 0x0a7f, 0x017f, 0x007c, 0x81ff, 0x0040, 0x3443, 0x2009,
+ 0x0001, 0x0078, 0x2bd7, 0x60cc, 0xd09c, 0x00c0, 0x344b, 0x2009,
+ 0x0005, 0x0078, 0x2bd7, 0x7924, 0x2140, 0xa18c, 0xff00, 0x810f,
+ 0xa182, 0x0080, 0x0048, 0x2bdb, 0xa182, 0x00ff, 0x00c8, 0x2bdb,
+ 0x7a2c, 0x7b28, 0x606c, 0xa306, 0x00c0, 0x3466, 0x6070, 0xa24e,
+ 0x0040, 0x2bdb, 0xa9cc, 0xff00, 0x0040, 0x2bdb, 0x0c7e, 0x1078,
+ 0x350f, 0x2c68, 0x0c7f, 0x0040, 0x349e, 0xa0c6, 0x4000, 0x00c0,
+ 0x3484, 0x0c7e, 0x007e, 0x2d60, 0x2009, 0x0000, 0x1078, 0x489b,
+ 0x00c0, 0x347b, 0xc185, 0x6000, 0xd0bc, 0x0040, 0x3480, 0xc18d,
+ 0x007f, 0x0c7f, 0x0078, 0x349b, 0xa0c6, 0x4007, 0x00c0, 0x348b,
+ 0x2408, 0x0078, 0x349b, 0xa0c6, 0x4008, 0x00c0, 0x3493, 0x2708,
+ 0x2610, 0x0078, 0x349b, 0xa0c6, 0x4009, 0x00c0, 0x3499, 0x0078,
+ 0x349b, 0x2001, 0x4006, 0x2020, 0x0078, 0x2baf, 0x2d00, 0x7022,
+ 0x017e, 0x0b7e, 0x0c7e, 0x0e7e, 0x2c70, 0x1078, 0x76c7, 0x0040,
+ 0x34e4, 0x2d00, 0x601a, 0x2001, 0xa657, 0x2004, 0xa084, 0x00ff,
+ 0x6842, 0x2e58, 0x0e7f, 0x0e7e, 0x0c7e, 0x1078, 0x35ba, 0x0c7f,
+ 0x2b70, 0x00c0, 0x34c5, 0x1078, 0x772d, 0x0e7f, 0x0c7f, 0x0b7f,
+ 0x017f, 0x2009, 0x0002, 0x0078, 0x2bd7, 0x6837, 0x0000, 0x2d00,
+ 0x6012, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x2880, 0x127f, 0x601f, 0x0001, 0x2001, 0x0000,
+ 0x1078, 0x44ee, 0x2001, 0x0002, 0x1078, 0x4502, 0x2009, 0x0002,
+ 0x1078, 0x775c, 0xa085, 0x0001, 0x0e7f, 0x0c7f, 0x0b7f, 0x017f,
+ 0x00c0, 0x34ee, 0x2009, 0x0003, 0x0078, 0x2bd7, 0x7007, 0x0003,
+ 0x701b, 0x34f3, 0x007c, 0x6830, 0xa086, 0x0100, 0x7020, 0x2060,
+ 0x00c0, 0x3501, 0x2009, 0x0004, 0x6204, 0xa294, 0x00ff, 0x0078,
+ 0x2bd7, 0x2009, 0x0000, 0x1078, 0x489b, 0x00c0, 0x3508, 0xc185,
+ 0x6000, 0xd0bc, 0x0040, 0x350d, 0xc18d, 0x0078, 0x2bad, 0x0e7e,
+ 0x0d7e, 0x2029, 0x0000, 0x2021, 0x0080, 0x20a9, 0x007f, 0x2071,
+ 0xa7b5, 0x2e04, 0xa005, 0x00c0, 0x3524, 0x2100, 0xa406, 0x00c0,
+ 0x3555, 0x2428, 0x0078, 0x3555, 0x2068, 0x6f10, 0x2700, 0xa306,
+ 0x00c0, 0x3546, 0x6e14, 0x2600, 0xa206, 0x00c0, 0x3546, 0x2400,
+ 0xa106, 0x00c0, 0x3542, 0x2d60, 0xd884, 0x0040, 0x356a, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x356a, 0x2001, 0x4000,
+ 0x0078, 0x356b, 0x2001, 0x4007, 0x0078, 0x356b, 0x2400, 0xa106,
+ 0x00c0, 0x3555, 0x6e14, 0x87ff, 0x00c0, 0x3551, 0x86ff, 0x0040,
+ 0x3521, 0x2001, 0x4008, 0x0078, 0x356b, 0x8420, 0x8e70, 0x00f0,
+ 0x3519, 0x85ff, 0x00c0, 0x3564, 0x2001, 0x4009, 0x0078, 0x356b,
+ 0x2001, 0x0001, 0x0078, 0x356b, 0x1078, 0x455c, 0x00c0, 0x3560,
+ 0x6312, 0x6216, 0xa006, 0xa005, 0x0d7f, 0x0e7f, 0x007c, 0x81ff,
+ 0x00c0, 0x2bd7, 0x1078, 0x35ba, 0x0040, 0x2bd7, 0x6837, 0x0000,
+ 0x6838, 0xc0fd, 0x683a, 0x7824, 0xa005, 0x0040, 0x2bdb, 0xa096,
+ 0x00ff, 0x0040, 0x3587, 0xa092, 0x0004, 0x00c8, 0x2bdb, 0x2010,
+ 0x2d18, 0x1078, 0x282f, 0x0040, 0x2bd7, 0x7007, 0x0003, 0x701b,
+ 0x3592, 0x007c, 0x6830, 0xa086, 0x0100, 0x0040, 0x2bd7, 0x0078,
+ 0x2bad, 0x7924, 0xa18c, 0xff00, 0x810f, 0xa182, 0x0080, 0x0048,
+ 0x2bdb, 0xa182, 0x00ff, 0x00c8, 0x2bdb, 0x127e, 0x2091, 0x8000,
+ 0x1078, 0x8d4b, 0x00c0, 0x35b7, 0xa190, 0xa735, 0x2204, 0xa065,
+ 0x0040, 0x35b7, 0x1078, 0x42f8, 0x127f, 0x0078, 0x2bad, 0x127f,
+ 0x0078, 0x2bd7, 0x1078, 0x138b, 0x0040, 0x35d1, 0xa006, 0x6802,
+ 0x7010, 0xa005, 0x00c0, 0x35c9, 0x2d00, 0x7012, 0x7016, 0x0078,
+ 0x35cf, 0x7014, 0x6802, 0x2060, 0x2d00, 0x6006, 0x7016, 0xad80,
+ 0x000d, 0x007c, 0x7924, 0x810f, 0xa18c, 0x00ff, 0x1078, 0x45c4,
+ 0x00c0, 0x35e1, 0x7e28, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0048,
+ 0x35e2, 0xa066, 0x8cff, 0x007c, 0x7e24, 0x860f, 0xa18c, 0x00ff,
+ 0x1078, 0x45c4, 0x00c0, 0x35f2, 0xa6b4, 0x00ff, 0xa682, 0x4000,
+ 0x0048, 0x35f3, 0xa066, 0x8cff, 0x007c, 0x017e, 0x7110, 0x81ff,
+ 0x0040, 0x3600, 0x2168, 0x6904, 0x1078, 0x13a4, 0x0078, 0x35f7,
+ 0x7112, 0x7116, 0x017f, 0x007c, 0x2031, 0x0001, 0x0078, 0x360a,
+ 0x2031, 0x0000, 0x2061, 0xa6d2, 0x6606, 0x6112, 0x600e, 0x6226,
+ 0x632a, 0x642e, 0x6532, 0x2c10, 0x1078, 0x13db, 0x7007, 0x0002,
+ 0x701b, 0x2bad, 0x007c, 0x0f7e, 0x127e, 0x2091, 0x8000, 0x2079,
+ 0x0000, 0x2001, 0xa690, 0x2004, 0xa005, 0x00c0, 0x3636, 0x0068,
+ 0x3636, 0x7818, 0xd084, 0x00c0, 0x3636, 0x7a22, 0x7b26, 0x7c2a,
+ 0x781b, 0x0001, 0x2091, 0x4080, 0x0078, 0x365b, 0x017e, 0x0c7e,
+ 0x0e7e, 0x2071, 0xa682, 0x7138, 0xa182, 0x0008, 0x0048, 0x3644,
+ 0x7030, 0x2060, 0x0078, 0x3655, 0x7030, 0xa0e0, 0x0008, 0xac82,
+ 0xa6d2, 0x0048, 0x364d, 0x2061, 0xa692, 0x2c00, 0x7032, 0x81ff,
+ 0x00c0, 0x3653, 0x7036, 0x8108, 0x713a, 0x2262, 0x6306, 0x640a,
+ 0x0e7f, 0x0c7f, 0x017f, 0x127f, 0x0f7f, 0x007c, 0x0e7e, 0x2071,
+ 0xa682, 0x7038, 0xa005, 0x0040, 0x3697, 0x127e, 0x2091, 0x8000,
+ 0x0068, 0x3696, 0x0f7e, 0x2079, 0x0000, 0x7818, 0xd084, 0x00c0,
+ 0x3695, 0x0c7e, 0x7034, 0x2060, 0x2c04, 0x7822, 0x6004, 0x7826,
+ 0x6008, 0x782a, 0x781b, 0x0001, 0x2091, 0x4080, 0x7038, 0x8001,
+ 0x703a, 0xa005, 0x00c0, 0x368b, 0x7033, 0xa692, 0x7037, 0xa692,
+ 0x0c7f, 0x0078, 0x3695, 0xac80, 0x0008, 0xa0fa, 0xa6d2, 0x0048,
+ 0x3693, 0x2001, 0xa692, 0x7036, 0x0c7f, 0x0f7f, 0x127f, 0x0e7f,
+ 0x007c, 0x027e, 0x2001, 0xa653, 0x2004, 0xd0c4, 0x0040, 0x36a4,
+ 0x2011, 0x8014, 0x1078, 0x361b, 0x027f, 0x007c, 0x81ff, 0x00c0,
+ 0x2bd7, 0x127e, 0x2091, 0x8000, 0x6030, 0xc08d, 0xc085, 0xc0ac,
+ 0x6032, 0x1078, 0x4224, 0x127f, 0x0078, 0x2bad, 0x81ff, 0x00c0,
+ 0x2bd7, 0x6000, 0xa086, 0x0003, 0x00c0, 0x2bd7, 0x2001, 0xa653,
+ 0x2004, 0xd0ac, 0x00c0, 0x2bd7, 0x1078, 0x35e4, 0x0040, 0x2bdb,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x36d3, 0x7828,
+ 0xa005, 0x0040, 0x2bad, 0x0c7e, 0x1078, 0x35ba, 0x0c7f, 0x0040,
+ 0x2bd7, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a,
+ 0x1078, 0x8f12, 0x0040, 0x2bd7, 0x7007, 0x0003, 0x701b, 0x36e9,
+ 0x007c, 0x6830, 0xa086, 0x0100, 0x0040, 0x2bd7, 0x0078, 0x2bad,
+ 0x2001, 0xa600, 0x2004, 0xa086, 0x0003, 0x00c0, 0x2bd7, 0x7f24,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x35ba, 0x0040, 0x2bd7,
+ 0x2009, 0x0000, 0x2031, 0x0000, 0x7023, 0x0000, 0x702f, 0x0000,
+ 0xad80, 0x0005, 0x7026, 0x20a0, 0x1078, 0x45c4, 0x00c0, 0x376d,
+ 0x6004, 0xa0c4, 0x00ff, 0xa8c6, 0x0006, 0x0040, 0x371d, 0xa0c4,
+ 0xff00, 0xa8c6, 0x0600, 0x00c0, 0x376d, 0x2001, 0xa653, 0x2004,
+ 0xd0ac, 0x00c0, 0x372a, 0x1078, 0x489b, 0x00c0, 0x372a, 0xd79c,
+ 0x0040, 0x376d, 0xd794, 0x00c0, 0x3730, 0xd784, 0x0040, 0x373c,
+ 0xac80, 0x0006, 0x2098, 0x3400, 0x20a9, 0x0004, 0x53a3, 0x1078,
+ 0x3426, 0xd794, 0x0040, 0x3745, 0xac80, 0x000a, 0x2098, 0x3400,
+ 0x20a9, 0x0004, 0x53a3, 0x1078, 0x3426, 0x21a2, 0xd794, 0x0040,
+ 0x3765, 0xac80, 0x0000, 0x2098, 0x94a0, 0x20a9, 0x0002, 0x53a3,
+ 0xac80, 0x0003, 0x20a6, 0x94a0, 0xac80, 0x0004, 0x2098, 0x3400,
+ 0x20a9, 0x0002, 0x53a3, 0x1078, 0x3418, 0xac80, 0x0026, 0x2098,
+ 0x20a9, 0x0002, 0x53a3, 0x0078, 0x3766, 0x94a0, 0xd794, 0x0040,
+ 0x376b, 0xa6b0, 0x000b, 0xa6b0, 0x0005, 0x8108, 0xd78c, 0x0040,
+ 0x3777, 0xa186, 0x0100, 0x0040, 0x3788, 0x0078, 0x377b, 0xa186,
+ 0x007e, 0x0040, 0x3788, 0xd794, 0x0040, 0x3782, 0xa686, 0x0020,
+ 0x0078, 0x3784, 0xa686, 0x0028, 0x0040, 0x3791, 0x0078, 0x370c,
+ 0x86ff, 0x00c0, 0x378f, 0x7120, 0x810b, 0x0078, 0x2bad, 0x702f,
+ 0x0001, 0x711e, 0x7020, 0xa600, 0x7022, 0x772a, 0x2061, 0xa6d2,
+ 0x6007, 0x0000, 0x6612, 0x7024, 0x600e, 0x6226, 0x632a, 0x642e,
+ 0x6532, 0x2c10, 0x1078, 0x13db, 0x7007, 0x0002, 0x701b, 0x37a9,
+ 0x007c, 0x702c, 0xa005, 0x00c0, 0x37bb, 0x711c, 0x7024, 0x20a0,
+ 0x7728, 0x2031, 0x0000, 0x2061, 0xa6d2, 0x6224, 0x6328, 0x642c,
+ 0x6530, 0x0078, 0x370c, 0x7120, 0x810b, 0x0078, 0x2bad, 0x2029,
+ 0x007e, 0x7924, 0x7a28, 0x7b2c, 0x7c38, 0xa184, 0xff00, 0x8007,
+ 0xa0e2, 0x0020, 0x0048, 0x2bdb, 0xa502, 0x0048, 0x2bdb, 0xa184,
+ 0x00ff, 0xa0e2, 0x0020, 0x0048, 0x2bdb, 0xa502, 0x0048, 0x2bdb,
+ 0xa284, 0xff00, 0x8007, 0xa0e2, 0x0020, 0x0048, 0x2bdb, 0xa502,
+ 0x0048, 0x2bdb, 0xa284, 0x00ff, 0xa0e2, 0x0020, 0x0048, 0x2bdb,
+ 0xa502, 0x0048, 0x2bdb, 0xa384, 0xff00, 0x8007, 0xa0e2, 0x0020,
+ 0x0048, 0x2bdb, 0xa502, 0x0048, 0x2bdb, 0xa384, 0x00ff, 0xa0e2,
+ 0x0020, 0x0048, 0x2bdb, 0xa502, 0x0048, 0x2bdb, 0xa484, 0xff00,
+ 0x8007, 0xa0e2, 0x0020, 0x0048, 0x2bdb, 0xa502, 0x0048, 0x2bdb,
+ 0xa484, 0x00ff, 0xa0e2, 0x0020, 0x0048, 0x2bdb, 0xa502, 0x0048,
+ 0x2bdb, 0x2061, 0xa8a5, 0x6102, 0x6206, 0x630a, 0x640e, 0x0078,
+ 0x2bad, 0x007e, 0x2001, 0xa653, 0x2004, 0xd0cc, 0x007f, 0x007c,
+ 0x007e, 0x2001, 0xa672, 0x2004, 0xd0bc, 0x007f, 0x007c, 0x6164,
+ 0x7a24, 0x6300, 0x82ff, 0x00c0, 0x3830, 0x7926, 0x0078, 0x2bad,
+ 0x83ff, 0x00c0, 0x2bdb, 0x2001, 0xfff0, 0xa200, 0x00c8, 0x2bdb,
+ 0x2019, 0xffff, 0x6068, 0xa302, 0xa200, 0x0048, 0x2bdb, 0x7926,
+ 0x6266, 0x0078, 0x2bad, 0x2001, 0xa600, 0x2004, 0xa086, 0x0003,
+ 0x00c0, 0x2bd7, 0x7c28, 0x7d24, 0x7e38, 0x7f2c, 0x1078, 0x35ba,
+ 0x0040, 0x2bd7, 0x2009, 0x0000, 0x2019, 0x0000, 0x7023, 0x0000,
+ 0x702f, 0x0000, 0xad80, 0x0003, 0x7026, 0x20a0, 0xa1e0, 0xa735,
+ 0x2c64, 0x8cff, 0x0040, 0x387d, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x0040, 0x3872, 0x6004, 0xa084, 0xff00, 0xa086, 0x0600,
+ 0x00c0, 0x387d, 0x6014, 0x20a2, 0x94a0, 0x6010, 0x8007, 0xa105,
+ 0x8007, 0x20a2, 0x94a0, 0xa398, 0x0002, 0x8108, 0xa182, 0x00ff,
+ 0x0040, 0x3888, 0xa386, 0x002a, 0x0040, 0x3891, 0x0078, 0x385e,
+ 0x83ff, 0x00c0, 0x388f, 0x7120, 0x810c, 0x0078, 0x2bad, 0x702f,
+ 0x0001, 0x711e, 0x7020, 0xa300, 0x7022, 0x2061, 0xa6d2, 0x6007,
+ 0x0000, 0x6312, 0x7024, 0x600e, 0x6426, 0x652a, 0x662e, 0x6732,
+ 0x2c10, 0x1078, 0x13db, 0x7007, 0x0002, 0x701b, 0x38a8, 0x007c,
+ 0x702c, 0xa005, 0x00c0, 0x38b9, 0x711c, 0x7024, 0x20a0, 0x2019,
+ 0x0000, 0x2061, 0xa6d2, 0x6424, 0x6528, 0x662c, 0x6730, 0x0078,
+ 0x385e, 0x7120, 0x810c, 0x0078, 0x2bad, 0x81ff, 0x00c0, 0x2bd7,
+ 0x60cc, 0xd09c, 0x0040, 0x2bd7, 0x1078, 0x35ba, 0x0040, 0x2bd7,
+ 0x7924, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x3604, 0x701b,
+ 0x38d2, 0x007c, 0x0d7e, 0xade8, 0x000d, 0x6828, 0xa0be, 0x7000,
+ 0x0040, 0x38e5, 0xa0be, 0x7100, 0x0040, 0x38e5, 0xa0be, 0x7200,
+ 0x0040, 0x38e5, 0x0d7f, 0x0078, 0x2bdb, 0x6820, 0x6924, 0x1078,
+ 0x254d, 0x00c0, 0x3910, 0x1078, 0x455c, 0x00c0, 0x3910, 0x7122,
+ 0x6612, 0x6516, 0x6e18, 0x0c7e, 0x1078, 0x35ba, 0x0040, 0x3910,
+ 0x1078, 0x35ba, 0x0040, 0x3910, 0x0c7f, 0x0d7f, 0x6837, 0x0000,
+ 0x6838, 0xc0fd, 0x683a, 0x6823, 0x0000, 0x6804, 0x2068, 0x1078,
+ 0x8e82, 0x0040, 0x2bd7, 0x7007, 0x0003, 0x701b, 0x3913, 0x007c,
+ 0x0d7f, 0x0078, 0x2bd7, 0x7120, 0x1078, 0x298e, 0x6820, 0xa086,
+ 0x8001, 0x0040, 0x2bd7, 0x2d00, 0x701e, 0x6804, 0xa080, 0x0002,
+ 0x007e, 0x20a9, 0x002a, 0x2098, 0x20a0, 0x1078, 0x4281, 0x007f,
+ 0xade8, 0x000d, 0x6a08, 0x6b0c, 0x6c10, 0x6d14, 0x2061, 0xa6d2,
+ 0x6007, 0x0000, 0x6e00, 0x6f28, 0xa7c6, 0x7000, 0x00c0, 0x393a,
+ 0x0078, 0x393e, 0xa7c6, 0x7100, 0x00c0, 0x3946, 0xa6c2, 0x0004,
+ 0x0048, 0x2bdb, 0x2009, 0x0004, 0x0078, 0x3608, 0xa7c6, 0x7200,
+ 0x00c0, 0x2bdb, 0xa6c2, 0x0054, 0x0048, 0x2bdb, 0x600e, 0x6013,
+ 0x002a, 0x6226, 0x632a, 0x642e, 0x6532, 0x2c10, 0x1078, 0x13db,
+ 0x7007, 0x0002, 0x701b, 0x395d, 0x007c, 0x701c, 0x2068, 0x6804,
+ 0xa080, 0x0001, 0x2004, 0xa080, 0x0002, 0x007e, 0x20a9, 0x002a,
+ 0x2098, 0x20a0, 0x1078, 0x4281, 0x007f, 0x2009, 0x002a, 0x2061,
+ 0xa6d2, 0x6224, 0x6328, 0x642c, 0x6530, 0x0078, 0x3608, 0x81ff,
+ 0x00c0, 0x2bd7, 0x792c, 0x2001, 0xa89d, 0x2102, 0x1078, 0x35d2,
+ 0x0040, 0x2bdb, 0x1078, 0x4673, 0x0040, 0x2bd7, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x47de, 0x127f, 0x0078, 0x2bad, 0x7824, 0xd08c,
+ 0x00c0, 0x3995, 0xd084, 0x0040, 0x31da, 0x1078, 0x35e4, 0x0040,
+ 0x2bdb, 0x0c7e, 0x1078, 0x35ba, 0x0c7f, 0x00c0, 0x39a3, 0x2009,
+ 0x0002, 0x0078, 0x2bd7, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006,
+ 0x0040, 0x39b0, 0xa08e, 0x0004, 0x0040, 0x39b0, 0xa08e, 0x0005,
+ 0x00c0, 0x39dd, 0x7824, 0xd08c, 0x0040, 0x39bb, 0x6000, 0xc08c,
+ 0x6002, 0x0078, 0x39c5, 0x2001, 0xa653, 0x2004, 0xd0b4, 0x0040,
+ 0x320f, 0x6000, 0xd08c, 0x00c0, 0x320f, 0x6837, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x1078, 0x8e9e, 0x00c0, 0x39d2, 0x2009, 0x0003,
+ 0x0078, 0x2bd7, 0x7007, 0x0003, 0x701b, 0x39d7, 0x007c, 0x1078,
+ 0x35e4, 0x0040, 0x2bdb, 0x0078, 0x320f, 0x2009, 0xa62f, 0x210c,
+ 0x81ff, 0x0040, 0x39e7, 0x2009, 0x0001, 0x0078, 0x2bd7, 0x2001,
+ 0xa600, 0x2004, 0xa086, 0x0003, 0x0040, 0x39f2, 0x2009, 0x0007,
+ 0x0078, 0x2bd7, 0x2001, 0xa653, 0x2004, 0xd0ac, 0x0040, 0x39fc,
+ 0x2009, 0x0008, 0x0078, 0x2bd7, 0x609c, 0xd0a4, 0x00c0, 0x3a03,
+ 0xd0ac, 0x00c0, 0x320f, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x1078, 0x8f12, 0x00c0, 0x3a12, 0x2009, 0x0003,
+ 0x0078, 0x2bd7, 0x7007, 0x0003, 0x701b, 0x3a17, 0x007c, 0x6830,
+ 0xa086, 0x0100, 0x00c0, 0x3a20, 0x2009, 0x0004, 0x0078, 0x2bd7,
+ 0x1078, 0x35e4, 0x0040, 0x2bdb, 0x0078, 0x39b2, 0x81ff, 0x2009,
+ 0x0001, 0x00c0, 0x2bd7, 0x6000, 0xa086, 0x0003, 0x2009, 0x0007,
+ 0x00c0, 0x2bd7, 0x2001, 0xa653, 0x2004, 0xd0ac, 0x2009, 0x0008,
+ 0x00c0, 0x2bd7, 0x1078, 0x35e4, 0x0040, 0x2bdb, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x2009, 0x0009, 0x00c0, 0x2bd7, 0x0c7e,
+ 0x1078, 0x35ba, 0x0c7f, 0x2009, 0x0002, 0x0040, 0x2bd7, 0x6837,
+ 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x7928, 0xa194,
+ 0xff00, 0xa18c, 0x00ff, 0xa006, 0x82ff, 0x00c0, 0x3a65, 0xc0ed,
+ 0x6952, 0x792c, 0x6956, 0x0078, 0x3a6e, 0xa28e, 0x0100, 0x00c0,
+ 0x2bdb, 0xc0e5, 0x6853, 0x0000, 0x6857, 0x0000, 0x683e, 0x1078,
+ 0x90bd, 0x2009, 0x0003, 0x0040, 0x2bd7, 0x7007, 0x0003, 0x701b,
+ 0x3a7a, 0x007c, 0x6830, 0xa086, 0x0100, 0x2009, 0x0004, 0x0040,
+ 0x2bd7, 0x0078, 0x2bad, 0x81ff, 0x2009, 0x0001, 0x00c0, 0x2bd7,
+ 0x6000, 0xa086, 0x0003, 0x2009, 0x0007, 0x00c0, 0x2bd7, 0x1078,
+ 0x35e4, 0x0040, 0x2bdb, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006,
+ 0x2009, 0x0009, 0x00c0, 0x2bd7, 0x0c7e, 0x1078, 0x35ba, 0x0c7f,
+ 0x2009, 0x0002, 0x0040, 0x2bd7, 0xad80, 0x000f, 0x2009, 0x0008,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x3604, 0x701b, 0x3ab1,
+ 0x007c, 0x0d7e, 0xade8, 0x000f, 0x6800, 0xa086, 0x0500, 0x00c0,
+ 0x3ac4, 0x6804, 0xa005, 0x00c0, 0x3ac4, 0x6808, 0xa084, 0xff00,
+ 0x00c0, 0x3ac4, 0x0078, 0x3ac7, 0x0d7f, 0x00c0, 0x2bdb, 0x0d7f,
+ 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x0c7e,
+ 0x1078, 0x35e4, 0x00c0, 0x3ad7, 0x0c7f, 0x0078, 0x2bdb, 0x1078,
+ 0x9119, 0x2009, 0x0003, 0x0c7f, 0x0040, 0x2bd7, 0x7007, 0x0003,
+ 0x701b, 0x3ae3, 0x007c, 0x6830, 0xa086, 0x0100, 0x2009, 0x0004,
+ 0x0040, 0x2bd7, 0x0078, 0x2bad, 0x127e, 0x0c7e, 0x0e7e, 0x2061,
+ 0x0100, 0x2071, 0xa600, 0x6044, 0xd0a4, 0x00c0, 0x3b15, 0xd084,
+ 0x0040, 0x3afe, 0x1078, 0x3c75, 0x0078, 0x3b11, 0xd08c, 0x0040,
+ 0x3b05, 0x1078, 0x3b8c, 0x0078, 0x3b11, 0xd094, 0x0040, 0x3b0c,
+ 0x1078, 0x3b60, 0x0078, 0x3b11, 0xd09c, 0x0040, 0x3b11, 0x1078,
+ 0x3b1f, 0x0e7f, 0x0c7f, 0x127f, 0x007c, 0x017e, 0x6128, 0xd19c,
+ 0x00c0, 0x3b1c, 0xc19d, 0x612a, 0x017f, 0x0078, 0x3b11, 0x624c,
+ 0xa286, 0xf0f0, 0x00c0, 0x3b30, 0x6048, 0xa086, 0xf0f0, 0x0040,
+ 0x3b30, 0x624a, 0x6043, 0x0090, 0x6043, 0x0010, 0x0078, 0x3b5f,
+ 0xa294, 0xff00, 0xa296, 0xf700, 0x0040, 0x3b45, 0x7134, 0xd1a4,
+ 0x00c0, 0x3b45, 0x6240, 0xa294, 0x0010, 0x0040, 0x3b45, 0x2009,
+ 0x00f7, 0x1078, 0x42a1, 0x0078, 0x3b5f, 0x6043, 0x0040, 0x6043,
+ 0x0000, 0x7077, 0x0000, 0x708f, 0x0001, 0x70b3, 0x0000, 0x70cf,
+ 0x0000, 0x2009, 0xacc0, 0x200b, 0x0000, 0x7087, 0x0000, 0x707b,
+ 0x000f, 0x2009, 0x000f, 0x2011, 0x41d5, 0x1078, 0x5add, 0x007c,
+ 0x157e, 0x7078, 0xa005, 0x00c0, 0x3b8a, 0x2011, 0x41d5, 0x1078,
+ 0x5a45, 0x6040, 0xa094, 0x0010, 0xa285, 0x0020, 0x6042, 0x20a9,
+ 0x00c8, 0x6044, 0xd08c, 0x00c0, 0x3b83, 0x00f0, 0x3b71, 0x6242,
+ 0x708b, 0x0000, 0x6040, 0xa094, 0x0010, 0xa285, 0x0080, 0x6042,
+ 0x6242, 0x0078, 0x3b8a, 0x6242, 0x708b, 0x0000, 0x707f, 0x0000,
+ 0x0078, 0x3b8a, 0x157f, 0x007c, 0x707c, 0xa08a, 0x0003, 0x00c8,
+ 0x3b95, 0x1079, 0x3b98, 0x0078, 0x3b97, 0x1078, 0x1332, 0x007c,
+ 0x3b9b, 0x3bea, 0x3c74, 0x0f7e, 0x707f, 0x0001, 0x20e1, 0xa000,
+ 0x20e1, 0x8700, 0x1078, 0x21f7, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x2079, 0xab00, 0x207b, 0x2200, 0x7807, 0x00ef, 0x780b, 0x0000,
+ 0x780f, 0x00ef, 0x7813, 0x0138, 0x7817, 0x0000, 0x781b, 0x0000,
+ 0x781f, 0x0000, 0x7823, 0xffff, 0x7827, 0xffff, 0x782b, 0x0000,
+ 0x782f, 0x0000, 0x2079, 0xab0c, 0x207b, 0x1101, 0x7807, 0x0000,
+ 0x2099, 0xa605, 0x20a1, 0xab0e, 0x20a9, 0x0004, 0x53a3, 0x2079,
+ 0xab12, 0x207b, 0x0000, 0x7807, 0x0000, 0x2099, 0xab00, 0x20a1,
+ 0x020b, 0x20a9, 0x0014, 0x53a6, 0x60c3, 0x000c, 0x600f, 0x0000,
+ 0x1078, 0x420b, 0x0f7f, 0x7083, 0x0000, 0x6043, 0x0008, 0x6043,
+ 0x0000, 0x007c, 0x0d7e, 0x7080, 0x7083, 0x0000, 0xa025, 0x0040,
+ 0x3c5e, 0x6020, 0xd0b4, 0x00c0, 0x3c5c, 0x718c, 0x81ff, 0x0040,
+ 0x3c4b, 0xa486, 0x000c, 0x00c0, 0x3c56, 0xa480, 0x0018, 0x8004,
+ 0x20a8, 0x2011, 0xab80, 0x2019, 0xab00, 0x220c, 0x2304, 0xa106,
+ 0x00c0, 0x3c22, 0x8210, 0x8318, 0x00f0, 0x3c05, 0x6043, 0x0004,
+ 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043, 0x0006, 0x707f, 0x0002,
+ 0x708b, 0x0002, 0x2009, 0x07d0, 0x2011, 0x41dc, 0x1078, 0x5add,
+ 0x0078, 0x3c5c, 0x2069, 0xab80, 0x6930, 0xa18e, 0x1101, 0x00c0,
+ 0x3c56, 0x6834, 0xa005, 0x00c0, 0x3c56, 0x6900, 0xa18c, 0x00ff,
+ 0x00c0, 0x3c36, 0x6804, 0xa005, 0x0040, 0x3c4b, 0x2011, 0xab8e,
+ 0x2019, 0xa605, 0x20a9, 0x0004, 0x220c, 0x2304, 0xa102, 0x0048,
+ 0x3c49, 0x00c0, 0x3c56, 0x8210, 0x8318, 0x00f0, 0x3c3c, 0x0078,
+ 0x3c56, 0x708f, 0x0000, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099,
+ 0xab80, 0x20a1, 0x020b, 0x20a9, 0x0014, 0x53a6, 0x6043, 0x0008,
+ 0x6043, 0x0000, 0x0078, 0x3c5e, 0x0d7f, 0x007c, 0x6020, 0xd0b4,
+ 0x00c0, 0x3c5c, 0x60c3, 0x000c, 0x2011, 0xa8bb, 0x2013, 0x0000,
+ 0x7083, 0x0000, 0x20e1, 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575,
+ 0x1078, 0x6e06, 0x0078, 0x3c5c, 0x007c, 0x7088, 0xa08a, 0x001d,
+ 0x00c8, 0x3c7e, 0x1079, 0x3c81, 0x0078, 0x3c80, 0x1078, 0x1332,
+ 0x007c, 0x3cab, 0x3cba, 0x3ce9, 0x3d02, 0x3d2e, 0x3d5a, 0x3d86,
+ 0x3dbc, 0x3de8, 0x3e10, 0x3e53, 0x3e7d, 0x3e9f, 0x3eb5, 0x3edb,
+ 0x3eee, 0x3ef7, 0x3f2b, 0x3f57, 0x3f83, 0x3faf, 0x3fe5, 0x4030,
+ 0x405f, 0x4081, 0x40c3, 0x40e9, 0x4102, 0x4103, 0x0c7e, 0x2061,
+ 0xa600, 0x6003, 0x0007, 0x2061, 0x0100, 0x6004, 0xa084, 0xfff9,
+ 0x6006, 0x0c7f, 0x007c, 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043,
+ 0x0002, 0x708b, 0x0001, 0x2009, 0x07d0, 0x2011, 0x41dc, 0x1078,
+ 0x5add, 0x007c, 0x0f7e, 0x7080, 0xa086, 0x0014, 0x00c0, 0x3ce7,
+ 0x6043, 0x0000, 0x6020, 0xd0b4, 0x00c0, 0x3ce7, 0x2079, 0xab80,
+ 0x7a30, 0xa296, 0x1102, 0x00c0, 0x3ce5, 0x7834, 0xa005, 0x00c0,
+ 0x3ce5, 0x7a38, 0xd2fc, 0x0040, 0x3cdb, 0x70b0, 0xa005, 0x00c0,
+ 0x3cdb, 0x70b3, 0x0001, 0x2011, 0x41dc, 0x1078, 0x5a45, 0x708b,
+ 0x0010, 0x1078, 0x3ef7, 0x0078, 0x3ce7, 0x1078, 0x4224, 0x0f7f,
+ 0x007c, 0x708b, 0x0003, 0x6043, 0x0004, 0x2011, 0x41dc, 0x1078,
+ 0x5a45, 0x1078, 0x4289, 0x20a3, 0x1102, 0x20a3, 0x0000, 0x20a9,
+ 0x000a, 0x20a3, 0x0000, 0x00f0, 0x3cf9, 0x60c3, 0x0014, 0x1078,
+ 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005, 0x0040, 0x3d2c, 0x2011,
+ 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0014, 0x00c0, 0x3d2a, 0x2079,
+ 0xab80, 0x7a30, 0xa296, 0x1102, 0x00c0, 0x3d2a, 0x7834, 0xa005,
+ 0x00c0, 0x3d2a, 0x7a38, 0xd2fc, 0x0040, 0x3d24, 0x70b0, 0xa005,
+ 0x00c0, 0x3d24, 0x70b3, 0x0001, 0x708b, 0x0004, 0x1078, 0x3d2e,
+ 0x0078, 0x3d2c, 0x1078, 0x4224, 0x0f7f, 0x007c, 0x708b, 0x0005,
+ 0x1078, 0x4289, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430, 0x2011,
+ 0xab8e, 0x1078, 0x42d4, 0x00c0, 0x3d4c, 0x7074, 0xa005, 0x00c0,
+ 0x3d4c, 0x7150, 0xa186, 0xffff, 0x0040, 0x3d4c, 0x1078, 0x419d,
+ 0x0040, 0x3d4c, 0x1078, 0x42b8, 0x20a9, 0x0008, 0x2298, 0x26a0,
+ 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078,
+ 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005, 0x0040, 0x3d84, 0x2011,
+ 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0014, 0x00c0, 0x3d82, 0x2079,
+ 0xab80, 0x7a30, 0xa296, 0x1103, 0x00c0, 0x3d82, 0x7834, 0xa005,
+ 0x00c0, 0x3d82, 0x7a38, 0xd2fc, 0x0040, 0x3d7c, 0x70b0, 0xa005,
+ 0x00c0, 0x3d7c, 0x70b3, 0x0001, 0x708b, 0x0006, 0x1078, 0x3d86,
+ 0x0078, 0x3d84, 0x1078, 0x4224, 0x0f7f, 0x007c, 0x708b, 0x0007,
+ 0x1078, 0x4289, 0x20a3, 0x1104, 0x20a3, 0x0000, 0x3430, 0x2011,
+ 0xab8e, 0x1078, 0x42d4, 0x00c0, 0x3dae, 0x7074, 0xa005, 0x00c0,
+ 0x3dae, 0x7154, 0xa186, 0xffff, 0x0040, 0x3dae, 0xa180, 0x29c0,
+ 0x200c, 0xa18c, 0xff00, 0x810f, 0x1078, 0x419d, 0x0040, 0x3dae,
+ 0x1078, 0x3820, 0x0040, 0x3dae, 0x1078, 0x256a, 0x20a9, 0x0008,
+ 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0014, 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005, 0x0040,
+ 0x3de6, 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0014, 0x00c0,
+ 0x3de4, 0x2079, 0xab80, 0x7a30, 0xa296, 0x1104, 0x00c0, 0x3de4,
+ 0x7834, 0xa005, 0x00c0, 0x3de4, 0x7a38, 0xd2fc, 0x0040, 0x3dde,
+ 0x70b0, 0xa005, 0x00c0, 0x3dde, 0x70b3, 0x0001, 0x708b, 0x0008,
+ 0x1078, 0x3de8, 0x0078, 0x3de6, 0x1078, 0x4224, 0x0f7f, 0x007c,
+ 0x708b, 0x0009, 0x1078, 0x4289, 0x20a3, 0x1105, 0x20a3, 0x0100,
+ 0x3430, 0x1078, 0x42d4, 0x00c0, 0x3e01, 0x7074, 0xa005, 0x00c0,
+ 0x3e01, 0x1078, 0x4104, 0x00c0, 0x3e0b, 0xa085, 0x0001, 0x1078,
+ 0x256a, 0x20a9, 0x0008, 0x2099, 0xab8e, 0x26a0, 0x53a6, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078, 0x420b, 0x007c,
+ 0x0f7e, 0x7080, 0xa005, 0x0040, 0x3e51, 0x2011, 0x41dc, 0x1078,
+ 0x5a45, 0xa086, 0x0014, 0x00c0, 0x3e4f, 0x2079, 0xab80, 0x7a30,
+ 0xa296, 0x1105, 0x00c0, 0x3e4f, 0x7834, 0x2011, 0x0100, 0xa21e,
+ 0x00c0, 0x3e3a, 0x7a38, 0xd2fc, 0x0040, 0x3e34, 0x70b0, 0xa005,
+ 0x00c0, 0x3e34, 0x70b3, 0x0001, 0x708b, 0x000a, 0x1078, 0x3e53,
+ 0x0078, 0x3e51, 0xa005, 0x00c0, 0x3e4f, 0x7a38, 0xd2fc, 0x0040,
+ 0x3e47, 0x70b0, 0xa005, 0x00c0, 0x3e47, 0x70b3, 0x0001, 0x7087,
+ 0x0000, 0x708b, 0x000e, 0x1078, 0x3edb, 0x0078, 0x3e51, 0x1078,
+ 0x4224, 0x0f7f, 0x007c, 0x708b, 0x000b, 0x2011, 0xab0e, 0x22a0,
+ 0x20a9, 0x0040, 0x2019, 0xffff, 0x43a4, 0x20a9, 0x0002, 0x2009,
+ 0x0000, 0x41a4, 0x1078, 0x4289, 0x20a3, 0x1106, 0x20a3, 0x0000,
+ 0x1078, 0x42d4, 0x0040, 0x3e70, 0x2013, 0x0000, 0x0078, 0x3e74,
+ 0x6030, 0xa085, 0x0100, 0x2012, 0x2298, 0x20a9, 0x0042, 0x53a6,
+ 0x60c3, 0x0084, 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005,
+ 0x0040, 0x3e9d, 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0084,
+ 0x00c0, 0x3e9b, 0x2079, 0xab80, 0x7a30, 0xa296, 0x1106, 0x00c0,
+ 0x3e9b, 0x7834, 0xa005, 0x00c0, 0x3e9b, 0x708b, 0x000c, 0x1078,
+ 0x3e9f, 0x0078, 0x3e9d, 0x1078, 0x4224, 0x0f7f, 0x007c, 0x708b,
+ 0x000d, 0x1078, 0x4289, 0x20a3, 0x1107, 0x20a3, 0x0000, 0x2099,
+ 0xab8e, 0x20a9, 0x0040, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0084, 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005,
+ 0x0040, 0x3ed9, 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0084,
+ 0x00c0, 0x3ed7, 0x2079, 0xab80, 0x7a30, 0xa296, 0x1107, 0x00c0,
+ 0x3ed7, 0x7834, 0xa005, 0x00c0, 0x3ed7, 0x7087, 0x0001, 0x1078,
+ 0x427b, 0x708b, 0x000e, 0x1078, 0x3edb, 0x0078, 0x3ed9, 0x1078,
+ 0x4224, 0x0f7f, 0x007c, 0x708b, 0x000f, 0x7083, 0x0000, 0x608b,
+ 0xbc85, 0x608f, 0xb5b5, 0x6043, 0x0005, 0x6043, 0x0004, 0x2009,
+ 0x07d0, 0x2011, 0x41dc, 0x1078, 0x5a38, 0x007c, 0x7080, 0xa005,
+ 0x0040, 0x3ef6, 0x2011, 0x41dc, 0x1078, 0x5a45, 0x007c, 0x708b,
+ 0x0011, 0x1078, 0x42d4, 0x00c0, 0x3f14, 0x716c, 0x81ff, 0x0040,
+ 0x3f14, 0x2009, 0x0000, 0x7070, 0xa084, 0x00ff, 0x1078, 0x254d,
+ 0xa186, 0x007e, 0x0040, 0x3f14, 0xa186, 0x0080, 0x0040, 0x3f14,
+ 0x2011, 0xab8e, 0x1078, 0x419d, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x2099, 0xab80, 0x20a1, 0x020b, 0x7480, 0xa480, 0x0018, 0xa080,
+ 0x0007, 0xa084, 0x03f8, 0x8004, 0x20a8, 0x53a6, 0x60c3, 0x0014,
+ 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005, 0x0040, 0x3f55,
+ 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0014, 0x00c0, 0x3f53,
+ 0x2079, 0xab80, 0x7a30, 0xa296, 0x1103, 0x00c0, 0x3f53, 0x7834,
+ 0xa005, 0x00c0, 0x3f53, 0x7a38, 0xd2fc, 0x0040, 0x3f4d, 0x70b0,
+ 0xa005, 0x00c0, 0x3f4d, 0x70b3, 0x0001, 0x708b, 0x0012, 0x1078,
+ 0x3f57, 0x0078, 0x3f55, 0x1078, 0x4224, 0x0f7f, 0x007c, 0x708b,
+ 0x0013, 0x1078, 0x4295, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430,
+ 0x2011, 0xab8e, 0x1078, 0x42d4, 0x00c0, 0x3f75, 0x7074, 0xa005,
+ 0x00c0, 0x3f75, 0x7150, 0xa186, 0xffff, 0x0040, 0x3f75, 0x1078,
+ 0x419d, 0x0040, 0x3f75, 0x1078, 0x42b8, 0x20a9, 0x0008, 0x2298,
+ 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005, 0x0040, 0x3fad,
+ 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0014, 0x00c0, 0x3fab,
+ 0x2079, 0xab80, 0x7a30, 0xa296, 0x1104, 0x00c0, 0x3fab, 0x7834,
+ 0xa005, 0x00c0, 0x3fab, 0x7a38, 0xd2fc, 0x0040, 0x3fa5, 0x70b0,
+ 0xa005, 0x00c0, 0x3fa5, 0x70b3, 0x0001, 0x708b, 0x0014, 0x1078,
+ 0x3faf, 0x0078, 0x3fad, 0x1078, 0x4224, 0x0f7f, 0x007c, 0x708b,
+ 0x0015, 0x1078, 0x4295, 0x20a3, 0x1104, 0x20a3, 0x0000, 0x3430,
+ 0x2011, 0xab8e, 0x1078, 0x42d4, 0x00c0, 0x3fd7, 0x7074, 0xa005,
+ 0x00c0, 0x3fd7, 0x7154, 0xa186, 0xffff, 0x0040, 0x3fd7, 0xa180,
+ 0x29c0, 0x200c, 0xa18c, 0xff00, 0x810f, 0x1078, 0x419d, 0x0040,
+ 0x3fd7, 0x1078, 0x3820, 0x0040, 0x3fd7, 0x1078, 0x256a, 0x20a9,
+ 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0014, 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005,
+ 0x0040, 0x402e, 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0014,
+ 0x00c0, 0x402c, 0x2079, 0xab80, 0x7a30, 0xa296, 0x1105, 0x00c0,
+ 0x402c, 0x7834, 0x2011, 0x0100, 0xa21e, 0x00c0, 0x400b, 0x7a38,
+ 0xd2fc, 0x0040, 0x4009, 0x70b0, 0xa005, 0x00c0, 0x4009, 0x70b3,
+ 0x0001, 0x0078, 0x401a, 0xa005, 0x00c0, 0x402c, 0x7a38, 0xd2fc,
+ 0x0040, 0x4018, 0x70b0, 0xa005, 0x00c0, 0x4018, 0x70b3, 0x0001,
+ 0x7087, 0x0000, 0x7a38, 0xd2f4, 0x0040, 0x4026, 0x2001, 0xa674,
+ 0x2004, 0xd0a4, 0x00c0, 0x4026, 0x70cf, 0x0008, 0x708b, 0x0016,
+ 0x1078, 0x4030, 0x0078, 0x402e, 0x1078, 0x4224, 0x0f7f, 0x007c,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xab80, 0x20a1, 0x020b,
+ 0x20a9, 0x000e, 0x53a6, 0x3430, 0x2011, 0xab8e, 0x708b, 0x0017,
+ 0x1078, 0x42d4, 0x00c0, 0x4050, 0x7074, 0xa005, 0x00c0, 0x4050,
+ 0x1078, 0x4104, 0x00c0, 0x405a, 0xa085, 0x0001, 0x1078, 0x256a,
+ 0x20a9, 0x0008, 0x2099, 0xab8e, 0x26a0, 0x53a6, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078, 0x420b, 0x007c, 0x0f7e,
+ 0x7080, 0xa005, 0x0040, 0x407f, 0x2011, 0x41dc, 0x1078, 0x5a45,
+ 0xa086, 0x0084, 0x00c0, 0x407d, 0x2079, 0xab80, 0x7a30, 0xa296,
+ 0x1106, 0x00c0, 0x407d, 0x7834, 0xa005, 0x00c0, 0x407d, 0x708b,
+ 0x0018, 0x1078, 0x4081, 0x0078, 0x407f, 0x1078, 0x4224, 0x0f7f,
+ 0x007c, 0x708b, 0x0019, 0x1078, 0x4295, 0x20a3, 0x1106, 0x20a3,
+ 0x0000, 0x3430, 0x2099, 0xab8e, 0x2039, 0xab0e, 0x27a0, 0x20a9,
+ 0x0040, 0x53a3, 0x1078, 0x42d4, 0x00c0, 0x40b5, 0x2728, 0x2514,
+ 0x8207, 0xa084, 0x00ff, 0x8000, 0x2018, 0xa294, 0x00ff, 0x8007,
+ 0xa205, 0x202a, 0x6030, 0x2310, 0x8214, 0xa2a0, 0xab0e, 0x2414,
+ 0xa38c, 0x0001, 0x0040, 0x40b0, 0xa294, 0xff00, 0x0078, 0x40b3,
+ 0xa294, 0x00ff, 0x8007, 0xa215, 0x2222, 0x2798, 0x26a0, 0x20a9,
+ 0x0040, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0084,
+ 0x1078, 0x420b, 0x007c, 0x0f7e, 0x7080, 0xa005, 0x0040, 0x40e7,
+ 0x2011, 0x41dc, 0x1078, 0x5a45, 0xa086, 0x0084, 0x00c0, 0x40e5,
+ 0x2079, 0xab80, 0x7a30, 0xa296, 0x1107, 0x00c0, 0x40e5, 0x7834,
+ 0xa005, 0x00c0, 0x40e5, 0x7087, 0x0001, 0x1078, 0x427b, 0x708b,
+ 0x001a, 0x1078, 0x40e9, 0x0078, 0x40e7, 0x1078, 0x4224, 0x0f7f,
+ 0x007c, 0x708b, 0x001b, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099,
+ 0xab80, 0x20a1, 0x020b, 0x7480, 0xa480, 0x0018, 0xa080, 0x0007,
+ 0xa084, 0x03f8, 0x8004, 0x20a8, 0x53a6, 0x60c3, 0x0084, 0x1078,
+ 0x420b, 0x007c, 0x007c, 0x007c, 0x087e, 0x097e, 0x2029, 0xa653,
+ 0x252c, 0x20a9, 0x0008, 0x2041, 0xab0e, 0x28a0, 0x2099, 0xab8e,
+ 0x53a3, 0x20a9, 0x0008, 0x2011, 0x0007, 0xd5d4, 0x0040, 0x411a,
+ 0x2011, 0x0000, 0x2800, 0xa200, 0x200c, 0xa1a6, 0xffff, 0x00c0,
+ 0x412c, 0xd5d4, 0x0040, 0x4127, 0x8210, 0x0078, 0x4128, 0x8211,
+ 0x00f0, 0x411a, 0x0078, 0x4194, 0x82ff, 0x00c0, 0x413e, 0xd5d4,
+ 0x0040, 0x4138, 0xa1a6, 0x3fff, 0x0040, 0x4124, 0x0078, 0x413c,
+ 0xa1a6, 0x3fff, 0x0040, 0x4194, 0xa18d, 0xc000, 0x20a9, 0x0010,
+ 0x2019, 0x0001, 0xd5d4, 0x0040, 0x4147, 0x2019, 0x0010, 0x2120,
+ 0xd5d4, 0x0040, 0x414e, 0x8423, 0x0078, 0x414f, 0x8424, 0x00c8,
+ 0x415c, 0xd5d4, 0x0040, 0x4157, 0x8319, 0x0078, 0x4158, 0x8318,
+ 0x00f0, 0x4148, 0x0078, 0x4194, 0x23a8, 0x2021, 0x0001, 0x8426,
+ 0x8425, 0x00f0, 0x4160, 0x2328, 0x8529, 0xa2be, 0x0007, 0x0040,
+ 0x4174, 0x007e, 0x2039, 0x0007, 0x2200, 0xa73a, 0x007f, 0x27a8,
+ 0xa5a8, 0x0010, 0x00f0, 0x4170, 0x7552, 0xa5c8, 0x29c0, 0x292c,
+ 0xa5ac, 0x00ff, 0x6532, 0x60e7, 0x0000, 0x65ea, 0x706f, 0x0000,
+ 0x7572, 0x2018, 0x2304, 0xa405, 0x201a, 0x7077, 0x0001, 0x26a0,
+ 0x2898, 0x20a9, 0x0008, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0xa085, 0x0001, 0x0078, 0x419a, 0xa006, 0x0078, 0x419a, 0xa006,
+ 0x1078, 0x1332, 0x097f, 0x087f, 0x007c, 0x2118, 0x2021, 0x0000,
+ 0x2001, 0x0007, 0xa39a, 0x0010, 0x0048, 0x41aa, 0x8420, 0x8001,
+ 0x0078, 0x41a2, 0x2118, 0x84ff, 0x0040, 0x41b3, 0xa39a, 0x0010,
+ 0x8421, 0x00c0, 0x41ae, 0x2021, 0x0001, 0x83ff, 0x0040, 0x41bc,
+ 0x8423, 0x8319, 0x00c0, 0x41b8, 0xa238, 0x2704, 0xa42c, 0x00c0,
+ 0x41d4, 0xa405, 0x203a, 0x7152, 0xa1a0, 0x29c0, 0x242c, 0xa5ac,
+ 0x00ff, 0x6532, 0x60e7, 0x0000, 0x65ea, 0x706f, 0x0000, 0x7572,
+ 0x7077, 0x0001, 0xa084, 0x0000, 0x007c, 0x0e7e, 0x2071, 0xa600,
+ 0x707b, 0x0000, 0x0e7f, 0x007c, 0x0e7e, 0x0f7e, 0x2001, 0x0002,
+ 0x1078, 0x5ae6, 0x2079, 0x0100, 0x2071, 0x0140, 0x1078, 0x6e0f,
+ 0x7004, 0xa084, 0x4000, 0x0040, 0x41f1, 0x7003, 0x1000, 0x7003,
+ 0x0000, 0x127e, 0x2091, 0x8000, 0x2071, 0xa622, 0x2073, 0x0000,
+ 0x7840, 0x027e, 0x017e, 0x2009, 0x00f7, 0x1078, 0x42a1, 0x017f,
+ 0xa094, 0x0010, 0xa285, 0x0080, 0x7842, 0x7a42, 0x027f, 0x127f,
+ 0x0f7f, 0x0e7f, 0x007c, 0x127e, 0x2091, 0x8000, 0x2011, 0xa8bb,
+ 0x2013, 0x0000, 0x7083, 0x0000, 0x127f, 0x20e1, 0x9080, 0x60a3,
+ 0x0056, 0x60a7, 0x9575, 0x1078, 0x6e06, 0x2009, 0x07d0, 0x2011,
+ 0x41dc, 0x1078, 0x5add, 0x007c, 0x017e, 0x027e, 0x0c7e, 0x127e,
+ 0x2091, 0x8000, 0x2011, 0x0003, 0x1078, 0x70e0, 0x2011, 0x0002,
+ 0x1078, 0x70ea, 0x1078, 0x6fc4, 0x037e, 0x2019, 0x0000, 0x1078,
+ 0x7058, 0x037f, 0x2009, 0x00f7, 0x1078, 0x42a1, 0x2061, 0xa8c4,
+ 0x601b, 0x0000, 0x601f, 0x0000, 0x2061, 0xa600, 0x6003, 0x0001,
+ 0x2061, 0x0100, 0x6043, 0x0090, 0x6043, 0x0010, 0x2009, 0x002d,
+ 0x2011, 0x4259, 0x1078, 0x5a38, 0x127f, 0x0c7f, 0x027f, 0x017f,
+ 0x007c, 0x0e7e, 0x007e, 0x127e, 0x2091, 0x8000, 0x2001, 0x0001,
+ 0x1078, 0x5ae6, 0x2071, 0x0100, 0x1078, 0x6e0f, 0x2071, 0x0140,
+ 0x7004, 0xa084, 0x4000, 0x0040, 0x4271, 0x7003, 0x1000, 0x7003,
+ 0x0000, 0x2001, 0x0001, 0x1078, 0x24e8, 0x1078, 0x4224, 0x127f,
+ 0x007f, 0x0e7f, 0x007c, 0x20a9, 0x0040, 0x20a1, 0xacc0, 0x2099,
+ 0xab8e, 0x3304, 0x8007, 0x20a2, 0x9398, 0x94a0, 0x00f0, 0x4281,
+ 0x007c, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xab00, 0x20a1,
+ 0x020b, 0x20a9, 0x000c, 0x53a6, 0x007c, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x2099, 0xab80, 0x20a1, 0x020b, 0x20a9, 0x000c, 0x53a6,
+ 0x007c, 0x0c7e, 0x007e, 0x2061, 0x0100, 0x810f, 0x2001, 0xa62f,
+ 0x2004, 0xa005, 0x00c0, 0x42b2, 0x6030, 0xa084, 0x00ff, 0xa105,
+ 0x0078, 0x42b4, 0xa185, 0x00f7, 0x604a, 0x007f, 0x0c7f, 0x007c,
+ 0x017e, 0x047e, 0x2001, 0xa653, 0x2004, 0xd0a4, 0x0040, 0x42cb,
+ 0xa006, 0x2020, 0x2009, 0x002a, 0x1078, 0xa21d, 0x2001, 0xa60c,
+ 0x200c, 0xc195, 0x2102, 0x2019, 0x002a, 0x2009, 0x0000, 0x1078,
+ 0x284f, 0x047f, 0x017f, 0x007c, 0x007e, 0x2001, 0xa60c, 0x2004,
+ 0xd09c, 0x0040, 0x42db, 0x007f, 0x007c, 0x007e, 0x017e, 0x127e,
+ 0x2091, 0x8000, 0x2001, 0x0101, 0x200c, 0xa18d, 0x0006, 0x2102,
+ 0x127f, 0x017f, 0x007f, 0x007c, 0x157e, 0x20a9, 0x00ff, 0x2009,
+ 0xa735, 0xa006, 0x200a, 0x8108, 0x00f0, 0x42f2, 0x157f, 0x007c,
+ 0x0d7e, 0x037e, 0x157e, 0x137e, 0x147e, 0x2069, 0xa652, 0xa006,
+ 0x6002, 0x6007, 0x0707, 0x600a, 0x600e, 0x6012, 0xa198, 0x29c0,
+ 0x231c, 0xa39c, 0x00ff, 0x6316, 0x20a9, 0x0004, 0xac98, 0x0006,
+ 0x23a0, 0x40a4, 0x20a9, 0x0004, 0xac98, 0x000a, 0x23a0, 0x40a4,
+ 0x603e, 0x6042, 0x604e, 0x6052, 0x6056, 0x605a, 0x605e, 0x6062,
+ 0x6066, 0x606a, 0x606e, 0x6072, 0x6076, 0x607a, 0x607e, 0x6082,
+ 0x6086, 0x608a, 0x608e, 0x6092, 0x6096, 0x609a, 0x609e, 0x60ae,
+ 0x61a2, 0x0d7e, 0x60a4, 0xa06d, 0x0040, 0x4338, 0x1078, 0x13a4,
+ 0x60a7, 0x0000, 0x60a8, 0xa06d, 0x0040, 0x4340, 0x1078, 0x13a4,
+ 0x60ab, 0x0000, 0x0d7f, 0xa006, 0x604a, 0x6810, 0x603a, 0x680c,
+ 0x6046, 0x6814, 0xa084, 0x00ff, 0x6042, 0x147f, 0x137f, 0x157f,
+ 0x037f, 0x0d7f, 0x007c, 0x127e, 0x2091, 0x8000, 0x6944, 0x6e48,
+ 0xa684, 0x3fff, 0xa082, 0x4000, 0x00c8, 0x4424, 0xa18c, 0xff00,
+ 0x810f, 0xa182, 0x00ff, 0x00c8, 0x442a, 0x2001, 0xa60c, 0x2004,
+ 0xa084, 0x0003, 0x0040, 0x4385, 0x2001, 0xa60c, 0x2004, 0xd084,
+ 0x00c0, 0x4405, 0xa188, 0xa735, 0x2104, 0xa065, 0x0040, 0x4405,
+ 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006, 0x00c0, 0x4405, 0x6000,
+ 0xd0c4, 0x0040, 0x4405, 0x0078, 0x4392, 0xa188, 0xa735, 0x2104,
+ 0xa065, 0x0040, 0x43e9, 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006,
+ 0x00c0, 0x43ef, 0x60a4, 0xa00d, 0x0040, 0x439a, 0x1078, 0x4817,
+ 0x0040, 0x43e3, 0x60a8, 0xa00d, 0x0040, 0x43b4, 0x1078, 0x486a,
+ 0x00c0, 0x43b4, 0x694c, 0xd1fc, 0x00c0, 0x43aa, 0x1078, 0x44df,
+ 0x0078, 0x43de, 0x1078, 0x4484, 0x694c, 0xd1ec, 0x00c0, 0x43de,
+ 0x1078, 0x46d6, 0x0078, 0x43de, 0x694c, 0xa184, 0xa000, 0x0040,
+ 0x43ce, 0xd1ec, 0x0040, 0x43c7, 0xd1fc, 0x0040, 0x43c3, 0x1078,
+ 0x46e7, 0x0078, 0x43ca, 0x1078, 0x46e7, 0x0078, 0x43ce, 0xd1fc,
+ 0x0040, 0x43ce, 0x1078, 0x4484, 0x0078, 0x43de, 0x6050, 0xa00d,
+ 0x0040, 0x43d9, 0x2d00, 0x200a, 0x6803, 0x0000, 0x6052, 0x0078,
+ 0x43de, 0x2d00, 0x6052, 0x604e, 0x6803, 0x0000, 0x1078, 0x5da9,
+ 0xa006, 0x127f, 0x007c, 0x2001, 0x0005, 0x2009, 0x0000, 0x0078,
+ 0x442e, 0x2001, 0x0028, 0x2009, 0x0000, 0x0078, 0x442e, 0xa082,
+ 0x0006, 0x00c8, 0x4405, 0x60a0, 0xd0bc, 0x00c0, 0x4401, 0x6100,
+ 0xd1fc, 0x0040, 0x4392, 0x2001, 0x0029, 0x2009, 0x1000, 0x0078,
+ 0x442e, 0x2001, 0x0028, 0x0078, 0x4420, 0x2009, 0xa60c, 0x210c,
+ 0xd18c, 0x0040, 0x440f, 0x2001, 0x0004, 0x0078, 0x4420, 0xd184,
+ 0x0040, 0x4416, 0x2001, 0x0004, 0x0078, 0x4420, 0x2001, 0x0029,
+ 0x6100, 0xd1fc, 0x0040, 0x4420, 0x2009, 0x1000, 0x0078, 0x442e,
+ 0x2009, 0x0000, 0x0078, 0x442e, 0x2001, 0x0029, 0x2009, 0x0000,
+ 0x0078, 0x442e, 0x2001, 0x0029, 0x2009, 0x0000, 0xa005, 0x127f,
+ 0x007c, 0x6944, 0x6e48, 0xa684, 0x3fff, 0xa082, 0x4000, 0x00c8,
+ 0x447e, 0xa18c, 0xff00, 0x810f, 0xa182, 0x00ff, 0x00c8, 0x4464,
+ 0xa188, 0xa735, 0x2104, 0xa065, 0x0040, 0x4464, 0x6004, 0xa084,
+ 0x00ff, 0xa08e, 0x0006, 0x00c0, 0x446a, 0x684c, 0xd0ec, 0x0040,
+ 0x4457, 0x1078, 0x46e7, 0x1078, 0x4484, 0x0078, 0x445f, 0x1078,
+ 0x4484, 0x684c, 0xd0fc, 0x0040, 0x445f, 0x1078, 0x46d6, 0x1078,
+ 0x472f, 0xa006, 0x0078, 0x4482, 0x2001, 0x0028, 0x2009, 0x0000,
+ 0x0078, 0x4482, 0xa082, 0x0006, 0x00c8, 0x4478, 0x6100, 0xd1fc,
+ 0x0040, 0x444d, 0x2001, 0x0029, 0x2009, 0x1000, 0x0078, 0x4482,
+ 0x2001, 0x0029, 0x2009, 0x0000, 0x0078, 0x4482, 0x2001, 0x0029,
+ 0x2009, 0x0000, 0xa005, 0x007c, 0x127e, 0x2091, 0x8000, 0x6050,
+ 0xa00d, 0x0040, 0x4492, 0x2d00, 0x200a, 0x6803, 0x0000, 0x6052,
+ 0x127f, 0x007c, 0x2d00, 0x6052, 0x604e, 0x6803, 0x0000, 0x0078,
+ 0x4490, 0x127e, 0x2091, 0x8000, 0x604c, 0xa005, 0x0040, 0x44af,
+ 0x0e7e, 0x2071, 0xa8b1, 0x7004, 0xa086, 0x0002, 0x0040, 0x44b6,
+ 0x0e7f, 0x604c, 0x6802, 0x2d00, 0x604e, 0x127f, 0x007c, 0x2d00,
+ 0x6052, 0x604e, 0x6803, 0x0000, 0x0078, 0x44ad, 0x701c, 0xac06,
+ 0x00c0, 0x44a8, 0x604c, 0x2070, 0x7000, 0x6802, 0x2d00, 0x7002,
+ 0x0e7f, 0x127f, 0x007c, 0x127e, 0x2091, 0x8000, 0x604c, 0xa06d,
+ 0x0040, 0x44d1, 0x6800, 0xa005, 0x00c0, 0x44cf, 0x6052, 0x604e,
+ 0xad05, 0x127f, 0x007c, 0x604c, 0xa06d, 0x0040, 0x44de, 0x6800,
+ 0xa005, 0x00c0, 0x44dc, 0x6052, 0x604e, 0xad05, 0x007c, 0x6803,
+ 0x0000, 0x6084, 0xa00d, 0x0040, 0x44e9, 0x2d00, 0x200a, 0x6086,
+ 0x007c, 0x2d00, 0x6086, 0x6082, 0x0078, 0x44e8, 0x127e, 0x0c7e,
+ 0x027e, 0x2091, 0x8000, 0x6218, 0x2260, 0x6200, 0xa005, 0x0040,
+ 0x44fc, 0xc285, 0x0078, 0x44fd, 0xc284, 0x6202, 0x027f, 0x0c7f,
+ 0x127f, 0x007c, 0x127e, 0x0c7e, 0x2091, 0x8000, 0x6218, 0x2260,
+ 0x6204, 0x007e, 0xa086, 0x0006, 0x00c0, 0x4521, 0x609c, 0xd0ac,
+ 0x0040, 0x4521, 0x2001, 0xa653, 0x2004, 0xd0a4, 0x0040, 0x4521,
+ 0xa284, 0xff00, 0x8007, 0xa086, 0x0007, 0x00c0, 0x4521, 0x2011,
+ 0x0600, 0x007f, 0xa294, 0xff00, 0xa215, 0x6206, 0x007e, 0xa086,
+ 0x0006, 0x00c0, 0x4531, 0x6290, 0x82ff, 0x00c0, 0x4531, 0x1078,
+ 0x1332, 0x007f, 0x0c7f, 0x127f, 0x007c, 0x127e, 0x0c7e, 0x2091,
+ 0x8000, 0x6218, 0x2260, 0x6204, 0x007e, 0xa086, 0x0006, 0x00c0,
+ 0x4553, 0x609c, 0xd0a4, 0x0040, 0x4553, 0x2001, 0xa653, 0x2004,
+ 0xd0ac, 0x00c0, 0x4553, 0xa284, 0x00ff, 0xa086, 0x0007, 0x00c0,
+ 0x4553, 0x2011, 0x0006, 0x007f, 0xa294, 0x00ff, 0x8007, 0xa215,
+ 0x6206, 0x0c7f, 0x127f, 0x007c, 0x027e, 0xa182, 0x00ff, 0x0048,
+ 0x4565, 0xa085, 0x0001, 0x0078, 0x457d, 0xa190, 0xa735, 0x2204,
+ 0xa065, 0x00c0, 0x457c, 0x017e, 0x0d7e, 0x1078, 0x1370, 0x2d60,
+ 0x0d7f, 0x017f, 0x0040, 0x4561, 0x2c00, 0x2012, 0x60a7, 0x0000,
+ 0x60ab, 0x0000, 0x1078, 0x42f8, 0xa006, 0x027f, 0x007c, 0x127e,
+ 0x2091, 0x8000, 0x027e, 0xa182, 0x00ff, 0x0048, 0x458b, 0xa085,
+ 0x0001, 0x0078, 0x45c1, 0x0d7e, 0xa190, 0xa735, 0x2204, 0xa06d,
+ 0x0040, 0x45bf, 0x2013, 0x0000, 0x0d7e, 0x0c7e, 0x2d60, 0x60a4,
+ 0xa06d, 0x0040, 0x459d, 0x1078, 0x13a4, 0x60a8, 0xa06d, 0x0040,
+ 0x45a3, 0x1078, 0x13a4, 0x0c7f, 0x0d7f, 0x0d7e, 0x0c7e, 0x68ac,
+ 0x2060, 0x8cff, 0x0040, 0x45bb, 0x600c, 0x007e, 0x6010, 0x2068,
+ 0x1078, 0x8d06, 0x0040, 0x45b6, 0x1078, 0x13b4, 0x1078, 0x772d,
+ 0x0c7f, 0x0078, 0x45a9, 0x0c7f, 0x0d7f, 0x1078, 0x13a4, 0x0d7f,
+ 0xa006, 0x027f, 0x127f, 0x007c, 0x017e, 0xa182, 0x00ff, 0x0048,
+ 0x45cd, 0xa085, 0x0001, 0x0078, 0x45d4, 0xa188, 0xa735, 0x2104,
+ 0xa065, 0x0040, 0x45c9, 0xa006, 0x017f, 0x007c, 0x0d7e, 0x157e,
+ 0x137e, 0x147e, 0x600b, 0x0000, 0x600f, 0x0000, 0x6000, 0xc08c,
+ 0x6002, 0x2069, 0xab8e, 0x6808, 0x605e, 0x6810, 0x6062, 0x6138,
+ 0xa10a, 0x0048, 0x45ec, 0x603a, 0x6814, 0x6066, 0x2099, 0xab96,
+ 0xac88, 0x000a, 0x21a0, 0x20a9, 0x0004, 0x53a3, 0x2099, 0xab9a,
+ 0xac88, 0x0006, 0x21a0, 0x20a9, 0x0004, 0x53a3, 0x2069, 0xabae,
+ 0x6808, 0x606a, 0x690c, 0x616e, 0x6810, 0x6072, 0x6818, 0x6076,
+ 0x60a0, 0xa086, 0x007e, 0x00c0, 0x4611, 0x2069, 0xab8e, 0x690c,
+ 0x616e, 0xa182, 0x0211, 0x00c8, 0x4619, 0x2009, 0x0008, 0x0078,
+ 0x4643, 0xa182, 0x0259, 0x00c8, 0x4621, 0x2009, 0x0007, 0x0078,
+ 0x4643, 0xa182, 0x02c1, 0x00c8, 0x4629, 0x2009, 0x0006, 0x0078,
+ 0x4643, 0xa182, 0x0349, 0x00c8, 0x4631, 0x2009, 0x0005, 0x0078,
+ 0x4643, 0xa182, 0x0421, 0x00c8, 0x4639, 0x2009, 0x0004, 0x0078,
+ 0x4643, 0xa182, 0x0581, 0x00c8, 0x4641, 0x2009, 0x0003, 0x0078,
+ 0x4643, 0x2009, 0x0002, 0x6192, 0x147f, 0x137f, 0x157f, 0x0d7f,
+ 0x007c, 0x017e, 0x027e, 0x0e7e, 0x2071, 0xab8d, 0x2e04, 0x6896,
+ 0x2071, 0xab8e, 0x7004, 0x689a, 0x701c, 0x689e, 0x6a00, 0x2009,
+ 0xa672, 0x210c, 0xd0bc, 0x0040, 0x4663, 0xd1ec, 0x0040, 0x4663,
+ 0xc2ad, 0x0078, 0x4664, 0xc2ac, 0xd0c4, 0x0040, 0x466d, 0xd1e4,
+ 0x0040, 0x466d, 0xc2bd, 0x0078, 0x466e, 0xc2bc, 0x6a02, 0x0e7f,
+ 0x027f, 0x017f, 0x007c, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x60a4,
+ 0xa06d, 0x0040, 0x4697, 0x6900, 0x81ff, 0x00c0, 0x46ab, 0x6a04,
+ 0xa282, 0x0010, 0x00c8, 0x46b0, 0xad88, 0x0004, 0x20a9, 0x0010,
+ 0x2104, 0xa086, 0xffff, 0x0040, 0x4692, 0x8108, 0x00f0, 0x4688,
+ 0x1078, 0x1332, 0x260a, 0x8210, 0x6a06, 0x0078, 0x46ab, 0x1078,
+ 0x138b, 0x0040, 0x46b0, 0x2d00, 0x60a6, 0x6803, 0x0000, 0xad88,
+ 0x0004, 0x20a9, 0x0010, 0x200b, 0xffff, 0x8108, 0x00f0, 0x46a3,
+ 0x6807, 0x0001, 0x6e12, 0xa085, 0x0001, 0x127f, 0x0d7f, 0x007c,
+ 0xa006, 0x0078, 0x46ad, 0x127e, 0x2091, 0x8000, 0x0d7e, 0x60a4,
+ 0xa00d, 0x0040, 0x46d3, 0x2168, 0x6800, 0xa005, 0x00c0, 0x46cf,
+ 0x1078, 0x4817, 0x00c0, 0x46d3, 0x200b, 0xffff, 0x6804, 0xa08a,
+ 0x0002, 0x0048, 0x46cf, 0x8001, 0x6806, 0x0078, 0x46d3, 0x1078,
+ 0x13a4, 0x60a7, 0x0000, 0x0d7f, 0x127f, 0x007c, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x487f, 0x0078, 0x46df, 0x1078, 0x4484, 0x1078,
+ 0x4775, 0x00c0, 0x46dd, 0x1078, 0x472f, 0x127f, 0x007c, 0x0d7e,
+ 0x127e, 0x2091, 0x8000, 0x60a8, 0xa06d, 0x0040, 0x470b, 0x6950,
+ 0x81ff, 0x00c0, 0x471f, 0x6a54, 0xa282, 0x0010, 0x00c8, 0x472c,
+ 0xad88, 0x0018, 0x20a9, 0x0010, 0x2104, 0xa086, 0xffff, 0x0040,
+ 0x4706, 0x8108, 0x00f0, 0x46fc, 0x1078, 0x1332, 0x260a, 0x8210,
+ 0x6a56, 0x0078, 0x471f, 0x1078, 0x138b, 0x0040, 0x472c, 0x2d00,
+ 0x60aa, 0x6853, 0x0000, 0xad88, 0x0018, 0x20a9, 0x0010, 0x200b,
+ 0xffff, 0x8108, 0x00f0, 0x4717, 0x6857, 0x0001, 0x6e62, 0x0078,
+ 0x4723, 0x1078, 0x44df, 0x1078, 0x4739, 0x00c0, 0x4721, 0xa085,
+ 0x0001, 0x127f, 0x0d7f, 0x007c, 0xa006, 0x0078, 0x4729, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x5da9, 0x127f, 0x007c, 0xa01e, 0x0078,
+ 0x473b, 0x2019, 0x0001, 0xa00e, 0x127e, 0x2091, 0x8000, 0x604c,
+ 0x2068, 0x6000, 0xd0dc, 0x00c0, 0x4759, 0x8dff, 0x0040, 0x4770,
+ 0x83ff, 0x0040, 0x4751, 0x6848, 0xa606, 0x0040, 0x475e, 0x0078,
+ 0x4759, 0x683c, 0xa406, 0x00c0, 0x4759, 0x6840, 0xa506, 0x0040,
+ 0x475e, 0x2d08, 0x6800, 0x2068, 0x0078, 0x4745, 0x1078, 0x7233,
+ 0x6a00, 0x604c, 0xad06, 0x00c0, 0x4768, 0x624e, 0x0078, 0x476b,
+ 0xa180, 0x0000, 0x2202, 0x82ff, 0x00c0, 0x4770, 0x6152, 0x8dff,
+ 0x127f, 0x007c, 0xa01e, 0x0078, 0x4777, 0x2019, 0x0001, 0xa00e,
+ 0x6080, 0x2068, 0x8dff, 0x0040, 0x47a3, 0x83ff, 0x0040, 0x4786,
+ 0x6848, 0xa606, 0x0040, 0x4793, 0x0078, 0x478e, 0x683c, 0xa406,
+ 0x00c0, 0x478e, 0x6840, 0xa506, 0x0040, 0x4793, 0x2d08, 0x6800,
+ 0x2068, 0x0078, 0x477a, 0x6a00, 0x6080, 0xad06, 0x00c0, 0x479b,
+ 0x6282, 0x0078, 0x479e, 0xa180, 0x0000, 0x2202, 0x82ff, 0x00c0,
+ 0x47a3, 0x6186, 0x8dff, 0x007c, 0xa016, 0x1078, 0x4810, 0x00c0,
+ 0x47ab, 0x2011, 0x0001, 0x1078, 0x4863, 0x00c0, 0x47b1, 0xa295,
+ 0x0002, 0x007c, 0x1078, 0x489b, 0x0040, 0x47ba, 0x1078, 0x8dca,
+ 0x0078, 0x47bc, 0xa085, 0x0001, 0x007c, 0x1078, 0x489b, 0x0040,
+ 0x47c5, 0x1078, 0x8d62, 0x0078, 0x47c7, 0xa085, 0x0001, 0x007c,
+ 0x1078, 0x489b, 0x0040, 0x47d0, 0x1078, 0x8dac, 0x0078, 0x47d2,
+ 0xa085, 0x0001, 0x007c, 0x1078, 0x489b, 0x0040, 0x47db, 0x1078,
+ 0x8d7e, 0x0078, 0x47dd, 0xa085, 0x0001, 0x007c, 0x1078, 0x489b,
+ 0x0040, 0x47e6, 0x1078, 0x8de8, 0x0078, 0x47e8, 0xa085, 0x0001,
+ 0x007c, 0x127e, 0x007e, 0x0d7e, 0x2091, 0x8000, 0x6080, 0xa06d,
+ 0x0040, 0x4808, 0x6800, 0x007e, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x1078, 0x8f7d, 0x007e, 0x6000, 0xd0fc, 0x0040, 0x4802,
+ 0x1078, 0xa4ed, 0x007f, 0x1078, 0x4a73, 0x007f, 0x0078, 0x47ef,
+ 0x6083, 0x0000, 0x6087, 0x0000, 0x0d7f, 0x007f, 0x127f, 0x007c,
+ 0x60a4, 0xa00d, 0x00c0, 0x4817, 0xa085, 0x0001, 0x007c, 0x0e7e,
+ 0x2170, 0x7000, 0xa005, 0x00c0, 0x482c, 0x20a9, 0x0010, 0xae88,
+ 0x0004, 0x2104, 0xa606, 0x0040, 0x482c, 0x8108, 0x00f0, 0x4821,
+ 0xa085, 0x0001, 0x0078, 0x482d, 0xa006, 0x0e7f, 0x007c, 0x0d7e,
+ 0x127e, 0x2091, 0x8000, 0x60a4, 0xa06d, 0x00c0, 0x483d, 0x1078,
+ 0x138b, 0x0040, 0x484f, 0x2d00, 0x60a6, 0x6803, 0x0001, 0x6807,
+ 0x0000, 0xad88, 0x0004, 0x20a9, 0x0010, 0x200b, 0xffff, 0x8108,
+ 0x00f0, 0x4845, 0xa085, 0x0001, 0x127f, 0x0d7f, 0x007c, 0xa006,
+ 0x0078, 0x484c, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x60a4, 0xa06d,
+ 0x0040, 0x4860, 0x60a7, 0x0000, 0x1078, 0x13a4, 0xa085, 0x0001,
+ 0x127f, 0x0d7f, 0x007c, 0x60a8, 0xa00d, 0x00c0, 0x486a, 0xa085,
+ 0x0001, 0x007c, 0x0e7e, 0x2170, 0x7050, 0xa005, 0x00c0, 0x487d,
+ 0x20a9, 0x0010, 0xae88, 0x0018, 0x2104, 0xa606, 0x0040, 0x487d,
+ 0x8108, 0x00f0, 0x4874, 0xa085, 0x0001, 0x0e7f, 0x007c, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x4863, 0x00c0, 0x4899, 0x200b, 0xffff,
+ 0x0d7e, 0x60a8, 0x2068, 0x6854, 0xa08a, 0x0002, 0x0048, 0x4894,
+ 0x8001, 0x6856, 0x0078, 0x4898, 0x1078, 0x13a4, 0x60ab, 0x0000,
+ 0x0d7f, 0x127f, 0x007c, 0x609c, 0xd0a4, 0x007c, 0x0f7e, 0x71b0,
+ 0x81ff, 0x00c0, 0x48b9, 0x71cc, 0xd19c, 0x0040, 0x48b9, 0x2001,
+ 0x007e, 0xa080, 0xa735, 0x2004, 0xa07d, 0x0040, 0x48b9, 0x7804,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x48b9, 0x7800, 0xc0ed,
+ 0x7802, 0x2079, 0xa652, 0x7804, 0xd0a4, 0x0040, 0x48df, 0x157e,
+ 0x0c7e, 0x20a9, 0x007f, 0x2009, 0x0000, 0x017e, 0x1078, 0x45c4,
+ 0x00c0, 0x48d9, 0x6004, 0xa084, 0xff00, 0x8007, 0xa096, 0x0004,
+ 0x0040, 0x48d6, 0xa086, 0x0006, 0x00c0, 0x48d9, 0x6000, 0xc0ed,
+ 0x6002, 0x017f, 0x8108, 0x00f0, 0x48c5, 0x0c7f, 0x157f, 0x1078,
+ 0x4967, 0x0040, 0x48e8, 0x2001, 0xa8a1, 0x200c, 0x0078, 0x48f0,
+ 0x2079, 0xa652, 0x7804, 0xd0a4, 0x0040, 0x48f4, 0x2009, 0x07d0,
+ 0x2011, 0x48f6, 0x1078, 0x5add, 0x0f7f, 0x007c, 0x2011, 0x48f6,
+ 0x1078, 0x5a45, 0x1078, 0x4967, 0x0040, 0x491e, 0x2001, 0xa7b3,
+ 0x2004, 0xa080, 0x0000, 0x200c, 0xc1ec, 0x2102, 0x2001, 0xa653,
+ 0x2004, 0xd0a4, 0x0040, 0x4912, 0x2009, 0x07d0, 0x2011, 0x48f6,
+ 0x1078, 0x5add, 0x0e7e, 0x2071, 0xa600, 0x706f, 0x0000, 0x7073,
+ 0x0000, 0x1078, 0x2677, 0x0e7f, 0x0078, 0x4956, 0x157e, 0x0c7e,
+ 0x20a9, 0x007f, 0x2009, 0x0000, 0x017e, 0x1078, 0x45c4, 0x00c0,
+ 0x4950, 0x6000, 0xd0ec, 0x0040, 0x4950, 0x047e, 0x62a0, 0xa294,
+ 0x00ff, 0x8227, 0xa006, 0x2009, 0x0029, 0x1078, 0xa21d, 0x6000,
+ 0xc0e5, 0xc0ec, 0x6002, 0x6004, 0xa084, 0x00ff, 0xa085, 0x0700,
+ 0x6006, 0x2019, 0x0029, 0x1078, 0x5f01, 0x077e, 0x2039, 0x0000,
+ 0x1078, 0x5e0a, 0x2009, 0x0000, 0x1078, 0x9f8b, 0x077f, 0x047f,
+ 0x017f, 0x8108, 0x00f0, 0x4924, 0x0c7f, 0x157f, 0x007c, 0x0c7e,
+ 0x6018, 0x2060, 0x6000, 0xc0ec, 0x6002, 0x0c7f, 0x007c, 0x7818,
+ 0x2004, 0xd0ac, 0x007c, 0x7818, 0x2004, 0xd0bc, 0x007c, 0x0f7e,
+ 0x2001, 0xa7b3, 0x2004, 0xa07d, 0x0040, 0x4970, 0x7800, 0xd0ec,
+ 0x0f7f, 0x007c, 0x127e, 0x027e, 0x2091, 0x8000, 0x007e, 0x62a0,
+ 0xa290, 0xa735, 0x2204, 0xac06, 0x10c0, 0x1332, 0x007f, 0x6200,
+ 0xa005, 0x0040, 0x4986, 0xc2fd, 0x0078, 0x4987, 0xc2fc, 0x6202,
+ 0x027f, 0x127f, 0x007c, 0x2011, 0xa633, 0x2204, 0xd0cc, 0x0040,
+ 0x4998, 0x2001, 0xa89f, 0x200c, 0x2011, 0x4999, 0x1078, 0x5add,
+ 0x007c, 0x2011, 0x4999, 0x1078, 0x5a45, 0x2011, 0xa633, 0x2204,
+ 0xc0cc, 0x2012, 0x007c, 0x2071, 0xa714, 0x7003, 0x0001, 0x7007,
+ 0x0000, 0x7013, 0x0000, 0x7017, 0x0000, 0x701b, 0x0000, 0x701f,
+ 0x0000, 0x700b, 0x0000, 0x704b, 0x0001, 0x704f, 0x0000, 0x705b,
+ 0x0020, 0x705f, 0x0040, 0x707f, 0x0000, 0x2071, 0xa87d, 0x7003,
+ 0xa714, 0x7007, 0x0000, 0x700b, 0x0000, 0x700f, 0xa85d, 0x7013,
+ 0x0020, 0x7017, 0x0040, 0x7037, 0x0000, 0x007c, 0x017e, 0x0e7e,
+ 0x2071, 0xa835, 0xa00e, 0x7186, 0x718a, 0x7097, 0x0001, 0x2001,
+ 0xa653, 0x2004, 0xd0fc, 0x00c0, 0x49e8, 0x2001, 0xa653, 0x2004,
+ 0xa00e, 0xd09c, 0x0040, 0x49e5, 0x8108, 0x7102, 0x0078, 0x4a3b,
+ 0x2001, 0xa672, 0x200c, 0xa184, 0x000f, 0x2009, 0xa673, 0x210c,
+ 0x0079, 0x49f2, 0x49dd, 0x4a13, 0x4a1b, 0x4a26, 0x4a2c, 0x49dd,
+ 0x49dd, 0x49dd, 0x4a02, 0x49dd, 0x49dd, 0x49dd, 0x49dd, 0x49dd,
+ 0x49dd, 0x49dd, 0x7003, 0x0004, 0x137e, 0x147e, 0x157e, 0x2099,
+ 0xa676, 0x20a1, 0xa886, 0x20a9, 0x0004, 0x53a3, 0x157f, 0x147f,
+ 0x137f, 0x0078, 0x4a3b, 0x708f, 0x0005, 0x7007, 0x0122, 0x2001,
+ 0x0002, 0x0078, 0x4a21, 0x708f, 0x0002, 0x7007, 0x0121, 0x2001,
+ 0x0003, 0x7002, 0x7097, 0x0001, 0x0078, 0x4a38, 0x7007, 0x0122,
+ 0x2001, 0x0002, 0x0078, 0x4a30, 0x7007, 0x0121, 0x2001, 0x0003,
+ 0x7002, 0xa006, 0x7096, 0x708e, 0xa184, 0xff00, 0x8007, 0x709a,
+ 0xa184, 0x00ff, 0x7092, 0x0e7f, 0x017f, 0x007c, 0x0e7e, 0x2071,
+ 0xa714, 0x684c, 0xa005, 0x00c0, 0x4a4c, 0x7028, 0xc085, 0x702a,
+ 0xa085, 0x0001, 0x0078, 0x4a71, 0x6a60, 0x7236, 0x6b64, 0x733a,
+ 0x6868, 0x703e, 0x7076, 0x686c, 0x7042, 0x707a, 0x684c, 0x702e,
+ 0x6844, 0x7032, 0x2009, 0x000d, 0x200a, 0x700b, 0x0000, 0x8007,
+ 0x8006, 0x8006, 0xa08c, 0x003f, 0xa084, 0xffc0, 0xa210, 0x2100,
+ 0xa319, 0x726e, 0x7372, 0x7028, 0xc084, 0x702a, 0x7007, 0x0001,
+ 0xa006, 0x0e7f, 0x007c, 0x0e7e, 0x027e, 0x6838, 0xd0fc, 0x00c0,
+ 0x4ac9, 0x6804, 0xa00d, 0x0040, 0x4a8f, 0x0d7e, 0x2071, 0xa600,
+ 0xa016, 0x702c, 0x2168, 0x6904, 0x206a, 0x8210, 0x2d00, 0x81ff,
+ 0x00c0, 0x4a82, 0x702e, 0x70ac, 0xa200, 0x70ae, 0x0d7f, 0x2071,
+ 0xa714, 0x701c, 0xa005, 0x00c0, 0x4adb, 0x0068, 0x4ad9, 0x2071,
+ 0xa835, 0x7200, 0x82ff, 0x0040, 0x4ad9, 0x6934, 0xa186, 0x0103,
+ 0x00c0, 0x4aec, 0x6948, 0x6844, 0xa105, 0x00c0, 0x4acc, 0x2009,
+ 0x8020, 0x2200, 0x0079, 0x4aac, 0x4ad9, 0x4ab1, 0x4b09, 0x4b17,
+ 0x4ad9, 0x2071, 0x0000, 0x7018, 0xd084, 0x00c0, 0x4ad9, 0x7122,
+ 0x683c, 0x7026, 0x6840, 0x702a, 0x701b, 0x0001, 0x2091, 0x4080,
+ 0x2071, 0xa600, 0x702c, 0x206a, 0x2d00, 0x702e, 0x70ac, 0x8000,
+ 0x70ae, 0x027f, 0x0e7f, 0x007c, 0x6844, 0xa086, 0x0100, 0x00c0,
+ 0x4ad9, 0x6868, 0xa005, 0x00c0, 0x4ad9, 0x2009, 0x8020, 0x0078,
+ 0x4aa9, 0x2071, 0xa714, 0x2d08, 0x206b, 0x0000, 0x7010, 0x8000,
+ 0x7012, 0x7018, 0xa06d, 0x711a, 0x0040, 0x4ae9, 0x6902, 0x0078,
+ 0x4aea, 0x711e, 0x0078, 0x4ac9, 0xa18c, 0x00ff, 0xa186, 0x0017,
+ 0x0040, 0x4afa, 0xa186, 0x001e, 0x0040, 0x4afa, 0xa18e, 0x001f,
+ 0x00c0, 0x4ad9, 0x684c, 0xd0cc, 0x0040, 0x4ad9, 0x6850, 0xa084,
+ 0x00ff, 0xa086, 0x0001, 0x00c0, 0x4ad9, 0x2009, 0x8021, 0x0078,
+ 0x4aa9, 0x7084, 0x8008, 0xa092, 0x001e, 0x00c8, 0x4ad9, 0x7186,
+ 0xae90, 0x0003, 0xa210, 0x683c, 0x2012, 0x0078, 0x4b27, 0x7084,
+ 0x8008, 0xa092, 0x000f, 0x00c8, 0x4ad9, 0x7186, 0xae90, 0x0003,
+ 0x8003, 0xa210, 0x683c, 0x2012, 0x8210, 0x6840, 0x2012, 0x7088,
+ 0xa10a, 0x0048, 0x4ac0, 0x718c, 0x7084, 0xa10a, 0x0048, 0x4ac0,
+ 0x2071, 0x0000, 0x7018, 0xd084, 0x00c0, 0x4ac0, 0x2071, 0xa835,
+ 0x7000, 0xa086, 0x0002, 0x00c0, 0x4b47, 0x1078, 0x4dc3, 0x2071,
+ 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0078, 0x4ac0, 0x1078,
+ 0x4dee, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0078,
+ 0x4ac0, 0x007e, 0x684c, 0x007e, 0x6837, 0x0103, 0x20a9, 0x001c,
+ 0xad80, 0x0011, 0x20a0, 0x2001, 0x0000, 0x40a4, 0x007f, 0xa084,
+ 0x00ff, 0x684e, 0x007f, 0x684a, 0x6952, 0x007c, 0x2071, 0xa714,
+ 0x7004, 0x0079, 0x4b6b, 0x4b75, 0x4b86, 0x4d94, 0x4d95, 0x4dbc,
+ 0x4dc2, 0x4b76, 0x4d82, 0x4d23, 0x4da5, 0x007c, 0x127e, 0x2091,
+ 0x8000, 0x0068, 0x4b85, 0x2009, 0x000d, 0x7030, 0x200a, 0x2091,
+ 0x4080, 0x7007, 0x0001, 0x700b, 0x0000, 0x127f, 0x2069, 0xa8c4,
+ 0x6844, 0xa005, 0x0050, 0x4bae, 0x00c0, 0x4bae, 0x127e, 0x2091,
+ 0x8000, 0x2069, 0x0000, 0x6934, 0x2001, 0xa720, 0x2004, 0xa10a,
+ 0x0040, 0x4ba9, 0x0068, 0x4bad, 0x2069, 0x0000, 0x6818, 0xd084,
+ 0x00c0, 0x4bad, 0x2009, 0x8040, 0x6922, 0x681b, 0x0001, 0x2091,
+ 0x4080, 0x2069, 0xa8c4, 0x6847, 0xffff, 0x127f, 0x2069, 0xa600,
+ 0x6848, 0x6964, 0xa102, 0x2069, 0xa835, 0x688a, 0x6984, 0x701c,
+ 0xa06d, 0x0040, 0x4bc0, 0x81ff, 0x0040, 0x4c08, 0x0078, 0x4bd6,
+ 0x81ff, 0x0040, 0x4cda, 0x2071, 0xa835, 0x7184, 0x7088, 0xa10a,
+ 0x00c8, 0x4bd6, 0x7190, 0x2071, 0xa8c4, 0x7040, 0xa005, 0x0040,
+ 0x4bd6, 0x00d0, 0x4cda, 0x7142, 0x0078, 0x4cda, 0x2071, 0xa835,
+ 0x718c, 0x127e, 0x2091, 0x8000, 0x7084, 0xa10a, 0x0048, 0x4cf7,
+ 0x0068, 0x4c8c, 0x2071, 0x0000, 0x7018, 0xd084, 0x00c0, 0x4c8c,
+ 0x2001, 0xffff, 0x2071, 0xa8c4, 0x7042, 0x2071, 0xa835, 0x7000,
+ 0xa086, 0x0002, 0x00c0, 0x4bfe, 0x1078, 0x4dc3, 0x2071, 0x0000,
+ 0x701b, 0x0001, 0x2091, 0x4080, 0x0078, 0x4c8c, 0x1078, 0x4dee,
+ 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0078, 0x4c8c,
+ 0x2071, 0xa835, 0x7000, 0xa005, 0x0040, 0x4cb9, 0x6934, 0xa186,
+ 0x0103, 0x00c0, 0x4c8f, 0x684c, 0xd0bc, 0x00c0, 0x4cb9, 0x6948,
+ 0x6844, 0xa105, 0x00c0, 0x4cac, 0x2009, 0x8020, 0x2071, 0xa835,
+ 0x7000, 0x0079, 0x4c23, 0x4cb9, 0x4c71, 0x4c49, 0x4c5b, 0x4c28,
+ 0x137e, 0x147e, 0x157e, 0x2099, 0xa676, 0x20a1, 0xa886, 0x20a9,
+ 0x0004, 0x53a3, 0x157f, 0x147f, 0x137f, 0x2071, 0xa87d, 0xad80,
+ 0x000f, 0x700e, 0x7013, 0x0002, 0x7007, 0x0002, 0x700b, 0x0000,
+ 0x2e10, 0x1078, 0x13db, 0x2071, 0xa714, 0x7007, 0x0009, 0x0078,
+ 0x4cda, 0x7084, 0x8008, 0xa092, 0x001e, 0x00c8, 0x4cda, 0xae90,
+ 0x0003, 0xa210, 0x683c, 0x2012, 0x7186, 0x2071, 0xa714, 0x1078,
+ 0x4e4c, 0x0078, 0x4cda, 0x7084, 0x8008, 0xa092, 0x000f, 0x00c8,
+ 0x4cda, 0xae90, 0x0003, 0x8003, 0xa210, 0x683c, 0x2012, 0x8210,
+ 0x6840, 0x2012, 0x7186, 0x2071, 0xa714, 0x1078, 0x4e4c, 0x0078,
+ 0x4cda, 0x127e, 0x2091, 0x8000, 0x0068, 0x4c8c, 0x2071, 0x0000,
+ 0x7018, 0xd084, 0x00c0, 0x4c8c, 0x7122, 0x683c, 0x7026, 0x6840,
+ 0x702a, 0x701b, 0x0001, 0x2091, 0x4080, 0x127f, 0x2071, 0xa714,
+ 0x1078, 0x4e4c, 0x0078, 0x4cda, 0x127f, 0x0078, 0x4cda, 0xa18c,
+ 0x00ff, 0xa186, 0x0017, 0x0040, 0x4c9d, 0xa186, 0x001e, 0x0040,
+ 0x4c9d, 0xa18e, 0x001f, 0x00c0, 0x4cb9, 0x684c, 0xd0cc, 0x0040,
+ 0x4cb9, 0x6850, 0xa084, 0x00ff, 0xa086, 0x0001, 0x00c0, 0x4cb9,
+ 0x2009, 0x8021, 0x0078, 0x4c1e, 0x6844, 0xa086, 0x0100, 0x00c0,
+ 0x4cb9, 0x6868, 0xa005, 0x00c0, 0x4cb9, 0x2009, 0x8020, 0x0078,
+ 0x4c1e, 0x2071, 0xa714, 0x1078, 0x4e60, 0x0040, 0x4cda, 0x2071,
+ 0xa714, 0x700f, 0x0001, 0x6934, 0xa184, 0x00ff, 0xa086, 0x0003,
+ 0x00c0, 0x4cd1, 0x810f, 0xa18c, 0x00ff, 0x8101, 0x0040, 0x4cd1,
+ 0x710e, 0x7007, 0x0003, 0x1078, 0x4e80, 0x7050, 0xa086, 0x0100,
+ 0x0040, 0x4d95, 0x127e, 0x2091, 0x8000, 0x2071, 0xa714, 0x7008,
+ 0xa086, 0x0001, 0x00c0, 0x4cf5, 0x0068, 0x4cf5, 0x2009, 0x000d,
+ 0x7030, 0x200a, 0x2091, 0x4080, 0x700b, 0x0000, 0x7004, 0xa086,
+ 0x0006, 0x00c0, 0x4cf5, 0x7007, 0x0001, 0x127f, 0x007c, 0x2071,
+ 0xa714, 0x1078, 0x4e60, 0x0040, 0x4d20, 0x2071, 0xa835, 0x7084,
+ 0x700a, 0x20a9, 0x0020, 0x2099, 0xa836, 0x20a1, 0xa85d, 0x53a3,
+ 0x7087, 0x0000, 0x2071, 0xa714, 0x2069, 0xa87d, 0x706c, 0x6826,
+ 0x7070, 0x682a, 0x7074, 0x682e, 0x7078, 0x6832, 0x2d10, 0x1078,
+ 0x13db, 0x7007, 0x0008, 0x2001, 0xffff, 0x2071, 0xa8c4, 0x7042,
+ 0x127f, 0x0078, 0x4cda, 0x2069, 0xa87d, 0x6808, 0xa08e, 0x0000,
+ 0x0040, 0x4d81, 0xa08e, 0x0200, 0x0040, 0x4d7f, 0xa08e, 0x0100,
+ 0x00c0, 0x4d81, 0x127e, 0x2091, 0x8000, 0x0068, 0x4d7c, 0x2069,
+ 0x0000, 0x6818, 0xd084, 0x00c0, 0x4d7c, 0x702c, 0x7130, 0x8108,
+ 0xa102, 0x0048, 0x4d4a, 0xa00e, 0x7034, 0x706e, 0x7038, 0x7072,
+ 0x0078, 0x4d54, 0x706c, 0xa080, 0x0040, 0x706e, 0x00c8, 0x4d54,
+ 0x7070, 0xa081, 0x0000, 0x7072, 0x7132, 0x6936, 0x700b, 0x0000,
+ 0x2001, 0xa85a, 0x2004, 0xa005, 0x00c0, 0x4d73, 0x6934, 0x2069,
+ 0xa835, 0x689c, 0x699e, 0x2069, 0xa8c4, 0xa102, 0x00c0, 0x4d6c,
+ 0x6844, 0xa005, 0x00d0, 0x4d7a, 0x2001, 0xa85b, 0x200c, 0x810d,
+ 0x6946, 0x0078, 0x4d7a, 0x2009, 0x8040, 0x6922, 0x681b, 0x0001,
+ 0x2091, 0x4080, 0x7007, 0x0001, 0x127f, 0x0078, 0x4d81, 0x7007,
+ 0x0005, 0x007c, 0x701c, 0xa06d, 0x0040, 0x4d93, 0x1078, 0x4e60,
+ 0x0040, 0x4d93, 0x7007, 0x0003, 0x1078, 0x4e80, 0x7050, 0xa086,
+ 0x0100, 0x0040, 0x4d95, 0x007c, 0x007c, 0x7050, 0xa09e, 0x0100,
+ 0x00c0, 0x4d9e, 0x7007, 0x0004, 0x0078, 0x4dbc, 0xa086, 0x0200,
+ 0x00c0, 0x4da4, 0x7007, 0x0005, 0x007c, 0x2001, 0xa87f, 0x2004,
+ 0xa08e, 0x0100, 0x00c0, 0x4db1, 0x7007, 0x0001, 0x1078, 0x4e4c,
+ 0x007c, 0xa08e, 0x0000, 0x0040, 0x4db0, 0xa08e, 0x0200, 0x00c0,
+ 0x4db0, 0x7007, 0x0005, 0x007c, 0x1078, 0x4e16, 0x7006, 0x1078,
+ 0x4e4c, 0x007c, 0x007c, 0x0e7e, 0x157e, 0x2071, 0xa835, 0x7184,
+ 0x81ff, 0x0040, 0x4deb, 0xa006, 0x7086, 0xae80, 0x0003, 0x2071,
+ 0x0000, 0x21a8, 0x2014, 0x7226, 0x8000, 0x0070, 0x4de8, 0x2014,
+ 0x722a, 0x8000, 0x0070, 0x4de8, 0x2014, 0x722e, 0x8000, 0x0070,
+ 0x4de8, 0x2014, 0x723a, 0x8000, 0x0070, 0x4de8, 0x2014, 0x723e,
+ 0xa180, 0x8030, 0x7022, 0x157f, 0x0e7f, 0x007c, 0x0e7e, 0x157e,
+ 0x2071, 0xa835, 0x7184, 0x81ff, 0x0040, 0x4e13, 0xa006, 0x7086,
+ 0xae80, 0x0003, 0x2071, 0x0000, 0x21a8, 0x2014, 0x7226, 0x8000,
+ 0x2014, 0x722a, 0x8000, 0x0070, 0x4e0c, 0x2014, 0x723a, 0x8000,
+ 0x2014, 0x723e, 0x0078, 0x4e10, 0x2001, 0x8020, 0x0078, 0x4e12,
+ 0x2001, 0x8042, 0x7022, 0x157f, 0x0e7f, 0x007c, 0x702c, 0x7130,
+ 0x8108, 0xa102, 0x0048, 0x4e23, 0xa00e, 0x7034, 0x706e, 0x7038,
+ 0x7072, 0x0078, 0x4e2d, 0x706c, 0xa080, 0x0040, 0x706e, 0x00c8,
+ 0x4e2d, 0x7070, 0xa081, 0x0000, 0x7072, 0x7132, 0x700c, 0x8001,
+ 0x700e, 0x00c0, 0x4e43, 0x127e, 0x2091, 0x8000, 0x0068, 0x4e46,
+ 0x2001, 0x000d, 0x2102, 0x2091, 0x4080, 0x2001, 0x0001, 0x700b,
+ 0x0000, 0x127f, 0x007c, 0x2001, 0x0007, 0x007c, 0x2001, 0x0006,
+ 0x700b, 0x0001, 0x127f, 0x007c, 0x701c, 0xa06d, 0x0040, 0x4e5f,
+ 0x127e, 0x2091, 0x8000, 0x7010, 0x8001, 0x7012, 0x2d04, 0x701e,
+ 0xa005, 0x00c0, 0x4e5c, 0x701a, 0x127f, 0x1078, 0x13a4, 0x007c,
+ 0x2019, 0x000d, 0x2304, 0x230c, 0xa10e, 0x0040, 0x4e6f, 0x2304,
+ 0x230c, 0xa10e, 0x0040, 0x4e6f, 0xa006, 0x0078, 0x4e7f, 0x732c,
+ 0x8319, 0x7130, 0xa102, 0x00c0, 0x4e79, 0x2300, 0xa005, 0x0078,
+ 0x4e7f, 0x0048, 0x4e7e, 0xa302, 0x0078, 0x4e7f, 0x8002, 0x007c,
+ 0x2d00, 0x7026, 0xa080, 0x000d, 0x7056, 0x7053, 0x0000, 0x127e,
+ 0x2091, 0x8000, 0x2009, 0xa8d6, 0x2104, 0xc08d, 0x200a, 0x127f,
+ 0x1078, 0x13f9, 0x007c, 0x2071, 0xa6e2, 0x7003, 0x0000, 0x7007,
+ 0x0000, 0x700f, 0x0000, 0x702b, 0x0001, 0x704f, 0x0000, 0x7053,
+ 0x0001, 0x705f, 0x0020, 0x7063, 0x0040, 0x7083, 0x0000, 0x708b,
+ 0x0000, 0x708f, 0x0001, 0x70bf, 0x0000, 0x007c, 0x0e7e, 0x2071,
+ 0xa6e2, 0x6848, 0xa005, 0x00c0, 0x4ebc, 0x7028, 0xc085, 0x702a,
+ 0xa085, 0x0001, 0x0078, 0x4ee1, 0x6a50, 0x7236, 0x6b54, 0x733a,
+ 0x6858, 0x703e, 0x707a, 0x685c, 0x7042, 0x707e, 0x6848, 0x702e,
+ 0x6840, 0x7032, 0x2009, 0x000c, 0x200a, 0x8007, 0x8006, 0x8006,
+ 0xa08c, 0x003f, 0xa084, 0xffc0, 0xa210, 0x2100, 0xa319, 0x7272,
+ 0x7376, 0x7028, 0xc084, 0x702a, 0x7007, 0x0001, 0x700f, 0x0000,
+ 0xa006, 0x0e7f, 0x007c, 0x2b78, 0x2071, 0xa6e2, 0x7004, 0x1079,
+ 0x4f41, 0x700c, 0x0079, 0x4eec, 0x4ef1, 0x4ee6, 0x4ee6, 0x4ee6,
+ 0x4ee6, 0x007c, 0x700c, 0x0079, 0x4ef5, 0x4efa, 0x4f3f, 0x4f3f,
+ 0x4f40, 0x4f40, 0x7830, 0x7930, 0xa106, 0x0040, 0x4f04, 0x7830,
+ 0x7930, 0xa106, 0x00c0, 0x4f2a, 0x7030, 0xa10a, 0x0040, 0x4f2a,
+ 0x00c8, 0x4f0c, 0x712c, 0xa10a, 0xa18a, 0x0002, 0x00c8, 0x4f2b,
+ 0x1078, 0x1370, 0x0040, 0x4f2a, 0x2d00, 0x705a, 0x7063, 0x0040,
+ 0x2001, 0x0003, 0x7057, 0x0000, 0x127e, 0x007e, 0x2091, 0x8000,
+ 0x2009, 0xa8d6, 0x2104, 0xc085, 0x200a, 0x007f, 0x700e, 0x127f,
+ 0x1078, 0x13f9, 0x007c, 0x1078, 0x1370, 0x0040, 0x4f2a, 0x2d00,
+ 0x705a, 0x1078, 0x1370, 0x00c0, 0x4f37, 0x0078, 0x4f16, 0x2d00,
+ 0x7086, 0x7063, 0x0080, 0x2001, 0x0004, 0x0078, 0x4f1a, 0x007c,
+ 0x007c, 0x4f52, 0x4f53, 0x4f8a, 0x4f8b, 0x4f3f, 0x4fc1, 0x4fc6,
+ 0x4ffd, 0x4ffe, 0x5019, 0x501a, 0x501b, 0x501c, 0x501d, 0x501e,
+ 0x509e, 0x50c8, 0x007c, 0x700c, 0x0079, 0x4f56, 0x4f5b, 0x4f5e,
+ 0x4f6e, 0x4f89, 0x4f89, 0x1078, 0x4ef2, 0x007c, 0x127e, 0x8001,
+ 0x700e, 0x7058, 0x007e, 0x1078, 0x5464, 0x0040, 0x4f6b, 0x2091,
+ 0x8000, 0x1078, 0x4ef2, 0x0d7f, 0x0078, 0x4f77, 0x127e, 0x8001,
+ 0x700e, 0x1078, 0x5464, 0x7058, 0x2068, 0x7084, 0x705a, 0x6803,
+ 0x0000, 0x6807, 0x0000, 0x6834, 0xa084, 0x00ff, 0xa08a, 0x0020,
+ 0x00c8, 0x4f86, 0x1079, 0x4fa1, 0x127f, 0x007c, 0x127f, 0x1078,
+ 0x501f, 0x007c, 0x007c, 0x007c, 0x0e7e, 0x2071, 0xa6e2, 0x700c,
+ 0x0079, 0x4f92, 0x4f97, 0x4f97, 0x4f97, 0x4f99, 0x4f9d, 0x0e7f,
+ 0x007c, 0x700f, 0x0001, 0x0078, 0x4f9f, 0x700f, 0x0002, 0x0e7f,
+ 0x007c, 0x501f, 0x501f, 0x503b, 0x501f, 0x5171, 0x501f, 0x501f,
+ 0x501f, 0x501f, 0x501f, 0x503b, 0x51bb, 0x5208, 0x5261, 0x5277,
+ 0x501f, 0x501f, 0x5057, 0x503b, 0x501f, 0x501f, 0x5078, 0x5338,
+ 0x5356, 0x501f, 0x5057, 0x501f, 0x501f, 0x501f, 0x501f, 0x506d,
+ 0x5356, 0x7020, 0x2068, 0x1078, 0x13a4, 0x007c, 0x700c, 0x0079,
+ 0x4fc9, 0x4fce, 0x4fd1, 0x4fe1, 0x4ffc, 0x4ffc, 0x1078, 0x4ef2,
+ 0x007c, 0x127e, 0x8001, 0x700e, 0x7058, 0x007e, 0x1078, 0x5464,
+ 0x0040, 0x4fde, 0x2091, 0x8000, 0x1078, 0x4ef2, 0x0d7f, 0x0078,
+ 0x4fea, 0x127e, 0x8001, 0x700e, 0x1078, 0x5464, 0x7058, 0x2068,
+ 0x7084, 0x705a, 0x6803, 0x0000, 0x6807, 0x0000, 0x6834, 0xa084,
+ 0x00ff, 0xa08a, 0x001a, 0x00c8, 0x4ff9, 0x1079, 0x4fff, 0x127f,
+ 0x007c, 0x127f, 0x1078, 0x501f, 0x007c, 0x007c, 0x007c, 0x501f,
+ 0x503b, 0x515b, 0x501f, 0x503b, 0x501f, 0x503b, 0x503b, 0x501f,
+ 0x503b, 0x515b, 0x503b, 0x503b, 0x503b, 0x503b, 0x503b, 0x501f,
+ 0x503b, 0x515b, 0x501f, 0x501f, 0x503b, 0x501f, 0x501f, 0x501f,
+ 0x503b, 0x007c, 0x007c, 0x007c, 0x007c, 0x007c, 0x007c, 0x7007,
+ 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0d5, 0x683a, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x4a73, 0x127f, 0x007c, 0x7007, 0x0001, 0x6838,
+ 0xa084, 0x00ff, 0xc0e5, 0x683a, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x4a73, 0x127f, 0x007c, 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff,
+ 0xc0ed, 0x683a, 0x127e, 0x2091, 0x8000, 0x1078, 0x4a73, 0x127f,
+ 0x007c, 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0dd, 0x683a,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x4a73, 0x127f, 0x007c, 0x6834,
+ 0x8007, 0xa084, 0x00ff, 0x0040, 0x502d, 0x8001, 0x00c0, 0x5064,
+ 0x7007, 0x0001, 0x0078, 0x513a, 0x7007, 0x0006, 0x7012, 0x2d00,
+ 0x7016, 0x701a, 0x704b, 0x513a, 0x007c, 0x684c, 0xa084, 0x00c0,
+ 0xa086, 0x00c0, 0x00c0, 0x5078, 0x7007, 0x0001, 0x0078, 0x5373,
+ 0x2d00, 0x7016, 0x701a, 0x20a9, 0x0004, 0xa080, 0x0024, 0x2098,
+ 0x20a1, 0xa70d, 0x53a3, 0x6858, 0x7012, 0xa082, 0x0401, 0x00c8,
+ 0x5049, 0x6884, 0xa08a, 0x0002, 0x00c8, 0x5049, 0x82ff, 0x00c0,
+ 0x509a, 0x6888, 0x698c, 0xa105, 0x0040, 0x509a, 0x2001, 0x510a,
+ 0x0078, 0x509d, 0xa280, 0x5100, 0x2004, 0x70c6, 0x7010, 0xa015,
+ 0x0040, 0x50e8, 0x1078, 0x1370, 0x00c0, 0x50a9, 0x7007, 0x000f,
+ 0x007c, 0x2d00, 0x7022, 0x70c4, 0x2060, 0x6000, 0x6836, 0x6004,
+ 0xad00, 0x7096, 0x6008, 0xa20a, 0x00c8, 0x50b8, 0xa00e, 0x2200,
+ 0x7112, 0x620c, 0x8003, 0x800b, 0xa296, 0x0004, 0x0040, 0x50c1,
+ 0xa108, 0x719a, 0x810b, 0x719e, 0xae90, 0x0022, 0x1078, 0x13db,
+ 0x7090, 0xa08e, 0x0100, 0x0040, 0x50dc, 0xa086, 0x0200, 0x0040,
+ 0x50d4, 0x7007, 0x0010, 0x007c, 0x7020, 0x2068, 0x1078, 0x13a4,
+ 0x7014, 0x2068, 0x0078, 0x5049, 0x7020, 0x2068, 0x7018, 0x6802,
+ 0x6807, 0x0000, 0x2d08, 0x2068, 0x6906, 0x711a, 0x0078, 0x509e,
+ 0x7014, 0x2068, 0x7007, 0x0001, 0x6884, 0xa005, 0x00c0, 0x50f7,
+ 0x6888, 0x698c, 0xa105, 0x0040, 0x50f7, 0x1078, 0x510e, 0x6834,
+ 0xa084, 0x00ff, 0xa086, 0x001e, 0x0040, 0x5373, 0x0078, 0x513a,
+ 0x5102, 0x5106, 0x0002, 0x0011, 0x0007, 0x0004, 0x000a, 0x000f,
+ 0x0005, 0x0006, 0x000a, 0x0011, 0x0005, 0x0004, 0x0f7e, 0x0e7e,
+ 0x0c7e, 0x077e, 0x067e, 0x6f88, 0x6e8c, 0x6804, 0x2060, 0xacf0,
+ 0x0021, 0xacf8, 0x0027, 0x2009, 0x0005, 0x700c, 0x7816, 0x7008,
+ 0x7812, 0x7004, 0x7806, 0x7000, 0x7802, 0x7e0e, 0x7f0a, 0x8109,
+ 0x0040, 0x5130, 0xaef2, 0x0004, 0xaffa, 0x0006, 0x0078, 0x511d,
+ 0x6004, 0xa065, 0x00c0, 0x5117, 0x067f, 0x077f, 0x0c7f, 0x0e7f,
+ 0x0f7f, 0x007c, 0x2009, 0xa62f, 0x210c, 0x81ff, 0x00c0, 0x5155,
+ 0x6838, 0xa084, 0x00ff, 0x683a, 0x1078, 0x4353, 0x00c0, 0x5149,
+ 0x007c, 0x1078, 0x4b51, 0x127e, 0x2091, 0x8000, 0x1078, 0x8f7d,
+ 0x1078, 0x4a73, 0x127f, 0x0078, 0x5148, 0x2001, 0x0028, 0x2009,
+ 0x0000, 0x0078, 0x5149, 0x7018, 0x6802, 0x2d08, 0x2068, 0x6906,
+ 0x711a, 0x7010, 0x8001, 0x7012, 0x0040, 0x516a, 0x7007, 0x0006,
+ 0x0078, 0x5170, 0x7014, 0x2068, 0x7007, 0x0001, 0x7048, 0x107a,
+ 0x007c, 0x7007, 0x0001, 0x6944, 0x810f, 0xa18c, 0x00ff, 0x6848,
+ 0xa084, 0x00ff, 0x20a9, 0x0001, 0xa096, 0x0001, 0x0040, 0x519a,
+ 0x2009, 0x0000, 0x20a9, 0x00ff, 0xa096, 0x0002, 0x0040, 0x519a,
+ 0xa005, 0x00c0, 0x51ad, 0x6944, 0x810f, 0xa18c, 0x00ff, 0x1078,
+ 0x45c4, 0x00c0, 0x51ad, 0x067e, 0x6e50, 0x1078, 0x46b3, 0x067f,
+ 0x0078, 0x51ad, 0x047e, 0x2011, 0xa60c, 0x2224, 0xc484, 0xc48c,
+ 0x2412, 0x047f, 0x0c7e, 0x1078, 0x45c4, 0x00c0, 0x51a9, 0x1078,
+ 0x4852, 0x8108, 0x00f0, 0x51a3, 0x0c7f, 0x684c, 0xd084, 0x00c0,
+ 0x51b4, 0x1078, 0x13a4, 0x007c, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x4a73, 0x127f, 0x007c, 0x127e, 0x2091, 0x8000, 0x7007, 0x0001,
+ 0x2001, 0xa653, 0x2004, 0xd0a4, 0x0040, 0x51ff, 0x2061, 0xa933,
+ 0x6100, 0xd184, 0x0040, 0x51df, 0x6858, 0xa084, 0x00ff, 0x00c0,
+ 0x5202, 0x6000, 0xd084, 0x0040, 0x51ff, 0x6004, 0xa005, 0x00c0,
+ 0x5205, 0x6003, 0x0000, 0x600b, 0x0000, 0x0078, 0x51fc, 0x2011,
+ 0x0001, 0x6860, 0xa005, 0x00c0, 0x51e7, 0x2001, 0x001e, 0x8000,
+ 0x6016, 0x6858, 0xa084, 0x00ff, 0x0040, 0x51ff, 0x6006, 0x6858,
+ 0x8007, 0xa084, 0x00ff, 0x0040, 0x51ff, 0x600a, 0x6858, 0x8000,
+ 0x00c0, 0x51fb, 0xc28d, 0x6202, 0x127f, 0x0078, 0x5453, 0x127f,
+ 0x0078, 0x544b, 0x127f, 0x0078, 0x5443, 0x127f, 0x0078, 0x5447,
+ 0x127e, 0x2091, 0x8000, 0x7007, 0x0001, 0x2001, 0xa653, 0x2004,
+ 0xd0a4, 0x0040, 0x525e, 0x2061, 0xa933, 0x6000, 0xd084, 0x0040,
+ 0x525e, 0x6204, 0x6308, 0xd08c, 0x00c0, 0x5250, 0x6c48, 0xa484,
+ 0x0003, 0x0040, 0x5236, 0x6958, 0xa18c, 0x00ff, 0x8001, 0x00c0,
+ 0x522f, 0x2100, 0xa210, 0x0048, 0x525b, 0x0078, 0x5236, 0x8001,
+ 0x00c0, 0x525b, 0x2100, 0xa212, 0x0048, 0x525b, 0xa484, 0x000c,
+ 0x0040, 0x5250, 0x6958, 0x810f, 0xa18c, 0x00ff, 0xa082, 0x0004,
+ 0x00c0, 0x5248, 0x2100, 0xa318, 0x0048, 0x525b, 0x0078, 0x5250,
+ 0xa082, 0x0004, 0x00c0, 0x525b, 0x2100, 0xa31a, 0x0048, 0x525b,
+ 0x6860, 0xa005, 0x0040, 0x5256, 0x8000, 0x6016, 0x6206, 0x630a,
+ 0x127f, 0x0078, 0x5453, 0x127f, 0x0078, 0x544f, 0x127f, 0x0078,
+ 0x544b, 0x127e, 0x2091, 0x8000, 0x7007, 0x0001, 0x2061, 0xa933,
+ 0x6300, 0xd38c, 0x00c0, 0x5271, 0x6308, 0x8318, 0x0048, 0x5274,
+ 0x630a, 0x127f, 0x0078, 0x5461, 0x127f, 0x0078, 0x544f, 0x127e,
+ 0x0c7e, 0x2091, 0x8000, 0x7007, 0x0001, 0x684c, 0xd0ac, 0x0040,
+ 0x528b, 0x0c7e, 0x2061, 0xa933, 0x6000, 0xa084, 0xfcff, 0x6002,
+ 0x0c7f, 0x0078, 0x52ba, 0x6858, 0xa005, 0x0040, 0x52d1, 0x685c,
+ 0xa065, 0x0040, 0x52cd, 0x2001, 0xa62f, 0x2004, 0xa005, 0x0040,
+ 0x529d, 0x1078, 0x8ec6, 0x0078, 0x52ab, 0x6013, 0x0400, 0x6037,
+ 0x0000, 0x694c, 0xd1a4, 0x0040, 0x52a7, 0x6950, 0x6136, 0x2009,
+ 0x0041, 0x1078, 0x775c, 0x6958, 0xa18c, 0xff00, 0xa186, 0x2000,
+ 0x00c0, 0x52ba, 0x027e, 0x2009, 0x0000, 0x2011, 0xfdff, 0x1078,
+ 0x5bf1, 0x027f, 0x684c, 0xd0c4, 0x0040, 0x52c9, 0x2061, 0xa933,
+ 0x6000, 0xd08c, 0x00c0, 0x52c9, 0x6008, 0x8000, 0x0048, 0x52cd,
+ 0x600a, 0x0c7f, 0x127f, 0x0078, 0x5453, 0x0c7f, 0x127f, 0x0078,
+ 0x544b, 0x6954, 0xa186, 0x0045, 0x0040, 0x5306, 0xa186, 0x002a,
+ 0x00c0, 0x52e1, 0x2001, 0xa60c, 0x200c, 0xc194, 0x2102, 0x0078,
+ 0x52ba, 0xa186, 0x0020, 0x0040, 0x52fa, 0xa186, 0x0029, 0x0040,
+ 0x52ed, 0xa186, 0x002d, 0x00c0, 0x52cd, 0x6944, 0xa18c, 0xff00,
+ 0x810f, 0x1078, 0x45c4, 0x00c0, 0x52ba, 0x6000, 0xc0e4, 0x6002,
+ 0x0078, 0x52ba, 0x685c, 0xa065, 0x0040, 0x52cd, 0x6007, 0x0024,
+ 0x2001, 0xa8a3, 0x2004, 0x6016, 0x0078, 0x52ba, 0x685c, 0xa065,
+ 0x0040, 0x52cd, 0x0e7e, 0x6860, 0xa075, 0x2001, 0xa62f, 0x2004,
+ 0xa005, 0x0040, 0x531e, 0x1078, 0x8ec6, 0x8eff, 0x0040, 0x531b,
+ 0x2e60, 0x1078, 0x8ec6, 0x0e7f, 0x0078, 0x52ba, 0x6024, 0xc0dc,
+ 0xc0d5, 0x6026, 0x2e60, 0x6007, 0x003a, 0x6870, 0xa005, 0x0040,
+ 0x532f, 0x6007, 0x003b, 0x6874, 0x602a, 0x6878, 0x6012, 0x6003,
+ 0x0001, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0e7f, 0x0078, 0x52ba,
+ 0x2061, 0xa933, 0x6000, 0xd084, 0x0040, 0x5352, 0xd08c, 0x00c0,
+ 0x5461, 0x2091, 0x8000, 0x6204, 0x8210, 0x0048, 0x534c, 0x6206,
+ 0x2091, 0x8001, 0x0078, 0x5461, 0x2091, 0x8001, 0x6853, 0x0016,
+ 0x0078, 0x545a, 0x6853, 0x0007, 0x0078, 0x545a, 0x6834, 0x8007,
+ 0xa084, 0x00ff, 0x00c0, 0x5360, 0x1078, 0x502d, 0x0078, 0x5372,
+ 0x2030, 0x8001, 0x00c0, 0x536a, 0x7007, 0x0001, 0x1078, 0x5373,
+ 0x0078, 0x5372, 0x7007, 0x0006, 0x7012, 0x2d00, 0x7016, 0x701a,
+ 0x704b, 0x5373, 0x007c, 0x0e7e, 0x127e, 0x2091, 0x8000, 0xa03e,
+ 0x2009, 0xa62f, 0x210c, 0x81ff, 0x00c0, 0x53ff, 0x2009, 0xa60c,
+ 0x210c, 0xd194, 0x00c0, 0x5431, 0x6848, 0x2070, 0xae82, 0xad00,
+ 0x0048, 0x53ef, 0x2001, 0xa616, 0x2004, 0xae02, 0x00c8, 0x53ef,
+ 0x2061, 0xa933, 0x6100, 0xa184, 0x0301, 0xa086, 0x0001, 0x00c0,
+ 0x53d2, 0x711c, 0xa186, 0x0006, 0x00c0, 0x53da, 0x7018, 0xa005,
+ 0x0040, 0x53ff, 0x2004, 0xd0e4, 0x00c0, 0x542b, 0x7024, 0xd0dc,
+ 0x00c0, 0x5435, 0x6853, 0x0000, 0x6803, 0x0000, 0x2d08, 0x7010,
+ 0xa005, 0x00c0, 0x53be, 0x7112, 0x684c, 0xd0f4, 0x00c0, 0x5439,
+ 0x2e60, 0x1078, 0x5b27, 0x127f, 0x0e7f, 0x007c, 0x2068, 0x6800,
+ 0xa005, 0x00c0, 0x53be, 0x6902, 0x2168, 0x684c, 0xd0f4, 0x00c0,
+ 0x5439, 0x127f, 0x0e7f, 0x007c, 0x127f, 0x0e7f, 0x6853, 0x0006,
+ 0x0078, 0x545a, 0xd184, 0x0040, 0x53cc, 0xd1c4, 0x00c0, 0x53f3,
+ 0x0078, 0x53f7, 0x6944, 0xa18c, 0xff00, 0x810f, 0x1078, 0x45c4,
+ 0x00c0, 0x542b, 0x6000, 0xd0e4, 0x00c0, 0x542b, 0x711c, 0xa186,
+ 0x0007, 0x00c0, 0x53ef, 0x6853, 0x0002, 0x0078, 0x542d, 0x6853,
+ 0x0008, 0x0078, 0x542d, 0x6853, 0x000e, 0x0078, 0x542d, 0x6853,
+ 0x0017, 0x0078, 0x542d, 0x6853, 0x0035, 0x0078, 0x542d, 0x2001,
+ 0xa672, 0x2004, 0xd0fc, 0x0040, 0x5427, 0x6848, 0x2070, 0xae82,
+ 0xad00, 0x0048, 0x5427, 0x6058, 0xae02, 0x00c8, 0x5427, 0x711c,
+ 0xa186, 0x0006, 0x00c0, 0x5427, 0x7018, 0xa005, 0x0040, 0x5427,
+ 0x2004, 0xd0bc, 0x0040, 0x5427, 0x2039, 0x0001, 0x7000, 0xa086,
+ 0x0007, 0x00c0, 0x537e, 0x7003, 0x0002, 0x0078, 0x537e, 0x6853,
+ 0x0028, 0x0078, 0x542d, 0x6853, 0x0029, 0x127f, 0x0e7f, 0x0078,
+ 0x545a, 0x6853, 0x002a, 0x0078, 0x542d, 0x6853, 0x0045, 0x0078,
+ 0x542d, 0x2e60, 0x2019, 0x0002, 0x6017, 0x0014, 0x1078, 0x9dc7,
+ 0x127f, 0x0e7f, 0x007c, 0x2009, 0x003e, 0x0078, 0x5455, 0x2009,
+ 0x0004, 0x0078, 0x5455, 0x2009, 0x0006, 0x0078, 0x5455, 0x2009,
+ 0x0016, 0x0078, 0x5455, 0x2009, 0x0001, 0x6854, 0xa084, 0xff00,
+ 0xa105, 0x6856, 0x2091, 0x8000, 0x1078, 0x4a73, 0x2091, 0x8001,
+ 0x007c, 0x1078, 0x13a4, 0x007c, 0x702c, 0x7130, 0x8108, 0xa102,
+ 0x0048, 0x5471, 0xa00e, 0x7034, 0x7072, 0x7038, 0x7076, 0x0078,
+ 0x547d, 0x7070, 0xa080, 0x0040, 0x7072, 0x00c8, 0x547d, 0x7074,
+ 0xa081, 0x0000, 0x7076, 0xa085, 0x0001, 0x7932, 0x7132, 0x007c,
+ 0x0d7e, 0x1078, 0x5b1e, 0x0d7f, 0x007c, 0x0d7e, 0x2011, 0x0004,
+ 0x2204, 0xa085, 0x8002, 0x2012, 0x0d7f, 0x007c, 0x20e1, 0x0002,
+ 0x3d08, 0x20e1, 0x2000, 0x3d00, 0xa084, 0x7000, 0x0040, 0x549c,
+ 0xa086, 0x1000, 0x00c0, 0x54d3, 0x20e1, 0x0000, 0x3d00, 0xa094,
+ 0xff00, 0x8217, 0xa084, 0xf000, 0xa086, 0x3000, 0x00c0, 0x54b7,
+ 0xa184, 0xff00, 0x8007, 0xa086, 0x0008, 0x00c0, 0x54d3, 0x1078,
+ 0x29bb, 0x00c0, 0x54d3, 0x1078, 0x56b2, 0x0078, 0x54ce, 0x20e1,
+ 0x0004, 0x3d60, 0xd1bc, 0x00c0, 0x54be, 0x3e60, 0xac84, 0x000f,
+ 0x00c0, 0x54d3, 0xac82, 0xad00, 0x0048, 0x54d3, 0x6858, 0xac02,
+ 0x00c8, 0x54d3, 0x2009, 0x0047, 0x1078, 0x775c, 0x7a1c, 0xd284,
+ 0x00c0, 0x548e, 0x007c, 0xa016, 0x1078, 0x15fa, 0x0078, 0x54ce,
+ 0x0078, 0x54d3, 0x781c, 0xd08c, 0x0040, 0x5502, 0x157e, 0x137e,
+ 0x147e, 0x20e1, 0x3000, 0x3d20, 0x3e28, 0xa584, 0x0076, 0x00c0,
+ 0x5518, 0xa484, 0x7000, 0xa086, 0x1000, 0x00c0, 0x5507, 0x1078,
+ 0x554e, 0x0040, 0x5518, 0x20e1, 0x3000, 0x7828, 0x7828, 0x1078,
+ 0x556c, 0x147f, 0x137f, 0x157f, 0x2009, 0xa8b9, 0x2104, 0xa005,
+ 0x00c0, 0x5503, 0x007c, 0x1078, 0x62d1, 0x0078, 0x5502, 0xa484,
+ 0x7000, 0x00c0, 0x5518, 0x1078, 0x554e, 0x0040, 0x552c, 0x7000,
+ 0xa084, 0xff00, 0xa086, 0x8100, 0x0040, 0x54f3, 0x0078, 0x552c,
+ 0x1078, 0xa54f, 0xd5a4, 0x0040, 0x5528, 0x047e, 0x1078, 0x1b22,
+ 0x047f, 0x20e1, 0x9010, 0x2001, 0x0138, 0x2202, 0x0078, 0x5530,
+ 0x1078, 0x554e, 0x6883, 0x0000, 0x20e1, 0x3000, 0x7828, 0x7828,
+ 0x1078, 0x5537, 0x147f, 0x137f, 0x157f, 0x0078, 0x5502, 0x2001,
+ 0xa60e, 0x2004, 0xd08c, 0x0040, 0x554d, 0x2001, 0xa600, 0x2004,
+ 0xa086, 0x0003, 0x00c0, 0x554d, 0x027e, 0x037e, 0x2011, 0x8048,
+ 0x2518, 0x1078, 0x361b, 0x037f, 0x027f, 0x007c, 0xa484, 0x01ff,
+ 0x6882, 0xa005, 0x0040, 0x5560, 0xa080, 0x001f, 0xa084, 0x03f8,
+ 0x80ac, 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0x007c,
+ 0x20a9, 0x000c, 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5,
+ 0xa085, 0x0001, 0x0078, 0x555f, 0x7000, 0xa084, 0xff00, 0xa08c,
+ 0xf000, 0x8007, 0xa196, 0x0000, 0x00c0, 0x5579, 0x0078, 0x57ba,
+ 0x007c, 0xa196, 0x2000, 0x00c0, 0x558a, 0x6900, 0xa18e, 0x0001,
+ 0x00c0, 0x5586, 0x1078, 0x3aec, 0x0078, 0x5578, 0x1078, 0x5592,
+ 0x0078, 0x5578, 0xa196, 0x8000, 0x00c0, 0x5578, 0x1078, 0x5871,
+ 0x0078, 0x5578, 0x0c7e, 0x7110, 0xa18c, 0xff00, 0x810f, 0xa196,
+ 0x0001, 0x0040, 0x559f, 0xa196, 0x0023, 0x00c0, 0x56aa, 0xa08e,
+ 0x0023, 0x00c0, 0x55d4, 0x1078, 0x591d, 0x0040, 0x56aa, 0x7124,
+ 0x610a, 0x7030, 0xa08e, 0x0200, 0x00c0, 0x55b8, 0x7034, 0xa005,
+ 0x00c0, 0x56aa, 0x2009, 0x0015, 0x1078, 0x775c, 0x0078, 0x56aa,
+ 0xa08e, 0x0214, 0x0040, 0x55c0, 0xa08e, 0x0210, 0x00c0, 0x55c6,
+ 0x2009, 0x0015, 0x1078, 0x775c, 0x0078, 0x56aa, 0xa08e, 0x0100,
+ 0x00c0, 0x56aa, 0x7034, 0xa005, 0x00c0, 0x56aa, 0x2009, 0x0016,
+ 0x1078, 0x775c, 0x0078, 0x56aa, 0xa08e, 0x0022, 0x00c0, 0x56aa,
+ 0x7030, 0xa08e, 0x0300, 0x00c0, 0x55e5, 0x7034, 0xa005, 0x00c0,
+ 0x56aa, 0x2009, 0x0017, 0x0078, 0x5676, 0xa08e, 0x0500, 0x00c0,
+ 0x55f1, 0x7034, 0xa005, 0x00c0, 0x56aa, 0x2009, 0x0018, 0x0078,
+ 0x5676, 0xa08e, 0x2010, 0x00c0, 0x55f9, 0x2009, 0x0019, 0x0078,
+ 0x5676, 0xa08e, 0x2110, 0x00c0, 0x5601, 0x2009, 0x001a, 0x0078,
+ 0x5676, 0xa08e, 0x5200, 0x00c0, 0x560d, 0x7034, 0xa005, 0x00c0,
+ 0x56aa, 0x2009, 0x001b, 0x0078, 0x5676, 0xa08e, 0x5000, 0x00c0,
+ 0x5619, 0x7034, 0xa005, 0x00c0, 0x56aa, 0x2009, 0x001c, 0x0078,
+ 0x5676, 0xa08e, 0x1300, 0x00c0, 0x5621, 0x2009, 0x0034, 0x0078,
+ 0x5676, 0xa08e, 0x1200, 0x00c0, 0x562d, 0x7034, 0xa005, 0x00c0,
+ 0x56aa, 0x2009, 0x0024, 0x0078, 0x5676, 0xa08c, 0xff00, 0xa18e,
+ 0x2400, 0x00c0, 0x5637, 0x2009, 0x002d, 0x0078, 0x5676, 0xa08c,
+ 0xff00, 0xa18e, 0x5300, 0x00c0, 0x5641, 0x2009, 0x002a, 0x0078,
+ 0x5676, 0xa08e, 0x0f00, 0x00c0, 0x5649, 0x2009, 0x0020, 0x0078,
+ 0x5676, 0xa08e, 0x5300, 0x00c0, 0x564f, 0x0078, 0x566c, 0xa08e,
+ 0x6104, 0x00c0, 0x566c, 0x2011, 0xab8d, 0x8208, 0x2204, 0xa082,
+ 0x0004, 0x20a8, 0x95ac, 0x95ac, 0x2011, 0x8015, 0x211c, 0x8108,
+ 0x047e, 0x2124, 0x1078, 0x361b, 0x047f, 0x8108, 0x00f0, 0x565c,
+ 0x2009, 0x0023, 0x0078, 0x5676, 0xa08e, 0x6000, 0x00c0, 0x5674,
+ 0x2009, 0x003f, 0x0078, 0x5676, 0x2009, 0x001d, 0x017e, 0x2011,
+ 0xab83, 0x2204, 0x8211, 0x220c, 0x1078, 0x254d, 0x00c0, 0x56ac,
+ 0x1078, 0x455c, 0x00c0, 0x56ac, 0x6612, 0x6516, 0x86ff, 0x0040,
+ 0x569c, 0x017f, 0x017e, 0xa186, 0x0017, 0x00c0, 0x569c, 0x686c,
+ 0xa606, 0x00c0, 0x569c, 0x6870, 0xa506, 0xa084, 0xff00, 0x00c0,
+ 0x569c, 0x6000, 0xc0f5, 0x6002, 0x0c7e, 0x1078, 0x76c7, 0x0040,
+ 0x56af, 0x017f, 0x611a, 0x601f, 0x0004, 0x7120, 0x610a, 0x017f,
+ 0x1078, 0x775c, 0x0c7f, 0x007c, 0x017f, 0x0078, 0x56aa, 0x0c7f,
+ 0x0078, 0x56ac, 0x0c7e, 0x1078, 0x570f, 0x00c0, 0x570d, 0xa28e,
+ 0x0033, 0x00c0, 0x56de, 0x1078, 0x591d, 0x0040, 0x570d, 0x7124,
+ 0x610a, 0x7030, 0xa08e, 0x0200, 0x00c0, 0x56d0, 0x7034, 0xa005,
+ 0x00c0, 0x570d, 0x2009, 0x0015, 0x1078, 0x775c, 0x0078, 0x570d,
+ 0xa08e, 0x0100, 0x00c0, 0x570d, 0x7034, 0xa005, 0x00c0, 0x570d,
+ 0x2009, 0x0016, 0x1078, 0x775c, 0x0078, 0x570d, 0xa28e, 0x0032,
+ 0x00c0, 0x570d, 0x7030, 0xa08e, 0x1400, 0x00c0, 0x570d, 0x2009,
+ 0x0038, 0x017e, 0x2011, 0xab83, 0x2204, 0x8211, 0x220c, 0x1078,
+ 0x254d, 0x00c0, 0x570c, 0x1078, 0x455c, 0x00c0, 0x570c, 0x6612,
+ 0x6516, 0x0c7e, 0x1078, 0x76c7, 0x0040, 0x570b, 0x017f, 0x611a,
+ 0x601f, 0x0004, 0x7120, 0x610a, 0x017f, 0x1078, 0x775c, 0x1078,
+ 0x62d1, 0x0078, 0x570d, 0x0c7f, 0x017f, 0x0c7f, 0x007c, 0x0f7e,
+ 0x0d7e, 0x027e, 0x017e, 0x137e, 0x147e, 0x157e, 0x3c00, 0x007e,
+ 0x2079, 0x0030, 0x2069, 0x0200, 0x1078, 0x1c6a, 0x00c0, 0x5750,
+ 0x1078, 0x1b40, 0x0040, 0x575d, 0x7908, 0xa18c, 0x1fff, 0xa182,
+ 0x0011, 0x00c8, 0x575a, 0x20a9, 0x000c, 0x20e1, 0x0000, 0x2ea0,
+ 0x2099, 0x020a, 0x53a5, 0x20e1, 0x2000, 0x2001, 0x020a, 0x2004,
+ 0x7a0c, 0x7808, 0xa080, 0x0007, 0xa084, 0x1ff8, 0xa08a, 0x0140,
+ 0x10c8, 0x1332, 0x80ac, 0x20e1, 0x6000, 0x2099, 0x020a, 0x53a5,
+ 0x20e1, 0x7000, 0x6828, 0x6828, 0x7803, 0x0004, 0xa294, 0x0070,
+ 0x007f, 0x20e0, 0x157f, 0x147f, 0x137f, 0x017f, 0x027f, 0x0d7f,
+ 0x0f7f, 0x007c, 0xa016, 0x1078, 0x15fa, 0xa085, 0x0001, 0x0078,
+ 0x5750, 0x047e, 0x0e7e, 0x0d7e, 0x2028, 0x2130, 0xa696, 0x00ff,
+ 0x00c0, 0x5782, 0xa596, 0xfffd, 0x00c0, 0x5772, 0x2009, 0x007f,
+ 0x0078, 0x57b5, 0xa596, 0xfffe, 0x00c0, 0x577a, 0x2009, 0x007e,
+ 0x0078, 0x57b5, 0xa596, 0xfffc, 0x00c0, 0x5782, 0x2009, 0x0080,
+ 0x0078, 0x57b5, 0x2011, 0x0000, 0x2021, 0x0081, 0x20a9, 0x007e,
+ 0x2071, 0xa7b6, 0x2e1c, 0x83ff, 0x00c0, 0x5794, 0x82ff, 0x00c0,
+ 0x57a9, 0x2410, 0x0078, 0x57a9, 0x2368, 0x6f10, 0x007e, 0x2100,
+ 0xa706, 0x007f, 0x6b14, 0x00c0, 0x57a3, 0xa346, 0x00c0, 0x57a3,
+ 0x2408, 0x0078, 0x57b5, 0x87ff, 0x00c0, 0x57a9, 0x83ff, 0x0040,
+ 0x578e, 0x8420, 0x8e70, 0x00f0, 0x578a, 0x82ff, 0x00c0, 0x57b4,
+ 0xa085, 0x0001, 0x0078, 0x57b6, 0x2208, 0xa006, 0x0d7f, 0x0e7f,
+ 0x047f, 0x007c, 0xa084, 0x0007, 0x0079, 0x57bf, 0x007c, 0x57c7,
+ 0x57c7, 0x57c7, 0x5933, 0x57c7, 0x57c8, 0x57e1, 0x5858, 0x007c,
+ 0x7110, 0xd1bc, 0x0040, 0x57e0, 0x7120, 0x2160, 0xac8c, 0x000f,
+ 0x00c0, 0x57e0, 0xac8a, 0xad00, 0x0048, 0x57e0, 0x6858, 0xac02,
+ 0x00c8, 0x57e0, 0x7124, 0x610a, 0x2009, 0x0046, 0x1078, 0x775c,
+ 0x007c, 0x0c7e, 0xa484, 0x01ff, 0x0040, 0x5833, 0x7110, 0xd1bc,
+ 0x00c0, 0x5833, 0x2011, 0xab83, 0x2204, 0x8211, 0x220c, 0x1078,
+ 0x254d, 0x00c0, 0x5833, 0x1078, 0x455c, 0x00c0, 0x5833, 0x6612,
+ 0x6516, 0x6000, 0xd0ec, 0x00c0, 0x5833, 0x6204, 0xa294, 0xff00,
+ 0x8217, 0xa286, 0x0006, 0x00c0, 0x5818, 0x0c7e, 0x1078, 0x76c7,
+ 0x017f, 0x0040, 0x5835, 0x611a, 0x601f, 0x0006, 0x7120, 0x610a,
+ 0x7130, 0x6122, 0x2009, 0x0044, 0x1078, 0x775c, 0x0078, 0x5833,
+ 0x0c7e, 0x1078, 0x76c7, 0x017f, 0x0040, 0x5833, 0x611a, 0x601f,
+ 0x0004, 0x7120, 0x610a, 0xa286, 0x0004, 0x00c0, 0x582b, 0x6007,
+ 0x0005, 0x0078, 0x582d, 0x6007, 0x0001, 0x6003, 0x0001, 0x1078,
+ 0x5dd7, 0x1078, 0x62d1, 0x0c7f, 0x007c, 0x2001, 0xa60d, 0x2004,
+ 0xd0ec, 0x0040, 0x583f, 0x2011, 0x8049, 0x1078, 0x361b, 0x0c7e,
+ 0x1078, 0x9187, 0x017f, 0x0040, 0x5833, 0x611a, 0x601f, 0x0006,
+ 0x7120, 0x610a, 0x7130, 0x6122, 0x6013, 0x0300, 0x6003, 0x0001,
+ 0x6007, 0x0041, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0078, 0x5833,
+ 0x7110, 0xd1bc, 0x0040, 0x5870, 0x7020, 0x2060, 0xac84, 0x000f,
+ 0x00c0, 0x5870, 0xac82, 0xad00, 0x0048, 0x5870, 0x6858, 0xac02,
+ 0x00c8, 0x5870, 0x7124, 0x610a, 0x2009, 0x0045, 0x1078, 0x775c,
+ 0x007c, 0x007e, 0x1078, 0x29bb, 0x007f, 0x00c0, 0x5887, 0x7110,
+ 0xa18c, 0xff00, 0x810f, 0xa18e, 0x0000, 0x00c0, 0x5887, 0xa084,
+ 0x000f, 0xa08a, 0x0006, 0x00c8, 0x5887, 0x1079, 0x5888, 0x007c,
+ 0x588e, 0x588f, 0x588e, 0x588e, 0x58ff, 0x590e, 0x007c, 0x7110,
+ 0xd1bc, 0x0040, 0x5897, 0x702c, 0xd084, 0x0040, 0x58fe, 0x700c,
+ 0x7108, 0x1078, 0x254d, 0x00c0, 0x58fe, 0x1078, 0x455c, 0x00c0,
+ 0x58fe, 0x6612, 0x6516, 0x6204, 0x7110, 0xd1bc, 0x0040, 0x58c9,
+ 0xa28c, 0x00ff, 0xa186, 0x0004, 0x0040, 0x58b2, 0xa186, 0x0006,
+ 0x00c0, 0x58ef, 0x0c7e, 0x1078, 0x591d, 0x0c7f, 0x0040, 0x58fe,
+ 0x0c7e, 0x1078, 0x76c7, 0x017f, 0x0040, 0x58fe, 0x611a, 0x601f,
+ 0x0002, 0x7120, 0x610a, 0x2009, 0x0088, 0x1078, 0x775c, 0x0078,
+ 0x58fe, 0xa28c, 0x00ff, 0xa186, 0x0006, 0x0040, 0x58de, 0xa186,
+ 0x0004, 0x0040, 0x58de, 0xa294, 0xff00, 0x8217, 0xa286, 0x0004,
+ 0x0040, 0x58de, 0xa286, 0x0006, 0x00c0, 0x58ef, 0x0c7e, 0x1078,
+ 0x76c7, 0x017f, 0x0040, 0x58fe, 0x611a, 0x601f, 0x0005, 0x7120,
+ 0x610a, 0x2009, 0x0088, 0x1078, 0x775c, 0x0078, 0x58fe, 0x0c7e,
+ 0x1078, 0x76c7, 0x017f, 0x0040, 0x58fe, 0x611a, 0x601f, 0x0004,
+ 0x7120, 0x610a, 0x2009, 0x0001, 0x1078, 0x775c, 0x007c, 0x7110,
+ 0xd1bc, 0x0040, 0x590d, 0x1078, 0x591d, 0x0040, 0x590d, 0x7124,
+ 0x610a, 0x2009, 0x0089, 0x1078, 0x775c, 0x007c, 0x7110, 0xd1bc,
+ 0x0040, 0x591c, 0x1078, 0x591d, 0x0040, 0x591c, 0x7124, 0x610a,
+ 0x2009, 0x008a, 0x1078, 0x775c, 0x007c, 0x7020, 0x2060, 0xac84,
+ 0x000f, 0x00c0, 0x5930, 0xac82, 0xad00, 0x0048, 0x5930, 0x2001,
+ 0xa616, 0x2004, 0xac02, 0x00c8, 0x5930, 0xa085, 0x0001, 0x007c,
+ 0xa006, 0x0078, 0x592f, 0x7110, 0xd1bc, 0x00c0, 0x5949, 0x7024,
+ 0x2060, 0xac84, 0x000f, 0x00c0, 0x5949, 0xac82, 0xad00, 0x0048,
+ 0x5949, 0x6858, 0xac02, 0x00c8, 0x5949, 0x2009, 0x0051, 0x1078,
+ 0x775c, 0x007c, 0x2071, 0xa8c4, 0x7003, 0x0003, 0x700f, 0x0361,
+ 0xa006, 0x701a, 0x7012, 0x7017, 0xad00, 0x7007, 0x0000, 0x7026,
+ 0x702b, 0x6e1c, 0x7032, 0x7037, 0x6e70, 0x703b, 0x0002, 0x703f,
+ 0x0000, 0x7043, 0xffff, 0x7047, 0xffff, 0x007c, 0x2071, 0xa8c4,
+ 0x00e0, 0x5a32, 0x2091, 0x6000, 0x700c, 0x8001, 0x700e, 0x00c0,
+ 0x59de, 0x700f, 0x0361, 0x7007, 0x0001, 0x127e, 0x2091, 0x8000,
+ 0x7138, 0x8109, 0x713a, 0x00c0, 0x59dc, 0x703b, 0x0002, 0x2009,
+ 0x0100, 0x2104, 0xa082, 0x0003, 0x00c8, 0x59dc, 0x703c, 0xa086,
+ 0x0001, 0x00c0, 0x59b9, 0x0d7e, 0x2069, 0x0140, 0x6804, 0xa084,
+ 0x4000, 0x0040, 0x5997, 0x6803, 0x1000, 0x0078, 0x599e, 0x6804,
+ 0xa084, 0x1000, 0x0040, 0x599e, 0x6803, 0x0100, 0x6803, 0x0000,
+ 0x703f, 0x0000, 0x2069, 0xa8b1, 0x6804, 0xa082, 0x0006, 0x00c0,
+ 0x59ab, 0x6807, 0x0000, 0x6830, 0xa082, 0x0003, 0x00c0, 0x59b2,
+ 0x6833, 0x0000, 0x1078, 0x62d1, 0x1078, 0x639b, 0x0d7f, 0x0078,
+ 0x59dc, 0x0d7e, 0x2069, 0xa600, 0x6948, 0x6864, 0xa102, 0x00c8,
+ 0x59db, 0x2069, 0xa8b1, 0x6804, 0xa086, 0x0000, 0x00c0, 0x59db,
+ 0x6830, 0xa086, 0x0000, 0x00c0, 0x59db, 0x703f, 0x0001, 0x6807,
+ 0x0006, 0x6833, 0x0003, 0x2069, 0x0100, 0x6830, 0x689e, 0x2069,
+ 0x0140, 0x6803, 0x0600, 0x0d7f, 0x0078, 0x59e1, 0x127e, 0x2091,
+ 0x8000, 0x7024, 0xa00d, 0x0040, 0x59f9, 0x7020, 0x8001, 0x7022,
+ 0x00c0, 0x59f9, 0x7023, 0x0009, 0x8109, 0x7126, 0xa186, 0x03e8,
+ 0x00c0, 0x59f4, 0x7028, 0x107a, 0x81ff, 0x00c0, 0x59f9, 0x7028,
+ 0x107a, 0x7030, 0xa00d, 0x0040, 0x5a10, 0x702c, 0x8001, 0x702e,
+ 0x00c0, 0x5a10, 0x702f, 0x0009, 0x8109, 0x7132, 0x0040, 0x5a0e,
+ 0xa184, 0x007f, 0x1040, 0x6ea2, 0x0078, 0x5a10, 0x7034, 0x107a,
+ 0x7040, 0xa005, 0x0040, 0x5a18, 0x0050, 0x5a18, 0x8001, 0x7042,
+ 0x7044, 0xa005, 0x0040, 0x5a20, 0x0050, 0x5a20, 0x8001, 0x7046,
+ 0x7018, 0xa00d, 0x0040, 0x5a31, 0x7008, 0x8001, 0x700a, 0x00c0,
+ 0x5a31, 0x700b, 0x0009, 0x8109, 0x711a, 0x00c0, 0x5a31, 0x701c,
+ 0x107a, 0x127f, 0x7004, 0x0079, 0x5a35, 0x5a5c, 0x5a5d, 0x5a79,
+ 0x0e7e, 0x2071, 0xa8c4, 0x7018, 0xa005, 0x00c0, 0x5a43, 0x711a,
+ 0x721e, 0x700b, 0x0009, 0x0e7f, 0x007c, 0x0e7e, 0x007e, 0x2071,
+ 0xa8c4, 0x701c, 0xa206, 0x00c0, 0x5a4f, 0x701a, 0x701e, 0x007f,
+ 0x0e7f, 0x007c, 0x0e7e, 0x2071, 0xa8c4, 0x6088, 0xa102, 0x0048,
+ 0x5a5a, 0x618a, 0x0e7f, 0x007c, 0x007c, 0x7110, 0x1078, 0x45c4,
+ 0x00c0, 0x5a6f, 0x6088, 0x8001, 0x0048, 0x5a6f, 0x608a, 0x00c0,
+ 0x5a6f, 0x127e, 0x2091, 0x8000, 0x1078, 0x62d1, 0x127f, 0x8108,
+ 0xa182, 0x00ff, 0x0048, 0x5a77, 0xa00e, 0x7007, 0x0002, 0x7112,
+ 0x007c, 0x7014, 0x2060, 0x127e, 0x2091, 0x8000, 0x603c, 0xa005,
+ 0x0040, 0x5a88, 0x8001, 0x603e, 0x00c0, 0x5a88, 0x1078, 0x8f9c,
+ 0x6014, 0xa005, 0x0040, 0x5ab2, 0x8001, 0x6016, 0x00c0, 0x5ab2,
+ 0x611c, 0xa186, 0x0003, 0x0040, 0x5a99, 0xa186, 0x0006, 0x00c0,
+ 0x5ab0, 0x6010, 0x2068, 0x6854, 0xa08a, 0x199a, 0x0048, 0x5ab0,
+ 0xa082, 0x1999, 0x6856, 0xa08a, 0x199a, 0x0048, 0x5aa9, 0x2001,
+ 0x1999, 0x8003, 0x800b, 0x810b, 0xa108, 0x6116, 0x0078, 0x5ab2,
+ 0x1078, 0x8abe, 0x127f, 0xac88, 0x0010, 0x7116, 0x2001, 0xcd00,
+ 0xa102, 0x0048, 0x5abf, 0x7017, 0xad00, 0x7007, 0x0000, 0x007c,
+ 0x0e7e, 0x2071, 0xa8c4, 0x7027, 0x07d0, 0x7023, 0x0009, 0x703b,
+ 0x0002, 0x0e7f, 0x007c, 0x2001, 0xa8cd, 0x2003, 0x0000, 0x007c,
+ 0x0e7e, 0x2071, 0xa8c4, 0x7132, 0x702f, 0x0009, 0x0e7f, 0x007c,
+ 0x2011, 0xa8d0, 0x2013, 0x0000, 0x007c, 0x0e7e, 0x2071, 0xa8c4,
+ 0x711a, 0x721e, 0x700b, 0x0009, 0x0e7f, 0x007c, 0x027e, 0x0e7e,
+ 0x0f7e, 0x2079, 0xa600, 0x7a34, 0xd294, 0x0040, 0x5b15, 0x2071,
+ 0xa8ac, 0x2e14, 0xa0fe, 0x0000, 0x0040, 0x5b02, 0xa0fe, 0x0001,
+ 0x0040, 0x5b06, 0xa0fe, 0x0002, 0x00c0, 0x5b11, 0xa292, 0x0085,
+ 0x0078, 0x5b08, 0xa292, 0x0005, 0x0078, 0x5b08, 0xa292, 0x0002,
+ 0x2272, 0x0040, 0x5b0d, 0x00c8, 0x5b15, 0x2011, 0x8037, 0x1078,
+ 0x361b, 0x2011, 0xa8ab, 0x2204, 0x2072, 0x0f7f, 0x0e7f, 0x027f,
+ 0x007c, 0x0c7e, 0x2061, 0xa933, 0x0c7f, 0x007c, 0xa184, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa080, 0xa933, 0x2060, 0x007c, 0x6854,
+ 0xa08a, 0x199a, 0x0048, 0x5b2e, 0x2001, 0x1999, 0xa005, 0x00c0,
+ 0x5b3d, 0x0c7e, 0x2061, 0xa933, 0x6014, 0x0c7f, 0xa005, 0x00c0,
+ 0x5b42, 0x2001, 0x001e, 0x0078, 0x5b42, 0xa08e, 0xffff, 0x00c0,
+ 0x5b42, 0xa006, 0x8003, 0x800b, 0x810b, 0xa108, 0x6116, 0x684c,
+ 0xa08c, 0x00c0, 0xa18e, 0x00c0, 0x0040, 0x5b9e, 0xd0b4, 0x00c0,
+ 0x5b59, 0xd0bc, 0x00c0, 0x5b8b, 0x2009, 0x0006, 0x1078, 0x5bc3,
+ 0x007c, 0xd0fc, 0x0040, 0x5b64, 0xa084, 0x0003, 0x0040, 0x5b64,
+ 0xa086, 0x0003, 0x00c0, 0x5bbc, 0x6024, 0xd0d4, 0x0040, 0x5b6e,
+ 0xc0d4, 0x6026, 0x6860, 0x602a, 0x685c, 0x602e, 0x2009, 0xa674,
+ 0x2104, 0xd084, 0x0040, 0x5b83, 0x6118, 0xa188, 0x0027, 0x2104,
+ 0xd08c, 0x00c0, 0x5b83, 0x87ff, 0x00c0, 0x5b82, 0x2009, 0x0042,
+ 0x1078, 0x775c, 0x007c, 0x87ff, 0x00c0, 0x5b8a, 0x2009, 0x0043,
+ 0x1078, 0x775c, 0x007c, 0xd0fc, 0x0040, 0x5b96, 0xa084, 0x0003,
+ 0x0040, 0x5b96, 0xa086, 0x0003, 0x00c0, 0x5bbc, 0x87ff, 0x00c0,
+ 0x5b9d, 0x2009, 0x0042, 0x1078, 0x775c, 0x007c, 0xd0fc, 0x0040,
+ 0x5baf, 0xa084, 0x0003, 0xa08e, 0x0002, 0x0040, 0x5bb3, 0x87ff,
+ 0x00c0, 0x5bae, 0x2009, 0x0041, 0x1078, 0x775c, 0x007c, 0x1078,
+ 0x5bc1, 0x0078, 0x5bae, 0x87ff, 0x00c0, 0x5bae, 0x2009, 0x0043,
+ 0x1078, 0x775c, 0x0078, 0x5bae, 0x2009, 0x0004, 0x1078, 0x5bc3,
+ 0x007c, 0x2009, 0x0001, 0x0d7e, 0x6010, 0xa0ec, 0xf000, 0x0040,
+ 0x5bef, 0x2068, 0x6952, 0x6800, 0x6012, 0xa186, 0x0001, 0x00c0,
+ 0x5be5, 0x694c, 0xa18c, 0x8100, 0xa18e, 0x8100, 0x00c0, 0x5be5,
+ 0x0c7e, 0x2061, 0xa933, 0x6200, 0xd28c, 0x00c0, 0x5be4, 0x6204,
+ 0x8210, 0x0048, 0x5be4, 0x6206, 0x0c7f, 0x1078, 0x4a73, 0x6010,
+ 0xa06d, 0x077e, 0x2039, 0x0000, 0x10c0, 0x5b27, 0x077f, 0x0d7f,
+ 0x007c, 0x157e, 0x0c7e, 0x2061, 0xa933, 0x6000, 0x81ff, 0x0040,
+ 0x5bfc, 0xa205, 0x0078, 0x5bfd, 0xa204, 0x6002, 0x0c7f, 0x157f,
+ 0x007c, 0x6800, 0xd08c, 0x00c0, 0x5c0d, 0x6808, 0xa005, 0x0040,
+ 0x5c0d, 0x8001, 0x680a, 0xa085, 0x0001, 0x007c, 0x20a9, 0x0010,
+ 0xa006, 0x8004, 0x8086, 0x818e, 0x00c8, 0x5c17, 0xa200, 0x00f0,
+ 0x5c12, 0x8086, 0x818e, 0x007c, 0x157e, 0x20a9, 0x0010, 0xa005,
+ 0x0040, 0x5c3d, 0xa11a, 0x00c8, 0x5c3d, 0x8213, 0x818d, 0x0048,
+ 0x5c30, 0xa11a, 0x00c8, 0x5c31, 0x00f0, 0x5c25, 0x0078, 0x5c35,
+ 0xa11a, 0x2308, 0x8210, 0x00f0, 0x5c25, 0x007e, 0x3200, 0xa084,
+ 0xf7ff, 0x2080, 0x007f, 0x157f, 0x007c, 0x007e, 0x3200, 0xa085,
+ 0x0800, 0x0078, 0x5c39, 0x127e, 0x2091, 0x2200, 0x2079, 0xa8b1,
+ 0x127f, 0x0d7e, 0x2069, 0xa8b1, 0x6803, 0x0005, 0x2069, 0x0004,
+ 0x2d04, 0xa085, 0x8001, 0x206a, 0x0d7f, 0x007c, 0x0c7e, 0x6027,
+ 0x0001, 0x7804, 0xa084, 0x0007, 0x0079, 0x5c5e, 0x5c68, 0x5c8d,
+ 0x5ce8, 0x5c6e, 0x5c8d, 0x5c68, 0x5c66, 0x5c66, 0x1078, 0x1332,
+ 0x1078, 0x5acb, 0x1078, 0x62d1, 0x0c7f, 0x007c, 0x62c0, 0x82ff,
+ 0x00c0, 0x5c74, 0x0c7f, 0x007c, 0x2011, 0x41dc, 0x1078, 0x5a45,
+ 0x7828, 0xa092, 0x00c8, 0x00c8, 0x5c83, 0x8000, 0x782a, 0x1078,
+ 0x421b, 0x0078, 0x5c72, 0x1078, 0x41dc, 0x7807, 0x0003, 0x7827,
+ 0x0000, 0x782b, 0x0000, 0x0078, 0x5c72, 0x1078, 0x5acb, 0x3c00,
+ 0x007e, 0x2011, 0x0209, 0x20e1, 0x4000, 0x2214, 0x007f, 0x20e0,
+ 0x82ff, 0x0040, 0x5cab, 0x62c0, 0x82ff, 0x00c0, 0x5cab, 0x782b,
+ 0x0000, 0x7824, 0xa065, 0x1040, 0x1332, 0x2009, 0x0013, 0x1078,
+ 0x775c, 0x0c7f, 0x007c, 0x3900, 0xa082, 0xa9e3, 0x00c8, 0x5cb2,
+ 0x1078, 0x747a, 0x0c7e, 0x7824, 0xa065, 0x1040, 0x1332, 0x7804,
+ 0xa086, 0x0004, 0x0040, 0x5d2d, 0x7828, 0xa092, 0x2710, 0x00c8,
+ 0x5cc8, 0x8000, 0x782a, 0x0c7f, 0x1078, 0x6e01, 0x0078, 0x5ca9,
+ 0x6104, 0xa186, 0x0003, 0x00c0, 0x5cdf, 0x0e7e, 0x2071, 0xa600,
+ 0x70d8, 0x0e7f, 0xd08c, 0x0040, 0x5cdf, 0x0c7e, 0x0e7e, 0x2061,
+ 0x0100, 0x2071, 0xa600, 0x1078, 0x4224, 0x0e7f, 0x0c7f, 0x1078,
+ 0xa5c4, 0x2009, 0x0014, 0x1078, 0x775c, 0x0c7f, 0x0078, 0x5ca9,
+ 0x2001, 0xa8cd, 0x2003, 0x0000, 0x62c0, 0x82ff, 0x00c0, 0x5cfc,
+ 0x782b, 0x0000, 0x7824, 0xa065, 0x1040, 0x1332, 0x2009, 0x0013,
+ 0x1078, 0x77b3, 0x0c7f, 0x007c, 0x0c7e, 0x0d7e, 0x3900, 0xa082,
+ 0xa9e3, 0x00c8, 0x5d05, 0x1078, 0x747a, 0x7824, 0xa005, 0x1040,
+ 0x1332, 0x781c, 0xa06d, 0x1040, 0x1332, 0x6800, 0xc0dc, 0x6802,
+ 0x7924, 0x2160, 0x1078, 0x772d, 0x693c, 0x81ff, 0x1040, 0x1332,
+ 0x8109, 0x693e, 0x6854, 0xa015, 0x0040, 0x5d21, 0x7a1e, 0x0078,
+ 0x5d23, 0x7918, 0x791e, 0x7807, 0x0000, 0x7827, 0x0000, 0x0d7f,
+ 0x0c7f, 0x1078, 0x62d1, 0x0078, 0x5cfa, 0x6104, 0xa186, 0x0002,
+ 0x0040, 0x5d38, 0xa186, 0x0004, 0x0040, 0x5d38, 0x0078, 0x5cbc,
+ 0x7808, 0xac06, 0x0040, 0x5cbc, 0x1078, 0x61cd, 0x1078, 0x5dd7,
+ 0x0c7f, 0x1078, 0x62d1, 0x0078, 0x5ca9, 0x0c7e, 0x6027, 0x0002,
+ 0x62c8, 0x82ff, 0x00c0, 0x5d61, 0x62c4, 0x82ff, 0x00c0, 0x5d61,
+ 0x793c, 0xa1e5, 0x0000, 0x0040, 0x5d5b, 0x2009, 0x0049, 0x1078,
+ 0x775c, 0x0c7f, 0x007c, 0x2011, 0xa8d0, 0x2013, 0x0000, 0x0078,
+ 0x5d59, 0x3908, 0xa192, 0xa9e3, 0x00c8, 0x5d68, 0x1078, 0x747a,
+ 0x6017, 0x0010, 0x793c, 0x81ff, 0x0040, 0x5d5b, 0x7944, 0xa192,
+ 0x7530, 0x00c8, 0x5d85, 0x8108, 0x7946, 0x793c, 0xa188, 0x0007,
+ 0x210c, 0xa18e, 0x0006, 0x00c0, 0x5d81, 0x6017, 0x0012, 0x0078,
+ 0x5d59, 0x6017, 0x0016, 0x0078, 0x5d59, 0x7848, 0xc085, 0x784a,
+ 0x0078, 0x5d59, 0x007e, 0x017e, 0x0c7e, 0x127e, 0x2091, 0x8000,
+ 0x600f, 0x0000, 0x2c08, 0x2061, 0xa8b1, 0x6020, 0x8000, 0x6022,
+ 0x6010, 0xa005, 0x0040, 0x5da5, 0xa080, 0x0003, 0x2102, 0x6112,
+ 0x127f, 0x0c7f, 0x017f, 0x007f, 0x007c, 0x6116, 0x6112, 0x0078,
+ 0x5da0, 0x0d7e, 0x2069, 0xa8b1, 0x6000, 0xd0d4, 0x0040, 0x5dbe,
+ 0x6820, 0x8000, 0x6822, 0xa086, 0x0001, 0x00c0, 0x5db9, 0x2c00,
+ 0x681e, 0x6804, 0xa084, 0x0007, 0x0079, 0x62d9, 0xc0d5, 0x6002,
+ 0x6818, 0xa005, 0x0040, 0x5dd0, 0x6056, 0x605b, 0x0000, 0x007e,
+ 0x2c00, 0x681a, 0x0d7f, 0x685a, 0x2069, 0xa8b1, 0x0078, 0x5db0,
+ 0x6056, 0x605a, 0x2c00, 0x681a, 0x681e, 0x0078, 0x5db0, 0x007e,
+ 0x017e, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x600f, 0x0000, 0x2c08,
+ 0x2061, 0xa8b1, 0x6020, 0x8000, 0x6022, 0x6008, 0xa005, 0x0040,
+ 0x5df2, 0xa080, 0x0003, 0x2102, 0x610a, 0x127f, 0x0c7f, 0x017f,
+ 0x007f, 0x007c, 0x610e, 0x610a, 0x0078, 0x5ded, 0x0c7e, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0xa8b1, 0x6034, 0xa005, 0x0040, 0x5e06,
+ 0xa080, 0x0003, 0x2102, 0x6136, 0x0c7f, 0x007c, 0x613a, 0x6136,
+ 0x0078, 0x5e04, 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e, 0x057e,
+ 0x037e, 0x027e, 0x017e, 0x007e, 0x127e, 0xa02e, 0x2071, 0xa8b1,
+ 0x7638, 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff, 0x0040, 0x5e8c,
+ 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x00c0, 0x5e87, 0x87ff,
+ 0x0040, 0x5e2e, 0x6020, 0xa106, 0x00c0, 0x5e87, 0x703c, 0xac06,
+ 0x00c0, 0x5e44, 0x037e, 0x2019, 0x0001, 0x1078, 0x7058, 0x7033,
+ 0x0000, 0x703f, 0x0000, 0x7043, 0x0000, 0x7047, 0x0000, 0x704b,
+ 0x0000, 0x037f, 0x2029, 0x0001, 0x7038, 0xac36, 0x00c0, 0x5e4a,
+ 0x660c, 0x763a, 0x7034, 0xac36, 0x00c0, 0x5e58, 0x2c00, 0xaf36,
+ 0x0040, 0x5e56, 0x2f00, 0x7036, 0x0078, 0x5e58, 0x7037, 0x0000,
+ 0x660c, 0x067e, 0x2c00, 0xaf06, 0x0040, 0x5e61, 0x7e0e, 0x0078,
+ 0x5e62, 0x2678, 0x600f, 0x0000, 0x1078, 0x8d06, 0x0040, 0x5e82,
+ 0x6010, 0x2068, 0x601c, 0xa086, 0x0003, 0x00c0, 0x5e9d, 0x6837,
+ 0x0103, 0x6b4a, 0x6847, 0x0000, 0x017e, 0x037e, 0x077e, 0x1078,
+ 0x8f7d, 0x1078, 0xa4e2, 0x1078, 0x4a73, 0x077f, 0x037f, 0x017f,
+ 0x1078, 0x8eb9, 0x1078, 0x8ec6, 0x0c7f, 0x0078, 0x5e1d, 0x2c78,
+ 0x600c, 0x2060, 0x0078, 0x5e1d, 0x85ff, 0x0040, 0x5e91, 0x1078,
+ 0x639b, 0x127f, 0x007f, 0x017f, 0x027f, 0x037f, 0x057f, 0x067f,
+ 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x601c, 0xa086, 0x0006,
+ 0x00c0, 0x5e6f, 0x017e, 0x037e, 0x077e, 0x1078, 0xa4e2, 0x1078,
+ 0xa1ca, 0x077f, 0x037f, 0x017f, 0x0078, 0x5e82, 0x007e, 0x067e,
+ 0x0c7e, 0x0d7e, 0x0f7e, 0x2031, 0x0000, 0x127e, 0x2091, 0x8000,
+ 0x2079, 0xa8b1, 0x7838, 0xa065, 0x0040, 0x5eef, 0x600c, 0x007e,
+ 0x600f, 0x0000, 0x783c, 0xac06, 0x00c0, 0x5ed6, 0x037e, 0x2019,
+ 0x0001, 0x1078, 0x7058, 0x7833, 0x0000, 0x783f, 0x0000, 0x7843,
+ 0x0000, 0x7847, 0x0000, 0x784b, 0x0000, 0x037f, 0x1078, 0x8d06,
+ 0x0040, 0x5eea, 0x6010, 0x2068, 0x601c, 0xa086, 0x0003, 0x00c0,
+ 0x5ef8, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x4a73,
+ 0x1078, 0x8eb9, 0x1078, 0x8ec6, 0x007f, 0x0078, 0x5ebb, 0x7e3a,
+ 0x7e36, 0x127f, 0x0f7f, 0x0d7f, 0x0c7f, 0x067f, 0x007f, 0x007c,
+ 0x601c, 0xa086, 0x0006, 0x00c0, 0x5ee1, 0x1078, 0xa1ca, 0x0078,
+ 0x5eea, 0x017e, 0x027e, 0x087e, 0x2041, 0x0000, 0x1078, 0x5f1b,
+ 0x1078, 0x5fdb, 0x087f, 0x027f, 0x017f, 0x007c, 0x0f7e, 0x127e,
+ 0x2079, 0xa8b1, 0x2091, 0x8000, 0x1078, 0x6076, 0x1078, 0x60ec,
+ 0x127f, 0x0f7f, 0x007c, 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e,
+ 0x017e, 0x007e, 0x127e, 0x2091, 0x8000, 0x2071, 0xa8b1, 0x7614,
+ 0x2660, 0x2678, 0x8cff, 0x0040, 0x5fb5, 0x6018, 0xa080, 0x0028,
+ 0x2004, 0xa206, 0x00c0, 0x5fb0, 0x88ff, 0x0040, 0x5f3b, 0x6020,
+ 0xa106, 0x00c0, 0x5fb0, 0x7024, 0xac06, 0x00c0, 0x5f6b, 0x2069,
+ 0x0100, 0x68c0, 0xa005, 0x0040, 0x5f66, 0x1078, 0x5acb, 0x1078,
+ 0x6e0f, 0x68c3, 0x0000, 0x1078, 0x7378, 0x7027, 0x0000, 0x037e,
+ 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040, 0x5f5b, 0x6803,
+ 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0040,
+ 0x5f63, 0x6827, 0x0001, 0x037f, 0x0078, 0x5f6b, 0x6003, 0x0009,
+ 0x630a, 0x0078, 0x5fb0, 0x7014, 0xac36, 0x00c0, 0x5f71, 0x660c,
+ 0x7616, 0x7010, 0xac36, 0x00c0, 0x5f7f, 0x2c00, 0xaf36, 0x0040,
+ 0x5f7d, 0x2f00, 0x7012, 0x0078, 0x5f7f, 0x7013, 0x0000, 0x660c,
+ 0x067e, 0x2c00, 0xaf06, 0x0040, 0x5f88, 0x7e0e, 0x0078, 0x5f89,
+ 0x2678, 0x600f, 0x0000, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040,
+ 0x5fa9, 0x601c, 0xa086, 0x0003, 0x00c0, 0x5fbe, 0x6837, 0x0103,
+ 0x6b4a, 0x6847, 0x0000, 0x017e, 0x037e, 0x087e, 0x1078, 0x8f7d,
+ 0x1078, 0xa4e2, 0x1078, 0x4a73, 0x087f, 0x037f, 0x017f, 0x1078,
+ 0x8eb9, 0x1078, 0x8ec6, 0x1078, 0x7233, 0x0c7f, 0x0078, 0x5f2a,
+ 0x2c78, 0x600c, 0x2060, 0x0078, 0x5f2a, 0x127f, 0x007f, 0x017f,
+ 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x601c, 0xa086,
+ 0x0006, 0x00c0, 0x5fcf, 0x017e, 0x037e, 0x087e, 0x1078, 0xa4e2,
+ 0x1078, 0xa1ca, 0x087f, 0x037f, 0x017f, 0x0078, 0x5fa9, 0x601c,
+ 0xa086, 0x0002, 0x00c0, 0x5fa9, 0x6004, 0xa086, 0x0085, 0x0040,
+ 0x5f96, 0x0078, 0x5fa9, 0x0c7e, 0x007e, 0x127e, 0x2091, 0x8000,
+ 0xa280, 0xa735, 0x2004, 0xa065, 0x0040, 0x6072, 0x0f7e, 0x0e7e,
+ 0x0d7e, 0x067e, 0x2071, 0xa8b1, 0x6654, 0x7018, 0xac06, 0x00c0,
+ 0x5ff2, 0x761a, 0x701c, 0xac06, 0x00c0, 0x5ffe, 0x86ff, 0x00c0,
+ 0x5ffd, 0x7018, 0x701e, 0x0078, 0x5ffe, 0x761e, 0x6058, 0xa07d,
+ 0x0040, 0x6003, 0x7e56, 0xa6ed, 0x0000, 0x0040, 0x6009, 0x2f00,
+ 0x685a, 0x6057, 0x0000, 0x605b, 0x0000, 0x6000, 0xc0d4, 0xc0dc,
+ 0x6002, 0x1078, 0x44d3, 0x0040, 0x606e, 0x7624, 0x86ff, 0x0040,
+ 0x605c, 0xa680, 0x0004, 0x2004, 0xad06, 0x00c0, 0x605c, 0x0d7e,
+ 0x2069, 0x0100, 0x68c0, 0xa005, 0x0040, 0x6053, 0x1078, 0x5acb,
+ 0x1078, 0x6e0f, 0x68c3, 0x0000, 0x1078, 0x7378, 0x7027, 0x0000,
+ 0x037e, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040, 0x603c,
+ 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084,
+ 0x0040, 0x6044, 0x6827, 0x0001, 0x037f, 0x0d7f, 0x0c7e, 0x603c,
+ 0xa005, 0x0040, 0x604d, 0x8001, 0x603e, 0x2660, 0x1078, 0x8ec6,
+ 0x0c7f, 0x0078, 0x605c, 0x0d7f, 0x0c7e, 0x2660, 0x6003, 0x0009,
+ 0x630a, 0x0c7f, 0x0078, 0x6011, 0x8dff, 0x0040, 0x606a, 0x6837,
+ 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x8f7d, 0x1078, 0xa4e2,
+ 0x1078, 0x4a73, 0x1078, 0x7233, 0x0078, 0x6011, 0x067f, 0x0d7f,
+ 0x0e7f, 0x0f7f, 0x127f, 0x007f, 0x0c7f, 0x007c, 0x007e, 0x067e,
+ 0x0c7e, 0x0d7e, 0x2031, 0x0000, 0x7814, 0xa065, 0x0040, 0x60d0,
+ 0x600c, 0x007e, 0x600f, 0x0000, 0x7824, 0xac06, 0x00c0, 0x60b5,
+ 0x2069, 0x0100, 0x68c0, 0xa005, 0x0040, 0x60af, 0x1078, 0x5acb,
+ 0x1078, 0x6e0f, 0x68c3, 0x0000, 0x1078, 0x7378, 0x7827, 0x0000,
+ 0x037e, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040, 0x60a4,
+ 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084,
+ 0x0040, 0x60ac, 0x6827, 0x0001, 0x037f, 0x0078, 0x60b5, 0x6003,
+ 0x0009, 0x630a, 0x2c30, 0x0078, 0x60cd, 0x6010, 0x2068, 0x1078,
+ 0x8d06, 0x0040, 0x60c9, 0x601c, 0xa086, 0x0003, 0x00c0, 0x60d7,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x4a73, 0x1078,
+ 0x8eb9, 0x1078, 0x8ec6, 0x1078, 0x7233, 0x007f, 0x0078, 0x607d,
+ 0x7e16, 0x7e12, 0x0d7f, 0x0c7f, 0x067f, 0x007f, 0x007c, 0x601c,
+ 0xa086, 0x0006, 0x00c0, 0x60e0, 0x1078, 0xa1ca, 0x0078, 0x60c9,
+ 0x601c, 0xa086, 0x0002, 0x00c0, 0x60c9, 0x6004, 0xa086, 0x0085,
+ 0x0040, 0x60c0, 0x0078, 0x60c9, 0x007e, 0x067e, 0x0c7e, 0x0d7e,
+ 0x7818, 0xa065, 0x0040, 0x615a, 0x6054, 0x007e, 0x6057, 0x0000,
+ 0x605b, 0x0000, 0x6000, 0xc0d4, 0xc0dc, 0x6002, 0x1078, 0x44d3,
+ 0x0040, 0x6157, 0x7e24, 0x86ff, 0x0040, 0x6149, 0xa680, 0x0004,
+ 0x2004, 0xad06, 0x00c0, 0x6149, 0x0d7e, 0x2069, 0x0100, 0x68c0,
+ 0xa005, 0x0040, 0x6140, 0x1078, 0x5acb, 0x1078, 0x6e0f, 0x68c3,
+ 0x0000, 0x1078, 0x7378, 0x7827, 0x0000, 0x037e, 0x2069, 0x0140,
+ 0x6b04, 0xa384, 0x1000, 0x0040, 0x6129, 0x6803, 0x0100, 0x6803,
+ 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0040, 0x6131, 0x6827,
+ 0x0001, 0x037f, 0x0d7f, 0x0c7e, 0x603c, 0xa005, 0x0040, 0x613a,
+ 0x8001, 0x603e, 0x2660, 0x1078, 0x8ec6, 0x0c7f, 0x0078, 0x6149,
+ 0x0d7f, 0x0c7e, 0x2660, 0x6003, 0x0009, 0x630a, 0x0c7f, 0x0078,
+ 0x60fe, 0x8dff, 0x0040, 0x6153, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x1078, 0x4a73, 0x1078, 0x7233, 0x0078, 0x60fe, 0x007f,
+ 0x0078, 0x60f1, 0x781e, 0x781a, 0x0d7f, 0x0c7f, 0x067f, 0x007f,
+ 0x007c, 0x0e7e, 0x0d7e, 0x067e, 0x6000, 0xd0dc, 0x0040, 0x6181,
+ 0x604c, 0xa06d, 0x0040, 0x6181, 0x6848, 0xa606, 0x00c0, 0x6181,
+ 0x2071, 0xa8b1, 0x7024, 0xa035, 0x0040, 0x6181, 0xa080, 0x0004,
+ 0x2004, 0xad06, 0x00c0, 0x6181, 0x6000, 0xc0dc, 0x6002, 0x1078,
+ 0x6185, 0x067f, 0x0d7f, 0x0e7f, 0x007c, 0x0f7e, 0x2079, 0x0100,
+ 0x78c0, 0xa005, 0x00c0, 0x6194, 0x0c7e, 0x2660, 0x6003, 0x0009,
+ 0x630a, 0x0c7f, 0x0078, 0x61cb, 0x1078, 0x6e0f, 0x78c3, 0x0000,
+ 0x1078, 0x7378, 0x7027, 0x0000, 0x037e, 0x2079, 0x0140, 0x7b04,
+ 0xa384, 0x1000, 0x0040, 0x61a8, 0x7803, 0x0100, 0x7803, 0x0000,
+ 0x2079, 0x0100, 0x7824, 0xd084, 0x0040, 0x61b0, 0x7827, 0x0001,
+ 0x1078, 0x7378, 0x037f, 0x1078, 0x44d3, 0x0c7e, 0x603c, 0xa005,
+ 0x0040, 0x61bc, 0x8001, 0x603e, 0x2660, 0x1078, 0x772d, 0x0c7f,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x8f7d, 0x1078,
+ 0x4a73, 0x1078, 0x7233, 0x0f7f, 0x007c, 0x0e7e, 0x0c7e, 0x2071,
+ 0xa8b1, 0x7004, 0xa084, 0x0007, 0x0079, 0x61d6, 0x61e0, 0x61e3,
+ 0x61fc, 0x6218, 0x6262, 0x61e0, 0x61e0, 0x61de, 0x1078, 0x1332,
+ 0x0c7f, 0x0e7f, 0x007c, 0x7024, 0xa065, 0x0040, 0x61f1, 0x7020,
+ 0x8001, 0x7022, 0x600c, 0xa015, 0x0040, 0x61f8, 0x7216, 0x600f,
+ 0x0000, 0x7007, 0x0000, 0x7027, 0x0000, 0x0c7f, 0x0e7f, 0x007c,
+ 0x7216, 0x7212, 0x0078, 0x61f1, 0x6018, 0x2060, 0x1078, 0x44d3,
+ 0x6000, 0xc0dc, 0x6002, 0x7020, 0x8001, 0x7022, 0x0040, 0x620d,
+ 0x6054, 0xa015, 0x0040, 0x6214, 0x721e, 0x7007, 0x0000, 0x7027,
+ 0x0000, 0x0c7f, 0x0e7f, 0x007c, 0x7218, 0x721e, 0x0078, 0x620d,
+ 0x7024, 0xa065, 0x0040, 0x625f, 0x700c, 0xac06, 0x00c0, 0x622f,
+ 0x1078, 0x7233, 0x600c, 0xa015, 0x0040, 0x622b, 0x720e, 0x600f,
+ 0x0000, 0x0078, 0x625d, 0x720e, 0x720a, 0x0078, 0x625d, 0x7014,
+ 0xac06, 0x00c0, 0x6242, 0x1078, 0x7233, 0x600c, 0xa015, 0x0040,
+ 0x623e, 0x7216, 0x600f, 0x0000, 0x0078, 0x625d, 0x7216, 0x7212,
+ 0x0078, 0x625d, 0x601c, 0xa086, 0x0003, 0x00c0, 0x625d, 0x6018,
+ 0x2060, 0x1078, 0x44d3, 0x6000, 0xc0dc, 0x6002, 0x1078, 0x7233,
+ 0x701c, 0xa065, 0x0040, 0x625d, 0x6054, 0xa015, 0x0040, 0x625b,
+ 0x721e, 0x0078, 0x625d, 0x7218, 0x721e, 0x7027, 0x0000, 0x0c7f,
+ 0x0e7f, 0x007c, 0x7024, 0xa065, 0x0040, 0x626f, 0x1078, 0x7233,
+ 0x600c, 0xa015, 0x0040, 0x6276, 0x720e, 0x600f, 0x0000, 0x1078,
+ 0x7378, 0x7027, 0x0000, 0x0c7f, 0x0e7f, 0x007c, 0x720e, 0x720a,
+ 0x0078, 0x626f, 0x0d7e, 0x2069, 0xa8b1, 0x6830, 0xa084, 0x0003,
+ 0x0079, 0x6282, 0x6288, 0x628a, 0x62b4, 0x6288, 0x1078, 0x1332,
+ 0x0d7f, 0x007c, 0x0c7e, 0x6840, 0xa086, 0x0001, 0x0040, 0x62aa,
+ 0x683c, 0xa065, 0x0040, 0x629b, 0x600c, 0xa015, 0x0040, 0x62a6,
+ 0x6a3a, 0x600f, 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x2011,
+ 0xa8d0, 0x2013, 0x0000, 0x0c7f, 0x0d7f, 0x007c, 0x683a, 0x6836,
+ 0x0078, 0x629b, 0x6843, 0x0000, 0x6838, 0xa065, 0x0040, 0x629b,
+ 0x6003, 0x0003, 0x0078, 0x629b, 0x0c7e, 0x6843, 0x0000, 0x6847,
+ 0x0000, 0x684b, 0x0000, 0x683c, 0xa065, 0x0040, 0x62ce, 0x600c,
+ 0xa015, 0x0040, 0x62ca, 0x6a3a, 0x600f, 0x0000, 0x683f, 0x0000,
+ 0x0078, 0x62ce, 0x683f, 0x0000, 0x683a, 0x6836, 0x0c7f, 0x0d7f,
+ 0x007c, 0x0d7e, 0x2069, 0xa8b1, 0x6804, 0xa084, 0x0007, 0x0079,
+ 0x62d9, 0x62e3, 0x638a, 0x638a, 0x638a, 0x638a, 0x638c, 0x638a,
+ 0x62e1, 0x1078, 0x1332, 0x6820, 0xa005, 0x00c0, 0x62e9, 0x0d7f,
+ 0x007c, 0x0c7e, 0x680c, 0xa065, 0x0040, 0x62f8, 0x6807, 0x0004,
+ 0x6826, 0x682b, 0x0000, 0x1078, 0x63d4, 0x0c7f, 0x0d7f, 0x007c,
+ 0x6814, 0xa065, 0x0040, 0x6306, 0x6807, 0x0001, 0x6826, 0x682b,
+ 0x0000, 0x1078, 0x63d4, 0x0c7f, 0x0d7f, 0x007c, 0x0e7e, 0x037e,
+ 0x6a1c, 0xa2f5, 0x0000, 0x0040, 0x6385, 0x704c, 0xa00d, 0x0040,
+ 0x6315, 0x7088, 0xa005, 0x0040, 0x632d, 0x7054, 0xa075, 0x0040,
+ 0x631e, 0xa20e, 0x0040, 0x6385, 0x0078, 0x6323, 0x6818, 0xa20e,
+ 0x0040, 0x6385, 0x2070, 0x704c, 0xa00d, 0x0040, 0x6315, 0x7088,
+ 0xa005, 0x00c0, 0x6315, 0x2e00, 0x681e, 0x733c, 0x7038, 0xa302,
+ 0x00c8, 0x6315, 0x1078, 0x76fc, 0x0040, 0x6385, 0x8318, 0x733e,
+ 0x6112, 0x2e10, 0x621a, 0xa180, 0x0014, 0x2004, 0xa084, 0x00ff,
+ 0x6032, 0xa180, 0x0014, 0x2003, 0x0000, 0xa180, 0x0015, 0x2004,
+ 0xa08a, 0x199a, 0x0048, 0x634e, 0x2001, 0x1999, 0x8003, 0x801b,
+ 0x831b, 0xa318, 0x6316, 0x037f, 0x0f7e, 0x2c78, 0x71a0, 0xd1bc,
+ 0x0040, 0x6367, 0x7100, 0xd1f4, 0x0040, 0x6363, 0x7114, 0xa18c,
+ 0x00ff, 0x0078, 0x636c, 0x2009, 0x0000, 0x0078, 0x636c, 0xa1e0,
+ 0x29c0, 0x2c0c, 0xa18c, 0x00ff, 0x2061, 0x0100, 0x619a, 0x1078,
+ 0x6965, 0x7300, 0xc3dd, 0x7302, 0x6807, 0x0002, 0x2f18, 0x6b26,
+ 0x682b, 0x0000, 0x781f, 0x0003, 0x7803, 0x0001, 0x7807, 0x0040,
+ 0x0f7f, 0x0e7f, 0x0c7f, 0x0d7f, 0x007c, 0x037f, 0x0e7f, 0x0c7f,
+ 0x0078, 0x6383, 0x0d7f, 0x007c, 0x0c7e, 0x680c, 0xa065, 0x0040,
+ 0x6398, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000, 0x1078, 0x63d4,
+ 0x0c7f, 0x0d7f, 0x007c, 0x0f7e, 0x0d7e, 0x2069, 0xa8b1, 0x6830,
+ 0xa086, 0x0000, 0x00c0, 0x63bb, 0x6838, 0xa07d, 0x0040, 0x63bb,
+ 0x6833, 0x0001, 0x683e, 0x6847, 0x0000, 0x684b, 0x0000, 0x127e,
+ 0x0f7e, 0x2091, 0x2200, 0x027f, 0x1078, 0x1d6d, 0x00c0, 0x63be,
+ 0x127f, 0x1078, 0x6cb3, 0x0d7f, 0x0f7f, 0x007c, 0x127f, 0x6843,
+ 0x0000, 0x7803, 0x0002, 0x780c, 0xa015, 0x0040, 0x63d0, 0x6a3a,
+ 0x780f, 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x0078, 0x63bb,
+ 0x683a, 0x6836, 0x0078, 0x63ca, 0x601c, 0xa084, 0x000f, 0x1079,
+ 0x63da, 0x007c, 0x63e3, 0x63e8, 0x6809, 0x6922, 0x63e8, 0x6809,
+ 0x6922, 0x63e3, 0x63e8, 0x1078, 0x61cd, 0x1078, 0x62d1, 0x007c,
+ 0x157e, 0x137e, 0x147e, 0x0c7e, 0x0f7e, 0x6004, 0xa08a, 0x0044,
+ 0x10c8, 0x1332, 0x6118, 0x2178, 0x79a0, 0xd1bc, 0x0040, 0x6405,
+ 0x7900, 0xd1f4, 0x0040, 0x6401, 0x7914, 0xa18c, 0x00ff, 0x0078,
+ 0x640a, 0x2009, 0x0000, 0x0078, 0x640a, 0xa1f8, 0x29c0, 0x2f0c,
+ 0xa18c, 0x00ff, 0x2c78, 0x2061, 0x0100, 0x619a, 0xa08a, 0x0040,
+ 0x00c8, 0x645c, 0x1079, 0x641a, 0x0f7f, 0x0c7f, 0x147f, 0x137f,
+ 0x157f, 0x007c, 0x64c2, 0x650a, 0x6532, 0x65cd, 0x65fd, 0x6605,
+ 0x662c, 0x663d, 0x664e, 0x6656, 0x666e, 0x6656, 0x66d9, 0x663d,
+ 0x66fa, 0x6702, 0x664e, 0x6702, 0x6713, 0x645a, 0x645a, 0x645a,
+ 0x645a, 0x645a, 0x645a, 0x645a, 0x645a, 0x645a, 0x645a, 0x645a,
+ 0x6eef, 0x6f14, 0x6f29, 0x6f4c, 0x6f6d, 0x662c, 0x645a, 0x662c,
+ 0x6656, 0x645a, 0x6532, 0x65cd, 0x645a, 0x749c, 0x6656, 0x645a,
+ 0x74bc, 0x6656, 0x645a, 0x645a, 0x64bd, 0x646b, 0x645a, 0x74e1,
+ 0x7558, 0x7640, 0x645a, 0x7651, 0x6626, 0x766d, 0x645a, 0x6f82,
+ 0x645a, 0x645a, 0x1078, 0x1332, 0x2100, 0x1079, 0x6465, 0x0f7f,
+ 0x0c7f, 0x147f, 0x137f, 0x157f, 0x007c, 0x6469, 0x6469, 0x6469,
+ 0x649f, 0x1078, 0x1332, 0x0d7e, 0x20a1, 0x020b, 0x1078, 0x6731,
+ 0x7810, 0x2068, 0x20a3, 0x2414, 0x20a3, 0x0018, 0x20a3, 0x0800,
+ 0x683c, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x6850, 0x20a2, 0x6854, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0018, 0x1078, 0x6dfb, 0x0d7f, 0x007c,
+ 0x0d7e, 0x7818, 0x2068, 0x68a0, 0xa082, 0x007e, 0x0048, 0x649c,
+ 0xa085, 0x0001, 0x0d7f, 0x007c, 0xa006, 0x0078, 0x649a, 0x0d7e,
+ 0x20a1, 0x020b, 0x1078, 0x6731, 0x20a3, 0x0500, 0x20a3, 0x0000,
+ 0x7810, 0xa0e8, 0x000f, 0x6808, 0x20a2, 0x680c, 0x20a2, 0x6810,
+ 0x20a2, 0x6814, 0x20a2, 0x6818, 0x20a2, 0x681c, 0x20a2, 0x60c3,
+ 0x0010, 0x1078, 0x6dfb, 0x0d7f, 0x007c, 0x6030, 0x609a, 0x1078,
+ 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078, 0x6731, 0x20a3, 0x5200,
+ 0x20a3, 0x0000, 0x0d7e, 0x2069, 0xa652, 0x6804, 0xd084, 0x0040,
+ 0x64dc, 0x6828, 0x20a3, 0x0000, 0x017e, 0x1078, 0x2564, 0x21a2,
+ 0x017f, 0x0d7f, 0x0078, 0x64e1, 0x0d7f, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a9, 0x0004, 0x2099, 0xa605, 0x53a6, 0x20a9, 0x0004,
+ 0x2099, 0xa601, 0x53a6, 0x7818, 0xa080, 0x0028, 0x2004, 0xa082,
+ 0x007f, 0x0048, 0x64fb, 0x2001, 0xa61b, 0x20a6, 0x2001, 0xa61c,
+ 0x20a6, 0x0078, 0x6501, 0x20a3, 0x0000, 0x6030, 0xa084, 0x00ff,
+ 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c, 0x1078,
+ 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078, 0x6731, 0x20a3, 0x0500,
+ 0x20a3, 0x0000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa082, 0x007f,
+ 0x0048, 0x6522, 0x2001, 0xa61b, 0x20a6, 0x2001, 0xa61c, 0x20a6,
+ 0x0078, 0x6528, 0x20a3, 0x0000, 0x6030, 0xa084, 0x00ff, 0x20a2,
+ 0x20a9, 0x0004, 0x2099, 0xa605, 0x53a6, 0x60c3, 0x0010, 0x1078,
+ 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078, 0x6731, 0x0c7e, 0x7818,
+ 0x2060, 0x2001, 0x0000, 0x1078, 0x4972, 0x0c7f, 0x7818, 0xa080,
+ 0x0028, 0x2004, 0xa086, 0x007e, 0x00c0, 0x654d, 0x20a3, 0x0400,
+ 0x620c, 0xc2b4, 0x620e, 0x0078, 0x654f, 0x20a3, 0x0300, 0x20a3,
+ 0x0000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa086, 0x007e, 0x00c0,
+ 0x659c, 0x2099, 0xa88d, 0x33a6, 0x9398, 0x33a6, 0x9398, 0x3304,
+ 0xa084, 0x3fff, 0x20a2, 0x9398, 0x33a6, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a9, 0x0004, 0x2099,
+ 0xa605, 0x53a6, 0x20a9, 0x0004, 0x2099, 0xa601, 0x53a6, 0x20a9,
+ 0x0010, 0x20a3, 0x0000, 0x00f0, 0x6579, 0x2099, 0xa895, 0x3304,
+ 0xc0dd, 0x20a2, 0x2001, 0xa672, 0x2004, 0xd0e4, 0x0040, 0x6594,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x9398, 0x9398, 0x9398, 0x33a6,
+ 0x20a9, 0x0004, 0x0078, 0x6596, 0x20a9, 0x0007, 0x20a3, 0x0000,
+ 0x00f0, 0x6596, 0x0078, 0x65bc, 0x2099, 0xa88d, 0x20a9, 0x0008,
+ 0x53a6, 0x20a9, 0x0004, 0x2099, 0xa605, 0x53a6, 0x20a9, 0x0004,
+ 0x2099, 0xa601, 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x00f0,
+ 0x65ad, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x00f0, 0x65b3, 0x2099,
+ 0xa895, 0x20a9, 0x0008, 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000,
+ 0x00f0, 0x65be, 0x20a9, 0x000a, 0x20a3, 0x0000, 0x00f0, 0x65c4,
+ 0x60c3, 0x0074, 0x1078, 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078,
+ 0x6731, 0x20a3, 0x2010, 0x20a3, 0x0014, 0x20a3, 0x0800, 0x20a3,
+ 0x2000, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x0f7e,
+ 0x2079, 0xa652, 0x7904, 0x0f7f, 0xd1ac, 0x00c0, 0x65e9, 0xa085,
+ 0x0020, 0xd1a4, 0x0040, 0x65ee, 0xa085, 0x0010, 0xa085, 0x0002,
+ 0x0d7e, 0x0078, 0x66b7, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0014, 0x1078, 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078,
+ 0x6731, 0x20a3, 0x5000, 0x0078, 0x654f, 0x20a1, 0x020b, 0x1078,
+ 0x6731, 0x20a3, 0x2110, 0x20a3, 0x0014, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x0014, 0x1078, 0x6dfb, 0x007c, 0x20a1, 0x020b,
+ 0x1078, 0x67b9, 0x0078, 0x6630, 0x20a1, 0x020b, 0x1078, 0x67c2,
+ 0x20a3, 0x0200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0004, 0x1078, 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078,
+ 0x67c2, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0003, 0x20a3,
+ 0x2a00, 0x60c3, 0x0008, 0x1078, 0x6dfb, 0x007c, 0x20a1, 0x020b,
+ 0x1078, 0x67c2, 0x20a3, 0x0200, 0x0078, 0x654f, 0x20a1, 0x020b,
+ 0x1078, 0x67c2, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x7828, 0xa005,
+ 0x0040, 0x6665, 0x20a2, 0x0078, 0x6667, 0x20a3, 0x0003, 0x7810,
+ 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6dfb, 0x007c, 0x0d7e, 0x20a1,
+ 0x020b, 0x1078, 0x67c2, 0x20a3, 0x0210, 0x20a3, 0x0014, 0x20a3,
+ 0x0800, 0x7818, 0x2068, 0x6894, 0xa086, 0x0014, 0x00c0, 0x6694,
+ 0x6998, 0xa184, 0xc000, 0x00c0, 0x6690, 0xd1ec, 0x0040, 0x668c,
+ 0x20a3, 0x2100, 0x0078, 0x6696, 0x20a3, 0x0100, 0x0078, 0x6696,
+ 0x20a3, 0x0400, 0x0078, 0x6696, 0x20a3, 0x0700, 0xa006, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x0f7e, 0x2079, 0xa652, 0x7904,
+ 0x0f7f, 0xd1ac, 0x00c0, 0x66a6, 0xa085, 0x0020, 0xd1a4, 0x0040,
+ 0x66ab, 0xa085, 0x0010, 0x2009, 0xa674, 0x210c, 0xd184, 0x0040,
+ 0x66b5, 0x699c, 0xd18c, 0x0040, 0x66b7, 0xa085, 0x0002, 0x027e,
+ 0x2009, 0xa672, 0x210c, 0xd1e4, 0x0040, 0x66c5, 0xc0c5, 0xa094,
+ 0x0030, 0xa296, 0x0010, 0x0040, 0x66cf, 0xd1ec, 0x0040, 0x66cf,
+ 0xa094, 0x0030, 0xa296, 0x0010, 0x0040, 0x66cf, 0xc0bd, 0x027f,
+ 0x20a2, 0x20a2, 0x20a2, 0x60c3, 0x0014, 0x1078, 0x6dfb, 0x0d7f,
+ 0x007c, 0x20a1, 0x020b, 0x1078, 0x67c2, 0x20a3, 0x0210, 0x20a3,
+ 0x0014, 0x20a3, 0x0000, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078,
+ 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078, 0x67c2, 0x20a3, 0x0200,
+ 0x0078, 0x64c8, 0x20a1, 0x020b, 0x1078, 0x67c2, 0x20a3, 0x0100,
+ 0x20a3, 0x0000, 0x20a3, 0x0003, 0x20a3, 0x2a00, 0x60c3, 0x0008,
+ 0x1078, 0x6dfb, 0x007c, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x20a1,
+ 0x020b, 0x1078, 0x67c2, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3,
+ 0x000b, 0x20a3, 0x0000, 0x60c3, 0x0008, 0x1078, 0x6dfb, 0x007c,
+ 0x027e, 0x037e, 0x047e, 0x2019, 0x3200, 0x2021, 0x0800, 0x0078,
+ 0x6738, 0x027e, 0x037e, 0x047e, 0x2019, 0x2200, 0x2021, 0x0100,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2014,
+ 0xa286, 0x007e, 0x00c0, 0x674b, 0xa385, 0x00ff, 0x20a2, 0x20a3,
+ 0xfffe, 0x0078, 0x6780, 0xa286, 0x007f, 0x00c0, 0x6757, 0x0d7e,
+ 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffd, 0x0078, 0x676e, 0xd2bc,
+ 0x0040, 0x6776, 0xa286, 0x0080, 0x0d7e, 0x00c0, 0x6766, 0xa385,
+ 0x00ff, 0x20a2, 0x20a3, 0xfffc, 0x0078, 0x676e, 0xa2e8, 0xa735,
+ 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6784, 0x0d7e, 0xa2e8,
+ 0xa735, 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x0d7f,
+ 0x20a3, 0x0000, 0x6230, 0x22a2, 0xa485, 0x0029, 0x20a2, 0x047f,
+ 0x037f, 0x20a3, 0x0000, 0x1078, 0x6dea, 0x22a2, 0x20a3, 0x0000,
+ 0x2fa2, 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f,
+ 0x007c, 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x20a3, 0x02ff,
+ 0x2011, 0xfffc, 0x22a2, 0x0d7e, 0x2069, 0xa61b, 0x2da6, 0x8d68,
+ 0x2da6, 0x0d7f, 0x20a3, 0x2029, 0x20a3, 0x0000, 0x0078, 0x678b,
+ 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0xfc02, 0x20a3, 0x0000,
+ 0x007c, 0x027e, 0x037e, 0x047e, 0x2019, 0x3300, 0x2021, 0x0800,
+ 0x0078, 0x67c9, 0x027e, 0x037e, 0x047e, 0x2019, 0x2300, 0x2021,
+ 0x0100, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xa092, 0x007e, 0x0048, 0x67e6, 0x0d7e, 0xa0e8, 0xa735,
+ 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x67f4, 0x0d7e, 0xa0e8,
+ 0xa735, 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x0d7f,
+ 0x20a3, 0x0000, 0x6230, 0x22a2, 0xa485, 0x0098, 0x20a2, 0x20a3,
+ 0x0000, 0x047f, 0x037f, 0x1078, 0x6dea, 0x22a2, 0x20a3, 0x0000,
+ 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f,
+ 0x007c, 0x0c7e, 0x0f7e, 0x6004, 0xa08a, 0x0085, 0x1048, 0x1332,
+ 0xa08a, 0x008c, 0x10c8, 0x1332, 0x6118, 0x2178, 0x79a0, 0xd1bc,
+ 0x0040, 0x6827, 0x7900, 0xd1f4, 0x0040, 0x6823, 0x7914, 0xa18c,
+ 0x00ff, 0x0078, 0x682c, 0x2009, 0x0000, 0x0078, 0x682c, 0xa1f8,
+ 0x29c0, 0x2f0c, 0xa18c, 0x00ff, 0x2c78, 0x2061, 0x0100, 0x619a,
+ 0xa082, 0x0085, 0x1079, 0x6837, 0x0f7f, 0x0c7f, 0x007c, 0x6840,
+ 0x684b, 0x6866, 0x683e, 0x683e, 0x683e, 0x6840, 0x1078, 0x1332,
+ 0x147e, 0x20a1, 0x020b, 0x1078, 0x6879, 0x60c3, 0x0000, 0x1078,
+ 0x6dfb, 0x147f, 0x007c, 0x147e, 0x20a1, 0x020b, 0x1078, 0x68ad,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808, 0x20a2, 0x7810, 0x20a2,
+ 0x20a3, 0x0000, 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x000c, 0x1078, 0x6dfb, 0x147f, 0x007c, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x68ee, 0x20a3, 0x0003, 0x20a3, 0x0300, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0004, 0x1078, 0x6dfb, 0x147f,
+ 0x007c, 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080,
+ 0x0028, 0x2004, 0xa092, 0x007e, 0x0048, 0x6898, 0x0d7e, 0xa0e8,
+ 0xa735, 0x2d6c, 0x6810, 0xa085, 0x8100, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xa61b, 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x68a7,
+ 0x0d7e, 0xa0e8, 0xa735, 0x2d6c, 0x6810, 0xa085, 0x8100, 0x20a2,
+ 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x20a3,
+ 0x0009, 0x20a3, 0x0000, 0x0078, 0x678b, 0x027e, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa092, 0x007e,
+ 0x0048, 0x68cc, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c, 0x6810, 0xa085,
+ 0x8400, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b, 0x2da6, 0x8d68,
+ 0x2da6, 0x0d7f, 0x0078, 0x68db, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c,
+ 0x6810, 0xa085, 0x8400, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3,
+ 0x0000, 0x6230, 0x22a2, 0x20a3, 0x0099, 0x20a3, 0x0000, 0x1078,
+ 0x6dea, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x7a10, 0x22a2,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f, 0x007c, 0x027e, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa092,
+ 0x007e, 0x0048, 0x690d, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c, 0x6810,
+ 0xa085, 0x8500, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b, 0x2da6,
+ 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x691c, 0x0d7e, 0xa0e8, 0xa735,
+ 0x2d6c, 0x6810, 0xa085, 0x8500, 0x20a2, 0x6814, 0x20a2, 0x0d7f,
+ 0x20a3, 0x0000, 0x6230, 0x22a2, 0x20a3, 0x0099, 0x20a3, 0x0000,
+ 0x0078, 0x68df, 0x0c7e, 0x0f7e, 0x2c78, 0x7804, 0xa08a, 0x0040,
+ 0x1048, 0x1332, 0xa08a, 0x0053, 0x10c8, 0x1332, 0x7918, 0x2160,
+ 0x61a0, 0xd1bc, 0x0040, 0x6941, 0x6100, 0xd1f4, 0x0040, 0x693d,
+ 0x6114, 0xa18c, 0x00ff, 0x0078, 0x6946, 0x2009, 0x0000, 0x0078,
+ 0x6946, 0xa1e0, 0x29c0, 0x2c0c, 0xa18c, 0x00ff, 0x2061, 0x0100,
+ 0x619a, 0xa082, 0x0040, 0x1079, 0x6950, 0x0f7f, 0x0c7f, 0x007c,
+ 0x6965, 0x6a73, 0x6a14, 0x6c27, 0x6963, 0x6963, 0x6963, 0x6963,
+ 0x6963, 0x6963, 0x6963, 0x714c, 0x715d, 0x716e, 0x717f, 0x6963,
+ 0x767e, 0x6963, 0x713b, 0x1078, 0x1332, 0x0d7e, 0x157e, 0x147e,
+ 0x780b, 0xffff, 0x20a1, 0x020b, 0x1078, 0x69d0, 0x7910, 0x2168,
+ 0x6948, 0x7922, 0x21a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x694c,
+ 0xa184, 0x000f, 0x00c0, 0x6980, 0x2001, 0x0005, 0x0078, 0x698a,
+ 0xd184, 0x0040, 0x6987, 0x2001, 0x0004, 0x0078, 0x698a, 0xa084,
+ 0x0006, 0x8004, 0x017e, 0x2008, 0x7830, 0xa084, 0x00ff, 0x8007,
+ 0xa105, 0x017f, 0x20a2, 0xd1ac, 0x0040, 0x699a, 0x20a3, 0x0002,
+ 0x0078, 0x69a6, 0xd1b4, 0x0040, 0x69a1, 0x20a3, 0x0001, 0x0078,
+ 0x69a6, 0x20a3, 0x0000, 0x2230, 0x0078, 0x69a8, 0x6a80, 0x6e7c,
+ 0x20a9, 0x0008, 0xad80, 0x0017, 0x200c, 0x810f, 0x21a2, 0x8000,
+ 0x00f0, 0x69ac, 0x22a2, 0x26a2, 0x60c3, 0x0020, 0x20e1, 0x9080,
+ 0x6014, 0xa084, 0x0004, 0xa085, 0x0009, 0x6016, 0x2001, 0xa8cd,
+ 0x2003, 0x07d0, 0x2001, 0xa8cc, 0x2003, 0x0009, 0x2001, 0xa8d2,
+ 0x2003, 0x0002, 0x1078, 0x158c, 0x147f, 0x157f, 0x0d7f, 0x007c,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7a18, 0xa280, 0x0023, 0x2014,
+ 0x8210, 0xa294, 0x00ff, 0x2202, 0x8217, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xd0bc, 0x0040, 0x69f6, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c,
+ 0x6810, 0xa085, 0x0600, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6a05, 0x0d7e, 0xa0e8,
+ 0xa735, 0x2d6c, 0x6810, 0xa085, 0x0600, 0x20a2, 0x6814, 0x20a2,
+ 0x0d7f, 0x20a3, 0x0000, 0x6130, 0x21a2, 0x20a3, 0x0829, 0x20a3,
+ 0x0000, 0x22a2, 0x20a3, 0x0000, 0x2fa2, 0x20a3, 0xffff, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x007c, 0x0d7e, 0x157e, 0x137e, 0x147e,
+ 0x20a1, 0x020b, 0x1078, 0x6a34, 0x7810, 0x2068, 0x6860, 0x20a2,
+ 0x685c, 0x20a2, 0x6880, 0x20a2, 0x687c, 0x20a2, 0xa006, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a2, 0x60c3, 0x000c, 0x1078, 0x6dfb, 0x147f,
+ 0x137f, 0x157f, 0x0d7f, 0x007c, 0x027e, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0xd0bc, 0x0040, 0x6a52,
+ 0x0d7e, 0xa0e8, 0xa735, 0x2d6c, 0x6810, 0xa085, 0x0500, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xa61b, 0x2da6, 0x8d68, 0x2da6, 0x0d7f,
+ 0x0078, 0x6a61, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c, 0x6810, 0xa085,
+ 0x0500, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230,
+ 0x22a2, 0x20a3, 0x0889, 0x20a3, 0x0000, 0x1078, 0x6dea, 0x22a2,
+ 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x027f, 0x007c, 0x0d7e, 0x157e, 0x137e, 0x147e, 0x7810,
+ 0xa0ec, 0xf000, 0x0040, 0x6a8b, 0xa06d, 0x1078, 0x495f, 0x0040,
+ 0x6a8b, 0x684c, 0xa084, 0x2020, 0xa086, 0x2020, 0x00c0, 0x6a8b,
+ 0x7824, 0xc0cd, 0x7826, 0x20a1, 0x020b, 0x1078, 0x6be0, 0xa016,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x7810, 0xa084, 0xf000,
+ 0x00c0, 0x6aa2, 0x7810, 0xa084, 0x0700, 0x8007, 0x1079, 0x6aaa,
+ 0x0078, 0x6aa5, 0xa006, 0x1079, 0x6aaa, 0x147f, 0x137f, 0x157f,
+ 0x0d7f, 0x007c, 0x6ab4, 0x6b4c, 0x6b57, 0x6b81, 0x6b95, 0x6bb1,
+ 0x6bbc, 0x6ab2, 0x1078, 0x1332, 0x017e, 0x037e, 0x694c, 0xa18c,
+ 0x0003, 0x0040, 0x6abf, 0xa186, 0x0003, 0x00c0, 0x6ace, 0x6b78,
+ 0x7824, 0xd0cc, 0x0040, 0x6ac5, 0xc3e5, 0x23a2, 0x6868, 0x20a2,
+ 0x6864, 0x20a2, 0x037f, 0x017f, 0x0078, 0x6b8c, 0xa186, 0x0001,
+ 0x10c0, 0x1332, 0x6b78, 0x7824, 0xd0cc, 0x0040, 0x6ad8, 0xc3e5,
+ 0x23a2, 0x6868, 0x20a2, 0x6864, 0x20a2, 0x22a2, 0x6874, 0x20a2,
+ 0x22a2, 0x687c, 0x20a2, 0x2009, 0x0018, 0xa384, 0x0300, 0x0040,
+ 0x6b46, 0xd3c4, 0x0040, 0x6aee, 0x687c, 0xa108, 0xd3cc, 0x0040,
+ 0x6af3, 0x6874, 0xa108, 0x157e, 0x20a9, 0x000d, 0xad80, 0x0020,
+ 0x201c, 0x831f, 0x23a2, 0x8000, 0x00f0, 0x6af8, 0x157f, 0x22a2,
+ 0x22a2, 0x22a2, 0xa184, 0x0003, 0x0040, 0x6b46, 0x20a1, 0x020b,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x007e, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xd0bc, 0x0040, 0x6b26, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c,
+ 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6b35, 0x0d7e, 0xa0e8,
+ 0xa735, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2,
+ 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x007f, 0x7b24, 0xd3cc,
+ 0x0040, 0x6b3e, 0x20a3, 0x0889, 0x0078, 0x6b40, 0x20a3, 0x0898,
+ 0x20a2, 0x1078, 0x6dea, 0x22a2, 0x20a3, 0x0000, 0x61c2, 0x037f,
+ 0x017f, 0x1078, 0x6dfb, 0x007c, 0x2011, 0x0008, 0x7824, 0xd0cc,
+ 0x0040, 0x6b53, 0xc2e5, 0x22a2, 0xa016, 0x0078, 0x6b8a, 0x2011,
+ 0x0302, 0x7824, 0xd0cc, 0x0040, 0x6b5e, 0xc2e5, 0x22a2, 0xa016,
+ 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x0012, 0x22a2, 0x20a3, 0x0008,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x7000, 0x20a3, 0x0500,
+ 0x22a2, 0x20a3, 0x000a, 0x22a2, 0x22a2, 0x20a3, 0x2500, 0x22a2,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0032, 0x1078, 0x6dfb,
+ 0x007c, 0x2011, 0x0028, 0x7824, 0xd0cc, 0x0040, 0x6b88, 0xc2e5,
+ 0x22a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2,
+ 0x60c3, 0x0018, 0x1078, 0x6dfb, 0x007c, 0x2011, 0x0100, 0x7824,
+ 0xd0cc, 0x0040, 0x6b9c, 0xc2e5, 0x22a2, 0xa016, 0x22a2, 0x22a2,
+ 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x0008, 0x22a2, 0x7834, 0xa084,
+ 0x00ff, 0x20a2, 0x22a2, 0x22a2, 0x60c3, 0x0020, 0x1078, 0x6dfb,
+ 0x007c, 0x2011, 0x0008, 0x7824, 0xd0cc, 0x0040, 0x6bb8, 0xc2e5,
+ 0x22a2, 0xa016, 0x0078, 0x6b8a, 0x037e, 0x7b10, 0xa384, 0xff00,
+ 0x7812, 0xa384, 0x00ff, 0x8001, 0x00c0, 0x6bcf, 0x7824, 0xd0cc,
+ 0x0040, 0x6bcb, 0xc2e5, 0x22a2, 0x037f, 0x0078, 0x6b8a, 0x047e,
+ 0x2021, 0x0800, 0x007e, 0x7824, 0xd0cc, 0x007f, 0x0040, 0x6bd9,
+ 0xc4e5, 0x24a2, 0x047f, 0x22a2, 0x20a2, 0x037f, 0x0078, 0x6b8c,
+ 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xd0bc, 0x0040, 0x6bfe, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c,
+ 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6c0d, 0x0d7e, 0xa0e8,
+ 0xa735, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2,
+ 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x7824, 0xd0cc, 0x0040,
+ 0x6c15, 0x20a3, 0x0889, 0x0078, 0x6c17, 0x20a3, 0x0898, 0x20a3,
+ 0x0000, 0x1078, 0x6dea, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2,
+ 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f, 0x007c, 0x0d7e,
+ 0x157e, 0x137e, 0x147e, 0x017e, 0x037e, 0x7810, 0xa084, 0x0700,
+ 0x8007, 0x1079, 0x6c3a, 0x037f, 0x017f, 0x147f, 0x137f, 0x157f,
+ 0x0d7f, 0x007c, 0x6c42, 0x6c42, 0x6c44, 0x6c42, 0x6c42, 0x6c42,
+ 0x6c69, 0x6c42, 0x1078, 0x1332, 0x7910, 0xa18c, 0xf8ff, 0xa18d,
+ 0x0600, 0x7912, 0x20a1, 0x020b, 0x2009, 0x0003, 0x1078, 0x6c73,
+ 0x0d7e, 0x2069, 0xa652, 0x6804, 0xd0bc, 0x0040, 0x6c5e, 0x682c,
+ 0xa084, 0x00ff, 0x8007, 0x20a2, 0x0078, 0x6c60, 0x20a3, 0x3f00,
+ 0x0d7f, 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0001, 0x1078, 0x6dfb,
+ 0x007c, 0x20a1, 0x020b, 0x2009, 0x0003, 0x1078, 0x6c73, 0x20a3,
+ 0x7f00, 0x0078, 0x6c61, 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xd0bc, 0x0040, 0x6c91, 0x0d7e,
+ 0xa0e8, 0xa735, 0x2d6c, 0x6810, 0xa085, 0x0100, 0x20a2, 0x6814,
+ 0x20a2, 0x2069, 0xa61b, 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078,
+ 0x6ca0, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c, 0x6810, 0xa085, 0x0100,
+ 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2,
+ 0x20a3, 0x0888, 0xa18d, 0x0008, 0x21a2, 0x1078, 0x6dea, 0x22a2,
+ 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x027f, 0x007c, 0x0e7e, 0x0d7e, 0x0c7e, 0x057e, 0x047e,
+ 0x037e, 0x2061, 0x0100, 0x2071, 0xa600, 0x6130, 0x7818, 0x2068,
+ 0x68a0, 0x2028, 0xd0bc, 0x00c0, 0x6cca, 0x6910, 0x6a14, 0x6430,
+ 0x0078, 0x6cce, 0x6910, 0x6a14, 0x736c, 0x7470, 0x781c, 0xa086,
+ 0x0006, 0x0040, 0x6d2d, 0xd5bc, 0x0040, 0x6cde, 0xa185, 0x0100,
+ 0x6062, 0x6266, 0x636a, 0x646e, 0x0078, 0x6ce5, 0xa185, 0x0100,
+ 0x6062, 0x6266, 0x606b, 0x0000, 0x646e, 0x6073, 0x0809, 0x6077,
+ 0x0008, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e, 0x8007, 0x607a,
+ 0x607f, 0x0000, 0x2f00, 0x6082, 0x7808, 0x6086, 0x7810, 0x2070,
+ 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6, 0x7008, 0x60ca,
+ 0x686c, 0x60ce, 0x60ab, 0x0036, 0x60af, 0x95d5, 0x60d7, 0x0000,
+ 0xa582, 0x0080, 0x0048, 0x6d17, 0x6a00, 0xd2f4, 0x0040, 0x6d15,
+ 0x6a14, 0xa294, 0x00ff, 0x0078, 0x6d17, 0x2011, 0x0000, 0x629e,
+ 0x6017, 0x0016, 0x2009, 0x07d0, 0x60c4, 0xa084, 0xfff0, 0xa005,
+ 0x0040, 0x6d24, 0x2009, 0x1b58, 0x1078, 0x5ad0, 0x037f, 0x047f,
+ 0x057f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0x7810, 0x2070, 0x704c,
+ 0xa084, 0x0003, 0xa086, 0x0002, 0x0040, 0x6d85, 0xd5bc, 0x0040,
+ 0x6d41, 0xa185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x0078,
+ 0x6d48, 0xa185, 0x0100, 0x6062, 0x6266, 0x606b, 0x0000, 0x646e,
+ 0x6073, 0x0880, 0x6077, 0x0008, 0x688c, 0x8000, 0xa084, 0x00ff,
+ 0x688e, 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6086, 0x7808,
+ 0x6082, 0x7060, 0x608a, 0x705c, 0x608e, 0x7080, 0x60c6, 0x707c,
+ 0x60ca, 0x707c, 0x792c, 0xa108, 0x792e, 0x7080, 0x7928, 0xa109,
+ 0x792a, 0x686c, 0x60ce, 0x60ab, 0x0036, 0x60af, 0x95d5, 0x60d7,
+ 0x0000, 0xa582, 0x0080, 0x0048, 0x6d80, 0x6a00, 0xd2f4, 0x0040,
+ 0x6d7e, 0x6a14, 0xa294, 0x00ff, 0x0078, 0x6d80, 0x2011, 0x0000,
+ 0x629e, 0x6017, 0x0012, 0x0078, 0x6d1a, 0xd5bc, 0x0040, 0x6d90,
+ 0xa185, 0x0700, 0x6062, 0x6266, 0x636a, 0x646e, 0x0078, 0x6d97,
+ 0xa185, 0x0700, 0x6062, 0x6266, 0x606b, 0x0000, 0x646e, 0x1078,
+ 0x495f, 0x0040, 0x6dad, 0x0d7e, 0x7810, 0xa06d, 0x684c, 0x0d7f,
+ 0xa084, 0x2020, 0xa086, 0x2020, 0x00c0, 0x6dad, 0x7824, 0xc0cd,
+ 0x7826, 0x6073, 0x0889, 0x0078, 0x6daf, 0x6073, 0x0898, 0x6077,
+ 0x0000, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e, 0x8007, 0x607a,
+ 0x607f, 0x0000, 0x2f00, 0x6086, 0x7808, 0x6082, 0x7014, 0x608a,
+ 0x7010, 0x608e, 0x700c, 0x60c6, 0x7008, 0x60ca, 0x686c, 0x60ce,
+ 0x60ab, 0x0036, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xa582, 0x0080,
+ 0x0048, 0x6ddd, 0x6a00, 0xd2f4, 0x0040, 0x6ddb, 0x6a14, 0xa294,
+ 0x00ff, 0x0078, 0x6ddd, 0x2011, 0x0000, 0x629e, 0x7824, 0xd0cc,
+ 0x0040, 0x6de6, 0x6017, 0x0016, 0x0078, 0x6d1a, 0x6017, 0x0012,
+ 0x0078, 0x6d1a, 0x7a18, 0xa280, 0x0023, 0x2014, 0x8210, 0xa294,
+ 0x00ff, 0x2202, 0x8217, 0x007c, 0x0d7e, 0x2069, 0xa8b1, 0x6843,
+ 0x0001, 0x0d7f, 0x007c, 0x20e1, 0x9080, 0x60a3, 0x0056, 0x60a7,
+ 0x9575, 0x1078, 0x6e06, 0x1078, 0x5ac0, 0x007c, 0x007e, 0x6014,
+ 0xa084, 0x0004, 0xa085, 0x0009, 0x6016, 0x007f, 0x007c, 0x007e,
+ 0x0c7e, 0x2061, 0x0100, 0x6014, 0xa084, 0x0004, 0xa085, 0x0008,
+ 0x6016, 0x0c7f, 0x007f, 0x007c, 0x0c7e, 0x0d7e, 0x017e, 0x027e,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x6904, 0xa194, 0x4000, 0x0040,
+ 0x6e59, 0x1078, 0x6e0f, 0x6803, 0x1000, 0x6803, 0x0000, 0x0c7e,
+ 0x2061, 0xa8b1, 0x6128, 0xa192, 0x00c8, 0x00c8, 0x6e44, 0x8108,
+ 0x612a, 0x6124, 0x0c7f, 0x81ff, 0x0040, 0x6e54, 0x1078, 0x5ac0,
+ 0x1078, 0x6e06, 0x0078, 0x6e54, 0x6124, 0xa1e5, 0x0000, 0x0040,
+ 0x6e51, 0x1078, 0xa5c4, 0x1078, 0x5acb, 0x2009, 0x0014, 0x1078,
+ 0x775c, 0x0c7f, 0x0078, 0x6e54, 0x027f, 0x017f, 0x0d7f, 0x0c7f,
+ 0x007c, 0x2001, 0xa8cd, 0x2004, 0xa005, 0x00c0, 0x6e54, 0x0c7e,
+ 0x2061, 0xa8b1, 0x6128, 0xa192, 0x0003, 0x00c8, 0x6e44, 0x8108,
+ 0x612a, 0x0c7f, 0x1078, 0x5ac0, 0x1078, 0x4224, 0x0078, 0x6e54,
+ 0x0c7e, 0x0d7e, 0x0e7e, 0x017e, 0x027e, 0x1078, 0x5ad8, 0x2071,
+ 0xa8b1, 0x713c, 0x81ff, 0x0040, 0x6e9a, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x6904, 0xa194, 0x4000, 0x0040, 0x6ea0, 0x6803, 0x1000,
+ 0x6803, 0x0000, 0x037e, 0x2019, 0x0001, 0x1078, 0x7058, 0x037f,
+ 0x713c, 0x2160, 0x1078, 0xa5c4, 0x2009, 0x004a, 0x1078, 0x775c,
+ 0x0078, 0x6e9a, 0x027f, 0x017f, 0x0e7f, 0x0d7f, 0x0c7f, 0x007c,
+ 0x0078, 0x6e8a, 0x0e7e, 0x2071, 0xa8b1, 0x7048, 0xd084, 0x0040,
+ 0x6ebc, 0x713c, 0x81ff, 0x0040, 0x6ebc, 0x2071, 0x0100, 0xa188,
+ 0x0007, 0x210c, 0xa18e, 0x0006, 0x00c0, 0x6eba, 0x7017, 0x0012,
+ 0x0078, 0x6ebc, 0x7017, 0x0016, 0x0e7f, 0x007c, 0x0e7e, 0x0d7e,
+ 0x0c7e, 0x067e, 0x057e, 0x047e, 0x007e, 0x127e, 0x2091, 0x8000,
+ 0x6018, 0x2068, 0x6ca0, 0x2071, 0xa8b1, 0x7018, 0x2068, 0x8dff,
+ 0x0040, 0x6ee6, 0x68a0, 0xa406, 0x0040, 0x6eda, 0x6854, 0x2068,
+ 0x0078, 0x6ecf, 0x6010, 0x2060, 0x643c, 0x6540, 0x6648, 0x2d60,
+ 0x1078, 0x4736, 0x0040, 0x6ee6, 0xa085, 0x0001, 0x127f, 0x007f,
+ 0x047f, 0x057f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0x20a1,
+ 0x020b, 0x1078, 0x6731, 0x20a3, 0x1200, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x781c, 0xa086, 0x0004, 0x00c0, 0x6f01, 0x6098, 0x0078,
+ 0x6f02, 0x6030, 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a9,
+ 0x0010, 0xa006, 0x20a2, 0x00f0, 0x6f0a, 0x20a2, 0x20a2, 0x60c3,
+ 0x002c, 0x1078, 0x6dfb, 0x007c, 0x157e, 0x147e, 0x20a1, 0x020b,
+ 0x1078, 0x6731, 0x20a3, 0x0f00, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x7808, 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6dfb, 0x147f, 0x157f,
+ 0x007c, 0x157e, 0x147e, 0x20a1, 0x020b, 0x1078, 0x67c2, 0x20a3,
+ 0x0200, 0x20a3, 0x0000, 0x20a9, 0x0006, 0x2011, 0xa640, 0x2019,
+ 0xa641, 0x23a6, 0x22a6, 0xa398, 0x0002, 0xa290, 0x0002, 0x00f0,
+ 0x6f39, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c, 0x1078,
+ 0x6dfb, 0x147f, 0x157f, 0x007c, 0x157e, 0x147e, 0x017e, 0x027e,
+ 0x20a1, 0x020b, 0x1078, 0x6799, 0x1078, 0x67b0, 0x7810, 0xa080,
+ 0x0000, 0x2004, 0xa080, 0x0015, 0x2098, 0x7808, 0xa088, 0x0002,
+ 0x21a8, 0x53a6, 0xa080, 0x0004, 0x8003, 0x60c2, 0x1078, 0x6dfb,
+ 0x027f, 0x017f, 0x147f, 0x157f, 0x007c, 0x157e, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x6731, 0x20a3, 0x6200, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x7808, 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6dfb, 0x147f,
+ 0x157f, 0x007c, 0x157e, 0x147e, 0x017e, 0x027e, 0x20a1, 0x020b,
+ 0x1078, 0x6731, 0x7810, 0xa080, 0x0000, 0x2004, 0xa080, 0x0017,
+ 0x2098, 0x7808, 0xa088, 0x0002, 0x21a8, 0x53a6, 0x8003, 0x60c2,
+ 0x1078, 0x6dfb, 0x027f, 0x017f, 0x147f, 0x157f, 0x007c, 0x0e7e,
+ 0x0c7e, 0x007e, 0x127e, 0x2091, 0x8000, 0x2071, 0xa8b1, 0x700c,
+ 0x2060, 0x8cff, 0x0040, 0x6fbb, 0x1078, 0x8f00, 0x00c0, 0x6fb2,
+ 0x1078, 0x7c83, 0x600c, 0x007e, 0x1078, 0x772d, 0x1078, 0x7233,
+ 0x0c7f, 0x0078, 0x6fa9, 0x700f, 0x0000, 0x700b, 0x0000, 0x127f,
+ 0x007f, 0x0c7f, 0x0e7f, 0x007c, 0x127e, 0x157e, 0x0f7e, 0x0e7e,
+ 0x0d7e, 0x0c7e, 0x027e, 0x017e, 0x007e, 0x2091, 0x8000, 0x2069,
+ 0x0100, 0x2079, 0x0140, 0x2071, 0xa8b1, 0x7024, 0x2060, 0x8cff,
+ 0x0040, 0x7014, 0x1078, 0x6e0f, 0x68c3, 0x0000, 0x1078, 0x5acb,
+ 0x2009, 0x0013, 0x1078, 0x775c, 0x20a9, 0x01f4, 0x6824, 0xd094,
+ 0x0040, 0x6ff7, 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x0040,
+ 0x7009, 0x7803, 0x1000, 0x7803, 0x0000, 0x0078, 0x7009, 0xd084,
+ 0x0040, 0x6ffe, 0x6827, 0x0001, 0x0078, 0x7000, 0x00f0, 0x6fe6,
+ 0x7804, 0xa084, 0x1000, 0x0040, 0x7009, 0x7803, 0x0100, 0x7803,
+ 0x0000, 0x6824, 0x007f, 0x017f, 0x027f, 0x0c7f, 0x0d7f, 0x0e7f,
+ 0x0f7f, 0x157f, 0x127f, 0x007c, 0x2001, 0xa600, 0x2004, 0xa096,
+ 0x0001, 0x0040, 0x704e, 0xa096, 0x0004, 0x0040, 0x704e, 0x1078,
+ 0x5acb, 0x6817, 0x0008, 0x68c3, 0x0000, 0x2011, 0x41dc, 0x1078,
+ 0x5a45, 0x20a9, 0x01f4, 0x6824, 0xd094, 0x0040, 0x703c, 0x6827,
+ 0x0004, 0x7804, 0xa084, 0x4000, 0x0040, 0x704e, 0x7803, 0x1000,
+ 0x7803, 0x0000, 0x0078, 0x704e, 0xd084, 0x0040, 0x7043, 0x6827,
+ 0x0001, 0x0078, 0x7045, 0x00f0, 0x702b, 0x7804, 0xa084, 0x1000,
+ 0x0040, 0x704e, 0x7803, 0x0100, 0x7803, 0x0000, 0x007f, 0x017f,
+ 0x027f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x157f, 0x127f, 0x007c,
+ 0x127e, 0x157e, 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x027e, 0x017e,
+ 0x007e, 0x2091, 0x8000, 0x2069, 0x0100, 0x2079, 0x0140, 0x2071,
+ 0xa8b1, 0x703c, 0x2060, 0x8cff, 0x0040, 0x70d6, 0x68af, 0x95f5,
+ 0x6817, 0x0010, 0x2009, 0x00fa, 0x8109, 0x00c0, 0x7074, 0x68c7,
+ 0x0000, 0x68cb, 0x0008, 0x1078, 0x5ad8, 0x1078, 0x1f7e, 0x047e,
+ 0x057e, 0x2009, 0x017f, 0x212c, 0x200b, 0x00a5, 0x2021, 0x0169,
+ 0x2404, 0xa084, 0x000f, 0xa086, 0x0004, 0x00c0, 0x70a5, 0x68c7,
+ 0x0000, 0x68cb, 0x0008, 0x0e7e, 0x0f7e, 0x2079, 0x0020, 0x2071,
+ 0xa908, 0x6814, 0xa084, 0x0004, 0xa085, 0x0012, 0x6816, 0x7803,
+ 0x0008, 0x7003, 0x0000, 0x0f7f, 0x0e7f, 0x250a, 0x057f, 0x047f,
+ 0xa39d, 0x0000, 0x00c0, 0x70b0, 0x2009, 0x0049, 0x1078, 0x775c,
+ 0x20a9, 0x03e8, 0x6824, 0xd094, 0x0040, 0x70c3, 0x6827, 0x0004,
+ 0x7804, 0xa084, 0x4000, 0x0040, 0x70d5, 0x7803, 0x1000, 0x7803,
+ 0x0000, 0x0078, 0x70d5, 0xd08c, 0x0040, 0x70ca, 0x6827, 0x0002,
+ 0x0078, 0x70cc, 0x00f0, 0x70b2, 0x7804, 0xa084, 0x1000, 0x0040,
+ 0x70d5, 0x7803, 0x0100, 0x7803, 0x0000, 0x6824, 0x007f, 0x017f,
+ 0x027f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x157f, 0x127f, 0x007c,
+ 0x0d7e, 0x127e, 0x2091, 0x8000, 0x2069, 0xa8b1, 0x6a06, 0x127f,
+ 0x0d7f, 0x007c, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x2069, 0xa8b1,
+ 0x6a32, 0x127f, 0x0d7f, 0x007c, 0x0f7e, 0x0e7e, 0x0c7e, 0x067e,
+ 0x007e, 0x127e, 0x2071, 0xa8b1, 0x7614, 0x2660, 0x2678, 0x2091,
+ 0x8000, 0x8cff, 0x0040, 0x7134, 0x601c, 0xa206, 0x00c0, 0x712f,
+ 0x7014, 0xac36, 0x00c0, 0x710e, 0x660c, 0x7616, 0x7010, 0xac36,
+ 0x00c0, 0x711c, 0x2c00, 0xaf36, 0x0040, 0x711a, 0x2f00, 0x7012,
+ 0x0078, 0x711c, 0x7013, 0x0000, 0x660c, 0x067e, 0x2c00, 0xaf06,
+ 0x0040, 0x7125, 0x7e0e, 0x0078, 0x7126, 0x2678, 0x600f, 0x0000,
+ 0x1078, 0x8ec6, 0x1078, 0x7233, 0x0c7f, 0x0078, 0x7101, 0x2c78,
+ 0x600c, 0x2060, 0x0078, 0x7101, 0x127f, 0x007f, 0x067f, 0x0c7f,
+ 0x0e7f, 0x0f7f, 0x007c, 0x157e, 0x147e, 0x20a1, 0x020b, 0x1078,
+ 0x69d0, 0x7810, 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2,
+ 0x20a3, 0x1000, 0x0078, 0x718e, 0x157e, 0x147e, 0x20a1, 0x020b,
+ 0x1078, 0x69d0, 0x7810, 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2,
+ 0x20a2, 0x20a3, 0x4000, 0x0078, 0x718e, 0x157e, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x69d0, 0x7810, 0x20a2, 0xa006, 0x20a2, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a3, 0x2000, 0x0078, 0x718e, 0x157e, 0x147e,
+ 0x20a1, 0x020b, 0x1078, 0x69d0, 0x7810, 0x20a2, 0xa006, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0400, 0x0078, 0x718e, 0x157e,
+ 0x147e, 0x20a1, 0x020b, 0x1078, 0x69d0, 0x7810, 0x20a2, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0200, 0x1078, 0x723e,
+ 0x60c3, 0x0020, 0x1078, 0x6dfb, 0x147f, 0x157f, 0x007c, 0x127e,
+ 0x0c7e, 0x2091, 0x8000, 0x2061, 0x0100, 0x6120, 0xd1b4, 0x00c0,
+ 0x71a6, 0xd1bc, 0x00c0, 0x71f0, 0x0078, 0x7230, 0x2009, 0x017f,
+ 0x200b, 0x00a1, 0x157e, 0x007e, 0x0d7e, 0x2069, 0x0140, 0x20a9,
+ 0x001e, 0x2009, 0x0169, 0x6804, 0xa084, 0x4000, 0x0040, 0x71e7,
+ 0x6020, 0xd0b4, 0x0040, 0x71e7, 0x6024, 0xd094, 0x00c0, 0x71e7,
+ 0x2104, 0xa084, 0x000f, 0xa086, 0x0004, 0x00c0, 0x71e7, 0x00f0,
+ 0x71b3, 0x027e, 0x6198, 0xa18c, 0x00ff, 0x8107, 0x6130, 0xa18c,
+ 0x00ff, 0xa10d, 0x6088, 0x628c, 0x618e, 0x608b, 0xbc91, 0x6043,
+ 0x0001, 0x6043, 0x0000, 0x608a, 0x628e, 0x6024, 0xd094, 0x00c0,
+ 0x71e6, 0x6a04, 0xa294, 0x4000, 0x00c0, 0x71dd, 0x027f, 0x0d7f,
+ 0x007f, 0x157f, 0x2009, 0x017f, 0x200b, 0x0000, 0x0078, 0x7230,
+ 0x2009, 0x017f, 0x200b, 0x00a1, 0x157e, 0x007e, 0x0d7e, 0x2069,
+ 0x0140, 0x20a9, 0x001e, 0x2009, 0x0169, 0x6804, 0xa084, 0x4000,
+ 0x0040, 0x7229, 0x6020, 0xd0bc, 0x0040, 0x7229, 0x2104, 0xa084,
+ 0x000f, 0xa086, 0x0004, 0x00c0, 0x7229, 0x00f0, 0x71fd, 0x027e,
+ 0x6164, 0xa18c, 0x00ff, 0x8107, 0x6130, 0xa18c, 0x00ff, 0xa10d,
+ 0x6088, 0x628c, 0x608b, 0xbc91, 0x618e, 0x6043, 0x0001, 0x6043,
+ 0x0000, 0x608a, 0x628e, 0x6a04, 0xa294, 0x4000, 0x00c0, 0x7223,
+ 0x027f, 0x0d7f, 0x007f, 0x157f, 0x2009, 0x017f, 0x200b, 0x0000,
+ 0x0c7f, 0x127f, 0x007c, 0x0e7e, 0x2071, 0xa8b1, 0x7020, 0xa005,
+ 0x0040, 0x723c, 0x8001, 0x7022, 0x0e7f, 0x007c, 0x20a9, 0x0008,
+ 0x20a2, 0x00f0, 0x7240, 0x20a2, 0x20a2, 0x007c, 0x0f7e, 0x0e7e,
+ 0x0d7e, 0x0c7e, 0x077e, 0x067e, 0x007e, 0x127e, 0x2091, 0x8000,
+ 0x2071, 0xa8b1, 0x7614, 0x2660, 0x2678, 0x2039, 0x0001, 0x87ff,
+ 0x0040, 0x72e2, 0x8cff, 0x0040, 0x72e2, 0x601c, 0xa086, 0x0006,
+ 0x00c0, 0x72dd, 0x88ff, 0x0040, 0x726d, 0x2800, 0xac06, 0x00c0,
+ 0x72dd, 0x2039, 0x0000, 0x0078, 0x7278, 0x6018, 0xa206, 0x00c0,
+ 0x72dd, 0x85ff, 0x0040, 0x7278, 0x6020, 0xa106, 0x00c0, 0x72dd,
+ 0x7024, 0xac06, 0x00c0, 0x72a8, 0x2069, 0x0100, 0x68c0, 0xa005,
+ 0x0040, 0x72a3, 0x1078, 0x5acb, 0x6817, 0x0008, 0x68c3, 0x0000,
+ 0x1078, 0x7378, 0x7027, 0x0000, 0x037e, 0x2069, 0x0140, 0x6b04,
+ 0xa384, 0x1000, 0x0040, 0x7298, 0x6803, 0x0100, 0x6803, 0x0000,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0040, 0x72a0, 0x6827, 0x0001,
+ 0x037f, 0x0078, 0x72a8, 0x6003, 0x0009, 0x630a, 0x0078, 0x72dd,
+ 0x7014, 0xac36, 0x00c0, 0x72ae, 0x660c, 0x7616, 0x7010, 0xac36,
+ 0x00c0, 0x72bc, 0x2c00, 0xaf36, 0x0040, 0x72ba, 0x2f00, 0x7012,
+ 0x0078, 0x72bc, 0x7013, 0x0000, 0x660c, 0x067e, 0x2c00, 0xaf06,
+ 0x0040, 0x72c5, 0x7e0e, 0x0078, 0x72c6, 0x2678, 0x89ff, 0x00c0,
+ 0x72d5, 0x600f, 0x0000, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040,
+ 0x72d3, 0x1078, 0xa1ca, 0x1078, 0x8ec6, 0x1078, 0x7233, 0x88ff,
+ 0x00c0, 0x72ec, 0x0c7f, 0x0078, 0x7257, 0x2c78, 0x600c, 0x2060,
+ 0x0078, 0x7257, 0xa006, 0x127f, 0x007f, 0x067f, 0x077f, 0x0c7f,
+ 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x6017, 0x0000, 0x0c7f, 0xa8c5,
+ 0x0001, 0x0078, 0x72e3, 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e,
+ 0x027e, 0x007e, 0x127e, 0x2091, 0x8000, 0x2071, 0xa8b1, 0x7638,
+ 0x2660, 0x2678, 0x8cff, 0x0040, 0x7367, 0x601c, 0xa086, 0x0006,
+ 0x00c0, 0x7362, 0x87ff, 0x0040, 0x7313, 0x2700, 0xac06, 0x00c0,
+ 0x7362, 0x0078, 0x731e, 0x6018, 0xa206, 0x00c0, 0x7362, 0x85ff,
+ 0x0040, 0x731e, 0x6020, 0xa106, 0x00c0, 0x7362, 0x703c, 0xac06,
+ 0x00c0, 0x7332, 0x037e, 0x2019, 0x0001, 0x1078, 0x7058, 0x7033,
+ 0x0000, 0x703f, 0x0000, 0x7043, 0x0000, 0x7047, 0x0000, 0x704b,
+ 0x0000, 0x037f, 0x7038, 0xac36, 0x00c0, 0x7338, 0x660c, 0x763a,
+ 0x7034, 0xac36, 0x00c0, 0x7346, 0x2c00, 0xaf36, 0x0040, 0x7344,
+ 0x2f00, 0x7036, 0x0078, 0x7346, 0x7037, 0x0000, 0x660c, 0x067e,
+ 0x2c00, 0xaf06, 0x0040, 0x734f, 0x7e0e, 0x0078, 0x7350, 0x2678,
+ 0x600f, 0x0000, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040, 0x735a,
+ 0x1078, 0xa1ca, 0x1078, 0x8ec6, 0x87ff, 0x00c0, 0x7371, 0x0c7f,
+ 0x0078, 0x7302, 0x2c78, 0x600c, 0x2060, 0x0078, 0x7302, 0xa006,
+ 0x127f, 0x007f, 0x027f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f,
+ 0x007c, 0x6017, 0x0000, 0x0c7f, 0xa7bd, 0x0001, 0x0078, 0x7368,
+ 0x0e7e, 0x2071, 0xa8b1, 0x2001, 0xa600, 0x2004, 0xa086, 0x0002,
+ 0x00c0, 0x7386, 0x7007, 0x0005, 0x0078, 0x7388, 0x7007, 0x0000,
+ 0x0e7f, 0x007c, 0x0f7e, 0x0e7e, 0x0c7e, 0x067e, 0x027e, 0x007e,
+ 0x127e, 0x2091, 0x8000, 0x2071, 0xa8b1, 0x2c10, 0x7638, 0x2660,
+ 0x2678, 0x8cff, 0x0040, 0x73c8, 0x2200, 0xac06, 0x00c0, 0x73c3,
+ 0x7038, 0xac36, 0x00c0, 0x73a6, 0x660c, 0x763a, 0x7034, 0xac36,
+ 0x00c0, 0x73b4, 0x2c00, 0xaf36, 0x0040, 0x73b2, 0x2f00, 0x7036,
+ 0x0078, 0x73b4, 0x7037, 0x0000, 0x660c, 0x2c00, 0xaf06, 0x0040,
+ 0x73bc, 0x7e0e, 0x0078, 0x73bd, 0x2678, 0x600f, 0x0000, 0xa085,
+ 0x0001, 0x0078, 0x73c8, 0x2c78, 0x600c, 0x2060, 0x0078, 0x7399,
+ 0x127f, 0x007f, 0x027f, 0x067f, 0x0c7f, 0x0e7f, 0x0f7f, 0x007c,
+ 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e, 0x007e, 0x127e, 0x2091,
+ 0x8000, 0x2071, 0xa8b1, 0x760c, 0x2660, 0x2678, 0x8cff, 0x0040,
+ 0x7469, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x00c0, 0x7464,
+ 0x7024, 0xac06, 0x00c0, 0x740f, 0x2069, 0x0100, 0x68c0, 0xa005,
+ 0x0040, 0x743d, 0x1078, 0x6e0f, 0x68c3, 0x0000, 0x1078, 0x7378,
+ 0x7027, 0x0000, 0x037e, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000,
+ 0x0040, 0x7406, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100,
+ 0x6824, 0xd084, 0x0040, 0x740e, 0x6827, 0x0001, 0x037f, 0x700c,
+ 0xac36, 0x00c0, 0x7415, 0x660c, 0x760e, 0x7008, 0xac36, 0x00c0,
+ 0x7423, 0x2c00, 0xaf36, 0x0040, 0x7421, 0x2f00, 0x700a, 0x0078,
+ 0x7423, 0x700b, 0x0000, 0x660c, 0x067e, 0x2c00, 0xaf06, 0x0040,
+ 0x742c, 0x7e0e, 0x0078, 0x742d, 0x2678, 0x600f, 0x0000, 0x1078,
+ 0x8eec, 0x00c0, 0x7441, 0x1078, 0x28a6, 0x1078, 0x8f00, 0x00c0,
+ 0x745d, 0x1078, 0x7c83, 0x0078, 0x745d, 0x1078, 0x7378, 0x0078,
+ 0x740f, 0x1078, 0x8f00, 0x00c0, 0x7449, 0x1078, 0x7c83, 0x0078,
+ 0x745d, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040, 0x745d, 0x601c,
+ 0xa086, 0x0003, 0x00c0, 0x7471, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x1078, 0x4a73, 0x1078, 0x8eb9, 0x1078, 0x8ec6, 0x1078,
+ 0x7233, 0x0c7f, 0x0078, 0x73de, 0x2c78, 0x600c, 0x2060, 0x0078,
+ 0x73de, 0x127f, 0x007f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f,
+ 0x007c, 0x601c, 0xa086, 0x0006, 0x00c0, 0x745d, 0x1078, 0xa1ca,
+ 0x0078, 0x745d, 0x037e, 0x157e, 0x137e, 0x147e, 0x3908, 0xa006,
+ 0xa190, 0x0020, 0x221c, 0xa39e, 0x2676, 0x00c0, 0x748b, 0x8210,
+ 0x8000, 0x0078, 0x7482, 0xa005, 0x0040, 0x7497, 0x20a9, 0x0020,
+ 0x2198, 0x8211, 0xa282, 0x0020, 0x20c8, 0x20a0, 0x53a3, 0x147f,
+ 0x137f, 0x157f, 0x037f, 0x007c, 0x0d7e, 0x20a1, 0x020b, 0x1078,
+ 0x67c2, 0x20a3, 0x0200, 0x20a3, 0x0014, 0x60c3, 0x0014, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x2099, 0xa8a5, 0x20a9, 0x0004, 0x53a6,
+ 0x20a3, 0x0004, 0x20a3, 0x7878, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x1078, 0x6dfb, 0x0d7f, 0x007c, 0x20a1, 0x020b, 0x1078, 0x67c2,
+ 0x20a3, 0x0214, 0x20a3, 0x0018, 0x20a3, 0x0800, 0x7810, 0xa084,
+ 0xff00, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x7810, 0xa084, 0x00ff, 0x20a2, 0x7828, 0x20a2,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0018, 0x1078, 0x6dfb,
+ 0x007c, 0x0d7e, 0x017e, 0x2f68, 0x2009, 0x0035, 0x1078, 0x91bc,
+ 0x00c0, 0x7551, 0x20a1, 0x020b, 0x1078, 0x6731, 0x20a3, 0x1300,
+ 0x20a3, 0x0000, 0x7828, 0x2068, 0x681c, 0xa086, 0x0003, 0x0040,
+ 0x752d, 0x7818, 0xa080, 0x0028, 0x2014, 0xa286, 0x007e, 0x00c0,
+ 0x7507, 0x20a3, 0x00ff, 0x20a3, 0xfffe, 0x0078, 0x7542, 0xa286,
+ 0x007f, 0x00c0, 0x7511, 0x20a3, 0x00ff, 0x20a3, 0xfffd, 0x0078,
+ 0x7542, 0xd2bc, 0x0040, 0x7527, 0xa286, 0x0080, 0x00c0, 0x751e,
+ 0x20a3, 0x00ff, 0x20a3, 0xfffc, 0x0078, 0x7542, 0xa2e8, 0xa735,
+ 0x2d6c, 0x6810, 0x20a2, 0x6814, 0x20a2, 0x0078, 0x7542, 0x20a3,
+ 0x0000, 0x6098, 0x20a2, 0x0078, 0x7542, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xa082, 0x007e, 0x0048, 0x753e, 0x0d7e, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x7542, 0x20a3, 0x0000,
+ 0x6030, 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x000c, 0x1078, 0x6dfb, 0x017f, 0x0d7f,
+ 0x007c, 0x7817, 0x0001, 0x7803, 0x0006, 0x017f, 0x0d7f, 0x007c,
+ 0x0d7e, 0x027e, 0x7928, 0x2168, 0x691c, 0xa186, 0x0006, 0x0040,
+ 0x757a, 0xa186, 0x0003, 0x0040, 0x75d5, 0xa186, 0x0005, 0x0040,
+ 0x75b8, 0xa186, 0x0004, 0x0040, 0x75a8, 0xa186, 0x0008, 0x0040,
+ 0x75c2, 0x7807, 0x0037, 0x7813, 0x1700, 0x1078, 0x7640, 0x027f,
+ 0x0d7f, 0x007c, 0x1078, 0x75fd, 0x2009, 0x4000, 0x6800, 0x0079,
+ 0x7581, 0x7594, 0x75a2, 0x7596, 0x75a2, 0x759d, 0x7594, 0x7594,
+ 0x75a2, 0x75a2, 0x75a2, 0x75a2, 0x7594, 0x7594, 0x7594, 0x7594,
+ 0x7594, 0x75a2, 0x7594, 0x75a2, 0x1078, 0x1332, 0x6824, 0xd0e4,
+ 0x0040, 0x759d, 0xd0cc, 0x0040, 0x75a0, 0xa00e, 0x0078, 0x75a2,
+ 0x2009, 0x2000, 0x6828, 0x20a2, 0x682c, 0x20a2, 0x0078, 0x75f3,
+ 0x1078, 0x75fd, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000,
+ 0x6a00, 0xa286, 0x0002, 0x00c0, 0x75b6, 0xa00e, 0x0078, 0x75f3,
+ 0x1078, 0x75fd, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000,
+ 0x0078, 0x75f3, 0x1078, 0x75fd, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x2009, 0x4000, 0xa286, 0x0005, 0x0040, 0x75d2, 0xa286, 0x0002,
+ 0x00c0, 0x75d3, 0xa00e, 0x0078, 0x75f3, 0x1078, 0x75fd, 0x6810,
+ 0x2068, 0x697c, 0x6810, 0xa112, 0x6980, 0x6814, 0xa103, 0x20a2,
+ 0x22a2, 0x7928, 0xa180, 0x0000, 0x2004, 0xa08e, 0x0002, 0x0040,
+ 0x75f1, 0xa08e, 0x0004, 0x0040, 0x75f1, 0x2009, 0x4000, 0x0078,
+ 0x75f3, 0x2009, 0x0000, 0x21a2, 0x20a3, 0x0000, 0x60c3, 0x0018,
+ 0x1078, 0x6dfb, 0x027f, 0x0d7f, 0x007c, 0x037e, 0x047e, 0x057e,
+ 0x067e, 0x20a1, 0x020b, 0x1078, 0x67c2, 0xa006, 0x20a3, 0x0200,
+ 0x20a2, 0x7934, 0x21a2, 0x7938, 0x21a2, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xa092, 0x007e, 0x0048, 0x7623, 0x0d7e, 0x2069, 0xa61b,
+ 0x2d2c, 0x8d68, 0x2d34, 0xa0e8, 0xa735, 0x2d6c, 0x6b10, 0x6c14,
+ 0x0d7f, 0x0078, 0x7629, 0x2019, 0x0000, 0x6498, 0x2029, 0x0000,
+ 0x6630, 0x7828, 0xa080, 0x0007, 0x2004, 0xa086, 0x0003, 0x00c0,
+ 0x7637, 0x25a2, 0x26a2, 0x23a2, 0x24a2, 0x0078, 0x763b, 0x23a2,
+ 0x24a2, 0x25a2, 0x26a2, 0x067f, 0x057f, 0x047f, 0x037f, 0x007c,
+ 0x20a1, 0x020b, 0x1078, 0x67c2, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x0009, 0x7810, 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6dfb,
+ 0x007c, 0x20a1, 0x020b, 0x1078, 0x6728, 0x20a3, 0x1400, 0x20a3,
+ 0x0000, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x7828, 0x20a2, 0x782c,
+ 0x20a2, 0x7830, 0xa084, 0x00ff, 0x8007, 0x20a2, 0x20a3, 0x0000,
+ 0x60c3, 0x0010, 0x1078, 0x6dfb, 0x007c, 0x20a1, 0x020b, 0x1078,
+ 0x67b9, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x7828, 0x20a2, 0x7810,
+ 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6dfb, 0x007c, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x7689, 0x60c3, 0x0000, 0x1078, 0x6dfb, 0x147f,
+ 0x007c, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xd0bc, 0x0040, 0x76a6, 0x0d7e, 0xa0e8, 0xa735, 0x2d6c,
+ 0x6810, 0xa085, 0x0300, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa61b,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x76ae, 0x20a3, 0x0300,
+ 0x6298, 0x22a2, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x20a3, 0x0819,
+ 0x20a3, 0x0000, 0x1078, 0x6dea, 0x22a2, 0x20a3, 0x0000, 0x2fa2,
+ 0x7a08, 0x22a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x007c, 0x2061,
+ 0xad00, 0x2a70, 0x7064, 0x704a, 0x704f, 0xad00, 0x007c, 0x0e7e,
+ 0x127e, 0x2071, 0xa600, 0x2091, 0x8000, 0x7548, 0xa582, 0x0010,
+ 0x0048, 0x76f9, 0x704c, 0x2060, 0x6000, 0xa086, 0x0000, 0x0040,
+ 0x76e5, 0xace0, 0x0010, 0x7058, 0xac02, 0x00c8, 0x76e1, 0x0078,
+ 0x76d4, 0x2061, 0xad00, 0x0078, 0x76d4, 0x6003, 0x0008, 0x8529,
+ 0x754a, 0xaca8, 0x0010, 0x7058, 0xa502, 0x00c8, 0x76f5, 0x754e,
+ 0xa085, 0x0001, 0x127f, 0x0e7f, 0x007c, 0x704f, 0xad00, 0x0078,
+ 0x76f0, 0xa006, 0x0078, 0x76f2, 0x0e7e, 0x2071, 0xa600, 0x7548,
+ 0xa582, 0x0010, 0x0048, 0x772a, 0x704c, 0x2060, 0x6000, 0xa086,
+ 0x0000, 0x0040, 0x7717, 0xace0, 0x0010, 0x7058, 0xac02, 0x00c8,
+ 0x7713, 0x0078, 0x7706, 0x2061, 0xad00, 0x0078, 0x7706, 0x6003,
+ 0x0008, 0x8529, 0x754a, 0xaca8, 0x0010, 0x7058, 0xa502, 0x00c8,
+ 0x7726, 0x754e, 0xa085, 0x0001, 0x0e7f, 0x007c, 0x704f, 0xad00,
+ 0x0078, 0x7722, 0xa006, 0x0078, 0x7724, 0xac82, 0xad00, 0x1048,
+ 0x1332, 0x2001, 0xa616, 0x2004, 0xac02, 0x10c8, 0x1332, 0xa006,
+ 0x6006, 0x600a, 0x600e, 0x6012, 0x6016, 0x601a, 0x601f, 0x0000,
+ 0x6003, 0x0000, 0x6022, 0x6026, 0x602a, 0x602e, 0x6032, 0x6036,
+ 0x603a, 0x603e, 0x2061, 0xa600, 0x6048, 0x8000, 0x604a, 0xa086,
+ 0x0001, 0x0040, 0x7754, 0x007c, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x62d1, 0x127f, 0x0078, 0x7753, 0x601c, 0xa084, 0x000f, 0x0079,
+ 0x7761, 0x776a, 0x777b, 0x7797, 0x77b3, 0x920e, 0x922a, 0x9246,
+ 0x776a, 0x777b, 0xa186, 0x0013, 0x00c0, 0x7773, 0x1078, 0x61cd,
+ 0x1078, 0x62d1, 0x007c, 0xa18e, 0x0047, 0x00c0, 0x777a, 0xa016,
+ 0x1078, 0x15fa, 0x007c, 0x067e, 0x6000, 0xa0b2, 0x0010, 0x10c8,
+ 0x1332, 0x1079, 0x7785, 0x067f, 0x007c, 0x7795, 0x7b00, 0x7cb2,
+ 0x7795, 0x7d36, 0x77cf, 0x7795, 0x7795, 0x7a92, 0x80f6, 0x7795,
+ 0x7795, 0x7795, 0x7795, 0x7795, 0x7795, 0x1078, 0x1332, 0x067e,
+ 0x6000, 0xa0b2, 0x0010, 0x10c8, 0x1332, 0x1079, 0x77a1, 0x067f,
+ 0x007c, 0x77b1, 0x87c3, 0x77b1, 0x77b1, 0x77b1, 0x77b1, 0x77b1,
+ 0x77b1, 0x8766, 0x8951, 0x77b1, 0x87f3, 0x8879, 0x87f3, 0x8879,
+ 0x77b1, 0x1078, 0x1332, 0x067e, 0x6000, 0xa0b2, 0x0010, 0x10c8,
+ 0x1332, 0x1079, 0x77bd, 0x067f, 0x007c, 0x77cd, 0x813d, 0x820e,
+ 0x8368, 0x84e4, 0x77cd, 0x77cd, 0x77cd, 0x8116, 0x870e, 0x8712,
+ 0x77cd, 0x77cd, 0x77cd, 0x77cd, 0x8742, 0x1078, 0x1332, 0xa1b6,
+ 0x0015, 0x00c0, 0x77d7, 0x1078, 0x772d, 0x0078, 0x77dd, 0xa1b6,
+ 0x0016, 0x10c0, 0x1332, 0x1078, 0x772d, 0x007c, 0x20a9, 0x000e,
+ 0x2e98, 0x6010, 0x20a0, 0x53a3, 0x20a9, 0x0006, 0x3310, 0x3420,
+ 0x9398, 0x94a0, 0x3318, 0x3428, 0x222e, 0x2326, 0xa290, 0x0002,
+ 0xa5a8, 0x0002, 0xa398, 0x0002, 0xa4a0, 0x0002, 0x00f0, 0x77ec,
+ 0x0e7e, 0x1078, 0x8d06, 0x0040, 0x7803, 0x6010, 0x2070, 0x7007,
+ 0x0000, 0x7037, 0x0103, 0x0e7f, 0x1078, 0x772d, 0x007c, 0x0d7e,
+ 0x037e, 0x7330, 0xa386, 0x0200, 0x00c0, 0x7814, 0x6018, 0x2068,
+ 0x6813, 0x00ff, 0x6817, 0xfffd, 0x6010, 0xa005, 0x0040, 0x781e,
+ 0x2068, 0x6807, 0x0000, 0x6837, 0x0103, 0x6b32, 0x1078, 0x772d,
+ 0x037f, 0x0d7f, 0x007c, 0x017e, 0x20a9, 0x002a, 0xae80, 0x000c,
+ 0x2098, 0x6010, 0xa080, 0x0002, 0x20a0, 0x53a3, 0x20a9, 0x002a,
+ 0x6010, 0xa080, 0x0001, 0x2004, 0xa080, 0x0002, 0x20a0, 0x53a3,
+ 0x0e7e, 0x6010, 0x2004, 0x2070, 0x7037, 0x0103, 0x0e7f, 0x1078,
+ 0x772d, 0x017f, 0x007c, 0x0e7e, 0x0d7e, 0x603f, 0x0000, 0x2c68,
+ 0x017e, 0x2009, 0x0035, 0x1078, 0x91bc, 0x017f, 0x00c0, 0x785f,
+ 0x027e, 0x6228, 0x2268, 0x027f, 0x2071, 0xab8c, 0x6b1c, 0xa386,
+ 0x0003, 0x0040, 0x7863, 0xa386, 0x0006, 0x0040, 0x7867, 0x1078,
+ 0x772d, 0x0078, 0x7869, 0x1078, 0x786c, 0x0078, 0x7869, 0x1078,
+ 0x7938, 0x0d7f, 0x0e7f, 0x007c, 0x0f7e, 0x6810, 0x2078, 0xa186,
+ 0x0015, 0x0040, 0x791d, 0xa18e, 0x0016, 0x00c0, 0x7936, 0x700c,
+ 0xa08c, 0xff00, 0xa186, 0x1700, 0x0040, 0x7882, 0xa186, 0x0300,
+ 0x00c0, 0x78f8, 0x8fff, 0x00c0, 0x788c, 0x6800, 0xa086, 0x000f,
+ 0x0040, 0x78db, 0x0078, 0x7934, 0x6808, 0xa086, 0xffff, 0x00c0,
+ 0x7921, 0x784c, 0xa084, 0x0060, 0xa086, 0x0020, 0x00c0, 0x78a2,
+ 0x797c, 0x7810, 0xa106, 0x00c0, 0x7921, 0x7980, 0x7814, 0xa106,
+ 0x00c0, 0x7921, 0x1078, 0x8eb9, 0x6830, 0x7852, 0x784c, 0xc0dc,
+ 0xc0f4, 0xc0d4, 0x784e, 0x027e, 0xa00e, 0x6a14, 0x2001, 0x000a,
+ 0x1078, 0x5c1c, 0x7854, 0xa20a, 0x0048, 0x78b7, 0x8011, 0x7a56,
+ 0x82ff, 0x027f, 0x00c0, 0x78c3, 0x0c7e, 0x2d60, 0x1078, 0x8ae0,
+ 0x0c7f, 0x0078, 0x7934, 0x0c7e, 0x0d7e, 0x2f68, 0x6838, 0xd0fc,
+ 0x00c0, 0x78ce, 0x1078, 0x4353, 0x0078, 0x78d0, 0x1078, 0x4431,
+ 0x0d7f, 0x0c7f, 0x00c0, 0x7921, 0x0c7e, 0x2d60, 0x1078, 0x772d,
+ 0x0c7f, 0x0078, 0x7934, 0x0c7e, 0x1078, 0x9187, 0x0040, 0x78f1,
+ 0x6013, 0x0000, 0x6818, 0x601a, 0x601f, 0x0003, 0x6904, 0x0c7e,
+ 0x2d60, 0x1078, 0x772d, 0x0c7f, 0x1078, 0x775c, 0x0c7f, 0x0078,
+ 0x7934, 0x2001, 0xa8a4, 0x2004, 0x683e, 0x0c7f, 0x0078, 0x7934,
+ 0x7008, 0xa086, 0x000b, 0x00c0, 0x7912, 0x6018, 0x200c, 0xc1bc,
+ 0x2102, 0x0c7e, 0x2d60, 0x7853, 0x0003, 0x6007, 0x0085, 0x6003,
+ 0x000b, 0x601f, 0x0002, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0c7f,
+ 0x0078, 0x7934, 0x700c, 0xa086, 0x2a00, 0x00c0, 0x7921, 0x2001,
+ 0xa8a4, 0x2004, 0x683e, 0x0078, 0x7934, 0x1078, 0x7953, 0x0078,
+ 0x7936, 0x8fff, 0x1040, 0x1332, 0x0c7e, 0x0d7e, 0x2d60, 0x2f68,
+ 0x6837, 0x0103, 0x684b, 0x0003, 0x1078, 0x89cf, 0x1078, 0x8eb9,
+ 0x1078, 0x8ec6, 0x0d7f, 0x0c7f, 0x1078, 0x772d, 0x0f7f, 0x007c,
+ 0xa186, 0x0015, 0x00c0, 0x7942, 0x2001, 0xa8a4, 0x2004, 0x683e,
+ 0x0078, 0x7950, 0xa18e, 0x0016, 0x00c0, 0x7952, 0x0c7e, 0x2d00,
+ 0x2060, 0x1078, 0xa495, 0x1078, 0x5bc1, 0x1078, 0x772d, 0x0c7f,
+ 0x1078, 0x772d, 0x007c, 0x027e, 0x037e, 0x047e, 0x7228, 0x7c80,
+ 0x7b7c, 0xd2f4, 0x0040, 0x7962, 0x2001, 0xa8a4, 0x2004, 0x683e,
+ 0x0078, 0x79c6, 0x0c7e, 0x2d60, 0x1078, 0x89f3, 0x0c7f, 0x6804,
+ 0xa086, 0x0050, 0x00c0, 0x797a, 0x0c7e, 0x2d00, 0x2060, 0x6003,
+ 0x0001, 0x6007, 0x0050, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0c7f,
+ 0x0078, 0x79c6, 0x6800, 0xa086, 0x000f, 0x0040, 0x799c, 0x8fff,
+ 0x1040, 0x1332, 0x6824, 0xd0dc, 0x00c0, 0x799c, 0x6800, 0xa086,
+ 0x0004, 0x00c0, 0x79a1, 0x784c, 0xd0ac, 0x0040, 0x79a1, 0x784c,
+ 0xc0dc, 0xc0f4, 0x784e, 0x7850, 0xc0f4, 0xc0fc, 0x7852, 0x2001,
+ 0x0001, 0x682e, 0x0078, 0x79c0, 0x2001, 0x0007, 0x682e, 0x0078,
+ 0x79c0, 0x784c, 0xd0b4, 0x00c0, 0x79ae, 0xd0ac, 0x0040, 0x799c,
+ 0x784c, 0xd0f4, 0x00c0, 0x799c, 0x0078, 0x798f, 0xd2ec, 0x00c0,
+ 0x799c, 0x7024, 0xa306, 0x00c0, 0x79b9, 0x7020, 0xa406, 0x0040,
+ 0x799c, 0x7020, 0x6836, 0x7024, 0x683a, 0x2001, 0x0005, 0x682e,
+ 0x1078, 0x8ff0, 0x1078, 0x62d1, 0x0078, 0x79c8, 0x1078, 0x772d,
+ 0x047f, 0x037f, 0x027f, 0x007c, 0x0e7e, 0x0d7e, 0x027e, 0x6034,
+ 0x2068, 0x6a1c, 0xa286, 0x0007, 0x0040, 0x7a35, 0xa286, 0x0002,
+ 0x0040, 0x7a35, 0xa286, 0x0000, 0x0040, 0x7a35, 0x6808, 0x6338,
+ 0xa306, 0x00c0, 0x7a35, 0x2071, 0xab8c, 0xa186, 0x0015, 0x0040,
+ 0x7a2f, 0xa18e, 0x0016, 0x00c0, 0x7a02, 0x6030, 0xa084, 0x00ff,
+ 0xa086, 0x0001, 0x00c0, 0x7a02, 0x700c, 0xa086, 0x2a00, 0x00c0,
+ 0x7a02, 0x6034, 0xa080, 0x0009, 0x200c, 0xc1dd, 0xc1f5, 0x2102,
+ 0x0078, 0x7a2f, 0x0c7e, 0x6034, 0x2060, 0x6104, 0xa186, 0x004b,
+ 0x0040, 0x7a22, 0xa186, 0x004c, 0x0040, 0x7a22, 0xa186, 0x004d,
+ 0x0040, 0x7a22, 0xa186, 0x004e, 0x0040, 0x7a22, 0xa186, 0x0052,
+ 0x0040, 0x7a22, 0x6010, 0x2068, 0x1078, 0x8d06, 0x1040, 0x1332,
+ 0x6853, 0x0003, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002,
+ 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0c7f, 0x0078, 0x7a35, 0x6034,
+ 0x2068, 0x2001, 0xa8a4, 0x2004, 0x683e, 0x1078, 0x772d, 0x027f,
+ 0x0d7f, 0x0e7f, 0x007c, 0x0d7e, 0x20a9, 0x000e, 0x2e98, 0x6010,
+ 0x20a0, 0x53a3, 0xa1b6, 0x0015, 0x00c0, 0x7a73, 0x6018, 0x2068,
+ 0x157e, 0x037e, 0x027e, 0xae90, 0x000c, 0xa290, 0x0004, 0x20a9,
+ 0x0004, 0xad98, 0x000a, 0x1078, 0x80de, 0x027f, 0x037f, 0x157f,
+ 0x00c0, 0x7a76, 0x157e, 0x037e, 0x027e, 0xae90, 0x000c, 0xa290,
+ 0x0008, 0x20a9, 0x0004, 0xad98, 0x0006, 0x1078, 0x80de, 0x027f,
+ 0x037f, 0x157f, 0x00c0, 0x7a76, 0x7038, 0x680a, 0x703c, 0x680e,
+ 0x6800, 0xc08d, 0x6802, 0x0d7f, 0x0078, 0x77f8, 0x1078, 0x2880,
+ 0x0c7e, 0x1078, 0x76c7, 0x2f00, 0x601a, 0x6013, 0x0000, 0x601f,
+ 0x0001, 0x6007, 0x0001, 0x6003, 0x0001, 0x2001, 0x0007, 0x1078,
+ 0x4502, 0x1078, 0x4535, 0x1078, 0x5dd7, 0x1078, 0x62d1, 0x0c7f,
+ 0x0078, 0x7a73, 0x2100, 0xa1b2, 0x0044, 0x10c8, 0x1332, 0xa1b2,
+ 0x0040, 0x00c8, 0x7af7, 0x0079, 0x7a9d, 0x7aeb, 0x7adf, 0x7aeb,
+ 0x7aeb, 0x7aeb, 0x7aeb, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add,
+ 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add,
+ 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add,
+ 0x7add, 0x7add, 0x7add, 0x7add, 0x7aeb, 0x7add, 0x7aeb, 0x7aeb,
+ 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7aeb, 0x7add, 0x7add,
+ 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7aeb,
+ 0x7aeb, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add, 0x7add,
+ 0x7add, 0x7add, 0x7aeb, 0x7add, 0x7add, 0x1078, 0x1332, 0x6003,
+ 0x0001, 0x6106, 0x1078, 0x5dd7, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x62d1, 0x127f, 0x007c, 0x6003, 0x0001, 0x6106, 0x1078, 0x5dd7,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x62d1, 0x127f, 0x007c, 0x2600,
+ 0x0079, 0x7afa, 0x7afe, 0x7afe, 0x7afe, 0x7aeb, 0x1078, 0x1332,
+ 0x6004, 0xa0b2, 0x0044, 0x10c8, 0x1332, 0xa1b6, 0x0013, 0x00c0,
+ 0x7b10, 0xa0b2, 0x0040, 0x00c8, 0x7c79, 0x2008, 0x0079, 0x7bbf,
+ 0xa1b6, 0x0027, 0x00c0, 0x7b7c, 0x1078, 0x61cd, 0x6004, 0x1078,
+ 0x8eec, 0x0040, 0x7b2d, 0x1078, 0x8f00, 0x0040, 0x7b74, 0xa08e,
+ 0x0021, 0x0040, 0x7b78, 0xa08e, 0x0022, 0x0040, 0x7b74, 0xa08e,
+ 0x003d, 0x0040, 0x7b78, 0x0078, 0x7b6f, 0x1078, 0x28a6, 0x2001,
+ 0x0007, 0x1078, 0x4502, 0x6018, 0xa080, 0x0028, 0x200c, 0x1078,
+ 0x7c83, 0xa186, 0x007e, 0x00c0, 0x7b42, 0x2001, 0xa633, 0x2014,
+ 0xc285, 0x2202, 0x017e, 0x027e, 0x037e, 0x2110, 0x027e, 0x2019,
+ 0x0028, 0x1078, 0x73d0, 0x027f, 0x1078, 0xa4f1, 0x037f, 0x027f,
+ 0x017f, 0x017e, 0x027e, 0x037e, 0x2110, 0x2019, 0x0028, 0x1078,
+ 0x5f01, 0x077e, 0x2039, 0x0000, 0x1078, 0x5e0a, 0x0c7e, 0x6018,
+ 0xa065, 0x0040, 0x7b65, 0x1078, 0x47e9, 0x0c7f, 0x2c08, 0x1078,
+ 0x9f8b, 0x077f, 0x037f, 0x027f, 0x017f, 0x1078, 0x457f, 0x1078,
+ 0x772d, 0x1078, 0x62d1, 0x007c, 0x1078, 0x7c83, 0x0078, 0x7b6f,
+ 0x1078, 0x7ca6, 0x0078, 0x7b6f, 0xa186, 0x0014, 0x00c0, 0x7b73,
+ 0x1078, 0x61cd, 0x1078, 0x2880, 0x1078, 0x8eec, 0x00c0, 0x7b9b,
+ 0x1078, 0x28a6, 0x6018, 0xa080, 0x0028, 0x200c, 0x1078, 0x7c83,
+ 0xa186, 0x007e, 0x00c0, 0x7b99, 0x2001, 0xa633, 0x200c, 0xc185,
+ 0x2102, 0x0078, 0x7b6f, 0x1078, 0x8f00, 0x00c0, 0x7ba3, 0x1078,
+ 0x7c83, 0x0078, 0x7b6f, 0x6004, 0xa08e, 0x0032, 0x00c0, 0x7bb4,
+ 0x0e7e, 0x0f7e, 0x2071, 0xa682, 0x2079, 0x0000, 0x1078, 0x2bd7,
+ 0x0f7f, 0x0e7f, 0x0078, 0x7b6f, 0x6004, 0xa08e, 0x0021, 0x0040,
+ 0x7b9f, 0xa08e, 0x0022, 0x1040, 0x7c83, 0x0078, 0x7b6f, 0x7c01,
+ 0x7c03, 0x7c07, 0x7c0b, 0x7c0f, 0x7c13, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7c17, 0x7c29, 0x7bff,
+ 0x7c2b, 0x7c29, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7c29,
+ 0x7c29, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7c5c, 0x7c29, 0x7bff, 0x7c23, 0x7bff, 0x7bff, 0x7bff,
+ 0x7c25, 0x7bff, 0x7bff, 0x7bff, 0x7c29, 0x7bff, 0x7bff, 0x1078,
+ 0x1332, 0x0078, 0x7c29, 0x2001, 0x000b, 0x0078, 0x7c36, 0x2001,
+ 0x0003, 0x0078, 0x7c36, 0x2001, 0x0005, 0x0078, 0x7c36, 0x2001,
+ 0x0001, 0x0078, 0x7c36, 0x2001, 0x0009, 0x0078, 0x7c36, 0x1078,
+ 0x61cd, 0x6003, 0x0005, 0x2001, 0xa8a4, 0x2004, 0x603e, 0x1078,
+ 0x62d1, 0x0078, 0x7c35, 0x0078, 0x7c29, 0x0078, 0x7c29, 0x1078,
+ 0x4502, 0x0078, 0x7c6e, 0x1078, 0x61cd, 0x6003, 0x0004, 0x2001,
+ 0xa8a2, 0x2004, 0x6016, 0x1078, 0x62d1, 0x007c, 0x1078, 0x4502,
+ 0x1078, 0x61cd, 0x2001, 0xa8a4, 0x2004, 0x603e, 0x6003, 0x0002,
+ 0x037e, 0x2019, 0xa65d, 0x2304, 0xa084, 0xff00, 0x00c0, 0x7c4d,
+ 0x2019, 0xa8a2, 0x231c, 0x0078, 0x7c56, 0x8007, 0xa09a, 0x0004,
+ 0x0048, 0x7c48, 0x8003, 0x801b, 0x831b, 0xa318, 0x6316, 0x037f,
+ 0x1078, 0x62d1, 0x0078, 0x7c35, 0x0e7e, 0x0f7e, 0x2071, 0xa682,
+ 0x2079, 0x0000, 0x1078, 0x2bd7, 0x0f7f, 0x0e7f, 0x1078, 0x61cd,
+ 0x1078, 0x772d, 0x1078, 0x62d1, 0x0078, 0x7c35, 0x1078, 0x61cd,
+ 0x6003, 0x0002, 0x2001, 0xa8a2, 0x2004, 0x6016, 0x1078, 0x62d1,
+ 0x007c, 0x2600, 0x2008, 0x0079, 0x7c7d, 0x7c81, 0x7c81, 0x7c81,
+ 0x7c6e, 0x1078, 0x1332, 0x0e7e, 0x1078, 0x8d06, 0x0040, 0x7c9f,
+ 0x6010, 0x2070, 0x7038, 0xd0fc, 0x0040, 0x7c9f, 0x7007, 0x0000,
+ 0x017e, 0x6004, 0xa08e, 0x0021, 0x0040, 0x7ca1, 0xa08e, 0x003d,
+ 0x0040, 0x7ca1, 0x017f, 0x7037, 0x0103, 0x7033, 0x0100, 0x0e7f,
+ 0x007c, 0x017f, 0x1078, 0x7ca6, 0x0078, 0x7c9f, 0x0e7e, 0xacf0,
+ 0x0004, 0x2e74, 0x7000, 0x2070, 0x7037, 0x0103, 0x7023, 0x8001,
+ 0x0e7f, 0x007c, 0x0d7e, 0x6618, 0x2668, 0x6804, 0xa084, 0x00ff,
+ 0x0d7f, 0xa0b2, 0x000c, 0x10c8, 0x1332, 0x6604, 0xa6b6, 0x0043,
+ 0x00c0, 0x7cc6, 0x1078, 0x9134, 0x0078, 0x7d25, 0x6604, 0xa6b6,
+ 0x0033, 0x00c0, 0x7ccf, 0x1078, 0x90d8, 0x0078, 0x7d25, 0x6604,
+ 0xa6b6, 0x0028, 0x00c0, 0x7cd8, 0x1078, 0x8f2f, 0x0078, 0x7d25,
+ 0x6604, 0xa6b6, 0x0029, 0x00c0, 0x7ce1, 0x1078, 0x8f49, 0x0078,
+ 0x7d25, 0x6604, 0xa6b6, 0x001f, 0x00c0, 0x7cea, 0x1078, 0x77de,
+ 0x0078, 0x7d25, 0x6604, 0xa6b6, 0x0000, 0x00c0, 0x7cf3, 0x1078,
+ 0x7a3b, 0x0078, 0x7d25, 0x6604, 0xa6b6, 0x0022, 0x00c0, 0x7cfc,
+ 0x1078, 0x7807, 0x0078, 0x7d25, 0x6604, 0xa6b6, 0x0035, 0x00c0,
+ 0x7d05, 0x1078, 0x7843, 0x0078, 0x7d25, 0x6604, 0xa6b6, 0x0039,
+ 0x00c0, 0x7d0e, 0x1078, 0x79cc, 0x0078, 0x7d25, 0x6604, 0xa6b6,
+ 0x003d, 0x00c0, 0x7d17, 0x1078, 0x7823, 0x0078, 0x7d25, 0xa1b6,
+ 0x0015, 0x00c0, 0x7d1f, 0x1079, 0x7d2a, 0x0078, 0x7d25, 0xa1b6,
+ 0x0016, 0x00c0, 0x7d26, 0x1079, 0x7e7f, 0x007c, 0x1078, 0x7773,
+ 0x0078, 0x7d25, 0x7d4e, 0x7d51, 0x7d4e, 0x7d9c, 0x7d4e, 0x7e13,
+ 0x7e8b, 0x7d4e, 0x7d4e, 0x7e57, 0x7d4e, 0x7e6d, 0xa1b6, 0x0048,
+ 0x0040, 0x7d42, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x1078,
+ 0x15fa, 0x007c, 0x0e7e, 0xacf0, 0x0004, 0x2e74, 0x7000, 0x2070,
+ 0x7037, 0x0103, 0x0e7f, 0x1078, 0x772d, 0x007c, 0x0005, 0x0005,
+ 0x007c, 0x0e7e, 0x2071, 0xa600, 0x7080, 0xa086, 0x0074, 0x00c0,
+ 0x7d85, 0x1078, 0x9f5f, 0x00c0, 0x7d77, 0x0d7e, 0x6018, 0x2068,
+ 0x7030, 0xd08c, 0x0040, 0x7d6a, 0x6800, 0xd0bc, 0x0040, 0x7d6a,
+ 0xc0c5, 0x6802, 0x1078, 0x7d89, 0x0d7f, 0x2001, 0x0006, 0x1078,
+ 0x4502, 0x1078, 0x28a6, 0x1078, 0x772d, 0x0078, 0x7d87, 0x2001,
+ 0x000a, 0x1078, 0x4502, 0x1078, 0x28a6, 0x6003, 0x0001, 0x6007,
+ 0x0001, 0x1078, 0x5dd7, 0x0078, 0x7d87, 0x1078, 0x7dff, 0x0e7f,
+ 0x007c, 0x6800, 0xd084, 0x0040, 0x7d9b, 0x2001, 0x0000, 0x1078,
+ 0x44ee, 0x2069, 0xa652, 0x6804, 0xd0a4, 0x0040, 0x7d9b, 0x2001,
+ 0x0006, 0x1078, 0x4535, 0x007c, 0x0d7e, 0x2011, 0xa620, 0x2204,
+ 0xa086, 0x0074, 0x00c0, 0x7dfb, 0x6018, 0x2068, 0x6aa0, 0xa286,
+ 0x007e, 0x00c0, 0x7daf, 0x1078, 0x7f9b, 0x0078, 0x7dfd, 0x1078,
+ 0x7f91, 0x6018, 0x2068, 0xa080, 0x0028, 0x2014, 0xa286, 0x0080,
+ 0x00c0, 0x7dd3, 0x6813, 0x00ff, 0x6817, 0xfffc, 0x6010, 0xa005,
+ 0x0040, 0x7dc9, 0x2068, 0x6807, 0x0000, 0x6837, 0x0103, 0x6833,
+ 0x0200, 0x2001, 0x0006, 0x1078, 0x4502, 0x1078, 0x28a6, 0x1078,
+ 0x772d, 0x0078, 0x7dfd, 0x0e7e, 0x2071, 0xa633, 0x2e04, 0xd09c,
+ 0x0040, 0x7dee, 0x2071, 0xab80, 0x7108, 0x720c, 0xa18c, 0x00ff,
+ 0x00c0, 0x7de6, 0xa284, 0xff00, 0x0040, 0x7dee, 0x6018, 0x2070,
+ 0x70a0, 0xd0bc, 0x00c0, 0x7dee, 0x7112, 0x7216, 0x0e7f, 0x2001,
+ 0x0004, 0x1078, 0x4502, 0x6003, 0x0001, 0x6007, 0x0003, 0x1078,
+ 0x5dd7, 0x0078, 0x7dfd, 0x1078, 0x7dff, 0x0d7f, 0x007c, 0x2001,
+ 0x0007, 0x1078, 0x4502, 0x2001, 0xa600, 0x2004, 0xa086, 0x0003,
+ 0x00c0, 0x7e0e, 0x2001, 0x0007, 0x1078, 0x4535, 0x1078, 0x28a6,
+ 0x1078, 0x772d, 0x007c, 0x0e7e, 0x2071, 0xa600, 0x7080, 0xa086,
+ 0x0014, 0x00c0, 0x7e51, 0x7000, 0xa086, 0x0003, 0x00c0, 0x7e26,
+ 0x6010, 0xa005, 0x00c0, 0x7e26, 0x1078, 0x3699, 0x0d7e, 0x6018,
+ 0x2068, 0x1078, 0x4649, 0x1078, 0x7d89, 0x0d7f, 0x1078, 0x8043,
+ 0x00c0, 0x7e51, 0x0d7e, 0x6018, 0x2068, 0x6890, 0x0d7f, 0xa005,
+ 0x0040, 0x7e51, 0x2001, 0x0006, 0x1078, 0x4502, 0x0e7e, 0x6010,
+ 0xa005, 0x0040, 0x7e4a, 0x2070, 0x7007, 0x0000, 0x7037, 0x0103,
+ 0x7033, 0x0200, 0x0e7f, 0x1078, 0x28a6, 0x1078, 0x772d, 0x0078,
+ 0x7e55, 0x1078, 0x7c83, 0x1078, 0x7dff, 0x0e7f, 0x007c, 0x2011,
+ 0xa620, 0x2204, 0xa086, 0x0014, 0x00c0, 0x7e6a, 0x2001, 0x0002,
+ 0x1078, 0x4502, 0x6003, 0x0001, 0x6007, 0x0001, 0x1078, 0x5dd7,
+ 0x0078, 0x7e6c, 0x1078, 0x7dff, 0x007c, 0x2011, 0xa620, 0x2204,
+ 0xa086, 0x0004, 0x00c0, 0x7e7c, 0x2001, 0x0007, 0x1078, 0x4502,
+ 0x1078, 0x772d, 0x0078, 0x7e7e, 0x1078, 0x7dff, 0x007c, 0x7d4e,
+ 0x7e97, 0x7d4e, 0x7ed2, 0x7d4e, 0x7f44, 0x7e8b, 0x7d4e, 0x7d4e,
+ 0x7f59, 0x7d4e, 0x7f6c, 0x6604, 0xa686, 0x0003, 0x0040, 0x7e13,
+ 0xa6b6, 0x001e, 0x00c0, 0x7e96, 0x1078, 0x772d, 0x007c, 0x0d7e,
+ 0x0c7e, 0x1078, 0x7f7f, 0x00c0, 0x7ead, 0x2001, 0x0000, 0x1078,
+ 0x44ee, 0x2001, 0x0002, 0x1078, 0x4502, 0x6003, 0x0001, 0x6007,
+ 0x0002, 0x1078, 0x5dd7, 0x0078, 0x7ecf, 0x2009, 0xab8e, 0x2104,
+ 0xa086, 0x0009, 0x00c0, 0x7ec2, 0x6018, 0x2068, 0x6840, 0xa084,
+ 0x00ff, 0xa005, 0x0040, 0x7ecd, 0x8001, 0x6842, 0x6017, 0x000a,
+ 0x0078, 0x7ecf, 0x2009, 0xab8f, 0x2104, 0xa084, 0xff00, 0xa086,
+ 0x1900, 0x00c0, 0x7ecd, 0x0078, 0x7ea1, 0x1078, 0x7dff, 0x0c7f,
+ 0x0d7f, 0x007c, 0x1078, 0x7f8e, 0x00c0, 0x7ee6, 0x2001, 0x0000,
+ 0x1078, 0x44ee, 0x2001, 0x0002, 0x1078, 0x4502, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x1078, 0x5dd7, 0x0078, 0x7f12, 0x1078, 0x7c83,
+ 0x2009, 0xab8e, 0x2134, 0xa6b4, 0x00ff, 0xa686, 0x0005, 0x0040,
+ 0x7f13, 0xa686, 0x000b, 0x0040, 0x7f10, 0x2009, 0xab8f, 0x2104,
+ 0xa084, 0xff00, 0x00c0, 0x7f00, 0xa686, 0x0009, 0x0040, 0x7f13,
+ 0xa086, 0x1900, 0x00c0, 0x7f10, 0xa686, 0x0009, 0x0040, 0x7f13,
+ 0x2001, 0x0004, 0x1078, 0x4502, 0x1078, 0x772d, 0x0078, 0x7f12,
+ 0x1078, 0x7dff, 0x007c, 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8d06,
+ 0x0040, 0x7f21, 0x6838, 0xd0fc, 0x0040, 0x7f21, 0x0d7f, 0x0078,
+ 0x7f10, 0x6018, 0x2068, 0x6840, 0xa084, 0x00ff, 0xa005, 0x0040,
+ 0x7f32, 0x8001, 0x6842, 0x6017, 0x000a, 0x6007, 0x0016, 0x0d7f,
+ 0x0078, 0x7f12, 0x68a0, 0xa086, 0x007e, 0x00c0, 0x7f3f, 0x0e7e,
+ 0x2071, 0xa600, 0x1078, 0x42b8, 0x0e7f, 0x0078, 0x7f41, 0x1078,
+ 0x2880, 0x0d7f, 0x0078, 0x7f10, 0x1078, 0x7f8e, 0x00c0, 0x7f54,
+ 0x2001, 0x0004, 0x1078, 0x4502, 0x6003, 0x0001, 0x6007, 0x0003,
+ 0x1078, 0x5dd7, 0x0078, 0x7f58, 0x1078, 0x7c83, 0x1078, 0x7dff,
+ 0x007c, 0x1078, 0x7f8e, 0x00c0, 0x7f69, 0x2001, 0x0008, 0x1078,
+ 0x4502, 0x6003, 0x0001, 0x6007, 0x0005, 0x1078, 0x5dd7, 0x0078,
+ 0x7f6b, 0x1078, 0x7dff, 0x007c, 0x1078, 0x7f8e, 0x00c0, 0x7f7c,
+ 0x2001, 0x000a, 0x1078, 0x4502, 0x6003, 0x0001, 0x6007, 0x0001,
+ 0x1078, 0x5dd7, 0x0078, 0x7f7e, 0x1078, 0x7dff, 0x007c, 0x2009,
+ 0xab8e, 0x2104, 0xa086, 0x0003, 0x00c0, 0x7f8d, 0x2009, 0xab8f,
+ 0x2104, 0xa084, 0xff00, 0xa086, 0x2a00, 0x007c, 0xa085, 0x0001,
+ 0x007c, 0x0c7e, 0x017e, 0xac88, 0x0006, 0x2164, 0x1078, 0x45d6,
+ 0x017f, 0x0c7f, 0x007c, 0x0f7e, 0x0e7e, 0x0d7e, 0x037e, 0x017e,
+ 0x6018, 0x2068, 0x2071, 0xa633, 0x2e04, 0xa085, 0x0003, 0x2072,
+ 0x1078, 0x8014, 0x0040, 0x7fd9, 0x2009, 0xa633, 0x2104, 0xc0cd,
+ 0x200a, 0x2001, 0xa653, 0x2004, 0xd0a4, 0x0040, 0x7fc2, 0xa006,
+ 0x2020, 0x2009, 0x002a, 0x1078, 0xa21d, 0x2001, 0xa60c, 0x200c,
+ 0xc195, 0x2102, 0x2019, 0x002a, 0x2009, 0x0001, 0x1078, 0x284f,
+ 0x2071, 0xa600, 0x1078, 0x2677, 0x0c7e, 0x157e, 0x20a9, 0x0081,
+ 0x2009, 0x007f, 0x1078, 0x298e, 0x8108, 0x00f0, 0x7fd2, 0x157f,
+ 0x0c7f, 0x1078, 0x7f91, 0x6813, 0x00ff, 0x6817, 0xfffe, 0x2071,
+ 0xab80, 0x2079, 0x0100, 0x2e04, 0xa084, 0x00ff, 0x2069, 0xa61b,
+ 0x206a, 0x78e6, 0x007e, 0x8e70, 0x2e04, 0x2069, 0xa61c, 0x206a,
+ 0x78ea, 0xa084, 0xff00, 0x017f, 0xa105, 0x2009, 0xa626, 0x200a,
+ 0x2069, 0xab8e, 0x2071, 0xa89e, 0x6810, 0x2072, 0x6814, 0x7006,
+ 0x6818, 0x700a, 0x681c, 0x700e, 0x1078, 0x906e, 0x2001, 0x0006,
+ 0x1078, 0x4502, 0x1078, 0x28a6, 0x1078, 0x772d, 0x017f, 0x037f,
+ 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x027e, 0x037e, 0x0e7e, 0x157e,
+ 0x2019, 0xa626, 0x231c, 0x83ff, 0x0040, 0x803e, 0x2071, 0xab80,
+ 0x2e14, 0xa294, 0x00ff, 0x7004, 0xa084, 0xff00, 0xa205, 0xa306,
+ 0x00c0, 0x803e, 0x2011, 0xab96, 0xad98, 0x000a, 0x20a9, 0x0004,
+ 0x1078, 0x80de, 0x00c0, 0x803e, 0x2011, 0xab9a, 0xad98, 0x0006,
+ 0x20a9, 0x0004, 0x1078, 0x80de, 0x00c0, 0x803e, 0x157f, 0x0e7f,
+ 0x037f, 0x027f, 0x007c, 0x0e7e, 0x2071, 0xab8c, 0x7004, 0xa086,
+ 0x0014, 0x00c0, 0x8066, 0x7008, 0xa086, 0x0800, 0x00c0, 0x8066,
+ 0x700c, 0xd0ec, 0x0040, 0x8064, 0xa084, 0x0f00, 0xa086, 0x0100,
+ 0x00c0, 0x8064, 0x7024, 0xd0a4, 0x00c0, 0x8061, 0xd0ac, 0x0040,
+ 0x8064, 0xa006, 0x0078, 0x8066, 0xa085, 0x0001, 0x0e7f, 0x007c,
+ 0x0e7e, 0x0d7e, 0x0c7e, 0x077e, 0x057e, 0x047e, 0x027e, 0x007e,
+ 0x127e, 0x2091, 0x8000, 0x2029, 0xa8ba, 0x252c, 0x2021, 0xa8c0,
+ 0x2424, 0x2061, 0xad00, 0x2071, 0xa600, 0x7248, 0x7064, 0xa202,
+ 0x00c8, 0x80cc, 0x1078, 0xa242, 0x0040, 0x80c4, 0x671c, 0xa786,
+ 0x0001, 0x0040, 0x80c4, 0xa786, 0x0007, 0x0040, 0x80c4, 0x2500,
+ 0xac06, 0x0040, 0x80c4, 0x2400, 0xac06, 0x0040, 0x80c4, 0x0c7e,
+ 0x6000, 0xa086, 0x0004, 0x00c0, 0x809f, 0x1078, 0x1757, 0xa786,
+ 0x0008, 0x00c0, 0x80ae, 0x1078, 0x8f00, 0x00c0, 0x80ae, 0x0c7f,
+ 0x1078, 0x7c83, 0x1078, 0x8ec6, 0x0078, 0x80c4, 0x6010, 0x2068,
+ 0x1078, 0x8d06, 0x0040, 0x80c1, 0xa786, 0x0003, 0x00c0, 0x80d6,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x4a73, 0x1078,
+ 0x8eb9, 0x1078, 0x8ec6, 0x0c7f, 0xace0, 0x0010, 0x7058, 0xac02,
+ 0x00c8, 0x80cc, 0x0078, 0x807d, 0x127f, 0x007f, 0x027f, 0x047f,
+ 0x057f, 0x077f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0xa786, 0x0006,
+ 0x00c0, 0x80b8, 0x1078, 0xa1ca, 0x0078, 0x80c1, 0x220c, 0x2304,
+ 0xa106, 0x00c0, 0x80e9, 0x8210, 0x8318, 0x00f0, 0x80de, 0xa006,
+ 0x007c, 0x2304, 0xa102, 0x0048, 0x80f1, 0x2001, 0x0001, 0x0078,
+ 0x80f3, 0x2001, 0x0000, 0xa18d, 0x0001, 0x007c, 0x6004, 0xa08a,
+ 0x0044, 0x10c8, 0x1332, 0x1078, 0x8eec, 0x0040, 0x8105, 0x1078,
+ 0x8f00, 0x0040, 0x8112, 0x0078, 0x810b, 0x1078, 0x28a6, 0x1078,
+ 0x8f00, 0x0040, 0x8112, 0x1078, 0x61cd, 0x1078, 0x772d, 0x1078,
+ 0x62d1, 0x007c, 0x1078, 0x7c83, 0x0078, 0x810b, 0xa182, 0x0040,
+ 0x0079, 0x811a, 0x812d, 0x812d, 0x812d, 0x812d, 0x812d, 0x812d,
+ 0x812d, 0x812d, 0x812d, 0x812d, 0x812d, 0x812f, 0x812f, 0x812f,
+ 0x812f, 0x812d, 0x812d, 0x812d, 0x812f, 0x1078, 0x1332, 0x600b,
+ 0xffff, 0x6003, 0x0001, 0x6106, 0x1078, 0x5d8a, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x62d1, 0x127f, 0x007c, 0xa186, 0x0013, 0x00c0,
+ 0x8146, 0x6004, 0xa082, 0x0040, 0x0079, 0x81d1, 0xa186, 0x0027,
+ 0x00c0, 0x8168, 0x1078, 0x61cd, 0x1078, 0x2880, 0x0d7e, 0x6110,
+ 0x2168, 0x1078, 0x8d06, 0x0040, 0x8162, 0x6837, 0x0103, 0x684b,
+ 0x0029, 0x6847, 0x0000, 0x694c, 0xc1c5, 0x694e, 0x1078, 0x4a73,
+ 0x1078, 0x8eb9, 0x0d7f, 0x1078, 0x772d, 0x1078, 0x62d1, 0x007c,
+ 0xa186, 0x0014, 0x00c0, 0x8171, 0x6004, 0xa082, 0x0040, 0x0079,
+ 0x8199, 0xa186, 0x0046, 0x0040, 0x817d, 0xa186, 0x0045, 0x0040,
+ 0x817d, 0xa186, 0x0047, 0x10c0, 0x1332, 0x2001, 0x0109, 0x2004,
+ 0xd084, 0x0040, 0x8196, 0x127e, 0x2091, 0x2200, 0x007e, 0x017e,
+ 0x027e, 0x1078, 0x5c56, 0x027f, 0x017f, 0x007f, 0x127f, 0x6000,
+ 0xa086, 0x0002, 0x00c0, 0x8196, 0x0078, 0x820e, 0x1078, 0x7773,
+ 0x007c, 0x81ae, 0x81ac, 0x81ac, 0x81ac, 0x81ac, 0x81ac, 0x81ac,
+ 0x81ac, 0x81ac, 0x81ac, 0x81ac, 0x81ca, 0x81ca, 0x81ca, 0x81ca,
+ 0x81ac, 0x81ca, 0x81ac, 0x81ca, 0x1078, 0x1332, 0x1078, 0x61cd,
+ 0x0d7e, 0x6110, 0x2168, 0x1078, 0x8d06, 0x0040, 0x81c4, 0x6837,
+ 0x0103, 0x684b, 0x0006, 0x6847, 0x0000, 0x6850, 0xc0ec, 0x6852,
+ 0x1078, 0x4a73, 0x1078, 0x8eb9, 0x0d7f, 0x1078, 0x772d, 0x1078,
+ 0x62d1, 0x007c, 0x1078, 0x61cd, 0x1078, 0x772d, 0x1078, 0x62d1,
+ 0x007c, 0x81e6, 0x81e4, 0x81e4, 0x81e4, 0x81e4, 0x81e4, 0x81e4,
+ 0x81e4, 0x81e4, 0x81e4, 0x81e4, 0x81f8, 0x81f8, 0x81f8, 0x81f8,
+ 0x81e4, 0x8207, 0x81e4, 0x81f8, 0x1078, 0x1332, 0x1078, 0x61cd,
+ 0x2001, 0xa8a4, 0x2004, 0x603e, 0x6003, 0x0002, 0x1078, 0x62d1,
+ 0x6010, 0xa088, 0x0013, 0x2104, 0xa085, 0x0400, 0x200a, 0x007c,
+ 0x1078, 0x61cd, 0x2001, 0xa8a2, 0x2004, 0x6016, 0x2001, 0xa8a4,
+ 0x2004, 0x603e, 0x6003, 0x000f, 0x1078, 0x62d1, 0x007c, 0x1078,
+ 0x61cd, 0x1078, 0x772d, 0x1078, 0x62d1, 0x007c, 0xa182, 0x0040,
+ 0x0079, 0x8212, 0x8225, 0x8225, 0x8225, 0x8225, 0x8225, 0x8227,
+ 0x8327, 0x8359, 0x8225, 0x8225, 0x8225, 0x8225, 0x8225, 0x8225,
+ 0x8225, 0x8225, 0x8225, 0x8225, 0x8225, 0x1078, 0x1332, 0x0e7e,
+ 0x0d7e, 0x603f, 0x0000, 0x2071, 0xab80, 0x7124, 0x610a, 0x2071,
+ 0xab8c, 0x6110, 0x2168, 0x7614, 0xa6b4, 0x0fff, 0x86ff, 0x0040,
+ 0x82e9, 0xa68c, 0x0c00, 0x0040, 0x825e, 0x0f7e, 0x2c78, 0x1078,
+ 0x4963, 0x0f7f, 0x0040, 0x825a, 0x684c, 0xd0ac, 0x0040, 0x825a,
+ 0x6024, 0xd0dc, 0x00c0, 0x825a, 0x6850, 0xd0bc, 0x00c0, 0x825a,
+ 0x7318, 0x6814, 0xa306, 0x00c0, 0x8301, 0x731c, 0x6810, 0xa306,
+ 0x00c0, 0x8301, 0x7318, 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff,
+ 0xa186, 0x0002, 0x0040, 0x8291, 0xa186, 0x0028, 0x00c0, 0x826e,
+ 0x1078, 0x8eda, 0x684b, 0x001c, 0x0078, 0x8293, 0xd6dc, 0x0040,
+ 0x828a, 0x684b, 0x0015, 0x684c, 0xd0ac, 0x0040, 0x8288, 0x6914,
+ 0x6a10, 0x2100, 0xa205, 0x0040, 0x8288, 0x7018, 0xa106, 0x00c0,
+ 0x8285, 0x701c, 0xa206, 0x0040, 0x8288, 0x6962, 0x6a5e, 0xc6dc,
+ 0x0078, 0x8293, 0xd6d4, 0x0040, 0x8291, 0x684b, 0x0007, 0x0078,
+ 0x8293, 0x684b, 0x0000, 0x6837, 0x0103, 0x6e46, 0xa01e, 0xd6c4,
+ 0x0040, 0x82bc, 0xa686, 0x0100, 0x00c0, 0x82a7, 0x2001, 0xab99,
+ 0x2004, 0xa005, 0x00c0, 0x82a7, 0xc6c4, 0x0078, 0x8236, 0x7328,
+ 0x732c, 0x6b56, 0x83ff, 0x0040, 0x82bc, 0xa38a, 0x0009, 0x0048,
+ 0x82b3, 0x2019, 0x0008, 0x037e, 0x2308, 0x2019, 0xab98, 0xad90,
+ 0x0019, 0x1078, 0x89e2, 0x037f, 0xd6cc, 0x0040, 0x8317, 0x7124,
+ 0x695a, 0x81ff, 0x0040, 0x8317, 0xa192, 0x0021, 0x00c8, 0x82d5,
+ 0x2071, 0xab98, 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x1078,
+ 0x89e2, 0x1078, 0x91f4, 0x0078, 0x8317, 0x6838, 0xd0fc, 0x0040,
+ 0x82de, 0x2009, 0x0020, 0x695a, 0x0078, 0x82c8, 0x0f7e, 0x2d78,
+ 0x1078, 0x897a, 0x0f7f, 0x1078, 0x91f4, 0x1078, 0x89cf, 0x0078,
+ 0x8319, 0x0f7e, 0x2c78, 0x1078, 0x4963, 0x0f7f, 0x0040, 0x8307,
+ 0x684c, 0xd0ac, 0x0040, 0x8307, 0x6024, 0xd0dc, 0x00c0, 0x8307,
+ 0x6850, 0xd0bc, 0x00c0, 0x8307, 0x6810, 0x6914, 0xa105, 0x0040,
+ 0x8307, 0x1078, 0x8fbf, 0x0d7f, 0x0e7f, 0x0078, 0x8326, 0x684b,
+ 0x0000, 0x6837, 0x0103, 0x6e46, 0x684c, 0xd0ac, 0x0040, 0x8317,
+ 0x6810, 0x6914, 0xa115, 0x0040, 0x8317, 0x1078, 0x84d5, 0x1078,
+ 0x4a73, 0x6218, 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x1078, 0x8f89,
+ 0x0d7f, 0x0e7f, 0x00c0, 0x8326, 0x1078, 0x772d, 0x007c, 0x0f7e,
+ 0x6003, 0x0003, 0x2079, 0xab8c, 0x7c04, 0x7b00, 0x7e0c, 0x7d08,
+ 0x6010, 0x2078, 0x784c, 0xd0ac, 0x0040, 0x833e, 0x6003, 0x0002,
+ 0x0f7f, 0x007c, 0x2130, 0x2228, 0x0078, 0x834a, 0x2400, 0x797c,
+ 0xa10a, 0x2300, 0x7a80, 0xa213, 0x2600, 0xa102, 0x2500, 0xa203,
+ 0x0048, 0x833a, 0x7c12, 0x7b16, 0x7e0a, 0x7d0e, 0x0f7f, 0x603f,
+ 0x0000, 0x2c10, 0x1078, 0x1cf0, 0x1078, 0x5df6, 0x1078, 0x639b,
+ 0x007c, 0x2001, 0xa8a4, 0x2004, 0x603e, 0x6003, 0x0004, 0x6110,
+ 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x1078, 0x15fa, 0x007c,
+ 0xa182, 0x0040, 0x0079, 0x836c, 0x837f, 0x837f, 0x837f, 0x837f,
+ 0x837f, 0x8381, 0x8424, 0x837f, 0x837f, 0x843a, 0x84ab, 0x837f,
+ 0x837f, 0x837f, 0x837f, 0x84ba, 0x837f, 0x837f, 0x837f, 0x1078,
+ 0x1332, 0x077e, 0x0f7e, 0x0e7e, 0x0d7e, 0x2071, 0xab8c, 0x6110,
+ 0x2178, 0x7614, 0xa6b4, 0x0fff, 0x7e46, 0x7f4c, 0xc7e5, 0x7f4e,
+ 0x6218, 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x86ff, 0x0040, 0x841f,
+ 0xa694, 0xff00, 0xa284, 0x0c00, 0x0040, 0x83a2, 0x7018, 0x7862,
+ 0x701c, 0x785e, 0xa284, 0x0300, 0x0040, 0x841f, 0x1078, 0x138b,
+ 0x1040, 0x1332, 0x2d00, 0x784a, 0x7f4c, 0xc7cd, 0x7f4e, 0x6837,
+ 0x0103, 0x7838, 0x683a, 0x783c, 0x683e, 0x7840, 0x6842, 0x6e46,
+ 0xa68c, 0x0c00, 0x0040, 0x83c0, 0x7318, 0x6b62, 0x731c, 0x6b5e,
+ 0xa68c, 0x00ff, 0xa186, 0x0002, 0x0040, 0x83dc, 0xa186, 0x0028,
+ 0x00c0, 0x83ce, 0x684b, 0x001c, 0x0078, 0x83de, 0xd6dc, 0x0040,
+ 0x83d5, 0x684b, 0x0015, 0x0078, 0x83de, 0xd6d4, 0x0040, 0x83dc,
+ 0x684b, 0x0007, 0x0078, 0x83de, 0x684b, 0x0000, 0x6f4e, 0x7850,
+ 0x6852, 0x7854, 0x6856, 0xa01e, 0xd6c4, 0x0040, 0x83fc, 0x7328,
+ 0x732c, 0x6b56, 0x83ff, 0x0040, 0x83fc, 0xa38a, 0x0009, 0x0048,
+ 0x83f3, 0x2019, 0x0008, 0x037e, 0x2308, 0x2019, 0xab98, 0xad90,
+ 0x0019, 0x1078, 0x89e2, 0x037f, 0xd6cc, 0x0040, 0x841f, 0x7124,
+ 0x695a, 0x81ff, 0x0040, 0x841f, 0xa192, 0x0021, 0x00c8, 0x8413,
+ 0x2071, 0xab98, 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x1078,
+ 0x89e2, 0x0078, 0x841f, 0x7838, 0xd0fc, 0x0040, 0x841c, 0x2009,
+ 0x0020, 0x695a, 0x0078, 0x8408, 0x2d78, 0x1078, 0x897a, 0x0d7f,
+ 0x0e7f, 0x0f7f, 0x077f, 0x007c, 0x0f7e, 0x6003, 0x0003, 0x2079,
+ 0xab8c, 0x7c04, 0x7b00, 0x7e0c, 0x7d08, 0x6010, 0x2078, 0x7c12,
+ 0x7b16, 0x7e0a, 0x7d0e, 0x0f7f, 0x2c10, 0x1078, 0x1cf0, 0x1078,
+ 0x6df4, 0x007c, 0x0d7e, 0x0f7e, 0x2c78, 0x1078, 0x4963, 0x0f7f,
+ 0x0040, 0x8446, 0x2001, 0xa8a4, 0x2004, 0x603e, 0x6003, 0x0002,
+ 0x1078, 0x627a, 0x1078, 0x639b, 0x6110, 0x2168, 0x694c, 0xd1e4,
+ 0x0040, 0x84a9, 0xd1cc, 0x0040, 0x8480, 0x6948, 0x6838, 0xd0fc,
+ 0x0040, 0x8478, 0x017e, 0x684c, 0x007e, 0x6850, 0x007e, 0xad90,
+ 0x000d, 0xa198, 0x000d, 0x2009, 0x0020, 0x157e, 0x21a8, 0x2304,
+ 0x2012, 0x8318, 0x8210, 0x00f0, 0x8467, 0x157f, 0x007f, 0x6852,
+ 0x007f, 0x684e, 0x017f, 0x2168, 0x1078, 0x13b4, 0x0078, 0x84a3,
+ 0x017e, 0x1078, 0x13b4, 0x0d7f, 0x1078, 0x89cf, 0x0078, 0x84a3,
+ 0x6837, 0x0103, 0x6944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x0040,
+ 0x849f, 0xa086, 0x0028, 0x00c0, 0x8491, 0x684b, 0x001c, 0x0078,
+ 0x84a1, 0xd1dc, 0x0040, 0x8498, 0x684b, 0x0015, 0x0078, 0x84a1,
+ 0xd1d4, 0x0040, 0x849f, 0x684b, 0x0007, 0x0078, 0x84a1, 0x684b,
+ 0x0000, 0x1078, 0x4a73, 0x1078, 0x8f89, 0x00c0, 0x84a9, 0x1078,
+ 0x772d, 0x0d7f, 0x007c, 0x2019, 0x0001, 0x1078, 0x7058, 0x6003,
+ 0x0002, 0x2001, 0xa8a4, 0x2004, 0x603e, 0x1078, 0x627a, 0x1078,
+ 0x639b, 0x007c, 0x1078, 0x627a, 0x1078, 0x2880, 0x0d7e, 0x6110,
+ 0x2168, 0x1078, 0x8d06, 0x0040, 0x84cf, 0x6837, 0x0103, 0x684b,
+ 0x0029, 0x6847, 0x0000, 0x1078, 0x4a73, 0x1078, 0x8eb9, 0x0d7f,
+ 0x1078, 0x772d, 0x1078, 0x639b, 0x007c, 0x684b, 0x0015, 0xd1fc,
+ 0x0040, 0x84e1, 0x684b, 0x0007, 0x8002, 0x8000, 0x810a, 0xa189,
+ 0x0000, 0x6962, 0x685e, 0x007c, 0xa182, 0x0040, 0x0079, 0x84e8,
+ 0x84fb, 0x84fb, 0x84fb, 0x84fb, 0x84fb, 0x84fd, 0x84fb, 0x85d0,
+ 0x85dc, 0x84fb, 0x84fb, 0x84fb, 0x84fb, 0x84fb, 0x84fb, 0x84fb,
+ 0x84fb, 0x84fb, 0x84fb, 0x1078, 0x1332, 0x077e, 0x0f7e, 0x0e7e,
+ 0x0d7e, 0x2071, 0xab8c, 0x6110, 0x2178, 0x7614, 0xa6b4, 0x0fff,
+ 0x0f7e, 0x2c78, 0x1078, 0x4963, 0x0f7f, 0x0040, 0x851b, 0xa684,
+ 0x00ff, 0x00c0, 0x851b, 0x6024, 0xd0f4, 0x0040, 0x851b, 0x1078,
+ 0x8fbf, 0x0078, 0x85cb, 0x7e46, 0x7f4c, 0xc7e5, 0x7f4e, 0x6218,
+ 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x86ff, 0x0040, 0x85c0, 0xa694,
+ 0xff00, 0xa284, 0x0c00, 0x0040, 0x8531, 0x7018, 0x7862, 0x701c,
+ 0x785e, 0xa284, 0x0300, 0x0040, 0x85bd, 0xa686, 0x0100, 0x00c0,
+ 0x8543, 0x2001, 0xab99, 0x2004, 0xa005, 0x00c0, 0x8543, 0xc6c4,
+ 0x7e46, 0x0078, 0x8524, 0x1078, 0x138b, 0x1040, 0x1332, 0x2d00,
+ 0x784a, 0x7f4c, 0xa7bd, 0x0200, 0x7f4e, 0x6837, 0x0103, 0x7838,
+ 0x683a, 0x783c, 0x683e, 0x7840, 0x6842, 0x6e46, 0xa68c, 0x0c00,
+ 0x0040, 0x855e, 0x7318, 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff,
+ 0xa186, 0x0002, 0x0040, 0x857a, 0xa186, 0x0028, 0x00c0, 0x856c,
+ 0x684b, 0x001c, 0x0078, 0x857c, 0xd6dc, 0x0040, 0x8573, 0x684b,
+ 0x0015, 0x0078, 0x857c, 0xd6d4, 0x0040, 0x857a, 0x684b, 0x0007,
+ 0x0078, 0x857c, 0x684b, 0x0000, 0x6f4e, 0x7850, 0x6852, 0x7854,
+ 0x6856, 0xa01e, 0xd6c4, 0x0040, 0x859a, 0x7328, 0x732c, 0x6b56,
+ 0x83ff, 0x0040, 0x859a, 0xa38a, 0x0009, 0x0048, 0x8591, 0x2019,
+ 0x0008, 0x037e, 0x2308, 0x2019, 0xab98, 0xad90, 0x0019, 0x1078,
+ 0x89e2, 0x037f, 0xd6cc, 0x0040, 0x85bd, 0x7124, 0x695a, 0x81ff,
+ 0x0040, 0x85bd, 0xa192, 0x0021, 0x00c8, 0x85b1, 0x2071, 0xab98,
+ 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x1078, 0x89e2, 0x0078,
+ 0x85bd, 0x7838, 0xd0fc, 0x0040, 0x85ba, 0x2009, 0x0020, 0x695a,
+ 0x0078, 0x85a6, 0x2d78, 0x1078, 0x897a, 0xd6dc, 0x00c0, 0x85c3,
+ 0xa006, 0x0078, 0x85c9, 0x2001, 0x0001, 0x2071, 0xab8c, 0x7218,
+ 0x731c, 0x1078, 0x1653, 0x0d7f, 0x0e7f, 0x0f7f, 0x077f, 0x007c,
+ 0x2001, 0xa8a4, 0x2004, 0x603e, 0x20e1, 0x0005, 0x3d18, 0x3e20,
+ 0x2c10, 0x1078, 0x15fa, 0x007c, 0x2001, 0xa8a4, 0x2004, 0x603e,
+ 0x0d7e, 0x6003, 0x0002, 0x6110, 0x2168, 0x694c, 0xd1e4, 0x0040,
+ 0x870c, 0x603f, 0x0000, 0x0f7e, 0x2c78, 0x1078, 0x4963, 0x0f7f,
+ 0x0040, 0x8622, 0x6814, 0x6910, 0xa115, 0x0040, 0x8622, 0x6a60,
+ 0xa206, 0x00c0, 0x85ff, 0x685c, 0xa106, 0x0040, 0x8622, 0x684c,
+ 0xc0e4, 0x684e, 0x6847, 0x0000, 0x6863, 0x0000, 0x685f, 0x0000,
+ 0x6024, 0xd0f4, 0x00c0, 0x8617, 0x697c, 0x6810, 0xa102, 0x603a,
+ 0x6980, 0x6814, 0xa103, 0x6036, 0x6024, 0xc0f5, 0x6026, 0x0d7e,
+ 0x6018, 0x2068, 0x683c, 0x8000, 0x683e, 0x0d7f, 0x1078, 0x8fbf,
+ 0x0078, 0x870c, 0x694c, 0xd1cc, 0x0040, 0x86d1, 0x6948, 0x6838,
+ 0xd0fc, 0x0040, 0x8689, 0x017e, 0x684c, 0x007e, 0x6850, 0x007e,
+ 0x0f7e, 0x2178, 0x7944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x0040,
+ 0x865c, 0xa086, 0x0028, 0x00c0, 0x8643, 0x684b, 0x001c, 0x784b,
+ 0x001c, 0x0078, 0x8667, 0xd1dc, 0x0040, 0x8653, 0x684b, 0x0015,
+ 0x784b, 0x0015, 0x1078, 0x916c, 0x0040, 0x8651, 0x7944, 0xc1dc,
+ 0x7946, 0x0078, 0x8667, 0xd1d4, 0x0040, 0x865c, 0x684b, 0x0007,
+ 0x784b, 0x0007, 0x0078, 0x8667, 0x684c, 0xd0ac, 0x0040, 0x8667,
+ 0x6810, 0x6914, 0xa115, 0x0040, 0x8667, 0x1078, 0x84d5, 0x6848,
+ 0x784a, 0x6860, 0x7862, 0x685c, 0x785e, 0xad90, 0x000d, 0xaf98,
+ 0x000d, 0x2009, 0x0020, 0x157e, 0x21a8, 0x2304, 0x2012, 0x8318,
+ 0x8210, 0x00f0, 0x8675, 0x157f, 0x0f7f, 0x007f, 0x6852, 0x007f,
+ 0x684e, 0x1078, 0x91f4, 0x017f, 0x2168, 0x1078, 0x13b4, 0x0078,
+ 0x8706, 0x017e, 0x0f7e, 0x2178, 0x7944, 0xa184, 0x00ff, 0xa0b6,
+ 0x0002, 0x0040, 0x86b6, 0xa086, 0x0028, 0x00c0, 0x869d, 0x684b,
+ 0x001c, 0x784b, 0x001c, 0x0078, 0x86c1, 0xd1dc, 0x0040, 0x86ad,
+ 0x684b, 0x0015, 0x784b, 0x0015, 0x1078, 0x916c, 0x0040, 0x86ab,
+ 0x7944, 0xc1dc, 0x7946, 0x0078, 0x86c1, 0xd1d4, 0x0040, 0x86b6,
+ 0x684b, 0x0007, 0x784b, 0x0007, 0x0078, 0x86c1, 0x684c, 0xd0ac,
+ 0x0040, 0x86c1, 0x6810, 0x6914, 0xa115, 0x0040, 0x86c1, 0x1078,
+ 0x84d5, 0x6860, 0x7862, 0x685c, 0x785e, 0x684c, 0x784e, 0x0f7f,
+ 0x1078, 0x13b4, 0x0d7f, 0x1078, 0x91f4, 0x1078, 0x89cf, 0x0078,
+ 0x8706, 0x6837, 0x0103, 0x6944, 0xa184, 0x00ff, 0xa0b6, 0x0002,
+ 0x0040, 0x86f7, 0xa086, 0x0028, 0x00c0, 0x86e2, 0x684b, 0x001c,
+ 0x0078, 0x8704, 0xd1dc, 0x0040, 0x86f0, 0x684b, 0x0015, 0x1078,
+ 0x916c, 0x0040, 0x86ee, 0x6944, 0xc1dc, 0x6946, 0x0078, 0x8704,
+ 0xd1d4, 0x0040, 0x86f7, 0x684b, 0x0007, 0x0078, 0x8704, 0x684b,
+ 0x0000, 0x684c, 0xd0ac, 0x0040, 0x8704, 0x6810, 0x6914, 0xa115,
+ 0x0040, 0x8704, 0x1078, 0x84d5, 0x1078, 0x4a73, 0x1078, 0x8f89,
+ 0x00c0, 0x870c, 0x1078, 0x772d, 0x0d7f, 0x007c, 0x1078, 0x61cd,
+ 0x0078, 0x8714, 0x1078, 0x627a, 0x1078, 0x8d06, 0x0040, 0x8733,
+ 0x0d7e, 0x6110, 0x2168, 0x6837, 0x0103, 0x2009, 0xa60c, 0x210c,
+ 0xd18c, 0x00c0, 0x873e, 0xd184, 0x00c0, 0x873a, 0x6108, 0x694a,
+ 0xa18e, 0x0029, 0x00c0, 0x872e, 0x1078, 0xa4e2, 0x6847, 0x0000,
+ 0x1078, 0x4a73, 0x0d7f, 0x1078, 0x772d, 0x1078, 0x62d1, 0x1078,
+ 0x639b, 0x007c, 0x684b, 0x0004, 0x0078, 0x872e, 0x684b, 0x0004,
+ 0x0078, 0x872e, 0xa182, 0x0040, 0x0079, 0x8746, 0x8759, 0x8759,
+ 0x8759, 0x8759, 0x8759, 0x875b, 0x8759, 0x875e, 0x8759, 0x8759,
+ 0x8759, 0x8759, 0x8759, 0x8759, 0x8759, 0x8759, 0x8759, 0x8759,
+ 0x8759, 0x1078, 0x1332, 0x1078, 0x772d, 0x007c, 0x007e, 0x027e,
+ 0xa016, 0x1078, 0x15fa, 0x027f, 0x007f, 0x007c, 0xa182, 0x0085,
+ 0x0079, 0x876a, 0x8773, 0x8771, 0x8771, 0x877f, 0x8771, 0x8771,
+ 0x8771, 0x1078, 0x1332, 0x6003, 0x0001, 0x6106, 0x1078, 0x5d8a,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x62d1, 0x127f, 0x007c, 0x027e,
+ 0x057e, 0x0d7e, 0x0e7e, 0x2071, 0xab80, 0x7224, 0x6212, 0x7220,
+ 0x1078, 0x8cf2, 0x0040, 0x87a4, 0x2268, 0x6800, 0xa086, 0x0000,
+ 0x0040, 0x87a4, 0x6018, 0x6d18, 0xa52e, 0x00c0, 0x87a4, 0x0c7e,
+ 0x2d60, 0x1078, 0x89f3, 0x0c7f, 0x0040, 0x87a4, 0x6803, 0x0002,
+ 0x6007, 0x0086, 0x0078, 0x87a6, 0x6007, 0x0087, 0x6003, 0x0001,
+ 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0f7e, 0x2278, 0x1078, 0x4963,
+ 0x0f7f, 0x0040, 0x87be, 0x6824, 0xd0ec, 0x0040, 0x87be, 0x0c7e,
+ 0x2260, 0x603f, 0x0000, 0x1078, 0x8fbf, 0x0c7f, 0x0e7f, 0x0d7f,
+ 0x057f, 0x027f, 0x007c, 0xa186, 0x0013, 0x00c0, 0x87d4, 0x6004,
+ 0xa08a, 0x0085, 0x1048, 0x1332, 0xa08a, 0x008c, 0x10c8, 0x1332,
+ 0xa082, 0x0085, 0x0079, 0x87e3, 0xa186, 0x0027, 0x0040, 0x87dc,
+ 0xa186, 0x0014, 0x10c0, 0x1332, 0x1078, 0x61cd, 0x1078, 0x8ec6,
+ 0x1078, 0x62d1, 0x007c, 0x87ea, 0x87ec, 0x87ec, 0x87ea, 0x87ea,
+ 0x87ea, 0x87ea, 0x1078, 0x1332, 0x1078, 0x61cd, 0x1078, 0x8ec6,
+ 0x1078, 0x62d1, 0x007c, 0xa186, 0x0013, 0x00c0, 0x87fd, 0x6004,
+ 0xa082, 0x0085, 0x2008, 0x0078, 0x8838, 0xa186, 0x0027, 0x00c0,
+ 0x8820, 0x1078, 0x61cd, 0x1078, 0x2880, 0x0d7e, 0x6010, 0x2068,
+ 0x1078, 0x8d06, 0x0040, 0x8816, 0x6837, 0x0103, 0x6847, 0x0000,
+ 0x684b, 0x0029, 0x1078, 0x4a73, 0x1078, 0x8eb9, 0x0d7f, 0x1078,
+ 0x772d, 0x1078, 0x62d1, 0x007c, 0x1078, 0x7773, 0x0078, 0x881b,
+ 0xa186, 0x0014, 0x00c0, 0x881c, 0x1078, 0x61cd, 0x0d7e, 0x6010,
+ 0x2068, 0x1078, 0x8d06, 0x0040, 0x8816, 0x6837, 0x0103, 0x6847,
+ 0x0000, 0x684b, 0x0006, 0x6850, 0xc0ec, 0x6852, 0x0078, 0x8812,
+ 0x0079, 0x883a, 0x8843, 0x8841, 0x8841, 0x8841, 0x8841, 0x8841,
+ 0x885e, 0x1078, 0x1332, 0x1078, 0x61cd, 0x6030, 0xa08c, 0xff00,
+ 0x810f, 0xa186, 0x0039, 0x0040, 0x8851, 0xa186, 0x0035, 0x00c0,
+ 0x8855, 0x2001, 0xa8a2, 0x0078, 0x8857, 0x2001, 0xa8a3, 0x2004,
+ 0x6016, 0x6003, 0x000c, 0x1078, 0x62d1, 0x007c, 0x1078, 0x61cd,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0040, 0x886c,
+ 0xa186, 0x0035, 0x00c0, 0x8870, 0x2001, 0xa8a2, 0x0078, 0x8872,
+ 0x2001, 0xa8a3, 0x2004, 0x6016, 0x6003, 0x000e, 0x1078, 0x62d1,
+ 0x007c, 0xa182, 0x008c, 0x00c8, 0x8883, 0xa182, 0x0085, 0x0048,
+ 0x8883, 0x0079, 0x8886, 0x1078, 0x7773, 0x007c, 0x888d, 0x888d,
+ 0x888d, 0x888d, 0x888f, 0x88ec, 0x888d, 0x1078, 0x1332, 0x0f7e,
+ 0x2c78, 0x1078, 0x4963, 0x0f7f, 0x0040, 0x88a2, 0x6030, 0xa08c,
+ 0xff00, 0x810f, 0xa186, 0x0039, 0x0040, 0x8903, 0xa186, 0x0035,
+ 0x0040, 0x8903, 0x0d7e, 0x1078, 0x8d06, 0x00c0, 0x88ab, 0x1078,
+ 0x8eb9, 0x0078, 0x88ce, 0x6010, 0x2068, 0x684c, 0xd0e4, 0x00c0,
+ 0x88b3, 0x1078, 0x8eb9, 0x6837, 0x0103, 0x6850, 0xd0b4, 0x0040,
+ 0x88bf, 0x684b, 0x0006, 0xc0ec, 0x6852, 0x0078, 0x88ca, 0xd0bc,
+ 0x0040, 0x88c6, 0x684b, 0x0002, 0x0078, 0x88ca, 0x684b, 0x0005,
+ 0x1078, 0x8f85, 0x6847, 0x0000, 0x1078, 0x4a73, 0x2c68, 0x1078,
+ 0x76c7, 0x0040, 0x88e7, 0x6003, 0x0001, 0x6007, 0x001e, 0x2009,
+ 0xab8e, 0x210c, 0x6136, 0x2009, 0xab8f, 0x210c, 0x613a, 0x6918,
+ 0x611a, 0x6920, 0x6122, 0x601f, 0x0001, 0x1078, 0x5d8a, 0x2d60,
+ 0x1078, 0x772d, 0x0d7f, 0x007c, 0x0f7e, 0x2c78, 0x1078, 0x4963,
+ 0x0f7f, 0x0040, 0x8929, 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186,
+ 0x0035, 0x0040, 0x8903, 0xa186, 0x001e, 0x0040, 0x8903, 0xa186,
+ 0x0039, 0x00c0, 0x8929, 0x0d7e, 0x2c68, 0x1078, 0x91bc, 0x00c0,
+ 0x894d, 0x1078, 0x76c7, 0x0040, 0x8926, 0x6106, 0x6003, 0x0001,
+ 0x601f, 0x0001, 0x6918, 0x611a, 0x6928, 0x612a, 0x692c, 0x612e,
+ 0x6930, 0xa18c, 0x00ff, 0x6132, 0x6934, 0x6136, 0x6938, 0x613a,
+ 0x6920, 0x6122, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x2d60, 0x0078,
+ 0x894d, 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040, 0x894d,
+ 0x6837, 0x0103, 0x6850, 0xd0b4, 0x0040, 0x893c, 0xc0ec, 0x6852,
+ 0x684b, 0x0006, 0x0078, 0x8947, 0xd0bc, 0x0040, 0x8943, 0x684b,
+ 0x0002, 0x0078, 0x8947, 0x684b, 0x0005, 0x1078, 0x8f85, 0x6847,
+ 0x0000, 0x1078, 0x4a73, 0x1078, 0x8eb9, 0x0d7f, 0x1078, 0x772d,
+ 0x007c, 0x017e, 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040,
+ 0x8961, 0x6837, 0x0103, 0x684b, 0x0028, 0x6847, 0x0000, 0x1078,
+ 0x4a73, 0x0d7f, 0x017f, 0xa186, 0x0013, 0x0040, 0x8973, 0xa186,
+ 0x0014, 0x0040, 0x8973, 0xa186, 0x0027, 0x0040, 0x8973, 0x1078,
+ 0x7773, 0x0078, 0x8979, 0x1078, 0x61cd, 0x1078, 0x8ec6, 0x1078,
+ 0x62d1, 0x007c, 0x057e, 0x067e, 0x0d7e, 0x0f7e, 0x2029, 0x0001,
+ 0xa182, 0x0101, 0x00c8, 0x8986, 0x0078, 0x8988, 0x2009, 0x0100,
+ 0x2130, 0x2069, 0xab98, 0x831c, 0x2300, 0xad18, 0x2009, 0x0020,
+ 0xaf90, 0x001d, 0x1078, 0x89e2, 0xa6b2, 0x0020, 0x7804, 0xa06d,
+ 0x0040, 0x899c, 0x1078, 0x13b4, 0x1078, 0x138b, 0x0040, 0x89c6,
+ 0x8528, 0x6837, 0x0110, 0x683b, 0x0000, 0x2d20, 0x7c06, 0xa68a,
+ 0x003d, 0x00c8, 0x89b2, 0x2608, 0xad90, 0x000f, 0x1078, 0x89e2,
+ 0x0078, 0x89c6, 0xa6b2, 0x003c, 0x2009, 0x003c, 0x2d78, 0xad90,
+ 0x000f, 0x1078, 0x89e2, 0x0078, 0x899c, 0x0f7f, 0x852f, 0xa5ad,
+ 0x0003, 0x7d36, 0xa5ac, 0x0000, 0x0078, 0x89cb, 0x0f7f, 0x852f,
+ 0xa5ad, 0x0003, 0x7d36, 0x0d7f, 0x067f, 0x057f, 0x007c, 0x0f7e,
+ 0x8dff, 0x0040, 0x89e0, 0x6804, 0xa07d, 0x0040, 0x89de, 0x6807,
+ 0x0000, 0x1078, 0x4a73, 0x2f68, 0x0078, 0x89d3, 0x1078, 0x4a73,
+ 0x0f7f, 0x007c, 0x157e, 0xa184, 0x0001, 0x0040, 0x89e8, 0x8108,
+ 0x810c, 0x21a8, 0x2304, 0x8007, 0x2012, 0x8318, 0x8210, 0x00f0,
+ 0x89ea, 0x157f, 0x007c, 0x067e, 0x127e, 0x2091, 0x8000, 0x2031,
+ 0x0001, 0x601c, 0xa084, 0x000f, 0x1079, 0x8a0f, 0x127f, 0x067f,
+ 0x007c, 0x127e, 0x2091, 0x8000, 0x067e, 0x2031, 0x0000, 0x601c,
+ 0xa084, 0x000f, 0x1079, 0x8a0f, 0x067f, 0x127f, 0x007c, 0x8a29,
+ 0x8a17, 0x8a24, 0x8a45, 0x8a17, 0x8a24, 0x8a45, 0x8a24, 0x1078,
+ 0x1332, 0x037e, 0x2019, 0x0010, 0x1078, 0x9dc7, 0x601f, 0x0006,
+ 0x6003, 0x0007, 0x037f, 0x007c, 0xa006, 0x007c, 0xa085, 0x0001,
+ 0x007c, 0x0d7e, 0x86ff, 0x00c0, 0x8a40, 0x6010, 0x2068, 0x1078,
+ 0x8d06, 0x0040, 0x8a42, 0xa00e, 0x2001, 0x0005, 0x1078, 0x4b51,
+ 0x1078, 0x8f85, 0x1078, 0x4a73, 0x1078, 0x772d, 0xa085, 0x0001,
+ 0x0d7f, 0x007c, 0xa006, 0x0078, 0x8a40, 0x6000, 0xa08a, 0x0010,
+ 0x10c8, 0x1332, 0x1079, 0x8a4d, 0x007c, 0x8a5d, 0x8a82, 0x8a5f,
+ 0x8aa5, 0x8a7e, 0x8a5d, 0x8a24, 0x8a29, 0x8a29, 0x8a24, 0x8a24,
+ 0x8a24, 0x8a24, 0x8a24, 0x8a24, 0x8a24, 0x1078, 0x1332, 0x86ff,
+ 0x00c0, 0x8a7b, 0x601c, 0xa086, 0x0006, 0x0040, 0x8a7b, 0x0d7e,
+ 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040, 0x8a70, 0x1078, 0x8f85,
+ 0x0d7f, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x1078,
+ 0x5d8a, 0x1078, 0x62d1, 0xa085, 0x0001, 0x007c, 0x1078, 0x1757,
+ 0x0078, 0x8a5f, 0x0e7e, 0x2071, 0xa8b1, 0x7024, 0xac06, 0x00c0,
+ 0x8a8b, 0x1078, 0x6fc4, 0x601c, 0xa084, 0x000f, 0xa086, 0x0006,
+ 0x00c0, 0x8a9d, 0x087e, 0x097e, 0x2049, 0x0001, 0x2c40, 0x1078,
+ 0x7246, 0x097f, 0x087f, 0x0078, 0x8a9f, 0x1078, 0x6ebe, 0x0e7f,
+ 0x00c0, 0x8a5f, 0x1078, 0x8a24, 0x007c, 0x037e, 0x0e7e, 0x2071,
+ 0xa8b1, 0x703c, 0xac06, 0x00c0, 0x8ab5, 0x2019, 0x0000, 0x1078,
+ 0x7058, 0x0e7f, 0x037f, 0x0078, 0x8a5f, 0x1078, 0x738a, 0x0e7f,
+ 0x037f, 0x00c0, 0x8a5f, 0x1078, 0x8a24, 0x007c, 0x0c7e, 0x601c,
+ 0xa084, 0x000f, 0x1079, 0x8ac6, 0x0c7f, 0x007c, 0x8ad5, 0x8b47,
+ 0x8c7f, 0x8ae0, 0x8ec6, 0x8ad5, 0x9db8, 0x772d, 0x8b47, 0x1078,
+ 0x8f00, 0x00c0, 0x8ad5, 0x1078, 0x7c83, 0x007c, 0x1078, 0x61cd,
+ 0x1078, 0x62d1, 0x1078, 0x772d, 0x007c, 0x6017, 0x0001, 0x007c,
+ 0x1078, 0x8d06, 0x0040, 0x8ae8, 0x6010, 0xa080, 0x0019, 0x2c02,
+ 0x6000, 0xa08a, 0x0010, 0x10c8, 0x1332, 0x1079, 0x8af0, 0x007c,
+ 0x8b00, 0x8b02, 0x8b24, 0x8b36, 0x8b43, 0x8b00, 0x8ad5, 0x8ad5,
+ 0x8ad5, 0x8b36, 0x8b36, 0x8b00, 0x8b00, 0x8b00, 0x8b00, 0x8b40,
+ 0x1078, 0x1332, 0x0e7e, 0x6010, 0x2070, 0x7050, 0xc0b5, 0x7052,
+ 0x2071, 0xa8b1, 0x7024, 0xac06, 0x0040, 0x8b20, 0x1078, 0x6ebe,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x2001, 0xa8a3,
+ 0x2004, 0x6016, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0e7f, 0x007c,
+ 0x6017, 0x0001, 0x0078, 0x8b1e, 0x0d7e, 0x6010, 0x2068, 0x6850,
+ 0xc0b5, 0x6852, 0x0d7f, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f,
+ 0x0002, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x007c, 0x0d7e, 0x6017,
+ 0x0001, 0x6010, 0x2068, 0x6850, 0xc0b5, 0x6852, 0x0d7f, 0x007c,
+ 0x1078, 0x772d, 0x007c, 0x1078, 0x1757, 0x0078, 0x8b24, 0x6000,
+ 0xa08a, 0x0010, 0x10c8, 0x1332, 0x1079, 0x8b4f, 0x007c, 0x8b5f,
+ 0x8add, 0x8b61, 0x8b5f, 0x8b61, 0x8b61, 0x8ad6, 0x8b5f, 0x8acf,
+ 0x8acf, 0x8b5f, 0x8b5f, 0x8b5f, 0x8b5f, 0x8b5f, 0x8b5f, 0x1078,
+ 0x1332, 0x0d7e, 0x6018, 0x2068, 0x6804, 0xa084, 0x00ff, 0x0d7f,
+ 0xa08a, 0x000c, 0x10c8, 0x1332, 0x1079, 0x8b6f, 0x007c, 0x8b7b,
+ 0x8c23, 0x8b7d, 0x8bbd, 0x8b7d, 0x8bbd, 0x8b7d, 0x8b8a, 0x8b7b,
+ 0x8bbd, 0x8b7b, 0x8ba7, 0x1078, 0x1332, 0x6004, 0xa08e, 0x0016,
+ 0x0040, 0x8bb8, 0xa08e, 0x0004, 0x0040, 0x8bb8, 0xa08e, 0x0002,
+ 0x0040, 0x8bb8, 0x6004, 0x1078, 0x8f00, 0x0040, 0x8c3e, 0xa08e,
+ 0x0021, 0x0040, 0x8c42, 0xa08e, 0x0022, 0x0040, 0x8c3e, 0xa08e,
+ 0x003d, 0x0040, 0x8c42, 0xa08e, 0x0039, 0x0040, 0x8c46, 0xa08e,
+ 0x0035, 0x0040, 0x8c46, 0xa08e, 0x001e, 0x0040, 0x8bba, 0xa08e,
+ 0x0001, 0x00c0, 0x8bb6, 0x0d7e, 0x6018, 0x2068, 0x6804, 0xa084,
+ 0x00ff, 0x0d7f, 0xa086, 0x0006, 0x0040, 0x8bb8, 0x1078, 0x2880,
+ 0x1078, 0x7c83, 0x1078, 0x8ec6, 0x007c, 0x0c7e, 0x0d7e, 0x6104,
+ 0xa186, 0x0016, 0x0040, 0x8c13, 0xa186, 0x0002, 0x00c0, 0x8be6,
+ 0x6018, 0x2068, 0x68a0, 0xd0bc, 0x00c0, 0x8c6a, 0x6840, 0xa084,
+ 0x00ff, 0xa005, 0x0040, 0x8be6, 0x8001, 0x6842, 0x6013, 0x0000,
+ 0x601f, 0x0007, 0x6017, 0x0398, 0x1078, 0x76c7, 0x0040, 0x8be6,
+ 0x2d00, 0x601a, 0x601f, 0x0001, 0x0078, 0x8c13, 0x0d7f, 0x0c7f,
+ 0x6004, 0xa08e, 0x0002, 0x00c0, 0x8c04, 0x6018, 0xa080, 0x0028,
+ 0x2004, 0xa086, 0x007e, 0x00c0, 0x8c04, 0x2009, 0xa633, 0x2104,
+ 0xc085, 0x200a, 0x0e7e, 0x2071, 0xa600, 0x1078, 0x42b8, 0x0e7f,
+ 0x1078, 0x7c83, 0x0078, 0x8c08, 0x1078, 0x7c83, 0x1078, 0x2880,
+ 0x0e7e, 0x127e, 0x2091, 0x8000, 0x1078, 0x28a6, 0x127f, 0x0e7f,
+ 0x1078, 0x8ec6, 0x007c, 0x2001, 0x0002, 0x1078, 0x4502, 0x6003,
+ 0x0001, 0x6007, 0x0002, 0x1078, 0x5dd7, 0x1078, 0x62d1, 0x0d7f,
+ 0x0c7f, 0x0078, 0x8c12, 0x0c7e, 0x0d7e, 0x6104, 0xa186, 0x0016,
+ 0x0040, 0x8c13, 0x6018, 0x2068, 0x6840, 0xa084, 0x00ff, 0xa005,
+ 0x0040, 0x8be6, 0x8001, 0x6842, 0x6003, 0x0001, 0x1078, 0x5dd7,
+ 0x1078, 0x62d1, 0x0d7f, 0x0c7f, 0x0078, 0x8c12, 0x1078, 0x7c83,
+ 0x0078, 0x8bba, 0x1078, 0x7ca6, 0x0078, 0x8bba, 0x0d7e, 0x2c68,
+ 0x6104, 0x1078, 0x91bc, 0x0d7f, 0x0040, 0x8c52, 0x1078, 0x772d,
+ 0x0078, 0x8c69, 0x6004, 0x8007, 0x6130, 0xa18c, 0x00ff, 0xa105,
+ 0x6032, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x6038,
+ 0x600a, 0x2001, 0xa8a3, 0x2004, 0x6016, 0x1078, 0x5d8a, 0x1078,
+ 0x62d1, 0x007c, 0x0d7f, 0x0c7f, 0x1078, 0x7c83, 0x1078, 0x2880,
+ 0x0e7e, 0x127e, 0x2091, 0x8000, 0x1078, 0x28a6, 0x6013, 0x0000,
+ 0x601f, 0x0007, 0x6017, 0x0398, 0x127f, 0x0e7f, 0x007c, 0x6000,
+ 0xa08a, 0x0010, 0x10c8, 0x1332, 0x1079, 0x8c87, 0x007c, 0x8c97,
+ 0x8c97, 0x8c97, 0x8c97, 0x8c97, 0x8c97, 0x8c97, 0x8c97, 0x8c97,
+ 0x8ad5, 0x8c97, 0x8add, 0x8c99, 0x8add, 0x8ca7, 0x8c97, 0x1078,
+ 0x1332, 0x6004, 0xa086, 0x008b, 0x0040, 0x8ca7, 0x6007, 0x008b,
+ 0x6003, 0x000d, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x007c, 0x1078,
+ 0x8eb9, 0x1078, 0x8d06, 0x0040, 0x8cdf, 0x1078, 0x2880, 0x0d7e,
+ 0x1078, 0x8d06, 0x0040, 0x8cc1, 0x6010, 0x2068, 0x6837, 0x0103,
+ 0x684b, 0x0006, 0x6847, 0x0000, 0x6850, 0xc0ed, 0x6852, 0x1078,
+ 0x4a73, 0x2c68, 0x1078, 0x76c7, 0x0040, 0x8ccf, 0x6818, 0x601a,
+ 0x0c7e, 0x2d60, 0x1078, 0x8ec6, 0x0c7f, 0x0078, 0x8cd0, 0x2d60,
+ 0x0d7f, 0x6013, 0x0000, 0x601f, 0x0001, 0x6007, 0x0001, 0x6003,
+ 0x0001, 0x1078, 0x5dd7, 0x1078, 0x62d1, 0x0078, 0x8cf1, 0x6030,
+ 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0040, 0x8ceb, 0xa186,
+ 0x0035, 0x00c0, 0x8cef, 0x1078, 0x2880, 0x0078, 0x8cc1, 0x1078,
+ 0x8ec6, 0x007c, 0xa284, 0x000f, 0x00c0, 0x8d03, 0xa282, 0xad00,
+ 0x0048, 0x8d03, 0x2001, 0xa616, 0x2004, 0xa202, 0x00c8, 0x8d03,
+ 0xa085, 0x0001, 0x007c, 0xa006, 0x0078, 0x8d02, 0x027e, 0x0e7e,
+ 0x2071, 0xa600, 0x6210, 0x705c, 0xa202, 0x0048, 0x8d18, 0x7060,
+ 0xa202, 0x00c8, 0x8d18, 0xa085, 0x0001, 0x0e7f, 0x027f, 0x007c,
+ 0xa006, 0x0078, 0x8d15, 0x0e7e, 0x0c7e, 0x037e, 0x007e, 0x127e,
+ 0x2091, 0x8000, 0x2061, 0xad00, 0x2071, 0xa600, 0x7348, 0x7064,
+ 0xa302, 0x00c8, 0x8d45, 0x601c, 0xa206, 0x00c0, 0x8d3d, 0x1078,
+ 0x902b, 0x0040, 0x8d3d, 0x1078, 0x8f00, 0x00c0, 0x8d39, 0x1078,
+ 0x7c83, 0x0c7e, 0x1078, 0x772d, 0x0c7f, 0xace0, 0x0010, 0x7058,
+ 0xac02, 0x00c8, 0x8d45, 0x0078, 0x8d26, 0x127f, 0x007f, 0x037f,
+ 0x0c7f, 0x0e7f, 0x007c, 0x0e7e, 0x0c7e, 0x017e, 0xa188, 0xa735,
+ 0x210c, 0x81ff, 0x0040, 0x8d59, 0x2061, 0xa9b3, 0x611a, 0x1078,
+ 0x2880, 0xa006, 0x0078, 0x8d5e, 0xa085, 0x0001, 0x017f, 0x0c7f,
+ 0x0e7f, 0x007c, 0x0c7e, 0x057e, 0x127e, 0x2091, 0x8000, 0x0c7e,
+ 0x1078, 0x76c7, 0x057f, 0x0040, 0x8d7b, 0x6612, 0x651a, 0x601f,
+ 0x0003, 0x2009, 0x004b, 0x1078, 0x775c, 0xa085, 0x0001, 0x127f,
+ 0x057f, 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8d77, 0x0c7e, 0x057e,
+ 0x127e, 0x2091, 0x8000, 0x62a0, 0x0c7e, 0x1078, 0x76c7, 0x057f,
+ 0x0040, 0x8da9, 0x6013, 0x0000, 0x651a, 0x601f, 0x0003, 0x0c7e,
+ 0x2560, 0x1078, 0x47e9, 0x0c7f, 0x1078, 0x5f01, 0x077e, 0x2039,
+ 0x0000, 0x1078, 0x5e0a, 0x2c08, 0x1078, 0x9f8b, 0x077f, 0x2009,
+ 0x004c, 0x1078, 0x775c, 0xa085, 0x0001, 0x127f, 0x057f, 0x0c7f,
+ 0x007c, 0xa006, 0x0078, 0x8da5, 0x0f7e, 0x0c7e, 0x047e, 0x0c7e,
+ 0x1078, 0x76c7, 0x2c78, 0x0c7f, 0x0040, 0x8dc6, 0x7e12, 0x2c00,
+ 0x781a, 0x781f, 0x0003, 0x2021, 0x0005, 0x1078, 0x8e11, 0x2f60,
+ 0x2009, 0x004d, 0x1078, 0x775c, 0xa085, 0x0001, 0x047f, 0x0c7f,
+ 0x0f7f, 0x007c, 0x0f7e, 0x0c7e, 0x047e, 0x0c7e, 0x1078, 0x76c7,
+ 0x2c78, 0x0c7f, 0x0040, 0x8de4, 0x7e12, 0x2c00, 0x781a, 0x781f,
+ 0x0003, 0x2021, 0x0005, 0x1078, 0x8e11, 0x2f60, 0x2009, 0x004e,
+ 0x1078, 0x775c, 0xa085, 0x0001, 0x047f, 0x0c7f, 0x0f7f, 0x007c,
+ 0x0f7e, 0x0c7e, 0x047e, 0x0c7e, 0x1078, 0x76c7, 0x2c78, 0x0c7f,
+ 0x0040, 0x8e0d, 0x7e12, 0x2c00, 0x781a, 0x781f, 0x0003, 0x2021,
+ 0x0004, 0x1078, 0x8e11, 0x2001, 0xa89d, 0x2004, 0xd0fc, 0x0040,
+ 0x8e06, 0x2f60, 0x1078, 0x772d, 0x0078, 0x8e0b, 0x2f60, 0x2009,
+ 0x0052, 0x1078, 0x775c, 0xa085, 0x0001, 0x047f, 0x0c7f, 0x0f7f,
+ 0x007c, 0x097e, 0x077e, 0x127e, 0x2091, 0x8000, 0x1078, 0x4775,
+ 0x0040, 0x8e1e, 0x2001, 0x8e16, 0x0078, 0x8e24, 0x1078, 0x4739,
+ 0x0040, 0x8e2d, 0x2001, 0x8e1e, 0x007e, 0xa00e, 0x2400, 0x1078,
+ 0x4b51, 0x1078, 0x4a73, 0x007f, 0x007a, 0x2418, 0x1078, 0x6161,
+ 0x62a0, 0x087e, 0x2041, 0x0001, 0x2039, 0x0001, 0x2608, 0x1078,
+ 0x5f1b, 0x087f, 0x1078, 0x5e0a, 0x2f08, 0x2648, 0x1078, 0x9f8b,
+ 0x613c, 0x81ff, 0x1040, 0x5fdb, 0x1078, 0x62d1, 0x127f, 0x077f,
+ 0x097f, 0x007c, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078,
+ 0x76c7, 0x017f, 0x0040, 0x8e63, 0x660a, 0x611a, 0x601f, 0x0001,
+ 0x2d00, 0x6012, 0x2009, 0x001f, 0x1078, 0x775c, 0xa085, 0x0001,
+ 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8e60, 0x0c7e, 0x127e,
+ 0x2091, 0x8000, 0x0c7e, 0x1078, 0x76c7, 0x017f, 0x0040, 0x8e7f,
+ 0x660a, 0x611a, 0x601f, 0x0008, 0x2d00, 0x6012, 0x2009, 0x0021,
+ 0x1078, 0x775c, 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006,
+ 0x0078, 0x8e7c, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078,
+ 0x76c7, 0x017f, 0x0040, 0x8e9b, 0x660a, 0x611a, 0x601f, 0x0001,
+ 0x2d00, 0x6012, 0x2009, 0x003d, 0x1078, 0x775c, 0xa085, 0x0001,
+ 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8e98, 0x0c7e, 0x127e,
+ 0x2091, 0x8000, 0x0c7e, 0x1078, 0x76c7, 0x017f, 0x0040, 0x8eb6,
+ 0x611a, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x0000, 0x1078,
+ 0x775c, 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078,
+ 0x8eb3, 0x027e, 0x0d7e, 0x6218, 0x2268, 0x6a3c, 0x82ff, 0x0040,
+ 0x8ec3, 0x8211, 0x6a3e, 0x0d7f, 0x027f, 0x007c, 0x007e, 0x6000,
+ 0xa086, 0x0000, 0x0040, 0x8ed8, 0x6013, 0x0000, 0x601f, 0x0007,
+ 0x2001, 0xa8a3, 0x2004, 0x6016, 0x1078, 0xa495, 0x603f, 0x0000,
+ 0x007f, 0x007c, 0x067e, 0x0c7e, 0x0d7e, 0x2031, 0xa653, 0x2634,
+ 0xd6e4, 0x0040, 0x8ee8, 0x6618, 0x2660, 0x6e48, 0x1078, 0x46e7,
+ 0x0d7f, 0x0c7f, 0x067f, 0x007c, 0x007e, 0x017e, 0x6004, 0xa08e,
+ 0x0002, 0x0040, 0x8efd, 0xa08e, 0x0003, 0x0040, 0x8efd, 0xa08e,
+ 0x0004, 0x0040, 0x8efd, 0xa085, 0x0001, 0x017f, 0x007f, 0x007c,
+ 0x007e, 0x0d7e, 0x6010, 0xa06d, 0x0040, 0x8f0d, 0x6838, 0xd0fc,
+ 0x0040, 0x8f0d, 0xa006, 0x0078, 0x8f0f, 0xa085, 0x0001, 0x0d7f,
+ 0x007f, 0x007c, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078,
+ 0x76c7, 0x017f, 0x0040, 0x8f2c, 0x611a, 0x601f, 0x0001, 0x2d00,
+ 0x6012, 0x1078, 0x2880, 0x2009, 0x0028, 0x1078, 0x775c, 0xa085,
+ 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8f29, 0xa186,
+ 0x0015, 0x00c0, 0x8f44, 0x2011, 0xa620, 0x2204, 0xa086, 0x0074,
+ 0x00c0, 0x8f44, 0x1078, 0x7f91, 0x6003, 0x0001, 0x6007, 0x0029,
+ 0x1078, 0x5dd7, 0x0078, 0x8f48, 0x1078, 0x7c83, 0x1078, 0x772d,
+ 0x007c, 0xa186, 0x0016, 0x00c0, 0x8f53, 0x2001, 0x0004, 0x1078,
+ 0x4502, 0x0078, 0x8f74, 0xa186, 0x0015, 0x00c0, 0x8f78, 0x2011,
+ 0xa620, 0x2204, 0xa086, 0x0014, 0x00c0, 0x8f78, 0x0d7e, 0x6018,
+ 0x2068, 0x1078, 0x4649, 0x0d7f, 0x1078, 0x8043, 0x00c0, 0x8f78,
+ 0x0d7e, 0x6018, 0x2068, 0x6890, 0x0d7f, 0xa005, 0x0040, 0x8f78,
+ 0x2001, 0x0006, 0x1078, 0x4502, 0x1078, 0x77f8, 0x0078, 0x8f7c,
+ 0x1078, 0x7c83, 0x1078, 0x772d, 0x007c, 0x6848, 0xa086, 0x0005,
+ 0x00c0, 0x8f84, 0x1078, 0x8f85, 0x007c, 0x6850, 0xc0ad, 0x6852,
+ 0x007c, 0x0e7e, 0x2071, 0xab8c, 0x7014, 0xd0e4, 0x0040, 0x8f9a,
+ 0x6013, 0x0000, 0x6003, 0x0001, 0x6007, 0x0050, 0x1078, 0x5d8a,
+ 0x1078, 0x62d1, 0x0e7f, 0x007c, 0x0c7e, 0x0f7e, 0x2c78, 0x1078,
+ 0x4963, 0x0f7f, 0x0040, 0x8fa9, 0x601c, 0xa084, 0x000f, 0x1079,
+ 0x8fab, 0x0c7f, 0x007c, 0x8ad5, 0x8fb6, 0x8fb9, 0x8fbc, 0xa25d,
+ 0xa279, 0xa27c, 0x8ad5, 0x8ad5, 0x1078, 0x1332, 0x0005, 0x0005,
+ 0x007c, 0x0005, 0x0005, 0x007c, 0x1078, 0x8fbf, 0x007c, 0x0f7e,
+ 0x2c78, 0x1078, 0x4963, 0x0040, 0x8fee, 0x1078, 0x76c7, 0x00c0,
+ 0x8fcf, 0x2001, 0xa8a4, 0x2004, 0x783e, 0x0078, 0x8fee, 0x7818,
+ 0x601a, 0x781c, 0xa086, 0x0003, 0x0040, 0x8fdc, 0x7808, 0x6036,
+ 0x2f00, 0x603a, 0x0078, 0x8fe0, 0x7808, 0x603a, 0x2f00, 0x6036,
+ 0x602a, 0x601f, 0x0001, 0x6007, 0x0035, 0x6003, 0x0001, 0x7920,
+ 0x6122, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x2f60, 0x0f7f, 0x007c,
+ 0x017e, 0x0f7e, 0x682c, 0x6032, 0xa08e, 0x0001, 0x0040, 0x9001,
+ 0xa086, 0x0005, 0x0040, 0x9005, 0xa006, 0x602a, 0x602e, 0x0078,
+ 0x9016, 0x6824, 0xc0f4, 0xc0d5, 0x6826, 0x6810, 0x2078, 0x787c,
+ 0x6938, 0xa102, 0x7880, 0x6934, 0xa103, 0x00c8, 0x8ffc, 0x6834,
+ 0x602a, 0x6838, 0xa084, 0xfffc, 0x683a, 0x602e, 0x2d00, 0x6036,
+ 0x6808, 0x603a, 0x6918, 0x611a, 0x6920, 0x6122, 0x601f, 0x0001,
+ 0x6007, 0x0039, 0x6003, 0x0001, 0x1078, 0x5d8a, 0x6803, 0x0002,
+ 0x0f7f, 0x017f, 0x007c, 0x007e, 0x017e, 0x6004, 0xa08e, 0x0034,
+ 0x0040, 0x9050, 0xa08e, 0x0035, 0x0040, 0x9050, 0xa08e, 0x0036,
+ 0x0040, 0x9050, 0xa08e, 0x0037, 0x0040, 0x9050, 0xa08e, 0x0038,
+ 0x0040, 0x9050, 0xa08e, 0x0039, 0x0040, 0x9050, 0xa08e, 0x003a,
+ 0x0040, 0x9050, 0xa08e, 0x003b, 0x0040, 0x9050, 0xa085, 0x0001,
+ 0x017f, 0x007f, 0x007c, 0x0f7e, 0x2c78, 0x1078, 0x4963, 0x00c0,
+ 0x905d, 0xa085, 0x0001, 0x0078, 0x906c, 0x6024, 0xd0f4, 0x00c0,
+ 0x906b, 0xc0f5, 0x6026, 0x6010, 0x2078, 0x7828, 0x603a, 0x782c,
+ 0x6036, 0x1078, 0x1757, 0xa006, 0x0f7f, 0x007c, 0x007e, 0x017e,
+ 0x027e, 0x037e, 0x0e7e, 0x2001, 0xa89e, 0x200c, 0x8000, 0x2014,
+ 0x2001, 0x0032, 0x1078, 0x5c1c, 0x2001, 0xa8a2, 0x82ff, 0x00c0,
+ 0x9083, 0x2011, 0x0014, 0x2202, 0x2001, 0xa8a0, 0x200c, 0x8000,
+ 0x2014, 0x2071, 0xa88d, 0x711a, 0x721e, 0x2001, 0x0064, 0x1078,
+ 0x5c1c, 0x2001, 0xa8a3, 0x82ff, 0x00c0, 0x9098, 0x2011, 0x0014,
+ 0x2202, 0x2009, 0xa8a4, 0xa280, 0x000a, 0x200a, 0x1078, 0x498b,
+ 0x0e7f, 0x037f, 0x027f, 0x017f, 0x007f, 0x007c, 0x007e, 0x0e7e,
+ 0x2001, 0xa8a2, 0x2003, 0x0028, 0x2001, 0xa8a3, 0x2003, 0x0014,
+ 0x2071, 0xa88d, 0x701b, 0x0000, 0x701f, 0x07d0, 0x2001, 0xa8a4,
+ 0x2003, 0x001e, 0x0e7f, 0x007f, 0x007c, 0x0c7e, 0x127e, 0x2091,
+ 0x8000, 0x0c7e, 0x1078, 0x76c7, 0x017f, 0x0040, 0x90d5, 0x611a,
+ 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x0033, 0x1078, 0x775c,
+ 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078, 0x90d2,
+ 0x0d7e, 0x0e7e, 0x0f7e, 0x2071, 0xa600, 0xa186, 0x0015, 0x00c0,
+ 0x9107, 0x7080, 0xa086, 0x0018, 0x00c0, 0x9107, 0x6010, 0x2068,
+ 0x6a3c, 0xd2e4, 0x00c0, 0x90fb, 0x2c78, 0x1078, 0x6490, 0x0040,
+ 0x910f, 0x706c, 0x6a50, 0xa206, 0x00c0, 0x9103, 0x7070, 0x6a54,
+ 0xa206, 0x00c0, 0x9103, 0x6218, 0xa290, 0x0028, 0x2214, 0x2009,
+ 0x0000, 0x1078, 0x28c8, 0x1078, 0x77f8, 0x0078, 0x910b, 0x1078,
+ 0x7c83, 0x1078, 0x772d, 0x0f7f, 0x0e7f, 0x0d7f, 0x007c, 0x7050,
+ 0xa080, 0x29c0, 0x2004, 0x6a54, 0xa206, 0x0040, 0x90fb, 0x0078,
+ 0x9103, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078, 0x76c7,
+ 0x017f, 0x0040, 0x9131, 0x611a, 0x601f, 0x0001, 0x2d00, 0x6012,
+ 0x2009, 0x0043, 0x1078, 0x775c, 0xa085, 0x0001, 0x127f, 0x0c7f,
+ 0x007c, 0xa006, 0x0078, 0x912e, 0x0d7e, 0x0e7e, 0x0f7e, 0x2071,
+ 0xa600, 0xa186, 0x0015, 0x00c0, 0x915a, 0x7080, 0xa086, 0x0004,
+ 0x00c0, 0x915a, 0x6010, 0xa0e8, 0x000f, 0x2c78, 0x1078, 0x6490,
+ 0x0040, 0x9162, 0x706c, 0x6a08, 0xa206, 0x00c0, 0x9156, 0x7070,
+ 0x6a0c, 0xa206, 0x00c0, 0x9156, 0x1078, 0x2880, 0x1078, 0x77f8,
+ 0x0078, 0x915e, 0x1078, 0x7c83, 0x1078, 0x772d, 0x0f7f, 0x0e7f,
+ 0x0d7f, 0x007c, 0x7050, 0xa080, 0x29c0, 0x2004, 0x6a0c, 0xa206,
+ 0x0040, 0x9154, 0x0078, 0x9156, 0x017e, 0x027e, 0x684c, 0xd0ac,
+ 0x0040, 0x9184, 0x6914, 0x6a10, 0x2100, 0xa205, 0x0040, 0x9184,
+ 0x6860, 0xa106, 0x00c0, 0x9180, 0x685c, 0xa206, 0x0040, 0x9184,
+ 0x6962, 0x6a5e, 0xa085, 0x0001, 0x027f, 0x017f, 0x007c, 0x0e7e,
+ 0x127e, 0x2071, 0xa600, 0x2091, 0x8000, 0x7548, 0xa582, 0x0001,
+ 0x0048, 0x91b9, 0x704c, 0x2060, 0x6000, 0xa086, 0x0000, 0x0040,
+ 0x91a5, 0xace0, 0x0010, 0x7058, 0xac02, 0x00c8, 0x91a1, 0x0078,
+ 0x9194, 0x2061, 0xad00, 0x0078, 0x9194, 0x6003, 0x0008, 0x8529,
+ 0x754a, 0xaca8, 0x0010, 0x7058, 0xa502, 0x00c8, 0x91b5, 0x754e,
+ 0xa085, 0x0001, 0x127f, 0x0e7f, 0x007c, 0x704f, 0xad00, 0x0078,
+ 0x91b0, 0xa006, 0x0078, 0x91b2, 0x0c7e, 0x027e, 0x017e, 0xa186,
+ 0x0035, 0x0040, 0x91c6, 0x6a34, 0x0078, 0x91c7, 0x6a28, 0x1078,
+ 0x8cf2, 0x0040, 0x91f0, 0x2260, 0x611c, 0xa186, 0x0003, 0x0040,
+ 0x91d5, 0xa186, 0x0006, 0x00c0, 0x91ec, 0x6834, 0xa206, 0x0040,
+ 0x91e4, 0x6838, 0xa206, 0x00c0, 0x91ec, 0x6108, 0x6834, 0xa106,
+ 0x00c0, 0x91ec, 0x0078, 0x91e9, 0x6008, 0x6938, 0xa106, 0x00c0,
+ 0x91ec, 0x6018, 0x6918, 0xa106, 0x017f, 0x027f, 0x0c7f, 0x007c,
+ 0xa085, 0x0001, 0x0078, 0x91ec, 0x6944, 0xd1cc, 0x0040, 0x920d,
+ 0xa18c, 0x00ff, 0xa18e, 0x0002, 0x00c0, 0x920d, 0xad88, 0x001e,
+ 0x210c, 0xa18c, 0x0f00, 0x810f, 0xa18e, 0x0001, 0x00c0, 0x920d,
+ 0x6810, 0x6914, 0xa115, 0x10c0, 0x84d5, 0x007c, 0x067e, 0x6000,
+ 0xa0b2, 0x0010, 0x10c8, 0x1332, 0x1079, 0x9218, 0x067f, 0x007c,
+ 0x9228, 0x96df, 0x97fb, 0x9228, 0x9228, 0x9228, 0x9228, 0x9228,
+ 0x9262, 0x988e, 0x9228, 0x9228, 0x9228, 0x9228, 0x9228, 0x9228,
+ 0x1078, 0x1332, 0x067e, 0x6000, 0xa0b2, 0x0010, 0x10c8, 0x1332,
+ 0x1079, 0x9234, 0x067f, 0x007c, 0x9244, 0x9d53, 0x9244, 0x9244,
+ 0x9244, 0x9244, 0x9244, 0x9244, 0x9d11, 0x9da1, 0x9244, 0xa3b0,
+ 0xa3e4, 0xa3b0, 0xa3e4, 0x9244, 0x1078, 0x1332, 0x067e, 0x6000,
+ 0xa0b2, 0x0010, 0x10c8, 0x1332, 0x1079, 0x9250, 0x067f, 0x007c,
+ 0x9260, 0x99eb, 0x9ac7, 0x9af5, 0x9b70, 0x9260, 0x9c76, 0x9c1e,
+ 0x989a, 0x9ce5, 0x9cfb, 0x9260, 0x9260, 0x9260, 0x9260, 0x9260,
+ 0x1078, 0x1332, 0xa1b2, 0x0044, 0x10c8, 0x1332, 0x2100, 0x0079,
+ 0x9269, 0x92a9, 0x9498, 0x92a9, 0x92a9, 0x92a9, 0x94a0, 0x92a9,
+ 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9,
+ 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9,
+ 0x92ab, 0x9311, 0x9320, 0x9377, 0x9396, 0x9415, 0x9485, 0x92a9,
+ 0x92a9, 0x94a4, 0x92a9, 0x92a9, 0x94b7, 0x94c2, 0x92a9, 0x92a9,
+ 0x92a9, 0x92a9, 0x92a9, 0x94fa, 0x92a9, 0x92a9, 0x9509, 0x92a9,
+ 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x9522, 0x92a9, 0x92a9,
+ 0x92a9, 0x95af, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9, 0x92a9,
+ 0x9629, 0x1078, 0x1332, 0x1078, 0x4967, 0x00c0, 0x92bb, 0x2001,
+ 0xa633, 0x2004, 0xd0cc, 0x00c0, 0x92bb, 0xa084, 0x0009, 0xa086,
+ 0x0008, 0x00c0, 0x92c3, 0x6007, 0x0009, 0x602b, 0x0009, 0x6013,
+ 0x0000, 0x0078, 0x9493, 0x1078, 0x4957, 0x0e7e, 0x0c7e, 0x037e,
+ 0x027e, 0x017e, 0x6218, 0x2270, 0x72a0, 0x027e, 0x2019, 0x0029,
+ 0x1078, 0x5f01, 0x077e, 0x2039, 0x0000, 0x1078, 0x5e0a, 0x2c08,
+ 0x1078, 0x9f8b, 0x077f, 0x017f, 0x2e60, 0x1078, 0x47e9, 0x017f,
+ 0x027f, 0x037f, 0x0c7f, 0x0e7f, 0x6618, 0x0c7e, 0x2660, 0x1078,
+ 0x45d6, 0x0c7f, 0xa6b0, 0x0001, 0x2634, 0xa684, 0x00ff, 0xa082,
+ 0x0006, 0x0048, 0x9303, 0x1078, 0x9ebf, 0x00c0, 0x9371, 0x1078,
+ 0x9e50, 0x00c0, 0x92ff, 0x6007, 0x0008, 0x0078, 0x9493, 0x6007,
+ 0x0009, 0x0078, 0x9493, 0x1078, 0xa09f, 0x0040, 0x930d, 0x1078,
+ 0x9ebf, 0x0040, 0x92f7, 0x0078, 0x9371, 0x6013, 0x1900, 0x0078,
+ 0x92ff, 0x1078, 0x29bb, 0x00c0, 0x9664, 0x6106, 0x1078, 0x9e05,
+ 0x6007, 0x0006, 0x0078, 0x9493, 0x6007, 0x0007, 0x0078, 0x9493,
+ 0x1078, 0xa41c, 0x00c0, 0x9664, 0x1078, 0x29bb, 0x00c0, 0x9664,
+ 0x0d7e, 0x6618, 0x2668, 0x6e04, 0xa684, 0x00ff, 0xa082, 0x0006,
+ 0x00c8, 0x9336, 0x2001, 0x0001, 0x1078, 0x44ee, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0006, 0x0040, 0x9353, 0xa686, 0x0004, 0x0040,
+ 0x9353, 0x6e04, 0xa6b4, 0x00ff, 0xa686, 0x0006, 0x0040, 0x9353,
+ 0xa686, 0x0004, 0x0040, 0x9353, 0xa686, 0x0005, 0x0040, 0x9353,
+ 0x0d7f, 0x0078, 0x9371, 0x1078, 0x9f25, 0x00c0, 0x936c, 0xa686,
+ 0x0006, 0x00c0, 0x9365, 0x027e, 0x6218, 0xa290, 0x0028, 0x2214,
+ 0x2009, 0x0000, 0x1078, 0x28c8, 0x027f, 0x1078, 0x4649, 0x6007,
+ 0x000a, 0x0d7f, 0x0078, 0x9493, 0x6007, 0x000b, 0x0d7f, 0x0078,
+ 0x9493, 0x1078, 0x2880, 0x6007, 0x0001, 0x0078, 0x9493, 0x1078,
+ 0xa41c, 0x00c0, 0x9664, 0x1078, 0x29bb, 0x00c0, 0x9664, 0x6618,
+ 0x0d7e, 0x2668, 0x6e04, 0x0d7f, 0xa686, 0x0707, 0x0040, 0x9371,
+ 0x027e, 0x6218, 0xa290, 0x0028, 0x2214, 0x2009, 0x0000, 0x1078,
+ 0x28c8, 0x027f, 0x6007, 0x000c, 0x0078, 0x9493, 0x1078, 0x4967,
+ 0x00c0, 0x93a3, 0x2001, 0xa633, 0x2004, 0xa084, 0x0009, 0xa086,
+ 0x0008, 0x00c0, 0x93ab, 0x6007, 0x0009, 0x602b, 0x0009, 0x6013,
+ 0x0000, 0x0078, 0x9493, 0x1078, 0x4957, 0x6618, 0xa6b0, 0x0001,
+ 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x0048, 0x93ef, 0xa6b4,
+ 0xff00, 0x8637, 0xa686, 0x0004, 0x0040, 0x93c2, 0xa686, 0x0006,
+ 0x00c0, 0x9371, 0x1078, 0x9f34, 0x00c0, 0x93ca, 0x6007, 0x000e,
+ 0x0078, 0x9493, 0x047e, 0x6418, 0xa4a0, 0x0028, 0x2424, 0xa4a4,
+ 0x00ff, 0x8427, 0x047e, 0x1078, 0x2880, 0x047f, 0x017e, 0xa006,
+ 0x2009, 0xa653, 0x210c, 0xd1a4, 0x0040, 0x93e9, 0x2009, 0x0029,
+ 0x1078, 0xa21d, 0x6018, 0x0d7e, 0x2068, 0x6800, 0xc0e5, 0x6802,
+ 0x0d7f, 0x017f, 0x047f, 0x6007, 0x0001, 0x0078, 0x9493, 0x2001,
+ 0x0001, 0x1078, 0x44ee, 0x157e, 0x017e, 0x027e, 0x037e, 0x20a9,
+ 0x0004, 0x2019, 0xa605, 0x2011, 0xab90, 0x1078, 0x80de, 0x037f,
+ 0x027f, 0x017f, 0x157f, 0xa005, 0x0040, 0x940f, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0006, 0x0040, 0x93c2, 0x0078, 0x9371, 0x6013,
+ 0x1900, 0x6007, 0x0009, 0x0078, 0x9493, 0x1078, 0x4967, 0x00c0,
+ 0x9422, 0x2001, 0xa633, 0x2004, 0xa084, 0x0009, 0xa086, 0x0008,
+ 0x00c0, 0x942a, 0x6007, 0x0009, 0x602b, 0x0009, 0x6013, 0x0000,
+ 0x0078, 0x9493, 0x1078, 0x4957, 0x6618, 0xa6b0, 0x0001, 0x2634,
+ 0xa684, 0x00ff, 0xa082, 0x0006, 0x0048, 0x9472, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0004, 0x0040, 0x9441, 0xa686, 0x0006, 0x00c0,
+ 0x9371, 0x1078, 0x9f5f, 0x00c0, 0x944d, 0x1078, 0x9e50, 0x00c0,
+ 0x944d, 0x6007, 0x0010, 0x0078, 0x9493, 0x047e, 0x6418, 0xa4a0,
+ 0x0028, 0x2424, 0xa4a4, 0x00ff, 0x8427, 0x047e, 0x1078, 0x2880,
+ 0x047f, 0x017e, 0xa006, 0x2009, 0xa653, 0x210c, 0xd1a4, 0x0040,
+ 0x946c, 0x2009, 0x0029, 0x1078, 0xa21d, 0x6018, 0x0d7e, 0x2068,
+ 0x6800, 0xc0e5, 0x6802, 0x0d7f, 0x017f, 0x047f, 0x6007, 0x0001,
+ 0x0078, 0x9493, 0x1078, 0xa09f, 0x0040, 0x947f, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0006, 0x0040, 0x9441, 0x0078, 0x9371, 0x6013,
+ 0x1900, 0x6007, 0x0009, 0x0078, 0x9493, 0x1078, 0x29bb, 0x00c0,
+ 0x9664, 0x1078, 0xa41c, 0x00c0, 0x9664, 0x1078, 0x9667, 0x00c0,
+ 0x9371, 0x6007, 0x0012, 0x6003, 0x0001, 0x1078, 0x5dd7, 0x007c,
+ 0x6007, 0x0001, 0x6003, 0x0001, 0x1078, 0x5dd7, 0x0078, 0x9497,
+ 0x6007, 0x0005, 0x0078, 0x949a, 0x1078, 0xa41c, 0x00c0, 0x9664,
+ 0x1078, 0x29bb, 0x00c0, 0x9664, 0x1078, 0x9667, 0x00c0, 0x9371,
+ 0x6007, 0x0020, 0x6003, 0x0001, 0x1078, 0x5dd7, 0x007c, 0x1078,
+ 0x29bb, 0x00c0, 0x9664, 0x6007, 0x0023, 0x6003, 0x0001, 0x1078,
+ 0x5dd7, 0x007c, 0x1078, 0xa41c, 0x00c0, 0x9664, 0x1078, 0x29bb,
+ 0x00c0, 0x9664, 0x1078, 0x9667, 0x00c0, 0x9371, 0x017e, 0x027e,
+ 0x2011, 0xab90, 0x2214, 0x2c08, 0xa006, 0x1078, 0xa1e6, 0x00c0,
+ 0x94e9, 0x2160, 0x6007, 0x0026, 0x6013, 0x1700, 0x2011, 0xab89,
+ 0x2214, 0xa296, 0xffff, 0x00c0, 0x94f3, 0x6007, 0x0025, 0x0078,
+ 0x94f3, 0x6004, 0xa086, 0x0024, 0x00c0, 0x94f0, 0x1078, 0x772d,
+ 0x2160, 0x6007, 0x0025, 0x6003, 0x0001, 0x1078, 0x5dd7, 0x027f,
+ 0x017f, 0x007c, 0x1078, 0x29bb, 0x00c0, 0x9664, 0x6106, 0x1078,
+ 0x9687, 0x6007, 0x002b, 0x0078, 0x9493, 0x6007, 0x002c, 0x0078,
+ 0x9493, 0x1078, 0xa41c, 0x00c0, 0x9664, 0x1078, 0x29bb, 0x00c0,
+ 0x9664, 0x1078, 0x9667, 0x00c0, 0x9371, 0x6106, 0x1078, 0x968c,
+ 0x00c0, 0x951e, 0x6007, 0x002e, 0x0078, 0x9493, 0x6007, 0x002f,
+ 0x0078, 0x9493, 0x1078, 0x29bb, 0x00c0, 0x9664, 0x0e7e, 0x0d7e,
+ 0x0c7e, 0x6018, 0xa080, 0x0001, 0x200c, 0xa184, 0x00ff, 0xa086,
+ 0x0006, 0x0040, 0x953f, 0xa184, 0xff00, 0x8007, 0xa086, 0x0006,
+ 0x0040, 0x953f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0078, 0x9498, 0x2001,
+ 0xa672, 0x2004, 0xd0e4, 0x0040, 0x95ab, 0x2071, 0xab8c, 0x7010,
+ 0x6036, 0x7014, 0x603a, 0x7108, 0x720c, 0x2001, 0xa653, 0x2004,
+ 0xd0a4, 0x0040, 0x955d, 0x6018, 0x2068, 0x6810, 0xa106, 0x00c0,
+ 0x955d, 0x6814, 0xa206, 0x0040, 0x9581, 0x2001, 0xa653, 0x2004,
+ 0xd0ac, 0x00c0, 0x959f, 0x2069, 0xa600, 0x6870, 0xa206, 0x00c0,
+ 0x959f, 0x686c, 0xa106, 0x00c0, 0x959f, 0x7210, 0x1078, 0x8cf2,
+ 0x0040, 0x95a5, 0x1078, 0xa28e, 0x0040, 0x95a5, 0x622a, 0x6007,
+ 0x0036, 0x6003, 0x0001, 0x1078, 0x5d8a, 0x0c7f, 0x0d7f, 0x0e7f,
+ 0x007c, 0x7214, 0xa286, 0xffff, 0x0040, 0x9593, 0x1078, 0x8cf2,
+ 0x0040, 0x95a5, 0xa280, 0x0002, 0x2004, 0x7110, 0xa106, 0x00c0,
+ 0x95a5, 0x0078, 0x956e, 0x7210, 0x2c08, 0xa085, 0x0001, 0x1078,
+ 0xa1e6, 0x2c10, 0x2160, 0x0040, 0x95a5, 0x0078, 0x956e, 0x6007,
+ 0x0037, 0x6013, 0x1500, 0x0078, 0x9579, 0x6007, 0x0037, 0x6013,
+ 0x1700, 0x0078, 0x9579, 0x6007, 0x0012, 0x0078, 0x9579, 0x1078,
+ 0x29bb, 0x00c0, 0x9664, 0x6018, 0xa080, 0x0001, 0x2004, 0xa084,
+ 0xff00, 0x8007, 0xa086, 0x0006, 0x00c0, 0x9498, 0x0e7e, 0x0d7e,
+ 0x0c7e, 0x2001, 0xa672, 0x2004, 0xd0e4, 0x0040, 0x9621, 0x2069,
+ 0xa600, 0x2071, 0xab8c, 0x7008, 0x6036, 0x720c, 0x623a, 0xa286,
+ 0xffff, 0x00c0, 0x95de, 0x7208, 0x0c7e, 0x2c08, 0xa085, 0x0001,
+ 0x1078, 0xa1e6, 0x2c10, 0x0c7f, 0x0040, 0x9615, 0x1078, 0x8cf2,
+ 0x0040, 0x9615, 0x0c7e, 0x027e, 0x2260, 0x1078, 0x89f3, 0x027f,
+ 0x0c7f, 0x7118, 0xa18c, 0xff00, 0x810f, 0xa186, 0x0001, 0x0040,
+ 0x95ff, 0xa186, 0x0005, 0x0040, 0x95f9, 0xa186, 0x0007, 0x00c0,
+ 0x9609, 0xa280, 0x0004, 0x2004, 0xa005, 0x0040, 0x9609, 0x057e,
+ 0x7510, 0x7614, 0x1078, 0xa2a3, 0x057f, 0x0c7f, 0x0d7f, 0x0e7f,
+ 0x007c, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013, 0x2a00, 0x6003,
+ 0x0001, 0x1078, 0x5d8a, 0x0078, 0x9605, 0x6007, 0x003b, 0x602b,
+ 0x0009, 0x6013, 0x1700, 0x6003, 0x0001, 0x1078, 0x5d8a, 0x0078,
+ 0x9605, 0x6007, 0x003b, 0x602b, 0x000b, 0x6013, 0x0000, 0x0078,
+ 0x9579, 0x0e7e, 0x027e, 0x1078, 0x4967, 0x0040, 0x965e, 0x1078,
+ 0x4957, 0x1078, 0xa4a9, 0x00c0, 0x965c, 0x2071, 0xa600, 0x70cc,
+ 0xc085, 0x70ce, 0x0f7e, 0x2079, 0x0100, 0x7298, 0xa284, 0x00ff,
+ 0x706e, 0x78e6, 0xa284, 0xff00, 0x7270, 0xa205, 0x7072, 0x78ea,
+ 0x0f7f, 0x70d7, 0x0000, 0x2001, 0xa653, 0x2004, 0xd0a4, 0x0040,
+ 0x9655, 0x2011, 0xa8ca, 0x2013, 0x07d0, 0xd0ac, 0x00c0, 0x965e,
+ 0x1078, 0x2677, 0x0078, 0x965e, 0x1078, 0xa4d9, 0x027f, 0x0e7f,
+ 0x1078, 0x772d, 0x0078, 0x9497, 0x1078, 0x772d, 0x007c, 0x0d7e,
+ 0x067e, 0x6618, 0x2668, 0x6e04, 0xa6b4, 0xff00, 0x8637, 0xa686,
+ 0x0006, 0x0040, 0x9684, 0xa686, 0x0004, 0x0040, 0x9684, 0x6e04,
+ 0xa6b4, 0x00ff, 0xa686, 0x0006, 0x0040, 0x9684, 0xa686, 0x0004,
+ 0x0040, 0x9684, 0xa085, 0x0001, 0x067f, 0x0d7f, 0x007c, 0x0d7e,
+ 0x1078, 0x96bb, 0x0d7f, 0x007c, 0x0d7e, 0x1078, 0x96ca, 0x00c0,
+ 0x96b4, 0x680c, 0xa08c, 0xff00, 0x6820, 0xa084, 0x00ff, 0xa115,
+ 0x6212, 0x6824, 0x602a, 0xd1e4, 0x0040, 0x96a2, 0x2009, 0x0001,
+ 0x0078, 0x96b0, 0xd1ec, 0x0040, 0x96b4, 0x6920, 0xa18c, 0x00ff,
+ 0x6824, 0x1078, 0x254d, 0x00c0, 0x96b4, 0x2110, 0x2009, 0x0000,
+ 0x1078, 0x28c8, 0x0078, 0x96b8, 0xa085, 0x0001, 0x0078, 0x96b9,
+ 0xa006, 0x0d7f, 0x007c, 0x2069, 0xab8d, 0x6800, 0xa082, 0x0010,
+ 0x00c8, 0x96c8, 0x6013, 0x0000, 0xa085, 0x0001, 0x0078, 0x96c9,
+ 0xa006, 0x007c, 0x6013, 0x0000, 0x2069, 0xab8c, 0x6808, 0xa084,
+ 0xff00, 0xa086, 0x0800, 0x00c0, 0x96de, 0x6800, 0xa084, 0x00ff,
+ 0xa08e, 0x0014, 0x0040, 0x96de, 0xa08e, 0x0010, 0x007c, 0x6004,
+ 0xa0b2, 0x0044, 0x10c8, 0x1332, 0xa1b6, 0x0013, 0x00c0, 0x96eb,
+ 0x2008, 0x0079, 0x96fe, 0xa1b6, 0x0027, 0x0040, 0x96f3, 0xa1b6,
+ 0x0014, 0x10c0, 0x1332, 0x2001, 0x0007, 0x1078, 0x4535, 0x1078,
+ 0x61cd, 0x1078, 0x8ec6, 0x1078, 0x62d1, 0x007c, 0x973e, 0x9740,
+ 0x973e, 0x973e, 0x973e, 0x9740, 0x974c, 0x97d6, 0x9799, 0x97d6,
+ 0x97ad, 0x97d6, 0x974c, 0x97d6, 0x97ce, 0x97d6, 0x97ce, 0x97d6,
+ 0x97d6, 0x973e, 0x973e, 0x973e, 0x973e, 0x973e, 0x973e, 0x973e,
+ 0x973e, 0x973e, 0x973e, 0x973e, 0x9740, 0x973e, 0x97d6, 0x973e,
+ 0x973e, 0x97d6, 0x973e, 0x97d6, 0x97d6, 0x973e, 0x973e, 0x973e,
+ 0x973e, 0x97d6, 0x97d6, 0x973e, 0x97d6, 0x97d6, 0x973e, 0x973e,
+ 0x973e, 0x973e, 0x973e, 0x9740, 0x97d6, 0x97d6, 0x973e, 0x973e,
+ 0x97d6, 0x97d6, 0x973e, 0x973e, 0x973e, 0x973e, 0x1078, 0x1332,
+ 0x1078, 0x61cd, 0x2001, 0xa8a2, 0x2004, 0x6016, 0x6003, 0x0002,
+ 0x1078, 0x62d1, 0x0078, 0x97dc, 0x0f7e, 0x2079, 0xa652, 0x7804,
+ 0x0f7f, 0xd0ac, 0x00c0, 0x97d6, 0x2001, 0x0000, 0x1078, 0x44ee,
+ 0x6018, 0xa080, 0x0004, 0x2004, 0xa086, 0x00ff, 0x0040, 0x97d6,
+ 0x0c7e, 0x6018, 0x2060, 0x6000, 0xd0f4, 0x00c0, 0x9770, 0x6010,
+ 0xa005, 0x0040, 0x9770, 0x0c7f, 0x1078, 0x3699, 0x0078, 0x97d6,
+ 0x0c7f, 0x2001, 0xa600, 0x2004, 0xa086, 0x0002, 0x00c0, 0x977f,
+ 0x0f7e, 0x2079, 0xa600, 0x7890, 0x8000, 0x7892, 0x0f7f, 0x2001,
+ 0x0002, 0x1078, 0x4502, 0x1078, 0x61cd, 0x601f, 0x0001, 0x6003,
+ 0x0001, 0x6007, 0x0002, 0x1078, 0x5dd7, 0x1078, 0x62d1, 0x0c7e,
+ 0x6118, 0x2160, 0x2009, 0x0001, 0x1078, 0x5a52, 0x0c7f, 0x0078,
+ 0x97dc, 0x6618, 0x0d7e, 0x2668, 0x6e04, 0x0d7f, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0006, 0x0040, 0x97d6, 0xa686, 0x0004, 0x0040,
+ 0x97d6, 0x2001, 0x0004, 0x0078, 0x97d4, 0x2001, 0xa600, 0x2004,
+ 0xa086, 0x0003, 0x00c0, 0x97b6, 0x1078, 0x3699, 0x2001, 0x0006,
+ 0x1078, 0x97dd, 0x6618, 0x0d7e, 0x2668, 0x6e04, 0x0d7f, 0xa6b4,
+ 0xff00, 0x8637, 0xa686, 0x0006, 0x0040, 0x97d6, 0x2001, 0x0006,
+ 0x0078, 0x97d4, 0x2001, 0x0004, 0x0078, 0x97d4, 0x2001, 0x0006,
+ 0x1078, 0x97dd, 0x0078, 0x97d6, 0x1078, 0x4535, 0x1078, 0x61cd,
+ 0x1078, 0x772d, 0x1078, 0x62d1, 0x007c, 0x017e, 0x0d7e, 0x6118,
+ 0x2168, 0x6900, 0xd184, 0x0040, 0x97f8, 0x6104, 0xa18e, 0x000a,
+ 0x00c0, 0x97f0, 0x699c, 0xd1a4, 0x00c0, 0x97f0, 0x2001, 0x0007,
+ 0x1078, 0x4502, 0x2001, 0x0000, 0x1078, 0x44ee, 0x1078, 0x28a6,
+ 0x0d7f, 0x017f, 0x007c, 0x0d7e, 0x6618, 0x2668, 0x6804, 0xa084,
+ 0xff00, 0x8007, 0x0d7f, 0xa0b2, 0x000c, 0x10c8, 0x1332, 0xa1b6,
+ 0x0015, 0x00c0, 0x980f, 0x1079, 0x9816, 0x0078, 0x9815, 0xa1b6,
+ 0x0016, 0x10c0, 0x1332, 0x1079, 0x9822, 0x007c, 0x7d4e, 0x7d4e,
+ 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e, 0x9877, 0x982e, 0x7d4e, 0x7d4e,
+ 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e,
+ 0x9877, 0x987f, 0x7d4e, 0x7d4e, 0x7d4e, 0x7d4e, 0x0f7e, 0x2079,
+ 0xa652, 0x7804, 0xd0ac, 0x00c0, 0x9855, 0x6018, 0xa07d, 0x0040,
+ 0x9855, 0x7800, 0xd0f4, 0x00c0, 0x9841, 0x7810, 0xa005, 0x00c0,
+ 0x9855, 0x2001, 0x0000, 0x1078, 0x44ee, 0x2001, 0x0002, 0x1078,
+ 0x4502, 0x601f, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x1078,
+ 0x5dd7, 0x1078, 0x62d1, 0x0078, 0x9875, 0x2011, 0xab83, 0x2204,
+ 0x8211, 0x220c, 0x1078, 0x254d, 0x00c0, 0x9875, 0x0c7e, 0x1078,
+ 0x45c4, 0x0040, 0x9868, 0x0c7f, 0x1078, 0x772d, 0x0078, 0x9875,
+ 0x6010, 0x007e, 0x6014, 0x007e, 0x1078, 0x42f8, 0x007f, 0x6016,
+ 0x007f, 0x6012, 0x0c7f, 0x1078, 0x772d, 0x0f7f, 0x007c, 0x6604,
+ 0xa6b6, 0x001e, 0x00c0, 0x987e, 0x1078, 0x772d, 0x007c, 0x1078,
+ 0x7f8e, 0x00c0, 0x988b, 0x6003, 0x0001, 0x6007, 0x0001, 0x1078,
+ 0x5dd7, 0x0078, 0x988d, 0x1078, 0x772d, 0x007c, 0x6004, 0xa08a,
+ 0x0044, 0x10c8, 0x1332, 0x1078, 0x61cd, 0x1078, 0x8ec6, 0x1078,
+ 0x62d1, 0x007c, 0xa182, 0x0040, 0x0079, 0x989e, 0x98b1, 0x98b1,
+ 0x98b1, 0x98b1, 0x98b3, 0x98b1, 0x98b1, 0x98b1, 0x98b1, 0x98b1,
+ 0x98b1, 0x98b1, 0x98b1, 0x98b1, 0x98b1, 0x98b1, 0x98b1, 0x98b1,
+ 0x98b1, 0x1078, 0x1332, 0x0d7e, 0x0e7e, 0x0f7e, 0x157e, 0x047e,
+ 0x027e, 0x6218, 0xa280, 0x002b, 0x2004, 0xa005, 0x0040, 0x98c4,
+ 0x2021, 0x0000, 0x1078, 0xa472, 0x6106, 0x2071, 0xab80, 0x7444,
+ 0xa4a4, 0xff00, 0x0040, 0x991b, 0xa486, 0x2000, 0x00c0, 0x98d6,
+ 0x2009, 0x0001, 0x2011, 0x0200, 0x1078, 0x5bf1, 0x1078, 0x138b,
+ 0x1040, 0x1332, 0x6003, 0x0007, 0x2d00, 0x6837, 0x010d, 0x6803,
+ 0x0000, 0x683b, 0x0000, 0x6c5a, 0x2c00, 0x685e, 0x6008, 0x68b2,
+ 0x6018, 0x2078, 0x78a0, 0x8007, 0x7130, 0x694a, 0x017e, 0xa084,
+ 0xff00, 0x6846, 0x684f, 0x0000, 0x6857, 0x0036, 0x1078, 0x4a73,
+ 0x017f, 0xa486, 0x2000, 0x00c0, 0x9903, 0x2019, 0x0017, 0x1078,
+ 0xa195, 0x0078, 0x997d, 0xa486, 0x0400, 0x00c0, 0x990d, 0x2019,
+ 0x0002, 0x1078, 0xa146, 0x0078, 0x997d, 0xa486, 0x0200, 0x00c0,
+ 0x9913, 0x1078, 0xa12b, 0xa486, 0x1000, 0x00c0, 0x9919, 0x1078,
+ 0xa17a, 0x0078, 0x997d, 0x2069, 0xa933, 0x6a00, 0xd284, 0x0040,
+ 0x99e7, 0xa284, 0x0300, 0x00c0, 0x99df, 0x6804, 0xa005, 0x0040,
+ 0x99c5, 0x2d78, 0x6003, 0x0007, 0x1078, 0x1370, 0x0040, 0x9984,
+ 0x7800, 0xd08c, 0x00c0, 0x9937, 0x7804, 0x8001, 0x7806, 0x6013,
+ 0x0000, 0x6803, 0x0000, 0x6837, 0x0116, 0x683b, 0x0000, 0x6008,
+ 0x68b2, 0x2c00, 0x684a, 0x6018, 0x2078, 0x78a0, 0x8007, 0x7130,
+ 0x6986, 0x6846, 0x7928, 0x698a, 0x792c, 0x698e, 0x7930, 0x6992,
+ 0x7934, 0x6996, 0x6853, 0x003d, 0x7244, 0xa294, 0x0003, 0xa286,
+ 0x0002, 0x00c0, 0x995f, 0x684f, 0x0040, 0x0078, 0x9969, 0xa286,
+ 0x0001, 0x00c0, 0x9967, 0x684f, 0x0080, 0x0078, 0x9969, 0x684f,
+ 0x0000, 0x20a9, 0x000a, 0x2001, 0xab90, 0xad90, 0x0015, 0x200c,
+ 0x810f, 0x2112, 0x8000, 0x8210, 0x00f0, 0x996f, 0x200c, 0x6982,
+ 0x8000, 0x200c, 0x697e, 0x1078, 0x4a73, 0x027f, 0x047f, 0x157f,
+ 0x0f7f, 0x0e7f, 0x0d7f, 0x007c, 0x2001, 0xa60e, 0x2004, 0xd084,
+ 0x0040, 0x998e, 0x1078, 0x138b, 0x00c0, 0x9930, 0x6013, 0x0100,
+ 0x6003, 0x0001, 0x6007, 0x0041, 0x1078, 0x5d8a, 0x1078, 0x62d1,
+ 0x0078, 0x997d, 0x2069, 0xab92, 0x2d04, 0xa084, 0xff00, 0xa086,
+ 0x1200, 0x00c0, 0x99b9, 0x2069, 0xab80, 0x686c, 0xa084, 0x00ff,
+ 0x017e, 0x6110, 0xa18c, 0x0700, 0xa10d, 0x6112, 0x017f, 0x6003,
+ 0x0001, 0x6007, 0x0043, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0078,
+ 0x997d, 0x6013, 0x0200, 0x6003, 0x0001, 0x6007, 0x0041, 0x1078,
+ 0x5d8a, 0x1078, 0x62d1, 0x0078, 0x997d, 0x2001, 0xa60d, 0x2004,
+ 0xd0ec, 0x0040, 0x99cf, 0x2011, 0x8049, 0x1078, 0x361b, 0x6013,
+ 0x0300, 0x0078, 0x99d5, 0x6013, 0x0100, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0078, 0x997d, 0x6013,
+ 0x0500, 0x0078, 0x99d5, 0x6013, 0x0600, 0x0078, 0x999a, 0x6013,
+ 0x0200, 0x0078, 0x999a, 0xa186, 0x0013, 0x00c0, 0x99fd, 0x6004,
+ 0xa08a, 0x0040, 0x1048, 0x1332, 0xa08a, 0x0053, 0x10c8, 0x1332,
+ 0xa082, 0x0040, 0x2008, 0x0079, 0x9a82, 0xa186, 0x0051, 0x0040,
+ 0x9a0a, 0xa186, 0x0047, 0x00c0, 0x9a23, 0x6004, 0xa086, 0x0041,
+ 0x0040, 0x9a31, 0x2001, 0x0109, 0x2004, 0xd084, 0x0040, 0x9a31,
+ 0x127e, 0x2091, 0x2200, 0x007e, 0x017e, 0x027e, 0x1078, 0x5c56,
+ 0x027f, 0x017f, 0x007f, 0x127f, 0x6000, 0xa086, 0x0002, 0x00c0,
+ 0x9a31, 0x0078, 0x9ac7, 0xa186, 0x0027, 0x0040, 0x9a2b, 0xa186,
+ 0x0014, 0x10c0, 0x1332, 0x6004, 0xa082, 0x0040, 0x2008, 0x0079,
+ 0x9a34, 0x1078, 0x7773, 0x007c, 0x9a47, 0x9a49, 0x9a49, 0x9a71,
+ 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47,
+ 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x9a47, 0x1078,
+ 0x1332, 0x1078, 0x61cd, 0x1078, 0x62d1, 0x037e, 0x0d7e, 0x6010,
+ 0xa06d, 0x0040, 0x9a6e, 0xad84, 0xf000, 0x0040, 0x9a6e, 0x6003,
+ 0x0002, 0x6018, 0x2004, 0xd0bc, 0x00c0, 0x9a6e, 0x2019, 0x0004,
+ 0x1078, 0xa1ca, 0x6013, 0x0000, 0x6014, 0xa005, 0x00c0, 0x9a6c,
+ 0x2001, 0xa8a3, 0x2004, 0x6016, 0x6003, 0x0007, 0x0d7f, 0x037f,
+ 0x007c, 0x0d7e, 0x1078, 0x61cd, 0x1078, 0x62d1, 0x1078, 0x8d06,
+ 0x0040, 0x9a7e, 0x6010, 0x2068, 0x1078, 0x13a4, 0x1078, 0x8ec6,
+ 0x0d7f, 0x007c, 0x9a95, 0x9ab4, 0x9a9e, 0x9ac1, 0x9a95, 0x9a95,
+ 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x9a95,
+ 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x1078, 0x1332, 0x6010,
+ 0xa088, 0x0013, 0x2104, 0xa085, 0x0400, 0x200a, 0x1078, 0x61cd,
+ 0x6010, 0xa080, 0x0013, 0x2004, 0xd0b4, 0x0040, 0x9aaf, 0x6003,
+ 0x0007, 0x2009, 0x0043, 0x1078, 0x775c, 0x0078, 0x9ab1, 0x6003,
+ 0x0002, 0x1078, 0x62d1, 0x007c, 0x1078, 0x61cd, 0x1078, 0xa423,
+ 0x00c0, 0x9abe, 0x1078, 0x5bc1, 0x1078, 0x772d, 0x1078, 0x62d1,
+ 0x007c, 0x1078, 0x61cd, 0x2009, 0x0041, 0x0078, 0x9c1e, 0xa182,
+ 0x0040, 0x0079, 0x9acb, 0x9ade, 0x9ae0, 0x9ade, 0x9ade, 0x9ade,
+ 0x9ade, 0x9ade, 0x9ae1, 0x9ade, 0x9ade, 0x9ade, 0x9ade, 0x9ade,
+ 0x9ade, 0x9ade, 0x9ade, 0x9ade, 0x9aec, 0x9ade, 0x1078, 0x1332,
+ 0x007c, 0x6003, 0x0004, 0x6110, 0x20e1, 0x0005, 0x3d18, 0x3e20,
+ 0x2c10, 0x1078, 0x15fa, 0x007c, 0x0d7e, 0x1078, 0x5bc1, 0x0d7f,
+ 0x1078, 0xa495, 0x1078, 0x772d, 0x007c, 0xa182, 0x0040, 0x0079,
+ 0x9af9, 0x9b0c, 0x9b0c, 0x9b0c, 0x9b0c, 0x9b0c, 0x9b0c, 0x9b0c,
+ 0x9b0e, 0x9b0c, 0x9b11, 0x9b3c, 0x9b0c, 0x9b0c, 0x9b0c, 0x9b0c,
+ 0x9b3c, 0x9b0c, 0x9b0c, 0x9b0c, 0x1078, 0x1332, 0x1078, 0x7773,
+ 0x007c, 0x1078, 0x627a, 0x1078, 0x639b, 0x6010, 0x0d7e, 0x2068,
+ 0x684c, 0xd0fc, 0x0040, 0x9b27, 0xa08c, 0x0003, 0xa18e, 0x0002,
+ 0x0040, 0x9b2f, 0x2009, 0x0041, 0x0d7f, 0x0078, 0x9c1e, 0x6003,
+ 0x0007, 0x6017, 0x0000, 0x1078, 0x5bc1, 0x0d7f, 0x007c, 0x1078,
+ 0xa423, 0x0040, 0x9b35, 0x0d7f, 0x007c, 0x1078, 0x5bc1, 0x1078,
+ 0x772d, 0x0d7f, 0x0078, 0x9b2e, 0x037e, 0x1078, 0x627a, 0x1078,
+ 0x639b, 0x6010, 0x0d7e, 0x2068, 0x6018, 0x2004, 0xd0bc, 0x0040,
+ 0x9b5c, 0x684c, 0xa084, 0x0003, 0xa086, 0x0002, 0x0040, 0x9b58,
+ 0x687c, 0x632c, 0xa31a, 0x632e, 0x6880, 0x6328, 0xa31b, 0x632a,
+ 0x6003, 0x0002, 0x0078, 0x9b6d, 0x2019, 0x0004, 0x1078, 0xa1ca,
+ 0x6014, 0xa005, 0x00c0, 0x9b69, 0x2001, 0xa8a3, 0x2004, 0x8003,
+ 0x6016, 0x6013, 0x0000, 0x6003, 0x0007, 0x0d7f, 0x037f, 0x007c,
+ 0xa186, 0x0013, 0x00c0, 0x9b7e, 0x6004, 0xa086, 0x0042, 0x10c0,
+ 0x1332, 0x1078, 0x61cd, 0x1078, 0x62d1, 0x007c, 0xa186, 0x0027,
+ 0x0040, 0x9b86, 0xa186, 0x0014, 0x00c0, 0x9b96, 0x6004, 0xa086,
+ 0x0042, 0x10c0, 0x1332, 0x2001, 0x0007, 0x1078, 0x4535, 0x1078,
+ 0x61cd, 0x1078, 0x8ec6, 0x1078, 0x62d1, 0x007c, 0xa182, 0x0040,
+ 0x0079, 0x9b9a, 0x9bad, 0x9bad, 0x9bad, 0x9bad, 0x9bad, 0x9bad,
+ 0x9bad, 0x9baf, 0x9bbb, 0x9bad, 0x9bad, 0x9bad, 0x9bad, 0x9bad,
+ 0x9bad, 0x9bad, 0x9bad, 0x9bad, 0x9bad, 0x1078, 0x1332, 0x037e,
+ 0x047e, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x1078, 0x15fa,
+ 0x047f, 0x037f, 0x007c, 0x6010, 0x0d7e, 0x2068, 0x6810, 0x6a14,
+ 0x6118, 0x210c, 0xd1bc, 0x0040, 0x9bda, 0x6124, 0xd1f4, 0x00c0,
+ 0x9bda, 0x007e, 0x047e, 0x057e, 0x6c7c, 0xa422, 0x6d80, 0x2200,
+ 0xa52b, 0x602c, 0xa420, 0x642e, 0x6028, 0xa529, 0x652a, 0x057f,
+ 0x047f, 0x007f, 0xa20d, 0x00c0, 0x9bee, 0x684c, 0xd0fc, 0x0040,
+ 0x9be6, 0x2009, 0x0041, 0x0d7f, 0x0078, 0x9c1e, 0x6003, 0x0007,
+ 0x6017, 0x0000, 0x1078, 0x5bc1, 0x0d7f, 0x007c, 0x007e, 0x0f7e,
+ 0x2c78, 0x1078, 0x4963, 0x0f7f, 0x007f, 0x0040, 0x9bfb, 0x6003,
+ 0x0002, 0x0d7f, 0x007c, 0x2009, 0xa60d, 0x210c, 0xd19c, 0x0040,
+ 0x9c05, 0x6003, 0x0007, 0x0078, 0x9c07, 0x6003, 0x0006, 0x1078,
+ 0x9c0d, 0x1078, 0x5bc3, 0x0d7f, 0x007c, 0xd2fc, 0x0040, 0x9c19,
+ 0x8002, 0x8000, 0x8212, 0xa291, 0x0000, 0x2009, 0x0009, 0x0078,
+ 0x9c1b, 0x2009, 0x0015, 0x6a6a, 0x6866, 0x007c, 0xa182, 0x0040,
+ 0x0048, 0x9c24, 0x0079, 0x9c31, 0xa186, 0x0013, 0x0040, 0x9c2c,
+ 0xa186, 0x0014, 0x10c0, 0x1332, 0x6024, 0xd0dc, 0x1040, 0x1332,
+ 0x007c, 0x9c44, 0x9c4b, 0x9c57, 0x9c63, 0x9c44, 0x9c44, 0x9c44,
+ 0x9c72, 0x9c44, 0x9c46, 0x9c46, 0x9c44, 0x9c44, 0x9c44, 0x9c44,
+ 0x9c44, 0x9c44, 0x9c44, 0x9c44, 0x1078, 0x1332, 0x6024, 0xd0dc,
+ 0x1040, 0x1332, 0x007c, 0x6003, 0x0001, 0x6106, 0x1078, 0x5d8a,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x62d1, 0x127f, 0x007c, 0x6003,
+ 0x0001, 0x6106, 0x1078, 0x5d8a, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x62d1, 0x127f, 0x007c, 0x6003, 0x0003, 0x6106, 0x2c10, 0x1078,
+ 0x1cf0, 0x127e, 0x2091, 0x8000, 0x1078, 0x5df6, 0x1078, 0x639b,
+ 0x127f, 0x007c, 0xa016, 0x1078, 0x15fa, 0x007c, 0x127e, 0x2091,
+ 0x8000, 0x037e, 0x0d7e, 0xa182, 0x0040, 0x1079, 0x9c83, 0x0d7f,
+ 0x037f, 0x127f, 0x007c, 0x9c93, 0x9c95, 0x9caa, 0x9cc9, 0x9c93,
+ 0x9c93, 0x9c93, 0x9ce1, 0x9c93, 0x9c93, 0x9c93, 0x9c93, 0x9c93,
+ 0x9c93, 0x9c93, 0x9c93, 0x1078, 0x1332, 0x6010, 0x2068, 0x684c,
+ 0xd0fc, 0x0040, 0x9cbf, 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0040,
+ 0x9cbf, 0x6003, 0x0001, 0x6106, 0x1078, 0x5d8a, 0x1078, 0x62d1,
+ 0x0078, 0x9ce4, 0x6010, 0x2068, 0x684c, 0xd0fc, 0x0040, 0x9cbf,
+ 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0040, 0x9cbf, 0x6003, 0x0001,
+ 0x6106, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0078, 0x9ce4, 0x6013,
+ 0x0000, 0x6017, 0x0000, 0x2019, 0x0004, 0x1078, 0xa1ca, 0x0078,
+ 0x9ce4, 0x6010, 0x2068, 0x684c, 0xd0fc, 0x0040, 0x9cbf, 0xa09c,
+ 0x0003, 0xa39e, 0x0003, 0x0040, 0x9cbf, 0x6003, 0x0003, 0x6106,
+ 0x2c10, 0x1078, 0x1cf0, 0x1078, 0x5df6, 0x1078, 0x639b, 0x0078,
+ 0x9ce4, 0xa016, 0x1078, 0x15fa, 0x007c, 0x1078, 0x61cd, 0x6110,
+ 0x81ff, 0x0040, 0x9cf6, 0x0d7e, 0x2168, 0x1078, 0xa4e2, 0x037e,
+ 0x2019, 0x0029, 0x1078, 0xa1ca, 0x037f, 0x0d7f, 0x1078, 0x8ec6,
+ 0x1078, 0x62d1, 0x007c, 0x1078, 0x627a, 0x6110, 0x81ff, 0x0040,
+ 0x9d0c, 0x0d7e, 0x2168, 0x1078, 0xa4e2, 0x037e, 0x2019, 0x0029,
+ 0x1078, 0xa1ca, 0x037f, 0x0d7f, 0x1078, 0x8ec6, 0x1078, 0x639b,
+ 0x007c, 0xa182, 0x0085, 0x0079, 0x9d15, 0x9d1e, 0x9d1c, 0x9d1c,
+ 0x9d2a, 0x9d1c, 0x9d1c, 0x9d1c, 0x1078, 0x1332, 0x6003, 0x000b,
+ 0x6106, 0x1078, 0x5d8a, 0x127e, 0x2091, 0x8000, 0x1078, 0x62d1,
+ 0x127f, 0x007c, 0x027e, 0x0e7e, 0x1078, 0xa41c, 0x0040, 0x9d34,
+ 0x1078, 0x772d, 0x0078, 0x9d50, 0x2071, 0xab80, 0x7224, 0x6212,
+ 0x7220, 0x1078, 0xa069, 0x0040, 0x9d41, 0x6007, 0x0086, 0x0078,
+ 0x9d4a, 0x6007, 0x0087, 0x7224, 0xa296, 0xffff, 0x00c0, 0x9d4a,
+ 0x6007, 0x0086, 0x6003, 0x0001, 0x1078, 0x5d8a, 0x1078, 0x62d1,
+ 0x0e7f, 0x027f, 0x007c, 0xa186, 0x0013, 0x00c0, 0x9d64, 0x6004,
+ 0xa08a, 0x0085, 0x1048, 0x1332, 0xa08a, 0x008c, 0x10c8, 0x1332,
+ 0xa082, 0x0085, 0x0079, 0x9d7b, 0xa186, 0x0027, 0x0040, 0x9d70,
+ 0xa186, 0x0014, 0x0040, 0x9d70, 0x1078, 0x7773, 0x0078, 0x9d7a,
+ 0x2001, 0x0007, 0x1078, 0x4535, 0x1078, 0x61cd, 0x1078, 0x8ec6,
+ 0x1078, 0x62d1, 0x007c, 0x9d82, 0x9d84, 0x9d84, 0x9d82, 0x9d82,
+ 0x9d82, 0x9d82, 0x1078, 0x1332, 0x1078, 0x61cd, 0x1078, 0x8ec6,
+ 0x1078, 0x62d1, 0x007c, 0xa182, 0x0085, 0x1048, 0x1332, 0xa182,
+ 0x008c, 0x10c8, 0x1332, 0xa182, 0x0085, 0x0079, 0x9d97, 0x9d9e,
+ 0x9d9e, 0x9d9e, 0x9da0, 0x9d9e, 0x9d9e, 0x9d9e, 0x1078, 0x1332,
+ 0x007c, 0xa186, 0x0013, 0x0040, 0x9db1, 0xa186, 0x0014, 0x0040,
+ 0x9db1, 0xa186, 0x0027, 0x0040, 0x9db1, 0x1078, 0x7773, 0x0078,
+ 0x9db7, 0x1078, 0x61cd, 0x1078, 0x8ec6, 0x1078, 0x62d1, 0x007c,
+ 0x037e, 0x1078, 0xa495, 0x603f, 0x0000, 0x2019, 0x000b, 0x1078,
+ 0x9dc7, 0x601f, 0x0006, 0x6003, 0x0007, 0x037f, 0x007c, 0x127e,
+ 0x037e, 0x2091, 0x8000, 0x087e, 0x2c40, 0x097e, 0x2049, 0x0000,
+ 0x1078, 0x7246, 0x097f, 0x087f, 0x00c0, 0x9e02, 0x077e, 0x2c38,
+ 0x1078, 0x72f3, 0x077f, 0x00c0, 0x9e02, 0x6000, 0xa086, 0x0000,
+ 0x0040, 0x9e02, 0x601c, 0xa086, 0x0007, 0x0040, 0x9e02, 0x0d7e,
+ 0x6000, 0xa086, 0x0004, 0x00c0, 0x9df3, 0x1078, 0xa495, 0x601f,
+ 0x0007, 0x1078, 0x1757, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040,
+ 0x9dfb, 0x1078, 0xa1ca, 0x0d7f, 0x6013, 0x0000, 0x1078, 0xa495,
+ 0x601f, 0x0007, 0x037f, 0x127f, 0x007c, 0x0f7e, 0x0c7e, 0x037e,
+ 0x157e, 0x2079, 0xab80, 0x7938, 0x783c, 0x1078, 0x254d, 0x00c0,
+ 0x9e49, 0x017e, 0x0c7e, 0x1078, 0x45c4, 0x00c0, 0x9e49, 0x017f,
+ 0x027f, 0x027e, 0x017e, 0x2019, 0x0029, 0x1078, 0x73d0, 0x1078,
+ 0x5f01, 0x077e, 0x2039, 0x0000, 0x1078, 0x5e0a, 0x077f, 0x017f,
+ 0x077e, 0x2039, 0x0000, 0x1078, 0x9f8b, 0x077f, 0x1078, 0x47e9,
+ 0x027e, 0x6204, 0xa294, 0xff00, 0x8217, 0xa286, 0x0006, 0x0040,
+ 0x9e3d, 0xa286, 0x0004, 0x00c0, 0x9e40, 0x62a0, 0x1078, 0x2942,
+ 0x027f, 0x017f, 0x1078, 0x42f8, 0x6612, 0x6516, 0xa006, 0x0078,
+ 0x9e4b, 0x0c7f, 0x017f, 0x157f, 0x037f, 0x0c7f, 0x0f7f, 0x007c,
+ 0x0c7e, 0x0d7e, 0x0e7e, 0x017e, 0x2009, 0xa620, 0x2104, 0xa086,
+ 0x0074, 0x00c0, 0x9eb3, 0x2069, 0xab8e, 0x690c, 0xa182, 0x0100,
+ 0x0048, 0x9ea3, 0x6908, 0xa184, 0x8000, 0x0040, 0x9eaf, 0x6018,
+ 0x2070, 0x7010, 0xa084, 0x00ff, 0x0040, 0x9e72, 0x7000, 0xd0f4,
+ 0x0040, 0x9e76, 0xa184, 0x0800, 0x0040, 0x9eaf, 0x6910, 0xa18a,
+ 0x0001, 0x0048, 0x9ea7, 0x6914, 0x2069, 0xabae, 0x6904, 0x81ff,
+ 0x00c0, 0x9e9b, 0x690c, 0xa182, 0x0100, 0x0048, 0x9ea3, 0x6908,
+ 0x81ff, 0x00c0, 0x9e9f, 0x6910, 0xa18a, 0x0001, 0x0048, 0x9ea7,
+ 0x6918, 0xa18a, 0x0001, 0x0048, 0x9eaf, 0x0078, 0x9eb9, 0x6013,
+ 0x0100, 0x0078, 0x9eb5, 0x6013, 0x0300, 0x0078, 0x9eb5, 0x6013,
+ 0x0500, 0x0078, 0x9eb5, 0x6013, 0x0700, 0x0078, 0x9eb5, 0x6013,
+ 0x0900, 0x0078, 0x9eb5, 0x6013, 0x0b00, 0x0078, 0x9eb5, 0x6013,
+ 0x0f00, 0x0078, 0x9eb5, 0x6013, 0x2d00, 0xa085, 0x0001, 0x0078,
+ 0x9eba, 0xa006, 0x017f, 0x0e7f, 0x0d7f, 0x0c7f, 0x007c, 0x0c7e,
+ 0x0d7e, 0x027e, 0x037e, 0x157e, 0x6218, 0x2268, 0x6b04, 0xa394,
+ 0x00ff, 0xa286, 0x0006, 0x0040, 0x9ee3, 0xa286, 0x0004, 0x0040,
+ 0x9ee3, 0xa394, 0xff00, 0x8217, 0xa286, 0x0006, 0x0040, 0x9ee3,
+ 0xa286, 0x0004, 0x0040, 0x9ee3, 0x0c7e, 0x2d60, 0x1078, 0x45d6,
+ 0x0c7f, 0x0078, 0x9f1e, 0x2011, 0xab96, 0xad98, 0x000a, 0x20a9,
+ 0x0004, 0x1078, 0x80de, 0x00c0, 0x9f1f, 0x2011, 0xab9a, 0xad98,
+ 0x0006, 0x20a9, 0x0004, 0x1078, 0x80de, 0x00c0, 0x9f1f, 0x047e,
+ 0x017e, 0x6aa0, 0xa294, 0x00ff, 0x8227, 0xa006, 0x2009, 0xa653,
+ 0x210c, 0xd1a4, 0x0040, 0x9f0b, 0x2009, 0x0029, 0x1078, 0xa21d,
+ 0x6800, 0xc0e5, 0x6802, 0x2019, 0x0029, 0x1078, 0x5f01, 0x077e,
+ 0x2039, 0x0000, 0x1078, 0x5e0a, 0x2c08, 0x1078, 0x9f8b, 0x077f,
+ 0x2001, 0x0007, 0x1078, 0x4535, 0x017f, 0x047f, 0xa006, 0x157f,
+ 0x037f, 0x027f, 0x0d7f, 0x0c7f, 0x007c, 0x0d7e, 0x2069, 0xab8e,
+ 0x6800, 0xa086, 0x0800, 0x0040, 0x9f31, 0x6013, 0x0000, 0x0078,
+ 0x9f32, 0xa006, 0x0d7f, 0x007c, 0x0c7e, 0x0f7e, 0x017e, 0x027e,
+ 0x037e, 0x157e, 0x2079, 0xab8c, 0x7930, 0x7834, 0x1078, 0x254d,
+ 0x00c0, 0x9f58, 0x1078, 0x45c4, 0x00c0, 0x9f58, 0x2011, 0xab90,
+ 0xac98, 0x000a, 0x20a9, 0x0004, 0x1078, 0x80de, 0x00c0, 0x9f58,
+ 0x2011, 0xab94, 0xac98, 0x0006, 0x20a9, 0x0004, 0x1078, 0x80de,
+ 0x157f, 0x037f, 0x027f, 0x017f, 0x0f7f, 0x0c7f, 0x007c, 0x0c7e,
+ 0x007e, 0x017e, 0x027e, 0x037e, 0x157e, 0x2011, 0xab83, 0x2204,
+ 0x8211, 0x220c, 0x1078, 0x254d, 0x00c0, 0x9f84, 0x1078, 0x45c4,
+ 0x00c0, 0x9f84, 0x2011, 0xab96, 0xac98, 0x000a, 0x20a9, 0x0004,
+ 0x1078, 0x80de, 0x00c0, 0x9f84, 0x2011, 0xab9a, 0xac98, 0x0006,
+ 0x20a9, 0x0004, 0x1078, 0x80de, 0x157f, 0x037f, 0x027f, 0x017f,
+ 0x007f, 0x0c7f, 0x007c, 0x0e7e, 0x0c7e, 0x087e, 0x077e, 0x067e,
+ 0x057e, 0x047e, 0x027e, 0x127e, 0x2091, 0x8000, 0x2740, 0x2029,
+ 0xa8ba, 0x252c, 0x2021, 0xa8c0, 0x2424, 0x2061, 0xad00, 0x2071,
+ 0xa600, 0x7648, 0x7064, 0x81ff, 0x0040, 0x9fb2, 0x007e, 0xa186,
+ 0xa9b3, 0x007f, 0x0040, 0x9fb2, 0x8001, 0xa602, 0x00c8, 0xa01c,
+ 0x0078, 0x9fb5, 0xa606, 0x0040, 0xa01c, 0x2100, 0xac06, 0x0040,
+ 0xa012, 0x1078, 0xa242, 0x0040, 0xa012, 0x671c, 0xa786, 0x0001,
+ 0x0040, 0xa037, 0xa786, 0x0004, 0x0040, 0xa037, 0xa786, 0x0007,
+ 0x0040, 0xa012, 0x2500, 0xac06, 0x0040, 0xa012, 0x2400, 0xac06,
+ 0x0040, 0xa012, 0x1078, 0xa256, 0x00c0, 0xa012, 0x88ff, 0x0040,
+ 0x9fdd, 0x6020, 0xa906, 0x00c0, 0xa012, 0x0d7e, 0x6000, 0xa086,
+ 0x0004, 0x00c0, 0x9fe7, 0x017e, 0x1078, 0x1757, 0x017f, 0xa786,
+ 0x0008, 0x00c0, 0x9ff6, 0x1078, 0x8f00, 0x00c0, 0x9ff6, 0x1078,
+ 0x7c83, 0x0d7f, 0x1078, 0x8ec6, 0x0078, 0xa012, 0x6010, 0x2068,
+ 0x1078, 0x8d06, 0x0040, 0xa00f, 0xa786, 0x0003, 0x00c0, 0xa026,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0xa4e2, 0x017e,
+ 0x1078, 0x8f7d, 0x1078, 0x4a73, 0x017f, 0x1078, 0x8eb9, 0x0d7f,
+ 0x1078, 0x8ec6, 0xace0, 0x0010, 0x2001, 0xa616, 0x2004, 0xac02,
+ 0x00c8, 0xa01c, 0x0078, 0x9f9f, 0x127f, 0x027f, 0x047f, 0x057f,
+ 0x067f, 0x077f, 0x087f, 0x0c7f, 0x0e7f, 0x007c, 0xa786, 0x0006,
+ 0x00c0, 0xa000, 0xa386, 0x0005, 0x0040, 0xa034, 0x1078, 0xa4e2,
+ 0x1078, 0xa1ca, 0x0078, 0xa00f, 0x0d7f, 0x0078, 0xa012, 0x1078,
+ 0xa256, 0x00c0, 0xa012, 0x81ff, 0x0040, 0xa012, 0xa180, 0x0001,
+ 0x2004, 0xa086, 0x0018, 0x0040, 0xa04c, 0xa180, 0x0001, 0x2004,
+ 0xa086, 0x002d, 0x00c0, 0xa012, 0x6000, 0xa086, 0x0002, 0x00c0,
+ 0xa012, 0x1078, 0x8eec, 0x0040, 0xa05d, 0x1078, 0x8f00, 0x00c0,
+ 0xa012, 0x1078, 0x7c83, 0x0078, 0xa065, 0x1078, 0x28a6, 0x1078,
+ 0x8f00, 0x00c0, 0xa065, 0x1078, 0x7c83, 0x1078, 0x8ec6, 0x0078,
+ 0xa012, 0x0c7e, 0x0e7e, 0x017e, 0x2c08, 0x2170, 0xa006, 0x1078,
+ 0xa1e6, 0x017f, 0x0040, 0xa079, 0x601c, 0xa084, 0x000f, 0x1079,
+ 0xa07c, 0x0e7f, 0x0c7f, 0x007c, 0xa084, 0xa084, 0xa084, 0xa084,
+ 0xa084, 0xa084, 0xa086, 0xa084, 0xa006, 0x007c, 0x047e, 0x017e,
+ 0x7018, 0xa080, 0x0028, 0x2024, 0xa4a4, 0x00ff, 0x8427, 0x2c00,
+ 0x2009, 0x0020, 0x1078, 0xa21d, 0x017f, 0x047f, 0x037e, 0x2019,
+ 0x0002, 0x1078, 0x9dc7, 0x037f, 0xa085, 0x0001, 0x007c, 0x2001,
+ 0x0001, 0x1078, 0x44ee, 0x157e, 0x017e, 0x027e, 0x037e, 0x20a9,
+ 0x0004, 0x2019, 0xa605, 0x2011, 0xab96, 0x1078, 0x80de, 0x037f,
+ 0x027f, 0x017f, 0x157f, 0xa005, 0x007c, 0x0f7e, 0x0e7e, 0x0c7e,
+ 0x087e, 0x077e, 0x067e, 0x027e, 0x127e, 0x2091, 0x8000, 0x2740,
+ 0x2061, 0xad00, 0x2079, 0x0001, 0x8fff, 0x0040, 0xa11d, 0x2071,
+ 0xa600, 0x7648, 0x7064, 0x8001, 0xa602, 0x00c8, 0xa11d, 0x88ff,
+ 0x0040, 0xa0d8, 0x2800, 0xac06, 0x00c0, 0xa113, 0x2079, 0x0000,
+ 0x1078, 0xa242, 0x0040, 0xa113, 0x2400, 0xac06, 0x0040, 0xa113,
+ 0x671c, 0xa786, 0x0006, 0x00c0, 0xa113, 0xa786, 0x0007, 0x0040,
+ 0xa113, 0x88ff, 0x00c0, 0xa0f7, 0x6018, 0xa206, 0x00c0, 0xa113,
+ 0x85ff, 0x0040, 0xa0f7, 0x6020, 0xa106, 0x00c0, 0xa113, 0x0d7e,
+ 0x6000, 0xa086, 0x0004, 0x00c0, 0xa103, 0x1078, 0xa495, 0x601f,
+ 0x0007, 0x1078, 0x1757, 0x6010, 0x2068, 0x1078, 0x8d06, 0x0040,
+ 0xa10d, 0x047e, 0x1078, 0xa1ca, 0x047f, 0x0d7f, 0x1078, 0x8ec6,
+ 0x88ff, 0x00c0, 0xa127, 0xace0, 0x0010, 0x2001, 0xa616, 0x2004,
+ 0xac02, 0x00c8, 0xa11d, 0x0078, 0xa0c4, 0xa006, 0x127f, 0x027f,
+ 0x067f, 0x077f, 0x087f, 0x0c7f, 0x0e7f, 0x0f7f, 0x007c, 0xa8c5,
+ 0x0001, 0x0078, 0xa11e, 0x077e, 0x057e, 0x087e, 0x2041, 0x0000,
+ 0x2029, 0x0001, 0x2c20, 0x2019, 0x0002, 0x6218, 0x097e, 0x2049,
+ 0x0000, 0x1078, 0x7246, 0x097f, 0x087f, 0x2039, 0x0000, 0x1078,
+ 0x72f3, 0x1078, 0xa0b5, 0x057f, 0x077f, 0x007c, 0x027e, 0x047e,
+ 0x057e, 0x077e, 0x0c7e, 0x157e, 0x2c20, 0x2128, 0x20a9, 0x007f,
+ 0x2009, 0x0000, 0x017e, 0x037e, 0x1078, 0x45c4, 0x00c0, 0xa16e,
+ 0x2c10, 0x057e, 0x087e, 0x2041, 0x0000, 0x2508, 0x2029, 0x0001,
+ 0x097e, 0x2049, 0x0000, 0x1078, 0x7246, 0x097f, 0x087f, 0x2039,
+ 0x0000, 0x1078, 0x72f3, 0x1078, 0xa0b5, 0x057f, 0x037f, 0x017f,
+ 0x8108, 0x00f0, 0xa152, 0x157f, 0x0c7f, 0x077f, 0x057f, 0x047f,
+ 0x027f, 0x007c, 0x077e, 0x057e, 0x6218, 0x087e, 0x2041, 0x0000,
+ 0x2029, 0x0001, 0x2019, 0x0048, 0x097e, 0x2049, 0x0000, 0x1078,
+ 0x7246, 0x097f, 0x087f, 0x2039, 0x0000, 0x1078, 0x72f3, 0x2c20,
+ 0x1078, 0xa0b5, 0x057f, 0x077f, 0x007c, 0x027e, 0x047e, 0x057e,
+ 0x077e, 0x0c7e, 0x157e, 0x2c20, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0x017e, 0x037e, 0x1078, 0x45c4, 0x00c0, 0xa1be, 0x2c10, 0x087e,
+ 0x2041, 0x0000, 0x2828, 0x047e, 0x2021, 0x0001, 0x1078, 0xa472,
+ 0x047f, 0x097e, 0x2049, 0x0000, 0x1078, 0x7246, 0x097f, 0x087f,
+ 0x2039, 0x0000, 0x1078, 0x72f3, 0x1078, 0xa0b5, 0x037f, 0x017f,
+ 0x8108, 0x00f0, 0xa1a0, 0x157f, 0x0c7f, 0x077f, 0x057f, 0x047f,
+ 0x027f, 0x007c, 0x017e, 0x0f7e, 0xad82, 0xcd00, 0x0048, 0xa1e3,
+ 0xad82, 0xffff, 0x00c8, 0xa1e3, 0x6800, 0xa07d, 0x0040, 0xa1e0,
+ 0x6803, 0x0000, 0x6b52, 0x1078, 0x4a73, 0x2f68, 0x0078, 0xa1d4,
+ 0x6b52, 0x1078, 0x4a73, 0x0f7f, 0x017f, 0x007c, 0x0e7e, 0x047e,
+ 0x037e, 0x2061, 0xad00, 0xa005, 0x00c0, 0xa1f6, 0x2071, 0xa600,
+ 0x7448, 0x7064, 0x8001, 0xa402, 0x00c8, 0xa218, 0x2100, 0xac06,
+ 0x0040, 0xa20a, 0x6000, 0xa086, 0x0000, 0x0040, 0xa20a, 0x6008,
+ 0xa206, 0x00c0, 0xa20a, 0x6018, 0xa1a0, 0x0006, 0x2424, 0xa406,
+ 0x0040, 0xa214, 0xace0, 0x0010, 0x2001, 0xa616, 0x2004, 0xac02,
+ 0x00c8, 0xa218, 0x0078, 0xa1f6, 0xa085, 0x0001, 0x0078, 0xa219,
+ 0xa006, 0x037f, 0x047f, 0x0e7f, 0x007c, 0x0d7e, 0x007e, 0x1078,
+ 0x138b, 0x007f, 0x1040, 0x1332, 0x6837, 0x010d, 0x685e, 0x027e,
+ 0x2010, 0x1078, 0x8cf2, 0x2001, 0x0000, 0x0040, 0xa233, 0x2200,
+ 0xa080, 0x0008, 0x2004, 0x027f, 0x684a, 0x6956, 0x6c46, 0x684f,
+ 0x0000, 0xa006, 0x68b2, 0x6802, 0x683a, 0x685a, 0x1078, 0x4a73,
+ 0x0d7f, 0x007c, 0x6700, 0xa786, 0x0000, 0x0040, 0xa255, 0xa786,
+ 0x0001, 0x0040, 0xa255, 0xa786, 0x000a, 0x0040, 0xa255, 0xa786,
+ 0x0009, 0x0040, 0xa255, 0xa085, 0x0001, 0x007c, 0x0e7e, 0x6018,
+ 0x2070, 0x70a0, 0xa206, 0x0e7f, 0x007c, 0x017e, 0x6004, 0xa08e,
+ 0x001e, 0x00c0, 0xa277, 0x8007, 0x6130, 0xa18c, 0x00ff, 0xa105,
+ 0x6032, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0005, 0x2001,
+ 0xa8a3, 0x2004, 0x6016, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x017f,
+ 0x007c, 0x0005, 0x0005, 0x007c, 0x6024, 0xd0e4, 0x0040, 0xa28d,
+ 0xd0cc, 0x0040, 0xa287, 0x1078, 0x8fbf, 0x0078, 0xa28d, 0x1078,
+ 0xa495, 0x1078, 0x5bc1, 0x1078, 0x772d, 0x007c, 0xa280, 0x0007,
+ 0x2004, 0xa084, 0x000f, 0x0079, 0xa295, 0xa29e, 0xa29e, 0xa29e,
+ 0xa2a0, 0xa29e, 0xa2a0, 0xa2a0, 0xa29e, 0xa2a0, 0xa006, 0x007c,
+ 0xa085, 0x0001, 0x007c, 0xa280, 0x0007, 0x2004, 0xa084, 0x000f,
+ 0x0079, 0xa2aa, 0xa2b3, 0xa2b3, 0xa2b3, 0xa2b3, 0xa2b3, 0xa2b3,
+ 0xa2be, 0xa2b3, 0xa2b3, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013,
+ 0x2a00, 0x6003, 0x0001, 0x1078, 0x5d8a, 0x007c, 0x0c7e, 0x2260,
+ 0x1078, 0xa495, 0x603f, 0x0000, 0x6024, 0xc0f4, 0xc0cc, 0x6026,
+ 0x0c7f, 0x0d7e, 0x2268, 0xa186, 0x0007, 0x00c0, 0xa31f, 0x6810,
+ 0xa005, 0x0040, 0xa2dc, 0xa080, 0x0013, 0x2004, 0xd0fc, 0x00c0,
+ 0xa2dc, 0x0d7f, 0x0078, 0xa2b3, 0x6007, 0x003a, 0x6003, 0x0001,
+ 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0c7e, 0x2d60, 0x6100, 0xa186,
+ 0x0002, 0x00c0, 0xa3ad, 0x6010, 0xa005, 0x00c0, 0xa2f6, 0x6000,
+ 0xa086, 0x0007, 0x10c0, 0x1332, 0x0078, 0xa3ad, 0xa08c, 0xf000,
+ 0x00c0, 0xa302, 0x0078, 0xa302, 0x2068, 0x6800, 0xa005, 0x00c0,
+ 0xa2fc, 0x2d00, 0xa080, 0x0013, 0x2004, 0xa084, 0x0003, 0xa086,
+ 0x0002, 0x00c0, 0xa31b, 0x6010, 0x2068, 0x684c, 0xc0dc, 0xc0f4,
+ 0x684e, 0x6850, 0xc0f4, 0xc0fc, 0x6852, 0x2009, 0x0043, 0x1078,
+ 0x9c1e, 0x0078, 0xa3ad, 0x2009, 0x0041, 0x0078, 0xa3a7, 0xa186,
+ 0x0005, 0x00c0, 0xa366, 0x6810, 0xa080, 0x0013, 0x2004, 0xd0bc,
+ 0x00c0, 0xa32d, 0x0d7f, 0x0078, 0xa2b3, 0xd0b4, 0x0040, 0xa335,
+ 0xd0fc, 0x1040, 0x1332, 0x0078, 0xa2cf, 0x6007, 0x003a, 0x6003,
+ 0x0001, 0x1078, 0x5d8a, 0x1078, 0x62d1, 0x0c7e, 0x2d60, 0x6100,
+ 0xa186, 0x0002, 0x0040, 0xa348, 0xa186, 0x0004, 0x00c0, 0xa3ad,
+ 0x2071, 0xa8e7, 0x7000, 0xa086, 0x0003, 0x00c0, 0xa355, 0x7004,
+ 0xac06, 0x00c0, 0xa355, 0x7003, 0x0000, 0x6810, 0xa080, 0x0013,
+ 0x200c, 0xc1f4, 0xc1dc, 0x2102, 0x8000, 0x200c, 0xc1f4, 0xc1fc,
+ 0xc1bc, 0x2102, 0x2009, 0x0042, 0x0078, 0xa3a7, 0x037e, 0x0d7e,
+ 0x0d7e, 0x1078, 0x138b, 0x037f, 0x1040, 0x1332, 0x6837, 0x010d,
+ 0x6803, 0x0000, 0x683b, 0x0000, 0x685b, 0x0000, 0x6b5e, 0x6857,
+ 0x0045, 0x2c00, 0x6862, 0x6034, 0x6872, 0x2360, 0x6024, 0xc0dd,
+ 0x6026, 0x6018, 0xa080, 0x0028, 0x2004, 0xa084, 0x00ff, 0x8007,
+ 0x6320, 0x6b4a, 0x6846, 0x684f, 0x0000, 0x6d6a, 0x6e66, 0x686f,
+ 0x0001, 0x1078, 0x4a73, 0x2019, 0x0045, 0x6008, 0x2068, 0x1078,
+ 0x9dc7, 0x2d00, 0x600a, 0x601f, 0x0006, 0x6003, 0x0007, 0x6017,
+ 0x0000, 0x603f, 0x0000, 0x0d7f, 0x037f, 0x0078, 0xa3ae, 0x603f,
+ 0x0000, 0x6003, 0x0007, 0x1078, 0x9c1e, 0x0c7f, 0x0d7f, 0x007c,
+ 0xa186, 0x0013, 0x00c0, 0xa3ba, 0x6004, 0xa082, 0x0085, 0x2008,
+ 0x0079, 0xa3d4, 0xa186, 0x0027, 0x00c0, 0xa3cd, 0x1078, 0x61cd,
+ 0x037e, 0x0d7e, 0x6010, 0x2068, 0x2019, 0x0004, 0x1078, 0xa1ca,
+ 0x0d7f, 0x037f, 0x1078, 0x62d1, 0x007c, 0xa186, 0x0014, 0x0040,
+ 0xa3be, 0x1078, 0x7773, 0x007c, 0xa3dd, 0xa3db, 0xa3db, 0xa3db,
+ 0xa3db, 0xa3db, 0xa3dd, 0x1078, 0x1332, 0x1078, 0x61cd, 0x6003,
+ 0x000c, 0x1078, 0x62d1, 0x007c, 0xa182, 0x008c, 0x00c8, 0xa3ee,
+ 0xa182, 0x0085, 0x0048, 0xa3ee, 0x0079, 0xa3f1, 0x1078, 0x7773,
+ 0x007c, 0xa3f8, 0xa3f8, 0xa3f8, 0xa3f8, 0xa3fa, 0xa419, 0xa3f8,
+ 0x1078, 0x1332, 0x0d7e, 0x2c68, 0x1078, 0x76c7, 0x0040, 0xa414,
+ 0x6003, 0x0001, 0x6007, 0x001e, 0x2009, 0xab8e, 0x210c, 0x6136,
+ 0x2009, 0xab8f, 0x210c, 0x613a, 0x600b, 0xffff, 0x6918, 0x611a,
+ 0x601f, 0x0004, 0x1078, 0x5d8a, 0x2d60, 0x1078, 0x772d, 0x0d7f,
+ 0x007c, 0x1078, 0x772d, 0x007c, 0x0e7e, 0x6018, 0x2070, 0x7000,
+ 0xd0ec, 0x0e7f, 0x007c, 0x6010, 0xa08c, 0xf000, 0x0040, 0xa471,
+ 0xa080, 0x0013, 0x200c, 0xd1ec, 0x0040, 0xa471, 0x2001, 0xa672,
+ 0x2004, 0xd0ec, 0x0040, 0xa471, 0x6003, 0x0002, 0x6024, 0xc0e5,
+ 0x6026, 0xd1ac, 0x0040, 0xa44f, 0x0f7e, 0x2c78, 0x1078, 0x495f,
+ 0x0f7f, 0x0040, 0xa44f, 0x2001, 0xa8a4, 0x2004, 0x603e, 0x2009,
+ 0xa672, 0x210c, 0xd1f4, 0x00c0, 0xa46f, 0x0078, 0xa461, 0x2009,
+ 0xa672, 0x210c, 0xd1f4, 0x0040, 0xa45b, 0x6024, 0xc0e4, 0x6026,
+ 0xa006, 0x0078, 0xa471, 0x2001, 0xa8a4, 0x200c, 0x8103, 0xa100,
+ 0x603e, 0x6018, 0xa088, 0x002b, 0x2104, 0xa005, 0x0040, 0xa46c,
+ 0xa088, 0x0003, 0x0078, 0xa464, 0x2c0a, 0x600f, 0x0000, 0xa085,
+ 0x0001, 0x007c, 0x017e, 0x0c7e, 0x0e7e, 0x6120, 0xa2f0, 0x002b,
+ 0x2e04, 0x2060, 0x8cff, 0x0040, 0xa491, 0x84ff, 0x00c0, 0xa484,
+ 0x6020, 0xa106, 0x00c0, 0xa48c, 0x600c, 0x2072, 0x1078, 0x5bc1,
+ 0x1078, 0x772d, 0x0078, 0xa48e, 0xacf0, 0x0003, 0x2e64, 0x0078,
+ 0xa47a, 0x0e7f, 0x0c7f, 0x017f, 0x007c, 0x0d7e, 0x6018, 0xa0e8,
+ 0x002b, 0x2d04, 0xa005, 0x0040, 0xa4a7, 0xac06, 0x0040, 0xa4a5,
+ 0x2d04, 0xa0e8, 0x0003, 0x0078, 0xa499, 0x600c, 0x206a, 0x0d7f,
+ 0x007c, 0x027e, 0x037e, 0x157e, 0x2011, 0xa626, 0x2204, 0xa084,
+ 0x00ff, 0x2019, 0xab8e, 0x2334, 0xa636, 0x00c0, 0xa4d5, 0x8318,
+ 0x2334, 0x2204, 0xa084, 0xff00, 0xa636, 0x00c0, 0xa4d5, 0x2011,
+ 0xab90, 0x6018, 0xa098, 0x000a, 0x20a9, 0x0004, 0x1078, 0x80de,
+ 0x00c0, 0xa4d5, 0x2011, 0xab94, 0x6018, 0xa098, 0x0006, 0x20a9,
+ 0x0004, 0x1078, 0x80de, 0x00c0, 0xa4d5, 0x157f, 0x037f, 0x027f,
+ 0x007c, 0x0e7e, 0x2071, 0xa600, 0x1078, 0x42b8, 0x1078, 0x2677,
+ 0x0e7f, 0x007c, 0x0e7e, 0x6018, 0x2070, 0x7000, 0xd0fc, 0x0040,
+ 0xa4eb, 0x1078, 0xa4ed, 0x0e7f, 0x007c, 0x6850, 0xc0e5, 0x6852,
+ 0x007c, 0x0e7e, 0x0c7e, 0x077e, 0x067e, 0x057e, 0x047e, 0x027e,
+ 0x017e, 0x127e, 0x2091, 0x8000, 0x2029, 0xa8ba, 0x252c, 0x2021,
+ 0xa8c0, 0x2424, 0x2061, 0xad00, 0x2071, 0xa600, 0x7648, 0x7064,
+ 0xa606, 0x0040, 0xa545, 0x671c, 0xa786, 0x0001, 0x0040, 0xa514,
+ 0xa786, 0x0008, 0x00c0, 0xa53b, 0x2500, 0xac06, 0x0040, 0xa53b,
+ 0x2400, 0xac06, 0x0040, 0xa53b, 0x1078, 0xa242, 0x0040, 0xa53b,
+ 0x1078, 0xa256, 0x00c0, 0xa53b, 0x6000, 0xa086, 0x0004, 0x00c0,
+ 0xa52d, 0x017e, 0x1078, 0x1757, 0x017f, 0x1078, 0x8eec, 0x00c0,
+ 0xa533, 0x1078, 0x28a6, 0x1078, 0x8f00, 0x00c0, 0xa539, 0x1078,
+ 0x7c83, 0x1078, 0x8ec6, 0xace0, 0x0010, 0x2001, 0xa616, 0x2004,
+ 0xac02, 0x00c8, 0xa545, 0x0078, 0xa504, 0x127f, 0x017f, 0x027f,
+ 0x047f, 0x057f, 0x067f, 0x077f, 0x0c7f, 0x0e7f, 0x007c, 0x127e,
+ 0x007e, 0x0e7e, 0x017e, 0x2091, 0x8000, 0x2071, 0xa640, 0xd5a4,
+ 0x0040, 0xa55d, 0x7034, 0x8000, 0x7036, 0xd5b4, 0x0040, 0xa563,
+ 0x7030, 0x8000, 0x7032, 0xd5ac, 0x0040, 0xa579, 0x2500, 0xa084,
+ 0x0007, 0xa08e, 0x0003, 0x0040, 0xa579, 0xa08e, 0x0004, 0x0040,
+ 0xa579, 0xa08e, 0x0005, 0x0040, 0xa579, 0x2071, 0xa64a, 0x1078,
+ 0xa5ba, 0x017f, 0x0e7f, 0x007f, 0x127f, 0x007c, 0x127e, 0x007e,
+ 0x0e7e, 0x017e, 0x2091, 0x8000, 0x2071, 0xa640, 0xd5a4, 0x0040,
+ 0xa58c, 0x7034, 0x8000, 0x7036, 0xd5b4, 0x0040, 0xa592, 0x7030,
+ 0x8000, 0x7032, 0xd5ac, 0x0040, 0xa5a8, 0x2500, 0xa084, 0x0007,
+ 0xa08e, 0x0003, 0x0040, 0xa5a8, 0xa08e, 0x0004, 0x0040, 0xa5a8,
+ 0xa08e, 0x0005, 0x0040, 0xa5a8, 0x2071, 0xa64a, 0x1078, 0xa5ba,
+ 0x017f, 0x0e7f, 0x007f, 0x127f, 0x007c, 0x127e, 0x007e, 0x0e7e,
+ 0x2091, 0x8000, 0x2071, 0xa642, 0x1078, 0xa5ba, 0x0e7f, 0x007f,
+ 0x127f, 0x007c, 0x2e04, 0x8000, 0x2072, 0x00c8, 0xa5c3, 0x8e70,
+ 0x2e04, 0x8000, 0x2072, 0x007c, 0x0e7e, 0x2071, 0xa640, 0x1078,
+ 0xa5ba, 0x0e7f, 0x007c, 0x0e7e, 0x2071, 0xa644, 0x1078, 0xa5ba,
+ 0x0e7f, 0x007c, 0x127e, 0x007e, 0x0e7e, 0x2091, 0x8000, 0x2071,
+ 0xa640, 0x7044, 0x8000, 0x7046, 0x0e7f, 0x007f, 0x127f, 0x007c,
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
+ 0xa50c
+};
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2100tp_length01 = 0x95f1;
+#else
+unsigned short risc_code_length01 = 0x95f1;
+#endif
+
diff --git a/drivers/scsi/qla2xxx/ql2200.c b/drivers/scsi/qla2xxx/ql2200.c
new file mode 100644
index 000000000000..2f5698d52722
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2200.c
@@ -0,0 +1,92 @@
+/*
+ * QLogic ISP2200 device driver for Linux 2.6.x
+ * Copyright (C) 2003 Christoph Hellwig.
+ * Copyright (C) 2003-2004 QLogic Corporation (www.qlogic.com)
+ *
+ * Released under GPL v2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "qla_def.h"
+
+static char qla_driver_name[] = "qla2200";
+
+extern unsigned char fw2200tp_version[];
+extern unsigned char fw2200tp_version_str[];
+extern unsigned short fw2200tp_addr01;
+extern unsigned short fw2200tp_code01[];
+extern unsigned short fw2200tp_length01;
+
+static struct qla_fw_info qla_fw_tbl[] = {
+ {
+ .addressing = FW_INFO_ADDR_NORMAL,
+ .fwcode = &fw2200tp_code01[0],
+ .fwlen = &fw2200tp_length01,
+ .fwstart = &fw2200tp_addr01,
+ },
+
+ { FW_INFO_ADDR_NOMORE, },
+};
+
+static struct qla_board_info qla_board_tbl = {
+ .drv_name = qla_driver_name,
+
+ .isp_name = "ISP2200",
+ .fw_info = qla_fw_tbl,
+};
+
+static struct pci_device_id qla2200_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP2200,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl,
+ },
+
+ {0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla2200_pci_tbl);
+
+static int __devinit
+qla2200_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ return qla2x00_probe_one(pdev,
+ (struct qla_board_info *)id->driver_data);
+}
+
+static void __devexit
+qla2200_remove_one(struct pci_dev *pdev)
+{
+ qla2x00_remove_one(pdev);
+}
+
+static struct pci_driver qla2200_pci_driver = {
+ .name = "qla2200",
+ .id_table = qla2200_pci_tbl,
+ .probe = qla2200_probe_one,
+ .remove = __devexit_p(qla2200_remove_one),
+};
+
+static int __init
+qla2200_init(void)
+{
+ return pci_module_init(&qla2200_pci_driver);
+}
+
+static void __exit
+qla2200_exit(void)
+{
+ pci_unregister_driver(&qla2200_pci_driver);
+}
+
+module_init(qla2200_init);
+module_exit(qla2200_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic ISP22xx FC-SCSI Host Bus Adapter driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff --git a/drivers/scsi/qla2xxx/ql2200_fw.c b/drivers/scsi/qla2xxx/ql2200_fw.c
new file mode 100644
index 000000000000..5412dcb4b6d0
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2200_fw.c
@@ -0,0 +1,5321 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *************************************************************************/
+
+/*
+ * Firmware Version 2.02.06 (08:46 Jun 26, 2003)
+ */
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2200tp_version = 2*1024+2;
+#else
+unsigned short risc_code_version = 2*1024+2;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned char fw2200tp_version_str[] = {2,2,6};
+#else
+unsigned char firmware_version[] = {2,2,6};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw2200tp_VERSION_STRING "2.02.06"
+#else
+#define FW_VERSION_STRING "2.02.06"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2200tp_addr01 = 0x1000 ;
+#else
+unsigned short risc_code_addr01 = 0x1000 ;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2200tp_code01[] = {
+#else
+unsigned short risc_code01[] = {
+#endif
+ 0x0470, 0x0000, 0x0000, 0xa46f, 0x0000, 0x0002, 0x0002, 0x0006,
+ 0x0017, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2032, 0x3030,
+ 0x3120, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3232, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x322e, 0x3032, 0x2e30, 0x3620, 0x2020, 0x2020, 0x2400, 0x20c1,
+ 0x0005, 0x2001, 0x017f, 0x2003, 0x0000, 0x20c9, 0xbaff, 0x2091,
+ 0x2000, 0x2059, 0x0000, 0x2b78, 0x7823, 0x0004, 0x2089, 0x296a,
+ 0x2051, 0xb500, 0x2a70, 0x2029, 0xed00, 0x2031, 0xffff, 0x2039,
+ 0xece9, 0x2021, 0x0200, 0x0804, 0x1468, 0x20a1, 0xb46f, 0xa00e,
+ 0x20a9, 0x0891, 0x41a4, 0x3400, 0x7562, 0x7666, 0x775e, 0x746a,
+ 0x746e, 0x20a1, 0xbd00, 0x7164, 0x810d, 0x810d, 0x810d, 0x810d,
+ 0xa18c, 0x000f, 0x2001, 0x000b, 0xa112, 0xa00e, 0x21a8, 0x41a4,
+ 0x3400, 0x8211, 0x1dd8, 0x7164, 0x3400, 0xa102, 0x0120, 0x0218,
+ 0x20a8, 0xa00e, 0x41a4, 0x3800, 0xd08c, 0x01d8, 0x2009, 0xb500,
+ 0x810d, 0x810d, 0x810d, 0x810d, 0xa18c, 0x000f, 0x2001, 0x0001,
+ 0xa112, 0x20a1, 0x1000, 0xa00e, 0x21a8, 0x41a4, 0x8211, 0x1de0,
+ 0x2009, 0xb500, 0x3400, 0xa102, 0x0120, 0x0218, 0x20a8, 0xa00e,
+ 0x41a4, 0x080c, 0x1411, 0x080c, 0x1632, 0x080c, 0x17cf, 0x080c,
+ 0x1fa2, 0x080c, 0x4bff, 0x080c, 0x85bf, 0x080c, 0x15bb, 0x080c,
+ 0x2ec4, 0x080c, 0x5d8a, 0x080c, 0x5341, 0x080c, 0x68ce, 0x080c,
+ 0x2510, 0x080c, 0x6b61, 0x080c, 0x63bb, 0x080c, 0x23ca, 0x080c,
+ 0x24de, 0x2091, 0x3009, 0x7823, 0x0000, 0x1004, 0x10c5, 0x7820,
+ 0xa086, 0x0002, 0x1150, 0x7823, 0x4000, 0x0e04, 0x10bd, 0x781b,
+ 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x2a70, 0x7003, 0x0000,
+ 0x2a70, 0x7000, 0xa08e, 0x0003, 0x1158, 0x080c, 0x3f08, 0x080c,
+ 0x2eeb, 0x080c, 0x5dd8, 0x080c, 0x54f0, 0x080c, 0x68f9, 0x0c80,
+ 0x000b, 0x0c98, 0x10e4, 0x10e5, 0x1210, 0x10e2, 0x12dd, 0x140e,
+ 0x140f, 0x1410, 0x080c, 0x1515, 0x0005, 0x0126, 0x00f6, 0x2091,
+ 0x8000, 0x7000, 0xa086, 0x0001, 0x1904, 0x11ed, 0x080c, 0x1588,
+ 0x080c, 0x5acf, 0x0150, 0x080c, 0x5af5, 0x15c0, 0x2079, 0x0100,
+ 0x7828, 0xa085, 0x1800, 0x782a, 0x0488, 0x080c, 0x5a07, 0x7000,
+ 0xa086, 0x0001, 0x1904, 0x11ed, 0x708c, 0xa086, 0x0028, 0x1904,
+ 0x11ed, 0x2001, 0x0161, 0x2003, 0x0001, 0x2079, 0x0100, 0x7827,
+ 0xffff, 0x7a28, 0xa295, 0x1e2f, 0x7a2a, 0x2011, 0x59a2, 0x080c,
+ 0x699c, 0x2011, 0x5995, 0x080c, 0x6a5c, 0x2011, 0x59e4, 0x080c,
+ 0x699c, 0x2011, 0x4adc, 0x080c, 0x699c, 0x2011, 0x8030, 0x2019,
+ 0x0000, 0x708b, 0x0000, 0x080c, 0x1de9, 0x00e8, 0x080c, 0x448f,
+ 0x2079, 0x0100, 0x7844, 0xa005, 0x1904, 0x11ed, 0x2011, 0x4adc,
+ 0x080c, 0x699c, 0x2011, 0x59e4, 0x080c, 0x699c, 0x080c, 0x1de9,
+ 0x2001, 0xb78d, 0x2004, 0x780e, 0x7840, 0xa084, 0xfffb, 0x7842,
+ 0x2011, 0x8010, 0x73cc, 0x080c, 0x3ecc, 0x723c, 0xc284, 0x723e,
+ 0x2001, 0xb50c, 0x200c, 0xc1ac, 0x2102, 0x080c, 0x7f35, 0x2011,
+ 0x0004, 0x080c, 0x9c60, 0x080c, 0x524d, 0x080c, 0x5acf, 0x0158,
+ 0x080c, 0x4be8, 0x0140, 0x708b, 0x0001, 0x70c7, 0x0000, 0x080c,
+ 0x462c, 0x0804, 0x11ed, 0x080c, 0x5309, 0x0120, 0x7a0c, 0xc2b4,
+ 0x7a0e, 0x0060, 0x7073, 0x0000, 0x080c, 0xa008, 0x70d4, 0xd09c,
+ 0x1128, 0x70a0, 0xa005, 0x0110, 0x080c, 0x4bc6, 0x70df, 0x0000,
+ 0x70db, 0x0000, 0x72d4, 0x080c, 0x5acf, 0x1178, 0x2011, 0x0000,
+ 0x0016, 0x080c, 0x28eb, 0x2019, 0xb78f, 0x211a, 0x001e, 0x7053,
+ 0xffff, 0x7057, 0x00ef, 0x7077, 0x0000, 0x2079, 0xb552, 0x7804,
+ 0xd0ac, 0x0108, 0xc295, 0x72d6, 0x080c, 0x5acf, 0x0118, 0xa296,
+ 0x0004, 0x0548, 0x2011, 0x0001, 0x080c, 0x9c60, 0x709b, 0x0000,
+ 0x709f, 0xffff, 0x7003, 0x0002, 0x2079, 0x0100, 0x7827, 0x0003,
+ 0x7828, 0xa085, 0x0003, 0x782a, 0x00fe, 0x080c, 0x2ab8, 0x2011,
+ 0x0005, 0x080c, 0x8075, 0x080c, 0x7173, 0x080c, 0x5acf, 0x0148,
+ 0x00c6, 0x2061, 0x0100, 0x0016, 0x080c, 0x28eb, 0x61e2, 0x001e,
+ 0x00ce, 0x012e, 0x0420, 0x709b, 0x0000, 0x709f, 0xffff, 0x7003,
+ 0x0002, 0x00f6, 0x2079, 0x0100, 0x7827, 0x0003, 0x7828, 0xa085,
+ 0x0003, 0x782a, 0x00fe, 0x2011, 0x0005, 0x080c, 0x8075, 0x080c,
+ 0x7173, 0x080c, 0x5acf, 0x0148, 0x00c6, 0x2061, 0x0100, 0x0016,
+ 0x080c, 0x28eb, 0x61e2, 0x001e, 0x00ce, 0x00fe, 0x012e, 0x0005,
+ 0x00c6, 0x080c, 0x5acf, 0x1118, 0x20a9, 0x0100, 0x0010, 0x20a9,
+ 0x0082, 0x080c, 0x5acf, 0x1118, 0x2009, 0x0000, 0x0010, 0x2009,
+ 0x007e, 0x080c, 0x2d97, 0x8108, 0x1f04, 0x1201, 0x00ce, 0x7073,
+ 0x0000, 0x7074, 0xa084, 0x00ff, 0x7076, 0x70a3, 0x0000, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x7000, 0xa086, 0x0002, 0x1904, 0x12db,
+ 0x709c, 0xa086, 0xffff, 0x0130, 0x080c, 0x2ab8, 0x080c, 0x7173,
+ 0x0804, 0x12db, 0x70d4, 0xd0ac, 0x1110, 0xd09c, 0x0540, 0xd084,
+ 0x0530, 0x0006, 0x0016, 0x2001, 0x0103, 0x2009, 0xb78d, 0x210c,
+ 0x2102, 0x001e, 0x000e, 0xd08c, 0x01d0, 0x70d8, 0xa086, 0xffff,
+ 0x0190, 0x080c, 0x2c17, 0x080c, 0x7173, 0x70d4, 0xd094, 0x1904,
+ 0x12db, 0x2011, 0x0001, 0x2019, 0x0000, 0x080c, 0x2c4f, 0x080c,
+ 0x7173, 0x0804, 0x12db, 0x70dc, 0xa005, 0x1904, 0x12db, 0x7098,
+ 0xa005, 0x1904, 0x12db, 0x70d4, 0xd0a4, 0x0118, 0xd0b4, 0x0904,
+ 0x12db, 0x080c, 0x5309, 0x1904, 0x12db, 0x2001, 0xb553, 0x2004,
+ 0xd0ac, 0x01c8, 0x0156, 0x00c6, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0x0016, 0x080c, 0x4fa9, 0x1118, 0x6000, 0xd0ec, 0x1138, 0x001e,
+ 0x8108, 0x1f04, 0x1268, 0x00ce, 0x015e, 0x0028, 0x001e, 0x00ce,
+ 0x015e, 0x0804, 0x12db, 0x0006, 0x0016, 0x2001, 0x0103, 0x2009,
+ 0xb78d, 0x210c, 0x2102, 0x001e, 0x000e, 0x71a8, 0x81ff, 0x11b0,
+ 0xa006, 0x2009, 0x0200, 0x20a9, 0x0002, 0x20a1, 0xb7de, 0x40a1,
+ 0x2009, 0x0700, 0x20a9, 0x0002, 0x20a1, 0xb7ce, 0x40a1, 0x7070,
+ 0x8007, 0x7174, 0x810f, 0x20a9, 0x0002, 0x40a1, 0x20a1, 0xb7d2,
+ 0x2009, 0x0000, 0x080c, 0x14fb, 0x2001, 0x0000, 0x810f, 0x20a9,
+ 0x0002, 0x40a1, 0x7030, 0xc08c, 0x7032, 0x7003, 0x0003, 0x709f,
+ 0xffff, 0x080c, 0x1581, 0xa006, 0x080c, 0x27c3, 0x080c, 0x3f3e,
+ 0x00f6, 0x2079, 0x0100, 0x080c, 0x5af5, 0x0150, 0x080c, 0x5acf,
+ 0x7828, 0x0118, 0xa084, 0xe1ff, 0x0010, 0xa084, 0xffdf, 0x782a,
+ 0x00fe, 0x2001, 0xb7e1, 0x2004, 0xa086, 0x0005, 0x1120, 0x2011,
+ 0x0000, 0x080c, 0x8075, 0x2011, 0x0000, 0x080c, 0x807f, 0x080c,
+ 0x7173, 0x080c, 0x7230, 0x012e, 0x0005, 0x0016, 0x0046, 0x00f6,
+ 0x0126, 0x2091, 0x8000, 0x2079, 0x0100, 0x2009, 0xb534, 0x2104,
+ 0xa005, 0x1110, 0x080c, 0x2917, 0x2009, 0x00f7, 0x080c, 0x4baf,
+ 0x7940, 0xa18c, 0x0010, 0x7942, 0x7924, 0xd1b4, 0x0110, 0x7827,
+ 0x0040, 0xd19c, 0x0110, 0x7827, 0x0008, 0x0006, 0x0036, 0x0156,
+ 0x7954, 0xd1ac, 0x1904, 0x134b, 0x080c, 0x5ae1, 0x0158, 0x080c,
+ 0x5af5, 0x1128, 0x2001, 0xb79e, 0x2003, 0x0000, 0x0070, 0x080c,
+ 0x5ad7, 0x0dc0, 0x2001, 0xb79e, 0x2003, 0xaaaa, 0x2001, 0xb79f,
+ 0x2003, 0x0001, 0x080c, 0x5a07, 0x0058, 0x080c, 0x5acf, 0x0140,
+ 0x2009, 0x00f8, 0x080c, 0x4baf, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x20a9, 0x09c4, 0x7820, 0xd09c, 0x1138, 0x080c, 0x5acf, 0x0138,
+ 0x7824, 0xd0ac, 0x1904, 0x13f5, 0x1f04, 0x132a, 0x0070, 0x7824,
+ 0x080c, 0x5aeb, 0x0118, 0xd0ac, 0x1904, 0x13f5, 0xa084, 0x1800,
+ 0x0d98, 0x7003, 0x0001, 0x0804, 0x13f5, 0x2001, 0x0001, 0x080c,
+ 0x27c3, 0x0804, 0x1404, 0x7850, 0xa084, 0x0180, 0x7852, 0x782f,
+ 0x0020, 0x20a9, 0x0046, 0x1d04, 0x1353, 0x080c, 0x6a44, 0x1f04,
+ 0x1353, 0x7850, 0xa084, 0x0180, 0xa085, 0x0400, 0x7852, 0x782f,
+ 0x0000, 0x080c, 0x5ae1, 0x0158, 0x080c, 0x5af5, 0x1128, 0x2001,
+ 0xb79e, 0x2003, 0x0000, 0x0070, 0x080c, 0x5ad7, 0x0dc0, 0x2001,
+ 0xb79e, 0x2003, 0xaaaa, 0x2001, 0xb79f, 0x2003, 0x0001, 0x080c,
+ 0x5a07, 0x0020, 0x2009, 0x00f8, 0x080c, 0x4baf, 0x20a9, 0x000e,
+ 0xe000, 0x1f04, 0x1380, 0x7850, 0xa084, 0x0180, 0xa085, 0x1400,
+ 0x7852, 0x080c, 0x5acf, 0x0120, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x2021, 0xe678, 0x2019, 0xea60, 0x7820, 0xd09c, 0x1558, 0x080c,
+ 0x5acf, 0x05d8, 0x7824, 0xd0ac, 0x1904, 0x13f5, 0x080c, 0x5af5,
+ 0x1508, 0x0046, 0x2021, 0x0190, 0x8421, 0x1df0, 0x004e, 0x8421,
+ 0x11c8, 0x7827, 0x0048, 0x20a9, 0x01f4, 0x1d04, 0x13ad, 0x080c,
+ 0x6a44, 0x1f04, 0x13ad, 0x7824, 0xa084, 0x0068, 0x15c8, 0x2001,
+ 0xb79e, 0x2003, 0xaaaa, 0x2001, 0xb79f, 0x2003, 0x0001, 0x7003,
+ 0x0001, 0x0498, 0x1d04, 0x13c6, 0x080c, 0x6a44, 0x8319, 0x1960,
+ 0x2009, 0xb534, 0x2104, 0x8000, 0x200a, 0xa084, 0xfff0, 0x0120,
+ 0x200b, 0x0000, 0x080c, 0x2917, 0x00d8, 0x080c, 0x5ae1, 0x1140,
+ 0xa4a2, 0x0064, 0x1128, 0x080c, 0x5aa6, 0x7003, 0x0001, 0x00a8,
+ 0x7827, 0x1800, 0xe000, 0xe000, 0x7824, 0x080c, 0x5aeb, 0x0110,
+ 0xd0ac, 0x1158, 0xa084, 0x1800, 0x09a8, 0x7003, 0x0001, 0x0028,
+ 0x2001, 0x0001, 0x080c, 0x27c3, 0x0048, 0x2001, 0xb534, 0x2003,
+ 0x0000, 0x7827, 0x0048, 0x7828, 0xc09d, 0x782a, 0x7850, 0xa084,
+ 0x0180, 0xa085, 0x0400, 0x7852, 0x015e, 0x003e, 0x000e, 0x080c,
+ 0x1558, 0x012e, 0x00fe, 0x004e, 0x001e, 0x0005, 0x0005, 0x0005,
+ 0x0005, 0x2a70, 0x2061, 0xb7c1, 0x2063, 0x0002, 0x6007, 0x0002,
+ 0x600b, 0x0006, 0x600f, 0x0017, 0x2001, 0xb79e, 0x2003, 0x0000,
+ 0x708b, 0x0000, 0x2009, 0x0100, 0x2104, 0xa082, 0x0002, 0x0218,
+ 0x7053, 0xffff, 0x0010, 0x7053, 0x0000, 0x705b, 0xffff, 0x7073,
+ 0x0000, 0x7077, 0x0000, 0x080c, 0xa008, 0x2061, 0xb78e, 0x6003,
+ 0x0909, 0x6007, 0x0000, 0x600b, 0x8800, 0x600f, 0x0200, 0x6013,
+ 0x00ff, 0x6017, 0x000f, 0x601b, 0x0000, 0x601f, 0x07d0, 0x2061,
+ 0xb796, 0x6003, 0x8000, 0x6007, 0x0000, 0x600b, 0x0000, 0x600f,
+ 0x0200, 0x6013, 0x00ff, 0x6017, 0x0000, 0x601b, 0x0001, 0x601f,
+ 0x0000, 0x2061, 0xb7b9, 0x6003, 0x514c, 0x6007, 0x4f47, 0x600b,
+ 0x4943, 0x600f, 0x2020, 0x2001, 0xb528, 0x2003, 0x0000, 0x0005,
+ 0x04a0, 0x2011, 0x0000, 0x81ff, 0x0570, 0xa186, 0x0001, 0x1148,
+ 0x2031, 0x8fff, 0x2039, 0xd501, 0x2021, 0x0100, 0x2029, 0xd500,
+ 0x00e8, 0xa186, 0x0002, 0x1118, 0x2011, 0x0000, 0x00b8, 0xa186,
+ 0x0005, 0x1118, 0x2011, 0x0001, 0x0088, 0xa186, 0x0009, 0x1118,
+ 0x2011, 0x0002, 0x0058, 0xa186, 0x000a, 0x1118, 0x2011, 0x0002,
+ 0x0028, 0xa186, 0x0055, 0x1110, 0x2011, 0x0003, 0x3800, 0xa084,
+ 0xfffc, 0xa205, 0x20c0, 0x0804, 0x104d, 0xa00e, 0x2011, 0x0003,
+ 0x2019, 0x14a4, 0x0804, 0x14f5, 0x2019, 0xaaaa, 0x2061, 0xffff,
+ 0x2c14, 0x2362, 0xe000, 0xe000, 0x2c04, 0xa306, 0x2262, 0x1110,
+ 0xc1b5, 0xc1a5, 0x2011, 0x0000, 0x2019, 0x14b7, 0x04f0, 0x2019,
+ 0xaaaa, 0x2061, 0xffff, 0x2c14, 0x2362, 0xe000, 0xe000, 0x2c1c,
+ 0x2061, 0x7fff, 0xe000, 0xe000, 0x2c04, 0x2061, 0xffff, 0x2262,
+ 0xa306, 0x0110, 0xc18d, 0x0008, 0xc185, 0x2011, 0x0002, 0x2019,
+ 0x14d2, 0x0418, 0x2061, 0xffff, 0x2019, 0xaaaa, 0x2c14, 0x2362,
+ 0xe000, 0xe000, 0x2c04, 0x2262, 0xa306, 0x1180, 0x2c14, 0x2362,
+ 0xe000, 0xe000, 0x2c1c, 0x2061, 0x7fff, 0x2c04, 0x2061, 0xffff,
+ 0x2262, 0xa306, 0x1110, 0xc195, 0x0008, 0xc19d, 0x2011, 0x0001,
+ 0x2019, 0x14f3, 0x0010, 0x0804, 0x1469, 0x3800, 0xa084, 0xfffc,
+ 0xa205, 0x20c0, 0x0837, 0x2011, 0x0000, 0x080c, 0x4fa9, 0x1178,
+ 0x6004, 0xa0c4, 0x00ff, 0xa8c6, 0x0006, 0x0128, 0xa0c4, 0xff00,
+ 0xa8c6, 0x0600, 0x1120, 0xa186, 0x0080, 0x0108, 0x8210, 0x8108,
+ 0xa186, 0x0100, 0x1d50, 0x2208, 0x0005, 0x2091, 0x8000, 0x0e04,
+ 0x1517, 0x0006, 0x0016, 0x2079, 0x0000, 0x7818, 0xd084, 0x1de8,
+ 0x001e, 0x792e, 0x000e, 0x782a, 0x000e, 0x7826, 0x3900, 0x783a,
+ 0x7823, 0x8002, 0x781b, 0x0001, 0x2091, 0x5000, 0x0126, 0x0156,
+ 0x0146, 0x20a9, 0x0010, 0x20a1, 0xb90c, 0x2091, 0x2000, 0x40a1,
+ 0x20a9, 0x0010, 0x2091, 0x2200, 0x40a1, 0x20a9, 0x0010, 0x2091,
+ 0x2400, 0x40a1, 0x20a9, 0x0010, 0x2091, 0x2600, 0x40a1, 0x20a9,
+ 0x0010, 0x2091, 0x2800, 0x40a1, 0x014e, 0x015e, 0x012e, 0x2079,
+ 0xb500, 0x7803, 0x0005, 0x2091, 0x4080, 0x04c9, 0x0cf8, 0x0005,
+ 0x0006, 0x080c, 0x15a3, 0x1518, 0x00f6, 0x2079, 0xb524, 0x2f04,
+ 0x8000, 0x207a, 0xa082, 0x000f, 0x0258, 0xa006, 0x207a, 0x2079,
+ 0xb526, 0x2f04, 0xa084, 0x0001, 0xa086, 0x0001, 0x207a, 0x0070,
+ 0x2079, 0xb526, 0x2f7c, 0x8fff, 0x1128, 0x2001, 0x0c03, 0x2003,
+ 0x0040, 0x0020, 0x2001, 0x0c03, 0x2003, 0x00c0, 0x00fe, 0x000e,
+ 0x0005, 0x0409, 0x1120, 0x2001, 0x0c03, 0x2003, 0x0080, 0x0005,
+ 0x00d1, 0x1120, 0x2001, 0x0c03, 0x2003, 0x0040, 0x0005, 0x0006,
+ 0x0091, 0x1178, 0x2001, 0x0c03, 0x2003, 0x0040, 0x2009, 0x0fff,
+ 0x00a1, 0x2001, 0x0c03, 0x2003, 0x0080, 0x2009, 0x0fff, 0x0069,
+ 0x0c88, 0x000e, 0x0005, 0x00c6, 0x2061, 0x0c00, 0x2c04, 0xa084,
+ 0x00ff, 0xa086, 0x00aa, 0x00ce, 0x0005, 0x0156, 0x0126, 0xa18c,
+ 0x0fff, 0x21a8, 0x1d04, 0x15b2, 0x2091, 0x6000, 0x1f04, 0x15b2,
+ 0x012e, 0x015e, 0x0005, 0x2071, 0xb500, 0x7160, 0x712e, 0x2021,
+ 0x0001, 0xa190, 0x0030, 0xa298, 0x0030, 0x0240, 0x7064, 0xa302,
+ 0x1228, 0x220a, 0x2208, 0x2310, 0x8420, 0x0ca8, 0x3800, 0xd08c,
+ 0x0148, 0x7064, 0xa086, 0xb500, 0x0128, 0x7067, 0xb500, 0x2011,
+ 0x1000, 0x0c48, 0x200b, 0x0000, 0x74b2, 0x74b6, 0x0005, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0xb500, 0x70b4, 0xa0ea, 0x0010,
+ 0x0268, 0x8001, 0x70b6, 0x702c, 0x2068, 0x2d04, 0x702e, 0x206b,
+ 0x0000, 0x6807, 0x0000, 0x012e, 0x00ee, 0x0005, 0xa06e, 0x0cd8,
+ 0x00e6, 0x2071, 0xb500, 0x0126, 0x2091, 0x8000, 0x70b4, 0x8001,
+ 0x0260, 0x70b6, 0x702c, 0x2068, 0x2d04, 0x702e, 0x206b, 0x0000,
+ 0x6807, 0x0000, 0x012e, 0x00ee, 0x0005, 0xa06e, 0x0cd8, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0xb500, 0x702c, 0x206a, 0x2d00,
+ 0x702e, 0x70b4, 0x8000, 0x70b6, 0x012e, 0x00ee, 0x0005, 0x8dff,
+ 0x0138, 0x6804, 0x6807, 0x0000, 0x0006, 0x0c49, 0x00de, 0x0cb8,
+ 0x0005, 0x00e6, 0x2071, 0xb500, 0x70b4, 0xa08a, 0x0010, 0xa00d,
+ 0x00ee, 0x0005, 0x00e6, 0x2071, 0xb812, 0x7007, 0x0000, 0x701b,
+ 0x0000, 0x701f, 0x0000, 0x2071, 0x0000, 0x7010, 0xa085, 0x8004,
+ 0x7012, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000, 0x00e6, 0x2270,
+ 0x700b, 0x0000, 0x2071, 0xb812, 0x7018, 0xa088, 0xb81b, 0x220a,
+ 0x8000, 0xa084, 0x0007, 0x701a, 0x7004, 0xa005, 0x1128, 0x00f6,
+ 0x2079, 0x0010, 0x0089, 0x00fe, 0x00ee, 0x012e, 0x0005, 0x00e6,
+ 0x2071, 0xb812, 0x7004, 0xa005, 0x1128, 0x00f6, 0x2079, 0x0010,
+ 0x0019, 0x00fe, 0x00ee, 0x0005, 0x7000, 0x0002, 0x1672, 0x16d6,
+ 0x16f3, 0x16f3, 0x7018, 0x711c, 0xa106, 0x1118, 0x7007, 0x0000,
+ 0x0005, 0x00d6, 0xa180, 0xb81b, 0x2004, 0x700a, 0x2068, 0x8108,
+ 0xa18c, 0x0007, 0x711e, 0x7803, 0x0026, 0x6824, 0x7832, 0x6828,
+ 0x7836, 0x682c, 0x783a, 0x6830, 0x783e, 0x6810, 0x700e, 0x680c,
+ 0x7016, 0x6804, 0x00de, 0xd084, 0x0120, 0x7007, 0x0001, 0x0029,
+ 0x0005, 0x7007, 0x0002, 0x00b1, 0x0005, 0x0016, 0x0026, 0x710c,
+ 0x2011, 0x0040, 0xa182, 0x0040, 0x1210, 0x2110, 0xa006, 0x700e,
+ 0x7212, 0x8203, 0x7822, 0x7803, 0x0020, 0x7803, 0x0041, 0x002e,
+ 0x001e, 0x0005, 0x0016, 0x0026, 0x0136, 0x0146, 0x0156, 0x7014,
+ 0x2098, 0x20a1, 0x0014, 0x7803, 0x0026, 0x710c, 0x2011, 0x0040,
+ 0xa182, 0x0040, 0x1210, 0x2110, 0xa006, 0x700e, 0x22a8, 0x53a6,
+ 0x8203, 0x7822, 0x7803, 0x0020, 0x3300, 0x7016, 0x7803, 0x0001,
+ 0x015e, 0x014e, 0x013e, 0x002e, 0x001e, 0x0005, 0x0136, 0x0146,
+ 0x0156, 0x2099, 0xb5fa, 0x20a1, 0x0018, 0x20a9, 0x0008, 0x53a3,
+ 0x7803, 0x0020, 0x0126, 0x2091, 0x8000, 0x7803, 0x0041, 0x7007,
+ 0x0003, 0x7000, 0xc084, 0x7002, 0x700b, 0xb5f5, 0x012e, 0x015e,
+ 0x014e, 0x013e, 0x0005, 0x0136, 0x0146, 0x0156, 0x2001, 0xb629,
+ 0x209c, 0x20a1, 0x0014, 0x7803, 0x0026, 0x2001, 0xb62a, 0x20ac,
+ 0x53a6, 0x2099, 0xb62b, 0x20a1, 0x0018, 0x20a9, 0x0008, 0x53a3,
+ 0x7803, 0x0020, 0x0126, 0x2091, 0x8000, 0x7803, 0x0001, 0x7007,
+ 0x0004, 0x7000, 0xc08c, 0x7002, 0x700b, 0xb626, 0x012e, 0x015e,
+ 0x014e, 0x013e, 0x0005, 0x0016, 0x00e6, 0x2071, 0xb812, 0x00f6,
+ 0x2079, 0x0010, 0x7904, 0x7803, 0x0002, 0xd1fc, 0x0120, 0xa18c,
+ 0x0700, 0x7004, 0x0023, 0x00fe, 0x00ee, 0x001e, 0x0005, 0x166c,
+ 0x1736, 0x1764, 0x178e, 0x17be, 0x1735, 0x0cf8, 0xa18c, 0x0700,
+ 0x1528, 0x0136, 0x0146, 0x0156, 0x7014, 0x20a0, 0x2099, 0x0014,
+ 0x7803, 0x0040, 0x7010, 0x20a8, 0x53a5, 0x3400, 0x7016, 0x015e,
+ 0x014e, 0x013e, 0x700c, 0xa005, 0x0570, 0x7830, 0x7832, 0x7834,
+ 0x7836, 0x080c, 0x169d, 0x0005, 0x7008, 0xa080, 0x0002, 0x2003,
+ 0x0100, 0x7007, 0x0000, 0x080c, 0x166c, 0x0005, 0x7008, 0xa080,
+ 0x0002, 0x2003, 0x0200, 0x0ca8, 0xa18c, 0x0700, 0x1150, 0x700c,
+ 0xa005, 0x0188, 0x7830, 0x7832, 0x7834, 0x7836, 0x080c, 0x16b2,
+ 0x0005, 0x7008, 0xa080, 0x0002, 0x2003, 0x0200, 0x7007, 0x0000,
+ 0x080c, 0x166c, 0x0005, 0x00d6, 0x7008, 0x2068, 0x7830, 0x6826,
+ 0x7834, 0x682a, 0x7838, 0x682e, 0x783c, 0x6832, 0x680b, 0x0100,
+ 0x00de, 0x7007, 0x0000, 0x080c, 0x166c, 0x0005, 0xa18c, 0x0700,
+ 0x1540, 0x0136, 0x0146, 0x0156, 0x2001, 0xb5f8, 0x2004, 0xa080,
+ 0x000d, 0x20a0, 0x2099, 0x0014, 0x7803, 0x0040, 0x20a9, 0x0020,
+ 0x53a5, 0x2001, 0xb5fa, 0x2004, 0xd0bc, 0x0148, 0x2001, 0xb603,
+ 0x2004, 0xa080, 0x000d, 0x20a0, 0x20a9, 0x0020, 0x53a5, 0x015e,
+ 0x014e, 0x013e, 0x7007, 0x0000, 0x080c, 0x5e6f, 0x080c, 0x166c,
+ 0x0005, 0x2011, 0x8003, 0x080c, 0x3ecc, 0x0cf8, 0xa18c, 0x0700,
+ 0x1148, 0x2001, 0xb628, 0x2003, 0x0100, 0x7007, 0x0000, 0x080c,
+ 0x166c, 0x0005, 0x2011, 0x8004, 0x080c, 0x3ecc, 0x0cf8, 0x0126,
+ 0x2091, 0x2200, 0x2079, 0x0030, 0x2071, 0xb823, 0x7003, 0x0000,
+ 0x700f, 0xb82f, 0x7013, 0xb82f, 0x780f, 0x00f6, 0x7803, 0x0004,
+ 0x012e, 0x0005, 0x6934, 0xa184, 0x0007, 0x0002, 0x17ee, 0x182c,
+ 0x17ee, 0x17ee, 0x17ee, 0x1814, 0x17fb, 0x17f2, 0xa085, 0x0001,
+ 0x0804, 0x1846, 0x684c, 0xd0bc, 0x0dc8, 0x6860, 0x682e, 0x685c,
+ 0x682a, 0x6858, 0x04c8, 0xa18c, 0x00ff, 0xa186, 0x001e, 0x1d70,
+ 0x684c, 0xd0bc, 0x0d58, 0x6860, 0x682e, 0x685c, 0x682a, 0x6804,
+ 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080, 0x22e5,
+ 0x2005, 0x6832, 0x6858, 0x0440, 0xa18c, 0x00ff, 0xa186, 0x0015,
+ 0x19a8, 0x684c, 0xd0ac, 0x0990, 0x6804, 0x681a, 0xa080, 0x000d,
+ 0x2004, 0xa084, 0x000f, 0xa080, 0x22e5, 0x2005, 0x6832, 0xa006,
+ 0x682e, 0x682a, 0x6858, 0x0080, 0x684c, 0xd0ac, 0x0904, 0x17ee,
+ 0xa006, 0x682e, 0x682a, 0x6858, 0xa18c, 0x000f, 0xa188, 0x22e5,
+ 0x210d, 0x6932, 0x2d08, 0x691a, 0x6826, 0x684c, 0xc0dd, 0x684e,
+ 0xa006, 0x680a, 0x697c, 0x6912, 0x6980, 0x6916, 0x0005, 0x684c,
+ 0xd0ac, 0x090c, 0x1515, 0x6833, 0x22e2, 0x2d08, 0x691a, 0x6858,
+ 0x8001, 0x6826, 0x684c, 0xc0dd, 0x684e, 0xa006, 0x680a, 0x682e,
+ 0x682a, 0x697c, 0x6912, 0x6980, 0x6916, 0x0005, 0x20e1, 0x0007,
+ 0x20e1, 0x2000, 0x2001, 0x020a, 0x2004, 0x82ff, 0x01e8, 0xa280,
+ 0x0004, 0x00d6, 0x206c, 0x684c, 0xd0dc, 0x1190, 0xa280, 0x0007,
+ 0x2004, 0xa086, 0x000a, 0x1110, 0x0891, 0x0010, 0x080c, 0x17e2,
+ 0x0138, 0x00de, 0xa280, 0x0000, 0x2003, 0x0002, 0xa016, 0x0020,
+ 0x6808, 0x8000, 0x680a, 0x00de, 0x0126, 0x0046, 0x0036, 0x0026,
+ 0x2091, 0x2200, 0x002e, 0x003e, 0x004e, 0x7000, 0xa005, 0x01d0,
+ 0x710c, 0x220a, 0x8108, 0x230a, 0x8108, 0x240a, 0x8108, 0xa182,
+ 0xb84a, 0x0210, 0x2009, 0xb82f, 0x710e, 0x7010, 0xa102, 0xa082,
+ 0x0009, 0x0118, 0xa080, 0x001b, 0x1118, 0x2009, 0x0138, 0x200a,
+ 0x012e, 0x0005, 0x7206, 0x2001, 0x18a8, 0x0006, 0x2260, 0x0804,
+ 0x19d5, 0x0126, 0x0026, 0x0036, 0x00c6, 0x0006, 0x2091, 0x2200,
+ 0x000e, 0x004e, 0x003e, 0x002e, 0x00d6, 0x00c6, 0x2460, 0x6110,
+ 0x2168, 0x6a62, 0x6b5e, 0xa005, 0x0904, 0x190a, 0x6808, 0xa005,
+ 0x0904, 0x1941, 0x7000, 0xa005, 0x1108, 0x0488, 0x700c, 0x7110,
+ 0xa106, 0x1904, 0x1949, 0x7004, 0xa406, 0x1548, 0x2001, 0x0005,
+ 0x2004, 0xd08c, 0x0168, 0x0046, 0x080c, 0x1b06, 0x004e, 0x2460,
+ 0x6010, 0xa080, 0x0002, 0x2004, 0xa005, 0x0904, 0x1941, 0x0c10,
+ 0x2001, 0x0207, 0x2004, 0xd09c, 0x1d48, 0x7804, 0xa084, 0x6000,
+ 0x0120, 0xa086, 0x6000, 0x0108, 0x0c08, 0x7818, 0x6812, 0x781c,
+ 0x6816, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0x2060, 0x6100,
+ 0xa18e, 0x0004, 0x1904, 0x1949, 0x2009, 0x0048, 0x080c, 0x864c,
+ 0x0804, 0x1949, 0x6808, 0xa005, 0x05a0, 0x7000, 0xa005, 0x0588,
+ 0x700c, 0x7110, 0xa106, 0x1118, 0x7004, 0xa406, 0x1550, 0x2001,
+ 0x0005, 0x2004, 0xd08c, 0x0160, 0x0046, 0x080c, 0x1b06, 0x004e,
+ 0x2460, 0x6010, 0xa080, 0x0002, 0x2004, 0xa005, 0x01d0, 0x0c28,
+ 0x2001, 0x0207, 0x2004, 0xd09c, 0x1d50, 0x2001, 0x0005, 0x2004,
+ 0xd08c, 0x1d50, 0x7804, 0xa084, 0x6000, 0x0118, 0xa086, 0x6000,
+ 0x19f0, 0x7818, 0x6812, 0x781c, 0x6816, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0x6100, 0xa18e, 0x0004, 0x1120, 0x2009, 0x0048, 0x080c,
+ 0x864c, 0x00ce, 0x00de, 0x012e, 0x0005, 0x00f6, 0x00e6, 0x0026,
+ 0x0036, 0x0046, 0x0056, 0x2071, 0xb823, 0x7000, 0xa086, 0x0000,
+ 0x0904, 0x19b3, 0x7004, 0xac06, 0x1904, 0x19a5, 0x2079, 0x0030,
+ 0x7000, 0xa086, 0x0003, 0x0904, 0x19a5, 0x7804, 0xd0fc, 0x15c8,
+ 0x20e1, 0x6000, 0x2011, 0x0032, 0x2001, 0x0208, 0x200c, 0x2001,
+ 0x0209, 0x2004, 0xa106, 0x1d88, 0x8211, 0x1db0, 0x7804, 0xd0fc,
+ 0x1540, 0x080c, 0x1e6e, 0x0026, 0x0056, 0x7803, 0x0004, 0x7804,
+ 0xd0ac, 0x1de8, 0x7803, 0x0002, 0x7803, 0x0009, 0x7003, 0x0003,
+ 0x7007, 0x0000, 0x005e, 0x002e, 0x2001, 0x015d, 0x2003, 0x0000,
+ 0x080c, 0x5acf, 0x1138, 0x0066, 0x2031, 0x0001, 0x080c, 0x5b51,
+ 0x006e, 0x0058, 0x2001, 0x0160, 0x2502, 0x2001, 0x0138, 0x2202,
+ 0x0020, 0x080c, 0x1b06, 0x0804, 0x1955, 0x0156, 0x20a9, 0x0009,
+ 0x2009, 0xb82f, 0x2104, 0xac06, 0x1108, 0x200a, 0xa188, 0x0003,
+ 0x1f04, 0x19aa, 0x015e, 0x005e, 0x004e, 0x003e, 0x002e, 0x00ee,
+ 0x00fe, 0x0005, 0x700c, 0x7110, 0xa106, 0x0904, 0x1a49, 0x2104,
+ 0x7006, 0x2060, 0x8108, 0x211c, 0x8108, 0x2124, 0x8108, 0xa182,
+ 0xb84a, 0x0210, 0x2009, 0xb82f, 0x7112, 0x700c, 0xa106, 0x1128,
+ 0x080c, 0x28eb, 0x2001, 0x0138, 0x2102, 0x8cff, 0x0598, 0x6010,
+ 0x2068, 0x2d58, 0x6828, 0xa406, 0x1590, 0x682c, 0xa306, 0x1578,
+ 0x7004, 0x2060, 0x6020, 0xc0d4, 0x6022, 0x684c, 0xd0f4, 0x0128,
+ 0x6817, 0xffff, 0x6813, 0xffff, 0x00e8, 0x6850, 0xd0f4, 0x1130,
+ 0x7803, 0x0004, 0x6810, 0x781a, 0x6814, 0x781e, 0x6824, 0x2050,
+ 0x6818, 0x2060, 0x6830, 0x2040, 0x6034, 0xa0cc, 0x000f, 0x2009,
+ 0x0011, 0x080c, 0x1a4c, 0x0120, 0x2009, 0x0001, 0x080c, 0x1a4c,
+ 0x2d58, 0x0005, 0x080c, 0x1ddd, 0x0904, 0x19ba, 0x0cd0, 0x6020,
+ 0xd0f4, 0x11e0, 0xd0d4, 0x01b8, 0x6038, 0xa402, 0x6034, 0xa303,
+ 0x0108, 0x1288, 0x643a, 0x6336, 0x6c2a, 0x6b2e, 0x0046, 0x0036,
+ 0x2400, 0x6c7c, 0xa402, 0x6812, 0x2300, 0x6b80, 0xa303, 0x6816,
+ 0x003e, 0x004e, 0x0018, 0x080c, 0x9f9a, 0x09e0, 0x601c, 0xa08e,
+ 0x0008, 0x0904, 0x19e0, 0xa08e, 0x000a, 0x0904, 0x19e0, 0x2001,
+ 0xb574, 0x2004, 0xd0b4, 0x1140, 0x6018, 0x2004, 0xd0bc, 0x1120,
+ 0x6817, 0x7fff, 0x6813, 0xffff, 0x080c, 0x2305, 0x1918, 0x0804,
+ 0x19e0, 0x7003, 0x0000, 0x0005, 0x8aff, 0x0904, 0x1ae0, 0xa03e,
+ 0x2730, 0xc9fc, 0x6850, 0xd0fc, 0x11b8, 0xd0f4, 0x1528, 0x00d6,
+ 0x2805, 0xac68, 0x2900, 0x0002, 0x1a9e, 0x1a82, 0x1a82, 0x1a9e,
+ 0x1a9e, 0x1a96, 0x1a9e, 0x1a82, 0x1a9e, 0x1a87, 0x1a87, 0x1a9e,
+ 0x1a9e, 0x1a9e, 0x1a8e, 0x1a87, 0x7803, 0x0004, 0xc0fc, 0x6852,
+ 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0x00d6, 0xd99c, 0x0550, 0x2805,
+ 0xac68, 0x6f08, 0x6e0c, 0x0430, 0xc0f4, 0x6852, 0x6b6c, 0x6a70,
+ 0x00d6, 0x0468, 0x6b08, 0x6a0c, 0x6d00, 0x6c04, 0x00d0, 0x6b10,
+ 0x6a14, 0x6d00, 0x6c04, 0x6f08, 0x6e0c, 0x00a0, 0x00de, 0x00d6,
+ 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e, 0x1140, 0x00de, 0x080c,
+ 0x22a7, 0x1904, 0x1a4c, 0xa00e, 0x0804, 0x1ae0, 0x00de, 0x080c,
+ 0x1515, 0xc9fd, 0x7b22, 0x7a26, 0x7d32, 0x7c36, 0x7f3a, 0x7e3e,
+ 0x7316, 0x721a, 0x751e, 0x7422, 0x7726, 0x762a, 0x7902, 0x7100,
+ 0x8108, 0x7102, 0x00de, 0x6828, 0xa300, 0x682a, 0x682c, 0xa201,
+ 0x682e, 0x8109, 0x2d08, 0x1500, 0xd9fc, 0x0160, 0xc9fc, 0x080c,
+ 0x22a7, 0x01e8, 0x2805, 0xac68, 0x6800, 0xa506, 0x11c0, 0x6804,
+ 0xa406, 0x00a8, 0xc9fc, 0x080c, 0x22a7, 0x0188, 0x2805, 0xac68,
+ 0x6800, 0xa506, 0x1160, 0x6804, 0xa406, 0x1148, 0x6808, 0xa706,
+ 0x1130, 0x680c, 0xa606, 0x0018, 0xc9fc, 0x080c, 0x22a7, 0x2168,
+ 0x0005, 0x080c, 0x1515, 0x080c, 0x1f55, 0x7004, 0x2060, 0x00d6,
+ 0x6010, 0x2068, 0x7003, 0x0000, 0x080c, 0x1dfe, 0x080c, 0x9c5a,
+ 0x0170, 0x6808, 0x8001, 0x680a, 0x697c, 0x6912, 0x6980, 0x6916,
+ 0x682b, 0xffff, 0x682f, 0xffff, 0x6850, 0xc0bd, 0x6852, 0x00de,
+ 0x080c, 0x992a, 0x0804, 0x1d2b, 0x080c, 0x1515, 0x0126, 0x2091,
+ 0x2200, 0x0006, 0x0016, 0x2b68, 0x6818, 0x2060, 0x7904, 0x7803,
+ 0x0002, 0xa184, 0x0700, 0x1978, 0xa184, 0x0003, 0xa086, 0x0003,
+ 0x0d58, 0x7000, 0x0002, 0x1b23, 0x1b29, 0x1c3a, 0x1d06, 0x1d1a,
+ 0x1b23, 0x1b23, 0x1b23, 0x7804, 0xd09c, 0x1904, 0x1d2b, 0x080c,
+ 0x1515, 0x8001, 0x7002, 0xd1bc, 0x11a0, 0xd19c, 0x1904, 0x1bbe,
+ 0xd1dc, 0x1178, 0x8aff, 0x0904, 0x1bbe, 0x2009, 0x0001, 0x080c,
+ 0x1a4c, 0x0904, 0x1d2b, 0x2009, 0x0001, 0x080c, 0x1a4c, 0x0804,
+ 0x1d2b, 0x7803, 0x0004, 0x7003, 0x0000, 0xd1bc, 0x1904, 0x1b9e,
+ 0x0026, 0x0036, 0x7c20, 0x7d24, 0x7e30, 0x7f34, 0x7818, 0x6812,
+ 0x781c, 0x6816, 0x2001, 0x0201, 0x2004, 0xa005, 0x0140, 0x7808,
+ 0xd0ec, 0x1128, 0x7803, 0x0009, 0x7003, 0x0004, 0x0010, 0x080c,
+ 0x1d2f, 0x6b28, 0x6a2c, 0x2400, 0x686e, 0xa31a, 0x2500, 0x6872,
+ 0xa213, 0x6b2a, 0x6a2e, 0x00c6, 0x7004, 0x2060, 0x6020, 0xd0f4,
+ 0x1110, 0x633a, 0x6236, 0x00ce, 0x003e, 0x002e, 0x6e1e, 0x6f22,
+ 0x2500, 0xa405, 0x0128, 0x080c, 0x22bd, 0x6850, 0xc0fd, 0x6852,
+ 0x2a00, 0x6826, 0x2c00, 0x681a, 0x2800, 0x6832, 0x6808, 0x8001,
+ 0x680a, 0x1148, 0x684c, 0xd0e4, 0x0130, 0x7004, 0x2060, 0x2009,
+ 0x0048, 0x080c, 0x864c, 0x7000, 0xa086, 0x0004, 0x0904, 0x1d2b,
+ 0x7003, 0x0000, 0x080c, 0x19ba, 0x0804, 0x1d2b, 0x0056, 0x7d0c,
+ 0xd5bc, 0x1110, 0x080c, 0xb407, 0x005e, 0x080c, 0x1dfe, 0x00f6,
+ 0x7004, 0x2078, 0x080c, 0x5305, 0x0118, 0x7820, 0xc0f5, 0x7822,
+ 0x00fe, 0x682b, 0xffff, 0x682f, 0xffff, 0x6808, 0x8001, 0x680a,
+ 0x697c, 0x791a, 0x6980, 0x791e, 0x0804, 0x1d2b, 0x7004, 0x00c6,
+ 0x2060, 0x6020, 0x00ce, 0xd0f4, 0x0120, 0x6808, 0x8001, 0x680a,
+ 0x04c0, 0x7818, 0x6812, 0x7a1c, 0x6a16, 0xd19c, 0x0160, 0xa205,
+ 0x0150, 0x7004, 0xa080, 0x0007, 0x2004, 0xa084, 0xfffd, 0xa086,
+ 0x0008, 0x1904, 0x1b41, 0x684c, 0xc0f5, 0x684e, 0x7814, 0xa005,
+ 0x1520, 0x7003, 0x0000, 0x6808, 0x8001, 0x680a, 0x01a0, 0x7004,
+ 0x2060, 0x601c, 0xa086, 0x000a, 0x11a0, 0x0156, 0x20a9, 0x0009,
+ 0x2009, 0xb82f, 0x2104, 0xac06, 0x1108, 0x200a, 0xa188, 0x0003,
+ 0x1f04, 0x1bf2, 0x015e, 0x7004, 0x2060, 0x2009, 0x0048, 0x080c,
+ 0x864c, 0x080c, 0x19ba, 0x0804, 0x1d2b, 0x7818, 0x6812, 0x781c,
+ 0x6816, 0x7814, 0x7908, 0xa18c, 0x0fff, 0xa192, 0x0841, 0x1a04,
+ 0x1ae3, 0xa188, 0x0007, 0x8114, 0x8214, 0x8214, 0xa10a, 0x8104,
+ 0x8004, 0x8004, 0xa20a, 0x810b, 0x810b, 0x810b, 0x080c, 0x1e99,
+ 0x7803, 0x0004, 0x780f, 0xffff, 0x7803, 0x0001, 0x7804, 0xd0fc,
+ 0x0de8, 0x7803, 0x0002, 0x7803, 0x0004, 0x780f, 0x00f6, 0x7004,
+ 0x7007, 0x0000, 0x2060, 0x2009, 0x0048, 0x080c, 0x864c, 0x080c,
+ 0x1eef, 0x0838, 0x8001, 0x7002, 0xd194, 0x01b0, 0x7804, 0xd0fc,
+ 0x1904, 0x1cd6, 0xd09c, 0x0138, 0x7804, 0xd0fc, 0x1904, 0x1cd6,
+ 0xd09c, 0x1904, 0x1cda, 0x8aff, 0x0904, 0x1d2b, 0x2009, 0x0001,
+ 0x080c, 0x1a4c, 0x0804, 0x1d2b, 0xa184, 0x0888, 0x1148, 0x8aff,
+ 0x0904, 0x1d2b, 0x2009, 0x0001, 0x080c, 0x1a4c, 0x0804, 0x1d2b,
+ 0x7818, 0x6812, 0x7a1c, 0x6a16, 0xa205, 0x0904, 0x1bdb, 0x7803,
+ 0x0004, 0x7003, 0x0000, 0xd1bc, 0x1904, 0x1cb8, 0x6834, 0xa084,
+ 0x00ff, 0xa086, 0x0029, 0x1118, 0xd19c, 0x1904, 0x1bdb, 0x0026,
+ 0x0036, 0x7c20, 0x7d24, 0x7e30, 0x7f34, 0x7818, 0x6812, 0x781c,
+ 0x6816, 0x2001, 0x0201, 0x2004, 0xa005, 0x0140, 0x7808, 0xd0ec,
+ 0x1128, 0x7803, 0x0009, 0x7003, 0x0004, 0x0020, 0x0016, 0x080c,
+ 0x1d2f, 0x001e, 0x6b28, 0x6a2c, 0x080c, 0x22bd, 0x00d6, 0x2805,
+ 0xac68, 0x6034, 0xd09c, 0x1128, 0x6808, 0xa31a, 0x680c, 0xa213,
+ 0x0020, 0x6810, 0xa31a, 0x6814, 0xa213, 0x00de, 0xd194, 0x0904,
+ 0x1b63, 0x2a00, 0x6826, 0x2c00, 0x681a, 0x2800, 0x6832, 0x6808,
+ 0x8001, 0x680a, 0x6b2a, 0x6a2e, 0x003e, 0x002e, 0x0804, 0x1c01,
+ 0x0056, 0x7d0c, 0x080c, 0xb407, 0x005e, 0x080c, 0x1dfe, 0x00f6,
+ 0x7004, 0x2078, 0x080c, 0x5305, 0x0118, 0x7820, 0xc0f5, 0x7822,
+ 0x00fe, 0x682b, 0xffff, 0x682f, 0xffff, 0x6808, 0x8001, 0x680a,
+ 0x697c, 0x791a, 0x6980, 0x791e, 0x0804, 0x1d2b, 0x7804, 0xd09c,
+ 0x0904, 0x1b0e, 0x7c20, 0x7824, 0xa405, 0x1904, 0x1b0e, 0x7818,
+ 0x6812, 0x7c1c, 0x6c16, 0xa405, 0x1120, 0x7803, 0x0002, 0x0804,
+ 0x1bdb, 0x751c, 0x7420, 0x7724, 0x7628, 0x7014, 0xa528, 0x7018,
+ 0xa421, 0xa7b9, 0x0000, 0xa6b1, 0x0000, 0x7830, 0xa506, 0x1150,
+ 0x7834, 0xa406, 0x1138, 0x7838, 0xa706, 0x1120, 0x783c, 0xa606,
+ 0x0904, 0x1b0e, 0x7803, 0x0002, 0x0804, 0x1c67, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0x7004, 0xa00d, 0x0150, 0x6808, 0x8001, 0x680a,
+ 0x1130, 0x7004, 0x2060, 0x2009, 0x0048, 0x080c, 0x864c, 0x080c,
+ 0x19ba, 0x0088, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0x2060,
+ 0x6010, 0xa005, 0x0da0, 0x2068, 0x6808, 0x8000, 0x680a, 0x6c28,
+ 0x6b2c, 0x080c, 0x19d5, 0x001e, 0x000e, 0x012e, 0x0005, 0x700c,
+ 0x7110, 0xa106, 0x0904, 0x1dd1, 0x7004, 0x0016, 0x210c, 0xa106,
+ 0x001e, 0x0904, 0x1dd1, 0x00d6, 0x00c6, 0x216c, 0x2d00, 0xa005,
+ 0x0904, 0x1dcf, 0x681c, 0xa086, 0x0008, 0x0904, 0x1dcf, 0x6820,
+ 0xd0d4, 0x1904, 0x1dcf, 0x6810, 0x2068, 0x6850, 0xd0fc, 0x05a8,
+ 0x8108, 0x2104, 0x6b2c, 0xa306, 0x1904, 0x1dcf, 0x8108, 0x2104,
+ 0x6a28, 0xa206, 0x1904, 0x1dcf, 0x6850, 0xc0fc, 0xc0f5, 0x6852,
+ 0x686c, 0x7822, 0x7016, 0x6870, 0x7826, 0x701a, 0x681c, 0x7832,
+ 0x701e, 0x6820, 0x7836, 0x7022, 0x6818, 0x2060, 0x6034, 0xd09c,
+ 0x0168, 0x6830, 0x2005, 0x00d6, 0xac68, 0x6808, 0x783a, 0x7026,
+ 0x680c, 0x783e, 0x702a, 0x00de, 0x0804, 0x1dc9, 0xa006, 0x783a,
+ 0x783e, 0x7026, 0x702a, 0x0804, 0x1dc9, 0x8108, 0x2104, 0xa005,
+ 0x1904, 0x1dcf, 0x6b2c, 0xa306, 0x1904, 0x1dcf, 0x8108, 0x2104,
+ 0xa005, 0x15e8, 0x6a28, 0xa206, 0x15d0, 0x6850, 0xc0f5, 0x6852,
+ 0x6830, 0x2005, 0x6918, 0xa160, 0xa180, 0x000d, 0x2004, 0xd09c,
+ 0x11a0, 0x6008, 0x7822, 0x7016, 0x686e, 0x600c, 0x7826, 0x701a,
+ 0x6872, 0x6000, 0x7832, 0x701e, 0x6004, 0x7836, 0x7022, 0xa006,
+ 0x783a, 0x783e, 0x7026, 0x702a, 0x00a0, 0x6010, 0x7822, 0x7016,
+ 0x686e, 0x6014, 0x7826, 0x701a, 0x6872, 0x6000, 0x7832, 0x701e,
+ 0x6004, 0x7836, 0x7022, 0x6008, 0x783a, 0x7026, 0x600c, 0x783e,
+ 0x702a, 0x6810, 0x781a, 0x6814, 0x781e, 0x7803, 0x0011, 0x00ce,
+ 0x00de, 0x0005, 0x2011, 0x0201, 0x2009, 0x003c, 0x2204, 0xa005,
+ 0x1118, 0x8109, 0x1dd8, 0x0005, 0x0005, 0x0ca1, 0x0118, 0x780c,
+ 0xd0a4, 0x0120, 0x00d9, 0xa085, 0x0001, 0x0010, 0x080c, 0x1eef,
+ 0x0005, 0x0126, 0x2091, 0x2200, 0x7000, 0xa086, 0x0003, 0x1160,
+ 0x700c, 0x7110, 0xa106, 0x0140, 0x080c, 0x295c, 0x20e1, 0x9028,
+ 0x700f, 0xb82f, 0x7013, 0xb82f, 0x012e, 0x0005, 0x00c6, 0x080c,
+ 0x5acf, 0x11b8, 0x2001, 0x0160, 0x2003, 0x0000, 0x2001, 0x0138,
+ 0x2003, 0x0000, 0x2011, 0x00c8, 0xe000, 0xe000, 0x8211, 0x1de0,
+ 0x04b1, 0x0066, 0x2031, 0x0000, 0x080c, 0x5b51, 0x006e, 0x00ce,
+ 0x0005, 0x080c, 0x1e6e, 0x080c, 0x295c, 0x20e1, 0x9028, 0x700c,
+ 0x7110, 0xa106, 0x01c0, 0x2104, 0xa005, 0x0130, 0x2060, 0x6010,
+ 0x2060, 0x6008, 0x8001, 0x600a, 0xa188, 0x0003, 0xa182, 0xb84a,
+ 0x0210, 0x2009, 0xb82f, 0x7112, 0x700c, 0xa106, 0x1d40, 0x080c,
+ 0x28eb, 0x2110, 0x0c20, 0x2001, 0x015d, 0x2003, 0x0000, 0x2001,
+ 0x0160, 0x2502, 0x2001, 0x0138, 0x2202, 0x00ce, 0x0005, 0x080c,
+ 0x295c, 0x20e1, 0x9028, 0x2001, 0x015d, 0x2003, 0x0000, 0x00e6,
+ 0x00c6, 0x0016, 0x2071, 0xb823, 0x700c, 0x7110, 0xa106, 0x0190,
+ 0x2104, 0xa005, 0x0130, 0x2060, 0x6010, 0x2060, 0x6008, 0x8001,
+ 0x600a, 0xa188, 0x0003, 0xa182, 0xb84a, 0x0210, 0x2009, 0xb82f,
+ 0x7112, 0x0c50, 0x001e, 0x00ce, 0x00ee, 0x0005, 0x2001, 0x0138,
+ 0x2014, 0x2003, 0x0000, 0x2001, 0x0160, 0x202c, 0x2003, 0x0000,
+ 0x080c, 0x5acf, 0x1148, 0x2021, 0x0002, 0x1d04, 0x1e7d, 0x2091,
+ 0x6000, 0x8421, 0x1dd0, 0x0005, 0x2021, 0xb015, 0x2001, 0x0141,
+ 0x201c, 0xd3dc, 0x1168, 0x2001, 0x0109, 0x201c, 0xa39c, 0x0048,
+ 0x1138, 0x2001, 0x0111, 0x201c, 0x83ff, 0x1110, 0x8421, 0x1d70,
+ 0x0005, 0x00e6, 0x2071, 0x0200, 0x7808, 0xa084, 0xf000, 0xa10d,
+ 0x0869, 0x2001, 0x0105, 0x2004, 0xa084, 0x0003, 0x1130, 0x2001,
+ 0xb84a, 0x2004, 0xa086, 0x0000, 0x0548, 0xa026, 0x2019, 0xf000,
+ 0x8319, 0x1148, 0x2001, 0x012b, 0x2003, 0x95f5, 0x2001, 0x0129,
+ 0x2003, 0x95f5, 0x00d8, 0x2001, 0x0105, 0x2004, 0xa084, 0x0003,
+ 0x1130, 0x2001, 0xb84a, 0x2004, 0xa086, 0x0000, 0x0178, 0x2001,
+ 0x0132, 0x2004, 0xa436, 0x0110, 0x2020, 0x0c00, 0x2001, 0x0021,
+ 0x2004, 0xd0fc, 0x09e8, 0x080c, 0x214a, 0x08c0, 0x20e1, 0x7000,
+ 0x7324, 0x7420, 0x7028, 0x7028, 0x7426, 0x7037, 0x0001, 0x810f,
+ 0x712e, 0x702f, 0x0100, 0x7037, 0x0008, 0x7326, 0x7422, 0x2001,
+ 0x0160, 0x2502, 0x2001, 0x0138, 0x2202, 0x00ee, 0x0005, 0x0026,
+ 0x2001, 0x015d, 0x2003, 0x0000, 0x7908, 0xa18c, 0x0fff, 0xa182,
+ 0x0ffd, 0x0210, 0x2009, 0x0000, 0xa190, 0x0007, 0xa294, 0x1ff8,
+ 0x8214, 0x8214, 0x8214, 0x2001, 0x020a, 0x82ff, 0x0140, 0x20e1,
+ 0x6000, 0x200c, 0x200c, 0x200c, 0x200c, 0x8211, 0x1dd0, 0x20e1,
+ 0x7000, 0x200c, 0x200c, 0x7003, 0x0000, 0x20e1, 0x6000, 0x2001,
+ 0x0208, 0x200c, 0x2001, 0x0209, 0x2004, 0xa106, 0x0158, 0x080c,
+ 0x1dd2, 0x0130, 0x7908, 0xd1ec, 0x1128, 0x790c, 0xd1a4, 0x0960,
+ 0x080c, 0x1dfe, 0xa006, 0x002e, 0x0005, 0x00f6, 0x00e6, 0x0016,
+ 0x0026, 0x2071, 0xb823, 0x2079, 0x0030, 0x2011, 0x0050, 0x7000,
+ 0xa086, 0x0000, 0x01a8, 0x8211, 0x0188, 0x2001, 0x0005, 0x2004,
+ 0xd08c, 0x0dc8, 0x7904, 0xa18c, 0x0780, 0x0016, 0x080c, 0x1b06,
+ 0x001e, 0x81ff, 0x1118, 0x2011, 0x0050, 0x0c48, 0xa085, 0x0001,
+ 0x002e, 0x001e, 0x00ee, 0x00fe, 0x0005, 0x7803, 0x0004, 0x2009,
+ 0x0064, 0x7804, 0xd0ac, 0x0904, 0x1fa1, 0x8109, 0x1dd0, 0x2009,
+ 0x0100, 0x210c, 0xa18a, 0x0003, 0x0a0c, 0x1515, 0x080c, 0x2251,
+ 0x00e6, 0x00f6, 0x2071, 0xb812, 0x2079, 0x0010, 0x7004, 0xa086,
+ 0x0000, 0x0538, 0x7800, 0x0006, 0x7820, 0x0006, 0x7830, 0x0006,
+ 0x7834, 0x0006, 0x7838, 0x0006, 0x783c, 0x0006, 0x7803, 0x0004,
+ 0xe000, 0xe000, 0x2079, 0x0030, 0x7804, 0xd0ac, 0x190c, 0x1515,
+ 0x2079, 0x0010, 0x000e, 0x783e, 0x000e, 0x783a, 0x000e, 0x7836,
+ 0x000e, 0x7832, 0x000e, 0x7822, 0x000e, 0x7802, 0x00fe, 0x00ee,
+ 0x0030, 0x00fe, 0x00ee, 0x7804, 0xd0ac, 0x190c, 0x1515, 0x080c,
+ 0x7230, 0x0005, 0x00e6, 0x2071, 0xb84a, 0x7003, 0x0000, 0x00ee,
+ 0x0005, 0x00d6, 0xa280, 0x0004, 0x206c, 0x694c, 0xd1dc, 0x1904,
+ 0x201f, 0x6934, 0xa184, 0x0007, 0x0002, 0x1fbd, 0x200a, 0x1fbd,
+ 0x1fbd, 0x1fbd, 0x1ff1, 0x1fd0, 0x1fbf, 0x080c, 0x1515, 0x684c,
+ 0xd0b4, 0x0904, 0x2107, 0x6860, 0x682e, 0x6816, 0x685c, 0x682a,
+ 0x6812, 0x687c, 0x680a, 0x6880, 0x680e, 0x6958, 0x0804, 0x2012,
+ 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e, 0x1d38, 0x684c, 0xd0b4,
+ 0x0904, 0x2107, 0x6860, 0x682e, 0x6816, 0x685c, 0x682a, 0x6812,
+ 0x687c, 0x680a, 0x6880, 0x680e, 0x6804, 0x681a, 0xa080, 0x000d,
+ 0x2004, 0xa084, 0x000f, 0xa080, 0x22e5, 0x2005, 0x6832, 0x6958,
+ 0x0450, 0xa18c, 0x00ff, 0xa186, 0x0015, 0x1548, 0x684c, 0xd0b4,
+ 0x0904, 0x2107, 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084,
+ 0x000f, 0xa080, 0x22e5, 0x2005, 0x6832, 0x6958, 0xa006, 0x682e,
+ 0x682a, 0x0088, 0x684c, 0xd0b4, 0x0904, 0x1ae1, 0x6958, 0xa006,
+ 0x682e, 0x682a, 0x2d00, 0x681a, 0x6834, 0xa084, 0x000f, 0xa080,
+ 0x22e5, 0x2005, 0x6832, 0x6926, 0x684c, 0xc0dd, 0x684e, 0x00de,
+ 0x0005, 0x00f6, 0x2079, 0x0020, 0x7804, 0xd0fc, 0x190c, 0x214a,
+ 0x00e6, 0x00d6, 0x2071, 0xb84a, 0x7000, 0xa005, 0x1904, 0x2087,
+ 0x00c6, 0x7206, 0xa280, 0x0004, 0x205c, 0x7004, 0x2068, 0x7803,
+ 0x0004, 0x6818, 0x00d6, 0x2068, 0x686c, 0x7812, 0x6890, 0x00f6,
+ 0x20e1, 0x9040, 0x2079, 0x0200, 0x781a, 0x2079, 0x0100, 0x8004,
+ 0x78d6, 0x00fe, 0x00de, 0x2b68, 0x6824, 0x2050, 0x6818, 0x2060,
+ 0x6830, 0x2040, 0x6034, 0xa0cc, 0x000f, 0x6908, 0x791a, 0x7116,
+ 0x680c, 0x781e, 0x701a, 0xa006, 0x700e, 0x7012, 0x7004, 0x692c,
+ 0x6814, 0xa106, 0x1120, 0x6928, 0x6810, 0xa106, 0x0158, 0x0036,
+ 0x0046, 0x6b14, 0x6c10, 0x080c, 0x2305, 0x004e, 0x003e, 0x0110,
+ 0x00ce, 0x00a8, 0x8aff, 0x1120, 0x00ce, 0xa085, 0x0001, 0x0078,
+ 0x0126, 0x2091, 0x8000, 0x2079, 0x0020, 0x2009, 0x0001, 0x0059,
+ 0x0118, 0x2009, 0x0001, 0x0039, 0x012e, 0x00ce, 0xa006, 0x00de,
+ 0x00ee, 0x00fe, 0x0005, 0x0076, 0x0066, 0x0056, 0x0046, 0x0036,
+ 0x0026, 0x8aff, 0x0904, 0x2100, 0x700c, 0x7214, 0xa23a, 0x7010,
+ 0x7218, 0xa203, 0x0a04, 0x20ff, 0xa705, 0x0904, 0x20ff, 0xa03e,
+ 0x2730, 0x6850, 0xd0fc, 0x11a8, 0x00d6, 0x2805, 0xac68, 0x2900,
+ 0x0002, 0x20e2, 0x20c7, 0x20c7, 0x20e2, 0x20e2, 0x20db, 0x20e2,
+ 0x20c7, 0x20e2, 0x20cc, 0x20cc, 0x20e2, 0x20e2, 0x20e2, 0x20d3,
+ 0x20cc, 0xc0fc, 0x6852, 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0xd99c,
+ 0x0528, 0x00d6, 0x2805, 0xac68, 0x6f08, 0x6e0c, 0x00f0, 0x6b08,
+ 0x6a0c, 0x6d00, 0x6c04, 0x00c8, 0x6b10, 0x6a14, 0x6d00, 0x6c04,
+ 0x6f08, 0x6e0c, 0x0090, 0x00de, 0x00d6, 0x6834, 0xa084, 0x00ff,
+ 0xa086, 0x001e, 0x1138, 0x00de, 0x080c, 0x22a7, 0x1904, 0x2091,
+ 0xa00e, 0x00f0, 0x00de, 0x080c, 0x1515, 0x00de, 0x7b22, 0x7a26,
+ 0x7d32, 0x7c36, 0x7f3a, 0x7e3e, 0x7902, 0x7000, 0x8000, 0x7002,
+ 0x6828, 0xa300, 0x682a, 0x682c, 0xa201, 0x682e, 0x700c, 0xa300,
+ 0x700e, 0x7010, 0xa201, 0x7012, 0x080c, 0x22a7, 0x0008, 0xa006,
+ 0x002e, 0x003e, 0x004e, 0x005e, 0x006e, 0x007e, 0x0005, 0x080c,
+ 0x1515, 0x0026, 0x2001, 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040,
+ 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0x2060, 0x00d6, 0x6010,
+ 0x2068, 0x080c, 0x9c5a, 0x0118, 0x6850, 0xc0bd, 0x6852, 0x601c,
+ 0xa086, 0x0006, 0x1180, 0x2061, 0x0100, 0x62c8, 0x2001, 0x00fa,
+ 0x8001, 0x1df0, 0x60c8, 0xa206, 0x1dc0, 0x60c4, 0x686a, 0x60c8,
+ 0x6866, 0x7004, 0x2060, 0x00de, 0x00c6, 0x080c, 0x992a, 0x00ce,
+ 0x2001, 0xb7ef, 0x2004, 0xac06, 0x1150, 0x20e1, 0x9040, 0x080c,
+ 0x825d, 0x2011, 0x0000, 0x080c, 0x807f, 0x080c, 0x7230, 0x002e,
+ 0x0804, 0x2204, 0x0126, 0x2091, 0x2400, 0x0006, 0x0016, 0x00f6,
+ 0x00e6, 0x00d6, 0x00c6, 0x2079, 0x0020, 0x2071, 0xb84a, 0x2b68,
+ 0x6818, 0x2060, 0x7904, 0x7803, 0x0002, 0xa184, 0x0700, 0x1904,
+ 0x2109, 0x7000, 0x0002, 0x2204, 0x2167, 0x21d7, 0x2202, 0x8001,
+ 0x7002, 0xd19c, 0x1170, 0x8aff, 0x05d0, 0x2009, 0x0001, 0x080c,
+ 0x208b, 0x0904, 0x2204, 0x2009, 0x0001, 0x080c, 0x208b, 0x0804,
+ 0x2204, 0x7803, 0x0004, 0xd194, 0x0148, 0x6850, 0xc0fc, 0x6852,
+ 0x8aff, 0x11d8, 0x684c, 0xc0f5, 0x684e, 0x00b8, 0x0026, 0x0036,
+ 0x6b28, 0x6a2c, 0x7820, 0x686e, 0xa31a, 0x7824, 0x6872, 0xa213,
+ 0x7830, 0x681e, 0x7834, 0x6822, 0x6b2a, 0x6a2e, 0x003e, 0x002e,
+ 0x080c, 0x22bd, 0x6850, 0xc0fd, 0x6852, 0x2a00, 0x6826, 0x2c00,
+ 0x681a, 0x2800, 0x6832, 0x7003, 0x0000, 0x0804, 0x2204, 0x00f6,
+ 0x0026, 0x781c, 0x0006, 0x7818, 0x0006, 0x2079, 0x0100, 0x7a14,
+ 0xa284, 0x0184, 0xa085, 0x0012, 0x7816, 0x0036, 0x2019, 0x1000,
+ 0x8319, 0x090c, 0x1515, 0x7820, 0xd0bc, 0x1dd0, 0x003e, 0x79c8,
+ 0x000e, 0xa102, 0x001e, 0x0006, 0x0016, 0x79c4, 0x000e, 0xa103,
+ 0x78c6, 0x000e, 0x78ca, 0xa284, 0x0184, 0xa085, 0x0012, 0x7816,
+ 0x002e, 0x00fe, 0x7803, 0x0008, 0x7003, 0x0000, 0x0468, 0x8001,
+ 0x7002, 0xd194, 0x0168, 0x7804, 0xd0fc, 0x1904, 0x215a, 0xd19c,
+ 0x11f8, 0x8aff, 0x0508, 0x2009, 0x0001, 0x080c, 0x208b, 0x00e0,
+ 0x0026, 0x0036, 0x6b28, 0x6a2c, 0x080c, 0x22bd, 0x00d6, 0x2805,
+ 0xac68, 0x6034, 0xd09c, 0x1128, 0x6808, 0xa31a, 0x680c, 0xa213,
+ 0x0020, 0x6810, 0xa31a, 0x6814, 0xa213, 0x00de, 0x0804, 0x218a,
+ 0x0804, 0x2186, 0x080c, 0x1515, 0x00ce, 0x00de, 0x00ee, 0x00fe,
+ 0x001e, 0x000e, 0x012e, 0x0005, 0x00f6, 0x00e6, 0x2071, 0xb84a,
+ 0x7000, 0xa086, 0x0000, 0x05d0, 0x2079, 0x0020, 0x0016, 0x2009,
+ 0x0207, 0x210c, 0xd194, 0x0198, 0x2009, 0x020c, 0x210c, 0xa184,
+ 0x0003, 0x0168, 0x080c, 0xb450, 0x2001, 0x0133, 0x2004, 0xa005,
+ 0x090c, 0x1515, 0x20e1, 0x9040, 0x2001, 0x020c, 0x2102, 0x2009,
+ 0x0206, 0x2104, 0x2009, 0x0203, 0x210c, 0xa106, 0x1110, 0x20e1,
+ 0x9040, 0x7804, 0xd0fc, 0x09d8, 0x080c, 0x214a, 0x7000, 0xa086,
+ 0x0000, 0x19a8, 0x001e, 0x7803, 0x0004, 0x7804, 0xd0ac, 0x1de8,
+ 0x20e1, 0x9040, 0x7803, 0x0002, 0x7003, 0x0000, 0x00ee, 0x00fe,
+ 0x0005, 0x0026, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2071, 0xb84a,
+ 0x2079, 0x0020, 0x7000, 0xa086, 0x0000, 0x0540, 0x7004, 0x2060,
+ 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0158, 0x6850, 0xc0b5, 0x6852,
+ 0x680c, 0x7a1c, 0xa206, 0x1120, 0x6808, 0x7a18, 0xa206, 0x01e0,
+ 0x2001, 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0x7004, 0x2060, 0x080c, 0x992a, 0x20e1, 0x9040,
+ 0x080c, 0x825d, 0x2011, 0x0000, 0x080c, 0x807f, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x002e, 0x0005, 0x6810, 0x6a14, 0xa205, 0x1d00,
+ 0x684c, 0xc0dc, 0x684e, 0x2c10, 0x080c, 0x1fa9, 0x2001, 0x0105,
+ 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003, 0x0000,
+ 0x2069, 0xb7e0, 0x6833, 0x0000, 0x683f, 0x0000, 0x08f8, 0x8840,
+ 0x2805, 0xa005, 0x1170, 0x6004, 0xa005, 0x0168, 0x681a, 0x2060,
+ 0x6034, 0xa084, 0x000f, 0xa080, 0x22e5, 0x2045, 0x88ff, 0x090c,
+ 0x1515, 0x8a51, 0x0005, 0x2050, 0x0005, 0x8a50, 0x8841, 0x2805,
+ 0xa005, 0x1190, 0x2c00, 0xad06, 0x0120, 0x6000, 0xa005, 0x1108,
+ 0x2d00, 0x2060, 0x681a, 0x6034, 0xa084, 0x000f, 0xa080, 0x22f5,
+ 0x2045, 0x88ff, 0x090c, 0x1515, 0x0005, 0x0000, 0x0011, 0x0015,
+ 0x0019, 0x001d, 0x0021, 0x0025, 0x0029, 0x0000, 0x000f, 0x0015,
+ 0x001b, 0x0021, 0x0027, 0x0000, 0x0000, 0x0000, 0x22da, 0x22d6,
+ 0x0000, 0x0000, 0x22e4, 0x0000, 0x22da, 0x0000, 0x22e1, 0x22de,
+ 0x0000, 0x0000, 0x0000, 0x22e4, 0x22e1, 0x0000, 0x22dc, 0x22dc,
+ 0x0000, 0x0000, 0x22e4, 0x0000, 0x22dc, 0x0000, 0x22e2, 0x22e2,
+ 0x0000, 0x0000, 0x0000, 0x22e4, 0x22e2, 0x00a6, 0x0096, 0x0086,
+ 0x6b2e, 0x6c2a, 0x6858, 0xa055, 0x0904, 0x2396, 0x2d60, 0x6034,
+ 0xa0cc, 0x000f, 0xa9c0, 0x22e5, 0xa986, 0x0007, 0x0130, 0xa986,
+ 0x000e, 0x0118, 0xa986, 0x000f, 0x1120, 0x605c, 0xa422, 0x6060,
+ 0xa31b, 0x2805, 0xa045, 0x1140, 0x0310, 0x0804, 0x2396, 0x6004,
+ 0xa065, 0x0904, 0x2396, 0x0c18, 0x2805, 0xa005, 0x01a8, 0xac68,
+ 0xd99c, 0x1128, 0x6808, 0xa422, 0x680c, 0xa31b, 0x0020, 0x6810,
+ 0xa422, 0x6814, 0xa31b, 0x0620, 0x2300, 0xa405, 0x0150, 0x8a51,
+ 0x0904, 0x2396, 0x8840, 0x0c40, 0x6004, 0xa065, 0x0904, 0x2396,
+ 0x0830, 0x8a51, 0x0904, 0x2396, 0x8840, 0x2805, 0xa005, 0x1158,
+ 0x6004, 0xa065, 0x0904, 0x2396, 0x6034, 0xa0cc, 0x000f, 0xa9c0,
+ 0x22e5, 0x2805, 0x2040, 0x2b68, 0x6850, 0xc0fc, 0x6852, 0x0458,
+ 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x00d6, 0x2b68, 0x6c6e,
+ 0x6b72, 0x00de, 0xd99c, 0x1168, 0x6908, 0x2400, 0xa122, 0x690c,
+ 0x2300, 0xa11b, 0x0a0c, 0x1515, 0x6800, 0xa420, 0x6804, 0xa319,
+ 0x0060, 0x6910, 0x2400, 0xa122, 0x6914, 0x2300, 0xa11b, 0x0a0c,
+ 0x1515, 0x6800, 0xa420, 0x6804, 0xa319, 0x2b68, 0x6c1e, 0x6b22,
+ 0x6850, 0xc0fd, 0x6852, 0x2c00, 0x681a, 0x2800, 0x6832, 0x2a00,
+ 0x6826, 0x000e, 0x000e, 0x000e, 0xa006, 0x0028, 0x008e, 0x009e,
+ 0x00ae, 0xa085, 0x0001, 0x0005, 0x2001, 0x0005, 0x2004, 0xa084,
+ 0x0007, 0x0002, 0x23aa, 0x23ab, 0x23ae, 0x23b1, 0x23b6, 0x23b9,
+ 0x23be, 0x23c3, 0x0005, 0x080c, 0x214a, 0x0005, 0x080c, 0x1b06,
+ 0x0005, 0x080c, 0x1b06, 0x080c, 0x214a, 0x0005, 0x080c, 0x171b,
+ 0x0005, 0x080c, 0x214a, 0x080c, 0x171b, 0x0005, 0x080c, 0x1b06,
+ 0x080c, 0x171b, 0x0005, 0x080c, 0x1b06, 0x080c, 0x214a, 0x080c,
+ 0x171b, 0x0005, 0x0126, 0x2091, 0x2600, 0x2079, 0x0200, 0x2071,
+ 0xbb80, 0x2069, 0xb500, 0x080c, 0x24c0, 0x080c, 0x24b0, 0x2009,
+ 0x0004, 0x7912, 0x7817, 0x0004, 0x080c, 0x27f8, 0x781b, 0x0002,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x20a9, 0x0080, 0x782f, 0x0000,
+ 0x1f04, 0x23e6, 0x20e1, 0x9080, 0x783b, 0x001f, 0x20e1, 0x8700,
+ 0x012e, 0x0005, 0x0126, 0x2091, 0x2600, 0x781c, 0xd0a4, 0x190c,
+ 0x24ad, 0xa084, 0x0007, 0x0002, 0x2416, 0x2404, 0x2407, 0x240a,
+ 0x240f, 0x2411, 0x2413, 0x2415, 0x080c, 0x63c4, 0x0078, 0x080c,
+ 0x6403, 0x0060, 0x080c, 0x63c4, 0x080c, 0x6403, 0x0038, 0x0041,
+ 0x0028, 0x0031, 0x0018, 0x0021, 0x0008, 0x0011, 0x012e, 0x0005,
+ 0x0006, 0x0016, 0x0026, 0x080c, 0xb450, 0x7930, 0xa184, 0x0003,
+ 0x01b0, 0x2001, 0xb7ef, 0x2004, 0xa005, 0x0170, 0x2001, 0x0133,
+ 0x2004, 0xa005, 0x090c, 0x1515, 0x00c6, 0x2001, 0xb7ef, 0x2064,
+ 0x080c, 0x992a, 0x00ce, 0x04b8, 0x20e1, 0x9040, 0x04a0, 0xa184,
+ 0x0030, 0x01e0, 0x6a00, 0xa286, 0x0003, 0x1108, 0x00a0, 0x080c,
+ 0x5acf, 0x1178, 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500,
+ 0x2003, 0x0001, 0xa085, 0x0001, 0x080c, 0x5b13, 0x080c, 0x5a07,
+ 0x0010, 0x080c, 0x4b1f, 0x080c, 0x24b0, 0x00a8, 0xa184, 0x00c0,
+ 0x0168, 0x00e6, 0x0036, 0x0046, 0x0056, 0x2071, 0xb823, 0x080c,
+ 0x1dfe, 0x005e, 0x004e, 0x003e, 0x00ee, 0x0028, 0xa184, 0x0300,
+ 0x0110, 0x20e1, 0x9020, 0x7932, 0x002e, 0x001e, 0x000e, 0x0005,
+ 0x0016, 0x00e6, 0x00f6, 0x2071, 0xb500, 0x7128, 0x2001, 0xb791,
+ 0x2102, 0x2001, 0xb799, 0x2102, 0xa182, 0x0211, 0x1218, 0x2009,
+ 0x0008, 0x0400, 0xa182, 0x0259, 0x1218, 0x2009, 0x0007, 0x00d0,
+ 0xa182, 0x02c1, 0x1218, 0x2009, 0x0006, 0x00a0, 0xa182, 0x0349,
+ 0x1218, 0x2009, 0x0005, 0x0070, 0xa182, 0x0421, 0x1218, 0x2009,
+ 0x0004, 0x0040, 0xa182, 0x0581, 0x1218, 0x2009, 0x0003, 0x0010,
+ 0x2009, 0x0002, 0x2079, 0x0200, 0x7912, 0x7817, 0x0004, 0x080c,
+ 0x27f8, 0x00fe, 0x00ee, 0x001e, 0x0005, 0x7938, 0x080c, 0x1515,
+ 0x00e6, 0x0026, 0x2071, 0x0200, 0x20e1, 0x1000, 0x7220, 0x7028,
+ 0x7020, 0xa206, 0x0de0, 0x20e1, 0x9010, 0x002e, 0x00ee, 0x0005,
+ 0x20e1, 0xa000, 0x7837, 0x0001, 0x782f, 0x0000, 0x782f, 0x0000,
+ 0x782f, 0x0000, 0x782f, 0x0000, 0x7837, 0x0005, 0x20a9, 0x0210,
+ 0x7830, 0xd0bc, 0x1110, 0x1f04, 0x24d0, 0x7837, 0x0001, 0x7837,
+ 0x0000, 0xe000, 0xe000, 0x20e1, 0xa000, 0x0005, 0x0126, 0x2091,
+ 0x2800, 0x2061, 0x0100, 0x2071, 0xb500, 0x6024, 0x6026, 0x6053,
+ 0x0030, 0x080c, 0x2837, 0x6050, 0xa084, 0xfe7f, 0x6052, 0x2009,
+ 0x00ef, 0x6132, 0x6136, 0x080c, 0x2847, 0x60e7, 0x0000, 0x61ea,
+ 0x60e3, 0x0008, 0x604b, 0xf7f7, 0x6043, 0x0000, 0x602f, 0x0080,
+ 0x602f, 0x0000, 0x6007, 0x0e9f, 0x601b, 0x001e, 0x600f, 0x00ff,
+ 0x2001, 0xb78d, 0x2003, 0x00ff, 0x602b, 0x002f, 0x012e, 0x0005,
+ 0x2001, 0xb532, 0x2003, 0x0000, 0x2001, 0xb531, 0x2003, 0x0001,
+ 0x0005, 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x6124,
+ 0xa184, 0x1e2c, 0x1118, 0xa184, 0x0007, 0x002a, 0xa195, 0x0004,
+ 0xa284, 0x0007, 0x0002, 0x254d, 0x2533, 0x2536, 0x2539, 0x253e,
+ 0x2540, 0x2544, 0x2548, 0x080c, 0x6b74, 0x00b8, 0x080c, 0x6c4f,
+ 0x00a0, 0x080c, 0x6c4f, 0x080c, 0x6b74, 0x0078, 0x0099, 0x0068,
+ 0x080c, 0x6b74, 0x0079, 0x0048, 0x080c, 0x6c4f, 0x0059, 0x0028,
+ 0x080c, 0x6c4f, 0x080c, 0x6b74, 0x0029, 0x002e, 0x001e, 0x000e,
+ 0x012e, 0x0005, 0x6124, 0x6028, 0xd09c, 0x0118, 0xd19c, 0x1904,
+ 0x2766, 0x080c, 0x5acf, 0x0578, 0x7000, 0xa086, 0x0003, 0x0198,
+ 0x6024, 0xa084, 0x1800, 0x0178, 0x080c, 0x5af5, 0x0118, 0x080c,
+ 0x5ae1, 0x1148, 0x6027, 0x0020, 0x6043, 0x0000, 0x2001, 0xb79e,
+ 0x2003, 0xaaaa, 0x0458, 0x080c, 0x5af5, 0x15d0, 0x6024, 0xa084,
+ 0x1800, 0x1108, 0x04a8, 0x2001, 0xb79e, 0x2003, 0xaaaa, 0x2001,
+ 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001, 0x080c,
+ 0x5a07, 0x0804, 0x2766, 0xd1ac, 0x1518, 0x6024, 0xd0dc, 0x1170,
+ 0xd0e4, 0x1188, 0xd0d4, 0x11a0, 0xd0cc, 0x0130, 0x708c, 0xa086,
+ 0x0028, 0x1110, 0x080c, 0x5c5e, 0x0804, 0x2766, 0x2001, 0xb79f,
+ 0x2003, 0x0000, 0x0048, 0x2001, 0xb79f, 0x2003, 0x0002, 0x0020,
+ 0x080c, 0x5bd1, 0x0804, 0x2766, 0x080c, 0x5d03, 0x0804, 0x2766,
+ 0xd1ac, 0x0904, 0x26ae, 0x080c, 0x5acf, 0x11d8, 0x6027, 0x0020,
+ 0x0006, 0x0026, 0x0036, 0x080c, 0x5aeb, 0x1170, 0x2001, 0xb79f,
+ 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001, 0x080c, 0x5a07,
+ 0x003e, 0x002e, 0x000e, 0x0005, 0x003e, 0x002e, 0x000e, 0x080c,
+ 0x5aa6, 0x0016, 0x0046, 0x00c6, 0x644c, 0xa486, 0xf0f0, 0x1138,
+ 0x2061, 0x0100, 0x644a, 0x6043, 0x0090, 0x6043, 0x0010, 0x74ce,
+ 0xa48c, 0xff00, 0x7034, 0xd084, 0x0178, 0xa186, 0xf800, 0x1160,
+ 0x703c, 0xd084, 0x1148, 0xc085, 0x703e, 0x0036, 0x2418, 0x2011,
+ 0x8016, 0x080c, 0x3ecc, 0x003e, 0xa196, 0xff00, 0x05b8, 0x7054,
+ 0xa084, 0x00ff, 0x810f, 0xa116, 0x0588, 0x7130, 0xd184, 0x1570,
+ 0x2011, 0xb553, 0x2214, 0xd2ec, 0x0138, 0xc18d, 0x7132, 0x2011,
+ 0xb553, 0x2214, 0xd2ac, 0x1510, 0x6240, 0xa294, 0x0010, 0x0130,
+ 0x6248, 0xa294, 0xff00, 0xa296, 0xff00, 0x01c0, 0x7030, 0xd08c,
+ 0x0904, 0x267b, 0x7034, 0xd08c, 0x1140, 0x2001, 0xb50c, 0x200c,
+ 0xd1ac, 0x1904, 0x267b, 0xc1ad, 0x2102, 0x0036, 0x73cc, 0x2011,
+ 0x8013, 0x080c, 0x3ecc, 0x003e, 0x0804, 0x267b, 0x7034, 0xd08c,
+ 0x1140, 0x2001, 0xb50c, 0x200c, 0xd1ac, 0x1904, 0x267b, 0xc1ad,
+ 0x2102, 0x0036, 0x73cc, 0x2011, 0x8013, 0x080c, 0x3ecc, 0x003e,
+ 0x7130, 0xc185, 0x7132, 0x2011, 0xb553, 0x220c, 0xd1a4, 0x01d0,
+ 0x0016, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c, 0x6b1a, 0x2019,
+ 0x000e, 0x080c, 0xb065, 0xa484, 0x00ff, 0xa080, 0x2dc4, 0x200d,
+ 0xa18c, 0xff00, 0x810f, 0x8127, 0xa006, 0x2009, 0x000e, 0x080c,
+ 0xb0e8, 0x001e, 0xd1ac, 0x1148, 0x0016, 0x2009, 0x0000, 0x2019,
+ 0x0004, 0x080c, 0x2c6f, 0x001e, 0x0070, 0x0156, 0x20a9, 0x007f,
+ 0x2009, 0x0000, 0x080c, 0x4fa9, 0x1110, 0x080c, 0x4c0b, 0x8108,
+ 0x1f04, 0x2672, 0x015e, 0x00ce, 0x004e, 0x2011, 0x0003, 0x080c,
+ 0x8075, 0x2011, 0x0002, 0x080c, 0x807f, 0x080c, 0x7f59, 0x0036,
+ 0x2019, 0x0000, 0x080c, 0x7fe4, 0x003e, 0x60e3, 0x0000, 0x001e,
+ 0x2001, 0xb500, 0x2014, 0xa296, 0x0004, 0x1128, 0xd19c, 0x11b0,
+ 0x6228, 0xc29d, 0x622a, 0x2003, 0x0001, 0x2001, 0xb523, 0x2003,
+ 0x0000, 0x6027, 0x0020, 0x080c, 0x5af5, 0x1140, 0x0016, 0x2009,
+ 0x07d0, 0x2011, 0x59e4, 0x080c, 0x6a22, 0x001e, 0xd194, 0x0904,
+ 0x2766, 0x0016, 0x6220, 0xd2b4, 0x0904, 0x2717, 0x080c, 0x6a10,
+ 0x080c, 0x7d7a, 0x6027, 0x0004, 0x00f6, 0x2019, 0xb7e9, 0x2304,
+ 0xa07d, 0x0570, 0x7804, 0xa086, 0x0032, 0x1550, 0x00d6, 0x00c6,
+ 0x00e6, 0x2069, 0x0140, 0x618c, 0x6288, 0x7818, 0x608e, 0x7808,
+ 0x608a, 0x6043, 0x0002, 0x2001, 0x0003, 0x8001, 0x1df0, 0x6043,
+ 0x0000, 0x6803, 0x1000, 0x6803, 0x0000, 0x618e, 0x628a, 0x080c,
+ 0x7090, 0x080c, 0x7173, 0x7810, 0x2070, 0x7037, 0x0103, 0x2f60,
+ 0x080c, 0x861d, 0x00ee, 0x00ce, 0x00de, 0x00fe, 0x001e, 0x0005,
+ 0x00fe, 0x00d6, 0x2069, 0x0140, 0x6804, 0xa084, 0x4000, 0x0120,
+ 0x6803, 0x1000, 0x6803, 0x0000, 0x00de, 0x00c6, 0x2061, 0xb7e0,
+ 0x6028, 0xa09a, 0x00c8, 0x1238, 0x8000, 0x602a, 0x00ce, 0x080c,
+ 0x7d6d, 0x0804, 0x2765, 0x2019, 0xb7e9, 0x2304, 0xa065, 0x0120,
+ 0x2009, 0x0027, 0x080c, 0x864c, 0x00ce, 0x0804, 0x2765, 0xd2bc,
+ 0x0904, 0x2765, 0x080c, 0x6a1d, 0x6014, 0xa084, 0x0184, 0xa085,
+ 0x0010, 0x6016, 0x6027, 0x0004, 0x00d6, 0x2069, 0x0140, 0x6804,
+ 0xa084, 0x4000, 0x0120, 0x6803, 0x1000, 0x6803, 0x0000, 0x00de,
+ 0x00c6, 0x2061, 0xb7e0, 0x6044, 0xa09a, 0x00c8, 0x12f0, 0x8000,
+ 0x6046, 0x603c, 0x00ce, 0xa005, 0x0540, 0x2009, 0x07d0, 0x080c,
+ 0x6a15, 0xa080, 0x0007, 0x2004, 0xa086, 0x0006, 0x1138, 0x6114,
+ 0xa18c, 0x0184, 0xa18d, 0x0012, 0x6116, 0x00b8, 0x6114, 0xa18c,
+ 0x0184, 0xa18d, 0x0016, 0x6116, 0x0080, 0x0036, 0x2019, 0x0001,
+ 0x080c, 0x7fe4, 0x003e, 0x2019, 0xb7ef, 0x2304, 0xa065, 0x0120,
+ 0x2009, 0x004f, 0x080c, 0x864c, 0x00ce, 0x001e, 0xd19c, 0x0904,
+ 0x27bf, 0x7034, 0xd0ac, 0x1560, 0x0016, 0x0156, 0x6027, 0x0008,
+ 0x602f, 0x0020, 0x20a9, 0x0006, 0x1d04, 0x2774, 0x2091, 0x6000,
+ 0x1f04, 0x2774, 0x602f, 0x0000, 0x6150, 0xa185, 0x1400, 0x6052,
+ 0x20a9, 0x0366, 0x1d04, 0x2782, 0x2091, 0x6000, 0x6020, 0xd09c,
+ 0x1130, 0x015e, 0x6152, 0x001e, 0x6027, 0x0008, 0x0480, 0x080c,
+ 0x2907, 0x1f04, 0x2782, 0x015e, 0x6152, 0x001e, 0x6027, 0x0008,
+ 0x0016, 0x6028, 0xc09c, 0x602a, 0x2011, 0x0003, 0x080c, 0x8075,
+ 0x2011, 0x0002, 0x080c, 0x807f, 0x080c, 0x7f59, 0x0036, 0x2019,
+ 0x0000, 0x080c, 0x7fe4, 0x003e, 0x60e3, 0x0000, 0x080c, 0xb42f,
+ 0x080c, 0xb44a, 0xa085, 0x0001, 0x080c, 0x5b13, 0x2001, 0xb500,
+ 0x2003, 0x0004, 0x6027, 0x0008, 0x080c, 0x12dd, 0x001e, 0xa18c,
+ 0xffd0, 0x6126, 0x0005, 0x0006, 0x0016, 0x0026, 0x00e6, 0x00f6,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0xb500, 0x71c4, 0x70c6, 0xa116,
+ 0x0500, 0x81ff, 0x0128, 0x2011, 0x8011, 0x080c, 0x3ecc, 0x00c8,
+ 0x2011, 0x8012, 0x080c, 0x3ecc, 0x2001, 0xb572, 0x2004, 0xd0fc,
+ 0x1180, 0x0036, 0x00c6, 0x080c, 0x2892, 0x080c, 0x7f35, 0x2061,
+ 0x0100, 0x2019, 0x0028, 0x2009, 0x0000, 0x080c, 0x2c6f, 0x00ce,
+ 0x003e, 0x012e, 0x00fe, 0x00ee, 0x002e, 0x001e, 0x000e, 0x0005,
+ 0x00c6, 0x00f6, 0x0006, 0x0026, 0x2061, 0x0100, 0xa190, 0x280b,
+ 0x2205, 0x60f2, 0x2011, 0x2818, 0x2205, 0x60ee, 0x002e, 0x000e,
+ 0x00fe, 0x00ce, 0x0005, 0x0840, 0x0840, 0x0840, 0x0580, 0x0420,
+ 0x0348, 0x02c0, 0x0258, 0x0210, 0x01a8, 0x01a8, 0x01a8, 0x01a8,
+ 0x0140, 0x00f8, 0x00d0, 0x00b0, 0x00a0, 0x2028, 0xa18c, 0x00ff,
+ 0x2130, 0xa094, 0xff00, 0x1110, 0x81ff, 0x0118, 0x080c, 0x66b1,
+ 0x0038, 0xa080, 0x2dc4, 0x200d, 0xa18c, 0xff00, 0x810f, 0xa006,
+ 0x0005, 0xa080, 0x2dc4, 0x200d, 0xa18c, 0x00ff, 0x0005, 0x00d6,
+ 0x2069, 0x0140, 0x2001, 0xb515, 0x2003, 0x00ef, 0x20a9, 0x0010,
+ 0xa006, 0x6852, 0x6856, 0x1f04, 0x2842, 0x00de, 0x0005, 0x0006,
+ 0x00d6, 0x0026, 0x2069, 0x0140, 0x2001, 0xb515, 0x2102, 0x8114,
+ 0x8214, 0x8214, 0x8214, 0x20a9, 0x0010, 0x6853, 0x0000, 0xa006,
+ 0x82ff, 0x1128, 0xa184, 0x000f, 0xa080, 0xb45e, 0x2005, 0x6856,
+ 0x8211, 0x1f04, 0x2857, 0x002e, 0x00de, 0x000e, 0x0005, 0x00c6,
+ 0x2061, 0xb500, 0x6030, 0x0110, 0xc09d, 0x0008, 0xc09c, 0x6032,
+ 0x00ce, 0x0005, 0x0156, 0x00d6, 0x0026, 0x0016, 0x0006, 0x2069,
+ 0x0140, 0x6980, 0xa116, 0x0180, 0xa112, 0x1230, 0x8212, 0x8210,
+ 0x22a8, 0x2001, 0x0402, 0x0018, 0x22a8, 0x2001, 0x0404, 0x680e,
+ 0x1f04, 0x2887, 0x680f, 0x0000, 0x000e, 0x001e, 0x002e, 0x00de,
+ 0x015e, 0x0005, 0x2001, 0xb553, 0x2004, 0xd0c4, 0x0150, 0xd0a4,
+ 0x0140, 0xa006, 0x0046, 0x2020, 0x2009, 0x002e, 0x080c, 0xb0e8,
+ 0x004e, 0x0005, 0x00f6, 0x0016, 0x0026, 0x2079, 0x0140, 0x78c4,
+ 0xd0dc, 0x0548, 0xa084, 0x0700, 0xa08e, 0x0300, 0x1520, 0x2011,
+ 0x0000, 0x2009, 0x0002, 0x2300, 0xa080, 0x0020, 0x2018, 0x2300,
+ 0x080c, 0x6b40, 0x2011, 0x0030, 0x2200, 0x8007, 0xa085, 0x004c,
+ 0x78c2, 0x2009, 0x0204, 0x210c, 0x2200, 0xa100, 0x2009, 0x0138,
+ 0x200a, 0x080c, 0x5acf, 0x1118, 0x2009, 0xb78f, 0x200a, 0x002e,
+ 0x001e, 0x00fe, 0x0005, 0x78c3, 0x0000, 0x0cc8, 0x0126, 0x2091,
+ 0x2800, 0x0006, 0x0016, 0x0026, 0x2001, 0x0170, 0x200c, 0x8000,
+ 0x2014, 0xa184, 0x0003, 0x0110, 0x0804, 0x1b04, 0x002e, 0x001e,
+ 0x000e, 0x012e, 0x0005, 0x0006, 0x2001, 0x0100, 0x2004, 0xa082,
+ 0x0005, 0x000e, 0x0268, 0x2001, 0x0170, 0x200c, 0xa18c, 0x00ff,
+ 0xa18e, 0x004c, 0x1128, 0x200c, 0xa18c, 0xff00, 0x810f, 0x0010,
+ 0x2009, 0x0000, 0x2001, 0x0204, 0x2004, 0xa108, 0x0005, 0x0006,
+ 0x0156, 0x00f6, 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854, 0xd08c,
+ 0x1110, 0x1f04, 0x290e, 0x00fe, 0x015e, 0x000e, 0x0005, 0x0016,
+ 0x00c6, 0x0006, 0x2061, 0x0100, 0x6030, 0x0006, 0x6048, 0x0006,
+ 0x60e4, 0x0006, 0x60e8, 0x0006, 0x6050, 0x0006, 0x60f0, 0x0006,
+ 0x60ec, 0x0006, 0x600c, 0x0006, 0x6004, 0x0006, 0x6028, 0x0006,
+ 0x60e0, 0x0006, 0x602f, 0x0100, 0x602f, 0x0000, 0xe000, 0xe000,
+ 0xe000, 0xe000, 0x602f, 0x0040, 0x602f, 0x0000, 0x000e, 0x60e2,
+ 0x000e, 0x602a, 0x000e, 0x6006, 0x000e, 0x600e, 0x000e, 0x60ee,
+ 0x000e, 0x60f2, 0x000e, 0x6052, 0x000e, 0x60ea, 0x000e, 0x60e6,
+ 0x000e, 0x604a, 0x000e, 0x6032, 0x6036, 0x2008, 0x080c, 0x2847,
+ 0x000e, 0x00ce, 0x001e, 0x0005, 0x2009, 0x0171, 0x2104, 0xd0dc,
+ 0x0140, 0x2009, 0x0170, 0x2104, 0x200b, 0x0080, 0xe000, 0xe000,
+ 0x200a, 0x0005, 0x29fa, 0x29fe, 0x2a02, 0x2a08, 0x2a0e, 0x2a14,
+ 0x2a1a, 0x2a22, 0x2a2a, 0x2a30, 0x2a36, 0x2a3e, 0x2a46, 0x2a4e,
+ 0x2a56, 0x2a60, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a,
+ 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a,
+ 0x2a6a, 0x2a6a, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2a6c, 0x2a6c, 0x2a72, 0x2a72, 0x2a79, 0x2a79,
+ 0x2a80, 0x2a80, 0x2a89, 0x2a89, 0x2a90, 0x2a90, 0x2a99, 0x2a99,
+ 0x2aa2, 0x2aa2, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a,
+ 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a,
+ 0x2a6a, 0x2a6a, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad, 0x2aad,
+ 0x2aad, 0x2aad, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a,
+ 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a, 0x2a6a,
+ 0x2a6a, 0x2a6a, 0x0106, 0x0006, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x0804, 0x2ab5, 0x0106, 0x0006, 0x080c, 0x2519, 0x0804, 0x2ab5,
+ 0x0106, 0x0006, 0x080c, 0x2519, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x080c, 0x239c, 0x0804, 0x2ab5, 0x0106, 0x0006, 0x080c, 0x239c,
+ 0x0804, 0x2ab5, 0x0106, 0x0006, 0x080c, 0x2519, 0x080c, 0x239c,
+ 0x0804, 0x2ab5, 0x0106, 0x0006, 0x080c, 0x2519, 0x080c, 0x239c,
+ 0x0804, 0x2ab5, 0x0106, 0x0006, 0x080c, 0x23f2, 0x0804, 0x2ab5,
+ 0x0106, 0x0006, 0x080c, 0x23f2, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x080c, 0x2519, 0x080c, 0x23f2, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x080c, 0x2519, 0x080c, 0x23f2, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x080c, 0x239c, 0x080c, 0x23f2, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x080c, 0x239c, 0x080c, 0x23f2, 0x0804, 0x2ab5, 0x0106, 0x0006,
+ 0x080c, 0x2519, 0x080c, 0x239c, 0x080c, 0x23f2, 0x0804, 0x2ab5,
+ 0x0106, 0x0006, 0x080c, 0x2519, 0x080c, 0x239c, 0x080c, 0x23f2,
+ 0x0804, 0x2ab5, 0xe000, 0x0cf0, 0x0106, 0x0006, 0x080c, 0x28d6,
+ 0x0804, 0x2ab5, 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x2519,
+ 0x04e0, 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x239c, 0x04a8,
+ 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x2519, 0x080c, 0x239c,
+ 0x0460, 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x23f2, 0x0428,
+ 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x2519, 0x080c, 0x23f2,
+ 0x00e0, 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x239c, 0x080c,
+ 0x23f2, 0x0098, 0x0106, 0x0006, 0x080c, 0x28d6, 0x080c, 0x2519,
+ 0x080c, 0x239c, 0x080c, 0x23f2, 0x0040, 0x20d1, 0x0000, 0x20d1,
+ 0x0001, 0x20d1, 0x0000, 0x080c, 0x1515, 0x000e, 0x010e, 0x000d,
+ 0x00c6, 0x0026, 0x0046, 0x2021, 0x0000, 0x080c, 0x5309, 0x1904,
+ 0x2b95, 0x72d4, 0x2001, 0xb79e, 0x2004, 0xa005, 0x1110, 0xd29c,
+ 0x0148, 0xd284, 0x1138, 0xd2bc, 0x1904, 0x2b95, 0x080c, 0x2b99,
+ 0x0804, 0x2b95, 0xd2cc, 0x1904, 0x2b95, 0x080c, 0x5acf, 0x1120,
+ 0x709f, 0xffff, 0x0804, 0x2b95, 0xd294, 0x0120, 0x709f, 0xffff,
+ 0x0804, 0x2b95, 0x2001, 0xb515, 0x203c, 0x7288, 0xd284, 0x0904,
+ 0x2b37, 0xd28c, 0x1904, 0x2b37, 0x0036, 0x739c, 0xa38e, 0xffff,
+ 0x1110, 0x2019, 0x0001, 0x8314, 0xa2e0, 0xbcc0, 0x2c04, 0xa38c,
+ 0x0001, 0x0120, 0xa084, 0xff00, 0x8007, 0x0010, 0xa084, 0x00ff,
+ 0xa70e, 0x0560, 0xa08e, 0x0000, 0x0548, 0xa08e, 0x00ff, 0x1150,
+ 0x7230, 0xd284, 0x1538, 0x7288, 0xc28d, 0x728a, 0x709f, 0xffff,
+ 0x003e, 0x0428, 0x2009, 0x0000, 0x080c, 0x281d, 0x080c, 0x4f4d,
+ 0x11b8, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1150, 0x7030,
+ 0xd08c, 0x0118, 0x6000, 0xd0bc, 0x0120, 0x080c, 0x2bac, 0x0140,
+ 0x0028, 0x080c, 0x2cdd, 0x080c, 0x2bda, 0x0110, 0x8318, 0x0818,
+ 0x739e, 0x0010, 0x709f, 0xffff, 0x003e, 0x0804, 0x2b95, 0xa780,
+ 0x2dc4, 0x203d, 0xa7bc, 0xff00, 0x873f, 0x2041, 0x007e, 0x709c,
+ 0xa096, 0xffff, 0x1120, 0x2009, 0x0000, 0x28a8, 0x0050, 0xa812,
+ 0x0220, 0x2008, 0xa802, 0x20a8, 0x0020, 0x709f, 0xffff, 0x0804,
+ 0x2b95, 0x2700, 0x0156, 0x0016, 0xa106, 0x05a0, 0xc484, 0x080c,
+ 0x4fa9, 0x0120, 0x080c, 0x4f4d, 0x15a8, 0x0008, 0xc485, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x1130, 0x7030, 0xd08c, 0x01e8,
+ 0x6000, 0xd0bc, 0x11d0, 0x7288, 0xd28c, 0x0188, 0x6004, 0xa084,
+ 0x00ff, 0xa082, 0x0006, 0x02b0, 0xd484, 0x1118, 0x080c, 0x4f6c,
+ 0x0028, 0x080c, 0x2d6a, 0x0170, 0x080c, 0x2d97, 0x0058, 0x080c,
+ 0x2cdd, 0x080c, 0x2bda, 0x0170, 0x0028, 0x080c, 0x2d6a, 0x0110,
+ 0x0419, 0x0140, 0x001e, 0x8108, 0x015e, 0x1f04, 0x2b51, 0x709f,
+ 0xffff, 0x0018, 0x001e, 0x015e, 0x719e, 0x004e, 0x002e, 0x00ce,
+ 0x0005, 0x00c6, 0x0016, 0x709f, 0x0001, 0x2009, 0x007e, 0x080c,
+ 0x4f4d, 0x1138, 0x080c, 0x2cdd, 0x04a9, 0x0118, 0x70d4, 0xc0bd,
+ 0x70d6, 0x001e, 0x00ce, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6,
+ 0x2c68, 0x2001, 0xb557, 0x2004, 0xa084, 0x00ff, 0x6842, 0x080c,
+ 0x9ed6, 0x01d8, 0x2d00, 0x601a, 0x080c, 0xa027, 0x601f, 0x0001,
+ 0x2001, 0x0000, 0x080c, 0x4eeb, 0x2001, 0x0000, 0x080c, 0x4efd,
+ 0x0126, 0x2091, 0x8000, 0x7098, 0x8000, 0x709a, 0x012e, 0x2009,
+ 0x0004, 0x080c, 0x864c, 0xa085, 0x0001, 0x00ce, 0x00de, 0x007e,
+ 0x001e, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x2c68, 0x2001,
+ 0xb557, 0x2004, 0xa084, 0x00ff, 0x6842, 0x080c, 0x9ed6, 0x0550,
+ 0x2d00, 0x601a, 0x6800, 0xc0c4, 0x6802, 0x68a0, 0xa086, 0x007e,
+ 0x0140, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1110, 0x080c,
+ 0x2c9c, 0x080c, 0xa027, 0x601f, 0x0001, 0x2001, 0x0000, 0x080c,
+ 0x4eeb, 0x2001, 0x0002, 0x080c, 0x4efd, 0x0126, 0x2091, 0x8000,
+ 0x7098, 0x8000, 0x709a, 0x012e, 0x2009, 0x0002, 0x080c, 0x864c,
+ 0xa085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005, 0x00c6,
+ 0x0026, 0x2009, 0x0080, 0x080c, 0x4f4d, 0x1120, 0x0031, 0x0110,
+ 0x70db, 0xffff, 0x002e, 0x00ce, 0x0005, 0x0016, 0x0076, 0x00d6,
+ 0x00c6, 0x2c68, 0x080c, 0x85c7, 0x01e8, 0x2d00, 0x601a, 0x080c,
+ 0xa027, 0x601f, 0x0001, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x2001,
+ 0x0002, 0x080c, 0x4efd, 0x0126, 0x2091, 0x8000, 0x080c, 0x2c9c,
+ 0x70dc, 0x8000, 0x70de, 0x012e, 0x2009, 0x0002, 0x080c, 0x864c,
+ 0xa085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005, 0x00c6,
+ 0x00d6, 0x0126, 0x2091, 0x8000, 0x2009, 0x007f, 0x080c, 0x4f4d,
+ 0x1190, 0x2c68, 0x080c, 0x85c7, 0x0170, 0x2d00, 0x601a, 0x6312,
+ 0x601f, 0x0001, 0x620a, 0x080c, 0xa027, 0x2009, 0x0022, 0x080c,
+ 0x864c, 0xa085, 0x0001, 0x012e, 0x00de, 0x00ce, 0x0005, 0x00e6,
+ 0x00c6, 0x0066, 0x0036, 0x0026, 0x080c, 0x6e01, 0x080c, 0x6da4,
+ 0x080c, 0x906f, 0x2130, 0x81ff, 0x0128, 0x20a9, 0x007e, 0x2009,
+ 0x0000, 0x0020, 0x20a9, 0x007f, 0x2009, 0x0000, 0x0016, 0x080c,
+ 0x4fa9, 0x1120, 0x080c, 0x51aa, 0x080c, 0x4c0b, 0x001e, 0x8108,
+ 0x1f04, 0x2c86, 0x86ff, 0x1110, 0x080c, 0x11f0, 0x002e, 0x003e,
+ 0x006e, 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x00c6, 0x0036, 0x0026,
+ 0x0016, 0x6218, 0x2270, 0x72a0, 0x0026, 0x2019, 0x0029, 0x080c,
+ 0x6df5, 0x0076, 0x2039, 0x0000, 0x080c, 0x6d02, 0x2c08, 0x080c,
+ 0xae82, 0x007e, 0x001e, 0x2e60, 0x080c, 0x51aa, 0x6210, 0x6314,
+ 0x080c, 0x4c0b, 0x6212, 0x6316, 0x001e, 0x002e, 0x003e, 0x00ce,
+ 0x00ee, 0x0005, 0x00e6, 0x0006, 0x6018, 0xa080, 0x0028, 0x2004,
+ 0xa086, 0x0080, 0x0150, 0x2071, 0xb500, 0x7098, 0xa005, 0x0110,
+ 0x8001, 0x709a, 0x000e, 0x00ee, 0x0005, 0x2071, 0xb500, 0x70dc,
+ 0xa005, 0x0dc0, 0x8001, 0x70de, 0x0ca8, 0x6000, 0xc08c, 0x6002,
+ 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0036, 0x0026, 0x0016, 0x0156,
+ 0x2178, 0x81ff, 0x1118, 0x20a9, 0x0001, 0x0098, 0x2001, 0xb553,
+ 0x2004, 0xd0c4, 0x0150, 0xd0a4, 0x0140, 0xa006, 0x0046, 0x2020,
+ 0x2009, 0x002d, 0x080c, 0xb0e8, 0x004e, 0x20a9, 0x00ff, 0x2011,
+ 0x0000, 0x0026, 0xa28e, 0x007e, 0x0904, 0x2d49, 0xa28e, 0x007f,
+ 0x0904, 0x2d49, 0xa28e, 0x0080, 0x05e0, 0xa288, 0xb635, 0x210c,
+ 0x81ff, 0x05b8, 0x8fff, 0x1148, 0x2001, 0xb7be, 0x0006, 0x2003,
+ 0x0001, 0x04d9, 0x000e, 0x2003, 0x0000, 0x00c6, 0x2160, 0x2001,
+ 0x0001, 0x080c, 0x5313, 0x00ce, 0x2019, 0x0029, 0x080c, 0x6df5,
+ 0x0076, 0x2039, 0x0000, 0x080c, 0x6d02, 0x00c6, 0x0026, 0x2160,
+ 0x6204, 0xa294, 0x00ff, 0xa286, 0x0006, 0x1118, 0x6007, 0x0404,
+ 0x0028, 0x2001, 0x0004, 0x8007, 0xa215, 0x6206, 0x002e, 0x00ce,
+ 0x0016, 0x2c08, 0x080c, 0xae82, 0x001e, 0x007e, 0x2160, 0x080c,
+ 0x51aa, 0x002e, 0x8210, 0x1f04, 0x2d01, 0x015e, 0x001e, 0x002e,
+ 0x003e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x0046, 0x0026, 0x0016,
+ 0x2001, 0xb553, 0x2004, 0xd0c4, 0x0148, 0xd0a4, 0x0138, 0xa006,
+ 0x2220, 0x8427, 0x2009, 0x0029, 0x080c, 0xb0e8, 0x001e, 0x002e,
+ 0x004e, 0x0005, 0x0016, 0x0026, 0x0036, 0x00c6, 0x7288, 0x82ff,
+ 0x01f8, 0x2011, 0xb553, 0x2214, 0xd2ac, 0x11d0, 0x2100, 0x080c,
+ 0x2831, 0x81ff, 0x01b8, 0x2019, 0x0001, 0x8314, 0xa2e0, 0xbcc0,
+ 0x2c04, 0xd384, 0x0120, 0xa084, 0xff00, 0x8007, 0x0010, 0xa084,
+ 0x00ff, 0xa116, 0x0138, 0xa096, 0x00ff, 0x0110, 0x8318, 0x0c68,
+ 0xa085, 0x0001, 0x00ce, 0x003e, 0x002e, 0x001e, 0x0005, 0x0016,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x0016, 0x0026, 0x0036, 0x2110,
+ 0x0026, 0x2019, 0x0029, 0x080c, 0x8299, 0x002e, 0x080c, 0xb38d,
+ 0x003e, 0x002e, 0x001e, 0xa180, 0xb635, 0x2004, 0xa065, 0x0158,
+ 0x0016, 0x00c6, 0x2061, 0xb8f4, 0x001e, 0x611a, 0x080c, 0x2c9c,
+ 0x001e, 0x080c, 0x4f6c, 0x012e, 0x00ce, 0x001e, 0x0005, 0x2001,
+ 0xb535, 0x2004, 0xd0cc, 0x0005, 0x7eef, 0x7de8, 0x7ce4, 0x80e2,
+ 0x7be1, 0x80e0, 0x80dc, 0x80da, 0x7ad9, 0x80d6, 0x80d5, 0x80d4,
+ 0x80d3, 0x80d2, 0x80d1, 0x79ce, 0x78cd, 0x80cc, 0x80cb, 0x80ca,
+ 0x80c9, 0x80c7, 0x80c6, 0x77c5, 0x76c3, 0x80bc, 0x80ba, 0x75b9,
+ 0x80b6, 0x74b5, 0x73b4, 0x72b3, 0x80b2, 0x80b1, 0x80ae, 0x71ad,
+ 0x80ac, 0x70ab, 0x6faa, 0x6ea9, 0x80a7, 0x6da6, 0x6ca5, 0x6ba3,
+ 0x6a9f, 0x699e, 0x689d, 0x809b, 0x8098, 0x6797, 0x6690, 0x658f,
+ 0x6488, 0x6384, 0x6282, 0x8081, 0x8080, 0x617c, 0x607a, 0x8079,
+ 0x5f76, 0x8075, 0x8074, 0x8073, 0x8072, 0x8071, 0x806e, 0x5e6d,
+ 0x806c, 0x5d6b, 0x5c6a, 0x5b69, 0x8067, 0x5a66, 0x5965, 0x5863,
+ 0x575c, 0x565a, 0x5559, 0x8056, 0x8055, 0x5454, 0x5353, 0x5252,
+ 0x5151, 0x504e, 0x4f4d, 0x804c, 0x804b, 0x4e4a, 0x4d49, 0x8047,
+ 0x4c46, 0x8045, 0x8043, 0x803c, 0x803a, 0x8039, 0x8036, 0x4b35,
+ 0x8034, 0x4a33, 0x4932, 0x4831, 0x802e, 0x472d, 0x462c, 0x452b,
+ 0x442a, 0x4329, 0x4227, 0x8026, 0x8025, 0x4123, 0x401f, 0x3f1e,
+ 0x3e1d, 0x3d1b, 0x3c18, 0x8017, 0x8010, 0x3b0f, 0x3a08, 0x8004,
+ 0x3902, 0x8001, 0x8000, 0x8000, 0x3800, 0x3700, 0x3600, 0x8000,
+ 0x3500, 0x8000, 0x8000, 0x8000, 0x3400, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x3300, 0x3200, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x3100, 0x3000, 0x8000, 0x8000, 0x2f00,
+ 0x8000, 0x2e00, 0x2d00, 0x2c00, 0x8000, 0x8000, 0x8000, 0x2b00,
+ 0x8000, 0x2a00, 0x2900, 0x2800, 0x8000, 0x2700, 0x2600, 0x2500,
+ 0x2400, 0x2300, 0x2200, 0x8000, 0x8000, 0x2100, 0x2000, 0x1f00,
+ 0x1e00, 0x1d00, 0x1c00, 0x8000, 0x8000, 0x1b00, 0x1a00, 0x8000,
+ 0x1900, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x1800,
+ 0x8000, 0x1700, 0x1600, 0x1500, 0x8000, 0x1400, 0x1300, 0x1200,
+ 0x1100, 0x1000, 0x0f00, 0x8000, 0x8000, 0x0e00, 0x0d00, 0x0c00,
+ 0x0b00, 0x0a00, 0x0900, 0x8000, 0x8000, 0x0800, 0x0700, 0x8000,
+ 0x0600, 0x8000, 0x8000, 0x8000, 0x0500, 0x0400, 0x0300, 0x8000,
+ 0x0200, 0x8000, 0x8000, 0x8000, 0x0100, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x0000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x2071, 0xb582, 0x7003, 0x0002,
+ 0xa006, 0x7012, 0x7016, 0x703a, 0x703e, 0x7033, 0xb592, 0x7037,
+ 0xb592, 0x7007, 0x0001, 0x2061, 0xb5d2, 0x6003, 0x0002, 0x0005,
+ 0x1004, 0x2eea, 0x0e04, 0x2eea, 0x2071, 0xb582, 0x2b78, 0x7818,
+ 0xd084, 0x1140, 0x2a60, 0x7820, 0xa08e, 0x0069, 0x1904, 0x2fcf,
+ 0x0804, 0x2f68, 0x0005, 0x2071, 0xb582, 0x7004, 0x0002, 0x2ef3,
+ 0x2ef4, 0x2efd, 0x2f0e, 0x0005, 0x1004, 0x2efc, 0x0e04, 0x2efc,
+ 0x2b78, 0x7818, 0xd084, 0x01e8, 0x0005, 0x2b78, 0x2061, 0xb5d2,
+ 0x6008, 0xa08e, 0x0100, 0x0128, 0xa086, 0x0200, 0x0904, 0x2fc9,
+ 0x0005, 0x7014, 0x2068, 0x2a60, 0x7018, 0x0807, 0x7010, 0x2068,
+ 0x6834, 0xa086, 0x0103, 0x0108, 0x0005, 0x2a60, 0x2b78, 0x7018,
+ 0x0807, 0x2a60, 0x7820, 0xa08a, 0x0040, 0x1210, 0x61c4, 0x0042,
+ 0x2100, 0xa08a, 0x003f, 0x1a04, 0x2fc6, 0x61c4, 0x0804, 0x2f68,
+ 0x2faa, 0x2fd5, 0x2fdd, 0x2fe1, 0x2fe9, 0x2fef, 0x2ff3, 0x2fff,
+ 0x3002, 0x300c, 0x300f, 0x2fc6, 0x2fc6, 0x2fc6, 0x3012, 0x2fc6,
+ 0x3021, 0x3038, 0x304f, 0x30c9, 0x30ce, 0x30f7, 0x3148, 0x3159,
+ 0x3178, 0x31b0, 0x31ba, 0x31c7, 0x31da, 0x31fb, 0x3204, 0x323a,
+ 0x3240, 0x2fc6, 0x3269, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6,
+ 0x3270, 0x327a, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6,
+ 0x2fc6, 0x2fc6, 0x3282, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6,
+ 0x3294, 0x329e, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6,
+ 0x0002, 0x32c8, 0x331c, 0x3377, 0x3391, 0x2fc6, 0x33c2, 0x37f5,
+ 0x4233, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6,
+ 0x2fc6, 0x300c, 0x300f, 0x37f7, 0x2fc6, 0x3804, 0x42cc, 0x4327,
+ 0x438b, 0x2fc6, 0x43ee, 0x4418, 0x4437, 0x4469, 0x2fc6, 0x2fc6,
+ 0x2fc6, 0x3808, 0x39ad, 0x39c7, 0x39e5, 0x3a46, 0x3aa6, 0x3ab1,
+ 0x3ae9, 0x3af8, 0x3b07, 0x3b0a, 0x3b2d, 0x3b79, 0x3bef, 0x3bfc,
+ 0x3cfd, 0x3e23, 0x3e4c, 0x3f4a, 0x3f6c, 0x3f78, 0x3fb1, 0x4075,
+ 0x2fc6, 0x2fc6, 0x2fc6, 0x2fc6, 0x40dd, 0x40f8, 0x416a, 0x421c,
+ 0x713c, 0x0000, 0x2021, 0x4000, 0x080c, 0x3ea9, 0x0126, 0x2091,
+ 0x8000, 0x0e04, 0x2fb6, 0x7818, 0xd084, 0x0110, 0x012e, 0x0cb0,
+ 0x7c22, 0x7926, 0x7a2a, 0x7b2e, 0x781b, 0x0001, 0x2091, 0x4080,
+ 0x7007, 0x0001, 0x2091, 0x5000, 0x012e, 0x0005, 0x2021, 0x4001,
+ 0x0c18, 0x2021, 0x4002, 0x0c00, 0x2021, 0x4003, 0x08e8, 0x2021,
+ 0x4005, 0x08d0, 0x2021, 0x4006, 0x08b8, 0xa02e, 0x2520, 0x7b28,
+ 0x7a2c, 0x7824, 0x7930, 0x0804, 0x3eb6, 0x7823, 0x0004, 0x7824,
+ 0x0807, 0xa02e, 0x2520, 0x7b28, 0x7a2c, 0x7824, 0x7930, 0x0804,
+ 0x3eb9, 0x7924, 0x7828, 0x2114, 0x200a, 0x0804, 0x2faa, 0x7924,
+ 0x2114, 0x0804, 0x2faa, 0x2099, 0x0009, 0x20a1, 0x0009, 0x20a9,
+ 0x0007, 0x53a3, 0x7924, 0x7a28, 0x7b2c, 0x0804, 0x2faa, 0x7824,
+ 0x2060, 0x0090, 0x2009, 0x0002, 0x2011, 0x0002, 0x2019, 0x0006,
+ 0x783b, 0x0017, 0x0804, 0x2faa, 0x7d38, 0x7c3c, 0x0840, 0x7d38,
+ 0x7c3c, 0x0888, 0x2061, 0x1000, 0xe10c, 0xa006, 0x2c15, 0xa200,
+ 0x8c60, 0x8109, 0x1dd8, 0x2010, 0xa005, 0x0904, 0x2faa, 0x0804,
+ 0x2fcc, 0x2069, 0xb552, 0x7824, 0x7930, 0xa11a, 0x1a04, 0x2fd2,
+ 0x8019, 0x0904, 0x2fd2, 0x684a, 0x6942, 0x782c, 0x6852, 0x7828,
+ 0x6856, 0xa006, 0x685a, 0x685e, 0x080c, 0x5da5, 0x0804, 0x2faa,
+ 0x2069, 0xb552, 0x7824, 0x7934, 0xa11a, 0x1a04, 0x2fd2, 0x8019,
+ 0x0904, 0x2fd2, 0x684e, 0x6946, 0x782c, 0x6862, 0x7828, 0x6866,
+ 0xa006, 0x686a, 0x686e, 0x080c, 0x53d5, 0x0804, 0x2faa, 0xa02e,
+ 0x2520, 0x81ff, 0x1904, 0x2fcf, 0x7924, 0x7b28, 0x7a2c, 0x20a9,
+ 0x0005, 0x20a1, 0xb589, 0x41a1, 0x080c, 0x3e75, 0x0904, 0x2fcf,
+ 0x2009, 0x0020, 0x080c, 0x3eb6, 0x701b, 0x3067, 0x0005, 0x6834,
+ 0x2008, 0xa084, 0x00ff, 0xa096, 0x0011, 0x0138, 0xa096, 0x0019,
+ 0x0120, 0xa096, 0x0015, 0x1904, 0x2fcf, 0x810f, 0xa18c, 0x00ff,
+ 0x0904, 0x2fcf, 0x710e, 0x700c, 0x8001, 0x0528, 0x700e, 0x080c,
+ 0x3e75, 0x0904, 0x2fcf, 0x2009, 0x0020, 0x2061, 0xb5d2, 0x6224,
+ 0x6328, 0x642c, 0x6530, 0xa290, 0x0040, 0xa399, 0x0000, 0xa4a1,
+ 0x0000, 0xa5a9, 0x0000, 0x080c, 0x3eb6, 0x701b, 0x3098, 0x0005,
+ 0x6834, 0xa084, 0x00ff, 0xa096, 0x0002, 0x0120, 0xa096, 0x000a,
+ 0x1904, 0x2fcf, 0x08c0, 0x7010, 0x2068, 0x6838, 0xc0fd, 0x683a,
+ 0x080c, 0x4e49, 0x1128, 0x7007, 0x0003, 0x701b, 0x30b2, 0x0005,
+ 0x080c, 0x54db, 0x0126, 0x2091, 0x8000, 0x20a9, 0x0005, 0x2099,
+ 0xb589, 0x530a, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000,
+ 0xa5a9, 0x0000, 0xad80, 0x000d, 0x2009, 0x0020, 0x012e, 0x0804,
+ 0x3eb9, 0x61ac, 0x7824, 0x60ae, 0x0804, 0x2faa, 0x2091, 0x8000,
+ 0x7823, 0x4000, 0x7827, 0x4953, 0x782b, 0x5020, 0x782f, 0x2020,
+ 0x2009, 0x017f, 0x2104, 0x7832, 0x3f00, 0x7836, 0x2061, 0x0100,
+ 0x6200, 0x2061, 0x0200, 0x603c, 0x8007, 0xa205, 0x783a, 0x2009,
+ 0x04fd, 0x2104, 0x783e, 0x781b, 0x0001, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x2071, 0x0010, 0x20c1, 0x00f0, 0x0804, 0x0427, 0x81ff,
+ 0x1904, 0x2fcf, 0x7924, 0x810f, 0xa18c, 0x00ff, 0x080c, 0x4fa9,
+ 0x1904, 0x2fd2, 0x7e38, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0210,
+ 0x0804, 0x2fd2, 0x7c28, 0x7d2c, 0x080c, 0x5171, 0xd28c, 0x1118,
+ 0x080c, 0x511a, 0x0010, 0x080c, 0x514a, 0x1518, 0x2061, 0xbd00,
+ 0x0126, 0x2091, 0x8000, 0x6000, 0xa086, 0x0000, 0x0148, 0x6010,
+ 0xa06d, 0x0130, 0x683c, 0xa406, 0x1118, 0x6840, 0xa506, 0x0150,
+ 0x012e, 0xace0, 0x0018, 0x2001, 0xb517, 0x2004, 0xac02, 0x1a04,
+ 0x2fcf, 0x0c30, 0x080c, 0x992a, 0x012e, 0x0904, 0x2fcf, 0x0804,
+ 0x2faa, 0xa00e, 0x2001, 0x0005, 0x080c, 0x54db, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x9ed2, 0x080c, 0x5408, 0x012e, 0x0804, 0x2faa,
+ 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c,
+ 0x506f, 0x0904, 0x2fcf, 0x080c, 0x517d, 0x0904, 0x2fcf, 0x0804,
+ 0x2faa, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e9a, 0x0904, 0x2fd2,
+ 0x080c, 0x51e9, 0x0904, 0x2fcf, 0x2019, 0x0005, 0x7924, 0x080c,
+ 0x5198, 0x0904, 0x2fcf, 0x7828, 0xa08a, 0x1000, 0x1a04, 0x2fd2,
+ 0x8003, 0x800b, 0x810b, 0xa108, 0x080c, 0x69a8, 0x0804, 0x2faa,
+ 0x0126, 0x2091, 0x8000, 0x81ff, 0x0118, 0x2009, 0x0001, 0x0450,
+ 0x2029, 0x00ff, 0x6450, 0x2400, 0xa506, 0x01f8, 0x2508, 0x080c,
+ 0x4fa9, 0x11d8, 0x080c, 0x51e9, 0x1128, 0x2009, 0x0002, 0x62b4,
+ 0x2518, 0x00c0, 0x2019, 0x0004, 0xa00e, 0x080c, 0x5198, 0x1118,
+ 0x2009, 0x0006, 0x0078, 0x7824, 0xa08a, 0x1000, 0x1270, 0x8003,
+ 0x800b, 0x810b, 0xa108, 0x080c, 0x69a8, 0x8529, 0x1ae0, 0x012e,
+ 0x0804, 0x2faa, 0x012e, 0x0804, 0x2fcf, 0x012e, 0x0804, 0x2fd2,
+ 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c, 0x50d5, 0x080c, 0x5171,
+ 0x0804, 0x2faa, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e8a, 0x0904,
+ 0x2fd2, 0x080c, 0x50c6, 0x080c, 0x5171, 0x0804, 0x2faa, 0x81ff,
+ 0x1904, 0x2fcf, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c, 0x514c,
+ 0x0904, 0x2fcf, 0x080c, 0x4e8d, 0x080c, 0x5113, 0x080c, 0x5171,
+ 0x0804, 0x2faa, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c, 0x506f,
+ 0x0904, 0x2fcf, 0x62a0, 0x2019, 0x0005, 0x00c6, 0x080c, 0x51aa,
+ 0x2061, 0x0000, 0x080c, 0x6df5, 0x0076, 0x2039, 0x0000, 0x080c,
+ 0x6d02, 0x2009, 0x0000, 0x080c, 0xae82, 0x007e, 0x00ce, 0x080c,
+ 0x5171, 0x0804, 0x2faa, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c,
+ 0x5171, 0x2208, 0x0804, 0x2faa, 0x0156, 0x00d6, 0x00e6, 0x2069,
+ 0xb614, 0x6810, 0x6914, 0xa10a, 0x1210, 0x2009, 0x0000, 0x6816,
+ 0x2011, 0x0000, 0x2019, 0x0000, 0x20a9, 0x007e, 0x2069, 0xb635,
+ 0x2d04, 0xa075, 0x0130, 0x704c, 0x0071, 0xa210, 0x7080, 0x0059,
+ 0xa318, 0x8d68, 0x1f04, 0x3218, 0x2300, 0xa218, 0x00ee, 0x00de,
+ 0x015e, 0x0804, 0x2faa, 0x00f6, 0x0016, 0xa07d, 0x0140, 0x2001,
+ 0x0000, 0x8000, 0x2f0c, 0x81ff, 0x0110, 0x2178, 0x0cd0, 0x001e,
+ 0x00fe, 0x0005, 0x2069, 0xb614, 0x6910, 0x62b0, 0x0804, 0x2faa,
+ 0x81ff, 0x1904, 0x2fcf, 0x6150, 0xa190, 0x2dc4, 0x2215, 0xa294,
+ 0x00ff, 0x6370, 0x83ff, 0x0108, 0x6274, 0x67d4, 0xd79c, 0x0118,
+ 0x2031, 0x0001, 0x0090, 0xd7ac, 0x0118, 0x2031, 0x0003, 0x0068,
+ 0xd7a4, 0x0118, 0x2031, 0x0002, 0x0040, 0x080c, 0x5acf, 0x1118,
+ 0x2031, 0x0004, 0x0010, 0x2031, 0x0000, 0x7e3a, 0x7f3e, 0x0804,
+ 0x2faa, 0x6140, 0x6244, 0x2019, 0xb7b6, 0x231c, 0x0804, 0x2faa,
+ 0x0126, 0x2091, 0x8000, 0x6134, 0xa006, 0x2010, 0x6338, 0x012e,
+ 0x0804, 0x2faa, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x6244, 0x6338,
+ 0x0804, 0x2faa, 0x6140, 0x6244, 0x7824, 0x6042, 0x7b28, 0x6346,
+ 0x2069, 0xb552, 0x831f, 0xa305, 0x6816, 0x782c, 0x2069, 0xb7b6,
+ 0x2d1c, 0x206a, 0x0804, 0x2faa, 0x0126, 0x2091, 0x8000, 0x7824,
+ 0x6036, 0x782c, 0x603a, 0x012e, 0x0804, 0x2faa, 0x7838, 0xa005,
+ 0x01a8, 0x7828, 0xa025, 0x0904, 0x2fd2, 0x782c, 0xa02d, 0x0904,
+ 0x2fd2, 0xa00e, 0x080c, 0x4fa9, 0x1120, 0x6244, 0x6338, 0x6446,
+ 0x653a, 0xa186, 0x00ff, 0x0190, 0x8108, 0x0ca0, 0x080c, 0x3e9a,
+ 0x0904, 0x2fd2, 0x7828, 0xa00d, 0x0904, 0x2fd2, 0x782c, 0xa005,
+ 0x0904, 0x2fd2, 0x6244, 0x6146, 0x6338, 0x603a, 0x0804, 0x2faa,
+ 0x2001, 0xb500, 0x2004, 0xa086, 0x0003, 0x1904, 0x2fcf, 0x00c6,
+ 0x2061, 0x0100, 0x7924, 0x810f, 0xa18c, 0x00ff, 0xa196, 0x00ff,
+ 0x1130, 0x2001, 0xb515, 0x2004, 0xa085, 0xff00, 0x0078, 0xa182,
+ 0x007f, 0x16a0, 0xa188, 0x2dc4, 0x210d, 0xa18c, 0x00ff, 0x2001,
+ 0xb515, 0x2004, 0xa116, 0x0550, 0x810f, 0xa105, 0x0126, 0x2091,
+ 0x8000, 0x0006, 0x080c, 0x85c7, 0x000e, 0x01e0, 0x601a, 0x600b,
+ 0xbc09, 0x601f, 0x0001, 0x080c, 0x3e75, 0x01d8, 0x6837, 0x0000,
+ 0x7007, 0x0003, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x701b,
+ 0x3370, 0x2d00, 0x6012, 0x2009, 0x0032, 0x080c, 0x864c, 0x012e,
+ 0x00ce, 0x0005, 0x012e, 0x00ce, 0x0804, 0x2fcf, 0x00ce, 0x0804,
+ 0x2fd2, 0x080c, 0x861d, 0x0cb0, 0x2001, 0xb500, 0x2004, 0xa086,
+ 0x0003, 0x1904, 0x2fcf, 0x00c6, 0x2061, 0x0100, 0x7924, 0x810f,
+ 0xa18c, 0x00ff, 0xa196, 0x00ff, 0x1130, 0x2001, 0xb515, 0x2004,
+ 0xa085, 0xff00, 0x0078, 0xa182, 0x007f, 0x16a0, 0xa188, 0x2dc4,
+ 0x210d, 0xa18c, 0x00ff, 0x2001, 0xb515, 0x2004, 0xa116, 0x0550,
+ 0x810f, 0xa105, 0x0126, 0x2091, 0x8000, 0x0006, 0x080c, 0x85c7,
+ 0x000e, 0x01e0, 0x601a, 0x600b, 0xbc05, 0x601f, 0x0001, 0x080c,
+ 0x3e75, 0x01d8, 0x6837, 0x0000, 0x7007, 0x0003, 0x6833, 0x0000,
+ 0x6838, 0xc0fd, 0x683a, 0x701b, 0x3370, 0x2d00, 0x6012, 0x2009,
+ 0x0032, 0x080c, 0x864c, 0x012e, 0x00ce, 0x0005, 0x012e, 0x00ce,
+ 0x0804, 0x2fcf, 0x00ce, 0x0804, 0x2fd2, 0x080c, 0x861d, 0x0cb0,
+ 0x6830, 0xa086, 0x0100, 0x0904, 0x2fcf, 0x0804, 0x2faa, 0x2061,
+ 0xb874, 0x0126, 0x2091, 0x8000, 0x6000, 0xd084, 0x0178, 0x6104,
+ 0x6208, 0x2a60, 0x6068, 0x783a, 0x60b4, 0x783e, 0x60b0, 0x2019,
+ 0x0072, 0x201a, 0x6348, 0x012e, 0x0804, 0x2faa, 0xa00e, 0x2110,
+ 0x0c80, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x5acf, 0x0904, 0x2fcf,
+ 0x0126, 0x2091, 0x8000, 0x6248, 0x6068, 0xa202, 0x0248, 0xa085,
+ 0x0001, 0x080c, 0x2867, 0x080c, 0x462c, 0x012e, 0x0804, 0x2faa,
+ 0x012e, 0x0804, 0x2fd2, 0x0006, 0x0016, 0x00c6, 0x00e6, 0x2001,
+ 0xb7bf, 0x2070, 0x2061, 0xb552, 0x6008, 0x2072, 0x2009, 0x0000,
+ 0x2011, 0x1000, 0x080c, 0x6b40, 0x7206, 0x00ee, 0x00ce, 0x001e,
+ 0x000e, 0x0005, 0x0126, 0x2091, 0x8000, 0x7824, 0xa084, 0x0007,
+ 0x0002, 0x33d4, 0x33dd, 0x33e4, 0x33d1, 0x33d1, 0x33d1, 0x33d1,
+ 0x33d1, 0x012e, 0x0804, 0x2fd2, 0x2009, 0x0114, 0x2104, 0xa085,
+ 0x0800, 0x200a, 0x080c, 0x354f, 0x0070, 0x2009, 0x010b, 0x200b,
+ 0x0010, 0x080c, 0x354f, 0x0038, 0x81ff, 0x0128, 0x012e, 0x2021,
+ 0x400b, 0x0804, 0x2fac, 0x0086, 0x0096, 0x00a6, 0x00b6, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x080c, 0x33ab, 0x2009, 0x0101, 0x210c,
+ 0x0016, 0x2001, 0x0138, 0x200c, 0x2003, 0x0001, 0x0016, 0x2001,
+ 0x007a, 0x2034, 0x2001, 0x007b, 0x202c, 0xa006, 0x2048, 0x2050,
+ 0x2058, 0x080c, 0x379a, 0x080c, 0x36fe, 0xa03e, 0x2720, 0x00f6,
+ 0x00e6, 0x00c6, 0x2d60, 0x2071, 0xb84a, 0x2079, 0x0020, 0x00d6,
+ 0x2069, 0x0000, 0x6824, 0xd0b4, 0x0140, 0x2001, 0x007d, 0x2004,
+ 0x783e, 0x2001, 0x007c, 0x2004, 0x783a, 0x00de, 0x2011, 0x0001,
+ 0x080c, 0x36aa, 0x080c, 0x36aa, 0x00ce, 0x00ee, 0x00fe, 0x080c,
+ 0x35f5, 0x080c, 0x36d2, 0x080c, 0x364f, 0x080c, 0x35b4, 0x080c,
+ 0x35e5, 0x00f6, 0x2079, 0x0100, 0x7824, 0xd094, 0x0530, 0x7814,
+ 0xa084, 0x0184, 0xa085, 0x0010, 0x7816, 0x2079, 0x0140, 0x080c,
+ 0x352d, 0x1110, 0x00fe, 0x0430, 0x7804, 0xd0dc, 0x0dc0, 0x2079,
+ 0x0100, 0x7827, 0x0086, 0x7814, 0xa084, 0x0184, 0xa085, 0x0032,
+ 0x7816, 0x080c, 0x352d, 0x1110, 0x00fe, 0x00a0, 0x7824, 0xd0bc,
+ 0x0dc0, 0x7827, 0x0080, 0xa026, 0x7c16, 0x7824, 0xd0ac, 0x0130,
+ 0x8b58, 0x080c, 0x3537, 0x00fe, 0x0804, 0x34f7, 0x00fe, 0x080c,
+ 0x352d, 0x1150, 0x8948, 0x2001, 0x007a, 0x2602, 0x2001, 0x007b,
+ 0x2502, 0x080c, 0x3537, 0x0088, 0x87ff, 0x0140, 0x2001, 0x0201,
+ 0x2004, 0xa005, 0x1904, 0x3431, 0x8739, 0x0038, 0x2001, 0xb823,
+ 0x2004, 0xa086, 0x0000, 0x1904, 0x3431, 0x2001, 0x0033, 0x2003,
+ 0x00f6, 0x8631, 0x1208, 0x8529, 0x2500, 0xa605, 0x0904, 0x34f7,
+ 0x7824, 0xd0bc, 0x0128, 0x2900, 0xaa05, 0xab05, 0x1904, 0x34f7,
+ 0x6033, 0x000d, 0x2001, 0x0030, 0x2003, 0x0004, 0x7824, 0xd0ac,
+ 0x1148, 0x2001, 0xb823, 0x2003, 0x0003, 0x2001, 0x0030, 0x2003,
+ 0x0009, 0x0040, 0x6027, 0x0001, 0x2001, 0x0075, 0x2004, 0xa005,
+ 0x0108, 0x6026, 0x2c00, 0x601a, 0x20e1, 0x9040, 0x2d00, 0x681a,
+ 0x6833, 0x000d, 0x7824, 0xd0a4, 0x1180, 0x6827, 0x0000, 0x00c6,
+ 0x20a9, 0x0004, 0x2061, 0x0020, 0x6003, 0x0008, 0x2001, 0x0203,
+ 0x2004, 0x1f04, 0x34cc, 0x00ce, 0x0040, 0x6827, 0x0001, 0x2001,
+ 0x0074, 0x2004, 0xa005, 0x0108, 0x6826, 0x00f6, 0x00c6, 0x2079,
+ 0x0100, 0x2061, 0x0020, 0x7827, 0x0002, 0x2001, 0x0072, 0x2004,
+ 0xa084, 0xfff8, 0x601a, 0x0006, 0x2001, 0x0073, 0x2004, 0x601e,
+ 0x78c6, 0x000e, 0x78ca, 0x00ce, 0x00fe, 0x0804, 0x340f, 0x2061,
+ 0x0100, 0x6027, 0x0002, 0x001e, 0x61e2, 0x001e, 0x6106, 0x7824,
+ 0xa084, 0x0003, 0xa086, 0x0002, 0x0188, 0x20e1, 0x9028, 0x6050,
+ 0xa084, 0xf7ef, 0x6052, 0x602f, 0x0000, 0x602c, 0xc0ac, 0x602e,
+ 0x604b, 0xf7f7, 0x6043, 0x0090, 0x6043, 0x0010, 0x2908, 0x2a10,
+ 0x2b18, 0x2b00, 0xaa05, 0xa905, 0x00fe, 0x00ee, 0x00de, 0x00ce,
+ 0x00be, 0x00ae, 0x009e, 0x008e, 0x1118, 0x012e, 0x0804, 0x2faa,
+ 0x012e, 0x2021, 0x400c, 0x0804, 0x2fac, 0xa085, 0x0001, 0x1d04,
+ 0x3536, 0x2091, 0x6000, 0x8420, 0xa486, 0x0064, 0x0005, 0x2001,
+ 0x0105, 0x2003, 0x0010, 0x2001, 0x0030, 0x2003, 0x0004, 0x2001,
+ 0x0020, 0x2003, 0x0004, 0x2001, 0xb823, 0x2003, 0x0000, 0x2001,
+ 0xb84a, 0x2003, 0x0000, 0x20e1, 0xf000, 0xa026, 0x0005, 0x00f6,
+ 0x2079, 0x0100, 0x2001, 0xb515, 0x200c, 0x7932, 0x7936, 0x080c,
+ 0x2847, 0x7850, 0xa084, 0x0980, 0xa085, 0x0030, 0x7852, 0x2019,
+ 0x01f4, 0x8319, 0x1df0, 0xa084, 0x0980, 0x7852, 0x782c, 0xc0ad,
+ 0x782e, 0x20a9, 0x0046, 0x1d04, 0x356b, 0x2091, 0x6000, 0x1f04,
+ 0x356b, 0x7850, 0xa085, 0x0400, 0x7852, 0x2001, 0x0009, 0x2004,
+ 0xa084, 0x0003, 0xa086, 0x0001, 0x1118, 0x782c, 0xc0ac, 0x782e,
+ 0x784b, 0xf7f7, 0x7843, 0x0090, 0x7843, 0x0010, 0x20a9, 0x000e,
+ 0xe000, 0x1f04, 0x3588, 0x7850, 0xa085, 0x1400, 0x7852, 0x2019,
+ 0x61a8, 0x7854, 0xe000, 0xe000, 0xd08c, 0x1110, 0x8319, 0x1dc8,
+ 0x7827, 0x0048, 0x7850, 0xa085, 0x0400, 0x7852, 0x7843, 0x0040,
+ 0x2019, 0x01f4, 0xe000, 0xe000, 0x8319, 0x1de0, 0x2001, 0x0140,
+ 0x2003, 0x0100, 0x7827, 0x0020, 0x7843, 0x0000, 0x2003, 0x0000,
+ 0x7827, 0x0048, 0x00fe, 0x0005, 0x7824, 0xd0ac, 0x11c8, 0x00f6,
+ 0x00e6, 0x2071, 0xb823, 0x2079, 0x0030, 0x2001, 0x0201, 0x2004,
+ 0xa005, 0x0160, 0x7000, 0xa086, 0x0000, 0x1140, 0x0051, 0xd0bc,
+ 0x0108, 0x8738, 0x7003, 0x0003, 0x7803, 0x0019, 0x00ee, 0x00fe,
+ 0x0005, 0x780c, 0xa08c, 0x0070, 0x0178, 0x2009, 0x007a, 0x260a,
+ 0x2009, 0x007b, 0x250a, 0xd0b4, 0x0108, 0x8a50, 0xd0ac, 0x0108,
+ 0x8948, 0xd0a4, 0x0108, 0x8b58, 0x0005, 0x00f6, 0x2079, 0x0200,
+ 0x781c, 0xd084, 0x0140, 0x20e1, 0x0007, 0x20e1, 0x2000, 0x2001,
+ 0x020a, 0x2004, 0x0ca8, 0x00fe, 0x0005, 0x00e6, 0x2071, 0x0100,
+ 0x2001, 0xb7c0, 0x2004, 0x70e2, 0x2009, 0xb515, 0x210c, 0x716e,
+ 0x7063, 0x0100, 0x7166, 0x719e, 0x706b, 0x0000, 0x7073, 0x0809,
+ 0x7077, 0x0008, 0x7078, 0xa080, 0x0100, 0x707a, 0x7080, 0x8000,
+ 0x7082, 0x7087, 0xaaaa, 0xa006, 0x708a, 0x708e, 0x707e, 0x70d6,
+ 0x70ab, 0x0036, 0x70af, 0x95d5, 0x7027, 0x0080, 0x7014, 0xa084,
+ 0x0184, 0xa085, 0x0032, 0x7016, 0x080c, 0x36d2, 0x080c, 0x352d,
+ 0x1110, 0x8421, 0x0028, 0x7024, 0xd0bc, 0x0db0, 0x7027, 0x0080,
+ 0x00f6, 0x00e6, 0x2071, 0xb823, 0x2079, 0x0030, 0x00d6, 0x2069,
+ 0x0000, 0x6824, 0xd0b4, 0x0120, 0x683c, 0x783e, 0x6838, 0x783a,
+ 0x00de, 0x2011, 0x0011, 0x080c, 0x36aa, 0x2011, 0x0001, 0x080c,
+ 0x36aa, 0x00ee, 0x00fe, 0x7017, 0x0000, 0x00ee, 0x0005, 0x00f6,
+ 0x00e6, 0x2071, 0xb823, 0x2079, 0x0030, 0x7904, 0xd1fc, 0x0904,
+ 0x36a7, 0x7803, 0x0002, 0xa026, 0xd19c, 0x1904, 0x36a3, 0x7000,
+ 0x0002, 0x36a7, 0x3665, 0x3689, 0x36a3, 0xd1bc, 0x1150, 0xd1dc,
+ 0x1150, 0x8001, 0x7002, 0x2011, 0x0001, 0x04e1, 0x05c0, 0x04d1,
+ 0x04b0, 0x780f, 0x0000, 0x7820, 0x7924, 0x7803, 0x0004, 0x7822,
+ 0x7926, 0x2001, 0x0201, 0x200c, 0x81ff, 0x0de8, 0x080c, 0x35d1,
+ 0x2009, 0x0001, 0x7808, 0xd0ec, 0x0110, 0x2009, 0x0011, 0x7902,
+ 0x00f0, 0x8001, 0x7002, 0xa184, 0x0880, 0x1138, 0x7804, 0xd0fc,
+ 0x1940, 0x2011, 0x0001, 0x00b1, 0x0090, 0x6030, 0xa092, 0x0004,
+ 0xa086, 0x0009, 0x1120, 0x6000, 0x601a, 0x2011, 0x0025, 0x6232,
+ 0xd1dc, 0x1988, 0x0870, 0x7803, 0x0004, 0x7003, 0x0000, 0x00ee,
+ 0x00fe, 0x0005, 0x6024, 0xa005, 0x0520, 0x8001, 0x6026, 0x6018,
+ 0x6130, 0xa140, 0x2804, 0x7832, 0x8840, 0x2804, 0x7836, 0x8840,
+ 0x2804, 0x7822, 0x8840, 0x2804, 0x7826, 0x8840, 0x7a02, 0x7000,
+ 0x8000, 0x7002, 0x6018, 0xa802, 0xa08a, 0x0029, 0x1138, 0x6018,
+ 0xa080, 0x0001, 0x2004, 0x601a, 0x2001, 0x000d, 0x6032, 0xa085,
+ 0x0001, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x2071, 0xb84a, 0x2079,
+ 0x0020, 0x7904, 0xd1fc, 0x01f0, 0x7803, 0x0002, 0x2d60, 0xa026,
+ 0x7000, 0x0002, 0x36fa, 0x36e5, 0x36f1, 0x8001, 0x7002, 0xd19c,
+ 0x1188, 0x2011, 0x0001, 0x080c, 0x36aa, 0x0160, 0x080c, 0x36aa,
+ 0x0048, 0x8001, 0x7002, 0x7804, 0xd0fc, 0x1d30, 0x2011, 0x0001,
+ 0x080c, 0x36aa, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x00e6,
+ 0x00c6, 0x2061, 0x0200, 0x2001, 0xb7c0, 0x2004, 0x601a, 0x2061,
+ 0x0100, 0x2001, 0xb7bf, 0x2004, 0x60ce, 0x6004, 0xc0ac, 0xa085,
+ 0x0200, 0x6006, 0x2001, 0x0074, 0x2004, 0xa005, 0x01f8, 0x2038,
+ 0x2001, 0x0076, 0x2024, 0x2001, 0x0077, 0x201c, 0x080c, 0x3e75,
+ 0x6833, 0x000d, 0x6f26, 0x2d00, 0x681a, 0xa78a, 0x0007, 0x0220,
+ 0x2138, 0x2009, 0x0007, 0x0010, 0x2708, 0xa03e, 0x6818, 0xa080,
+ 0x000d, 0x04b1, 0x1d90, 0x2d00, 0x681a, 0x0088, 0x080c, 0x3e75,
+ 0x6833, 0x000d, 0x2070, 0x6827, 0x0001, 0x2d00, 0x681a, 0x2001,
+ 0x0076, 0x2004, 0x2072, 0x2001, 0x0077, 0x2004, 0x7006, 0x2061,
+ 0x0020, 0x2079, 0x0100, 0x2001, 0xb7bf, 0x2004, 0x6012, 0x20e1,
+ 0x9040, 0x2001, 0x0072, 0x2004, 0xa084, 0xfff8, 0x700a, 0x601a,
+ 0x0006, 0x2001, 0x0073, 0x2004, 0x700e, 0x601e, 0x78c6, 0x000e,
+ 0x78ca, 0xa006, 0x603a, 0x603e, 0x00ce, 0x00ee, 0x00fe, 0x0005,
+ 0x00e6, 0x2071, 0x0010, 0x20a0, 0x2099, 0x0014, 0x7003, 0x0026,
+ 0x7432, 0x7336, 0xa006, 0x703a, 0x703e, 0x810b, 0x810b, 0x21a8,
+ 0x810b, 0x7122, 0x7003, 0x0041, 0x7004, 0xd0fc, 0x0de8, 0x7003,
+ 0x0002, 0x7003, 0x0040, 0x53a5, 0x7430, 0x7334, 0x87ff, 0x0180,
+ 0x00c6, 0x00d6, 0x2d60, 0x00c6, 0x080c, 0x3e75, 0x00ce, 0x6018,
+ 0x2070, 0x2d00, 0x7006, 0x601a, 0x00de, 0x00ce, 0xa085, 0x0001,
+ 0x00ee, 0x0005, 0x00e6, 0x2001, 0x0075, 0x2004, 0xa005, 0x0508,
+ 0x2038, 0x2001, 0x0078, 0x2024, 0x2001, 0x0079, 0x201c, 0x080c,
+ 0x3e75, 0x2d60, 0x6833, 0x000d, 0x6f26, 0x2d00, 0x681a, 0xa78a,
+ 0x0007, 0x0220, 0x2138, 0x2009, 0x0007, 0x0010, 0x2708, 0xa03e,
+ 0x6818, 0xa080, 0x000d, 0x080c, 0x3768, 0x1d88, 0x2d00, 0x681a,
+ 0x00e0, 0x080c, 0x3e75, 0x2d60, 0x6033, 0x000d, 0x2070, 0x6027,
+ 0x0001, 0x2c00, 0x601a, 0x2001, 0x0078, 0x2004, 0x2072, 0x2001,
+ 0x0079, 0x2004, 0x7006, 0x2001, 0x0072, 0x2004, 0xa084, 0xfff8,
+ 0x700a, 0x2001, 0x0073, 0x2004, 0x700e, 0x2001, 0x0030, 0x2003,
+ 0x0004, 0x7824, 0xd0ac, 0x1178, 0x2001, 0x0101, 0x200c, 0xc1ed,
+ 0x2102, 0x6027, 0x0000, 0x2001, 0xb823, 0x2003, 0x0003, 0x2001,
+ 0x0030, 0x2003, 0x0009, 0x00ee, 0x0005, 0x0804, 0x2faa, 0x0126,
+ 0x2091, 0x8000, 0x20a9, 0x0012, 0x2001, 0xb540, 0x20a0, 0xa006,
+ 0x40a4, 0x012e, 0x0804, 0x2faa, 0x7d38, 0x7c3c, 0x0804, 0x3051,
+ 0x080c, 0x3e75, 0x0904, 0x2fcf, 0x080c, 0x5acf, 0x0110, 0x080c,
+ 0x4bf0, 0x2009, 0x001c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c,
+ 0x3eb6, 0x701b, 0x381c, 0x0005, 0xade8, 0x000d, 0x6800, 0xa005,
+ 0x0904, 0x2fd2, 0x6804, 0xd0ac, 0x0118, 0xd0a4, 0x0904, 0x2fd2,
+ 0xd094, 0x00c6, 0x2061, 0x0100, 0x6104, 0x0138, 0x6200, 0xa292,
+ 0x0005, 0x0218, 0xa18c, 0xffdf, 0x0010, 0xa18d, 0x0020, 0x6106,
+ 0x00ce, 0xd08c, 0x00c6, 0x2061, 0x0100, 0x6104, 0x0118, 0xa18d,
+ 0x0010, 0x0010, 0xa18c, 0xffef, 0x6106, 0x00ce, 0x2009, 0x0100,
+ 0x210c, 0xa18a, 0x0002, 0x0268, 0xd084, 0x0158, 0x6a28, 0xa28a,
+ 0x007f, 0x1a04, 0x2fd2, 0xa288, 0x2dc4, 0x210d, 0xa18c, 0x00ff,
+ 0x615a, 0xd0dc, 0x0130, 0x6828, 0xa08a, 0x007f, 0x1a04, 0x2fd2,
+ 0x6052, 0x6808, 0xa08a, 0x0100, 0x0a04, 0x2fd2, 0xa08a, 0x0841,
+ 0x1a04, 0x2fd2, 0xa084, 0x0007, 0x1904, 0x2fd2, 0x680c, 0xa005,
+ 0x0904, 0x2fd2, 0x6810, 0xa005, 0x0904, 0x2fd2, 0x6848, 0x6940,
+ 0xa10a, 0x1a04, 0x2fd2, 0x8001, 0x0904, 0x2fd2, 0x684c, 0x6944,
+ 0xa10a, 0x1a04, 0x2fd2, 0x8001, 0x0904, 0x2fd2, 0x6804, 0xd0fc,
+ 0x0560, 0x080c, 0x3e75, 0x0904, 0x2fcf, 0x2009, 0x0014, 0x7a2c,
+ 0x7b28, 0x7c3c, 0x7d38, 0xa290, 0x0038, 0xa399, 0x0000, 0x080c,
+ 0x3eb6, 0x701b, 0x389c, 0x0005, 0xade8, 0x000d, 0x20a9, 0x0014,
+ 0x2d98, 0x2069, 0xb56e, 0x2da0, 0x53a3, 0x7010, 0xa0e8, 0x000d,
+ 0x2001, 0xb572, 0x200c, 0xd1e4, 0x0140, 0x00c6, 0x2061, 0x0100,
+ 0x6004, 0xa085, 0x0b00, 0x6006, 0x00ce, 0x2009, 0xb7b1, 0x200b,
+ 0x0000, 0x2001, 0xb574, 0x2004, 0xd0ac, 0x0158, 0x7824, 0x200a,
+ 0x2009, 0x017f, 0x200a, 0x3200, 0xa084, 0x003f, 0xa085, 0x3020,
+ 0x2090, 0x20a9, 0x001c, 0x2d98, 0x2069, 0xb552, 0x2da0, 0x53a3,
+ 0x6814, 0xa08c, 0x00ff, 0x6142, 0x8007, 0xa084, 0x00ff, 0x6046,
+ 0x080c, 0x5da5, 0x080c, 0x536c, 0x080c, 0x53d5, 0x6000, 0xa086,
+ 0x0000, 0x1904, 0x3997, 0x6808, 0x602a, 0x080c, 0x2470, 0x0006,
+ 0x2001, 0x0100, 0x2004, 0xa082, 0x0005, 0x000e, 0x0268, 0x2009,
+ 0x0170, 0x200b, 0x0080, 0xe000, 0xe000, 0x200b, 0x0000, 0x0036,
+ 0x6b08, 0x080c, 0x28a2, 0x003e, 0x6818, 0x691c, 0x6a20, 0x6b24,
+ 0x8007, 0x810f, 0x8217, 0x831f, 0x6016, 0x611a, 0x621e, 0x6322,
+ 0x6c04, 0xd4f4, 0x0148, 0x6830, 0x6934, 0x6a38, 0x6b3c, 0x8007,
+ 0x810f, 0x8217, 0x831f, 0x0010, 0xa084, 0xf0ff, 0x6006, 0x610a,
+ 0x620e, 0x6312, 0x8007, 0x810f, 0x8217, 0x831f, 0x20a9, 0x0004,
+ 0x20a1, 0xb7c6, 0x40a1, 0x080c, 0x6a68, 0x6904, 0xd1fc, 0x0520,
+ 0x00c6, 0x2009, 0x0000, 0x20a9, 0x0001, 0x6b70, 0xd384, 0x01c8,
+ 0x0020, 0x839d, 0x12b0, 0x3508, 0x8109, 0x080c, 0x635c, 0x6878,
+ 0x6016, 0x6874, 0x2008, 0xa084, 0xff00, 0x8007, 0x600a, 0xa184,
+ 0x00ff, 0x6006, 0x8108, 0x1118, 0x6003, 0x0003, 0x0010, 0x6003,
+ 0x0001, 0x1f04, 0x3931, 0x00ce, 0x2069, 0xb552, 0x2001, 0xb79e,
+ 0x6a80, 0xa294, 0x0030, 0xa28e, 0x0000, 0x0170, 0xa28e, 0x0010,
+ 0x0118, 0xa28e, 0x0020, 0x0140, 0x2003, 0xaaaa, 0x080c, 0x28eb,
+ 0x2001, 0xb78f, 0x2102, 0x0008, 0x2102, 0x00c6, 0x2061, 0x0100,
+ 0x602f, 0x0040, 0x602f, 0x0000, 0x00ce, 0x080c, 0x5acf, 0x0128,
+ 0x080c, 0x40cf, 0x0110, 0x080c, 0x2867, 0x60c8, 0xa005, 0x01d0,
+ 0x6003, 0x0001, 0x2009, 0x397d, 0x00e0, 0x080c, 0x5acf, 0x1178,
+ 0x2011, 0x59a2, 0x080c, 0x699c, 0x2011, 0x5995, 0x080c, 0x6a5c,
+ 0x2001, 0xb79f, 0x2003, 0x0000, 0x080c, 0x5a07, 0x0040, 0x080c,
+ 0x4b1f, 0x0028, 0x6003, 0x0004, 0x2009, 0x3997, 0x0010, 0x0804,
+ 0x2faa, 0x2001, 0x0100, 0x2004, 0xa082, 0x0005, 0x0258, 0x2001,
+ 0x0170, 0x2004, 0xa084, 0x00ff, 0xa086, 0x004c, 0x1118, 0x2091,
+ 0x309d, 0x0817, 0x2091, 0x301d, 0x0817, 0x6000, 0xa086, 0x0000,
+ 0x0904, 0x2fcf, 0x2069, 0xb552, 0x7830, 0x6842, 0x7834, 0x6846,
+ 0x6804, 0xd0fc, 0x0118, 0x2009, 0x0030, 0x0010, 0x2009, 0x001c,
+ 0x2d00, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3eb9, 0xa006,
+ 0x080c, 0x2867, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x5acf, 0x1178,
+ 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001,
+ 0xa085, 0x0001, 0x080c, 0x5b13, 0x080c, 0x5a07, 0x0020, 0x080c,
+ 0x4bf0, 0x080c, 0x4b1f, 0x0804, 0x2faa, 0x81ff, 0x1904, 0x2fcf,
+ 0x080c, 0x5acf, 0x1110, 0x0804, 0x2fcf, 0x6188, 0x81ff, 0x0198,
+ 0x703f, 0x0000, 0x2001, 0xbcc0, 0x2009, 0x0040, 0x7a2c, 0x7b28,
+ 0x7c3c, 0x7d38, 0x0126, 0x2091, 0x8000, 0x080c, 0x3eb9, 0x701b,
+ 0x2fa8, 0x012e, 0x0005, 0x703f, 0x0001, 0x00d6, 0x2069, 0xbcc0,
+ 0x20a9, 0x0040, 0x20a1, 0xbcc0, 0x2019, 0xffff, 0x43a4, 0x6550,
+ 0xa588, 0x2dc4, 0x210d, 0xa18c, 0x00ff, 0x216a, 0xa00e, 0x2011,
+ 0x0002, 0x2100, 0xa506, 0x01a8, 0x080c, 0x4fa9, 0x1190, 0x6014,
+ 0x821c, 0x0238, 0xa398, 0xbcc0, 0xa085, 0xff00, 0x8007, 0x201a,
+ 0x0038, 0xa398, 0xbcc0, 0x2324, 0xa4a4, 0xff00, 0xa405, 0x201a,
+ 0x8210, 0x8108, 0xa182, 0x0080, 0x1208, 0x0c18, 0x8201, 0x8007,
+ 0x2d0c, 0xa105, 0x206a, 0x00de, 0x20a9, 0x0040, 0x20a1, 0xbcc0,
+ 0x2099, 0xbcc0, 0x080c, 0x4b8f, 0x0804, 0x39f2, 0x080c, 0x3e9a,
+ 0x0904, 0x2fd2, 0x00c6, 0x080c, 0x3e75, 0x00ce, 0x1120, 0x2009,
+ 0x0002, 0x0804, 0x2fcf, 0x2001, 0xb553, 0x2004, 0xd0b4, 0x0550,
+ 0x7824, 0xa084, 0xff00, 0xa08e, 0x7e00, 0x0520, 0xa08e, 0x7f00,
+ 0x0508, 0xa08e, 0x8000, 0x01f0, 0x6000, 0xd08c, 0x11d8, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x11a8, 0x6837, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x080c, 0x9dda, 0x1120, 0x2009, 0x0003, 0x0804,
+ 0x2fcf, 0x7007, 0x0003, 0x701b, 0x3a7e, 0x0005, 0x080c, 0x3e9a,
+ 0x0904, 0x2fd2, 0x20a9, 0x002b, 0x2c98, 0xade8, 0x0002, 0x2da0,
+ 0x53a3, 0x20a9, 0x0004, 0xac80, 0x0006, 0x2098, 0xad80, 0x0006,
+ 0x20a0, 0x080c, 0x4b8f, 0x20a9, 0x0004, 0xac80, 0x000a, 0x2098,
+ 0xad80, 0x000a, 0x20a0, 0x080c, 0x4b8f, 0x2d00, 0x2009, 0x002b,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3eb9, 0x81ff, 0x1904,
+ 0x2fcf, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c, 0x5186, 0x0804,
+ 0x2faa, 0x81ff, 0x1904, 0x2fcf, 0x7828, 0xa08a, 0x1000, 0x1a04,
+ 0x2fd2, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x080c, 0x51e9, 0x0904,
+ 0x2fcf, 0x2019, 0x0004, 0xa00e, 0x080c, 0x5198, 0x7924, 0x810f,
+ 0x7a28, 0x0011, 0x0804, 0x2faa, 0xa186, 0x00ff, 0x0110, 0x0071,
+ 0x0060, 0x2029, 0x007e, 0x2061, 0xb500, 0x6450, 0x2400, 0xa506,
+ 0x0110, 0x2508, 0x0019, 0x8529, 0x1ec8, 0x0005, 0x080c, 0x4fa9,
+ 0x1138, 0x2200, 0x8003, 0x800b, 0x810b, 0xa108, 0x080c, 0x69a8,
+ 0x0005, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e8a, 0x0904, 0x2fd2,
+ 0x080c, 0x506f, 0x0904, 0x2fcf, 0x080c, 0x518f, 0x0804, 0x2faa,
+ 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c,
+ 0x506f, 0x0904, 0x2fcf, 0x080c, 0x517d, 0x0804, 0x2faa, 0x6100,
+ 0x0804, 0x2faa, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x2001, 0xb500,
+ 0x2004, 0xa086, 0x0003, 0x1904, 0x2fcf, 0x00d6, 0xace8, 0x000a,
+ 0x7924, 0xd184, 0x0110, 0xace8, 0x0006, 0x680c, 0x8007, 0x783e,
+ 0x6808, 0x8007, 0x783a, 0x6b04, 0x831f, 0x6a00, 0x8217, 0x00de,
+ 0x6100, 0xa18c, 0x0200, 0x0804, 0x2faa, 0x7824, 0xa09c, 0x0003,
+ 0xd0b4, 0x1160, 0xa39a, 0x0003, 0x1a04, 0x2fcf, 0x6250, 0xa294,
+ 0x00ff, 0xa084, 0xff00, 0x8007, 0xa206, 0x1150, 0x2001, 0xb540,
+ 0x2009, 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3eb9,
+ 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x1904, 0x2fcf, 0x00c6, 0x080c,
+ 0x3e75, 0x00ce, 0x0904, 0x2fcf, 0x6837, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x080c, 0x9d86, 0x0904, 0x2fcf, 0x7007, 0x0003, 0x701b,
+ 0x3b6a, 0x0005, 0x6830, 0xa086, 0x0100, 0x0904, 0x2fcf, 0xad80,
+ 0x000e, 0x2009, 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804,
+ 0x3eb9, 0xa006, 0x080c, 0x2867, 0x7824, 0xa084, 0x00ff, 0xa086,
+ 0x00ff, 0x0118, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x5acf, 0x0110,
+ 0x080c, 0x4bf0, 0x7828, 0xa08a, 0x1000, 0x1a04, 0x2fd2, 0x7924,
+ 0xa18c, 0xff00, 0x810f, 0xa186, 0x00ff, 0x0138, 0xa182, 0x007f,
+ 0x1a04, 0x2fd2, 0x2100, 0x080c, 0x2831, 0x0026, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x2061, 0xb7f3, 0x601b, 0x0000, 0x601f, 0x0000,
+ 0x080c, 0x5acf, 0x1178, 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001,
+ 0xb500, 0x2003, 0x0001, 0xa085, 0x0001, 0x080c, 0x5b13, 0x080c,
+ 0x5a07, 0x0420, 0x2011, 0x0003, 0x080c, 0x8075, 0x2011, 0x0002,
+ 0x080c, 0x807f, 0x080c, 0x7f59, 0x0036, 0x2019, 0x0000, 0x080c,
+ 0x7fe4, 0x003e, 0x2061, 0x0100, 0x2001, 0xb515, 0x2004, 0xa084,
+ 0x00ff, 0x810f, 0xa105, 0x604a, 0x6043, 0x0090, 0x6043, 0x0010,
+ 0x2009, 0x002d, 0x2011, 0x4b54, 0x080c, 0x6a22, 0x7924, 0xa18c,
+ 0xff00, 0x810f, 0x080c, 0x5acf, 0x1110, 0x2009, 0x00ff, 0x7a28,
+ 0x080c, 0x3acc, 0x012e, 0x00ce, 0x002e, 0x0804, 0x2faa, 0x7924,
+ 0xa18c, 0xff00, 0x810f, 0x00c6, 0x080c, 0x4f4d, 0x2c08, 0x00ce,
+ 0x1904, 0x2fd2, 0x0804, 0x2faa, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x2fcf, 0x60d4, 0xd0ac, 0x1130, 0xd09c, 0x1120, 0x2009,
+ 0x0005, 0x0804, 0x2fcf, 0x080c, 0x3e75, 0x1120, 0x2009, 0x0002,
+ 0x0804, 0x2fcf, 0x7924, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c,
+ 0x3eb6, 0x701b, 0x3c1c, 0x0005, 0x2009, 0x0080, 0x080c, 0x4fa9,
+ 0x1130, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x0120, 0x2021,
+ 0x400a, 0x0804, 0x2fac, 0x00d6, 0xade8, 0x000d, 0x6900, 0x6a08,
+ 0x6b0c, 0x6c10, 0x6d14, 0x6e18, 0x6820, 0xa0be, 0x0100, 0x0904,
+ 0x3c93, 0xa0be, 0x0112, 0x0904, 0x3c93, 0xa0be, 0x0113, 0x0904,
+ 0x3c93, 0xa0be, 0x0114, 0x0904, 0x3c93, 0xa0be, 0x0117, 0x0904,
+ 0x3c93, 0xa0be, 0x011a, 0x0904, 0x3c93, 0xa0be, 0x011c, 0x0904,
+ 0x3c93, 0xa0be, 0x0121, 0x05b0, 0xa0be, 0x0131, 0x0598, 0xa0be,
+ 0x0171, 0x05c8, 0xa0be, 0x0173, 0x05b0, 0xa0be, 0x01a1, 0x1120,
+ 0x6830, 0x8007, 0x6832, 0x04a8, 0xa0be, 0x0212, 0x0540, 0xa0be,
+ 0x0213, 0x0528, 0xa0be, 0x0214, 0x01b0, 0xa0be, 0x0217, 0x0168,
+ 0xa0be, 0x021a, 0x1120, 0x6838, 0x8007, 0x683a, 0x00e0, 0xa0be,
+ 0x0300, 0x01c8, 0x00de, 0x0804, 0x2fd2, 0xad80, 0x0010, 0x20a9,
+ 0x0007, 0x080c, 0x3cd9, 0xad80, 0x000e, 0x20a9, 0x0001, 0x080c,
+ 0x3cd9, 0x0048, 0xad80, 0x000c, 0x080c, 0x3ce7, 0x0050, 0xad80,
+ 0x000e, 0x080c, 0x3ce7, 0xad80, 0x000c, 0x20a9, 0x0001, 0x080c,
+ 0x3cd9, 0x00c6, 0x080c, 0x3e75, 0x0568, 0x6838, 0xc0fd, 0x683a,
+ 0x6837, 0x0119, 0x6853, 0x0000, 0x684f, 0x0020, 0x685b, 0x0001,
+ 0x810b, 0x697e, 0x6883, 0x0000, 0x6a86, 0x6b8a, 0x6c8e, 0x6d92,
+ 0x6996, 0x689b, 0x0000, 0x00ce, 0x00de, 0x6837, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x6823, 0x0000, 0x6804, 0x2068, 0x080c, 0x9da2,
+ 0x1120, 0x2009, 0x0003, 0x0804, 0x2fcf, 0x7007, 0x0003, 0x701b,
+ 0x3cd0, 0x0005, 0x00ce, 0x00de, 0x2009, 0x0002, 0x0804, 0x2fcf,
+ 0x6820, 0xa086, 0x8001, 0x1904, 0x2faa, 0x2009, 0x0004, 0x0804,
+ 0x2fcf, 0x0016, 0x2008, 0x2044, 0x8000, 0x204c, 0x8000, 0x290a,
+ 0x8108, 0x280a, 0x8108, 0x1f04, 0x3cdb, 0x001e, 0x0005, 0x0016,
+ 0x00a6, 0x00b6, 0x2008, 0x2044, 0x8000, 0x204c, 0x8000, 0x2054,
+ 0x8000, 0x205c, 0x2b0a, 0x8108, 0x2a0a, 0x8108, 0x290a, 0x8108,
+ 0x280a, 0x00be, 0x00ae, 0x001e, 0x0005, 0x81ff, 0x0120, 0x2009,
+ 0x0001, 0x0804, 0x2fcf, 0x60d4, 0xd0ac, 0x1130, 0xd09c, 0x1120,
+ 0x2009, 0x0005, 0x0804, 0x2fcf, 0x7924, 0x2140, 0xa18c, 0xff00,
+ 0x810f, 0x60d4, 0xd0ac, 0x1120, 0xa182, 0x0080, 0x0a04, 0x2fd2,
+ 0xa182, 0x00ff, 0x1a04, 0x2fd2, 0x7a2c, 0x7b28, 0x6070, 0xa306,
+ 0x1140, 0x6074, 0xa24e, 0x0904, 0x2fd2, 0xa9cc, 0xff00, 0x0904,
+ 0x2fd2, 0x00c6, 0x080c, 0x3dc5, 0x2c68, 0x00ce, 0x0530, 0xa0c6,
+ 0x4000, 0x1178, 0x00c6, 0x0006, 0x2d60, 0xa00e, 0x080c, 0x524a,
+ 0x1108, 0xc185, 0x6000, 0xd0bc, 0x0108, 0xc18d, 0x000e, 0x00ce,
+ 0x0088, 0xa0c6, 0x4007, 0x1110, 0x2408, 0x0060, 0xa0c6, 0x4008,
+ 0x1118, 0x2708, 0x2610, 0x0030, 0xa0c6, 0x4009, 0x1108, 0x0010,
+ 0x2001, 0x4006, 0x2020, 0x0804, 0x2fac, 0x2d00, 0x7022, 0x0016,
+ 0x00b6, 0x00c6, 0x00e6, 0x2c70, 0x080c, 0x85c7, 0x05d8, 0x2d00,
+ 0x601a, 0x080c, 0xa027, 0x2e58, 0x00ee, 0x00e6, 0x00c6, 0x080c,
+ 0x3e75, 0x00ce, 0x2b70, 0x1150, 0x080c, 0x861d, 0x00ee, 0x00ce,
+ 0x00be, 0x001e, 0x2009, 0x0002, 0x0804, 0x2fcf, 0x6837, 0x0000,
+ 0x683b, 0x0000, 0x2d00, 0x6012, 0x6833, 0x0000, 0x6838, 0xc0fd,
+ 0xd88c, 0x0108, 0xc0f5, 0x683a, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x2c9c, 0x012e, 0x601f, 0x0001, 0x2001, 0x0000, 0x080c, 0x4eeb,
+ 0x2001, 0x0002, 0x080c, 0x4efd, 0x2009, 0x0002, 0x080c, 0x864c,
+ 0xa085, 0x0001, 0x00ee, 0x00ce, 0x00be, 0x001e, 0x1120, 0x2009,
+ 0x0003, 0x0804, 0x2fcf, 0x7007, 0x0003, 0x701b, 0x3da8, 0x0005,
+ 0x6830, 0xa086, 0x0100, 0x7020, 0x2060, 0x1138, 0x2009, 0x0004,
+ 0x6204, 0xa294, 0x00ff, 0x0804, 0x2fcf, 0x2009, 0x0000, 0x6838,
+ 0xd0f4, 0x1904, 0x2faa, 0x080c, 0x524a, 0x1108, 0xc185, 0x6000,
+ 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x2faa, 0x00e6, 0x00d6, 0xa02e,
+ 0x2001, 0xb535, 0x2004, 0xd0ac, 0x0130, 0xa026, 0x20a9, 0x00ff,
+ 0x2071, 0xb635, 0x0030, 0x2021, 0x0080, 0x20a9, 0x007f, 0x2071,
+ 0xb6b5, 0x2e04, 0xa005, 0x1130, 0x2100, 0xa406, 0x1570, 0x2428,
+ 0xc5fd, 0x0458, 0x2068, 0x6f10, 0x2700, 0xa306, 0x11b0, 0x6e14,
+ 0x2600, 0xa206, 0x1190, 0x2400, 0xa106, 0x1160, 0x2d60, 0xd884,
+ 0x0568, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1538, 0x2001,
+ 0x4000, 0x0428, 0x2001, 0x4007, 0x0410, 0x2400, 0xa106, 0x1168,
+ 0x6e14, 0x87ff, 0x1138, 0x86ff, 0x09d0, 0x2001, 0xb535, 0x2004,
+ 0xd0ac, 0x19a8, 0x2001, 0x4008, 0x0090, 0x8420, 0x8e70, 0x1f04,
+ 0x3dd9, 0x85ff, 0x1130, 0x2001, 0x4009, 0x0048, 0x2001, 0x0001,
+ 0x0030, 0x080c, 0x4f4d, 0x1dd0, 0x6312, 0x6216, 0xa006, 0xa005,
+ 0x00de, 0x00ee, 0x0005, 0x81ff, 0x1904, 0x2fcf, 0x080c, 0x3e75,
+ 0x0904, 0x2fcf, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x7824,
+ 0xa005, 0x0904, 0x2fd2, 0xa096, 0x00ff, 0x0120, 0xa092, 0x0004,
+ 0x1a04, 0x2fd2, 0x2010, 0x2d18, 0x080c, 0x2c4f, 0x0904, 0x2fcf,
+ 0x7007, 0x0003, 0x701b, 0x3e45, 0x0005, 0x6830, 0xa086, 0x0100,
+ 0x0904, 0x2fcf, 0x0804, 0x2faa, 0x7924, 0xa18c, 0xff00, 0x810f,
+ 0x60d4, 0xd0ac, 0x1120, 0xa182, 0x0080, 0x0a04, 0x2fd2, 0xa182,
+ 0x00ff, 0x1a04, 0x2fd2, 0x0126, 0x2091, 0x8000, 0x080c, 0x9c8a,
+ 0x1188, 0xa190, 0xb635, 0x2204, 0xa065, 0x0160, 0x080c, 0x4c0b,
+ 0x2001, 0xb535, 0x2004, 0xd0ac, 0x0110, 0x6017, 0x0000, 0x012e,
+ 0x0804, 0x2faa, 0x012e, 0x0804, 0x2fcf, 0x080c, 0x15f8, 0x0188,
+ 0xa006, 0x6802, 0x7010, 0xa005, 0x1120, 0x2d00, 0x7012, 0x7016,
+ 0x0030, 0x7014, 0x6802, 0x2060, 0x2d00, 0x6006, 0x7016, 0xad80,
+ 0x000d, 0x0005, 0x7924, 0x810f, 0xa18c, 0x00ff, 0x080c, 0x4fa9,
+ 0x1130, 0x7e28, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0208, 0xa066,
+ 0x8cff, 0x0005, 0x7e24, 0x860f, 0xa18c, 0x00ff, 0x080c, 0x4fa9,
+ 0x1128, 0xa6b4, 0x00ff, 0xa682, 0x4000, 0x0208, 0xa066, 0x8cff,
+ 0x0005, 0x0016, 0x7110, 0x81ff, 0x0128, 0x2168, 0x6904, 0x080c,
+ 0x160f, 0x0cc8, 0x7112, 0x7116, 0x001e, 0x0005, 0x2031, 0x0001,
+ 0x0010, 0x2031, 0x0000, 0x2061, 0xb5d2, 0x6606, 0x6112, 0x600e,
+ 0x6226, 0x632a, 0x642e, 0x6532, 0x2c10, 0x080c, 0x1643, 0x7007,
+ 0x0002, 0x701b, 0x2faa, 0x0005, 0x00f6, 0x0126, 0x2091, 0x8000,
+ 0x2079, 0x0000, 0x2001, 0xb590, 0x2004, 0xa005, 0x1168, 0x0e04,
+ 0x3ee4, 0x7818, 0xd084, 0x1140, 0x7a22, 0x7b26, 0x7c2a, 0x781b,
+ 0x0001, 0x2091, 0x4080, 0x0408, 0x0016, 0x00c6, 0x00e6, 0x2071,
+ 0xb582, 0x7138, 0xa182, 0x0010, 0x0218, 0x7030, 0x2060, 0x0078,
+ 0x7030, 0xa0e0, 0x0004, 0xac82, 0xb5d2, 0x0210, 0x2061, 0xb592,
+ 0x2c00, 0x7032, 0x81ff, 0x1108, 0x7036, 0x8108, 0x713a, 0x2262,
+ 0x6306, 0x640a, 0x00ee, 0x00ce, 0x001e, 0x012e, 0x00fe, 0x0005,
+ 0x00e6, 0x2071, 0xb582, 0x7038, 0xa005, 0x0570, 0x0126, 0x2091,
+ 0x8000, 0x0e04, 0x3f3b, 0x00f6, 0x2079, 0x0000, 0x7818, 0xd084,
+ 0x1508, 0x00c6, 0x7034, 0x2060, 0x2c04, 0x7822, 0x6004, 0x7826,
+ 0x6008, 0x782a, 0x781b, 0x0001, 0x2091, 0x4080, 0x7038, 0x8001,
+ 0x703a, 0xa005, 0x1130, 0x7033, 0xb592, 0x7037, 0xb592, 0x00ce,
+ 0x0048, 0xac80, 0x0004, 0xa0fa, 0xb5d2, 0x0210, 0x2001, 0xb592,
+ 0x7036, 0x00ce, 0x00fe, 0x012e, 0x00ee, 0x0005, 0x0026, 0x2001,
+ 0xb553, 0x2004, 0xd0c4, 0x0120, 0x2011, 0x8014, 0x080c, 0x3ecc,
+ 0x002e, 0x0005, 0x81ff, 0x1904, 0x2fcf, 0x0126, 0x2091, 0x8000,
+ 0x6030, 0xc08d, 0xc085, 0xc0ac, 0x6032, 0x080c, 0x5acf, 0x1178,
+ 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001,
+ 0xa085, 0x0001, 0x080c, 0x5b13, 0x080c, 0x5a07, 0x0010, 0x080c,
+ 0x4b1f, 0x012e, 0x0804, 0x2faa, 0x7824, 0x2008, 0xa18c, 0xfffd,
+ 0x1128, 0x61e0, 0xa10d, 0x61e2, 0x0804, 0x2faa, 0x0804, 0x2fd2,
+ 0x81ff, 0x1904, 0x2fcf, 0x6000, 0xa086, 0x0003, 0x1904, 0x2fcf,
+ 0x2001, 0xb553, 0x2004, 0xd0ac, 0x1904, 0x2fcf, 0x080c, 0x3e9a,
+ 0x0904, 0x2fd2, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1120,
+ 0x7828, 0xa005, 0x0904, 0x2faa, 0x00c6, 0x080c, 0x3e75, 0x00ce,
+ 0x0904, 0x2fcf, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x080c, 0x9e6b, 0x0904, 0x2fcf, 0x7007, 0x0003, 0x701b,
+ 0x3faa, 0x0005, 0x6830, 0xa086, 0x0100, 0x0904, 0x2fcf, 0x0804,
+ 0x2faa, 0x2001, 0xb500, 0x2004, 0xa086, 0x0003, 0x1904, 0x2fcf,
+ 0x7f24, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3e75, 0x0904,
+ 0x2fcf, 0x2009, 0x0000, 0x2031, 0x0000, 0x7023, 0x0000, 0x702f,
+ 0x0000, 0xad80, 0x0005, 0x7026, 0x20a0, 0x080c, 0x4fa9, 0x1904,
+ 0x4024, 0x6004, 0xa0c4, 0x00ff, 0xa8c6, 0x0006, 0x0130, 0xa0c4,
+ 0xff00, 0xa8c6, 0x0600, 0x1904, 0x4024, 0x2001, 0xb553, 0x2004,
+ 0xd0ac, 0x1128, 0x080c, 0x524a, 0x1110, 0xd79c, 0x05e8, 0xd794,
+ 0x1110, 0xd784, 0x0158, 0xac80, 0x0006, 0x2098, 0x3400, 0x20a9,
+ 0x0004, 0x53a3, 0x080c, 0x3ce7, 0xd794, 0x0148, 0xac80, 0x000a,
+ 0x2098, 0x3400, 0x20a9, 0x0004, 0x53a3, 0x080c, 0x3ce7, 0x21a2,
+ 0xd794, 0x01d8, 0xac80, 0x0000, 0x2098, 0x94a0, 0x20a9, 0x0002,
+ 0x53a3, 0xac80, 0x0003, 0x20a6, 0x94a0, 0xac80, 0x0004, 0x2098,
+ 0x3400, 0x20a9, 0x0002, 0x53a3, 0x080c, 0x3cd9, 0xac80, 0x0026,
+ 0x2098, 0x20a9, 0x0002, 0x53a3, 0x0008, 0x94a0, 0xd794, 0x0110,
+ 0xa6b0, 0x000b, 0xa6b0, 0x0005, 0x8108, 0x2001, 0xb535, 0x2004,
+ 0xd0ac, 0x0118, 0xa186, 0x0100, 0x0040, 0xd78c, 0x0120, 0xa186,
+ 0x0100, 0x0170, 0x0018, 0xa186, 0x007e, 0x0150, 0xd794, 0x0118,
+ 0xa686, 0x0020, 0x0010, 0xa686, 0x0028, 0x0150, 0x0804, 0x3fcd,
+ 0x86ff, 0x1120, 0x7120, 0x810b, 0x0804, 0x2faa, 0x702f, 0x0001,
+ 0x711e, 0x7020, 0xa600, 0x7022, 0x772a, 0x2061, 0xb5d2, 0x6007,
+ 0x0000, 0x6612, 0x7024, 0x600e, 0x6226, 0x632a, 0x642e, 0x6532,
+ 0x2c10, 0x080c, 0x1643, 0x7007, 0x0002, 0x701b, 0x4060, 0x0005,
+ 0x702c, 0xa005, 0x1170, 0x711c, 0x7024, 0x20a0, 0x7728, 0x2031,
+ 0x0000, 0x2061, 0xb5d2, 0x6224, 0x6328, 0x642c, 0x6530, 0x0804,
+ 0x3fcd, 0x7120, 0x810b, 0x0804, 0x2faa, 0x2029, 0x007e, 0x7924,
+ 0x7a28, 0x7b2c, 0x7c38, 0xa184, 0xff00, 0x8007, 0xa0e2, 0x0020,
+ 0x0a04, 0x2fd2, 0xa502, 0x0a04, 0x2fd2, 0xa184, 0x00ff, 0xa0e2,
+ 0x0020, 0x0a04, 0x2fd2, 0xa502, 0x0a04, 0x2fd2, 0xa284, 0xff00,
+ 0x8007, 0xa0e2, 0x0020, 0x0a04, 0x2fd2, 0xa502, 0x0a04, 0x2fd2,
+ 0xa284, 0x00ff, 0xa0e2, 0x0020, 0x0a04, 0x2fd2, 0xa502, 0x0a04,
+ 0x2fd2, 0xa384, 0xff00, 0x8007, 0xa0e2, 0x0020, 0x0a04, 0x2fd2,
+ 0xa502, 0x0a04, 0x2fd2, 0xa384, 0x00ff, 0xa0e2, 0x0020, 0x0a04,
+ 0x2fd2, 0xa502, 0x0a04, 0x2fd2, 0xa484, 0xff00, 0x8007, 0xa0e2,
+ 0x0020, 0x0a04, 0x2fd2, 0xa502, 0x0a04, 0x2fd2, 0xa484, 0x00ff,
+ 0xa0e2, 0x0020, 0x0a04, 0x2fd2, 0xa502, 0x0a04, 0x2fd2, 0x2061,
+ 0xb7b9, 0x6102, 0x6206, 0x630a, 0x640e, 0x0804, 0x2faa, 0x0006,
+ 0x2001, 0xb553, 0x2004, 0xd0cc, 0x000e, 0x0005, 0x0006, 0x2001,
+ 0xb572, 0x2004, 0xd0bc, 0x000e, 0x0005, 0x6168, 0x7a24, 0x6300,
+ 0x82ff, 0x1118, 0x7926, 0x0804, 0x2faa, 0x83ff, 0x1904, 0x2fd2,
+ 0x2001, 0xfff0, 0xa200, 0x1a04, 0x2fd2, 0x2019, 0xffff, 0x606c,
+ 0xa302, 0xa200, 0x0a04, 0x2fd2, 0x7926, 0x626a, 0x0804, 0x2faa,
+ 0x2001, 0xb500, 0x2004, 0xa086, 0x0003, 0x1904, 0x2fcf, 0x7c28,
+ 0x7d24, 0x7e38, 0x7f2c, 0x080c, 0x3e75, 0x0904, 0x2fcf, 0x2009,
+ 0x0000, 0x2019, 0x0000, 0x7023, 0x0000, 0x702f, 0x0000, 0xad80,
+ 0x0003, 0x7026, 0x20a0, 0xa1e0, 0xb635, 0x2c64, 0x8cff, 0x01b8,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x0130, 0x6004, 0xa084,
+ 0xff00, 0xa086, 0x0600, 0x1158, 0x6014, 0x20a2, 0x94a0, 0x6010,
+ 0x8007, 0xa105, 0x8007, 0x20a2, 0x94a0, 0xa398, 0x0002, 0x8108,
+ 0xa182, 0x00ff, 0x0120, 0xa386, 0x002a, 0x0148, 0x08e0, 0x83ff,
+ 0x1120, 0x7120, 0x810c, 0x0804, 0x2faa, 0x702f, 0x0001, 0x711e,
+ 0x7020, 0xa300, 0x7022, 0x2061, 0xb5d2, 0x6007, 0x0000, 0x6312,
+ 0x7024, 0x600e, 0x6426, 0x652a, 0x662e, 0x6732, 0x2c10, 0x080c,
+ 0x1643, 0x7007, 0x0002, 0x701b, 0x4156, 0x0005, 0x702c, 0xa005,
+ 0x1168, 0x711c, 0x7024, 0x20a0, 0x2019, 0x0000, 0x2061, 0xb5d2,
+ 0x6424, 0x6528, 0x662c, 0x6730, 0x0804, 0x4113, 0x7120, 0x810c,
+ 0x0804, 0x2faa, 0x81ff, 0x1904, 0x2fcf, 0x60d4, 0xd0ac, 0x1118,
+ 0xd09c, 0x0904, 0x2fcf, 0x080c, 0x3e75, 0x0904, 0x2fcf, 0x7924,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3eb6, 0x701b, 0x4181,
+ 0x0005, 0x00d6, 0xade8, 0x000d, 0x6828, 0xa0be, 0x7000, 0x0148,
+ 0xa0be, 0x7100, 0x0130, 0xa0be, 0x7200, 0x0118, 0x00de, 0x0804,
+ 0x2fd2, 0x6820, 0x6924, 0x080c, 0x281d, 0x1510, 0x080c, 0x4f4d,
+ 0x11f8, 0x7122, 0x6612, 0x6516, 0x6e18, 0x00c6, 0x080c, 0x3e75,
+ 0x01b8, 0x080c, 0x3e75, 0x01a0, 0x00ce, 0x00de, 0x6837, 0x0000,
+ 0x6838, 0xc0fd, 0x683a, 0x6823, 0x0000, 0x6804, 0x2068, 0x080c,
+ 0x9dbe, 0x0904, 0x2fcf, 0x7007, 0x0003, 0x701b, 0x41bb, 0x0005,
+ 0x00de, 0x0804, 0x2fcf, 0x7120, 0x080c, 0x2d97, 0x6820, 0xa086,
+ 0x8001, 0x0904, 0x2fcf, 0x2d00, 0x701e, 0x6804, 0xa080, 0x0002,
+ 0x0006, 0x20a9, 0x002a, 0x2098, 0x20a0, 0x080c, 0x4b8f, 0x000e,
+ 0xade8, 0x000d, 0x6a08, 0x6b0c, 0x6c10, 0x6d14, 0x2061, 0xb5d2,
+ 0x6007, 0x0000, 0x6e00, 0x6f28, 0xa7c6, 0x7000, 0x1108, 0x0018,
+ 0xa7c6, 0x7100, 0x1140, 0xa6c2, 0x0004, 0x0a04, 0x2fd2, 0x2009,
+ 0x0004, 0x0804, 0x3eb9, 0xa7c6, 0x7200, 0x1904, 0x2fd2, 0xa6c2,
+ 0x0054, 0x0a04, 0x2fd2, 0x600e, 0x6013, 0x002a, 0x6226, 0x632a,
+ 0x642e, 0x6532, 0x2c10, 0x080c, 0x1643, 0x7007, 0x0002, 0x701b,
+ 0x4202, 0x0005, 0x701c, 0x2068, 0x6804, 0xa080, 0x0001, 0x2004,
+ 0xa080, 0x0002, 0x0006, 0x20a9, 0x002a, 0x2098, 0x20a0, 0x080c,
+ 0x4b8f, 0x000e, 0x2009, 0x002a, 0x2061, 0xb5d2, 0x6224, 0x6328,
+ 0x642c, 0x6530, 0x0804, 0x3eb9, 0x81ff, 0x1904, 0x2fcf, 0x792c,
+ 0x2001, 0xb7a0, 0x2102, 0x080c, 0x3e8a, 0x0904, 0x2fd2, 0x080c,
+ 0x506f, 0x0904, 0x2fcf, 0x0126, 0x2091, 0x8000, 0x080c, 0x51a1,
+ 0x012e, 0x0804, 0x2faa, 0x7824, 0xd08c, 0x1118, 0xd084, 0x0904,
+ 0x3a46, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x00c6, 0x080c, 0x3e75,
+ 0x00ce, 0x1120, 0x2009, 0x0002, 0x0804, 0x2fcf, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x0128, 0xa08e, 0x0004, 0x0110, 0xa08e,
+ 0x0005, 0x15b8, 0x7824, 0xd08c, 0x0120, 0x6000, 0xc08c, 0x6002,
+ 0x0030, 0x2001, 0xb553, 0x2004, 0xd0b4, 0x0904, 0x3a82, 0x7824,
+ 0xa084, 0xff00, 0xa08e, 0x7e00, 0x0904, 0x3a82, 0xa08e, 0x7f00,
+ 0x0904, 0x3a82, 0xa08e, 0x8000, 0x0904, 0x3a82, 0x6000, 0xd08c,
+ 0x1904, 0x3a82, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x080c,
+ 0x9dda, 0x1120, 0x2009, 0x0003, 0x0804, 0x2fcf, 0x7007, 0x0003,
+ 0x701b, 0x4283, 0x0005, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x0804,
+ 0x3a82, 0x2009, 0xb531, 0x210c, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x2fcf, 0x2001, 0xb500, 0x2004, 0xa086, 0x0003, 0x0120,
+ 0x2009, 0x0007, 0x0804, 0x2fcf, 0x2001, 0xb553, 0x2004, 0xd0ac,
+ 0x0120, 0x2009, 0x0008, 0x0804, 0x2fcf, 0x609c, 0xd0a4, 0x1118,
+ 0xd0ac, 0x1904, 0x3a82, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x080c, 0x9e6b, 0x1120, 0x2009, 0x0003, 0x0804,
+ 0x2fcf, 0x7007, 0x0003, 0x701b, 0x42be, 0x0005, 0x6830, 0xa086,
+ 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x2fcf, 0x080c, 0x3e9a,
+ 0x0904, 0x2fd2, 0x0804, 0x4252, 0x81ff, 0x2009, 0x0001, 0x1904,
+ 0x2fcf, 0x6000, 0xa086, 0x0003, 0x2009, 0x0007, 0x1904, 0x2fcf,
+ 0x2001, 0xb553, 0x2004, 0xd0ac, 0x2009, 0x0008, 0x1904, 0x2fcf,
+ 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x2009, 0x0009, 0x1904, 0x2fcf, 0x00c6, 0x080c, 0x3e75,
+ 0x00ce, 0x2009, 0x0002, 0x0904, 0x2fcf, 0x6837, 0x0000, 0x6833,
+ 0x0000, 0x6838, 0xc0fd, 0x683a, 0x7928, 0xa194, 0xff00, 0xa18c,
+ 0x00ff, 0xa006, 0x82ff, 0x1128, 0xc0ed, 0x6952, 0x792c, 0x6956,
+ 0x0048, 0xa28e, 0x0100, 0x1904, 0x2fd2, 0xc0e5, 0x6853, 0x0000,
+ 0x6857, 0x0000, 0x683e, 0x080c, 0xa028, 0x2009, 0x0003, 0x0904,
+ 0x2fcf, 0x7007, 0x0003, 0x701b, 0x431e, 0x0005, 0x6830, 0xa086,
+ 0x0100, 0x2009, 0x0004, 0x0904, 0x2fcf, 0x0804, 0x2faa, 0x81ff,
+ 0x2009, 0x0001, 0x1904, 0x2fcf, 0x6000, 0xa086, 0x0003, 0x2009,
+ 0x0007, 0x1904, 0x2fcf, 0x080c, 0x3e9a, 0x0904, 0x2fd2, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x2009, 0x0009, 0x1904, 0x2fcf,
+ 0x00c6, 0x080c, 0x3e75, 0x00ce, 0x2009, 0x0002, 0x0904, 0x2fcf,
+ 0xad80, 0x000f, 0x2009, 0x0008, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x080c, 0x3eb6, 0x701b, 0x4355, 0x0005, 0x00d6, 0xade8, 0x000f,
+ 0x6800, 0xa086, 0x0500, 0x1140, 0x6804, 0xa005, 0x1128, 0x6808,
+ 0xa084, 0xff00, 0x1108, 0x0018, 0x00de, 0x1904, 0x2fd2, 0x00de,
+ 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x00c6,
+ 0x080c, 0x3e9a, 0x1118, 0x00ce, 0x0804, 0x2fd2, 0x080c, 0xa077,
+ 0x2009, 0x0003, 0x00ce, 0x0904, 0x2fcf, 0x7007, 0x0003, 0x701b,
+ 0x4382, 0x0005, 0x6830, 0xa086, 0x0100, 0x2009, 0x0004, 0x0904,
+ 0x2fcf, 0x0804, 0x2faa, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804,
+ 0x2fcf, 0x6000, 0xa086, 0x0003, 0x0120, 0x2009, 0x0007, 0x0804,
+ 0x2fcf, 0x7e24, 0x860f, 0xa18c, 0x00ff, 0xa6b4, 0x00ff, 0x080c,
+ 0x4fa9, 0x1904, 0x2fd2, 0xa186, 0x007f, 0x0150, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x0120, 0x2009, 0x0009, 0x0804, 0x2fcf,
+ 0x00c6, 0x080c, 0x3e75, 0x00ce, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x2fcf, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x2001, 0x0100,
+ 0x8007, 0x680a, 0x080c, 0x9df5, 0x1120, 0x2009, 0x0003, 0x0804,
+ 0x2fcf, 0x7007, 0x0003, 0x701b, 0x43ce, 0x0005, 0x6808, 0x8007,
+ 0xa086, 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x2fcf, 0x68b0,
+ 0x6836, 0x6810, 0x8007, 0xa084, 0x00ff, 0x800c, 0x6814, 0x8007,
+ 0xa084, 0x00ff, 0x8004, 0xa080, 0x0002, 0xa108, 0xad80, 0x0004,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3eb9, 0x080c, 0x3e75,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x2fcf, 0x7924, 0xa194, 0xff00,
+ 0xa18c, 0x00ff, 0x8217, 0x82ff, 0x0110, 0x0804, 0x2fd2, 0x2009,
+ 0x001a, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3eb6, 0x701b,
+ 0x440a, 0x0005, 0x2001, 0xb52a, 0x2003, 0x0001, 0xad80, 0x000d,
+ 0x2098, 0x20a9, 0x001a, 0x20a1, 0xb7c6, 0x53a3, 0x0804, 0x2faa,
+ 0x080c, 0x3e75, 0x1120, 0x2009, 0x0002, 0x0804, 0x2fcf, 0x7924,
+ 0xa194, 0xff00, 0xa18c, 0x00ff, 0x8217, 0x82ff, 0x0110, 0x0804,
+ 0x2fd2, 0x2099, 0xb7c6, 0x20a0, 0x20a9, 0x001a, 0x53a3, 0x2009,
+ 0x001a, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3eb9, 0x7824,
+ 0xa08a, 0x1000, 0x1a04, 0x2fd2, 0x0126, 0x2091, 0x8000, 0x8003,
+ 0x800b, 0x810b, 0xa108, 0x00c6, 0x2061, 0xb7f3, 0x6142, 0x00ce,
+ 0x012e, 0x0804, 0x2faa, 0x00c6, 0x080c, 0x5acf, 0x1188, 0x2001,
+ 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001, 0xa085,
+ 0x0001, 0x080c, 0x5b13, 0x080c, 0x5a07, 0x080c, 0x1515, 0x0038,
+ 0x2061, 0xb500, 0x6030, 0xc09d, 0x6032, 0x080c, 0x4b1f, 0x00ce,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x00c6, 0x2061, 0xb7f3, 0x7924,
+ 0x6152, 0x614e, 0x6057, 0x0000, 0x604b, 0x0009, 0x7838, 0x606a,
+ 0x783c, 0x6066, 0x7828, 0x6062, 0x782c, 0x605e, 0x2061, 0xb7a1,
+ 0x2001, 0xb808, 0x600e, 0x6013, 0x0001, 0x6017, 0x0002, 0x6007,
+ 0x0000, 0x6037, 0x0000, 0x00ce, 0x012e, 0x0804, 0x2faa, 0x0126,
+ 0x00c6, 0x00e6, 0x2061, 0x0100, 0x2071, 0xb500, 0x6044, 0xd0a4,
+ 0x11b0, 0xd084, 0x0118, 0x080c, 0x4606, 0x0068, 0xd08c, 0x0118,
+ 0x080c, 0x4527, 0x0040, 0xd094, 0x0118, 0x080c, 0x44f8, 0x0018,
+ 0xd09c, 0x0108, 0x0061, 0x00ee, 0x00ce, 0x012e, 0x0005, 0x0016,
+ 0x6128, 0xd19c, 0x1110, 0xc19d, 0x612a, 0x001e, 0x0ca0, 0x624c,
+ 0xa286, 0xf0f0, 0x1150, 0x6048, 0xa086, 0xf0f0, 0x0130, 0x624a,
+ 0x6043, 0x0090, 0x6043, 0x0010, 0x0490, 0xa294, 0xff00, 0xa296,
+ 0xf700, 0x0178, 0x7134, 0xd1a4, 0x1160, 0x6240, 0xa295, 0x0100,
+ 0x6242, 0xa294, 0x0010, 0x0128, 0x2009, 0x00f7, 0x080c, 0x4baf,
+ 0x00f0, 0x6040, 0xa084, 0x0010, 0xa085, 0x0140, 0x6042, 0x6043,
+ 0x0000, 0x707b, 0x0000, 0x7097, 0x0001, 0x70bb, 0x0000, 0x70d7,
+ 0x0000, 0x2009, 0xbcc0, 0x200b, 0x0000, 0x708b, 0x0000, 0x707f,
+ 0x000a, 0x2009, 0x000a, 0x2011, 0x4ad5, 0x080c, 0x6a22, 0x0005,
+ 0x0156, 0x2001, 0xb574, 0x2004, 0xd08c, 0x0110, 0x7053, 0xffff,
+ 0x707c, 0xa005, 0x1510, 0x2011, 0x4ad5, 0x080c, 0x699c, 0x6040,
+ 0xa094, 0x0010, 0xa285, 0x0020, 0x6042, 0x20a9, 0x00c8, 0x6044,
+ 0xd08c, 0x1168, 0x1f04, 0x450f, 0x6242, 0x708f, 0x0000, 0x6040,
+ 0xa094, 0x0010, 0xa285, 0x0080, 0x6042, 0x6242, 0x0030, 0x6242,
+ 0x708f, 0x0000, 0x7083, 0x0000, 0x0000, 0x015e, 0x0005, 0x7080,
+ 0xa08a, 0x0003, 0x1210, 0x0023, 0x0010, 0x080c, 0x1515, 0x0005,
+ 0x4533, 0x4583, 0x4605, 0x00f6, 0x7083, 0x0001, 0x20e1, 0xa000,
+ 0xe000, 0x20e1, 0x8700, 0x080c, 0x2470, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x2079, 0xbb00, 0x207b, 0x2200, 0x7807, 0x00ef, 0x780b,
+ 0x0000, 0x780f, 0x00ef, 0x7813, 0x0138, 0x7817, 0x0000, 0x781b,
+ 0x0000, 0x781f, 0x0000, 0x7823, 0xffff, 0x7827, 0xffff, 0x782b,
+ 0x0000, 0x782f, 0x0000, 0x2079, 0xbb0c, 0x207b, 0x1101, 0x7807,
+ 0x0000, 0x2099, 0xb505, 0x20a1, 0xbb0e, 0x20a9, 0x0004, 0x53a3,
+ 0x2079, 0xbb12, 0x207b, 0x0000, 0x7807, 0x0000, 0x2099, 0xbb00,
+ 0x20a1, 0x020b, 0x20a9, 0x0014, 0x53a6, 0x60c3, 0x000c, 0x600f,
+ 0x0000, 0x080c, 0x4b06, 0x00fe, 0x7087, 0x0000, 0x6043, 0x0008,
+ 0x6043, 0x0000, 0x0005, 0x00d6, 0x7084, 0x7087, 0x0000, 0xa025,
+ 0x0904, 0x45ed, 0x6020, 0xd0b4, 0x1904, 0x45eb, 0x7194, 0x81ff,
+ 0x0904, 0x45db, 0xa486, 0x000c, 0x1904, 0x45e6, 0xa480, 0x0018,
+ 0x8004, 0x20a8, 0x2011, 0xbb80, 0x2019, 0xbb00, 0x220c, 0x2304,
+ 0xa106, 0x11b8, 0x8210, 0x8318, 0x1f04, 0x459e, 0x6043, 0x0004,
+ 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043, 0x0006, 0x7083, 0x0002,
+ 0x708f, 0x0002, 0x2009, 0x07d0, 0x2011, 0x4adc, 0x080c, 0x6a22,
+ 0x0490, 0x2069, 0xbb80, 0x6930, 0xa18e, 0x1101, 0x1538, 0x6834,
+ 0xa005, 0x1520, 0x6900, 0xa18c, 0x00ff, 0x1118, 0x6804, 0xa005,
+ 0x0190, 0x2011, 0xbb8e, 0x2019, 0xb505, 0x20a9, 0x0004, 0x220c,
+ 0x2304, 0xa102, 0x0230, 0x1190, 0x8210, 0x8318, 0x1f04, 0x45cf,
+ 0x0068, 0x7097, 0x0000, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099,
+ 0xbb80, 0x20a1, 0x020b, 0x20a9, 0x0014, 0x53a6, 0x6043, 0x0008,
+ 0x6043, 0x0000, 0x0010, 0x00de, 0x0005, 0x6040, 0xa085, 0x0100,
+ 0x6042, 0x6020, 0xd0b4, 0x1db8, 0x60c3, 0x000c, 0x2011, 0xb7ea,
+ 0x2013, 0x0000, 0x7087, 0x0000, 0x20e1, 0x9080, 0x60a3, 0x0056,
+ 0x60a7, 0x9575, 0x080c, 0x7d71, 0x0c30, 0x0005, 0x708c, 0xa08a,
+ 0x001d, 0x1210, 0x0023, 0x0010, 0x080c, 0x1515, 0x0005, 0x4639,
+ 0x4648, 0x4670, 0x4689, 0x46ad, 0x46d5, 0x46f9, 0x472a, 0x474e,
+ 0x4776, 0x47ad, 0x47d5, 0x47f1, 0x4807, 0x4827, 0x483a, 0x4842,
+ 0x4872, 0x4896, 0x48be, 0x48e2, 0x4913, 0x4950, 0x497f, 0x499b,
+ 0x49da, 0x49fa, 0x4a13, 0x4a14, 0x00c6, 0x2061, 0xb500, 0x6003,
+ 0x0007, 0x2061, 0x0100, 0x6004, 0xa084, 0xfff9, 0x6006, 0x00ce,
+ 0x0005, 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043, 0x0002, 0x708f,
+ 0x0001, 0x2009, 0x07d0, 0x2011, 0x4adc, 0x080c, 0x6a22, 0x0005,
+ 0x00f6, 0x7084, 0xa086, 0x0014, 0x1508, 0x6043, 0x0000, 0x6020,
+ 0xd0b4, 0x11e0, 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1102, 0x11a0,
+ 0x7834, 0xa005, 0x1188, 0x7a38, 0xd2fc, 0x0128, 0x70b8, 0xa005,
+ 0x1110, 0x70bb, 0x0001, 0x2011, 0x4adc, 0x080c, 0x699c, 0x708f,
+ 0x0010, 0x080c, 0x4842, 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005,
+ 0x708f, 0x0003, 0x6043, 0x0004, 0x2011, 0x4adc, 0x080c, 0x699c,
+ 0x080c, 0x4b97, 0x20a3, 0x1102, 0x20a3, 0x0000, 0x20a9, 0x000a,
+ 0x20a3, 0x0000, 0x1f04, 0x4680, 0x60c3, 0x0014, 0x080c, 0x4b06,
+ 0x0005, 0x00f6, 0x7084, 0xa005, 0x01f0, 0x2011, 0x4adc, 0x080c,
+ 0x699c, 0xa086, 0x0014, 0x11a8, 0x2079, 0xbb80, 0x7a30, 0xa296,
+ 0x1102, 0x1178, 0x7834, 0xa005, 0x1160, 0x7a38, 0xd2fc, 0x0128,
+ 0x70b8, 0xa005, 0x1110, 0x70bb, 0x0001, 0x708f, 0x0004, 0x0029,
+ 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005, 0x708f, 0x0005, 0x080c,
+ 0x4b97, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430, 0x2011, 0xbb8e,
+ 0x080c, 0x4be8, 0x1160, 0x7078, 0xa005, 0x1148, 0x7150, 0xa186,
+ 0xffff, 0x0128, 0x080c, 0x4aa0, 0x0110, 0x080c, 0x4bc6, 0x20a9,
+ 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0014, 0x080c, 0x4b06, 0x0005, 0x00f6, 0x7084, 0xa005,
+ 0x01f0, 0x2011, 0x4adc, 0x080c, 0x699c, 0xa086, 0x0014, 0x11a8,
+ 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1103, 0x1178, 0x7834, 0xa005,
+ 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70b8, 0xa005, 0x1110, 0x70bb,
+ 0x0001, 0x708f, 0x0006, 0x0029, 0x0010, 0x080c, 0x4b1f, 0x00fe,
+ 0x0005, 0x708f, 0x0007, 0x080c, 0x4b97, 0x20a3, 0x1104, 0x20a3,
+ 0x0000, 0x3430, 0x2011, 0xbb8e, 0x080c, 0x4be8, 0x11a8, 0x7078,
+ 0xa005, 0x1190, 0x7158, 0xa186, 0xffff, 0x0170, 0xa180, 0x2dc4,
+ 0x200d, 0xa18c, 0xff00, 0x810f, 0x080c, 0x4aa0, 0x0128, 0x080c,
+ 0x40d6, 0x0110, 0x080c, 0x2867, 0x20a9, 0x0008, 0x2298, 0x26a0,
+ 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x080c,
+ 0x4b06, 0x0005, 0x00f6, 0x7084, 0xa005, 0x01f0, 0x2011, 0x4adc,
+ 0x080c, 0x699c, 0xa086, 0x0014, 0x11a8, 0x2079, 0xbb80, 0x7a30,
+ 0xa296, 0x1104, 0x1178, 0x7834, 0xa005, 0x1160, 0x7a38, 0xd2fc,
+ 0x0128, 0x70b8, 0xa005, 0x1110, 0x70bb, 0x0001, 0x708f, 0x0008,
+ 0x0029, 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005, 0x708f, 0x0009,
+ 0x080c, 0x4b97, 0x20a3, 0x1105, 0x20a3, 0x0100, 0x3430, 0x080c,
+ 0x4be8, 0x1150, 0x7078, 0xa005, 0x1138, 0x080c, 0x4a15, 0x1170,
+ 0xa085, 0x0001, 0x080c, 0x2867, 0x20a9, 0x0008, 0x2099, 0xbb8e,
+ 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x080c, 0x4b06, 0x0010, 0x080c, 0x462c, 0x0005, 0x00f6, 0x7084,
+ 0xa005, 0x0588, 0x2011, 0x4adc, 0x080c, 0x699c, 0xa086, 0x0014,
+ 0x1540, 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1105, 0x1510, 0x7834,
+ 0x2011, 0x0100, 0xa21e, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70b8,
+ 0xa005, 0x1110, 0x70bb, 0x0001, 0x708f, 0x000a, 0x00b1, 0x0098,
+ 0xa005, 0x1178, 0x7a38, 0xd2fc, 0x0128, 0x70b8, 0xa005, 0x1110,
+ 0x70bb, 0x0001, 0x708b, 0x0000, 0x708f, 0x000e, 0x080c, 0x4827,
+ 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005, 0x708f, 0x000b, 0x2011,
+ 0xbb0e, 0x22a0, 0x20a9, 0x0040, 0x2019, 0xffff, 0x43a4, 0x20a9,
+ 0x0002, 0x2009, 0x0000, 0x41a4, 0x080c, 0x4b97, 0x20a3, 0x1106,
+ 0x20a3, 0x0000, 0x080c, 0x4be8, 0x0118, 0x2013, 0x0000, 0x0020,
+ 0x7054, 0xa085, 0x0100, 0x2012, 0x2298, 0x20a9, 0x0042, 0x53a6,
+ 0x60c3, 0x0084, 0x080c, 0x4b06, 0x0005, 0x00f6, 0x7084, 0xa005,
+ 0x01b0, 0x2011, 0x4adc, 0x080c, 0x699c, 0xa086, 0x0084, 0x1168,
+ 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1106, 0x1138, 0x7834, 0xa005,
+ 0x1120, 0x708f, 0x000c, 0x0029, 0x0010, 0x080c, 0x4b1f, 0x00fe,
+ 0x0005, 0x708f, 0x000d, 0x080c, 0x4b97, 0x20a3, 0x1107, 0x20a3,
+ 0x0000, 0x2099, 0xbb8e, 0x20a9, 0x0040, 0x53a6, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0084, 0x080c, 0x4b06, 0x0005, 0x00f6,
+ 0x7084, 0xa005, 0x01d0, 0x2011, 0x4adc, 0x080c, 0x699c, 0xa086,
+ 0x0084, 0x1188, 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1107, 0x1158,
+ 0x7834, 0xa005, 0x1140, 0x708b, 0x0001, 0x080c, 0x4b89, 0x708f,
+ 0x000e, 0x0029, 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005, 0x708f,
+ 0x000f, 0x7087, 0x0000, 0x608b, 0xbc85, 0x608f, 0xb5b5, 0x6043,
+ 0x0005, 0x6043, 0x0004, 0x2009, 0x07d0, 0x2011, 0x4adc, 0x080c,
+ 0x6990, 0x0005, 0x7084, 0xa005, 0x0120, 0x2011, 0x4adc, 0x080c,
+ 0x699c, 0x0005, 0x708f, 0x0011, 0x080c, 0x4be8, 0x11a0, 0x7170,
+ 0x81ff, 0x0188, 0x2009, 0x0000, 0x7074, 0xa084, 0x00ff, 0x080c,
+ 0x281d, 0xa186, 0x007e, 0x0138, 0xa186, 0x0080, 0x0120, 0x2011,
+ 0xbb8e, 0x080c, 0x4aa0, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099,
+ 0xbb80, 0x20a1, 0x020b, 0x7484, 0xa480, 0x0018, 0xa080, 0x0007,
+ 0xa084, 0x03f8, 0x8004, 0x20a8, 0x53a6, 0x60c3, 0x0014, 0x080c,
+ 0x4b06, 0x0005, 0x00f6, 0x7084, 0xa005, 0x01f0, 0x2011, 0x4adc,
+ 0x080c, 0x699c, 0xa086, 0x0014, 0x11a8, 0x2079, 0xbb80, 0x7a30,
+ 0xa296, 0x1103, 0x1178, 0x7834, 0xa005, 0x1160, 0x7a38, 0xd2fc,
+ 0x0128, 0x70b8, 0xa005, 0x1110, 0x70bb, 0x0001, 0x708f, 0x0012,
+ 0x0029, 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005, 0x708f, 0x0013,
+ 0x080c, 0x4ba3, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430, 0x2011,
+ 0xbb8e, 0x080c, 0x4be8, 0x1160, 0x7078, 0xa005, 0x1148, 0x7150,
+ 0xa186, 0xffff, 0x0128, 0x080c, 0x4aa0, 0x0110, 0x080c, 0x4bc6,
+ 0x20a9, 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x0014, 0x080c, 0x4b06, 0x0005, 0x00f6, 0x7084,
+ 0xa005, 0x01f0, 0x2011, 0x4adc, 0x080c, 0x699c, 0xa086, 0x0014,
+ 0x11a8, 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1104, 0x1178, 0x7834,
+ 0xa005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70b8, 0xa005, 0x1110,
+ 0x70bb, 0x0001, 0x708f, 0x0014, 0x0029, 0x0010, 0x080c, 0x4b1f,
+ 0x00fe, 0x0005, 0x708f, 0x0015, 0x080c, 0x4ba3, 0x20a3, 0x1104,
+ 0x20a3, 0x0000, 0x3430, 0x2011, 0xbb8e, 0x080c, 0x4be8, 0x11a8,
+ 0x7078, 0xa005, 0x1190, 0x7158, 0xa186, 0xffff, 0x0170, 0xa180,
+ 0x2dc4, 0x200d, 0xa18c, 0xff00, 0x810f, 0x080c, 0x4aa0, 0x0128,
+ 0x080c, 0x40d6, 0x0110, 0x080c, 0x2867, 0x20a9, 0x0008, 0x2298,
+ 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x080c, 0x4b06, 0x0005, 0x00f6, 0x7084, 0xa005, 0x05b8, 0x2011,
+ 0x4adc, 0x080c, 0x699c, 0xa086, 0x0014, 0x1570, 0x2079, 0xbb80,
+ 0x7a30, 0xa296, 0x1105, 0x1540, 0x7834, 0x2011, 0x0100, 0xa21e,
+ 0x1148, 0x7a38, 0xd2fc, 0x0128, 0x70b8, 0xa005, 0x1110, 0x70bb,
+ 0x0001, 0x0060, 0xa005, 0x11c0, 0x7a38, 0xd2fc, 0x0128, 0x70b8,
+ 0xa005, 0x1110, 0x70bb, 0x0001, 0x708b, 0x0000, 0x7a38, 0xd2f4,
+ 0x0138, 0x2001, 0xb574, 0x2004, 0xd0a4, 0x1110, 0x70d7, 0x0008,
+ 0x708f, 0x0016, 0x0029, 0x0010, 0x080c, 0x4b1f, 0x00fe, 0x0005,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xbb80, 0x20a1, 0x020b,
+ 0x20a9, 0x000e, 0x53a6, 0x3430, 0x2011, 0xbb8e, 0x708f, 0x0017,
+ 0x080c, 0x4be8, 0x1150, 0x7078, 0xa005, 0x1138, 0x080c, 0x4a15,
+ 0x1170, 0xa085, 0x0001, 0x080c, 0x2867, 0x20a9, 0x0008, 0x2099,
+ 0xbb8e, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0014, 0x080c, 0x4b06, 0x0010, 0x080c, 0x462c, 0x0005, 0x00f6,
+ 0x7084, 0xa005, 0x01b0, 0x2011, 0x4adc, 0x080c, 0x699c, 0xa086,
+ 0x0084, 0x1168, 0x2079, 0xbb80, 0x7a30, 0xa296, 0x1106, 0x1138,
+ 0x7834, 0xa005, 0x1120, 0x708f, 0x0018, 0x0029, 0x0010, 0x080c,
+ 0x4b1f, 0x00fe, 0x0005, 0x708f, 0x0019, 0x080c, 0x4ba3, 0x20a3,
+ 0x1106, 0x20a3, 0x0000, 0x3430, 0x2099, 0xbb8e, 0x2039, 0xbb0e,
+ 0x27a0, 0x20a9, 0x0040, 0x53a3, 0x080c, 0x4be8, 0x11e8, 0x2728,
+ 0x2514, 0x8207, 0xa084, 0x00ff, 0x8000, 0x2018, 0xa294, 0x00ff,
+ 0x8007, 0xa205, 0x202a, 0x7054, 0x2310, 0x8214, 0xa2a0, 0xbb0e,
+ 0x2414, 0xa38c, 0x0001, 0x0118, 0xa294, 0xff00, 0x0018, 0xa294,
+ 0x00ff, 0x8007, 0xa215, 0x2222, 0x2798, 0x26a0, 0x20a9, 0x0040,
+ 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0084, 0x080c,
+ 0x4b06, 0x0005, 0x00f6, 0x7084, 0xa005, 0x01d0, 0x2011, 0x4adc,
+ 0x080c, 0x699c, 0xa086, 0x0084, 0x1188, 0x2079, 0xbb80, 0x7a30,
+ 0xa296, 0x1107, 0x1158, 0x7834, 0xa005, 0x1140, 0x708b, 0x0001,
+ 0x080c, 0x4b89, 0x708f, 0x001a, 0x0029, 0x0010, 0x080c, 0x4b1f,
+ 0x00fe, 0x0005, 0x708f, 0x001b, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x2099, 0xbb80, 0x20a1, 0x020b, 0x7484, 0xa480, 0x0018, 0xa080,
+ 0x0007, 0xa084, 0x03f8, 0x8004, 0x20a8, 0x53a6, 0x60c3, 0x0084,
+ 0x080c, 0x4b06, 0x0005, 0x0005, 0x0005, 0x0086, 0x0096, 0x2029,
+ 0xb553, 0x252c, 0x20a9, 0x0008, 0x2041, 0xbb0e, 0x28a0, 0x2099,
+ 0xbb8e, 0x53a3, 0x20a9, 0x0008, 0x2011, 0x0007, 0xd5d4, 0x0110,
+ 0x2011, 0x0000, 0x2800, 0xa200, 0x200c, 0xa1a6, 0xffff, 0x1148,
+ 0xd5d4, 0x0110, 0x8210, 0x0008, 0x8211, 0x1f04, 0x4a2a, 0x0804,
+ 0x4a98, 0x82ff, 0x1160, 0xd5d4, 0x0120, 0xa1a6, 0x3fff, 0x0d90,
+ 0x0020, 0xa1a6, 0x3fff, 0x0904, 0x4a98, 0xa18d, 0xc000, 0x20a9,
+ 0x0010, 0x2019, 0x0001, 0xd5d4, 0x0110, 0x2019, 0x0010, 0x2120,
+ 0xd5d4, 0x0110, 0x8423, 0x0008, 0x8424, 0x1240, 0xd5d4, 0x0110,
+ 0x8319, 0x0008, 0x8318, 0x1f04, 0x4a50, 0x04d0, 0x23a8, 0x2021,
+ 0x0001, 0x8426, 0x8425, 0x1f04, 0x4a62, 0x2328, 0x8529, 0xa2be,
+ 0x0007, 0x0158, 0x0006, 0x2039, 0x0007, 0x2200, 0xa73a, 0x000e,
+ 0x27a8, 0xa5a8, 0x0010, 0x1f04, 0x4a71, 0x7552, 0xa5c8, 0x2dc4,
+ 0x292d, 0xa5ac, 0x00ff, 0x7576, 0x6532, 0x6536, 0x0016, 0x2508,
+ 0x080c, 0x2847, 0x001e, 0x60e7, 0x0000, 0x65ea, 0x2018, 0x2304,
+ 0xa405, 0x201a, 0x707b, 0x0001, 0x26a0, 0x2898, 0x20a9, 0x0008,
+ 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0xa085, 0x0001, 0x0028,
+ 0xa006, 0x0018, 0xa006, 0x080c, 0x1515, 0x009e, 0x008e, 0x0005,
+ 0x2118, 0x2021, 0x0000, 0x2001, 0x0007, 0xa39a, 0x0010, 0x0218,
+ 0x8420, 0x8001, 0x0cd0, 0x2118, 0x84ff, 0x0120, 0xa39a, 0x0010,
+ 0x8421, 0x1de0, 0x2021, 0x0001, 0x83ff, 0x0118, 0x8423, 0x8319,
+ 0x1de8, 0xa238, 0x2704, 0xa42c, 0x11b8, 0xa405, 0x203a, 0x7152,
+ 0xa1a0, 0x2dc4, 0x242d, 0xa5ac, 0x00ff, 0x7576, 0x6532, 0x6536,
+ 0x0016, 0x2508, 0x080c, 0x2847, 0x001e, 0x60e7, 0x0000, 0x65ea,
+ 0x707b, 0x0001, 0xa084, 0x0000, 0x0005, 0x00e6, 0x2071, 0xb500,
+ 0x707f, 0x0000, 0x00ee, 0x0005, 0x00e6, 0x00f6, 0x2079, 0x0100,
+ 0x2071, 0x0140, 0x080c, 0x7d7a, 0x7004, 0xa084, 0x4000, 0x0120,
+ 0x7003, 0x1000, 0x7003, 0x0000, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0xb523, 0x2073, 0x0000, 0x7840, 0x0026, 0x0016, 0x2009, 0x00f7,
+ 0x080c, 0x4baf, 0x001e, 0xa094, 0x0010, 0xa285, 0x0080, 0x7842,
+ 0x7a42, 0x002e, 0x012e, 0x00fe, 0x00ee, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x2011, 0xb7ea, 0x2013, 0x0000, 0x7087, 0x0000, 0x012e,
+ 0x20e1, 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0x7d71,
+ 0x2009, 0x07d0, 0x2011, 0x4adc, 0x080c, 0x6a22, 0x0005, 0x0016,
+ 0x0026, 0x00c6, 0x0126, 0x2091, 0x8000, 0x2011, 0x0003, 0x080c,
+ 0x8075, 0x2011, 0x0002, 0x080c, 0x807f, 0x080c, 0x7f59, 0x0036,
+ 0x2019, 0x0000, 0x080c, 0x7fe4, 0x003e, 0x2009, 0x00f7, 0x080c,
+ 0x4baf, 0x2061, 0xb7f3, 0x601b, 0x0000, 0x601f, 0x0000, 0x2061,
+ 0xb500, 0x6003, 0x0001, 0x2061, 0x0100, 0x6043, 0x0090, 0x6043,
+ 0x0010, 0x2009, 0x002d, 0x2011, 0x4b54, 0x080c, 0x6990, 0x012e,
+ 0x00ce, 0x002e, 0x001e, 0x0005, 0x00e6, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x0100, 0x080c, 0x7d7a, 0x2071, 0x0140, 0x7004,
+ 0xa084, 0x4000, 0x0120, 0x7003, 0x1000, 0x7003, 0x0000, 0x080c,
+ 0x5ad7, 0x01a8, 0x080c, 0x5af5, 0x1190, 0x2001, 0xb79e, 0x2003,
+ 0xaaaa, 0x0016, 0x080c, 0x28eb, 0x2001, 0xb78f, 0x2102, 0x001e,
+ 0x2001, 0xb79f, 0x2003, 0x0000, 0x080c, 0x5a07, 0x0030, 0x2001,
+ 0x0001, 0x080c, 0x27c3, 0x080c, 0x4b1f, 0x012e, 0x000e, 0x00ee,
+ 0x0005, 0x20a9, 0x0040, 0x20a1, 0xbcc0, 0x2099, 0xbb8e, 0x3304,
+ 0x8007, 0x20a2, 0x9398, 0x94a0, 0x1f04, 0x4b8f, 0x0005, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x2099, 0xbb00, 0x20a1, 0x020b, 0x20a9,
+ 0x000c, 0x53a6, 0x0005, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099,
+ 0xbb80, 0x20a1, 0x020b, 0x20a9, 0x000c, 0x53a6, 0x0005, 0x00c6,
+ 0x0006, 0x2061, 0x0100, 0x810f, 0x2001, 0xb531, 0x2004, 0xa005,
+ 0x1138, 0x2001, 0xb515, 0x2004, 0xa084, 0x00ff, 0xa105, 0x0010,
+ 0xa185, 0x00f7, 0x604a, 0x000e, 0x00ce, 0x0005, 0x0016, 0x0046,
+ 0x2001, 0xb553, 0x2004, 0xd0a4, 0x0158, 0xa006, 0x2020, 0x2009,
+ 0x002a, 0x080c, 0xb0e8, 0x2001, 0xb50c, 0x200c, 0xc195, 0x2102,
+ 0x2019, 0x002a, 0x2009, 0x0000, 0x080c, 0x2c6f, 0x004e, 0x001e,
+ 0x0005, 0x080c, 0x4b1f, 0x708f, 0x0000, 0x7087, 0x0000, 0x0005,
+ 0x0006, 0x2001, 0xb50c, 0x2004, 0xd09c, 0x0100, 0x000e, 0x0005,
+ 0x0006, 0x0016, 0x0126, 0x2091, 0x8000, 0x2001, 0x0101, 0x200c,
+ 0xa18d, 0x0006, 0x2102, 0x012e, 0x001e, 0x000e, 0x0005, 0x0156,
+ 0x20a9, 0x00ff, 0x2009, 0xb635, 0xa006, 0x200a, 0x8108, 0x1f04,
+ 0x4c05, 0x015e, 0x0005, 0x00d6, 0x0036, 0x0156, 0x0136, 0x0146,
+ 0x2069, 0xb552, 0xa006, 0x6002, 0x6007, 0x0707, 0x600a, 0x600e,
+ 0x6012, 0xa198, 0x2dc4, 0x231d, 0xa39c, 0x00ff, 0x6316, 0x20a9,
+ 0x0004, 0xac98, 0x0006, 0x23a0, 0x40a4, 0x20a9, 0x0004, 0xac98,
+ 0x000a, 0x23a0, 0x40a4, 0x603e, 0x6042, 0x604e, 0x6052, 0x6056,
+ 0x605a, 0x605e, 0x6062, 0x6066, 0x606a, 0x606e, 0x6072, 0x6076,
+ 0x607a, 0x607e, 0x6082, 0x6086, 0x608a, 0x608e, 0x6092, 0x6096,
+ 0x609a, 0x609e, 0x60ae, 0x61a2, 0x00d6, 0x60a4, 0xa06d, 0x0110,
+ 0x080c, 0x160f, 0x60a7, 0x0000, 0x60a8, 0xa06d, 0x0110, 0x080c,
+ 0x160f, 0x60ab, 0x0000, 0x00de, 0xa006, 0x604a, 0x6810, 0x603a,
+ 0x680c, 0x6046, 0x6814, 0xa084, 0x00ff, 0x6042, 0x014e, 0x013e,
+ 0x015e, 0x003e, 0x00de, 0x0005, 0x0126, 0x2091, 0x8000, 0x6944,
+ 0x6e48, 0xa684, 0x3fff, 0xa082, 0x4000, 0x1a04, 0x4d1a, 0xa18c,
+ 0xff00, 0x810f, 0xa182, 0x00ff, 0x1a04, 0x4d1f, 0x2001, 0xb50c,
+ 0x2004, 0xa084, 0x0003, 0x01c0, 0x2001, 0xb50c, 0x2004, 0xd084,
+ 0x1904, 0x4d02, 0xa188, 0xb635, 0x2104, 0xa065, 0x0904, 0x4d02,
+ 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006, 0x1904, 0x4d02, 0x6000,
+ 0xd0c4, 0x0904, 0x4d02, 0x0068, 0xa188, 0xb635, 0x2104, 0xa065,
+ 0x0904, 0x4ce6, 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006, 0x1904,
+ 0x4ceb, 0x60a4, 0xa00d, 0x0118, 0x080c, 0x51d4, 0x05d0, 0x60a8,
+ 0xa00d, 0x0188, 0x080c, 0x521f, 0x1170, 0x694c, 0xd1fc, 0x1118,
+ 0x080c, 0x4ede, 0x0448, 0x080c, 0x4e8d, 0x694c, 0xd1ec, 0x1520,
+ 0x080c, 0x50c6, 0x0408, 0x694c, 0xa184, 0xa000, 0x0178, 0xd1ec,
+ 0x0140, 0xd1fc, 0x0118, 0x080c, 0x50d5, 0x0028, 0x080c, 0x50d5,
+ 0x0028, 0xd1fc, 0x0118, 0x080c, 0x4e8d, 0x0070, 0x6050, 0xa00d,
+ 0x0130, 0x2d00, 0x200a, 0x6803, 0x0000, 0x6052, 0x0028, 0x2d00,
+ 0x6052, 0x604e, 0x6803, 0x0000, 0x080c, 0x6caa, 0xa006, 0x012e,
+ 0x0005, 0x2001, 0x0005, 0x2009, 0x0000, 0x04e8, 0x2001, 0x0028,
+ 0x2009, 0x0000, 0x04c0, 0xa082, 0x0006, 0x12a0, 0x2001, 0xb535,
+ 0x2004, 0xd0ac, 0x1160, 0x60a0, 0xd0bc, 0x1148, 0x6100, 0xd1fc,
+ 0x0904, 0x4ca1, 0x2001, 0x0029, 0x2009, 0x1000, 0x0420, 0x2001,
+ 0x0028, 0x00a8, 0x2009, 0xb50c, 0x210c, 0xd18c, 0x0118, 0x2001,
+ 0x0004, 0x0068, 0xd184, 0x0118, 0x2001, 0x0004, 0x0040, 0x2001,
+ 0x0029, 0x6100, 0xd1fc, 0x0118, 0x2009, 0x1000, 0x0060, 0x2009,
+ 0x0000, 0x0048, 0x2001, 0x0029, 0x2009, 0x0000, 0x0020, 0x2001,
+ 0x0029, 0x2009, 0x0000, 0xa005, 0x012e, 0x0005, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0x6844, 0x8007, 0xa084, 0x00ff, 0x2008, 0xa182,
+ 0x00ff, 0x1a04, 0x4d79, 0xa188, 0xb635, 0x2104, 0xa065, 0x01c0,
+ 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006, 0x11a8, 0x2c70, 0x080c,
+ 0x85c7, 0x05e8, 0x2e00, 0x601a, 0x2d00, 0x6012, 0x600b, 0xffff,
+ 0x601f, 0x000a, 0x2009, 0x0003, 0x080c, 0x864c, 0xa006, 0x0460,
+ 0x2001, 0x0028, 0x0440, 0xa082, 0x0006, 0x1298, 0x2001, 0xb535,
+ 0x2004, 0xd0ac, 0x1158, 0x60a0, 0xd0bc, 0x1140, 0x6100, 0xd1fc,
+ 0x09e8, 0x2001, 0x0029, 0x2009, 0x1000, 0x00a8, 0x2001, 0x0028,
+ 0x0090, 0x2009, 0xb50c, 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004,
+ 0x0050, 0xd184, 0x0118, 0x2001, 0x0004, 0x0028, 0x2001, 0x0029,
+ 0x0010, 0x2001, 0x0029, 0xa005, 0x012e, 0x00ee, 0x0005, 0x2001,
+ 0x002c, 0x0cc8, 0x00f6, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2011,
+ 0x0000, 0x2079, 0xb500, 0x6944, 0xa18c, 0xff00, 0x810f, 0xa182,
+ 0x00ff, 0x1a04, 0x4e44, 0x080c, 0x4fa9, 0x11a0, 0x6004, 0xa084,
+ 0x00ff, 0xa082, 0x0006, 0x1270, 0x6864, 0xa0c6, 0x006f, 0x0150,
+ 0x2001, 0xb535, 0x2004, 0xd0ac, 0x1904, 0x4e2d, 0x60a0, 0xd0bc,
+ 0x1904, 0x4e2d, 0x6864, 0xa0c6, 0x006f, 0x0118, 0x2008, 0x0804,
+ 0x4df6, 0x6968, 0x2140, 0xa18c, 0xff00, 0x810f, 0x78d4, 0xd0ac,
+ 0x1118, 0xa182, 0x0080, 0x06d0, 0xa182, 0x00ff, 0x16b8, 0x6a70,
+ 0x6b6c, 0x7870, 0xa306, 0x1160, 0x7874, 0xa24e, 0x1118, 0x2208,
+ 0x2310, 0x0460, 0xa9cc, 0xff00, 0x1118, 0x2208, 0x2310, 0x0430,
+ 0x080c, 0x3dc5, 0x2c70, 0x0550, 0x2009, 0x0000, 0x2011, 0x0000,
+ 0xa0c6, 0x4000, 0x1160, 0x0006, 0x2e60, 0x080c, 0x524a, 0x1108,
+ 0xc185, 0x7000, 0xd0bc, 0x0108, 0xc18d, 0x000e, 0x0088, 0xa0c6,
+ 0x4007, 0x1110, 0x2408, 0x0060, 0xa0c6, 0x4008, 0x1118, 0x2708,
+ 0x2610, 0x0030, 0xa0c6, 0x4009, 0x1108, 0x0010, 0x2001, 0x4006,
+ 0x6866, 0x696a, 0x6a6e, 0x2001, 0x0030, 0x0450, 0x080c, 0x85c7,
+ 0x1138, 0x2001, 0x4005, 0x2009, 0x0003, 0x2011, 0x0000, 0x0c80,
+ 0x2e00, 0x601a, 0x080c, 0xa027, 0x2d00, 0x6012, 0x601f, 0x0001,
+ 0x6838, 0xd88c, 0x0108, 0xc0f5, 0x683a, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x2c9c, 0x012e, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x2001,
+ 0x0002, 0x080c, 0x4efd, 0x2009, 0x0002, 0x080c, 0x864c, 0xa006,
+ 0xa005, 0x012e, 0x00ee, 0x00fe, 0x0005, 0x2001, 0x0028, 0x2009,
+ 0x0000, 0x0cb0, 0x2009, 0xb50c, 0x210c, 0xd18c, 0x0118, 0x2001,
+ 0x0004, 0x0038, 0xd184, 0x0118, 0x2001, 0x0004, 0x0010, 0x2001,
+ 0x0029, 0x2009, 0x0000, 0x0c20, 0x2001, 0x0029, 0x2009, 0x0000,
+ 0x08f8, 0x6944, 0x6e48, 0xa684, 0x3fff, 0xa082, 0x4000, 0x16b8,
+ 0xa18c, 0xff00, 0x810f, 0xa182, 0x00ff, 0x12e0, 0xa188, 0xb635,
+ 0x2104, 0xa065, 0x01b8, 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006,
+ 0x11b0, 0x684c, 0xd0ec, 0x0120, 0x080c, 0x50d5, 0x0431, 0x0030,
+ 0x0421, 0x684c, 0xd0fc, 0x0110, 0x080c, 0x50c6, 0x080c, 0x5113,
+ 0xa006, 0x00c8, 0x2001, 0x0028, 0x2009, 0x0000, 0x00a0, 0xa082,
+ 0x0006, 0x1240, 0x6100, 0xd1fc, 0x0d20, 0x2001, 0x0029, 0x2009,
+ 0x1000, 0x0048, 0x2001, 0x0029, 0x2009, 0x0000, 0x0020, 0x2001,
+ 0x0029, 0x2009, 0x0000, 0xa005, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x6050, 0xa00d, 0x0138, 0x2d00, 0x200a, 0x6803, 0x0000, 0x6052,
+ 0x012e, 0x0005, 0x2d00, 0x6052, 0x604e, 0x6803, 0x0000, 0x0cc0,
+ 0x0126, 0x2091, 0x8000, 0x604c, 0xa005, 0x0170, 0x00e6, 0x2071,
+ 0xb7e0, 0x7004, 0xa086, 0x0002, 0x0168, 0x00ee, 0x604c, 0x6802,
+ 0x2d00, 0x604e, 0x012e, 0x0005, 0x2d00, 0x6052, 0x604e, 0x6803,
+ 0x0000, 0x0cc0, 0x701c, 0xac06, 0x1d80, 0x604c, 0x2070, 0x7000,
+ 0x6802, 0x2d00, 0x7002, 0x00ee, 0x012e, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x604c, 0xa06d, 0x0130, 0x6800, 0xa005, 0x1108, 0x6052,
+ 0x604e, 0xad05, 0x012e, 0x0005, 0x604c, 0xa06d, 0x0130, 0x6800,
+ 0xa005, 0x1108, 0x6052, 0x604e, 0xad05, 0x0005, 0x6803, 0x0000,
+ 0x6084, 0xa00d, 0x0120, 0x2d00, 0x200a, 0x6086, 0x0005, 0x2d00,
+ 0x6086, 0x6082, 0x0cd8, 0x0126, 0x00c6, 0x0026, 0x2091, 0x8000,
+ 0x6218, 0x2260, 0x6200, 0xa005, 0x0110, 0xc285, 0x0008, 0xc284,
+ 0x6202, 0x002e, 0x00ce, 0x012e, 0x0005, 0x0126, 0x00c6, 0x2091,
+ 0x8000, 0x6218, 0x2260, 0x6204, 0x0006, 0xa086, 0x0006, 0x1180,
+ 0x609c, 0xd0ac, 0x0168, 0x2001, 0xb553, 0x2004, 0xd0a4, 0x0140,
+ 0xa284, 0xff00, 0x8007, 0xa086, 0x0007, 0x1110, 0x2011, 0x0600,
+ 0x000e, 0xa294, 0xff00, 0xa215, 0x6206, 0x0006, 0xa086, 0x0006,
+ 0x1128, 0x6290, 0x82ff, 0x1110, 0x080c, 0x1515, 0x000e, 0x00ce,
+ 0x012e, 0x0005, 0x0126, 0x00c6, 0x2091, 0x8000, 0x6218, 0x2260,
+ 0x6204, 0x0006, 0xa086, 0x0006, 0x1178, 0x609c, 0xd0a4, 0x0160,
+ 0x2001, 0xb553, 0x2004, 0xd0ac, 0x1138, 0xa284, 0x00ff, 0xa086,
+ 0x0007, 0x1110, 0x2011, 0x0006, 0x000e, 0xa294, 0x00ff, 0x8007,
+ 0xa215, 0x6206, 0x00ce, 0x012e, 0x0005, 0x0026, 0xa182, 0x00ff,
+ 0x0218, 0xa085, 0x0001, 0x00b0, 0xa190, 0xb635, 0x2204, 0xa065,
+ 0x1180, 0x0016, 0x00d6, 0x080c, 0x15df, 0x2d60, 0x00de, 0x001e,
+ 0x0d80, 0x2c00, 0x2012, 0x60a7, 0x0000, 0x60ab, 0x0000, 0x080c,
+ 0x4c0b, 0xa006, 0x002e, 0x0005, 0x0126, 0x2091, 0x8000, 0x0026,
+ 0xa182, 0x00ff, 0x0218, 0xa085, 0x0001, 0x0480, 0x00d6, 0xa190,
+ 0xb635, 0x2204, 0xa06d, 0x0540, 0x2013, 0x0000, 0x00d6, 0x00c6,
+ 0x2d60, 0x60a4, 0xa06d, 0x0110, 0x080c, 0x160f, 0x60a8, 0xa06d,
+ 0x0110, 0x080c, 0x160f, 0x00ce, 0x00de, 0x00d6, 0x00c6, 0x68ac,
+ 0x2060, 0x8cff, 0x0168, 0x600c, 0x0006, 0x6010, 0x2068, 0x080c,
+ 0x9c5a, 0x0110, 0x080c, 0x161f, 0x080c, 0x861d, 0x00ce, 0x0c88,
+ 0x00ce, 0x00de, 0x080c, 0x160f, 0x00de, 0xa006, 0x002e, 0x012e,
+ 0x0005, 0x0016, 0xa182, 0x00ff, 0x0218, 0xa085, 0x0001, 0x0030,
+ 0xa188, 0xb635, 0x2104, 0xa065, 0x0dc0, 0xa006, 0x001e, 0x0005,
+ 0x00d6, 0x0156, 0x0136, 0x0146, 0x600b, 0x0000, 0x600f, 0x0000,
+ 0x6000, 0xc08c, 0x6002, 0x080c, 0x5acf, 0x1558, 0x60a0, 0xa086,
+ 0x007e, 0x2069, 0xbb90, 0x0130, 0x2001, 0xb535, 0x2004, 0xd0ac,
+ 0x1500, 0x0098, 0x2d04, 0xd0e4, 0x01e0, 0x00d6, 0x2069, 0xbb8e,
+ 0x00c6, 0x2061, 0xb7b2, 0x6810, 0x2062, 0x6814, 0x6006, 0x6818,
+ 0x600a, 0x681c, 0x600e, 0x00ce, 0x00de, 0x8d69, 0x2d04, 0x2069,
+ 0x0140, 0xa005, 0x1110, 0x2001, 0x0001, 0x6886, 0x2069, 0xb500,
+ 0x68a6, 0x2069, 0xbb8e, 0x6808, 0x605e, 0x6810, 0x6062, 0x6138,
+ 0xa10a, 0x0208, 0x603a, 0x6814, 0x6066, 0x2099, 0xbb96, 0xac88,
+ 0x000a, 0x21a0, 0x20a9, 0x0004, 0x53a3, 0x2099, 0xbb9a, 0xac88,
+ 0x0006, 0x21a0, 0x20a9, 0x0004, 0x53a3, 0x2069, 0xbbae, 0x6808,
+ 0x606a, 0x690c, 0x616e, 0x6810, 0x6072, 0x6818, 0x6076, 0x60a0,
+ 0xa086, 0x007e, 0x1120, 0x2069, 0xbb8e, 0x690c, 0x616e, 0xa182,
+ 0x0211, 0x1218, 0x2009, 0x0008, 0x0400, 0xa182, 0x0259, 0x1218,
+ 0x2009, 0x0007, 0x00d0, 0xa182, 0x02c1, 0x1218, 0x2009, 0x0006,
+ 0x00a0, 0xa182, 0x0349, 0x1218, 0x2009, 0x0005, 0x0070, 0xa182,
+ 0x0421, 0x1218, 0x2009, 0x0004, 0x0040, 0xa182, 0x0581, 0x1218,
+ 0x2009, 0x0003, 0x0010, 0x2009, 0x0002, 0x6192, 0x014e, 0x013e,
+ 0x015e, 0x00de, 0x0005, 0x0016, 0x0026, 0x00e6, 0x2071, 0xbb8d,
+ 0x2e04, 0x6896, 0x2071, 0xbb8e, 0x7004, 0x689a, 0x701c, 0x689e,
+ 0x6a00, 0x2009, 0xb572, 0x210c, 0xd0bc, 0x0120, 0xd1ec, 0x0110,
+ 0xc2ad, 0x0008, 0xc2ac, 0xd0c4, 0x0120, 0xd1e4, 0x0110, 0xc2bd,
+ 0x0008, 0xc2bc, 0x6a02, 0x00ee, 0x002e, 0x001e, 0x0005, 0x00d6,
+ 0x0126, 0x2091, 0x8000, 0x60a4, 0xa06d, 0x01c0, 0x6900, 0x81ff,
+ 0x1540, 0x6a04, 0xa282, 0x0010, 0x1648, 0xad88, 0x0004, 0x20a9,
+ 0x0010, 0x2104, 0xa086, 0xffff, 0x0128, 0x8108, 0x1f04, 0x5081,
+ 0x080c, 0x1515, 0x260a, 0x8210, 0x6a06, 0x0098, 0x080c, 0x15f8,
+ 0x01a8, 0x2d00, 0x60a6, 0x6803, 0x0000, 0xad88, 0x0004, 0x20a9,
+ 0x0010, 0x200b, 0xffff, 0x8108, 0x1f04, 0x5099, 0x6807, 0x0001,
+ 0x6e12, 0xa085, 0x0001, 0x012e, 0x00de, 0x0005, 0xa006, 0x0cd8,
+ 0x0126, 0x2091, 0x8000, 0x00d6, 0x60a4, 0xa00d, 0x01a0, 0x2168,
+ 0x6800, 0xa005, 0x1160, 0x080c, 0x51d4, 0x1168, 0x200b, 0xffff,
+ 0x6804, 0xa08a, 0x0002, 0x0218, 0x8001, 0x6806, 0x0020, 0x080c,
+ 0x160f, 0x60a7, 0x0000, 0x00de, 0x012e, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x5232, 0x0010, 0x080c, 0x4e8d, 0x080c, 0x514c,
+ 0x1dd8, 0x080c, 0x5113, 0x012e, 0x0005, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x60a8, 0xa06d, 0x01c0, 0x6950, 0x81ff, 0x1540, 0x6a54,
+ 0xa282, 0x0010, 0x1670, 0xad88, 0x0018, 0x20a9, 0x0010, 0x2104,
+ 0xa086, 0xffff, 0x0128, 0x8108, 0x1f04, 0x50e7, 0x080c, 0x1515,
+ 0x260a, 0x8210, 0x6a56, 0x0098, 0x080c, 0x15f8, 0x01d0, 0x2d00,
+ 0x60aa, 0x6853, 0x0000, 0xad88, 0x0018, 0x20a9, 0x0010, 0x200b,
+ 0xffff, 0x8108, 0x1f04, 0x50ff, 0x6857, 0x0001, 0x6e62, 0x0010,
+ 0x080c, 0x4ede, 0x0089, 0x1de0, 0xa085, 0x0001, 0x012e, 0x00de,
+ 0x0005, 0xa006, 0x0cd8, 0x0126, 0x2091, 0x8000, 0x080c, 0x6caa,
+ 0x012e, 0x0005, 0xa01e, 0x0010, 0x2019, 0x0001, 0xa00e, 0x0126,
+ 0x2091, 0x8000, 0x604c, 0x2068, 0x6000, 0xd0dc, 0x1170, 0x8dff,
+ 0x01f8, 0x83ff, 0x0120, 0x6848, 0xa606, 0x0158, 0x0030, 0x683c,
+ 0xa406, 0x1118, 0x6840, 0xa506, 0x0120, 0x2d08, 0x6800, 0x2068,
+ 0x0c70, 0x080c, 0x811e, 0x6a00, 0x604c, 0xad06, 0x1110, 0x624e,
+ 0x0018, 0xa180, 0x0000, 0x2202, 0x82ff, 0x1110, 0x6152, 0x8dff,
+ 0x012e, 0x0005, 0xa01e, 0x0010, 0x2019, 0x0001, 0xa00e, 0x6080,
+ 0x2068, 0x8dff, 0x01e8, 0x83ff, 0x0120, 0x6848, 0xa606, 0x0158,
+ 0x0030, 0x683c, 0xa406, 0x1118, 0x6840, 0xa506, 0x0120, 0x2d08,
+ 0x6800, 0x2068, 0x0c70, 0x6a00, 0x6080, 0xad06, 0x1110, 0x6282,
+ 0x0018, 0xa180, 0x0000, 0x2202, 0x82ff, 0x1110, 0x6186, 0x8dff,
+ 0x0005, 0xa016, 0x080c, 0x51ce, 0x1110, 0x2011, 0x0001, 0x080c,
+ 0x5219, 0x1110, 0xa295, 0x0002, 0x0005, 0x080c, 0x524a, 0x0118,
+ 0x080c, 0x9d0f, 0x0010, 0xa085, 0x0001, 0x0005, 0x080c, 0x524a,
+ 0x0118, 0x080c, 0x9c9f, 0x0010, 0xa085, 0x0001, 0x0005, 0x080c,
+ 0x524a, 0x0118, 0x080c, 0x9cf2, 0x0010, 0xa085, 0x0001, 0x0005,
+ 0x080c, 0x524a, 0x0118, 0x080c, 0x9cbb, 0x0010, 0xa085, 0x0001,
+ 0x0005, 0x080c, 0x524a, 0x0118, 0x080c, 0x9d2b, 0x0010, 0xa085,
+ 0x0001, 0x0005, 0x0126, 0x0006, 0x00d6, 0x2091, 0x8000, 0x6080,
+ 0xa06d, 0x01a0, 0x6800, 0x0006, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x080c, 0x9ecc, 0x0006, 0x6000, 0xd0fc, 0x0110, 0x080c,
+ 0xb389, 0x000e, 0x080c, 0x5408, 0x000e, 0x0c50, 0x6083, 0x0000,
+ 0x6087, 0x0000, 0x00de, 0x000e, 0x012e, 0x0005, 0x60a4, 0xa00d,
+ 0x1118, 0xa085, 0x0001, 0x0005, 0x00e6, 0x2170, 0x7000, 0xa005,
+ 0x1168, 0x20a9, 0x0010, 0xae88, 0x0004, 0x2104, 0xa606, 0x0130,
+ 0x8108, 0x1f04, 0x51dd, 0xa085, 0x0001, 0x0008, 0xa006, 0x00ee,
+ 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000, 0x60a4, 0xa06d, 0x1128,
+ 0x080c, 0x15f8, 0x01a0, 0x2d00, 0x60a6, 0x6803, 0x0001, 0x6807,
+ 0x0000, 0xad88, 0x0004, 0x20a9, 0x0010, 0x200b, 0xffff, 0x8108,
+ 0x1f04, 0x51fd, 0xa085, 0x0001, 0x012e, 0x00de, 0x0005, 0xa006,
+ 0x0cd8, 0x00d6, 0x0126, 0x2091, 0x8000, 0x60a4, 0xa06d, 0x0130,
+ 0x60a7, 0x0000, 0x080c, 0x160f, 0xa085, 0x0001, 0x012e, 0x00de,
+ 0x0005, 0x60a8, 0xa00d, 0x1118, 0xa085, 0x0001, 0x0005, 0x00e6,
+ 0x2170, 0x7050, 0xa005, 0x1160, 0x20a9, 0x0010, 0xae88, 0x0018,
+ 0x2104, 0xa606, 0x0128, 0x8108, 0x1f04, 0x5228, 0xa085, 0x0001,
+ 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000, 0x0c19, 0x1188, 0x200b,
+ 0xffff, 0x00d6, 0x60a8, 0x2068, 0x6854, 0xa08a, 0x0002, 0x0218,
+ 0x8001, 0x6856, 0x0020, 0x080c, 0x160f, 0x60ab, 0x0000, 0x00de,
+ 0x012e, 0x0005, 0x609c, 0xd0a4, 0x0005, 0x00f6, 0x080c, 0x5acf,
+ 0x01b0, 0x71b8, 0x81ff, 0x1198, 0x71d4, 0xd19c, 0x0180, 0x2001,
+ 0x007e, 0xa080, 0xb635, 0x2004, 0xa07d, 0x0148, 0x7804, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x1118, 0x7800, 0xc0ed, 0x7802, 0x2079,
+ 0xb552, 0x7804, 0xd0a4, 0x01e8, 0x0156, 0x00c6, 0x20a9, 0x007f,
+ 0x2009, 0x0000, 0x0016, 0x080c, 0x4fa9, 0x1168, 0x6004, 0xa084,
+ 0xff00, 0x8007, 0xa096, 0x0004, 0x0118, 0xa086, 0x0006, 0x1118,
+ 0x6000, 0xc0ed, 0x6002, 0x001e, 0x8108, 0x1f04, 0x5272, 0x00ce,
+ 0x015e, 0x080c, 0x5309, 0x0120, 0x2001, 0xb7b5, 0x200c, 0x0038,
+ 0x2079, 0xb552, 0x7804, 0xd0a4, 0x0130, 0x2009, 0x07d0, 0x2011,
+ 0x529d, 0x080c, 0x6a22, 0x00fe, 0x0005, 0x2011, 0x529d, 0x080c,
+ 0x699c, 0x080c, 0x5309, 0x01f0, 0x2001, 0xb6b3, 0x2004, 0xa080,
+ 0x0000, 0x200c, 0xc1ec, 0x2102, 0x2001, 0xb553, 0x2004, 0xd0a4,
+ 0x0130, 0x2009, 0x07d0, 0x2011, 0x529d, 0x080c, 0x6a22, 0x00e6,
+ 0x2071, 0xb500, 0x7073, 0x0000, 0x7077, 0x0000, 0x080c, 0x2ab8,
+ 0x00ee, 0x04b0, 0x0156, 0x00c6, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0x0016, 0x080c, 0x4fa9, 0x1530, 0x6000, 0xd0ec, 0x0518, 0x0046,
+ 0x62a0, 0xa294, 0x00ff, 0x8227, 0xa006, 0x2009, 0x0029, 0x080c,
+ 0xb0e8, 0x6000, 0xc0e5, 0xc0ec, 0x6002, 0x6004, 0xa084, 0x00ff,
+ 0xa085, 0x0700, 0x6006, 0x2019, 0x0029, 0x080c, 0x6df5, 0x0076,
+ 0x2039, 0x0000, 0x080c, 0x6d02, 0x2009, 0x0000, 0x080c, 0xae82,
+ 0x007e, 0x004e, 0x001e, 0x8108, 0x1f04, 0x52c8, 0x00ce, 0x015e,
+ 0x0005, 0x00c6, 0x6018, 0x2060, 0x6000, 0xc0ec, 0x6002, 0x00ce,
+ 0x0005, 0x7818, 0x2004, 0xd0ac, 0x0005, 0x7818, 0x2004, 0xd0bc,
+ 0x0005, 0x00f6, 0x2001, 0xb6b3, 0x2004, 0xa07d, 0x0110, 0x7800,
+ 0xd0ec, 0x00fe, 0x0005, 0x0126, 0x0026, 0x2091, 0x8000, 0x0006,
+ 0x62a0, 0xa290, 0xb635, 0x2204, 0xac06, 0x190c, 0x1515, 0x000e,
+ 0x6200, 0xa005, 0x0110, 0xc2fd, 0x0008, 0xc2fc, 0x6202, 0x002e,
+ 0x012e, 0x0005, 0x2011, 0xb535, 0x2204, 0xd0cc, 0x0138, 0x2001,
+ 0xb7b3, 0x200c, 0x2011, 0x5337, 0x080c, 0x6a22, 0x0005, 0x2011,
+ 0x5337, 0x080c, 0x699c, 0x2011, 0xb535, 0x2204, 0xc0cc, 0x2012,
+ 0x0005, 0x2071, 0xb614, 0x7003, 0x0001, 0x7007, 0x0000, 0x7013,
+ 0x0000, 0x7017, 0x0000, 0x701b, 0x0000, 0x701f, 0x0000, 0x700b,
+ 0x0000, 0x704b, 0x0001, 0x704f, 0x0000, 0x705b, 0x0020, 0x705f,
+ 0x0040, 0x707f, 0x0000, 0x2071, 0xb77d, 0x7003, 0xb614, 0x7007,
+ 0x0000, 0x700b, 0x0000, 0x700f, 0xb75d, 0x7013, 0x0020, 0x7017,
+ 0x0040, 0x7037, 0x0000, 0x0005, 0x0016, 0x00e6, 0x2071, 0xb735,
+ 0xa00e, 0x7186, 0x718a, 0x7097, 0x0001, 0x2001, 0xb553, 0x2004,
+ 0xd0fc, 0x1150, 0x2001, 0xb553, 0x2004, 0xa00e, 0xd09c, 0x0108,
+ 0x8108, 0x7102, 0x0804, 0x53d2, 0x2001, 0xb572, 0x200c, 0xa184,
+ 0x000f, 0x2009, 0xb573, 0x210c, 0x0002, 0x537a, 0x53ad, 0x53b4,
+ 0x53be, 0x53c3, 0x537a, 0x537a, 0x537a, 0x539d, 0x537a, 0x537a,
+ 0x537a, 0x537a, 0x537a, 0x537a, 0x537a, 0x7003, 0x0004, 0x0136,
+ 0x0146, 0x0156, 0x2099, 0xb576, 0x20a1, 0xb786, 0x20a9, 0x0004,
+ 0x53a3, 0x015e, 0x014e, 0x013e, 0x0428, 0x708f, 0x0005, 0x7007,
+ 0x0122, 0x2001, 0x0002, 0x0030, 0x708f, 0x0002, 0x7007, 0x0121,
+ 0x2001, 0x0003, 0x7002, 0x7097, 0x0001, 0x0088, 0x7007, 0x0122,
+ 0x2001, 0x0002, 0x0020, 0x7007, 0x0121, 0x2001, 0x0003, 0x7002,
+ 0xa006, 0x7096, 0x708e, 0xa184, 0xff00, 0x8007, 0x709a, 0xa184,
+ 0x00ff, 0x7092, 0x00ee, 0x001e, 0x0005, 0x00e6, 0x2071, 0xb614,
+ 0x684c, 0xa005, 0x1130, 0x7028, 0xc085, 0x702a, 0xa085, 0x0001,
+ 0x0428, 0x6a60, 0x7236, 0x6b64, 0x733a, 0x6868, 0x703e, 0x7076,
+ 0x686c, 0x7042, 0x707a, 0x684c, 0x702e, 0x6844, 0x7032, 0x2009,
+ 0x000d, 0x200a, 0x700b, 0x0000, 0x8007, 0x8006, 0x8006, 0xa08c,
+ 0x003f, 0xa084, 0xffc0, 0xa210, 0x2100, 0xa319, 0x726e, 0x7372,
+ 0x7028, 0xc084, 0x702a, 0x7007, 0x0001, 0xa006, 0x00ee, 0x0005,
+ 0x0156, 0x00e6, 0x0026, 0x6838, 0xd0fc, 0x1904, 0x5461, 0x6804,
+ 0xa00d, 0x0188, 0x00d6, 0x2071, 0xb500, 0xa016, 0x702c, 0x2168,
+ 0x6904, 0x206a, 0x8210, 0x2d00, 0x81ff, 0x1dc8, 0x702e, 0x70b4,
+ 0xa200, 0x70b6, 0x00de, 0x2071, 0xb614, 0x701c, 0xa005, 0x1904,
+ 0x5471, 0x20a9, 0x0032, 0x0f04, 0x546f, 0x0e04, 0x542b, 0x2071,
+ 0xb735, 0x7200, 0x82ff, 0x05d8, 0x6934, 0xa186, 0x0103, 0x1904,
+ 0x547f, 0x6948, 0x6844, 0xa105, 0x1540, 0x2009, 0x8020, 0x2200,
+ 0x0002, 0x546f, 0x5446, 0x5497, 0x54a3, 0x546f, 0x2071, 0x0000,
+ 0x20a9, 0x0032, 0x0f04, 0x546f, 0x7018, 0xd084, 0x1dd8, 0x7122,
+ 0x683c, 0x7026, 0x6840, 0x702a, 0x701b, 0x0001, 0x2091, 0x4080,
+ 0x2071, 0xb500, 0x702c, 0x206a, 0x2d00, 0x702e, 0x70b4, 0x8000,
+ 0x70b6, 0x002e, 0x00ee, 0x015e, 0x0005, 0x6844, 0xa086, 0x0100,
+ 0x1130, 0x6868, 0xa005, 0x1118, 0x2009, 0x8020, 0x0880, 0x2071,
+ 0xb614, 0x2d08, 0x206b, 0x0000, 0x7010, 0x8000, 0x7012, 0x7018,
+ 0xa06d, 0x711a, 0x0110, 0x6902, 0x0008, 0x711e, 0x0c10, 0xa18c,
+ 0x00ff, 0xa186, 0x0017, 0x0130, 0xa186, 0x001e, 0x0118, 0xa18e,
+ 0x001f, 0x1d28, 0x684c, 0xd0cc, 0x0d10, 0x6850, 0xa084, 0x00ff,
+ 0xa086, 0x0001, 0x19e0, 0x2009, 0x8021, 0x0804, 0x543f, 0x7084,
+ 0x8008, 0xa092, 0x001e, 0x1a98, 0x7186, 0xae90, 0x0003, 0xa210,
+ 0x683c, 0x2012, 0x0078, 0x7084, 0x8008, 0xa092, 0x000f, 0x1a38,
+ 0x7186, 0xae90, 0x0003, 0x8003, 0xa210, 0x683c, 0x2012, 0x8210,
+ 0x6840, 0x2012, 0x7088, 0xa10a, 0x0a04, 0x5458, 0x718c, 0x7084,
+ 0xa10a, 0x0a04, 0x5458, 0x2071, 0x0000, 0x7018, 0xd084, 0x1904,
+ 0x5458, 0x2071, 0xb735, 0x7000, 0xa086, 0x0002, 0x1150, 0x080c,
+ 0x5722, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0804,
+ 0x5458, 0x080c, 0x574c, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091,
+ 0x4080, 0x0804, 0x5458, 0x0006, 0x684c, 0x0006, 0x6837, 0x0103,
+ 0x20a9, 0x001c, 0xad80, 0x0011, 0x20a0, 0x2001, 0x0000, 0x40a4,
+ 0x000e, 0xa084, 0x00ff, 0x684e, 0x000e, 0x684a, 0x6952, 0x0005,
+ 0x2071, 0xb614, 0x7004, 0x0002, 0x54fe, 0x550f, 0x570d, 0x570e,
+ 0x571b, 0x5721, 0x54ff, 0x56fe, 0x5694, 0x56ea, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x0e04, 0x550e, 0x2009, 0x000d, 0x7030, 0x200a,
+ 0x2091, 0x4080, 0x7007, 0x0001, 0x700b, 0x0000, 0x012e, 0x2069,
+ 0xb7f3, 0x683c, 0xa005, 0x03f8, 0x11f0, 0x0126, 0x2091, 0x8000,
+ 0x2069, 0x0000, 0x6934, 0x2001, 0xb620, 0x2004, 0xa10a, 0x0170,
+ 0x0e04, 0x5532, 0x2069, 0x0000, 0x6818, 0xd084, 0x1158, 0x2009,
+ 0x8040, 0x6922, 0x681b, 0x0001, 0x2091, 0x4080, 0x2069, 0xb7f3,
+ 0x683f, 0xffff, 0x012e, 0x2069, 0xb500, 0x6848, 0x6968, 0xa102,
+ 0x2069, 0xb735, 0x688a, 0x6984, 0x701c, 0xa06d, 0x0120, 0x81ff,
+ 0x0904, 0x5588, 0x00a0, 0x81ff, 0x0904, 0x564e, 0x2071, 0xb735,
+ 0x7184, 0x7088, 0xa10a, 0x1258, 0x7190, 0x2071, 0xb7f3, 0x7038,
+ 0xa005, 0x0128, 0x1b04, 0x564e, 0x713a, 0x0804, 0x564e, 0x2071,
+ 0xb735, 0x718c, 0x0126, 0x2091, 0x8000, 0x7084, 0xa10a, 0x0a04,
+ 0x5669, 0x0e04, 0x560a, 0x2071, 0x0000, 0x7018, 0xd084, 0x1904,
+ 0x560a, 0x2001, 0xffff, 0x2071, 0xb7f3, 0x703a, 0x2071, 0xb735,
+ 0x7000, 0xa086, 0x0002, 0x1150, 0x080c, 0x5722, 0x2071, 0x0000,
+ 0x701b, 0x0001, 0x2091, 0x4080, 0x0804, 0x560a, 0x080c, 0x574c,
+ 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0804, 0x560a,
+ 0x2071, 0xb735, 0x7000, 0xa005, 0x0904, 0x5630, 0x6934, 0xa186,
+ 0x0103, 0x1904, 0x560d, 0x684c, 0xd0bc, 0x1904, 0x5630, 0x6948,
+ 0x6844, 0xa105, 0x1904, 0x5625, 0x2009, 0x8020, 0x2071, 0xb735,
+ 0x7000, 0x0002, 0x5630, 0x55f0, 0x55c8, 0x55da, 0x55a7, 0x0136,
+ 0x0146, 0x0156, 0x2099, 0xb576, 0x20a1, 0xb786, 0x20a9, 0x0004,
+ 0x53a3, 0x015e, 0x014e, 0x013e, 0x2071, 0xb77d, 0xad80, 0x000f,
+ 0x700e, 0x7013, 0x0002, 0x7007, 0x0002, 0x700b, 0x0000, 0x2e10,
+ 0x080c, 0x1643, 0x2071, 0xb614, 0x7007, 0x0009, 0x0804, 0x564e,
+ 0x7084, 0x8008, 0xa092, 0x001e, 0x1a04, 0x564e, 0xae90, 0x0003,
+ 0xa210, 0x683c, 0x2012, 0x7186, 0x2071, 0xb614, 0x080c, 0x57a3,
+ 0x0804, 0x564e, 0x7084, 0x8008, 0xa092, 0x000f, 0x1a04, 0x564e,
+ 0xae90, 0x0003, 0x8003, 0xa210, 0x683c, 0x2012, 0x8210, 0x6840,
+ 0x2012, 0x7186, 0x2071, 0xb614, 0x080c, 0x57a3, 0x0804, 0x564e,
+ 0x0126, 0x2091, 0x8000, 0x0e04, 0x560a, 0x2071, 0x0000, 0x7018,
+ 0xd084, 0x1180, 0x7122, 0x683c, 0x7026, 0x6840, 0x702a, 0x701b,
+ 0x0001, 0x2091, 0x4080, 0x012e, 0x2071, 0xb614, 0x080c, 0x57a3,
+ 0x0804, 0x564e, 0x012e, 0x0804, 0x564e, 0xa18c, 0x00ff, 0xa186,
+ 0x0017, 0x0130, 0xa186, 0x001e, 0x0118, 0xa18e, 0x001f, 0x11c0,
+ 0x684c, 0xd0cc, 0x01a8, 0x6850, 0xa084, 0x00ff, 0xa086, 0x0001,
+ 0x1178, 0x2009, 0x8021, 0x0804, 0x559e, 0x6844, 0xa086, 0x0100,
+ 0x1138, 0x6868, 0xa005, 0x1120, 0x2009, 0x8020, 0x0804, 0x559e,
+ 0x2071, 0xb614, 0x080c, 0x57b5, 0x01c8, 0x2071, 0xb614, 0x700f,
+ 0x0001, 0x6934, 0xa184, 0x00ff, 0xa086, 0x0003, 0x1130, 0x810f,
+ 0xa18c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x7007, 0x0003, 0x080c,
+ 0x57ce, 0x7050, 0xa086, 0x0100, 0x0904, 0x570e, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0xb614, 0x7008, 0xa086, 0x0001, 0x1180, 0x0e04,
+ 0x5667, 0x2009, 0x000d, 0x7030, 0x200a, 0x2091, 0x4080, 0x700b,
+ 0x0000, 0x7004, 0xa086, 0x0006, 0x1110, 0x7007, 0x0001, 0x012e,
+ 0x0005, 0x2071, 0xb614, 0x080c, 0x57b5, 0x0518, 0x2071, 0xb735,
+ 0x7084, 0x700a, 0x20a9, 0x0020, 0x2099, 0xb736, 0x20a1, 0xb75d,
+ 0x53a3, 0x7087, 0x0000, 0x2071, 0xb614, 0x2069, 0xb77d, 0x706c,
+ 0x6826, 0x7070, 0x682a, 0x7074, 0x682e, 0x7078, 0x6832, 0x2d10,
+ 0x080c, 0x1643, 0x7007, 0x0008, 0x2001, 0xffff, 0x2071, 0xb7f3,
+ 0x703a, 0x012e, 0x0804, 0x564e, 0x2069, 0xb77d, 0x6808, 0xa08e,
+ 0x0000, 0x0904, 0x56e9, 0xa08e, 0x0200, 0x0904, 0x56e7, 0xa08e,
+ 0x0100, 0x1904, 0x56e9, 0x0126, 0x2091, 0x8000, 0x0e04, 0x56e5,
+ 0x2069, 0x0000, 0x6818, 0xd084, 0x15c0, 0x702c, 0x7130, 0x8108,
+ 0xa102, 0x0230, 0xa00e, 0x7034, 0x706e, 0x7038, 0x7072, 0x0048,
+ 0x706c, 0xa080, 0x0040, 0x706e, 0x1220, 0x7070, 0xa081, 0x0000,
+ 0x7072, 0x7132, 0x6936, 0x700b, 0x0000, 0x2001, 0xb75a, 0x2004,
+ 0xa005, 0x1190, 0x6934, 0x2069, 0xb735, 0x689c, 0x699e, 0x2069,
+ 0xb7f3, 0xa102, 0x1118, 0x683c, 0xa005, 0x1368, 0x2001, 0xb75b,
+ 0x200c, 0x810d, 0x693e, 0x0038, 0x2009, 0x8040, 0x6922, 0x681b,
+ 0x0001, 0x2091, 0x4080, 0x7007, 0x0001, 0x012e, 0x0010, 0x7007,
+ 0x0005, 0x0005, 0x2001, 0xb77f, 0x2004, 0xa08e, 0x0100, 0x1128,
+ 0x7007, 0x0001, 0x080c, 0x57a3, 0x0005, 0xa08e, 0x0000, 0x0de0,
+ 0xa08e, 0x0200, 0x1dc8, 0x7007, 0x0005, 0x0005, 0x701c, 0xa06d,
+ 0x0158, 0x080c, 0x57b5, 0x0140, 0x7007, 0x0003, 0x080c, 0x57ce,
+ 0x7050, 0xa086, 0x0100, 0x0110, 0x0005, 0x0005, 0x7050, 0xa09e,
+ 0x0100, 0x1118, 0x7007, 0x0004, 0x0030, 0xa086, 0x0200, 0x1110,
+ 0x7007, 0x0005, 0x0005, 0x080c, 0x5771, 0x7006, 0x080c, 0x57a3,
+ 0x0005, 0x0005, 0x00e6, 0x0156, 0x2071, 0xb735, 0x7184, 0x81ff,
+ 0x0500, 0xa006, 0x7086, 0xae80, 0x0003, 0x2071, 0x0000, 0x21a8,
+ 0x2014, 0x7226, 0x8000, 0x0f04, 0x5746, 0x2014, 0x722a, 0x8000,
+ 0x0f04, 0x5746, 0x2014, 0x722e, 0x8000, 0x0f04, 0x5746, 0x2014,
+ 0x723a, 0x8000, 0x0f04, 0x5746, 0x2014, 0x723e, 0xa180, 0x8030,
+ 0x7022, 0x015e, 0x00ee, 0x0005, 0x00e6, 0x0156, 0x2071, 0xb735,
+ 0x7184, 0x81ff, 0x01d8, 0xa006, 0x7086, 0xae80, 0x0003, 0x2071,
+ 0x0000, 0x21a8, 0x2014, 0x7226, 0x8000, 0x2014, 0x722a, 0x8000,
+ 0x0f04, 0x5768, 0x2014, 0x723a, 0x8000, 0x2014, 0x723e, 0x0018,
+ 0x2001, 0x8020, 0x0010, 0x2001, 0x8042, 0x7022, 0x015e, 0x00ee,
+ 0x0005, 0x702c, 0x7130, 0x8108, 0xa102, 0x0230, 0xa00e, 0x7034,
+ 0x706e, 0x7038, 0x7072, 0x0048, 0x706c, 0xa080, 0x0040, 0x706e,
+ 0x1220, 0x7070, 0xa081, 0x0000, 0x7072, 0x7132, 0x700c, 0x8001,
+ 0x700e, 0x1180, 0x0126, 0x2091, 0x8000, 0x0e04, 0x579d, 0x2001,
+ 0x000d, 0x2102, 0x2091, 0x4080, 0x2001, 0x0001, 0x700b, 0x0000,
+ 0x012e, 0x0005, 0x2001, 0x0007, 0x0005, 0x2001, 0x0006, 0x700b,
+ 0x0001, 0x012e, 0x0005, 0x701c, 0xa06d, 0x0170, 0x0126, 0x2091,
+ 0x8000, 0x7010, 0x8001, 0x7012, 0x2d04, 0x701e, 0xa005, 0x1108,
+ 0x701a, 0x012e, 0x080c, 0x160f, 0x0005, 0x2019, 0x000d, 0x2304,
+ 0x230c, 0xa10e, 0x0130, 0x2304, 0x230c, 0xa10e, 0x0110, 0xa006,
+ 0x0060, 0x732c, 0x8319, 0x7130, 0xa102, 0x1118, 0x2300, 0xa005,
+ 0x0020, 0x0210, 0xa302, 0x0008, 0x8002, 0x0005, 0x2d00, 0x7026,
+ 0xa080, 0x000d, 0x7056, 0x7053, 0x0000, 0x0126, 0x2091, 0x8000,
+ 0x2009, 0xb812, 0x2104, 0xc08d, 0x200a, 0x012e, 0x080c, 0x165f,
+ 0x0005, 0x708c, 0xa08a, 0x0029, 0x1220, 0xa082, 0x001d, 0x0033,
+ 0x0010, 0x080c, 0x1515, 0x6027, 0x1e00, 0x0005, 0x58dc, 0x5857,
+ 0x586f, 0x58ac, 0x58cd, 0x5907, 0x5919, 0x586f, 0x58f3, 0x57fb,
+ 0x5829, 0x57fa, 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0xa005,
+ 0x1180, 0x6808, 0xa005, 0x1518, 0x708f, 0x0028, 0x2069, 0xb7c5,
+ 0x2d04, 0x7002, 0x080c, 0x5bd1, 0x6028, 0xa085, 0x0600, 0x602a,
+ 0x00b0, 0x708f, 0x0028, 0x2069, 0xb7c5, 0x2d04, 0x7002, 0x6028,
+ 0xa085, 0x0600, 0x602a, 0x00e6, 0x0036, 0x0046, 0x0056, 0x2071,
+ 0xb823, 0x080c, 0x1dfe, 0x005e, 0x004e, 0x003e, 0x00ee, 0x00de,
+ 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0xa005, 0x1180, 0x6808,
+ 0xa005, 0x1518, 0x708f, 0x0028, 0x2069, 0xb7c5, 0x2d04, 0x7002,
+ 0x080c, 0x5c5e, 0x6028, 0xa085, 0x0600, 0x602a, 0x00b0, 0x708f,
+ 0x0028, 0x2069, 0xb7c5, 0x2d04, 0x7002, 0x6028, 0xa085, 0x0600,
+ 0x602a, 0x00e6, 0x0036, 0x0046, 0x0056, 0x2071, 0xb823, 0x080c,
+ 0x1dfe, 0x005e, 0x004e, 0x003e, 0x00ee, 0x00de, 0x0005, 0x6803,
+ 0x0090, 0x6124, 0xd1e4, 0x1190, 0x080c, 0x5984, 0xd1d4, 0x1160,
+ 0xd1dc, 0x1138, 0xd1cc, 0x0150, 0x708f, 0x0020, 0x080c, 0x5984,
+ 0x0028, 0x708f, 0x001d, 0x0010, 0x708f, 0x001f, 0x0005, 0x6803,
+ 0x0088, 0x6124, 0xd1cc, 0x1590, 0xd1dc, 0x1568, 0xd1e4, 0x1540,
+ 0xa184, 0x1e00, 0x1580, 0x60e3, 0x0001, 0x600c, 0xc0b4, 0x600e,
+ 0x080c, 0x5aff, 0x080c, 0x24b0, 0x0156, 0x6803, 0x0100, 0x20a9,
+ 0x0014, 0x6804, 0xd0dc, 0x1118, 0x1f04, 0x5889, 0x0048, 0x20a9,
+ 0x0014, 0x6803, 0x0080, 0x6804, 0xd0d4, 0x1130, 0x1f04, 0x5893,
+ 0x080c, 0x5b20, 0x015e, 0x0078, 0x015e, 0x708f, 0x0028, 0x0058,
+ 0x708f, 0x001e, 0x0040, 0x708f, 0x001d, 0x0028, 0x708f, 0x0020,
+ 0x0010, 0x708f, 0x001f, 0x0005, 0x60e3, 0x0001, 0x600c, 0xc0b4,
+ 0x600e, 0x080c, 0x5aff, 0x080c, 0x24b0, 0x6803, 0x0080, 0x6124,
+ 0xd1d4, 0x1180, 0xd1dc, 0x1158, 0xd1e4, 0x1130, 0xa184, 0x1e00,
+ 0x1158, 0x708f, 0x0028, 0x0040, 0x708f, 0x001e, 0x0028, 0x708f,
+ 0x001d, 0x0010, 0x708f, 0x001f, 0x0005, 0x6803, 0x00a0, 0x6124,
+ 0xd1dc, 0x1138, 0xd1e4, 0x0138, 0x080c, 0x1e47, 0x708f, 0x001e,
+ 0x0010, 0x708f, 0x001d, 0x0005, 0x080c, 0x59f6, 0x6124, 0xd1dc,
+ 0x1188, 0x080c, 0x5984, 0x0016, 0x080c, 0x1e47, 0x001e, 0xd1d4,
+ 0x1128, 0xd1e4, 0x0138, 0x708f, 0x001e, 0x0020, 0x708f, 0x001f,
+ 0x080c, 0x5984, 0x0005, 0x6803, 0x00a0, 0x6124, 0xd1d4, 0x1160,
+ 0xd1cc, 0x1150, 0xd1dc, 0x1128, 0xd1e4, 0x0140, 0x708f, 0x001e,
+ 0x0028, 0x708f, 0x001d, 0x0010, 0x708f, 0x0021, 0x0005, 0x080c,
+ 0x59f6, 0x6124, 0xd1d4, 0x1150, 0xd1dc, 0x1128, 0xd1e4, 0x0140,
+ 0x708f, 0x001e, 0x0028, 0x708f, 0x001d, 0x0010, 0x708f, 0x001f,
+ 0x0005, 0x6803, 0x0090, 0x6124, 0xd1d4, 0x1178, 0xd1cc, 0x1150,
+ 0xd1dc, 0x1128, 0xd1e4, 0x0158, 0x708f, 0x001e, 0x0040, 0x708f,
+ 0x001d, 0x0028, 0x708f, 0x0020, 0x0010, 0x708f, 0x001f, 0x0005,
+ 0x0016, 0x00c6, 0x00d6, 0x00e6, 0x0126, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x2071, 0xb500, 0x2091, 0x8000, 0x080c, 0x5acf, 0x11e8,
+ 0x2001, 0xb50c, 0x200c, 0xd1b4, 0x01c0, 0xc1b4, 0x2102, 0x6027,
+ 0x0200, 0xe000, 0xe000, 0x6024, 0xd0cc, 0x0158, 0x6803, 0x00a0,
+ 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001,
+ 0x0428, 0x6028, 0xc0cd, 0x602a, 0x0408, 0x080c, 0x5aeb, 0x0150,
+ 0x080c, 0x5ae1, 0x1138, 0x2001, 0x0001, 0x080c, 0x27c3, 0x080c,
+ 0x5aa6, 0x00a0, 0x080c, 0x59f3, 0x0178, 0x2001, 0x0001, 0x080c,
+ 0x27c3, 0x708c, 0xa086, 0x001e, 0x0120, 0x708c, 0xa086, 0x0022,
+ 0x1118, 0x708f, 0x0025, 0x0010, 0x708f, 0x0021, 0x012e, 0x00ee,
+ 0x00de, 0x00ce, 0x001e, 0x0005, 0x0026, 0x2011, 0x5995, 0x080c,
+ 0x6a5c, 0x002e, 0x0016, 0x0026, 0x2009, 0x0064, 0x2011, 0x5995,
+ 0x080c, 0x6a53, 0x002e, 0x001e, 0x0005, 0x00e6, 0x00f6, 0x0016,
+ 0x080c, 0x7d7a, 0x2071, 0xb500, 0x080c, 0x5930, 0x001e, 0x00fe,
+ 0x00ee, 0x0005, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x00f6, 0x0126, 0x080c, 0x7d7a, 0x2061, 0x0100, 0x2069, 0x0140,
+ 0x2071, 0xb500, 0x2091, 0x8000, 0x6028, 0xc09c, 0x602a, 0x2011,
+ 0x0003, 0x080c, 0x8075, 0x2011, 0x0002, 0x080c, 0x807f, 0x080c,
+ 0x7f59, 0x080c, 0x6a10, 0x0036, 0x2019, 0x0000, 0x080c, 0x7fe4,
+ 0x003e, 0x60e3, 0x0000, 0x080c, 0xb42f, 0x080c, 0xb44a, 0x2001,
+ 0xb500, 0x2003, 0x0004, 0x6027, 0x0008, 0x080c, 0x12dd, 0x2001,
+ 0x0001, 0x080c, 0x27c3, 0x012e, 0x00fe, 0x00ee, 0x00de, 0x00ce,
+ 0x003e, 0x002e, 0x001e, 0x0005, 0x2001, 0xb500, 0x2004, 0xa086,
+ 0x0004, 0x0140, 0x2001, 0xb79e, 0x2003, 0xaaaa, 0x2001, 0xb79f,
+ 0x2003, 0x0000, 0x0005, 0x6020, 0xd09c, 0x0005, 0x6800, 0xa086,
+ 0x00c0, 0x0160, 0x6803, 0x00c0, 0x0156, 0x20a9, 0x002d, 0x1d04,
+ 0x59ff, 0x2091, 0x6000, 0x1f04, 0x59ff, 0x015e, 0x0005, 0x00c6,
+ 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0xb500,
+ 0x2001, 0xb79f, 0x200c, 0xa186, 0x0000, 0x0158, 0xa186, 0x0001,
+ 0x0158, 0xa186, 0x0002, 0x0158, 0xa186, 0x0003, 0x0158, 0x0804,
+ 0x5a94, 0x708f, 0x0022, 0x0040, 0x708f, 0x0021, 0x0028, 0x708f,
+ 0x0023, 0x0020, 0x708f, 0x0024, 0x6043, 0x0000, 0x60e3, 0x0000,
+ 0x6887, 0x0001, 0x2001, 0x0001, 0x080c, 0x2872, 0x0026, 0x2011,
+ 0x0003, 0x080c, 0x8075, 0x2011, 0x0002, 0x080c, 0x807f, 0x080c,
+ 0x7f59, 0x0036, 0x2019, 0x0000, 0x080c, 0x7fe4, 0x003e, 0x002e,
+ 0x7000, 0xa08e, 0x0004, 0x0118, 0x602b, 0x0028, 0x0010, 0x602b,
+ 0x0020, 0x0156, 0x0126, 0x2091, 0x8000, 0x20a9, 0x0005, 0x6024,
+ 0xd0ac, 0x0120, 0x012e, 0x015e, 0x0804, 0x5aa2, 0x6800, 0xa084,
+ 0x00a0, 0xc0bd, 0x6802, 0x6904, 0xd1d4, 0x1130, 0x6803, 0x0100,
+ 0x1f04, 0x5a57, 0x080c, 0x5b20, 0x012e, 0x015e, 0x080c, 0x5ae1,
+ 0x01a8, 0x6044, 0xa005, 0x0168, 0x6050, 0x0006, 0xa085, 0x0020,
+ 0x6052, 0x080c, 0x5b20, 0xa006, 0x8001, 0x1df0, 0x000e, 0x6052,
+ 0x0028, 0x6804, 0xd0d4, 0x1110, 0x080c, 0x5b20, 0x0016, 0x0026,
+ 0x2009, 0x00c8, 0x2011, 0x59a2, 0x080c, 0x6a22, 0x002e, 0x001e,
+ 0x2001, 0xb79f, 0x2003, 0x0004, 0x080c, 0x57e1, 0x080c, 0x5ae1,
+ 0x0148, 0x6804, 0xd0d4, 0x1130, 0xd0dc, 0x1100, 0x2001, 0xb79f,
+ 0x2003, 0x0000, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0xb500, 0x2001,
+ 0xb79e, 0x2003, 0x0000, 0x2001, 0xb78f, 0x2003, 0x0000, 0x708f,
+ 0x0000, 0x60e3, 0x0000, 0x6887, 0x0000, 0x2001, 0x0000, 0x080c,
+ 0x2872, 0x6803, 0x0000, 0x6043, 0x0090, 0x6043, 0x0010, 0x6027,
+ 0xffff, 0x602b, 0x182f, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0006,
+ 0x2001, 0xb79e, 0x2004, 0xa086, 0xaaaa, 0x000e, 0x0005, 0x0006,
+ 0x2001, 0xb572, 0x2004, 0xa084, 0x0030, 0xa086, 0x0000, 0x000e,
+ 0x0005, 0x0006, 0x2001, 0xb572, 0x2004, 0xa084, 0x0030, 0xa086,
+ 0x0030, 0x000e, 0x0005, 0x0006, 0x2001, 0xb572, 0x2004, 0xa084,
+ 0x0030, 0xa086, 0x0010, 0x000e, 0x0005, 0x0006, 0x2001, 0xb572,
+ 0x2004, 0xa084, 0x0030, 0xa086, 0x0020, 0x000e, 0x0005, 0x2001,
+ 0xb50c, 0x2004, 0xd0a4, 0x0170, 0x080c, 0x2892, 0x0036, 0x0016,
+ 0x2009, 0x0000, 0x2019, 0x0028, 0x080c, 0x2c6f, 0x001e, 0x003e,
+ 0xa006, 0x0009, 0x0005, 0x00e6, 0x2071, 0xb50c, 0x2e04, 0x0118,
+ 0xa085, 0x0010, 0x0010, 0xa084, 0xffef, 0x2072, 0x00ee, 0x0005,
+ 0x6050, 0x0006, 0x60f0, 0x0006, 0x60ec, 0x0006, 0x600c, 0x0006,
+ 0x6004, 0x0006, 0x6028, 0x0006, 0x602f, 0x0100, 0x602f, 0x0000,
+ 0x602f, 0x0040, 0x602f, 0x0000, 0x000e, 0x602a, 0x000e, 0x6006,
+ 0x000e, 0x600e, 0x000e, 0x60ee, 0x000e, 0x60f2, 0x60e3, 0x0000,
+ 0x6887, 0x0001, 0x2001, 0x0001, 0x080c, 0x2872, 0x6800, 0xa084,
+ 0x00a0, 0xc0bd, 0x6802, 0x6803, 0x00a0, 0x000e, 0x6052, 0x6050,
+ 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0xb500, 0x6020, 0xa084,
+ 0x0080, 0x0138, 0x2001, 0xb50c, 0x200c, 0xc1bd, 0x2102, 0x0804,
+ 0x5bc9, 0x2001, 0xb50c, 0x200c, 0xc1bc, 0x2102, 0x6028, 0xa084,
+ 0xe1ff, 0x602a, 0x6027, 0x0200, 0x6803, 0x0090, 0x20a9, 0x0384,
+ 0x6024, 0xd0cc, 0x1508, 0x1d04, 0x5b78, 0x2091, 0x6000, 0x1f04,
+ 0x5b78, 0x2011, 0x0003, 0x080c, 0x8075, 0x2011, 0x0002, 0x080c,
+ 0x807f, 0x080c, 0x7f59, 0x2019, 0x0000, 0x080c, 0x7fe4, 0x6803,
+ 0x00a0, 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003,
+ 0x0001, 0xa085, 0x0001, 0x0468, 0x86ff, 0x1120, 0x080c, 0x1e47,
+ 0x080c, 0x24b0, 0x60e3, 0x0000, 0x2001, 0xb78f, 0x2004, 0x080c,
+ 0x2872, 0x60e2, 0x6803, 0x0080, 0x20a9, 0x0384, 0x6027, 0x1e00,
+ 0x2009, 0x1e00, 0xe000, 0x6024, 0xa10c, 0x0138, 0x1d04, 0x5bae,
+ 0x2091, 0x6000, 0x1f04, 0x5bae, 0x0820, 0x6028, 0xa085, 0x1e00,
+ 0x602a, 0x70a4, 0xa005, 0x1118, 0x6887, 0x0001, 0x0008, 0x6886,
+ 0xa006, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e,
+ 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2071, 0xb500, 0x2069, 0x0140, 0x6020, 0xa084,
+ 0x00c0, 0x0120, 0x6884, 0xa005, 0x1904, 0x5c25, 0x6803, 0x0088,
+ 0x60e3, 0x0000, 0x6887, 0x0000, 0x2001, 0x0000, 0x080c, 0x2872,
+ 0x2069, 0x0200, 0x6804, 0xa005, 0x1118, 0x6808, 0xa005, 0x01c0,
+ 0x6028, 0xa084, 0xfbff, 0x602a, 0x6027, 0x0400, 0x2069, 0xb7c5,
+ 0x7000, 0x206a, 0x708f, 0x0026, 0x7003, 0x0001, 0x20a9, 0x0002,
+ 0x1d04, 0x5c08, 0x2091, 0x6000, 0x1f04, 0x5c08, 0x0804, 0x5c56,
+ 0x2069, 0x0140, 0x20a9, 0x0384, 0x6027, 0x1e00, 0x2009, 0x1e00,
+ 0xe000, 0x6024, 0xa10c, 0x0520, 0xa084, 0x1a00, 0x1508, 0x1d04,
+ 0x5c14, 0x2091, 0x6000, 0x1f04, 0x5c14, 0x2011, 0x0003, 0x080c,
+ 0x8075, 0x2011, 0x0002, 0x080c, 0x807f, 0x080c, 0x7f59, 0x2019,
+ 0x0000, 0x080c, 0x7fe4, 0x6803, 0x00a0, 0x2001, 0xb79f, 0x2003,
+ 0x0001, 0x2001, 0xb500, 0x2003, 0x0001, 0xa085, 0x0001, 0x00b0,
+ 0x080c, 0x24b0, 0x6803, 0x0080, 0x2069, 0x0140, 0x60e3, 0x0000,
+ 0x70a4, 0xa005, 0x1118, 0x6887, 0x0001, 0x0008, 0x6886, 0x2001,
+ 0xb78f, 0x2004, 0x080c, 0x2872, 0x60e2, 0xa006, 0x00ee, 0x00de,
+ 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016,
+ 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2071,
+ 0xb500, 0x6020, 0xa084, 0x00c0, 0x01e0, 0x2011, 0x0003, 0x080c,
+ 0x8075, 0x2011, 0x0002, 0x080c, 0x807f, 0x080c, 0x7f59, 0x2019,
+ 0x0000, 0x080c, 0x7fe4, 0x2069, 0x0140, 0x6803, 0x00a0, 0x2001,
+ 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001, 0x0804,
+ 0x5cfb, 0x2001, 0xb50c, 0x200c, 0xd1b4, 0x1160, 0xc1b5, 0x2102,
+ 0x080c, 0x598a, 0x2069, 0x0140, 0x080c, 0x24b0, 0x6803, 0x0080,
+ 0x60e3, 0x0000, 0x2069, 0x0200, 0x6804, 0xa005, 0x1118, 0x6808,
+ 0xa005, 0x01c0, 0x6028, 0xa084, 0xfdff, 0x602a, 0x6027, 0x0200,
+ 0x2069, 0xb7c5, 0x7000, 0x206a, 0x708f, 0x0027, 0x7003, 0x0001,
+ 0x20a9, 0x0002, 0x1d04, 0x5cb2, 0x2091, 0x6000, 0x1f04, 0x5cb2,
+ 0x0804, 0x5cfb, 0x6027, 0x1e00, 0x2009, 0x1e00, 0xe000, 0x6024,
+ 0xa10c, 0x01c8, 0xa084, 0x1c00, 0x11b0, 0x1d04, 0x5cba, 0x0006,
+ 0x0016, 0x00c6, 0x00d6, 0x00e6, 0x080c, 0x68f9, 0x00ee, 0x00de,
+ 0x00ce, 0x001e, 0x000e, 0x00e6, 0x2071, 0xb7f3, 0x7018, 0x00ee,
+ 0xa005, 0x1d00, 0x0500, 0x0026, 0x2011, 0x59a2, 0x080c, 0x699c,
+ 0x2011, 0x5995, 0x080c, 0x6a5c, 0x002e, 0x2069, 0x0140, 0x60e3,
+ 0x0000, 0x70a4, 0xa005, 0x1118, 0x6887, 0x0001, 0x0008, 0x6886,
+ 0x2001, 0xb78f, 0x2004, 0x080c, 0x2872, 0x60e2, 0x2001, 0xb50c,
+ 0x200c, 0xc1b4, 0x2102, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e,
+ 0x001e, 0x015e, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x0046,
+ 0x00c6, 0x00e6, 0x2061, 0x0100, 0x2071, 0xb500, 0x7130, 0xd184,
+ 0x1180, 0x2011, 0xb553, 0x2214, 0xd2ec, 0x0138, 0xc18d, 0x7132,
+ 0x2011, 0xb553, 0x2214, 0xd2ac, 0x1120, 0x7030, 0xd08c, 0x0904,
+ 0x5d68, 0x7130, 0xc185, 0x7132, 0x2011, 0xb553, 0x220c, 0xd1a4,
+ 0x0530, 0x0016, 0x2019, 0x000e, 0x080c, 0xb065, 0x0156, 0x20a9,
+ 0x007f, 0x2009, 0x0000, 0xa186, 0x007e, 0x01a0, 0xa186, 0x0080,
+ 0x0188, 0x080c, 0x4fa9, 0x1170, 0x8127, 0xa006, 0x0016, 0x2009,
+ 0x000e, 0x080c, 0xb0e8, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c,
+ 0x6b1a, 0x001e, 0x8108, 0x1f04, 0x5d33, 0x015e, 0x001e, 0xd1ac,
+ 0x1148, 0x0016, 0x2009, 0x0000, 0x2019, 0x0004, 0x080c, 0x2c6f,
+ 0x001e, 0x0070, 0x0156, 0x20a9, 0x007f, 0x2009, 0x0000, 0x080c,
+ 0x4fa9, 0x1110, 0x080c, 0x4c0b, 0x8108, 0x1f04, 0x5d5f, 0x015e,
+ 0x080c, 0x1e47, 0x2011, 0x0003, 0x080c, 0x8075, 0x2011, 0x0002,
+ 0x080c, 0x807f, 0x080c, 0x7f59, 0x0036, 0x2019, 0x0000, 0x080c,
+ 0x7fe4, 0x003e, 0x60e3, 0x0000, 0x2001, 0xb500, 0x2003, 0x0001,
+ 0x080c, 0x5a07, 0x00ee, 0x00ce, 0x004e, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x0005, 0x2071, 0xb5e2, 0x7003, 0x0000, 0x7007, 0x0000,
+ 0x700f, 0x0000, 0x702b, 0x0001, 0x704f, 0x0000, 0x7053, 0x0001,
+ 0x705f, 0x0020, 0x7063, 0x0040, 0x7083, 0x0000, 0x708b, 0x0000,
+ 0x708f, 0x0001, 0x70bf, 0x0000, 0x0005, 0x00e6, 0x2071, 0xb5e2,
+ 0x6848, 0xa005, 0x1130, 0x7028, 0xc085, 0x702a, 0xa085, 0x0001,
+ 0x0428, 0x6a50, 0x7236, 0x6b54, 0x733a, 0x6858, 0x703e, 0x707a,
+ 0x685c, 0x7042, 0x707e, 0x6848, 0x702e, 0x6840, 0x7032, 0x2009,
+ 0x000c, 0x200a, 0x8007, 0x8006, 0x8006, 0xa08c, 0x003f, 0xa084,
+ 0xffc0, 0xa210, 0x2100, 0xa319, 0x7272, 0x7376, 0x7028, 0xc084,
+ 0x702a, 0x7007, 0x0001, 0x700f, 0x0000, 0xa006, 0x00ee, 0x0005,
+ 0x2b78, 0x2071, 0xb5e2, 0x7004, 0x0043, 0x700c, 0x0002, 0x5de4,
+ 0x5ddb, 0x5ddb, 0x5ddb, 0x5ddb, 0x0005, 0x5e3a, 0x5e3b, 0x5e6d,
+ 0x5e6e, 0x5e38, 0x5ebc, 0x5ec1, 0x5ef2, 0x5ef3, 0x5f0e, 0x5f0f,
+ 0x5f10, 0x5f11, 0x5f12, 0x5f13, 0x5fc9, 0x5ff0, 0x700c, 0x0002,
+ 0x5dfd, 0x5e38, 0x5e38, 0x5e39, 0x5e39, 0x7830, 0x7930, 0xa106,
+ 0x0120, 0x7830, 0x7930, 0xa106, 0x1510, 0x7030, 0xa10a, 0x01f8,
+ 0x1210, 0x712c, 0xa10a, 0xa18a, 0x0002, 0x12d0, 0x080c, 0x15df,
+ 0x01b0, 0x2d00, 0x705a, 0x7063, 0x0040, 0x2001, 0x0003, 0x7057,
+ 0x0000, 0x0126, 0x0006, 0x2091, 0x8000, 0x2009, 0xb812, 0x2104,
+ 0xc085, 0x200a, 0x000e, 0x700e, 0x012e, 0x080c, 0x165f, 0x0005,
+ 0x080c, 0x15df, 0x0de0, 0x2d00, 0x705a, 0x080c, 0x15df, 0x1108,
+ 0x0c10, 0x2d00, 0x7086, 0x7063, 0x0080, 0x2001, 0x0004, 0x08f8,
+ 0x0005, 0x0005, 0x0005, 0x700c, 0x0002, 0x5e42, 0x5e45, 0x5e53,
+ 0x5e6c, 0x5e6c, 0x080c, 0x5df6, 0x0005, 0x0126, 0x8001, 0x700e,
+ 0x7058, 0x0006, 0x080c, 0x6343, 0x0120, 0x2091, 0x8000, 0x080c,
+ 0x5df6, 0x00de, 0x0048, 0x0126, 0x8001, 0x700e, 0x080c, 0x6343,
+ 0x7058, 0x2068, 0x7084, 0x705a, 0x6803, 0x0000, 0x6807, 0x0000,
+ 0x6834, 0xa084, 0x00ff, 0xa08a, 0x003a, 0x1218, 0x00db, 0x012e,
+ 0x0005, 0x012e, 0x080c, 0x5f14, 0x0005, 0x0005, 0x0005, 0x00e6,
+ 0x2071, 0xb5e2, 0x700c, 0x0002, 0x5e79, 0x5e79, 0x5e79, 0x5e7b,
+ 0x5e7e, 0x00ee, 0x0005, 0x700f, 0x0001, 0x0010, 0x700f, 0x0002,
+ 0x00ee, 0x0005, 0x5f14, 0x5f14, 0x5f30, 0x5f14, 0x60ad, 0x5f14,
+ 0x5f14, 0x5f14, 0x5f14, 0x5f14, 0x5f30, 0x60ef, 0x6132, 0x617b,
+ 0x618f, 0x5f14, 0x5f14, 0x5f4c, 0x5f30, 0x5f14, 0x5f14, 0x5fa6,
+ 0x623b, 0x6256, 0x5f14, 0x5f4c, 0x5f14, 0x5f14, 0x5f14, 0x5f14,
+ 0x5f9c, 0x6256, 0x5f14, 0x5f14, 0x5f14, 0x5f14, 0x5f14, 0x5f14,
+ 0x5f14, 0x5f14, 0x5f14, 0x5f60, 0x5f14, 0x5f14, 0x5f14, 0x5f14,
+ 0x5f14, 0x5f14, 0x5f14, 0x5f14, 0x5f14, 0x6361, 0x5f14, 0x5f14,
+ 0x5f14, 0x5f14, 0x5f14, 0x5f75, 0x7020, 0x2068, 0x080c, 0x160f,
+ 0x0005, 0x700c, 0x0002, 0x5ec8, 0x5ecb, 0x5ed9, 0x5ef1, 0x5ef1,
+ 0x080c, 0x5df6, 0x0005, 0x0126, 0x8001, 0x700e, 0x7058, 0x0006,
+ 0x080c, 0x6343, 0x0120, 0x2091, 0x8000, 0x080c, 0x5df6, 0x00de,
+ 0x0048, 0x0126, 0x8001, 0x700e, 0x080c, 0x6343, 0x7058, 0x2068,
+ 0x7084, 0x705a, 0x6803, 0x0000, 0x6807, 0x0000, 0x6834, 0xa084,
+ 0x00ff, 0xa08a, 0x001a, 0x1218, 0x003b, 0x012e, 0x0005, 0x012e,
+ 0x0419, 0x0005, 0x0005, 0x0005, 0x5f14, 0x5f30, 0x6099, 0x5f14,
+ 0x5f30, 0x5f14, 0x5f30, 0x5f30, 0x5f14, 0x5f30, 0x6099, 0x5f30,
+ 0x5f30, 0x5f30, 0x5f30, 0x5f30, 0x5f14, 0x5f30, 0x6099, 0x5f14,
+ 0x5f14, 0x5f30, 0x5f14, 0x5f14, 0x5f14, 0x5f30, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x7007, 0x0001, 0x6838, 0xa084,
+ 0x00ff, 0xc0d5, 0x683a, 0x0126, 0x2091, 0x8000, 0x080c, 0x5408,
+ 0x012e, 0x0005, 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0e5,
+ 0x683a, 0x0126, 0x2091, 0x8000, 0x080c, 0x5408, 0x012e, 0x0005,
+ 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0ed, 0x683a, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x5408, 0x012e, 0x0005, 0x7007, 0x0001,
+ 0x6838, 0xa084, 0x00ff, 0xc0dd, 0x683a, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x5408, 0x012e, 0x0005, 0x6834, 0x8007, 0xa084, 0x00ff,
+ 0x0988, 0x8001, 0x1120, 0x7007, 0x0001, 0x0804, 0x6059, 0x7007,
+ 0x0006, 0x7012, 0x2d00, 0x7016, 0x701a, 0x704b, 0x6059, 0x0005,
+ 0x6834, 0x8007, 0xa084, 0x00ff, 0x0904, 0x5f22, 0x8001, 0x1120,
+ 0x7007, 0x0001, 0x0804, 0x6076, 0x7007, 0x0006, 0x7012, 0x2d00,
+ 0x7016, 0x701a, 0x704b, 0x6076, 0x0005, 0x6834, 0x8007, 0xa084,
+ 0x00ff, 0xa086, 0x0001, 0x1904, 0x5f22, 0x7007, 0x0001, 0x2009,
+ 0xb531, 0x210c, 0x81ff, 0x11a8, 0x6838, 0xa084, 0x00ff, 0x683a,
+ 0x6853, 0x0000, 0x080c, 0x4d82, 0x1108, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x6837, 0x0139, 0x684a, 0x6952, 0x080c, 0x5408, 0x012e,
+ 0x0ca0, 0x2001, 0x0028, 0x0c90, 0x684c, 0xa084, 0x00c0, 0xa086,
+ 0x00c0, 0x1120, 0x7007, 0x0001, 0x0804, 0x626e, 0x2d00, 0x7016,
+ 0x701a, 0x20a9, 0x0004, 0xa080, 0x0024, 0x2098, 0x20a1, 0xb60d,
+ 0x53a3, 0x6858, 0x7012, 0xa082, 0x0401, 0x1a04, 0x5f3e, 0x6a84,
+ 0xa28a, 0x0002, 0x1a04, 0x5f3e, 0x82ff, 0x1138, 0x6888, 0x698c,
+ 0xa105, 0x0118, 0x2001, 0x602c, 0x0018, 0xa280, 0x6022, 0x2005,
+ 0x70c6, 0x7010, 0xa015, 0x0904, 0x600e, 0x080c, 0x15df, 0x1118,
+ 0x7007, 0x000f, 0x0005, 0x2d00, 0x7022, 0x70c4, 0x2060, 0x2c05,
+ 0x6836, 0xe004, 0xad00, 0x7096, 0xe008, 0xa20a, 0x1210, 0xa00e,
+ 0x2200, 0x7112, 0xe20c, 0x8003, 0x800b, 0xa296, 0x0004, 0x0108,
+ 0xa108, 0x719a, 0x810b, 0x719e, 0xae90, 0x0022, 0x080c, 0x1643,
+ 0x7090, 0xa08e, 0x0100, 0x0170, 0xa086, 0x0200, 0x0118, 0x7007,
+ 0x0010, 0x0005, 0x7020, 0x2068, 0x080c, 0x160f, 0x7014, 0x2068,
+ 0x0804, 0x5f3e, 0x7020, 0x2068, 0x7018, 0x6802, 0x6807, 0x0000,
+ 0x2d08, 0x2068, 0x6906, 0x711a, 0x0804, 0x5fc9, 0x7014, 0x2068,
+ 0x7007, 0x0001, 0x6884, 0xa005, 0x1128, 0x6888, 0x698c, 0xa105,
+ 0x0108, 0x00b1, 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e, 0x0904,
+ 0x626e, 0x04b8, 0x6024, 0x6028, 0x0002, 0x0011, 0x0007, 0x0004,
+ 0x000a, 0x000f, 0x0005, 0x0006, 0x000a, 0x0011, 0x0005, 0x0004,
+ 0x00f6, 0x00e6, 0x00c6, 0x0076, 0x0066, 0x6f88, 0x6e8c, 0x6804,
+ 0x2060, 0xacf0, 0x0021, 0xacf8, 0x0027, 0x2009, 0x0005, 0x700c,
+ 0x7816, 0x7008, 0x7812, 0x7004, 0x7806, 0x7000, 0x7802, 0x7e0e,
+ 0x7f0a, 0x8109, 0x0128, 0xaef2, 0x0004, 0xaffa, 0x0006, 0x0c78,
+ 0x6004, 0xa065, 0x1d30, 0x006e, 0x007e, 0x00ce, 0x00ee, 0x00fe,
+ 0x0005, 0x2009, 0xb531, 0x210c, 0x81ff, 0x1198, 0x6838, 0xa084,
+ 0x00ff, 0x683a, 0x080c, 0x4c64, 0x1108, 0x0005, 0x080c, 0x54db,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x9ecc, 0x080c, 0x5408, 0x012e,
+ 0x0ca0, 0x2001, 0x0028, 0x2009, 0x0000, 0x0c80, 0x2009, 0xb531,
+ 0x210c, 0x81ff, 0x11b0, 0x6858, 0xa005, 0x01c0, 0x6838, 0xa084,
+ 0x00ff, 0x683a, 0x6853, 0x0000, 0x080c, 0x4d26, 0x1108, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x684a, 0x6952, 0x080c, 0x5408, 0x012e,
+ 0x0cb0, 0x2001, 0x0028, 0x2009, 0x0000, 0x0c90, 0x2001, 0x0000,
+ 0x0c78, 0x7018, 0x6802, 0x2d08, 0x2068, 0x6906, 0x711a, 0x7010,
+ 0x8001, 0x7012, 0x0118, 0x7007, 0x0006, 0x0030, 0x7014, 0x2068,
+ 0x7007, 0x0001, 0x7048, 0x080f, 0x0005, 0x7007, 0x0001, 0x6944,
+ 0x810f, 0xa18c, 0x00ff, 0x6848, 0xa084, 0x00ff, 0x20a9, 0x0001,
+ 0xa096, 0x0001, 0x01b0, 0x2009, 0x0000, 0x20a9, 0x00ff, 0xa096,
+ 0x0002, 0x0178, 0xa005, 0x11f0, 0x6944, 0x810f, 0xa18c, 0x00ff,
+ 0x080c, 0x4fa9, 0x11b8, 0x0066, 0x6e50, 0x080c, 0x50a8, 0x006e,
+ 0x0088, 0x0046, 0x2011, 0xb50c, 0x2224, 0xc484, 0x2412, 0x004e,
+ 0x00c6, 0x080c, 0x4fa9, 0x1110, 0x080c, 0x5209, 0x8108, 0x1f04,
+ 0x60d9, 0x00ce, 0x684c, 0xd084, 0x1118, 0x080c, 0x160f, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x5408, 0x012e, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x7007, 0x0001, 0x2001, 0xb553, 0x2004, 0xd0a4,
+ 0x0580, 0x2061, 0xb874, 0x6100, 0xd184, 0x0178, 0x6858, 0xa084,
+ 0x00ff, 0x1550, 0x6000, 0xd084, 0x0520, 0x6004, 0xa005, 0x1538,
+ 0x6003, 0x0000, 0x600b, 0x0000, 0x00c8, 0x2011, 0x0001, 0x6860,
+ 0xa005, 0x1110, 0x2001, 0x001e, 0x8000, 0x6016, 0x6858, 0xa084,
+ 0x00ff, 0x0178, 0x6006, 0x6858, 0x8007, 0xa084, 0x00ff, 0x0148,
+ 0x600a, 0x6858, 0x8000, 0x1108, 0xc28d, 0x6202, 0x012e, 0x0804,
+ 0x6332, 0x012e, 0x0804, 0x632c, 0x012e, 0x0804, 0x6326, 0x012e,
+ 0x0804, 0x6329, 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x2001,
+ 0xb553, 0x2004, 0xd0a4, 0x05e0, 0x2061, 0xb874, 0x6000, 0xd084,
+ 0x05b8, 0x6204, 0x6308, 0xd08c, 0x1530, 0x6c48, 0xa484, 0x0003,
+ 0x0170, 0x6958, 0xa18c, 0x00ff, 0x8001, 0x1120, 0x2100, 0xa210,
+ 0x0620, 0x0028, 0x8001, 0x1508, 0x2100, 0xa212, 0x02f0, 0xa484,
+ 0x000c, 0x0188, 0x6958, 0x810f, 0xa18c, 0x00ff, 0xa082, 0x0004,
+ 0x1120, 0x2100, 0xa318, 0x0288, 0x0030, 0xa082, 0x0004, 0x1168,
+ 0x2100, 0xa31a, 0x0250, 0x6860, 0xa005, 0x0110, 0x8000, 0x6016,
+ 0x6206, 0x630a, 0x012e, 0x0804, 0x6332, 0x012e, 0x0804, 0x632f,
+ 0x012e, 0x0804, 0x632c, 0x0126, 0x2091, 0x8000, 0x7007, 0x0001,
+ 0x2061, 0xb874, 0x6300, 0xd38c, 0x1120, 0x6308, 0x8318, 0x0220,
+ 0x630a, 0x012e, 0x0804, 0x6340, 0x012e, 0x0804, 0x632f, 0x0126,
+ 0x00c6, 0x2091, 0x8000, 0x7007, 0x0001, 0x684c, 0xd0ac, 0x0148,
+ 0x00c6, 0x2061, 0xb874, 0x6000, 0xa084, 0xfcff, 0x6002, 0x00ce,
+ 0x0448, 0x6858, 0xa005, 0x05d0, 0x685c, 0xa065, 0x0598, 0x2001,
+ 0xb531, 0x2004, 0xa005, 0x0118, 0x080c, 0x9e1d, 0x0068, 0x6013,
+ 0x0400, 0x6057, 0x0000, 0x694c, 0xd1a4, 0x0110, 0x6950, 0x6156,
+ 0x2009, 0x0041, 0x080c, 0x864c, 0x6958, 0xa18c, 0xff00, 0xa186,
+ 0x2000, 0x1140, 0x0026, 0x2009, 0x0000, 0x2011, 0xfdff, 0x080c,
+ 0x6b1a, 0x002e, 0x684c, 0xd0c4, 0x0148, 0x2061, 0xb874, 0x6000,
+ 0xd08c, 0x1120, 0x6008, 0x8000, 0x0208, 0x600a, 0x00ce, 0x012e,
+ 0x0804, 0x6332, 0x00ce, 0x012e, 0x0804, 0x632c, 0x6954, 0xa186,
+ 0x002e, 0x0d40, 0xa186, 0x002d, 0x0d28, 0xa186, 0x0045, 0x0528,
+ 0xa186, 0x002a, 0x1130, 0x2001, 0xb50c, 0x200c, 0xc194, 0x2102,
+ 0x08c8, 0xa186, 0x0020, 0x0170, 0xa186, 0x0029, 0x1d18, 0x6944,
+ 0xa18c, 0xff00, 0x810f, 0x080c, 0x4fa9, 0x1960, 0x6000, 0xc0e4,
+ 0x6002, 0x0840, 0x685c, 0xa065, 0x09a8, 0x6007, 0x0024, 0x2001,
+ 0xb7b6, 0x2004, 0x6016, 0x0804, 0x61ca, 0x685c, 0xa065, 0x0950,
+ 0x00e6, 0x6860, 0xa075, 0x2001, 0xb531, 0x2004, 0xa005, 0x0150,
+ 0x080c, 0x9e1d, 0x8eff, 0x0118, 0x2e60, 0x080c, 0x9e1d, 0x00ee,
+ 0x0804, 0x61ca, 0x6020, 0xc0dc, 0xc0d5, 0x6022, 0x2e60, 0x6007,
+ 0x003a, 0x6870, 0xa005, 0x0130, 0x6007, 0x003b, 0x6874, 0x602a,
+ 0x6878, 0x6012, 0x6003, 0x0001, 0x080c, 0x6c8d, 0x080c, 0x7173,
+ 0x00ee, 0x0804, 0x61ca, 0x2061, 0xb874, 0x6000, 0xd084, 0x0190,
+ 0xd08c, 0x1904, 0x6340, 0x0126, 0x2091, 0x8000, 0x6204, 0x8210,
+ 0x0220, 0x6206, 0x012e, 0x0804, 0x6340, 0x012e, 0x6853, 0x0016,
+ 0x0804, 0x6339, 0x6853, 0x0007, 0x0804, 0x6339, 0x6834, 0x8007,
+ 0xa084, 0x00ff, 0x1118, 0x080c, 0x5f22, 0x0078, 0x2030, 0x8001,
+ 0x1120, 0x7007, 0x0001, 0x0051, 0x0040, 0x7007, 0x0006, 0x7012,
+ 0x2d00, 0x7016, 0x701a, 0x704b, 0x626e, 0x0005, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0xa03e, 0x2009, 0xb531, 0x210c, 0x81ff, 0x1904,
+ 0x62ec, 0x2009, 0xb50c, 0x210c, 0xd194, 0x1904, 0x6316, 0x6848,
+ 0x2070, 0xae82, 0xbd00, 0x0a04, 0x62e0, 0x2001, 0xb517, 0x2004,
+ 0xae02, 0x1a04, 0x62e0, 0x711c, 0xa186, 0x0006, 0x1904, 0x62cf,
+ 0x7018, 0xa005, 0x0904, 0x62ec, 0x2004, 0xd0e4, 0x1904, 0x6311,
+ 0x2061, 0xb874, 0x6100, 0xa184, 0x0301, 0xa086, 0x0001, 0x1550,
+ 0x7020, 0xd0dc, 0x1904, 0x6319, 0x6853, 0x0000, 0x6803, 0x0000,
+ 0x2d08, 0x7010, 0xa005, 0x1158, 0x7112, 0x684c, 0xd0f4, 0x1904,
+ 0x631c, 0x2e60, 0x080c, 0x6a76, 0x012e, 0x00ee, 0x0005, 0x2068,
+ 0x6800, 0xa005, 0x1de0, 0x6902, 0x2168, 0x684c, 0xd0f4, 0x1904,
+ 0x631c, 0x012e, 0x00ee, 0x0005, 0x012e, 0x00ee, 0x6853, 0x0006,
+ 0x0804, 0x6339, 0xd184, 0x0dc0, 0xd1c4, 0x11a8, 0x00b8, 0x6944,
+ 0xa18c, 0xff00, 0x810f, 0x080c, 0x4fa9, 0x15d8, 0x6000, 0xd0e4,
+ 0x15c0, 0x711c, 0xa186, 0x0007, 0x1118, 0x6853, 0x0002, 0x0498,
+ 0x6853, 0x0008, 0x0480, 0x6853, 0x000e, 0x0468, 0x6853, 0x0017,
+ 0x0450, 0x6853, 0x0035, 0x0438, 0x2001, 0xb572, 0x2004, 0xd0fc,
+ 0x01e8, 0x6848, 0x2070, 0xae82, 0xbd00, 0x02c0, 0x605c, 0xae02,
+ 0x12a8, 0x711c, 0xa186, 0x0006, 0x1188, 0x7018, 0xa005, 0x0170,
+ 0x2004, 0xd0bc, 0x0158, 0x2039, 0x0001, 0x7000, 0xa086, 0x0007,
+ 0x1904, 0x6279, 0x7003, 0x0002, 0x0804, 0x6279, 0x6853, 0x0028,
+ 0x0010, 0x6853, 0x0029, 0x012e, 0x00ee, 0x0418, 0x6853, 0x002a,
+ 0x0cd0, 0x6853, 0x0045, 0x0cb8, 0x2e60, 0x2019, 0x0002, 0x6017,
+ 0x0014, 0x080c, 0xace0, 0x012e, 0x00ee, 0x0005, 0x2009, 0x003e,
+ 0x0058, 0x2009, 0x0004, 0x0040, 0x2009, 0x0006, 0x0028, 0x2009,
+ 0x0016, 0x0010, 0x2009, 0x0001, 0x6854, 0xa084, 0xff00, 0xa105,
+ 0x6856, 0x0126, 0x2091, 0x8000, 0x080c, 0x5408, 0x012e, 0x0005,
+ 0x080c, 0x160f, 0x0005, 0x702c, 0x7130, 0x8108, 0xa102, 0x0230,
+ 0xa00e, 0x7034, 0x7072, 0x7038, 0x7076, 0x0058, 0x7070, 0xa080,
+ 0x0040, 0x7072, 0x1230, 0x7074, 0xa081, 0x0000, 0x7076, 0xa085,
+ 0x0001, 0x7932, 0x7132, 0x0005, 0x00d6, 0x080c, 0x6a6d, 0x00de,
+ 0x0005, 0x00d6, 0x00c6, 0x0036, 0x0026, 0x0016, 0x7007, 0x0001,
+ 0x6a44, 0xa282, 0x0004, 0x1a04, 0x63ac, 0xd284, 0x0170, 0x6a4c,
+ 0xa290, 0xb635, 0x2204, 0xa065, 0x6004, 0x05e0, 0x8007, 0xa084,
+ 0x00ff, 0xa084, 0x0006, 0x1108, 0x04a8, 0x2c10, 0x080c, 0x85c7,
+ 0x1118, 0x080c, 0x9ed6, 0x05a0, 0x621a, 0x6844, 0x0002, 0x638b,
+ 0x6390, 0x6393, 0x6399, 0x2019, 0x0002, 0x080c, 0xb065, 0x0060,
+ 0x080c, 0xaffc, 0x0048, 0x2019, 0x0002, 0x6950, 0x080c, 0xb017,
+ 0x0018, 0x6950, 0x080c, 0xaffc, 0x080c, 0x861d, 0x6857, 0x0000,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x5408, 0x012e, 0x001e, 0x002e,
+ 0x003e, 0x00ce, 0x00de, 0x0005, 0x6857, 0x0006, 0x0c88, 0x6857,
+ 0x0002, 0x0c70, 0x6857, 0x0005, 0x0c58, 0x6857, 0x0004, 0x0c40,
+ 0x6857, 0x0007, 0x0c28, 0x00d6, 0x2011, 0x0004, 0x2204, 0xa085,
+ 0x8002, 0x2012, 0x00de, 0x0005, 0x20e1, 0x0002, 0x3d08, 0x20e1,
+ 0x2000, 0x3d00, 0xa084, 0x7000, 0x0118, 0xa086, 0x1000, 0x1570,
+ 0x20e1, 0x0000, 0x3d00, 0xa094, 0xff00, 0x8217, 0xa084, 0xf000,
+ 0xa086, 0x3000, 0x1160, 0xa184, 0xff00, 0x8007, 0xa086, 0x0008,
+ 0x11e8, 0x080c, 0x2dbf, 0x11d0, 0x080c, 0x6603, 0x0098, 0x20e1,
+ 0x0004, 0x3d60, 0xd1bc, 0x1108, 0x3e60, 0xac84, 0x0007, 0x1170,
+ 0xac82, 0xbd00, 0x0258, 0x685c, 0xac02, 0x1240, 0x2009, 0x0047,
+ 0x080c, 0x864c, 0x7a1c, 0xd284, 0x1938, 0x0005, 0xa016, 0x080c,
+ 0x185e, 0x0cc0, 0x0cd8, 0x781c, 0xd08c, 0x0500, 0x0156, 0x0136,
+ 0x0146, 0x20e1, 0x3000, 0x3d20, 0x3e28, 0xa584, 0x0076, 0x1538,
+ 0xa484, 0x7000, 0xa086, 0x1000, 0x11a8, 0x080c, 0x647e, 0x01f8,
+ 0x20e1, 0x3000, 0x7828, 0x7828, 0x080c, 0x649a, 0x014e, 0x013e,
+ 0x015e, 0x2009, 0xb7e8, 0x2104, 0xa005, 0x1108, 0x0005, 0x080c,
+ 0x7173, 0x0ce0, 0xa484, 0x7000, 0x1548, 0x080c, 0x647e, 0x01d8,
+ 0x7000, 0xa084, 0xff00, 0xa086, 0x8100, 0x0d10, 0x00a0, 0xd5a4,
+ 0x0178, 0x0056, 0x0046, 0x080c, 0x1e6e, 0x080c, 0x24b0, 0x2001,
+ 0x0160, 0x2502, 0x2001, 0x0138, 0x2202, 0x004e, 0x005e, 0x0048,
+ 0x04a9, 0x6887, 0x0000, 0x080c, 0xb3df, 0x20e1, 0x3000, 0x7828,
+ 0x7828, 0x00b9, 0x014e, 0x013e, 0x015e, 0x0880, 0x0439, 0x1130,
+ 0x7000, 0xa084, 0xff00, 0xa086, 0x8100, 0x1d68, 0x080c, 0xb3df,
+ 0x20e1, 0x3000, 0x7828, 0x7828, 0x0056, 0x080c, 0x6874, 0x005e,
+ 0x0c40, 0x2001, 0xb50e, 0x2004, 0xd08c, 0x0178, 0x2001, 0xb500,
+ 0x2004, 0xa086, 0x0003, 0x1148, 0x0026, 0x0036, 0x2011, 0x8048,
+ 0x2518, 0x080c, 0x3ecc, 0x003e, 0x002e, 0x0005, 0xa484, 0x01ff,
+ 0x6886, 0xa005, 0x0160, 0xa080, 0x001f, 0xa084, 0x03f8, 0x80ac,
+ 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0x0005, 0x20a9,
+ 0x000c, 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0xa085,
+ 0x0001, 0x0ca0, 0x7000, 0xa084, 0xff00, 0xa08c, 0xf000, 0x8007,
+ 0xa196, 0x0000, 0x1118, 0x0804, 0x6708, 0x0005, 0xa196, 0x2000,
+ 0x1148, 0x6900, 0xa18e, 0x0001, 0x1118, 0x080c, 0x448f, 0x0ca8,
+ 0x0039, 0x0c98, 0xa196, 0x8000, 0x1d80, 0x080c, 0x67b4, 0x0c68,
+ 0x00c6, 0x6a84, 0x82ff, 0x0904, 0x65fd, 0x7110, 0xa18c, 0xff00,
+ 0x810f, 0xa196, 0x0001, 0x0120, 0xa196, 0x0023, 0x1904, 0x65fd,
+ 0xa08e, 0x0023, 0x1570, 0x080c, 0x684f, 0x0904, 0x65fd, 0x7124,
+ 0x610a, 0x7030, 0xa08e, 0x0200, 0x1150, 0x7034, 0xa005, 0x1904,
+ 0x65fd, 0x2009, 0x0015, 0x080c, 0x864c, 0x0804, 0x65fd, 0xa08e,
+ 0x0214, 0x0118, 0xa08e, 0x0210, 0x1130, 0x2009, 0x0015, 0x080c,
+ 0x864c, 0x0804, 0x65fd, 0xa08e, 0x0100, 0x1904, 0x65fd, 0x7034,
+ 0xa005, 0x1904, 0x65fd, 0x2009, 0x0016, 0x080c, 0x864c, 0x0804,
+ 0x65fd, 0xa08e, 0x0022, 0x1904, 0x65fd, 0x7030, 0xa08e, 0x0300,
+ 0x1580, 0x68d4, 0xd0a4, 0x0528, 0xc0b5, 0x68d6, 0x7100, 0xa18c,
+ 0x00ff, 0x6972, 0x7004, 0x6876, 0x00f6, 0x2079, 0x0100, 0x79e6,
+ 0x78ea, 0x0006, 0xa084, 0x00ff, 0x0016, 0x2008, 0x080c, 0x2847,
+ 0x7932, 0x7936, 0x001e, 0x000e, 0x00fe, 0x080c, 0x281d, 0x6952,
+ 0x703c, 0x00e6, 0x2071, 0x0140, 0x7086, 0x2071, 0xb500, 0x70a6,
+ 0x00ee, 0x7034, 0xa005, 0x1904, 0x65fd, 0x2009, 0x0017, 0x0804,
+ 0x65c3, 0xa08e, 0x0400, 0x1158, 0x7034, 0xa005, 0x1904, 0x65fd,
+ 0x68d4, 0xc0a5, 0x68d6, 0x2009, 0x0030, 0x0804, 0x65c3, 0xa08e,
+ 0x0500, 0x1140, 0x7034, 0xa005, 0x1904, 0x65fd, 0x2009, 0x0018,
+ 0x0804, 0x65c3, 0xa08e, 0x2010, 0x1120, 0x2009, 0x0019, 0x0804,
+ 0x65c3, 0xa08e, 0x2110, 0x1120, 0x2009, 0x001a, 0x0804, 0x65c3,
+ 0xa08e, 0x5200, 0x1140, 0x7034, 0xa005, 0x1904, 0x65fd, 0x2009,
+ 0x001b, 0x0804, 0x65c3, 0xa08e, 0x5000, 0x1140, 0x7034, 0xa005,
+ 0x1904, 0x65fd, 0x2009, 0x001c, 0x0804, 0x65c3, 0xa08e, 0x1300,
+ 0x1120, 0x2009, 0x0034, 0x0804, 0x65c3, 0xa08e, 0x1200, 0x1140,
+ 0x7034, 0xa005, 0x1904, 0x65fd, 0x2009, 0x0024, 0x0804, 0x65c3,
+ 0xa08c, 0xff00, 0xa18e, 0x2400, 0x1118, 0x2009, 0x002d, 0x04d8,
+ 0xa08c, 0xff00, 0xa18e, 0x5300, 0x1118, 0x2009, 0x002a, 0x0498,
+ 0xa08e, 0x0f00, 0x1118, 0x2009, 0x0020, 0x0468, 0xa08e, 0x5300,
+ 0x1108, 0x00d8, 0xa08e, 0x6104, 0x11c0, 0x2011, 0xbb8d, 0x8208,
+ 0x2204, 0xa082, 0x0004, 0x20a8, 0x95ac, 0x95ac, 0x2011, 0x8015,
+ 0x211c, 0x8108, 0x0046, 0x2124, 0x080c, 0x3ecc, 0x004e, 0x8108,
+ 0x1f04, 0x65a6, 0x2009, 0x0023, 0x0070, 0xa08e, 0x6000, 0x1118,
+ 0x2009, 0x003f, 0x0040, 0xa08e, 0x7800, 0x1118, 0x2009, 0x0045,
+ 0x0010, 0x2009, 0x001d, 0x0016, 0x2011, 0xbb83, 0x2204, 0x8211,
+ 0x220c, 0x080c, 0x281d, 0x1598, 0x080c, 0x4f4d, 0x1580, 0x6612,
+ 0x6516, 0x86ff, 0x01e8, 0x001e, 0x0016, 0xa186, 0x0017, 0x1158,
+ 0x6870, 0xa606, 0x11a8, 0x6874, 0xa506, 0xa084, 0xff00, 0x1180,
+ 0x6000, 0xc0f5, 0x6002, 0xa186, 0x0046, 0x1150, 0x6870, 0xa606,
+ 0x1138, 0x6874, 0xa506, 0xa084, 0xff00, 0x1110, 0x001e, 0x0068,
+ 0x00c6, 0x080c, 0x85c7, 0x0168, 0x001e, 0x611a, 0x601f, 0x0004,
+ 0x7120, 0x610a, 0x001e, 0x080c, 0x864c, 0x00ce, 0x0005, 0x001e,
+ 0x0ce0, 0x00ce, 0x0ce0, 0x00c6, 0x0046, 0x080c, 0x6657, 0x1904,
+ 0x6654, 0xa28e, 0x0033, 0x11e8, 0x080c, 0x684f, 0x0904, 0x6654,
+ 0x7124, 0x610a, 0x7030, 0xa08e, 0x0200, 0x1140, 0x7034, 0xa005,
+ 0x15d8, 0x2009, 0x0015, 0x080c, 0x864c, 0x04b0, 0xa08e, 0x0100,
+ 0x1598, 0x7034, 0xa005, 0x1580, 0x2009, 0x0016, 0x080c, 0x864c,
+ 0x0458, 0xa28e, 0x0032, 0x1540, 0x7030, 0xa08e, 0x1400, 0x1520,
+ 0x2009, 0x0038, 0x0016, 0x2011, 0xbb83, 0x2204, 0x8211, 0x220c,
+ 0x080c, 0x281d, 0x11c0, 0x080c, 0x4f4d, 0x11a8, 0x6612, 0x6516,
+ 0x00c6, 0x080c, 0x85c7, 0x0170, 0x001e, 0x611a, 0x080c, 0xa027,
+ 0x601f, 0x0004, 0x7120, 0x610a, 0x001e, 0x080c, 0x864c, 0x080c,
+ 0x7173, 0x0010, 0x00ce, 0x001e, 0x004e, 0x00ce, 0x0005, 0x00f6,
+ 0x00d6, 0x0026, 0x0016, 0x0136, 0x0146, 0x0156, 0x3c00, 0x0006,
+ 0x2079, 0x0030, 0x2069, 0x0200, 0x080c, 0x1f2d, 0x1590, 0x080c,
+ 0x1dd2, 0x05e0, 0x04f1, 0x1130, 0x7908, 0xa18c, 0x1fff, 0xa182,
+ 0x0011, 0x1688, 0x20a9, 0x000c, 0x20e1, 0x0000, 0x2ea0, 0x2099,
+ 0x020a, 0x53a5, 0x20e1, 0x2000, 0x2001, 0x020a, 0x2004, 0x7a0c,
+ 0x7808, 0xa080, 0x0007, 0xa084, 0x1ff8, 0x0419, 0x1120, 0xa08a,
+ 0x0140, 0x1a0c, 0x1515, 0x80ac, 0x20e1, 0x6000, 0x2099, 0x020a,
+ 0x53a5, 0x20e1, 0x7000, 0x6828, 0x6828, 0x7803, 0x0004, 0xa294,
+ 0x0070, 0x000e, 0x20e0, 0x015e, 0x014e, 0x013e, 0x001e, 0x002e,
+ 0x00de, 0x00fe, 0x0005, 0xa016, 0x080c, 0x185e, 0xa085, 0x0001,
+ 0x0c80, 0x0006, 0x2001, 0x0111, 0x2004, 0xa084, 0x0003, 0x000e,
+ 0x0005, 0x0046, 0x00e6, 0x00d6, 0x2028, 0x2130, 0xa696, 0x00ff,
+ 0x1198, 0xa596, 0xfffd, 0x1120, 0x2009, 0x007f, 0x0804, 0x6703,
+ 0xa596, 0xfffe, 0x1118, 0x2009, 0x007e, 0x04e8, 0xa596, 0xfffc,
+ 0x1118, 0x2009, 0x0080, 0x04b8, 0x2011, 0x0000, 0x2019, 0xb535,
+ 0x231c, 0xd3ac, 0x0138, 0x2021, 0x0000, 0x20a9, 0x00ff, 0x2071,
+ 0xb635, 0x0030, 0x2021, 0x0081, 0x20a9, 0x007e, 0x2071, 0xb6b6,
+ 0x2e1c, 0x83ff, 0x1128, 0x82ff, 0x1198, 0x2410, 0xc2fd, 0x0080,
+ 0x2368, 0x6f10, 0x0006, 0x2100, 0xa706, 0x000e, 0x6b14, 0x1120,
+ 0xa346, 0x1110, 0x2408, 0x0078, 0x87ff, 0x1110, 0x83ff, 0x0d58,
+ 0x8420, 0x8e70, 0x1f04, 0x66e0, 0x82ff, 0x1118, 0xa085, 0x0001,
+ 0x0018, 0xc2fc, 0x2208, 0xa006, 0x00de, 0x00ee, 0x004e, 0x0005,
+ 0xa084, 0x0007, 0x000a, 0x0005, 0x6714, 0x6714, 0x6714, 0x6861,
+ 0x6714, 0x6715, 0x672a, 0x679f, 0x0005, 0x7110, 0xd1bc, 0x0188,
+ 0x7120, 0x2160, 0xac8c, 0x0007, 0x1160, 0xac8a, 0xbd00, 0x0248,
+ 0x685c, 0xac02, 0x1230, 0x7124, 0x610a, 0x2009, 0x0046, 0x080c,
+ 0x864c, 0x0005, 0x00c6, 0xa484, 0x01ff, 0x0904, 0x677d, 0x7110,
+ 0xd1bc, 0x1904, 0x677d, 0x2011, 0xbb83, 0x2204, 0x8211, 0x220c,
+ 0x080c, 0x281d, 0x1904, 0x677d, 0x080c, 0x4f4d, 0x15f0, 0x6612,
+ 0x6516, 0x6000, 0xd0ec, 0x15c8, 0x6204, 0xa294, 0xff00, 0x8217,
+ 0xa286, 0x0006, 0x0148, 0x6204, 0xa294, 0x00ff, 0xa286, 0x0006,
+ 0x11a0, 0xa295, 0x0600, 0x6206, 0x00c6, 0x080c, 0x85c7, 0x001e,
+ 0x0530, 0x611a, 0x601f, 0x0006, 0x7120, 0x610a, 0x7130, 0x6152,
+ 0x2009, 0x0044, 0x080c, 0x864c, 0x00c0, 0x00c6, 0x080c, 0x85c7,
+ 0x001e, 0x0198, 0x611a, 0x601f, 0x0004, 0x7120, 0x610a, 0xa286,
+ 0x0004, 0x1118, 0x6007, 0x0005, 0x0010, 0x6007, 0x0001, 0x6003,
+ 0x0001, 0x080c, 0x6cd3, 0x080c, 0x7173, 0x00ce, 0x0005, 0x2001,
+ 0xb50d, 0x2004, 0xd0ec, 0x0120, 0x2011, 0x8049, 0x080c, 0x3ecc,
+ 0x00c6, 0x080c, 0x9ed6, 0x001e, 0x0d80, 0x611a, 0x601f, 0x0006,
+ 0x7120, 0x610a, 0x7130, 0x6152, 0x6013, 0x0300, 0x6003, 0x0001,
+ 0x6007, 0x0041, 0x080c, 0x6c8d, 0x080c, 0x7173, 0x08f0, 0x7110,
+ 0xd1bc, 0x0188, 0x7020, 0x2060, 0xac84, 0x0007, 0x1160, 0xac82,
+ 0xbd00, 0x0248, 0x685c, 0xac02, 0x1230, 0x7124, 0x610a, 0x2009,
+ 0x0045, 0x080c, 0x864c, 0x0005, 0x0006, 0x080c, 0x2dbf, 0x000e,
+ 0x1168, 0x7110, 0xa18c, 0xff00, 0x810f, 0xa18e, 0x0000, 0x1130,
+ 0xa084, 0x000f, 0xa08a, 0x0006, 0x1208, 0x000b, 0x0005, 0x67cd,
+ 0x67ce, 0x67cd, 0x67cd, 0x6837, 0x6843, 0x0005, 0x7110, 0xd1bc,
+ 0x0120, 0x702c, 0xd084, 0x0904, 0x6836, 0x700c, 0x7108, 0x080c,
+ 0x281d, 0x1904, 0x6836, 0x080c, 0x4f4d, 0x1904, 0x6836, 0x6612,
+ 0x6516, 0x6204, 0x7110, 0xd1bc, 0x01f8, 0xa28c, 0x00ff, 0xa186,
+ 0x0004, 0x0118, 0xa186, 0x0006, 0x15c8, 0x00c6, 0x080c, 0x684f,
+ 0x00ce, 0x0904, 0x6836, 0x00c6, 0x080c, 0x85c7, 0x001e, 0x05f0,
+ 0x611a, 0x080c, 0xa027, 0x601f, 0x0002, 0x7120, 0x610a, 0x2009,
+ 0x0088, 0x080c, 0x864c, 0x0490, 0xa28c, 0x00ff, 0xa186, 0x0006,
+ 0x0160, 0xa186, 0x0004, 0x0148, 0xa294, 0xff00, 0x8217, 0xa286,
+ 0x0004, 0x0118, 0xa286, 0x0006, 0x1188, 0x00c6, 0x080c, 0x85c7,
+ 0x001e, 0x01e0, 0x611a, 0x080c, 0xa027, 0x601f, 0x0005, 0x7120,
+ 0x610a, 0x2009, 0x0088, 0x080c, 0x864c, 0x0080, 0x00c6, 0x080c,
+ 0x85c7, 0x001e, 0x0158, 0x611a, 0x080c, 0xa027, 0x601f, 0x0004,
+ 0x7120, 0x610a, 0x2009, 0x0001, 0x080c, 0x864c, 0x0005, 0x7110,
+ 0xd1bc, 0x0140, 0x00a1, 0x0130, 0x7124, 0x610a, 0x2009, 0x0089,
+ 0x080c, 0x864c, 0x0005, 0x7110, 0xd1bc, 0x0140, 0x0041, 0x0130,
+ 0x7124, 0x610a, 0x2009, 0x008a, 0x080c, 0x864c, 0x0005, 0x7020,
+ 0x2060, 0xac84, 0x0007, 0x1158, 0xac82, 0xbd00, 0x0240, 0x2001,
+ 0xb517, 0x2004, 0xac02, 0x1218, 0xa085, 0x0001, 0x0005, 0xa006,
+ 0x0ce8, 0x7110, 0xd1bc, 0x1178, 0x7024, 0x2060, 0xac84, 0x0007,
+ 0x1150, 0xac82, 0xbd00, 0x0238, 0x685c, 0xac02, 0x1220, 0x2009,
+ 0x0051, 0x080c, 0x864c, 0x0005, 0x2031, 0x0105, 0x0069, 0x0005,
+ 0x2031, 0x0206, 0x0049, 0x0005, 0x2031, 0x0207, 0x0029, 0x0005,
+ 0x2031, 0x0213, 0x0009, 0x0005, 0x00c6, 0x00d6, 0x00f6, 0x7000,
+ 0xa084, 0xf000, 0xa086, 0xc000, 0x05b0, 0x080c, 0x85c7, 0x0598,
+ 0x0066, 0x00c6, 0x0046, 0x2011, 0xbb83, 0x2204, 0x8211, 0x220c,
+ 0x080c, 0x281d, 0x1580, 0x080c, 0x4f4d, 0x1568, 0x6612, 0x6516,
+ 0x2c00, 0x004e, 0x00ce, 0x601a, 0x080c, 0xa027, 0x080c, 0x15f8,
+ 0x01f0, 0x2d00, 0x6056, 0x6803, 0x0000, 0x6837, 0x0000, 0x6c3a,
+ 0xadf8, 0x000f, 0x20a9, 0x000e, 0x2fa0, 0x2e98, 0x53a3, 0x006e,
+ 0x6612, 0x6007, 0x003e, 0x601f, 0x0001, 0x6003, 0x0001, 0x080c,
+ 0x6cd3, 0x080c, 0x7173, 0x00fe, 0x00de, 0x00ce, 0x0005, 0x080c,
+ 0x861d, 0x006e, 0x0cc0, 0x004e, 0x00ce, 0x0cc8, 0x2071, 0xb7f3,
+ 0x7003, 0x0003, 0x700f, 0x0361, 0xa006, 0x701a, 0x7076, 0x7012,
+ 0x7017, 0xbd00, 0x7007, 0x0000, 0x7026, 0x702b, 0x7d91, 0x7032,
+ 0x7037, 0x7df1, 0x703b, 0xffff, 0x703f, 0xffff, 0x7042, 0x7047,
+ 0x444b, 0x704a, 0x705b, 0x6a2b, 0x2001, 0xb7a1, 0x2003, 0x0003,
+ 0x2001, 0xb7a3, 0x2003, 0x0100, 0x3a00, 0xa084, 0x0005, 0x706e,
+ 0x0005, 0x2071, 0xb7f3, 0x1d04, 0x698b, 0x2091, 0x6000, 0x700c,
+ 0x8001, 0x700e, 0x1518, 0x700f, 0x0361, 0x7007, 0x0001, 0x0126,
+ 0x2091, 0x8000, 0x7040, 0xa00d, 0x0128, 0x8109, 0x7142, 0x1110,
+ 0x7044, 0x080f, 0x00c6, 0x2061, 0xb500, 0x6034, 0x00ce, 0xd0cc,
+ 0x0180, 0x3a00, 0xa084, 0x0005, 0x726c, 0xa216, 0x0150, 0x706e,
+ 0x2011, 0x8043, 0x2018, 0x080c, 0x3ecc, 0x0018, 0x0126, 0x2091,
+ 0x8000, 0x7024, 0xa00d, 0x0188, 0x7020, 0x8001, 0x7022, 0x1168,
+ 0x7023, 0x0009, 0x8109, 0x7126, 0xa186, 0x03e8, 0x1110, 0x7028,
+ 0x080f, 0x81ff, 0x1110, 0x7028, 0x080f, 0x7030, 0xa00d, 0x0180,
+ 0x702c, 0x8001, 0x702e, 0x1160, 0x702f, 0x0009, 0x8109, 0x7132,
+ 0x0128, 0xa184, 0x007f, 0x090c, 0x7e36, 0x0010, 0x7034, 0x080f,
+ 0x7038, 0xa005, 0x0118, 0x0310, 0x8001, 0x703a, 0x703c, 0xa005,
+ 0x0118, 0x0310, 0x8001, 0x703e, 0x704c, 0xa00d, 0x0168, 0x7048,
+ 0x8001, 0x704a, 0x1148, 0x704b, 0x0009, 0x8109, 0x714e, 0x1120,
+ 0x7150, 0x714e, 0x7058, 0x080f, 0x7018, 0xa00d, 0x01d8, 0x0016,
+ 0x7074, 0xa00d, 0x0158, 0x7070, 0x8001, 0x7072, 0x1138, 0x7073,
+ 0x0009, 0x8109, 0x7176, 0x1110, 0x7078, 0x080f, 0x001e, 0x7008,
+ 0x8001, 0x700a, 0x1138, 0x700b, 0x0009, 0x8109, 0x711a, 0x1110,
+ 0x701c, 0x080f, 0x012e, 0x7004, 0x0002, 0x69b1, 0x69b2, 0x69ca,
+ 0x00e6, 0x2071, 0xb7f3, 0x7018, 0xa005, 0x1120, 0x711a, 0x721e,
+ 0x700b, 0x0009, 0x00ee, 0x0005, 0x00e6, 0x0006, 0x2071, 0xb7f3,
+ 0x701c, 0xa206, 0x1110, 0x701a, 0x701e, 0x000e, 0x00ee, 0x0005,
+ 0x00e6, 0x2071, 0xb7f3, 0x6088, 0xa102, 0x0208, 0x618a, 0x00ee,
+ 0x0005, 0x0005, 0x7110, 0x080c, 0x4fa9, 0x1158, 0x6088, 0x8001,
+ 0x0240, 0x608a, 0x1130, 0x0126, 0x2091, 0x8000, 0x080c, 0x7173,
+ 0x012e, 0x8108, 0xa182, 0x00ff, 0x0218, 0xa00e, 0x7007, 0x0002,
+ 0x7112, 0x0005, 0x7014, 0x2060, 0x0126, 0x2091, 0x8000, 0x603c,
+ 0xa005, 0x0128, 0x8001, 0x603e, 0x1110, 0x080c, 0x9f15, 0x6014,
+ 0xa005, 0x0500, 0x8001, 0x6016, 0x11e8, 0x611c, 0xa186, 0x0003,
+ 0x0118, 0xa186, 0x0006, 0x11a0, 0x6010, 0x2068, 0x6854, 0xa08a,
+ 0x199a, 0x0270, 0xa082, 0x1999, 0x6856, 0xa08a, 0x199a, 0x0210,
+ 0x2001, 0x1999, 0x8003, 0x800b, 0x810b, 0xa108, 0x6116, 0x0010,
+ 0x080c, 0x99e5, 0x012e, 0xac88, 0x0018, 0x7116, 0x2001, 0xed00,
+ 0xa102, 0x0220, 0x7017, 0xbd00, 0x7007, 0x0000, 0x0005, 0x00e6,
+ 0x2071, 0xb7f3, 0x7027, 0x07d0, 0x7023, 0x0009, 0x00ee, 0x0005,
+ 0x2001, 0xb7fc, 0x2003, 0x0000, 0x0005, 0x00e6, 0x2071, 0xb7f3,
+ 0x7132, 0x702f, 0x0009, 0x00ee, 0x0005, 0x2011, 0xb7ff, 0x2013,
+ 0x0000, 0x0005, 0x00e6, 0x2071, 0xb7f3, 0x711a, 0x721e, 0x700b,
+ 0x0009, 0x00ee, 0x0005, 0x00c6, 0x0026, 0x7054, 0x8000, 0x7056,
+ 0x2061, 0xb7a1, 0x6008, 0xa086, 0x0000, 0x0158, 0x7068, 0x6032,
+ 0x7064, 0x602e, 0x7060, 0x602a, 0x705c, 0x6026, 0x2c10, 0x080c,
+ 0x1643, 0x002e, 0x00ce, 0x0005, 0x0006, 0x0016, 0x00c6, 0x00d6,
+ 0x00e6, 0x00f6, 0x080c, 0x68f9, 0x00fe, 0x00ee, 0x00de, 0x00ce,
+ 0x001e, 0x000e, 0x0005, 0x00e6, 0x2071, 0xb7f3, 0x7176, 0x727a,
+ 0x7073, 0x0009, 0x00ee, 0x0005, 0x00e6, 0x0006, 0x2071, 0xb7f3,
+ 0x7078, 0xa206, 0x1110, 0x7076, 0x707a, 0x000e, 0x00ee, 0x0005,
+ 0x00c6, 0x2061, 0xb874, 0x00ce, 0x0005, 0xa184, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa080, 0xb874, 0x2060, 0x0005, 0x6854, 0xa08a,
+ 0x199a, 0x0210, 0x2001, 0x1999, 0xa005, 0x1150, 0x00c6, 0x2061,
+ 0xb874, 0x6014, 0x00ce, 0xa005, 0x1138, 0x2001, 0x001e, 0x0020,
+ 0xa08e, 0xffff, 0x1108, 0xa006, 0x8003, 0x800b, 0x810b, 0xa108,
+ 0x6116, 0x684c, 0xa08c, 0x00c0, 0xa18e, 0x00c0, 0x05e8, 0xd0b4,
+ 0x1138, 0xd0bc, 0x1550, 0x2009, 0x0006, 0x080c, 0x6af1, 0x0005,
+ 0xd0fc, 0x0138, 0xa084, 0x0003, 0x0120, 0xa086, 0x0003, 0x1904,
+ 0x6aeb, 0x6020, 0xd0d4, 0x0130, 0xc0d4, 0x6022, 0x6860, 0x602a,
+ 0x685c, 0x602e, 0x2009, 0xb574, 0x2104, 0xd084, 0x0138, 0x87ff,
+ 0x1120, 0x2009, 0x0042, 0x080c, 0x864c, 0x0005, 0x87ff, 0x1120,
+ 0x2009, 0x0043, 0x080c, 0x864c, 0x0005, 0xd0fc, 0x0130, 0xa084,
+ 0x0003, 0x0118, 0xa086, 0x0003, 0x11f0, 0x87ff, 0x1120, 0x2009,
+ 0x0042, 0x080c, 0x864c, 0x0005, 0xd0fc, 0x0160, 0xa084, 0x0003,
+ 0xa08e, 0x0002, 0x0148, 0x87ff, 0x1120, 0x2009, 0x0041, 0x080c,
+ 0x864c, 0x0005, 0x0061, 0x0ce8, 0x87ff, 0x1dd8, 0x2009, 0x0043,
+ 0x080c, 0x864c, 0x0cb0, 0x2009, 0x0004, 0x0019, 0x0005, 0x2009,
+ 0x0001, 0x00d6, 0x6010, 0xa0ec, 0xf000, 0x0510, 0x2068, 0x6952,
+ 0x6800, 0x6012, 0xa186, 0x0001, 0x1188, 0x694c, 0xa18c, 0x8100,
+ 0xa18e, 0x8100, 0x1158, 0x00c6, 0x2061, 0xb874, 0x6200, 0xd28c,
+ 0x1120, 0x6204, 0x8210, 0x0208, 0x6206, 0x00ce, 0x080c, 0x5408,
+ 0x6010, 0xa06d, 0x0076, 0x2039, 0x0000, 0x190c, 0x6a76, 0x007e,
+ 0x00de, 0x0005, 0x0156, 0x00c6, 0x2061, 0xb874, 0x6000, 0x81ff,
+ 0x0110, 0xa205, 0x0008, 0xa204, 0x6002, 0x00ce, 0x015e, 0x0005,
+ 0x6800, 0xd08c, 0x1138, 0x6808, 0xa005, 0x0120, 0x8001, 0x680a,
+ 0xa085, 0x0001, 0x0005, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086,
+ 0x818e, 0x1208, 0xa200, 0x1f04, 0x6b37, 0x8086, 0x818e, 0x0005,
+ 0x0156, 0x20a9, 0x0010, 0xa005, 0x01b8, 0xa11a, 0x12a8, 0x8213,
+ 0x818d, 0x0228, 0xa11a, 0x1220, 0x1f04, 0x6b47, 0x0028, 0xa11a,
+ 0x2308, 0x8210, 0x1f04, 0x6b47, 0x0006, 0x3200, 0xa084, 0xefff,
+ 0x2080, 0x000e, 0x015e, 0x0005, 0x0006, 0x3200, 0xa085, 0x1000,
+ 0x0cb8, 0x0126, 0x2091, 0x2800, 0x2079, 0xb7e0, 0x012e, 0x00d6,
+ 0x2069, 0xb7e0, 0x6803, 0x0005, 0x2069, 0x0004, 0x2d04, 0xa085,
+ 0x8001, 0x206a, 0x00de, 0x0005, 0x00c6, 0x6027, 0x0001, 0x7804,
+ 0xa084, 0x0007, 0x0002, 0x6b85, 0x6ba6, 0x6bf9, 0x6b8b, 0x6ba6,
+ 0x6b85, 0x6b83, 0x6b83, 0x080c, 0x1515, 0x080c, 0x6a10, 0x080c,
+ 0x7173, 0x00ce, 0x0005, 0x62c0, 0x82ff, 0x1110, 0x00ce, 0x0005,
+ 0x2011, 0x4adc, 0x080c, 0x699c, 0x7828, 0xa092, 0x00c8, 0x1228,
+ 0x8000, 0x782a, 0x080c, 0x4b16, 0x0c88, 0x080c, 0x4adc, 0x7807,
+ 0x0003, 0x7827, 0x0000, 0x782b, 0x0000, 0x0c40, 0x080c, 0x6a10,
+ 0x3c00, 0x0006, 0x2011, 0x0209, 0x20e1, 0x4000, 0x2214, 0x000e,
+ 0x20e0, 0x82ff, 0x0178, 0x62c0, 0x82ff, 0x1160, 0x782b, 0x0000,
+ 0x7824, 0xa065, 0x090c, 0x1515, 0x2009, 0x0013, 0x080c, 0x864c,
+ 0x00ce, 0x0005, 0x3900, 0xa082, 0xb92c, 0x1210, 0x080c, 0x8332,
+ 0x00c6, 0x7824, 0xa065, 0x090c, 0x1515, 0x7804, 0xa086, 0x0004,
+ 0x0904, 0x6c39, 0x7828, 0xa092, 0x2710, 0x1230, 0x8000, 0x782a,
+ 0x00ce, 0x080c, 0x7d6d, 0x0c20, 0x6104, 0xa186, 0x0003, 0x1188,
+ 0x00e6, 0x2071, 0xb500, 0x70e0, 0x00ee, 0xd08c, 0x0150, 0x00c6,
+ 0x00e6, 0x2061, 0x0100, 0x2071, 0xb500, 0x080c, 0x4b1f, 0x00ee,
+ 0x00ce, 0x080c, 0xb444, 0x2009, 0x0014, 0x080c, 0x864c, 0x00ce,
+ 0x0838, 0x2001, 0xb7fc, 0x2003, 0x0000, 0x62c0, 0x82ff, 0x1160,
+ 0x782b, 0x0000, 0x7824, 0xa065, 0x090c, 0x1515, 0x2009, 0x0013,
+ 0x080c, 0x86a0, 0x00ce, 0x0005, 0x00c6, 0x00d6, 0x3900, 0xa082,
+ 0xb92c, 0x1210, 0x080c, 0x8332, 0x7824, 0xa005, 0x090c, 0x1515,
+ 0x781c, 0xa06d, 0x090c, 0x1515, 0x6800, 0xc0dc, 0x6802, 0x7924,
+ 0x2160, 0x080c, 0x861d, 0x693c, 0x81ff, 0x090c, 0x1515, 0x8109,
+ 0x693e, 0x6854, 0xa015, 0x0110, 0x7a1e, 0x0010, 0x7918, 0x791e,
+ 0x7807, 0x0000, 0x7827, 0x0000, 0x00de, 0x00ce, 0x080c, 0x7173,
+ 0x0888, 0x6104, 0xa186, 0x0002, 0x0128, 0xa186, 0x0004, 0x0110,
+ 0x0804, 0x6bd2, 0x7808, 0xac06, 0x0904, 0x6bd2, 0x080c, 0x7090,
+ 0x080c, 0x6cd3, 0x00ce, 0x080c, 0x7173, 0x0804, 0x6bc0, 0x00c6,
+ 0x6027, 0x0002, 0x62c8, 0x60c4, 0xa205, 0x1178, 0x793c, 0xa1e5,
+ 0x0000, 0x0130, 0x2009, 0x0049, 0x080c, 0x864c, 0x00ce, 0x0005,
+ 0x2011, 0xb7ff, 0x2013, 0x0000, 0x0cc8, 0x3908, 0xa192, 0xb92c,
+ 0x1210, 0x080c, 0x8332, 0x793c, 0x81ff, 0x0d90, 0x7944, 0xa192,
+ 0x7530, 0x12b8, 0x8108, 0x7946, 0x793c, 0xa188, 0x0007, 0x210c,
+ 0xa18e, 0x0006, 0x1138, 0x6014, 0xa084, 0x0184, 0xa085, 0x0012,
+ 0x6016, 0x08e0, 0x6014, 0xa084, 0x0184, 0xa085, 0x0016, 0x6016,
+ 0x08a8, 0x7848, 0xc085, 0x784a, 0x0888, 0x0006, 0x0016, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x600f, 0x0000, 0x2c08, 0x2061, 0xb7e0,
+ 0x6020, 0x8000, 0x6022, 0x6010, 0xa005, 0x0148, 0xa080, 0x0003,
+ 0x2102, 0x6112, 0x012e, 0x00ce, 0x001e, 0x000e, 0x0005, 0x6116,
+ 0x6112, 0x0cc0, 0x00d6, 0x2069, 0xb7e0, 0x6000, 0xd0d4, 0x0168,
+ 0x6820, 0x8000, 0x6822, 0xa086, 0x0001, 0x1110, 0x2c00, 0x681e,
+ 0x6804, 0xa084, 0x0007, 0x0804, 0x7179, 0xc0d5, 0x6002, 0x6818,
+ 0xa005, 0x0158, 0x6056, 0x605b, 0x0000, 0x0006, 0x2c00, 0x681a,
+ 0x00de, 0x685a, 0x2069, 0xb7e0, 0x0c18, 0x6056, 0x605a, 0x2c00,
+ 0x681a, 0x681e, 0x08e8, 0x0006, 0x0016, 0x00c6, 0x0126, 0x2091,
+ 0x8000, 0x600f, 0x0000, 0x2c08, 0x2061, 0xb7e0, 0x6020, 0x8000,
+ 0x6022, 0x6008, 0xa005, 0x0148, 0xa080, 0x0003, 0x2102, 0x610a,
+ 0x012e, 0x00ce, 0x001e, 0x000e, 0x0005, 0x610e, 0x610a, 0x0cc0,
+ 0x00c6, 0x600f, 0x0000, 0x2c08, 0x2061, 0xb7e0, 0x6034, 0xa005,
+ 0x0130, 0xa080, 0x0003, 0x2102, 0x6136, 0x00ce, 0x0005, 0x613a,
+ 0x6136, 0x0cd8, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0076, 0x0066,
+ 0x0056, 0x0036, 0x0026, 0x0016, 0x0006, 0x0126, 0xa02e, 0x2071,
+ 0xb7e0, 0x7638, 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff, 0x0904,
+ 0x6d7b, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x1904, 0x6d76,
+ 0x87ff, 0x0120, 0x6050, 0xa106, 0x1904, 0x6d76, 0x703c, 0xac06,
+ 0x1190, 0x0036, 0x2019, 0x0001, 0x080c, 0x7fe4, 0x7033, 0x0000,
+ 0x703f, 0x0000, 0x7043, 0x0000, 0x7047, 0x0000, 0x704b, 0x0000,
+ 0x003e, 0x2029, 0x0001, 0x7038, 0xac36, 0x1110, 0x660c, 0x763a,
+ 0x7034, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7036,
+ 0x0010, 0x7037, 0x0000, 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x080c, 0x9c5a, 0x01c8,
+ 0x6010, 0x2068, 0x601c, 0xa086, 0x0003, 0x1580, 0x6837, 0x0103,
+ 0x6b4a, 0x6847, 0x0000, 0x0016, 0x0036, 0x0076, 0x080c, 0x9ecc,
+ 0x080c, 0xb380, 0x080c, 0x5408, 0x007e, 0x003e, 0x001e, 0x080c,
+ 0x9e11, 0x080c, 0x9e1d, 0x00ce, 0x0804, 0x6d16, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0x6d16, 0x85ff, 0x0120, 0x0036, 0x080c, 0x7230,
+ 0x003e, 0x012e, 0x000e, 0x001e, 0x002e, 0x003e, 0x005e, 0x006e,
+ 0x007e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601c, 0xa086,
+ 0x0006, 0x1158, 0x0016, 0x0036, 0x0076, 0x080c, 0xb380, 0x080c,
+ 0xb099, 0x007e, 0x003e, 0x001e, 0x08a0, 0x601c, 0xa086, 0x000a,
+ 0x0904, 0x6d60, 0x0804, 0x6d5e, 0x0006, 0x0066, 0x00c6, 0x00d6,
+ 0x00f6, 0x2031, 0x0000, 0x0126, 0x2091, 0x8000, 0x2079, 0xb7e0,
+ 0x7838, 0xa065, 0x0568, 0x600c, 0x0006, 0x600f, 0x0000, 0x783c,
+ 0xac06, 0x1180, 0x0036, 0x2019, 0x0001, 0x080c, 0x7fe4, 0x7833,
+ 0x0000, 0x783f, 0x0000, 0x7843, 0x0000, 0x7847, 0x0000, 0x784b,
+ 0x0000, 0x003e, 0x080c, 0x9c5a, 0x0178, 0x6010, 0x2068, 0x601c,
+ 0xa086, 0x0003, 0x11b0, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000,
+ 0x080c, 0x5408, 0x080c, 0x9e11, 0x080c, 0x9e1d, 0x000e, 0x0888,
+ 0x7e3a, 0x7e36, 0x012e, 0x00fe, 0x00de, 0x00ce, 0x006e, 0x000e,
+ 0x0005, 0x601c, 0xa086, 0x0006, 0x1118, 0x080c, 0xb099, 0x0c60,
+ 0x601c, 0xa086, 0x000a, 0x0d08, 0x08f0, 0x0016, 0x0026, 0x0086,
+ 0x2041, 0x0000, 0x0099, 0x080c, 0x6ec3, 0x008e, 0x002e, 0x001e,
+ 0x0005, 0x00f6, 0x0126, 0x2079, 0xb7e0, 0x2091, 0x8000, 0x080c,
+ 0x6f50, 0x080c, 0x6fc2, 0x012e, 0x00fe, 0x0005, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x0066, 0x0016, 0x0006, 0x0126, 0x2091, 0x8000,
+ 0x2071, 0xb7e0, 0x7614, 0x2660, 0x2678, 0x8cff, 0x0904, 0x6e99,
+ 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x1904, 0x6e94, 0x88ff,
+ 0x0120, 0x6050, 0xa106, 0x1904, 0x6e94, 0x7024, 0xac06, 0x1538,
+ 0x2069, 0x0100, 0x68c0, 0xa005, 0x01f0, 0x080c, 0x6a10, 0x080c,
+ 0x7d7a, 0x68c3, 0x0000, 0x080c, 0x824d, 0x7027, 0x0000, 0x0036,
+ 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803, 0x0100,
+ 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827,
+ 0x0001, 0x003e, 0x0020, 0x6003, 0x0009, 0x630a, 0x04e8, 0x7014,
+ 0xac36, 0x1110, 0x660c, 0x7616, 0x7010, 0xac36, 0x1140, 0x2c00,
+ 0xaf36, 0x0118, 0x2f00, 0x7012, 0x0010, 0x7013, 0x0000, 0x660c,
+ 0x0066, 0x2c00, 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f,
+ 0x0000, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x01b8, 0x601c, 0xa086,
+ 0x0003, 0x1540, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x0016,
+ 0x0036, 0x0086, 0x080c, 0x9ecc, 0x080c, 0xb380, 0x080c, 0x5408,
+ 0x008e, 0x003e, 0x001e, 0x080c, 0x9e11, 0x080c, 0x9e1d, 0x080c,
+ 0x811e, 0x00ce, 0x0804, 0x6e1d, 0x2c78, 0x600c, 0x2060, 0x0804,
+ 0x6e1d, 0x012e, 0x000e, 0x001e, 0x006e, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x601c, 0xa086, 0x0006, 0x1158, 0x0016, 0x0036,
+ 0x0086, 0x080c, 0xb380, 0x080c, 0xb099, 0x008e, 0x003e, 0x001e,
+ 0x08e0, 0x601c, 0xa086, 0x0002, 0x1128, 0x6004, 0xa086, 0x0085,
+ 0x0908, 0x0898, 0x601c, 0xa086, 0x0005, 0x1978, 0x6004, 0xa086,
+ 0x0085, 0x0d20, 0x0850, 0x00c6, 0x0006, 0x0126, 0x2091, 0x8000,
+ 0xa280, 0xb635, 0x2004, 0xa065, 0x0904, 0x6f4c, 0x00f6, 0x00e6,
+ 0x00d6, 0x0066, 0x2071, 0xb7e0, 0x6654, 0x7018, 0xac06, 0x1108,
+ 0x761a, 0x701c, 0xac06, 0x1130, 0x86ff, 0x1118, 0x7018, 0x701e,
+ 0x0008, 0x761e, 0x6058, 0xa07d, 0x0108, 0x7e56, 0xa6ed, 0x0000,
+ 0x0110, 0x2f00, 0x685a, 0x6057, 0x0000, 0x605b, 0x0000, 0x6000,
+ 0xc0d4, 0xc0dc, 0x6002, 0x080c, 0x4ed4, 0x0904, 0x6f48, 0x7624,
+ 0x86ff, 0x05e8, 0xa680, 0x0004, 0x2004, 0xad06, 0x15c0, 0x00d6,
+ 0x2069, 0x0100, 0x68c0, 0xa005, 0x0548, 0x080c, 0x6a10, 0x080c,
+ 0x7d7a, 0x68c3, 0x0000, 0x080c, 0x824d, 0x7027, 0x0000, 0x0036,
+ 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803, 0x0100,
+ 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827,
+ 0x0001, 0x003e, 0x00de, 0x00c6, 0x603c, 0xa005, 0x0110, 0x8001,
+ 0x603e, 0x2660, 0x080c, 0x9e1d, 0x00ce, 0x0048, 0x00de, 0x00c6,
+ 0x2660, 0x6003, 0x0009, 0x630a, 0x00ce, 0x0804, 0x6ef3, 0x8dff,
+ 0x0158, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x9ecc,
+ 0x080c, 0xb380, 0x080c, 0x5408, 0x080c, 0x811e, 0x0804, 0x6ef3,
+ 0x006e, 0x00de, 0x00ee, 0x00fe, 0x012e, 0x000e, 0x00ce, 0x0005,
+ 0x0006, 0x0066, 0x00c6, 0x00d6, 0x2031, 0x0000, 0x7814, 0xa065,
+ 0x0904, 0x6fa2, 0x600c, 0x0006, 0x600f, 0x0000, 0x7824, 0xac06,
+ 0x1540, 0x2069, 0x0100, 0x68c0, 0xa005, 0x01f0, 0x080c, 0x6a10,
+ 0x080c, 0x7d7a, 0x68c3, 0x0000, 0x080c, 0x824d, 0x7827, 0x0000,
+ 0x0036, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803,
+ 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110,
+ 0x6827, 0x0001, 0x003e, 0x0028, 0x6003, 0x0009, 0x630a, 0x2c30,
+ 0x00b0, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0168, 0x601c, 0xa086,
+ 0x0003, 0x11b8, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c,
+ 0x5408, 0x080c, 0x9e11, 0x080c, 0x9e1d, 0x080c, 0x811e, 0x000e,
+ 0x0804, 0x6f57, 0x7e16, 0x7e12, 0x00de, 0x00ce, 0x006e, 0x000e,
+ 0x0005, 0x601c, 0xa086, 0x0006, 0x1118, 0x080c, 0xb099, 0x0c58,
+ 0x601c, 0xa086, 0x0002, 0x1128, 0x6004, 0xa086, 0x0085, 0x09d0,
+ 0x0c10, 0x601c, 0xa086, 0x0005, 0x19f0, 0x6004, 0xa086, 0x0085,
+ 0x0d60, 0x08c8, 0x0006, 0x0066, 0x00c6, 0x00d6, 0x7818, 0xa065,
+ 0x0904, 0x7028, 0x6054, 0x0006, 0x6057, 0x0000, 0x605b, 0x0000,
+ 0x6000, 0xc0d4, 0xc0dc, 0x6002, 0x080c, 0x4ed4, 0x0904, 0x7025,
+ 0x7e24, 0x86ff, 0x05e8, 0xa680, 0x0004, 0x2004, 0xad06, 0x15c0,
+ 0x00d6, 0x2069, 0x0100, 0x68c0, 0xa005, 0x0548, 0x080c, 0x6a10,
+ 0x080c, 0x7d7a, 0x68c3, 0x0000, 0x080c, 0x824d, 0x7827, 0x0000,
+ 0x0036, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803,
+ 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110,
+ 0x6827, 0x0001, 0x003e, 0x00de, 0x00c6, 0x603c, 0xa005, 0x0110,
+ 0x8001, 0x603e, 0x2660, 0x080c, 0x9e1d, 0x00ce, 0x0048, 0x00de,
+ 0x00c6, 0x2660, 0x6003, 0x0009, 0x630a, 0x00ce, 0x0804, 0x6fd4,
+ 0x8dff, 0x0138, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c,
+ 0x5408, 0x080c, 0x811e, 0x0804, 0x6fd4, 0x000e, 0x0804, 0x6fc7,
+ 0x781e, 0x781a, 0x00de, 0x00ce, 0x006e, 0x000e, 0x0005, 0x00e6,
+ 0x00d6, 0x0066, 0x6000, 0xd0dc, 0x01a0, 0x604c, 0xa06d, 0x0188,
+ 0x6848, 0xa606, 0x1170, 0x2071, 0xb7e0, 0x7024, 0xa035, 0x0148,
+ 0xa080, 0x0004, 0x2004, 0xad06, 0x1120, 0x6000, 0xc0dc, 0x6002,
+ 0x0021, 0x006e, 0x00de, 0x00ee, 0x0005, 0x00f6, 0x2079, 0x0100,
+ 0x78c0, 0xa005, 0x1138, 0x00c6, 0x2660, 0x6003, 0x0009, 0x630a,
+ 0x00ce, 0x04a0, 0x080c, 0x7d7a, 0x78c3, 0x0000, 0x080c, 0x824d,
+ 0x7027, 0x0000, 0x0036, 0x2079, 0x0140, 0x7b04, 0xa384, 0x1000,
+ 0x0120, 0x7803, 0x0100, 0x7803, 0x0000, 0x2079, 0x0100, 0x7824,
+ 0xd084, 0x0110, 0x7827, 0x0001, 0x080c, 0x824d, 0x003e, 0x080c,
+ 0x4ed4, 0x00c6, 0x603c, 0xa005, 0x0110, 0x8001, 0x603e, 0x2660,
+ 0x080c, 0x861d, 0x00ce, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000,
+ 0x080c, 0x9ecc, 0x080c, 0x5408, 0x080c, 0x811e, 0x00fe, 0x0005,
+ 0x00e6, 0x00c6, 0x2071, 0xb7e0, 0x7004, 0xa084, 0x0007, 0x0002,
+ 0x70a2, 0x70a5, 0x70bb, 0x70d4, 0x7111, 0x70a2, 0x70a0, 0x70a0,
+ 0x080c, 0x1515, 0x00ce, 0x00ee, 0x0005, 0x7024, 0xa065, 0x0148,
+ 0x7020, 0x8001, 0x7022, 0x600c, 0xa015, 0x0150, 0x7216, 0x600f,
+ 0x0000, 0x7007, 0x0000, 0x7027, 0x0000, 0x00ce, 0x00ee, 0x0005,
+ 0x7216, 0x7212, 0x0cb0, 0x6018, 0x2060, 0x080c, 0x4ed4, 0x6000,
+ 0xc0dc, 0x6002, 0x7020, 0x8001, 0x7022, 0x0120, 0x6054, 0xa015,
+ 0x0140, 0x721e, 0x7007, 0x0000, 0x7027, 0x0000, 0x00ce, 0x00ee,
+ 0x0005, 0x7218, 0x721e, 0x0cb0, 0x7024, 0xa065, 0x05b8, 0x700c,
+ 0xac06, 0x1160, 0x080c, 0x811e, 0x600c, 0xa015, 0x0120, 0x720e,
+ 0x600f, 0x0000, 0x0448, 0x720e, 0x720a, 0x0430, 0x7014, 0xac06,
+ 0x1160, 0x080c, 0x811e, 0x600c, 0xa015, 0x0120, 0x7216, 0x600f,
+ 0x0000, 0x00d0, 0x7216, 0x7212, 0x00b8, 0x601c, 0xa086, 0x0003,
+ 0x1198, 0x6018, 0x2060, 0x080c, 0x4ed4, 0x6000, 0xc0dc, 0x6002,
+ 0x080c, 0x811e, 0x701c, 0xa065, 0x0138, 0x6054, 0xa015, 0x0110,
+ 0x721e, 0x0010, 0x7218, 0x721e, 0x7027, 0x0000, 0x00ce, 0x00ee,
+ 0x0005, 0x7024, 0xa065, 0x0140, 0x080c, 0x811e, 0x600c, 0xa015,
+ 0x0150, 0x720e, 0x600f, 0x0000, 0x080c, 0x824d, 0x7027, 0x0000,
+ 0x00ce, 0x00ee, 0x0005, 0x720e, 0x720a, 0x0cb0, 0x00d6, 0x2069,
+ 0xb7e0, 0x6830, 0xa084, 0x0003, 0x0002, 0x7133, 0x7135, 0x7159,
+ 0x7131, 0x080c, 0x1515, 0x00de, 0x0005, 0x00c6, 0x6840, 0xa086,
+ 0x0001, 0x01b8, 0x683c, 0xa065, 0x0130, 0x600c, 0xa015, 0x0170,
+ 0x6a3a, 0x600f, 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x2011,
+ 0xb7ff, 0x2013, 0x0000, 0x00ce, 0x00de, 0x0005, 0x683a, 0x6836,
+ 0x0c90, 0x6843, 0x0000, 0x6838, 0xa065, 0x0d68, 0x6003, 0x0003,
+ 0x0c50, 0x00c6, 0x6843, 0x0000, 0x6847, 0x0000, 0x684b, 0x0000,
+ 0x683c, 0xa065, 0x0168, 0x600c, 0xa015, 0x0130, 0x6a3a, 0x600f,
+ 0x0000, 0x683f, 0x0000, 0x0020, 0x683f, 0x0000, 0x683a, 0x6836,
+ 0x00ce, 0x00de, 0x0005, 0x00d6, 0x2069, 0xb7e0, 0x6804, 0xa084,
+ 0x0007, 0x0002, 0x7184, 0x7220, 0x7220, 0x7220, 0x7220, 0x7222,
+ 0x7182, 0x7182, 0x080c, 0x1515, 0x6820, 0xa005, 0x1110, 0x00de,
+ 0x0005, 0x00c6, 0x680c, 0xa065, 0x0150, 0x6807, 0x0004, 0x6826,
+ 0x682b, 0x0000, 0x080c, 0x7272, 0x00ce, 0x00de, 0x0005, 0x6814,
+ 0xa065, 0x0150, 0x6807, 0x0001, 0x6826, 0x682b, 0x0000, 0x080c,
+ 0x7272, 0x00ce, 0x00de, 0x0005, 0x00e6, 0x0036, 0x6a1c, 0xa2f5,
+ 0x0000, 0x0904, 0x721c, 0x704c, 0xa00d, 0x0118, 0x7088, 0xa005,
+ 0x01a0, 0x7054, 0xa075, 0x0120, 0xa20e, 0x0904, 0x721c, 0x0028,
+ 0x6818, 0xa20e, 0x0904, 0x721c, 0x2070, 0x704c, 0xa00d, 0x0d88,
+ 0x7088, 0xa005, 0x1d70, 0x2e00, 0x681e, 0x733c, 0x7038, 0xa302,
+ 0x1e40, 0x080c, 0x85f4, 0x0904, 0x721c, 0x8318, 0x733e, 0x6112,
+ 0x2e10, 0x621a, 0xa180, 0x0014, 0x2004, 0xa084, 0x00ff, 0x605a,
+ 0xa180, 0x0014, 0x2003, 0x0000, 0xa180, 0x0015, 0x2004, 0xa08a,
+ 0x199a, 0x0210, 0x2001, 0x1999, 0x8003, 0x801b, 0x831b, 0xa318,
+ 0x6316, 0x003e, 0x00f6, 0x2c78, 0x71a0, 0x2001, 0xb535, 0x2004,
+ 0xd0ac, 0x1110, 0xd1bc, 0x0150, 0x7100, 0xd1f4, 0x0120, 0x7114,
+ 0xa18c, 0x00ff, 0x0040, 0x2009, 0x0000, 0x0028, 0xa1e0, 0x2dc4,
+ 0x2c0d, 0xa18c, 0x00ff, 0x2061, 0x0100, 0x619a, 0x080c, 0x78a2,
+ 0x7300, 0xc3dd, 0x7302, 0x6807, 0x0002, 0x2f18, 0x6b26, 0x682b,
+ 0x0000, 0x781f, 0x0003, 0x7803, 0x0001, 0x7807, 0x0040, 0x00fe,
+ 0x00ee, 0x00ce, 0x00de, 0x0005, 0x003e, 0x00ee, 0x00ce, 0x0cd0,
+ 0x00de, 0x0005, 0x00c6, 0x680c, 0xa065, 0x0138, 0x6807, 0x0004,
+ 0x6826, 0x682b, 0x0000, 0x080c, 0x7272, 0x00ce, 0x00de, 0x0005,
+ 0x00f6, 0x00d6, 0x2069, 0xb7e0, 0x6830, 0xa086, 0x0000, 0x11d0,
+ 0x2001, 0xb50c, 0x200c, 0xd1bc, 0x1560, 0x6838, 0xa07d, 0x0190,
+ 0x6833, 0x0001, 0x683e, 0x6847, 0x0000, 0x684b, 0x0000, 0x0126,
+ 0x00f6, 0x2091, 0x2400, 0x002e, 0x080c, 0x2021, 0x1130, 0x012e,
+ 0x080c, 0x7beb, 0x00de, 0x00fe, 0x0005, 0x012e, 0xe000, 0x6843,
+ 0x0000, 0x7803, 0x0002, 0x780c, 0xa015, 0x0140, 0x6a3a, 0x780f,
+ 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x0c60, 0x683a, 0x6836,
+ 0x0cc0, 0xc1bc, 0x2102, 0x0066, 0x2031, 0x0001, 0x080c, 0x5b51,
+ 0x006e, 0x0858, 0x601c, 0xa084, 0x000f, 0x000b, 0x0005, 0x7280,
+ 0x7285, 0x7743, 0x785f, 0x7285, 0x7743, 0x785f, 0x7280, 0x7285,
+ 0x080c, 0x7090, 0x080c, 0x7173, 0x0005, 0x0156, 0x0136, 0x0146,
+ 0x00c6, 0x00f6, 0x6004, 0xa08a, 0x0080, 0x1a0c, 0x1515, 0x6118,
+ 0x2178, 0x79a0, 0x2011, 0xb535, 0x2214, 0xd2ac, 0x1110, 0xd1bc,
+ 0x0150, 0x7900, 0xd1f4, 0x0120, 0x7914, 0xa18c, 0x00ff, 0x0040,
+ 0x2009, 0x0000, 0x0028, 0xa1f8, 0x2dc4, 0x2f0d, 0xa18c, 0x00ff,
+ 0x2c78, 0x2061, 0x0100, 0x619a, 0xa08a, 0x0040, 0x1a04, 0x72f9,
+ 0x0033, 0x00fe, 0x00ce, 0x014e, 0x013e, 0x015e, 0x0005, 0x73a8,
+ 0x73f3, 0x7420, 0x74ed, 0x751b, 0x7523, 0x7549, 0x755a, 0x756b,
+ 0x7573, 0x7589, 0x7573, 0x75ea, 0x755a, 0x760b, 0x7613, 0x756b,
+ 0x7613, 0x7624, 0x72f7, 0x72f7, 0x72f7, 0x72f7, 0x72f7, 0x72f7,
+ 0x72f7, 0x72f7, 0x72f7, 0x72f7, 0x72f7, 0x7e85, 0x7eaa, 0x7ebf,
+ 0x7ee2, 0x7f03, 0x7549, 0x72f7, 0x7549, 0x7573, 0x72f7, 0x7420,
+ 0x74ed, 0x72f7, 0x834f, 0x7573, 0x72f7, 0x836f, 0x7573, 0x72f7,
+ 0x756b, 0x73a1, 0x730c, 0x72f7, 0x8394, 0x8409, 0x84e0, 0x72f7,
+ 0x84f1, 0x7544, 0x850d, 0x72f7, 0x7f18, 0x8568, 0x72f7, 0x080c,
+ 0x1515, 0x2100, 0x0033, 0x00fe, 0x00ce, 0x014e, 0x013e, 0x015e,
+ 0x0005, 0x730a, 0x730a, 0x730a, 0x7340, 0x735e, 0x7374, 0x730a,
+ 0x730a, 0x730a, 0x080c, 0x1515, 0x00d6, 0x20a1, 0x020b, 0x080c,
+ 0x7641, 0x7810, 0x2068, 0x20a3, 0x2414, 0x20a3, 0x0018, 0x20a3,
+ 0x0800, 0x683c, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x6850, 0x20a2, 0x6854, 0x20a2, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0018, 0x080c, 0x7d67, 0x00de,
+ 0x0005, 0x00d6, 0x7818, 0x2068, 0x68a0, 0x2069, 0xb500, 0x6ad4,
+ 0xd2ac, 0x1110, 0xd0bc, 0x0110, 0xa085, 0x0001, 0x00de, 0x0005,
+ 0x00d6, 0x20a1, 0x020b, 0x080c, 0x7641, 0x20a3, 0x0500, 0x20a3,
+ 0x0000, 0x7810, 0xa0e8, 0x000f, 0x6808, 0x20a2, 0x680c, 0x20a2,
+ 0x6810, 0x20a2, 0x6814, 0x20a2, 0x6818, 0x20a2, 0x681c, 0x20a2,
+ 0x60c3, 0x0010, 0x080c, 0x7d67, 0x00de, 0x0005, 0x0156, 0x0146,
+ 0x20a1, 0x020b, 0x080c, 0x7641, 0x20a3, 0x7800, 0x20a3, 0x0000,
+ 0x7808, 0x8007, 0x20a2, 0x20a3, 0x0000, 0x60c3, 0x0008, 0x080c,
+ 0x7d67, 0x014e, 0x015e, 0x0005, 0x0156, 0x0146, 0x20a1, 0x020b,
+ 0x080c, 0x76dd, 0x20a3, 0x0200, 0x20a3, 0x0000, 0x20a3, 0xdf10,
+ 0x20a3, 0x0034, 0x2099, 0xb505, 0x20a9, 0x0004, 0x53a6, 0x2099,
+ 0xb501, 0x20a9, 0x0004, 0x53a6, 0x2099, 0xb7c6, 0x20a9, 0x001a,
+ 0x3304, 0x8007, 0x20a2, 0x9398, 0x1f04, 0x7390, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x004c, 0x080c, 0x7d67, 0x014e, 0x015e,
+ 0x0005, 0x2001, 0xb515, 0x2004, 0x609a, 0x080c, 0x7d67, 0x0005,
+ 0x20a1, 0x020b, 0x080c, 0x7641, 0x20a3, 0x5200, 0x20a3, 0x0000,
+ 0x00d6, 0x2069, 0xb552, 0x6804, 0xd084, 0x0150, 0x6828, 0x20a3,
+ 0x0000, 0x0016, 0x080c, 0x2831, 0x21a2, 0x001e, 0x00de, 0x0028,
+ 0x00de, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a9, 0x0004, 0x2099,
+ 0xb505, 0x53a6, 0x20a9, 0x0004, 0x2099, 0xb501, 0x53a6, 0x2001,
+ 0xb535, 0x2004, 0xd0ac, 0x1138, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0xa082, 0x007f, 0x0238, 0x2001, 0xb51c, 0x20a6, 0x2001, 0xb51d,
+ 0x20a6, 0x0040, 0x20a3, 0x0000, 0x2001, 0xb515, 0x2004, 0xa084,
+ 0x00ff, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c,
+ 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c, 0x7641, 0x20a3,
+ 0x0500, 0x20a3, 0x0000, 0x2001, 0xb535, 0x2004, 0xd0ac, 0x1138,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xa082, 0x007f, 0x0238, 0x2001,
+ 0xb51c, 0x20a6, 0x2001, 0xb51d, 0x20a6, 0x0040, 0x20a3, 0x0000,
+ 0x2001, 0xb515, 0x2004, 0xa084, 0x00ff, 0x20a2, 0x20a9, 0x0004,
+ 0x2099, 0xb505, 0x53a6, 0x60c3, 0x0010, 0x080c, 0x7d67, 0x0005,
+ 0x20a1, 0x020b, 0x080c, 0x7641, 0x00c6, 0x7818, 0x2060, 0x2001,
+ 0x0000, 0x080c, 0x5313, 0x00ce, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0xa086, 0x007e, 0x1130, 0x20a3, 0x0400, 0x620c, 0xc2b4, 0x620e,
+ 0x0010, 0x20a3, 0x0300, 0x20a3, 0x0000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xa086, 0x007e, 0x1904, 0x74af, 0x2001, 0xb535, 0x2004,
+ 0xd0a4, 0x01c8, 0x2099, 0xb78e, 0x33a6, 0x9398, 0x20a3, 0x0000,
+ 0x9398, 0x3304, 0xa084, 0x2000, 0x20a2, 0x9398, 0x33a6, 0x9398,
+ 0x20a3, 0x0000, 0x9398, 0x2001, 0x2710, 0x20a2, 0x9398, 0x33a6,
+ 0x9398, 0x33a6, 0x00d0, 0x2099, 0xb78e, 0x33a6, 0x9398, 0x33a6,
+ 0x9398, 0x3304, 0x080c, 0x5acf, 0x1118, 0xa084, 0x37ff, 0x0010,
+ 0xa084, 0x3fff, 0x20a2, 0x9398, 0x33a6, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a9, 0x0004, 0x2099,
+ 0xb505, 0x53a6, 0x20a9, 0x0004, 0x2099, 0xb501, 0x53a6, 0x20a9,
+ 0x0008, 0x20a3, 0x0000, 0x1f04, 0x7489, 0x20a9, 0x0008, 0x20a3,
+ 0x0000, 0x1f04, 0x748f, 0x2099, 0xb796, 0x3304, 0xc0dd, 0x20a2,
+ 0x2001, 0xb572, 0x2004, 0xd0e4, 0x0158, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x9398, 0x9398, 0x9398, 0x33a6, 0x20a9, 0x0004, 0x0010,
+ 0x20a9, 0x0007, 0x20a3, 0x0000, 0x1f04, 0x74aa, 0x0468, 0x2001,
+ 0xb535, 0x2004, 0xd0a4, 0x0140, 0x2001, 0xb78f, 0x2004, 0x60e3,
+ 0x0000, 0x080c, 0x2872, 0x60e2, 0x2099, 0xb78e, 0x20a9, 0x0008,
+ 0x53a6, 0x20a9, 0x0004, 0x2099, 0xb505, 0x53a6, 0x20a9, 0x0004,
+ 0x2099, 0xb501, 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x1f04,
+ 0x74cd, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x1f04, 0x74d3, 0x2099,
+ 0xb796, 0x20a9, 0x0008, 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000,
+ 0x1f04, 0x74de, 0x20a9, 0x000a, 0x20a3, 0x0000, 0x1f04, 0x74e4,
+ 0x60c3, 0x0074, 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c,
+ 0x7641, 0x20a3, 0x2010, 0x20a3, 0x0014, 0x20a3, 0x0800, 0x20a3,
+ 0x2000, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x00f6,
+ 0x2079, 0xb552, 0x7904, 0x00fe, 0xd1ac, 0x1110, 0xa085, 0x0020,
+ 0xd1a4, 0x0110, 0xa085, 0x0010, 0xa085, 0x0002, 0x00d6, 0x0804,
+ 0x75cc, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c, 0x7641, 0x20a3,
+ 0x5000, 0x0804, 0x743b, 0x20a1, 0x020b, 0x080c, 0x7641, 0x20a3,
+ 0x2110, 0x20a3, 0x0014, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0014, 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c, 0x76d5,
+ 0x0020, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0200, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0004, 0x080c,
+ 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0100,
+ 0x20a3, 0x0000, 0x20a3, 0x0003, 0x20a3, 0x2a00, 0x60c3, 0x0008,
+ 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3,
+ 0x0200, 0x0804, 0x743b, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3,
+ 0x0100, 0x20a3, 0x0000, 0x7828, 0xa005, 0x0110, 0x20a2, 0x0010,
+ 0x20a3, 0x0003, 0x7810, 0x20a2, 0x60c3, 0x0008, 0x080c, 0x7d67,
+ 0x0005, 0x00d6, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0210,
+ 0x20a3, 0x0014, 0x20a3, 0x0800, 0x7818, 0x2068, 0x6894, 0xa086,
+ 0x0014, 0x1198, 0x699c, 0xa184, 0x0030, 0x0190, 0x6998, 0xa184,
+ 0xc000, 0x1140, 0xd1ec, 0x0118, 0x20a3, 0x2100, 0x0058, 0x20a3,
+ 0x0100, 0x0040, 0x20a3, 0x0400, 0x0028, 0x20a3, 0x0700, 0x0010,
+ 0x700f, 0x0800, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a2,
+ 0x00f6, 0x2079, 0xb552, 0x7904, 0x00fe, 0xd1ac, 0x1110, 0xa085,
+ 0x0020, 0xd1a4, 0x0110, 0xa085, 0x0010, 0x2009, 0xb574, 0x210c,
+ 0xd184, 0x1110, 0xa085, 0x0002, 0x0026, 0x2009, 0xb572, 0x210c,
+ 0xd1e4, 0x0130, 0xc0c5, 0xa094, 0x0030, 0xa296, 0x0010, 0x0140,
+ 0xd1ec, 0x0130, 0xa094, 0x0030, 0xa296, 0x0010, 0x0108, 0xc0bd,
+ 0x002e, 0x20a2, 0x20a2, 0x20a2, 0x60c3, 0x0014, 0x080c, 0x7d67,
+ 0x00de, 0x0005, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0210,
+ 0x20a3, 0x0014, 0x20a3, 0x0000, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3,
+ 0x0200, 0x0804, 0x73ae, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3,
+ 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0003, 0x20a3, 0x2a00, 0x60c3,
+ 0x0008, 0x080c, 0x7d67, 0x0005, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x000b, 0x20a3, 0x0000, 0x60c3, 0x0008, 0x080c, 0x7d67,
+ 0x0005, 0x0026, 0x0036, 0x0046, 0x2019, 0x3200, 0x2021, 0x0800,
+ 0x0038, 0x0026, 0x0036, 0x0046, 0x2019, 0x2200, 0x2021, 0x0100,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2014,
+ 0xa286, 0x007e, 0x11a0, 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffe,
+ 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x2001, 0xb79e, 0x2004,
+ 0xa005, 0x0118, 0x2011, 0xb51d, 0x2214, 0x22a2, 0x04d0, 0xa286,
+ 0x007f, 0x1138, 0x00d6, 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffd,
+ 0x00c8, 0x2001, 0xb535, 0x2004, 0xd0ac, 0x1110, 0xd2bc, 0x01c8,
+ 0xa286, 0x0080, 0x00d6, 0x1130, 0xa385, 0x00ff, 0x20a2, 0x20a3,
+ 0xfffc, 0x0040, 0xa2e8, 0xb635, 0x2d6c, 0x6810, 0xa305, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de,
+ 0x0080, 0x00d6, 0xa2e8, 0xb635, 0x2d6c, 0x6810, 0xa305, 0x20a2,
+ 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214,
+ 0x22a2, 0xa485, 0x0029, 0x20a2, 0x004e, 0x003e, 0x20a3, 0x0000,
+ 0x080c, 0x7d56, 0x22a2, 0x20a3, 0x0000, 0x2fa2, 0x20a3, 0xffff,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x002e, 0x0005, 0x0026, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x20a3, 0x02ff, 0x2011, 0xfffc, 0x22a2,
+ 0x00d6, 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x20a3,
+ 0x2029, 0x20a3, 0x0000, 0x08e0, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0xfc02, 0x20a3, 0x0000, 0x0005, 0x0026, 0x0036, 0x0046,
+ 0x2019, 0x3300, 0x2021, 0x0800, 0x0038, 0x0026, 0x0036, 0x0046,
+ 0x2019, 0x2300, 0x2021, 0x0100, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0x2011, 0xb535, 0x2214, 0xd2ac,
+ 0x1118, 0xa092, 0x007e, 0x02d8, 0x00d6, 0xa0e8, 0xb635, 0x2d6c,
+ 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x6810, 0xa005, 0x1140,
+ 0x6814, 0xa005, 0x1128, 0x20a3, 0x00ff, 0x20a3, 0xfffe, 0x0028,
+ 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0080, 0x00d6,
+ 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2,
+ 0x00de, 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2, 0xa485,
+ 0x0098, 0x20a2, 0x20a3, 0x0000, 0x004e, 0x003e, 0x080c, 0x7d56,
+ 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x002e, 0x0005, 0x080c, 0x7d56, 0x22a2, 0x20a3,
+ 0x0000, 0x7a08, 0x22a2, 0x7810, 0x20a2, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x002e, 0x0005, 0x00c6, 0x00f6, 0x6004, 0xa08a, 0x0085,
+ 0x0a0c, 0x1515, 0xa08a, 0x008c, 0x1a0c, 0x1515, 0x6118, 0x2178,
+ 0x79a0, 0x2011, 0xb535, 0x2214, 0xd2ac, 0x1110, 0xd1bc, 0x0150,
+ 0x7900, 0xd1f4, 0x0120, 0x7914, 0xa18c, 0x00ff, 0x0040, 0x2009,
+ 0x0000, 0x0028, 0xa1f8, 0x2dc4, 0x2f0d, 0xa18c, 0x00ff, 0x2c78,
+ 0x2061, 0x0100, 0x619a, 0xa082, 0x0085, 0x001b, 0x00fe, 0x00ce,
+ 0x0005, 0x777a, 0x7784, 0x779f, 0x7778, 0x7778, 0x7778, 0x777a,
+ 0x080c, 0x1515, 0x0146, 0x20a1, 0x020b, 0x04a1, 0x60c3, 0x0000,
+ 0x080c, 0x7d67, 0x014e, 0x0005, 0x0146, 0x20a1, 0x020b, 0x080c,
+ 0x77eb, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808, 0x20a2, 0x7810,
+ 0x20a2, 0x20a3, 0x0000, 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x000c, 0x080c, 0x7d67, 0x014e, 0x0005, 0x0146,
+ 0x20a1, 0x020b, 0x080c, 0x7825, 0x20a3, 0x0003, 0x20a3, 0x0300,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0004, 0x080c, 0x7d67,
+ 0x014e, 0x0005, 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0x2011, 0xb535, 0x2214, 0xd2ac, 0x1118,
+ 0xa092, 0x007e, 0x0288, 0x00d6, 0xa0e8, 0xb635, 0x2d6c, 0x6810,
+ 0xa085, 0x8100, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xb51c, 0x2da6,
+ 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6, 0xa0e8, 0xb635, 0x2d6c,
+ 0x6810, 0xa085, 0x8100, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3,
+ 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2, 0x20a3, 0x0009, 0x20a3,
+ 0x0000, 0x0804, 0x76a8, 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0x2011, 0xb535, 0x2214, 0xd2ac,
+ 0x1118, 0xa092, 0x007e, 0x0288, 0x00d6, 0xa0e8, 0xb635, 0x2d6c,
+ 0x6810, 0xa085, 0x8400, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xb51c,
+ 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6, 0xa0e8, 0xb635,
+ 0x2d6c, 0x6810, 0xa085, 0x8400, 0x20a2, 0x6814, 0x20a2, 0x00de,
+ 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2, 0x2001, 0x0099,
+ 0x20a2, 0x20a3, 0x0000, 0x0804, 0x7734, 0x0026, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0x2011, 0xb535,
+ 0x2214, 0xd2ac, 0x1118, 0xa092, 0x007e, 0x0288, 0x00d6, 0xa0e8,
+ 0xb635, 0x2d6c, 0x6810, 0xa085, 0x8500, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6,
+ 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085, 0x8500, 0x20a2, 0x6814,
+ 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2,
+ 0x2001, 0x0099, 0x20a2, 0x20a3, 0x0000, 0x0804, 0x7734, 0x00c6,
+ 0x00f6, 0x2c78, 0x7804, 0xa08a, 0x0040, 0x0a0c, 0x1515, 0xa08a,
+ 0x0053, 0x1a0c, 0x1515, 0x7918, 0x2160, 0x61a0, 0x2011, 0xb535,
+ 0x2214, 0xd2ac, 0x1110, 0xd1bc, 0x0150, 0x6100, 0xd1f4, 0x0120,
+ 0x6114, 0xa18c, 0x00ff, 0x0040, 0x2009, 0x0000, 0x0028, 0xa1e0,
+ 0x2dc4, 0x2c0d, 0xa18c, 0x00ff, 0x2061, 0x0100, 0x619a, 0xa082,
+ 0x0040, 0x001b, 0x00fe, 0x00ce, 0x0005, 0x78a2, 0x79ae, 0x794b,
+ 0x7b60, 0x78a0, 0x78a0, 0x78a0, 0x78a0, 0x78a0, 0x78a0, 0x78a0,
+ 0x80d7, 0x80e7, 0x80f7, 0x8107, 0x78a0, 0x851e, 0x78a0, 0x80c6,
+ 0x080c, 0x1515, 0x00d6, 0x0156, 0x0146, 0x780b, 0xffff, 0x20a1,
+ 0x020b, 0x080c, 0x7902, 0x7910, 0x2168, 0x6948, 0x7952, 0x21a2,
+ 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x694c, 0xa184, 0x000f, 0x1118,
+ 0x2001, 0x0005, 0x0040, 0xd184, 0x0118, 0x2001, 0x0004, 0x0018,
+ 0xa084, 0x0006, 0x8004, 0x0016, 0x2008, 0x7858, 0xa084, 0x00ff,
+ 0x8007, 0xa105, 0x001e, 0x20a2, 0xd1ac, 0x0118, 0x20a3, 0x0002,
+ 0x0048, 0xd1b4, 0x0118, 0x20a3, 0x0001, 0x0020, 0x20a3, 0x0000,
+ 0x2230, 0x0010, 0x6a80, 0x6e7c, 0x20a9, 0x0008, 0x0136, 0xad88,
+ 0x0017, 0x2198, 0x20a1, 0x021b, 0x53a6, 0x013e, 0x20a1, 0x020b,
+ 0x22a2, 0x26a2, 0x60c3, 0x0020, 0x20e1, 0x9080, 0x6014, 0xa084,
+ 0x0004, 0xa085, 0x0009, 0x6016, 0x2001, 0xb7fc, 0x2003, 0x07d0,
+ 0x2001, 0xb7fb, 0x2003, 0x0009, 0x080c, 0x17e2, 0x014e, 0x015e,
+ 0x00de, 0x0005, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7a18, 0xa280,
+ 0x0023, 0x2014, 0x8210, 0xa294, 0x00ff, 0x2202, 0x8217, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0x2019, 0xb535, 0x231c, 0xd3ac, 0x1110,
+ 0xd0bc, 0x0188, 0x00d6, 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085,
+ 0x0600, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xb51c, 0x2da6, 0x8d68,
+ 0x2da6, 0x00de, 0x0088, 0x00d6, 0xa0e8, 0xb635, 0x2d6c, 0x6810,
+ 0xa085, 0x0600, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000,
+ 0x2009, 0xb515, 0x210c, 0x21a2, 0x20a3, 0x0829, 0x20a3, 0x0000,
+ 0x22a2, 0x20a3, 0x0000, 0x2fa2, 0x20a3, 0xffff, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x0005, 0x00d6, 0x0156, 0x0136, 0x0146, 0x20a1,
+ 0x020b, 0x00c1, 0x7810, 0x2068, 0x6860, 0x20a2, 0x685c, 0x20a2,
+ 0x6880, 0x20a2, 0x687c, 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2,
+ 0x20a2, 0x60c3, 0x000c, 0x080c, 0x7d67, 0x014e, 0x013e, 0x015e,
+ 0x00de, 0x0005, 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0x2011, 0xb535, 0x2214, 0xd2ac, 0x1110,
+ 0xd0bc, 0x0188, 0x00d6, 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085,
+ 0x0500, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xb51c, 0x2da6, 0x8d68,
+ 0x2da6, 0x00de, 0x0088, 0x00d6, 0xa0e8, 0xb635, 0x2d6c, 0x6810,
+ 0xa085, 0x0500, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000,
+ 0x2011, 0xb515, 0x2214, 0x22a2, 0x20a3, 0x0889, 0x20a3, 0x0000,
+ 0x080c, 0x7d56, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x002e, 0x0005, 0x00d6, 0x0156,
+ 0x0136, 0x0146, 0x7810, 0xa0ec, 0xf000, 0x0168, 0xa06d, 0x080c,
+ 0x5301, 0x0148, 0x684c, 0xa084, 0x2020, 0xa086, 0x2020, 0x1118,
+ 0x7820, 0xc0cd, 0x7822, 0x20a1, 0x020b, 0x080c, 0x7b16, 0xa016,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x7810, 0xa084, 0xf000,
+ 0x1130, 0x7810, 0xa084, 0x0700, 0x8007, 0x0043, 0x0010, 0xa006,
+ 0x002b, 0x014e, 0x013e, 0x015e, 0x00de, 0x0005, 0x79e8, 0x7a7d,
+ 0x7a8d, 0x7abf, 0x7ad2, 0x7aed, 0x7af6, 0x79e6, 0x080c, 0x1515,
+ 0x0016, 0x0036, 0x694c, 0xa18c, 0x0003, 0x0118, 0xa186, 0x0003,
+ 0x1170, 0x6b78, 0x7820, 0xd0cc, 0x0108, 0xc3e5, 0x23a2, 0x6868,
+ 0x20a2, 0x6864, 0x20a2, 0x003e, 0x001e, 0x0804, 0x7ac9, 0xa186,
+ 0x0001, 0x190c, 0x1515, 0x6b78, 0x7820, 0xd0cc, 0x0108, 0xc3e5,
+ 0x23a2, 0x6868, 0x20a2, 0x6864, 0x20a2, 0x22a2, 0x6874, 0x20a2,
+ 0x22a2, 0x687c, 0x20a2, 0x2009, 0x0018, 0xa384, 0x0300, 0x0904,
+ 0x7a77, 0xd3c4, 0x0110, 0x687c, 0xa108, 0xd3cc, 0x0110, 0x6874,
+ 0xa108, 0x0156, 0x20a9, 0x000d, 0xad80, 0x0020, 0x201c, 0x831f,
+ 0x23a2, 0x8000, 0x1f04, 0x7a26, 0x015e, 0x22a2, 0x22a2, 0x22a2,
+ 0xa184, 0x0003, 0x0904, 0x7a77, 0x20a1, 0x020b, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x0006, 0x7818, 0xa080, 0x0028, 0x2004, 0x2011,
+ 0xb535, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188, 0x00d6, 0xa0e8,
+ 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6,
+ 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814,
+ 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2,
+ 0x000e, 0x7b20, 0xd3cc, 0x0118, 0x20a3, 0x0889, 0x0010, 0x20a3,
+ 0x0898, 0x20a2, 0x080c, 0x7d56, 0x22a2, 0x20a3, 0x0000, 0x61c2,
+ 0x003e, 0x001e, 0x080c, 0x7d67, 0x0005, 0x2011, 0x0008, 0x2001,
+ 0xb50d, 0x2004, 0xd0f4, 0x0110, 0x2011, 0x0028, 0x7820, 0xd0cc,
+ 0x0108, 0xc2e5, 0x22a2, 0xa016, 0x04d0, 0x2011, 0x0302, 0x0016,
+ 0x0036, 0x7828, 0x792c, 0xa11d, 0x0108, 0xc2dd, 0x7b20, 0xd3cc,
+ 0x0108, 0xc2e5, 0x22a2, 0x20a2, 0x21a2, 0x003e, 0x001e, 0xa016,
+ 0x22a2, 0x20a3, 0x0012, 0x22a2, 0x20a3, 0x0008, 0x22a2, 0x22a2,
+ 0x22a2, 0x22a2, 0x20a3, 0x7000, 0x20a3, 0x0500, 0x22a2, 0x20a3,
+ 0x000a, 0x22a2, 0x22a2, 0x20a3, 0x2500, 0x22a2, 0x22a2, 0x22a2,
+ 0x22a2, 0x22a2, 0x60c3, 0x0032, 0x080c, 0x7d67, 0x0005, 0x2011,
+ 0x0028, 0x7820, 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0xa016, 0x22a2,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0018, 0x080c,
+ 0x7d67, 0x0005, 0x2011, 0x0100, 0x7820, 0xd0cc, 0x0108, 0xc2e5,
+ 0x22a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x20a3,
+ 0x0008, 0x22a2, 0x7854, 0xa084, 0x00ff, 0x20a2, 0x22a2, 0x22a2,
+ 0x60c3, 0x0020, 0x080c, 0x7d67, 0x0005, 0x2011, 0x0008, 0x7820,
+ 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0xa016, 0x0888, 0x0036, 0x7b10,
+ 0xa384, 0xff00, 0x7812, 0xa384, 0x00ff, 0x8001, 0x1138, 0x7820,
+ 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0x003e, 0x0808, 0x0046, 0x2021,
+ 0x0800, 0x0006, 0x7820, 0xd0cc, 0x000e, 0x0108, 0xc4e5, 0x24a2,
+ 0x004e, 0x22a2, 0x20a2, 0x003e, 0x0804, 0x7ac9, 0x0026, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0x2011,
+ 0xb535, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188, 0x00d6, 0xa0e8,
+ 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6,
+ 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814,
+ 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2,
+ 0x7820, 0xd0cc, 0x0118, 0x20a3, 0x0889, 0x0010, 0x20a3, 0x0898,
+ 0x20a3, 0x0000, 0x080c, 0x7d56, 0x22a2, 0x20a3, 0x0000, 0x7a08,
+ 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x002e, 0x0005,
+ 0x00d6, 0x0156, 0x0136, 0x0146, 0x0016, 0x0036, 0x7810, 0xa084,
+ 0x0700, 0x8007, 0x003b, 0x003e, 0x001e, 0x014e, 0x013e, 0x015e,
+ 0x00de, 0x0005, 0x7b7a, 0x7b7a, 0x7b7c, 0x7b7a, 0x7b7a, 0x7b7a,
+ 0x7b9e, 0x7b7a, 0x080c, 0x1515, 0x7910, 0xa18c, 0xf8ff, 0xa18d,
+ 0x0600, 0x7912, 0x20a1, 0x020b, 0x2009, 0x0003, 0x00f9, 0x00d6,
+ 0x2069, 0xb552, 0x6804, 0xd0bc, 0x0130, 0x682c, 0xa084, 0x00ff,
+ 0x8007, 0x20a2, 0x0010, 0x20a3, 0x3f00, 0x00de, 0x22a2, 0x22a2,
+ 0x22a2, 0x60c3, 0x0001, 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b,
+ 0x2009, 0x0003, 0x0019, 0x20a3, 0x7f00, 0x0c80, 0x0026, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0x2011,
+ 0xb535, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188, 0x00d6, 0xa0e8,
+ 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0100, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6,
+ 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0100, 0x20a2, 0x6814,
+ 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xb515, 0x2214, 0x22a2,
+ 0x20a3, 0x0888, 0xa18d, 0x0008, 0x21a2, 0x080c, 0x7d56, 0x22a2,
+ 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x002e, 0x0005, 0x00e6, 0x00d6, 0x00c6, 0x0056, 0x0046,
+ 0x0036, 0x2061, 0x0100, 0x2071, 0xb500, 0x7154, 0x7818, 0x2068,
+ 0x68a0, 0x2028, 0x76d4, 0xd6ac, 0x1130, 0xd0bc, 0x1120, 0x6910,
+ 0x6a14, 0x7454, 0x0020, 0x6910, 0x6a14, 0x7370, 0x7474, 0x781c,
+ 0xa0be, 0x0006, 0x0904, 0x7ca1, 0xa0be, 0x000a, 0x15e8, 0xa185,
+ 0x0200, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x2029, 0x6077,
+ 0x0000, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e, 0x8007, 0x607a,
+ 0x607f, 0x0000, 0x2f00, 0x6082, 0x7808, 0x6086, 0x7810, 0x2070,
+ 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6, 0x7008, 0x60ca,
+ 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0x609f, 0x0000,
+ 0x080c, 0x85b9, 0x2009, 0x07d0, 0x60c4, 0xa084, 0xfff0, 0xa005,
+ 0x0110, 0x2009, 0x1b58, 0x080c, 0x6a15, 0x003e, 0x004e, 0x005e,
+ 0x00ce, 0x00de, 0x00ee, 0x0005, 0x70d4, 0xd0ac, 0x1110, 0xd5bc,
+ 0x0138, 0xa185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x0038,
+ 0xa185, 0x0100, 0x6062, 0x6266, 0x606b, 0x0000, 0x646e, 0x6073,
+ 0x0809, 0x6077, 0x0008, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e,
+ 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6082, 0x7808, 0x6086,
+ 0x7810, 0x2070, 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6,
+ 0x7008, 0x60ca, 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000,
+ 0xa582, 0x0080, 0x0248, 0x6a00, 0xd2f4, 0x0120, 0x6a14, 0xa294,
+ 0x00ff, 0x0010, 0x2011, 0x0000, 0x629e, 0x080c, 0x85b9, 0x2009,
+ 0x07d0, 0x60c4, 0xa084, 0xfff0, 0xa005, 0x0110, 0x2009, 0x1b58,
+ 0x080c, 0x6a15, 0x003e, 0x004e, 0x005e, 0x00ce, 0x00de, 0x00ee,
+ 0x0005, 0x7810, 0x2070, 0x704c, 0xa084, 0x0003, 0xa086, 0x0002,
+ 0x0904, 0x7cf7, 0x2001, 0xb535, 0x2004, 0xd0ac, 0x1110, 0xd5bc,
+ 0x0138, 0xa185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x0038,
+ 0xa185, 0x0100, 0x6062, 0x6266, 0x606b, 0x0000, 0x646e, 0x6073,
+ 0x0880, 0x6077, 0x0008, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e,
+ 0x8007, 0x607a, 0x7834, 0x607e, 0x2f00, 0x6086, 0x7808, 0x6082,
+ 0x7060, 0x608a, 0x705c, 0x608e, 0x7080, 0x60c6, 0x707c, 0x60ca,
+ 0x707c, 0x792c, 0xa108, 0x792e, 0x7080, 0x7928, 0xa109, 0x792a,
+ 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xa582, 0x0080,
+ 0x0248, 0x6a00, 0xd2f4, 0x0120, 0x6a14, 0xa294, 0x00ff, 0x0010,
+ 0x2011, 0x0000, 0x629e, 0x080c, 0x85b6, 0x0804, 0x7c8f, 0x2001,
+ 0xb535, 0x2004, 0xd0ac, 0x1110, 0xd5bc, 0x0138, 0xa185, 0x0700,
+ 0x6062, 0x6266, 0x636a, 0x646e, 0x0038, 0xa185, 0x0700, 0x6062,
+ 0x6266, 0x606b, 0x0000, 0x646e, 0x080c, 0x5301, 0x0180, 0x00d6,
+ 0x7810, 0xa06d, 0x684c, 0x00de, 0xa084, 0x2020, 0xa086, 0x2020,
+ 0x1130, 0x7820, 0xc0cd, 0x7822, 0x6073, 0x0889, 0x0010, 0x6073,
+ 0x0898, 0x6077, 0x0000, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e,
+ 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6086, 0x7808, 0x6082,
+ 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6, 0x7008, 0x60ca,
+ 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xa582, 0x0080,
+ 0x0248, 0x6a00, 0xd2f4, 0x0120, 0x6a14, 0xa294, 0x00ff, 0x0010,
+ 0x2011, 0x0000, 0x629e, 0x7820, 0xd0cc, 0x0120, 0x080c, 0x85b9,
+ 0x0804, 0x7c8f, 0x080c, 0x85b6, 0x0804, 0x7c8f, 0x7a18, 0xa280,
+ 0x0023, 0x2014, 0x8210, 0xa294, 0x00ff, 0x2202, 0x8217, 0x0005,
+ 0x00d6, 0x2069, 0xb7e0, 0x6843, 0x0001, 0x00de, 0x0005, 0x20e1,
+ 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x0019, 0x080c, 0x6a07,
+ 0x0005, 0x0006, 0x6014, 0xa084, 0x0004, 0xa085, 0x0009, 0x6016,
+ 0x000e, 0x0005, 0x0016, 0x00c6, 0x0006, 0x2061, 0x0100, 0x61a4,
+ 0x60a7, 0x95f5, 0x6014, 0xa084, 0x0004, 0xa085, 0x0008, 0x6016,
+ 0x000e, 0xe000, 0xe000, 0xe000, 0xe000, 0x61a6, 0x00ce, 0x001e,
+ 0x0005, 0x00c6, 0x00d6, 0x0016, 0x0026, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x080c, 0x5acf, 0x1198, 0x2001, 0xb7fc, 0x2004, 0xa005,
+ 0x15b8, 0x0066, 0x2031, 0x0001, 0x080c, 0x5b51, 0x006e, 0x1118,
+ 0x080c, 0x6a07, 0x0468, 0x00c6, 0x2061, 0xb7e0, 0x00d8, 0x6904,
+ 0xa194, 0x4000, 0x0550, 0x0831, 0x6803, 0x1000, 0x6803, 0x0000,
+ 0x00c6, 0x2061, 0xb7e0, 0x6128, 0xa192, 0x00c8, 0x1258, 0x8108,
+ 0x612a, 0x6124, 0x00ce, 0x81ff, 0x0198, 0x080c, 0x6a07, 0x080c,
+ 0x7d71, 0x0070, 0x6124, 0xa1e5, 0x0000, 0x0140, 0x080c, 0xb444,
+ 0x080c, 0x6a10, 0x2009, 0x0014, 0x080c, 0x864c, 0x00ce, 0x0000,
+ 0x002e, 0x001e, 0x00de, 0x00ce, 0x0005, 0x2001, 0xb7fc, 0x2004,
+ 0xa005, 0x1db0, 0x00c6, 0x2061, 0xb7e0, 0x6128, 0xa192, 0x0003,
+ 0x1e08, 0x8108, 0x612a, 0x00ce, 0x080c, 0x6a07, 0x080c, 0x4b1f,
+ 0x0c38, 0x00c6, 0x00d6, 0x00e6, 0x0016, 0x0026, 0x080c, 0x6a1d,
+ 0x2071, 0xb7e0, 0x713c, 0x81ff, 0x0590, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x080c, 0x5acf, 0x11a8, 0x0036, 0x2019, 0x0002, 0x080c,
+ 0x7fe4, 0x003e, 0x713c, 0x2160, 0x080c, 0xb444, 0x2009, 0x004a,
+ 0x080c, 0x864c, 0x0066, 0x2031, 0x0001, 0x080c, 0x5b51, 0x006e,
+ 0x00b0, 0x6904, 0xa194, 0x4000, 0x01c0, 0x6803, 0x1000, 0x6803,
+ 0x0000, 0x0036, 0x2019, 0x0001, 0x080c, 0x7fe4, 0x003e, 0x713c,
+ 0x2160, 0x080c, 0xb444, 0x2009, 0x004a, 0x080c, 0x864c, 0x002e,
+ 0x001e, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0c58, 0x0026, 0x00e6,
+ 0x2071, 0xb7e0, 0x7048, 0xd084, 0x01c0, 0x713c, 0x81ff, 0x01a8,
+ 0x2071, 0x0100, 0xa188, 0x0007, 0x2114, 0xa28e, 0x0006, 0x1138,
+ 0x7014, 0xa084, 0x0184, 0xa085, 0x0012, 0x7016, 0x0030, 0x7014,
+ 0xa084, 0x0184, 0xa085, 0x0016, 0x7016, 0x00ee, 0x002e, 0x0005,
+ 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0056, 0x0046, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x6018, 0x2068, 0x6ca0, 0x2071, 0xb7e0, 0x7018,
+ 0x2068, 0x8dff, 0x0188, 0x68a0, 0xa406, 0x0118, 0x6854, 0x2068,
+ 0x0cc0, 0x6010, 0x2060, 0x643c, 0x6540, 0x6648, 0x2d60, 0x080c,
+ 0x511a, 0x0110, 0xa085, 0x0001, 0x012e, 0x000e, 0x004e, 0x005e,
+ 0x006e, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x20a1, 0x020b, 0x080c,
+ 0x7641, 0x20a3, 0x1200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x781c,
+ 0xa086, 0x0004, 0x1110, 0x6098, 0x0018, 0x2001, 0xb515, 0x2004,
+ 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a9, 0x0010, 0xa006,
+ 0x20a2, 0x1f04, 0x7ea0, 0x20a2, 0x20a2, 0x60c3, 0x002c, 0x080c,
+ 0x7d67, 0x0005, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x7641,
+ 0x20a3, 0x0f00, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808, 0x20a2,
+ 0x60c3, 0x0008, 0x080c, 0x7d67, 0x014e, 0x015e, 0x0005, 0x0156,
+ 0x0146, 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0200, 0x20a3,
+ 0x0000, 0x20a9, 0x0006, 0x2011, 0xb540, 0x2019, 0xb541, 0x23a6,
+ 0x22a6, 0xa398, 0x0002, 0xa290, 0x0002, 0x1f04, 0x7ecf, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c, 0x080c, 0x7d67, 0x014e,
+ 0x015e, 0x0005, 0x0156, 0x0146, 0x0016, 0x0026, 0x20a1, 0x020b,
+ 0x080c, 0x76b6, 0x080c, 0x76cc, 0x7810, 0xa080, 0x0000, 0x2004,
+ 0xa080, 0x0015, 0x2098, 0x7808, 0xa088, 0x0002, 0x21a8, 0x53a6,
+ 0xa080, 0x0004, 0x8003, 0x60c2, 0x080c, 0x7d67, 0x002e, 0x001e,
+ 0x014e, 0x015e, 0x0005, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c,
+ 0x7641, 0x20a3, 0x6200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808,
+ 0x20a2, 0x60c3, 0x0008, 0x080c, 0x7d67, 0x014e, 0x015e, 0x0005,
+ 0x0156, 0x0146, 0x0016, 0x0026, 0x20a1, 0x020b, 0x080c, 0x7641,
+ 0x7810, 0xa080, 0x0000, 0x2004, 0xa080, 0x0017, 0x2098, 0x7808,
+ 0xa088, 0x0002, 0x21a8, 0x53a6, 0x8003, 0x60c2, 0x080c, 0x7d67,
+ 0x002e, 0x001e, 0x014e, 0x015e, 0x0005, 0x00e6, 0x00c6, 0x0006,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0xb7e0, 0x700c, 0x2060, 0x8cff,
+ 0x0178, 0x080c, 0x9e58, 0x1110, 0x080c, 0x8c19, 0x600c, 0x0006,
+ 0x080c, 0xa01f, 0x080c, 0x861d, 0x080c, 0x811e, 0x00ce, 0x0c78,
+ 0x700f, 0x0000, 0x700b, 0x0000, 0x012e, 0x000e, 0x00ce, 0x00ee,
+ 0x0005, 0x0126, 0x0156, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0026,
+ 0x0016, 0x0006, 0x2091, 0x8000, 0x2069, 0x0100, 0x2079, 0x0140,
+ 0x2071, 0xb7e0, 0x7024, 0x2060, 0x8cff, 0x05a0, 0x080c, 0x7d7a,
+ 0x68c3, 0x0000, 0x080c, 0x6a10, 0x2009, 0x0013, 0x080c, 0x864c,
+ 0x20a9, 0x01f4, 0x6824, 0xd094, 0x0158, 0x6827, 0x0004, 0x7804,
+ 0xa084, 0x4000, 0x01a0, 0x7803, 0x1000, 0x7803, 0x0000, 0x0078,
+ 0xd084, 0x0118, 0x6827, 0x0001, 0x0010, 0x1f04, 0x7f7a, 0x7804,
+ 0xa084, 0x1000, 0x0120, 0x7803, 0x0100, 0x7803, 0x0000, 0x6824,
+ 0x000e, 0x001e, 0x002e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e,
+ 0x012e, 0x0005, 0x2001, 0xb500, 0x2004, 0xa096, 0x0001, 0x0590,
+ 0xa096, 0x0004, 0x0578, 0x080c, 0x6a10, 0x6814, 0xa084, 0x0001,
+ 0x0110, 0x68a7, 0x95f5, 0x6817, 0x0008, 0x68c3, 0x0000, 0x2011,
+ 0x4adc, 0x080c, 0x699c, 0x20a9, 0x01f4, 0x6824, 0xd094, 0x0158,
+ 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x01a0, 0x7803, 0x1000,
+ 0x7803, 0x0000, 0x0078, 0xd084, 0x0118, 0x6827, 0x0001, 0x0010,
+ 0x1f04, 0x7fbd, 0x7804, 0xa084, 0x1000, 0x0120, 0x7803, 0x0100,
+ 0x7803, 0x0000, 0x000e, 0x001e, 0x002e, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x015e, 0x012e, 0x0005, 0x0126, 0x0156, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x0026, 0x0016, 0x0006, 0x2091, 0x8000, 0x2069,
+ 0x0100, 0x2079, 0x0140, 0x2071, 0xb7e0, 0x703c, 0x2060, 0x8cff,
+ 0x0904, 0x806b, 0xa386, 0x0002, 0x1128, 0x6814, 0xa084, 0x0002,
+ 0x0904, 0x806b, 0x68af, 0x95f5, 0x6817, 0x0010, 0x2009, 0x00fa,
+ 0x8109, 0x1df0, 0x68c7, 0x0000, 0x68cb, 0x0008, 0x080c, 0x6a1d,
+ 0x080c, 0x220c, 0x0046, 0x2009, 0x017f, 0x200b, 0x00a5, 0x2021,
+ 0x0169, 0x2404, 0xa084, 0x000f, 0xa086, 0x0004, 0x1500, 0x68af,
+ 0x95f5, 0x68c7, 0x0000, 0x68cb, 0x0008, 0x00e6, 0x00f6, 0x2079,
+ 0x0020, 0x2071, 0xb84a, 0x6814, 0xa084, 0x0184, 0xa085, 0x0012,
+ 0x6816, 0x7803, 0x0008, 0x7003, 0x0000, 0x00fe, 0x00ee, 0xa386,
+ 0x0002, 0x1128, 0x7884, 0xa005, 0x1110, 0x7887, 0x0001, 0x2001,
+ 0xb7b1, 0x2004, 0x200a, 0x004e, 0xa39d, 0x0000, 0x1120, 0x2009,
+ 0x0049, 0x080c, 0x864c, 0x20a9, 0x03e8, 0x6824, 0xd094, 0x0158,
+ 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x01a0, 0x7803, 0x1000,
+ 0x7803, 0x0000, 0x0078, 0xd08c, 0x0118, 0x6827, 0x0002, 0x0010,
+ 0x1f04, 0x804d, 0x7804, 0xa084, 0x1000, 0x0120, 0x7803, 0x0100,
+ 0x7803, 0x0000, 0x6824, 0x000e, 0x001e, 0x002e, 0x00ce, 0x00de,
+ 0x00ee, 0x00fe, 0x015e, 0x012e, 0x0005, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x2069, 0xb7e0, 0x6a06, 0x012e, 0x00de, 0x0005, 0x00d6,
+ 0x0126, 0x2091, 0x8000, 0x2069, 0xb7e0, 0x6a32, 0x012e, 0x00de,
+ 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0066, 0x0006, 0x0126, 0x2071,
+ 0xb7e0, 0x7614, 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff, 0x0538,
+ 0x601c, 0xa206, 0x1500, 0x7014, 0xac36, 0x1110, 0x660c, 0x7616,
+ 0x7010, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7012,
+ 0x0010, 0x7013, 0x0000, 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x080c, 0x9e1d, 0x080c,
+ 0x811e, 0x00ce, 0x08d8, 0x2c78, 0x600c, 0x2060, 0x08b8, 0x012e,
+ 0x000e, 0x006e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x0156, 0x0146,
+ 0x20a1, 0x020b, 0x080c, 0x7902, 0x7810, 0x20a2, 0xa006, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x1000, 0x0804, 0x8116, 0x0156,
+ 0x0146, 0x20a1, 0x020b, 0x080c, 0x7902, 0x7810, 0x20a2, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x4000, 0x0478, 0x0156,
+ 0x0146, 0x20a1, 0x020b, 0x080c, 0x7902, 0x7810, 0x20a2, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x2000, 0x00f8, 0x0156,
+ 0x0146, 0x20a1, 0x020b, 0x080c, 0x7902, 0x7810, 0x20a2, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0400, 0x0078, 0x0156,
+ 0x0146, 0x20a1, 0x020b, 0x080c, 0x7902, 0x7810, 0x20a2, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0200, 0x0089, 0x60c3,
+ 0x0020, 0x080c, 0x7d67, 0x014e, 0x015e, 0x0005, 0x00e6, 0x2071,
+ 0xb7e0, 0x7020, 0xa005, 0x0110, 0x8001, 0x7022, 0x00ee, 0x0005,
+ 0x20a9, 0x0008, 0x20a2, 0x1f04, 0x812a, 0x20a2, 0x20a2, 0x0005,
+ 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0076, 0x0066, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0xb7e0, 0x7614, 0x2660, 0x2678, 0x2039,
+ 0x0001, 0x87ff, 0x0904, 0x81c6, 0x8cff, 0x0904, 0x81c6, 0x601c,
+ 0xa086, 0x0006, 0x1904, 0x81c1, 0x88ff, 0x0138, 0x2800, 0xac06,
+ 0x1904, 0x81c1, 0x2039, 0x0000, 0x0050, 0x6018, 0xa206, 0x1904,
+ 0x81c1, 0x85ff, 0x0120, 0x6050, 0xa106, 0x1904, 0x81c1, 0x7024,
+ 0xac06, 0x1598, 0x2069, 0x0100, 0x68c0, 0xa005, 0x1160, 0x6824,
+ 0xd084, 0x0148, 0x6827, 0x0001, 0x080c, 0x6a10, 0x080c, 0x824d,
+ 0x7027, 0x0000, 0x0410, 0x080c, 0x6a10, 0x6820, 0xd0b4, 0x0110,
+ 0x68a7, 0x95f5, 0x6817, 0x0008, 0x68c3, 0x0000, 0x080c, 0x824d,
+ 0x7027, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000,
+ 0x0120, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824,
+ 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x7014, 0xac36, 0x1110,
+ 0x660c, 0x7616, 0x7010, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118,
+ 0x2f00, 0x7012, 0x0010, 0x7013, 0x0000, 0x660c, 0x0066, 0x2c00,
+ 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x89ff, 0x1158, 0x600f,
+ 0x0000, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0110, 0x080c, 0xb099,
+ 0x080c, 0x9e1d, 0x080c, 0x811e, 0x88ff, 0x1190, 0x00ce, 0x0804,
+ 0x8141, 0x2c78, 0x600c, 0x2060, 0x0804, 0x8141, 0xa006, 0x012e,
+ 0x000e, 0x006e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005,
+ 0x6017, 0x0000, 0x00ce, 0xa8c5, 0x0001, 0x0c88, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000,
+ 0x2071, 0xb7e0, 0x7638, 0x2660, 0x2678, 0x8cff, 0x0904, 0x823d,
+ 0x601c, 0xa086, 0x0006, 0x1904, 0x8238, 0x87ff, 0x0128, 0x2700,
+ 0xac06, 0x1904, 0x8238, 0x0048, 0x6018, 0xa206, 0x1904, 0x8238,
+ 0x85ff, 0x0118, 0x6050, 0xa106, 0x15d8, 0x703c, 0xac06, 0x1180,
+ 0x0036, 0x2019, 0x0001, 0x080c, 0x7fe4, 0x7033, 0x0000, 0x703f,
+ 0x0000, 0x7043, 0x0000, 0x7047, 0x0000, 0x704b, 0x0000, 0x003e,
+ 0x7038, 0xac36, 0x1110, 0x660c, 0x763a, 0x7034, 0xac36, 0x1140,
+ 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7036, 0x0010, 0x7037, 0x0000,
+ 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x600f, 0x0000, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0110, 0x080c,
+ 0xb099, 0x080c, 0x9e1d, 0x87ff, 0x1190, 0x00ce, 0x0804, 0x81e5,
+ 0x2c78, 0x600c, 0x2060, 0x0804, 0x81e5, 0xa006, 0x012e, 0x000e,
+ 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x6017,
+ 0x0000, 0x00ce, 0xa7bd, 0x0001, 0x0c88, 0x00e6, 0x2071, 0xb7e0,
+ 0x2001, 0xb500, 0x2004, 0xa086, 0x0002, 0x1118, 0x7007, 0x0005,
+ 0x0010, 0x7007, 0x0000, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x00c6,
+ 0x0066, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0xb7e0,
+ 0x2c10, 0x7638, 0x2660, 0x2678, 0x8cff, 0x0518, 0x2200, 0xac06,
+ 0x11e0, 0x7038, 0xac36, 0x1110, 0x660c, 0x763a, 0x7034, 0xac36,
+ 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7036, 0x0010, 0x7037,
+ 0x0000, 0x660c, 0x2c00, 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x600f, 0x0000, 0xa085, 0x0001, 0x0020, 0x2c78, 0x600c, 0x2060,
+ 0x08d8, 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce, 0x00ee, 0x00fe,
+ 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0xb7e0, 0x760c, 0x2660, 0x2678, 0x8cff,
+ 0x0904, 0x8323, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x1904,
+ 0x831e, 0x7024, 0xac06, 0x1508, 0x2069, 0x0100, 0x68c0, 0xa005,
+ 0x0904, 0x82fa, 0x080c, 0x7d7a, 0x68c3, 0x0000, 0x080c, 0x824d,
+ 0x7027, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000,
+ 0x0120, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824,
+ 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x700c, 0xac36, 0x1110,
+ 0x660c, 0x760e, 0x7008, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118,
+ 0x2f00, 0x700a, 0x0010, 0x700b, 0x0000, 0x660c, 0x0066, 0x2c00,
+ 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x080c,
+ 0x9e47, 0x1158, 0x080c, 0x2cc2, 0x080c, 0x9e58, 0x11f0, 0x080c,
+ 0x8c19, 0x00d8, 0x080c, 0x824d, 0x08c0, 0x080c, 0x9e58, 0x1118,
+ 0x080c, 0x8c19, 0x0090, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0168,
+ 0x601c, 0xa086, 0x0003, 0x11f8, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x080c, 0x5408, 0x080c, 0x9e11, 0x080c, 0xa01f, 0x080c,
+ 0x9e1d, 0x080c, 0x811e, 0x00ce, 0x0804, 0x82a7, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0x82a7, 0x012e, 0x000e, 0x006e, 0x00ce, 0x00de,
+ 0x00ee, 0x00fe, 0x0005, 0x601c, 0xa086, 0x0006, 0x1d30, 0x080c,
+ 0xb099, 0x0c18, 0x0036, 0x0156, 0x0136, 0x0146, 0x3908, 0xa006,
+ 0xa190, 0x0020, 0x221c, 0xa39e, 0x2ab7, 0x1118, 0x8210, 0x8000,
+ 0x0cc8, 0xa005, 0x0138, 0x20a9, 0x0020, 0x2198, 0xa110, 0x22a0,
+ 0x22c8, 0x53a3, 0x014e, 0x013e, 0x015e, 0x003e, 0x0005, 0x00d6,
+ 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0200, 0x20a3, 0x0014,
+ 0x60c3, 0x0014, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2099, 0xb7b9,
+ 0x20a9, 0x0004, 0x53a6, 0x20a3, 0x0004, 0x20a3, 0x7878, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x080c, 0x7d67, 0x00de, 0x0005, 0x20a1,
+ 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0214, 0x20a3, 0x0018, 0x20a3,
+ 0x0800, 0x7810, 0xa084, 0xff00, 0x20a2, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7810, 0xa084, 0x00ff,
+ 0x20a2, 0x7828, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0018, 0x080c, 0x7d67, 0x0005, 0x00d6, 0x0016, 0x2f68, 0x2009,
+ 0x0035, 0x080c, 0xa10a, 0x1904, 0x8402, 0x20a1, 0x020b, 0x080c,
+ 0x7641, 0x20a3, 0x1300, 0x20a3, 0x0000, 0x7828, 0x2068, 0x681c,
+ 0xa086, 0x0003, 0x0580, 0x7818, 0xa080, 0x0028, 0x2014, 0x2001,
+ 0xb535, 0x2004, 0xd0ac, 0x11d0, 0xa286, 0x007e, 0x1128, 0x20a3,
+ 0x00ff, 0x20a3, 0xfffe, 0x04b8, 0xa286, 0x007f, 0x1128, 0x20a3,
+ 0x00ff, 0x20a3, 0xfffd, 0x0478, 0xd2bc, 0x0180, 0xa286, 0x0080,
+ 0x1128, 0x20a3, 0x00ff, 0x20a3, 0xfffc, 0x0428, 0xa2e8, 0xb635,
+ 0x2d6c, 0x6810, 0x20a2, 0x6814, 0x20a2, 0x00e8, 0x20a3, 0x0000,
+ 0x6098, 0x20a2, 0x00c0, 0x2001, 0xb535, 0x2004, 0xd0ac, 0x1138,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xa082, 0x007e, 0x0240, 0x00d6,
+ 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0020, 0x20a3,
+ 0x0000, 0x6034, 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x000c, 0x080c, 0x7d67, 0x001e,
+ 0x00de, 0x0005, 0x7817, 0x0001, 0x7803, 0x0006, 0x001e, 0x00de,
+ 0x0005, 0x00d6, 0x0026, 0x7928, 0x2168, 0x691c, 0xa186, 0x0006,
+ 0x01c0, 0xa186, 0x0003, 0x0904, 0x8478, 0xa186, 0x0005, 0x0904,
+ 0x8461, 0xa186, 0x0004, 0x05b8, 0xa186, 0x0008, 0x0904, 0x8469,
+ 0x7807, 0x0037, 0x7813, 0x1700, 0x080c, 0x84e0, 0x002e, 0x00de,
+ 0x0005, 0x080c, 0x849c, 0x2009, 0x4000, 0x6800, 0x0002, 0x8442,
+ 0x844d, 0x8444, 0x844d, 0x8449, 0x8442, 0x8442, 0x844d, 0x844d,
+ 0x844d, 0x844d, 0x8442, 0x8442, 0x8442, 0x8442, 0x8442, 0x844d,
+ 0x8442, 0x844d, 0x080c, 0x1515, 0x6820, 0xd0e4, 0x0110, 0xd0cc,
+ 0x0110, 0xa00e, 0x0010, 0x2009, 0x2000, 0x6828, 0x20a2, 0x682c,
+ 0x20a2, 0x0804, 0x8492, 0x080c, 0x849c, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x2009, 0x4000, 0x6a00, 0xa286, 0x0002, 0x1108, 0xa00e,
+ 0x0488, 0x04d1, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000,
+ 0x0448, 0x0491, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000,
+ 0xa286, 0x0005, 0x0118, 0xa286, 0x0002, 0x1108, 0xa00e, 0x00d0,
+ 0x0419, 0x6810, 0x2068, 0x697c, 0x6810, 0xa112, 0x6980, 0x6814,
+ 0xa103, 0x20a2, 0x22a2, 0x7928, 0xa180, 0x0000, 0x2004, 0xa08e,
+ 0x0002, 0x0130, 0xa08e, 0x0004, 0x0118, 0x2009, 0x4000, 0x0010,
+ 0x2009, 0x0000, 0x21a2, 0x20a3, 0x0000, 0x60c3, 0x0018, 0x080c,
+ 0x7d67, 0x002e, 0x00de, 0x0005, 0x0036, 0x0046, 0x0056, 0x0066,
+ 0x20a1, 0x020b, 0x080c, 0x76dd, 0xa006, 0x20a3, 0x0200, 0x20a2,
+ 0x7934, 0x21a2, 0x7938, 0x21a2, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0x2011, 0xb535, 0x2214, 0xd2ac, 0x1118, 0xa092, 0x007e, 0x0268,
+ 0x00d6, 0x2069, 0xb51c, 0x2d2c, 0x8d68, 0x2d34, 0xa0e8, 0xb635,
+ 0x2d6c, 0x6b10, 0x6c14, 0x00de, 0x0030, 0x2019, 0x0000, 0x6498,
+ 0x2029, 0x0000, 0x6634, 0x7828, 0xa080, 0x0007, 0x2004, 0xa086,
+ 0x0003, 0x1128, 0x25a2, 0x26a2, 0x23a2, 0x24a2, 0x0020, 0x23a2,
+ 0x24a2, 0x25a2, 0x26a2, 0x006e, 0x005e, 0x004e, 0x003e, 0x0005,
+ 0x20a1, 0x020b, 0x080c, 0x76dd, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x0009, 0x7810, 0x20a2, 0x60c3, 0x0008, 0x080c, 0x7d67,
+ 0x0005, 0x20a1, 0x020b, 0x080c, 0x7639, 0x20a3, 0x1400, 0x20a3,
+ 0x0000, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x7828, 0x20a2, 0x782c,
+ 0x20a2, 0x7830, 0xa084, 0x00ff, 0x8007, 0x20a2, 0x20a3, 0x0000,
+ 0x60c3, 0x0010, 0x080c, 0x7d67, 0x0005, 0x20a1, 0x020b, 0x080c,
+ 0x76d5, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x7828, 0x20a2, 0x7810,
+ 0x20a2, 0x60c3, 0x0008, 0x080c, 0x7d67, 0x0005, 0x0146, 0x20a1,
+ 0x020b, 0x0031, 0x60c3, 0x0000, 0x080c, 0x7d67, 0x014e, 0x0005,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0x2011, 0xb535, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188, 0x00d6,
+ 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0300, 0x20a2, 0x6814,
+ 0x20a2, 0x2069, 0xb51c, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0078,
+ 0x00d6, 0xa0e8, 0xb635, 0x2d6c, 0x6810, 0xa085, 0x0300, 0x20a2,
+ 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x6234, 0x22a2, 0x20a3,
+ 0x0819, 0x20a3, 0x0000, 0x080c, 0x7d56, 0x22a2, 0x20a3, 0x0000,
+ 0x2fa2, 0x7a08, 0x22a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x0005,
+ 0x20a1, 0x020b, 0x0079, 0x7910, 0x21a2, 0x20a3, 0x0000, 0x60c3,
+ 0x0000, 0x20e1, 0x9080, 0x60a7, 0x9575, 0x080c, 0x7d71, 0x080c,
+ 0x6a07, 0x0005, 0x0156, 0x0136, 0x0036, 0x00d6, 0x00e6, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x7854, 0x2068, 0xadf0, 0x000f, 0x7210,
+ 0xa296, 0x00c0, 0xa294, 0xfffd, 0x7212, 0x7214, 0xa294, 0x0300,
+ 0x7216, 0x7100, 0xa194, 0x00ff, 0x7308, 0xa384, 0x00ff, 0xa08d,
+ 0xc200, 0x7102, 0xa384, 0xff00, 0xa215, 0x720a, 0x7004, 0x720c,
+ 0x700e, 0x7206, 0x20a9, 0x000a, 0x2e98, 0x53a6, 0x60a3, 0x0035,
+ 0x6a38, 0xa294, 0x7000, 0xa286, 0x3000, 0x0110, 0x60a3, 0x0037,
+ 0x00ee, 0x00de, 0x003e, 0x013e, 0x015e, 0x0005, 0x2009, 0x0092,
+ 0x0010, 0x2009, 0x0096, 0x60ab, 0x0036, 0x6116, 0x0005, 0x2061,
+ 0xbd00, 0x2a70, 0x7068, 0x704a, 0x704f, 0xbd00, 0x0005, 0x00e6,
+ 0x0126, 0x2071, 0xb500, 0x2091, 0x8000, 0x7548, 0xa582, 0x0010,
+ 0x0608, 0x704c, 0x2060, 0x6000, 0xa086, 0x0000, 0x0148, 0xace0,
+ 0x0018, 0x705c, 0xac02, 0x1208, 0x0cb0, 0x2061, 0xbd00, 0x0c98,
+ 0x6003, 0x0008, 0x8529, 0x754a, 0xaca8, 0x0018, 0x705c, 0xa502,
+ 0x1230, 0x754e, 0xa085, 0x0001, 0x012e, 0x00ee, 0x0005, 0x704f,
+ 0xbd00, 0x0cc0, 0xa006, 0x0cc0, 0x00e6, 0x2071, 0xb500, 0x7548,
+ 0xa582, 0x0010, 0x0600, 0x704c, 0x2060, 0x6000, 0xa086, 0x0000,
+ 0x0148, 0xace0, 0x0018, 0x705c, 0xac02, 0x1208, 0x0cb0, 0x2061,
+ 0xbd00, 0x0c98, 0x6003, 0x0008, 0x8529, 0x754a, 0xaca8, 0x0018,
+ 0x705c, 0xa502, 0x1228, 0x754e, 0xa085, 0x0001, 0x00ee, 0x0005,
+ 0x704f, 0xbd00, 0x0cc8, 0xa006, 0x0cc8, 0xac82, 0xbd00, 0x0a0c,
+ 0x1515, 0x2001, 0xb517, 0x2004, 0xac02, 0x1a0c, 0x1515, 0xa006,
+ 0x6006, 0x600a, 0x600e, 0x6012, 0x6016, 0x601a, 0x601f, 0x0000,
+ 0x6003, 0x0000, 0x6052, 0x6056, 0x6022, 0x6026, 0x602a, 0x602e,
+ 0x6032, 0x6036, 0x603a, 0x603e, 0x2061, 0xb500, 0x6048, 0x8000,
+ 0x604a, 0xa086, 0x0001, 0x0108, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x7173, 0x012e, 0x0cc0, 0x601c, 0xa084, 0x000f, 0x0002,
+ 0x865b, 0x866a, 0x8685, 0x86a0, 0xa152, 0xa16d, 0xa188, 0x865b,
+ 0x866a, 0x865b, 0x86bb, 0xa186, 0x0013, 0x1128, 0x080c, 0x7090,
+ 0x080c, 0x7173, 0x0005, 0xa18e, 0x0047, 0x1118, 0xa016, 0x080c,
+ 0x185e, 0x0005, 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x1515,
+ 0x0013, 0x006e, 0x0005, 0x8683, 0x8a9b, 0x8c53, 0x8683, 0x8cc8,
+ 0x8779, 0x8683, 0x8683, 0x8a2d, 0x90ef, 0x8683, 0x8683, 0x8683,
+ 0x8683, 0x8683, 0x8683, 0x080c, 0x1515, 0x0066, 0x6000, 0xa0b2,
+ 0x0010, 0x1a0c, 0x1515, 0x0013, 0x006e, 0x0005, 0x869e, 0x9722,
+ 0x869e, 0x869e, 0x869e, 0x869e, 0x869e, 0x869e, 0x96cd, 0x988e,
+ 0x869e, 0x974f, 0x97c6, 0x974f, 0x97c6, 0x869e, 0x080c, 0x1515,
+ 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x1515, 0x0013, 0x006e,
+ 0x0005, 0x86b9, 0x9130, 0x91fa, 0x9335, 0x9491, 0x86b9, 0x86b9,
+ 0x86b9, 0x910a, 0x967d, 0x9680, 0x86b9, 0x86b9, 0x86b9, 0x86b9,
+ 0x96aa, 0x080c, 0x1515, 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c,
+ 0x1515, 0x0013, 0x006e, 0x0005, 0x86d4, 0x86d4, 0x86d4, 0x8702,
+ 0x874f, 0x86d4, 0x86d4, 0x86d4, 0x86d6, 0x86d4, 0x86d4, 0x86d4,
+ 0x86d4, 0x86d4, 0x86d4, 0x86d4, 0x080c, 0x1515, 0xa186, 0x0003,
+ 0x190c, 0x1515, 0x00d6, 0x6003, 0x0003, 0x6106, 0x6010, 0x2068,
+ 0x684f, 0x0040, 0x687c, 0x680a, 0x6880, 0x680e, 0x6813, 0x0000,
+ 0x6817, 0x0000, 0x6854, 0xa092, 0x199a, 0x0210, 0x2001, 0x1999,
+ 0x8003, 0x8013, 0x8213, 0xa210, 0x6216, 0x00de, 0x2c10, 0x080c,
+ 0x1fa9, 0x080c, 0x6cf0, 0x0126, 0x2091, 0x8000, 0x080c, 0x7230,
+ 0x012e, 0x0005, 0xa182, 0x0047, 0x0002, 0x870e, 0x870e, 0x8710,
+ 0x8729, 0x870e, 0x870e, 0x870e, 0x870e, 0x873b, 0x080c, 0x1515,
+ 0x00d6, 0x0016, 0x080c, 0x7126, 0x080c, 0x7230, 0x6003, 0x0004,
+ 0x6110, 0x2168, 0x684f, 0x0020, 0x685c, 0x685a, 0x6874, 0x687e,
+ 0x6878, 0x6882, 0x6897, 0x0000, 0x689b, 0x0000, 0x001e, 0x00de,
+ 0x0005, 0x080c, 0x7126, 0x00d6, 0x6110, 0x2168, 0x080c, 0x9c5a,
+ 0x0120, 0x684b, 0x0006, 0x080c, 0x5408, 0x00de, 0x080c, 0x861d,
+ 0x080c, 0x7230, 0x0005, 0x080c, 0x7126, 0x080c, 0x2c9c, 0x00d6,
+ 0x6110, 0x2168, 0x080c, 0x9c5a, 0x0120, 0x684b, 0x0029, 0x080c,
+ 0x5408, 0x00de, 0x080c, 0x861d, 0x080c, 0x7230, 0x0005, 0xa182,
+ 0x0047, 0x0002, 0x875d, 0x876c, 0x875b, 0x875b, 0x875b, 0x875b,
+ 0x875b, 0x875b, 0x875b, 0x080c, 0x1515, 0x00d6, 0x6010, 0x2068,
+ 0x684c, 0xc0f4, 0x684e, 0x00de, 0x20e1, 0x0005, 0x3d18, 0x3e20,
+ 0x2c10, 0x080c, 0x185e, 0x0005, 0x00d6, 0x6110, 0x2168, 0x684b,
+ 0x0000, 0x6853, 0x0000, 0x080c, 0x5408, 0x00de, 0x080c, 0x861d,
+ 0x0005, 0xa1b6, 0x0015, 0x1118, 0x080c, 0x861d, 0x0030, 0xa1b6,
+ 0x0016, 0x190c, 0x1515, 0x080c, 0x861d, 0x0005, 0x20a9, 0x000e,
+ 0x2e98, 0x6010, 0x20a0, 0x53a3, 0x20a9, 0x0006, 0x3310, 0x3420,
+ 0x9398, 0x94a0, 0x3318, 0x3428, 0x222e, 0x2326, 0xa290, 0x0002,
+ 0xa5a8, 0x0002, 0xa398, 0x0002, 0xa4a0, 0x0002, 0x1f04, 0x8794,
+ 0x00e6, 0x080c, 0x9c5a, 0x0130, 0x6010, 0x2070, 0x7007, 0x0000,
+ 0x7037, 0x0103, 0x00ee, 0x080c, 0x861d, 0x0005, 0x00d6, 0x0036,
+ 0x7330, 0xa386, 0x0200, 0x1130, 0x6018, 0x2068, 0x6813, 0x00ff,
+ 0x6817, 0xfffd, 0x6010, 0xa005, 0x0130, 0x2068, 0x6807, 0x0000,
+ 0x6837, 0x0103, 0x6b32, 0x080c, 0x861d, 0x003e, 0x00de, 0x0005,
+ 0x0016, 0x20a9, 0x002a, 0xae80, 0x000c, 0x2098, 0x6010, 0xa080,
+ 0x0002, 0x20a0, 0x53a3, 0x20a9, 0x002a, 0x6010, 0xa080, 0x0001,
+ 0x2004, 0xa080, 0x0002, 0x20a0, 0x53a3, 0x00e6, 0x6010, 0x2004,
+ 0x2070, 0x7037, 0x0103, 0x00ee, 0x080c, 0x861d, 0x001e, 0x0005,
+ 0x0016, 0x2009, 0x0000, 0x7030, 0xa086, 0x0100, 0x0140, 0x7038,
+ 0xa084, 0x00ff, 0x800c, 0x703c, 0xa084, 0x00ff, 0x8004, 0xa080,
+ 0x0004, 0xa108, 0x21a8, 0xae80, 0x000c, 0x2098, 0x6010, 0xa080,
+ 0x0002, 0x20a0, 0x080c, 0x4b8f, 0x00e6, 0x080c, 0x9c5a, 0x0140,
+ 0x6010, 0x2070, 0x7007, 0x0000, 0x7034, 0x70b2, 0x7037, 0x0103,
+ 0x00ee, 0x080c, 0x861d, 0x001e, 0x0005, 0x00e6, 0x00d6, 0x603f,
+ 0x0000, 0x2c68, 0x0016, 0x2009, 0x0035, 0x080c, 0xa10a, 0x001e,
+ 0x1168, 0x0026, 0x6228, 0x2268, 0x002e, 0x2071, 0xbb8c, 0x6b1c,
+ 0xa386, 0x0003, 0x0130, 0xa386, 0x0006, 0x0128, 0x080c, 0x861d,
+ 0x0020, 0x0031, 0x0010, 0x080c, 0x88f6, 0x00de, 0x00ee, 0x0005,
+ 0x00f6, 0x6810, 0x2078, 0xa186, 0x0015, 0x0904, 0x88dd, 0xa18e,
+ 0x0016, 0x1904, 0x88f4, 0x700c, 0xa08c, 0xff00, 0xa186, 0x1700,
+ 0x0120, 0xa186, 0x0300, 0x1904, 0x88bc, 0x8fff, 0x1138, 0x6800,
+ 0xa086, 0x000f, 0x0904, 0x88a0, 0x0804, 0x88f2, 0x6808, 0xa086,
+ 0xffff, 0x1904, 0x88df, 0x784c, 0xa084, 0x0060, 0xa086, 0x0020,
+ 0x1150, 0x797c, 0x7810, 0xa106, 0x1904, 0x88df, 0x7980, 0x7814,
+ 0xa106, 0x1904, 0x88df, 0x080c, 0x9e11, 0x6858, 0x7852, 0x784c,
+ 0xc0dc, 0xc0f4, 0xc0d4, 0x784e, 0x0026, 0xa00e, 0x6a14, 0x2001,
+ 0x000a, 0x080c, 0x6b40, 0x7854, 0xa20a, 0x0208, 0x8011, 0x7a56,
+ 0x82ff, 0x002e, 0x1138, 0x00c6, 0x2d60, 0x080c, 0x9a09, 0x00ce,
+ 0x0804, 0x88f2, 0x00c6, 0x00d6, 0x2f68, 0x6838, 0xd0fc, 0x1118,
+ 0x080c, 0x4c64, 0x0010, 0x080c, 0x4e49, 0x00de, 0x00ce, 0x1904,
+ 0x88df, 0x00c6, 0x2d60, 0x080c, 0x861d, 0x00ce, 0x0804, 0x88f2,
+ 0x00c6, 0x080c, 0x9ed6, 0x0190, 0x6013, 0x0000, 0x6818, 0x601a,
+ 0x080c, 0xa027, 0x601f, 0x0003, 0x6904, 0x00c6, 0x2d60, 0x080c,
+ 0x861d, 0x00ce, 0x080c, 0x864c, 0x00ce, 0x04e0, 0x2001, 0xb7b8,
+ 0x2004, 0x683e, 0x00ce, 0x04b0, 0x7008, 0xa086, 0x000b, 0x11a0,
+ 0x6018, 0x200c, 0xc1bc, 0x2102, 0x00c6, 0x2d60, 0x7853, 0x0003,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x080c, 0x6c8d,
+ 0x080c, 0x7173, 0x00ce, 0x00f0, 0x700c, 0xa086, 0x2a00, 0x1138,
+ 0x2001, 0xb7b8, 0x2004, 0x683e, 0x00a8, 0x0481, 0x00a8, 0x8fff,
+ 0x090c, 0x1515, 0x00c6, 0x00d6, 0x2d60, 0x2f68, 0x6837, 0x0103,
+ 0x684b, 0x0003, 0x080c, 0x98fd, 0x080c, 0x9e11, 0x080c, 0x9e1d,
+ 0x00de, 0x00ce, 0x080c, 0x861d, 0x00fe, 0x0005, 0xa186, 0x0015,
+ 0x1128, 0x2001, 0xb7b8, 0x2004, 0x683e, 0x0068, 0xa18e, 0x0016,
+ 0x1160, 0x00c6, 0x2d00, 0x2060, 0x080c, 0xb33a, 0x080c, 0x6aef,
+ 0x080c, 0x861d, 0x00ce, 0x080c, 0x861d, 0x0005, 0x0026, 0x0036,
+ 0x0046, 0x7228, 0x7c80, 0x7b7c, 0xd2f4, 0x0130, 0x2001, 0xb7b8,
+ 0x2004, 0x683e, 0x0804, 0x8970, 0x00c6, 0x2d60, 0x080c, 0x991d,
+ 0x00ce, 0x6804, 0xa086, 0x0050, 0x1168, 0x00c6, 0x2d00, 0x2060,
+ 0x6003, 0x0001, 0x6007, 0x0050, 0x080c, 0x6c8d, 0x080c, 0x7173,
+ 0x00ce, 0x04f0, 0x6800, 0xa086, 0x000f, 0x01c8, 0x8fff, 0x090c,
+ 0x1515, 0x6820, 0xd0dc, 0x1198, 0x6800, 0xa086, 0x0004, 0x1198,
+ 0x784c, 0xd0ac, 0x0180, 0x784c, 0xc0dc, 0xc0f4, 0x784e, 0x7850,
+ 0xc0f4, 0xc0fc, 0x7852, 0x2001, 0x0001, 0x682e, 0x00e0, 0x2001,
+ 0x0007, 0x682e, 0x00c0, 0x784c, 0xd0b4, 0x1130, 0xd0ac, 0x0db8,
+ 0x784c, 0xd0f4, 0x1da0, 0x0c38, 0xd2ec, 0x1d88, 0x7024, 0xa306,
+ 0x1118, 0x7020, 0xa406, 0x0d58, 0x7020, 0x6836, 0x7024, 0x683a,
+ 0x2001, 0x0005, 0x682e, 0x080c, 0x9f63, 0x080c, 0x7173, 0x0010,
+ 0x080c, 0x861d, 0x004e, 0x003e, 0x002e, 0x0005, 0x00e6, 0x00d6,
+ 0x0026, 0x6034, 0x2068, 0x6a1c, 0xa286, 0x0007, 0x0904, 0x89d4,
+ 0xa286, 0x0002, 0x0904, 0x89d4, 0xa286, 0x0000, 0x0904, 0x89d4,
+ 0x6808, 0x6338, 0xa306, 0x1904, 0x89d4, 0x2071, 0xbb8c, 0xa186,
+ 0x0015, 0x05e0, 0xa18e, 0x0016, 0x1190, 0x6030, 0xa084, 0x00ff,
+ 0xa086, 0x0001, 0x1160, 0x700c, 0xa086, 0x2a00, 0x1140, 0x6034,
+ 0xa080, 0x0008, 0x200c, 0xc1dd, 0xc1f5, 0x2102, 0x0438, 0x00c6,
+ 0x6034, 0x2060, 0x6104, 0xa186, 0x004b, 0x01a0, 0xa186, 0x004c,
+ 0x0188, 0xa186, 0x004d, 0x0170, 0xa186, 0x004e, 0x0158, 0xa186,
+ 0x0052, 0x0140, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x090c, 0x1515,
+ 0x6853, 0x0003, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002,
+ 0x080c, 0x6c8d, 0x080c, 0x7173, 0x00ce, 0x0030, 0x6034, 0x2070,
+ 0x2001, 0xb7b8, 0x2004, 0x703e, 0x080c, 0x861d, 0x002e, 0x00de,
+ 0x00ee, 0x0005, 0x00d6, 0x20a9, 0x000e, 0x2e98, 0x6010, 0x20a0,
+ 0x53a3, 0xa1b6, 0x0015, 0x1558, 0x6018, 0x2068, 0x0156, 0x0036,
+ 0x0026, 0xae90, 0x000c, 0xa290, 0x0004, 0x20a9, 0x0004, 0xad98,
+ 0x000a, 0x080c, 0x90da, 0x002e, 0x003e, 0x015e, 0x11d8, 0x0156,
+ 0x0036, 0x0026, 0xae90, 0x000c, 0xa290, 0x0008, 0x20a9, 0x0004,
+ 0xad98, 0x0006, 0x080c, 0x90da, 0x002e, 0x003e, 0x015e, 0x1150,
+ 0x7038, 0x680a, 0x703c, 0x680e, 0x6800, 0xc08d, 0x6802, 0x00de,
+ 0x0804, 0x87a0, 0x080c, 0x2c9c, 0x00c6, 0x080c, 0x85c7, 0x2f00,
+ 0x601a, 0x6013, 0x0000, 0x601f, 0x0001, 0x6007, 0x0001, 0x6003,
+ 0x0001, 0x2001, 0x0007, 0x080c, 0x4efd, 0x080c, 0x4f2a, 0x080c,
+ 0x6cd3, 0x080c, 0x7173, 0x00ce, 0x0c10, 0x2100, 0xa1b2, 0x0080,
+ 0x1a0c, 0x1515, 0xa1b2, 0x0040, 0x1a04, 0x8a91, 0x0002, 0x8a85,
+ 0x8a79, 0x8a85, 0x8a85, 0x8a85, 0x8a85, 0x8a77, 0x8a77, 0x8a77,
+ 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77,
+ 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77,
+ 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a85, 0x8a77,
+ 0x8a85, 0x8a85, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a85,
+ 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77,
+ 0x8a77, 0x8a85, 0x8a85, 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a77,
+ 0x8a77, 0x8a77, 0x8a77, 0x8a77, 0x8a85, 0x8a77, 0x8a77, 0x080c,
+ 0x1515, 0x6003, 0x0001, 0x6106, 0x080c, 0x6cd3, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x7173, 0x012e, 0x0005, 0x6003, 0x0001, 0x6106,
+ 0x080c, 0x6cd3, 0x0126, 0x2091, 0x8000, 0x080c, 0x7173, 0x012e,
+ 0x0005, 0x2600, 0x0002, 0x8a85, 0x8a85, 0x8a99, 0x8a85, 0x8a85,
+ 0x8a99, 0x080c, 0x1515, 0x6004, 0xa0b2, 0x0080, 0x1a0c, 0x1515,
+ 0xa1b6, 0x0013, 0x0904, 0x8b4b, 0xa1b6, 0x0027, 0x1904, 0x8b11,
+ 0x080c, 0x7090, 0x6004, 0x080c, 0x9e47, 0x0190, 0x080c, 0x9e58,
+ 0x0904, 0x8b0b, 0xa08e, 0x0021, 0x0904, 0x8b0e, 0xa08e, 0x0022,
+ 0x0904, 0x8b0b, 0xa08e, 0x003d, 0x0904, 0x8b0e, 0x0804, 0x8b04,
+ 0x080c, 0x2cc2, 0x2001, 0x0007, 0x080c, 0x4efd, 0x6018, 0xa080,
+ 0x0028, 0x200c, 0x080c, 0x8c19, 0xa186, 0x007e, 0x1148, 0x2001,
+ 0xb535, 0x2014, 0xc285, 0x080c, 0x5acf, 0x1108, 0xc2ad, 0x2202,
+ 0x0016, 0x0026, 0x0036, 0x2110, 0x0026, 0x2019, 0x0028, 0x080c,
+ 0x8299, 0x002e, 0x080c, 0xb38d, 0x003e, 0x002e, 0x001e, 0x0016,
+ 0x0026, 0x0036, 0x2110, 0x2019, 0x0028, 0x080c, 0x6df5, 0x0076,
+ 0x2039, 0x0000, 0x080c, 0x6d02, 0x00c6, 0x6018, 0xa065, 0x0110,
+ 0x080c, 0x51aa, 0x00ce, 0x2c08, 0x080c, 0xae82, 0x007e, 0x003e,
+ 0x002e, 0x001e, 0x080c, 0x4f6c, 0x080c, 0xa01f, 0x080c, 0x861d,
+ 0x080c, 0x7173, 0x0005, 0x080c, 0x8c19, 0x0cb0, 0x080c, 0x8c47,
+ 0x0c98, 0xa186, 0x0014, 0x1db0, 0x080c, 0x7090, 0x080c, 0x2c9c,
+ 0x080c, 0x9e47, 0x1188, 0x080c, 0x2cc2, 0x6018, 0xa080, 0x0028,
+ 0x200c, 0x080c, 0x8c19, 0xa186, 0x007e, 0x1128, 0x2001, 0xb535,
+ 0x200c, 0xc185, 0x2102, 0x08c0, 0x080c, 0x9e58, 0x1118, 0x080c,
+ 0x8c19, 0x0890, 0x6004, 0xa08e, 0x0032, 0x1158, 0x00e6, 0x00f6,
+ 0x2071, 0xb582, 0x2079, 0x0000, 0x080c, 0x2fcf, 0x00fe, 0x00ee,
+ 0x0818, 0x6004, 0xa08e, 0x0021, 0x0d50, 0xa08e, 0x0022, 0x090c,
+ 0x8c19, 0x0804, 0x8b04, 0xa0b2, 0x0040, 0x1a04, 0x8c0e, 0x2008,
+ 0x0002, 0x8b93, 0x8b94, 0x8b97, 0x8b9a, 0x8b9d, 0x8ba0, 0x8b91,
+ 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91,
+ 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91,
+ 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8ba3,
+ 0x8bb2, 0x8b91, 0x8bb4, 0x8bb2, 0x8b91, 0x8b91, 0x8b91, 0x8b91,
+ 0x8b91, 0x8bb2, 0x8bb2, 0x8b91, 0x8b91, 0x8b91, 0x8b91, 0x8b91,
+ 0x8b91, 0x8b91, 0x8b91, 0x8bee, 0x8bb2, 0x8b91, 0x8bae, 0x8b91,
+ 0x8b91, 0x8b91, 0x8baf, 0x8b91, 0x8b91, 0x8b91, 0x8bb2, 0x8be5,
+ 0x8b91, 0x080c, 0x1515, 0x00f0, 0x2001, 0x000b, 0x0460, 0x2001,
+ 0x0003, 0x0448, 0x2001, 0x0005, 0x0430, 0x2001, 0x0001, 0x0418,
+ 0x2001, 0x0009, 0x0400, 0x080c, 0x7090, 0x6003, 0x0005, 0x2001,
+ 0xb7b8, 0x2004, 0x603e, 0x080c, 0x7173, 0x00a0, 0x0018, 0x0010,
+ 0x080c, 0x4efd, 0x0804, 0x8bff, 0x080c, 0x7090, 0x2001, 0xb7b6,
+ 0x2004, 0x6016, 0x2001, 0xb7b8, 0x2004, 0x603e, 0x6003, 0x0004,
+ 0x080c, 0x7173, 0x0005, 0x080c, 0x4efd, 0x080c, 0x7090, 0x6003,
+ 0x0002, 0x2001, 0xb7b8, 0x2004, 0x603e, 0x0036, 0x2019, 0xb55d,
+ 0x2304, 0xa084, 0xff00, 0x1120, 0x2001, 0xb7b6, 0x201c, 0x0040,
+ 0x8007, 0xa09a, 0x0004, 0x0ec0, 0x8003, 0x801b, 0x831b, 0xa318,
+ 0x6316, 0x003e, 0x080c, 0x7173, 0x08e8, 0x080c, 0x7090, 0x080c,
+ 0xa01f, 0x080c, 0x861d, 0x080c, 0x7173, 0x08a0, 0x00e6, 0x00f6,
+ 0x2071, 0xb582, 0x2079, 0x0000, 0x080c, 0x2fcf, 0x00fe, 0x00ee,
+ 0x080c, 0x7090, 0x080c, 0x861d, 0x080c, 0x7173, 0x0818, 0x080c,
+ 0x7090, 0x2001, 0xb7b8, 0x2004, 0x603e, 0x6003, 0x0002, 0x2001,
+ 0xb7b6, 0x2004, 0x6016, 0x080c, 0x7173, 0x0005, 0x2600, 0x2008,
+ 0x0002, 0x8c17, 0x8c17, 0x8c17, 0x8bff, 0x8bff, 0x8c17, 0x080c,
+ 0x1515, 0x00e6, 0x0026, 0x0016, 0x080c, 0x9c5a, 0x0508, 0x6010,
+ 0x2070, 0x7034, 0xa086, 0x0139, 0x1148, 0x2001, 0x0030, 0x2009,
+ 0x0000, 0x2011, 0x4005, 0x080c, 0xa0d6, 0x0090, 0x7038, 0xd0fc,
+ 0x0178, 0x7007, 0x0000, 0x0016, 0x6004, 0xa08e, 0x0021, 0x0160,
+ 0xa08e, 0x003d, 0x0148, 0x001e, 0x7037, 0x0103, 0x7033, 0x0100,
+ 0x001e, 0x002e, 0x00ee, 0x0005, 0x001e, 0x0009, 0x0cc8, 0x00e6,
+ 0xacf0, 0x0004, 0x2e74, 0x7000, 0x2070, 0x7037, 0x0103, 0x7023,
+ 0x8001, 0x00ee, 0x0005, 0x00d6, 0x6618, 0x2668, 0x6804, 0xa084,
+ 0x00ff, 0x00de, 0xa0b2, 0x000c, 0x1a0c, 0x1515, 0x6604, 0xa6b6,
+ 0x0043, 0x1120, 0x080c, 0xa092, 0x0804, 0x8cb8, 0x6604, 0xa6b6,
+ 0x0033, 0x1120, 0x080c, 0xa042, 0x0804, 0x8cb8, 0x6604, 0xa6b6,
+ 0x0028, 0x1120, 0x080c, 0x9e88, 0x0804, 0x8cb8, 0x6604, 0xa6b6,
+ 0x0029, 0x1118, 0x080c, 0x9e9f, 0x04d8, 0x6604, 0xa6b6, 0x001f,
+ 0x1118, 0x080c, 0x8786, 0x04a0, 0x6604, 0xa6b6, 0x0000, 0x1118,
+ 0x080c, 0x89da, 0x0468, 0x6604, 0xa6b6, 0x0022, 0x1118, 0x080c,
+ 0x87ae, 0x0430, 0x6604, 0xa6b6, 0x0035, 0x1118, 0x080c, 0x8815,
+ 0x00f8, 0x6604, 0xa6b6, 0x0039, 0x1118, 0x080c, 0x8976, 0x00c0,
+ 0x6604, 0xa6b6, 0x003d, 0x1118, 0x080c, 0x87c8, 0x0088, 0x6604,
+ 0xa6b6, 0x0044, 0x1118, 0x080c, 0x87e8, 0x0050, 0xa1b6, 0x0015,
+ 0x1110, 0x0053, 0x0028, 0xa1b6, 0x0016, 0x1118, 0x0804, 0x8e7c,
+ 0x0005, 0x080c, 0x8663, 0x0ce0, 0x8cdf, 0x8ce2, 0x8cdf, 0x8d24,
+ 0x8cdf, 0x8e09, 0x8e8a, 0x8cdf, 0x8cdf, 0x8e58, 0x8cdf, 0x8e6c,
+ 0xa1b6, 0x0048, 0x0140, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10,
+ 0x080c, 0x185e, 0x0005, 0x00e6, 0xacf0, 0x0004, 0x2e74, 0x7000,
+ 0x2070, 0x7037, 0x0103, 0x00ee, 0x080c, 0x861d, 0x0005, 0xe000,
+ 0xe000, 0x0005, 0x00e6, 0x2071, 0xb500, 0x7084, 0xa086, 0x0074,
+ 0x1530, 0x080c, 0xae59, 0x11b0, 0x00d6, 0x6018, 0x2068, 0x7030,
+ 0xd08c, 0x0128, 0x6800, 0xd0bc, 0x0110, 0xc0c5, 0x6802, 0x00d9,
+ 0x00de, 0x2001, 0x0006, 0x080c, 0x4efd, 0x080c, 0x2cc2, 0x080c,
+ 0x861d, 0x0078, 0x2001, 0x000a, 0x080c, 0x4efd, 0x080c, 0x2cc2,
+ 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x6cd3, 0x0010, 0x080c,
+ 0x8df6, 0x00ee, 0x0005, 0x6800, 0xd084, 0x0168, 0x2001, 0x0000,
+ 0x080c, 0x4eeb, 0x2069, 0xb552, 0x6804, 0xd0a4, 0x0120, 0x2001,
+ 0x0006, 0x080c, 0x4f2a, 0x0005, 0x00d6, 0x2011, 0xb521, 0x2204,
+ 0xa086, 0x0074, 0x1904, 0x8df3, 0x6018, 0x2068, 0x6aa0, 0xa286,
+ 0x007e, 0x1120, 0x080c, 0x8fa2, 0x0804, 0x8d92, 0x080c, 0x8f98,
+ 0x6018, 0x2068, 0xa080, 0x0028, 0x2014, 0xa286, 0x0080, 0x11c0,
+ 0x6813, 0x00ff, 0x6817, 0xfffc, 0x6010, 0xa005, 0x0138, 0x2068,
+ 0x6807, 0x0000, 0x6837, 0x0103, 0x6833, 0x0200, 0x2001, 0x0006,
+ 0x080c, 0x4efd, 0x080c, 0x2cc2, 0x080c, 0x861d, 0x0804, 0x8df4,
+ 0x00e6, 0x2071, 0xb535, 0x2e04, 0xd09c, 0x0188, 0x2071, 0xbb80,
+ 0x7108, 0x720c, 0xa18c, 0x00ff, 0x1118, 0xa284, 0xff00, 0x0138,
+ 0x6018, 0x2070, 0x70a0, 0xd0bc, 0x1110, 0x7112, 0x7216, 0x00ee,
+ 0x6010, 0xa005, 0x0198, 0x2068, 0x6838, 0xd0f4, 0x0178, 0x6834,
+ 0xa084, 0x00ff, 0xa086, 0x0039, 0x1958, 0x2001, 0x0000, 0x2009,
+ 0x0000, 0x2011, 0x4000, 0x080c, 0xa0d6, 0x0840, 0x2001, 0x0004,
+ 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007, 0x0003, 0x080c, 0x6cd3,
+ 0x0804, 0x8df4, 0x685c, 0xd0e4, 0x01d8, 0x080c, 0x9fd2, 0x080c,
+ 0x5acf, 0x0118, 0xd0dc, 0x1904, 0x8d4e, 0x2011, 0xb535, 0x2204,
+ 0xc0ad, 0x2012, 0x2001, 0xb78f, 0x2004, 0x00f6, 0x2079, 0x0100,
+ 0x78e3, 0x0000, 0x080c, 0x2872, 0x78e2, 0x00fe, 0x0804, 0x8d4e,
+ 0x080c, 0xa008, 0x2011, 0xb535, 0x2204, 0xc0a5, 0x2012, 0x0006,
+ 0x080c, 0xaf7b, 0x000e, 0x1904, 0x8d4e, 0xc0b5, 0x2012, 0x2001,
+ 0x0006, 0x080c, 0x4efd, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x00c6,
+ 0x2009, 0x00ef, 0x00f6, 0x2079, 0x0100, 0x79ea, 0x7932, 0x7936,
+ 0x00fe, 0x080c, 0x2847, 0x00f6, 0x2079, 0xb500, 0x7976, 0x2100,
+ 0x2009, 0x0000, 0x080c, 0x281d, 0x7952, 0x00fe, 0x8108, 0x080c,
+ 0x4f4d, 0x2c00, 0x00ce, 0x1904, 0x8d4e, 0x601a, 0x2001, 0x0002,
+ 0x080c, 0x4efd, 0x601f, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002,
+ 0x080c, 0x6cd3, 0x0008, 0x0011, 0x00de, 0x0005, 0x2001, 0x0007,
+ 0x080c, 0x4efd, 0x2001, 0xb500, 0x2004, 0xa086, 0x0003, 0x1120,
+ 0x2001, 0x0007, 0x080c, 0x4f2a, 0x080c, 0x2cc2, 0x080c, 0x861d,
+ 0x0005, 0x00e6, 0x0026, 0x0016, 0x2071, 0xb500, 0x7084, 0xa086,
+ 0x0014, 0x15f0, 0x7000, 0xa086, 0x0003, 0x1128, 0x6010, 0xa005,
+ 0x1110, 0x080c, 0x3f3e, 0x00d6, 0x6018, 0x2068, 0x080c, 0x504b,
+ 0x080c, 0x8d13, 0x00de, 0x080c, 0x9051, 0x1550, 0x00d6, 0x6018,
+ 0x2068, 0x6890, 0x00de, 0xa005, 0x0518, 0x2001, 0x0006, 0x080c,
+ 0x4efd, 0x00e6, 0x6010, 0xa075, 0x01a8, 0x7034, 0xa084, 0x00ff,
+ 0xa086, 0x0039, 0x1148, 0x2001, 0x0000, 0x2009, 0x0000, 0x2011,
+ 0x4000, 0x080c, 0xa0d6, 0x0030, 0x7007, 0x0000, 0x7037, 0x0103,
+ 0x7033, 0x0200, 0x00ee, 0x080c, 0x2cc2, 0x080c, 0x861d, 0x0020,
+ 0x080c, 0x8c19, 0x080c, 0x8df6, 0x001e, 0x002e, 0x00ee, 0x0005,
+ 0x2011, 0xb521, 0x2204, 0xa086, 0x0014, 0x1158, 0x2001, 0x0002,
+ 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x6cd3,
+ 0x0010, 0x080c, 0x8df6, 0x0005, 0x2011, 0xb521, 0x2204, 0xa086,
+ 0x0004, 0x1138, 0x2001, 0x0007, 0x080c, 0x4efd, 0x080c, 0x861d,
+ 0x0010, 0x080c, 0x8df6, 0x0005, 0x000b, 0x0005, 0x8cdf, 0x8e95,
+ 0x8cdf, 0x8ec9, 0x8cdf, 0x8f54, 0x8e8a, 0x8cdf, 0x8cdf, 0x8f67,
+ 0x8cdf, 0x8f77, 0x6604, 0xa686, 0x0003, 0x0904, 0x8e09, 0xa6b6,
+ 0x001e, 0x1110, 0x080c, 0x861d, 0x0005, 0x00d6, 0x00c6, 0x080c,
+ 0x8f87, 0x1178, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x2001, 0x0002,
+ 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x6cd3,
+ 0x00e8, 0x2009, 0xbb8e, 0x2104, 0xa086, 0x0009, 0x1160, 0x6018,
+ 0x2068, 0x6840, 0xa084, 0x00ff, 0xa005, 0x0170, 0x8001, 0x6842,
+ 0x6017, 0x000a, 0x0058, 0x2009, 0xbb8f, 0x2104, 0xa084, 0xff00,
+ 0xa086, 0x1900, 0x1108, 0x08d0, 0x080c, 0x8df6, 0x00ce, 0x00de,
+ 0x0005, 0x0026, 0x2011, 0x0000, 0x080c, 0x8f95, 0x00d6, 0x2069,
+ 0xb79e, 0x2d04, 0xa005, 0x0168, 0x6018, 0x2068, 0x68a0, 0xa086,
+ 0x007e, 0x1138, 0x2069, 0xb51d, 0x2d04, 0x8000, 0x206a, 0x00de,
+ 0x0010, 0x00de, 0x0078, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x2001,
+ 0x0002, 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c,
+ 0x6cd3, 0x0480, 0x00d6, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x00de,
+ 0x0108, 0x6a34, 0x080c, 0x8c19, 0x2009, 0xbb8e, 0x2134, 0xa6b4,
+ 0x00ff, 0xa686, 0x0005, 0x0500, 0xa686, 0x000b, 0x01c8, 0x2009,
+ 0xbb8f, 0x2104, 0xa084, 0xff00, 0x1118, 0xa686, 0x0009, 0x01a0,
+ 0xa086, 0x1900, 0x1168, 0xa686, 0x0009, 0x0170, 0x2001, 0x0004,
+ 0x080c, 0x4efd, 0x2001, 0x0028, 0x6016, 0x6007, 0x004b, 0x0010,
+ 0x080c, 0x8df6, 0x002e, 0x0005, 0x00d6, 0xa286, 0x0139, 0x0160,
+ 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0148, 0x6834, 0xa086, 0x0139,
+ 0x0118, 0x6838, 0xd0fc, 0x0110, 0x00de, 0x0c50, 0x6018, 0x2068,
+ 0x6840, 0xa084, 0x00ff, 0xa005, 0x0140, 0x8001, 0x6842, 0x6017,
+ 0x000a, 0x6007, 0x0016, 0x00de, 0x08e8, 0x68a0, 0xa086, 0x007e,
+ 0x1138, 0x00e6, 0x2071, 0xb500, 0x080c, 0x4bc6, 0x00ee, 0x0010,
+ 0x080c, 0x2c9c, 0x00de, 0x0860, 0x080c, 0x8f95, 0x1158, 0x2001,
+ 0x0004, 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007, 0x0003, 0x080c,
+ 0x6cd3, 0x0020, 0x080c, 0x8c19, 0x080c, 0x8df6, 0x0005, 0x0469,
+ 0x1158, 0x2001, 0x0008, 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007,
+ 0x0005, 0x080c, 0x6cd3, 0x0010, 0x080c, 0x8df6, 0x0005, 0x00e9,
+ 0x1158, 0x2001, 0x000a, 0x080c, 0x4efd, 0x6003, 0x0001, 0x6007,
+ 0x0001, 0x080c, 0x6cd3, 0x0010, 0x080c, 0x8df6, 0x0005, 0x2009,
+ 0xbb8e, 0x2104, 0xa086, 0x0003, 0x1138, 0x2009, 0xbb8f, 0x2104,
+ 0xa084, 0xff00, 0xa086, 0x2a00, 0x0005, 0xa085, 0x0001, 0x0005,
+ 0x00c6, 0x0016, 0xac88, 0x0006, 0x2164, 0x080c, 0x4fb8, 0x001e,
+ 0x00ce, 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x0036, 0x0016, 0x6018,
+ 0x2068, 0x2071, 0xb535, 0x2e04, 0xa085, 0x0003, 0x2072, 0x080c,
+ 0x9026, 0x0560, 0x2009, 0xb535, 0x2104, 0xc0cd, 0x200a, 0x2001,
+ 0xb553, 0x2004, 0xd0a4, 0x0158, 0xa006, 0x2020, 0x2009, 0x002a,
+ 0x080c, 0xb0e8, 0x2001, 0xb50c, 0x200c, 0xc195, 0x2102, 0x2019,
+ 0x002a, 0x2009, 0x0001, 0x080c, 0x2c6f, 0x2071, 0xb500, 0x080c,
+ 0x2ab8, 0x00c6, 0x0156, 0x20a9, 0x0081, 0x2009, 0x007f, 0x080c,
+ 0x2d97, 0x8108, 0x1f04, 0x8fd7, 0x015e, 0x00ce, 0x080c, 0x8f98,
+ 0x6813, 0x00ff, 0x6817, 0xfffe, 0x2071, 0xbb80, 0x2079, 0x0100,
+ 0x2e04, 0xa084, 0x00ff, 0x2069, 0xb51c, 0x206a, 0x78e6, 0x0006,
+ 0x8e70, 0x2e04, 0x2069, 0xb51d, 0x206a, 0x78ea, 0x7832, 0x7836,
+ 0x2010, 0xa084, 0xff00, 0x001e, 0xa105, 0x2009, 0xb528, 0x200a,
+ 0x2200, 0xa084, 0x00ff, 0x2008, 0x080c, 0x2847, 0x080c, 0x5acf,
+ 0x0170, 0x2069, 0xbb8e, 0x2071, 0xb7b2, 0x6810, 0x2072, 0x6814,
+ 0x7006, 0x6818, 0x700a, 0x681c, 0x700e, 0x080c, 0x9fd2, 0x0040,
+ 0x2001, 0x0006, 0x080c, 0x4efd, 0x080c, 0x2cc2, 0x080c, 0x861d,
+ 0x001e, 0x003e, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x0026, 0x0036,
+ 0x00e6, 0x0156, 0x2019, 0xb528, 0x231c, 0x83ff, 0x01e8, 0x2071,
+ 0xbb80, 0x2e14, 0xa294, 0x00ff, 0x7004, 0xa084, 0xff00, 0xa205,
+ 0xa306, 0x1190, 0x2011, 0xbb96, 0xad98, 0x000a, 0x20a9, 0x0004,
+ 0x080c, 0x90da, 0x1148, 0x2011, 0xbb9a, 0xad98, 0x0006, 0x20a9,
+ 0x0004, 0x080c, 0x90da, 0x1100, 0x015e, 0x00ee, 0x003e, 0x002e,
+ 0x0005, 0x00e6, 0x2071, 0xbb8c, 0x7004, 0xa086, 0x0014, 0x11a8,
+ 0x7008, 0xa086, 0x0800, 0x1188, 0x700c, 0xd0ec, 0x0160, 0xa084,
+ 0x0f00, 0xa086, 0x0100, 0x1138, 0x7024, 0xd0a4, 0x1110, 0xd0ac,
+ 0x0110, 0xa006, 0x0010, 0xa085, 0x0001, 0x00ee, 0x0005, 0x00e6,
+ 0x00d6, 0x00c6, 0x0076, 0x0056, 0x0046, 0x0026, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2029, 0xb7e9, 0x252c, 0x2021, 0xb7ef, 0x2424,
+ 0x2061, 0xbd00, 0x2071, 0xb500, 0x7248, 0x7068, 0xa202, 0x16f0,
+ 0x080c, 0xb110, 0x05a0, 0x671c, 0xa786, 0x0001, 0x0580, 0xa786,
+ 0x0007, 0x0568, 0x2500, 0xac06, 0x0550, 0x2400, 0xac06, 0x0538,
+ 0x00c6, 0x6000, 0xa086, 0x0004, 0x1110, 0x080c, 0x194d, 0xa786,
+ 0x0008, 0x1148, 0x080c, 0x9e58, 0x1130, 0x00ce, 0x080c, 0x8c19,
+ 0x080c, 0x9e1d, 0x00a0, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0160,
+ 0xa786, 0x0003, 0x11e8, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000,
+ 0x080c, 0x5408, 0x080c, 0x9e11, 0x080c, 0x9e1d, 0x00ce, 0xace0,
+ 0x0018, 0x705c, 0xac02, 0x1210, 0x0804, 0x9084, 0x012e, 0x000e,
+ 0x002e, 0x004e, 0x005e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x0005,
+ 0xa786, 0x0006, 0x1118, 0x080c, 0xb099, 0x0c30, 0xa786, 0x000a,
+ 0x09e0, 0x08c8, 0x220c, 0x2304, 0xa106, 0x1130, 0x8210, 0x8318,
+ 0x1f04, 0x90da, 0xa006, 0x0005, 0x2304, 0xa102, 0x0218, 0x2001,
+ 0x0001, 0x0010, 0x2001, 0x0000, 0xa18d, 0x0001, 0x0005, 0x6004,
+ 0xa08a, 0x0080, 0x1a0c, 0x1515, 0x080c, 0x9e47, 0x0120, 0x080c,
+ 0x9e58, 0x0168, 0x0028, 0x080c, 0x2cc2, 0x080c, 0x9e58, 0x0138,
+ 0x080c, 0x7090, 0x080c, 0x861d, 0x080c, 0x7173, 0x0005, 0x080c,
+ 0x8c19, 0x0cb0, 0xa182, 0x0040, 0x0002, 0x9120, 0x9120, 0x9120,
+ 0x9120, 0x9120, 0x9120, 0x9120, 0x9120, 0x9120, 0x9120, 0x9120,
+ 0x9122, 0x9122, 0x9122, 0x9122, 0x9120, 0x9120, 0x9120, 0x9122,
+ 0x080c, 0x1515, 0x600b, 0xffff, 0x6003, 0x0001, 0x6106, 0x080c,
+ 0x6c8d, 0x0126, 0x2091, 0x8000, 0x080c, 0x7173, 0x012e, 0x0005,
+ 0xa186, 0x0013, 0x1128, 0x6004, 0xa082, 0x0040, 0x0804, 0x91bc,
+ 0xa186, 0x0027, 0x11e8, 0x080c, 0x7090, 0x080c, 0x2c9c, 0x00d6,
+ 0x6110, 0x2168, 0x080c, 0x9c5a, 0x0168, 0x6837, 0x0103, 0x684b,
+ 0x0029, 0x6847, 0x0000, 0x694c, 0xc1c5, 0x694e, 0x080c, 0x5408,
+ 0x080c, 0x9e11, 0x00de, 0x080c, 0x861d, 0x080c, 0x7173, 0x0005,
+ 0xa186, 0x0014, 0x1120, 0x6004, 0xa082, 0x0040, 0x0428, 0xa186,
+ 0x0046, 0x0138, 0xa186, 0x0045, 0x0120, 0xa186, 0x0047, 0x190c,
+ 0x1515, 0x2001, 0x0109, 0x2004, 0xd084, 0x0198, 0x0126, 0x2091,
+ 0x2800, 0x0006, 0x0016, 0x0026, 0x080c, 0x6b74, 0x002e, 0x001e,
+ 0x000e, 0x012e, 0xe000, 0x6000, 0xa086, 0x0002, 0x1110, 0x0804,
+ 0x91fa, 0x080c, 0x8663, 0x0005, 0x0002, 0x919a, 0x9198, 0x9198,
+ 0x9198, 0x9198, 0x9198, 0x9198, 0x9198, 0x9198, 0x9198, 0x9198,
+ 0x91b5, 0x91b5, 0x91b5, 0x91b5, 0x9198, 0x91b5, 0x9198, 0x91b5,
+ 0x080c, 0x1515, 0x080c, 0x7090, 0x00d6, 0x6110, 0x2168, 0x080c,
+ 0x9c5a, 0x0168, 0x6837, 0x0103, 0x684b, 0x0006, 0x6847, 0x0000,
+ 0x6850, 0xc0ec, 0x6852, 0x080c, 0x5408, 0x080c, 0x9e11, 0x00de,
+ 0x080c, 0x861d, 0x080c, 0x7173, 0x0005, 0x080c, 0x7090, 0x080c,
+ 0x861d, 0x080c, 0x7173, 0x0005, 0x0002, 0x91d2, 0x91d0, 0x91d0,
+ 0x91d0, 0x91d0, 0x91d0, 0x91d0, 0x91d0, 0x91d0, 0x91d0, 0x91d0,
+ 0x91e4, 0x91e4, 0x91e4, 0x91e4, 0x91d0, 0x91f3, 0x91d0, 0x91e4,
+ 0x080c, 0x1515, 0x080c, 0x7090, 0x2001, 0xb7b8, 0x2004, 0x603e,
+ 0x6003, 0x0002, 0x080c, 0x7173, 0x6010, 0xa088, 0x0013, 0x2104,
+ 0xa085, 0x0400, 0x200a, 0x0005, 0x080c, 0x7090, 0x2001, 0xb7b6,
+ 0x2004, 0x6016, 0x2001, 0xb7b8, 0x2004, 0x603e, 0x6003, 0x000f,
+ 0x080c, 0x7173, 0x0005, 0x080c, 0x7090, 0x080c, 0x861d, 0x080c,
+ 0x7173, 0x0005, 0xa182, 0x0040, 0x0002, 0x9210, 0x9210, 0x9210,
+ 0x9210, 0x9210, 0x9212, 0x92f7, 0x9326, 0x9210, 0x9210, 0x9210,
+ 0x9210, 0x9210, 0x9210, 0x9210, 0x9210, 0x9210, 0x9210, 0x9210,
+ 0x080c, 0x1515, 0x00e6, 0x00d6, 0x603f, 0x0000, 0x2071, 0xbb80,
+ 0x7124, 0x610a, 0x2071, 0xbb8c, 0x6110, 0x2168, 0x7614, 0xa6b4,
+ 0x0fff, 0x86ff, 0x0904, 0x92c0, 0xa68c, 0x0c00, 0x0518, 0x00f6,
+ 0x2c78, 0x080c, 0x5305, 0x00fe, 0x01c8, 0x684c, 0xd0ac, 0x01b0,
+ 0x6020, 0xd0dc, 0x1198, 0x6850, 0xd0bc, 0x1180, 0x7318, 0x6814,
+ 0xa306, 0x1904, 0x92d3, 0x731c, 0x6810, 0xa31e, 0x0138, 0xd6d4,
+ 0x0904, 0x92d3, 0x6b14, 0xa305, 0x1904, 0x92d3, 0x7318, 0x6b62,
+ 0x731c, 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002, 0x0518, 0xa186,
+ 0x0028, 0x1128, 0x080c, 0x9e36, 0x684b, 0x001c, 0x00e8, 0xd6dc,
+ 0x01a0, 0x684b, 0x0015, 0x684c, 0xd0ac, 0x0170, 0x6914, 0x6a10,
+ 0x2100, 0xa205, 0x0148, 0x7018, 0xa106, 0x1118, 0x701c, 0xa206,
+ 0x0118, 0x6962, 0x6a5e, 0xc6dc, 0x0038, 0xd6d4, 0x0118, 0x684b,
+ 0x0007, 0x0010, 0x684b, 0x0000, 0x6837, 0x0103, 0x6e46, 0xa01e,
+ 0xd6c4, 0x01f0, 0xa686, 0x0100, 0x1140, 0x2001, 0xbb99, 0x2004,
+ 0xa005, 0x1118, 0xc6c4, 0x0804, 0x9221, 0x7328, 0x732c, 0x6b56,
+ 0x83ff, 0x0170, 0xa38a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036,
+ 0x2308, 0x2019, 0xbb98, 0xad90, 0x0019, 0x080c, 0x990d, 0x003e,
+ 0xd6cc, 0x0904, 0x92e6, 0x7124, 0x695a, 0x81ff, 0x0904, 0x92e6,
+ 0xa192, 0x0021, 0x1260, 0x2071, 0xbb98, 0x831c, 0x2300, 0xae18,
+ 0xad90, 0x001d, 0x080c, 0x990d, 0x080c, 0xa137, 0x04b8, 0x6838,
+ 0xd0fc, 0x0120, 0x2009, 0x0020, 0x695a, 0x0c68, 0x00f6, 0x2d78,
+ 0x080c, 0x98b2, 0x00fe, 0x080c, 0xa137, 0x080c, 0x98fd, 0x0440,
+ 0x00f6, 0x2c78, 0x080c, 0x5305, 0x00fe, 0x0190, 0x684c, 0xd0ac,
+ 0x0178, 0x6020, 0xd0dc, 0x1160, 0x6850, 0xd0bc, 0x1148, 0x6810,
+ 0x6914, 0xa105, 0x0128, 0x080c, 0x9f35, 0x00de, 0x00ee, 0x00f0,
+ 0x684b, 0x0000, 0x6837, 0x0103, 0x6e46, 0x684c, 0xd0ac, 0x0130,
+ 0x6810, 0x6914, 0xa115, 0x0110, 0x080c, 0x9483, 0x080c, 0x5408,
+ 0x6218, 0x2268, 0x6a3c, 0x82ff, 0x0110, 0x8211, 0x6a3e, 0x080c,
+ 0x9f03, 0x00de, 0x00ee, 0x1110, 0x080c, 0x861d, 0x0005, 0x00f6,
+ 0x6003, 0x0003, 0x2079, 0xbb8c, 0x7c04, 0x7b00, 0x7e0c, 0x7d08,
+ 0x6010, 0x2078, 0x784c, 0xd0ac, 0x0138, 0x6003, 0x0002, 0x00fe,
+ 0x0005, 0x2130, 0x2228, 0x0058, 0x2400, 0x797c, 0xa10a, 0x2300,
+ 0x7a80, 0xa213, 0x2600, 0xa102, 0x2500, 0xa203, 0x0e90, 0x7c12,
+ 0x7b16, 0x7e0a, 0x7d0e, 0x00fe, 0x603f, 0x0000, 0x2c10, 0x080c,
+ 0x1fa9, 0x080c, 0x6cf0, 0x080c, 0x7230, 0x0005, 0x2001, 0xb7b8,
+ 0x2004, 0x603e, 0x6003, 0x0004, 0x6110, 0x20e1, 0x0005, 0x3d18,
+ 0x3e20, 0x2c10, 0x080c, 0x185e, 0x0005, 0xa182, 0x0040, 0x0002,
+ 0x934b, 0x934b, 0x934b, 0x934b, 0x934b, 0x934d, 0x93e0, 0x934b,
+ 0x934b, 0x93f6, 0x945a, 0x934b, 0x934b, 0x934b, 0x934b, 0x9469,
+ 0x934b, 0x934b, 0x934b, 0x080c, 0x1515, 0x0076, 0x00f6, 0x00e6,
+ 0x00d6, 0x2071, 0xbb8c, 0x6110, 0x2178, 0x7614, 0xa6b4, 0x0fff,
+ 0x7e46, 0x7f4c, 0xc7e5, 0x7f4e, 0x6218, 0x2268, 0x6a3c, 0x82ff,
+ 0x0110, 0x8211, 0x6a3e, 0x86ff, 0x0904, 0x93db, 0xa694, 0xff00,
+ 0xa284, 0x0c00, 0x0120, 0x7018, 0x7862, 0x701c, 0x785e, 0xa284,
+ 0x0300, 0x0904, 0x93db, 0x080c, 0x15f8, 0x090c, 0x1515, 0x2d00,
+ 0x784a, 0x7f4c, 0xc7cd, 0x7f4e, 0x6837, 0x0103, 0x7838, 0x683a,
+ 0x783c, 0x683e, 0x7840, 0x6842, 0x6e46, 0xa68c, 0x0c00, 0x0120,
+ 0x7318, 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002,
+ 0x0180, 0xa186, 0x0028, 0x1118, 0x684b, 0x001c, 0x0060, 0xd6dc,
+ 0x0118, 0x684b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0x684b, 0x0007,
+ 0x0010, 0x684b, 0x0000, 0x6f4e, 0x7850, 0x6852, 0x7854, 0x6856,
+ 0xa01e, 0xd6c4, 0x0198, 0x7328, 0x732c, 0x6b56, 0x83ff, 0x0170,
+ 0xa38a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019,
+ 0xbb98, 0xad90, 0x0019, 0x080c, 0x990d, 0x003e, 0xd6cc, 0x01d8,
+ 0x7124, 0x695a, 0x81ff, 0x01b8, 0xa192, 0x0021, 0x1250, 0x2071,
+ 0xbb98, 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x080c, 0x990d,
+ 0x0050, 0x7838, 0xd0fc, 0x0120, 0x2009, 0x0020, 0x695a, 0x0c78,
+ 0x2d78, 0x080c, 0x98b2, 0x00de, 0x00ee, 0x00fe, 0x007e, 0x0005,
+ 0x00f6, 0x6003, 0x0003, 0x2079, 0xbb8c, 0x7c04, 0x7b00, 0x7e0c,
+ 0x7d08, 0x6010, 0x2078, 0x7c12, 0x7b16, 0x7e0a, 0x7d0e, 0x00fe,
+ 0x2c10, 0x080c, 0x1fa9, 0x080c, 0x7d60, 0x0005, 0x00d6, 0x00f6,
+ 0x2c78, 0x080c, 0x5305, 0x00fe, 0x0120, 0x2001, 0xb7b8, 0x2004,
+ 0x603e, 0x6003, 0x0002, 0x080c, 0x7126, 0x080c, 0x7230, 0x6110,
+ 0x2168, 0x694c, 0xd1e4, 0x0904, 0x9458, 0xd1cc, 0x0540, 0x6948,
+ 0x6838, 0xd0fc, 0x01e8, 0x0016, 0x684c, 0x0006, 0x6850, 0x0006,
+ 0xad90, 0x000d, 0xa198, 0x000d, 0x2009, 0x0020, 0x0156, 0x21a8,
+ 0x2304, 0x2012, 0x8318, 0x8210, 0x1f04, 0x9420, 0x015e, 0x000e,
+ 0x6852, 0x000e, 0x684e, 0x001e, 0x2168, 0x080c, 0x161f, 0x0418,
+ 0x0016, 0x080c, 0x161f, 0x00de, 0x080c, 0x98fd, 0x00e0, 0x6837,
+ 0x0103, 0x6944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x0180, 0xa086,
+ 0x0028, 0x1118, 0x684b, 0x001c, 0x0060, 0xd1dc, 0x0118, 0x684b,
+ 0x0015, 0x0038, 0xd1d4, 0x0118, 0x684b, 0x0007, 0x0010, 0x684b,
+ 0x0000, 0x080c, 0x5408, 0x080c, 0x9f03, 0x1110, 0x080c, 0x861d,
+ 0x00de, 0x0005, 0x2019, 0x0001, 0x080c, 0x7fe4, 0x6003, 0x0002,
+ 0x2001, 0xb7b8, 0x2004, 0x603e, 0x080c, 0x7126, 0x080c, 0x7230,
+ 0x0005, 0x080c, 0x7126, 0x080c, 0x2c9c, 0x00d6, 0x6110, 0x2168,
+ 0x080c, 0x9c5a, 0x0150, 0x6837, 0x0103, 0x684b, 0x0029, 0x6847,
+ 0x0000, 0x080c, 0x5408, 0x080c, 0x9e11, 0x00de, 0x080c, 0x861d,
+ 0x080c, 0x7230, 0x0005, 0x684b, 0x0015, 0xd1fc, 0x0138, 0x684b,
+ 0x0007, 0x8002, 0x8000, 0x810a, 0xa189, 0x0000, 0x6962, 0x685e,
+ 0x0005, 0xa182, 0x0040, 0x0002, 0x94a7, 0x94a7, 0x94a7, 0x94a7,
+ 0x94a7, 0x94a9, 0x94a7, 0x9564, 0x9570, 0x94a7, 0x94a7, 0x94a7,
+ 0x94a7, 0x94a7, 0x94a7, 0x94a7, 0x94a7, 0x94a7, 0x94a7, 0x080c,
+ 0x1515, 0x0076, 0x00f6, 0x00e6, 0x00d6, 0x2071, 0xbb8c, 0x6110,
+ 0x2178, 0x7614, 0xa6b4, 0x0fff, 0x00f6, 0x2c78, 0x080c, 0x5305,
+ 0x00fe, 0x0150, 0xa684, 0x00ff, 0x1138, 0x6020, 0xd0f4, 0x0120,
+ 0x080c, 0x9f35, 0x0804, 0x955f, 0x7e46, 0x7f4c, 0xc7e5, 0x7f4e,
+ 0x6218, 0x2268, 0x6a3c, 0x82ff, 0x0110, 0x8211, 0x6a3e, 0x86ff,
+ 0x0904, 0x9555, 0xa694, 0xff00, 0xa284, 0x0c00, 0x0120, 0x7018,
+ 0x7862, 0x701c, 0x785e, 0xa284, 0x0300, 0x0904, 0x9553, 0xa686,
+ 0x0100, 0x1140, 0x2001, 0xbb99, 0x2004, 0xa005, 0x1118, 0xc6c4,
+ 0x7e46, 0x0c28, 0x080c, 0x15f8, 0x090c, 0x1515, 0x2d00, 0x784a,
+ 0x7f4c, 0xa7bd, 0x0200, 0x7f4e, 0x6837, 0x0103, 0x7838, 0x683a,
+ 0x783c, 0x683e, 0x7840, 0x6842, 0x6e46, 0xa68c, 0x0c00, 0x0120,
+ 0x7318, 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002,
+ 0x0180, 0xa186, 0x0028, 0x1118, 0x684b, 0x001c, 0x0060, 0xd6dc,
+ 0x0118, 0x684b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0x684b, 0x0007,
+ 0x0010, 0x684b, 0x0000, 0x6f4e, 0x7850, 0x6852, 0x7854, 0x6856,
+ 0xa01e, 0xd6c4, 0x0198, 0x7328, 0x732c, 0x6b56, 0x83ff, 0x0170,
+ 0xa38a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019,
+ 0xbb98, 0xad90, 0x0019, 0x080c, 0x990d, 0x003e, 0xd6cc, 0x01d8,
+ 0x7124, 0x695a, 0x81ff, 0x01b8, 0xa192, 0x0021, 0x1250, 0x2071,
+ 0xbb98, 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x080c, 0x990d,
+ 0x0050, 0x7838, 0xd0fc, 0x0120, 0x2009, 0x0020, 0x695a, 0x0c78,
+ 0x2d78, 0x080c, 0x98b2, 0xd6dc, 0x1110, 0xa006, 0x0030, 0x2001,
+ 0x0001, 0x2071, 0xbb8c, 0x7218, 0x731c, 0x080c, 0x18b1, 0x00de,
+ 0x00ee, 0x00fe, 0x007e, 0x0005, 0x2001, 0xb7b8, 0x2004, 0x603e,
+ 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x080c, 0x185e, 0x0005,
+ 0x2001, 0xb7b8, 0x2004, 0x603e, 0x00d6, 0x6003, 0x0002, 0x6110,
+ 0x2168, 0x694c, 0xd1e4, 0x0904, 0x967b, 0x603f, 0x0000, 0x00f6,
+ 0x2c78, 0x080c, 0x5305, 0x00fe, 0x0560, 0x6814, 0x6910, 0xa115,
+ 0x0540, 0x6a60, 0xa206, 0x1118, 0x685c, 0xa106, 0x0510, 0x684c,
+ 0xc0e4, 0x684e, 0x6847, 0x0000, 0x6863, 0x0000, 0x685f, 0x0000,
+ 0x6020, 0xd0f4, 0x1158, 0x697c, 0x6810, 0xa102, 0x603a, 0x6980,
+ 0x6814, 0xa103, 0x6036, 0x6020, 0xc0f5, 0x6022, 0x00d6, 0x6018,
+ 0x2068, 0x683c, 0x8000, 0x683e, 0x00de, 0x080c, 0x9f35, 0x0804,
+ 0x967b, 0x694c, 0xd1cc, 0x0904, 0x964b, 0x6948, 0x6838, 0xd0fc,
+ 0x0904, 0x960e, 0x0016, 0x684c, 0x0006, 0x6850, 0x0006, 0x00f6,
+ 0x2178, 0x7944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x01e0, 0xa086,
+ 0x0028, 0x1128, 0x684b, 0x001c, 0x784b, 0x001c, 0x00e8, 0xd1dc,
+ 0x0158, 0x684b, 0x0015, 0x784b, 0x0015, 0x080c, 0xa0bf, 0x0118,
+ 0x7944, 0xc1dc, 0x7946, 0x0080, 0xd1d4, 0x0128, 0x684b, 0x0007,
+ 0x784b, 0x0007, 0x0048, 0x684c, 0xd0ac, 0x0130, 0x6810, 0x6914,
+ 0xa115, 0x0110, 0x080c, 0x9483, 0x6848, 0x784a, 0x6860, 0x7862,
+ 0x685c, 0x785e, 0xad90, 0x000d, 0xaf98, 0x000d, 0x2009, 0x0020,
+ 0x0156, 0x21a8, 0x2304, 0x2012, 0x8318, 0x8210, 0x1f04, 0x95fa,
+ 0x015e, 0x00fe, 0x000e, 0x6852, 0x000e, 0x684e, 0x080c, 0xa137,
+ 0x001e, 0x2168, 0x080c, 0x161f, 0x0804, 0x9676, 0x0016, 0x00f6,
+ 0x2178, 0x7944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x01e0, 0xa086,
+ 0x0028, 0x1128, 0x684b, 0x001c, 0x784b, 0x001c, 0x00e8, 0xd1dc,
+ 0x0158, 0x684b, 0x0015, 0x784b, 0x0015, 0x080c, 0xa0bf, 0x0118,
+ 0x7944, 0xc1dc, 0x7946, 0x0080, 0xd1d4, 0x0128, 0x684b, 0x0007,
+ 0x784b, 0x0007, 0x0048, 0x684c, 0xd0ac, 0x0130, 0x6810, 0x6914,
+ 0xa115, 0x0110, 0x080c, 0x9483, 0x6860, 0x7862, 0x685c, 0x785e,
+ 0x684c, 0x784e, 0x00fe, 0x080c, 0x161f, 0x00de, 0x080c, 0xa137,
+ 0x080c, 0x98fd, 0x0458, 0x6837, 0x0103, 0x6944, 0xa184, 0x00ff,
+ 0xa0b6, 0x0002, 0x01b0, 0xa086, 0x0028, 0x1118, 0x684b, 0x001c,
+ 0x00d8, 0xd1dc, 0x0148, 0x684b, 0x0015, 0x080c, 0xa0bf, 0x0118,
+ 0x6944, 0xc1dc, 0x6946, 0x0080, 0xd1d4, 0x0118, 0x684b, 0x0007,
+ 0x0058, 0x684b, 0x0000, 0x684c, 0xd0ac, 0x0130, 0x6810, 0x6914,
+ 0xa115, 0x0110, 0x080c, 0x9483, 0x080c, 0x5408, 0x080c, 0x9f03,
+ 0x1110, 0x080c, 0x861d, 0x00de, 0x0005, 0x080c, 0x7090, 0x0010,
+ 0x080c, 0x7126, 0x080c, 0x9c5a, 0x01c0, 0x00d6, 0x6110, 0x2168,
+ 0x6837, 0x0103, 0x2009, 0xb50c, 0x210c, 0xd18c, 0x11c0, 0xd184,
+ 0x1198, 0x6108, 0x694a, 0xa18e, 0x0029, 0x1110, 0x080c, 0xb380,
+ 0x6847, 0x0000, 0x080c, 0x5408, 0x00de, 0x080c, 0x861d, 0x080c,
+ 0x7173, 0x080c, 0x7230, 0x0005, 0x684b, 0x0004, 0x0c88, 0x684b,
+ 0x0004, 0x0c70, 0xa182, 0x0040, 0x0002, 0x96c0, 0x96c0, 0x96c0,
+ 0x96c0, 0x96c0, 0x96c2, 0x96c0, 0x96c5, 0x96c0, 0x96c0, 0x96c0,
+ 0x96c0, 0x96c0, 0x96c0, 0x96c0, 0x96c0, 0x96c0, 0x96c0, 0x96c0,
+ 0x080c, 0x1515, 0x080c, 0x861d, 0x0005, 0x0006, 0x0026, 0xa016,
+ 0x080c, 0x185e, 0x002e, 0x000e, 0x0005, 0xa182, 0x0085, 0x0002,
+ 0x96d9, 0x96d7, 0x96d7, 0x96e5, 0x96d7, 0x96d7, 0x96d7, 0x080c,
+ 0x1515, 0x6003, 0x0001, 0x6106, 0x080c, 0x6c8d, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x7173, 0x012e, 0x0005, 0x0026, 0x0056, 0x00d6,
+ 0x00e6, 0x2071, 0xbb80, 0x7224, 0x6212, 0x7220, 0x080c, 0x9c4a,
+ 0x01a0, 0x2268, 0x6800, 0xa086, 0x0000, 0x0178, 0x6018, 0x6d18,
+ 0xa52e, 0x1158, 0x00c6, 0x2d60, 0x080c, 0x991d, 0x00ce, 0x0128,
+ 0x6803, 0x0002, 0x6007, 0x0086, 0x0010, 0x6007, 0x0087, 0x6003,
+ 0x0001, 0x080c, 0x6c8d, 0x080c, 0x7173, 0x00f6, 0x2278, 0x080c,
+ 0x5305, 0x00fe, 0x0150, 0x6820, 0xd0ec, 0x0138, 0x00c6, 0x2260,
+ 0x603f, 0x0000, 0x080c, 0x9f35, 0x00ce, 0x00ee, 0x00de, 0x005e,
+ 0x002e, 0x0005, 0xa186, 0x0013, 0x1160, 0x6004, 0xa08a, 0x0085,
+ 0x0a0c, 0x1515, 0xa08a, 0x008c, 0x1a0c, 0x1515, 0xa082, 0x0085,
+ 0x0072, 0xa186, 0x0027, 0x0120, 0xa186, 0x0014, 0x190c, 0x1515,
+ 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c, 0x7173, 0x0005, 0x9746,
+ 0x9748, 0x9748, 0x9746, 0x9746, 0x9746, 0x9746, 0x080c, 0x1515,
+ 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c, 0x7173, 0x0005, 0xa186,
+ 0x0013, 0x1128, 0x6004, 0xa082, 0x0085, 0x2008, 0x04a8, 0xa186,
+ 0x0027, 0x11e8, 0x080c, 0x7090, 0x080c, 0x2c9c, 0x00d6, 0x6010,
+ 0x2068, 0x080c, 0x9c5a, 0x0150, 0x6837, 0x0103, 0x6847, 0x0000,
+ 0x684b, 0x0029, 0x080c, 0x5408, 0x080c, 0x9e11, 0x00de, 0x080c,
+ 0x861d, 0x080c, 0x7173, 0x0005, 0x080c, 0x8663, 0x0ce0, 0xa186,
+ 0x0014, 0x1dd0, 0x080c, 0x7090, 0x00d6, 0x6010, 0x2068, 0x080c,
+ 0x9c5a, 0x0d60, 0x6837, 0x0103, 0x6847, 0x0000, 0x684b, 0x0006,
+ 0x6850, 0xc0ec, 0x6852, 0x08f0, 0x0002, 0x9796, 0x9794, 0x9794,
+ 0x9794, 0x9794, 0x9794, 0x97ae, 0x080c, 0x1515, 0x080c, 0x7090,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0118, 0xa186,
+ 0x0035, 0x1118, 0x2001, 0xb7b6, 0x0010, 0x2001, 0xb7b7, 0x2004,
+ 0x6016, 0x6003, 0x000c, 0x080c, 0x7173, 0x0005, 0x080c, 0x7090,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0118, 0xa186,
+ 0x0035, 0x1118, 0x2001, 0xb7b6, 0x0010, 0x2001, 0xb7b7, 0x2004,
+ 0x6016, 0x6003, 0x000e, 0x080c, 0x7173, 0x0005, 0xa182, 0x008c,
+ 0x1220, 0xa182, 0x0085, 0x0208, 0x001a, 0x080c, 0x8663, 0x0005,
+ 0x97d7, 0x97d7, 0x97d7, 0x97d7, 0x97d9, 0x9832, 0x97d7, 0x080c,
+ 0x1515, 0x00d6, 0x00f6, 0x2c78, 0x080c, 0x5305, 0x00fe, 0x0168,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0118, 0xa186,
+ 0x0035, 0x1118, 0x00de, 0x0804, 0x9845, 0x080c, 0x9c5a, 0x1118,
+ 0x080c, 0x9e11, 0x00f0, 0x6010, 0x2068, 0x684c, 0xd0e4, 0x1110,
+ 0x080c, 0x9e11, 0x6837, 0x0103, 0x6850, 0xd0b4, 0x0128, 0x684b,
+ 0x0006, 0xc0ec, 0x6852, 0x0048, 0xd0bc, 0x0118, 0x684b, 0x0002,
+ 0x0020, 0x684b, 0x0005, 0x080c, 0x9ed2, 0x6847, 0x0000, 0x080c,
+ 0x5408, 0x2c68, 0x080c, 0x85c7, 0x01c0, 0x6003, 0x0001, 0x6007,
+ 0x001e, 0x600b, 0xffff, 0x2009, 0xbb8e, 0x210c, 0x6136, 0x2009,
+ 0xbb8f, 0x210c, 0x613a, 0x6918, 0x611a, 0x080c, 0xa027, 0x6950,
+ 0x6152, 0x601f, 0x0001, 0x080c, 0x6c8d, 0x2d60, 0x080c, 0x861d,
+ 0x00de, 0x0005, 0x00f6, 0x2c78, 0x080c, 0x5305, 0x00fe, 0x0598,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0035, 0x0130, 0xa186,
+ 0x001e, 0x0118, 0xa186, 0x0039, 0x1530, 0x00d6, 0x2c68, 0x080c,
+ 0xa10a, 0x1904, 0x988a, 0x080c, 0x85c7, 0x01d8, 0x6106, 0x6003,
+ 0x0001, 0x601f, 0x0001, 0x6918, 0x611a, 0x6928, 0x612a, 0x692c,
+ 0x612e, 0x6930, 0xa18c, 0x00ff, 0x6132, 0x6934, 0x6136, 0x6938,
+ 0x613a, 0x6950, 0x6152, 0x080c, 0xa027, 0x080c, 0x6c8d, 0x080c,
+ 0x7173, 0x2d60, 0x00f8, 0x00d6, 0x6010, 0x2068, 0x080c, 0x9c5a,
+ 0x01c8, 0x6837, 0x0103, 0x6850, 0xd0b4, 0x0128, 0xc0ec, 0x6852,
+ 0x684b, 0x0006, 0x0048, 0xd0bc, 0x0118, 0x684b, 0x0002, 0x0020,
+ 0x684b, 0x0005, 0x080c, 0x9ed2, 0x6847, 0x0000, 0x080c, 0x5408,
+ 0x080c, 0x9e11, 0x00de, 0x080c, 0x861d, 0x0005, 0x0016, 0x00d6,
+ 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0140, 0x6837, 0x0103, 0x684b,
+ 0x0028, 0x6847, 0x0000, 0x080c, 0x5408, 0x00de, 0x001e, 0xa186,
+ 0x0013, 0x0148, 0xa186, 0x0014, 0x0130, 0xa186, 0x0027, 0x0118,
+ 0x080c, 0x8663, 0x0030, 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c,
+ 0x7173, 0x0005, 0x0056, 0x0066, 0x00d6, 0x00f6, 0x2029, 0x0001,
+ 0xa182, 0x0101, 0x1208, 0x0010, 0x2009, 0x0100, 0x2130, 0x2069,
+ 0xbb98, 0x831c, 0x2300, 0xad18, 0x2009, 0x0020, 0xaf90, 0x001d,
+ 0x080c, 0x990d, 0xa6b2, 0x0020, 0x7804, 0xa06d, 0x0110, 0x080c,
+ 0x161f, 0x080c, 0x15f8, 0x0500, 0x8528, 0x6837, 0x0110, 0x683b,
+ 0x0000, 0x2d20, 0x7c06, 0xa68a, 0x003d, 0x1228, 0x2608, 0xad90,
+ 0x000f, 0x0459, 0x0088, 0xa6b2, 0x003c, 0x2009, 0x003c, 0x2d78,
+ 0xad90, 0x000f, 0x0411, 0x0c28, 0x00fe, 0x852f, 0xa5ad, 0x0003,
+ 0x7d36, 0xa5ac, 0x0000, 0x0028, 0x00fe, 0x852f, 0xa5ad, 0x0003,
+ 0x7d36, 0x00de, 0x006e, 0x005e, 0x0005, 0x00f6, 0x8dff, 0x0158,
+ 0x6804, 0xa07d, 0x0130, 0x6807, 0x0000, 0x080c, 0x5408, 0x2f68,
+ 0x0cb8, 0x080c, 0x5408, 0x00fe, 0x0005, 0x0156, 0xa184, 0x0001,
+ 0x0108, 0x8108, 0x810c, 0x21a8, 0x2304, 0x8007, 0x2012, 0x8318,
+ 0x8210, 0x1f04, 0x9914, 0x015e, 0x0005, 0x0066, 0x0126, 0x2091,
+ 0x8000, 0x2031, 0x0001, 0x601c, 0xa084, 0x000f, 0x0083, 0x012e,
+ 0x006e, 0x0005, 0x0126, 0x2091, 0x8000, 0x0066, 0x2031, 0x0000,
+ 0x601c, 0xa084, 0x000f, 0x001b, 0x006e, 0x012e, 0x0005, 0x9954,
+ 0x9954, 0x994f, 0x9976, 0x9942, 0x994f, 0x9976, 0x994f, 0x994f,
+ 0x9942, 0x994f, 0x080c, 0x1515, 0x0036, 0x2019, 0x0010, 0x080c,
+ 0xace0, 0x601f, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005, 0xa006,
+ 0x0005, 0xa085, 0x0001, 0x0005, 0x00d6, 0x86ff, 0x11d8, 0x6010,
+ 0x2068, 0x080c, 0x9c5a, 0x01c0, 0x6834, 0xa086, 0x0139, 0x1128,
+ 0x684b, 0x0005, 0x6853, 0x0000, 0x0028, 0xa00e, 0x2001, 0x0005,
+ 0x080c, 0x54db, 0x080c, 0x9ed2, 0x080c, 0x5408, 0x080c, 0x861d,
+ 0xa085, 0x0001, 0x00de, 0x0005, 0xa006, 0x0ce0, 0x6000, 0xa08a,
+ 0x0010, 0x1a0c, 0x1515, 0x000b, 0x0005, 0x998d, 0x99ae, 0x998f,
+ 0x99cd, 0x99ab, 0x998d, 0x994f, 0x9954, 0x9954, 0x994f, 0x994f,
+ 0x994f, 0x994f, 0x994f, 0x994f, 0x994f, 0x080c, 0x1515, 0x86ff,
+ 0x11b8, 0x601c, 0xa086, 0x0006, 0x0198, 0x00d6, 0x6010, 0x2068,
+ 0x080c, 0x9c5a, 0x0110, 0x080c, 0x9ed2, 0x00de, 0x6007, 0x0085,
+ 0x6003, 0x000b, 0x601f, 0x0002, 0x080c, 0x6c8d, 0x080c, 0x7173,
+ 0xa085, 0x0001, 0x0005, 0x080c, 0x194d, 0x0c08, 0x00e6, 0x2071,
+ 0xb7e0, 0x7024, 0xac06, 0x1110, 0x080c, 0x7f59, 0x601c, 0xa084,
+ 0x000f, 0xa086, 0x0006, 0x1150, 0x0086, 0x0096, 0x2049, 0x0001,
+ 0x2c40, 0x080c, 0x8130, 0x009e, 0x008e, 0x0010, 0x080c, 0x7e58,
+ 0x00ee, 0x1928, 0x080c, 0x994f, 0x0005, 0x0036, 0x00e6, 0x2071,
+ 0xb7e0, 0x703c, 0xac06, 0x1140, 0x2019, 0x0000, 0x080c, 0x7fe4,
+ 0x00ee, 0x003e, 0x0804, 0x998f, 0x080c, 0x825d, 0x00ee, 0x003e,
+ 0x1904, 0x998f, 0x080c, 0x994f, 0x0005, 0x00c6, 0x601c, 0xa084,
+ 0x000f, 0x0013, 0x00ce, 0x0005, 0x99fe, 0x9a6b, 0x9bb9, 0x9a09,
+ 0x9e1d, 0x99fe, 0xacd2, 0xa14e, 0x9a6b, 0x99f7, 0x9c24, 0x080c,
+ 0x1515, 0x080c, 0x9e58, 0x1110, 0x080c, 0x8c19, 0x0005, 0x080c,
+ 0x7090, 0x080c, 0x7173, 0x080c, 0x861d, 0x0005, 0x6017, 0x0001,
+ 0x0005, 0x080c, 0x9c5a, 0x0120, 0x6010, 0xa080, 0x0019, 0x2c02,
+ 0x6000, 0xa08a, 0x0010, 0x1a0c, 0x1515, 0x000b, 0x0005, 0x9a27,
+ 0x9a29, 0x9a49, 0x9a5b, 0x9a68, 0x9a27, 0x99fe, 0x99fe, 0x99fe,
+ 0x9a5b, 0x9a5b, 0x9a27, 0x9a27, 0x9a27, 0x9a27, 0x9a65, 0x080c,
+ 0x1515, 0x00e6, 0x6010, 0x2070, 0x7050, 0xc0b5, 0x7052, 0x2071,
+ 0xb7e0, 0x7024, 0xac06, 0x0190, 0x080c, 0x7e58, 0x6007, 0x0085,
+ 0x6003, 0x000b, 0x601f, 0x0002, 0x2001, 0xb7b7, 0x2004, 0x6016,
+ 0x080c, 0x6c8d, 0x080c, 0x7173, 0x00ee, 0x0005, 0x6017, 0x0001,
+ 0x0cd8, 0x00d6, 0x6010, 0x2068, 0x6850, 0xc0b5, 0x6852, 0x00de,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x080c, 0x6c8d,
+ 0x080c, 0x7173, 0x0005, 0x00d6, 0x6017, 0x0001, 0x6010, 0x2068,
+ 0x6850, 0xc0b5, 0x6852, 0x00de, 0x0005, 0x080c, 0x861d, 0x0005,
+ 0x080c, 0x194d, 0x08f0, 0x6000, 0xa08a, 0x0010, 0x1a0c, 0x1515,
+ 0x000b, 0x0005, 0x9a82, 0x9a06, 0x9a84, 0x9a82, 0x9a84, 0x9a84,
+ 0x99ff, 0x9a82, 0x99f9, 0x99f9, 0x9a82, 0x9a82, 0x9a82, 0x9a82,
+ 0x9a82, 0x9a82, 0x080c, 0x1515, 0x00d6, 0x6018, 0x2068, 0x6804,
+ 0xa084, 0x00ff, 0x00de, 0xa08a, 0x000c, 0x1a0c, 0x1515, 0x000b,
+ 0x0005, 0x9a9d, 0x9b5f, 0x9a9f, 0x9add, 0x9a9f, 0x9add, 0x9a9f,
+ 0x9aad, 0x9a9d, 0x9add, 0x9a9d, 0x9ac9, 0x080c, 0x1515, 0x6004,
+ 0xa08e, 0x0016, 0x05a8, 0xa08e, 0x0004, 0x0590, 0xa08e, 0x0002,
+ 0x0578, 0xa08e, 0x004b, 0x0904, 0x9b5b, 0x6004, 0x080c, 0x9e58,
+ 0x0904, 0x9b78, 0xa08e, 0x0021, 0x0904, 0x9b7c, 0xa08e, 0x0022,
+ 0x0904, 0x9b78, 0xa08e, 0x003d, 0x0904, 0x9b7c, 0xa08e, 0x0039,
+ 0x0904, 0x9b80, 0xa08e, 0x0035, 0x0904, 0x9b80, 0xa08e, 0x001e,
+ 0x0188, 0xa08e, 0x0001, 0x1150, 0x00d6, 0x6018, 0x2068, 0x6804,
+ 0xa084, 0x00ff, 0x00de, 0xa086, 0x0006, 0x0110, 0x080c, 0x2c9c,
+ 0x080c, 0x8c19, 0x080c, 0x9e1d, 0x0005, 0x00c6, 0x00d6, 0x6104,
+ 0xa186, 0x0016, 0x0904, 0x9b4c, 0xa186, 0x0002, 0x15d8, 0x2001,
+ 0xb535, 0x2004, 0xd08c, 0x1198, 0x080c, 0x5acf, 0x1180, 0x2001,
+ 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500, 0x2003, 0x0001, 0xa085,
+ 0x0001, 0x080c, 0x5b13, 0x080c, 0x5a07, 0x0804, 0x9ba2, 0x6018,
+ 0x2068, 0x2001, 0xb535, 0x2004, 0xd0ac, 0x1904, 0x9ba2, 0x68a0,
+ 0xd0bc, 0x1904, 0x9ba2, 0x6840, 0xa084, 0x00ff, 0xa005, 0x0190,
+ 0x8001, 0x6842, 0x6013, 0x0000, 0x601f, 0x0007, 0x6017, 0x0398,
+ 0x603f, 0x0000, 0x080c, 0x85c7, 0x0128, 0x2d00, 0x601a, 0x601f,
+ 0x0001, 0x0450, 0x00de, 0x00ce, 0x6004, 0xa08e, 0x0002, 0x11a8,
+ 0x6018, 0xa080, 0x0028, 0x2004, 0xa086, 0x007e, 0x1170, 0x2009,
+ 0xb535, 0x2104, 0xc085, 0x200a, 0x00e6, 0x2071, 0xb500, 0x080c,
+ 0x4bc6, 0x00ee, 0x080c, 0x8c19, 0x0020, 0x080c, 0x8c19, 0x080c,
+ 0x2c9c, 0x00e6, 0x0126, 0x2091, 0x8000, 0x080c, 0x2cc2, 0x012e,
+ 0x00ee, 0x080c, 0x9e1d, 0x0005, 0x2001, 0x0002, 0x080c, 0x4efd,
+ 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x6cd3, 0x080c, 0x7173,
+ 0x00de, 0x00ce, 0x0c80, 0x080c, 0x2cc2, 0x0804, 0x9ad8, 0x00c6,
+ 0x00d6, 0x6104, 0xa186, 0x0016, 0x0d38, 0x6018, 0x2068, 0x6840,
+ 0xa084, 0x00ff, 0xa005, 0x0904, 0x9b22, 0x8001, 0x6842, 0x6003,
+ 0x0001, 0x080c, 0x6cd3, 0x080c, 0x7173, 0x00de, 0x00ce, 0x0898,
+ 0x080c, 0x8c19, 0x0804, 0x9ada, 0x080c, 0x8c47, 0x0804, 0x9ada,
+ 0x00d6, 0x2c68, 0x6104, 0x080c, 0xa10a, 0x00de, 0x0118, 0x080c,
+ 0x861d, 0x00b8, 0x6004, 0x8007, 0x6130, 0xa18c, 0x00ff, 0xa105,
+ 0x6032, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x6038,
+ 0x600a, 0x2001, 0xb7b7, 0x2004, 0x6016, 0x080c, 0x6c8d, 0x080c,
+ 0x7173, 0x0005, 0x00de, 0x00ce, 0x080c, 0x8c19, 0x080c, 0x2c9c,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x080c, 0x2cc2, 0x6013, 0x0000,
+ 0x601f, 0x0007, 0x6017, 0x0398, 0x603f, 0x0000, 0x012e, 0x00ee,
+ 0x0005, 0x6000, 0xa08a, 0x0010, 0x1a0c, 0x1515, 0x000b, 0x0005,
+ 0x9bd0, 0x9bd0, 0x9bd0, 0x9bd0, 0x9bd0, 0x9bd0, 0x9bd0, 0x9bd0,
+ 0x9bd0, 0x99fe, 0x9bd0, 0x9a06, 0x9bd2, 0x9a06, 0x9bdf, 0x9bd0,
+ 0x080c, 0x1515, 0x6004, 0xa086, 0x008b, 0x0148, 0x6007, 0x008b,
+ 0x6003, 0x000d, 0x080c, 0x6c8d, 0x080c, 0x7173, 0x0005, 0x080c,
+ 0x9e11, 0x080c, 0x9c5a, 0x0580, 0x080c, 0x2c9c, 0x00d6, 0x080c,
+ 0x9c5a, 0x0168, 0x6010, 0x2068, 0x6837, 0x0103, 0x684b, 0x0006,
+ 0x6847, 0x0000, 0x6850, 0xc0ed, 0x6852, 0x080c, 0x5408, 0x2c68,
+ 0x080c, 0x85c7, 0x0150, 0x6818, 0x601a, 0x080c, 0xa027, 0x00c6,
+ 0x2d60, 0x080c, 0x9e1d, 0x00ce, 0x0008, 0x2d60, 0x00de, 0x6013,
+ 0x0000, 0x601f, 0x0001, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c,
+ 0x6cd3, 0x080c, 0x7173, 0x0078, 0x6030, 0xa08c, 0xff00, 0x810f,
+ 0xa186, 0x0039, 0x0118, 0xa186, 0x0035, 0x1118, 0x080c, 0x2c9c,
+ 0x08b0, 0x080c, 0x9e1d, 0x0005, 0x6000, 0xa08a, 0x0010, 0x1a0c,
+ 0x1515, 0x000b, 0x0005, 0x9c3b, 0x9c3b, 0x9c3b, 0x9c3d, 0x9c3d,
+ 0x9c3b, 0x9c3b, 0x9c3b, 0x9c3b, 0x9c3b, 0x9c3b, 0x9c3b, 0x9c3b,
+ 0x9c3b, 0x9c3b, 0x9c3b, 0x080c, 0x1515, 0x080c, 0x825d, 0x190c,
+ 0x1515, 0x6110, 0x2168, 0x684b, 0x0006, 0x080c, 0x5408, 0x080c,
+ 0x861d, 0x0005, 0xa284, 0x0007, 0x1158, 0xa282, 0xbd00, 0x0240,
+ 0x2001, 0xb517, 0x2004, 0xa202, 0x1218, 0xa085, 0x0001, 0x0005,
+ 0xa006, 0x0ce8, 0x0026, 0x6210, 0xa294, 0xf000, 0x002e, 0x0005,
+ 0x00e6, 0x00c6, 0x0036, 0x0006, 0x0126, 0x2091, 0x8000, 0x2061,
+ 0xbd00, 0x2071, 0xb500, 0x7348, 0x7068, 0xa302, 0x12a8, 0x601c,
+ 0xa206, 0x1160, 0x080c, 0x9fb2, 0x0148, 0x080c, 0x9e58, 0x1110,
+ 0x080c, 0x8c19, 0x00c6, 0x080c, 0x861d, 0x00ce, 0xace0, 0x0018,
+ 0x705c, 0xac02, 0x1208, 0x0c38, 0x012e, 0x000e, 0x003e, 0x00ce,
+ 0x00ee, 0x0005, 0x00e6, 0x00c6, 0x0016, 0xa188, 0xb635, 0x210c,
+ 0x81ff, 0x0128, 0x2061, 0xb8f4, 0x611a, 0x080c, 0x2c9c, 0xa006,
+ 0x0010, 0xa085, 0x0001, 0x001e, 0x00ce, 0x00ee, 0x0005, 0x00c6,
+ 0x0056, 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c, 0x85c7, 0x005e,
+ 0x0180, 0x6612, 0x651a, 0x080c, 0xa027, 0x601f, 0x0003, 0x2009,
+ 0x004b, 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x005e, 0x00ce,
+ 0x0005, 0xa006, 0x0cd0, 0x00c6, 0x0056, 0x0126, 0x2091, 0x8000,
+ 0x62a0, 0x00c6, 0x080c, 0x9ed6, 0x005e, 0x0550, 0x6013, 0x0000,
+ 0x651a, 0x080c, 0xa027, 0x601f, 0x0003, 0x0016, 0x00c6, 0x2560,
+ 0x080c, 0x51aa, 0x00ce, 0x080c, 0x6df5, 0x0076, 0x2039, 0x0000,
+ 0x080c, 0x6d02, 0x2c08, 0x080c, 0xae82, 0x007e, 0x001e, 0xd184,
+ 0x0128, 0x080c, 0x861d, 0xa085, 0x0001, 0x0030, 0x2009, 0x004c,
+ 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x005e, 0x00ce, 0x0005,
+ 0xa006, 0x0cd0, 0x00f6, 0x00c6, 0x0046, 0x00c6, 0x080c, 0x85c7,
+ 0x2c78, 0x00ce, 0x0180, 0x7e12, 0x2c00, 0x781a, 0x781f, 0x0003,
+ 0x2021, 0x0005, 0x080c, 0x9d50, 0x2f60, 0x2009, 0x004d, 0x080c,
+ 0x864c, 0xa085, 0x0001, 0x004e, 0x00ce, 0x00fe, 0x0005, 0x00f6,
+ 0x00c6, 0x0046, 0x00c6, 0x080c, 0x85c7, 0x2c78, 0x00ce, 0x0178,
+ 0x7e12, 0x2c00, 0x781a, 0x781f, 0x0003, 0x2021, 0x0005, 0x0481,
+ 0x2f60, 0x2009, 0x004e, 0x080c, 0x864c, 0xa085, 0x0001, 0x004e,
+ 0x00ce, 0x00fe, 0x0005, 0x00f6, 0x00c6, 0x0046, 0x00c6, 0x080c,
+ 0x85c7, 0x2c78, 0x00ce, 0x01c0, 0x7e12, 0x2c00, 0x781a, 0x781f,
+ 0x0003, 0x2021, 0x0004, 0x00a1, 0x2001, 0xb7a0, 0x2004, 0xd0fc,
+ 0x0120, 0x2f60, 0x080c, 0x861d, 0x0028, 0x2f60, 0x2009, 0x0052,
+ 0x080c, 0x864c, 0xa085, 0x0001, 0x004e, 0x00ce, 0x00fe, 0x0005,
+ 0x0096, 0x0076, 0x0126, 0x2091, 0x8000, 0x080c, 0x514c, 0x0118,
+ 0x2001, 0x9d55, 0x0028, 0x080c, 0x511c, 0x0158, 0x2001, 0x9d5b,
+ 0x0006, 0xa00e, 0x2400, 0x080c, 0x54db, 0x080c, 0x5408, 0x000e,
+ 0x0807, 0x2418, 0x080c, 0x702f, 0x62a0, 0x0086, 0x2041, 0x0001,
+ 0x2039, 0x0001, 0x2608, 0x080c, 0x6e0e, 0x008e, 0x080c, 0x6d02,
+ 0x2f08, 0x2648, 0x080c, 0xae82, 0x613c, 0x81ff, 0x090c, 0x6ec3,
+ 0x080c, 0x7173, 0x012e, 0x007e, 0x009e, 0x0005, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x00c6, 0x080c, 0x85c7, 0x001e, 0x0188, 0x660a,
+ 0x611a, 0x080c, 0xa027, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009,
+ 0x001f, 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0xa006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c,
+ 0x85c7, 0x001e, 0x0188, 0x660a, 0x611a, 0x080c, 0xa027, 0x601f,
+ 0x0008, 0x2d00, 0x6012, 0x2009, 0x0021, 0x080c, 0x864c, 0xa085,
+ 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006, 0x0cd8, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x00c6, 0x080c, 0x85c7, 0x001e, 0x0188, 0x660a,
+ 0x611a, 0x080c, 0xa027, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009,
+ 0x003d, 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0xa006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c,
+ 0x9ed6, 0x001e, 0x0180, 0x611a, 0x080c, 0xa027, 0x601f, 0x0001,
+ 0x2d00, 0x6012, 0x2009, 0x0000, 0x080c, 0x864c, 0xa085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0xa006, 0x0cd8, 0x00c6, 0x0126, 0x2091,
+ 0x8000, 0x00c6, 0x080c, 0x85c7, 0x001e, 0x0188, 0x660a, 0x611a,
+ 0x080c, 0xa027, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x0044,
+ 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006,
+ 0x0cd8, 0x0026, 0x00d6, 0x6218, 0x2268, 0x6a3c, 0x82ff, 0x0110,
+ 0x8211, 0x6a3e, 0x00de, 0x002e, 0x0005, 0x0006, 0x6000, 0xa086,
+ 0x0000, 0x0190, 0x6013, 0x0000, 0x601f, 0x0007, 0x2001, 0xb7b6,
+ 0x2004, 0x0006, 0xa082, 0x0051, 0x000e, 0x0208, 0x8004, 0x6016,
+ 0x080c, 0xb33a, 0x603f, 0x0000, 0x000e, 0x0005, 0x0066, 0x00c6,
+ 0x00d6, 0x2031, 0xb553, 0x2634, 0xd6e4, 0x0128, 0x6618, 0x2660,
+ 0x6e48, 0x080c, 0x50d5, 0x00de, 0x00ce, 0x006e, 0x0005, 0x0006,
+ 0x0016, 0x6004, 0xa08e, 0x0002, 0x0140, 0xa08e, 0x0003, 0x0128,
+ 0xa08e, 0x0004, 0x0110, 0xa085, 0x0001, 0x001e, 0x000e, 0x0005,
+ 0x0006, 0x00d6, 0x6010, 0xa06d, 0x0148, 0x6834, 0xa086, 0x0139,
+ 0x0138, 0x6838, 0xd0fc, 0x0110, 0xa006, 0x0010, 0xa085, 0x0001,
+ 0x00de, 0x000e, 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000, 0x00c6,
+ 0x080c, 0x85c7, 0x001e, 0x0190, 0x611a, 0x080c, 0xa027, 0x601f,
+ 0x0001, 0x2d00, 0x6012, 0x080c, 0x2c9c, 0x2009, 0x0028, 0x080c,
+ 0x864c, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006, 0x0cd8,
+ 0xa186, 0x0015, 0x1178, 0x2011, 0xb521, 0x2204, 0xa086, 0x0074,
+ 0x1148, 0x080c, 0x8f98, 0x6003, 0x0001, 0x6007, 0x0029, 0x080c,
+ 0x6cd3, 0x0020, 0x080c, 0x8c19, 0x080c, 0x861d, 0x0005, 0xa186,
+ 0x0016, 0x1128, 0x2001, 0x0004, 0x080c, 0x4efd, 0x00e8, 0xa186,
+ 0x0015, 0x11e8, 0x2011, 0xb521, 0x2204, 0xa086, 0x0014, 0x11b8,
+ 0x00d6, 0x6018, 0x2068, 0x080c, 0x504b, 0x00de, 0x080c, 0x9051,
+ 0x1170, 0x00d6, 0x6018, 0x2068, 0x6890, 0x00de, 0xa005, 0x0138,
+ 0x2001, 0x0006, 0x080c, 0x4efd, 0x080c, 0x87a0, 0x0020, 0x080c,
+ 0x8c19, 0x080c, 0x861d, 0x0005, 0x6848, 0xa086, 0x0005, 0x1108,
+ 0x0009, 0x0005, 0x6850, 0xc0ad, 0x6852, 0x0005, 0x00e6, 0x0126,
+ 0x2071, 0xb500, 0x2091, 0x8000, 0x7548, 0xa582, 0x0001, 0x0608,
+ 0x704c, 0x2060, 0x6000, 0xa086, 0x0000, 0x0148, 0xace0, 0x0018,
+ 0x705c, 0xac02, 0x1208, 0x0cb0, 0x2061, 0xbd00, 0x0c98, 0x6003,
+ 0x0008, 0x8529, 0x754a, 0xaca8, 0x0018, 0x705c, 0xa502, 0x1230,
+ 0x754e, 0xa085, 0x0001, 0x012e, 0x00ee, 0x0005, 0x704f, 0xbd00,
+ 0x0cc0, 0xa006, 0x0cc0, 0x00e6, 0x2071, 0xbb8c, 0x7014, 0xd0e4,
+ 0x0150, 0x6013, 0x0000, 0x6003, 0x0001, 0x6007, 0x0050, 0x080c,
+ 0x6c8d, 0x080c, 0x7173, 0x00ee, 0x0005, 0x00c6, 0x00f6, 0x2c78,
+ 0x080c, 0x5305, 0x00fe, 0x0120, 0x601c, 0xa084, 0x000f, 0x0013,
+ 0x00ce, 0x0005, 0x99fe, 0x9f2d, 0x9f30, 0x9f33, 0xb127, 0xb142,
+ 0xb145, 0x99fe, 0x99fe, 0x080c, 0x1515, 0xe000, 0xe000, 0x0005,
+ 0xe000, 0xe000, 0x0005, 0x0009, 0x0005, 0x00f6, 0x2c78, 0x080c,
+ 0x5305, 0x0538, 0x080c, 0x85c7, 0x1128, 0x2001, 0xb7b8, 0x2004,
+ 0x783e, 0x00f8, 0x7818, 0x601a, 0x080c, 0xa027, 0x781c, 0xa086,
+ 0x0003, 0x0128, 0x7808, 0x6036, 0x2f00, 0x603a, 0x0020, 0x7808,
+ 0x603a, 0x2f00, 0x6036, 0x602a, 0x601f, 0x0001, 0x6007, 0x0035,
+ 0x6003, 0x0001, 0x7950, 0x6152, 0x080c, 0x6c8d, 0x080c, 0x7173,
+ 0x2f60, 0x00fe, 0x0005, 0x0016, 0x00f6, 0x682c, 0x6032, 0xa08e,
+ 0x0001, 0x0138, 0xa086, 0x0005, 0x0140, 0xa006, 0x602a, 0x602e,
+ 0x00a0, 0x6820, 0xc0f4, 0xc0d5, 0x6822, 0x6810, 0x2078, 0x787c,
+ 0x6938, 0xa102, 0x7880, 0x6934, 0xa103, 0x1e78, 0x6834, 0x602a,
+ 0x6838, 0xa084, 0xfffc, 0x683a, 0x602e, 0x2d00, 0x6036, 0x6808,
+ 0x603a, 0x6918, 0x611a, 0x6950, 0x6152, 0x601f, 0x0001, 0x6007,
+ 0x0039, 0x6003, 0x0001, 0x080c, 0x6c8d, 0x6803, 0x0002, 0x00fe,
+ 0x001e, 0x0005, 0x00f6, 0x2c78, 0x080c, 0x5305, 0x1118, 0xa085,
+ 0x0001, 0x0070, 0x6020, 0xd0f4, 0x1150, 0xc0f5, 0x6022, 0x6010,
+ 0x2078, 0x7828, 0x603a, 0x782c, 0x6036, 0x080c, 0x194d, 0xa006,
+ 0x00fe, 0x0005, 0x0006, 0x0016, 0x6004, 0xa08e, 0x0034, 0x01b8,
+ 0xa08e, 0x0035, 0x01a0, 0xa08e, 0x0036, 0x0188, 0xa08e, 0x0037,
+ 0x0170, 0xa08e, 0x0038, 0x0158, 0xa08e, 0x0039, 0x0140, 0xa08e,
+ 0x003a, 0x0128, 0xa08e, 0x003b, 0x0110, 0xa085, 0x0001, 0x001e,
+ 0x000e, 0x0005, 0x0006, 0x0016, 0x0026, 0x0036, 0x00e6, 0x2001,
+ 0xb7b2, 0x200c, 0x8000, 0x2014, 0x2001, 0x0032, 0x080c, 0x6b40,
+ 0x2001, 0xb7b6, 0x82ff, 0x1110, 0x2011, 0x0014, 0x2202, 0x2001,
+ 0xb7b4, 0x200c, 0x8000, 0x2014, 0x2071, 0xb78e, 0x711a, 0x721e,
+ 0x2001, 0x0064, 0x080c, 0x6b40, 0x2001, 0xb7b7, 0x82ff, 0x1110,
+ 0x2011, 0x0014, 0x2202, 0x2009, 0xb7b8, 0xa280, 0x000a, 0x200a,
+ 0x080c, 0x532a, 0x00ee, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005,
+ 0x0006, 0x00e6, 0x2001, 0xb7b6, 0x2003, 0x0028, 0x2001, 0xb7b7,
+ 0x2003, 0x0014, 0x2071, 0xb78e, 0x701b, 0x0000, 0x701f, 0x07d0,
+ 0x2001, 0xb7b8, 0x2003, 0x001e, 0x00ee, 0x000e, 0x0005, 0x00d6,
+ 0x6054, 0xa06d, 0x0110, 0x080c, 0x160f, 0x00de, 0x0005, 0x0005,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c, 0x85c7, 0x001e,
+ 0x0178, 0x611a, 0x0ca1, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009,
+ 0x0033, 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0xa006, 0x0cd8, 0x00d6, 0x00e6, 0x00f6, 0x2071, 0xb500, 0xa186,
+ 0x0015, 0x1500, 0x7084, 0xa086, 0x0018, 0x11e0, 0x6010, 0x2068,
+ 0x6a3c, 0xd2e4, 0x1160, 0x2c78, 0x080c, 0x7331, 0x01d8, 0x7070,
+ 0x6a50, 0xa206, 0x1160, 0x7074, 0x6a54, 0xa206, 0x1140, 0x6218,
+ 0xa290, 0x0028, 0x2214, 0x2009, 0x0000, 0x080c, 0x2ce1, 0x080c,
+ 0x87a0, 0x0020, 0x080c, 0x8c19, 0x080c, 0x861d, 0x00fe, 0x00ee,
+ 0x00de, 0x0005, 0x7054, 0x6a54, 0xa206, 0x0d48, 0x0c80, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c, 0x85c7, 0x001e, 0x0180,
+ 0x611a, 0x080c, 0xa027, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009,
+ 0x0043, 0x080c, 0x864c, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0xa006, 0x0cd8, 0x00d6, 0x00e6, 0x00f6, 0x2071, 0xb500, 0xa186,
+ 0x0015, 0x11c0, 0x7084, 0xa086, 0x0004, 0x11a0, 0x6010, 0xa0e8,
+ 0x000f, 0x2c78, 0x080c, 0x7331, 0x01a8, 0x7070, 0x6a08, 0xa206,
+ 0x1130, 0x7074, 0x6a0c, 0xa206, 0x1110, 0x080c, 0x2c9c, 0x080c,
+ 0x87a0, 0x0020, 0x080c, 0x8c19, 0x080c, 0x861d, 0x00fe, 0x00ee,
+ 0x00de, 0x0005, 0x7054, 0x6a0c, 0xa206, 0x0d78, 0x0c80, 0x0016,
+ 0x0026, 0x684c, 0xd0ac, 0x0178, 0x6914, 0x6a10, 0x2100, 0xa205,
+ 0x0150, 0x6860, 0xa106, 0x1118, 0x685c, 0xa206, 0x0120, 0x6962,
+ 0x6a5e, 0xa085, 0x0001, 0x002e, 0x001e, 0x0005, 0x00d6, 0x0036,
+ 0x6310, 0x2368, 0x684a, 0x6952, 0xa29e, 0x4000, 0x11a0, 0x00c6,
+ 0x6318, 0x2360, 0x2009, 0x0000, 0x6838, 0xd0f4, 0x1140, 0x080c,
+ 0x524a, 0x1108, 0xc185, 0x6000, 0xd0bc, 0x0108, 0xc18d, 0x6a66,
+ 0x696a, 0x00ce, 0x0080, 0x6a66, 0x3918, 0xa398, 0x0006, 0x231c,
+ 0x686b, 0x0004, 0x6b72, 0x00c6, 0x6318, 0x2360, 0x6004, 0xa084,
+ 0x00ff, 0x686e, 0x00ce, 0x080c, 0x5408, 0x6013, 0x0000, 0x003e,
+ 0x00de, 0x0005, 0x00c6, 0x0026, 0x0016, 0xa186, 0x0035, 0x0110,
+ 0x6a34, 0x0008, 0x6a28, 0x080c, 0x9c4a, 0x01f0, 0x2260, 0x611c,
+ 0xa186, 0x0003, 0x0118, 0xa186, 0x0006, 0x1190, 0x6834, 0xa206,
+ 0x0140, 0x6838, 0xa206, 0x1160, 0x6108, 0x6834, 0xa106, 0x1140,
+ 0x0020, 0x6008, 0x6938, 0xa106, 0x1118, 0x6018, 0x6918, 0xa106,
+ 0x001e, 0x002e, 0x00ce, 0x0005, 0xa085, 0x0001, 0x0cc8, 0x6944,
+ 0xd1cc, 0x0198, 0xa18c, 0x00ff, 0xa18e, 0x0002, 0x1170, 0xad88,
+ 0x001e, 0x210c, 0xa18c, 0x0f00, 0x810f, 0xa18e, 0x0001, 0x1128,
+ 0x6810, 0x6914, 0xa115, 0x190c, 0x9483, 0x0005, 0x080c, 0x861d,
+ 0x0804, 0x7173, 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x1515,
+ 0x0013, 0x006e, 0x0005, 0xa16b, 0xa646, 0xa76c, 0xa16b, 0xa16b,
+ 0xa16b, 0xa16b, 0xa16b, 0xa1a3, 0xa7f0, 0xa16b, 0xa16b, 0xa16b,
+ 0xa16b, 0xa16b, 0xa16b, 0x080c, 0x1515, 0x0066, 0x6000, 0xa0b2,
+ 0x0010, 0x1a0c, 0x1515, 0x0013, 0x006e, 0x0005, 0xa186, 0xac77,
+ 0xa186, 0xa186, 0xa186, 0xa186, 0xa186, 0xa186, 0xac39, 0xacbf,
+ 0xa186, 0xb26c, 0xb29c, 0xb26c, 0xb29c, 0xa186, 0x080c, 0x1515,
+ 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x1515, 0x0013, 0x006e,
+ 0x0005, 0xa1a1, 0xa940, 0xaa0d, 0xaa3a, 0xaabe, 0xa1a1, 0xabab,
+ 0xab56, 0xa7fc, 0xac0f, 0xac24, 0xa1a1, 0xa1a1, 0xa1a1, 0xa1a1,
+ 0xa1a1, 0x080c, 0x1515, 0xa1b2, 0x0080, 0x1a0c, 0x1515, 0x2100,
+ 0xa1b2, 0x0040, 0x1a04, 0xa5ba, 0x0002, 0xa1ed, 0xa3b8, 0xa1ed,
+ 0xa1ed, 0xa1ed, 0xa3bf, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed,
+ 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed,
+ 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ef, 0xa24d, 0xa25c, 0xa2aa,
+ 0xa2c8, 0xa346, 0xa3a5, 0xa1ed, 0xa1ed, 0xa3c2, 0xa1ed, 0xa1ed,
+ 0xa3d5, 0xa3e0, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa46b,
+ 0xa1ed, 0xa1ed, 0xa47e, 0xa1ed, 0xa1ed, 0xa436, 0xa1ed, 0xa1ed,
+ 0xa1ed, 0xa496, 0xa1ed, 0xa1ed, 0xa1ed, 0xa510, 0xa1ed, 0xa1ed,
+ 0xa1ed, 0xa1ed, 0xa1ed, 0xa1ed, 0xa581, 0x080c, 0x1515, 0x080c,
+ 0x5309, 0x1150, 0x2001, 0xb535, 0x2004, 0xd0cc, 0x1128, 0xa084,
+ 0x0009, 0xa086, 0x0008, 0x1140, 0x6007, 0x0009, 0x602b, 0x0009,
+ 0x6013, 0x0000, 0x0804, 0xa3b3, 0x080c, 0x52f9, 0x00e6, 0x00c6,
+ 0x0036, 0x0026, 0x0016, 0x6218, 0x2270, 0x72a0, 0x0026, 0x2019,
+ 0x0029, 0x080c, 0x6df5, 0x0076, 0x2039, 0x0000, 0x080c, 0x6d02,
+ 0x2c08, 0x080c, 0xae82, 0x007e, 0x001e, 0x2e60, 0x080c, 0x51aa,
+ 0x001e, 0x002e, 0x003e, 0x00ce, 0x00ee, 0x6618, 0x00c6, 0x2660,
+ 0x080c, 0x4fb8, 0x00ce, 0xa6b0, 0x0001, 0x2634, 0xa684, 0x00ff,
+ 0xa082, 0x0006, 0x0278, 0x080c, 0xadc6, 0x1904, 0xa2a4, 0x080c,
+ 0xad66, 0x1120, 0x6007, 0x0008, 0x0804, 0xa3b3, 0x6007, 0x0009,
+ 0x0804, 0xa3b3, 0x080c, 0xaf7b, 0x0128, 0x080c, 0xadc6, 0x0d78,
+ 0x0804, 0xa2a4, 0x6013, 0x1900, 0x0c88, 0x080c, 0x2dbf, 0x1904,
+ 0xa5b7, 0x6106, 0x080c, 0xad20, 0x6007, 0x0006, 0x0804, 0xa3b3,
+ 0x6007, 0x0007, 0x0804, 0xa3b3, 0x080c, 0xb2d0, 0x1904, 0xa5b7,
+ 0x080c, 0x2dbf, 0x1904, 0xa5b7, 0x00d6, 0x6618, 0x2668, 0x6e04,
+ 0xa684, 0x00ff, 0xa082, 0x0006, 0x1220, 0x2001, 0x0001, 0x080c,
+ 0x4eeb, 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0006, 0x0188, 0xa686,
+ 0x0004, 0x0170, 0x6e04, 0xa6b4, 0x00ff, 0xa686, 0x0006, 0x0140,
+ 0xa686, 0x0004, 0x0128, 0xa686, 0x0005, 0x0110, 0x00de, 0x00e0,
+ 0x080c, 0xae24, 0x11a0, 0xa686, 0x0006, 0x1150, 0x0026, 0x6218,
+ 0xa290, 0x0028, 0x2214, 0x2009, 0x0000, 0x080c, 0x2ce1, 0x002e,
+ 0x080c, 0x504b, 0x6007, 0x000a, 0x00de, 0x0804, 0xa3b3, 0x6007,
+ 0x000b, 0x00de, 0x0804, 0xa3b3, 0x080c, 0x2c9c, 0x6007, 0x0001,
+ 0x0804, 0xa3b3, 0x080c, 0xb2d0, 0x1904, 0xa5b7, 0x080c, 0x2dbf,
+ 0x1904, 0xa5b7, 0x6618, 0x00d6, 0x2668, 0x6e04, 0x00de, 0xa686,
+ 0x0707, 0x0d50, 0x0026, 0x6218, 0xa290, 0x0028, 0x2214, 0x2009,
+ 0x0000, 0x080c, 0x2ce1, 0x002e, 0x6007, 0x000c, 0x0804, 0xa3b3,
+ 0x080c, 0x5309, 0x1140, 0x2001, 0xb535, 0x2004, 0xa084, 0x0009,
+ 0xa086, 0x0008, 0x1110, 0x0804, 0xa1fc, 0x080c, 0x52f9, 0x6618,
+ 0xa6b0, 0x0001, 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x06e8,
+ 0x1138, 0x0026, 0x2001, 0x0006, 0x080c, 0x4f2a, 0x002e, 0x0050,
+ 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0004, 0x0120, 0xa686, 0x0006,
+ 0x1904, 0xa2a4, 0x080c, 0xae31, 0x1120, 0x6007, 0x000e, 0x0804,
+ 0xa3b3, 0x0046, 0x6418, 0xa4a0, 0x0028, 0x2424, 0xa4a4, 0x00ff,
+ 0x8427, 0x0046, 0x080c, 0x2c9c, 0x004e, 0x0016, 0xa006, 0x2009,
+ 0xb553, 0x210c, 0xd1a4, 0x0158, 0x2009, 0x0029, 0x080c, 0xb0e8,
+ 0x6018, 0x00d6, 0x2068, 0x6800, 0xc0e5, 0x6802, 0x00de, 0x001e,
+ 0x004e, 0x6007, 0x0001, 0x0804, 0xa3b3, 0x2001, 0x0001, 0x080c,
+ 0x4eeb, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019,
+ 0xb505, 0x2011, 0xbb90, 0x080c, 0x90da, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0xa005, 0x0168, 0xa6b4, 0xff00, 0x8637, 0xa682, 0x0004,
+ 0x0a04, 0xa2a4, 0xa682, 0x0007, 0x0a04, 0xa2f2, 0x0804, 0xa2a4,
+ 0x6013, 0x1900, 0x6007, 0x0009, 0x0804, 0xa3b3, 0x080c, 0x5309,
+ 0x1140, 0x2001, 0xb535, 0x2004, 0xa084, 0x0009, 0xa086, 0x0008,
+ 0x1110, 0x0804, 0xa1fc, 0x080c, 0x52f9, 0x6618, 0xa6b0, 0x0001,
+ 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x06b8, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0004, 0x0120, 0xa686, 0x0006, 0x1904, 0xa2a4,
+ 0x080c, 0xae59, 0x1138, 0x080c, 0xad66, 0x1120, 0x6007, 0x0010,
+ 0x0804, 0xa3b3, 0x0046, 0x6418, 0xa4a0, 0x0028, 0x2424, 0xa4a4,
+ 0x00ff, 0x8427, 0x0046, 0x080c, 0x2c9c, 0x004e, 0x0016, 0xa006,
+ 0x2009, 0xb553, 0x210c, 0xd1a4, 0x0158, 0x2009, 0x0029, 0x080c,
+ 0xb0e8, 0x6018, 0x00d6, 0x2068, 0x6800, 0xc0e5, 0x6802, 0x00de,
+ 0x001e, 0x004e, 0x6007, 0x0001, 0x00f0, 0x080c, 0xaf7b, 0x0140,
+ 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0006, 0x0950, 0x0804, 0xa2a4,
+ 0x6013, 0x1900, 0x6007, 0x0009, 0x0070, 0x080c, 0x2dbf, 0x1904,
+ 0xa5b7, 0x080c, 0xb2d0, 0x1904, 0xa5b7, 0x080c, 0xa5df, 0x1904,
+ 0xa2a4, 0x6007, 0x0012, 0x6003, 0x0001, 0x080c, 0x6cd3, 0x0005,
+ 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x6cd3, 0x0cc0, 0x6007,
+ 0x0005, 0x0cc0, 0x080c, 0xb2d0, 0x1904, 0xa5b7, 0x080c, 0x2dbf,
+ 0x1904, 0xa5b7, 0x080c, 0xa5df, 0x1904, 0xa2a4, 0x6007, 0x0020,
+ 0x6003, 0x0001, 0x080c, 0x6cd3, 0x0005, 0x080c, 0x2dbf, 0x1904,
+ 0xa5b7, 0x6007, 0x0023, 0x6003, 0x0001, 0x080c, 0x6cd3, 0x0005,
+ 0x080c, 0xb2d0, 0x1904, 0xa5b7, 0x080c, 0x2dbf, 0x1904, 0xa5b7,
+ 0x080c, 0xa5df, 0x1904, 0xa2a4, 0x0016, 0x0026, 0x2011, 0xbb91,
+ 0x2214, 0xa286, 0xffff, 0x0190, 0x2c08, 0x080c, 0x9c4a, 0x01e0,
+ 0x2260, 0x2011, 0xbb90, 0x2214, 0x6008, 0xa206, 0x11a8, 0x6018,
+ 0xa190, 0x0006, 0x2214, 0xa206, 0x01e8, 0x0070, 0x2011, 0xbb90,
+ 0x2214, 0x2c08, 0xa006, 0x080c, 0xb0ba, 0x11a0, 0x2011, 0xbb91,
+ 0x2214, 0xa286, 0xffff, 0x01c0, 0x2160, 0x6007, 0x0026, 0x6013,
+ 0x1700, 0x2011, 0xbb89, 0x2214, 0xa296, 0xffff, 0x1180, 0x6007,
+ 0x0025, 0x0068, 0x601c, 0xa086, 0x0007, 0x1d70, 0x6004, 0xa086,
+ 0x0024, 0x1110, 0x080c, 0x861d, 0x2160, 0x6007, 0x0025, 0x6003,
+ 0x0001, 0x080c, 0x6cd3, 0x002e, 0x001e, 0x0005, 0x2001, 0x0001,
+ 0x080c, 0x4eeb, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004,
+ 0x2019, 0xb505, 0x2011, 0xbb96, 0x080c, 0x90da, 0x003e, 0x002e,
+ 0x001e, 0x015e, 0x0120, 0x6007, 0x0031, 0x0804, 0xa3b3, 0x080c,
+ 0x8df6, 0x080c, 0x5acf, 0x11b0, 0x0006, 0x0026, 0x0036, 0x080c,
+ 0x5aeb, 0x1158, 0x2001, 0xb79f, 0x2003, 0x0001, 0x2001, 0xb500,
+ 0x2003, 0x0001, 0x080c, 0x5a07, 0x0010, 0x080c, 0x5aa6, 0x003e,
+ 0x002e, 0x000e, 0x0005, 0x080c, 0x2dbf, 0x1904, 0xa5b7, 0x080c,
+ 0xa5df, 0x1904, 0xa2a4, 0x6106, 0x080c, 0xa5fb, 0x6007, 0x002b,
+ 0x0804, 0xa3b3, 0x6007, 0x002c, 0x0804, 0xa3b3, 0x080c, 0xb2d0,
+ 0x1904, 0xa5b7, 0x080c, 0x2dbf, 0x1904, 0xa5b7, 0x080c, 0xa5df,
+ 0x1904, 0xa2a4, 0x6106, 0x080c, 0xa5ff, 0x1120, 0x6007, 0x002e,
+ 0x0804, 0xa3b3, 0x6007, 0x002f, 0x0804, 0xa3b3, 0x080c, 0x2dbf,
+ 0x1904, 0xa5b7, 0x00e6, 0x00d6, 0x00c6, 0x6018, 0xa080, 0x0001,
+ 0x200c, 0xa184, 0x00ff, 0xa086, 0x0006, 0x0158, 0xa184, 0xff00,
+ 0x8007, 0xa086, 0x0006, 0x0128, 0x00ce, 0x00de, 0x00ee, 0x0804,
+ 0xa3b8, 0x2001, 0xb572, 0x2004, 0xd0e4, 0x0904, 0xa50d, 0x2071,
+ 0xbb8c, 0x7010, 0x6036, 0x7014, 0x603a, 0x7108, 0x720c, 0x2001,
+ 0xb553, 0x2004, 0xd0a4, 0x0140, 0x6018, 0x2068, 0x6810, 0xa106,
+ 0x1118, 0x6814, 0xa206, 0x01f8, 0x2001, 0xb553, 0x2004, 0xd0ac,
+ 0x1590, 0x2069, 0xb500, 0x6874, 0xa206, 0x1568, 0x6870, 0xa106,
+ 0x1550, 0x7210, 0x080c, 0x9c4a, 0x0558, 0x080c, 0xb154, 0x0540,
+ 0x622a, 0x6007, 0x0036, 0x6003, 0x0001, 0x080c, 0x6c8d, 0x00ce,
+ 0x00de, 0x00ee, 0x0005, 0x7214, 0xa286, 0xffff, 0x0150, 0x080c,
+ 0x9c4a, 0x01b0, 0xa280, 0x0002, 0x2004, 0x7110, 0xa106, 0x1180,
+ 0x0c08, 0x7210, 0x2c08, 0xa085, 0x0001, 0x080c, 0xb0ba, 0x2c10,
+ 0x2160, 0x0130, 0x08b8, 0x6007, 0x0037, 0x6013, 0x1500, 0x08d8,
+ 0x6007, 0x0037, 0x6013, 0x1700, 0x08b0, 0x6007, 0x0012, 0x0898,
+ 0x080c, 0x2dbf, 0x1904, 0xa5b7, 0x6018, 0xa080, 0x0001, 0x2004,
+ 0xa084, 0xff00, 0x8007, 0xa086, 0x0006, 0x1904, 0xa3b8, 0x00e6,
+ 0x00d6, 0x00c6, 0x2001, 0xb572, 0x2004, 0xd0e4, 0x0904, 0xa579,
+ 0x2069, 0xb500, 0x2071, 0xbb8c, 0x7008, 0x6036, 0x720c, 0x623a,
+ 0xa286, 0xffff, 0x1150, 0x7208, 0x00c6, 0x2c08, 0xa085, 0x0001,
+ 0x080c, 0xb0ba, 0x2c10, 0x00ce, 0x0588, 0x080c, 0x9c4a, 0x0570,
+ 0x00c6, 0x0026, 0x2260, 0x080c, 0x991d, 0x002e, 0x00ce, 0x7118,
+ 0xa18c, 0xff00, 0x810f, 0xa186, 0x0001, 0x0158, 0xa186, 0x0005,
+ 0x0118, 0xa186, 0x0007, 0x1178, 0xa280, 0x0004, 0x2004, 0xa005,
+ 0x0150, 0x0056, 0x7510, 0x7614, 0x080c, 0xb16b, 0x005e, 0x00ce,
+ 0x00de, 0x00ee, 0x0005, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013,
+ 0x2a00, 0x6003, 0x0001, 0x080c, 0x6c8d, 0x0c88, 0x6007, 0x003b,
+ 0x602b, 0x0009, 0x6013, 0x1700, 0x6003, 0x0001, 0x080c, 0x6c8d,
+ 0x0c30, 0x6007, 0x003b, 0x602b, 0x000b, 0x6013, 0x0000, 0x0804,
+ 0xa4e3, 0x00e6, 0x0026, 0x080c, 0x5309, 0x0558, 0x080c, 0x52f9,
+ 0x080c, 0xb34b, 0x1520, 0x2071, 0xb500, 0x70d4, 0xc085, 0x70d6,
+ 0x00f6, 0x2079, 0x0100, 0x72a0, 0xa284, 0x00ff, 0x7072, 0x78e6,
+ 0xa284, 0xff00, 0x7274, 0xa205, 0x7076, 0x78ea, 0x00fe, 0x70df,
+ 0x0000, 0x2001, 0xb553, 0x2004, 0xd0a4, 0x0120, 0x2011, 0xb7f9,
+ 0x2013, 0x07d0, 0xd0ac, 0x1128, 0x080c, 0x2ab8, 0x0010, 0x080c,
+ 0xb377, 0x002e, 0x00ee, 0x080c, 0x861d, 0x0804, 0xa3b7, 0x080c,
+ 0x861d, 0x0005, 0x2600, 0x0002, 0xa5c5, 0xa5c5, 0xa5c5, 0xa5c5,
+ 0xa5c5, 0xa5c7, 0xa5c5, 0xa5c5, 0xa5c5, 0x080c, 0x1515, 0x080c,
+ 0xb2d0, 0x1d68, 0x080c, 0x2dbf, 0x1d50, 0x0089, 0x1138, 0x6007,
+ 0x0045, 0x6003, 0x0001, 0x080c, 0x6cd3, 0x0005, 0x080c, 0x2c9c,
+ 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x6cd3, 0x0005, 0x00d6,
+ 0x0066, 0x6618, 0x2668, 0x6e04, 0xa6b4, 0xff00, 0x8637, 0xa686,
+ 0x0006, 0x0170, 0xa686, 0x0004, 0x0158, 0x6e04, 0xa6b4, 0x00ff,
+ 0xa686, 0x0006, 0x0128, 0xa686, 0x0004, 0x0110, 0xa085, 0x0001,
+ 0x006e, 0x00de, 0x0005, 0x00d6, 0x0449, 0x00de, 0x0005, 0x00d6,
+ 0x0491, 0x11f0, 0x680c, 0xa08c, 0xff00, 0x6820, 0xa084, 0x00ff,
+ 0xa115, 0x6212, 0x6824, 0x602a, 0xd1e4, 0x0118, 0x2009, 0x0001,
+ 0x0060, 0xd1ec, 0x0168, 0x6920, 0xa18c, 0x00ff, 0x6824, 0x080c,
+ 0x281d, 0x1130, 0x2110, 0x2009, 0x0000, 0x080c, 0x2ce1, 0x0018,
+ 0xa085, 0x0001, 0x0008, 0xa006, 0x00de, 0x0005, 0x2069, 0xbb8d,
+ 0x6800, 0xa082, 0x0010, 0x1228, 0x6013, 0x0000, 0xa085, 0x0001,
+ 0x0008, 0xa006, 0x0005, 0x6013, 0x0000, 0x2069, 0xbb8c, 0x6808,
+ 0xa084, 0xff00, 0xa086, 0x0800, 0x1140, 0x6800, 0xa084, 0x00ff,
+ 0xa08e, 0x0014, 0x0110, 0xa08e, 0x0010, 0x0005, 0x6004, 0xa0b2,
+ 0x0080, 0x1a0c, 0x1515, 0xa1b6, 0x0013, 0x1130, 0x2008, 0xa1b2,
+ 0x0040, 0x1a04, 0xa746, 0x0092, 0xa1b6, 0x0027, 0x0120, 0xa1b6,
+ 0x0014, 0x190c, 0x1515, 0x2001, 0x0007, 0x080c, 0x4f2a, 0x080c,
+ 0x7090, 0x080c, 0x9e1d, 0x080c, 0x7173, 0x0005, 0xa6a6, 0xa6a8,
+ 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a8, 0xa6ba, 0xa73f, 0xa70a, 0xa73f,
+ 0xa71b, 0xa73f, 0xa6ba, 0xa73f, 0xa737, 0xa73f, 0xa737, 0xa73f,
+ 0xa73f, 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6,
+ 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a8, 0xa6a6, 0xa73f, 0xa6a6,
+ 0xa6a6, 0xa73f, 0xa6a6, 0xa73c, 0xa73f, 0xa6a6, 0xa6a6, 0xa6a6,
+ 0xa6a6, 0xa73f, 0xa73f, 0xa6a6, 0xa73f, 0xa73f, 0xa6a6, 0xa6b4,
+ 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6, 0xa73b, 0xa73f, 0xa6a6, 0xa6a6,
+ 0xa73f, 0xa73f, 0xa6a6, 0xa6a6, 0xa6a6, 0xa6a6, 0x080c, 0x1515,
+ 0x080c, 0x7090, 0x2001, 0xb7b6, 0x2004, 0x6016, 0x6003, 0x0002,
+ 0x080c, 0x7173, 0x0804, 0xa745, 0x2001, 0x0000, 0x080c, 0x4eeb,
+ 0x0804, 0xa73f, 0x00f6, 0x2079, 0xb552, 0x7804, 0x00fe, 0xd0ac,
+ 0x1904, 0xa73f, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x6018, 0xa080,
+ 0x0004, 0x2004, 0xa086, 0x00ff, 0x1140, 0x00f6, 0x2079, 0xb500,
+ 0x7898, 0x8000, 0x789a, 0x00fe, 0x00e0, 0x00c6, 0x6018, 0x2060,
+ 0x6000, 0xd0f4, 0x1140, 0x6010, 0xa005, 0x0128, 0x00ce, 0x080c,
+ 0x3f3e, 0x0804, 0xa73f, 0x00ce, 0x2001, 0xb500, 0x2004, 0xa086,
+ 0x0002, 0x1138, 0x00f6, 0x2079, 0xb500, 0x7898, 0x8000, 0x789a,
+ 0x00fe, 0x2001, 0x0002, 0x080c, 0x4efd, 0x080c, 0x7090, 0x601f,
+ 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x6cd3, 0x080c,
+ 0x7173, 0x00c6, 0x6118, 0x2160, 0x2009, 0x0001, 0x080c, 0x69a8,
+ 0x00ce, 0x04d8, 0x6618, 0x00d6, 0x2668, 0x6e04, 0x00de, 0xa6b4,
+ 0xff00, 0x8637, 0xa686, 0x0006, 0x0550, 0xa686, 0x0004, 0x0538,
+ 0x2001, 0x0004, 0x0410, 0x2001, 0xb500, 0x2004, 0xa086, 0x0003,
+ 0x1110, 0x080c, 0x3f3e, 0x2001, 0x0006, 0x04a1, 0x6618, 0x00d6,
+ 0x2668, 0x6e04, 0x00de, 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0006,
+ 0x0170, 0x2001, 0x0006, 0x0048, 0x2001, 0x0004, 0x0030, 0x2001,
+ 0x0006, 0x0401, 0x0020, 0x0018, 0x0010, 0x080c, 0x4f2a, 0x080c,
+ 0x7090, 0x080c, 0x861d, 0x080c, 0x7173, 0x0005, 0x2600, 0x0002,
+ 0xa751, 0xa751, 0xa751, 0xa751, 0xa751, 0xa753, 0xa751, 0xa751,
+ 0xa751, 0x080c, 0x1515, 0x080c, 0x7090, 0x080c, 0x861d, 0x080c,
+ 0x7173, 0x0005, 0x0016, 0x00d6, 0x6118, 0x2168, 0x6900, 0xd184,
+ 0x0140, 0x080c, 0x4efd, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x080c,
+ 0x2cc2, 0x00de, 0x001e, 0x0005, 0x00d6, 0x6618, 0x2668, 0x6804,
+ 0xa084, 0xff00, 0x8007, 0x00de, 0xa0b2, 0x000c, 0x1a0c, 0x1515,
+ 0xa1b6, 0x0015, 0x1110, 0x003b, 0x0028, 0xa1b6, 0x0016, 0x190c,
+ 0x1515, 0x006b, 0x0005, 0x8cdf, 0x8cdf, 0x8cdf, 0x8cdf, 0x8cdf,
+ 0x8cdf, 0xa7dc, 0xa79b, 0x8cdf, 0x8cdf, 0x8cdf, 0x8cdf, 0x8cdf,
+ 0x8cdf, 0x8cdf, 0x8cdf, 0x8cdf, 0x8cdf, 0xa7dc, 0xa7e3, 0x8cdf,
+ 0x8cdf, 0x8cdf, 0x8cdf, 0x00f6, 0x2079, 0xb552, 0x7804, 0xd0ac,
+ 0x11e0, 0x6018, 0xa07d, 0x01c8, 0x7800, 0xd0f4, 0x1118, 0x7810,
+ 0xa005, 0x1198, 0x2001, 0x0000, 0x080c, 0x4eeb, 0x2001, 0x0002,
+ 0x080c, 0x4efd, 0x601f, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002,
+ 0x080c, 0x6cd3, 0x080c, 0x7173, 0x00e8, 0x2011, 0xbb83, 0x2204,
+ 0x8211, 0x220c, 0x080c, 0x281d, 0x11a8, 0x00c6, 0x080c, 0x4fa9,
+ 0x0120, 0x00ce, 0x080c, 0x861d, 0x0068, 0x6010, 0x0006, 0x6014,
+ 0x0006, 0x080c, 0x4c0b, 0x000e, 0x6016, 0x000e, 0x6012, 0x00ce,
+ 0x080c, 0x861d, 0x00fe, 0x0005, 0x6604, 0xa6b6, 0x001e, 0x1110,
+ 0x080c, 0x861d, 0x0005, 0x080c, 0x8f95, 0x1138, 0x6003, 0x0001,
+ 0x6007, 0x0001, 0x080c, 0x6cd3, 0x0010, 0x080c, 0x861d, 0x0005,
+ 0x6004, 0xa08a, 0x0080, 0x1a0c, 0x1515, 0x080c, 0x7090, 0x080c,
+ 0x9e1d, 0x080c, 0x7173, 0x0005, 0xa182, 0x0040, 0x0002, 0xa812,
+ 0xa812, 0xa812, 0xa812, 0xa814, 0xa812, 0xa812, 0xa812, 0xa812,
+ 0xa812, 0xa812, 0xa812, 0xa812, 0xa812, 0xa812, 0xa812, 0xa812,
+ 0xa812, 0xa812, 0x080c, 0x1515, 0x00d6, 0x00e6, 0x00f6, 0x0156,
+ 0x0046, 0x0026, 0x6218, 0xa280, 0x002b, 0x2004, 0xa005, 0x0120,
+ 0x2021, 0x0000, 0x080c, 0xb31c, 0x6106, 0x2071, 0xbb80, 0x7444,
+ 0xa4a4, 0xff00, 0x0904, 0xa878, 0xa486, 0x2000, 0x1130, 0x2009,
+ 0x0001, 0x2011, 0x0200, 0x080c, 0x6b1a, 0x080c, 0x15f8, 0x090c,
+ 0x1515, 0x6003, 0x0007, 0x2d00, 0x6837, 0x010d, 0x6803, 0x0000,
+ 0x683b, 0x0000, 0x6c5a, 0x2c00, 0x685e, 0x6008, 0x68b2, 0x6018,
+ 0x2078, 0x78a0, 0x8007, 0x7130, 0x694a, 0x0016, 0xa084, 0xff00,
+ 0x6846, 0x684f, 0x0000, 0x6853, 0x0000, 0x6857, 0x0036, 0x080c,
+ 0x5408, 0x001e, 0xa486, 0x2000, 0x1130, 0x2019, 0x0017, 0x080c,
+ 0xb065, 0x0804, 0xa8d5, 0xa486, 0x0400, 0x1130, 0x2019, 0x0002,
+ 0x080c, 0xb017, 0x0804, 0xa8d5, 0xa486, 0x0200, 0x1110, 0x080c,
+ 0xaffc, 0xa486, 0x1000, 0x1110, 0x080c, 0xb04a, 0x0804, 0xa8d5,
+ 0x2069, 0xb874, 0x6a00, 0xd284, 0x0904, 0xa93c, 0xa284, 0x0300,
+ 0x1904, 0xa935, 0x6804, 0xa005, 0x0904, 0xa91d, 0x2d78, 0x6003,
+ 0x0007, 0x080c, 0x15df, 0x0904, 0xa8dc, 0x7800, 0xd08c, 0x1118,
+ 0x7804, 0x8001, 0x7806, 0x6013, 0x0000, 0x6803, 0x0000, 0x6837,
+ 0x0116, 0x683b, 0x0000, 0x6008, 0x68b2, 0x2c00, 0x684a, 0x6018,
+ 0x2078, 0x78a0, 0x8007, 0x7130, 0x6986, 0x6846, 0x7928, 0x698a,
+ 0x792c, 0x698e, 0x7930, 0x6992, 0x7934, 0x6996, 0x6853, 0x003d,
+ 0x7244, 0xa294, 0x0003, 0xa286, 0x0002, 0x1118, 0x684f, 0x0040,
+ 0x0040, 0xa286, 0x0001, 0x1118, 0x684f, 0x0080, 0x0010, 0x684f,
+ 0x0000, 0x20a9, 0x000a, 0x2001, 0xbb90, 0xad90, 0x0015, 0x200c,
+ 0x810f, 0x2112, 0x8000, 0x8210, 0x1f04, 0xa8c7, 0x200c, 0x6982,
+ 0x8000, 0x200c, 0x697e, 0x080c, 0x5408, 0x002e, 0x004e, 0x015e,
+ 0x00fe, 0x00ee, 0x00de, 0x0005, 0x2001, 0xb50e, 0x2004, 0xd084,
+ 0x0120, 0x080c, 0x15f8, 0x1904, 0xa88d, 0x6013, 0x0100, 0x6003,
+ 0x0001, 0x6007, 0x0041, 0x080c, 0x6c8d, 0x080c, 0x7173, 0x0c28,
+ 0x2069, 0xbb92, 0x2d04, 0xa084, 0xff00, 0xa086, 0x1200, 0x11a8,
+ 0x2069, 0xbb80, 0x686c, 0xa084, 0x00ff, 0x0016, 0x6110, 0xa18c,
+ 0x0700, 0xa10d, 0x6112, 0x001e, 0x6003, 0x0001, 0x6007, 0x0043,
+ 0x080c, 0x6c8d, 0x080c, 0x7173, 0x0840, 0x6868, 0x602a, 0x686c,
+ 0x602e, 0x6013, 0x0200, 0x6003, 0x0001, 0x6007, 0x0041, 0x080c,
+ 0x6c8d, 0x080c, 0x7173, 0x0804, 0xa8d5, 0x2001, 0xb50d, 0x2004,
+ 0xd0ec, 0x0120, 0x2011, 0x8049, 0x080c, 0x3ecc, 0x6013, 0x0300,
+ 0x0010, 0x6013, 0x0100, 0x6003, 0x0001, 0x6007, 0x0041, 0x080c,
+ 0x6c8d, 0x080c, 0x7173, 0x0804, 0xa8d5, 0x6013, 0x0500, 0x0c98,
+ 0x6013, 0x0600, 0x0804, 0xa8f0, 0x6013, 0x0200, 0x0804, 0xa8f0,
+ 0xa186, 0x0013, 0x1170, 0x6004, 0xa08a, 0x0040, 0x0a0c, 0x1515,
+ 0xa08a, 0x0053, 0x1a0c, 0x1515, 0xa082, 0x0040, 0x2008, 0x0804,
+ 0xa9ca, 0xa186, 0x0051, 0x0138, 0xa186, 0x0047, 0x11d8, 0x6004,
+ 0xa086, 0x0041, 0x0518, 0x2001, 0x0109, 0x2004, 0xd084, 0x01f0,
+ 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x080c, 0x6b74,
+ 0x002e, 0x001e, 0x000e, 0x012e, 0x6000, 0xa086, 0x0002, 0x1170,
+ 0x0804, 0xaa0d, 0xa186, 0x0027, 0x0120, 0xa186, 0x0014, 0x190c,
+ 0x1515, 0x6004, 0xa082, 0x0040, 0x2008, 0x001a, 0x080c, 0x8663,
+ 0x0005, 0xa994, 0xa996, 0xa996, 0xa9ba, 0xa994, 0xa994, 0xa994,
+ 0xa994, 0xa994, 0xa994, 0xa994, 0xa994, 0xa994, 0xa994, 0xa994,
+ 0xa994, 0xa994, 0xa994, 0xa994, 0x080c, 0x1515, 0x080c, 0x7090,
+ 0x080c, 0x7173, 0x0036, 0x00d6, 0x6010, 0xa06d, 0x01c0, 0xad84,
+ 0xf000, 0x01a8, 0x6003, 0x0002, 0x6018, 0x2004, 0xd0bc, 0x1178,
+ 0x2019, 0x0004, 0x080c, 0xb099, 0x6013, 0x0000, 0x6014, 0xa005,
+ 0x1120, 0x2001, 0xb7b7, 0x2004, 0x6016, 0x6003, 0x0007, 0x00de,
+ 0x003e, 0x0005, 0x00d6, 0x080c, 0x7090, 0x080c, 0x7173, 0x080c,
+ 0x9c5a, 0x0120, 0x6010, 0x2068, 0x080c, 0x160f, 0x080c, 0x9e1d,
+ 0x00de, 0x0005, 0x0002, 0xa9de, 0xa9fb, 0xa9e7, 0xaa07, 0xa9de,
+ 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0xa9de,
+ 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0xa9de, 0x080c, 0x1515,
+ 0x6010, 0xa088, 0x0013, 0x2104, 0xa085, 0x0400, 0x200a, 0x080c,
+ 0x7090, 0x6010, 0xa080, 0x0013, 0x2004, 0xd0b4, 0x0138, 0x6003,
+ 0x0007, 0x2009, 0x0043, 0x080c, 0x864c, 0x0010, 0x6003, 0x0002,
+ 0x080c, 0x7173, 0x0005, 0x080c, 0x7090, 0x080c, 0xb2d7, 0x1120,
+ 0x080c, 0x6aef, 0x080c, 0x861d, 0x080c, 0x7173, 0x0005, 0x080c,
+ 0x7090, 0x2009, 0x0041, 0x0804, 0xab56, 0xa182, 0x0040, 0x0002,
+ 0xaa23, 0xaa25, 0xaa23, 0xaa23, 0xaa23, 0xaa23, 0xaa23, 0xaa26,
+ 0xaa23, 0xaa23, 0xaa23, 0xaa23, 0xaa23, 0xaa23, 0xaa23, 0xaa23,
+ 0xaa23, 0xaa31, 0xaa23, 0x080c, 0x1515, 0x0005, 0x6003, 0x0004,
+ 0x6110, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x080c, 0x185e,
+ 0x0005, 0x00d6, 0x080c, 0x6aef, 0x00de, 0x080c, 0xb33a, 0x080c,
+ 0x861d, 0x0005, 0xa182, 0x0040, 0x0002, 0xaa50, 0xaa50, 0xaa50,
+ 0xaa50, 0xaa50, 0xaa50, 0xaa50, 0xaa52, 0xaa50, 0xaa55, 0xaa8e,
+ 0xaa50, 0xaa50, 0xaa50, 0xaa50, 0xaa8e, 0xaa50, 0xaa50, 0xaa50,
+ 0x080c, 0x1515, 0x080c, 0x8663, 0x0005, 0x2001, 0xb572, 0x2004,
+ 0xd0e4, 0x0158, 0x2001, 0x0100, 0x2004, 0xa082, 0x0005, 0x0228,
+ 0x2001, 0x011f, 0x2004, 0x6036, 0x0010, 0x6037, 0x0000, 0x080c,
+ 0x7126, 0x080c, 0x7230, 0x6010, 0x00d6, 0x2068, 0x684c, 0xd0fc,
+ 0x0150, 0xa08c, 0x0003, 0xa18e, 0x0002, 0x0168, 0x2009, 0x0041,
+ 0x00de, 0x0804, 0xab56, 0x6003, 0x0007, 0x6017, 0x0000, 0x080c,
+ 0x6aef, 0x00de, 0x0005, 0x080c, 0xb2d7, 0x0110, 0x00de, 0x0005,
+ 0x080c, 0x6aef, 0x080c, 0x861d, 0x00de, 0x0ca0, 0x0036, 0x080c,
+ 0x7126, 0x080c, 0x7230, 0x6010, 0x00d6, 0x2068, 0x6018, 0x2004,
+ 0xd0bc, 0x0188, 0x684c, 0xa084, 0x0003, 0xa086, 0x0002, 0x0140,
+ 0x687c, 0x632c, 0xa31a, 0x632e, 0x6880, 0x6328, 0xa31b, 0x632a,
+ 0x6003, 0x0002, 0x0080, 0x2019, 0x0004, 0x080c, 0xb099, 0x6014,
+ 0xa005, 0x1128, 0x2001, 0xb7b7, 0x2004, 0x8003, 0x6016, 0x6013,
+ 0x0000, 0x6003, 0x0007, 0x00de, 0x003e, 0x0005, 0xa186, 0x0013,
+ 0x1150, 0x6004, 0xa086, 0x0042, 0x190c, 0x1515, 0x080c, 0x7090,
+ 0x080c, 0x7173, 0x0005, 0xa186, 0x0027, 0x0118, 0xa186, 0x0014,
+ 0x1180, 0x6004, 0xa086, 0x0042, 0x190c, 0x1515, 0x2001, 0x0007,
+ 0x080c, 0x4f2a, 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c, 0x7173,
+ 0x0005, 0xa182, 0x0040, 0x0002, 0xaaf7, 0xaaf7, 0xaaf7, 0xaaf7,
+ 0xaaf7, 0xaaf7, 0xaaf7, 0xaaf9, 0xab05, 0xaaf7, 0xaaf7, 0xaaf7,
+ 0xaaf7, 0xaaf7, 0xaaf7, 0xaaf7, 0xaaf7, 0xaaf7, 0xaaf7, 0x080c,
+ 0x1515, 0x0036, 0x0046, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10,
+ 0x080c, 0x185e, 0x004e, 0x003e, 0x0005, 0x6010, 0x00d6, 0x2068,
+ 0x6810, 0x6a14, 0x0006, 0x0046, 0x0056, 0x6c7c, 0xa422, 0x6d80,
+ 0x2200, 0xa52b, 0x602c, 0xa420, 0x642e, 0x6028, 0xa529, 0x652a,
+ 0x005e, 0x004e, 0x000e, 0xa20d, 0x1178, 0x684c, 0xd0fc, 0x0120,
+ 0x2009, 0x0041, 0x00de, 0x0490, 0x6003, 0x0007, 0x6017, 0x0000,
+ 0x080c, 0x6aef, 0x00de, 0x0005, 0x0006, 0x00f6, 0x2c78, 0x080c,
+ 0x5305, 0x00fe, 0x000e, 0x0120, 0x6003, 0x0002, 0x00de, 0x0005,
+ 0x2009, 0xb50d, 0x210c, 0xd19c, 0x0118, 0x6003, 0x0007, 0x0010,
+ 0x6003, 0x0006, 0x0021, 0x080c, 0x6af1, 0x00de, 0x0005, 0xd2fc,
+ 0x0140, 0x8002, 0x8000, 0x8212, 0xa291, 0x0000, 0x2009, 0x0009,
+ 0x0010, 0x2009, 0x0015, 0x6a6a, 0x6866, 0x0005, 0xa182, 0x0040,
+ 0x0208, 0x0062, 0xa186, 0x0013, 0x0120, 0xa186, 0x0014, 0x190c,
+ 0x1515, 0x6020, 0xd0dc, 0x090c, 0x1515, 0x0005, 0xab79, 0xab80,
+ 0xab8c, 0xab98, 0xab79, 0xab79, 0xab79, 0xaba7, 0xab79, 0xab7b,
+ 0xab7b, 0xab79, 0xab79, 0xab79, 0xab79, 0xab7b, 0xab79, 0xab7b,
+ 0xab79, 0x080c, 0x1515, 0x6020, 0xd0dc, 0x090c, 0x1515, 0x0005,
+ 0x6003, 0x0001, 0x6106, 0x080c, 0x6c8d, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x7173, 0x012e, 0x0005, 0x6003, 0x0001, 0x6106, 0x080c,
+ 0x6c8d, 0x0126, 0x2091, 0x8000, 0x080c, 0x7173, 0x012e, 0x0005,
+ 0x6003, 0x0003, 0x6106, 0x2c10, 0x080c, 0x1fa9, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6cf0, 0x080c, 0x7230, 0x012e, 0x0005, 0xa016,
+ 0x080c, 0x185e, 0x0005, 0x0126, 0x2091, 0x8000, 0x0036, 0x00d6,
+ 0xa182, 0x0040, 0x0023, 0x00de, 0x003e, 0x012e, 0x0005, 0xabc7,
+ 0xabc9, 0xabdb, 0xabf6, 0xabc7, 0xabc7, 0xabc7, 0xac0b, 0xabc7,
+ 0xabc7, 0xabc7, 0xabc7, 0xabc7, 0xabc7, 0xabc7, 0xabc7, 0x080c,
+ 0x1515, 0x6010, 0x2068, 0x684c, 0xd0fc, 0x01f8, 0xa09c, 0x0003,
+ 0xa39e, 0x0003, 0x01d0, 0x6003, 0x0001, 0x6106, 0x080c, 0x6c8d,
+ 0x080c, 0x7173, 0x0498, 0x6010, 0x2068, 0x684c, 0xd0fc, 0x0168,
+ 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0140, 0x6003, 0x0001, 0x6106,
+ 0x080c, 0x6c8d, 0x080c, 0x7173, 0x0408, 0x6013, 0x0000, 0x6017,
+ 0x0000, 0x2019, 0x0004, 0x080c, 0xb099, 0x00c0, 0x6010, 0x2068,
+ 0x684c, 0xd0fc, 0x0d90, 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0d68,
+ 0x6003, 0x0003, 0x6106, 0x2c10, 0x080c, 0x1fa9, 0x080c, 0x6cf0,
+ 0x080c, 0x7230, 0x0018, 0xa016, 0x080c, 0x185e, 0x0005, 0x080c,
+ 0x7090, 0x6110, 0x81ff, 0x0158, 0x00d6, 0x2168, 0x080c, 0xb380,
+ 0x0036, 0x2019, 0x0029, 0x080c, 0xb099, 0x003e, 0x00de, 0x080c,
+ 0x9e1d, 0x080c, 0x7173, 0x0005, 0x080c, 0x7126, 0x6110, 0x81ff,
+ 0x0158, 0x00d6, 0x2168, 0x080c, 0xb380, 0x0036, 0x2019, 0x0029,
+ 0x080c, 0xb099, 0x003e, 0x00de, 0x080c, 0x9e1d, 0x080c, 0x7230,
+ 0x0005, 0xa182, 0x0085, 0x0002, 0xac45, 0xac43, 0xac43, 0xac51,
+ 0xac43, 0xac43, 0xac43, 0x080c, 0x1515, 0x6003, 0x000b, 0x6106,
+ 0x080c, 0x6c8d, 0x0126, 0x2091, 0x8000, 0x080c, 0x7173, 0x012e,
+ 0x0005, 0x0026, 0x00e6, 0x080c, 0xb2d0, 0x0118, 0x080c, 0x861d,
+ 0x00d8, 0x2071, 0xbb80, 0x7224, 0x6212, 0x7220, 0x080c, 0xaf47,
+ 0x0118, 0x6007, 0x0086, 0x0040, 0x6007, 0x0087, 0x7224, 0xa296,
+ 0xffff, 0x1110, 0x6007, 0x0086, 0x6003, 0x0001, 0x080c, 0x6c8d,
+ 0x080c, 0x7173, 0x080c, 0x7230, 0x00ee, 0x002e, 0x0005, 0xa186,
+ 0x0013, 0x1160, 0x6004, 0xa08a, 0x0085, 0x0a0c, 0x1515, 0xa08a,
+ 0x008c, 0x1a0c, 0x1515, 0xa082, 0x0085, 0x00a2, 0xa186, 0x0027,
+ 0x0130, 0xa186, 0x0014, 0x0118, 0x080c, 0x8663, 0x0050, 0x2001,
+ 0x0007, 0x080c, 0x4f2a, 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c,
+ 0x7173, 0x0005, 0xaca1, 0xaca3, 0xaca3, 0xaca1, 0xaca1, 0xaca1,
+ 0xaca1, 0x080c, 0x1515, 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c,
+ 0x7173, 0x0005, 0xa182, 0x0085, 0x0a0c, 0x1515, 0xa182, 0x008c,
+ 0x1a0c, 0x1515, 0xa182, 0x0085, 0x0002, 0xacbc, 0xacbc, 0xacbc,
+ 0xacbe, 0xacbc, 0xacbc, 0xacbc, 0x080c, 0x1515, 0x0005, 0xa186,
+ 0x0013, 0x0148, 0xa186, 0x0014, 0x0130, 0xa186, 0x0027, 0x0118,
+ 0x080c, 0x8663, 0x0030, 0x080c, 0x7090, 0x080c, 0x9e1d, 0x080c,
+ 0x7173, 0x0005, 0x0036, 0x080c, 0xb33a, 0x603f, 0x0000, 0x2019,
+ 0x000b, 0x0031, 0x601f, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005,
+ 0x0126, 0x0036, 0x2091, 0x8000, 0x0086, 0x2c40, 0x0096, 0x2049,
+ 0x0000, 0x080c, 0x8130, 0x009e, 0x008e, 0x1578, 0x0076, 0x2c38,
+ 0x080c, 0x81d6, 0x007e, 0x1548, 0x6000, 0xa086, 0x0000, 0x0528,
+ 0x601c, 0xa086, 0x0007, 0x0508, 0x00d6, 0x6000, 0xa086, 0x0004,
+ 0x1150, 0x080c, 0xb33a, 0x601f, 0x0007, 0x2001, 0xb7b6, 0x2004,
+ 0x6016, 0x080c, 0x194d, 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0110,
+ 0x080c, 0xb099, 0x00de, 0x6013, 0x0000, 0x080c, 0xb33a, 0x601f,
+ 0x0007, 0x2001, 0xb7b6, 0x2004, 0x6016, 0x003e, 0x012e, 0x0005,
+ 0x00f6, 0x00c6, 0x0036, 0x0156, 0x2079, 0xbb80, 0x7938, 0x783c,
+ 0x080c, 0x281d, 0x15b0, 0x0016, 0x00c6, 0x080c, 0x4fa9, 0x1578,
+ 0x001e, 0x002e, 0x0026, 0x0016, 0x2019, 0x0029, 0x080c, 0x8299,
+ 0x080c, 0x6df5, 0x0076, 0x2039, 0x0000, 0x080c, 0x6d02, 0x007e,
+ 0x001e, 0x0076, 0x2039, 0x0000, 0x080c, 0xae82, 0x007e, 0x080c,
+ 0x51aa, 0x0026, 0x6204, 0xa294, 0xff00, 0x8217, 0xa286, 0x0006,
+ 0x0118, 0xa286, 0x0004, 0x1118, 0x62a0, 0x080c, 0x2d55, 0x002e,
+ 0x001e, 0x080c, 0x4c0b, 0x6612, 0x6516, 0xa006, 0x0010, 0x00ce,
+ 0x001e, 0x015e, 0x003e, 0x00ce, 0x00fe, 0x0005, 0x00c6, 0x00d6,
+ 0x00e6, 0x0016, 0x2009, 0xb521, 0x2104, 0xa086, 0x0074, 0x1904,
+ 0xadbb, 0x2069, 0xbb8e, 0x690c, 0xa182, 0x0100, 0x06c0, 0x6908,
+ 0xa184, 0x8000, 0x05e8, 0x2001, 0xb79e, 0x2004, 0xa005, 0x1160,
+ 0x6018, 0x2070, 0x7010, 0xa084, 0x00ff, 0x0118, 0x7000, 0xd0f4,
+ 0x0118, 0xa184, 0x0800, 0x0560, 0x6910, 0xa18a, 0x0001, 0x0610,
+ 0x6914, 0x2069, 0xbbae, 0x6904, 0x81ff, 0x1198, 0x690c, 0xa182,
+ 0x0100, 0x02a8, 0x6908, 0x81ff, 0x1178, 0x6910, 0xa18a, 0x0001,
+ 0x0288, 0x6918, 0xa18a, 0x0001, 0x0298, 0x00d0, 0x6013, 0x0100,
+ 0x00a0, 0x6013, 0x0300, 0x0088, 0x6013, 0x0500, 0x0070, 0x6013,
+ 0x0700, 0x0058, 0x6013, 0x0900, 0x0040, 0x6013, 0x0b00, 0x0028,
+ 0x6013, 0x0f00, 0x0010, 0x6013, 0x2d00, 0xa085, 0x0001, 0x0008,
+ 0xa006, 0x001e, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00d6,
+ 0x0026, 0x0036, 0x0156, 0x6218, 0x2268, 0x6b04, 0xa394, 0x00ff,
+ 0xa286, 0x0006, 0x0190, 0xa286, 0x0004, 0x0178, 0xa394, 0xff00,
+ 0x8217, 0xa286, 0x0006, 0x0148, 0xa286, 0x0004, 0x0130, 0x00c6,
+ 0x2d60, 0x080c, 0x4fb8, 0x00ce, 0x04c0, 0x2011, 0xbb96, 0xad98,
+ 0x000a, 0x20a9, 0x0004, 0x080c, 0x90da, 0x1580, 0x2011, 0xbb9a,
+ 0xad98, 0x0006, 0x20a9, 0x0004, 0x080c, 0x90da, 0x1538, 0x0046,
+ 0x0016, 0x6aa0, 0xa294, 0x00ff, 0x8227, 0xa006, 0x2009, 0xb553,
+ 0x210c, 0xd1a4, 0x0138, 0x2009, 0x0029, 0x080c, 0xb0e8, 0x6800,
+ 0xc0e5, 0x6802, 0x2019, 0x0029, 0x080c, 0x6df5, 0x0076, 0x2039,
+ 0x0000, 0x080c, 0x6d02, 0x2c08, 0x080c, 0xae82, 0x007e, 0x2001,
+ 0x0007, 0x080c, 0x4f2a, 0x001e, 0x004e, 0xa006, 0x015e, 0x003e,
+ 0x002e, 0x00de, 0x00ce, 0x0005, 0x00d6, 0x2069, 0xbb8e, 0x6800,
+ 0xa086, 0x0800, 0x0118, 0x6013, 0x0000, 0x0008, 0xa006, 0x00de,
+ 0x0005, 0x00c6, 0x00f6, 0x0016, 0x0026, 0x0036, 0x0156, 0x2079,
+ 0xbb8c, 0x7930, 0x7834, 0x080c, 0x281d, 0x11a0, 0x080c, 0x4fa9,
+ 0x1188, 0x2011, 0xbb90, 0xac98, 0x000a, 0x20a9, 0x0004, 0x080c,
+ 0x90da, 0x1140, 0x2011, 0xbb94, 0xac98, 0x0006, 0x20a9, 0x0004,
+ 0x080c, 0x90da, 0x015e, 0x003e, 0x002e, 0x001e, 0x00fe, 0x00ce,
+ 0x0005, 0x00c6, 0x0006, 0x0016, 0x0026, 0x0036, 0x0156, 0x2011,
+ 0xbb83, 0x2204, 0x8211, 0x220c, 0x080c, 0x281d, 0x11a0, 0x080c,
+ 0x4fa9, 0x1188, 0x2011, 0xbb96, 0xac98, 0x000a, 0x20a9, 0x0004,
+ 0x080c, 0x90da, 0x1140, 0x2011, 0xbb9a, 0xac98, 0x0006, 0x20a9,
+ 0x0004, 0x080c, 0x90da, 0x015e, 0x003e, 0x002e, 0x001e, 0x000e,
+ 0x00ce, 0x0005, 0x00e6, 0x00c6, 0x0086, 0x0076, 0x0066, 0x0056,
+ 0x0046, 0x0026, 0x0126, 0x2091, 0x8000, 0x2740, 0x2029, 0xb7e9,
+ 0x252c, 0x2021, 0xb7ef, 0x2424, 0x2061, 0xbd00, 0x2071, 0xb500,
+ 0x7648, 0x7068, 0x81ff, 0x0150, 0x0006, 0xa186, 0xb8f4, 0x000e,
+ 0x0128, 0x8001, 0xa602, 0x1a04, 0xaf03, 0x0018, 0xa606, 0x0904,
+ 0xaf03, 0x2100, 0xac06, 0x0904, 0xaefa, 0x080c, 0xb110, 0x0904,
+ 0xaefa, 0x671c, 0xa786, 0x0001, 0x0904, 0xaf1e, 0xa786, 0x0004,
+ 0x0904, 0xaf1e, 0xa786, 0x0007, 0x05e8, 0x2500, 0xac06, 0x05d0,
+ 0x2400, 0xac06, 0x05b8, 0x080c, 0xb120, 0x15a0, 0x88ff, 0x0118,
+ 0x6050, 0xa906, 0x1578, 0x00d6, 0x6000, 0xa086, 0x0004, 0x1120,
+ 0x0016, 0x080c, 0x194d, 0x001e, 0xa786, 0x0008, 0x1148, 0x080c,
+ 0x9e58, 0x1130, 0x080c, 0x8c19, 0x00de, 0x080c, 0x9e1d, 0x00d0,
+ 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0190, 0xa786, 0x0003, 0x1528,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0xb380, 0x0016,
+ 0x080c, 0x9ecc, 0x080c, 0x5408, 0x001e, 0x080c, 0x9e11, 0x00de,
+ 0x080c, 0x9e1d, 0xace0, 0x0018, 0x2001, 0xb517, 0x2004, 0xac02,
+ 0x1210, 0x0804, 0xae96, 0x012e, 0x002e, 0x004e, 0x005e, 0x006e,
+ 0x007e, 0x008e, 0x00ce, 0x00ee, 0x0005, 0xa786, 0x0006, 0x1150,
+ 0xa386, 0x0005, 0x0128, 0x080c, 0xb380, 0x080c, 0xb099, 0x08f8,
+ 0x00de, 0x0c00, 0xa786, 0x000a, 0x0968, 0x0850, 0x080c, 0xb120,
+ 0x19c8, 0x81ff, 0x09b8, 0xa180, 0x0001, 0x2004, 0xa086, 0x0018,
+ 0x0130, 0xa180, 0x0001, 0x2004, 0xa086, 0x002d, 0x1958, 0x6000,
+ 0xa086, 0x0002, 0x1938, 0x080c, 0x9e47, 0x0130, 0x080c, 0x9e58,
+ 0x1908, 0x080c, 0x8c19, 0x0038, 0x080c, 0x2cc2, 0x080c, 0x9e58,
+ 0x1110, 0x080c, 0x8c19, 0x080c, 0x9e1d, 0x0804, 0xaefa, 0x00c6,
+ 0x00e6, 0x0016, 0x2c08, 0x2170, 0xa006, 0x080c, 0xb0ba, 0x001e,
+ 0x0120, 0x601c, 0xa084, 0x000f, 0x001b, 0x00ee, 0x00ce, 0x0005,
+ 0xaf60, 0xaf60, 0xaf60, 0xaf60, 0xaf60, 0xaf60, 0xaf62, 0xaf60,
+ 0xa006, 0x0005, 0x0046, 0x0016, 0x7018, 0xa080, 0x0028, 0x2024,
+ 0xa4a4, 0x00ff, 0x8427, 0x2c00, 0x2009, 0x0020, 0x080c, 0xb0e8,
+ 0x001e, 0x004e, 0x0036, 0x2019, 0x0002, 0x080c, 0xace0, 0x003e,
+ 0xa085, 0x0001, 0x0005, 0x2001, 0x0001, 0x080c, 0x4eeb, 0x0156,
+ 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019, 0xb505, 0x2011,
+ 0xbb96, 0x080c, 0x90da, 0x003e, 0x002e, 0x001e, 0x015e, 0xa005,
+ 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0086, 0x0076, 0x0066, 0x0026,
+ 0x0126, 0x2091, 0x8000, 0x2740, 0x2061, 0xbd00, 0x2079, 0x0001,
+ 0x8fff, 0x0904, 0xafef, 0x2071, 0xb500, 0x7648, 0x7068, 0x8001,
+ 0xa602, 0x1a04, 0xafef, 0x88ff, 0x0128, 0x2800, 0xac06, 0x15b0,
+ 0x2079, 0x0000, 0x080c, 0xb110, 0x0588, 0x2400, 0xac06, 0x0570,
+ 0x671c, 0xa786, 0x0006, 0x1550, 0xa786, 0x0007, 0x0538, 0x88ff,
+ 0x1140, 0x6018, 0xa206, 0x1510, 0x85ff, 0x0118, 0x6050, 0xa106,
+ 0x11e8, 0x00d6, 0x6000, 0xa086, 0x0004, 0x1150, 0x080c, 0xb33a,
+ 0x601f, 0x0007, 0x2001, 0xb7b6, 0x2004, 0x6016, 0x080c, 0x194d,
+ 0x6010, 0x2068, 0x080c, 0x9c5a, 0x0120, 0x0046, 0x080c, 0xb099,
+ 0x004e, 0x00de, 0x080c, 0x9e1d, 0x88ff, 0x1198, 0xace0, 0x0018,
+ 0x2001, 0xb517, 0x2004, 0xac02, 0x1210, 0x0804, 0xafa0, 0xa006,
+ 0x012e, 0x002e, 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee, 0x00fe,
+ 0x0005, 0xa8c5, 0x0001, 0x0ca0, 0x0076, 0x0056, 0x0086, 0x2041,
+ 0x0000, 0x2029, 0x0001, 0x2c20, 0x2019, 0x0002, 0x6218, 0x0096,
+ 0x2049, 0x0000, 0x080c, 0x8130, 0x009e, 0x008e, 0x2039, 0x0000,
+ 0x080c, 0x81d6, 0x080c, 0xaf91, 0x005e, 0x007e, 0x0005, 0x0026,
+ 0x0046, 0x0056, 0x0076, 0x00c6, 0x0156, 0x2c20, 0x2128, 0x20a9,
+ 0x007f, 0x2009, 0x0000, 0x0016, 0x0036, 0x080c, 0x4fa9, 0x11b0,
+ 0x2c10, 0x0056, 0x0086, 0x2041, 0x0000, 0x2508, 0x2029, 0x0001,
+ 0x0096, 0x2049, 0x0000, 0x080c, 0x8130, 0x009e, 0x008e, 0x2039,
+ 0x0000, 0x080c, 0x81d6, 0x080c, 0xaf91, 0x005e, 0x003e, 0x001e,
+ 0x8108, 0x1f04, 0xb023, 0x015e, 0x00ce, 0x007e, 0x005e, 0x004e,
+ 0x002e, 0x0005, 0x0076, 0x0056, 0x6218, 0x0086, 0x2041, 0x0000,
+ 0x2029, 0x0001, 0x2019, 0x0048, 0x0096, 0x2049, 0x0000, 0x080c,
+ 0x8130, 0x009e, 0x008e, 0x2039, 0x0000, 0x080c, 0x81d6, 0x2c20,
+ 0x080c, 0xaf91, 0x005e, 0x007e, 0x0005, 0x0026, 0x0046, 0x0056,
+ 0x0076, 0x00c6, 0x0156, 0x2c20, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0x0016, 0x0036, 0x080c, 0x4fa9, 0x11c0, 0x2c10, 0x0086, 0x2041,
+ 0x0000, 0x2828, 0x0046, 0x2021, 0x0001, 0x080c, 0xb31c, 0x004e,
+ 0x0096, 0x2049, 0x0000, 0x080c, 0x8130, 0x009e, 0x008e, 0x2039,
+ 0x0000, 0x080c, 0x81d6, 0x080c, 0xaf91, 0x003e, 0x001e, 0x8108,
+ 0x1f04, 0xb070, 0x015e, 0x00ce, 0x007e, 0x005e, 0x004e, 0x002e,
+ 0x0005, 0x0016, 0x00f6, 0x3800, 0xd08c, 0x0130, 0xad82, 0x1000,
+ 0x02b0, 0xad82, 0xb500, 0x0230, 0xad82, 0xed00, 0x0280, 0xad82,
+ 0xffff, 0x1268, 0x6800, 0xa07d, 0x0138, 0x6803, 0x0000, 0x6b52,
+ 0x080c, 0x5408, 0x2f68, 0x0cb0, 0x6b52, 0x080c, 0x5408, 0x00fe,
+ 0x001e, 0x0005, 0x00e6, 0x0046, 0x0036, 0x2061, 0xbd00, 0xa005,
+ 0x1138, 0x2071, 0xb500, 0x7448, 0x7068, 0x8001, 0xa402, 0x12d8,
+ 0x2100, 0xac06, 0x0168, 0x6000, 0xa086, 0x0000, 0x0148, 0x6008,
+ 0xa206, 0x1130, 0x6018, 0xa1a0, 0x0006, 0x2424, 0xa406, 0x0140,
+ 0xace0, 0x0018, 0x2001, 0xb517, 0x2004, 0xac02, 0x1220, 0x0c40,
+ 0xa085, 0x0001, 0x0008, 0xa006, 0x003e, 0x004e, 0x00ee, 0x0005,
+ 0x00d6, 0x0006, 0x080c, 0x15f8, 0x000e, 0x090c, 0x1515, 0x6837,
+ 0x010d, 0x685e, 0x0026, 0x2010, 0x080c, 0x9c4a, 0x2001, 0x0000,
+ 0x0120, 0x2200, 0xa080, 0x0014, 0x2004, 0x002e, 0x684a, 0x6956,
+ 0x6c46, 0x684f, 0x0000, 0x2001, 0xb7be, 0x2004, 0x6852, 0xa006,
+ 0x68b2, 0x6802, 0x683a, 0x685a, 0x080c, 0x5408, 0x00de, 0x0005,
+ 0x6700, 0xa786, 0x0000, 0x0158, 0xa786, 0x0001, 0x0140, 0xa786,
+ 0x000a, 0x0128, 0xa786, 0x0009, 0x0110, 0xa085, 0x0001, 0x0005,
+ 0x00e6, 0x6018, 0x2070, 0x70a0, 0xa206, 0x00ee, 0x0005, 0x0016,
+ 0x6004, 0xa08e, 0x001e, 0x11a0, 0x8007, 0x6130, 0xa18c, 0x00ff,
+ 0xa105, 0x6032, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0005,
+ 0x2001, 0xb7b7, 0x2004, 0x6016, 0x080c, 0x6c8d, 0x080c, 0x7173,
+ 0x001e, 0x0005, 0xe000, 0xe000, 0x0005, 0x6020, 0xd0e4, 0x0158,
+ 0xd0cc, 0x0118, 0x080c, 0x9f35, 0x0030, 0x080c, 0xb33a, 0x080c,
+ 0x6aef, 0x080c, 0x861d, 0x0005, 0xa280, 0x0007, 0x2004, 0xa084,
+ 0x000f, 0x0002, 0xb163, 0xb163, 0xb163, 0xb168, 0xb163, 0xb165,
+ 0xb165, 0xb163, 0xb165, 0xa006, 0x0005, 0x00c6, 0x2260, 0x00ce,
+ 0xa085, 0x0001, 0x0005, 0xa280, 0x0007, 0x2004, 0xa084, 0x000f,
+ 0x0002, 0xb17a, 0xb17a, 0xb17a, 0xb17a, 0xb17a, 0xb17a, 0xb185,
+ 0xb17a, 0xb17a, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013, 0x2a00,
+ 0x6003, 0x0001, 0x080c, 0x6c8d, 0x0005, 0x00c6, 0x2260, 0x080c,
+ 0xb33a, 0x603f, 0x0000, 0x6020, 0xc0f4, 0xc0cc, 0x6022, 0x6037,
+ 0x0000, 0x00ce, 0x00d6, 0x2268, 0xa186, 0x0007, 0x1904, 0xb1e0,
+ 0x6810, 0xa005, 0x0138, 0xa080, 0x0013, 0x2004, 0xd0fc, 0x1110,
+ 0x00de, 0x08c0, 0x6007, 0x003a, 0x6003, 0x0001, 0x080c, 0x6c8d,
+ 0x080c, 0x7173, 0x00c6, 0x2d60, 0x6100, 0xa186, 0x0002, 0x1904,
+ 0xb269, 0x6010, 0xa005, 0x1138, 0x6000, 0xa086, 0x0007, 0x190c,
+ 0x1515, 0x0804, 0xb269, 0xa08c, 0xf000, 0x1130, 0x0028, 0x2068,
+ 0x6800, 0xa005, 0x1de0, 0x2d00, 0xa080, 0x0013, 0x2004, 0xa084,
+ 0x0003, 0xa086, 0x0002, 0x1180, 0x6010, 0x2068, 0x684c, 0xc0dc,
+ 0xc0f4, 0x684e, 0x6850, 0xc0f4, 0xc0fc, 0x6852, 0x2009, 0x0043,
+ 0x080c, 0xab56, 0x0804, 0xb269, 0x2009, 0x0041, 0x0804, 0xb263,
+ 0xa186, 0x0005, 0x15f0, 0x6810, 0xa080, 0x0013, 0x2004, 0xd0bc,
+ 0x1118, 0x00de, 0x0804, 0xb17a, 0xd0b4, 0x0128, 0xd0fc, 0x090c,
+ 0x1515, 0x0804, 0xb198, 0x6007, 0x003a, 0x6003, 0x0001, 0x080c,
+ 0x6c8d, 0x080c, 0x7173, 0x00c6, 0x2d60, 0x6100, 0xa186, 0x0002,
+ 0x0120, 0xa186, 0x0004, 0x1904, 0xb269, 0x2071, 0xb823, 0x7000,
+ 0xa086, 0x0003, 0x1128, 0x7004, 0xac06, 0x1110, 0x7003, 0x0000,
+ 0x6810, 0xa080, 0x0013, 0x200c, 0xc1f4, 0xc1dc, 0x2102, 0x8000,
+ 0x200c, 0xc1f4, 0xc1fc, 0xc1bc, 0x2102, 0x2009, 0x0042, 0x0804,
+ 0xb263, 0x0036, 0x00d6, 0x00d6, 0x080c, 0x15f8, 0x003e, 0x090c,
+ 0x1515, 0x6837, 0x010d, 0x6803, 0x0000, 0x683b, 0x0000, 0x685b,
+ 0x0000, 0x6b5e, 0x6857, 0x0045, 0x2c00, 0x6862, 0x6034, 0x6872,
+ 0x2360, 0x6020, 0xc0dd, 0x6022, 0x6018, 0xa080, 0x0028, 0x2004,
+ 0xa084, 0x00ff, 0x8007, 0x6350, 0x6b4a, 0x6846, 0x684f, 0x0000,
+ 0x6853, 0x0000, 0x6d6a, 0x6e66, 0x686f, 0x0001, 0x080c, 0x5408,
+ 0x2019, 0x0045, 0x6008, 0x2068, 0x080c, 0xace0, 0x2d00, 0x600a,
+ 0x601f, 0x0006, 0x6003, 0x0007, 0x6017, 0x0000, 0x603f, 0x0000,
+ 0x00de, 0x003e, 0x0038, 0x603f, 0x0000, 0x6003, 0x0007, 0x080c,
+ 0xab56, 0x00ce, 0x00de, 0x0005, 0xa186, 0x0013, 0x1128, 0x6004,
+ 0xa082, 0x0085, 0x2008, 0x00c2, 0xa186, 0x0027, 0x1178, 0x080c,
+ 0x7090, 0x0036, 0x00d6, 0x6010, 0x2068, 0x2019, 0x0004, 0x080c,
+ 0xb099, 0x00de, 0x003e, 0x080c, 0x7173, 0x0005, 0xa186, 0x0014,
+ 0x0d70, 0x080c, 0x8663, 0x0005, 0xb295, 0xb293, 0xb293, 0xb293,
+ 0xb293, 0xb293, 0xb295, 0x080c, 0x1515, 0x080c, 0x7090, 0x6003,
+ 0x000c, 0x080c, 0x7173, 0x0005, 0xa182, 0x008c, 0x1220, 0xa182,
+ 0x0085, 0x0208, 0x001a, 0x080c, 0x8663, 0x0005, 0xb2ad, 0xb2ad,
+ 0xb2ad, 0xb2ad, 0xb2af, 0xb2cd, 0xb2ad, 0x080c, 0x1515, 0x00d6,
+ 0x2c68, 0x080c, 0x85c7, 0x01a0, 0x6003, 0x0001, 0x6007, 0x001e,
+ 0x2009, 0xbb8e, 0x210c, 0x6136, 0x2009, 0xbb8f, 0x210c, 0x613a,
+ 0x600b, 0xffff, 0x6918, 0x611a, 0x601f, 0x0004, 0x080c, 0x6c8d,
+ 0x2d60, 0x080c, 0x861d, 0x00de, 0x0005, 0x080c, 0x861d, 0x0005,
+ 0x00e6, 0x6018, 0x2070, 0x7000, 0xd0ec, 0x00ee, 0x0005, 0x6010,
+ 0xa08c, 0xf000, 0x0904, 0xb31b, 0xa080, 0x0013, 0x200c, 0xd1ec,
+ 0x05d0, 0x2001, 0xb572, 0x2004, 0xd0ec, 0x05a8, 0x6003, 0x0002,
+ 0x6020, 0xc0e5, 0x6022, 0xd1ac, 0x0180, 0x00f6, 0x2c78, 0x080c,
+ 0x5301, 0x00fe, 0x0150, 0x2001, 0xb7b8, 0x2004, 0x603e, 0x2009,
+ 0xb572, 0x210c, 0xd1f4, 0x11e8, 0x0080, 0x2009, 0xb572, 0x210c,
+ 0xd1f4, 0x0128, 0x6020, 0xc0e4, 0x6022, 0xa006, 0x00a0, 0x2001,
+ 0xb7b8, 0x200c, 0x8103, 0xa100, 0x603e, 0x6018, 0xa088, 0x002b,
+ 0x2104, 0xa005, 0x0118, 0xa088, 0x0003, 0x0cd0, 0x2c0a, 0x600f,
+ 0x0000, 0xa085, 0x0001, 0x0005, 0x0016, 0x00c6, 0x00e6, 0x6150,
+ 0xa2f0, 0x002b, 0x2e04, 0x2060, 0x8cff, 0x0180, 0x84ff, 0x1118,
+ 0x6050, 0xa106, 0x1138, 0x600c, 0x2072, 0x080c, 0x6aef, 0x080c,
+ 0x861d, 0x0010, 0xacf0, 0x0003, 0x2e64, 0x0c70, 0x00ee, 0x00ce,
+ 0x001e, 0x0005, 0x00d6, 0x6018, 0xa0e8, 0x002b, 0x2d04, 0xa005,
+ 0x0140, 0xac06, 0x0120, 0x2d04, 0xa0e8, 0x0003, 0x0cb8, 0x600c,
+ 0x206a, 0x00de, 0x0005, 0x0026, 0x0036, 0x0156, 0x2011, 0xb528,
+ 0x2204, 0xa084, 0x00ff, 0x2019, 0xbb8e, 0x2334, 0xa636, 0x11d8,
+ 0x8318, 0x2334, 0x2204, 0xa084, 0xff00, 0xa636, 0x11a0, 0x2011,
+ 0xbb90, 0x6018, 0xa098, 0x000a, 0x20a9, 0x0004, 0x080c, 0x90da,
+ 0x1150, 0x2011, 0xbb94, 0x6018, 0xa098, 0x0006, 0x20a9, 0x0004,
+ 0x080c, 0x90da, 0x1100, 0x015e, 0x003e, 0x002e, 0x0005, 0x00e6,
+ 0x2071, 0xb500, 0x080c, 0x4bc6, 0x080c, 0x2ab8, 0x00ee, 0x0005,
+ 0x00e6, 0x6018, 0x2070, 0x7000, 0xd0fc, 0x0108, 0x0011, 0x00ee,
+ 0x0005, 0x6850, 0xc0e5, 0x6852, 0x0005, 0x00e6, 0x00c6, 0x0076,
+ 0x0066, 0x0056, 0x0046, 0x0026, 0x0016, 0x0126, 0x2091, 0x8000,
+ 0x2029, 0xb7e9, 0x252c, 0x2021, 0xb7ef, 0x2424, 0x2061, 0xbd00,
+ 0x2071, 0xb500, 0x7648, 0x7068, 0xa606, 0x0578, 0x671c, 0xa786,
+ 0x0001, 0x0118, 0xa786, 0x0008, 0x1500, 0x2500, 0xac06, 0x01e8,
+ 0x2400, 0xac06, 0x01d0, 0x080c, 0xb110, 0x01b8, 0x080c, 0xb120,
+ 0x11a0, 0x6000, 0xa086, 0x0004, 0x1120, 0x0016, 0x080c, 0x194d,
+ 0x001e, 0x080c, 0x9e47, 0x1110, 0x080c, 0x2cc2, 0x080c, 0x9e58,
+ 0x1110, 0x080c, 0x8c19, 0x080c, 0x9e1d, 0xace0, 0x0018, 0x2001,
+ 0xb517, 0x2004, 0xac02, 0x1208, 0x0858, 0x012e, 0x001e, 0x002e,
+ 0x004e, 0x005e, 0x006e, 0x007e, 0x00ce, 0x00ee, 0x0005, 0x0126,
+ 0x0006, 0x00e6, 0x0016, 0x2091, 0x8000, 0x2071, 0xb540, 0xd5a4,
+ 0x0118, 0x7034, 0x8000, 0x7036, 0xd5b4, 0x0118, 0x7030, 0x8000,
+ 0x7032, 0xd5ac, 0x0178, 0x2500, 0xa084, 0x0007, 0xa08e, 0x0003,
+ 0x0148, 0xa08e, 0x0004, 0x0130, 0xa08e, 0x0005, 0x0118, 0x2071,
+ 0xb54a, 0x04c9, 0x001e, 0x00ee, 0x000e, 0x012e, 0x0005, 0x0126,
+ 0x0006, 0x00e6, 0x0016, 0x2091, 0x8000, 0x2071, 0xb540, 0xd5a4,
+ 0x0118, 0x7034, 0x8000, 0x7036, 0xd5b4, 0x0118, 0x7030, 0x8000,
+ 0x7032, 0xd5ac, 0x0178, 0x2500, 0xa084, 0x0007, 0xa08e, 0x0003,
+ 0x0148, 0xa08e, 0x0004, 0x0130, 0xa08e, 0x0005, 0x0118, 0x2071,
+ 0xb54a, 0x0089, 0x001e, 0x00ee, 0x000e, 0x012e, 0x0005, 0x0126,
+ 0x0006, 0x00e6, 0x2091, 0x8000, 0x2071, 0xb542, 0x0021, 0x00ee,
+ 0x000e, 0x012e, 0x0005, 0x2e04, 0x8000, 0x2072, 0x1220, 0x8e70,
+ 0x2e04, 0x8000, 0x2072, 0x0005, 0x00e6, 0x2071, 0xb540, 0x0c99,
+ 0x00ee, 0x0005, 0x00e6, 0x2071, 0xb544, 0x0c69, 0x00ee, 0x0005,
+ 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000, 0x2071, 0xb540, 0x7044,
+ 0x8000, 0x7046, 0x00ee, 0x000e, 0x012e, 0x0005, 0x0001, 0x0002,
+ 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,
+ 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 0x2440
+};
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2200tp_length01 = 0xa46f;
+#else
+unsigned short risc_code_length01 = 0xa46f;
+#endif
+
diff --git a/drivers/scsi/qla2xxx/ql2300.c b/drivers/scsi/qla2xxx/ql2300.c
new file mode 100644
index 000000000000..a4988cf99304
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2300.c
@@ -0,0 +1,103 @@
+/*
+ * QLogic ISP2300 device driver for Linux 2.6.x
+ * Copyright (C) 2003 Christoph Hellwig.
+ * Copyright (C) 2003-2004 QLogic Corporation (www.qlogic.com)
+ *
+ * Released under GPL v2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "qla_def.h"
+
+static char qla_driver_name[] = "qla2300";
+
+extern unsigned char fw2300ipx_version[];
+extern unsigned char fw2300ipx_version_str[];
+extern unsigned short fw2300ipx_addr01;
+extern unsigned short fw2300ipx_code01[];
+extern unsigned short fw2300ipx_length01;
+
+static struct qla_fw_info qla_fw_tbl[] = {
+ {
+ .addressing = FW_INFO_ADDR_NORMAL,
+ .fwcode = &fw2300ipx_code01[0],
+ .fwlen = &fw2300ipx_length01,
+ .fwstart = &fw2300ipx_addr01,
+ },
+ { FW_INFO_ADDR_NOMORE, },
+};
+
+static struct qla_board_info qla_board_tbl[] = {
+ {
+ .drv_name = qla_driver_name,
+ .isp_name = "ISP2300",
+ .fw_info = qla_fw_tbl,
+ },
+ {
+ .drv_name = qla_driver_name,
+ .isp_name = "ISP2312",
+ .fw_info = qla_fw_tbl,
+ },
+};
+
+static struct pci_device_id qla2300_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP2300,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl[0],
+ },
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP2312,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl[1],
+ },
+ {0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla2300_pci_tbl);
+
+static int __devinit
+qla2300_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ return qla2x00_probe_one(pdev,
+ (struct qla_board_info *)id->driver_data);
+}
+
+static void __devexit
+qla2300_remove_one(struct pci_dev *pdev)
+{
+ qla2x00_remove_one(pdev);
+}
+
+static struct pci_driver qla2300_pci_driver = {
+ .name = "qla2300",
+ .id_table = qla2300_pci_tbl,
+ .probe = qla2300_probe_one,
+ .remove = __devexit_p(qla2300_remove_one),
+};
+
+static int __init
+qla2300_init(void)
+{
+ return pci_module_init(&qla2300_pci_driver);
+}
+
+static void __exit
+qla2300_exit(void)
+{
+ pci_unregister_driver(&qla2300_pci_driver);
+}
+
+module_init(qla2300_init);
+module_exit(qla2300_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic ISP23xx FC-SCSI Host Bus Adapter driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff --git a/drivers/scsi/qla2xxx/ql2300_fw.c b/drivers/scsi/qla2xxx/ql2300_fw.c
new file mode 100644
index 000000000000..9af06ba723df
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2300_fw.c
@@ -0,0 +1,7590 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+/*
+ * Firmware Version 3.03.08 (10:02 Nov 12, 2004)
+ */
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300ipx_version = 3*1024+3;
+#else
+unsigned short risc_code_version = 3*1024+3;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned char fw2300ipx_version_str[] = {3, 3, 8};
+#else
+unsigned char firmware_version[] = {3, 3, 8};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw2300ipx_VERSION_STRING "3.03.08"
+#else
+#define FW_VERSION_STRING "3.03.08"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300ipx_addr01 = 0x0800 ;
+#else
+unsigned short risc_code_addr01 = 0x0800 ;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300ipx_code01[] = {
+#else
+unsigned short risc_code01[] = {
+#endif
+ 0x0470, 0x0000, 0x0000, 0xeb57, 0x0000, 0x0003, 0x0003, 0x0008,
+ 0x0137, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2032, 0x3030,
+ 0x3120, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3233, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x332e, 0x3033, 0x2e30, 0x3820, 0x2020, 0x2020, 0x2400, 0x20a9,
+ 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2200, 0x20a9, 0x000f,
+ 0x2001, 0x0000, 0x400f, 0x2091, 0x2400, 0x20a9, 0x000f, 0x2001,
+ 0x0000, 0x400f, 0x2091, 0x2600, 0x20a9, 0x000f, 0x2001, 0x0000,
+ 0x400f, 0x2091, 0x2800, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f,
+ 0x2091, 0x2a00, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091,
+ 0x2c00, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2e00,
+ 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2000, 0x2001,
+ 0x0000, 0x20c1, 0x0004, 0x20c9, 0x1bff, 0x2059, 0x0000, 0x2b78,
+ 0x7883, 0x0004, 0x2089, 0x2d88, 0x2051, 0x1800, 0x2a70, 0x20e1,
+ 0x0001, 0x20e9, 0x0001, 0x2009, 0x0000, 0x080c, 0x0e51, 0x2029,
+ 0x4d00, 0x2031, 0xffff, 0x2039, 0x4cd0, 0x2021, 0x0200, 0x20e9,
+ 0x0001, 0x20a1, 0x0000, 0x20a9, 0x0800, 0x900e, 0x4104, 0x20e9,
+ 0x0001, 0x20a1, 0x1000, 0x900e, 0x2001, 0x0cc0, 0x9084, 0x0fff,
+ 0x20a8, 0x4104, 0x2001, 0x0000, 0x9086, 0x0000, 0x0120, 0x21a8,
+ 0x4104, 0x8001, 0x1de0, 0x756e, 0x7672, 0x776a, 0x7476, 0x747a,
+ 0x00e6, 0x2071, 0x1aca, 0x2472, 0x00ee, 0x20a1, 0x1cd0, 0x7170,
+ 0x810d, 0x810d, 0x810d, 0x810d, 0x918c, 0x000f, 0x2001, 0x0001,
+ 0x9112, 0x900e, 0x21a8, 0x4104, 0x8211, 0x1de0, 0x7170, 0x3400,
+ 0x8001, 0x9102, 0x0120, 0x0218, 0x20a8, 0x900e, 0x4104, 0x2009,
+ 0x1800, 0x810d, 0x810d, 0x810d, 0x810d, 0x810d, 0x918c, 0x001f,
+ 0x2001, 0x0001, 0x9112, 0x20e9, 0x0001, 0x20a1, 0x0800, 0x900e,
+ 0x20a9, 0x0800, 0x4104, 0x8211, 0x1dd8, 0x080c, 0x0f17, 0x080c,
+ 0x60bb, 0x080c, 0xaed9, 0x080c, 0x10ce, 0x080c, 0x12ed, 0x080c,
+ 0x1be2, 0x080c, 0x0d69, 0x080c, 0x1053, 0x080c, 0x3484, 0x080c,
+ 0x7738, 0x080c, 0x6a30, 0x080c, 0x87b3, 0x080c, 0x84e7, 0x080c,
+ 0x24b6, 0x080c, 0x9057, 0x080c, 0x7e03, 0x080c, 0x22ef, 0x080c,
+ 0x2423, 0x080c, 0x24ab, 0x2091, 0x3009, 0x7883, 0x0000, 0x1004,
+ 0x091f, 0x7880, 0x9086, 0x0002, 0x1190, 0x7883, 0x4000, 0x7837,
+ 0x4000, 0x7833, 0x0010, 0x0e04, 0x0913, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x2071,
+ 0x1800, 0x7003, 0x0000, 0x2071, 0x1800, 0x7000, 0x908e, 0x0003,
+ 0x1178, 0x080c, 0x4c44, 0x080c, 0x34ab, 0x080c, 0x77a9, 0x080c,
+ 0x6f61, 0x080c, 0x8896, 0x080c, 0x8510, 0x080c, 0x2cf2, 0x0c58,
+ 0x000b, 0x0c78, 0x0944, 0x0945, 0x0ae7, 0x0942, 0x0bae, 0x0d68,
+ 0x0d68, 0x0d68, 0x080c, 0x0dd5, 0x0005, 0x0126, 0x00f6, 0x2091,
+ 0x8000, 0x7000, 0x9086, 0x0001, 0x1904, 0x0aba, 0x080c, 0x576c,
+ 0x1130, 0x0026, 0x2011, 0x0080, 0x080c, 0x0edf, 0x002e, 0x080c,
+ 0x743e, 0x0150, 0x080c, 0x7461, 0x15a0, 0x2079, 0x0100, 0x7828,
+ 0x9085, 0x1800, 0x782a, 0x0468, 0x080c, 0x736a, 0x7000, 0x9086,
+ 0x0001, 0x1904, 0x0aba, 0x7098, 0x9086, 0x0028, 0x1904, 0x0aba,
+ 0x080c, 0x84d0, 0x080c, 0x84c2, 0x2001, 0x0161, 0x2003, 0x0001,
+ 0x2079, 0x0100, 0x7827, 0xffff, 0x7a28, 0x9295, 0x5e2f, 0x7a2a,
+ 0x2011, 0x72ce, 0x080c, 0x85b0, 0x2011, 0x72c1, 0x080c, 0x868a,
+ 0x2011, 0x5f16, 0x080c, 0x85b0, 0x2011, 0x8030, 0x901e, 0x7396,
+ 0x04d0, 0x080c, 0x57c3, 0x2079, 0x0100, 0x7844, 0x9005, 0x1904,
+ 0x0aba, 0x2011, 0x5f16, 0x080c, 0x85b0, 0x2011, 0x72ce, 0x080c,
+ 0x85b0, 0x2011, 0x72c1, 0x080c, 0x868a, 0x2001, 0x0265, 0x2001,
+ 0x0205, 0x2003, 0x0000, 0x7840, 0x9084, 0xfffb, 0x7842, 0x2001,
+ 0x19a5, 0x2004, 0x9005, 0x1140, 0x00c6, 0x2061, 0x0100, 0x080c,
+ 0x6063, 0x00ce, 0x0804, 0x0aba, 0x780f, 0x006b, 0x7a28, 0x080c,
+ 0x7446, 0x0118, 0x9295, 0x5e2f, 0x0010, 0x9295, 0x402f, 0x7a2a,
+ 0x2011, 0x8010, 0x73d8, 0x2001, 0x19a6, 0x2003, 0x0001, 0x080c,
+ 0x2b97, 0x080c, 0x4b7f, 0x7248, 0xc284, 0x724a, 0x2001, 0x180c,
+ 0x200c, 0xc1ac, 0xc1cc, 0x2102, 0x080c, 0xa613, 0x2011, 0x0004,
+ 0x080c, 0xcc96, 0x080c, 0x68bc, 0x080c, 0x743e, 0x1120, 0x080c,
+ 0x2bdb, 0x02e0, 0x0400, 0x080c, 0x606a, 0x0140, 0x7097, 0x0001,
+ 0x70d3, 0x0000, 0x080c, 0x5990, 0x0804, 0x0aba, 0x080c, 0x575d,
+ 0xd094, 0x0188, 0x2011, 0x180c, 0x2204, 0xc0cd, 0x2012, 0x080c,
+ 0x5761, 0xd0d4, 0x1118, 0x080c, 0x2bdb, 0x1270, 0x2011, 0x180c,
+ 0x2204, 0xc0bc, 0x00a8, 0x080c, 0x5761, 0xd0d4, 0x1db8, 0x2011,
+ 0x180c, 0x2204, 0xc0bd, 0x0060, 0x2011, 0x180c, 0x2204, 0xc0bd,
+ 0x2012, 0x080c, 0x6a04, 0x1128, 0xd0a4, 0x0118, 0x2204, 0xc0fd,
+ 0x2012, 0x080c, 0x69ca, 0x0120, 0x7a0c, 0xc2b4, 0x7a0e, 0x00a8,
+ 0x707f, 0x0000, 0x080c, 0x743e, 0x1130, 0x70b0, 0x9005, 0x1168,
+ 0x080c, 0xd0d9, 0x0050, 0x080c, 0xd0d9, 0x70dc, 0xd09c, 0x1128,
+ 0x70b0, 0x9005, 0x0110, 0x080c, 0x6040, 0x70e7, 0x0000, 0x70e3,
+ 0x0000, 0x70a7, 0x0000, 0x080c, 0x2be3, 0x0228, 0x2011, 0x0101,
+ 0x2204, 0xc0c4, 0x2012, 0x72dc, 0x080c, 0x743e, 0x1178, 0x9016,
+ 0x0016, 0x080c, 0x2994, 0x2019, 0x196c, 0x211a, 0x001e, 0x705f,
+ 0xffff, 0x7063, 0x00ef, 0x7083, 0x0000, 0x0020, 0x2019, 0x196c,
+ 0x201b, 0x0000, 0x2079, 0x1847, 0x7804, 0xd0ac, 0x0108, 0xc295,
+ 0x72de, 0x080c, 0x743e, 0x0118, 0x9296, 0x0004, 0x0548, 0x2011,
+ 0x0001, 0x080c, 0xcc96, 0x70ab, 0x0000, 0x70af, 0xffff, 0x7003,
+ 0x0002, 0x2079, 0x0100, 0x7827, 0x0003, 0x7828, 0x9085, 0x0003,
+ 0x782a, 0x00fe, 0x080c, 0x2ff5, 0x2011, 0x0005, 0x080c, 0xa722,
+ 0x080c, 0x9763, 0x080c, 0x743e, 0x0148, 0x00c6, 0x2061, 0x0100,
+ 0x0016, 0x080c, 0x2994, 0x61e2, 0x001e, 0x00ce, 0x012e, 0x0420,
+ 0x70ab, 0x0000, 0x70af, 0xffff, 0x7003, 0x0002, 0x00f6, 0x2079,
+ 0x0100, 0x7827, 0x0003, 0x7828, 0x9085, 0x0003, 0x782a, 0x00fe,
+ 0x2011, 0x0005, 0x080c, 0xa722, 0x080c, 0x9763, 0x080c, 0x743e,
+ 0x0148, 0x00c6, 0x2061, 0x0100, 0x0016, 0x080c, 0x2994, 0x61e2,
+ 0x001e, 0x00ce, 0x00fe, 0x012e, 0x0005, 0x00c6, 0x00b6, 0x080c,
+ 0x743e, 0x1118, 0x20a9, 0x0800, 0x0010, 0x20a9, 0x0782, 0x080c,
+ 0x743e, 0x1110, 0x900e, 0x0010, 0x2009, 0x007e, 0x86ff, 0x0138,
+ 0x9180, 0x1000, 0x2004, 0x905d, 0x0110, 0xb800, 0xd0bc, 0x090c,
+ 0x331a, 0x8108, 0x1f04, 0x0ace, 0x707f, 0x0000, 0x7080, 0x9084,
+ 0x00ff, 0x7082, 0x70b3, 0x0000, 0x00be, 0x00ce, 0x0005, 0x00b6,
+ 0x0126, 0x2091, 0x8000, 0x7000, 0x9086, 0x0002, 0x1904, 0x0bab,
+ 0x70ac, 0x9086, 0xffff, 0x0130, 0x080c, 0x2ff5, 0x080c, 0x9763,
+ 0x0804, 0x0bab, 0x70dc, 0xd0ac, 0x1110, 0xd09c, 0x0558, 0xd084,
+ 0x0548, 0x0006, 0x2001, 0x0103, 0x2003, 0x002b, 0x000e, 0xd08c,
+ 0x0508, 0x080c, 0x337d, 0x11d0, 0x70e0, 0x9086, 0xffff, 0x01b0,
+ 0x080c, 0x318a, 0x080c, 0x9763, 0x70dc, 0xd094, 0x1904, 0x0bab,
+ 0x2011, 0x0001, 0x080c, 0xd388, 0x0110, 0x2011, 0x0003, 0x901e,
+ 0x080c, 0x31c4, 0x080c, 0x9763, 0x0804, 0x0bab, 0x70e4, 0x9005,
+ 0x1904, 0x0bab, 0x70a8, 0x9005, 0x1904, 0x0bab, 0x70dc, 0xd0a4,
+ 0x0118, 0xd0b4, 0x0904, 0x0bab, 0x080c, 0x69ca, 0x1904, 0x0bab,
+ 0x080c, 0x6a1d, 0x1904, 0x0bab, 0x080c, 0x6a04, 0x01c0, 0x0156,
+ 0x00c6, 0x20a9, 0x007f, 0x900e, 0x0016, 0x080c, 0x6699, 0x1118,
+ 0xb800, 0xd0ec, 0x1138, 0x001e, 0x8108, 0x1f04, 0x0b44, 0x00ce,
+ 0x015e, 0x0028, 0x001e, 0x00ce, 0x015e, 0x0804, 0x0bab, 0x0006,
+ 0x2001, 0x0103, 0x2003, 0x002b, 0x000e, 0x2011, 0x19b2, 0x080c,
+ 0x0f87, 0x2011, 0x19cc, 0x080c, 0x0f87, 0x7030, 0xc08c, 0x7032,
+ 0x7003, 0x0003, 0x70af, 0xffff, 0x080c, 0x576c, 0x1130, 0x0026,
+ 0x2011, 0x0040, 0x080c, 0x0edf, 0x002e, 0x9006, 0x080c, 0x2828,
+ 0x080c, 0x337d, 0x0118, 0x080c, 0x4d1c, 0x0050, 0x0036, 0x0046,
+ 0x2019, 0xffff, 0x2021, 0x0006, 0x080c, 0x4d36, 0x004e, 0x003e,
+ 0x00f6, 0x2079, 0x0100, 0x080c, 0x7461, 0x0150, 0x080c, 0x743e,
+ 0x7828, 0x0118, 0x9084, 0xe1ff, 0x0010, 0x9084, 0xffdf, 0x782a,
+ 0x00fe, 0x2001, 0x19e7, 0x2004, 0x9086, 0x0005, 0x1120, 0x2011,
+ 0x0000, 0x080c, 0xa722, 0x2011, 0x0000, 0x080c, 0xa72c, 0x080c,
+ 0x9763, 0x080c, 0x9891, 0x012e, 0x00be, 0x0005, 0x0016, 0x0046,
+ 0x00f6, 0x0126, 0x2091, 0x8000, 0x2079, 0x0100, 0x7904, 0x918c,
+ 0xfffd, 0x7906, 0x2009, 0x00f7, 0x080c, 0x6029, 0x7940, 0x918c,
+ 0x0010, 0x7942, 0x7924, 0xd1b4, 0x0110, 0x7827, 0x0040, 0xd19c,
+ 0x0110, 0x7827, 0x0008, 0x0006, 0x0036, 0x0156, 0x7954, 0xd1ac,
+ 0x1904, 0x0c3b, 0x2001, 0x19a6, 0x2004, 0x9005, 0x1518, 0x080c,
+ 0x2c5e, 0x1148, 0x2001, 0x0001, 0x080c, 0x2bc6, 0x2001, 0x0001,
+ 0x080c, 0x2ba9, 0x00b8, 0x080c, 0x2c66, 0x1138, 0x9006, 0x080c,
+ 0x2bc6, 0x9006, 0x080c, 0x2ba9, 0x0068, 0x080c, 0x2c6e, 0x1d50,
+ 0x2001, 0x1997, 0x2004, 0xd0fc, 0x0108, 0x0020, 0x080c, 0x29c0,
+ 0x0804, 0x0d1a, 0x080c, 0x744f, 0x0148, 0x080c, 0x7461, 0x1118,
+ 0x080c, 0x7733, 0x0050, 0x080c, 0x7446, 0x0dd0, 0x080c, 0x772e,
+ 0x080c, 0x7724, 0x080c, 0x736a, 0x0058, 0x080c, 0x743e, 0x0140,
+ 0x2009, 0x00f8, 0x080c, 0x6029, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x20a9, 0x09c4, 0x7820, 0xd09c, 0x1138, 0x080c, 0x743e, 0x0138,
+ 0x7824, 0xd0ac, 0x1904, 0x0d1f, 0x1f04, 0x0c1a, 0x0070, 0x7824,
+ 0x080c, 0x7458, 0x0118, 0xd0ac, 0x1904, 0x0d1f, 0x9084, 0x1800,
+ 0x0d98, 0x7003, 0x0001, 0x0804, 0x0d1f, 0x2001, 0x0001, 0x080c,
+ 0x2828, 0x0804, 0x0d32, 0x2001, 0x19a6, 0x2004, 0x9005, 0x1518,
+ 0x080c, 0x2c5e, 0x1148, 0x2001, 0x0001, 0x080c, 0x2bc6, 0x2001,
+ 0x0001, 0x080c, 0x2ba9, 0x00b8, 0x080c, 0x2c66, 0x1138, 0x9006,
+ 0x080c, 0x2bc6, 0x9006, 0x080c, 0x2ba9, 0x0068, 0x080c, 0x2c6e,
+ 0x1d50, 0x2001, 0x1997, 0x2004, 0xd0fc, 0x0108, 0x0020, 0x080c,
+ 0x29c0, 0x0804, 0x0d1a, 0x7850, 0x9085, 0x0040, 0x7852, 0x7938,
+ 0x7850, 0x9084, 0xfbcf, 0x7852, 0x080c, 0x2c76, 0x9085, 0x2000,
+ 0x7852, 0x793a, 0x20a9, 0x0046, 0x1d04, 0x0c74, 0x080c, 0x866a,
+ 0x1f04, 0x0c74, 0x7850, 0x9085, 0x0400, 0x9084, 0xdfbf, 0x7852,
+ 0x793a, 0x080c, 0x744f, 0x0148, 0x080c, 0x7461, 0x1118, 0x080c,
+ 0x7733, 0x0050, 0x080c, 0x7446, 0x0dd0, 0x080c, 0x772e, 0x080c,
+ 0x7724, 0x080c, 0x736a, 0x0020, 0x2009, 0x00f8, 0x080c, 0x6029,
+ 0x20a9, 0x0028, 0xa001, 0x1f04, 0x0c9a, 0x7850, 0x9085, 0x1400,
+ 0x7852, 0x080c, 0x743e, 0x0120, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x2021, 0xe678, 0x2019, 0xea60, 0x0d0c, 0x866a, 0x7820, 0xd09c,
+ 0x1580, 0x080c, 0x743e, 0x0904, 0x0cff, 0x7824, 0xd0ac, 0x1904,
+ 0x0d1f, 0x080c, 0x7461, 0x1528, 0x0046, 0x2021, 0x0320, 0x8421,
+ 0x1df0, 0x004e, 0x7827, 0x1800, 0x080c, 0x2c76, 0x7824, 0x9084,
+ 0x1800, 0x1160, 0x9484, 0x0fff, 0x1138, 0x2001, 0x1810, 0x2004,
+ 0xd0fc, 0x0110, 0x080c, 0x0d45, 0x8421, 0x1158, 0x1d04, 0x0cda,
+ 0x080c, 0x866a, 0x080c, 0x772e, 0x080c, 0x7724, 0x7003, 0x0001,
+ 0x04f0, 0x8319, 0x1948, 0x1d04, 0x0ce7, 0x080c, 0x866a, 0x2009,
+ 0x199a, 0x2104, 0x9005, 0x0118, 0x8001, 0x200a, 0x1178, 0x200b,
+ 0x000a, 0x7827, 0x0048, 0x20a9, 0x0002, 0x080c, 0x2c57, 0x7924,
+ 0x080c, 0x2c76, 0xd19c, 0x0110, 0x080c, 0x2b97, 0x00d8, 0x080c,
+ 0x744f, 0x1140, 0x94a2, 0x03e8, 0x1128, 0x080c, 0x7416, 0x7003,
+ 0x0001, 0x00a8, 0x7827, 0x1800, 0x080c, 0x2c76, 0x7824, 0x080c,
+ 0x7458, 0x0110, 0xd0ac, 0x1158, 0x9084, 0x1800, 0x0950, 0x7003,
+ 0x0001, 0x0028, 0x2001, 0x0001, 0x080c, 0x2828, 0x0078, 0x2009,
+ 0x180c, 0x210c, 0xd19c, 0x1120, 0x7904, 0x918d, 0x0002, 0x7906,
+ 0x7827, 0x0048, 0x7828, 0x9085, 0x0028, 0x782a, 0x7850, 0x9085,
+ 0x0400, 0x7852, 0x2001, 0x19a6, 0x2003, 0x0000, 0x9006, 0x78f2,
+ 0x015e, 0x003e, 0x000e, 0x080c, 0x576c, 0x1110, 0x080c, 0x0e62,
+ 0x012e, 0x00fe, 0x004e, 0x001e, 0x0005, 0x0006, 0x0016, 0x0036,
+ 0x0046, 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x0156, 0x0069,
+ 0x0d0c, 0x866a, 0x015e, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be,
+ 0x004e, 0x003e, 0x001e, 0x000e, 0x0005, 0x00e6, 0x2071, 0x189e,
+ 0x7004, 0x9086, 0x0001, 0x1110, 0x080c, 0x34ab, 0x00ee, 0x0005,
+ 0x0005, 0x2a70, 0x2061, 0x19aa, 0x2063, 0x0003, 0x6007, 0x0003,
+ 0x600b, 0x0008, 0x600f, 0x0137, 0x2001, 0x197b, 0x900e, 0x2102,
+ 0x7196, 0x2001, 0x0100, 0x2004, 0x9082, 0x0002, 0x0218, 0x705f,
+ 0xffff, 0x0008, 0x715e, 0x7067, 0xffff, 0x717e, 0x7182, 0x080c,
+ 0xd0d9, 0x2061, 0x196b, 0x6003, 0x0909, 0x6106, 0x600b, 0x8800,
+ 0x600f, 0x0200, 0x6013, 0x00ff, 0x6017, 0x001f, 0x611a, 0x601f,
+ 0x07d0, 0x2061, 0x1973, 0x6003, 0x8000, 0x6106, 0x610a, 0x600f,
+ 0x0200, 0x6013, 0x00ff, 0x6116, 0x601b, 0x0001, 0x611e, 0x2061,
+ 0x1988, 0x6003, 0x514c, 0x6007, 0x4f47, 0x600b, 0x4943, 0x600f,
+ 0x2020, 0x2001, 0x182c, 0x2102, 0x0005, 0x9016, 0x080c, 0x6699,
+ 0x1178, 0xb804, 0x90c4, 0x00ff, 0x98c6, 0x0006, 0x0128, 0x90c4,
+ 0xff00, 0x98c6, 0x0600, 0x1120, 0x9186, 0x0080, 0x0108, 0x8210,
+ 0x8108, 0x9186, 0x0800, 0x1d50, 0x2208, 0x0005, 0x2091, 0x8000,
+ 0x2079, 0x0000, 0x000e, 0x00f6, 0x0010, 0x2091, 0x8000, 0x0e04,
+ 0x0dd7, 0x0006, 0x0016, 0x2001, 0x8002, 0x0006, 0x2079, 0x0000,
+ 0x000e, 0x7882, 0x7836, 0x001e, 0x798e, 0x000e, 0x788a, 0x000e,
+ 0x7886, 0x3900, 0x789a, 0x7833, 0x0012, 0x2091, 0x5000, 0x0156,
+ 0x00d6, 0x0036, 0x0026, 0x2079, 0x0300, 0x2069, 0x1aa4, 0x7a08,
+ 0x226a, 0x2069, 0x1aa5, 0x7a18, 0x226a, 0x8d68, 0x7a1c, 0x226a,
+ 0x782c, 0x2019, 0x1ab2, 0x201a, 0x2019, 0x1ab5, 0x9016, 0x7808,
+ 0xd09c, 0x0168, 0x7820, 0x201a, 0x8210, 0x8318, 0x9386, 0x1aca,
+ 0x0108, 0x0ca8, 0x7808, 0xd09c, 0x0110, 0x2011, 0xdead, 0x2019,
+ 0x1ab3, 0x782c, 0x201a, 0x8318, 0x221a, 0x7803, 0x0000, 0x2069,
+ 0x1a84, 0x901e, 0x20a9, 0x0020, 0x7b26, 0x7a28, 0x226a, 0x8d68,
+ 0x8318, 0x1f04, 0x0e24, 0x002e, 0x003e, 0x00de, 0x015e, 0x2079,
+ 0x1800, 0x7803, 0x0005, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004,
+ 0xd084, 0x0180, 0x2001, 0x1a18, 0x2004, 0x9005, 0x0128, 0x2001,
+ 0x008b, 0x2004, 0xd0fc, 0x0dd8, 0x2001, 0x008a, 0x2003, 0x0002,
+ 0x2003, 0x1001, 0x080c, 0x576c, 0x1110, 0x080c, 0x0e99, 0x0cd0,
+ 0x0005, 0x918c, 0x03ff, 0x2001, 0x0003, 0x2004, 0x9084, 0x0600,
+ 0x1118, 0x918d, 0x2800, 0x0010, 0x918d, 0x2000, 0x2001, 0x017f,
+ 0x2102, 0x0005, 0x00f6, 0x0006, 0x2079, 0x1827, 0x2f04, 0x8000,
+ 0x207a, 0x080c, 0x2c6e, 0x1150, 0x0006, 0x2001, 0x1997, 0x2004,
+ 0xd0fc, 0x000e, 0x1118, 0x9082, 0x7530, 0x0010, 0x9082, 0x000f,
+ 0x0258, 0x9006, 0x207a, 0x2079, 0x182a, 0x2f04, 0x9084, 0x0001,
+ 0x9086, 0x0001, 0x207a, 0x0090, 0x2079, 0x182a, 0x2f7c, 0x8fff,
+ 0x1138, 0x0026, 0x2011, 0x0080, 0x080c, 0x0edf, 0x002e, 0x0030,
+ 0x0026, 0x2011, 0x0000, 0x080c, 0x0edf, 0x002e, 0x000e, 0x00fe,
+ 0x0005, 0x0026, 0x0126, 0x2011, 0x0080, 0x080c, 0x0edf, 0x20a9,
+ 0x0fff, 0x080c, 0x0f00, 0x2011, 0x0040, 0x04c9, 0x20a9, 0x0fff,
+ 0x080c, 0x0f00, 0x0c80, 0x7038, 0xd0b4, 0x1128, 0x0026, 0x2011,
+ 0x0040, 0x0469, 0x002e, 0x0005, 0x7038, 0xd0b4, 0x1128, 0x0026,
+ 0x2011, 0x0080, 0x0421, 0x002e, 0x0005, 0x0026, 0x70ef, 0x0000,
+ 0x0459, 0x1148, 0x080c, 0x2c6e, 0x1118, 0x2011, 0x8484, 0x0058,
+ 0x2011, 0x8282, 0x0040, 0x080c, 0x2c6e, 0x1118, 0x2011, 0xcdc5,
+ 0x0010, 0x2011, 0xcac2, 0x00e9, 0x002e, 0x0005, 0xd0b4, 0x0130,
+ 0x0006, 0x3b00, 0x9084, 0xff3f, 0x20d8, 0x000e, 0x0005, 0x0016,
+ 0x3b08, 0x3a00, 0x9104, 0x918d, 0x00c0, 0x21d8, 0x9084, 0xff3f,
+ 0x9205, 0x20d0, 0x001e, 0x0005, 0x2001, 0x183a, 0x2004, 0xd0dc,
+ 0x0005, 0x9e86, 0x1800, 0x190c, 0x0dd5, 0x70e8, 0xd0e4, 0x0108,
+ 0xc2e5, 0x72ea, 0xd0e4, 0x1118, 0x9294, 0x00c0, 0x0c01, 0x0005,
+ 0x1d04, 0x0f00, 0x2091, 0x6000, 0x1f04, 0x0f00, 0x0005, 0x890e,
+ 0x810e, 0x810f, 0x9194, 0x003f, 0x918c, 0xffc0, 0x0005, 0x0006,
+ 0x2200, 0x914d, 0x894f, 0x894d, 0x894d, 0x000e, 0x0005, 0x01d6,
+ 0x0146, 0x0036, 0x0096, 0x2061, 0x188d, 0x600b, 0x0000, 0x600f,
+ 0x0000, 0x6003, 0x0000, 0x6007, 0x0000, 0x2009, 0xffc0, 0x2105,
+ 0x0006, 0x2001, 0xaaaa, 0x200f, 0x2019, 0x5555, 0x9016, 0x2049,
+ 0x0bff, 0xab02, 0xa001, 0xa001, 0xa800, 0x9306, 0x1138, 0x2105,
+ 0x9306, 0x0120, 0x8210, 0x99c8, 0x0400, 0x0c98, 0x000e, 0x200f,
+ 0x2001, 0x189d, 0x928a, 0x000e, 0x1638, 0x928a, 0x0006, 0x2011,
+ 0x0006, 0x1210, 0x2011, 0x0000, 0x2202, 0x9006, 0x2008, 0x82ff,
+ 0x01b0, 0x8200, 0x600a, 0x600f, 0xffff, 0x6003, 0x0002, 0x6007,
+ 0x0000, 0x0026, 0x2019, 0x0010, 0x9280, 0x0001, 0x20e8, 0x21a0,
+ 0x21a8, 0x4104, 0x8319, 0x1de0, 0x8211, 0x1da0, 0x002e, 0x009e,
+ 0x003e, 0x014e, 0x01de, 0x0005, 0x2011, 0x000e, 0x08e8, 0x0016,
+ 0x0026, 0x0096, 0x3348, 0x080c, 0x0f07, 0x2100, 0x9300, 0x2098,
+ 0x22e0, 0x009e, 0x002e, 0x001e, 0x0036, 0x3518, 0x20a9, 0x0001,
+ 0x4002, 0x8007, 0x4004, 0x8319, 0x1dd8, 0x003e, 0x0005, 0x20e9,
+ 0x0001, 0x71b8, 0x81ff, 0x11c0, 0x9006, 0x2009, 0x0200, 0x20a9,
+ 0x0002, 0x9298, 0x0018, 0x23a0, 0x4001, 0x2009, 0x0700, 0x20a9,
+ 0x0002, 0x9298, 0x0008, 0x23a0, 0x4001, 0x707c, 0x8007, 0x7180,
+ 0x810f, 0x20a9, 0x0002, 0x4001, 0x9298, 0x000c, 0x23a0, 0x900e,
+ 0x080c, 0x0db5, 0x2001, 0x0000, 0x810f, 0x20a9, 0x0002, 0x4001,
+ 0x0005, 0x89ff, 0x0140, 0xa804, 0xa807, 0x0000, 0x0006, 0x080c,
+ 0x1031, 0x009e, 0x0cb0, 0x0005, 0x00e6, 0x2071, 0x1800, 0x080c,
+ 0x10aa, 0x090c, 0x0dd5, 0x00ee, 0x0005, 0x0086, 0x00e6, 0x0006,
+ 0x0026, 0x0036, 0x0126, 0x2091, 0x8000, 0x00c9, 0x2071, 0x1800,
+ 0x73c0, 0x702c, 0x9016, 0x9045, 0x0158, 0x8210, 0x9906, 0x090c,
+ 0x0dd5, 0x2300, 0x9202, 0x0120, 0x1a0c, 0x0dd5, 0xa000, 0x0c98,
+ 0x012e, 0x003e, 0x002e, 0x000e, 0x00ee, 0x008e, 0x0005, 0x0086,
+ 0x00e6, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x1910, 0x7010,
+ 0x9005, 0x0140, 0x7018, 0x9045, 0x0128, 0x9906, 0x090c, 0x0dd5,
+ 0xa000, 0x0cc8, 0x012e, 0x000e, 0x00ee, 0x008e, 0x0005, 0x00e6,
+ 0x2071, 0x1800, 0x0126, 0x2091, 0x8000, 0x70c0, 0x8001, 0x0270,
+ 0x70c2, 0x702c, 0x2048, 0x9085, 0x0001, 0xa800, 0x702e, 0xa803,
+ 0x0000, 0xa807, 0x0000, 0x012e, 0x00ee, 0x0005, 0x904e, 0x0cd8,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x1800, 0x70c0, 0x90ca,
+ 0x0020, 0x0268, 0x8001, 0x70c2, 0x702c, 0x2048, 0xa800, 0x702e,
+ 0xa803, 0x0000, 0xa807, 0x0000, 0x012e, 0x00ee, 0x0005, 0x904e,
+ 0x0cd8, 0x00e6, 0x0126, 0x2091, 0x8000, 0x0016, 0x890e, 0x810e,
+ 0x810f, 0x9184, 0x003f, 0xa862, 0x9184, 0xffc0, 0xa85e, 0x001e,
+ 0x0020, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x1800, 0x702c,
+ 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c, 0x84c2,
+ 0x012e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9026, 0x2009, 0x0000,
+ 0x2049, 0x0400, 0x2900, 0x702e, 0x8940, 0x2800, 0xa802, 0xa95e,
+ 0xa863, 0x0001, 0x8420, 0x9886, 0x0440, 0x0120, 0x2848, 0x9188,
+ 0x0040, 0x0c90, 0x2071, 0x188d, 0x7000, 0x9005, 0x11a0, 0x2001,
+ 0x0534, 0xa802, 0x2048, 0x2009, 0x4d00, 0x8940, 0x2800, 0xa802,
+ 0xa95e, 0xa863, 0x0001, 0x8420, 0x9886, 0x0800, 0x0120, 0x2848,
+ 0x9188, 0x0040, 0x0c90, 0x2071, 0x188d, 0x7104, 0x7200, 0x82ff,
+ 0x01d0, 0x7308, 0x8318, 0x831f, 0x831b, 0x831b, 0x7312, 0x8319,
+ 0x2001, 0x0800, 0xa802, 0x2048, 0x8900, 0xa802, 0x2040, 0xa95e,
+ 0xaa62, 0x8420, 0x2300, 0x9906, 0x0130, 0x2848, 0x9188, 0x0040,
+ 0x9291, 0x0000, 0x0c88, 0xa803, 0x0000, 0x2071, 0x1800, 0x74be,
+ 0x74c2, 0x0005, 0x00e6, 0x0016, 0x9984, 0xfc00, 0x01e8, 0x908c,
+ 0xf800, 0x1168, 0x9982, 0x0400, 0x02b8, 0x9982, 0x0440, 0x0278,
+ 0x9982, 0x0534, 0x0288, 0x9982, 0x0800, 0x1270, 0x0040, 0x9982,
+ 0x0800, 0x0250, 0x2071, 0x188d, 0x7010, 0x9902, 0x1228, 0x9085,
+ 0x0001, 0x001e, 0x00ee, 0x0005, 0x9006, 0x0cd8, 0x00e6, 0x2071,
+ 0x1a17, 0x7007, 0x0000, 0x9006, 0x701e, 0x7022, 0x7002, 0x2071,
+ 0x0000, 0x7010, 0x9085, 0x8044, 0x7012, 0x2071, 0x0080, 0x9006,
+ 0x20a9, 0x0040, 0x7022, 0x1f04, 0x10e2, 0x702b, 0x0020, 0x00ee,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x00e6, 0xa06f, 0x0000, 0x2071,
+ 0x1a17, 0x701c, 0x9088, 0x1a21, 0x280a, 0x8000, 0x9084, 0x003f,
+ 0x701e, 0x7120, 0x9106, 0x090c, 0x0dd5, 0x7004, 0x9005, 0x1128,
+ 0x00f6, 0x2079, 0x0080, 0x00a9, 0x00fe, 0x00ee, 0x012e, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x00e6, 0x2071, 0x1a17, 0x7004, 0x9005,
+ 0x1128, 0x00f6, 0x2079, 0x0080, 0x0021, 0x00fe, 0x00ee, 0x012e,
+ 0x0005, 0x7004, 0x9086, 0x0000, 0x1110, 0x7007, 0x0006, 0x7000,
+ 0x0002, 0x112b, 0x12ae, 0x1129, 0x1129, 0x12a2, 0x12a2, 0x12a2,
+ 0x12a2, 0x080c, 0x0dd5, 0x701c, 0x7120, 0x9106, 0x1148, 0x792c,
+ 0x9184, 0x0001, 0x1120, 0xd1fc, 0x1110, 0x7007, 0x0000, 0x0005,
+ 0x0096, 0x9180, 0x1a21, 0x2004, 0x700a, 0x2048, 0x8108, 0x918c,
+ 0x003f, 0x7122, 0x782b, 0x0026, 0xa88c, 0x7802, 0xa890, 0x7806,
+ 0xa894, 0x780a, 0xa898, 0x780e, 0xa878, 0x700e, 0xa870, 0x7016,
+ 0xa874, 0x701a, 0xa868, 0x009e, 0xd084, 0x0120, 0x7007, 0x0001,
+ 0x0029, 0x0005, 0x7007, 0x0002, 0x00b1, 0x0005, 0x0016, 0x0026,
+ 0x710c, 0x2011, 0x0040, 0x9182, 0x0040, 0x1210, 0x2110, 0x9006,
+ 0x700e, 0x7212, 0x8203, 0x7812, 0x782b, 0x0020, 0x782b, 0x0041,
+ 0x002e, 0x001e, 0x0005, 0x0016, 0x0026, 0x0136, 0x0146, 0x0156,
+ 0x7014, 0x20e0, 0x7018, 0x2098, 0x20e9, 0x0000, 0x20a1, 0x0088,
+ 0x782b, 0x0026, 0x710c, 0x2011, 0x0040, 0x9182, 0x0040, 0x1210,
+ 0x2110, 0x9006, 0x700e, 0x22a8, 0x4006, 0x8203, 0x7812, 0x782b,
+ 0x0020, 0x3300, 0x701a, 0x782b, 0x0001, 0x015e, 0x014e, 0x013e,
+ 0x002e, 0x001e, 0x0005, 0x2009, 0x1a17, 0x2104, 0xc095, 0x200a,
+ 0x080c, 0x1108, 0x0005, 0x0016, 0x00e6, 0x2071, 0x1a17, 0x00f6,
+ 0x2079, 0x0080, 0x792c, 0xd1bc, 0x190c, 0x0dce, 0x782b, 0x0002,
+ 0xd1fc, 0x0120, 0x918c, 0x0700, 0x7004, 0x0023, 0x00fe, 0x00ee,
+ 0x001e, 0x0005, 0x1119, 0x11c1, 0x11f5, 0x12cd, 0x0dd5, 0x12e8,
+ 0x0dd5, 0x918c, 0x0700, 0x1550, 0x0136, 0x0146, 0x0156, 0x7014,
+ 0x20e8, 0x7018, 0x20a0, 0x20e1, 0x0000, 0x2099, 0x0088, 0x782b,
+ 0x0040, 0x7010, 0x20a8, 0x4005, 0x3400, 0x701a, 0x015e, 0x014e,
+ 0x013e, 0x700c, 0x9005, 0x0578, 0x7800, 0x7802, 0x7804, 0x7806,
+ 0x080c, 0x115e, 0x0005, 0x7008, 0x0096, 0x2048, 0xa86f, 0x0100,
+ 0x009e, 0x7007, 0x0000, 0x080c, 0x1119, 0x0005, 0x7008, 0x0096,
+ 0x2048, 0xa86f, 0x0200, 0x009e, 0x0ca0, 0x918c, 0x0700, 0x1150,
+ 0x700c, 0x9005, 0x0180, 0x7800, 0x7802, 0x7804, 0x7806, 0x080c,
+ 0x1173, 0x0005, 0x7008, 0x0096, 0x2048, 0xa86f, 0x0200, 0x009e,
+ 0x7007, 0x0000, 0x0080, 0x0096, 0x7008, 0x2048, 0x7800, 0xa88e,
+ 0x7804, 0xa892, 0x7808, 0xa896, 0x780c, 0xa89a, 0xa86f, 0x0100,
+ 0x009e, 0x7007, 0x0000, 0x0096, 0x00d6, 0x7008, 0x2048, 0x2001,
+ 0x18b9, 0x2004, 0x9906, 0x1128, 0xa89c, 0x080f, 0x00de, 0x009e,
+ 0x00a0, 0x00de, 0x009e, 0x0096, 0x00d6, 0x7008, 0x2048, 0x0081,
+ 0x0150, 0xa89c, 0x0086, 0x2940, 0x080f, 0x008e, 0x00de, 0x009e,
+ 0x080c, 0x1108, 0x0005, 0x00de, 0x009e, 0x080c, 0x1108, 0x0005,
+ 0xa8a8, 0xd08c, 0x0005, 0x0096, 0xa0a0, 0x904d, 0x090c, 0x0dd5,
+ 0xa06c, 0x908e, 0x0100, 0x0130, 0xa87b, 0x0030, 0xa883, 0x0000,
+ 0xa897, 0x4002, 0x080c, 0x6d0b, 0xa09f, 0x0000, 0xa0a3, 0x0000,
+ 0x2848, 0x080c, 0x1031, 0x009e, 0x0005, 0x00a6, 0xa0a0, 0x904d,
+ 0x090c, 0x0dd5, 0xa06c, 0x908e, 0x0100, 0x0128, 0xa87b, 0x0001,
+ 0xa883, 0x0000, 0x00c0, 0xa80c, 0x2050, 0xb004, 0x9005, 0x0198,
+ 0xa80e, 0x2050, 0x8006, 0x8006, 0x8007, 0x908c, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x0002, 0xa076, 0xa172, 0xb000, 0xa07a, 0x2810,
+ 0x080c, 0x10e9, 0x00e8, 0xa97c, 0xa894, 0x0016, 0x0006, 0x080c,
+ 0x6d0b, 0x000e, 0x001e, 0xd1fc, 0x1138, 0xd1f4, 0x0128, 0x00c6,
+ 0x2060, 0x080c, 0xaf43, 0x00ce, 0x7008, 0x2048, 0xa89f, 0x0000,
+ 0xa8a3, 0x0000, 0x080c, 0x1031, 0x7007, 0x0000, 0x080c, 0x1108,
+ 0x00ae, 0x0005, 0x0126, 0x2091, 0x8000, 0x782b, 0x1001, 0x7007,
+ 0x0005, 0x7000, 0xc094, 0x7002, 0x012e, 0x0005, 0x0096, 0x2001,
+ 0x192e, 0x204c, 0xa87c, 0x7812, 0xa88c, 0x7802, 0xa890, 0x7806,
+ 0xa894, 0x780a, 0xa898, 0x780e, 0x782b, 0x0020, 0x0126, 0x2091,
+ 0x8000, 0x782b, 0x0041, 0x7007, 0x0003, 0x7000, 0xc084, 0x7002,
+ 0x2900, 0x700a, 0x012e, 0x009e, 0x0005, 0x20e1, 0x0000, 0x2099,
+ 0x0088, 0x782b, 0x0040, 0x0096, 0x2001, 0x192e, 0x204c, 0xaa7c,
+ 0x009e, 0x080c, 0x8ad0, 0x2009, 0x188c, 0x2104, 0x9084, 0xfffc,
+ 0x200a, 0x080c, 0x8939, 0x7007, 0x0000, 0x080c, 0x1119, 0x0005,
+ 0x7007, 0x0000, 0x080c, 0x1119, 0x0005, 0x0126, 0x2091, 0x2200,
+ 0x2079, 0x0300, 0x2071, 0x1a61, 0x7003, 0x0000, 0x78bf, 0x00f6,
+ 0x781b, 0x4800, 0x00c1, 0x7803, 0x0003, 0x780f, 0x0000, 0x20a9,
+ 0x03d0, 0x2061, 0xeba8, 0x2c0d, 0x7912, 0xe104, 0x9ce0, 0x0002,
+ 0x7916, 0x1f04, 0x1303, 0x7807, 0x0007, 0x7803, 0x0000, 0x7803,
+ 0x0001, 0x012e, 0x0005, 0x00c6, 0x7803, 0x0000, 0x7808, 0xd09c,
+ 0x0120, 0x7820, 0x080c, 0x1362, 0x0cc8, 0x2001, 0x1a62, 0x2003,
+ 0x0000, 0x78ab, 0x0004, 0x78ac, 0xd0ac, 0x1de8, 0x78ab, 0x0002,
+ 0x7807, 0x0007, 0x7827, 0x0030, 0x782b, 0x0400, 0x7827, 0x0031,
+ 0x782b, 0x1a84, 0x781f, 0xff00, 0x781b, 0xb700, 0x2001, 0x0200,
+ 0x2004, 0xd0dc, 0x0110, 0x781f, 0x0303, 0x2061, 0x1a84, 0x602f,
+ 0x1cd0, 0x2001, 0x181a, 0x2004, 0x9082, 0x1cd0, 0x6032, 0x603b,
+ 0x20ce, 0x2001, 0x3384, 0xd0fc, 0x190c, 0x0dd5, 0x2001, 0x0003,
+ 0x2004, 0xd0d4, 0x1118, 0x783f, 0x3384, 0x0020, 0x9084, 0xc000,
+ 0x783f, 0xb384, 0x604f, 0x193c, 0x2001, 0x1927, 0x2004, 0x6042,
+ 0x00ce, 0x0005, 0x9086, 0x000d, 0x11d0, 0x7808, 0xd09c, 0x01b8,
+ 0x7820, 0x0026, 0x2010, 0x080c, 0xcc74, 0x0180, 0x2260, 0x6000,
+ 0x9086, 0x0004, 0x1158, 0x0016, 0x6120, 0x9186, 0x0009, 0x0108,
+ 0x0020, 0x2009, 0x004c, 0x080c, 0xafbe, 0x001e, 0x002e, 0x0005,
+ 0x0126, 0x2091, 0x2200, 0x7908, 0x9184, 0x0070, 0x190c, 0x0dce,
+ 0xd19c, 0x0158, 0x7820, 0x908c, 0xf000, 0x15e8, 0x908a, 0x0024,
+ 0x1a0c, 0x0dd5, 0x0023, 0x012e, 0x0005, 0x012e, 0x0005, 0x13bb,
+ 0x13bb, 0x13d2, 0x13d7, 0x13db, 0x13e0, 0x1408, 0x140c, 0x141a,
+ 0x141e, 0x13bb, 0x14eb, 0x14ef, 0x1561, 0x1568, 0x13bb, 0x1569,
+ 0x156a, 0x1575, 0x157c, 0x13bb, 0x13bb, 0x13bb, 0x13bb, 0x13bb,
+ 0x13bb, 0x13bb, 0x13e2, 0x13bb, 0x13bb, 0x13bb, 0x13bb, 0x13bb,
+ 0x13bb, 0x13bf, 0x13bd, 0x080c, 0x0dd5, 0x080c, 0x0dce, 0x080c,
+ 0x1587, 0x2009, 0x1a7a, 0x2104, 0x8000, 0x200a, 0x080c, 0x7ed7,
+ 0x080c, 0x1aec, 0x0005, 0x2009, 0x0048, 0x2060, 0x080c, 0xafbe,
+ 0x012e, 0x0005, 0x7004, 0xc085, 0xc0b5, 0x7006, 0x0005, 0x7004,
+ 0xc085, 0x7006, 0x0005, 0x080c, 0x1587, 0x080c, 0x16e7, 0x0005,
+ 0x080c, 0x0dd5, 0x080c, 0x1587, 0x2060, 0x6014, 0x0096, 0x2048,
+ 0xa83b, 0xffff, 0x009e, 0x2009, 0x0048, 0x080c, 0xafbe, 0x2001,
+ 0x015d, 0x2003, 0x0000, 0x2009, 0x03e8, 0x8109, 0x0160, 0x2001,
+ 0x0201, 0x2004, 0x9005, 0x0dc8, 0x2001, 0x0218, 0x2004, 0xd0ec,
+ 0x1110, 0x080c, 0x158c, 0x2001, 0x0307, 0x2003, 0x8000, 0x0005,
+ 0x7004, 0xc095, 0x7006, 0x0005, 0x080c, 0x1587, 0x2060, 0x6014,
+ 0x0096, 0x2048, 0xa83b, 0xffff, 0x009e, 0x2009, 0x0048, 0x080c,
+ 0xafbe, 0x0005, 0x080c, 0x1587, 0x080c, 0x0dd5, 0x080c, 0x1587,
+ 0x080c, 0x14d6, 0x7827, 0x0018, 0x79ac, 0xd1dc, 0x0904, 0x1487,
+ 0x7827, 0x0015, 0x7828, 0x782b, 0x0000, 0x9065, 0x0140, 0x2001,
+ 0x020d, 0x2003, 0x0050, 0x2003, 0x0020, 0x0804, 0x148d, 0x7004,
+ 0x9005, 0x01c8, 0x1188, 0x78ab, 0x0004, 0x7827, 0x0018, 0x782b,
+ 0x0000, 0xd1bc, 0x090c, 0x0dd5, 0x2001, 0x020d, 0x2003, 0x0050,
+ 0x2003, 0x0020, 0x0804, 0x14bb, 0x78ab, 0x0004, 0x7803, 0x0001,
+ 0x080c, 0x14ef, 0x0005, 0x7827, 0x0018, 0xa001, 0x7828, 0x7827,
+ 0x0011, 0xa001, 0x7928, 0x9106, 0x0110, 0x79ac, 0x08e0, 0x00e6,
+ 0x2071, 0x0200, 0x702c, 0xd0c4, 0x0140, 0x00ee, 0x080c, 0x1aec,
+ 0x080c, 0x1313, 0x7803, 0x0001, 0x0005, 0x7037, 0x0001, 0xa001,
+ 0x7150, 0x00ee, 0x918c, 0xff00, 0x9186, 0x0500, 0x0110, 0x79ac,
+ 0x0810, 0x7004, 0xc09d, 0x7006, 0x78ab, 0x0004, 0x7803, 0x0001,
+ 0x080c, 0x14ef, 0x2001, 0x020d, 0x2003, 0x0020, 0x0005, 0x7828,
+ 0x782b, 0x0000, 0x9065, 0x090c, 0x0dd5, 0x6014, 0x2048, 0x78ab,
+ 0x0004, 0x918c, 0x0700, 0x01a8, 0x080c, 0x7ed7, 0x080c, 0x1aec,
+ 0x080c, 0xcc86, 0x0158, 0xa9ac, 0xa936, 0xa9b0, 0xa93a, 0xa83f,
+ 0xffff, 0xa843, 0xffff, 0xa880, 0xc0bd, 0xa882, 0x080c, 0xc8a5,
+ 0x0005, 0x6020, 0x9086, 0x0009, 0x1128, 0x2009, 0x004c, 0x080c,
+ 0xafbe, 0x0048, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc,
+ 0x6024, 0x190c, 0xd072, 0x2029, 0x00c8, 0x8529, 0x0128, 0x2001,
+ 0x0201, 0x2004, 0x9005, 0x0dc8, 0x7dbc, 0x080c, 0xeb51, 0xd5a4,
+ 0x1118, 0x080c, 0x158c, 0x0005, 0x080c, 0x7ed7, 0x080c, 0x1aec,
+ 0x0005, 0x781f, 0x0300, 0x7803, 0x0001, 0x0005, 0x0016, 0x0066,
+ 0x0076, 0x00f6, 0x2079, 0x0300, 0x7908, 0x918c, 0x0007, 0x9186,
+ 0x0003, 0x0120, 0x2001, 0x0016, 0x080c, 0x15fd, 0x00fe, 0x007e,
+ 0x006e, 0x001e, 0x0005, 0x7004, 0xc09d, 0x7006, 0x0005, 0x7104,
+ 0x9184, 0x0004, 0x190c, 0x0dd5, 0xd184, 0x11b1, 0xd19c, 0x0180,
+ 0xc19c, 0x7106, 0x0016, 0x080c, 0x16ca, 0x001e, 0x0148, 0x2001,
+ 0x020d, 0x2003, 0x0050, 0x2003, 0x0020, 0x080c, 0x158c, 0x0005,
+ 0x81ff, 0x190c, 0x0dd5, 0x0005, 0x2100, 0xc184, 0xc1b4, 0x7106,
+ 0xd0b4, 0x0016, 0x00e6, 0x1904, 0x1556, 0x2071, 0x0200, 0x080c,
+ 0x16b7, 0x05e0, 0x080c, 0x16ca, 0x05b0, 0x6014, 0x9005, 0x05b0,
+ 0x0096, 0x2048, 0xa864, 0x009e, 0x9084, 0x00ff, 0x908e, 0x0029,
+ 0x0160, 0x908e, 0x0048, 0x1550, 0x601c, 0xd084, 0x11e0, 0x00f6,
+ 0x2c78, 0x080c, 0x1754, 0x00fe, 0x00b0, 0x00f6, 0x2c78, 0x080c,
+ 0x18dd, 0x00fe, 0x2009, 0x01f4, 0x8109, 0x0168, 0x2001, 0x0201,
+ 0x2004, 0x9005, 0x0dc8, 0x2001, 0x0218, 0x2004, 0xd0ec, 0x1118,
+ 0x080c, 0x158c, 0x0040, 0x2001, 0x020d, 0x2003, 0x0020, 0x080c,
+ 0x1313, 0x7803, 0x0001, 0x00ee, 0x001e, 0x0005, 0x080c, 0x16ca,
+ 0x0dd0, 0x2001, 0x020d, 0x2003, 0x0050, 0x2003, 0x0020, 0x0461,
+ 0x0c90, 0x0429, 0x2060, 0x2009, 0x0053, 0x080c, 0xafbe, 0x0005,
+ 0x0005, 0x0005, 0x00e1, 0x2008, 0x00d1, 0x0006, 0x7004, 0xc09d,
+ 0x7006, 0x000e, 0x080c, 0x8e21, 0x0005, 0x0089, 0x9005, 0x0118,
+ 0x080c, 0x8a28, 0x0cd0, 0x0005, 0x2001, 0x0036, 0x2009, 0x1820,
+ 0x210c, 0x2011, 0x181f, 0x2214, 0x080c, 0x15fd, 0x0005, 0x7808,
+ 0xd09c, 0x0de8, 0x7820, 0x0005, 0x080c, 0x14d6, 0x00d6, 0x2069,
+ 0x0200, 0x2009, 0x01f4, 0x8109, 0x0510, 0x6804, 0x9005, 0x0dd8,
+ 0x2001, 0x015d, 0x2003, 0x0000, 0x79bc, 0xd1a4, 0x1528, 0x79b8,
+ 0x918c, 0x0fff, 0x0180, 0x9182, 0x0841, 0x1268, 0x9188, 0x0007,
+ 0x918c, 0x0ff8, 0x810c, 0x810c, 0x810c, 0x080c, 0x15ef, 0x6827,
+ 0x0001, 0x8109, 0x1dd0, 0x04d9, 0x6827, 0x0002, 0x04c1, 0x6804,
+ 0x9005, 0x1130, 0x682c, 0xd0e4, 0x1500, 0x6804, 0x9005, 0x0de8,
+ 0x79b8, 0xd1ec, 0x1130, 0x08c0, 0x080c, 0x7ed7, 0x080c, 0x1aec,
+ 0x0090, 0x7827, 0x0015, 0x782b, 0x0000, 0x7827, 0x0018, 0x782b,
+ 0x0000, 0x2001, 0x020d, 0x2003, 0x0020, 0x2001, 0x0307, 0x2003,
+ 0x0300, 0x7803, 0x0001, 0x00de, 0x0005, 0x682c, 0x9084, 0x5400,
+ 0x9086, 0x5400, 0x0d30, 0x7827, 0x0015, 0x782b, 0x0000, 0x7803,
+ 0x0001, 0x6800, 0x9085, 0x1800, 0x6802, 0x00de, 0x0005, 0x6824,
+ 0x9084, 0x0003, 0x1de0, 0x0005, 0x2001, 0x0030, 0x2c08, 0x621c,
+ 0x0021, 0x7830, 0x9086, 0x0041, 0x0005, 0x00f6, 0x2079, 0x0300,
+ 0x0006, 0x7808, 0xd09c, 0x0140, 0x0016, 0x0026, 0x00c6, 0x080c,
+ 0x1380, 0x00ce, 0x002e, 0x001e, 0x000e, 0x0006, 0x7832, 0x7936,
+ 0x7a3a, 0x781b, 0x8080, 0x0059, 0x1118, 0x000e, 0x00fe, 0x0005,
+ 0x000e, 0x792c, 0x3900, 0x8000, 0x2004, 0x080c, 0x0dd5, 0x2009,
+ 0x180c, 0x2104, 0xc0f4, 0x200a, 0x2009, 0xff00, 0x8109, 0x0904,
+ 0x167b, 0x7a18, 0x9284, 0x0030, 0x0904, 0x1676, 0x9284, 0x0048,
+ 0x9086, 0x0008, 0x1904, 0x1676, 0x2001, 0x0109, 0x2004, 0xd08c,
+ 0x01f0, 0x0006, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x0126,
+ 0x2091, 0x2800, 0x00f6, 0x0026, 0x0016, 0x2009, 0x1a7d, 0x2104,
+ 0x8000, 0x0208, 0x200a, 0x080c, 0x9163, 0x001e, 0x002e, 0x00fe,
+ 0x012e, 0x015e, 0x014e, 0x013e, 0x01de, 0x01ce, 0x000e, 0x2001,
+ 0x009b, 0x2004, 0xd0fc, 0x01d0, 0x0006, 0x0126, 0x01c6, 0x01d6,
+ 0x0136, 0x0146, 0x0156, 0x00f6, 0x0016, 0x2009, 0x1a7e, 0x2104,
+ 0x8000, 0x0208, 0x200a, 0x080c, 0x1ef2, 0x001e, 0x00fe, 0x015e,
+ 0x014e, 0x013e, 0x01de, 0x01ce, 0x012e, 0x000e, 0x7818, 0xd0bc,
+ 0x1904, 0x1626, 0x0005, 0x2001, 0x180c, 0x2004, 0xd0f4, 0x1528,
+ 0x7a18, 0x9284, 0x0030, 0x0508, 0x9284, 0x0048, 0x9086, 0x0008,
+ 0x11e0, 0x2001, 0x19f5, 0x2004, 0x9005, 0x01b8, 0x2001, 0x1a65,
+ 0x2004, 0x9086, 0x0000, 0x0188, 0x2009, 0x1a7c, 0x2104, 0x8000,
+ 0x0208, 0x200a, 0x080c, 0xa3d4, 0x2009, 0x180c, 0x2104, 0xc0f5,
+ 0x200a, 0x2009, 0xff00, 0x0804, 0x1626, 0x9085, 0x0001, 0x0005,
+ 0x7832, 0x7936, 0x7a3a, 0x781b, 0x8080, 0x080c, 0x161f, 0x1108,
+ 0x0005, 0x792c, 0x3900, 0x8000, 0x2004, 0x080c, 0x0dd5, 0x7037,
+ 0x0001, 0x7150, 0x7037, 0x0002, 0x7050, 0x2060, 0xd1bc, 0x1110,
+ 0x7054, 0x2060, 0x918c, 0xff00, 0x9186, 0x0500, 0x0110, 0x9085,
+ 0x0001, 0x0005, 0x0006, 0x0046, 0x00e6, 0x2071, 0x0200, 0x7037,
+ 0x0002, 0x7058, 0x9084, 0xff00, 0x8007, 0x9086, 0x00bc, 0x1158,
+ 0x2021, 0x1a7b, 0x2404, 0x8000, 0x0208, 0x2022, 0x080c, 0x7ed7,
+ 0x080c, 0x1aec, 0x9006, 0x00ee, 0x004e, 0x000e, 0x0005, 0x0c11,
+ 0x1108, 0x0005, 0x00e6, 0x0016, 0x2071, 0x0200, 0x0841, 0x6124,
+ 0xd1dc, 0x01f8, 0x701c, 0xd08c, 0x0904, 0x1749, 0x7017, 0x0000,
+ 0x2001, 0x0264, 0x2004, 0xd0bc, 0x0904, 0x1749, 0x2001, 0x0268,
+ 0x00c6, 0x2064, 0x6104, 0x6038, 0x00ce, 0x918e, 0x0039, 0x1904,
+ 0x1749, 0x9c06, 0x15f0, 0x0126, 0x2091, 0x2600, 0x080c, 0x7e1e,
+ 0x012e, 0x7358, 0x745c, 0x6014, 0x905d, 0x0598, 0x2b48, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x190c, 0xd04d, 0xab42,
+ 0xac3e, 0x2001, 0x1869, 0x2004, 0xd0b4, 0x1170, 0x601c, 0xd0e4,
+ 0x1158, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1120,
+ 0xa83b, 0x7fff, 0xa837, 0xffff, 0x080c, 0x20ee, 0x1190, 0x080c,
+ 0x193a, 0x2a00, 0xa816, 0x0130, 0x2800, 0xa80e, 0x2c05, 0xa80a,
+ 0x2c00, 0xa812, 0x7037, 0x0020, 0x781f, 0x0300, 0x001e, 0x00ee,
+ 0x0005, 0x7037, 0x0050, 0x7037, 0x0020, 0x001e, 0x00ee, 0x080c,
+ 0x158c, 0x0005, 0x080c, 0x0dd5, 0x2ff0, 0x0126, 0x2091, 0x2200,
+ 0x0016, 0x00c6, 0x3e60, 0x6014, 0x2048, 0x2940, 0x903e, 0x2730,
+ 0xa864, 0x2068, 0xa81a, 0x9d84, 0x000f, 0x9088, 0x20ce, 0x2165,
+ 0x0002, 0x1780, 0x17ee, 0x1780, 0x1780, 0x1784, 0x17cf, 0x1780,
+ 0x17a4, 0x1779, 0x17e5, 0x1780, 0x1780, 0x1789, 0x18db, 0x17b8,
+ 0x17ae, 0xa964, 0x918c, 0x00ff, 0x918e, 0x0048, 0x0904, 0x17e5,
+ 0x9085, 0x0001, 0x0804, 0x18d1, 0xa87c, 0xd0ac, 0x0dc8, 0x0804,
+ 0x17f5, 0xa87c, 0xd0ac, 0x0da0, 0x0804, 0x1860, 0xa898, 0x901d,
+ 0x1108, 0xab9c, 0x9016, 0xaab2, 0xaa3e, 0xaa42, 0x3e00, 0x9080,
+ 0x0008, 0x2004, 0x9080, 0x8fef, 0x2005, 0x9005, 0x090c, 0x0dd5,
+ 0x2004, 0xa8ae, 0x0804, 0x18b9, 0xa87c, 0xd0bc, 0x09c8, 0xa890,
+ 0xa842, 0xa88c, 0xa83e, 0xa888, 0x0804, 0x17f5, 0xa87c, 0xd0bc,
+ 0x0978, 0xa890, 0xa842, 0xa88c, 0xa83e, 0xa888, 0x0804, 0x1860,
+ 0xa87c, 0xd0bc, 0x0928, 0xa890, 0xa842, 0xa88c, 0xa83e, 0xa804,
+ 0x9045, 0x090c, 0x0dd5, 0xa164, 0xa91a, 0x91ec, 0x000f, 0x9d80,
+ 0x20ce, 0x2065, 0xa888, 0xd19c, 0x1904, 0x1860, 0x0430, 0xa87c,
+ 0xd0ac, 0x0904, 0x1780, 0xa804, 0x9045, 0x090c, 0x0dd5, 0xa164,
+ 0xa91a, 0x91ec, 0x000f, 0x9d80, 0x20ce, 0x2065, 0x9006, 0xa842,
+ 0xa83e, 0xd19c, 0x1904, 0x1860, 0x0080, 0xa87c, 0xd0ac, 0x0904,
+ 0x1780, 0x9006, 0xa842, 0xa83e, 0x0804, 0x1860, 0xa87c, 0xd0ac,
+ 0x0904, 0x1780, 0x9006, 0xa842, 0xa83e, 0x2c05, 0x908a, 0x0036,
+ 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x1818, 0x1818, 0x181a,
+ 0x1818, 0x1818, 0x1818, 0x1824, 0x1818, 0x1818, 0x1818, 0x182e,
+ 0x1818, 0x1818, 0x1818, 0x1838, 0x1818, 0x1818, 0x1818, 0x1842,
+ 0x1818, 0x1818, 0x1818, 0x184c, 0x1818, 0x1818, 0x1818, 0x1856,
+ 0x080c, 0x0dd5, 0xa574, 0xa478, 0x9d86, 0x0024, 0x0904, 0x178e,
+ 0xa37c, 0xa280, 0x0804, 0x18b9, 0xa584, 0xa488, 0x9d86, 0x0024,
+ 0x0904, 0x178e, 0xa38c, 0xa290, 0x0804, 0x18b9, 0xa594, 0xa498,
+ 0x9d86, 0x0024, 0x0904, 0x178e, 0xa39c, 0xa2a0, 0x0804, 0x18b9,
+ 0xa5a4, 0xa4a8, 0x9d86, 0x0024, 0x0904, 0x178e, 0xa3ac, 0xa2b0,
+ 0x0804, 0x18b9, 0xa5b4, 0xa4b8, 0x9d86, 0x0024, 0x0904, 0x178e,
+ 0xa3bc, 0xa2c0, 0x0804, 0x18b9, 0xa5c4, 0xa4c8, 0x9d86, 0x0024,
+ 0x0904, 0x178e, 0xa3cc, 0xa2d0, 0x0804, 0x18b9, 0xa5d4, 0xa4d8,
+ 0x9d86, 0x0024, 0x0904, 0x178e, 0xa3dc, 0xa2e0, 0x0804, 0x18b9,
+ 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002,
+ 0x1883, 0x1881, 0x1881, 0x1881, 0x1881, 0x1881, 0x188e, 0x1881,
+ 0x1881, 0x1881, 0x1881, 0x1881, 0x1899, 0x1881, 0x1881, 0x1881,
+ 0x1881, 0x1881, 0x18a4, 0x1881, 0x1881, 0x1881, 0x1881, 0x1881,
+ 0x18af, 0x080c, 0x0dd5, 0xa56c, 0xa470, 0xa774, 0xa678, 0x9d86,
+ 0x002c, 0x0904, 0x178e, 0xa37c, 0xa280, 0x0458, 0xa584, 0xa488,
+ 0xa78c, 0xa690, 0x9d86, 0x002c, 0x0904, 0x178e, 0xa394, 0xa298,
+ 0x0400, 0xa59c, 0xa4a0, 0xa7a4, 0xa6a8, 0x9d86, 0x002c, 0x0904,
+ 0x178e, 0xa3ac, 0xa2b0, 0x00a8, 0xa5b4, 0xa4b8, 0xa7bc, 0xa6c0,
+ 0x9d86, 0x002c, 0x0904, 0x178e, 0xa3c4, 0xa2c8, 0x0050, 0xa5cc,
+ 0xa4d0, 0xa7d4, 0xa6d8, 0x9d86, 0x002c, 0x0904, 0x178e, 0xa3dc,
+ 0xa2e0, 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a, 0xa988,
+ 0x8c60, 0x2c1d, 0xa8ac, 0xaab0, 0xa836, 0xaa3a, 0x8109, 0xa916,
+ 0x1160, 0x3e60, 0x601c, 0xc085, 0x601e, 0xa87c, 0xc0dd, 0xa87e,
+ 0x9006, 0x00ce, 0x001e, 0x012e, 0x0005, 0x2800, 0xa80e, 0xab0a,
+ 0x2c00, 0xa812, 0x0c70, 0x0804, 0x1780, 0x2ff0, 0x0126, 0x2091,
+ 0x2200, 0x0016, 0x00c6, 0x3e60, 0x6014, 0x2048, 0x2940, 0xa80e,
+ 0x2061, 0x20c9, 0xa813, 0x20c9, 0x2c05, 0xa80a, 0xa964, 0xa91a,
+ 0xa87c, 0xd0ac, 0x090c, 0x0dd5, 0x9006, 0xa842, 0xa83e, 0x2c05,
+ 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0xadcc, 0xacd0, 0xafd4, 0xaed8,
+ 0xabdc, 0xaae0, 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a,
+ 0xa8ac, 0xaab0, 0xa836, 0xaa3a, 0xa988, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0008, 0x1120, 0x8109, 0xa916, 0x0128, 0x0080, 0x918a,
+ 0x0002, 0xa916, 0x1160, 0x3e60, 0x601c, 0xc085, 0x601e, 0xa87c,
+ 0xc0dd, 0xa87e, 0x9006, 0x00ce, 0x001e, 0x012e, 0x0005, 0xa804,
+ 0x9045, 0x090c, 0x0dd5, 0xa80e, 0xa064, 0xa81a, 0x9084, 0x000f,
+ 0x9080, 0x20ce, 0x2015, 0x82ff, 0x090c, 0x0dd5, 0xaa12, 0x2205,
+ 0xa80a, 0x0c08, 0x903e, 0x2730, 0xa880, 0xd0fc, 0x1190, 0x2d00,
+ 0x0002, 0x1a64, 0x1991, 0x1991, 0x1a64, 0x1991, 0x1a5e, 0x1a64,
+ 0x1991, 0x1a01, 0x1a01, 0x1a01, 0x1a64, 0x1a01, 0x1a64, 0x1a5b,
+ 0x1a01, 0xc0fc, 0xa882, 0xab2c, 0xaa30, 0xad1c, 0xac20, 0xdd9c,
+ 0x0904, 0x1a66, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0x9082,
+ 0x001b, 0x0002, 0x197d, 0x197b, 0x197b, 0x197b, 0x197b, 0x197b,
+ 0x1981, 0x197b, 0x197b, 0x197b, 0x197b, 0x197b, 0x1985, 0x197b,
+ 0x197b, 0x197b, 0x197b, 0x197b, 0x1989, 0x197b, 0x197b, 0x197b,
+ 0x197b, 0x197b, 0x198d, 0x080c, 0x0dd5, 0xa774, 0xa678, 0x0804,
+ 0x1a66, 0xa78c, 0xa690, 0x0804, 0x1a66, 0xa7a4, 0xa6a8, 0x0804,
+ 0x1a66, 0xa7bc, 0xa6c0, 0x0804, 0x1a66, 0xa7d4, 0xa6d8, 0x0804,
+ 0x1a66, 0xa898, 0x901d, 0x1108, 0xab9c, 0x9016, 0x2c05, 0x908a,
+ 0x0036, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x19b9, 0x19b9,
+ 0x19bb, 0x19b9, 0x19b9, 0x19b9, 0x19c5, 0x19b9, 0x19b9, 0x19b9,
+ 0x19cf, 0x19b9, 0x19b9, 0x19b9, 0x19d9, 0x19b9, 0x19b9, 0x19b9,
+ 0x19e3, 0x19b9, 0x19b9, 0x19b9, 0x19ed, 0x19b9, 0x19b9, 0x19b9,
+ 0x19f7, 0x080c, 0x0dd5, 0xa574, 0xa478, 0x9d86, 0x0004, 0x0904,
+ 0x1a66, 0xa37c, 0xa280, 0x0804, 0x1a66, 0xa584, 0xa488, 0x9d86,
+ 0x0004, 0x0904, 0x1a66, 0xa38c, 0xa290, 0x0804, 0x1a66, 0xa594,
+ 0xa498, 0x9d86, 0x0004, 0x0904, 0x1a66, 0xa39c, 0xa2a0, 0x0804,
+ 0x1a66, 0xa5a4, 0xa4a8, 0x9d86, 0x0004, 0x0904, 0x1a66, 0xa3ac,
+ 0xa2b0, 0x0804, 0x1a66, 0xa5b4, 0xa4b8, 0x9d86, 0x0004, 0x0904,
+ 0x1a66, 0xa3bc, 0xa2c0, 0x0804, 0x1a66, 0xa5c4, 0xa4c8, 0x9d86,
+ 0x0004, 0x0904, 0x1a66, 0xa3cc, 0xa2d0, 0x0804, 0x1a66, 0xa5d4,
+ 0xa4d8, 0x9d86, 0x0004, 0x0904, 0x1a66, 0xa3dc, 0xa2e0, 0x0804,
+ 0x1a66, 0xa898, 0x901d, 0x1108, 0xab9c, 0x9016, 0x2c05, 0x908a,
+ 0x0034, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x1a29, 0x1a27,
+ 0x1a27, 0x1a27, 0x1a27, 0x1a27, 0x1a33, 0x1a27, 0x1a27, 0x1a27,
+ 0x1a27, 0x1a27, 0x1a3d, 0x1a27, 0x1a27, 0x1a27, 0x1a27, 0x1a27,
+ 0x1a47, 0x1a27, 0x1a27, 0x1a27, 0x1a27, 0x1a27, 0x1a51, 0x080c,
+ 0x0dd5, 0xa56c, 0xa470, 0xa774, 0xa678, 0x9d86, 0x000c, 0x05b0,
+ 0xa37c, 0xa280, 0x0498, 0xa584, 0xa488, 0xa78c, 0xa690, 0x9d86,
+ 0x000c, 0x0560, 0xa394, 0xa298, 0x0448, 0xa59c, 0xa4a0, 0xa7a4,
+ 0xa6a8, 0x9d86, 0x000c, 0x0510, 0xa3ac, 0xa2b0, 0x00f8, 0xa5b4,
+ 0xa4b8, 0xa7bc, 0xa6c0, 0x9d86, 0x000c, 0x01c0, 0xa3c4, 0xa2c8,
+ 0x00a8, 0xa5cc, 0xa4d0, 0xa7d4, 0xa6d8, 0x9d86, 0x000c, 0x0170,
+ 0xa3dc, 0xa2e0, 0x0058, 0x9d86, 0x000e, 0x1130, 0x080c, 0x2086,
+ 0x1904, 0x193a, 0x900e, 0x0050, 0x080c, 0x0dd5, 0xab2e, 0xaa32,
+ 0xad1e, 0xac22, 0xaf26, 0xae2a, 0x080c, 0x2086, 0x0005, 0x6014,
+ 0x2048, 0x6118, 0x810c, 0x810c, 0x810c, 0x81ff, 0x1118, 0xa887,
+ 0x0001, 0x0008, 0xa986, 0x601b, 0x0002, 0xa874, 0x9084, 0x00ff,
+ 0x9084, 0x0008, 0x0150, 0x00e9, 0x6000, 0x9086, 0x0004, 0x1120,
+ 0x2009, 0x0048, 0x080c, 0xafbe, 0x0005, 0xa974, 0xd1dc, 0x1108,
+ 0x0005, 0xa934, 0xa88c, 0x9106, 0x1158, 0xa938, 0xa890, 0x9106,
+ 0x1138, 0x601c, 0xc084, 0x601e, 0x2009, 0x0048, 0x0804, 0xafbe,
+ 0x0005, 0x0126, 0x00c6, 0x2091, 0x2200, 0x00ce, 0x7908, 0x918c,
+ 0x0007, 0x9186, 0x0000, 0x05b0, 0x9186, 0x0003, 0x0598, 0x6020,
+ 0x6023, 0x0000, 0x0006, 0x2031, 0x0008, 0x00c6, 0x781f, 0x0808,
+ 0x7808, 0xd09c, 0x0120, 0x080c, 0x1380, 0x8631, 0x1db8, 0x00ce,
+ 0x781f, 0x0800, 0x2031, 0x0168, 0x00c6, 0x7808, 0xd09c, 0x190c,
+ 0x1380, 0x00ce, 0x2001, 0x0038, 0x080c, 0x1b74, 0x7930, 0x9186,
+ 0x0040, 0x0160, 0x9186, 0x0042, 0x190c, 0x0dd5, 0x2001, 0x001e,
+ 0x8001, 0x1df0, 0x8631, 0x1d40, 0x080c, 0x1b83, 0x000e, 0x6022,
+ 0x012e, 0x0005, 0x080c, 0x1b70, 0x7827, 0x0015, 0x7828, 0x9c06,
+ 0x1db8, 0x782b, 0x0000, 0x0ca0, 0x00f6, 0x2079, 0x0300, 0x7803,
+ 0x0000, 0x78ab, 0x0004, 0x00fe, 0x080c, 0x743e, 0x1188, 0x2001,
+ 0x0138, 0x2003, 0x0000, 0x2001, 0x0160, 0x2003, 0x0000, 0x2011,
+ 0x012c, 0xa001, 0xa001, 0x8211, 0x1de0, 0x0059, 0x0804, 0x74ee,
+ 0x0479, 0x0039, 0x2001, 0x0160, 0x2502, 0x2001, 0x0138, 0x2202,
+ 0x0005, 0x00e6, 0x2071, 0x0200, 0x080c, 0x2c82, 0x2009, 0x003c,
+ 0x080c, 0x2410, 0x2001, 0x015d, 0x2003, 0x0000, 0x7000, 0x9084,
+ 0x003c, 0x1de0, 0x080c, 0x84c2, 0x70a0, 0x70a2, 0x7098, 0x709a,
+ 0x709c, 0x709e, 0x2001, 0x020d, 0x2003, 0x0020, 0x00f6, 0x2079,
+ 0x0300, 0x080c, 0x1313, 0x7803, 0x0001, 0x00fe, 0x00ee, 0x0005,
+ 0x2001, 0x0138, 0x2014, 0x2003, 0x0000, 0x2001, 0x0160, 0x202c,
+ 0x2003, 0x0000, 0x080c, 0x743e, 0x1108, 0x0005, 0x2021, 0x0260,
+ 0x2001, 0x0141, 0x201c, 0xd3dc, 0x1168, 0x2001, 0x0109, 0x201c,
+ 0x939c, 0x0048, 0x1160, 0x2001, 0x0111, 0x201c, 0x83ff, 0x1110,
+ 0x8421, 0x1d70, 0x2001, 0x015d, 0x2003, 0x0000, 0x0005, 0x0046,
+ 0x2021, 0x0019, 0x2003, 0x0048, 0xa001, 0xa001, 0x201c, 0x939c,
+ 0x0048, 0x0120, 0x8421, 0x1db0, 0x004e, 0x0c60, 0x004e, 0x0c40,
+ 0x601c, 0xc084, 0x601e, 0x0005, 0x2c08, 0x621c, 0x080c, 0x15fd,
+ 0x7930, 0x0005, 0x2c08, 0x621c, 0x080c, 0x16a8, 0x7930, 0x0005,
+ 0x8001, 0x1df0, 0x0005, 0x2031, 0x0064, 0x781c, 0x9084, 0x0007,
+ 0x0170, 0x2001, 0x0038, 0x0c41, 0x9186, 0x0040, 0x0904, 0x1be1,
+ 0x2001, 0x001e, 0x0c69, 0x8631, 0x1d80, 0x080c, 0x0dd5, 0x781f,
+ 0x0202, 0x2001, 0x015d, 0x2003, 0x0000, 0x2001, 0x0dac, 0x0c01,
+ 0x781c, 0xd084, 0x0110, 0x0861, 0x04e0, 0x2001, 0x0030, 0x0891,
+ 0x9186, 0x0040, 0x0568, 0x781c, 0xd084, 0x1da8, 0x781f, 0x0101,
+ 0x2001, 0x0014, 0x0869, 0x2001, 0x0037, 0x0821, 0x9186, 0x0040,
+ 0x0140, 0x2001, 0x0030, 0x080c, 0x1b7a, 0x9186, 0x0040, 0x190c,
+ 0x0dd5, 0x00d6, 0x2069, 0x0200, 0x692c, 0xd1f4, 0x1170, 0xd1c4,
+ 0x0160, 0xd19c, 0x0130, 0x6800, 0x9085, 0x1800, 0x6802, 0x00de,
+ 0x0080, 0x6908, 0x9184, 0x0007, 0x1db0, 0x00de, 0x781f, 0x0100,
+ 0x791c, 0x9184, 0x0007, 0x090c, 0x0dd5, 0xa001, 0xa001, 0x781f,
+ 0x0200, 0x0005, 0x0126, 0x2091, 0x2400, 0x2071, 0x1a65, 0x2079,
+ 0x0090, 0x012e, 0x0005, 0x9280, 0x0005, 0x2004, 0x2048, 0xa97c,
+ 0xd1dc, 0x1904, 0x1c83, 0xa964, 0x9184, 0x0007, 0x0002, 0x1bff,
+ 0x1c6e, 0x1c16, 0x1c18, 0x1c16, 0x1c56, 0x1c36, 0x1c25, 0x918c,
+ 0x00ff, 0x9186, 0x0008, 0x1170, 0xa87c, 0xd0b4, 0x0904, 0x1ead,
+ 0x9006, 0xa842, 0xa83e, 0xa988, 0x2900, 0xa85a, 0xa813, 0x20c9,
+ 0x0804, 0x1c7f, 0x9186, 0x0048, 0x0904, 0x1c6e, 0x080c, 0x0dd5,
+ 0x9184, 0x00ff, 0x9086, 0x0013, 0x0904, 0x1c6e, 0x9184, 0x00ff,
+ 0x9086, 0x001b, 0x0904, 0x1c6e, 0x0c88, 0xa87c, 0xd0b4, 0x0904,
+ 0x1ead, 0xa890, 0xa842, 0xa83a, 0xa88c, 0xa83e, 0xa836, 0xa8ac,
+ 0xa846, 0xa8b0, 0xa84a, 0xa988, 0x0804, 0x1c76, 0xa864, 0x9084,
+ 0x00ff, 0x9086, 0x001e, 0x19d0, 0xa87c, 0xd0b4, 0x0904, 0x1ead,
+ 0xa890, 0xa842, 0xa83a, 0xa88c, 0xa83e, 0xa836, 0xa8ac, 0xa846,
+ 0xa8b0, 0xa84a, 0xa804, 0xa85a, 0x2040, 0xa064, 0x9084, 0x000f,
+ 0x9080, 0x20ce, 0x2005, 0xa812, 0xa988, 0x0448, 0x918c, 0x00ff,
+ 0x9186, 0x0015, 0x1540, 0xa87c, 0xd0b4, 0x0904, 0x1ead, 0xa804,
+ 0xa85a, 0x2040, 0xa064, 0x9084, 0x000f, 0x9080, 0x20ce, 0x2005,
+ 0xa812, 0xa988, 0x9006, 0xa842, 0xa83e, 0x0088, 0xa87c, 0xd0b4,
+ 0x0904, 0x1ead, 0xa988, 0x9006, 0xa842, 0xa83e, 0x2900, 0xa85a,
+ 0xa864, 0x9084, 0x000f, 0x9080, 0x20ce, 0x2005, 0xa812, 0xa916,
+ 0xa87c, 0xc0dd, 0xa87e, 0x0005, 0x00f6, 0x2079, 0x0090, 0x782c,
+ 0xd0fc, 0x190c, 0x1ef2, 0x00e6, 0x2071, 0x1a65, 0x7000, 0x9005,
+ 0x1904, 0x1cec, 0x7206, 0x9280, 0x0005, 0x204c, 0x9280, 0x0004,
+ 0x2004, 0x782b, 0x0004, 0x00f6, 0x2079, 0x0200, 0x7803, 0x0040,
+ 0x00fe, 0x00b6, 0x2058, 0xb86c, 0x7836, 0xb890, 0x00be, 0x00f6,
+ 0x2079, 0x0200, 0x7803, 0x0040, 0xa001, 0xa001, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0x781a, 0x2079, 0x0100, 0x8004, 0x78d6, 0x00fe,
+ 0xa814, 0x2050, 0xa858, 0x2040, 0xa810, 0x2060, 0xa064, 0x90ec,
+ 0x000f, 0xa944, 0x791a, 0x7116, 0xa848, 0x781e, 0x701a, 0x9006,
+ 0x700e, 0x7012, 0x7004, 0xa940, 0xa838, 0x9106, 0x1500, 0xa93c,
+ 0xa834, 0x9106, 0x11e0, 0x0006, 0x0016, 0xa938, 0xa834, 0x9105,
+ 0x0118, 0x001e, 0x000e, 0x0098, 0x001e, 0x000e, 0x8aff, 0x01c8,
+ 0x0126, 0x2091, 0x8000, 0x2009, 0x0306, 0x200b, 0x0808, 0x00d9,
+ 0x0108, 0x00c9, 0x012e, 0x9006, 0x00ee, 0x00fe, 0x0005, 0x0036,
+ 0x0046, 0xab38, 0xac34, 0x080c, 0x20ee, 0x004e, 0x003e, 0x0d30,
+ 0x0c98, 0x9085, 0x0001, 0x0c80, 0x2009, 0x0306, 0x200b, 0x4800,
+ 0x7027, 0x0000, 0x0005, 0x0076, 0x0066, 0x0056, 0x0046, 0x0036,
+ 0x0026, 0x8aff, 0x0904, 0x1ea6, 0x700c, 0x7214, 0x923a, 0x7010,
+ 0x7218, 0x9203, 0x0a04, 0x1ea5, 0x9705, 0x0904, 0x1ea5, 0x903e,
+ 0x2730, 0xa880, 0xd0fc, 0x1190, 0x2d00, 0x0002, 0x1e2f, 0x1d6e,
+ 0x1d6e, 0x1e2f, 0x1e2f, 0x1e0c, 0x1e2f, 0x1d6e, 0x1e13, 0x1dbd,
+ 0x1dbd, 0x1e2f, 0x1e2f, 0x1e2f, 0x1e06, 0x1dbd, 0xc0fc, 0xa882,
+ 0xab2c, 0xaa30, 0xad1c, 0xac20, 0xdd9c, 0x0904, 0x1e3c, 0x2c05,
+ 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x1d5a,
+ 0x1d58, 0x1d58, 0x1d58, 0x1d58, 0x1d58, 0x1d5e, 0x1d58, 0x1d58,
+ 0x1d58, 0x1d58, 0x1d58, 0x1d62, 0x1d58, 0x1d58, 0x1d58, 0x1d58,
+ 0x1d58, 0x1d66, 0x1d58, 0x1d58, 0x1d58, 0x1d58, 0x1d58, 0x1d6a,
+ 0x080c, 0x0dd5, 0xa774, 0xa678, 0x0804, 0x1e3c, 0xa78c, 0xa690,
+ 0x0804, 0x1e3c, 0xa7a4, 0xa6a8, 0x0804, 0x1e3c, 0xa7bc, 0xa6c0,
+ 0x0804, 0x1e3c, 0xa7d4, 0xa6d8, 0x0804, 0x1e3c, 0x2c05, 0x908a,
+ 0x0036, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x1d91, 0x1d91,
+ 0x1d93, 0x1d91, 0x1d91, 0x1d91, 0x1d99, 0x1d91, 0x1d91, 0x1d91,
+ 0x1d9f, 0x1d91, 0x1d91, 0x1d91, 0x1da5, 0x1d91, 0x1d91, 0x1d91,
+ 0x1dab, 0x1d91, 0x1d91, 0x1d91, 0x1db1, 0x1d91, 0x1d91, 0x1d91,
+ 0x1db7, 0x080c, 0x0dd5, 0xa574, 0xa478, 0xa37c, 0xa280, 0x0804,
+ 0x1e3c, 0xa584, 0xa488, 0xa38c, 0xa290, 0x0804, 0x1e3c, 0xa594,
+ 0xa498, 0xa39c, 0xa2a0, 0x0804, 0x1e3c, 0xa5a4, 0xa4a8, 0xa3ac,
+ 0xa2b0, 0x0804, 0x1e3c, 0xa5b4, 0xa4b8, 0xa3bc, 0xa2c0, 0x0804,
+ 0x1e3c, 0xa5c4, 0xa4c8, 0xa3cc, 0xa2d0, 0x0804, 0x1e3c, 0xa5d4,
+ 0xa4d8, 0xa3dc, 0xa2e0, 0x0804, 0x1e3c, 0x2c05, 0x908a, 0x0034,
+ 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x1de0, 0x1dde, 0x1dde,
+ 0x1dde, 0x1dde, 0x1dde, 0x1de8, 0x1dde, 0x1dde, 0x1dde, 0x1dde,
+ 0x1dde, 0x1df0, 0x1dde, 0x1dde, 0x1dde, 0x1dde, 0x1dde, 0x1df8,
+ 0x1dde, 0x1dde, 0x1dde, 0x1dde, 0x1dde, 0x1dff, 0x080c, 0x0dd5,
+ 0xa56c, 0xa470, 0xa774, 0xa678, 0xa37c, 0xa280, 0x0804, 0x1e3c,
+ 0xa584, 0xa488, 0xa78c, 0xa690, 0xa394, 0xa298, 0x0804, 0x1e3c,
+ 0xa59c, 0xa4a0, 0xa7a4, 0xa6a8, 0xa3ac, 0xa2b0, 0x0804, 0x1e3c,
+ 0xa5b4, 0xa4b8, 0xa7bc, 0xa6c0, 0xa3c4, 0xa2c8, 0x04e8, 0xa5cc,
+ 0xa4d0, 0xa7d4, 0xa6d8, 0xa3dc, 0xa2e0, 0x04b0, 0xa864, 0x9084,
+ 0x00ff, 0x9086, 0x001e, 0x1518, 0x080c, 0x2086, 0x1904, 0x1d09,
+ 0x900e, 0x0804, 0x1ea6, 0xab64, 0x939c, 0x00ff, 0x9386, 0x0048,
+ 0x1180, 0x00c6, 0x7004, 0x2060, 0x6004, 0x9086, 0x0043, 0x00ce,
+ 0x0904, 0x1dbd, 0xab9c, 0x9016, 0xad8c, 0xac90, 0xaf94, 0xae98,
+ 0x0098, 0x9386, 0x0008, 0x0904, 0x1dbd, 0x080c, 0x0dd5, 0xa964,
+ 0x918c, 0x00ff, 0x9186, 0x0013, 0x0904, 0x1d6e, 0x9186, 0x001b,
+ 0x0904, 0x1dbd, 0x080c, 0x0dd5, 0x2009, 0x030f, 0x2104, 0xd0fc,
+ 0x0530, 0x0066, 0x2009, 0x0306, 0x2104, 0x9084, 0x0030, 0x15c8,
+ 0x2031, 0x1000, 0x200b, 0x4000, 0x2600, 0x9302, 0x928b, 0x0000,
+ 0xa82e, 0xa932, 0x0278, 0x9105, 0x0168, 0x2011, 0x0000, 0x2618,
+ 0x2600, 0x9500, 0xa81e, 0x9481, 0x0000, 0xa822, 0xa880, 0xc0fd,
+ 0xa882, 0x0020, 0xa82f, 0x0000, 0xa833, 0x0000, 0x006e, 0x7b12,
+ 0x7a16, 0x7d02, 0x7c06, 0x7f0a, 0x7e0e, 0x782b, 0x0001, 0x7000,
+ 0x8000, 0x7002, 0xa83c, 0x9300, 0xa83e, 0xa840, 0x9201, 0xa842,
+ 0x700c, 0x9300, 0x700e, 0x7010, 0x9201, 0x7012, 0x080c, 0x2086,
+ 0x0428, 0x2031, 0x0080, 0x9584, 0x007f, 0x0108, 0x9632, 0x7124,
+ 0x7000, 0x9086, 0x0000, 0x1198, 0xc185, 0x7126, 0x2009, 0x0306,
+ 0x2104, 0xd0b4, 0x1904, 0x1e4c, 0x200b, 0x4040, 0x2009, 0x1a7f,
+ 0x2104, 0x8000, 0x0a04, 0x1e4c, 0x200a, 0x0804, 0x1e4c, 0xc18d,
+ 0x7126, 0xd184, 0x1d58, 0x0804, 0x1e4c, 0x9006, 0x002e, 0x003e,
+ 0x004e, 0x005e, 0x006e, 0x007e, 0x0005, 0x080c, 0x0dd5, 0x0026,
+ 0x2001, 0x0105, 0x2003, 0x0010, 0x782b, 0x0004, 0x7003, 0x0000,
+ 0x7004, 0x0016, 0x080c, 0x1cfc, 0x001e, 0x2060, 0x6014, 0x2048,
+ 0x080c, 0xcc86, 0x0118, 0xa880, 0xc0bd, 0xa882, 0x6020, 0x9086,
+ 0x0006, 0x1180, 0x2061, 0x0100, 0x62c8, 0x2001, 0x00fa, 0x8001,
+ 0x1df0, 0x60c8, 0x9206, 0x1dc0, 0x60c4, 0xa89a, 0x60c8, 0xa896,
+ 0x7004, 0x2060, 0x00c6, 0x080c, 0xc8a5, 0x00ce, 0x2001, 0x19f5,
+ 0x2004, 0x9c06, 0x1160, 0x2009, 0x0040, 0x080c, 0x2410, 0x080c,
+ 0xa89b, 0x2011, 0x0000, 0x080c, 0xa72c, 0x080c, 0x9891, 0x002e,
+ 0x0804, 0x2036, 0x0126, 0x2091, 0x2400, 0xa858, 0x2040, 0x792c,
+ 0x782b, 0x0002, 0x9184, 0x0700, 0x1904, 0x1eaf, 0x7000, 0x0002,
+ 0x2036, 0x1f04, 0x1f84, 0x2034, 0x8001, 0x7002, 0x7027, 0x0000,
+ 0xd19c, 0x1158, 0x8aff, 0x0904, 0x1f51, 0x080c, 0x1d03, 0x0904,
+ 0x2036, 0x080c, 0x1d03, 0x0804, 0x2036, 0x782b, 0x0004, 0xd194,
+ 0x0148, 0xa880, 0xc0fc, 0xa882, 0x8aff, 0x1518, 0xa87c, 0xc0f5,
+ 0xa87e, 0x00f8, 0x0026, 0x0036, 0xab3c, 0xaa40, 0x0016, 0x7910,
+ 0xa82c, 0x9100, 0xa82e, 0x7914, 0xa830, 0x9101, 0xa832, 0x001e,
+ 0x7810, 0x931a, 0x7814, 0x9213, 0x7800, 0xa81e, 0x7804, 0xa822,
+ 0xab3e, 0xaa42, 0x003e, 0x002e, 0x080c, 0x20a1, 0xa880, 0xc0fd,
+ 0xa882, 0x2a00, 0xa816, 0x2800, 0xa85a, 0x2c00, 0xa812, 0x7003,
+ 0x0000, 0x2009, 0x0306, 0x200b, 0x4800, 0x7027, 0x0000, 0x0804,
+ 0x2036, 0x00f6, 0x0026, 0x781c, 0x0006, 0x7818, 0x0006, 0x2079,
+ 0x0100, 0x7a14, 0x9284, 0x1984, 0x9085, 0x0012, 0x7816, 0x0036,
+ 0x2019, 0x1000, 0x8319, 0x090c, 0x0dd5, 0x7820, 0xd0bc, 0x1dd0,
+ 0x003e, 0x79c8, 0x000e, 0x9102, 0x001e, 0x0006, 0x0016, 0x79c4,
+ 0x000e, 0x9103, 0x78c6, 0x000e, 0x78ca, 0x9284, 0x1984, 0x9085,
+ 0x0012, 0x7816, 0x002e, 0x00fe, 0x782b, 0x0008, 0x7003, 0x0000,
+ 0x080c, 0x1cfc, 0x0804, 0x2036, 0x8001, 0x7002, 0x7024, 0x8004,
+ 0x7026, 0xd194, 0x0170, 0x782c, 0xd0fc, 0x1904, 0x1ef7, 0xd19c,
+ 0x1904, 0x2032, 0x8aff, 0x0904, 0x2036, 0x080c, 0x1d03, 0x0804,
+ 0x2036, 0x0026, 0x0036, 0xab3c, 0xaa40, 0x080c, 0x20a1, 0xdd9c,
+ 0x1904, 0x1ff1, 0x2c05, 0x908a, 0x0036, 0x1a0c, 0x0dd5, 0x9082,
+ 0x001b, 0x0002, 0x1fc5, 0x1fc5, 0x1fc7, 0x1fc5, 0x1fc5, 0x1fc5,
+ 0x1fcd, 0x1fc5, 0x1fc5, 0x1fc5, 0x1fd3, 0x1fc5, 0x1fc5, 0x1fc5,
+ 0x1fd9, 0x1fc5, 0x1fc5, 0x1fc5, 0x1fdf, 0x1fc5, 0x1fc5, 0x1fc5,
+ 0x1fe5, 0x1fc5, 0x1fc5, 0x1fc5, 0x1feb, 0x080c, 0x0dd5, 0xa07c,
+ 0x931a, 0xa080, 0x9213, 0x0804, 0x1f26, 0xa08c, 0x931a, 0xa090,
+ 0x9213, 0x0804, 0x1f26, 0xa09c, 0x931a, 0xa0a0, 0x9213, 0x0804,
+ 0x1f26, 0xa0ac, 0x931a, 0xa0b0, 0x9213, 0x0804, 0x1f26, 0xa0bc,
+ 0x931a, 0xa0c0, 0x9213, 0x0804, 0x1f26, 0xa0cc, 0x931a, 0xa0d0,
+ 0x9213, 0x0804, 0x1f26, 0xa0dc, 0x931a, 0xa0e0, 0x9213, 0x0804,
+ 0x1f26, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0x9082, 0x001b,
+ 0x0002, 0x2014, 0x2012, 0x2012, 0x2012, 0x2012, 0x2012, 0x201a,
+ 0x2012, 0x2012, 0x2012, 0x2012, 0x2012, 0x2020, 0x2012, 0x2012,
+ 0x2012, 0x2012, 0x2012, 0x2026, 0x2012, 0x2012, 0x2012, 0x2012,
+ 0x2012, 0x202c, 0x080c, 0x0dd5, 0xa07c, 0x931a, 0xa080, 0x9213,
+ 0x0804, 0x1f26, 0xa094, 0x931a, 0xa098, 0x9213, 0x0804, 0x1f26,
+ 0xa0ac, 0x931a, 0xa0b0, 0x9213, 0x0804, 0x1f26, 0xa0c4, 0x931a,
+ 0xa0c8, 0x9213, 0x0804, 0x1f26, 0xa0dc, 0x931a, 0xa0e0, 0x9213,
+ 0x0804, 0x1f26, 0x0804, 0x1f22, 0x080c, 0x0dd5, 0x012e, 0x0005,
+ 0x00f6, 0x00e6, 0x2071, 0x1a65, 0x7000, 0x9086, 0x0000, 0x0904,
+ 0x2081, 0x2079, 0x0090, 0x2009, 0x0207, 0x210c, 0xd194, 0x01b8,
+ 0x2009, 0x020c, 0x210c, 0x9184, 0x0003, 0x0188, 0x080c, 0xeb9a,
+ 0x2001, 0x0133, 0x2004, 0x9005, 0x090c, 0x0dd5, 0x0016, 0x2009,
+ 0x0040, 0x080c, 0x2410, 0x001e, 0x2001, 0x020c, 0x2102, 0x2009,
+ 0x0206, 0x2104, 0x2009, 0x0203, 0x210c, 0x9106, 0x1120, 0x2009,
+ 0x0040, 0x080c, 0x2410, 0x782c, 0xd0fc, 0x09a8, 0x080c, 0x1ef2,
+ 0x7000, 0x9086, 0x0000, 0x1978, 0x782b, 0x0004, 0x782c, 0xd0ac,
+ 0x1de8, 0x2009, 0x0040, 0x080c, 0x2410, 0x782b, 0x0002, 0x7003,
+ 0x0000, 0x080c, 0x1cfc, 0x00ee, 0x00fe, 0x0005, 0xa880, 0xd0fc,
+ 0x11a8, 0x8c60, 0x2c05, 0x9005, 0x0110, 0x8a51, 0x0005, 0xa004,
+ 0x9005, 0x0168, 0xa85a, 0x2040, 0xa064, 0x9084, 0x000f, 0x9080,
+ 0x20ce, 0x2065, 0x8cff, 0x090c, 0x0dd5, 0x8a51, 0x0005, 0x2050,
+ 0x0005, 0xa880, 0xd0fc, 0x11b8, 0x8a50, 0x8c61, 0x2c05, 0x9005,
+ 0x1190, 0x2800, 0x9906, 0x0120, 0xa000, 0x9005, 0x1108, 0x2900,
+ 0x2040, 0xa85a, 0xa064, 0x9084, 0x000f, 0x9080, 0x20de, 0x2065,
+ 0x8cff, 0x090c, 0x0dd5, 0x0005, 0x0000, 0x001d, 0x0021, 0x0025,
+ 0x0029, 0x002d, 0x0031, 0x0035, 0x0000, 0x001b, 0x0021, 0x0027,
+ 0x002d, 0x0033, 0x0000, 0x0000, 0x0023, 0x0000, 0x0000, 0x20c1,
+ 0x20bd, 0x20c1, 0x20c1, 0x20cb, 0x0000, 0x20c1, 0x20c8, 0x20c8,
+ 0x20c5, 0x20c8, 0x20c8, 0x0000, 0x20cb, 0x20c8, 0x0000, 0x20c3,
+ 0x20c3, 0x0000, 0x20c3, 0x20cb, 0x0000, 0x20c3, 0x20c9, 0x20c9,
+ 0x20c9, 0x0000, 0x20c9, 0x0000, 0x20cb, 0x20c9, 0x00c6, 0x00d6,
+ 0x0086, 0xab42, 0xac3e, 0xa888, 0x9055, 0x0904, 0x22cd, 0x2940,
+ 0xa064, 0x90ec, 0x000f, 0x9084, 0x00ff, 0x9086, 0x0008, 0x1118,
+ 0x2061, 0x20c9, 0x00d0, 0x9de0, 0x20ce, 0x9d86, 0x0007, 0x0130,
+ 0x9d86, 0x000e, 0x0118, 0x9d86, 0x000f, 0x1120, 0xa08c, 0x9422,
+ 0xa090, 0x931b, 0x2c05, 0x9065, 0x1140, 0x0310, 0x0804, 0x22cd,
+ 0xa004, 0x9045, 0x0904, 0x22cd, 0x08d8, 0x2c05, 0x9005, 0x0904,
+ 0x21b5, 0xdd9c, 0x1904, 0x2171, 0x908a, 0x0036, 0x1a0c, 0x0dd5,
+ 0x9082, 0x001b, 0x0002, 0x2146, 0x2146, 0x2148, 0x2146, 0x2146,
+ 0x2146, 0x214e, 0x2146, 0x2146, 0x2146, 0x2154, 0x2146, 0x2146,
+ 0x2146, 0x215a, 0x2146, 0x2146, 0x2146, 0x2160, 0x2146, 0x2146,
+ 0x2146, 0x2166, 0x2146, 0x2146, 0x2146, 0x216c, 0x080c, 0x0dd5,
+ 0xa07c, 0x9422, 0xa080, 0x931b, 0x0804, 0x21ab, 0xa08c, 0x9422,
+ 0xa090, 0x931b, 0x0804, 0x21ab, 0xa09c, 0x9422, 0xa0a0, 0x931b,
+ 0x0804, 0x21ab, 0xa0ac, 0x9422, 0xa0b0, 0x931b, 0x0804, 0x21ab,
+ 0xa0bc, 0x9422, 0xa0c0, 0x931b, 0x0804, 0x21ab, 0xa0cc, 0x9422,
+ 0xa0d0, 0x931b, 0x0804, 0x21ab, 0xa0dc, 0x9422, 0xa0e0, 0x931b,
+ 0x04d0, 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002,
+ 0x2193, 0x2191, 0x2191, 0x2191, 0x2191, 0x2191, 0x2198, 0x2191,
+ 0x2191, 0x2191, 0x2191, 0x2191, 0x219d, 0x2191, 0x2191, 0x2191,
+ 0x2191, 0x2191, 0x21a2, 0x2191, 0x2191, 0x2191, 0x2191, 0x2191,
+ 0x21a7, 0x080c, 0x0dd5, 0xa07c, 0x9422, 0xa080, 0x931b, 0x0098,
+ 0xa094, 0x9422, 0xa098, 0x931b, 0x0070, 0xa0ac, 0x9422, 0xa0b0,
+ 0x931b, 0x0048, 0xa0c4, 0x9422, 0xa0c8, 0x931b, 0x0020, 0xa0dc,
+ 0x9422, 0xa0e0, 0x931b, 0x0630, 0x2300, 0x9405, 0x0160, 0x8a51,
+ 0x0904, 0x22cd, 0x8c60, 0x0804, 0x211d, 0xa004, 0x9045, 0x0904,
+ 0x22cd, 0x0804, 0x20f8, 0x8a51, 0x0904, 0x22cd, 0x8c60, 0x2c05,
+ 0x9005, 0x1158, 0xa004, 0x9045, 0x0904, 0x22cd, 0xa064, 0x90ec,
+ 0x000f, 0x9de0, 0x20ce, 0x2c05, 0x2060, 0xa880, 0xc0fc, 0xa882,
+ 0x0804, 0x22c2, 0x2c05, 0x8422, 0x8420, 0x831a, 0x9399, 0x0000,
+ 0xac2e, 0xab32, 0xdd9c, 0x1904, 0x225f, 0x9082, 0x001b, 0x0002,
+ 0x21fb, 0x21fb, 0x21fd, 0x21fb, 0x21fb, 0x21fb, 0x220b, 0x21fb,
+ 0x21fb, 0x21fb, 0x2219, 0x21fb, 0x21fb, 0x21fb, 0x2227, 0x21fb,
+ 0x21fb, 0x21fb, 0x2235, 0x21fb, 0x21fb, 0x21fb, 0x2243, 0x21fb,
+ 0x21fb, 0x21fb, 0x2251, 0x080c, 0x0dd5, 0xa17c, 0x2400, 0x9122,
+ 0xa180, 0x2300, 0x911b, 0x0a0c, 0x0dd5, 0xa074, 0x9420, 0xa078,
+ 0x9319, 0x0804, 0x22bd, 0xa18c, 0x2400, 0x9122, 0xa190, 0x2300,
+ 0x911b, 0x0a0c, 0x0dd5, 0xa084, 0x9420, 0xa088, 0x9319, 0x0804,
+ 0x22bd, 0xa19c, 0x2400, 0x9122, 0xa1a0, 0x2300, 0x911b, 0x0a0c,
+ 0x0dd5, 0xa094, 0x9420, 0xa098, 0x9319, 0x0804, 0x22bd, 0xa1ac,
+ 0x2400, 0x9122, 0xa1b0, 0x2300, 0x911b, 0x0a0c, 0x0dd5, 0xa0a4,
+ 0x9420, 0xa0a8, 0x9319, 0x0804, 0x22bd, 0xa1bc, 0x2400, 0x9122,
+ 0xa1c0, 0x2300, 0x911b, 0x0a0c, 0x0dd5, 0xa0b4, 0x9420, 0xa0b8,
+ 0x9319, 0x0804, 0x22bd, 0xa1cc, 0x2400, 0x9122, 0xa1d0, 0x2300,
+ 0x911b, 0x0a0c, 0x0dd5, 0xa0c4, 0x9420, 0xa0c8, 0x9319, 0x0804,
+ 0x22bd, 0xa1dc, 0x2400, 0x9122, 0xa1e0, 0x2300, 0x911b, 0x0a0c,
+ 0x0dd5, 0xa0d4, 0x9420, 0xa0d8, 0x9319, 0x0804, 0x22bd, 0x9082,
+ 0x001b, 0x0002, 0x227d, 0x227b, 0x227b, 0x227b, 0x227b, 0x227b,
+ 0x228a, 0x227b, 0x227b, 0x227b, 0x227b, 0x227b, 0x2297, 0x227b,
+ 0x227b, 0x227b, 0x227b, 0x227b, 0x22a4, 0x227b, 0x227b, 0x227b,
+ 0x227b, 0x227b, 0x22b1, 0x080c, 0x0dd5, 0xa17c, 0x2400, 0x9122,
+ 0xa180, 0x2300, 0x911b, 0x0a0c, 0x0dd5, 0xa06c, 0x9420, 0xa070,
+ 0x9319, 0x0498, 0xa194, 0x2400, 0x9122, 0xa198, 0x2300, 0x911b,
+ 0x0a0c, 0x0dd5, 0xa084, 0x9420, 0xa088, 0x9319, 0x0430, 0xa1ac,
+ 0x2400, 0x9122, 0xa1b0, 0x2300, 0x911b, 0x0a0c, 0x0dd5, 0xa09c,
+ 0x9420, 0xa0a0, 0x9319, 0x00c8, 0xa1c4, 0x2400, 0x9122, 0xa1c8,
+ 0x2300, 0x911b, 0x0a0c, 0x0dd5, 0xa0b4, 0x9420, 0xa0b8, 0x9319,
+ 0x0060, 0xa1dc, 0x2400, 0x9122, 0xa1e0, 0x2300, 0x911b, 0x0a0c,
+ 0x0dd5, 0xa0cc, 0x9420, 0xa0d0, 0x9319, 0xac1e, 0xab22, 0xa880,
+ 0xc0fd, 0xa882, 0x2800, 0xa85a, 0x2c00, 0xa812, 0x2a00, 0xa816,
+ 0x000e, 0x000e, 0x000e, 0x9006, 0x0028, 0x008e, 0x00de, 0x00ce,
+ 0x9085, 0x0001, 0x0005, 0x2001, 0x0005, 0x2004, 0xd0bc, 0x190c,
+ 0x0dce, 0x9084, 0x0007, 0x0002, 0x22ee, 0x1ef2, 0x22ee, 0x22e4,
+ 0x22e7, 0x22ea, 0x22e7, 0x22ea, 0x080c, 0x1ef2, 0x0005, 0x080c,
+ 0x11a3, 0x0005, 0x080c, 0x1ef2, 0x080c, 0x11a3, 0x0005, 0x0126,
+ 0x2091, 0x2600, 0x2079, 0x0200, 0x2071, 0x0260, 0x2069, 0x1800,
+ 0x7817, 0x0000, 0x789b, 0x0814, 0x78a3, 0x0406, 0x789f, 0x0410,
+ 0x2009, 0x013b, 0x200b, 0x0400, 0x781b, 0x0002, 0x783b, 0x001f,
+ 0x7837, 0x0020, 0x7803, 0x1600, 0x012e, 0x0005, 0x2091, 0x2600,
+ 0x781c, 0xd0a4, 0x190c, 0x240d, 0x7900, 0xd1dc, 0x1118, 0x9084,
+ 0x0006, 0x001a, 0x9084, 0x000e, 0x0002, 0x2335, 0x232d, 0x7e1e,
+ 0x232d, 0x232f, 0x232f, 0x232f, 0x232f, 0x7e04, 0x232d, 0x2331,
+ 0x232d, 0x232f, 0x232d, 0x232f, 0x232d, 0x080c, 0x0dd5, 0x0031,
+ 0x0020, 0x080c, 0x7e04, 0x080c, 0x7e1e, 0x0005, 0x0006, 0x0016,
+ 0x0026, 0x080c, 0xeb9a, 0x7930, 0x9184, 0x0003, 0x01c0, 0x2001,
+ 0x19f5, 0x2004, 0x9005, 0x0170, 0x2001, 0x0133, 0x2004, 0x9005,
+ 0x090c, 0x0dd5, 0x00c6, 0x2001, 0x19f5, 0x2064, 0x080c, 0xc8a5,
+ 0x00ce, 0x00f8, 0x2009, 0x0040, 0x080c, 0x2410, 0x00d0, 0x9184,
+ 0x0014, 0x01a0, 0x6a00, 0x9286, 0x0003, 0x0160, 0x080c, 0x743e,
+ 0x1138, 0x080c, 0x7724, 0x080c, 0x60ad, 0x080c, 0x736a, 0x0010,
+ 0x080c, 0x5f6c, 0x080c, 0x7ecd, 0x0041, 0x0018, 0x9184, 0x9540,
+ 0x1dc8, 0x002e, 0x001e, 0x000e, 0x0005, 0x00e6, 0x0036, 0x0046,
+ 0x0056, 0x2071, 0x1a61, 0x080c, 0x1aec, 0x005e, 0x004e, 0x003e,
+ 0x00ee, 0x0005, 0x0126, 0x2091, 0x2e00, 0x2071, 0x1800, 0x7128,
+ 0x2001, 0x196e, 0x2102, 0x2001, 0x1976, 0x2102, 0x2001, 0x013b,
+ 0x2102, 0x2079, 0x0200, 0x2001, 0x0201, 0x789e, 0x78a3, 0x0200,
+ 0x9198, 0x0007, 0x831c, 0x831c, 0x831c, 0x9398, 0x0005, 0x2320,
+ 0x9182, 0x0204, 0x1230, 0x2011, 0x0008, 0x8423, 0x8423, 0x8423,
+ 0x0488, 0x9182, 0x024c, 0x1240, 0x2011, 0x0007, 0x8403, 0x8003,
+ 0x9400, 0x9400, 0x9420, 0x0430, 0x9182, 0x02bc, 0x1238, 0x2011,
+ 0x0006, 0x8403, 0x8003, 0x9400, 0x9420, 0x00e0, 0x9182, 0x034c,
+ 0x1230, 0x2011, 0x0005, 0x8403, 0x8003, 0x9420, 0x0098, 0x9182,
+ 0x042c, 0x1228, 0x2011, 0x0004, 0x8423, 0x8423, 0x0058, 0x9182,
+ 0x059c, 0x1228, 0x2011, 0x0003, 0x8403, 0x9420, 0x0018, 0x2011,
+ 0x0002, 0x8423, 0x9482, 0x0228, 0x8002, 0x8020, 0x8301, 0x9402,
+ 0x0110, 0x0208, 0x8321, 0x8217, 0x8203, 0x9405, 0x789a, 0x012e,
+ 0x0005, 0x0006, 0x00d6, 0x2069, 0x0200, 0x6814, 0x9084, 0xffc0,
+ 0x910d, 0x6916, 0x00de, 0x000e, 0x0005, 0x00d6, 0x2069, 0x0200,
+ 0x9005, 0x6810, 0x0110, 0xc0a5, 0x0008, 0xc0a4, 0x6812, 0x00de,
+ 0x0005, 0x0006, 0x00d6, 0x2069, 0x0200, 0x6810, 0x9084, 0xfff8,
+ 0x910d, 0x6912, 0x00de, 0x000e, 0x0005, 0x7938, 0x080c, 0x0dce,
+ 0x00f6, 0x2079, 0x0200, 0x7902, 0xa001, 0xa001, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0x7902, 0xa001, 0xa001, 0xa001, 0xa001, 0xa001,
+ 0xa001, 0x00fe, 0x0005, 0x0126, 0x2091, 0x2800, 0x2061, 0x0100,
+ 0x2071, 0x1800, 0x2009, 0x0000, 0x080c, 0x2c7c, 0x080c, 0x2b97,
+ 0x6054, 0x8004, 0x8004, 0x8004, 0x8004, 0x9084, 0x000c, 0x6150,
+ 0x918c, 0xfff3, 0x9105, 0x6052, 0x6050, 0x9084, 0xb17f, 0x9085,
+ 0x2000, 0x6052, 0x2009, 0x199c, 0x2011, 0x199d, 0x6358, 0x939c,
+ 0x38f0, 0x2320, 0x080c, 0x2bdb, 0x1238, 0x939d, 0x4003, 0x94a5,
+ 0x8603, 0x230a, 0x2412, 0x0030, 0x939d, 0x0203, 0x94a5, 0x8603,
+ 0x230a, 0x2412, 0x9006, 0x080c, 0x2bc6, 0x9006, 0x080c, 0x2ba9,
+ 0x20a9, 0x0012, 0x1d04, 0x2462, 0x2091, 0x6000, 0x1f04, 0x2462,
+ 0x602f, 0x0100, 0x602f, 0x0000, 0x6050, 0x9085, 0x0400, 0x9084,
+ 0xdfff, 0x6052, 0x6024, 0x6026, 0x080c, 0x28b5, 0x2009, 0x00ef,
+ 0x6132, 0x6136, 0x080c, 0x28c5, 0x60e7, 0x0000, 0x61ea, 0x60e3,
+ 0x0008, 0x604b, 0xf7f7, 0x6043, 0x0000, 0x602f, 0x0080, 0x602f,
+ 0x0000, 0x6007, 0x349f, 0x60bb, 0x0000, 0x20a9, 0x0018, 0x60bf,
+ 0x0000, 0x1f04, 0x248f, 0x60bb, 0x0000, 0x60bf, 0x0108, 0x60bf,
+ 0x0012, 0x60bf, 0x0405, 0x60bf, 0x0014, 0x60bf, 0x0320, 0x60bf,
+ 0x0018, 0x601b, 0x00f0, 0x601f, 0x001e, 0x600f, 0x006b, 0x602b,
+ 0x402f, 0x012e, 0x0005, 0x00f6, 0x2079, 0x0140, 0x78c3, 0x0080,
+ 0x78c3, 0x0083, 0x78c3, 0x0000, 0x00fe, 0x0005, 0x2001, 0x1835,
+ 0x2003, 0x0000, 0x2001, 0x1834, 0x2003, 0x0001, 0x0005, 0x0126,
+ 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x6124, 0x0066, 0x2031,
+ 0x1837, 0x2634, 0x96b4, 0x0028, 0x006e, 0x1138, 0x6020, 0xd1bc,
+ 0x0120, 0xd0bc, 0x1168, 0xd0b4, 0x1198, 0x9184, 0x5e2c, 0x1118,
+ 0x9184, 0x0007, 0x00aa, 0x9195, 0x0004, 0x9284, 0x0007, 0x0082,
+ 0x0016, 0x2001, 0x188b, 0x200c, 0xd184, 0x001e, 0x0d70, 0x0c98,
+ 0x0016, 0x2001, 0x188b, 0x200c, 0xd194, 0x001e, 0x0d30, 0x0c58,
+ 0x2512, 0x24f8, 0x24fb, 0x24fe, 0x2503, 0x2505, 0x2509, 0x250d,
+ 0x080c, 0x9094, 0x00b8, 0x080c, 0x9163, 0x00a0, 0x080c, 0x9163,
+ 0x080c, 0x9094, 0x0078, 0x0099, 0x0068, 0x080c, 0x9094, 0x0079,
+ 0x0048, 0x080c, 0x9163, 0x0059, 0x0028, 0x080c, 0x9163, 0x080c,
+ 0x9094, 0x0029, 0x002e, 0x001e, 0x000e, 0x012e, 0x0005, 0x00a6,
+ 0x6124, 0x6028, 0xd09c, 0x0118, 0xd19c, 0x1904, 0x277a, 0xd1f4,
+ 0x190c, 0x0dce, 0x080c, 0x743e, 0x0904, 0x256d, 0x080c, 0xd388,
+ 0x1120, 0x7000, 0x9086, 0x0003, 0x0570, 0x6024, 0x9084, 0x1800,
+ 0x0550, 0x080c, 0x7461, 0x0118, 0x080c, 0x744f, 0x1520, 0x6027,
+ 0x0020, 0x6043, 0x0000, 0x080c, 0xd388, 0x0168, 0x080c, 0x7461,
+ 0x1150, 0x2001, 0x19a6, 0x2003, 0x0001, 0x6027, 0x1800, 0x080c,
+ 0x72ce, 0x0804, 0x277d, 0x70a4, 0x9005, 0x1150, 0x70a7, 0x0001,
+ 0x00d6, 0x2069, 0x0140, 0x080c, 0x7495, 0x00de, 0x1904, 0x277d,
+ 0x080c, 0x772e, 0x0428, 0x080c, 0x7461, 0x1590, 0x6024, 0x9084,
+ 0x1800, 0x1108, 0x0468, 0x080c, 0x772e, 0x080c, 0x7724, 0x080c,
+ 0x60ad, 0x080c, 0x736a, 0x0804, 0x277a, 0xd1ac, 0x1508, 0x6024,
+ 0xd0dc, 0x1170, 0xd0e4, 0x1178, 0xd0d4, 0x1190, 0xd0cc, 0x0130,
+ 0x7098, 0x9086, 0x0028, 0x1110, 0x080c, 0x7611, 0x0804, 0x277a,
+ 0x080c, 0x7729, 0x0048, 0x2001, 0x197c, 0x2003, 0x0002, 0x0020,
+ 0x080c, 0x7576, 0x0804, 0x277a, 0x080c, 0x76ac, 0x0804, 0x277a,
+ 0x6220, 0xd1bc, 0x0138, 0xd2bc, 0x1904, 0x27ed, 0xd2b4, 0x1904,
+ 0x2800, 0x0000, 0xd1ac, 0x0904, 0x268f, 0x0036, 0x6328, 0xc3bc,
+ 0x632a, 0x003e, 0x080c, 0x743e, 0x11c0, 0x6027, 0x0020, 0x0006,
+ 0x0026, 0x0036, 0x080c, 0x7458, 0x1158, 0x080c, 0x7724, 0x080c,
+ 0x60ad, 0x080c, 0x736a, 0x003e, 0x002e, 0x000e, 0x00ae, 0x0005,
+ 0x003e, 0x002e, 0x000e, 0x080c, 0x7416, 0x0016, 0x0046, 0x00c6,
+ 0x644c, 0x9486, 0xf0f0, 0x1138, 0x2061, 0x0100, 0x644a, 0x6043,
+ 0x0090, 0x6043, 0x0010, 0x74da, 0x948c, 0xff00, 0x7038, 0xd084,
+ 0x0178, 0x9186, 0xf800, 0x1160, 0x7048, 0xd084, 0x1148, 0xc085,
+ 0x704a, 0x0036, 0x2418, 0x2011, 0x8016, 0x080c, 0x4b7f, 0x003e,
+ 0x080c, 0xd381, 0x1904, 0x266c, 0x9196, 0xff00, 0x05a8, 0x7060,
+ 0x9084, 0x00ff, 0x810f, 0x81ff, 0x0110, 0x9116, 0x0568, 0x7130,
+ 0xd184, 0x1550, 0x080c, 0x3378, 0x0128, 0xc18d, 0x7132, 0x080c,
+ 0x6a04, 0x1510, 0x6240, 0x9294, 0x0010, 0x0130, 0x6248, 0x9294,
+ 0xff00, 0x9296, 0xff00, 0x01c0, 0x7030, 0xd08c, 0x0904, 0x266c,
+ 0x7038, 0xd08c, 0x1140, 0x2001, 0x180c, 0x200c, 0xd1ac, 0x1904,
+ 0x266c, 0xc1ad, 0x2102, 0x0036, 0x73d8, 0x2011, 0x8013, 0x080c,
+ 0x4b7f, 0x003e, 0x0804, 0x266c, 0x7038, 0xd08c, 0x1140, 0x2001,
+ 0x180c, 0x200c, 0xd1ac, 0x1904, 0x266c, 0xc1ad, 0x2102, 0x0036,
+ 0x73d8, 0x2011, 0x8013, 0x080c, 0x4b7f, 0x003e, 0x7130, 0xc185,
+ 0x7132, 0x2011, 0x1848, 0x220c, 0xd1a4, 0x01f0, 0x0016, 0x2009,
+ 0x0001, 0x2011, 0x0100, 0x080c, 0x879a, 0x2019, 0x000e, 0x00c6,
+ 0x2061, 0x0000, 0x080c, 0xe6ae, 0x00ce, 0x9484, 0x00ff, 0x9080,
+ 0x3384, 0x200d, 0x918c, 0xff00, 0x810f, 0x2120, 0x9006, 0x2009,
+ 0x000e, 0x080c, 0xe73a, 0x001e, 0x0016, 0x2009, 0x0002, 0x2019,
+ 0x0004, 0x080c, 0x31e9, 0x001e, 0x0078, 0x0156, 0x00b6, 0x20a9,
+ 0x007f, 0x900e, 0x080c, 0x6699, 0x1110, 0x080c, 0x60c7, 0x8108,
+ 0x1f04, 0x2662, 0x00be, 0x015e, 0x00ce, 0x004e, 0x080c, 0xaeb4,
+ 0x60e3, 0x0000, 0x001e, 0x2001, 0x1800, 0x2014, 0x9296, 0x0004,
+ 0x1170, 0xd19c, 0x11a0, 0x2011, 0x180c, 0x2214, 0xd29c, 0x1120,
+ 0x6204, 0x9295, 0x0002, 0x6206, 0x6228, 0xc29d, 0x622a, 0x2003,
+ 0x0001, 0x2001, 0x1826, 0x2003, 0x0000, 0x6027, 0x0020, 0xd194,
+ 0x0904, 0x277a, 0x0016, 0x6220, 0xd2b4, 0x0904, 0x2717, 0x080c,
+ 0x8636, 0x080c, 0xa356, 0x6027, 0x0004, 0x00f6, 0x2019, 0x19ef,
+ 0x2304, 0x907d, 0x0904, 0x26e6, 0x7804, 0x9086, 0x0032, 0x15f0,
+ 0x00d6, 0x00c6, 0x00e6, 0x0096, 0x2069, 0x0140, 0x782c, 0x685e,
+ 0x7808, 0x685a, 0x6043, 0x0002, 0x2001, 0x0003, 0x8001, 0x1df0,
+ 0x6043, 0x0000, 0x2001, 0x003c, 0x8001, 0x1df0, 0x080c, 0x2d5e,
+ 0x2001, 0x001e, 0x8001, 0x0240, 0x20a9, 0x0009, 0x080c, 0x2c57,
+ 0x6904, 0xd1dc, 0x1140, 0x0cb0, 0x2001, 0x0100, 0x080c, 0x2d4e,
+ 0x9006, 0x080c, 0x2d4e, 0x080c, 0x9657, 0x080c, 0x9763, 0x7814,
+ 0x2048, 0xa867, 0x0103, 0x2f60, 0x080c, 0xaf43, 0x009e, 0x00ee,
+ 0x00ce, 0x00de, 0x00fe, 0x001e, 0x00ae, 0x0005, 0x00fe, 0x00d6,
+ 0x2069, 0x0140, 0x6804, 0x9084, 0x4000, 0x0110, 0x080c, 0x2d5e,
+ 0x00de, 0x00c6, 0x2061, 0x19e6, 0x6028, 0x080c, 0xd388, 0x0120,
+ 0x909a, 0x0003, 0x1258, 0x0018, 0x909a, 0x00c8, 0x1238, 0x8000,
+ 0x602a, 0x00ce, 0x080c, 0xa332, 0x0804, 0x2779, 0x2061, 0x0100,
+ 0x62c0, 0x080c, 0xad3a, 0x2019, 0x19ef, 0x2304, 0x9065, 0x0120,
+ 0x2009, 0x0027, 0x080c, 0xafbe, 0x00ce, 0x0804, 0x2779, 0xd2bc,
+ 0x0904, 0x2760, 0x080c, 0x8643, 0x6014, 0x9084, 0x1984, 0x9085,
+ 0x0010, 0x6016, 0x6027, 0x0004, 0x00d6, 0x2069, 0x0140, 0x6804,
+ 0x9084, 0x4000, 0x0110, 0x080c, 0x2d5e, 0x00de, 0x00c6, 0x2061,
+ 0x19e6, 0x6044, 0x080c, 0xd388, 0x0120, 0x909a, 0x0003, 0x1658,
+ 0x0018, 0x909a, 0x00c8, 0x1638, 0x8000, 0x6046, 0x603c, 0x00ce,
+ 0x9005, 0x05b8, 0x2009, 0x07d0, 0x080c, 0x863b, 0x9080, 0x0008,
+ 0x2004, 0x9086, 0x0006, 0x1138, 0x6114, 0x918c, 0x1984, 0x918d,
+ 0x0012, 0x6116, 0x0430, 0x9080, 0x0008, 0x2004, 0x9086, 0x0009,
+ 0x0d98, 0x6114, 0x918c, 0x1984, 0x918d, 0x0016, 0x6116, 0x00c8,
+ 0x6027, 0x0004, 0x00b0, 0x0036, 0x2019, 0x0001, 0x080c, 0xa6ac,
+ 0x003e, 0x2019, 0x19f5, 0x2304, 0x9065, 0x0150, 0x2009, 0x004f,
+ 0x6020, 0x9086, 0x0009, 0x1110, 0x2009, 0x004f, 0x080c, 0xafbe,
+ 0x00ce, 0x001e, 0xd19c, 0x0904, 0x27e8, 0x7038, 0xd0ac, 0x1904,
+ 0x27c1, 0x0016, 0x0156, 0x6027, 0x0008, 0x6050, 0x9085, 0x0040,
+ 0x6052, 0x6050, 0x9084, 0xfbcf, 0x6052, 0x080c, 0x2c76, 0x9085,
+ 0x2000, 0x6052, 0x20a9, 0x0012, 0x1d04, 0x2794, 0x080c, 0x866a,
+ 0x1f04, 0x2794, 0x6050, 0x9085, 0x0400, 0x9084, 0xdfbf, 0x6052,
+ 0x20a9, 0x0028, 0xa001, 0x1f04, 0x27a2, 0x6150, 0x9185, 0x1400,
+ 0x6052, 0x20a9, 0x0366, 0x1d04, 0x27ab, 0x080c, 0x866a, 0x6020,
+ 0xd09c, 0x1130, 0x015e, 0x6152, 0x001e, 0x6027, 0x0008, 0x0480,
+ 0x080c, 0x2c3e, 0x1f04, 0x27ab, 0x015e, 0x6152, 0x001e, 0x6027,
+ 0x0008, 0x0016, 0x6028, 0xc09c, 0x602a, 0x080c, 0xaeb4, 0x60e3,
+ 0x0000, 0x080c, 0xeb79, 0x080c, 0xeb94, 0x080c, 0x5761, 0xd0fc,
+ 0x1138, 0x080c, 0xd381, 0x1120, 0x9085, 0x0001, 0x080c, 0x7485,
+ 0x9006, 0x080c, 0x2d4e, 0x2009, 0x0002, 0x080c, 0x2c7c, 0x2001,
+ 0x1800, 0x2003, 0x0004, 0x6027, 0x0008, 0x080c, 0x0bae, 0x001e,
+ 0x918c, 0xffd0, 0x6126, 0x00ae, 0x0005, 0x0016, 0x2001, 0x188b,
+ 0x200c, 0xd184, 0x001e, 0x0904, 0x259a, 0x0016, 0x2009, 0x27f9,
+ 0x00d0, 0x2001, 0x188b, 0x200c, 0xc184, 0x2102, 0x001e, 0x0c40,
+ 0x0016, 0x2001, 0x188b, 0x200c, 0xd194, 0x001e, 0x0904, 0x259a,
+ 0x0016, 0x2009, 0x280c, 0x0038, 0x2001, 0x188b, 0x200c, 0xc194,
+ 0x2102, 0x001e, 0x08a8, 0x6028, 0xc0bc, 0x602a, 0x2001, 0x0156,
+ 0x2003, 0xbc91, 0x8000, 0x2003, 0xffff, 0x6043, 0x0001, 0x080c,
+ 0x2c76, 0x6027, 0x0080, 0x6017, 0x0000, 0x6043, 0x0000, 0x0817,
+ 0x0006, 0x0016, 0x0026, 0x0036, 0x00e6, 0x00f6, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x1800, 0x71d0, 0x70d2, 0x9116, 0x05e8, 0x81ff,
+ 0x01a0, 0x2009, 0x0000, 0x080c, 0x2c7c, 0x2011, 0x8011, 0x2019,
+ 0x010e, 0x231c, 0x939e, 0x0007, 0x1118, 0x2019, 0x0001, 0x0010,
+ 0x2019, 0x0000, 0x080c, 0x4b7f, 0x0438, 0x2001, 0x19a7, 0x200c,
+ 0x81ff, 0x1140, 0x2001, 0x0109, 0x2004, 0xd0b4, 0x0118, 0x2019,
+ 0x0003, 0x0008, 0x2118, 0x2011, 0x8012, 0x080c, 0x4b7f, 0x080c,
+ 0x5761, 0xd0fc, 0x1188, 0x080c, 0xd381, 0x1170, 0x00c6, 0x080c,
+ 0x2910, 0x080c, 0xa613, 0x2061, 0x0100, 0x2019, 0x0028, 0x2009,
+ 0x0002, 0x080c, 0x31e9, 0x00ce, 0x012e, 0x00fe, 0x00ee, 0x003e,
+ 0x002e, 0x001e, 0x000e, 0x0005, 0x2028, 0x918c, 0x00ff, 0x2130,
+ 0x9094, 0xff00, 0x11f0, 0x2011, 0x1837, 0x2214, 0xd2ac, 0x11c8,
+ 0x81ff, 0x01e8, 0x2011, 0x181f, 0x2204, 0x9106, 0x1190, 0x2011,
+ 0x1820, 0x2214, 0x9294, 0xff00, 0x9584, 0xff00, 0x9206, 0x1148,
+ 0x2011, 0x1820, 0x2214, 0x9294, 0x00ff, 0x9584, 0x00ff, 0x9206,
+ 0x1120, 0x2500, 0x080c, 0x8142, 0x0048, 0x9584, 0x00ff, 0x9080,
+ 0x3384, 0x200d, 0x918c, 0xff00, 0x810f, 0x9006, 0x0005, 0x9080,
+ 0x3384, 0x200d, 0x918c, 0x00ff, 0x0005, 0x00d6, 0x2069, 0x0140,
+ 0x2001, 0x1818, 0x2003, 0x00ef, 0x20a9, 0x0010, 0x9006, 0x6852,
+ 0x6856, 0x1f04, 0x28c0, 0x00de, 0x0005, 0x0006, 0x00d6, 0x0026,
+ 0x2069, 0x0140, 0x2001, 0x1818, 0x2102, 0x8114, 0x8214, 0x8214,
+ 0x8214, 0x20a9, 0x0010, 0x6853, 0x0000, 0x9006, 0x82ff, 0x1128,
+ 0x9184, 0x000f, 0x9080, 0xf346, 0x2005, 0x6856, 0x8211, 0x1f04,
+ 0x28d5, 0x002e, 0x00de, 0x000e, 0x0005, 0x00c6, 0x2061, 0x1800,
+ 0x6030, 0x0110, 0xc09d, 0x0008, 0xc09c, 0x6032, 0x00ce, 0x0005,
+ 0x0156, 0x00d6, 0x0026, 0x0016, 0x0006, 0x2069, 0x0140, 0x6980,
+ 0x9116, 0x0180, 0x9112, 0x1230, 0x8212, 0x8210, 0x22a8, 0x2001,
+ 0x0402, 0x0018, 0x22a8, 0x2001, 0x0404, 0x680e, 0x1f04, 0x2905,
+ 0x680f, 0x0000, 0x000e, 0x001e, 0x002e, 0x00de, 0x015e, 0x0005,
+ 0x080c, 0x575d, 0xd0c4, 0x0150, 0xd0a4, 0x0140, 0x9006, 0x0046,
+ 0x2020, 0x2009, 0x002e, 0x080c, 0xe73a, 0x004e, 0x0005, 0x00f6,
+ 0x0016, 0x0026, 0x2079, 0x0140, 0x78c4, 0xd0dc, 0x0904, 0x297c,
+ 0x080c, 0x2bdb, 0x0660, 0x9084, 0x0700, 0x908e, 0x0600, 0x1120,
+ 0x2011, 0x4000, 0x900e, 0x0458, 0x908e, 0x0500, 0x1120, 0x2011,
+ 0x8000, 0x900e, 0x0420, 0x908e, 0x0400, 0x1120, 0x9016, 0x2009,
+ 0x0001, 0x00e8, 0x908e, 0x0300, 0x1120, 0x9016, 0x2009, 0x0002,
+ 0x00b0, 0x908e, 0x0200, 0x1120, 0x9016, 0x2009, 0x0004, 0x0078,
+ 0x908e, 0x0100, 0x1548, 0x9016, 0x2009, 0x0008, 0x0040, 0x9084,
+ 0x0700, 0x908e, 0x0300, 0x1500, 0x2011, 0x0030, 0x0058, 0x2300,
+ 0x9080, 0x0020, 0x2018, 0x080c, 0x9027, 0x928c, 0xff00, 0x0110,
+ 0x2011, 0x00ff, 0x2200, 0x8007, 0x9085, 0x004c, 0x78c2, 0x2009,
+ 0x0138, 0x220a, 0x080c, 0x743e, 0x1118, 0x2009, 0x196c, 0x220a,
+ 0x002e, 0x001e, 0x00fe, 0x0005, 0x78c3, 0x0000, 0x0cc8, 0x0126,
+ 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x2001, 0x0170, 0x200c,
+ 0x8000, 0x2014, 0x9184, 0x0003, 0x0110, 0x080c, 0x0dce, 0x002e,
+ 0x001e, 0x000e, 0x012e, 0x0005, 0x2001, 0x0171, 0x2004, 0xd0dc,
+ 0x0168, 0x2001, 0x0170, 0x200c, 0x918c, 0x00ff, 0x918e, 0x004c,
+ 0x1128, 0x200c, 0x918c, 0xff00, 0x810f, 0x0005, 0x900e, 0x2001,
+ 0x0227, 0x2004, 0x8007, 0x9084, 0x00ff, 0x8004, 0x9108, 0x2001,
+ 0x0226, 0x2004, 0x8007, 0x9084, 0x00ff, 0x8004, 0x9108, 0x0005,
+ 0x0018, 0x000c, 0x0018, 0x0020, 0x1000, 0x0800, 0x1000, 0x1800,
+ 0x0156, 0x0006, 0x0016, 0x0026, 0x00e6, 0x2001, 0x198f, 0x2004,
+ 0x908a, 0x0007, 0x1a0c, 0x0dd5, 0x0033, 0x00ee, 0x002e, 0x001e,
+ 0x000e, 0x015e, 0x0005, 0x29da, 0x29f8, 0x2a1c, 0x2a1e, 0x2a47,
+ 0x2a49, 0x2a4b, 0x2001, 0x0001, 0x080c, 0x2828, 0x080c, 0x2c39,
+ 0x2001, 0x1991, 0x2003, 0x0000, 0x7828, 0x9084, 0xe1d7, 0x782a,
+ 0x9006, 0x20a9, 0x0009, 0x080c, 0x2bf7, 0x2001, 0x198f, 0x2003,
+ 0x0006, 0x2009, 0x001e, 0x2011, 0x2a4c, 0x080c, 0x8648, 0x0005,
+ 0x2009, 0x1994, 0x200b, 0x0000, 0x2001, 0x1999, 0x2003, 0x0036,
+ 0x2001, 0x1998, 0x2003, 0x002a, 0x2001, 0x1991, 0x2003, 0x0001,
+ 0x9006, 0x080c, 0x2ba9, 0x2001, 0xffff, 0x20a9, 0x0009, 0x080c,
+ 0x2bf7, 0x2001, 0x198f, 0x2003, 0x0006, 0x2009, 0x001e, 0x2011,
+ 0x2a4c, 0x080c, 0x8648, 0x0005, 0x080c, 0x0dd5, 0x2001, 0x1999,
+ 0x2003, 0x0036, 0x2001, 0x1991, 0x2003, 0x0003, 0x7a38, 0x9294,
+ 0x0005, 0x9296, 0x0004, 0x0110, 0x9006, 0x0010, 0x2001, 0x0001,
+ 0x080c, 0x2ba9, 0x2001, 0x1995, 0x2003, 0x0000, 0x2001, 0xffff,
+ 0x20a9, 0x0009, 0x080c, 0x2bf7, 0x2001, 0x198f, 0x2003, 0x0006,
+ 0x2009, 0x001e, 0x2011, 0x2a4c, 0x080c, 0x8648, 0x0005, 0x080c,
+ 0x0dd5, 0x080c, 0x0dd5, 0x0005, 0x0006, 0x0016, 0x0026, 0x00e6,
+ 0x00f6, 0x0156, 0x0126, 0x2091, 0x8000, 0x2079, 0x0100, 0x2001,
+ 0x1991, 0x2004, 0x908a, 0x0007, 0x1a0c, 0x0dd5, 0x0043, 0x012e,
+ 0x015e, 0x00fe, 0x00ee, 0x002e, 0x001e, 0x000e, 0x0005, 0x2a6e,
+ 0x2a8e, 0x2ace, 0x2afe, 0x2b22, 0x2b32, 0x2b34, 0x080c, 0x2beb,
+ 0x11b0, 0x7850, 0x9084, 0xefff, 0x7852, 0x2009, 0x1997, 0x2104,
+ 0x7a38, 0x9294, 0x0005, 0x9296, 0x0004, 0x0110, 0xc08d, 0x0008,
+ 0xc085, 0x200a, 0x2001, 0x198f, 0x2003, 0x0001, 0x0030, 0x080c,
+ 0x2b58, 0x2001, 0xffff, 0x080c, 0x29e9, 0x0005, 0x080c, 0x2b36,
+ 0x05e0, 0x2009, 0x1998, 0x2104, 0x8001, 0x200a, 0x080c, 0x2beb,
+ 0x1178, 0x7850, 0x9084, 0xefff, 0x7852, 0x7a38, 0x9294, 0x0005,
+ 0x9296, 0x0005, 0x0518, 0x2009, 0x1997, 0x2104, 0xc085, 0x200a,
+ 0x2009, 0x1994, 0x2104, 0x8000, 0x200a, 0x9086, 0x0005, 0x0118,
+ 0x080c, 0x2b3e, 0x00c0, 0x200b, 0x0000, 0x7a38, 0x9294, 0x0006,
+ 0x9296, 0x0004, 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x080c,
+ 0x2bc6, 0x2001, 0x1991, 0x2003, 0x0002, 0x0028, 0x2001, 0x198f,
+ 0x2003, 0x0003, 0x0010, 0x080c, 0x2a0b, 0x0005, 0x080c, 0x2b36,
+ 0x0560, 0x2009, 0x1998, 0x2104, 0x8001, 0x200a, 0x080c, 0x2beb,
+ 0x1168, 0x7850, 0x9084, 0xefff, 0x7852, 0x2001, 0x198f, 0x2003,
+ 0x0003, 0x2001, 0x1990, 0x2003, 0x0000, 0x00b8, 0x2009, 0x1998,
+ 0x2104, 0x9005, 0x1118, 0x080c, 0x2b7b, 0x0010, 0x080c, 0x2b4b,
+ 0x080c, 0x2b3e, 0x2009, 0x1994, 0x200b, 0x0000, 0x2001, 0x1991,
+ 0x2003, 0x0001, 0x080c, 0x2a0b, 0x0000, 0x0005, 0x04b9, 0x0508,
+ 0x080c, 0x2beb, 0x11b8, 0x7850, 0x9084, 0xefff, 0x7852, 0x2009,
+ 0x1995, 0x2104, 0x8000, 0x200a, 0x9086, 0x0007, 0x0108, 0x0078,
+ 0x2001, 0x199a, 0x2003, 0x000a, 0x2009, 0x1997, 0x2104, 0xc0fd,
+ 0x200a, 0x0038, 0x0419, 0x2001, 0x1991, 0x2003, 0x0004, 0x080c,
+ 0x2a36, 0x0005, 0x0099, 0x0168, 0x080c, 0x2beb, 0x1138, 0x7850,
+ 0x9084, 0xefff, 0x7852, 0x080c, 0x2a22, 0x0018, 0x0079, 0x080c,
+ 0x2a36, 0x0005, 0x080c, 0x0dd5, 0x080c, 0x0dd5, 0x2009, 0x1999,
+ 0x2104, 0x8001, 0x200a, 0x090c, 0x2b97, 0x0005, 0x7a38, 0x9294,
+ 0x0005, 0x9296, 0x0005, 0x0110, 0x9006, 0x0010, 0x2001, 0x0001,
+ 0x080c, 0x2bc6, 0x0005, 0x7a38, 0x9294, 0x0006, 0x9296, 0x0006,
+ 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x080c, 0x2ba9, 0x0005,
+ 0x2009, 0x1994, 0x2104, 0x8000, 0x200a, 0x9086, 0x0005, 0x0108,
+ 0x0068, 0x200b, 0x0000, 0x7a38, 0x9294, 0x0006, 0x9296, 0x0006,
+ 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x04d9, 0x7a38, 0x9294,
+ 0x0005, 0x9296, 0x0005, 0x0110, 0x9006, 0x0010, 0x2001, 0x0001,
+ 0x080c, 0x2bc6, 0x0005, 0x0086, 0x2001, 0x1997, 0x2004, 0x9084,
+ 0x7fff, 0x090c, 0x0dd5, 0x2009, 0x1996, 0x2144, 0x8846, 0x280a,
+ 0x9844, 0x0dd8, 0xd08c, 0x1120, 0xd084, 0x1120, 0x080c, 0x0dd5,
+ 0x9006, 0x0010, 0x2001, 0x0001, 0x00a1, 0x008e, 0x0005, 0x0006,
+ 0x0156, 0x2001, 0x198f, 0x20a9, 0x0009, 0x2003, 0x0000, 0x8000,
+ 0x1f04, 0x2b9d, 0x2001, 0x1996, 0x2003, 0x8000, 0x015e, 0x000e,
+ 0x0005, 0x00f6, 0x2079, 0x0100, 0x9085, 0x0000, 0x0158, 0x7838,
+ 0x9084, 0xfff9, 0x9085, 0x0004, 0x783a, 0x2009, 0x199c, 0x210c,
+ 0x795a, 0x0050, 0x7838, 0x9084, 0xfffb, 0x9085, 0x0006, 0x783a,
+ 0x2009, 0x199d, 0x210c, 0x795a, 0x00fe, 0x0005, 0x00f6, 0x2079,
+ 0x0100, 0x9085, 0x0000, 0x0138, 0x7838, 0x9084, 0xfffa, 0x9085,
+ 0x0004, 0x783a, 0x0030, 0x7838, 0x9084, 0xfffb, 0x9085, 0x0005,
+ 0x783a, 0x00fe, 0x0005, 0x0006, 0x2001, 0x0100, 0x2004, 0x9082,
+ 0x0007, 0x000e, 0x0005, 0x0006, 0x2001, 0x0100, 0x2004, 0x9082,
+ 0x0009, 0x000e, 0x0005, 0x0156, 0x20a9, 0x0064, 0x7820, 0x080c,
+ 0x2c76, 0xd09c, 0x1110, 0x1f04, 0x2bee, 0x015e, 0x0005, 0x0126,
+ 0x0016, 0x0006, 0x2091, 0x8000, 0x7850, 0x9085, 0x0040, 0x7852,
+ 0x7850, 0x9084, 0xfbcf, 0x7852, 0x080c, 0x2c76, 0x9085, 0x2000,
+ 0x7852, 0x000e, 0x2008, 0x9186, 0x0000, 0x1118, 0x783b, 0x0007,
+ 0x0090, 0x9186, 0x0001, 0x1118, 0x783b, 0x0006, 0x0060, 0x9186,
+ 0x0002, 0x1118, 0x783b, 0x0005, 0x0030, 0x9186, 0x0003, 0x1118,
+ 0x783b, 0x0004, 0x0000, 0x0006, 0x1d04, 0x2c24, 0x080c, 0x866a,
+ 0x1f04, 0x2c24, 0x7850, 0x9085, 0x0400, 0x9084, 0xdfbf, 0x7852,
+ 0x080c, 0x2c76, 0x9085, 0x1000, 0x7852, 0x000e, 0x001e, 0x012e,
+ 0x0005, 0x7850, 0x9084, 0xffcf, 0x7852, 0x0005, 0x0006, 0x0156,
+ 0x00f6, 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854, 0xd0ac, 0x1130,
+ 0x7820, 0xd0e4, 0x1140, 0x1f04, 0x2c48, 0x0028, 0x7854, 0xd08c,
+ 0x1110, 0x1f04, 0x2c4e, 0x00fe, 0x015e, 0x000e, 0x0005, 0x1d04,
+ 0x2c57, 0x080c, 0x866a, 0x1f04, 0x2c57, 0x0005, 0x0006, 0x2001,
+ 0x199b, 0x2004, 0x9086, 0x0000, 0x000e, 0x0005, 0x0006, 0x2001,
+ 0x199b, 0x2004, 0x9086, 0x0001, 0x000e, 0x0005, 0x0006, 0x2001,
+ 0x199b, 0x2004, 0x9086, 0x0002, 0x000e, 0x0005, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0xa001, 0x0005, 0x0006, 0x2001, 0x19a7, 0x2102,
+ 0x000e, 0x0005, 0x2009, 0x0171, 0x2104, 0xd0dc, 0x0140, 0x2009,
+ 0x0170, 0x2104, 0x200b, 0x0080, 0xa001, 0xa001, 0x200a, 0x0005,
+ 0x0036, 0x0046, 0x2001, 0x0141, 0x200c, 0x918c, 0xff00, 0x9186,
+ 0x2100, 0x0140, 0x9186, 0x2000, 0x0170, 0x9186, 0x0100, 0x1904,
+ 0x2cef, 0x0048, 0x0016, 0x2009, 0x1a82, 0x2104, 0x8000, 0x0208,
+ 0x200a, 0x001e, 0x04f0, 0x2009, 0x00a2, 0x080c, 0x0e51, 0x2019,
+ 0x0160, 0x2324, 0x2011, 0x0003, 0x2009, 0x0169, 0x2104, 0x9084,
+ 0x0007, 0x210c, 0x918c, 0x0007, 0x910e, 0x1db0, 0x9086, 0x0003,
+ 0x1548, 0x2304, 0x0066, 0x0076, 0x2031, 0x0002, 0x233c, 0x973e,
+ 0x0148, 0x8631, 0x1dd8, 0x2031, 0x1a83, 0x263c, 0x8738, 0x0208,
+ 0x2732, 0x2304, 0x007e, 0x006e, 0x9402, 0x02a0, 0x19d0, 0x8211,
+ 0x19d8, 0x84ff, 0x0170, 0x2001, 0x0141, 0x200c, 0x918c, 0xff00,
+ 0x9186, 0x0100, 0x0130, 0x2009, 0x180c, 0x2104, 0xc0dd, 0x200a,
+ 0x0008, 0x0421, 0x2001, 0x1980, 0x200c, 0x080c, 0x0e51, 0x004e,
+ 0x003e, 0x0005, 0x2001, 0x180c, 0x2004, 0xd0dc, 0x01b0, 0x2001,
+ 0x0160, 0x2004, 0x9005, 0x0140, 0x2001, 0x0141, 0x2004, 0x9084,
+ 0xff00, 0x9086, 0x0100, 0x1148, 0x0126, 0x2091, 0x8000, 0x0016,
+ 0x0026, 0x0021, 0x002e, 0x001e, 0x012e, 0x0005, 0x00c6, 0x2061,
+ 0x0100, 0x6014, 0x0006, 0x2001, 0x0161, 0x2003, 0x0000, 0x6017,
+ 0x0018, 0xa001, 0xa001, 0x602f, 0x0008, 0x6104, 0x918e, 0x0010,
+ 0x6106, 0x918e, 0x0010, 0x6106, 0x6017, 0x0040, 0x04b9, 0x001e,
+ 0x9184, 0x0003, 0x01e0, 0x0036, 0x0016, 0x2019, 0x0141, 0x6124,
+ 0x918c, 0x0028, 0x1120, 0x2304, 0x9084, 0x2800, 0x0dc0, 0x001e,
+ 0x919c, 0xffe4, 0x9184, 0x0001, 0x0118, 0x9385, 0x0009, 0x6016,
+ 0x9184, 0x0002, 0x0118, 0x9385, 0x0012, 0x6016, 0x003e, 0x2001,
+ 0x180c, 0x200c, 0xc1dc, 0x2102, 0x00ce, 0x0005, 0x0016, 0x0026,
+ 0x080c, 0x7458, 0x0108, 0xc0bc, 0x2009, 0x0140, 0x2114, 0x9294,
+ 0x0001, 0x9215, 0x220a, 0x002e, 0x001e, 0x0005, 0x0016, 0x0026,
+ 0x2009, 0x0140, 0x2114, 0x9294, 0x0001, 0x9285, 0x1000, 0x200a,
+ 0x220a, 0x002e, 0x001e, 0x0005, 0x0016, 0x0026, 0x2009, 0x0140,
+ 0x2114, 0x9294, 0x0001, 0x9215, 0x220a, 0x002e, 0x001e, 0x0005,
+ 0x0006, 0x0016, 0x2009, 0x0140, 0x2104, 0x1128, 0x080c, 0x7458,
+ 0x0110, 0xc0bc, 0x0008, 0xc0bd, 0x200a, 0x001e, 0x000e, 0x0005,
+ 0x2ff4, 0x2ff4, 0x2e18, 0x2e18, 0x2e24, 0x2e24, 0x2e30, 0x2e30,
+ 0x2e3e, 0x2e3e, 0x2e4a, 0x2e4a, 0x2e58, 0x2e58, 0x2e66, 0x2e66,
+ 0x2e78, 0x2e78, 0x2e84, 0x2e84, 0x2e92, 0x2e92, 0x2eb0, 0x2eb0,
+ 0x2ed0, 0x2ed0, 0x2ea0, 0x2ea0, 0x2ec0, 0x2ec0, 0x2ede, 0x2ede,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2ef0, 0x2ef0, 0x2efc, 0x2efc, 0x2f0a, 0x2f0a, 0x2f18, 0x2f18,
+ 0x2f28, 0x2f28, 0x2f36, 0x2f36, 0x2f46, 0x2f46, 0x2f56, 0x2f56,
+ 0x2f68, 0x2f68, 0x2f76, 0x2f76, 0x2f86, 0x2f86, 0x2fa8, 0x2fa8,
+ 0x2fca, 0x2fca, 0x2f96, 0x2f96, 0x2fb9, 0x2fb9, 0x2fd9, 0x2fd9,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76, 0x2e76,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x24bf, 0x0804, 0x2fec, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x22d3, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x22d3, 0x080c, 0x24bf, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x230e,
+ 0x0804, 0x2fec, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x24bf, 0x080c, 0x230e, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x22d3, 0x080c, 0x230e, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x22d3,
+ 0x080c, 0x24bf, 0x080c, 0x230e, 0x0804, 0x2fec, 0xa001, 0x0cf0,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x1380, 0x0804, 0x2fec, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x24bf, 0x080c, 0x1380,
+ 0x0804, 0x2fec, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x22d3, 0x080c, 0x1380, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x24bf, 0x080c, 0x1380, 0x080c, 0x230e, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x22d3, 0x080c, 0x24bf, 0x080c, 0x1380, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x22d3, 0x080c, 0x1380, 0x080c, 0x230e, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x1380, 0x080c, 0x230e, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x22d3,
+ 0x080c, 0x24bf, 0x080c, 0x1380, 0x080c, 0x230e, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x297f, 0x0804, 0x2fec, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f, 0x080c, 0x24bf,
+ 0x0804, 0x2fec, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x297f, 0x080c, 0x22d3, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x297f, 0x080c, 0x22d3, 0x080c, 0x24bf, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x297f, 0x080c, 0x230e, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f,
+ 0x080c, 0x24bf, 0x080c, 0x230e, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f,
+ 0x080c, 0x22d3, 0x080c, 0x230e, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f,
+ 0x080c, 0x22d3, 0x080c, 0x24bf, 0x080c, 0x230e, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x297f, 0x080c, 0x1380, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f,
+ 0x080c, 0x24bf, 0x080c, 0x1380, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f,
+ 0x080c, 0x22d3, 0x080c, 0x1380, 0x0804, 0x2fec, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x297f,
+ 0x080c, 0x24bf, 0x080c, 0x1380, 0x080c, 0x230e, 0x0804, 0x2fec,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x297f, 0x080c, 0x22d3, 0x080c, 0x24bf, 0x080c, 0x1380,
+ 0x0498, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x080c, 0x297f, 0x080c, 0x22d3, 0x080c, 0x1380, 0x080c,
+ 0x230e, 0x0410, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x297f, 0x080c, 0x1380, 0x080c, 0x230e,
+ 0x0098, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x080c, 0x297f, 0x080c, 0x22d3, 0x080c, 0x24bf, 0x080c,
+ 0x1380, 0x080c, 0x230e, 0x0000, 0x015e, 0x014e, 0x013e, 0x01de,
+ 0x01ce, 0x012e, 0x000e, 0x010e, 0x000d, 0x00b6, 0x00c6, 0x0026,
+ 0x0046, 0x9026, 0x080c, 0x69ca, 0x1904, 0x3105, 0x72dc, 0x2001,
+ 0x197b, 0x2004, 0x9005, 0x1110, 0xd29c, 0x0148, 0xd284, 0x1138,
+ 0xd2bc, 0x1904, 0x3105, 0x080c, 0x310a, 0x0804, 0x3105, 0xd2cc,
+ 0x1904, 0x3105, 0x080c, 0x743e, 0x1120, 0x70af, 0xffff, 0x0804,
+ 0x3105, 0xd294, 0x0120, 0x70af, 0xffff, 0x0804, 0x3105, 0x080c,
+ 0x3373, 0x0160, 0x080c, 0xd388, 0x0128, 0x2001, 0x1818, 0x203c,
+ 0x0804, 0x3092, 0x70af, 0xffff, 0x0804, 0x3105, 0x2001, 0x1818,
+ 0x203c, 0x7294, 0xd284, 0x0904, 0x3092, 0xd28c, 0x1904, 0x3092,
+ 0x0036, 0x73ac, 0x938e, 0xffff, 0x1110, 0x2019, 0x0001, 0x8314,
+ 0x92e0, 0x1c80, 0x2c04, 0x938c, 0x0001, 0x0120, 0x9084, 0xff00,
+ 0x8007, 0x0010, 0x9084, 0x00ff, 0x970e, 0x05d0, 0x908e, 0x0000,
+ 0x05b8, 0x908e, 0x00ff, 0x1150, 0x7230, 0xd284, 0x15b0, 0x7294,
+ 0xc28d, 0x7296, 0x70af, 0xffff, 0x003e, 0x04a0, 0x900e, 0x080c,
+ 0x287c, 0x080c, 0x6638, 0x1538, 0x9006, 0xb8bb, 0x0520, 0xb8ac,
+ 0x9005, 0x0148, 0x00c6, 0x2060, 0x080c, 0x8a3d, 0x00ce, 0x090c,
+ 0x8dda, 0xb8af, 0x0000, 0x080c, 0x6a0c, 0x1168, 0x7030, 0xd08c,
+ 0x0130, 0xb800, 0xd0bc, 0x0138, 0x080c, 0x68b9, 0x0120, 0x080c,
+ 0x3123, 0x0148, 0x0028, 0x080c, 0x3263, 0x080c, 0x314f, 0x0118,
+ 0x8318, 0x0804, 0x303f, 0x73ae, 0x0010, 0x70af, 0xffff, 0x003e,
+ 0x0804, 0x3105, 0x9780, 0x3384, 0x203d, 0x97bc, 0xff00, 0x873f,
+ 0x2041, 0x007e, 0x70ac, 0x9096, 0xffff, 0x1118, 0x900e, 0x28a8,
+ 0x0050, 0x9812, 0x0220, 0x2008, 0x9802, 0x20a8, 0x0020, 0x70af,
+ 0xffff, 0x0804, 0x3105, 0x2700, 0x0156, 0x0016, 0x9106, 0x0904,
+ 0x30fa, 0xc484, 0x080c, 0x6699, 0x0148, 0x080c, 0xd388, 0x1904,
+ 0x30fa, 0x080c, 0x6638, 0x1904, 0x3102, 0x0008, 0xc485, 0xb8bb,
+ 0x0520, 0xb8ac, 0x9005, 0x0148, 0x00c6, 0x2060, 0x080c, 0x8a3d,
+ 0x00ce, 0x090c, 0x8dda, 0xb8af, 0x0000, 0x080c, 0x6a0c, 0x1130,
+ 0x7030, 0xd08c, 0x01f8, 0xb800, 0xd0bc, 0x11e0, 0x7294, 0xd28c,
+ 0x0180, 0x080c, 0x6a0c, 0x9082, 0x0006, 0x02e0, 0xd484, 0x1118,
+ 0x080c, 0x665d, 0x0028, 0x080c, 0x32ef, 0x01a0, 0x080c, 0x331a,
+ 0x0088, 0x080c, 0x3263, 0x080c, 0xd388, 0x1160, 0x080c, 0x314f,
+ 0x0188, 0x0040, 0x080c, 0xd388, 0x1118, 0x080c, 0x32ef, 0x0110,
+ 0x0451, 0x0140, 0x001e, 0x8108, 0x015e, 0x1f04, 0x30ab, 0x70af,
+ 0xffff, 0x0018, 0x001e, 0x015e, 0x71ae, 0x004e, 0x002e, 0x00ce,
+ 0x00be, 0x0005, 0x00c6, 0x0016, 0x70af, 0x0001, 0x2009, 0x007e,
+ 0x080c, 0x6638, 0x1168, 0xb813, 0x00ff, 0xb817, 0xfffe, 0x080c,
+ 0x3263, 0x04a9, 0x0128, 0x70dc, 0xc0bd, 0x70de, 0x080c, 0xd0d9,
+ 0x001e, 0x00ce, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x2001,
+ 0x184c, 0x2004, 0x9084, 0x00ff, 0xb842, 0x080c, 0xaf91, 0x01d0,
+ 0x2b00, 0x6012, 0x080c, 0xd102, 0x6023, 0x0001, 0x9006, 0x080c,
+ 0x65d5, 0x2001, 0x0000, 0x080c, 0x65e9, 0x0126, 0x2091, 0x8000,
+ 0x70a8, 0x8000, 0x70aa, 0x012e, 0x2009, 0x0004, 0x080c, 0xafbe,
+ 0x9085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005, 0x0016,
+ 0x0076, 0x00d6, 0x00c6, 0x2001, 0x184c, 0x2004, 0x9084, 0x00ff,
+ 0xb842, 0x080c, 0xaf91, 0x0548, 0x2b00, 0x6012, 0xb800, 0xc0c4,
+ 0xb802, 0xb8a0, 0x9086, 0x007e, 0x0140, 0xb804, 0x9084, 0x00ff,
+ 0x9086, 0x0006, 0x1110, 0x080c, 0x321e, 0x080c, 0xd102, 0x6023,
+ 0x0001, 0x9006, 0x080c, 0x65d5, 0x2001, 0x0002, 0x080c, 0x65e9,
+ 0x0126, 0x2091, 0x8000, 0x70a8, 0x8000, 0x70aa, 0x012e, 0x2009,
+ 0x0002, 0x080c, 0xafbe, 0x9085, 0x0001, 0x00ce, 0x00de, 0x007e,
+ 0x001e, 0x0005, 0x00b6, 0x00c6, 0x0026, 0x2009, 0x0080, 0x080c,
+ 0x6638, 0x1140, 0xb813, 0x00ff, 0xb817, 0xfffc, 0x0039, 0x0110,
+ 0x70e3, 0xffff, 0x002e, 0x00ce, 0x00be, 0x0005, 0x0016, 0x0076,
+ 0x00d6, 0x00c6, 0x080c, 0xaeed, 0x01d0, 0x2b00, 0x6012, 0x080c,
+ 0xd102, 0x6023, 0x0001, 0x9006, 0x080c, 0x65d5, 0x2001, 0x0002,
+ 0x080c, 0x65e9, 0x0126, 0x2091, 0x8000, 0x70e4, 0x8000, 0x70e6,
+ 0x012e, 0x2009, 0x0002, 0x080c, 0xafbe, 0x9085, 0x0001, 0x00ce,
+ 0x00de, 0x007e, 0x001e, 0x0005, 0x00c6, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x2009, 0x007f, 0x080c, 0x6638, 0x11b8, 0xb813, 0x00ff,
+ 0xb817, 0xfffd, 0xb8cf, 0x0004, 0x080c, 0xaeed, 0x0170, 0x2b00,
+ 0x6012, 0x6316, 0x6023, 0x0001, 0x620a, 0x080c, 0xd102, 0x2009,
+ 0x0022, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e, 0x00de, 0x00ce,
+ 0x0005, 0x00e6, 0x00c6, 0x0066, 0x0036, 0x0026, 0x00b6, 0x21f0,
+ 0x080c, 0x9361, 0x080c, 0x92e1, 0x080c, 0xad81, 0x080c, 0xbe6b,
+ 0x3e08, 0x2130, 0x81ff, 0x0120, 0x20a9, 0x007e, 0x900e, 0x0018,
+ 0x20a9, 0x007f, 0x900e, 0x0016, 0x080c, 0x6699, 0x1140, 0x9686,
+ 0x0002, 0x1118, 0xb800, 0xd0bc, 0x1110, 0x080c, 0x60c7, 0x001e,
+ 0x8108, 0x1f04, 0x3203, 0x9686, 0x0001, 0x190c, 0x3347, 0x00be,
+ 0x002e, 0x003e, 0x006e, 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x00c6,
+ 0x0046, 0x0036, 0x0026, 0x0016, 0x00b6, 0x6210, 0x2258, 0xbaa0,
+ 0x0026, 0x2019, 0x0029, 0x080c, 0x9356, 0x0076, 0x2039, 0x0000,
+ 0x080c, 0x9229, 0x2c08, 0x080c, 0xe477, 0x007e, 0x001e, 0xba10,
+ 0xbb14, 0xbcc0, 0x080c, 0x60c7, 0xba12, 0xbb16, 0xbcc2, 0x00be,
+ 0x001e, 0x002e, 0x003e, 0x004e, 0x00ce, 0x00ee, 0x0005, 0x00e6,
+ 0x0006, 0x00b6, 0x6010, 0x2058, 0xb8a0, 0x00be, 0x9086, 0x0080,
+ 0x0150, 0x2071, 0x1800, 0x70a8, 0x9005, 0x0110, 0x8001, 0x70aa,
+ 0x000e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x70e4, 0x9005, 0x0dc0,
+ 0x8001, 0x70e6, 0x0ca8, 0xb800, 0xc08c, 0xb802, 0x0005, 0x00f6,
+ 0x00e6, 0x00c6, 0x00b6, 0x0046, 0x0036, 0x0026, 0x0016, 0x0156,
+ 0x2178, 0x81ff, 0x1118, 0x20a9, 0x0001, 0x0078, 0x080c, 0x575d,
+ 0xd0c4, 0x0140, 0xd0a4, 0x0130, 0x9006, 0x2020, 0x2009, 0x002d,
+ 0x080c, 0xe73a, 0x20a9, 0x0800, 0x9016, 0x0026, 0x928e, 0x007e,
+ 0x0904, 0x32ce, 0x928e, 0x007f, 0x0904, 0x32ce, 0x928e, 0x0080,
+ 0x05e8, 0x9288, 0x1000, 0x210c, 0x81ff, 0x05c0, 0x8fff, 0x1148,
+ 0x2001, 0x198d, 0x0006, 0x2003, 0x0001, 0x04f1, 0x000e, 0x2003,
+ 0x0000, 0x00b6, 0x00c6, 0x2158, 0x2001, 0x0001, 0x080c, 0x69d6,
+ 0x00ce, 0x00be, 0x2019, 0x0029, 0x080c, 0x9356, 0x0076, 0x2039,
+ 0x0000, 0x080c, 0x9229, 0x00b6, 0x00c6, 0x0026, 0x2158, 0xba04,
+ 0x9294, 0x00ff, 0x9286, 0x0006, 0x1118, 0xb807, 0x0404, 0x0028,
+ 0x2001, 0x0004, 0x8007, 0x9215, 0xba06, 0x002e, 0x00ce, 0x00be,
+ 0x0016, 0x2c08, 0x080c, 0xe477, 0x001e, 0x007e, 0x002e, 0x8210,
+ 0x1f04, 0x3285, 0x015e, 0x001e, 0x002e, 0x003e, 0x004e, 0x00be,
+ 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x0046, 0x0026, 0x0016, 0x080c,
+ 0x575d, 0xd0c4, 0x0140, 0xd0a4, 0x0130, 0x9006, 0x2220, 0x2009,
+ 0x0029, 0x080c, 0xe73a, 0x001e, 0x002e, 0x004e, 0x0005, 0x0016,
+ 0x0026, 0x0036, 0x00c6, 0x7294, 0x82ff, 0x01e8, 0x080c, 0x6a04,
+ 0x11d0, 0x2100, 0x080c, 0x28af, 0x81ff, 0x01b8, 0x2019, 0x0001,
+ 0x8314, 0x92e0, 0x1c80, 0x2c04, 0xd384, 0x0120, 0x9084, 0xff00,
+ 0x8007, 0x0010, 0x9084, 0x00ff, 0x9116, 0x0138, 0x9096, 0x00ff,
+ 0x0110, 0x8318, 0x0c68, 0x9085, 0x0001, 0x00ce, 0x003e, 0x002e,
+ 0x001e, 0x0005, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x0036,
+ 0x2019, 0x0029, 0x00a9, 0x003e, 0x9180, 0x1000, 0x2004, 0x9065,
+ 0x0158, 0x0016, 0x00c6, 0x2061, 0x1ab2, 0x001e, 0x6112, 0x080c,
+ 0x321e, 0x001e, 0x080c, 0x665d, 0x012e, 0x00ce, 0x001e, 0x0005,
+ 0x0016, 0x0026, 0x2110, 0x080c, 0xa8dc, 0x080c, 0xeaa3, 0x002e,
+ 0x001e, 0x0005, 0x2001, 0x1837, 0x2004, 0xd0cc, 0x0005, 0x00c6,
+ 0x00b6, 0x080c, 0x743e, 0x1118, 0x20a9, 0x0800, 0x0010, 0x20a9,
+ 0x0782, 0x080c, 0x743e, 0x1110, 0x900e, 0x0010, 0x2009, 0x007e,
+ 0x9180, 0x1000, 0x2004, 0x905d, 0x0130, 0x86ff, 0x0110, 0xb800,
+ 0xd0bc, 0x090c, 0x665d, 0x8108, 0x1f04, 0x3358, 0x2061, 0x1800,
+ 0x607f, 0x0000, 0x6080, 0x9084, 0x00ff, 0x6082, 0x60b3, 0x0000,
+ 0x00be, 0x00ce, 0x0005, 0x2001, 0x1869, 0x2004, 0xd0bc, 0x0005,
+ 0x2011, 0x1848, 0x2214, 0xd2ec, 0x0005, 0x0026, 0x2011, 0x1867,
+ 0x2214, 0xd2dc, 0x002e, 0x0005, 0x7eef, 0x7de8, 0x7ce4, 0x80e2,
+ 0x7be1, 0x80e0, 0x80dc, 0x80da, 0x7ad9, 0x80d6, 0x80d5, 0x80d4,
+ 0x80d3, 0x80d2, 0x80d1, 0x79ce, 0x78cd, 0x80cc, 0x80cb, 0x80ca,
+ 0x80c9, 0x80c7, 0x80c6, 0x77c5, 0x76c3, 0x80bc, 0x80ba, 0x75b9,
+ 0x80b6, 0x74b5, 0x73b4, 0x72b3, 0x80b2, 0x80b1, 0x80ae, 0x71ad,
+ 0x80ac, 0x70ab, 0x6faa, 0x6ea9, 0x80a7, 0x6da6, 0x6ca5, 0x6ba3,
+ 0x6a9f, 0x699e, 0x689d, 0x809b, 0x8098, 0x6797, 0x6690, 0x658f,
+ 0x6488, 0x6384, 0x6282, 0x8081, 0x8080, 0x617c, 0x607a, 0x8079,
+ 0x5f76, 0x8075, 0x8074, 0x8073, 0x8072, 0x8071, 0x806e, 0x5e6d,
+ 0x806c, 0x5d6b, 0x5c6a, 0x5b69, 0x8067, 0x5a66, 0x5965, 0x5863,
+ 0x575c, 0x565a, 0x5559, 0x8056, 0x8055, 0x5454, 0x5353, 0x5252,
+ 0x5151, 0x504e, 0x4f4d, 0x804c, 0x804b, 0x4e4a, 0x4d49, 0x8047,
+ 0x4c46, 0x8045, 0x8043, 0x803c, 0x803a, 0x8039, 0x8036, 0x4b35,
+ 0x8034, 0x4a33, 0x4932, 0x4831, 0x802e, 0x472d, 0x462c, 0x452b,
+ 0x442a, 0x4329, 0x4227, 0x8026, 0x8025, 0x4123, 0x401f, 0x3f1e,
+ 0x3e1d, 0x3d1b, 0x3c18, 0x8017, 0x8010, 0x3b0f, 0x3a08, 0x8004,
+ 0x3902, 0x8001, 0x8000, 0x8000, 0x3800, 0x3700, 0x3600, 0x8000,
+ 0x3500, 0x8000, 0x8000, 0x8000, 0x3400, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x3300, 0x3200, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x3100, 0x3000, 0x8000, 0x8000, 0x2f00,
+ 0x8000, 0x2e00, 0x2d00, 0x2c00, 0x8000, 0x8000, 0x8000, 0x2b00,
+ 0x8000, 0x2a00, 0x2900, 0x2800, 0x8000, 0x2700, 0x2600, 0x2500,
+ 0x2400, 0x2300, 0x2200, 0x8000, 0x8000, 0x2100, 0x2000, 0x1f00,
+ 0x1e00, 0x1d00, 0x1c00, 0x8000, 0x8000, 0x1b00, 0x1a00, 0x8000,
+ 0x1900, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x1800,
+ 0x8000, 0x1700, 0x1600, 0x1500, 0x8000, 0x1400, 0x1300, 0x1200,
+ 0x1100, 0x1000, 0x0f00, 0x8000, 0x8000, 0x0e00, 0x0d00, 0x0c00,
+ 0x0b00, 0x0a00, 0x0900, 0x8000, 0x8000, 0x0800, 0x0700, 0x8000,
+ 0x0600, 0x8000, 0x8000, 0x8000, 0x0500, 0x0400, 0x0300, 0x8000,
+ 0x0200, 0x8000, 0x8000, 0x8000, 0x0100, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x0000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x2071, 0x189e, 0x7003, 0x0002,
+ 0x9006, 0x7016, 0x701a, 0x704a, 0x704e, 0x700e, 0x7042, 0x7046,
+ 0x703b, 0x18ba, 0x703f, 0x18ba, 0x7007, 0x0001, 0x080c, 0x1018,
+ 0x090c, 0x0dd5, 0x2900, 0x706a, 0xa867, 0x0002, 0xa8ab, 0xdcb0,
+ 0x080c, 0x1018, 0x090c, 0x0dd5, 0x2900, 0x706e, 0xa867, 0x0002,
+ 0xa8ab, 0xdcb0, 0x0005, 0x2071, 0x189e, 0x7004, 0x0002, 0x34b3,
+ 0x34b4, 0x34c7, 0x34db, 0x0005, 0x1004, 0x34c4, 0x0e04, 0x34c4,
+ 0x2079, 0x0000, 0x0126, 0x2091, 0x8000, 0x700c, 0x9005, 0x1128,
+ 0x700f, 0x0001, 0x012e, 0x0468, 0x0005, 0x012e, 0x0ce8, 0x2079,
+ 0x0000, 0x2061, 0x18b8, 0x2c4c, 0xa86c, 0x908e, 0x0100, 0x0128,
+ 0x9086, 0x0200, 0x0904, 0x35af, 0x0005, 0x7018, 0x2048, 0x2061,
+ 0x1800, 0x701c, 0x0807, 0x7014, 0x2048, 0xa864, 0x9094, 0x00ff,
+ 0x9296, 0x0029, 0x1120, 0xaa78, 0xd2fc, 0x0128, 0x0005, 0x9086,
+ 0x0103, 0x0108, 0x0005, 0x2079, 0x0000, 0x2061, 0x1800, 0x701c,
+ 0x0807, 0x2061, 0x1800, 0x7880, 0x908a, 0x0040, 0x1210, 0x61d0,
+ 0x0042, 0x2100, 0x908a, 0x003f, 0x1a04, 0x35ac, 0x61d0, 0x0804,
+ 0x3541, 0x3583, 0x35bb, 0x35ac, 0x35c5, 0x35cf, 0x35d5, 0x35d9,
+ 0x35e9, 0x35ed, 0x3603, 0x3609, 0x360f, 0x361a, 0x3625, 0x3634,
+ 0x3643, 0x3651, 0x3668, 0x3683, 0x35ac, 0x372c, 0x376a, 0x3810,
+ 0x3821, 0x3844, 0x35ac, 0x35ac, 0x35ac, 0x387c, 0x3898, 0x38a1,
+ 0x38d0, 0x38d6, 0x35ac, 0x391c, 0x35ac, 0x35ac, 0x35ac, 0x35ac,
+ 0x35ac, 0x3927, 0x3930, 0x3938, 0x393a, 0x35ac, 0x35ac, 0x35ac,
+ 0x35ac, 0x35ac, 0x35ac, 0x3966, 0x35ac, 0x35ac, 0x35ac, 0x35ac,
+ 0x35ac, 0x3983, 0x39e4, 0x35ac, 0x35ac, 0x35ac, 0x35ac, 0x35ac,
+ 0x35ac, 0x0002, 0x3a0e, 0x3a11, 0x3a70, 0x3a89, 0x3ab9, 0x3d57,
+ 0x35ac, 0x5321, 0x35ac, 0x35ac, 0x35ac, 0x35ac, 0x35ac, 0x35ac,
+ 0x35ac, 0x35ac, 0x3603, 0x3609, 0x4278, 0x5781, 0x4296, 0x53b0,
+ 0x5401, 0x550c, 0x35ac, 0x556e, 0x55aa, 0x55db, 0x56e3, 0x5608,
+ 0x5663, 0x35ac, 0x429a, 0x445b, 0x4471, 0x4496, 0x44fb, 0x456f,
+ 0x458f, 0x4606, 0x4662, 0x46be, 0x46c1, 0x46e6, 0x4791, 0x47f7,
+ 0x47ff, 0x4931, 0x4aa9, 0x4add, 0x4d41, 0x35ac, 0x4d5f, 0x4e05,
+ 0x4ee7, 0x4f41, 0x35ac, 0x4ff8, 0x35ac, 0x5060, 0x507b, 0x47ff,
+ 0x52c1, 0x714c, 0x0000, 0x2021, 0x4000, 0x080c, 0x4b5b, 0x0126,
+ 0x2091, 0x8000, 0x0e04, 0x358d, 0x0010, 0x012e, 0x0cc0, 0x7c36,
+ 0x9486, 0x4000, 0x0118, 0x7833, 0x0011, 0x0010, 0x7833, 0x0010,
+ 0x7c82, 0x7986, 0x7a8a, 0x7b8e, 0x2091, 0x4080, 0x2001, 0x0089,
+ 0x2004, 0xd084, 0x190c, 0x119b, 0x7007, 0x0001, 0x2091, 0x5000,
+ 0x700f, 0x0000, 0x012e, 0x0005, 0x2021, 0x4001, 0x08b0, 0x2021,
+ 0x4002, 0x0898, 0x2021, 0x4003, 0x0880, 0x2021, 0x4005, 0x0868,
+ 0x2021, 0x4006, 0x0850, 0x2039, 0x0001, 0x902e, 0x2520, 0x7b88,
+ 0x7a8c, 0x7884, 0x7990, 0x0804, 0x4b68, 0x2039, 0x0001, 0x902e,
+ 0x2520, 0x7b88, 0x7a8c, 0x7884, 0x7990, 0x0804, 0x4b6b, 0x7984,
+ 0x7888, 0x2114, 0x200a, 0x0804, 0x3583, 0x7984, 0x2114, 0x0804,
+ 0x3583, 0x20e1, 0x0000, 0x2099, 0x0021, 0x20e9, 0x0000, 0x20a1,
+ 0x0021, 0x20a9, 0x001f, 0x4003, 0x7984, 0x7a88, 0x7b8c, 0x0804,
+ 0x3583, 0x7884, 0x2060, 0x0804, 0x3636, 0x2009, 0x0003, 0x2011,
+ 0x0003, 0x2019, 0x0008, 0x789b, 0x0137, 0x7893, 0xffff, 0x2001,
+ 0x188f, 0x2004, 0x9005, 0x0118, 0x7896, 0x0804, 0x3583, 0x7897,
+ 0x0001, 0x0804, 0x3583, 0x2039, 0x0001, 0x7d98, 0x7c9c, 0x0804,
+ 0x35bf, 0x2039, 0x0001, 0x7d98, 0x7c9c, 0x0804, 0x35c9, 0x79a0,
+ 0x9182, 0x0040, 0x0210, 0x0804, 0x35b8, 0x2138, 0x7d98, 0x7c9c,
+ 0x0804, 0x35bf, 0x79a0, 0x9182, 0x0040, 0x0210, 0x0804, 0x35b8,
+ 0x2138, 0x7d98, 0x7c9c, 0x0804, 0x35c9, 0x79a0, 0x9182, 0x0040,
+ 0x0210, 0x0804, 0x35b8, 0x21e8, 0x7984, 0x7888, 0x20a9, 0x0001,
+ 0x21a0, 0x4004, 0x0804, 0x3583, 0x2061, 0x0800, 0xe10c, 0x9006,
+ 0x2c15, 0x9200, 0x8c60, 0x8109, 0x1dd8, 0x2010, 0x9005, 0x0904,
+ 0x3583, 0x0804, 0x35b2, 0x79a0, 0x9182, 0x0040, 0x0210, 0x0804,
+ 0x35b8, 0x21e0, 0x20a9, 0x0001, 0x7984, 0x2198, 0x4012, 0x0804,
+ 0x3583, 0x2069, 0x1847, 0x7884, 0x7990, 0x911a, 0x1a04, 0x35b8,
+ 0x8019, 0x0904, 0x35b8, 0x684a, 0x6942, 0x788c, 0x6852, 0x7888,
+ 0x6856, 0x9006, 0x685a, 0x685e, 0x080c, 0x7755, 0x0804, 0x3583,
+ 0x2069, 0x1847, 0x7884, 0x7994, 0x911a, 0x1a04, 0x35b8, 0x8019,
+ 0x0904, 0x35b8, 0x684e, 0x6946, 0x788c, 0x6862, 0x7888, 0x6866,
+ 0x9006, 0x686a, 0x686e, 0x0126, 0x2091, 0x8000, 0x080c, 0x6a72,
+ 0x012e, 0x0804, 0x3583, 0x902e, 0x2520, 0x81ff, 0x0120, 0x2009,
+ 0x0001, 0x0804, 0x35b5, 0x7984, 0x7b88, 0x7a8c, 0x20a9, 0x0005,
+ 0x20e9, 0x0001, 0x20a1, 0x18a6, 0x4101, 0x080c, 0x4b1f, 0x1120,
+ 0x2009, 0x0002, 0x0804, 0x35b5, 0x2009, 0x0020, 0xa85c, 0x9080,
+ 0x0019, 0xaf60, 0x080c, 0x4b68, 0x701f, 0x36a7, 0x0005, 0xa864,
+ 0x2008, 0x9084, 0x00ff, 0x9096, 0x0011, 0x0168, 0x9096, 0x0019,
+ 0x0150, 0x9096, 0x0015, 0x0138, 0x9096, 0x0048, 0x0120, 0x9096,
+ 0x0029, 0x1904, 0x35b5, 0x810f, 0x918c, 0x00ff, 0x0904, 0x35b5,
+ 0x7112, 0x7010, 0x8001, 0x0560, 0x7012, 0x080c, 0x4b1f, 0x1120,
+ 0x2009, 0x0002, 0x0804, 0x35b5, 0x2009, 0x0020, 0x7068, 0x2040,
+ 0xa28c, 0xa390, 0xa494, 0xa598, 0x9290, 0x0040, 0x9399, 0x0000,
+ 0x94a1, 0x0000, 0x95a9, 0x0000, 0xa85c, 0x9080, 0x0019, 0xaf60,
+ 0x080c, 0x4b68, 0x701f, 0x36e5, 0x0005, 0xa864, 0x9084, 0x00ff,
+ 0x9096, 0x0002, 0x0120, 0x9096, 0x000a, 0x1904, 0x35b5, 0x0888,
+ 0x7014, 0x2048, 0xa868, 0xc0fd, 0xa86a, 0xa864, 0x9084, 0x00ff,
+ 0x9096, 0x0029, 0x1160, 0xc2fd, 0xaa7a, 0x080c, 0x621e, 0x0150,
+ 0x0126, 0x2091, 0x8000, 0xa87a, 0xa982, 0x012e, 0x0050, 0x080c,
+ 0x654e, 0x1128, 0x7007, 0x0003, 0x701f, 0x3711, 0x0005, 0x080c,
+ 0x6f4a, 0x0126, 0x2091, 0x8000, 0x20a9, 0x0005, 0x20e1, 0x0001,
+ 0x2099, 0x18a6, 0x400a, 0x2100, 0x9210, 0x9399, 0x0000, 0x94a1,
+ 0x0000, 0x95a9, 0x0000, 0xa85c, 0x9080, 0x0019, 0x2009, 0x0020,
+ 0x012e, 0xaf60, 0x0804, 0x4b6b, 0x2091, 0x8000, 0x7837, 0x4000,
+ 0x7833, 0x0010, 0x7883, 0x4000, 0x7887, 0x4953, 0x788b, 0x5020,
+ 0x788f, 0x2020, 0x2009, 0x017f, 0x2104, 0x7892, 0x3f00, 0x7896,
+ 0x2061, 0x0100, 0x6200, 0x2061, 0x0200, 0x603c, 0x8007, 0x9205,
+ 0x789a, 0x2009, 0x04fd, 0x2104, 0x789e, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x0180, 0x2001, 0x1a18,
+ 0x2004, 0x9005, 0x0128, 0x2001, 0x008b, 0x2004, 0xd0fc, 0x0dd8,
+ 0x2001, 0x008a, 0x2003, 0x0002, 0x2003, 0x1001, 0x2071, 0x0080,
+ 0x0804, 0x0427, 0x81ff, 0x1904, 0x35b5, 0x7984, 0x080c, 0x6699,
+ 0x1904, 0x35b8, 0x7e98, 0x9684, 0x3fff, 0x9082, 0x4000, 0x1a04,
+ 0x35b8, 0x7c88, 0x7d8c, 0x080c, 0x67fc, 0x080c, 0x67cb, 0x0000,
+ 0x1518, 0x2061, 0x1cd0, 0x0126, 0x2091, 0x8000, 0x6000, 0x9086,
+ 0x0000, 0x0148, 0x6014, 0x904d, 0x0130, 0xa86c, 0x9406, 0x1118,
+ 0xa870, 0x9506, 0x0150, 0x012e, 0x9ce0, 0x0018, 0x2001, 0x181a,
+ 0x2004, 0x9c02, 0x1a04, 0x35b5, 0x0c30, 0x080c, 0xc8a5, 0x012e,
+ 0x0904, 0x35b5, 0x0804, 0x3583, 0x900e, 0x2001, 0x0005, 0x080c,
+ 0x6f4a, 0x0126, 0x2091, 0x8000, 0x080c, 0xcf82, 0x080c, 0x6d17,
+ 0x012e, 0x0804, 0x3583, 0x00a6, 0x2950, 0xb198, 0x080c, 0x6699,
+ 0x1904, 0x37fd, 0xb6a4, 0x9684, 0x3fff, 0x9082, 0x4000, 0x16e8,
+ 0xb49c, 0xb5a0, 0x080c, 0x67fc, 0x080c, 0x67cb, 0x1520, 0x2061,
+ 0x1cd0, 0x0126, 0x2091, 0x8000, 0x6000, 0x9086, 0x0000, 0x0148,
+ 0x6014, 0x904d, 0x0130, 0xa86c, 0x9406, 0x1118, 0xa870, 0x9506,
+ 0x0158, 0x012e, 0x9ce0, 0x0018, 0x2001, 0x181a, 0x2004, 0x9c02,
+ 0x2009, 0x000d, 0x12b0, 0x0c28, 0x080c, 0xc8a5, 0x012e, 0x2009,
+ 0x0003, 0x0178, 0x00e0, 0x900e, 0x2001, 0x0005, 0x080c, 0x6f4a,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0xcf82, 0x080c, 0x6d0b, 0x012e,
+ 0x0070, 0xb097, 0x4005, 0xb19a, 0x0010, 0xb097, 0x4006, 0x900e,
+ 0x9085, 0x0001, 0x2001, 0x0030, 0x2a48, 0x00ae, 0x0005, 0xb097,
+ 0x4000, 0x9006, 0x918d, 0x0001, 0x2008, 0x2a48, 0x00ae, 0x0005,
+ 0x81ff, 0x1904, 0x35b5, 0x080c, 0x4b36, 0x0904, 0x35b8, 0x080c,
+ 0x6760, 0x0904, 0x35b5, 0x080c, 0x6802, 0x0904, 0x35b5, 0x0804,
+ 0x4586, 0x81ff, 0x1904, 0x35b5, 0x080c, 0x4b52, 0x0904, 0x35b8,
+ 0x080c, 0x6890, 0x0904, 0x35b5, 0x2019, 0x0005, 0x79a8, 0x080c,
+ 0x681d, 0x0904, 0x35b5, 0x7888, 0x908a, 0x1000, 0x1a04, 0x35b8,
+ 0x8003, 0x800b, 0x810b, 0x9108, 0x080c, 0x85be, 0x7984, 0xd184,
+ 0x1904, 0x3583, 0x0804, 0x4586, 0x0126, 0x2091, 0x8000, 0x81ff,
+ 0x0118, 0x2009, 0x0001, 0x0450, 0x2029, 0x07ff, 0x645c, 0x2400,
+ 0x9506, 0x01f8, 0x2508, 0x080c, 0x6699, 0x11d8, 0x080c, 0x6890,
+ 0x1128, 0x2009, 0x0002, 0x62c0, 0x2518, 0x00c0, 0x2019, 0x0004,
+ 0x900e, 0x080c, 0x681d, 0x1118, 0x2009, 0x0006, 0x0078, 0x7884,
+ 0x908a, 0x1000, 0x1270, 0x8003, 0x800b, 0x810b, 0x9108, 0x080c,
+ 0x85be, 0x8529, 0x1ae0, 0x012e, 0x0804, 0x3583, 0x012e, 0x0804,
+ 0x35b5, 0x012e, 0x0804, 0x35b8, 0x080c, 0x4b36, 0x0904, 0x35b8,
+ 0x080c, 0x6760, 0x0904, 0x35b5, 0xbaa0, 0x2019, 0x0005, 0x00c6,
+ 0x9066, 0x080c, 0x9356, 0x0076, 0x903e, 0x080c, 0x9229, 0x900e,
+ 0x080c, 0xe477, 0x007e, 0x00ce, 0x080c, 0x67fc, 0x0804, 0x3583,
+ 0x080c, 0x4b36, 0x0904, 0x35b8, 0x080c, 0x67fc, 0x2208, 0x0804,
+ 0x3583, 0x0156, 0x00d6, 0x00e6, 0x2069, 0x1910, 0x6810, 0x6914,
+ 0x910a, 0x1208, 0x900e, 0x6816, 0x9016, 0x901e, 0x20a9, 0x007e,
+ 0x2069, 0x1000, 0x2d04, 0x905d, 0x0118, 0xb84c, 0x0059, 0x9210,
+ 0x8d68, 0x1f04, 0x38b2, 0x2300, 0x9218, 0x00ee, 0x00de, 0x015e,
+ 0x0804, 0x3583, 0x00f6, 0x0016, 0x907d, 0x0138, 0x9006, 0x8000,
+ 0x2f0c, 0x81ff, 0x0110, 0x2178, 0x0cd0, 0x001e, 0x00fe, 0x0005,
+ 0x2069, 0x1910, 0x6910, 0x62bc, 0x0804, 0x3583, 0x81ff, 0x0120,
+ 0x2009, 0x0001, 0x0804, 0x35b5, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x5771, 0x0128, 0x2009, 0x0007, 0x012e, 0x0804, 0x35b5, 0x012e,
+ 0x615c, 0x9190, 0x3384, 0x2215, 0x9294, 0x00ff, 0x637c, 0x83ff,
+ 0x0108, 0x6280, 0x67dc, 0x97c4, 0x000a, 0x98c6, 0x000a, 0x1118,
+ 0x2031, 0x0001, 0x00e8, 0x97c4, 0x0022, 0x98c6, 0x0022, 0x1118,
+ 0x2031, 0x0003, 0x00a8, 0x97c4, 0x0012, 0x98c6, 0x0012, 0x1118,
+ 0x2031, 0x0002, 0x0068, 0x080c, 0x743e, 0x1118, 0x2031, 0x0004,
+ 0x0038, 0xd79c, 0x0120, 0x2009, 0x0005, 0x0804, 0x35b5, 0x9036,
+ 0x7e9a, 0x7f9e, 0x0804, 0x3583, 0x614c, 0x6250, 0x2019, 0x1985,
+ 0x231c, 0x2001, 0x1986, 0x2004, 0x789a, 0x0804, 0x3583, 0x0126,
+ 0x2091, 0x8000, 0x6138, 0x623c, 0x6340, 0x012e, 0x0804, 0x3583,
+ 0x080c, 0x4b52, 0x0904, 0x35b8, 0xba44, 0xbb38, 0x0804, 0x3583,
+ 0x080c, 0x0dd5, 0x080c, 0x4b52, 0x2110, 0x0904, 0x35b8, 0xb804,
+ 0x908c, 0x00ff, 0x918e, 0x0006, 0x0140, 0x9084, 0xff00, 0x9086,
+ 0x0600, 0x2009, 0x0009, 0x1904, 0x35b5, 0x0126, 0x2091, 0x8000,
+ 0x2019, 0x0005, 0x00c6, 0x9066, 0x080c, 0xa8dc, 0x080c, 0x9356,
+ 0x0076, 0x903e, 0x080c, 0x9229, 0x900e, 0x080c, 0xe477, 0x007e,
+ 0x00ce, 0xb807, 0x0407, 0x012e, 0x0804, 0x3583, 0x614c, 0x6250,
+ 0x7884, 0x604e, 0x7b88, 0x6352, 0x2069, 0x1847, 0x831f, 0x9305,
+ 0x6816, 0x788c, 0x2069, 0x1985, 0x2d1c, 0x206a, 0x7e98, 0x9682,
+ 0x0014, 0x1210, 0x2031, 0x07d0, 0x2069, 0x1986, 0x2d04, 0x266a,
+ 0x789a, 0x0804, 0x3583, 0x0126, 0x2091, 0x8000, 0x7884, 0x603a,
+ 0xd0c4, 0x01a8, 0x00d6, 0x78a8, 0x2009, 0x199c, 0x200a, 0x78ac,
+ 0x2011, 0x199d, 0x2012, 0x2069, 0x0100, 0x6838, 0x9086, 0x0007,
+ 0x1118, 0x2214, 0x6a5a, 0x0010, 0x210c, 0x695a, 0x00de, 0x7884,
+ 0xd0b4, 0x0120, 0x3b00, 0x9084, 0xff3f, 0x20d8, 0x7888, 0x603e,
+ 0x2011, 0x0114, 0x220c, 0x7888, 0xd08c, 0x0118, 0x918d, 0x0080,
+ 0x0010, 0x918c, 0xff7f, 0x2112, 0x788c, 0x6042, 0x9084, 0x0020,
+ 0x0130, 0x78b4, 0x6046, 0x9084, 0x0001, 0x090c, 0x4278, 0x6040,
+ 0xd0cc, 0x0120, 0x78b0, 0x2011, 0x0114, 0x2012, 0x012e, 0x0804,
+ 0x3583, 0x00f6, 0x2079, 0x1800, 0x7a38, 0xa898, 0x9084, 0xfebf,
+ 0x9215, 0xa89c, 0x9084, 0xfebf, 0x8002, 0x9214, 0x7838, 0x9084,
+ 0x0140, 0x9215, 0x7a3a, 0xa897, 0x4000, 0x900e, 0x9085, 0x0001,
+ 0x2001, 0x0000, 0x00fe, 0x0005, 0x7898, 0x9005, 0x01a8, 0x7888,
+ 0x9025, 0x0904, 0x35b8, 0x788c, 0x902d, 0x0904, 0x35b8, 0x900e,
+ 0x080c, 0x6699, 0x1120, 0xba44, 0xbb38, 0xbc46, 0xbd3a, 0x9186,
+ 0x07ff, 0x0190, 0x8108, 0x0ca0, 0x080c, 0x4b52, 0x0904, 0x35b8,
+ 0x7888, 0x900d, 0x0904, 0x35b8, 0x788c, 0x9005, 0x0904, 0x35b8,
+ 0xba44, 0xb946, 0xbb38, 0xb83a, 0x0804, 0x3583, 0x2011, 0xbc09,
+ 0x0010, 0x2011, 0xbc05, 0x080c, 0x5771, 0x1904, 0x35b5, 0x00c6,
+ 0x2061, 0x0100, 0x7984, 0x9186, 0x00ff, 0x1130, 0x2001, 0x1818,
+ 0x2004, 0x9085, 0xff00, 0x0088, 0x9182, 0x007f, 0x16e0, 0x9188,
+ 0x3384, 0x210d, 0x918c, 0x00ff, 0x2001, 0x1818, 0x2004, 0x0026,
+ 0x9116, 0x002e, 0x0580, 0x810f, 0x9105, 0x0126, 0x2091, 0x8000,
+ 0x0006, 0x080c, 0xaeed, 0x000e, 0x0510, 0x602e, 0x620a, 0x7984,
+ 0x00b6, 0x080c, 0x663e, 0x2b08, 0x00be, 0x1500, 0x6112, 0x6023,
+ 0x0001, 0x080c, 0x4b1f, 0x01d0, 0x9006, 0xa866, 0x7007, 0x0003,
+ 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x701f, 0x3a69, 0x2900, 0x6016,
+ 0x2009, 0x0032, 0x080c, 0xafbe, 0x012e, 0x00ce, 0x0005, 0x012e,
+ 0x00ce, 0x0804, 0x35b5, 0x00ce, 0x0804, 0x35b8, 0x080c, 0xaf43,
+ 0x0cb0, 0xa830, 0x9086, 0x0100, 0x0904, 0x35b5, 0x0804, 0x3583,
+ 0x2061, 0x1a70, 0x0126, 0x2091, 0x8000, 0x6000, 0xd084, 0x0170,
+ 0x6104, 0x6208, 0x2061, 0x1800, 0x6354, 0x6074, 0x789a, 0x60c0,
+ 0x789e, 0x60bc, 0x78aa, 0x012e, 0x0804, 0x3583, 0x900e, 0x2110,
+ 0x0c88, 0x81ff, 0x1904, 0x35b5, 0x080c, 0x743e, 0x0904, 0x35b5,
+ 0x0126, 0x2091, 0x8000, 0x6254, 0x6074, 0x9202, 0x0248, 0x9085,
+ 0x0001, 0x080c, 0x28e5, 0x080c, 0x5990, 0x012e, 0x0804, 0x3583,
+ 0x012e, 0x0804, 0x35b8, 0x0006, 0x0016, 0x00c6, 0x00e6, 0x2001,
+ 0x19a8, 0x2070, 0x2061, 0x1847, 0x6008, 0x2072, 0x900e, 0x2011,
+ 0x1400, 0x080c, 0x9027, 0x7206, 0x00ee, 0x00ce, 0x001e, 0x000e,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x81ff, 0x0128, 0x012e, 0x2021,
+ 0x400b, 0x0804, 0x3585, 0x7884, 0xd0fc, 0x0148, 0x2001, 0x002a,
+ 0x2004, 0x9082, 0x00e1, 0x0288, 0x012e, 0x0804, 0x35b8, 0x2001,
+ 0x002a, 0x2004, 0x2069, 0x1847, 0x6908, 0x9102, 0x1230, 0x012e,
+ 0x0804, 0x35b8, 0x012e, 0x0804, 0x35b5, 0x080c, 0xaead, 0x0dd0,
+ 0x7884, 0xd0fc, 0x0904, 0x3b34, 0x00c6, 0x080c, 0x4b1f, 0x00ce,
+ 0x0d88, 0xa867, 0x0000, 0x7884, 0xa80a, 0x7898, 0xa80e, 0x789c,
+ 0xa812, 0x2001, 0x002e, 0x2004, 0xa81a, 0x2001, 0x002f, 0x2004,
+ 0xa81e, 0x2001, 0x0030, 0x2004, 0xa822, 0x2001, 0x0031, 0x2004,
+ 0xa826, 0x2001, 0x0034, 0x2004, 0xa82a, 0x2001, 0x0035, 0x2004,
+ 0xa82e, 0x2001, 0x002a, 0x2004, 0x9080, 0x0003, 0x9084, 0x00fc,
+ 0x8004, 0xa816, 0x080c, 0x3cba, 0x0928, 0x7014, 0x2048, 0xad2c,
+ 0xac28, 0xab1c, 0xaa18, 0xa930, 0xa808, 0xd0b4, 0x1120, 0x2029,
+ 0x0000, 0x2021, 0x0000, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f,
+ 0x9084, 0xffc0, 0x9080, 0x001b, 0x080c, 0x4b68, 0x701f, 0x3bf7,
+ 0x7023, 0x0001, 0x012e, 0x0005, 0x0046, 0x0086, 0x0096, 0x00a6,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x080c, 0x3aa3, 0x2001,
+ 0x199e, 0x2003, 0x0000, 0x2021, 0x000a, 0x2061, 0x0100, 0x6104,
+ 0x0016, 0x60bb, 0x0000, 0x60bf, 0x32e1, 0x60bf, 0x0012, 0x080c,
+ 0x3d29, 0x080c, 0x3ce8, 0x00f6, 0x00e6, 0x0086, 0x2940, 0x2071,
+ 0x1a65, 0x2079, 0x0090, 0x00d6, 0x2069, 0x0000, 0x6884, 0xd0b4,
+ 0x0140, 0x2001, 0x0035, 0x2004, 0x780e, 0x2001, 0x0034, 0x2004,
+ 0x780a, 0x00de, 0x2011, 0x0001, 0x080c, 0x40bc, 0x008e, 0x00ee,
+ 0x00fe, 0x080c, 0x3fe9, 0x080c, 0x3eee, 0x05b8, 0x2001, 0x020b,
+ 0x2004, 0x9084, 0x0140, 0x1db8, 0x080c, 0x4130, 0x00f6, 0x2079,
+ 0x0300, 0x78bc, 0x00fe, 0x908c, 0x0070, 0x1560, 0x2071, 0x0200,
+ 0x7037, 0x0000, 0x7050, 0x9084, 0xff00, 0x9086, 0x3200, 0x1510,
+ 0x7037, 0x0001, 0x7050, 0x9084, 0xff00, 0x9086, 0xe100, 0x11d0,
+ 0x7037, 0x0000, 0x7054, 0x7037, 0x0000, 0x715c, 0x9106, 0x1190,
+ 0x2001, 0x1820, 0x2004, 0x9106, 0x1168, 0x00c6, 0x2061, 0x0100,
+ 0x6024, 0x9084, 0x1e00, 0x00ce, 0x0138, 0x080c, 0x3ef8, 0x080c,
+ 0x3ce3, 0x0058, 0x080c, 0x3ce3, 0x080c, 0x4054, 0x080c, 0x3fdf,
+ 0x2001, 0x020b, 0x2004, 0xd0e4, 0x0dd8, 0x2001, 0x032a, 0x2003,
+ 0x0004, 0x2061, 0x0100, 0x6027, 0x0002, 0x001e, 0x6106, 0x2011,
+ 0x020d, 0x2013, 0x0020, 0x60bb, 0x0000, 0x60bf, 0x0108, 0x60bf,
+ 0x0012, 0x2001, 0x0004, 0x200c, 0x918c, 0xfffd, 0x2102, 0x080c,
+ 0x12ed, 0x2009, 0x0028, 0x080c, 0x2410, 0x2001, 0x0227, 0x200c,
+ 0x2102, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e,
+ 0x008e, 0x004e, 0x2001, 0x199e, 0x2004, 0x9005, 0x1118, 0x012e,
+ 0x0804, 0x3583, 0x012e, 0x2021, 0x400c, 0x0804, 0x3585, 0x0016,
+ 0x0026, 0x0036, 0x0046, 0x0056, 0x0076, 0x0086, 0x0096, 0x00d6,
+ 0x0156, 0x7014, 0x2048, 0x7020, 0x20a8, 0x8000, 0x7022, 0xa804,
+ 0x9005, 0x0904, 0x3c53, 0x2048, 0x1f04, 0x3c07, 0x7068, 0x2040,
+ 0xa28c, 0xa390, 0xa494, 0xa598, 0xa930, 0xa808, 0xd0b4, 0x1120,
+ 0x2029, 0x0000, 0x2021, 0x0000, 0x0096, 0x7014, 0x2048, 0xa864,
+ 0x009e, 0x9086, 0x0103, 0x0170, 0x8906, 0x8006, 0x8007, 0x90bc,
+ 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b, 0x080c, 0x4b68, 0x701f,
+ 0x3bf7, 0x00b0, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x001b, 0x21a8, 0x27e0, 0x2098, 0x27e8, 0x20a0,
+ 0x0006, 0x080c, 0x0f7c, 0x000e, 0x080c, 0x4b6b, 0x701f, 0x3bf7,
+ 0x015e, 0x00de, 0x009e, 0x008e, 0x007e, 0x005e, 0x004e, 0x003e,
+ 0x002e, 0x001e, 0x0005, 0x7014, 0x2048, 0xa864, 0x9086, 0x0103,
+ 0x1118, 0x701f, 0x3cb8, 0x0450, 0x7014, 0x2048, 0xa868, 0xc0fd,
+ 0xa86a, 0x2009, 0x007f, 0x080c, 0x6638, 0x0110, 0x9006, 0x0030,
+ 0xb813, 0x00ff, 0xb817, 0xfffd, 0x080c, 0xd151, 0x015e, 0x00de,
+ 0x009e, 0x008e, 0x007e, 0x005e, 0x004e, 0x003e, 0x002e, 0x001e,
+ 0x0904, 0x35b5, 0x0016, 0x0026, 0x0036, 0x0046, 0x0056, 0x0076,
+ 0x0086, 0x0096, 0x00d6, 0x0156, 0x701f, 0x3c8a, 0x7007, 0x0003,
+ 0x0804, 0x3c48, 0xa830, 0x9086, 0x0100, 0x2021, 0x400c, 0x0904,
+ 0x3585, 0x0076, 0xad10, 0xac0c, 0xab24, 0xaa20, 0xa930, 0xa808,
+ 0xd0b4, 0x1120, 0x2029, 0x0000, 0x2021, 0x0000, 0x8906, 0x8006,
+ 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b, 0x21a8,
+ 0x27e0, 0x2098, 0x27e8, 0x20a0, 0x0006, 0x080c, 0x0f7c, 0x000e,
+ 0x080c, 0x4b6b, 0x007e, 0x701f, 0x3bf7, 0x7023, 0x0001, 0x0005,
+ 0x0804, 0x3583, 0x0156, 0x00c6, 0xa814, 0x908a, 0x001e, 0x0218,
+ 0xa833, 0x001e, 0x0010, 0xa832, 0x0078, 0x81ff, 0x0168, 0x0016,
+ 0x080c, 0x4b1f, 0x001e, 0x0130, 0xa800, 0x2040, 0xa008, 0xa80a,
+ 0x2100, 0x0c58, 0x9006, 0x0010, 0x9085, 0x0001, 0x00ce, 0x015e,
+ 0x0005, 0x0006, 0x00f6, 0x2079, 0x0000, 0x7880, 0x9086, 0x0044,
+ 0x00fe, 0x000e, 0x0005, 0x2001, 0x199e, 0x2003, 0x0001, 0x0005,
+ 0x00f6, 0x00e6, 0x00c6, 0x2061, 0x0200, 0x2001, 0x19a9, 0x2004,
+ 0x601a, 0x2061, 0x0100, 0x2001, 0x19a8, 0x2004, 0x60ce, 0x6104,
+ 0xc1ac, 0x6106, 0x080c, 0x4b1f, 0xa813, 0x0019, 0xa817, 0x0001,
+ 0x2900, 0xa85a, 0x2001, 0x002e, 0x2004, 0xa866, 0x2001, 0x002f,
+ 0x2004, 0xa86a, 0x2061, 0x0090, 0x2079, 0x0100, 0x2001, 0x19a8,
+ 0x2004, 0x6036, 0x2009, 0x0040, 0x080c, 0x2410, 0x2001, 0x002a,
+ 0x2004, 0x9084, 0xfff8, 0xa86e, 0x601a, 0xa873, 0x0000, 0x601f,
+ 0x0000, 0x78ca, 0x9006, 0x600a, 0x600e, 0x00ce, 0x00ee, 0x00fe,
+ 0x0005, 0x00e6, 0x080c, 0x4b1f, 0x2940, 0xa013, 0x0019, 0xa017,
+ 0x0001, 0x2800, 0xa05a, 0x2001, 0x0030, 0x2004, 0xa866, 0x2001,
+ 0x0031, 0x2004, 0xa86a, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8,
+ 0xa86e, 0xa873, 0x0000, 0x2001, 0x032a, 0x2003, 0x0004, 0x2001,
+ 0x0300, 0x2003, 0x0000, 0x2001, 0x020d, 0x2003, 0x0000, 0x2001,
+ 0x0004, 0x200c, 0x918d, 0x0002, 0x2102, 0x00ee, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x81ff, 0x0148, 0x080c, 0x2c6e, 0x1130, 0x9006,
+ 0x080c, 0x2bc6, 0x9006, 0x080c, 0x2ba9, 0x7884, 0x9084, 0x0007,
+ 0x0002, 0x3d74, 0x3d7d, 0x3d86, 0x3d71, 0x3d71, 0x3d71, 0x3d71,
+ 0x3d71, 0x012e, 0x0804, 0x35b8, 0x2009, 0x0114, 0x2104, 0x9085,
+ 0x0800, 0x200a, 0x080c, 0x3f42, 0x00c0, 0x2009, 0x0114, 0x2104,
+ 0x9085, 0x4000, 0x200a, 0x080c, 0x3f42, 0x0078, 0x080c, 0x743e,
+ 0x1128, 0x012e, 0x2009, 0x0016, 0x0804, 0x35b5, 0x81ff, 0x0128,
+ 0x012e, 0x2021, 0x400b, 0x0804, 0x3585, 0x0086, 0x0096, 0x00a6,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x080c, 0x3aa3, 0x2009,
+ 0x0101, 0x210c, 0x0016, 0x7ec8, 0x7dcc, 0x9006, 0x2068, 0x2060,
+ 0x2058, 0x080c, 0x420b, 0x080c, 0x415b, 0x903e, 0x2720, 0x00f6,
+ 0x00e6, 0x0086, 0x2940, 0x2071, 0x1a65, 0x2079, 0x0090, 0x00d6,
+ 0x2069, 0x0000, 0x6884, 0xd0b4, 0x0120, 0x68d4, 0x780e, 0x68d0,
+ 0x780a, 0x00de, 0x2011, 0x0001, 0x080c, 0x40bc, 0x080c, 0x2c76,
+ 0x080c, 0x2c76, 0x080c, 0x2c76, 0x080c, 0x2c76, 0x080c, 0x40bc,
+ 0x008e, 0x00ee, 0x00fe, 0x080c, 0x3fe9, 0x2009, 0x9c40, 0x8109,
+ 0x11b0, 0x080c, 0x3ef8, 0x2001, 0x0004, 0x200c, 0x918c, 0xfffd,
+ 0x2102, 0x001e, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae,
+ 0x009e, 0x008e, 0x2009, 0x0017, 0x080c, 0x35b5, 0x0cf8, 0x2001,
+ 0x020b, 0x2004, 0x9084, 0x0140, 0x1d10, 0x00f6, 0x2079, 0x0000,
+ 0x7884, 0x00fe, 0xd0bc, 0x0178, 0x2001, 0x0201, 0x200c, 0x81ff,
+ 0x0150, 0x080c, 0x3fc7, 0x2d00, 0x9c05, 0x9b05, 0x0120, 0x080c,
+ 0x3ef8, 0x0804, 0x3ea5, 0x080c, 0x4130, 0x080c, 0x4054, 0x080c,
+ 0x3faa, 0x080c, 0x3fdf, 0x00f6, 0x2079, 0x0100, 0x7824, 0xd0ac,
+ 0x0130, 0x8b58, 0x080c, 0x3ef8, 0x00fe, 0x0804, 0x3ea5, 0x00fe,
+ 0x080c, 0x3eee, 0x1150, 0x8d68, 0x2001, 0x0032, 0x2602, 0x2001,
+ 0x0033, 0x2502, 0x080c, 0x3ef8, 0x0080, 0x87ff, 0x0138, 0x2001,
+ 0x0201, 0x2004, 0x9005, 0x1908, 0x8739, 0x0038, 0x2001, 0x1a61,
+ 0x2004, 0x9086, 0x0000, 0x1904, 0x3df5, 0x2001, 0x032f, 0x2003,
+ 0x00f6, 0x8631, 0x1208, 0x8529, 0x2500, 0x9605, 0x0904, 0x3ea5,
+ 0x7884, 0xd0bc, 0x0128, 0x2d00, 0x9c05, 0x9b05, 0x1904, 0x3ea5,
+ 0xa013, 0x0019, 0x2001, 0x032a, 0x2003, 0x0004, 0x7884, 0xd0ac,
+ 0x1148, 0x2001, 0x1a61, 0x2003, 0x0003, 0x2001, 0x032a, 0x2003,
+ 0x0009, 0x0030, 0xa017, 0x0001, 0x78b4, 0x9005, 0x0108, 0xa016,
+ 0x2800, 0xa05a, 0x2009, 0x0040, 0x080c, 0x2410, 0x2900, 0xa85a,
+ 0xa813, 0x0019, 0x7884, 0xd0a4, 0x1180, 0xa817, 0x0000, 0x00c6,
+ 0x20a9, 0x0004, 0x2061, 0x0090, 0x602b, 0x0008, 0x2001, 0x0203,
+ 0x2004, 0x1f04, 0x3e7c, 0x00ce, 0x0030, 0xa817, 0x0001, 0x78b0,
+ 0x9005, 0x0108, 0xa816, 0x00f6, 0x00c6, 0x2079, 0x0100, 0x2061,
+ 0x0090, 0x7827, 0x0002, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8,
+ 0x601a, 0x0006, 0x2001, 0x002b, 0x2004, 0x601e, 0x78c6, 0x000e,
+ 0x78ca, 0x00ce, 0x00fe, 0x0804, 0x3daf, 0x001e, 0x00c6, 0x2001,
+ 0x032a, 0x2003, 0x0004, 0x2061, 0x0100, 0x6027, 0x0002, 0x6106,
+ 0x2011, 0x020d, 0x2013, 0x0020, 0x2001, 0x0004, 0x200c, 0x918c,
+ 0xfffd, 0x2102, 0x080c, 0x12ed, 0x7884, 0x9084, 0x0003, 0x9086,
+ 0x0002, 0x01a0, 0x2009, 0x0028, 0x080c, 0x2410, 0x2001, 0x0227,
+ 0x200c, 0x2102, 0x6050, 0x9084, 0xb7ef, 0x6052, 0x602f, 0x0000,
+ 0x604b, 0xf7f7, 0x6043, 0x0090, 0x6043, 0x0010, 0x00ce, 0x2d08,
+ 0x2c10, 0x2b18, 0x2b00, 0x9c05, 0x9d05, 0x00fe, 0x00ee, 0x00de,
+ 0x00ce, 0x00be, 0x00ae, 0x009e, 0x008e, 0x1118, 0x012e, 0x0804,
+ 0x3583, 0x012e, 0x2021, 0x400c, 0x0804, 0x3585, 0x9085, 0x0001,
+ 0x1d04, 0x3ef7, 0x2091, 0x6000, 0x8420, 0x9486, 0x0064, 0x0005,
+ 0x2001, 0x0105, 0x2003, 0x0010, 0x2001, 0x032a, 0x2003, 0x0004,
+ 0x2001, 0x1a61, 0x2003, 0x0000, 0x0071, 0x2009, 0x0048, 0x080c,
+ 0x2410, 0x2001, 0x0227, 0x2024, 0x2402, 0x2001, 0x0109, 0x2003,
+ 0x4000, 0x9026, 0x0005, 0x00f6, 0x00e6, 0x2071, 0x1a65, 0x7000,
+ 0x9086, 0x0000, 0x0520, 0x2079, 0x0090, 0x2009, 0x0206, 0x2104,
+ 0x2009, 0x0203, 0x210c, 0x9106, 0x1120, 0x2009, 0x0040, 0x080c,
+ 0x2410, 0x782c, 0xd0fc, 0x0d88, 0x080c, 0x4130, 0x7000, 0x9086,
+ 0x0000, 0x1d58, 0x782b, 0x0004, 0x782c, 0xd0ac, 0x1de8, 0x2009,
+ 0x0040, 0x080c, 0x2410, 0x782b, 0x0002, 0x7003, 0x0000, 0x00ee,
+ 0x00fe, 0x0005, 0x00f6, 0x2079, 0x0100, 0x2001, 0x1818, 0x200c,
+ 0x7932, 0x7936, 0x080c, 0x28c5, 0x7850, 0x9084, 0xfbff, 0x9085,
+ 0x0030, 0x7852, 0x2019, 0x01f4, 0x8319, 0x1df0, 0x9084, 0xffcf,
+ 0x9085, 0x2000, 0x7852, 0x20a9, 0x0046, 0x1d04, 0x3f5d, 0x2091,
+ 0x6000, 0x1f04, 0x3f5d, 0x7850, 0x9085, 0x0400, 0x9084, 0xdfff,
+ 0x7852, 0x2001, 0x0021, 0x2004, 0x9084, 0x0003, 0x9086, 0x0001,
+ 0x1120, 0x7850, 0x9084, 0xdfff, 0x7852, 0x784b, 0xf7f7, 0x7843,
+ 0x0090, 0x7843, 0x0010, 0x20a9, 0x0028, 0xa001, 0x1f04, 0x3f7d,
+ 0x7850, 0x9085, 0x1400, 0x7852, 0x2019, 0x61a8, 0x7854, 0xa001,
+ 0xa001, 0xd08c, 0x1110, 0x8319, 0x1dc8, 0x7827, 0x0048, 0x7850,
+ 0x9085, 0x0400, 0x7852, 0x7843, 0x0040, 0x2019, 0x01f4, 0xa001,
+ 0xa001, 0x8319, 0x1de0, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x7827,
+ 0x0020, 0x7843, 0x0000, 0x9006, 0x080c, 0x2d4e, 0x7827, 0x0048,
+ 0x00fe, 0x0005, 0x7884, 0xd0ac, 0x11c8, 0x00f6, 0x00e6, 0x2071,
+ 0x1a61, 0x2079, 0x0320, 0x2001, 0x0201, 0x2004, 0x9005, 0x0160,
+ 0x7000, 0x9086, 0x0000, 0x1140, 0x0051, 0xd0bc, 0x0108, 0x8738,
+ 0x7003, 0x0003, 0x782b, 0x0019, 0x00ee, 0x00fe, 0x0005, 0x00f6,
+ 0x2079, 0x0300, 0x78bc, 0x00fe, 0x908c, 0x0070, 0x0178, 0x2009,
+ 0x0032, 0x260a, 0x2009, 0x0033, 0x250a, 0xd0b4, 0x0108, 0x8c60,
+ 0xd0ac, 0x0108, 0x8d68, 0xd0a4, 0x0108, 0x8b58, 0x0005, 0x00f6,
+ 0x2079, 0x0200, 0x781c, 0xd084, 0x0110, 0x7837, 0x0050, 0x00fe,
+ 0x0005, 0x00e6, 0x2071, 0x0100, 0x2001, 0x19a9, 0x2004, 0x70e2,
+ 0x080c, 0x3cd9, 0x1188, 0x2001, 0x1820, 0x2004, 0x2009, 0x181f,
+ 0x210c, 0x918c, 0x00ff, 0x706e, 0x716a, 0x7066, 0x918d, 0x3200,
+ 0x7162, 0x7073, 0xe109, 0x0080, 0x702c, 0x9085, 0x0002, 0x702e,
+ 0x2009, 0x1818, 0x210c, 0x716e, 0x7063, 0x0100, 0x7166, 0x719e,
+ 0x706b, 0x0000, 0x7073, 0x0809, 0x7077, 0x0008, 0x7078, 0x9080,
+ 0x0100, 0x707a, 0x7080, 0x8000, 0x7082, 0x7087, 0xaaaa, 0x9006,
+ 0x708a, 0x708e, 0x707e, 0x70d6, 0x70ab, 0x0036, 0x70af, 0x95d5,
+ 0x7014, 0x9084, 0x1984, 0x9085, 0x0092, 0x7016, 0x080c, 0x4130,
+ 0x00f6, 0x2071, 0x1a61, 0x2079, 0x0320, 0x00d6, 0x2069, 0x0000,
+ 0x6884, 0xd0b4, 0x0120, 0x689c, 0x780e, 0x6898, 0x780a, 0x00de,
+ 0x2009, 0x03e8, 0x8109, 0x1df0, 0x792c, 0xd1fc, 0x0110, 0x782b,
+ 0x0004, 0x2011, 0x0011, 0x080c, 0x40bc, 0x2011, 0x0001, 0x080c,
+ 0x40bc, 0x00fe, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x2071, 0x1a61,
+ 0x2079, 0x0320, 0x792c, 0xd1fc, 0x0904, 0x40b9, 0x782b, 0x0002,
+ 0x9026, 0xd19c, 0x1904, 0x40b5, 0x7000, 0x0002, 0x40b9, 0x406a,
+ 0x409a, 0x40b5, 0xd1bc, 0x1170, 0xd1dc, 0x1190, 0x8001, 0x7002,
+ 0x2011, 0x0001, 0x080c, 0x40bc, 0x0904, 0x40b9, 0x080c, 0x40bc,
+ 0x0804, 0x40b9, 0x00f6, 0x2079, 0x0300, 0x78bf, 0x0000, 0x00fe,
+ 0x7810, 0x7914, 0x782b, 0x0004, 0x7812, 0x7916, 0x2001, 0x0201,
+ 0x200c, 0x81ff, 0x0de8, 0x080c, 0x3fc7, 0x2009, 0x0001, 0x00f6,
+ 0x2079, 0x0300, 0x78b8, 0x00fe, 0xd0ec, 0x0110, 0x2009, 0x0011,
+ 0x792a, 0x00f8, 0x8001, 0x7002, 0x9184, 0x0880, 0x1140, 0x782c,
+ 0xd0fc, 0x1904, 0x405e, 0x2011, 0x0001, 0x00b1, 0x0090, 0xa010,
+ 0x9092, 0x0004, 0x9086, 0x0015, 0x1120, 0xa000, 0xa05a, 0x2011,
+ 0x0031, 0xa212, 0xd1dc, 0x1960, 0x0828, 0x782b, 0x0004, 0x7003,
+ 0x0000, 0x00ee, 0x00fe, 0x0005, 0xa014, 0x9005, 0x0550, 0x8001,
+ 0x0036, 0x0096, 0xa016, 0xa058, 0x2048, 0xa010, 0x2009, 0x0031,
+ 0x911a, 0x831c, 0x831c, 0x938a, 0x0007, 0x1a0c, 0x0dd5, 0x9398,
+ 0x40ea, 0x231d, 0x083f, 0x9080, 0x0004, 0x7a2a, 0x7100, 0x8108,
+ 0x7102, 0x009e, 0x003e, 0x908a, 0x0035, 0x1140, 0x0096, 0xa058,
+ 0x2048, 0xa804, 0xa05a, 0x2001, 0x0019, 0x009e, 0xa012, 0x9085,
+ 0x0001, 0x0005, 0x4127, 0x411e, 0x4115, 0x410c, 0x4103, 0x40fa,
+ 0x40f1, 0xa964, 0x7902, 0xa968, 0x7906, 0xa96c, 0x7912, 0xa970,
+ 0x7916, 0x0005, 0xa974, 0x7902, 0xa978, 0x7906, 0xa97c, 0x7912,
+ 0xa980, 0x7916, 0x0005, 0xa984, 0x7902, 0xa988, 0x7906, 0xa98c,
+ 0x7912, 0xa990, 0x7916, 0x0005, 0xa994, 0x7902, 0xa998, 0x7906,
+ 0xa99c, 0x7912, 0xa9a0, 0x7916, 0x0005, 0xa9a4, 0x7902, 0xa9a8,
+ 0x7906, 0xa9ac, 0x7912, 0xa9b0, 0x7916, 0x0005, 0xa9b4, 0x7902,
+ 0xa9b8, 0x7906, 0xa9bc, 0x7912, 0xa9c0, 0x7916, 0x0005, 0xa9c4,
+ 0x7902, 0xa9c8, 0x7906, 0xa9cc, 0x7912, 0xa9d0, 0x7916, 0x0005,
+ 0x00f6, 0x00e6, 0x0086, 0x2071, 0x1a65, 0x2079, 0x0090, 0x792c,
+ 0xd1fc, 0x01e8, 0x782b, 0x0002, 0x2940, 0x9026, 0x7000, 0x0002,
+ 0x4157, 0x4143, 0x414e, 0x8001, 0x7002, 0xd19c, 0x1180, 0x2011,
+ 0x0001, 0x080c, 0x40bc, 0x190c, 0x40bc, 0x0048, 0x8001, 0x7002,
+ 0x782c, 0xd0fc, 0x1d38, 0x2011, 0x0001, 0x080c, 0x40bc, 0x008e,
+ 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0086, 0x2061,
+ 0x0200, 0x2001, 0x19a9, 0x2004, 0x601a, 0x2061, 0x0100, 0x2001,
+ 0x19a8, 0x2004, 0x60ce, 0x6104, 0xc1ac, 0x6106, 0x2001, 0x002c,
+ 0x2004, 0x9005, 0x0520, 0x2038, 0x2001, 0x002e, 0x2024, 0x2001,
+ 0x002f, 0x201c, 0x080c, 0x4b1f, 0xa813, 0x0019, 0xaf16, 0x2900,
+ 0xa85a, 0x978a, 0x0007, 0x0220, 0x2138, 0x2009, 0x0007, 0x0010,
+ 0x2708, 0x903e, 0x0096, 0xa858, 0x2048, 0xa85c, 0x9080, 0x0019,
+ 0x009e, 0x080c, 0x41d3, 0x1d68, 0x2900, 0xa85a, 0x00d0, 0x080c,
+ 0x4b1f, 0xa813, 0x0019, 0xa817, 0x0001, 0x2900, 0xa85a, 0x2001,
+ 0x002e, 0x2004, 0xa866, 0x2001, 0x002f, 0x2004, 0xa86a, 0x2001,
+ 0x002a, 0x2004, 0x9084, 0xfff8, 0xa86e, 0x2001, 0x002b, 0x2004,
+ 0xa872, 0x2061, 0x0090, 0x2079, 0x0100, 0x2001, 0x19a8, 0x2004,
+ 0x6036, 0x2009, 0x0040, 0x080c, 0x2410, 0x2001, 0x002a, 0x2004,
+ 0x9084, 0xfff8, 0x601a, 0x0006, 0x2001, 0x002b, 0x2004, 0x601e,
+ 0x78c6, 0x000e, 0x78ca, 0x9006, 0x600a, 0x600e, 0x008e, 0x00ce,
+ 0x00ee, 0x00fe, 0x0005, 0x00e6, 0x2071, 0x0080, 0xaa60, 0x22e8,
+ 0x20a0, 0x20e1, 0x0000, 0x2099, 0x0088, 0x702b, 0x0026, 0x7402,
+ 0x7306, 0x9006, 0x700a, 0x700e, 0x810b, 0x810b, 0x21a8, 0x810b,
+ 0x7112, 0x702b, 0x0041, 0x702c, 0xd0fc, 0x0de8, 0x702b, 0x0002,
+ 0x702b, 0x0040, 0x4005, 0x7400, 0x7304, 0x87ff, 0x0190, 0x0086,
+ 0x0096, 0x2940, 0x0086, 0x080c, 0x4b1f, 0x008e, 0xa058, 0x00a6,
+ 0x2050, 0x2900, 0xb006, 0xa05a, 0x00ae, 0x009e, 0x008e, 0x9085,
+ 0x0001, 0x00ee, 0x0005, 0x00e6, 0x2001, 0x002d, 0x2004, 0x9005,
+ 0x0528, 0x2038, 0x2001, 0x0030, 0x2024, 0x2001, 0x0031, 0x201c,
+ 0x080c, 0x4b1f, 0x2940, 0xa813, 0x0019, 0xaf16, 0x2900, 0xa85a,
+ 0x978a, 0x0007, 0x0220, 0x2138, 0x2009, 0x0007, 0x0010, 0x2708,
+ 0x903e, 0x0096, 0xa858, 0x2048, 0xa85c, 0x9080, 0x0019, 0x009e,
+ 0x080c, 0x41d3, 0x1d68, 0x2900, 0xa85a, 0x00d8, 0x080c, 0x4b1f,
+ 0x2940, 0xa013, 0x0019, 0xa017, 0x0001, 0x2800, 0xa05a, 0x2001,
+ 0x0030, 0x2004, 0xa066, 0x2001, 0x0031, 0x2004, 0xa06a, 0x2001,
+ 0x002a, 0x2004, 0x9084, 0xfff8, 0xa06e, 0x2001, 0x002b, 0x2004,
+ 0xa072, 0x2001, 0x032a, 0x2003, 0x0004, 0x7884, 0xd0ac, 0x1180,
+ 0x2001, 0x0101, 0x200c, 0x918d, 0x0200, 0x2102, 0xa017, 0x0000,
+ 0x2001, 0x1a61, 0x2003, 0x0003, 0x2001, 0x032a, 0x2003, 0x0009,
+ 0x2001, 0x0300, 0x2003, 0x0000, 0x2001, 0x020d, 0x2003, 0x0000,
+ 0x2001, 0x0004, 0x200c, 0x918d, 0x0002, 0x2102, 0x00ee, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x20a9, 0x0007, 0x20a1, 0x1840, 0x20e9,
+ 0x0001, 0x9006, 0x4004, 0x20a9, 0x0014, 0x20a1, 0xffec, 0x20e9,
+ 0x0000, 0x9006, 0x4004, 0x2009, 0x013c, 0x200a, 0x012e, 0x7880,
+ 0x9086, 0x0052, 0x0108, 0x0005, 0x0804, 0x3583, 0x7d98, 0x7c9c,
+ 0x0804, 0x3685, 0x080c, 0x743e, 0x190c, 0x6072, 0x6040, 0x9084,
+ 0x0020, 0x09b1, 0x2069, 0x1847, 0x2d00, 0x2009, 0x0030, 0x7a8c,
+ 0x7b88, 0x7c9c, 0x7d98, 0x2039, 0x0001, 0x080c, 0x4b68, 0x701f,
+ 0x42b2, 0x0005, 0x080c, 0x576c, 0x1130, 0x3b00, 0x3a08, 0xc194,
+ 0xc095, 0x20d8, 0x21d0, 0x2069, 0x1847, 0x6800, 0x9005, 0x0904,
+ 0x35b8, 0x6804, 0xd0ac, 0x0118, 0xd0a4, 0x0904, 0x35b8, 0xd094,
+ 0x00c6, 0x2061, 0x0100, 0x6104, 0x0138, 0x6200, 0x9292, 0x0005,
+ 0x0218, 0x918c, 0xffdf, 0x0010, 0x918d, 0x0020, 0x6106, 0x00ce,
+ 0xd08c, 0x00c6, 0x2061, 0x0100, 0x6104, 0x0118, 0x918d, 0x0010,
+ 0x0010, 0x918c, 0xffef, 0x6106, 0x00ce, 0xd084, 0x0158, 0x6a28,
+ 0x928a, 0x007f, 0x1a04, 0x35b8, 0x9288, 0x3384, 0x210d, 0x918c,
+ 0x00ff, 0x6166, 0xd0dc, 0x0130, 0x6828, 0x908a, 0x007f, 0x1a04,
+ 0x35b8, 0x605e, 0x6888, 0x9084, 0x0030, 0x8004, 0x8004, 0x8004,
+ 0x8004, 0x0006, 0x2009, 0x19b0, 0x9080, 0x29b8, 0x2005, 0x200a,
+ 0x000e, 0x2009, 0x19b1, 0x9080, 0x29bc, 0x2005, 0x200a, 0x6808,
+ 0x908a, 0x0100, 0x0a04, 0x35b8, 0x908a, 0x0841, 0x1a04, 0x35b8,
+ 0x9084, 0x0007, 0x1904, 0x35b8, 0x680c, 0x9005, 0x0904, 0x35b8,
+ 0x6810, 0x9005, 0x0904, 0x35b8, 0x6848, 0x6940, 0x910a, 0x1a04,
+ 0x35b8, 0x8001, 0x0904, 0x35b8, 0x684c, 0x6944, 0x910a, 0x1a04,
+ 0x35b8, 0x8001, 0x0904, 0x35b8, 0x2009, 0x1980, 0x200b, 0x0000,
+ 0x2001, 0x1869, 0x2004, 0xd0c4, 0x0140, 0x7884, 0x200a, 0x2009,
+ 0x017f, 0x200a, 0x3b00, 0xc085, 0x20d8, 0x6814, 0x908c, 0x00ff,
+ 0x614e, 0x8007, 0x9084, 0x00ff, 0x6052, 0x080c, 0x7755, 0x080c,
+ 0x6a3e, 0x080c, 0x6a72, 0x6808, 0x602a, 0x080c, 0x2382, 0x2009,
+ 0x0170, 0x200b, 0x0080, 0xa001, 0xa001, 0x200b, 0x0000, 0x0036,
+ 0x6b08, 0x080c, 0x291f, 0x003e, 0x6000, 0x9086, 0x0000, 0x1904,
+ 0x4449, 0x6818, 0x691c, 0x6a20, 0x6b24, 0x8007, 0x810f, 0x8217,
+ 0x831f, 0x6016, 0x611a, 0x621e, 0x6322, 0x6c04, 0xd4f4, 0x0148,
+ 0x6830, 0x6934, 0x6a38, 0x6b3c, 0x8007, 0x810f, 0x8217, 0x831f,
+ 0x0010, 0x9084, 0xf0ff, 0x6006, 0x610a, 0x620e, 0x6312, 0x8007,
+ 0x810f, 0x8217, 0x831f, 0x20a9, 0x0004, 0x20a1, 0x19b2, 0x20e9,
+ 0x0001, 0x4001, 0x20a9, 0x0004, 0x20a1, 0x19cc, 0x20e9, 0x0001,
+ 0x4001, 0x080c, 0x86ac, 0x00c6, 0x900e, 0x20a9, 0x0001, 0x6b70,
+ 0xd384, 0x0510, 0x0068, 0x2009, 0x0100, 0x210c, 0x918e, 0x0008,
+ 0x1110, 0x839d, 0x0010, 0x83f5, 0x3e18, 0x12b0, 0x3508, 0x8109,
+ 0x080c, 0x7d0c, 0x6878, 0x6016, 0x6874, 0x2008, 0x9084, 0xff00,
+ 0x8007, 0x600a, 0x9184, 0x00ff, 0x6006, 0x8108, 0x1118, 0x6003,
+ 0x0003, 0x0010, 0x6003, 0x0001, 0x1f04, 0x43a3, 0x00ce, 0x00c6,
+ 0x2061, 0x199b, 0x6a88, 0x9284, 0xc000, 0x2010, 0x9286, 0x0000,
+ 0x1158, 0x2063, 0x0000, 0x2001, 0x0001, 0x080c, 0x2bc6, 0x2001,
+ 0x0001, 0x080c, 0x2ba9, 0x0088, 0x9286, 0x4000, 0x1148, 0x2063,
+ 0x0001, 0x9006, 0x080c, 0x2bc6, 0x9006, 0x080c, 0x2ba9, 0x0028,
+ 0x9286, 0x8000, 0x1d30, 0x2063, 0x0002, 0x00ce, 0x6888, 0xd0ec,
+ 0x0130, 0x2011, 0x0114, 0x2204, 0x9085, 0x0100, 0x2012, 0x6a80,
+ 0x9284, 0x0030, 0x9086, 0x0030, 0x1128, 0x9294, 0xffcf, 0x9295,
+ 0x0020, 0x6a82, 0x2001, 0x197b, 0x6a80, 0x9294, 0x0030, 0x928e,
+ 0x0000, 0x0170, 0x928e, 0x0010, 0x0118, 0x928e, 0x0020, 0x0140,
+ 0x2003, 0xaaaa, 0x080c, 0x2994, 0x2001, 0x196c, 0x2102, 0x0008,
+ 0x2102, 0x00c6, 0x2061, 0x0100, 0x602f, 0x0040, 0x602f, 0x0000,
+ 0x00ce, 0x080c, 0x743e, 0x0128, 0x080c, 0x5054, 0x0110, 0x080c,
+ 0x28e5, 0x60d4, 0x9005, 0x01c0, 0x6003, 0x0001, 0x2009, 0x4431,
+ 0x00e0, 0x080c, 0x743e, 0x1168, 0x2011, 0x72ce, 0x080c, 0x85b0,
+ 0x2011, 0x72c1, 0x080c, 0x868a, 0x080c, 0x7729, 0x080c, 0x736a,
+ 0x0040, 0x080c, 0x5f6c, 0x0028, 0x6003, 0x0004, 0x2009, 0x4449,
+ 0x0020, 0x080c, 0x696e, 0x0804, 0x3583, 0x2001, 0x0170, 0x2004,
+ 0x9084, 0x00ff, 0x9086, 0x004c, 0x1118, 0x2091, 0x30bd, 0x0817,
+ 0x2091, 0x303d, 0x0817, 0x6000, 0x9086, 0x0000, 0x0904, 0x35b5,
+ 0x2069, 0x1847, 0x7890, 0x6842, 0x7894, 0x6846, 0x2d00, 0x2009,
+ 0x0030, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x2039, 0x0001, 0x0804,
+ 0x4b6b, 0x9006, 0x080c, 0x28e5, 0x81ff, 0x1904, 0x35b5, 0x080c,
+ 0x743e, 0x11b0, 0x080c, 0x7724, 0x080c, 0x60ad, 0x080c, 0x3378,
+ 0x0118, 0x6130, 0xc18d, 0x6132, 0x080c, 0xd388, 0x0130, 0x080c,
+ 0x7461, 0x1118, 0x080c, 0x7416, 0x0038, 0x080c, 0x736a, 0x0020,
+ 0x080c, 0x6072, 0x080c, 0x5f6c, 0x0804, 0x3583, 0x81ff, 0x1904,
+ 0x35b5, 0x080c, 0x743e, 0x1110, 0x0804, 0x35b5, 0x6194, 0x81ff,
+ 0x01a8, 0x704f, 0x0000, 0x2001, 0x1c80, 0x2009, 0x0040, 0x7a8c,
+ 0x7b88, 0x7c9c, 0x7d98, 0x0126, 0x2091, 0x8000, 0x2039, 0x0001,
+ 0x080c, 0x4b6b, 0x701f, 0x3581, 0x012e, 0x0005, 0x704f, 0x0001,
+ 0x00d6, 0x2069, 0x1c80, 0x20a9, 0x0040, 0x20e9, 0x0001, 0x20a1,
+ 0x1c80, 0x2019, 0xffff, 0x4304, 0x655c, 0x9588, 0x3384, 0x210d,
+ 0x918c, 0x00ff, 0x216a, 0x900e, 0x2011, 0x0002, 0x2100, 0x9506,
+ 0x01a8, 0x080c, 0x6699, 0x1190, 0xb814, 0x821c, 0x0238, 0x9398,
+ 0x1c80, 0x9085, 0xff00, 0x8007, 0x201a, 0x0038, 0x9398, 0x1c80,
+ 0x2324, 0x94a4, 0xff00, 0x9405, 0x201a, 0x8210, 0x8108, 0x9182,
+ 0x0080, 0x1208, 0x0c18, 0x8201, 0x8007, 0x2d0c, 0x9105, 0x206a,
+ 0x00de, 0x20a9, 0x0040, 0x20a1, 0x1c80, 0x2099, 0x1c80, 0x080c,
+ 0x5ffd, 0x0804, 0x44a3, 0x080c, 0x4b52, 0x0904, 0x35b8, 0x080c,
+ 0x4b1f, 0x1120, 0x2009, 0x0002, 0x0804, 0x35b5, 0x080c, 0x575d,
+ 0xd0b4, 0x0558, 0x7884, 0x908e, 0x007e, 0x0538, 0x908e, 0x007f,
+ 0x0520, 0x908e, 0x0080, 0x0508, 0x080c, 0x3373, 0x1148, 0xb800,
+ 0xd08c, 0x11d8, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x11a8,
+ 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xce51, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007, 0x0003, 0x701f, 0x4531,
+ 0x0005, 0x080c, 0x4b52, 0x0904, 0x35b8, 0x20a9, 0x002b, 0xb8c4,
+ 0x20e0, 0xb8c8, 0x2098, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0002,
+ 0x20a0, 0x4003, 0x20a9, 0x0008, 0x9080, 0x0006, 0x20a0, 0xb8c4,
+ 0x20e0, 0xb8c8, 0x9080, 0x0006, 0x2098, 0x080c, 0x0f7c, 0x0070,
+ 0x20a9, 0x0004, 0xa85c, 0x9080, 0x000a, 0x20a0, 0xb8c4, 0x20e0,
+ 0xb8c8, 0x9080, 0x000a, 0x2098, 0x080c, 0x0f7c, 0x8906, 0x8006,
+ 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002, 0x2009,
+ 0x002b, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804, 0x4b6b, 0x81ff,
+ 0x1904, 0x35b5, 0x080c, 0x4b36, 0x0904, 0x35b8, 0x080c, 0x680b,
+ 0x0904, 0x35b5, 0x0058, 0xa878, 0x9005, 0x0120, 0x2009, 0x0004,
+ 0x0804, 0x35b5, 0xa974, 0xaa94, 0x0804, 0x3583, 0x080c, 0x5765,
+ 0x0904, 0x3583, 0x701f, 0x457b, 0x7007, 0x0003, 0x0005, 0x81ff,
+ 0x1904, 0x35b5, 0x7888, 0x908a, 0x1000, 0x1a04, 0x35b8, 0x080c,
+ 0x4b52, 0x0904, 0x35b8, 0x080c, 0x6a0c, 0x0120, 0x080c, 0x6a14,
+ 0x1904, 0x35b8, 0x080c, 0x6890, 0x0904, 0x35b5, 0x2019, 0x0004,
+ 0x900e, 0x080c, 0x681d, 0x0904, 0x35b5, 0x7984, 0x7a88, 0x04c9,
+ 0x08a8, 0xa89c, 0x908a, 0x1000, 0x12f8, 0x080c, 0x4b50, 0x01e0,
+ 0x080c, 0x6a0c, 0x0118, 0x080c, 0x6a14, 0x11b0, 0x080c, 0x6890,
+ 0x2009, 0x0002, 0x0168, 0x2009, 0x0002, 0x2019, 0x0004, 0x080c,
+ 0x681d, 0x2009, 0x0003, 0x0120, 0xa998, 0xaa9c, 0x00d1, 0x0060,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x080c, 0x5765,
+ 0x0110, 0x9006, 0x0018, 0x900e, 0x9085, 0x0001, 0x2001, 0x0000,
+ 0x0005, 0x9186, 0x00ff, 0x0110, 0x0071, 0x0060, 0x2029, 0x007e,
+ 0x2061, 0x1800, 0x645c, 0x2400, 0x9506, 0x0110, 0x2508, 0x0019,
+ 0x8529, 0x1ec8, 0x0005, 0x080c, 0x6699, 0x1138, 0x2200, 0x8003,
+ 0x800b, 0x810b, 0x9108, 0x080c, 0x85be, 0x0005, 0x81ff, 0x1904,
+ 0x35b5, 0x798c, 0x2001, 0x197f, 0x918c, 0x8000, 0x2102, 0x080c,
+ 0x4b36, 0x0904, 0x35b8, 0x080c, 0x6a0c, 0x0120, 0x080c, 0x6a14,
+ 0x1904, 0x35b8, 0x080c, 0x6760, 0x0904, 0x35b5, 0x080c, 0x6814,
+ 0x0904, 0x35b5, 0x2001, 0x197f, 0x2004, 0xd0fc, 0x1904, 0x3583,
+ 0x0804, 0x4586, 0xa9a0, 0x2001, 0x197f, 0x918c, 0x8000, 0xc18d,
+ 0x2102, 0x080c, 0x4b43, 0x01a0, 0x080c, 0x6a0c, 0x0118, 0x080c,
+ 0x6a14, 0x1170, 0x080c, 0x6760, 0x2009, 0x0002, 0x0128, 0x080c,
+ 0x6814, 0x1170, 0x2009, 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010,
+ 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x0005,
+ 0xa897, 0x4000, 0x2001, 0x197f, 0x2004, 0xd0fc, 0x1128, 0x080c,
+ 0x5765, 0x0110, 0x9006, 0x0018, 0x900e, 0x9085, 0x0001, 0x2001,
+ 0x0000, 0x0005, 0x81ff, 0x1904, 0x35b5, 0x798c, 0x2001, 0x197e,
+ 0x918c, 0x8000, 0x2102, 0x080c, 0x4b36, 0x0904, 0x35b8, 0x080c,
+ 0x6a0c, 0x0120, 0x080c, 0x6a14, 0x1904, 0x35b8, 0x080c, 0x6760,
+ 0x0904, 0x35b5, 0x080c, 0x6802, 0x0904, 0x35b5, 0x2001, 0x197e,
+ 0x2004, 0xd0fc, 0x1904, 0x3583, 0x0804, 0x4586, 0xa9a0, 0x2001,
+ 0x197e, 0x918c, 0x8000, 0xc18d, 0x2102, 0x080c, 0x4b43, 0x01a0,
+ 0x080c, 0x6a0c, 0x0118, 0x080c, 0x6a14, 0x1170, 0x080c, 0x6760,
+ 0x2009, 0x0002, 0x0128, 0x080c, 0x6802, 0x1170, 0x2009, 0x0003,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x2001, 0x197e,
+ 0x2004, 0xd0fc, 0x1128, 0x080c, 0x5765, 0x0110, 0x9006, 0x0018,
+ 0x900e, 0x9085, 0x0001, 0x2001, 0x0000, 0x0005, 0x6100, 0x0804,
+ 0x3583, 0x080c, 0x4b52, 0x0904, 0x35b8, 0x080c, 0x5771, 0x1904,
+ 0x35b5, 0x79a8, 0xd184, 0x1158, 0xb834, 0x8007, 0x789e, 0xb830,
+ 0x8007, 0x789a, 0xbb2c, 0x831f, 0xba28, 0x8217, 0x0050, 0xb824,
+ 0x8007, 0x789e, 0xb820, 0x8007, 0x789a, 0xbb1c, 0x831f, 0xba18,
+ 0x8217, 0xb900, 0x918c, 0x0202, 0x0804, 0x3583, 0x78a8, 0x909c,
+ 0x0003, 0xd0ac, 0x1158, 0xd0b4, 0x1148, 0x939a, 0x0003, 0x1a04,
+ 0x35b5, 0x625c, 0x7884, 0x9206, 0x1904, 0x473b, 0x080c, 0x8696,
+ 0x2001, 0xffec, 0x2009, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98,
+ 0x2039, 0x0000, 0x0006, 0x78a8, 0x9084, 0x0080, 0x11f8, 0x0006,
+ 0x0036, 0x2001, 0x1a7f, 0x201c, 0x7b9a, 0x2003, 0x0000, 0x2001,
+ 0x1a80, 0x201c, 0x7b9e, 0x2003, 0x0000, 0x2001, 0x1a81, 0x201c,
+ 0x7ba2, 0x2003, 0x0000, 0x2001, 0x1a7b, 0x201c, 0x7baa, 0x2003,
+ 0x0000, 0x003e, 0x000e, 0x000e, 0x0804, 0x4b6b, 0x000e, 0x2031,
+ 0x0000, 0x2061, 0x18b8, 0x2c44, 0xa66a, 0xa17a, 0xa772, 0xa076,
+ 0xa28e, 0xa392, 0xa496, 0xa59a, 0x080c, 0x10e9, 0x7007, 0x0002,
+ 0x701f, 0x475b, 0x0005, 0x81ff, 0x1904, 0x35b5, 0x080c, 0x4b52,
+ 0x0904, 0x35b8, 0x080c, 0x6a0c, 0x1904, 0x35b5, 0x00c6, 0x080c,
+ 0x4b1f, 0x00ce, 0x0904, 0x35b5, 0xa867, 0x0000, 0xa868, 0xc0fd,
+ 0xa86a, 0x7ea8, 0x080c, 0xcdf7, 0x0904, 0x35b5, 0x7007, 0x0003,
+ 0x701f, 0x477b, 0x0005, 0x080c, 0x4278, 0x0006, 0x0036, 0x2001,
+ 0x1a7f, 0x201c, 0x7b9a, 0x2003, 0x0000, 0x2001, 0x1a80, 0x201c,
+ 0x7b9e, 0x2003, 0x0000, 0x2001, 0x1a81, 0x201c, 0x7ba2, 0x2003,
+ 0x0000, 0x2001, 0x1a7b, 0x201c, 0x7baa, 0x2003, 0x0000, 0x003e,
+ 0x000e, 0x0804, 0x3583, 0xa830, 0x9086, 0x0100, 0x0904, 0x35b5,
+ 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080,
+ 0x001b, 0x2009, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804,
+ 0x4b6b, 0x9006, 0x080c, 0x28e5, 0x78a8, 0x9084, 0x00ff, 0x9086,
+ 0x00ff, 0x0118, 0x81ff, 0x1904, 0x35b5, 0x080c, 0x743e, 0x0110,
+ 0x080c, 0x6072, 0x7888, 0x908a, 0x1000, 0x1a04, 0x35b8, 0x7984,
+ 0x9186, 0x00ff, 0x0138, 0x9182, 0x007f, 0x1a04, 0x35b8, 0x2100,
+ 0x080c, 0x28af, 0x0026, 0x00c6, 0x0126, 0x2091, 0x8000, 0x2061,
+ 0x19f9, 0x601b, 0x0000, 0x601f, 0x0000, 0x6073, 0x0000, 0x6077,
+ 0x0000, 0x080c, 0x743e, 0x1158, 0x080c, 0x7724, 0x080c, 0x60ad,
+ 0x9085, 0x0001, 0x080c, 0x7485, 0x080c, 0x736a, 0x00d0, 0x080c,
+ 0xaeb4, 0x2061, 0x0100, 0x2001, 0x1818, 0x2004, 0x9084, 0x00ff,
+ 0x810f, 0x9105, 0x604a, 0x6043, 0x0090, 0x6043, 0x0010, 0x2009,
+ 0x1998, 0x200b, 0x0000, 0x2009, 0x002d, 0x2011, 0x5f98, 0x080c,
+ 0x8648, 0x7984, 0x080c, 0x743e, 0x1110, 0x2009, 0x00ff, 0x7a88,
+ 0x080c, 0x45e9, 0x012e, 0x00ce, 0x002e, 0x0804, 0x3583, 0x7984,
+ 0x080c, 0x6638, 0x2b08, 0x1904, 0x35b8, 0x0804, 0x3583, 0x81ff,
+ 0x0120, 0x2009, 0x0001, 0x0804, 0x35b5, 0x60dc, 0xd0ac, 0x1130,
+ 0xd09c, 0x1120, 0x2009, 0x0005, 0x0804, 0x35b5, 0x080c, 0x4b1f,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x35b5, 0x7984, 0x9192, 0x0021,
+ 0x1a04, 0x35b8, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080,
+ 0x0019, 0x702a, 0xaf60, 0x7736, 0x080c, 0x4b68, 0x701f, 0x482f,
+ 0x7880, 0x9086, 0x006e, 0x0110, 0x701f, 0x5206, 0x0005, 0x2009,
+ 0x0080, 0x080c, 0x6699, 0x1118, 0x080c, 0x6a0c, 0x0120, 0x2021,
+ 0x400a, 0x0804, 0x3585, 0x00d6, 0x0096, 0xa964, 0xaa6c, 0xab70,
+ 0xac74, 0xad78, 0xae7c, 0xa884, 0x90be, 0x0100, 0x0904, 0x48c8,
+ 0x90be, 0x0112, 0x0904, 0x48c8, 0x90be, 0x0113, 0x0904, 0x48c8,
+ 0x90be, 0x0114, 0x0904, 0x48c8, 0x90be, 0x0117, 0x0904, 0x48c8,
+ 0x90be, 0x011a, 0x0904, 0x48c8, 0x90be, 0x011c, 0x0904, 0x48c8,
+ 0x90be, 0x0121, 0x0904, 0x48af, 0x90be, 0x0131, 0x0904, 0x48af,
+ 0x90be, 0x0171, 0x0904, 0x48c8, 0x90be, 0x0173, 0x0904, 0x48c8,
+ 0x90be, 0x01a1, 0x1128, 0xa894, 0x8007, 0xa896, 0x0804, 0x48d3,
+ 0x90be, 0x0212, 0x0904, 0x48bc, 0x90be, 0x0213, 0x05e8, 0x90be,
+ 0x0214, 0x0500, 0x90be, 0x0217, 0x0188, 0x90be, 0x021a, 0x1120,
+ 0xa89c, 0x8007, 0xa89e, 0x04e0, 0x90be, 0x021f, 0x05c8, 0x90be,
+ 0x0300, 0x05b0, 0x009e, 0x00de, 0x0804, 0x35b8, 0x7028, 0x9080,
+ 0x0010, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9, 0x0007,
+ 0x080c, 0x4911, 0x7028, 0x9080, 0x000e, 0x2098, 0x20a0, 0x7034,
+ 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x080c, 0x4911, 0x00c8, 0x7028,
+ 0x9080, 0x000c, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9,
+ 0x0001, 0x080c, 0x491e, 0x00b8, 0x7028, 0x9080, 0x000e, 0x2098,
+ 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x080c, 0x491e,
+ 0x7028, 0x9080, 0x000c, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8,
+ 0x20a9, 0x0001, 0x04f1, 0x00c6, 0x080c, 0x4b1f, 0x0550, 0xa868,
+ 0xc0fd, 0xa86a, 0xa867, 0x0119, 0x9006, 0xa882, 0xa87f, 0x0020,
+ 0xa88b, 0x0001, 0x810b, 0xa9ae, 0xa8b2, 0xaab6, 0xabba, 0xacbe,
+ 0xadc2, 0xa9c6, 0xa8ca, 0x00ce, 0x009e, 0x00de, 0xa866, 0xa822,
+ 0xa868, 0xc0fd, 0xa86a, 0xa804, 0x2048, 0x080c, 0xce12, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007, 0x0003, 0x701f, 0x4908,
+ 0x0005, 0x00ce, 0x009e, 0x00de, 0x2009, 0x0002, 0x0804, 0x35b5,
+ 0xa820, 0x9086, 0x8001, 0x1904, 0x3583, 0x2009, 0x0004, 0x0804,
+ 0x35b5, 0x0016, 0x0026, 0x3510, 0x20a9, 0x0002, 0x4002, 0x4104,
+ 0x4004, 0x8211, 0x1dc8, 0x002e, 0x001e, 0x0005, 0x0016, 0x0026,
+ 0x0036, 0x0046, 0x3520, 0x20a9, 0x0004, 0x4002, 0x4304, 0x4204,
+ 0x4104, 0x4004, 0x8421, 0x1db8, 0x004e, 0x003e, 0x002e, 0x001e,
+ 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x35b5, 0x60dc,
+ 0xd0ac, 0x1130, 0xd09c, 0x1120, 0x2009, 0x0005, 0x0804, 0x35b5,
+ 0x7984, 0x78a8, 0x2040, 0x080c, 0xaead, 0x1120, 0x9182, 0x007f,
+ 0x0a04, 0x35b8, 0x9186, 0x00ff, 0x0904, 0x35b8, 0x9182, 0x0800,
+ 0x1a04, 0x35b8, 0x7a8c, 0x7b88, 0x607c, 0x9306, 0x1158, 0x6080,
+ 0x924e, 0x0904, 0x35b8, 0x080c, 0xaead, 0x1120, 0x99cc, 0xff00,
+ 0x0904, 0x35b8, 0x0126, 0x2091, 0x8000, 0x080c, 0x4a32, 0x0904,
+ 0x49b2, 0x0086, 0x90c6, 0x4000, 0x008e, 0x1538, 0x00c6, 0x0006,
+ 0x0036, 0xb818, 0xbb1c, 0x9305, 0xbb20, 0x9305, 0xbb24, 0x9305,
+ 0xbb28, 0x9305, 0xbb2c, 0x9305, 0xbb30, 0x9305, 0xbb34, 0x9305,
+ 0x003e, 0x0570, 0xd88c, 0x1128, 0x080c, 0x6a0c, 0x0110, 0xc89d,
+ 0x0438, 0x900e, 0x080c, 0x68b9, 0x1108, 0xc185, 0xb800, 0xd0bc,
+ 0x0108, 0xc18d, 0x000e, 0x00ce, 0x00b8, 0x90c6, 0x4007, 0x1110,
+ 0x2408, 0x0090, 0x90c6, 0x4008, 0x1118, 0x2708, 0x2610, 0x0060,
+ 0x90c6, 0x4009, 0x1108, 0x0040, 0x90c6, 0x4006, 0x1108, 0x0020,
+ 0x2001, 0x4005, 0x2009, 0x000a, 0x2020, 0x012e, 0x0804, 0x3585,
+ 0x000e, 0x00ce, 0x2b00, 0x7026, 0x0016, 0x00b6, 0x00c6, 0x00e6,
+ 0x2c70, 0x080c, 0xaf91, 0x0904, 0x4a07, 0x2b00, 0x6012, 0x080c,
+ 0xd102, 0x2e58, 0x00ee, 0x00e6, 0x00c6, 0x080c, 0x4b1f, 0x00ce,
+ 0x2b70, 0x1158, 0x080c, 0xaf43, 0x00ee, 0x00ce, 0x00be, 0x001e,
+ 0x012e, 0x2009, 0x0002, 0x0804, 0x35b5, 0x900e, 0xa966, 0xa96a,
+ 0x2900, 0x6016, 0xa932, 0xa868, 0xc0fd, 0xd88c, 0x0108, 0xc0f5,
+ 0xa86a, 0xd89c, 0x1110, 0x080c, 0x321e, 0x6023, 0x0001, 0x9006,
+ 0x080c, 0x65d5, 0xd89c, 0x0138, 0x2001, 0x0004, 0x080c, 0x65e9,
+ 0x2009, 0x0003, 0x0030, 0x2001, 0x0002, 0x080c, 0x65e9, 0x2009,
+ 0x0002, 0x080c, 0xafbe, 0x78a8, 0xd094, 0x0138, 0x00ee, 0x7024,
+ 0x00e6, 0x2058, 0xb8cc, 0xc08d, 0xb8ce, 0x9085, 0x0001, 0x00ee,
+ 0x00ce, 0x00be, 0x001e, 0x012e, 0x1120, 0x2009, 0x0003, 0x0804,
+ 0x35b5, 0x7007, 0x0003, 0x701f, 0x4a16, 0x0005, 0xa830, 0x9086,
+ 0x0100, 0x7024, 0x2058, 0x1138, 0x2009, 0x0004, 0xba04, 0x9294,
+ 0x00ff, 0x0804, 0x56b1, 0x900e, 0xa868, 0xd0f4, 0x1904, 0x3583,
+ 0x080c, 0x68b9, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d,
+ 0x0804, 0x3583, 0x00e6, 0x00d6, 0x0096, 0x83ff, 0x0904, 0x4a81,
+ 0x902e, 0x080c, 0xaead, 0x0130, 0x9026, 0x20a9, 0x0800, 0x2071,
+ 0x1000, 0x0030, 0x2021, 0x007f, 0x20a9, 0x0781, 0x2071, 0x107f,
+ 0x2e04, 0x9005, 0x11b8, 0x2100, 0x9406, 0x1904, 0x4a92, 0x2428,
+ 0x94ce, 0x007f, 0x1120, 0x92ce, 0xfffd, 0x1558, 0x0030, 0x94ce,
+ 0x0080, 0x1130, 0x92ce, 0xfffc, 0x1520, 0x93ce, 0x00ff, 0x1508,
+ 0xc5fd, 0x0480, 0x2058, 0xbf10, 0x2700, 0x9306, 0x11e8, 0xbe14,
+ 0x2600, 0x9206, 0x11c8, 0x2400, 0x9106, 0x1180, 0xd884, 0x0598,
+ 0xd894, 0x1588, 0x080c, 0x69ac, 0x1570, 0x2001, 0x4000, 0x0460,
+ 0x080c, 0x6a0c, 0x1540, 0x2001, 0x4000, 0x0430, 0x2001, 0x4007,
+ 0x0418, 0x2001, 0x4006, 0x0400, 0x2400, 0x9106, 0x1158, 0xbe14,
+ 0x87ff, 0x1128, 0x86ff, 0x0918, 0x080c, 0xaead, 0x1900, 0x2001,
+ 0x4008, 0x0090, 0x8420, 0x8e70, 0x1f04, 0x4a48, 0x85ff, 0x1130,
+ 0x2001, 0x4009, 0x0048, 0x2001, 0x0001, 0x0030, 0x080c, 0x6638,
+ 0x1dd0, 0xbb12, 0xba16, 0x9006, 0x9005, 0x009e, 0x00de, 0x00ee,
+ 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x35b5, 0x080c,
+ 0x4b1f, 0x1120, 0x2009, 0x0002, 0x0804, 0x35b5, 0xa867, 0x0000,
+ 0xa868, 0xc0fd, 0xa86a, 0x7884, 0x9005, 0x0904, 0x35b8, 0x9096,
+ 0x00ff, 0x0120, 0x9092, 0x0004, 0x1a04, 0x35b8, 0x2010, 0x2918,
+ 0x080c, 0x31c4, 0x1120, 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007,
+ 0x0003, 0x701f, 0x4ad4, 0x0005, 0xa830, 0x9086, 0x0100, 0x1904,
+ 0x3583, 0x2009, 0x0004, 0x0804, 0x35b5, 0x7984, 0x080c, 0xaead,
+ 0x1120, 0x9182, 0x007f, 0x0a04, 0x35b8, 0x9186, 0x00ff, 0x0904,
+ 0x35b8, 0x9182, 0x0800, 0x1a04, 0x35b8, 0x2001, 0x9400, 0x080c,
+ 0x570c, 0x1904, 0x35b5, 0x0804, 0x3583, 0xa998, 0x080c, 0xaead,
+ 0x1118, 0x9182, 0x007f, 0x0280, 0x9186, 0x00ff, 0x0168, 0x9182,
+ 0x0800, 0x1250, 0x2001, 0x9400, 0x080c, 0x570c, 0x11a8, 0x0060,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0000, 0x0005, 0x2009, 0x000a, 0x0c48, 0x080c,
+ 0x0fff, 0x0198, 0x9006, 0xa802, 0x7014, 0x9005, 0x1120, 0x2900,
+ 0x7016, 0x701a, 0x0040, 0x7018, 0xa802, 0x0086, 0x2040, 0x2900,
+ 0xa006, 0x701a, 0x008e, 0x9085, 0x0001, 0x0005, 0x7984, 0x080c,
+ 0x6699, 0x1130, 0x7e88, 0x9684, 0x3fff, 0x9082, 0x4000, 0x0208,
+ 0x905e, 0x8bff, 0x0005, 0xa998, 0x080c, 0x6699, 0x1130, 0xae9c,
+ 0x9684, 0x3fff, 0x9082, 0x4000, 0x0208, 0x905e, 0x8bff, 0x0005,
+ 0xae98, 0x0008, 0x7e84, 0x2608, 0x080c, 0x6699, 0x1108, 0x0008,
+ 0x905e, 0x8bff, 0x0005, 0x0016, 0x7114, 0x81ff, 0x0128, 0x2148,
+ 0xa904, 0x080c, 0x1031, 0x0cc8, 0x7116, 0x711a, 0x001e, 0x0005,
+ 0x2031, 0x0001, 0x0010, 0x2031, 0x0000, 0x2061, 0x18b8, 0x2c44,
+ 0xa66a, 0xa17a, 0xa772, 0xa076, 0xa28e, 0xa392, 0xa496, 0xa59a,
+ 0x080c, 0x10e9, 0x7007, 0x0002, 0x701f, 0x3583, 0x0005, 0x00f6,
+ 0x0126, 0x2091, 0x8000, 0x2079, 0x0000, 0x2001, 0x18b0, 0x2004,
+ 0x9005, 0x1190, 0x0e04, 0x4b9c, 0x7a36, 0x7833, 0x0012, 0x7a82,
+ 0x7b86, 0x7c8a, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x119b, 0x0804, 0x4c02, 0x0016, 0x0086, 0x0096, 0x00c6,
+ 0x00e6, 0x2071, 0x189e, 0x7044, 0x9005, 0x1540, 0x7148, 0x9182,
+ 0x0010, 0x0288, 0x7038, 0x2060, 0x080c, 0x0fff, 0x0904, 0x4bfa,
+ 0xa84b, 0x0000, 0x2900, 0x7046, 0x2001, 0x0002, 0x9080, 0x20ce,
+ 0x2005, 0xa846, 0x0098, 0x7038, 0x90e0, 0x0004, 0x2001, 0x18ba,
+ 0x9c82, 0x18fa, 0x0210, 0x2061, 0x18ba, 0x2c00, 0x703a, 0x7148,
+ 0x81ff, 0x1108, 0x703e, 0x8108, 0x714a, 0x0460, 0x7148, 0x8108,
+ 0x714a, 0x7044, 0x2040, 0xa144, 0x2105, 0x0016, 0x908a, 0x0036,
+ 0x1a0c, 0x0dd5, 0x2060, 0x001e, 0x8108, 0x2105, 0x9005, 0xa146,
+ 0x1520, 0x080c, 0x0fff, 0x1130, 0x8109, 0xa946, 0x7148, 0x8109,
+ 0x714a, 0x00d8, 0x9006, 0xa806, 0xa84a, 0xa046, 0x2800, 0xa802,
+ 0x2900, 0xa006, 0x7046, 0x2001, 0x0002, 0x9080, 0x20ce, 0x2005,
+ 0xa846, 0x0058, 0x2262, 0x6306, 0x640a, 0x00ee, 0x00ce, 0x009e,
+ 0x008e, 0x001e, 0x012e, 0x00fe, 0x0005, 0x2c00, 0x9082, 0x001b,
+ 0x0002, 0x4c24, 0x4c24, 0x4c26, 0x4c24, 0x4c24, 0x4c24, 0x4c2a,
+ 0x4c24, 0x4c24, 0x4c24, 0x4c2e, 0x4c24, 0x4c24, 0x4c24, 0x4c32,
+ 0x4c24, 0x4c24, 0x4c24, 0x4c36, 0x4c24, 0x4c24, 0x4c24, 0x4c3a,
+ 0x4c24, 0x4c24, 0x4c24, 0x4c3f, 0x080c, 0x0dd5, 0xa276, 0xa37a,
+ 0xa47e, 0x0898, 0xa286, 0xa38a, 0xa48e, 0x0878, 0xa296, 0xa39a,
+ 0xa49e, 0x0858, 0xa2a6, 0xa3aa, 0xa4ae, 0x0838, 0xa2b6, 0xa3ba,
+ 0xa4be, 0x0818, 0xa2c6, 0xa3ca, 0xa4ce, 0x0804, 0x4bfd, 0xa2d6,
+ 0xa3da, 0xa4de, 0x0804, 0x4bfd, 0x00e6, 0x2071, 0x189e, 0x7048,
+ 0x9005, 0x0904, 0x4cd6, 0x0126, 0x2091, 0x8000, 0x0e04, 0x4cd5,
+ 0x00f6, 0x2079, 0x0000, 0x00c6, 0x0096, 0x0086, 0x0076, 0x9006,
+ 0x2038, 0x7040, 0x2048, 0x9005, 0x0500, 0xa948, 0x2105, 0x0016,
+ 0x908a, 0x0036, 0x1a0c, 0x0dd5, 0x2060, 0x001e, 0x8108, 0x2105,
+ 0x9005, 0xa94a, 0x1904, 0x4cd8, 0xa804, 0x9005, 0x090c, 0x0dd5,
+ 0x7042, 0x2938, 0x2040, 0xa003, 0x0000, 0x2001, 0x0002, 0x9080,
+ 0x20ce, 0x2005, 0xa04a, 0x0804, 0x4cd8, 0x703c, 0x2060, 0x2c14,
+ 0x6304, 0x6408, 0x650c, 0x2200, 0x7836, 0x7833, 0x0012, 0x7882,
+ 0x2300, 0x7886, 0x2400, 0x788a, 0x2091, 0x4080, 0x2001, 0x0089,
+ 0x2004, 0xd084, 0x190c, 0x119b, 0x87ff, 0x0118, 0x2748, 0x080c,
+ 0x1031, 0x7048, 0x8001, 0x704a, 0x9005, 0x1170, 0x7040, 0x2048,
+ 0x9005, 0x0128, 0x080c, 0x1031, 0x9006, 0x7042, 0x7046, 0x703b,
+ 0x18ba, 0x703f, 0x18ba, 0x0420, 0x7040, 0x9005, 0x1508, 0x7238,
+ 0x2c00, 0x9206, 0x0148, 0x9c80, 0x0004, 0x90fa, 0x18fa, 0x0210,
+ 0x2001, 0x18ba, 0x703e, 0x00a0, 0x9006, 0x703e, 0x703a, 0x7044,
+ 0x9005, 0x090c, 0x0dd5, 0x2048, 0xa800, 0x9005, 0x1de0, 0x2900,
+ 0x7042, 0x2001, 0x0002, 0x9080, 0x20ce, 0x2005, 0xa84a, 0x0000,
+ 0x007e, 0x008e, 0x009e, 0x00ce, 0x00fe, 0x012e, 0x00ee, 0x0005,
+ 0x2c00, 0x9082, 0x001b, 0x0002, 0x4cf7, 0x4cf7, 0x4cf9, 0x4cf7,
+ 0x4cf7, 0x4cf7, 0x4cfe, 0x4cf7, 0x4cf7, 0x4cf7, 0x4d03, 0x4cf7,
+ 0x4cf7, 0x4cf7, 0x4d08, 0x4cf7, 0x4cf7, 0x4cf7, 0x4d0d, 0x4cf7,
+ 0x4cf7, 0x4cf7, 0x4d12, 0x4cf7, 0x4cf7, 0x4cf7, 0x4d17, 0x080c,
+ 0x0dd5, 0xaa74, 0xab78, 0xac7c, 0x0804, 0x4c83, 0xaa84, 0xab88,
+ 0xac8c, 0x0804, 0x4c83, 0xaa94, 0xab98, 0xac9c, 0x0804, 0x4c83,
+ 0xaaa4, 0xaba8, 0xacac, 0x0804, 0x4c83, 0xaab4, 0xabb8, 0xacbc,
+ 0x0804, 0x4c83, 0xaac4, 0xabc8, 0xaccc, 0x0804, 0x4c83, 0xaad4,
+ 0xabd8, 0xacdc, 0x0804, 0x4c83, 0x0016, 0x0026, 0x0036, 0x00b6,
+ 0x00c6, 0x2009, 0x007e, 0x080c, 0x6699, 0x2019, 0x0001, 0xb85c,
+ 0xd0ac, 0x0110, 0x2019, 0x0000, 0x2011, 0x801b, 0x080c, 0x4b7f,
+ 0x00ce, 0x00be, 0x003e, 0x002e, 0x001e, 0x0005, 0x0026, 0x080c,
+ 0x575d, 0xd0c4, 0x0120, 0x2011, 0x8014, 0x080c, 0x4b7f, 0x002e,
+ 0x0005, 0x81ff, 0x1904, 0x35b5, 0x0126, 0x2091, 0x8000, 0x6030,
+ 0xc08d, 0xc085, 0xc0ac, 0x6032, 0x080c, 0x743e, 0x1158, 0x080c,
+ 0x7724, 0x080c, 0x60ad, 0x9085, 0x0001, 0x080c, 0x7485, 0x080c,
+ 0x736a, 0x0010, 0x080c, 0x5f6c, 0x012e, 0x0804, 0x3583, 0x81ff,
+ 0x0120, 0x2009, 0x0001, 0x0804, 0x35b5, 0x080c, 0x5771, 0x0120,
+ 0x2009, 0x0007, 0x0804, 0x35b5, 0x080c, 0x6a04, 0x0120, 0x2009,
+ 0x0008, 0x0804, 0x35b5, 0x7984, 0x080c, 0x6638, 0x1904, 0x35b8,
+ 0x080c, 0x4b52, 0x0904, 0x35b8, 0x2b00, 0x7026, 0x080c, 0x6a0c,
+ 0x7888, 0x1170, 0x9084, 0x0005, 0x1158, 0x900e, 0x080c, 0x68b9,
+ 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x3583,
+ 0x080c, 0x4b1f, 0x0904, 0x35b5, 0x9006, 0xa866, 0xa832, 0xa868,
+ 0xc0fd, 0xa86a, 0x080c, 0xceb0, 0x0904, 0x35b5, 0x7888, 0xd094,
+ 0x0118, 0xb8cc, 0xc08d, 0xb8ce, 0x7007, 0x0003, 0x701f, 0x4df2,
+ 0x0005, 0x2061, 0x1800, 0x080c, 0x5771, 0x2009, 0x0007, 0x1560,
+ 0x080c, 0x6a04, 0x0118, 0x2009, 0x0008, 0x0430, 0xa998, 0x080c,
+ 0x6638, 0x1530, 0x080c, 0x4b50, 0x0518, 0x080c, 0x6a0c, 0xa89c,
+ 0x1168, 0x9084, 0x0005, 0x1150, 0x900e, 0x080c, 0x68b9, 0x1108,
+ 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0x00d0, 0xa868, 0xc0fc,
+ 0xa86a, 0x080c, 0xceb0, 0x11e0, 0xa89c, 0xd094, 0x0118, 0xb8cc,
+ 0xc08d, 0xb8ce, 0x2009, 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010,
+ 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x0005,
+ 0xa897, 0x4000, 0xa99a, 0x9006, 0x918d, 0x0001, 0x2008, 0x0005,
+ 0x9006, 0x0005, 0xa830, 0x9086, 0x0100, 0x7024, 0x2058, 0x1110,
+ 0x0804, 0x56b1, 0x900e, 0x080c, 0x68b9, 0x1108, 0xc185, 0xb800,
+ 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x3583, 0x080c, 0x5771, 0x0120,
+ 0x2009, 0x0007, 0x0804, 0x35b5, 0x7f84, 0x7a8c, 0x7b88, 0x7c9c,
+ 0x7d98, 0x080c, 0x4b1f, 0x1120, 0x2009, 0x0002, 0x0804, 0x35b5,
+ 0x900e, 0x2130, 0x7126, 0x7132, 0xa860, 0x20e8, 0x7036, 0xa85c,
+ 0x9080, 0x0005, 0x702a, 0x20a0, 0x080c, 0x6699, 0x1904, 0x4e94,
+ 0x080c, 0x6a0c, 0x0138, 0x080c, 0x6a14, 0x0120, 0x080c, 0x69ac,
+ 0x1904, 0x4e94, 0xd794, 0x1110, 0xd784, 0x01a8, 0xb8c4, 0x20e0,
+ 0xb8c8, 0x9080, 0x0006, 0x2098, 0x3400, 0xd794, 0x0160, 0x20a9,
+ 0x0008, 0x4003, 0x2098, 0x20a0, 0x3d00, 0x20e0, 0x20a9, 0x0002,
+ 0x080c, 0x491e, 0x0048, 0x20a9, 0x0004, 0x4003, 0x2098, 0x20a0,
+ 0x3d00, 0x20e0, 0x080c, 0x491e, 0x9186, 0x007e, 0x0170, 0x9186,
+ 0x0080, 0x0158, 0x080c, 0x6a0c, 0x90c2, 0x0006, 0x1210, 0xc1fd,
+ 0x0020, 0x080c, 0x68b9, 0x1108, 0xc1fd, 0x4104, 0xc1fc, 0xd794,
+ 0x0528, 0xb8c4, 0x20e0, 0xb8c8, 0x2060, 0x9c80, 0x0000, 0x2098,
+ 0x20a9, 0x0002, 0x4003, 0x9c80, 0x0003, 0x2098, 0x20a9, 0x0001,
+ 0x4005, 0x9c80, 0x0004, 0x2098, 0x3400, 0x20a9, 0x0002, 0x4003,
+ 0x2098, 0x20a0, 0x3d00, 0x20e0, 0x080c, 0x4911, 0x9c80, 0x0026,
+ 0x2098, 0xb8c4, 0x20e0, 0x20a9, 0x0002, 0x4003, 0xd794, 0x0110,
+ 0x96b0, 0x000b, 0x96b0, 0x0005, 0x8108, 0x080c, 0xaead, 0x0118,
+ 0x9186, 0x0800, 0x0040, 0xd78c, 0x0120, 0x9186, 0x0800, 0x0170,
+ 0x0018, 0x9186, 0x007e, 0x0150, 0xd794, 0x0118, 0x9686, 0x0020,
+ 0x0010, 0x9686, 0x0028, 0x0150, 0x0804, 0x4e24, 0x86ff, 0x1120,
+ 0x7124, 0x810b, 0x0804, 0x3583, 0x7033, 0x0001, 0x7122, 0x7024,
+ 0x9600, 0x7026, 0x772e, 0x2061, 0x18b8, 0x2c44, 0xa06b, 0x0000,
+ 0xa67a, 0x7034, 0xa072, 0x7028, 0xa076, 0xa28e, 0xa392, 0xa496,
+ 0xa59a, 0x080c, 0x10e9, 0x7007, 0x0002, 0x701f, 0x4ed0, 0x0005,
+ 0x7030, 0x9005, 0x1180, 0x7120, 0x7028, 0x20a0, 0x772c, 0x9036,
+ 0x7034, 0x20e8, 0x2061, 0x18b8, 0x2c44, 0xa28c, 0xa390, 0xa494,
+ 0xa598, 0x0804, 0x4e24, 0x7124, 0x810b, 0x0804, 0x3583, 0x2029,
+ 0x007e, 0x7984, 0x7a88, 0x7b8c, 0x7c98, 0x9184, 0xff00, 0x8007,
+ 0x90e2, 0x0020, 0x0a04, 0x35b8, 0x9502, 0x0a04, 0x35b8, 0x9184,
+ 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x35b8, 0x9502, 0x0a04, 0x35b8,
+ 0x9284, 0xff00, 0x8007, 0x90e2, 0x0020, 0x0a04, 0x35b8, 0x9502,
+ 0x0a04, 0x35b8, 0x9284, 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x35b8,
+ 0x9502, 0x0a04, 0x35b8, 0x9384, 0xff00, 0x8007, 0x90e2, 0x0020,
+ 0x0a04, 0x35b8, 0x9502, 0x0a04, 0x35b8, 0x9384, 0x00ff, 0x90e2,
+ 0x0020, 0x0a04, 0x35b8, 0x9502, 0x0a04, 0x35b8, 0x9484, 0xff00,
+ 0x8007, 0x90e2, 0x0020, 0x0a04, 0x35b8, 0x9502, 0x0a04, 0x35b8,
+ 0x9484, 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x35b8, 0x9502, 0x0a04,
+ 0x35b8, 0x2061, 0x1988, 0x6102, 0x6206, 0x630a, 0x640e, 0x0804,
+ 0x3583, 0x080c, 0x4b1f, 0x0904, 0x35b5, 0x2009, 0x0016, 0x7a8c,
+ 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x080c,
+ 0x4b68, 0x701f, 0x4f54, 0x0005, 0x2001, 0x0138, 0x2003, 0x0000,
+ 0x00e6, 0x2071, 0x0300, 0x701c, 0xd0a4, 0x1de8, 0x00ee, 0x20a9,
+ 0x0016, 0x896e, 0x8d6e, 0x8d6f, 0x9d84, 0xffc0, 0x9080, 0x0019,
+ 0x2098, 0x9d84, 0x003f, 0x20e0, 0x2069, 0x1877, 0x20e9, 0x0001,
+ 0x2da0, 0x4003, 0x6800, 0x9005, 0x0904, 0x4fd5, 0x6804, 0x2008,
+ 0x918c, 0xfff8, 0x1904, 0x4fd5, 0x680c, 0x9005, 0x0904, 0x4fd5,
+ 0x9082, 0xff01, 0x1a04, 0x4fd5, 0x6810, 0x9082, 0x005c, 0x0a04,
+ 0x4fd5, 0x6824, 0x2008, 0x9082, 0x0008, 0x0a04, 0x4fd5, 0x9182,
+ 0x0400, 0x1a04, 0x4fd5, 0x0056, 0x2029, 0x0000, 0x080c, 0x8bbf,
+ 0x005e, 0x6944, 0x6820, 0x9102, 0x06c0, 0x6820, 0x9082, 0x0019,
+ 0x16a0, 0x6828, 0x6944, 0x810c, 0x9102, 0x0678, 0x6840, 0x9082,
+ 0x000f, 0x1658, 0x080c, 0x1018, 0x2900, 0x0904, 0x4ff1, 0x684e,
+ 0x00e6, 0x2071, 0x1930, 0x00b6, 0x2059, 0x0000, 0x080c, 0x8a7b,
+ 0x00be, 0x00ee, 0x0568, 0x080c, 0x87ce, 0x080c, 0x8819, 0x11e0,
+ 0x6857, 0x0000, 0x00c6, 0x2061, 0x0100, 0x6104, 0x918d, 0x2000,
+ 0x6106, 0x6b10, 0x2061, 0x1a61, 0x630a, 0x00ce, 0x080c, 0x2994,
+ 0x2001, 0x0138, 0x2102, 0x0804, 0x3583, 0x080c, 0x2994, 0x2001,
+ 0x0138, 0x2102, 0x0804, 0x35b8, 0x080c, 0x8812, 0x00e6, 0x2071,
+ 0x1930, 0x080c, 0x8c3f, 0x080c, 0x8c4e, 0x080c, 0x8a62, 0x00ee,
+ 0x2001, 0x188a, 0x204c, 0x080c, 0x1031, 0x2001, 0x188a, 0x2003,
+ 0x0000, 0x080c, 0x2994, 0x2001, 0x0138, 0x2102, 0x0804, 0x35b5,
+ 0x2001, 0x1924, 0x200c, 0x918e, 0x0000, 0x0904, 0x5052, 0x080c,
+ 0x8a5d, 0x0904, 0x5052, 0x2001, 0x0101, 0x200c, 0x918c, 0xdfff,
+ 0x2102, 0x2001, 0x0138, 0x2003, 0x0000, 0x00e6, 0x2071, 0x0300,
+ 0x701c, 0xd0a4, 0x1de8, 0x00ee, 0x080c, 0x8a62, 0x2001, 0x0035,
+ 0x080c, 0x15fd, 0x00c6, 0x2061, 0x193c, 0x6004, 0x6100, 0x9106,
+ 0x1de0, 0x00ce, 0x080c, 0x2994, 0x2001, 0x0138, 0x2102, 0x00e6,
+ 0x00f6, 0x2071, 0x1923, 0x080c, 0x899c, 0x0120, 0x2f00, 0x080c,
+ 0x8a28, 0x0cc8, 0x00fe, 0x00ee, 0x0126, 0x2091, 0x8000, 0x2001,
+ 0x188a, 0x200c, 0x81ff, 0x0138, 0x2148, 0x080c, 0x1031, 0x2001,
+ 0x188a, 0x2003, 0x0000, 0x2001, 0x183c, 0x2003, 0x0020, 0x080c,
+ 0x8812, 0x00e6, 0x2071, 0x1930, 0x080c, 0x8c3f, 0x080c, 0x8c4e,
+ 0x00ee, 0x012e, 0x0804, 0x3583, 0x0006, 0x080c, 0x575d, 0xd0cc,
+ 0x000e, 0x0005, 0x0006, 0x080c, 0x5761, 0xd0bc, 0x000e, 0x0005,
+ 0x6174, 0x7a84, 0x6300, 0x82ff, 0x1118, 0x7986, 0x0804, 0x3583,
+ 0x83ff, 0x1904, 0x35b8, 0x2001, 0xfff0, 0x9200, 0x1a04, 0x35b8,
+ 0x2019, 0xffff, 0x6078, 0x9302, 0x9200, 0x0a04, 0x35b8, 0x7986,
+ 0x6276, 0x0804, 0x3583, 0x080c, 0x5771, 0x1904, 0x35b5, 0x7c88,
+ 0x7d84, 0x7e98, 0x7f8c, 0x080c, 0x4b1f, 0x0904, 0x35b5, 0x900e,
+ 0x901e, 0x7326, 0x7332, 0xa860, 0x20e8, 0x7036, 0xa85c, 0x9080,
+ 0x0003, 0x702a, 0x20a0, 0x91d8, 0x1000, 0x2b5c, 0x8bff, 0x0178,
+ 0x080c, 0x6a0c, 0x0118, 0x080c, 0x6a14, 0x1148, 0x20a9, 0x0001,
+ 0xb814, 0x4004, 0xb810, 0x4004, 0x4104, 0x9398, 0x0003, 0x8108,
+ 0x9182, 0x0800, 0x0120, 0x9386, 0x003c, 0x0170, 0x0c20, 0x83ff,
+ 0x1148, 0x7224, 0x900e, 0x2001, 0x0003, 0x080c, 0x9027, 0x2208,
+ 0x0804, 0x3583, 0x7033, 0x0001, 0x7122, 0x7024, 0x9300, 0x7026,
+ 0x2061, 0x18b8, 0x2c44, 0xa06b, 0x0000, 0xa37a, 0x7028, 0xa076,
+ 0x7034, 0xa072, 0xa48e, 0xa592, 0xa696, 0xa79a, 0x080c, 0x10e9,
+ 0x7007, 0x0002, 0x701f, 0x50d5, 0x0005, 0x7030, 0x9005, 0x1178,
+ 0x7120, 0x7028, 0x20a0, 0x901e, 0x7034, 0x20e8, 0x2061, 0x18b8,
+ 0x2c44, 0xa48c, 0xa590, 0xa694, 0xa798, 0x0804, 0x5093, 0x7224,
+ 0x900e, 0x2001, 0x0003, 0x080c, 0x9027, 0x2208, 0x0804, 0x3583,
+ 0x00f6, 0x00e6, 0x080c, 0x5771, 0x2009, 0x0007, 0x1904, 0x5168,
+ 0x2071, 0x189e, 0x745c, 0x84ff, 0x2009, 0x000e, 0x1904, 0x5168,
+ 0xac9c, 0xad98, 0xaea4, 0xafa0, 0x0096, 0x080c, 0x1018, 0x2009,
+ 0x0002, 0x0904, 0x5168, 0x2900, 0x705e, 0x900e, 0x901e, 0x7356,
+ 0x7362, 0xa860, 0x7066, 0xa85c, 0x9080, 0x0003, 0x705a, 0x20a0,
+ 0x91d8, 0x1000, 0x2b5c, 0x8bff, 0x0178, 0x080c, 0x6a0c, 0x0118,
+ 0x080c, 0x6a14, 0x1148, 0xb814, 0x20a9, 0x0001, 0x4004, 0xb810,
+ 0x4004, 0x4104, 0x9398, 0x0003, 0x8108, 0x9182, 0x0800, 0x0120,
+ 0x9386, 0x003c, 0x01e8, 0x0c20, 0x83ff, 0x11c0, 0x7254, 0x900e,
+ 0x2001, 0x0003, 0x080c, 0x9027, 0x2208, 0x009e, 0xa897, 0x4000,
+ 0xa99a, 0x715c, 0x81ff, 0x090c, 0x0dd5, 0x2148, 0x080c, 0x1031,
+ 0x9006, 0x705e, 0x918d, 0x0001, 0x2008, 0x0418, 0x7063, 0x0001,
+ 0x7152, 0x7054, 0x9300, 0x7056, 0x2061, 0x18b9, 0x2c44, 0xa37a,
+ 0x7058, 0xa076, 0x7064, 0xa072, 0xa48e, 0xa592, 0xa696, 0xa79a,
+ 0xa09f, 0x5174, 0x000e, 0xa0a2, 0x080c, 0x10e9, 0x9006, 0x0048,
+ 0x009e, 0xa897, 0x4005, 0xa99a, 0x900e, 0x9085, 0x0001, 0x2001,
+ 0x0030, 0x00ee, 0x00fe, 0x0005, 0x00f6, 0xa0a0, 0x904d, 0x090c,
+ 0x0dd5, 0x00e6, 0x2071, 0x189e, 0xa06c, 0x908e, 0x0100, 0x0138,
+ 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4002, 0x00d8, 0x7060,
+ 0x9005, 0x1158, 0x7150, 0x7058, 0x20a0, 0x901e, 0x7064, 0x20e8,
+ 0xa48c, 0xa590, 0xa694, 0xa798, 0x0428, 0xa87b, 0x0000, 0xa883,
+ 0x0000, 0xa897, 0x4000, 0x7254, 0x900e, 0x2001, 0x0003, 0x080c,
+ 0x9027, 0xaa9a, 0x715c, 0x81ff, 0x090c, 0x0dd5, 0x2148, 0x080c,
+ 0x1031, 0x705f, 0x0000, 0xa0a0, 0x2048, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6d17, 0x012e, 0xa09f, 0x0000, 0xa0a3, 0x0000, 0x00ee,
+ 0x00fe, 0x0005, 0x91d8, 0x1000, 0x2b5c, 0x8bff, 0x0178, 0x080c,
+ 0x6a0c, 0x0118, 0x080c, 0x6a14, 0x1148, 0xb814, 0x20a9, 0x0001,
+ 0x4004, 0xb810, 0x4004, 0x4104, 0x9398, 0x0003, 0x8108, 0x9182,
+ 0x0800, 0x0120, 0x9386, 0x003c, 0x0518, 0x0c20, 0x83ff, 0x11f0,
+ 0x7154, 0x810c, 0xa99a, 0xa897, 0x4000, 0x715c, 0x81ff, 0x090c,
+ 0x0dd5, 0x2148, 0x080c, 0x1031, 0x9006, 0x705e, 0x918d, 0x0001,
+ 0x2008, 0xa0a0, 0x2048, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17,
+ 0x012e, 0xa09f, 0x0000, 0xa0a3, 0x0000, 0x0070, 0x7063, 0x0001,
+ 0x7152, 0x7054, 0x9300, 0x7056, 0xa37a, 0xa48e, 0xa592, 0xa696,
+ 0xa79a, 0x080c, 0x10e9, 0x9006, 0x00ee, 0x0005, 0x0096, 0xa88c,
+ 0x90be, 0x7000, 0x0148, 0x90be, 0x7100, 0x0130, 0x90be, 0x7200,
+ 0x0118, 0x009e, 0x0804, 0x35b8, 0xa884, 0xa988, 0x080c, 0x287c,
+ 0x1518, 0x080c, 0x6638, 0x1500, 0x7126, 0xbe12, 0xbd16, 0xae7c,
+ 0x080c, 0x4b1f, 0x01c8, 0x080c, 0x4b1f, 0x01b0, 0x009e, 0xa867,
+ 0x0000, 0xa868, 0xc0fd, 0xa86a, 0xa823, 0x0000, 0xa804, 0x2048,
+ 0x080c, 0xce32, 0x1120, 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007,
+ 0x0003, 0x701f, 0x5241, 0x0005, 0x009e, 0x2009, 0x0002, 0x0804,
+ 0x35b5, 0x7124, 0x080c, 0x331a, 0xa820, 0x9086, 0x8001, 0x1120,
+ 0x2009, 0x0004, 0x0804, 0x35b5, 0x2900, 0x7022, 0xa804, 0x0096,
+ 0x2048, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0,
+ 0x009e, 0x9080, 0x0002, 0x0076, 0x0006, 0x2098, 0x20a0, 0x27e0,
+ 0x27e8, 0x20a9, 0x002a, 0x080c, 0x0f7c, 0xaa6c, 0xab70, 0xac74,
+ 0xad78, 0x2061, 0x18b8, 0x2c44, 0xa06b, 0x0000, 0xae64, 0xaf8c,
+ 0x97c6, 0x7000, 0x0118, 0x97c6, 0x7100, 0x1148, 0x96c2, 0x0004,
+ 0x0600, 0x2009, 0x0004, 0x000e, 0x007e, 0x0804, 0x4b6b, 0x97c6,
+ 0x7200, 0x11b8, 0x96c2, 0x0054, 0x02a0, 0x000e, 0x007e, 0x2061,
+ 0x18b8, 0x2c44, 0xa076, 0xa772, 0xa07b, 0x002a, 0xa28e, 0xa392,
+ 0xa496, 0xa59a, 0x080c, 0x10e9, 0x7007, 0x0002, 0x701f, 0x529d,
+ 0x0005, 0x000e, 0x007e, 0x0804, 0x35b8, 0x7020, 0x2048, 0xa804,
+ 0x2048, 0xa804, 0x2048, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f,
+ 0x9084, 0xffc0, 0x9080, 0x0002, 0x2098, 0x20a0, 0x27e0, 0x27e8,
+ 0x20a9, 0x002a, 0x080c, 0x0f7c, 0x2100, 0x2238, 0x2061, 0x18b8,
+ 0x2c44, 0xa28c, 0xa390, 0xa494, 0xa598, 0x2009, 0x002a, 0x0804,
+ 0x4b6b, 0x81ff, 0x1904, 0x35b5, 0x798c, 0x2001, 0x197d, 0x918c,
+ 0x8000, 0x2102, 0x080c, 0x4b36, 0x0904, 0x35b8, 0x080c, 0x6a0c,
+ 0x0120, 0x080c, 0x6a14, 0x1904, 0x35b8, 0x080c, 0x6760, 0x0904,
+ 0x35b5, 0x0126, 0x2091, 0x8000, 0x080c, 0x6826, 0x012e, 0x0904,
+ 0x35b5, 0x2001, 0x197d, 0x2004, 0xd0fc, 0x1904, 0x3583, 0x0804,
+ 0x4586, 0xa9a0, 0x2001, 0x197d, 0x918c, 0x8000, 0xc18d, 0x2102,
+ 0x080c, 0x4b43, 0x01a0, 0x080c, 0x6a0c, 0x0118, 0x080c, 0x6a14,
+ 0x1170, 0x080c, 0x6760, 0x2009, 0x0002, 0x0128, 0x080c, 0x6826,
+ 0x1170, 0x2009, 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897,
+ 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x0005, 0xa897,
+ 0x4000, 0x2001, 0x197d, 0x2004, 0xd0fc, 0x1128, 0x080c, 0x5765,
+ 0x0110, 0x9006, 0x0018, 0x900e, 0x9085, 0x0001, 0x2001, 0x0000,
+ 0x0005, 0x78a8, 0xd08c, 0x1118, 0xd084, 0x0904, 0x44fb, 0x080c,
+ 0x4b52, 0x0904, 0x35b8, 0x080c, 0x4b1f, 0x1120, 0x2009, 0x0002,
+ 0x0804, 0x35b5, 0x080c, 0x6a0c, 0x0130, 0x908e, 0x0004, 0x0118,
+ 0x908e, 0x0005, 0x15a0, 0x78a8, 0xd08c, 0x0120, 0xb800, 0xc08c,
+ 0xb802, 0x0028, 0x080c, 0x575d, 0xd0b4, 0x0904, 0x4535, 0x7884,
+ 0x908e, 0x007e, 0x0904, 0x4535, 0x908e, 0x007f, 0x0904, 0x4535,
+ 0x908e, 0x0080, 0x0904, 0x4535, 0xb800, 0xd08c, 0x1904, 0x4535,
+ 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xce51, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007, 0x0003, 0x701f, 0x5369,
+ 0x0005, 0x080c, 0x4b52, 0x0904, 0x35b8, 0x0804, 0x4535, 0x080c,
+ 0x3373, 0x0108, 0x0005, 0x2009, 0x1834, 0x210c, 0x81ff, 0x0120,
+ 0x2009, 0x0001, 0x0804, 0x35b5, 0x080c, 0x5771, 0x0120, 0x2009,
+ 0x0007, 0x0804, 0x35b5, 0x080c, 0x6a04, 0x0120, 0x2009, 0x0008,
+ 0x0804, 0x35b5, 0xb89c, 0xd0a4, 0x1118, 0xd0ac, 0x1904, 0x4535,
+ 0x9006, 0xa866, 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xceb0,
+ 0x1120, 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007, 0x0003, 0x701f,
+ 0x53a2, 0x0005, 0xa830, 0x9086, 0x0100, 0x1120, 0x2009, 0x0004,
+ 0x0804, 0x56b1, 0x080c, 0x4b52, 0x0904, 0x35b8, 0x0804, 0x533b,
+ 0x81ff, 0x2009, 0x0001, 0x1904, 0x35b5, 0x080c, 0x5771, 0x2009,
+ 0x0007, 0x1904, 0x35b5, 0x080c, 0x6a04, 0x0120, 0x2009, 0x0008,
+ 0x0804, 0x35b5, 0x080c, 0x4b52, 0x0904, 0x35b8, 0x080c, 0x6a0c,
+ 0x2009, 0x0009, 0x1904, 0x35b5, 0x080c, 0x4b1f, 0x2009, 0x0002,
+ 0x0904, 0x35b5, 0x9006, 0xa866, 0xa832, 0xa868, 0xc0fd, 0xa86a,
+ 0x7988, 0x9194, 0xff00, 0x918c, 0x00ff, 0x9006, 0x82ff, 0x1128,
+ 0xc0ed, 0xa952, 0x798c, 0xa956, 0x0038, 0x928e, 0x0100, 0x1904,
+ 0x35b8, 0xc0e5, 0xa952, 0xa956, 0xa83e, 0x080c, 0xd103, 0x2009,
+ 0x0003, 0x0904, 0x35b5, 0x7007, 0x0003, 0x701f, 0x53f8, 0x0005,
+ 0xa830, 0x9086, 0x0100, 0x2009, 0x0004, 0x0904, 0x35b5, 0x0804,
+ 0x3583, 0x7aa8, 0x9284, 0xc000, 0x0148, 0xd2ec, 0x01a0, 0x080c,
+ 0x5771, 0x1188, 0x2009, 0x0014, 0x0804, 0x35b5, 0xd2dc, 0x1578,
+ 0x81ff, 0x2009, 0x0001, 0x1904, 0x35b5, 0x080c, 0x5771, 0x2009,
+ 0x0007, 0x1904, 0x35b5, 0xd2f4, 0x0138, 0x9284, 0x5000, 0xc0d5,
+ 0x080c, 0x5737, 0x0804, 0x3583, 0xd2fc, 0x0160, 0x080c, 0x4b52,
+ 0x0904, 0x35b8, 0x7984, 0x9284, 0x9000, 0xc0d5, 0x080c, 0x570c,
+ 0x0804, 0x3583, 0x080c, 0x4b52, 0x0904, 0x35b8, 0xb804, 0x9084,
+ 0x00ff, 0x9086, 0x0006, 0x2009, 0x0009, 0x1904, 0x54e7, 0x080c,
+ 0x4b1f, 0x2009, 0x0002, 0x0904, 0x54e7, 0xa85c, 0x9080, 0x001b,
+ 0xaf60, 0x2009, 0x0008, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x080c,
+ 0x4b68, 0x701f, 0x5454, 0x0005, 0xa86c, 0x9086, 0x0500, 0x1138,
+ 0xa870, 0x9005, 0x1120, 0xa874, 0x9084, 0xff00, 0x0110, 0x1904,
+ 0x35b8, 0xa866, 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0x4b52,
+ 0x1110, 0x0804, 0x35b8, 0x2009, 0x0043, 0x080c, 0xd16b, 0x2009,
+ 0x0003, 0x0904, 0x54e7, 0x7007, 0x0003, 0x701f, 0x5478, 0x0005,
+ 0xa830, 0x9086, 0x0100, 0x2009, 0x0004, 0x0904, 0x54e7, 0x7984,
+ 0x7aa8, 0x9284, 0x1000, 0xe085, 0x080c, 0x570c, 0x0804, 0x3583,
+ 0x00c6, 0xaab0, 0x9284, 0xc000, 0x0148, 0xd2ec, 0x0170, 0x080c,
+ 0x5771, 0x1158, 0x2009, 0x0014, 0x0804, 0x54d6, 0x2061, 0x1800,
+ 0x080c, 0x5771, 0x2009, 0x0007, 0x15c8, 0xd2f4, 0x0130, 0x9284,
+ 0x5000, 0xc0d5, 0x080c, 0x5737, 0x0058, 0xd2fc, 0x0180, 0x080c,
+ 0x4b50, 0x0590, 0xa998, 0x9284, 0x9000, 0xc0d5, 0x080c, 0x570c,
+ 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0x0438, 0x080c,
+ 0x4b50, 0x0510, 0x080c, 0x6a0c, 0x2009, 0x0009, 0x11b8, 0xa8c4,
+ 0x9086, 0x0500, 0x11c8, 0xa8c8, 0x9005, 0x11b0, 0xa8cc, 0x9084,
+ 0xff00, 0x1190, 0x080c, 0x4b50, 0x1108, 0x0070, 0x2009, 0x004b,
+ 0x080c, 0xd16b, 0x2009, 0x0003, 0x0108, 0x0078, 0x0431, 0x19c0,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x00ce, 0x0005, 0x9006, 0x0ce0, 0x7aa8,
+ 0xd2dc, 0x0904, 0x35b5, 0x0016, 0x7984, 0x9284, 0x1000, 0xc0fd,
+ 0x080c, 0x570c, 0x001e, 0x1904, 0x35b5, 0x0804, 0x3583, 0x00f6,
+ 0x2d78, 0xaab0, 0x0021, 0x00fe, 0x0005, 0xaab0, 0xc2d5, 0xd2dc,
+ 0x0150, 0x0016, 0xa998, 0x9284, 0x1400, 0xc0fd, 0x080c, 0x570c,
+ 0x001e, 0x9085, 0x0001, 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x35b5, 0x080c, 0x5771, 0x0120, 0x2009, 0x0007, 0x0804,
+ 0x35b5, 0x7984, 0x7ea8, 0x96b4, 0x00ff, 0x080c, 0x6699, 0x1904,
+ 0x35b8, 0x9186, 0x007f, 0x0138, 0x080c, 0x6a0c, 0x0120, 0x2009,
+ 0x0009, 0x0804, 0x35b5, 0x080c, 0x4b1f, 0x1120, 0x2009, 0x0002,
+ 0x0804, 0x35b5, 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x2001,
+ 0x0100, 0x8007, 0xa80a, 0x080c, 0xce6b, 0x1120, 0x2009, 0x0003,
+ 0x0804, 0x35b5, 0x7007, 0x0003, 0x701f, 0x5547, 0x0005, 0xa808,
+ 0x8007, 0x9086, 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x35b5,
+ 0xa8e0, 0xa866, 0xa810, 0x8007, 0x9084, 0x00ff, 0x800c, 0xa814,
+ 0x8007, 0x9084, 0x00ff, 0x8004, 0x9080, 0x0002, 0x9108, 0x8906,
+ 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0004,
+ 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804, 0x4b6b, 0x080c, 0x4b1f,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x35b5, 0x7984, 0x9194, 0xff00,
+ 0x918c, 0x00ff, 0x8217, 0x82ff, 0x1118, 0x7023, 0x19b2, 0x0040,
+ 0x92c6, 0x0001, 0x1118, 0x7023, 0x19cc, 0x0010, 0x0804, 0x35b8,
+ 0x2009, 0x001a, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080,
+ 0x0019, 0xaf60, 0x080c, 0x4b68, 0x701f, 0x5597, 0x0005, 0x2001,
+ 0x182e, 0x2003, 0x0001, 0xa85c, 0x9080, 0x0019, 0x2098, 0xa860,
+ 0x20e0, 0x20a9, 0x001a, 0x7020, 0x20a0, 0x20e9, 0x0001, 0x4003,
+ 0x0804, 0x3583, 0x080c, 0x4b1f, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x35b5, 0x7984, 0x9194, 0xff00, 0x918c, 0x00ff, 0x8217, 0x82ff,
+ 0x1118, 0x2099, 0x19b2, 0x0040, 0x92c6, 0x0001, 0x1118, 0x2099,
+ 0x19cc, 0x0010, 0x0804, 0x35b8, 0xa85c, 0x9080, 0x0019, 0x20a0,
+ 0xa860, 0x20e8, 0x20a9, 0x001a, 0x20e1, 0x0001, 0x4003, 0x2009,
+ 0x001a, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080, 0x0019,
+ 0xaf60, 0x0804, 0x4b6b, 0x7884, 0x908a, 0x1000, 0x1a04, 0x35b8,
+ 0x0126, 0x2091, 0x8000, 0x8003, 0x800b, 0x810b, 0x9108, 0x00c6,
+ 0x2061, 0x19f9, 0x6142, 0x00ce, 0x012e, 0x0804, 0x3583, 0x00c6,
+ 0x080c, 0x743e, 0x1160, 0x080c, 0x7724, 0x080c, 0x60ad, 0x9085,
+ 0x0001, 0x080c, 0x7485, 0x080c, 0x736a, 0x080c, 0x0dd5, 0x2061,
+ 0x1800, 0x6030, 0xc09d, 0x6032, 0x080c, 0x5f6c, 0x00ce, 0x0005,
+ 0x00c6, 0x2001, 0x1800, 0x2004, 0x908e, 0x0000, 0x0904, 0x35b5,
+ 0x7884, 0x9005, 0x0188, 0x7888, 0x2061, 0x199b, 0x2c0c, 0x2062,
+ 0x080c, 0x2c5e, 0x01a0, 0x080c, 0x2c66, 0x0188, 0x080c, 0x2c6e,
+ 0x0170, 0x2162, 0x0804, 0x35b8, 0x2061, 0x0100, 0x6038, 0x9086,
+ 0x0007, 0x1118, 0x2009, 0x0001, 0x0010, 0x2009, 0x0000, 0x7884,
+ 0x9086, 0x0002, 0x1568, 0x2061, 0x0100, 0x6028, 0xc09c, 0x602a,
+ 0x0026, 0x2011, 0x0003, 0x080c, 0xa722, 0x2011, 0x0002, 0x080c,
+ 0xa72c, 0x002e, 0x080c, 0xa636, 0x0036, 0x901e, 0x080c, 0xa6ac,
+ 0x003e, 0x60e3, 0x0000, 0x080c, 0xeb79, 0x080c, 0xeb94, 0x9085,
+ 0x0001, 0x080c, 0x7485, 0x9006, 0x080c, 0x2d4e, 0x2001, 0x1800,
+ 0x2003, 0x0004, 0x2001, 0x19a6, 0x2003, 0x0000, 0x6027, 0x0008,
+ 0x00ce, 0x0804, 0x3583, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804,
+ 0x35b5, 0x080c, 0x5771, 0x0120, 0x2009, 0x0007, 0x0804, 0x35b5,
+ 0x7984, 0x7ea8, 0x96b4, 0x00ff, 0x080c, 0x6699, 0x1904, 0x35b8,
+ 0x9186, 0x007f, 0x0138, 0x080c, 0x6a0c, 0x0120, 0x2009, 0x0009,
+ 0x0804, 0x35b5, 0x080c, 0x4b1f, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x35b5, 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xce6e,
+ 0x1120, 0x2009, 0x0003, 0x0804, 0x35b5, 0x7007, 0x0003, 0x701f,
+ 0x569a, 0x0005, 0xa830, 0x9086, 0x0100, 0x1120, 0x2009, 0x0004,
+ 0x0804, 0x35b5, 0xa8e0, 0xa866, 0xa834, 0x8007, 0x800c, 0xa85c,
+ 0x9080, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xaf60, 0x0804,
+ 0x4b6b, 0xa898, 0x9086, 0x000d, 0x1904, 0x35b5, 0x2021, 0x4005,
+ 0x0126, 0x2091, 0x8000, 0x0e04, 0x56be, 0x0010, 0x012e, 0x0cc0,
+ 0x7c36, 0x9486, 0x4000, 0x0118, 0x7833, 0x0011, 0x0010, 0x7833,
+ 0x0010, 0x7883, 0x4005, 0xa998, 0x7986, 0xa9a4, 0x799a, 0xa9a8,
+ 0x799e, 0x080c, 0x4b5b, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004,
+ 0xd084, 0x190c, 0x119b, 0x7007, 0x0001, 0x2091, 0x5000, 0x700f,
+ 0x0000, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x00c6, 0x2061,
+ 0x19f9, 0x7984, 0x6152, 0x614e, 0x6057, 0x0000, 0x604b, 0x0009,
+ 0x7898, 0x606a, 0x789c, 0x6066, 0x7888, 0x6062, 0x788c, 0x605e,
+ 0x2001, 0x1a07, 0x2044, 0x2001, 0x1a0e, 0xa076, 0xa060, 0xa072,
+ 0xa07b, 0x0001, 0xa07f, 0x0002, 0xa06b, 0x0000, 0xa09f, 0x0000,
+ 0x00ce, 0x012e, 0x0804, 0x3583, 0x0126, 0x2091, 0x8000, 0x00b6,
+ 0x00c6, 0x90e4, 0xc000, 0x0168, 0x0006, 0xd0d4, 0x0130, 0x0036,
+ 0x2019, 0x0029, 0x080c, 0x3338, 0x003e, 0x080c, 0xccd3, 0x000e,
+ 0x1198, 0xd0e4, 0x0160, 0x9180, 0x1000, 0x2004, 0x905d, 0x0160,
+ 0x080c, 0x60c7, 0x080c, 0xaead, 0x0110, 0xb817, 0x0000, 0x9006,
+ 0x00ce, 0x00be, 0x012e, 0x0005, 0x9085, 0x0001, 0x0cc8, 0x0126,
+ 0x2091, 0x8000, 0x0156, 0x2010, 0x900e, 0x20a9, 0x0800, 0x0016,
+ 0x9180, 0x1000, 0x2004, 0x9005, 0x0188, 0x9186, 0x007e, 0x0170,
+ 0x9186, 0x007f, 0x0158, 0x9186, 0x0080, 0x0140, 0x9186, 0x00ff,
+ 0x0128, 0x0026, 0x2200, 0x080c, 0x570c, 0x002e, 0x001e, 0x8108,
+ 0x1f04, 0x573f, 0x015e, 0x012e, 0x0005, 0x2001, 0x1848, 0x2004,
+ 0x0005, 0x2001, 0x1867, 0x2004, 0x0005, 0x0006, 0x2001, 0x1810,
+ 0x2004, 0xd0d4, 0x000e, 0x0005, 0x2001, 0x180e, 0x2004, 0xd0b4,
+ 0x0005, 0x2001, 0x1800, 0x2004, 0x9086, 0x0003, 0x0005, 0x0016,
+ 0x00e6, 0x2071, 0x189e, 0x7108, 0x910d, 0x710a, 0x00ee, 0x001e,
+ 0x0005, 0x79a4, 0x9182, 0x0081, 0x1a04, 0x35b8, 0x810c, 0x0016,
+ 0x080c, 0x4b1f, 0x0170, 0x080c, 0x0f07, 0x2100, 0x2238, 0x7d84,
+ 0x7c88, 0x7b8c, 0x7a90, 0x001e, 0x080c, 0x4b68, 0x701f, 0x579d,
+ 0x0005, 0x2009, 0x0002, 0x0804, 0x35b5, 0x2079, 0x0000, 0x7d94,
+ 0x7c98, 0x7ba8, 0x7aac, 0x79a4, 0x810c, 0x2061, 0x18b8, 0x2c44,
+ 0xa770, 0xa074, 0x2071, 0x189e, 0x080c, 0x4b6b, 0x701f, 0x57b1,
+ 0x0005, 0x2061, 0x18b8, 0x2c44, 0x0016, 0x0026, 0xa270, 0xa174,
+ 0x080c, 0x0f0f, 0x002e, 0x001e, 0x080c, 0x0fbc, 0x9006, 0xa802,
+ 0xa806, 0x0804, 0x3583, 0x0126, 0x0156, 0x0136, 0x0146, 0x01c6,
+ 0x01d6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2061, 0x0100, 0x2069,
+ 0x0200, 0x2071, 0x1800, 0x6044, 0xd0a4, 0x11e8, 0xd084, 0x0118,
+ 0x080c, 0x596c, 0x0068, 0xd08c, 0x0118, 0x080c, 0x5875, 0x0040,
+ 0xd094, 0x0118, 0x080c, 0x5845, 0x0018, 0xd09c, 0x0108, 0x0099,
+ 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x01de, 0x01ce, 0x014e, 0x013e,
+ 0x015e, 0x012e, 0x0005, 0x0016, 0x6128, 0xd19c, 0x1110, 0xc19d,
+ 0x612a, 0x001e, 0x0c68, 0x0006, 0x7098, 0x9005, 0x000e, 0x0120,
+ 0x709b, 0x0000, 0x7093, 0x0000, 0x624c, 0x9286, 0xf0f0, 0x1150,
+ 0x6048, 0x9086, 0xf0f0, 0x0130, 0x624a, 0x6043, 0x0090, 0x6043,
+ 0x0010, 0x0490, 0x9294, 0xff00, 0x9296, 0xf700, 0x0178, 0x7138,
+ 0xd1a4, 0x1160, 0x6240, 0x9295, 0x0100, 0x6242, 0x9294, 0x0010,
+ 0x0128, 0x2009, 0x00f7, 0x080c, 0x6029, 0x00f0, 0x6040, 0x9084,
+ 0x0010, 0x9085, 0x0140, 0x6042, 0x6043, 0x0000, 0x7087, 0x0000,
+ 0x70a3, 0x0001, 0x70c7, 0x0000, 0x70df, 0x0000, 0x2009, 0x1c80,
+ 0x200b, 0x0000, 0x7097, 0x0000, 0x708b, 0x000f, 0x2009, 0x000f,
+ 0x2011, 0x5f0f, 0x080c, 0x8648, 0x0005, 0x2001, 0x1869, 0x2004,
+ 0xd08c, 0x0110, 0x705f, 0xffff, 0x7088, 0x9005, 0x1528, 0x2011,
+ 0x5f0f, 0x080c, 0x85b0, 0x6040, 0x9094, 0x0010, 0x9285, 0x0020,
+ 0x6042, 0x20a9, 0x00c8, 0x6044, 0xd08c, 0x1168, 0x1f04, 0x585b,
+ 0x6242, 0x709b, 0x0000, 0x6040, 0x9094, 0x0010, 0x9285, 0x0080,
+ 0x6042, 0x6242, 0x0048, 0x6242, 0x709b, 0x0000, 0x708f, 0x0000,
+ 0x9006, 0x080c, 0x60b2, 0x0000, 0x0005, 0x708c, 0x908a, 0x0003,
+ 0x1a0c, 0x0dd5, 0x000b, 0x0005, 0x587f, 0x58d0, 0x596b, 0x00f6,
+ 0x0016, 0x6900, 0x918c, 0x0800, 0x708f, 0x0001, 0x2001, 0x015d,
+ 0x2003, 0x0000, 0x6803, 0x00fc, 0x20a9, 0x0004, 0x6800, 0x9084,
+ 0x00fc, 0x0120, 0x1f04, 0x588e, 0x080c, 0x0dd5, 0x68a0, 0x68a2,
+ 0x689c, 0x689e, 0x6898, 0x689a, 0xa001, 0x918d, 0x1600, 0x6902,
+ 0x001e, 0x6837, 0x0020, 0x080c, 0x608e, 0x2079, 0x1c00, 0x7833,
+ 0x1101, 0x7837, 0x0000, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9,
+ 0x0001, 0x20a1, 0x1c0e, 0x20a9, 0x0004, 0x4003, 0x080c, 0xabfe,
+ 0x20e1, 0x0001, 0x2099, 0x1c00, 0x20e9, 0x0000, 0x20a1, 0x0240,
+ 0x20a9, 0x0014, 0x4003, 0x60c3, 0x000c, 0x600f, 0x0000, 0x080c,
+ 0x5f40, 0x00fe, 0x9006, 0x7092, 0x6043, 0x0008, 0x6042, 0x0005,
+ 0x00f6, 0x7090, 0x7093, 0x0000, 0x9025, 0x0904, 0x5948, 0x6020,
+ 0xd0b4, 0x1904, 0x5946, 0x71a0, 0x81ff, 0x0904, 0x5934, 0x9486,
+ 0x000c, 0x1904, 0x5941, 0x9480, 0x0018, 0x8004, 0x20a8, 0x080c,
+ 0x6087, 0x2011, 0x0260, 0x2019, 0x1c00, 0x220c, 0x2304, 0x9106,
+ 0x11e8, 0x8210, 0x8318, 0x1f04, 0x58ed, 0x6043, 0x0004, 0x2061,
+ 0x0140, 0x605b, 0xbc94, 0x605f, 0xf0f0, 0x2061, 0x0100, 0x6043,
+ 0x0006, 0x708f, 0x0002, 0x709b, 0x0002, 0x2009, 0x07d0, 0x2011,
+ 0x5f16, 0x080c, 0x8648, 0x080c, 0x608e, 0x04c0, 0x080c, 0x6087,
+ 0x2079, 0x0260, 0x7930, 0x918e, 0x1101, 0x1558, 0x7834, 0x9005,
+ 0x1540, 0x7900, 0x918c, 0x00ff, 0x1118, 0x7804, 0x9005, 0x0190,
+ 0x080c, 0x6087, 0x2011, 0x026e, 0x2019, 0x1805, 0x20a9, 0x0004,
+ 0x220c, 0x2304, 0x9102, 0x0230, 0x11a0, 0x8210, 0x8318, 0x1f04,
+ 0x5928, 0x0078, 0x70a3, 0x0000, 0x080c, 0x6087, 0x20e1, 0x0000,
+ 0x2099, 0x0260, 0x20e9, 0x0001, 0x20a1, 0x1c00, 0x20a9, 0x0014,
+ 0x4003, 0x6043, 0x0008, 0x6043, 0x0000, 0x0010, 0x00fe, 0x0005,
+ 0x6040, 0x9085, 0x0100, 0x6042, 0x6020, 0xd0b4, 0x1db8, 0x080c,
+ 0xabfe, 0x20e1, 0x0001, 0x2099, 0x1c00, 0x20e9, 0x0000, 0x20a1,
+ 0x0240, 0x20a9, 0x0014, 0x4003, 0x60c3, 0x000c, 0x2011, 0x19f0,
+ 0x2013, 0x0000, 0x7093, 0x0000, 0x60a3, 0x0056, 0x60a7, 0x9575,
+ 0x080c, 0xa34d, 0x08d8, 0x0005, 0x7098, 0x908a, 0x001d, 0x1a0c,
+ 0x0dd5, 0x000b, 0x0005, 0x599d, 0x59b0, 0x59d9, 0x59f9, 0x5a1f,
+ 0x5a4e, 0x5a74, 0x5aac, 0x5ad2, 0x5b00, 0x5b3b, 0x5b73, 0x5b91,
+ 0x5bbc, 0x5bde, 0x5bf9, 0x5c03, 0x5c37, 0x5c5d, 0x5c8c, 0x5cb2,
+ 0x5cea, 0x5d2e, 0x5d6b, 0x5d8c, 0x5de5, 0x5e07, 0x5e35, 0x5e35,
+ 0x00c6, 0x2061, 0x1800, 0x6003, 0x0007, 0x2061, 0x0100, 0x6004,
+ 0x9084, 0xfff9, 0x6006, 0x00ce, 0x0005, 0x2061, 0x0140, 0x605b,
+ 0xbc94, 0x605f, 0xf0f0, 0x2061, 0x0100, 0x6043, 0x0002, 0x709b,
+ 0x0001, 0x2009, 0x07d0, 0x2011, 0x5f16, 0x080c, 0x8648, 0x0005,
+ 0x00f6, 0x7090, 0x9086, 0x0014, 0x1510, 0x6042, 0x6020, 0xd0b4,
+ 0x11f0, 0x080c, 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1102,
+ 0x11a0, 0x7834, 0x9005, 0x1188, 0x7a38, 0xd2fc, 0x0128, 0x70c4,
+ 0x9005, 0x1110, 0x70c7, 0x0001, 0x2011, 0x5f16, 0x080c, 0x85b0,
+ 0x709b, 0x0010, 0x080c, 0x5c03, 0x0010, 0x7093, 0x0000, 0x00fe,
+ 0x0005, 0x00f6, 0x709b, 0x0003, 0x6043, 0x0004, 0x2011, 0x5f16,
+ 0x080c, 0x85b0, 0x080c, 0x600b, 0x2079, 0x0240, 0x7833, 0x1102,
+ 0x7837, 0x0000, 0x20a9, 0x0008, 0x9f88, 0x000e, 0x200b, 0x0000,
+ 0x8108, 0x1f04, 0x59ee, 0x60c3, 0x0014, 0x080c, 0x5f40, 0x00fe,
+ 0x0005, 0x00f6, 0x7090, 0x9005, 0x0500, 0x2011, 0x5f16, 0x080c,
+ 0x85b0, 0x9086, 0x0014, 0x11b8, 0x080c, 0x6087, 0x2079, 0x0260,
+ 0x7a30, 0x9296, 0x1102, 0x1178, 0x7834, 0x9005, 0x1160, 0x7a38,
+ 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001, 0x709b,
+ 0x0004, 0x0029, 0x0010, 0x080c, 0x6063, 0x00fe, 0x0005, 0x00f6,
+ 0x709b, 0x0005, 0x080c, 0x600b, 0x2079, 0x0240, 0x7833, 0x1103,
+ 0x7837, 0x0000, 0x080c, 0x6087, 0x080c, 0x606a, 0x1170, 0x7084,
+ 0x9005, 0x1158, 0x715c, 0x9186, 0xffff, 0x0138, 0x2011, 0x0008,
+ 0x080c, 0x5ec3, 0x0168, 0x080c, 0x6040, 0x20a9, 0x0008, 0x20e1,
+ 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003,
+ 0x60c3, 0x0014, 0x080c, 0x5f40, 0x00fe, 0x0005, 0x00f6, 0x7090,
+ 0x9005, 0x0500, 0x2011, 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0014,
+ 0x11b8, 0x080c, 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1103,
+ 0x1178, 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4,
+ 0x9005, 0x1110, 0x70c7, 0x0001, 0x709b, 0x0006, 0x0029, 0x0010,
+ 0x080c, 0x6063, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0007, 0x080c,
+ 0x600b, 0x2079, 0x0240, 0x7833, 0x1104, 0x7837, 0x0000, 0x080c,
+ 0x6087, 0x080c, 0x606a, 0x11b8, 0x7084, 0x9005, 0x11a0, 0x7164,
+ 0x9186, 0xffff, 0x0180, 0x9180, 0x3384, 0x200d, 0x918c, 0xff00,
+ 0x810f, 0x2011, 0x0008, 0x080c, 0x5ec3, 0x0180, 0x080c, 0x505a,
+ 0x0110, 0x080c, 0x28e5, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099,
+ 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014,
+ 0x080c, 0x5f40, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x0500,
+ 0x2011, 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0014, 0x11b8, 0x080c,
+ 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1104, 0x1178, 0x7834,
+ 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110,
+ 0x70c7, 0x0001, 0x709b, 0x0008, 0x0029, 0x0010, 0x080c, 0x6063,
+ 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0009, 0x080c, 0x600b, 0x2079,
+ 0x0240, 0x7833, 0x1105, 0x7837, 0x0100, 0x080c, 0x606a, 0x1150,
+ 0x7084, 0x9005, 0x1138, 0x080c, 0x5e36, 0x1188, 0x9085, 0x0001,
+ 0x080c, 0x28e5, 0x20a9, 0x0008, 0x080c, 0x6087, 0x20e1, 0x0000,
+ 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3,
+ 0x0014, 0x080c, 0x5f40, 0x0010, 0x080c, 0x5990, 0x00fe, 0x0005,
+ 0x00f6, 0x7090, 0x9005, 0x05a8, 0x2011, 0x5f16, 0x080c, 0x85b0,
+ 0x9086, 0x0014, 0x1560, 0x080c, 0x6087, 0x2079, 0x0260, 0x7a30,
+ 0x9296, 0x1105, 0x1520, 0x7834, 0x9084, 0x0100, 0x2011, 0x0100,
+ 0x921e, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110,
+ 0x70c7, 0x0001, 0x709b, 0x000a, 0x00b1, 0x0098, 0x9005, 0x1178,
+ 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001,
+ 0x7097, 0x0000, 0x709b, 0x000e, 0x080c, 0x5bde, 0x0010, 0x080c,
+ 0x6063, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x000b, 0x2011, 0x1c0e,
+ 0x20e9, 0x0001, 0x22a0, 0x20a9, 0x0040, 0x2019, 0xffff, 0x4304,
+ 0x080c, 0x600b, 0x2079, 0x0240, 0x7833, 0x1106, 0x7837, 0x0000,
+ 0x080c, 0x606a, 0x0118, 0x2013, 0x0000, 0x0020, 0x7060, 0x9085,
+ 0x0100, 0x2012, 0x20a9, 0x0040, 0x2009, 0x024e, 0x2011, 0x1c0e,
+ 0x220e, 0x8210, 0x8108, 0x9186, 0x0260, 0x1128, 0x6810, 0x8000,
+ 0x6812, 0x2009, 0x0240, 0x1f04, 0x5b60, 0x60c3, 0x0084, 0x080c,
+ 0x5f40, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x01c0, 0x2011,
+ 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0084, 0x1178, 0x080c, 0x6087,
+ 0x2079, 0x0260, 0x7a30, 0x9296, 0x1106, 0x1138, 0x7834, 0x9005,
+ 0x1120, 0x709b, 0x000c, 0x0029, 0x0010, 0x080c, 0x6063, 0x00fe,
+ 0x0005, 0x00f6, 0x709b, 0x000d, 0x080c, 0x600b, 0x2079, 0x0240,
+ 0x7833, 0x1107, 0x7837, 0x0000, 0x080c, 0x6087, 0x20a9, 0x0040,
+ 0x2011, 0x026e, 0x2009, 0x024e, 0x220e, 0x8210, 0x8108, 0x9186,
+ 0x0260, 0x1150, 0x6810, 0x8000, 0x6812, 0x2009, 0x0240, 0x6814,
+ 0x8000, 0x6816, 0x2011, 0x0260, 0x1f04, 0x5ba4, 0x60c3, 0x0084,
+ 0x080c, 0x5f40, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x01e0,
+ 0x2011, 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0084, 0x1198, 0x080c,
+ 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1107, 0x1158, 0x7834,
+ 0x9005, 0x1140, 0x7097, 0x0001, 0x080c, 0x5fdd, 0x709b, 0x000e,
+ 0x0029, 0x0010, 0x080c, 0x6063, 0x00fe, 0x0005, 0x918d, 0x0001,
+ 0x080c, 0x60b2, 0x709b, 0x000f, 0x7093, 0x0000, 0x2061, 0x0140,
+ 0x605b, 0xbc85, 0x605f, 0xb5b5, 0x2061, 0x0100, 0x6043, 0x0005,
+ 0x6043, 0x0004, 0x2009, 0x07d0, 0x2011, 0x5f16, 0x080c, 0x85a4,
+ 0x0005, 0x7090, 0x9005, 0x0130, 0x2011, 0x5f16, 0x080c, 0x85b0,
+ 0x709b, 0x0000, 0x0005, 0x709b, 0x0011, 0x080c, 0xabfe, 0x080c,
+ 0x6087, 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9, 0x0000, 0x20a1,
+ 0x0240, 0x7490, 0x9480, 0x0018, 0x9080, 0x0007, 0x9084, 0x03f8,
+ 0x8004, 0x20a8, 0x4003, 0x080c, 0x606a, 0x11a0, 0x717c, 0x81ff,
+ 0x0188, 0x900e, 0x7080, 0x9084, 0x00ff, 0x0160, 0x080c, 0x287c,
+ 0x9186, 0x007e, 0x0138, 0x9186, 0x0080, 0x0120, 0x2011, 0x0008,
+ 0x080c, 0x5ec3, 0x60c3, 0x0014, 0x080c, 0x5f40, 0x0005, 0x00f6,
+ 0x7090, 0x9005, 0x0500, 0x2011, 0x5f16, 0x080c, 0x85b0, 0x9086,
+ 0x0014, 0x11b8, 0x080c, 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296,
+ 0x1103, 0x1178, 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128,
+ 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001, 0x709b, 0x0012, 0x0029,
+ 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0013,
+ 0x080c, 0x6019, 0x2079, 0x0240, 0x7833, 0x1103, 0x7837, 0x0000,
+ 0x080c, 0x6087, 0x080c, 0x606a, 0x1170, 0x7084, 0x9005, 0x1158,
+ 0x715c, 0x9186, 0xffff, 0x0138, 0x2011, 0x0008, 0x080c, 0x5ec3,
+ 0x0168, 0x080c, 0x6040, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099,
+ 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014,
+ 0x080c, 0x5f40, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x0500,
+ 0x2011, 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0014, 0x11b8, 0x080c,
+ 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1104, 0x1178, 0x7834,
+ 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110,
+ 0x70c7, 0x0001, 0x709b, 0x0014, 0x0029, 0x0010, 0x7093, 0x0000,
+ 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0015, 0x080c, 0x6019, 0x2079,
+ 0x0240, 0x7833, 0x1104, 0x7837, 0x0000, 0x080c, 0x6087, 0x080c,
+ 0x606a, 0x11b8, 0x7084, 0x9005, 0x11a0, 0x7164, 0x9186, 0xffff,
+ 0x0180, 0x9180, 0x3384, 0x200d, 0x918c, 0xff00, 0x810f, 0x2011,
+ 0x0008, 0x080c, 0x5ec3, 0x0180, 0x080c, 0x505a, 0x0110, 0x080c,
+ 0x28e5, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9,
+ 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c, 0x5f40,
+ 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x05f0, 0x2011, 0x5f16,
+ 0x080c, 0x85b0, 0x9086, 0x0014, 0x15a8, 0x080c, 0x6087, 0x2079,
+ 0x0260, 0x7a30, 0x9296, 0x1105, 0x1568, 0x7834, 0x9084, 0x0100,
+ 0x2011, 0x0100, 0x921e, 0x1168, 0x9085, 0x0001, 0x080c, 0x60b2,
+ 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001,
+ 0x0080, 0x9005, 0x11b8, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005,
+ 0x1110, 0x70c7, 0x0001, 0x9085, 0x0001, 0x080c, 0x60b2, 0x7097,
+ 0x0000, 0x7a38, 0xd2f4, 0x0110, 0x70df, 0x0008, 0x709b, 0x0016,
+ 0x0029, 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005, 0x080c, 0xabfe,
+ 0x080c, 0x6087, 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9, 0x0000,
+ 0x20a1, 0x0240, 0x20a9, 0x000e, 0x4003, 0x2011, 0x026d, 0x2204,
+ 0x9084, 0x0100, 0x2011, 0x024d, 0x2012, 0x2011, 0x026e, 0x709b,
+ 0x0017, 0x080c, 0x606a, 0x1150, 0x7084, 0x9005, 0x1138, 0x080c,
+ 0x5e36, 0x1188, 0x9085, 0x0001, 0x080c, 0x28e5, 0x20a9, 0x0008,
+ 0x080c, 0x6087, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000,
+ 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c, 0x5f40, 0x0010,
+ 0x080c, 0x5990, 0x0005, 0x00f6, 0x7090, 0x9005, 0x01d8, 0x2011,
+ 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0084, 0x1190, 0x080c, 0x6087,
+ 0x2079, 0x0260, 0x7a30, 0x9296, 0x1106, 0x1150, 0x7834, 0x9005,
+ 0x1138, 0x9006, 0x080c, 0x60b2, 0x709b, 0x0018, 0x0029, 0x0010,
+ 0x7093, 0x0000, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0019, 0x080c,
+ 0x6019, 0x2079, 0x0240, 0x7833, 0x1106, 0x7837, 0x0000, 0x080c,
+ 0x6087, 0x2009, 0x026e, 0x2039, 0x1c0e, 0x20a9, 0x0040, 0x213e,
+ 0x8738, 0x8108, 0x9186, 0x0280, 0x1128, 0x6814, 0x8000, 0x6816,
+ 0x2009, 0x0260, 0x1f04, 0x5d9f, 0x2039, 0x1c0e, 0x080c, 0x606a,
+ 0x11e8, 0x2728, 0x2514, 0x8207, 0x9084, 0x00ff, 0x8000, 0x2018,
+ 0x9294, 0x00ff, 0x8007, 0x9205, 0x202a, 0x7060, 0x2310, 0x8214,
+ 0x92a0, 0x1c0e, 0x2414, 0x938c, 0x0001, 0x0118, 0x9294, 0xff00,
+ 0x0018, 0x9294, 0x00ff, 0x8007, 0x9215, 0x2222, 0x20a9, 0x0040,
+ 0x2009, 0x024e, 0x270e, 0x8738, 0x8108, 0x9186, 0x0260, 0x1128,
+ 0x6810, 0x8000, 0x6812, 0x2009, 0x0240, 0x1f04, 0x5dd2, 0x60c3,
+ 0x0084, 0x080c, 0x5f40, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005,
+ 0x01e0, 0x2011, 0x5f16, 0x080c, 0x85b0, 0x9086, 0x0084, 0x1198,
+ 0x080c, 0x6087, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1107, 0x1158,
+ 0x7834, 0x9005, 0x1140, 0x7097, 0x0001, 0x080c, 0x5fdd, 0x709b,
+ 0x001a, 0x0029, 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005, 0x9085,
+ 0x0001, 0x080c, 0x60b2, 0x709b, 0x001b, 0x080c, 0xabfe, 0x080c,
+ 0x6087, 0x2011, 0x0260, 0x2009, 0x0240, 0x7490, 0x9480, 0x0018,
+ 0x9080, 0x0007, 0x9084, 0x03f8, 0x8004, 0x20a8, 0x220e, 0x8210,
+ 0x8108, 0x9186, 0x0260, 0x1150, 0x6810, 0x8000, 0x6812, 0x2009,
+ 0x0240, 0x6814, 0x8000, 0x6816, 0x2011, 0x0260, 0x1f04, 0x5e1e,
+ 0x60c3, 0x0084, 0x080c, 0x5f40, 0x0005, 0x0005, 0x0086, 0x0096,
+ 0x2029, 0x1848, 0x252c, 0x20a9, 0x0008, 0x2041, 0x1c0e, 0x20e9,
+ 0x0001, 0x28a0, 0x080c, 0x6087, 0x20e1, 0x0000, 0x2099, 0x026e,
+ 0x4003, 0x20a9, 0x0008, 0x2011, 0x0007, 0xd5d4, 0x0108, 0x9016,
+ 0x2800, 0x9200, 0x200c, 0x91a6, 0xffff, 0x1148, 0xd5d4, 0x0110,
+ 0x8210, 0x0008, 0x8211, 0x1f04, 0x5e50, 0x0804, 0x5ebf, 0x82ff,
+ 0x1160, 0xd5d4, 0x0120, 0x91a6, 0x3fff, 0x0d90, 0x0020, 0x91a6,
+ 0x3fff, 0x0904, 0x5ebf, 0x918d, 0xc000, 0x20a9, 0x0010, 0x2019,
+ 0x0001, 0xd5d4, 0x0110, 0x2019, 0x0010, 0x2120, 0xd5d4, 0x0110,
+ 0x8423, 0x0008, 0x8424, 0x1240, 0xd5d4, 0x0110, 0x8319, 0x0008,
+ 0x8318, 0x1f04, 0x5e76, 0x04d8, 0x23a8, 0x2021, 0x0001, 0x8426,
+ 0x8425, 0x1f04, 0x5e88, 0x2328, 0x8529, 0x92be, 0x0007, 0x0158,
+ 0x0006, 0x2039, 0x0007, 0x2200, 0x973a, 0x000e, 0x27a8, 0x95a8,
+ 0x0010, 0x1f04, 0x5e97, 0x755e, 0x95c8, 0x3384, 0x292d, 0x95ac,
+ 0x00ff, 0x7582, 0x6532, 0x6536, 0x0016, 0x2508, 0x080c, 0x28c5,
+ 0x001e, 0x60e7, 0x0000, 0x65ea, 0x2018, 0x2304, 0x9405, 0x201a,
+ 0x7087, 0x0001, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x20e1, 0x0001,
+ 0x2898, 0x20a9, 0x0008, 0x4003, 0x9085, 0x0001, 0x0008, 0x9006,
+ 0x009e, 0x008e, 0x0005, 0x0156, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x22a8, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x2011,
+ 0x024e, 0x22a0, 0x4003, 0x014e, 0x013e, 0x01de, 0x01ce, 0x015e,
+ 0x2118, 0x9026, 0x2001, 0x0007, 0x939a, 0x0010, 0x0218, 0x8420,
+ 0x8001, 0x0cd0, 0x2118, 0x84ff, 0x0120, 0x939a, 0x0010, 0x8421,
+ 0x1de0, 0x2021, 0x0001, 0x83ff, 0x0118, 0x8423, 0x8319, 0x1de8,
+ 0x9238, 0x2029, 0x026e, 0x9528, 0x2504, 0x942c, 0x11b8, 0x9405,
+ 0x203a, 0x715e, 0x91a0, 0x3384, 0x242d, 0x95ac, 0x00ff, 0x7582,
+ 0x6532, 0x6536, 0x0016, 0x2508, 0x080c, 0x28c5, 0x001e, 0x60e7,
+ 0x0000, 0x65ea, 0x7087, 0x0001, 0x9084, 0x0000, 0x0005, 0x00e6,
+ 0x2071, 0x1800, 0x708b, 0x0000, 0x00ee, 0x0005, 0x00e6, 0x00f6,
+ 0x2079, 0x0100, 0x2071, 0x0140, 0x080c, 0x5fcc, 0x080c, 0xa356,
+ 0x7004, 0x9084, 0x4000, 0x0110, 0x080c, 0x2d5e, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x1826, 0x2073, 0x0000, 0x7840, 0x0026, 0x0016,
+ 0x2009, 0x00f7, 0x080c, 0x6029, 0x001e, 0x9094, 0x0010, 0x9285,
+ 0x0080, 0x7842, 0x7a42, 0x002e, 0x012e, 0x00fe, 0x00ee, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x2be3, 0x0228, 0x2011, 0x0101,
+ 0x2204, 0xc0c5, 0x2012, 0x2011, 0x19f0, 0x2013, 0x0000, 0x7093,
+ 0x0000, 0x012e, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0xa34d,
+ 0x6144, 0xd184, 0x0120, 0x7198, 0x918d, 0x2000, 0x0018, 0x718c,
+ 0x918d, 0x1000, 0x2011, 0x1998, 0x2112, 0x2009, 0x07d0, 0x2011,
+ 0x5f16, 0x080c, 0x8648, 0x0005, 0x0016, 0x0026, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0xaeb4, 0x2009, 0x00f7, 0x080c, 0x6029,
+ 0x2061, 0x19f9, 0x900e, 0x611a, 0x611e, 0x6172, 0x6176, 0x2061,
+ 0x1800, 0x6003, 0x0001, 0x2061, 0x0100, 0x6043, 0x0090, 0x6043,
+ 0x0010, 0x2009, 0x1998, 0x200b, 0x0000, 0x2009, 0x002d, 0x2011,
+ 0x5f98, 0x080c, 0x85a4, 0x012e, 0x00ce, 0x002e, 0x001e, 0x0005,
+ 0x00e6, 0x0006, 0x0126, 0x2091, 0x8000, 0x0471, 0x2071, 0x0100,
+ 0x080c, 0xa356, 0x2071, 0x0140, 0x7004, 0x9084, 0x4000, 0x0110,
+ 0x080c, 0x2d5e, 0x080c, 0x7446, 0x0188, 0x080c, 0x7461, 0x1170,
+ 0x080c, 0x772e, 0x0016, 0x080c, 0x2994, 0x2001, 0x196c, 0x2102,
+ 0x001e, 0x080c, 0x7729, 0x080c, 0x736a, 0x0050, 0x2009, 0x0001,
+ 0x080c, 0x2c7c, 0x2001, 0x0001, 0x080c, 0x2828, 0x080c, 0x5f6c,
+ 0x012e, 0x000e, 0x00ee, 0x0005, 0x2001, 0x180e, 0x2004, 0xd0bc,
+ 0x0158, 0x0026, 0x0036, 0x2011, 0x8017, 0x2001, 0x1998, 0x201c,
+ 0x080c, 0x4b7f, 0x003e, 0x002e, 0x0005, 0x20a9, 0x0012, 0x20e9,
+ 0x0001, 0x20a1, 0x1c80, 0x080c, 0x6087, 0x20e9, 0x0000, 0x2099,
+ 0x026e, 0x0099, 0x20a9, 0x0020, 0x080c, 0x6081, 0x2099, 0x0260,
+ 0x20a1, 0x1c92, 0x0051, 0x20a9, 0x000e, 0x080c, 0x6084, 0x2099,
+ 0x0260, 0x20a1, 0x1cb2, 0x0009, 0x0005, 0x0016, 0x0026, 0x3410,
+ 0x3308, 0x2104, 0x8007, 0x2012, 0x8108, 0x8210, 0x1f04, 0x6001,
+ 0x002e, 0x001e, 0x0005, 0x080c, 0xabfe, 0x20e1, 0x0001, 0x2099,
+ 0x1c00, 0x20e9, 0x0000, 0x20a1, 0x0240, 0x20a9, 0x000c, 0x4003,
+ 0x0005, 0x080c, 0xabfe, 0x080c, 0x6087, 0x20e1, 0x0000, 0x2099,
+ 0x0260, 0x20e9, 0x0000, 0x20a1, 0x0240, 0x20a9, 0x000c, 0x4003,
+ 0x0005, 0x00c6, 0x0006, 0x2061, 0x0100, 0x810f, 0x2001, 0x1834,
+ 0x2004, 0x9005, 0x1138, 0x2001, 0x1818, 0x2004, 0x9084, 0x00ff,
+ 0x9105, 0x0010, 0x9185, 0x00f7, 0x604a, 0x000e, 0x00ce, 0x0005,
+ 0x0016, 0x0046, 0x080c, 0x6a08, 0x0158, 0x9006, 0x2020, 0x2009,
+ 0x002a, 0x080c, 0xe73a, 0x2001, 0x180c, 0x200c, 0xc195, 0x2102,
+ 0x2019, 0x002a, 0x900e, 0x080c, 0x31e9, 0x080c, 0xd388, 0x0140,
+ 0x0036, 0x2019, 0xffff, 0x2021, 0x0007, 0x080c, 0x4d36, 0x003e,
+ 0x004e, 0x001e, 0x0005, 0x080c, 0x5f6c, 0x709b, 0x0000, 0x7093,
+ 0x0000, 0x0005, 0x0006, 0x2001, 0x180c, 0x2004, 0xd09c, 0x0100,
+ 0x000e, 0x0005, 0x0006, 0x0016, 0x0126, 0x2091, 0x8000, 0x2001,
+ 0x0101, 0x200c, 0x918d, 0x0006, 0x2102, 0x012e, 0x001e, 0x000e,
+ 0x0005, 0x2009, 0x0001, 0x0020, 0x2009, 0x0002, 0x0008, 0x900e,
+ 0x6814, 0x9084, 0xffc0, 0x910d, 0x6916, 0x0005, 0x00f6, 0x0156,
+ 0x0146, 0x01d6, 0x9006, 0x20a9, 0x0080, 0x20e9, 0x0001, 0x20a1,
+ 0x1c00, 0x4004, 0x2079, 0x1c00, 0x7803, 0x2200, 0x7807, 0x00ef,
+ 0x780f, 0x00ef, 0x7813, 0x0138, 0x7823, 0xffff, 0x7827, 0xffff,
+ 0x01de, 0x014e, 0x015e, 0x00fe, 0x0005, 0x2001, 0x1800, 0x2003,
+ 0x0001, 0x0005, 0x2001, 0x19a5, 0x0118, 0x2003, 0x0001, 0x0010,
+ 0x2003, 0x0000, 0x0005, 0x0156, 0x20a9, 0x0800, 0x2009, 0x1000,
+ 0x9006, 0x200a, 0x8108, 0x1f04, 0x60c1, 0x015e, 0x0005, 0x00d6,
+ 0x0036, 0x0156, 0x0136, 0x0146, 0x2069, 0x1847, 0x9006, 0xb802,
+ 0xb8ce, 0xb807, 0x0707, 0xb80a, 0xb80e, 0xb812, 0x9198, 0x3384,
+ 0x231d, 0x939c, 0x00ff, 0xbb16, 0x0016, 0x0026, 0xb8c2, 0x080c,
+ 0xaead, 0x1120, 0x9192, 0x007e, 0x1208, 0xbbc2, 0x20a9, 0x0004,
+ 0xb8c4, 0x20e8, 0xb9c8, 0x9198, 0x0006, 0x9006, 0x23a0, 0x4004,
+ 0x20a9, 0x0004, 0x9198, 0x000a, 0x23a0, 0x4004, 0x002e, 0x001e,
+ 0xb83e, 0xb842, 0xb84e, 0xb852, 0xb856, 0xb85a, 0xb85e, 0xb862,
+ 0xb866, 0xb86a, 0xb86f, 0x0100, 0xb872, 0xb876, 0xb87a, 0xb88a,
+ 0xb88e, 0xb893, 0x0008, 0xb896, 0xb89a, 0xb89e, 0xb8be, 0xb9a2,
+ 0x0096, 0xb8a4, 0x904d, 0x0110, 0x080c, 0x1031, 0xb8a7, 0x0000,
+ 0x009e, 0x9006, 0xb84a, 0x6810, 0xb83a, 0x680c, 0xb846, 0xb8bb,
+ 0x0520, 0xb8ac, 0x9005, 0x0198, 0x00c6, 0x2060, 0x9c82, 0x1cd0,
+ 0x0a0c, 0x0dd5, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1a0c, 0x0dd5,
+ 0x080c, 0x8a3d, 0x00ce, 0x090c, 0x8dda, 0xb8af, 0x0000, 0x6814,
+ 0x9084, 0x00ff, 0xb842, 0x014e, 0x013e, 0x015e, 0x003e, 0x00de,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0xa974, 0xae78, 0x9684, 0x3fff,
+ 0x9082, 0x4000, 0x1a04, 0x61af, 0x9182, 0x0800, 0x1a04, 0x61b3,
+ 0x2001, 0x180c, 0x2004, 0x9084, 0x0003, 0x1904, 0x61b9, 0x9188,
+ 0x1000, 0x2104, 0x905d, 0x0518, 0xb804, 0x9084, 0x00ff, 0x908e,
+ 0x0006, 0x1508, 0xb8a4, 0x900d, 0x1904, 0x61cb, 0xb850, 0x900d,
+ 0x1148, 0xa802, 0x2900, 0xb852, 0xb84e, 0x080c, 0x91ce, 0x9006,
+ 0x012e, 0x0005, 0x00a6, 0x2150, 0x2900, 0xb002, 0xa803, 0x0000,
+ 0x00ae, 0xb852, 0x0c90, 0x2001, 0x0005, 0x900e, 0x04b8, 0x2001,
+ 0x0028, 0x900e, 0x0498, 0x9082, 0x0006, 0x1290, 0x080c, 0xaead,
+ 0x1160, 0xb8a0, 0x9084, 0xff80, 0x1140, 0xb900, 0xd1fc, 0x0990,
+ 0x2001, 0x0029, 0x2009, 0x1000, 0x0408, 0x2001, 0x0028, 0x00a8,
+ 0x2009, 0x180c, 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0068,
+ 0xd184, 0x0118, 0x2001, 0x0004, 0x0040, 0x2001, 0x0029, 0xb900,
+ 0xd1fc, 0x0118, 0x2009, 0x1000, 0x0048, 0x900e, 0x0038, 0x2001,
+ 0x0029, 0x900e, 0x0018, 0x2001, 0x0029, 0x900e, 0x9005, 0x012e,
+ 0x0005, 0x2001, 0x180c, 0x2004, 0xd084, 0x19d0, 0x9188, 0x1000,
+ 0x2104, 0x905d, 0x09a8, 0x080c, 0x6a0c, 0x1990, 0xb800, 0xd0bc,
+ 0x0978, 0x0804, 0x6162, 0x080c, 0x6835, 0x0904, 0x617b, 0x0804,
+ 0x6166, 0x00b6, 0x00e6, 0x0126, 0x2091, 0x8000, 0xa874, 0x908e,
+ 0x00ff, 0x1120, 0x2001, 0x196a, 0x205c, 0x0060, 0xa974, 0x9182,
+ 0x0800, 0x1690, 0x9188, 0x1000, 0x2104, 0x905d, 0x01d0, 0x080c,
+ 0x69ac, 0x11d0, 0x080c, 0xaeed, 0x0570, 0x2b00, 0x6012, 0x2900,
+ 0x6016, 0x6023, 0x0009, 0x600b, 0x0000, 0xa874, 0x908e, 0x00ff,
+ 0x1110, 0x600b, 0x8000, 0x2009, 0x0043, 0x080c, 0xafbe, 0x9006,
+ 0x00b0, 0x2001, 0x0028, 0x0090, 0x2009, 0x180c, 0x210c, 0xd18c,
+ 0x0118, 0x2001, 0x0004, 0x0038, 0xd184, 0x0118, 0x2001, 0x0004,
+ 0x0010, 0x2001, 0x0029, 0x0010, 0x2001, 0x0029, 0x9005, 0x012e,
+ 0x00ee, 0x00be, 0x0005, 0x2001, 0x002c, 0x0cc0, 0x00b6, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0xa974, 0x9182, 0x0800, 0x1a04, 0x629c,
+ 0x9188, 0x1000, 0x2104, 0x905d, 0x0904, 0x6274, 0xb8a0, 0x9086,
+ 0x007f, 0x0190, 0xa87c, 0xd0fc, 0x1178, 0x080c, 0x6a14, 0x0160,
+ 0xa994, 0x81ff, 0x0130, 0x908e, 0x0004, 0x0130, 0x908e, 0x0005,
+ 0x0118, 0x080c, 0x6a0c, 0x1598, 0xa87c, 0xd0fc, 0x01e0, 0xa894,
+ 0x9005, 0x01c8, 0x2060, 0x0026, 0x2010, 0x080c, 0xcc74, 0x002e,
+ 0x1120, 0x2001, 0x0008, 0x0804, 0x629e, 0x6020, 0x9086, 0x000a,
+ 0x0120, 0x2001, 0x0008, 0x0804, 0x629e, 0x601a, 0x6003, 0x0008,
+ 0x2900, 0x6016, 0x0058, 0x080c, 0xaeed, 0x05e8, 0x2b00, 0x6012,
+ 0x2900, 0x6016, 0x600b, 0xffff, 0x6023, 0x000a, 0x2009, 0x0003,
+ 0x080c, 0xafbe, 0x9006, 0x0458, 0x2001, 0x0028, 0x0438, 0x9082,
+ 0x0006, 0x1290, 0x080c, 0xaead, 0x1160, 0xb8a0, 0x9084, 0xff80,
+ 0x1140, 0xb900, 0xd1fc, 0x0900, 0x2001, 0x0029, 0x2009, 0x1000,
+ 0x00a8, 0x2001, 0x0028, 0x0090, 0x2009, 0x180c, 0x210c, 0xd18c,
+ 0x0118, 0x2001, 0x0004, 0x0050, 0xd184, 0x0118, 0x2001, 0x0004,
+ 0x0028, 0x2001, 0x0029, 0x0010, 0x2001, 0x0029, 0x9005, 0x012e,
+ 0x00ee, 0x00be, 0x0005, 0x2001, 0x002c, 0x0cc0, 0x00f6, 0x00b6,
+ 0x0126, 0x2091, 0x8000, 0xa8e0, 0x9005, 0x1550, 0xa8dc, 0x9082,
+ 0x0101, 0x1630, 0xa8c8, 0x9005, 0x1518, 0xa8c4, 0x9082, 0x0101,
+ 0x12f8, 0xa974, 0x2079, 0x1800, 0x9182, 0x0800, 0x12e8, 0x7830,
+ 0x9084, 0x0003, 0x1130, 0xaa98, 0xab94, 0xa878, 0x9084, 0x0007,
+ 0x00ea, 0x7930, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038, 0xd184,
+ 0x0118, 0x2001, 0x0004, 0x0010, 0x2001, 0x0029, 0x900e, 0x0038,
+ 0x2001, 0x002c, 0x900e, 0x0018, 0x2001, 0x0029, 0x900e, 0x9006,
+ 0x0008, 0x9005, 0x012e, 0x00be, 0x00fe, 0x0005, 0x6333, 0x62ee,
+ 0x6305, 0x6333, 0x6333, 0x6333, 0x6333, 0x6333, 0x2100, 0x9082,
+ 0x007e, 0x1278, 0x080c, 0x6638, 0x0148, 0x9046, 0xb810, 0x9306,
+ 0x1904, 0x633b, 0xb814, 0x9206, 0x15f0, 0x0028, 0xbb12, 0xba16,
+ 0x0010, 0x080c, 0x4a32, 0x0150, 0x04b0, 0x080c, 0x6699, 0x1598,
+ 0xb810, 0x9306, 0x1580, 0xb814, 0x9206, 0x1568, 0x080c, 0xaeed,
+ 0x0530, 0x2b00, 0x6012, 0x080c, 0xd102, 0x2900, 0x6016, 0x600b,
+ 0xffff, 0x6023, 0x000a, 0xa878, 0x9086, 0x0001, 0x1170, 0x080c,
+ 0x321e, 0x9006, 0x080c, 0x65d5, 0x2001, 0x0002, 0x080c, 0x65e9,
+ 0x2001, 0x0200, 0xb86e, 0xb893, 0x0002, 0x2009, 0x0003, 0x080c,
+ 0xafbe, 0x9006, 0x0068, 0x2001, 0x0001, 0x900e, 0x0038, 0x2001,
+ 0x002c, 0x900e, 0x0018, 0x2001, 0x0028, 0x900e, 0x9005, 0x0000,
+ 0x012e, 0x00be, 0x00fe, 0x0005, 0x00b6, 0x00f6, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0xa894, 0x90c6, 0x0015, 0x0904, 0x6526, 0x90c6,
+ 0x0056, 0x0904, 0x652a, 0x90c6, 0x0066, 0x0904, 0x652e, 0x90c6,
+ 0x0067, 0x0904, 0x6532, 0x90c6, 0x0068, 0x0904, 0x6536, 0x90c6,
+ 0x0071, 0x0904, 0x653a, 0x90c6, 0x0074, 0x0904, 0x653e, 0x90c6,
+ 0x007c, 0x0904, 0x6542, 0x90c6, 0x007e, 0x0904, 0x6546, 0x90c6,
+ 0x0037, 0x0904, 0x654a, 0x9016, 0x2079, 0x1800, 0xa974, 0x9186,
+ 0x00ff, 0x0904, 0x6521, 0x9182, 0x0800, 0x1a04, 0x6521, 0x080c,
+ 0x6699, 0x1198, 0xb804, 0x9084, 0x00ff, 0x9082, 0x0006, 0x1268,
+ 0xa894, 0x90c6, 0x006f, 0x0148, 0x080c, 0xaead, 0x1904, 0x650a,
+ 0xb8a0, 0x9084, 0xff80, 0x1904, 0x650a, 0xa894, 0x90c6, 0x006f,
+ 0x0158, 0x90c6, 0x005e, 0x0904, 0x646a, 0x90c6, 0x0064, 0x0904,
+ 0x6493, 0x2008, 0x0804, 0x642c, 0xa998, 0xa8b0, 0x2040, 0x080c,
+ 0xaead, 0x1120, 0x9182, 0x007f, 0x0a04, 0x642c, 0x9186, 0x00ff,
+ 0x0904, 0x642c, 0x9182, 0x0800, 0x1a04, 0x642c, 0xaaa0, 0xab9c,
+ 0x787c, 0x9306, 0x11a8, 0x7880, 0x0096, 0x924e, 0x1128, 0x2208,
+ 0x2310, 0x009e, 0x0804, 0x642c, 0x080c, 0xaead, 0x1140, 0x99cc,
+ 0xff00, 0x009e, 0x1128, 0x2208, 0x2310, 0x0804, 0x642c, 0x009e,
+ 0x080c, 0x4a32, 0x0904, 0x6436, 0x900e, 0x9016, 0x90c6, 0x4000,
+ 0x15e0, 0x0006, 0x080c, 0x68b9, 0x1108, 0xc185, 0xb800, 0xd0bc,
+ 0x0108, 0xc18d, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0031, 0x20a0, 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x0006, 0x2098,
+ 0x080c, 0x0f7c, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0035, 0x20a0, 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x000a, 0x2098,
+ 0x080c, 0x0f7c, 0xa8c4, 0xabc8, 0x9305, 0xabcc, 0x9305, 0xabd0,
+ 0x9305, 0xabd4, 0x9305, 0xabd8, 0x9305, 0xabdc, 0x9305, 0xabe0,
+ 0x9305, 0x9005, 0x0510, 0x000e, 0x00c8, 0x90c6, 0x4007, 0x1110,
+ 0x2408, 0x00a0, 0x90c6, 0x4008, 0x1118, 0x2708, 0x2610, 0x0070,
+ 0x90c6, 0x4009, 0x1108, 0x0050, 0x90c6, 0x4006, 0x0138, 0x2001,
+ 0x4005, 0x2009, 0x000a, 0x0010, 0x2001, 0x4006, 0xa896, 0xa99a,
+ 0xaa9e, 0x2001, 0x0030, 0x900e, 0x0478, 0x000e, 0x080c, 0xaeed,
+ 0x1130, 0x2001, 0x4005, 0x2009, 0x0003, 0x9016, 0x0c78, 0x2b00,
+ 0x6012, 0x080c, 0xd102, 0x2900, 0x6016, 0x6023, 0x0001, 0xa868,
+ 0xd88c, 0x0108, 0xc0f5, 0xa86a, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x321e, 0x012e, 0x9006, 0x080c, 0x65d5, 0x2001, 0x0002, 0x080c,
+ 0x65e9, 0x2009, 0x0002, 0x080c, 0xafbe, 0xa8b0, 0xd094, 0x0118,
+ 0xb8cc, 0xc08d, 0xb8ce, 0x9006, 0x9005, 0x012e, 0x00ee, 0x00fe,
+ 0x00be, 0x0005, 0x080c, 0x5771, 0x0118, 0x2009, 0x0007, 0x00f8,
+ 0xa998, 0xaeb0, 0x080c, 0x6699, 0x1904, 0x6427, 0x9186, 0x007f,
+ 0x0130, 0x080c, 0x6a0c, 0x0118, 0x2009, 0x0009, 0x0080, 0x0096,
+ 0x080c, 0x0fff, 0x1120, 0x009e, 0x2009, 0x0002, 0x0040, 0x2900,
+ 0x009e, 0xa806, 0x080c, 0xce6e, 0x19b0, 0x2009, 0x0003, 0x2001,
+ 0x4005, 0x0804, 0x642e, 0xa998, 0xaeb0, 0x080c, 0x6699, 0x1904,
+ 0x6427, 0x0096, 0x080c, 0x0fff, 0x1128, 0x009e, 0x2009, 0x0002,
+ 0x0804, 0x64e7, 0x2900, 0x009e, 0xa806, 0x0096, 0x2048, 0x20a9,
+ 0x002b, 0xb8c4, 0x20e0, 0xb8c8, 0x2098, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0002, 0x20a0, 0x4003, 0x20a9, 0x0008, 0x9080, 0x0006,
+ 0x20a0, 0xbbc8, 0x9398, 0x0006, 0x2398, 0x080c, 0x0f7c, 0x009e,
+ 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0xd684, 0x1168,
+ 0x080c, 0x575d, 0xd0b4, 0x1118, 0xa89b, 0x000b, 0x00e0, 0xb800,
+ 0xd08c, 0x0118, 0xa89b, 0x000c, 0x00b0, 0x080c, 0x6a0c, 0x0118,
+ 0xa89b, 0x0009, 0x0080, 0x080c, 0x5771, 0x0118, 0xa89b, 0x0007,
+ 0x0050, 0x080c, 0xce51, 0x1904, 0x6463, 0x2009, 0x0003, 0x2001,
+ 0x4005, 0x0804, 0x642e, 0xa87b, 0x0030, 0xa897, 0x4005, 0xa804,
+ 0x8006, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080,
+ 0x0002, 0x2009, 0x002b, 0xaaa0, 0xab9c, 0xaca8, 0xada4, 0x2031,
+ 0x0000, 0x2041, 0x1243, 0x080c, 0xb45d, 0x1904, 0x6463, 0x2009,
+ 0x0002, 0x08e8, 0x2001, 0x0028, 0x900e, 0x0804, 0x6464, 0x2009,
+ 0x180c, 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038, 0xd184,
+ 0x0118, 0x2001, 0x0004, 0x0010, 0x2001, 0x0029, 0x900e, 0x0804,
+ 0x6464, 0x2001, 0x0029, 0x900e, 0x0804, 0x6464, 0x080c, 0x37b3,
+ 0x0804, 0x6465, 0x080c, 0x5488, 0x0804, 0x6465, 0x080c, 0x45b1,
+ 0x0804, 0x6465, 0x080c, 0x462a, 0x0804, 0x6465, 0x080c, 0x4686,
+ 0x0804, 0x6465, 0x080c, 0x4af5, 0x0804, 0x6465, 0x080c, 0x4da9,
+ 0x0804, 0x6465, 0x080c, 0x50f0, 0x0804, 0x6465, 0x080c, 0x52e9,
+ 0x0804, 0x6465, 0x080c, 0x39c9, 0x0804, 0x6465, 0x00b6, 0xa974,
+ 0xae78, 0x9684, 0x3fff, 0x9082, 0x4000, 0x1618, 0x9182, 0x0800,
+ 0x1268, 0x9188, 0x1000, 0x2104, 0x905d, 0x0140, 0x080c, 0x6a0c,
+ 0x1148, 0x00e9, 0x080c, 0x67c4, 0x9006, 0x00b0, 0x2001, 0x0028,
+ 0x900e, 0x0090, 0x9082, 0x0006, 0x1240, 0xb900, 0xd1fc, 0x0d88,
+ 0x2001, 0x0029, 0x2009, 0x1000, 0x0038, 0x2001, 0x0029, 0x900e,
+ 0x0018, 0x2001, 0x0029, 0x900e, 0x9005, 0x00be, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0xb850, 0x900d, 0x0150, 0x2900, 0x0096, 0x2148,
+ 0xa802, 0x009e, 0xa803, 0x0000, 0xb852, 0x012e, 0x0005, 0x2900,
+ 0xb852, 0xb84e, 0xa803, 0x0000, 0x0cc0, 0x0126, 0x2091, 0x8000,
+ 0xb84c, 0x9005, 0x0170, 0x00e6, 0x2071, 0x19e6, 0x7004, 0x9086,
+ 0x0002, 0x0168, 0x00ee, 0xb84c, 0xa802, 0x2900, 0xb84e, 0x012e,
+ 0x0005, 0x2900, 0xb852, 0xb84e, 0xa803, 0x0000, 0x0cc0, 0x701c,
+ 0x9b06, 0x1d80, 0xb84c, 0x00a6, 0x2050, 0xb000, 0xa802, 0x2900,
+ 0xb002, 0x00ae, 0x00ee, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0xb84c, 0x904d, 0x0130, 0xa800, 0x9005, 0x1108, 0xb852, 0xb84e,
+ 0x9905, 0x012e, 0x0005, 0xb84c, 0x904d, 0x0130, 0xa800, 0x9005,
+ 0x1108, 0xb852, 0xb84e, 0x9905, 0x0005, 0x00b6, 0x0126, 0x00c6,
+ 0x0026, 0x2091, 0x8000, 0x6210, 0x2258, 0xba00, 0x9005, 0x0110,
+ 0xc285, 0x0008, 0xc284, 0xba02, 0x002e, 0x00ce, 0x012e, 0x00be,
+ 0x0005, 0x00b6, 0x0126, 0x00c6, 0x2091, 0x8000, 0x6210, 0x2258,
+ 0xba04, 0x0006, 0x9086, 0x0006, 0x1170, 0xb89c, 0xd0ac, 0x0158,
+ 0x080c, 0x6a08, 0x0140, 0x9284, 0xff00, 0x8007, 0x9086, 0x0007,
+ 0x1110, 0x2011, 0x0600, 0x000e, 0x9294, 0xff00, 0x9215, 0xba06,
+ 0x0006, 0x9086, 0x0006, 0x1120, 0xba90, 0x82ff, 0x090c, 0x0dd5,
+ 0x000e, 0x00ce, 0x012e, 0x00be, 0x0005, 0x00b6, 0x0126, 0x00c6,
+ 0x2091, 0x8000, 0x6210, 0x2258, 0xba04, 0x0006, 0x9086, 0x0006,
+ 0x1168, 0xb89c, 0xd0a4, 0x0150, 0x080c, 0x6a04, 0x1138, 0x9284,
+ 0x00ff, 0x9086, 0x0007, 0x1110, 0x2011, 0x0006, 0x000e, 0x9294,
+ 0x00ff, 0x8007, 0x9215, 0xba06, 0x00ce, 0x012e, 0x00be, 0x0005,
+ 0x9182, 0x0800, 0x0218, 0x9085, 0x0001, 0x0005, 0x00d6, 0x0026,
+ 0x9190, 0x1000, 0x2204, 0x905d, 0x1188, 0x0096, 0x080c, 0x0fff,
+ 0x2958, 0x009e, 0x0168, 0x2b00, 0x2012, 0xb85c, 0xb8ca, 0xb860,
+ 0xb8c6, 0x9006, 0xb8a6, 0xb8ae, 0x080c, 0x60c7, 0x9006, 0x0010,
+ 0x9085, 0x0001, 0x002e, 0x00de, 0x0005, 0x00b6, 0x0096, 0x0126,
+ 0x2091, 0x8000, 0x0026, 0x9182, 0x0800, 0x0218, 0x9085, 0x0001,
+ 0x0458, 0x00d6, 0x9190, 0x1000, 0x2204, 0x905d, 0x0518, 0x2013,
+ 0x0000, 0xb8a4, 0x904d, 0x0110, 0x080c, 0x1031, 0x00d6, 0x00c6,
+ 0xb8bc, 0x2060, 0x8cff, 0x0168, 0x600c, 0x0006, 0x6014, 0x2048,
+ 0x080c, 0xcc86, 0x0110, 0x080c, 0x0fb1, 0x080c, 0xaf43, 0x00ce,
+ 0x0c88, 0x00ce, 0x00de, 0x2b48, 0xb8c8, 0xb85e, 0xb8c4, 0xb862,
+ 0x080c, 0x1041, 0x00de, 0x9006, 0x002e, 0x012e, 0x009e, 0x00be,
+ 0x0005, 0x0016, 0x9182, 0x0800, 0x0218, 0x9085, 0x0001, 0x0030,
+ 0x9188, 0x1000, 0x2104, 0x905d, 0x0dc0, 0x9006, 0x001e, 0x0005,
+ 0x00d6, 0x0156, 0x0136, 0x0146, 0x9006, 0xb80a, 0xb80e, 0xb800,
+ 0xc08c, 0xb802, 0x080c, 0x743e, 0x1510, 0xb8a0, 0x9086, 0x007e,
+ 0x0120, 0x080c, 0xaead, 0x11d8, 0x0078, 0x7040, 0xd0e4, 0x01b8,
+ 0x00c6, 0x2061, 0x1981, 0x7048, 0x2062, 0x704c, 0x6006, 0x7050,
+ 0x600a, 0x7054, 0x600e, 0x00ce, 0x703c, 0x2069, 0x0140, 0x9005,
+ 0x1110, 0x2001, 0x0001, 0x6886, 0x2069, 0x1800, 0x68b6, 0x7040,
+ 0xb85e, 0x7048, 0xb862, 0x704c, 0xb866, 0x20e1, 0x0000, 0x2099,
+ 0x0276, 0xb8c4, 0x20e8, 0xb8c8, 0x9088, 0x000a, 0x21a0, 0x20a9,
+ 0x0004, 0x4003, 0x2099, 0x027a, 0x9088, 0x0006, 0x21a0, 0x20a9,
+ 0x0004, 0x4003, 0x2069, 0x0200, 0x6817, 0x0001, 0x7040, 0xb86a,
+ 0x7144, 0xb96e, 0x7048, 0xb872, 0x7050, 0xb876, 0x2069, 0x0200,
+ 0x6817, 0x0000, 0xb8a0, 0x9086, 0x007e, 0x1110, 0x7144, 0xb96e,
+ 0x9182, 0x0211, 0x1218, 0x2009, 0x0008, 0x0400, 0x9182, 0x0259,
+ 0x1218, 0x2009, 0x0007, 0x00d0, 0x9182, 0x02c1, 0x1218, 0x2009,
+ 0x0006, 0x00a0, 0x9182, 0x0349, 0x1218, 0x2009, 0x0005, 0x0070,
+ 0x9182, 0x0421, 0x1218, 0x2009, 0x0004, 0x0040, 0x9182, 0x0581,
+ 0x1218, 0x2009, 0x0003, 0x0010, 0x2009, 0x0002, 0xb992, 0x014e,
+ 0x013e, 0x015e, 0x00de, 0x0005, 0x0016, 0x0026, 0x00e6, 0x2071,
+ 0x0260, 0x7034, 0xb896, 0x703c, 0xb89a, 0x7054, 0xb89e, 0x0036,
+ 0xbbcc, 0xc384, 0xba00, 0x2009, 0x1867, 0x210c, 0xd0bc, 0x0120,
+ 0xd1ec, 0x0110, 0xc2ad, 0x0008, 0xc2ac, 0xd0c4, 0x0148, 0xd1e4,
+ 0x0138, 0xc2bd, 0xd0cc, 0x0128, 0xd38c, 0x1108, 0xc385, 0x0008,
+ 0xc2bc, 0xba02, 0xbbce, 0x003e, 0x00ee, 0x002e, 0x001e, 0x0005,
+ 0x0096, 0x0126, 0x2091, 0x8000, 0xb8a4, 0x904d, 0x0578, 0xa900,
+ 0x81ff, 0x15c0, 0xaa04, 0x9282, 0x0010, 0x16c8, 0x0136, 0x0146,
+ 0x01c6, 0x01d6, 0x8906, 0x8006, 0x8007, 0x908c, 0x003f, 0x21e0,
+ 0x9084, 0xffc0, 0x9080, 0x0004, 0x2098, 0x2009, 0x0010, 0x20a9,
+ 0x0001, 0x4002, 0x9086, 0xffff, 0x0120, 0x8109, 0x1dd0, 0x080c,
+ 0x0dd5, 0x3c00, 0x20e8, 0x3300, 0x8001, 0x20a0, 0x4604, 0x8210,
+ 0xaa06, 0x01de, 0x01ce, 0x014e, 0x013e, 0x0060, 0x080c, 0x0fff,
+ 0x0170, 0x2900, 0xb8a6, 0xa803, 0x0000, 0x080c, 0x6855, 0xa807,
+ 0x0001, 0xae12, 0x9085, 0x0001, 0x012e, 0x009e, 0x0005, 0x9006,
+ 0x0cd8, 0x0126, 0x2091, 0x8000, 0x0096, 0xb8a4, 0x904d, 0x0188,
+ 0xa800, 0x9005, 0x1150, 0x080c, 0x6864, 0x1158, 0xa804, 0x908a,
+ 0x0002, 0x0218, 0x8001, 0xa806, 0x0020, 0x080c, 0x1031, 0xb8a7,
+ 0x0000, 0x009e, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x91ce, 0x012e, 0x0005, 0x901e, 0x0010, 0x2019, 0x0001, 0x900e,
+ 0x0126, 0x2091, 0x8000, 0xb84c, 0x2048, 0xb800, 0xd0dc, 0x1170,
+ 0x89ff, 0x0500, 0x83ff, 0x0120, 0xa878, 0x9606, 0x0158, 0x0030,
+ 0xa86c, 0x9406, 0x1118, 0xa870, 0x9506, 0x0120, 0x2908, 0xa800,
+ 0x2048, 0x0c70, 0x080c, 0xa761, 0xaa00, 0xb84c, 0x9906, 0x1110,
+ 0xba4e, 0x0020, 0x00a6, 0x2150, 0xb202, 0x00ae, 0x82ff, 0x1110,
+ 0xb952, 0x89ff, 0x012e, 0x0005, 0x9016, 0x0489, 0x1110, 0x2011,
+ 0x0001, 0x0005, 0x080c, 0x68b9, 0x0128, 0x080c, 0xcd43, 0x0010,
+ 0x9085, 0x0001, 0x0005, 0x080c, 0x68b9, 0x0128, 0x080c, 0xcce8,
+ 0x0010, 0x9085, 0x0001, 0x0005, 0x080c, 0x68b9, 0x0128, 0x080c,
+ 0xcd40, 0x0010, 0x9085, 0x0001, 0x0005, 0x080c, 0x68b9, 0x0128,
+ 0x080c, 0xcd07, 0x0010, 0x9085, 0x0001, 0x0005, 0x080c, 0x68b9,
+ 0x0128, 0x080c, 0xcd86, 0x0010, 0x9085, 0x0001, 0x0005, 0xb8a4,
+ 0x900d, 0x1118, 0x9085, 0x0001, 0x0005, 0x0136, 0x01c6, 0xa800,
+ 0x9005, 0x11b8, 0x890e, 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0,
+ 0x9184, 0xffc0, 0x9080, 0x0004, 0x2098, 0x20a9, 0x0001, 0x2009,
+ 0x0010, 0x4002, 0x9606, 0x0128, 0x8109, 0x1dd8, 0x9085, 0x0001,
+ 0x0008, 0x9006, 0x01ce, 0x013e, 0x0005, 0x0146, 0x01d6, 0xa860,
+ 0x20e8, 0xa85c, 0x9080, 0x0004, 0x20a0, 0x20a9, 0x0010, 0x2009,
+ 0xffff, 0x4104, 0x01de, 0x014e, 0x0136, 0x01c6, 0xa800, 0x9005,
+ 0x11b8, 0x890e, 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184,
+ 0xffc0, 0x9080, 0x0004, 0x2098, 0x20a9, 0x0001, 0x2009, 0x0010,
+ 0x4002, 0x9606, 0x0128, 0x8109, 0x1dd8, 0x9085, 0x0001, 0x0068,
+ 0x0146, 0x01d6, 0x3300, 0x8001, 0x20a0, 0x3c00, 0x20e8, 0x2001,
+ 0xffff, 0x4004, 0x01de, 0x014e, 0x9006, 0x01ce, 0x013e, 0x0005,
+ 0x0096, 0x0126, 0x2091, 0x8000, 0xb8a4, 0x904d, 0x1128, 0x080c,
+ 0x0fff, 0x0168, 0x2900, 0xb8a6, 0x080c, 0x6855, 0xa803, 0x0001,
+ 0xa807, 0x0000, 0x9085, 0x0001, 0x012e, 0x009e, 0x0005, 0x9006,
+ 0x0cd8, 0x0096, 0x0126, 0x2091, 0x8000, 0xb8a4, 0x904d, 0x0130,
+ 0xb8a7, 0x0000, 0x080c, 0x1031, 0x9085, 0x0001, 0x012e, 0x009e,
+ 0x0005, 0xb89c, 0xd0a4, 0x0005, 0x00b6, 0x00f6, 0x080c, 0x743e,
+ 0x01b0, 0x71c4, 0x81ff, 0x1198, 0x71dc, 0xd19c, 0x0180, 0x2001,
+ 0x007e, 0x9080, 0x1000, 0x2004, 0x905d, 0x0148, 0xb804, 0x9084,
+ 0x00ff, 0x9086, 0x0006, 0x1118, 0xb800, 0xc0ed, 0xb802, 0x2079,
+ 0x1847, 0x7804, 0xd0a4, 0x01d0, 0x0156, 0x20a9, 0x007f, 0x900e,
+ 0x0016, 0x080c, 0x6699, 0x1168, 0xb804, 0x9084, 0xff00, 0x8007,
+ 0x9096, 0x0004, 0x0118, 0x9086, 0x0006, 0x1118, 0xb800, 0xc0ed,
+ 0xb802, 0x001e, 0x8108, 0x1f04, 0x68e0, 0x015e, 0x080c, 0x69ca,
+ 0x0120, 0x2001, 0x1984, 0x200c, 0x0038, 0x2079, 0x1847, 0x7804,
+ 0xd0a4, 0x0130, 0x2009, 0x07d0, 0x2011, 0x690b, 0x080c, 0x8648,
+ 0x00fe, 0x00be, 0x0005, 0x00b6, 0x2011, 0x690b, 0x080c, 0x85b0,
+ 0x080c, 0x69ca, 0x01d8, 0x2001, 0x107e, 0x2004, 0x2058, 0xb900,
+ 0xc1ec, 0xb902, 0x080c, 0x6a08, 0x0130, 0x2009, 0x07d0, 0x2011,
+ 0x690b, 0x080c, 0x8648, 0x00e6, 0x2071, 0x1800, 0x9006, 0x707e,
+ 0x7060, 0x7082, 0x080c, 0x2ff5, 0x00ee, 0x04b0, 0x0156, 0x00c6,
+ 0x20a9, 0x007f, 0x900e, 0x0016, 0x080c, 0x6699, 0x1538, 0xb800,
+ 0xd0ec, 0x0520, 0x0046, 0xbaa0, 0x2220, 0x9006, 0x2009, 0x0029,
+ 0x080c, 0xe73a, 0xb800, 0xc0e5, 0xc0ec, 0xb802, 0x080c, 0x6a04,
+ 0x2001, 0x0707, 0x1128, 0xb804, 0x9084, 0x00ff, 0x9085, 0x0700,
+ 0xb806, 0x2019, 0x0029, 0x080c, 0x9356, 0x0076, 0x903e, 0x080c,
+ 0x9229, 0x900e, 0x080c, 0xe477, 0x007e, 0x004e, 0x001e, 0x8108,
+ 0x1f04, 0x6933, 0x00ce, 0x015e, 0x00be, 0x0005, 0x00b6, 0x6010,
+ 0x2058, 0xb800, 0xc0ec, 0xb802, 0x00be, 0x0005, 0x00b6, 0x00c6,
+ 0x0096, 0x080c, 0x1018, 0x090c, 0x0dd5, 0x2958, 0x009e, 0x2001,
+ 0x196a, 0x2b02, 0x8b07, 0x8006, 0x8006, 0x908c, 0x003f, 0xb9c6,
+ 0x908c, 0xffc0, 0xb9ca, 0xb8af, 0x0000, 0x2009, 0x00ff, 0x080c,
+ 0x60c7, 0xb807, 0x0006, 0xb813, 0x00ff, 0xb817, 0xffff, 0xb86f,
+ 0x0200, 0xb86c, 0xb893, 0x0002, 0xb8bb, 0x0520, 0xb8a3, 0x00ff,
+ 0xb8af, 0x0000, 0x00ce, 0x00be, 0x0005, 0x7810, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0ac, 0x0005, 0x6010, 0x00b6, 0x905d, 0x0108,
+ 0xb800, 0x00be, 0xd0bc, 0x0005, 0x0006, 0x0016, 0x0026, 0xb804,
+ 0x908c, 0x00ff, 0x9196, 0x0006, 0x0188, 0x9196, 0x0004, 0x0170,
+ 0x9196, 0x0005, 0x0158, 0x908c, 0xff00, 0x810f, 0x9196, 0x0006,
+ 0x0128, 0x9196, 0x0004, 0x0110, 0x9196, 0x0005, 0x002e, 0x001e,
+ 0x000e, 0x0005, 0x00b6, 0x00f6, 0x2001, 0x107e, 0x2004, 0x905d,
+ 0x0110, 0xb800, 0xd0ec, 0x00fe, 0x00be, 0x0005, 0x0126, 0x0026,
+ 0x2091, 0x8000, 0x0006, 0xbaa0, 0x9290, 0x1000, 0x2204, 0x9b06,
+ 0x190c, 0x0dd5, 0x000e, 0xba00, 0x9005, 0x0110, 0xc2fd, 0x0008,
+ 0xc2fc, 0xba02, 0x002e, 0x012e, 0x0005, 0x2011, 0x1837, 0x2204,
+ 0xd0cc, 0x0138, 0x2001, 0x1982, 0x200c, 0x2011, 0x69fa, 0x080c,
+ 0x8648, 0x0005, 0x2011, 0x69fa, 0x080c, 0x85b0, 0x2011, 0x1837,
+ 0x2204, 0xc0cc, 0x2012, 0x0005, 0x080c, 0x575d, 0xd0ac, 0x0005,
+ 0x080c, 0x575d, 0xd0a4, 0x0005, 0x0016, 0xb904, 0x9184, 0x00ff,
+ 0x908e, 0x0006, 0x001e, 0x0005, 0x0016, 0xb904, 0x9184, 0xff00,
+ 0x8007, 0x908e, 0x0006, 0x001e, 0x0005, 0x00b6, 0x00f6, 0x080c,
+ 0xd388, 0x0158, 0x70dc, 0x9084, 0x0028, 0x0138, 0x2001, 0x107f,
+ 0x2004, 0x905d, 0x0110, 0xb8cc, 0xd094, 0x00fe, 0x00be, 0x0005,
+ 0x2071, 0x1910, 0x7003, 0x0001, 0x7007, 0x0000, 0x9006, 0x7012,
+ 0x7016, 0x701a, 0x701e, 0x700a, 0x7046, 0x0005, 0x0016, 0x00e6,
+ 0x2071, 0x1947, 0x900e, 0x710a, 0x080c, 0x575d, 0xd0fc, 0x1140,
+ 0x080c, 0x575d, 0x900e, 0xd09c, 0x0108, 0x8108, 0x7102, 0x00f8,
+ 0x2001, 0x1867, 0x200c, 0x9184, 0x0007, 0x0002, 0x6a48, 0x6a48,
+ 0x6a48, 0x6a48, 0x6a48, 0x6a5e, 0x6a6c, 0x6a48, 0x7003, 0x0003,
+ 0x2009, 0x1868, 0x210c, 0x9184, 0xff00, 0x8007, 0x9005, 0x1110,
+ 0x2001, 0x0002, 0x7006, 0x0018, 0x7003, 0x0005, 0x0c88, 0x00ee,
+ 0x001e, 0x0005, 0x00e6, 0x2071, 0x0050, 0x684c, 0x9005, 0x1150,
+ 0x00e6, 0x2071, 0x1910, 0x7028, 0xc085, 0x702a, 0x00ee, 0x9085,
+ 0x0001, 0x0488, 0x6844, 0x9005, 0x0158, 0x080c, 0x7796, 0x6a60,
+ 0x9200, 0x7002, 0x6864, 0x9101, 0x7006, 0x9006, 0x7012, 0x7016,
+ 0x6860, 0x7002, 0x6864, 0x7006, 0x6868, 0x700a, 0x686c, 0x700e,
+ 0x6844, 0x9005, 0x1110, 0x7012, 0x7016, 0x684c, 0x701a, 0x701c,
+ 0x9085, 0x0040, 0x701e, 0x7037, 0x0019, 0x702b, 0x0001, 0x00e6,
+ 0x2071, 0x1910, 0x7028, 0xc084, 0x702a, 0x7007, 0x0001, 0x700b,
+ 0x0000, 0x00ee, 0x9006, 0x00ee, 0x0005, 0x00e6, 0x0026, 0x2071,
+ 0x1947, 0x7000, 0x9015, 0x0904, 0x6d1c, 0x9286, 0x0003, 0x0904,
+ 0x6bb2, 0x9286, 0x0005, 0x0904, 0x6bb2, 0x2071, 0x1877, 0xa87c,
+ 0x9005, 0x0904, 0x6b13, 0x7140, 0xa868, 0x9102, 0x0a04, 0x6d1c,
+ 0xa878, 0xd084, 0x15d8, 0xa853, 0x0019, 0x2001, 0x8023, 0xa84e,
+ 0x2071, 0x1910, 0x701c, 0x9005, 0x1904, 0x6eb2, 0x0e04, 0x6f20,
+ 0x2071, 0x0000, 0xa850, 0x7032, 0xa84c, 0x7082, 0xa870, 0x7086,
+ 0xa86c, 0x708a, 0xa880, 0x708e, 0x7036, 0x0146, 0x01d6, 0x0136,
+ 0x01c6, 0x0156, 0x20e9, 0x0000, 0x20a1, 0x002a, 0xa868, 0x20a8,
+ 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0021, 0x2098, 0x4003, 0x015e,
+ 0x01ce, 0x013e, 0x01de, 0x014e, 0x2091, 0x4080, 0x2001, 0x0089,
+ 0x2004, 0xd084, 0x190c, 0x119b, 0x0804, 0x6b95, 0xa853, 0x001b,
+ 0x2001, 0x8027, 0x0820, 0x7004, 0xd08c, 0x1904, 0x6d1c, 0xa853,
+ 0x001a, 0x2001, 0x8024, 0x0804, 0x6ad7, 0x00e6, 0x0026, 0x2071,
+ 0x1947, 0x7000, 0x9015, 0x0904, 0x6d1c, 0x9286, 0x0003, 0x0904,
+ 0x6bb2, 0x9286, 0x0005, 0x0904, 0x6bb2, 0xa84f, 0x8022, 0xa853,
+ 0x0018, 0x0804, 0x6b7a, 0xa868, 0xd0fc, 0x11d8, 0x00e6, 0x0026,
+ 0x2001, 0x1947, 0x2004, 0x9005, 0x0904, 0x6d1c, 0xa87c, 0xd0bc,
+ 0x1904, 0x6d1c, 0xa978, 0xa874, 0x9105, 0x1904, 0x6d1c, 0x2001,
+ 0x1947, 0x2004, 0x0002, 0x6d1c, 0x6b76, 0x6bb2, 0x6bb2, 0x6d1c,
+ 0x6bb2, 0x0005, 0xa868, 0xd0fc, 0x1500, 0x00e6, 0x0026, 0x2009,
+ 0x1947, 0x210c, 0x81ff, 0x0904, 0x6d1c, 0xa87c, 0xd0cc, 0x0904,
+ 0x6d1c, 0xa880, 0x9084, 0x00ff, 0x9086, 0x0001, 0x1904, 0x6d1c,
+ 0x9186, 0x0003, 0x0904, 0x6bb2, 0x9186, 0x0005, 0x0904, 0x6bb2,
+ 0xa84f, 0x8021, 0xa853, 0x0017, 0x0028, 0x0005, 0xa84f, 0x8020,
+ 0xa853, 0x0016, 0x2071, 0x1910, 0x701c, 0x9005, 0x1904, 0x6eb2,
+ 0x0e04, 0x6f20, 0x2071, 0x0000, 0xa84c, 0x7082, 0xa850, 0x7032,
+ 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x2071, 0x1800, 0x2011,
+ 0x0001, 0xa804, 0x900d, 0x702c, 0x1158, 0xa802, 0x2900, 0x702e,
+ 0x70c0, 0x9200, 0x70c2, 0x080c, 0x84c2, 0x002e, 0x00ee, 0x0005,
+ 0x0096, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8,
+ 0x009e, 0x0c58, 0xa84f, 0x0000, 0x00f6, 0x2079, 0x0050, 0x2071,
+ 0x1910, 0xa803, 0x0000, 0x7010, 0x9005, 0x1904, 0x6ca1, 0x782c,
+ 0x908c, 0x0780, 0x190c, 0x706e, 0x8004, 0x8004, 0x8004, 0x9084,
+ 0x0003, 0x0002, 0x6bd0, 0x6ca1, 0x6bf5, 0x6c3c, 0x080c, 0x0dd5,
+ 0x2071, 0x1800, 0x2900, 0x7822, 0xa804, 0x900d, 0x1170, 0x2071,
+ 0x19f9, 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004, 0x8005,
+ 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0,
+ 0x9200, 0x70c2, 0x080c, 0x84c2, 0x0c10, 0x2071, 0x1800, 0x2900,
+ 0x7822, 0xa804, 0x900d, 0x1580, 0x7824, 0x00e6, 0x2071, 0x0040,
+ 0x712c, 0xd19c, 0x1148, 0x2009, 0x1830, 0x210c, 0x918a, 0x0020,
+ 0x0218, 0x7022, 0x00ee, 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802,
+ 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c, 0x84c2, 0x782c,
+ 0x9094, 0x0780, 0x190c, 0x706e, 0xd0a4, 0x19f0, 0x2071, 0x19f9,
+ 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004, 0x8005, 0x703e,
+ 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148, 0xa904,
+ 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200,
+ 0x70c2, 0x080c, 0x84c2, 0x0800, 0x0096, 0x00e6, 0x7824, 0x2048,
+ 0x2071, 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000,
+ 0x70c2, 0x080c, 0x84c2, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e,
+ 0xd0a4, 0x1d60, 0x00ee, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e,
+ 0xd09c, 0x11a0, 0x009e, 0x2900, 0x7822, 0xa804, 0x900d, 0x1560,
+ 0x2071, 0x19f9, 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004,
+ 0x8005, 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x009e, 0x2908,
+ 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110, 0xa902,
+ 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1170, 0x2071, 0x19f9,
+ 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004, 0x8005, 0x703e,
+ 0x00fe, 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c,
+ 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e,
+ 0x70c0, 0x9200, 0x70c2, 0x080c, 0x84c2, 0x00fe, 0x002e, 0x00ee,
+ 0x0005, 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a,
+ 0x0110, 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1904,
+ 0x6cf6, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e, 0xd09c, 0x1198,
+ 0x701c, 0x904d, 0x0180, 0x7010, 0x8001, 0x7012, 0x1108, 0x701a,
+ 0xa800, 0x701e, 0x2900, 0x7822, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x706e, 0xd09c, 0x0d68, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e,
+ 0xd0a4, 0x01b0, 0x00e6, 0x7824, 0x2048, 0x2071, 0x1800, 0x702c,
+ 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c, 0x84c2,
+ 0x782c, 0x9094, 0x0780, 0x190c, 0x706e, 0xd0a4, 0x1d60, 0x00ee,
+ 0x2071, 0x19f9, 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004,
+ 0x8005, 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x00e6, 0x2071,
+ 0x1800, 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900,
+ 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x84c2,
+ 0x00ee, 0x0804, 0x6cb1, 0xa868, 0xd0fc, 0x1560, 0x0096, 0xa804,
+ 0xa807, 0x0000, 0x904d, 0x190c, 0x0fb1, 0x009e, 0x0018, 0xa868,
+ 0xd0fc, 0x1500, 0x00e6, 0x0026, 0xa84f, 0x0000, 0x00f6, 0x2079,
+ 0x0050, 0x2071, 0x1910, 0xa803, 0x0000, 0x7010, 0x9005, 0x1904,
+ 0x6e30, 0x782c, 0x908c, 0x0780, 0x190c, 0x706e, 0x8004, 0x8004,
+ 0x8004, 0x9084, 0x0003, 0x0002, 0x6d3b, 0x6e30, 0x6d56, 0x6dc3,
+ 0x080c, 0x0dd5, 0x0005, 0x2071, 0x1800, 0x2900, 0x7822, 0xa804,
+ 0x900d, 0x1120, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c,
+ 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e,
+ 0x70c0, 0x9200, 0x70c2, 0x080c, 0x84c2, 0x0c60, 0x2071, 0x1800,
+ 0x2900, 0x7822, 0xa804, 0x900d, 0x1904, 0x6db2, 0x7830, 0x8007,
+ 0x9084, 0x001f, 0x9082, 0x0001, 0x1220, 0x00fe, 0x002e, 0x00ee,
+ 0x0005, 0x7824, 0x00e6, 0x2071, 0x0040, 0x712c, 0xd19c, 0x1148,
+ 0x2009, 0x1830, 0x210c, 0x918a, 0x0020, 0x0218, 0x7022, 0x00ee,
+ 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0,
+ 0x8000, 0x70c2, 0x080c, 0x84c2, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x706e, 0xd0a4, 0x19f0, 0x0e04, 0x6da9, 0x7838, 0x7938, 0x910e,
+ 0x1de0, 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de,
+ 0x2001, 0x1921, 0x200c, 0xc184, 0x2102, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x00fe, 0x002e, 0x00ee,
+ 0x0005, 0x2001, 0x1921, 0x200c, 0xc185, 0x2102, 0x00fe, 0x002e,
+ 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210,
+ 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c,
+ 0x84c2, 0x0804, 0x6d69, 0x0096, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2,
+ 0x080c, 0x84c2, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e, 0xd0a4,
+ 0x1d60, 0x00ee, 0x0e04, 0x6e03, 0x7838, 0x7938, 0x910e, 0x1de0,
+ 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x7044,
+ 0xc084, 0x7046, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x119b, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e, 0xd09c,
+ 0x1170, 0x009e, 0x2900, 0x7822, 0xa804, 0x900d, 0x11e0, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x7044, 0xc085, 0x7046, 0x0c58, 0x009e,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1120, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0,
+ 0x9200, 0x70c2, 0x080c, 0x84c2, 0x00fe, 0x002e, 0x00ee, 0x0005,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1904, 0x6e9d,
+ 0x782c, 0x9094, 0x0780, 0x190c, 0x706e, 0xd09c, 0x11b0, 0x701c,
+ 0x904d, 0x0198, 0xa84c, 0x9005, 0x1180, 0x7010, 0x8001, 0x7012,
+ 0x1108, 0x701a, 0xa800, 0x701e, 0x2900, 0x7822, 0x782c, 0x9094,
+ 0x0780, 0x190c, 0x706e, 0xd09c, 0x0d50, 0x782c, 0x9094, 0x0780,
+ 0x190c, 0x706e, 0xd0a4, 0x05a8, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2,
+ 0x080c, 0x84c2, 0x782c, 0x9094, 0x0780, 0x190c, 0x706e, 0xd0a4,
+ 0x1d60, 0x00ee, 0x0e04, 0x6e96, 0x7838, 0x7938, 0x910e, 0x1de0,
+ 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x7044,
+ 0xc084, 0x7046, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x119b, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x7044, 0xc085,
+ 0x7046, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x84c2, 0x00ee,
+ 0x0804, 0x6e40, 0x2071, 0x1910, 0xa803, 0x0000, 0x2908, 0x7010,
+ 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110, 0xa902, 0x0008,
+ 0x711e, 0x2148, 0xa804, 0x900d, 0x1128, 0x1e04, 0x6edd, 0x002e,
+ 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148, 0xa904,
+ 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200,
+ 0x70c2, 0x080c, 0x84c2, 0x0e04, 0x6ec7, 0x2071, 0x1910, 0x701c,
+ 0x2048, 0xa84c, 0x900d, 0x0d18, 0x2071, 0x0000, 0x7182, 0xa850,
+ 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0xa850, 0x9082,
+ 0x0019, 0x1278, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x119b, 0x2071, 0x1910, 0x080c, 0x705a, 0x002e, 0x00ee,
+ 0x0005, 0xa850, 0x9082, 0x001c, 0x1e68, 0xa880, 0x708e, 0x7036,
+ 0x0146, 0x01d6, 0x0136, 0x01c6, 0x0156, 0x20e9, 0x0000, 0x20a1,
+ 0x002a, 0xa868, 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0021,
+ 0x2098, 0x4003, 0x015e, 0x01ce, 0x013e, 0x01de, 0x014e, 0x0890,
+ 0x2071, 0x1910, 0xa803, 0x0000, 0x2908, 0x7010, 0x8000, 0x7012,
+ 0x7018, 0x904d, 0x711a, 0x0110, 0xa902, 0x0008, 0x711e, 0x2148,
+ 0xa804, 0x900d, 0x1118, 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800,
+ 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x84c2, 0x002e,
+ 0x00ee, 0x0005, 0x0006, 0xa87c, 0x0006, 0xa867, 0x0103, 0x20a9,
+ 0x001c, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x001d, 0x20a0, 0x9006,
+ 0x4004, 0x000e, 0x9084, 0x00ff, 0xa87e, 0x000e, 0xa87a, 0xa982,
+ 0x0005, 0x2071, 0x1910, 0x7004, 0x0002, 0x6f6d, 0x6f6e, 0x7059,
+ 0x6f6e, 0x6f6b, 0x7059, 0x080c, 0x0dd5, 0x0005, 0x2001, 0x1947,
+ 0x2004, 0x0002, 0x6f78, 0x6f78, 0x6ff2, 0x6ff3, 0x6f78, 0x6ff3,
+ 0x0126, 0x2091, 0x8000, 0x1e0c, 0x7079, 0x701c, 0x904d, 0x0508,
+ 0xa84c, 0x9005, 0x0904, 0x6fc3, 0x0e04, 0x6fa1, 0xa94c, 0x2071,
+ 0x0000, 0x7182, 0xa850, 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870,
+ 0x708a, 0xa850, 0x9082, 0x0019, 0x1278, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x2071, 0x1910, 0x080c,
+ 0x705a, 0x012e, 0x0804, 0x6ff1, 0xa850, 0x9082, 0x001c, 0x1e68,
+ 0xa880, 0x708e, 0x7036, 0x0146, 0x01d6, 0x0136, 0x01c6, 0x0156,
+ 0x20e9, 0x0000, 0x20a1, 0x002a, 0xa868, 0x20a8, 0xa860, 0x20e0,
+ 0xa85c, 0x9080, 0x0021, 0x2098, 0x4003, 0x015e, 0x01ce, 0x013e,
+ 0x01de, 0x014e, 0x0890, 0x2001, 0x005b, 0x2004, 0x9094, 0x0780,
+ 0x190c, 0x706e, 0xd09c, 0x2071, 0x1910, 0x1510, 0x2071, 0x1910,
+ 0x700f, 0x0001, 0xa964, 0x9184, 0x00ff, 0x9086, 0x0003, 0x1130,
+ 0x810f, 0x918c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x2900, 0x00d6,
+ 0x2069, 0x0050, 0x6822, 0x00de, 0x2071, 0x1910, 0x701c, 0x2048,
+ 0x7010, 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a,
+ 0x012e, 0x0005, 0x0005, 0x00d6, 0x2008, 0x2069, 0x19f9, 0x683c,
+ 0x9005, 0x0760, 0x0158, 0x9186, 0x0003, 0x0540, 0x2001, 0x1815,
+ 0x2004, 0x2009, 0x1aca, 0x210c, 0x9102, 0x1500, 0x0126, 0x2091,
+ 0x8000, 0x2069, 0x0050, 0x693c, 0x6838, 0x9106, 0x0190, 0x0e04,
+ 0x7025, 0x2069, 0x0000, 0x6837, 0x8040, 0x6833, 0x0012, 0x6883,
+ 0x8040, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c,
+ 0x119b, 0x2069, 0x19f9, 0x683f, 0xffff, 0x012e, 0x00de, 0x0126,
+ 0x2091, 0x8000, 0x1e0c, 0x70da, 0x701c, 0x904d, 0x0540, 0x2001,
+ 0x005b, 0x2004, 0x9094, 0x0780, 0x15c9, 0xd09c, 0x1500, 0x2071,
+ 0x1910, 0x700f, 0x0001, 0xa964, 0x9184, 0x00ff, 0x9086, 0x0003,
+ 0x1130, 0x810f, 0x918c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x2900,
+ 0x00d6, 0x2069, 0x0050, 0x6822, 0x00de, 0x701c, 0x2048, 0x7010,
+ 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a, 0x012e,
+ 0x0005, 0x0005, 0x0126, 0x2091, 0x8000, 0x701c, 0x904d, 0x0160,
+ 0x7010, 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a,
+ 0x012e, 0x080c, 0x1031, 0x0005, 0x012e, 0x0005, 0x2091, 0x8000,
+ 0x0e04, 0x7070, 0x0006, 0x0016, 0x2001, 0x8004, 0x0006, 0x0804,
+ 0x0dde, 0x0096, 0x00f6, 0x2079, 0x0050, 0x7044, 0xd084, 0x01c0,
+ 0xc084, 0x7046, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069,
+ 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x00fe, 0x009e, 0x0005,
+ 0x782c, 0x9094, 0x0780, 0x1991, 0xd0a4, 0x0db8, 0x00e6, 0x2071,
+ 0x1800, 0x7824, 0x00e6, 0x2071, 0x0040, 0x712c, 0xd19c, 0x1148,
+ 0x2009, 0x1830, 0x210c, 0x918a, 0x0020, 0x0218, 0x7022, 0x00ee,
+ 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0,
+ 0x8000, 0x70c2, 0x080c, 0x84c2, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x706e, 0xd0a4, 0x19f0, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6,
+ 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080,
+ 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x00ee, 0x00fe,
+ 0x009e, 0x0005, 0x00f6, 0x2079, 0x0050, 0x7044, 0xd084, 0x01b8,
+ 0xc084, 0x7046, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069,
+ 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x119b, 0x00fe, 0x0005, 0x782c,
+ 0x9094, 0x0780, 0x190c, 0x706e, 0xd0a4, 0x0db8, 0x00e6, 0x2071,
+ 0x1800, 0x7824, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0,
+ 0x8000, 0x70c2, 0x080c, 0x84c2, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x706e, 0xd0a4, 0x1d70, 0x00d6, 0x2069, 0x0050, 0x693c, 0x2069,
+ 0x1947, 0x6808, 0x690a, 0x2069, 0x19f9, 0x9102, 0x1118, 0x683c,
+ 0x9005, 0x1328, 0x2001, 0x1948, 0x200c, 0x810d, 0x693e, 0x00de,
+ 0x00ee, 0x00fe, 0x0005, 0x7098, 0x908a, 0x0029, 0x1a0c, 0x0dd5,
+ 0x9082, 0x001d, 0x001b, 0x6027, 0x1e00, 0x0005, 0x7202, 0x7188,
+ 0x71a4, 0x71ce, 0x71f1, 0x7231, 0x7243, 0x71a4, 0x7219, 0x7143,
+ 0x7171, 0x7142, 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0x9005,
+ 0x1180, 0x6808, 0x9005, 0x1518, 0x709b, 0x0028, 0x2069, 0x198e,
+ 0x2d04, 0x7002, 0x080c, 0x7576, 0x6028, 0x9085, 0x0600, 0x602a,
+ 0x00b0, 0x709b, 0x0028, 0x2069, 0x198e, 0x2d04, 0x7002, 0x6028,
+ 0x9085, 0x0600, 0x602a, 0x00e6, 0x0036, 0x0046, 0x0056, 0x2071,
+ 0x1a61, 0x080c, 0x1aec, 0x005e, 0x004e, 0x003e, 0x00ee, 0x00de,
+ 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0x9005, 0x1178, 0x6808,
+ 0x9005, 0x1160, 0x709b, 0x0028, 0x2069, 0x198e, 0x2d04, 0x7002,
+ 0x080c, 0x7611, 0x6028, 0x9085, 0x0600, 0x602a, 0x00de, 0x0005,
+ 0x0006, 0x2001, 0x0090, 0x080c, 0x2d4e, 0x000e, 0x6124, 0xd1e4,
+ 0x1190, 0x080c, 0x72b0, 0xd1d4, 0x1160, 0xd1dc, 0x1138, 0xd1cc,
+ 0x0150, 0x709b, 0x0020, 0x080c, 0x72b0, 0x0028, 0x709b, 0x001d,
+ 0x0010, 0x709b, 0x001f, 0x0005, 0x2001, 0x0088, 0x080c, 0x2d4e,
+ 0x6124, 0xd1cc, 0x11e8, 0xd1dc, 0x11c0, 0xd1e4, 0x1198, 0x9184,
+ 0x1e00, 0x11d8, 0x080c, 0x1b11, 0x60e3, 0x0001, 0x600c, 0xc0b4,
+ 0x600e, 0x080c, 0x746a, 0x2001, 0x0080, 0x080c, 0x2d4e, 0x709b,
+ 0x0028, 0x0058, 0x709b, 0x001e, 0x0040, 0x709b, 0x001d, 0x0028,
+ 0x709b, 0x0020, 0x0010, 0x709b, 0x001f, 0x0005, 0x080c, 0x1b11,
+ 0x60e3, 0x0001, 0x600c, 0xc0b4, 0x600e, 0x080c, 0x746a, 0x2001,
+ 0x0080, 0x080c, 0x2d4e, 0x6124, 0xd1d4, 0x1180, 0xd1dc, 0x1158,
+ 0xd1e4, 0x1130, 0x9184, 0x1e00, 0x1158, 0x709b, 0x0028, 0x0040,
+ 0x709b, 0x001e, 0x0028, 0x709b, 0x001d, 0x0010, 0x709b, 0x001f,
+ 0x0005, 0x2001, 0x00a0, 0x080c, 0x2d4e, 0x6124, 0xd1dc, 0x1138,
+ 0xd1e4, 0x0138, 0x080c, 0x1b11, 0x709b, 0x001e, 0x0010, 0x709b,
+ 0x001d, 0x0005, 0x080c, 0x7333, 0x6124, 0xd1dc, 0x1188, 0x080c,
+ 0x72b0, 0x0016, 0x080c, 0x1b11, 0x001e, 0xd1d4, 0x1128, 0xd1e4,
+ 0x0138, 0x709b, 0x001e, 0x0020, 0x709b, 0x001f, 0x080c, 0x72b0,
+ 0x0005, 0x0006, 0x2001, 0x00a0, 0x080c, 0x2d4e, 0x000e, 0x6124,
+ 0xd1d4, 0x1160, 0xd1cc, 0x1150, 0xd1dc, 0x1128, 0xd1e4, 0x0140,
+ 0x709b, 0x001e, 0x0028, 0x709b, 0x001d, 0x0010, 0x709b, 0x0021,
+ 0x0005, 0x080c, 0x7333, 0x6124, 0xd1d4, 0x1150, 0xd1dc, 0x1128,
+ 0xd1e4, 0x0140, 0x709b, 0x001e, 0x0028, 0x709b, 0x001d, 0x0010,
+ 0x709b, 0x001f, 0x0005, 0x0006, 0x2001, 0x0090, 0x080c, 0x2d4e,
+ 0x000e, 0x6124, 0xd1d4, 0x1178, 0xd1cc, 0x1150, 0xd1dc, 0x1128,
+ 0xd1e4, 0x0158, 0x709b, 0x001e, 0x0040, 0x709b, 0x001d, 0x0028,
+ 0x709b, 0x0020, 0x0010, 0x709b, 0x001f, 0x0005, 0x0016, 0x00c6,
+ 0x00d6, 0x00e6, 0x0126, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071,
+ 0x1800, 0x2091, 0x8000, 0x080c, 0x743e, 0x11d8, 0x2001, 0x180c,
+ 0x200c, 0xd1b4, 0x01b0, 0xc1b4, 0x2102, 0x6027, 0x0200, 0x080c,
+ 0x2c76, 0x6024, 0xd0cc, 0x0148, 0x2001, 0x00a0, 0x080c, 0x2d4e,
+ 0x080c, 0x7724, 0x080c, 0x60ad, 0x0428, 0x6028, 0xc0cd, 0x602a,
+ 0x0408, 0x080c, 0x7458, 0x0150, 0x080c, 0x744f, 0x1138, 0x2001,
+ 0x0001, 0x080c, 0x2828, 0x080c, 0x7416, 0x00a0, 0x080c, 0x7330,
+ 0x0178, 0x2001, 0x0001, 0x080c, 0x2828, 0x7098, 0x9086, 0x001e,
+ 0x0120, 0x7098, 0x9086, 0x0022, 0x1118, 0x709b, 0x0025, 0x0010,
+ 0x709b, 0x0021, 0x012e, 0x00ee, 0x00de, 0x00ce, 0x001e, 0x0005,
+ 0x0026, 0x2011, 0x72c1, 0x080c, 0x868a, 0x002e, 0x0016, 0x0026,
+ 0x2009, 0x0064, 0x2011, 0x72c1, 0x080c, 0x8681, 0x002e, 0x001e,
+ 0x0005, 0x00e6, 0x00f6, 0x0016, 0x080c, 0xa356, 0x2071, 0x1800,
+ 0x080c, 0x725e, 0x001e, 0x00fe, 0x00ee, 0x0005, 0x0016, 0x0026,
+ 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x0126, 0x080c, 0xa356,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x2091, 0x8000,
+ 0x6028, 0xc09c, 0x602a, 0x2011, 0x0003, 0x080c, 0xa722, 0x2011,
+ 0x0002, 0x080c, 0xa72c, 0x080c, 0xa636, 0x080c, 0x8636, 0x0036,
+ 0x901e, 0x080c, 0xa6ac, 0x003e, 0x60e3, 0x0000, 0x080c, 0xeb79,
+ 0x080c, 0xeb94, 0x2009, 0x0004, 0x080c, 0x2c7c, 0x080c, 0x2b97,
+ 0x2001, 0x1800, 0x2003, 0x0004, 0x6027, 0x0008, 0x2011, 0x72c1,
+ 0x080c, 0x868a, 0x080c, 0x7458, 0x0118, 0x9006, 0x080c, 0x2d4e,
+ 0x080c, 0x0bae, 0x2001, 0x0001, 0x080c, 0x2828, 0x012e, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e, 0x0005, 0x0026,
+ 0x00e6, 0x2011, 0x72ce, 0x2071, 0x19f9, 0x701c, 0x9206, 0x1118,
+ 0x7018, 0x9005, 0x0110, 0x9085, 0x0001, 0x00ee, 0x002e, 0x0005,
+ 0x6020, 0xd09c, 0x0005, 0x6800, 0x9084, 0xfffe, 0x9086, 0x00c0,
+ 0x0170, 0x2001, 0x00c0, 0x080c, 0x2d4e, 0x0156, 0x20a9, 0x002d,
+ 0x1d04, 0x7340, 0x2091, 0x6000, 0x1f04, 0x7340, 0x015e, 0x0005,
+ 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071,
+ 0x1800, 0x080c, 0x7733, 0x2001, 0x196c, 0x2003, 0x0000, 0x9006,
+ 0x709a, 0x60e2, 0x6886, 0x080c, 0x28f0, 0x9006, 0x080c, 0x2d4e,
+ 0x080c, 0x5f6c, 0x6027, 0xffff, 0x602b, 0x182f, 0x00ee, 0x00de,
+ 0x00ce, 0x0005, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x2071, 0x1800, 0x2001, 0x197c, 0x200c, 0x9186, 0x0000,
+ 0x0158, 0x9186, 0x0001, 0x0158, 0x9186, 0x0002, 0x0158, 0x9186,
+ 0x0003, 0x0158, 0x0804, 0x7406, 0x709b, 0x0022, 0x0040, 0x709b,
+ 0x0021, 0x0028, 0x709b, 0x0023, 0x0010, 0x709b, 0x0024, 0x60e3,
+ 0x0000, 0x6887, 0x0001, 0x2001, 0x0001, 0x080c, 0x28f0, 0x0026,
+ 0x080c, 0xaeb4, 0x002e, 0x7000, 0x908e, 0x0004, 0x0118, 0x602b,
+ 0x0028, 0x0010, 0x602b, 0x0020, 0x0156, 0x0126, 0x2091, 0x8000,
+ 0x20a9, 0x0005, 0x6024, 0xd0ac, 0x0150, 0x012e, 0x015e, 0x080c,
+ 0xd388, 0x0118, 0x9006, 0x080c, 0x2d78, 0x0804, 0x7412, 0x6800,
+ 0x9084, 0x00a1, 0xc0bd, 0x6802, 0x080c, 0x2c76, 0x6904, 0xd1d4,
+ 0x1140, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x1f04, 0x73aa, 0x080c,
+ 0x7495, 0x012e, 0x015e, 0x080c, 0x744f, 0x01d8, 0x6044, 0x9005,
+ 0x0198, 0x2011, 0x0114, 0x2204, 0x9085, 0x0100, 0x2012, 0x6050,
+ 0x0006, 0x9085, 0x0020, 0x6052, 0x080c, 0x7495, 0x9006, 0x8001,
+ 0x1df0, 0x000e, 0x6052, 0x0028, 0x6804, 0xd0d4, 0x1110, 0x080c,
+ 0x7495, 0x080c, 0xd388, 0x0118, 0x9006, 0x080c, 0x2d78, 0x0016,
+ 0x0026, 0x7000, 0x908e, 0x0004, 0x0130, 0x2009, 0x00c8, 0x2011,
+ 0x72ce, 0x080c, 0x8648, 0x002e, 0x001e, 0x080c, 0x84b9, 0x7034,
+ 0xc085, 0x7036, 0x2001, 0x197c, 0x2003, 0x0004, 0x080c, 0x712b,
+ 0x080c, 0x744f, 0x0138, 0x6804, 0xd0d4, 0x1120, 0xd0dc, 0x1100,
+ 0x080c, 0x7729, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x080c,
+ 0x84d0, 0x080c, 0x84c2, 0x080c, 0x7733, 0x2001, 0x196c, 0x2003,
+ 0x0000, 0x9006, 0x709a, 0x60e2, 0x6886, 0x080c, 0x28f0, 0x9006,
+ 0x080c, 0x2d4e, 0x6043, 0x0090, 0x6043, 0x0010, 0x6027, 0xffff,
+ 0x602b, 0x182f, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0006, 0x2001,
+ 0x197b, 0x2004, 0x9086, 0xaaaa, 0x000e, 0x0005, 0x0006, 0x080c,
+ 0x5761, 0x9084, 0x0030, 0x9086, 0x0000, 0x000e, 0x0005, 0x0006,
+ 0x080c, 0x5761, 0x9084, 0x0030, 0x9086, 0x0030, 0x000e, 0x0005,
+ 0x0006, 0x080c, 0x5761, 0x9084, 0x0030, 0x9086, 0x0010, 0x000e,
+ 0x0005, 0x0006, 0x080c, 0x5761, 0x9084, 0x0030, 0x9086, 0x0020,
+ 0x000e, 0x0005, 0x0036, 0x0016, 0x2001, 0x180c, 0x2004, 0x908c,
+ 0x0013, 0x0180, 0x0020, 0x080c, 0x2910, 0x900e, 0x0028, 0x080c,
+ 0x6a04, 0x1dc8, 0x2009, 0x0002, 0x2019, 0x0028, 0x080c, 0x31e9,
+ 0x9006, 0x0019, 0x001e, 0x003e, 0x0005, 0x00e6, 0x2071, 0x180c,
+ 0x2e04, 0x0130, 0x080c, 0xd381, 0x1128, 0x9085, 0x0010, 0x0010,
+ 0x9084, 0xffef, 0x2072, 0x00ee, 0x0005, 0x6050, 0x0006, 0x60ec,
+ 0x0006, 0x600c, 0x0006, 0x6004, 0x0006, 0x6028, 0x0006, 0x0016,
+ 0x6138, 0x6050, 0x9084, 0xfbff, 0x9085, 0x2000, 0x6052, 0x613a,
+ 0x20a9, 0x0012, 0x1d04, 0x74aa, 0x2091, 0x6000, 0x1f04, 0x74aa,
+ 0x602f, 0x0100, 0x602f, 0x0000, 0x6050, 0x9085, 0x0400, 0x9084,
+ 0xdfff, 0x6052, 0x613a, 0x001e, 0x602f, 0x0040, 0x602f, 0x0000,
+ 0x000e, 0x602a, 0x000e, 0x6006, 0x000e, 0x600e, 0x000e, 0x60ee,
+ 0x60e3, 0x0000, 0x6887, 0x0001, 0x2001, 0x0001, 0x080c, 0x28f0,
+ 0x2001, 0x00a0, 0x0006, 0x080c, 0xd388, 0x000e, 0x0130, 0x080c,
+ 0x2d6c, 0x9006, 0x080c, 0x2d78, 0x0010, 0x080c, 0x2d4e, 0x000e,
+ 0x6052, 0x6050, 0x0006, 0xc0e5, 0x6052, 0x00f6, 0x2079, 0x0100,
+ 0x080c, 0x2beb, 0x00fe, 0x000e, 0x6052, 0x0005, 0x0156, 0x0016,
+ 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x2071, 0x1800, 0x6020, 0x9084, 0x0080, 0x0138, 0x2001,
+ 0x180c, 0x200c, 0xc1c5, 0x2102, 0x0804, 0x7568, 0x2001, 0x180c,
+ 0x200c, 0xc1c4, 0x2102, 0x6028, 0x9084, 0xe1ff, 0x602a, 0x6027,
+ 0x0200, 0x2001, 0x0090, 0x080c, 0x2d4e, 0x20a9, 0x0366, 0x6024,
+ 0xd0cc, 0x1518, 0x1d04, 0x7517, 0x2091, 0x6000, 0x1f04, 0x7517,
+ 0x2011, 0x0003, 0x080c, 0xa722, 0x2011, 0x0002, 0x080c, 0xa72c,
+ 0x080c, 0xa636, 0x901e, 0x080c, 0xa6ac, 0x2001, 0x00a0, 0x080c,
+ 0x2d4e, 0x080c, 0x7724, 0x080c, 0x60ad, 0x080c, 0xd388, 0x0110,
+ 0x080c, 0x0d45, 0x9085, 0x0001, 0x0488, 0x080c, 0x1b11, 0x60e3,
+ 0x0000, 0x2001, 0x196c, 0x2004, 0x080c, 0x28f0, 0x60e2, 0x2001,
+ 0x0080, 0x080c, 0x2d4e, 0x20a9, 0x0366, 0x6027, 0x1e00, 0x2009,
+ 0x1e00, 0x080c, 0x2c76, 0x6024, 0x910c, 0x0138, 0x1d04, 0x754d,
+ 0x2091, 0x6000, 0x1f04, 0x754d, 0x0818, 0x6028, 0x9085, 0x1e00,
+ 0x602a, 0x70b4, 0x9005, 0x1118, 0x6887, 0x0001, 0x0008, 0x6886,
+ 0x080c, 0xd388, 0x0110, 0x080c, 0x0d45, 0x9006, 0x00ee, 0x00de,
+ 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016,
+ 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2071,
+ 0x1800, 0x7000, 0x9086, 0x0003, 0x1168, 0x2001, 0x020b, 0x2004,
+ 0x9084, 0x5540, 0x9086, 0x5540, 0x1128, 0x2069, 0x1a78, 0x2d04,
+ 0x8000, 0x206a, 0x2069, 0x0140, 0x6020, 0x9084, 0x00c0, 0x0120,
+ 0x6884, 0x9005, 0x1904, 0x75db, 0x2001, 0x0088, 0x080c, 0x2d4e,
+ 0x9006, 0x60e2, 0x6886, 0x080c, 0x28f0, 0x2069, 0x0200, 0x6804,
+ 0x9005, 0x1118, 0x6808, 0x9005, 0x01c0, 0x6028, 0x9084, 0xfbff,
+ 0x602a, 0x6027, 0x0400, 0x2069, 0x198e, 0x7000, 0x206a, 0x709b,
+ 0x0026, 0x7003, 0x0001, 0x20a9, 0x0002, 0x1d04, 0x75bd, 0x2091,
+ 0x6000, 0x1f04, 0x75bd, 0x0804, 0x7609, 0x2069, 0x0140, 0x20a9,
+ 0x0384, 0x6027, 0x1e00, 0x2009, 0x1e00, 0x080c, 0x2c76, 0x6024,
+ 0x910c, 0x0508, 0x9084, 0x1a00, 0x11f0, 0x1d04, 0x75c9, 0x2091,
+ 0x6000, 0x1f04, 0x75c9, 0x2011, 0x0003, 0x080c, 0xa722, 0x2011,
+ 0x0002, 0x080c, 0xa72c, 0x080c, 0xa636, 0x901e, 0x080c, 0xa6ac,
+ 0x2001, 0x00a0, 0x080c, 0x2d4e, 0x080c, 0x7724, 0x080c, 0x60ad,
+ 0x9085, 0x0001, 0x00b0, 0x2001, 0x0080, 0x080c, 0x2d4e, 0x2069,
+ 0x0140, 0x60e3, 0x0000, 0x70b4, 0x9005, 0x1118, 0x6887, 0x0001,
+ 0x0008, 0x6886, 0x2001, 0x196c, 0x2004, 0x080c, 0x28f0, 0x60e2,
+ 0x9006, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e,
+ 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2071, 0x1800, 0x6020, 0x9084, 0x00c0, 0x01c8,
+ 0x2011, 0x0003, 0x080c, 0xa722, 0x2011, 0x0002, 0x080c, 0xa72c,
+ 0x080c, 0xa636, 0x901e, 0x080c, 0xa6ac, 0x2069, 0x0140, 0x2001,
+ 0x00a0, 0x080c, 0x2d4e, 0x080c, 0x7724, 0x080c, 0x60ad, 0x0804,
+ 0x76a4, 0x2001, 0x180c, 0x200c, 0xd1b4, 0x1160, 0xc1b5, 0x2102,
+ 0x080c, 0x72b6, 0x2069, 0x0140, 0x2001, 0x0080, 0x080c, 0x2d4e,
+ 0x60e3, 0x0000, 0x2069, 0x0200, 0x6804, 0x9005, 0x1118, 0x6808,
+ 0x9005, 0x0180, 0x6028, 0x9084, 0xfdff, 0x602a, 0x6027, 0x0200,
+ 0x2069, 0x198e, 0x7000, 0x206a, 0x709b, 0x0027, 0x7003, 0x0001,
+ 0x0804, 0x76a4, 0x6027, 0x1e00, 0x2009, 0x1e00, 0x080c, 0x2c76,
+ 0x6024, 0x910c, 0x01c8, 0x9084, 0x1c00, 0x11b0, 0x1d04, 0x7662,
+ 0x0006, 0x0016, 0x00c6, 0x00d6, 0x00e6, 0x080c, 0x8510, 0x00ee,
+ 0x00de, 0x00ce, 0x001e, 0x000e, 0x00e6, 0x2071, 0x19f9, 0x7070,
+ 0x00ee, 0x9005, 0x19f8, 0x0400, 0x0026, 0x2011, 0x72ce, 0x080c,
+ 0x85b0, 0x2011, 0x72c1, 0x080c, 0x868a, 0x002e, 0x2069, 0x0140,
+ 0x60e3, 0x0000, 0x70b4, 0x9005, 0x1118, 0x6887, 0x0001, 0x0008,
+ 0x6886, 0x2001, 0x196c, 0x2004, 0x080c, 0x28f0, 0x60e2, 0x2001,
+ 0x180c, 0x200c, 0xc1b4, 0x2102, 0x00ee, 0x00de, 0x00ce, 0x003e,
+ 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036,
+ 0x0046, 0x00c6, 0x00e6, 0x2061, 0x0100, 0x2071, 0x1800, 0x080c,
+ 0xd381, 0x1904, 0x7712, 0x7130, 0xd184, 0x1170, 0x080c, 0x3378,
+ 0x0138, 0xc18d, 0x7132, 0x2011, 0x1848, 0x2214, 0xd2ac, 0x1120,
+ 0x7030, 0xd08c, 0x0904, 0x7712, 0x2011, 0x1848, 0x220c, 0xd1a4,
+ 0x0538, 0x0016, 0x2019, 0x000e, 0x080c, 0xe6ae, 0x0156, 0x00b6,
+ 0x20a9, 0x007f, 0x900e, 0x9186, 0x007e, 0x01a0, 0x9186, 0x0080,
+ 0x0188, 0x080c, 0x6699, 0x1170, 0x2120, 0x9006, 0x0016, 0x2009,
+ 0x000e, 0x080c, 0xe73a, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c,
+ 0x879a, 0x001e, 0x8108, 0x1f04, 0x76db, 0x00be, 0x015e, 0x001e,
+ 0xd1ac, 0x1148, 0x0016, 0x2009, 0x0002, 0x2019, 0x0004, 0x080c,
+ 0x31e9, 0x001e, 0x0078, 0x0156, 0x00b6, 0x20a9, 0x007f, 0x900e,
+ 0x080c, 0x6699, 0x1110, 0x080c, 0x60c7, 0x8108, 0x1f04, 0x7708,
+ 0x00be, 0x015e, 0x080c, 0x1b11, 0x080c, 0xaeb4, 0x60e3, 0x0000,
+ 0x080c, 0x60ad, 0x080c, 0x736a, 0x00ee, 0x00ce, 0x004e, 0x003e,
+ 0x002e, 0x001e, 0x015e, 0x0005, 0x2001, 0x197c, 0x2003, 0x0001,
+ 0x0005, 0x2001, 0x197c, 0x2003, 0x0000, 0x0005, 0x2001, 0x197b,
+ 0x2003, 0xaaaa, 0x0005, 0x2001, 0x197b, 0x2003, 0x0000, 0x0005,
+ 0x2071, 0x18fa, 0x7003, 0x0000, 0x7007, 0x0000, 0x080c, 0x1018,
+ 0x090c, 0x0dd5, 0xa8ab, 0xdcb0, 0x2900, 0x704e, 0x080c, 0x1018,
+ 0x090c, 0x0dd5, 0xa8ab, 0xdcb0, 0x2900, 0x7052, 0xa867, 0x0000,
+ 0xa86b, 0x0001, 0xa89f, 0x0000, 0x0005, 0x00e6, 0x2071, 0x0040,
+ 0x6848, 0x9005, 0x1118, 0x9085, 0x0001, 0x04b0, 0x6840, 0x9005,
+ 0x0150, 0x04a1, 0x6a50, 0x9200, 0x7002, 0x6854, 0x9101, 0x7006,
+ 0x9006, 0x7012, 0x7016, 0x6850, 0x7002, 0x6854, 0x7006, 0x6858,
+ 0x700a, 0x685c, 0x700e, 0x6840, 0x9005, 0x1110, 0x7012, 0x7016,
+ 0x6848, 0x701a, 0x701c, 0x9085, 0x0040, 0x701e, 0x2001, 0x0019,
+ 0x7036, 0x702b, 0x0001, 0x2001, 0x0004, 0x200c, 0x918c, 0xfff7,
+ 0x918d, 0x8000, 0x2102, 0x00d6, 0x2069, 0x18fa, 0x6807, 0x0001,
+ 0x00de, 0x080c, 0x7d11, 0x9006, 0x00ee, 0x0005, 0x900e, 0x0156,
+ 0x20a9, 0x0006, 0x8003, 0x2011, 0x0100, 0x2214, 0x9296, 0x0008,
+ 0x1110, 0x818d, 0x0010, 0x81f5, 0x3e08, 0x1f04, 0x779a, 0x015e,
+ 0x0005, 0x2079, 0x0040, 0x2071, 0x18fa, 0x7004, 0x0002, 0x77b9,
+ 0x77ba, 0x77f2, 0x784d, 0x795d, 0x77b7, 0x77b7, 0x7987, 0x080c,
+ 0x0dd5, 0x0005, 0x2079, 0x0040, 0x782c, 0x908c, 0x0780, 0x190c,
+ 0x7df3, 0xd0a4, 0x01f8, 0x7824, 0x2048, 0x9006, 0xa802, 0xa806,
+ 0xa864, 0x9084, 0x00ff, 0x908a, 0x0040, 0x0610, 0x00c0, 0x2001,
+ 0x1800, 0x200c, 0x9186, 0x0003, 0x1168, 0x7004, 0x0002, 0x77e2,
+ 0x77bc, 0x77e2, 0x77e0, 0x77e2, 0x77e2, 0x77e2, 0x77e2, 0x77e2,
+ 0x080c, 0x784d, 0x782c, 0xd09c, 0x090c, 0x7d11, 0x0005, 0x9082,
+ 0x005a, 0x1218, 0x2100, 0x003b, 0x0c10, 0x080c, 0x7883, 0x0c90,
+ 0x00e3, 0x08e8, 0x0005, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883,
+ 0x7883, 0x7883, 0x7883, 0x78a5, 0x7883, 0x7883, 0x7883, 0x7883,
+ 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883,
+ 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x788f,
+ 0x7883, 0x7a78, 0x7883, 0x7883, 0x7883, 0x78a5, 0x7883, 0x788f,
+ 0x7ab9, 0x7afa, 0x7b41, 0x7b55, 0x7883, 0x7883, 0x78a5, 0x788f,
+ 0x78b9, 0x7883, 0x7931, 0x7c00, 0x7c1b, 0x7883, 0x78a5, 0x7883,
+ 0x78b9, 0x7883, 0x7883, 0x7927, 0x7c1b, 0x7883, 0x7883, 0x7883,
+ 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x78cd, 0x7883,
+ 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x7883,
+ 0x7d97, 0x7883, 0x7d41, 0x7883, 0x7d41, 0x7883, 0x78e2, 0x7883,
+ 0x7883, 0x7883, 0x7883, 0x7883, 0x7883, 0x2079, 0x0040, 0x7004,
+ 0x9086, 0x0003, 0x1198, 0x782c, 0x080c, 0x7d3a, 0xd0a4, 0x0170,
+ 0x7824, 0x2048, 0x9006, 0xa802, 0xa806, 0xa864, 0x9084, 0x00ff,
+ 0x908a, 0x001a, 0x1210, 0x002b, 0x0c50, 0x00e9, 0x080c, 0x7d11,
+ 0x0005, 0x7883, 0x788f, 0x7a64, 0x7883, 0x788f, 0x7883, 0x788f,
+ 0x788f, 0x7883, 0x788f, 0x7a64, 0x788f, 0x788f, 0x788f, 0x788f,
+ 0x788f, 0x7883, 0x788f, 0x7a64, 0x7883, 0x7883, 0x788f, 0x7883,
+ 0x7883, 0x7883, 0x788f, 0x00e6, 0x2071, 0x18fa, 0x2009, 0x0400,
+ 0x0071, 0x00ee, 0x0005, 0x2009, 0x1000, 0x0049, 0x0005, 0x2009,
+ 0x2000, 0x0029, 0x0005, 0x2009, 0x0800, 0x0009, 0x0005, 0x7007,
+ 0x0001, 0xa868, 0x9084, 0x00ff, 0x9105, 0xa86a, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6d17, 0x012e, 0x0005, 0xa864, 0x8007, 0x9084,
+ 0x00ff, 0x0d08, 0x8001, 0x1120, 0x7007, 0x0001, 0x0804, 0x7a06,
+ 0x7007, 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b, 0x7a06,
+ 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff, 0x0968, 0x8001, 0x1120,
+ 0x7007, 0x0001, 0x0804, 0x7a21, 0x7007, 0x0003, 0x7012, 0x2900,
+ 0x7016, 0x701a, 0x704b, 0x7a21, 0x0005, 0xa864, 0x8007, 0x9084,
+ 0x00ff, 0x0904, 0x788b, 0x8001, 0x1120, 0x7007, 0x0001, 0x0804,
+ 0x7a3d, 0x7007, 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b,
+ 0x7a3d, 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff, 0x9086, 0x0001,
+ 0x1904, 0x788b, 0x7007, 0x0001, 0x2009, 0x1834, 0x210c, 0x81ff,
+ 0x11a8, 0xa868, 0x9084, 0x00ff, 0xa86a, 0xa883, 0x0000, 0x080c,
+ 0x6344, 0x1108, 0x0005, 0x0126, 0x2091, 0x8000, 0xa867, 0x0139,
+ 0xa87a, 0xa982, 0x080c, 0x6d17, 0x012e, 0x0ca0, 0xa994, 0x9186,
+ 0x0071, 0x0d38, 0x9186, 0x0064, 0x0d20, 0x9186, 0x007c, 0x0d08,
+ 0x9186, 0x0028, 0x09f0, 0x9186, 0x0038, 0x09d8, 0x9186, 0x0078,
+ 0x09c0, 0x9186, 0x005f, 0x09a8, 0x9186, 0x0056, 0x0990, 0xa897,
+ 0x4005, 0xa89b, 0x0001, 0x2001, 0x0030, 0x900e, 0x08a0, 0xa87c,
+ 0x9084, 0x00c0, 0x9086, 0x00c0, 0x1120, 0x7007, 0x0001, 0x0804,
+ 0x7c32, 0x2900, 0x7016, 0x701a, 0x20a9, 0x0004, 0xa860, 0x20e0,
+ 0xa85c, 0x9080, 0x0030, 0x2098, 0x7050, 0x2040, 0xa060, 0x20e8,
+ 0xa05c, 0x9080, 0x0023, 0x20a0, 0x4003, 0xa888, 0x7012, 0x9082,
+ 0x0401, 0x1a04, 0x7893, 0xaab4, 0x928a, 0x0002, 0x1a04, 0x7893,
+ 0x82ff, 0x1138, 0xa8b8, 0xa9bc, 0x9105, 0x0118, 0x2001, 0x79c4,
+ 0x0018, 0x9280, 0x79ba, 0x2005, 0x7056, 0x7010, 0x9015, 0x0904,
+ 0x79a5, 0x080c, 0x1018, 0x1118, 0x7007, 0x0004, 0x0005, 0x2900,
+ 0x7022, 0x7054, 0x2060, 0xe000, 0xa866, 0x7050, 0x2040, 0xa95c,
+ 0xe004, 0x9100, 0xa076, 0xa860, 0xa072, 0xe008, 0x920a, 0x1210,
+ 0x900e, 0x2200, 0x7112, 0xe20c, 0x8003, 0x800b, 0x9296, 0x0004,
+ 0x0108, 0x9108, 0xa17a, 0x810b, 0xa17e, 0x080c, 0x10e9, 0xa06c,
+ 0x908e, 0x0100, 0x0170, 0x9086, 0x0200, 0x0118, 0x7007, 0x0007,
+ 0x0005, 0x7020, 0x2048, 0x080c, 0x1031, 0x7014, 0x2048, 0x0804,
+ 0x7893, 0x7020, 0x2048, 0x7018, 0xa802, 0xa807, 0x0000, 0x2908,
+ 0x2048, 0xa906, 0x711a, 0x0804, 0x795d, 0x7014, 0x2048, 0x7007,
+ 0x0001, 0xa8b4, 0x9005, 0x1128, 0xa8b8, 0xa9bc, 0x9105, 0x0108,
+ 0x00b9, 0xa864, 0x9084, 0x00ff, 0x9086, 0x001e, 0x0904, 0x7c32,
+ 0x0804, 0x7a06, 0x79bc, 0x79c0, 0x0002, 0x001d, 0x0007, 0x0004,
+ 0x000a, 0x001b, 0x0005, 0x0006, 0x000a, 0x001d, 0x0005, 0x0004,
+ 0x0076, 0x0066, 0xafb8, 0xaebc, 0xa804, 0x2050, 0xb0c0, 0xb0e2,
+ 0xb0bc, 0xb0de, 0xb0b8, 0xb0d2, 0xb0b4, 0xb0ce, 0xb6da, 0xb7d6,
+ 0xb0b0, 0xb0ca, 0xb0ac, 0xb0c6, 0xb0a8, 0xb0ba, 0xb0a4, 0xb0b6,
+ 0xb6c2, 0xb7be, 0xb0a0, 0xb0b2, 0xb09c, 0xb0ae, 0xb098, 0xb0a2,
+ 0xb094, 0xb09e, 0xb6aa, 0xb7a6, 0xb090, 0xb09a, 0xb08c, 0xb096,
+ 0xb088, 0xb08a, 0xb084, 0xb086, 0xb692, 0xb78e, 0xb080, 0xb082,
+ 0xb07c, 0xb07e, 0xb078, 0xb072, 0xb074, 0xb06e, 0xb67a, 0xb776,
+ 0xb004, 0x9055, 0x1958, 0x006e, 0x007e, 0x0005, 0x2009, 0x1834,
+ 0x210c, 0x81ff, 0x1178, 0x080c, 0x6141, 0x1108, 0x0005, 0x080c,
+ 0x6f4a, 0x0126, 0x2091, 0x8000, 0x080c, 0xcf7c, 0x080c, 0x6d17,
+ 0x012e, 0x0ca0, 0x080c, 0xd381, 0x1d70, 0x2001, 0x0028, 0x900e,
+ 0x0c70, 0x2009, 0x1834, 0x210c, 0x81ff, 0x1188, 0xa888, 0x9005,
+ 0x0188, 0xa883, 0x0000, 0x080c, 0x61d1, 0x1108, 0x0005, 0xa87a,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17, 0x012e, 0x0cb8, 0x2001,
+ 0x0028, 0x0ca8, 0x2001, 0x0000, 0x0c90, 0x2009, 0x1834, 0x210c,
+ 0x81ff, 0x11d8, 0xa888, 0x9005, 0x01e0, 0xa883, 0x0000, 0xa87c,
+ 0xd0f4, 0x0120, 0x080c, 0x62a6, 0x1138, 0x0005, 0x9006, 0xa87a,
+ 0x080c, 0x621e, 0x1108, 0x0005, 0x0126, 0x2091, 0x8000, 0xa87a,
+ 0xa982, 0x080c, 0x6d17, 0x012e, 0x0cb0, 0x2001, 0x0028, 0x900e,
+ 0x0c98, 0x2001, 0x0000, 0x0c80, 0x7018, 0xa802, 0x2908, 0x2048,
+ 0xa906, 0x711a, 0x7010, 0x8001, 0x7012, 0x0118, 0x7007, 0x0003,
+ 0x0030, 0x7014, 0x2048, 0x7007, 0x0001, 0x7048, 0x080f, 0x0005,
+ 0x00b6, 0x7007, 0x0001, 0xa974, 0xa878, 0x9084, 0x00ff, 0x9096,
+ 0x0004, 0x0540, 0x20a9, 0x0001, 0x9096, 0x0001, 0x0190, 0x900e,
+ 0x20a9, 0x0800, 0x9096, 0x0002, 0x0160, 0x9005, 0x11d8, 0xa974,
+ 0x080c, 0x6699, 0x11b8, 0x0066, 0xae80, 0x080c, 0x67a9, 0x006e,
+ 0x0088, 0x0046, 0x2011, 0x180c, 0x2224, 0xc484, 0x2412, 0x004e,
+ 0x00c6, 0x080c, 0x6699, 0x1110, 0x080c, 0x68a9, 0x8108, 0x1f04,
+ 0x7aa1, 0x00ce, 0xa87c, 0xd084, 0x1120, 0x080c, 0x1031, 0x00be,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17, 0x012e, 0x00be,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x080c, 0x6a08,
+ 0x0580, 0x2061, 0x1a70, 0x6100, 0xd184, 0x0178, 0xa888, 0x9084,
+ 0x00ff, 0x1550, 0x6000, 0xd084, 0x0520, 0x6004, 0x9005, 0x1538,
+ 0x6003, 0x0000, 0x600b, 0x0000, 0x00c8, 0x2011, 0x0001, 0xa890,
+ 0x9005, 0x1110, 0x2001, 0x001e, 0x8000, 0x6016, 0xa888, 0x9084,
+ 0x00ff, 0x0178, 0x6006, 0xa888, 0x8007, 0x9084, 0x00ff, 0x0148,
+ 0x600a, 0xa888, 0x8000, 0x1108, 0xc28d, 0x6202, 0x012e, 0x0804,
+ 0x7cfb, 0x012e, 0x0804, 0x7cf5, 0x012e, 0x0804, 0x7cef, 0x012e,
+ 0x0804, 0x7cf2, 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x080c,
+ 0x6a08, 0x05e0, 0x2061, 0x1a70, 0x6000, 0xd084, 0x05b8, 0x6204,
+ 0x6308, 0xd08c, 0x1530, 0xac78, 0x9484, 0x0003, 0x0170, 0xa988,
+ 0x918c, 0x00ff, 0x8001, 0x1120, 0x2100, 0x9210, 0x0620, 0x0028,
+ 0x8001, 0x1508, 0x2100, 0x9212, 0x02f0, 0x9484, 0x000c, 0x0188,
+ 0xa988, 0x810f, 0x918c, 0x00ff, 0x9082, 0x0004, 0x1120, 0x2100,
+ 0x9318, 0x0288, 0x0030, 0x9082, 0x0004, 0x1168, 0x2100, 0x931a,
+ 0x0250, 0xa890, 0x9005, 0x0110, 0x8000, 0x6016, 0x6206, 0x630a,
+ 0x012e, 0x0804, 0x7cfb, 0x012e, 0x0804, 0x7cf8, 0x012e, 0x0804,
+ 0x7cf5, 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x2061, 0x1a70,
+ 0x6300, 0xd38c, 0x1120, 0x6308, 0x8318, 0x0220, 0x630a, 0x012e,
+ 0x0804, 0x7d09, 0x012e, 0x0804, 0x7cf8, 0x00b6, 0x0126, 0x00c6,
+ 0x2091, 0x8000, 0x7007, 0x0001, 0xa87c, 0xd0ac, 0x0148, 0x00c6,
+ 0x2061, 0x1a70, 0x6000, 0x9084, 0xfcff, 0x6002, 0x00ce, 0x0440,
+ 0xa888, 0x9005, 0x05d8, 0xa88c, 0x9065, 0x0598, 0x2001, 0x1834,
+ 0x2004, 0x9005, 0x0118, 0x080c, 0xaf74, 0x0068, 0x6017, 0xf400,
+ 0x605b, 0x0000, 0xa97c, 0xd1a4, 0x0110, 0xa980, 0x615a, 0x2009,
+ 0x0041, 0x080c, 0xafbe, 0xa988, 0x918c, 0xff00, 0x9186, 0x2000,
+ 0x1138, 0x0026, 0x900e, 0x2011, 0xfdff, 0x080c, 0x879a, 0x002e,
+ 0xa87c, 0xd0c4, 0x0148, 0x2061, 0x1a70, 0x6000, 0xd08c, 0x1120,
+ 0x6008, 0x8000, 0x0208, 0x600a, 0x00ce, 0x012e, 0x00be, 0x0804,
+ 0x7cfb, 0x00ce, 0x012e, 0x00be, 0x0804, 0x7cf5, 0xa984, 0x9186,
+ 0x002e, 0x0d30, 0x9186, 0x002d, 0x0d18, 0x9186, 0x0045, 0x0510,
+ 0x9186, 0x002a, 0x1130, 0x2001, 0x180c, 0x200c, 0xc194, 0x2102,
+ 0x08b8, 0x9186, 0x0020, 0x0158, 0x9186, 0x0029, 0x1d10, 0xa974,
+ 0x080c, 0x6699, 0x1968, 0xb800, 0xc0e4, 0xb802, 0x0848, 0xa88c,
+ 0x9065, 0x09b8, 0x6007, 0x0024, 0x2001, 0x1985, 0x2004, 0x601a,
+ 0x0804, 0x7b90, 0xa88c, 0x9065, 0x0960, 0x00e6, 0xa890, 0x9075,
+ 0x2001, 0x1834, 0x2004, 0x9005, 0x0150, 0x080c, 0xaf74, 0x8eff,
+ 0x0118, 0x2e60, 0x080c, 0xaf74, 0x00ee, 0x0804, 0x7b90, 0x6024,
+ 0xc0dc, 0xc0d5, 0x6026, 0x2e60, 0x6007, 0x003a, 0xa8a0, 0x9005,
+ 0x0130, 0x6007, 0x003b, 0xa8a4, 0x602e, 0xa8a8, 0x6016, 0x6003,
+ 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763, 0x00ee, 0x0804, 0x7b90,
+ 0x2061, 0x1a70, 0x6000, 0xd084, 0x0190, 0xd08c, 0x1904, 0x7d09,
+ 0x0126, 0x2091, 0x8000, 0x6204, 0x8210, 0x0220, 0x6206, 0x012e,
+ 0x0804, 0x7d09, 0x012e, 0xa883, 0x0016, 0x0804, 0x7d02, 0xa883,
+ 0x0007, 0x0804, 0x7d02, 0xa864, 0x8007, 0x9084, 0x00ff, 0x0130,
+ 0x8001, 0x1138, 0x7007, 0x0001, 0x0069, 0x0005, 0x080c, 0x788b,
+ 0x0040, 0x7007, 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b,
+ 0x7c32, 0x0005, 0x00b6, 0x00e6, 0x0126, 0x2091, 0x8000, 0x903e,
+ 0x2061, 0x1800, 0x61d0, 0x81ff, 0x1904, 0x7cb4, 0x6130, 0xd194,
+ 0x1904, 0x7cde, 0xa878, 0x2070, 0x9e82, 0x1cd0, 0x0a04, 0x7ca8,
+ 0x6068, 0x9e02, 0x1a04, 0x7ca8, 0x7120, 0x9186, 0x0006, 0x1904,
+ 0x7c9a, 0x7010, 0x905d, 0x0904, 0x7cb4, 0xb800, 0xd0e4, 0x1904,
+ 0x7cd8, 0x2061, 0x1a70, 0x6100, 0x9184, 0x0301, 0x9086, 0x0001,
+ 0x15a0, 0x7024, 0xd0dc, 0x1904, 0x7ce1, 0xa883, 0x0000, 0xa803,
+ 0x0000, 0x2908, 0x7014, 0x9005, 0x1198, 0x7116, 0xa87c, 0xd0f4,
+ 0x1904, 0x7ce4, 0x080c, 0x575d, 0xd09c, 0x1118, 0xa87c, 0xc0cc,
+ 0xa87e, 0x2e60, 0x080c, 0x86ba, 0x012e, 0x00ee, 0x00be, 0x0005,
+ 0x2048, 0xa800, 0x9005, 0x1de0, 0xa902, 0x2148, 0xa87c, 0xd0f4,
+ 0x1904, 0x7ce4, 0x012e, 0x00ee, 0x00be, 0x0005, 0x012e, 0x00ee,
+ 0xa883, 0x0006, 0x00be, 0x0804, 0x7d02, 0xd184, 0x0db8, 0xd1c4,
+ 0x1190, 0x00a0, 0xa974, 0x080c, 0x6699, 0x15d0, 0xb800, 0xd0e4,
+ 0x15b8, 0x7120, 0x9186, 0x0007, 0x1118, 0xa883, 0x0002, 0x0490,
+ 0xa883, 0x0008, 0x0478, 0xa883, 0x000e, 0x0460, 0xa883, 0x0017,
+ 0x0448, 0xa883, 0x0035, 0x0430, 0x080c, 0x5761, 0xd0fc, 0x01e8,
+ 0xa878, 0x2070, 0x9e82, 0x1cd0, 0x02c0, 0x6068, 0x9e02, 0x12a8,
+ 0x7120, 0x9186, 0x0006, 0x1188, 0x7010, 0x905d, 0x0170, 0xb800,
+ 0xd0bc, 0x0158, 0x2039, 0x0001, 0x7000, 0x9086, 0x0007, 0x1904,
+ 0x7c3e, 0x7003, 0x0002, 0x0804, 0x7c3e, 0xa883, 0x0028, 0x0010,
+ 0xa883, 0x0029, 0x012e, 0x00ee, 0x00be, 0x0420, 0xa883, 0x002a,
+ 0x0cc8, 0xa883, 0x0045, 0x0cb0, 0x2e60, 0x2019, 0x0002, 0x601b,
+ 0x0014, 0x080c, 0xe2c0, 0x012e, 0x00ee, 0x00be, 0x0005, 0x2009,
+ 0x003e, 0x0058, 0x2009, 0x0004, 0x0040, 0x2009, 0x0006, 0x0028,
+ 0x2009, 0x0016, 0x0010, 0x2009, 0x0001, 0xa884, 0x9084, 0xff00,
+ 0x9105, 0xa886, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17, 0x012e,
+ 0x0005, 0x080c, 0x1031, 0x0005, 0x00d6, 0x080c, 0x86b1, 0x00de,
+ 0x0005, 0x00d6, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x0040,
+ 0x702c, 0xd084, 0x01d8, 0x908c, 0x0780, 0x190c, 0x7df3, 0xd09c,
+ 0x11a8, 0x2071, 0x1800, 0x70c0, 0x90ea, 0x0020, 0x0278, 0x8001,
+ 0x70c2, 0x702c, 0x2048, 0xa800, 0x702e, 0x9006, 0xa802, 0xa806,
+ 0x2071, 0x0040, 0x2900, 0x7022, 0x702c, 0x0c28, 0x012e, 0x00ee,
+ 0x00de, 0x0005, 0x0006, 0x9084, 0x0780, 0x190c, 0x7df3, 0x000e,
+ 0x0005, 0xa898, 0x9084, 0x0003, 0x05a8, 0x080c, 0xaeed, 0x05d8,
+ 0x2900, 0x6016, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0035, 0x1138,
+ 0x6008, 0xc0fd, 0x600a, 0x2001, 0x196a, 0x2004, 0x0098, 0xa8a0,
+ 0x9084, 0x00ff, 0xa99c, 0x918c, 0xff00, 0x9105, 0xa99c, 0x918c,
+ 0x00ff, 0x080c, 0x287c, 0x1540, 0x00b6, 0x080c, 0x6699, 0x2b00,
+ 0x00be, 0x1510, 0x6012, 0x6023, 0x0001, 0x2009, 0x0040, 0xa864,
+ 0x9084, 0x00ff, 0x9086, 0x0035, 0x0110, 0x2009, 0x0041, 0x080c,
+ 0xafbe, 0x0005, 0xa87b, 0x0101, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x6d17, 0x012e, 0x0005, 0xa87b, 0x002c, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6d17, 0x012e, 0x0005, 0xa87b, 0x0028, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6d17, 0x012e, 0x080c, 0xaf43, 0x0005, 0x00d6,
+ 0x00c6, 0x0036, 0x0026, 0x0016, 0x00b6, 0x7007, 0x0001, 0xaa74,
+ 0x9282, 0x0004, 0x1a04, 0x7de4, 0xa97c, 0x9188, 0x1000, 0x2104,
+ 0x905d, 0xb804, 0xd284, 0x0140, 0x05e8, 0x8007, 0x9084, 0x00ff,
+ 0x9084, 0x0006, 0x1108, 0x04b0, 0x2b10, 0x080c, 0xaeed, 0x1118,
+ 0x080c, 0xaf91, 0x05a8, 0x6212, 0xa874, 0x0002, 0x7dc2, 0x7dc7,
+ 0x7dca, 0x7dd0, 0x2019, 0x0002, 0x080c, 0xe6ae, 0x0060, 0x080c,
+ 0xe64a, 0x0048, 0x2019, 0x0002, 0xa980, 0x080c, 0xe665, 0x0018,
+ 0xa980, 0x080c, 0xe64a, 0x080c, 0xaf43, 0xa887, 0x0000, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x6d17, 0x012e, 0x00be, 0x001e, 0x002e,
+ 0x003e, 0x00ce, 0x00de, 0x0005, 0xa887, 0x0006, 0x0c80, 0xa887,
+ 0x0002, 0x0c68, 0xa887, 0x0005, 0x0c50, 0xa887, 0x0004, 0x0c38,
+ 0xa887, 0x0007, 0x0c20, 0x2091, 0x8000, 0x0e04, 0x7df5, 0x0006,
+ 0x0016, 0x2001, 0x8003, 0x0006, 0x0804, 0x0dde, 0x2001, 0x1834,
+ 0x2004, 0x9005, 0x0005, 0x0005, 0x00f6, 0x2079, 0x0300, 0x2001,
+ 0x0200, 0x200c, 0xc1e5, 0xc1dc, 0x2102, 0x2009, 0x0218, 0x210c,
+ 0xd1ec, 0x1120, 0x080c, 0x158c, 0x00fe, 0x0005, 0x2001, 0x020d,
+ 0x2003, 0x0020, 0x781f, 0x0300, 0x00fe, 0x0005, 0x781c, 0xd08c,
+ 0x0904, 0x7e75, 0x68c0, 0x90aa, 0x0005, 0x0a04, 0x84b9, 0x7d44,
+ 0x7c40, 0x9584, 0x00f6, 0x1510, 0x9484, 0x7000, 0x0140, 0x908a,
+ 0x2000, 0x1260, 0x9584, 0x0700, 0x8007, 0x0804, 0x7e7c, 0x7000,
+ 0x9084, 0xff00, 0x9086, 0x8100, 0x0da8, 0x00b0, 0x9484, 0x0fff,
+ 0x1130, 0x7000, 0x9084, 0xff00, 0x9086, 0x8100, 0x11c0, 0x080c,
+ 0xeb51, 0x080c, 0x839e, 0x7817, 0x0140, 0x00a8, 0x9584, 0x0076,
+ 0x1118, 0x080c, 0x83fc, 0x19c0, 0xd5a4, 0x0148, 0x0046, 0x0056,
+ 0x080c, 0x7ed7, 0x080c, 0x2375, 0x005e, 0x004e, 0x0020, 0x080c,
+ 0xeb51, 0x7817, 0x0140, 0x080c, 0x743e, 0x0168, 0x2001, 0x0111,
+ 0x2004, 0xd08c, 0x0140, 0x6893, 0x0000, 0x2001, 0x0110, 0x2003,
+ 0x0008, 0x2003, 0x0000, 0x080c, 0x7eb8, 0x2001, 0x19ef, 0x2004,
+ 0x9005, 0x090c, 0x9763, 0x0005, 0x0002, 0x7e8e, 0x81a6, 0x7e85,
+ 0x7e85, 0x7e85, 0x7e85, 0x7e85, 0x7e85, 0x7817, 0x0140, 0x2001,
+ 0x19ef, 0x2004, 0x9005, 0x090c, 0x9763, 0x0005, 0x7000, 0x908c,
+ 0xff00, 0x9194, 0xf000, 0x810f, 0x9484, 0x0fff, 0x6892, 0x9286,
+ 0x2000, 0x1150, 0x6800, 0x9086, 0x0001, 0x1118, 0x080c, 0x57c3,
+ 0x0070, 0x080c, 0x7ef7, 0x0058, 0x9286, 0x3000, 0x1118, 0x080c,
+ 0x80de, 0x0028, 0x9286, 0x8000, 0x1110, 0x080c, 0x82c5, 0x7817,
+ 0x0140, 0x2001, 0x19ef, 0x2004, 0x9005, 0x090c, 0x9763, 0x0005,
+ 0x2001, 0x1810, 0x2004, 0xd08c, 0x0178, 0x2001, 0x1800, 0x2004,
+ 0x9086, 0x0003, 0x1148, 0x0026, 0x0036, 0x2011, 0x8048, 0x2518,
+ 0x080c, 0x4b7f, 0x003e, 0x002e, 0x0005, 0x0036, 0x0046, 0x0056,
+ 0x00f6, 0x2079, 0x0200, 0x2019, 0xfffe, 0x7c30, 0x0050, 0x0036,
+ 0x0046, 0x0056, 0x00f6, 0x2079, 0x0200, 0x7d44, 0x7c40, 0x2019,
+ 0xffff, 0x2001, 0x1810, 0x2004, 0xd08c, 0x0160, 0x2001, 0x1800,
+ 0x2004, 0x9086, 0x0003, 0x1130, 0x0026, 0x2011, 0x8048, 0x080c,
+ 0x4b7f, 0x002e, 0x00fe, 0x005e, 0x004e, 0x003e, 0x0005, 0x00b6,
+ 0x00c6, 0x7010, 0x9084, 0xff00, 0x8007, 0x9096, 0x0001, 0x0120,
+ 0x9096, 0x0023, 0x1904, 0x80af, 0x9186, 0x0023, 0x15c0, 0x080c,
+ 0x8363, 0x0904, 0x80af, 0x6120, 0x9186, 0x0001, 0x0150, 0x9186,
+ 0x0004, 0x0138, 0x9186, 0x0008, 0x0120, 0x9186, 0x000a, 0x1904,
+ 0x80af, 0x7124, 0x610a, 0x7030, 0x908e, 0x0200, 0x1130, 0x2009,
+ 0x0015, 0x080c, 0xafbe, 0x0804, 0x80af, 0x908e, 0x0214, 0x0118,
+ 0x908e, 0x0210, 0x1130, 0x2009, 0x0015, 0x080c, 0xafbe, 0x0804,
+ 0x80af, 0x908e, 0x0100, 0x1904, 0x80af, 0x7034, 0x9005, 0x1904,
+ 0x80af, 0x2009, 0x0016, 0x080c, 0xafbe, 0x0804, 0x80af, 0x9186,
+ 0x0022, 0x1904, 0x80af, 0x7030, 0x908e, 0x0300, 0x1580, 0x68dc,
+ 0xd0a4, 0x0528, 0xc0b5, 0x68de, 0x7100, 0x918c, 0x00ff, 0x697e,
+ 0x7004, 0x6882, 0x00f6, 0x2079, 0x0100, 0x79e6, 0x78ea, 0x0006,
+ 0x9084, 0x00ff, 0x0016, 0x2008, 0x080c, 0x28c5, 0x7932, 0x7936,
+ 0x001e, 0x000e, 0x00fe, 0x080c, 0x287c, 0x695e, 0x703c, 0x00e6,
+ 0x2071, 0x0140, 0x7086, 0x2071, 0x1800, 0x70b6, 0x00ee, 0x7034,
+ 0x9005, 0x1904, 0x80af, 0x2009, 0x0017, 0x0804, 0x805f, 0x908e,
+ 0x0400, 0x1190, 0x7034, 0x9005, 0x1904, 0x80af, 0x080c, 0x743e,
+ 0x0120, 0x2009, 0x001d, 0x0804, 0x805f, 0x68dc, 0xc0a5, 0x68de,
+ 0x2009, 0x0030, 0x0804, 0x805f, 0x908e, 0x0500, 0x1140, 0x7034,
+ 0x9005, 0x1904, 0x80af, 0x2009, 0x0018, 0x0804, 0x805f, 0x908e,
+ 0x2010, 0x1120, 0x2009, 0x0019, 0x0804, 0x805f, 0x908e, 0x2110,
+ 0x1120, 0x2009, 0x001a, 0x0804, 0x805f, 0x908e, 0x5200, 0x1140,
+ 0x7034, 0x9005, 0x1904, 0x80af, 0x2009, 0x001b, 0x0804, 0x805f,
+ 0x908e, 0x5000, 0x1140, 0x7034, 0x9005, 0x1904, 0x80af, 0x2009,
+ 0x001c, 0x0804, 0x805f, 0x908e, 0x1300, 0x1120, 0x2009, 0x0034,
+ 0x0804, 0x805f, 0x908e, 0x1200, 0x1140, 0x7034, 0x9005, 0x1904,
+ 0x80af, 0x2009, 0x0024, 0x0804, 0x805f, 0x908c, 0xff00, 0x918e,
+ 0x2400, 0x1170, 0x2009, 0x002d, 0x2001, 0x1810, 0x2004, 0xd09c,
+ 0x0904, 0x805f, 0x080c, 0xda85, 0x1904, 0x80af, 0x0804, 0x805d,
+ 0x908c, 0xff00, 0x918e, 0x5300, 0x1120, 0x2009, 0x002a, 0x0804,
+ 0x805f, 0x908e, 0x0f00, 0x1120, 0x2009, 0x0020, 0x0804, 0x805f,
+ 0x908e, 0x6104, 0x1530, 0x2029, 0x0205, 0x2011, 0x026d, 0x8208,
+ 0x2204, 0x9082, 0x0004, 0x8004, 0x8004, 0x20a8, 0x2011, 0x8015,
+ 0x211c, 0x8108, 0x0046, 0x2124, 0x080c, 0x4b7f, 0x004e, 0x8108,
+ 0x0f04, 0x8013, 0x9186, 0x0280, 0x1d88, 0x2504, 0x8000, 0x202a,
+ 0x2009, 0x0260, 0x0c58, 0x202b, 0x0000, 0x2009, 0x0023, 0x0804,
+ 0x805f, 0x908e, 0x6000, 0x1120, 0x2009, 0x003f, 0x0804, 0x805f,
+ 0x908e, 0x5400, 0x1138, 0x080c, 0x8469, 0x1904, 0x80af, 0x2009,
+ 0x0046, 0x04a8, 0x908e, 0x5500, 0x1148, 0x080c, 0x8491, 0x1118,
+ 0x2009, 0x0041, 0x0460, 0x2009, 0x0042, 0x0448, 0x908e, 0x7800,
+ 0x1118, 0x2009, 0x0045, 0x0418, 0x908e, 0x1000, 0x1118, 0x2009,
+ 0x004e, 0x00e8, 0x908e, 0x6300, 0x1118, 0x2009, 0x004a, 0x00b8,
+ 0x908c, 0xff00, 0x918e, 0x5600, 0x1118, 0x2009, 0x004f, 0x0078,
+ 0x908c, 0xff00, 0x918e, 0x5700, 0x1118, 0x2009, 0x0050, 0x0038,
+ 0x2009, 0x001d, 0x6838, 0xd0d4, 0x0110, 0x2009, 0x004c, 0x0016,
+ 0x2011, 0x0263, 0x2204, 0x8211, 0x220c, 0x080c, 0x287c, 0x1904,
+ 0x80b2, 0x080c, 0x6638, 0x1904, 0x80b2, 0xbe12, 0xbd16, 0x001e,
+ 0x0016, 0x080c, 0x743e, 0x01c0, 0x68dc, 0xd08c, 0x1148, 0x7000,
+ 0x9084, 0x00ff, 0x1188, 0x7004, 0x9084, 0xff00, 0x1168, 0x0040,
+ 0x687c, 0x9606, 0x1148, 0x6880, 0x9506, 0x9084, 0xff00, 0x1120,
+ 0x9584, 0x00ff, 0xb8c2, 0x0080, 0xb8c0, 0x9005, 0x1168, 0x9186,
+ 0x0046, 0x1150, 0x687c, 0x9606, 0x1138, 0x6880, 0x9506, 0x9084,
+ 0xff00, 0x1110, 0x001e, 0x0098, 0x080c, 0xaeed, 0x01a8, 0x2b08,
+ 0x6112, 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x9186, 0x004c,
+ 0x1110, 0x6023, 0x000a, 0x0016, 0x001e, 0x080c, 0xafbe, 0x00ce,
+ 0x00be, 0x0005, 0x001e, 0x0cd8, 0x2001, 0x180e, 0x2004, 0xd0ec,
+ 0x0120, 0x2011, 0x8049, 0x080c, 0x4b7f, 0x080c, 0xaf91, 0x0d90,
+ 0x2b08, 0x6112, 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x0016,
+ 0x9186, 0x0017, 0x0118, 0x9186, 0x0030, 0x1128, 0x6007, 0x0009,
+ 0x6017, 0x2900, 0x0020, 0x6007, 0x0051, 0x6017, 0x0000, 0x602f,
+ 0x0009, 0x6003, 0x0001, 0x080c, 0x91f9, 0x08a0, 0x080c, 0x84d8,
+ 0x1158, 0x080c, 0x3342, 0x1140, 0x7010, 0x9084, 0xff00, 0x8007,
+ 0x908e, 0x0008, 0x1108, 0x0009, 0x0005, 0x00b6, 0x00c6, 0x0046,
+ 0x7000, 0x908c, 0xff00, 0x810f, 0x9186, 0x0033, 0x11e8, 0x080c,
+ 0x8363, 0x0904, 0x813e, 0x7124, 0x610a, 0x7030, 0x908e, 0x0200,
+ 0x1140, 0x7034, 0x9005, 0x15d0, 0x2009, 0x0015, 0x080c, 0xafbe,
+ 0x04a8, 0x908e, 0x0100, 0x1590, 0x7034, 0x9005, 0x1578, 0x2009,
+ 0x0016, 0x080c, 0xafbe, 0x0450, 0x9186, 0x0032, 0x1538, 0x7030,
+ 0x908e, 0x1400, 0x1518, 0x2009, 0x0038, 0x0016, 0x2011, 0x0263,
+ 0x2204, 0x8211, 0x220c, 0x080c, 0x287c, 0x11b8, 0x080c, 0x6638,
+ 0x11a0, 0xbe12, 0xbd16, 0x080c, 0xaeed, 0x0178, 0x2b08, 0x6112,
+ 0x080c, 0xd102, 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x080c,
+ 0xafbe, 0x080c, 0x9763, 0x0010, 0x00ce, 0x001e, 0x004e, 0x00ce,
+ 0x00be, 0x0005, 0x00b6, 0x0046, 0x00e6, 0x00d6, 0x2028, 0x2130,
+ 0x9696, 0x00ff, 0x11b8, 0x9592, 0xfffc, 0x02a0, 0x9596, 0xfffd,
+ 0x1120, 0x2009, 0x007f, 0x0804, 0x81a0, 0x9596, 0xfffe, 0x1120,
+ 0x2009, 0x007e, 0x0804, 0x81a0, 0x9596, 0xfffc, 0x1118, 0x2009,
+ 0x0080, 0x04f0, 0x2011, 0x0000, 0x2019, 0x1837, 0x231c, 0xd3ac,
+ 0x0130, 0x9026, 0x20a9, 0x0800, 0x2071, 0x1000, 0x0030, 0x2021,
+ 0x0081, 0x20a9, 0x077f, 0x2071, 0x1081, 0x2e1c, 0x93dd, 0x0000,
+ 0x1140, 0x82ff, 0x11d0, 0x9496, 0x00ff, 0x01b8, 0x2410, 0xc2fd,
+ 0x00a0, 0xbf10, 0x2600, 0x9706, 0xb814, 0x1120, 0x9546, 0x1110,
+ 0x2408, 0x00b0, 0x9745, 0x1148, 0x94c6, 0x007e, 0x0130, 0x94c6,
+ 0x007f, 0x0118, 0x94c6, 0x0080, 0x1d20, 0x8420, 0x8e70, 0x1f04,
+ 0x8175, 0x82ff, 0x1118, 0x9085, 0x0001, 0x0018, 0xc2fc, 0x2208,
+ 0x9006, 0x00de, 0x00ee, 0x004e, 0x00be, 0x0005, 0x2001, 0x1837,
+ 0x200c, 0x9184, 0x0080, 0x0110, 0xd18c, 0x0138, 0x7000, 0x908c,
+ 0xff00, 0x810f, 0x9184, 0x000f, 0x004a, 0x7817, 0x0140, 0x2001,
+ 0x19ef, 0x2004, 0x9005, 0x090c, 0x9763, 0x0005, 0x81ce, 0x81ce,
+ 0x81ce, 0x8375, 0x81ce, 0x81d7, 0x8202, 0x8290, 0x81ce, 0x81ce,
+ 0x81ce, 0x81ce, 0x81ce, 0x81ce, 0x81ce, 0x81ce, 0x7817, 0x0140,
+ 0x2001, 0x19ef, 0x2004, 0x9005, 0x090c, 0x9763, 0x0005, 0x00b6,
+ 0x7110, 0xd1bc, 0x01e8, 0x7120, 0x2160, 0x9c8c, 0x0007, 0x11c0,
+ 0x9c8a, 0x1cd0, 0x02a8, 0x6868, 0x9c02, 0x1290, 0x7008, 0x9084,
+ 0x00ff, 0x6110, 0x2158, 0xb910, 0x9106, 0x1150, 0x700c, 0xb914,
+ 0x9106, 0x1130, 0x7124, 0x610a, 0x2009, 0x0046, 0x080c, 0xafbe,
+ 0x7817, 0x0140, 0x2001, 0x19ef, 0x2004, 0x9005, 0x090c, 0x9763,
+ 0x00be, 0x0005, 0x00b6, 0x00c6, 0x9484, 0x0fff, 0x0904, 0x8266,
+ 0x7110, 0xd1bc, 0x1904, 0x8266, 0x7108, 0x700c, 0x2028, 0x918c,
+ 0x00ff, 0x2130, 0x9094, 0xff00, 0x15b0, 0x81ff, 0x15a0, 0x9080,
+ 0x3384, 0x200d, 0x918c, 0xff00, 0x810f, 0x2001, 0x0080, 0x9106,
+ 0x0904, 0x8266, 0x080c, 0x6638, 0x1904, 0x8266, 0xbe12, 0xbd16,
+ 0xb800, 0xd0ec, 0x15d8, 0xba04, 0x9294, 0xff00, 0x9286, 0x0600,
+ 0x11a0, 0x080c, 0xaeed, 0x05e8, 0x2b08, 0x7028, 0x6046, 0x702c,
+ 0x604a, 0x6112, 0x6023, 0x0006, 0x7120, 0x610a, 0x7130, 0x6156,
+ 0x2009, 0x0044, 0x080c, 0xdce5, 0x0408, 0x080c, 0x6a0c, 0x1138,
+ 0xb807, 0x0606, 0x0c30, 0x190c, 0x8142, 0x11c0, 0x0898, 0x080c,
+ 0xaeed, 0x2b08, 0x0198, 0x6112, 0x6023, 0x0004, 0x7120, 0x610a,
+ 0x9286, 0x0400, 0x1118, 0x6007, 0x0005, 0x0010, 0x6007, 0x0001,
+ 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c, 0x9763, 0x7817, 0x0140,
+ 0x2001, 0x19ef, 0x2004, 0x9005, 0x090c, 0x9763, 0x00ce, 0x00be,
+ 0x0005, 0x2001, 0x180e, 0x2004, 0xd0ec, 0x0120, 0x2011, 0x8049,
+ 0x080c, 0x4b7f, 0x080c, 0xaf91, 0x0d48, 0x2b08, 0x6112, 0x6023,
+ 0x0006, 0x7120, 0x610a, 0x7130, 0x6156, 0x6017, 0xf300, 0x6003,
+ 0x0001, 0x6007, 0x0041, 0x080c, 0x91b1, 0x080c, 0x9763, 0x08b0,
+ 0x00b6, 0x7110, 0xd1bc, 0x01e8, 0x7020, 0x2060, 0x9c84, 0x0007,
+ 0x11c0, 0x9c82, 0x1cd0, 0x02a8, 0x6868, 0x9c02, 0x1290, 0x7008,
+ 0x9084, 0x00ff, 0x6110, 0x2158, 0xb910, 0x9106, 0x1150, 0x700c,
+ 0xb914, 0x9106, 0x1130, 0x7124, 0x610a, 0x2009, 0x0045, 0x080c,
+ 0xafbe, 0x7817, 0x0140, 0x2001, 0x19ef, 0x2004, 0x9005, 0x090c,
+ 0x9763, 0x00be, 0x0005, 0x6120, 0x9186, 0x0002, 0x0128, 0x9186,
+ 0x0005, 0x0110, 0x9085, 0x0001, 0x0005, 0x080c, 0x84d8, 0x1180,
+ 0x080c, 0x3342, 0x1168, 0x7010, 0x9084, 0xff00, 0x8007, 0x9086,
+ 0x0000, 0x1130, 0x9184, 0x000f, 0x908a, 0x0006, 0x1208, 0x000b,
+ 0x0005, 0x82df, 0x82e0, 0x82df, 0x82df, 0x8345, 0x8354, 0x0005,
+ 0x00b6, 0x700c, 0x7108, 0x080c, 0x287c, 0x1904, 0x8343, 0x080c,
+ 0x6638, 0x1904, 0x8343, 0xbe12, 0xbd16, 0x7110, 0xd1bc, 0x0540,
+ 0x702c, 0xd084, 0x1120, 0xb800, 0xd0bc, 0x1904, 0x8343, 0x080c,
+ 0x6a0c, 0x0148, 0x9086, 0x0004, 0x0130, 0x080c, 0x6a14, 0x0118,
+ 0x9086, 0x0004, 0x1588, 0x00c6, 0x080c, 0x8363, 0x00ce, 0x05d8,
+ 0x080c, 0xaeed, 0x2b08, 0x05b8, 0x6112, 0x080c, 0xd102, 0x6023,
+ 0x0002, 0x7120, 0x610a, 0x2009, 0x0088, 0x080c, 0xafbe, 0x0458,
+ 0x080c, 0x6a0c, 0x0148, 0x9086, 0x0004, 0x0130, 0x080c, 0x6a14,
+ 0x0118, 0x9086, 0x0004, 0x1180, 0x080c, 0xaeed, 0x2b08, 0x01d8,
+ 0x6112, 0x080c, 0xd102, 0x6023, 0x0005, 0x7120, 0x610a, 0x2009,
+ 0x0088, 0x080c, 0xafbe, 0x0078, 0x080c, 0xaeed, 0x2b08, 0x0158,
+ 0x6112, 0x080c, 0xd102, 0x6023, 0x0004, 0x7120, 0x610a, 0x2009,
+ 0x0001, 0x080c, 0xafbe, 0x00be, 0x0005, 0x7110, 0xd1bc, 0x0158,
+ 0x00d1, 0x0148, 0x080c, 0x82bb, 0x1130, 0x7124, 0x610a, 0x2009,
+ 0x0089, 0x080c, 0xafbe, 0x0005, 0x7110, 0xd1bc, 0x0158, 0x0059,
+ 0x0148, 0x080c, 0x82bb, 0x1130, 0x7124, 0x610a, 0x2009, 0x008a,
+ 0x080c, 0xafbe, 0x0005, 0x7020, 0x2060, 0x9c84, 0x0007, 0x1158,
+ 0x9c82, 0x1cd0, 0x0240, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1218,
+ 0x9085, 0x0001, 0x0005, 0x9006, 0x0ce8, 0x00b6, 0x7110, 0xd1bc,
+ 0x11d8, 0x7024, 0x2060, 0x9c84, 0x0007, 0x11b0, 0x9c82, 0x1cd0,
+ 0x0298, 0x6868, 0x9c02, 0x1280, 0x7008, 0x9084, 0x00ff, 0x6110,
+ 0x2158, 0xb910, 0x9106, 0x1140, 0x700c, 0xb914, 0x9106, 0x1120,
+ 0x2009, 0x0051, 0x080c, 0xafbe, 0x7817, 0x0140, 0x2001, 0x19ef,
+ 0x2004, 0x9005, 0x090c, 0x9763, 0x00be, 0x0005, 0x2031, 0x0105,
+ 0x0069, 0x0005, 0x2031, 0x0206, 0x0049, 0x0005, 0x2031, 0x0207,
+ 0x0029, 0x0005, 0x2031, 0x0213, 0x0009, 0x0005, 0x00c6, 0x0096,
+ 0x00f6, 0x7000, 0x9084, 0xf000, 0x9086, 0xc000, 0x05d0, 0x080c,
+ 0xaeed, 0x05b8, 0x0066, 0x00c6, 0x0046, 0x2011, 0x0263, 0x2204,
+ 0x8211, 0x220c, 0x080c, 0x287c, 0x15a0, 0x080c, 0x6638, 0x1588,
+ 0xbe12, 0xbd16, 0x2b00, 0x004e, 0x00ce, 0x6012, 0x080c, 0xd102,
+ 0x080c, 0x0fff, 0x0510, 0x2900, 0x605a, 0x9006, 0xa802, 0xa866,
+ 0xac6a, 0xa85c, 0x90f8, 0x001b, 0x20a9, 0x000e, 0xa860, 0x20e8,
+ 0x20e1, 0x0000, 0x2fa0, 0x2e98, 0x4003, 0x006e, 0x6616, 0x6007,
+ 0x003e, 0x6023, 0x0001, 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c,
+ 0x9763, 0x00fe, 0x009e, 0x00ce, 0x0005, 0x080c, 0xaf43, 0x006e,
+ 0x0cc0, 0x004e, 0x00ce, 0x0cc8, 0x00c6, 0x7000, 0x908c, 0xff00,
+ 0x9184, 0xf000, 0x810f, 0x9086, 0x2000, 0x1904, 0x8453, 0x9186,
+ 0x0022, 0x15f0, 0x2001, 0x0111, 0x2004, 0x9005, 0x1904, 0x8455,
+ 0x7030, 0x908e, 0x0400, 0x0904, 0x8455, 0x908e, 0x6000, 0x05e8,
+ 0x908e, 0x5400, 0x05d0, 0x908e, 0x0300, 0x11d8, 0x2009, 0x1837,
+ 0x210c, 0xd18c, 0x1590, 0xd1a4, 0x1580, 0x080c, 0x69ca, 0x0588,
+ 0x68b0, 0x9084, 0x00ff, 0x7100, 0x918c, 0x00ff, 0x9106, 0x1518,
+ 0x6880, 0x69b0, 0x918c, 0xff00, 0x9105, 0x7104, 0x9106, 0x11d8,
+ 0x00e0, 0x2009, 0x0103, 0x210c, 0xd1b4, 0x11a8, 0x908e, 0x5200,
+ 0x09e8, 0x908e, 0x0500, 0x09d0, 0x908e, 0x5000, 0x09b8, 0x0058,
+ 0x9186, 0x0023, 0x1140, 0x080c, 0x8363, 0x0128, 0x6004, 0x9086,
+ 0x0002, 0x0118, 0x0000, 0x9006, 0x0010, 0x9085, 0x0001, 0x00ce,
+ 0x0005, 0x7030, 0x908e, 0x0300, 0x0118, 0x908e, 0x5200, 0x1d98,
+ 0x2001, 0x1837, 0x2004, 0x9084, 0x0009, 0x9086, 0x0008, 0x0d68,
+ 0x0c50, 0x0156, 0x0046, 0x0016, 0x0036, 0x7038, 0x2020, 0x8427,
+ 0x94a4, 0x0007, 0xd484, 0x0148, 0x20a9, 0x0004, 0x2019, 0x1805,
+ 0x2011, 0x027a, 0x080c, 0xbefd, 0x1178, 0xd48c, 0x0148, 0x20a9,
+ 0x0004, 0x2019, 0x1801, 0x2011, 0x027e, 0x080c, 0xbefd, 0x1120,
+ 0xd494, 0x0110, 0x9085, 0x0001, 0x003e, 0x001e, 0x004e, 0x015e,
+ 0x0005, 0x0156, 0x0046, 0x0016, 0x0036, 0x7038, 0x2020, 0x8427,
+ 0x94a4, 0x0007, 0xd484, 0x0148, 0x20a9, 0x0004, 0x2019, 0x1805,
+ 0x2011, 0x0272, 0x080c, 0xbefd, 0x1178, 0xd48c, 0x0148, 0x20a9,
+ 0x0004, 0x2019, 0x1801, 0x2011, 0x0276, 0x080c, 0xbefd, 0x1120,
+ 0xd494, 0x0110, 0x9085, 0x0001, 0x003e, 0x001e, 0x004e, 0x015e,
+ 0x0005, 0x00f6, 0x2079, 0x0200, 0x7800, 0xc0e5, 0xc0cc, 0x7802,
+ 0x00fe, 0x0005, 0x00f6, 0x2079, 0x1800, 0x7834, 0xd084, 0x1130,
+ 0x2079, 0x0200, 0x7800, 0x9085, 0x1200, 0x7802, 0x00fe, 0x0005,
+ 0x00e6, 0x2071, 0x1800, 0x7034, 0xc084, 0x7036, 0x00ee, 0x0005,
+ 0x0016, 0x2001, 0x1837, 0x200c, 0x9184, 0x0080, 0x0118, 0xd18c,
+ 0x0118, 0x9006, 0x001e, 0x0005, 0x9085, 0x0001, 0x0cd8, 0x2071,
+ 0x19f9, 0x7003, 0x0003, 0x700f, 0x0361, 0x9006, 0x701a, 0x7072,
+ 0x7012, 0x7017, 0x1cd0, 0x7007, 0x0000, 0x7026, 0x702b, 0xa36c,
+ 0x7032, 0x7037, 0xa3d4, 0x703f, 0xffff, 0x7042, 0x7047, 0x55ef,
+ 0x704a, 0x705b, 0x8651, 0x080c, 0x1018, 0x090c, 0x0dd5, 0x2900,
+ 0x703a, 0xa867, 0x0003, 0xa86f, 0x0100, 0xa8ab, 0xdcb0, 0x0005,
+ 0x2071, 0x19f9, 0x1d04, 0x859f, 0x2091, 0x6000, 0x700c, 0x8001,
+ 0x700e, 0x1530, 0x2001, 0x013c, 0x2004, 0x9005, 0x190c, 0x8696,
+ 0x2001, 0x1869, 0x2004, 0xd0c4, 0x0158, 0x3a00, 0xd08c, 0x1140,
+ 0x20d1, 0x0000, 0x20d1, 0x0001, 0x20d1, 0x0000, 0x080c, 0x0dd5,
+ 0x700f, 0x0361, 0x7007, 0x0001, 0x0126, 0x2091, 0x8000, 0x7040,
+ 0x900d, 0x0148, 0x8109, 0x7142, 0x1130, 0x7044, 0x080f, 0x0018,
+ 0x0126, 0x2091, 0x8000, 0x7024, 0x900d, 0x0188, 0x7020, 0x8001,
+ 0x7022, 0x1168, 0x7023, 0x0009, 0x8109, 0x7126, 0x9186, 0x03e8,
+ 0x1110, 0x7028, 0x080f, 0x81ff, 0x1110, 0x7028, 0x080f, 0x7030,
+ 0x900d, 0x0180, 0x702c, 0x8001, 0x702e, 0x1160, 0x702f, 0x0009,
+ 0x8109, 0x7132, 0x0128, 0x9184, 0x007f, 0x090c, 0xa50e, 0x0010,
+ 0x7034, 0x080f, 0x703c, 0x9005, 0x0118, 0x0310, 0x8001, 0x703e,
+ 0x704c, 0x900d, 0x0168, 0x7048, 0x8001, 0x704a, 0x1148, 0x704b,
+ 0x0009, 0x8109, 0x714e, 0x1120, 0x7150, 0x714e, 0x7058, 0x080f,
+ 0x7018, 0x900d, 0x01d8, 0x0016, 0x7070, 0x900d, 0x0158, 0x706c,
+ 0x8001, 0x706e, 0x1138, 0x706f, 0x0009, 0x8109, 0x7172, 0x1110,
+ 0x7074, 0x080f, 0x001e, 0x7008, 0x8001, 0x700a, 0x1138, 0x700b,
+ 0x0009, 0x8109, 0x711a, 0x1110, 0x701c, 0x080f, 0x012e, 0x7004,
+ 0x0002, 0x85c7, 0x85c8, 0x85e4, 0x00e6, 0x2071, 0x19f9, 0x7018,
+ 0x9005, 0x1120, 0x711a, 0x721e, 0x700b, 0x0009, 0x00ee, 0x0005,
+ 0x00e6, 0x0006, 0x2071, 0x19f9, 0x701c, 0x9206, 0x1120, 0x701a,
+ 0x701e, 0x7072, 0x7076, 0x000e, 0x00ee, 0x0005, 0x00e6, 0x2071,
+ 0x19f9, 0xb888, 0x9102, 0x0208, 0xb98a, 0x00ee, 0x0005, 0x0005,
+ 0x00b6, 0x7110, 0x080c, 0x6699, 0x1168, 0xb888, 0x8001, 0x0250,
+ 0xb88a, 0x1140, 0x0126, 0x2091, 0x8000, 0x0016, 0x080c, 0x9763,
+ 0x001e, 0x012e, 0x8108, 0x9182, 0x0800, 0x0218, 0x900e, 0x7007,
+ 0x0002, 0x7112, 0x00be, 0x0005, 0x7014, 0x2060, 0x0126, 0x2091,
+ 0x8000, 0x6040, 0x9005, 0x0128, 0x8001, 0x6042, 0x1110, 0x080c,
+ 0xcf93, 0x6018, 0x9005, 0x0558, 0x8001, 0x601a, 0x1540, 0x6120,
+ 0x9186, 0x0003, 0x0148, 0x9186, 0x0006, 0x0130, 0x9186, 0x0009,
+ 0x11e0, 0x611c, 0xd1c4, 0x1100, 0x080c, 0xcc86, 0x01b0, 0x6014,
+ 0x2048, 0xa884, 0x908a, 0x199a, 0x0280, 0x9082, 0x1999, 0xa886,
+ 0x908a, 0x199a, 0x0210, 0x2001, 0x1999, 0x8003, 0x800b, 0x810b,
+ 0x9108, 0x611a, 0xa87c, 0xd0e4, 0x0110, 0x080c, 0xc972, 0x012e,
+ 0x9c88, 0x0018, 0x7116, 0x2001, 0x181a, 0x2004, 0x9102, 0x0220,
+ 0x7017, 0x1cd0, 0x7007, 0x0000, 0x0005, 0x00e6, 0x2071, 0x19f9,
+ 0x7027, 0x07d0, 0x7023, 0x0009, 0x00ee, 0x0005, 0x2001, 0x1a02,
+ 0x2003, 0x0000, 0x0005, 0x00e6, 0x2071, 0x19f9, 0x7132, 0x702f,
+ 0x0009, 0x00ee, 0x0005, 0x2011, 0x1a05, 0x2013, 0x0000, 0x0005,
+ 0x00e6, 0x2071, 0x19f9, 0x711a, 0x721e, 0x700b, 0x0009, 0x00ee,
+ 0x0005, 0x0086, 0x0026, 0x7054, 0x8000, 0x7056, 0x2001, 0x1a07,
+ 0x2044, 0xa06c, 0x9086, 0x0000, 0x0150, 0x7068, 0xa09a, 0x7064,
+ 0xa096, 0x7060, 0xa092, 0x705c, 0xa08e, 0x080c, 0x10e9, 0x002e,
+ 0x008e, 0x0005, 0x0006, 0x0016, 0x0096, 0x00a6, 0x00b6, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x0156, 0x080c, 0x8510, 0x015e, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e, 0x001e, 0x000e,
+ 0x0005, 0x00e6, 0x2071, 0x19f9, 0x7172, 0x7276, 0x706f, 0x0009,
+ 0x00ee, 0x0005, 0x00e6, 0x0006, 0x2071, 0x19f9, 0x7074, 0x9206,
+ 0x1110, 0x7072, 0x7076, 0x000e, 0x00ee, 0x0005, 0x0016, 0x00c6,
+ 0x2009, 0xfff4, 0x210d, 0x2061, 0x0100, 0x60f0, 0x9100, 0x60f3,
+ 0x0000, 0x2009, 0xfff4, 0x200f, 0x1220, 0x8108, 0x2105, 0x8000,
+ 0x200f, 0x00ce, 0x001e, 0x0005, 0x00c6, 0x2061, 0x1a70, 0x00ce,
+ 0x0005, 0x9184, 0x000f, 0x8003, 0x8003, 0x8003, 0x9080, 0x1a70,
+ 0x2060, 0x0005, 0xa884, 0x908a, 0x199a, 0x1638, 0x9005, 0x1150,
+ 0x00c6, 0x2061, 0x1a70, 0x6014, 0x00ce, 0x9005, 0x1130, 0x2001,
+ 0x001e, 0x0018, 0x908e, 0xffff, 0x01b0, 0x8003, 0x800b, 0x810b,
+ 0x9108, 0x611a, 0xa87c, 0x908c, 0x00c0, 0x918e, 0x00c0, 0x0904,
+ 0x8744, 0xd0b4, 0x1168, 0xd0bc, 0x1904, 0x871d, 0x2009, 0x0006,
+ 0x080c, 0x8771, 0x0005, 0x900e, 0x0c60, 0x2001, 0x1999, 0x08b0,
+ 0xd0fc, 0x0160, 0x908c, 0x0003, 0x0120, 0x918e, 0x0003, 0x1904,
+ 0x876b, 0x908c, 0x2020, 0x918e, 0x2020, 0x01a8, 0x6024, 0xd0d4,
+ 0x11e8, 0x2009, 0x1869, 0x2104, 0xd084, 0x1138, 0x87ff, 0x1120,
+ 0x2009, 0x0043, 0x0804, 0xafbe, 0x0005, 0x87ff, 0x1de8, 0x2009,
+ 0x0042, 0x0804, 0xafbe, 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be,
+ 0xd1ac, 0x0d20, 0x6024, 0xc0cd, 0x6026, 0x0c00, 0xc0d4, 0x6026,
+ 0xa890, 0x602e, 0xa88c, 0x6032, 0x08e0, 0xd0fc, 0x0160, 0x908c,
+ 0x0003, 0x0120, 0x918e, 0x0003, 0x1904, 0x876b, 0x908c, 0x2020,
+ 0x918e, 0x2020, 0x0170, 0x0076, 0x00f6, 0x2c78, 0x080c, 0x1754,
+ 0x00fe, 0x007e, 0x87ff, 0x1120, 0x2009, 0x0042, 0x080c, 0xafbe,
+ 0x0005, 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac, 0x0d58,
+ 0x6124, 0xc1cd, 0x6126, 0x0c38, 0xd0fc, 0x0188, 0x908c, 0x2020,
+ 0x918e, 0x2020, 0x01a8, 0x9084, 0x0003, 0x908e, 0x0002, 0x0148,
+ 0x87ff, 0x1120, 0x2009, 0x0041, 0x080c, 0xafbe, 0x0005, 0x00b9,
+ 0x0ce8, 0x87ff, 0x1dd8, 0x2009, 0x0043, 0x080c, 0xafbe, 0x0cb0,
+ 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac, 0x0d20, 0x6124,
+ 0xc1cd, 0x6126, 0x0c00, 0x2009, 0x0004, 0x0019, 0x0005, 0x2009,
+ 0x0001, 0x0096, 0x080c, 0xcc86, 0x0518, 0x6014, 0x2048, 0xa982,
+ 0xa800, 0x6016, 0x9186, 0x0001, 0x1188, 0xa97c, 0x918c, 0x8100,
+ 0x918e, 0x8100, 0x1158, 0x00c6, 0x2061, 0x1a70, 0x6200, 0xd28c,
+ 0x1120, 0x6204, 0x8210, 0x0208, 0x6206, 0x00ce, 0x080c, 0x6b52,
+ 0x6014, 0x904d, 0x0076, 0x2039, 0x0000, 0x190c, 0x86ba, 0x007e,
+ 0x009e, 0x0005, 0x0156, 0x00c6, 0x2061, 0x1a70, 0x6000, 0x81ff,
+ 0x0110, 0x9205, 0x0008, 0x9204, 0x6002, 0x00ce, 0x015e, 0x0005,
+ 0x6800, 0xd08c, 0x1138, 0x6808, 0x9005, 0x0120, 0x8001, 0x680a,
+ 0x9085, 0x0001, 0x0005, 0x2071, 0x1923, 0x7003, 0x0006, 0x7007,
+ 0x0000, 0x700f, 0x0000, 0x7013, 0x0001, 0x080c, 0x1018, 0x090c,
+ 0x0dd5, 0xa867, 0x0006, 0xa86b, 0x0001, 0xa8ab, 0xdcb0, 0xa89f,
+ 0x0000, 0x2900, 0x702e, 0x7033, 0x0000, 0x0005, 0x0096, 0x00e6,
+ 0x2071, 0x1923, 0x702c, 0x2048, 0x6a2c, 0x721e, 0x6b30, 0x7322,
+ 0x6834, 0x7026, 0xa896, 0x6838, 0x702a, 0xa89a, 0x6824, 0x7016,
+ 0x683c, 0x701a, 0x2009, 0x0028, 0x200a, 0x9005, 0x0148, 0x900e,
+ 0x9188, 0x000c, 0x8001, 0x1de0, 0x2100, 0x9210, 0x1208, 0x8318,
+ 0xaa8e, 0xab92, 0x7010, 0xd084, 0x0168, 0xc084, 0x7007, 0x0001,
+ 0x700f, 0x0000, 0x0006, 0x2009, 0x1aca, 0x2104, 0x9082, 0x0007,
+ 0x200a, 0x000e, 0xc095, 0x7012, 0x2008, 0x2001, 0x003b, 0x080c,
+ 0x15fd, 0x9006, 0x2071, 0x193c, 0x7002, 0x7006, 0x702a, 0x00ee,
+ 0x009e, 0x0005, 0x2009, 0x1aca, 0x2104, 0x9080, 0x0007, 0x200a,
+ 0x0005, 0x00e6, 0x0126, 0x0156, 0x2091, 0x8000, 0x2071, 0x1800,
+ 0x7154, 0x2001, 0x0008, 0x910a, 0x0638, 0x2001, 0x187d, 0x20ac,
+ 0x9006, 0x9080, 0x0008, 0x1f04, 0x8829, 0x71c0, 0x9102, 0x02e0,
+ 0x2071, 0x1877, 0x20a9, 0x0007, 0x00c6, 0x080c, 0xaeed, 0x6023,
+ 0x0009, 0x6003, 0x0004, 0x601f, 0x0101, 0x0089, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x89a7, 0x012e, 0x1f04, 0x8835, 0x9006, 0x00ce,
+ 0x015e, 0x012e, 0x00ee, 0x0005, 0x9085, 0x0001, 0x0cc8, 0x00e6,
+ 0x00b6, 0x0096, 0x0086, 0x0056, 0x0046, 0x0026, 0x7118, 0x720c,
+ 0x7620, 0x7004, 0xd084, 0x1128, 0x2021, 0x0024, 0x2029, 0x0002,
+ 0x0020, 0x2021, 0x002c, 0x2029, 0x000a, 0x080c, 0x0fff, 0x090c,
+ 0x0dd5, 0x2900, 0x6016, 0x2058, 0xac66, 0x9006, 0xa802, 0xa806,
+ 0xa86a, 0xa87a, 0xa8aa, 0xa887, 0x0005, 0xa87f, 0x0020, 0x7008,
+ 0xa89a, 0x7010, 0xa89e, 0xae8a, 0xa8af, 0xffff, 0xa8b3, 0x0000,
+ 0x8109, 0x0160, 0x080c, 0x0fff, 0x090c, 0x0dd5, 0xad66, 0x2b00,
+ 0xa802, 0x2900, 0xb806, 0x2058, 0x8109, 0x1da0, 0x002e, 0x004e,
+ 0x005e, 0x008e, 0x009e, 0x00be, 0x00ee, 0x0005, 0x2079, 0x0000,
+ 0x2071, 0x1923, 0x7004, 0x004b, 0x700c, 0x0002, 0x88a1, 0x889a,
+ 0x889a, 0x0005, 0x88ab, 0x8901, 0x8901, 0x8901, 0x8902, 0x8913,
+ 0x8913, 0x700c, 0x0cba, 0x0126, 0x2091, 0x8000, 0x78a0, 0x79a0,
+ 0x9106, 0x1904, 0x88f3, 0x7814, 0xd0bc, 0x1904, 0x88fc, 0x012e,
+ 0x7018, 0x910a, 0x1128, 0x7030, 0x9005, 0x1904, 0x8945, 0x0005,
+ 0x1210, 0x7114, 0x910a, 0x9192, 0x000a, 0x0210, 0x2009, 0x000a,
+ 0x2001, 0x1888, 0x2014, 0x2001, 0x1935, 0x2004, 0x9100, 0x9202,
+ 0x0e50, 0x080c, 0x8a9b, 0x2200, 0x9102, 0x0208, 0x2208, 0x0096,
+ 0x702c, 0x2048, 0xa873, 0x0001, 0xa976, 0x080c, 0x8ba4, 0x2100,
+ 0xa87e, 0xa86f, 0x0000, 0x009e, 0x0126, 0x2091, 0x8000, 0x2009,
+ 0x1a17, 0x2104, 0xc085, 0x200a, 0x700f, 0x0002, 0x012e, 0x080c,
+ 0x1108, 0x1de8, 0x0005, 0x78a0, 0x79a0, 0x9106, 0x0904, 0x88b3,
+ 0x080c, 0x8a73, 0x012e, 0x0005, 0x7810, 0xc0c5, 0x7812, 0x0804,
+ 0x88b3, 0x0005, 0x700c, 0x0002, 0x8907, 0x890a, 0x8909, 0x080c,
+ 0x88a9, 0x0005, 0x8001, 0x700e, 0x0096, 0x702c, 0x2048, 0xa974,
+ 0x009e, 0x0011, 0x0ca0, 0x0005, 0x0096, 0x702c, 0x2048, 0x7018,
+ 0x9100, 0x7214, 0x921a, 0x1130, 0x701c, 0xa88e, 0x7020, 0xa892,
+ 0x9006, 0x0068, 0x0006, 0x080c, 0x8ba4, 0x2100, 0xaa8c, 0x9210,
+ 0xaa8e, 0x1220, 0xa890, 0x9081, 0x0000, 0xa892, 0x000e, 0x009e,
+ 0x0126, 0x2091, 0x8000, 0x78a2, 0x701a, 0x080c, 0x8a73, 0x012e,
+ 0x0005, 0x00e6, 0x2071, 0x1923, 0x700c, 0x0002, 0x8943, 0x8943,
+ 0x8941, 0x700f, 0x0001, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x7030, 0x9005, 0x0508, 0x2078, 0x7814, 0x2048, 0xae88, 0x00b6,
+ 0x2059, 0x0000, 0x080c, 0x89b0, 0x00be, 0x01b0, 0x00e6, 0x2071,
+ 0x193c, 0x080c, 0x89f7, 0x00ee, 0x0178, 0x0096, 0x080c, 0x1018,
+ 0x2900, 0x009e, 0x0148, 0xa8aa, 0x04b9, 0x0041, 0x2001, 0x1946,
+ 0x2003, 0x0000, 0x012e, 0x08c8, 0x012e, 0x0005, 0x00d6, 0x00c6,
+ 0x0086, 0x00a6, 0x2940, 0x2650, 0x2600, 0x9005, 0x0180, 0xa864,
+ 0x9084, 0x000f, 0x2068, 0x9d88, 0x20ce, 0x2165, 0x0056, 0x2029,
+ 0x0000, 0x080c, 0x8b29, 0x080c, 0x2086, 0x1dd8, 0x005e, 0x00ae,
+ 0x2001, 0x187f, 0x2004, 0xa88a, 0x080c, 0x1754, 0x781f, 0x0101,
+ 0x7813, 0x0000, 0x0126, 0x2091, 0x8000, 0x080c, 0x8a06, 0x012e,
+ 0x008e, 0x00ce, 0x00de, 0x0005, 0x7030, 0x9005, 0x0138, 0x2078,
+ 0x780c, 0x7032, 0x2001, 0x1946, 0x2003, 0x0001, 0x0005, 0x00e6,
+ 0x2071, 0x1923, 0x7030, 0x600e, 0x2c00, 0x7032, 0x00ee, 0x0005,
+ 0x00d6, 0x00c6, 0x0026, 0x9b80, 0x8c72, 0x2005, 0x906d, 0x090c,
+ 0x0dd5, 0x9b80, 0x8c6a, 0x2005, 0x9065, 0x090c, 0x0dd5, 0x6114,
+ 0x2600, 0x9102, 0x0248, 0x6828, 0x9102, 0x02f0, 0x9085, 0x0001,
+ 0x002e, 0x00ce, 0x00de, 0x0005, 0x6804, 0xd094, 0x0148, 0x6854,
+ 0xd084, 0x1178, 0xc085, 0x6856, 0x2011, 0x8026, 0x080c, 0x4b7f,
+ 0x684c, 0x0096, 0x904d, 0x090c, 0x0dd5, 0xa804, 0x8000, 0xa806,
+ 0x009e, 0x9006, 0x2030, 0x0c20, 0x6854, 0xd08c, 0x1d08, 0xc08d,
+ 0x6856, 0x2011, 0x8025, 0x080c, 0x4b7f, 0x684c, 0x0096, 0x904d,
+ 0x090c, 0x0dd5, 0xa800, 0x8000, 0xa802, 0x009e, 0x0888, 0x7000,
+ 0x2019, 0x0008, 0x8319, 0x7104, 0x9102, 0x1118, 0x2300, 0x9005,
+ 0x0020, 0x0210, 0x9302, 0x0008, 0x8002, 0x0005, 0x00d6, 0x7814,
+ 0x9005, 0x090c, 0x0dd5, 0x781c, 0x9084, 0x0101, 0x9086, 0x0101,
+ 0x190c, 0x0dd5, 0x7827, 0x0000, 0x2069, 0x193c, 0x6804, 0x9080,
+ 0x193e, 0x2f08, 0x2102, 0x6904, 0x8108, 0x9182, 0x0008, 0x0208,
+ 0x900e, 0x6906, 0x9180, 0x193e, 0x2003, 0x0000, 0x00de, 0x0005,
+ 0x0096, 0x00c6, 0x2060, 0x6014, 0x2048, 0xa8a8, 0x0096, 0x2048,
+ 0x9005, 0x190c, 0x1031, 0x009e, 0xa8ab, 0x0000, 0x080c, 0x0fb1,
+ 0x080c, 0xaf43, 0x00ce, 0x009e, 0x0005, 0x6020, 0x9086, 0x0009,
+ 0x1128, 0x601c, 0xd0c4, 0x0110, 0x9006, 0x0005, 0x9085, 0x0001,
+ 0x0005, 0x6000, 0x9086, 0x0000, 0x0178, 0x6010, 0x9005, 0x0150,
+ 0x00b6, 0x2058, 0x080c, 0x8da7, 0x00be, 0x6013, 0x0000, 0x601b,
+ 0x0000, 0x0010, 0x2c00, 0x0861, 0x0005, 0x2009, 0x1927, 0x210c,
+ 0xd194, 0x0005, 0x00e6, 0x2071, 0x1923, 0x7110, 0xc194, 0xd19c,
+ 0x1118, 0xc185, 0x7007, 0x0000, 0x7112, 0x2001, 0x003b, 0x080c,
+ 0x15fd, 0x00ee, 0x0005, 0x7814, 0xd0bc, 0x1108, 0x0005, 0x7810,
+ 0xc0c5, 0x7812, 0x0cc0, 0x0096, 0x00d6, 0x9006, 0x7006, 0x700e,
+ 0x701a, 0x701e, 0x7022, 0x7016, 0x702a, 0x7026, 0x702f, 0x0000,
+ 0x080c, 0x8bf2, 0x0170, 0x080c, 0x8c27, 0x0158, 0x2900, 0x7002,
+ 0x700a, 0x701a, 0x7013, 0x0001, 0x701f, 0x000a, 0x00de, 0x009e,
+ 0x0005, 0x900e, 0x0cd8, 0x00e6, 0x0096, 0x0086, 0x00d6, 0x00c6,
+ 0x2071, 0x1930, 0x721c, 0x2100, 0x9202, 0x1618, 0x080c, 0x8c27,
+ 0x090c, 0x0dd5, 0x7018, 0x9005, 0x1160, 0x2900, 0x7002, 0x700a,
+ 0x701a, 0x9006, 0x7006, 0x700e, 0xa806, 0xa802, 0x7012, 0x701e,
+ 0x0038, 0x2040, 0xa806, 0x2900, 0xa002, 0x701a, 0xa803, 0x0000,
+ 0x7010, 0x8000, 0x7012, 0x701c, 0x9080, 0x000a, 0x701e, 0x721c,
+ 0x08d0, 0x721c, 0x00ce, 0x00de, 0x008e, 0x009e, 0x00ee, 0x0005,
+ 0x0096, 0x0156, 0x0136, 0x0146, 0x00e6, 0x0126, 0x2091, 0x8000,
+ 0x2071, 0x1930, 0x7300, 0x831f, 0x831e, 0x831e, 0x9384, 0x003f,
+ 0x20e8, 0x939c, 0xffc0, 0x9398, 0x0003, 0x7104, 0x080c, 0x8ba4,
+ 0x810c, 0x2100, 0x9318, 0x8003, 0x2228, 0x2021, 0x0078, 0x9402,
+ 0x9532, 0x0208, 0x2028, 0x2500, 0x8004, 0x20a8, 0x23a0, 0xa001,
+ 0xa001, 0x4005, 0x2508, 0x080c, 0x8bad, 0x2130, 0x7014, 0x9600,
+ 0x7016, 0x2600, 0x711c, 0x9102, 0x701e, 0x7004, 0x9600, 0x2008,
+ 0x9082, 0x000a, 0x1190, 0x7000, 0x2048, 0xa800, 0x9005, 0x1148,
+ 0x2009, 0x0001, 0x0026, 0x080c, 0x8a9b, 0x002e, 0x7000, 0x2048,
+ 0xa800, 0x7002, 0x7007, 0x0000, 0x0008, 0x7106, 0x2500, 0x9212,
+ 0x1904, 0x8ada, 0x012e, 0x00ee, 0x014e, 0x013e, 0x015e, 0x009e,
+ 0x0005, 0x0016, 0x0026, 0x00e6, 0x0126, 0x2091, 0x8000, 0x9580,
+ 0x8c6a, 0x2005, 0x9075, 0x090c, 0x0dd5, 0x080c, 0x8b7f, 0x012e,
+ 0x9580, 0x8c66, 0x2005, 0x9075, 0x090c, 0x0dd5, 0x0156, 0x0136,
+ 0x01c6, 0x0146, 0x01d6, 0x831f, 0x831e, 0x831e, 0x9384, 0x003f,
+ 0x20e0, 0x9384, 0xffc0, 0x9100, 0x2098, 0xa860, 0x20e8, 0xa95c,
+ 0x2c05, 0x9100, 0x20a0, 0x20a9, 0x0002, 0x4003, 0x2e0c, 0x2d00,
+ 0x0002, 0x8b69, 0x8b69, 0x8b6b, 0x8b69, 0x8b6b, 0x8b69, 0x8b69,
+ 0x8b69, 0x8b69, 0x8b69, 0x8b71, 0x8b69, 0x8b71, 0x8b69, 0x8b69,
+ 0x8b69, 0x080c, 0x0dd5, 0x4104, 0x20a9, 0x0002, 0x4002, 0x4003,
+ 0x0028, 0x20a9, 0x0002, 0x4003, 0x4104, 0x4003, 0x01de, 0x014e,
+ 0x01ce, 0x013e, 0x015e, 0x00ee, 0x002e, 0x001e, 0x0005, 0x0096,
+ 0x7014, 0x8001, 0x7016, 0x710c, 0x2110, 0x00f1, 0x810c, 0x9188,
+ 0x0003, 0x7308, 0x8210, 0x9282, 0x000a, 0x1198, 0x7008, 0x2048,
+ 0xa800, 0x9005, 0x0158, 0x0006, 0x080c, 0x8c36, 0x009e, 0xa807,
+ 0x0000, 0x2900, 0x700a, 0x7010, 0x8001, 0x7012, 0x700f, 0x0000,
+ 0x0008, 0x720e, 0x009e, 0x0005, 0x0006, 0x810b, 0x810b, 0x2100,
+ 0x810b, 0x9100, 0x2008, 0x000e, 0x0005, 0x0006, 0x0026, 0x2100,
+ 0x9005, 0x0158, 0x9092, 0x000c, 0x0240, 0x900e, 0x8108, 0x9082,
+ 0x000c, 0x1de0, 0x002e, 0x000e, 0x0005, 0x900e, 0x0cd8, 0x2d00,
+ 0x90b8, 0x0008, 0x2031, 0x8bf0, 0x901e, 0x6808, 0x9005, 0x0108,
+ 0x8318, 0x690c, 0x910a, 0x0248, 0x0140, 0x8318, 0x6810, 0x9112,
+ 0x0220, 0x0118, 0x8318, 0x2208, 0x0cd0, 0x233a, 0x6804, 0xd084,
+ 0x2300, 0x2021, 0x0001, 0x1150, 0x9082, 0x0003, 0x0967, 0x0a67,
+ 0x8420, 0x9082, 0x0007, 0x0967, 0x0a67, 0x0cd0, 0x9082, 0x0002,
+ 0x0967, 0x0a67, 0x8420, 0x9082, 0x0005, 0x0967, 0x0a67, 0x0cd0,
+ 0x6c1a, 0x0005, 0x0096, 0x0046, 0x0126, 0x2091, 0x8000, 0x2b00,
+ 0x9080, 0x8c6e, 0x2005, 0x9005, 0x090c, 0x0dd5, 0x2004, 0x90a0,
+ 0x000a, 0x080c, 0x1018, 0x01d0, 0x2900, 0x7026, 0xa803, 0x0000,
+ 0xa807, 0x0000, 0x080c, 0x1018, 0x0188, 0x7024, 0xa802, 0xa807,
+ 0x0000, 0x2900, 0x7026, 0x94a2, 0x000a, 0x0110, 0x0208, 0x0c90,
+ 0x9085, 0x0001, 0x012e, 0x004e, 0x009e, 0x0005, 0x7024, 0x9005,
+ 0x0dc8, 0x2048, 0xac00, 0x080c, 0x1031, 0x2400, 0x0cc0, 0x0126,
+ 0x2091, 0x8000, 0x7024, 0x2048, 0x9005, 0x0130, 0xa800, 0x7026,
+ 0xa803, 0x0000, 0xa807, 0x0000, 0x012e, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x7024, 0xa802, 0x2900, 0x7026, 0x012e, 0x0005, 0x0096,
+ 0x9e80, 0x0009, 0x2004, 0x9005, 0x0138, 0x2048, 0xa800, 0x0006,
+ 0x080c, 0x1031, 0x000e, 0x0cb8, 0x009e, 0x0005, 0x0096, 0x7008,
+ 0x9005, 0x0138, 0x2048, 0xa800, 0x0006, 0x080c, 0x1031, 0x000e,
+ 0x0cb8, 0x9006, 0x7002, 0x700a, 0x7006, 0x700e, 0x701a, 0x701e,
+ 0x7022, 0x702a, 0x7026, 0x702e, 0x009e, 0x0005, 0x1a63, 0x0000,
+ 0x0000, 0x0000, 0x1930, 0x0000, 0x0000, 0x0000, 0x1888, 0x0000,
+ 0x0000, 0x0000, 0x1877, 0x0000, 0x0000, 0x0000, 0x00e6, 0x00c6,
+ 0x00b6, 0x00a6, 0xa8a8, 0x2040, 0x2071, 0x1877, 0x080c, 0x8d92,
+ 0xa067, 0x0023, 0x6010, 0x905d, 0x0904, 0x8d67, 0xb814, 0xa06e,
+ 0xb910, 0xa172, 0xb9a0, 0xa176, 0x2001, 0x0003, 0xa07e, 0xa834,
+ 0xa082, 0xa07b, 0x0000, 0xa898, 0x9005, 0x0118, 0xa078, 0xc085,
+ 0xa07a, 0x2858, 0x2031, 0x0018, 0xa068, 0x908a, 0x0019, 0x1a0c,
+ 0x0dd5, 0x2020, 0x2050, 0x2940, 0xa864, 0x90bc, 0x00ff, 0x908c,
+ 0x000f, 0x91e0, 0x20ce, 0x2c65, 0x9786, 0x0024, 0x2c05, 0x1590,
+ 0x908a, 0x0036, 0x1a0c, 0x0dd5, 0x9082, 0x001b, 0x0002, 0x8cd2,
+ 0x8cd2, 0x8cd4, 0x8cd2, 0x8cd2, 0x8cd2, 0x8cd6, 0x8cd2, 0x8cd2,
+ 0x8cd2, 0x8cd8, 0x8cd2, 0x8cd2, 0x8cd2, 0x8cda, 0x8cd2, 0x8cd2,
+ 0x8cd2, 0x8cdc, 0x8cd2, 0x8cd2, 0x8cd2, 0x8cde, 0x8cd2, 0x8cd2,
+ 0x8cd2, 0x8ce0, 0x080c, 0x0dd5, 0xa180, 0x04b8, 0xa190, 0x04a8,
+ 0xa1a0, 0x0498, 0xa1b0, 0x0488, 0xa1c0, 0x0478, 0xa1d0, 0x0468,
+ 0xa1e0, 0x0458, 0x908a, 0x0034, 0x1a0c, 0x0dd5, 0x9082, 0x001b,
+ 0x0002, 0x8d04, 0x8d02, 0x8d02, 0x8d02, 0x8d02, 0x8d02, 0x8d06,
+ 0x8d02, 0x8d02, 0x8d02, 0x8d02, 0x8d02, 0x8d08, 0x8d02, 0x8d02,
+ 0x8d02, 0x8d02, 0x8d02, 0x8d0a, 0x8d02, 0x8d02, 0x8d02, 0x8d02,
+ 0x8d02, 0x8d0c, 0x080c, 0x0dd5, 0xa180, 0x0038, 0xa198, 0x0028,
+ 0xa1b0, 0x0018, 0xa1c8, 0x0008, 0xa1e0, 0x2600, 0x0002, 0x8d28,
+ 0x8d2a, 0x8d2c, 0x8d2e, 0x8d30, 0x8d32, 0x8d34, 0x8d36, 0x8d38,
+ 0x8d3a, 0x8d3c, 0x8d3e, 0x8d40, 0x8d42, 0x8d44, 0x8d46, 0x8d48,
+ 0x8d4a, 0x8d4c, 0x8d4e, 0x8d50, 0x8d52, 0x8d54, 0x8d56, 0x8d58,
+ 0x080c, 0x0dd5, 0xb9e2, 0x0468, 0xb9de, 0x0458, 0xb9da, 0x0448,
+ 0xb9d6, 0x0438, 0xb9d2, 0x0428, 0xb9ce, 0x0418, 0xb9ca, 0x0408,
+ 0xb9c6, 0x00f8, 0xb9c2, 0x00e8, 0xb9be, 0x00d8, 0xb9ba, 0x00c8,
+ 0xb9b6, 0x00b8, 0xb9b2, 0x00a8, 0xb9ae, 0x0098, 0xb9aa, 0x0088,
+ 0xb9a6, 0x0078, 0xb9a2, 0x0068, 0xb99e, 0x0058, 0xb99a, 0x0048,
+ 0xb996, 0x0038, 0xb992, 0x0028, 0xb98e, 0x0018, 0xb98a, 0x0008,
+ 0xb986, 0x8631, 0x8421, 0x0130, 0x080c, 0x2086, 0x090c, 0x0dd5,
+ 0x0804, 0x8cac, 0x00ae, 0x00be, 0x00ce, 0x00ee, 0x0005, 0xa86c,
+ 0xa06e, 0xa870, 0xa072, 0xa077, 0x00ff, 0x9006, 0x0804, 0x8c8e,
+ 0x0006, 0x0016, 0x00b6, 0x6010, 0x2058, 0xb810, 0x9005, 0x01b0,
+ 0x2001, 0x1924, 0x2004, 0x9005, 0x0188, 0x2001, 0x1800, 0x2004,
+ 0x9086, 0x0003, 0x1158, 0x0036, 0x0046, 0xbba0, 0x2021, 0x0004,
+ 0x2011, 0x8014, 0x080c, 0x4b7f, 0x004e, 0x003e, 0x00be, 0x001e,
+ 0x000e, 0x0005, 0x9016, 0x710c, 0xa834, 0x910a, 0xa936, 0x7008,
+ 0x9005, 0x0120, 0x8210, 0x910a, 0x0238, 0x0130, 0x7010, 0x8210,
+ 0x910a, 0x0210, 0x0108, 0x0cd8, 0xaa8a, 0xa26a, 0x0005, 0x00f6,
+ 0x00d6, 0x0036, 0x2079, 0x0300, 0x781b, 0x0200, 0x7818, 0xd094,
+ 0x1dd8, 0x781b, 0x0202, 0xa001, 0xa001, 0x7818, 0xd094, 0x1da0,
+ 0xb8ac, 0x9005, 0x01b8, 0x2068, 0x2079, 0x0000, 0x2c08, 0x911e,
+ 0x1118, 0x680c, 0xb8ae, 0x0060, 0x9106, 0x0140, 0x2d00, 0x2078,
+ 0x680c, 0x9005, 0x090c, 0x0dd5, 0x2068, 0x0cb0, 0x6b0c, 0x7b0e,
+ 0x600f, 0x0000, 0x2079, 0x0300, 0x781b, 0x0200, 0x003e, 0x00de,
+ 0x00fe, 0x0005, 0x00e6, 0x00d6, 0x0096, 0x00c6, 0x0036, 0x0126,
+ 0x2091, 0x8000, 0x0156, 0x20a9, 0x01ff, 0x2071, 0x0300, 0x701b,
+ 0x0200, 0x7018, 0xd094, 0x0110, 0x1f04, 0x8de7, 0x701b, 0x0202,
+ 0xa001, 0xa001, 0x7018, 0xd094, 0x1d90, 0xb8ac, 0x9005, 0x01e8,
+ 0x2060, 0x600c, 0xb8ae, 0x6024, 0xc08d, 0x6026, 0x6003, 0x0004,
+ 0x601b, 0x0000, 0x6013, 0x0000, 0x601f, 0x0101, 0x6014, 0x2048,
+ 0xa88b, 0x0000, 0xa8a8, 0xa8ab, 0x0000, 0x904d, 0x090c, 0x0dd5,
+ 0x080c, 0x1031, 0x080c, 0x89a7, 0x0c00, 0x2071, 0x0300, 0x701b,
+ 0x0200, 0x015e, 0x012e, 0x003e, 0x00ce, 0x009e, 0x00de, 0x00ee,
+ 0x0005, 0x00c6, 0x00b6, 0x0016, 0x0006, 0x0156, 0x080c, 0x287c,
+ 0x015e, 0x11b0, 0x080c, 0x6638, 0x190c, 0x0dd5, 0x000e, 0x001e,
+ 0xb912, 0xb816, 0x080c, 0xaeed, 0x0140, 0x2b00, 0x6012, 0x6023,
+ 0x0001, 0x2009, 0x0001, 0x080c, 0xafbe, 0x00be, 0x00ce, 0x0005,
+ 0x000e, 0x001e, 0x0cd0, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c,
+ 0x0dd5, 0x0013, 0x006e, 0x0005, 0x8e5c, 0x8e5c, 0x8e5c, 0x8e5e,
+ 0x8eaf, 0x8e5c, 0x8e5c, 0x8e5c, 0x8f16, 0x8e5c, 0x8f53, 0x8e5c,
+ 0x8e5c, 0x8e5c, 0x8e5c, 0x8e5c, 0x080c, 0x0dd5, 0x9182, 0x0040,
+ 0x0002, 0x8e71, 0x8e71, 0x8e71, 0x8e71, 0x8e71, 0x8e71, 0x8e71,
+ 0x8e71, 0x8e71, 0x8e73, 0x8e88, 0x8e71, 0x8e71, 0x8e71, 0x8e71,
+ 0x8e9b, 0x080c, 0x0dd5, 0x0096, 0x080c, 0x9713, 0x080c, 0x9891,
+ 0x6114, 0x2148, 0xa87b, 0x0000, 0x6010, 0x00b6, 0x2058, 0xb8bb,
+ 0x0500, 0x00be, 0x080c, 0x6b1d, 0x080c, 0xaf43, 0x009e, 0x0005,
+ 0x080c, 0x9713, 0x00d6, 0x6114, 0x080c, 0xcc86, 0x0130, 0x0096,
+ 0x6114, 0x2148, 0x080c, 0x6d17, 0x009e, 0x00de, 0x080c, 0xaf43,
+ 0x080c, 0x9891, 0x0005, 0x080c, 0x9713, 0x080c, 0x321e, 0x6114,
+ 0x0096, 0x2148, 0x080c, 0xcc86, 0x0120, 0xa87b, 0x0029, 0x080c,
+ 0x6d17, 0x009e, 0x080c, 0xaf43, 0x080c, 0x9891, 0x0005, 0x601b,
+ 0x0000, 0x9182, 0x0040, 0x0096, 0x0002, 0x8eca, 0x8eca, 0x8eca,
+ 0x8eca, 0x8eca, 0x8eca, 0x8eca, 0x8eca, 0x8ecc, 0x8eca, 0x8eca,
+ 0x8eca, 0x8f12, 0x8eca, 0x8eca, 0x8eca, 0x8eca, 0x8eca, 0x8eca,
+ 0x8ed3, 0x8eca, 0x080c, 0x0dd5, 0x6114, 0x2148, 0xa938, 0x918e,
+ 0xffff, 0x0904, 0x8f12, 0x6024, 0xd08c, 0x15c0, 0x00e6, 0x6114,
+ 0x2148, 0x080c, 0x8c76, 0x0096, 0xa8a8, 0x2048, 0x080c, 0x6ab5,
+ 0x009e, 0xa8ab, 0x0000, 0x6010, 0x9005, 0x0128, 0x00b6, 0x2058,
+ 0x080c, 0x8da7, 0x00be, 0xae88, 0x00b6, 0x2059, 0x0000, 0x080c,
+ 0x89b0, 0x00be, 0x01e0, 0x2071, 0x193c, 0x080c, 0x89f7, 0x01b8,
+ 0x9086, 0x0001, 0x1128, 0x2001, 0x1946, 0x2004, 0x9005, 0x1178,
+ 0x0096, 0x080c, 0x0fff, 0x2900, 0x009e, 0x0148, 0xa8aa, 0x00f6,
+ 0x2c78, 0x080c, 0x896e, 0x00fe, 0x00ee, 0x009e, 0x0005, 0x080c,
+ 0x89a7, 0x0cd0, 0x080c, 0x8fc0, 0x009e, 0x0005, 0x9182, 0x0040,
+ 0x0096, 0x0002, 0x8f2a, 0x8f2a, 0x8f2a, 0x8f2c, 0x8f2a, 0x8f2a,
+ 0x8f2a, 0x8f51, 0x8f2a, 0x8f2a, 0x8f2a, 0x8f2a, 0x8f2a, 0x8f2a,
+ 0x8f2a, 0x8f2a, 0x080c, 0x0dd5, 0x6003, 0x0003, 0x6106, 0x6014,
+ 0x2048, 0xa8ac, 0xa846, 0xa8b0, 0xa84a, 0xa837, 0x0000, 0xa83b,
+ 0x0000, 0xa884, 0x9092, 0x199a, 0x0210, 0x2001, 0x1999, 0x8003,
+ 0x8013, 0x8213, 0x9210, 0x621a, 0x2c10, 0x080c, 0x1beb, 0x080c,
+ 0x9216, 0x0126, 0x2091, 0x8000, 0x080c, 0x9891, 0x012e, 0x009e,
+ 0x0005, 0x080c, 0x0dd5, 0x080c, 0x9713, 0x080c, 0x9891, 0x6114,
+ 0x2148, 0xa87b, 0x0000, 0x6010, 0x00b6, 0x2058, 0xb8bb, 0x0500,
+ 0x00be, 0x080c, 0x6d17, 0x080c, 0xaf43, 0x009e, 0x0005, 0x6000,
+ 0x908a, 0x0016, 0x1a0c, 0x0dd5, 0x0096, 0x0013, 0x009e, 0x0005,
+ 0x8f80, 0x8f80, 0x8f80, 0x8f82, 0x8f93, 0x8f80, 0x8f80, 0x8f80,
+ 0x8f80, 0x8f80, 0x8f80, 0x8f80, 0x8f80, 0x8f80, 0x8f80, 0x8f80,
+ 0x080c, 0x0dd5, 0x080c, 0xa89b, 0x6114, 0x2148, 0xa87b, 0x0006,
+ 0x6010, 0x00b6, 0x2058, 0xb8bb, 0x0500, 0x00be, 0x080c, 0x6d17,
+ 0x080c, 0xaf43, 0x0005, 0x0461, 0x0005, 0x6000, 0x908a, 0x0016,
+ 0x1a0c, 0x0dd5, 0x0096, 0x0013, 0x009e, 0x0005, 0x8fae, 0x8fae,
+ 0x8fae, 0x8fb0, 0x8fc0, 0x8fae, 0x8fae, 0x8fae, 0x8fae, 0x8fae,
+ 0x8fae, 0x8fae, 0x8fae, 0x8fae, 0x8fae, 0x8fae, 0x080c, 0x0dd5,
+ 0x0036, 0x00e6, 0x2071, 0x19e6, 0x703c, 0x9c06, 0x1120, 0x2019,
+ 0x0000, 0x080c, 0xa6ac, 0x080c, 0xa89b, 0x00ee, 0x003e, 0x0005,
+ 0x6024, 0xd08c, 0x11f0, 0x00f6, 0x00e6, 0x601b, 0x0000, 0x6014,
+ 0x2048, 0x6010, 0x9005, 0x0128, 0x00b6, 0x2058, 0x080c, 0x8da7,
+ 0x00be, 0x2071, 0x193c, 0x080c, 0x89f7, 0x0160, 0x2001, 0x187f,
+ 0x2004, 0xa88a, 0x2031, 0x0000, 0x2c78, 0x080c, 0x896e, 0x00ee,
+ 0x00fe, 0x0005, 0x0096, 0xa88b, 0x0000, 0xa8a8, 0x2048, 0x080c,
+ 0x1031, 0x009e, 0xa8ab, 0x0000, 0x080c, 0x89a7, 0x0c80, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x187a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0126,
+ 0x2091, 0x8000, 0x0036, 0x0046, 0x20a9, 0x0010, 0x9006, 0x8004,
+ 0x2019, 0x0100, 0x231c, 0x93a6, 0x0008, 0x1118, 0x8086, 0x818e,
+ 0x0020, 0x80f6, 0x3e00, 0x81f6, 0x3e08, 0x1208, 0x9200, 0x1f04,
+ 0x9008, 0x93a6, 0x0008, 0x1118, 0x8086, 0x818e, 0x0020, 0x80f6,
+ 0x3e00, 0x81f6, 0x3e08, 0x004e, 0x003e, 0x012e, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x0076, 0x0156, 0x20a9, 0x0010, 0x9005, 0x0510,
+ 0x911a, 0x1600, 0x8213, 0x2039, 0x0100, 0x273c, 0x97be, 0x0008,
+ 0x1110, 0x818d, 0x0010, 0x81f5, 0x3e08, 0x0228, 0x911a, 0x1220,
+ 0x1f04, 0x9032, 0x0028, 0x911a, 0x2308, 0x8210, 0x1f04, 0x9032,
+ 0x0006, 0x3200, 0x9084, 0xefff, 0x2080, 0x000e, 0x015e, 0x007e,
+ 0x012e, 0x0005, 0x0006, 0x3200, 0x9085, 0x1000, 0x0ca8, 0x0126,
+ 0x2091, 0x2800, 0x2079, 0x19e6, 0x012e, 0x00d6, 0x2069, 0x19e6,
+ 0x6803, 0x0005, 0x0156, 0x0146, 0x01d6, 0x20e9, 0x0000, 0x2069,
+ 0x0200, 0x080c, 0xabfe, 0x0401, 0x080c, 0xabe9, 0x00e9, 0x080c,
+ 0xabec, 0x00d1, 0x080c, 0xabef, 0x00b9, 0x080c, 0xabf2, 0x00a1,
+ 0x080c, 0xabf5, 0x0089, 0x080c, 0xabf8, 0x0071, 0x080c, 0xabfb,
+ 0x0059, 0x01de, 0x014e, 0x015e, 0x2069, 0x0004, 0x2d04, 0x9085,
+ 0x8001, 0x206a, 0x00de, 0x0005, 0x20a9, 0x0020, 0x20a1, 0x0240,
+ 0x2001, 0x0000, 0x4004, 0x0005, 0x00c6, 0x6027, 0x0001, 0x7804,
+ 0x9084, 0x0007, 0x0002, 0x90a5, 0x90c9, 0x910a, 0x90ab, 0x90c9,
+ 0x90a5, 0x90a3, 0x90a3, 0x080c, 0x0dd5, 0x080c, 0x8636, 0x080c,
+ 0x9763, 0x00ce, 0x0005, 0x62c0, 0x82ff, 0x1110, 0x00ce, 0x0005,
+ 0x2011, 0x5f16, 0x080c, 0x85b0, 0x7828, 0x9092, 0x00c8, 0x1228,
+ 0x8000, 0x782a, 0x080c, 0x5f56, 0x0c88, 0x62c0, 0x080c, 0xad3a,
+ 0x080c, 0x5f16, 0x7807, 0x0003, 0x7827, 0x0000, 0x782b, 0x0000,
+ 0x0c28, 0x080c, 0x8636, 0x6220, 0xd2a4, 0x0170, 0xd2cc, 0x0160,
+ 0x782b, 0x0000, 0x7824, 0x9065, 0x090c, 0x0dd5, 0x2009, 0x0013,
+ 0x080c, 0xafbe, 0x00ce, 0x0005, 0x00c6, 0x7824, 0x9065, 0x090c,
+ 0x0dd5, 0x7828, 0x9092, 0xc350, 0x12c0, 0x8000, 0x782a, 0x00ce,
+ 0x080c, 0x2be3, 0x0278, 0x00c6, 0x7924, 0x2160, 0x6010, 0x906d,
+ 0x090c, 0x0dd5, 0x7807, 0x0000, 0x7827, 0x0000, 0x00ce, 0x080c,
+ 0x9763, 0x0c00, 0x080c, 0xa332, 0x08e8, 0x2011, 0x0130, 0x2214,
+ 0x080c, 0xad3a, 0x080c, 0xeb8e, 0x2009, 0x0014, 0x080c, 0xafbe,
+ 0x00ce, 0x0880, 0x2001, 0x1a02, 0x2003, 0x0000, 0x62c0, 0x82ff,
+ 0x1160, 0x782b, 0x0000, 0x7824, 0x9065, 0x090c, 0x0dd5, 0x2009,
+ 0x0013, 0x080c, 0xb010, 0x00ce, 0x0005, 0x00b6, 0x00c6, 0x00d6,
+ 0x7824, 0x9005, 0x090c, 0x0dd5, 0x7828, 0x9092, 0xc350, 0x1648,
+ 0x8000, 0x782a, 0x00de, 0x00ce, 0x00be, 0x080c, 0x2be3, 0x02f0,
+ 0x00b6, 0x00c6, 0x00d6, 0x781c, 0x905d, 0x090c, 0x0dd5, 0xb800,
+ 0xc0dc, 0xb802, 0x7924, 0x2160, 0x080c, 0xaf43, 0xb93c, 0x81ff,
+ 0x090c, 0x0dd5, 0x8109, 0xb93e, 0x7807, 0x0000, 0x7827, 0x0000,
+ 0x00de, 0x00ce, 0x00be, 0x080c, 0x9763, 0x0868, 0x080c, 0xa332,
+ 0x0850, 0x2011, 0x0130, 0x2214, 0x080c, 0xad3a, 0x080c, 0xeb8e,
+ 0x7824, 0x9065, 0x2009, 0x0014, 0x080c, 0xafbe, 0x00de, 0x00ce,
+ 0x00be, 0x0804, 0x911b, 0x00c6, 0x2001, 0x009b, 0x2004, 0xd0fc,
+ 0x190c, 0x1ef2, 0x6024, 0x6027, 0x0002, 0xd0f4, 0x15b8, 0x62c8,
+ 0x60c4, 0x9205, 0x1170, 0x783c, 0x9065, 0x0130, 0x2009, 0x0049,
+ 0x080c, 0xafbe, 0x00ce, 0x0005, 0x2011, 0x1a05, 0x2013, 0x0000,
+ 0x0cc8, 0x793c, 0x81ff, 0x0dc0, 0x7944, 0x9192, 0x7530, 0x1628,
+ 0x8108, 0x7946, 0x793c, 0x9188, 0x0008, 0x210c, 0x918e, 0x0006,
+ 0x1138, 0x6014, 0x9084, 0x1984, 0x9085, 0x0012, 0x6016, 0x0c10,
+ 0x793c, 0x9188, 0x0008, 0x210c, 0x918e, 0x0009, 0x0d90, 0x6014,
+ 0x9084, 0x1984, 0x9085, 0x0016, 0x6016, 0x08a0, 0x793c, 0x2160,
+ 0x2009, 0x004a, 0x080c, 0xafbe, 0x0868, 0x7848, 0xc085, 0x784a,
+ 0x0848, 0x0006, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0x19e6, 0x6020, 0x8000, 0x6022, 0x6010,
+ 0x9005, 0x0148, 0x9080, 0x0003, 0x2102, 0x6112, 0x012e, 0x00ce,
+ 0x001e, 0x000e, 0x0005, 0x6116, 0x6112, 0x0cc0, 0x00d6, 0x2069,
+ 0x19e6, 0xb800, 0xd0d4, 0x0168, 0x6820, 0x8000, 0x6822, 0x9086,
+ 0x0001, 0x1110, 0x2b00, 0x681e, 0x00de, 0x0804, 0x9763, 0x00de,
+ 0x0005, 0xc0d5, 0xb802, 0x6818, 0x9005, 0x0168, 0xb856, 0xb85b,
+ 0x0000, 0x0086, 0x0006, 0x2b00, 0x681a, 0x008e, 0xa05a, 0x008e,
+ 0x2069, 0x19e6, 0x0c08, 0xb856, 0xb85a, 0x2b00, 0x681a, 0x681e,
+ 0x08d8, 0x0006, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0x19e6, 0x6020, 0x8000, 0x6022, 0x6008,
+ 0x9005, 0x0148, 0x9080, 0x0003, 0x2102, 0x610a, 0x012e, 0x00ce,
+ 0x001e, 0x000e, 0x0005, 0x610e, 0x610a, 0x0cc0, 0x00c6, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0x19e6, 0x6034, 0x9005, 0x0130, 0x9080,
+ 0x0003, 0x2102, 0x6136, 0x00ce, 0x0005, 0x613a, 0x6136, 0x00ce,
+ 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x00b6, 0x0096, 0x0076,
+ 0x0066, 0x0056, 0x0036, 0x0026, 0x0016, 0x0006, 0x0126, 0x902e,
+ 0x2071, 0x19e6, 0x7638, 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff,
+ 0x0904, 0x92a5, 0x6010, 0x2058, 0xb8a0, 0x9206, 0x1904, 0x92a0,
+ 0x87ff, 0x0120, 0x6054, 0x9106, 0x1904, 0x92a0, 0x703c, 0x9c06,
+ 0x1178, 0x0036, 0x2019, 0x0001, 0x080c, 0xa6ac, 0x7033, 0x0000,
+ 0x9006, 0x703e, 0x7042, 0x7046, 0x704a, 0x003e, 0x2029, 0x0001,
+ 0x7038, 0x9c36, 0x1110, 0x660c, 0x763a, 0x7034, 0x9c36, 0x1140,
+ 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x7036, 0x0010, 0x7037, 0x0000,
+ 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x600f, 0x0000, 0x080c, 0xcc86, 0x01f0, 0x6014, 0x2048, 0x6020,
+ 0x9086, 0x0003, 0x15b8, 0x6004, 0x9086, 0x0040, 0x090c, 0xa88b,
+ 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x0016, 0x0036, 0x0076,
+ 0x080c, 0xcf7c, 0x080c, 0xea94, 0x080c, 0x6d17, 0x007e, 0x003e,
+ 0x001e, 0x080c, 0xce71, 0x080c, 0xaf74, 0x00ce, 0x0804, 0x923f,
+ 0x2c78, 0x600c, 0x2060, 0x0804, 0x923f, 0x85ff, 0x0120, 0x0036,
+ 0x080c, 0x9891, 0x003e, 0x012e, 0x000e, 0x001e, 0x002e, 0x003e,
+ 0x005e, 0x006e, 0x007e, 0x009e, 0x00be, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x6020, 0x9086, 0x0006, 0x1158, 0x0016, 0x0036,
+ 0x0076, 0x080c, 0xea94, 0x080c, 0xe6dd, 0x007e, 0x003e, 0x001e,
+ 0x0890, 0x6020, 0x9086, 0x0009, 0x1168, 0xa87b, 0x0006, 0x0016,
+ 0x0036, 0x0076, 0x080c, 0x6d17, 0x080c, 0xaf43, 0x007e, 0x003e,
+ 0x001e, 0x0818, 0x6020, 0x9086, 0x000a, 0x0904, 0x928a, 0x0804,
+ 0x9283, 0x0006, 0x0066, 0x0096, 0x00c6, 0x00d6, 0x00f6, 0x9036,
+ 0x0126, 0x2091, 0x8000, 0x2079, 0x19e6, 0x7838, 0x9065, 0x0904,
+ 0x9336, 0x600c, 0x0006, 0x600f, 0x0000, 0x783c, 0x9c06, 0x1168,
+ 0x0036, 0x2019, 0x0001, 0x080c, 0xa6ac, 0x7833, 0x0000, 0x901e,
+ 0x7b3e, 0x7b42, 0x7b46, 0x7b4a, 0x003e, 0x080c, 0xcc86, 0x0548,
+ 0x6014, 0x2048, 0x6020, 0x9086, 0x0003, 0x1590, 0x3e08, 0x918e,
+ 0x0002, 0x1188, 0x6010, 0x9005, 0x0170, 0x00b6, 0x2058, 0xb800,
+ 0x00be, 0xd0bc, 0x0140, 0x6040, 0x9005, 0x11a8, 0x2001, 0x1987,
+ 0x2004, 0x6042, 0x0080, 0x6004, 0x9086, 0x0040, 0x090c, 0xa88b,
+ 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0x6d0b, 0x080c,
+ 0xce71, 0x080c, 0xaf74, 0x000e, 0x0804, 0x92ee, 0x7e3a, 0x7e36,
+ 0x012e, 0x00fe, 0x00de, 0x00ce, 0x009e, 0x006e, 0x000e, 0x0005,
+ 0x6020, 0x9086, 0x0006, 0x1118, 0x080c, 0xe6dd, 0x0c50, 0x6020,
+ 0x9086, 0x0009, 0x1130, 0xab7a, 0x080c, 0x6d17, 0x080c, 0xaf43,
+ 0x0c10, 0x6020, 0x9086, 0x000a, 0x09a8, 0x0868, 0x0016, 0x0026,
+ 0x0086, 0x9046, 0x0099, 0x080c, 0x9441, 0x008e, 0x002e, 0x001e,
+ 0x0005, 0x00f6, 0x0126, 0x2079, 0x19e6, 0x2091, 0x8000, 0x080c,
+ 0x94d8, 0x080c, 0x9568, 0x012e, 0x00fe, 0x0005, 0x00b6, 0x0096,
+ 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0016, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x19e6, 0x7614, 0x2660, 0x2678, 0x8cff,
+ 0x0904, 0x9406, 0x6010, 0x2058, 0xb8a0, 0x9206, 0x1904, 0x9401,
+ 0x88ff, 0x0120, 0x6054, 0x9106, 0x1904, 0x9401, 0x7024, 0x9c06,
+ 0x1568, 0x2069, 0x0100, 0x6820, 0xd0a4, 0x0110, 0xd0cc, 0x1508,
+ 0x080c, 0x8636, 0x080c, 0xa356, 0x68c3, 0x0000, 0x080c, 0xa88b,
+ 0x7027, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000,
+ 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x9006, 0x080c, 0x2d4e,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x0028, 0x6003, 0x0009, 0x630a, 0x0804, 0x9401, 0x7014, 0x9c36,
+ 0x1110, 0x660c, 0x7616, 0x7010, 0x9c36, 0x1140, 0x2c00, 0x9f36,
+ 0x0118, 0x2f00, 0x7012, 0x0010, 0x7013, 0x0000, 0x660c, 0x0066,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x6014, 0x2048, 0x080c, 0xcc86, 0x01e8, 0x6020, 0x9086, 0x0003,
+ 0x1580, 0x080c, 0xce8e, 0x1118, 0x080c, 0xb905, 0x0098, 0xa867,
+ 0x0103, 0xab7a, 0xa877, 0x0000, 0x0016, 0x0036, 0x0086, 0x080c,
+ 0xcf7c, 0x080c, 0xea94, 0x080c, 0x6d17, 0x008e, 0x003e, 0x001e,
+ 0x080c, 0xce71, 0x080c, 0xaf74, 0x080c, 0xa761, 0x00ce, 0x0804,
+ 0x937f, 0x2c78, 0x600c, 0x2060, 0x0804, 0x937f, 0x012e, 0x000e,
+ 0x001e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x009e, 0x00be,
+ 0x0005, 0x6020, 0x9086, 0x0006, 0x1158, 0x0016, 0x0036, 0x0086,
+ 0x080c, 0xea94, 0x080c, 0xe6dd, 0x008e, 0x003e, 0x001e, 0x08d0,
+ 0x080c, 0xb905, 0x6020, 0x9086, 0x0002, 0x1160, 0x6004, 0x0006,
+ 0x9086, 0x0085, 0x000e, 0x0904, 0x93e7, 0x9086, 0x008b, 0x0904,
+ 0x93e7, 0x0840, 0x6020, 0x9086, 0x0005, 0x1920, 0x6004, 0x0006,
+ 0x9086, 0x0085, 0x000e, 0x09c8, 0x9086, 0x008b, 0x09b0, 0x0804,
+ 0x93fa, 0x00b6, 0x00a6, 0x0096, 0x00c6, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x9280, 0x1000, 0x2004, 0x905d, 0x0904, 0x94d1, 0x00f6,
+ 0x00e6, 0x00d6, 0x0066, 0x2071, 0x19e6, 0xbe54, 0x7018, 0x9b06,
+ 0x1108, 0x761a, 0x701c, 0x9b06, 0x1130, 0x86ff, 0x1118, 0x7018,
+ 0x701e, 0x0008, 0x761e, 0xb858, 0x904d, 0x0108, 0xae56, 0x96d5,
+ 0x0000, 0x0110, 0x2900, 0xb05a, 0xb857, 0x0000, 0xb85b, 0x0000,
+ 0xb800, 0xc0d4, 0xc0dc, 0xb802, 0x080c, 0x65cb, 0x0904, 0x94cd,
+ 0x7624, 0x86ff, 0x0904, 0x94bc, 0x9680, 0x0005, 0x2004, 0x9906,
+ 0x15d8, 0x00d6, 0x2069, 0x0100, 0x68c0, 0x9005, 0x0560, 0x080c,
+ 0x8636, 0x080c, 0xa356, 0x68c3, 0x0000, 0x080c, 0xa88b, 0x7027,
+ 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138,
+ 0x2001, 0x0100, 0x080c, 0x2d4e, 0x9006, 0x080c, 0x2d4e, 0x2069,
+ 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x00de,
+ 0x00c6, 0xb83c, 0x9005, 0x0110, 0x8001, 0xb83e, 0x2660, 0x080c,
+ 0xaf74, 0x00ce, 0x0048, 0x00de, 0x00c6, 0x2660, 0x6003, 0x0009,
+ 0x630a, 0x00ce, 0x0804, 0x9474, 0x89ff, 0x0158, 0xa867, 0x0103,
+ 0xab7a, 0xa877, 0x0000, 0x080c, 0xcf7c, 0x080c, 0xea94, 0x080c,
+ 0x6d17, 0x080c, 0xa761, 0x0804, 0x9474, 0x006e, 0x00de, 0x00ee,
+ 0x00fe, 0x012e, 0x000e, 0x00ce, 0x009e, 0x00ae, 0x00be, 0x0005,
+ 0x0096, 0x0006, 0x0066, 0x00c6, 0x00d6, 0x9036, 0x7814, 0x9065,
+ 0x0904, 0x953b, 0x600c, 0x0006, 0x600f, 0x0000, 0x7824, 0x9c06,
+ 0x1580, 0x2069, 0x0100, 0x6820, 0xd0a4, 0x0110, 0xd0cc, 0x1508,
+ 0x080c, 0x8636, 0x080c, 0xa356, 0x68c3, 0x0000, 0x080c, 0xa88b,
+ 0x7827, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000,
+ 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x9006, 0x080c, 0x2d4e,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x0040, 0x080c, 0x69a4, 0x1520, 0x6003, 0x0009, 0x630a, 0x2c30,
+ 0x00f8, 0x6014, 0x2048, 0x080c, 0xcc84, 0x01b0, 0x6020, 0x9086,
+ 0x0003, 0x1508, 0x080c, 0xce8e, 0x1118, 0x080c, 0xb905, 0x0060,
+ 0x080c, 0x69a4, 0x1168, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000,
+ 0x080c, 0x6d17, 0x080c, 0xce71, 0x080c, 0xaf74, 0x080c, 0xa761,
+ 0x000e, 0x0804, 0x94df, 0x7e16, 0x7e12, 0x00de, 0x00ce, 0x006e,
+ 0x000e, 0x009e, 0x0005, 0x6020, 0x9086, 0x0006, 0x1118, 0x080c,
+ 0xe6dd, 0x0c50, 0x080c, 0xb905, 0x6020, 0x9086, 0x0002, 0x1150,
+ 0x6004, 0x0006, 0x9086, 0x0085, 0x000e, 0x0990, 0x9086, 0x008b,
+ 0x0978, 0x08d0, 0x6020, 0x9086, 0x0005, 0x19b0, 0x6004, 0x0006,
+ 0x9086, 0x0085, 0x000e, 0x0d18, 0x9086, 0x008b, 0x0d00, 0x0860,
+ 0x0006, 0x0066, 0x0096, 0x00b6, 0x00c6, 0x00d6, 0x7818, 0x905d,
+ 0x0904, 0x95e8, 0xb854, 0x0006, 0x9006, 0xb856, 0xb85a, 0xb800,
+ 0xc0d4, 0xc0dc, 0xb802, 0x080c, 0x65cb, 0x0904, 0x95e5, 0x7e24,
+ 0x86ff, 0x0904, 0x95d8, 0x9680, 0x0005, 0x2004, 0x9906, 0x1904,
+ 0x95d8, 0x00d6, 0x2069, 0x0100, 0x68c0, 0x9005, 0x0904, 0x95cf,
+ 0x080c, 0x8636, 0x080c, 0xa356, 0x68c3, 0x0000, 0x080c, 0xa88b,
+ 0x7827, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000,
+ 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x9006, 0x080c, 0x2d4e,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x00de, 0x00c6, 0x3e08, 0x918e, 0x0002, 0x1168, 0xb800, 0xd0bc,
+ 0x0150, 0x9680, 0x0010, 0x200c, 0x81ff, 0x1518, 0x2009, 0x1987,
+ 0x210c, 0x2102, 0x00f0, 0xb83c, 0x9005, 0x0110, 0x8001, 0xb83e,
+ 0x2660, 0x600f, 0x0000, 0x080c, 0xaf74, 0x00ce, 0x0048, 0x00de,
+ 0x00c6, 0x2660, 0x6003, 0x0009, 0x630a, 0x00ce, 0x0804, 0x957b,
+ 0x89ff, 0x0138, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c,
+ 0x6d17, 0x080c, 0xa761, 0x0804, 0x957b, 0x000e, 0x0804, 0x956f,
+ 0x781e, 0x781a, 0x00de, 0x00ce, 0x00be, 0x009e, 0x006e, 0x000e,
+ 0x0005, 0x00e6, 0x00d6, 0x0096, 0x0066, 0xb800, 0xd0dc, 0x01a0,
+ 0xb84c, 0x904d, 0x0188, 0xa878, 0x9606, 0x1170, 0x2071, 0x19e6,
+ 0x7024, 0x9035, 0x0148, 0x9080, 0x0005, 0x2004, 0x9906, 0x1120,
+ 0xb800, 0xc0dc, 0xb802, 0x0029, 0x006e, 0x009e, 0x00de, 0x00ee,
+ 0x0005, 0x00f6, 0x2079, 0x0100, 0x78c0, 0x9005, 0x1138, 0x00c6,
+ 0x2660, 0x6003, 0x0009, 0x630a, 0x00ce, 0x04b8, 0x080c, 0xa356,
+ 0x78c3, 0x0000, 0x080c, 0xa88b, 0x7027, 0x0000, 0x0036, 0x2079,
+ 0x0140, 0x7b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c,
+ 0x2d4e, 0x9006, 0x080c, 0x2d4e, 0x2079, 0x0100, 0x7824, 0xd084,
+ 0x0110, 0x7827, 0x0001, 0x080c, 0xa88b, 0x003e, 0x080c, 0x65cb,
+ 0x00c6, 0xb83c, 0x9005, 0x0110, 0x8001, 0xb83e, 0x2660, 0x080c,
+ 0xaf43, 0x00ce, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c,
+ 0xcf7c, 0x080c, 0x6d17, 0x080c, 0xa761, 0x00fe, 0x0005, 0x00b6,
+ 0x00e6, 0x00c6, 0x2011, 0x0101, 0x2204, 0xc0c4, 0x2012, 0x2001,
+ 0x180c, 0x2014, 0xc2e4, 0x2202, 0x2071, 0x19e6, 0x7004, 0x9084,
+ 0x0007, 0x0002, 0x9674, 0x9678, 0x9696, 0x96bf, 0x96fd, 0x9674,
+ 0x968f, 0x9672, 0x080c, 0x0dd5, 0x00ce, 0x00ee, 0x00be, 0x0005,
+ 0x7024, 0x9065, 0x0148, 0x7020, 0x8001, 0x7022, 0x600c, 0x9015,
+ 0x0158, 0x7216, 0x600f, 0x0000, 0x7007, 0x0000, 0x7027, 0x0000,
+ 0x00ce, 0x00ee, 0x00be, 0x0005, 0x7216, 0x7212, 0x0ca8, 0x7007,
+ 0x0000, 0x7027, 0x0000, 0x7020, 0x9005, 0x0070, 0x6010, 0x2058,
+ 0x080c, 0x65cb, 0xb800, 0xc0dc, 0xb802, 0x7007, 0x0000, 0x7027,
+ 0x0000, 0x7020, 0x8001, 0x7022, 0x1148, 0x2001, 0x180c, 0x2014,
+ 0xd2ec, 0x1180, 0x00ce, 0x00ee, 0x00be, 0x0005, 0xb854, 0x9015,
+ 0x0120, 0x721e, 0x080c, 0x9763, 0x0ca8, 0x7218, 0x721e, 0x080c,
+ 0x9763, 0x0c80, 0xc2ec, 0x2202, 0x080c, 0x9891, 0x0c58, 0x7024,
+ 0x9065, 0x05b8, 0x700c, 0x9c06, 0x1160, 0x080c, 0xa761, 0x600c,
+ 0x9015, 0x0120, 0x720e, 0x600f, 0x0000, 0x0448, 0x720e, 0x720a,
+ 0x0430, 0x7014, 0x9c06, 0x1160, 0x080c, 0xa761, 0x600c, 0x9015,
+ 0x0120, 0x7216, 0x600f, 0x0000, 0x00d0, 0x7216, 0x7212, 0x00b8,
+ 0x6020, 0x9086, 0x0003, 0x1198, 0x6010, 0x2058, 0x080c, 0x65cb,
+ 0xb800, 0xc0dc, 0xb802, 0x080c, 0xa761, 0x701c, 0x9065, 0x0138,
+ 0xb854, 0x9015, 0x0110, 0x721e, 0x0010, 0x7218, 0x721e, 0x7027,
+ 0x0000, 0x00ce, 0x00ee, 0x00be, 0x0005, 0x7024, 0x9065, 0x0140,
+ 0x080c, 0xa761, 0x600c, 0x9015, 0x0158, 0x720e, 0x600f, 0x0000,
+ 0x080c, 0xa88b, 0x7027, 0x0000, 0x00ce, 0x00ee, 0x00be, 0x0005,
+ 0x720e, 0x720a, 0x0ca8, 0x00d6, 0x2069, 0x19e6, 0x6830, 0x9084,
+ 0x0003, 0x0002, 0x9720, 0x9722, 0x9746, 0x971e, 0x080c, 0x0dd5,
+ 0x00de, 0x0005, 0x00c6, 0x6840, 0x9086, 0x0001, 0x01b8, 0x683c,
+ 0x9065, 0x0130, 0x600c, 0x9015, 0x0170, 0x6a3a, 0x600f, 0x0000,
+ 0x6833, 0x0000, 0x683f, 0x0000, 0x2011, 0x1a05, 0x2013, 0x0000,
+ 0x00ce, 0x00de, 0x0005, 0x683a, 0x6836, 0x0c90, 0x6843, 0x0000,
+ 0x6838, 0x9065, 0x0d68, 0x6003, 0x0003, 0x0c50, 0x00c6, 0x9006,
+ 0x6842, 0x6846, 0x684a, 0x683c, 0x9065, 0x0160, 0x600c, 0x9015,
+ 0x0130, 0x6a3a, 0x600f, 0x0000, 0x683f, 0x0000, 0x0018, 0x683e,
+ 0x683a, 0x6836, 0x00ce, 0x00de, 0x0005, 0x2001, 0x180c, 0x200c,
+ 0xc1e5, 0x2102, 0x0005, 0x2001, 0x180c, 0x200c, 0xd1ec, 0x0120,
+ 0xc1ec, 0x2102, 0x080c, 0x9891, 0x2001, 0x19f2, 0x2004, 0x9086,
+ 0x0001, 0x0d58, 0x00d6, 0x2069, 0x19e6, 0x6804, 0x9084, 0x0007,
+ 0x0006, 0x9005, 0x11c8, 0x2001, 0x1837, 0x2004, 0x9084, 0x0028,
+ 0x1198, 0x2001, 0x197b, 0x2004, 0x9086, 0xaaaa, 0x0168, 0x2001,
+ 0x188b, 0x2004, 0xd08c, 0x1118, 0xd084, 0x1118, 0x0028, 0x080c,
+ 0x9891, 0x000e, 0x00de, 0x0005, 0x000e, 0x0002, 0x97a0, 0x985f,
+ 0x985f, 0x985f, 0x985f, 0x9861, 0x985f, 0x979e, 0x080c, 0x0dd5,
+ 0x6820, 0x9005, 0x1110, 0x00de, 0x0005, 0x00c6, 0x680c, 0x9065,
+ 0x0520, 0x6114, 0x0096, 0x2148, 0xa964, 0x009e, 0x918c, 0x00ff,
+ 0x918e, 0x0035, 0x1180, 0x2009, 0x1837, 0x210c, 0x918c, 0x0028,
+ 0x1150, 0x080c, 0x743e, 0x0138, 0x0006, 0x2009, 0x188b, 0x2104,
+ 0xc095, 0x200a, 0x000e, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000,
+ 0x080c, 0x993a, 0x00ce, 0x00de, 0x0005, 0x6814, 0x9065, 0x0150,
+ 0x6807, 0x0001, 0x6826, 0x682b, 0x0000, 0x080c, 0x993a, 0x00ce,
+ 0x00de, 0x0005, 0x00b6, 0x00e6, 0x6a1c, 0x92dd, 0x0000, 0x0904,
+ 0x9849, 0xb84c, 0x900d, 0x0118, 0xb888, 0x9005, 0x01a0, 0xb854,
+ 0x905d, 0x0120, 0x920e, 0x0904, 0x9849, 0x0028, 0x6818, 0x920e,
+ 0x0904, 0x9849, 0x2058, 0xb84c, 0x900d, 0x0d88, 0xb888, 0x9005,
+ 0x1d70, 0x2b00, 0x681e, 0xbb3c, 0xb838, 0x9302, 0x1e40, 0x080c,
+ 0xaf1a, 0x0904, 0x9849, 0x8318, 0xbb3e, 0x6116, 0x2b10, 0x6212,
+ 0x0096, 0x2148, 0xa880, 0x9084, 0x00ff, 0x605e, 0xa883, 0x0000,
+ 0xa884, 0x009e, 0x908a, 0x199a, 0x0210, 0x2001, 0x1999, 0x8003,
+ 0x801b, 0x831b, 0x9318, 0x631a, 0x6114, 0x0096, 0x2148, 0xa964,
+ 0x009e, 0x918c, 0x00ff, 0x918e, 0x0048, 0x0538, 0x00f6, 0x2c78,
+ 0x2061, 0x0100, 0xbac0, 0x629a, 0x2069, 0x0200, 0x2071, 0x0240,
+ 0x080c, 0x9e91, 0x2069, 0x19e6, 0xbb00, 0xc3dd, 0xbb02, 0x6807,
+ 0x0002, 0x2f18, 0x6b26, 0x682b, 0x0000, 0x7823, 0x0003, 0x7803,
+ 0x0001, 0x7807, 0x0040, 0x00fe, 0x00ee, 0x00be, 0x00ce, 0x00de,
+ 0x0005, 0x00ee, 0x00be, 0x00ce, 0x0cd0, 0x6807, 0x0006, 0x2c18,
+ 0x6b26, 0x6820, 0x8001, 0x6822, 0x682b, 0x0000, 0x080c, 0x65cb,
+ 0x080c, 0xad5a, 0x00ee, 0x00be, 0x00ce, 0x00de, 0x0005, 0x00de,
+ 0x0005, 0x00c6, 0x680c, 0x9065, 0x0508, 0x6114, 0x0096, 0x2148,
+ 0xa964, 0x009e, 0x918c, 0x00ff, 0x918e, 0x0035, 0x1180, 0x2009,
+ 0x1837, 0x210c, 0x918c, 0x0028, 0x1150, 0x080c, 0x743e, 0x0138,
+ 0x0006, 0x2009, 0x188b, 0x2104, 0xc095, 0x200a, 0x000e, 0x6807,
+ 0x0004, 0x6826, 0x682b, 0x0000, 0x080c, 0x993a, 0x00ce, 0x00de,
+ 0x0005, 0x2001, 0x180c, 0x2014, 0xc2ed, 0x2202, 0x00de, 0x00fe,
+ 0x0005, 0x00f6, 0x00d6, 0x2069, 0x19e6, 0x6830, 0x9086, 0x0000,
+ 0x1570, 0x2001, 0x180c, 0x2014, 0xd2e4, 0x0130, 0xc2e4, 0x2202,
+ 0x080c, 0x9772, 0x2069, 0x19e6, 0x2001, 0x180c, 0x200c, 0xd1c4,
+ 0x1508, 0x6838, 0x907d, 0x01d8, 0x6a04, 0x9296, 0x0000, 0x1904,
+ 0x992e, 0x7920, 0x918e, 0x0009, 0x0568, 0x6833, 0x0001, 0x683e,
+ 0x6847, 0x0000, 0x684b, 0x0000, 0x0126, 0x00f6, 0x2091, 0x2400,
+ 0x002e, 0x080c, 0x1c84, 0x1158, 0x012e, 0x080c, 0xa1b3, 0x00de,
+ 0x00fe, 0x0005, 0xc1c4, 0x2102, 0x080c, 0x74ee, 0x08d0, 0x012e,
+ 0x6843, 0x0000, 0x7803, 0x0002, 0x780c, 0x9015, 0x0140, 0x6a3a,
+ 0x780f, 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x0c40, 0x683a,
+ 0x6836, 0x0cc0, 0x7908, 0xd1fc, 0x1198, 0x6833, 0x0001, 0x683e,
+ 0x6847, 0x0000, 0x684b, 0x0000, 0x0126, 0x00f6, 0x2091, 0x2400,
+ 0x002e, 0x080c, 0x1c84, 0x19d8, 0x012e, 0x080c, 0xa134, 0x0878,
+ 0x2001, 0x1837, 0x2004, 0x9084, 0x0028, 0x1188, 0x2001, 0x197b,
+ 0x2004, 0x9086, 0xaaaa, 0x0158, 0x2001, 0x19e7, 0x2004, 0x9005,
+ 0x11f0, 0x2001, 0x188b, 0x200c, 0xc185, 0xc18c, 0x2102, 0x2f00,
+ 0x6833, 0x0001, 0x683e, 0x6847, 0x0000, 0x684b, 0x0000, 0x0126,
+ 0x00f6, 0x2091, 0x2400, 0x002e, 0x080c, 0x1c84, 0x1904, 0x98cf,
+ 0x012e, 0x6a3c, 0x2278, 0x080c, 0xa0be, 0x0804, 0x98c7, 0x2011,
+ 0x188b, 0x2204, 0xc08d, 0x2012, 0x0804, 0x98c7, 0x6a04, 0x9296,
+ 0x0006, 0x1904, 0x9889, 0x6a30, 0x9296, 0x0000, 0x0904, 0x98b1,
+ 0x0804, 0x9889, 0x6020, 0x9084, 0x000f, 0x000b, 0x0005, 0x994e,
+ 0x9953, 0x9dc1, 0x9e5a, 0x9953, 0x9dc1, 0x9e5a, 0x994e, 0x9953,
+ 0x994e, 0x994e, 0x994e, 0x994e, 0x994e, 0x994e, 0x080c, 0x9657,
+ 0x080c, 0x9763, 0x0005, 0x00b6, 0x0156, 0x0136, 0x0146, 0x01c6,
+ 0x01d6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2069, 0x0200, 0x2071,
+ 0x0240, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0dd5, 0x6110, 0x2158,
+ 0xb9c0, 0x2c78, 0x2061, 0x0100, 0x619a, 0x908a, 0x0040, 0x1a04,
+ 0x99bf, 0x005b, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x01de, 0x01ce,
+ 0x014e, 0x013e, 0x015e, 0x00be, 0x0005, 0x9b44, 0x9b7f, 0x9ba8,
+ 0x9c50, 0x9c72, 0x9c78, 0x9c85, 0x9c8d, 0x9c99, 0x9c9f, 0x9cb0,
+ 0x9c9f, 0x9d08, 0x9c8d, 0x9d14, 0x9d1a, 0x9c99, 0x9d1a, 0x9d26,
+ 0x99bd, 0x99bd, 0x99bd, 0x99bd, 0x99bd, 0x99bd, 0x99bd, 0x99bd,
+ 0x99bd, 0x99bd, 0x99bd, 0xa563, 0xa586, 0xa597, 0xa5b7, 0xa5e9,
+ 0x9c85, 0x99bd, 0x9c85, 0x9c9f, 0x99bd, 0x9ba8, 0x9c50, 0x99bd,
+ 0xa982, 0x9c9f, 0x99bd, 0xa99e, 0x9c9f, 0x99bd, 0x9c99, 0x9b3e,
+ 0x99e0, 0x99bd, 0xa9ba, 0xaa27, 0xab02, 0x99bd, 0xab0f, 0x9c82,
+ 0xab3a, 0x99bd, 0xa5f3, 0xab67, 0x99bd, 0x080c, 0x0dd5, 0x2100,
+ 0x005b, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x01de, 0x01ce, 0x014e,
+ 0x013e, 0x015e, 0x00be, 0x0005, 0xac02, 0xacb4, 0x99de, 0x9a07,
+ 0x9ab3, 0x9abe, 0x99de, 0x9c85, 0x99de, 0x9b05, 0x9b11, 0x9a22,
+ 0x99de, 0x9a3d, 0x9a71, 0xae21, 0xae66, 0x9c9f, 0x080c, 0x0dd5,
+ 0x00d6, 0x0096, 0x080c, 0x9d39, 0x7003, 0x2414, 0x7007, 0x0018,
+ 0x700b, 0x0800, 0x7814, 0x2048, 0xa83c, 0x700e, 0xa850, 0x7022,
+ 0xa854, 0x7026, 0x60c3, 0x0018, 0x080c, 0xa32a, 0x009e, 0x00de,
+ 0x0005, 0x7810, 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x080c, 0xaead,
+ 0x1118, 0x9084, 0xff80, 0x0110, 0x9085, 0x0001, 0x0005, 0x00d6,
+ 0x0096, 0x080c, 0x9d39, 0x7003, 0x0500, 0x7814, 0x2048, 0xa874,
+ 0x700a, 0xa878, 0x700e, 0xa87c, 0x7012, 0xa880, 0x7016, 0xa884,
+ 0x701a, 0xa888, 0x701e, 0x60c3, 0x0010, 0x080c, 0xa32a, 0x009e,
+ 0x00de, 0x0005, 0x00d6, 0x0096, 0x080c, 0x9d39, 0x7003, 0x0500,
+ 0x7814, 0x2048, 0xa8cc, 0x700a, 0xa8d0, 0x700e, 0xa8d4, 0x7012,
+ 0xa8d8, 0x7016, 0xa8dc, 0x701a, 0xa8e0, 0x701e, 0x60c3, 0x0010,
+ 0x080c, 0xa32a, 0x009e, 0x00de, 0x0005, 0x00d6, 0x0096, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x9d39, 0x20e9, 0x0000, 0x2001, 0x19a2,
+ 0x2003, 0x0000, 0x7814, 0x2048, 0xa814, 0x8003, 0x60c2, 0xa830,
+ 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098, 0x2001,
+ 0x19a2, 0x0016, 0x200c, 0x2001, 0x0001, 0x080c, 0x23f5, 0x080c,
+ 0xd9e7, 0x9006, 0x080c, 0x23f5, 0x001e, 0xa804, 0x9005, 0x0110,
+ 0x2048, 0x0c28, 0x04d9, 0x080c, 0xa32a, 0x012e, 0x009e, 0x00de,
+ 0x0005, 0x00d6, 0x0096, 0x0126, 0x2091, 0x8000, 0x080c, 0x9d84,
+ 0x20e9, 0x0000, 0x2001, 0x19a2, 0x2003, 0x0000, 0x7814, 0x2048,
+ 0xa86f, 0x0200, 0xa873, 0x0000, 0xa814, 0x8003, 0x60c2, 0xa830,
+ 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098, 0x2001,
+ 0x19a2, 0x0016, 0x200c, 0x080c, 0xd9e7, 0x001e, 0xa804, 0x9005,
+ 0x0110, 0x2048, 0x0c60, 0x0051, 0x7814, 0x2048, 0x080c, 0x0fb1,
+ 0x080c, 0xa32a, 0x012e, 0x009e, 0x00de, 0x0005, 0x60c0, 0x8004,
+ 0x9084, 0x0003, 0x9005, 0x0130, 0x9082, 0x0004, 0x20a3, 0x0000,
+ 0x8000, 0x1de0, 0x0005, 0x080c, 0x9d39, 0x7003, 0x7800, 0x7808,
+ 0x8007, 0x700a, 0x60c3, 0x0008, 0x0804, 0xa32a, 0x00d6, 0x00e6,
+ 0x080c, 0x9d84, 0x7814, 0x9084, 0xff00, 0x2073, 0x0200, 0x8e70,
+ 0x8e70, 0x9095, 0x0010, 0x2272, 0x8e70, 0x2073, 0x0034, 0x8e70,
+ 0x2069, 0x1805, 0x20a9, 0x0004, 0x2d76, 0x8d68, 0x8e70, 0x1f04,
+ 0x9ad4, 0x2069, 0x1801, 0x20a9, 0x0004, 0x2d76, 0x8d68, 0x8e70,
+ 0x1f04, 0x9add, 0x2069, 0x19b2, 0x9086, 0xdf00, 0x0110, 0x2069,
+ 0x19cc, 0x20a9, 0x001a, 0x9e86, 0x0260, 0x1148, 0x00c6, 0x2061,
+ 0x0200, 0x6010, 0x8000, 0x6012, 0x00ce, 0x2071, 0x0240, 0x2d04,
+ 0x8007, 0x2072, 0x8d68, 0x8e70, 0x1f04, 0x9aeb, 0x60c3, 0x004c,
+ 0x080c, 0xa32a, 0x00ee, 0x00de, 0x0005, 0x080c, 0x9d39, 0x7003,
+ 0x6300, 0x7007, 0x0028, 0x7808, 0x700e, 0x60c3, 0x0008, 0x0804,
+ 0xa32a, 0x00d6, 0x0026, 0x0016, 0x080c, 0x9d84, 0x7003, 0x0200,
+ 0x7814, 0x700e, 0x00e6, 0x9ef0, 0x0004, 0x2009, 0x0001, 0x2011,
+ 0x000c, 0x2069, 0x1923, 0x6810, 0xd084, 0x1148, 0x2073, 0x0500,
+ 0x8e70, 0x2073, 0x0000, 0x8e70, 0x8108, 0x9290, 0x0004, 0x2073,
+ 0x0800, 0x8e70, 0x2073, 0x0000, 0x00ee, 0x7206, 0x710a, 0x62c2,
+ 0x080c, 0xa32a, 0x001e, 0x002e, 0x00de, 0x0005, 0x2001, 0x1818,
+ 0x2004, 0x609a, 0x0804, 0xa32a, 0x080c, 0x9d39, 0x7003, 0x5200,
+ 0x2069, 0x1847, 0x6804, 0xd084, 0x0130, 0x6828, 0x0016, 0x080c,
+ 0x28af, 0x710e, 0x001e, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099,
+ 0x1805, 0x20e9, 0x0000, 0x20a1, 0x0250, 0x4003, 0x20a9, 0x0004,
+ 0x2099, 0x1801, 0x20a1, 0x0254, 0x4003, 0x080c, 0xaead, 0x1120,
+ 0xb8a0, 0x9082, 0x007f, 0x0248, 0x2001, 0x181f, 0x2004, 0x7032,
+ 0x2001, 0x1820, 0x2004, 0x7036, 0x0030, 0x2001, 0x1818, 0x2004,
+ 0x9084, 0x00ff, 0x7036, 0x60c3, 0x001c, 0x0804, 0xa32a, 0x080c,
+ 0x9d39, 0x7003, 0x0500, 0x080c, 0xaead, 0x1120, 0xb8a0, 0x9082,
+ 0x007f, 0x0248, 0x2001, 0x181f, 0x2004, 0x700a, 0x2001, 0x1820,
+ 0x2004, 0x700e, 0x0030, 0x2001, 0x1818, 0x2004, 0x9084, 0x00ff,
+ 0x700e, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9,
+ 0x0000, 0x20a1, 0x0250, 0x4003, 0x60c3, 0x0010, 0x0804, 0xa32a,
+ 0x080c, 0x9d39, 0x9006, 0x080c, 0x69d6, 0xb8a0, 0x9086, 0x007e,
+ 0x1130, 0x7003, 0x0400, 0x620c, 0xc2b4, 0x620e, 0x0058, 0x7814,
+ 0x0096, 0x904d, 0x0120, 0x9006, 0xa89a, 0xa8a6, 0xa8aa, 0x009e,
+ 0x7003, 0x0300, 0xb8a0, 0x9086, 0x007e, 0x1904, 0x9c17, 0x00d6,
+ 0x2069, 0x196b, 0x2001, 0x1837, 0x2004, 0xd0a4, 0x0188, 0x6800,
+ 0x700a, 0x6808, 0x9084, 0x2000, 0x7012, 0x080c, 0xaec4, 0x680c,
+ 0x7016, 0x701f, 0x2710, 0x6818, 0x7022, 0x681c, 0x7026, 0x0090,
+ 0x6800, 0x700a, 0x6804, 0x700e, 0x6808, 0x080c, 0x743e, 0x1118,
+ 0x9084, 0x37ff, 0x0010, 0x9084, 0x3fff, 0x7012, 0x080c, 0xaec4,
+ 0x680c, 0x7016, 0x00de, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099,
+ 0x1805, 0x20e9, 0x0000, 0x20a1, 0x0256, 0x4003, 0x20a9, 0x0004,
+ 0x2099, 0x1801, 0x20a1, 0x025a, 0x4003, 0x00d6, 0x080c, 0xabe9,
+ 0x2069, 0x1973, 0x2071, 0x024e, 0x6800, 0xc0dd, 0x7002, 0x080c,
+ 0x5761, 0xd0e4, 0x0110, 0x680c, 0x700e, 0x00de, 0x04a8, 0x2001,
+ 0x1837, 0x2004, 0xd0a4, 0x0170, 0x0016, 0x2001, 0x196c, 0x200c,
+ 0x60e0, 0x9106, 0x0130, 0x2100, 0x60e3, 0x0000, 0x080c, 0x28f0,
+ 0x61e2, 0x001e, 0x20e1, 0x0001, 0x2099, 0x196b, 0x20e9, 0x0000,
+ 0x20a1, 0x024e, 0x20a9, 0x0008, 0x4003, 0x20a9, 0x0004, 0x2099,
+ 0x1805, 0x20a1, 0x0256, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1801,
+ 0x20a1, 0x025a, 0x4003, 0x080c, 0xabe9, 0x20a1, 0x024e, 0x20a9,
+ 0x0008, 0x2099, 0x1973, 0x4003, 0x60c3, 0x0074, 0x0804, 0xa32a,
+ 0x080c, 0x9d39, 0x7003, 0x2010, 0x7007, 0x0014, 0x700b, 0x0800,
+ 0x700f, 0x2000, 0x9006, 0x00f6, 0x2079, 0x1847, 0x7904, 0x00fe,
+ 0xd1ac, 0x1110, 0x9085, 0x0020, 0xd1a4, 0x0110, 0x9085, 0x0010,
+ 0x9085, 0x0002, 0x00d6, 0x0804, 0x9ce9, 0x7026, 0x60c3, 0x0014,
+ 0x0804, 0xa32a, 0x080c, 0x9d39, 0x7003, 0x5000, 0x0804, 0x9bc2,
+ 0x080c, 0x9d39, 0x7003, 0x2110, 0x7007, 0x0014, 0x60c3, 0x0014,
+ 0x0804, 0xa32a, 0x080c, 0x9d7b, 0x0010, 0x080c, 0x9d84, 0x7003,
+ 0x0200, 0x60c3, 0x0004, 0x0804, 0xa32a, 0x080c, 0x9d84, 0x7003,
+ 0x0100, 0x700b, 0x0003, 0x700f, 0x2a00, 0x60c3, 0x0008, 0x0804,
+ 0xa32a, 0x080c, 0x9d84, 0x7003, 0x0200, 0x0804, 0x9bc2, 0x080c,
+ 0x9d84, 0x7003, 0x0100, 0x782c, 0x9005, 0x0110, 0x700a, 0x0010,
+ 0x700b, 0x0003, 0x7814, 0x700e, 0x60c3, 0x0008, 0x0804, 0xa32a,
+ 0x00d6, 0x080c, 0x9d84, 0x7003, 0x0210, 0x7007, 0x0014, 0x700b,
+ 0x0800, 0xb894, 0x9086, 0x0014, 0x1198, 0xb99c, 0x9184, 0x0030,
+ 0x0190, 0xb998, 0x9184, 0xc000, 0x1140, 0xd1ec, 0x0118, 0x700f,
+ 0x2100, 0x0058, 0x700f, 0x0100, 0x0040, 0x700f, 0x0400, 0x0028,
+ 0x700f, 0x0700, 0x0010, 0x700f, 0x0800, 0x00f6, 0x2079, 0x1847,
+ 0x7904, 0x00fe, 0xd1ac, 0x1110, 0x9085, 0x0020, 0xd1a4, 0x0110,
+ 0x9085, 0x0010, 0x2009, 0x1869, 0x210c, 0xd184, 0x1110, 0x9085,
+ 0x0002, 0x0026, 0x2009, 0x1867, 0x210c, 0xd1e4, 0x0150, 0xc0c5,
+ 0xbacc, 0xd28c, 0x1108, 0xc0cd, 0x9094, 0x0030, 0x9296, 0x0010,
+ 0x0140, 0xd1ec, 0x0130, 0x9094, 0x0030, 0x9296, 0x0010, 0x0108,
+ 0xc0bd, 0x002e, 0x7026, 0x60c3, 0x0014, 0x00de, 0x0804, 0xa32a,
+ 0x080c, 0x9d84, 0x7003, 0x0210, 0x7007, 0x0014, 0x700f, 0x0100,
+ 0x60c3, 0x0014, 0x0804, 0xa32a, 0x080c, 0x9d84, 0x7003, 0x0200,
+ 0x0804, 0x9b48, 0x080c, 0x9d84, 0x7003, 0x0100, 0x700b, 0x0003,
+ 0x700f, 0x2a00, 0x60c3, 0x0008, 0x0804, 0xa32a, 0x080c, 0x9d84,
+ 0x7003, 0x0100, 0x700b, 0x000b, 0x60c3, 0x0008, 0x0804, 0xa32a,
+ 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019, 0x3200, 0x2021, 0x0800,
+ 0x0040, 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019, 0x2200, 0x2021,
+ 0x0100, 0x080c, 0xabfe, 0xb810, 0x9305, 0x7002, 0xb814, 0x7006,
+ 0x2069, 0x1800, 0x687c, 0x700a, 0x6880, 0x700e, 0x9485, 0x0029,
+ 0x7012, 0x004e, 0x003e, 0x00de, 0x080c, 0xa318, 0x721a, 0x9f95,
+ 0x0000, 0x7222, 0x7027, 0xffff, 0x2071, 0x024c, 0x002e, 0x0005,
+ 0x0026, 0x080c, 0xabfe, 0x7003, 0x02ff, 0x7007, 0xfffc, 0x00d6,
+ 0x2069, 0x1800, 0x687c, 0x700a, 0x6880, 0x700e, 0x00de, 0x7013,
+ 0x2029, 0x0c10, 0x7003, 0x0100, 0x7007, 0x0000, 0x700b, 0xfc02,
+ 0x700f, 0x0000, 0x0005, 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019,
+ 0x3300, 0x2021, 0x0800, 0x0040, 0x0026, 0x00d6, 0x0036, 0x0046,
+ 0x2019, 0x2300, 0x2021, 0x0100, 0x080c, 0xabfe, 0xb810, 0x9305,
+ 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0xb810, 0x9005, 0x1140,
+ 0xb814, 0x9005, 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffe, 0x0020,
+ 0x687c, 0x700a, 0x6880, 0x700e, 0x0000, 0x9485, 0x0098, 0x7012,
+ 0x004e, 0x003e, 0x00de, 0x080c, 0xa318, 0x721a, 0x7a08, 0x7222,
+ 0x2f10, 0x7226, 0x2071, 0x024c, 0x002e, 0x0005, 0x080c, 0xa318,
+ 0x721a, 0x7a08, 0x7222, 0x7814, 0x7026, 0x2071, 0x024c, 0x002e,
+ 0x0005, 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2069, 0x0200,
+ 0x2071, 0x0240, 0x6004, 0x908a, 0x0085, 0x0a0c, 0x0dd5, 0x908a,
+ 0x0092, 0x1a0c, 0x0dd5, 0x6110, 0x2158, 0xb9c0, 0x2c78, 0x2061,
+ 0x0100, 0x619a, 0x9082, 0x0085, 0x0033, 0x00fe, 0x00ee, 0x00de,
+ 0x00ce, 0x00be, 0x0005, 0x9df2, 0x9e01, 0x9e0c, 0x9df0, 0x9df0,
+ 0x9df0, 0x9df2, 0x9df0, 0x9df0, 0x9df0, 0x9df0, 0x9df0, 0x9df0,
+ 0x080c, 0x0dd5, 0x0411, 0x60c3, 0x0000, 0x0026, 0x080c, 0x2be3,
+ 0x0228, 0x2011, 0x0101, 0x2204, 0xc0c5, 0x2012, 0x002e, 0x0804,
+ 0xa32a, 0x0431, 0x7808, 0x700a, 0x7814, 0x700e, 0x7017, 0xffff,
+ 0x60c3, 0x000c, 0x0804, 0xa32a, 0x04a1, 0x7003, 0x0003, 0x7007,
+ 0x0300, 0x60c3, 0x0004, 0x0804, 0xa32a, 0x0026, 0x080c, 0xabfe,
+ 0xb810, 0x9085, 0x8100, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800,
+ 0x687c, 0x700a, 0x6880, 0x700e, 0x7013, 0x0009, 0x0804, 0x9d54,
+ 0x0026, 0x080c, 0xabfe, 0xb810, 0x9085, 0x8400, 0x7002, 0xb814,
+ 0x7006, 0x2069, 0x1800, 0x687c, 0x700a, 0x6880, 0x700e, 0x2001,
+ 0x0099, 0x7a20, 0x9296, 0x0005, 0x0108, 0xc0bc, 0x7012, 0x0804,
+ 0x9db6, 0x0026, 0x080c, 0xabfe, 0xb810, 0x9085, 0x8500, 0x7002,
+ 0xb814, 0x7006, 0x2069, 0x1800, 0x687c, 0x700a, 0x6880, 0x700e,
+ 0x2001, 0x0099, 0x7a20, 0x9296, 0x0005, 0x0108, 0xc0bc, 0x7012,
+ 0x0804, 0x9db6, 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2c78,
+ 0x2069, 0x0200, 0x2071, 0x0240, 0x7804, 0x908a, 0x0040, 0x0a0c,
+ 0x0dd5, 0x908a, 0x0054, 0x1a0c, 0x0dd5, 0x7910, 0x2158, 0xb9c0,
+ 0x2061, 0x0100, 0x619a, 0x9082, 0x0040, 0x0033, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x00be, 0x0005, 0x9e91, 0x9f4d, 0x9f20, 0xa06f,
+ 0x9e8f, 0x9e8f, 0x9e8f, 0x9e8f, 0x9e8f, 0x9e8f, 0x9e8f, 0xa73e,
+ 0xa746, 0xa74e, 0xa756, 0x9e8f, 0xab46, 0x9e8f, 0xa736, 0x080c,
+ 0x0dd5, 0x0096, 0x780b, 0xffff, 0x080c, 0x9efc, 0x7914, 0x2148,
+ 0xa978, 0x7956, 0xae64, 0x96b4, 0x00ff, 0x9686, 0x0008, 0x1148,
+ 0xa8b4, 0x7032, 0xa8b8, 0x7036, 0xa8bc, 0x703a, 0xa8c0, 0x703e,
+ 0x0008, 0x7132, 0xa97c, 0x9184, 0x000f, 0x1118, 0x2001, 0x0005,
+ 0x0040, 0xd184, 0x0118, 0x2001, 0x0004, 0x0018, 0x9084, 0x0006,
+ 0x8004, 0x2010, 0x785c, 0x9084, 0x00ff, 0x8007, 0x9205, 0x7042,
+ 0xd1ac, 0x0158, 0x7047, 0x0002, 0x9686, 0x0008, 0x1118, 0x080c,
+ 0x18dd, 0x0010, 0x080c, 0x1754, 0x0050, 0xd1b4, 0x0118, 0x7047,
+ 0x0001, 0x0028, 0x7047, 0x0000, 0x9016, 0x2230, 0x0010, 0xaab0,
+ 0xaeac, 0x726a, 0x766e, 0x20a9, 0x0008, 0x20e9, 0x0000, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x0023, 0x2098, 0x20a1, 0x0252, 0x2069,
+ 0x0200, 0x6813, 0x0018, 0x4003, 0x6813, 0x0008, 0x60c3, 0x0020,
+ 0x6017, 0x0009, 0x2001, 0x1a02, 0x2003, 0x07d0, 0x2001, 0x1a01,
+ 0x2003, 0x0009, 0x009e, 0x0005, 0x6813, 0x0008, 0xba8c, 0x8210,
+ 0xb8cc, 0xd084, 0x0128, 0x7a4a, 0x7b14, 0x7b46, 0x722e, 0x732a,
+ 0x9294, 0x00ff, 0xba8e, 0x8217, 0x721a, 0xba10, 0x9295, 0x0600,
+ 0x7202, 0xba14, 0x7206, 0x2069, 0x1800, 0x6a7c, 0x720a, 0x6a80,
+ 0x720e, 0x7013, 0x0829, 0x2f10, 0x7222, 0x7027, 0xffff, 0x0005,
+ 0x00d6, 0x0096, 0x0081, 0x7814, 0x2048, 0xa890, 0x7002, 0xa88c,
+ 0x7006, 0xa8b0, 0x700a, 0xa8ac, 0x700e, 0x60c3, 0x000c, 0x009e,
+ 0x00de, 0x0804, 0xa32a, 0x6813, 0x0008, 0xb810, 0x9085, 0x0500,
+ 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x687c, 0x700a, 0x6880,
+ 0x700e, 0x7013, 0x0889, 0x080c, 0xa318, 0x721a, 0x7a08, 0x7222,
+ 0x2f10, 0x7226, 0x2071, 0x024c, 0x0005, 0x00d6, 0x0096, 0x080c,
+ 0xa04d, 0x7814, 0x2048, 0x080c, 0xcc84, 0x1130, 0x7814, 0x9084,
+ 0x0700, 0x8007, 0x0033, 0x0010, 0x9006, 0x001b, 0x009e, 0x00de,
+ 0x0005, 0x9f6b, 0x9fd4, 0x9fe4, 0xa00a, 0xa016, 0xa027, 0xa02f,
+ 0x9f69, 0x080c, 0x0dd5, 0x0016, 0x0036, 0xa97c, 0x918c, 0x0003,
+ 0x0118, 0x9186, 0x0003, 0x1198, 0xaba8, 0x7824, 0xd0cc, 0x1168,
+ 0x7316, 0xa898, 0x701a, 0xa894, 0x701e, 0x003e, 0x001e, 0x2001,
+ 0x19b0, 0x2004, 0x60c2, 0x0804, 0xa32a, 0xc3e5, 0x0c88, 0x9186,
+ 0x0001, 0x190c, 0x0dd5, 0xaba8, 0x7824, 0xd0cc, 0x1904, 0x9fd1,
+ 0x7316, 0xa898, 0x701a, 0xa894, 0x701e, 0xa8a4, 0x7026, 0xa8ac,
+ 0x702e, 0x2009, 0x0018, 0x9384, 0x0300, 0x0570, 0xd3c4, 0x0110,
+ 0xa8ac, 0x9108, 0xd3cc, 0x0110, 0xa8a4, 0x9108, 0x6810, 0x9085,
+ 0x0010, 0x6812, 0x2011, 0x0258, 0x20e9, 0x0000, 0x22a0, 0x0156,
+ 0x20a9, 0x0008, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x002c, 0x2098,
+ 0x4003, 0x6810, 0x8000, 0x6812, 0x2011, 0x0240, 0x22a0, 0x20a9,
+ 0x0005, 0x4003, 0x6810, 0xc084, 0x6812, 0x015e, 0x9184, 0x0003,
+ 0x0118, 0x2019, 0x0245, 0x201a, 0x61c2, 0x003e, 0x001e, 0x0804,
+ 0xa32a, 0xc3e5, 0x0804, 0x9f90, 0x2011, 0x0008, 0x2001, 0x180f,
+ 0x2004, 0xd0a4, 0x0110, 0x2011, 0x0028, 0x7824, 0xd0cc, 0x1110,
+ 0x7216, 0x0470, 0x0ce8, 0xc2e5, 0x2011, 0x0302, 0x0016, 0x782c,
+ 0x701a, 0x7930, 0x711e, 0x9105, 0x0108, 0xc2dd, 0x001e, 0x7824,
+ 0xd0cc, 0x0108, 0xc2e5, 0x7216, 0x7027, 0x0012, 0x702f, 0x0008,
+ 0x7043, 0x7000, 0x7047, 0x0500, 0x704f, 0x000a, 0x2069, 0x0200,
+ 0x6813, 0x0009, 0x2071, 0x0240, 0x700b, 0x2500, 0x60c3, 0x0032,
+ 0x0804, 0xa32a, 0x2011, 0x0028, 0x7824, 0xd0cc, 0x1128, 0x7216,
+ 0x60c3, 0x0018, 0x0804, 0xa32a, 0x0cd0, 0xc2e5, 0x2011, 0x0100,
+ 0x7824, 0xd0cc, 0x0108, 0xc2e5, 0x7216, 0x702f, 0x0008, 0x7858,
+ 0x9084, 0x00ff, 0x7036, 0x60c3, 0x0020, 0x0804, 0xa32a, 0x2011,
+ 0x0008, 0x7824, 0xd0cc, 0x0108, 0xc2e5, 0x7216, 0x0c08, 0x0036,
+ 0x7b14, 0x9384, 0xff00, 0x7816, 0x9384, 0x00ff, 0x8001, 0x1138,
+ 0x7824, 0xd0cc, 0x0108, 0xc2e5, 0x7216, 0x003e, 0x0888, 0x0046,
+ 0x2021, 0x0800, 0x0006, 0x7824, 0xd0cc, 0x000e, 0x0108, 0xc4e5,
+ 0x7416, 0x004e, 0x701e, 0x003e, 0x0818, 0x00d6, 0x6813, 0x0008,
+ 0xb810, 0x9085, 0x0700, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800,
+ 0x687c, 0x700a, 0x6880, 0x700e, 0x7824, 0xd0cc, 0x1168, 0x7013,
+ 0x0898, 0x080c, 0xa318, 0x721a, 0x7a08, 0x7222, 0x2f10, 0x7226,
+ 0x2071, 0x024c, 0x00de, 0x0005, 0x7013, 0x0889, 0x0c90, 0x0016,
+ 0x7814, 0x9084, 0x0700, 0x8007, 0x0013, 0x001e, 0x0005, 0xa07f,
+ 0xa07f, 0xa081, 0xa07f, 0xa07f, 0xa07f, 0xa09b, 0xa07f, 0x080c,
+ 0x0dd5, 0x7914, 0x918c, 0x08ff, 0x918d, 0xf600, 0x7916, 0x2009,
+ 0x0003, 0x00b9, 0x2069, 0x1847, 0x6804, 0xd0bc, 0x0130, 0x682c,
+ 0x9084, 0x00ff, 0x8007, 0x7032, 0x0010, 0x7033, 0x3f00, 0x60c3,
+ 0x0001, 0x0804, 0xa32a, 0x2009, 0x0003, 0x0019, 0x7033, 0x7f00,
+ 0x0cb0, 0x0016, 0x080c, 0xabfe, 0x001e, 0xb810, 0x9085, 0x0100,
+ 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x6a7c, 0x720a, 0x6a80,
+ 0x720e, 0x7013, 0x0888, 0x918d, 0x0008, 0x7116, 0x080c, 0xa318,
+ 0x721a, 0x7a08, 0x7222, 0x2f10, 0x7226, 0x0005, 0x00b6, 0x00e6,
+ 0x00d6, 0x00c6, 0x0066, 0x0056, 0x0046, 0x0036, 0x2061, 0x0100,
+ 0x2071, 0x1800, 0x7160, 0x7810, 0x2058, 0x76dc, 0x96b4, 0x0028,
+ 0x0110, 0x737c, 0x7480, 0x2500, 0x76dc, 0x96b4, 0x0028, 0x0140,
+ 0x2001, 0x04ff, 0x6062, 0x6067, 0xffff, 0x636a, 0x646e, 0x0050,
+ 0x2001, 0x00ff, 0x9085, 0x0400, 0x6062, 0x6067, 0xffff, 0x606b,
+ 0x0000, 0x616e, 0xb8b8, 0x6073, 0x0530, 0x6077, 0x0008, 0xb88c,
+ 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007, 0x9085, 0x0020, 0x607a,
+ 0x607f, 0x0000, 0x2b00, 0x6082, 0x6087, 0xffff, 0x7814, 0x0096,
+ 0x2048, 0xa838, 0x608a, 0xa834, 0x608e, 0xa848, 0x60c6, 0xa844,
+ 0x60ca, 0x009e, 0xb86c, 0x60ce, 0x60ab, 0x0036, 0x60af, 0x95d5,
+ 0x60d7, 0x0000, 0x2001, 0x1837, 0x2004, 0x9084, 0x0028, 0x0128,
+ 0x609f, 0x0000, 0x2001, 0x0092, 0x0048, 0x6028, 0xc0bd, 0x602a,
+ 0x609f, 0x00ff, 0x6027, 0xffff, 0x2001, 0x00b2, 0x6016, 0x2009,
+ 0x07d0, 0x080c, 0x863b, 0x003e, 0x004e, 0x005e, 0x006e, 0x00ce,
+ 0x00de, 0x00ee, 0x00be, 0x0005, 0x00b6, 0x00e6, 0x00d6, 0x00c6,
+ 0x0066, 0x0056, 0x0046, 0x0036, 0x2061, 0x0100, 0x2071, 0x1800,
+ 0x7160, 0x7810, 0x2058, 0xb8a0, 0x2028, 0x76dc, 0xd6ac, 0x1168,
+ 0x9582, 0x007e, 0x1250, 0x2500, 0x9094, 0xff80, 0x1130, 0x9080,
+ 0x3384, 0x2015, 0x9294, 0x00ff, 0x0020, 0xb910, 0xba14, 0x737c,
+ 0x7480, 0x70dc, 0xd0ac, 0x1130, 0x9582, 0x007e, 0x1218, 0x9584,
+ 0xff80, 0x0138, 0x9185, 0x0400, 0x6062, 0x6266, 0x636a, 0x646e,
+ 0x0030, 0x6063, 0x0400, 0x6266, 0x606b, 0x0000, 0x616e, 0xb8b8,
+ 0x6072, 0x6077, 0x0000, 0xb864, 0xd0a4, 0x0110, 0x6077, 0x0008,
+ 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007, 0x9085, 0x0020,
+ 0x607a, 0x607f, 0x0000, 0x2b00, 0x6082, 0x6087, 0xffff, 0x7814,
+ 0x0096, 0x2048, 0xa838, 0x608a, 0xa834, 0x608e, 0xa848, 0x60c6,
+ 0xa844, 0x60ca, 0x009e, 0xb86c, 0x60ce, 0x60ab, 0x0036, 0x60af,
+ 0x95d5, 0x60d7, 0x0000, 0xbac0, 0x629e, 0x00f6, 0x2079, 0x0140,
+ 0x7803, 0x0000, 0x00fe, 0x2009, 0x0092, 0x6116, 0x2009, 0x07d0,
+ 0x080c, 0x863b, 0x003e, 0x004e, 0x005e, 0x006e, 0x00ce, 0x00de,
+ 0x00ee, 0x00be, 0x0005, 0x00b6, 0x0096, 0x00e6, 0x00d6, 0x00c6,
+ 0x0056, 0x0046, 0x0036, 0x2061, 0x0100, 0x2071, 0x1800, 0x7810,
+ 0x2058, 0xb8a0, 0x2028, 0xb910, 0xba14, 0x737c, 0x7480, 0x7820,
+ 0x90be, 0x0006, 0x0904, 0xa287, 0x90be, 0x000a, 0x1904, 0xa243,
+ 0xb8c0, 0x609e, 0x7814, 0x2048, 0xa87c, 0xd0fc, 0x0558, 0xaf90,
+ 0x9784, 0xff00, 0x9105, 0x6062, 0x873f, 0x9784, 0xff00, 0x0006,
+ 0x7814, 0x2048, 0xa878, 0xc0fc, 0x9005, 0x000e, 0x1160, 0xaf94,
+ 0x87ff, 0x0198, 0x2039, 0x0098, 0x9705, 0x6072, 0x7808, 0x6082,
+ 0x2f00, 0x6086, 0x0038, 0x9185, 0x2200, 0x6062, 0x6073, 0x0129,
+ 0x6077, 0x0000, 0xb8c0, 0x609e, 0x0050, 0x2039, 0x0029, 0x9705,
+ 0x6072, 0x0cc0, 0x9185, 0x0200, 0x6062, 0x6073, 0x2029, 0xa87c,
+ 0xd0fc, 0x0118, 0xaf94, 0x87ff, 0x1120, 0x2f00, 0x6082, 0x7808,
+ 0x6086, 0x6266, 0x636a, 0x646e, 0x6077, 0x0000, 0xb88c, 0x8000,
+ 0x9084, 0x00ff, 0xb88e, 0x8007, 0x607a, 0x607f, 0x0000, 0xa838,
+ 0x608a, 0xa834, 0x608e, 0xa848, 0x60c6, 0xa844, 0x60ca, 0xb86c,
+ 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0x080c, 0xabe3, 0x2009,
+ 0x07d0, 0x60c4, 0x9084, 0xfff0, 0x9005, 0x0110, 0x2009, 0x1b58,
+ 0x080c, 0x863b, 0x003e, 0x004e, 0x005e, 0x00ce, 0x00de, 0x00ee,
+ 0x009e, 0x00be, 0x0005, 0x7804, 0x9086, 0x0040, 0x0904, 0xa2c3,
+ 0x9185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x0809,
+ 0x6077, 0x0008, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xb88c, 0x8000,
+ 0x9084, 0x00ff, 0xb88e, 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00,
+ 0x6082, 0x7808, 0x6086, 0x7814, 0x2048, 0xa838, 0x608a, 0xa834,
+ 0x608e, 0xa848, 0x60c6, 0xa844, 0x60ca, 0xb86c, 0x60ce, 0xbac0,
+ 0x629e, 0x080c, 0xabe3, 0x2009, 0x07d0, 0x60c4, 0x9084, 0xfff0,
+ 0x9005, 0x0110, 0x2009, 0x1b58, 0x080c, 0x863b, 0x003e, 0x004e,
+ 0x005e, 0x00ce, 0x00de, 0x00ee, 0x009e, 0x00be, 0x0005, 0x7814,
+ 0x2048, 0xa87c, 0x9084, 0x0003, 0x9086, 0x0002, 0x0904, 0xa2df,
+ 0x9185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x0880,
+ 0x6077, 0x0008, 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007,
+ 0x607a, 0x7838, 0x607e, 0x2f00, 0x6086, 0x7808, 0x6082, 0xa890,
+ 0x608a, 0xa88c, 0x608e, 0xa8b0, 0x60c6, 0xa8ac, 0x60ca, 0xa8ac,
+ 0x7930, 0x9108, 0x7932, 0xa8b0, 0x792c, 0x9109, 0x792e, 0xb86c,
+ 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xbac0, 0x629e, 0x080c,
+ 0xabc0, 0x0804, 0xa273, 0xb8cc, 0xd084, 0x0148, 0xb88c, 0x7814,
+ 0x2048, 0xb88c, 0x784a, 0xa836, 0x2900, 0xa83a, 0xb046, 0x9185,
+ 0x0600, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x0829, 0x6077,
+ 0x0000, 0x60af, 0x9575, 0x60d7, 0x0000, 0x0804, 0xa256, 0x9185,
+ 0x0700, 0x6062, 0x6266, 0x636a, 0x646e, 0x7824, 0xd0cc, 0x7826,
+ 0x0118, 0x6073, 0x0889, 0x0010, 0x6073, 0x0898, 0x6077, 0x0000,
+ 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007, 0x607a, 0x607f,
+ 0x0000, 0x2f00, 0x6086, 0x7808, 0x6082, 0xa838, 0x608a, 0xa834,
+ 0x608e, 0xa848, 0x60c6, 0xa844, 0x60ca, 0xb86c, 0x60ce, 0x60af,
+ 0x95d5, 0x60d7, 0x0000, 0xbac0, 0x629e, 0x7824, 0xd0cc, 0x0120,
+ 0x080c, 0xabe3, 0x0804, 0xa273, 0x080c, 0xabc0, 0x0804, 0xa273,
+ 0x7a10, 0x00b6, 0x2258, 0xba8c, 0x8210, 0x9294, 0x00ff, 0xba8e,
+ 0x00be, 0x8217, 0x0005, 0x00d6, 0x2069, 0x19e6, 0x6843, 0x0001,
+ 0x00de, 0x0005, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x00f1, 0x080c,
+ 0x862d, 0x0005, 0x0016, 0x2001, 0x180c, 0x200c, 0x9184, 0x0600,
+ 0x9086, 0x0600, 0x0128, 0x0089, 0x080c, 0x862d, 0x001e, 0x0005,
+ 0xc1e5, 0x2001, 0x180c, 0x2102, 0x2001, 0x19e7, 0x2003, 0x0000,
+ 0x2001, 0x19ef, 0x2003, 0x0000, 0x0c88, 0x0006, 0x6014, 0x9084,
+ 0x1804, 0x9085, 0x0009, 0x6016, 0x000e, 0x0005, 0x0016, 0x00c6,
+ 0x0006, 0x2061, 0x0100, 0x61a4, 0x60a7, 0x95f5, 0x6014, 0x9084,
+ 0x1804, 0x9085, 0x0008, 0x6016, 0x000e, 0xa001, 0xa001, 0xa001,
+ 0x61a6, 0x00ce, 0x001e, 0x0005, 0x00c6, 0x00d6, 0x0016, 0x0026,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x080c, 0x743e, 0x11c0, 0x2001,
+ 0x1a02, 0x2004, 0x9005, 0x15d0, 0x080c, 0x74ee, 0x1160, 0x2061,
+ 0x0100, 0x6020, 0xd0b4, 0x1120, 0x6024, 0xd084, 0x090c, 0x0dd5,
+ 0x080c, 0x862d, 0x0458, 0x00c6, 0x2061, 0x19e6, 0x00c8, 0x6904,
+ 0x9194, 0x4000, 0x0540, 0x0811, 0x080c, 0x2d5e, 0x00c6, 0x2061,
+ 0x19e6, 0x6128, 0x9192, 0x0008, 0x1258, 0x8108, 0x612a, 0x6124,
+ 0x00ce, 0x81ff, 0x0198, 0x080c, 0x862d, 0x080c, 0xa34d, 0x0070,
+ 0x6124, 0x91e5, 0x0000, 0x0140, 0x080c, 0xeb8e, 0x080c, 0x8636,
+ 0x2009, 0x0014, 0x080c, 0xafbe, 0x00ce, 0x0000, 0x002e, 0x001e,
+ 0x00de, 0x00ce, 0x0005, 0x2001, 0x1a02, 0x2004, 0x9005, 0x1db0,
+ 0x00c6, 0x2061, 0x19e6, 0x6128, 0x9192, 0x0003, 0x1e08, 0x8108,
+ 0x612a, 0x00ce, 0x080c, 0x862d, 0x080c, 0x5f6c, 0x2009, 0x1846,
+ 0x2114, 0x8210, 0x220a, 0x0c10, 0x0096, 0x00c6, 0x00d6, 0x00e6,
+ 0x0016, 0x0026, 0x080c, 0x8643, 0x2071, 0x19e6, 0x713c, 0x81ff,
+ 0x0904, 0xa456, 0x2061, 0x0100, 0x2069, 0x0140, 0x080c, 0x743e,
+ 0x11e0, 0x0036, 0x2019, 0x0002, 0x080c, 0xa6ac, 0x003e, 0x713c,
+ 0x2160, 0x080c, 0xeb8e, 0x2009, 0x004a, 0x6220, 0x9296, 0x0009,
+ 0x1130, 0x6114, 0x2148, 0xa87b, 0x0006, 0x2009, 0x004a, 0x080c,
+ 0xafbe, 0x080c, 0x74ee, 0x0804, 0xa456, 0x080c, 0xa462, 0x0904,
+ 0xa456, 0x6904, 0xd1f4, 0x0904, 0xa45d, 0x080c, 0x2d5e, 0x00c6,
+ 0x703c, 0x9065, 0x090c, 0x0dd5, 0x6020, 0x00ce, 0x9086, 0x0006,
+ 0x1528, 0x61c8, 0x60c4, 0x9105, 0x1508, 0x2009, 0x180c, 0x2104,
+ 0xd0d4, 0x01e0, 0x6214, 0x9294, 0x1800, 0x1128, 0x6224, 0x9294,
+ 0x0002, 0x1560, 0x0030, 0xc0d4, 0x200a, 0xd0cc, 0x0110, 0x080c,
+ 0x2c90, 0x6014, 0x9084, 0xe7fd, 0x9085, 0x0010, 0x6016, 0x703c,
+ 0x2060, 0x2009, 0x0049, 0x080c, 0xafbe, 0x00c0, 0x0036, 0x2019,
+ 0x0001, 0x080c, 0xa6ac, 0x003e, 0x713c, 0x2160, 0x080c, 0xeb8e,
+ 0x2009, 0x004a, 0x6220, 0x9296, 0x0009, 0x1130, 0x6114, 0x2148,
+ 0xa87b, 0x0006, 0x2009, 0x004a, 0x080c, 0xafbe, 0x002e, 0x001e,
+ 0x00ee, 0x00de, 0x00ce, 0x009e, 0x0005, 0xd1ec, 0x1904, 0xa40d,
+ 0x0804, 0xa40f, 0x00d6, 0x00c6, 0x0096, 0x703c, 0x9065, 0x090c,
+ 0x0dd5, 0x2001, 0x0306, 0x200c, 0x9184, 0x0030, 0x0904, 0xa50b,
+ 0x9184, 0x0048, 0x9086, 0x0008, 0x1904, 0xa50b, 0x2009, 0x0206,
+ 0x2104, 0x2009, 0x0203, 0x210c, 0x9106, 0x1904, 0xa50b, 0x2009,
+ 0x022a, 0x2104, 0x2009, 0x022f, 0x210c, 0x9116, 0x9084, 0x03ff,
+ 0x918c, 0x03ff, 0x9294, 0x0400, 0x0110, 0x9102, 0x0030, 0x2010,
+ 0x2100, 0x9202, 0x2009, 0x0228, 0x9102, 0x9082, 0x0005, 0x0250,
+ 0x2008, 0x2001, 0x013b, 0x2004, 0x8004, 0x8004, 0x8004, 0x9102,
+ 0x1a04, 0xa50b, 0x2009, 0x1a80, 0x2104, 0x8000, 0x0208, 0x200a,
+ 0x2069, 0x0100, 0x6914, 0x918c, 0x0184, 0x918d, 0x0010, 0x6916,
+ 0x69c8, 0x2011, 0x0020, 0x68c8, 0x9106, 0x1570, 0x8211, 0x1dd8,
+ 0x2001, 0x0306, 0x2003, 0x4800, 0x2001, 0x009a, 0x2003, 0x0004,
+ 0x2001, 0x1a65, 0x2003, 0x0000, 0x2001, 0x1a6e, 0x2003, 0x0000,
+ 0x6a88, 0x698c, 0x2200, 0x9105, 0x1120, 0x2c10, 0x080c, 0x1beb,
+ 0x0040, 0x6014, 0x2048, 0xaa3a, 0xa936, 0x6ac4, 0x69c8, 0xa946,
+ 0xaa4a, 0x0126, 0x00c6, 0x2091, 0x2400, 0x002e, 0x080c, 0x1c84,
+ 0x190c, 0x0dd5, 0x012e, 0x0090, 0x2009, 0x1a81, 0x2104, 0x8000,
+ 0x0208, 0x200a, 0x69c8, 0x2011, 0x0020, 0x8211, 0x1df0, 0x68c8,
+ 0x9106, 0x1dc0, 0x69c4, 0x68c8, 0x9105, 0x0160, 0x6824, 0xd08c,
+ 0x0110, 0x6827, 0x0002, 0x7048, 0xc085, 0x704a, 0x0079, 0x7048,
+ 0xc084, 0x704a, 0x2009, 0x07d0, 0x080c, 0x863b, 0x9006, 0x009e,
+ 0x00ce, 0x00de, 0x0005, 0x9085, 0x0001, 0x0cc8, 0x0026, 0x00e6,
+ 0x2071, 0x19e6, 0x7048, 0xd084, 0x01d8, 0x713c, 0x81ff, 0x01c0,
+ 0x2071, 0x0100, 0x9188, 0x0008, 0x2114, 0x928e, 0x0006, 0x1138,
+ 0x7014, 0x9084, 0x1984, 0x9085, 0x0012, 0x7016, 0x0048, 0x928e,
+ 0x0009, 0x0db0, 0x7014, 0x9084, 0x1984, 0x9085, 0x0016, 0x7016,
+ 0x00ee, 0x002e, 0x0005, 0x00b6, 0x00e6, 0x00d6, 0x00c6, 0x0066,
+ 0x0056, 0x0046, 0x0006, 0x0126, 0x2091, 0x8000, 0x6010, 0x2058,
+ 0xbca0, 0x2071, 0x19e6, 0x7018, 0x2058, 0x8bff, 0x0190, 0xb8a0,
+ 0x9406, 0x0118, 0xb854, 0x2058, 0x0cc0, 0x6014, 0x0096, 0x2048,
+ 0xac6c, 0xad70, 0xae78, 0x009e, 0x080c, 0x67cb, 0x0110, 0x9085,
+ 0x0001, 0x012e, 0x000e, 0x004e, 0x005e, 0x006e, 0x00ce, 0x00de,
+ 0x00ee, 0x00be, 0x0005, 0x080c, 0x9d39, 0x7003, 0x1200, 0x7838,
+ 0x7012, 0x783c, 0x7016, 0x00c6, 0x7820, 0x9086, 0x0004, 0x1148,
+ 0x7810, 0x9005, 0x0130, 0x00b6, 0x2058, 0xb810, 0xb914, 0x00be,
+ 0x0020, 0x2061, 0x1800, 0x607c, 0x6180, 0x9084, 0x00ff, 0x700a,
+ 0x710e, 0x00ce, 0x60c3, 0x002c, 0x0804, 0xa32a, 0x080c, 0x9d39,
+ 0x7003, 0x0f00, 0x7808, 0xd09c, 0x0128, 0xb810, 0x9084, 0x00ff,
+ 0x700a, 0xb814, 0x700e, 0x60c3, 0x0008, 0x0804, 0xa32a, 0x0156,
+ 0x080c, 0x9d84, 0x7003, 0x0200, 0x080c, 0x8696, 0x20a9, 0x0006,
+ 0x2011, 0xffec, 0x2019, 0xffed, 0x9ef0, 0x0002, 0x2305, 0x2072,
+ 0x8e70, 0x2205, 0x2072, 0x8e70, 0x9398, 0x0002, 0x9290, 0x0002,
+ 0x1f04, 0xa5a6, 0x60c3, 0x001c, 0x015e, 0x0804, 0xa32a, 0x0016,
+ 0x0026, 0x080c, 0x9d60, 0x080c, 0x9d72, 0x9e80, 0x0004, 0x20e9,
+ 0x0000, 0x20a0, 0x7814, 0x0096, 0x2048, 0xa800, 0x2048, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x0021, 0x2098, 0x009e, 0x7808, 0x9088,
+ 0x0002, 0x21a8, 0x9192, 0x0010, 0x1250, 0x4003, 0x9080, 0x0004,
+ 0x8003, 0x60c2, 0x080c, 0xa32a, 0x002e, 0x001e, 0x0005, 0x20a9,
+ 0x0010, 0x4003, 0x080c, 0xabe9, 0x20a1, 0x0240, 0x22a8, 0x4003,
+ 0x0c68, 0x080c, 0x9d39, 0x7003, 0x6200, 0x7808, 0x700e, 0x60c3,
+ 0x0008, 0x0804, 0xa32a, 0x0016, 0x0026, 0x080c, 0x9d39, 0x20e9,
+ 0x0000, 0x20a1, 0x024c, 0x7814, 0x0096, 0x2048, 0xa800, 0x2048,
+ 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0023, 0x2098, 0x009e, 0x7808,
+ 0x9088, 0x0002, 0x21a8, 0x4003, 0x8003, 0x60c2, 0x080c, 0xa32a,
+ 0x002e, 0x001e, 0x0005, 0x00e6, 0x00c6, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x19e6, 0x700c, 0x2060, 0x8cff, 0x0178, 0x080c,
+ 0xce8e, 0x1110, 0x080c, 0xb905, 0x600c, 0x0006, 0x080c, 0xd0fa,
+ 0x080c, 0xaf43, 0x080c, 0xa761, 0x00ce, 0x0c78, 0x2c00, 0x700e,
+ 0x700a, 0x012e, 0x000e, 0x00ce, 0x00ee, 0x0005, 0x0126, 0x0156,
+ 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0026, 0x0016, 0x0006,
+ 0x2091, 0x8000, 0x2001, 0x180c, 0x200c, 0x918c, 0xe7ff, 0x2102,
+ 0x2069, 0x0100, 0x2079, 0x0140, 0x2071, 0x19e6, 0x7024, 0x2060,
+ 0x8cff, 0x01f8, 0x080c, 0xa356, 0x6ac0, 0x68c3, 0x0000, 0x080c,
+ 0x8636, 0x00c6, 0x2061, 0x0100, 0x080c, 0xad3a, 0x00ce, 0x20a9,
+ 0x01f4, 0x0461, 0x2009, 0x0013, 0x080c, 0xafbe, 0x000e, 0x001e,
+ 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e, 0x012e,
+ 0x0005, 0x2001, 0x1800, 0x2004, 0x9096, 0x0001, 0x0d78, 0x9096,
+ 0x0004, 0x0d60, 0x080c, 0x8636, 0x6814, 0x9084, 0x0001, 0x0110,
+ 0x68a7, 0x95f5, 0x6817, 0x0008, 0x68c3, 0x0000, 0x2011, 0x5f16,
+ 0x080c, 0x85b0, 0x20a9, 0x01f4, 0x0009, 0x08c0, 0x6824, 0xd094,
+ 0x0140, 0x6827, 0x0004, 0x7804, 0x9084, 0x4000, 0x190c, 0x2d5e,
+ 0x0090, 0xd084, 0x0118, 0x6827, 0x0001, 0x0010, 0x1f04, 0xa68e,
+ 0x7804, 0x9084, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e,
+ 0x9006, 0x080c, 0x2d4e, 0x0005, 0x0126, 0x0156, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x0066, 0x0026, 0x0016, 0x0006, 0x2091, 0x8000,
+ 0x2001, 0x180c, 0x200c, 0x918c, 0xdbff, 0x2102, 0x2069, 0x0100,
+ 0x2079, 0x0140, 0x2071, 0x19e6, 0x703c, 0x2060, 0x8cff, 0x0904,
+ 0xa717, 0x9386, 0x0002, 0x1128, 0x6814, 0x9084, 0x0002, 0x0904,
+ 0xa717, 0x68af, 0x95f5, 0x6817, 0x0010, 0x2009, 0x00fa, 0x8109,
+ 0x1df0, 0x69c6, 0x68cb, 0x0008, 0x080c, 0x8643, 0x080c, 0x2038,
+ 0x2001, 0x0032, 0x6920, 0xd1bc, 0x0130, 0x8001, 0x1dd8, 0x692c,
+ 0x918d, 0x0008, 0x692e, 0x20a9, 0x03e8, 0x6824, 0xd094, 0x0140,
+ 0x6827, 0x0004, 0x7804, 0x9084, 0x4000, 0x190c, 0x2d5e, 0x0090,
+ 0xd08c, 0x0118, 0x6827, 0x0002, 0x0010, 0x1f04, 0xa6ed, 0x7804,
+ 0x9084, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x9006,
+ 0x080c, 0x2d4e, 0x6827, 0x4000, 0x6824, 0x83ff, 0x1140, 0x2009,
+ 0x0049, 0x6020, 0x9086, 0x0009, 0x0110, 0x080c, 0xafbe, 0x000e,
+ 0x001e, 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e,
+ 0x012e, 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000, 0x2069, 0x19e6,
+ 0x6a06, 0x012e, 0x00de, 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000,
+ 0x2069, 0x19e6, 0x6a32, 0x012e, 0x00de, 0x0005, 0x080c, 0x9efc,
+ 0x7854, 0x7032, 0x7042, 0x7047, 0x1000, 0x00f8, 0x080c, 0x9efc,
+ 0x7854, 0x7032, 0x7042, 0x7047, 0x4000, 0x00b8, 0x080c, 0x9efc,
+ 0x7854, 0x7032, 0x7042, 0x7047, 0x2000, 0x0078, 0x080c, 0x9efc,
+ 0x7854, 0x7032, 0x7042, 0x7047, 0x0400, 0x0038, 0x080c, 0x9efc,
+ 0x7854, 0x7032, 0x7042, 0x7047, 0x0200, 0x60c3, 0x0020, 0x0804,
+ 0xa32a, 0x00e6, 0x2071, 0x19e6, 0x7020, 0x9005, 0x0110, 0x8001,
+ 0x7022, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0076,
+ 0x0066, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x19e6, 0x7614,
+ 0x2660, 0x2678, 0x2039, 0x0001, 0x87ff, 0x0904, 0xa806, 0x8cff,
+ 0x0904, 0xa806, 0x6020, 0x9086, 0x0006, 0x1904, 0xa801, 0x88ff,
+ 0x0138, 0x2800, 0x9c06, 0x1904, 0xa801, 0x2039, 0x0000, 0x0050,
+ 0x6010, 0x9b06, 0x1904, 0xa801, 0x85ff, 0x0120, 0x6054, 0x9106,
+ 0x1904, 0xa801, 0x7024, 0x9c06, 0x15b0, 0x2069, 0x0100, 0x68c0,
+ 0x9005, 0x1160, 0x6824, 0xd084, 0x0148, 0x6827, 0x0001, 0x080c,
+ 0x8636, 0x080c, 0xa88b, 0x7027, 0x0000, 0x0428, 0x080c, 0x8636,
+ 0x6820, 0xd0b4, 0x0110, 0x68a7, 0x95f5, 0x6817, 0x0008, 0x68c3,
+ 0x0000, 0x080c, 0xa88b, 0x7027, 0x0000, 0x0036, 0x2069, 0x0140,
+ 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e,
+ 0x9006, 0x080c, 0x2d4e, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110,
+ 0x6827, 0x0001, 0x003e, 0x7014, 0x9c36, 0x1110, 0x660c, 0x7616,
+ 0x7010, 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x7012,
+ 0x0010, 0x7013, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x89ff, 0x1168, 0x600f, 0x0000, 0x6014,
+ 0x0096, 0x2048, 0x080c, 0xcc84, 0x0110, 0x080c, 0xe6dd, 0x009e,
+ 0x080c, 0xaf74, 0x080c, 0xa761, 0x88ff, 0x1190, 0x00ce, 0x0804,
+ 0xa77c, 0x2c78, 0x600c, 0x2060, 0x0804, 0xa77c, 0x9006, 0x012e,
+ 0x000e, 0x006e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005,
+ 0x601b, 0x0000, 0x00ce, 0x98c5, 0x0001, 0x0c88, 0x00f6, 0x00e6,
+ 0x00d6, 0x0096, 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x19e6, 0x7638, 0x2660, 0x2678, 0x8cff, 0x0904,
+ 0xa87a, 0x6020, 0x9086, 0x0006, 0x1904, 0xa875, 0x87ff, 0x0128,
+ 0x2700, 0x9c06, 0x1904, 0xa875, 0x0040, 0x6010, 0x9b06, 0x15e8,
+ 0x85ff, 0x0118, 0x6054, 0x9106, 0x15c0, 0x703c, 0x9c06, 0x1168,
+ 0x0036, 0x2019, 0x0001, 0x080c, 0xa6ac, 0x7033, 0x0000, 0x9006,
+ 0x703e, 0x7042, 0x7046, 0x704a, 0x003e, 0x7038, 0x9c36, 0x1110,
+ 0x660c, 0x763a, 0x7034, 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118,
+ 0x2f00, 0x7036, 0x0010, 0x7037, 0x0000, 0x660c, 0x0066, 0x2c00,
+ 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x6014,
+ 0x2048, 0x080c, 0xcc84, 0x0110, 0x080c, 0xe6dd, 0x080c, 0xaf74,
+ 0x87ff, 0x1198, 0x00ce, 0x0804, 0xa826, 0x2c78, 0x600c, 0x2060,
+ 0x0804, 0xa826, 0x9006, 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce,
+ 0x009e, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601b, 0x0000, 0x00ce,
+ 0x97bd, 0x0001, 0x0c80, 0x00e6, 0x2071, 0x19e6, 0x2001, 0x1800,
+ 0x2004, 0x9086, 0x0002, 0x1118, 0x7007, 0x0005, 0x0010, 0x7007,
+ 0x0000, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0066, 0x0026,
+ 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x19e6, 0x2c10, 0x7638,
+ 0x2660, 0x2678, 0x8cff, 0x0540, 0x2200, 0x9c06, 0x1508, 0x7038,
+ 0x9c36, 0x1110, 0x660c, 0x763a, 0x7034, 0x9c36, 0x1140, 0x2c00,
+ 0x9f36, 0x0118, 0x2f00, 0x7036, 0x0010, 0x7037, 0x0000, 0x660c,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x6004, 0x9086, 0x0040, 0x090c, 0x9657, 0x9085, 0x0001, 0x0020,
+ 0x2c78, 0x600c, 0x2060, 0x08b0, 0x012e, 0x000e, 0x002e, 0x006e,
+ 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x0096, 0x00f6, 0x00e6, 0x00d6,
+ 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x19e6, 0x760c, 0x2660, 0x2678, 0x8cff, 0x0904, 0xa971, 0x6010,
+ 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x9206, 0x1904, 0xa96c, 0x7024,
+ 0x9c06, 0x1520, 0x2069, 0x0100, 0x68c0, 0x9005, 0x0904, 0xa943,
+ 0x080c, 0xa356, 0x68c3, 0x0000, 0x080c, 0xa88b, 0x7027, 0x0000,
+ 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001,
+ 0x0100, 0x080c, 0x2d4e, 0x9006, 0x080c, 0x2d4e, 0x2069, 0x0100,
+ 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x700c, 0x9c36,
+ 0x1110, 0x660c, 0x760e, 0x7008, 0x9c36, 0x1140, 0x2c00, 0x9f36,
+ 0x0118, 0x2f00, 0x700a, 0x0010, 0x700b, 0x0000, 0x660c, 0x0066,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x080c, 0xce7d, 0x1180, 0x080c, 0x3247, 0x080c, 0xce8e, 0x1518,
+ 0x080c, 0xb905, 0x0400, 0x080c, 0xa88b, 0x6824, 0xd084, 0x09b0,
+ 0x6827, 0x0001, 0x0898, 0x080c, 0xce8e, 0x1118, 0x080c, 0xb905,
+ 0x0090, 0x6014, 0x2048, 0x080c, 0xcc84, 0x0168, 0x6020, 0x9086,
+ 0x0003, 0x1508, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c,
+ 0x6d0b, 0x080c, 0xce71, 0x080c, 0xd0fa, 0x080c, 0xaf74, 0x080c,
+ 0xa761, 0x00ce, 0x0804, 0xa8ec, 0x2c78, 0x600c, 0x2060, 0x0804,
+ 0xa8ec, 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x009e, 0x0005, 0x6020, 0x9086, 0x0006, 0x1d20, 0x080c,
+ 0xe6dd, 0x0c08, 0x00d6, 0x080c, 0x9d84, 0x7003, 0x0200, 0x7007,
+ 0x0014, 0x60c3, 0x0014, 0x20e1, 0x0001, 0x2099, 0x1988, 0x20e9,
+ 0x0000, 0x20a1, 0x0250, 0x20a9, 0x0004, 0x4003, 0x7023, 0x0004,
+ 0x7027, 0x7878, 0x080c, 0xa32a, 0x00de, 0x0005, 0x080c, 0x9d84,
+ 0x700b, 0x0800, 0x7814, 0x9084, 0xff00, 0x700e, 0x7814, 0x9084,
+ 0x00ff, 0x7022, 0x782c, 0x7026, 0x7858, 0x9084, 0x00ff, 0x9085,
+ 0x0200, 0x7002, 0x7858, 0x9084, 0xff00, 0x8007, 0x7006, 0x60c2,
+ 0x0804, 0xa32a, 0x00b6, 0x00d6, 0x0016, 0x00d6, 0x2f68, 0x2009,
+ 0x0035, 0x080c, 0xd300, 0x00de, 0x1904, 0xaa1f, 0x080c, 0x9d39,
+ 0x7003, 0x1300, 0x782c, 0x080c, 0xab25, 0x2068, 0x6820, 0x9086,
+ 0x0003, 0x0560, 0x7810, 0x2058, 0xbaa0, 0x080c, 0xaead, 0x11d8,
+ 0x9286, 0x007e, 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffe, 0x0498,
+ 0x9286, 0x007f, 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffd, 0x0458,
+ 0x9284, 0xff80, 0x0180, 0x9286, 0x0080, 0x1128, 0x700b, 0x00ff,
+ 0x700f, 0xfffc, 0x0400, 0x92d8, 0x1000, 0x2b5c, 0xb810, 0x700a,
+ 0xb814, 0x700e, 0x00c0, 0x6098, 0x700e, 0x00a8, 0x080c, 0xaead,
+ 0x1130, 0x7810, 0x2058, 0xb8a0, 0x9082, 0x007e, 0x0250, 0x00d6,
+ 0x2069, 0x181f, 0x2d04, 0x700a, 0x8d68, 0x2d04, 0x700e, 0x00de,
+ 0x0010, 0x6034, 0x700e, 0x7838, 0x7012, 0x783c, 0x7016, 0x60c3,
+ 0x000c, 0x001e, 0x00de, 0x080c, 0xa32a, 0x00be, 0x0005, 0x781b,
+ 0x0001, 0x7803, 0x0006, 0x001e, 0x00de, 0x00be, 0x0005, 0x792c,
+ 0x9180, 0x0008, 0x200c, 0x9186, 0x0006, 0x01c0, 0x9186, 0x0003,
+ 0x0904, 0xaa9a, 0x9186, 0x0005, 0x0904, 0xaa82, 0x9186, 0x0004,
+ 0x05d8, 0x9186, 0x0008, 0x0904, 0xaa8b, 0x7807, 0x0037, 0x782f,
+ 0x0003, 0x7817, 0x1700, 0x080c, 0xab02, 0x0005, 0x080c, 0xaac3,
+ 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x6800, 0x0002,
+ 0xaa63, 0xaa6e, 0xaa65, 0xaa6e, 0xaa6a, 0xaa63, 0xaa63, 0xaa6e,
+ 0xaa6e, 0xaa6e, 0xaa6e, 0xaa63, 0xaa63, 0xaa63, 0xaa63, 0xaa63,
+ 0xaa6e, 0xaa63, 0xaa6e, 0x080c, 0x0dd5, 0x6824, 0xd0e4, 0x0110,
+ 0xd0cc, 0x0110, 0x900e, 0x0010, 0x2009, 0x2000, 0x682c, 0x7022,
+ 0x6830, 0x7026, 0x0804, 0xaabc, 0x080c, 0xaac3, 0x00d6, 0x0026,
+ 0x792c, 0x2168, 0x2009, 0x4000, 0x6a00, 0x9286, 0x0002, 0x1108,
+ 0x900e, 0x04d0, 0x080c, 0xaac3, 0x00d6, 0x0026, 0x792c, 0x2168,
+ 0x2009, 0x4000, 0x0488, 0x04b9, 0x00d6, 0x0026, 0x792c, 0x2168,
+ 0x2009, 0x4000, 0x9286, 0x0005, 0x0118, 0x9286, 0x0002, 0x1108,
+ 0x900e, 0x0410, 0x0441, 0x00d6, 0x0026, 0x792c, 0x2168, 0x6814,
+ 0x6924, 0xc185, 0x6926, 0x0096, 0x2048, 0xa9ac, 0xa834, 0x9112,
+ 0xa9b0, 0xa838, 0x009e, 0x9103, 0x7022, 0x7226, 0x792c, 0x9180,
+ 0x0000, 0x2004, 0x908e, 0x0002, 0x0130, 0x908e, 0x0004, 0x0118,
+ 0x2009, 0x4000, 0x0008, 0x900e, 0x712a, 0x60c3, 0x0018, 0x002e,
+ 0x00de, 0x0804, 0xa32a, 0x00b6, 0x0036, 0x0046, 0x0056, 0x0066,
+ 0x080c, 0x9d84, 0x9006, 0x7003, 0x0200, 0x7938, 0x710a, 0x793c,
+ 0x710e, 0x7810, 0x2058, 0xb8a0, 0x080c, 0xaead, 0x1118, 0x9092,
+ 0x007e, 0x0268, 0x00d6, 0x2069, 0x181f, 0x2d2c, 0x8d68, 0x2d34,
+ 0x90d8, 0x1000, 0x2b5c, 0xbb10, 0xbc14, 0x00de, 0x0028, 0x901e,
+ 0x6498, 0x2029, 0x0000, 0x6634, 0x782c, 0x9080, 0x0008, 0x2004,
+ 0x9086, 0x0003, 0x1128, 0x7512, 0x7616, 0x731a, 0x741e, 0x0020,
+ 0x7312, 0x7416, 0x751a, 0x761e, 0x006e, 0x005e, 0x004e, 0x003e,
+ 0x00be, 0x0005, 0x080c, 0x9d84, 0x7003, 0x0100, 0x782c, 0x700a,
+ 0x7814, 0x700e, 0x700e, 0x60c3, 0x0008, 0x0804, 0xa32a, 0x080c,
+ 0x9d30, 0x7003, 0x1400, 0x7838, 0x700a, 0x0079, 0x783c, 0x700e,
+ 0x782c, 0x7012, 0x7830, 0x7016, 0x7834, 0x9084, 0x00ff, 0x8007,
+ 0x701a, 0x60c3, 0x0010, 0x0804, 0xa32a, 0x00e6, 0x2071, 0x0240,
+ 0x0006, 0x00f6, 0x2078, 0x7810, 0x00b6, 0x2058, 0xb8cc, 0xd084,
+ 0x0120, 0x7844, 0x702a, 0x7848, 0x702e, 0x00be, 0x00fe, 0x000e,
+ 0x00ee, 0x0005, 0x080c, 0x9d7b, 0x7003, 0x0100, 0x782c, 0x700a,
+ 0x7814, 0x700e, 0x60c3, 0x0008, 0x0804, 0xa32a, 0x0021, 0x60c3,
+ 0x0000, 0x0804, 0xa32a, 0x00d6, 0x080c, 0xabfe, 0xb810, 0x9085,
+ 0x0300, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x687c, 0x700a,
+ 0x6880, 0x700e, 0x7013, 0x0819, 0x080c, 0xa318, 0x721a, 0x2f10,
+ 0x7222, 0x7a08, 0x7226, 0x2071, 0x024c, 0x00de, 0x0005, 0x00a9,
+ 0x7914, 0x712a, 0x60c3, 0x0000, 0x60a7, 0x9575, 0x0026, 0x080c,
+ 0x2be3, 0x0228, 0x2011, 0x0101, 0x2204, 0xc0c5, 0x2012, 0x002e,
+ 0x080c, 0xa34d, 0x080c, 0x862d, 0x0005, 0x0036, 0x0096, 0x00d6,
+ 0x00e6, 0x7858, 0x2048, 0xaa7c, 0x9296, 0x00c0, 0x9294, 0xfffd,
+ 0xaa7e, 0xaa80, 0x9294, 0x0300, 0xaa82, 0xa96c, 0x9194, 0x00ff,
+ 0xab74, 0x9384, 0x00ff, 0x908d, 0xc200, 0xa96e, 0x9384, 0xff00,
+ 0x9215, 0xaa76, 0xa870, 0xaa78, 0xa87a, 0xaa72, 0x00d6, 0x2069,
+ 0x0200, 0x080c, 0xabfe, 0x00de, 0x20e9, 0x0000, 0x20a1, 0x0240,
+ 0x20a9, 0x000a, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098,
+ 0x4003, 0x60a3, 0x0035, 0xaa68, 0x9294, 0x7000, 0x9286, 0x3000,
+ 0x0110, 0x60a3, 0x0037, 0x00ee, 0x00de, 0x009e, 0x003e, 0x0005,
+ 0x900e, 0x7814, 0x0096, 0x2048, 0xa87c, 0xd0fc, 0x01c0, 0x9084,
+ 0x0003, 0x11a8, 0x2001, 0x180c, 0x2004, 0xd0bc, 0x0180, 0x7824,
+ 0xd0cc, 0x1168, 0xd0c4, 0x1158, 0xa8a8, 0x9005, 0x1140, 0x2001,
+ 0x180c, 0x200c, 0xc1d5, 0x2102, 0x2009, 0x19b1, 0x210c, 0x009e,
+ 0x918d, 0x0092, 0x0010, 0x2009, 0x0096, 0x60ab, 0x0036, 0x6116,
+ 0x0005, 0x2009, 0x0009, 0x00a0, 0x2009, 0x000a, 0x0088, 0x2009,
+ 0x000b, 0x0070, 0x2009, 0x000c, 0x0058, 0x2009, 0x000d, 0x0040,
+ 0x2009, 0x000e, 0x0028, 0x2009, 0x000f, 0x0010, 0x2009, 0x0008,
+ 0x6912, 0x0005, 0x080c, 0x9d39, 0x0016, 0x0026, 0x0096, 0x00d6,
+ 0x7814, 0x2048, 0x7013, 0x0138, 0x2001, 0x1837, 0x2004, 0x9084,
+ 0x0028, 0x1138, 0x2001, 0x197b, 0x2004, 0x9086, 0xaaaa, 0x1904,
+ 0xaca3, 0x7003, 0x5400, 0x00c6, 0x2061, 0x1800, 0x607c, 0x9084,
+ 0x00ff, 0xa998, 0x810f, 0x918c, 0xff00, 0x9105, 0x700a, 0x6080,
+ 0x700e, 0xa998, 0x918c, 0xff00, 0x7112, 0x20a9, 0x0004, 0x2009,
+ 0x1805, 0x2e10, 0x9290, 0x0006, 0x2104, 0x2012, 0x8108, 0x8210,
+ 0x1f04, 0xac34, 0x20a9, 0x0004, 0x2009, 0x1801, 0x2104, 0x2012,
+ 0x8108, 0x8210, 0x1f04, 0xac3e, 0xa860, 0x20e0, 0xa85c, 0x9080,
+ 0x0029, 0x2098, 0x2009, 0x0006, 0x20a9, 0x0001, 0x4002, 0x8007,
+ 0x2012, 0x8210, 0x8109, 0x1dc0, 0x00d6, 0x2069, 0x0200, 0x080c,
+ 0xabe9, 0x00de, 0x2071, 0x0240, 0x2011, 0x0240, 0x2009, 0x0002,
+ 0x20a9, 0x0001, 0x4002, 0x8007, 0x2012, 0x8210, 0x8109, 0x1dc0,
+ 0x2009, 0x0008, 0x20a9, 0x0001, 0x4002, 0x8007, 0x2012, 0x8210,
+ 0x8109, 0x1dc0, 0xa85c, 0x9080, 0x0031, 0x2098, 0x2009, 0x0008,
+ 0x20a9, 0x0001, 0x4002, 0x8007, 0x2012, 0x8210, 0x8109, 0x1dc0,
+ 0x00ce, 0x60c3, 0x004c, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x2001,
+ 0x1837, 0x2004, 0x9084, 0x0028, 0x1168, 0x080c, 0x743e, 0x0150,
+ 0x6028, 0xc0bd, 0x602a, 0x6014, 0x9084, 0x1804, 0x9085, 0x0029,
+ 0x6016, 0x0010, 0x080c, 0xa32a, 0x080c, 0x862d, 0x00de, 0x009e,
+ 0x002e, 0x001e, 0x0005, 0x00e6, 0x2071, 0x0240, 0x2001, 0x2200,
+ 0x9085, 0x00ff, 0x7002, 0x7007, 0xffff, 0x2071, 0x0100, 0x709b,
+ 0x00ff, 0x00ee, 0x0804, 0xac19, 0x080c, 0x9d39, 0x0016, 0x0026,
+ 0x0096, 0x00d6, 0x7814, 0x2048, 0x7013, 0x0138, 0x7003, 0x5500,
+ 0x00c6, 0xa89c, 0x9084, 0x00ff, 0xa998, 0x810f, 0x918c, 0xff00,
+ 0x9105, 0x700a, 0xa99c, 0x918c, 0xff00, 0xa8a0, 0x9084, 0x00ff,
+ 0x9105, 0x700e, 0xa998, 0x918c, 0xff00, 0x2061, 0x1800, 0x607c,
+ 0x9084, 0x00ff, 0x910d, 0x7112, 0x6180, 0x7116, 0x2009, 0x0008,
+ 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0029, 0x2098, 0x2e10, 0x9290,
+ 0x0006, 0x20a9, 0x0001, 0x4002, 0x8007, 0x2012, 0x8210, 0x8109,
+ 0x1dc0, 0x20a9, 0x0004, 0x2009, 0x1805, 0x2104, 0x2012, 0x8108,
+ 0x8210, 0x1f04, 0xacf5, 0x20a9, 0x0002, 0x2009, 0x1801, 0x2104,
+ 0x2012, 0x8108, 0x8210, 0x1f04, 0xacff, 0x00d6, 0x0016, 0x2069,
+ 0x0200, 0x080c, 0xabe9, 0x001e, 0x00de, 0x2071, 0x0240, 0x20a9,
+ 0x0002, 0x2009, 0x1803, 0x2011, 0x0240, 0x2104, 0x2012, 0x8108,
+ 0x8210, 0x1f04, 0xad15, 0x2009, 0x0008, 0x4002, 0x8007, 0x2012,
+ 0x8210, 0x8109, 0x1dd0, 0x9006, 0x20a9, 0x0008, 0x2012, 0x8210,
+ 0x1f04, 0xad26, 0x00ce, 0x60c3, 0x004c, 0x60a3, 0x0056, 0x60a7,
+ 0x9575, 0x080c, 0xa32a, 0x080c, 0x862d, 0x00de, 0x009e, 0x002e,
+ 0x001e, 0x0005, 0x00d6, 0x9290, 0x0018, 0x8214, 0x20e9, 0x0000,
+ 0x2069, 0x0200, 0x6813, 0x0000, 0x22a8, 0x9284, 0x00e0, 0x0128,
+ 0x20a9, 0x0020, 0x9292, 0x0020, 0x0008, 0x9016, 0x20a1, 0x0240,
+ 0x9006, 0x4004, 0x82ff, 0x0120, 0x6810, 0x8000, 0x6812, 0x0c60,
+ 0x00de, 0x0005, 0x00d6, 0x0096, 0x6014, 0x2048, 0xa878, 0x6056,
+ 0x9006, 0xa836, 0xa83a, 0xa99c, 0xa946, 0xa84a, 0x6023, 0x0003,
+ 0x6007, 0x0040, 0x6003, 0x0003, 0x600b, 0xffff, 0xa817, 0x0001,
+ 0xa842, 0xa83e, 0x2900, 0xa85a, 0xa813, 0x20cc, 0x080c, 0x9216,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x9891, 0x012e, 0x009e, 0x00de,
+ 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x00a6, 0x0096, 0x0066,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0x19e6, 0x760c, 0x2660, 0x2678,
+ 0x8cff, 0x0904, 0xae0d, 0x7024, 0x9c06, 0x1520, 0x2069, 0x0100,
+ 0x68c0, 0x9005, 0x0904, 0xaddf, 0x080c, 0xa356, 0x68c3, 0x0000,
+ 0x080c, 0xa88b, 0x7027, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04,
+ 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2d4e, 0x9006,
+ 0x080c, 0x2d4e, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827,
+ 0x0001, 0x003e, 0x700c, 0x9c36, 0x1110, 0x660c, 0x760e, 0x7008,
+ 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x700a, 0x0010,
+ 0x700b, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e,
+ 0x0008, 0x2678, 0x600f, 0x0000, 0x080c, 0xce7d, 0x1180, 0x080c,
+ 0x3247, 0x080c, 0xce8e, 0x1518, 0x080c, 0xb905, 0x0400, 0x080c,
+ 0xa88b, 0x6824, 0xd084, 0x09b0, 0x6827, 0x0001, 0x0898, 0x080c,
+ 0xce8e, 0x1118, 0x080c, 0xb905, 0x0090, 0x6014, 0x2048, 0x080c,
+ 0xcc84, 0x0168, 0x6020, 0x9086, 0x0003, 0x1520, 0xa867, 0x0103,
+ 0xab7a, 0xa877, 0x0000, 0x080c, 0x6d17, 0x080c, 0xce71, 0x080c,
+ 0xd0fa, 0x080c, 0xaf74, 0x080c, 0xa761, 0x00ce, 0x0804, 0xad90,
+ 0x2c78, 0x600c, 0x2060, 0x0804, 0xad90, 0x700f, 0x0000, 0x700b,
+ 0x0000, 0x012e, 0x006e, 0x009e, 0x00ae, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x6020, 0x9086, 0x0006, 0x1d08, 0x080c, 0xe6dd,
+ 0x08f0, 0x00d6, 0x0156, 0x080c, 0x9d84, 0x7a14, 0x82ff, 0x0138,
+ 0x7003, 0x0100, 0x700b, 0x0003, 0x60c3, 0x0008, 0x0490, 0x7003,
+ 0x0200, 0x7007, 0x0000, 0x2069, 0x1800, 0x901e, 0x6800, 0x9086,
+ 0x0004, 0x1110, 0xc38d, 0x0060, 0x080c, 0x743e, 0x1110, 0xc3ad,
+ 0x0008, 0xc3a5, 0x6adc, 0xd29c, 0x1110, 0xd2ac, 0x0108, 0xc39d,
+ 0x730e, 0x080c, 0x8696, 0x20a9, 0x0006, 0x2011, 0xffec, 0x2019,
+ 0xffed, 0x2071, 0x0250, 0x2305, 0x2072, 0x8e70, 0x2205, 0x2072,
+ 0x8e70, 0x9398, 0x0002, 0x9290, 0x0002, 0x1f04, 0xae53, 0x60c3,
+ 0x0020, 0x080c, 0xa32a, 0x015e, 0x00de, 0x0005, 0x0156, 0x080c,
+ 0x9d84, 0x7a14, 0x82ff, 0x0168, 0x9286, 0xffff, 0x0118, 0x9282,
+ 0x000e, 0x1238, 0x7003, 0x0100, 0x700b, 0x0003, 0x60c3, 0x0008,
+ 0x0488, 0x7003, 0x0200, 0x7007, 0x001c, 0x700f, 0x0001, 0x2011,
+ 0x19bc, 0x2204, 0x8007, 0x701a, 0x8210, 0x2204, 0x8007, 0x701e,
+ 0x0421, 0x1120, 0xb8a0, 0x9082, 0x007f, 0x0248, 0x2001, 0x181f,
+ 0x2004, 0x7022, 0x2001, 0x1820, 0x2004, 0x7026, 0x0030, 0x2001,
+ 0x1818, 0x2004, 0x9084, 0x00ff, 0x7026, 0x20a9, 0x0004, 0x20e1,
+ 0x0001, 0x2099, 0x1805, 0x20e9, 0x0000, 0x20a1, 0x0256, 0x4003,
+ 0x60c3, 0x001c, 0x015e, 0x0804, 0xa32a, 0x0006, 0x2001, 0x1837,
+ 0x2004, 0xd0ac, 0x000e, 0x0005, 0x2011, 0x0003, 0x080c, 0xa722,
+ 0x2011, 0x0002, 0x080c, 0xa72c, 0x080c, 0xa636, 0x0036, 0x901e,
+ 0x080c, 0xa6ac, 0x003e, 0x0005, 0x080c, 0x337d, 0x0188, 0x0016,
+ 0x00b6, 0x00c6, 0x7010, 0x9085, 0x0020, 0x7012, 0x2009, 0x007e,
+ 0x080c, 0x6699, 0xb85c, 0xc0ac, 0xb85e, 0x00ce, 0x00be, 0x001e,
+ 0x0005, 0x2071, 0x188d, 0x7000, 0x9005, 0x0140, 0x2001, 0x0976,
+ 0x2071, 0x1800, 0x7076, 0x707a, 0x706b, 0xffe0, 0x2071, 0x1800,
+ 0x7074, 0x7056, 0x705b, 0x1cd0, 0x0005, 0x00e6, 0x0126, 0x2071,
+ 0x1800, 0x2091, 0x8000, 0x7554, 0x9582, 0x0010, 0x0608, 0x7058,
+ 0x2060, 0x6000, 0x9086, 0x0000, 0x0148, 0x9ce0, 0x0018, 0x7068,
+ 0x9c02, 0x1208, 0x0cb0, 0x2061, 0x1cd0, 0x0c98, 0x6003, 0x0008,
+ 0x8529, 0x7556, 0x9ca8, 0x0018, 0x7068, 0x9502, 0x1230, 0x755a,
+ 0x9085, 0x0001, 0x012e, 0x00ee, 0x0005, 0x705b, 0x1cd0, 0x0cc0,
+ 0x9006, 0x0cc0, 0x00e6, 0x2071, 0x1800, 0x7554, 0x9582, 0x0010,
+ 0x0600, 0x7058, 0x2060, 0x6000, 0x9086, 0x0000, 0x0148, 0x9ce0,
+ 0x0018, 0x7068, 0x9c02, 0x1208, 0x0cb0, 0x2061, 0x1cd0, 0x0c98,
+ 0x6003, 0x0008, 0x8529, 0x7556, 0x9ca8, 0x0018, 0x7068, 0x9502,
+ 0x1228, 0x755a, 0x9085, 0x0001, 0x00ee, 0x0005, 0x705b, 0x1cd0,
+ 0x0cc8, 0x9006, 0x0cc8, 0x9c82, 0x1cd0, 0x0a0c, 0x0dd5, 0x2001,
+ 0x181a, 0x2004, 0x9c02, 0x1a0c, 0x0dd5, 0x9006, 0x6006, 0x600a,
+ 0x600e, 0x6016, 0x601a, 0x6012, 0x6023, 0x0000, 0x6003, 0x0000,
+ 0x601e, 0x6056, 0x605a, 0x6026, 0x602a, 0x602e, 0x6032, 0x6036,
+ 0x603a, 0x603e, 0x6042, 0x602a, 0x2061, 0x1800, 0x6054, 0x8000,
+ 0x6056, 0x9086, 0x0001, 0x0108, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x9763, 0x012e, 0x0cc0, 0x0006, 0x6000, 0x9086, 0x0000,
+ 0x01b0, 0x601c, 0xd084, 0x190c, 0x1aa1, 0x6017, 0x0000, 0x6023,
+ 0x0007, 0x2001, 0x1985, 0x2004, 0x0006, 0x9082, 0x0051, 0x000e,
+ 0x0208, 0x8004, 0x601a, 0x080c, 0xe997, 0x6043, 0x0000, 0x000e,
+ 0x0005, 0x00e6, 0x0126, 0x2071, 0x1800, 0x2091, 0x8000, 0x7554,
+ 0x9582, 0x0001, 0x0608, 0x7058, 0x2060, 0x6000, 0x9086, 0x0000,
+ 0x0148, 0x9ce0, 0x0018, 0x7068, 0x9c02, 0x1208, 0x0cb0, 0x2061,
+ 0x1cd0, 0x0c98, 0x6003, 0x0008, 0x8529, 0x7556, 0x9ca8, 0x0018,
+ 0x7068, 0x9502, 0x1230, 0x755a, 0x9085, 0x0001, 0x012e, 0x00ee,
+ 0x0005, 0x705b, 0x1cd0, 0x0cc0, 0x9006, 0x0cc0, 0x6020, 0x9084,
+ 0x000f, 0x0002, 0xafd1, 0xafda, 0xaff5, 0xb010, 0xd3ae, 0xd3cb,
+ 0xd3e6, 0xafd1, 0xafda, 0x8e43, 0xb02c, 0xafd1, 0xafd1, 0xafd1,
+ 0xafd1, 0x9186, 0x0013, 0x1128, 0x080c, 0x9657, 0x080c, 0x9763,
+ 0x0005, 0x0005, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c, 0x0dd5,
+ 0x0013, 0x006e, 0x0005, 0xaff3, 0xb76f, 0xb94c, 0xaff3, 0xb9e2,
+ 0xb30f, 0xaff3, 0xaff3, 0xb6f1, 0xbf49, 0xaff3, 0xaff3, 0xaff3,
+ 0xaff3, 0xaff3, 0xaff3, 0x080c, 0x0dd5, 0x0066, 0x6000, 0x90b2,
+ 0x0016, 0x1a0c, 0x0dd5, 0x0013, 0x006e, 0x0005, 0xb00e, 0xc630,
+ 0xb00e, 0xb00e, 0xb00e, 0xb00e, 0xb00e, 0xb00e, 0xc5c7, 0xc7b2,
+ 0xb00e, 0xc671, 0xc6f0, 0xc671, 0xc6f0, 0xb00e, 0x080c, 0x0dd5,
+ 0x6000, 0x9082, 0x0016, 0x1a0c, 0x0dd5, 0x6000, 0x0002, 0xb02a,
+ 0xbf90, 0xc075, 0xc1a5, 0xc354, 0xb02a, 0xb02a, 0xb02a, 0xbf64,
+ 0xc553, 0xc556, 0xb02a, 0xb02a, 0xb02a, 0xb02a, 0xc585, 0xb02a,
+ 0xb02a, 0xb02a, 0x080c, 0x0dd5, 0x0066, 0x6000, 0x90b2, 0x0016,
+ 0x1a0c, 0x0dd5, 0x0013, 0x006e, 0x0005, 0xb045, 0xb045, 0xb088,
+ 0xb127, 0xb1bc, 0xb045, 0xb045, 0xb045, 0xb047, 0xb045, 0xb045,
+ 0xb045, 0xb045, 0xb045, 0xb045, 0xb045, 0x080c, 0x0dd5, 0x9186,
+ 0x004c, 0x0588, 0x9186, 0x0003, 0x190c, 0x0dd5, 0x0096, 0x601c,
+ 0xc0ed, 0x601e, 0x6003, 0x0003, 0x6106, 0x6014, 0x2048, 0xa87c,
+ 0x9084, 0xa000, 0xc0b5, 0xa87e, 0xa8ac, 0xa846, 0xa8b0, 0xa84a,
+ 0x9006, 0xa836, 0xa83a, 0xa884, 0x9092, 0x199a, 0x0210, 0x2001,
+ 0x1999, 0x8003, 0x8013, 0x8213, 0x9210, 0x621a, 0x009e, 0x2c10,
+ 0x080c, 0x1beb, 0x080c, 0x9216, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x9891, 0x012e, 0x0005, 0x6010, 0x00b6, 0x2058, 0xbca0, 0x00be,
+ 0x2c00, 0x080c, 0xb1de, 0x080c, 0xd3a0, 0x6003, 0x0007, 0x0005,
+ 0x00d6, 0x0096, 0x00f6, 0x2079, 0x1800, 0x7a90, 0x6014, 0x2048,
+ 0xa87c, 0xd0ec, 0x1110, 0x9290, 0x0018, 0xac78, 0xc4fc, 0x0046,
+ 0xa8e0, 0x9005, 0x1140, 0xa8dc, 0x921a, 0x0140, 0x0220, 0xa87b,
+ 0x0007, 0x2010, 0x0028, 0xa87b, 0x0015, 0x0010, 0xa87b, 0x0000,
+ 0x8214, 0xa883, 0x0000, 0xaa02, 0x0006, 0x0016, 0x0026, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x2400, 0x9005, 0x1108, 0x009a, 0x2100,
+ 0x9086, 0x0015, 0x1118, 0x2001, 0x0001, 0x0038, 0x2100, 0x9086,
+ 0x0016, 0x0118, 0x2001, 0x0001, 0x002a, 0x94a4, 0x0007, 0x8423,
+ 0x9405, 0x0002, 0xb0ef, 0xb0ef, 0xb0ea, 0xb0ed, 0xb0ef, 0xb0e7,
+ 0xb0da, 0xb0da, 0xb0da, 0xb0da, 0xb0da, 0xb0da, 0xb0da, 0xb0da,
+ 0xb0da, 0xb0da, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x002e, 0x001e,
+ 0x000e, 0x004e, 0x00fe, 0x009e, 0x00de, 0x080c, 0x0dd5, 0x080c,
+ 0xbba1, 0x0028, 0x080c, 0xbc86, 0x0010, 0x080c, 0xbd7c, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x002e, 0x001e, 0x2c00, 0xa896, 0x000e,
+ 0x080c, 0xb29c, 0x0530, 0xa804, 0xa80e, 0x00a6, 0x2050, 0xb100,
+ 0x00ae, 0x8006, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0,
+ 0x9080, 0x0002, 0xaacc, 0xabd0, 0xacd4, 0xadd8, 0x2031, 0x0000,
+ 0x2041, 0x125d, 0x080c, 0xb45d, 0x0160, 0x000e, 0x9005, 0x0120,
+ 0x00fe, 0x009e, 0x00de, 0x0005, 0x00fe, 0x009e, 0x00de, 0x0804,
+ 0xaf43, 0x2001, 0x002c, 0x900e, 0x080c, 0xb302, 0x0c70, 0x91b6,
+ 0x0015, 0x0170, 0x91b6, 0x0016, 0x0158, 0x91b2, 0x0047, 0x0a0c,
+ 0x0dd5, 0x91b2, 0x0050, 0x1a0c, 0x0dd5, 0x9182, 0x0047, 0x00ca,
+ 0x2001, 0x0109, 0x2004, 0xd08c, 0x0198, 0x0126, 0x2091, 0x2800,
+ 0x0006, 0x0016, 0x0026, 0x080c, 0x9163, 0x002e, 0x001e, 0x000e,
+ 0x012e, 0xa001, 0x6000, 0x9086, 0x0002, 0x1110, 0x0804, 0xb088,
+ 0x0005, 0xb15a, 0xb15a, 0xb15c, 0xb192, 0xb15a, 0xb15a, 0xb15a,
+ 0xb15a, 0xb1a5, 0x080c, 0x0dd5, 0x00d6, 0x0016, 0x0096, 0x080c,
+ 0x9713, 0x080c, 0x9891, 0x6003, 0x0004, 0x6114, 0x2148, 0xa87c,
+ 0xd0fc, 0x01c0, 0xa878, 0xc0fc, 0x9005, 0x1158, 0xa894, 0x9005,
+ 0x0140, 0x2001, 0x0000, 0x900e, 0x080c, 0xb302, 0x080c, 0xaf43,
+ 0x00a8, 0x6003, 0x0002, 0xa8a4, 0xa9a8, 0x9105, 0x1178, 0xa8ae,
+ 0xa8b2, 0x0c78, 0xa87f, 0x0020, 0xa88c, 0xa88a, 0xa8a4, 0xa8ae,
+ 0xa8a8, 0xa8b2, 0xa8c7, 0x0000, 0xa8cb, 0x0000, 0x009e, 0x001e,
+ 0x00de, 0x0005, 0x080c, 0x9713, 0x00d6, 0x0096, 0x6114, 0x2148,
+ 0x080c, 0xcc86, 0x0120, 0xa87b, 0x0006, 0x080c, 0x6d17, 0x009e,
+ 0x00de, 0x080c, 0xaf43, 0x0804, 0x9891, 0x080c, 0x9713, 0x080c,
+ 0x321e, 0x080c, 0xd39d, 0x00d6, 0x0096, 0x6114, 0x2148, 0x080c,
+ 0xcc86, 0x0120, 0xa87b, 0x0029, 0x080c, 0x6d17, 0x009e, 0x00de,
+ 0x080c, 0xaf43, 0x0804, 0x9891, 0x9182, 0x0047, 0x0002, 0xb1cc,
+ 0xb1ce, 0xb1cc, 0xb1cc, 0xb1cc, 0xb1cc, 0xb1cc, 0xb1cc, 0xb1cc,
+ 0xb1cc, 0xb1cc, 0xb1cc, 0xb1ce, 0x080c, 0x0dd5, 0x00d6, 0x0096,
+ 0x601f, 0x0000, 0x6114, 0x2148, 0xa87b, 0x0000, 0xa883, 0x0000,
+ 0x080c, 0x6d17, 0x009e, 0x00de, 0x0804, 0xaf43, 0x0026, 0x0036,
+ 0x0056, 0x0066, 0x0096, 0x00a6, 0x00f6, 0x0006, 0x080c, 0x0fff,
+ 0x000e, 0x090c, 0x0dd5, 0xa960, 0x21e8, 0xa95c, 0x9188, 0x0019,
+ 0x21a0, 0x900e, 0x20a9, 0x0020, 0x4104, 0xa87a, 0x2079, 0x1800,
+ 0x7990, 0x9188, 0x0018, 0x918c, 0x0fff, 0xa972, 0xac76, 0x2950,
+ 0x00a6, 0x2001, 0x0205, 0x2003, 0x0000, 0x901e, 0x2029, 0x0001,
+ 0x9182, 0x0034, 0x1228, 0x2011, 0x001f, 0x080c, 0xc837, 0x04c0,
+ 0x2130, 0x2009, 0x0034, 0x2011, 0x001f, 0x080c, 0xc837, 0x96b2,
+ 0x0034, 0xb004, 0x904d, 0x0110, 0x080c, 0x0fb1, 0x080c, 0x0fff,
+ 0x01d0, 0x8528, 0xa867, 0x0110, 0xa86b, 0x0000, 0x2920, 0xb406,
+ 0x968a, 0x003d, 0x1230, 0x2608, 0x2011, 0x001b, 0x080c, 0xc837,
+ 0x00b8, 0x96b2, 0x003c, 0x2009, 0x003c, 0x2950, 0x2011, 0x001b,
+ 0x080c, 0xc837, 0x0c18, 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae,
+ 0x852f, 0x95ad, 0x0050, 0xb566, 0xb070, 0xc0fd, 0xb072, 0x0048,
+ 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f, 0x95ad, 0x0050,
+ 0xb566, 0x2a48, 0xa804, 0xa807, 0x0000, 0x0006, 0x080c, 0x6d17,
+ 0x000e, 0x2048, 0x9005, 0x1db0, 0x00fe, 0x00ae, 0x009e, 0x006e,
+ 0x005e, 0x003e, 0x002e, 0x0005, 0x00d6, 0x00f6, 0x0096, 0x0006,
+ 0x080c, 0x0fff, 0x000e, 0x090c, 0x0dd5, 0xa960, 0x21e8, 0xa95c,
+ 0x9188, 0x0019, 0x21a0, 0x900e, 0x20a9, 0x0020, 0x4104, 0xaa66,
+ 0xa87a, 0x2079, 0x1800, 0x7990, 0x810c, 0x9188, 0x000c, 0x9182,
+ 0x001a, 0x0210, 0x2009, 0x001a, 0x21a8, 0x810b, 0xa972, 0xac76,
+ 0x2e98, 0xa85c, 0x9080, 0x001f, 0x20a0, 0x2001, 0x0205, 0x200c,
+ 0x918d, 0x0080, 0x2102, 0x4003, 0x2003, 0x0000, 0x080c, 0x6d17,
+ 0x009e, 0x00fe, 0x00de, 0x0005, 0x0016, 0x00d6, 0x00f6, 0x0096,
+ 0x0016, 0x2001, 0x0205, 0x200c, 0x918d, 0x0080, 0x2102, 0x001e,
+ 0x2079, 0x0200, 0x2e98, 0xa87c, 0xd0ec, 0x0118, 0x9e80, 0x000c,
+ 0x2098, 0x2021, 0x003e, 0x901e, 0x9282, 0x0020, 0x0218, 0x2011,
+ 0x0020, 0x2018, 0x9486, 0x003e, 0x1170, 0x0096, 0x080c, 0x0fff,
+ 0x2900, 0x009e, 0x05c0, 0xa806, 0x2048, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0002, 0x20a0, 0x3300, 0x908e, 0x0260, 0x0140, 0x2009,
+ 0x0280, 0x9102, 0x920a, 0x0218, 0x2010, 0x2100, 0x9318, 0x2200,
+ 0x9402, 0x1228, 0x2400, 0x9202, 0x2410, 0x9318, 0x9006, 0x2020,
+ 0x22a8, 0xa800, 0x9200, 0xa802, 0x20e1, 0x0000, 0x4003, 0x83ff,
+ 0x0180, 0x3300, 0x9086, 0x0280, 0x1130, 0x7814, 0x8000, 0x9085,
+ 0x0080, 0x7816, 0x2e98, 0x2310, 0x84ff, 0x0904, 0xb2b1, 0x0804,
+ 0xb2b3, 0x9085, 0x0001, 0x7817, 0x0000, 0x009e, 0x00fe, 0x00de,
+ 0x001e, 0x0005, 0x00d6, 0x0036, 0x0096, 0x6314, 0x2348, 0xa87a,
+ 0xa982, 0x080c, 0x6d0b, 0x009e, 0x003e, 0x00de, 0x0005, 0x91b6,
+ 0x0015, 0x1118, 0x080c, 0xaf43, 0x0030, 0x91b6, 0x0016, 0x190c,
+ 0x0dd5, 0x080c, 0xaf43, 0x0005, 0x20a9, 0x000e, 0x20e1, 0x0000,
+ 0x2e98, 0x6014, 0x0096, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x20a0,
+ 0x009e, 0x4003, 0x0136, 0x9080, 0x001b, 0x20a0, 0x2011, 0x0006,
+ 0x20a9, 0x0001, 0x3418, 0x8318, 0x23a0, 0x4003, 0x3318, 0x8318,
+ 0x2398, 0x8211, 0x1db8, 0x2011, 0x0006, 0x013e, 0x20a0, 0x3318,
+ 0x8318, 0x2398, 0x4003, 0x3418, 0x8318, 0x23a0, 0x8211, 0x1db8,
+ 0x0096, 0x080c, 0xcc86, 0x0130, 0x6014, 0x2048, 0xa807, 0x0000,
+ 0xa867, 0x0103, 0x009e, 0x0804, 0xaf43, 0x0096, 0x00d6, 0x0036,
+ 0x7330, 0x9386, 0x0200, 0x11a8, 0x6010, 0x00b6, 0x2058, 0xb8cf,
+ 0x0000, 0x00be, 0x6014, 0x9005, 0x0130, 0x2048, 0xa807, 0x0000,
+ 0xa867, 0x0103, 0xab32, 0x080c, 0xaf43, 0x003e, 0x00de, 0x009e,
+ 0x0005, 0x0011, 0x1d48, 0x0cc8, 0x0006, 0x0016, 0x080c, 0xd388,
+ 0x0188, 0x6014, 0x9005, 0x1170, 0x600b, 0x0003, 0x601b, 0x0000,
+ 0x6043, 0x0000, 0x2009, 0x0022, 0x080c, 0xb747, 0x9006, 0x001e,
+ 0x000e, 0x0005, 0x9085, 0x0001, 0x0cd0, 0x0096, 0x0016, 0x20a9,
+ 0x0014, 0x9e80, 0x000c, 0x20e1, 0x0000, 0x2098, 0x6014, 0x2048,
+ 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0002, 0x20a0, 0x4003, 0x2001,
+ 0x0205, 0x2003, 0x0001, 0x2099, 0x0260, 0x20a9, 0x0016, 0x4003,
+ 0x20a9, 0x000a, 0xa804, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0002, 0x20a0, 0x4003, 0x2001, 0x0205, 0x2003, 0x0002, 0x2099,
+ 0x0260, 0x20a9, 0x0020, 0x4003, 0x2003, 0x0000, 0x6014, 0x2048,
+ 0xa800, 0x2048, 0xa867, 0x0103, 0x080c, 0xaf43, 0x001e, 0x009e,
+ 0x0005, 0x0096, 0x0016, 0x900e, 0x7030, 0x9086, 0x0100, 0x0140,
+ 0x7038, 0x9084, 0x00ff, 0x800c, 0x703c, 0x9084, 0x00ff, 0x8004,
+ 0x9080, 0x0004, 0x9108, 0x810b, 0x2011, 0x0002, 0x2019, 0x000c,
+ 0x6014, 0x2048, 0x080c, 0xc837, 0x080c, 0xcc86, 0x0140, 0x6014,
+ 0x2048, 0xa807, 0x0000, 0xa864, 0xa8e2, 0xa867, 0x0103, 0x080c,
+ 0xaf43, 0x001e, 0x009e, 0x0005, 0x0016, 0x2009, 0x0000, 0x7030,
+ 0x9086, 0x0200, 0x0110, 0x2009, 0x0001, 0x0096, 0x6014, 0x904d,
+ 0x090c, 0x0dd5, 0xa97a, 0x080c, 0x6d17, 0x009e, 0x080c, 0xaf43,
+ 0x001e, 0x0005, 0x0016, 0x0096, 0x7030, 0x9086, 0x0100, 0x1118,
+ 0x2009, 0x0004, 0x0010, 0x7034, 0x800c, 0x810b, 0x2011, 0x000c,
+ 0x2019, 0x000c, 0x6014, 0x2048, 0xa804, 0x0096, 0x9005, 0x0108,
+ 0x2048, 0x080c, 0xc837, 0x009e, 0x080c, 0xcc86, 0x0148, 0xa804,
+ 0x9005, 0x1158, 0xa807, 0x0000, 0xa864, 0xa8e2, 0xa867, 0x0103,
+ 0x080c, 0xaf43, 0x009e, 0x001e, 0x0005, 0x0086, 0x2040, 0xa030,
+ 0x8007, 0x9086, 0x0100, 0x1118, 0x080c, 0xb905, 0x00e0, 0xa034,
+ 0x8007, 0x800c, 0x8806, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x000c, 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897,
+ 0x4000, 0xaaa0, 0xab9c, 0xaca8, 0xada4, 0x2031, 0x0000, 0x2041,
+ 0x1243, 0x0019, 0x0d08, 0x008e, 0x0898, 0x0096, 0x0006, 0x080c,
+ 0x0fff, 0x000e, 0x01b0, 0xa8ab, 0x0dcb, 0xa876, 0x000e, 0xa8a2,
+ 0x0006, 0xae6a, 0x2800, 0xa89e, 0xa97a, 0xaf72, 0xaa8e, 0xab92,
+ 0xac96, 0xad9a, 0x0086, 0x2940, 0x080c, 0x10e9, 0x008e, 0x9085,
+ 0x0001, 0x009e, 0x0005, 0x00e6, 0x00d6, 0x0026, 0x7008, 0x9084,
+ 0x00ff, 0x6210, 0x00b6, 0x2258, 0xba10, 0x00be, 0x9206, 0x1520,
+ 0x700c, 0x6210, 0x00b6, 0x2258, 0xba14, 0x00be, 0x9206, 0x11e0,
+ 0x6043, 0x0000, 0x2c68, 0x0016, 0x2009, 0x0035, 0x080c, 0xd300,
+ 0x001e, 0x1158, 0x622c, 0x2268, 0x2071, 0x026c, 0x6b20, 0x9386,
+ 0x0003, 0x0130, 0x9386, 0x0006, 0x0128, 0x080c, 0xaf43, 0x0020,
+ 0x0039, 0x0010, 0x080c, 0xb57c, 0x002e, 0x00de, 0x00ee, 0x0005,
+ 0x0096, 0x6814, 0x2048, 0x9186, 0x0015, 0x0904, 0xb564, 0x918e,
+ 0x0016, 0x1904, 0xb57a, 0x700c, 0x908c, 0xff00, 0x9186, 0x1700,
+ 0x0120, 0x9186, 0x0300, 0x1904, 0xb53e, 0x89ff, 0x1138, 0x6800,
+ 0x9086, 0x000f, 0x0904, 0xb521, 0x0804, 0xb578, 0x6808, 0x9086,
+ 0xffff, 0x1904, 0xb566, 0xa87c, 0x9084, 0x0060, 0x9086, 0x0020,
+ 0x1128, 0xa83c, 0xa940, 0x9105, 0x1904, 0xb566, 0x6824, 0xd084,
+ 0x1904, 0xb566, 0xd0b4, 0x0158, 0x0016, 0x2001, 0x1985, 0x200c,
+ 0x6018, 0x9102, 0x9082, 0x0005, 0x001e, 0x1a04, 0xb566, 0x080c,
+ 0xce71, 0x685c, 0xa882, 0xa87c, 0xc0dc, 0xc0f4, 0xc0d4, 0xa87e,
+ 0x0026, 0x900e, 0x6a18, 0x2001, 0x000a, 0x080c, 0x9027, 0xa884,
+ 0x920a, 0x0208, 0x8011, 0xaa86, 0x82ff, 0x002e, 0x1138, 0x00c6,
+ 0x2d60, 0x080c, 0xc999, 0x00ce, 0x0804, 0xb578, 0x00c6, 0xa868,
+ 0xd0fc, 0x1118, 0x080c, 0x6141, 0x0010, 0x080c, 0x654e, 0x00ce,
+ 0x1904, 0xb566, 0x00c6, 0x2d60, 0x080c, 0xaf43, 0x00ce, 0x0804,
+ 0xb578, 0x00c6, 0x080c, 0xaf91, 0x0198, 0x6017, 0x0000, 0x6810,
+ 0x6012, 0x080c, 0xd102, 0x6023, 0x0003, 0x6904, 0x00c6, 0x2d60,
+ 0x080c, 0xaf43, 0x00ce, 0x080c, 0xafbe, 0x00ce, 0x0804, 0xb578,
+ 0x2001, 0x1987, 0x2004, 0x6842, 0x00ce, 0x04d0, 0x7008, 0x9086,
+ 0x000b, 0x11c8, 0x6010, 0x00b6, 0x2058, 0xb900, 0xc1bc, 0xb902,
+ 0x00be, 0x00c6, 0x2d60, 0xa87b, 0x0003, 0x080c, 0xd342, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c, 0x91b1, 0x080c,
+ 0x9763, 0x00ce, 0x00e8, 0x700c, 0x9086, 0x2a00, 0x1138, 0x2001,
+ 0x1987, 0x2004, 0x6842, 0x00a0, 0x0479, 0x00a0, 0x89ff, 0x090c,
+ 0x0dd5, 0x00c6, 0x00d6, 0x2d60, 0xa867, 0x0103, 0xa87b, 0x0003,
+ 0x080c, 0x6b33, 0x080c, 0xce71, 0x080c, 0xaf74, 0x00de, 0x00ce,
+ 0x080c, 0xaf43, 0x009e, 0x0005, 0x9186, 0x0015, 0x1128, 0x2001,
+ 0x1987, 0x2004, 0x6842, 0x0068, 0x918e, 0x0016, 0x1160, 0x00c6,
+ 0x2d00, 0x2060, 0x080c, 0xe997, 0x080c, 0x876f, 0x080c, 0xaf43,
+ 0x00ce, 0x080c, 0xaf43, 0x0005, 0x0026, 0x0036, 0x0046, 0x7228,
+ 0xacb0, 0xabac, 0xd2f4, 0x0130, 0x2001, 0x1987, 0x2004, 0x6842,
+ 0x0804, 0xb5f6, 0x00c6, 0x2d60, 0x080c, 0xc898, 0x00ce, 0x6804,
+ 0x9086, 0x0050, 0x1168, 0x00c6, 0x2d00, 0x2060, 0x6003, 0x0001,
+ 0x6007, 0x0050, 0x080c, 0x91b1, 0x080c, 0x9763, 0x00ce, 0x04f0,
+ 0x6800, 0x9086, 0x000f, 0x01a8, 0x89ff, 0x090c, 0x0dd5, 0x6800,
+ 0x9086, 0x0004, 0x1190, 0xa87c, 0xd0ac, 0x0178, 0xa843, 0x0fff,
+ 0xa83f, 0x0fff, 0xa880, 0xc0fc, 0xa882, 0x2001, 0x0001, 0x6832,
+ 0x0400, 0x2001, 0x0007, 0x6832, 0x00e0, 0xa87c, 0xd0b4, 0x1150,
+ 0xd0ac, 0x0db8, 0x6824, 0xd0f4, 0x1d48, 0xa838, 0xa934, 0x9105,
+ 0x0d80, 0x0c20, 0xd2ec, 0x1d68, 0x7024, 0x9306, 0x1118, 0x7020,
+ 0x9406, 0x0d38, 0x7020, 0x683e, 0x7024, 0x683a, 0x2001, 0x0005,
+ 0x6832, 0x080c, 0xcff9, 0x080c, 0x9763, 0x0010, 0x080c, 0xaf43,
+ 0x004e, 0x003e, 0x002e, 0x0005, 0x00e6, 0x00d6, 0x0026, 0x7008,
+ 0x9084, 0x00ff, 0x6210, 0x00b6, 0x2258, 0xba10, 0x00be, 0x9206,
+ 0x1904, 0xb661, 0x700c, 0x6210, 0x00b6, 0x2258, 0xba14, 0x00be,
+ 0x9206, 0x1904, 0xb661, 0x6038, 0x2068, 0x6824, 0xc0dc, 0x6826,
+ 0x6a20, 0x9286, 0x0007, 0x0904, 0xb661, 0x9286, 0x0002, 0x0904,
+ 0xb661, 0x9286, 0x0000, 0x05e8, 0x6808, 0x633c, 0x9306, 0x15c8,
+ 0x2071, 0x026c, 0x9186, 0x0015, 0x0570, 0x918e, 0x0016, 0x1100,
+ 0x00c6, 0x6038, 0x2060, 0x6104, 0x9186, 0x004b, 0x01c0, 0x9186,
+ 0x004c, 0x01a8, 0x9186, 0x004d, 0x0190, 0x9186, 0x004e, 0x0178,
+ 0x9186, 0x0052, 0x0160, 0x6014, 0x0096, 0x2048, 0x080c, 0xcc86,
+ 0x090c, 0x0dd5, 0xa87b, 0x0003, 0x009e, 0x080c, 0xd342, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c, 0x91b1, 0x080c,
+ 0x9763, 0x00ce, 0x0030, 0x6038, 0x2070, 0x2001, 0x1987, 0x2004,
+ 0x7042, 0x080c, 0xaf43, 0x002e, 0x00de, 0x00ee, 0x0005, 0x00b6,
+ 0x0096, 0x00f6, 0x6014, 0x2048, 0x6010, 0x2058, 0x91b6, 0x0015,
+ 0x0130, 0xba08, 0xbb0c, 0xbc00, 0xc48c, 0xbc02, 0x0460, 0x0096,
+ 0x0156, 0x0036, 0x0026, 0x2b48, 0x9e90, 0x0010, 0x2019, 0x000a,
+ 0x20a9, 0x0004, 0x080c, 0xbf11, 0x002e, 0x003e, 0x015e, 0x009e,
+ 0x1904, 0xb6d0, 0x0096, 0x0156, 0x0036, 0x0026, 0x2b48, 0x9e90,
+ 0x0014, 0x2019, 0x0006, 0x20a9, 0x0004, 0x080c, 0xbf11, 0x002e,
+ 0x003e, 0x015e, 0x009e, 0x15a0, 0x7238, 0xba0a, 0x733c, 0xbb0e,
+ 0xbc00, 0xc48d, 0xbc02, 0xa804, 0x9005, 0x1128, 0x00fe, 0x009e,
+ 0x00be, 0x0804, 0xb348, 0x0096, 0x2048, 0xaa12, 0xab16, 0xac0a,
+ 0x009e, 0x8006, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0,
+ 0x9080, 0x0002, 0x2009, 0x002b, 0xaaa0, 0xab9c, 0xaca8, 0xada4,
+ 0x2031, 0x0000, 0x2041, 0x1243, 0x080c, 0xb45d, 0x0130, 0x00fe,
+ 0x009e, 0x080c, 0xaf43, 0x00be, 0x0005, 0x080c, 0xb905, 0x0cb8,
+ 0x2b78, 0x00f6, 0x080c, 0x321e, 0x080c, 0xd39d, 0x00fe, 0x00c6,
+ 0x080c, 0xaeed, 0x2f00, 0x6012, 0x6017, 0x0000, 0x6023, 0x0001,
+ 0x6007, 0x0001, 0x6003, 0x0001, 0x2001, 0x0007, 0x080c, 0x65e9,
+ 0x080c, 0x6615, 0x080c, 0x91f9, 0x080c, 0x9763, 0x00ce, 0x0804,
+ 0xb6a3, 0x2100, 0x91b2, 0x0053, 0x1a0c, 0x0dd5, 0x91b2, 0x0040,
+ 0x1a04, 0xb759, 0x0002, 0xb747, 0xb747, 0xb73d, 0xb747, 0xb747,
+ 0xb747, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b,
+ 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b,
+ 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b,
+ 0xb73b, 0xb73b, 0xb747, 0xb73b, 0xb747, 0xb747, 0xb73b, 0xb73b,
+ 0xb73b, 0xb73b, 0xb73b, 0xb73d, 0xb73b, 0xb73b, 0xb73b, 0xb73b,
+ 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb747, 0xb747, 0xb73b,
+ 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b, 0xb73b,
+ 0xb747, 0xb73b, 0xb73b, 0x080c, 0x0dd5, 0x0066, 0x00b6, 0x6610,
+ 0x2658, 0xb8cc, 0xc08c, 0xb8ce, 0x00be, 0x006e, 0x0000, 0x6003,
+ 0x0001, 0x6106, 0x9186, 0x0032, 0x0118, 0x080c, 0x91f9, 0x0010,
+ 0x080c, 0x91b1, 0x0126, 0x2091, 0x8000, 0x080c, 0x9763, 0x012e,
+ 0x0005, 0x2600, 0x0002, 0xb747, 0xb747, 0xb76d, 0xb747, 0xb747,
+ 0xb76d, 0xb76d, 0xb76d, 0xb76d, 0xb747, 0xb76d, 0xb747, 0xb76d,
+ 0xb747, 0xb76d, 0xb76d, 0xb76d, 0xb76d, 0x080c, 0x0dd5, 0x6004,
+ 0x90b2, 0x0053, 0x1a0c, 0x0dd5, 0x91b6, 0x0013, 0x0904, 0xb831,
+ 0x91b6, 0x0027, 0x1904, 0xb7ec, 0x080c, 0x9657, 0x6004, 0x080c,
+ 0xce7d, 0x01b0, 0x080c, 0xce8e, 0x01a8, 0x908e, 0x0021, 0x0904,
+ 0xb7e9, 0x908e, 0x0022, 0x1130, 0x080c, 0xb374, 0x0904, 0xb7e5,
+ 0x0804, 0xb7e6, 0x908e, 0x003d, 0x0904, 0xb7e9, 0x0804, 0xb7df,
+ 0x080c, 0x3247, 0x2001, 0x0007, 0x080c, 0x65e9, 0x6010, 0x00b6,
+ 0x2058, 0xb9a0, 0x00be, 0x080c, 0xb905, 0x9186, 0x007e, 0x1148,
+ 0x2001, 0x1837, 0x2014, 0xc285, 0x080c, 0x743e, 0x1108, 0xc2ad,
+ 0x2202, 0x0036, 0x0026, 0x2019, 0x0028, 0x2110, 0x080c, 0xeaa3,
+ 0x002e, 0x003e, 0x0016, 0x0026, 0x0036, 0x2110, 0x2019, 0x0028,
+ 0x080c, 0x9356, 0x0076, 0x903e, 0x080c, 0x9229, 0x6010, 0x00b6,
+ 0x905d, 0x0100, 0x00be, 0x2c08, 0x080c, 0xe477, 0x007e, 0x003e,
+ 0x002e, 0x001e, 0x080c, 0xd39d, 0x0016, 0x080c, 0xd0fa, 0x080c,
+ 0xaf43, 0x001e, 0x080c, 0x331a, 0x080c, 0x9763, 0x0030, 0x080c,
+ 0xd0fa, 0x080c, 0xaf43, 0x080c, 0x9763, 0x0005, 0x080c, 0xb905,
+ 0x0cb0, 0x080c, 0xb941, 0x0c98, 0x9186, 0x0014, 0x1db0, 0x080c,
+ 0x9657, 0x6004, 0x908e, 0x0022, 0x1118, 0x080c, 0xb374, 0x0d68,
+ 0x080c, 0x321e, 0x080c, 0xd39d, 0x080c, 0xce7d, 0x1190, 0x080c,
+ 0x3247, 0x6010, 0x00b6, 0x2058, 0xb9a0, 0x00be, 0x080c, 0xb905,
+ 0x9186, 0x007e, 0x1128, 0x2001, 0x1837, 0x200c, 0xc185, 0x2102,
+ 0x0870, 0x080c, 0xce8e, 0x1118, 0x080c, 0xb905, 0x0840, 0x6004,
+ 0x908e, 0x0032, 0x1160, 0x00e6, 0x00f6, 0x2071, 0x189e, 0x2079,
+ 0x0000, 0x080c, 0x35b5, 0x00fe, 0x00ee, 0x0804, 0xb7df, 0x6004,
+ 0x908e, 0x0021, 0x0d48, 0x908e, 0x0022, 0x090c, 0xb905, 0x0804,
+ 0xb7df, 0x90b2, 0x0040, 0x1a04, 0xb8e1, 0x2008, 0x0002, 0xb879,
+ 0xb87a, 0xb87d, 0xb880, 0xb883, 0xb886, 0xb877, 0xb877, 0xb877,
+ 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877,
+ 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877,
+ 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb889, 0xb896, 0xb877,
+ 0xb898, 0xb896, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb896,
+ 0xb896, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877, 0xb877,
+ 0xb877, 0xb8c8, 0xb896, 0xb877, 0xb892, 0xb877, 0xb877, 0xb877,
+ 0xb893, 0xb877, 0xb877, 0xb877, 0xb896, 0xb8bf, 0xb877, 0x080c,
+ 0x0dd5, 0x00e0, 0x2001, 0x000b, 0x0420, 0x2001, 0x0003, 0x0408,
+ 0x2001, 0x0005, 0x00f0, 0x2001, 0x0001, 0x00d8, 0x2001, 0x0009,
+ 0x00c0, 0x080c, 0x9657, 0x6003, 0x0005, 0x080c, 0xd3a0, 0x080c,
+ 0x9763, 0x0070, 0x0018, 0x0010, 0x080c, 0x65e9, 0x0804, 0xb8d9,
+ 0x080c, 0x9657, 0x080c, 0xd3a0, 0x6003, 0x0004, 0x080c, 0x9763,
+ 0x0005, 0x080c, 0x65e9, 0x080c, 0x9657, 0x6003, 0x0002, 0x0036,
+ 0x2019, 0x1852, 0x2304, 0x9084, 0xff00, 0x1120, 0x2001, 0x1985,
+ 0x201c, 0x0040, 0x8007, 0x909a, 0x0004, 0x0ec0, 0x8003, 0x801b,
+ 0x831b, 0x9318, 0x631a, 0x003e, 0x080c, 0x9763, 0x0c08, 0x080c,
+ 0x9657, 0x080c, 0xd0fa, 0x080c, 0xaf43, 0x080c, 0x9763, 0x08c0,
+ 0x00e6, 0x00f6, 0x2071, 0x189e, 0x2079, 0x0000, 0x080c, 0x35b5,
+ 0x00fe, 0x00ee, 0x080c, 0x9657, 0x080c, 0xaf43, 0x080c, 0x9763,
+ 0x0838, 0x080c, 0x9657, 0x6003, 0x0002, 0x080c, 0xd3a0, 0x0804,
+ 0x9763, 0x2600, 0x2008, 0x0002, 0xb8f8, 0xb8d9, 0xb8f6, 0xb8d9,
+ 0xb8d9, 0xb8f6, 0xb8f6, 0xb8f6, 0xb8f6, 0xb8d9, 0xb8f6, 0xb8d9,
+ 0xb8f6, 0xb8d9, 0xb8f6, 0xb8f6, 0xb8f6, 0xb8f6, 0x080c, 0x0dd5,
+ 0x080c, 0x9657, 0x0096, 0x6014, 0x2048, 0x080c, 0x6d17, 0x009e,
+ 0x080c, 0xaf43, 0x080c, 0x9763, 0x0005, 0x00e6, 0x0096, 0x0026,
+ 0x0016, 0x080c, 0xcc86, 0x0568, 0x6014, 0x2048, 0xa864, 0x9086,
+ 0x0139, 0x11a8, 0xa894, 0x9086, 0x0056, 0x1148, 0x080c, 0x54f7,
+ 0x0130, 0x2001, 0x0000, 0x900e, 0x2011, 0x4000, 0x0028, 0x2001,
+ 0x0030, 0x900e, 0x2011, 0x4005, 0x080c, 0xd267, 0x0090, 0xa868,
+ 0xd0fc, 0x0178, 0xa807, 0x0000, 0x0016, 0x6004, 0x908e, 0x0021,
+ 0x0168, 0x908e, 0x003d, 0x0150, 0x001e, 0xa867, 0x0103, 0xa833,
+ 0x0100, 0x001e, 0x002e, 0x009e, 0x00ee, 0x0005, 0x001e, 0x0009,
+ 0x0cc0, 0x0096, 0x6014, 0x2048, 0xa800, 0x2048, 0xa867, 0x0103,
+ 0xa823, 0x8001, 0x009e, 0x0005, 0x00b6, 0x6610, 0x2658, 0xb804,
+ 0x9084, 0x00ff, 0x90b2, 0x000c, 0x1a0c, 0x0dd5, 0x6604, 0x96b6,
+ 0x004d, 0x1120, 0x080c, 0xd186, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x0043, 0x1120, 0x080c, 0xd1cf, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x004b, 0x1120, 0x080c, 0xd1fb, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x0033, 0x1120, 0x080c, 0xd11c, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x0028, 0x1120, 0x080c, 0xcecc, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x0029, 0x1120, 0x080c, 0xcf0d, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x001f, 0x1120, 0x080c, 0xb31c, 0x0804, 0xb9d1, 0x6604, 0x96b6,
+ 0x0000, 0x1118, 0x080c, 0xb667, 0x04e0, 0x6604, 0x96b6, 0x0022,
+ 0x1118, 0x080c, 0xb355, 0x04a8, 0x6604, 0x96b6, 0x0035, 0x1118,
+ 0x080c, 0xb47b, 0x0470, 0x6604, 0x96b6, 0x0039, 0x1118, 0x080c,
+ 0xb5fc, 0x0438, 0x6604, 0x96b6, 0x003d, 0x1118, 0x080c, 0xb38d,
+ 0x0400, 0x6604, 0x96b6, 0x0044, 0x1118, 0x080c, 0xb3c9, 0x00c8,
+ 0x6604, 0x96b6, 0x0049, 0x1118, 0x080c, 0xb40a, 0x0090, 0x6604,
+ 0x96b6, 0x0041, 0x1118, 0x080c, 0xb3f4, 0x0058, 0x91b6, 0x0015,
+ 0x1110, 0x0063, 0x0030, 0x91b6, 0x0016, 0x1128, 0x00be, 0x0804,
+ 0xbc2d, 0x00be, 0x0005, 0x080c, 0xafd9, 0x0cd8, 0xb9ee, 0xb9f1,
+ 0xb9ee, 0xba38, 0xb9ee, 0xbba1, 0xbc3a, 0xb9ee, 0xb9ee, 0xbc03,
+ 0xb9ee, 0xbc19, 0x0096, 0x601f, 0x0000, 0x6014, 0x2048, 0xa800,
+ 0x2048, 0xa867, 0x0103, 0x009e, 0x0804, 0xaf43, 0xa001, 0xa001,
+ 0x0005, 0x00e6, 0x2071, 0x1800, 0x7090, 0x9086, 0x0074, 0x1540,
+ 0x080c, 0xe448, 0x11b0, 0x6010, 0x00b6, 0x2058, 0x7030, 0xd08c,
+ 0x0128, 0xb800, 0xd0bc, 0x0110, 0xc0c5, 0xb802, 0x00f9, 0x00be,
+ 0x2001, 0x0006, 0x080c, 0x65e9, 0x080c, 0x3247, 0x080c, 0xaf43,
+ 0x0098, 0x2001, 0x000a, 0x080c, 0x65e9, 0x080c, 0x3247, 0x6003,
+ 0x0001, 0x6007, 0x0001, 0x080c, 0x91f9, 0x080c, 0x9763, 0x0020,
+ 0x2001, 0x0001, 0x080c, 0xbb71, 0x00ee, 0x0005, 0x00d6, 0xb800,
+ 0xd084, 0x0160, 0x9006, 0x080c, 0x65d5, 0x2069, 0x1847, 0x6804,
+ 0xd0a4, 0x0120, 0x2001, 0x0006, 0x080c, 0x6615, 0x00de, 0x0005,
+ 0x00b6, 0x0096, 0x00d6, 0x2011, 0x1824, 0x2204, 0x9086, 0x0074,
+ 0x1904, 0xbb46, 0x6010, 0x2058, 0xbaa0, 0x9286, 0x007e, 0x1120,
+ 0x080c, 0xbd87, 0x0804, 0xbaaa, 0x080c, 0xbd7c, 0x6010, 0x2058,
+ 0xbaa0, 0x9286, 0x0080, 0x1510, 0x6014, 0x9005, 0x01a8, 0x2048,
+ 0xa864, 0x9084, 0x00ff, 0x9086, 0x0039, 0x1140, 0x2001, 0x0000,
+ 0x900e, 0x2011, 0x4000, 0x080c, 0xd267, 0x0030, 0xa807, 0x0000,
+ 0xa867, 0x0103, 0xa833, 0x0200, 0x2001, 0x0006, 0x080c, 0x65e9,
+ 0x080c, 0x3247, 0x080c, 0xaf43, 0x0804, 0xbb4b, 0x080c, 0xbb59,
+ 0x6014, 0x9005, 0x0190, 0x2048, 0xa868, 0xd0f4, 0x01e8, 0xa864,
+ 0x9084, 0x00ff, 0x9086, 0x0039, 0x1d08, 0x2001, 0x0000, 0x900e,
+ 0x2011, 0x4000, 0x080c, 0xd267, 0x08f8, 0x080c, 0xbb4f, 0x0160,
+ 0x9006, 0x080c, 0x65d5, 0x2001, 0x0004, 0x080c, 0x6615, 0x2001,
+ 0x0007, 0x080c, 0x65e9, 0x08a0, 0x2001, 0x0004, 0x080c, 0x65e9,
+ 0x6003, 0x0001, 0x6007, 0x0003, 0x080c, 0x91f9, 0x080c, 0x9763,
+ 0x0804, 0xbb4b, 0xb85c, 0xd0e4, 0x01d8, 0x080c, 0xd09c, 0x080c,
+ 0x743e, 0x0118, 0xd0dc, 0x1904, 0xba6c, 0x2011, 0x1837, 0x2204,
+ 0xc0ad, 0x2012, 0x2001, 0x196c, 0x2004, 0x00f6, 0x2079, 0x0100,
+ 0x78e3, 0x0000, 0x080c, 0x28f0, 0x78e2, 0x00fe, 0x0804, 0xba6c,
+ 0x080c, 0xd0d9, 0x2011, 0x1837, 0x2204, 0xc0a5, 0x2012, 0x0006,
+ 0x080c, 0xe5cd, 0x000e, 0x1904, 0xba6c, 0xc0b5, 0x2012, 0x2001,
+ 0x0006, 0x080c, 0x65e9, 0x9006, 0x080c, 0x65d5, 0x00c6, 0x2001,
+ 0x180f, 0x2004, 0xd09c, 0x0520, 0x00f6, 0x2079, 0x0100, 0x00e6,
+ 0x2071, 0x1800, 0x700c, 0x9084, 0x00ff, 0x78e6, 0x707e, 0x7010,
+ 0x78ea, 0x7082, 0x908c, 0x00ff, 0x00ee, 0x780c, 0xc0b5, 0x780e,
+ 0x00fe, 0x080c, 0x28c5, 0x00f6, 0x2100, 0x900e, 0x080c, 0x287c,
+ 0x795e, 0x00fe, 0x9186, 0x0081, 0x01d8, 0x2009, 0x0081, 0x00c8,
+ 0x2009, 0x00ef, 0x00f6, 0x2079, 0x0100, 0x79ea, 0x7932, 0x7936,
+ 0x780c, 0xc0b5, 0x780e, 0x00fe, 0x080c, 0x28c5, 0x00f6, 0x2079,
+ 0x1800, 0x7982, 0x2100, 0x900e, 0x080c, 0x287c, 0x795e, 0x00fe,
+ 0x8108, 0x080c, 0x6638, 0x2b00, 0x00ce, 0x1904, 0xba6c, 0x6012,
+ 0x2009, 0x180f, 0x210c, 0xd19c, 0x0150, 0x2009, 0x027c, 0x210c,
+ 0x918c, 0x00ff, 0xb912, 0x2009, 0x027d, 0x210c, 0xb916, 0x2001,
+ 0x0002, 0x080c, 0x65e9, 0x6023, 0x0001, 0x6003, 0x0001, 0x6007,
+ 0x0002, 0x080c, 0x91f9, 0x080c, 0x9763, 0x0028, 0x080c, 0xb905,
+ 0x2001, 0x0001, 0x0431, 0x00de, 0x009e, 0x00be, 0x0005, 0x2001,
+ 0x1810, 0x2004, 0xd0a4, 0x0120, 0x2001, 0x1848, 0x2004, 0xd0ac,
+ 0x0005, 0x00e6, 0x080c, 0xeafc, 0x0190, 0x2071, 0x0260, 0x7108,
+ 0x720c, 0x918c, 0x00ff, 0x1118, 0x9284, 0xff00, 0x0140, 0x6010,
+ 0x2058, 0xb8a0, 0x9084, 0xff80, 0x1110, 0xb912, 0xba16, 0x00ee,
+ 0x0005, 0x2030, 0x9005, 0x0158, 0x2001, 0x0007, 0x080c, 0x65e9,
+ 0x080c, 0x5771, 0x1120, 0x2001, 0x0007, 0x080c, 0x6615, 0x2600,
+ 0x9005, 0x11b0, 0x6014, 0x0096, 0x2048, 0xa868, 0x009e, 0xd0fc,
+ 0x1178, 0x0036, 0x0046, 0x6010, 0x00b6, 0x2058, 0xbba0, 0x00be,
+ 0x2021, 0x0004, 0x2011, 0x8014, 0x080c, 0x4b7f, 0x004e, 0x003e,
+ 0x080c, 0x3247, 0x6020, 0x9086, 0x000a, 0x1108, 0x0005, 0x0804,
+ 0xaf43, 0x00b6, 0x00e6, 0x0026, 0x0016, 0x2071, 0x1800, 0x7090,
+ 0x9086, 0x0014, 0x1904, 0xbbf9, 0x080c, 0x5771, 0x1170, 0x6014,
+ 0x9005, 0x1158, 0x0036, 0x0046, 0x6010, 0x2058, 0xbba0, 0x2021,
+ 0x0006, 0x080c, 0x4d36, 0x004e, 0x003e, 0x00d6, 0x6010, 0x2058,
+ 0x080c, 0x6734, 0x080c, 0xba26, 0x00de, 0x080c, 0xbe4d, 0x1588,
+ 0x6010, 0x2058, 0xb890, 0x9005, 0x0560, 0x2001, 0x0006, 0x080c,
+ 0x65e9, 0x0096, 0x6014, 0x904d, 0x01d0, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0039, 0x1140, 0x2001, 0x0000, 0x900e, 0x2011, 0x4000,
+ 0x080c, 0xd267, 0x0060, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0029,
+ 0x0130, 0xa807, 0x0000, 0xa867, 0x0103, 0xa833, 0x0200, 0x009e,
+ 0x080c, 0x3247, 0x6020, 0x9086, 0x000a, 0x0140, 0x080c, 0xaf43,
+ 0x0028, 0x080c, 0xb905, 0x9006, 0x080c, 0xbb71, 0x001e, 0x002e,
+ 0x00ee, 0x00be, 0x0005, 0x2011, 0x1824, 0x2204, 0x9086, 0x0014,
+ 0x1160, 0x2001, 0x0002, 0x080c, 0x65e9, 0x6003, 0x0001, 0x6007,
+ 0x0001, 0x080c, 0x91f9, 0x0804, 0x9763, 0x2001, 0x0001, 0x0804,
+ 0xbb71, 0x2030, 0x2011, 0x1824, 0x2204, 0x9086, 0x0004, 0x1148,
+ 0x96b6, 0x000b, 0x1120, 0x2001, 0x0007, 0x080c, 0x65e9, 0x0804,
+ 0xaf43, 0x2001, 0x0001, 0x0804, 0xbb71, 0x0002, 0xb9ee, 0xbc45,
+ 0xb9ee, 0xbc86, 0xb9ee, 0xbd33, 0xbc3a, 0xb9ee, 0xb9ee, 0xbd47,
+ 0xb9ee, 0xbd59, 0x6604, 0x9686, 0x0003, 0x0904, 0xbba1, 0x96b6,
+ 0x001e, 0x1110, 0x080c, 0xaf43, 0x0005, 0x00b6, 0x00d6, 0x00c6,
+ 0x080c, 0xbd6b, 0x11a0, 0x9006, 0x080c, 0x65d5, 0x080c, 0x321e,
+ 0x080c, 0xd39d, 0x2001, 0x0002, 0x080c, 0x65e9, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x080c, 0x91f9, 0x080c, 0x9763, 0x0418, 0x2009,
+ 0x026e, 0x2104, 0x9086, 0x0009, 0x1160, 0x6010, 0x2058, 0xb840,
+ 0x9084, 0x00ff, 0x9005, 0x0170, 0x8001, 0xb842, 0x601b, 0x000a,
+ 0x0088, 0x2009, 0x026f, 0x2104, 0x9084, 0xff00, 0x9086, 0x1900,
+ 0x1108, 0x08a0, 0x080c, 0x321e, 0x080c, 0xd39d, 0x2001, 0x0001,
+ 0x080c, 0xbb71, 0x00ce, 0x00de, 0x00be, 0x0005, 0x0096, 0x00b6,
+ 0x0026, 0x9016, 0x080c, 0xbd79, 0x00d6, 0x2069, 0x197b, 0x2d04,
+ 0x9005, 0x0168, 0x6010, 0x2058, 0xb8a0, 0x9086, 0x007e, 0x1138,
+ 0x2069, 0x1820, 0x2d04, 0x8000, 0x206a, 0x00de, 0x0010, 0x00de,
+ 0x0088, 0x9006, 0x080c, 0x65d5, 0x2001, 0x0002, 0x080c, 0x65e9,
+ 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x91f9, 0x080c, 0x9763,
+ 0x0804, 0xbd03, 0x080c, 0xcc86, 0x01b0, 0x6014, 0x2048, 0xa864,
+ 0x2010, 0x9086, 0x0139, 0x1138, 0x6007, 0x0016, 0x2001, 0x0002,
+ 0x080c, 0xd2c1, 0x00b0, 0x6014, 0x2048, 0xa864, 0xd0fc, 0x0118,
+ 0x2001, 0x0001, 0x0ca8, 0x2001, 0x180e, 0x2004, 0xd0dc, 0x0148,
+ 0x6010, 0x2058, 0xb840, 0x9084, 0x00ff, 0x9005, 0x1110, 0x9006,
+ 0x0c38, 0x080c, 0xb905, 0x2009, 0x026e, 0x2134, 0x96b4, 0x00ff,
+ 0x9686, 0x0005, 0x0520, 0x9686, 0x000b, 0x01c8, 0x2009, 0x026f,
+ 0x2104, 0x9084, 0xff00, 0x1118, 0x9686, 0x0009, 0x01c0, 0x9086,
+ 0x1900, 0x1168, 0x9686, 0x0009, 0x0190, 0x2001, 0x0004, 0x080c,
+ 0x65e9, 0x2001, 0x0028, 0x601a, 0x6007, 0x0052, 0x0020, 0x2001,
+ 0x0001, 0x080c, 0xbb71, 0x002e, 0x00be, 0x009e, 0x0005, 0x9286,
+ 0x0139, 0x0160, 0x6014, 0x2048, 0x080c, 0xcc86, 0x0140, 0xa864,
+ 0x9086, 0x0139, 0x0118, 0xa868, 0xd0fc, 0x0108, 0x0c40, 0x6010,
+ 0x2058, 0xb840, 0x9084, 0x00ff, 0x9005, 0x0138, 0x8001, 0xb842,
+ 0x601b, 0x000a, 0x6007, 0x0016, 0x08f0, 0xb8a0, 0x9086, 0x007e,
+ 0x1138, 0x00e6, 0x2071, 0x1800, 0x080c, 0x6040, 0x00ee, 0x0010,
+ 0x080c, 0x321e, 0x0860, 0x080c, 0xbd79, 0x1160, 0x2001, 0x0004,
+ 0x080c, 0x65e9, 0x6003, 0x0001, 0x6007, 0x0003, 0x080c, 0x91f9,
+ 0x0804, 0x9763, 0x080c, 0xb905, 0x9006, 0x0804, 0xbb71, 0x0489,
+ 0x1160, 0x2001, 0x0008, 0x080c, 0x65e9, 0x6003, 0x0001, 0x6007,
+ 0x0005, 0x080c, 0x91f9, 0x0804, 0x9763, 0x2001, 0x0001, 0x0804,
+ 0xbb71, 0x00f9, 0x1160, 0x2001, 0x000a, 0x080c, 0x65e9, 0x6003,
+ 0x0001, 0x6007, 0x0001, 0x080c, 0x91f9, 0x0804, 0x9763, 0x2001,
+ 0x0001, 0x0804, 0xbb71, 0x2009, 0x026e, 0x2104, 0x9086, 0x0003,
+ 0x1138, 0x2009, 0x026f, 0x2104, 0x9084, 0xff00, 0x9086, 0x2a00,
+ 0x0005, 0x9085, 0x0001, 0x0005, 0x00b6, 0x00c6, 0x0016, 0x6110,
+ 0x2158, 0x080c, 0x66a8, 0x001e, 0x00ce, 0x00be, 0x0005, 0x00b6,
+ 0x00f6, 0x00e6, 0x00d6, 0x0036, 0x0016, 0x6010, 0x2058, 0x2009,
+ 0x1837, 0x2104, 0x9085, 0x0003, 0x200a, 0x080c, 0xbe1f, 0x0560,
+ 0x2009, 0x1837, 0x2104, 0xc0cd, 0x200a, 0x080c, 0x6a08, 0x0158,
+ 0x9006, 0x2020, 0x2009, 0x002a, 0x080c, 0xe73a, 0x2001, 0x180c,
+ 0x200c, 0xc195, 0x2102, 0x2019, 0x002a, 0x2009, 0x0001, 0x080c,
+ 0x31e9, 0x00e6, 0x2071, 0x1800, 0x080c, 0x2ff5, 0x00ee, 0x00c6,
+ 0x0156, 0x20a9, 0x0781, 0x2009, 0x007f, 0x080c, 0x331a, 0x8108,
+ 0x1f04, 0xbdbd, 0x015e, 0x00ce, 0x080c, 0xbd7c, 0x2071, 0x0260,
+ 0x2079, 0x0200, 0x7817, 0x0001, 0x2001, 0x1837, 0x200c, 0xc1c5,
+ 0x7018, 0xd0fc, 0x0110, 0xd0dc, 0x0118, 0x7038, 0xd0dc, 0x1108,
+ 0xc1c4, 0x7817, 0x0000, 0x2001, 0x1837, 0x2102, 0x2079, 0x0100,
+ 0x2e04, 0x9084, 0x00ff, 0x2069, 0x181f, 0x206a, 0x78e6, 0x0006,
+ 0x8e70, 0x2e04, 0x2069, 0x1820, 0x206a, 0x78ea, 0x7832, 0x7836,
+ 0x2010, 0x9084, 0xff00, 0x001e, 0x9105, 0x2009, 0x182c, 0x200a,
+ 0x2200, 0x9084, 0x00ff, 0x2008, 0x080c, 0x28c5, 0x080c, 0x743e,
+ 0x0170, 0x2071, 0x0260, 0x2069, 0x1981, 0x7048, 0x206a, 0x704c,
+ 0x6806, 0x7050, 0x680a, 0x7054, 0x680e, 0x080c, 0xd09c, 0x0040,
+ 0x2001, 0x0006, 0x080c, 0x65e9, 0x080c, 0x3247, 0x080c, 0xaf43,
+ 0x001e, 0x003e, 0x00de, 0x00ee, 0x00fe, 0x00be, 0x0005, 0x0096,
+ 0x0026, 0x0036, 0x00e6, 0x0156, 0x2019, 0x182c, 0x231c, 0x83ff,
+ 0x01f0, 0x2071, 0x0260, 0x7200, 0x9294, 0x00ff, 0x7004, 0x9084,
+ 0xff00, 0x9205, 0x9306, 0x1198, 0x2011, 0x0276, 0x20a9, 0x0004,
+ 0x2b48, 0x2019, 0x000a, 0x080c, 0xbf11, 0x1148, 0x2011, 0x027a,
+ 0x20a9, 0x0004, 0x2019, 0x0006, 0x080c, 0xbf11, 0x1100, 0x015e,
+ 0x00ee, 0x003e, 0x002e, 0x009e, 0x0005, 0x00e6, 0x2071, 0x0260,
+ 0x7034, 0x9086, 0x0014, 0x11a8, 0x7038, 0x9086, 0x0800, 0x1188,
+ 0x703c, 0xd0ec, 0x0160, 0x9084, 0x0f00, 0x9086, 0x0100, 0x1138,
+ 0x7054, 0xd0a4, 0x1110, 0xd0ac, 0x0110, 0x9006, 0x0010, 0x9085,
+ 0x0001, 0x00ee, 0x0005, 0x00e6, 0x0096, 0x00c6, 0x0076, 0x0056,
+ 0x0046, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2029, 0x19ef,
+ 0x252c, 0x2021, 0x19f5, 0x2424, 0x2061, 0x1cd0, 0x2071, 0x1800,
+ 0x7254, 0x7074, 0x9202, 0x1a04, 0xbedd, 0x080c, 0x8a3d, 0x0904,
+ 0xbed6, 0x080c, 0xe76b, 0x0904, 0xbed6, 0x6720, 0x9786, 0x0007,
+ 0x0904, 0xbed6, 0x2500, 0x9c06, 0x0904, 0xbed6, 0x2400, 0x9c06,
+ 0x05e8, 0x3e08, 0x9186, 0x0002, 0x1148, 0x6010, 0x9005, 0x0130,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1580, 0x00c6, 0x6000,
+ 0x9086, 0x0004, 0x1110, 0x080c, 0x1aa1, 0x9786, 0x000a, 0x0148,
+ 0x080c, 0xce8e, 0x1130, 0x00ce, 0x080c, 0xb905, 0x080c, 0xaf74,
+ 0x00e8, 0x6014, 0x2048, 0x080c, 0xcc86, 0x01a8, 0x9786, 0x0003,
+ 0x1530, 0xa867, 0x0103, 0xa87c, 0xd0cc, 0x0130, 0x0096, 0xa878,
+ 0x2048, 0x080c, 0x0fb1, 0x009e, 0xab7a, 0xa877, 0x0000, 0x080c,
+ 0x6d0b, 0x080c, 0xce71, 0x080c, 0xaf74, 0x00ce, 0x9ce0, 0x0018,
+ 0x7068, 0x9c02, 0x1210, 0x0804, 0xbe80, 0x012e, 0x000e, 0x002e,
+ 0x004e, 0x005e, 0x007e, 0x00ce, 0x009e, 0x00ee, 0x0005, 0x9786,
+ 0x0006, 0x1118, 0x080c, 0xe6dd, 0x0c30, 0x9786, 0x0009, 0x1148,
+ 0x6000, 0x9086, 0x0004, 0x0d08, 0x2009, 0x004c, 0x080c, 0xafbe,
+ 0x08e0, 0x9786, 0x000a, 0x0980, 0x0820, 0x220c, 0x2304, 0x9106,
+ 0x1130, 0x8210, 0x8318, 0x1f04, 0xbefd, 0x9006, 0x0005, 0x2304,
+ 0x9102, 0x0218, 0x2001, 0x0001, 0x0008, 0x9006, 0x918d, 0x0001,
+ 0x0005, 0x0136, 0x01c6, 0x0016, 0x8906, 0x8006, 0x8007, 0x908c,
+ 0x003f, 0x21e0, 0x9084, 0xffc0, 0x9300, 0x2098, 0x3518, 0x20a9,
+ 0x0001, 0x220c, 0x4002, 0x910e, 0x1140, 0x8210, 0x8319, 0x1dc8,
+ 0x9006, 0x001e, 0x01ce, 0x013e, 0x0005, 0x220c, 0x9102, 0x0218,
+ 0x2001, 0x0001, 0x0010, 0x2001, 0x0000, 0x918d, 0x0001, 0x001e,
+ 0x01ce, 0x013e, 0x0005, 0x220c, 0x810f, 0x2304, 0x9106, 0x1130,
+ 0x8210, 0x8318, 0x1f04, 0xbf3b, 0x9006, 0x0005, 0x918d, 0x0001,
+ 0x0005, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0dd5, 0x080c, 0xce7d,
+ 0x0120, 0x080c, 0xce8e, 0x0168, 0x0028, 0x080c, 0x3247, 0x080c,
+ 0xce8e, 0x0138, 0x080c, 0x9657, 0x080c, 0xaf43, 0x080c, 0x9763,
+ 0x0005, 0x080c, 0xb905, 0x0cb0, 0x9182, 0x0054, 0x1220, 0x9182,
+ 0x0040, 0x0208, 0x000a, 0x0005, 0xbf80, 0xbf80, 0xbf80, 0xbf80,
+ 0xbf80, 0xbf80, 0xbf80, 0xbf80, 0xbf80, 0xbf80, 0xbf80, 0xbf82,
+ 0xbf82, 0xbf82, 0xbf82, 0xbf80, 0xbf80, 0xbf80, 0xbf82, 0xbf80,
+ 0x080c, 0x0dd5, 0x600b, 0xffff, 0x6003, 0x0001, 0x6106, 0x080c,
+ 0x91b1, 0x0126, 0x2091, 0x8000, 0x080c, 0x9763, 0x012e, 0x0005,
+ 0x9186, 0x0013, 0x1128, 0x6004, 0x9082, 0x0040, 0x0804, 0xc037,
+ 0x9186, 0x0027, 0x1520, 0x080c, 0x9657, 0x080c, 0x321e, 0x080c,
+ 0xd39d, 0x0096, 0x6114, 0x2148, 0x080c, 0xcc86, 0x0198, 0x080c,
+ 0xce8e, 0x1118, 0x080c, 0xb905, 0x0068, 0xa867, 0x0103, 0xa87b,
+ 0x0029, 0xa877, 0x0000, 0xa97c, 0xc1c5, 0xa97e, 0x080c, 0x6d17,
+ 0x080c, 0xce71, 0x009e, 0x080c, 0xaf43, 0x0804, 0x9763, 0x9186,
+ 0x0014, 0x1120, 0x6004, 0x9082, 0x0040, 0x04a0, 0x9186, 0x0046,
+ 0x0150, 0x9186, 0x0045, 0x0138, 0x9186, 0x0053, 0x0120, 0x9186,
+ 0x0048, 0x190c, 0x0dd5, 0x2001, 0x0109, 0x2004, 0xd084, 0x0508,
+ 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x0036, 0x00f6,
+ 0x00e6, 0x00c6, 0x2079, 0x19e6, 0x2071, 0x1800, 0x2061, 0x0100,
+ 0x080c, 0x9094, 0x00ce, 0x00ee, 0x00fe, 0x003e, 0x002e, 0x001e,
+ 0x000e, 0x012e, 0xa001, 0x6000, 0x9086, 0x0002, 0x1110, 0x0804,
+ 0xc075, 0x0005, 0x0002, 0xc011, 0xc00f, 0xc00f, 0xc00f, 0xc00f,
+ 0xc00f, 0xc00f, 0xc00f, 0xc00f, 0xc00f, 0xc00f, 0xc02c, 0xc02c,
+ 0xc02c, 0xc02c, 0xc00f, 0xc02c, 0xc00f, 0xc02c, 0xc00f, 0x080c,
+ 0x0dd5, 0x080c, 0x9657, 0x0096, 0x6114, 0x2148, 0x080c, 0xcc86,
+ 0x0168, 0xa867, 0x0103, 0xa87b, 0x0006, 0xa877, 0x0000, 0xa880,
+ 0xc0ec, 0xa882, 0x080c, 0x6d17, 0x080c, 0xce71, 0x009e, 0x080c,
+ 0xaf43, 0x080c, 0x9763, 0x0005, 0x080c, 0x9657, 0x080c, 0xce8e,
+ 0x090c, 0xb905, 0x080c, 0xaf43, 0x080c, 0x9763, 0x0005, 0x0002,
+ 0xc04e, 0xc04c, 0xc04c, 0xc04c, 0xc04c, 0xc04c, 0xc04c, 0xc04c,
+ 0xc04c, 0xc04c, 0xc04c, 0xc065, 0xc065, 0xc065, 0xc065, 0xc04c,
+ 0xc06f, 0xc04c, 0xc065, 0xc04c, 0x080c, 0x0dd5, 0x0096, 0x080c,
+ 0x9657, 0x6014, 0x2048, 0x2001, 0x1987, 0x2004, 0x6042, 0xa97c,
+ 0xd1ac, 0x0140, 0x6003, 0x0004, 0xa87c, 0x9085, 0x0400, 0xa87e,
+ 0x009e, 0x0005, 0x6003, 0x0002, 0x0cb8, 0x080c, 0x9657, 0x080c,
+ 0xd3a0, 0x080c, 0xd3a5, 0x6003, 0x000f, 0x0804, 0x9763, 0x080c,
+ 0x9657, 0x080c, 0xaf43, 0x0804, 0x9763, 0x9182, 0x0054, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xc091, 0xc091, 0xc091,
+ 0xc091, 0xc091, 0xc093, 0xc170, 0xc091, 0xc1a4, 0xc091, 0xc091,
+ 0xc091, 0xc091, 0xc091, 0xc091, 0xc091, 0xc091, 0xc091, 0xc091,
+ 0xc1a4, 0x080c, 0x0dd5, 0x00b6, 0x0096, 0x6114, 0x2148, 0x7644,
+ 0x96b4, 0x0fff, 0x86ff, 0x1528, 0x6010, 0x2058, 0xb800, 0xd0bc,
+ 0x1904, 0xc15f, 0xa87b, 0x0000, 0xa867, 0x0103, 0xae76, 0xa87c,
+ 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115, 0x190c, 0xc33d, 0x080c,
+ 0x6b33, 0x6210, 0x2258, 0xba3c, 0x82ff, 0x0110, 0x8211, 0xba3e,
+ 0x7044, 0xd0e4, 0x1904, 0xc143, 0x080c, 0xaf43, 0x009e, 0x00be,
+ 0x0005, 0x968c, 0x0c00, 0x0150, 0x6010, 0x2058, 0xb800, 0xd0bc,
+ 0x1904, 0xc147, 0x7348, 0xab92, 0x734c, 0xab8e, 0x968c, 0x00ff,
+ 0x9186, 0x0002, 0x0508, 0x9186, 0x0028, 0x1118, 0xa87b, 0x001c,
+ 0x00e8, 0xd6dc, 0x01a0, 0xa87b, 0x0015, 0xa87c, 0xd0ac, 0x0170,
+ 0xa938, 0xaa34, 0x2100, 0x9205, 0x0148, 0x7048, 0x9106, 0x1118,
+ 0x704c, 0x9206, 0x0118, 0xa992, 0xaa8e, 0xc6dc, 0x0038, 0xd6d4,
+ 0x0118, 0xa87b, 0x0007, 0x0010, 0xa87b, 0x0000, 0xa867, 0x0103,
+ 0xae76, 0x901e, 0xd6c4, 0x01d8, 0x9686, 0x0100, 0x1130, 0x7064,
+ 0x9005, 0x1118, 0xc6c4, 0x0804, 0xc09a, 0x735c, 0xab86, 0x83ff,
+ 0x0170, 0x938a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308,
+ 0x2019, 0x0018, 0x2011, 0x0025, 0x080c, 0xc837, 0x003e, 0xd6cc,
+ 0x0904, 0xc0af, 0x7154, 0xa98a, 0x81ff, 0x0904, 0xc0af, 0x9192,
+ 0x0021, 0x1278, 0x8304, 0x9098, 0x0018, 0x2011, 0x0029, 0x080c,
+ 0xc837, 0x2011, 0x0205, 0x2013, 0x0000, 0x080c, 0xd32d, 0x0804,
+ 0xc0af, 0xa868, 0xd0fc, 0x0120, 0x2009, 0x0020, 0xa98a, 0x0c50,
+ 0x00a6, 0x2950, 0x080c, 0xc7d6, 0x00ae, 0x080c, 0xd32d, 0x080c,
+ 0xc827, 0x0804, 0xc0b1, 0x080c, 0xcf86, 0x0804, 0xc0be, 0xa87c,
+ 0xd0ac, 0x0904, 0xc0ca, 0xa880, 0xd0bc, 0x1904, 0xc0ca, 0x7348,
+ 0xa838, 0x9306, 0x11c8, 0x734c, 0xa834, 0x931e, 0x0904, 0xc0ca,
+ 0xd6d4, 0x0190, 0xab38, 0x9305, 0x0904, 0xc0ca, 0x0068, 0xa87c,
+ 0xd0ac, 0x0904, 0xc0a2, 0xa838, 0xa934, 0x9105, 0x0904, 0xc0a2,
+ 0xa880, 0xd0bc, 0x1904, 0xc0a2, 0x080c, 0xcfc0, 0x0804, 0xc0be,
+ 0x0096, 0x00f6, 0x6003, 0x0003, 0x6007, 0x0043, 0x2079, 0x026c,
+ 0x7c04, 0x7b00, 0x7e0c, 0x7d08, 0x6014, 0x2048, 0xa87c, 0xd0ac,
+ 0x0140, 0x6003, 0x0002, 0x00fe, 0x009e, 0x0005, 0x2130, 0x2228,
+ 0x0058, 0x2400, 0xa9ac, 0x910a, 0x2300, 0xaab0, 0x9213, 0x2600,
+ 0x9102, 0x2500, 0x9203, 0x0e90, 0xac36, 0xab3a, 0xae46, 0xad4a,
+ 0x00fe, 0x6043, 0x0000, 0x2c10, 0x080c, 0x1beb, 0x080c, 0x9216,
+ 0x080c, 0x9891, 0x009e, 0x0005, 0x0005, 0x9182, 0x0054, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xc1c1, 0xc1c1, 0xc1c1,
+ 0xc1c1, 0xc1c1, 0xc1c3, 0xc259, 0xc1c1, 0xc1c1, 0xc270, 0xc300,
+ 0xc1c1, 0xc1c1, 0xc1c1, 0xc1c1, 0xc315, 0xc1c1, 0xc1c1, 0xc1c1,
+ 0xc1c1, 0x080c, 0x0dd5, 0x0076, 0x00a6, 0x00e6, 0x0096, 0x2071,
+ 0x0260, 0x6114, 0x2150, 0x7644, 0xb676, 0x96b4, 0x0fff, 0xb77c,
+ 0xc7e5, 0xb77e, 0x6210, 0x00b6, 0x2258, 0xba3c, 0x82ff, 0x0110,
+ 0x8211, 0xba3e, 0x00be, 0x86ff, 0x0904, 0xc254, 0x9694, 0xff00,
+ 0x9284, 0x0c00, 0x0120, 0x7048, 0xb092, 0x704c, 0xb08e, 0x9284,
+ 0x0300, 0x0904, 0xc254, 0x080c, 0x0fff, 0x090c, 0x0dd5, 0x2900,
+ 0xb07a, 0xb77c, 0xc7cd, 0xb77e, 0xa867, 0x0103, 0xb068, 0xa86a,
+ 0xb06c, 0xa86e, 0xb070, 0xa872, 0xae76, 0x968c, 0x0c00, 0x0120,
+ 0x7348, 0xab92, 0x734c, 0xab8e, 0x968c, 0x00ff, 0x9186, 0x0002,
+ 0x0180, 0x9186, 0x0028, 0x1118, 0xa87b, 0x001c, 0x0060, 0xd6dc,
+ 0x0118, 0xa87b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0xa87b, 0x0007,
+ 0x0010, 0xa87b, 0x0000, 0xaf7e, 0xb080, 0xa882, 0xb084, 0xa886,
+ 0x901e, 0xd6c4, 0x0190, 0x735c, 0xab86, 0x83ff, 0x0170, 0x938a,
+ 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019, 0x0018,
+ 0x2011, 0x0025, 0x080c, 0xc837, 0x003e, 0xd6cc, 0x01e8, 0x7154,
+ 0xa98a, 0x81ff, 0x01c8, 0x9192, 0x0021, 0x1260, 0x8304, 0x9098,
+ 0x0018, 0x2011, 0x0029, 0x080c, 0xc837, 0x2011, 0x0205, 0x2013,
+ 0x0000, 0x0050, 0xb068, 0xd0fc, 0x0120, 0x2009, 0x0020, 0xa98a,
+ 0x0c68, 0x2950, 0x080c, 0xc7d6, 0x009e, 0x00ee, 0x00ae, 0x007e,
+ 0x0005, 0x00f6, 0x00a6, 0x6003, 0x0003, 0x2079, 0x026c, 0x7c04,
+ 0x7b00, 0x7e0c, 0x7d08, 0x6014, 0x2050, 0xb436, 0xb33a, 0xb646,
+ 0xb54a, 0x00ae, 0x00fe, 0x2c10, 0x080c, 0x1beb, 0x0804, 0xa323,
+ 0x6003, 0x0002, 0x6004, 0x9086, 0x0040, 0x11c8, 0x0096, 0x6014,
+ 0x2048, 0xa87c, 0xd0ac, 0x0160, 0x601c, 0xd084, 0x1130, 0x00f6,
+ 0x2c00, 0x2078, 0x080c, 0x1754, 0x00fe, 0x6003, 0x0004, 0x0010,
+ 0x6003, 0x0002, 0x009e, 0x080c, 0x9657, 0x080c, 0x9763, 0x0096,
+ 0x2001, 0x1987, 0x2004, 0x6042, 0x080c, 0x9713, 0x080c, 0x9891,
+ 0x6114, 0x2148, 0xa97c, 0xd1e4, 0x0904, 0xc2fb, 0xd1cc, 0x05c8,
+ 0xa978, 0xa868, 0xd0fc, 0x0540, 0x0016, 0xa87c, 0x0006, 0xa880,
+ 0x0006, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0019, 0x20a0, 0x810e,
+ 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184, 0xffc0, 0x9080,
+ 0x0019, 0x2098, 0x0156, 0x20a9, 0x0020, 0x4003, 0x015e, 0x000e,
+ 0xa882, 0x000e, 0xc0cc, 0xa87e, 0x001e, 0xa874, 0x0006, 0x2148,
+ 0x080c, 0x0fb1, 0x001e, 0x0458, 0x0016, 0x080c, 0x0fb1, 0x009e,
+ 0xa87c, 0xc0cc, 0xa87e, 0xa974, 0x0016, 0x080c, 0xc827, 0x001e,
+ 0x00f0, 0xa867, 0x0103, 0xa974, 0x9184, 0x00ff, 0x90b6, 0x0002,
+ 0x0180, 0x9086, 0x0028, 0x1118, 0xa87b, 0x001c, 0x0060, 0xd1dc,
+ 0x0118, 0xa87b, 0x0015, 0x0038, 0xd1d4, 0x0118, 0xa87b, 0x0007,
+ 0x0010, 0xa87b, 0x0000, 0x0016, 0x080c, 0x6b33, 0x001e, 0xd1e4,
+ 0x1120, 0x080c, 0xaf43, 0x009e, 0x0005, 0x080c, 0xcf86, 0x0cd8,
+ 0x6004, 0x9086, 0x0040, 0x1120, 0x080c, 0x9657, 0x080c, 0x9763,
+ 0x2019, 0x0001, 0x080c, 0xa6ac, 0x6003, 0x0002, 0x080c, 0xd3a5,
+ 0x080c, 0x9713, 0x080c, 0x9891, 0x0005, 0x6004, 0x9086, 0x0040,
+ 0x1120, 0x080c, 0x9657, 0x080c, 0x9763, 0x2019, 0x0001, 0x080c,
+ 0xa6ac, 0x080c, 0x9713, 0x080c, 0x321e, 0x080c, 0xd39d, 0x0096,
+ 0x6114, 0x2148, 0x080c, 0xcc86, 0x0150, 0xa867, 0x0103, 0xa87b,
+ 0x0029, 0xa877, 0x0000, 0x080c, 0x6d17, 0x080c, 0xce71, 0x009e,
+ 0x080c, 0xaf43, 0x080c, 0x9891, 0x0005, 0xa87b, 0x0015, 0xd1fc,
+ 0x0180, 0xa87b, 0x0007, 0x8002, 0x8000, 0x810a, 0x9189, 0x0000,
+ 0x0006, 0x0016, 0x2009, 0x1a79, 0x2104, 0x8000, 0x200a, 0x001e,
+ 0x000e, 0xa992, 0xa88e, 0x0005, 0x9182, 0x0054, 0x1220, 0x9182,
+ 0x0040, 0x0208, 0x000a, 0x0005, 0xc370, 0xc370, 0xc370, 0xc370,
+ 0xc370, 0xc372, 0xc370, 0xc370, 0xc418, 0xc370, 0xc370, 0xc370,
+ 0xc370, 0xc370, 0xc370, 0xc370, 0xc370, 0xc370, 0xc370, 0xc54a,
+ 0x080c, 0x0dd5, 0x0076, 0x00a6, 0x00e6, 0x0096, 0x2071, 0x0260,
+ 0x6114, 0x2150, 0x7644, 0xb676, 0x96b4, 0x0fff, 0xb77c, 0xc7e5,
+ 0xb77e, 0x6210, 0x00b6, 0x2258, 0xba3c, 0x82ff, 0x0110, 0x8211,
+ 0xba3e, 0x00be, 0x86ff, 0x0904, 0xc411, 0x9694, 0xff00, 0x9284,
+ 0x0c00, 0x0120, 0x7048, 0xb092, 0x704c, 0xb08e, 0x9284, 0x0300,
+ 0x0904, 0xc411, 0x9686, 0x0100, 0x1130, 0x7064, 0x9005, 0x1118,
+ 0xc6c4, 0xb676, 0x0c38, 0x080c, 0x0fff, 0x090c, 0x0dd5, 0x2900,
+ 0xb07a, 0xb77c, 0x97bd, 0x0200, 0xb77e, 0xa867, 0x0103, 0xb068,
+ 0xa86a, 0xb06c, 0xa86e, 0xb070, 0xa872, 0x7044, 0x9084, 0xf000,
+ 0x9635, 0xae76, 0x968c, 0x0c00, 0x0120, 0x7348, 0xab92, 0x734c,
+ 0xab8e, 0x968c, 0x00ff, 0x9186, 0x0002, 0x0180, 0x9186, 0x0028,
+ 0x1118, 0xa87b, 0x001c, 0x0060, 0xd6dc, 0x0118, 0xa87b, 0x0015,
+ 0x0038, 0xd6d4, 0x0118, 0xa87b, 0x0007, 0x0010, 0xa87b, 0x0000,
+ 0xaf7e, 0xb080, 0xa882, 0xb084, 0xa886, 0x901e, 0xd6c4, 0x0190,
+ 0x735c, 0xab86, 0x83ff, 0x0170, 0x938a, 0x0009, 0x0210, 0x2019,
+ 0x0008, 0x0036, 0x2308, 0x2019, 0x0018, 0x2011, 0x0025, 0x080c,
+ 0xc837, 0x003e, 0xd6cc, 0x01e8, 0x7154, 0xa98a, 0x81ff, 0x01c8,
+ 0x9192, 0x0021, 0x1260, 0x8304, 0x9098, 0x0018, 0x2011, 0x0029,
+ 0x080c, 0xc837, 0x2011, 0x0205, 0x2013, 0x0000, 0x0050, 0xb068,
+ 0xd0fc, 0x0120, 0x2009, 0x0020, 0xa98a, 0x0c68, 0x2950, 0x080c,
+ 0xc7d6, 0x080c, 0x1a6f, 0x009e, 0x00ee, 0x00ae, 0x007e, 0x0005,
+ 0x2001, 0x1987, 0x2004, 0x6042, 0x0096, 0x6114, 0x2148, 0xa83c,
+ 0xa940, 0x9105, 0x1118, 0xa87c, 0xc0dc, 0xa87e, 0x6003, 0x0002,
+ 0xa97c, 0xd1e4, 0x0904, 0xc545, 0x6043, 0x0000, 0x6010, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1500, 0xd1cc, 0x0904, 0xc514,
+ 0xa978, 0xa868, 0xd0fc, 0x0904, 0xc4d5, 0x0016, 0xa87c, 0x0006,
+ 0xa880, 0x0006, 0x00a6, 0x2150, 0xb174, 0x9184, 0x00ff, 0x90b6,
+ 0x0002, 0x0904, 0xc4a2, 0x9086, 0x0028, 0x1904, 0xc48e, 0xa87b,
+ 0x001c, 0xb07b, 0x001c, 0x0804, 0xc4aa, 0x6024, 0xd0f4, 0x11d0,
+ 0xa838, 0xaa34, 0x9205, 0x09c8, 0xa838, 0xaa90, 0x9206, 0x1120,
+ 0xa88c, 0xaa34, 0x9206, 0x0988, 0x6024, 0xd0d4, 0x1148, 0xa9ac,
+ 0xa834, 0x9102, 0x603a, 0xa9b0, 0xa838, 0x9103, 0x603e, 0x6024,
+ 0xc0f5, 0x6026, 0x6010, 0x00b6, 0x2058, 0xb83c, 0x8000, 0xb83e,
+ 0x00be, 0x9006, 0xa876, 0xa892, 0xa88e, 0xa87c, 0xc0e4, 0xa87e,
+ 0xd0cc, 0x0140, 0xc0cc, 0xa87e, 0x0096, 0xa878, 0x2048, 0x080c,
+ 0x0fb1, 0x009e, 0x080c, 0xcfc0, 0x0804, 0xc545, 0xd1dc, 0x0158,
+ 0xa87b, 0x0015, 0xb07b, 0x0015, 0x080c, 0xd250, 0x0118, 0xb174,
+ 0xc1dc, 0xb176, 0x0078, 0xd1d4, 0x0128, 0xa87b, 0x0007, 0xb07b,
+ 0x0007, 0x0040, 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115,
+ 0x190c, 0xc33d, 0xa87c, 0xb07e, 0xa890, 0xb092, 0xa88c, 0xb08e,
+ 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0019, 0x20a0, 0x20a9, 0x0020,
+ 0x8a06, 0x8006, 0x8007, 0x9094, 0x003f, 0x22e0, 0x9084, 0xffc0,
+ 0x9080, 0x0019, 0x2098, 0x4003, 0x00ae, 0x000e, 0xa882, 0x000e,
+ 0xc0cc, 0xa87e, 0x080c, 0xd32d, 0x001e, 0xa874, 0x0006, 0x2148,
+ 0x080c, 0x0fb1, 0x001e, 0x0804, 0xc541, 0x0016, 0x00a6, 0x2150,
+ 0xb174, 0x9184, 0x00ff, 0x90b6, 0x0002, 0x01e0, 0x9086, 0x0028,
+ 0x1128, 0xa87b, 0x001c, 0xb07b, 0x001c, 0x00e0, 0xd1dc, 0x0158,
+ 0xa87b, 0x0015, 0xb07b, 0x0015, 0x080c, 0xd250, 0x0118, 0xb174,
+ 0xc1dc, 0xb176, 0x0078, 0xd1d4, 0x0128, 0xa87b, 0x0007, 0xb07b,
+ 0x0007, 0x0040, 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115,
+ 0x190c, 0xc33d, 0xa890, 0xb092, 0xa88c, 0xb08e, 0xa87c, 0xb07e,
+ 0x00ae, 0x080c, 0x0fb1, 0x009e, 0x080c, 0xd32d, 0xa974, 0x0016,
+ 0x080c, 0xc827, 0x001e, 0x0468, 0xa867, 0x0103, 0xa974, 0x9184,
+ 0x00ff, 0x90b6, 0x0002, 0x01b0, 0x9086, 0x0028, 0x1118, 0xa87b,
+ 0x001c, 0x00d0, 0xd1dc, 0x0148, 0xa87b, 0x0015, 0x080c, 0xd250,
+ 0x0118, 0xa974, 0xc1dc, 0xa976, 0x0078, 0xd1d4, 0x0118, 0xa87b,
+ 0x0007, 0x0050, 0xa87b, 0x0000, 0xa87c, 0xd0ac, 0x0128, 0xa834,
+ 0xa938, 0x9115, 0x190c, 0xc33d, 0xa974, 0x0016, 0x080c, 0x6b33,
+ 0x001e, 0xd1e4, 0x1120, 0x080c, 0xaf43, 0x009e, 0x0005, 0x080c,
+ 0xcf86, 0x0cd8, 0x6114, 0x0096, 0x2148, 0xa97c, 0xd1e4, 0x190c,
+ 0x1a8d, 0x009e, 0x0005, 0x080c, 0x9657, 0x0010, 0x080c, 0x9713,
+ 0x080c, 0xcc86, 0x01f0, 0x0096, 0x6114, 0x2148, 0x080c, 0xce8e,
+ 0x1118, 0x080c, 0xb905, 0x00a0, 0xa867, 0x0103, 0x2009, 0x180c,
+ 0x210c, 0xd18c, 0x11b8, 0xd184, 0x1190, 0x6108, 0xa97a, 0x918e,
+ 0x0029, 0x1110, 0x080c, 0xea94, 0xa877, 0x0000, 0x080c, 0x6d17,
+ 0x009e, 0x080c, 0xaf43, 0x080c, 0x9763, 0x0804, 0x9891, 0xa87b,
+ 0x0004, 0x0c90, 0xa87b, 0x0004, 0x0c78, 0x9182, 0x0054, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xc5a1, 0xc5a1, 0xc5a1,
+ 0xc5a1, 0xc5a1, 0xc5a3, 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1,
+ 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1, 0xc5a1,
+ 0xc5a1, 0x080c, 0x0dd5, 0x080c, 0x5765, 0x01f8, 0x6014, 0x7144,
+ 0x918c, 0x0fff, 0x9016, 0xd1c4, 0x0118, 0x7264, 0x9294, 0x00ff,
+ 0x0096, 0x904d, 0x0188, 0xa87b, 0x0000, 0xa864, 0x9086, 0x0139,
+ 0x0128, 0xa867, 0x0103, 0xa976, 0xaa96, 0x0030, 0xa897, 0x4000,
+ 0xa99a, 0xaa9e, 0x080c, 0x6d17, 0x009e, 0x0804, 0xaf43, 0x9182,
+ 0x0085, 0x0002, 0xc5d9, 0xc5d7, 0xc5d7, 0xc5e5, 0xc5d7, 0xc5d7,
+ 0xc5d7, 0xc5d7, 0xc5d7, 0xc5d7, 0xc5d7, 0xc5d7, 0xc5d7, 0x080c,
+ 0x0dd5, 0x6003, 0x0001, 0x6106, 0x080c, 0x91b1, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x9763, 0x012e, 0x0005, 0x0026, 0x0056, 0x00d6,
+ 0x00e6, 0x2071, 0x0260, 0x7224, 0x6216, 0x7220, 0x080c, 0xcc74,
+ 0x01f8, 0x2268, 0x6800, 0x9086, 0x0000, 0x01d0, 0x6010, 0x6d10,
+ 0x952e, 0x11b0, 0x00c6, 0x2d60, 0x00d6, 0x080c, 0xc898, 0x00de,
+ 0x00ce, 0x0158, 0x702c, 0xd084, 0x1118, 0x080c, 0xc862, 0x0010,
+ 0x6803, 0x0002, 0x6007, 0x0086, 0x0028, 0x080c, 0xc884, 0x0d90,
+ 0x6007, 0x0087, 0x6003, 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763,
+ 0x7220, 0x080c, 0xcc74, 0x0178, 0x6810, 0x00b6, 0x2058, 0xb800,
+ 0x00be, 0xd0bc, 0x0140, 0x6824, 0xd0ec, 0x0128, 0x00c6, 0x2d60,
+ 0x080c, 0xcfc0, 0x00ce, 0x00ee, 0x00de, 0x005e, 0x002e, 0x0005,
+ 0x9186, 0x0013, 0x1160, 0x6004, 0x908a, 0x0085, 0x0a0c, 0x0dd5,
+ 0x908a, 0x0092, 0x1a0c, 0x0dd5, 0x9082, 0x0085, 0x00e2, 0x9186,
+ 0x0027, 0x0120, 0x9186, 0x0014, 0x190c, 0x0dd5, 0x080c, 0x9657,
+ 0x0096, 0x6014, 0x2048, 0x080c, 0xcc86, 0x0140, 0xa867, 0x0103,
+ 0xa877, 0x0000, 0xa87b, 0x0029, 0x080c, 0x6d17, 0x009e, 0x080c,
+ 0xaf74, 0x0804, 0x9763, 0xc668, 0xc66a, 0xc66a, 0xc668, 0xc668,
+ 0xc668, 0xc668, 0xc668, 0xc668, 0xc668, 0xc668, 0xc668, 0xc668,
+ 0x080c, 0x0dd5, 0x080c, 0x9657, 0x080c, 0xaf74, 0x080c, 0x9763,
+ 0x0005, 0x9186, 0x0013, 0x1128, 0x6004, 0x9082, 0x0085, 0x2008,
+ 0x04b8, 0x9186, 0x0027, 0x11f8, 0x080c, 0x9657, 0x080c, 0x321e,
+ 0x080c, 0xd39d, 0x0096, 0x6014, 0x2048, 0x080c, 0xcc86, 0x0150,
+ 0xa867, 0x0103, 0xa877, 0x0000, 0xa87b, 0x0029, 0x080c, 0x6d17,
+ 0x080c, 0xce71, 0x009e, 0x080c, 0xaf43, 0x080c, 0x9763, 0x0005,
+ 0x080c, 0xafd9, 0x0ce0, 0x9186, 0x0014, 0x1dd0, 0x080c, 0x9657,
+ 0x0096, 0x6014, 0x2048, 0x080c, 0xcc86, 0x0d60, 0xa867, 0x0103,
+ 0xa877, 0x0000, 0xa87b, 0x0006, 0xa880, 0xc0ec, 0xa882, 0x08f0,
+ 0x0002, 0xc6c0, 0xc6be, 0xc6be, 0xc6be, 0xc6be, 0xc6be, 0xc6d8,
+ 0xc6be, 0xc6be, 0xc6be, 0xc6be, 0xc6be, 0xc6be, 0x080c, 0x0dd5,
+ 0x080c, 0x9657, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039,
+ 0x0118, 0x9186, 0x0035, 0x1118, 0x2001, 0x1985, 0x0010, 0x2001,
+ 0x1986, 0x2004, 0x601a, 0x6003, 0x000c, 0x080c, 0x9763, 0x0005,
+ 0x080c, 0x9657, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039,
+ 0x0118, 0x9186, 0x0035, 0x1118, 0x2001, 0x1985, 0x0010, 0x2001,
+ 0x1986, 0x2004, 0x601a, 0x6003, 0x000e, 0x080c, 0x9763, 0x0005,
+ 0x9182, 0x0092, 0x1220, 0x9182, 0x0085, 0x0208, 0x0012, 0x0804,
+ 0xafd9, 0xc706, 0xc706, 0xc706, 0xc706, 0xc708, 0xc755, 0xc706,
+ 0xc706, 0xc706, 0xc706, 0xc706, 0xc706, 0xc706, 0x080c, 0x0dd5,
+ 0x0096, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0168,
+ 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039, 0x0118, 0x9186,
+ 0x0035, 0x1118, 0x009e, 0x0804, 0xc769, 0x080c, 0xcc86, 0x1118,
+ 0x080c, 0xce71, 0x0068, 0x6014, 0x2048, 0xa87c, 0xd0e4, 0x1110,
+ 0x080c, 0xce71, 0xa867, 0x0103, 0x080c, 0xd368, 0x080c, 0x6d17,
+ 0x00d6, 0x2c68, 0x080c, 0xaeed, 0x01d0, 0x6003, 0x0001, 0x6007,
+ 0x001e, 0x600b, 0xffff, 0x2009, 0x026e, 0x210c, 0x613a, 0x2009,
+ 0x026f, 0x210c, 0x613e, 0x6910, 0x6112, 0x080c, 0xd102, 0x6954,
+ 0x6156, 0x6023, 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763, 0x2d60,
+ 0x00de, 0x080c, 0xaf43, 0x009e, 0x0005, 0x6010, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0bc, 0x05a0, 0x6034, 0x908c, 0xff00, 0x810f,
+ 0x9186, 0x0035, 0x0130, 0x9186, 0x001e, 0x0118, 0x9186, 0x0039,
+ 0x1538, 0x00d6, 0x2c68, 0x080c, 0xd300, 0x11f0, 0x080c, 0xaeed,
+ 0x01d8, 0x6106, 0x6003, 0x0001, 0x6023, 0x0001, 0x6910, 0x6112,
+ 0x692c, 0x612e, 0x6930, 0x6132, 0x6934, 0x918c, 0x00ff, 0x6136,
+ 0x6938, 0x613a, 0x693c, 0x613e, 0x6954, 0x6156, 0x080c, 0xd102,
+ 0x080c, 0x91b1, 0x080c, 0x9763, 0x2d60, 0x00de, 0x0804, 0xaf43,
+ 0x0096, 0x6014, 0x2048, 0x080c, 0xcc86, 0x01c8, 0xa867, 0x0103,
+ 0xa880, 0xd0b4, 0x0128, 0xc0ec, 0xa882, 0xa87b, 0x0006, 0x0048,
+ 0xd0bc, 0x0118, 0xa87b, 0x0002, 0x0020, 0xa87b, 0x0005, 0x080c,
+ 0xcf82, 0xa877, 0x0000, 0x080c, 0x6d17, 0x080c, 0xce71, 0x009e,
+ 0x0804, 0xaf43, 0x0016, 0x0096, 0x6014, 0x2048, 0x080c, 0xcc86,
+ 0x0140, 0xa867, 0x0103, 0xa87b, 0x0028, 0xa877, 0x0000, 0x080c,
+ 0x6d17, 0x009e, 0x001e, 0x9186, 0x0013, 0x0148, 0x9186, 0x0014,
+ 0x0130, 0x9186, 0x0027, 0x0118, 0x080c, 0xafd9, 0x0030, 0x080c,
+ 0x9657, 0x080c, 0xaf74, 0x080c, 0x9763, 0x0005, 0x0056, 0x0066,
+ 0x0096, 0x00a6, 0x2029, 0x0001, 0x9182, 0x0101, 0x1208, 0x0010,
+ 0x2009, 0x0100, 0x2130, 0x8304, 0x9098, 0x0018, 0x2009, 0x0020,
+ 0x2011, 0x0029, 0x080c, 0xc837, 0x96b2, 0x0020, 0xb004, 0x904d,
+ 0x0110, 0x080c, 0x0fb1, 0x080c, 0x0fff, 0x0520, 0x8528, 0xa867,
+ 0x0110, 0xa86b, 0x0000, 0x2920, 0xb406, 0x968a, 0x003d, 0x1228,
+ 0x2608, 0x2011, 0x001b, 0x0499, 0x00a8, 0x96b2, 0x003c, 0x2009,
+ 0x003c, 0x2950, 0x2011, 0x001b, 0x0451, 0x0c28, 0x2001, 0x0205,
+ 0x2003, 0x0000, 0x00ae, 0x852f, 0x95ad, 0x0003, 0xb566, 0x95ac,
+ 0x0000, 0x0048, 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f,
+ 0x95ad, 0x0003, 0xb566, 0x009e, 0x006e, 0x005e, 0x0005, 0x00a6,
+ 0x89ff, 0x0158, 0xa804, 0x9055, 0x0130, 0xa807, 0x0000, 0x080c,
+ 0x6d17, 0x2a48, 0x0cb8, 0x080c, 0x6d17, 0x00ae, 0x0005, 0x00f6,
+ 0x2079, 0x0200, 0x7814, 0x9085, 0x0080, 0x7816, 0xd184, 0x0108,
+ 0x8108, 0x810c, 0x20a9, 0x0001, 0xa860, 0x20e8, 0xa85c, 0x9200,
+ 0x20a0, 0x20e1, 0x0000, 0x2300, 0x9e00, 0x2098, 0x4003, 0x8318,
+ 0x9386, 0x0020, 0x1148, 0x2018, 0x2300, 0x9e00, 0x2098, 0x7814,
+ 0x8000, 0x9085, 0x0080, 0x7816, 0x8109, 0x1d80, 0x7817, 0x0000,
+ 0x00fe, 0x0005, 0x6920, 0x9186, 0x0003, 0x0118, 0x9186, 0x0002,
+ 0x11d0, 0x00c6, 0x00d6, 0x00e6, 0x2d60, 0x0096, 0x6014, 0x2048,
+ 0x080c, 0xcc86, 0x0150, 0x2001, 0x0006, 0xa980, 0xc1d5, 0x080c,
+ 0x6f4a, 0x080c, 0x6d0b, 0x080c, 0xce71, 0x009e, 0x080c, 0xaf74,
+ 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x702c, 0xd084, 0x1170,
+ 0x6008, 0x2060, 0x6020, 0x9086, 0x0002, 0x1140, 0x6104, 0x9186,
+ 0x0085, 0x0118, 0x9186, 0x008b, 0x1108, 0x9006, 0x00ce, 0x0005,
+ 0x0066, 0x0126, 0x2091, 0x8000, 0x2031, 0x0001, 0x6020, 0x9084,
+ 0x000f, 0x0083, 0x012e, 0x006e, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x0066, 0x2031, 0x0000, 0x6020, 0x9084, 0x000f, 0x001b, 0x006e,
+ 0x012e, 0x0005, 0xc8d3, 0xc8d3, 0xc8ce, 0xc8f5, 0xc8c1, 0xc8ce,
+ 0xc8f5, 0xc8ce, 0xc8c1, 0x8f95, 0xc8ce, 0xc8ce, 0xc8ce, 0xc8c1,
+ 0xc8c1, 0x080c, 0x0dd5, 0x0036, 0x2019, 0x0010, 0x080c, 0xe2c0,
+ 0x6023, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005, 0x9006, 0x0005,
+ 0x9085, 0x0001, 0x0005, 0x0096, 0x86ff, 0x11d8, 0x6014, 0x2048,
+ 0x080c, 0xcc86, 0x01c0, 0xa864, 0x9086, 0x0139, 0x1128, 0xa87b,
+ 0x0005, 0xa883, 0x0000, 0x0028, 0x900e, 0x2001, 0x0005, 0x080c,
+ 0x6f4a, 0x080c, 0xcf82, 0x080c, 0x6d0b, 0x080c, 0xaf74, 0x9085,
+ 0x0001, 0x009e, 0x0005, 0x9006, 0x0ce0, 0x6000, 0x908a, 0x0016,
+ 0x1a0c, 0x0dd5, 0x0002, 0xc90b, 0xc93b, 0xc90d, 0xc95c, 0xc936,
+ 0xc90b, 0xc8ce, 0xc8d3, 0xc8d3, 0xc8ce, 0xc8ce, 0xc8ce, 0xc8ce,
+ 0xc8ce, 0xc8ce, 0xc8ce, 0x080c, 0x0dd5, 0x86ff, 0x1520, 0x6020,
+ 0x9086, 0x0006, 0x0500, 0x0096, 0x6014, 0x2048, 0x080c, 0xcc86,
+ 0x0168, 0xa87c, 0xd0cc, 0x0140, 0x0096, 0xc0cc, 0xa87e, 0xa878,
+ 0x2048, 0x080c, 0x0fb1, 0x009e, 0x080c, 0xcf82, 0x009e, 0x080c,
+ 0xd342, 0x6007, 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c,
+ 0x91b1, 0x080c, 0x9763, 0x9085, 0x0001, 0x0005, 0x0066, 0x080c,
+ 0x1aa1, 0x006e, 0x0890, 0x00e6, 0x2071, 0x19e6, 0x7024, 0x9c06,
+ 0x1120, 0x080c, 0xa636, 0x00ee, 0x0840, 0x6020, 0x9084, 0x000f,
+ 0x9086, 0x0006, 0x1150, 0x0086, 0x0096, 0x2049, 0x0001, 0x2c40,
+ 0x080c, 0xa76b, 0x009e, 0x008e, 0x0010, 0x080c, 0xa533, 0x00ee,
+ 0x1904, 0xc90d, 0x0804, 0xc8ce, 0x0036, 0x00e6, 0x2071, 0x19e6,
+ 0x703c, 0x9c06, 0x1138, 0x901e, 0x080c, 0xa6ac, 0x00ee, 0x003e,
+ 0x0804, 0xc90d, 0x080c, 0xa89b, 0x00ee, 0x003e, 0x1904, 0xc90d,
+ 0x0804, 0xc8ce, 0x00c6, 0x6020, 0x9084, 0x000f, 0x0013, 0x00ce,
+ 0x0005, 0xc98f, 0xca5a, 0xcbc4, 0xc999, 0xaf74, 0xc98f, 0xe2b2,
+ 0xd3aa, 0xca5a, 0x8f67, 0xcc50, 0xc988, 0xc988, 0xc988, 0xc988,
+ 0x080c, 0x0dd5, 0x080c, 0xce8e, 0x1110, 0x080c, 0xb905, 0x0005,
+ 0x080c, 0x9657, 0x080c, 0x9763, 0x0804, 0xaf43, 0x601b, 0x0001,
+ 0x0005, 0x080c, 0xcc86, 0x0130, 0x6014, 0x0096, 0x2048, 0x2c00,
+ 0xa896, 0x009e, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0dd5, 0x0002,
+ 0xc9b8, 0xc9ba, 0xc9de, 0xc9f2, 0xca18, 0xc9b8, 0xc98f, 0xc98f,
+ 0xc98f, 0xc9f2, 0xc9f2, 0xc9b8, 0xc9b8, 0xc9b8, 0xc9b8, 0xc9fc,
+ 0x080c, 0x0dd5, 0x00e6, 0x6014, 0x0096, 0x2048, 0xa880, 0xc0b5,
+ 0xa882, 0x009e, 0x2071, 0x19e6, 0x7024, 0x9c06, 0x01a0, 0x080c,
+ 0xa533, 0x080c, 0xd342, 0x6007, 0x0085, 0x6003, 0x000b, 0x6023,
+ 0x0002, 0x2001, 0x1986, 0x2004, 0x601a, 0x080c, 0x91b1, 0x080c,
+ 0x9763, 0x00ee, 0x0005, 0x601b, 0x0001, 0x0cd8, 0x0096, 0x6014,
+ 0x2048, 0xa880, 0xc0b5, 0xa882, 0x009e, 0x080c, 0xd342, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c, 0x91b1, 0x080c,
+ 0x9763, 0x0005, 0x0096, 0x601b, 0x0001, 0x6014, 0x2048, 0xa880,
+ 0xc0b5, 0xa882, 0x009e, 0x0005, 0x080c, 0x5765, 0x01b8, 0x6014,
+ 0x0096, 0x904d, 0x0190, 0xa864, 0xa867, 0x0103, 0xa87b, 0x0006,
+ 0x9086, 0x0139, 0x1150, 0xa867, 0x0139, 0xa87b, 0x0030, 0xa897,
+ 0x4005, 0xa89b, 0x0004, 0x080c, 0x6d17, 0x009e, 0x0804, 0xaf43,
+ 0x6014, 0x0096, 0x904d, 0x05c8, 0xa97c, 0xd1e4, 0x05b0, 0x2001,
+ 0x180f, 0x2004, 0xd0c4, 0x0110, 0x009e, 0x0005, 0xa884, 0x009e,
+ 0x8003, 0x800b, 0x810b, 0x9108, 0x611a, 0x2001, 0x0030, 0x2c08,
+ 0x080c, 0x15fd, 0x2001, 0x030c, 0x2004, 0x9086, 0x0041, 0x11a0,
+ 0x6014, 0x0096, 0x904d, 0x090c, 0x0dd5, 0xa880, 0xd0f4, 0x1130,
+ 0xc0f5, 0xa882, 0x009e, 0x601b, 0x0002, 0x0070, 0x009e, 0x2001,
+ 0x0037, 0x2c08, 0x080c, 0x15fd, 0x6000, 0x9086, 0x0004, 0x1120,
+ 0x2009, 0x0048, 0x080c, 0xafbe, 0x0005, 0x009e, 0x080c, 0x1aa1,
+ 0x0804, 0xc9de, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0dd5, 0x000b,
+ 0x0005, 0xca71, 0xc996, 0xca73, 0xca71, 0xca73, 0xca73, 0xc990,
+ 0xca71, 0xc98a, 0xc98a, 0xca71, 0xca71, 0xca71, 0xca71, 0xca71,
+ 0xca71, 0x080c, 0x0dd5, 0x6010, 0x00b6, 0x2058, 0xb804, 0x9084,
+ 0x00ff, 0x00be, 0x908a, 0x000c, 0x1a0c, 0x0dd5, 0x00b6, 0x0013,
+ 0x00be, 0x0005, 0xca8e, 0xcb5b, 0xca90, 0xcad0, 0xca90, 0xcad0,
+ 0xca90, 0xca9e, 0xca8e, 0xcad0, 0xca8e, 0xcabf, 0x080c, 0x0dd5,
+ 0x6004, 0x908e, 0x0016, 0x05c0, 0x908e, 0x0004, 0x05a8, 0x908e,
+ 0x0002, 0x0590, 0x908e, 0x0052, 0x0904, 0xcb57, 0x6004, 0x080c,
+ 0xce8e, 0x0904, 0xcb74, 0x908e, 0x0004, 0x1110, 0x080c, 0x3247,
+ 0x908e, 0x0021, 0x0904, 0xcb78, 0x908e, 0x0022, 0x0904, 0xcbbf,
+ 0x908e, 0x003d, 0x0904, 0xcb78, 0x908e, 0x0039, 0x0904, 0xcb7c,
+ 0x908e, 0x0035, 0x0904, 0xcb7c, 0x908e, 0x001e, 0x0178, 0x908e,
+ 0x0001, 0x1140, 0x6010, 0x2058, 0xb804, 0x9084, 0x00ff, 0x9086,
+ 0x0006, 0x0110, 0x080c, 0x321e, 0x080c, 0xb905, 0x0804, 0xaf74,
+ 0x00c6, 0x00d6, 0x6104, 0x9186, 0x0016, 0x0904, 0xcb48, 0x9186,
+ 0x0002, 0x1904, 0xcb1d, 0x2001, 0x1837, 0x2004, 0xd08c, 0x11c8,
+ 0x080c, 0x743e, 0x11b0, 0x080c, 0xd388, 0x0138, 0x080c, 0x7461,
+ 0x1120, 0x080c, 0x7348, 0x0804, 0xcba8, 0x2001, 0x197c, 0x2003,
+ 0x0001, 0x2001, 0x1800, 0x2003, 0x0001, 0x080c, 0x736a, 0x0804,
+ 0xcba8, 0x6010, 0x2058, 0x2001, 0x1837, 0x2004, 0xd0ac, 0x1904,
+ 0xcba8, 0xb8a0, 0x9084, 0xff80, 0x1904, 0xcba8, 0xb840, 0x9084,
+ 0x00ff, 0x9005, 0x0190, 0x8001, 0xb842, 0x6017, 0x0000, 0x6023,
+ 0x0007, 0x601b, 0x0398, 0x6043, 0x0000, 0x080c, 0xaeed, 0x0128,
+ 0x2b00, 0x6012, 0x6023, 0x0001, 0x0458, 0x00de, 0x00ce, 0x6004,
+ 0x908e, 0x0002, 0x11a0, 0x6010, 0x2058, 0xb8a0, 0x9086, 0x007e,
+ 0x1170, 0x2009, 0x1837, 0x2104, 0xc085, 0x200a, 0x00e6, 0x2071,
+ 0x1800, 0x080c, 0x6040, 0x00ee, 0x080c, 0xb905, 0x0030, 0x080c,
+ 0xb905, 0x080c, 0x321e, 0x080c, 0xd39d, 0x00e6, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x3247, 0x012e, 0x00ee, 0x080c, 0xaf74, 0x0005,
+ 0x2001, 0x0002, 0x080c, 0x65e9, 0x6003, 0x0001, 0x6007, 0x0002,
+ 0x080c, 0x91f9, 0x080c, 0x9763, 0x00de, 0x00ce, 0x0c80, 0x080c,
+ 0x3247, 0x0804, 0xcacc, 0x00c6, 0x00d6, 0x6104, 0x9186, 0x0016,
+ 0x0d38, 0x6010, 0x2058, 0xb840, 0x9084, 0x00ff, 0x9005, 0x0904,
+ 0xcb1d, 0x8001, 0xb842, 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c,
+ 0x9763, 0x00de, 0x00ce, 0x0898, 0x080c, 0xb905, 0x0804, 0xcace,
+ 0x080c, 0xb941, 0x0804, 0xcace, 0x00d6, 0x2c68, 0x6104, 0x080c,
+ 0xd300, 0x00de, 0x0118, 0x080c, 0xaf43, 0x0408, 0x6004, 0x8007,
+ 0x6134, 0x918c, 0x00ff, 0x9105, 0x6036, 0x6007, 0x0085, 0x6003,
+ 0x000b, 0x6023, 0x0002, 0x603c, 0x600a, 0x2001, 0x1986, 0x2004,
+ 0x601a, 0x602c, 0x2c08, 0x2060, 0x6024, 0xd0b4, 0x0108, 0xc085,
+ 0xc0b5, 0x6026, 0x2160, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0005,
+ 0x00de, 0x00ce, 0x080c, 0xb905, 0x080c, 0x321e, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x3247, 0x6017, 0x0000, 0x6023, 0x0007,
+ 0x601b, 0x0398, 0x6043, 0x0000, 0x012e, 0x00ee, 0x0005, 0x080c,
+ 0xb374, 0x1904, 0xcb74, 0x0005, 0x6000, 0x908a, 0x0016, 0x1a0c,
+ 0x0dd5, 0x0096, 0x00d6, 0x001b, 0x00de, 0x009e, 0x0005, 0xcbdf,
+ 0xcbdf, 0xcbdf, 0xcbdf, 0xcbdf, 0xcbdf, 0xcbdf, 0xcbdf, 0xcbdf,
+ 0xc98f, 0xcbdf, 0xc996, 0xcbe1, 0xc996, 0xcbfb, 0xcbdf, 0x080c,
+ 0x0dd5, 0x6004, 0x9086, 0x008b, 0x01b0, 0x6034, 0x908c, 0xff00,
+ 0x810f, 0x9186, 0x0035, 0x1130, 0x602c, 0x9080, 0x0009, 0x200c,
+ 0xc185, 0x2102, 0x6007, 0x008b, 0x6003, 0x000d, 0x080c, 0x91b1,
+ 0x080c, 0x9763, 0x0005, 0x080c, 0xd37c, 0x0118, 0x080c, 0xd38f,
+ 0x0010, 0x080c, 0xd39d, 0x080c, 0xce71, 0x080c, 0xcc86, 0x0570,
+ 0x080c, 0x321e, 0x080c, 0xcc86, 0x0168, 0x6014, 0x2048, 0xa867,
+ 0x0103, 0xa87b, 0x0006, 0xa877, 0x0000, 0xa880, 0xc0ed, 0xa882,
+ 0x080c, 0x6d17, 0x2c68, 0x080c, 0xaeed, 0x0150, 0x6810, 0x6012,
+ 0x080c, 0xd102, 0x00c6, 0x2d60, 0x080c, 0xaf74, 0x00ce, 0x0008,
+ 0x2d60, 0x6017, 0x0000, 0x6023, 0x0001, 0x6007, 0x0001, 0x6003,
+ 0x0001, 0x080c, 0x91f9, 0x080c, 0x9763, 0x00c8, 0x080c, 0xd37c,
+ 0x0138, 0x6034, 0x9086, 0x4000, 0x1118, 0x080c, 0x321e, 0x08d0,
+ 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039, 0x0118, 0x9186,
+ 0x0035, 0x1118, 0x080c, 0x321e, 0x0868, 0x080c, 0xaf74, 0x0005,
+ 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0dd5, 0x0002, 0xcc66, 0xcc66,
+ 0xcc68, 0xcc68, 0xcc68, 0xcc66, 0xcc66, 0xaf74, 0xcc66, 0xcc66,
+ 0xcc66, 0xcc66, 0xcc66, 0xcc66, 0xcc66, 0xcc66, 0x080c, 0x0dd5,
+ 0x080c, 0xa89b, 0x6114, 0x0096, 0x2148, 0xa87b, 0x0006, 0x080c,
+ 0x6d17, 0x009e, 0x0804, 0xaf43, 0x9284, 0x0007, 0x1158, 0x9282,
+ 0x1cd0, 0x0240, 0x2001, 0x181a, 0x2004, 0x9202, 0x1218, 0x9085,
+ 0x0001, 0x0005, 0x9006, 0x0ce8, 0x0096, 0x0028, 0x0096, 0x0006,
+ 0x6014, 0x2048, 0x000e, 0x0006, 0x9984, 0xf000, 0x9086, 0xf000,
+ 0x0110, 0x080c, 0x10aa, 0x000e, 0x009e, 0x0005, 0x00e6, 0x00c6,
+ 0x0036, 0x0006, 0x0126, 0x2091, 0x8000, 0x2061, 0x1cd0, 0x2071,
+ 0x1800, 0x7354, 0x7074, 0x9302, 0x1640, 0x6020, 0x9206, 0x11f8,
+ 0x080c, 0xd388, 0x0180, 0x9286, 0x0001, 0x1168, 0x6004, 0x9086,
+ 0x0004, 0x1148, 0x080c, 0x321e, 0x080c, 0xd39d, 0x00c6, 0x080c,
+ 0xaf74, 0x00ce, 0x0060, 0x080c, 0xd07c, 0x0148, 0x080c, 0xce8e,
+ 0x1110, 0x080c, 0xb905, 0x00c6, 0x080c, 0xaf43, 0x00ce, 0x9ce0,
+ 0x0018, 0x7068, 0x9c02, 0x1208, 0x08a0, 0x012e, 0x000e, 0x003e,
+ 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x00c6, 0x0016, 0x9188, 0x1000,
+ 0x210c, 0x81ff, 0x0128, 0x2061, 0x1ab2, 0x6112, 0x080c, 0x321e,
+ 0x9006, 0x0010, 0x9085, 0x0001, 0x001e, 0x00ce, 0x00ee, 0x0005,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xaeed, 0x01b0, 0x6656,
+ 0x2b00, 0x6012, 0x080c, 0x5765, 0x0118, 0x080c, 0xcdb5, 0x0168,
+ 0x080c, 0xd102, 0x6023, 0x0003, 0x2009, 0x004b, 0x080c, 0xafbe,
+ 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0xbaa0, 0x080c, 0xaf91, 0x0560, 0x6057,
+ 0x0000, 0x2b00, 0x6012, 0x080c, 0xd102, 0x6023, 0x0003, 0x0016,
+ 0x080c, 0x9356, 0x0076, 0x903e, 0x080c, 0x9229, 0x2c08, 0x080c,
+ 0xe477, 0x007e, 0x001e, 0xd184, 0x0128, 0x080c, 0xaf43, 0x9085,
+ 0x0001, 0x0070, 0x080c, 0x5765, 0x0128, 0xd18c, 0x1170, 0x080c,
+ 0xcdb5, 0x0148, 0x2009, 0x004c, 0x080c, 0xafbe, 0x9085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x2900, 0x6016, 0x0c90,
+ 0x2009, 0x004d, 0x0010, 0x2009, 0x004e, 0x00f6, 0x00c6, 0x0046,
+ 0x0016, 0x080c, 0xaeed, 0x2c78, 0x05a0, 0x7e56, 0x2b00, 0x7812,
+ 0x7823, 0x0003, 0x0016, 0x2021, 0x0005, 0x080c, 0xcdc7, 0x001e,
+ 0x9186, 0x004d, 0x0118, 0x9186, 0x004e, 0x0148, 0x2001, 0x197f,
+ 0x200c, 0xd1fc, 0x0168, 0x2f60, 0x080c, 0xaf43, 0x00d0, 0x2001,
+ 0x197e, 0x200c, 0xd1fc, 0x0120, 0x2f60, 0x080c, 0xaf43, 0x0088,
+ 0x2f60, 0x080c, 0x5765, 0x0138, 0xd18c, 0x1118, 0x04f1, 0x0148,
+ 0x0010, 0x2900, 0x7816, 0x001e, 0x0016, 0x080c, 0xafbe, 0x9085,
+ 0x0001, 0x001e, 0x004e, 0x00ce, 0x00fe, 0x0005, 0x00f6, 0x00c6,
+ 0x0046, 0x080c, 0xaeed, 0x2c78, 0x0508, 0x7e56, 0x2b00, 0x7812,
+ 0x7823, 0x0003, 0x0096, 0x2021, 0x0004, 0x0489, 0x009e, 0x2001,
+ 0x197d, 0x200c, 0xd1fc, 0x0120, 0x2f60, 0x080c, 0xaf43, 0x0060,
+ 0x2f60, 0x080c, 0x5765, 0x0120, 0xd18c, 0x1160, 0x0071, 0x0130,
+ 0x2009, 0x0052, 0x080c, 0xafbe, 0x9085, 0x0001, 0x004e, 0x00ce,
+ 0x00fe, 0x0005, 0x2900, 0x7816, 0x0c98, 0x00c6, 0x080c, 0x4b1f,
+ 0x00ce, 0x1120, 0x080c, 0xaf43, 0x9006, 0x0005, 0xa867, 0x0000,
+ 0xa86b, 0x8000, 0x2900, 0x6016, 0x9085, 0x0001, 0x0005, 0x0096,
+ 0x0076, 0x0126, 0x2091, 0x8000, 0x080c, 0x67cd, 0x0158, 0x2001,
+ 0xcdcc, 0x0006, 0x900e, 0x2400, 0x080c, 0x6f4a, 0x080c, 0x6d17,
+ 0x000e, 0x0807, 0x2418, 0x080c, 0x95f1, 0xbaa0, 0x0086, 0x2041,
+ 0x0001, 0x2039, 0x0001, 0x2608, 0x080c, 0x936e, 0x008e, 0x080c,
+ 0x9229, 0x2f08, 0x2648, 0x080c, 0xe477, 0xb93c, 0x81ff, 0x090c,
+ 0x9441, 0x080c, 0x9763, 0x012e, 0x007e, 0x009e, 0x0005, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0xaeed, 0x0190, 0x660a, 0x2b08,
+ 0x6112, 0x080c, 0xd102, 0x6023, 0x0001, 0x2900, 0x6016, 0x2009,
+ 0x001f, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0x9006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xaf91,
+ 0x01b8, 0x660a, 0x2b08, 0x6112, 0x080c, 0xd102, 0x6023, 0x0008,
+ 0x2900, 0x6016, 0x00f6, 0x2c78, 0x080c, 0x1754, 0x00fe, 0x2009,
+ 0x0021, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0x9006, 0x0cd8, 0x2009, 0x003d, 0x00c6, 0x0126, 0x0016, 0x2091,
+ 0x8000, 0x080c, 0xaeed, 0x0198, 0x660a, 0x2b08, 0x6112, 0x080c,
+ 0xd102, 0x6023, 0x0001, 0x2900, 0x6016, 0x001e, 0x0016, 0x080c,
+ 0xafbe, 0x9085, 0x0001, 0x001e, 0x012e, 0x00ce, 0x0005, 0x9006,
+ 0x0cd0, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xaf91, 0x0188,
+ 0x2b08, 0x6112, 0x080c, 0xd102, 0x6023, 0x0001, 0x2900, 0x6016,
+ 0x2009, 0x0000, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0x9006, 0x0cd8, 0x2009, 0x0044, 0x0830, 0x2009, 0x0049,
+ 0x0818, 0x0026, 0x00b6, 0x6210, 0x2258, 0xba3c, 0x82ff, 0x0110,
+ 0x8211, 0xba3e, 0x00be, 0x002e, 0x0005, 0x0006, 0x0016, 0x6004,
+ 0x908e, 0x0002, 0x0140, 0x908e, 0x0003, 0x0128, 0x908e, 0x0004,
+ 0x0110, 0x9085, 0x0001, 0x001e, 0x000e, 0x0005, 0x0006, 0x0086,
+ 0x0096, 0x6020, 0x9086, 0x0004, 0x01a8, 0x6014, 0x904d, 0x080c,
+ 0xcc86, 0x0180, 0xa864, 0x9086, 0x0139, 0x0170, 0x6020, 0x90c6,
+ 0x0003, 0x0140, 0x90c6, 0x0002, 0x0128, 0xa868, 0xd0fc, 0x0110,
+ 0x9006, 0x0010, 0x9085, 0x0001, 0x009e, 0x008e, 0x000e, 0x0005,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xaf91, 0x0198, 0x2b08,
+ 0x6112, 0x080c, 0xd102, 0x6023, 0x0001, 0x2900, 0x6016, 0x080c,
+ 0x321e, 0x2009, 0x0028, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e,
+ 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x9186, 0x0015, 0x11a8, 0x2011,
+ 0x1824, 0x2204, 0x9086, 0x0074, 0x1178, 0x00b6, 0x080c, 0xbb59,
+ 0x00be, 0x080c, 0xbd7c, 0x6003, 0x0001, 0x6007, 0x0029, 0x080c,
+ 0x91f9, 0x080c, 0x9763, 0x0078, 0x6014, 0x0096, 0x2048, 0xa868,
+ 0x009e, 0xd0fc, 0x0148, 0x2001, 0x0001, 0x080c, 0xd2c1, 0x080c,
+ 0xb905, 0x080c, 0xaf43, 0x0005, 0x0096, 0x6014, 0x904d, 0x090c,
+ 0x0dd5, 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4005, 0xa89b,
+ 0x0004, 0xa867, 0x0139, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17,
+ 0x012e, 0x009e, 0x080c, 0xaf43, 0x0c30, 0x0096, 0x9186, 0x0016,
+ 0x1128, 0x2001, 0x0004, 0x080c, 0x65e9, 0x00e8, 0x9186, 0x0015,
+ 0x1510, 0x2011, 0x1824, 0x2204, 0x9086, 0x0014, 0x11e0, 0x6010,
+ 0x00b6, 0x2058, 0x080c, 0x6734, 0x00be, 0x080c, 0xbe4d, 0x1198,
+ 0x6010, 0x00b6, 0x2058, 0xb890, 0x00be, 0x9005, 0x0160, 0x2001,
+ 0x0006, 0x080c, 0x65e9, 0x6014, 0x2048, 0xa868, 0xd0fc, 0x0170,
+ 0x080c, 0xb348, 0x0048, 0x6014, 0x2048, 0xa868, 0xd0fc, 0x0528,
+ 0x080c, 0xb905, 0x080c, 0xaf43, 0x009e, 0x0005, 0x6014, 0x6310,
+ 0x2358, 0x904d, 0x090c, 0x0dd5, 0xa87b, 0x0000, 0xa883, 0x0000,
+ 0xa897, 0x4000, 0x900e, 0x080c, 0x68b9, 0x1108, 0xc185, 0xb800,
+ 0xd0bc, 0x0108, 0xc18d, 0xa99a, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x6d17, 0x012e, 0x080c, 0xaf43, 0x08f8, 0x6014, 0x904d, 0x090c,
+ 0x0dd5, 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4005, 0xa89b,
+ 0x0004, 0xa867, 0x0139, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17,
+ 0x012e, 0x080c, 0xaf43, 0x0840, 0xa878, 0x9086, 0x0005, 0x1108,
+ 0x0009, 0x0005, 0xa880, 0xc0ad, 0xa882, 0x0005, 0x6043, 0x0000,
+ 0x6017, 0x0000, 0x6003, 0x0001, 0x6007, 0x0050, 0x080c, 0x91b1,
+ 0x080c, 0x9763, 0x0005, 0x00c6, 0x6010, 0x00b6, 0x2058, 0xb800,
+ 0x00be, 0xd0bc, 0x0120, 0x6020, 0x9084, 0x000f, 0x0013, 0x00ce,
+ 0x0005, 0xc98f, 0xcfb2, 0xcfb2, 0xcfb5, 0xe789, 0xe7a4, 0xe7a7,
+ 0xc98f, 0xc98f, 0xc98f, 0xc98f, 0xc98f, 0xc98f, 0xc98f, 0xc98f,
+ 0x080c, 0x0dd5, 0xa001, 0xa001, 0x0005, 0x0096, 0x6014, 0x904d,
+ 0x0118, 0xa87c, 0xd0e4, 0x1110, 0x009e, 0x0010, 0x009e, 0x0005,
+ 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0550, 0x2001,
+ 0x1834, 0x2004, 0x9005, 0x1540, 0x00f6, 0x2c78, 0x080c, 0xaeed,
+ 0x0508, 0x7810, 0x6012, 0x080c, 0xd102, 0x7820, 0x9086, 0x0003,
+ 0x0128, 0x7808, 0x603a, 0x2f00, 0x603e, 0x0020, 0x7808, 0x603e,
+ 0x2f00, 0x603a, 0x602e, 0x6023, 0x0001, 0x6007, 0x0035, 0x6003,
+ 0x0001, 0x7954, 0x6156, 0x080c, 0x91b1, 0x080c, 0x9763, 0x2f60,
+ 0x00fe, 0x0005, 0x2f60, 0x00fe, 0x2001, 0x1987, 0x2004, 0x6042,
+ 0x0005, 0x0016, 0x0096, 0x6814, 0x2048, 0xa87c, 0xd0e4, 0x0180,
+ 0xc0e4, 0xa87e, 0xa877, 0x0000, 0xa893, 0x0000, 0xa88f, 0x0000,
+ 0xd0cc, 0x0130, 0xc0cc, 0xa87e, 0xa878, 0x2048, 0x080c, 0x0fb1,
+ 0x6830, 0x6036, 0x908e, 0x0001, 0x0148, 0x6803, 0x0002, 0x9086,
+ 0x0005, 0x0170, 0x9006, 0x602e, 0x6032, 0x00d0, 0x681c, 0xc085,
+ 0x681e, 0x6803, 0x0004, 0x6824, 0xc0f4, 0x9085, 0x0c00, 0x6826,
+ 0x6814, 0x2048, 0xa8ac, 0x6938, 0x9102, 0xa8b0, 0x693c, 0x9103,
+ 0x1e48, 0x683c, 0x602e, 0x6838, 0x9084, 0xfffc, 0x683a, 0x6032,
+ 0x2d00, 0x603a, 0x6808, 0x603e, 0x6910, 0x6112, 0x6954, 0x6156,
+ 0x6023, 0x0001, 0x6007, 0x0039, 0x6003, 0x0001, 0x080c, 0x91b1,
+ 0x080c, 0x9763, 0x009e, 0x001e, 0x0005, 0x6024, 0xd0d4, 0x0510,
+ 0xd0f4, 0x11f8, 0x6038, 0x940a, 0x603c, 0x9303, 0x0230, 0x9105,
+ 0x0120, 0x6024, 0xc0d4, 0xc0f5, 0x0098, 0x643a, 0x633e, 0xac3e,
+ 0xab42, 0x0046, 0x0036, 0x2400, 0xacac, 0x9402, 0xa836, 0x2300,
+ 0xabb0, 0x9303, 0xa83a, 0x003e, 0x004e, 0x6024, 0xc0d4, 0x0000,
+ 0x6026, 0x0005, 0xd0f4, 0x1138, 0xa83c, 0x603a, 0xa840, 0x603e,
+ 0x6024, 0xc0f5, 0x6026, 0x0005, 0x0006, 0x0016, 0x6004, 0x908e,
+ 0x0034, 0x01b8, 0x908e, 0x0035, 0x01a0, 0x908e, 0x0036, 0x0188,
+ 0x908e, 0x0037, 0x0170, 0x908e, 0x0038, 0x0158, 0x908e, 0x0039,
+ 0x0140, 0x908e, 0x003a, 0x0128, 0x908e, 0x003b, 0x0110, 0x9085,
+ 0x0001, 0x001e, 0x000e, 0x0005, 0x0006, 0x0016, 0x0026, 0x0036,
+ 0x00e6, 0x2001, 0x1981, 0x200c, 0x8000, 0x2014, 0x2001, 0x0032,
+ 0x080c, 0x9027, 0x2001, 0x1985, 0x82ff, 0x1110, 0x2011, 0x0014,
+ 0x2202, 0x2001, 0x1983, 0x200c, 0x8000, 0x2014, 0x2071, 0x196b,
+ 0x711a, 0x721e, 0x2001, 0x0064, 0x080c, 0x9027, 0x2001, 0x1986,
+ 0x82ff, 0x1110, 0x2011, 0x0014, 0x2202, 0x2001, 0x1987, 0x9288,
+ 0x000a, 0x2102, 0x2001, 0x1a93, 0x2102, 0x2001, 0x0032, 0x080c,
+ 0x15fd, 0x080c, 0x69ed, 0x00ee, 0x003e, 0x002e, 0x001e, 0x000e,
+ 0x0005, 0x0006, 0x0016, 0x00e6, 0x2001, 0x1985, 0x2003, 0x0028,
+ 0x2001, 0x1986, 0x2003, 0x0014, 0x2071, 0x196b, 0x701b, 0x0000,
+ 0x701f, 0x07d0, 0x2001, 0x1987, 0x2009, 0x001e, 0x2102, 0x2001,
+ 0x1a93, 0x2102, 0x2001, 0x0032, 0x080c, 0x15fd, 0x00ee, 0x001e,
+ 0x000e, 0x0005, 0x0096, 0x6058, 0x904d, 0x0110, 0x080c, 0x1031,
+ 0x009e, 0x0005, 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0xaeed, 0x0180, 0x2b08, 0x6112, 0x0ca9, 0x6023, 0x0001, 0x2900,
+ 0x6016, 0x2009, 0x0033, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e,
+ 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x0096, 0x00e6, 0x00f6, 0x2071,
+ 0x1800, 0x9186, 0x0015, 0x1500, 0x7090, 0x9086, 0x0018, 0x11e0,
+ 0x6014, 0x2048, 0xaa3c, 0xd2e4, 0x1160, 0x2c78, 0x080c, 0x99f9,
+ 0x01d8, 0x707c, 0xaa50, 0x9206, 0x1160, 0x7080, 0xaa54, 0x9206,
+ 0x1140, 0x6210, 0x00b6, 0x2258, 0xbaa0, 0x00be, 0x900e, 0x080c,
+ 0x3267, 0x080c, 0xb348, 0x0020, 0x080c, 0xb905, 0x080c, 0xaf43,
+ 0x00fe, 0x00ee, 0x009e, 0x0005, 0x7060, 0xaa54, 0x9206, 0x0d48,
+ 0x0c80, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xaeed, 0x0188,
+ 0x2b08, 0x6112, 0x080c, 0xd102, 0x6023, 0x0001, 0x2900, 0x6016,
+ 0x2009, 0x004d, 0x080c, 0xafbe, 0x9085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0x9006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x0016,
+ 0x080c, 0xaeed, 0x0180, 0x2b08, 0x6112, 0x080c, 0xd102, 0x6023,
+ 0x0001, 0x2900, 0x6016, 0x001e, 0x080c, 0xafbe, 0x9085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0x001e, 0x9006, 0x0cd0, 0x0016, 0x0026,
+ 0x0036, 0x0046, 0x0056, 0x0066, 0x0096, 0x00e6, 0x00f6, 0x2071,
+ 0x1800, 0x9186, 0x0015, 0x1568, 0x7190, 0x6014, 0x2048, 0xa814,
+ 0x8003, 0x9106, 0x1530, 0x20e1, 0x0000, 0x2001, 0x199f, 0x2003,
+ 0x0000, 0x6014, 0x2048, 0xa830, 0x20a8, 0x8906, 0x8006, 0x8007,
+ 0x9094, 0x003f, 0x22e8, 0x9084, 0xffc0, 0x9080, 0x001b, 0x20a0,
+ 0x2001, 0x199f, 0x0016, 0x200c, 0x080c, 0xd99b, 0x001e, 0xa804,
+ 0x9005, 0x0110, 0x2048, 0x0c38, 0x6014, 0x2048, 0xa867, 0x0103,
+ 0x0010, 0x080c, 0xb905, 0x080c, 0xaf43, 0x00fe, 0x00ee, 0x009e,
+ 0x006e, 0x005e, 0x004e, 0x003e, 0x002e, 0x001e, 0x0005, 0x0096,
+ 0x00e6, 0x00f6, 0x2071, 0x1800, 0x9186, 0x0015, 0x11b8, 0x7090,
+ 0x9086, 0x0004, 0x1198, 0x6014, 0x2048, 0x2c78, 0x080c, 0x99f9,
+ 0x01a8, 0x707c, 0xaa74, 0x9206, 0x1130, 0x7080, 0xaa78, 0x9206,
+ 0x1110, 0x080c, 0x321e, 0x080c, 0xb348, 0x0020, 0x080c, 0xb905,
+ 0x080c, 0xaf43, 0x00fe, 0x00ee, 0x009e, 0x0005, 0x7060, 0xaa78,
+ 0x9206, 0x0d78, 0x0c80, 0x0096, 0x00e6, 0x00f6, 0x2071, 0x1800,
+ 0x9186, 0x0015, 0x1550, 0x7090, 0x9086, 0x0004, 0x1530, 0x6014,
+ 0x2048, 0x2c78, 0x080c, 0x99f9, 0x05f0, 0x707c, 0xaacc, 0x9206,
+ 0x1180, 0x7080, 0xaad0, 0x9206, 0x1160, 0x080c, 0x321e, 0x0016,
+ 0xa998, 0xaab0, 0x9284, 0x1000, 0xc0fd, 0x080c, 0x570c, 0x001e,
+ 0x0010, 0x080c, 0x54f7, 0x080c, 0xcc86, 0x0508, 0xa87b, 0x0000,
+ 0xa883, 0x0000, 0xa897, 0x4000, 0x0080, 0x080c, 0xcc86, 0x01b8,
+ 0x6014, 0x2048, 0x080c, 0x54f7, 0x1d70, 0xa87b, 0x0030, 0xa883,
+ 0x0000, 0xa897, 0x4005, 0xa89b, 0x0004, 0x0126, 0x2091, 0x8000,
+ 0xa867, 0x0139, 0x080c, 0x6d17, 0x012e, 0x080c, 0xaf43, 0x00fe,
+ 0x00ee, 0x009e, 0x0005, 0x7060, 0xaad0, 0x9206, 0x0930, 0x0888,
+ 0x0016, 0x0026, 0xa87c, 0xd0ac, 0x0178, 0xa938, 0xaa34, 0x2100,
+ 0x9205, 0x0150, 0xa890, 0x9106, 0x1118, 0xa88c, 0x9206, 0x0120,
+ 0xa992, 0xaa8e, 0x9085, 0x0001, 0x002e, 0x001e, 0x0005, 0x00b6,
+ 0x00d6, 0x0036, 0x080c, 0xcc86, 0x0904, 0xd2bd, 0x0096, 0x6314,
+ 0x2348, 0xa87a, 0xa982, 0x929e, 0x4000, 0x1580, 0x6310, 0x00c6,
+ 0x2358, 0x2009, 0x0000, 0xa868, 0xd0f4, 0x1140, 0x080c, 0x68b9,
+ 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0xaa96, 0xa99a,
+ 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0031, 0x20a0,
+ 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x0006, 0x2098, 0x080c, 0x0f7c,
+ 0x20a9, 0x0004, 0xa85c, 0x9080, 0x0035, 0x20a0, 0xb8c8, 0x9080,
+ 0x000a, 0x2098, 0x080c, 0x0f7c, 0x00ce, 0x0090, 0xaa96, 0x3918,
+ 0x9398, 0x0007, 0x231c, 0x6004, 0x9086, 0x0016, 0x0110, 0xa89b,
+ 0x0004, 0xaba2, 0x6310, 0x2358, 0xb804, 0x9084, 0x00ff, 0xa89e,
+ 0x080c, 0x6d0b, 0x6017, 0x0000, 0x009e, 0x003e, 0x00de, 0x00be,
+ 0x0005, 0x0026, 0x0036, 0x0046, 0x00b6, 0x0096, 0x00f6, 0x6214,
+ 0x2248, 0x6210, 0x2258, 0x2079, 0x0260, 0x9096, 0x0000, 0x11a0,
+ 0xb814, 0x9084, 0x00ff, 0x900e, 0x080c, 0x287c, 0x2118, 0x831f,
+ 0x939c, 0xff00, 0x7838, 0x9084, 0x00ff, 0x931d, 0x7c3c, 0x2011,
+ 0x8018, 0x080c, 0x4b7f, 0x00a8, 0x9096, 0x0001, 0x1148, 0x89ff,
+ 0x0180, 0xa89b, 0x000d, 0x7838, 0xa8a6, 0x783c, 0xa8aa, 0x0048,
+ 0x9096, 0x0002, 0x1130, 0xa89b, 0x000d, 0x7838, 0xa8a6, 0x783c,
+ 0xa8aa, 0x00fe, 0x009e, 0x00be, 0x004e, 0x003e, 0x002e, 0x0005,
+ 0x00c6, 0x0026, 0x0016, 0x9186, 0x0035, 0x0110, 0x6a38, 0x0008,
+ 0x6a2c, 0x080c, 0xcc74, 0x01f0, 0x2260, 0x6120, 0x9186, 0x0003,
+ 0x0118, 0x9186, 0x0006, 0x1190, 0x6838, 0x9206, 0x0140, 0x683c,
+ 0x9206, 0x1160, 0x6108, 0x6838, 0x9106, 0x1140, 0x0020, 0x6008,
+ 0x693c, 0x9106, 0x1118, 0x6010, 0x6910, 0x9106, 0x001e, 0x002e,
+ 0x00ce, 0x0005, 0x9085, 0x0001, 0x0cc8, 0xa974, 0xd1cc, 0x0188,
+ 0x918c, 0x00ff, 0x918e, 0x0002, 0x1160, 0xa9a8, 0x918c, 0x0f00,
+ 0x810f, 0x918e, 0x0001, 0x1128, 0xa834, 0xa938, 0x9115, 0x190c,
+ 0xc33d, 0x0005, 0x0036, 0x2019, 0x0001, 0x0010, 0x0036, 0x901e,
+ 0x0499, 0x01e0, 0x080c, 0xcc86, 0x01c8, 0x080c, 0xce71, 0x6037,
+ 0x4000, 0x6014, 0x6017, 0x0000, 0x0096, 0x2048, 0xa87c, 0x080c,
+ 0xce8e, 0x1118, 0x080c, 0xb905, 0x0040, 0xa867, 0x0103, 0xa877,
+ 0x0000, 0x83ff, 0x1129, 0x080c, 0x6d17, 0x009e, 0x003e, 0x0005,
+ 0xa880, 0xd0b4, 0x0128, 0xa87b, 0x0006, 0xc0ec, 0xa882, 0x0048,
+ 0xd0bc, 0x0118, 0xa87b, 0x0002, 0x0020, 0xa87b, 0x0005, 0x080c,
+ 0xcf82, 0xa877, 0x0000, 0x0005, 0x2001, 0x1810, 0x2004, 0xd0ec,
+ 0x0005, 0x0006, 0x2001, 0x1810, 0x2004, 0xd0f4, 0x000e, 0x0005,
+ 0x0006, 0x2001, 0x1810, 0x2004, 0xd0e4, 0x000e, 0x0005, 0x0036,
+ 0x0046, 0x6010, 0x00b6, 0x2058, 0xbba0, 0x00be, 0x2021, 0x0007,
+ 0x080c, 0x4d36, 0x004e, 0x003e, 0x0005, 0x0c51, 0x1d81, 0x0005,
+ 0x2001, 0x1985, 0x2004, 0x601a, 0x0005, 0x2001, 0x1987, 0x2004,
+ 0x6042, 0x0005, 0x080c, 0xaf43, 0x0804, 0x9763, 0x00b6, 0x0066,
+ 0x6000, 0x90b2, 0x0016, 0x1a0c, 0x0dd5, 0x001b, 0x006e, 0x00be,
+ 0x0005, 0xd3c9, 0xdaf8, 0xdc55, 0xd3c9, 0xd3c9, 0xd3c9, 0xd3c9,
+ 0xd3c9, 0xd400, 0xdcd9, 0xd3c9, 0xd3c9, 0xd3c9, 0xd3c9, 0xd3c9,
+ 0xd3c9, 0x080c, 0x0dd5, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c,
+ 0x0dd5, 0x0013, 0x006e, 0x0005, 0xd3e4, 0xe24b, 0xd3e4, 0xd3e4,
+ 0xd3e4, 0xd3e4, 0xd3e4, 0xd3e4, 0xe1f8, 0xe29f, 0xd3e4, 0xe8c4,
+ 0xe8fa, 0xe8c4, 0xe8fa, 0xd3e4, 0x080c, 0x0dd5, 0x6000, 0x9082,
+ 0x0016, 0x1a0c, 0x0dd5, 0x6000, 0x000a, 0x0005, 0xd3fe, 0xdeb7,
+ 0xdfa9, 0xdfcc, 0xe08c, 0xd3fe, 0xe16b, 0xe114, 0xdce5, 0xe1ce,
+ 0xe1e3, 0xd3fe, 0xd3fe, 0xd3fe, 0xd3fe, 0xd3fe, 0x080c, 0x0dd5,
+ 0x91b2, 0x0053, 0x1a0c, 0x0dd5, 0x2100, 0x91b2, 0x0040, 0x1a04,
+ 0xd86c, 0x0002, 0xd44a, 0xd63a, 0xd44a, 0xd44a, 0xd44a, 0xd643,
+ 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a,
+ 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a,
+ 0xd44a, 0xd44c, 0xd4af, 0xd4be, 0xd522, 0xd54d, 0xd5c6, 0xd625,
+ 0xd44a, 0xd44a, 0xd646, 0xd44a, 0xd44a, 0xd65b, 0xd668, 0xd44a,
+ 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd70e, 0xd44a, 0xd44a, 0xd722,
+ 0xd44a, 0xd44a, 0xd6dd, 0xd44a, 0xd44a, 0xd44a, 0xd73a, 0xd44a,
+ 0xd44a, 0xd44a, 0xd7b7, 0xd44a, 0xd44a, 0xd44a, 0xd44a, 0xd44a,
+ 0xd44a, 0xd834, 0x080c, 0x0dd5, 0x080c, 0x69ca, 0x1150, 0x2001,
+ 0x1837, 0x2004, 0xd0cc, 0x1128, 0x9084, 0x0009, 0x9086, 0x0008,
+ 0x1140, 0x6007, 0x0009, 0x602f, 0x0009, 0x6017, 0x0000, 0x0804,
+ 0xd633, 0x080c, 0x6966, 0x00e6, 0x00c6, 0x0036, 0x0026, 0x0016,
+ 0x6210, 0x2258, 0xbaa0, 0x0026, 0x2019, 0x0029, 0x080c, 0x9356,
+ 0x0076, 0x903e, 0x080c, 0x9229, 0x2c08, 0x080c, 0xe477, 0x007e,
+ 0x001e, 0x001e, 0x002e, 0x003e, 0x00ce, 0x00ee, 0x6610, 0x2658,
+ 0x080c, 0x66a8, 0xbe04, 0x9684, 0x00ff, 0x9082, 0x0006, 0x1268,
+ 0x0016, 0x0026, 0x6210, 0x00b6, 0x2258, 0xbaa0, 0x00be, 0x2c08,
+ 0x080c, 0xeb23, 0x002e, 0x001e, 0x1178, 0x080c, 0xe3a9, 0x1904,
+ 0xd51a, 0x080c, 0xe345, 0x1120, 0x6007, 0x0008, 0x0804, 0xd633,
+ 0x6007, 0x0009, 0x0804, 0xd633, 0x080c, 0xe5cd, 0x0128, 0x080c,
+ 0xe3a9, 0x0d78, 0x0804, 0xd51a, 0x6017, 0x1900, 0x0c88, 0x080c,
+ 0x3342, 0x1904, 0xd869, 0x6106, 0x080c, 0xe2fa, 0x6007, 0x0006,
+ 0x0804, 0xd633, 0x6007, 0x0007, 0x0804, 0xd633, 0x080c, 0xe936,
+ 0x1904, 0xd869, 0x080c, 0x3342, 0x1904, 0xd869, 0x00d6, 0x6610,
+ 0x2658, 0xbe04, 0x9684, 0x00ff, 0x9082, 0x0006, 0x1220, 0x2001,
+ 0x0001, 0x080c, 0x65d5, 0x96b4, 0xff00, 0x8637, 0x9686, 0x0006,
+ 0x0188, 0x9686, 0x0004, 0x0170, 0xbe04, 0x96b4, 0x00ff, 0x9686,
+ 0x0006, 0x0140, 0x9686, 0x0004, 0x0128, 0x9686, 0x0005, 0x0110,
+ 0x00de, 0x0480, 0x00e6, 0x2071, 0x0260, 0x7034, 0x9084, 0x0003,
+ 0x1140, 0x7034, 0x9082, 0x0014, 0x0220, 0x7030, 0x9084, 0x0003,
+ 0x0130, 0x00ee, 0x6017, 0x0000, 0x602f, 0x0007, 0x00b0, 0x00ee,
+ 0x080c, 0xe40d, 0x1190, 0x9686, 0x0006, 0x1140, 0x0026, 0x6210,
+ 0x2258, 0xbaa0, 0x900e, 0x080c, 0x3267, 0x002e, 0x080c, 0x6734,
+ 0x6007, 0x000a, 0x00de, 0x0804, 0xd633, 0x6007, 0x000b, 0x00de,
+ 0x0804, 0xd633, 0x080c, 0x321e, 0x080c, 0xd39d, 0x6007, 0x0001,
+ 0x0804, 0xd633, 0x080c, 0xe936, 0x1904, 0xd869, 0x080c, 0x3342,
+ 0x1904, 0xd869, 0x2071, 0x0260, 0x7034, 0x90b4, 0x0003, 0x1948,
+ 0x90b2, 0x0014, 0x0a30, 0x7030, 0x9084, 0x0003, 0x1910, 0x6610,
+ 0x2658, 0xbe04, 0x9686, 0x0707, 0x09e8, 0x0026, 0x6210, 0x2258,
+ 0xbaa0, 0x900e, 0x080c, 0x3267, 0x002e, 0x6007, 0x000c, 0x2001,
+ 0x0001, 0x080c, 0xeb03, 0x0804, 0xd633, 0x080c, 0x69ca, 0x1140,
+ 0x2001, 0x1837, 0x2004, 0x9084, 0x0009, 0x9086, 0x0008, 0x1110,
+ 0x0804, 0xd459, 0x080c, 0x6966, 0x6610, 0x2658, 0xbe04, 0x9684,
+ 0x00ff, 0x9082, 0x0006, 0x06c8, 0x1138, 0x0026, 0x2001, 0x0006,
+ 0x080c, 0x6615, 0x002e, 0x0050, 0x96b4, 0xff00, 0x8637, 0x9686,
+ 0x0004, 0x0120, 0x9686, 0x0006, 0x1904, 0xd51a, 0x080c, 0xe41a,
+ 0x1120, 0x6007, 0x000e, 0x0804, 0xd633, 0x0046, 0x6410, 0x2458,
+ 0xbca0, 0x0046, 0x080c, 0x321e, 0x080c, 0xd39d, 0x004e, 0x0016,
+ 0x9006, 0x2009, 0x1848, 0x210c, 0xd1a4, 0x0148, 0x2009, 0x0029,
+ 0x080c, 0xe73a, 0x6010, 0x2058, 0xb800, 0xc0e5, 0xb802, 0x001e,
+ 0x004e, 0x6007, 0x0001, 0x0804, 0xd633, 0x2001, 0x0001, 0x080c,
+ 0x65d5, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019,
+ 0x1805, 0x2011, 0x0270, 0x080c, 0xbefd, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x9005, 0x0168, 0x96b4, 0xff00, 0x8637, 0x9682, 0x0004,
+ 0x0a04, 0xd51a, 0x9682, 0x0007, 0x0a04, 0xd576, 0x0804, 0xd51a,
+ 0x6017, 0x1900, 0x6007, 0x0009, 0x0804, 0xd633, 0x080c, 0x69ca,
+ 0x1140, 0x2001, 0x1837, 0x2004, 0x9084, 0x0009, 0x9086, 0x0008,
+ 0x1110, 0x0804, 0xd459, 0x080c, 0x6966, 0x6610, 0x2658, 0xbe04,
+ 0x9684, 0x00ff, 0x0006, 0x9086, 0x0001, 0x000e, 0x0170, 0x9082,
+ 0x0006, 0x0698, 0x0150, 0x96b4, 0xff00, 0x8637, 0x9686, 0x0004,
+ 0x0120, 0x9686, 0x0006, 0x1904, 0xd51a, 0x080c, 0xe448, 0x1130,
+ 0x080c, 0xe345, 0x1118, 0x6007, 0x0010, 0x04e8, 0x0046, 0x6410,
+ 0x2458, 0xbca0, 0x0046, 0x080c, 0x321e, 0x080c, 0xd39d, 0x004e,
+ 0x0016, 0x9006, 0x2009, 0x1848, 0x210c, 0xd1a4, 0x0148, 0x2009,
+ 0x0029, 0x080c, 0xe73a, 0x6010, 0x2058, 0xb800, 0xc0e5, 0xb802,
+ 0x001e, 0x004e, 0x6007, 0x0001, 0x00f0, 0x080c, 0xe5cd, 0x0140,
+ 0x96b4, 0xff00, 0x8637, 0x9686, 0x0006, 0x0978, 0x0804, 0xd51a,
+ 0x6017, 0x1900, 0x6007, 0x0009, 0x0070, 0x080c, 0x3342, 0x1904,
+ 0xd869, 0x080c, 0xe936, 0x1904, 0xd869, 0x080c, 0xda36, 0x1904,
+ 0xd51a, 0x6007, 0x0012, 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c,
+ 0x9763, 0x0005, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x91f9,
+ 0x080c, 0x9763, 0x0cb0, 0x6007, 0x0005, 0x0c68, 0x080c, 0xe936,
+ 0x1904, 0xd869, 0x080c, 0x3342, 0x1904, 0xd869, 0x080c, 0xda36,
+ 0x1904, 0xd51a, 0x6007, 0x0020, 0x6003, 0x0001, 0x080c, 0x91f9,
+ 0x080c, 0x9763, 0x0005, 0x080c, 0x3342, 0x1904, 0xd869, 0x6007,
+ 0x0023, 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c, 0x9763, 0x0005,
+ 0x080c, 0xe936, 0x1904, 0xd869, 0x080c, 0x3342, 0x1904, 0xd869,
+ 0x080c, 0xda36, 0x1904, 0xd51a, 0x0016, 0x0026, 0x00e6, 0x2071,
+ 0x0260, 0x2c08, 0x2011, 0x1820, 0x2214, 0x703c, 0x9206, 0x11e0,
+ 0x2011, 0x181f, 0x2214, 0x7038, 0x9084, 0x00ff, 0x9206, 0x11a0,
+ 0x7240, 0x080c, 0xcc74, 0x0570, 0x2260, 0x6008, 0x9086, 0xffff,
+ 0x0120, 0x7244, 0x6008, 0x9206, 0x1528, 0x6020, 0x9086, 0x0007,
+ 0x1508, 0x080c, 0xaf43, 0x04a0, 0x7244, 0x9286, 0xffff, 0x0180,
+ 0x2c08, 0x080c, 0xcc74, 0x01b0, 0x2260, 0x7240, 0x6008, 0x9206,
+ 0x1188, 0x6010, 0x9190, 0x0004, 0x2214, 0x9206, 0x01b8, 0x0050,
+ 0x7240, 0x2c08, 0x9006, 0x080c, 0xe704, 0x1180, 0x7244, 0x9286,
+ 0xffff, 0x01b0, 0x2160, 0x6007, 0x0026, 0x6017, 0x1700, 0x7214,
+ 0x9296, 0xffff, 0x1180, 0x6007, 0x0025, 0x0068, 0x6020, 0x9086,
+ 0x0007, 0x1d80, 0x6004, 0x9086, 0x0024, 0x1110, 0x080c, 0xaf43,
+ 0x2160, 0x6007, 0x0025, 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c,
+ 0x9763, 0x00ee, 0x002e, 0x001e, 0x0005, 0x2001, 0x0001, 0x080c,
+ 0x65d5, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019,
+ 0x1805, 0x2011, 0x0276, 0x080c, 0xbefd, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x0120, 0x6007, 0x0031, 0x0804, 0xd633, 0x080c, 0xbb71,
+ 0x080c, 0x743e, 0x1190, 0x0006, 0x0026, 0x0036, 0x080c, 0x7458,
+ 0x1138, 0x080c, 0x7724, 0x080c, 0x60ad, 0x080c, 0x736a, 0x0010,
+ 0x080c, 0x7416, 0x003e, 0x002e, 0x000e, 0x0005, 0x080c, 0x3342,
+ 0x1904, 0xd869, 0x080c, 0xda36, 0x1904, 0xd51a, 0x6106, 0x080c,
+ 0xda52, 0x1120, 0x6007, 0x002b, 0x0804, 0xd633, 0x6007, 0x002c,
+ 0x0804, 0xd633, 0x080c, 0xe936, 0x1904, 0xd869, 0x080c, 0x3342,
+ 0x1904, 0xd869, 0x080c, 0xda36, 0x1904, 0xd51a, 0x6106, 0x080c,
+ 0xda57, 0x1120, 0x6007, 0x002e, 0x0804, 0xd633, 0x6007, 0x002f,
+ 0x0804, 0xd633, 0x080c, 0x3342, 0x1904, 0xd869, 0x00e6, 0x00d6,
+ 0x00c6, 0x6010, 0x2058, 0xb904, 0x9184, 0x00ff, 0x9086, 0x0006,
+ 0x0158, 0x9184, 0xff00, 0x8007, 0x9086, 0x0006, 0x0128, 0x00ce,
+ 0x00de, 0x00ee, 0x0804, 0xd63a, 0x080c, 0x5761, 0xd0e4, 0x0904,
+ 0xd7b4, 0x2071, 0x026c, 0x7010, 0x603a, 0x7014, 0x603e, 0x7108,
+ 0x720c, 0x080c, 0x6a08, 0x0140, 0x6010, 0x2058, 0xb810, 0x9106,
+ 0x1118, 0xb814, 0x9206, 0x0510, 0x080c, 0x6a04, 0x15b8, 0x2069,
+ 0x1800, 0x6880, 0x9206, 0x1590, 0x687c, 0x9106, 0x1578, 0x7210,
+ 0x080c, 0xcc74, 0x0590, 0x080c, 0xd921, 0x0578, 0x080c, 0xe7b6,
+ 0x0560, 0x622e, 0x6007, 0x0036, 0x6003, 0x0001, 0x080c, 0x91b1,
+ 0x080c, 0x9763, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x7214, 0x9286,
+ 0xffff, 0x0150, 0x080c, 0xcc74, 0x01c0, 0x9280, 0x0002, 0x2004,
+ 0x7110, 0x9106, 0x1190, 0x08e0, 0x7210, 0x2c08, 0x9085, 0x0001,
+ 0x080c, 0xe704, 0x2c10, 0x2160, 0x0140, 0x0890, 0x6007, 0x0037,
+ 0x602f, 0x0009, 0x6017, 0x1500, 0x08b8, 0x6007, 0x0037, 0x602f,
+ 0x0003, 0x6017, 0x1700, 0x0880, 0x6007, 0x0012, 0x0868, 0x080c,
+ 0x3342, 0x1904, 0xd869, 0x6010, 0x2058, 0xb804, 0x9084, 0xff00,
+ 0x8007, 0x9086, 0x0006, 0x1904, 0xd63a, 0x00e6, 0x00d6, 0x00c6,
+ 0x080c, 0x5761, 0xd0e4, 0x0904, 0xd82c, 0x2069, 0x1800, 0x2071,
+ 0x026c, 0x7008, 0x603a, 0x720c, 0x623e, 0x9286, 0xffff, 0x1150,
+ 0x7208, 0x00c6, 0x2c08, 0x9085, 0x0001, 0x080c, 0xe704, 0x2c10,
+ 0x00ce, 0x05e8, 0x080c, 0xcc74, 0x05d0, 0x7108, 0x9280, 0x0002,
+ 0x2004, 0x9106, 0x15a0, 0x00c6, 0x0026, 0x2260, 0x080c, 0xc898,
+ 0x002e, 0x00ce, 0x7118, 0x918c, 0xff00, 0x810f, 0x9186, 0x0001,
+ 0x0178, 0x9186, 0x0005, 0x0118, 0x9186, 0x0007, 0x1198, 0x9280,
+ 0x0005, 0x2004, 0x9005, 0x0170, 0x080c, 0xd921, 0x0904, 0xd7ad,
+ 0x0056, 0x7510, 0x7614, 0x080c, 0xe7cf, 0x005e, 0x00ce, 0x00de,
+ 0x00ee, 0x0005, 0x6007, 0x003b, 0x602f, 0x0009, 0x6017, 0x2a00,
+ 0x6003, 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0c78, 0x6007,
+ 0x003b, 0x602f, 0x0003, 0x6017, 0x0300, 0x6003, 0x0001, 0x080c,
+ 0x91b1, 0x080c, 0x9763, 0x0c10, 0x6007, 0x003b, 0x602f, 0x000b,
+ 0x6017, 0x0000, 0x0804, 0xd784, 0x00e6, 0x0026, 0x080c, 0x69ca,
+ 0x0550, 0x080c, 0x6966, 0x080c, 0xe9a8, 0x1518, 0x2071, 0x1800,
+ 0x70dc, 0x9085, 0x0003, 0x70de, 0x00f6, 0x2079, 0x0100, 0x72b0,
+ 0x9284, 0x00ff, 0x707e, 0x78e6, 0x9284, 0xff00, 0x7280, 0x9205,
+ 0x7082, 0x78ea, 0x00fe, 0x70e7, 0x0000, 0x080c, 0x6a08, 0x0120,
+ 0x2011, 0x19ff, 0x2013, 0x07d0, 0xd0ac, 0x1128, 0x080c, 0x2ff5,
+ 0x0010, 0x080c, 0xe9da, 0x002e, 0x00ee, 0x080c, 0xaf43, 0x0804,
+ 0xd639, 0x080c, 0xaf43, 0x0005, 0x2600, 0x0002, 0xd880, 0xd8b1,
+ 0xd8c2, 0xd880, 0xd880, 0xd882, 0xd8d3, 0xd880, 0xd880, 0xd880,
+ 0xd89f, 0xd880, 0xd880, 0xd880, 0xd8de, 0xd8eb, 0xd91c, 0xd880,
+ 0x080c, 0x0dd5, 0x080c, 0xe936, 0x1d20, 0x080c, 0x3342, 0x1d08,
+ 0x080c, 0xda36, 0x1148, 0x7038, 0x6016, 0x6007, 0x0045, 0x6003,
+ 0x0001, 0x080c, 0x91f9, 0x0005, 0x080c, 0x321e, 0x080c, 0xd39d,
+ 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x91f9, 0x0005, 0x080c,
+ 0xe936, 0x1938, 0x080c, 0x3342, 0x1920, 0x080c, 0xda36, 0x1d60,
+ 0x703c, 0x6016, 0x6007, 0x004a, 0x6003, 0x0001, 0x080c, 0x91f9,
+ 0x0005, 0x080c, 0x3342, 0x1904, 0xd869, 0x2009, 0x0041, 0x080c,
+ 0xe9e3, 0x6007, 0x0047, 0x6003, 0x0001, 0x080c, 0x91f9, 0x080c,
+ 0x9763, 0x0005, 0x080c, 0x3342, 0x1904, 0xd869, 0x2009, 0x0042,
+ 0x080c, 0xe9e3, 0x6007, 0x0047, 0x6003, 0x0001, 0x080c, 0x91f9,
+ 0x080c, 0x9763, 0x0005, 0x080c, 0x3342, 0x1904, 0xd869, 0x2009,
+ 0x0046, 0x080c, 0xe9e3, 0x080c, 0xaf43, 0x0005, 0x080c, 0xd93e,
+ 0x0904, 0xd869, 0x6007, 0x004e, 0x6003, 0x0001, 0x080c, 0x91f9,
+ 0x080c, 0x9763, 0x0005, 0x6007, 0x004f, 0x6017, 0x0000, 0x7134,
+ 0x918c, 0x00ff, 0x81ff, 0x0508, 0x9186, 0x0001, 0x1160, 0x7140,
+ 0x2001, 0x19bc, 0x2004, 0x9106, 0x11b0, 0x7144, 0x2001, 0x19bd,
+ 0x2004, 0x9106, 0x0190, 0x9186, 0x0002, 0x1168, 0x2011, 0x0276,
+ 0x20a9, 0x0004, 0x6010, 0x0096, 0x2048, 0x2019, 0x000a, 0x080c,
+ 0xbf11, 0x009e, 0x0110, 0x6017, 0x0001, 0x6003, 0x0001, 0x080c,
+ 0x91f9, 0x080c, 0x9763, 0x0005, 0x6007, 0x0050, 0x703c, 0x6016,
+ 0x0ca0, 0x0016, 0x00e6, 0x2071, 0x0260, 0x00b6, 0x00c6, 0x2260,
+ 0x6010, 0x2058, 0xb8cc, 0xd084, 0x0150, 0x7128, 0x6044, 0x9106,
+ 0x1120, 0x712c, 0x6048, 0x9106, 0x0110, 0x9006, 0x0010, 0x9085,
+ 0x0001, 0x00ce, 0x00be, 0x00ee, 0x001e, 0x0005, 0x0016, 0x0096,
+ 0x0086, 0x00e6, 0x01c6, 0x01d6, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x1800, 0x7090, 0x908a, 0x00f9, 0x16e8, 0x20e1, 0x0000, 0x2001,
+ 0x199f, 0x2003, 0x0000, 0x080c, 0x1018, 0x05a0, 0x2900, 0x6016,
+ 0x7090, 0x8004, 0xa816, 0x908a, 0x001e, 0x02d0, 0xa833, 0x001e,
+ 0x20a9, 0x001e, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x001b, 0x20a0,
+ 0x2001, 0x199f, 0x0016, 0x200c, 0x0471, 0x001e, 0x2940, 0x080c,
+ 0x1018, 0x01c0, 0x2900, 0xa006, 0x2100, 0x81ff, 0x0180, 0x0c18,
+ 0xa832, 0x20a8, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x001b, 0x20a0,
+ 0x2001, 0x199f, 0x0016, 0x200c, 0x00b1, 0x001e, 0x0000, 0x9085,
+ 0x0001, 0x0048, 0x2071, 0x1800, 0x7093, 0x0000, 0x6014, 0x2048,
+ 0x080c, 0x0fb1, 0x9006, 0x012e, 0x01de, 0x01ce, 0x00ee, 0x008e,
+ 0x009e, 0x001e, 0x0005, 0x0006, 0x0016, 0x0026, 0x0036, 0x00c6,
+ 0x918c, 0xffff, 0x11a8, 0x080c, 0x23e9, 0x2099, 0x026c, 0x2001,
+ 0x0014, 0x3518, 0x9312, 0x1218, 0x23a8, 0x4003, 0x00f8, 0x20a8,
+ 0x4003, 0x22a8, 0x8108, 0x080c, 0x23e9, 0x2099, 0x0260, 0x0ca8,
+ 0x080c, 0x23e9, 0x2061, 0x199f, 0x6004, 0x2098, 0x6008, 0x3518,
+ 0x9312, 0x1218, 0x23a8, 0x4003, 0x0048, 0x20a8, 0x4003, 0x22a8,
+ 0x8108, 0x080c, 0x23e9, 0x2099, 0x0260, 0x0ca8, 0x2061, 0x199f,
+ 0x2019, 0x0280, 0x3300, 0x931e, 0x0110, 0x6006, 0x0020, 0x2001,
+ 0x0260, 0x6006, 0x8108, 0x2162, 0x9292, 0x0021, 0x9296, 0xffff,
+ 0x620a, 0x00ce, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005, 0x0006,
+ 0x0016, 0x0026, 0x0036, 0x00c6, 0x81ff, 0x11b8, 0x080c, 0x2401,
+ 0x20a1, 0x024c, 0x2001, 0x0014, 0x3518, 0x9312, 0x1218, 0x23a8,
+ 0x4003, 0x0418, 0x20a8, 0x4003, 0x82ff, 0x01f8, 0x22a8, 0x8108,
+ 0x080c, 0x2401, 0x20a1, 0x0240, 0x0c98, 0x080c, 0x2401, 0x2061,
+ 0x19a2, 0x6004, 0x20a0, 0x6008, 0x3518, 0x9312, 0x1218, 0x23a8,
+ 0x4003, 0x0058, 0x20a8, 0x4003, 0x82ff, 0x0138, 0x22a8, 0x8108,
+ 0x080c, 0x2401, 0x20a1, 0x0240, 0x0c98, 0x2061, 0x19a2, 0x2019,
+ 0x0260, 0x3400, 0x931e, 0x0110, 0x6006, 0x0020, 0x2001, 0x0240,
+ 0x6006, 0x8108, 0x2162, 0x9292, 0x0021, 0x9296, 0xffff, 0x620a,
+ 0x00ce, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005, 0x00b6, 0x0066,
+ 0x6610, 0x2658, 0xbe04, 0x96b4, 0xff00, 0x8637, 0x9686, 0x0006,
+ 0x0170, 0x9686, 0x0004, 0x0158, 0xbe04, 0x96b4, 0x00ff, 0x9686,
+ 0x0006, 0x0128, 0x9686, 0x0004, 0x0110, 0x9085, 0x0001, 0x006e,
+ 0x00be, 0x0005, 0x00d6, 0x080c, 0xdace, 0x00de, 0x0005, 0x00d6,
+ 0x080c, 0xdadb, 0x1520, 0x680c, 0x908c, 0xff00, 0x6820, 0x9084,
+ 0x00ff, 0x9115, 0x6216, 0x6824, 0x602e, 0xd1e4, 0x0130, 0x9006,
+ 0x080c, 0xeb03, 0x2009, 0x0001, 0x0078, 0xd1ec, 0x0180, 0x6920,
+ 0x918c, 0x00ff, 0x6824, 0x080c, 0x287c, 0x1148, 0x2001, 0x0001,
+ 0x080c, 0xeb03, 0x2110, 0x900e, 0x080c, 0x3267, 0x0018, 0x9085,
+ 0x0001, 0x0008, 0x9006, 0x00de, 0x0005, 0x00b6, 0x00c6, 0x080c,
+ 0xaf91, 0x05a8, 0x0016, 0x0026, 0x00c6, 0x2011, 0x0263, 0x2204,
+ 0x8211, 0x220c, 0x080c, 0x287c, 0x1578, 0x080c, 0x6638, 0x1560,
+ 0xbe12, 0xbd16, 0x00ce, 0x002e, 0x001e, 0x2b00, 0x6012, 0x080c,
+ 0xe936, 0x11d8, 0x080c, 0x3342, 0x11c0, 0x080c, 0xda36, 0x0510,
+ 0x2001, 0x0007, 0x080c, 0x65e9, 0x2001, 0x0007, 0x080c, 0x6615,
+ 0x6017, 0x0000, 0x6023, 0x0001, 0x6007, 0x0001, 0x6003, 0x0001,
+ 0x080c, 0x91f9, 0x080c, 0x9763, 0x0010, 0x080c, 0xaf43, 0x9085,
+ 0x0001, 0x00ce, 0x00be, 0x0005, 0x080c, 0xaf43, 0x00ce, 0x002e,
+ 0x001e, 0x0ca8, 0x080c, 0xaf43, 0x9006, 0x0c98, 0x2069, 0x026d,
+ 0x6800, 0x9082, 0x0010, 0x1228, 0x6017, 0x0000, 0x9085, 0x0001,
+ 0x0008, 0x9006, 0x0005, 0x6017, 0x0000, 0x2069, 0x026c, 0x6808,
+ 0x9084, 0xff00, 0x9086, 0x0800, 0x1190, 0x6904, 0x9186, 0x0018,
+ 0x0118, 0x9186, 0x0014, 0x1158, 0x810f, 0x6800, 0x9084, 0x00ff,
+ 0x910d, 0x615a, 0x908e, 0x0014, 0x0110, 0x908e, 0x0010, 0x0005,
+ 0x6004, 0x90b2, 0x0053, 0x1a0c, 0x0dd5, 0x91b6, 0x0013, 0x1130,
+ 0x2008, 0x91b2, 0x0040, 0x1a04, 0xdc25, 0x0092, 0x91b6, 0x0027,
+ 0x0120, 0x91b6, 0x0014, 0x190c, 0x0dd5, 0x2001, 0x0007, 0x080c,
+ 0x6615, 0x080c, 0x9657, 0x080c, 0xaf74, 0x080c, 0x9763, 0x0005,
+ 0xdb58, 0xdb5a, 0xdb58, 0xdb58, 0xdb58, 0xdb5a, 0xdb69, 0xdc1e,
+ 0xdbbb, 0xdc1e, 0xdbcf, 0xdc1e, 0xdb69, 0xdc1e, 0xdc16, 0xdc1e,
+ 0xdc16, 0xdc1e, 0xdc1e, 0xdb58, 0xdb58, 0xdb58, 0xdb58, 0xdb58,
+ 0xdb58, 0xdb58, 0xdb58, 0xdb58, 0xdb58, 0xdb58, 0xdb5a, 0xdb58,
+ 0xdc1e, 0xdb58, 0xdb58, 0xdc1e, 0xdb58, 0xdc1b, 0xdc1e, 0xdb58,
+ 0xdb58, 0xdb58, 0xdb58, 0xdc1e, 0xdc1e, 0xdb58, 0xdc1e, 0xdc1e,
+ 0xdb58, 0xdb64, 0xdb58, 0xdb58, 0xdb58, 0xdb58, 0xdc1a, 0xdc1e,
+ 0xdb58, 0xdb58, 0xdc1e, 0xdc1e, 0xdb58, 0xdb58, 0xdb58, 0xdb58,
+ 0x080c, 0x0dd5, 0x080c, 0x9657, 0x080c, 0xd3a0, 0x6003, 0x0002,
+ 0x080c, 0x9763, 0x0804, 0xdc24, 0x9006, 0x080c, 0x65d5, 0x0804,
+ 0xdc1e, 0x080c, 0x6a04, 0x1904, 0xdc1e, 0x9006, 0x080c, 0x65d5,
+ 0x6010, 0x2058, 0xb810, 0x9086, 0x00ff, 0x1140, 0x00f6, 0x2079,
+ 0x1800, 0x78a8, 0x8000, 0x78aa, 0x00fe, 0x0428, 0x6010, 0x2058,
+ 0xb8c0, 0x9005, 0x1178, 0x080c, 0xd388, 0x1904, 0xdc1e, 0x0036,
+ 0x0046, 0xbba0, 0x2021, 0x0007, 0x080c, 0x4d36, 0x004e, 0x003e,
+ 0x0804, 0xdc1e, 0x080c, 0x3373, 0x1904, 0xdc1e, 0x2001, 0x1800,
+ 0x2004, 0x9086, 0x0002, 0x1138, 0x00f6, 0x2079, 0x1800, 0x78a8,
+ 0x8000, 0x78aa, 0x00fe, 0x2001, 0x0002, 0x080c, 0x65e9, 0x080c,
+ 0x9657, 0x6023, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c,
+ 0x91f9, 0x080c, 0x9763, 0x6110, 0x2158, 0x2009, 0x0001, 0x080c,
+ 0x85be, 0x0804, 0xdc24, 0x6610, 0x2658, 0xbe04, 0x96b4, 0xff00,
+ 0x8637, 0x9686, 0x0006, 0x0904, 0xdc1e, 0x9686, 0x0004, 0x0904,
+ 0xdc1e, 0x080c, 0x8d70, 0x2001, 0x0004, 0x0804, 0xdc1c, 0x2001,
+ 0x1800, 0x2004, 0x9086, 0x0003, 0x1158, 0x0036, 0x0046, 0x6010,
+ 0x2058, 0xbba0, 0x2021, 0x0006, 0x080c, 0x4d36, 0x004e, 0x003e,
+ 0x2001, 0x0006, 0x080c, 0xdc42, 0x6610, 0x2658, 0xbe04, 0x0066,
+ 0x96b4, 0xff00, 0x8637, 0x9686, 0x0006, 0x006e, 0x0168, 0x2001,
+ 0x0006, 0x080c, 0x6615, 0x9284, 0x00ff, 0x908e, 0x0007, 0x1120,
+ 0x2001, 0x0006, 0x080c, 0x65e9, 0x080c, 0x6a04, 0x11f8, 0x2001,
+ 0x1837, 0x2004, 0xd0a4, 0x01d0, 0xbe04, 0x96b4, 0x00ff, 0x9686,
+ 0x0006, 0x01a0, 0x00f6, 0x2079, 0x1800, 0x78a8, 0x8000, 0x78aa,
+ 0x00fe, 0x0804, 0xdba3, 0x2001, 0x0004, 0x0030, 0x2001, 0x0006,
+ 0x0449, 0x0020, 0x0018, 0x0010, 0x080c, 0x6615, 0x080c, 0x9657,
+ 0x080c, 0xaf43, 0x080c, 0x9763, 0x0005, 0x2600, 0x0002, 0xdc39,
+ 0xdc39, 0xdc39, 0xdc39, 0xdc39, 0xdc3b, 0xdc39, 0xdc3b, 0xdc39,
+ 0xdc39, 0xdc3b, 0xdc39, 0xdc39, 0xdc39, 0xdc3b, 0xdc3b, 0xdc3b,
+ 0xdc3b, 0x080c, 0x0dd5, 0x080c, 0x9657, 0x080c, 0xaf43, 0x080c,
+ 0x9763, 0x0005, 0x0016, 0x00b6, 0x00d6, 0x6110, 0x2158, 0xb900,
+ 0xd184, 0x0138, 0x080c, 0x65e9, 0x9006, 0x080c, 0x65d5, 0x080c,
+ 0x3247, 0x00de, 0x00be, 0x001e, 0x0005, 0x6610, 0x2658, 0xb804,
+ 0x9084, 0xff00, 0x8007, 0x90b2, 0x000c, 0x1a0c, 0x0dd5, 0x91b6,
+ 0x0015, 0x1110, 0x003b, 0x0028, 0x91b6, 0x0016, 0x190c, 0x0dd5,
+ 0x006b, 0x0005, 0xb9ee, 0xb9ee, 0xb9ee, 0xb9ee, 0xdcd7, 0xb9ee,
+ 0xdcc1, 0xdc82, 0xb9ee, 0xb9ee, 0xb9ee, 0xb9ee, 0xb9ee, 0xb9ee,
+ 0xb9ee, 0xb9ee, 0xdcd7, 0xb9ee, 0xdcc1, 0xdcc8, 0xb9ee, 0xb9ee,
+ 0xb9ee, 0xb9ee, 0x00f6, 0x080c, 0x6a04, 0x11d8, 0x080c, 0xd388,
+ 0x11c0, 0x6010, 0x905d, 0x01a8, 0xb8c0, 0x9005, 0x0190, 0x9006,
+ 0x080c, 0x65d5, 0x2001, 0x0002, 0x080c, 0x65e9, 0x6023, 0x0001,
+ 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x91f9, 0x080c, 0x9763,
+ 0x00f0, 0x2011, 0x0263, 0x2204, 0x8211, 0x220c, 0x080c, 0x287c,
+ 0x11b0, 0x080c, 0x6699, 0x0118, 0x080c, 0xaf43, 0x0080, 0xb810,
+ 0x0006, 0xb814, 0x0006, 0xb8c0, 0x0006, 0x080c, 0x60c7, 0x000e,
+ 0xb8c2, 0x000e, 0xb816, 0x000e, 0xb812, 0x080c, 0xaf43, 0x00fe,
+ 0x0005, 0x6604, 0x96b6, 0x001e, 0x1110, 0x080c, 0xaf43, 0x0005,
+ 0x080c, 0xbd79, 0x1148, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c,
+ 0x91f9, 0x080c, 0x9763, 0x0010, 0x080c, 0xaf43, 0x0005, 0x0804,
+ 0xaf43, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0dd5, 0x080c, 0x9657,
+ 0x080c, 0xaf74, 0x080c, 0x9763, 0x0005, 0x9182, 0x0040, 0x0002,
+ 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfe, 0xdcfc, 0xdcfc, 0xdcfc,
+ 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc,
+ 0xdcfc, 0xdcfc, 0xdcfc, 0xdcfc, 0x080c, 0x0dd5, 0x0096, 0x00b6,
+ 0x00d6, 0x00e6, 0x00f6, 0x0046, 0x0026, 0x6210, 0x2258, 0xb8bc,
+ 0x9005, 0x11a8, 0x6106, 0x2071, 0x0260, 0x7444, 0x94a4, 0xff00,
+ 0x0904, 0xdd64, 0x080c, 0xeaf7, 0x1170, 0x9486, 0x2000, 0x1158,
+ 0x2009, 0x0001, 0x2011, 0x0200, 0x080c, 0x879a, 0x0020, 0x9026,
+ 0x080c, 0xe97b, 0x0c38, 0x080c, 0x0fff, 0x090c, 0x0dd5, 0x6003,
+ 0x0007, 0xa867, 0x010d, 0x9006, 0xa802, 0xa86a, 0xac8a, 0x2c00,
+ 0xa88e, 0x6008, 0xa8e2, 0x6010, 0x2058, 0xb8a0, 0x7130, 0xa97a,
+ 0x0016, 0xa876, 0xa87f, 0x0000, 0xa883, 0x0000, 0xa887, 0x0036,
+ 0x080c, 0x6d17, 0x001e, 0x080c, 0xeaf7, 0x1904, 0xddc4, 0x9486,
+ 0x2000, 0x1130, 0x2019, 0x0017, 0x080c, 0xe6ae, 0x0804, 0xddc4,
+ 0x9486, 0x0200, 0x1120, 0x080c, 0xe64a, 0x0804, 0xddc4, 0x9486,
+ 0x0400, 0x0120, 0x9486, 0x1000, 0x1904, 0xddc4, 0x2019, 0x0002,
+ 0x080c, 0xe665, 0x0804, 0xddc4, 0x2069, 0x1a70, 0x6a00, 0xd284,
+ 0x0904, 0xde2e, 0x9284, 0x0300, 0x1904, 0xde27, 0x6804, 0x9005,
+ 0x0904, 0xde0f, 0x2d78, 0x6003, 0x0007, 0x080c, 0x1018, 0x0904,
+ 0xddd0, 0x7800, 0xd08c, 0x1118, 0x7804, 0x8001, 0x7806, 0x6017,
+ 0x0000, 0x2001, 0x180f, 0x2004, 0xd084, 0x1904, 0xde32, 0x9006,
+ 0xa802, 0xa867, 0x0116, 0xa86a, 0x6008, 0xa8e2, 0x2c00, 0xa87a,
+ 0x6010, 0x2058, 0xb8a0, 0x7130, 0xa9b6, 0xa876, 0xb928, 0xa9ba,
+ 0xb92c, 0xa9be, 0xb930, 0xa9c2, 0xb934, 0xa9c6, 0xa883, 0x003d,
+ 0x7044, 0x9084, 0x0003, 0x9080, 0xddcc, 0x2005, 0xa87e, 0x20a9,
+ 0x000a, 0x2001, 0x0270, 0xaa5c, 0x9290, 0x0021, 0x2009, 0x0205,
+ 0x200b, 0x0080, 0x20e1, 0x0000, 0xab60, 0x23e8, 0x2098, 0x22a0,
+ 0x4003, 0x200b, 0x0000, 0x2001, 0x027a, 0x200c, 0xa9b2, 0x8000,
+ 0x200c, 0xa9ae, 0x080c, 0x6d17, 0x002e, 0x004e, 0x00fe, 0x00ee,
+ 0x00de, 0x00be, 0x009e, 0x0005, 0x0000, 0x0080, 0x0040, 0x0000,
+ 0x2001, 0x1810, 0x2004, 0xd084, 0x0120, 0x080c, 0x0fff, 0x1904,
+ 0xdd79, 0x6017, 0xf100, 0x6003, 0x0001, 0x6007, 0x0041, 0x080c,
+ 0x91b1, 0x080c, 0x9763, 0x0c00, 0x2069, 0x0260, 0x6848, 0x9084,
+ 0xff00, 0x9086, 0x1200, 0x1198, 0x686c, 0x9084, 0x00ff, 0x0016,
+ 0x6114, 0x918c, 0xf700, 0x910d, 0x6116, 0x001e, 0x6003, 0x0001,
+ 0x6007, 0x0043, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0828, 0x6868,
+ 0x602e, 0x686c, 0x6032, 0x6017, 0xf200, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0804, 0xddc4, 0x2001,
+ 0x180e, 0x2004, 0xd0ec, 0x0120, 0x2011, 0x8049, 0x080c, 0x4b7f,
+ 0x6017, 0xf300, 0x0010, 0x6017, 0xf100, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0804, 0xddc4, 0x6017,
+ 0xf500, 0x0c98, 0x6017, 0xf600, 0x0804, 0xdde4, 0x6017, 0xf200,
+ 0x0804, 0xdde4, 0xa867, 0x0146, 0xa86b, 0x0000, 0x6008, 0xa886,
+ 0x2c00, 0xa87a, 0x7044, 0x9084, 0x0003, 0x9080, 0xddcc, 0x2005,
+ 0xa87e, 0x2928, 0x6010, 0x2058, 0xb8a0, 0xa876, 0xb828, 0xa88a,
+ 0xb82c, 0xa88e, 0xb830, 0xa892, 0xb834, 0xa896, 0xa883, 0x003d,
+ 0x2009, 0x0205, 0x2104, 0x9085, 0x0080, 0x200a, 0x20e1, 0x0000,
+ 0x2011, 0x0210, 0x2214, 0x9294, 0x0fff, 0xaaa2, 0x9282, 0x0111,
+ 0x1a0c, 0x0dd5, 0x8210, 0x821c, 0x2001, 0x026c, 0x2098, 0xa860,
+ 0x20e8, 0xa85c, 0x9080, 0x0029, 0x20a0, 0x2011, 0xdeae, 0x2041,
+ 0x0001, 0x223d, 0x9784, 0x00ff, 0x9322, 0x1208, 0x2300, 0x20a8,
+ 0x4003, 0x931a, 0x0530, 0x8210, 0xd7fc, 0x1130, 0x8d68, 0x2d0a,
+ 0x2001, 0x0260, 0x2098, 0x0c68, 0x2950, 0x080c, 0x1018, 0x0170,
+ 0x2900, 0xb002, 0xa867, 0x0147, 0xa86b, 0x0000, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x001b, 0x20a0, 0x8840, 0x08d8, 0x2548, 0xa800,
+ 0x902d, 0x0118, 0x080c, 0x1031, 0x0cc8, 0x080c, 0x1031, 0x0804,
+ 0xddd0, 0x2548, 0x8847, 0x9885, 0x0046, 0xa866, 0x2009, 0x0205,
+ 0x200b, 0x0000, 0x080c, 0xe6dd, 0x0804, 0xddc4, 0x8010, 0x0004,
+ 0x801a, 0x0006, 0x8018, 0x0008, 0x8016, 0x000a, 0x8014, 0x9186,
+ 0x0013, 0x1160, 0x6004, 0x908a, 0x0054, 0x1a0c, 0x0dd5, 0x9082,
+ 0x0040, 0x0a0c, 0x0dd5, 0x2008, 0x0804, 0xdf60, 0x9186, 0x0051,
+ 0x0108, 0x00c0, 0x2001, 0x0109, 0x2004, 0xd084, 0x0904, 0xdf10,
+ 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x080c, 0x9094,
+ 0x002e, 0x001e, 0x000e, 0x012e, 0x6000, 0x9086, 0x0002, 0x1580,
+ 0x0804, 0xdfa9, 0x9186, 0x0027, 0x0530, 0x9186, 0x0048, 0x0128,
+ 0x9186, 0x0014, 0x0500, 0x190c, 0x0dd5, 0x2001, 0x0109, 0x2004,
+ 0xd084, 0x01f0, 0x00c6, 0x0126, 0x2091, 0x2800, 0x00c6, 0x2061,
+ 0x0100, 0x0006, 0x0016, 0x0026, 0x080c, 0x9094, 0x002e, 0x001e,
+ 0x000e, 0x00ce, 0x012e, 0x00ce, 0x6000, 0x9086, 0x0004, 0x190c,
+ 0x0dd5, 0x0804, 0xe08c, 0x6004, 0x9082, 0x0040, 0x2008, 0x001a,
+ 0x080c, 0xafd9, 0x0005, 0xdf27, 0xdf29, 0xdf29, 0xdf50, 0xdf27,
+ 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27,
+ 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0xdf27, 0x080c,
+ 0x0dd5, 0x080c, 0x9657, 0x080c, 0x9763, 0x0036, 0x0096, 0x6014,
+ 0x904d, 0x01d8, 0x080c, 0xcc86, 0x01c0, 0x6003, 0x0002, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1178, 0x2019, 0x0004,
+ 0x080c, 0xe6dd, 0x6017, 0x0000, 0x6018, 0x9005, 0x1120, 0x2001,
+ 0x1986, 0x2004, 0x601a, 0x6003, 0x0007, 0x009e, 0x003e, 0x0005,
+ 0x0096, 0x080c, 0x9657, 0x080c, 0x9763, 0x080c, 0xcc86, 0x0120,
+ 0x6014, 0x2048, 0x080c, 0x1031, 0x080c, 0xaf74, 0x009e, 0x0005,
+ 0x0002, 0xdf75, 0xdf8c, 0xdf77, 0xdfa3, 0xdf75, 0xdf75, 0xdf75,
+ 0xdf75, 0xdf75, 0xdf75, 0xdf75, 0xdf75, 0xdf75, 0xdf75, 0xdf75,
+ 0xdf75, 0xdf75, 0xdf75, 0xdf75, 0xdf75, 0x080c, 0x0dd5, 0x0096,
+ 0x080c, 0x9657, 0x6014, 0x2048, 0xa87c, 0xd0b4, 0x0138, 0x6003,
+ 0x0007, 0x2009, 0x0043, 0x080c, 0xafbe, 0x0010, 0x6003, 0x0004,
+ 0x080c, 0x9763, 0x009e, 0x0005, 0x080c, 0x9657, 0x080c, 0xcc86,
+ 0x0138, 0x6114, 0x0096, 0x2148, 0xa97c, 0x009e, 0xd1ec, 0x1138,
+ 0x080c, 0x876f, 0x080c, 0xaf43, 0x080c, 0x9763, 0x0005, 0x080c,
+ 0xe93f, 0x0db0, 0x0cc8, 0x080c, 0x9657, 0x2009, 0x0041, 0x0804,
+ 0xe114, 0x9182, 0x0040, 0x0002, 0xdfc0, 0xdfc2, 0xdfc0, 0xdfc0,
+ 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0,
+ 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc0, 0xdfc3, 0xdfc0, 0xdfc0,
+ 0x080c, 0x0dd5, 0x0005, 0x00d6, 0x080c, 0x876f, 0x00de, 0x080c,
+ 0xe997, 0x080c, 0xaf43, 0x0005, 0x9182, 0x0040, 0x0002, 0xdfe3,
+ 0xdfe3, 0xdfe3, 0xdfe3, 0xdfe3, 0xdfe3, 0xdfe3, 0xdfe3, 0xdfe3,
+ 0xdfe5, 0xe054, 0xdfe3, 0xdfe3, 0xdfe3, 0xdfe3, 0xe054, 0xdfe3,
+ 0xdfe3, 0xdfe3, 0xdfe3, 0x080c, 0x0dd5, 0x2001, 0x0105, 0x2004,
+ 0x9084, 0x1800, 0x01c8, 0x2001, 0x0132, 0x200c, 0x2001, 0x0131,
+ 0x2004, 0x9105, 0x1904, 0xe054, 0x2009, 0x180c, 0x2104, 0xd0d4,
+ 0x0904, 0xe054, 0xc0d4, 0x200a, 0x2009, 0x0105, 0x2104, 0x9084,
+ 0xe7fd, 0x9085, 0x0010, 0x200a, 0x2001, 0x1867, 0x2004, 0xd0e4,
+ 0x1528, 0x603b, 0x0000, 0x080c, 0x9713, 0x6014, 0x0096, 0x2048,
+ 0xa87c, 0xd0fc, 0x0188, 0x908c, 0x0003, 0x918e, 0x0002, 0x0508,
+ 0x2001, 0x180c, 0x2004, 0xd0d4, 0x11e0, 0x080c, 0x9891, 0x2009,
+ 0x0041, 0x009e, 0x0804, 0xe114, 0x080c, 0x9891, 0x6003, 0x0007,
+ 0x601b, 0x0000, 0x080c, 0x876f, 0x009e, 0x0005, 0x2001, 0x0100,
+ 0x2004, 0x9082, 0x0005, 0x0aa8, 0x2001, 0x011f, 0x2004, 0x603a,
+ 0x0890, 0x2001, 0x180c, 0x200c, 0xc1d4, 0x2102, 0xd1cc, 0x0110,
+ 0x080c, 0x2c90, 0x080c, 0x9891, 0x6014, 0x2048, 0xa97c, 0xd1ec,
+ 0x1130, 0x080c, 0x876f, 0x080c, 0xaf43, 0x009e, 0x0005, 0x080c,
+ 0xe93f, 0x0db8, 0x009e, 0x0005, 0x2001, 0x180c, 0x200c, 0xc1d4,
+ 0x2102, 0x0036, 0x080c, 0x9713, 0x080c, 0x9891, 0x6014, 0x0096,
+ 0x2048, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0188,
+ 0xa87c, 0x9084, 0x0003, 0x9086, 0x0002, 0x0140, 0xa8ac, 0x6330,
+ 0x931a, 0x6332, 0xa8b0, 0x632c, 0x931b, 0x632e, 0x6003, 0x0002,
+ 0x0080, 0x2019, 0x0004, 0x080c, 0xe6dd, 0x6018, 0x9005, 0x1128,
+ 0x2001, 0x1986, 0x2004, 0x8003, 0x601a, 0x6017, 0x0000, 0x6003,
+ 0x0007, 0x009e, 0x003e, 0x0005, 0x9182, 0x0040, 0x0002, 0xe0a3,
+ 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a5,
+ 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3, 0xe0a3,
+ 0xe0a3, 0xe0a3, 0xe0f0, 0x080c, 0x0dd5, 0x6014, 0x0096, 0x2048,
+ 0xa834, 0xaa38, 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1bc,
+ 0x1190, 0x920d, 0x1518, 0xa87c, 0xd0fc, 0x0128, 0x2009, 0x0041,
+ 0x009e, 0x0804, 0xe114, 0x6003, 0x0007, 0x601b, 0x0000, 0x080c,
+ 0x876f, 0x009e, 0x0005, 0x6124, 0xd1f4, 0x1d58, 0x0006, 0x0046,
+ 0xacac, 0x9422, 0xa9b0, 0x2200, 0x910b, 0x6030, 0x9420, 0x6432,
+ 0x602c, 0x9109, 0x612e, 0x004e, 0x000e, 0x08d8, 0x6110, 0x00b6,
+ 0x2158, 0xb900, 0x00be, 0xd1bc, 0x1178, 0x2009, 0x180e, 0x210c,
+ 0xd19c, 0x0118, 0x6003, 0x0007, 0x0010, 0x6003, 0x0006, 0x00e9,
+ 0x080c, 0x8771, 0x009e, 0x0005, 0x6003, 0x0002, 0x009e, 0x0005,
+ 0x6024, 0xd0f4, 0x0128, 0x080c, 0x15f4, 0x1904, 0xe0a5, 0x0005,
+ 0x6014, 0x0096, 0x2048, 0xa834, 0xa938, 0x009e, 0x9105, 0x1120,
+ 0x080c, 0x15f4, 0x1904, 0xe0a5, 0x0005, 0xd2fc, 0x0140, 0x8002,
+ 0x8000, 0x8212, 0x9291, 0x0000, 0x2009, 0x0009, 0x0010, 0x2009,
+ 0x0015, 0xaa9a, 0xa896, 0x0005, 0x9182, 0x0040, 0x0208, 0x0062,
+ 0x9186, 0x0013, 0x0120, 0x9186, 0x0014, 0x190c, 0x0dd5, 0x6024,
+ 0xd0dc, 0x090c, 0x0dd5, 0x0005, 0xe138, 0xe144, 0xe150, 0xe15c,
+ 0xe138, 0xe138, 0xe138, 0xe138, 0xe13f, 0xe13a, 0xe13a, 0xe138,
+ 0xe138, 0xe138, 0xe138, 0xe13a, 0xe138, 0xe13a, 0xe138, 0xe13f,
+ 0x080c, 0x0dd5, 0x6024, 0xd0dc, 0x090c, 0x0dd5, 0x0005, 0x6014,
+ 0x9005, 0x190c, 0x0dd5, 0x0005, 0x6003, 0x0001, 0x6106, 0x080c,
+ 0x91b1, 0x0126, 0x2091, 0x8000, 0x080c, 0x9763, 0x012e, 0x0005,
+ 0x6003, 0x0001, 0x6106, 0x080c, 0x91b1, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x9763, 0x012e, 0x0005, 0x6003, 0x0003, 0x6106, 0x2c10,
+ 0x080c, 0x1beb, 0x0126, 0x2091, 0x8000, 0x080c, 0x9216, 0x080c,
+ 0x9891, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x0036, 0x0096,
+ 0x9182, 0x0040, 0x0023, 0x009e, 0x003e, 0x012e, 0x0005, 0xe18b,
+ 0xe18d, 0xe19f, 0xe1b9, 0xe18b, 0xe18b, 0xe18b, 0xe18b, 0xe18b,
+ 0xe18b, 0xe18b, 0xe18b, 0xe18b, 0xe18b, 0xe18b, 0xe18b, 0xe18b,
+ 0xe18b, 0xe18b, 0xe18b, 0x080c, 0x0dd5, 0x6014, 0x2048, 0xa87c,
+ 0xd0fc, 0x01f8, 0x909c, 0x0003, 0x939e, 0x0003, 0x01d0, 0x6003,
+ 0x0001, 0x6106, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0470, 0x6014,
+ 0x2048, 0xa87c, 0xd0fc, 0x0168, 0x909c, 0x0003, 0x939e, 0x0003,
+ 0x0140, 0x6003, 0x0001, 0x6106, 0x080c, 0x91b1, 0x080c, 0x9763,
+ 0x00e0, 0x901e, 0x6316, 0x631a, 0x2019, 0x0004, 0x080c, 0xe6dd,
+ 0x00a0, 0x6014, 0x2048, 0xa87c, 0xd0fc, 0x0d98, 0x909c, 0x0003,
+ 0x939e, 0x0003, 0x0d70, 0x6003, 0x0003, 0x6106, 0x2c10, 0x080c,
+ 0x1beb, 0x080c, 0x9216, 0x080c, 0x9891, 0x0005, 0x080c, 0x9657,
+ 0x6114, 0x81ff, 0x0158, 0x0096, 0x2148, 0x080c, 0xea94, 0x0036,
+ 0x2019, 0x0029, 0x080c, 0xe6dd, 0x003e, 0x009e, 0x080c, 0xaf74,
+ 0x080c, 0x9763, 0x0005, 0x080c, 0x9713, 0x6114, 0x81ff, 0x0158,
+ 0x0096, 0x2148, 0x080c, 0xea94, 0x0036, 0x2019, 0x0029, 0x080c,
+ 0xe6dd, 0x003e, 0x009e, 0x080c, 0xaf74, 0x080c, 0x9891, 0x0005,
+ 0x9182, 0x0085, 0x0002, 0xe20a, 0xe208, 0xe208, 0xe216, 0xe208,
+ 0xe208, 0xe208, 0xe208, 0xe208, 0xe208, 0xe208, 0xe208, 0xe208,
+ 0x080c, 0x0dd5, 0x6003, 0x000b, 0x6106, 0x080c, 0x91b1, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x9763, 0x012e, 0x0005, 0x0026, 0x00e6,
+ 0x080c, 0xe936, 0x0118, 0x080c, 0xaf43, 0x0450, 0x2071, 0x0260,
+ 0x7224, 0x6216, 0x2001, 0x180e, 0x2004, 0xd0e4, 0x0150, 0x6010,
+ 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00, 0x2011, 0x014e, 0x080c,
+ 0xb264, 0x7220, 0x080c, 0xe583, 0x0118, 0x6007, 0x0086, 0x0040,
+ 0x6007, 0x0087, 0x7224, 0x9296, 0xffff, 0x1110, 0x6007, 0x0086,
+ 0x6003, 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763, 0x080c, 0x9891,
+ 0x00ee, 0x002e, 0x0005, 0x9186, 0x0013, 0x1160, 0x6004, 0x908a,
+ 0x0085, 0x0a0c, 0x0dd5, 0x908a, 0x0092, 0x1a0c, 0x0dd5, 0x9082,
+ 0x0085, 0x00a2, 0x9186, 0x0027, 0x0130, 0x9186, 0x0014, 0x0118,
+ 0x080c, 0xafd9, 0x0050, 0x2001, 0x0007, 0x080c, 0x6615, 0x080c,
+ 0x9657, 0x080c, 0xaf74, 0x080c, 0x9763, 0x0005, 0xe27b, 0xe27d,
+ 0xe27d, 0xe27b, 0xe27b, 0xe27b, 0xe27b, 0xe27b, 0xe27b, 0xe27b,
+ 0xe27b, 0xe27b, 0xe27b, 0x080c, 0x0dd5, 0x080c, 0x9657, 0x080c,
+ 0xaf74, 0x080c, 0x9763, 0x0005, 0x9182, 0x0085, 0x0a0c, 0x0dd5,
+ 0x9182, 0x0092, 0x1a0c, 0x0dd5, 0x9182, 0x0085, 0x0002, 0xe29c,
+ 0xe29c, 0xe29c, 0xe29e, 0xe29c, 0xe29c, 0xe29c, 0xe29c, 0xe29c,
+ 0xe29c, 0xe29c, 0xe29c, 0xe29c, 0x080c, 0x0dd5, 0x0005, 0x9186,
+ 0x0013, 0x0148, 0x9186, 0x0014, 0x0130, 0x9186, 0x0027, 0x0118,
+ 0x080c, 0xafd9, 0x0030, 0x080c, 0x9657, 0x080c, 0xaf74, 0x080c,
+ 0x9763, 0x0005, 0x0036, 0x080c, 0xe997, 0x6043, 0x0000, 0x2019,
+ 0x000b, 0x0031, 0x6023, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005,
+ 0x0126, 0x0036, 0x2091, 0x8000, 0x0086, 0x2c40, 0x0096, 0x904e,
+ 0x080c, 0xa76b, 0x009e, 0x008e, 0x1550, 0x0076, 0x2c38, 0x080c,
+ 0xa816, 0x007e, 0x1520, 0x6000, 0x9086, 0x0000, 0x0500, 0x6020,
+ 0x9086, 0x0007, 0x01e0, 0x0096, 0x601c, 0xd084, 0x0140, 0x080c,
+ 0xe997, 0x080c, 0xd3a0, 0x080c, 0x1aa1, 0x6023, 0x0007, 0x6014,
+ 0x2048, 0x080c, 0xcc86, 0x0110, 0x080c, 0xe6dd, 0x009e, 0x6017,
+ 0x0000, 0x080c, 0xe997, 0x6023, 0x0007, 0x080c, 0xd3a0, 0x003e,
+ 0x012e, 0x0005, 0x00f6, 0x00c6, 0x00b6, 0x0036, 0x0156, 0x2079,
+ 0x0260, 0x7938, 0x783c, 0x080c, 0x287c, 0x15c8, 0x0016, 0x00c6,
+ 0x080c, 0x6699, 0x1590, 0x001e, 0x00c6, 0x2160, 0x080c, 0xd39d,
+ 0x00ce, 0x002e, 0x0026, 0x0016, 0x2019, 0x0029, 0x080c, 0xa8dc,
+ 0x080c, 0x9356, 0x0076, 0x903e, 0x080c, 0x9229, 0x007e, 0x001e,
+ 0x0076, 0x903e, 0x080c, 0xe477, 0x007e, 0x0026, 0xba04, 0x9294,
+ 0xff00, 0x8217, 0x9286, 0x0006, 0x0118, 0x9286, 0x0004, 0x1118,
+ 0xbaa0, 0x080c, 0x32dc, 0x002e, 0xbcc0, 0x001e, 0x080c, 0x60c7,
+ 0xbe12, 0xbd16, 0xbcc2, 0x9006, 0x0010, 0x00ce, 0x001e, 0x015e,
+ 0x003e, 0x00be, 0x00ce, 0x00fe, 0x0005, 0x00c6, 0x00d6, 0x00b6,
+ 0x0016, 0x2009, 0x1824, 0x2104, 0x9086, 0x0074, 0x1904, 0xe39e,
+ 0x2069, 0x0260, 0x6944, 0x9182, 0x0100, 0x06e0, 0x6940, 0x9184,
+ 0x8000, 0x0904, 0xe39b, 0x2001, 0x197b, 0x2004, 0x9005, 0x1140,
+ 0x6010, 0x2058, 0xb8c0, 0x9005, 0x0118, 0x9184, 0x0800, 0x0598,
+ 0x6948, 0x918a, 0x0001, 0x0648, 0x080c, 0xeafc, 0x0118, 0x6978,
+ 0xd1fc, 0x11b8, 0x2009, 0x0205, 0x200b, 0x0001, 0x693c, 0x81ff,
+ 0x1198, 0x6944, 0x9182, 0x0100, 0x02a8, 0x6940, 0x81ff, 0x1178,
+ 0x6948, 0x918a, 0x0001, 0x0288, 0x6950, 0x918a, 0x0001, 0x0298,
+ 0x00d0, 0x6017, 0x0100, 0x00a0, 0x6017, 0x0300, 0x0088, 0x6017,
+ 0x0500, 0x0070, 0x6017, 0x0700, 0x0058, 0x6017, 0x0900, 0x0040,
+ 0x6017, 0x0b00, 0x0028, 0x6017, 0x0f00, 0x0010, 0x6017, 0x2d00,
+ 0x9085, 0x0001, 0x0008, 0x9006, 0x001e, 0x00be, 0x00de, 0x00ce,
+ 0x0005, 0x00c6, 0x00b6, 0x0026, 0x0036, 0x0156, 0x6210, 0x2258,
+ 0xbb04, 0x9394, 0x00ff, 0x9286, 0x0006, 0x0180, 0x9286, 0x0004,
+ 0x0168, 0x9394, 0xff00, 0x8217, 0x9286, 0x0006, 0x0138, 0x9286,
+ 0x0004, 0x0120, 0x080c, 0x66a8, 0x0804, 0xe406, 0x2011, 0x0276,
+ 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019, 0x000a, 0x080c, 0xbf11,
+ 0x009e, 0x15a8, 0x2011, 0x027a, 0x20a9, 0x0004, 0x0096, 0x2b48,
+ 0x2019, 0x0006, 0x080c, 0xbf11, 0x009e, 0x1548, 0x0046, 0x0016,
+ 0xbaa0, 0x2220, 0x9006, 0x2009, 0x1848, 0x210c, 0xd1a4, 0x0138,
+ 0x2009, 0x0029, 0x080c, 0xe73a, 0xb800, 0xc0e5, 0xb802, 0x2019,
+ 0x0029, 0x080c, 0x9356, 0x0076, 0x2039, 0x0000, 0x080c, 0x9229,
+ 0x2c08, 0x080c, 0xe477, 0x007e, 0x2001, 0x0007, 0x080c, 0x6615,
+ 0x2001, 0x0007, 0x080c, 0x65e9, 0x001e, 0x004e, 0x9006, 0x015e,
+ 0x003e, 0x002e, 0x00be, 0x00ce, 0x0005, 0x00d6, 0x2069, 0x026e,
+ 0x6800, 0x9086, 0x0800, 0x0118, 0x6017, 0x0000, 0x0008, 0x9006,
+ 0x00de, 0x0005, 0x00b6, 0x00f6, 0x0016, 0x0026, 0x0036, 0x0156,
+ 0x2079, 0x026c, 0x7930, 0x7834, 0x080c, 0x287c, 0x11d0, 0x080c,
+ 0x6699, 0x11b8, 0x2011, 0x0270, 0x20a9, 0x0004, 0x0096, 0x2b48,
+ 0x2019, 0x000a, 0x080c, 0xbf11, 0x009e, 0x1158, 0x2011, 0x0274,
+ 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xbf11,
+ 0x009e, 0x015e, 0x003e, 0x002e, 0x001e, 0x00fe, 0x00be, 0x0005,
+ 0x00b6, 0x0006, 0x0016, 0x0026, 0x0036, 0x0156, 0x2011, 0x0263,
+ 0x2204, 0x8211, 0x220c, 0x080c, 0x287c, 0x11d0, 0x080c, 0x6699,
+ 0x11b8, 0x2011, 0x0276, 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019,
+ 0x000a, 0x080c, 0xbf11, 0x009e, 0x1158, 0x2011, 0x027a, 0x20a9,
+ 0x0004, 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xbf11, 0x009e,
+ 0x015e, 0x003e, 0x002e, 0x001e, 0x000e, 0x00be, 0x0005, 0x00e6,
+ 0x00c6, 0x0086, 0x0076, 0x0066, 0x0056, 0x0046, 0x0026, 0x0126,
+ 0x2091, 0x8000, 0x2740, 0x2029, 0x19ef, 0x252c, 0x2021, 0x19f5,
+ 0x2424, 0x2061, 0x1cd0, 0x2071, 0x1800, 0x7654, 0x7074, 0x81ff,
+ 0x0150, 0x0006, 0x9186, 0x1ab2, 0x000e, 0x0128, 0x8001, 0x9602,
+ 0x1a04, 0xe514, 0x0018, 0x9606, 0x0904, 0xe514, 0x080c, 0x8a3d,
+ 0x0904, 0xe50b, 0x2100, 0x9c06, 0x0904, 0xe50b, 0x080c, 0xe77b,
+ 0x1904, 0xe50b, 0x080c, 0xeb19, 0x0904, 0xe50b, 0x080c, 0xe76b,
+ 0x0904, 0xe50b, 0x6720, 0x9786, 0x0001, 0x1148, 0x080c, 0x3373,
+ 0x0904, 0xe553, 0x6004, 0x9086, 0x0000, 0x1904, 0xe553, 0x9786,
+ 0x0004, 0x0904, 0xe553, 0x9786, 0x0007, 0x0904, 0xe50b, 0x2500,
+ 0x9c06, 0x0904, 0xe50b, 0x2400, 0x9c06, 0x05e8, 0x88ff, 0x0118,
+ 0x6054, 0x9906, 0x15c0, 0x0096, 0x6000, 0x9086, 0x0004, 0x1120,
+ 0x0016, 0x080c, 0x1aa1, 0x001e, 0x9786, 0x000a, 0x0148, 0x080c,
+ 0xce8e, 0x1130, 0x080c, 0xb905, 0x009e, 0x080c, 0xaf74, 0x0418,
+ 0x6014, 0x2048, 0x080c, 0xcc86, 0x01d8, 0x9786, 0x0003, 0x1570,
+ 0xa867, 0x0103, 0xa87c, 0xd0cc, 0x0130, 0x0096, 0xa878, 0x2048,
+ 0x080c, 0x0fb1, 0x009e, 0xab7a, 0xa877, 0x0000, 0x080c, 0xea94,
+ 0x0016, 0x080c, 0xcf7c, 0x080c, 0x6d0b, 0x001e, 0x080c, 0xce71,
+ 0x009e, 0x080c, 0xaf74, 0x9ce0, 0x0018, 0x2001, 0x181a, 0x2004,
+ 0x9c02, 0x1210, 0x0804, 0xe48b, 0x012e, 0x002e, 0x004e, 0x005e,
+ 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee, 0x0005, 0x9786, 0x0006,
+ 0x1150, 0x9386, 0x0005, 0x0128, 0x080c, 0xea94, 0x080c, 0xe6dd,
+ 0x08f8, 0x009e, 0x0c00, 0x9786, 0x0009, 0x11f8, 0x6000, 0x9086,
+ 0x0004, 0x01c0, 0x6000, 0x9086, 0x0003, 0x11a0, 0x080c, 0x9713,
+ 0x0096, 0x6114, 0x2148, 0x080c, 0xcc86, 0x0118, 0x6010, 0x080c,
+ 0x6d17, 0x009e, 0x00c6, 0x080c, 0xaf43, 0x00ce, 0x0036, 0x080c,
+ 0x9891, 0x003e, 0x009e, 0x0804, 0xe50b, 0x9786, 0x000a, 0x0904,
+ 0xe4fb, 0x0804, 0xe4f0, 0x81ff, 0x0904, 0xe50b, 0x9180, 0x0001,
+ 0x2004, 0x9086, 0x0018, 0x0138, 0x9180, 0x0001, 0x2004, 0x9086,
+ 0x002d, 0x1904, 0xe50b, 0x6000, 0x9086, 0x0002, 0x1904, 0xe50b,
+ 0x080c, 0xce7d, 0x0138, 0x080c, 0xce8e, 0x1904, 0xe50b, 0x080c,
+ 0xb905, 0x0038, 0x080c, 0x3247, 0x080c, 0xce8e, 0x1110, 0x080c,
+ 0xb905, 0x080c, 0xaf74, 0x0804, 0xe50b, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0039, 0x0005, 0x00c6, 0x00e6, 0x0016, 0x2c08, 0x2170,
+ 0x9006, 0x080c, 0xe704, 0x001e, 0x0120, 0x6020, 0x9084, 0x000f,
+ 0x001b, 0x00ee, 0x00ce, 0x0005, 0xe5a2, 0xe5a2, 0xe5a2, 0xe5a2,
+ 0xe5a2, 0xe5a2, 0xe5a4, 0xe5a2, 0xe5a2, 0xe5a2, 0xe5a2, 0xaf74,
+ 0xaf74, 0xe5a2, 0x9006, 0x0005, 0x0036, 0x0046, 0x0016, 0x7010,
+ 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00, 0x2009, 0x0020, 0x080c,
+ 0xe73a, 0x001e, 0x004e, 0x2019, 0x0002, 0x080c, 0xe2c0, 0x003e,
+ 0x9085, 0x0001, 0x0005, 0x0096, 0x080c, 0xcc86, 0x0140, 0x6014,
+ 0x904d, 0x080c, 0xc8a5, 0x687b, 0x0005, 0x080c, 0x6d17, 0x009e,
+ 0x080c, 0xaf74, 0x9085, 0x0001, 0x0005, 0x2001, 0x0001, 0x080c,
+ 0x65d5, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019,
+ 0x1805, 0x2011, 0x0276, 0x080c, 0xbefd, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x9005, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0086, 0x0076,
+ 0x0066, 0x00b6, 0x0126, 0x2091, 0x8000, 0x2740, 0x2061, 0x1cd0,
+ 0x2079, 0x0001, 0x8fff, 0x0904, 0xe63d, 0x2071, 0x1800, 0x7654,
+ 0x7074, 0x8001, 0x9602, 0x1a04, 0xe63d, 0x88ff, 0x0120, 0x2800,
+ 0x9c06, 0x1590, 0x2078, 0x080c, 0xe76b, 0x0570, 0x2400, 0x9c06,
+ 0x0558, 0x6720, 0x9786, 0x0006, 0x1538, 0x9786, 0x0007, 0x0520,
+ 0x88ff, 0x1140, 0x6010, 0x9b06, 0x11f8, 0x85ff, 0x0118, 0x6054,
+ 0x9106, 0x11d0, 0x0096, 0x601c, 0xd084, 0x0140, 0x080c, 0xe997,
+ 0x080c, 0xd3a0, 0x080c, 0x1aa1, 0x6023, 0x0007, 0x6014, 0x2048,
+ 0x080c, 0xcc86, 0x0120, 0x0046, 0x080c, 0xe6dd, 0x004e, 0x009e,
+ 0x080c, 0xaf74, 0x88ff, 0x1198, 0x9ce0, 0x0018, 0x2001, 0x181a,
+ 0x2004, 0x9c02, 0x1210, 0x0804, 0xe5f2, 0x9006, 0x012e, 0x00be,
+ 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x98c5,
+ 0x0001, 0x0ca0, 0x00b6, 0x0076, 0x0056, 0x0086, 0x9046, 0x2029,
+ 0x0001, 0x2c20, 0x2019, 0x0002, 0x6210, 0x2258, 0x0096, 0x904e,
+ 0x080c, 0xa76b, 0x009e, 0x008e, 0x903e, 0x080c, 0xa816, 0x080c,
+ 0xe5e3, 0x005e, 0x007e, 0x00be, 0x0005, 0x00b6, 0x0046, 0x0056,
+ 0x0076, 0x00c6, 0x0156, 0x2c20, 0x2128, 0x20a9, 0x007f, 0x900e,
+ 0x0016, 0x0036, 0x080c, 0x6699, 0x1190, 0x0056, 0x0086, 0x9046,
+ 0x2508, 0x2029, 0x0001, 0x0096, 0x904e, 0x080c, 0xa76b, 0x009e,
+ 0x008e, 0x903e, 0x080c, 0xa816, 0x080c, 0xe5e3, 0x005e, 0x003e,
+ 0x001e, 0x8108, 0x1f04, 0xe670, 0x015e, 0x00ce, 0x007e, 0x005e,
+ 0x004e, 0x00be, 0x0005, 0x00b6, 0x0076, 0x0056, 0x6210, 0x2258,
+ 0x0086, 0x9046, 0x2029, 0x0001, 0x2019, 0x0048, 0x0096, 0x904e,
+ 0x080c, 0xa76b, 0x009e, 0x008e, 0x903e, 0x080c, 0xa816, 0x2c20,
+ 0x080c, 0xe5e3, 0x005e, 0x007e, 0x00be, 0x0005, 0x00b6, 0x0046,
+ 0x0056, 0x0076, 0x00c6, 0x0156, 0x2c20, 0x20a9, 0x0800, 0x900e,
+ 0x0016, 0x0036, 0x080c, 0x6699, 0x11a0, 0x0086, 0x9046, 0x2828,
+ 0x0046, 0x2021, 0x0001, 0x080c, 0xe97b, 0x004e, 0x0096, 0x904e,
+ 0x080c, 0xa76b, 0x009e, 0x008e, 0x903e, 0x080c, 0xa816, 0x080c,
+ 0xe5e3, 0x003e, 0x001e, 0x8108, 0x1f04, 0xe6b8, 0x015e, 0x00ce,
+ 0x007e, 0x005e, 0x004e, 0x00be, 0x0005, 0x0016, 0x00f6, 0x080c,
+ 0xcc84, 0x0198, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0046, 0x0180,
+ 0xa800, 0x907d, 0x0138, 0xa803, 0x0000, 0xab82, 0x080c, 0x6d17,
+ 0x2f48, 0x0cb0, 0xab82, 0x080c, 0x6d17, 0x00fe, 0x001e, 0x0005,
+ 0xa800, 0x907d, 0x0130, 0xa803, 0x0000, 0x080c, 0x6d17, 0x2f48,
+ 0x0cb8, 0x080c, 0x6d17, 0x0c88, 0x00e6, 0x0046, 0x0036, 0x2061,
+ 0x1cd0, 0x9005, 0x1138, 0x2071, 0x1800, 0x7454, 0x7074, 0x8001,
+ 0x9402, 0x12f8, 0x2100, 0x9c06, 0x0188, 0x6000, 0x9086, 0x0000,
+ 0x0168, 0x6008, 0x9206, 0x1150, 0x6320, 0x9386, 0x0009, 0x01b0,
+ 0x6010, 0x91a0, 0x0004, 0x2424, 0x9406, 0x0140, 0x9ce0, 0x0018,
+ 0x2001, 0x181a, 0x2004, 0x9c02, 0x1220, 0x0c20, 0x9085, 0x0001,
+ 0x0008, 0x9006, 0x003e, 0x004e, 0x00ee, 0x0005, 0x631c, 0xd3c4,
+ 0x1d68, 0x0c30, 0x0096, 0x0006, 0x080c, 0x0fff, 0x000e, 0x090c,
+ 0x0dd5, 0xaae2, 0xa867, 0x010d, 0xa88e, 0x0026, 0x2010, 0x080c,
+ 0xcc74, 0x2001, 0x0000, 0x0120, 0x2200, 0x9080, 0x0015, 0x2004,
+ 0x002e, 0xa87a, 0x9186, 0x0020, 0x0110, 0xa8e3, 0xffff, 0xa986,
+ 0xac76, 0xa87f, 0x0000, 0x2001, 0x198d, 0x2004, 0xa882, 0x9006,
+ 0xa802, 0xa86a, 0xa88a, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d17,
+ 0x012e, 0x009e, 0x0005, 0x6700, 0x9786, 0x0000, 0x0158, 0x9786,
+ 0x0001, 0x0140, 0x9786, 0x000a, 0x0128, 0x9786, 0x0009, 0x0110,
+ 0x9085, 0x0001, 0x0005, 0x00e6, 0x6010, 0x9075, 0x0138, 0x00b6,
+ 0x2058, 0xb8a0, 0x00be, 0x9206, 0x00ee, 0x0005, 0x9085, 0x0001,
+ 0x0cd8, 0x0016, 0x6004, 0x908e, 0x001e, 0x11a0, 0x8007, 0x6134,
+ 0x918c, 0x00ff, 0x9105, 0x6036, 0x6007, 0x0085, 0x6003, 0x000b,
+ 0x6023, 0x0005, 0x2001, 0x1986, 0x2004, 0x601a, 0x080c, 0x91b1,
+ 0x080c, 0x9763, 0x001e, 0x0005, 0xa001, 0xa001, 0x0005, 0x6024,
+ 0xd0e4, 0x0158, 0xd0cc, 0x0118, 0x080c, 0xcfc0, 0x0030, 0x080c,
+ 0xe997, 0x080c, 0x876f, 0x080c, 0xaf43, 0x0005, 0x9280, 0x0008,
+ 0x2004, 0x9084, 0x000f, 0x0002, 0xe7ca, 0xe7ca, 0xe7ca, 0xe7cc,
+ 0xe7ca, 0xe7cc, 0xe7cc, 0xe7ca, 0xe7cc, 0xe7ca, 0xe7ca, 0xe7ca,
+ 0xe7ca, 0xe7ca, 0x9006, 0x0005, 0x9085, 0x0001, 0x0005, 0x9280,
+ 0x0008, 0x2004, 0x9084, 0x000f, 0x0002, 0xe7e3, 0xe7e3, 0xe7e3,
+ 0xe7e3, 0xe7e3, 0xe7e3, 0xe7f0, 0xe7e3, 0xe7e3, 0xe7e3, 0xe7e3,
+ 0xe7e3, 0xe7e3, 0xe7e3, 0x6007, 0x003b, 0x602f, 0x0009, 0x6017,
+ 0x2a00, 0x6003, 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763, 0x0005,
+ 0x0096, 0x00c6, 0x2260, 0x080c, 0xe997, 0x6043, 0x0000, 0x6024,
+ 0xc0f4, 0xc0e4, 0x6026, 0x603b, 0x0000, 0x00ce, 0x00d6, 0x2268,
+ 0x9186, 0x0007, 0x1904, 0xe849, 0x6814, 0x9005, 0x0138, 0x2048,
+ 0xa87c, 0xd0fc, 0x1118, 0x00de, 0x009e, 0x08a8, 0x6007, 0x003a,
+ 0x6003, 0x0001, 0x080c, 0x91b1, 0x080c, 0x9763, 0x00c6, 0x2d60,
+ 0x6100, 0x9186, 0x0002, 0x1904, 0xe8c0, 0x6014, 0x9005, 0x1138,
+ 0x6000, 0x9086, 0x0007, 0x190c, 0x0dd5, 0x0804, 0xe8c0, 0x2048,
+ 0x080c, 0xcc86, 0x1130, 0x0028, 0x2048, 0xa800, 0x9005, 0x1de0,
+ 0x2900, 0x2048, 0xa87c, 0x9084, 0x0003, 0x9086, 0x0002, 0x1168,
+ 0xa87c, 0xc0dc, 0xc0f4, 0xa87e, 0xa880, 0xc0fc, 0xa882, 0x2009,
+ 0x0043, 0x080c, 0xe114, 0x0804, 0xe8c0, 0x2009, 0x0041, 0x0804,
+ 0xe8ba, 0x9186, 0x0005, 0x15a0, 0x6814, 0x2048, 0xa87c, 0xd0bc,
+ 0x1120, 0x00de, 0x009e, 0x0804, 0xe7e3, 0xd0b4, 0x0128, 0xd0fc,
+ 0x090c, 0x0dd5, 0x0804, 0xe804, 0x6007, 0x003a, 0x6003, 0x0001,
+ 0x080c, 0x91b1, 0x080c, 0x9763, 0x00c6, 0x2d60, 0x6100, 0x9186,
+ 0x0002, 0x0120, 0x9186, 0x0004, 0x1904, 0xe8c0, 0x6814, 0x2048,
+ 0xa97c, 0xc1f4, 0xc1dc, 0xa97e, 0xa980, 0xc1fc, 0xc1bc, 0xa982,
+ 0x00f6, 0x2c78, 0x080c, 0x1754, 0x00fe, 0x2009, 0x0042, 0x04d0,
+ 0x0036, 0x080c, 0x0fff, 0x090c, 0x0dd5, 0xa867, 0x010d, 0x9006,
+ 0xa802, 0xa86a, 0xa88a, 0x2d18, 0xab8e, 0xa887, 0x0045, 0x2c00,
+ 0xa892, 0x6038, 0xa8a2, 0x2360, 0x6024, 0xc0dd, 0x6026, 0x6010,
+ 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x2004, 0x6354, 0xab7a, 0xa876,
+ 0x9006, 0xa87e, 0xa882, 0xad9a, 0xae96, 0xa89f, 0x0001, 0x080c,
+ 0x6d17, 0x2019, 0x0045, 0x6008, 0x2068, 0x080c, 0xe2c0, 0x2d00,
+ 0x600a, 0x6023, 0x0006, 0x6003, 0x0007, 0x901e, 0x631a, 0x6342,
+ 0x003e, 0x0038, 0x6043, 0x0000, 0x6003, 0x0007, 0x080c, 0xe114,
+ 0x00ce, 0x00de, 0x009e, 0x0005, 0x9186, 0x0013, 0x1128, 0x6004,
+ 0x9082, 0x0085, 0x2008, 0x00c2, 0x9186, 0x0027, 0x1178, 0x080c,
+ 0x9657, 0x0036, 0x0096, 0x6014, 0x2048, 0x2019, 0x0004, 0x080c,
+ 0xe6dd, 0x009e, 0x003e, 0x080c, 0x9763, 0x0005, 0x9186, 0x0014,
+ 0x0d70, 0x080c, 0xafd9, 0x0005, 0xe8f3, 0xe8f1, 0xe8f1, 0xe8f1,
+ 0xe8f1, 0xe8f1, 0xe8f3, 0xe8f1, 0xe8f1, 0xe8f1, 0xe8f1, 0xe8f1,
+ 0xe8f1, 0x080c, 0x0dd5, 0x080c, 0x9657, 0x6003, 0x000c, 0x080c,
+ 0x9763, 0x0005, 0x9182, 0x0092, 0x1220, 0x9182, 0x0085, 0x0208,
+ 0x001a, 0x080c, 0xafd9, 0x0005, 0xe911, 0xe911, 0xe911, 0xe911,
+ 0xe913, 0xe933, 0xe911, 0xe911, 0xe911, 0xe911, 0xe911, 0xe911,
+ 0xe911, 0x080c, 0x0dd5, 0x00d6, 0x2c68, 0x080c, 0xaeed, 0x01b0,
+ 0x6003, 0x0001, 0x6007, 0x001e, 0x2009, 0x026e, 0x210c, 0x613a,
+ 0x2009, 0x026f, 0x210c, 0x613e, 0x600b, 0xffff, 0x6910, 0x6112,
+ 0x6023, 0x0004, 0x080c, 0x91b1, 0x080c, 0x9763, 0x2d60, 0x080c,
+ 0xaf43, 0x00de, 0x0005, 0x080c, 0xaf43, 0x0005, 0x00e6, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0ec, 0x00ee, 0x0005, 0x2009,
+ 0x1867, 0x210c, 0xd1ec, 0x05b0, 0x6003, 0x0002, 0x6024, 0xc0e5,
+ 0x6026, 0xd0cc, 0x0150, 0x2001, 0x1987, 0x2004, 0x6042, 0x2009,
+ 0x1867, 0x210c, 0xd1f4, 0x1520, 0x00a0, 0x2009, 0x1867, 0x210c,
+ 0xd1f4, 0x0128, 0x6024, 0xc0e4, 0x6026, 0x9006, 0x00d8, 0x2001,
+ 0x1987, 0x200c, 0x2001, 0x1985, 0x2004, 0x9100, 0x9080, 0x000a,
+ 0x6042, 0x6010, 0x00b6, 0x2058, 0xb8bc, 0x00be, 0x0008, 0x2104,
+ 0x9005, 0x0118, 0x9088, 0x0003, 0x0cd0, 0x2c0a, 0x600f, 0x0000,
+ 0x9085, 0x0001, 0x0005, 0x0016, 0x00c6, 0x00e6, 0x6154, 0xb8bc,
+ 0x2060, 0x8cff, 0x0180, 0x84ff, 0x1118, 0x6054, 0x9106, 0x1138,
+ 0x600c, 0x2072, 0x080c, 0x876f, 0x080c, 0xaf43, 0x0010, 0x9cf0,
+ 0x0003, 0x2e64, 0x0c70, 0x00ee, 0x00ce, 0x001e, 0x0005, 0x00d6,
+ 0x00b6, 0x6010, 0x2058, 0xb8bc, 0x2068, 0x9005, 0x0130, 0x9c06,
+ 0x0110, 0x680c, 0x0cd0, 0x600c, 0x680e, 0x00be, 0x00de, 0x0005,
+ 0x0026, 0x0036, 0x0156, 0x2011, 0x182c, 0x2204, 0x9084, 0x00ff,
+ 0x2019, 0x026e, 0x2334, 0x9636, 0x1508, 0x8318, 0x2334, 0x2204,
+ 0x9084, 0xff00, 0x9636, 0x11d0, 0x2011, 0x0270, 0x20a9, 0x0004,
+ 0x6010, 0x0096, 0x2048, 0x2019, 0x000a, 0x080c, 0xbf11, 0x009e,
+ 0x1168, 0x2011, 0x0274, 0x20a9, 0x0004, 0x6010, 0x0096, 0x2048,
+ 0x2019, 0x0006, 0x080c, 0xbf11, 0x009e, 0x1100, 0x015e, 0x003e,
+ 0x002e, 0x0005, 0x00e6, 0x2071, 0x1800, 0x080c, 0x6040, 0x080c,
+ 0x2ff5, 0x00ee, 0x0005, 0x0096, 0x0026, 0x080c, 0x0fff, 0x090c,
+ 0x0dd5, 0xa85c, 0x9080, 0x001a, 0x20a0, 0x20a9, 0x000c, 0xa860,
+ 0x20e8, 0x9006, 0x4004, 0x9186, 0x0046, 0x1118, 0xa867, 0x0136,
+ 0x0038, 0xa867, 0x0138, 0x9186, 0x0041, 0x0110, 0xa87b, 0x0001,
+ 0x7038, 0x9084, 0xff00, 0x7240, 0x9294, 0xff00, 0x8007, 0x9215,
+ 0xaa9a, 0x9186, 0x0046, 0x1168, 0x7038, 0x9084, 0x00ff, 0x723c,
+ 0x9294, 0xff00, 0x9215, 0xaa9e, 0x723c, 0x9294, 0x00ff, 0xaaa2,
+ 0x0060, 0x7040, 0x9084, 0x00ff, 0x7244, 0x9294, 0xff00, 0x9215,
+ 0xaa9e, 0x7244, 0x9294, 0x00ff, 0xaaa2, 0x9186, 0x0046, 0x1118,
+ 0x9e90, 0x0012, 0x0010, 0x9e90, 0x001a, 0x2204, 0x8007, 0xa8a6,
+ 0x8210, 0x2204, 0x8007, 0xa8aa, 0x8210, 0x2204, 0x8007, 0xa8ae,
+ 0x8210, 0x2204, 0x8007, 0xa8b2, 0x8210, 0x9186, 0x0046, 0x11b8,
+ 0x9e90, 0x0016, 0x2204, 0x8007, 0xa8b6, 0x8210, 0x2204, 0x8007,
+ 0xa8ba, 0x8210, 0x2204, 0x8007, 0xa8be, 0x8210, 0x2204, 0x8007,
+ 0xa8c2, 0x8210, 0x2011, 0x0205, 0x2013, 0x0001, 0x00b0, 0x9e90,
+ 0x001e, 0x2204, 0x8007, 0xa8b6, 0x8210, 0x2204, 0x8007, 0xa8ba,
+ 0x2011, 0x0205, 0x2013, 0x0001, 0x2011, 0x0260, 0x2204, 0x8007,
+ 0xa8be, 0x8210, 0x2204, 0x8007, 0xa8c2, 0x9186, 0x0046, 0x1118,
+ 0x2011, 0x0262, 0x0010, 0x2011, 0x026a, 0x0146, 0x01d6, 0x0036,
+ 0x20a9, 0x0001, 0x2019, 0x0008, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0031, 0x20a0, 0x2204, 0x8007, 0x4004, 0x8210, 0x8319, 0x1dd0,
+ 0x003e, 0x01ce, 0x013e, 0x2011, 0x0205, 0x2013, 0x0000, 0x002e,
+ 0x080c, 0x6d17, 0x009e, 0x0005, 0x00e6, 0x6010, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0fc, 0x0108, 0x0011, 0x00ee, 0x0005, 0xa880,
+ 0xc0e5, 0xa882, 0x0005, 0x00e6, 0x00d6, 0x00c6, 0x0076, 0x0066,
+ 0x0056, 0x0046, 0x0026, 0x0016, 0x0126, 0x2091, 0x8000, 0x2029,
+ 0x19ef, 0x252c, 0x2021, 0x19f5, 0x2424, 0x2061, 0x1cd0, 0x2071,
+ 0x1800, 0x7654, 0x7074, 0x9606, 0x0578, 0x6720, 0x9786, 0x0001,
+ 0x0118, 0x9786, 0x0008, 0x1500, 0x2500, 0x9c06, 0x01e8, 0x2400,
+ 0x9c06, 0x01d0, 0x080c, 0xe76b, 0x01b8, 0x080c, 0xe77b, 0x11a0,
+ 0x6000, 0x9086, 0x0004, 0x1120, 0x0016, 0x080c, 0x1aa1, 0x001e,
+ 0x080c, 0xce7d, 0x1110, 0x080c, 0x3247, 0x080c, 0xce8e, 0x1110,
+ 0x080c, 0xb905, 0x080c, 0xaf74, 0x9ce0, 0x0018, 0x2001, 0x181a,
+ 0x2004, 0x9c02, 0x1208, 0x0858, 0x012e, 0x001e, 0x002e, 0x004e,
+ 0x005e, 0x006e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x2001,
+ 0x1810, 0x2004, 0xd0dc, 0x0005, 0x0006, 0x2001, 0x1837, 0x2004,
+ 0xd09c, 0x000e, 0x0005, 0x0006, 0x0036, 0x0046, 0x080c, 0xd388,
+ 0x0168, 0x2019, 0xffff, 0x9005, 0x0128, 0x6010, 0x00b6, 0x2058,
+ 0xbba0, 0x00be, 0x2021, 0x0004, 0x080c, 0x4d36, 0x004e, 0x003e,
+ 0x000e, 0x6004, 0x9086, 0x0001, 0x1128, 0x080c, 0xa8dc, 0x080c,
+ 0xaf74, 0x9006, 0x0005, 0x00e6, 0x00c6, 0x00b6, 0x0046, 0x2061,
+ 0x1cd0, 0x2071, 0x1800, 0x7454, 0x7074, 0x8001, 0x9402, 0x12d8,
+ 0x2100, 0x9c06, 0x0168, 0x6000, 0x9086, 0x0000, 0x0148, 0x6010,
+ 0x2058, 0xb8a0, 0x9206, 0x1120, 0x6004, 0x9086, 0x0002, 0x0140,
+ 0x9ce0, 0x0018, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1220, 0x0c40,
+ 0x9085, 0x0001, 0x0008, 0x9006, 0x004e, 0x00be, 0x00ce, 0x00ee,
+ 0x0005, 0x0126, 0x0006, 0x00e6, 0x0016, 0x2091, 0x8000, 0x2071,
+ 0x1840, 0xd5a4, 0x0118, 0x7004, 0x8000, 0x7006, 0xd5b4, 0x0118,
+ 0x7000, 0x8000, 0x7002, 0xd5ac, 0x0178, 0x2500, 0x9084, 0x0007,
+ 0x908e, 0x0003, 0x0148, 0x908e, 0x0004, 0x0130, 0x908e, 0x0005,
+ 0x0118, 0x2071, 0xfff6, 0x0089, 0x001e, 0x00ee, 0x000e, 0x012e,
+ 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000, 0x2071, 0xffee,
+ 0x0021, 0x00ee, 0x000e, 0x012e, 0x0005, 0x2e05, 0x8000, 0x2077,
+ 0x1220, 0x8e70, 0x2e05, 0x8000, 0x2077, 0x0005, 0x00e6, 0x2071,
+ 0xffec, 0x0c99, 0x00ee, 0x0005, 0x00e6, 0x2071, 0xfff0, 0x0c69,
+ 0x00ee, 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000, 0x2071,
+ 0x1840, 0x7014, 0x8000, 0x7016, 0x00ee, 0x000e, 0x012e, 0x0005,
+ 0x0003, 0x000b, 0x079e, 0x0000, 0xc000, 0x0001, 0x8064, 0x0008,
+ 0x0010, 0x0000, 0x8066, 0x0000, 0x0101, 0x0008, 0x4407, 0x0003,
+ 0x8060, 0x0000, 0x0400, 0x0000, 0x580d, 0x000b, 0x79a8, 0x000b,
+ 0x50ee, 0x000b, 0x4c0a, 0x0003, 0xbac0, 0x0009, 0x008a, 0x0000,
+ 0x0c0a, 0x000b, 0x15fe, 0x0008, 0x340a, 0x0003, 0xc4c0, 0x0009,
+ 0x7000, 0x0000, 0xffa0, 0x0001, 0x2000, 0x0000, 0x1668, 0x000b,
+ 0x808c, 0x0008, 0x0001, 0x0000, 0x0000, 0x0007, 0x4028, 0x0000,
+ 0x4047, 0x000a, 0x808c, 0x0008, 0x0002, 0x0000, 0x0822, 0x0003,
+ 0x4022, 0x0000, 0x0028, 0x000b, 0x4122, 0x0008, 0x94c0, 0x0009,
+ 0xff00, 0x0008, 0xffe0, 0x0009, 0x0500, 0x0008, 0x0a93, 0x000b,
+ 0x4447, 0x0002, 0x0e90, 0x0003, 0x0bfe, 0x0008, 0x11a0, 0x0001,
+ 0x126e, 0x0003, 0x0ca0, 0x0001, 0x126e, 0x0003, 0x9180, 0x0001,
+ 0x0004, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x0009, 0x0008, 0x4436, 0x000b, 0x808c, 0x0008,
+ 0x0000, 0x0008, 0x0060, 0x0008, 0x8062, 0x0008, 0x0004, 0x0000,
+ 0x8066, 0x0000, 0x0411, 0x0000, 0x443e, 0x0003, 0x03fe, 0x0000,
+ 0x43e0, 0x0001, 0x0e6b, 0x000b, 0xc2c0, 0x0009, 0x00ff, 0x0008,
+ 0x02e0, 0x0001, 0x0e6b, 0x000b, 0x9180, 0x0001, 0x0005, 0x0008,
+ 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000,
+ 0x0019, 0x0000, 0x444d, 0x000b, 0x0240, 0x0002, 0x0a68, 0x0003,
+ 0x00fe, 0x0000, 0x326b, 0x000b, 0x0248, 0x000a, 0x085c, 0x0003,
+ 0x9180, 0x0001, 0x0006, 0x0008, 0x7f62, 0x0008, 0x8002, 0x0008,
+ 0x0003, 0x0008, 0x8066, 0x0000, 0x020a, 0x0000, 0x445b, 0x0003,
+ 0x112a, 0x0000, 0x002e, 0x0008, 0x022c, 0x0008, 0x3a44, 0x0002,
+ 0x0c0a, 0x000b, 0x808c, 0x0008, 0x0002, 0x0000, 0x1760, 0x0008,
+ 0x8062, 0x0008, 0x000f, 0x0008, 0x8066, 0x0000, 0x0011, 0x0008,
+ 0x4468, 0x0003, 0x01fe, 0x0008, 0x42e0, 0x0009, 0x0e5c, 0x0003,
+ 0x00fe, 0x0000, 0x43e0, 0x0001, 0x0e5c, 0x0003, 0x1734, 0x0000,
+ 0x1530, 0x0000, 0x1632, 0x0008, 0x0d2a, 0x0008, 0x9880, 0x0001,
+ 0x0010, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x1e0a, 0x0008, 0x447a, 0x0003, 0x808a, 0x0008,
+ 0x0003, 0x0008, 0x1a60, 0x0000, 0x8062, 0x0008, 0x0002, 0x0000,
+ 0x5880, 0x000b, 0x8066, 0x0000, 0x3679, 0x0000, 0x4483, 0x0003,
+ 0x5884, 0x0003, 0x3efe, 0x0008, 0x7f4f, 0x0002, 0x088a, 0x000b,
+ 0x0d00, 0x0000, 0x0092, 0x000c, 0x8054, 0x0008, 0x0011, 0x0008,
+ 0x8074, 0x0000, 0x1010, 0x0008, 0x1efe, 0x0000, 0x300a, 0x000b,
+ 0x00c8, 0x000c, 0x000a, 0x000b, 0x00fe, 0x0000, 0x349a, 0x0003,
+ 0x1a60, 0x0000, 0x8062, 0x0008, 0x0007, 0x0000, 0x8066, 0x0000,
+ 0x0231, 0x0008, 0x4499, 0x000b, 0x03fe, 0x0000, 0x04d0, 0x0001,
+ 0x0cc0, 0x000b, 0x82c0, 0x0001, 0x1f00, 0x0000, 0xffa0, 0x0001,
+ 0x0400, 0x0000, 0x08af, 0x0003, 0x14c0, 0x000b, 0x01fe, 0x0008,
+ 0x0580, 0x0009, 0x7f06, 0x0000, 0x02fe, 0x0008, 0xffc0, 0x0001,
+ 0x00ff, 0x0008, 0x0690, 0x0001, 0x10af, 0x0003, 0x7f08, 0x0008,
+ 0x84c0, 0x0001, 0xff00, 0x0008, 0x08c0, 0x0003, 0x00fe, 0x0000,
+ 0x34b6, 0x000b, 0x8072, 0x0000, 0x1010, 0x0008, 0x3944, 0x0002,
+ 0x08b1, 0x0003, 0x00ba, 0x0003, 0x8072, 0x0000, 0x2020, 0x0008,
+ 0x3945, 0x000a, 0x08b6, 0x000b, 0x3946, 0x000a, 0x0cc7, 0x0003,
+ 0x0000, 0x0007, 0x3943, 0x000a, 0x08c7, 0x000b, 0x00ba, 0x0003,
+ 0x00fe, 0x0000, 0x34c5, 0x0003, 0x8072, 0x0000, 0x1000, 0x0000,
+ 0x00c7, 0x0003, 0x8072, 0x0000, 0x2000, 0x0000, 0x4000, 0x000f,
+ 0x1c60, 0x0000, 0x1b62, 0x0000, 0x8066, 0x0000, 0x0231, 0x0008,
+ 0x44cc, 0x000b, 0x58cd, 0x000b, 0x0140, 0x0008, 0x0242, 0x0000,
+ 0x1f43, 0x0002, 0x0cdb, 0x000b, 0x0d44, 0x0000, 0x0d46, 0x0008,
+ 0x0348, 0x0008, 0x044a, 0x0008, 0x030a, 0x0008, 0x040c, 0x0000,
+ 0x0d06, 0x0000, 0x0d08, 0x0008, 0x00df, 0x0003, 0x0344, 0x0008,
+ 0x0446, 0x0008, 0x0548, 0x0008, 0x064a, 0x0000, 0x1948, 0x000a,
+ 0x08e2, 0x0003, 0x0d4a, 0x0008, 0x58e2, 0x0003, 0x3efe, 0x0008,
+ 0x7f4f, 0x0002, 0x08e9, 0x000b, 0x8000, 0x0000, 0x0001, 0x0000,
+ 0x0092, 0x000c, 0x8054, 0x0008, 0x0001, 0x0000, 0x8074, 0x0000,
+ 0x2020, 0x0008, 0x4000, 0x000f, 0x3a40, 0x000a, 0x0c0d, 0x0003,
+ 0x2b24, 0x0008, 0x2b24, 0x0008, 0x58f2, 0x000b, 0x8054, 0x0008,
+ 0x0002, 0x0000, 0x1242, 0x0002, 0x0940, 0x0003, 0x3a45, 0x000a,
+ 0x092f, 0x0003, 0x8072, 0x0000, 0x1000, 0x0000, 0x3945, 0x000a,
+ 0x08ff, 0x0003, 0x8072, 0x0000, 0x3010, 0x0000, 0x1e10, 0x000a,
+ 0x7f3c, 0x0000, 0x092a, 0x0003, 0x1d00, 0x0002, 0x7f3a, 0x0000,
+ 0x0d60, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000, 0x0009, 0x0008,
+ 0x4508, 0x000b, 0x00fe, 0x0000, 0x3527, 0x000b, 0x1c60, 0x0000,
+ 0x8062, 0x0008, 0x0001, 0x0000, 0x8066, 0x0000, 0x0009, 0x0008,
+ 0x4510, 0x000b, 0x00fe, 0x0000, 0x3243, 0x000b, 0x0038, 0x0000,
+ 0x0060, 0x0008, 0x8062, 0x0008, 0x0019, 0x0000, 0x8066, 0x0000,
+ 0x0009, 0x0008, 0x4519, 0x000b, 0x80c0, 0x0009, 0x00ff, 0x0008,
+ 0x7f3e, 0x0008, 0x0d60, 0x0000, 0x0efe, 0x0008, 0x1f80, 0x0001,
+ 0x7f62, 0x0008, 0x8066, 0x0000, 0x0009, 0x0008, 0x4523, 0x000b,
+ 0x003a, 0x0008, 0x1dfe, 0x0000, 0x0104, 0x000b, 0x0036, 0x0008,
+ 0x00c8, 0x000c, 0x0140, 0x000b, 0x8074, 0x0000, 0x2000, 0x0000,
+ 0x8072, 0x0000, 0x2000, 0x0000, 0x0140, 0x000b, 0x3a44, 0x0002,
+ 0x0a71, 0x000b, 0x8074, 0x0000, 0x1000, 0x0000, 0x8072, 0x0000,
+ 0x1000, 0x0000, 0x2d0e, 0x0000, 0x2d0e, 0x0000, 0x3640, 0x0003,
+ 0x26fe, 0x0008, 0x26fe, 0x0008, 0x2700, 0x0008, 0x2700, 0x0008,
+ 0x00d0, 0x0009, 0x0d52, 0x000b, 0x8074, 0x0000, 0x4040, 0x0008,
+ 0x5940, 0x0003, 0x50ee, 0x000b, 0x3a46, 0x000a, 0x0d52, 0x000b,
+ 0x3a47, 0x0002, 0x094d, 0x000b, 0x8054, 0x0008, 0x0004, 0x0000,
+ 0x8074, 0x0000, 0x8000, 0x0000, 0x8072, 0x0000, 0x3000, 0x0008,
+ 0x019c, 0x0003, 0x92c0, 0x0009, 0x0fc8, 0x0000, 0x080a, 0x0003,
+ 0x1246, 0x000a, 0x0e3a, 0x0003, 0x1a60, 0x0000, 0x8062, 0x0008,
+ 0x0002, 0x0000, 0x8066, 0x0000, 0x362a, 0x0000, 0x4557, 0x000b,
+ 0x2000, 0x0000, 0x2000, 0x0000, 0x2102, 0x0000, 0x2102, 0x0000,
+ 0x2204, 0x0000, 0x2204, 0x0000, 0x2306, 0x0000, 0x2306, 0x0000,
+ 0x2408, 0x0000, 0x2408, 0x0000, 0x250a, 0x0000, 0x250a, 0x0000,
+ 0x260c, 0x0000, 0x260c, 0x0000, 0x270e, 0x0000, 0x270e, 0x0000,
+ 0x2810, 0x0000, 0x2810, 0x0000, 0x2912, 0x0000, 0x2912, 0x0000,
+ 0x1a60, 0x0000, 0x8062, 0x0008, 0x0007, 0x0000, 0x8066, 0x0000,
+ 0x0052, 0x0000, 0x4571, 0x0003, 0x92c0, 0x0009, 0x0780, 0x0008,
+ 0x0e56, 0x0003, 0x124b, 0x0002, 0x097a, 0x0003, 0x2e4d, 0x0002,
+ 0x2e4d, 0x0002, 0x0a40, 0x0003, 0x3a46, 0x000a, 0x0d8a, 0x000b,
+ 0x597c, 0x0003, 0x8054, 0x0008, 0x0004, 0x0000, 0x1243, 0x000a,
+ 0x0998, 0x0003, 0x8010, 0x0008, 0x000d, 0x0000, 0x021b, 0x000c,
+ 0x1948, 0x000a, 0x0987, 0x000b, 0x0210, 0x0004, 0x1810, 0x0000,
+ 0x021b, 0x000c, 0x0198, 0x000b, 0x1948, 0x000a, 0x098e, 0x000b,
+ 0x1243, 0x000a, 0x0a43, 0x0003, 0x194d, 0x000a, 0x0992, 0x0003,
+ 0x1243, 0x000a, 0x0a4a, 0x0003, 0x5992, 0x0003, 0x8054, 0x0008,
+ 0x0004, 0x0000, 0x0210, 0x0004, 0x1810, 0x0000, 0x021b, 0x000c,
+ 0x8074, 0x0000, 0xf000, 0x0008, 0x8072, 0x0000, 0x3000, 0x0008,
+ 0x0d30, 0x0000, 0x3a42, 0x0002, 0x0da2, 0x000b, 0x15fe, 0x0008,
+ 0x3461, 0x000b, 0x000a, 0x000b, 0x8074, 0x0000, 0x0501, 0x0000,
+ 0x8010, 0x0008, 0x000c, 0x0008, 0x021b, 0x000c, 0x000a, 0x000b,
+ 0xbbe0, 0x0009, 0x0030, 0x0008, 0x0db8, 0x0003, 0x18fe, 0x0000,
+ 0x3ce0, 0x0009, 0x09b5, 0x0003, 0x15fe, 0x0008, 0x3ce0, 0x0009,
+ 0x09b5, 0x0003, 0x020b, 0x0004, 0x8076, 0x0008, 0x0040, 0x0000,
+ 0x0208, 0x000b, 0x8076, 0x0008, 0x0041, 0x0008, 0x0208, 0x000b,
+ 0xbbe0, 0x0009, 0x0032, 0x0000, 0x0dbd, 0x0003, 0x3c1e, 0x0008,
+ 0x0208, 0x000b, 0xbbe0, 0x0009, 0x003b, 0x0000, 0x0dc2, 0x000b,
+ 0x3c20, 0x0000, 0x0208, 0x000b, 0xbbe0, 0x0009, 0x0035, 0x0008,
+ 0x0dc8, 0x000b, 0x8072, 0x0000, 0x8000, 0x0000, 0x0384, 0x000b,
+ 0xbbe0, 0x0009, 0x0036, 0x0008, 0x0aa5, 0x000b, 0xbbe0, 0x0009,
+ 0x0037, 0x0000, 0x0de9, 0x000b, 0x18fe, 0x0000, 0x3ce0, 0x0009,
+ 0x0db5, 0x000b, 0x8076, 0x0008, 0x0040, 0x0000, 0x1a60, 0x0000,
+ 0x8062, 0x0008, 0x000d, 0x0000, 0x2604, 0x0008, 0x2604, 0x0008,
+ 0x2706, 0x0008, 0x2706, 0x0008, 0x2808, 0x0000, 0x2808, 0x0000,
+ 0x290a, 0x0000, 0x290a, 0x0000, 0x8066, 0x0000, 0x0422, 0x0000,
+ 0x45e0, 0x000b, 0x0210, 0x0004, 0x8054, 0x0008, 0x0004, 0x0000,
+ 0x8074, 0x0000, 0xf000, 0x0008, 0x8072, 0x0000, 0xb000, 0x0000,
+ 0x019c, 0x0003, 0xbbe0, 0x0009, 0x0038, 0x0000, 0x0dfb, 0x000b,
+ 0x18fe, 0x0000, 0x3ce0, 0x0009, 0x09f8, 0x0003, 0x15fe, 0x0008,
+ 0x3ce0, 0x0009, 0x0db1, 0x0003, 0x020b, 0x0004, 0x8076, 0x0008,
+ 0x0040, 0x0000, 0x8072, 0x0000, 0x8000, 0x0000, 0x0268, 0x000b,
+ 0x8076, 0x0008, 0x0042, 0x0008, 0x0208, 0x000b, 0xbbe0, 0x0009,
+ 0x0016, 0x0000, 0x0e08, 0x000b, 0x8074, 0x0000, 0x0808, 0x0008,
+ 0x3a44, 0x0002, 0x0c0c, 0x000b, 0x8074, 0x0000, 0x0800, 0x0000,
+ 0x8072, 0x0000, 0x8000, 0x0000, 0x8000, 0x000f, 0x000a, 0x000b,
+ 0x8072, 0x0000, 0x8000, 0x0000, 0x000a, 0x000b, 0x3d30, 0x000a,
+ 0x7f00, 0x0000, 0xbc80, 0x0001, 0x0007, 0x0000, 0x0214, 0x0003,
+ 0x1930, 0x000a, 0x7f00, 0x0000, 0x9880, 0x0001, 0x0007, 0x0000,
+ 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000,
+ 0x000a, 0x0008, 0x4619, 0x000b, 0x4000, 0x000f, 0x221e, 0x000b,
+ 0x0870, 0x0008, 0x4000, 0x000f, 0x7e1b, 0x000b, 0xbbe0, 0x0009,
+ 0x0030, 0x0008, 0x0e1b, 0x0003, 0x18fe, 0x0000, 0x3ce0, 0x0009,
+ 0x0a2c, 0x0003, 0x15fe, 0x0008, 0x3ce0, 0x0009, 0x0a2c, 0x0003,
+ 0x020b, 0x0004, 0x8076, 0x0008, 0x0040, 0x0000, 0x022e, 0x0003,
+ 0x8076, 0x0008, 0x0041, 0x0008, 0x8072, 0x0000, 0x8000, 0x0000,
+ 0x021b, 0x0003, 0xbac0, 0x0009, 0x0090, 0x0008, 0x0a37, 0x0003,
+ 0x8074, 0x0000, 0x0706, 0x0000, 0x0239, 0x0003, 0x8074, 0x0000,
+ 0x0703, 0x0000, 0x4000, 0x000f, 0x8010, 0x0008, 0x0023, 0x0000,
+ 0x0276, 0x000b, 0x8010, 0x0008, 0x0008, 0x0000, 0x0276, 0x000b,
+ 0x8010, 0x0008, 0x0022, 0x0008, 0x0276, 0x000b, 0x0210, 0x0004,
+ 0x8010, 0x0008, 0x0007, 0x0000, 0x021b, 0x000c, 0x1810, 0x0000,
+ 0x021b, 0x000c, 0x0282, 0x0003, 0x0210, 0x0004, 0x8010, 0x0008,
+ 0x001b, 0x0008, 0x021b, 0x000c, 0x1810, 0x0000, 0x021b, 0x000c,
+ 0x8074, 0x0000, 0xf080, 0x0000, 0x8072, 0x0000, 0x3000, 0x0008,
+ 0x0d30, 0x0000, 0x000a, 0x000b, 0x8010, 0x0008, 0x0009, 0x0008,
+ 0x0276, 0x000b, 0x8010, 0x0008, 0x0005, 0x0008, 0x0276, 0x000b,
+ 0x1648, 0x000a, 0x0c6f, 0x000b, 0x808c, 0x0008, 0x0001, 0x0000,
+ 0x8010, 0x0008, 0x0004, 0x0000, 0x4143, 0x000a, 0x086f, 0x0003,
+ 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x0d2a, 0x0008, 0x0276, 0x000b,
+ 0x8010, 0x0008, 0x0003, 0x0008, 0x027a, 0x000b, 0x8010, 0x0008,
+ 0x000b, 0x0000, 0x027a, 0x000b, 0x8010, 0x0008, 0x0002, 0x0000,
+ 0x027a, 0x000b, 0x3a47, 0x0002, 0x0d40, 0x000b, 0x8010, 0x0008,
+ 0x0006, 0x0008, 0x027a, 0x000b, 0x8074, 0x0000, 0xf000, 0x0008,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x021b, 0x000c, 0x0231, 0x0004,
+ 0x3a40, 0x000a, 0x080a, 0x0003, 0x8010, 0x0008, 0x000c, 0x0008,
+ 0x021b, 0x000c, 0x000a, 0x000b, 0x8074, 0x0000, 0xf080, 0x0000,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000, 0x2e4d, 0x0002,
+ 0x2e4d, 0x0002, 0x0a8d, 0x000b, 0x8054, 0x0008, 0x0019, 0x0000,
+ 0x000a, 0x000b, 0x8054, 0x0008, 0x0009, 0x0008, 0x000a, 0x000b,
+ 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x026b, 0x000b, 0x808c, 0x0008,
+ 0x0000, 0x0008, 0x4447, 0x0002, 0x0ab9, 0x0003, 0xc0c0, 0x0001,
+ 0x00ff, 0x0008, 0xffe0, 0x0009, 0x00ff, 0x0008, 0x0e90, 0x0003,
+ 0xc1e0, 0x0001, 0xffff, 0x0008, 0x0e90, 0x0003, 0x8010, 0x0008,
+ 0x0013, 0x0000, 0x021b, 0x000c, 0x8074, 0x0000, 0x0202, 0x0008,
+ 0x000a, 0x000b, 0x3a40, 0x000a, 0x0eb6, 0x000b, 0x8074, 0x0000,
+ 0x0200, 0x0000, 0x3d00, 0x0000, 0x3cfe, 0x0000, 0x8072, 0x0000,
+ 0x8000, 0x0000, 0x43e0, 0x0001, 0x0eb4, 0x0003, 0x42fe, 0x0000,
+ 0xffc0, 0x0001, 0x00ff, 0x0008, 0x00e0, 0x0009, 0x0a90, 0x000b,
+ 0x0d08, 0x0008, 0x0309, 0x000b, 0x8072, 0x0000, 0x8000, 0x0000,
+ 0x000a, 0x000b, 0x038d, 0x0004, 0x808c, 0x0008, 0x0001, 0x0000,
+ 0x04fe, 0x0008, 0x3370, 0x0003, 0x0460, 0x0000, 0x8062, 0x0008,
+ 0x0001, 0x0000, 0x8066, 0x0000, 0x0009, 0x0008, 0x46c3, 0x0003,
+ 0x0004, 0x0000, 0x80c0, 0x0009, 0x00ff, 0x0008, 0x7f00, 0x0000,
+ 0x80e0, 0x0001, 0x0004, 0x0000, 0x0add, 0x000b, 0x80e0, 0x0001,
+ 0x0005, 0x0008, 0x0add, 0x000b, 0x80e0, 0x0001, 0x0006, 0x0008,
+ 0x0add, 0x000b, 0x82c0, 0x0001, 0xff00, 0x0008, 0x7f04, 0x0008,
+ 0x82e0, 0x0009, 0x0600, 0x0008, 0x0add, 0x000b, 0x82e0, 0x0009,
+ 0x0500, 0x0008, 0x0add, 0x000b, 0x82e0, 0x0009, 0x0400, 0x0000,
+ 0x0f70, 0x0003, 0xc4c0, 0x0009, 0x7000, 0x0000, 0xffe0, 0x0009,
+ 0x1000, 0x0000, 0x0b09, 0x0003, 0x037e, 0x0004, 0x3941, 0x0002,
+ 0x0ae8, 0x000b, 0x8072, 0x0000, 0x0400, 0x0000, 0x000a, 0x000b,
+ 0x0460, 0x0000, 0x80fe, 0x0008, 0x002b, 0x0008, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x2209, 0x0008, 0x46ee, 0x0003, 0x11fe, 0x0000,
+ 0x3304, 0x0003, 0x9180, 0x0001, 0x0002, 0x0000, 0x8060, 0x0000,
+ 0x0400, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000, 0x0609, 0x0008,
+ 0x46f8, 0x000b, 0x42fe, 0x0000, 0xffc0, 0x0001, 0xff00, 0x0008,
+ 0x03e0, 0x0009, 0x0f01, 0x0003, 0x8072, 0x0000, 0x0400, 0x0000,
+ 0x0046, 0x0003, 0x9180, 0x0001, 0x0003, 0x0008, 0x02eb, 0x0003,
+ 0x8072, 0x0000, 0x0400, 0x0000, 0x8010, 0x0008, 0x0010, 0x0000,
+ 0x0361, 0x0003, 0x037e, 0x0004, 0x3941, 0x0002, 0x0b0f, 0x0003,
+ 0x8072, 0x0000, 0x0400, 0x0000, 0x000a, 0x000b, 0x0346, 0x000c,
+ 0x11fe, 0x0000, 0x3717, 0x0003, 0x8072, 0x0000, 0x0400, 0x0000,
+ 0x8010, 0x0008, 0x000e, 0x0000, 0x0361, 0x0003, 0x8060, 0x0000,
+ 0x0400, 0x0000, 0x04fe, 0x0008, 0x372c, 0x000b, 0x808c, 0x0008,
+ 0x0000, 0x0008, 0x9180, 0x0001, 0x0005, 0x0008, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x0009, 0x0008, 0x4722, 0x000b, 0x0060, 0x0008,
+ 0x8062, 0x0008, 0x001b, 0x0008, 0x4304, 0x0008, 0x4206, 0x0008,
+ 0x8066, 0x0000, 0x0412, 0x0000, 0x472a, 0x0003, 0x0343, 0x0003,
+ 0x808c, 0x0008, 0x0001, 0x0000, 0x0460, 0x0000, 0x8062, 0x0008,
+ 0x002b, 0x0008, 0x8066, 0x0000, 0x0609, 0x0008, 0x4733, 0x000b,
+ 0x8066, 0x0000, 0x220a, 0x0008, 0x4736, 0x000b, 0x42fe, 0x0000,
+ 0xffc0, 0x0001, 0xff00, 0x0008, 0x7f04, 0x0008, 0x8060, 0x0000,
+ 0x0400, 0x0000, 0x9180, 0x0001, 0x0002, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x041a, 0x0008, 0x4742, 0x000b, 0x8072, 0x0000,
+ 0x0400, 0x0000, 0x0046, 0x0003, 0x8060, 0x0000, 0x0400, 0x0000,
+ 0x1362, 0x0008, 0x8066, 0x0000, 0x0411, 0x0000, 0x474b, 0x000b,
+ 0x02fe, 0x0008, 0x03e0, 0x0009, 0x0f51, 0x0003, 0x0d22, 0x0000,
+ 0x4000, 0x000f, 0x8280, 0x0009, 0x0002, 0x0000, 0x1380, 0x0001,
+ 0x7f62, 0x0008, 0x8066, 0x0000, 0x2209, 0x0008, 0x4757, 0x0003,
+ 0x0200, 0x000a, 0xffc0, 0x0001, 0x0007, 0x0000, 0x7f06, 0x0000,
+ 0x1362, 0x0008, 0x8066, 0x0000, 0x060a, 0x0008, 0x475f, 0x000b,
+ 0x4000, 0x000f, 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x2f44, 0x000a,
+ 0x2f44, 0x000a, 0x0e6b, 0x000b, 0x808a, 0x0008, 0x0003, 0x0008,
+ 0x8074, 0x0000, 0xf080, 0x0000, 0x8072, 0x0000, 0x3000, 0x0008,
+ 0x5b6c, 0x0003, 0x8054, 0x0008, 0x0019, 0x0000, 0x000a, 0x000b,
+ 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x808c, 0x0008, 0x0000, 0x0008,
+ 0x8010, 0x0008, 0x0011, 0x0008, 0x021b, 0x000c, 0x42fe, 0x0000,
+ 0xffc0, 0x0001, 0x00ff, 0x0008, 0x7f10, 0x0008, 0x021b, 0x000c,
+ 0x4310, 0x0008, 0x027a, 0x000b, 0x3941, 0x0002, 0x0b81, 0x0003,
+ 0x4000, 0x000f, 0x8072, 0x0000, 0x0404, 0x0008, 0x4000, 0x000f,
+ 0x8010, 0x0008, 0x0012, 0x0008, 0x021b, 0x000c, 0x0346, 0x000c,
+ 0x1110, 0x0000, 0x021b, 0x000c, 0x11fe, 0x0000, 0x3787, 0x0003,
+ 0x000a, 0x000b, 0xc2c0, 0x0009, 0x00ff, 0x0008, 0x7f00, 0x0000,
+ 0xc3c0, 0x0001, 0xff00, 0x0008, 0x00d0, 0x0009, 0x0bb2, 0x0003,
+ 0x0d0a, 0x0000, 0x8580, 0x0001, 0x1000, 0x0000, 0x7f62, 0x0008,
+ 0x8060, 0x0000, 0x0400, 0x0000, 0x8066, 0x0000, 0x0809, 0x0000,
+ 0x479c, 0x000b, 0x04fe, 0x0008, 0x33ab, 0x0003, 0x0460, 0x0000,
+ 0x8062, 0x0008, 0x0004, 0x0000, 0x8066, 0x0000, 0x0211, 0x0000,
+ 0x47a4, 0x0003, 0x01fe, 0x0008, 0x00e0, 0x0009, 0x0fab, 0x0003,
+ 0x02fe, 0x0008, 0x43e0, 0x0001, 0x0bb1, 0x0003, 0x0500, 0x0002,
+ 0x7f0a, 0x0000, 0xffe0, 0x0009, 0x0800, 0x0000, 0x0f95, 0x000b,
+ 0x0d08, 0x0008, 0x4000, 0x000f, 0x43fe, 0x0008, 0x3e80, 0x0001,
+ 0xffc0, 0x0001, 0x7fff, 0x0000, 0x0d60, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x0809, 0x0000, 0x47ba, 0x0003, 0x8060, 0x0000,
+ 0x0400, 0x0000, 0x84c0, 0x0001, 0xff00, 0x0008, 0x7f60, 0x000a,
+ 0x7f60, 0x000a, 0x7f60, 0x000a, 0x7f60, 0x000a, 0x7f60, 0x000a,
+ 0x7f60, 0x000a, 0x7f60, 0x000a, 0x7f60, 0x000a, 0xff80, 0x0009,
+ 0x1000, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000, 0x0809, 0x0000,
+ 0x47cc, 0x000b, 0x4000, 0x000f, 0x5ff4, 0xebed, 0x0001, 0x0002,
+ 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,
+ 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 0xa258
+};
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300ipx_length01 = 0xeb57;
+#else
+unsigned short risc_code_length01 = 0xeb57;
+#endif
+
diff --git a/drivers/scsi/qla2xxx/ql2322.c b/drivers/scsi/qla2xxx/ql2322.c
new file mode 100644
index 000000000000..70b6d75ed634
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2322.c
@@ -0,0 +1,108 @@
+/*
+ * QLogic ISP2322 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation (www.qlogic.com)
+ *
+ * Released under GPL v2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "qla_def.h"
+
+static char qla_driver_name[] = "qla2322";
+
+extern unsigned char fw2322ipx_version[];
+extern unsigned char fw2322ipx_version_str[];
+extern unsigned short fw2322ipx_addr01;
+extern unsigned short fw2322ipx_code01[];
+extern unsigned short fw2322ipx_length01;
+extern unsigned long rseqipx_code_addr01;
+extern unsigned short rseqipx_code01[];
+extern unsigned short rseqipx_code_length01;
+extern unsigned long xseqipx_code_addr01;
+extern unsigned short xseqipx_code01[];
+extern unsigned short xseqipx_code_length01;
+
+static struct qla_fw_info qla_fw_tbl[] = {
+ {
+ .addressing = FW_INFO_ADDR_NORMAL,
+ .fwcode = &fw2322ipx_code01[0],
+ .fwlen = &fw2322ipx_length01,
+ .fwstart = &fw2322ipx_addr01,
+ },
+ {
+ .addressing = FW_INFO_ADDR_EXTENDED,
+ .fwcode = &rseqipx_code01[0],
+ .fwlen = &rseqipx_code_length01,
+ .lfwstart = &rseqipx_code_addr01,
+ },
+ {
+ .addressing = FW_INFO_ADDR_EXTENDED,
+ .fwcode = &xseqipx_code01[0],
+ .fwlen = &xseqipx_code_length01,
+ .lfwstart = &xseqipx_code_addr01,
+ },
+ { FW_INFO_ADDR_NOMORE, },
+};
+
+static struct qla_board_info qla_board_tbl[] = {
+ {
+ .drv_name = qla_driver_name,
+ .isp_name = "ISP2322",
+ .fw_info = qla_fw_tbl,
+ },
+};
+
+static struct pci_device_id qla2322_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP2322,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl[0],
+ },
+ {0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla2322_pci_tbl);
+
+static int __devinit
+qla2322_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ return qla2x00_probe_one(pdev,
+ (struct qla_board_info *)id->driver_data);
+}
+
+static void __devexit
+qla2322_remove_one(struct pci_dev *pdev)
+{
+ qla2x00_remove_one(pdev);
+}
+
+static struct pci_driver qla2322_pci_driver = {
+ .name = "qla2322",
+ .id_table = qla2322_pci_tbl,
+ .probe = qla2322_probe_one,
+ .remove = __devexit_p(qla2322_remove_one),
+};
+
+static int __init
+qla2322_init(void)
+{
+ return pci_module_init(&qla2322_pci_driver);
+}
+
+static void __exit
+qla2322_exit(void)
+{
+ pci_unregister_driver(&qla2322_pci_driver);
+}
+
+module_init(qla2322_init);
+module_exit(qla2322_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic ISP2322 FC-SCSI Host Bus Adapter driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff --git a/drivers/scsi/qla2xxx/ql2322_fw.c b/drivers/scsi/qla2xxx/ql2322_fw.c
new file mode 100644
index 000000000000..d8c5a8dbd086
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql2322_fw.c
@@ -0,0 +1,8124 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+/*
+ * Firmware Version 3.03.08 (10:03 Nov 12, 2004)
+ */
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2322ipx_version = 3*1024+3;
+#else
+unsigned short risc_code_version = 3*1024+3;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned char fw2322ipx_version_str[] = {3, 3, 8};
+#else
+unsigned char firmware_version[] = {3, 3, 8};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw2322ipx_VERSION_STRING "3.03.08"
+#else
+#define FW_VERSION_STRING "3.03.08"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2322ipx_addr01 = 0x0800 ;
+#else
+unsigned short risc_code_addr01 = 0x0800 ;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2322ipx_code01[] = {
+#else
+unsigned short risc_code01[] = {
+#endif
+ 0x0470, 0x0000, 0x0000, 0xe0c2, 0x0000, 0x0003, 0x0003, 0x0008,
+ 0x0137, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2032, 0x3030,
+ 0x3120, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3233, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x332e, 0x3033, 0x2e30, 0x3820, 0x2020, 0x2020, 0x2400, 0x20a9,
+ 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2200, 0x20a9, 0x000f,
+ 0x2001, 0x0000, 0x400f, 0x2091, 0x2400, 0x20a9, 0x000f, 0x2001,
+ 0x0000, 0x400f, 0x2091, 0x2600, 0x20a9, 0x000f, 0x2001, 0x0000,
+ 0x400f, 0x2091, 0x2800, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f,
+ 0x2091, 0x2a00, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091,
+ 0x2c00, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2e00,
+ 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2000, 0x2001,
+ 0x0000, 0x20c1, 0x0004, 0x20c9, 0x1cff, 0x2059, 0x0000, 0x2b78,
+ 0x7883, 0x0004, 0x2089, 0x2b14, 0x2051, 0x1800, 0x2a70, 0x20e1,
+ 0x0001, 0x20e9, 0x0001, 0x2009, 0x0000, 0x080c, 0x0e62, 0x00f6,
+ 0x7888, 0x9005, 0x11f8, 0x2061, 0xc000, 0x080c, 0x20b0, 0x1170,
+ 0x2079, 0x0300, 0x080c, 0x20c6, 0x2061, 0xe000, 0x080c, 0x20b0,
+ 0x1128, 0x2079, 0x0380, 0x080c, 0x20c6, 0x0060, 0x00fe, 0x7883,
+ 0x4010, 0x7837, 0x4010, 0x7833, 0x0010, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x0cf8, 0x00fe, 0x2029, 0x5600, 0x2031, 0xffff, 0x2039,
+ 0x55dc, 0x2021, 0x0200, 0x20e9, 0x0001, 0x20a1, 0x0000, 0x20a9,
+ 0x0800, 0x900e, 0x4104, 0x20e9, 0x0001, 0x20a1, 0x1000, 0x900e,
+ 0x2001, 0x0dc1, 0x9084, 0x0fff, 0x20a8, 0x4104, 0x2001, 0x0000,
+ 0x9086, 0x0000, 0x0120, 0x21a8, 0x4104, 0x8001, 0x1de0, 0x756e,
+ 0x7672, 0x776a, 0x7476, 0x747a, 0x00e6, 0x2071, 0x1b50, 0x2472,
+ 0x00ee, 0x20a1, 0x1ddc, 0x7170, 0x810d, 0x810d, 0x810d, 0x810d,
+ 0x918c, 0x000f, 0x2001, 0x0001, 0x9112, 0x900e, 0x21a8, 0x4104,
+ 0x8211, 0x1de0, 0x7170, 0x3400, 0x8001, 0x9102, 0x0120, 0x0218,
+ 0x20a8, 0x900e, 0x4104, 0x2009, 0x1800, 0x810d, 0x810d, 0x810d,
+ 0x810d, 0x810d, 0x918c, 0x001f, 0x2001, 0x0001, 0x9112, 0x20e9,
+ 0x0001, 0x20a1, 0x0800, 0x900e, 0x20a9, 0x0800, 0x4104, 0x8211,
+ 0x1dd8, 0x080c, 0x0f5f, 0x080c, 0x60a0, 0x080c, 0xac46, 0x080c,
+ 0x1116, 0x080c, 0x1340, 0x080c, 0x1c06, 0x080c, 0x921f, 0x080c,
+ 0x0d0f, 0x080c, 0x109b, 0x080c, 0x34b9, 0x080c, 0x7854, 0x080c,
+ 0x6b01, 0x080c, 0x8992, 0x080c, 0x85f3, 0x080c, 0x22a1, 0x080c,
+ 0x7f2a, 0x080c, 0x20df, 0x080c, 0x221d, 0x080c, 0x2296, 0x2091,
+ 0x3009, 0x7883, 0x0000, 0x1004, 0x0943, 0x7880, 0x9086, 0x0002,
+ 0x1190, 0x7883, 0x4000, 0x7837, 0x4000, 0x7833, 0x0010, 0x0e04,
+ 0x0937, 0x2091, 0x5000, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004,
+ 0xd084, 0x190c, 0x11ee, 0x2071, 0x1800, 0x7003, 0x0000, 0x780c,
+ 0x9084, 0x0030, 0x9086, 0x0000, 0x190c, 0x0d7d, 0x2071, 0x1800,
+ 0x7000, 0x908e, 0x0003, 0x1168, 0x080c, 0x4c17, 0x080c, 0x34e0,
+ 0x080c, 0x78bc, 0x080c, 0x7039, 0x080c, 0x8a75, 0x080c, 0x861c,
+ 0x0c68, 0x000b, 0x0c88, 0x096d, 0x096e, 0x0b09, 0x096b, 0x0bc3,
+ 0x0d0e, 0x0d0e, 0x0d0e, 0x080c, 0x0d7d, 0x0005, 0x0126, 0x00f6,
+ 0x2091, 0x8000, 0x7000, 0x9086, 0x0001, 0x1904, 0x0adc, 0x080c,
+ 0x0eb2, 0x080c, 0x753d, 0x0150, 0x080c, 0x7560, 0x15b0, 0x2079,
+ 0x0100, 0x7828, 0x9085, 0x1800, 0x782a, 0x0478, 0x080c, 0x746e,
+ 0x7000, 0x9086, 0x0001, 0x1904, 0x0adc, 0x7098, 0x9086, 0x0028,
+ 0x1904, 0x0adc, 0x080c, 0x85dc, 0x080c, 0x85ce, 0x2001, 0x0161,
+ 0x2003, 0x0001, 0x2079, 0x0100, 0x2011, 0xffff, 0x080c, 0x2ab4,
+ 0x7a28, 0x9295, 0x5e2c, 0x7a2a, 0x2011, 0x73b3, 0x080c, 0x86c8,
+ 0x2011, 0x73a6, 0x080c, 0x87d4, 0x2011, 0x5ef7, 0x080c, 0x86c8,
+ 0x2011, 0x8030, 0x901e, 0x7396, 0x04d0, 0x080c, 0x57a4, 0x2079,
+ 0x0100, 0x7844, 0x9005, 0x1904, 0x0adc, 0x2011, 0x5ef7, 0x080c,
+ 0x86c8, 0x2011, 0x73b3, 0x080c, 0x86c8, 0x2011, 0x73a6, 0x080c,
+ 0x87d4, 0x2001, 0x0265, 0x2001, 0x0205, 0x2003, 0x0000, 0x7840,
+ 0x9084, 0xfffb, 0x7842, 0x2001, 0x19a5, 0x2004, 0x9005, 0x1140,
+ 0x00c6, 0x2061, 0x0100, 0x080c, 0x6048, 0x00ce, 0x0804, 0x0adc,
+ 0x780f, 0x006b, 0x7a28, 0x080c, 0x7545, 0x0118, 0x9295, 0x5e2c,
+ 0x0010, 0x9295, 0x402c, 0x7a2a, 0x2011, 0x8010, 0x73d8, 0x2001,
+ 0x19a6, 0x2003, 0x0001, 0x080c, 0x297c, 0x080c, 0x4b52, 0x7248,
+ 0xc284, 0x724a, 0x2001, 0x180c, 0x200c, 0xc1ac, 0xc1cc, 0x2102,
+ 0x2001, 0x0390, 0x2003, 0x0400, 0x080c, 0xa91e, 0x080c, 0xa113,
+ 0x2011, 0x0004, 0x080c, 0xc98a, 0x080c, 0xa93a, 0x080c, 0x6989,
+ 0x080c, 0x753d, 0x1120, 0x080c, 0x29dd, 0x0600, 0x0420, 0x080c,
+ 0x604f, 0x0140, 0x7097, 0x0001, 0x70d3, 0x0000, 0x080c, 0x5971,
+ 0x0804, 0x0adc, 0x080c, 0x573e, 0xd094, 0x01a8, 0x2001, 0x0390,
+ 0x2003, 0x0404, 0x2011, 0x180c, 0x2204, 0xc0cd, 0x2012, 0x080c,
+ 0x5742, 0xd0d4, 0x1118, 0x080c, 0x29dd, 0x1270, 0x2011, 0x180c,
+ 0x2204, 0xc0bc, 0x00a8, 0x080c, 0x5742, 0xd0d4, 0x1db8, 0x2011,
+ 0x180c, 0x2204, 0xc0bd, 0x0060, 0x2011, 0x180c, 0x2204, 0xc0bd,
+ 0x2012, 0x080c, 0x6ad5, 0x1128, 0xd0a4, 0x0118, 0x2204, 0xc0fd,
+ 0x2012, 0x080c, 0x6a9b, 0x0120, 0x7a0c, 0xc2b4, 0x7a0e, 0x00a8,
+ 0x707f, 0x0000, 0x080c, 0x753d, 0x1130, 0x70b0, 0x9005, 0x1168,
+ 0x080c, 0xcde8, 0x0050, 0x080c, 0xcde8, 0x70dc, 0xd09c, 0x1128,
+ 0x70b0, 0x9005, 0x0110, 0x080c, 0x6025, 0x70e7, 0x0000, 0x70e3,
+ 0x0000, 0x70a7, 0x0000, 0x080c, 0x29e5, 0x0228, 0x2011, 0x0101,
+ 0x2204, 0xc0c4, 0x2012, 0x72dc, 0x080c, 0x753d, 0x1178, 0x9016,
+ 0x0016, 0x080c, 0x2779, 0x2019, 0x196c, 0x211a, 0x001e, 0x705f,
+ 0xffff, 0x7063, 0x00ef, 0x7083, 0x0000, 0x0020, 0x2019, 0x196c,
+ 0x201b, 0x0000, 0x2079, 0x1847, 0x7804, 0xd0ac, 0x0108, 0xc295,
+ 0x72de, 0x080c, 0x753d, 0x0118, 0x9296, 0x0004, 0x0518, 0x2011,
+ 0x0001, 0x080c, 0xc98a, 0x70ab, 0x0000, 0x70af, 0xffff, 0x7003,
+ 0x0002, 0x00fe, 0x080c, 0x3011, 0x080c, 0xa91e, 0x2011, 0x0005,
+ 0x080c, 0xa243, 0x080c, 0xa93a, 0x080c, 0x753d, 0x0148, 0x00c6,
+ 0x2061, 0x0100, 0x0016, 0x080c, 0x2779, 0x61e2, 0x001e, 0x00ce,
+ 0x012e, 0x00e0, 0x70ab, 0x0000, 0x70af, 0xffff, 0x7003, 0x0002,
+ 0x080c, 0xa91e, 0x2011, 0x0005, 0x080c, 0xa243, 0x080c, 0xa93a,
+ 0x080c, 0x753d, 0x0148, 0x00c6, 0x2061, 0x0100, 0x0016, 0x080c,
+ 0x2779, 0x61e2, 0x001e, 0x00ce, 0x00fe, 0x012e, 0x0005, 0x00c6,
+ 0x00b6, 0x080c, 0x753d, 0x1118, 0x20a9, 0x0800, 0x0010, 0x20a9,
+ 0x0782, 0x080c, 0x753d, 0x1110, 0x900e, 0x0010, 0x2009, 0x007e,
+ 0x86ff, 0x0138, 0x9180, 0x1000, 0x2004, 0x905d, 0x0110, 0xb800,
+ 0xd0bc, 0x090c, 0x3349, 0x8108, 0x1f04, 0x0af0, 0x707f, 0x0000,
+ 0x7080, 0x9084, 0x00ff, 0x7082, 0x70b3, 0x0000, 0x00be, 0x00ce,
+ 0x0005, 0x00b6, 0x0126, 0x2091, 0x8000, 0x7000, 0x9086, 0x0002,
+ 0x1904, 0x0bc0, 0x70ac, 0x9086, 0xffff, 0x0120, 0x080c, 0x3011,
+ 0x0804, 0x0bc0, 0x70dc, 0xd0ac, 0x1110, 0xd09c, 0x0538, 0xd084,
+ 0x0528, 0x0006, 0x2001, 0x0103, 0x2003, 0x002b, 0x000e, 0xd08c,
+ 0x01e8, 0x080c, 0x33b2, 0x11b0, 0x70e0, 0x9086, 0xffff, 0x0190,
+ 0x080c, 0x31a6, 0x70dc, 0xd094, 0x1904, 0x0bc0, 0x2011, 0x0001,
+ 0x080c, 0xd09b, 0x0110, 0x2011, 0x0003, 0x901e, 0x080c, 0x31e0,
+ 0x0804, 0x0bc0, 0x70e4, 0x9005, 0x1904, 0x0bc0, 0x70a8, 0x9005,
+ 0x1904, 0x0bc0, 0x70dc, 0xd0a4, 0x0118, 0xd0b4, 0x0904, 0x0bc0,
+ 0x080c, 0x6a9b, 0x1904, 0x0bc0, 0x080c, 0x6aee, 0x1904, 0x0bc0,
+ 0x080c, 0x6ad5, 0x01c0, 0x0156, 0x00c6, 0x20a9, 0x007f, 0x900e,
+ 0x0016, 0x080c, 0x6693, 0x1118, 0xb800, 0xd0ec, 0x1138, 0x001e,
+ 0x8108, 0x1f04, 0x0b60, 0x00ce, 0x015e, 0x0028, 0x001e, 0x00ce,
+ 0x015e, 0x0804, 0x0bc0, 0x0006, 0x2001, 0x0103, 0x2003, 0x002b,
+ 0x000e, 0x2011, 0x19b2, 0x080c, 0x0fcf, 0x2011, 0x19cc, 0x080c,
+ 0x0fcf, 0x7030, 0xc08c, 0x7032, 0x7003, 0x0003, 0x70af, 0xffff,
+ 0x080c, 0x0e86, 0x9006, 0x080c, 0x2606, 0x080c, 0x33b2, 0x0118,
+ 0x080c, 0x4cef, 0x0050, 0x0036, 0x0046, 0x2019, 0xffff, 0x2021,
+ 0x0006, 0x080c, 0x4d09, 0x004e, 0x003e, 0x00f6, 0x2079, 0x0100,
+ 0x080c, 0x7560, 0x0150, 0x080c, 0x753d, 0x7828, 0x0118, 0x9084,
+ 0xe1ff, 0x0010, 0x9084, 0xffdf, 0x782a, 0x00fe, 0x080c, 0xa91e,
+ 0x2001, 0x19e7, 0x2004, 0x9086, 0x0005, 0x1120, 0x2011, 0x0000,
+ 0x080c, 0xa243, 0x2011, 0x0000, 0x080c, 0xa24d, 0x080c, 0xa93a,
+ 0x012e, 0x00be, 0x0005, 0x0016, 0x0026, 0x0046, 0x00f6, 0x0126,
+ 0x2091, 0x8000, 0x2079, 0x0100, 0x7904, 0x918c, 0xfffd, 0x7906,
+ 0x2009, 0x00f7, 0x080c, 0x600e, 0x7940, 0x918c, 0x0010, 0x7942,
+ 0x7924, 0xd1b4, 0x0120, 0x2011, 0x0040, 0x080c, 0x2ab4, 0xd19c,
+ 0x0120, 0x2011, 0x0008, 0x080c, 0x2ab4, 0x0006, 0x0036, 0x0156,
+ 0x0000, 0x2001, 0x19a6, 0x2004, 0x9005, 0x1518, 0x080c, 0x2a48,
+ 0x1148, 0x2001, 0x0001, 0x080c, 0x29ab, 0x2001, 0x0001, 0x080c,
+ 0x298e, 0x00b8, 0x080c, 0x2a50, 0x1138, 0x9006, 0x080c, 0x29ab,
+ 0x9006, 0x080c, 0x298e, 0x0068, 0x080c, 0x2a58, 0x1d50, 0x2001,
+ 0x1997, 0x2004, 0xd0fc, 0x0108, 0x0020, 0x080c, 0x27a5, 0x0804,
+ 0x0cc1, 0x080c, 0x2ad7, 0x080c, 0x2b0a, 0x20a9, 0x003a, 0x1d04,
+ 0x0c17, 0x080c, 0x87b4, 0x1f04, 0x0c17, 0x080c, 0x754e, 0x0148,
+ 0x080c, 0x7560, 0x1118, 0x080c, 0x784f, 0x0050, 0x080c, 0x7545,
+ 0x0dd0, 0x080c, 0x784a, 0x080c, 0x7840, 0x080c, 0x746e, 0x0020,
+ 0x2009, 0x00f8, 0x080c, 0x600e, 0x7850, 0xc0e5, 0x7852, 0x080c,
+ 0x753d, 0x0120, 0x7843, 0x0090, 0x7843, 0x0010, 0x2021, 0xe678,
+ 0x2019, 0xea60, 0x0d0c, 0x87b4, 0x7820, 0xd09c, 0x15a0, 0x080c,
+ 0x753d, 0x0904, 0x0ca3, 0x7824, 0xd0ac, 0x1904, 0x0cc6, 0x080c,
+ 0x7560, 0x1548, 0x0046, 0x2021, 0x0320, 0x8421, 0x1df0, 0x004e,
+ 0x2011, 0x1800, 0x080c, 0x2ab4, 0x080c, 0x2a60, 0x7824, 0x9084,
+ 0x1800, 0x1168, 0x9484, 0x0fff, 0x1140, 0x2001, 0x1810, 0x2004,
+ 0x9084, 0x9000, 0x0110, 0x080c, 0x0ce9, 0x8421, 0x1160, 0x1d04,
+ 0x0c73, 0x080c, 0x87b4, 0x080c, 0x784a, 0x080c, 0x7840, 0x7003,
+ 0x0001, 0x0804, 0x0cc6, 0x8319, 0x1928, 0x2001, 0x1810, 0x2004,
+ 0x9084, 0x9000, 0x0110, 0x080c, 0x0ce9, 0x1d04, 0x0c89, 0x080c,
+ 0x87b4, 0x2009, 0x199a, 0x2104, 0x9005, 0x0118, 0x8001, 0x200a,
+ 0x1188, 0x200b, 0x000a, 0x2011, 0x0048, 0x080c, 0x2ab4, 0x20a9,
+ 0x0002, 0x080c, 0x2a41, 0x7924, 0x080c, 0x2a60, 0xd19c, 0x0110,
+ 0x080c, 0x297c, 0x00f0, 0x080c, 0x754e, 0x1140, 0x94a2, 0x03e8,
+ 0x1128, 0x080c, 0x7511, 0x7003, 0x0001, 0x00c0, 0x2011, 0x1800,
+ 0x080c, 0x2ab4, 0x080c, 0x2a60, 0x7824, 0x080c, 0x7557, 0x0110,
+ 0xd0ac, 0x1160, 0x9084, 0x1800, 0x0904, 0x0c7b, 0x7003, 0x0001,
+ 0x0028, 0x2001, 0x0001, 0x080c, 0x2606, 0x00a0, 0x7850, 0xc0e4,
+ 0x7852, 0x2009, 0x180c, 0x210c, 0xd19c, 0x1120, 0x7904, 0x918d,
+ 0x0002, 0x7906, 0x2011, 0x0048, 0x080c, 0x2ab4, 0x7828, 0x9085,
+ 0x0028, 0x782a, 0x2001, 0x19a6, 0x2003, 0x0000, 0x9006, 0x78f2,
+ 0x015e, 0x003e, 0x000e, 0x012e, 0x00fe, 0x004e, 0x002e, 0x001e,
+ 0x0005, 0x0006, 0x0016, 0x0026, 0x0036, 0x0046, 0x00b6, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x0156, 0x0071, 0x0d0c, 0x87b4, 0x015e,
+ 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x004e, 0x003e, 0x002e,
+ 0x001e, 0x000e, 0x0005, 0x00e6, 0x2071, 0x189e, 0x7004, 0x9086,
+ 0x0001, 0x1110, 0x080c, 0x34e0, 0x00ee, 0x0005, 0x0005, 0x2a70,
+ 0x2061, 0x19aa, 0x2063, 0x0003, 0x6007, 0x0003, 0x600b, 0x0008,
+ 0x600f, 0x0137, 0x2001, 0x197b, 0x900e, 0x2102, 0x7196, 0x2001,
+ 0x0100, 0x2004, 0x9082, 0x0002, 0x0218, 0x705f, 0xffff, 0x0008,
+ 0x715e, 0x7067, 0xffff, 0x717e, 0x7182, 0x080c, 0xcde8, 0x70ef,
+ 0x00c0, 0x2061, 0x196b, 0x6003, 0x0909, 0x6106, 0x600b, 0x8800,
+ 0x600f, 0x0200, 0x6013, 0x00ff, 0x6017, 0x001f, 0x611a, 0x601f,
+ 0x07d0, 0x2061, 0x1973, 0x6003, 0x8000, 0x6106, 0x610a, 0x600f,
+ 0x0200, 0x6013, 0x00ff, 0x6116, 0x601b, 0x0001, 0x611e, 0x2061,
+ 0x1988, 0x6003, 0x514c, 0x6007, 0x4f47, 0x600b, 0x4943, 0x600f,
+ 0x2020, 0x2001, 0x182c, 0x2102, 0x0005, 0x9016, 0x080c, 0x6693,
+ 0x1178, 0xb804, 0x90c4, 0x00ff, 0x98c6, 0x0006, 0x0128, 0x90c4,
+ 0xff00, 0x98c6, 0x0600, 0x1120, 0x9186, 0x0080, 0x0108, 0x8210,
+ 0x8108, 0x9186, 0x0800, 0x1d50, 0x2208, 0x0005, 0x2091, 0x8000,
+ 0x2079, 0x0000, 0x000e, 0x00f6, 0x0010, 0x2091, 0x8000, 0x0e04,
+ 0x0d7f, 0x0006, 0x0016, 0x2001, 0x8002, 0x0006, 0x2079, 0x0000,
+ 0x000e, 0x7882, 0x7836, 0x001e, 0x798e, 0x000e, 0x788a, 0x000e,
+ 0x7886, 0x3900, 0x789a, 0x7833, 0x0012, 0x2091, 0x5000, 0x0156,
+ 0x00d6, 0x0036, 0x0026, 0x2079, 0x0300, 0x2069, 0x1b26, 0x7a08,
+ 0x226a, 0x2069, 0x1b27, 0x7a18, 0x226a, 0x8d68, 0x7a1c, 0x226a,
+ 0x782c, 0x2019, 0x1b34, 0x201a, 0x2019, 0x1b37, 0x9016, 0x7808,
+ 0xd09c, 0x0168, 0x7820, 0x201a, 0x8210, 0x8318, 0x9386, 0x1b50,
+ 0x0108, 0x0ca8, 0x7808, 0xd09c, 0x0110, 0x2011, 0xdead, 0x2019,
+ 0x1b35, 0x782c, 0x201a, 0x8318, 0x221a, 0x7803, 0x0000, 0x2069,
+ 0x1a7c, 0x901e, 0x20a9, 0x0020, 0x7b26, 0x7a28, 0x226a, 0x8d68,
+ 0x8318, 0x1f04, 0x0dcc, 0x2069, 0x1a9c, 0x2019, 0x0050, 0x20a9,
+ 0x0020, 0x7b26, 0x7a28, 0x226a, 0x8d68, 0x8318, 0x1f04, 0x0dd9,
+ 0x0491, 0x002e, 0x003e, 0x00de, 0x015e, 0x2079, 0x1800, 0x7803,
+ 0x0005, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x0180,
+ 0x2001, 0x1a21, 0x2004, 0x9005, 0x0128, 0x2001, 0x008b, 0x2004,
+ 0xd0fc, 0x0dd8, 0x2001, 0x008a, 0x2003, 0x0002, 0x2003, 0x1001,
+ 0x080c, 0x574d, 0x1170, 0x080c, 0x0f20, 0x0110, 0x080c, 0x0e73,
+ 0x080c, 0x574d, 0x1130, 0x2071, 0x1800, 0x2011, 0x8000, 0x080c,
+ 0x0f34, 0x0c70, 0x0005, 0x2001, 0x0382, 0x2004, 0x9084, 0x0007,
+ 0x9086, 0x0001, 0x1120, 0x2001, 0x0015, 0x080c, 0xa90f, 0x2079,
+ 0x0380, 0x2069, 0x1b06, 0x7818, 0x6802, 0x781c, 0x6806, 0x7840,
+ 0x680a, 0x7844, 0x680e, 0x782c, 0x6812, 0x2019, 0x1b11, 0x9016,
+ 0x7808, 0xd09c, 0x0150, 0x7820, 0x201a, 0x8210, 0x8318, 0x8210,
+ 0x9282, 0x0011, 0x0ea8, 0x2011, 0xdead, 0x6a2a, 0x7830, 0x681a,
+ 0x7834, 0x681e, 0x7838, 0x6822, 0x783c, 0x6826, 0x7803, 0x0000,
+ 0x2069, 0x1ac6, 0x901e, 0x20a9, 0x0020, 0x7b26, 0x7828, 0x206a,
+ 0x8d68, 0x8318, 0x1f04, 0x0e4d, 0x2069, 0x1ae6, 0x2019, 0x00b0,
+ 0x20a9, 0x0020, 0x7b26, 0x7828, 0x206a, 0x8d68, 0x8318, 0x1f04,
+ 0x0e5a, 0x0005, 0x918c, 0x03ff, 0x2001, 0x0003, 0x2004, 0x9084,
+ 0x0600, 0x1118, 0x918d, 0x6c00, 0x0010, 0x918d, 0x6400, 0x2001,
+ 0x017f, 0x2102, 0x0005, 0x0026, 0x0126, 0x2011, 0x0080, 0x080c,
+ 0x0f12, 0x20a9, 0x0900, 0x080c, 0x0f48, 0x2011, 0x0040, 0x080c,
+ 0x0f12, 0x20a9, 0x0900, 0x080c, 0x0f48, 0x0c78, 0x0026, 0x080c,
+ 0x0f20, 0x1188, 0x2011, 0x010e, 0x2214, 0x9294, 0x0007, 0x9296,
+ 0x0007, 0x0118, 0x2011, 0x0947, 0x0010, 0x2011, 0x1b47, 0x080c,
+ 0x0f34, 0x002e, 0x0005, 0x2011, 0x010e, 0x2214, 0x9294, 0x0007,
+ 0x9296, 0x0007, 0x0118, 0x2011, 0xa880, 0x0010, 0x2011, 0x6840,
+ 0xd0e4, 0x70f3, 0x0000, 0x1120, 0x70f3, 0x0fa0, 0x080c, 0x0f25,
+ 0x002e, 0x0005, 0x0026, 0x080c, 0x0f20, 0x0148, 0xd0a4, 0x1138,
+ 0x2011, 0xcdd5, 0x0010, 0x2011, 0x0080, 0x080c, 0x0f25, 0x002e,
+ 0x0005, 0x0026, 0x70f3, 0x0000, 0x080c, 0x0f20, 0x1130, 0x2011,
+ 0x8040, 0x080c, 0x0f34, 0x002e, 0x0005, 0x080c, 0x2a58, 0x1118,
+ 0x2011, 0xcdc5, 0x0010, 0x2011, 0xcac2, 0x080c, 0x0f25, 0x002e,
+ 0x0005, 0x00e6, 0x0016, 0x0006, 0x2071, 0x1800, 0xd0b4, 0x70ec,
+ 0x71e8, 0x1118, 0xc0e4, 0xc1f4, 0x0050, 0x0006, 0x3b00, 0x9084,
+ 0xff3e, 0x20d8, 0x000e, 0x70f3, 0x0000, 0xc0e5, 0xc1f5, 0x0099,
+ 0x000e, 0x001e, 0x00ee, 0x0005, 0x00e6, 0x2071, 0x1800, 0xd0e4,
+ 0x70ec, 0x1110, 0xc0dc, 0x0008, 0xc0dd, 0x0016, 0x71e8, 0x0019,
+ 0x001e, 0x00ee, 0x0005, 0x70ee, 0x71ea, 0x7000, 0x9084, 0x0007,
+ 0x000b, 0x0005, 0x0ed8, 0x0eb2, 0x0eb2, 0x0e86, 0x0ec1, 0x0eb2,
+ 0x0eb2, 0x0ec1, 0xc284, 0x0016, 0x3b08, 0x3a00, 0x9104, 0x918d,
+ 0x00c1, 0x21d8, 0x9084, 0xff3e, 0x9205, 0x20d0, 0x001e, 0x0005,
+ 0x2001, 0x183b, 0x2004, 0xd0dc, 0x0005, 0x9e86, 0x1800, 0x190c,
+ 0x0d7d, 0x70ec, 0xd0e4, 0x0108, 0xc2e5, 0x72ee, 0xd0e4, 0x1118,
+ 0x9294, 0x00c1, 0x08f9, 0x0005, 0x9e86, 0x1800, 0x190c, 0x0d7d,
+ 0x70e8, 0xd0f4, 0x0108, 0xc2f5, 0x72ea, 0xd0f4, 0x1140, 0x9284,
+ 0x8000, 0x8005, 0xc284, 0x9215, 0x9294, 0x00c1, 0x0861, 0x0005,
+ 0x1d04, 0x0f48, 0x2091, 0x6000, 0x1f04, 0x0f48, 0x0005, 0x890e,
+ 0x810e, 0x810f, 0x9194, 0x003f, 0x918c, 0xffc0, 0x0005, 0x0006,
+ 0x2200, 0x914d, 0x894f, 0x894d, 0x894d, 0x000e, 0x0005, 0x01d6,
+ 0x0146, 0x0036, 0x0096, 0x2061, 0x188d, 0x600b, 0x0000, 0x600f,
+ 0x0000, 0x6003, 0x0000, 0x6007, 0x0000, 0x2009, 0xffc0, 0x2105,
+ 0x0006, 0x2001, 0xaaaa, 0x200f, 0x2019, 0x5555, 0x9016, 0x2049,
+ 0x0bff, 0xab02, 0xa001, 0xa001, 0xa800, 0x9306, 0x1138, 0x2105,
+ 0x9306, 0x0120, 0x8210, 0x99c8, 0x0400, 0x0c98, 0x000e, 0x200f,
+ 0x2001, 0x189d, 0x928a, 0x000e, 0x1638, 0x928a, 0x0006, 0x2011,
+ 0x0006, 0x1210, 0x2011, 0x0000, 0x2202, 0x9006, 0x2008, 0x82ff,
+ 0x01b0, 0x8200, 0x600a, 0x600f, 0xffff, 0x6003, 0x0002, 0x6007,
+ 0x0000, 0x0026, 0x2019, 0x0010, 0x9280, 0x0001, 0x20e8, 0x21a0,
+ 0x21a8, 0x4104, 0x8319, 0x1de0, 0x8211, 0x1da0, 0x002e, 0x009e,
+ 0x003e, 0x014e, 0x01de, 0x0005, 0x2011, 0x000e, 0x08e8, 0x0016,
+ 0x0026, 0x0096, 0x3348, 0x080c, 0x0f4f, 0x2100, 0x9300, 0x2098,
+ 0x22e0, 0x009e, 0x002e, 0x001e, 0x0036, 0x3518, 0x20a9, 0x0001,
+ 0x4002, 0x8007, 0x4004, 0x8319, 0x1dd8, 0x003e, 0x0005, 0x20e9,
+ 0x0001, 0x71b8, 0x81ff, 0x11c0, 0x9006, 0x2009, 0x0200, 0x20a9,
+ 0x0002, 0x9298, 0x0018, 0x23a0, 0x4001, 0x2009, 0x0700, 0x20a9,
+ 0x0002, 0x9298, 0x0008, 0x23a0, 0x4001, 0x707c, 0x8007, 0x7180,
+ 0x810f, 0x20a9, 0x0002, 0x4001, 0x9298, 0x000c, 0x23a0, 0x900e,
+ 0x080c, 0x0d5d, 0x2001, 0x0000, 0x810f, 0x20a9, 0x0002, 0x4001,
+ 0x0005, 0x89ff, 0x0140, 0xa804, 0xa807, 0x0000, 0x0006, 0x080c,
+ 0x1079, 0x009e, 0x0cb0, 0x0005, 0x00e6, 0x2071, 0x1800, 0x080c,
+ 0x10f2, 0x090c, 0x0d7d, 0x00ee, 0x0005, 0x0086, 0x00e6, 0x0006,
+ 0x0026, 0x0036, 0x0126, 0x2091, 0x8000, 0x00c9, 0x2071, 0x1800,
+ 0x73c0, 0x702c, 0x9016, 0x9045, 0x0158, 0x8210, 0x9906, 0x090c,
+ 0x0d7d, 0x2300, 0x9202, 0x0120, 0x1a0c, 0x0d7d, 0xa000, 0x0c98,
+ 0x012e, 0x003e, 0x002e, 0x000e, 0x00ee, 0x008e, 0x0005, 0x0086,
+ 0x00e6, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x1910, 0x7010,
+ 0x9005, 0x0140, 0x7018, 0x9045, 0x0128, 0x9906, 0x090c, 0x0d7d,
+ 0xa000, 0x0cc8, 0x012e, 0x000e, 0x00ee, 0x008e, 0x0005, 0x00e6,
+ 0x2071, 0x1800, 0x0126, 0x2091, 0x8000, 0x70c0, 0x8001, 0x0270,
+ 0x70c2, 0x702c, 0x2048, 0x9085, 0x0001, 0xa800, 0x702e, 0xa803,
+ 0x0000, 0xa807, 0x0000, 0x012e, 0x00ee, 0x0005, 0x904e, 0x0cd8,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x1800, 0x70c0, 0x90ca,
+ 0x0020, 0x0268, 0x8001, 0x70c2, 0x702c, 0x2048, 0xa800, 0x702e,
+ 0xa803, 0x0000, 0xa807, 0x0000, 0x012e, 0x00ee, 0x0005, 0x904e,
+ 0x0cd8, 0x00e6, 0x0126, 0x2091, 0x8000, 0x0016, 0x890e, 0x810e,
+ 0x810f, 0x9184, 0x003f, 0xa862, 0x9184, 0xffc0, 0xa85e, 0x001e,
+ 0x0020, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x1800, 0x702c,
+ 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c, 0x85ce,
+ 0x012e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9026, 0x2009, 0x0000,
+ 0x2049, 0x0400, 0x2900, 0x702e, 0x8940, 0x2800, 0xa802, 0xa95e,
+ 0xa863, 0x0001, 0x8420, 0x9886, 0x0440, 0x0120, 0x2848, 0x9188,
+ 0x0040, 0x0c90, 0x2071, 0x188d, 0x7000, 0x9005, 0x11a0, 0x2001,
+ 0x0558, 0xa802, 0x2048, 0x2009, 0x5600, 0x8940, 0x2800, 0xa802,
+ 0xa95e, 0xa863, 0x0001, 0x8420, 0x9886, 0x0800, 0x0120, 0x2848,
+ 0x9188, 0x0040, 0x0c90, 0x2071, 0x188d, 0x7104, 0x7200, 0x82ff,
+ 0x01d0, 0x7308, 0x8318, 0x831f, 0x831b, 0x831b, 0x7312, 0x8319,
+ 0x2001, 0x0800, 0xa802, 0x2048, 0x8900, 0xa802, 0x2040, 0xa95e,
+ 0xaa62, 0x8420, 0x2300, 0x9906, 0x0130, 0x2848, 0x9188, 0x0040,
+ 0x9291, 0x0000, 0x0c88, 0xa803, 0x0000, 0x2071, 0x1800, 0x74be,
+ 0x74c2, 0x0005, 0x00e6, 0x0016, 0x9984, 0xfc00, 0x01e8, 0x908c,
+ 0xf800, 0x1168, 0x9982, 0x0400, 0x02b8, 0x9982, 0x0440, 0x0278,
+ 0x9982, 0x0558, 0x0288, 0x9982, 0x0800, 0x1270, 0x0040, 0x9982,
+ 0x0800, 0x0250, 0x2071, 0x188d, 0x7010, 0x9902, 0x1228, 0x9085,
+ 0x0001, 0x001e, 0x00ee, 0x0005, 0x9006, 0x0cd8, 0x00e6, 0x2071,
+ 0x1a20, 0x7007, 0x0000, 0x9006, 0x701e, 0x7022, 0x7002, 0x2071,
+ 0x0000, 0x7010, 0x9085, 0x8044, 0x7012, 0x2071, 0x0080, 0x9006,
+ 0x702b, 0x0060, 0x20a9, 0x0040, 0x7022, 0x1f04, 0x112c, 0x702b,
+ 0x0060, 0x702b, 0x0020, 0x20a9, 0x0040, 0x7022, 0x1f04, 0x1135,
+ 0x702b, 0x0020, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000, 0x00e6,
+ 0xa06f, 0x0000, 0x2071, 0x1a20, 0x701c, 0x9088, 0x1a2a, 0x280a,
+ 0x8000, 0x9084, 0x003f, 0x701e, 0x7120, 0x9106, 0x090c, 0x0d7d,
+ 0x7004, 0x9005, 0x1128, 0x00f6, 0x2079, 0x0080, 0x00a9, 0x00fe,
+ 0x00ee, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x00e6, 0x2071,
+ 0x1a20, 0x7004, 0x9005, 0x1128, 0x00f6, 0x2079, 0x0080, 0x0021,
+ 0x00fe, 0x00ee, 0x012e, 0x0005, 0x7004, 0x9086, 0x0000, 0x1110,
+ 0x7007, 0x0006, 0x7000, 0x0002, 0x117e, 0x1301, 0x117c, 0x117c,
+ 0x12f5, 0x12f5, 0x12f5, 0x12f5, 0x080c, 0x0d7d, 0x701c, 0x7120,
+ 0x9106, 0x1148, 0x792c, 0x9184, 0x0001, 0x1120, 0xd1fc, 0x1110,
+ 0x7007, 0x0000, 0x0005, 0x0096, 0x9180, 0x1a2a, 0x2004, 0x700a,
+ 0x2048, 0x8108, 0x918c, 0x003f, 0x7122, 0x782b, 0x0026, 0xa88c,
+ 0x7802, 0xa890, 0x7806, 0xa894, 0x780a, 0xa898, 0x780e, 0xa878,
+ 0x700e, 0xa870, 0x7016, 0xa874, 0x701a, 0xa868, 0x009e, 0xd084,
+ 0x0120, 0x7007, 0x0001, 0x0029, 0x0005, 0x7007, 0x0002, 0x00b1,
+ 0x0005, 0x0016, 0x0026, 0x710c, 0x2011, 0x0040, 0x9182, 0x0040,
+ 0x1210, 0x2110, 0x9006, 0x700e, 0x7212, 0x8203, 0x7812, 0x782b,
+ 0x0020, 0x782b, 0x0041, 0x002e, 0x001e, 0x0005, 0x0016, 0x0026,
+ 0x0136, 0x0146, 0x0156, 0x7014, 0x20e0, 0x7018, 0x2098, 0x20e9,
+ 0x0000, 0x20a1, 0x0088, 0x782b, 0x0026, 0x710c, 0x2011, 0x0040,
+ 0x9182, 0x0040, 0x1210, 0x2110, 0x9006, 0x700e, 0x22a8, 0x4006,
+ 0x8203, 0x7812, 0x782b, 0x0020, 0x3300, 0x701a, 0x782b, 0x0001,
+ 0x015e, 0x014e, 0x013e, 0x002e, 0x001e, 0x0005, 0x2009, 0x1a20,
+ 0x2104, 0xc095, 0x200a, 0x080c, 0x115b, 0x0005, 0x0016, 0x00e6,
+ 0x2071, 0x1a20, 0x00f6, 0x2079, 0x0080, 0x792c, 0xd1bc, 0x190c,
+ 0x0d76, 0x782b, 0x0002, 0xd1fc, 0x0120, 0x918c, 0x0700, 0x7004,
+ 0x0023, 0x00fe, 0x00ee, 0x001e, 0x0005, 0x116c, 0x1214, 0x1248,
+ 0x1320, 0x0d7d, 0x133b, 0x0d7d, 0x918c, 0x0700, 0x1550, 0x0136,
+ 0x0146, 0x0156, 0x7014, 0x20e8, 0x7018, 0x20a0, 0x20e1, 0x0000,
+ 0x2099, 0x0088, 0x782b, 0x0040, 0x7010, 0x20a8, 0x4005, 0x3400,
+ 0x701a, 0x015e, 0x014e, 0x013e, 0x700c, 0x9005, 0x0578, 0x7800,
+ 0x7802, 0x7804, 0x7806, 0x080c, 0x11b1, 0x0005, 0x7008, 0x0096,
+ 0x2048, 0xa86f, 0x0100, 0x009e, 0x7007, 0x0000, 0x080c, 0x116c,
+ 0x0005, 0x7008, 0x0096, 0x2048, 0xa86f, 0x0200, 0x009e, 0x0ca0,
+ 0x918c, 0x0700, 0x1150, 0x700c, 0x9005, 0x0180, 0x7800, 0x7802,
+ 0x7804, 0x7806, 0x080c, 0x11c6, 0x0005, 0x7008, 0x0096, 0x2048,
+ 0xa86f, 0x0200, 0x009e, 0x7007, 0x0000, 0x0080, 0x0096, 0x7008,
+ 0x2048, 0x7800, 0xa88e, 0x7804, 0xa892, 0x7808, 0xa896, 0x780c,
+ 0xa89a, 0xa86f, 0x0100, 0x009e, 0x7007, 0x0000, 0x0096, 0x00d6,
+ 0x7008, 0x2048, 0x2001, 0x18b9, 0x2004, 0x9906, 0x1128, 0xa89c,
+ 0x080f, 0x00de, 0x009e, 0x00a0, 0x00de, 0x009e, 0x0096, 0x00d6,
+ 0x7008, 0x2048, 0x0081, 0x0150, 0xa89c, 0x0086, 0x2940, 0x080f,
+ 0x008e, 0x00de, 0x009e, 0x080c, 0x115b, 0x0005, 0x00de, 0x009e,
+ 0x080c, 0x115b, 0x0005, 0xa8a8, 0xd08c, 0x0005, 0x0096, 0xa0a0,
+ 0x904d, 0x090c, 0x0d7d, 0xa06c, 0x908e, 0x0100, 0x0130, 0xa87b,
+ 0x0030, 0xa883, 0x0000, 0xa897, 0x4002, 0x080c, 0x6de2, 0xa09f,
+ 0x0000, 0xa0a3, 0x0000, 0x2848, 0x080c, 0x1079, 0x009e, 0x0005,
+ 0x00a6, 0xa0a0, 0x904d, 0x090c, 0x0d7d, 0xa06c, 0x908e, 0x0100,
+ 0x0128, 0xa87b, 0x0001, 0xa883, 0x0000, 0x00c0, 0xa80c, 0x2050,
+ 0xb004, 0x9005, 0x0198, 0xa80e, 0x2050, 0x8006, 0x8006, 0x8007,
+ 0x908c, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002, 0xa076, 0xa172,
+ 0xb000, 0xa07a, 0x2810, 0x080c, 0x113c, 0x00e8, 0xa97c, 0xa894,
+ 0x0016, 0x0006, 0x080c, 0x6de2, 0x000e, 0x001e, 0xd1fc, 0x1138,
+ 0xd1f4, 0x0128, 0x00c6, 0x2060, 0x080c, 0xacb0, 0x00ce, 0x7008,
+ 0x2048, 0xa89f, 0x0000, 0xa8a3, 0x0000, 0x080c, 0x1079, 0x7007,
+ 0x0000, 0x080c, 0x115b, 0x00ae, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x782b, 0x1001, 0x7007, 0x0005, 0x7000, 0xc094, 0x7002, 0x012e,
+ 0x0005, 0x0096, 0x2001, 0x192e, 0x204c, 0xa87c, 0x7812, 0xa88c,
+ 0x7802, 0xa890, 0x7806, 0xa894, 0x780a, 0xa898, 0x780e, 0x782b,
+ 0x0020, 0x0126, 0x2091, 0x8000, 0x782b, 0x0041, 0x7007, 0x0003,
+ 0x7000, 0xc084, 0x7002, 0x2900, 0x700a, 0x012e, 0x009e, 0x0005,
+ 0x20e1, 0x0000, 0x2099, 0x0088, 0x782b, 0x0040, 0x0096, 0x2001,
+ 0x192e, 0x204c, 0xaa7c, 0x009e, 0x080c, 0x8cb2, 0x2009, 0x188c,
+ 0x2104, 0x9084, 0xfffc, 0x200a, 0x080c, 0x8b18, 0x7007, 0x0000,
+ 0x080c, 0x116c, 0x0005, 0x7007, 0x0000, 0x080c, 0x116c, 0x0005,
+ 0x0126, 0x2091, 0x2200, 0x2079, 0x0300, 0x2071, 0x1a6a, 0x7003,
+ 0x0000, 0x78bf, 0x00f6, 0x0041, 0x7807, 0x0007, 0x7803, 0x0000,
+ 0x7803, 0x0001, 0x012e, 0x0005, 0x00c6, 0x7803, 0x0000, 0x2001,
+ 0x0165, 0x2003, 0x4198, 0x7808, 0xd09c, 0x0118, 0x7820, 0x04e9,
+ 0x0cd0, 0x2001, 0x1a6b, 0x2003, 0x0000, 0x78ab, 0x0004, 0x78ac,
+ 0xd0ac, 0x1de8, 0x78ab, 0x0002, 0x7807, 0x0007, 0x7827, 0x0030,
+ 0x782b, 0x0400, 0x7827, 0x0031, 0x782b, 0x1a7c, 0x781f, 0xff00,
+ 0x781b, 0xff00, 0x2001, 0x0200, 0x2004, 0xd0dc, 0x0110, 0x781f,
+ 0x0303, 0x2061, 0x1a7c, 0x602f, 0x1ddc, 0x2001, 0x181a, 0x2004,
+ 0x9082, 0x1ddc, 0x6032, 0x603b, 0x1eab, 0x602b, 0x1abc, 0x6007,
+ 0x1a9c, 0x2061, 0x1a9c, 0x606f, 0x193c, 0x2001, 0x1927, 0x2004,
+ 0x607a, 0x783f, 0x33b9, 0x00ce, 0x0005, 0x9086, 0x000d, 0x11d0,
+ 0x7808, 0xd09c, 0x01b8, 0x7820, 0x0026, 0x2010, 0x080c, 0xc968,
+ 0x0180, 0x2260, 0x6000, 0x9086, 0x0004, 0x1158, 0x0016, 0x6120,
+ 0x9186, 0x0009, 0x0108, 0x0020, 0x2009, 0x004c, 0x080c, 0xad4d,
+ 0x001e, 0x002e, 0x0005, 0x0126, 0x2091, 0x2200, 0x7908, 0x9184,
+ 0x0070, 0x190c, 0x0d76, 0xd19c, 0x05a0, 0x7820, 0x908c, 0xf000,
+ 0x0540, 0x2060, 0x6020, 0x9086, 0x0003, 0x1550, 0x6000, 0x9086,
+ 0x0004, 0x1530, 0x6114, 0x2148, 0xa876, 0xa87a, 0xa867, 0x0103,
+ 0x080c, 0x6c04, 0x00b6, 0x6010, 0x2058, 0xba3c, 0x8211, 0x0208,
+ 0xba3e, 0xb8d0, 0x9005, 0x190c, 0x67be, 0x00be, 0x6044, 0xd0fc,
+ 0x190c, 0xa947, 0x080c, 0xacd9, 0x7808, 0xd09c, 0x19b0, 0x012e,
+ 0x0005, 0x908a, 0x0024, 0x1a0c, 0x0d7d, 0x002b, 0x012e, 0x0005,
+ 0x04b0, 0x012e, 0x0005, 0x141f, 0x1445, 0x1475, 0x147a, 0x147e,
+ 0x1483, 0x14ab, 0x14af, 0x14bd, 0x14c1, 0x141f, 0x158e, 0x1592,
+ 0x1604, 0x160b, 0x141f, 0x160c, 0x160d, 0x1618, 0x161f, 0x141f,
+ 0x141f, 0x141f, 0x141f, 0x141f, 0x141f, 0x141f, 0x1485, 0x141f,
+ 0x144d, 0x1472, 0x1439, 0x141f, 0x1459, 0x1423, 0x1421, 0x080c,
+ 0x0d7d, 0x080c, 0x0d76, 0x080c, 0x162a, 0x2009, 0x1a78, 0x2104,
+ 0x8000, 0x200a, 0x080c, 0x7fed, 0x080c, 0x1b10, 0x0005, 0x6044,
+ 0xd0fc, 0x190c, 0xa947, 0x2009, 0x0055, 0x080c, 0xad4d, 0x012e,
+ 0x0005, 0x080c, 0x162a, 0x2060, 0x6044, 0xd0fc, 0x190c, 0xa947,
+ 0x2009, 0x0055, 0x080c, 0xad4d, 0x0005, 0x2009, 0x0048, 0x080c,
+ 0x162a, 0x2060, 0x080c, 0xad4d, 0x0005, 0x2009, 0x0054, 0x080c,
+ 0x162a, 0x2060, 0x6044, 0xd0fc, 0x190c, 0xa947, 0x080c, 0xad4d,
+ 0x0005, 0x080c, 0x162a, 0x2060, 0x0056, 0x0066, 0x080c, 0x162a,
+ 0x2028, 0x080c, 0x162a, 0x2030, 0x0036, 0x0046, 0x2021, 0x0000,
+ 0x2418, 0x2009, 0x0056, 0x080c, 0xad4d, 0x004e, 0x003e, 0x006e,
+ 0x005e, 0x0005, 0x080c, 0x162a, 0x0005, 0x7004, 0xc085, 0xc0b5,
+ 0x7006, 0x0005, 0x7004, 0xc085, 0x7006, 0x0005, 0x080c, 0x162a,
+ 0x080c, 0x170b, 0x0005, 0x080c, 0x0d7d, 0x080c, 0x162a, 0x2060,
+ 0x6014, 0x0096, 0x2048, 0xa83b, 0xffff, 0x009e, 0x2009, 0x0048,
+ 0x080c, 0xad4d, 0x2001, 0x015d, 0x2003, 0x0000, 0x2009, 0x03e8,
+ 0x8109, 0x0160, 0x2001, 0x0201, 0x2004, 0x9005, 0x0dc8, 0x2001,
+ 0x0218, 0x2004, 0xd0ec, 0x1110, 0x080c, 0x162f, 0x2001, 0x0307,
+ 0x2003, 0x8000, 0x0005, 0x7004, 0xc095, 0x7006, 0x0005, 0x080c,
+ 0x162a, 0x2060, 0x6014, 0x0096, 0x2048, 0xa83b, 0xffff, 0x009e,
+ 0x2009, 0x0048, 0x080c, 0xad4d, 0x0005, 0x080c, 0x162a, 0x080c,
+ 0x0d7d, 0x080c, 0x162a, 0x080c, 0x1579, 0x7827, 0x0018, 0x79ac,
+ 0xd1dc, 0x0904, 0x152a, 0x7827, 0x0015, 0x7828, 0x782b, 0x0000,
+ 0x9065, 0x0140, 0x2001, 0x020d, 0x2003, 0x0050, 0x2003, 0x0020,
+ 0x0804, 0x1530, 0x7004, 0x9005, 0x01c8, 0x1188, 0x78ab, 0x0004,
+ 0x7827, 0x0018, 0x782b, 0x0000, 0xd1bc, 0x090c, 0x0d7d, 0x2001,
+ 0x020d, 0x2003, 0x0050, 0x2003, 0x0020, 0x0804, 0x155e, 0x78ab,
+ 0x0004, 0x7803, 0x0001, 0x080c, 0x1592, 0x0005, 0x7827, 0x0018,
+ 0xa001, 0x7828, 0x7827, 0x0011, 0xa001, 0x7928, 0x9106, 0x0110,
+ 0x79ac, 0x08e0, 0x00e6, 0x2071, 0x0200, 0x702c, 0xd0c4, 0x0140,
+ 0x00ee, 0x080c, 0x1b10, 0x080c, 0x1354, 0x7803, 0x0001, 0x0005,
+ 0x7037, 0x0001, 0xa001, 0x7150, 0x00ee, 0x918c, 0xff00, 0x9186,
+ 0x0500, 0x0110, 0x79ac, 0x0810, 0x7004, 0xc09d, 0x7006, 0x78ab,
+ 0x0004, 0x7803, 0x0001, 0x080c, 0x1592, 0x2001, 0x020d, 0x2003,
+ 0x0020, 0x0005, 0x7828, 0x782b, 0x0000, 0x9065, 0x090c, 0x0d7d,
+ 0x6014, 0x2048, 0x78ab, 0x0004, 0x918c, 0x0700, 0x01a8, 0x080c,
+ 0x7fed, 0x080c, 0x1b10, 0x080c, 0xc97a, 0x0158, 0xa9ac, 0xa936,
+ 0xa9b0, 0xa93a, 0xa83f, 0xffff, 0xa843, 0xffff, 0xa880, 0xc0bd,
+ 0xa882, 0x080c, 0xc566, 0x0005, 0x6020, 0x9086, 0x0009, 0x1128,
+ 0x2009, 0x004c, 0x080c, 0xad4d, 0x0048, 0x6010, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0bc, 0x6024, 0x190c, 0xcd7d, 0x2029, 0x00c8,
+ 0x8529, 0x0128, 0x2001, 0x0201, 0x2004, 0x9005, 0x0dc8, 0x7dbc,
+ 0x080c, 0xe85a, 0xd5a4, 0x1118, 0x080c, 0x162f, 0x0005, 0x080c,
+ 0x7fed, 0x080c, 0x1b10, 0x0005, 0x781f, 0x0300, 0x7803, 0x0001,
+ 0x0005, 0x0016, 0x0066, 0x0076, 0x00f6, 0x2079, 0x0300, 0x7908,
+ 0x918c, 0x0007, 0x9186, 0x0003, 0x0120, 0x2001, 0x0016, 0x080c,
+ 0x16a0, 0x00fe, 0x007e, 0x006e, 0x001e, 0x0005, 0x7004, 0xc09d,
+ 0x7006, 0x0005, 0x7104, 0x9184, 0x0004, 0x190c, 0x0d7d, 0xd184,
+ 0x11b1, 0xd19c, 0x0180, 0xc19c, 0x7106, 0x0016, 0x080c, 0x16ee,
+ 0x001e, 0x0148, 0x2001, 0x020d, 0x2003, 0x0050, 0x2003, 0x0020,
+ 0x080c, 0x162f, 0x0005, 0x81ff, 0x190c, 0x0d7d, 0x0005, 0x2100,
+ 0xc184, 0xc1b4, 0x7106, 0xd0b4, 0x0016, 0x00e6, 0x1904, 0x15f9,
+ 0x2071, 0x0200, 0x080c, 0x16db, 0x05e0, 0x080c, 0x16ee, 0x05b0,
+ 0x6014, 0x9005, 0x05b0, 0x0096, 0x2048, 0xa864, 0x009e, 0x9084,
+ 0x00ff, 0x908e, 0x0029, 0x0160, 0x908e, 0x0048, 0x1550, 0x601c,
+ 0xd084, 0x11e0, 0x00f6, 0x2c78, 0x080c, 0x1778, 0x00fe, 0x00b0,
+ 0x00f6, 0x2c78, 0x080c, 0x1901, 0x00fe, 0x2009, 0x01f4, 0x8109,
+ 0x0168, 0x2001, 0x0201, 0x2004, 0x9005, 0x0dc8, 0x2001, 0x0218,
+ 0x2004, 0xd0ec, 0x1118, 0x080c, 0x162f, 0x0040, 0x2001, 0x020d,
+ 0x2003, 0x0020, 0x080c, 0x1354, 0x7803, 0x0001, 0x00ee, 0x001e,
+ 0x0005, 0x080c, 0x16ee, 0x0dd0, 0x2001, 0x020d, 0x2003, 0x0050,
+ 0x2003, 0x0020, 0x0461, 0x0c90, 0x0429, 0x2060, 0x2009, 0x0053,
+ 0x080c, 0xad4d, 0x0005, 0x0005, 0x0005, 0x00e1, 0x2008, 0x00d1,
+ 0x0006, 0x7004, 0xc09d, 0x7006, 0x000e, 0x080c, 0x9003, 0x0005,
+ 0x0089, 0x9005, 0x0118, 0x080c, 0x8c0a, 0x0cd0, 0x0005, 0x2001,
+ 0x0036, 0x2009, 0x1820, 0x210c, 0x2011, 0x181f, 0x2214, 0x080c,
+ 0x16a0, 0x0005, 0x7808, 0xd09c, 0x0de8, 0x7820, 0x0005, 0x080c,
+ 0x1579, 0x00d6, 0x2069, 0x0200, 0x2009, 0x01f4, 0x8109, 0x0510,
+ 0x6804, 0x9005, 0x0dd8, 0x2001, 0x015d, 0x2003, 0x0000, 0x79bc,
+ 0xd1a4, 0x1528, 0x79b8, 0x918c, 0x0fff, 0x0180, 0x9182, 0x0841,
+ 0x1268, 0x9188, 0x0007, 0x918c, 0x0ff8, 0x810c, 0x810c, 0x810c,
+ 0x080c, 0x1692, 0x6827, 0x0001, 0x8109, 0x1dd0, 0x04d9, 0x6827,
+ 0x0002, 0x04c1, 0x6804, 0x9005, 0x1130, 0x682c, 0xd0e4, 0x1500,
+ 0x6804, 0x9005, 0x0de8, 0x79b8, 0xd1ec, 0x1130, 0x08c0, 0x080c,
+ 0x7fed, 0x080c, 0x1b10, 0x0090, 0x7827, 0x0015, 0x782b, 0x0000,
+ 0x7827, 0x0018, 0x782b, 0x0000, 0x2001, 0x020d, 0x2003, 0x0020,
+ 0x2001, 0x0307, 0x2003, 0x0300, 0x7803, 0x0001, 0x00de, 0x0005,
+ 0x682c, 0x9084, 0x5400, 0x9086, 0x5400, 0x0d30, 0x7827, 0x0015,
+ 0x782b, 0x0000, 0x7803, 0x0001, 0x6800, 0x9085, 0x1800, 0x6802,
+ 0x00de, 0x0005, 0x6824, 0x9084, 0x0003, 0x1de0, 0x0005, 0x2001,
+ 0x0030, 0x2c08, 0x621c, 0x0021, 0x7830, 0x9086, 0x0041, 0x0005,
+ 0x00f6, 0x2079, 0x0300, 0x0006, 0x7808, 0xd09c, 0x0140, 0x0016,
+ 0x0026, 0x00c6, 0x080c, 0x13bb, 0x00ce, 0x002e, 0x001e, 0x000e,
+ 0x0006, 0x7832, 0x7936, 0x7a3a, 0x781b, 0x8080, 0x0059, 0x1118,
+ 0x000e, 0x00fe, 0x0005, 0x000e, 0x792c, 0x3900, 0x8000, 0x2004,
+ 0x080c, 0x0d7d, 0x2009, 0xff00, 0x8109, 0x0120, 0x7818, 0xd0bc,
+ 0x1dd8, 0x0005, 0x9085, 0x0001, 0x0005, 0x7832, 0x7936, 0x7a3a,
+ 0x781b, 0x8080, 0x0c79, 0x1108, 0x0005, 0x792c, 0x3900, 0x8000,
+ 0x2004, 0x080c, 0x0d7d, 0x7037, 0x0001, 0x7150, 0x7037, 0x0002,
+ 0x7050, 0x2060, 0xd1bc, 0x1110, 0x7054, 0x2060, 0x918c, 0xff00,
+ 0x9186, 0x0500, 0x0110, 0x9085, 0x0001, 0x0005, 0x0006, 0x0046,
+ 0x00e6, 0x2071, 0x0200, 0x7037, 0x0002, 0x7058, 0x9084, 0xff00,
+ 0x8007, 0x9086, 0x00bc, 0x1158, 0x2021, 0x1a79, 0x2404, 0x8000,
+ 0x0208, 0x2022, 0x080c, 0x7fed, 0x080c, 0x1b10, 0x9006, 0x00ee,
+ 0x004e, 0x000e, 0x0005, 0x0c11, 0x1108, 0x0005, 0x00e6, 0x0016,
+ 0x2071, 0x0200, 0x0841, 0x6124, 0xd1dc, 0x01f8, 0x701c, 0xd08c,
+ 0x0904, 0x176d, 0x7017, 0x0000, 0x2001, 0x0264, 0x2004, 0xd0bc,
+ 0x0904, 0x176d, 0x2001, 0x0268, 0x00c6, 0x2064, 0x6104, 0x6038,
+ 0x00ce, 0x918e, 0x0039, 0x1904, 0x176d, 0x9c06, 0x15f0, 0x0126,
+ 0x2091, 0x2600, 0x080c, 0x7f45, 0x012e, 0x7358, 0x745c, 0x6014,
+ 0x905d, 0x0598, 0x2b48, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be,
+ 0xd0bc, 0x190c, 0xcd58, 0xab42, 0xac3e, 0x2001, 0x1869, 0x2004,
+ 0xd0b4, 0x1170, 0x601c, 0xd0e4, 0x1158, 0x6010, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0bc, 0x1120, 0xa83b, 0x7fff, 0xa837, 0xffff,
+ 0x080c, 0x1ecb, 0x1190, 0x080c, 0x195e, 0x2a00, 0xa816, 0x0130,
+ 0x2800, 0xa80e, 0x2c05, 0xa80a, 0x2c00, 0xa812, 0x7037, 0x0020,
+ 0x781f, 0x0300, 0x001e, 0x00ee, 0x0005, 0x7037, 0x0050, 0x7037,
+ 0x0020, 0x001e, 0x00ee, 0x080c, 0x162f, 0x0005, 0x080c, 0x0d7d,
+ 0x2cf0, 0x0126, 0x2091, 0x2200, 0x0016, 0x00c6, 0x3e60, 0x6014,
+ 0x2048, 0x2940, 0x903e, 0x2730, 0xa864, 0x2068, 0xa81a, 0x9d84,
+ 0x000f, 0x9088, 0x1eab, 0x2165, 0x0002, 0x17a4, 0x1812, 0x17a4,
+ 0x17a4, 0x17a8, 0x17f3, 0x17a4, 0x17c8, 0x179d, 0x1809, 0x17a4,
+ 0x17a4, 0x17ad, 0x18ff, 0x17dc, 0x17d2, 0xa964, 0x918c, 0x00ff,
+ 0x918e, 0x0048, 0x0904, 0x1809, 0x9085, 0x0001, 0x0804, 0x18f5,
+ 0xa87c, 0xd0ac, 0x0dc8, 0x0804, 0x1819, 0xa87c, 0xd0ac, 0x0da0,
+ 0x0804, 0x1884, 0xa898, 0x901d, 0x1108, 0xab9c, 0x9016, 0xaab2,
+ 0xaa3e, 0xaa42, 0x3e00, 0x9080, 0x0008, 0x2004, 0x9080, 0x91d3,
+ 0x2005, 0x9005, 0x090c, 0x0d7d, 0x2004, 0xa8ae, 0x0804, 0x18dd,
+ 0xa87c, 0xd0bc, 0x09c8, 0xa890, 0xa842, 0xa88c, 0xa83e, 0xa888,
+ 0x0804, 0x1819, 0xa87c, 0xd0bc, 0x0978, 0xa890, 0xa842, 0xa88c,
+ 0xa83e, 0xa888, 0x0804, 0x1884, 0xa87c, 0xd0bc, 0x0928, 0xa890,
+ 0xa842, 0xa88c, 0xa83e, 0xa804, 0x9045, 0x090c, 0x0d7d, 0xa164,
+ 0xa91a, 0x91ec, 0x000f, 0x9d80, 0x1eab, 0x2065, 0xa888, 0xd19c,
+ 0x1904, 0x1884, 0x0430, 0xa87c, 0xd0ac, 0x0904, 0x17a4, 0xa804,
+ 0x9045, 0x090c, 0x0d7d, 0xa164, 0xa91a, 0x91ec, 0x000f, 0x9d80,
+ 0x1eab, 0x2065, 0x9006, 0xa842, 0xa83e, 0xd19c, 0x1904, 0x1884,
+ 0x0080, 0xa87c, 0xd0ac, 0x0904, 0x17a4, 0x9006, 0xa842, 0xa83e,
+ 0x0804, 0x1884, 0xa87c, 0xd0ac, 0x0904, 0x17a4, 0x9006, 0xa842,
+ 0xa83e, 0x2c05, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x9082, 0x001b,
+ 0x0002, 0x183c, 0x183c, 0x183e, 0x183c, 0x183c, 0x183c, 0x1848,
+ 0x183c, 0x183c, 0x183c, 0x1852, 0x183c, 0x183c, 0x183c, 0x185c,
+ 0x183c, 0x183c, 0x183c, 0x1866, 0x183c, 0x183c, 0x183c, 0x1870,
+ 0x183c, 0x183c, 0x183c, 0x187a, 0x080c, 0x0d7d, 0xa574, 0xa478,
+ 0x9d86, 0x0024, 0x0904, 0x17b2, 0xa37c, 0xa280, 0x0804, 0x18dd,
+ 0xa584, 0xa488, 0x9d86, 0x0024, 0x0904, 0x17b2, 0xa38c, 0xa290,
+ 0x0804, 0x18dd, 0xa594, 0xa498, 0x9d86, 0x0024, 0x0904, 0x17b2,
+ 0xa39c, 0xa2a0, 0x0804, 0x18dd, 0xa5a4, 0xa4a8, 0x9d86, 0x0024,
+ 0x0904, 0x17b2, 0xa3ac, 0xa2b0, 0x0804, 0x18dd, 0xa5b4, 0xa4b8,
+ 0x9d86, 0x0024, 0x0904, 0x17b2, 0xa3bc, 0xa2c0, 0x0804, 0x18dd,
+ 0xa5c4, 0xa4c8, 0x9d86, 0x0024, 0x0904, 0x17b2, 0xa3cc, 0xa2d0,
+ 0x0804, 0x18dd, 0xa5d4, 0xa4d8, 0x9d86, 0x0024, 0x0904, 0x17b2,
+ 0xa3dc, 0xa2e0, 0x0804, 0x18dd, 0x2c05, 0x908a, 0x0034, 0x1a0c,
+ 0x0d7d, 0x9082, 0x001b, 0x0002, 0x18a7, 0x18a5, 0x18a5, 0x18a5,
+ 0x18a5, 0x18a5, 0x18b2, 0x18a5, 0x18a5, 0x18a5, 0x18a5, 0x18a5,
+ 0x18bd, 0x18a5, 0x18a5, 0x18a5, 0x18a5, 0x18a5, 0x18c8, 0x18a5,
+ 0x18a5, 0x18a5, 0x18a5, 0x18a5, 0x18d3, 0x080c, 0x0d7d, 0xa56c,
+ 0xa470, 0xa774, 0xa678, 0x9d86, 0x002c, 0x0904, 0x17b2, 0xa37c,
+ 0xa280, 0x0458, 0xa584, 0xa488, 0xa78c, 0xa690, 0x9d86, 0x002c,
+ 0x0904, 0x17b2, 0xa394, 0xa298, 0x0400, 0xa59c, 0xa4a0, 0xa7a4,
+ 0xa6a8, 0x9d86, 0x002c, 0x0904, 0x17b2, 0xa3ac, 0xa2b0, 0x00a8,
+ 0xa5b4, 0xa4b8, 0xa7bc, 0xa6c0, 0x9d86, 0x002c, 0x0904, 0x17b2,
+ 0xa3c4, 0xa2c8, 0x0050, 0xa5cc, 0xa4d0, 0xa7d4, 0xa6d8, 0x9d86,
+ 0x002c, 0x0904, 0x17b2, 0xa3dc, 0xa2e0, 0xab2e, 0xaa32, 0xad1e,
+ 0xac22, 0xaf26, 0xae2a, 0xa988, 0x8c60, 0x2c1d, 0xa8ac, 0xaab0,
+ 0xa836, 0xaa3a, 0x8109, 0xa916, 0x1160, 0x3e60, 0x601c, 0xc085,
+ 0x601e, 0xa87c, 0xc0dd, 0xa87e, 0x9006, 0x00ce, 0x001e, 0x012e,
+ 0x0005, 0x2800, 0xa80e, 0xab0a, 0x2c00, 0xa812, 0x0c70, 0x0804,
+ 0x17a4, 0x2ff0, 0x0126, 0x2091, 0x2200, 0x0016, 0x00c6, 0x3e60,
+ 0x6014, 0x2048, 0x2940, 0xa80e, 0x2061, 0x1ea6, 0xa813, 0x1ea6,
+ 0x2c05, 0xa80a, 0xa964, 0xa91a, 0xa87c, 0xd0ac, 0x090c, 0x0d7d,
+ 0x9006, 0xa842, 0xa83e, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0d7d,
+ 0xadcc, 0xacd0, 0xafd4, 0xaed8, 0xabdc, 0xaae0, 0xab2e, 0xaa32,
+ 0xad1e, 0xac22, 0xaf26, 0xae2a, 0xa8ac, 0xaab0, 0xa836, 0xaa3a,
+ 0xa988, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0008, 0x1120, 0x8109,
+ 0xa916, 0x0128, 0x0080, 0x918a, 0x0002, 0xa916, 0x1160, 0x3e60,
+ 0x601c, 0xc085, 0x601e, 0xa87c, 0xc0dd, 0xa87e, 0x9006, 0x00ce,
+ 0x001e, 0x012e, 0x0005, 0xa804, 0x9045, 0x090c, 0x0d7d, 0xa80e,
+ 0xa064, 0xa81a, 0x9084, 0x000f, 0x9080, 0x1eab, 0x2015, 0x82ff,
+ 0x090c, 0x0d7d, 0xaa12, 0x2205, 0xa80a, 0x0c08, 0x903e, 0x2730,
+ 0xa880, 0xd0fc, 0x1190, 0x2d00, 0x0002, 0x1a88, 0x19b5, 0x19b5,
+ 0x1a88, 0x19b5, 0x1a82, 0x1a88, 0x19b5, 0x1a25, 0x1a25, 0x1a25,
+ 0x1a88, 0x1a25, 0x1a88, 0x1a7f, 0x1a25, 0xc0fc, 0xa882, 0xab2c,
+ 0xaa30, 0xad1c, 0xac20, 0xdd9c, 0x0904, 0x1a8a, 0x2c05, 0x908a,
+ 0x0034, 0x1a0c, 0x0d7d, 0x9082, 0x001b, 0x0002, 0x19a1, 0x199f,
+ 0x199f, 0x199f, 0x199f, 0x199f, 0x19a5, 0x199f, 0x199f, 0x199f,
+ 0x199f, 0x199f, 0x19a9, 0x199f, 0x199f, 0x199f, 0x199f, 0x199f,
+ 0x19ad, 0x199f, 0x199f, 0x199f, 0x199f, 0x199f, 0x19b1, 0x080c,
+ 0x0d7d, 0xa774, 0xa678, 0x0804, 0x1a8a, 0xa78c, 0xa690, 0x0804,
+ 0x1a8a, 0xa7a4, 0xa6a8, 0x0804, 0x1a8a, 0xa7bc, 0xa6c0, 0x0804,
+ 0x1a8a, 0xa7d4, 0xa6d8, 0x0804, 0x1a8a, 0xa898, 0x901d, 0x1108,
+ 0xab9c, 0x9016, 0x2c05, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x9082,
+ 0x001b, 0x0002, 0x19dd, 0x19dd, 0x19df, 0x19dd, 0x19dd, 0x19dd,
+ 0x19e9, 0x19dd, 0x19dd, 0x19dd, 0x19f3, 0x19dd, 0x19dd, 0x19dd,
+ 0x19fd, 0x19dd, 0x19dd, 0x19dd, 0x1a07, 0x19dd, 0x19dd, 0x19dd,
+ 0x1a11, 0x19dd, 0x19dd, 0x19dd, 0x1a1b, 0x080c, 0x0d7d, 0xa574,
+ 0xa478, 0x9d86, 0x0004, 0x0904, 0x1a8a, 0xa37c, 0xa280, 0x0804,
+ 0x1a8a, 0xa584, 0xa488, 0x9d86, 0x0004, 0x0904, 0x1a8a, 0xa38c,
+ 0xa290, 0x0804, 0x1a8a, 0xa594, 0xa498, 0x9d86, 0x0004, 0x0904,
+ 0x1a8a, 0xa39c, 0xa2a0, 0x0804, 0x1a8a, 0xa5a4, 0xa4a8, 0x9d86,
+ 0x0004, 0x0904, 0x1a8a, 0xa3ac, 0xa2b0, 0x0804, 0x1a8a, 0xa5b4,
+ 0xa4b8, 0x9d86, 0x0004, 0x0904, 0x1a8a, 0xa3bc, 0xa2c0, 0x0804,
+ 0x1a8a, 0xa5c4, 0xa4c8, 0x9d86, 0x0004, 0x0904, 0x1a8a, 0xa3cc,
+ 0xa2d0, 0x0804, 0x1a8a, 0xa5d4, 0xa4d8, 0x9d86, 0x0004, 0x0904,
+ 0x1a8a, 0xa3dc, 0xa2e0, 0x0804, 0x1a8a, 0xa898, 0x901d, 0x1108,
+ 0xab9c, 0x9016, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0d7d, 0x9082,
+ 0x001b, 0x0002, 0x1a4d, 0x1a4b, 0x1a4b, 0x1a4b, 0x1a4b, 0x1a4b,
+ 0x1a57, 0x1a4b, 0x1a4b, 0x1a4b, 0x1a4b, 0x1a4b, 0x1a61, 0x1a4b,
+ 0x1a4b, 0x1a4b, 0x1a4b, 0x1a4b, 0x1a6b, 0x1a4b, 0x1a4b, 0x1a4b,
+ 0x1a4b, 0x1a4b, 0x1a75, 0x080c, 0x0d7d, 0xa56c, 0xa470, 0xa774,
+ 0xa678, 0x9d86, 0x000c, 0x05b0, 0xa37c, 0xa280, 0x0498, 0xa584,
+ 0xa488, 0xa78c, 0xa690, 0x9d86, 0x000c, 0x0560, 0xa394, 0xa298,
+ 0x0448, 0xa59c, 0xa4a0, 0xa7a4, 0xa6a8, 0x9d86, 0x000c, 0x0510,
+ 0xa3ac, 0xa2b0, 0x00f8, 0xa5b4, 0xa4b8, 0xa7bc, 0xa6c0, 0x9d86,
+ 0x000c, 0x01c0, 0xa3c4, 0xa2c8, 0x00a8, 0xa5cc, 0xa4d0, 0xa7d4,
+ 0xa6d8, 0x9d86, 0x000c, 0x0170, 0xa3dc, 0xa2e0, 0x0058, 0x9d86,
+ 0x000e, 0x1130, 0x080c, 0x1e81, 0x1904, 0x195e, 0x900e, 0x0050,
+ 0x080c, 0x0d7d, 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a,
+ 0x080c, 0x1e81, 0x0005, 0x6014, 0x2048, 0x6118, 0x810c, 0x810c,
+ 0x810c, 0x81ff, 0x1118, 0xa887, 0x0001, 0x0008, 0xa986, 0x601b,
+ 0x0002, 0xa874, 0x9084, 0x00ff, 0x9084, 0x0008, 0x0150, 0x00e9,
+ 0x6000, 0x9086, 0x0004, 0x1120, 0x2009, 0x0048, 0x080c, 0xad4d,
+ 0x0005, 0xa974, 0xd1dc, 0x1108, 0x0005, 0xa934, 0xa88c, 0x9106,
+ 0x1158, 0xa938, 0xa890, 0x9106, 0x1138, 0x601c, 0xc084, 0x601e,
+ 0x2009, 0x0048, 0x0804, 0xad4d, 0x0005, 0x0126, 0x00c6, 0x2091,
+ 0x2200, 0x00ce, 0x7908, 0x918c, 0x0007, 0x9186, 0x0000, 0x05b0,
+ 0x9186, 0x0003, 0x0598, 0x6020, 0x6023, 0x0000, 0x0006, 0x2031,
+ 0x0008, 0x00c6, 0x781f, 0x0808, 0x7808, 0xd09c, 0x0120, 0x080c,
+ 0x13bb, 0x8631, 0x1db8, 0x00ce, 0x781f, 0x0800, 0x2031, 0x0168,
+ 0x00c6, 0x7808, 0xd09c, 0x190c, 0x13bb, 0x00ce, 0x2001, 0x0038,
+ 0x080c, 0x1b98, 0x7930, 0x9186, 0x0040, 0x0160, 0x9186, 0x0042,
+ 0x190c, 0x0d7d, 0x2001, 0x001e, 0x8001, 0x1df0, 0x8631, 0x1d40,
+ 0x080c, 0x1ba7, 0x000e, 0x6022, 0x012e, 0x0005, 0x080c, 0x1b94,
+ 0x7827, 0x0015, 0x7828, 0x9c06, 0x1db8, 0x782b, 0x0000, 0x0ca0,
+ 0x00f6, 0x2079, 0x0300, 0x7803, 0x0000, 0x78ab, 0x0004, 0x00fe,
+ 0x080c, 0x753d, 0x1188, 0x2001, 0x0138, 0x2003, 0x0000, 0x2001,
+ 0x0160, 0x2003, 0x0000, 0x2011, 0x012c, 0xa001, 0xa001, 0x8211,
+ 0x1de0, 0x0059, 0x0804, 0x75e2, 0x0479, 0x0039, 0x2001, 0x0160,
+ 0x2502, 0x2001, 0x0138, 0x2202, 0x0005, 0x00e6, 0x2071, 0x0200,
+ 0x080c, 0x2a6c, 0x2009, 0x003c, 0x080c, 0x220a, 0x2001, 0x015d,
+ 0x2003, 0x0000, 0x7000, 0x9084, 0x003c, 0x1de0, 0x080c, 0x85ce,
+ 0x70a0, 0x70a2, 0x7098, 0x709a, 0x709c, 0x709e, 0x2001, 0x020d,
+ 0x2003, 0x0020, 0x00f6, 0x2079, 0x0300, 0x080c, 0x1354, 0x7803,
+ 0x0001, 0x00fe, 0x00ee, 0x0005, 0x2001, 0x0138, 0x2014, 0x2003,
+ 0x0000, 0x2001, 0x0160, 0x202c, 0x2003, 0x0000, 0x080c, 0x753d,
+ 0x1108, 0x0005, 0x2021, 0x0260, 0x2001, 0x0141, 0x201c, 0xd3dc,
+ 0x1168, 0x2001, 0x0109, 0x201c, 0x939c, 0x0048, 0x1160, 0x2001,
+ 0x0111, 0x201c, 0x83ff, 0x1110, 0x8421, 0x1d70, 0x2001, 0x015d,
+ 0x2003, 0x0000, 0x0005, 0x0046, 0x2021, 0x0019, 0x2003, 0x0048,
+ 0xa001, 0xa001, 0x201c, 0x939c, 0x0048, 0x0120, 0x8421, 0x1db0,
+ 0x004e, 0x0c60, 0x004e, 0x0c40, 0x601c, 0xc084, 0x601e, 0x0005,
+ 0x2c08, 0x621c, 0x080c, 0x16a0, 0x7930, 0x0005, 0x2c08, 0x621c,
+ 0x080c, 0x16cd, 0x7930, 0x0005, 0x8001, 0x1df0, 0x0005, 0x2031,
+ 0x0064, 0x781c, 0x9084, 0x0007, 0x0170, 0x2001, 0x0038, 0x0c41,
+ 0x9186, 0x0040, 0x0904, 0x1c05, 0x2001, 0x001e, 0x0c69, 0x8631,
+ 0x1d80, 0x080c, 0x0d7d, 0x781f, 0x0202, 0x2001, 0x015d, 0x2003,
+ 0x0000, 0x2001, 0x0dac, 0x0c01, 0x781c, 0xd084, 0x0110, 0x0861,
+ 0x04e0, 0x2001, 0x0030, 0x0891, 0x9186, 0x0040, 0x0568, 0x781c,
+ 0xd084, 0x1da8, 0x781f, 0x0101, 0x2001, 0x0014, 0x0869, 0x2001,
+ 0x0037, 0x0821, 0x9186, 0x0040, 0x0140, 0x2001, 0x0030, 0x080c,
+ 0x1b9e, 0x9186, 0x0040, 0x190c, 0x0d7d, 0x00d6, 0x2069, 0x0200,
+ 0x692c, 0xd1f4, 0x1170, 0xd1c4, 0x0160, 0xd19c, 0x0130, 0x6800,
+ 0x9085, 0x1800, 0x6802, 0x00de, 0x0080, 0x6908, 0x9184, 0x0007,
+ 0x1db0, 0x00de, 0x781f, 0x0100, 0x791c, 0x9184, 0x0007, 0x090c,
+ 0x0d7d, 0xa001, 0xa001, 0x781f, 0x0200, 0x0005, 0x0126, 0x2091,
+ 0x2400, 0x2079, 0x0380, 0x2001, 0x19e6, 0x2070, 0x012e, 0x0005,
+ 0x2cf0, 0x0126, 0x2091, 0x2400, 0x3e60, 0x6014, 0x2048, 0xa964,
+ 0xa91a, 0x918c, 0x00ff, 0x9184, 0x000f, 0x0002, 0x1c3a, 0x1c3a,
+ 0x1c3a, 0x1c3c, 0x1c3a, 0x1c3a, 0x1c3a, 0x1c3a, 0x1c2e, 0x1c44,
+ 0x1c3a, 0x1c40, 0x1c3a, 0x1c3a, 0x1c3a, 0x1c3a, 0x9086, 0x0008,
+ 0x1148, 0xa87c, 0xd0b4, 0x0904, 0x1db4, 0x2011, 0x1ea6, 0x2205,
+ 0xab88, 0x00a8, 0x080c, 0x0d7d, 0x9186, 0x0013, 0x0128, 0x0cd0,
+ 0x9186, 0x001b, 0x0108, 0x0cb0, 0xa87c, 0xd0b4, 0x0904, 0x1db4,
+ 0x9184, 0x000f, 0x9080, 0x1eab, 0x2015, 0x2205, 0xab88, 0x2908,
+ 0xa80a, 0xa90e, 0xaa12, 0xab16, 0x9006, 0xa842, 0xa83e, 0x012e,
+ 0x0005, 0x2cf0, 0x0126, 0x2091, 0x2400, 0x3e60, 0x6014, 0x2048,
+ 0xa88c, 0xa990, 0xaaac, 0xabb0, 0xaa36, 0xab3a, 0xa83e, 0xa942,
+ 0xa846, 0xa94a, 0xa964, 0x918c, 0x00ff, 0x9186, 0x001e, 0x0198,
+ 0x2940, 0xa064, 0xa81a, 0x90ec, 0x000f, 0x9d80, 0x1eab, 0x2065,
+ 0x2c05, 0x2808, 0x2c10, 0xab88, 0xa80a, 0xa90e, 0xaa12, 0xab16,
+ 0x012e, 0x3e60, 0x0005, 0xa804, 0x2040, 0x0c58, 0x2cf0, 0x0126,
+ 0x2091, 0x2400, 0x3e60, 0x6014, 0x2048, 0xa97c, 0x2950, 0xd1dc,
+ 0x1904, 0x1d7e, 0xc1dd, 0xa97e, 0x9006, 0xa842, 0xa83e, 0xa988,
+ 0x8109, 0xa916, 0xa964, 0xa91a, 0x9184, 0x000f, 0x9088, 0x1eab,
+ 0x2145, 0x0002, 0x1cb2, 0x1cc0, 0x1cb2, 0x1cb2, 0x1cb2, 0x1cb4,
+ 0x1cb2, 0x1cb2, 0x1d15, 0x1d15, 0x1cb2, 0x1cb2, 0x1cb2, 0x1d13,
+ 0x1cb2, 0x1cb2, 0x080c, 0x0d7d, 0xa804, 0x2050, 0xb164, 0xa91a,
+ 0x9184, 0x000f, 0x9080, 0x1eab, 0x2045, 0xd19c, 0x1904, 0x1d15,
+ 0x9036, 0x2638, 0x2805, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x9082,
+ 0x001b, 0x0002, 0x1ce5, 0x1ce5, 0x1ce7, 0x1ce5, 0x1ce5, 0x1ce5,
+ 0x1ced, 0x1ce5, 0x1ce5, 0x1ce5, 0x1cf3, 0x1ce5, 0x1ce5, 0x1ce5,
+ 0x1cf9, 0x1ce5, 0x1ce5, 0x1ce5, 0x1cff, 0x1ce5, 0x1ce5, 0x1ce5,
+ 0x1d05, 0x1ce5, 0x1ce5, 0x1ce5, 0x1d0b, 0x080c, 0x0d7d, 0xb574,
+ 0xb478, 0xb37c, 0xb280, 0x0804, 0x1d5a, 0xb584, 0xb488, 0xb38c,
+ 0xb290, 0x0804, 0x1d5a, 0xb594, 0xb498, 0xb39c, 0xb2a0, 0x0804,
+ 0x1d5a, 0xb5a4, 0xb4a8, 0xb3ac, 0xb2b0, 0x0804, 0x1d5a, 0xb5b4,
+ 0xb4b8, 0xb3bc, 0xb2c0, 0x0804, 0x1d5a, 0xb5c4, 0xb4c8, 0xb3cc,
+ 0xb2d0, 0x0804, 0x1d5a, 0xb5d4, 0xb4d8, 0xb3dc, 0xb2e0, 0x0804,
+ 0x1d5a, 0x0804, 0x1d5a, 0x080c, 0x0d7d, 0x2805, 0x908a, 0x0034,
+ 0x1a0c, 0x0d7d, 0x9082, 0x001b, 0x0002, 0x1d38, 0x1d36, 0x1d36,
+ 0x1d36, 0x1d36, 0x1d36, 0x1d3f, 0x1d36, 0x1d36, 0x1d36, 0x1d36,
+ 0x1d36, 0x1d46, 0x1d36, 0x1d36, 0x1d36, 0x1d36, 0x1d36, 0x1d4d,
+ 0x1d36, 0x1d36, 0x1d36, 0x1d36, 0x1d36, 0x1d54, 0x080c, 0x0d7d,
+ 0xb56c, 0xb470, 0xb774, 0xb678, 0xb37c, 0xb280, 0x00d8, 0xb584,
+ 0xb488, 0xb78c, 0xb690, 0xb394, 0xb298, 0x00a0, 0xb59c, 0xb4a0,
+ 0xb7a4, 0xb6a8, 0xb3ac, 0xb2b0, 0x0068, 0xb5b4, 0xb4b8, 0xb7bc,
+ 0xb6c0, 0xb3c4, 0xb2c8, 0x0030, 0xb5cc, 0xb4d0, 0xb7d4, 0xb6d8,
+ 0xb3dc, 0xb2e0, 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a,
+ 0xa988, 0x8109, 0xa916, 0x1118, 0x9006, 0x012e, 0x0005, 0x8840,
+ 0x2805, 0x9005, 0x1168, 0xb004, 0x9005, 0x090c, 0x0d7d, 0x2050,
+ 0xb164, 0xa91a, 0x9184, 0x000f, 0x9080, 0x1eab, 0x2045, 0x2805,
+ 0x2810, 0x2a08, 0xa80a, 0xa90e, 0xaa12, 0x0c30, 0x3e60, 0x6344,
+ 0xd3fc, 0x190c, 0x0d7d, 0xa93c, 0xaa40, 0xa844, 0x9106, 0x1118,
+ 0xa848, 0x9206, 0x0508, 0x2958, 0xab48, 0xac44, 0x2940, 0x080c,
+ 0x1ecb, 0x1998, 0x2850, 0x2c40, 0xab14, 0xa880, 0xd0fc, 0x1140,
+ 0xa810, 0x2005, 0xa80a, 0x2a00, 0xa80e, 0x2009, 0x8015, 0x0070,
+ 0x00c6, 0x3e60, 0x6044, 0xc0a4, 0x9085, 0x8005, 0x6046, 0x00ce,
+ 0x8319, 0xab16, 0x1904, 0x1d67, 0x2009, 0x8005, 0x3e60, 0x6044,
+ 0x9105, 0x6046, 0x0804, 0x1d64, 0x080c, 0x0d7d, 0x00f6, 0x00e6,
+ 0x0096, 0x00c6, 0x0026, 0x704c, 0x9c06, 0x190c, 0x0d7d, 0x2079,
+ 0x0090, 0x2001, 0x0105, 0x2003, 0x0010, 0x782b, 0x0004, 0x7057,
+ 0x0000, 0x6014, 0x2048, 0x080c, 0xc97a, 0x0118, 0xa880, 0xc0bd,
+ 0xa882, 0x6020, 0x9086, 0x0006, 0x1170, 0x2061, 0x0100, 0x62c8,
+ 0x2001, 0x00fa, 0x8001, 0x1df0, 0x60c8, 0x9206, 0x1dc0, 0x60c4,
+ 0xa89a, 0x60c8, 0xa896, 0x704c, 0x2060, 0x00c6, 0x080c, 0xc566,
+ 0x080c, 0xa91e, 0x00ce, 0x704c, 0x9c06, 0x1150, 0x2009, 0x0040,
+ 0x080c, 0x220a, 0x080c, 0xa3c3, 0x2011, 0x0000, 0x080c, 0xa24d,
+ 0x002e, 0x00ce, 0x009e, 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x2079,
+ 0x0090, 0x781c, 0x0006, 0x7818, 0x0006, 0x2079, 0x0100, 0x7a14,
+ 0x9284, 0x1984, 0x9085, 0x0012, 0x7816, 0x2019, 0x1000, 0x8319,
+ 0x090c, 0x0d7d, 0x7820, 0xd0bc, 0x1dd0, 0x79c8, 0x000e, 0x9102,
+ 0x001e, 0x0006, 0x0016, 0x79c4, 0x000e, 0x9103, 0x78c6, 0x000e,
+ 0x78ca, 0x9284, 0x1984, 0x9085, 0x0012, 0x7816, 0x2079, 0x0090,
+ 0x782b, 0x0008, 0x7057, 0x0000, 0x00fe, 0x0005, 0x00f6, 0x00e6,
+ 0x2071, 0x19e6, 0x7054, 0x9086, 0x0000, 0x0904, 0x1e7c, 0x2079,
+ 0x0090, 0x2009, 0x0207, 0x210c, 0xd194, 0x01b8, 0x2009, 0x020c,
+ 0x210c, 0x9184, 0x0003, 0x0188, 0x080c, 0xe8a3, 0x2001, 0x0133,
+ 0x2004, 0x9005, 0x090c, 0x0d7d, 0x0016, 0x2009, 0x0040, 0x080c,
+ 0x220a, 0x001e, 0x2001, 0x020c, 0x2102, 0x2009, 0x0206, 0x2104,
+ 0x2009, 0x0203, 0x210c, 0x9106, 0x1120, 0x2009, 0x0040, 0x080c,
+ 0x220a, 0x782c, 0xd0fc, 0x09a8, 0x080c, 0xa93a, 0x782c, 0xd0fc,
+ 0x1de8, 0x080c, 0xa91e, 0x7054, 0x9086, 0x0000, 0x1950, 0x782b,
+ 0x0004, 0x782c, 0xd0ac, 0x1de8, 0x2009, 0x0040, 0x080c, 0x220a,
+ 0x782b, 0x0002, 0x7057, 0x0000, 0x00ee, 0x00fe, 0x0005, 0x080c,
+ 0x0d7d, 0x8c60, 0x2c05, 0x9005, 0x0110, 0x8a51, 0x0005, 0xa004,
+ 0x9005, 0x0168, 0xa85a, 0x2040, 0xa064, 0x9084, 0x000f, 0x9080,
+ 0x1eab, 0x2065, 0x8cff, 0x090c, 0x0d7d, 0x8a51, 0x0005, 0x2050,
+ 0x0005, 0x0000, 0x001d, 0x0021, 0x0025, 0x0029, 0x002d, 0x0031,
+ 0x0035, 0x0000, 0x001b, 0x0021, 0x0027, 0x002d, 0x0033, 0x0000,
+ 0x0000, 0x0023, 0x0000, 0x0000, 0x1e9e, 0x1e9a, 0x1e9e, 0x1e9e,
+ 0x1ea8, 0x0000, 0x1e9e, 0x1ea5, 0x1ea5, 0x1ea2, 0x1ea5, 0x1ea5,
+ 0x0000, 0x1ea8, 0x1ea5, 0x0000, 0x1ea0, 0x1ea0, 0x0000, 0x1ea0,
+ 0x1ea8, 0x0000, 0x1ea0, 0x1ea6, 0x1ea6, 0x1ea6, 0x0000, 0x1ea6,
+ 0x0000, 0x1ea8, 0x1ea6, 0x00c6, 0x00d6, 0x0086, 0xab42, 0xac3e,
+ 0xa888, 0x9055, 0x0904, 0x20aa, 0x2940, 0xa064, 0x90ec, 0x000f,
+ 0x9084, 0x00ff, 0x9086, 0x0008, 0x1118, 0x2061, 0x1ea6, 0x00d0,
+ 0x9de0, 0x1eab, 0x9d86, 0x0007, 0x0130, 0x9d86, 0x000e, 0x0118,
+ 0x9d86, 0x000f, 0x1120, 0xa08c, 0x9422, 0xa090, 0x931b, 0x2c05,
+ 0x9065, 0x1140, 0x0310, 0x0804, 0x20aa, 0xa004, 0x9045, 0x0904,
+ 0x20aa, 0x08d8, 0x2c05, 0x9005, 0x0904, 0x1f92, 0xdd9c, 0x1904,
+ 0x1f4e, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x9082, 0x001b, 0x0002,
+ 0x1f23, 0x1f23, 0x1f25, 0x1f23, 0x1f23, 0x1f23, 0x1f2b, 0x1f23,
+ 0x1f23, 0x1f23, 0x1f31, 0x1f23, 0x1f23, 0x1f23, 0x1f37, 0x1f23,
+ 0x1f23, 0x1f23, 0x1f3d, 0x1f23, 0x1f23, 0x1f23, 0x1f43, 0x1f23,
+ 0x1f23, 0x1f23, 0x1f49, 0x080c, 0x0d7d, 0xa07c, 0x9422, 0xa080,
+ 0x931b, 0x0804, 0x1f88, 0xa08c, 0x9422, 0xa090, 0x931b, 0x0804,
+ 0x1f88, 0xa09c, 0x9422, 0xa0a0, 0x931b, 0x0804, 0x1f88, 0xa0ac,
+ 0x9422, 0xa0b0, 0x931b, 0x0804, 0x1f88, 0xa0bc, 0x9422, 0xa0c0,
+ 0x931b, 0x0804, 0x1f88, 0xa0cc, 0x9422, 0xa0d0, 0x931b, 0x0804,
+ 0x1f88, 0xa0dc, 0x9422, 0xa0e0, 0x931b, 0x04d0, 0x908a, 0x0034,
+ 0x1a0c, 0x0d7d, 0x9082, 0x001b, 0x0002, 0x1f70, 0x1f6e, 0x1f6e,
+ 0x1f6e, 0x1f6e, 0x1f6e, 0x1f75, 0x1f6e, 0x1f6e, 0x1f6e, 0x1f6e,
+ 0x1f6e, 0x1f7a, 0x1f6e, 0x1f6e, 0x1f6e, 0x1f6e, 0x1f6e, 0x1f7f,
+ 0x1f6e, 0x1f6e, 0x1f6e, 0x1f6e, 0x1f6e, 0x1f84, 0x080c, 0x0d7d,
+ 0xa07c, 0x9422, 0xa080, 0x931b, 0x0098, 0xa094, 0x9422, 0xa098,
+ 0x931b, 0x0070, 0xa0ac, 0x9422, 0xa0b0, 0x931b, 0x0048, 0xa0c4,
+ 0x9422, 0xa0c8, 0x931b, 0x0020, 0xa0dc, 0x9422, 0xa0e0, 0x931b,
+ 0x0630, 0x2300, 0x9405, 0x0160, 0x8a51, 0x0904, 0x20aa, 0x8c60,
+ 0x0804, 0x1efa, 0xa004, 0x9045, 0x0904, 0x20aa, 0x0804, 0x1ed5,
+ 0x8a51, 0x0904, 0x20aa, 0x8c60, 0x2c05, 0x9005, 0x1158, 0xa004,
+ 0x9045, 0x0904, 0x20aa, 0xa064, 0x90ec, 0x000f, 0x9de0, 0x1eab,
+ 0x2c05, 0x2060, 0xa880, 0xc0fc, 0xa882, 0x0804, 0x209f, 0x2c05,
+ 0x8422, 0x8420, 0x831a, 0x9399, 0x0000, 0xac2e, 0xab32, 0xdd9c,
+ 0x1904, 0x203c, 0x9082, 0x001b, 0x0002, 0x1fd8, 0x1fd8, 0x1fda,
+ 0x1fd8, 0x1fd8, 0x1fd8, 0x1fe8, 0x1fd8, 0x1fd8, 0x1fd8, 0x1ff6,
+ 0x1fd8, 0x1fd8, 0x1fd8, 0x2004, 0x1fd8, 0x1fd8, 0x1fd8, 0x2012,
+ 0x1fd8, 0x1fd8, 0x1fd8, 0x2020, 0x1fd8, 0x1fd8, 0x1fd8, 0x202e,
+ 0x080c, 0x0d7d, 0xa17c, 0x2400, 0x9122, 0xa180, 0x2300, 0x911b,
+ 0x0a0c, 0x0d7d, 0xa074, 0x9420, 0xa078, 0x9319, 0x0804, 0x209a,
+ 0xa18c, 0x2400, 0x9122, 0xa190, 0x2300, 0x911b, 0x0a0c, 0x0d7d,
+ 0xa084, 0x9420, 0xa088, 0x9319, 0x0804, 0x209a, 0xa19c, 0x2400,
+ 0x9122, 0xa1a0, 0x2300, 0x911b, 0x0a0c, 0x0d7d, 0xa094, 0x9420,
+ 0xa098, 0x9319, 0x0804, 0x209a, 0xa1ac, 0x2400, 0x9122, 0xa1b0,
+ 0x2300, 0x911b, 0x0a0c, 0x0d7d, 0xa0a4, 0x9420, 0xa0a8, 0x9319,
+ 0x0804, 0x209a, 0xa1bc, 0x2400, 0x9122, 0xa1c0, 0x2300, 0x911b,
+ 0x0a0c, 0x0d7d, 0xa0b4, 0x9420, 0xa0b8, 0x9319, 0x0804, 0x209a,
+ 0xa1cc, 0x2400, 0x9122, 0xa1d0, 0x2300, 0x911b, 0x0a0c, 0x0d7d,
+ 0xa0c4, 0x9420, 0xa0c8, 0x9319, 0x0804, 0x209a, 0xa1dc, 0x2400,
+ 0x9122, 0xa1e0, 0x2300, 0x911b, 0x0a0c, 0x0d7d, 0xa0d4, 0x9420,
+ 0xa0d8, 0x9319, 0x0804, 0x209a, 0x9082, 0x001b, 0x0002, 0x205a,
+ 0x2058, 0x2058, 0x2058, 0x2058, 0x2058, 0x2067, 0x2058, 0x2058,
+ 0x2058, 0x2058, 0x2058, 0x2074, 0x2058, 0x2058, 0x2058, 0x2058,
+ 0x2058, 0x2081, 0x2058, 0x2058, 0x2058, 0x2058, 0x2058, 0x208e,
+ 0x080c, 0x0d7d, 0xa17c, 0x2400, 0x9122, 0xa180, 0x2300, 0x911b,
+ 0x0a0c, 0x0d7d, 0xa06c, 0x9420, 0xa070, 0x9319, 0x0498, 0xa194,
+ 0x2400, 0x9122, 0xa198, 0x2300, 0x911b, 0x0a0c, 0x0d7d, 0xa084,
+ 0x9420, 0xa088, 0x9319, 0x0430, 0xa1ac, 0x2400, 0x9122, 0xa1b0,
+ 0x2300, 0x911b, 0x0a0c, 0x0d7d, 0xa09c, 0x9420, 0xa0a0, 0x9319,
+ 0x00c8, 0xa1c4, 0x2400, 0x9122, 0xa1c8, 0x2300, 0x911b, 0x0a0c,
+ 0x0d7d, 0xa0b4, 0x9420, 0xa0b8, 0x9319, 0x0060, 0xa1dc, 0x2400,
+ 0x9122, 0xa1e0, 0x2300, 0x911b, 0x0a0c, 0x0d7d, 0xa0cc, 0x9420,
+ 0xa0d0, 0x9319, 0xac1e, 0xab22, 0xa880, 0xc0fd, 0xa882, 0x2800,
+ 0xa85a, 0x2c00, 0xa812, 0x2a00, 0xa816, 0x000e, 0x000e, 0x000e,
+ 0x9006, 0x0028, 0x008e, 0x00de, 0x00ce, 0x9085, 0x0001, 0x0005,
+ 0x00c6, 0x610c, 0x0016, 0x9026, 0x2410, 0x6004, 0x9420, 0x9291,
+ 0x0000, 0x2c04, 0x9210, 0x9ce0, 0x0002, 0x918a, 0x0002, 0x1da8,
+ 0x9284, 0x000f, 0x9405, 0x001e, 0x00ce, 0x0005, 0x7803, 0x0003,
+ 0x780f, 0x0000, 0x6004, 0x7812, 0x2c04, 0x7816, 0x9ce0, 0x0002,
+ 0x918a, 0x0002, 0x1db8, 0x0005, 0x2001, 0x0005, 0x2004, 0xd0bc,
+ 0x190c, 0x0d76, 0xd094, 0x0110, 0x080c, 0x11f6, 0x0005, 0x0126,
+ 0x2091, 0x2600, 0x2079, 0x0200, 0x2071, 0x0260, 0x2069, 0x1800,
+ 0x7817, 0x0000, 0x789b, 0x0814, 0x78a3, 0x0406, 0x789f, 0x0410,
+ 0x2009, 0x013b, 0x200b, 0x0400, 0x781b, 0x0002, 0x783b, 0x001f,
+ 0x7837, 0x0020, 0x7803, 0x1600, 0x012e, 0x0005, 0x2091, 0x2600,
+ 0x781c, 0xd0a4, 0x190c, 0x2207, 0x7900, 0xd1dc, 0x1118, 0x9084,
+ 0x0006, 0x001a, 0x9084, 0x000e, 0x0002, 0x2125, 0x211d, 0x7f45,
+ 0x211d, 0x211f, 0x211f, 0x211f, 0x211f, 0x7f2b, 0x211d, 0x2121,
+ 0x211d, 0x211f, 0x211d, 0x211f, 0x211d, 0x080c, 0x0d7d, 0x0031,
+ 0x0020, 0x080c, 0x7f2b, 0x080c, 0x7f45, 0x0005, 0x0006, 0x0016,
+ 0x0026, 0x080c, 0xe8a3, 0x7930, 0x9184, 0x0003, 0x0510, 0x080c,
+ 0xa91e, 0x2001, 0x19f9, 0x2004, 0x9005, 0x01a0, 0x2001, 0x0133,
+ 0x2004, 0x9005, 0x090c, 0x0d7d, 0x00c6, 0x2001, 0x19f9, 0x2064,
+ 0x080c, 0xa93a, 0x080c, 0xc566, 0x2009, 0x0040, 0x080c, 0x220a,
+ 0x00ce, 0x0408, 0x2009, 0x0040, 0x080c, 0x220a, 0x080c, 0xa93a,
+ 0x00d0, 0x9184, 0x0014, 0x01a0, 0x6a00, 0x9286, 0x0003, 0x0160,
+ 0x080c, 0x753d, 0x1138, 0x080c, 0x7840, 0x080c, 0x6092, 0x080c,
+ 0x746e, 0x0010, 0x080c, 0x5f4d, 0x080c, 0x7fe3, 0x0041, 0x0018,
+ 0x9184, 0x9540, 0x1dc8, 0x002e, 0x001e, 0x000e, 0x0005, 0x00e6,
+ 0x0036, 0x0046, 0x0056, 0x2071, 0x1a6a, 0x080c, 0x1b10, 0x005e,
+ 0x004e, 0x003e, 0x00ee, 0x0005, 0x0126, 0x2091, 0x2e00, 0x2071,
+ 0x1800, 0x7128, 0x2001, 0x196e, 0x2102, 0x2001, 0x1976, 0x2102,
+ 0x2001, 0x013b, 0x2102, 0x2079, 0x0200, 0x2001, 0x0201, 0x789e,
+ 0x78a3, 0x0200, 0x9198, 0x0007, 0x831c, 0x831c, 0x831c, 0x9398,
+ 0x0005, 0x2320, 0x9182, 0x0204, 0x1230, 0x2011, 0x0008, 0x8423,
+ 0x8423, 0x8423, 0x0488, 0x9182, 0x024c, 0x1240, 0x2011, 0x0007,
+ 0x8403, 0x8003, 0x9400, 0x9400, 0x9420, 0x0430, 0x9182, 0x02bc,
+ 0x1238, 0x2011, 0x0006, 0x8403, 0x8003, 0x9400, 0x9420, 0x00e0,
+ 0x9182, 0x034c, 0x1230, 0x2011, 0x0005, 0x8403, 0x8003, 0x9420,
+ 0x0098, 0x9182, 0x042c, 0x1228, 0x2011, 0x0004, 0x8423, 0x8423,
+ 0x0058, 0x9182, 0x059c, 0x1228, 0x2011, 0x0003, 0x8403, 0x9420,
+ 0x0018, 0x2011, 0x0002, 0x8423, 0x9482, 0x0228, 0x8002, 0x8020,
+ 0x8301, 0x9402, 0x0110, 0x0208, 0x8321, 0x8217, 0x8203, 0x9405,
+ 0x789a, 0x012e, 0x0005, 0x0006, 0x00d6, 0x2069, 0x0200, 0x6814,
+ 0x9084, 0xffc0, 0x910d, 0x6916, 0x00de, 0x000e, 0x0005, 0x00d6,
+ 0x2069, 0x0200, 0x9005, 0x6810, 0x0110, 0xc0a5, 0x0008, 0xc0a4,
+ 0x6812, 0x00de, 0x0005, 0x0006, 0x00d6, 0x2069, 0x0200, 0x6810,
+ 0x9084, 0xfff8, 0x910d, 0x6912, 0x00de, 0x000e, 0x0005, 0x7938,
+ 0x080c, 0x0d76, 0x00f6, 0x2079, 0x0200, 0x7902, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0xa001, 0xa001, 0x7902, 0xa001, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0xa001, 0x00fe, 0x0005, 0x0126, 0x2091, 0x2800,
+ 0x2061, 0x0100, 0x2071, 0x1800, 0x2009, 0x0000, 0x080c, 0x2a66,
+ 0x080c, 0x297c, 0x2001, 0x199c, 0x2003, 0x0700, 0x2001, 0x199d,
+ 0x2003, 0x0700, 0x080c, 0x2ad7, 0x9006, 0x080c, 0x29ab, 0x9006,
+ 0x080c, 0x298e, 0x20a9, 0x0012, 0x1d04, 0x223c, 0x2091, 0x6000,
+ 0x1f04, 0x223c, 0x602f, 0x0100, 0x602f, 0x0000, 0x6050, 0x9085,
+ 0x0400, 0x9084, 0xdfff, 0x6052, 0x6224, 0x080c, 0x2ab4, 0x080c,
+ 0x269a, 0x2009, 0x00ef, 0x6132, 0x6136, 0x080c, 0x26aa, 0x60e7,
+ 0x0000, 0x61ea, 0x60e3, 0x0008, 0x604b, 0xf7f7, 0x6043, 0x0000,
+ 0x602f, 0x0080, 0x602f, 0x0000, 0x6007, 0x349f, 0x00c6, 0x2061,
+ 0x0140, 0x608b, 0x000b, 0x608f, 0x10b8, 0x6093, 0x0000, 0x6097,
+ 0x0198, 0x00ce, 0x6004, 0x9085, 0x8000, 0x6006, 0x60bb, 0x0000,
+ 0x20a9, 0x0018, 0x60bf, 0x0000, 0x1f04, 0x227a, 0x60bb, 0x0000,
+ 0x60bf, 0x0108, 0x60bf, 0x0012, 0x60bf, 0x0405, 0x60bf, 0x0014,
+ 0x60bf, 0x0320, 0x60bf, 0x0018, 0x601b, 0x00f0, 0x601f, 0x001e,
+ 0x600f, 0x006b, 0x602b, 0x402c, 0x012e, 0x0005, 0x00f6, 0x2079,
+ 0x0140, 0x78c3, 0x0080, 0x78c3, 0x0083, 0x78c3, 0x0000, 0x00fe,
+ 0x0005, 0x2001, 0x1835, 0x2003, 0x0000, 0x2001, 0x1834, 0x2003,
+ 0x0001, 0x0005, 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026,
+ 0x6124, 0x6028, 0x910c, 0x0066, 0x2031, 0x1837, 0x2634, 0x96b4,
+ 0x0028, 0x006e, 0x1138, 0x6020, 0xd1bc, 0x0120, 0xd0bc, 0x1168,
+ 0xd0b4, 0x1198, 0x9184, 0x5e2c, 0x1118, 0x9184, 0x0007, 0x00aa,
+ 0x9195, 0x0004, 0x9284, 0x0007, 0x0082, 0x0016, 0x2001, 0x0387,
+ 0x200c, 0xd1a4, 0x001e, 0x0d70, 0x0c98, 0x0016, 0x2001, 0x0387,
+ 0x200c, 0xd1b4, 0x001e, 0x0d30, 0x0c58, 0x22e8, 0x22e5, 0x22e5,
+ 0x22e5, 0x22e7, 0x22e5, 0x22e5, 0x22e5, 0x080c, 0x0d7d, 0x0029,
+ 0x002e, 0x001e, 0x000e, 0x012e, 0x0005, 0x00a6, 0x6124, 0x6028,
+ 0xd09c, 0x0118, 0xd19c, 0x1904, 0x2562, 0xd1f4, 0x190c, 0x0d76,
+ 0x080c, 0x753d, 0x0904, 0x2345, 0x080c, 0xd09b, 0x1120, 0x7000,
+ 0x9086, 0x0003, 0x0580, 0x6024, 0x9084, 0x1800, 0x0560, 0x080c,
+ 0x7560, 0x0118, 0x080c, 0x754e, 0x1530, 0x2011, 0x0020, 0x080c,
+ 0x2ab4, 0x6043, 0x0000, 0x080c, 0xd09b, 0x0168, 0x080c, 0x7560,
+ 0x1150, 0x2001, 0x19a6, 0x2003, 0x0001, 0x6027, 0x1800, 0x080c,
+ 0x73b3, 0x0804, 0x2565, 0x70a4, 0x9005, 0x1150, 0x70a7, 0x0001,
+ 0x00d6, 0x2069, 0x0140, 0x080c, 0x7594, 0x00de, 0x1904, 0x2565,
+ 0x080c, 0x784a, 0x0428, 0x080c, 0x7560, 0x1590, 0x6024, 0x9084,
+ 0x1800, 0x1108, 0x0468, 0x080c, 0x784a, 0x080c, 0x7840, 0x080c,
+ 0x6092, 0x080c, 0x746e, 0x0804, 0x2562, 0xd1ac, 0x1508, 0x6024,
+ 0xd0dc, 0x1170, 0xd0e4, 0x1178, 0xd0d4, 0x1190, 0xd0cc, 0x0130,
+ 0x7098, 0x9086, 0x0028, 0x1110, 0x080c, 0x7721, 0x0804, 0x2562,
+ 0x080c, 0x7845, 0x0048, 0x2001, 0x197c, 0x2003, 0x0002, 0x0020,
+ 0x080c, 0x767e, 0x0804, 0x2562, 0x080c, 0x77c4, 0x0804, 0x2562,
+ 0x6220, 0xd1bc, 0x0138, 0xd2bc, 0x1904, 0x25cb, 0xd2b4, 0x1904,
+ 0x25dd, 0x0000, 0xd1ac, 0x0904, 0x246f, 0x0036, 0x6328, 0xc3bc,
+ 0x632a, 0x003e, 0x080c, 0x753d, 0x11d0, 0x2011, 0x0020, 0x080c,
+ 0x2ab4, 0x0006, 0x0026, 0x0036, 0x080c, 0x7557, 0x1158, 0x080c,
+ 0x7840, 0x080c, 0x6092, 0x080c, 0x746e, 0x003e, 0x002e, 0x000e,
+ 0x00ae, 0x0005, 0x003e, 0x002e, 0x000e, 0x080c, 0x7511, 0x0016,
+ 0x0046, 0x00c6, 0x644c, 0x9486, 0xf0f0, 0x1138, 0x2061, 0x0100,
+ 0x644a, 0x6043, 0x0090, 0x6043, 0x0010, 0x74da, 0x948c, 0xff00,
+ 0x7038, 0xd084, 0x0178, 0x9186, 0xf800, 0x1160, 0x7048, 0xd084,
+ 0x1148, 0xc085, 0x704a, 0x0036, 0x2418, 0x2011, 0x8016, 0x080c,
+ 0x4b52, 0x003e, 0x080c, 0xd094, 0x1904, 0x2446, 0x9196, 0xff00,
+ 0x05a8, 0x7060, 0x9084, 0x00ff, 0x810f, 0x81ff, 0x0110, 0x9116,
+ 0x0568, 0x7130, 0xd184, 0x1550, 0x080c, 0x33ad, 0x0128, 0xc18d,
+ 0x7132, 0x080c, 0x6ad5, 0x1510, 0x6240, 0x9294, 0x0010, 0x0130,
+ 0x6248, 0x9294, 0xff00, 0x9296, 0xff00, 0x01c0, 0x7030, 0xd08c,
+ 0x0904, 0x2446, 0x7038, 0xd08c, 0x1140, 0x2001, 0x180c, 0x200c,
+ 0xd1ac, 0x1904, 0x2446, 0xc1ad, 0x2102, 0x0036, 0x73d8, 0x2011,
+ 0x8013, 0x080c, 0x4b52, 0x003e, 0x0804, 0x2446, 0x7038, 0xd08c,
+ 0x1140, 0x2001, 0x180c, 0x200c, 0xd1ac, 0x1904, 0x2446, 0xc1ad,
+ 0x2102, 0x0036, 0x73d8, 0x2011, 0x8013, 0x080c, 0x4b52, 0x003e,
+ 0x7130, 0xc185, 0x7132, 0x2011, 0x1848, 0x220c, 0xd1a4, 0x01f0,
+ 0x0016, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c, 0x8979, 0x2019,
+ 0x000e, 0x00c6, 0x2061, 0x0000, 0x080c, 0xe3b5, 0x00ce, 0x9484,
+ 0x00ff, 0x9080, 0x33b9, 0x200d, 0x918c, 0xff00, 0x810f, 0x2120,
+ 0x9006, 0x2009, 0x000e, 0x080c, 0xe445, 0x001e, 0x0016, 0x2009,
+ 0x0002, 0x2019, 0x0004, 0x080c, 0x3205, 0x001e, 0x0078, 0x0156,
+ 0x00b6, 0x20a9, 0x007f, 0x900e, 0x080c, 0x6693, 0x1110, 0x080c,
+ 0x60ac, 0x8108, 0x1f04, 0x243c, 0x00be, 0x015e, 0x00ce, 0x004e,
+ 0x080c, 0xa91e, 0x080c, 0xabe9, 0x080c, 0xa93a, 0x60e3, 0x0000,
+ 0x001e, 0x2001, 0x1800, 0x2014, 0x9296, 0x0004, 0x1170, 0xd19c,
+ 0x11b0, 0x2011, 0x180c, 0x2214, 0xd29c, 0x1120, 0x6204, 0x9295,
+ 0x0002, 0x6206, 0x6228, 0xc29d, 0x622a, 0x2003, 0x0001, 0x2001,
+ 0x1826, 0x2003, 0x0000, 0x2011, 0x0020, 0x080c, 0x2ab4, 0xd194,
+ 0x0904, 0x2562, 0x0016, 0x080c, 0xa91e, 0x6220, 0xd2b4, 0x0904,
+ 0x24fd, 0x080c, 0x8780, 0x080c, 0x9ed4, 0x2011, 0x0004, 0x080c,
+ 0x2ab4, 0x00f6, 0x2019, 0x19f2, 0x2304, 0x907d, 0x0904, 0x24ca,
+ 0x7804, 0x9086, 0x0032, 0x15f0, 0x00d6, 0x00c6, 0x00e6, 0x0096,
+ 0x2069, 0x0140, 0x782c, 0x685e, 0x7808, 0x685a, 0x6043, 0x0002,
+ 0x2001, 0x0003, 0x8001, 0x1df0, 0x6043, 0x0000, 0x2001, 0x003c,
+ 0x8001, 0x1df0, 0x080c, 0x2a8a, 0x2001, 0x001e, 0x8001, 0x0240,
+ 0x20a9, 0x0009, 0x080c, 0x2a41, 0x6904, 0xd1dc, 0x1140, 0x0cb0,
+ 0x2001, 0x0100, 0x080c, 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x080c,
+ 0x967a, 0x080c, 0xa93a, 0x7814, 0x2048, 0xa867, 0x0103, 0x2f60,
+ 0x080c, 0xacb0, 0x009e, 0x00ee, 0x00ce, 0x00de, 0x00fe, 0x001e,
+ 0x00ae, 0x0005, 0x00fe, 0x00d6, 0x2069, 0x0140, 0x6804, 0x9084,
+ 0x4000, 0x0110, 0x080c, 0x2a8a, 0x00de, 0x00c6, 0x2061, 0x19e6,
+ 0x6034, 0x080c, 0xd09b, 0x0120, 0x909a, 0x0003, 0x1258, 0x0018,
+ 0x909a, 0x00c8, 0x1238, 0x8000, 0x6036, 0x00ce, 0x080c, 0x9eac,
+ 0x0804, 0x255f, 0x2061, 0x0100, 0x62c0, 0x080c, 0xa84f, 0x2019,
+ 0x19f2, 0x2304, 0x9065, 0x0130, 0x6003, 0x0001, 0x2009, 0x0027,
+ 0x080c, 0xad4d, 0x00ce, 0x0804, 0x255f, 0xd2bc, 0x0904, 0x2542,
+ 0x080c, 0x878d, 0x2011, 0x0004, 0x080c, 0x2ab4, 0x00d6, 0x2069,
+ 0x0140, 0x6804, 0x9084, 0x4000, 0x0110, 0x080c, 0x2a8a, 0x00de,
+ 0x00c6, 0x2061, 0x19e6, 0x6050, 0x080c, 0xd09b, 0x0120, 0x909a,
+ 0x0003, 0x1668, 0x0018, 0x909a, 0x00c8, 0x1648, 0x8000, 0x6052,
+ 0x604c, 0x00ce, 0x9005, 0x05d8, 0x2009, 0x07d0, 0x080c, 0x8785,
+ 0x9080, 0x0008, 0x2004, 0x9086, 0x0006, 0x1138, 0x2009, 0x1984,
+ 0x2011, 0x0012, 0x080c, 0x2ac3, 0x0450, 0x9080, 0x0008, 0x2004,
+ 0x9086, 0x0009, 0x0d98, 0x2009, 0x1984, 0x2011, 0x0016, 0x080c,
+ 0x2ac3, 0x00e8, 0x2011, 0x0004, 0x080c, 0x2ab4, 0x00c0, 0x0036,
+ 0x2019, 0x0001, 0x080c, 0xa1b8, 0x003e, 0x2019, 0x19f9, 0x2304,
+ 0x9065, 0x0160, 0x2009, 0x004f, 0x6020, 0x9086, 0x0009, 0x1110,
+ 0x2009, 0x004f, 0x6003, 0x0003, 0x080c, 0xad4d, 0x00ce, 0x080c,
+ 0xa93a, 0x001e, 0xd19c, 0x0904, 0x25c4, 0x7038, 0xd0ac, 0x1558,
+ 0x0016, 0x0156, 0x2011, 0x0008, 0x080c, 0x2ab4, 0x080c, 0x2ad7,
+ 0x080c, 0x2b0a, 0x6050, 0xc0e5, 0x6052, 0x20a9, 0x0367, 0x1f04,
+ 0x2591, 0x1d04, 0x2579, 0x080c, 0x87b4, 0x6020, 0xd09c, 0x1db8,
+ 0x00f6, 0x2079, 0x0100, 0x080c, 0x29ed, 0x00fe, 0x1d80, 0x6050,
+ 0xc0e4, 0x6052, 0x2011, 0x0008, 0x080c, 0x2ab4, 0x015e, 0x001e,
+ 0x0498, 0x015e, 0x001e, 0x0016, 0x6028, 0xc09c, 0x602a, 0x080c,
+ 0xa91e, 0x080c, 0xabe9, 0x080c, 0xa93a, 0x60e3, 0x0000, 0x080c,
+ 0xe882, 0x080c, 0xe89d, 0x080c, 0x5742, 0xd0fc, 0x1138, 0x080c,
+ 0xd094, 0x1120, 0x9085, 0x0001, 0x080c, 0x7584, 0x9006, 0x080c,
+ 0x2a7a, 0x2009, 0x0002, 0x080c, 0x2a66, 0x00e6, 0x2071, 0x1800,
+ 0x7003, 0x0004, 0x080c, 0x0ec1, 0x00ee, 0x2011, 0x0008, 0x080c,
+ 0x2ab4, 0x080c, 0x0bc3, 0x001e, 0x918c, 0xffd0, 0x2110, 0x080c,
+ 0x2ab4, 0x00ae, 0x0005, 0x0016, 0x2001, 0x0387, 0x200c, 0xd1a4,
+ 0x001e, 0x0904, 0x2372, 0x0016, 0x2009, 0x25d7, 0x00c0, 0x2001,
+ 0x0387, 0x2003, 0x1000, 0x001e, 0x0c38, 0x0016, 0x2001, 0x0387,
+ 0x200c, 0xd1b4, 0x001e, 0x0904, 0x2372, 0x0016, 0x2009, 0x25e9,
+ 0x0030, 0x2001, 0x0387, 0x2003, 0x4000, 0x001e, 0x08a8, 0x6028,
+ 0xc0bc, 0x602a, 0x2001, 0x0156, 0x2003, 0xbc91, 0x8000, 0x2003,
+ 0xffff, 0x6043, 0x0001, 0x080c, 0x2a60, 0x2011, 0x0080, 0x080c,
+ 0x2ab4, 0x6017, 0x0000, 0x6043, 0x0000, 0x0817, 0x0006, 0x0016,
+ 0x0026, 0x0036, 0x00e6, 0x00f6, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x1800, 0x71d0, 0x70d2, 0x9116, 0x0904, 0x2659, 0x81ff, 0x01a0,
+ 0x2009, 0x0000, 0x080c, 0x2a66, 0x2011, 0x8011, 0x2019, 0x010e,
+ 0x231c, 0x939e, 0x0007, 0x1118, 0x2019, 0x0001, 0x0010, 0x2019,
+ 0x0000, 0x080c, 0x4b52, 0x0468, 0x2001, 0x19a7, 0x200c, 0x81ff,
+ 0x1140, 0x2001, 0x0109, 0x2004, 0xd0b4, 0x0118, 0x2019, 0x0003,
+ 0x0008, 0x2118, 0x2011, 0x8012, 0x080c, 0x4b52, 0x080c, 0x0ec1,
+ 0x080c, 0x5742, 0xd0fc, 0x11a8, 0x080c, 0xd094, 0x1190, 0x00c6,
+ 0x080c, 0x26f5, 0x080c, 0xa91e, 0x080c, 0xa113, 0x080c, 0xa93a,
+ 0x2061, 0x0100, 0x2019, 0x0028, 0x2009, 0x0002, 0x080c, 0x3205,
+ 0x00ce, 0x012e, 0x00fe, 0x00ee, 0x003e, 0x002e, 0x001e, 0x000e,
+ 0x0005, 0x2028, 0x918c, 0x00ff, 0x2130, 0x9094, 0xff00, 0x11f0,
+ 0x2011, 0x1837, 0x2214, 0xd2ac, 0x11c8, 0x81ff, 0x01e8, 0x2011,
+ 0x181f, 0x2204, 0x9106, 0x1190, 0x2011, 0x1820, 0x2214, 0x9294,
+ 0xff00, 0x9584, 0xff00, 0x9206, 0x1148, 0x2011, 0x1820, 0x2214,
+ 0x9294, 0x00ff, 0x9584, 0x00ff, 0x9206, 0x1120, 0x2500, 0x080c,
+ 0x8256, 0x0048, 0x9584, 0x00ff, 0x9080, 0x33b9, 0x200d, 0x918c,
+ 0xff00, 0x810f, 0x9006, 0x0005, 0x9080, 0x33b9, 0x200d, 0x918c,
+ 0x00ff, 0x0005, 0x00d6, 0x2069, 0x0140, 0x2001, 0x1818, 0x2003,
+ 0x00ef, 0x20a9, 0x0010, 0x9006, 0x6852, 0x6856, 0x1f04, 0x26a5,
+ 0x00de, 0x0005, 0x0006, 0x00d6, 0x0026, 0x2069, 0x0140, 0x2001,
+ 0x1818, 0x2102, 0x8114, 0x8214, 0x8214, 0x8214, 0x20a9, 0x0010,
+ 0x6853, 0x0000, 0x9006, 0x82ff, 0x1128, 0x9184, 0x000f, 0x9080,
+ 0xe8b1, 0x2005, 0x6856, 0x8211, 0x1f04, 0x26ba, 0x002e, 0x00de,
+ 0x000e, 0x0005, 0x00c6, 0x2061, 0x1800, 0x6030, 0x0110, 0xc09d,
+ 0x0008, 0xc09c, 0x6032, 0x00ce, 0x0005, 0x0156, 0x00d6, 0x0026,
+ 0x0016, 0x0006, 0x2069, 0x0140, 0x6980, 0x9116, 0x0180, 0x9112,
+ 0x1230, 0x8212, 0x8210, 0x22a8, 0x2001, 0x0402, 0x0018, 0x22a8,
+ 0x2001, 0x0404, 0x680e, 0x1f04, 0x26ea, 0x680f, 0x0000, 0x000e,
+ 0x001e, 0x002e, 0x00de, 0x015e, 0x0005, 0x080c, 0x573e, 0xd0c4,
+ 0x0150, 0xd0a4, 0x0140, 0x9006, 0x0046, 0x2020, 0x2009, 0x002e,
+ 0x080c, 0xe445, 0x004e, 0x0005, 0x00f6, 0x0016, 0x0026, 0x2079,
+ 0x0140, 0x78c4, 0xd0dc, 0x0904, 0x2761, 0x080c, 0x29dd, 0x0660,
+ 0x9084, 0x0700, 0x908e, 0x0600, 0x1120, 0x2011, 0x4000, 0x900e,
+ 0x0458, 0x908e, 0x0500, 0x1120, 0x2011, 0x8000, 0x900e, 0x0420,
+ 0x908e, 0x0400, 0x1120, 0x9016, 0x2009, 0x0001, 0x00e8, 0x908e,
+ 0x0300, 0x1120, 0x9016, 0x2009, 0x0002, 0x00b0, 0x908e, 0x0200,
+ 0x1120, 0x9016, 0x2009, 0x0004, 0x0078, 0x908e, 0x0100, 0x1548,
+ 0x9016, 0x2009, 0x0008, 0x0040, 0x9084, 0x0700, 0x908e, 0x0300,
+ 0x1500, 0x2011, 0x0030, 0x0058, 0x2300, 0x9080, 0x0020, 0x2018,
+ 0x080c, 0x91f8, 0x928c, 0xff00, 0x0110, 0x2011, 0x00ff, 0x2200,
+ 0x8007, 0x9085, 0x004c, 0x78c2, 0x2009, 0x0138, 0x220a, 0x080c,
+ 0x753d, 0x1118, 0x2009, 0x196c, 0x220a, 0x002e, 0x001e, 0x00fe,
+ 0x0005, 0x78c3, 0x0000, 0x0cc8, 0x0126, 0x2091, 0x2800, 0x0006,
+ 0x0016, 0x0026, 0x2001, 0x0170, 0x200c, 0x8000, 0x2014, 0x9184,
+ 0x0003, 0x0110, 0x080c, 0x0d76, 0x002e, 0x001e, 0x000e, 0x012e,
+ 0x0005, 0x2001, 0x0171, 0x2004, 0xd0dc, 0x0168, 0x2001, 0x0170,
+ 0x200c, 0x918c, 0x00ff, 0x918e, 0x004c, 0x1128, 0x200c, 0x918c,
+ 0xff00, 0x810f, 0x0005, 0x900e, 0x2001, 0x0227, 0x2004, 0x8007,
+ 0x9084, 0x00ff, 0x8004, 0x9108, 0x2001, 0x0226, 0x2004, 0x8007,
+ 0x9084, 0x00ff, 0x8004, 0x9108, 0x0005, 0x0018, 0x000c, 0x0018,
+ 0x0020, 0x1000, 0x0800, 0x1000, 0x1800, 0x0156, 0x0006, 0x0016,
+ 0x0026, 0x00e6, 0x2001, 0x198f, 0x2004, 0x908a, 0x0007, 0x1a0c,
+ 0x0d7d, 0x0033, 0x00ee, 0x002e, 0x001e, 0x000e, 0x015e, 0x0005,
+ 0x27bf, 0x27dd, 0x2801, 0x2803, 0x282c, 0x282e, 0x2830, 0x2001,
+ 0x0001, 0x080c, 0x2606, 0x080c, 0x2a2b, 0x2001, 0x1991, 0x2003,
+ 0x0000, 0x7828, 0x9084, 0xe1d7, 0x782a, 0x9006, 0x20a9, 0x0009,
+ 0x080c, 0x29f9, 0x2001, 0x198f, 0x2003, 0x0006, 0x2009, 0x001e,
+ 0x2011, 0x2831, 0x080c, 0x8792, 0x0005, 0x2009, 0x1994, 0x200b,
+ 0x0000, 0x2001, 0x1999, 0x2003, 0x0036, 0x2001, 0x1998, 0x2003,
+ 0x002a, 0x2001, 0x1991, 0x2003, 0x0001, 0x9006, 0x080c, 0x298e,
+ 0x2001, 0xffff, 0x20a9, 0x0009, 0x080c, 0x29f9, 0x2001, 0x198f,
+ 0x2003, 0x0006, 0x2009, 0x001e, 0x2011, 0x2831, 0x080c, 0x8792,
+ 0x0005, 0x080c, 0x0d7d, 0x2001, 0x1999, 0x2003, 0x0036, 0x2001,
+ 0x1991, 0x2003, 0x0003, 0x7a38, 0x9294, 0x0005, 0x9296, 0x0004,
+ 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x080c, 0x298e, 0x2001,
+ 0x1995, 0x2003, 0x0000, 0x2001, 0xffff, 0x20a9, 0x0009, 0x080c,
+ 0x29f9, 0x2001, 0x198f, 0x2003, 0x0006, 0x2009, 0x001e, 0x2011,
+ 0x2831, 0x080c, 0x8792, 0x0005, 0x080c, 0x0d7d, 0x080c, 0x0d7d,
+ 0x0005, 0x0006, 0x0016, 0x0026, 0x00e6, 0x00f6, 0x0156, 0x0126,
+ 0x2091, 0x8000, 0x2079, 0x0100, 0x2001, 0x1991, 0x2004, 0x908a,
+ 0x0007, 0x1a0c, 0x0d7d, 0x0043, 0x012e, 0x015e, 0x00fe, 0x00ee,
+ 0x002e, 0x001e, 0x000e, 0x0005, 0x2853, 0x2873, 0x28b3, 0x28e3,
+ 0x2907, 0x2917, 0x2919, 0x080c, 0x29ed, 0x11b0, 0x7850, 0x9084,
+ 0xefff, 0x7852, 0x2009, 0x1997, 0x2104, 0x7a38, 0x9294, 0x0005,
+ 0x9296, 0x0004, 0x0110, 0xc08d, 0x0008, 0xc085, 0x200a, 0x2001,
+ 0x198f, 0x2003, 0x0001, 0x0030, 0x080c, 0x293d, 0x2001, 0xffff,
+ 0x080c, 0x27ce, 0x0005, 0x080c, 0x291b, 0x05e0, 0x2009, 0x1998,
+ 0x2104, 0x8001, 0x200a, 0x080c, 0x29ed, 0x1178, 0x7850, 0x9084,
+ 0xefff, 0x7852, 0x7a38, 0x9294, 0x0005, 0x9296, 0x0005, 0x0518,
+ 0x2009, 0x1997, 0x2104, 0xc085, 0x200a, 0x2009, 0x1994, 0x2104,
+ 0x8000, 0x200a, 0x9086, 0x0005, 0x0118, 0x080c, 0x2923, 0x00c0,
+ 0x200b, 0x0000, 0x7a38, 0x9294, 0x0006, 0x9296, 0x0004, 0x0110,
+ 0x9006, 0x0010, 0x2001, 0x0001, 0x080c, 0x29ab, 0x2001, 0x1991,
+ 0x2003, 0x0002, 0x0028, 0x2001, 0x198f, 0x2003, 0x0003, 0x0010,
+ 0x080c, 0x27f0, 0x0005, 0x080c, 0x291b, 0x0560, 0x2009, 0x1998,
+ 0x2104, 0x8001, 0x200a, 0x080c, 0x29ed, 0x1168, 0x7850, 0x9084,
+ 0xefff, 0x7852, 0x2001, 0x198f, 0x2003, 0x0003, 0x2001, 0x1990,
+ 0x2003, 0x0000, 0x00b8, 0x2009, 0x1998, 0x2104, 0x9005, 0x1118,
+ 0x080c, 0x2960, 0x0010, 0x080c, 0x2930, 0x080c, 0x2923, 0x2009,
+ 0x1994, 0x200b, 0x0000, 0x2001, 0x1991, 0x2003, 0x0001, 0x080c,
+ 0x27f0, 0x0000, 0x0005, 0x04b9, 0x0508, 0x080c, 0x29ed, 0x11b8,
+ 0x7850, 0x9084, 0xefff, 0x7852, 0x2009, 0x1995, 0x2104, 0x8000,
+ 0x200a, 0x9086, 0x0007, 0x0108, 0x0078, 0x2001, 0x199a, 0x2003,
+ 0x000a, 0x2009, 0x1997, 0x2104, 0xc0fd, 0x200a, 0x0038, 0x0419,
+ 0x2001, 0x1991, 0x2003, 0x0004, 0x080c, 0x281b, 0x0005, 0x0099,
+ 0x0168, 0x080c, 0x29ed, 0x1138, 0x7850, 0x9084, 0xefff, 0x7852,
+ 0x080c, 0x2807, 0x0018, 0x0079, 0x080c, 0x281b, 0x0005, 0x080c,
+ 0x0d7d, 0x080c, 0x0d7d, 0x2009, 0x1999, 0x2104, 0x8001, 0x200a,
+ 0x090c, 0x297c, 0x0005, 0x7a38, 0x9294, 0x0005, 0x9296, 0x0005,
+ 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x080c, 0x29ab, 0x0005,
+ 0x7a38, 0x9294, 0x0006, 0x9296, 0x0006, 0x0110, 0x9006, 0x0010,
+ 0x2001, 0x0001, 0x080c, 0x298e, 0x0005, 0x2009, 0x1994, 0x2104,
+ 0x8000, 0x200a, 0x9086, 0x0005, 0x0108, 0x0068, 0x200b, 0x0000,
+ 0x7a38, 0x9294, 0x0006, 0x9296, 0x0006, 0x0110, 0x9006, 0x0010,
+ 0x2001, 0x0001, 0x04d9, 0x7a38, 0x9294, 0x0005, 0x9296, 0x0005,
+ 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x080c, 0x29ab, 0x0005,
+ 0x0086, 0x2001, 0x1997, 0x2004, 0x9084, 0x7fff, 0x090c, 0x0d7d,
+ 0x2009, 0x1996, 0x2144, 0x8846, 0x280a, 0x9844, 0x0dd8, 0xd08c,
+ 0x1120, 0xd084, 0x1120, 0x080c, 0x0d7d, 0x9006, 0x0010, 0x2001,
+ 0x0001, 0x00a1, 0x008e, 0x0005, 0x0006, 0x0156, 0x2001, 0x198f,
+ 0x20a9, 0x0009, 0x2003, 0x0000, 0x8000, 0x1f04, 0x2982, 0x2001,
+ 0x1996, 0x2003, 0x8000, 0x015e, 0x000e, 0x0005, 0x00f6, 0x2079,
+ 0x0100, 0x9085, 0x0000, 0x0158, 0x7838, 0x9084, 0xfff9, 0x9085,
+ 0x0004, 0x783a, 0x2009, 0x199c, 0x210c, 0x795a, 0x0050, 0x7838,
+ 0x9084, 0xfffb, 0x9085, 0x0006, 0x783a, 0x2009, 0x199d, 0x210c,
+ 0x795a, 0x00fe, 0x0005, 0x00f6, 0x2079, 0x0100, 0x9085, 0x0000,
+ 0x0158, 0x7838, 0x9084, 0xfffa, 0x9085, 0x0004, 0x783a, 0x7850,
+ 0x9084, 0xfff0, 0x7852, 0x00f8, 0x7838, 0x9084, 0xfffb, 0x9085,
+ 0x0005, 0x783a, 0x7850, 0x9084, 0xfff0, 0x0016, 0x2009, 0x017f,
+ 0x210c, 0x918e, 0x0005, 0x0140, 0x2009, 0x0003, 0x210c, 0x918c,
+ 0x0600, 0x918e, 0x0400, 0x0118, 0x9085, 0x000a, 0x0010, 0x9085,
+ 0x0000, 0x001e, 0x7852, 0x00fe, 0x0005, 0x0006, 0x2001, 0x0100,
+ 0x2004, 0x9082, 0x0007, 0x000e, 0x0005, 0x0006, 0x2001, 0x0100,
+ 0x2004, 0x9082, 0x0009, 0x000e, 0x0005, 0x0156, 0x20a9, 0x0064,
+ 0x7820, 0x080c, 0x2a60, 0xd09c, 0x1110, 0x1f04, 0x29f0, 0x015e,
+ 0x0005, 0x0126, 0x0016, 0x0006, 0x2091, 0x8000, 0x080c, 0x2ad7,
+ 0x080c, 0x2b0a, 0x000e, 0x2008, 0x9186, 0x0000, 0x1118, 0x783b,
+ 0x0007, 0x0090, 0x9186, 0x0001, 0x1118, 0x783b, 0x0006, 0x0060,
+ 0x9186, 0x0002, 0x1118, 0x783b, 0x0005, 0x0030, 0x9186, 0x0003,
+ 0x1118, 0x783b, 0x0004, 0x0000, 0x0006, 0x1d04, 0x2a1d, 0x080c,
+ 0x87b4, 0x1f04, 0x2a1d, 0x7850, 0x9085, 0x1000, 0x7852, 0x000e,
+ 0x001e, 0x012e, 0x0005, 0x080c, 0x2b0a, 0x0005, 0x0006, 0x0156,
+ 0x00f6, 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854, 0xd0ac, 0x1100,
+ 0x7854, 0xd08c, 0x1110, 0x1f04, 0x2a38, 0x00fe, 0x015e, 0x000e,
+ 0x0005, 0x1d04, 0x2a41, 0x080c, 0x87b4, 0x1f04, 0x2a41, 0x0005,
+ 0x0006, 0x2001, 0x199b, 0x2004, 0x9086, 0x0000, 0x000e, 0x0005,
+ 0x0006, 0x2001, 0x199b, 0x2004, 0x9086, 0x0001, 0x000e, 0x0005,
+ 0x0006, 0x2001, 0x199b, 0x2004, 0x9086, 0x0002, 0x000e, 0x0005,
+ 0xa001, 0xa001, 0xa001, 0xa001, 0xa001, 0x0005, 0x0006, 0x2001,
+ 0x19a7, 0x2102, 0x000e, 0x0005, 0x2009, 0x0171, 0x2104, 0xd0dc,
+ 0x0140, 0x2009, 0x0170, 0x2104, 0x200b, 0x0080, 0xa001, 0xa001,
+ 0x200a, 0x0005, 0x0016, 0x0026, 0x080c, 0x7557, 0x0108, 0xc0bc,
+ 0x2009, 0x0140, 0x2114, 0x9294, 0x0001, 0x9215, 0x220a, 0x002e,
+ 0x001e, 0x0005, 0x0016, 0x0026, 0x2009, 0x0140, 0x2114, 0x9294,
+ 0x0001, 0x9285, 0x1000, 0x200a, 0x220a, 0x002e, 0x001e, 0x0005,
+ 0x0016, 0x0026, 0x2009, 0x0140, 0x2114, 0x9294, 0x0001, 0x9215,
+ 0x220a, 0x002e, 0x001e, 0x0005, 0x0006, 0x0016, 0x2009, 0x0140,
+ 0x2104, 0x1128, 0x080c, 0x7557, 0x0110, 0xc0bc, 0x0008, 0xc0bd,
+ 0x200a, 0x001e, 0x000e, 0x0005, 0x00f6, 0x2079, 0x0380, 0x7843,
+ 0x0101, 0x7844, 0xd084, 0x1de8, 0x2001, 0x0109, 0x2202, 0x7843,
+ 0x0100, 0x00fe, 0x0005, 0x00f6, 0x2079, 0x0380, 0x7843, 0x0202,
+ 0x7844, 0xd08c, 0x1de8, 0x2079, 0x0100, 0x7814, 0x9104, 0x9205,
+ 0x7a16, 0x2079, 0x0380, 0x7843, 0x0200, 0x00fe, 0x0005, 0x0016,
+ 0x0026, 0x0036, 0x00c6, 0x2061, 0x0100, 0x6050, 0x9084, 0xfbff,
+ 0x9085, 0x0040, 0x6052, 0x20a9, 0x0002, 0x080c, 0x2a41, 0x6050,
+ 0x9085, 0x0400, 0x9084, 0xff9f, 0x6052, 0x20a9, 0x0005, 0x080c,
+ 0x2a41, 0x6054, 0xd0bc, 0x090c, 0x0d7d, 0x20a9, 0x0005, 0x080c,
+ 0x2a41, 0x6054, 0xd0ac, 0x090c, 0x0d7d, 0x2009, 0x19ae, 0x9084,
+ 0x7e00, 0x8007, 0x8004, 0x8004, 0x200a, 0x00ce, 0x003e, 0x002e,
+ 0x001e, 0x0005, 0x0006, 0x00c6, 0x2061, 0x0100, 0x6050, 0xc0cd,
+ 0x6052, 0x00ce, 0x000e, 0x0005, 0x3010, 0x3010, 0x2c14, 0x2c14,
+ 0x2c20, 0x2c20, 0x2c2c, 0x2c2c, 0x2c3a, 0x2c3a, 0x2c46, 0x2c46,
+ 0x2c54, 0x2c54, 0x2c62, 0x2c62, 0x2c74, 0x2c74, 0x2c80, 0x2c80,
+ 0x2c8e, 0x2c8e, 0x2cac, 0x2cac, 0x2ccc, 0x2ccc, 0x2c9c, 0x2c9c,
+ 0x2cbc, 0x2cbc, 0x2cda, 0x2cda, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2cec, 0x2cec, 0x2cf8, 0x2cf8,
+ 0x2d06, 0x2d06, 0x2d14, 0x2d14, 0x2d24, 0x2d24, 0x2d32, 0x2d32,
+ 0x2d42, 0x2d42, 0x2d52, 0x2d52, 0x2d64, 0x2d64, 0x2d72, 0x2d72,
+ 0x2d82, 0x2d82, 0x2da4, 0x2da4, 0x2dc8, 0x2dc8, 0x2d92, 0x2d92,
+ 0x2db6, 0x2db6, 0x2dd8, 0x2dd8, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2dec, 0x2dec, 0x2df8, 0x2df8,
+ 0x2e06, 0x2e06, 0x2e14, 0x2e14, 0x2e24, 0x2e24, 0x2e32, 0x2e32,
+ 0x2e42, 0x2e42, 0x2e52, 0x2e52, 0x2e64, 0x2e64, 0x2e72, 0x2e72,
+ 0x2e82, 0x2e82, 0x2e92, 0x2e92, 0x2ea4, 0x2ea4, 0x2eb4, 0x2eb4,
+ 0x2ec6, 0x2ec6, 0x2ed8, 0x2ed8, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2eec, 0x2eec, 0x2efa, 0x2efa,
+ 0x2f0a, 0x2f0a, 0x2f1a, 0x2f1a, 0x2f2c, 0x2f2c, 0x2f3c, 0x2f3c,
+ 0x2f4e, 0x2f4e, 0x2f60, 0x2f60, 0x2f74, 0x2f74, 0x2f84, 0x2f84,
+ 0x2f96, 0x2f96, 0x2fa8, 0x2fa8, 0x2fbc, 0x2fbc, 0x2fcd, 0x2fcd,
+ 0x2fe0, 0x2fe0, 0x2ff3, 0x2ff3, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x2c72,
+ 0x2c72, 0x2c72, 0x2c72, 0x2c72, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x22aa, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x20d4, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0x22aa,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x22aa,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0x22aa, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0xa001, 0x0cf0, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x13bb, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x22aa, 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4,
+ 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0x22aa,
+ 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x13bb, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2764, 0x080c, 0x22aa, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764,
+ 0x080c, 0x20d4, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4,
+ 0x080c, 0x22aa, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x22aa, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0x22aa,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x13bb,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0x13bb,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4,
+ 0x080c, 0x22aa, 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764,
+ 0x080c, 0x20d4, 0x080c, 0x13bb, 0x080c, 0x20fe, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2764, 0x080c, 0x13bb, 0x080c, 0x20fe, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0xa984, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0xa984, 0x080c, 0x22aa, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4,
+ 0x080c, 0xa984, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0xa984,
+ 0x080c, 0x22aa, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0xa984, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0xa984, 0x080c, 0x22aa, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x20fe,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x22aa,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0xa984, 0x080c, 0x13bb,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0xa984, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x13bb,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x22aa,
+ 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0xa984, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0xa984, 0x080c, 0x22aa,
+ 0x080c, 0x13bb, 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x20d4,
+ 0x080c, 0xa984, 0x080c, 0x13bb, 0x080c, 0x20fe, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x22aa, 0x080c, 0x13bb,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984, 0x080c, 0x22aa,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0xa984,
+ 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0xa984,
+ 0x080c, 0x22aa, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984,
+ 0x080c, 0x22aa, 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764,
+ 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x20fe, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x22aa,
+ 0x080c, 0x20fe, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984,
+ 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984,
+ 0x080c, 0x22aa, 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006,
+ 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764,
+ 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x13bb, 0x0804, 0x3008,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0xa984, 0x080c, 0x22aa,
+ 0x080c, 0x13bb, 0x0804, 0x3008, 0x0106, 0x0006, 0x0126, 0x01c6,
+ 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0xa984,
+ 0x080c, 0x13bb, 0x080c, 0x20fe, 0x04d8, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c,
+ 0xa984, 0x080c, 0x22aa, 0x080c, 0x13bb, 0x080c, 0x20fe, 0x0440,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c, 0x13bb, 0x080c, 0xa984,
+ 0x080c, 0x20fe, 0x00a8, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6,
+ 0x0136, 0x0146, 0x0156, 0x080c, 0x2764, 0x080c, 0x20d4, 0x080c,
+ 0xa984, 0x080c, 0x22aa, 0x080c, 0x13bb, 0x080c, 0x20fe, 0x0000,
+ 0x015e, 0x014e, 0x013e, 0x01de, 0x01ce, 0x012e, 0x000e, 0x010e,
+ 0x000d, 0x00b6, 0x00c6, 0x0026, 0x0046, 0x9026, 0x080c, 0x6a9b,
+ 0x1904, 0x3121, 0x72dc, 0x2001, 0x197b, 0x2004, 0x9005, 0x1110,
+ 0xd29c, 0x0148, 0xd284, 0x1138, 0xd2bc, 0x1904, 0x3121, 0x080c,
+ 0x3126, 0x0804, 0x3121, 0xd2cc, 0x1904, 0x3121, 0x080c, 0x753d,
+ 0x1120, 0x70af, 0xffff, 0x0804, 0x3121, 0xd294, 0x0120, 0x70af,
+ 0xffff, 0x0804, 0x3121, 0x080c, 0x33a8, 0x0160, 0x080c, 0xd09b,
+ 0x0128, 0x2001, 0x1818, 0x203c, 0x0804, 0x30ae, 0x70af, 0xffff,
+ 0x0804, 0x3121, 0x2001, 0x1818, 0x203c, 0x7294, 0xd284, 0x0904,
+ 0x30ae, 0xd28c, 0x1904, 0x30ae, 0x0036, 0x73ac, 0x938e, 0xffff,
+ 0x1110, 0x2019, 0x0001, 0x8314, 0x92e0, 0x1d80, 0x2c04, 0x938c,
+ 0x0001, 0x0120, 0x9084, 0xff00, 0x8007, 0x0010, 0x9084, 0x00ff,
+ 0x970e, 0x05d0, 0x908e, 0x0000, 0x05b8, 0x908e, 0x00ff, 0x1150,
+ 0x7230, 0xd284, 0x15b0, 0x7294, 0xc28d, 0x7296, 0x70af, 0xffff,
+ 0x003e, 0x04a0, 0x900e, 0x080c, 0x2661, 0x080c, 0x6632, 0x1538,
+ 0x9006, 0xb8bb, 0x0520, 0xb8ac, 0x9005, 0x0148, 0x00c6, 0x2060,
+ 0x080c, 0x8c1f, 0x00ce, 0x090c, 0x8fbc, 0xb8af, 0x0000, 0x080c,
+ 0x6add, 0x1168, 0x7030, 0xd08c, 0x0130, 0xb800, 0xd0bc, 0x0138,
+ 0x080c, 0x6986, 0x0120, 0x080c, 0x313f, 0x0148, 0x0028, 0x080c,
+ 0x328b, 0x080c, 0x316b, 0x0118, 0x8318, 0x0804, 0x305b, 0x73ae,
+ 0x0010, 0x70af, 0xffff, 0x003e, 0x0804, 0x3121, 0x9780, 0x33b9,
+ 0x203d, 0x97bc, 0xff00, 0x873f, 0x2041, 0x007e, 0x70ac, 0x9096,
+ 0xffff, 0x1118, 0x900e, 0x28a8, 0x0050, 0x9812, 0x0220, 0x2008,
+ 0x9802, 0x20a8, 0x0020, 0x70af, 0xffff, 0x0804, 0x3121, 0x2700,
+ 0x0156, 0x0016, 0x9106, 0x0904, 0x3116, 0xc484, 0x080c, 0x6693,
+ 0x0148, 0x080c, 0xd09b, 0x1904, 0x3116, 0x080c, 0x6632, 0x1904,
+ 0x311e, 0x0008, 0xc485, 0xb8bb, 0x0520, 0xb8ac, 0x9005, 0x0148,
+ 0x00c6, 0x2060, 0x080c, 0x8c1f, 0x00ce, 0x090c, 0x8fbc, 0xb8af,
+ 0x0000, 0x080c, 0x6add, 0x1130, 0x7030, 0xd08c, 0x01f8, 0xb800,
+ 0xd0bc, 0x11e0, 0x7294, 0xd28c, 0x0180, 0x080c, 0x6add, 0x9082,
+ 0x0006, 0x02e0, 0xd484, 0x1118, 0x080c, 0x6657, 0x0028, 0x080c,
+ 0x331e, 0x01a0, 0x080c, 0x3349, 0x0088, 0x080c, 0x328b, 0x080c,
+ 0xd09b, 0x1160, 0x080c, 0x316b, 0x0188, 0x0040, 0x080c, 0xd09b,
+ 0x1118, 0x080c, 0x331e, 0x0110, 0x0451, 0x0140, 0x001e, 0x8108,
+ 0x015e, 0x1f04, 0x30c7, 0x70af, 0xffff, 0x0018, 0x001e, 0x015e,
+ 0x71ae, 0x004e, 0x002e, 0x00ce, 0x00be, 0x0005, 0x00c6, 0x0016,
+ 0x70af, 0x0001, 0x2009, 0x007e, 0x080c, 0x6632, 0x1168, 0xb813,
+ 0x00ff, 0xb817, 0xfffe, 0x080c, 0x328b, 0x04a9, 0x0128, 0x70dc,
+ 0xc0bd, 0x70de, 0x080c, 0xcde8, 0x001e, 0x00ce, 0x0005, 0x0016,
+ 0x0076, 0x00d6, 0x00c6, 0x2001, 0x184c, 0x2004, 0x9084, 0x00ff,
+ 0xb842, 0x080c, 0xad20, 0x01d0, 0x2b00, 0x6012, 0x080c, 0xce15,
+ 0x6023, 0x0001, 0x9006, 0x080c, 0x65cf, 0x2001, 0x0000, 0x080c,
+ 0x65e3, 0x0126, 0x2091, 0x8000, 0x70a8, 0x8000, 0x70aa, 0x012e,
+ 0x2009, 0x0004, 0x080c, 0xad4d, 0x9085, 0x0001, 0x00ce, 0x00de,
+ 0x007e, 0x001e, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x2001,
+ 0x184c, 0x2004, 0x9084, 0x00ff, 0xb842, 0x080c, 0xad20, 0x0548,
+ 0x2b00, 0x6012, 0xb800, 0xc0c4, 0xb802, 0xb8a0, 0x9086, 0x007e,
+ 0x0140, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x1110, 0x080c,
+ 0x3240, 0x080c, 0xce15, 0x6023, 0x0001, 0x9006, 0x080c, 0x65cf,
+ 0x2001, 0x0002, 0x080c, 0x65e3, 0x0126, 0x2091, 0x8000, 0x70a8,
+ 0x8000, 0x70aa, 0x012e, 0x2009, 0x0002, 0x080c, 0xad4d, 0x9085,
+ 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005, 0x00b6, 0x00c6,
+ 0x0026, 0x2009, 0x0080, 0x080c, 0x6632, 0x1140, 0xb813, 0x00ff,
+ 0xb817, 0xfffc, 0x0039, 0x0110, 0x70e3, 0xffff, 0x002e, 0x00ce,
+ 0x00be, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x080c, 0xac5a,
+ 0x01d0, 0x2b00, 0x6012, 0x080c, 0xce15, 0x6023, 0x0001, 0x9006,
+ 0x080c, 0x65cf, 0x2001, 0x0002, 0x080c, 0x65e3, 0x0126, 0x2091,
+ 0x8000, 0x70e4, 0x8000, 0x70e6, 0x012e, 0x2009, 0x0002, 0x080c,
+ 0xad4d, 0x9085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005,
+ 0x00c6, 0x00d6, 0x0126, 0x2091, 0x8000, 0x2009, 0x007f, 0x080c,
+ 0x6632, 0x11b8, 0xb813, 0x00ff, 0xb817, 0xfffd, 0xb8d7, 0x0004,
+ 0x080c, 0xac5a, 0x0170, 0x2b00, 0x6012, 0x6316, 0x6023, 0x0001,
+ 0x620a, 0x080c, 0xce15, 0x2009, 0x0022, 0x080c, 0xad4d, 0x9085,
+ 0x0001, 0x012e, 0x00de, 0x00ce, 0x0005, 0x00e6, 0x00c6, 0x0066,
+ 0x0036, 0x0026, 0x00b6, 0x21f0, 0x080c, 0xa91e, 0x0106, 0x080c,
+ 0x9448, 0x080c, 0x93b9, 0x080c, 0xa86f, 0x080c, 0xbbf9, 0x010e,
+ 0x090c, 0xa93a, 0x3e08, 0x2130, 0x81ff, 0x0120, 0x20a9, 0x007e,
+ 0x900e, 0x0018, 0x20a9, 0x007f, 0x900e, 0x0016, 0x080c, 0x6693,
+ 0x1140, 0x9686, 0x0002, 0x1118, 0xb800, 0xd0bc, 0x1110, 0x080c,
+ 0x60ac, 0x001e, 0x8108, 0x1f04, 0x3225, 0x9686, 0x0001, 0x190c,
+ 0x337c, 0x00be, 0x002e, 0x003e, 0x006e, 0x00ce, 0x00ee, 0x0005,
+ 0x00e6, 0x00c6, 0x0046, 0x0036, 0x0026, 0x0016, 0x00b6, 0x080c,
+ 0xa91e, 0x0106, 0x6210, 0x2258, 0xbaa0, 0x0026, 0x2019, 0x0029,
+ 0x080c, 0x943d, 0x0076, 0x2039, 0x0000, 0x080c, 0x9306, 0x2c08,
+ 0x080c, 0xe167, 0x007e, 0x001e, 0x010e, 0x090c, 0xa93a, 0xba10,
+ 0xbb14, 0xbc84, 0x080c, 0x60ac, 0xba12, 0xbb16, 0xbc86, 0x00be,
+ 0x001e, 0x002e, 0x003e, 0x004e, 0x00ce, 0x00ee, 0x0005, 0x00e6,
+ 0x0006, 0x00b6, 0x6010, 0x2058, 0xb8a0, 0x00be, 0x9086, 0x0080,
+ 0x0150, 0x2071, 0x1800, 0x70a8, 0x9005, 0x0110, 0x8001, 0x70aa,
+ 0x000e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x70e4, 0x9005, 0x0dc0,
+ 0x8001, 0x70e6, 0x0ca8, 0xb800, 0xc08c, 0xb802, 0x0005, 0x00f6,
+ 0x00e6, 0x00c6, 0x00b6, 0x0046, 0x0036, 0x0026, 0x0016, 0x0156,
+ 0x2178, 0x080c, 0xa91e, 0x0106, 0x81ff, 0x1118, 0x20a9, 0x0001,
+ 0x0078, 0x080c, 0x573e, 0xd0c4, 0x0140, 0xd0a4, 0x0130, 0x9006,
+ 0x2020, 0x2009, 0x002d, 0x080c, 0xe445, 0x20a9, 0x0800, 0x9016,
+ 0x0026, 0x928e, 0x007e, 0x0904, 0x32fa, 0x928e, 0x007f, 0x0904,
+ 0x32fa, 0x928e, 0x0080, 0x05f0, 0x9288, 0x1000, 0x210c, 0x81ff,
+ 0x05c8, 0x8fff, 0x1150, 0x2001, 0x198d, 0x0006, 0x2003, 0x0001,
+ 0x080c, 0x330b, 0x000e, 0x2003, 0x0000, 0x00b6, 0x00c6, 0x2158,
+ 0x2001, 0x0001, 0x080c, 0x6aa7, 0x00ce, 0x00be, 0x2019, 0x0029,
+ 0x080c, 0x943d, 0x0076, 0x2039, 0x0000, 0x080c, 0x9306, 0x00b6,
+ 0x00c6, 0x0026, 0x2158, 0xba04, 0x9294, 0x00ff, 0x9286, 0x0006,
+ 0x1118, 0xb807, 0x0404, 0x0028, 0x2001, 0x0004, 0x8007, 0x9215,
+ 0xba06, 0x002e, 0x00ce, 0x00be, 0x0016, 0x2c08, 0x080c, 0xe167,
+ 0x001e, 0x007e, 0x002e, 0x8210, 0x1f04, 0x32b0, 0x010e, 0x090c,
+ 0xa93a, 0x015e, 0x001e, 0x002e, 0x003e, 0x004e, 0x00be, 0x00ce,
+ 0x00ee, 0x00fe, 0x0005, 0x0046, 0x0026, 0x0016, 0x080c, 0x573e,
+ 0xd0c4, 0x0140, 0xd0a4, 0x0130, 0x9006, 0x2220, 0x2009, 0x0029,
+ 0x080c, 0xe445, 0x001e, 0x002e, 0x004e, 0x0005, 0x0016, 0x0026,
+ 0x0036, 0x00c6, 0x7294, 0x82ff, 0x01e8, 0x080c, 0x6ad5, 0x11d0,
+ 0x2100, 0x080c, 0x2694, 0x81ff, 0x01b8, 0x2019, 0x0001, 0x8314,
+ 0x92e0, 0x1d80, 0x2c04, 0xd384, 0x0120, 0x9084, 0xff00, 0x8007,
+ 0x0010, 0x9084, 0x00ff, 0x9116, 0x0138, 0x9096, 0x00ff, 0x0110,
+ 0x8318, 0x0c68, 0x9085, 0x0001, 0x00ce, 0x003e, 0x002e, 0x001e,
+ 0x0005, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xa91e,
+ 0x0106, 0x0036, 0x2019, 0x0029, 0x00c1, 0x003e, 0x010e, 0x090c,
+ 0xa93a, 0x9180, 0x1000, 0x2004, 0x9065, 0x0158, 0x0016, 0x00c6,
+ 0x2061, 0x1b34, 0x001e, 0x6112, 0x080c, 0x3240, 0x001e, 0x080c,
+ 0x6657, 0x012e, 0x00ce, 0x001e, 0x0005, 0x0016, 0x0026, 0x2110,
+ 0x080c, 0xa404, 0x080c, 0xe7ac, 0x002e, 0x001e, 0x0005, 0x2001,
+ 0x1837, 0x2004, 0xd0cc, 0x0005, 0x00c6, 0x00b6, 0x080c, 0x753d,
+ 0x1118, 0x20a9, 0x0800, 0x0010, 0x20a9, 0x0782, 0x080c, 0x753d,
+ 0x1110, 0x900e, 0x0010, 0x2009, 0x007e, 0x9180, 0x1000, 0x2004,
+ 0x905d, 0x0130, 0x86ff, 0x0110, 0xb800, 0xd0bc, 0x090c, 0x6657,
+ 0x8108, 0x1f04, 0x338d, 0x2061, 0x1800, 0x607f, 0x0000, 0x6080,
+ 0x9084, 0x00ff, 0x6082, 0x60b3, 0x0000, 0x00be, 0x00ce, 0x0005,
+ 0x2001, 0x1869, 0x2004, 0xd0bc, 0x0005, 0x2011, 0x1848, 0x2214,
+ 0xd2ec, 0x0005, 0x0026, 0x2011, 0x1867, 0x2214, 0xd2dc, 0x002e,
+ 0x0005, 0x7eef, 0x7de8, 0x7ce4, 0x80e2, 0x7be1, 0x80e0, 0x80dc,
+ 0x80da, 0x7ad9, 0x80d6, 0x80d5, 0x80d4, 0x80d3, 0x80d2, 0x80d1,
+ 0x79ce, 0x78cd, 0x80cc, 0x80cb, 0x80ca, 0x80c9, 0x80c7, 0x80c6,
+ 0x77c5, 0x76c3, 0x80bc, 0x80ba, 0x75b9, 0x80b6, 0x74b5, 0x73b4,
+ 0x72b3, 0x80b2, 0x80b1, 0x80ae, 0x71ad, 0x80ac, 0x70ab, 0x6faa,
+ 0x6ea9, 0x80a7, 0x6da6, 0x6ca5, 0x6ba3, 0x6a9f, 0x699e, 0x689d,
+ 0x809b, 0x8098, 0x6797, 0x6690, 0x658f, 0x6488, 0x6384, 0x6282,
+ 0x8081, 0x8080, 0x617c, 0x607a, 0x8079, 0x5f76, 0x8075, 0x8074,
+ 0x8073, 0x8072, 0x8071, 0x806e, 0x5e6d, 0x806c, 0x5d6b, 0x5c6a,
+ 0x5b69, 0x8067, 0x5a66, 0x5965, 0x5863, 0x575c, 0x565a, 0x5559,
+ 0x8056, 0x8055, 0x5454, 0x5353, 0x5252, 0x5151, 0x504e, 0x4f4d,
+ 0x804c, 0x804b, 0x4e4a, 0x4d49, 0x8047, 0x4c46, 0x8045, 0x8043,
+ 0x803c, 0x803a, 0x8039, 0x8036, 0x4b35, 0x8034, 0x4a33, 0x4932,
+ 0x4831, 0x802e, 0x472d, 0x462c, 0x452b, 0x442a, 0x4329, 0x4227,
+ 0x8026, 0x8025, 0x4123, 0x401f, 0x3f1e, 0x3e1d, 0x3d1b, 0x3c18,
+ 0x8017, 0x8010, 0x3b0f, 0x3a08, 0x8004, 0x3902, 0x8001, 0x8000,
+ 0x8000, 0x3800, 0x3700, 0x3600, 0x8000, 0x3500, 0x8000, 0x8000,
+ 0x8000, 0x3400, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x3300, 0x3200, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x3100, 0x3000, 0x8000, 0x8000, 0x2f00, 0x8000, 0x2e00, 0x2d00,
+ 0x2c00, 0x8000, 0x8000, 0x8000, 0x2b00, 0x8000, 0x2a00, 0x2900,
+ 0x2800, 0x8000, 0x2700, 0x2600, 0x2500, 0x2400, 0x2300, 0x2200,
+ 0x8000, 0x8000, 0x2100, 0x2000, 0x1f00, 0x1e00, 0x1d00, 0x1c00,
+ 0x8000, 0x8000, 0x1b00, 0x1a00, 0x8000, 0x1900, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x1800, 0x8000, 0x1700, 0x1600,
+ 0x1500, 0x8000, 0x1400, 0x1300, 0x1200, 0x1100, 0x1000, 0x0f00,
+ 0x8000, 0x8000, 0x0e00, 0x0d00, 0x0c00, 0x0b00, 0x0a00, 0x0900,
+ 0x8000, 0x8000, 0x0800, 0x0700, 0x8000, 0x0600, 0x8000, 0x8000,
+ 0x8000, 0x0500, 0x0400, 0x0300, 0x8000, 0x0200, 0x8000, 0x8000,
+ 0x8000, 0x0100, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x0000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x2071, 0x189e, 0x7003, 0x0002, 0x9006, 0x7016, 0x701a,
+ 0x704a, 0x704e, 0x700e, 0x7042, 0x7046, 0x703b, 0x18ba, 0x703f,
+ 0x18ba, 0x7007, 0x0001, 0x080c, 0x1060, 0x090c, 0x0d7d, 0x2900,
+ 0x706a, 0xa867, 0x0002, 0xa8ab, 0xdcb0, 0x080c, 0x1060, 0x090c,
+ 0x0d7d, 0x2900, 0x706e, 0xa867, 0x0002, 0xa8ab, 0xdcb0, 0x0005,
+ 0x2071, 0x189e, 0x7004, 0x0002, 0x34e8, 0x34e9, 0x34fc, 0x3510,
+ 0x0005, 0x1004, 0x34f9, 0x0e04, 0x34f9, 0x2079, 0x0000, 0x0126,
+ 0x2091, 0x8000, 0x700c, 0x9005, 0x1128, 0x700f, 0x0001, 0x012e,
+ 0x0468, 0x0005, 0x012e, 0x0ce8, 0x2079, 0x0000, 0x2061, 0x18b8,
+ 0x2c4c, 0xa86c, 0x908e, 0x0100, 0x0128, 0x9086, 0x0200, 0x0904,
+ 0x35e4, 0x0005, 0x7018, 0x2048, 0x2061, 0x1800, 0x701c, 0x0807,
+ 0x7014, 0x2048, 0xa864, 0x9094, 0x00ff, 0x9296, 0x0029, 0x1120,
+ 0xaa78, 0xd2fc, 0x0128, 0x0005, 0x9086, 0x0103, 0x0108, 0x0005,
+ 0x2079, 0x0000, 0x2061, 0x1800, 0x701c, 0x0807, 0x2061, 0x1800,
+ 0x7880, 0x908a, 0x0040, 0x1210, 0x61d0, 0x0042, 0x2100, 0x908a,
+ 0x003f, 0x1a04, 0x35e1, 0x61d0, 0x0804, 0x3576, 0x35b8, 0x35f0,
+ 0x35e1, 0x35fa, 0x3604, 0x360a, 0x360e, 0x361e, 0x3622, 0x3638,
+ 0x363e, 0x3644, 0x364f, 0x365a, 0x3669, 0x3678, 0x3686, 0x369d,
+ 0x36b8, 0x35e1, 0x3761, 0x379f, 0x3844, 0x3855, 0x3878, 0x35e1,
+ 0x35e1, 0x35e1, 0x38b0, 0x38d0, 0x38d9, 0x3905, 0x390b, 0x35e1,
+ 0x3951, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x395c, 0x3965,
+ 0x396d, 0x396f, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1,
+ 0x399f, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x39bc, 0x3a26,
+ 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x0002, 0x3a50,
+ 0x3a53, 0x3ab2, 0x3acb, 0x3afb, 0x3d9d, 0x35e1, 0x52f4, 0x35e1,
+ 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x35e1, 0x3638,
+ 0x363e, 0x428c, 0x5762, 0x42aa, 0x5383, 0x53d4, 0x54df, 0x35e1,
+ 0x5541, 0x557d, 0x55ae, 0x56be, 0x55db, 0x563e, 0x35e1, 0x42ae,
+ 0x4463, 0x4479, 0x449e, 0x4503, 0x4577, 0x4597, 0x460e, 0x466a,
+ 0x46c6, 0x46c9, 0x46ee, 0x4760, 0x47ca, 0x47d2, 0x4904, 0x4a7c,
+ 0x4ab0, 0x4d14, 0x35e1, 0x4d32, 0x4dd8, 0x4eba, 0x4f14, 0x35e1,
+ 0x4fcb, 0x35e1, 0x5033, 0x504e, 0x47d2, 0x5294, 0x714c, 0x0000,
+ 0x2021, 0x4000, 0x080c, 0x4b2e, 0x0126, 0x2091, 0x8000, 0x0e04,
+ 0x35c2, 0x0010, 0x012e, 0x0cc0, 0x7c36, 0x9486, 0x4000, 0x0118,
+ 0x7833, 0x0011, 0x0010, 0x7833, 0x0010, 0x7c82, 0x7986, 0x7a8a,
+ 0x7b8e, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c,
+ 0x11ee, 0x7007, 0x0001, 0x2091, 0x5000, 0x700f, 0x0000, 0x012e,
+ 0x0005, 0x2021, 0x4001, 0x08b0, 0x2021, 0x4002, 0x0898, 0x2021,
+ 0x4003, 0x0880, 0x2021, 0x4005, 0x0868, 0x2021, 0x4006, 0x0850,
+ 0x2039, 0x0001, 0x902e, 0x2520, 0x7b88, 0x7a8c, 0x7884, 0x7990,
+ 0x0804, 0x4b3b, 0x2039, 0x0001, 0x902e, 0x2520, 0x7b88, 0x7a8c,
+ 0x7884, 0x7990, 0x0804, 0x4b3e, 0x7984, 0x7888, 0x2114, 0x200a,
+ 0x0804, 0x35b8, 0x7984, 0x2114, 0x0804, 0x35b8, 0x20e1, 0x0000,
+ 0x2099, 0x0021, 0x20e9, 0x0000, 0x20a1, 0x0021, 0x20a9, 0x001f,
+ 0x4003, 0x7984, 0x7a88, 0x7b8c, 0x0804, 0x35b8, 0x7884, 0x2060,
+ 0x0804, 0x366b, 0x2009, 0x0003, 0x2011, 0x0003, 0x2019, 0x0008,
+ 0x789b, 0x0137, 0x7893, 0xffff, 0x2001, 0x188f, 0x2004, 0x9005,
+ 0x0118, 0x7896, 0x0804, 0x35b8, 0x7897, 0x0001, 0x0804, 0x35b8,
+ 0x2039, 0x0001, 0x7d98, 0x7c9c, 0x0804, 0x35f4, 0x2039, 0x0001,
+ 0x7d98, 0x7c9c, 0x0804, 0x35fe, 0x79a0, 0x9182, 0x0040, 0x0210,
+ 0x0804, 0x35ed, 0x2138, 0x7d98, 0x7c9c, 0x0804, 0x35f4, 0x79a0,
+ 0x9182, 0x0040, 0x0210, 0x0804, 0x35ed, 0x2138, 0x7d98, 0x7c9c,
+ 0x0804, 0x35fe, 0x79a0, 0x9182, 0x0040, 0x0210, 0x0804, 0x35ed,
+ 0x21e8, 0x7984, 0x7888, 0x20a9, 0x0001, 0x21a0, 0x4004, 0x0804,
+ 0x35b8, 0x2061, 0x0800, 0xe10c, 0x9006, 0x2c15, 0x9200, 0x8c60,
+ 0x8109, 0x1dd8, 0x2010, 0x9005, 0x0904, 0x35b8, 0x0804, 0x35e7,
+ 0x79a0, 0x9182, 0x0040, 0x0210, 0x0804, 0x35ed, 0x21e0, 0x20a9,
+ 0x0001, 0x7984, 0x2198, 0x4012, 0x0804, 0x35b8, 0x2069, 0x1847,
+ 0x7884, 0x7990, 0x911a, 0x1a04, 0x35ed, 0x8019, 0x0904, 0x35ed,
+ 0x684a, 0x6942, 0x788c, 0x6852, 0x7888, 0x6856, 0x9006, 0x685a,
+ 0x685e, 0x080c, 0x7871, 0x0804, 0x35b8, 0x2069, 0x1847, 0x7884,
+ 0x7994, 0x911a, 0x1a04, 0x35ed, 0x8019, 0x0904, 0x35ed, 0x684e,
+ 0x6946, 0x788c, 0x6862, 0x7888, 0x6866, 0x9006, 0x686a, 0x686e,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x6b43, 0x012e, 0x0804, 0x35b8,
+ 0x902e, 0x2520, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x35ea,
+ 0x7984, 0x7b88, 0x7a8c, 0x20a9, 0x0005, 0x20e9, 0x0001, 0x20a1,
+ 0x18a6, 0x4101, 0x080c, 0x4af2, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x35ea, 0x2009, 0x0020, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x080c,
+ 0x4b3b, 0x701f, 0x36dc, 0x0005, 0xa864, 0x2008, 0x9084, 0x00ff,
+ 0x9096, 0x0011, 0x0168, 0x9096, 0x0019, 0x0150, 0x9096, 0x0015,
+ 0x0138, 0x9096, 0x0048, 0x0120, 0x9096, 0x0029, 0x1904, 0x35ea,
+ 0x810f, 0x918c, 0x00ff, 0x0904, 0x35ea, 0x7112, 0x7010, 0x8001,
+ 0x0560, 0x7012, 0x080c, 0x4af2, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x35ea, 0x2009, 0x0020, 0x7068, 0x2040, 0xa28c, 0xa390, 0xa494,
+ 0xa598, 0x9290, 0x0040, 0x9399, 0x0000, 0x94a1, 0x0000, 0x95a9,
+ 0x0000, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x080c, 0x4b3b, 0x701f,
+ 0x371a, 0x0005, 0xa864, 0x9084, 0x00ff, 0x9096, 0x0002, 0x0120,
+ 0x9096, 0x000a, 0x1904, 0x35ea, 0x0888, 0x7014, 0x2048, 0xa868,
+ 0xc0fd, 0xa86a, 0xa864, 0x9084, 0x00ff, 0x9096, 0x0029, 0x1160,
+ 0xc2fd, 0xaa7a, 0x080c, 0x61ff, 0x0150, 0x0126, 0x2091, 0x8000,
+ 0xa87a, 0xa982, 0x012e, 0x0050, 0x080c, 0x652f, 0x1128, 0x7007,
+ 0x0003, 0x701f, 0x3746, 0x0005, 0x080c, 0x7022, 0x0126, 0x2091,
+ 0x8000, 0x20a9, 0x0005, 0x20e1, 0x0001, 0x2099, 0x18a6, 0x400a,
+ 0x2100, 0x9210, 0x9399, 0x0000, 0x94a1, 0x0000, 0x95a9, 0x0000,
+ 0xa85c, 0x9080, 0x0019, 0x2009, 0x0020, 0x012e, 0xaf60, 0x0804,
+ 0x4b3e, 0x2091, 0x8000, 0x7837, 0x4000, 0x7833, 0x0010, 0x7883,
+ 0x4000, 0x7887, 0x4953, 0x788b, 0x5020, 0x788f, 0x2020, 0x2009,
+ 0x017f, 0x2104, 0x7892, 0x3f00, 0x7896, 0x2061, 0x0100, 0x6200,
+ 0x2061, 0x0200, 0x603c, 0x8007, 0x9205, 0x789a, 0x2009, 0x04fd,
+ 0x2104, 0x789e, 0x2091, 0x5000, 0x2091, 0x4080, 0x2001, 0x0089,
+ 0x2004, 0xd084, 0x0180, 0x2001, 0x1a21, 0x2004, 0x9005, 0x0128,
+ 0x2001, 0x008b, 0x2004, 0xd0fc, 0x0dd8, 0x2001, 0x008a, 0x2003,
+ 0x0002, 0x2003, 0x1001, 0x2071, 0x0080, 0x0804, 0x0427, 0x81ff,
+ 0x1904, 0x35ea, 0x7984, 0x080c, 0x6693, 0x1904, 0x35ed, 0x7e98,
+ 0x9684, 0x3fff, 0x9082, 0x4000, 0x1a04, 0x35ed, 0x7c88, 0x7d8c,
+ 0x080c, 0x68c9, 0x080c, 0x6856, 0x1518, 0x2061, 0x1ddc, 0x0126,
+ 0x2091, 0x8000, 0x6000, 0x9086, 0x0000, 0x0148, 0x6014, 0x904d,
+ 0x0130, 0xa86c, 0x9406, 0x1118, 0xa870, 0x9506, 0x0150, 0x012e,
+ 0x9ce0, 0x001c, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1a04, 0x35ea,
+ 0x0c30, 0x080c, 0xc566, 0x012e, 0x0904, 0x35ea, 0x0804, 0x35b8,
+ 0x900e, 0x2001, 0x0005, 0x080c, 0x7022, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xcc85, 0x080c, 0x6dee, 0x012e, 0x0804, 0x35b8, 0x00a6,
+ 0x2950, 0xb198, 0x080c, 0x6693, 0x1904, 0x3831, 0xb6a4, 0x9684,
+ 0x3fff, 0x9082, 0x4000, 0x16e8, 0xb49c, 0xb5a0, 0x080c, 0x68c9,
+ 0x080c, 0x6873, 0x1520, 0x2061, 0x1ddc, 0x0126, 0x2091, 0x8000,
+ 0x6000, 0x9086, 0x0000, 0x0148, 0x6014, 0x904d, 0x0130, 0xa86c,
+ 0x9406, 0x1118, 0xa870, 0x9506, 0x0158, 0x012e, 0x9ce0, 0x001c,
+ 0x2001, 0x181a, 0x2004, 0x9c02, 0x2009, 0x000d, 0x12b0, 0x0c28,
+ 0x080c, 0xc566, 0x012e, 0x2009, 0x0003, 0x0178, 0x00e0, 0x900e,
+ 0x2001, 0x0005, 0x080c, 0x7022, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0xcc85, 0x080c, 0x6de2, 0x012e, 0x0070, 0xb097, 0x4005, 0xb19a,
+ 0x0010, 0xb097, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030,
+ 0x2a48, 0x00ae, 0x0005, 0xb097, 0x4000, 0x9006, 0x918d, 0x0001,
+ 0x2008, 0x2a48, 0x00ae, 0x0005, 0x81ff, 0x1904, 0x35ea, 0x080c,
+ 0x4b09, 0x0904, 0x35ed, 0x080c, 0x675a, 0x0904, 0x35ea, 0x080c,
+ 0x68cf, 0x0904, 0x35ea, 0x0804, 0x458e, 0x81ff, 0x1904, 0x35ea,
+ 0x080c, 0x4b25, 0x0904, 0x35ed, 0x080c, 0x695d, 0x0904, 0x35ea,
+ 0x2019, 0x0005, 0x79a8, 0x080c, 0x68ea, 0x0904, 0x35ea, 0x7888,
+ 0x908a, 0x1000, 0x1a04, 0x35ed, 0x8003, 0x800b, 0x810b, 0x9108,
+ 0x080c, 0x86d6, 0x7984, 0xd184, 0x1904, 0x35b8, 0x0804, 0x458e,
+ 0x0126, 0x2091, 0x8000, 0x81ff, 0x0118, 0x2009, 0x0001, 0x0450,
+ 0x2029, 0x07ff, 0x645c, 0x2400, 0x9506, 0x01f8, 0x2508, 0x080c,
+ 0x6693, 0x11d8, 0x080c, 0x695d, 0x1128, 0x2009, 0x0002, 0x62c0,
+ 0x2518, 0x00c0, 0x2019, 0x0004, 0x900e, 0x080c, 0x68ea, 0x1118,
+ 0x2009, 0x0006, 0x0078, 0x7884, 0x908a, 0x1000, 0x1270, 0x8003,
+ 0x800b, 0x810b, 0x9108, 0x080c, 0x86d6, 0x8529, 0x1ae0, 0x012e,
+ 0x0804, 0x35b8, 0x012e, 0x0804, 0x35ea, 0x012e, 0x0804, 0x35ed,
+ 0x080c, 0x4b09, 0x0904, 0x35ed, 0x080c, 0x675a, 0x0904, 0x35ea,
+ 0x080c, 0xa91e, 0xbaa0, 0x2019, 0x0005, 0x00c6, 0x9066, 0x080c,
+ 0x943d, 0x0076, 0x903e, 0x080c, 0x9306, 0x900e, 0x080c, 0xe167,
+ 0x007e, 0x00ce, 0x080c, 0xa93a, 0x080c, 0x68c9, 0x0804, 0x35b8,
+ 0x080c, 0x4b09, 0x0904, 0x35ed, 0x080c, 0x68c9, 0x2208, 0x0804,
+ 0x35b8, 0x0156, 0x00d6, 0x00e6, 0x00c6, 0x2069, 0x1910, 0x6810,
+ 0x6914, 0x910a, 0x1208, 0x900e, 0x6816, 0x9016, 0x901e, 0x2071,
+ 0x19e6, 0x7028, 0x9065, 0x0118, 0x8210, 0x600c, 0x0cd8, 0x2300,
+ 0x9218, 0x00ce, 0x00ee, 0x00de, 0x015e, 0x0804, 0x35b8, 0x00f6,
+ 0x0016, 0x907d, 0x0138, 0x9006, 0x8000, 0x2f0c, 0x81ff, 0x0110,
+ 0x2178, 0x0cd0, 0x001e, 0x00fe, 0x0005, 0x2069, 0x1910, 0x6910,
+ 0x62bc, 0x0804, 0x35b8, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804,
+ 0x35ea, 0x0126, 0x2091, 0x8000, 0x080c, 0x5752, 0x0128, 0x2009,
+ 0x0007, 0x012e, 0x0804, 0x35ea, 0x012e, 0x615c, 0x9190, 0x33b9,
+ 0x2215, 0x9294, 0x00ff, 0x637c, 0x83ff, 0x0108, 0x6280, 0x67dc,
+ 0x97c4, 0x000a, 0x98c6, 0x000a, 0x1118, 0x2031, 0x0001, 0x00e8,
+ 0x97c4, 0x0022, 0x98c6, 0x0022, 0x1118, 0x2031, 0x0003, 0x00a8,
+ 0x97c4, 0x0012, 0x98c6, 0x0012, 0x1118, 0x2031, 0x0002, 0x0068,
+ 0x080c, 0x753d, 0x1118, 0x2031, 0x0004, 0x0038, 0xd79c, 0x0120,
+ 0x2009, 0x0005, 0x0804, 0x35ea, 0x9036, 0x7e9a, 0x7f9e, 0x0804,
+ 0x35b8, 0x614c, 0x6250, 0x2019, 0x1985, 0x231c, 0x2001, 0x1986,
+ 0x2004, 0x789a, 0x0804, 0x35b8, 0x0126, 0x2091, 0x8000, 0x6138,
+ 0x623c, 0x6340, 0x012e, 0x0804, 0x35b8, 0x080c, 0x4b25, 0x0904,
+ 0x35ed, 0xba44, 0xbb38, 0x0804, 0x35b8, 0x080c, 0x0d7d, 0x080c,
+ 0x4b25, 0x2110, 0x0904, 0x35ed, 0xb804, 0x908c, 0x00ff, 0x918e,
+ 0x0006, 0x0140, 0x9084, 0xff00, 0x9086, 0x0600, 0x2009, 0x0009,
+ 0x1904, 0x35ea, 0x0126, 0x2091, 0x8000, 0x2019, 0x0005, 0x00c6,
+ 0x9066, 0x080c, 0xa91e, 0x080c, 0xa404, 0x080c, 0x943d, 0x0076,
+ 0x903e, 0x080c, 0x9306, 0x900e, 0x080c, 0xe167, 0x007e, 0x00ce,
+ 0x080c, 0xa93a, 0xb807, 0x0407, 0x012e, 0x0804, 0x35b8, 0x614c,
+ 0x6250, 0x7884, 0x604e, 0x7b88, 0x6352, 0x2069, 0x1847, 0x831f,
+ 0x9305, 0x6816, 0x788c, 0x2069, 0x1985, 0x2d1c, 0x206a, 0x7e98,
+ 0x9682, 0x0014, 0x1210, 0x2031, 0x07d0, 0x2069, 0x1986, 0x2d04,
+ 0x266a, 0x789a, 0x0804, 0x35b8, 0x0126, 0x2091, 0x8000, 0x6138,
+ 0x7884, 0x603a, 0x910e, 0xd1b4, 0x190c, 0x0ed9, 0xd0c4, 0x01a8,
+ 0x00d6, 0x78a8, 0x2009, 0x199c, 0x200a, 0x78ac, 0x2011, 0x199d,
+ 0x2012, 0x2069, 0x0100, 0x6838, 0x9086, 0x0007, 0x1118, 0x2214,
+ 0x6a5a, 0x0010, 0x210c, 0x695a, 0x00de, 0x2011, 0x0116, 0x220c,
+ 0x7888, 0xd08c, 0x0118, 0x918d, 0x0040, 0x0010, 0x918c, 0xff7f,
+ 0x2112, 0x603c, 0x7988, 0x613e, 0x6140, 0x910d, 0x788c, 0x6042,
+ 0x7a88, 0x9294, 0x1000, 0x9205, 0x910e, 0xd1e4, 0x190c, 0x0ef4,
+ 0x9084, 0x0020, 0x0130, 0x78b4, 0x6046, 0x9084, 0x0001, 0x090c,
+ 0x428c, 0x6040, 0xd0cc, 0x0120, 0x78b0, 0x2011, 0x0114, 0x2012,
+ 0x012e, 0x0804, 0x35b8, 0x00f6, 0x2079, 0x1800, 0x7a38, 0xa898,
+ 0x9084, 0xfebf, 0x9215, 0xa89c, 0x9084, 0xfebf, 0x8002, 0x9214,
+ 0x7838, 0x9084, 0x0140, 0x9215, 0x7a3a, 0xa897, 0x4000, 0x900e,
+ 0x9085, 0x0001, 0x2001, 0x0000, 0x00fe, 0x0005, 0x7898, 0x9005,
+ 0x01a8, 0x7888, 0x9025, 0x0904, 0x35ed, 0x788c, 0x902d, 0x0904,
+ 0x35ed, 0x900e, 0x080c, 0x6693, 0x1120, 0xba44, 0xbb38, 0xbc46,
+ 0xbd3a, 0x9186, 0x07ff, 0x0190, 0x8108, 0x0ca0, 0x080c, 0x4b25,
+ 0x0904, 0x35ed, 0x7888, 0x900d, 0x0904, 0x35ed, 0x788c, 0x9005,
+ 0x0904, 0x35ed, 0xba44, 0xb946, 0xbb38, 0xb83a, 0x0804, 0x35b8,
+ 0x2011, 0xbc09, 0x0010, 0x2011, 0xbc05, 0x080c, 0x5752, 0x1904,
+ 0x35ea, 0x00c6, 0x2061, 0x0100, 0x7984, 0x9186, 0x00ff, 0x1130,
+ 0x2001, 0x1818, 0x2004, 0x9085, 0xff00, 0x0088, 0x9182, 0x007f,
+ 0x16e0, 0x9188, 0x33b9, 0x210d, 0x918c, 0x00ff, 0x2001, 0x1818,
+ 0x2004, 0x0026, 0x9116, 0x002e, 0x0580, 0x810f, 0x9105, 0x0126,
+ 0x2091, 0x8000, 0x0006, 0x080c, 0xac5a, 0x000e, 0x0510, 0x602e,
+ 0x620a, 0x7984, 0x00b6, 0x080c, 0x6638, 0x2b08, 0x00be, 0x1500,
+ 0x6112, 0x6023, 0x0001, 0x080c, 0x4af2, 0x01d0, 0x9006, 0xa866,
+ 0x7007, 0x0003, 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x701f, 0x3aab,
+ 0x2900, 0x6016, 0x2009, 0x0032, 0x080c, 0xad4d, 0x012e, 0x00ce,
+ 0x0005, 0x012e, 0x00ce, 0x0804, 0x35ea, 0x00ce, 0x0804, 0x35ed,
+ 0x080c, 0xacb0, 0x0cb0, 0xa830, 0x9086, 0x0100, 0x0904, 0x35ea,
+ 0x0804, 0x35b8, 0x2061, 0x1a6e, 0x0126, 0x2091, 0x8000, 0x6000,
+ 0xd084, 0x0170, 0x6104, 0x6208, 0x2061, 0x1800, 0x6354, 0x6074,
+ 0x789a, 0x60c0, 0x789e, 0x60bc, 0x78aa, 0x012e, 0x0804, 0x35b8,
+ 0x900e, 0x2110, 0x0c88, 0x81ff, 0x1904, 0x35ea, 0x080c, 0x753d,
+ 0x0904, 0x35ea, 0x0126, 0x2091, 0x8000, 0x6254, 0x6074, 0x9202,
+ 0x0248, 0x9085, 0x0001, 0x080c, 0x26ca, 0x080c, 0x5971, 0x012e,
+ 0x0804, 0x35b8, 0x012e, 0x0804, 0x35ed, 0x0006, 0x0016, 0x00c6,
+ 0x00e6, 0x2001, 0x19a8, 0x2070, 0x2061, 0x1847, 0x6008, 0x2072,
+ 0x900e, 0x2011, 0x1400, 0x080c, 0x91f8, 0x7206, 0x00ee, 0x00ce,
+ 0x001e, 0x000e, 0x0005, 0x0126, 0x2091, 0x8000, 0x81ff, 0x0128,
+ 0x012e, 0x2021, 0x400b, 0x0804, 0x35ba, 0x7884, 0xd0fc, 0x0148,
+ 0x2001, 0x002a, 0x2004, 0x9082, 0x00e1, 0x0288, 0x012e, 0x0804,
+ 0x35ed, 0x2001, 0x002a, 0x2004, 0x2069, 0x1847, 0x6908, 0x9102,
+ 0x1230, 0x012e, 0x0804, 0x35ed, 0x012e, 0x0804, 0x35ea, 0x080c,
+ 0xabe2, 0x0dd0, 0x7884, 0xd0fc, 0x0904, 0x3b76, 0x00c6, 0x080c,
+ 0x4af2, 0x00ce, 0x0d88, 0xa867, 0x0000, 0x7884, 0xa80a, 0x7898,
+ 0xa80e, 0x789c, 0xa812, 0x2001, 0x002e, 0x2004, 0xa81a, 0x2001,
+ 0x002f, 0x2004, 0xa81e, 0x2001, 0x0030, 0x2004, 0xa822, 0x2001,
+ 0x0031, 0x2004, 0xa826, 0x2001, 0x0034, 0x2004, 0xa82a, 0x2001,
+ 0x0035, 0x2004, 0xa82e, 0x2001, 0x002a, 0x2004, 0x9080, 0x0003,
+ 0x9084, 0x00fc, 0x8004, 0xa816, 0x080c, 0x3d00, 0x0928, 0x7014,
+ 0x2048, 0xad2c, 0xac28, 0xab1c, 0xaa18, 0xa930, 0xa808, 0xd0b4,
+ 0x1120, 0x2029, 0x0000, 0x2021, 0x0000, 0x8906, 0x8006, 0x8007,
+ 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b, 0x080c, 0x4b3b,
+ 0x701f, 0x3c3d, 0x7023, 0x0001, 0x012e, 0x0005, 0x080c, 0xa91e,
+ 0x0046, 0x0086, 0x0096, 0x00a6, 0x00b6, 0x00c6, 0x00d6, 0x00e6,
+ 0x00f6, 0x080c, 0x3ae5, 0x2001, 0x199e, 0x2003, 0x0000, 0x2021,
+ 0x000a, 0x2061, 0x0100, 0x6104, 0x0016, 0x60bb, 0x0000, 0x60bf,
+ 0x32e1, 0x60bf, 0x0012, 0x080c, 0x3d6f, 0x080c, 0x3d2e, 0x00f6,
+ 0x00e6, 0x0086, 0x2940, 0x2071, 0x19e6, 0x2079, 0x0090, 0x00d6,
+ 0x2069, 0x0000, 0x6884, 0xd0b4, 0x0140, 0x2001, 0x0035, 0x2004,
+ 0x780e, 0x2001, 0x0034, 0x2004, 0x780a, 0x00de, 0x2011, 0x0001,
+ 0x080c, 0x40d0, 0x008e, 0x00ee, 0x00fe, 0x080c, 0x3ffd, 0x080c,
+ 0x3f2a, 0x05b8, 0x2001, 0x020b, 0x2004, 0x9084, 0x0140, 0x1db8,
+ 0x080c, 0x4144, 0x00f6, 0x2079, 0x0300, 0x78bc, 0x00fe, 0x908c,
+ 0x0070, 0x1560, 0x2071, 0x0200, 0x7037, 0x0000, 0x7050, 0x9084,
+ 0xff00, 0x9086, 0x3200, 0x1510, 0x7037, 0x0001, 0x7050, 0x9084,
+ 0xff00, 0x9086, 0xe100, 0x11d0, 0x7037, 0x0000, 0x7054, 0x7037,
+ 0x0000, 0x715c, 0x9106, 0x1190, 0x2001, 0x1820, 0x2004, 0x9106,
+ 0x1168, 0x00c6, 0x2061, 0x0100, 0x6024, 0x9084, 0x1e00, 0x00ce,
+ 0x0138, 0x080c, 0x3f34, 0x080c, 0x3d29, 0x0058, 0x080c, 0x3d29,
+ 0x080c, 0x4068, 0x080c, 0x3ff3, 0x2001, 0x020b, 0x2004, 0xd0e4,
+ 0x0dd8, 0x2001, 0x032a, 0x2003, 0x0004, 0x2061, 0x0100, 0x6027,
+ 0x0002, 0x001e, 0x6106, 0x2011, 0x020d, 0x2013, 0x0020, 0x60bb,
+ 0x0000, 0x60bf, 0x0108, 0x60bf, 0x0012, 0x2001, 0x0004, 0x200c,
+ 0x918c, 0xfffd, 0x2102, 0x080c, 0x1340, 0x2009, 0x0028, 0x080c,
+ 0x220a, 0x2001, 0x0227, 0x200c, 0x2102, 0x080c, 0xa93a, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e, 0x008e, 0x004e,
+ 0x2001, 0x199e, 0x2004, 0x9005, 0x1118, 0x012e, 0x0804, 0x35b8,
+ 0x012e, 0x2021, 0x400c, 0x0804, 0x35ba, 0x0016, 0x0026, 0x0036,
+ 0x0046, 0x0056, 0x0076, 0x0086, 0x0096, 0x00d6, 0x0156, 0x7014,
+ 0x2048, 0x7020, 0x20a8, 0x8000, 0x7022, 0xa804, 0x9005, 0x0904,
+ 0x3c99, 0x2048, 0x1f04, 0x3c4d, 0x7068, 0x2040, 0xa28c, 0xa390,
+ 0xa494, 0xa598, 0xa930, 0xa808, 0xd0b4, 0x1120, 0x2029, 0x0000,
+ 0x2021, 0x0000, 0x0096, 0x7014, 0x2048, 0xa864, 0x009e, 0x9086,
+ 0x0103, 0x0170, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x001b, 0x080c, 0x4b3b, 0x701f, 0x3c3d, 0x00b0,
+ 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080,
+ 0x001b, 0x21a8, 0x27e0, 0x2098, 0x27e8, 0x20a0, 0x0006, 0x080c,
+ 0x0fc4, 0x000e, 0x080c, 0x4b3e, 0x701f, 0x3c3d, 0x015e, 0x00de,
+ 0x009e, 0x008e, 0x007e, 0x005e, 0x004e, 0x003e, 0x002e, 0x001e,
+ 0x0005, 0x7014, 0x2048, 0xa864, 0x9086, 0x0103, 0x1118, 0x701f,
+ 0x3cfe, 0x0450, 0x7014, 0x2048, 0xa868, 0xc0fd, 0xa86a, 0x2009,
+ 0x007f, 0x080c, 0x6632, 0x0110, 0x9006, 0x0030, 0xb813, 0x00ff,
+ 0xb817, 0xfffd, 0x080c, 0xce64, 0x015e, 0x00de, 0x009e, 0x008e,
+ 0x007e, 0x005e, 0x004e, 0x003e, 0x002e, 0x001e, 0x0904, 0x35ea,
+ 0x0016, 0x0026, 0x0036, 0x0046, 0x0056, 0x0076, 0x0086, 0x0096,
+ 0x00d6, 0x0156, 0x701f, 0x3cd0, 0x7007, 0x0003, 0x0804, 0x3c8e,
+ 0xa830, 0x9086, 0x0100, 0x2021, 0x400c, 0x0904, 0x35ba, 0x0076,
+ 0xad10, 0xac0c, 0xab24, 0xaa20, 0xa930, 0xa808, 0xd0b4, 0x1120,
+ 0x2029, 0x0000, 0x2021, 0x0000, 0x8906, 0x8006, 0x8007, 0x90bc,
+ 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b, 0x21a8, 0x27e0, 0x2098,
+ 0x27e8, 0x20a0, 0x0006, 0x080c, 0x0fc4, 0x000e, 0x080c, 0x4b3e,
+ 0x007e, 0x701f, 0x3c3d, 0x7023, 0x0001, 0x0005, 0x0804, 0x35b8,
+ 0x0156, 0x00c6, 0xa814, 0x908a, 0x001e, 0x0218, 0xa833, 0x001e,
+ 0x0010, 0xa832, 0x0078, 0x81ff, 0x0168, 0x0016, 0x080c, 0x4af2,
+ 0x001e, 0x0130, 0xa800, 0x2040, 0xa008, 0xa80a, 0x2100, 0x0c58,
+ 0x9006, 0x0010, 0x9085, 0x0001, 0x00ce, 0x015e, 0x0005, 0x0006,
+ 0x00f6, 0x2079, 0x0000, 0x7880, 0x9086, 0x0044, 0x00fe, 0x000e,
+ 0x0005, 0x2001, 0x199e, 0x2003, 0x0001, 0x0005, 0x00f6, 0x00e6,
+ 0x00c6, 0x2061, 0x0200, 0x2001, 0x19a9, 0x2004, 0x601a, 0x2061,
+ 0x0100, 0x2001, 0x19a8, 0x2004, 0x60ce, 0x6104, 0xc1ac, 0x6106,
+ 0x080c, 0x4af2, 0xa813, 0x0019, 0xa817, 0x0001, 0x2900, 0xa85a,
+ 0x2001, 0x002e, 0x2004, 0xa866, 0x2001, 0x002f, 0x2004, 0xa86a,
+ 0x2061, 0x0090, 0x2079, 0x0100, 0x2001, 0x19a8, 0x2004, 0x6036,
+ 0x2009, 0x0040, 0x080c, 0x220a, 0x2001, 0x002a, 0x2004, 0x9084,
+ 0xfff8, 0xa86e, 0x601a, 0xa873, 0x0000, 0x601f, 0x0000, 0x78ca,
+ 0x9006, 0x600a, 0x600e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x00e6,
+ 0x080c, 0x4af2, 0x2940, 0xa013, 0x0019, 0xa017, 0x0001, 0x2800,
+ 0xa05a, 0x2001, 0x0030, 0x2004, 0xa866, 0x2001, 0x0031, 0x2004,
+ 0xa86a, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8, 0xa86e, 0xa873,
+ 0x0000, 0x2001, 0x032a, 0x2003, 0x0004, 0x2001, 0x0300, 0x2003,
+ 0x0000, 0x2001, 0x020d, 0x2003, 0x0000, 0x2001, 0x0004, 0x200c,
+ 0x918d, 0x0002, 0x2102, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x81ff, 0x0148, 0x080c, 0x2a58, 0x1130, 0x9006, 0x080c, 0x29ab,
+ 0x9006, 0x080c, 0x298e, 0x7884, 0x9084, 0x0007, 0x0002, 0x3dba,
+ 0x3dbb, 0x3dbc, 0x3db7, 0x3db7, 0x3db7, 0x3db7, 0x3db7, 0x012e,
+ 0x0804, 0x35ed, 0x0ce0, 0x0cd8, 0x080c, 0x753d, 0x1128, 0x012e,
+ 0x2009, 0x0016, 0x0804, 0x35ea, 0x81ff, 0x0128, 0x012e, 0x2021,
+ 0x400b, 0x0804, 0x35ba, 0x080c, 0xa91e, 0x0086, 0x0096, 0x00a6,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x080c, 0x3ae5, 0x2009,
+ 0x0101, 0x210c, 0x0016, 0x7ec8, 0x7dcc, 0x9006, 0x2068, 0x2060,
+ 0x2058, 0x080c, 0x421f, 0x080c, 0x416f, 0x903e, 0x2720, 0x00f6,
+ 0x00e6, 0x0086, 0x2940, 0x2071, 0x19e6, 0x2079, 0x0090, 0x00d6,
+ 0x2069, 0x0000, 0x6884, 0xd0b4, 0x0120, 0x68d4, 0x780e, 0x68d0,
+ 0x780a, 0x00de, 0x2011, 0x0001, 0x080c, 0x40d0, 0x080c, 0x2a60,
+ 0x080c, 0x2a60, 0x080c, 0x2a60, 0x080c, 0x2a60, 0x080c, 0x40d0,
+ 0x008e, 0x00ee, 0x00fe, 0x080c, 0x3ffd, 0x2009, 0x9c40, 0x8109,
+ 0x11b0, 0x080c, 0x3f34, 0x2001, 0x0004, 0x200c, 0x918c, 0xfffd,
+ 0x2102, 0x001e, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae,
+ 0x009e, 0x008e, 0x2009, 0x0017, 0x080c, 0x35ea, 0x0cf8, 0x2001,
+ 0x020b, 0x2004, 0x9084, 0x0140, 0x1d10, 0x00f6, 0x2079, 0x0000,
+ 0x7884, 0x00fe, 0xd0bc, 0x0178, 0x2001, 0x0201, 0x200c, 0x81ff,
+ 0x0150, 0x080c, 0x3fdb, 0x2d00, 0x9c05, 0x9b05, 0x0120, 0x080c,
+ 0x3f34, 0x0804, 0x3edd, 0x080c, 0x4144, 0x080c, 0x4068, 0x080c,
+ 0x3fbe, 0x080c, 0x3ff3, 0x00f6, 0x2079, 0x0100, 0x7824, 0xd0ac,
+ 0x0130, 0x8b58, 0x080c, 0x3f34, 0x00fe, 0x0804, 0x3edd, 0x00fe,
+ 0x080c, 0x3f2a, 0x1150, 0x8d68, 0x2001, 0x0032, 0x2602, 0x2001,
+ 0x0033, 0x2502, 0x080c, 0x3f34, 0x0080, 0x87ff, 0x0138, 0x2001,
+ 0x0201, 0x2004, 0x9005, 0x1908, 0x8739, 0x0038, 0x2001, 0x1a6a,
+ 0x2004, 0x9086, 0x0000, 0x1904, 0x3e2d, 0x2001, 0x032f, 0x2003,
+ 0x00f6, 0x8631, 0x1208, 0x8529, 0x2500, 0x9605, 0x0904, 0x3edd,
+ 0x7884, 0xd0bc, 0x0128, 0x2d00, 0x9c05, 0x9b05, 0x1904, 0x3edd,
+ 0xa013, 0x0019, 0x2001, 0x032a, 0x2003, 0x0004, 0x7884, 0xd0ac,
+ 0x1148, 0x2001, 0x1a6a, 0x2003, 0x0003, 0x2001, 0x032a, 0x2003,
+ 0x0009, 0x0030, 0xa017, 0x0001, 0x78b4, 0x9005, 0x0108, 0xa016,
+ 0x2800, 0xa05a, 0x2009, 0x0040, 0x080c, 0x220a, 0x2900, 0xa85a,
+ 0xa813, 0x0019, 0x7884, 0xd0a4, 0x1180, 0xa817, 0x0000, 0x00c6,
+ 0x20a9, 0x0004, 0x2061, 0x0090, 0x602b, 0x0008, 0x2001, 0x0203,
+ 0x2004, 0x1f04, 0x3eb4, 0x00ce, 0x0030, 0xa817, 0x0001, 0x78b0,
+ 0x9005, 0x0108, 0xa816, 0x00f6, 0x00c6, 0x2079, 0x0100, 0x2061,
+ 0x0090, 0x7827, 0x0002, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8,
+ 0x601a, 0x0006, 0x2001, 0x002b, 0x2004, 0x601e, 0x78c6, 0x000e,
+ 0x78ca, 0x00ce, 0x00fe, 0x0804, 0x3de7, 0x001e, 0x00c6, 0x2001,
+ 0x032a, 0x2003, 0x0004, 0x2061, 0x0100, 0x6027, 0x0002, 0x6106,
+ 0x2011, 0x020d, 0x2013, 0x0020, 0x2001, 0x0004, 0x200c, 0x918c,
+ 0xfffd, 0x2102, 0x080c, 0x1340, 0x7884, 0x9084, 0x0003, 0x9086,
+ 0x0002, 0x01b0, 0x2009, 0x0028, 0x080c, 0x220a, 0x2001, 0x0227,
+ 0x200c, 0x2102, 0x6050, 0x9084, 0xb7ff, 0x080c, 0x2b0a, 0x6052,
+ 0x602f, 0x0000, 0x604b, 0xf7f7, 0x6043, 0x0090, 0x6043, 0x0010,
+ 0x080c, 0xa93a, 0x00ce, 0x2d08, 0x2c10, 0x2b18, 0x2b00, 0x9c05,
+ 0x9d05, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e,
+ 0x008e, 0x1118, 0x012e, 0x0804, 0x35b8, 0x012e, 0x2021, 0x400c,
+ 0x0804, 0x35ba, 0x9085, 0x0001, 0x1d04, 0x3f33, 0x2091, 0x6000,
+ 0x8420, 0x9486, 0x0064, 0x0005, 0x2001, 0x0105, 0x2003, 0x0010,
+ 0x2001, 0x032a, 0x2003, 0x0004, 0x2001, 0x1a6a, 0x2003, 0x0000,
+ 0x0071, 0x2009, 0x0048, 0x080c, 0x220a, 0x2001, 0x0227, 0x2024,
+ 0x2402, 0x2001, 0x0109, 0x2003, 0x4000, 0x9026, 0x0005, 0x00f6,
+ 0x00e6, 0x2071, 0x19e6, 0x7054, 0x9086, 0x0000, 0x0520, 0x2079,
+ 0x0090, 0x2009, 0x0206, 0x2104, 0x2009, 0x0203, 0x210c, 0x9106,
+ 0x1120, 0x2009, 0x0040, 0x080c, 0x220a, 0x782c, 0xd0fc, 0x0d88,
+ 0x080c, 0x4144, 0x7054, 0x9086, 0x0000, 0x1d58, 0x782b, 0x0004,
+ 0x782c, 0xd0ac, 0x1de8, 0x2009, 0x0040, 0x080c, 0x220a, 0x782b,
+ 0x0002, 0x7057, 0x0000, 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x2079,
+ 0x0100, 0x2001, 0x1818, 0x200c, 0x7932, 0x7936, 0x080c, 0x26aa,
+ 0x080c, 0x2ad7, 0x080c, 0x2b0a, 0x784b, 0xf7f7, 0x7843, 0x0090,
+ 0x7843, 0x0010, 0x7850, 0xc0e5, 0x7852, 0x2019, 0x61a8, 0x7820,
+ 0xd09c, 0x0110, 0x8319, 0x1dd8, 0x7850, 0xc0e4, 0x7852, 0x2011,
+ 0x0048, 0x080c, 0x2ab4, 0x7843, 0x0040, 0x2019, 0x01f4, 0xa001,
+ 0xa001, 0x8319, 0x1de0, 0x2001, 0x0100, 0x080c, 0x2a7a, 0x2011,
+ 0x0020, 0x080c, 0x2ab4, 0x7843, 0x0000, 0x9006, 0x080c, 0x2a7a,
+ 0x2011, 0x0048, 0x080c, 0x2ab4, 0x00fe, 0x0005, 0x7884, 0xd0ac,
+ 0x11c8, 0x00f6, 0x00e6, 0x2071, 0x1a6a, 0x2079, 0x0320, 0x2001,
+ 0x0201, 0x2004, 0x9005, 0x0160, 0x7000, 0x9086, 0x0000, 0x1140,
+ 0x0051, 0xd0bc, 0x0108, 0x8738, 0x7003, 0x0003, 0x782b, 0x0019,
+ 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x2079, 0x0300, 0x78bc, 0x00fe,
+ 0x908c, 0x0070, 0x0178, 0x2009, 0x0032, 0x260a, 0x2009, 0x0033,
+ 0x250a, 0xd0b4, 0x0108, 0x8c60, 0xd0ac, 0x0108, 0x8d68, 0xd0a4,
+ 0x0108, 0x8b58, 0x0005, 0x00f6, 0x2079, 0x0200, 0x781c, 0xd084,
+ 0x0110, 0x7837, 0x0050, 0x00fe, 0x0005, 0x00e6, 0x2071, 0x0100,
+ 0x2001, 0x19a9, 0x2004, 0x70e2, 0x080c, 0x3d1f, 0x1188, 0x2001,
+ 0x1820, 0x2004, 0x2009, 0x181f, 0x210c, 0x918c, 0x00ff, 0x706e,
+ 0x716a, 0x7066, 0x918d, 0x3200, 0x7162, 0x7073, 0xe109, 0x0080,
+ 0x702c, 0x9085, 0x0002, 0x702e, 0x2009, 0x1818, 0x210c, 0x716e,
+ 0x7063, 0x0100, 0x7166, 0x719e, 0x706b, 0x0000, 0x7073, 0x0809,
+ 0x7077, 0x0008, 0x7078, 0x9080, 0x0100, 0x707a, 0x7080, 0x8000,
+ 0x7082, 0x7087, 0xaaaa, 0x9006, 0x708a, 0x708e, 0x707e, 0x70d6,
+ 0x70ab, 0x0036, 0x70af, 0x95d5, 0x7014, 0x9084, 0x1984, 0x9085,
+ 0x0092, 0x7016, 0x080c, 0x4144, 0x00f6, 0x2071, 0x1a6a, 0x2079,
+ 0x0320, 0x00d6, 0x2069, 0x0000, 0x6884, 0xd0b4, 0x0120, 0x689c,
+ 0x780e, 0x6898, 0x780a, 0x00de, 0x2009, 0x03e8, 0x8109, 0x1df0,
+ 0x792c, 0xd1fc, 0x0110, 0x782b, 0x0004, 0x2011, 0x0011, 0x080c,
+ 0x40d0, 0x2011, 0x0001, 0x080c, 0x40d0, 0x00fe, 0x00ee, 0x0005,
+ 0x00f6, 0x00e6, 0x2071, 0x1a6a, 0x2079, 0x0320, 0x792c, 0xd1fc,
+ 0x0904, 0x40cd, 0x782b, 0x0002, 0x9026, 0xd19c, 0x1904, 0x40c9,
+ 0x7000, 0x0002, 0x40cd, 0x407e, 0x40ae, 0x40c9, 0xd1bc, 0x1170,
+ 0xd1dc, 0x1190, 0x8001, 0x7002, 0x2011, 0x0001, 0x080c, 0x40d0,
+ 0x0904, 0x40cd, 0x080c, 0x40d0, 0x0804, 0x40cd, 0x00f6, 0x2079,
+ 0x0300, 0x78bf, 0x0000, 0x00fe, 0x7810, 0x7914, 0x782b, 0x0004,
+ 0x7812, 0x7916, 0x2001, 0x0201, 0x200c, 0x81ff, 0x0de8, 0x080c,
+ 0x3fdb, 0x2009, 0x0001, 0x00f6, 0x2079, 0x0300, 0x78b8, 0x00fe,
+ 0xd0ec, 0x0110, 0x2009, 0x0011, 0x792a, 0x00f8, 0x8001, 0x7002,
+ 0x9184, 0x0880, 0x1140, 0x782c, 0xd0fc, 0x1904, 0x4072, 0x2011,
+ 0x0001, 0x00b1, 0x0090, 0xa010, 0x9092, 0x0004, 0x9086, 0x0015,
+ 0x1120, 0xa000, 0xa05a, 0x2011, 0x0031, 0xa212, 0xd1dc, 0x1960,
+ 0x0828, 0x782b, 0x0004, 0x7003, 0x0000, 0x00ee, 0x00fe, 0x0005,
+ 0xa014, 0x9005, 0x0550, 0x8001, 0x0036, 0x0096, 0xa016, 0xa058,
+ 0x2048, 0xa010, 0x2009, 0x0031, 0x911a, 0x831c, 0x831c, 0x938a,
+ 0x0007, 0x1a0c, 0x0d7d, 0x9398, 0x40fe, 0x231d, 0x083f, 0x9080,
+ 0x0004, 0x7a2a, 0x7100, 0x8108, 0x7102, 0x009e, 0x003e, 0x908a,
+ 0x0035, 0x1140, 0x0096, 0xa058, 0x2048, 0xa804, 0xa05a, 0x2001,
+ 0x0019, 0x009e, 0xa012, 0x9085, 0x0001, 0x0005, 0x413b, 0x4132,
+ 0x4129, 0x4120, 0x4117, 0x410e, 0x4105, 0xa964, 0x7902, 0xa968,
+ 0x7906, 0xa96c, 0x7912, 0xa970, 0x7916, 0x0005, 0xa974, 0x7902,
+ 0xa978, 0x7906, 0xa97c, 0x7912, 0xa980, 0x7916, 0x0005, 0xa984,
+ 0x7902, 0xa988, 0x7906, 0xa98c, 0x7912, 0xa990, 0x7916, 0x0005,
+ 0xa994, 0x7902, 0xa998, 0x7906, 0xa99c, 0x7912, 0xa9a0, 0x7916,
+ 0x0005, 0xa9a4, 0x7902, 0xa9a8, 0x7906, 0xa9ac, 0x7912, 0xa9b0,
+ 0x7916, 0x0005, 0xa9b4, 0x7902, 0xa9b8, 0x7906, 0xa9bc, 0x7912,
+ 0xa9c0, 0x7916, 0x0005, 0xa9c4, 0x7902, 0xa9c8, 0x7906, 0xa9cc,
+ 0x7912, 0xa9d0, 0x7916, 0x0005, 0x00f6, 0x00e6, 0x0086, 0x2071,
+ 0x19e6, 0x2079, 0x0090, 0x792c, 0xd1fc, 0x01e8, 0x782b, 0x0002,
+ 0x2940, 0x9026, 0x7054, 0x0002, 0x416b, 0x4157, 0x4162, 0x8001,
+ 0x7056, 0xd19c, 0x1180, 0x2011, 0x0001, 0x080c, 0x40d0, 0x190c,
+ 0x40d0, 0x0048, 0x8001, 0x7056, 0x782c, 0xd0fc, 0x1d38, 0x2011,
+ 0x0001, 0x080c, 0x40d0, 0x008e, 0x00ee, 0x00fe, 0x0005, 0x00f6,
+ 0x00e6, 0x00c6, 0x0086, 0x2061, 0x0200, 0x2001, 0x19a9, 0x2004,
+ 0x601a, 0x2061, 0x0100, 0x2001, 0x19a8, 0x2004, 0x60ce, 0x6104,
+ 0xc1ac, 0x6106, 0x2001, 0x002c, 0x2004, 0x9005, 0x0520, 0x2038,
+ 0x2001, 0x002e, 0x2024, 0x2001, 0x002f, 0x201c, 0x080c, 0x4af2,
+ 0xa813, 0x0019, 0xaf16, 0x2900, 0xa85a, 0x978a, 0x0007, 0x0220,
+ 0x2138, 0x2009, 0x0007, 0x0010, 0x2708, 0x903e, 0x0096, 0xa858,
+ 0x2048, 0xa85c, 0x9080, 0x0019, 0x009e, 0x080c, 0x41e7, 0x1d68,
+ 0x2900, 0xa85a, 0x00d0, 0x080c, 0x4af2, 0xa813, 0x0019, 0xa817,
+ 0x0001, 0x2900, 0xa85a, 0x2001, 0x002e, 0x2004, 0xa866, 0x2001,
+ 0x002f, 0x2004, 0xa86a, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8,
+ 0xa86e, 0x2001, 0x002b, 0x2004, 0xa872, 0x2061, 0x0090, 0x2079,
+ 0x0100, 0x2001, 0x19a8, 0x2004, 0x6036, 0x2009, 0x0040, 0x080c,
+ 0x220a, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8, 0x601a, 0x0006,
+ 0x2001, 0x002b, 0x2004, 0x601e, 0x78c6, 0x000e, 0x78ca, 0x9006,
+ 0x600a, 0x600e, 0x008e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x00e6,
+ 0x2071, 0x0080, 0xaa60, 0x22e8, 0x20a0, 0x20e1, 0x0000, 0x2099,
+ 0x0088, 0x702b, 0x0026, 0x7402, 0x7306, 0x9006, 0x700a, 0x700e,
+ 0x810b, 0x810b, 0x21a8, 0x810b, 0x7112, 0x702b, 0x0041, 0x702c,
+ 0xd0fc, 0x0de8, 0x702b, 0x0002, 0x702b, 0x0040, 0x4005, 0x7400,
+ 0x7304, 0x87ff, 0x0190, 0x0086, 0x0096, 0x2940, 0x0086, 0x080c,
+ 0x4af2, 0x008e, 0xa058, 0x00a6, 0x2050, 0x2900, 0xb006, 0xa05a,
+ 0x00ae, 0x009e, 0x008e, 0x9085, 0x0001, 0x00ee, 0x0005, 0x00e6,
+ 0x2001, 0x002d, 0x2004, 0x9005, 0x0528, 0x2038, 0x2001, 0x0030,
+ 0x2024, 0x2001, 0x0031, 0x201c, 0x080c, 0x4af2, 0x2940, 0xa813,
+ 0x0019, 0xaf16, 0x2900, 0xa85a, 0x978a, 0x0007, 0x0220, 0x2138,
+ 0x2009, 0x0007, 0x0010, 0x2708, 0x903e, 0x0096, 0xa858, 0x2048,
+ 0xa85c, 0x9080, 0x0019, 0x009e, 0x080c, 0x41e7, 0x1d68, 0x2900,
+ 0xa85a, 0x00d8, 0x080c, 0x4af2, 0x2940, 0xa013, 0x0019, 0xa017,
+ 0x0001, 0x2800, 0xa05a, 0x2001, 0x0030, 0x2004, 0xa066, 0x2001,
+ 0x0031, 0x2004, 0xa06a, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8,
+ 0xa06e, 0x2001, 0x002b, 0x2004, 0xa072, 0x2001, 0x032a, 0x2003,
+ 0x0004, 0x7884, 0xd0ac, 0x1180, 0x2001, 0x0101, 0x200c, 0x918d,
+ 0x0200, 0x2102, 0xa017, 0x0000, 0x2001, 0x1a6a, 0x2003, 0x0003,
+ 0x2001, 0x032a, 0x2003, 0x0009, 0x2001, 0x0300, 0x2003, 0x0000,
+ 0x2001, 0x020d, 0x2003, 0x0000, 0x2001, 0x0004, 0x200c, 0x918d,
+ 0x0002, 0x2102, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000, 0x20a9,
+ 0x0007, 0x20a1, 0x1840, 0x20e9, 0x0001, 0x9006, 0x4004, 0x20a9,
+ 0x0014, 0x20a1, 0xffec, 0x20e9, 0x0000, 0x9006, 0x4004, 0x2009,
+ 0x013c, 0x200a, 0x012e, 0x7880, 0x9086, 0x0052, 0x0108, 0x0005,
+ 0x0804, 0x35b8, 0x7d98, 0x7c9c, 0x0804, 0x36ba, 0x080c, 0x753d,
+ 0x190c, 0x6057, 0x6040, 0x9084, 0x0020, 0x09b1, 0x2069, 0x1847,
+ 0x2d00, 0x2009, 0x0030, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x2039,
+ 0x0001, 0x080c, 0x4b3b, 0x701f, 0x42c6, 0x0005, 0x080c, 0x574d,
+ 0x1130, 0x3b00, 0x3a08, 0xc194, 0xc095, 0x20d8, 0x21d0, 0x2069,
+ 0x1847, 0x6800, 0x9005, 0x0904, 0x35ed, 0x6804, 0xd0ac, 0x0118,
+ 0xd0a4, 0x0904, 0x35ed, 0xd094, 0x00c6, 0x2061, 0x0100, 0x6104,
+ 0x0138, 0x6200, 0x9292, 0x0005, 0x0218, 0x918c, 0xffdf, 0x0010,
+ 0x918d, 0x0020, 0x6106, 0x00ce, 0xd08c, 0x00c6, 0x2061, 0x0100,
+ 0x6104, 0x0118, 0x918d, 0x0010, 0x0010, 0x918c, 0xffef, 0x6106,
+ 0x00ce, 0xd084, 0x0158, 0x6a28, 0x928a, 0x007f, 0x1a04, 0x35ed,
+ 0x9288, 0x33b9, 0x210d, 0x918c, 0x00ff, 0x6166, 0xd0dc, 0x0130,
+ 0x6828, 0x908a, 0x007f, 0x1a04, 0x35ed, 0x605e, 0x6888, 0x9084,
+ 0x0030, 0x8004, 0x8004, 0x8004, 0x8004, 0x0006, 0x2009, 0x19b0,
+ 0x9080, 0x279d, 0x2005, 0x200a, 0x2008, 0x2001, 0x0018, 0x080c,
+ 0xa90f, 0x2009, 0x0390, 0x200b, 0x0400, 0x000e, 0x2009, 0x19b1,
+ 0x9080, 0x27a1, 0x2005, 0x200a, 0x6808, 0x908a, 0x0100, 0x0a04,
+ 0x35ed, 0x908a, 0x0841, 0x1a04, 0x35ed, 0x9084, 0x0007, 0x1904,
+ 0x35ed, 0x680c, 0x9005, 0x0904, 0x35ed, 0x6810, 0x9005, 0x0904,
+ 0x35ed, 0x6848, 0x6940, 0x910a, 0x1a04, 0x35ed, 0x8001, 0x0904,
+ 0x35ed, 0x684c, 0x6944, 0x910a, 0x1a04, 0x35ed, 0x8001, 0x0904,
+ 0x35ed, 0x6814, 0x908c, 0x00ff, 0x614e, 0x8007, 0x9084, 0x00ff,
+ 0x6052, 0x080c, 0x7871, 0x080c, 0x6b0f, 0x080c, 0x6b43, 0x6808,
+ 0x602a, 0x080c, 0x217c, 0x2009, 0x0170, 0x200b, 0x0080, 0xa001,
+ 0xa001, 0x200b, 0x0000, 0x0036, 0x6b08, 0x080c, 0x2704, 0x003e,
+ 0x6000, 0x9086, 0x0000, 0x1904, 0x4451, 0x6818, 0x691c, 0x6a20,
+ 0x6b24, 0x8007, 0x810f, 0x8217, 0x831f, 0x6016, 0x611a, 0x621e,
+ 0x6322, 0x6c04, 0xd4f4, 0x0148, 0x6830, 0x6934, 0x6a38, 0x6b3c,
+ 0x8007, 0x810f, 0x8217, 0x831f, 0x0010, 0x9084, 0xf0ff, 0x6006,
+ 0x610a, 0x620e, 0x6312, 0x8007, 0x810f, 0x8217, 0x831f, 0x20a9,
+ 0x0004, 0x20a1, 0x19b2, 0x20e9, 0x0001, 0x4001, 0x20a9, 0x0004,
+ 0x20a1, 0x19cc, 0x20e9, 0x0001, 0x4001, 0x080c, 0x885b, 0x00c6,
+ 0x900e, 0x20a9, 0x0001, 0x6b70, 0xd384, 0x01c8, 0x0020, 0x839d,
+ 0x12b0, 0x3508, 0x8109, 0x080c, 0x7e33, 0x6878, 0x6016, 0x6874,
+ 0x2008, 0x9084, 0xff00, 0x8007, 0x600a, 0x9184, 0x00ff, 0x6006,
+ 0x8108, 0x1118, 0x6003, 0x0003, 0x0010, 0x6003, 0x0001, 0x1f04,
+ 0x43af, 0x00ce, 0x00c6, 0x2061, 0x199b, 0x6a88, 0x9284, 0xc000,
+ 0x2010, 0x9286, 0x0000, 0x1158, 0x2063, 0x0000, 0x2001, 0x0001,
+ 0x080c, 0x29ab, 0x2001, 0x0001, 0x080c, 0x298e, 0x0088, 0x9286,
+ 0x4000, 0x1148, 0x2063, 0x0001, 0x9006, 0x080c, 0x29ab, 0x9006,
+ 0x080c, 0x298e, 0x0028, 0x9286, 0x8000, 0x1d30, 0x2063, 0x0002,
+ 0x00ce, 0x00e6, 0x2c70, 0x080c, 0x0ec1, 0x00ee, 0x6888, 0xd0ec,
+ 0x0130, 0x2011, 0x0114, 0x2204, 0x9085, 0x0180, 0x2012, 0x6a80,
+ 0x9284, 0x0030, 0x9086, 0x0030, 0x1128, 0x9294, 0xffcf, 0x9295,
+ 0x0020, 0x6a82, 0x2001, 0x197b, 0x6a80, 0x9294, 0x0030, 0x928e,
+ 0x0000, 0x0170, 0x928e, 0x0010, 0x0118, 0x928e, 0x0020, 0x0140,
+ 0x2003, 0xaaaa, 0x080c, 0x2779, 0x2001, 0x196c, 0x2102, 0x0008,
+ 0x2102, 0x00c6, 0x2061, 0x0100, 0x602f, 0x0040, 0x602f, 0x0000,
+ 0x00ce, 0x080c, 0x753d, 0x0128, 0x080c, 0x5027, 0x0110, 0x080c,
+ 0x26ca, 0x60d4, 0x9005, 0x01c0, 0x6003, 0x0001, 0x2009, 0x4439,
+ 0x00e0, 0x080c, 0x753d, 0x1168, 0x2011, 0x73b3, 0x080c, 0x86c8,
+ 0x2011, 0x73a6, 0x080c, 0x87d4, 0x080c, 0x7845, 0x080c, 0x746e,
+ 0x0040, 0x080c, 0x5f4d, 0x0028, 0x6003, 0x0004, 0x2009, 0x4451,
+ 0x0020, 0x080c, 0x6a3f, 0x0804, 0x35b8, 0x2001, 0x0170, 0x2004,
+ 0x9084, 0x00ff, 0x9086, 0x004c, 0x1118, 0x2091, 0x31bd, 0x0817,
+ 0x2091, 0x313d, 0x0817, 0x6000, 0x9086, 0x0000, 0x0904, 0x35ea,
+ 0x2069, 0x1847, 0x7890, 0x6842, 0x7894, 0x6846, 0x2d00, 0x2009,
+ 0x0030, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x2039, 0x0001, 0x0804,
+ 0x4b3e, 0x9006, 0x080c, 0x26ca, 0x81ff, 0x1904, 0x35ea, 0x080c,
+ 0x753d, 0x11b0, 0x080c, 0x7840, 0x080c, 0x6092, 0x080c, 0x33ad,
+ 0x0118, 0x6130, 0xc18d, 0x6132, 0x080c, 0xd09b, 0x0130, 0x080c,
+ 0x7560, 0x1118, 0x080c, 0x7511, 0x0038, 0x080c, 0x746e, 0x0020,
+ 0x080c, 0x6057, 0x080c, 0x5f4d, 0x0804, 0x35b8, 0x81ff, 0x1904,
+ 0x35ea, 0x080c, 0x753d, 0x1110, 0x0804, 0x35ea, 0x6194, 0x81ff,
+ 0x01a8, 0x704f, 0x0000, 0x2001, 0x1d80, 0x2009, 0x0040, 0x7a8c,
+ 0x7b88, 0x7c9c, 0x7d98, 0x0126, 0x2091, 0x8000, 0x2039, 0x0001,
+ 0x080c, 0x4b3e, 0x701f, 0x35b6, 0x012e, 0x0005, 0x704f, 0x0001,
+ 0x00d6, 0x2069, 0x1d80, 0x20a9, 0x0040, 0x20e9, 0x0001, 0x20a1,
+ 0x1d80, 0x2019, 0xffff, 0x4304, 0x655c, 0x9588, 0x33b9, 0x210d,
+ 0x918c, 0x00ff, 0x216a, 0x900e, 0x2011, 0x0002, 0x2100, 0x9506,
+ 0x01a8, 0x080c, 0x6693, 0x1190, 0xb814, 0x821c, 0x0238, 0x9398,
+ 0x1d80, 0x9085, 0xff00, 0x8007, 0x201a, 0x0038, 0x9398, 0x1d80,
+ 0x2324, 0x94a4, 0xff00, 0x9405, 0x201a, 0x8210, 0x8108, 0x9182,
+ 0x0080, 0x1208, 0x0c18, 0x8201, 0x8007, 0x2d0c, 0x9105, 0x206a,
+ 0x00de, 0x20a9, 0x0040, 0x20a1, 0x1d80, 0x2099, 0x1d80, 0x080c,
+ 0x5fe2, 0x0804, 0x44ab, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x080c,
+ 0x4af2, 0x1120, 0x2009, 0x0002, 0x0804, 0x35ea, 0x080c, 0x573e,
+ 0xd0b4, 0x0558, 0x7884, 0x908e, 0x007e, 0x0538, 0x908e, 0x007f,
+ 0x0520, 0x908e, 0x0080, 0x0508, 0x080c, 0x33a8, 0x1148, 0xb800,
+ 0xd08c, 0x11d8, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x11a8,
+ 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xcb4b, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x35ea, 0x7007, 0x0003, 0x701f, 0x4539,
+ 0x0005, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x20a9, 0x002b, 0xb8c4,
+ 0x20e0, 0xb8c8, 0x2098, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0002,
+ 0x20a0, 0x4003, 0x20a9, 0x0008, 0x9080, 0x0006, 0x20a0, 0xb8c4,
+ 0x20e0, 0xb8c8, 0x9080, 0x0006, 0x2098, 0x080c, 0x0fc4, 0x0070,
+ 0x20a9, 0x0004, 0xa85c, 0x9080, 0x000a, 0x20a0, 0xb8c4, 0x20e0,
+ 0xb8c8, 0x9080, 0x000a, 0x2098, 0x080c, 0x0fc4, 0x8906, 0x8006,
+ 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002, 0x2009,
+ 0x002b, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804, 0x4b3e, 0x81ff,
+ 0x1904, 0x35ea, 0x080c, 0x4b09, 0x0904, 0x35ed, 0x080c, 0x68d8,
+ 0x0904, 0x35ea, 0x0058, 0xa878, 0x9005, 0x0120, 0x2009, 0x0004,
+ 0x0804, 0x35ea, 0xa974, 0xaa94, 0x0804, 0x35b8, 0x080c, 0x5746,
+ 0x0904, 0x35b8, 0x701f, 0x4583, 0x7007, 0x0003, 0x0005, 0x81ff,
+ 0x1904, 0x35ea, 0x7888, 0x908a, 0x1000, 0x1a04, 0x35ed, 0x080c,
+ 0x4b25, 0x0904, 0x35ed, 0x080c, 0x6add, 0x0120, 0x080c, 0x6ae5,
+ 0x1904, 0x35ed, 0x080c, 0x695d, 0x0904, 0x35ea, 0x2019, 0x0004,
+ 0x900e, 0x080c, 0x68ea, 0x0904, 0x35ea, 0x7984, 0x7a88, 0x04c9,
+ 0x08a8, 0xa89c, 0x908a, 0x1000, 0x12f8, 0x080c, 0x4b23, 0x01e0,
+ 0x080c, 0x6add, 0x0118, 0x080c, 0x6ae5, 0x11b0, 0x080c, 0x695d,
+ 0x2009, 0x0002, 0x0168, 0x2009, 0x0002, 0x2019, 0x0004, 0x080c,
+ 0x68ea, 0x2009, 0x0003, 0x0120, 0xa998, 0xaa9c, 0x00d1, 0x0060,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x080c, 0x5746,
+ 0x0110, 0x9006, 0x0018, 0x900e, 0x9085, 0x0001, 0x2001, 0x0000,
+ 0x0005, 0x9186, 0x00ff, 0x0110, 0x0071, 0x0060, 0x2029, 0x007e,
+ 0x2061, 0x1800, 0x645c, 0x2400, 0x9506, 0x0110, 0x2508, 0x0019,
+ 0x8529, 0x1ec8, 0x0005, 0x080c, 0x6693, 0x1138, 0x2200, 0x8003,
+ 0x800b, 0x810b, 0x9108, 0x080c, 0x86d6, 0x0005, 0x81ff, 0x1904,
+ 0x35ea, 0x798c, 0x2001, 0x197f, 0x918c, 0x8000, 0x2102, 0x080c,
+ 0x4b09, 0x0904, 0x35ed, 0x080c, 0x6add, 0x0120, 0x080c, 0x6ae5,
+ 0x1904, 0x35ed, 0x080c, 0x675a, 0x0904, 0x35ea, 0x080c, 0x68e1,
+ 0x0904, 0x35ea, 0x2001, 0x197f, 0x2004, 0xd0fc, 0x1904, 0x35b8,
+ 0x0804, 0x458e, 0xa9a0, 0x2001, 0x197f, 0x918c, 0x8000, 0xc18d,
+ 0x2102, 0x080c, 0x4b16, 0x01a0, 0x080c, 0x6add, 0x0118, 0x080c,
+ 0x6ae5, 0x1170, 0x080c, 0x675a, 0x2009, 0x0002, 0x0128, 0x080c,
+ 0x68e1, 0x1170, 0x2009, 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010,
+ 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x0005,
+ 0xa897, 0x4000, 0x2001, 0x197f, 0x2004, 0xd0fc, 0x1128, 0x080c,
+ 0x5746, 0x0110, 0x9006, 0x0018, 0x900e, 0x9085, 0x0001, 0x2001,
+ 0x0000, 0x0005, 0x81ff, 0x1904, 0x35ea, 0x798c, 0x2001, 0x197e,
+ 0x918c, 0x8000, 0x2102, 0x080c, 0x4b09, 0x0904, 0x35ed, 0x080c,
+ 0x6add, 0x0120, 0x080c, 0x6ae5, 0x1904, 0x35ed, 0x080c, 0x675a,
+ 0x0904, 0x35ea, 0x080c, 0x68cf, 0x0904, 0x35ea, 0x2001, 0x197e,
+ 0x2004, 0xd0fc, 0x1904, 0x35b8, 0x0804, 0x458e, 0xa9a0, 0x2001,
+ 0x197e, 0x918c, 0x8000, 0xc18d, 0x2102, 0x080c, 0x4b16, 0x01a0,
+ 0x080c, 0x6add, 0x0118, 0x080c, 0x6ae5, 0x1170, 0x080c, 0x675a,
+ 0x2009, 0x0002, 0x0128, 0x080c, 0x68cf, 0x1170, 0x2009, 0x0003,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x2001, 0x197e,
+ 0x2004, 0xd0fc, 0x1128, 0x080c, 0x5746, 0x0110, 0x9006, 0x0018,
+ 0x900e, 0x9085, 0x0001, 0x2001, 0x0000, 0x0005, 0x6100, 0x0804,
+ 0x35b8, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x080c, 0x5752, 0x1904,
+ 0x35ea, 0x79a8, 0xd184, 0x1158, 0xb834, 0x8007, 0x789e, 0xb830,
+ 0x8007, 0x789a, 0xbb2c, 0x831f, 0xba28, 0x8217, 0x0050, 0xb824,
+ 0x8007, 0x789e, 0xb820, 0x8007, 0x789a, 0xbb1c, 0x831f, 0xba18,
+ 0x8217, 0xb900, 0x918c, 0x0202, 0x0804, 0x35b8, 0x78a8, 0x909c,
+ 0x0003, 0xd0ac, 0x1150, 0xd0b4, 0x1140, 0x939a, 0x0003, 0x1a04,
+ 0x35ea, 0x625c, 0x7884, 0x9206, 0x1548, 0x080c, 0x8845, 0x2001,
+ 0xffec, 0x2009, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x2039,
+ 0x0000, 0x0006, 0x78a8, 0x9084, 0x0080, 0x1118, 0x000e, 0x0804,
+ 0x4b3e, 0x000e, 0x2031, 0x0000, 0x2061, 0x18b8, 0x2c44, 0xa66a,
+ 0xa17a, 0xa772, 0xa076, 0xa28e, 0xa392, 0xa496, 0xa59a, 0x080c,
+ 0x113c, 0x7007, 0x0002, 0x701f, 0x4746, 0x0005, 0x81ff, 0x1904,
+ 0x35ea, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x080c, 0x6add, 0x1904,
+ 0x35ea, 0x00c6, 0x080c, 0x4af2, 0x00ce, 0x0904, 0x35ea, 0xa867,
+ 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x7ea8, 0x080c, 0xcaf1, 0x0904,
+ 0x35ea, 0x7007, 0x0003, 0x701f, 0x474a, 0x0005, 0x080c, 0x428c,
+ 0x0804, 0x35b8, 0xa830, 0x9086, 0x0100, 0x0904, 0x35ea, 0x8906,
+ 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b,
+ 0x2009, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804, 0x4b3e,
+ 0x9006, 0x080c, 0x26ca, 0x78a8, 0x9084, 0x00ff, 0x9086, 0x00ff,
+ 0x0118, 0x81ff, 0x1904, 0x35ea, 0x080c, 0x753d, 0x0110, 0x080c,
+ 0x6057, 0x7888, 0x908a, 0x1000, 0x1a04, 0x35ed, 0x7984, 0x9186,
+ 0x00ff, 0x0138, 0x9182, 0x007f, 0x1a04, 0x35ed, 0x2100, 0x080c,
+ 0x2694, 0x0026, 0x00c6, 0x0126, 0x2091, 0x8000, 0x2061, 0x1a02,
+ 0x601b, 0x0000, 0x601f, 0x0000, 0x6073, 0x0000, 0x6077, 0x0000,
+ 0x080c, 0x753d, 0x1158, 0x080c, 0x7840, 0x080c, 0x6092, 0x9085,
+ 0x0001, 0x080c, 0x7584, 0x080c, 0x746e, 0x00f0, 0x080c, 0xa91e,
+ 0x080c, 0xabe9, 0x080c, 0xa93a, 0x2061, 0x0100, 0x2001, 0x1818,
+ 0x2004, 0x9084, 0x00ff, 0x810f, 0x9105, 0x604a, 0x6043, 0x0090,
+ 0x6043, 0x0010, 0x2009, 0x1998, 0x200b, 0x0000, 0x2009, 0x002d,
+ 0x2011, 0x5f7d, 0x080c, 0x8792, 0x7984, 0x080c, 0x753d, 0x1110,
+ 0x2009, 0x00ff, 0x7a88, 0x080c, 0x45f1, 0x012e, 0x00ce, 0x002e,
+ 0x0804, 0x35b8, 0x7984, 0x080c, 0x6632, 0x2b08, 0x1904, 0x35ed,
+ 0x0804, 0x35b8, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x35ea,
+ 0x60dc, 0xd0ac, 0x1130, 0xd09c, 0x1120, 0x2009, 0x0005, 0x0804,
+ 0x35ea, 0x080c, 0x4af2, 0x1120, 0x2009, 0x0002, 0x0804, 0x35ea,
+ 0x7984, 0x9192, 0x0021, 0x1a04, 0x35ed, 0x7a8c, 0x7b88, 0x7c9c,
+ 0x7d98, 0xa85c, 0x9080, 0x0019, 0x702a, 0xaf60, 0x7736, 0x080c,
+ 0x4b3b, 0x701f, 0x4802, 0x7880, 0x9086, 0x006e, 0x0110, 0x701f,
+ 0x51d9, 0x0005, 0x2009, 0x0080, 0x080c, 0x6693, 0x1118, 0x080c,
+ 0x6add, 0x0120, 0x2021, 0x400a, 0x0804, 0x35ba, 0x00d6, 0x0096,
+ 0xa964, 0xaa6c, 0xab70, 0xac74, 0xad78, 0xae7c, 0xa884, 0x90be,
+ 0x0100, 0x0904, 0x489b, 0x90be, 0x0112, 0x0904, 0x489b, 0x90be,
+ 0x0113, 0x0904, 0x489b, 0x90be, 0x0114, 0x0904, 0x489b, 0x90be,
+ 0x0117, 0x0904, 0x489b, 0x90be, 0x011a, 0x0904, 0x489b, 0x90be,
+ 0x011c, 0x0904, 0x489b, 0x90be, 0x0121, 0x0904, 0x4882, 0x90be,
+ 0x0131, 0x0904, 0x4882, 0x90be, 0x0171, 0x0904, 0x489b, 0x90be,
+ 0x0173, 0x0904, 0x489b, 0x90be, 0x01a1, 0x1128, 0xa894, 0x8007,
+ 0xa896, 0x0804, 0x48a6, 0x90be, 0x0212, 0x0904, 0x488f, 0x90be,
+ 0x0213, 0x05e8, 0x90be, 0x0214, 0x0500, 0x90be, 0x0217, 0x0188,
+ 0x90be, 0x021a, 0x1120, 0xa89c, 0x8007, 0xa89e, 0x04e0, 0x90be,
+ 0x021f, 0x05c8, 0x90be, 0x0300, 0x05b0, 0x009e, 0x00de, 0x0804,
+ 0x35ed, 0x7028, 0x9080, 0x0010, 0x2098, 0x20a0, 0x7034, 0x20e0,
+ 0x20e8, 0x20a9, 0x0007, 0x080c, 0x48e4, 0x7028, 0x9080, 0x000e,
+ 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x080c,
+ 0x48e4, 0x00c8, 0x7028, 0x9080, 0x000c, 0x2098, 0x20a0, 0x7034,
+ 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x080c, 0x48f1, 0x00b8, 0x7028,
+ 0x9080, 0x000e, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9,
+ 0x0001, 0x080c, 0x48f1, 0x7028, 0x9080, 0x000c, 0x2098, 0x20a0,
+ 0x7034, 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x04f1, 0x00c6, 0x080c,
+ 0x4af2, 0x0550, 0xa868, 0xc0fd, 0xa86a, 0xa867, 0x0119, 0x9006,
+ 0xa882, 0xa87f, 0x0020, 0xa88b, 0x0001, 0x810b, 0xa9ae, 0xa8b2,
+ 0xaab6, 0xabba, 0xacbe, 0xadc2, 0xa9c6, 0xa8ca, 0x00ce, 0x009e,
+ 0x00de, 0xa866, 0xa822, 0xa868, 0xc0fd, 0xa86a, 0xa804, 0x2048,
+ 0x080c, 0xcb0c, 0x1120, 0x2009, 0x0003, 0x0804, 0x35ea, 0x7007,
+ 0x0003, 0x701f, 0x48db, 0x0005, 0x00ce, 0x009e, 0x00de, 0x2009,
+ 0x0002, 0x0804, 0x35ea, 0xa820, 0x9086, 0x8001, 0x1904, 0x35b8,
+ 0x2009, 0x0004, 0x0804, 0x35ea, 0x0016, 0x0026, 0x3510, 0x20a9,
+ 0x0002, 0x4002, 0x4104, 0x4004, 0x8211, 0x1dc8, 0x002e, 0x001e,
+ 0x0005, 0x0016, 0x0026, 0x0036, 0x0046, 0x3520, 0x20a9, 0x0004,
+ 0x4002, 0x4304, 0x4204, 0x4104, 0x4004, 0x8421, 0x1db8, 0x004e,
+ 0x003e, 0x002e, 0x001e, 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x35ea, 0x60dc, 0xd0ac, 0x1130, 0xd09c, 0x1120, 0x2009,
+ 0x0005, 0x0804, 0x35ea, 0x7984, 0x78a8, 0x2040, 0x080c, 0xabe2,
+ 0x1120, 0x9182, 0x007f, 0x0a04, 0x35ed, 0x9186, 0x00ff, 0x0904,
+ 0x35ed, 0x9182, 0x0800, 0x1a04, 0x35ed, 0x7a8c, 0x7b88, 0x607c,
+ 0x9306, 0x1158, 0x6080, 0x924e, 0x0904, 0x35ed, 0x080c, 0xabe2,
+ 0x1120, 0x99cc, 0xff00, 0x0904, 0x35ed, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x4a05, 0x0904, 0x4985, 0x0086, 0x90c6, 0x4000, 0x008e,
+ 0x1538, 0x00c6, 0x0006, 0x0036, 0xb818, 0xbb1c, 0x9305, 0xbb20,
+ 0x9305, 0xbb24, 0x9305, 0xbb28, 0x9305, 0xbb2c, 0x9305, 0xbb30,
+ 0x9305, 0xbb34, 0x9305, 0x003e, 0x0570, 0xd88c, 0x1128, 0x080c,
+ 0x6add, 0x0110, 0xc89d, 0x0438, 0x900e, 0x080c, 0x6986, 0x1108,
+ 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0x000e, 0x00ce, 0x00b8,
+ 0x90c6, 0x4007, 0x1110, 0x2408, 0x0090, 0x90c6, 0x4008, 0x1118,
+ 0x2708, 0x2610, 0x0060, 0x90c6, 0x4009, 0x1108, 0x0040, 0x90c6,
+ 0x4006, 0x1108, 0x0020, 0x2001, 0x4005, 0x2009, 0x000a, 0x2020,
+ 0x012e, 0x0804, 0x35ba, 0x000e, 0x00ce, 0x2b00, 0x7026, 0x0016,
+ 0x00b6, 0x00c6, 0x00e6, 0x2c70, 0x080c, 0xad20, 0x0904, 0x49da,
+ 0x2b00, 0x6012, 0x080c, 0xce15, 0x2e58, 0x00ee, 0x00e6, 0x00c6,
+ 0x080c, 0x4af2, 0x00ce, 0x2b70, 0x1158, 0x080c, 0xacb0, 0x00ee,
+ 0x00ce, 0x00be, 0x001e, 0x012e, 0x2009, 0x0002, 0x0804, 0x35ea,
+ 0x900e, 0xa966, 0xa96a, 0x2900, 0x6016, 0xa932, 0xa868, 0xc0fd,
+ 0xd88c, 0x0108, 0xc0f5, 0xa86a, 0xd89c, 0x1110, 0x080c, 0x3240,
+ 0x6023, 0x0001, 0x9006, 0x080c, 0x65cf, 0xd89c, 0x0138, 0x2001,
+ 0x0004, 0x080c, 0x65e3, 0x2009, 0x0003, 0x0030, 0x2001, 0x0002,
+ 0x080c, 0x65e3, 0x2009, 0x0002, 0x080c, 0xad4d, 0x78a8, 0xd094,
+ 0x0138, 0x00ee, 0x7024, 0x00e6, 0x2058, 0xb8d4, 0xc08d, 0xb8d6,
+ 0x9085, 0x0001, 0x00ee, 0x00ce, 0x00be, 0x001e, 0x012e, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x35ea, 0x7007, 0x0003, 0x701f, 0x49e9,
+ 0x0005, 0xa830, 0x9086, 0x0100, 0x7024, 0x2058, 0x1138, 0x2009,
+ 0x0004, 0xba04, 0x9294, 0x00ff, 0x0804, 0x568c, 0x900e, 0xa868,
+ 0xd0f4, 0x1904, 0x35b8, 0x080c, 0x6986, 0x1108, 0xc185, 0xb800,
+ 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x35b8, 0x00e6, 0x00d6, 0x0096,
+ 0x83ff, 0x0904, 0x4a54, 0x902e, 0x080c, 0xabe2, 0x0130, 0x9026,
+ 0x20a9, 0x0800, 0x2071, 0x1000, 0x0030, 0x2021, 0x007f, 0x20a9,
+ 0x0781, 0x2071, 0x107f, 0x2e04, 0x9005, 0x11b8, 0x2100, 0x9406,
+ 0x1904, 0x4a65, 0x2428, 0x94ce, 0x007f, 0x1120, 0x92ce, 0xfffd,
+ 0x1558, 0x0030, 0x94ce, 0x0080, 0x1130, 0x92ce, 0xfffc, 0x1520,
+ 0x93ce, 0x00ff, 0x1508, 0xc5fd, 0x0480, 0x2058, 0xbf10, 0x2700,
+ 0x9306, 0x11e8, 0xbe14, 0x2600, 0x9206, 0x11c8, 0x2400, 0x9106,
+ 0x1180, 0xd884, 0x0598, 0xd894, 0x1588, 0x080c, 0x6a7d, 0x1570,
+ 0x2001, 0x4000, 0x0460, 0x080c, 0x6add, 0x1540, 0x2001, 0x4000,
+ 0x0430, 0x2001, 0x4007, 0x0418, 0x2001, 0x4006, 0x0400, 0x2400,
+ 0x9106, 0x1158, 0xbe14, 0x87ff, 0x1128, 0x86ff, 0x0918, 0x080c,
+ 0xabe2, 0x1900, 0x2001, 0x4008, 0x0090, 0x8420, 0x8e70, 0x1f04,
+ 0x4a1b, 0x85ff, 0x1130, 0x2001, 0x4009, 0x0048, 0x2001, 0x0001,
+ 0x0030, 0x080c, 0x6632, 0x1dd0, 0xbb12, 0xba16, 0x9006, 0x9005,
+ 0x009e, 0x00de, 0x00ee, 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x35ea, 0x080c, 0x4af2, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x35ea, 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x7884, 0x9005,
+ 0x0904, 0x35ed, 0x9096, 0x00ff, 0x0120, 0x9092, 0x0004, 0x1a04,
+ 0x35ed, 0x2010, 0x2918, 0x080c, 0x31e0, 0x1120, 0x2009, 0x0003,
+ 0x0804, 0x35ea, 0x7007, 0x0003, 0x701f, 0x4aa7, 0x0005, 0xa830,
+ 0x9086, 0x0100, 0x1904, 0x35b8, 0x2009, 0x0004, 0x0804, 0x35ea,
+ 0x7984, 0x080c, 0xabe2, 0x1120, 0x9182, 0x007f, 0x0a04, 0x35ed,
+ 0x9186, 0x00ff, 0x0904, 0x35ed, 0x9182, 0x0800, 0x1a04, 0x35ed,
+ 0x2001, 0x9400, 0x080c, 0x56e7, 0x1904, 0x35ea, 0x0804, 0x35b8,
+ 0xa998, 0x080c, 0xabe2, 0x1118, 0x9182, 0x007f, 0x0280, 0x9186,
+ 0x00ff, 0x0168, 0x9182, 0x0800, 0x1250, 0x2001, 0x9400, 0x080c,
+ 0x56e7, 0x11a8, 0x0060, 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897,
+ 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x0005, 0xa897,
+ 0x4000, 0x900e, 0x9085, 0x0001, 0x2001, 0x0000, 0x0005, 0x2009,
+ 0x000a, 0x0c48, 0x080c, 0x1047, 0x0198, 0x9006, 0xa802, 0x7014,
+ 0x9005, 0x1120, 0x2900, 0x7016, 0x701a, 0x0040, 0x7018, 0xa802,
+ 0x0086, 0x2040, 0x2900, 0xa006, 0x701a, 0x008e, 0x9085, 0x0001,
+ 0x0005, 0x7984, 0x080c, 0x6693, 0x1130, 0x7e88, 0x9684, 0x3fff,
+ 0x9082, 0x4000, 0x0208, 0x905e, 0x8bff, 0x0005, 0xa998, 0x080c,
+ 0x6693, 0x1130, 0xae9c, 0x9684, 0x3fff, 0x9082, 0x4000, 0x0208,
+ 0x905e, 0x8bff, 0x0005, 0xae98, 0x0008, 0x7e84, 0x2608, 0x080c,
+ 0x6693, 0x1108, 0x0008, 0x905e, 0x8bff, 0x0005, 0x0016, 0x7114,
+ 0x81ff, 0x0128, 0x2148, 0xa904, 0x080c, 0x1079, 0x0cc8, 0x7116,
+ 0x711a, 0x001e, 0x0005, 0x2031, 0x0001, 0x0010, 0x2031, 0x0000,
+ 0x2061, 0x18b8, 0x2c44, 0xa66a, 0xa17a, 0xa772, 0xa076, 0xa28e,
+ 0xa392, 0xa496, 0xa59a, 0x080c, 0x113c, 0x7007, 0x0002, 0x701f,
+ 0x35b8, 0x0005, 0x00f6, 0x0126, 0x2091, 0x8000, 0x2079, 0x0000,
+ 0x2001, 0x18b0, 0x2004, 0x9005, 0x1190, 0x0e04, 0x4b6f, 0x7a36,
+ 0x7833, 0x0012, 0x7a82, 0x7b86, 0x7c8a, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x0804, 0x4bd5, 0x0016,
+ 0x0086, 0x0096, 0x00c6, 0x00e6, 0x2071, 0x189e, 0x7044, 0x9005,
+ 0x1540, 0x7148, 0x9182, 0x0010, 0x0288, 0x7038, 0x2060, 0x080c,
+ 0x1047, 0x0904, 0x4bcd, 0xa84b, 0x0000, 0x2900, 0x7046, 0x2001,
+ 0x0002, 0x9080, 0x1eab, 0x2005, 0xa846, 0x0098, 0x7038, 0x90e0,
+ 0x0004, 0x2001, 0x18ba, 0x9c82, 0x18fa, 0x0210, 0x2061, 0x18ba,
+ 0x2c00, 0x703a, 0x7148, 0x81ff, 0x1108, 0x703e, 0x8108, 0x714a,
+ 0x0460, 0x7148, 0x8108, 0x714a, 0x7044, 0x2040, 0xa144, 0x2105,
+ 0x0016, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x2060, 0x001e, 0x8108,
+ 0x2105, 0x9005, 0xa146, 0x1520, 0x080c, 0x1047, 0x1130, 0x8109,
+ 0xa946, 0x7148, 0x8109, 0x714a, 0x00d8, 0x9006, 0xa806, 0xa84a,
+ 0xa046, 0x2800, 0xa802, 0x2900, 0xa006, 0x7046, 0x2001, 0x0002,
+ 0x9080, 0x1eab, 0x2005, 0xa846, 0x0058, 0x2262, 0x6306, 0x640a,
+ 0x00ee, 0x00ce, 0x009e, 0x008e, 0x001e, 0x012e, 0x00fe, 0x0005,
+ 0x2c00, 0x9082, 0x001b, 0x0002, 0x4bf7, 0x4bf7, 0x4bf9, 0x4bf7,
+ 0x4bf7, 0x4bf7, 0x4bfd, 0x4bf7, 0x4bf7, 0x4bf7, 0x4c01, 0x4bf7,
+ 0x4bf7, 0x4bf7, 0x4c05, 0x4bf7, 0x4bf7, 0x4bf7, 0x4c09, 0x4bf7,
+ 0x4bf7, 0x4bf7, 0x4c0d, 0x4bf7, 0x4bf7, 0x4bf7, 0x4c12, 0x080c,
+ 0x0d7d, 0xa276, 0xa37a, 0xa47e, 0x0898, 0xa286, 0xa38a, 0xa48e,
+ 0x0878, 0xa296, 0xa39a, 0xa49e, 0x0858, 0xa2a6, 0xa3aa, 0xa4ae,
+ 0x0838, 0xa2b6, 0xa3ba, 0xa4be, 0x0818, 0xa2c6, 0xa3ca, 0xa4ce,
+ 0x0804, 0x4bd0, 0xa2d6, 0xa3da, 0xa4de, 0x0804, 0x4bd0, 0x00e6,
+ 0x2071, 0x189e, 0x7048, 0x9005, 0x0904, 0x4ca9, 0x0126, 0x2091,
+ 0x8000, 0x0e04, 0x4ca8, 0x00f6, 0x2079, 0x0000, 0x00c6, 0x0096,
+ 0x0086, 0x0076, 0x9006, 0x2038, 0x7040, 0x2048, 0x9005, 0x0500,
+ 0xa948, 0x2105, 0x0016, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x2060,
+ 0x001e, 0x8108, 0x2105, 0x9005, 0xa94a, 0x1904, 0x4cab, 0xa804,
+ 0x9005, 0x090c, 0x0d7d, 0x7042, 0x2938, 0x2040, 0xa003, 0x0000,
+ 0x2001, 0x0002, 0x9080, 0x1eab, 0x2005, 0xa04a, 0x0804, 0x4cab,
+ 0x703c, 0x2060, 0x2c14, 0x6304, 0x6408, 0x650c, 0x2200, 0x7836,
+ 0x7833, 0x0012, 0x7882, 0x2300, 0x7886, 0x2400, 0x788a, 0x2091,
+ 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x87ff,
+ 0x0118, 0x2748, 0x080c, 0x1079, 0x7048, 0x8001, 0x704a, 0x9005,
+ 0x1170, 0x7040, 0x2048, 0x9005, 0x0128, 0x080c, 0x1079, 0x9006,
+ 0x7042, 0x7046, 0x703b, 0x18ba, 0x703f, 0x18ba, 0x0420, 0x7040,
+ 0x9005, 0x1508, 0x7238, 0x2c00, 0x9206, 0x0148, 0x9c80, 0x0004,
+ 0x90fa, 0x18fa, 0x0210, 0x2001, 0x18ba, 0x703e, 0x00a0, 0x9006,
+ 0x703e, 0x703a, 0x7044, 0x9005, 0x090c, 0x0d7d, 0x2048, 0xa800,
+ 0x9005, 0x1de0, 0x2900, 0x7042, 0x2001, 0x0002, 0x9080, 0x1eab,
+ 0x2005, 0xa84a, 0x0000, 0x007e, 0x008e, 0x009e, 0x00ce, 0x00fe,
+ 0x012e, 0x00ee, 0x0005, 0x2c00, 0x9082, 0x001b, 0x0002, 0x4cca,
+ 0x4cca, 0x4ccc, 0x4cca, 0x4cca, 0x4cca, 0x4cd1, 0x4cca, 0x4cca,
+ 0x4cca, 0x4cd6, 0x4cca, 0x4cca, 0x4cca, 0x4cdb, 0x4cca, 0x4cca,
+ 0x4cca, 0x4ce0, 0x4cca, 0x4cca, 0x4cca, 0x4ce5, 0x4cca, 0x4cca,
+ 0x4cca, 0x4cea, 0x080c, 0x0d7d, 0xaa74, 0xab78, 0xac7c, 0x0804,
+ 0x4c56, 0xaa84, 0xab88, 0xac8c, 0x0804, 0x4c56, 0xaa94, 0xab98,
+ 0xac9c, 0x0804, 0x4c56, 0xaaa4, 0xaba8, 0xacac, 0x0804, 0x4c56,
+ 0xaab4, 0xabb8, 0xacbc, 0x0804, 0x4c56, 0xaac4, 0xabc8, 0xaccc,
+ 0x0804, 0x4c56, 0xaad4, 0xabd8, 0xacdc, 0x0804, 0x4c56, 0x0016,
+ 0x0026, 0x0036, 0x00b6, 0x00c6, 0x2009, 0x007e, 0x080c, 0x6693,
+ 0x2019, 0x0001, 0xb85c, 0xd0ac, 0x0110, 0x2019, 0x0000, 0x2011,
+ 0x801b, 0x080c, 0x4b52, 0x00ce, 0x00be, 0x003e, 0x002e, 0x001e,
+ 0x0005, 0x0026, 0x080c, 0x573e, 0xd0c4, 0x0120, 0x2011, 0x8014,
+ 0x080c, 0x4b52, 0x002e, 0x0005, 0x81ff, 0x1904, 0x35ea, 0x0126,
+ 0x2091, 0x8000, 0x6030, 0xc08d, 0xc085, 0xc0ac, 0x6032, 0x080c,
+ 0x753d, 0x1158, 0x080c, 0x7840, 0x080c, 0x6092, 0x9085, 0x0001,
+ 0x080c, 0x7584, 0x080c, 0x746e, 0x0010, 0x080c, 0x5f4d, 0x012e,
+ 0x0804, 0x35b8, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x35ea,
+ 0x080c, 0x5752, 0x0120, 0x2009, 0x0007, 0x0804, 0x35ea, 0x080c,
+ 0x6ad5, 0x0120, 0x2009, 0x0008, 0x0804, 0x35ea, 0x7984, 0x080c,
+ 0x6632, 0x1904, 0x35ed, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x2b00,
+ 0x7026, 0x080c, 0x6add, 0x7888, 0x1170, 0x9084, 0x0005, 0x1158,
+ 0x900e, 0x080c, 0x6986, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108,
+ 0xc18d, 0x0804, 0x35b8, 0x080c, 0x4af2, 0x0904, 0x35ea, 0x9006,
+ 0xa866, 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xcbb3, 0x0904,
+ 0x35ea, 0x7888, 0xd094, 0x0118, 0xb8d4, 0xc08d, 0xb8d6, 0x7007,
+ 0x0003, 0x701f, 0x4dc5, 0x0005, 0x2061, 0x1800, 0x080c, 0x5752,
+ 0x2009, 0x0007, 0x1560, 0x080c, 0x6ad5, 0x0118, 0x2009, 0x0008,
+ 0x0430, 0xa998, 0x080c, 0x6632, 0x1530, 0x080c, 0x4b23, 0x0518,
+ 0x080c, 0x6add, 0xa89c, 0x1168, 0x9084, 0x0005, 0x1150, 0x900e,
+ 0x080c, 0x6986, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d,
+ 0x00d0, 0xa868, 0xc0fc, 0xa86a, 0x080c, 0xcbb3, 0x11e0, 0xa89c,
+ 0xd094, 0x0118, 0xb8d4, 0xc08d, 0xb8d6, 0x2009, 0x0003, 0xa897,
+ 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085, 0x0001,
+ 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0xa99a, 0x9006, 0x918d,
+ 0x0001, 0x2008, 0x0005, 0x9006, 0x0005, 0xa830, 0x9086, 0x0100,
+ 0x7024, 0x2058, 0x1110, 0x0804, 0x568c, 0x900e, 0x080c, 0x6986,
+ 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x35b8,
+ 0x080c, 0x5752, 0x0120, 0x2009, 0x0007, 0x0804, 0x35ea, 0x7f84,
+ 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x080c, 0x4af2, 0x1120, 0x2009,
+ 0x0002, 0x0804, 0x35ea, 0x900e, 0x2130, 0x7126, 0x7132, 0xa860,
+ 0x20e8, 0x7036, 0xa85c, 0x9080, 0x0005, 0x702a, 0x20a0, 0x080c,
+ 0x6693, 0x1904, 0x4e67, 0x080c, 0x6add, 0x0138, 0x080c, 0x6ae5,
+ 0x0120, 0x080c, 0x6a7d, 0x1904, 0x4e67, 0xd794, 0x1110, 0xd784,
+ 0x01a8, 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x0006, 0x2098, 0x3400,
+ 0xd794, 0x0160, 0x20a9, 0x0008, 0x4003, 0x2098, 0x20a0, 0x3d00,
+ 0x20e0, 0x20a9, 0x0002, 0x080c, 0x48f1, 0x0048, 0x20a9, 0x0004,
+ 0x4003, 0x2098, 0x20a0, 0x3d00, 0x20e0, 0x080c, 0x48f1, 0x9186,
+ 0x007e, 0x0170, 0x9186, 0x0080, 0x0158, 0x080c, 0x6add, 0x90c2,
+ 0x0006, 0x1210, 0xc1fd, 0x0020, 0x080c, 0x6986, 0x1108, 0xc1fd,
+ 0x4104, 0xc1fc, 0xd794, 0x0528, 0xb8c4, 0x20e0, 0xb8c8, 0x2060,
+ 0x9c80, 0x0000, 0x2098, 0x20a9, 0x0002, 0x4003, 0x9c80, 0x0003,
+ 0x2098, 0x20a9, 0x0001, 0x4005, 0x9c80, 0x0004, 0x2098, 0x3400,
+ 0x20a9, 0x0002, 0x4003, 0x2098, 0x20a0, 0x3d00, 0x20e0, 0x080c,
+ 0x48e4, 0x9c80, 0x0026, 0x2098, 0xb8c4, 0x20e0, 0x20a9, 0x0002,
+ 0x4003, 0xd794, 0x0110, 0x96b0, 0x000b, 0x96b0, 0x0005, 0x8108,
+ 0x080c, 0xabe2, 0x0118, 0x9186, 0x0800, 0x0040, 0xd78c, 0x0120,
+ 0x9186, 0x0800, 0x0170, 0x0018, 0x9186, 0x007e, 0x0150, 0xd794,
+ 0x0118, 0x9686, 0x0020, 0x0010, 0x9686, 0x0028, 0x0150, 0x0804,
+ 0x4df7, 0x86ff, 0x1120, 0x7124, 0x810b, 0x0804, 0x35b8, 0x7033,
+ 0x0001, 0x7122, 0x7024, 0x9600, 0x7026, 0x772e, 0x2061, 0x18b8,
+ 0x2c44, 0xa06b, 0x0000, 0xa67a, 0x7034, 0xa072, 0x7028, 0xa076,
+ 0xa28e, 0xa392, 0xa496, 0xa59a, 0x080c, 0x113c, 0x7007, 0x0002,
+ 0x701f, 0x4ea3, 0x0005, 0x7030, 0x9005, 0x1180, 0x7120, 0x7028,
+ 0x20a0, 0x772c, 0x9036, 0x7034, 0x20e8, 0x2061, 0x18b8, 0x2c44,
+ 0xa28c, 0xa390, 0xa494, 0xa598, 0x0804, 0x4df7, 0x7124, 0x810b,
+ 0x0804, 0x35b8, 0x2029, 0x007e, 0x7984, 0x7a88, 0x7b8c, 0x7c98,
+ 0x9184, 0xff00, 0x8007, 0x90e2, 0x0020, 0x0a04, 0x35ed, 0x9502,
+ 0x0a04, 0x35ed, 0x9184, 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x35ed,
+ 0x9502, 0x0a04, 0x35ed, 0x9284, 0xff00, 0x8007, 0x90e2, 0x0020,
+ 0x0a04, 0x35ed, 0x9502, 0x0a04, 0x35ed, 0x9284, 0x00ff, 0x90e2,
+ 0x0020, 0x0a04, 0x35ed, 0x9502, 0x0a04, 0x35ed, 0x9384, 0xff00,
+ 0x8007, 0x90e2, 0x0020, 0x0a04, 0x35ed, 0x9502, 0x0a04, 0x35ed,
+ 0x9384, 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x35ed, 0x9502, 0x0a04,
+ 0x35ed, 0x9484, 0xff00, 0x8007, 0x90e2, 0x0020, 0x0a04, 0x35ed,
+ 0x9502, 0x0a04, 0x35ed, 0x9484, 0x00ff, 0x90e2, 0x0020, 0x0a04,
+ 0x35ed, 0x9502, 0x0a04, 0x35ed, 0x2061, 0x1988, 0x6102, 0x6206,
+ 0x630a, 0x640e, 0x0804, 0x35b8, 0x080c, 0x4af2, 0x0904, 0x35ea,
+ 0x2009, 0x0016, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080,
+ 0x0019, 0xaf60, 0x080c, 0x4b3b, 0x701f, 0x4f27, 0x0005, 0x2001,
+ 0x0138, 0x2003, 0x0000, 0x00e6, 0x2071, 0x0300, 0x701c, 0xd0a4,
+ 0x1de8, 0x00ee, 0x20a9, 0x0016, 0x896e, 0x8d6e, 0x8d6f, 0x9d84,
+ 0xffc0, 0x9080, 0x0019, 0x2098, 0x9d84, 0x003f, 0x20e0, 0x2069,
+ 0x1877, 0x20e9, 0x0001, 0x2da0, 0x4003, 0x6800, 0x9005, 0x0904,
+ 0x4fa8, 0x6804, 0x2008, 0x918c, 0xfff8, 0x1904, 0x4fa8, 0x680c,
+ 0x9005, 0x0904, 0x4fa8, 0x9082, 0xff01, 0x1a04, 0x4fa8, 0x6810,
+ 0x9082, 0x005c, 0x0a04, 0x4fa8, 0x6824, 0x2008, 0x9082, 0x0008,
+ 0x0a04, 0x4fa8, 0x9182, 0x0400, 0x1a04, 0x4fa8, 0x0056, 0x2029,
+ 0x0000, 0x080c, 0x8da1, 0x005e, 0x6944, 0x6820, 0x9102, 0x06c0,
+ 0x6820, 0x9082, 0x0019, 0x16a0, 0x6828, 0x6944, 0x810c, 0x9102,
+ 0x0678, 0x6840, 0x9082, 0x000f, 0x1658, 0x080c, 0x1060, 0x2900,
+ 0x0904, 0x4fc4, 0x684e, 0x00e6, 0x2071, 0x1930, 0x00b6, 0x2059,
+ 0x0000, 0x080c, 0x8c5d, 0x00be, 0x00ee, 0x0568, 0x080c, 0x89ad,
+ 0x080c, 0x89f8, 0x11e0, 0x6857, 0x0000, 0x00c6, 0x2061, 0x0100,
+ 0x6104, 0x918d, 0x2000, 0x6106, 0x6b10, 0x2061, 0x1a6a, 0x630a,
+ 0x00ce, 0x080c, 0x2779, 0x2001, 0x0138, 0x2102, 0x0804, 0x35b8,
+ 0x080c, 0x2779, 0x2001, 0x0138, 0x2102, 0x0804, 0x35ed, 0x080c,
+ 0x89f1, 0x00e6, 0x2071, 0x1930, 0x080c, 0x8e21, 0x080c, 0x8e30,
+ 0x080c, 0x8c44, 0x00ee, 0x2001, 0x188a, 0x204c, 0x080c, 0x1079,
+ 0x2001, 0x188a, 0x2003, 0x0000, 0x080c, 0x2779, 0x2001, 0x0138,
+ 0x2102, 0x0804, 0x35ea, 0x2001, 0x1924, 0x200c, 0x918e, 0x0000,
+ 0x0904, 0x5025, 0x080c, 0x8c3f, 0x0904, 0x5025, 0x2001, 0x0101,
+ 0x200c, 0x918c, 0xdfff, 0x2102, 0x2001, 0x0138, 0x2003, 0x0000,
+ 0x00e6, 0x2071, 0x0300, 0x701c, 0xd0a4, 0x1de8, 0x00ee, 0x080c,
+ 0x8c44, 0x2001, 0x0035, 0x080c, 0x16a0, 0x00c6, 0x2061, 0x193c,
+ 0x6004, 0x6100, 0x9106, 0x1de0, 0x00ce, 0x080c, 0x2779, 0x2001,
+ 0x0138, 0x2102, 0x00e6, 0x00f6, 0x2071, 0x1923, 0x080c, 0x8b7e,
+ 0x0120, 0x2f00, 0x080c, 0x8c0a, 0x0cc8, 0x00fe, 0x00ee, 0x0126,
+ 0x2091, 0x8000, 0x2001, 0x188a, 0x200c, 0x81ff, 0x0138, 0x2148,
+ 0x080c, 0x1079, 0x2001, 0x188a, 0x2003, 0x0000, 0x2001, 0x183d,
+ 0x2003, 0x0020, 0x080c, 0x89f1, 0x00e6, 0x2071, 0x1930, 0x080c,
+ 0x8e21, 0x080c, 0x8e30, 0x00ee, 0x012e, 0x0804, 0x35b8, 0x0006,
+ 0x080c, 0x573e, 0xd0cc, 0x000e, 0x0005, 0x0006, 0x080c, 0x5742,
+ 0xd0bc, 0x000e, 0x0005, 0x6174, 0x7a84, 0x6300, 0x82ff, 0x1118,
+ 0x7986, 0x0804, 0x35b8, 0x83ff, 0x1904, 0x35ed, 0x2001, 0xfff0,
+ 0x9200, 0x1a04, 0x35ed, 0x2019, 0xffff, 0x6078, 0x9302, 0x9200,
+ 0x0a04, 0x35ed, 0x7986, 0x6276, 0x0804, 0x35b8, 0x080c, 0x5752,
+ 0x1904, 0x35ea, 0x7c88, 0x7d84, 0x7e98, 0x7f8c, 0x080c, 0x4af2,
+ 0x0904, 0x35ea, 0x900e, 0x901e, 0x7326, 0x7332, 0xa860, 0x20e8,
+ 0x7036, 0xa85c, 0x9080, 0x0003, 0x702a, 0x20a0, 0x91d8, 0x1000,
+ 0x2b5c, 0x8bff, 0x0178, 0x080c, 0x6add, 0x0118, 0x080c, 0x6ae5,
+ 0x1148, 0x20a9, 0x0001, 0xb814, 0x4004, 0xb810, 0x4004, 0x4104,
+ 0x9398, 0x0003, 0x8108, 0x9182, 0x0800, 0x0120, 0x9386, 0x003c,
+ 0x0170, 0x0c20, 0x83ff, 0x1148, 0x7224, 0x900e, 0x2001, 0x0003,
+ 0x080c, 0x91f8, 0x2208, 0x0804, 0x35b8, 0x7033, 0x0001, 0x7122,
+ 0x7024, 0x9300, 0x7026, 0x2061, 0x18b8, 0x2c44, 0xa06b, 0x0000,
+ 0xa37a, 0x7028, 0xa076, 0x7034, 0xa072, 0xa48e, 0xa592, 0xa696,
+ 0xa79a, 0x080c, 0x113c, 0x7007, 0x0002, 0x701f, 0x50a8, 0x0005,
+ 0x7030, 0x9005, 0x1178, 0x7120, 0x7028, 0x20a0, 0x901e, 0x7034,
+ 0x20e8, 0x2061, 0x18b8, 0x2c44, 0xa48c, 0xa590, 0xa694, 0xa798,
+ 0x0804, 0x5066, 0x7224, 0x900e, 0x2001, 0x0003, 0x080c, 0x91f8,
+ 0x2208, 0x0804, 0x35b8, 0x00f6, 0x00e6, 0x080c, 0x5752, 0x2009,
+ 0x0007, 0x1904, 0x513b, 0x2071, 0x189e, 0x745c, 0x84ff, 0x2009,
+ 0x000e, 0x1904, 0x513b, 0xac9c, 0xad98, 0xaea4, 0xafa0, 0x0096,
+ 0x080c, 0x1060, 0x2009, 0x0002, 0x0904, 0x513b, 0x2900, 0x705e,
+ 0x900e, 0x901e, 0x7356, 0x7362, 0xa860, 0x7066, 0xa85c, 0x9080,
+ 0x0003, 0x705a, 0x20a0, 0x91d8, 0x1000, 0x2b5c, 0x8bff, 0x0178,
+ 0x080c, 0x6add, 0x0118, 0x080c, 0x6ae5, 0x1148, 0xb814, 0x20a9,
+ 0x0001, 0x4004, 0xb810, 0x4004, 0x4104, 0x9398, 0x0003, 0x8108,
+ 0x9182, 0x0800, 0x0120, 0x9386, 0x003c, 0x01e8, 0x0c20, 0x83ff,
+ 0x11c0, 0x7254, 0x900e, 0x2001, 0x0003, 0x080c, 0x91f8, 0x2208,
+ 0x009e, 0xa897, 0x4000, 0xa99a, 0x715c, 0x81ff, 0x090c, 0x0d7d,
+ 0x2148, 0x080c, 0x1079, 0x9006, 0x705e, 0x918d, 0x0001, 0x2008,
+ 0x0418, 0x7063, 0x0001, 0x7152, 0x7054, 0x9300, 0x7056, 0x2061,
+ 0x18b9, 0x2c44, 0xa37a, 0x7058, 0xa076, 0x7064, 0xa072, 0xa48e,
+ 0xa592, 0xa696, 0xa79a, 0xa09f, 0x5147, 0x000e, 0xa0a2, 0x080c,
+ 0x113c, 0x9006, 0x0048, 0x009e, 0xa897, 0x4005, 0xa99a, 0x900e,
+ 0x9085, 0x0001, 0x2001, 0x0030, 0x00ee, 0x00fe, 0x0005, 0x00f6,
+ 0xa0a0, 0x904d, 0x090c, 0x0d7d, 0x00e6, 0x2071, 0x189e, 0xa06c,
+ 0x908e, 0x0100, 0x0138, 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897,
+ 0x4002, 0x00d8, 0x7060, 0x9005, 0x1158, 0x7150, 0x7058, 0x20a0,
+ 0x901e, 0x7064, 0x20e8, 0xa48c, 0xa590, 0xa694, 0xa798, 0x0428,
+ 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0x7254, 0x900e,
+ 0x2001, 0x0003, 0x080c, 0x91f8, 0xaa9a, 0x715c, 0x81ff, 0x090c,
+ 0x0d7d, 0x2148, 0x080c, 0x1079, 0x705f, 0x0000, 0xa0a0, 0x2048,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x6dee, 0x012e, 0xa09f, 0x0000,
+ 0xa0a3, 0x0000, 0x00ee, 0x00fe, 0x0005, 0x91d8, 0x1000, 0x2b5c,
+ 0x8bff, 0x0178, 0x080c, 0x6add, 0x0118, 0x080c, 0x6ae5, 0x1148,
+ 0xb814, 0x20a9, 0x0001, 0x4004, 0xb810, 0x4004, 0x4104, 0x9398,
+ 0x0003, 0x8108, 0x9182, 0x0800, 0x0120, 0x9386, 0x003c, 0x0518,
+ 0x0c20, 0x83ff, 0x11f0, 0x7154, 0x810c, 0xa99a, 0xa897, 0x4000,
+ 0x715c, 0x81ff, 0x090c, 0x0d7d, 0x2148, 0x080c, 0x1079, 0x9006,
+ 0x705e, 0x918d, 0x0001, 0x2008, 0xa0a0, 0x2048, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6dee, 0x012e, 0xa09f, 0x0000, 0xa0a3, 0x0000,
+ 0x0070, 0x7063, 0x0001, 0x7152, 0x7054, 0x9300, 0x7056, 0xa37a,
+ 0xa48e, 0xa592, 0xa696, 0xa79a, 0x080c, 0x113c, 0x9006, 0x00ee,
+ 0x0005, 0x0096, 0xa88c, 0x90be, 0x7000, 0x0148, 0x90be, 0x7100,
+ 0x0130, 0x90be, 0x7200, 0x0118, 0x009e, 0x0804, 0x35ed, 0xa884,
+ 0xa988, 0x080c, 0x2661, 0x1518, 0x080c, 0x6632, 0x1500, 0x7126,
+ 0xbe12, 0xbd16, 0xae7c, 0x080c, 0x4af2, 0x01c8, 0x080c, 0x4af2,
+ 0x01b0, 0x009e, 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0xa823,
+ 0x0000, 0xa804, 0x2048, 0x080c, 0xcb2c, 0x1120, 0x2009, 0x0003,
+ 0x0804, 0x35ea, 0x7007, 0x0003, 0x701f, 0x5214, 0x0005, 0x009e,
+ 0x2009, 0x0002, 0x0804, 0x35ea, 0x7124, 0x080c, 0x3349, 0xa820,
+ 0x9086, 0x8001, 0x1120, 0x2009, 0x0004, 0x0804, 0x35ea, 0x2900,
+ 0x7022, 0xa804, 0x0096, 0x2048, 0x8906, 0x8006, 0x8007, 0x90bc,
+ 0x003f, 0x9084, 0xffc0, 0x009e, 0x9080, 0x0002, 0x0076, 0x0006,
+ 0x2098, 0x20a0, 0x27e0, 0x27e8, 0x20a9, 0x002a, 0x080c, 0x0fc4,
+ 0xaa6c, 0xab70, 0xac74, 0xad78, 0x2061, 0x18b8, 0x2c44, 0xa06b,
+ 0x0000, 0xae64, 0xaf8c, 0x97c6, 0x7000, 0x0118, 0x97c6, 0x7100,
+ 0x1148, 0x96c2, 0x0004, 0x0600, 0x2009, 0x0004, 0x000e, 0x007e,
+ 0x0804, 0x4b3e, 0x97c6, 0x7200, 0x11b8, 0x96c2, 0x0054, 0x02a0,
+ 0x000e, 0x007e, 0x2061, 0x18b8, 0x2c44, 0xa076, 0xa772, 0xa07b,
+ 0x002a, 0xa28e, 0xa392, 0xa496, 0xa59a, 0x080c, 0x113c, 0x7007,
+ 0x0002, 0x701f, 0x5270, 0x0005, 0x000e, 0x007e, 0x0804, 0x35ed,
+ 0x7020, 0x2048, 0xa804, 0x2048, 0xa804, 0x2048, 0x8906, 0x8006,
+ 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002, 0x2098,
+ 0x20a0, 0x27e0, 0x27e8, 0x20a9, 0x002a, 0x080c, 0x0fc4, 0x2100,
+ 0x2238, 0x2061, 0x18b8, 0x2c44, 0xa28c, 0xa390, 0xa494, 0xa598,
+ 0x2009, 0x002a, 0x0804, 0x4b3e, 0x81ff, 0x1904, 0x35ea, 0x798c,
+ 0x2001, 0x197d, 0x918c, 0x8000, 0x2102, 0x080c, 0x4b09, 0x0904,
+ 0x35ed, 0x080c, 0x6add, 0x0120, 0x080c, 0x6ae5, 0x1904, 0x35ed,
+ 0x080c, 0x675a, 0x0904, 0x35ea, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x68f3, 0x012e, 0x0904, 0x35ea, 0x2001, 0x197d, 0x2004, 0xd0fc,
+ 0x1904, 0x35b8, 0x0804, 0x458e, 0xa9a0, 0x2001, 0x197d, 0x918c,
+ 0x8000, 0xc18d, 0x2102, 0x080c, 0x4b16, 0x01a0, 0x080c, 0x6add,
+ 0x0118, 0x080c, 0x6ae5, 0x1170, 0x080c, 0x675a, 0x2009, 0x0002,
+ 0x0128, 0x080c, 0x68f3, 0x1170, 0x2009, 0x0003, 0xa897, 0x4005,
+ 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001,
+ 0x0030, 0x0005, 0xa897, 0x4000, 0x2001, 0x197d, 0x2004, 0xd0fc,
+ 0x1128, 0x080c, 0x5746, 0x0110, 0x9006, 0x0018, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0000, 0x0005, 0x78a8, 0xd08c, 0x1118, 0xd084,
+ 0x0904, 0x4503, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x080c, 0x4af2,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x35ea, 0x080c, 0x6add, 0x0130,
+ 0x908e, 0x0004, 0x0118, 0x908e, 0x0005, 0x15a0, 0x78a8, 0xd08c,
+ 0x0120, 0xb800, 0xc08c, 0xb802, 0x0028, 0x080c, 0x573e, 0xd0b4,
+ 0x0904, 0x453d, 0x7884, 0x908e, 0x007e, 0x0904, 0x453d, 0x908e,
+ 0x007f, 0x0904, 0x453d, 0x908e, 0x0080, 0x0904, 0x453d, 0xb800,
+ 0xd08c, 0x1904, 0x453d, 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a,
+ 0x080c, 0xcb4b, 0x1120, 0x2009, 0x0003, 0x0804, 0x35ea, 0x7007,
+ 0x0003, 0x701f, 0x533c, 0x0005, 0x080c, 0x4b25, 0x0904, 0x35ed,
+ 0x0804, 0x453d, 0x080c, 0x33a8, 0x0108, 0x0005, 0x2009, 0x1834,
+ 0x210c, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x35ea, 0x080c,
+ 0x5752, 0x0120, 0x2009, 0x0007, 0x0804, 0x35ea, 0x080c, 0x6ad5,
+ 0x0120, 0x2009, 0x0008, 0x0804, 0x35ea, 0xb89c, 0xd0a4, 0x1118,
+ 0xd0ac, 0x1904, 0x453d, 0x9006, 0xa866, 0xa832, 0xa868, 0xc0fd,
+ 0xa86a, 0x080c, 0xcbb3, 0x1120, 0x2009, 0x0003, 0x0804, 0x35ea,
+ 0x7007, 0x0003, 0x701f, 0x5375, 0x0005, 0xa830, 0x9086, 0x0100,
+ 0x1120, 0x2009, 0x0004, 0x0804, 0x568c, 0x080c, 0x4b25, 0x0904,
+ 0x35ed, 0x0804, 0x530e, 0x81ff, 0x2009, 0x0001, 0x1904, 0x35ea,
+ 0x080c, 0x5752, 0x2009, 0x0007, 0x1904, 0x35ea, 0x080c, 0x6ad5,
+ 0x0120, 0x2009, 0x0008, 0x0804, 0x35ea, 0x080c, 0x4b25, 0x0904,
+ 0x35ed, 0x080c, 0x6add, 0x2009, 0x0009, 0x1904, 0x35ea, 0x080c,
+ 0x4af2, 0x2009, 0x0002, 0x0904, 0x35ea, 0x9006, 0xa866, 0xa832,
+ 0xa868, 0xc0fd, 0xa86a, 0x7988, 0x9194, 0xff00, 0x918c, 0x00ff,
+ 0x9006, 0x82ff, 0x1128, 0xc0ed, 0xa952, 0x798c, 0xa956, 0x0038,
+ 0x928e, 0x0100, 0x1904, 0x35ed, 0xc0e5, 0xa952, 0xa956, 0xa83e,
+ 0x080c, 0xce16, 0x2009, 0x0003, 0x0904, 0x35ea, 0x7007, 0x0003,
+ 0x701f, 0x53cb, 0x0005, 0xa830, 0x9086, 0x0100, 0x2009, 0x0004,
+ 0x0904, 0x35ea, 0x0804, 0x35b8, 0x7aa8, 0x9284, 0xc000, 0x0148,
+ 0xd2ec, 0x01a0, 0x080c, 0x5752, 0x1188, 0x2009, 0x0014, 0x0804,
+ 0x35ea, 0xd2dc, 0x1578, 0x81ff, 0x2009, 0x0001, 0x1904, 0x35ea,
+ 0x080c, 0x5752, 0x2009, 0x0007, 0x1904, 0x35ea, 0xd2f4, 0x0138,
+ 0x9284, 0x5000, 0xc0d5, 0x080c, 0x5718, 0x0804, 0x35b8, 0xd2fc,
+ 0x0160, 0x080c, 0x4b25, 0x0904, 0x35ed, 0x7984, 0x9284, 0x9000,
+ 0xc0d5, 0x080c, 0x56e7, 0x0804, 0x35b8, 0x080c, 0x4b25, 0x0904,
+ 0x35ed, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x2009, 0x0009,
+ 0x1904, 0x54ba, 0x080c, 0x4af2, 0x2009, 0x0002, 0x0904, 0x54ba,
+ 0xa85c, 0x9080, 0x001b, 0xaf60, 0x2009, 0x0008, 0x7a8c, 0x7b88,
+ 0x7c9c, 0x7d98, 0x080c, 0x4b3b, 0x701f, 0x5427, 0x0005, 0xa86c,
+ 0x9086, 0x0500, 0x1138, 0xa870, 0x9005, 0x1120, 0xa874, 0x9084,
+ 0xff00, 0x0110, 0x1904, 0x35ed, 0xa866, 0xa832, 0xa868, 0xc0fd,
+ 0xa86a, 0x080c, 0x4b25, 0x1110, 0x0804, 0x35ed, 0x2009, 0x0043,
+ 0x080c, 0xce7e, 0x2009, 0x0003, 0x0904, 0x54ba, 0x7007, 0x0003,
+ 0x701f, 0x544b, 0x0005, 0xa830, 0x9086, 0x0100, 0x2009, 0x0004,
+ 0x0904, 0x54ba, 0x7984, 0x7aa8, 0x9284, 0x1000, 0xe085, 0x080c,
+ 0x56e7, 0x0804, 0x35b8, 0x00c6, 0xaab0, 0x9284, 0xc000, 0x0148,
+ 0xd2ec, 0x0170, 0x080c, 0x5752, 0x1158, 0x2009, 0x0014, 0x0804,
+ 0x54a9, 0x2061, 0x1800, 0x080c, 0x5752, 0x2009, 0x0007, 0x15c8,
+ 0xd2f4, 0x0130, 0x9284, 0x5000, 0xc0d5, 0x080c, 0x5718, 0x0058,
+ 0xd2fc, 0x0180, 0x080c, 0x4b23, 0x0590, 0xa998, 0x9284, 0x9000,
+ 0xc0d5, 0x080c, 0x56e7, 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897,
+ 0x4000, 0x0438, 0x080c, 0x4b23, 0x0510, 0x080c, 0x6add, 0x2009,
+ 0x0009, 0x11b8, 0xa8c4, 0x9086, 0x0500, 0x11c8, 0xa8c8, 0x9005,
+ 0x11b0, 0xa8cc, 0x9084, 0xff00, 0x1190, 0x080c, 0x4b23, 0x1108,
+ 0x0070, 0x2009, 0x004b, 0x080c, 0xce7e, 0x2009, 0x0003, 0x0108,
+ 0x0078, 0x0431, 0x19c0, 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897,
+ 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x00ce, 0x0005,
+ 0x9006, 0x0ce0, 0x7aa8, 0xd2dc, 0x0904, 0x35ea, 0x0016, 0x7984,
+ 0x9284, 0x1000, 0xc0fd, 0x080c, 0x56e7, 0x001e, 0x1904, 0x35ea,
+ 0x0804, 0x35b8, 0x00f6, 0x2d78, 0xaab0, 0x0021, 0x00fe, 0x0005,
+ 0xaab0, 0xc2d5, 0xd2dc, 0x0150, 0x0016, 0xa998, 0x9284, 0x1400,
+ 0xc0fd, 0x080c, 0x56e7, 0x001e, 0x9085, 0x0001, 0x0005, 0x81ff,
+ 0x0120, 0x2009, 0x0001, 0x0804, 0x35ea, 0x080c, 0x5752, 0x0120,
+ 0x2009, 0x0007, 0x0804, 0x35ea, 0x7984, 0x7ea8, 0x96b4, 0x00ff,
+ 0x080c, 0x6693, 0x1904, 0x35ed, 0x9186, 0x007f, 0x0138, 0x080c,
+ 0x6add, 0x0120, 0x2009, 0x0009, 0x0804, 0x35ea, 0x080c, 0x4af2,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x35ea, 0xa867, 0x0000, 0xa868,
+ 0xc0fd, 0xa86a, 0x2001, 0x0100, 0x8007, 0xa80a, 0x080c, 0xcb65,
+ 0x1120, 0x2009, 0x0003, 0x0804, 0x35ea, 0x7007, 0x0003, 0x701f,
+ 0x551a, 0x0005, 0xa808, 0x8007, 0x9086, 0x0100, 0x1120, 0x2009,
+ 0x0004, 0x0804, 0x35ea, 0xa8e0, 0xa866, 0xa810, 0x8007, 0x9084,
+ 0x00ff, 0x800c, 0xa814, 0x8007, 0x9084, 0x00ff, 0x8004, 0x9080,
+ 0x0002, 0x9108, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x0004, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804,
+ 0x4b3e, 0x080c, 0x4af2, 0x1120, 0x2009, 0x0002, 0x0804, 0x35ea,
+ 0x7984, 0x9194, 0xff00, 0x918c, 0x00ff, 0x8217, 0x82ff, 0x1118,
+ 0x7023, 0x19b2, 0x0040, 0x92c6, 0x0001, 0x1118, 0x7023, 0x19cc,
+ 0x0010, 0x0804, 0x35ed, 0x2009, 0x001a, 0x7a8c, 0x7b88, 0x7c9c,
+ 0x7d98, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x080c, 0x4b3b, 0x701f,
+ 0x556a, 0x0005, 0x2001, 0x182e, 0x2003, 0x0001, 0xa85c, 0x9080,
+ 0x0019, 0x2098, 0xa860, 0x20e0, 0x20a9, 0x001a, 0x7020, 0x20a0,
+ 0x20e9, 0x0001, 0x4003, 0x0804, 0x35b8, 0x080c, 0x4af2, 0x1120,
+ 0x2009, 0x0002, 0x0804, 0x35ea, 0x7984, 0x9194, 0xff00, 0x918c,
+ 0x00ff, 0x8217, 0x82ff, 0x1118, 0x2099, 0x19b2, 0x0040, 0x92c6,
+ 0x0001, 0x1118, 0x2099, 0x19cc, 0x0010, 0x0804, 0x35ed, 0xa85c,
+ 0x9080, 0x0019, 0x20a0, 0xa860, 0x20e8, 0x20a9, 0x001a, 0x20e1,
+ 0x0001, 0x4003, 0x2009, 0x001a, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98,
+ 0xa85c, 0x9080, 0x0019, 0xaf60, 0x0804, 0x4b3e, 0x7884, 0x908a,
+ 0x1000, 0x1a04, 0x35ed, 0x0126, 0x2091, 0x8000, 0x8003, 0x800b,
+ 0x810b, 0x9108, 0x00c6, 0x2061, 0x1a02, 0x6142, 0x00ce, 0x012e,
+ 0x0804, 0x35b8, 0x00c6, 0x080c, 0x753d, 0x1160, 0x080c, 0x7840,
+ 0x080c, 0x6092, 0x9085, 0x0001, 0x080c, 0x7584, 0x080c, 0x746e,
+ 0x080c, 0x0d7d, 0x2061, 0x1800, 0x6030, 0xc09d, 0x6032, 0x080c,
+ 0x5f4d, 0x00ce, 0x0005, 0x00c6, 0x2001, 0x1800, 0x2004, 0x908e,
+ 0x0000, 0x0904, 0x35ea, 0x7884, 0x9005, 0x0188, 0x7888, 0x2061,
+ 0x199b, 0x2c0c, 0x2062, 0x080c, 0x2a48, 0x01a0, 0x080c, 0x2a50,
+ 0x0188, 0x080c, 0x2a58, 0x0170, 0x2162, 0x0804, 0x35ed, 0x2061,
+ 0x0100, 0x6038, 0x9086, 0x0007, 0x1118, 0x2009, 0x0001, 0x0010,
+ 0x2009, 0x0000, 0x7884, 0x9086, 0x0002, 0x15a8, 0x2061, 0x0100,
+ 0x6028, 0xc09c, 0x602a, 0x080c, 0xa91e, 0x0026, 0x2011, 0x0003,
+ 0x080c, 0xa243, 0x2011, 0x0002, 0x080c, 0xa24d, 0x002e, 0x080c,
+ 0xa138, 0x0036, 0x901e, 0x080c, 0xa1b8, 0x003e, 0x080c, 0xa93a,
+ 0x60e3, 0x0000, 0x080c, 0xe882, 0x080c, 0xe89d, 0x9085, 0x0001,
+ 0x080c, 0x7584, 0x9006, 0x080c, 0x2a7a, 0x2001, 0x1800, 0x2003,
+ 0x0004, 0x2001, 0x19a6, 0x2003, 0x0000, 0x0026, 0x2011, 0x0008,
+ 0x080c, 0x2ab4, 0x002e, 0x00ce, 0x0804, 0x35b8, 0x81ff, 0x0120,
+ 0x2009, 0x0001, 0x0804, 0x35ea, 0x080c, 0x5752, 0x0120, 0x2009,
+ 0x0007, 0x0804, 0x35ea, 0x7984, 0x7ea8, 0x96b4, 0x00ff, 0x080c,
+ 0x6693, 0x1904, 0x35ed, 0x9186, 0x007f, 0x0138, 0x080c, 0x6add,
+ 0x0120, 0x2009, 0x0009, 0x0804, 0x35ea, 0x080c, 0x4af2, 0x1120,
+ 0x2009, 0x0002, 0x0804, 0x35ea, 0xa867, 0x0000, 0xa868, 0xc0fd,
+ 0xa86a, 0x080c, 0xcb68, 0x1120, 0x2009, 0x0003, 0x0804, 0x35ea,
+ 0x7007, 0x0003, 0x701f, 0x5675, 0x0005, 0xa830, 0x9086, 0x0100,
+ 0x1120, 0x2009, 0x0004, 0x0804, 0x35ea, 0xa8e0, 0xa866, 0xa834,
+ 0x8007, 0x800c, 0xa85c, 0x9080, 0x000c, 0x7a8c, 0x7b88, 0x7c9c,
+ 0x7d98, 0xaf60, 0x0804, 0x4b3e, 0xa898, 0x9086, 0x000d, 0x1904,
+ 0x35ea, 0x2021, 0x4005, 0x0126, 0x2091, 0x8000, 0x0e04, 0x5699,
+ 0x0010, 0x012e, 0x0cc0, 0x7c36, 0x9486, 0x4000, 0x0118, 0x7833,
+ 0x0011, 0x0010, 0x7833, 0x0010, 0x7883, 0x4005, 0xa998, 0x7986,
+ 0xa9a4, 0x799a, 0xa9a8, 0x799e, 0x080c, 0x4b2e, 0x2091, 0x4080,
+ 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x7007, 0x0001,
+ 0x2091, 0x5000, 0x700f, 0x0000, 0x012e, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x00c6, 0x2061, 0x1a02, 0x7984, 0x6152, 0x614e, 0x6057,
+ 0x0000, 0x604b, 0x0009, 0x7898, 0x606a, 0x789c, 0x6066, 0x7888,
+ 0x6062, 0x788c, 0x605e, 0x2001, 0x1a10, 0x2044, 0x2001, 0x1a17,
+ 0xa076, 0xa060, 0xa072, 0xa07b, 0x0001, 0xa07f, 0x0002, 0xa06b,
+ 0x0000, 0xa09f, 0x0000, 0x00ce, 0x012e, 0x0804, 0x35b8, 0x0126,
+ 0x2091, 0x8000, 0x00b6, 0x00c6, 0x90e4, 0xc000, 0x0198, 0x0006,
+ 0xd0d4, 0x0160, 0x0036, 0x2019, 0x0029, 0x080c, 0xa91e, 0x0106,
+ 0x080c, 0x336d, 0x010e, 0x090c, 0xa93a, 0x003e, 0x080c, 0xc9c7,
+ 0x000e, 0x1198, 0xd0e4, 0x0160, 0x9180, 0x1000, 0x2004, 0x905d,
+ 0x0160, 0x080c, 0x60ac, 0x080c, 0xabe2, 0x0110, 0xb817, 0x0000,
+ 0x9006, 0x00ce, 0x00be, 0x012e, 0x0005, 0x9085, 0x0001, 0x0cc8,
+ 0x0126, 0x2091, 0x8000, 0x0156, 0x2010, 0x900e, 0x20a9, 0x0800,
+ 0x0016, 0x9180, 0x1000, 0x2004, 0x9005, 0x0188, 0x9186, 0x007e,
+ 0x0170, 0x9186, 0x007f, 0x0158, 0x9186, 0x0080, 0x0140, 0x9186,
+ 0x00ff, 0x0128, 0x0026, 0x2200, 0x080c, 0x56e7, 0x002e, 0x001e,
+ 0x8108, 0x1f04, 0x5720, 0x015e, 0x012e, 0x0005, 0x2001, 0x1848,
+ 0x2004, 0x0005, 0x2001, 0x1867, 0x2004, 0x0005, 0x0006, 0x2001,
+ 0x1810, 0x2004, 0xd0d4, 0x000e, 0x0005, 0x2001, 0x180e, 0x2004,
+ 0xd0b4, 0x0005, 0x2001, 0x1800, 0x2004, 0x9086, 0x0003, 0x0005,
+ 0x0016, 0x00e6, 0x2071, 0x189e, 0x7108, 0x910d, 0x710a, 0x00ee,
+ 0x001e, 0x0005, 0x79a4, 0x9182, 0x0081, 0x1a04, 0x35ed, 0x810c,
+ 0x0016, 0x080c, 0x4af2, 0x0170, 0x080c, 0x0f4f, 0x2100, 0x2238,
+ 0x7d84, 0x7c88, 0x7b8c, 0x7a90, 0x001e, 0x080c, 0x4b3b, 0x701f,
+ 0x577e, 0x0005, 0x2009, 0x0002, 0x0804, 0x35ea, 0x2079, 0x0000,
+ 0x7d94, 0x7c98, 0x7ba8, 0x7aac, 0x79a4, 0x810c, 0x2061, 0x18b8,
+ 0x2c44, 0xa770, 0xa074, 0x2071, 0x189e, 0x080c, 0x4b3e, 0x701f,
+ 0x5792, 0x0005, 0x2061, 0x18b8, 0x2c44, 0x0016, 0x0026, 0xa270,
+ 0xa174, 0x080c, 0x0f57, 0x002e, 0x001e, 0x080c, 0x1004, 0x9006,
+ 0xa802, 0xa806, 0x0804, 0x35b8, 0x0126, 0x0156, 0x0136, 0x0146,
+ 0x01c6, 0x01d6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2061, 0x0100,
+ 0x2069, 0x0200, 0x2071, 0x1800, 0x6044, 0xd0a4, 0x11e8, 0xd084,
+ 0x0118, 0x080c, 0x594d, 0x0068, 0xd08c, 0x0118, 0x080c, 0x5856,
+ 0x0040, 0xd094, 0x0118, 0x080c, 0x5826, 0x0018, 0xd09c, 0x0108,
+ 0x0099, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x01de, 0x01ce, 0x014e,
+ 0x013e, 0x015e, 0x012e, 0x0005, 0x0016, 0x6128, 0xd19c, 0x1110,
+ 0xc19d, 0x612a, 0x001e, 0x0c68, 0x0006, 0x7098, 0x9005, 0x000e,
+ 0x0120, 0x709b, 0x0000, 0x7093, 0x0000, 0x624c, 0x9286, 0xf0f0,
+ 0x1150, 0x6048, 0x9086, 0xf0f0, 0x0130, 0x624a, 0x6043, 0x0090,
+ 0x6043, 0x0010, 0x0490, 0x9294, 0xff00, 0x9296, 0xf700, 0x0178,
+ 0x7138, 0xd1a4, 0x1160, 0x6240, 0x9295, 0x0100, 0x6242, 0x9294,
+ 0x0010, 0x0128, 0x2009, 0x00f7, 0x080c, 0x600e, 0x00f0, 0x6040,
+ 0x9084, 0x0010, 0x9085, 0x0140, 0x6042, 0x6043, 0x0000, 0x7087,
+ 0x0000, 0x70a3, 0x0001, 0x70c7, 0x0000, 0x70df, 0x0000, 0x2009,
+ 0x1d80, 0x200b, 0x0000, 0x7097, 0x0000, 0x708b, 0x000f, 0x2009,
+ 0x000f, 0x2011, 0x5ef0, 0x080c, 0x8792, 0x0005, 0x2001, 0x1869,
+ 0x2004, 0xd08c, 0x0110, 0x705f, 0xffff, 0x7088, 0x9005, 0x1528,
+ 0x2011, 0x5ef0, 0x080c, 0x86c8, 0x6040, 0x9094, 0x0010, 0x9285,
+ 0x0020, 0x6042, 0x20a9, 0x00c8, 0x6044, 0xd08c, 0x1168, 0x1f04,
+ 0x583c, 0x6242, 0x709b, 0x0000, 0x6040, 0x9094, 0x0010, 0x9285,
+ 0x0080, 0x6042, 0x6242, 0x0048, 0x6242, 0x709b, 0x0000, 0x708f,
+ 0x0000, 0x9006, 0x080c, 0x6097, 0x0000, 0x0005, 0x708c, 0x908a,
+ 0x0003, 0x1a0c, 0x0d7d, 0x000b, 0x0005, 0x5860, 0x58b1, 0x594c,
+ 0x00f6, 0x0016, 0x6900, 0x918c, 0x0800, 0x708f, 0x0001, 0x2001,
+ 0x015d, 0x2003, 0x0000, 0x6803, 0x00fc, 0x20a9, 0x0004, 0x6800,
+ 0x9084, 0x00fc, 0x0120, 0x1f04, 0x586f, 0x080c, 0x0d7d, 0x68a0,
+ 0x68a2, 0x689c, 0x689e, 0x6898, 0x689a, 0xa001, 0x918d, 0x1600,
+ 0x6902, 0x001e, 0x6837, 0x0020, 0x080c, 0x6073, 0x2079, 0x1d00,
+ 0x7833, 0x1101, 0x7837, 0x0000, 0x20e1, 0x0001, 0x2099, 0x1805,
+ 0x20e9, 0x0001, 0x20a1, 0x1d0e, 0x20a9, 0x0004, 0x4003, 0x080c,
+ 0xa713, 0x20e1, 0x0001, 0x2099, 0x1d00, 0x20e9, 0x0000, 0x20a1,
+ 0x0240, 0x20a9, 0x0014, 0x4003, 0x60c3, 0x000c, 0x600f, 0x0000,
+ 0x080c, 0x5f21, 0x00fe, 0x9006, 0x7092, 0x6043, 0x0008, 0x6042,
+ 0x0005, 0x00f6, 0x7090, 0x7093, 0x0000, 0x9025, 0x0904, 0x5929,
+ 0x6020, 0xd0b4, 0x1904, 0x5927, 0x71a0, 0x81ff, 0x0904, 0x5915,
+ 0x9486, 0x000c, 0x1904, 0x5922, 0x9480, 0x0018, 0x8004, 0x20a8,
+ 0x080c, 0x606c, 0x2011, 0x0260, 0x2019, 0x1d00, 0x220c, 0x2304,
+ 0x9106, 0x11e8, 0x8210, 0x8318, 0x1f04, 0x58ce, 0x6043, 0x0004,
+ 0x2061, 0x0140, 0x605b, 0xbc94, 0x605f, 0xf0f0, 0x2061, 0x0100,
+ 0x6043, 0x0006, 0x708f, 0x0002, 0x709b, 0x0002, 0x2009, 0x07d0,
+ 0x2011, 0x5ef7, 0x080c, 0x8792, 0x080c, 0x6073, 0x04c0, 0x080c,
+ 0x606c, 0x2079, 0x0260, 0x7930, 0x918e, 0x1101, 0x1558, 0x7834,
+ 0x9005, 0x1540, 0x7900, 0x918c, 0x00ff, 0x1118, 0x7804, 0x9005,
+ 0x0190, 0x080c, 0x606c, 0x2011, 0x026e, 0x2019, 0x1805, 0x20a9,
+ 0x0004, 0x220c, 0x2304, 0x9102, 0x0230, 0x11a0, 0x8210, 0x8318,
+ 0x1f04, 0x5909, 0x0078, 0x70a3, 0x0000, 0x080c, 0x606c, 0x20e1,
+ 0x0000, 0x2099, 0x0260, 0x20e9, 0x0001, 0x20a1, 0x1d00, 0x20a9,
+ 0x0014, 0x4003, 0x6043, 0x0008, 0x6043, 0x0000, 0x0010, 0x00fe,
+ 0x0005, 0x6040, 0x9085, 0x0100, 0x6042, 0x6020, 0xd0b4, 0x1db8,
+ 0x080c, 0xa713, 0x20e1, 0x0001, 0x2099, 0x1d00, 0x20e9, 0x0000,
+ 0x20a1, 0x0240, 0x20a9, 0x0014, 0x4003, 0x60c3, 0x000c, 0x2011,
+ 0x19f3, 0x2013, 0x0000, 0x7093, 0x0000, 0x60a3, 0x0056, 0x60a7,
+ 0x9575, 0x080c, 0x9ec7, 0x08d8, 0x0005, 0x7098, 0x908a, 0x001d,
+ 0x1a0c, 0x0d7d, 0x000b, 0x0005, 0x597e, 0x5991, 0x59ba, 0x59da,
+ 0x5a00, 0x5a2f, 0x5a55, 0x5a8d, 0x5ab3, 0x5ae1, 0x5b1c, 0x5b54,
+ 0x5b72, 0x5b9d, 0x5bbf, 0x5bda, 0x5be4, 0x5c18, 0x5c3e, 0x5c6d,
+ 0x5c93, 0x5ccb, 0x5d0f, 0x5d4c, 0x5d6d, 0x5dc6, 0x5de8, 0x5e16,
+ 0x5e16, 0x00c6, 0x2061, 0x1800, 0x6003, 0x0007, 0x2061, 0x0100,
+ 0x6004, 0x9084, 0xfff9, 0x6006, 0x00ce, 0x0005, 0x2061, 0x0140,
+ 0x605b, 0xbc94, 0x605f, 0xf0f0, 0x2061, 0x0100, 0x6043, 0x0002,
+ 0x709b, 0x0001, 0x2009, 0x07d0, 0x2011, 0x5ef7, 0x080c, 0x8792,
+ 0x0005, 0x00f6, 0x7090, 0x9086, 0x0014, 0x1510, 0x6042, 0x6020,
+ 0xd0b4, 0x11f0, 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296,
+ 0x1102, 0x11a0, 0x7834, 0x9005, 0x1188, 0x7a38, 0xd2fc, 0x0128,
+ 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001, 0x2011, 0x5ef7, 0x080c,
+ 0x86c8, 0x709b, 0x0010, 0x080c, 0x5be4, 0x0010, 0x7093, 0x0000,
+ 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0003, 0x6043, 0x0004, 0x2011,
+ 0x5ef7, 0x080c, 0x86c8, 0x080c, 0x5ff0, 0x2079, 0x0240, 0x7833,
+ 0x1102, 0x7837, 0x0000, 0x20a9, 0x0008, 0x9f88, 0x000e, 0x200b,
+ 0x0000, 0x8108, 0x1f04, 0x59cf, 0x60c3, 0x0014, 0x080c, 0x5f21,
+ 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x0500, 0x2011, 0x5ef7,
+ 0x080c, 0x86c8, 0x9086, 0x0014, 0x11b8, 0x080c, 0x606c, 0x2079,
+ 0x0260, 0x7a30, 0x9296, 0x1102, 0x1178, 0x7834, 0x9005, 0x1160,
+ 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001,
+ 0x709b, 0x0004, 0x0029, 0x0010, 0x080c, 0x6048, 0x00fe, 0x0005,
+ 0x00f6, 0x709b, 0x0005, 0x080c, 0x5ff0, 0x2079, 0x0240, 0x7833,
+ 0x1103, 0x7837, 0x0000, 0x080c, 0x606c, 0x080c, 0x604f, 0x1170,
+ 0x7084, 0x9005, 0x1158, 0x715c, 0x9186, 0xffff, 0x0138, 0x2011,
+ 0x0008, 0x080c, 0x5ea4, 0x0168, 0x080c, 0x6025, 0x20a9, 0x0008,
+ 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e,
+ 0x4003, 0x60c3, 0x0014, 0x080c, 0x5f21, 0x00fe, 0x0005, 0x00f6,
+ 0x7090, 0x9005, 0x0500, 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086,
+ 0x0014, 0x11b8, 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296,
+ 0x1103, 0x1178, 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128,
+ 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001, 0x709b, 0x0006, 0x0029,
+ 0x0010, 0x080c, 0x6048, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0007,
+ 0x080c, 0x5ff0, 0x2079, 0x0240, 0x7833, 0x1104, 0x7837, 0x0000,
+ 0x080c, 0x606c, 0x080c, 0x604f, 0x11b8, 0x7084, 0x9005, 0x11a0,
+ 0x7164, 0x9186, 0xffff, 0x0180, 0x9180, 0x33b9, 0x200d, 0x918c,
+ 0xff00, 0x810f, 0x2011, 0x0008, 0x080c, 0x5ea4, 0x0180, 0x080c,
+ 0x502d, 0x0110, 0x080c, 0x26ca, 0x20a9, 0x0008, 0x20e1, 0x0000,
+ 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3,
+ 0x0014, 0x080c, 0x5f21, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005,
+ 0x0500, 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0014, 0x11b8,
+ 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1104, 0x1178,
+ 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005,
+ 0x1110, 0x70c7, 0x0001, 0x709b, 0x0008, 0x0029, 0x0010, 0x080c,
+ 0x6048, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0009, 0x080c, 0x5ff0,
+ 0x2079, 0x0240, 0x7833, 0x1105, 0x7837, 0x0100, 0x080c, 0x604f,
+ 0x1150, 0x7084, 0x9005, 0x1138, 0x080c, 0x5e17, 0x1188, 0x9085,
+ 0x0001, 0x080c, 0x26ca, 0x20a9, 0x0008, 0x080c, 0x606c, 0x20e1,
+ 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003,
+ 0x60c3, 0x0014, 0x080c, 0x5f21, 0x0010, 0x080c, 0x5971, 0x00fe,
+ 0x0005, 0x00f6, 0x7090, 0x9005, 0x05a8, 0x2011, 0x5ef7, 0x080c,
+ 0x86c8, 0x9086, 0x0014, 0x1560, 0x080c, 0x606c, 0x2079, 0x0260,
+ 0x7a30, 0x9296, 0x1105, 0x1520, 0x7834, 0x9084, 0x0100, 0x2011,
+ 0x0100, 0x921e, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005,
+ 0x1110, 0x70c7, 0x0001, 0x709b, 0x000a, 0x00b1, 0x0098, 0x9005,
+ 0x1178, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7,
+ 0x0001, 0x7097, 0x0000, 0x709b, 0x000e, 0x080c, 0x5bbf, 0x0010,
+ 0x080c, 0x6048, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x000b, 0x2011,
+ 0x1d0e, 0x20e9, 0x0001, 0x22a0, 0x20a9, 0x0040, 0x2019, 0xffff,
+ 0x4304, 0x080c, 0x5ff0, 0x2079, 0x0240, 0x7833, 0x1106, 0x7837,
+ 0x0000, 0x080c, 0x604f, 0x0118, 0x2013, 0x0000, 0x0020, 0x7060,
+ 0x9085, 0x0100, 0x2012, 0x20a9, 0x0040, 0x2009, 0x024e, 0x2011,
+ 0x1d0e, 0x220e, 0x8210, 0x8108, 0x9186, 0x0260, 0x1128, 0x6810,
+ 0x8000, 0x6812, 0x2009, 0x0240, 0x1f04, 0x5b41, 0x60c3, 0x0084,
+ 0x080c, 0x5f21, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x01c0,
+ 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0084, 0x1178, 0x080c,
+ 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1106, 0x1138, 0x7834,
+ 0x9005, 0x1120, 0x709b, 0x000c, 0x0029, 0x0010, 0x080c, 0x6048,
+ 0x00fe, 0x0005, 0x00f6, 0x709b, 0x000d, 0x080c, 0x5ff0, 0x2079,
+ 0x0240, 0x7833, 0x1107, 0x7837, 0x0000, 0x080c, 0x606c, 0x20a9,
+ 0x0040, 0x2011, 0x026e, 0x2009, 0x024e, 0x220e, 0x8210, 0x8108,
+ 0x9186, 0x0260, 0x1150, 0x6810, 0x8000, 0x6812, 0x2009, 0x0240,
+ 0x6814, 0x8000, 0x6816, 0x2011, 0x0260, 0x1f04, 0x5b85, 0x60c3,
+ 0x0084, 0x080c, 0x5f21, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005,
+ 0x01e0, 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0084, 0x1198,
+ 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1107, 0x1158,
+ 0x7834, 0x9005, 0x1140, 0x7097, 0x0001, 0x080c, 0x5fc2, 0x709b,
+ 0x000e, 0x0029, 0x0010, 0x080c, 0x6048, 0x00fe, 0x0005, 0x918d,
+ 0x0001, 0x080c, 0x6097, 0x709b, 0x000f, 0x7093, 0x0000, 0x2061,
+ 0x0140, 0x605b, 0xbc85, 0x605f, 0xb5b5, 0x2061, 0x0100, 0x6043,
+ 0x0005, 0x6043, 0x0004, 0x2009, 0x07d0, 0x2011, 0x5ef7, 0x080c,
+ 0x86bc, 0x0005, 0x7090, 0x9005, 0x0130, 0x2011, 0x5ef7, 0x080c,
+ 0x86c8, 0x709b, 0x0000, 0x0005, 0x709b, 0x0011, 0x080c, 0xa713,
+ 0x080c, 0x606c, 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9, 0x0000,
+ 0x20a1, 0x0240, 0x7490, 0x9480, 0x0018, 0x9080, 0x0007, 0x9084,
+ 0x03f8, 0x8004, 0x20a8, 0x4003, 0x080c, 0x604f, 0x11a0, 0x717c,
+ 0x81ff, 0x0188, 0x900e, 0x7080, 0x9084, 0x00ff, 0x0160, 0x080c,
+ 0x2661, 0x9186, 0x007e, 0x0138, 0x9186, 0x0080, 0x0120, 0x2011,
+ 0x0008, 0x080c, 0x5ea4, 0x60c3, 0x0014, 0x080c, 0x5f21, 0x0005,
+ 0x00f6, 0x7090, 0x9005, 0x0500, 0x2011, 0x5ef7, 0x080c, 0x86c8,
+ 0x9086, 0x0014, 0x11b8, 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30,
+ 0x9296, 0x1103, 0x1178, 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc,
+ 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7, 0x0001, 0x709b, 0x0012,
+ 0x0029, 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005, 0x00f6, 0x709b,
+ 0x0013, 0x080c, 0x5ffe, 0x2079, 0x0240, 0x7833, 0x1103, 0x7837,
+ 0x0000, 0x080c, 0x606c, 0x080c, 0x604f, 0x1170, 0x7084, 0x9005,
+ 0x1158, 0x715c, 0x9186, 0xffff, 0x0138, 0x2011, 0x0008, 0x080c,
+ 0x5ea4, 0x0168, 0x080c, 0x6025, 0x20a9, 0x0008, 0x20e1, 0x0000,
+ 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3,
+ 0x0014, 0x080c, 0x5f21, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005,
+ 0x0500, 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0014, 0x11b8,
+ 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1104, 0x1178,
+ 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005,
+ 0x1110, 0x70c7, 0x0001, 0x709b, 0x0014, 0x0029, 0x0010, 0x7093,
+ 0x0000, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0015, 0x080c, 0x5ffe,
+ 0x2079, 0x0240, 0x7833, 0x1104, 0x7837, 0x0000, 0x080c, 0x606c,
+ 0x080c, 0x604f, 0x11b8, 0x7084, 0x9005, 0x11a0, 0x7164, 0x9186,
+ 0xffff, 0x0180, 0x9180, 0x33b9, 0x200d, 0x918c, 0xff00, 0x810f,
+ 0x2011, 0x0008, 0x080c, 0x5ea4, 0x0180, 0x080c, 0x502d, 0x0110,
+ 0x080c, 0x26ca, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099, 0x026e,
+ 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c,
+ 0x5f21, 0x00fe, 0x0005, 0x00f6, 0x7090, 0x9005, 0x05f0, 0x2011,
+ 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0014, 0x15a8, 0x080c, 0x606c,
+ 0x2079, 0x0260, 0x7a30, 0x9296, 0x1105, 0x1568, 0x7834, 0x9084,
+ 0x0100, 0x2011, 0x0100, 0x921e, 0x1168, 0x9085, 0x0001, 0x080c,
+ 0x6097, 0x7a38, 0xd2fc, 0x0128, 0x70c4, 0x9005, 0x1110, 0x70c7,
+ 0x0001, 0x0080, 0x9005, 0x11b8, 0x7a38, 0xd2fc, 0x0128, 0x70c4,
+ 0x9005, 0x1110, 0x70c7, 0x0001, 0x9085, 0x0001, 0x080c, 0x6097,
+ 0x7097, 0x0000, 0x7a38, 0xd2f4, 0x0110, 0x70df, 0x0008, 0x709b,
+ 0x0016, 0x0029, 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005, 0x080c,
+ 0xa713, 0x080c, 0x606c, 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9,
+ 0x0000, 0x20a1, 0x0240, 0x20a9, 0x000e, 0x4003, 0x2011, 0x026d,
+ 0x2204, 0x9084, 0x0100, 0x2011, 0x024d, 0x2012, 0x2011, 0x026e,
+ 0x709b, 0x0017, 0x080c, 0x604f, 0x1150, 0x7084, 0x9005, 0x1138,
+ 0x080c, 0x5e17, 0x1188, 0x9085, 0x0001, 0x080c, 0x26ca, 0x20a9,
+ 0x0008, 0x080c, 0x606c, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9,
+ 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c, 0x5f21,
+ 0x0010, 0x080c, 0x5971, 0x0005, 0x00f6, 0x7090, 0x9005, 0x01d8,
+ 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0084, 0x1190, 0x080c,
+ 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1106, 0x1150, 0x7834,
+ 0x9005, 0x1138, 0x9006, 0x080c, 0x6097, 0x709b, 0x0018, 0x0029,
+ 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005, 0x00f6, 0x709b, 0x0019,
+ 0x080c, 0x5ffe, 0x2079, 0x0240, 0x7833, 0x1106, 0x7837, 0x0000,
+ 0x080c, 0x606c, 0x2009, 0x026e, 0x2039, 0x1d0e, 0x20a9, 0x0040,
+ 0x213e, 0x8738, 0x8108, 0x9186, 0x0280, 0x1128, 0x6814, 0x8000,
+ 0x6816, 0x2009, 0x0260, 0x1f04, 0x5d80, 0x2039, 0x1d0e, 0x080c,
+ 0x604f, 0x11e8, 0x2728, 0x2514, 0x8207, 0x9084, 0x00ff, 0x8000,
+ 0x2018, 0x9294, 0x00ff, 0x8007, 0x9205, 0x202a, 0x7060, 0x2310,
+ 0x8214, 0x92a0, 0x1d0e, 0x2414, 0x938c, 0x0001, 0x0118, 0x9294,
+ 0xff00, 0x0018, 0x9294, 0x00ff, 0x8007, 0x9215, 0x2222, 0x20a9,
+ 0x0040, 0x2009, 0x024e, 0x270e, 0x8738, 0x8108, 0x9186, 0x0260,
+ 0x1128, 0x6810, 0x8000, 0x6812, 0x2009, 0x0240, 0x1f04, 0x5db3,
+ 0x60c3, 0x0084, 0x080c, 0x5f21, 0x00fe, 0x0005, 0x00f6, 0x7090,
+ 0x9005, 0x01e0, 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x9086, 0x0084,
+ 0x1198, 0x080c, 0x606c, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1107,
+ 0x1158, 0x7834, 0x9005, 0x1140, 0x7097, 0x0001, 0x080c, 0x5fc2,
+ 0x709b, 0x001a, 0x0029, 0x0010, 0x7093, 0x0000, 0x00fe, 0x0005,
+ 0x9085, 0x0001, 0x080c, 0x6097, 0x709b, 0x001b, 0x080c, 0xa713,
+ 0x080c, 0x606c, 0x2011, 0x0260, 0x2009, 0x0240, 0x7490, 0x9480,
+ 0x0018, 0x9080, 0x0007, 0x9084, 0x03f8, 0x8004, 0x20a8, 0x220e,
+ 0x8210, 0x8108, 0x9186, 0x0260, 0x1150, 0x6810, 0x8000, 0x6812,
+ 0x2009, 0x0240, 0x6814, 0x8000, 0x6816, 0x2011, 0x0260, 0x1f04,
+ 0x5dff, 0x60c3, 0x0084, 0x080c, 0x5f21, 0x0005, 0x0005, 0x0086,
+ 0x0096, 0x2029, 0x1848, 0x252c, 0x20a9, 0x0008, 0x2041, 0x1d0e,
+ 0x20e9, 0x0001, 0x28a0, 0x080c, 0x606c, 0x20e1, 0x0000, 0x2099,
+ 0x026e, 0x4003, 0x20a9, 0x0008, 0x2011, 0x0007, 0xd5d4, 0x0108,
+ 0x9016, 0x2800, 0x9200, 0x200c, 0x91a6, 0xffff, 0x1148, 0xd5d4,
+ 0x0110, 0x8210, 0x0008, 0x8211, 0x1f04, 0x5e31, 0x0804, 0x5ea0,
+ 0x82ff, 0x1160, 0xd5d4, 0x0120, 0x91a6, 0x3fff, 0x0d90, 0x0020,
+ 0x91a6, 0x3fff, 0x0904, 0x5ea0, 0x918d, 0xc000, 0x20a9, 0x0010,
+ 0x2019, 0x0001, 0xd5d4, 0x0110, 0x2019, 0x0010, 0x2120, 0xd5d4,
+ 0x0110, 0x8423, 0x0008, 0x8424, 0x1240, 0xd5d4, 0x0110, 0x8319,
+ 0x0008, 0x8318, 0x1f04, 0x5e57, 0x04d8, 0x23a8, 0x2021, 0x0001,
+ 0x8426, 0x8425, 0x1f04, 0x5e69, 0x2328, 0x8529, 0x92be, 0x0007,
+ 0x0158, 0x0006, 0x2039, 0x0007, 0x2200, 0x973a, 0x000e, 0x27a8,
+ 0x95a8, 0x0010, 0x1f04, 0x5e78, 0x755e, 0x95c8, 0x33b9, 0x292d,
+ 0x95ac, 0x00ff, 0x7582, 0x6532, 0x6536, 0x0016, 0x2508, 0x080c,
+ 0x26aa, 0x001e, 0x60e7, 0x0000, 0x65ea, 0x2018, 0x2304, 0x9405,
+ 0x201a, 0x7087, 0x0001, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x20e1,
+ 0x0001, 0x2898, 0x20a9, 0x0008, 0x4003, 0x9085, 0x0001, 0x0008,
+ 0x9006, 0x009e, 0x008e, 0x0005, 0x0156, 0x01c6, 0x01d6, 0x0136,
+ 0x0146, 0x22a8, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000,
+ 0x2011, 0x024e, 0x22a0, 0x4003, 0x014e, 0x013e, 0x01de, 0x01ce,
+ 0x015e, 0x2118, 0x9026, 0x2001, 0x0007, 0x939a, 0x0010, 0x0218,
+ 0x8420, 0x8001, 0x0cd0, 0x2118, 0x84ff, 0x0120, 0x939a, 0x0010,
+ 0x8421, 0x1de0, 0x2021, 0x0001, 0x83ff, 0x0118, 0x8423, 0x8319,
+ 0x1de8, 0x9238, 0x2029, 0x026e, 0x9528, 0x2504, 0x942c, 0x11b8,
+ 0x9405, 0x203a, 0x715e, 0x91a0, 0x33b9, 0x242d, 0x95ac, 0x00ff,
+ 0x7582, 0x6532, 0x6536, 0x0016, 0x2508, 0x080c, 0x26aa, 0x001e,
+ 0x60e7, 0x0000, 0x65ea, 0x7087, 0x0001, 0x9084, 0x0000, 0x0005,
+ 0x00e6, 0x2071, 0x1800, 0x708b, 0x0000, 0x00ee, 0x0005, 0x00e6,
+ 0x00f6, 0x2079, 0x0100, 0x2071, 0x0140, 0x080c, 0x5fb1, 0x080c,
+ 0x9ed4, 0x7004, 0x9084, 0x4000, 0x0110, 0x080c, 0x2a8a, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x1826, 0x2073, 0x0000, 0x7840, 0x0026,
+ 0x0016, 0x2009, 0x00f7, 0x080c, 0x600e, 0x001e, 0x9094, 0x0010,
+ 0x9285, 0x0080, 0x7842, 0x7a42, 0x002e, 0x012e, 0x00fe, 0x00ee,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x080c, 0x29e5, 0x0228, 0x2011,
+ 0x0101, 0x2204, 0xc0c5, 0x2012, 0x2011, 0x19f3, 0x2013, 0x0000,
+ 0x7093, 0x0000, 0x012e, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c,
+ 0x9ec7, 0x6144, 0xd184, 0x0120, 0x7198, 0x918d, 0x2000, 0x0018,
+ 0x718c, 0x918d, 0x1000, 0x2011, 0x1998, 0x2112, 0x2009, 0x07d0,
+ 0x2011, 0x5ef7, 0x080c, 0x8792, 0x0005, 0x0016, 0x0026, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0xa91e, 0x080c, 0xabe9, 0x080c,
+ 0xa93a, 0x2009, 0x00f7, 0x080c, 0x600e, 0x2061, 0x1a02, 0x900e,
+ 0x611a, 0x611e, 0x6172, 0x6176, 0x2061, 0x1800, 0x6003, 0x0001,
+ 0x2061, 0x0100, 0x6043, 0x0090, 0x6043, 0x0010, 0x2009, 0x1998,
+ 0x200b, 0x0000, 0x2009, 0x002d, 0x2011, 0x5f7d, 0x080c, 0x86bc,
+ 0x012e, 0x00ce, 0x002e, 0x001e, 0x0005, 0x00e6, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x0471, 0x2071, 0x0100, 0x080c, 0x9ed4, 0x2071,
+ 0x0140, 0x7004, 0x9084, 0x4000, 0x0110, 0x080c, 0x2a8a, 0x080c,
+ 0x7545, 0x0188, 0x080c, 0x7560, 0x1170, 0x080c, 0x784a, 0x0016,
+ 0x080c, 0x2779, 0x2001, 0x196c, 0x2102, 0x001e, 0x080c, 0x7845,
+ 0x080c, 0x746e, 0x0050, 0x2009, 0x0001, 0x080c, 0x2a66, 0x2001,
+ 0x0001, 0x080c, 0x2606, 0x080c, 0x5f4d, 0x012e, 0x000e, 0x00ee,
+ 0x0005, 0x2001, 0x180e, 0x2004, 0xd0bc, 0x0158, 0x0026, 0x0036,
+ 0x2011, 0x8017, 0x2001, 0x1998, 0x201c, 0x080c, 0x4b52, 0x003e,
+ 0x002e, 0x0005, 0x20a9, 0x0012, 0x20e9, 0x0001, 0x20a1, 0x1d80,
+ 0x080c, 0x606c, 0x20e9, 0x0000, 0x2099, 0x026e, 0x0099, 0x20a9,
+ 0x0020, 0x080c, 0x6066, 0x2099, 0x0260, 0x20a1, 0x1d92, 0x0051,
+ 0x20a9, 0x000e, 0x080c, 0x6069, 0x2099, 0x0260, 0x20a1, 0x1db2,
+ 0x0009, 0x0005, 0x0016, 0x0026, 0x3410, 0x3308, 0x2104, 0x8007,
+ 0x2012, 0x8108, 0x8210, 0x1f04, 0x5fe6, 0x002e, 0x001e, 0x0005,
+ 0x080c, 0xa713, 0x20e1, 0x0001, 0x2099, 0x1d00, 0x20e9, 0x0000,
+ 0x20a1, 0x0240, 0x20a9, 0x000c, 0x4003, 0x0005, 0x080c, 0xa713,
+ 0x080c, 0x606c, 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9, 0x0000,
+ 0x20a1, 0x0240, 0x20a9, 0x000c, 0x4003, 0x0005, 0x00c6, 0x0006,
+ 0x2061, 0x0100, 0x810f, 0x2001, 0x1834, 0x2004, 0x9005, 0x1138,
+ 0x2001, 0x1818, 0x2004, 0x9084, 0x00ff, 0x9105, 0x0010, 0x9185,
+ 0x00f7, 0x604a, 0x000e, 0x00ce, 0x0005, 0x0016, 0x0046, 0x080c,
+ 0x6ad9, 0x0158, 0x9006, 0x2020, 0x2009, 0x002a, 0x080c, 0xe445,
+ 0x2001, 0x180c, 0x200c, 0xc195, 0x2102, 0x2019, 0x002a, 0x900e,
+ 0x080c, 0x3205, 0x080c, 0xd09b, 0x0140, 0x0036, 0x2019, 0xffff,
+ 0x2021, 0x0007, 0x080c, 0x4d09, 0x003e, 0x004e, 0x001e, 0x0005,
+ 0x080c, 0x5f4d, 0x709b, 0x0000, 0x7093, 0x0000, 0x0005, 0x0006,
+ 0x2001, 0x180c, 0x2004, 0xd09c, 0x0100, 0x000e, 0x0005, 0x0006,
+ 0x0016, 0x0126, 0x2091, 0x8000, 0x2001, 0x0101, 0x200c, 0x918d,
+ 0x0006, 0x2102, 0x012e, 0x001e, 0x000e, 0x0005, 0x2009, 0x0001,
+ 0x0020, 0x2009, 0x0002, 0x0008, 0x900e, 0x6814, 0x9084, 0xffc0,
+ 0x910d, 0x6916, 0x0005, 0x00f6, 0x0156, 0x0146, 0x01d6, 0x9006,
+ 0x20a9, 0x0080, 0x20e9, 0x0001, 0x20a1, 0x1d00, 0x4004, 0x2079,
+ 0x1d00, 0x7803, 0x2200, 0x7807, 0x00ef, 0x780f, 0x00ef, 0x7813,
+ 0x0138, 0x7823, 0xffff, 0x7827, 0xffff, 0x01de, 0x014e, 0x015e,
+ 0x00fe, 0x0005, 0x2001, 0x1800, 0x2003, 0x0001, 0x0005, 0x2001,
+ 0x19a5, 0x0118, 0x2003, 0x0001, 0x0010, 0x2003, 0x0000, 0x0005,
+ 0x0156, 0x20a9, 0x0800, 0x2009, 0x1000, 0x9006, 0x200a, 0x8108,
+ 0x1f04, 0x60a6, 0x015e, 0x0005, 0x00d6, 0x0036, 0x0156, 0x0136,
+ 0x0146, 0x2069, 0x1847, 0x9006, 0xb802, 0xb8d6, 0xb807, 0x0707,
+ 0xb80a, 0xb80e, 0xb812, 0x9198, 0x33b9, 0x231d, 0x939c, 0x00ff,
+ 0xbb16, 0x0016, 0x0026, 0xb886, 0x080c, 0xabe2, 0x1120, 0x9192,
+ 0x007e, 0x1208, 0xbb86, 0x20a9, 0x0004, 0xb8c4, 0x20e8, 0xb9c8,
+ 0x9198, 0x0006, 0x9006, 0x23a0, 0x4004, 0x20a9, 0x0004, 0x9198,
+ 0x000a, 0x23a0, 0x4004, 0x002e, 0x001e, 0xb83e, 0xb842, 0xb8ce,
+ 0xb8d2, 0xb85e, 0xb862, 0xb866, 0xb86a, 0xb86f, 0x0100, 0xb872,
+ 0xb876, 0xb87a, 0xb88a, 0xb88e, 0xb893, 0x0008, 0xb896, 0xb89a,
+ 0xb89e, 0xb8be, 0xb9a2, 0x0096, 0xb8a4, 0x904d, 0x0110, 0x080c,
+ 0x1079, 0xb8a7, 0x0000, 0x009e, 0x9006, 0xb84a, 0x6810, 0xb83a,
+ 0x680c, 0xb846, 0xb8bb, 0x0520, 0xb8ac, 0x9005, 0x0198, 0x00c6,
+ 0x2060, 0x9c82, 0x1ddc, 0x0a0c, 0x0d7d, 0x2001, 0x181a, 0x2004,
+ 0x9c02, 0x1a0c, 0x0d7d, 0x080c, 0x8c1f, 0x00ce, 0x090c, 0x8fbc,
+ 0xb8af, 0x0000, 0x6814, 0x9084, 0x00ff, 0xb842, 0x014e, 0x013e,
+ 0x015e, 0x003e, 0x00de, 0x0005, 0x0126, 0x2091, 0x8000, 0xa974,
+ 0xae78, 0x9684, 0x3fff, 0x9082, 0x4000, 0x1a04, 0x6182, 0x9182,
+ 0x0800, 0x1a04, 0x6186, 0x2001, 0x180c, 0x2004, 0x9084, 0x0003,
+ 0x1904, 0x618c, 0x9188, 0x1000, 0x2104, 0x905d, 0x0198, 0xb804,
+ 0x9084, 0x00ff, 0x908e, 0x0006, 0x1188, 0xb8a4, 0x900d, 0x1904,
+ 0x619e, 0x080c, 0x655e, 0x9006, 0x012e, 0x0005, 0x2001, 0x0005,
+ 0x900e, 0x04b8, 0x2001, 0x0028, 0x900e, 0x0498, 0x9082, 0x0006,
+ 0x1290, 0x080c, 0xabe2, 0x1160, 0xb8a0, 0x9084, 0xff80, 0x1140,
+ 0xb900, 0xd1fc, 0x0d10, 0x2001, 0x0029, 0x2009, 0x1000, 0x0408,
+ 0x2001, 0x0028, 0x00a8, 0x2009, 0x180c, 0x210c, 0xd18c, 0x0118,
+ 0x2001, 0x0004, 0x0068, 0xd184, 0x0118, 0x2001, 0x0004, 0x0040,
+ 0x2001, 0x0029, 0xb900, 0xd1fc, 0x0118, 0x2009, 0x1000, 0x0048,
+ 0x900e, 0x0038, 0x2001, 0x0029, 0x900e, 0x0018, 0x2001, 0x0029,
+ 0x900e, 0x9005, 0x012e, 0x0005, 0x2001, 0x180c, 0x2004, 0xd084,
+ 0x19d0, 0x9188, 0x1000, 0x2104, 0x9065, 0x09a8, 0x080c, 0x6add,
+ 0x1990, 0xb800, 0xd0bc, 0x0978, 0x0804, 0x6145, 0x080c, 0x6902,
+ 0x0904, 0x614e, 0x0804, 0x6149, 0x00e6, 0x2071, 0x19e6, 0x7004,
+ 0x9086, 0x0002, 0x1128, 0x7030, 0x9080, 0x0004, 0x2004, 0x9b06,
+ 0x00ee, 0x0005, 0x00b6, 0x00e6, 0x0126, 0x2091, 0x8000, 0xa874,
+ 0x908e, 0x00ff, 0x1120, 0x2001, 0x196a, 0x205c, 0x0060, 0xa974,
+ 0x9182, 0x0800, 0x1690, 0x9188, 0x1000, 0x2104, 0x905d, 0x01d0,
+ 0x080c, 0x6a7d, 0x11d0, 0x080c, 0xac5a, 0x0570, 0x2b00, 0x6012,
+ 0x2900, 0x6016, 0x6023, 0x0009, 0x602b, 0x0000, 0xa874, 0x908e,
+ 0x00ff, 0x1110, 0x602b, 0x8000, 0x2009, 0x0043, 0x080c, 0xad4d,
+ 0x9006, 0x00b0, 0x2001, 0x0028, 0x0090, 0x2009, 0x180c, 0x210c,
+ 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038, 0xd184, 0x0118, 0x2001,
+ 0x0004, 0x0010, 0x2001, 0x0029, 0x0010, 0x2001, 0x0029, 0x9005,
+ 0x012e, 0x00ee, 0x00be, 0x0005, 0x2001, 0x002c, 0x0cc0, 0x00b6,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0xa974, 0x9182, 0x0800, 0x1a04,
+ 0x627d, 0x9188, 0x1000, 0x2104, 0x905d, 0x0904, 0x6255, 0xb8a0,
+ 0x9086, 0x007f, 0x0190, 0xa87c, 0xd0fc, 0x1178, 0x080c, 0x6ae5,
+ 0x0160, 0xa994, 0x81ff, 0x0130, 0x908e, 0x0004, 0x0130, 0x908e,
+ 0x0005, 0x0118, 0x080c, 0x6add, 0x1598, 0xa87c, 0xd0fc, 0x01e0,
+ 0xa894, 0x9005, 0x01c8, 0x2060, 0x0026, 0x2010, 0x080c, 0xc968,
+ 0x002e, 0x1120, 0x2001, 0x0008, 0x0804, 0x627f, 0x6020, 0x9086,
+ 0x000a, 0x0120, 0x2001, 0x0008, 0x0804, 0x627f, 0x601a, 0x6003,
+ 0x0008, 0x2900, 0x6016, 0x0058, 0x080c, 0xac5a, 0x05e8, 0x2b00,
+ 0x6012, 0x2900, 0x6016, 0x600b, 0xffff, 0x6023, 0x000a, 0x2009,
+ 0x0003, 0x080c, 0xad4d, 0x9006, 0x0458, 0x2001, 0x0028, 0x0438,
+ 0x9082, 0x0006, 0x1290, 0x080c, 0xabe2, 0x1160, 0xb8a0, 0x9084,
+ 0xff80, 0x1140, 0xb900, 0xd1fc, 0x0900, 0x2001, 0x0029, 0x2009,
+ 0x1000, 0x00a8, 0x2001, 0x0028, 0x0090, 0x2009, 0x180c, 0x210c,
+ 0xd18c, 0x0118, 0x2001, 0x0004, 0x0050, 0xd184, 0x0118, 0x2001,
+ 0x0004, 0x0028, 0x2001, 0x0029, 0x0010, 0x2001, 0x0029, 0x9005,
+ 0x012e, 0x00ee, 0x00be, 0x0005, 0x2001, 0x002c, 0x0cc0, 0x00f6,
+ 0x00b6, 0x0126, 0x2091, 0x8000, 0xa8e0, 0x9005, 0x1550, 0xa8dc,
+ 0x9082, 0x0101, 0x1630, 0xa8c8, 0x9005, 0x1518, 0xa8c4, 0x9082,
+ 0x0101, 0x12f8, 0xa974, 0x2079, 0x1800, 0x9182, 0x0800, 0x12e8,
+ 0x7830, 0x9084, 0x0003, 0x1130, 0xaa98, 0xab94, 0xa878, 0x9084,
+ 0x0007, 0x00ea, 0x7930, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038,
+ 0xd184, 0x0118, 0x2001, 0x0004, 0x0010, 0x2001, 0x0029, 0x900e,
+ 0x0038, 0x2001, 0x002c, 0x900e, 0x0018, 0x2001, 0x0029, 0x900e,
+ 0x9006, 0x0008, 0x9005, 0x012e, 0x00be, 0x00fe, 0x0005, 0x6314,
+ 0x62cf, 0x62e6, 0x6314, 0x6314, 0x6314, 0x6314, 0x6314, 0x2100,
+ 0x9082, 0x007e, 0x1278, 0x080c, 0x6632, 0x0148, 0x9046, 0xb810,
+ 0x9306, 0x1904, 0x631c, 0xb814, 0x9206, 0x15f0, 0x0028, 0xbb12,
+ 0xba16, 0x0010, 0x080c, 0x4a05, 0x0150, 0x04b0, 0x080c, 0x6693,
+ 0x1598, 0xb810, 0x9306, 0x1580, 0xb814, 0x9206, 0x1568, 0x080c,
+ 0xac5a, 0x0530, 0x2b00, 0x6012, 0x080c, 0xce15, 0x2900, 0x6016,
+ 0x600b, 0xffff, 0x6023, 0x000a, 0xa878, 0x9086, 0x0001, 0x1170,
+ 0x080c, 0x3240, 0x9006, 0x080c, 0x65cf, 0x2001, 0x0002, 0x080c,
+ 0x65e3, 0x2001, 0x0200, 0xb86e, 0xb893, 0x0002, 0x2009, 0x0003,
+ 0x080c, 0xad4d, 0x9006, 0x0068, 0x2001, 0x0001, 0x900e, 0x0038,
+ 0x2001, 0x002c, 0x900e, 0x0018, 0x2001, 0x0028, 0x900e, 0x9005,
+ 0x0000, 0x012e, 0x00be, 0x00fe, 0x0005, 0x00b6, 0x00f6, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0xa894, 0x90c6, 0x0015, 0x0904, 0x6507,
+ 0x90c6, 0x0056, 0x0904, 0x650b, 0x90c6, 0x0066, 0x0904, 0x650f,
+ 0x90c6, 0x0067, 0x0904, 0x6513, 0x90c6, 0x0068, 0x0904, 0x6517,
+ 0x90c6, 0x0071, 0x0904, 0x651b, 0x90c6, 0x0074, 0x0904, 0x651f,
+ 0x90c6, 0x007c, 0x0904, 0x6523, 0x90c6, 0x007e, 0x0904, 0x6527,
+ 0x90c6, 0x0037, 0x0904, 0x652b, 0x9016, 0x2079, 0x1800, 0xa974,
+ 0x9186, 0x00ff, 0x0904, 0x6502, 0x9182, 0x0800, 0x1a04, 0x6502,
+ 0x080c, 0x6693, 0x1198, 0xb804, 0x9084, 0x00ff, 0x9082, 0x0006,
+ 0x1268, 0xa894, 0x90c6, 0x006f, 0x0148, 0x080c, 0xabe2, 0x1904,
+ 0x64eb, 0xb8a0, 0x9084, 0xff80, 0x1904, 0x64eb, 0xa894, 0x90c6,
+ 0x006f, 0x0158, 0x90c6, 0x005e, 0x0904, 0x644b, 0x90c6, 0x0064,
+ 0x0904, 0x6474, 0x2008, 0x0804, 0x640d, 0xa998, 0xa8b0, 0x2040,
+ 0x080c, 0xabe2, 0x1120, 0x9182, 0x007f, 0x0a04, 0x640d, 0x9186,
+ 0x00ff, 0x0904, 0x640d, 0x9182, 0x0800, 0x1a04, 0x640d, 0xaaa0,
+ 0xab9c, 0x787c, 0x9306, 0x11a8, 0x7880, 0x0096, 0x924e, 0x1128,
+ 0x2208, 0x2310, 0x009e, 0x0804, 0x640d, 0x080c, 0xabe2, 0x1140,
+ 0x99cc, 0xff00, 0x009e, 0x1128, 0x2208, 0x2310, 0x0804, 0x640d,
+ 0x009e, 0x080c, 0x4a05, 0x0904, 0x6417, 0x900e, 0x9016, 0x90c6,
+ 0x4000, 0x15e0, 0x0006, 0x080c, 0x6986, 0x1108, 0xc185, 0xb800,
+ 0xd0bc, 0x0108, 0xc18d, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0031, 0x20a0, 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x0006,
+ 0x2098, 0x080c, 0x0fc4, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0035, 0x20a0, 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x000a,
+ 0x2098, 0x080c, 0x0fc4, 0xa8c4, 0xabc8, 0x9305, 0xabcc, 0x9305,
+ 0xabd0, 0x9305, 0xabd4, 0x9305, 0xabd8, 0x9305, 0xabdc, 0x9305,
+ 0xabe0, 0x9305, 0x9005, 0x0510, 0x000e, 0x00c8, 0x90c6, 0x4007,
+ 0x1110, 0x2408, 0x00a0, 0x90c6, 0x4008, 0x1118, 0x2708, 0x2610,
+ 0x0070, 0x90c6, 0x4009, 0x1108, 0x0050, 0x90c6, 0x4006, 0x0138,
+ 0x2001, 0x4005, 0x2009, 0x000a, 0x0010, 0x2001, 0x4006, 0xa896,
+ 0xa99a, 0xaa9e, 0x2001, 0x0030, 0x900e, 0x0478, 0x000e, 0x080c,
+ 0xac5a, 0x1130, 0x2001, 0x4005, 0x2009, 0x0003, 0x9016, 0x0c78,
+ 0x2b00, 0x6012, 0x080c, 0xce15, 0x2900, 0x6016, 0x6023, 0x0001,
+ 0xa868, 0xd88c, 0x0108, 0xc0f5, 0xa86a, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x3240, 0x012e, 0x9006, 0x080c, 0x65cf, 0x2001, 0x0002,
+ 0x080c, 0x65e3, 0x2009, 0x0002, 0x080c, 0xad4d, 0xa8b0, 0xd094,
+ 0x0118, 0xb8d4, 0xc08d, 0xb8d6, 0x9006, 0x9005, 0x012e, 0x00ee,
+ 0x00fe, 0x00be, 0x0005, 0x080c, 0x5752, 0x0118, 0x2009, 0x0007,
+ 0x00f8, 0xa998, 0xaeb0, 0x080c, 0x6693, 0x1904, 0x6408, 0x9186,
+ 0x007f, 0x0130, 0x080c, 0x6add, 0x0118, 0x2009, 0x0009, 0x0080,
+ 0x0096, 0x080c, 0x1047, 0x1120, 0x009e, 0x2009, 0x0002, 0x0040,
+ 0x2900, 0x009e, 0xa806, 0x080c, 0xcb68, 0x19b0, 0x2009, 0x0003,
+ 0x2001, 0x4005, 0x0804, 0x640f, 0xa998, 0xaeb0, 0x080c, 0x6693,
+ 0x1904, 0x6408, 0x0096, 0x080c, 0x1047, 0x1128, 0x009e, 0x2009,
+ 0x0002, 0x0804, 0x64c8, 0x2900, 0x009e, 0xa806, 0x0096, 0x2048,
+ 0x20a9, 0x002b, 0xb8c4, 0x20e0, 0xb8c8, 0x2098, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x0002, 0x20a0, 0x4003, 0x20a9, 0x0008, 0x9080,
+ 0x0006, 0x20a0, 0xbbc8, 0x9398, 0x0006, 0x2398, 0x080c, 0x0fc4,
+ 0x009e, 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0xd684,
+ 0x1168, 0x080c, 0x573e, 0xd0b4, 0x1118, 0xa89b, 0x000b, 0x00e0,
+ 0xb800, 0xd08c, 0x0118, 0xa89b, 0x000c, 0x00b0, 0x080c, 0x6add,
+ 0x0118, 0xa89b, 0x0009, 0x0080, 0x080c, 0x5752, 0x0118, 0xa89b,
+ 0x0007, 0x0050, 0x080c, 0xcb4b, 0x1904, 0x6444, 0x2009, 0x0003,
+ 0x2001, 0x4005, 0x0804, 0x640f, 0xa87b, 0x0030, 0xa897, 0x4005,
+ 0xa804, 0x8006, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0,
+ 0x9080, 0x0002, 0x2009, 0x002b, 0xaaa0, 0xab9c, 0xaca8, 0xada4,
+ 0x2031, 0x0000, 0x2041, 0x1296, 0x080c, 0xb1d4, 0x1904, 0x6444,
+ 0x2009, 0x0002, 0x08e8, 0x2001, 0x0028, 0x900e, 0x0804, 0x6445,
+ 0x2009, 0x180c, 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038,
+ 0xd184, 0x0118, 0x2001, 0x0004, 0x0010, 0x2001, 0x0029, 0x900e,
+ 0x0804, 0x6445, 0x2001, 0x0029, 0x900e, 0x0804, 0x6445, 0x080c,
+ 0x37e7, 0x0804, 0x6446, 0x080c, 0x545b, 0x0804, 0x6446, 0x080c,
+ 0x45b9, 0x0804, 0x6446, 0x080c, 0x4632, 0x0804, 0x6446, 0x080c,
+ 0x468e, 0x0804, 0x6446, 0x080c, 0x4ac8, 0x0804, 0x6446, 0x080c,
+ 0x4d7c, 0x0804, 0x6446, 0x080c, 0x50c3, 0x0804, 0x6446, 0x080c,
+ 0x52bc, 0x0804, 0x6446, 0x080c, 0x3a0b, 0x0804, 0x6446, 0x00b6,
+ 0xa974, 0xae78, 0x9684, 0x3fff, 0x9082, 0x4000, 0x1608, 0x9182,
+ 0x0800, 0x1258, 0x9188, 0x1000, 0x2104, 0x905d, 0x0130, 0x080c,
+ 0x6add, 0x1138, 0x00d9, 0x9006, 0x00b0, 0x2001, 0x0028, 0x900e,
+ 0x0090, 0x9082, 0x0006, 0x1240, 0xb900, 0xd1fc, 0x0d98, 0x2001,
+ 0x0029, 0x2009, 0x1000, 0x0038, 0x2001, 0x0029, 0x900e, 0x0018,
+ 0x2001, 0x0029, 0x900e, 0x9005, 0x00be, 0x0005, 0xa877, 0x0000,
+ 0xb8d0, 0x9005, 0x1904, 0x65c3, 0xb888, 0x9005, 0x1904, 0x65c3,
+ 0xb838, 0xb93c, 0x9102, 0x1a04, 0x65c3, 0x2b10, 0x080c, 0xac87,
+ 0x0904, 0x65bf, 0x8108, 0xb93e, 0x6212, 0x2900, 0x6016, 0x6023,
+ 0x0003, 0x600b, 0xffff, 0x6007, 0x0040, 0xa878, 0x605e, 0xa880,
+ 0x6066, 0xa883, 0x0000, 0xa87c, 0xd0ac, 0x0588, 0xc0dd, 0xa87e,
+ 0xa888, 0x8001, 0x1530, 0xa816, 0xa864, 0x9094, 0x00f7, 0x9296,
+ 0x0011, 0x11f8, 0x9084, 0x00ff, 0xc0bd, 0x601e, 0xa8ac, 0xaab0,
+ 0xa836, 0xaa3a, 0x2001, 0x000f, 0x8001, 0x1df0, 0x2001, 0x8004,
+ 0x6003, 0x0004, 0x6046, 0x00f6, 0x2079, 0x0380, 0x7818, 0xd0bc,
+ 0x1de8, 0x7833, 0x0010, 0x2c00, 0x7836, 0x781b, 0x8080, 0x00fe,
+ 0x0005, 0x080c, 0x1778, 0x601c, 0xc0bd, 0x601e, 0x0c38, 0xd0b4,
+ 0x190c, 0x1c86, 0x2001, 0x8004, 0x6003, 0x0002, 0x0c18, 0x81ff,
+ 0x1110, 0xb88b, 0x0001, 0x2908, 0xb8cc, 0xb9ce, 0x9005, 0x1110,
+ 0xb9d2, 0x0020, 0x0096, 0x2048, 0xa902, 0x009e, 0x0005, 0x00b6,
+ 0x0126, 0x00c6, 0x0026, 0x2091, 0x8000, 0x6210, 0x2258, 0xba00,
+ 0x9005, 0x0110, 0xc285, 0x0008, 0xc284, 0xba02, 0x002e, 0x00ce,
+ 0x012e, 0x00be, 0x0005, 0x00b6, 0x0126, 0x00c6, 0x2091, 0x8000,
+ 0x6210, 0x2258, 0xba04, 0x0006, 0x9086, 0x0006, 0x1170, 0xb89c,
+ 0xd0ac, 0x0158, 0x080c, 0x6ad9, 0x0140, 0x9284, 0xff00, 0x8007,
+ 0x9086, 0x0007, 0x1110, 0x2011, 0x0600, 0x000e, 0x9294, 0xff00,
+ 0x9215, 0xba06, 0x0006, 0x9086, 0x0006, 0x1120, 0xba90, 0x82ff,
+ 0x090c, 0x0d7d, 0x000e, 0x00ce, 0x012e, 0x00be, 0x0005, 0x00b6,
+ 0x0126, 0x00c6, 0x2091, 0x8000, 0x6210, 0x2258, 0xba04, 0x0006,
+ 0x9086, 0x0006, 0x1168, 0xb89c, 0xd0a4, 0x0150, 0x080c, 0x6ad5,
+ 0x1138, 0x9284, 0x00ff, 0x9086, 0x0007, 0x1110, 0x2011, 0x0006,
+ 0x000e, 0x9294, 0x00ff, 0x8007, 0x9215, 0xba06, 0x00ce, 0x012e,
+ 0x00be, 0x0005, 0x9182, 0x0800, 0x0218, 0x9085, 0x0001, 0x0005,
+ 0x00d6, 0x0026, 0x9190, 0x1000, 0x2204, 0x905d, 0x1188, 0x0096,
+ 0x080c, 0x1047, 0x2958, 0x009e, 0x0168, 0x2b00, 0x2012, 0xb85c,
+ 0xb8ca, 0xb860, 0xb8c6, 0x9006, 0xb8a6, 0xb8ae, 0x080c, 0x60ac,
+ 0x9006, 0x0010, 0x9085, 0x0001, 0x002e, 0x00de, 0x0005, 0x00b6,
+ 0x0096, 0x0126, 0x2091, 0x8000, 0x0026, 0x9182, 0x0800, 0x0218,
+ 0x9085, 0x0001, 0x0458, 0x00d6, 0x9190, 0x1000, 0x2204, 0x905d,
+ 0x0518, 0x2013, 0x0000, 0xb8a4, 0x904d, 0x0110, 0x080c, 0x1079,
+ 0x00d6, 0x00c6, 0xb8bc, 0x2060, 0x8cff, 0x0168, 0x600c, 0x0006,
+ 0x6014, 0x2048, 0x080c, 0xc97a, 0x0110, 0x080c, 0x0ff9, 0x080c,
+ 0xacb0, 0x00ce, 0x0c88, 0x00ce, 0x00de, 0x2b48, 0xb8c8, 0xb85e,
+ 0xb8c4, 0xb862, 0x080c, 0x1089, 0x00de, 0x9006, 0x002e, 0x012e,
+ 0x009e, 0x00be, 0x0005, 0x0016, 0x9182, 0x0800, 0x0218, 0x9085,
+ 0x0001, 0x0030, 0x9188, 0x1000, 0x2104, 0x905d, 0x0dc0, 0x9006,
+ 0x001e, 0x0005, 0x00d6, 0x0156, 0x0136, 0x0146, 0x9006, 0xb80a,
+ 0xb80e, 0xb800, 0xc08c, 0xb802, 0x080c, 0x753d, 0x1510, 0xb8a0,
+ 0x9086, 0x007e, 0x0120, 0x080c, 0xabe2, 0x11d8, 0x0078, 0x7040,
+ 0xd0e4, 0x01b8, 0x00c6, 0x2061, 0x1981, 0x7048, 0x2062, 0x704c,
+ 0x6006, 0x7050, 0x600a, 0x7054, 0x600e, 0x00ce, 0x703c, 0x2069,
+ 0x0140, 0x9005, 0x1110, 0x2001, 0x0001, 0x6886, 0x2069, 0x1800,
+ 0x68b6, 0x7040, 0xb85e, 0x7048, 0xb862, 0x704c, 0xb866, 0x20e1,
+ 0x0000, 0x2099, 0x0276, 0xb8c4, 0x20e8, 0xb8c8, 0x9088, 0x000a,
+ 0x21a0, 0x20a9, 0x0004, 0x4003, 0x2099, 0x027a, 0x9088, 0x0006,
+ 0x21a0, 0x20a9, 0x0004, 0x4003, 0x2069, 0x0200, 0x6817, 0x0001,
+ 0x7040, 0xb86a, 0x7144, 0xb96e, 0x7048, 0xb872, 0x7050, 0xb876,
+ 0x2069, 0x0200, 0x6817, 0x0000, 0xb8a0, 0x9086, 0x007e, 0x1110,
+ 0x7144, 0xb96e, 0x9182, 0x0211, 0x1218, 0x2009, 0x0008, 0x0400,
+ 0x9182, 0x0259, 0x1218, 0x2009, 0x0007, 0x00d0, 0x9182, 0x02c1,
+ 0x1218, 0x2009, 0x0006, 0x00a0, 0x9182, 0x0349, 0x1218, 0x2009,
+ 0x0005, 0x0070, 0x9182, 0x0421, 0x1218, 0x2009, 0x0004, 0x0040,
+ 0x9182, 0x0581, 0x1218, 0x2009, 0x0003, 0x0010, 0x2009, 0x0002,
+ 0xb992, 0x014e, 0x013e, 0x015e, 0x00de, 0x0005, 0x0016, 0x0026,
+ 0x00e6, 0x2071, 0x0260, 0x7034, 0xb896, 0x703c, 0xb89a, 0x7054,
+ 0xb89e, 0x0036, 0xbbd4, 0xc384, 0xba00, 0x2009, 0x1867, 0x210c,
+ 0xd0bc, 0x0120, 0xd1ec, 0x0110, 0xc2ad, 0x0008, 0xc2ac, 0xd0c4,
+ 0x0148, 0xd1e4, 0x0138, 0xc2bd, 0xd0cc, 0x0128, 0xd38c, 0x1108,
+ 0xc385, 0x0008, 0xc2bc, 0xba02, 0xbbd6, 0x003e, 0x00ee, 0x002e,
+ 0x001e, 0x0005, 0x0096, 0x0126, 0x2091, 0x8000, 0xb8a4, 0x904d,
+ 0x0578, 0xa900, 0x81ff, 0x15c0, 0xaa04, 0x9282, 0x0010, 0x16c8,
+ 0x0136, 0x0146, 0x01c6, 0x01d6, 0x8906, 0x8006, 0x8007, 0x908c,
+ 0x003f, 0x21e0, 0x9084, 0xffc0, 0x9080, 0x0004, 0x2098, 0x2009,
+ 0x0010, 0x20a9, 0x0001, 0x4002, 0x9086, 0xffff, 0x0120, 0x8109,
+ 0x1dd0, 0x080c, 0x0d7d, 0x3c00, 0x20e8, 0x3300, 0x8001, 0x20a0,
+ 0x4604, 0x8210, 0xaa06, 0x01de, 0x01ce, 0x014e, 0x013e, 0x0060,
+ 0x080c, 0x1047, 0x0170, 0x2900, 0xb8a6, 0xa803, 0x0000, 0x080c,
+ 0x6922, 0xa807, 0x0001, 0xae12, 0x9085, 0x0001, 0x012e, 0x009e,
+ 0x0005, 0x9006, 0x0cd8, 0x0126, 0x2091, 0x8000, 0x0096, 0xb8a4,
+ 0x904d, 0x0188, 0xa800, 0x9005, 0x1150, 0x080c, 0x6931, 0x1158,
+ 0xa804, 0x908a, 0x0002, 0x0218, 0x8001, 0xa806, 0x0020, 0x080c,
+ 0x1079, 0xb8a7, 0x0000, 0x009e, 0x012e, 0x0005, 0x0096, 0x00c6,
+ 0xb888, 0x9005, 0x1904, 0x6817, 0xb8d0, 0x904d, 0x0904, 0x6817,
+ 0x080c, 0xac87, 0x0904, 0x6813, 0x8210, 0xba3e, 0xa800, 0xb8d2,
+ 0x9005, 0x1108, 0xb8ce, 0x2b00, 0x6012, 0x2900, 0x6016, 0x6023,
+ 0x0003, 0x600b, 0xffff, 0x6007, 0x0040, 0xa878, 0x605e, 0xa880,
+ 0x9084, 0x00ff, 0x6066, 0xa883, 0x0000, 0xa87c, 0xd0ac, 0x01c8,
+ 0xc0dd, 0xa87e, 0xa888, 0x8001, 0x1568, 0xa816, 0xa864, 0x9094,
+ 0x00f7, 0x9296, 0x0011, 0x1530, 0x9084, 0x00ff, 0xc0bd, 0x601e,
+ 0xa8ac, 0xaab0, 0xa836, 0xaa3a, 0x2001, 0x8004, 0x6003, 0x0004,
+ 0x0030, 0x080c, 0x1c86, 0x2001, 0x8004, 0x6003, 0x0002, 0x6046,
+ 0x2001, 0x0010, 0x2c08, 0x080c, 0xa90f, 0xb838, 0xba3c, 0x9202,
+ 0x0a04, 0x67c4, 0x0020, 0x82ff, 0x1110, 0xb88b, 0x0001, 0x00ce,
+ 0x009e, 0x0005, 0x080c, 0x1778, 0x601c, 0xc0bd, 0x601e, 0x08e0,
+ 0x00b6, 0x0096, 0x0016, 0x20a9, 0x0800, 0x900e, 0x0016, 0x080c,
+ 0x6693, 0x1158, 0xb8d0, 0x904d, 0x0140, 0x3e00, 0x9086, 0x0002,
+ 0x1118, 0xb800, 0xd0bc, 0x1108, 0x0041, 0x001e, 0x8108, 0x1f04,
+ 0x6826, 0x001e, 0x00be, 0x009e, 0x0005, 0x0096, 0x0016, 0xb8d0,
+ 0x904d, 0x0188, 0xa800, 0xb8d2, 0x9005, 0x1108, 0xb8ce, 0x9006,
+ 0xa802, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0xcc7f,
+ 0x080c, 0x6dee, 0x0c60, 0x001e, 0x009e, 0x0005, 0x0086, 0x9046,
+ 0xb8d0, 0x904d, 0x01b0, 0xa86c, 0x9406, 0x1118, 0xa870, 0x9506,
+ 0x0128, 0x2940, 0xa800, 0x904d, 0x0160, 0x0ca8, 0xa800, 0x88ff,
+ 0x1128, 0xb8d2, 0x9005, 0x1118, 0xb8ce, 0x0008, 0xa002, 0xa803,
+ 0x0000, 0x008e, 0x0005, 0x901e, 0x0010, 0x2019, 0x0001, 0x0126,
+ 0x2091, 0x8000, 0x00e6, 0x0096, 0x00c6, 0x0086, 0x0026, 0x2071,
+ 0x19e6, 0x9046, 0x7028, 0x9065, 0x01e8, 0x6014, 0x2068, 0x83ff,
+ 0x0120, 0x605c, 0x9606, 0x0158, 0x0030, 0xa86c, 0x9406, 0x1118,
+ 0xa870, 0x9506, 0x0120, 0x2c40, 0x600c, 0x2060, 0x0c60, 0x600c,
+ 0x0006, 0x0066, 0x2830, 0x080c, 0xa042, 0x006e, 0x000e, 0x83ff,
+ 0x0508, 0x0c08, 0x9046, 0xb8d0, 0x904d, 0x01e0, 0x83ff, 0x0120,
+ 0xa878, 0x9606, 0x0158, 0x0030, 0xa86c, 0x9406, 0x1118, 0xa870,
+ 0x9506, 0x0120, 0x2940, 0xa800, 0x2048, 0x0c70, 0xb8d0, 0xaa00,
+ 0x0026, 0x9906, 0x1110, 0xbad2, 0x0008, 0xa202, 0x000e, 0x83ff,
+ 0x0108, 0x0c10, 0x002e, 0x008e, 0x00ce, 0x009e, 0x00ee, 0x012e,
+ 0x0005, 0x9016, 0x0489, 0x1110, 0x2011, 0x0001, 0x0005, 0x080c,
+ 0x6986, 0x0128, 0x080c, 0xca3b, 0x0010, 0x9085, 0x0001, 0x0005,
+ 0x080c, 0x6986, 0x0128, 0x080c, 0xc9dc, 0x0010, 0x9085, 0x0001,
+ 0x0005, 0x080c, 0x6986, 0x0128, 0x080c, 0xca38, 0x0010, 0x9085,
+ 0x0001, 0x0005, 0x080c, 0x6986, 0x0128, 0x080c, 0xc9fb, 0x0010,
+ 0x9085, 0x0001, 0x0005, 0x080c, 0x6986, 0x0128, 0x080c, 0xca7e,
+ 0x0010, 0x9085, 0x0001, 0x0005, 0xb8a4, 0x900d, 0x1118, 0x9085,
+ 0x0001, 0x0005, 0x0136, 0x01c6, 0xa800, 0x9005, 0x11b8, 0x890e,
+ 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184, 0xffc0, 0x9080,
+ 0x0004, 0x2098, 0x20a9, 0x0001, 0x2009, 0x0010, 0x4002, 0x9606,
+ 0x0128, 0x8109, 0x1dd8, 0x9085, 0x0001, 0x0008, 0x9006, 0x01ce,
+ 0x013e, 0x0005, 0x0146, 0x01d6, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0004, 0x20a0, 0x20a9, 0x0010, 0x2009, 0xffff, 0x4104, 0x01de,
+ 0x014e, 0x0136, 0x01c6, 0xa800, 0x9005, 0x11b8, 0x890e, 0x810e,
+ 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184, 0xffc0, 0x9080, 0x0004,
+ 0x2098, 0x20a9, 0x0001, 0x2009, 0x0010, 0x4002, 0x9606, 0x0128,
+ 0x8109, 0x1dd8, 0x9085, 0x0001, 0x0068, 0x0146, 0x01d6, 0x3300,
+ 0x8001, 0x20a0, 0x3c00, 0x20e8, 0x2001, 0xffff, 0x4004, 0x01de,
+ 0x014e, 0x9006, 0x01ce, 0x013e, 0x0005, 0x0096, 0x0126, 0x2091,
+ 0x8000, 0xb8a4, 0x904d, 0x1128, 0x080c, 0x1047, 0x0168, 0x2900,
+ 0xb8a6, 0x080c, 0x6922, 0xa803, 0x0001, 0xa807, 0x0000, 0x9085,
+ 0x0001, 0x012e, 0x009e, 0x0005, 0x9006, 0x0cd8, 0x0096, 0x0126,
+ 0x2091, 0x8000, 0xb8a4, 0x904d, 0x0130, 0xb8a7, 0x0000, 0x080c,
+ 0x1079, 0x9085, 0x0001, 0x012e, 0x009e, 0x0005, 0xb89c, 0xd0a4,
+ 0x0005, 0x00b6, 0x00f6, 0x080c, 0x753d, 0x01b0, 0x71c4, 0x81ff,
+ 0x1198, 0x71dc, 0xd19c, 0x0180, 0x2001, 0x007e, 0x9080, 0x1000,
+ 0x2004, 0x905d, 0x0148, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006,
+ 0x1118, 0xb800, 0xc0ed, 0xb802, 0x2079, 0x1847, 0x7804, 0xd0a4,
+ 0x01d0, 0x0156, 0x20a9, 0x007f, 0x900e, 0x0016, 0x080c, 0x6693,
+ 0x1168, 0xb804, 0x9084, 0xff00, 0x8007, 0x9096, 0x0004, 0x0118,
+ 0x9086, 0x0006, 0x1118, 0xb800, 0xc0ed, 0xb802, 0x001e, 0x8108,
+ 0x1f04, 0x69ad, 0x015e, 0x080c, 0x6a9b, 0x0120, 0x2001, 0x1984,
+ 0x200c, 0x0038, 0x2079, 0x1847, 0x7804, 0xd0a4, 0x0130, 0x2009,
+ 0x07d0, 0x2011, 0x69d8, 0x080c, 0x8792, 0x00fe, 0x00be, 0x0005,
+ 0x00b6, 0x2011, 0x69d8, 0x080c, 0x86c8, 0x080c, 0x6a9b, 0x01d8,
+ 0x2001, 0x107e, 0x2004, 0x2058, 0xb900, 0xc1ec, 0xb902, 0x080c,
+ 0x6ad9, 0x0130, 0x2009, 0x07d0, 0x2011, 0x69d8, 0x080c, 0x8792,
+ 0x00e6, 0x2071, 0x1800, 0x9006, 0x707e, 0x7060, 0x7082, 0x080c,
+ 0x3011, 0x00ee, 0x04d0, 0x0156, 0x00c6, 0x20a9, 0x007f, 0x900e,
+ 0x0016, 0x080c, 0x6693, 0x1558, 0xb800, 0xd0ec, 0x0540, 0x0046,
+ 0xbaa0, 0x2220, 0x9006, 0x2009, 0x0029, 0x080c, 0xe445, 0xb800,
+ 0xc0e5, 0xc0ec, 0xb802, 0x080c, 0x6ad5, 0x2001, 0x0707, 0x1128,
+ 0xb804, 0x9084, 0x00ff, 0x9085, 0x0700, 0xb806, 0x080c, 0xa91e,
+ 0x2019, 0x0029, 0x080c, 0x943d, 0x0076, 0x903e, 0x080c, 0x9306,
+ 0x900e, 0x080c, 0xe167, 0x007e, 0x004e, 0x080c, 0xa93a, 0x001e,
+ 0x8108, 0x1f04, 0x6a00, 0x00ce, 0x015e, 0x00be, 0x0005, 0x00b6,
+ 0x6010, 0x2058, 0xb800, 0xc0ec, 0xb802, 0x00be, 0x0005, 0x00b6,
+ 0x00c6, 0x0096, 0x080c, 0x1060, 0x090c, 0x0d7d, 0x2958, 0x009e,
+ 0x2001, 0x196a, 0x2b02, 0x8b07, 0x8006, 0x8006, 0x908c, 0x003f,
+ 0xb9c6, 0x908c, 0xffc0, 0xb9ca, 0xb8af, 0x0000, 0x2009, 0x00ff,
+ 0x080c, 0x60ac, 0xb807, 0x0006, 0xb813, 0x00ff, 0xb817, 0xffff,
+ 0xb86f, 0x0200, 0xb86c, 0xb893, 0x0002, 0xb8bb, 0x0520, 0xb8a3,
+ 0x00ff, 0xb8af, 0x0000, 0x00ce, 0x00be, 0x0005, 0x7810, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0ac, 0x0005, 0x6010, 0x00b6, 0x905d,
+ 0x0108, 0xb800, 0x00be, 0xd0bc, 0x0005, 0x0006, 0x0016, 0x0026,
+ 0xb804, 0x908c, 0x00ff, 0x9196, 0x0006, 0x0188, 0x9196, 0x0004,
+ 0x0170, 0x9196, 0x0005, 0x0158, 0x908c, 0xff00, 0x810f, 0x9196,
+ 0x0006, 0x0128, 0x9196, 0x0004, 0x0110, 0x9196, 0x0005, 0x002e,
+ 0x001e, 0x000e, 0x0005, 0x00b6, 0x00f6, 0x2001, 0x107e, 0x2004,
+ 0x905d, 0x0110, 0xb800, 0xd0ec, 0x00fe, 0x00be, 0x0005, 0x0126,
+ 0x0026, 0x2091, 0x8000, 0x0006, 0xbaa0, 0x9290, 0x1000, 0x2204,
+ 0x9b06, 0x190c, 0x0d7d, 0x000e, 0xba00, 0x9005, 0x0110, 0xc2fd,
+ 0x0008, 0xc2fc, 0xba02, 0x002e, 0x012e, 0x0005, 0x2011, 0x1837,
+ 0x2204, 0xd0cc, 0x0138, 0x2001, 0x1982, 0x200c, 0x2011, 0x6acb,
+ 0x080c, 0x8792, 0x0005, 0x2011, 0x6acb, 0x080c, 0x86c8, 0x2011,
+ 0x1837, 0x2204, 0xc0cc, 0x2012, 0x0005, 0x080c, 0x573e, 0xd0ac,
+ 0x0005, 0x080c, 0x573e, 0xd0a4, 0x0005, 0x0016, 0xb904, 0x9184,
+ 0x00ff, 0x908e, 0x0006, 0x001e, 0x0005, 0x0016, 0xb904, 0x9184,
+ 0xff00, 0x8007, 0x908e, 0x0006, 0x001e, 0x0005, 0x00b6, 0x00f6,
+ 0x080c, 0xd09b, 0x0158, 0x70dc, 0x9084, 0x0028, 0x0138, 0x2001,
+ 0x107f, 0x2004, 0x905d, 0x0110, 0xb8d4, 0xd094, 0x00fe, 0x00be,
+ 0x0005, 0x2071, 0x1910, 0x7003, 0x0001, 0x7007, 0x0000, 0x9006,
+ 0x7012, 0x7016, 0x701a, 0x701e, 0x700a, 0x7046, 0x0005, 0x0016,
+ 0x00e6, 0x2071, 0x1947, 0x900e, 0x710a, 0x080c, 0x573e, 0xd0fc,
+ 0x1140, 0x080c, 0x573e, 0x900e, 0xd09c, 0x0108, 0x8108, 0x7102,
+ 0x00f8, 0x2001, 0x1867, 0x200c, 0x9184, 0x0007, 0x0002, 0x6b19,
+ 0x6b19, 0x6b19, 0x6b19, 0x6b19, 0x6b2f, 0x6b3d, 0x6b19, 0x7003,
+ 0x0003, 0x2009, 0x1868, 0x210c, 0x9184, 0xff00, 0x8007, 0x9005,
+ 0x1110, 0x2001, 0x0002, 0x7006, 0x0018, 0x7003, 0x0005, 0x0c88,
+ 0x00ee, 0x001e, 0x0005, 0x00e6, 0x2071, 0x0050, 0x684c, 0x9005,
+ 0x1150, 0x00e6, 0x2071, 0x1910, 0x7028, 0xc085, 0x702a, 0x00ee,
+ 0x9085, 0x0001, 0x0488, 0x6844, 0x9005, 0x0158, 0x080c, 0x78b2,
+ 0x6a60, 0x9200, 0x7002, 0x6864, 0x9101, 0x7006, 0x9006, 0x7012,
+ 0x7016, 0x6860, 0x7002, 0x6864, 0x7006, 0x6868, 0x700a, 0x686c,
+ 0x700e, 0x6844, 0x9005, 0x1110, 0x7012, 0x7016, 0x684c, 0x701a,
+ 0x701c, 0x9085, 0x0040, 0x701e, 0x7037, 0x0019, 0x702b, 0x0001,
+ 0x00e6, 0x2071, 0x1910, 0x7028, 0xc084, 0x702a, 0x7007, 0x0001,
+ 0x700b, 0x0000, 0x00ee, 0x9006, 0x00ee, 0x0005, 0x00e6, 0x0026,
+ 0x2071, 0x1947, 0x7000, 0x9015, 0x0904, 0x6df3, 0x9286, 0x0003,
+ 0x0904, 0x6c83, 0x9286, 0x0005, 0x0904, 0x6c83, 0x2071, 0x1877,
+ 0xa87c, 0x9005, 0x0904, 0x6be4, 0x7140, 0xa868, 0x9102, 0x0a04,
+ 0x6df3, 0xa878, 0xd084, 0x15d8, 0xa853, 0x0019, 0x2001, 0x8023,
+ 0xa84e, 0x2071, 0x1910, 0x701c, 0x9005, 0x1904, 0x6f8a, 0x0e04,
+ 0x6ff8, 0x2071, 0x0000, 0xa850, 0x7032, 0xa84c, 0x7082, 0xa870,
+ 0x7086, 0xa86c, 0x708a, 0xa880, 0x708e, 0x7036, 0x0146, 0x01d6,
+ 0x0136, 0x01c6, 0x0156, 0x20e9, 0x0000, 0x20a1, 0x002a, 0xa868,
+ 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0021, 0x2098, 0x4003,
+ 0x015e, 0x01ce, 0x013e, 0x01de, 0x014e, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x0804, 0x6c66, 0xa853,
+ 0x001b, 0x2001, 0x8027, 0x0820, 0x7004, 0xd08c, 0x1904, 0x6df3,
+ 0xa853, 0x001a, 0x2001, 0x8024, 0x0804, 0x6ba8, 0x00e6, 0x0026,
+ 0x2071, 0x1947, 0x7000, 0x9015, 0x0904, 0x6df3, 0x9286, 0x0003,
+ 0x0904, 0x6c83, 0x9286, 0x0005, 0x0904, 0x6c83, 0xa84f, 0x8022,
+ 0xa853, 0x0018, 0x0804, 0x6c4b, 0xa868, 0xd0fc, 0x11d8, 0x00e6,
+ 0x0026, 0x2001, 0x1947, 0x2004, 0x9005, 0x0904, 0x6df3, 0xa87c,
+ 0xd0bc, 0x1904, 0x6df3, 0xa978, 0xa874, 0x9105, 0x1904, 0x6df3,
+ 0x2001, 0x1947, 0x2004, 0x0002, 0x6df3, 0x6c47, 0x6c83, 0x6c83,
+ 0x6df3, 0x6c83, 0x0005, 0xa868, 0xd0fc, 0x1500, 0x00e6, 0x0026,
+ 0x2009, 0x1947, 0x210c, 0x81ff, 0x0904, 0x6df3, 0xa87c, 0xd0cc,
+ 0x0904, 0x6df3, 0xa880, 0x9084, 0x00ff, 0x9086, 0x0001, 0x1904,
+ 0x6df3, 0x9186, 0x0003, 0x0904, 0x6c83, 0x9186, 0x0005, 0x0904,
+ 0x6c83, 0xa84f, 0x8021, 0xa853, 0x0017, 0x0028, 0x0005, 0xa84f,
+ 0x8020, 0xa853, 0x0016, 0x2071, 0x1910, 0x701c, 0x9005, 0x1904,
+ 0x6f8a, 0x0e04, 0x6ff8, 0x2071, 0x0000, 0xa84c, 0x7082, 0xa850,
+ 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0x2091, 0x4080,
+ 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x2071, 0x1800,
+ 0x2011, 0x0001, 0xa804, 0x900d, 0x702c, 0x1158, 0xa802, 0x2900,
+ 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x85ce, 0x002e, 0x00ee,
+ 0x0005, 0x0096, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x009e, 0x0c58, 0xa84f, 0x0000, 0x00f6, 0x2079, 0x0050,
+ 0x2071, 0x1910, 0xa803, 0x0000, 0x7010, 0x9005, 0x1904, 0x6d78,
+ 0x782c, 0x908c, 0x0780, 0x190c, 0x7146, 0x8004, 0x8004, 0x8004,
+ 0x9084, 0x0003, 0x0002, 0x6ca1, 0x6d78, 0x6cc6, 0x6d13, 0x080c,
+ 0x0d7d, 0x2071, 0x1800, 0x2900, 0x7822, 0xa804, 0x900d, 0x1170,
+ 0x2071, 0x1a02, 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004,
+ 0x8005, 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c,
+ 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e,
+ 0x70c0, 0x9200, 0x70c2, 0x080c, 0x85ce, 0x0c10, 0x2071, 0x1800,
+ 0x2900, 0x7822, 0xa804, 0x900d, 0x15a8, 0x7824, 0x00e6, 0x2071,
+ 0x0040, 0x712c, 0xd19c, 0x1170, 0x2009, 0x1830, 0x210c, 0x918a,
+ 0x0020, 0x0240, 0x7022, 0x2001, 0x1dc0, 0x200c, 0x8108, 0x2102,
+ 0x00ee, 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e,
+ 0x70c0, 0x8000, 0x70c2, 0x080c, 0x85ce, 0x782c, 0x9094, 0x0780,
+ 0x190c, 0x7146, 0xd0a4, 0x19c8, 0x2071, 0x1a02, 0x703c, 0x9005,
+ 0x1328, 0x2001, 0x1948, 0x2004, 0x8005, 0x703e, 0x00fe, 0x002e,
+ 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210,
+ 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c,
+ 0x85ce, 0x0804, 0x6ccd, 0x0096, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2,
+ 0x080c, 0x85ce, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4,
+ 0x1d60, 0x00ee, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd09c,
+ 0x11a0, 0x009e, 0x2900, 0x7822, 0xa804, 0x900d, 0x1560, 0x2071,
+ 0x1a02, 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004, 0x8005,
+ 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x009e, 0x2908, 0x7010,
+ 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110, 0xa902, 0x0008,
+ 0x711e, 0x2148, 0xa804, 0x900d, 0x1170, 0x2071, 0x1a02, 0x703c,
+ 0x9005, 0x1328, 0x2001, 0x1948, 0x2004, 0x8005, 0x703e, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0,
+ 0x9200, 0x70c2, 0x080c, 0x85ce, 0x00fe, 0x002e, 0x00ee, 0x0005,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1904, 0x6dcd,
+ 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd09c, 0x1198, 0x701c,
+ 0x904d, 0x0180, 0x7010, 0x8001, 0x7012, 0x1108, 0x701a, 0xa800,
+ 0x701e, 0x2900, 0x7822, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146,
+ 0xd09c, 0x0d68, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4,
+ 0x01b0, 0x00e6, 0x7824, 0x2048, 0x2071, 0x1800, 0x702c, 0xa802,
+ 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c, 0x85ce, 0x782c,
+ 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4, 0x1d60, 0x00ee, 0x2071,
+ 0x1a02, 0x703c, 0x9005, 0x1328, 0x2001, 0x1948, 0x2004, 0x8005,
+ 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x85ce, 0x00ee,
+ 0x0804, 0x6d88, 0xa868, 0xd0fc, 0x1560, 0x0096, 0xa804, 0xa807,
+ 0x0000, 0x904d, 0x190c, 0x0ff9, 0x009e, 0x0018, 0xa868, 0xd0fc,
+ 0x1500, 0x00e6, 0x0026, 0xa84f, 0x0000, 0x00f6, 0x2079, 0x0050,
+ 0x2071, 0x1910, 0xa803, 0x0000, 0x7010, 0x9005, 0x1904, 0x6f08,
+ 0x782c, 0x908c, 0x0780, 0x190c, 0x7146, 0x8004, 0x8004, 0x8004,
+ 0x9084, 0x0003, 0x0002, 0x6e12, 0x6f08, 0x6e2d, 0x6e9b, 0x080c,
+ 0x0d7d, 0x0005, 0x2071, 0x1800, 0x2900, 0x7822, 0xa804, 0x900d,
+ 0x1120, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0,
+ 0x9200, 0x70c2, 0x080c, 0x85ce, 0x0c60, 0x2071, 0x1800, 0x2900,
+ 0x7822, 0xa804, 0x900d, 0x1904, 0x6e8a, 0x7830, 0xd0dc, 0x1120,
+ 0x00fe, 0x002e, 0x00ee, 0x0005, 0x7824, 0x00e6, 0x2071, 0x0040,
+ 0x712c, 0xd19c, 0x1170, 0x2009, 0x1830, 0x210c, 0x918a, 0x0020,
+ 0x0240, 0x7022, 0x2001, 0x1dc0, 0x200c, 0x8108, 0x2102, 0x00ee,
+ 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0,
+ 0x8000, 0x70c2, 0x080c, 0x85ce, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x7146, 0xd0a4, 0x19c8, 0x0e04, 0x6e81, 0x7838, 0x7938, 0x910e,
+ 0x1de0, 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de,
+ 0x2001, 0x1921, 0x200c, 0xc184, 0x2102, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x00fe, 0x002e, 0x00ee,
+ 0x0005, 0x2001, 0x1921, 0x200c, 0xc185, 0x2102, 0x00fe, 0x002e,
+ 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210,
+ 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c,
+ 0x85ce, 0x0804, 0x6e3c, 0x0096, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2,
+ 0x080c, 0x85ce, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4,
+ 0x1d60, 0x00ee, 0x0e04, 0x6edb, 0x7838, 0x7938, 0x910e, 0x1de0,
+ 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x7044,
+ 0xc084, 0x7046, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11ee, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd09c,
+ 0x1170, 0x009e, 0x2900, 0x7822, 0xa804, 0x900d, 0x11e0, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x7044, 0xc085, 0x7046, 0x0c58, 0x009e,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1120, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0,
+ 0x9200, 0x70c2, 0x080c, 0x85ce, 0x00fe, 0x002e, 0x00ee, 0x0005,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1904, 0x6f75,
+ 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd09c, 0x11b0, 0x701c,
+ 0x904d, 0x0198, 0xa84c, 0x9005, 0x1180, 0x7010, 0x8001, 0x7012,
+ 0x1108, 0x701a, 0xa800, 0x701e, 0x2900, 0x7822, 0x782c, 0x9094,
+ 0x0780, 0x190c, 0x7146, 0xd09c, 0x0d50, 0x782c, 0x9094, 0x0780,
+ 0x190c, 0x7146, 0xd0a4, 0x05a8, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2,
+ 0x080c, 0x85ce, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4,
+ 0x1d60, 0x00ee, 0x0e04, 0x6f6e, 0x7838, 0x7938, 0x910e, 0x1de0,
+ 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x7044,
+ 0xc084, 0x7046, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11ee, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x7044, 0xc085,
+ 0x7046, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x85ce, 0x00ee,
+ 0x0804, 0x6f18, 0x2071, 0x1910, 0xa803, 0x0000, 0x2908, 0x7010,
+ 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110, 0xa902, 0x0008,
+ 0x711e, 0x2148, 0xa804, 0x900d, 0x1128, 0x1e04, 0x6fb5, 0x002e,
+ 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148, 0xa904,
+ 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70c0, 0x9200,
+ 0x70c2, 0x080c, 0x85ce, 0x0e04, 0x6f9f, 0x2071, 0x1910, 0x701c,
+ 0x2048, 0xa84c, 0x900d, 0x0d18, 0x2071, 0x0000, 0x7182, 0xa850,
+ 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0xa850, 0x9082,
+ 0x0019, 0x1278, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11ee, 0x2071, 0x1910, 0x080c, 0x7132, 0x002e, 0x00ee,
+ 0x0005, 0xa850, 0x9082, 0x001c, 0x1e68, 0xa880, 0x708e, 0x7036,
+ 0x0146, 0x01d6, 0x0136, 0x01c6, 0x0156, 0x20e9, 0x0000, 0x20a1,
+ 0x002a, 0xa868, 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0021,
+ 0x2098, 0x4003, 0x015e, 0x01ce, 0x013e, 0x01de, 0x014e, 0x0890,
+ 0x2071, 0x1910, 0xa803, 0x0000, 0x2908, 0x7010, 0x8000, 0x7012,
+ 0x7018, 0x904d, 0x711a, 0x0110, 0xa902, 0x0008, 0x711e, 0x2148,
+ 0xa804, 0x900d, 0x1118, 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800,
+ 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x702e, 0x70c0, 0x9200, 0x70c2, 0x080c, 0x85ce, 0x002e,
+ 0x00ee, 0x0005, 0x0006, 0xa87c, 0x0006, 0xa867, 0x0103, 0x20a9,
+ 0x001c, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x001d, 0x20a0, 0x9006,
+ 0x4004, 0x000e, 0x9084, 0x00ff, 0xa87e, 0x000e, 0xa87a, 0xa982,
+ 0x0005, 0x2071, 0x1910, 0x7004, 0x0002, 0x7045, 0x7046, 0x7131,
+ 0x7046, 0x7043, 0x7131, 0x080c, 0x0d7d, 0x0005, 0x2001, 0x1947,
+ 0x2004, 0x0002, 0x7050, 0x7050, 0x70ca, 0x70cb, 0x7050, 0x70cb,
+ 0x0126, 0x2091, 0x8000, 0x1e0c, 0x7151, 0x701c, 0x904d, 0x0508,
+ 0xa84c, 0x9005, 0x0904, 0x709b, 0x0e04, 0x7079, 0xa94c, 0x2071,
+ 0x0000, 0x7182, 0xa850, 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870,
+ 0x708a, 0xa850, 0x9082, 0x0019, 0x1278, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x2071, 0x1910, 0x080c,
+ 0x7132, 0x012e, 0x0804, 0x70c9, 0xa850, 0x9082, 0x001c, 0x1e68,
+ 0xa880, 0x708e, 0x7036, 0x0146, 0x01d6, 0x0136, 0x01c6, 0x0156,
+ 0x20e9, 0x0000, 0x20a1, 0x002a, 0xa868, 0x20a8, 0xa860, 0x20e0,
+ 0xa85c, 0x9080, 0x0021, 0x2098, 0x4003, 0x015e, 0x01ce, 0x013e,
+ 0x01de, 0x014e, 0x0890, 0x2001, 0x005b, 0x2004, 0x9094, 0x0780,
+ 0x190c, 0x7146, 0xd09c, 0x2071, 0x1910, 0x1510, 0x2071, 0x1910,
+ 0x700f, 0x0001, 0xa964, 0x9184, 0x00ff, 0x9086, 0x0003, 0x1130,
+ 0x810f, 0x918c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x2900, 0x00d6,
+ 0x2069, 0x0050, 0x6822, 0x00de, 0x2071, 0x1910, 0x701c, 0x2048,
+ 0x7010, 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a,
+ 0x012e, 0x0005, 0x0005, 0x00d6, 0x2008, 0x2069, 0x1a02, 0x683c,
+ 0x9005, 0x0760, 0x0158, 0x9186, 0x0003, 0x0540, 0x2001, 0x1815,
+ 0x2004, 0x2009, 0x1b50, 0x210c, 0x9102, 0x1500, 0x0126, 0x2091,
+ 0x8000, 0x2069, 0x0050, 0x693c, 0x6838, 0x9106, 0x0190, 0x0e04,
+ 0x70fd, 0x2069, 0x0000, 0x6837, 0x8040, 0x6833, 0x0012, 0x6883,
+ 0x8040, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c,
+ 0x11ee, 0x2069, 0x1a02, 0x683f, 0xffff, 0x012e, 0x00de, 0x0126,
+ 0x2091, 0x8000, 0x1e0c, 0x71b7, 0x701c, 0x904d, 0x0540, 0x2001,
+ 0x005b, 0x2004, 0x9094, 0x0780, 0x15c9, 0xd09c, 0x1500, 0x2071,
+ 0x1910, 0x700f, 0x0001, 0xa964, 0x9184, 0x00ff, 0x9086, 0x0003,
+ 0x1130, 0x810f, 0x918c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x2900,
+ 0x00d6, 0x2069, 0x0050, 0x6822, 0x00de, 0x701c, 0x2048, 0x7010,
+ 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a, 0x012e,
+ 0x0005, 0x0005, 0x0126, 0x2091, 0x8000, 0x701c, 0x904d, 0x0160,
+ 0x7010, 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a,
+ 0x012e, 0x080c, 0x1079, 0x0005, 0x012e, 0x0005, 0x2091, 0x8000,
+ 0x0e04, 0x7148, 0x0006, 0x0016, 0x2001, 0x8004, 0x0006, 0x0804,
+ 0x0d86, 0x0096, 0x00f6, 0x2079, 0x0050, 0x7044, 0xd084, 0x01c0,
+ 0xc084, 0x7046, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069,
+ 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11ee, 0x00fe, 0x009e, 0x0005,
+ 0x782c, 0x9094, 0x0780, 0x1991, 0xd0a4, 0x0db8, 0x00e6, 0x2071,
+ 0x1800, 0x7824, 0x00e6, 0x2071, 0x0040, 0x712c, 0xd19c, 0x1170,
+ 0x2009, 0x1830, 0x210c, 0x918a, 0x0020, 0x0240, 0x7022, 0x2001,
+ 0x1dc0, 0x200c, 0x8108, 0x2102, 0x00ee, 0x0058, 0x00ee, 0x2048,
+ 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c,
+ 0x85ce, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4, 0x19c8,
+ 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069, 0x0000, 0x6836,
+ 0x6833, 0x0013, 0x00de, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004,
+ 0xd084, 0x190c, 0x11ee, 0x00ee, 0x00fe, 0x009e, 0x0005, 0x00f6,
+ 0x2079, 0x0050, 0x7044, 0xd084, 0x01b8, 0xc084, 0x7046, 0x7838,
+ 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833,
+ 0x0013, 0x00de, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11ee, 0x00fe, 0x0005, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x7146, 0xd0a4, 0x0db8, 0x00e6, 0x2071, 0x1800, 0x7824, 0x2048,
+ 0x702c, 0xa802, 0x2900, 0x702e, 0x70c0, 0x8000, 0x70c2, 0x080c,
+ 0x85ce, 0x782c, 0x9094, 0x0780, 0x190c, 0x7146, 0xd0a4, 0x1d70,
+ 0x00d6, 0x2069, 0x0050, 0x693c, 0x2069, 0x1947, 0x6808, 0x690a,
+ 0x2069, 0x1a02, 0x9102, 0x1118, 0x683c, 0x9005, 0x1328, 0x2001,
+ 0x1948, 0x200c, 0x810d, 0x693e, 0x00de, 0x00ee, 0x00fe, 0x0005,
+ 0x7098, 0x908a, 0x0029, 0x1a0c, 0x0d7d, 0x9082, 0x001d, 0x003b,
+ 0x0026, 0x2011, 0x1e00, 0x080c, 0x2ab4, 0x002e, 0x0005, 0x72e3,
+ 0x7269, 0x7285, 0x72af, 0x72d2, 0x7312, 0x7324, 0x7285, 0x72fa,
+ 0x7224, 0x7252, 0x7223, 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804,
+ 0x9005, 0x1180, 0x6808, 0x9005, 0x1518, 0x709b, 0x0028, 0x2069,
+ 0x198e, 0x2d04, 0x7002, 0x080c, 0x767e, 0x6028, 0x9085, 0x0600,
+ 0x602a, 0x00b0, 0x709b, 0x0028, 0x2069, 0x198e, 0x2d04, 0x7002,
+ 0x6028, 0x9085, 0x0600, 0x602a, 0x00e6, 0x0036, 0x0046, 0x0056,
+ 0x2071, 0x1a6a, 0x080c, 0x1b10, 0x005e, 0x004e, 0x003e, 0x00ee,
+ 0x00de, 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0x9005, 0x1178,
+ 0x6808, 0x9005, 0x1160, 0x709b, 0x0028, 0x2069, 0x198e, 0x2d04,
+ 0x7002, 0x080c, 0x7721, 0x6028, 0x9085, 0x0600, 0x602a, 0x00de,
+ 0x0005, 0x0006, 0x2001, 0x0090, 0x080c, 0x2a7a, 0x000e, 0x6124,
+ 0xd1e4, 0x1190, 0x080c, 0x7395, 0xd1d4, 0x1160, 0xd1dc, 0x1138,
+ 0xd1cc, 0x0150, 0x709b, 0x0020, 0x080c, 0x7395, 0x0028, 0x709b,
+ 0x001d, 0x0010, 0x709b, 0x001f, 0x0005, 0x2001, 0x0088, 0x080c,
+ 0x2a7a, 0x6124, 0xd1cc, 0x11e8, 0xd1dc, 0x11c0, 0xd1e4, 0x1198,
+ 0x9184, 0x1e00, 0x11d8, 0x080c, 0x1b35, 0x60e3, 0x0001, 0x600c,
+ 0xc0b4, 0x600e, 0x080c, 0x7569, 0x2001, 0x0080, 0x080c, 0x2a7a,
+ 0x709b, 0x0028, 0x0058, 0x709b, 0x001e, 0x0040, 0x709b, 0x001d,
+ 0x0028, 0x709b, 0x0020, 0x0010, 0x709b, 0x001f, 0x0005, 0x080c,
+ 0x1b35, 0x60e3, 0x0001, 0x600c, 0xc0b4, 0x600e, 0x080c, 0x7569,
+ 0x2001, 0x0080, 0x080c, 0x2a7a, 0x6124, 0xd1d4, 0x1180, 0xd1dc,
+ 0x1158, 0xd1e4, 0x1130, 0x9184, 0x1e00, 0x1158, 0x709b, 0x0028,
+ 0x0040, 0x709b, 0x001e, 0x0028, 0x709b, 0x001d, 0x0010, 0x709b,
+ 0x001f, 0x0005, 0x2001, 0x00a0, 0x080c, 0x2a7a, 0x6124, 0xd1dc,
+ 0x1138, 0xd1e4, 0x0138, 0x080c, 0x1b35, 0x709b, 0x001e, 0x0010,
+ 0x709b, 0x001d, 0x0005, 0x080c, 0x741e, 0x6124, 0xd1dc, 0x1188,
+ 0x080c, 0x7395, 0x0016, 0x080c, 0x1b35, 0x001e, 0xd1d4, 0x1128,
+ 0xd1e4, 0x0138, 0x709b, 0x001e, 0x0020, 0x709b, 0x001f, 0x080c,
+ 0x7395, 0x0005, 0x0006, 0x2001, 0x00a0, 0x080c, 0x2a7a, 0x000e,
+ 0x6124, 0xd1d4, 0x1160, 0xd1cc, 0x1150, 0xd1dc, 0x1128, 0xd1e4,
+ 0x0140, 0x709b, 0x001e, 0x0028, 0x709b, 0x001d, 0x0010, 0x709b,
+ 0x0021, 0x0005, 0x080c, 0x741e, 0x6124, 0xd1d4, 0x1150, 0xd1dc,
+ 0x1128, 0xd1e4, 0x0140, 0x709b, 0x001e, 0x0028, 0x709b, 0x001d,
+ 0x0010, 0x709b, 0x001f, 0x0005, 0x0006, 0x2001, 0x0090, 0x080c,
+ 0x2a7a, 0x000e, 0x6124, 0xd1d4, 0x1178, 0xd1cc, 0x1150, 0xd1dc,
+ 0x1128, 0xd1e4, 0x0158, 0x709b, 0x001e, 0x0040, 0x709b, 0x001d,
+ 0x0028, 0x709b, 0x0020, 0x0010, 0x709b, 0x001f, 0x0005, 0x0016,
+ 0x00c6, 0x00d6, 0x00e6, 0x0126, 0x2061, 0x0100, 0x2069, 0x0140,
+ 0x2071, 0x1800, 0x2091, 0x8000, 0x080c, 0x753d, 0x11f8, 0x2001,
+ 0x180c, 0x200c, 0xd1b4, 0x01d0, 0xc1b4, 0x2102, 0x0026, 0x2011,
+ 0x0200, 0x080c, 0x2ab4, 0x002e, 0x080c, 0x2a60, 0x6024, 0xd0cc,
+ 0x0148, 0x2001, 0x00a0, 0x080c, 0x2a7a, 0x080c, 0x7840, 0x080c,
+ 0x6092, 0x0428, 0x6028, 0xc0cd, 0x602a, 0x0408, 0x080c, 0x7557,
+ 0x0150, 0x080c, 0x754e, 0x1138, 0x2001, 0x0001, 0x080c, 0x2606,
+ 0x080c, 0x7511, 0x00a0, 0x080c, 0x741b, 0x0178, 0x2001, 0x0001,
+ 0x080c, 0x2606, 0x7098, 0x9086, 0x001e, 0x0120, 0x7098, 0x9086,
+ 0x0022, 0x1118, 0x709b, 0x0025, 0x0010, 0x709b, 0x0021, 0x012e,
+ 0x00ee, 0x00de, 0x00ce, 0x001e, 0x0005, 0x0026, 0x2011, 0x73a6,
+ 0x080c, 0x87d4, 0x002e, 0x0016, 0x0026, 0x2009, 0x0064, 0x2011,
+ 0x73a6, 0x080c, 0x87cb, 0x002e, 0x001e, 0x0005, 0x00e6, 0x00f6,
+ 0x0016, 0x080c, 0x9ed4, 0x2071, 0x1800, 0x080c, 0x733f, 0x001e,
+ 0x00fe, 0x00ee, 0x0005, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6,
+ 0x00e6, 0x00f6, 0x0126, 0x080c, 0x9ed4, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x2071, 0x1800, 0x2091, 0x8000, 0x6028, 0xc09c, 0x602a,
+ 0x080c, 0xa91e, 0x2011, 0x0003, 0x080c, 0xa243, 0x2011, 0x0002,
+ 0x080c, 0xa24d, 0x080c, 0xa138, 0x080c, 0x8780, 0x0036, 0x901e,
+ 0x080c, 0xa1b8, 0x003e, 0x080c, 0xa93a, 0x60e3, 0x0000, 0x080c,
+ 0xe882, 0x080c, 0xe89d, 0x2009, 0x0004, 0x080c, 0x2a66, 0x080c,
+ 0x297c, 0x2001, 0x1800, 0x2003, 0x0004, 0x2011, 0x0008, 0x080c,
+ 0x2ab4, 0x2011, 0x73a6, 0x080c, 0x87d4, 0x080c, 0x7557, 0x0118,
+ 0x9006, 0x080c, 0x2a7a, 0x080c, 0x0bc3, 0x2001, 0x0001, 0x080c,
+ 0x2606, 0x012e, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e,
+ 0x001e, 0x0005, 0x0026, 0x00e6, 0x2011, 0x73b3, 0x2071, 0x1a02,
+ 0x701c, 0x9206, 0x1118, 0x7018, 0x9005, 0x0110, 0x9085, 0x0001,
+ 0x00ee, 0x002e, 0x0005, 0x6020, 0xd09c, 0x0005, 0x6800, 0x9084,
+ 0xfffe, 0x9086, 0x00c0, 0x01b8, 0x2001, 0x00c0, 0x080c, 0x2a7a,
+ 0x0156, 0x20a9, 0x002d, 0x1d04, 0x742b, 0x2091, 0x6000, 0x1f04,
+ 0x742b, 0x015e, 0x00d6, 0x2069, 0x1800, 0x689c, 0x8001, 0x0220,
+ 0x0118, 0x689e, 0x00de, 0x0005, 0x689f, 0x0014, 0x68ec, 0xd0dc,
+ 0x0dc8, 0x6800, 0x9086, 0x0001, 0x1da8, 0x080c, 0x87e0, 0x0c90,
+ 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071,
+ 0x1800, 0x080c, 0x784f, 0x2001, 0x196c, 0x2003, 0x0000, 0x9006,
+ 0x709a, 0x60e2, 0x6886, 0x080c, 0x26d5, 0x9006, 0x080c, 0x2a7a,
+ 0x080c, 0x5f4d, 0x0026, 0x2011, 0xffff, 0x080c, 0x2ab4, 0x002e,
+ 0x602b, 0x182c, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x2001,
+ 0x197c, 0x200c, 0x9186, 0x0000, 0x0158, 0x9186, 0x0001, 0x0158,
+ 0x9186, 0x0002, 0x0158, 0x9186, 0x0003, 0x0158, 0x0804, 0x7501,
+ 0x709b, 0x0022, 0x0040, 0x709b, 0x0021, 0x0028, 0x709b, 0x0023,
+ 0x0010, 0x709b, 0x0024, 0x60e3, 0x0000, 0x6887, 0x0001, 0x2001,
+ 0x0001, 0x080c, 0x26d5, 0x080c, 0xa91e, 0x0026, 0x080c, 0xabe9,
+ 0x002e, 0x080c, 0xa93a, 0x7000, 0x908e, 0x0004, 0x0118, 0x602b,
+ 0x0028, 0x0010, 0x602b, 0x0020, 0x0156, 0x0126, 0x2091, 0x8000,
+ 0x20a9, 0x0005, 0x6024, 0xd0ac, 0x0150, 0x012e, 0x015e, 0x080c,
+ 0xd09b, 0x0118, 0x9006, 0x080c, 0x2aa4, 0x0804, 0x750d, 0x6800,
+ 0x9084, 0x00a1, 0xc0bd, 0x6802, 0x080c, 0x2a60, 0x6904, 0xd1d4,
+ 0x1140, 0x2001, 0x0100, 0x080c, 0x2a7a, 0x1f04, 0x74b2, 0x080c,
+ 0x7594, 0x012e, 0x015e, 0x080c, 0x754e, 0x0170, 0x6044, 0x9005,
+ 0x0130, 0x080c, 0x7594, 0x9006, 0x8001, 0x1df0, 0x0028, 0x6804,
+ 0xd0d4, 0x1110, 0x080c, 0x7594, 0x080c, 0xd09b, 0x0118, 0x9006,
+ 0x080c, 0x2aa4, 0x0016, 0x0026, 0x7000, 0x908e, 0x0004, 0x0130,
+ 0x2009, 0x00c8, 0x2011, 0x73b3, 0x080c, 0x8792, 0x002e, 0x001e,
+ 0x080c, 0x85c5, 0x7034, 0xc085, 0x7036, 0x2001, 0x197c, 0x2003,
+ 0x0004, 0x080c, 0x7208, 0x080c, 0x754e, 0x0138, 0x6804, 0xd0d4,
+ 0x1120, 0xd0dc, 0x1100, 0x080c, 0x7845, 0x00ee, 0x00de, 0x00ce,
+ 0x0005, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140,
+ 0x2071, 0x1800, 0x080c, 0x85dc, 0x080c, 0x85ce, 0x080c, 0x784f,
+ 0x2001, 0x196c, 0x2003, 0x0000, 0x9006, 0x709a, 0x60e2, 0x6886,
+ 0x080c, 0x26d5, 0x9006, 0x080c, 0x2a7a, 0x6043, 0x0090, 0x6043,
+ 0x0010, 0x0026, 0x2011, 0xffff, 0x080c, 0x2ab4, 0x002e, 0x602b,
+ 0x182c, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0006, 0x2001, 0x197b,
+ 0x2004, 0x9086, 0xaaaa, 0x000e, 0x0005, 0x0006, 0x080c, 0x5742,
+ 0x9084, 0x0030, 0x9086, 0x0000, 0x000e, 0x0005, 0x0006, 0x080c,
+ 0x5742, 0x9084, 0x0030, 0x9086, 0x0030, 0x000e, 0x0005, 0x0006,
+ 0x080c, 0x5742, 0x9084, 0x0030, 0x9086, 0x0010, 0x000e, 0x0005,
+ 0x0006, 0x080c, 0x5742, 0x9084, 0x0030, 0x9086, 0x0020, 0x000e,
+ 0x0005, 0x0036, 0x0016, 0x2001, 0x180c, 0x2004, 0x908c, 0x0013,
+ 0x0180, 0x0020, 0x080c, 0x26f5, 0x900e, 0x0028, 0x080c, 0x6ad5,
+ 0x1dc8, 0x2009, 0x0002, 0x2019, 0x0028, 0x080c, 0x3205, 0x9006,
+ 0x0019, 0x001e, 0x003e, 0x0005, 0x00e6, 0x2071, 0x180c, 0x2e04,
+ 0x0130, 0x080c, 0xd094, 0x1128, 0x9085, 0x0010, 0x0010, 0x9084,
+ 0xffef, 0x2072, 0x00ee, 0x0005, 0x6050, 0x0006, 0x60ec, 0x0006,
+ 0x600c, 0x0006, 0x6004, 0x0006, 0x6028, 0x0006, 0x080c, 0x2ad7,
+ 0x080c, 0x2b0a, 0x602f, 0x0100, 0x602f, 0x0000, 0x602f, 0x0040,
+ 0x602f, 0x0000, 0x20a9, 0x0002, 0x080c, 0x2a41, 0x0026, 0x2011,
+ 0x0040, 0x080c, 0x2ab4, 0x002e, 0x000e, 0x602a, 0x000e, 0x6006,
+ 0x000e, 0x600e, 0x000e, 0x60ee, 0x60e3, 0x0000, 0x6887, 0x0001,
+ 0x2001, 0x0001, 0x080c, 0x26d5, 0x2001, 0x00a0, 0x0006, 0x080c,
+ 0xd09b, 0x000e, 0x0130, 0x080c, 0x2a98, 0x9006, 0x080c, 0x2aa4,
+ 0x0010, 0x080c, 0x2a7a, 0x000e, 0x6052, 0x6050, 0x0006, 0xc0e5,
+ 0x6052, 0x00f6, 0x2079, 0x0100, 0x080c, 0x29ed, 0x00fe, 0x000e,
+ 0x6052, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x080c,
+ 0xa97c, 0x0158, 0x2001, 0x0386, 0x2004, 0xd0b4, 0x1130, 0x2001,
+ 0x0016, 0x080c, 0xa90f, 0x0804, 0x7670, 0x2001, 0x180c, 0x200c,
+ 0xc1c4, 0x2102, 0x6028, 0x9084, 0xe1ff, 0x602a, 0x2011, 0x0200,
+ 0x080c, 0x2ab4, 0x2001, 0x0090, 0x080c, 0x2a7a, 0x20a9, 0x0366,
+ 0x6024, 0xd0cc, 0x1558, 0x1d04, 0x7610, 0x2091, 0x6000, 0x1f04,
+ 0x7610, 0x080c, 0xa91e, 0x2011, 0x0003, 0x080c, 0xa243, 0x2011,
+ 0x0002, 0x080c, 0xa24d, 0x080c, 0xa138, 0x901e, 0x080c, 0xa1b8,
+ 0x2001, 0x0386, 0x2003, 0x7000, 0x080c, 0xa93a, 0x2001, 0x00a0,
+ 0x080c, 0x2a7a, 0x080c, 0x7840, 0x080c, 0x6092, 0x080c, 0xd09b,
+ 0x0110, 0x080c, 0x0ce9, 0x9085, 0x0001, 0x04c0, 0x080c, 0x1b35,
+ 0x60e3, 0x0000, 0x2001, 0x196c, 0x2004, 0x080c, 0x26d5, 0x60e2,
+ 0x2001, 0x0080, 0x080c, 0x2a7a, 0x20a9, 0x0366, 0x2011, 0x1e00,
+ 0x080c, 0x2ab4, 0x2009, 0x1e00, 0x080c, 0x2a60, 0x6024, 0x910c,
+ 0x0140, 0x1d04, 0x764e, 0x2091, 0x6000, 0x1f04, 0x764e, 0x0804,
+ 0x7619, 0x2001, 0x0386, 0x2003, 0x7000, 0x6028, 0x9085, 0x1e00,
+ 0x602a, 0x70b4, 0x9005, 0x1118, 0x6887, 0x0001, 0x0008, 0x6886,
+ 0x080c, 0xd09b, 0x0110, 0x080c, 0x0ce9, 0x9006, 0x00ee, 0x00de,
+ 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016,
+ 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2071,
+ 0x1800, 0x7000, 0x9086, 0x0003, 0x1168, 0x2001, 0x020b, 0x2004,
+ 0x9084, 0x5540, 0x9086, 0x5540, 0x1128, 0x2069, 0x1a76, 0x2d04,
+ 0x8000, 0x206a, 0x2069, 0x0140, 0x6020, 0x9084, 0x00c0, 0x0120,
+ 0x6884, 0x9005, 0x1904, 0x76e7, 0x2001, 0x0088, 0x080c, 0x2a7a,
+ 0x9006, 0x60e2, 0x6886, 0x080c, 0x26d5, 0x2069, 0x0200, 0x6804,
+ 0x9005, 0x1118, 0x6808, 0x9005, 0x01d0, 0x6028, 0x9084, 0xfbff,
+ 0x602a, 0x2011, 0x0400, 0x080c, 0x2ab4, 0x2069, 0x198e, 0x7000,
+ 0x206a, 0x709b, 0x0026, 0x7003, 0x0001, 0x20a9, 0x0002, 0x1d04,
+ 0x76c7, 0x2091, 0x6000, 0x1f04, 0x76c7, 0x0804, 0x7719, 0x2069,
+ 0x0140, 0x20a9, 0x0384, 0x2011, 0x1e00, 0x080c, 0x2ab4, 0x2009,
+ 0x1e00, 0x080c, 0x2a60, 0x6024, 0x910c, 0x0528, 0x9084, 0x1a00,
+ 0x1510, 0x1d04, 0x76d3, 0x2091, 0x6000, 0x1f04, 0x76d3, 0x080c,
+ 0xa91e, 0x2011, 0x0003, 0x080c, 0xa243, 0x2011, 0x0002, 0x080c,
+ 0xa24d, 0x080c, 0xa138, 0x901e, 0x080c, 0xa1b8, 0x080c, 0xa93a,
+ 0x2001, 0x00a0, 0x080c, 0x2a7a, 0x080c, 0x7840, 0x080c, 0x6092,
+ 0x9085, 0x0001, 0x00b0, 0x2001, 0x0080, 0x080c, 0x2a7a, 0x2069,
+ 0x0140, 0x60e3, 0x0000, 0x70b4, 0x9005, 0x1118, 0x6887, 0x0001,
+ 0x0008, 0x6886, 0x2001, 0x196c, 0x2004, 0x080c, 0x26d5, 0x60e2,
+ 0x9006, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e,
+ 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2071, 0x1800, 0x6020, 0x9084, 0x00c0, 0x01e8,
+ 0x080c, 0xa91e, 0x2011, 0x0003, 0x080c, 0xa243, 0x2011, 0x0002,
+ 0x080c, 0xa24d, 0x080c, 0xa138, 0x901e, 0x080c, 0xa1b8, 0x080c,
+ 0xa93a, 0x2069, 0x0140, 0x2001, 0x00a0, 0x080c, 0x2a7a, 0x080c,
+ 0x7840, 0x080c, 0x6092, 0x0804, 0x77bc, 0x2001, 0x180c, 0x200c,
+ 0xd1b4, 0x1160, 0xc1b5, 0x2102, 0x080c, 0x739b, 0x2069, 0x0140,
+ 0x2001, 0x0080, 0x080c, 0x2a7a, 0x60e3, 0x0000, 0x2069, 0x0200,
+ 0x6804, 0x9005, 0x1118, 0x6808, 0x9005, 0x0190, 0x6028, 0x9084,
+ 0xfdff, 0x602a, 0x2011, 0x0200, 0x080c, 0x2ab4, 0x2069, 0x198e,
+ 0x7000, 0x206a, 0x709b, 0x0027, 0x7003, 0x0001, 0x0804, 0x77bc,
+ 0x2011, 0x1e00, 0x080c, 0x2ab4, 0x2009, 0x1e00, 0x080c, 0x2a60,
+ 0x6024, 0x910c, 0x01c8, 0x9084, 0x1c00, 0x11b0, 0x1d04, 0x7778,
+ 0x0006, 0x0016, 0x00c6, 0x00d6, 0x00e6, 0x080c, 0x861c, 0x00ee,
+ 0x00de, 0x00ce, 0x001e, 0x000e, 0x00e6, 0x2071, 0x1a02, 0x7070,
+ 0x00ee, 0x9005, 0x19e8, 0x0400, 0x0026, 0x2011, 0x73b3, 0x080c,
+ 0x86c8, 0x2011, 0x73a6, 0x080c, 0x87d4, 0x002e, 0x2069, 0x0140,
+ 0x60e3, 0x0000, 0x70b4, 0x9005, 0x1118, 0x6887, 0x0001, 0x0008,
+ 0x6886, 0x2001, 0x196c, 0x2004, 0x080c, 0x26d5, 0x60e2, 0x2001,
+ 0x180c, 0x200c, 0xc1b4, 0x2102, 0x00ee, 0x00de, 0x00ce, 0x003e,
+ 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036,
+ 0x0046, 0x00c6, 0x00e6, 0x2061, 0x0100, 0x2071, 0x1800, 0x080c,
+ 0xd094, 0x1904, 0x782a, 0x7130, 0xd184, 0x1170, 0x080c, 0x33ad,
+ 0x0138, 0xc18d, 0x7132, 0x2011, 0x1848, 0x2214, 0xd2ac, 0x1120,
+ 0x7030, 0xd08c, 0x0904, 0x782a, 0x2011, 0x1848, 0x220c, 0xd1a4,
+ 0x0538, 0x0016, 0x2019, 0x000e, 0x080c, 0xe3b5, 0x0156, 0x00b6,
+ 0x20a9, 0x007f, 0x900e, 0x9186, 0x007e, 0x01a0, 0x9186, 0x0080,
+ 0x0188, 0x080c, 0x6693, 0x1170, 0x2120, 0x9006, 0x0016, 0x2009,
+ 0x000e, 0x080c, 0xe445, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c,
+ 0x8979, 0x001e, 0x8108, 0x1f04, 0x77f3, 0x00be, 0x015e, 0x001e,
+ 0xd1ac, 0x1148, 0x0016, 0x2009, 0x0002, 0x2019, 0x0004, 0x080c,
+ 0x3205, 0x001e, 0x0078, 0x0156, 0x00b6, 0x20a9, 0x007f, 0x900e,
+ 0x080c, 0x6693, 0x1110, 0x080c, 0x60ac, 0x8108, 0x1f04, 0x7820,
+ 0x00be, 0x015e, 0x080c, 0x1b35, 0x080c, 0xa91e, 0x080c, 0xabe9,
+ 0x080c, 0xa93a, 0x60e3, 0x0000, 0x080c, 0x6092, 0x080c, 0x746e,
+ 0x00ee, 0x00ce, 0x004e, 0x003e, 0x002e, 0x001e, 0x015e, 0x0005,
+ 0x2001, 0x197c, 0x2003, 0x0001, 0x0005, 0x2001, 0x197c, 0x2003,
+ 0x0000, 0x0005, 0x2001, 0x197b, 0x2003, 0xaaaa, 0x0005, 0x2001,
+ 0x197b, 0x2003, 0x0000, 0x0005, 0x2071, 0x18fa, 0x7003, 0x0000,
+ 0x7007, 0x0000, 0x080c, 0x1060, 0x090c, 0x0d7d, 0xa8ab, 0xdcb0,
+ 0x2900, 0x704e, 0x080c, 0x1060, 0x090c, 0x0d7d, 0xa8ab, 0xdcb0,
+ 0x2900, 0x7052, 0xa867, 0x0000, 0xa86b, 0x0001, 0xa89f, 0x0000,
+ 0x0005, 0x00e6, 0x2071, 0x0040, 0x6848, 0x9005, 0x1118, 0x9085,
+ 0x0001, 0x04b0, 0x6840, 0x9005, 0x0150, 0x04a1, 0x6a50, 0x9200,
+ 0x7002, 0x6854, 0x9101, 0x7006, 0x9006, 0x7012, 0x7016, 0x6850,
+ 0x7002, 0x6854, 0x7006, 0x6858, 0x700a, 0x685c, 0x700e, 0x6840,
+ 0x9005, 0x1110, 0x7012, 0x7016, 0x6848, 0x701a, 0x701c, 0x9085,
+ 0x0040, 0x701e, 0x2001, 0x0019, 0x7036, 0x702b, 0x0001, 0x2001,
+ 0x0004, 0x200c, 0x918c, 0xfff7, 0x918d, 0x8000, 0x2102, 0x00d6,
+ 0x2069, 0x18fa, 0x6807, 0x0001, 0x00de, 0x080c, 0x7e38, 0x9006,
+ 0x00ee, 0x0005, 0x900e, 0x0156, 0x20a9, 0x0006, 0x8003, 0x818d,
+ 0x1f04, 0x78b6, 0x015e, 0x0005, 0x2079, 0x0040, 0x2071, 0x18fa,
+ 0x7004, 0x0002, 0x78cc, 0x78cd, 0x7919, 0x7974, 0x7a84, 0x78ca,
+ 0x78ca, 0x7aae, 0x080c, 0x0d7d, 0x0005, 0x2079, 0x0040, 0x2001,
+ 0x1dc0, 0x2003, 0x0000, 0x782c, 0x908c, 0x0780, 0x190c, 0x7f1a,
+ 0xd0a4, 0x0578, 0x2001, 0x1dc0, 0x2004, 0x9082, 0x0080, 0x1648,
+ 0x1d04, 0x78ea, 0x2001, 0x1a05, 0x200c, 0x8109, 0x0510, 0x2091,
+ 0x6000, 0x2102, 0x7824, 0x2048, 0x9006, 0xa802, 0xa806, 0xa864,
+ 0x9084, 0x00ff, 0x908a, 0x0040, 0x0610, 0x00c0, 0x2001, 0x1800,
+ 0x200c, 0x9186, 0x0003, 0x1168, 0x7004, 0x0002, 0x7909, 0x78d3,
+ 0x7909, 0x7907, 0x7909, 0x7909, 0x7909, 0x7909, 0x7909, 0x080c,
+ 0x7974, 0x782c, 0xd09c, 0x090c, 0x7e38, 0x0005, 0x9082, 0x005a,
+ 0x1218, 0x2100, 0x003b, 0x0c10, 0x080c, 0x79aa, 0x0c90, 0x00e3,
+ 0x08e8, 0x0005, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa,
+ 0x79aa, 0x79aa, 0x79cc, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa,
+ 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa,
+ 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79b6, 0x79aa,
+ 0x7b9f, 0x79aa, 0x79aa, 0x79aa, 0x79cc, 0x79aa, 0x79b6, 0x7be0,
+ 0x7c21, 0x7c68, 0x7c7c, 0x79aa, 0x79aa, 0x79cc, 0x79b6, 0x79e0,
+ 0x79aa, 0x7a58, 0x7d27, 0x7d42, 0x79aa, 0x79cc, 0x79aa, 0x79e0,
+ 0x79aa, 0x79aa, 0x7a4e, 0x7d42, 0x79aa, 0x79aa, 0x79aa, 0x79aa,
+ 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79f4, 0x79aa, 0x79aa,
+ 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x7ebe,
+ 0x79aa, 0x7e68, 0x79aa, 0x7e68, 0x79aa, 0x7a09, 0x79aa, 0x79aa,
+ 0x79aa, 0x79aa, 0x79aa, 0x79aa, 0x2079, 0x0040, 0x7004, 0x9086,
+ 0x0003, 0x1198, 0x782c, 0x080c, 0x7e61, 0xd0a4, 0x0170, 0x7824,
+ 0x2048, 0x9006, 0xa802, 0xa806, 0xa864, 0x9084, 0x00ff, 0x908a,
+ 0x001a, 0x1210, 0x002b, 0x0c50, 0x00e9, 0x080c, 0x7e38, 0x0005,
+ 0x79aa, 0x79b6, 0x7b8b, 0x79aa, 0x79b6, 0x79aa, 0x79b6, 0x79b6,
+ 0x79aa, 0x79b6, 0x7b8b, 0x79b6, 0x79b6, 0x79b6, 0x79b6, 0x79b6,
+ 0x79aa, 0x79b6, 0x7b8b, 0x79aa, 0x79aa, 0x79b6, 0x79aa, 0x79aa,
+ 0x79aa, 0x79b6, 0x00e6, 0x2071, 0x18fa, 0x2009, 0x0400, 0x0071,
+ 0x00ee, 0x0005, 0x2009, 0x1000, 0x0049, 0x0005, 0x2009, 0x2000,
+ 0x0029, 0x0005, 0x2009, 0x0800, 0x0009, 0x0005, 0x7007, 0x0001,
+ 0xa868, 0x9084, 0x00ff, 0x9105, 0xa86a, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6dee, 0x012e, 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff,
+ 0x0d08, 0x8001, 0x1120, 0x7007, 0x0001, 0x0804, 0x7b2d, 0x7007,
+ 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b, 0x7b2d, 0x0005,
+ 0xa864, 0x8007, 0x9084, 0x00ff, 0x0968, 0x8001, 0x1120, 0x7007,
+ 0x0001, 0x0804, 0x7b48, 0x7007, 0x0003, 0x7012, 0x2900, 0x7016,
+ 0x701a, 0x704b, 0x7b48, 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff,
+ 0x0904, 0x79b2, 0x8001, 0x1120, 0x7007, 0x0001, 0x0804, 0x7b64,
+ 0x7007, 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b, 0x7b64,
+ 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff, 0x9086, 0x0001, 0x1904,
+ 0x79b2, 0x7007, 0x0001, 0x2009, 0x1834, 0x210c, 0x81ff, 0x11a8,
+ 0xa868, 0x9084, 0x00ff, 0xa86a, 0xa883, 0x0000, 0x080c, 0x6325,
+ 0x1108, 0x0005, 0x0126, 0x2091, 0x8000, 0xa867, 0x0139, 0xa87a,
+ 0xa982, 0x080c, 0x6dee, 0x012e, 0x0ca0, 0xa994, 0x9186, 0x0071,
+ 0x0d38, 0x9186, 0x0064, 0x0d20, 0x9186, 0x007c, 0x0d08, 0x9186,
+ 0x0028, 0x09f0, 0x9186, 0x0038, 0x09d8, 0x9186, 0x0078, 0x09c0,
+ 0x9186, 0x005f, 0x09a8, 0x9186, 0x0056, 0x0990, 0xa897, 0x4005,
+ 0xa89b, 0x0001, 0x2001, 0x0030, 0x900e, 0x08a0, 0xa87c, 0x9084,
+ 0x00c0, 0x9086, 0x00c0, 0x1120, 0x7007, 0x0001, 0x0804, 0x7d59,
+ 0x2900, 0x7016, 0x701a, 0x20a9, 0x0004, 0xa860, 0x20e0, 0xa85c,
+ 0x9080, 0x0030, 0x2098, 0x7050, 0x2040, 0xa060, 0x20e8, 0xa05c,
+ 0x9080, 0x0023, 0x20a0, 0x4003, 0xa888, 0x7012, 0x9082, 0x0401,
+ 0x1a04, 0x79ba, 0xaab4, 0x928a, 0x0002, 0x1a04, 0x79ba, 0x82ff,
+ 0x1138, 0xa8b8, 0xa9bc, 0x9105, 0x0118, 0x2001, 0x7aeb, 0x0018,
+ 0x9280, 0x7ae1, 0x2005, 0x7056, 0x7010, 0x9015, 0x0904, 0x7acc,
+ 0x080c, 0x1060, 0x1118, 0x7007, 0x0004, 0x0005, 0x2900, 0x7022,
+ 0x7054, 0x2060, 0xe000, 0xa866, 0x7050, 0x2040, 0xa95c, 0xe004,
+ 0x9100, 0xa076, 0xa860, 0xa072, 0xe008, 0x920a, 0x1210, 0x900e,
+ 0x2200, 0x7112, 0xe20c, 0x8003, 0x800b, 0x9296, 0x0004, 0x0108,
+ 0x9108, 0xa17a, 0x810b, 0xa17e, 0x080c, 0x113c, 0xa06c, 0x908e,
+ 0x0100, 0x0170, 0x9086, 0x0200, 0x0118, 0x7007, 0x0007, 0x0005,
+ 0x7020, 0x2048, 0x080c, 0x1079, 0x7014, 0x2048, 0x0804, 0x79ba,
+ 0x7020, 0x2048, 0x7018, 0xa802, 0xa807, 0x0000, 0x2908, 0x2048,
+ 0xa906, 0x711a, 0x0804, 0x7a84, 0x7014, 0x2048, 0x7007, 0x0001,
+ 0xa8b4, 0x9005, 0x1128, 0xa8b8, 0xa9bc, 0x9105, 0x0108, 0x00b9,
+ 0xa864, 0x9084, 0x00ff, 0x9086, 0x001e, 0x0904, 0x7d59, 0x0804,
+ 0x7b2d, 0x7ae3, 0x7ae7, 0x0002, 0x001d, 0x0007, 0x0004, 0x000a,
+ 0x001b, 0x0005, 0x0006, 0x000a, 0x001d, 0x0005, 0x0004, 0x0076,
+ 0x0066, 0xafb8, 0xaebc, 0xa804, 0x2050, 0xb0c0, 0xb0e2, 0xb0bc,
+ 0xb0de, 0xb0b8, 0xb0d2, 0xb0b4, 0xb0ce, 0xb6da, 0xb7d6, 0xb0b0,
+ 0xb0ca, 0xb0ac, 0xb0c6, 0xb0a8, 0xb0ba, 0xb0a4, 0xb0b6, 0xb6c2,
+ 0xb7be, 0xb0a0, 0xb0b2, 0xb09c, 0xb0ae, 0xb098, 0xb0a2, 0xb094,
+ 0xb09e, 0xb6aa, 0xb7a6, 0xb090, 0xb09a, 0xb08c, 0xb096, 0xb088,
+ 0xb08a, 0xb084, 0xb086, 0xb692, 0xb78e, 0xb080, 0xb082, 0xb07c,
+ 0xb07e, 0xb078, 0xb072, 0xb074, 0xb06e, 0xb67a, 0xb776, 0xb004,
+ 0x9055, 0x1958, 0x006e, 0x007e, 0x0005, 0x2009, 0x1834, 0x210c,
+ 0x81ff, 0x1178, 0x080c, 0x6124, 0x1108, 0x0005, 0x080c, 0x7022,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0xcc7f, 0x080c, 0x6dee, 0x012e,
+ 0x0ca0, 0x080c, 0xd094, 0x1d70, 0x2001, 0x0028, 0x900e, 0x0c70,
+ 0x2009, 0x1834, 0x210c, 0x81ff, 0x1188, 0xa888, 0x9005, 0x0188,
+ 0xa883, 0x0000, 0x080c, 0x61b2, 0x1108, 0x0005, 0xa87a, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x6dee, 0x012e, 0x0cb8, 0x2001, 0x0028,
+ 0x0ca8, 0x2001, 0x0000, 0x0c90, 0x2009, 0x1834, 0x210c, 0x81ff,
+ 0x11d8, 0xa888, 0x9005, 0x01e0, 0xa883, 0x0000, 0xa87c, 0xd0f4,
+ 0x0120, 0x080c, 0x6287, 0x1138, 0x0005, 0x9006, 0xa87a, 0x080c,
+ 0x61ff, 0x1108, 0x0005, 0x0126, 0x2091, 0x8000, 0xa87a, 0xa982,
+ 0x080c, 0x6dee, 0x012e, 0x0cb0, 0x2001, 0x0028, 0x900e, 0x0c98,
+ 0x2001, 0x0000, 0x0c80, 0x7018, 0xa802, 0x2908, 0x2048, 0xa906,
+ 0x711a, 0x7010, 0x8001, 0x7012, 0x0118, 0x7007, 0x0003, 0x0030,
+ 0x7014, 0x2048, 0x7007, 0x0001, 0x7048, 0x080f, 0x0005, 0x00b6,
+ 0x7007, 0x0001, 0xa974, 0xa878, 0x9084, 0x00ff, 0x9096, 0x0004,
+ 0x0540, 0x20a9, 0x0001, 0x9096, 0x0001, 0x0190, 0x900e, 0x20a9,
+ 0x0800, 0x9096, 0x0002, 0x0160, 0x9005, 0x11d8, 0xa974, 0x080c,
+ 0x6693, 0x11b8, 0x0066, 0xae80, 0x080c, 0x67a3, 0x006e, 0x0088,
+ 0x0046, 0x2011, 0x180c, 0x2224, 0xc484, 0x2412, 0x004e, 0x00c6,
+ 0x080c, 0x6693, 0x1110, 0x080c, 0x6976, 0x8108, 0x1f04, 0x7bc8,
+ 0x00ce, 0xa87c, 0xd084, 0x1120, 0x080c, 0x1079, 0x00be, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x6dee, 0x012e, 0x00be, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x080c, 0x6ad9, 0x0580,
+ 0x2061, 0x1a6e, 0x6100, 0xd184, 0x0178, 0xa888, 0x9084, 0x00ff,
+ 0x1550, 0x6000, 0xd084, 0x0520, 0x6004, 0x9005, 0x1538, 0x6003,
+ 0x0000, 0x600b, 0x0000, 0x00c8, 0x2011, 0x0001, 0xa890, 0x9005,
+ 0x1110, 0x2001, 0x001e, 0x8000, 0x6016, 0xa888, 0x9084, 0x00ff,
+ 0x0178, 0x6006, 0xa888, 0x8007, 0x9084, 0x00ff, 0x0148, 0x600a,
+ 0xa888, 0x8000, 0x1108, 0xc28d, 0x6202, 0x012e, 0x0804, 0x7e22,
+ 0x012e, 0x0804, 0x7e1c, 0x012e, 0x0804, 0x7e16, 0x012e, 0x0804,
+ 0x7e19, 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x080c, 0x6ad9,
+ 0x05e0, 0x2061, 0x1a6e, 0x6000, 0xd084, 0x05b8, 0x6204, 0x6308,
+ 0xd08c, 0x1530, 0xac78, 0x9484, 0x0003, 0x0170, 0xa988, 0x918c,
+ 0x00ff, 0x8001, 0x1120, 0x2100, 0x9210, 0x0620, 0x0028, 0x8001,
+ 0x1508, 0x2100, 0x9212, 0x02f0, 0x9484, 0x000c, 0x0188, 0xa988,
+ 0x810f, 0x918c, 0x00ff, 0x9082, 0x0004, 0x1120, 0x2100, 0x9318,
+ 0x0288, 0x0030, 0x9082, 0x0004, 0x1168, 0x2100, 0x931a, 0x0250,
+ 0xa890, 0x9005, 0x0110, 0x8000, 0x6016, 0x6206, 0x630a, 0x012e,
+ 0x0804, 0x7e22, 0x012e, 0x0804, 0x7e1f, 0x012e, 0x0804, 0x7e1c,
+ 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x2061, 0x1a6e, 0x6300,
+ 0xd38c, 0x1120, 0x6308, 0x8318, 0x0220, 0x630a, 0x012e, 0x0804,
+ 0x7e30, 0x012e, 0x0804, 0x7e1f, 0x00b6, 0x0126, 0x00c6, 0x2091,
+ 0x8000, 0x7007, 0x0001, 0xa87c, 0xd0ac, 0x0148, 0x00c6, 0x2061,
+ 0x1a6e, 0x6000, 0x9084, 0xfcff, 0x6002, 0x00ce, 0x0440, 0xa888,
+ 0x9005, 0x05d8, 0xa88c, 0x9065, 0x0598, 0x2001, 0x1834, 0x2004,
+ 0x9005, 0x0118, 0x080c, 0xaceb, 0x0068, 0x6017, 0xf400, 0x6063,
+ 0x0000, 0xa97c, 0xd1a4, 0x0110, 0xa980, 0x6162, 0x2009, 0x0041,
+ 0x080c, 0xad4d, 0xa988, 0x918c, 0xff00, 0x9186, 0x2000, 0x1138,
+ 0x0026, 0x900e, 0x2011, 0xfdff, 0x080c, 0x8979, 0x002e, 0xa87c,
+ 0xd0c4, 0x0148, 0x2061, 0x1a6e, 0x6000, 0xd08c, 0x1120, 0x6008,
+ 0x8000, 0x0208, 0x600a, 0x00ce, 0x012e, 0x00be, 0x0804, 0x7e22,
+ 0x00ce, 0x012e, 0x00be, 0x0804, 0x7e1c, 0xa984, 0x9186, 0x002e,
+ 0x0d30, 0x9186, 0x002d, 0x0d18, 0x9186, 0x0045, 0x0510, 0x9186,
+ 0x002a, 0x1130, 0x2001, 0x180c, 0x200c, 0xc194, 0x2102, 0x08b8,
+ 0x9186, 0x0020, 0x0158, 0x9186, 0x0029, 0x1d10, 0xa974, 0x080c,
+ 0x6693, 0x1968, 0xb800, 0xc0e4, 0xb802, 0x0848, 0xa88c, 0x9065,
+ 0x09b8, 0x6007, 0x0024, 0x2001, 0x1985, 0x2004, 0x601a, 0x0804,
+ 0x7cb7, 0xa88c, 0x9065, 0x0960, 0x00e6, 0xa890, 0x9075, 0x2001,
+ 0x1834, 0x2004, 0x9005, 0x0150, 0x080c, 0xaceb, 0x8eff, 0x0118,
+ 0x2e60, 0x080c, 0xaceb, 0x00ee, 0x0804, 0x7cb7, 0x6024, 0xc0dc,
+ 0xc0d5, 0x6026, 0x2e60, 0x6007, 0x003a, 0xa8a0, 0x9005, 0x0130,
+ 0x6007, 0x003b, 0xa8a4, 0x602e, 0xa8a8, 0x6016, 0x6003, 0x0001,
+ 0x2009, 0x8020, 0x080c, 0x92b0, 0x00ee, 0x0804, 0x7cb7, 0x2061,
+ 0x1a6e, 0x6000, 0xd084, 0x0190, 0xd08c, 0x1904, 0x7e30, 0x0126,
+ 0x2091, 0x8000, 0x6204, 0x8210, 0x0220, 0x6206, 0x012e, 0x0804,
+ 0x7e30, 0x012e, 0xa883, 0x0016, 0x0804, 0x7e29, 0xa883, 0x0007,
+ 0x0804, 0x7e29, 0xa864, 0x8007, 0x9084, 0x00ff, 0x0130, 0x8001,
+ 0x1138, 0x7007, 0x0001, 0x0069, 0x0005, 0x080c, 0x79b2, 0x0040,
+ 0x7007, 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b, 0x7d59,
+ 0x0005, 0x00b6, 0x00e6, 0x0126, 0x2091, 0x8000, 0x903e, 0x2061,
+ 0x1800, 0x61d0, 0x81ff, 0x1904, 0x7ddb, 0x6130, 0xd194, 0x1904,
+ 0x7e05, 0xa878, 0x2070, 0x9e82, 0x1ddc, 0x0a04, 0x7dcf, 0x6068,
+ 0x9e02, 0x1a04, 0x7dcf, 0x7120, 0x9186, 0x0006, 0x1904, 0x7dc1,
+ 0x7010, 0x905d, 0x0904, 0x7ddb, 0xb800, 0xd0e4, 0x1904, 0x7dff,
+ 0x2061, 0x1a6e, 0x6100, 0x9184, 0x0301, 0x9086, 0x0001, 0x15a0,
+ 0x7024, 0xd0dc, 0x1904, 0x7e08, 0xa883, 0x0000, 0xa803, 0x0000,
+ 0x2908, 0x7014, 0x9005, 0x1198, 0x7116, 0xa87c, 0xd0f4, 0x1904,
+ 0x7e0b, 0x080c, 0x573e, 0xd09c, 0x1118, 0xa87c, 0xc0cc, 0xa87e,
+ 0x2e60, 0x080c, 0x8869, 0x012e, 0x00ee, 0x00be, 0x0005, 0x2048,
+ 0xa800, 0x9005, 0x1de0, 0xa902, 0x2148, 0xa87c, 0xd0f4, 0x1904,
+ 0x7e0b, 0x012e, 0x00ee, 0x00be, 0x0005, 0x012e, 0x00ee, 0xa883,
+ 0x0006, 0x00be, 0x0804, 0x7e29, 0xd184, 0x0db8, 0xd1c4, 0x1190,
+ 0x00a0, 0xa974, 0x080c, 0x6693, 0x15d0, 0xb800, 0xd0e4, 0x15b8,
+ 0x7120, 0x9186, 0x0007, 0x1118, 0xa883, 0x0002, 0x0490, 0xa883,
+ 0x0008, 0x0478, 0xa883, 0x000e, 0x0460, 0xa883, 0x0017, 0x0448,
+ 0xa883, 0x0035, 0x0430, 0x080c, 0x5742, 0xd0fc, 0x01e8, 0xa878,
+ 0x2070, 0x9e82, 0x1ddc, 0x02c0, 0x6068, 0x9e02, 0x12a8, 0x7120,
+ 0x9186, 0x0006, 0x1188, 0x7010, 0x905d, 0x0170, 0xb800, 0xd0bc,
+ 0x0158, 0x2039, 0x0001, 0x7000, 0x9086, 0x0007, 0x1904, 0x7d65,
+ 0x7003, 0x0002, 0x0804, 0x7d65, 0xa883, 0x0028, 0x0010, 0xa883,
+ 0x0029, 0x012e, 0x00ee, 0x00be, 0x0420, 0xa883, 0x002a, 0x0cc8,
+ 0xa883, 0x0045, 0x0cb0, 0x2e60, 0x2019, 0x0002, 0x601b, 0x0014,
+ 0x080c, 0xdfa1, 0x012e, 0x00ee, 0x00be, 0x0005, 0x2009, 0x003e,
+ 0x0058, 0x2009, 0x0004, 0x0040, 0x2009, 0x0006, 0x0028, 0x2009,
+ 0x0016, 0x0010, 0x2009, 0x0001, 0xa884, 0x9084, 0xff00, 0x9105,
+ 0xa886, 0x0126, 0x2091, 0x8000, 0x080c, 0x6dee, 0x012e, 0x0005,
+ 0x080c, 0x1079, 0x0005, 0x00d6, 0x080c, 0x8860, 0x00de, 0x0005,
+ 0x00d6, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x0040, 0x702c,
+ 0xd084, 0x01d8, 0x908c, 0x0780, 0x190c, 0x7f1a, 0xd09c, 0x11a8,
+ 0x2071, 0x1800, 0x70c0, 0x90ea, 0x0020, 0x0278, 0x8001, 0x70c2,
+ 0x702c, 0x2048, 0xa800, 0x702e, 0x9006, 0xa802, 0xa806, 0x2071,
+ 0x0040, 0x2900, 0x7022, 0x702c, 0x0c28, 0x012e, 0x00ee, 0x00de,
+ 0x0005, 0x0006, 0x9084, 0x0780, 0x190c, 0x7f1a, 0x000e, 0x0005,
+ 0xa898, 0x9084, 0x0003, 0x05a8, 0x080c, 0xac5a, 0x05d8, 0x2900,
+ 0x6016, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0035, 0x1138, 0x6028,
+ 0xc0fd, 0x602a, 0x2001, 0x196a, 0x2004, 0x0098, 0xa8a0, 0x9084,
+ 0x00ff, 0xa99c, 0x918c, 0xff00, 0x9105, 0xa99c, 0x918c, 0x00ff,
+ 0x080c, 0x2661, 0x1540, 0x00b6, 0x080c, 0x6693, 0x2b00, 0x00be,
+ 0x1510, 0x6012, 0x6023, 0x0001, 0x2009, 0x0040, 0xa864, 0x9084,
+ 0x00ff, 0x9086, 0x0035, 0x0110, 0x2009, 0x0041, 0x080c, 0xad4d,
+ 0x0005, 0xa87b, 0x0101, 0x0126, 0x2091, 0x8000, 0x080c, 0x6dee,
+ 0x012e, 0x0005, 0xa87b, 0x002c, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x6dee, 0x012e, 0x0005, 0xa87b, 0x0028, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6dee, 0x012e, 0x080c, 0xacb0, 0x0005, 0x00d6, 0x00c6,
+ 0x0036, 0x0026, 0x0016, 0x00b6, 0x7007, 0x0001, 0xaa74, 0x9282,
+ 0x0004, 0x1a04, 0x7f0b, 0xa97c, 0x9188, 0x1000, 0x2104, 0x905d,
+ 0xb804, 0xd284, 0x0140, 0x05e8, 0x8007, 0x9084, 0x00ff, 0x9084,
+ 0x0006, 0x1108, 0x04b0, 0x2b10, 0x080c, 0xac5a, 0x1118, 0x080c,
+ 0xad20, 0x05a8, 0x6212, 0xa874, 0x0002, 0x7ee9, 0x7eee, 0x7ef1,
+ 0x7ef7, 0x2019, 0x0002, 0x080c, 0xe3b5, 0x0060, 0x080c, 0xe345,
+ 0x0048, 0x2019, 0x0002, 0xa980, 0x080c, 0xe364, 0x0018, 0xa980,
+ 0x080c, 0xe345, 0x080c, 0xacb0, 0xa887, 0x0000, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6dee, 0x012e, 0x00be, 0x001e, 0x002e, 0x003e,
+ 0x00ce, 0x00de, 0x0005, 0xa887, 0x0006, 0x0c80, 0xa887, 0x0002,
+ 0x0c68, 0xa887, 0x0005, 0x0c50, 0xa887, 0x0004, 0x0c38, 0xa887,
+ 0x0007, 0x0c20, 0x2091, 0x8000, 0x0e04, 0x7f1c, 0x0006, 0x0016,
+ 0x2001, 0x8003, 0x0006, 0x0804, 0x0d86, 0x2001, 0x1834, 0x2004,
+ 0x9005, 0x0005, 0x0005, 0x00f6, 0x2079, 0x0300, 0x2001, 0x0200,
+ 0x200c, 0xc1e5, 0xc1dc, 0x2102, 0x2009, 0x0218, 0x210c, 0xd1ec,
+ 0x1120, 0x080c, 0x162f, 0x00fe, 0x0005, 0x2001, 0x020d, 0x2003,
+ 0x0020, 0x781f, 0x0300, 0x00fe, 0x0005, 0x781c, 0xd08c, 0x0904,
+ 0x7f9d, 0x68c0, 0x90aa, 0x0005, 0x0a04, 0x85c5, 0x7d44, 0x7c40,
+ 0xd59c, 0x190c, 0x0d7d, 0x9584, 0x00f6, 0x1508, 0x9484, 0x7000,
+ 0x0138, 0x908a, 0x2000, 0x1258, 0x9584, 0x0700, 0x8007, 0x04f0,
+ 0x7000, 0x9084, 0xff00, 0x9086, 0x8100, 0x0db0, 0x00b0, 0x9484,
+ 0x0fff, 0x1130, 0x7000, 0x9084, 0xff00, 0x9086, 0x8100, 0x11c0,
+ 0x080c, 0xe85a, 0x080c, 0x84ac, 0x7817, 0x0140, 0x00a8, 0x9584,
+ 0x0076, 0x1118, 0x080c, 0x8508, 0x19c8, 0xd5a4, 0x0148, 0x0046,
+ 0x0056, 0x080c, 0x7fed, 0x080c, 0x216f, 0x005e, 0x004e, 0x0020,
+ 0x080c, 0xe85a, 0x7817, 0x0140, 0x080c, 0x753d, 0x0168, 0x2001,
+ 0x0111, 0x2004, 0xd08c, 0x0140, 0x6893, 0x0000, 0x2001, 0x0110,
+ 0x2003, 0x0008, 0x2003, 0x0000, 0x0489, 0x0005, 0x0002, 0x7faa,
+ 0x82ba, 0x7fa7, 0x7fa7, 0x7fa7, 0x7fa7, 0x7fa7, 0x7fa7, 0x7817,
+ 0x0140, 0x0005, 0x7000, 0x908c, 0xff00, 0x9194, 0xf000, 0x810f,
+ 0x9484, 0x0fff, 0x6892, 0x9286, 0x2000, 0x1150, 0x6800, 0x9086,
+ 0x0001, 0x1118, 0x080c, 0x57a4, 0x0070, 0x080c, 0x800d, 0x0058,
+ 0x9286, 0x3000, 0x1118, 0x080c, 0x81f4, 0x0028, 0x9286, 0x8000,
+ 0x1110, 0x080c, 0x83d9, 0x7817, 0x0140, 0x0005, 0x2001, 0x1810,
+ 0x2004, 0xd08c, 0x0178, 0x2001, 0x1800, 0x2004, 0x9086, 0x0003,
+ 0x1148, 0x0026, 0x0036, 0x2011, 0x8048, 0x2518, 0x080c, 0x4b52,
+ 0x003e, 0x002e, 0x0005, 0x0036, 0x0046, 0x0056, 0x00f6, 0x2079,
+ 0x0200, 0x2019, 0xfffe, 0x7c30, 0x0050, 0x0036, 0x0046, 0x0056,
+ 0x00f6, 0x2079, 0x0200, 0x7d44, 0x7c40, 0x2019, 0xffff, 0x2001,
+ 0x1810, 0x2004, 0xd08c, 0x0160, 0x2001, 0x1800, 0x2004, 0x9086,
+ 0x0003, 0x1130, 0x0026, 0x2011, 0x8048, 0x080c, 0x4b52, 0x002e,
+ 0x00fe, 0x005e, 0x004e, 0x003e, 0x0005, 0x00b6, 0x00c6, 0x7010,
+ 0x9084, 0xff00, 0x8007, 0x9096, 0x0001, 0x0120, 0x9096, 0x0023,
+ 0x1904, 0x81c5, 0x9186, 0x0023, 0x15c0, 0x080c, 0x8477, 0x0904,
+ 0x81c5, 0x6120, 0x9186, 0x0001, 0x0150, 0x9186, 0x0004, 0x0138,
+ 0x9186, 0x0008, 0x0120, 0x9186, 0x000a, 0x1904, 0x81c5, 0x7124,
+ 0x610a, 0x7030, 0x908e, 0x0200, 0x1130, 0x2009, 0x0015, 0x080c,
+ 0xad4d, 0x0804, 0x81c5, 0x908e, 0x0214, 0x0118, 0x908e, 0x0210,
+ 0x1130, 0x2009, 0x0015, 0x080c, 0xad4d, 0x0804, 0x81c5, 0x908e,
+ 0x0100, 0x1904, 0x81c5, 0x7034, 0x9005, 0x1904, 0x81c5, 0x2009,
+ 0x0016, 0x080c, 0xad4d, 0x0804, 0x81c5, 0x9186, 0x0022, 0x1904,
+ 0x81c5, 0x7030, 0x908e, 0x0300, 0x1580, 0x68dc, 0xd0a4, 0x0528,
+ 0xc0b5, 0x68de, 0x7100, 0x918c, 0x00ff, 0x697e, 0x7004, 0x6882,
+ 0x00f6, 0x2079, 0x0100, 0x79e6, 0x78ea, 0x0006, 0x9084, 0x00ff,
+ 0x0016, 0x2008, 0x080c, 0x26aa, 0x7932, 0x7936, 0x001e, 0x000e,
+ 0x00fe, 0x080c, 0x2661, 0x695e, 0x703c, 0x00e6, 0x2071, 0x0140,
+ 0x7086, 0x2071, 0x1800, 0x70b6, 0x00ee, 0x7034, 0x9005, 0x1904,
+ 0x81c5, 0x2009, 0x0017, 0x0804, 0x8175, 0x908e, 0x0400, 0x1190,
+ 0x7034, 0x9005, 0x1904, 0x81c5, 0x080c, 0x753d, 0x0120, 0x2009,
+ 0x001d, 0x0804, 0x8175, 0x68dc, 0xc0a5, 0x68de, 0x2009, 0x0030,
+ 0x0804, 0x8175, 0x908e, 0x0500, 0x1140, 0x7034, 0x9005, 0x1904,
+ 0x81c5, 0x2009, 0x0018, 0x0804, 0x8175, 0x908e, 0x2010, 0x1120,
+ 0x2009, 0x0019, 0x0804, 0x8175, 0x908e, 0x2110, 0x1120, 0x2009,
+ 0x001a, 0x0804, 0x8175, 0x908e, 0x5200, 0x1140, 0x7034, 0x9005,
+ 0x1904, 0x81c5, 0x2009, 0x001b, 0x0804, 0x8175, 0x908e, 0x5000,
+ 0x1140, 0x7034, 0x9005, 0x1904, 0x81c5, 0x2009, 0x001c, 0x0804,
+ 0x8175, 0x908e, 0x1300, 0x1120, 0x2009, 0x0034, 0x0804, 0x8175,
+ 0x908e, 0x1200, 0x1140, 0x7034, 0x9005, 0x1904, 0x81c5, 0x2009,
+ 0x0024, 0x0804, 0x8175, 0x908c, 0xff00, 0x918e, 0x2400, 0x1170,
+ 0x2009, 0x002d, 0x2001, 0x1810, 0x2004, 0xd09c, 0x0904, 0x8175,
+ 0x080c, 0xd7c9, 0x1904, 0x81c5, 0x0804, 0x8173, 0x908c, 0xff00,
+ 0x918e, 0x5300, 0x1120, 0x2009, 0x002a, 0x0804, 0x8175, 0x908e,
+ 0x0f00, 0x1120, 0x2009, 0x0020, 0x0804, 0x8175, 0x908e, 0x6104,
+ 0x1530, 0x2029, 0x0205, 0x2011, 0x026d, 0x8208, 0x2204, 0x9082,
+ 0x0004, 0x8004, 0x8004, 0x20a8, 0x2011, 0x8015, 0x211c, 0x8108,
+ 0x0046, 0x2124, 0x080c, 0x4b52, 0x004e, 0x8108, 0x0f04, 0x8129,
+ 0x9186, 0x0280, 0x1d88, 0x2504, 0x8000, 0x202a, 0x2009, 0x0260,
+ 0x0c58, 0x202b, 0x0000, 0x2009, 0x0023, 0x0804, 0x8175, 0x908e,
+ 0x6000, 0x1120, 0x2009, 0x003f, 0x0804, 0x8175, 0x908e, 0x5400,
+ 0x1138, 0x080c, 0x8575, 0x1904, 0x81c5, 0x2009, 0x0046, 0x04a8,
+ 0x908e, 0x5500, 0x1148, 0x080c, 0x859d, 0x1118, 0x2009, 0x0041,
+ 0x0460, 0x2009, 0x0042, 0x0448, 0x908e, 0x7800, 0x1118, 0x2009,
+ 0x0045, 0x0418, 0x908e, 0x1000, 0x1118, 0x2009, 0x004e, 0x00e8,
+ 0x908e, 0x6300, 0x1118, 0x2009, 0x004a, 0x00b8, 0x908c, 0xff00,
+ 0x918e, 0x5600, 0x1118, 0x2009, 0x004f, 0x0078, 0x908c, 0xff00,
+ 0x918e, 0x5700, 0x1118, 0x2009, 0x0050, 0x0038, 0x2009, 0x001d,
+ 0x6838, 0xd0d4, 0x0110, 0x2009, 0x004c, 0x0016, 0x2011, 0x0263,
+ 0x2204, 0x8211, 0x220c, 0x080c, 0x2661, 0x1904, 0x81c8, 0x080c,
+ 0x6632, 0x1904, 0x81c8, 0xbe12, 0xbd16, 0x001e, 0x0016, 0x080c,
+ 0x753d, 0x01c0, 0x68dc, 0xd08c, 0x1148, 0x7000, 0x9084, 0x00ff,
+ 0x1188, 0x7004, 0x9084, 0xff00, 0x1168, 0x0040, 0x687c, 0x9606,
+ 0x1148, 0x6880, 0x9506, 0x9084, 0xff00, 0x1120, 0x9584, 0x00ff,
+ 0xb886, 0x0080, 0xb884, 0x9005, 0x1168, 0x9186, 0x0046, 0x1150,
+ 0x687c, 0x9606, 0x1138, 0x6880, 0x9506, 0x9084, 0xff00, 0x1110,
+ 0x001e, 0x0098, 0x080c, 0xac5a, 0x01a8, 0x2b08, 0x6112, 0x6023,
+ 0x0004, 0x7120, 0x610a, 0x001e, 0x9186, 0x004c, 0x1110, 0x6023,
+ 0x000a, 0x0016, 0x001e, 0x080c, 0xad4d, 0x00ce, 0x00be, 0x0005,
+ 0x001e, 0x0cd8, 0x2001, 0x180e, 0x2004, 0xd0ec, 0x0120, 0x2011,
+ 0x8049, 0x080c, 0x4b52, 0x080c, 0xad20, 0x0d90, 0x2b08, 0x6112,
+ 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x0016, 0x9186, 0x0017,
+ 0x0118, 0x9186, 0x0030, 0x1128, 0x6007, 0x0009, 0x6017, 0x2900,
+ 0x0020, 0x6007, 0x0051, 0x6017, 0x0000, 0x602f, 0x0009, 0x6003,
+ 0x0001, 0x080c, 0x92b7, 0x08a0, 0x080c, 0x85e4, 0x1158, 0x080c,
+ 0x3377, 0x1140, 0x7010, 0x9084, 0xff00, 0x8007, 0x908e, 0x0008,
+ 0x1108, 0x0009, 0x0005, 0x00b6, 0x00c6, 0x0046, 0x7000, 0x908c,
+ 0xff00, 0x810f, 0x9186, 0x0033, 0x11e8, 0x080c, 0x8477, 0x0904,
+ 0x8252, 0x7124, 0x610a, 0x7030, 0x908e, 0x0200, 0x1140, 0x7034,
+ 0x9005, 0x15c0, 0x2009, 0x0015, 0x080c, 0xad4d, 0x0498, 0x908e,
+ 0x0100, 0x1580, 0x7034, 0x9005, 0x1568, 0x2009, 0x0016, 0x080c,
+ 0xad4d, 0x0440, 0x9186, 0x0032, 0x1528, 0x7030, 0x908e, 0x1400,
+ 0x1508, 0x2009, 0x0038, 0x0016, 0x2011, 0x0263, 0x2204, 0x8211,
+ 0x220c, 0x080c, 0x2661, 0x11a8, 0x080c, 0x6632, 0x1190, 0xbe12,
+ 0xbd16, 0x080c, 0xac5a, 0x0168, 0x2b08, 0x6112, 0x080c, 0xce15,
+ 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x080c, 0xad4d, 0x0010,
+ 0x00ce, 0x001e, 0x004e, 0x00ce, 0x00be, 0x0005, 0x00b6, 0x0046,
+ 0x00e6, 0x00d6, 0x2028, 0x2130, 0x9696, 0x00ff, 0x11b8, 0x9592,
+ 0xfffc, 0x02a0, 0x9596, 0xfffd, 0x1120, 0x2009, 0x007f, 0x0804,
+ 0x82b4, 0x9596, 0xfffe, 0x1120, 0x2009, 0x007e, 0x0804, 0x82b4,
+ 0x9596, 0xfffc, 0x1118, 0x2009, 0x0080, 0x04f0, 0x2011, 0x0000,
+ 0x2019, 0x1837, 0x231c, 0xd3ac, 0x0130, 0x9026, 0x20a9, 0x0800,
+ 0x2071, 0x1000, 0x0030, 0x2021, 0x0081, 0x20a9, 0x077f, 0x2071,
+ 0x1081, 0x2e1c, 0x93dd, 0x0000, 0x1140, 0x82ff, 0x11d0, 0x9496,
+ 0x00ff, 0x01b8, 0x2410, 0xc2fd, 0x00a0, 0xbf10, 0x2600, 0x9706,
+ 0xb814, 0x1120, 0x9546, 0x1110, 0x2408, 0x00b0, 0x9745, 0x1148,
+ 0x94c6, 0x007e, 0x0130, 0x94c6, 0x007f, 0x0118, 0x94c6, 0x0080,
+ 0x1d20, 0x8420, 0x8e70, 0x1f04, 0x8289, 0x82ff, 0x1118, 0x9085,
+ 0x0001, 0x0018, 0xc2fc, 0x2208, 0x9006, 0x00de, 0x00ee, 0x004e,
+ 0x00be, 0x0005, 0x2001, 0x1837, 0x200c, 0x9184, 0x0080, 0x0110,
+ 0xd18c, 0x0138, 0x7000, 0x908c, 0xff00, 0x810f, 0x9184, 0x000f,
+ 0x001a, 0x7817, 0x0140, 0x0005, 0x82dc, 0x82dc, 0x82dc, 0x8489,
+ 0x82dc, 0x82df, 0x8304, 0x838d, 0x82dc, 0x82dc, 0x82dc, 0x82dc,
+ 0x82dc, 0x82dc, 0x82dc, 0x82dc, 0x7817, 0x0140, 0x0005, 0x00b6,
+ 0x7110, 0xd1bc, 0x01e8, 0x7120, 0x2160, 0x9c8c, 0x0003, 0x11c0,
+ 0x9c8a, 0x1ddc, 0x02a8, 0x6868, 0x9c02, 0x1290, 0x7008, 0x9084,
+ 0x00ff, 0x6110, 0x2158, 0xb910, 0x9106, 0x1150, 0x700c, 0xb914,
+ 0x9106, 0x1130, 0x7124, 0x610a, 0x2009, 0x0046, 0x080c, 0xad4d,
+ 0x7817, 0x0140, 0x00be, 0x0005, 0x00b6, 0x00c6, 0x9484, 0x0fff,
+ 0x0904, 0x8369, 0x7110, 0xd1bc, 0x1904, 0x8369, 0x7108, 0x700c,
+ 0x2028, 0x918c, 0x00ff, 0x2130, 0x9094, 0xff00, 0x15c8, 0x81ff,
+ 0x15b8, 0x9080, 0x33b9, 0x200d, 0x918c, 0xff00, 0x810f, 0x2001,
+ 0x0080, 0x9106, 0x0904, 0x8369, 0x9182, 0x0801, 0x1a04, 0x8369,
+ 0x9190, 0x1000, 0x2204, 0x905d, 0x05e0, 0xbe12, 0xbd16, 0xb800,
+ 0xd0ec, 0x15b8, 0xba04, 0x9294, 0xff00, 0x9286, 0x0600, 0x1190,
+ 0x080c, 0xac5a, 0x0598, 0x2b08, 0x7028, 0x604e, 0x702c, 0x6052,
+ 0x6112, 0x6023, 0x0006, 0x7120, 0x610a, 0x7130, 0x615e, 0x080c,
+ 0xda32, 0x00f8, 0x080c, 0x6add, 0x1138, 0xb807, 0x0606, 0x0c40,
+ 0x190c, 0x8256, 0x11b0, 0x0880, 0x080c, 0xac5a, 0x2b08, 0x0188,
+ 0x6112, 0x6023, 0x0004, 0x7120, 0x610a, 0x9286, 0x0400, 0x1118,
+ 0x6007, 0x0005, 0x0010, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c,
+ 0x92b7, 0x7817, 0x0140, 0x00ce, 0x00be, 0x0005, 0x2001, 0x180e,
+ 0x2004, 0xd0ec, 0x0120, 0x2011, 0x8049, 0x080c, 0x4b52, 0x080c,
+ 0xad20, 0x0d78, 0x2b08, 0x6112, 0x6023, 0x0006, 0x7120, 0x610a,
+ 0x7130, 0x615e, 0x6017, 0xf300, 0x6003, 0x0001, 0x6007, 0x0041,
+ 0x2009, 0xa022, 0x080c, 0x92b0, 0x08e0, 0x00b6, 0x7110, 0xd1bc,
+ 0x05d0, 0x7020, 0x2060, 0x9c84, 0x0003, 0x15a8, 0x9c82, 0x1ddc,
+ 0x0690, 0x6868, 0x9c02, 0x1678, 0x9484, 0x0fff, 0x9082, 0x000c,
+ 0x0650, 0x7008, 0x9084, 0x00ff, 0x6110, 0x2158, 0xb910, 0x9106,
+ 0x1510, 0x700c, 0xb914, 0x9106, 0x11f0, 0x7124, 0x610a, 0x601c,
+ 0xd0fc, 0x11c8, 0x2001, 0x0271, 0x2004, 0x9005, 0x1180, 0x9484,
+ 0x0fff, 0x9082, 0x000c, 0x0158, 0x0066, 0x2031, 0x0100, 0xa001,
+ 0xa001, 0x8631, 0x1de0, 0x006e, 0x601c, 0xd0fc, 0x1120, 0x2009,
+ 0x0045, 0x080c, 0xad4d, 0x7817, 0x0140, 0x00be, 0x0005, 0x6120,
+ 0x9186, 0x0002, 0x0128, 0x9186, 0x0005, 0x0110, 0x9085, 0x0001,
+ 0x0005, 0x080c, 0x85e4, 0x1180, 0x080c, 0x3377, 0x1168, 0x7010,
+ 0x9084, 0xff00, 0x8007, 0x9086, 0x0000, 0x1130, 0x9184, 0x000f,
+ 0x908a, 0x0006, 0x1208, 0x000b, 0x0005, 0x83f3, 0x83f4, 0x83f3,
+ 0x83f3, 0x8459, 0x8468, 0x0005, 0x00b6, 0x700c, 0x7108, 0x080c,
+ 0x2661, 0x1904, 0x8457, 0x080c, 0x6632, 0x1904, 0x8457, 0xbe12,
+ 0xbd16, 0x7110, 0xd1bc, 0x0540, 0x702c, 0xd084, 0x1120, 0xb800,
+ 0xd0bc, 0x1904, 0x8457, 0x080c, 0x6add, 0x0148, 0x9086, 0x0004,
+ 0x0130, 0x080c, 0x6ae5, 0x0118, 0x9086, 0x0004, 0x1588, 0x00c6,
+ 0x080c, 0x8477, 0x00ce, 0x05d8, 0x080c, 0xac5a, 0x2b08, 0x05b8,
+ 0x6112, 0x080c, 0xce15, 0x6023, 0x0002, 0x7120, 0x610a, 0x2009,
+ 0x0088, 0x080c, 0xad4d, 0x0458, 0x080c, 0x6add, 0x0148, 0x9086,
+ 0x0004, 0x0130, 0x080c, 0x6ae5, 0x0118, 0x9086, 0x0004, 0x1180,
+ 0x080c, 0xac5a, 0x2b08, 0x01d8, 0x6112, 0x080c, 0xce15, 0x6023,
+ 0x0005, 0x7120, 0x610a, 0x2009, 0x0088, 0x080c, 0xad4d, 0x0078,
+ 0x080c, 0xac5a, 0x2b08, 0x0158, 0x6112, 0x080c, 0xce15, 0x6023,
+ 0x0004, 0x7120, 0x610a, 0x2009, 0x0001, 0x080c, 0xad4d, 0x00be,
+ 0x0005, 0x7110, 0xd1bc, 0x0158, 0x00d1, 0x0148, 0x080c, 0x83cf,
+ 0x1130, 0x7124, 0x610a, 0x2009, 0x0089, 0x080c, 0xad4d, 0x0005,
+ 0x7110, 0xd1bc, 0x0158, 0x0059, 0x0148, 0x080c, 0x83cf, 0x1130,
+ 0x7124, 0x610a, 0x2009, 0x008a, 0x080c, 0xad4d, 0x0005, 0x7020,
+ 0x2060, 0x9c84, 0x0003, 0x1158, 0x9c82, 0x1ddc, 0x0240, 0x2001,
+ 0x181a, 0x2004, 0x9c02, 0x1218, 0x9085, 0x0001, 0x0005, 0x9006,
+ 0x0ce8, 0x00b6, 0x7110, 0xd1bc, 0x11d8, 0x7024, 0x2060, 0x9c84,
+ 0x0003, 0x11b0, 0x9c82, 0x1ddc, 0x0298, 0x6868, 0x9c02, 0x1280,
+ 0x7008, 0x9084, 0x00ff, 0x6110, 0x2158, 0xb910, 0x9106, 0x1140,
+ 0x700c, 0xb914, 0x9106, 0x1120, 0x2009, 0x0051, 0x080c, 0xad4d,
+ 0x7817, 0x0140, 0x00be, 0x0005, 0x2031, 0x0105, 0x0069, 0x0005,
+ 0x2031, 0x0206, 0x0049, 0x0005, 0x2031, 0x0207, 0x0029, 0x0005,
+ 0x2031, 0x0213, 0x0009, 0x0005, 0x00c6, 0x0096, 0x00f6, 0x7000,
+ 0x9084, 0xf000, 0x9086, 0xc000, 0x05c0, 0x080c, 0xac5a, 0x05a8,
+ 0x0066, 0x00c6, 0x0046, 0x2011, 0x0263, 0x2204, 0x8211, 0x220c,
+ 0x080c, 0x2661, 0x1590, 0x080c, 0x6632, 0x1578, 0xbe12, 0xbd16,
+ 0x2b00, 0x004e, 0x00ce, 0x6012, 0x080c, 0xce15, 0x080c, 0x1047,
+ 0x0500, 0x2900, 0x6062, 0x9006, 0xa802, 0xa866, 0xac6a, 0xa85c,
+ 0x90f8, 0x001b, 0x20a9, 0x000e, 0xa860, 0x20e8, 0x20e1, 0x0000,
+ 0x2fa0, 0x2e98, 0x4003, 0x006e, 0x6616, 0x6007, 0x003e, 0x6023,
+ 0x0001, 0x6003, 0x0001, 0x080c, 0x92b7, 0x00fe, 0x009e, 0x00ce,
+ 0x0005, 0x080c, 0xacb0, 0x006e, 0x0cc0, 0x004e, 0x00ce, 0x0cc8,
+ 0x00c6, 0x7000, 0x908c, 0xff00, 0x9184, 0xf000, 0x810f, 0x9086,
+ 0x2000, 0x1904, 0x855f, 0x9186, 0x0022, 0x15f0, 0x2001, 0x0111,
+ 0x2004, 0x9005, 0x1904, 0x8561, 0x7030, 0x908e, 0x0400, 0x0904,
+ 0x8561, 0x908e, 0x6000, 0x05e8, 0x908e, 0x5400, 0x05d0, 0x908e,
+ 0x0300, 0x11d8, 0x2009, 0x1837, 0x210c, 0xd18c, 0x1590, 0xd1a4,
+ 0x1580, 0x080c, 0x6a9b, 0x0588, 0x68b0, 0x9084, 0x00ff, 0x7100,
+ 0x918c, 0x00ff, 0x9106, 0x1518, 0x6880, 0x69b0, 0x918c, 0xff00,
+ 0x9105, 0x7104, 0x9106, 0x11d8, 0x00e0, 0x2009, 0x0103, 0x210c,
+ 0xd1b4, 0x11a8, 0x908e, 0x5200, 0x09e8, 0x908e, 0x0500, 0x09d0,
+ 0x908e, 0x5000, 0x09b8, 0x0058, 0x9186, 0x0023, 0x1140, 0x080c,
+ 0x8477, 0x0128, 0x6004, 0x9086, 0x0002, 0x0118, 0x0000, 0x9006,
+ 0x0010, 0x9085, 0x0001, 0x00ce, 0x0005, 0x7030, 0x908e, 0x0300,
+ 0x0118, 0x908e, 0x5200, 0x1d98, 0x2001, 0x1837, 0x2004, 0x9084,
+ 0x0009, 0x9086, 0x0008, 0x0d68, 0x0c50, 0x0156, 0x0046, 0x0016,
+ 0x0036, 0x7038, 0x2020, 0x8427, 0x94a4, 0x0007, 0xd484, 0x0148,
+ 0x20a9, 0x0004, 0x2019, 0x1805, 0x2011, 0x027a, 0x080c, 0xbc8e,
+ 0x1178, 0xd48c, 0x0148, 0x20a9, 0x0004, 0x2019, 0x1801, 0x2011,
+ 0x027e, 0x080c, 0xbc8e, 0x1120, 0xd494, 0x0110, 0x9085, 0x0001,
+ 0x003e, 0x001e, 0x004e, 0x015e, 0x0005, 0x0156, 0x0046, 0x0016,
+ 0x0036, 0x7038, 0x2020, 0x8427, 0x94a4, 0x0007, 0xd484, 0x0148,
+ 0x20a9, 0x0004, 0x2019, 0x1805, 0x2011, 0x0272, 0x080c, 0xbc8e,
+ 0x1178, 0xd48c, 0x0148, 0x20a9, 0x0004, 0x2019, 0x1801, 0x2011,
+ 0x0276, 0x080c, 0xbc8e, 0x1120, 0xd494, 0x0110, 0x9085, 0x0001,
+ 0x003e, 0x001e, 0x004e, 0x015e, 0x0005, 0x00f6, 0x2079, 0x0200,
+ 0x7800, 0xc0e5, 0xc0cc, 0x7802, 0x00fe, 0x0005, 0x00f6, 0x2079,
+ 0x1800, 0x7834, 0xd084, 0x1130, 0x2079, 0x0200, 0x7800, 0x9085,
+ 0x1200, 0x7802, 0x00fe, 0x0005, 0x00e6, 0x2071, 0x1800, 0x7034,
+ 0xc084, 0x7036, 0x00ee, 0x0005, 0x0016, 0x2001, 0x1837, 0x200c,
+ 0x9184, 0x0080, 0x0118, 0xd18c, 0x0118, 0x9006, 0x001e, 0x0005,
+ 0x9085, 0x0001, 0x0cd8, 0x2071, 0x1a02, 0x7003, 0x0003, 0x700f,
+ 0x0361, 0x9006, 0x701a, 0x7072, 0x7012, 0x7017, 0x1ddc, 0x7007,
+ 0x0000, 0x7026, 0x702b, 0x9ef4, 0x7032, 0x7037, 0x9f71, 0x703f,
+ 0xffff, 0x7042, 0x7047, 0x55c2, 0x704a, 0x705b, 0x879b, 0x080c,
+ 0x1060, 0x090c, 0x0d7d, 0x2900, 0x703a, 0xa867, 0x0003, 0xa86f,
+ 0x0100, 0xa8ab, 0xdcb0, 0x0005, 0x2071, 0x1a02, 0x1d04, 0x86b7,
+ 0x2091, 0x6000, 0x700c, 0x8001, 0x700e, 0x1590, 0x2001, 0x013c,
+ 0x2004, 0x9005, 0x190c, 0x8845, 0x2001, 0x1869, 0x2004, 0xd0c4,
+ 0x0158, 0x3a00, 0xd08c, 0x1140, 0x20d1, 0x0000, 0x20d1, 0x0001,
+ 0x20d1, 0x0000, 0x080c, 0x0d7d, 0x700f, 0x0361, 0x7007, 0x0001,
+ 0x0126, 0x2091, 0x8000, 0x2069, 0x1800, 0x69ec, 0xd1e4, 0x1138,
+ 0xd1dc, 0x1118, 0x080c, 0x8809, 0x0010, 0x080c, 0x87e0, 0x7040,
+ 0x900d, 0x0148, 0x8109, 0x7142, 0x1130, 0x7044, 0x080f, 0x0018,
+ 0x0126, 0x2091, 0x8000, 0x7024, 0x900d, 0x0188, 0x7020, 0x8001,
+ 0x7022, 0x1168, 0x7023, 0x0009, 0x8109, 0x7126, 0x9186, 0x03e8,
+ 0x1110, 0x7028, 0x080f, 0x81ff, 0x1110, 0x7028, 0x080f, 0x7030,
+ 0x900d, 0x0180, 0x702c, 0x8001, 0x702e, 0x1160, 0x702f, 0x0009,
+ 0x8109, 0x7132, 0x0128, 0x9184, 0x007f, 0x090c, 0xa00d, 0x0010,
+ 0x7034, 0x080f, 0x703c, 0x9005, 0x0118, 0x0310, 0x8001, 0x703e,
+ 0x704c, 0x900d, 0x0168, 0x7048, 0x8001, 0x704a, 0x1148, 0x704b,
+ 0x0009, 0x8109, 0x714e, 0x1120, 0x7150, 0x714e, 0x7058, 0x080f,
+ 0x7018, 0x900d, 0x01d8, 0x0016, 0x7070, 0x900d, 0x0158, 0x706c,
+ 0x8001, 0x706e, 0x1138, 0x706f, 0x0009, 0x8109, 0x7172, 0x1110,
+ 0x7074, 0x080f, 0x001e, 0x7008, 0x8001, 0x700a, 0x1138, 0x700b,
+ 0x0009, 0x8109, 0x711a, 0x1110, 0x701c, 0x080f, 0x012e, 0x7004,
+ 0x0002, 0x86df, 0x86e0, 0x870a, 0x00e6, 0x2071, 0x1a02, 0x7018,
+ 0x9005, 0x1120, 0x711a, 0x721e, 0x700b, 0x0009, 0x00ee, 0x0005,
+ 0x00e6, 0x0006, 0x2071, 0x1a02, 0x701c, 0x9206, 0x1120, 0x701a,
+ 0x701e, 0x7072, 0x7076, 0x000e, 0x00ee, 0x0005, 0x00e6, 0x2071,
+ 0x1a02, 0xb888, 0x9102, 0x0208, 0xb98a, 0x00ee, 0x0005, 0x0005,
+ 0x00b6, 0x2031, 0x0010, 0x7110, 0x080c, 0x6693, 0x11a8, 0xb888,
+ 0x8001, 0x0290, 0xb88a, 0x1180, 0x0126, 0x2091, 0x8000, 0x0066,
+ 0xb8d0, 0x9005, 0x0138, 0x0026, 0xba3c, 0x0016, 0x080c, 0x67be,
+ 0x001e, 0x002e, 0x006e, 0x012e, 0x8108, 0x9182, 0x0800, 0x1220,
+ 0x8631, 0x0128, 0x7112, 0x0c00, 0x900e, 0x7007, 0x0002, 0x7112,
+ 0x00be, 0x0005, 0x2031, 0x0010, 0x7014, 0x2060, 0x0126, 0x2091,
+ 0x8000, 0x6048, 0x9005, 0x0128, 0x8001, 0x604a, 0x1110, 0x080c,
+ 0xcc96, 0x6018, 0x9005, 0x0904, 0x8762, 0x00f6, 0x2079, 0x0300,
+ 0x7918, 0xd1b4, 0x1904, 0x8775, 0x781b, 0x2020, 0xa001, 0x7918,
+ 0xd1b4, 0x0120, 0x781b, 0x2000, 0x0804, 0x8775, 0x8001, 0x601a,
+ 0x0106, 0x781b, 0x2000, 0xa001, 0x7918, 0xd1ac, 0x1dd0, 0x010e,
+ 0x00fe, 0x1540, 0x6120, 0x9186, 0x0003, 0x0148, 0x9186, 0x0006,
+ 0x0130, 0x9186, 0x0009, 0x11e0, 0x611c, 0xd1c4, 0x1100, 0x080c,
+ 0xc97a, 0x01b0, 0x6014, 0x2048, 0xa884, 0x908a, 0x199a, 0x0280,
+ 0x9082, 0x1999, 0xa886, 0x908a, 0x199a, 0x0210, 0x2001, 0x1999,
+ 0x8003, 0x800b, 0x810b, 0x9108, 0x611a, 0x080c, 0xd0c7, 0x0110,
+ 0x080c, 0xc65b, 0x012e, 0x9c88, 0x001c, 0x7116, 0x2001, 0x181a,
+ 0x2004, 0x9102, 0x1228, 0x8631, 0x0138, 0x2160, 0x0804, 0x870e,
+ 0x7017, 0x1ddc, 0x7007, 0x0000, 0x0005, 0x00fe, 0x0c58, 0x00e6,
+ 0x2071, 0x1a02, 0x7027, 0x07d0, 0x7023, 0x0009, 0x00ee, 0x0005,
+ 0x2001, 0x1a0b, 0x2003, 0x0000, 0x0005, 0x00e6, 0x2071, 0x1a02,
+ 0x7132, 0x702f, 0x0009, 0x00ee, 0x0005, 0x2011, 0x1a0e, 0x2013,
+ 0x0000, 0x0005, 0x00e6, 0x2071, 0x1a02, 0x711a, 0x721e, 0x700b,
+ 0x0009, 0x00ee, 0x0005, 0x0086, 0x0026, 0x7054, 0x8000, 0x7056,
+ 0x2001, 0x1a10, 0x2044, 0xa06c, 0x9086, 0x0000, 0x0150, 0x7068,
+ 0xa09a, 0x7064, 0xa096, 0x7060, 0xa092, 0x705c, 0xa08e, 0x080c,
+ 0x113c, 0x002e, 0x008e, 0x0005, 0x0006, 0x0016, 0x0096, 0x00a6,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x0156, 0x080c, 0x861c,
+ 0x015e, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e,
+ 0x001e, 0x000e, 0x0005, 0x00e6, 0x2071, 0x1a02, 0x7172, 0x7276,
+ 0x706f, 0x0009, 0x00ee, 0x0005, 0x00e6, 0x0006, 0x2071, 0x1a02,
+ 0x7074, 0x9206, 0x1110, 0x7072, 0x7076, 0x000e, 0x00ee, 0x0005,
+ 0x2069, 0x1800, 0x69ec, 0xd1e4, 0x1518, 0x0026, 0xd1ec, 0x0140,
+ 0x6a54, 0x6874, 0x9202, 0x0288, 0x8117, 0x9294, 0x00c1, 0x0088,
+ 0x9184, 0x0007, 0x01a0, 0x8109, 0x9184, 0x0007, 0x0110, 0x69ee,
+ 0x0070, 0x8107, 0x9084, 0x0007, 0x910d, 0x8107, 0x9106, 0x9094,
+ 0x00c1, 0x9184, 0xff3e, 0x9205, 0x68ee, 0x080c, 0x0f12, 0x002e,
+ 0x0005, 0x69e8, 0x9184, 0x003f, 0x05b8, 0x8109, 0x9184, 0x003f,
+ 0x01a8, 0x6a54, 0x6874, 0x9202, 0x0220, 0xd1bc, 0x0168, 0xc1bc,
+ 0x0018, 0xd1bc, 0x1148, 0xc1bd, 0x2110, 0x00e6, 0x2071, 0x1800,
+ 0x080c, 0x0f34, 0x00ee, 0x0400, 0x69ea, 0x00f0, 0x0026, 0x8107,
+ 0x9094, 0x0007, 0x0128, 0x8001, 0x8007, 0x9085, 0x0007, 0x0050,
+ 0x2010, 0x8004, 0x8004, 0x8004, 0x9084, 0x0007, 0x9205, 0x8007,
+ 0x9085, 0x0028, 0x9086, 0x0040, 0x2010, 0x00e6, 0x2071, 0x1800,
+ 0x080c, 0x0f34, 0x00ee, 0x002e, 0x0005, 0x0016, 0x00c6, 0x2009,
+ 0xfff4, 0x210d, 0x2061, 0x0100, 0x60f0, 0x9100, 0x60f3, 0x0000,
+ 0x2009, 0xfff4, 0x200f, 0x1220, 0x8108, 0x2105, 0x8000, 0x200f,
+ 0x00ce, 0x001e, 0x0005, 0x00c6, 0x2061, 0x1a6e, 0x00ce, 0x0005,
+ 0x9184, 0x000f, 0x8003, 0x8003, 0x8003, 0x9080, 0x1a6e, 0x2060,
+ 0x0005, 0xa884, 0x908a, 0x199a, 0x1638, 0x9005, 0x1150, 0x00c6,
+ 0x2061, 0x1a6e, 0x6014, 0x00ce, 0x9005, 0x1130, 0x2001, 0x001e,
+ 0x0018, 0x908e, 0xffff, 0x01b0, 0x8003, 0x800b, 0x810b, 0x9108,
+ 0x611a, 0xa87c, 0x908c, 0x00c0, 0x918e, 0x00c0, 0x0904, 0x8923,
+ 0xd0b4, 0x1168, 0xd0bc, 0x1904, 0x88fc, 0x2009, 0x0006, 0x080c,
+ 0x8950, 0x0005, 0x900e, 0x0c60, 0x2001, 0x1999, 0x08b0, 0xd0fc,
+ 0x05e0, 0x908c, 0x2023, 0x1568, 0x87ff, 0x1558, 0xa9a8, 0x81ff,
+ 0x1540, 0x6124, 0x918c, 0x0500, 0x1520, 0x6100, 0x918e, 0x0007,
+ 0x1500, 0x2009, 0x1869, 0x210c, 0xd184, 0x11d8, 0x6003, 0x0003,
+ 0x6007, 0x0043, 0x6047, 0xb035, 0x080c, 0x1c59, 0xa87c, 0xc0dd,
+ 0xa87e, 0x600f, 0x0000, 0x00f6, 0x2079, 0x0380, 0x7818, 0xd0bc,
+ 0x1de8, 0x7833, 0x0013, 0x2c00, 0x7836, 0x781b, 0x8080, 0x00fe,
+ 0x0005, 0x908c, 0x0003, 0x0120, 0x918e, 0x0003, 0x1904, 0x894a,
+ 0x908c, 0x2020, 0x918e, 0x2020, 0x01a8, 0x6024, 0xd0d4, 0x11e8,
+ 0x2009, 0x1869, 0x2104, 0xd084, 0x1138, 0x87ff, 0x1120, 0x2009,
+ 0x0043, 0x0804, 0xad4d, 0x0005, 0x87ff, 0x1de8, 0x2009, 0x0042,
+ 0x0804, 0xad4d, 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac,
+ 0x0d20, 0x6024, 0xc0cd, 0x6026, 0x0c00, 0xc0d4, 0x6026, 0xa890,
+ 0x602e, 0xa88c, 0x6032, 0x08e0, 0xd0fc, 0x0160, 0x908c, 0x0003,
+ 0x0120, 0x918e, 0x0003, 0x1904, 0x894a, 0x908c, 0x2020, 0x918e,
+ 0x2020, 0x0170, 0x0076, 0x00f6, 0x2c78, 0x080c, 0x1778, 0x00fe,
+ 0x007e, 0x87ff, 0x1120, 0x2009, 0x0042, 0x080c, 0xad4d, 0x0005,
+ 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac, 0x0d58, 0x6124,
+ 0xc1cd, 0x6126, 0x0c38, 0xd0fc, 0x0188, 0x908c, 0x2020, 0x918e,
+ 0x2020, 0x01a8, 0x9084, 0x0003, 0x908e, 0x0002, 0x0148, 0x87ff,
+ 0x1120, 0x2009, 0x0041, 0x080c, 0xad4d, 0x0005, 0x00b9, 0x0ce8,
+ 0x87ff, 0x1dd8, 0x2009, 0x0043, 0x080c, 0xad4d, 0x0cb0, 0x6110,
+ 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac, 0x0d20, 0x6124, 0xc1cd,
+ 0x6126, 0x0c00, 0x2009, 0x0004, 0x0019, 0x0005, 0x2009, 0x0001,
+ 0x0096, 0x080c, 0xc97a, 0x0518, 0x6014, 0x2048, 0xa982, 0xa800,
+ 0x6016, 0x9186, 0x0001, 0x1188, 0xa97c, 0x918c, 0x8100, 0x918e,
+ 0x8100, 0x1158, 0x00c6, 0x2061, 0x1a6e, 0x6200, 0xd28c, 0x1120,
+ 0x6204, 0x8210, 0x0208, 0x6206, 0x00ce, 0x080c, 0x6c23, 0x6014,
+ 0x904d, 0x0076, 0x2039, 0x0000, 0x190c, 0x8869, 0x007e, 0x009e,
+ 0x0005, 0x0156, 0x00c6, 0x2061, 0x1a6e, 0x6000, 0x81ff, 0x0110,
+ 0x9205, 0x0008, 0x9204, 0x6002, 0x00ce, 0x015e, 0x0005, 0x6800,
+ 0xd08c, 0x1138, 0x6808, 0x9005, 0x0120, 0x8001, 0x680a, 0x9085,
+ 0x0001, 0x0005, 0x2071, 0x1923, 0x7003, 0x0006, 0x7007, 0x0000,
+ 0x700f, 0x0000, 0x7013, 0x0001, 0x080c, 0x1060, 0x090c, 0x0d7d,
+ 0xa867, 0x0006, 0xa86b, 0x0001, 0xa8ab, 0xdcb0, 0xa89f, 0x0000,
+ 0x2900, 0x702e, 0x7033, 0x0000, 0x0005, 0x0096, 0x00e6, 0x2071,
+ 0x1923, 0x702c, 0x2048, 0x6a2c, 0x721e, 0x6b30, 0x7322, 0x6834,
+ 0x7026, 0xa896, 0x6838, 0x702a, 0xa89a, 0x6824, 0x7016, 0x683c,
+ 0x701a, 0x2009, 0x0028, 0x200a, 0x9005, 0x0148, 0x900e, 0x9188,
+ 0x000c, 0x8001, 0x1de0, 0x2100, 0x9210, 0x1208, 0x8318, 0xaa8e,
+ 0xab92, 0x7010, 0xd084, 0x0168, 0xc084, 0x7007, 0x0001, 0x700f,
+ 0x0000, 0x0006, 0x2009, 0x1b50, 0x2104, 0x9082, 0x0007, 0x200a,
+ 0x000e, 0xc095, 0x7012, 0x2008, 0x2001, 0x003b, 0x080c, 0x16a0,
+ 0x9006, 0x2071, 0x193c, 0x7002, 0x7006, 0x702a, 0x00ee, 0x009e,
+ 0x0005, 0x2009, 0x1b50, 0x2104, 0x9080, 0x0007, 0x200a, 0x0005,
+ 0x00e6, 0x0126, 0x0156, 0x2091, 0x8000, 0x2071, 0x1800, 0x7154,
+ 0x2001, 0x0008, 0x910a, 0x0638, 0x2001, 0x187d, 0x20ac, 0x9006,
+ 0x9080, 0x0008, 0x1f04, 0x8a08, 0x71c0, 0x9102, 0x02e0, 0x2071,
+ 0x1877, 0x20a9, 0x0007, 0x00c6, 0x080c, 0xac5a, 0x6023, 0x0009,
+ 0x6003, 0x0004, 0x601f, 0x0101, 0x0089, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x8b89, 0x012e, 0x1f04, 0x8a14, 0x9006, 0x00ce, 0x015e,
+ 0x012e, 0x00ee, 0x0005, 0x9085, 0x0001, 0x0cc8, 0x00e6, 0x00b6,
+ 0x0096, 0x0086, 0x0056, 0x0046, 0x0026, 0x7118, 0x720c, 0x7620,
+ 0x7004, 0xd084, 0x1128, 0x2021, 0x0024, 0x2029, 0x0002, 0x0020,
+ 0x2021, 0x002c, 0x2029, 0x000a, 0x080c, 0x1047, 0x090c, 0x0d7d,
+ 0x2900, 0x6016, 0x2058, 0xac66, 0x9006, 0xa802, 0xa806, 0xa86a,
+ 0xa87a, 0xa8aa, 0xa887, 0x0005, 0xa87f, 0x0020, 0x7008, 0xa89a,
+ 0x7010, 0xa89e, 0xae8a, 0xa8af, 0xffff, 0xa8b3, 0x0000, 0x8109,
+ 0x0160, 0x080c, 0x1047, 0x090c, 0x0d7d, 0xad66, 0x2b00, 0xa802,
+ 0x2900, 0xb806, 0x2058, 0x8109, 0x1da0, 0x002e, 0x004e, 0x005e,
+ 0x008e, 0x009e, 0x00be, 0x00ee, 0x0005, 0x2079, 0x0000, 0x2071,
+ 0x1923, 0x7004, 0x004b, 0x700c, 0x0002, 0x8a80, 0x8a79, 0x8a79,
+ 0x0005, 0x8a8a, 0x8ae0, 0x8ae0, 0x8ae0, 0x8ae1, 0x8af2, 0x8af2,
+ 0x700c, 0x0cba, 0x0126, 0x2091, 0x8000, 0x78a0, 0x79a0, 0x9106,
+ 0x1904, 0x8ad2, 0x7814, 0xd0bc, 0x1904, 0x8adb, 0x012e, 0x7018,
+ 0x910a, 0x1128, 0x7030, 0x9005, 0x1904, 0x8b24, 0x0005, 0x1210,
+ 0x7114, 0x910a, 0x9192, 0x000a, 0x0210, 0x2009, 0x000a, 0x2001,
+ 0x1888, 0x2014, 0x2001, 0x1935, 0x2004, 0x9100, 0x9202, 0x0e50,
+ 0x080c, 0x8c7d, 0x2200, 0x9102, 0x0208, 0x2208, 0x0096, 0x702c,
+ 0x2048, 0xa873, 0x0001, 0xa976, 0x080c, 0x8d86, 0x2100, 0xa87e,
+ 0xa86f, 0x0000, 0x009e, 0x0126, 0x2091, 0x8000, 0x2009, 0x1a20,
+ 0x2104, 0xc085, 0x200a, 0x700f, 0x0002, 0x012e, 0x080c, 0x115b,
+ 0x1de8, 0x0005, 0x78a0, 0x79a0, 0x9106, 0x0904, 0x8a92, 0x080c,
+ 0x8c55, 0x012e, 0x0005, 0x7810, 0xc0c5, 0x7812, 0x0804, 0x8a92,
+ 0x0005, 0x700c, 0x0002, 0x8ae6, 0x8ae9, 0x8ae8, 0x080c, 0x8a88,
+ 0x0005, 0x8001, 0x700e, 0x0096, 0x702c, 0x2048, 0xa974, 0x009e,
+ 0x0011, 0x0ca0, 0x0005, 0x0096, 0x702c, 0x2048, 0x7018, 0x9100,
+ 0x7214, 0x921a, 0x1130, 0x701c, 0xa88e, 0x7020, 0xa892, 0x9006,
+ 0x0068, 0x0006, 0x080c, 0x8d86, 0x2100, 0xaa8c, 0x9210, 0xaa8e,
+ 0x1220, 0xa890, 0x9081, 0x0000, 0xa892, 0x000e, 0x009e, 0x0126,
+ 0x2091, 0x8000, 0x78a2, 0x701a, 0x080c, 0x8c55, 0x012e, 0x0005,
+ 0x00e6, 0x2071, 0x1923, 0x700c, 0x0002, 0x8b22, 0x8b22, 0x8b20,
+ 0x700f, 0x0001, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000, 0x7030,
+ 0x9005, 0x0508, 0x2078, 0x7814, 0x2048, 0xae88, 0x00b6, 0x2059,
+ 0x0000, 0x080c, 0x8b92, 0x00be, 0x01b0, 0x00e6, 0x2071, 0x193c,
+ 0x080c, 0x8bd9, 0x00ee, 0x0178, 0x0096, 0x080c, 0x1060, 0x2900,
+ 0x009e, 0x0148, 0xa8aa, 0x04d1, 0x0041, 0x2001, 0x1946, 0x2003,
+ 0x0000, 0x012e, 0x08c8, 0x012e, 0x0005, 0x00d6, 0x00c6, 0x0086,
+ 0x00a6, 0x2940, 0x2650, 0x2600, 0x9005, 0x0180, 0xa864, 0x9084,
+ 0x000f, 0x2068, 0x9d88, 0x1eab, 0x2165, 0x0056, 0x2029, 0x0000,
+ 0x080c, 0x8d0b, 0x080c, 0x1e81, 0x1dd8, 0x005e, 0x00ae, 0x2001,
+ 0x187f, 0x2004, 0xa88a, 0x00c6, 0x2f60, 0x080c, 0x1778, 0x00ce,
+ 0x781f, 0x0101, 0x7813, 0x0000, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x8be8, 0x012e, 0x008e, 0x00ce, 0x00de, 0x0005, 0x7030, 0x9005,
+ 0x0138, 0x2078, 0x780c, 0x7032, 0x2001, 0x1946, 0x2003, 0x0001,
+ 0x0005, 0x00e6, 0x2071, 0x1923, 0x7030, 0x600e, 0x2c00, 0x7032,
+ 0x00ee, 0x0005, 0x00d6, 0x00c6, 0x0026, 0x9b80, 0x8e54, 0x2005,
+ 0x906d, 0x090c, 0x0d7d, 0x9b80, 0x8e4c, 0x2005, 0x9065, 0x090c,
+ 0x0d7d, 0x6114, 0x2600, 0x9102, 0x0248, 0x6828, 0x9102, 0x02f0,
+ 0x9085, 0x0001, 0x002e, 0x00ce, 0x00de, 0x0005, 0x6804, 0xd094,
+ 0x0148, 0x6854, 0xd084, 0x1178, 0xc085, 0x6856, 0x2011, 0x8026,
+ 0x080c, 0x4b52, 0x684c, 0x0096, 0x904d, 0x090c, 0x0d7d, 0xa804,
+ 0x8000, 0xa806, 0x009e, 0x9006, 0x2030, 0x0c20, 0x6854, 0xd08c,
+ 0x1d08, 0xc08d, 0x6856, 0x2011, 0x8025, 0x080c, 0x4b52, 0x684c,
+ 0x0096, 0x904d, 0x090c, 0x0d7d, 0xa800, 0x8000, 0xa802, 0x009e,
+ 0x0888, 0x7000, 0x2019, 0x0008, 0x8319, 0x7104, 0x9102, 0x1118,
+ 0x2300, 0x9005, 0x0020, 0x0210, 0x9302, 0x0008, 0x8002, 0x0005,
+ 0x00d6, 0x7814, 0x9005, 0x090c, 0x0d7d, 0x781c, 0x9084, 0x0101,
+ 0x9086, 0x0101, 0x190c, 0x0d7d, 0x7827, 0x0000, 0x2069, 0x193c,
+ 0x6804, 0x9080, 0x193e, 0x2f08, 0x2102, 0x6904, 0x8108, 0x9182,
+ 0x0008, 0x0208, 0x900e, 0x6906, 0x9180, 0x193e, 0x2003, 0x0000,
+ 0x00de, 0x0005, 0x0096, 0x00c6, 0x2060, 0x6014, 0x2048, 0xa8a8,
+ 0x0096, 0x2048, 0x9005, 0x190c, 0x1079, 0x009e, 0xa8ab, 0x0000,
+ 0x080c, 0x0ff9, 0x080c, 0xacb0, 0x00ce, 0x009e, 0x0005, 0x6020,
+ 0x9086, 0x0009, 0x1128, 0x601c, 0xd0c4, 0x0110, 0x9006, 0x0005,
+ 0x9085, 0x0001, 0x0005, 0x6000, 0x9086, 0x0000, 0x0178, 0x6010,
+ 0x9005, 0x0150, 0x00b6, 0x2058, 0x080c, 0x8f89, 0x00be, 0x6013,
+ 0x0000, 0x601b, 0x0000, 0x0010, 0x2c00, 0x0861, 0x0005, 0x2009,
+ 0x1927, 0x210c, 0xd194, 0x0005, 0x00e6, 0x2071, 0x1923, 0x7110,
+ 0xc194, 0xd19c, 0x1118, 0xc185, 0x7007, 0x0000, 0x7112, 0x2001,
+ 0x003b, 0x080c, 0x16a0, 0x00ee, 0x0005, 0x7814, 0xd0bc, 0x1108,
+ 0x0005, 0x7810, 0xc0c5, 0x7812, 0x0cc0, 0x0096, 0x00d6, 0x9006,
+ 0x7006, 0x700e, 0x701a, 0x701e, 0x7022, 0x7016, 0x702a, 0x7026,
+ 0x702f, 0x0000, 0x080c, 0x8dd4, 0x0170, 0x080c, 0x8e09, 0x0158,
+ 0x2900, 0x7002, 0x700a, 0x701a, 0x7013, 0x0001, 0x701f, 0x000a,
+ 0x00de, 0x009e, 0x0005, 0x900e, 0x0cd8, 0x00e6, 0x0096, 0x0086,
+ 0x00d6, 0x00c6, 0x2071, 0x1930, 0x721c, 0x2100, 0x9202, 0x1618,
+ 0x080c, 0x8e09, 0x090c, 0x0d7d, 0x7018, 0x9005, 0x1160, 0x2900,
+ 0x7002, 0x700a, 0x701a, 0x9006, 0x7006, 0x700e, 0xa806, 0xa802,
+ 0x7012, 0x701e, 0x0038, 0x2040, 0xa806, 0x2900, 0xa002, 0x701a,
+ 0xa803, 0x0000, 0x7010, 0x8000, 0x7012, 0x701c, 0x9080, 0x000a,
+ 0x701e, 0x721c, 0x08d0, 0x721c, 0x00ce, 0x00de, 0x008e, 0x009e,
+ 0x00ee, 0x0005, 0x0096, 0x0156, 0x0136, 0x0146, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x1930, 0x7300, 0x831f, 0x831e, 0x831e,
+ 0x9384, 0x003f, 0x20e8, 0x939c, 0xffc0, 0x9398, 0x0003, 0x7104,
+ 0x080c, 0x8d86, 0x810c, 0x2100, 0x9318, 0x8003, 0x2228, 0x2021,
+ 0x0078, 0x9402, 0x9532, 0x0208, 0x2028, 0x2500, 0x8004, 0x20a8,
+ 0x23a0, 0xa001, 0xa001, 0x4005, 0x2508, 0x080c, 0x8d8f, 0x2130,
+ 0x7014, 0x9600, 0x7016, 0x2600, 0x711c, 0x9102, 0x701e, 0x7004,
+ 0x9600, 0x2008, 0x9082, 0x000a, 0x1190, 0x7000, 0x2048, 0xa800,
+ 0x9005, 0x1148, 0x2009, 0x0001, 0x0026, 0x080c, 0x8c7d, 0x002e,
+ 0x7000, 0x2048, 0xa800, 0x7002, 0x7007, 0x0000, 0x0008, 0x7106,
+ 0x2500, 0x9212, 0x1904, 0x8cbc, 0x012e, 0x00ee, 0x014e, 0x013e,
+ 0x015e, 0x009e, 0x0005, 0x0016, 0x0026, 0x00e6, 0x0126, 0x2091,
+ 0x8000, 0x9580, 0x8e4c, 0x2005, 0x9075, 0x090c, 0x0d7d, 0x080c,
+ 0x8d61, 0x012e, 0x9580, 0x8e48, 0x2005, 0x9075, 0x090c, 0x0d7d,
+ 0x0156, 0x0136, 0x01c6, 0x0146, 0x01d6, 0x831f, 0x831e, 0x831e,
+ 0x9384, 0x003f, 0x20e0, 0x9384, 0xffc0, 0x9100, 0x2098, 0xa860,
+ 0x20e8, 0xa95c, 0x2c05, 0x9100, 0x20a0, 0x20a9, 0x0002, 0x4003,
+ 0x2e0c, 0x2d00, 0x0002, 0x8d4b, 0x8d4b, 0x8d4d, 0x8d4b, 0x8d4d,
+ 0x8d4b, 0x8d4b, 0x8d4b, 0x8d4b, 0x8d4b, 0x8d53, 0x8d4b, 0x8d53,
+ 0x8d4b, 0x8d4b, 0x8d4b, 0x080c, 0x0d7d, 0x4104, 0x20a9, 0x0002,
+ 0x4002, 0x4003, 0x0028, 0x20a9, 0x0002, 0x4003, 0x4104, 0x4003,
+ 0x01de, 0x014e, 0x01ce, 0x013e, 0x015e, 0x00ee, 0x002e, 0x001e,
+ 0x0005, 0x0096, 0x7014, 0x8001, 0x7016, 0x710c, 0x2110, 0x00f1,
+ 0x810c, 0x9188, 0x0003, 0x7308, 0x8210, 0x9282, 0x000a, 0x1198,
+ 0x7008, 0x2048, 0xa800, 0x9005, 0x0158, 0x0006, 0x080c, 0x8e18,
+ 0x009e, 0xa807, 0x0000, 0x2900, 0x700a, 0x7010, 0x8001, 0x7012,
+ 0x700f, 0x0000, 0x0008, 0x720e, 0x009e, 0x0005, 0x0006, 0x810b,
+ 0x810b, 0x2100, 0x810b, 0x9100, 0x2008, 0x000e, 0x0005, 0x0006,
+ 0x0026, 0x2100, 0x9005, 0x0158, 0x9092, 0x000c, 0x0240, 0x900e,
+ 0x8108, 0x9082, 0x000c, 0x1de0, 0x002e, 0x000e, 0x0005, 0x900e,
+ 0x0cd8, 0x2d00, 0x90b8, 0x0008, 0x2031, 0x8dd2, 0x901e, 0x6808,
+ 0x9005, 0x0108, 0x8318, 0x690c, 0x910a, 0x0248, 0x0140, 0x8318,
+ 0x6810, 0x9112, 0x0220, 0x0118, 0x8318, 0x2208, 0x0cd0, 0x233a,
+ 0x6804, 0xd084, 0x2300, 0x2021, 0x0001, 0x1150, 0x9082, 0x0003,
+ 0x0967, 0x0a67, 0x8420, 0x9082, 0x0007, 0x0967, 0x0a67, 0x0cd0,
+ 0x9082, 0x0002, 0x0967, 0x0a67, 0x8420, 0x9082, 0x0005, 0x0967,
+ 0x0a67, 0x0cd0, 0x6c1a, 0x0005, 0x0096, 0x0046, 0x0126, 0x2091,
+ 0x8000, 0x2b00, 0x9080, 0x8e50, 0x2005, 0x9005, 0x090c, 0x0d7d,
+ 0x2004, 0x90a0, 0x000a, 0x080c, 0x1060, 0x01d0, 0x2900, 0x7026,
+ 0xa803, 0x0000, 0xa807, 0x0000, 0x080c, 0x1060, 0x0188, 0x7024,
+ 0xa802, 0xa807, 0x0000, 0x2900, 0x7026, 0x94a2, 0x000a, 0x0110,
+ 0x0208, 0x0c90, 0x9085, 0x0001, 0x012e, 0x004e, 0x009e, 0x0005,
+ 0x7024, 0x9005, 0x0dc8, 0x2048, 0xac00, 0x080c, 0x1079, 0x2400,
+ 0x0cc0, 0x0126, 0x2091, 0x8000, 0x7024, 0x2048, 0x9005, 0x0130,
+ 0xa800, 0x7026, 0xa803, 0x0000, 0xa807, 0x0000, 0x012e, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x7024, 0xa802, 0x2900, 0x7026, 0x012e,
+ 0x0005, 0x0096, 0x9e80, 0x0009, 0x2004, 0x9005, 0x0138, 0x2048,
+ 0xa800, 0x0006, 0x080c, 0x1079, 0x000e, 0x0cb8, 0x009e, 0x0005,
+ 0x0096, 0x7008, 0x9005, 0x0138, 0x2048, 0xa800, 0x0006, 0x080c,
+ 0x1079, 0x000e, 0x0cb8, 0x9006, 0x7002, 0x700a, 0x7006, 0x700e,
+ 0x701a, 0x701e, 0x7022, 0x702a, 0x7026, 0x702e, 0x009e, 0x0005,
+ 0x1a6c, 0x0000, 0x0000, 0x0000, 0x1930, 0x0000, 0x0000, 0x0000,
+ 0x1888, 0x0000, 0x0000, 0x0000, 0x1877, 0x0000, 0x0000, 0x0000,
+ 0x00e6, 0x00c6, 0x00b6, 0x00a6, 0xa8a8, 0x2040, 0x2071, 0x1877,
+ 0x080c, 0x8f74, 0xa067, 0x0023, 0x6010, 0x905d, 0x0904, 0x8f49,
+ 0xb814, 0xa06e, 0xb910, 0xa172, 0xb9a0, 0xa176, 0x2001, 0x0003,
+ 0xa07e, 0xa834, 0xa082, 0xa07b, 0x0000, 0xa898, 0x9005, 0x0118,
+ 0xa078, 0xc085, 0xa07a, 0x2858, 0x2031, 0x0018, 0xa068, 0x908a,
+ 0x0019, 0x1a0c, 0x0d7d, 0x2020, 0x2050, 0x2940, 0xa864, 0x90bc,
+ 0x00ff, 0x908c, 0x000f, 0x91e0, 0x1eab, 0x2c65, 0x9786, 0x0024,
+ 0x2c05, 0x1590, 0x908a, 0x0036, 0x1a0c, 0x0d7d, 0x9082, 0x001b,
+ 0x0002, 0x8eb4, 0x8eb4, 0x8eb6, 0x8eb4, 0x8eb4, 0x8eb4, 0x8eb8,
+ 0x8eb4, 0x8eb4, 0x8eb4, 0x8eba, 0x8eb4, 0x8eb4, 0x8eb4, 0x8ebc,
+ 0x8eb4, 0x8eb4, 0x8eb4, 0x8ebe, 0x8eb4, 0x8eb4, 0x8eb4, 0x8ec0,
+ 0x8eb4, 0x8eb4, 0x8eb4, 0x8ec2, 0x080c, 0x0d7d, 0xa180, 0x04b8,
+ 0xa190, 0x04a8, 0xa1a0, 0x0498, 0xa1b0, 0x0488, 0xa1c0, 0x0478,
+ 0xa1d0, 0x0468, 0xa1e0, 0x0458, 0x908a, 0x0034, 0x1a0c, 0x0d7d,
+ 0x9082, 0x001b, 0x0002, 0x8ee6, 0x8ee4, 0x8ee4, 0x8ee4, 0x8ee4,
+ 0x8ee4, 0x8ee8, 0x8ee4, 0x8ee4, 0x8ee4, 0x8ee4, 0x8ee4, 0x8eea,
+ 0x8ee4, 0x8ee4, 0x8ee4, 0x8ee4, 0x8ee4, 0x8eec, 0x8ee4, 0x8ee4,
+ 0x8ee4, 0x8ee4, 0x8ee4, 0x8eee, 0x080c, 0x0d7d, 0xa180, 0x0038,
+ 0xa198, 0x0028, 0xa1b0, 0x0018, 0xa1c8, 0x0008, 0xa1e0, 0x2600,
+ 0x0002, 0x8f0a, 0x8f0c, 0x8f0e, 0x8f10, 0x8f12, 0x8f14, 0x8f16,
+ 0x8f18, 0x8f1a, 0x8f1c, 0x8f1e, 0x8f20, 0x8f22, 0x8f24, 0x8f26,
+ 0x8f28, 0x8f2a, 0x8f2c, 0x8f2e, 0x8f30, 0x8f32, 0x8f34, 0x8f36,
+ 0x8f38, 0x8f3a, 0x080c, 0x0d7d, 0xb9e2, 0x0468, 0xb9de, 0x0458,
+ 0xb9da, 0x0448, 0xb9d6, 0x0438, 0xb9d2, 0x0428, 0xb9ce, 0x0418,
+ 0xb9ca, 0x0408, 0xb9c6, 0x00f8, 0xb9c2, 0x00e8, 0xb9be, 0x00d8,
+ 0xb9ba, 0x00c8, 0xb9b6, 0x00b8, 0xb9b2, 0x00a8, 0xb9ae, 0x0098,
+ 0xb9aa, 0x0088, 0xb9a6, 0x0078, 0xb9a2, 0x0068, 0xb99e, 0x0058,
+ 0xb99a, 0x0048, 0xb996, 0x0038, 0xb992, 0x0028, 0xb98e, 0x0018,
+ 0xb98a, 0x0008, 0xb986, 0x8631, 0x8421, 0x0130, 0x080c, 0x1e81,
+ 0x090c, 0x0d7d, 0x0804, 0x8e8e, 0x00ae, 0x00be, 0x00ce, 0x00ee,
+ 0x0005, 0xa86c, 0xa06e, 0xa870, 0xa072, 0xa077, 0x00ff, 0x9006,
+ 0x0804, 0x8e70, 0x0006, 0x0016, 0x00b6, 0x6010, 0x2058, 0xb810,
+ 0x9005, 0x01b0, 0x2001, 0x1924, 0x2004, 0x9005, 0x0188, 0x2001,
+ 0x1800, 0x2004, 0x9086, 0x0003, 0x1158, 0x0036, 0x0046, 0xbba0,
+ 0x2021, 0x0004, 0x2011, 0x8014, 0x080c, 0x4b52, 0x004e, 0x003e,
+ 0x00be, 0x001e, 0x000e, 0x0005, 0x9016, 0x710c, 0xa834, 0x910a,
+ 0xa936, 0x7008, 0x9005, 0x0120, 0x8210, 0x910a, 0x0238, 0x0130,
+ 0x7010, 0x8210, 0x910a, 0x0210, 0x0108, 0x0cd8, 0xaa8a, 0xa26a,
+ 0x0005, 0x00f6, 0x00d6, 0x0036, 0x2079, 0x0300, 0x781b, 0x0200,
+ 0x7818, 0xd094, 0x1dd8, 0x781b, 0x0202, 0xa001, 0xa001, 0x7818,
+ 0xd094, 0x1da0, 0xb8ac, 0x9005, 0x01b8, 0x2068, 0x2079, 0x0000,
+ 0x2c08, 0x911e, 0x1118, 0x680c, 0xb8ae, 0x0060, 0x9106, 0x0140,
+ 0x2d00, 0x2078, 0x680c, 0x9005, 0x090c, 0x0d7d, 0x2068, 0x0cb0,
+ 0x6b0c, 0x7b0e, 0x600f, 0x0000, 0x2079, 0x0300, 0x781b, 0x0200,
+ 0x003e, 0x00de, 0x00fe, 0x0005, 0x00e6, 0x00d6, 0x0096, 0x00c6,
+ 0x0036, 0x0126, 0x2091, 0x8000, 0x0156, 0x20a9, 0x01ff, 0x2071,
+ 0x0300, 0x701b, 0x0200, 0x7018, 0xd094, 0x0110, 0x1f04, 0x8fc9,
+ 0x701b, 0x0202, 0xa001, 0xa001, 0x7018, 0xd094, 0x1d90, 0xb8ac,
+ 0x9005, 0x01e8, 0x2060, 0x600c, 0xb8ae, 0x6024, 0xc08d, 0x6026,
+ 0x6003, 0x0004, 0x601b, 0x0000, 0x6013, 0x0000, 0x601f, 0x0101,
+ 0x6014, 0x2048, 0xa88b, 0x0000, 0xa8a8, 0xa8ab, 0x0000, 0x904d,
+ 0x090c, 0x0d7d, 0x080c, 0x1079, 0x080c, 0x8b89, 0x0c00, 0x2071,
+ 0x0300, 0x701b, 0x0200, 0x015e, 0x012e, 0x003e, 0x00ce, 0x009e,
+ 0x00de, 0x00ee, 0x0005, 0x00c6, 0x00b6, 0x0016, 0x0006, 0x0156,
+ 0x080c, 0x2661, 0x015e, 0x11b0, 0x080c, 0x6632, 0x190c, 0x0d7d,
+ 0x000e, 0x001e, 0xb912, 0xb816, 0x080c, 0xac5a, 0x0140, 0x2b00,
+ 0x6012, 0x6023, 0x0001, 0x2009, 0x0001, 0x080c, 0xad4d, 0x00be,
+ 0x00ce, 0x0005, 0x000e, 0x001e, 0x0cd0, 0x0066, 0x6000, 0x90b2,
+ 0x0016, 0x1a0c, 0x0d7d, 0x0013, 0x006e, 0x0005, 0x903e, 0x903e,
+ 0x903e, 0x9040, 0x9089, 0x903e, 0x903e, 0x903e, 0x90f0, 0x903e,
+ 0x9128, 0x903e, 0x903e, 0x903e, 0x903e, 0x903e, 0x080c, 0x0d7d,
+ 0x9182, 0x0040, 0x0002, 0x9053, 0x9053, 0x9053, 0x9053, 0x9053,
+ 0x9053, 0x9053, 0x9053, 0x9053, 0x9055, 0x9066, 0x9053, 0x9053,
+ 0x9053, 0x9053, 0x9077, 0x080c, 0x0d7d, 0x0096, 0x6114, 0x2148,
+ 0xa87b, 0x0000, 0x6010, 0x00b6, 0x2058, 0xb8bb, 0x0500, 0x00be,
+ 0x080c, 0x6bee, 0x080c, 0xacb0, 0x009e, 0x0005, 0x080c, 0x96d5,
+ 0x00d6, 0x6114, 0x080c, 0xc97a, 0x0130, 0x0096, 0x6114, 0x2148,
+ 0x080c, 0x6dee, 0x009e, 0x00de, 0x080c, 0xacb0, 0x0005, 0x080c,
+ 0x96d5, 0x080c, 0x3240, 0x6114, 0x0096, 0x2148, 0x080c, 0xc97a,
+ 0x0120, 0xa87b, 0x0029, 0x080c, 0x6dee, 0x009e, 0x080c, 0xacb0,
+ 0x0005, 0x601b, 0x0000, 0x9182, 0x0040, 0x0096, 0x0002, 0x90a4,
+ 0x90a4, 0x90a4, 0x90a4, 0x90a4, 0x90a4, 0x90a4, 0x90a4, 0x90a6,
+ 0x90a4, 0x90a4, 0x90a4, 0x90ec, 0x90a4, 0x90a4, 0x90a4, 0x90a4,
+ 0x90a4, 0x90a4, 0x90ad, 0x90a4, 0x080c, 0x0d7d, 0x6114, 0x2148,
+ 0xa938, 0x918e, 0xffff, 0x0904, 0x90ec, 0x6024, 0xd08c, 0x15c0,
+ 0x00e6, 0x6114, 0x2148, 0x080c, 0x8e58, 0x0096, 0xa8a8, 0x2048,
+ 0x080c, 0x6b86, 0x009e, 0xa8ab, 0x0000, 0x6010, 0x9005, 0x0128,
+ 0x00b6, 0x2058, 0x080c, 0x8f89, 0x00be, 0xae88, 0x00b6, 0x2059,
+ 0x0000, 0x080c, 0x8b92, 0x00be, 0x01e0, 0x2071, 0x193c, 0x080c,
+ 0x8bd9, 0x01b8, 0x9086, 0x0001, 0x1128, 0x2001, 0x1946, 0x2004,
+ 0x9005, 0x1178, 0x0096, 0x080c, 0x1047, 0x2900, 0x009e, 0x0148,
+ 0xa8aa, 0x00f6, 0x2c78, 0x080c, 0x8b4d, 0x00fe, 0x00ee, 0x009e,
+ 0x0005, 0x080c, 0x8b89, 0x0cd0, 0x080c, 0x91a4, 0x009e, 0x0005,
+ 0x9182, 0x0040, 0x0096, 0x0002, 0x9104, 0x9104, 0x9104, 0x9106,
+ 0x9104, 0x9104, 0x9104, 0x9126, 0x9104, 0x9104, 0x9104, 0x9104,
+ 0x9104, 0x9104, 0x9104, 0x9104, 0x080c, 0x0d7d, 0x6003, 0x0003,
+ 0x6106, 0x6014, 0x2048, 0xa8ac, 0xa836, 0xa8b0, 0xa83a, 0xa847,
+ 0x0000, 0xa84b, 0x0000, 0xa884, 0x9092, 0x199a, 0x0210, 0x2001,
+ 0x1999, 0x8003, 0x8013, 0x8213, 0x9210, 0x621a, 0x080c, 0x1c10,
+ 0x2009, 0x8030, 0x080c, 0x92f7, 0x009e, 0x0005, 0x080c, 0x0d7d,
+ 0x080c, 0x96d5, 0x6114, 0x2148, 0xa87b, 0x0000, 0x6010, 0x00b6,
+ 0x2058, 0xb8bb, 0x0500, 0x00be, 0x080c, 0x6dee, 0x080c, 0xacb0,
+ 0x009e, 0x0005, 0x080c, 0xa91e, 0x6144, 0xd1fc, 0x0120, 0xd1ac,
+ 0x1110, 0x6003, 0x0003, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0d7d,
+ 0x0096, 0x0023, 0x009e, 0x080c, 0xa93a, 0x0005, 0x915e, 0x915e,
+ 0x915e, 0x9160, 0x9171, 0x915e, 0x915e, 0x915e, 0x915e, 0x915e,
+ 0x915e, 0x915e, 0x915e, 0x915e, 0x915e, 0x915e, 0x080c, 0x0d7d,
+ 0x080c, 0xaab5, 0x6114, 0x2148, 0xa87b, 0x0006, 0x6010, 0x00b6,
+ 0x2058, 0xb8bb, 0x0500, 0x00be, 0x080c, 0x6dee, 0x080c, 0xacb0,
+ 0x0005, 0x0491, 0x0005, 0x080c, 0xa91e, 0x6000, 0x6144, 0xd1fc,
+ 0x0130, 0xd1ac, 0x1120, 0x6003, 0x0003, 0x2009, 0x0003, 0x908a,
+ 0x0016, 0x1a0c, 0x0d7d, 0x0096, 0x0033, 0x009e, 0x0106, 0x080c,
+ 0xa93a, 0x010e, 0x0005, 0x919b, 0x919b, 0x919b, 0x919d, 0x91a4,
+ 0x919b, 0x919b, 0x919b, 0x919b, 0x919b, 0x919b, 0x919b, 0x919b,
+ 0x919b, 0x919b, 0x919b, 0x080c, 0x0d7d, 0x0036, 0x00e6, 0x080c,
+ 0xaab5, 0x00ee, 0x003e, 0x0005, 0x6024, 0xd08c, 0x11f0, 0x00f6,
+ 0x00e6, 0x601b, 0x0000, 0x6014, 0x2048, 0x6010, 0x9005, 0x0128,
+ 0x00b6, 0x2058, 0x080c, 0x8f89, 0x00be, 0x2071, 0x193c, 0x080c,
+ 0x8bd9, 0x0160, 0x2001, 0x187f, 0x2004, 0xa88a, 0x2031, 0x0000,
+ 0x2c78, 0x080c, 0x8b4d, 0x00ee, 0x00fe, 0x0005, 0x0096, 0xa88b,
+ 0x0000, 0xa8a8, 0x2048, 0x080c, 0x1079, 0x009e, 0xa8ab, 0x0000,
+ 0x080c, 0x8b89, 0x0c80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x187a, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0126, 0x2091, 0x8000, 0x0036, 0x0046,
+ 0x20a9, 0x0010, 0x9006, 0x8004, 0x8086, 0x818e, 0x1208, 0x9200,
+ 0x1f04, 0x91ec, 0x8086, 0x818e, 0x004e, 0x003e, 0x012e, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x0076, 0x0156, 0x20a9, 0x0010, 0x9005,
+ 0x01c8, 0x911a, 0x12b8, 0x8213, 0x818d, 0x0228, 0x911a, 0x1220,
+ 0x1f04, 0x9203, 0x0028, 0x911a, 0x2308, 0x8210, 0x1f04, 0x9203,
+ 0x0006, 0x3200, 0x9084, 0xefff, 0x2080, 0x000e, 0x015e, 0x007e,
+ 0x012e, 0x0005, 0x0006, 0x3200, 0x9085, 0x1000, 0x0ca8, 0x0126,
+ 0x2091, 0x2800, 0x2079, 0x19e6, 0x012e, 0x00d6, 0x2069, 0x19e6,
+ 0x6803, 0x0005, 0x0156, 0x0146, 0x01d6, 0x20e9, 0x0000, 0x2069,
+ 0x0200, 0x080c, 0xa713, 0x04a9, 0x080c, 0xa6fe, 0x0491, 0x080c,
+ 0xa701, 0x0479, 0x080c, 0xa704, 0x0461, 0x080c, 0xa707, 0x0449,
+ 0x080c, 0xa70a, 0x0431, 0x080c, 0xa70d, 0x0419, 0x080c, 0xa710,
+ 0x0401, 0x01de, 0x014e, 0x015e, 0x6857, 0x0000, 0x00f6, 0x2079,
+ 0x0380, 0x00f9, 0x7807, 0x0003, 0x7803, 0x0000, 0x7803, 0x0001,
+ 0x2069, 0x0004, 0x2d04, 0x9084, 0xfffe, 0x9085, 0x8000, 0x206a,
+ 0x2069, 0x0100, 0x6828, 0x9084, 0xfffc, 0x682a, 0x00fe, 0x00de,
+ 0x0005, 0x20a9, 0x0020, 0x20a1, 0x0240, 0x2001, 0x0000, 0x4004,
+ 0x0005, 0x00c6, 0x7803, 0x0000, 0x9006, 0x7827, 0x0030, 0x782b,
+ 0x0400, 0x7827, 0x0031, 0x782b, 0x1af1, 0x781f, 0xff00, 0x781b,
+ 0xff00, 0x2061, 0x1ae6, 0x602f, 0x19e6, 0x6033, 0x1800, 0x6037,
+ 0x1a02, 0x603b, 0x1eab, 0x603f, 0x1ebb, 0x6042, 0x6047, 0x1abc,
+ 0x00ce, 0x0005, 0x2001, 0x0382, 0x2004, 0x9084, 0x0007, 0x9086,
+ 0x0001, 0x01b0, 0x00c6, 0x6146, 0x600f, 0x0000, 0x2c08, 0x2061,
+ 0x19e6, 0x602c, 0x8000, 0x602e, 0x601c, 0x9005, 0x0130, 0x9080,
+ 0x0003, 0x2102, 0x611e, 0x00ce, 0x0005, 0x6122, 0x611e, 0x0cd8,
+ 0x6146, 0x2c08, 0x2001, 0x0012, 0x080c, 0xa90f, 0x0005, 0x0016,
+ 0x2009, 0x8020, 0x6146, 0x2c08, 0x2001, 0x0382, 0x2004, 0x9084,
+ 0x0007, 0x9086, 0x0001, 0x1128, 0x2001, 0x0019, 0x080c, 0xa90f,
+ 0x0088, 0x00c6, 0x2061, 0x19e6, 0x602c, 0x8000, 0x602e, 0x600c,
+ 0x9005, 0x0128, 0x9080, 0x0003, 0x2102, 0x610e, 0x0010, 0x6112,
+ 0x610e, 0x00ce, 0x001e, 0x0005, 0x2001, 0x0382, 0x2004, 0x9084,
+ 0x0007, 0x9086, 0x0001, 0x0198, 0x00c6, 0x6146, 0x600f, 0x0000,
+ 0x2c08, 0x2061, 0x19e6, 0x6044, 0x9005, 0x0130, 0x9080, 0x0003,
+ 0x2102, 0x6146, 0x00ce, 0x0005, 0x614a, 0x6146, 0x0cd8, 0x6146,
+ 0x600f, 0x0000, 0x2c08, 0x2001, 0x0013, 0x080c, 0xa90f, 0x0005,
+ 0x6044, 0xd0dc, 0x0110, 0x080c, 0xa3ac, 0x0005, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x00b6, 0x0096, 0x0076, 0x0066, 0x0056, 0x0036,
+ 0x0026, 0x0016, 0x0006, 0x0126, 0x902e, 0x2071, 0x19e6, 0x7648,
+ 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff, 0x0904, 0x9383, 0x6010,
+ 0x2058, 0xb8a0, 0x9206, 0x1904, 0x937e, 0x87ff, 0x0120, 0x605c,
+ 0x9106, 0x1904, 0x937e, 0x704c, 0x9c06, 0x1178, 0x0036, 0x2019,
+ 0x0001, 0x080c, 0xa1b8, 0x703f, 0x0000, 0x9006, 0x704e, 0x706a,
+ 0x7052, 0x706e, 0x003e, 0x2029, 0x0001, 0x0811, 0x7048, 0x9c36,
+ 0x1110, 0x660c, 0x764a, 0x7044, 0x9c36, 0x1140, 0x2c00, 0x9f36,
+ 0x0118, 0x2f00, 0x7046, 0x0010, 0x7047, 0x0000, 0x660c, 0x0066,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x080c, 0xc97a, 0x01f0, 0x6014, 0x2048, 0x6020, 0x9086, 0x0003,
+ 0x1588, 0x6004, 0x9086, 0x0040, 0x090c, 0xa3ac, 0xa867, 0x0103,
+ 0xab7a, 0xa877, 0x0000, 0x0016, 0x0036, 0x0076, 0x080c, 0xcc7f,
+ 0x080c, 0xe79d, 0x080c, 0x6dee, 0x007e, 0x003e, 0x001e, 0x080c,
+ 0xcb6b, 0x080c, 0xaceb, 0x00ce, 0x0804, 0x931c, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0x931c, 0x012e, 0x000e, 0x001e, 0x002e, 0x003e,
+ 0x005e, 0x006e, 0x007e, 0x009e, 0x00be, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x6020, 0x9086, 0x0006, 0x1158, 0x0016, 0x0036,
+ 0x0076, 0x080c, 0xe79d, 0x080c, 0xe3e8, 0x007e, 0x003e, 0x001e,
+ 0x08c0, 0x6020, 0x9086, 0x0009, 0x1168, 0xa87b, 0x0006, 0x0016,
+ 0x0036, 0x0076, 0x080c, 0x6dee, 0x080c, 0xacb0, 0x007e, 0x003e,
+ 0x001e, 0x0848, 0x6020, 0x9086, 0x000a, 0x0904, 0x9368, 0x0804,
+ 0x9361, 0x0006, 0x0066, 0x0096, 0x00c6, 0x00d6, 0x00f6, 0x9036,
+ 0x0126, 0x2091, 0x8000, 0x2079, 0x19e6, 0x7848, 0x9065, 0x0904,
+ 0x941d, 0x600c, 0x0006, 0x600f, 0x0000, 0x784c, 0x9c06, 0x11a0,
+ 0x0036, 0x2019, 0x0001, 0x080c, 0xa1b8, 0x783f, 0x0000, 0x901e,
+ 0x7b4e, 0x7b6a, 0x7b52, 0x7b6e, 0x003e, 0x000e, 0x9005, 0x1118,
+ 0x600c, 0x600f, 0x0000, 0x0006, 0x00e6, 0x2f70, 0x080c, 0x9300,
+ 0x00ee, 0x080c, 0xc97a, 0x0548, 0x6014, 0x2048, 0x6020, 0x9086,
+ 0x0003, 0x15a8, 0x3e08, 0x918e, 0x0002, 0x1188, 0x6010, 0x9005,
+ 0x0170, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0140, 0x6048,
+ 0x9005, 0x11c0, 0x2001, 0x1987, 0x2004, 0x604a, 0x0098, 0x6004,
+ 0x9086, 0x0040, 0x090c, 0xa3ac, 0xa867, 0x0103, 0xab7a, 0xa877,
+ 0x0000, 0x080c, 0x6de2, 0x080c, 0xcb6b, 0x6044, 0xc0fc, 0x6046,
+ 0x080c, 0xaceb, 0x000e, 0x0804, 0x93c6, 0x7e4a, 0x7e46, 0x012e,
+ 0x00fe, 0x00de, 0x00ce, 0x009e, 0x006e, 0x000e, 0x0005, 0x6020,
+ 0x9086, 0x0006, 0x1118, 0x080c, 0xe3e8, 0x0c38, 0x6020, 0x9086,
+ 0x0009, 0x1130, 0xab7a, 0x080c, 0x6dee, 0x080c, 0xacb0, 0x0c10,
+ 0x6020, 0x9086, 0x000a, 0x0990, 0x0850, 0x0016, 0x0026, 0x0086,
+ 0x9046, 0x00a9, 0x080c, 0x9530, 0x008e, 0x002e, 0x001e, 0x0005,
+ 0x00f6, 0x0126, 0x2079, 0x19e6, 0x2091, 0x8000, 0x080c, 0x9579,
+ 0x080c, 0x960f, 0x080c, 0x6820, 0x012e, 0x00fe, 0x0005, 0x00b6,
+ 0x0096, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0016, 0x0006,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0x19e6, 0x7620, 0x2660, 0x2678,
+ 0x8cff, 0x0904, 0x94f5, 0x6010, 0x2058, 0xb8a0, 0x9206, 0x1904,
+ 0x94f0, 0x88ff, 0x0120, 0x605c, 0x9106, 0x1904, 0x94f0, 0x7030,
+ 0x9c06, 0x1580, 0x2069, 0x0100, 0x6820, 0xd0a4, 0x0110, 0xd0cc,
+ 0x1508, 0x080c, 0x8780, 0x080c, 0x9ed4, 0x68c3, 0x0000, 0x080c,
+ 0xa3ac, 0x7033, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384,
+ 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2a7a, 0x9006, 0x080c,
+ 0x2a7a, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001,
+ 0x003e, 0x0040, 0x7008, 0xc0ad, 0x700a, 0x6003, 0x0009, 0x630a,
+ 0x0804, 0x94f0, 0x7020, 0x9c36, 0x1110, 0x660c, 0x7622, 0x701c,
+ 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x701e, 0x0010,
+ 0x701f, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e,
+ 0x0008, 0x2678, 0x600f, 0x0000, 0x6044, 0xc0fc, 0x6046, 0x6014,
+ 0x2048, 0x080c, 0xc97a, 0x01e8, 0x6020, 0x9086, 0x0003, 0x1580,
+ 0x080c, 0xcb91, 0x1118, 0x080c, 0xb693, 0x0098, 0xa867, 0x0103,
+ 0xab7a, 0xa877, 0x0000, 0x0016, 0x0036, 0x0086, 0x080c, 0xcc7f,
+ 0x080c, 0xe79d, 0x080c, 0x6dee, 0x008e, 0x003e, 0x001e, 0x080c,
+ 0xcb6b, 0x080c, 0xaceb, 0x080c, 0xa282, 0x00ce, 0x0804, 0x9468,
+ 0x2c78, 0x600c, 0x2060, 0x0804, 0x9468, 0x012e, 0x000e, 0x001e,
+ 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x009e, 0x00be, 0x0005,
+ 0x6020, 0x9086, 0x0006, 0x1158, 0x0016, 0x0036, 0x0086, 0x080c,
+ 0xe79d, 0x080c, 0xe3e8, 0x008e, 0x003e, 0x001e, 0x08d0, 0x080c,
+ 0xb693, 0x6020, 0x9086, 0x0002, 0x1160, 0x6004, 0x0006, 0x9086,
+ 0x0085, 0x000e, 0x0904, 0x94d6, 0x9086, 0x008b, 0x0904, 0x94d6,
+ 0x0840, 0x6020, 0x9086, 0x0005, 0x1920, 0x6004, 0x0006, 0x9086,
+ 0x0085, 0x000e, 0x09c8, 0x9086, 0x008b, 0x09b0, 0x0804, 0x94e9,
+ 0x0006, 0x00f6, 0x00e6, 0x0096, 0x00b6, 0x00c6, 0x0066, 0x0016,
+ 0x0126, 0x2091, 0x8000, 0x9280, 0x1000, 0x2004, 0x905d, 0x2079,
+ 0x19e6, 0x9036, 0x7828, 0x2060, 0x8cff, 0x0538, 0x6010, 0x9b06,
+ 0x1500, 0x6043, 0xffff, 0x080c, 0xab00, 0x01d8, 0x610c, 0x0016,
+ 0x080c, 0xa042, 0x6014, 0x2048, 0xa867, 0x0103, 0xab7a, 0xa877,
+ 0x0000, 0x0016, 0x0036, 0x0086, 0x080c, 0xcc7f, 0x080c, 0xe79d,
+ 0x080c, 0x6dee, 0x008e, 0x003e, 0x001e, 0x080c, 0xaceb, 0x00ce,
+ 0x08d8, 0x2c30, 0x600c, 0x2060, 0x08b8, 0x080c, 0x683d, 0x012e,
+ 0x001e, 0x006e, 0x00ce, 0x00be, 0x009e, 0x00ee, 0x00fe, 0x000e,
+ 0x0005, 0x0096, 0x0006, 0x0066, 0x00c6, 0x00d6, 0x9036, 0x7820,
+ 0x9065, 0x0904, 0x95e2, 0x600c, 0x0006, 0x6044, 0xc0fc, 0x6046,
+ 0x600f, 0x0000, 0x7830, 0x9c06, 0x1598, 0x2069, 0x0100, 0x6820,
+ 0xd0a4, 0x0110, 0xd0cc, 0x1508, 0x080c, 0x8780, 0x080c, 0x9ed4,
+ 0x68c3, 0x0000, 0x080c, 0xa3ac, 0x7833, 0x0000, 0x0036, 0x2069,
+ 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c,
+ 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x2069, 0x0100, 0x6824, 0xd084,
+ 0x0110, 0x6827, 0x0001, 0x003e, 0x0058, 0x080c, 0x6a75, 0x1538,
+ 0x6003, 0x0009, 0x630a, 0x7808, 0xc0ad, 0x780a, 0x2c30, 0x00f8,
+ 0x6014, 0x2048, 0x080c, 0xc978, 0x01b0, 0x6020, 0x9086, 0x0003,
+ 0x1508, 0x080c, 0xcb91, 0x1118, 0x080c, 0xb693, 0x0060, 0x080c,
+ 0x6a75, 0x1168, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c,
+ 0x6dee, 0x080c, 0xcb6b, 0x080c, 0xaceb, 0x080c, 0xa282, 0x000e,
+ 0x0804, 0x9580, 0x7e22, 0x7e1e, 0x00de, 0x00ce, 0x006e, 0x000e,
+ 0x009e, 0x0005, 0x6020, 0x9086, 0x0006, 0x1118, 0x080c, 0xe3e8,
+ 0x0c50, 0x080c, 0xb693, 0x6020, 0x9086, 0x0002, 0x1150, 0x6004,
+ 0x0006, 0x9086, 0x0085, 0x000e, 0x0990, 0x9086, 0x008b, 0x0978,
+ 0x08d0, 0x6020, 0x9086, 0x0005, 0x19b0, 0x6004, 0x0006, 0x9086,
+ 0x0085, 0x000e, 0x0d18, 0x9086, 0x008b, 0x0d00, 0x0860, 0x0006,
+ 0x0096, 0x00b6, 0x00c6, 0x0066, 0x9036, 0x7828, 0x9065, 0x0510,
+ 0x6010, 0x2058, 0x600c, 0x0006, 0x3e08, 0x918e, 0x0002, 0x1118,
+ 0xb800, 0xd0bc, 0x11a8, 0x6043, 0xffff, 0x080c, 0xab00, 0x0180,
+ 0x610c, 0x080c, 0xa042, 0x6014, 0x2048, 0xa867, 0x0103, 0xab7a,
+ 0xa877, 0x0000, 0x080c, 0x6dee, 0x080c, 0xaceb, 0x000e, 0x08f0,
+ 0x2c30, 0x0ce0, 0x006e, 0x00ce, 0x00be, 0x009e, 0x000e, 0x0005,
+ 0x00e6, 0x00d6, 0x0096, 0x0066, 0x080c, 0x61a4, 0x11b0, 0x2071,
+ 0x19e6, 0x7030, 0x9080, 0x0005, 0x2004, 0x904d, 0x0170, 0xa878,
+ 0x9606, 0x1158, 0x2071, 0x19e6, 0x7030, 0x9035, 0x0130, 0x9080,
+ 0x0005, 0x2004, 0x9906, 0x1108, 0x0029, 0x006e, 0x009e, 0x00de,
+ 0x00ee, 0x0005, 0x00c6, 0x2660, 0x6043, 0xffff, 0x080c, 0xab00,
+ 0x0178, 0x080c, 0xa042, 0x6014, 0x2048, 0xa867, 0x0103, 0xab7a,
+ 0xa877, 0x0000, 0x080c, 0xcc7f, 0x080c, 0x6dee, 0x080c, 0xaceb,
+ 0x00ce, 0x0005, 0x00b6, 0x00e6, 0x00c6, 0x080c, 0xa91e, 0x0106,
+ 0x2071, 0x0101, 0x2e04, 0xc0c4, 0x2072, 0x6044, 0xd0fc, 0x1138,
+ 0x010e, 0x090c, 0xa93a, 0x00ce, 0x00ee, 0x00be, 0x0005, 0x2071,
+ 0x19e6, 0x7030, 0x9005, 0x0da0, 0x9c06, 0x190c, 0x0d7d, 0x7036,
+ 0x080c, 0x8780, 0x7004, 0x9084, 0x0007, 0x0002, 0x96a8, 0x96aa,
+ 0x96b1, 0x96bb, 0x96c9, 0x96a8, 0x96b6, 0x96a6, 0x080c, 0x0d7d,
+ 0x0428, 0x0005, 0x080c, 0xaaeb, 0x7007, 0x0000, 0x7033, 0x0000,
+ 0x00e8, 0x0066, 0x9036, 0x080c, 0xa042, 0x006e, 0x7007, 0x0000,
+ 0x7033, 0x0000, 0x0098, 0x080c, 0xaad6, 0x0140, 0x080c, 0xaaeb,
+ 0x0128, 0x0066, 0x9036, 0x080c, 0xa042, 0x006e, 0x7033, 0x0000,
+ 0x0028, 0x080c, 0xaad6, 0x080c, 0xa3ac, 0x0000, 0x010e, 0x090c,
+ 0xa93a, 0x00ce, 0x00ee, 0x00be, 0x0005, 0x00d6, 0x00c6, 0x080c,
+ 0xa91e, 0x0106, 0x6044, 0xd0fc, 0x1130, 0x010e, 0x090c, 0xa93a,
+ 0x00ce, 0x00de, 0x0005, 0x2069, 0x19e6, 0x684c, 0x9005, 0x0da8,
+ 0x9c06, 0x190c, 0x0d7d, 0x6852, 0x00e6, 0x2d70, 0x080c, 0x9300,
+ 0x00ee, 0x080c, 0x878d, 0x0016, 0x2009, 0x0040, 0x080c, 0x220a,
+ 0x001e, 0x683c, 0x9084, 0x0003, 0x0002, 0x9703, 0x9704, 0x9722,
+ 0x9701, 0x080c, 0x0d7d, 0x0460, 0x6868, 0x9086, 0x0001, 0x0190,
+ 0x600c, 0x9015, 0x0160, 0x6a4a, 0x600f, 0x0000, 0x6044, 0xc0fc,
+ 0x6046, 0x9006, 0x7042, 0x684e, 0x683f, 0x0000, 0x00c8, 0x684a,
+ 0x6846, 0x0ca0, 0x686b, 0x0000, 0x6848, 0x9065, 0x0d78, 0x6003,
+ 0x0002, 0x0c60, 0x9006, 0x686a, 0x6852, 0x686e, 0x600c, 0x9015,
+ 0x0120, 0x6a4a, 0x600f, 0x0000, 0x0018, 0x684e, 0x684a, 0x6846,
+ 0x684f, 0x0000, 0x010e, 0x090c, 0xa93a, 0x00ce, 0x00de, 0x0005,
+ 0x0005, 0x6020, 0x9084, 0x000f, 0x000b, 0x0005, 0x974e, 0x9751,
+ 0x9bbf, 0x9c58, 0x9751, 0x9bbf, 0x9c58, 0x974e, 0x9751, 0x974e,
+ 0x974e, 0x974e, 0x974e, 0x974e, 0x974e, 0x974e, 0x080c, 0x967a,
+ 0x0005, 0x00b6, 0x0156, 0x0136, 0x0146, 0x01c6, 0x01d6, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x2069, 0x0200, 0x2071, 0x0240, 0x6004,
+ 0x908a, 0x0053, 0x1a0c, 0x0d7d, 0x6110, 0x2158, 0xb984, 0x2c78,
+ 0x2061, 0x0100, 0x619a, 0x908a, 0x0040, 0x1a04, 0x97bd, 0x005b,
+ 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x01de, 0x01ce, 0x014e, 0x013e,
+ 0x015e, 0x00be, 0x0005, 0x9942, 0x997d, 0x99a6, 0x9a4e, 0x9a70,
+ 0x9a76, 0x9a83, 0x9a8b, 0x9a97, 0x9a9d, 0x9aae, 0x9a9d, 0x9b06,
+ 0x9a8b, 0x9b12, 0x9b18, 0x9a97, 0x9b18, 0x9b24, 0x97bb, 0x97bb,
+ 0x97bb, 0x97bb, 0x97bb, 0x97bb, 0x97bb, 0x97bb, 0x97bb, 0x97bb,
+ 0x97bb, 0xa063, 0xa086, 0xa097, 0xa0b7, 0xa0e9, 0x9a83, 0x97bb,
+ 0x9a83, 0x9a9d, 0x97bb, 0x99a6, 0x9a4e, 0x97bb, 0xa4aa, 0x9a9d,
+ 0x97bb, 0xa4c6, 0x9a9d, 0x97bb, 0x9a97, 0x993c, 0x97de, 0x97bb,
+ 0xa4e2, 0xa54f, 0xa633, 0x97bb, 0xa640, 0x9a80, 0xa66b, 0x97bb,
+ 0xa0f3, 0xa677, 0x97bb, 0x080c, 0x0d7d, 0x2100, 0x005b, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x01de, 0x01ce, 0x014e, 0x013e, 0x015e,
+ 0x00be, 0x0005, 0xa717, 0xa7c9, 0x97dc, 0x9805, 0x98b1, 0x98bc,
+ 0x97dc, 0x9a83, 0x97dc, 0x9903, 0x990f, 0x9820, 0x97dc, 0x983b,
+ 0x986f, 0xab56, 0xab9b, 0x9a9d, 0x080c, 0x0d7d, 0x00d6, 0x0096,
+ 0x080c, 0x9b37, 0x7003, 0x2414, 0x7007, 0x0018, 0x700b, 0x0800,
+ 0x7814, 0x2048, 0xa83c, 0x700e, 0xa850, 0x7022, 0xa854, 0x7026,
+ 0x60c3, 0x0018, 0x080c, 0x9ea4, 0x009e, 0x00de, 0x0005, 0x7810,
+ 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x080c, 0xabe2, 0x1118, 0x9084,
+ 0xff80, 0x0110, 0x9085, 0x0001, 0x0005, 0x00d6, 0x0096, 0x080c,
+ 0x9b37, 0x7003, 0x0500, 0x7814, 0x2048, 0xa874, 0x700a, 0xa878,
+ 0x700e, 0xa87c, 0x7012, 0xa880, 0x7016, 0xa884, 0x701a, 0xa888,
+ 0x701e, 0x60c3, 0x0010, 0x080c, 0x9ea4, 0x009e, 0x00de, 0x0005,
+ 0x00d6, 0x0096, 0x080c, 0x9b37, 0x7003, 0x0500, 0x7814, 0x2048,
+ 0xa8cc, 0x700a, 0xa8d0, 0x700e, 0xa8d4, 0x7012, 0xa8d8, 0x7016,
+ 0xa8dc, 0x701a, 0xa8e0, 0x701e, 0x60c3, 0x0010, 0x080c, 0x9ea4,
+ 0x009e, 0x00de, 0x0005, 0x00d6, 0x0096, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x9b37, 0x20e9, 0x0000, 0x2001, 0x19a2, 0x2003, 0x0000,
+ 0x7814, 0x2048, 0xa814, 0x8003, 0x60c2, 0xa830, 0x20a8, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098, 0x2001, 0x19a2, 0x0016,
+ 0x200c, 0x2001, 0x0001, 0x080c, 0x21ef, 0x080c, 0xd72b, 0x9006,
+ 0x080c, 0x21ef, 0x001e, 0xa804, 0x9005, 0x0110, 0x2048, 0x0c28,
+ 0x04d9, 0x080c, 0x9ea4, 0x012e, 0x009e, 0x00de, 0x0005, 0x00d6,
+ 0x0096, 0x0126, 0x2091, 0x8000, 0x080c, 0x9b82, 0x20e9, 0x0000,
+ 0x2001, 0x19a2, 0x2003, 0x0000, 0x7814, 0x2048, 0xa86f, 0x0200,
+ 0xa873, 0x0000, 0xa814, 0x8003, 0x60c2, 0xa830, 0x20a8, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098, 0x2001, 0x19a2, 0x0016,
+ 0x200c, 0x080c, 0xd72b, 0x001e, 0xa804, 0x9005, 0x0110, 0x2048,
+ 0x0c60, 0x0051, 0x7814, 0x2048, 0x080c, 0x0ff9, 0x080c, 0x9ea4,
+ 0x012e, 0x009e, 0x00de, 0x0005, 0x60c0, 0x8004, 0x9084, 0x0003,
+ 0x9005, 0x0130, 0x9082, 0x0004, 0x20a3, 0x0000, 0x8000, 0x1de0,
+ 0x0005, 0x080c, 0x9b37, 0x7003, 0x7800, 0x7808, 0x8007, 0x700a,
+ 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x00d6, 0x00e6, 0x080c, 0x9b82,
+ 0x7814, 0x9084, 0xff00, 0x2073, 0x0200, 0x8e70, 0x8e70, 0x9095,
+ 0x0010, 0x2272, 0x8e70, 0x2073, 0x0034, 0x8e70, 0x2069, 0x1805,
+ 0x20a9, 0x0004, 0x2d76, 0x8d68, 0x8e70, 0x1f04, 0x98d2, 0x2069,
+ 0x1801, 0x20a9, 0x0004, 0x2d76, 0x8d68, 0x8e70, 0x1f04, 0x98db,
+ 0x2069, 0x19b2, 0x9086, 0xdf00, 0x0110, 0x2069, 0x19cc, 0x20a9,
+ 0x001a, 0x9e86, 0x0260, 0x1148, 0x00c6, 0x2061, 0x0200, 0x6010,
+ 0x8000, 0x6012, 0x00ce, 0x2071, 0x0240, 0x2d04, 0x8007, 0x2072,
+ 0x8d68, 0x8e70, 0x1f04, 0x98e9, 0x60c3, 0x004c, 0x080c, 0x9ea4,
+ 0x00ee, 0x00de, 0x0005, 0x080c, 0x9b37, 0x7003, 0x6300, 0x7007,
+ 0x0028, 0x7808, 0x700e, 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x00d6,
+ 0x0026, 0x0016, 0x080c, 0x9b82, 0x7003, 0x0200, 0x7814, 0x700e,
+ 0x00e6, 0x9ef0, 0x0004, 0x2009, 0x0001, 0x2011, 0x000c, 0x2069,
+ 0x1923, 0x6810, 0xd084, 0x1148, 0x2073, 0x0500, 0x8e70, 0x2073,
+ 0x0000, 0x8e70, 0x8108, 0x9290, 0x0004, 0x2073, 0x0800, 0x8e70,
+ 0x2073, 0x0000, 0x00ee, 0x7206, 0x710a, 0x62c2, 0x080c, 0x9ea4,
+ 0x001e, 0x002e, 0x00de, 0x0005, 0x2001, 0x1818, 0x2004, 0x609a,
+ 0x0804, 0x9ea4, 0x080c, 0x9b37, 0x7003, 0x5200, 0x2069, 0x1847,
+ 0x6804, 0xd084, 0x0130, 0x6828, 0x0016, 0x080c, 0x2694, 0x710e,
+ 0x001e, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9,
+ 0x0000, 0x20a1, 0x0250, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1801,
+ 0x20a1, 0x0254, 0x4003, 0x080c, 0xabe2, 0x1120, 0xb8a0, 0x9082,
+ 0x007f, 0x0248, 0x2001, 0x181f, 0x2004, 0x7032, 0x2001, 0x1820,
+ 0x2004, 0x7036, 0x0030, 0x2001, 0x1818, 0x2004, 0x9084, 0x00ff,
+ 0x7036, 0x60c3, 0x001c, 0x0804, 0x9ea4, 0x080c, 0x9b37, 0x7003,
+ 0x0500, 0x080c, 0xabe2, 0x1120, 0xb8a0, 0x9082, 0x007f, 0x0248,
+ 0x2001, 0x181f, 0x2004, 0x700a, 0x2001, 0x1820, 0x2004, 0x700e,
+ 0x0030, 0x2001, 0x1818, 0x2004, 0x9084, 0x00ff, 0x700e, 0x20a9,
+ 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9, 0x0000, 0x20a1,
+ 0x0250, 0x4003, 0x60c3, 0x0010, 0x0804, 0x9ea4, 0x080c, 0x9b37,
+ 0x9006, 0x080c, 0x6aa7, 0xb8a0, 0x9086, 0x007e, 0x1130, 0x7003,
+ 0x0400, 0x620c, 0xc2b4, 0x620e, 0x0058, 0x7814, 0x0096, 0x904d,
+ 0x0120, 0x9006, 0xa89a, 0xa8a6, 0xa8aa, 0x009e, 0x7003, 0x0300,
+ 0xb8a0, 0x9086, 0x007e, 0x1904, 0x9a15, 0x00d6, 0x2069, 0x196b,
+ 0x2001, 0x1837, 0x2004, 0xd0a4, 0x0188, 0x6800, 0x700a, 0x6808,
+ 0x9084, 0x2000, 0x7012, 0x080c, 0xabf9, 0x680c, 0x7016, 0x701f,
+ 0x2710, 0x6818, 0x7022, 0x681c, 0x7026, 0x0090, 0x6800, 0x700a,
+ 0x6804, 0x700e, 0x6808, 0x080c, 0x753d, 0x1118, 0x9084, 0x37ff,
+ 0x0010, 0x9084, 0x3fff, 0x7012, 0x080c, 0xabf9, 0x680c, 0x7016,
+ 0x00de, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9,
+ 0x0000, 0x20a1, 0x0256, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1801,
+ 0x20a1, 0x025a, 0x4003, 0x00d6, 0x080c, 0xa6fe, 0x2069, 0x1973,
+ 0x2071, 0x024e, 0x6800, 0xc0dd, 0x7002, 0x080c, 0x5742, 0xd0e4,
+ 0x0110, 0x680c, 0x700e, 0x00de, 0x04a8, 0x2001, 0x1837, 0x2004,
+ 0xd0a4, 0x0170, 0x0016, 0x2001, 0x196c, 0x200c, 0x60e0, 0x9106,
+ 0x0130, 0x2100, 0x60e3, 0x0000, 0x080c, 0x26d5, 0x61e2, 0x001e,
+ 0x20e1, 0x0001, 0x2099, 0x196b, 0x20e9, 0x0000, 0x20a1, 0x024e,
+ 0x20a9, 0x0008, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1805, 0x20a1,
+ 0x0256, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1801, 0x20a1, 0x025a,
+ 0x4003, 0x080c, 0xa6fe, 0x20a1, 0x024e, 0x20a9, 0x0008, 0x2099,
+ 0x1973, 0x4003, 0x60c3, 0x0074, 0x0804, 0x9ea4, 0x080c, 0x9b37,
+ 0x7003, 0x2010, 0x7007, 0x0014, 0x700b, 0x0800, 0x700f, 0x2000,
+ 0x9006, 0x00f6, 0x2079, 0x1847, 0x7904, 0x00fe, 0xd1ac, 0x1110,
+ 0x9085, 0x0020, 0xd1a4, 0x0110, 0x9085, 0x0010, 0x9085, 0x0002,
+ 0x00d6, 0x0804, 0x9ae7, 0x7026, 0x60c3, 0x0014, 0x0804, 0x9ea4,
+ 0x080c, 0x9b37, 0x7003, 0x5000, 0x0804, 0x99c0, 0x080c, 0x9b37,
+ 0x7003, 0x2110, 0x7007, 0x0014, 0x60c3, 0x0014, 0x0804, 0x9ea4,
+ 0x080c, 0x9b79, 0x0010, 0x080c, 0x9b82, 0x7003, 0x0200, 0x60c3,
+ 0x0004, 0x0804, 0x9ea4, 0x080c, 0x9b82, 0x7003, 0x0100, 0x700b,
+ 0x0003, 0x700f, 0x2a00, 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x080c,
+ 0x9b82, 0x7003, 0x0200, 0x0804, 0x99c0, 0x080c, 0x9b82, 0x7003,
+ 0x0100, 0x782c, 0x9005, 0x0110, 0x700a, 0x0010, 0x700b, 0x0003,
+ 0x7814, 0x700e, 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x00d6, 0x080c,
+ 0x9b82, 0x7003, 0x0210, 0x7007, 0x0014, 0x700b, 0x0800, 0xb894,
+ 0x9086, 0x0014, 0x1198, 0xb99c, 0x9184, 0x0030, 0x0190, 0xb998,
+ 0x9184, 0xc000, 0x1140, 0xd1ec, 0x0118, 0x700f, 0x2100, 0x0058,
+ 0x700f, 0x0100, 0x0040, 0x700f, 0x0400, 0x0028, 0x700f, 0x0700,
+ 0x0010, 0x700f, 0x0800, 0x00f6, 0x2079, 0x1847, 0x7904, 0x00fe,
+ 0xd1ac, 0x1110, 0x9085, 0x0020, 0xd1a4, 0x0110, 0x9085, 0x0010,
+ 0x2009, 0x1869, 0x210c, 0xd184, 0x1110, 0x9085, 0x0002, 0x0026,
+ 0x2009, 0x1867, 0x210c, 0xd1e4, 0x0150, 0xc0c5, 0xbad4, 0xd28c,
+ 0x1108, 0xc0cd, 0x9094, 0x0030, 0x9296, 0x0010, 0x0140, 0xd1ec,
+ 0x0130, 0x9094, 0x0030, 0x9296, 0x0010, 0x0108, 0xc0bd, 0x002e,
+ 0x7026, 0x60c3, 0x0014, 0x00de, 0x0804, 0x9ea4, 0x080c, 0x9b82,
+ 0x7003, 0x0210, 0x7007, 0x0014, 0x700f, 0x0100, 0x60c3, 0x0014,
+ 0x0804, 0x9ea4, 0x080c, 0x9b82, 0x7003, 0x0200, 0x0804, 0x9946,
+ 0x080c, 0x9b82, 0x7003, 0x0100, 0x700b, 0x0003, 0x700f, 0x2a00,
+ 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x080c, 0x9b82, 0x7003, 0x0100,
+ 0x700b, 0x000b, 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x0026, 0x00d6,
+ 0x0036, 0x0046, 0x2019, 0x3200, 0x2021, 0x0800, 0x0040, 0x0026,
+ 0x00d6, 0x0036, 0x0046, 0x2019, 0x2200, 0x2021, 0x0100, 0x080c,
+ 0xa713, 0xb810, 0x9305, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800,
+ 0x687c, 0x700a, 0x6880, 0x700e, 0x9485, 0x0029, 0x7012, 0x004e,
+ 0x003e, 0x00de, 0x080c, 0x9e98, 0x721a, 0x9f95, 0x0000, 0x7222,
+ 0x7027, 0xffff, 0x2071, 0x024c, 0x002e, 0x0005, 0x0026, 0x080c,
+ 0xa713, 0x7003, 0x02ff, 0x7007, 0xfffc, 0x00d6, 0x2069, 0x1800,
+ 0x687c, 0x700a, 0x6880, 0x700e, 0x00de, 0x7013, 0x2029, 0x0c10,
+ 0x7003, 0x0100, 0x7007, 0x0000, 0x700b, 0xfc02, 0x700f, 0x0000,
+ 0x0005, 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019, 0x3300, 0x2021,
+ 0x0800, 0x0040, 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019, 0x2300,
+ 0x2021, 0x0100, 0x080c, 0xa713, 0xb810, 0x9305, 0x7002, 0xb814,
+ 0x7006, 0x2069, 0x1800, 0xb810, 0x9005, 0x1140, 0xb814, 0x9005,
+ 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffe, 0x0020, 0x687c, 0x700a,
+ 0x6880, 0x700e, 0x0000, 0x9485, 0x0098, 0x7012, 0x004e, 0x003e,
+ 0x00de, 0x080c, 0x9e98, 0x721a, 0x7a08, 0x7222, 0x2f10, 0x7226,
+ 0x2071, 0x024c, 0x002e, 0x0005, 0x080c, 0x9e98, 0x721a, 0x7a08,
+ 0x7222, 0x7814, 0x7026, 0x2071, 0x024c, 0x002e, 0x0005, 0x00b6,
+ 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2069, 0x0200, 0x2071, 0x0240,
+ 0x6004, 0x908a, 0x0085, 0x0a0c, 0x0d7d, 0x908a, 0x0092, 0x1a0c,
+ 0x0d7d, 0x6110, 0x2158, 0xb984, 0x2c78, 0x2061, 0x0100, 0x619a,
+ 0x9082, 0x0085, 0x0033, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be,
+ 0x0005, 0x9bf0, 0x9bff, 0x9c0a, 0x9bee, 0x9bee, 0x9bee, 0x9bf0,
+ 0x9bee, 0x9bee, 0x9bee, 0x9bee, 0x9bee, 0x9bee, 0x080c, 0x0d7d,
+ 0x0411, 0x60c3, 0x0000, 0x0026, 0x080c, 0x29e5, 0x0228, 0x2011,
+ 0x0101, 0x2204, 0xc0c5, 0x2012, 0x002e, 0x0804, 0x9ea4, 0x0431,
+ 0x7808, 0x700a, 0x7814, 0x700e, 0x7017, 0xffff, 0x60c3, 0x000c,
+ 0x0804, 0x9ea4, 0x04a1, 0x7003, 0x0003, 0x7007, 0x0300, 0x60c3,
+ 0x0004, 0x0804, 0x9ea4, 0x0026, 0x080c, 0xa713, 0xb810, 0x9085,
+ 0x8100, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x687c, 0x700a,
+ 0x6880, 0x700e, 0x7013, 0x0009, 0x0804, 0x9b52, 0x0026, 0x080c,
+ 0xa713, 0xb810, 0x9085, 0x8400, 0x7002, 0xb814, 0x7006, 0x2069,
+ 0x1800, 0x687c, 0x700a, 0x6880, 0x700e, 0x2001, 0x0099, 0x7a20,
+ 0x9296, 0x0005, 0x0108, 0xc0bc, 0x7012, 0x0804, 0x9bb4, 0x0026,
+ 0x080c, 0xa713, 0xb810, 0x9085, 0x8500, 0x7002, 0xb814, 0x7006,
+ 0x2069, 0x1800, 0x687c, 0x700a, 0x6880, 0x700e, 0x2001, 0x0099,
+ 0x7a20, 0x9296, 0x0005, 0x0108, 0xc0bc, 0x7012, 0x0804, 0x9bb4,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2c78, 0x2069, 0x0200,
+ 0x2071, 0x0240, 0x7804, 0x908a, 0x0040, 0x0a0c, 0x0d7d, 0x908a,
+ 0x0057, 0x1a0c, 0x0d7d, 0x7910, 0x2158, 0xb984, 0x2061, 0x0100,
+ 0x619a, 0x9082, 0x0040, 0x0033, 0x00fe, 0x00ee, 0x00de, 0x00ce,
+ 0x00be, 0x0005, 0x9c8d, 0x9c8d, 0x9c8d, 0x9cb3, 0x9c8d, 0x9c8d,
+ 0x9c8d, 0x9c8d, 0x9c8d, 0x9c8d, 0x9c8d, 0xa25f, 0xa267, 0xa26f,
+ 0xa277, 0x9c8d, 0x9c8d, 0x9c8d, 0xa257, 0x080c, 0x0d7d, 0x6813,
+ 0x0008, 0xba8c, 0x8210, 0xb8d4, 0xd084, 0x0128, 0x7a52, 0x7b14,
+ 0x7b4e, 0x722e, 0x732a, 0x9294, 0x00ff, 0xba8e, 0x8217, 0x721a,
+ 0xba10, 0x9295, 0x0600, 0x7202, 0xba14, 0x7206, 0x2069, 0x1800,
+ 0x6a7c, 0x720a, 0x6a80, 0x720e, 0x7013, 0x0829, 0x2f10, 0x7222,
+ 0x7027, 0xffff, 0x0005, 0x0016, 0x7814, 0x9084, 0x0700, 0x8007,
+ 0x0013, 0x001e, 0x0005, 0x9cc3, 0x9cc3, 0x9cc5, 0x9cc3, 0x9cc3,
+ 0x9cc3, 0x9cdf, 0x9cc3, 0x080c, 0x0d7d, 0x7914, 0x918c, 0x08ff,
+ 0x918d, 0xf600, 0x7916, 0x2009, 0x0003, 0x00b9, 0x2069, 0x1847,
+ 0x6804, 0xd0bc, 0x0130, 0x682c, 0x9084, 0x00ff, 0x8007, 0x7032,
+ 0x0010, 0x7033, 0x3f00, 0x60c3, 0x0001, 0x0804, 0x9ea4, 0x2009,
+ 0x0003, 0x0019, 0x7033, 0x7f00, 0x0cb0, 0x0016, 0x080c, 0xa713,
+ 0x001e, 0xb810, 0x9085, 0x0100, 0x7002, 0xb814, 0x7006, 0x2069,
+ 0x1800, 0x6a7c, 0x720a, 0x6a80, 0x720e, 0x7013, 0x0888, 0x918d,
+ 0x0008, 0x7116, 0x080c, 0x9e98, 0x721a, 0x7a08, 0x7222, 0x2f10,
+ 0x7226, 0x0005, 0x00b6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0056,
+ 0x0046, 0x0036, 0x2061, 0x0100, 0x2071, 0x1800, 0x7160, 0x7810,
+ 0x2058, 0x76dc, 0x96b4, 0x0028, 0x0110, 0x737c, 0x7480, 0x2500,
+ 0x76dc, 0x96b4, 0x0028, 0x0140, 0x2001, 0x04ff, 0x6062, 0x6067,
+ 0xffff, 0x636a, 0x646e, 0x0050, 0x2001, 0x00ff, 0x9085, 0x0400,
+ 0x6062, 0x6067, 0xffff, 0x606b, 0x0000, 0x616e, 0xb8b8, 0x6073,
+ 0x0530, 0x6077, 0x0008, 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e,
+ 0x8007, 0x9085, 0x0020, 0x607a, 0x607f, 0x0000, 0x2b00, 0x6082,
+ 0x6087, 0xffff, 0x7814, 0x0096, 0x2048, 0xa848, 0x608a, 0xa844,
+ 0x608e, 0xa838, 0x60c6, 0xa834, 0x60ca, 0x009e, 0xb86c, 0x60ce,
+ 0x60ab, 0x0036, 0x60af, 0x95d5, 0x60d7, 0x0000, 0x2001, 0x1837,
+ 0x2004, 0x9084, 0x0028, 0x0128, 0x609f, 0x0000, 0x2001, 0x0092,
+ 0x0058, 0x6028, 0xc0bd, 0x602a, 0x609f, 0x00ff, 0x2011, 0xffff,
+ 0x080c, 0x2ab4, 0x2001, 0x00b2, 0x2010, 0x900e, 0x080c, 0x2ac3,
+ 0x2009, 0x07d0, 0x080c, 0x8785, 0x003e, 0x004e, 0x005e, 0x006e,
+ 0x00ce, 0x00de, 0x00ee, 0x00be, 0x0005, 0x00b6, 0x00e6, 0x00d6,
+ 0x00c6, 0x0066, 0x0056, 0x0046, 0x0036, 0x2061, 0x0100, 0x2071,
+ 0x1800, 0x7160, 0x7810, 0x2058, 0xb8a0, 0x2028, 0x76dc, 0xd6ac,
+ 0x1168, 0x9582, 0x007e, 0x1250, 0x2500, 0x9094, 0xff80, 0x1130,
+ 0x9080, 0x33b9, 0x2015, 0x9294, 0x00ff, 0x0020, 0xb910, 0xba14,
+ 0x737c, 0x7480, 0x70dc, 0xd0ac, 0x1130, 0x9582, 0x007e, 0x1218,
+ 0x9584, 0xff80, 0x0138, 0x9185, 0x0400, 0x6062, 0x6266, 0x636a,
+ 0x646e, 0x0030, 0x6063, 0x0400, 0x6266, 0x606b, 0x0000, 0x616e,
+ 0xb8b8, 0x6072, 0x6077, 0x0000, 0xb864, 0xd0a4, 0x0110, 0x6077,
+ 0x0008, 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007, 0x9085,
+ 0x0020, 0x607a, 0x607f, 0x0000, 0x2b00, 0x6082, 0x6087, 0xffff,
+ 0x7814, 0x0096, 0x2048, 0xa848, 0x608a, 0xa844, 0x608e, 0xa838,
+ 0x60c6, 0xa834, 0x60ca, 0x009e, 0xb86c, 0x60ce, 0x60ab, 0x0036,
+ 0x60af, 0x95d5, 0x60d7, 0x0000, 0xba84, 0x629e, 0x00f6, 0x2079,
+ 0x0140, 0x7803, 0x0000, 0x00fe, 0x900e, 0x2011, 0x0092, 0x080c,
+ 0x2ac3, 0x2009, 0x07d0, 0x080c, 0x8785, 0x003e, 0x004e, 0x005e,
+ 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00be, 0x0005, 0x00b6, 0x0096,
+ 0x00e6, 0x00d6, 0x00c6, 0x0056, 0x0046, 0x0036, 0x2061, 0x0100,
+ 0x2071, 0x1800, 0x7810, 0x2058, 0xb8a0, 0x2028, 0xb910, 0xba14,
+ 0x737c, 0x7480, 0x7820, 0x0002, 0x9e23, 0x9e23, 0x9e23, 0x9e23,
+ 0x9e23, 0x9e23, 0x9e23, 0x9e23, 0x9e23, 0x9e23, 0x9e25, 0x9e23,
+ 0x9e23, 0x9e23, 0x9e23, 0x080c, 0x0d7d, 0xb884, 0x609e, 0x7814,
+ 0x2048, 0xa87c, 0xd0fc, 0x0558, 0xaf90, 0x9784, 0xff00, 0x9105,
+ 0x6062, 0x873f, 0x9784, 0xff00, 0x0006, 0x7814, 0x2048, 0xa878,
+ 0xc0fc, 0x9005, 0x000e, 0x1160, 0xaf94, 0x87ff, 0x0198, 0x2039,
+ 0x0098, 0x9705, 0x6072, 0x7808, 0x6082, 0x2f00, 0x6086, 0x0038,
+ 0x9185, 0x2200, 0x6062, 0x6073, 0x0129, 0x6077, 0x0000, 0xb884,
+ 0x609e, 0x0050, 0x2039, 0x0029, 0x9705, 0x6072, 0x0cc0, 0x9185,
+ 0x0200, 0x6062, 0x6073, 0x2029, 0xa87c, 0xd0fc, 0x0118, 0xaf94,
+ 0x87ff, 0x1120, 0x2f00, 0x6082, 0x7808, 0x6086, 0x6266, 0x636a,
+ 0x646e, 0x6077, 0x0000, 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e,
+ 0x8007, 0x607a, 0x607f, 0x0000, 0xa848, 0x608a, 0xa844, 0x608e,
+ 0xa838, 0x60c6, 0xa834, 0x60ca, 0xb86c, 0x60ce, 0x60af, 0x95d5,
+ 0x60d7, 0x0000, 0x080c, 0xa6f3, 0x2009, 0x07d0, 0x60c4, 0x9084,
+ 0xfff0, 0x9005, 0x0110, 0x2009, 0x1b58, 0x080c, 0x8785, 0x003e,
+ 0x004e, 0x005e, 0x00ce, 0x00de, 0x00ee, 0x009e, 0x00be, 0x0005,
+ 0x7a40, 0x9294, 0x00ff, 0x8217, 0x0005, 0x00d6, 0x2069, 0x19e6,
+ 0x686b, 0x0001, 0x00de, 0x0005, 0x60a3, 0x0056, 0x60a7, 0x9575,
+ 0x00f1, 0x080c, 0x8777, 0x0005, 0x0016, 0x2001, 0x180c, 0x200c,
+ 0x9184, 0x0600, 0x9086, 0x0600, 0x0128, 0x0089, 0x080c, 0x8777,
+ 0x001e, 0x0005, 0xc1e5, 0x2001, 0x180c, 0x2102, 0x2001, 0x19e7,
+ 0x2003, 0x0000, 0x2001, 0x19f2, 0x2003, 0x0000, 0x0c88, 0x0006,
+ 0x0016, 0x0026, 0x2009, 0x1804, 0x2011, 0x0009, 0x080c, 0x2ac3,
+ 0x002e, 0x001e, 0x000e, 0x0005, 0x0016, 0x00c6, 0x0006, 0x080c,
+ 0xa91e, 0x0106, 0x2061, 0x0100, 0x61a4, 0x60a7, 0x95f5, 0x0016,
+ 0x0026, 0x2009, 0x1804, 0x2011, 0x0008, 0x080c, 0x2ac3, 0x002e,
+ 0x001e, 0x010e, 0x090c, 0xa93a, 0x000e, 0xa001, 0xa001, 0xa001,
+ 0x61a6, 0x00ce, 0x001e, 0x0005, 0x00c6, 0x00d6, 0x0016, 0x0026,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x080c, 0x753d, 0x1510, 0x2001,
+ 0x1a0b, 0x2004, 0x9005, 0x1904, 0x9f53, 0x080c, 0x75e2, 0x11a8,
+ 0x2069, 0x0380, 0x6843, 0x0101, 0x6844, 0xd084, 0x1de8, 0x2061,
+ 0x0100, 0x6020, 0xd0b4, 0x1120, 0x6024, 0xd084, 0x090c, 0x0d7d,
+ 0x6843, 0x0100, 0x080c, 0x8777, 0x04b0, 0x00c6, 0x2061, 0x19e6,
+ 0x00f0, 0x6904, 0x9194, 0x4000, 0x0598, 0x080c, 0x9ed4, 0x080c,
+ 0x2a8a, 0x00c6, 0x2061, 0x19e6, 0x6134, 0x9192, 0x0008, 0x1278,
+ 0x8108, 0x6136, 0x080c, 0xa91e, 0x6130, 0x080c, 0xa93a, 0x00ce,
+ 0x81ff, 0x01c8, 0x080c, 0x8777, 0x080c, 0x9ec7, 0x00a0, 0x080c,
+ 0xa91e, 0x6130, 0x91e5, 0x0000, 0x0150, 0x080c, 0xe897, 0x080c,
+ 0x8780, 0x6003, 0x0001, 0x2009, 0x0014, 0x080c, 0xad4d, 0x080c,
+ 0xa93a, 0x00ce, 0x0000, 0x002e, 0x001e, 0x00de, 0x00ce, 0x0005,
+ 0x2001, 0x1a0b, 0x2004, 0x9005, 0x1db0, 0x00c6, 0x2061, 0x19e6,
+ 0x6134, 0x9192, 0x0003, 0x1ad8, 0x8108, 0x6136, 0x00ce, 0x080c,
+ 0x8777, 0x080c, 0x5f4d, 0x2009, 0x1846, 0x2114, 0x8210, 0x220a,
+ 0x0c10, 0x0096, 0x00c6, 0x00d6, 0x00e6, 0x0016, 0x0026, 0x080c,
+ 0x878d, 0x080c, 0xa91e, 0x2001, 0x0387, 0x2003, 0x0202, 0x2071,
+ 0x19e6, 0x714c, 0x81ff, 0x0904, 0x9ffb, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x080c, 0x753d, 0x1510, 0x0036, 0x2019, 0x0002, 0x080c,
+ 0xa1b8, 0x003e, 0x714c, 0x2160, 0x080c, 0xe897, 0x2009, 0x004a,
+ 0x6220, 0x9296, 0x0009, 0x1130, 0x6114, 0x2148, 0xa87b, 0x0006,
+ 0x2009, 0x004a, 0x6003, 0x0003, 0x080c, 0xad4d, 0x2001, 0x0386,
+ 0x2003, 0x5040, 0x080c, 0x75e2, 0x0804, 0x9ffb, 0x6904, 0xd1f4,
+ 0x0904, 0xa008, 0x080c, 0x2a8a, 0x00c6, 0x704c, 0x9065, 0x090c,
+ 0x0d7d, 0x6020, 0x00ce, 0x9086, 0x0006, 0x1518, 0x61c8, 0x60c4,
+ 0x9105, 0x11f8, 0x2009, 0x180c, 0x2104, 0xd0d4, 0x01d0, 0x6214,
+ 0x9294, 0x1800, 0x1128, 0x6224, 0x9294, 0x0002, 0x1560, 0x0010,
+ 0xc0d4, 0x200a, 0x6014, 0x9084, 0xe7fd, 0x9085, 0x0010, 0x6016,
+ 0x704c, 0x2060, 0x080c, 0x96d5, 0x2009, 0x0049, 0x080c, 0xad4d,
+ 0x00d0, 0x0036, 0x2019, 0x0001, 0x080c, 0xa1b8, 0x003e, 0x714c,
+ 0x2160, 0x080c, 0xe897, 0x2009, 0x004a, 0x6220, 0x9296, 0x0009,
+ 0x1130, 0x6114, 0x2148, 0xa87b, 0x0006, 0x2009, 0x004a, 0x6003,
+ 0x0003, 0x080c, 0xad4d, 0x2001, 0x0387, 0x2003, 0x0200, 0x080c,
+ 0xa93a, 0x002e, 0x001e, 0x00ee, 0x00de, 0x00ce, 0x009e, 0x0005,
+ 0xd1ec, 0x1904, 0x9fb2, 0x0804, 0x9fb4, 0x0026, 0x00e6, 0x2071,
+ 0x19e6, 0x706c, 0xd084, 0x01e8, 0xc084, 0x706e, 0x714c, 0x81ff,
+ 0x01c0, 0x2071, 0x0100, 0x9188, 0x0008, 0x2114, 0x928e, 0x0006,
+ 0x1138, 0x2009, 0x1984, 0x2011, 0x0012, 0x080c, 0x2ac3, 0x0048,
+ 0x928e, 0x0009, 0x0db0, 0x2009, 0x1984, 0x2011, 0x0016, 0x080c,
+ 0x2ac3, 0x00ee, 0x002e, 0x0005, 0x9036, 0x2001, 0x19f0, 0x2004,
+ 0x9005, 0x0128, 0x9c06, 0x0128, 0x2c30, 0x600c, 0x0cc8, 0x9085,
+ 0x0001, 0x0005, 0x00f6, 0x2079, 0x19e6, 0x610c, 0x9006, 0x600e,
+ 0x6044, 0xc0fc, 0x6046, 0x86ff, 0x1140, 0x7824, 0x9c06, 0x1118,
+ 0x7826, 0x782a, 0x0050, 0x792a, 0x0040, 0x00c6, 0x2660, 0x610e,
+ 0x00ce, 0x7824, 0x9c06, 0x1108, 0x7e26, 0x080c, 0xa282, 0x080c,
+ 0xcb6b, 0x00fe, 0x0005, 0x080c, 0x9b37, 0x7003, 0x1200, 0x7838,
+ 0x7012, 0x783c, 0x7016, 0x00c6, 0x7820, 0x9086, 0x0004, 0x1148,
+ 0x7810, 0x9005, 0x0130, 0x00b6, 0x2058, 0xb810, 0xb914, 0x00be,
+ 0x0020, 0x2061, 0x1800, 0x607c, 0x6180, 0x9084, 0x00ff, 0x700a,
+ 0x710e, 0x00ce, 0x60c3, 0x002c, 0x0804, 0x9ea4, 0x080c, 0x9b37,
+ 0x7003, 0x0f00, 0x7808, 0xd09c, 0x0128, 0xb810, 0x9084, 0x00ff,
+ 0x700a, 0xb814, 0x700e, 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x0156,
+ 0x080c, 0x9b82, 0x7003, 0x0200, 0x080c, 0x8845, 0x20a9, 0x0006,
+ 0x2011, 0xffec, 0x2019, 0xffed, 0x9ef0, 0x0002, 0x2305, 0x2072,
+ 0x8e70, 0x2205, 0x2072, 0x8e70, 0x9398, 0x0002, 0x9290, 0x0002,
+ 0x1f04, 0xa0a6, 0x60c3, 0x001c, 0x015e, 0x0804, 0x9ea4, 0x0016,
+ 0x0026, 0x080c, 0x9b5e, 0x080c, 0x9b70, 0x9e80, 0x0004, 0x20e9,
+ 0x0000, 0x20a0, 0x7814, 0x0096, 0x2048, 0xa800, 0x2048, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x0021, 0x2098, 0x009e, 0x7808, 0x9088,
+ 0x0002, 0x21a8, 0x9192, 0x0010, 0x1250, 0x4003, 0x9080, 0x0004,
+ 0x8003, 0x60c2, 0x080c, 0x9ea4, 0x002e, 0x001e, 0x0005, 0x20a9,
+ 0x0010, 0x4003, 0x080c, 0xa6fe, 0x20a1, 0x0240, 0x22a8, 0x4003,
+ 0x0c68, 0x080c, 0x9b37, 0x7003, 0x6200, 0x7808, 0x700e, 0x60c3,
+ 0x0008, 0x0804, 0x9ea4, 0x0016, 0x0026, 0x080c, 0x9b37, 0x20e9,
+ 0x0000, 0x20a1, 0x024c, 0x7814, 0x0096, 0x2048, 0xa800, 0x2048,
+ 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0023, 0x2098, 0x009e, 0x7808,
+ 0x9088, 0x0002, 0x21a8, 0x4003, 0x8003, 0x60c2, 0x080c, 0x9ea4,
+ 0x002e, 0x001e, 0x0005, 0x00e6, 0x00c6, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x19e6, 0x7010, 0x2060, 0x8cff, 0x0188, 0x080c,
+ 0xcb91, 0x1110, 0x080c, 0xb693, 0x600c, 0x0006, 0x080c, 0xce0d,
+ 0x600f, 0x0000, 0x080c, 0xacb0, 0x080c, 0xa282, 0x00ce, 0x0c68,
+ 0x2c00, 0x7012, 0x700e, 0x012e, 0x000e, 0x00ce, 0x00ee, 0x0005,
+ 0x0126, 0x0156, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0026,
+ 0x0016, 0x0006, 0x2091, 0x8000, 0x2001, 0x180c, 0x200c, 0x918c,
+ 0xe7ff, 0x2102, 0x2069, 0x0100, 0x2079, 0x0140, 0x2071, 0x19e6,
+ 0x7030, 0x2060, 0x8cff, 0x0548, 0x080c, 0x9ed4, 0x6ac0, 0x68c3,
+ 0x0000, 0x080c, 0x8780, 0x00c6, 0x2061, 0x0100, 0x080c, 0xa84f,
+ 0x00ce, 0x20a9, 0x01f4, 0x04b1, 0x080c, 0x967a, 0x6044, 0xd0ac,
+ 0x1128, 0x2001, 0x1987, 0x2004, 0x604a, 0x0020, 0x2009, 0x0013,
+ 0x080c, 0xad4d, 0x000e, 0x001e, 0x002e, 0x006e, 0x00ce, 0x00de,
+ 0x00ee, 0x00fe, 0x015e, 0x012e, 0x0005, 0x2001, 0x1800, 0x2004,
+ 0x9096, 0x0001, 0x0d78, 0x9096, 0x0004, 0x0d60, 0x080c, 0x8780,
+ 0x6814, 0x9084, 0x0001, 0x0110, 0x68a7, 0x95f5, 0x6817, 0x0008,
+ 0x68c3, 0x0000, 0x2011, 0x5ef7, 0x080c, 0x86c8, 0x20a9, 0x01f4,
+ 0x0009, 0x08c0, 0x6824, 0xd094, 0x0140, 0x6827, 0x0004, 0x7804,
+ 0x9084, 0x4000, 0x190c, 0x2a8a, 0x0090, 0xd084, 0x0118, 0x6827,
+ 0x0001, 0x0010, 0x1f04, 0xa19a, 0x7804, 0x9084, 0x1000, 0x0138,
+ 0x2001, 0x0100, 0x080c, 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x0005,
+ 0x0126, 0x0156, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0026,
+ 0x0016, 0x0006, 0x2091, 0x8000, 0x2001, 0x180c, 0x200c, 0x918c,
+ 0xdbff, 0x2102, 0x2069, 0x0100, 0x2079, 0x0140, 0x2071, 0x0380,
+ 0x701c, 0x0006, 0x701f, 0x0202, 0x2071, 0x19e6, 0x704c, 0x2060,
+ 0x8cff, 0x0904, 0xa231, 0x9386, 0x0002, 0x1128, 0x6814, 0x9084,
+ 0x0002, 0x0904, 0xa231, 0x68af, 0x95f5, 0x6817, 0x0010, 0x2009,
+ 0x00fa, 0x8109, 0x1df0, 0x69c6, 0x68cb, 0x0008, 0x080c, 0x878d,
+ 0x080c, 0x1e2e, 0x2001, 0x0032, 0x6920, 0xd1bc, 0x0130, 0x8001,
+ 0x1dd8, 0x692c, 0x918d, 0x0008, 0x692e, 0x20a9, 0x03e8, 0x6824,
+ 0xd094, 0x0140, 0x6827, 0x0004, 0x7804, 0x9084, 0x4000, 0x190c,
+ 0x2a8a, 0x0090, 0xd08c, 0x0118, 0x6827, 0x0002, 0x0010, 0x1f04,
+ 0xa1ff, 0x7804, 0x9084, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c,
+ 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x6827, 0x4000, 0x6824, 0x83ff,
+ 0x1180, 0x2009, 0x0049, 0x6020, 0x9086, 0x0009, 0x0150, 0x080c,
+ 0x96d5, 0x6044, 0xd0ac, 0x1118, 0x6003, 0x0002, 0x0010, 0x080c,
+ 0xad4d, 0x000e, 0x2071, 0x0380, 0xd08c, 0x1110, 0x701f, 0x0200,
+ 0x000e, 0x001e, 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe,
+ 0x015e, 0x012e, 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000, 0x2069,
+ 0x19e6, 0x6a06, 0x012e, 0x00de, 0x0005, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x2069, 0x19e6, 0x6a3e, 0x012e, 0x00de, 0x0005, 0x080c,
+ 0x9c8f, 0x785c, 0x7032, 0x7042, 0x7047, 0x1000, 0x00f8, 0x080c,
+ 0x9c8f, 0x785c, 0x7032, 0x7042, 0x7047, 0x4000, 0x00b8, 0x080c,
+ 0x9c8f, 0x785c, 0x7032, 0x7042, 0x7047, 0x2000, 0x0078, 0x080c,
+ 0x9c8f, 0x785c, 0x7032, 0x7042, 0x7047, 0x0400, 0x0038, 0x080c,
+ 0x9c8f, 0x785c, 0x7032, 0x7042, 0x7047, 0x0200, 0x60c3, 0x0020,
+ 0x0804, 0x9ea4, 0x00e6, 0x2071, 0x19e6, 0x702c, 0x9005, 0x0110,
+ 0x8001, 0x702e, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6,
+ 0x0076, 0x0066, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x19e6,
+ 0x7620, 0x2660, 0x2678, 0x2039, 0x0001, 0x87ff, 0x0904, 0xa327,
+ 0x8cff, 0x0904, 0xa327, 0x6020, 0x9086, 0x0006, 0x1904, 0xa322,
+ 0x88ff, 0x0138, 0x2800, 0x9c06, 0x1904, 0xa322, 0x2039, 0x0000,
+ 0x0050, 0x6010, 0x9b06, 0x1904, 0xa322, 0x85ff, 0x0120, 0x605c,
+ 0x9106, 0x1904, 0xa322, 0x7030, 0x9c06, 0x15b0, 0x2069, 0x0100,
+ 0x68c0, 0x9005, 0x1160, 0x6824, 0xd084, 0x0148, 0x6827, 0x0001,
+ 0x080c, 0x8780, 0x080c, 0xa3ac, 0x7033, 0x0000, 0x0428, 0x080c,
+ 0x8780, 0x6820, 0xd0b4, 0x0110, 0x68a7, 0x95f5, 0x6817, 0x0008,
+ 0x68c3, 0x0000, 0x080c, 0xa3ac, 0x7033, 0x0000, 0x0036, 0x2069,
+ 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c,
+ 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x2069, 0x0100, 0x6824, 0xd084,
+ 0x0110, 0x6827, 0x0001, 0x003e, 0x7020, 0x9c36, 0x1110, 0x660c,
+ 0x7622, 0x701c, 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00,
+ 0x701e, 0x0010, 0x701f, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06,
+ 0x0110, 0x7e0e, 0x0008, 0x2678, 0x89ff, 0x1168, 0x600f, 0x0000,
+ 0x6014, 0x0096, 0x2048, 0x080c, 0xc978, 0x0110, 0x080c, 0xe3e8,
+ 0x009e, 0x080c, 0xaceb, 0x080c, 0xa282, 0x88ff, 0x1190, 0x00ce,
+ 0x0804, 0xa29d, 0x2c78, 0x600c, 0x2060, 0x0804, 0xa29d, 0x9006,
+ 0x012e, 0x000e, 0x006e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x00fe,
+ 0x0005, 0x601b, 0x0000, 0x00ce, 0x98c5, 0x0001, 0x0c88, 0x00f6,
+ 0x00e6, 0x00d6, 0x0096, 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x19e6, 0x7648, 0x2660, 0x2678, 0x8cff,
+ 0x0904, 0xa39b, 0x6020, 0x9086, 0x0006, 0x1904, 0xa396, 0x87ff,
+ 0x0128, 0x2700, 0x9c06, 0x1904, 0xa396, 0x0040, 0x6010, 0x9b06,
+ 0x15e8, 0x85ff, 0x0118, 0x605c, 0x9106, 0x15c0, 0x704c, 0x9c06,
+ 0x1168, 0x0036, 0x2019, 0x0001, 0x080c, 0xa1b8, 0x703f, 0x0000,
+ 0x9006, 0x704e, 0x706a, 0x7052, 0x706e, 0x003e, 0x7048, 0x9c36,
+ 0x1110, 0x660c, 0x764a, 0x7044, 0x9c36, 0x1140, 0x2c00, 0x9f36,
+ 0x0118, 0x2f00, 0x7046, 0x0010, 0x7047, 0x0000, 0x660c, 0x0066,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x6014, 0x2048, 0x080c, 0xc978, 0x0110, 0x080c, 0xe3e8, 0x080c,
+ 0xaceb, 0x87ff, 0x1198, 0x00ce, 0x0804, 0xa347, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0xa347, 0x9006, 0x012e, 0x000e, 0x002e, 0x006e,
+ 0x00ce, 0x009e, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601b, 0x0000,
+ 0x00ce, 0x97bd, 0x0001, 0x0c80, 0x00e6, 0x2071, 0x19e6, 0x9006,
+ 0x7032, 0x700a, 0x7004, 0x9086, 0x0003, 0x0158, 0x2001, 0x1800,
+ 0x2004, 0x9086, 0x0002, 0x1118, 0x7007, 0x0005, 0x0010, 0x7007,
+ 0x0000, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0066, 0x0026,
+ 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x19e6, 0x2c10, 0x7648,
+ 0x2660, 0x2678, 0x8cff, 0x0540, 0x2200, 0x9c06, 0x1508, 0x7048,
+ 0x9c36, 0x1110, 0x660c, 0x764a, 0x7044, 0x9c36, 0x1140, 0x2c00,
+ 0x9f36, 0x0118, 0x2f00, 0x7046, 0x0010, 0x7047, 0x0000, 0x660c,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x6004, 0x9086, 0x0040, 0x090c, 0x967a, 0x9085, 0x0001, 0x0020,
+ 0x2c78, 0x600c, 0x2060, 0x08b0, 0x012e, 0x000e, 0x002e, 0x006e,
+ 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x0096, 0x00f6, 0x00e6, 0x00d6,
+ 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x19e6, 0x7610, 0x2660, 0x2678, 0x8cff, 0x0904, 0xa499, 0x6010,
+ 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x9206, 0x1904, 0xa494, 0x7030,
+ 0x9c06, 0x1520, 0x2069, 0x0100, 0x68c0, 0x9005, 0x0904, 0xa46b,
+ 0x080c, 0x9ed4, 0x68c3, 0x0000, 0x080c, 0xa3ac, 0x7033, 0x0000,
+ 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001,
+ 0x0100, 0x080c, 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x2069, 0x0100,
+ 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x7010, 0x9c36,
+ 0x1110, 0x660c, 0x7612, 0x700c, 0x9c36, 0x1140, 0x2c00, 0x9f36,
+ 0x0118, 0x2f00, 0x700e, 0x0010, 0x700f, 0x0000, 0x660c, 0x0066,
+ 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000,
+ 0x080c, 0xcb80, 0x1180, 0x080c, 0x326f, 0x080c, 0xcb91, 0x1518,
+ 0x080c, 0xb693, 0x0400, 0x080c, 0xa3ac, 0x6824, 0xd084, 0x09b0,
+ 0x6827, 0x0001, 0x0898, 0x080c, 0xcb91, 0x1118, 0x080c, 0xb693,
+ 0x0090, 0x6014, 0x2048, 0x080c, 0xc978, 0x0168, 0x6020, 0x9086,
+ 0x0003, 0x1508, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c,
+ 0x6de2, 0x080c, 0xcb6b, 0x080c, 0xce0d, 0x080c, 0xaceb, 0x080c,
+ 0xa282, 0x00ce, 0x0804, 0xa414, 0x2c78, 0x600c, 0x2060, 0x0804,
+ 0xa414, 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x009e, 0x0005, 0x6020, 0x9086, 0x0006, 0x1d20, 0x080c,
+ 0xe3e8, 0x0c08, 0x00d6, 0x080c, 0x9b82, 0x7003, 0x0200, 0x7007,
+ 0x0014, 0x60c3, 0x0014, 0x20e1, 0x0001, 0x2099, 0x1988, 0x20e9,
+ 0x0000, 0x20a1, 0x0250, 0x20a9, 0x0004, 0x4003, 0x7023, 0x0004,
+ 0x7027, 0x7878, 0x080c, 0x9ea4, 0x00de, 0x0005, 0x080c, 0x9b82,
+ 0x700b, 0x0800, 0x7814, 0x9084, 0xff00, 0x700e, 0x7814, 0x9084,
+ 0x00ff, 0x7022, 0x782c, 0x7026, 0x7860, 0x9084, 0x00ff, 0x9085,
+ 0x0200, 0x7002, 0x7860, 0x9084, 0xff00, 0x8007, 0x7006, 0x60c2,
+ 0x0804, 0x9ea4, 0x00b6, 0x00d6, 0x0016, 0x00d6, 0x2f68, 0x2009,
+ 0x0035, 0x080c, 0xd013, 0x00de, 0x1904, 0xa547, 0x080c, 0x9b37,
+ 0x7003, 0x1300, 0x782c, 0x080c, 0xa656, 0x2068, 0x6820, 0x9086,
+ 0x0003, 0x0560, 0x7810, 0x2058, 0xbaa0, 0x080c, 0xabe2, 0x11d8,
+ 0x9286, 0x007e, 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffe, 0x0498,
+ 0x9286, 0x007f, 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffd, 0x0458,
+ 0x9284, 0xff80, 0x0180, 0x9286, 0x0080, 0x1128, 0x700b, 0x00ff,
+ 0x700f, 0xfffc, 0x0400, 0x92d8, 0x1000, 0x2b5c, 0xb810, 0x700a,
+ 0xb814, 0x700e, 0x00c0, 0xb884, 0x700e, 0x00a8, 0x080c, 0xabe2,
+ 0x1130, 0x7810, 0x2058, 0xb8a0, 0x9082, 0x007e, 0x0250, 0x00d6,
+ 0x2069, 0x181f, 0x2d04, 0x700a, 0x8d68, 0x2d04, 0x700e, 0x00de,
+ 0x0010, 0x6034, 0x700e, 0x7838, 0x7012, 0x783c, 0x7016, 0x60c3,
+ 0x000c, 0x001e, 0x00de, 0x080c, 0x9ea4, 0x00be, 0x0005, 0x781b,
+ 0x0001, 0x7803, 0x0006, 0x001e, 0x00de, 0x00be, 0x0005, 0x792c,
+ 0x9180, 0x0008, 0x200c, 0x9186, 0x0006, 0x01c0, 0x9186, 0x0003,
+ 0x0904, 0xa5c6, 0x9186, 0x0005, 0x0904, 0xa5ae, 0x9186, 0x0004,
+ 0x05f0, 0x9186, 0x0008, 0x0904, 0xa5b7, 0x7807, 0x0037, 0x782f,
+ 0x0003, 0x7817, 0x1700, 0x080c, 0xa633, 0x0005, 0x080c, 0xa5f4,
+ 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x6800, 0x6a44,
+ 0xd2fc, 0x11f8, 0x0002, 0xa58e, 0xa599, 0xa590, 0xa599, 0xa595,
+ 0xa58e, 0xa58e, 0xa599, 0xa599, 0xa599, 0xa599, 0xa58e, 0xa58e,
+ 0xa58e, 0xa58e, 0xa58e, 0xa599, 0xa58e, 0xa599, 0x080c, 0x0d7d,
+ 0x6824, 0xd0e4, 0x0110, 0xd0cc, 0x0110, 0x900e, 0x0010, 0x2009,
+ 0x2000, 0x682c, 0x7022, 0x6830, 0x7026, 0x0804, 0xa5ed, 0x080c,
+ 0xa5f4, 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x6a00,
+ 0x9286, 0x0002, 0x1108, 0x900e, 0x0804, 0xa5ed, 0x080c, 0xa5f4,
+ 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x04b0, 0x04e1,
+ 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x9286, 0x0005,
+ 0x0118, 0x9286, 0x0002, 0x1108, 0x900e, 0x0438, 0x0469, 0x00d6,
+ 0x0026, 0x792c, 0x2168, 0x6814, 0x6924, 0xc185, 0x6926, 0x0096,
+ 0x2048, 0xa9ac, 0xa834, 0x9112, 0xa9b0, 0xa838, 0x009e, 0x9103,
+ 0x7022, 0x7226, 0x792c, 0x9180, 0x0011, 0x2004, 0xd0fc, 0x1148,
+ 0x9180, 0x0000, 0x2004, 0x908e, 0x0002, 0x0130, 0x908e, 0x0004,
+ 0x0118, 0x2009, 0x4000, 0x0008, 0x900e, 0x712a, 0x60c3, 0x0018,
+ 0x002e, 0x00de, 0x0804, 0x9ea4, 0x00b6, 0x0036, 0x0046, 0x0056,
+ 0x0066, 0x080c, 0x9b82, 0x9006, 0x7003, 0x0200, 0x7938, 0x710a,
+ 0x793c, 0x710e, 0x7810, 0x2058, 0xb8a0, 0x080c, 0xabe2, 0x1118,
+ 0x9092, 0x007e, 0x0268, 0x00d6, 0x2069, 0x181f, 0x2d2c, 0x8d68,
+ 0x2d34, 0x90d8, 0x1000, 0x2b5c, 0xbb10, 0xbc14, 0x00de, 0x0028,
+ 0x901e, 0xbc84, 0x2029, 0x0000, 0x6634, 0x782c, 0x9080, 0x0008,
+ 0x2004, 0x9086, 0x0003, 0x1128, 0x7512, 0x7616, 0x731a, 0x741e,
+ 0x0020, 0x7312, 0x7416, 0x751a, 0x761e, 0x006e, 0x005e, 0x004e,
+ 0x003e, 0x00be, 0x0005, 0x080c, 0x9b82, 0x7003, 0x0100, 0x782c,
+ 0x700a, 0x7814, 0x700e, 0x700e, 0x60c3, 0x0008, 0x0804, 0x9ea4,
+ 0x080c, 0x9b2e, 0x7003, 0x1400, 0x7838, 0x700a, 0x0079, 0x783c,
+ 0x700e, 0x782c, 0x7012, 0x7830, 0x7016, 0x7834, 0x9084, 0x00ff,
+ 0x8007, 0x701a, 0x60c3, 0x0010, 0x0804, 0x9ea4, 0x00e6, 0x2071,
+ 0x0240, 0x0006, 0x00f6, 0x2078, 0x7810, 0x00b6, 0x2058, 0xb8d4,
+ 0xd084, 0x0120, 0x784c, 0x702a, 0x7850, 0x702e, 0x00be, 0x00fe,
+ 0x000e, 0x00ee, 0x0005, 0x080c, 0x9b79, 0x7003, 0x0100, 0x782c,
+ 0x700a, 0x7814, 0x700e, 0x60c3, 0x0008, 0x0804, 0x9ea4, 0x00a9,
+ 0x7914, 0x712a, 0x60c3, 0x0000, 0x60a7, 0x9575, 0x0026, 0x080c,
+ 0x29e5, 0x0228, 0x2011, 0x0101, 0x2204, 0xc0c5, 0x2012, 0x002e,
+ 0x080c, 0x9ec7, 0x080c, 0x8777, 0x0005, 0x0036, 0x0096, 0x00d6,
+ 0x00e6, 0x7860, 0x2048, 0xaa7c, 0x9296, 0x00c0, 0x9294, 0xfffd,
+ 0xaa7e, 0xaa80, 0x9294, 0x0300, 0xaa82, 0xa96c, 0x9194, 0x00ff,
+ 0xab74, 0x9384, 0x00ff, 0x908d, 0xc200, 0xa96e, 0x9384, 0xff00,
+ 0x9215, 0xaa76, 0xa870, 0xaa78, 0xa87a, 0xaa72, 0x00d6, 0x2069,
+ 0x0200, 0x080c, 0xa713, 0x00de, 0x20e9, 0x0000, 0x20a1, 0x0240,
+ 0x20a9, 0x000a, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098,
+ 0x4003, 0x60a3, 0x0035, 0xaa68, 0x9294, 0x7000, 0x9286, 0x3000,
+ 0x0110, 0x60a3, 0x0037, 0x00ee, 0x00de, 0x009e, 0x003e, 0x0005,
+ 0x900e, 0x7814, 0x0096, 0x2048, 0xa87c, 0xd0fc, 0x01c0, 0x9084,
+ 0x0003, 0x11a8, 0x2001, 0x180c, 0x2004, 0xd0bc, 0x0180, 0x7824,
+ 0xd0cc, 0x1168, 0xd0c4, 0x1158, 0xa8a8, 0x9005, 0x1140, 0x2001,
+ 0x180c, 0x200c, 0xc1d5, 0x2102, 0x2009, 0x19b1, 0x210c, 0x009e,
+ 0x918d, 0x0092, 0x0010, 0x2009, 0x0096, 0x60ab, 0x0036, 0x0026,
+ 0x2110, 0x900e, 0x080c, 0x2ac3, 0x002e, 0x0005, 0x2009, 0x0009,
+ 0x00a0, 0x2009, 0x000a, 0x0088, 0x2009, 0x000b, 0x0070, 0x2009,
+ 0x000c, 0x0058, 0x2009, 0x000d, 0x0040, 0x2009, 0x000e, 0x0028,
+ 0x2009, 0x000f, 0x0010, 0x2009, 0x0008, 0x6912, 0x0005, 0x080c,
+ 0x9b37, 0x0016, 0x0026, 0x0096, 0x00d6, 0x7814, 0x2048, 0x7013,
+ 0x0138, 0x2001, 0x1837, 0x2004, 0x9084, 0x0028, 0x1138, 0x2001,
+ 0x197b, 0x2004, 0x9086, 0xaaaa, 0x1904, 0xa7b8, 0x7003, 0x5400,
+ 0x00c6, 0x2061, 0x1800, 0x607c, 0x9084, 0x00ff, 0xa998, 0x810f,
+ 0x918c, 0xff00, 0x9105, 0x700a, 0x6080, 0x700e, 0xa998, 0x918c,
+ 0xff00, 0x7112, 0x20a9, 0x0004, 0x2009, 0x1805, 0x2e10, 0x9290,
+ 0x0006, 0x2104, 0x2012, 0x8108, 0x8210, 0x1f04, 0xa749, 0x20a9,
+ 0x0004, 0x2009, 0x1801, 0x2104, 0x2012, 0x8108, 0x8210, 0x1f04,
+ 0xa753, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0029, 0x2098, 0x2009,
+ 0x0006, 0x20a9, 0x0001, 0x4002, 0x8007, 0x2012, 0x8210, 0x8109,
+ 0x1dc0, 0x00d6, 0x2069, 0x0200, 0x080c, 0xa6fe, 0x00de, 0x2071,
+ 0x0240, 0x2011, 0x0240, 0x2009, 0x0002, 0x20a9, 0x0001, 0x4002,
+ 0x8007, 0x2012, 0x8210, 0x8109, 0x1dc0, 0x2009, 0x0008, 0x20a9,
+ 0x0001, 0x4002, 0x8007, 0x2012, 0x8210, 0x8109, 0x1dc0, 0xa85c,
+ 0x9080, 0x0031, 0x2098, 0x2009, 0x0008, 0x20a9, 0x0001, 0x4002,
+ 0x8007, 0x2012, 0x8210, 0x8109, 0x1dc0, 0x00ce, 0x60c3, 0x004c,
+ 0x60a3, 0x0056, 0x60a7, 0x9575, 0x2001, 0x1837, 0x2004, 0x9084,
+ 0x0028, 0x1168, 0x080c, 0x753d, 0x0150, 0x6028, 0xc0bd, 0x602a,
+ 0x2009, 0x1804, 0x2011, 0x0029, 0x080c, 0x2ac3, 0x0010, 0x080c,
+ 0x9ea4, 0x080c, 0x8777, 0x00de, 0x009e, 0x002e, 0x001e, 0x0005,
+ 0x00e6, 0x2071, 0x0240, 0x2001, 0x2200, 0x9085, 0x00ff, 0x7002,
+ 0x7007, 0xffff, 0x2071, 0x0100, 0x709b, 0x00ff, 0x00ee, 0x0804,
+ 0xa72e, 0x080c, 0x9b37, 0x0016, 0x0026, 0x0096, 0x00d6, 0x7814,
+ 0x2048, 0x7013, 0x0138, 0x7003, 0x5500, 0x00c6, 0xa89c, 0x9084,
+ 0x00ff, 0xa998, 0x810f, 0x918c, 0xff00, 0x9105, 0x700a, 0xa99c,
+ 0x918c, 0xff00, 0xa8a0, 0x9084, 0x00ff, 0x9105, 0x700e, 0xa998,
+ 0x918c, 0xff00, 0x2061, 0x1800, 0x607c, 0x9084, 0x00ff, 0x910d,
+ 0x7112, 0x6180, 0x7116, 0x2009, 0x0008, 0xa860, 0x20e0, 0xa85c,
+ 0x9080, 0x0029, 0x2098, 0x2e10, 0x9290, 0x0006, 0x20a9, 0x0001,
+ 0x4002, 0x8007, 0x2012, 0x8210, 0x8109, 0x1dc0, 0x20a9, 0x0004,
+ 0x2009, 0x1805, 0x2104, 0x2012, 0x8108, 0x8210, 0x1f04, 0xa80a,
+ 0x20a9, 0x0002, 0x2009, 0x1801, 0x2104, 0x2012, 0x8108, 0x8210,
+ 0x1f04, 0xa814, 0x00d6, 0x0016, 0x2069, 0x0200, 0x080c, 0xa6fe,
+ 0x001e, 0x00de, 0x2071, 0x0240, 0x20a9, 0x0002, 0x2009, 0x1803,
+ 0x2011, 0x0240, 0x2104, 0x2012, 0x8108, 0x8210, 0x1f04, 0xa82a,
+ 0x2009, 0x0008, 0x4002, 0x8007, 0x2012, 0x8210, 0x8109, 0x1dd0,
+ 0x9006, 0x20a9, 0x0008, 0x2012, 0x8210, 0x1f04, 0xa83b, 0x00ce,
+ 0x60c3, 0x004c, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0x9ea4,
+ 0x080c, 0x8777, 0x00de, 0x009e, 0x002e, 0x001e, 0x0005, 0x00d6,
+ 0x9290, 0x0018, 0x8214, 0x20e9, 0x0000, 0x2069, 0x0200, 0x6813,
+ 0x0000, 0x22a8, 0x9284, 0x00e0, 0x0128, 0x20a9, 0x0020, 0x9292,
+ 0x0020, 0x0008, 0x9016, 0x20a1, 0x0240, 0x9006, 0x4004, 0x82ff,
+ 0x0120, 0x6810, 0x8000, 0x6812, 0x0c60, 0x00de, 0x0005, 0x00f6,
+ 0x00e6, 0x00d6, 0x00c6, 0x00a6, 0x0096, 0x0066, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x19e6, 0x7610, 0x2660, 0x2678, 0x8cff, 0x0904,
+ 0xa8fb, 0x7030, 0x9c06, 0x1520, 0x2069, 0x0100, 0x68c0, 0x9005,
+ 0x0904, 0xa8cd, 0x080c, 0x9ed4, 0x68c3, 0x0000, 0x080c, 0xa3ac,
+ 0x7033, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000,
+ 0x0138, 0x2001, 0x0100, 0x080c, 0x2a7a, 0x9006, 0x080c, 0x2a7a,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x7010, 0x9c36, 0x1110, 0x660c, 0x7612, 0x700c, 0x9c36, 0x1140,
+ 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x700e, 0x0010, 0x700f, 0x0000,
+ 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x600f, 0x0000, 0x080c, 0xcb80, 0x1180, 0x080c, 0x326f, 0x080c,
+ 0xcb91, 0x1518, 0x080c, 0xb693, 0x0400, 0x080c, 0xa3ac, 0x6824,
+ 0xd084, 0x09b0, 0x6827, 0x0001, 0x0898, 0x080c, 0xcb91, 0x1118,
+ 0x080c, 0xb693, 0x0090, 0x6014, 0x2048, 0x080c, 0xc978, 0x0168,
+ 0x6020, 0x9086, 0x0003, 0x1520, 0xa867, 0x0103, 0xab7a, 0xa877,
+ 0x0000, 0x080c, 0x6dee, 0x080c, 0xcb6b, 0x080c, 0xce0d, 0x080c,
+ 0xaceb, 0x080c, 0xa282, 0x00ce, 0x0804, 0xa87e, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0xa87e, 0x7013, 0x0000, 0x700f, 0x0000, 0x012e,
+ 0x006e, 0x009e, 0x00ae, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005,
+ 0x6020, 0x9086, 0x0006, 0x1d08, 0x080c, 0xe3e8, 0x08f0, 0x00f6,
+ 0x0036, 0x2079, 0x0380, 0x7b18, 0xd3bc, 0x1de8, 0x7832, 0x7936,
+ 0x7a3a, 0x781b, 0x8080, 0x003e, 0x00fe, 0x0005, 0x0016, 0x2001,
+ 0x0382, 0x2004, 0x9084, 0x0007, 0x9086, 0x0001, 0x1188, 0x2001,
+ 0x0015, 0x0c29, 0x2009, 0x1000, 0x2001, 0x0382, 0x2004, 0x9084,
+ 0x0007, 0x9086, 0x0003, 0x0120, 0x8109, 0x1db0, 0x080c, 0x0d7d,
+ 0x001e, 0x0005, 0x2001, 0x0382, 0x2004, 0x9084, 0x0007, 0x9086,
+ 0x0003, 0x1120, 0x2001, 0x0380, 0x2003, 0x0001, 0x0005, 0x0156,
+ 0x0016, 0x0026, 0x00e6, 0x900e, 0x2071, 0x19e6, 0x0469, 0x0106,
+ 0x0190, 0x7004, 0x9086, 0x0003, 0x0148, 0x20a9, 0x1000, 0x6044,
+ 0xd0fc, 0x01d8, 0x1f04, 0xa957, 0x080c, 0x0d7d, 0x080c, 0xa91e,
+ 0x6044, 0xd0fc, 0x0190, 0x7030, 0x9c06, 0x1148, 0x080c, 0x967a,
+ 0x6044, 0xd0dc, 0x0150, 0xc0dc, 0x6046, 0x700a, 0x7042, 0x704c,
+ 0x9c06, 0x190c, 0x0d7d, 0x080c, 0x96d5, 0x010e, 0x1919, 0x00ee,
+ 0x002e, 0x001e, 0x015e, 0x0005, 0x2001, 0x0382, 0x2004, 0x9084,
+ 0x0007, 0x9086, 0x0003, 0x0005, 0x0126, 0x2091, 0x2400, 0x7808,
+ 0xd0a4, 0x190c, 0x0d76, 0xd09c, 0x0128, 0x7820, 0x908c, 0xf000,
+ 0x11b8, 0x0012, 0x012e, 0x0005, 0xa9a4, 0xa9e2, 0xaa0c, 0xaa43,
+ 0xaa53, 0xaa64, 0xaa73, 0xaa81, 0xaaae, 0xaab2, 0xa9a4, 0xa9a4,
+ 0xa9a4, 0xa9a4, 0xa9a4, 0xa9a4, 0x080c, 0x0d7d, 0x012e, 0x0005,
+ 0x2060, 0x6044, 0xd0bc, 0x0140, 0xc0bc, 0x6046, 0x6000, 0x908a,
+ 0x0016, 0x1a0c, 0x0d7d, 0x0012, 0x012e, 0x0005, 0xa9c9, 0xa9cb,
+ 0xa9c9, 0xa9d1, 0xa9c9, 0xa9c9, 0xa9c9, 0xa9c9, 0xa9c9, 0xa9cb,
+ 0xa9c9, 0xa9cb, 0xa9c9, 0xa9cb, 0xa9c9, 0xa9c9, 0xa9c9, 0xa9cb,
+ 0xa9c9, 0x080c, 0x0d7d, 0x2009, 0x0013, 0x080c, 0xad4d, 0x012e,
+ 0x0005, 0x6014, 0x2048, 0xa87c, 0xd0dc, 0x0130, 0x080c, 0x894e,
+ 0x080c, 0xacb0, 0x012e, 0x0005, 0x2009, 0x0049, 0x080c, 0xad4d,
+ 0x012e, 0x0005, 0x080c, 0xa91e, 0x2001, 0x1a0b, 0x2003, 0x0000,
+ 0x7030, 0x9065, 0x090c, 0x0d7d, 0x7034, 0x9092, 0xc350, 0x1258,
+ 0x8000, 0x7036, 0x7004, 0x9086, 0x0003, 0x0110, 0x7007, 0x0000,
+ 0x781f, 0x0808, 0x0058, 0x080c, 0xac0e, 0x0140, 0x080c, 0xe897,
+ 0x6003, 0x0001, 0x2009, 0x0014, 0x080c, 0xad4d, 0x781f, 0x0100,
+ 0x080c, 0xa93a, 0x012e, 0x0005, 0x080c, 0xa91e, 0x714c, 0x81ff,
+ 0x1128, 0x2011, 0x1a0e, 0x2013, 0x0000, 0x0438, 0x2061, 0x0100,
+ 0x7150, 0x9192, 0x7530, 0x12f0, 0x8108, 0x7152, 0x714c, 0x9188,
+ 0x0008, 0x210c, 0x918e, 0x0006, 0x1138, 0x6014, 0x9084, 0x1984,
+ 0x9085, 0x0012, 0x6016, 0x0088, 0x714c, 0x9188, 0x0008, 0x210c,
+ 0x918e, 0x0009, 0x0d90, 0x6014, 0x9084, 0x1984, 0x9085, 0x0016,
+ 0x6016, 0x0018, 0x706c, 0xc085, 0x706e, 0x781f, 0x0200, 0x080c,
+ 0xa93a, 0x012e, 0x0005, 0x080c, 0xa91e, 0x714c, 0x2160, 0x6003,
+ 0x0003, 0x2009, 0x004a, 0x080c, 0xad4d, 0x781f, 0x0200, 0x080c,
+ 0xa93a, 0x012e, 0x0005, 0x7808, 0xd09c, 0x0de8, 0x7820, 0x2060,
+ 0x6003, 0x0003, 0x080c, 0xa91e, 0x080c, 0x1db6, 0x781f, 0x0400,
+ 0x080c, 0xa93a, 0x012e, 0x0005, 0x7808, 0xd09c, 0x0de8, 0x7820,
+ 0x2060, 0x080c, 0xa91e, 0x080c, 0x1dfe, 0x781f, 0x0400, 0x080c,
+ 0xa93a, 0x012e, 0x0005, 0x7030, 0x9065, 0x0148, 0x6044, 0xc0bc,
+ 0x6046, 0x7104, 0x9186, 0x0003, 0x0110, 0x080c, 0x9739, 0x012e,
+ 0x0005, 0x00f6, 0x703c, 0x9086, 0x0002, 0x0528, 0x704c, 0x907d,
+ 0x0510, 0x7844, 0xc0bc, 0x7846, 0x7820, 0x9086, 0x0009, 0x0118,
+ 0x080c, 0x9dfe, 0x00c0, 0x7828, 0xd0fc, 0x1118, 0x080c, 0x9d7d,
+ 0x0090, 0x2001, 0x1837, 0x2004, 0x9084, 0x0028, 0x1130, 0x2001,
+ 0x197b, 0x2004, 0x9086, 0xaaaa, 0x1120, 0x2001, 0x0387, 0x2003,
+ 0x1000, 0x080c, 0x9d02, 0x00fe, 0x012e, 0x0005, 0x080c, 0x75e2,
+ 0x012e, 0x0005, 0x080c, 0x0d7d, 0x0005, 0x00e6, 0x2071, 0x19e6,
+ 0x6044, 0xc0bc, 0x6046, 0xd0fc, 0x01b8, 0x704c, 0x9c06, 0x1190,
+ 0x2019, 0x0001, 0x080c, 0xa1b8, 0x704f, 0x0000, 0x2001, 0x0109,
+ 0x2004, 0xd08c, 0x1138, 0x2001, 0x0108, 0x2004, 0xd0bc, 0x1110,
+ 0x703f, 0x0000, 0x080c, 0xa3c3, 0x00ee, 0x0005, 0x0026, 0x7010,
+ 0x9c06, 0x1178, 0x080c, 0xa282, 0x6044, 0xc0fc, 0x6046, 0x600c,
+ 0x9015, 0x0120, 0x7212, 0x600f, 0x0000, 0x0010, 0x7212, 0x720e,
+ 0x9006, 0x002e, 0x0005, 0x0026, 0x7020, 0x9c06, 0x1178, 0x080c,
+ 0xa282, 0x6044, 0xc0fc, 0x6046, 0x600c, 0x9015, 0x0120, 0x7222,
+ 0x600f, 0x0000, 0x0010, 0x7222, 0x721e, 0x9006, 0x002e, 0x0005,
+ 0x00d6, 0x0036, 0x7830, 0x9c06, 0x1558, 0x2069, 0x0100, 0x68c0,
+ 0x9005, 0x01f8, 0x080c, 0x8780, 0x080c, 0x9ed4, 0x68c3, 0x0000,
+ 0x080c, 0xa3ac, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138,
+ 0x2001, 0x0100, 0x080c, 0x2a7a, 0x9006, 0x080c, 0x2a7a, 0x2069,
+ 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x9085, 0x0001,
+ 0x0038, 0x7808, 0xc0ad, 0x780a, 0x6003, 0x0009, 0x630a, 0x9006,
+ 0x003e, 0x00de, 0x0005, 0x0016, 0x0026, 0x0036, 0x6100, 0x2019,
+ 0x0100, 0x2001, 0x0382, 0x2004, 0xd09c, 0x0190, 0x00c6, 0x0126,
+ 0x2091, 0x2800, 0x0016, 0x0036, 0x080c, 0xa984, 0x003e, 0x001e,
+ 0x012e, 0x00ce, 0x6200, 0x2200, 0x9106, 0x0d58, 0x2200, 0x0010,
+ 0x8319, 0x1d38, 0x003e, 0x002e, 0x001e, 0x0005, 0x00d6, 0x0156,
+ 0x080c, 0x9b82, 0x7a14, 0x82ff, 0x0138, 0x7003, 0x0100, 0x700b,
+ 0x0003, 0x60c3, 0x0008, 0x0490, 0x7003, 0x0200, 0x7007, 0x0000,
+ 0x2069, 0x1800, 0x901e, 0x6800, 0x9086, 0x0004, 0x1110, 0xc38d,
+ 0x0060, 0x080c, 0x753d, 0x1110, 0xc3ad, 0x0008, 0xc3a5, 0x6adc,
+ 0xd29c, 0x1110, 0xd2ac, 0x0108, 0xc39d, 0x730e, 0x080c, 0x8845,
+ 0x20a9, 0x0006, 0x2011, 0xffec, 0x2019, 0xffed, 0x2071, 0x0250,
+ 0x2305, 0x2072, 0x8e70, 0x2205, 0x2072, 0x8e70, 0x9398, 0x0002,
+ 0x9290, 0x0002, 0x1f04, 0xab88, 0x60c3, 0x0020, 0x080c, 0x9ea4,
+ 0x015e, 0x00de, 0x0005, 0x0156, 0x080c, 0x9b82, 0x7a14, 0x82ff,
+ 0x0168, 0x9286, 0xffff, 0x0118, 0x9282, 0x000e, 0x1238, 0x7003,
+ 0x0100, 0x700b, 0x0003, 0x60c3, 0x0008, 0x0488, 0x7003, 0x0200,
+ 0x7007, 0x001c, 0x700f, 0x0001, 0x2011, 0x19bc, 0x2204, 0x8007,
+ 0x701a, 0x8210, 0x2204, 0x8007, 0x701e, 0x0421, 0x1120, 0xb8a0,
+ 0x9082, 0x007f, 0x0248, 0x2001, 0x181f, 0x2004, 0x7022, 0x2001,
+ 0x1820, 0x2004, 0x7026, 0x0030, 0x2001, 0x1818, 0x2004, 0x9084,
+ 0x00ff, 0x7026, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805,
+ 0x20e9, 0x0000, 0x20a1, 0x0256, 0x4003, 0x60c3, 0x001c, 0x015e,
+ 0x0804, 0x9ea4, 0x0006, 0x2001, 0x1837, 0x2004, 0xd0ac, 0x000e,
+ 0x0005, 0x2011, 0x0003, 0x080c, 0xa243, 0x2011, 0x0002, 0x080c,
+ 0xa24d, 0x080c, 0xa138, 0x0036, 0x901e, 0x080c, 0xa1b8, 0x003e,
+ 0x0005, 0x080c, 0x33b2, 0x0188, 0x0016, 0x00b6, 0x00c6, 0x7010,
+ 0x9085, 0x0020, 0x7012, 0x2009, 0x007e, 0x080c, 0x6693, 0xb85c,
+ 0xc0ac, 0xb85e, 0x00ce, 0x00be, 0x001e, 0x0005, 0x00d6, 0x00f6,
+ 0x7104, 0x9186, 0x0004, 0x1120, 0x7410, 0x9e90, 0x0004, 0x0068,
+ 0x9186, 0x0001, 0x1120, 0x7420, 0x9e90, 0x0008, 0x0030, 0x9186,
+ 0x0002, 0x1508, 0x7428, 0x9e90, 0x000a, 0x6110, 0x2468, 0x680c,
+ 0x907d, 0x01c8, 0x7810, 0x9106, 0x1128, 0x2f68, 0x780c, 0x907d,
+ 0x1dc8, 0x0088, 0x780c, 0x680e, 0x7c0e, 0x2f12, 0x9006, 0x7032,
+ 0x7036, 0x7004, 0x9086, 0x0003, 0x0110, 0x7007, 0x0000, 0x9006,
+ 0x00fe, 0x00de, 0x0005, 0x9085, 0x0001, 0x0cd0, 0x2071, 0x188d,
+ 0x7000, 0x9005, 0x0140, 0x2001, 0x0812, 0x2071, 0x1800, 0x7076,
+ 0x707a, 0x706b, 0xffd4, 0x2071, 0x1800, 0x7074, 0x7056, 0x705b,
+ 0x1ddc, 0x0005, 0x00e6, 0x0126, 0x2071, 0x1800, 0x2091, 0x8000,
+ 0x7554, 0x9582, 0x0010, 0x0608, 0x7058, 0x2060, 0x6000, 0x9086,
+ 0x0000, 0x0148, 0x9ce0, 0x001c, 0x7068, 0x9c02, 0x1208, 0x0cb0,
+ 0x2061, 0x1ddc, 0x0c98, 0x6003, 0x0008, 0x8529, 0x7556, 0x9ca8,
+ 0x001c, 0x7068, 0x9502, 0x1230, 0x755a, 0x9085, 0x0001, 0x012e,
+ 0x00ee, 0x0005, 0x705b, 0x1ddc, 0x0cc0, 0x9006, 0x0cc0, 0x00e6,
+ 0x2071, 0x1800, 0x7554, 0x9582, 0x0010, 0x0600, 0x7058, 0x2060,
+ 0x6000, 0x9086, 0x0000, 0x0148, 0x9ce0, 0x001c, 0x7068, 0x9c02,
+ 0x1208, 0x0cb0, 0x2061, 0x1ddc, 0x0c98, 0x6003, 0x0008, 0x8529,
+ 0x7556, 0x9ca8, 0x001c, 0x7068, 0x9502, 0x1228, 0x755a, 0x9085,
+ 0x0001, 0x00ee, 0x0005, 0x705b, 0x1ddc, 0x0cc8, 0x9006, 0x0cc8,
+ 0x9c82, 0x1ddc, 0x0a0c, 0x0d7d, 0x2001, 0x181a, 0x2004, 0x9c02,
+ 0x1a0c, 0x0d7d, 0x9006, 0x6006, 0x600a, 0x600e, 0x6016, 0x601a,
+ 0x6012, 0x6023, 0x0000, 0x6003, 0x0000, 0x601e, 0x605e, 0x6062,
+ 0x6026, 0x602a, 0x602e, 0x6032, 0x6036, 0x603a, 0x603e, 0x604a,
+ 0x602a, 0x6046, 0x6042, 0x2061, 0x1800, 0x6054, 0x8000, 0x6056,
+ 0x0005, 0x9006, 0x600e, 0x6016, 0x601a, 0x6012, 0x6022, 0x6002,
+ 0x601e, 0x605e, 0x6062, 0x604a, 0x6046, 0x2061, 0x1800, 0x6054,
+ 0x8000, 0x6056, 0x0005, 0x0006, 0x6000, 0x9086, 0x0000, 0x01d0,
+ 0x601c, 0xd084, 0x190c, 0x1ac5, 0x6023, 0x0007, 0x2001, 0x1985,
+ 0x2004, 0x0006, 0x9082, 0x0051, 0x000e, 0x0208, 0x8004, 0x601a,
+ 0x080c, 0xe6a0, 0x604b, 0x0000, 0x6044, 0xd0fc, 0x1129, 0x9006,
+ 0x6046, 0x6016, 0x000e, 0x0005, 0x080c, 0xa91e, 0x0106, 0x2001,
+ 0x19f9, 0x2004, 0x9c06, 0x1130, 0x0036, 0x2019, 0x0001, 0x080c,
+ 0xa1b8, 0x003e, 0x080c, 0xa3c3, 0x010e, 0x090c, 0xa93a, 0x0005,
+ 0x00e6, 0x0126, 0x2071, 0x1800, 0x2091, 0x8000, 0x7554, 0x9582,
+ 0x0001, 0x0608, 0x7058, 0x2060, 0x6000, 0x9086, 0x0000, 0x0148,
+ 0x9ce0, 0x001c, 0x7068, 0x9c02, 0x1208, 0x0cb0, 0x2061, 0x1ddc,
+ 0x0c98, 0x6003, 0x0008, 0x8529, 0x7556, 0x9ca8, 0x001c, 0x7068,
+ 0x9502, 0x1230, 0x755a, 0x9085, 0x0001, 0x012e, 0x00ee, 0x0005,
+ 0x705b, 0x1ddc, 0x0cc0, 0x9006, 0x0cc0, 0x6020, 0x9084, 0x000f,
+ 0x0002, 0xad61, 0xad6b, 0xad86, 0xada1, 0xd0ee, 0xd10b, 0xd126,
+ 0xad61, 0xad6b, 0x9025, 0xadbd, 0xad61, 0xad61, 0xad61, 0xad61,
+ 0xad61, 0x9186, 0x0013, 0x1130, 0x6044, 0xd0fc, 0x0110, 0x080c,
+ 0x967a, 0x0005, 0x0005, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c,
+ 0x0d7d, 0x0013, 0x006e, 0x0005, 0xad84, 0xb4fd, 0xb6da, 0xad84,
+ 0xb770, 0xb086, 0xad84, 0xad84, 0xb47f, 0xbcda, 0xad84, 0xad84,
+ 0xad84, 0xad84, 0xad84, 0xad84, 0x080c, 0x0d7d, 0x0066, 0x6000,
+ 0x90b2, 0x0016, 0x1a0c, 0x0d7d, 0x0013, 0x006e, 0x0005, 0xad9f,
+ 0xc2f2, 0xad9f, 0xad9f, 0xad9f, 0xad9f, 0xad9f, 0xad9f, 0xc289,
+ 0xc475, 0xad9f, 0xc32f, 0xc3b3, 0xc32f, 0xc3b3, 0xad9f, 0x080c,
+ 0x0d7d, 0x6000, 0x9082, 0x0016, 0x1a0c, 0x0d7d, 0x6000, 0x0002,
+ 0xadbb, 0xbd24, 0xbdbe, 0xbf3e, 0xbfad, 0xadbb, 0xadbb, 0xadbb,
+ 0xbcf3, 0xc20a, 0xc20d, 0xadbb, 0xadbb, 0xadbb, 0xadbb, 0xc23d,
+ 0xadbb, 0xadbb, 0xadbb, 0x080c, 0x0d7d, 0x0066, 0x6000, 0x90b2,
+ 0x0016, 0x1a0c, 0x0d7d, 0x0013, 0x006e, 0x0005, 0xadd6, 0xadd6,
+ 0xae14, 0xaeb3, 0xaf33, 0xadd6, 0xadd6, 0xadd6, 0xadd8, 0xadd6,
+ 0xadd6, 0xadd6, 0xadd6, 0xadd6, 0xadd6, 0xadd6, 0x080c, 0x0d7d,
+ 0x9186, 0x004c, 0x0560, 0x9186, 0x0003, 0x190c, 0x0d7d, 0x0096,
+ 0x601c, 0xc0ed, 0x601e, 0x6003, 0x0003, 0x6106, 0x6014, 0x2048,
+ 0xa87c, 0x9084, 0xa000, 0xc0b5, 0xa87e, 0xa8ac, 0xa836, 0xa8b0,
+ 0xa83a, 0x9006, 0xa846, 0xa84a, 0xa884, 0x9092, 0x199a, 0x0210,
+ 0x2001, 0x1999, 0x8003, 0x8013, 0x8213, 0x9210, 0x621a, 0x009e,
+ 0x080c, 0x1c10, 0x2009, 0x8030, 0x080c, 0x92f7, 0x0005, 0x6010,
+ 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00, 0x080c, 0xaf55, 0x080c,
+ 0xd0b3, 0x6003, 0x0007, 0x0005, 0x00d6, 0x0096, 0x00f6, 0x2079,
+ 0x1800, 0x7a90, 0x6014, 0x2048, 0xa87c, 0xd0ec, 0x1110, 0x9290,
+ 0x0018, 0xac78, 0xc4fc, 0x0046, 0xa8e0, 0x9005, 0x1140, 0xa8dc,
+ 0x921a, 0x0140, 0x0220, 0xa87b, 0x0007, 0x2010, 0x0028, 0xa87b,
+ 0x0015, 0x0010, 0xa87b, 0x0000, 0x8214, 0xa883, 0x0000, 0xaa02,
+ 0x0006, 0x0016, 0x0026, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2400,
+ 0x9005, 0x1108, 0x009a, 0x2100, 0x9086, 0x0015, 0x1118, 0x2001,
+ 0x0001, 0x0038, 0x2100, 0x9086, 0x0016, 0x0118, 0x2001, 0x0001,
+ 0x002a, 0x94a4, 0x0007, 0x8423, 0x9405, 0x0002, 0xae7b, 0xae7b,
+ 0xae76, 0xae79, 0xae7b, 0xae73, 0xae66, 0xae66, 0xae66, 0xae66,
+ 0xae66, 0xae66, 0xae66, 0xae66, 0xae66, 0xae66, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x002e, 0x001e, 0x000e, 0x004e, 0x00fe, 0x009e,
+ 0x00de, 0x080c, 0x0d7d, 0x080c, 0xb92f, 0x0028, 0x080c, 0xba14,
+ 0x0010, 0x080c, 0xbb0a, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x002e,
+ 0x001e, 0x2c00, 0xa896, 0x000e, 0x080c, 0xb013, 0x0530, 0xa804,
+ 0xa80e, 0x00a6, 0x2050, 0xb100, 0x00ae, 0x8006, 0x8006, 0x8007,
+ 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002, 0xaacc, 0xabd0,
+ 0xacd4, 0xadd8, 0x2031, 0x0000, 0x2041, 0x12b0, 0x080c, 0xb1d4,
+ 0x0160, 0x000e, 0x9005, 0x0120, 0x00fe, 0x009e, 0x00de, 0x0005,
+ 0x00fe, 0x009e, 0x00de, 0x0804, 0xacb0, 0x2001, 0x002c, 0x900e,
+ 0x080c, 0xb079, 0x0c70, 0x91b6, 0x0015, 0x0170, 0x91b6, 0x0016,
+ 0x0158, 0x91b2, 0x0047, 0x0a0c, 0x0d7d, 0x91b2, 0x0050, 0x1a0c,
+ 0x0d7d, 0x9182, 0x0047, 0x0042, 0x080c, 0xab33, 0x0120, 0x9086,
+ 0x0002, 0x0904, 0xae14, 0x0005, 0xaed5, 0xaed5, 0xaed7, 0xaf09,
+ 0xaed5, 0xaed5, 0xaed5, 0xaed5, 0xaf1c, 0x080c, 0x0d7d, 0x00d6,
+ 0x0016, 0x0096, 0x6003, 0x0004, 0x6114, 0x2148, 0xa87c, 0xd0fc,
+ 0x01c0, 0xa878, 0xc0fc, 0x9005, 0x1158, 0xa894, 0x9005, 0x0140,
+ 0x2001, 0x0000, 0x900e, 0x080c, 0xb079, 0x080c, 0xacb0, 0x00a8,
+ 0x6003, 0x0002, 0xa8a4, 0xa9a8, 0x9105, 0x1178, 0xa8ae, 0xa8b2,
+ 0x0c78, 0xa87f, 0x0020, 0xa88c, 0xa88a, 0xa8a4, 0xa8ae, 0xa8a8,
+ 0xa8b2, 0xa8c7, 0x0000, 0xa8cb, 0x0000, 0x009e, 0x001e, 0x00de,
+ 0x0005, 0x080c, 0x96d5, 0x00d6, 0x0096, 0x6114, 0x2148, 0x080c,
+ 0xc97a, 0x0120, 0xa87b, 0x0006, 0x080c, 0x6dee, 0x009e, 0x00de,
+ 0x080c, 0xacb0, 0x0804, 0x9738, 0x080c, 0x96d5, 0x080c, 0x3240,
+ 0x080c, 0xd0b0, 0x00d6, 0x0096, 0x6114, 0x2148, 0x080c, 0xc97a,
+ 0x0120, 0xa87b, 0x0029, 0x080c, 0x6dee, 0x009e, 0x00de, 0x080c,
+ 0xacb0, 0x0804, 0x9738, 0x9182, 0x0047, 0x0002, 0xaf43, 0xaf45,
+ 0xaf43, 0xaf43, 0xaf43, 0xaf43, 0xaf43, 0xaf43, 0xaf43, 0xaf43,
+ 0xaf43, 0xaf43, 0xaf45, 0x080c, 0x0d7d, 0x00d6, 0x0096, 0x601f,
+ 0x0000, 0x6114, 0x2148, 0xa87b, 0x0000, 0xa883, 0x0000, 0x080c,
+ 0x6dee, 0x009e, 0x00de, 0x0804, 0xacb0, 0x0026, 0x0036, 0x0056,
+ 0x0066, 0x0096, 0x00a6, 0x00f6, 0x0006, 0x080c, 0x1047, 0x000e,
+ 0x090c, 0x0d7d, 0xa960, 0x21e8, 0xa95c, 0x9188, 0x0019, 0x21a0,
+ 0x900e, 0x20a9, 0x0020, 0x4104, 0xa87a, 0x2079, 0x1800, 0x7990,
+ 0x9188, 0x0018, 0x918c, 0x0fff, 0xa972, 0xac76, 0x2950, 0x00a6,
+ 0x2001, 0x0205, 0x2003, 0x0000, 0x901e, 0x2029, 0x0001, 0x9182,
+ 0x0034, 0x1228, 0x2011, 0x001f, 0x080c, 0xc4f8, 0x04c0, 0x2130,
+ 0x2009, 0x0034, 0x2011, 0x001f, 0x080c, 0xc4f8, 0x96b2, 0x0034,
+ 0xb004, 0x904d, 0x0110, 0x080c, 0x0ff9, 0x080c, 0x1047, 0x01d0,
+ 0x8528, 0xa867, 0x0110, 0xa86b, 0x0000, 0x2920, 0xb406, 0x968a,
+ 0x003d, 0x1230, 0x2608, 0x2011, 0x001b, 0x080c, 0xc4f8, 0x00b8,
+ 0x96b2, 0x003c, 0x2009, 0x003c, 0x2950, 0x2011, 0x001b, 0x080c,
+ 0xc4f8, 0x0c18, 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f,
+ 0x95ad, 0x0050, 0xb566, 0xb070, 0xc0fd, 0xb072, 0x0048, 0x2001,
+ 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f, 0x95ad, 0x0050, 0xb566,
+ 0x2a48, 0xa804, 0xa807, 0x0000, 0x0006, 0x080c, 0x6dee, 0x000e,
+ 0x2048, 0x9005, 0x1db0, 0x00fe, 0x00ae, 0x009e, 0x006e, 0x005e,
+ 0x003e, 0x002e, 0x0005, 0x00d6, 0x00f6, 0x0096, 0x0006, 0x080c,
+ 0x1047, 0x000e, 0x090c, 0x0d7d, 0xa960, 0x21e8, 0xa95c, 0x9188,
+ 0x0019, 0x21a0, 0x900e, 0x20a9, 0x0020, 0x4104, 0xaa66, 0xa87a,
+ 0x2079, 0x1800, 0x7990, 0x810c, 0x9188, 0x000c, 0x9182, 0x001a,
+ 0x0210, 0x2009, 0x001a, 0x21a8, 0x810b, 0xa972, 0xac76, 0x2e98,
+ 0xa85c, 0x9080, 0x001f, 0x20a0, 0x2001, 0x0205, 0x200c, 0x918d,
+ 0x0080, 0x2102, 0x4003, 0x2003, 0x0000, 0x080c, 0x6dee, 0x009e,
+ 0x00fe, 0x00de, 0x0005, 0x0016, 0x00d6, 0x00f6, 0x0096, 0x0016,
+ 0x2001, 0x0205, 0x200c, 0x918d, 0x0080, 0x2102, 0x001e, 0x2079,
+ 0x0200, 0x2e98, 0xa87c, 0xd0ec, 0x0118, 0x9e80, 0x000c, 0x2098,
+ 0x2021, 0x003e, 0x901e, 0x9282, 0x0020, 0x0218, 0x2011, 0x0020,
+ 0x2018, 0x9486, 0x003e, 0x1170, 0x0096, 0x080c, 0x1047, 0x2900,
+ 0x009e, 0x05c0, 0xa806, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0002, 0x20a0, 0x3300, 0x908e, 0x0260, 0x0140, 0x2009, 0x0280,
+ 0x9102, 0x920a, 0x0218, 0x2010, 0x2100, 0x9318, 0x2200, 0x9402,
+ 0x1228, 0x2400, 0x9202, 0x2410, 0x9318, 0x9006, 0x2020, 0x22a8,
+ 0xa800, 0x9200, 0xa802, 0x20e1, 0x0000, 0x4003, 0x83ff, 0x0180,
+ 0x3300, 0x9086, 0x0280, 0x1130, 0x7814, 0x8000, 0x9085, 0x0080,
+ 0x7816, 0x2e98, 0x2310, 0x84ff, 0x0904, 0xb028, 0x0804, 0xb02a,
+ 0x9085, 0x0001, 0x7817, 0x0000, 0x009e, 0x00fe, 0x00de, 0x001e,
+ 0x0005, 0x00d6, 0x0036, 0x0096, 0x6314, 0x2348, 0xa87a, 0xa982,
+ 0x080c, 0x6de2, 0x009e, 0x003e, 0x00de, 0x0005, 0x91b6, 0x0015,
+ 0x1118, 0x080c, 0xacb0, 0x0030, 0x91b6, 0x0016, 0x190c, 0x0d7d,
+ 0x080c, 0xacb0, 0x0005, 0x20a9, 0x000e, 0x20e1, 0x0000, 0x2e98,
+ 0x6014, 0x0096, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x20a0, 0x009e,
+ 0x4003, 0x0136, 0x9080, 0x001b, 0x20a0, 0x2011, 0x0006, 0x20a9,
+ 0x0001, 0x3418, 0x8318, 0x23a0, 0x4003, 0x3318, 0x8318, 0x2398,
+ 0x8211, 0x1db8, 0x2011, 0x0006, 0x013e, 0x20a0, 0x3318, 0x8318,
+ 0x2398, 0x4003, 0x3418, 0x8318, 0x23a0, 0x8211, 0x1db8, 0x0096,
+ 0x080c, 0xc97a, 0x0130, 0x6014, 0x2048, 0xa807, 0x0000, 0xa867,
+ 0x0103, 0x009e, 0x0804, 0xacb0, 0x0096, 0x00d6, 0x0036, 0x7330,
+ 0x9386, 0x0200, 0x11a8, 0x6010, 0x00b6, 0x2058, 0xb8d7, 0x0000,
+ 0x00be, 0x6014, 0x9005, 0x0130, 0x2048, 0xa807, 0x0000, 0xa867,
+ 0x0103, 0xab32, 0x080c, 0xacb0, 0x003e, 0x00de, 0x009e, 0x0005,
+ 0x0011, 0x1d48, 0x0cc8, 0x0006, 0x0016, 0x080c, 0xd09b, 0x0188,
+ 0x6014, 0x9005, 0x1170, 0x600b, 0x0003, 0x601b, 0x0000, 0x604b,
+ 0x0000, 0x2009, 0x0022, 0x080c, 0xb4d5, 0x9006, 0x001e, 0x000e,
+ 0x0005, 0x9085, 0x0001, 0x0cd0, 0x0096, 0x0016, 0x20a9, 0x0014,
+ 0x9e80, 0x000c, 0x20e1, 0x0000, 0x2098, 0x6014, 0x2048, 0xa860,
+ 0x20e8, 0xa85c, 0x9080, 0x0002, 0x20a0, 0x4003, 0x2001, 0x0205,
+ 0x2003, 0x0001, 0x2099, 0x0260, 0x20a9, 0x0016, 0x4003, 0x20a9,
+ 0x000a, 0xa804, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0002,
+ 0x20a0, 0x4003, 0x2001, 0x0205, 0x2003, 0x0002, 0x2099, 0x0260,
+ 0x20a9, 0x0020, 0x4003, 0x2003, 0x0000, 0x6014, 0x2048, 0xa800,
+ 0x2048, 0xa867, 0x0103, 0x080c, 0xacb0, 0x001e, 0x009e, 0x0005,
+ 0x0096, 0x0016, 0x900e, 0x7030, 0x9086, 0x0100, 0x0140, 0x7038,
+ 0x9084, 0x00ff, 0x800c, 0x703c, 0x9084, 0x00ff, 0x8004, 0x9080,
+ 0x0004, 0x9108, 0x810b, 0x2011, 0x0002, 0x2019, 0x000c, 0x6014,
+ 0x2048, 0x080c, 0xc4f8, 0x080c, 0xc97a, 0x0140, 0x6014, 0x2048,
+ 0xa807, 0x0000, 0xa864, 0xa8e2, 0xa867, 0x0103, 0x080c, 0xacb0,
+ 0x001e, 0x009e, 0x0005, 0x0016, 0x2009, 0x0000, 0x7030, 0x9086,
+ 0x0200, 0x0110, 0x2009, 0x0001, 0x0096, 0x6014, 0x904d, 0x090c,
+ 0x0d7d, 0xa97a, 0x080c, 0x6dee, 0x009e, 0x080c, 0xacb0, 0x001e,
+ 0x0005, 0x0016, 0x0096, 0x7030, 0x9086, 0x0100, 0x1118, 0x2009,
+ 0x0004, 0x0010, 0x7034, 0x800c, 0x810b, 0x2011, 0x000c, 0x2019,
+ 0x000c, 0x6014, 0x2048, 0xa804, 0x0096, 0x9005, 0x0108, 0x2048,
+ 0x080c, 0xc4f8, 0x009e, 0x080c, 0xc97a, 0x0148, 0xa804, 0x9005,
+ 0x1158, 0xa807, 0x0000, 0xa864, 0xa8e2, 0xa867, 0x0103, 0x080c,
+ 0xacb0, 0x009e, 0x001e, 0x0005, 0x0086, 0x2040, 0xa030, 0x8007,
+ 0x9086, 0x0100, 0x1118, 0x080c, 0xb693, 0x00e0, 0xa034, 0x8007,
+ 0x800c, 0x8806, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0,
+ 0x9080, 0x000c, 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897, 0x4000,
+ 0xaaa0, 0xab9c, 0xaca8, 0xada4, 0x2031, 0x0000, 0x2041, 0x1296,
+ 0x0019, 0x0d08, 0x008e, 0x0898, 0x0096, 0x0006, 0x080c, 0x1047,
+ 0x000e, 0x01b0, 0xa8ab, 0x0dcb, 0xa876, 0x000e, 0xa8a2, 0x0006,
+ 0xae6a, 0x2800, 0xa89e, 0xa97a, 0xaf72, 0xaa8e, 0xab92, 0xac96,
+ 0xad9a, 0x0086, 0x2940, 0x080c, 0x113c, 0x008e, 0x9085, 0x0001,
+ 0x009e, 0x0005, 0x00e6, 0x00d6, 0x0026, 0x7008, 0x9084, 0x00ff,
+ 0x6210, 0x00b6, 0x2258, 0xba10, 0x00be, 0x9206, 0x1520, 0x700c,
+ 0x6210, 0x00b6, 0x2258, 0xba14, 0x00be, 0x9206, 0x11e0, 0x604b,
+ 0x0000, 0x2c68, 0x0016, 0x2009, 0x0035, 0x080c, 0xd013, 0x001e,
+ 0x1158, 0x622c, 0x2268, 0x2071, 0x026c, 0x6b20, 0x9386, 0x0003,
+ 0x0130, 0x9386, 0x0006, 0x0128, 0x080c, 0xacb0, 0x0020, 0x0039,
+ 0x0010, 0x080c, 0xb30a, 0x002e, 0x00de, 0x00ee, 0x0005, 0x0096,
+ 0x6814, 0x2048, 0x9186, 0x0015, 0x0904, 0xb2e9, 0x918e, 0x0016,
+ 0x1904, 0xb308, 0x700c, 0x908c, 0xff00, 0x9186, 0x1700, 0x0120,
+ 0x9186, 0x0300, 0x1904, 0xb2c3, 0x89ff, 0x1138, 0x6800, 0x9086,
+ 0x000f, 0x0904, 0xb2a5, 0x0804, 0xb306, 0x6808, 0x9086, 0xffff,
+ 0x1904, 0xb2eb, 0xa87c, 0x9084, 0x0060, 0x9086, 0x0020, 0x1150,
+ 0xa8ac, 0xa934, 0x9106, 0x1904, 0xb2eb, 0xa8b0, 0xa938, 0x9106,
+ 0x1904, 0xb2eb, 0x6824, 0xd084, 0x1904, 0xb2eb, 0xd0b4, 0x0158,
+ 0x0016, 0x2001, 0x1985, 0x200c, 0x6018, 0x9102, 0x9082, 0x0005,
+ 0x001e, 0x1a04, 0xb2eb, 0x080c, 0xcb6b, 0x6810, 0x0096, 0x2048,
+ 0xa9a0, 0x009e, 0x685c, 0xa87a, 0xa976, 0x6864, 0xa882, 0xa87c,
+ 0xc0dc, 0xc0f4, 0xc0d4, 0xa87e, 0x0026, 0x900e, 0x6a18, 0x2001,
+ 0x000a, 0x080c, 0x91f8, 0xa884, 0x920a, 0x0208, 0x8011, 0xaa86,
+ 0x82ff, 0x002e, 0x1138, 0x00c6, 0x2d60, 0x080c, 0xc683, 0x00ce,
+ 0x0804, 0xb306, 0x00c6, 0xa868, 0xd0fc, 0x1118, 0x080c, 0x6124,
+ 0x0010, 0x080c, 0x652f, 0x00ce, 0x1904, 0xb2eb, 0x00c6, 0x2d60,
+ 0x080c, 0xacb0, 0x00ce, 0x0804, 0xb306, 0x00c6, 0x080c, 0xad20,
+ 0x0198, 0x6017, 0x0000, 0x6810, 0x6012, 0x080c, 0xce15, 0x6023,
+ 0x0003, 0x6904, 0x00c6, 0x2d60, 0x080c, 0xacb0, 0x00ce, 0x080c,
+ 0xad4d, 0x00ce, 0x0804, 0xb306, 0x2001, 0x1987, 0x2004, 0x684a,
+ 0x00ce, 0x0804, 0xb306, 0x7008, 0x9086, 0x000b, 0x11c8, 0x6010,
+ 0x00b6, 0x2058, 0xb900, 0xc1bc, 0xb902, 0x00be, 0x00c6, 0x2d60,
+ 0xa87b, 0x0003, 0x080c, 0xd055, 0x6007, 0x0085, 0x6003, 0x000b,
+ 0x6023, 0x0002, 0x2009, 0x8020, 0x080c, 0x92b0, 0x00ce, 0x0430,
+ 0x700c, 0x9086, 0x2a00, 0x1138, 0x2001, 0x1987, 0x2004, 0x684a,
+ 0x00e8, 0x04c1, 0x00e8, 0x89ff, 0x090c, 0x0d7d, 0x00c6, 0x00d6,
+ 0x2d60, 0xa867, 0x0103, 0xa87b, 0x0003, 0x080c, 0x6c04, 0x080c,
+ 0xcb6b, 0x080c, 0xaceb, 0x0026, 0x6010, 0x00b6, 0x2058, 0xba3c,
+ 0x080c, 0x67be, 0x00be, 0x002e, 0x00de, 0x00ce, 0x080c, 0xacb0,
+ 0x009e, 0x0005, 0x9186, 0x0015, 0x1128, 0x2001, 0x1987, 0x2004,
+ 0x684a, 0x0068, 0x918e, 0x0016, 0x1160, 0x00c6, 0x2d00, 0x2060,
+ 0x080c, 0xe6a0, 0x080c, 0x894e, 0x080c, 0xacb0, 0x00ce, 0x080c,
+ 0xacb0, 0x0005, 0x0026, 0x0036, 0x0046, 0x7228, 0xacb0, 0xabac,
+ 0xd2f4, 0x0130, 0x2001, 0x1987, 0x2004, 0x684a, 0x0804, 0xb384,
+ 0x00c6, 0x2d60, 0x080c, 0xc559, 0x00ce, 0x6804, 0x9086, 0x0050,
+ 0x1168, 0x00c6, 0x2d00, 0x2060, 0x6003, 0x0001, 0x6007, 0x0050,
+ 0x2009, 0x8023, 0x080c, 0x92b0, 0x00ce, 0x04f0, 0x6800, 0x9086,
+ 0x000f, 0x01a8, 0x89ff, 0x090c, 0x0d7d, 0x6800, 0x9086, 0x0004,
+ 0x1190, 0xa87c, 0xd0ac, 0x0178, 0xa843, 0x0fff, 0xa83f, 0x0fff,
+ 0xa880, 0xc0fc, 0xa882, 0x2001, 0x0001, 0x6832, 0x0400, 0x2001,
+ 0x0007, 0x6832, 0x00e0, 0xa87c, 0xd0b4, 0x1150, 0xd0ac, 0x0db8,
+ 0x6824, 0xd0f4, 0x1d48, 0xa838, 0xa934, 0x9105, 0x0d80, 0x0c20,
+ 0xd2ec, 0x1d68, 0x7024, 0x9306, 0x1118, 0x7020, 0x9406, 0x0d38,
+ 0x7020, 0x683e, 0x7024, 0x683a, 0x2001, 0x0005, 0x6832, 0x080c,
+ 0xccff, 0x080c, 0x9738, 0x0010, 0x080c, 0xacb0, 0x004e, 0x003e,
+ 0x002e, 0x0005, 0x00e6, 0x00d6, 0x0026, 0x7008, 0x9084, 0x00ff,
+ 0x6210, 0x00b6, 0x2258, 0xba10, 0x00be, 0x9206, 0x1904, 0xb3ef,
+ 0x700c, 0x6210, 0x00b6, 0x2258, 0xba14, 0x00be, 0x9206, 0x1904,
+ 0xb3ef, 0x6038, 0x2068, 0x6824, 0xc0dc, 0x6826, 0x6a20, 0x9286,
+ 0x0007, 0x0904, 0xb3ef, 0x9286, 0x0002, 0x0904, 0xb3ef, 0x9286,
+ 0x0000, 0x05e8, 0x6808, 0x633c, 0x9306, 0x15c8, 0x2071, 0x026c,
+ 0x9186, 0x0015, 0x0570, 0x918e, 0x0016, 0x1100, 0x00c6, 0x6038,
+ 0x2060, 0x6104, 0x9186, 0x004b, 0x01c0, 0x9186, 0x004c, 0x01a8,
+ 0x9186, 0x004d, 0x0190, 0x9186, 0x004e, 0x0178, 0x9186, 0x0052,
+ 0x0160, 0x6014, 0x0096, 0x2048, 0x080c, 0xc97a, 0x090c, 0x0d7d,
+ 0xa87b, 0x0003, 0x009e, 0x080c, 0xd055, 0x6007, 0x0085, 0x6003,
+ 0x000b, 0x6023, 0x0002, 0x2009, 0x8020, 0x080c, 0x92b0, 0x00ce,
+ 0x0030, 0x6038, 0x2070, 0x2001, 0x1987, 0x2004, 0x704a, 0x080c,
+ 0xacb0, 0x002e, 0x00de, 0x00ee, 0x0005, 0x00b6, 0x0096, 0x00f6,
+ 0x6014, 0x2048, 0x6010, 0x2058, 0x91b6, 0x0015, 0x0130, 0xba08,
+ 0xbb0c, 0xbc00, 0xc48c, 0xbc02, 0x0460, 0x0096, 0x0156, 0x0036,
+ 0x0026, 0x2b48, 0x9e90, 0x0010, 0x2019, 0x000a, 0x20a9, 0x0004,
+ 0x080c, 0xbca2, 0x002e, 0x003e, 0x015e, 0x009e, 0x1904, 0xb45e,
+ 0x0096, 0x0156, 0x0036, 0x0026, 0x2b48, 0x9e90, 0x0014, 0x2019,
+ 0x0006, 0x20a9, 0x0004, 0x080c, 0xbca2, 0x002e, 0x003e, 0x015e,
+ 0x009e, 0x15a0, 0x7238, 0xba0a, 0x733c, 0xbb0e, 0xbc00, 0xc48d,
+ 0xbc02, 0xa804, 0x9005, 0x1128, 0x00fe, 0x009e, 0x00be, 0x0804,
+ 0xb0bf, 0x0096, 0x2048, 0xaa12, 0xab16, 0xac0a, 0x009e, 0x8006,
+ 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002,
+ 0x2009, 0x002b, 0xaaa0, 0xab9c, 0xaca8, 0xada4, 0x2031, 0x0000,
+ 0x2041, 0x1296, 0x080c, 0xb1d4, 0x0130, 0x00fe, 0x009e, 0x080c,
+ 0xacb0, 0x00be, 0x0005, 0x080c, 0xb693, 0x0cb8, 0x2b78, 0x00f6,
+ 0x080c, 0x3240, 0x080c, 0xd0b0, 0x00fe, 0x00c6, 0x080c, 0xac5a,
+ 0x2f00, 0x6012, 0x6017, 0x0000, 0x6023, 0x0001, 0x6007, 0x0001,
+ 0x6003, 0x0001, 0x2001, 0x0007, 0x080c, 0x65e3, 0x080c, 0x660f,
+ 0x080c, 0x92b7, 0x080c, 0x9738, 0x00ce, 0x0804, 0xb431, 0x2100,
+ 0x91b2, 0x0053, 0x1a0c, 0x0d7d, 0x91b2, 0x0040, 0x1a04, 0xb4e7,
+ 0x0002, 0xb4d5, 0xb4d5, 0xb4cb, 0xb4d5, 0xb4d5, 0xb4d5, 0xb4c9,
+ 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9,
+ 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9,
+ 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9,
+ 0xb4d5, 0xb4c9, 0xb4d5, 0xb4d5, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9,
+ 0xb4c9, 0xb4cb, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9,
+ 0xb4c9, 0xb4c9, 0xb4c9, 0xb4d5, 0xb4d5, 0xb4c9, 0xb4c9, 0xb4c9,
+ 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4c9, 0xb4d5, 0xb4c9,
+ 0xb4c9, 0x080c, 0x0d7d, 0x0066, 0x00b6, 0x6610, 0x2658, 0xb8d4,
+ 0xc08c, 0xb8d6, 0x00be, 0x006e, 0x0000, 0x6003, 0x0001, 0x6106,
+ 0x9186, 0x0032, 0x0118, 0x080c, 0x92b7, 0x0010, 0x080c, 0x92b0,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x9738, 0x012e, 0x0005, 0x2600,
+ 0x0002, 0xb4d5, 0xb4d5, 0xb4fb, 0xb4d5, 0xb4d5, 0xb4fb, 0xb4fb,
+ 0xb4fb, 0xb4fb, 0xb4d5, 0xb4fb, 0xb4d5, 0xb4fb, 0xb4d5, 0xb4fb,
+ 0xb4fb, 0xb4fb, 0xb4fb, 0x080c, 0x0d7d, 0x6004, 0x90b2, 0x0053,
+ 0x1a0c, 0x0d7d, 0x91b6, 0x0013, 0x0904, 0xb5d2, 0x91b6, 0x0027,
+ 0x1904, 0xb57e, 0x080c, 0x967a, 0x6004, 0x080c, 0xcb80, 0x01b0,
+ 0x080c, 0xcb91, 0x01a8, 0x908e, 0x0021, 0x0904, 0xb57b, 0x908e,
+ 0x0022, 0x1130, 0x080c, 0xb0eb, 0x0904, 0xb577, 0x0804, 0xb578,
+ 0x908e, 0x003d, 0x0904, 0xb57b, 0x0804, 0xb571, 0x080c, 0x326f,
+ 0x2001, 0x0007, 0x080c, 0x65e3, 0x6010, 0x00b6, 0x2058, 0xb9a0,
+ 0x00be, 0x080c, 0xb693, 0x9186, 0x007e, 0x1148, 0x2001, 0x1837,
+ 0x2014, 0xc285, 0x080c, 0x753d, 0x1108, 0xc2ad, 0x2202, 0x080c,
+ 0xa91e, 0x0036, 0x0026, 0x2019, 0x0028, 0x2110, 0x080c, 0xe7ac,
+ 0x002e, 0x003e, 0x0016, 0x0026, 0x0036, 0x2110, 0x2019, 0x0028,
+ 0x080c, 0x943d, 0x0076, 0x903e, 0x080c, 0x9306, 0x6010, 0x00b6,
+ 0x905d, 0x0100, 0x00be, 0x2c08, 0x080c, 0xe167, 0x007e, 0x003e,
+ 0x002e, 0x001e, 0x080c, 0xa93a, 0x080c, 0xd0b0, 0x0016, 0x080c,
+ 0xce0d, 0x080c, 0xacb0, 0x001e, 0x080c, 0x3349, 0x080c, 0x9738,
+ 0x0030, 0x080c, 0xce0d, 0x080c, 0xacb0, 0x080c, 0x9738, 0x0005,
+ 0x080c, 0xb693, 0x0cb0, 0x080c, 0xb6cf, 0x0c98, 0x9186, 0x0015,
+ 0x0118, 0x9186, 0x0016, 0x1140, 0x080c, 0xab33, 0x0d80, 0x9086,
+ 0x0002, 0x0904, 0xb6da, 0x0c58, 0x9186, 0x0014, 0x1d40, 0x080c,
+ 0x967a, 0x6004, 0x908e, 0x0022, 0x1118, 0x080c, 0xb0eb, 0x09f8,
+ 0x080c, 0x3240, 0x080c, 0xd0b0, 0x080c, 0xcb80, 0x1190, 0x080c,
+ 0x326f, 0x6010, 0x00b6, 0x2058, 0xb9a0, 0x00be, 0x080c, 0xb693,
+ 0x9186, 0x007e, 0x1128, 0x2001, 0x1837, 0x200c, 0xc185, 0x2102,
+ 0x0800, 0x080c, 0xcb91, 0x1120, 0x080c, 0xb693, 0x0804, 0xb571,
+ 0x6004, 0x908e, 0x0032, 0x1160, 0x00e6, 0x00f6, 0x2071, 0x189e,
+ 0x2079, 0x0000, 0x080c, 0x35ea, 0x00fe, 0x00ee, 0x0804, 0xb571,
+ 0x6004, 0x908e, 0x0021, 0x0d40, 0x908e, 0x0022, 0x090c, 0xb693,
+ 0x0804, 0xb571, 0x90b2, 0x0040, 0x1a04, 0xb673, 0x2008, 0x0002,
+ 0xb61a, 0xb61b, 0xb61e, 0xb621, 0xb624, 0xb627, 0xb618, 0xb618,
+ 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618,
+ 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618,
+ 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb62a, 0xb635,
+ 0xb618, 0xb636, 0xb635, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618,
+ 0xb635, 0xb635, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618, 0xb618,
+ 0xb618, 0xb618, 0xb65e, 0xb635, 0xb618, 0xb631, 0xb618, 0xb618,
+ 0xb618, 0xb632, 0xb618, 0xb618, 0xb618, 0xb635, 0xb659, 0xb618,
+ 0x080c, 0x0d7d, 0x00d0, 0x2001, 0x000b, 0x00f8, 0x2001, 0x0003,
+ 0x00e0, 0x2001, 0x0005, 0x00c8, 0x2001, 0x0001, 0x00b0, 0x2001,
+ 0x0009, 0x0098, 0x6003, 0x0005, 0x080c, 0xd0b3, 0x080c, 0x9738,
+ 0x0058, 0x0018, 0x0010, 0x080c, 0x65e3, 0x04b8, 0x080c, 0xd0b3,
+ 0x6003, 0x0004, 0x080c, 0x9738, 0x0005, 0x080c, 0x65e3, 0x6003,
+ 0x0002, 0x0036, 0x2019, 0x1852, 0x2304, 0x9084, 0xff00, 0x1120,
+ 0x2001, 0x1985, 0x201c, 0x0040, 0x8007, 0x909a, 0x0004, 0x0ec0,
+ 0x8003, 0x801b, 0x831b, 0x9318, 0x631a, 0x003e, 0x080c, 0x9738,
+ 0x0c18, 0x080c, 0xce0d, 0x080c, 0xacb0, 0x08f0, 0x00e6, 0x00f6,
+ 0x2071, 0x189e, 0x2079, 0x0000, 0x080c, 0x35ea, 0x00fe, 0x00ee,
+ 0x080c, 0x967a, 0x080c, 0xacb0, 0x0878, 0x6003, 0x0002, 0x080c,
+ 0xd0b3, 0x0804, 0x9738, 0x2600, 0x2008, 0x0002, 0xb68a, 0xb66d,
+ 0xb688, 0xb66d, 0xb66d, 0xb688, 0xb688, 0xb688, 0xb688, 0xb66d,
+ 0xb688, 0xb66d, 0xb688, 0xb66d, 0xb688, 0xb688, 0xb688, 0xb688,
+ 0x080c, 0x0d7d, 0x0096, 0x6014, 0x2048, 0x080c, 0x6dee, 0x009e,
+ 0x080c, 0xacb0, 0x0005, 0x00e6, 0x0096, 0x0026, 0x0016, 0x080c,
+ 0xc97a, 0x0568, 0x6014, 0x2048, 0xa864, 0x9086, 0x0139, 0x11a8,
+ 0xa894, 0x9086, 0x0056, 0x1148, 0x080c, 0x54ca, 0x0130, 0x2001,
+ 0x0000, 0x900e, 0x2011, 0x4000, 0x0028, 0x2001, 0x0030, 0x900e,
+ 0x2011, 0x4005, 0x080c, 0xcf7a, 0x0090, 0xa868, 0xd0fc, 0x0178,
+ 0xa807, 0x0000, 0x0016, 0x6004, 0x908e, 0x0021, 0x0168, 0x908e,
+ 0x003d, 0x0150, 0x001e, 0xa867, 0x0103, 0xa833, 0x0100, 0x001e,
+ 0x002e, 0x009e, 0x00ee, 0x0005, 0x001e, 0x0009, 0x0cc0, 0x0096,
+ 0x6014, 0x2048, 0xa800, 0x2048, 0xa867, 0x0103, 0xa823, 0x8001,
+ 0x009e, 0x0005, 0x00b6, 0x6610, 0x2658, 0xb804, 0x9084, 0x00ff,
+ 0x90b2, 0x000c, 0x1a0c, 0x0d7d, 0x6604, 0x96b6, 0x004d, 0x1120,
+ 0x080c, 0xce99, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x0043, 0x1120,
+ 0x080c, 0xcee2, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x004b, 0x1120,
+ 0x080c, 0xcf0e, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x0033, 0x1120,
+ 0x080c, 0xce2f, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x0028, 0x1120,
+ 0x080c, 0xcbcf, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x0029, 0x1120,
+ 0x080c, 0xcc10, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x001f, 0x1120,
+ 0x080c, 0xb093, 0x0804, 0xb75f, 0x6604, 0x96b6, 0x0000, 0x1118,
+ 0x080c, 0xb3f5, 0x04e0, 0x6604, 0x96b6, 0x0022, 0x1118, 0x080c,
+ 0xb0cc, 0x04a8, 0x6604, 0x96b6, 0x0035, 0x1118, 0x080c, 0xb1f2,
+ 0x0470, 0x6604, 0x96b6, 0x0039, 0x1118, 0x080c, 0xb38a, 0x0438,
+ 0x6604, 0x96b6, 0x003d, 0x1118, 0x080c, 0xb104, 0x0400, 0x6604,
+ 0x96b6, 0x0044, 0x1118, 0x080c, 0xb140, 0x00c8, 0x6604, 0x96b6,
+ 0x0049, 0x1118, 0x080c, 0xb181, 0x0090, 0x6604, 0x96b6, 0x0041,
+ 0x1118, 0x080c, 0xb16b, 0x0058, 0x91b6, 0x0015, 0x1110, 0x0063,
+ 0x0030, 0x91b6, 0x0016, 0x1128, 0x00be, 0x0804, 0xb9bb, 0x00be,
+ 0x0005, 0x080c, 0xad6a, 0x0cd8, 0xb77c, 0xb77f, 0xb77c, 0xb7c6,
+ 0xb77c, 0xb92f, 0xb9c8, 0xb77c, 0xb77c, 0xb991, 0xb77c, 0xb9a7,
+ 0x0096, 0x601f, 0x0000, 0x6014, 0x2048, 0xa800, 0x2048, 0xa867,
+ 0x0103, 0x009e, 0x0804, 0xacb0, 0xa001, 0xa001, 0x0005, 0x00e6,
+ 0x2071, 0x1800, 0x7090, 0x9086, 0x0074, 0x1540, 0x080c, 0xe138,
+ 0x11b0, 0x6010, 0x00b6, 0x2058, 0x7030, 0xd08c, 0x0128, 0xb800,
+ 0xd0bc, 0x0110, 0xc0c5, 0xb802, 0x00f9, 0x00be, 0x2001, 0x0006,
+ 0x080c, 0x65e3, 0x080c, 0x326f, 0x080c, 0xacb0, 0x0098, 0x2001,
+ 0x000a, 0x080c, 0x65e3, 0x080c, 0x326f, 0x6003, 0x0001, 0x6007,
+ 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0020, 0x2001, 0x0001,
+ 0x080c, 0xb8ff, 0x00ee, 0x0005, 0x00d6, 0xb800, 0xd084, 0x0160,
+ 0x9006, 0x080c, 0x65cf, 0x2069, 0x1847, 0x6804, 0xd0a4, 0x0120,
+ 0x2001, 0x0006, 0x080c, 0x660f, 0x00de, 0x0005, 0x00b6, 0x0096,
+ 0x00d6, 0x2011, 0x1824, 0x2204, 0x9086, 0x0074, 0x1904, 0xb8d4,
+ 0x6010, 0x2058, 0xbaa0, 0x9286, 0x007e, 0x1120, 0x080c, 0xbb15,
+ 0x0804, 0xb838, 0x080c, 0xbb0a, 0x6010, 0x2058, 0xbaa0, 0x9286,
+ 0x0080, 0x1510, 0x6014, 0x9005, 0x01a8, 0x2048, 0xa864, 0x9084,
+ 0x00ff, 0x9086, 0x0039, 0x1140, 0x2001, 0x0000, 0x900e, 0x2011,
+ 0x4000, 0x080c, 0xcf7a, 0x0030, 0xa807, 0x0000, 0xa867, 0x0103,
+ 0xa833, 0x0200, 0x2001, 0x0006, 0x080c, 0x65e3, 0x080c, 0x326f,
+ 0x080c, 0xacb0, 0x0804, 0xb8d9, 0x080c, 0xb8e7, 0x6014, 0x9005,
+ 0x0190, 0x2048, 0xa868, 0xd0f4, 0x01e8, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0039, 0x1d08, 0x2001, 0x0000, 0x900e, 0x2011, 0x4000,
+ 0x080c, 0xcf7a, 0x08f8, 0x080c, 0xb8dd, 0x0160, 0x9006, 0x080c,
+ 0x65cf, 0x2001, 0x0004, 0x080c, 0x660f, 0x2001, 0x0007, 0x080c,
+ 0x65e3, 0x08a0, 0x2001, 0x0004, 0x080c, 0x65e3, 0x6003, 0x0001,
+ 0x6007, 0x0003, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0804, 0xb8d9,
+ 0xb85c, 0xd0e4, 0x01d8, 0x080c, 0xcda7, 0x080c, 0x753d, 0x0118,
+ 0xd0dc, 0x1904, 0xb7fa, 0x2011, 0x1837, 0x2204, 0xc0ad, 0x2012,
+ 0x2001, 0x196c, 0x2004, 0x00f6, 0x2079, 0x0100, 0x78e3, 0x0000,
+ 0x080c, 0x26d5, 0x78e2, 0x00fe, 0x0804, 0xb7fa, 0x080c, 0xcde8,
+ 0x2011, 0x1837, 0x2204, 0xc0a5, 0x2012, 0x0006, 0x080c, 0xe2c8,
+ 0x000e, 0x1904, 0xb7fa, 0xc0b5, 0x2012, 0x2001, 0x0006, 0x080c,
+ 0x65e3, 0x9006, 0x080c, 0x65cf, 0x00c6, 0x2001, 0x180f, 0x2004,
+ 0xd09c, 0x0520, 0x00f6, 0x2079, 0x0100, 0x00e6, 0x2071, 0x1800,
+ 0x700c, 0x9084, 0x00ff, 0x78e6, 0x707e, 0x7010, 0x78ea, 0x7082,
+ 0x908c, 0x00ff, 0x00ee, 0x780c, 0xc0b5, 0x780e, 0x00fe, 0x080c,
+ 0x26aa, 0x00f6, 0x2100, 0x900e, 0x080c, 0x2661, 0x795e, 0x00fe,
+ 0x9186, 0x0081, 0x01d8, 0x2009, 0x0081, 0x00c8, 0x2009, 0x00ef,
+ 0x00f6, 0x2079, 0x0100, 0x79ea, 0x7932, 0x7936, 0x780c, 0xc0b5,
+ 0x780e, 0x00fe, 0x080c, 0x26aa, 0x00f6, 0x2079, 0x1800, 0x7982,
+ 0x2100, 0x900e, 0x080c, 0x2661, 0x795e, 0x00fe, 0x8108, 0x080c,
+ 0x6632, 0x2b00, 0x00ce, 0x1904, 0xb7fa, 0x6012, 0x2009, 0x180f,
+ 0x210c, 0xd19c, 0x0150, 0x2009, 0x027c, 0x210c, 0x918c, 0x00ff,
+ 0xb912, 0x2009, 0x027d, 0x210c, 0xb916, 0x2001, 0x0002, 0x080c,
+ 0x65e3, 0x6023, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c,
+ 0x92b7, 0x080c, 0x9738, 0x0028, 0x080c, 0xb693, 0x2001, 0x0001,
+ 0x0431, 0x00de, 0x009e, 0x00be, 0x0005, 0x2001, 0x1810, 0x2004,
+ 0xd0a4, 0x0120, 0x2001, 0x1848, 0x2004, 0xd0ac, 0x0005, 0x00e6,
+ 0x080c, 0xe805, 0x0190, 0x2071, 0x0260, 0x7108, 0x720c, 0x918c,
+ 0x00ff, 0x1118, 0x9284, 0xff00, 0x0140, 0x6010, 0x2058, 0xb8a0,
+ 0x9084, 0xff80, 0x1110, 0xb912, 0xba16, 0x00ee, 0x0005, 0x2030,
+ 0x9005, 0x0158, 0x2001, 0x0007, 0x080c, 0x65e3, 0x080c, 0x5752,
+ 0x1120, 0x2001, 0x0007, 0x080c, 0x660f, 0x2600, 0x9005, 0x11b0,
+ 0x6014, 0x0096, 0x2048, 0xa868, 0x009e, 0xd0fc, 0x1178, 0x0036,
+ 0x0046, 0x6010, 0x00b6, 0x2058, 0xbba0, 0x00be, 0x2021, 0x0004,
+ 0x2011, 0x8014, 0x080c, 0x4b52, 0x004e, 0x003e, 0x080c, 0x326f,
+ 0x6020, 0x9086, 0x000a, 0x1108, 0x0005, 0x0804, 0xacb0, 0x00b6,
+ 0x00e6, 0x0026, 0x0016, 0x2071, 0x1800, 0x7090, 0x9086, 0x0014,
+ 0x1904, 0xb987, 0x080c, 0x5752, 0x1170, 0x6014, 0x9005, 0x1158,
+ 0x0036, 0x0046, 0x6010, 0x2058, 0xbba0, 0x2021, 0x0006, 0x080c,
+ 0x4d09, 0x004e, 0x003e, 0x00d6, 0x6010, 0x2058, 0x080c, 0x672e,
+ 0x080c, 0xb7b4, 0x00de, 0x080c, 0xbbdb, 0x1588, 0x6010, 0x2058,
+ 0xb890, 0x9005, 0x0560, 0x2001, 0x0006, 0x080c, 0x65e3, 0x0096,
+ 0x6014, 0x904d, 0x01d0, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0039,
+ 0x1140, 0x2001, 0x0000, 0x900e, 0x2011, 0x4000, 0x080c, 0xcf7a,
+ 0x0060, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0029, 0x0130, 0xa807,
+ 0x0000, 0xa867, 0x0103, 0xa833, 0x0200, 0x009e, 0x080c, 0x326f,
+ 0x6020, 0x9086, 0x000a, 0x0140, 0x080c, 0xacb0, 0x0028, 0x080c,
+ 0xb693, 0x9006, 0x080c, 0xb8ff, 0x001e, 0x002e, 0x00ee, 0x00be,
+ 0x0005, 0x2011, 0x1824, 0x2204, 0x9086, 0x0014, 0x1160, 0x2001,
+ 0x0002, 0x080c, 0x65e3, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c,
+ 0x92b7, 0x0804, 0x9738, 0x2001, 0x0001, 0x0804, 0xb8ff, 0x2030,
+ 0x2011, 0x1824, 0x2204, 0x9086, 0x0004, 0x1148, 0x96b6, 0x000b,
+ 0x1120, 0x2001, 0x0007, 0x080c, 0x65e3, 0x0804, 0xacb0, 0x2001,
+ 0x0001, 0x0804, 0xb8ff, 0x0002, 0xb77c, 0xb9d3, 0xb77c, 0xba14,
+ 0xb77c, 0xbac1, 0xb9c8, 0xb77c, 0xb77c, 0xbad5, 0xb77c, 0xbae7,
+ 0x6604, 0x9686, 0x0003, 0x0904, 0xb92f, 0x96b6, 0x001e, 0x1110,
+ 0x080c, 0xacb0, 0x0005, 0x00b6, 0x00d6, 0x00c6, 0x080c, 0xbaf9,
+ 0x11a0, 0x9006, 0x080c, 0x65cf, 0x080c, 0x3240, 0x080c, 0xd0b0,
+ 0x2001, 0x0002, 0x080c, 0x65e3, 0x6003, 0x0001, 0x6007, 0x0002,
+ 0x080c, 0x92b7, 0x080c, 0x9738, 0x0418, 0x2009, 0x026e, 0x2104,
+ 0x9086, 0x0009, 0x1160, 0x6010, 0x2058, 0xb840, 0x9084, 0x00ff,
+ 0x9005, 0x0170, 0x8001, 0xb842, 0x601b, 0x000a, 0x0088, 0x2009,
+ 0x026f, 0x2104, 0x9084, 0xff00, 0x9086, 0x1900, 0x1108, 0x08a0,
+ 0x080c, 0x3240, 0x080c, 0xd0b0, 0x2001, 0x0001, 0x080c, 0xb8ff,
+ 0x00ce, 0x00de, 0x00be, 0x0005, 0x0096, 0x00b6, 0x0026, 0x9016,
+ 0x080c, 0xbb07, 0x00d6, 0x2069, 0x197b, 0x2d04, 0x9005, 0x0168,
+ 0x6010, 0x2058, 0xb8a0, 0x9086, 0x007e, 0x1138, 0x2069, 0x1820,
+ 0x2d04, 0x8000, 0x206a, 0x00de, 0x0010, 0x00de, 0x0088, 0x9006,
+ 0x080c, 0x65cf, 0x2001, 0x0002, 0x080c, 0x65e3, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0804, 0xba91,
+ 0x080c, 0xc97a, 0x01b0, 0x6014, 0x2048, 0xa864, 0x2010, 0x9086,
+ 0x0139, 0x1138, 0x6007, 0x0016, 0x2001, 0x0002, 0x080c, 0xcfd4,
+ 0x00b0, 0x6014, 0x2048, 0xa864, 0xd0fc, 0x0118, 0x2001, 0x0001,
+ 0x0ca8, 0x2001, 0x180e, 0x2004, 0xd0dc, 0x0148, 0x6010, 0x2058,
+ 0xb840, 0x9084, 0x00ff, 0x9005, 0x1110, 0x9006, 0x0c38, 0x080c,
+ 0xb693, 0x2009, 0x026e, 0x2134, 0x96b4, 0x00ff, 0x9686, 0x0005,
+ 0x0520, 0x9686, 0x000b, 0x01c8, 0x2009, 0x026f, 0x2104, 0x9084,
+ 0xff00, 0x1118, 0x9686, 0x0009, 0x01c0, 0x9086, 0x1900, 0x1168,
+ 0x9686, 0x0009, 0x0190, 0x2001, 0x0004, 0x080c, 0x65e3, 0x2001,
+ 0x0028, 0x601a, 0x6007, 0x0052, 0x0020, 0x2001, 0x0001, 0x080c,
+ 0xb8ff, 0x002e, 0x00be, 0x009e, 0x0005, 0x9286, 0x0139, 0x0160,
+ 0x6014, 0x2048, 0x080c, 0xc97a, 0x0140, 0xa864, 0x9086, 0x0139,
+ 0x0118, 0xa868, 0xd0fc, 0x0108, 0x0c40, 0x6010, 0x2058, 0xb840,
+ 0x9084, 0x00ff, 0x9005, 0x0138, 0x8001, 0xb842, 0x601b, 0x000a,
+ 0x6007, 0x0016, 0x08f0, 0xb8a0, 0x9086, 0x007e, 0x1138, 0x00e6,
+ 0x2071, 0x1800, 0x080c, 0x6025, 0x00ee, 0x0010, 0x080c, 0x3240,
+ 0x0860, 0x080c, 0xbb07, 0x1160, 0x2001, 0x0004, 0x080c, 0x65e3,
+ 0x6003, 0x0001, 0x6007, 0x0003, 0x080c, 0x92b7, 0x0804, 0x9738,
+ 0x080c, 0xb693, 0x9006, 0x0804, 0xb8ff, 0x0489, 0x1160, 0x2001,
+ 0x0008, 0x080c, 0x65e3, 0x6003, 0x0001, 0x6007, 0x0005, 0x080c,
+ 0x92b7, 0x0804, 0x9738, 0x2001, 0x0001, 0x0804, 0xb8ff, 0x00f9,
+ 0x1160, 0x2001, 0x000a, 0x080c, 0x65e3, 0x6003, 0x0001, 0x6007,
+ 0x0001, 0x080c, 0x92b7, 0x0804, 0x9738, 0x2001, 0x0001, 0x0804,
+ 0xb8ff, 0x2009, 0x026e, 0x2104, 0x9086, 0x0003, 0x1138, 0x2009,
+ 0x026f, 0x2104, 0x9084, 0xff00, 0x9086, 0x2a00, 0x0005, 0x9085,
+ 0x0001, 0x0005, 0x00b6, 0x00c6, 0x0016, 0x6110, 0x2158, 0x080c,
+ 0x66a2, 0x001e, 0x00ce, 0x00be, 0x0005, 0x00b6, 0x00f6, 0x00e6,
+ 0x00d6, 0x0036, 0x0016, 0x6010, 0x2058, 0x2009, 0x1837, 0x2104,
+ 0x9085, 0x0003, 0x200a, 0x080c, 0xbbad, 0x0560, 0x2009, 0x1837,
+ 0x2104, 0xc0cd, 0x200a, 0x080c, 0x6ad9, 0x0158, 0x9006, 0x2020,
+ 0x2009, 0x002a, 0x080c, 0xe445, 0x2001, 0x180c, 0x200c, 0xc195,
+ 0x2102, 0x2019, 0x002a, 0x2009, 0x0001, 0x080c, 0x3205, 0x00e6,
+ 0x2071, 0x1800, 0x080c, 0x3011, 0x00ee, 0x00c6, 0x0156, 0x20a9,
+ 0x0781, 0x2009, 0x007f, 0x080c, 0x3349, 0x8108, 0x1f04, 0xbb4b,
+ 0x015e, 0x00ce, 0x080c, 0xbb0a, 0x2071, 0x0260, 0x2079, 0x0200,
+ 0x7817, 0x0001, 0x2001, 0x1837, 0x200c, 0xc1c5, 0x7018, 0xd0fc,
+ 0x0110, 0xd0dc, 0x0118, 0x7038, 0xd0dc, 0x1108, 0xc1c4, 0x7817,
+ 0x0000, 0x2001, 0x1837, 0x2102, 0x2079, 0x0100, 0x2e04, 0x9084,
+ 0x00ff, 0x2069, 0x181f, 0x206a, 0x78e6, 0x0006, 0x8e70, 0x2e04,
+ 0x2069, 0x1820, 0x206a, 0x78ea, 0x7832, 0x7836, 0x2010, 0x9084,
+ 0xff00, 0x001e, 0x9105, 0x2009, 0x182c, 0x200a, 0x2200, 0x9084,
+ 0x00ff, 0x2008, 0x080c, 0x26aa, 0x080c, 0x753d, 0x0170, 0x2071,
+ 0x0260, 0x2069, 0x1981, 0x7048, 0x206a, 0x704c, 0x6806, 0x7050,
+ 0x680a, 0x7054, 0x680e, 0x080c, 0xcda7, 0x0040, 0x2001, 0x0006,
+ 0x080c, 0x65e3, 0x080c, 0x326f, 0x080c, 0xacb0, 0x001e, 0x003e,
+ 0x00de, 0x00ee, 0x00fe, 0x00be, 0x0005, 0x0096, 0x0026, 0x0036,
+ 0x00e6, 0x0156, 0x2019, 0x182c, 0x231c, 0x83ff, 0x01f0, 0x2071,
+ 0x0260, 0x7200, 0x9294, 0x00ff, 0x7004, 0x9084, 0xff00, 0x9205,
+ 0x9306, 0x1198, 0x2011, 0x0276, 0x20a9, 0x0004, 0x2b48, 0x2019,
+ 0x000a, 0x080c, 0xbca2, 0x1148, 0x2011, 0x027a, 0x20a9, 0x0004,
+ 0x2019, 0x0006, 0x080c, 0xbca2, 0x1100, 0x015e, 0x00ee, 0x003e,
+ 0x002e, 0x009e, 0x0005, 0x00e6, 0x2071, 0x0260, 0x7034, 0x9086,
+ 0x0014, 0x11a8, 0x7038, 0x9086, 0x0800, 0x1188, 0x703c, 0xd0ec,
+ 0x0160, 0x9084, 0x0f00, 0x9086, 0x0100, 0x1138, 0x7054, 0xd0a4,
+ 0x1110, 0xd0ac, 0x0110, 0x9006, 0x0010, 0x9085, 0x0001, 0x00ee,
+ 0x0005, 0x00e6, 0x0096, 0x00c6, 0x0076, 0x0056, 0x0046, 0x0026,
+ 0x0006, 0x0126, 0x2091, 0x8000, 0x2029, 0x19f2, 0x252c, 0x2021,
+ 0x19f9, 0x2424, 0x2061, 0x1ddc, 0x2071, 0x1800, 0x7254, 0x7074,
+ 0x9202, 0x1a04, 0xbc6e, 0x080c, 0x8c1f, 0x0904, 0xbc67, 0x080c,
+ 0xe476, 0x0904, 0xbc67, 0x6720, 0x9786, 0x0007, 0x0904, 0xbc67,
+ 0x2500, 0x9c06, 0x0904, 0xbc67, 0x2400, 0x9c06, 0x0904, 0xbc67,
+ 0x3e08, 0x9186, 0x0002, 0x1148, 0x6010, 0x9005, 0x0130, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1590, 0x00c6, 0x6043, 0xffff,
+ 0x6000, 0x9086, 0x0004, 0x1110, 0x080c, 0x1ac5, 0x9786, 0x000a,
+ 0x0148, 0x080c, 0xcb91, 0x1130, 0x00ce, 0x080c, 0xb693, 0x080c,
+ 0xaceb, 0x00e8, 0x6014, 0x2048, 0x080c, 0xc97a, 0x01a8, 0x9786,
+ 0x0003, 0x1530, 0xa867, 0x0103, 0xa87c, 0xd0cc, 0x0130, 0x0096,
+ 0xa878, 0x2048, 0x080c, 0x0ff9, 0x009e, 0xab7a, 0xa877, 0x0000,
+ 0x080c, 0x6de2, 0x080c, 0xcb6b, 0x080c, 0xaceb, 0x00ce, 0x9ce0,
+ 0x001c, 0x7068, 0x9c02, 0x1210, 0x0804, 0xbc0e, 0x012e, 0x000e,
+ 0x002e, 0x004e, 0x005e, 0x007e, 0x00ce, 0x009e, 0x00ee, 0x0005,
+ 0x9786, 0x0006, 0x1118, 0x080c, 0xe3e8, 0x0c30, 0x9786, 0x0009,
+ 0x1148, 0x6000, 0x9086, 0x0004, 0x0d08, 0x2009, 0x004c, 0x080c,
+ 0xad4d, 0x08e0, 0x9786, 0x000a, 0x0980, 0x0820, 0x220c, 0x2304,
+ 0x9106, 0x1130, 0x8210, 0x8318, 0x1f04, 0xbc8e, 0x9006, 0x0005,
+ 0x2304, 0x9102, 0x0218, 0x2001, 0x0001, 0x0008, 0x9006, 0x918d,
+ 0x0001, 0x0005, 0x0136, 0x01c6, 0x0016, 0x8906, 0x8006, 0x8007,
+ 0x908c, 0x003f, 0x21e0, 0x9084, 0xffc0, 0x9300, 0x2098, 0x3518,
+ 0x20a9, 0x0001, 0x220c, 0x4002, 0x910e, 0x1140, 0x8210, 0x8319,
+ 0x1dc8, 0x9006, 0x001e, 0x01ce, 0x013e, 0x0005, 0x220c, 0x9102,
+ 0x0218, 0x2001, 0x0001, 0x0010, 0x2001, 0x0000, 0x918d, 0x0001,
+ 0x001e, 0x01ce, 0x013e, 0x0005, 0x220c, 0x810f, 0x2304, 0x9106,
+ 0x1130, 0x8210, 0x8318, 0x1f04, 0xbccc, 0x9006, 0x0005, 0x918d,
+ 0x0001, 0x0005, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0d7d, 0x080c,
+ 0xcb80, 0x0120, 0x080c, 0xcb91, 0x0158, 0x0028, 0x080c, 0x326f,
+ 0x080c, 0xcb91, 0x0128, 0x080c, 0x967a, 0x080c, 0xacb0, 0x0005,
+ 0x080c, 0xb693, 0x0cc0, 0x9182, 0x0057, 0x1220, 0x9182, 0x0040,
+ 0x0208, 0x000a, 0x0005, 0xbd12, 0xbd12, 0xbd12, 0xbd12, 0xbd12,
+ 0xbd12, 0xbd12, 0xbd12, 0xbd12, 0xbd12, 0xbd12, 0xbd14, 0xbd14,
+ 0xbd14, 0xbd14, 0xbd12, 0xbd12, 0xbd12, 0xbd14, 0xbd12, 0xbd12,
+ 0xbd12, 0xbd12, 0x080c, 0x0d7d, 0x600b, 0xffff, 0x6003, 0x000f,
+ 0x6106, 0x0126, 0x2091, 0x8000, 0x080c, 0xd0b3, 0x2009, 0x8000,
+ 0x080c, 0x92b0, 0x012e, 0x0005, 0x9186, 0x0013, 0x1128, 0x6004,
+ 0x9082, 0x0040, 0x0804, 0xbd9c, 0x9186, 0x0027, 0x1520, 0x080c,
+ 0x967a, 0x080c, 0x3240, 0x080c, 0xd0b0, 0x0096, 0x6114, 0x2148,
+ 0x080c, 0xc97a, 0x0198, 0x080c, 0xcb91, 0x1118, 0x080c, 0xb693,
+ 0x0068, 0xa867, 0x0103, 0xa87b, 0x0029, 0xa877, 0x0000, 0xa97c,
+ 0xc1c5, 0xa97e, 0x080c, 0x6dee, 0x080c, 0xcb6b, 0x009e, 0x080c,
+ 0xacb0, 0x0804, 0x9738, 0x9186, 0x0014, 0x1120, 0x6004, 0x9082,
+ 0x0040, 0x0030, 0x9186, 0x0053, 0x0110, 0x080c, 0x0d7d, 0x0005,
+ 0x0002, 0xbd7a, 0xbd78, 0xbd78, 0xbd78, 0xbd78, 0xbd78, 0xbd78,
+ 0xbd78, 0xbd78, 0xbd78, 0xbd78, 0xbd93, 0xbd93, 0xbd93, 0xbd93,
+ 0xbd78, 0xbd93, 0xbd78, 0xbd93, 0xbd78, 0xbd78, 0xbd78, 0xbd78,
+ 0x080c, 0x0d7d, 0x080c, 0x967a, 0x0096, 0x6114, 0x2148, 0x080c,
+ 0xc97a, 0x0168, 0xa867, 0x0103, 0xa87b, 0x0006, 0xa877, 0x0000,
+ 0xa880, 0xc0ec, 0xa882, 0x080c, 0x6dee, 0x080c, 0xcb6b, 0x009e,
+ 0x080c, 0xacb0, 0x0005, 0x080c, 0x967a, 0x080c, 0xcb91, 0x090c,
+ 0xb693, 0x080c, 0xacb0, 0x0005, 0x0002, 0xbdb6, 0xbdb4, 0xbdb4,
+ 0xbdb4, 0xbdb4, 0xbdb4, 0xbdb4, 0xbdb4, 0xbdb4, 0xbdb4, 0xbdb4,
+ 0xbdb8, 0xbdb8, 0xbdb8, 0xbdb8, 0xbdb4, 0xbdba, 0xbdb4, 0xbdb8,
+ 0xbdb4, 0xbdb4, 0xbdb4, 0xbdb4, 0x080c, 0x0d7d, 0x080c, 0x0d7d,
+ 0x080c, 0x0d7d, 0x080c, 0xacb0, 0x0804, 0x9738, 0x9182, 0x0057,
+ 0x1220, 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xbddd, 0xbddd,
+ 0xbddd, 0xbddd, 0xbddd, 0xbe16, 0xbf05, 0xbddd, 0xbf11, 0xbddd,
+ 0xbddd, 0xbddd, 0xbddd, 0xbddd, 0xbddd, 0xbddd, 0xbddd, 0xbddd,
+ 0xbddd, 0xbf11, 0xbddf, 0xbddd, 0xbf0f, 0x080c, 0x0d7d, 0x00b6,
+ 0x0096, 0x6114, 0x2148, 0x6010, 0x2058, 0xb800, 0xd0bc, 0x1508,
+ 0xa87b, 0x0000, 0xa867, 0x0103, 0xa877, 0x0000, 0xa87c, 0xd0ac,
+ 0x0128, 0xa834, 0xa938, 0x9115, 0x190c, 0xbf96, 0x080c, 0x6c04,
+ 0x6210, 0x2258, 0xba3c, 0x82ff, 0x0110, 0x8211, 0xba3e, 0xb8d0,
+ 0x9005, 0x0110, 0x080c, 0x67be, 0x080c, 0xacb0, 0x009e, 0x00be,
+ 0x0005, 0xa87c, 0xd0ac, 0x09e0, 0xa838, 0xa934, 0x9105, 0x09c0,
+ 0xa880, 0xd0bc, 0x19a8, 0x080c, 0xccc6, 0x0c80, 0x00b6, 0x0096,
+ 0x6114, 0x2148, 0x601c, 0xd0fc, 0x1110, 0x7644, 0x0008, 0x9036,
+ 0x96b4, 0x0fff, 0x86ff, 0x1590, 0x6010, 0x2058, 0xb800, 0xd0bc,
+ 0x1904, 0xbef4, 0xa87b, 0x0000, 0xa867, 0x0103, 0xae76, 0xa87c,
+ 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115, 0x190c, 0xbf96, 0x080c,
+ 0x6c04, 0x6210, 0x2258, 0xba3c, 0x82ff, 0x0110, 0x8211, 0xba3e,
+ 0xb8d0, 0x9005, 0x0110, 0x080c, 0x67be, 0x601c, 0xd0fc, 0x1148,
+ 0x7044, 0xd0e4, 0x1904, 0xbed8, 0x080c, 0xacb0, 0x009e, 0x00be,
+ 0x0005, 0x2009, 0x0211, 0x210c, 0x080c, 0x0d7d, 0x968c, 0x0c00,
+ 0x0150, 0x6010, 0x2058, 0xb800, 0xd0bc, 0x1904, 0xbedc, 0x7348,
+ 0xab92, 0x734c, 0xab8e, 0x968c, 0x00ff, 0x9186, 0x0002, 0x0508,
+ 0x9186, 0x0028, 0x1118, 0xa87b, 0x001c, 0x00e8, 0xd6dc, 0x01a0,
+ 0xa87b, 0x0015, 0xa87c, 0xd0ac, 0x0170, 0xa938, 0xaa34, 0x2100,
+ 0x9205, 0x0148, 0x7048, 0x9106, 0x1118, 0x704c, 0x9206, 0x0118,
+ 0xa992, 0xaa8e, 0xc6dc, 0x0038, 0xd6d4, 0x0118, 0xa87b, 0x0007,
+ 0x0010, 0xa87b, 0x0000, 0xa867, 0x0103, 0xae76, 0x901e, 0xd6c4,
+ 0x01d8, 0x9686, 0x0100, 0x1130, 0x7064, 0x9005, 0x1118, 0xc6c4,
+ 0x0804, 0xbe22, 0x735c, 0xab86, 0x83ff, 0x0170, 0x938a, 0x0009,
+ 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019, 0x0018, 0x2011,
+ 0x0025, 0x080c, 0xc4f8, 0x003e, 0xd6cc, 0x0904, 0xbe37, 0x7154,
+ 0xa98a, 0x81ff, 0x0904, 0xbe37, 0x9192, 0x0021, 0x1278, 0x8304,
+ 0x9098, 0x0018, 0x2011, 0x0029, 0x080c, 0xc4f8, 0x2011, 0x0205,
+ 0x2013, 0x0000, 0x080c, 0xd040, 0x0804, 0xbe37, 0xa868, 0xd0fc,
+ 0x0120, 0x2009, 0x0020, 0xa98a, 0x0c50, 0x00a6, 0x2950, 0x080c,
+ 0xc497, 0x00ae, 0x080c, 0xd040, 0x080c, 0xc4e8, 0x0804, 0xbe39,
+ 0x080c, 0xcc89, 0x0804, 0xbe4e, 0xa87c, 0xd0ac, 0x0904, 0xbe5f,
+ 0xa880, 0xd0bc, 0x1904, 0xbe5f, 0x7348, 0xa838, 0x9306, 0x11c8,
+ 0x734c, 0xa834, 0x931e, 0x0904, 0xbe5f, 0xd6d4, 0x0190, 0xab38,
+ 0x9305, 0x0904, 0xbe5f, 0x0068, 0xa87c, 0xd0ac, 0x0904, 0xbe2a,
+ 0xa838, 0xa934, 0x9105, 0x0904, 0xbe2a, 0xa880, 0xd0bc, 0x1904,
+ 0xbe2a, 0x080c, 0xccc6, 0x0804, 0xbe4e, 0x00f6, 0x2079, 0x026c,
+ 0x7c04, 0x7b00, 0x7e0c, 0x7d08, 0x00fe, 0x0021, 0x0005, 0x0011,
+ 0x0005, 0x0005, 0x0096, 0x6003, 0x0002, 0x6007, 0x0043, 0x6014,
+ 0x2048, 0xa87c, 0xd0ac, 0x0128, 0x009e, 0x0005, 0x2130, 0x2228,
+ 0x0058, 0x2400, 0xa9ac, 0x910a, 0x2300, 0xaab0, 0x9213, 0x2600,
+ 0x9102, 0x2500, 0x9203, 0x0e90, 0xac46, 0xab4a, 0xae36, 0xad3a,
+ 0x6044, 0xd0fc, 0x190c, 0xa947, 0x604b, 0x0000, 0x080c, 0x1c86,
+ 0x1118, 0x6144, 0x080c, 0x92dc, 0x009e, 0x0005, 0x9182, 0x0057,
+ 0x1220, 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xbf5d, 0xbf5d,
+ 0xbf5d, 0xbf5d, 0xbf5d, 0xbf5d, 0xbf5d, 0xbf5d, 0xbf5d, 0xbf5d,
+ 0xbf5f, 0xbf5d, 0xbf5d, 0xbf5d, 0xbf5d, 0xbf70, 0xbf5d, 0xbf5d,
+ 0xbf5d, 0xbf5d, 0xbf94, 0xbf5d, 0xbf5d, 0x080c, 0x0d7d, 0x6004,
+ 0x9086, 0x0040, 0x1110, 0x080c, 0x967a, 0x2019, 0x0001, 0x080c,
+ 0xa1b8, 0x6003, 0x0002, 0x080c, 0xd0b8, 0x080c, 0x96d5, 0x0005,
+ 0x6004, 0x9086, 0x0040, 0x1110, 0x080c, 0x967a, 0x2019, 0x0001,
+ 0x080c, 0xa1b8, 0x080c, 0x96d5, 0x080c, 0x3240, 0x080c, 0xd0b0,
+ 0x0096, 0x6114, 0x2148, 0x080c, 0xc97a, 0x0150, 0xa867, 0x0103,
+ 0xa87b, 0x0029, 0xa877, 0x0000, 0x080c, 0x6dee, 0x080c, 0xcb6b,
+ 0x009e, 0x080c, 0xacb0, 0x0005, 0x080c, 0x0d7d, 0xa87b, 0x0015,
+ 0xd1fc, 0x0180, 0xa87b, 0x0007, 0x8002, 0x8000, 0x810a, 0x9189,
+ 0x0000, 0x0006, 0x0016, 0x2009, 0x1a77, 0x2104, 0x8000, 0x200a,
+ 0x001e, 0x000e, 0xa992, 0xa88e, 0x0005, 0x9182, 0x0057, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xbfcc, 0xbfcc, 0xbfcc,
+ 0xbfcc, 0xbfcc, 0xbfce, 0xbfcc, 0xbfcc, 0xc08b, 0xbfcc, 0xbfcc,
+ 0xbfcc, 0xbfcc, 0xbfcc, 0xbfcc, 0xbfcc, 0xbfcc, 0xbfcc, 0xbfcc,
+ 0xc1cb, 0xbfcc, 0xc1d5, 0xbfcc, 0x080c, 0x0d7d, 0x601c, 0xd0bc,
+ 0x0178, 0xd084, 0x0168, 0xd0f4, 0x0120, 0xc084, 0x601e, 0x0804,
+ 0xbdbe, 0x6114, 0x0096, 0x2148, 0xa87c, 0xc0e5, 0xa87e, 0x009e,
+ 0x0076, 0x00a6, 0x00e6, 0x0096, 0x2071, 0x0260, 0x6114, 0x2150,
+ 0x601c, 0xd0fc, 0x1110, 0x7644, 0x0008, 0x9036, 0xb676, 0x96b4,
+ 0x0fff, 0xb77c, 0xc7e5, 0xb77e, 0x6210, 0x00b6, 0x2258, 0xba3c,
+ 0x82ff, 0x0110, 0x8211, 0xba3e, 0x00be, 0x86ff, 0x0904, 0xc084,
+ 0x9694, 0xff00, 0x9284, 0x0c00, 0x0120, 0x7048, 0xb092, 0x704c,
+ 0xb08e, 0x9284, 0x0300, 0x0904, 0xc084, 0x9686, 0x0100, 0x1130,
+ 0x7064, 0x9005, 0x1118, 0xc6c4, 0xb676, 0x0c38, 0x080c, 0x1047,
+ 0x090c, 0x0d7d, 0x2900, 0xb07a, 0xb77c, 0x97bd, 0x0200, 0xb77e,
+ 0xa867, 0x0103, 0xb068, 0xa86a, 0xb06c, 0xa86e, 0xb070, 0xa872,
+ 0x7044, 0x9084, 0xf000, 0x9635, 0xae76, 0x968c, 0x0c00, 0x0120,
+ 0x7348, 0xab92, 0x734c, 0xab8e, 0x968c, 0x00ff, 0x9186, 0x0002,
+ 0x0180, 0x9186, 0x0028, 0x1118, 0xa87b, 0x001c, 0x0060, 0xd6dc,
+ 0x0118, 0xa87b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0xa87b, 0x0007,
+ 0x0010, 0xa87b, 0x0000, 0xaf7e, 0xb080, 0xa882, 0xb084, 0xa886,
+ 0x901e, 0xd6c4, 0x0190, 0x735c, 0xab86, 0x83ff, 0x0170, 0x938a,
+ 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019, 0x0018,
+ 0x2011, 0x0025, 0x080c, 0xc4f8, 0x003e, 0xd6cc, 0x01e8, 0x7154,
+ 0xa98a, 0x81ff, 0x01c8, 0x9192, 0x0021, 0x1260, 0x8304, 0x9098,
+ 0x0018, 0x2011, 0x0029, 0x080c, 0xc4f8, 0x2011, 0x0205, 0x2013,
+ 0x0000, 0x0050, 0xb068, 0xd0fc, 0x0120, 0x2009, 0x0020, 0xa98a,
+ 0x0c68, 0x2950, 0x080c, 0xc497, 0x080c, 0x1a93, 0x009e, 0x00ee,
+ 0x00ae, 0x007e, 0x0005, 0x2001, 0x1987, 0x2004, 0x604a, 0x0096,
+ 0x6114, 0x2148, 0xa83c, 0xa940, 0x9105, 0x1118, 0xa87c, 0xc0dc,
+ 0xa87e, 0x6003, 0x0002, 0x080c, 0xd0c1, 0x0904, 0xc1c6, 0x604b,
+ 0x0000, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1500,
+ 0xd1cc, 0x0904, 0xc18a, 0xa978, 0xa868, 0xd0fc, 0x0904, 0xc14b,
+ 0x0016, 0xa87c, 0x0006, 0xa880, 0x0006, 0x00a6, 0x2150, 0xb174,
+ 0x9184, 0x00ff, 0x90b6, 0x0002, 0x0904, 0xc118, 0x9086, 0x0028,
+ 0x1904, 0xc104, 0xa87b, 0x001c, 0xb07b, 0x001c, 0x0804, 0xc120,
+ 0x6024, 0xd0f4, 0x11d0, 0xa838, 0xaa34, 0x9205, 0x09c8, 0xa838,
+ 0xaa90, 0x9206, 0x1120, 0xa88c, 0xaa34, 0x9206, 0x0988, 0x6024,
+ 0xd0d4, 0x1148, 0xa9ac, 0xa834, 0x9102, 0x603a, 0xa9b0, 0xa838,
+ 0x9103, 0x603e, 0x6024, 0xc0f5, 0x6026, 0x6010, 0x00b6, 0x2058,
+ 0xb83c, 0x8000, 0xb83e, 0x00be, 0x601c, 0xc0fc, 0x601e, 0x9006,
+ 0xa876, 0xa892, 0xa88e, 0xa87c, 0xc0e4, 0xa87e, 0xd0cc, 0x0140,
+ 0xc0cc, 0xa87e, 0x0096, 0xa878, 0x2048, 0x080c, 0x0ff9, 0x009e,
+ 0x080c, 0xccc6, 0x0804, 0xc1c6, 0xd1dc, 0x0158, 0xa87b, 0x0015,
+ 0xb07b, 0x0015, 0x080c, 0xcf63, 0x0118, 0xb174, 0xc1dc, 0xb176,
+ 0x0078, 0xd1d4, 0x0128, 0xa87b, 0x0007, 0xb07b, 0x0007, 0x0040,
+ 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115, 0x190c, 0xbf96,
+ 0xa87c, 0xb07e, 0xa890, 0xb092, 0xa88c, 0xb08e, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x0019, 0x20a0, 0x20a9, 0x0020, 0x8a06, 0x8006,
+ 0x8007, 0x9094, 0x003f, 0x22e0, 0x9084, 0xffc0, 0x9080, 0x0019,
+ 0x2098, 0x4003, 0x00ae, 0x000e, 0xa882, 0x000e, 0xc0cc, 0xa87e,
+ 0x080c, 0xd040, 0x001e, 0xa874, 0x0006, 0x2148, 0x080c, 0x0ff9,
+ 0x001e, 0x0804, 0xc1b7, 0x0016, 0x00a6, 0x2150, 0xb174, 0x9184,
+ 0x00ff, 0x90b6, 0x0002, 0x01e0, 0x9086, 0x0028, 0x1128, 0xa87b,
+ 0x001c, 0xb07b, 0x001c, 0x00e0, 0xd1dc, 0x0158, 0xa87b, 0x0015,
+ 0xb07b, 0x0015, 0x080c, 0xcf63, 0x0118, 0xb174, 0xc1dc, 0xb176,
+ 0x0078, 0xd1d4, 0x0128, 0xa87b, 0x0007, 0xb07b, 0x0007, 0x0040,
+ 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115, 0x190c, 0xbf96,
+ 0xa890, 0xb092, 0xa88c, 0xb08e, 0xa87c, 0xb07e, 0x00ae, 0x080c,
+ 0x0ff9, 0x009e, 0x080c, 0xd040, 0xa974, 0x0016, 0x080c, 0xc4e8,
+ 0x001e, 0x0468, 0xa867, 0x0103, 0xa974, 0x9184, 0x00ff, 0x90b6,
+ 0x0002, 0x01b0, 0x9086, 0x0028, 0x1118, 0xa87b, 0x001c, 0x00d0,
+ 0xd1dc, 0x0148, 0xa87b, 0x0015, 0x080c, 0xcf63, 0x0118, 0xa974,
+ 0xc1dc, 0xa976, 0x0078, 0xd1d4, 0x0118, 0xa87b, 0x0007, 0x0050,
+ 0xa87b, 0x0000, 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115,
+ 0x190c, 0xbf96, 0xa974, 0x0016, 0x080c, 0x6c04, 0x001e, 0x6010,
+ 0x00b6, 0x2058, 0xba3c, 0xb8d0, 0x0016, 0x9005, 0x190c, 0x67be,
+ 0x001e, 0x00be, 0xd1e4, 0x1120, 0x080c, 0xacb0, 0x009e, 0x0005,
+ 0x080c, 0xcc89, 0x0cd8, 0x6114, 0x0096, 0x2148, 0xa97c, 0x080c,
+ 0xd0c1, 0x190c, 0x1ab1, 0x009e, 0x0005, 0x0096, 0x6114, 0x2148,
+ 0xa83c, 0xa940, 0x9105, 0x01e8, 0xa877, 0x0000, 0xa87b, 0x0000,
+ 0xa867, 0x0103, 0x00b6, 0x6010, 0x2058, 0xa834, 0xa938, 0x9115,
+ 0x11a0, 0x080c, 0x6c04, 0xba3c, 0x8211, 0x0208, 0xba3e, 0xb8d0,
+ 0x9005, 0x0110, 0x080c, 0x67be, 0x080c, 0xacb0, 0x00be, 0x009e,
+ 0x0005, 0xa87c, 0xc0dc, 0xa87e, 0x08f8, 0xb800, 0xd0bc, 0x1120,
+ 0xa834, 0x080c, 0xbf96, 0x0c28, 0xa880, 0xd0bc, 0x1dc8, 0x080c,
+ 0xccc6, 0x0c60, 0x080c, 0x967a, 0x0010, 0x080c, 0x96d5, 0x601c,
+ 0xd084, 0x0110, 0x080c, 0x1ac5, 0x080c, 0xc97a, 0x01f0, 0x0096,
+ 0x6114, 0x2148, 0x080c, 0xcb91, 0x1118, 0x080c, 0xb693, 0x00a0,
+ 0xa867, 0x0103, 0x2009, 0x180c, 0x210c, 0xd18c, 0x1198, 0xd184,
+ 0x1170, 0x6108, 0xa97a, 0x918e, 0x0029, 0x1110, 0x080c, 0xe79d,
+ 0xa877, 0x0000, 0x080c, 0x6dee, 0x009e, 0x0804, 0xaceb, 0xa87b,
+ 0x0004, 0x0cb0, 0xa87b, 0x0004, 0x0c98, 0x9182, 0x0057, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xc25c, 0xc25c, 0xc25c,
+ 0xc25c, 0xc25c, 0xc25e, 0xc25c, 0xc25c, 0xc25c, 0xc25c, 0xc25c,
+ 0xc25c, 0xc25c, 0xc25c, 0xc25c, 0xc25c, 0xc25c, 0xc25c, 0xc25c,
+ 0xc25c, 0xc282, 0xc25c, 0xc25c, 0x080c, 0x0d7d, 0x080c, 0x5746,
+ 0x01f8, 0x6014, 0x7144, 0x918c, 0x0fff, 0x9016, 0xd1c4, 0x0118,
+ 0x7264, 0x9294, 0x00ff, 0x0096, 0x904d, 0x0188, 0xa87b, 0x0000,
+ 0xa864, 0x9086, 0x0139, 0x0128, 0xa867, 0x0103, 0xa976, 0xaa96,
+ 0x0030, 0xa897, 0x4000, 0xa99a, 0xaa9e, 0x080c, 0x6dee, 0x009e,
+ 0x0804, 0xacb0, 0x080c, 0x5746, 0x0dd8, 0x6014, 0x900e, 0x9016,
+ 0x0c10, 0x9182, 0x0085, 0x0002, 0xc29b, 0xc299, 0xc299, 0xc2a7,
+ 0xc299, 0xc299, 0xc299, 0xc299, 0xc299, 0xc299, 0xc299, 0xc299,
+ 0xc299, 0x080c, 0x0d7d, 0x6003, 0x0001, 0x6106, 0x0126, 0x2091,
+ 0x8000, 0x2009, 0x8020, 0x080c, 0x92b0, 0x012e, 0x0005, 0x0026,
+ 0x0056, 0x00d6, 0x00e6, 0x2071, 0x0260, 0x7224, 0x6216, 0x7220,
+ 0x080c, 0xc968, 0x01f8, 0x2268, 0x6800, 0x9086, 0x0000, 0x01d0,
+ 0x6010, 0x6d10, 0x952e, 0x11b0, 0x00c6, 0x2d60, 0x00d6, 0x080c,
+ 0xc559, 0x00de, 0x00ce, 0x0158, 0x702c, 0xd084, 0x1118, 0x080c,
+ 0xc523, 0x0010, 0x6803, 0x0002, 0x6007, 0x0086, 0x0028, 0x080c,
+ 0xc545, 0x0d90, 0x6007, 0x0087, 0x6003, 0x0001, 0x2009, 0x8020,
+ 0x080c, 0x92b0, 0x7220, 0x080c, 0xc968, 0x0178, 0x6810, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0140, 0x6824, 0xd0ec, 0x0128,
+ 0x00c6, 0x2d60, 0x080c, 0xccc6, 0x00ce, 0x00ee, 0x00de, 0x005e,
+ 0x002e, 0x0005, 0x9186, 0x0013, 0x1160, 0x6004, 0x908a, 0x0085,
+ 0x0a0c, 0x0d7d, 0x908a, 0x0092, 0x1a0c, 0x0d7d, 0x9082, 0x0085,
+ 0x00e2, 0x9186, 0x0027, 0x0120, 0x9186, 0x0014, 0x190c, 0x0d7d,
+ 0x080c, 0x967a, 0x0096, 0x6014, 0x2048, 0x080c, 0xc97a, 0x0140,
+ 0xa867, 0x0103, 0xa877, 0x0000, 0xa87b, 0x0029, 0x080c, 0x6dee,
+ 0x009e, 0x080c, 0xaceb, 0x0804, 0x9738, 0xc32a, 0xc32c, 0xc32c,
+ 0xc32a, 0xc32a, 0xc32a, 0xc32a, 0xc32a, 0xc32a, 0xc32a, 0xc32a,
+ 0xc32a, 0xc32a, 0x080c, 0x0d7d, 0x080c, 0xaceb, 0x0005, 0x9186,
+ 0x0013, 0x1130, 0x6004, 0x9082, 0x0085, 0x2008, 0x0804, 0xc37b,
+ 0x9186, 0x0027, 0x1558, 0x080c, 0x967a, 0x080c, 0x3240, 0x080c,
+ 0xd0b0, 0x0096, 0x6014, 0x2048, 0x080c, 0xc97a, 0x0150, 0xa867,
+ 0x0103, 0xa877, 0x0000, 0xa87b, 0x0029, 0x080c, 0x6dee, 0x080c,
+ 0xcb6b, 0x009e, 0x080c, 0xacb0, 0x0005, 0x9186, 0x0089, 0x0118,
+ 0x9186, 0x008a, 0x1140, 0x080c, 0xab33, 0x0128, 0x9086, 0x000c,
+ 0x0904, 0xc3b3, 0x0000, 0x080c, 0xad6a, 0x0c70, 0x9186, 0x0014,
+ 0x1d60, 0x080c, 0x967a, 0x0096, 0x6014, 0x2048, 0x080c, 0xc97a,
+ 0x0d00, 0xa867, 0x0103, 0xa877, 0x0000, 0xa87b, 0x0006, 0xa880,
+ 0xc0ec, 0xa882, 0x0890, 0x0002, 0xc38b, 0xc389, 0xc389, 0xc389,
+ 0xc389, 0xc389, 0xc39f, 0xc389, 0xc389, 0xc389, 0xc389, 0xc389,
+ 0xc389, 0x080c, 0x0d7d, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186,
+ 0x0039, 0x0118, 0x9186, 0x0035, 0x1118, 0x2001, 0x1985, 0x0010,
+ 0x2001, 0x1986, 0x2004, 0x601a, 0x6003, 0x000c, 0x0005, 0x6034,
+ 0x908c, 0xff00, 0x810f, 0x9186, 0x0039, 0x0118, 0x9186, 0x0035,
+ 0x1118, 0x2001, 0x1985, 0x0010, 0x2001, 0x1986, 0x2004, 0x601a,
+ 0x6003, 0x000e, 0x0005, 0x9182, 0x0092, 0x1220, 0x9182, 0x0085,
+ 0x0208, 0x0012, 0x0804, 0xad6a, 0xc3c9, 0xc3c9, 0xc3c9, 0xc3c9,
+ 0xc3cb, 0xc418, 0xc3c9, 0xc3c9, 0xc3c9, 0xc3c9, 0xc3c9, 0xc3c9,
+ 0xc3c9, 0x080c, 0x0d7d, 0x0096, 0x6010, 0x00b6, 0x2058, 0xb800,
+ 0x00be, 0xd0bc, 0x0168, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186,
+ 0x0039, 0x0118, 0x9186, 0x0035, 0x1118, 0x009e, 0x0804, 0xc42c,
+ 0x080c, 0xc97a, 0x1118, 0x080c, 0xcb6b, 0x0068, 0x6014, 0x2048,
+ 0x080c, 0xd0c7, 0x1110, 0x080c, 0xcb6b, 0xa867, 0x0103, 0x080c,
+ 0xd07b, 0x080c, 0x6dee, 0x00d6, 0x2c68, 0x080c, 0xac5a, 0x01d0,
+ 0x6003, 0x0001, 0x6007, 0x001e, 0x600b, 0xffff, 0x2009, 0x026e,
+ 0x210c, 0x613a, 0x2009, 0x026f, 0x210c, 0x613e, 0x6910, 0x6112,
+ 0x080c, 0xce15, 0x695c, 0x615e, 0x6023, 0x0001, 0x2009, 0x8020,
+ 0x080c, 0x92b0, 0x2d60, 0x00de, 0x080c, 0xacb0, 0x009e, 0x0005,
+ 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x05a0, 0x6034,
+ 0x908c, 0xff00, 0x810f, 0x9186, 0x0035, 0x0130, 0x9186, 0x001e,
+ 0x0118, 0x9186, 0x0039, 0x1538, 0x00d6, 0x2c68, 0x080c, 0xd013,
+ 0x11f0, 0x080c, 0xac5a, 0x01d8, 0x6106, 0x6003, 0x0001, 0x6023,
+ 0x0001, 0x6910, 0x6112, 0x692c, 0x612e, 0x6930, 0x6132, 0x6934,
+ 0x918c, 0x00ff, 0x6136, 0x6938, 0x613a, 0x693c, 0x613e, 0x695c,
+ 0x615e, 0x080c, 0xce15, 0x2009, 0x8020, 0x080c, 0x92b0, 0x2d60,
+ 0x00de, 0x0804, 0xacb0, 0x0096, 0x6014, 0x2048, 0x080c, 0xc97a,
+ 0x01c8, 0xa867, 0x0103, 0xa880, 0xd0b4, 0x0128, 0xc0ec, 0xa882,
+ 0xa87b, 0x0006, 0x0048, 0xd0bc, 0x0118, 0xa87b, 0x0002, 0x0020,
+ 0xa87b, 0x0005, 0x080c, 0xcc85, 0xa877, 0x0000, 0x080c, 0x6dee,
+ 0x080c, 0xcb6b, 0x009e, 0x0804, 0xacb0, 0x0016, 0x0096, 0x6014,
+ 0x2048, 0x080c, 0xc97a, 0x0140, 0xa867, 0x0103, 0xa87b, 0x0028,
+ 0xa877, 0x0000, 0x080c, 0x6dee, 0x009e, 0x001e, 0x9186, 0x0013,
+ 0x0158, 0x9186, 0x0014, 0x0130, 0x9186, 0x0027, 0x0118, 0x080c,
+ 0xad6a, 0x0020, 0x080c, 0x967a, 0x080c, 0xaceb, 0x0005, 0x0056,
+ 0x0066, 0x0096, 0x00a6, 0x2029, 0x0001, 0x9182, 0x0101, 0x1208,
+ 0x0010, 0x2009, 0x0100, 0x2130, 0x8304, 0x9098, 0x0018, 0x2009,
+ 0x0020, 0x2011, 0x0029, 0x080c, 0xc4f8, 0x96b2, 0x0020, 0xb004,
+ 0x904d, 0x0110, 0x080c, 0x0ff9, 0x080c, 0x1047, 0x0520, 0x8528,
+ 0xa867, 0x0110, 0xa86b, 0x0000, 0x2920, 0xb406, 0x968a, 0x003d,
+ 0x1228, 0x2608, 0x2011, 0x001b, 0x0499, 0x00a8, 0x96b2, 0x003c,
+ 0x2009, 0x003c, 0x2950, 0x2011, 0x001b, 0x0451, 0x0c28, 0x2001,
+ 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f, 0x95ad, 0x0003, 0xb566,
+ 0x95ac, 0x0000, 0x0048, 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae,
+ 0x852f, 0x95ad, 0x0003, 0xb566, 0x009e, 0x006e, 0x005e, 0x0005,
+ 0x00a6, 0x89ff, 0x0158, 0xa804, 0x9055, 0x0130, 0xa807, 0x0000,
+ 0x080c, 0x6dee, 0x2a48, 0x0cb8, 0x080c, 0x6dee, 0x00ae, 0x0005,
+ 0x00f6, 0x2079, 0x0200, 0x7814, 0x9085, 0x0080, 0x7816, 0xd184,
+ 0x0108, 0x8108, 0x810c, 0x20a9, 0x0001, 0xa860, 0x20e8, 0xa85c,
+ 0x9200, 0x20a0, 0x20e1, 0x0000, 0x2300, 0x9e00, 0x2098, 0x4003,
+ 0x8318, 0x9386, 0x0020, 0x1148, 0x2018, 0x2300, 0x9e00, 0x2098,
+ 0x7814, 0x8000, 0x9085, 0x0080, 0x7816, 0x8109, 0x1d80, 0x7817,
+ 0x0000, 0x00fe, 0x0005, 0x6920, 0x9186, 0x0003, 0x0118, 0x9186,
+ 0x0002, 0x11d0, 0x00c6, 0x00d6, 0x00e6, 0x2d60, 0x0096, 0x6014,
+ 0x2048, 0x080c, 0xc97a, 0x0150, 0x2001, 0x0006, 0xa980, 0xc1d5,
+ 0x080c, 0x7022, 0x080c, 0x6de2, 0x080c, 0xcb6b, 0x009e, 0x080c,
+ 0xaceb, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x702c, 0xd084,
+ 0x1170, 0x6008, 0x2060, 0x6020, 0x9086, 0x0002, 0x1140, 0x6104,
+ 0x9186, 0x0085, 0x0118, 0x9186, 0x008b, 0x1108, 0x9006, 0x00ce,
+ 0x0005, 0x0066, 0x0126, 0x2091, 0x8000, 0x2031, 0x0001, 0x6020,
+ 0x9084, 0x000f, 0x0083, 0x012e, 0x006e, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x0066, 0x2031, 0x0000, 0x6020, 0x9084, 0x000f, 0x001b,
+ 0x006e, 0x012e, 0x0005, 0xc5ab, 0xc5ab, 0xc5a6, 0xc5cf, 0xc583,
+ 0xc5a6, 0xc585, 0xc5a6, 0xc583, 0x9173, 0xc5a6, 0xc5a6, 0xc5a6,
+ 0xc583, 0xc583, 0xc583, 0x080c, 0x0d7d, 0x6010, 0x9080, 0x0000,
+ 0x2004, 0xd0bc, 0x190c, 0xc5cf, 0x0036, 0x6014, 0x0096, 0x2048,
+ 0xa880, 0x009e, 0xd0cc, 0x0118, 0x2019, 0x000c, 0x0038, 0xd094,
+ 0x0118, 0x2019, 0x000d, 0x0010, 0x2019, 0x0010, 0x080c, 0xdfa1,
+ 0x6023, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005, 0x9006, 0x0005,
+ 0x9085, 0x0001, 0x0005, 0x0096, 0x86ff, 0x11e8, 0x6014, 0x2048,
+ 0x080c, 0xc97a, 0x01d0, 0x6043, 0xffff, 0xa864, 0x9086, 0x0139,
+ 0x1128, 0xa87b, 0x0005, 0xa883, 0x0000, 0x0028, 0x900e, 0x2001,
+ 0x0005, 0x080c, 0x7022, 0x080c, 0xcc85, 0x080c, 0x6de2, 0x080c,
+ 0xaceb, 0x9085, 0x0001, 0x009e, 0x0005, 0x9006, 0x0ce0, 0x080c,
+ 0xa91e, 0x080c, 0xd0d5, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0d7d,
+ 0x002b, 0x0106, 0x080c, 0xa93a, 0x010e, 0x0005, 0xc5ee, 0xc61e,
+ 0xc5f0, 0xc645, 0xc619, 0xc5ee, 0xc5a6, 0xc5ab, 0xc5ab, 0xc5a6,
+ 0xc5a6, 0xc5a6, 0xc5a6, 0xc5a6, 0xc5a6, 0xc5a6, 0x080c, 0x0d7d,
+ 0x86ff, 0x1520, 0x6020, 0x9086, 0x0006, 0x0500, 0x0096, 0x6014,
+ 0x2048, 0x080c, 0xc97a, 0x0168, 0xa87c, 0xd0cc, 0x0140, 0x0096,
+ 0xc0cc, 0xa87e, 0xa878, 0x2048, 0x080c, 0x0ff9, 0x009e, 0x080c,
+ 0xcc85, 0x009e, 0x080c, 0xd055, 0x6007, 0x0085, 0x6003, 0x000b,
+ 0x6023, 0x0002, 0x2009, 0x8020, 0x080c, 0x9292, 0x9085, 0x0001,
+ 0x0005, 0x0066, 0x080c, 0x1ac5, 0x006e, 0x0890, 0x00e6, 0x2071,
+ 0x19e6, 0x7030, 0x9c06, 0x1120, 0x080c, 0xa138, 0x00ee, 0x0840,
+ 0x6020, 0x9084, 0x000f, 0x9086, 0x0006, 0x1150, 0x0086, 0x0096,
+ 0x2049, 0x0001, 0x2c40, 0x080c, 0xa28c, 0x009e, 0x008e, 0x0040,
+ 0x0066, 0x080c, 0xa034, 0x190c, 0x0d7d, 0x080c, 0xa042, 0x006e,
+ 0x00ee, 0x1904, 0xc5f0, 0x0804, 0xc5a6, 0x0036, 0x00e6, 0x2071,
+ 0x19e6, 0x704c, 0x9c06, 0x1138, 0x901e, 0x080c, 0xa1b8, 0x00ee,
+ 0x003e, 0x0804, 0xc5f0, 0x080c, 0xa3c3, 0x00ee, 0x003e, 0x1904,
+ 0xc5f0, 0x0804, 0xc5a6, 0x00c6, 0x0066, 0x6020, 0x9084, 0x000f,
+ 0x001b, 0x006e, 0x00ce, 0x0005, 0xc67b, 0xc74a, 0xc8b4, 0xc683,
+ 0xaceb, 0xc67b, 0xdf93, 0xd0bd, 0xc74a, 0x913a, 0xc940, 0xc674,
+ 0xc674, 0xc674, 0xc674, 0xc674, 0x080c, 0x0d7d, 0x080c, 0xcb91,
+ 0x1110, 0x080c, 0xb693, 0x0005, 0x080c, 0x967a, 0x0804, 0xacb0,
+ 0x601b, 0x0001, 0x0005, 0x080c, 0xc97a, 0x0130, 0x6014, 0x0096,
+ 0x2048, 0x2c00, 0xa896, 0x009e, 0x080c, 0xa91e, 0x080c, 0xd0d5,
+ 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0d7d, 0x0013, 0x0804, 0xa93a,
+ 0xc6a8, 0xc6aa, 0xc6d4, 0xc6e8, 0xc715, 0xc6a8, 0xc67b, 0xc67b,
+ 0xc67b, 0xc6ef, 0xc6ef, 0xc6a8, 0xc6a8, 0xc6a8, 0xc6a8, 0xc6f9,
+ 0x080c, 0x0d7d, 0x00e6, 0x6014, 0x0096, 0x2048, 0xa880, 0xc0b5,
+ 0xa882, 0x009e, 0x2071, 0x19e6, 0x7030, 0x9c06, 0x01d0, 0x0066,
+ 0x080c, 0xa034, 0x190c, 0x0d7d, 0x080c, 0xa042, 0x006e, 0x080c,
+ 0xd055, 0x6007, 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x2001,
+ 0x1986, 0x2004, 0x601a, 0x2009, 0x8020, 0x080c, 0x9292, 0x00ee,
+ 0x0005, 0x601b, 0x0001, 0x0cd8, 0x0096, 0x6014, 0x2048, 0xa880,
+ 0xc0b5, 0xa882, 0x009e, 0x080c, 0xd055, 0x6007, 0x0085, 0x6003,
+ 0x000b, 0x6023, 0x0002, 0x2009, 0x8020, 0x080c, 0x9292, 0x0005,
+ 0x080c, 0xa91e, 0x080c, 0xaab5, 0x080c, 0xa93a, 0x0c28, 0x0096,
+ 0x601b, 0x0001, 0x6014, 0x2048, 0xa880, 0xc0b5, 0xa882, 0x009e,
+ 0x0005, 0x080c, 0x5746, 0x01b8, 0x6014, 0x0096, 0x904d, 0x0190,
+ 0xa864, 0xa867, 0x0103, 0xa87b, 0x0006, 0x9086, 0x0139, 0x1150,
+ 0xa867, 0x0139, 0xa87b, 0x0030, 0xa897, 0x4005, 0xa89b, 0x0004,
+ 0x080c, 0x6dee, 0x009e, 0x0804, 0xacb0, 0x6014, 0x0096, 0x904d,
+ 0x0560, 0xa97c, 0xd1e4, 0x1158, 0x611c, 0xd1fc, 0x0530, 0x6110,
+ 0x00b6, 0x2158, 0xb93c, 0x8109, 0x0208, 0xb93e, 0x00be, 0x080c,
+ 0xa93a, 0x2001, 0x180f, 0x2004, 0xd0c4, 0x0110, 0x009e, 0x0005,
+ 0xa884, 0x009e, 0x8003, 0x800b, 0x810b, 0x9108, 0x611a, 0x2001,
+ 0x0037, 0x2c08, 0x080c, 0x16a0, 0x6000, 0x9086, 0x0004, 0x1120,
+ 0x2009, 0x0048, 0x080c, 0xad4d, 0x0005, 0x009e, 0x080c, 0x1ac5,
+ 0x0804, 0xc6d4, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0d7d, 0x000b,
+ 0x0005, 0xc761, 0xc680, 0xc763, 0xc761, 0xc763, 0xc763, 0xc67c,
+ 0xc761, 0xc676, 0xc676, 0xc761, 0xc761, 0xc761, 0xc761, 0xc761,
+ 0xc761, 0x080c, 0x0d7d, 0x6010, 0x00b6, 0x2058, 0xb804, 0x9084,
+ 0x00ff, 0x00be, 0x908a, 0x000c, 0x1a0c, 0x0d7d, 0x00b6, 0x0013,
+ 0x00be, 0x0005, 0xc77e, 0xc84b, 0xc780, 0xc7c0, 0xc780, 0xc7c0,
+ 0xc780, 0xc78e, 0xc77e, 0xc7c0, 0xc77e, 0xc7af, 0x080c, 0x0d7d,
+ 0x6004, 0x908e, 0x0016, 0x05c0, 0x908e, 0x0004, 0x05a8, 0x908e,
+ 0x0002, 0x0590, 0x908e, 0x0052, 0x0904, 0xc847, 0x6004, 0x080c,
+ 0xcb91, 0x0904, 0xc864, 0x908e, 0x0004, 0x1110, 0x080c, 0x326f,
+ 0x908e, 0x0021, 0x0904, 0xc868, 0x908e, 0x0022, 0x0904, 0xc8af,
+ 0x908e, 0x003d, 0x0904, 0xc868, 0x908e, 0x0039, 0x0904, 0xc86c,
+ 0x908e, 0x0035, 0x0904, 0xc86c, 0x908e, 0x001e, 0x0178, 0x908e,
+ 0x0001, 0x1140, 0x6010, 0x2058, 0xb804, 0x9084, 0x00ff, 0x9086,
+ 0x0006, 0x0110, 0x080c, 0x3240, 0x080c, 0xb693, 0x0804, 0xaceb,
+ 0x00c6, 0x00d6, 0x6104, 0x9186, 0x0016, 0x0904, 0xc838, 0x9186,
+ 0x0002, 0x1904, 0xc80d, 0x2001, 0x1837, 0x2004, 0xd08c, 0x11c8,
+ 0x080c, 0x753d, 0x11b0, 0x080c, 0xd09b, 0x0138, 0x080c, 0x7560,
+ 0x1120, 0x080c, 0x7448, 0x0804, 0xc898, 0x2001, 0x197c, 0x2003,
+ 0x0001, 0x2001, 0x1800, 0x2003, 0x0001, 0x080c, 0x746e, 0x0804,
+ 0xc898, 0x6010, 0x2058, 0x2001, 0x1837, 0x2004, 0xd0ac, 0x1904,
+ 0xc898, 0xb8a0, 0x9084, 0xff80, 0x1904, 0xc898, 0xb840, 0x9084,
+ 0x00ff, 0x9005, 0x0190, 0x8001, 0xb842, 0x6017, 0x0000, 0x6023,
+ 0x0007, 0x601b, 0x0398, 0x604b, 0x0000, 0x080c, 0xac5a, 0x0128,
+ 0x2b00, 0x6012, 0x6023, 0x0001, 0x0458, 0x00de, 0x00ce, 0x6004,
+ 0x908e, 0x0002, 0x11a0, 0x6010, 0x2058, 0xb8a0, 0x9086, 0x007e,
+ 0x1170, 0x2009, 0x1837, 0x2104, 0xc085, 0x200a, 0x00e6, 0x2071,
+ 0x1800, 0x080c, 0x6025, 0x00ee, 0x080c, 0xb693, 0x0030, 0x080c,
+ 0xb693, 0x080c, 0x3240, 0x080c, 0xd0b0, 0x00e6, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x326f, 0x012e, 0x00ee, 0x080c, 0xaceb, 0x0005,
+ 0x2001, 0x0002, 0x080c, 0x65e3, 0x6003, 0x0001, 0x6007, 0x0002,
+ 0x080c, 0x92b7, 0x080c, 0x9738, 0x00de, 0x00ce, 0x0c80, 0x080c,
+ 0x326f, 0x0804, 0xc7bc, 0x00c6, 0x00d6, 0x6104, 0x9186, 0x0016,
+ 0x0d38, 0x6010, 0x2058, 0xb840, 0x9084, 0x00ff, 0x9005, 0x0904,
+ 0xc80d, 0x8001, 0xb842, 0x6003, 0x0001, 0x080c, 0x92b7, 0x080c,
+ 0x9738, 0x00de, 0x00ce, 0x0898, 0x080c, 0xb693, 0x0804, 0xc7be,
+ 0x080c, 0xb6cf, 0x0804, 0xc7be, 0x00d6, 0x2c68, 0x6104, 0x080c,
+ 0xd013, 0x00de, 0x0118, 0x080c, 0xacb0, 0x0408, 0x6004, 0x8007,
+ 0x6134, 0x918c, 0x00ff, 0x9105, 0x6036, 0x6007, 0x0085, 0x6003,
+ 0x000b, 0x6023, 0x0002, 0x603c, 0x600a, 0x2001, 0x1986, 0x2004,
+ 0x601a, 0x602c, 0x2c08, 0x2060, 0x6024, 0xd0b4, 0x0108, 0xc085,
+ 0xc0b5, 0x6026, 0x2160, 0x2009, 0x8020, 0x080c, 0x92b0, 0x0005,
+ 0x00de, 0x00ce, 0x080c, 0xb693, 0x080c, 0x3240, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x326f, 0x6017, 0x0000, 0x6023, 0x0007,
+ 0x601b, 0x0398, 0x604b, 0x0000, 0x012e, 0x00ee, 0x0005, 0x080c,
+ 0xb0eb, 0x1904, 0xc864, 0x0005, 0x6000, 0x908a, 0x0016, 0x1a0c,
+ 0x0d7d, 0x0096, 0x00d6, 0x001b, 0x00de, 0x009e, 0x0005, 0xc8cf,
+ 0xc8cf, 0xc8cf, 0xc8cf, 0xc8cf, 0xc8cf, 0xc8cf, 0xc8cf, 0xc8cf,
+ 0xc67b, 0xc8cf, 0xc680, 0xc8d1, 0xc680, 0xc8eb, 0xc8cf, 0x080c,
+ 0x0d7d, 0x6004, 0x9086, 0x008b, 0x01b0, 0x6034, 0x908c, 0xff00,
+ 0x810f, 0x9186, 0x0035, 0x1130, 0x602c, 0x9080, 0x0009, 0x200c,
+ 0xc185, 0x2102, 0x6007, 0x008b, 0x6003, 0x000d, 0x2009, 0x8020,
+ 0x080c, 0x92b0, 0x0005, 0x080c, 0xd08f, 0x0118, 0x080c, 0xd0a2,
+ 0x0010, 0x080c, 0xd0b0, 0x080c, 0xcb6b, 0x080c, 0xc97a, 0x0570,
+ 0x080c, 0x3240, 0x080c, 0xc97a, 0x0168, 0x6014, 0x2048, 0xa867,
+ 0x0103, 0xa87b, 0x0006, 0xa877, 0x0000, 0xa880, 0xc0ed, 0xa882,
+ 0x080c, 0x6dee, 0x2c68, 0x080c, 0xac5a, 0x0150, 0x6810, 0x6012,
+ 0x080c, 0xce15, 0x00c6, 0x2d60, 0x080c, 0xaceb, 0x00ce, 0x0008,
+ 0x2d60, 0x6017, 0x0000, 0x6023, 0x0001, 0x6007, 0x0001, 0x6003,
+ 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x00c8, 0x080c, 0xd08f,
+ 0x0138, 0x6034, 0x9086, 0x4000, 0x1118, 0x080c, 0x3240, 0x08d0,
+ 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039, 0x0118, 0x9186,
+ 0x0035, 0x1118, 0x080c, 0x3240, 0x0868, 0x080c, 0xaceb, 0x0005,
+ 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0d7d, 0x0002, 0xc956, 0xc956,
+ 0xc958, 0xc958, 0xc958, 0xc956, 0xc956, 0xaceb, 0xc956, 0xc956,
+ 0xc956, 0xc956, 0xc956, 0xc956, 0xc956, 0xc956, 0x080c, 0x0d7d,
+ 0x080c, 0xa91e, 0x080c, 0xaab5, 0x080c, 0xa93a, 0x6114, 0x0096,
+ 0x2148, 0xa87b, 0x0006, 0x080c, 0x6dee, 0x009e, 0x0804, 0xacb0,
+ 0x9284, 0x0003, 0x1158, 0x9282, 0x1ddc, 0x0240, 0x2001, 0x181a,
+ 0x2004, 0x9202, 0x1218, 0x9085, 0x0001, 0x0005, 0x9006, 0x0ce8,
+ 0x0096, 0x0028, 0x0096, 0x0006, 0x6014, 0x2048, 0x000e, 0x0006,
+ 0x9984, 0xf000, 0x9086, 0xf000, 0x0110, 0x080c, 0x10f2, 0x000e,
+ 0x009e, 0x0005, 0x00e6, 0x00c6, 0x0036, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2061, 0x1ddc, 0x2071, 0x1800, 0x7354, 0x7074, 0x9302,
+ 0x1640, 0x6020, 0x9206, 0x11f8, 0x080c, 0xd09b, 0x0180, 0x9286,
+ 0x0001, 0x1168, 0x6004, 0x9086, 0x0004, 0x1148, 0x080c, 0x3240,
+ 0x080c, 0xd0b0, 0x00c6, 0x080c, 0xaceb, 0x00ce, 0x0060, 0x080c,
+ 0xcd87, 0x0148, 0x080c, 0xcb91, 0x1110, 0x080c, 0xb693, 0x00c6,
+ 0x080c, 0xacb0, 0x00ce, 0x9ce0, 0x001c, 0x7068, 0x9c02, 0x1208,
+ 0x08a0, 0x012e, 0x000e, 0x003e, 0x00ce, 0x00ee, 0x0005, 0x00e6,
+ 0x00c6, 0x0016, 0x9188, 0x1000, 0x210c, 0x81ff, 0x0128, 0x2061,
+ 0x1b34, 0x6112, 0x080c, 0x3240, 0x9006, 0x0010, 0x9085, 0x0001,
+ 0x001e, 0x00ce, 0x00ee, 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xac5a, 0x01b0, 0x665e, 0x2b00, 0x6012, 0x080c, 0x5746,
+ 0x0118, 0x080c, 0xcaad, 0x0168, 0x080c, 0xce15, 0x6023, 0x0003,
+ 0x2009, 0x004b, 0x080c, 0xad4d, 0x9085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0x9006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0xbaa0,
+ 0x080c, 0xad20, 0x0580, 0x605f, 0x0000, 0x2b00, 0x6012, 0x080c,
+ 0xce15, 0x6023, 0x0003, 0x0016, 0x080c, 0xa91e, 0x080c, 0x943d,
+ 0x0076, 0x903e, 0x080c, 0x9306, 0x2c08, 0x080c, 0xe167, 0x007e,
+ 0x080c, 0xa93a, 0x001e, 0xd184, 0x0128, 0x080c, 0xacb0, 0x9085,
+ 0x0001, 0x0070, 0x080c, 0x5746, 0x0128, 0xd18c, 0x1170, 0x080c,
+ 0xcaad, 0x0148, 0x2009, 0x004c, 0x080c, 0xad4d, 0x9085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x2900, 0x6016, 0x0c90,
+ 0x2009, 0x004d, 0x0010, 0x2009, 0x004e, 0x00f6, 0x00c6, 0x0046,
+ 0x0016, 0x080c, 0xac5a, 0x2c78, 0x05a0, 0x7e5e, 0x2b00, 0x7812,
+ 0x7823, 0x0003, 0x0016, 0x2021, 0x0005, 0x080c, 0xcabf, 0x001e,
+ 0x9186, 0x004d, 0x0118, 0x9186, 0x004e, 0x0148, 0x2001, 0x197f,
+ 0x200c, 0xd1fc, 0x0168, 0x2f60, 0x080c, 0xacb0, 0x00d0, 0x2001,
+ 0x197e, 0x200c, 0xd1fc, 0x0120, 0x2f60, 0x080c, 0xacb0, 0x0088,
+ 0x2f60, 0x080c, 0x5746, 0x0138, 0xd18c, 0x1118, 0x04f1, 0x0148,
+ 0x0010, 0x2900, 0x7816, 0x001e, 0x0016, 0x080c, 0xad4d, 0x9085,
+ 0x0001, 0x001e, 0x004e, 0x00ce, 0x00fe, 0x0005, 0x00f6, 0x00c6,
+ 0x0046, 0x080c, 0xac5a, 0x2c78, 0x0508, 0x7e5e, 0x2b00, 0x7812,
+ 0x7823, 0x0003, 0x0096, 0x2021, 0x0004, 0x0489, 0x009e, 0x2001,
+ 0x197d, 0x200c, 0xd1fc, 0x0120, 0x2f60, 0x080c, 0xacb0, 0x0060,
+ 0x2f60, 0x080c, 0x5746, 0x0120, 0xd18c, 0x1160, 0x0071, 0x0130,
+ 0x2009, 0x0052, 0x080c, 0xad4d, 0x9085, 0x0001, 0x004e, 0x00ce,
+ 0x00fe, 0x0005, 0x2900, 0x7816, 0x0c98, 0x00c6, 0x080c, 0x4af2,
+ 0x00ce, 0x1120, 0x080c, 0xacb0, 0x9006, 0x0005, 0xa867, 0x0000,
+ 0xa86b, 0x8000, 0x2900, 0x6016, 0x9085, 0x0001, 0x0005, 0x0096,
+ 0x0076, 0x0126, 0x2091, 0x8000, 0x080c, 0xa91e, 0x080c, 0x6875,
+ 0x0158, 0x2001, 0xcac6, 0x0006, 0x900e, 0x2400, 0x080c, 0x7022,
+ 0x080c, 0x6dee, 0x000e, 0x0807, 0x2418, 0x080c, 0x9640, 0xbaa0,
+ 0x0086, 0x2041, 0x0001, 0x2039, 0x0001, 0x2608, 0x080c, 0x9457,
+ 0x008e, 0x080c, 0x9306, 0x2f08, 0x2648, 0x080c, 0xe167, 0xb93c,
+ 0x81ff, 0x090c, 0x9530, 0x080c, 0xa93a, 0x012e, 0x007e, 0x009e,
+ 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xac5a, 0x0190,
+ 0x660a, 0x2b08, 0x6112, 0x080c, 0xce15, 0x6023, 0x0001, 0x2900,
+ 0x6016, 0x2009, 0x001f, 0x080c, 0xad4d, 0x9085, 0x0001, 0x012e,
+ 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xad20, 0x01b8, 0x660a, 0x2b08, 0x6112, 0x080c, 0xce15,
+ 0x6023, 0x0008, 0x2900, 0x6016, 0x00f6, 0x2c78, 0x080c, 0x1778,
+ 0x00fe, 0x2009, 0x0021, 0x080c, 0xad4d, 0x9085, 0x0001, 0x012e,
+ 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x2009, 0x003d, 0x00c6, 0x0126,
+ 0x0016, 0x2091, 0x8000, 0x080c, 0xac5a, 0x0198, 0x660a, 0x2b08,
+ 0x6112, 0x080c, 0xce15, 0x6023, 0x0001, 0x2900, 0x6016, 0x001e,
+ 0x0016, 0x080c, 0xad4d, 0x9085, 0x0001, 0x001e, 0x012e, 0x00ce,
+ 0x0005, 0x9006, 0x0cd0, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0xad20, 0x0188, 0x2b08, 0x6112, 0x080c, 0xce15, 0x6023, 0x0001,
+ 0x2900, 0x6016, 0x2009, 0x0000, 0x080c, 0xad4d, 0x9085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x2009, 0x0044, 0x0830,
+ 0x2009, 0x0049, 0x0818, 0x0026, 0x00b6, 0x6210, 0x2258, 0xba3c,
+ 0x82ff, 0x0118, 0x8211, 0xba3e, 0x1140, 0xb8d0, 0x9005, 0x0128,
+ 0xb888, 0x9005, 0x1110, 0xb88b, 0x0001, 0x00be, 0x002e, 0x0005,
+ 0x0006, 0x0016, 0x6004, 0x908e, 0x0002, 0x0140, 0x908e, 0x0003,
+ 0x0128, 0x908e, 0x0004, 0x0110, 0x9085, 0x0001, 0x001e, 0x000e,
+ 0x0005, 0x0006, 0x0086, 0x0096, 0x6020, 0x9086, 0x0004, 0x01a8,
+ 0x6014, 0x904d, 0x080c, 0xc97a, 0x0180, 0xa864, 0x9086, 0x0139,
+ 0x0170, 0x6020, 0x90c6, 0x0003, 0x0140, 0x90c6, 0x0002, 0x0128,
+ 0xa868, 0xd0fc, 0x0110, 0x9006, 0x0010, 0x9085, 0x0001, 0x009e,
+ 0x008e, 0x000e, 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0xad20, 0x0198, 0x2b08, 0x6112, 0x080c, 0xce15, 0x6023, 0x0001,
+ 0x2900, 0x6016, 0x080c, 0x3240, 0x2009, 0x0028, 0x080c, 0xad4d,
+ 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x9186,
+ 0x0015, 0x11a8, 0x2011, 0x1824, 0x2204, 0x9086, 0x0074, 0x1178,
+ 0x00b6, 0x080c, 0xb8e7, 0x00be, 0x080c, 0xbb0a, 0x6003, 0x0001,
+ 0x6007, 0x0029, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0078, 0x6014,
+ 0x0096, 0x2048, 0xa868, 0x009e, 0xd0fc, 0x0148, 0x2001, 0x0001,
+ 0x080c, 0xcfd4, 0x080c, 0xb693, 0x080c, 0xacb0, 0x0005, 0x0096,
+ 0x6014, 0x904d, 0x090c, 0x0d7d, 0xa87b, 0x0030, 0xa883, 0x0000,
+ 0xa897, 0x4005, 0xa89b, 0x0004, 0xa867, 0x0139, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6dee, 0x012e, 0x009e, 0x080c, 0xacb0, 0x0c30,
+ 0x0096, 0x9186, 0x0016, 0x1128, 0x2001, 0x0004, 0x080c, 0x65e3,
+ 0x00e8, 0x9186, 0x0015, 0x1510, 0x2011, 0x1824, 0x2204, 0x9086,
+ 0x0014, 0x11e0, 0x6010, 0x00b6, 0x2058, 0x080c, 0x672e, 0x00be,
+ 0x080c, 0xbbdb, 0x1198, 0x6010, 0x00b6, 0x2058, 0xb890, 0x00be,
+ 0x9005, 0x0160, 0x2001, 0x0006, 0x080c, 0x65e3, 0x6014, 0x2048,
+ 0xa868, 0xd0fc, 0x0170, 0x080c, 0xb0bf, 0x0048, 0x6014, 0x2048,
+ 0xa868, 0xd0fc, 0x0528, 0x080c, 0xb693, 0x080c, 0xacb0, 0x009e,
+ 0x0005, 0x6014, 0x6310, 0x2358, 0x904d, 0x090c, 0x0d7d, 0xa87b,
+ 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0x900e, 0x080c, 0x6986,
+ 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0xa99a, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x6dee, 0x012e, 0x080c, 0xacb0, 0x08f8,
+ 0x6014, 0x904d, 0x090c, 0x0d7d, 0xa87b, 0x0030, 0xa883, 0x0000,
+ 0xa897, 0x4005, 0xa89b, 0x0004, 0xa867, 0x0139, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6dee, 0x012e, 0x080c, 0xacb0, 0x0840, 0xa878,
+ 0x9086, 0x0005, 0x1108, 0x0009, 0x0005, 0xa880, 0xc0ad, 0xa882,
+ 0x0005, 0x604b, 0x0000, 0x6017, 0x0000, 0x6003, 0x0001, 0x6007,
+ 0x0050, 0x2009, 0x8023, 0x080c, 0x92b0, 0x0005, 0x00c6, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0130, 0x0066, 0x6020,
+ 0x9084, 0x000f, 0x001b, 0x006e, 0x00ce, 0x0005, 0xc67b, 0xccb8,
+ 0xccb8, 0xccbb, 0xe494, 0xe4af, 0xe4b2, 0xc67b, 0xc67b, 0xc67b,
+ 0xc67b, 0xc67b, 0xc67b, 0xc67b, 0xc67b, 0xc67b, 0x080c, 0x0d7d,
+ 0xa001, 0xa001, 0x0005, 0x0096, 0x6014, 0x904d, 0x0118, 0xa87c,
+ 0xd0e4, 0x1110, 0x009e, 0x0010, 0x009e, 0x0005, 0x6010, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0550, 0x2001, 0x1834, 0x2004,
+ 0x9005, 0x1540, 0x00f6, 0x2c78, 0x080c, 0xac5a, 0x0508, 0x7810,
+ 0x6012, 0x080c, 0xce15, 0x7820, 0x9086, 0x0003, 0x0128, 0x7808,
+ 0x603a, 0x2f00, 0x603e, 0x0020, 0x7808, 0x603e, 0x2f00, 0x603a,
+ 0x602e, 0x6023, 0x0001, 0x6007, 0x0035, 0x6003, 0x0001, 0x795c,
+ 0x615e, 0x2009, 0x8020, 0x080c, 0x92b0, 0x2f60, 0x00fe, 0x0005,
+ 0x2f60, 0x00fe, 0x2001, 0x1987, 0x2004, 0x604a, 0x0005, 0x0016,
+ 0x0096, 0x6814, 0x2048, 0x681c, 0xd0fc, 0xc0fc, 0x681e, 0xa87c,
+ 0x1108, 0xd0e4, 0x0180, 0xc0e4, 0xa87e, 0xa877, 0x0000, 0xa893,
+ 0x0000, 0xa88f, 0x0000, 0xd0cc, 0x0130, 0xc0cc, 0xa87e, 0xa878,
+ 0x2048, 0x080c, 0x0ff9, 0x6830, 0x6036, 0x908e, 0x0001, 0x0148,
+ 0x6803, 0x0002, 0x9086, 0x0005, 0x0170, 0x9006, 0x602e, 0x6032,
+ 0x00d0, 0x681c, 0xc085, 0x681e, 0x6803, 0x0004, 0x6824, 0xc0f4,
+ 0x9085, 0x0c00, 0x6826, 0x6814, 0x2048, 0xa8ac, 0x6938, 0x9102,
+ 0xa8b0, 0x693c, 0x9103, 0x1e48, 0x683c, 0x602e, 0x6838, 0x9084,
+ 0xfffc, 0x683a, 0x6032, 0x2d00, 0x603a, 0x6808, 0x603e, 0x6910,
+ 0x6112, 0x695c, 0x615e, 0x6023, 0x0001, 0x6007, 0x0039, 0x6003,
+ 0x0001, 0x2009, 0x8020, 0x080c, 0x92b0, 0x009e, 0x001e, 0x0005,
+ 0x6024, 0xd0d4, 0x0510, 0xd0f4, 0x11f8, 0x6038, 0x940a, 0x603c,
+ 0x9303, 0x0230, 0x9105, 0x0120, 0x6024, 0xc0d4, 0xc0f5, 0x0098,
+ 0x643a, 0x633e, 0xac3e, 0xab42, 0x0046, 0x0036, 0x2400, 0xacac,
+ 0x9402, 0xa836, 0x2300, 0xabb0, 0x9303, 0xa83a, 0x003e, 0x004e,
+ 0x6024, 0xc0d4, 0x0000, 0x6026, 0x0005, 0xd0f4, 0x1138, 0xa83c,
+ 0x603a, 0xa840, 0x603e, 0x6024, 0xc0f5, 0x6026, 0x0005, 0x0006,
+ 0x0016, 0x6004, 0x908e, 0x0034, 0x01b8, 0x908e, 0x0035, 0x01a0,
+ 0x908e, 0x0036, 0x0188, 0x908e, 0x0037, 0x0170, 0x908e, 0x0038,
+ 0x0158, 0x908e, 0x0039, 0x0140, 0x908e, 0x003a, 0x0128, 0x908e,
+ 0x003b, 0x0110, 0x9085, 0x0001, 0x001e, 0x000e, 0x0005, 0x0006,
+ 0x0016, 0x0026, 0x0036, 0x00e6, 0x2001, 0x1981, 0x200c, 0x8000,
+ 0x2014, 0x2001, 0x0032, 0x080c, 0x91f8, 0x2001, 0x1985, 0x82ff,
+ 0x1110, 0x2011, 0x0014, 0x2202, 0x2001, 0x1983, 0x200c, 0x8000,
+ 0x2014, 0x2071, 0x196b, 0x711a, 0x721e, 0x2001, 0x0064, 0x080c,
+ 0x91f8, 0x2001, 0x1986, 0x82ff, 0x1110, 0x2011, 0x0014, 0x2202,
+ 0x2001, 0x1987, 0x9288, 0x000a, 0x2102, 0x2001, 0x0017, 0x080c,
+ 0xa90f, 0x2001, 0x1a8b, 0x2102, 0x2001, 0x0032, 0x080c, 0x16a0,
+ 0x080c, 0x6abe, 0x00ee, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005,
+ 0x0006, 0x0016, 0x00e6, 0x2001, 0x1985, 0x2003, 0x0028, 0x2001,
+ 0x1986, 0x2003, 0x0014, 0x2071, 0x196b, 0x701b, 0x0000, 0x701f,
+ 0x07d0, 0x2001, 0x1987, 0x2009, 0x001e, 0x2102, 0x2001, 0x0017,
+ 0x080c, 0xa90f, 0x2001, 0x1a8b, 0x2102, 0x2001, 0x0032, 0x080c,
+ 0x16a0, 0x00ee, 0x001e, 0x000e, 0x0005, 0x0096, 0x6060, 0x904d,
+ 0x0110, 0x080c, 0x1079, 0x009e, 0x0005, 0x0005, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0xac5a, 0x0180, 0x2b08, 0x6112, 0x0ca9,
+ 0x6023, 0x0001, 0x2900, 0x6016, 0x2009, 0x0033, 0x080c, 0xad4d,
+ 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x0096,
+ 0x00e6, 0x00f6, 0x2071, 0x1800, 0x9186, 0x0015, 0x1500, 0x7090,
+ 0x9086, 0x0018, 0x11e0, 0x6014, 0x2048, 0xaa3c, 0xd2e4, 0x1160,
+ 0x2c78, 0x080c, 0x97f7, 0x01d8, 0x707c, 0xaa50, 0x9206, 0x1160,
+ 0x7080, 0xaa54, 0x9206, 0x1140, 0x6210, 0x00b6, 0x2258, 0xbaa0,
+ 0x00be, 0x900e, 0x080c, 0x328f, 0x080c, 0xb0bf, 0x0020, 0x080c,
+ 0xb693, 0x080c, 0xacb0, 0x00fe, 0x00ee, 0x009e, 0x0005, 0x7060,
+ 0xaa54, 0x9206, 0x0d48, 0x0c80, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xac5a, 0x0188, 0x2b08, 0x6112, 0x080c, 0xce15, 0x6023,
+ 0x0001, 0x2900, 0x6016, 0x2009, 0x004d, 0x080c, 0xad4d, 0x9085,
+ 0x0001, 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x0016, 0x080c, 0xac5a, 0x0180, 0x2b08, 0x6112,
+ 0x080c, 0xce15, 0x6023, 0x0001, 0x2900, 0x6016, 0x001e, 0x080c,
+ 0xad4d, 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005, 0x001e, 0x9006,
+ 0x0cd0, 0x0016, 0x0026, 0x0036, 0x0046, 0x0056, 0x0066, 0x0096,
+ 0x00e6, 0x00f6, 0x2071, 0x1800, 0x9186, 0x0015, 0x1568, 0x7190,
+ 0x6014, 0x2048, 0xa814, 0x8003, 0x9106, 0x1530, 0x20e1, 0x0000,
+ 0x2001, 0x199f, 0x2003, 0x0000, 0x6014, 0x2048, 0xa830, 0x20a8,
+ 0x8906, 0x8006, 0x8007, 0x9094, 0x003f, 0x22e8, 0x9084, 0xffc0,
+ 0x9080, 0x001b, 0x20a0, 0x2001, 0x199f, 0x0016, 0x200c, 0x080c,
+ 0xd6df, 0x001e, 0xa804, 0x9005, 0x0110, 0x2048, 0x0c38, 0x6014,
+ 0x2048, 0xa867, 0x0103, 0x0010, 0x080c, 0xb693, 0x080c, 0xacb0,
+ 0x00fe, 0x00ee, 0x009e, 0x006e, 0x005e, 0x004e, 0x003e, 0x002e,
+ 0x001e, 0x0005, 0x0096, 0x00e6, 0x00f6, 0x2071, 0x1800, 0x9186,
+ 0x0015, 0x11b8, 0x7090, 0x9086, 0x0004, 0x1198, 0x6014, 0x2048,
+ 0x2c78, 0x080c, 0x97f7, 0x01a8, 0x707c, 0xaa74, 0x9206, 0x1130,
+ 0x7080, 0xaa78, 0x9206, 0x1110, 0x080c, 0x3240, 0x080c, 0xb0bf,
+ 0x0020, 0x080c, 0xb693, 0x080c, 0xacb0, 0x00fe, 0x00ee, 0x009e,
+ 0x0005, 0x7060, 0xaa78, 0x9206, 0x0d78, 0x0c80, 0x0096, 0x00e6,
+ 0x00f6, 0x2071, 0x1800, 0x9186, 0x0015, 0x1550, 0x7090, 0x9086,
+ 0x0004, 0x1530, 0x6014, 0x2048, 0x2c78, 0x080c, 0x97f7, 0x05f0,
+ 0x707c, 0xaacc, 0x9206, 0x1180, 0x7080, 0xaad0, 0x9206, 0x1160,
+ 0x080c, 0x3240, 0x0016, 0xa998, 0xaab0, 0x9284, 0x1000, 0xc0fd,
+ 0x080c, 0x56e7, 0x001e, 0x0010, 0x080c, 0x54ca, 0x080c, 0xc97a,
+ 0x0508, 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0x0080,
+ 0x080c, 0xc97a, 0x01b8, 0x6014, 0x2048, 0x080c, 0x54ca, 0x1d70,
+ 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4005, 0xa89b, 0x0004,
+ 0x0126, 0x2091, 0x8000, 0xa867, 0x0139, 0x080c, 0x6dee, 0x012e,
+ 0x080c, 0xacb0, 0x00fe, 0x00ee, 0x009e, 0x0005, 0x7060, 0xaad0,
+ 0x9206, 0x0930, 0x0888, 0x0016, 0x0026, 0xa87c, 0xd0ac, 0x0178,
+ 0xa938, 0xaa34, 0x2100, 0x9205, 0x0150, 0xa890, 0x9106, 0x1118,
+ 0xa88c, 0x9206, 0x0120, 0xa992, 0xaa8e, 0x9085, 0x0001, 0x002e,
+ 0x001e, 0x0005, 0x00b6, 0x00d6, 0x0036, 0x080c, 0xc97a, 0x0904,
+ 0xcfd0, 0x0096, 0x6314, 0x2348, 0xa87a, 0xa982, 0x929e, 0x4000,
+ 0x1580, 0x6310, 0x00c6, 0x2358, 0x2009, 0x0000, 0xa868, 0xd0f4,
+ 0x1140, 0x080c, 0x6986, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108,
+ 0xc18d, 0xaa96, 0xa99a, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0031, 0x20a0, 0xb8c4, 0x20e0, 0xb8c8, 0x9080, 0x0006,
+ 0x2098, 0x080c, 0x0fc4, 0x20a9, 0x0004, 0xa85c, 0x9080, 0x0035,
+ 0x20a0, 0xb8c8, 0x9080, 0x000a, 0x2098, 0x080c, 0x0fc4, 0x00ce,
+ 0x0090, 0xaa96, 0x3918, 0x9398, 0x0007, 0x231c, 0x6004, 0x9086,
+ 0x0016, 0x0110, 0xa89b, 0x0004, 0xaba2, 0x6310, 0x2358, 0xb804,
+ 0x9084, 0x00ff, 0xa89e, 0x080c, 0x6de2, 0x6017, 0x0000, 0x009e,
+ 0x003e, 0x00de, 0x00be, 0x0005, 0x0026, 0x0036, 0x0046, 0x00b6,
+ 0x0096, 0x00f6, 0x6214, 0x2248, 0x6210, 0x2258, 0x2079, 0x0260,
+ 0x9096, 0x0000, 0x11a0, 0xb814, 0x9084, 0x00ff, 0x900e, 0x080c,
+ 0x2661, 0x2118, 0x831f, 0x939c, 0xff00, 0x7838, 0x9084, 0x00ff,
+ 0x931d, 0x7c3c, 0x2011, 0x8018, 0x080c, 0x4b52, 0x00a8, 0x9096,
+ 0x0001, 0x1148, 0x89ff, 0x0180, 0xa89b, 0x000d, 0x7838, 0xa8a6,
+ 0x783c, 0xa8aa, 0x0048, 0x9096, 0x0002, 0x1130, 0xa89b, 0x000d,
+ 0x7838, 0xa8a6, 0x783c, 0xa8aa, 0x00fe, 0x009e, 0x00be, 0x004e,
+ 0x003e, 0x002e, 0x0005, 0x00c6, 0x0026, 0x0016, 0x9186, 0x0035,
+ 0x0110, 0x6a38, 0x0008, 0x6a2c, 0x080c, 0xc968, 0x01f0, 0x2260,
+ 0x6120, 0x9186, 0x0003, 0x0118, 0x9186, 0x0006, 0x1190, 0x6838,
+ 0x9206, 0x0140, 0x683c, 0x9206, 0x1160, 0x6108, 0x6838, 0x9106,
+ 0x1140, 0x0020, 0x6008, 0x693c, 0x9106, 0x1118, 0x6010, 0x6910,
+ 0x9106, 0x001e, 0x002e, 0x00ce, 0x0005, 0x9085, 0x0001, 0x0cc8,
+ 0xa974, 0xd1cc, 0x0188, 0x918c, 0x00ff, 0x918e, 0x0002, 0x1160,
+ 0xa9a8, 0x918c, 0x0f00, 0x810f, 0x918e, 0x0001, 0x1128, 0xa834,
+ 0xa938, 0x9115, 0x190c, 0xbf96, 0x0005, 0x0036, 0x2019, 0x0001,
+ 0x0010, 0x0036, 0x901e, 0x0499, 0x01e0, 0x080c, 0xc97a, 0x01c8,
+ 0x080c, 0xcb6b, 0x6037, 0x4000, 0x6014, 0x6017, 0x0000, 0x0096,
+ 0x2048, 0xa87c, 0x080c, 0xcb91, 0x1118, 0x080c, 0xb693, 0x0040,
+ 0xa867, 0x0103, 0xa877, 0x0000, 0x83ff, 0x1129, 0x080c, 0x6dee,
+ 0x009e, 0x003e, 0x0005, 0xa880, 0xd0b4, 0x0128, 0xa87b, 0x0006,
+ 0xc0ec, 0xa882, 0x0048, 0xd0bc, 0x0118, 0xa87b, 0x0002, 0x0020,
+ 0xa87b, 0x0005, 0x080c, 0xcc85, 0xa877, 0x0000, 0x0005, 0x2001,
+ 0x1810, 0x2004, 0xd0ec, 0x0005, 0x0006, 0x2001, 0x1810, 0x2004,
+ 0xd0f4, 0x000e, 0x0005, 0x0006, 0x2001, 0x1810, 0x2004, 0xd0e4,
+ 0x000e, 0x0005, 0x0036, 0x0046, 0x6010, 0x00b6, 0x2058, 0xbba0,
+ 0x00be, 0x2021, 0x0007, 0x080c, 0x4d09, 0x004e, 0x003e, 0x0005,
+ 0x0c51, 0x1d81, 0x0005, 0x2001, 0x1985, 0x2004, 0x601a, 0x0005,
+ 0x2001, 0x1987, 0x2004, 0x604a, 0x0005, 0x080c, 0xacb0, 0x0804,
+ 0x9738, 0x611c, 0xd1fc, 0xa97c, 0x1108, 0xd1e4, 0x0005, 0x601c,
+ 0xd0fc, 0xa87c, 0x1108, 0xd0e4, 0x0005, 0x601c, 0xd0fc, 0xc0fc,
+ 0x601e, 0xa87c, 0x1108, 0xd0e4, 0x0005, 0x6044, 0xd0fc, 0x1138,
+ 0xd0bc, 0x0198, 0xc0bc, 0x6046, 0x6003, 0x0002, 0x0070, 0xd0ac,
+ 0x1160, 0xd0dc, 0x1128, 0x908c, 0x000f, 0x9186, 0x0005, 0x1118,
+ 0x6003, 0x0003, 0x0010, 0x6003, 0x0001, 0x0005, 0x00b6, 0x0066,
+ 0x6000, 0x90b2, 0x0016, 0x1a0c, 0x0d7d, 0x001b, 0x006e, 0x00be,
+ 0x0005, 0xd109, 0xd83a, 0xd98b, 0xd109, 0xd109, 0xd109, 0xd109,
+ 0xd109, 0xd140, 0xda0f, 0xd109, 0xd109, 0xd109, 0xd109, 0xd109,
+ 0xd109, 0x080c, 0x0d7d, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c,
+ 0x0d7d, 0x0013, 0x006e, 0x0005, 0xd124, 0xdf30, 0xd124, 0xd124,
+ 0xd124, 0xd124, 0xd124, 0xd124, 0xdedf, 0xdf82, 0xd124, 0xe5cf,
+ 0xe603, 0xe5cf, 0xe603, 0xd124, 0x080c, 0x0d7d, 0x6000, 0x9082,
+ 0x0016, 0x1a0c, 0x0d7d, 0x6000, 0x000a, 0x0005, 0xd13e, 0xdbec,
+ 0xdcb7, 0xdcda, 0xdd56, 0xd13e, 0xde51, 0xddde, 0xda19, 0xdeb7,
+ 0xdecc, 0xd13e, 0xd13e, 0xd13e, 0xd13e, 0xd13e, 0x080c, 0x0d7d,
+ 0x91b2, 0x0053, 0x1a0c, 0x0d7d, 0x2100, 0x91b2, 0x0040, 0x1a04,
+ 0xd5b0, 0x0002, 0xd18a, 0xd37e, 0xd18a, 0xd18a, 0xd18a, 0xd387,
+ 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a,
+ 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a,
+ 0xd18a, 0xd18c, 0xd1f3, 0xd202, 0xd266, 0xd291, 0xd30a, 0xd369,
+ 0xd18a, 0xd18a, 0xd38a, 0xd18a, 0xd18a, 0xd39f, 0xd3ac, 0xd18a,
+ 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd452, 0xd18a, 0xd18a, 0xd466,
+ 0xd18a, 0xd18a, 0xd421, 0xd18a, 0xd18a, 0xd18a, 0xd47e, 0xd18a,
+ 0xd18a, 0xd18a, 0xd4fb, 0xd18a, 0xd18a, 0xd18a, 0xd18a, 0xd18a,
+ 0xd18a, 0xd578, 0x080c, 0x0d7d, 0x080c, 0x6a9b, 0x1150, 0x2001,
+ 0x1837, 0x2004, 0xd0cc, 0x1128, 0x9084, 0x0009, 0x9086, 0x0008,
+ 0x1140, 0x6007, 0x0009, 0x602f, 0x0009, 0x6017, 0x0000, 0x0804,
+ 0xd377, 0x080c, 0x6a37, 0x00e6, 0x00c6, 0x0036, 0x0026, 0x0016,
+ 0x6210, 0x2258, 0xbaa0, 0x0026, 0x2019, 0x0029, 0x080c, 0xa91e,
+ 0x080c, 0x943d, 0x0076, 0x903e, 0x080c, 0x9306, 0x2c08, 0x080c,
+ 0xe167, 0x007e, 0x001e, 0x080c, 0xa93a, 0x001e, 0x002e, 0x003e,
+ 0x00ce, 0x00ee, 0x6610, 0x2658, 0x080c, 0x66a2, 0xbe04, 0x9684,
+ 0x00ff, 0x9082, 0x0006, 0x1268, 0x0016, 0x0026, 0x6210, 0x00b6,
+ 0x2258, 0xbaa0, 0x00be, 0x2c08, 0x080c, 0xe82c, 0x002e, 0x001e,
+ 0x1178, 0x080c, 0xe095, 0x1904, 0xd25e, 0x080c, 0xe031, 0x1120,
+ 0x6007, 0x0008, 0x0804, 0xd377, 0x6007, 0x0009, 0x0804, 0xd377,
+ 0x080c, 0xe2c8, 0x0128, 0x080c, 0xe095, 0x0d78, 0x0804, 0xd25e,
+ 0x6017, 0x1900, 0x0c88, 0x080c, 0x3377, 0x1904, 0xd5ad, 0x6106,
+ 0x080c, 0xdfe2, 0x6007, 0x0006, 0x0804, 0xd377, 0x6007, 0x0007,
+ 0x0804, 0xd377, 0x080c, 0xe63f, 0x1904, 0xd5ad, 0x080c, 0x3377,
+ 0x1904, 0xd5ad, 0x00d6, 0x6610, 0x2658, 0xbe04, 0x9684, 0x00ff,
+ 0x9082, 0x0006, 0x1220, 0x2001, 0x0001, 0x080c, 0x65cf, 0x96b4,
+ 0xff00, 0x8637, 0x9686, 0x0006, 0x0188, 0x9686, 0x0004, 0x0170,
+ 0xbe04, 0x96b4, 0x00ff, 0x9686, 0x0006, 0x0140, 0x9686, 0x0004,
+ 0x0128, 0x9686, 0x0005, 0x0110, 0x00de, 0x0480, 0x00e6, 0x2071,
+ 0x0260, 0x7034, 0x9084, 0x0003, 0x1140, 0x7034, 0x9082, 0x0014,
+ 0x0220, 0x7030, 0x9084, 0x0003, 0x0130, 0x00ee, 0x6017, 0x0000,
+ 0x602f, 0x0007, 0x00b0, 0x00ee, 0x080c, 0xe0fd, 0x1190, 0x9686,
+ 0x0006, 0x1140, 0x0026, 0x6210, 0x2258, 0xbaa0, 0x900e, 0x080c,
+ 0x328f, 0x002e, 0x080c, 0x672e, 0x6007, 0x000a, 0x00de, 0x0804,
+ 0xd377, 0x6007, 0x000b, 0x00de, 0x0804, 0xd377, 0x080c, 0x3240,
+ 0x080c, 0xd0b0, 0x6007, 0x0001, 0x0804, 0xd377, 0x080c, 0xe63f,
+ 0x1904, 0xd5ad, 0x080c, 0x3377, 0x1904, 0xd5ad, 0x2071, 0x0260,
+ 0x7034, 0x90b4, 0x0003, 0x1948, 0x90b2, 0x0014, 0x0a30, 0x7030,
+ 0x9084, 0x0003, 0x1910, 0x6610, 0x2658, 0xbe04, 0x9686, 0x0707,
+ 0x09e8, 0x0026, 0x6210, 0x2258, 0xbaa0, 0x900e, 0x080c, 0x328f,
+ 0x002e, 0x6007, 0x000c, 0x2001, 0x0001, 0x080c, 0xe80c, 0x0804,
+ 0xd377, 0x080c, 0x6a9b, 0x1140, 0x2001, 0x1837, 0x2004, 0x9084,
+ 0x0009, 0x9086, 0x0008, 0x1110, 0x0804, 0xd199, 0x080c, 0x6a37,
+ 0x6610, 0x2658, 0xbe04, 0x9684, 0x00ff, 0x9082, 0x0006, 0x06c8,
+ 0x1138, 0x0026, 0x2001, 0x0006, 0x080c, 0x660f, 0x002e, 0x0050,
+ 0x96b4, 0xff00, 0x8637, 0x9686, 0x0004, 0x0120, 0x9686, 0x0006,
+ 0x1904, 0xd25e, 0x080c, 0xe10a, 0x1120, 0x6007, 0x000e, 0x0804,
+ 0xd377, 0x0046, 0x6410, 0x2458, 0xbca0, 0x0046, 0x080c, 0x3240,
+ 0x080c, 0xd0b0, 0x004e, 0x0016, 0x9006, 0x2009, 0x1848, 0x210c,
+ 0xd1a4, 0x0148, 0x2009, 0x0029, 0x080c, 0xe445, 0x6010, 0x2058,
+ 0xb800, 0xc0e5, 0xb802, 0x001e, 0x004e, 0x6007, 0x0001, 0x0804,
+ 0xd377, 0x2001, 0x0001, 0x080c, 0x65cf, 0x0156, 0x0016, 0x0026,
+ 0x0036, 0x20a9, 0x0004, 0x2019, 0x1805, 0x2011, 0x0270, 0x080c,
+ 0xbc8e, 0x003e, 0x002e, 0x001e, 0x015e, 0x9005, 0x0168, 0x96b4,
+ 0xff00, 0x8637, 0x9682, 0x0004, 0x0a04, 0xd25e, 0x9682, 0x0007,
+ 0x0a04, 0xd2ba, 0x0804, 0xd25e, 0x6017, 0x1900, 0x6007, 0x0009,
+ 0x0804, 0xd377, 0x080c, 0x6a9b, 0x1140, 0x2001, 0x1837, 0x2004,
+ 0x9084, 0x0009, 0x9086, 0x0008, 0x1110, 0x0804, 0xd199, 0x080c,
+ 0x6a37, 0x6610, 0x2658, 0xbe04, 0x9684, 0x00ff, 0x0006, 0x9086,
+ 0x0001, 0x000e, 0x0170, 0x9082, 0x0006, 0x0698, 0x0150, 0x96b4,
+ 0xff00, 0x8637, 0x9686, 0x0004, 0x0120, 0x9686, 0x0006, 0x1904,
+ 0xd25e, 0x080c, 0xe138, 0x1130, 0x080c, 0xe031, 0x1118, 0x6007,
+ 0x0010, 0x04e8, 0x0046, 0x6410, 0x2458, 0xbca0, 0x0046, 0x080c,
+ 0x3240, 0x080c, 0xd0b0, 0x004e, 0x0016, 0x9006, 0x2009, 0x1848,
+ 0x210c, 0xd1a4, 0x0148, 0x2009, 0x0029, 0x080c, 0xe445, 0x6010,
+ 0x2058, 0xb800, 0xc0e5, 0xb802, 0x001e, 0x004e, 0x6007, 0x0001,
+ 0x00f0, 0x080c, 0xe2c8, 0x0140, 0x96b4, 0xff00, 0x8637, 0x9686,
+ 0x0006, 0x0978, 0x0804, 0xd25e, 0x6017, 0x1900, 0x6007, 0x0009,
+ 0x0070, 0x080c, 0x3377, 0x1904, 0xd5ad, 0x080c, 0xe63f, 0x1904,
+ 0xd5ad, 0x080c, 0xd77a, 0x1904, 0xd25e, 0x6007, 0x0012, 0x6003,
+ 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0005, 0x6007, 0x0001,
+ 0x6003, 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0cb0, 0x6007,
+ 0x0005, 0x0c68, 0x080c, 0xe63f, 0x1904, 0xd5ad, 0x080c, 0x3377,
+ 0x1904, 0xd5ad, 0x080c, 0xd77a, 0x1904, 0xd25e, 0x6007, 0x0020,
+ 0x6003, 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0005, 0x080c,
+ 0x3377, 0x1904, 0xd5ad, 0x6007, 0x0023, 0x6003, 0x0001, 0x080c,
+ 0x92b7, 0x080c, 0x9738, 0x0005, 0x080c, 0xe63f, 0x1904, 0xd5ad,
+ 0x080c, 0x3377, 0x1904, 0xd5ad, 0x080c, 0xd77a, 0x1904, 0xd25e,
+ 0x0016, 0x0026, 0x00e6, 0x2071, 0x0260, 0x2c08, 0x2011, 0x1820,
+ 0x2214, 0x703c, 0x9206, 0x11e0, 0x2011, 0x181f, 0x2214, 0x7038,
+ 0x9084, 0x00ff, 0x9206, 0x11a0, 0x7240, 0x080c, 0xc968, 0x0570,
+ 0x2260, 0x6008, 0x9086, 0xffff, 0x0120, 0x7244, 0x6008, 0x9206,
+ 0x1528, 0x6020, 0x9086, 0x0007, 0x1508, 0x080c, 0xacb0, 0x04a0,
+ 0x7244, 0x9286, 0xffff, 0x0180, 0x2c08, 0x080c, 0xc968, 0x01b0,
+ 0x2260, 0x7240, 0x6008, 0x9206, 0x1188, 0x6010, 0x9190, 0x0004,
+ 0x2214, 0x9206, 0x01b8, 0x0050, 0x7240, 0x2c08, 0x9006, 0x080c,
+ 0xe40f, 0x1180, 0x7244, 0x9286, 0xffff, 0x01b0, 0x2160, 0x6007,
+ 0x0026, 0x6017, 0x1700, 0x7214, 0x9296, 0xffff, 0x1180, 0x6007,
+ 0x0025, 0x0068, 0x6020, 0x9086, 0x0007, 0x1d80, 0x6004, 0x9086,
+ 0x0024, 0x1110, 0x080c, 0xacb0, 0x2160, 0x6007, 0x0025, 0x6003,
+ 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x00ee, 0x002e, 0x001e,
+ 0x0005, 0x2001, 0x0001, 0x080c, 0x65cf, 0x0156, 0x0016, 0x0026,
+ 0x0036, 0x20a9, 0x0004, 0x2019, 0x1805, 0x2011, 0x0276, 0x080c,
+ 0xbc8e, 0x003e, 0x002e, 0x001e, 0x015e, 0x0120, 0x6007, 0x0031,
+ 0x0804, 0xd377, 0x080c, 0xb8ff, 0x080c, 0x753d, 0x1190, 0x0006,
+ 0x0026, 0x0036, 0x080c, 0x7557, 0x1138, 0x080c, 0x7840, 0x080c,
+ 0x6092, 0x080c, 0x746e, 0x0010, 0x080c, 0x7511, 0x003e, 0x002e,
+ 0x000e, 0x0005, 0x080c, 0x3377, 0x1904, 0xd5ad, 0x080c, 0xd77a,
+ 0x1904, 0xd25e, 0x6106, 0x080c, 0xd796, 0x1120, 0x6007, 0x002b,
+ 0x0804, 0xd377, 0x6007, 0x002c, 0x0804, 0xd377, 0x080c, 0xe63f,
+ 0x1904, 0xd5ad, 0x080c, 0x3377, 0x1904, 0xd5ad, 0x080c, 0xd77a,
+ 0x1904, 0xd25e, 0x6106, 0x080c, 0xd79b, 0x1120, 0x6007, 0x002e,
+ 0x0804, 0xd377, 0x6007, 0x002f, 0x0804, 0xd377, 0x080c, 0x3377,
+ 0x1904, 0xd5ad, 0x00e6, 0x00d6, 0x00c6, 0x6010, 0x2058, 0xb904,
+ 0x9184, 0x00ff, 0x9086, 0x0006, 0x0158, 0x9184, 0xff00, 0x8007,
+ 0x9086, 0x0006, 0x0128, 0x00ce, 0x00de, 0x00ee, 0x0804, 0xd37e,
+ 0x080c, 0x5742, 0xd0e4, 0x0904, 0xd4f8, 0x2071, 0x026c, 0x7010,
+ 0x603a, 0x7014, 0x603e, 0x7108, 0x720c, 0x080c, 0x6ad9, 0x0140,
+ 0x6010, 0x2058, 0xb810, 0x9106, 0x1118, 0xb814, 0x9206, 0x0510,
+ 0x080c, 0x6ad5, 0x15b8, 0x2069, 0x1800, 0x6880, 0x9206, 0x1590,
+ 0x687c, 0x9106, 0x1578, 0x7210, 0x080c, 0xc968, 0x0590, 0x080c,
+ 0xd665, 0x0578, 0x080c, 0xe4c1, 0x0560, 0x622e, 0x6007, 0x0036,
+ 0x6003, 0x0001, 0x2009, 0x8020, 0x080c, 0x92b0, 0x00ce, 0x00de,
+ 0x00ee, 0x0005, 0x7214, 0x9286, 0xffff, 0x0150, 0x080c, 0xc968,
+ 0x01c0, 0x9280, 0x0002, 0x2004, 0x7110, 0x9106, 0x1190, 0x08e0,
+ 0x7210, 0x2c08, 0x9085, 0x0001, 0x080c, 0xe40f, 0x2c10, 0x2160,
+ 0x0140, 0x0890, 0x6007, 0x0037, 0x602f, 0x0009, 0x6017, 0x1500,
+ 0x08b8, 0x6007, 0x0037, 0x602f, 0x0003, 0x6017, 0x1700, 0x0880,
+ 0x6007, 0x0012, 0x0868, 0x080c, 0x3377, 0x1904, 0xd5ad, 0x6010,
+ 0x2058, 0xb804, 0x9084, 0xff00, 0x8007, 0x9086, 0x0006, 0x1904,
+ 0xd37e, 0x00e6, 0x00d6, 0x00c6, 0x080c, 0x5742, 0xd0e4, 0x0904,
+ 0xd570, 0x2069, 0x1800, 0x2071, 0x026c, 0x7008, 0x603a, 0x720c,
+ 0x623e, 0x9286, 0xffff, 0x1150, 0x7208, 0x00c6, 0x2c08, 0x9085,
+ 0x0001, 0x080c, 0xe40f, 0x2c10, 0x00ce, 0x05e8, 0x080c, 0xc968,
+ 0x05d0, 0x7108, 0x9280, 0x0002, 0x2004, 0x9106, 0x15a0, 0x00c6,
+ 0x0026, 0x2260, 0x080c, 0xc559, 0x002e, 0x00ce, 0x7118, 0x918c,
+ 0xff00, 0x810f, 0x9186, 0x0001, 0x0178, 0x9186, 0x0005, 0x0118,
+ 0x9186, 0x0007, 0x1198, 0x9280, 0x0005, 0x2004, 0x9005, 0x0170,
+ 0x080c, 0xd665, 0x0904, 0xd4f1, 0x0056, 0x7510, 0x7614, 0x080c,
+ 0xe4da, 0x005e, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x6007, 0x003b,
+ 0x602f, 0x0009, 0x6017, 0x2a00, 0x6003, 0x0001, 0x2009, 0x8020,
+ 0x080c, 0x92b0, 0x0c78, 0x6007, 0x003b, 0x602f, 0x0003, 0x6017,
+ 0x0300, 0x6003, 0x0001, 0x2009, 0x8020, 0x080c, 0x92b0, 0x0c10,
+ 0x6007, 0x003b, 0x602f, 0x000b, 0x6017, 0x0000, 0x0804, 0xd4c8,
+ 0x00e6, 0x0026, 0x080c, 0x6a9b, 0x0550, 0x080c, 0x6a37, 0x080c,
+ 0xe6b1, 0x1518, 0x2071, 0x1800, 0x70dc, 0x9085, 0x0003, 0x70de,
+ 0x00f6, 0x2079, 0x0100, 0x72b0, 0x9284, 0x00ff, 0x707e, 0x78e6,
+ 0x9284, 0xff00, 0x7280, 0x9205, 0x7082, 0x78ea, 0x00fe, 0x70e7,
+ 0x0000, 0x080c, 0x6ad9, 0x0120, 0x2011, 0x1a08, 0x2013, 0x07d0,
+ 0xd0ac, 0x1128, 0x080c, 0x3011, 0x0010, 0x080c, 0xe6e3, 0x002e,
+ 0x00ee, 0x080c, 0xacb0, 0x0804, 0xd37d, 0x080c, 0xacb0, 0x0005,
+ 0x2600, 0x0002, 0xd5c4, 0xd5f5, 0xd606, 0xd5c4, 0xd5c4, 0xd5c6,
+ 0xd617, 0xd5c4, 0xd5c4, 0xd5c4, 0xd5e3, 0xd5c4, 0xd5c4, 0xd5c4,
+ 0xd622, 0xd62f, 0xd660, 0xd5c4, 0x080c, 0x0d7d, 0x080c, 0xe63f,
+ 0x1d20, 0x080c, 0x3377, 0x1d08, 0x080c, 0xd77a, 0x1148, 0x7038,
+ 0x6016, 0x6007, 0x0045, 0x6003, 0x0001, 0x080c, 0x92b7, 0x0005,
+ 0x080c, 0x3240, 0x080c, 0xd0b0, 0x6007, 0x0001, 0x6003, 0x0001,
+ 0x080c, 0x92b7, 0x0005, 0x080c, 0xe63f, 0x1938, 0x080c, 0x3377,
+ 0x1920, 0x080c, 0xd77a, 0x1d60, 0x703c, 0x6016, 0x6007, 0x004a,
+ 0x6003, 0x0001, 0x080c, 0x92b7, 0x0005, 0x080c, 0x3377, 0x1904,
+ 0xd5ad, 0x2009, 0x0041, 0x080c, 0xe6ec, 0x6007, 0x0047, 0x6003,
+ 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0005, 0x080c, 0x3377,
+ 0x1904, 0xd5ad, 0x2009, 0x0042, 0x080c, 0xe6ec, 0x6007, 0x0047,
+ 0x6003, 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0005, 0x080c,
+ 0x3377, 0x1904, 0xd5ad, 0x2009, 0x0046, 0x080c, 0xe6ec, 0x080c,
+ 0xacb0, 0x0005, 0x080c, 0xd682, 0x0904, 0xd5ad, 0x6007, 0x004e,
+ 0x6003, 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0005, 0x6007,
+ 0x004f, 0x6017, 0x0000, 0x7134, 0x918c, 0x00ff, 0x81ff, 0x0508,
+ 0x9186, 0x0001, 0x1160, 0x7140, 0x2001, 0x19bc, 0x2004, 0x9106,
+ 0x11b0, 0x7144, 0x2001, 0x19bd, 0x2004, 0x9106, 0x0190, 0x9186,
+ 0x0002, 0x1168, 0x2011, 0x0276, 0x20a9, 0x0004, 0x6010, 0x0096,
+ 0x2048, 0x2019, 0x000a, 0x080c, 0xbca2, 0x009e, 0x0110, 0x6017,
+ 0x0001, 0x6003, 0x0001, 0x080c, 0x92b7, 0x080c, 0x9738, 0x0005,
+ 0x6007, 0x0050, 0x703c, 0x6016, 0x0ca0, 0x0016, 0x00e6, 0x2071,
+ 0x0260, 0x00b6, 0x00c6, 0x2260, 0x6010, 0x2058, 0xb8d4, 0xd084,
+ 0x0150, 0x7128, 0x604c, 0x9106, 0x1120, 0x712c, 0x6050, 0x9106,
+ 0x0110, 0x9006, 0x0010, 0x9085, 0x0001, 0x00ce, 0x00be, 0x00ee,
+ 0x001e, 0x0005, 0x0016, 0x0096, 0x0086, 0x00e6, 0x01c6, 0x01d6,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0x1800, 0x7090, 0x908a, 0x00f9,
+ 0x16e8, 0x20e1, 0x0000, 0x2001, 0x199f, 0x2003, 0x0000, 0x080c,
+ 0x1060, 0x05a0, 0x2900, 0x6016, 0x7090, 0x8004, 0xa816, 0x908a,
+ 0x001e, 0x02d0, 0xa833, 0x001e, 0x20a9, 0x001e, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x001b, 0x20a0, 0x2001, 0x199f, 0x0016, 0x200c,
+ 0x0471, 0x001e, 0x2940, 0x080c, 0x1060, 0x01c0, 0x2900, 0xa006,
+ 0x2100, 0x81ff, 0x0180, 0x0c18, 0xa832, 0x20a8, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x001b, 0x20a0, 0x2001, 0x199f, 0x0016, 0x200c,
+ 0x00b1, 0x001e, 0x0000, 0x9085, 0x0001, 0x0048, 0x2071, 0x1800,
+ 0x7093, 0x0000, 0x6014, 0x2048, 0x080c, 0x0ff9, 0x9006, 0x012e,
+ 0x01de, 0x01ce, 0x00ee, 0x008e, 0x009e, 0x001e, 0x0005, 0x0006,
+ 0x0016, 0x0026, 0x0036, 0x00c6, 0x918c, 0xffff, 0x11a8, 0x080c,
+ 0x21e3, 0x2099, 0x026c, 0x2001, 0x0014, 0x3518, 0x9312, 0x1218,
+ 0x23a8, 0x4003, 0x00f8, 0x20a8, 0x4003, 0x22a8, 0x8108, 0x080c,
+ 0x21e3, 0x2099, 0x0260, 0x0ca8, 0x080c, 0x21e3, 0x2061, 0x199f,
+ 0x6004, 0x2098, 0x6008, 0x3518, 0x9312, 0x1218, 0x23a8, 0x4003,
+ 0x0048, 0x20a8, 0x4003, 0x22a8, 0x8108, 0x080c, 0x21e3, 0x2099,
+ 0x0260, 0x0ca8, 0x2061, 0x199f, 0x2019, 0x0280, 0x3300, 0x931e,
+ 0x0110, 0x6006, 0x0020, 0x2001, 0x0260, 0x6006, 0x8108, 0x2162,
+ 0x9292, 0x0021, 0x9296, 0xffff, 0x620a, 0x00ce, 0x003e, 0x002e,
+ 0x001e, 0x000e, 0x0005, 0x0006, 0x0016, 0x0026, 0x0036, 0x00c6,
+ 0x81ff, 0x11b8, 0x080c, 0x21fb, 0x20a1, 0x024c, 0x2001, 0x0014,
+ 0x3518, 0x9312, 0x1218, 0x23a8, 0x4003, 0x0418, 0x20a8, 0x4003,
+ 0x82ff, 0x01f8, 0x22a8, 0x8108, 0x080c, 0x21fb, 0x20a1, 0x0240,
+ 0x0c98, 0x080c, 0x21fb, 0x2061, 0x19a2, 0x6004, 0x20a0, 0x6008,
+ 0x3518, 0x9312, 0x1218, 0x23a8, 0x4003, 0x0058, 0x20a8, 0x4003,
+ 0x82ff, 0x0138, 0x22a8, 0x8108, 0x080c, 0x21fb, 0x20a1, 0x0240,
+ 0x0c98, 0x2061, 0x19a2, 0x2019, 0x0260, 0x3400, 0x931e, 0x0110,
+ 0x6006, 0x0020, 0x2001, 0x0240, 0x6006, 0x8108, 0x2162, 0x9292,
+ 0x0021, 0x9296, 0xffff, 0x620a, 0x00ce, 0x003e, 0x002e, 0x001e,
+ 0x000e, 0x0005, 0x00b6, 0x0066, 0x6610, 0x2658, 0xbe04, 0x96b4,
+ 0xff00, 0x8637, 0x9686, 0x0006, 0x0170, 0x9686, 0x0004, 0x0158,
+ 0xbe04, 0x96b4, 0x00ff, 0x9686, 0x0006, 0x0128, 0x9686, 0x0004,
+ 0x0110, 0x9085, 0x0001, 0x006e, 0x00be, 0x0005, 0x00d6, 0x080c,
+ 0xd810, 0x00de, 0x0005, 0x00d6, 0x080c, 0xd81d, 0x1520, 0x680c,
+ 0x908c, 0xff00, 0x6820, 0x9084, 0x00ff, 0x9115, 0x6216, 0x6824,
+ 0x602e, 0xd1e4, 0x0130, 0x9006, 0x080c, 0xe80c, 0x2009, 0x0001,
+ 0x0078, 0xd1ec, 0x0180, 0x6920, 0x918c, 0x00ff, 0x6824, 0x080c,
+ 0x2661, 0x1148, 0x2001, 0x0001, 0x080c, 0xe80c, 0x2110, 0x900e,
+ 0x080c, 0x328f, 0x0018, 0x9085, 0x0001, 0x0008, 0x9006, 0x00de,
+ 0x0005, 0x00b6, 0x00c6, 0x080c, 0xad20, 0x0598, 0x0016, 0x0026,
+ 0x00c6, 0x2011, 0x0263, 0x2204, 0x8211, 0x220c, 0x080c, 0x2661,
+ 0x1568, 0x080c, 0x6632, 0x1550, 0xbe12, 0xbd16, 0x00ce, 0x002e,
+ 0x001e, 0x2b00, 0x6012, 0x080c, 0xe63f, 0x11c8, 0x080c, 0x3377,
+ 0x11b0, 0x080c, 0xd77a, 0x0500, 0x2001, 0x0007, 0x080c, 0x65e3,
+ 0x2001, 0x0007, 0x080c, 0x660f, 0x6017, 0x0000, 0x6023, 0x0001,
+ 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x92b7, 0x0010, 0x080c,
+ 0xacb0, 0x9085, 0x0001, 0x00ce, 0x00be, 0x0005, 0x080c, 0xacb0,
+ 0x00ce, 0x002e, 0x001e, 0x0ca8, 0x080c, 0xacb0, 0x9006, 0x0c98,
+ 0x2069, 0x026d, 0x6800, 0x9082, 0x0010, 0x1228, 0x6017, 0x0000,
+ 0x9085, 0x0001, 0x0008, 0x9006, 0x0005, 0x6017, 0x0000, 0x2069,
+ 0x026c, 0x6808, 0x9084, 0xff00, 0x9086, 0x0800, 0x1190, 0x6904,
+ 0x9186, 0x0018, 0x0118, 0x9186, 0x0014, 0x1158, 0x810f, 0x6800,
+ 0x9084, 0x00ff, 0x910d, 0x6162, 0x908e, 0x0014, 0x0110, 0x908e,
+ 0x0010, 0x0005, 0x6004, 0x90b2, 0x0053, 0x1a0c, 0x0d7d, 0x91b6,
+ 0x0013, 0x1130, 0x2008, 0x91b2, 0x0040, 0x1a04, 0xd95f, 0x0092,
+ 0x91b6, 0x0027, 0x0120, 0x91b6, 0x0014, 0x190c, 0x0d7d, 0x2001,
+ 0x0007, 0x080c, 0x660f, 0x080c, 0x967a, 0x080c, 0xaceb, 0x080c,
+ 0x9738, 0x0005, 0xd89a, 0xd89c, 0xd89a, 0xd89a, 0xd89a, 0xd89c,
+ 0xd8a9, 0xd95c, 0xd8f9, 0xd95c, 0xd90d, 0xd95c, 0xd8a9, 0xd95c,
+ 0xd954, 0xd95c, 0xd954, 0xd95c, 0xd95c, 0xd89a, 0xd89a, 0xd89a,
+ 0xd89a, 0xd89a, 0xd89a, 0xd89a, 0xd89a, 0xd89a, 0xd89a, 0xd89a,
+ 0xd89c, 0xd89a, 0xd95c, 0xd89a, 0xd89a, 0xd95c, 0xd89a, 0xd959,
+ 0xd95c, 0xd89a, 0xd89a, 0xd89a, 0xd89a, 0xd95c, 0xd95c, 0xd89a,
+ 0xd95c, 0xd95c, 0xd89a, 0xd8a4, 0xd89a, 0xd89a, 0xd89a, 0xd89a,
+ 0xd958, 0xd95c, 0xd89a, 0xd89a, 0xd95c, 0xd95c, 0xd89a, 0xd89a,
+ 0xd89a, 0xd89a, 0x080c, 0x0d7d, 0x080c, 0xd0b3, 0x6003, 0x0002,
+ 0x080c, 0x9738, 0x0804, 0xd95e, 0x9006, 0x080c, 0x65cf, 0x0804,
+ 0xd95c, 0x080c, 0x6ad5, 0x1904, 0xd95c, 0x9006, 0x080c, 0x65cf,
+ 0x6010, 0x2058, 0xb810, 0x9086, 0x00ff, 0x1140, 0x00f6, 0x2079,
+ 0x1800, 0x78a8, 0x8000, 0x78aa, 0x00fe, 0x0428, 0x6010, 0x2058,
+ 0xb884, 0x9005, 0x1178, 0x080c, 0xd09b, 0x1904, 0xd95c, 0x0036,
+ 0x0046, 0xbba0, 0x2021, 0x0007, 0x080c, 0x4d09, 0x004e, 0x003e,
+ 0x0804, 0xd95c, 0x080c, 0x33a8, 0x1904, 0xd95c, 0x2001, 0x1800,
+ 0x2004, 0x9086, 0x0002, 0x1138, 0x00f6, 0x2079, 0x1800, 0x78a8,
+ 0x8000, 0x78aa, 0x00fe, 0x2001, 0x0002, 0x080c, 0x65e3, 0x6023,
+ 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x92b7, 0x080c,
+ 0x9738, 0x6110, 0x2158, 0x2009, 0x0001, 0x080c, 0x86d6, 0x0804,
+ 0xd95e, 0x6610, 0x2658, 0xbe04, 0x96b4, 0xff00, 0x8637, 0x9686,
+ 0x0006, 0x0904, 0xd95c, 0x9686, 0x0004, 0x0904, 0xd95c, 0x080c,
+ 0x8f52, 0x2001, 0x0004, 0x0804, 0xd95a, 0x2001, 0x1800, 0x2004,
+ 0x9086, 0x0003, 0x1158, 0x0036, 0x0046, 0x6010, 0x2058, 0xbba0,
+ 0x2021, 0x0006, 0x080c, 0x4d09, 0x004e, 0x003e, 0x2001, 0x0006,
+ 0x080c, 0xd978, 0x6610, 0x2658, 0xbe04, 0x0066, 0x96b4, 0xff00,
+ 0x8637, 0x9686, 0x0006, 0x006e, 0x0168, 0x2001, 0x0006, 0x080c,
+ 0x660f, 0x9284, 0x00ff, 0x908e, 0x0007, 0x1120, 0x2001, 0x0006,
+ 0x080c, 0x65e3, 0x080c, 0x6ad5, 0x11f8, 0x2001, 0x1837, 0x2004,
+ 0xd0a4, 0x01d0, 0xbe04, 0x96b4, 0x00ff, 0x9686, 0x0006, 0x01a0,
+ 0x00f6, 0x2079, 0x1800, 0x78a8, 0x8000, 0x78aa, 0x00fe, 0x0804,
+ 0xd8e3, 0x2001, 0x0004, 0x0030, 0x2001, 0x0006, 0x0409, 0x0020,
+ 0x0018, 0x0010, 0x080c, 0x660f, 0x080c, 0xacb0, 0x0005, 0x2600,
+ 0x0002, 0xd973, 0xd973, 0xd973, 0xd973, 0xd973, 0xd975, 0xd973,
+ 0xd975, 0xd973, 0xd973, 0xd975, 0xd973, 0xd973, 0xd973, 0xd975,
+ 0xd975, 0xd975, 0xd975, 0x080c, 0x0d7d, 0x080c, 0xacb0, 0x0005,
+ 0x0016, 0x00b6, 0x00d6, 0x6110, 0x2158, 0xb900, 0xd184, 0x0138,
+ 0x080c, 0x65e3, 0x9006, 0x080c, 0x65cf, 0x080c, 0x326f, 0x00de,
+ 0x00be, 0x001e, 0x0005, 0x6610, 0x2658, 0xb804, 0x9084, 0xff00,
+ 0x8007, 0x90b2, 0x000c, 0x1a0c, 0x0d7d, 0x91b6, 0x0015, 0x1110,
+ 0x003b, 0x0028, 0x91b6, 0x0016, 0x190c, 0x0d7d, 0x006b, 0x0005,
+ 0xb77c, 0xb77c, 0xb77c, 0xb77c, 0xda0d, 0xb77c, 0xd9f7, 0xd9b8,
+ 0xb77c, 0xb77c, 0xb77c, 0xb77c, 0xb77c, 0xb77c, 0xb77c, 0xb77c,
+ 0xda0d, 0xb77c, 0xd9f7, 0xd9fe, 0xb77c, 0xb77c, 0xb77c, 0xb77c,
+ 0x00f6, 0x080c, 0x6ad5, 0x11d8, 0x080c, 0xd09b, 0x11c0, 0x6010,
+ 0x905d, 0x01a8, 0xb884, 0x9005, 0x0190, 0x9006, 0x080c, 0x65cf,
+ 0x2001, 0x0002, 0x080c, 0x65e3, 0x6023, 0x0001, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x080c, 0x92b7, 0x080c, 0x9738, 0x00f0, 0x2011,
+ 0x0263, 0x2204, 0x8211, 0x220c, 0x080c, 0x2661, 0x11b0, 0x080c,
+ 0x6693, 0x0118, 0x080c, 0xacb0, 0x0080, 0xb810, 0x0006, 0xb814,
+ 0x0006, 0xb884, 0x0006, 0x080c, 0x60ac, 0x000e, 0xb886, 0x000e,
+ 0xb816, 0x000e, 0xb812, 0x080c, 0xacb0, 0x00fe, 0x0005, 0x6604,
+ 0x96b6, 0x001e, 0x1110, 0x080c, 0xacb0, 0x0005, 0x080c, 0xbb07,
+ 0x1148, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x92b7, 0x080c,
+ 0x9738, 0x0010, 0x080c, 0xacb0, 0x0005, 0x0804, 0xacb0, 0x6004,
+ 0x908a, 0x0053, 0x1a0c, 0x0d7d, 0x080c, 0x967a, 0x080c, 0xaceb,
+ 0x0005, 0x9182, 0x0040, 0x0002, 0xda30, 0xda30, 0xda30, 0xda30,
+ 0xda32, 0xda30, 0xda30, 0xda30, 0xda30, 0xda30, 0xda30, 0xda30,
+ 0xda30, 0xda30, 0xda30, 0xda30, 0xda30, 0xda30, 0xda30, 0xda30,
+ 0x080c, 0x0d7d, 0x0096, 0x00b6, 0x00d6, 0x00e6, 0x00f6, 0x0046,
+ 0x0026, 0x6210, 0x2258, 0xb8bc, 0x9005, 0x11b0, 0x6007, 0x0044,
+ 0x2071, 0x0260, 0x7444, 0x94a4, 0xff00, 0x0904, 0xda99, 0x080c,
+ 0xe800, 0x1170, 0x9486, 0x2000, 0x1158, 0x2009, 0x0001, 0x2011,
+ 0x0200, 0x080c, 0x8979, 0x0020, 0x9026, 0x080c, 0xe684, 0x0c30,
+ 0x080c, 0x1047, 0x090c, 0x0d7d, 0x6003, 0x0007, 0xa867, 0x010d,
+ 0x9006, 0xa802, 0xa86a, 0xac8a, 0x2c00, 0xa88e, 0x6008, 0xa8e2,
+ 0x6010, 0x2058, 0xb8a0, 0x7130, 0xa97a, 0x0016, 0xa876, 0xa87f,
+ 0x0000, 0xa883, 0x0000, 0xa887, 0x0036, 0x080c, 0x6dee, 0x001e,
+ 0x080c, 0xe800, 0x1904, 0xdaf9, 0x9486, 0x2000, 0x1130, 0x2019,
+ 0x0017, 0x080c, 0xe3b5, 0x0804, 0xdaf9, 0x9486, 0x0200, 0x1120,
+ 0x080c, 0xe345, 0x0804, 0xdaf9, 0x9486, 0x0400, 0x0120, 0x9486,
+ 0x1000, 0x1904, 0xdaf9, 0x2019, 0x0002, 0x080c, 0xe364, 0x0804,
+ 0xdaf9, 0x2069, 0x1a6e, 0x6a00, 0xd284, 0x0904, 0xdb63, 0x9284,
+ 0x0300, 0x1904, 0xdb5c, 0x6804, 0x9005, 0x0904, 0xdb44, 0x2d78,
+ 0x6003, 0x0007, 0x080c, 0x1060, 0x0904, 0xdb05, 0x7800, 0xd08c,
+ 0x1118, 0x7804, 0x8001, 0x7806, 0x6017, 0x0000, 0x2001, 0x180f,
+ 0x2004, 0xd084, 0x1904, 0xdb67, 0x9006, 0xa802, 0xa867, 0x0116,
+ 0xa86a, 0x6008, 0xa8e2, 0x2c00, 0xa87a, 0x6010, 0x2058, 0xb8a0,
+ 0x7130, 0xa9b6, 0xa876, 0xb928, 0xa9ba, 0xb92c, 0xa9be, 0xb930,
+ 0xa9c2, 0xb934, 0xa9c6, 0xa883, 0x003d, 0x7044, 0x9084, 0x0003,
+ 0x9080, 0xdb01, 0x2005, 0xa87e, 0x20a9, 0x000a, 0x2001, 0x0270,
+ 0xaa5c, 0x9290, 0x0021, 0x2009, 0x0205, 0x200b, 0x0080, 0x20e1,
+ 0x0000, 0xab60, 0x23e8, 0x2098, 0x22a0, 0x4003, 0x200b, 0x0000,
+ 0x2001, 0x027a, 0x200c, 0xa9b2, 0x8000, 0x200c, 0xa9ae, 0x080c,
+ 0x6df1, 0x002e, 0x004e, 0x00fe, 0x00ee, 0x00de, 0x00be, 0x009e,
+ 0x0005, 0x0000, 0x0080, 0x0040, 0x0000, 0x2001, 0x1810, 0x2004,
+ 0xd084, 0x0120, 0x080c, 0x1047, 0x1904, 0xdaae, 0x6017, 0xf100,
+ 0x6003, 0x0001, 0x6007, 0x0041, 0x2009, 0xa022, 0x080c, 0x92b0,
+ 0x0c00, 0x2069, 0x0260, 0x6848, 0x9084, 0xff00, 0x9086, 0x1200,
+ 0x1198, 0x686c, 0x9084, 0x00ff, 0x0016, 0x6114, 0x918c, 0xf700,
+ 0x910d, 0x6116, 0x001e, 0x6003, 0x0001, 0x6007, 0x0043, 0x2009,
+ 0xa025, 0x080c, 0x92b0, 0x0828, 0x6868, 0x602e, 0x686c, 0x6032,
+ 0x6017, 0xf200, 0x6003, 0x0001, 0x6007, 0x0041, 0x2009, 0xa022,
+ 0x080c, 0x92b0, 0x0804, 0xdaf9, 0x2001, 0x180e, 0x2004, 0xd0ec,
+ 0x0120, 0x2011, 0x8049, 0x080c, 0x4b52, 0x6017, 0xf300, 0x0010,
+ 0x6017, 0xf100, 0x6003, 0x0001, 0x6007, 0x0041, 0x2009, 0xa022,
+ 0x080c, 0x92b0, 0x0804, 0xdaf9, 0x6017, 0xf500, 0x0c98, 0x6017,
+ 0xf600, 0x0804, 0xdb19, 0x6017, 0xf200, 0x0804, 0xdb19, 0xa867,
+ 0x0146, 0xa86b, 0x0000, 0x6008, 0xa886, 0x2c00, 0xa87a, 0x7044,
+ 0x9084, 0x0003, 0x9080, 0xdb01, 0x2005, 0xa87e, 0x2928, 0x6010,
+ 0x2058, 0xb8a0, 0xa876, 0xb828, 0xa88a, 0xb82c, 0xa88e, 0xb830,
+ 0xa892, 0xb834, 0xa896, 0xa883, 0x003d, 0x2009, 0x0205, 0x2104,
+ 0x9085, 0x0080, 0x200a, 0x20e1, 0x0000, 0x2011, 0x0210, 0x2214,
+ 0x9294, 0x0fff, 0xaaa2, 0x9282, 0x0111, 0x1a0c, 0x0d7d, 0x8210,
+ 0x821c, 0x2001, 0x026c, 0x2098, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0029, 0x20a0, 0x2011, 0xdbe3, 0x2041, 0x0001, 0x223d, 0x9784,
+ 0x00ff, 0x9322, 0x1208, 0x2300, 0x20a8, 0x4003, 0x931a, 0x0530,
+ 0x8210, 0xd7fc, 0x1130, 0x8d68, 0x2d0a, 0x2001, 0x0260, 0x2098,
+ 0x0c68, 0x2950, 0x080c, 0x1060, 0x0170, 0x2900, 0xb002, 0xa867,
+ 0x0147, 0xa86b, 0x0000, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x001b,
+ 0x20a0, 0x8840, 0x08d8, 0x2548, 0xa800, 0x902d, 0x0118, 0x080c,
+ 0x1079, 0x0cc8, 0x080c, 0x1079, 0x0804, 0xdb05, 0x2548, 0x8847,
+ 0x9885, 0x0046, 0xa866, 0x2009, 0x0205, 0x200b, 0x0000, 0x080c,
+ 0xe3e8, 0x0804, 0xdaf9, 0x8010, 0x0004, 0x801a, 0x0006, 0x8018,
+ 0x0008, 0x8016, 0x000a, 0x8014, 0x9186, 0x0013, 0x1160, 0x6004,
+ 0x908a, 0x0057, 0x1a0c, 0x0d7d, 0x9082, 0x0040, 0x0a0c, 0x0d7d,
+ 0x2008, 0x0804, 0xdc6f, 0x9186, 0x0051, 0x0108, 0x0040, 0x080c,
+ 0xab33, 0x01e8, 0x9086, 0x0002, 0x0904, 0xdcb7, 0x00c0, 0x9186,
+ 0x0027, 0x0180, 0x9186, 0x0048, 0x0128, 0x9186, 0x0014, 0x0150,
+ 0x190c, 0x0d7d, 0x080c, 0xab33, 0x0150, 0x9086, 0x0004, 0x0904,
+ 0xdd56, 0x0028, 0x6004, 0x9082, 0x0040, 0x2008, 0x001a, 0x080c,
+ 0xad6a, 0x0005, 0xdc36, 0xdc38, 0xdc38, 0xdc5f, 0xdc36, 0xdc36,
+ 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0xdc36,
+ 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0xdc36, 0x080c, 0x0d7d,
+ 0x080c, 0x967a, 0x080c, 0x9738, 0x0036, 0x0096, 0x6014, 0x904d,
+ 0x01d8, 0x080c, 0xc97a, 0x01c0, 0x6003, 0x0002, 0x6010, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1178, 0x2019, 0x0004, 0x080c,
+ 0xe3e8, 0x6017, 0x0000, 0x6018, 0x9005, 0x1120, 0x2001, 0x1986,
+ 0x2004, 0x601a, 0x6003, 0x0007, 0x009e, 0x003e, 0x0005, 0x0096,
+ 0x080c, 0x967a, 0x080c, 0x9738, 0x080c, 0xc97a, 0x0120, 0x6014,
+ 0x2048, 0x080c, 0x1079, 0x080c, 0xaceb, 0x009e, 0x0005, 0x0002,
+ 0xdc84, 0xdc99, 0xdc86, 0xdcae, 0xdc84, 0xdc84, 0xdc84, 0xdc84,
+ 0xdc84, 0xdc84, 0xdc84, 0xdc84, 0xdc84, 0xdc84, 0xdc84, 0xdc84,
+ 0xdc84, 0xdc84, 0xdc84, 0xdc84, 0x080c, 0x0d7d, 0x0096, 0x6014,
+ 0x2048, 0xa87c, 0xd0b4, 0x0138, 0x6003, 0x0007, 0x2009, 0x0043,
+ 0x080c, 0xad4d, 0x0010, 0x6003, 0x0004, 0x080c, 0x9738, 0x009e,
+ 0x0005, 0x080c, 0xc97a, 0x0138, 0x6114, 0x0096, 0x2148, 0xa97c,
+ 0x009e, 0xd1ec, 0x1138, 0x080c, 0x894e, 0x080c, 0xacb0, 0x080c,
+ 0x9738, 0x0005, 0x080c, 0xe648, 0x0db0, 0x0cc8, 0x6003, 0x0001,
+ 0x6007, 0x0041, 0x2009, 0xa022, 0x080c, 0x92b0, 0x0005, 0x9182,
+ 0x0040, 0x0002, 0xdcce, 0xdcd0, 0xdcce, 0xdcce, 0xdcce, 0xdcce,
+ 0xdcce, 0xdcce, 0xdcce, 0xdcce, 0xdcce, 0xdcce, 0xdcce, 0xdcce,
+ 0xdcce, 0xdcce, 0xdcce, 0xdcd1, 0xdcce, 0xdcce, 0x080c, 0x0d7d,
+ 0x0005, 0x00d6, 0x080c, 0x894e, 0x00de, 0x080c, 0xe6a0, 0x080c,
+ 0xacb0, 0x0005, 0x9182, 0x0040, 0x0002, 0xdcf1, 0xdcf1, 0xdcf1,
+ 0xdcf1, 0xdcf1, 0xdcf1, 0xdcf1, 0xdcf1, 0xdcf1, 0xdcf3, 0xdd1e,
+ 0xdcf1, 0xdcf1, 0xdcf1, 0xdcf1, 0xdd1e, 0xdcf1, 0xdcf1, 0xdcf1,
+ 0xdcf1, 0x080c, 0x0d7d, 0x6014, 0x0096, 0x2048, 0xa87c, 0xd0fc,
+ 0x0168, 0x908c, 0x0003, 0x918e, 0x0002, 0x0180, 0x6144, 0xd1e4,
+ 0x1168, 0x2009, 0x0041, 0x009e, 0x0804, 0xddde, 0x6003, 0x0007,
+ 0x601b, 0x0000, 0x080c, 0x894e, 0x009e, 0x0005, 0x6014, 0x2048,
+ 0xa97c, 0xd1ec, 0x1130, 0x080c, 0x894e, 0x080c, 0xacb0, 0x009e,
+ 0x0005, 0x080c, 0xe648, 0x0db8, 0x009e, 0x0005, 0x2001, 0x180c,
+ 0x200c, 0xc1d4, 0x2102, 0x0036, 0x080c, 0x96d5, 0x080c, 0x9738,
+ 0x6014, 0x0096, 0x2048, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be,
+ 0xd0bc, 0x0188, 0xa87c, 0x9084, 0x0003, 0x9086, 0x0002, 0x0140,
+ 0xa8ac, 0x6330, 0x931a, 0x6332, 0xa8b0, 0x632c, 0x931b, 0x632e,
+ 0x6003, 0x0002, 0x0080, 0x2019, 0x0004, 0x080c, 0xe3e8, 0x6018,
+ 0x9005, 0x1128, 0x2001, 0x1986, 0x2004, 0x8003, 0x601a, 0x6017,
+ 0x0000, 0x6003, 0x0007, 0x009e, 0x003e, 0x0005, 0x9182, 0x0040,
+ 0x0002, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d,
+ 0xdd6d, 0xdd6f, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d,
+ 0xdd6d, 0xdd6d, 0xdd6d, 0xdd6d, 0xddba, 0x080c, 0x0d7d, 0x6014,
+ 0x0096, 0x2048, 0xa834, 0xaa38, 0x6110, 0x00b6, 0x2158, 0xb900,
+ 0x00be, 0xd1bc, 0x1190, 0x920d, 0x1518, 0xa87c, 0xd0fc, 0x0128,
+ 0x2009, 0x0041, 0x009e, 0x0804, 0xddde, 0x6003, 0x0007, 0x601b,
+ 0x0000, 0x080c, 0x894e, 0x009e, 0x0005, 0x6124, 0xd1f4, 0x1d58,
+ 0x0006, 0x0046, 0xacac, 0x9422, 0xa9b0, 0x2200, 0x910b, 0x6030,
+ 0x9420, 0x6432, 0x602c, 0x9109, 0x612e, 0x004e, 0x000e, 0x08d8,
+ 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1bc, 0x1178, 0x2009,
+ 0x180e, 0x210c, 0xd19c, 0x0118, 0x6003, 0x0007, 0x0010, 0x6003,
+ 0x0006, 0x00e9, 0x080c, 0x8950, 0x009e, 0x0005, 0x6003, 0x0002,
+ 0x009e, 0x0005, 0x6024, 0xd0f4, 0x0128, 0x080c, 0x1697, 0x1904,
+ 0xdd6f, 0x0005, 0x6014, 0x0096, 0x2048, 0xa834, 0xa938, 0x009e,
+ 0x9105, 0x1120, 0x080c, 0x1697, 0x1904, 0xdd6f, 0x0005, 0xd2fc,
+ 0x0140, 0x8002, 0x8000, 0x8212, 0x9291, 0x0000, 0x2009, 0x0009,
+ 0x0010, 0x2009, 0x0015, 0xaa9a, 0xa896, 0x0005, 0x9182, 0x0040,
+ 0x0208, 0x0062, 0x9186, 0x0013, 0x0120, 0x9186, 0x0014, 0x190c,
+ 0x0d7d, 0x6024, 0xd0dc, 0x090c, 0x0d7d, 0x0005, 0xde02, 0xde0e,
+ 0xde1a, 0xde26, 0xde02, 0xde02, 0xde02, 0xde02, 0xde09, 0xde04,
+ 0xde04, 0xde02, 0xde02, 0xde02, 0xde02, 0xde04, 0xde02, 0xde04,
+ 0xde02, 0xde09, 0x080c, 0x0d7d, 0x6024, 0xd0dc, 0x090c, 0x0d7d,
+ 0x0005, 0x6014, 0x9005, 0x190c, 0x0d7d, 0x0005, 0x6003, 0x0001,
+ 0x6106, 0x0126, 0x2091, 0x8000, 0x2009, 0xa022, 0x080c, 0x9292,
+ 0x012e, 0x0005, 0x6003, 0x0004, 0x6106, 0x0126, 0x2091, 0x8000,
+ 0x2009, 0xa001, 0x080c, 0x92b0, 0x012e, 0x0005, 0x6003, 0x0003,
+ 0x6106, 0x080c, 0x1c59, 0x0126, 0x2091, 0x8000, 0x6014, 0x0096,
+ 0x2048, 0xa87c, 0xd0fc, 0x0188, 0x9084, 0x0003, 0x9086, 0x0002,
+ 0x01a0, 0x6024, 0xd0cc, 0x1148, 0xd0c4, 0x1138, 0xa8a8, 0x9005,
+ 0x1120, 0x6144, 0x918d, 0xb035, 0x0018, 0x6144, 0x918d, 0xa035,
+ 0x009e, 0x080c, 0x92f7, 0x012e, 0x0005, 0x6144, 0x918d, 0xa032,
+ 0x0cb8, 0x0126, 0x2091, 0x8000, 0x0036, 0x0096, 0x9182, 0x0040,
+ 0x0023, 0x009e, 0x003e, 0x012e, 0x0005, 0xde71, 0xde73, 0xde88,
+ 0xdea2, 0xde71, 0xde71, 0xde71, 0xde71, 0xde71, 0xde71, 0xde71,
+ 0xde71, 0xde71, 0xde71, 0xde71, 0xde71, 0xde71, 0xde71, 0xde71,
+ 0xde71, 0x080c, 0x0d7d, 0x6014, 0x2048, 0xa87c, 0xd0fc, 0x0510,
+ 0x909c, 0x0003, 0x939e, 0x0003, 0x01e8, 0x6003, 0x0001, 0x6106,
+ 0x0126, 0x2091, 0x8000, 0x2009, 0xa022, 0x080c, 0x92b0, 0x0470,
+ 0x6014, 0x2048, 0xa87c, 0xd0fc, 0x0168, 0x909c, 0x0003, 0x939e,
+ 0x0003, 0x0140, 0x6003, 0x0001, 0x6106, 0x2009, 0xa001, 0x080c,
+ 0x92b0, 0x00e0, 0x901e, 0x6316, 0x631a, 0x2019, 0x0004, 0x080c,
+ 0xe3e8, 0x00a0, 0x6014, 0x2048, 0xa87c, 0xd0fc, 0x0d98, 0x909c,
+ 0x0003, 0x939e, 0x0003, 0x0d70, 0x6003, 0x0003, 0x6106, 0x080c,
+ 0x1c59, 0x6144, 0x918d, 0xa035, 0x080c, 0x92f7, 0x0005, 0x080c,
+ 0x967a, 0x6114, 0x81ff, 0x0158, 0x0096, 0x2148, 0x080c, 0xe79d,
+ 0x0036, 0x2019, 0x0029, 0x080c, 0xe3e8, 0x003e, 0x009e, 0x080c,
+ 0xaceb, 0x080c, 0x9738, 0x0005, 0x080c, 0x96d5, 0x6114, 0x81ff,
+ 0x0158, 0x0096, 0x2148, 0x080c, 0xe79d, 0x0036, 0x2019, 0x0029,
+ 0x080c, 0xe3e8, 0x003e, 0x009e, 0x080c, 0xaceb, 0x0005, 0x9182,
+ 0x0085, 0x0002, 0xdef1, 0xdeef, 0xdeef, 0xdefd, 0xdeef, 0xdeef,
+ 0xdeef, 0xdeef, 0xdeef, 0xdeef, 0xdeef, 0xdeef, 0xdeef, 0x080c,
+ 0x0d7d, 0x6003, 0x000b, 0x6106, 0x0126, 0x2091, 0x8000, 0x2009,
+ 0x8020, 0x080c, 0x92b0, 0x012e, 0x0005, 0x0026, 0x00e6, 0x080c,
+ 0xe63f, 0x0118, 0x080c, 0xacb0, 0x0440, 0x2071, 0x0260, 0x7224,
+ 0x6216, 0x2001, 0x180e, 0x2004, 0xd0e4, 0x0150, 0x6010, 0x00b6,
+ 0x2058, 0xbca0, 0x00be, 0x2c00, 0x2011, 0x014e, 0x080c, 0xafdb,
+ 0x7220, 0x080c, 0xe27e, 0x0118, 0x6007, 0x0086, 0x0040, 0x6007,
+ 0x0087, 0x7224, 0x9296, 0xffff, 0x1110, 0x6007, 0x0086, 0x6003,
+ 0x0001, 0x2009, 0x8020, 0x080c, 0x92b0, 0x00ee, 0x002e, 0x0005,
+ 0x9186, 0x0013, 0x1160, 0x6004, 0x908a, 0x0085, 0x0a0c, 0x0d7d,
+ 0x908a, 0x0092, 0x1a0c, 0x0d7d, 0x9082, 0x0085, 0x00a2, 0x9186,
+ 0x0027, 0x0130, 0x9186, 0x0014, 0x0118, 0x080c, 0xad6a, 0x0050,
+ 0x2001, 0x0007, 0x080c, 0x660f, 0x080c, 0x967a, 0x080c, 0xaceb,
+ 0x080c, 0x9738, 0x0005, 0xdf60, 0xdf62, 0xdf62, 0xdf60, 0xdf60,
+ 0xdf60, 0xdf60, 0xdf60, 0xdf60, 0xdf60, 0xdf60, 0xdf60, 0xdf60,
+ 0x080c, 0x0d7d, 0x080c, 0xaceb, 0x080c, 0x9738, 0x0005, 0x9182,
+ 0x0085, 0x0a0c, 0x0d7d, 0x9182, 0x0092, 0x1a0c, 0x0d7d, 0x9182,
+ 0x0085, 0x0002, 0xdf7f, 0xdf7f, 0xdf7f, 0xdf81, 0xdf7f, 0xdf7f,
+ 0xdf7f, 0xdf7f, 0xdf7f, 0xdf7f, 0xdf7f, 0xdf7f, 0xdf7f, 0x080c,
+ 0x0d7d, 0x0005, 0x9186, 0x0013, 0x0148, 0x9186, 0x0014, 0x0130,
+ 0x9186, 0x0027, 0x0118, 0x080c, 0xad6a, 0x0020, 0x080c, 0x967a,
+ 0x080c, 0xaceb, 0x0005, 0x0036, 0x080c, 0xe6a0, 0x604b, 0x0000,
+ 0x2019, 0x000b, 0x0031, 0x6023, 0x0006, 0x6003, 0x0007, 0x003e,
+ 0x0005, 0x0126, 0x0036, 0x2091, 0x8000, 0x080c, 0xa91e, 0x0106,
+ 0x0086, 0x2c40, 0x0096, 0x904e, 0x080c, 0xa28c, 0x009e, 0x008e,
+ 0x1558, 0x0076, 0x2c38, 0x080c, 0xa337, 0x007e, 0x1528, 0x6000,
+ 0x9086, 0x0000, 0x0508, 0x6020, 0x9086, 0x0007, 0x01e8, 0x0096,
+ 0x601c, 0xd084, 0x0140, 0x080c, 0xe6a0, 0x080c, 0xd0b3, 0x080c,
+ 0x1ac5, 0x6023, 0x0007, 0x6014, 0x2048, 0x080c, 0xc97a, 0x0110,
+ 0x080c, 0xe3e8, 0x009e, 0x9006, 0x6046, 0x6016, 0x080c, 0xe6a0,
+ 0x6023, 0x0007, 0x080c, 0xd0b3, 0x010e, 0x090c, 0xa93a, 0x003e,
+ 0x012e, 0x0005, 0x00f6, 0x00c6, 0x00b6, 0x0036, 0x0156, 0x2079,
+ 0x0260, 0x7938, 0x783c, 0x080c, 0x2661, 0x15e8, 0x0016, 0x00c6,
+ 0x080c, 0x6693, 0x15b0, 0x001e, 0x00c6, 0x2160, 0x080c, 0xd0b0,
+ 0x00ce, 0x002e, 0x0026, 0x0016, 0x080c, 0xa91e, 0x2019, 0x0029,
+ 0x080c, 0xa404, 0x080c, 0x943d, 0x0076, 0x903e, 0x080c, 0x9306,
+ 0x007e, 0x001e, 0x0076, 0x903e, 0x080c, 0xe167, 0x007e, 0x080c,
+ 0xa93a, 0x0026, 0xba04, 0x9294, 0xff00, 0x8217, 0x9286, 0x0006,
+ 0x0118, 0x9286, 0x0004, 0x1118, 0xbaa0, 0x080c, 0x330b, 0x002e,
+ 0xbc84, 0x001e, 0x080c, 0x60ac, 0xbe12, 0xbd16, 0xbc86, 0x9006,
+ 0x0010, 0x00ce, 0x001e, 0x015e, 0x003e, 0x00be, 0x00ce, 0x00fe,
+ 0x0005, 0x00c6, 0x00d6, 0x00b6, 0x0016, 0x2009, 0x1824, 0x2104,
+ 0x9086, 0x0074, 0x1904, 0xe08a, 0x2069, 0x0260, 0x6944, 0x9182,
+ 0x0100, 0x06e0, 0x6940, 0x9184, 0x8000, 0x0904, 0xe087, 0x2001,
+ 0x197b, 0x2004, 0x9005, 0x1140, 0x6010, 0x2058, 0xb884, 0x9005,
+ 0x0118, 0x9184, 0x0800, 0x0598, 0x6948, 0x918a, 0x0001, 0x0648,
+ 0x080c, 0xe805, 0x0118, 0x6978, 0xd1fc, 0x11b8, 0x2009, 0x0205,
+ 0x200b, 0x0001, 0x693c, 0x81ff, 0x1198, 0x6944, 0x9182, 0x0100,
+ 0x02a8, 0x6940, 0x81ff, 0x1178, 0x6948, 0x918a, 0x0001, 0x0288,
+ 0x6950, 0x918a, 0x0001, 0x0298, 0x00d0, 0x6017, 0x0100, 0x00a0,
+ 0x6017, 0x0300, 0x0088, 0x6017, 0x0500, 0x0070, 0x6017, 0x0700,
+ 0x0058, 0x6017, 0x0900, 0x0040, 0x6017, 0x0b00, 0x0028, 0x6017,
+ 0x0f00, 0x0010, 0x6017, 0x2d00, 0x9085, 0x0001, 0x0008, 0x9006,
+ 0x001e, 0x00be, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00b6, 0x0026,
+ 0x0036, 0x0156, 0x6210, 0x2258, 0xbb04, 0x9394, 0x00ff, 0x9286,
+ 0x0006, 0x0180, 0x9286, 0x0004, 0x0168, 0x9394, 0xff00, 0x8217,
+ 0x9286, 0x0006, 0x0138, 0x9286, 0x0004, 0x0120, 0x080c, 0x66a2,
+ 0x0804, 0xe0f6, 0x2011, 0x0276, 0x20a9, 0x0004, 0x0096, 0x2b48,
+ 0x2019, 0x000a, 0x080c, 0xbca2, 0x009e, 0x15c8, 0x2011, 0x027a,
+ 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xbca2,
+ 0x009e, 0x1568, 0x0046, 0x0016, 0xbaa0, 0x2220, 0x9006, 0x2009,
+ 0x1848, 0x210c, 0xd1a4, 0x0138, 0x2009, 0x0029, 0x080c, 0xe445,
+ 0xb800, 0xc0e5, 0xb802, 0x080c, 0xa91e, 0x2019, 0x0029, 0x080c,
+ 0x943d, 0x0076, 0x2039, 0x0000, 0x080c, 0x9306, 0x2c08, 0x080c,
+ 0xe167, 0x007e, 0x080c, 0xa93a, 0x2001, 0x0007, 0x080c, 0x660f,
+ 0x2001, 0x0007, 0x080c, 0x65e3, 0x001e, 0x004e, 0x9006, 0x015e,
+ 0x003e, 0x002e, 0x00be, 0x00ce, 0x0005, 0x00d6, 0x2069, 0x026e,
+ 0x6800, 0x9086, 0x0800, 0x0118, 0x6017, 0x0000, 0x0008, 0x9006,
+ 0x00de, 0x0005, 0x00b6, 0x00f6, 0x0016, 0x0026, 0x0036, 0x0156,
+ 0x2079, 0x026c, 0x7930, 0x7834, 0x080c, 0x2661, 0x11d0, 0x080c,
+ 0x6693, 0x11b8, 0x2011, 0x0270, 0x20a9, 0x0004, 0x0096, 0x2b48,
+ 0x2019, 0x000a, 0x080c, 0xbca2, 0x009e, 0x1158, 0x2011, 0x0274,
+ 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xbca2,
+ 0x009e, 0x015e, 0x003e, 0x002e, 0x001e, 0x00fe, 0x00be, 0x0005,
+ 0x00b6, 0x0006, 0x0016, 0x0026, 0x0036, 0x0156, 0x2011, 0x0263,
+ 0x2204, 0x8211, 0x220c, 0x080c, 0x2661, 0x11d0, 0x080c, 0x6693,
+ 0x11b8, 0x2011, 0x0276, 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019,
+ 0x000a, 0x080c, 0xbca2, 0x009e, 0x1158, 0x2011, 0x027a, 0x20a9,
+ 0x0004, 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xbca2, 0x009e,
+ 0x015e, 0x003e, 0x002e, 0x001e, 0x000e, 0x00be, 0x0005, 0x00e6,
+ 0x00c6, 0x0086, 0x0076, 0x0066, 0x0056, 0x0046, 0x0026, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0xa97c, 0x0106, 0x190c, 0xa91e, 0x2740,
+ 0x2029, 0x19f2, 0x252c, 0x2021, 0x19f9, 0x2424, 0x2061, 0x1ddc,
+ 0x2071, 0x1800, 0x7654, 0x7074, 0x81ff, 0x0150, 0x0006, 0x9186,
+ 0x1b34, 0x000e, 0x0128, 0x8001, 0x9602, 0x1a04, 0xe20c, 0x0018,
+ 0x9606, 0x0904, 0xe20c, 0x080c, 0x8c1f, 0x0904, 0xe203, 0x2100,
+ 0x9c06, 0x0904, 0xe203, 0x080c, 0xe486, 0x1904, 0xe203, 0x080c,
+ 0xe822, 0x0904, 0xe203, 0x080c, 0xe476, 0x0904, 0xe203, 0x6720,
+ 0x9786, 0x0001, 0x1148, 0x080c, 0x33a8, 0x0904, 0xe24e, 0x6004,
+ 0x9086, 0x0000, 0x1904, 0xe24e, 0x9786, 0x0004, 0x0904, 0xe24e,
+ 0x9786, 0x0007, 0x0904, 0xe203, 0x2500, 0x9c06, 0x0904, 0xe203,
+ 0x2400, 0x9c06, 0x0904, 0xe203, 0x88ff, 0x0118, 0x605c, 0x9906,
+ 0x15d0, 0x0096, 0x6043, 0xffff, 0x6000, 0x9086, 0x0004, 0x1120,
+ 0x0016, 0x080c, 0x1ac5, 0x001e, 0x9786, 0x000a, 0x0148, 0x080c,
+ 0xcb91, 0x1130, 0x080c, 0xb693, 0x009e, 0x080c, 0xaceb, 0x0418,
+ 0x6014, 0x2048, 0x080c, 0xc97a, 0x01d8, 0x9786, 0x0003, 0x1588,
+ 0xa867, 0x0103, 0xa87c, 0xd0cc, 0x0130, 0x0096, 0xa878, 0x2048,
+ 0x080c, 0x0ff9, 0x009e, 0xab7a, 0xa877, 0x0000, 0x080c, 0xe79d,
+ 0x0016, 0x080c, 0xcc7f, 0x080c, 0x6de2, 0x001e, 0x080c, 0xcb6b,
+ 0x009e, 0x080c, 0xaceb, 0x9ce0, 0x001c, 0x2001, 0x181a, 0x2004,
+ 0x9c02, 0x1210, 0x0804, 0xe180, 0x010e, 0x190c, 0xa93a, 0x012e,
+ 0x002e, 0x004e, 0x005e, 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee,
+ 0x0005, 0x9786, 0x0006, 0x1150, 0x9386, 0x0005, 0x0128, 0x080c,
+ 0xe79d, 0x080c, 0xe3e8, 0x08e0, 0x009e, 0x08e8, 0x9786, 0x0009,
+ 0x11f8, 0x6000, 0x9086, 0x0004, 0x01c0, 0x6000, 0x9086, 0x0003,
+ 0x11a0, 0x080c, 0x96d5, 0x0096, 0x6114, 0x2148, 0x080c, 0xc97a,
+ 0x0118, 0x6010, 0x080c, 0x6dee, 0x009e, 0x00c6, 0x080c, 0xacb0,
+ 0x00ce, 0x0036, 0x080c, 0x9738, 0x003e, 0x009e, 0x0804, 0xe203,
+ 0x9786, 0x000a, 0x0904, 0xe1f3, 0x0804, 0xe1e8, 0x81ff, 0x0904,
+ 0xe203, 0x9180, 0x0001, 0x2004, 0x9086, 0x0018, 0x0138, 0x9180,
+ 0x0001, 0x2004, 0x9086, 0x002d, 0x1904, 0xe203, 0x6000, 0x9086,
+ 0x0002, 0x1904, 0xe203, 0x080c, 0xcb80, 0x0138, 0x080c, 0xcb91,
+ 0x1904, 0xe203, 0x080c, 0xb693, 0x0038, 0x080c, 0x326f, 0x080c,
+ 0xcb91, 0x1110, 0x080c, 0xb693, 0x080c, 0xaceb, 0x0804, 0xe203,
+ 0xa864, 0x9084, 0x00ff, 0x9086, 0x0039, 0x0005, 0x00c6, 0x00e6,
+ 0x0016, 0x2c08, 0x2170, 0x9006, 0x080c, 0xe40f, 0x001e, 0x0120,
+ 0x6020, 0x9084, 0x000f, 0x001b, 0x00ee, 0x00ce, 0x0005, 0xe29d,
+ 0xe29d, 0xe29d, 0xe29d, 0xe29d, 0xe29d, 0xe29f, 0xe29d, 0xe29d,
+ 0xe29d, 0xe29d, 0xaceb, 0xaceb, 0xe29d, 0x9006, 0x0005, 0x0036,
+ 0x0046, 0x0016, 0x7010, 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00,
+ 0x2009, 0x0020, 0x080c, 0xe445, 0x001e, 0x004e, 0x2019, 0x0002,
+ 0x080c, 0xdfa1, 0x003e, 0x9085, 0x0001, 0x0005, 0x0096, 0x080c,
+ 0xc97a, 0x0140, 0x6014, 0x904d, 0x080c, 0xc566, 0x687b, 0x0005,
+ 0x080c, 0x6dee, 0x009e, 0x080c, 0xaceb, 0x9085, 0x0001, 0x0005,
+ 0x2001, 0x0001, 0x080c, 0x65cf, 0x0156, 0x0016, 0x0026, 0x0036,
+ 0x20a9, 0x0004, 0x2019, 0x1805, 0x2011, 0x0276, 0x080c, 0xbc8e,
+ 0x003e, 0x002e, 0x001e, 0x015e, 0x9005, 0x0005, 0x00f6, 0x00e6,
+ 0x00c6, 0x0086, 0x0076, 0x0066, 0x00b6, 0x0126, 0x2091, 0x8000,
+ 0x2740, 0x2061, 0x1ddc, 0x2079, 0x0001, 0x8fff, 0x0904, 0xe338,
+ 0x2071, 0x1800, 0x7654, 0x7074, 0x8001, 0x9602, 0x1a04, 0xe338,
+ 0x88ff, 0x0120, 0x2800, 0x9c06, 0x1590, 0x2078, 0x080c, 0xe476,
+ 0x0570, 0x2400, 0x9c06, 0x0558, 0x6720, 0x9786, 0x0006, 0x1538,
+ 0x9786, 0x0007, 0x0520, 0x88ff, 0x1140, 0x6010, 0x9b06, 0x11f8,
+ 0x85ff, 0x0118, 0x605c, 0x9106, 0x11d0, 0x0096, 0x601c, 0xd084,
+ 0x0140, 0x080c, 0xe6a0, 0x080c, 0xd0b3, 0x080c, 0x1ac5, 0x6023,
+ 0x0007, 0x6014, 0x2048, 0x080c, 0xc97a, 0x0120, 0x0046, 0x080c,
+ 0xe3e8, 0x004e, 0x009e, 0x080c, 0xaceb, 0x88ff, 0x1198, 0x9ce0,
+ 0x001c, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1210, 0x0804, 0xe2ed,
+ 0x9006, 0x012e, 0x00be, 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee,
+ 0x00fe, 0x0005, 0x98c5, 0x0001, 0x0ca0, 0x080c, 0xa91e, 0x00b6,
+ 0x0076, 0x0056, 0x0086, 0x9046, 0x2029, 0x0001, 0x2c20, 0x2019,
+ 0x0002, 0x6210, 0x2258, 0x0096, 0x904e, 0x080c, 0xa28c, 0x009e,
+ 0x008e, 0x903e, 0x080c, 0xa337, 0x080c, 0xe2de, 0x005e, 0x007e,
+ 0x00be, 0x080c, 0xa93a, 0x0005, 0x080c, 0xa91e, 0x00b6, 0x0046,
+ 0x0056, 0x0076, 0x00c6, 0x0156, 0x2c20, 0x2128, 0x20a9, 0x007f,
+ 0x900e, 0x0016, 0x0036, 0x080c, 0x6693, 0x1190, 0x0056, 0x0086,
+ 0x9046, 0x2508, 0x2029, 0x0001, 0x0096, 0x904e, 0x080c, 0xa28c,
+ 0x009e, 0x008e, 0x903e, 0x080c, 0xa337, 0x080c, 0xe2de, 0x005e,
+ 0x003e, 0x001e, 0x8108, 0x1f04, 0xe371, 0x015e, 0x00ce, 0x007e,
+ 0x005e, 0x004e, 0x00be, 0x080c, 0xa93a, 0x0005, 0x080c, 0xa91e,
+ 0x00b6, 0x0076, 0x0056, 0x6210, 0x2258, 0x0086, 0x9046, 0x2029,
+ 0x0001, 0x2019, 0x0048, 0x0096, 0x904e, 0x080c, 0xa28c, 0x009e,
+ 0x008e, 0x903e, 0x080c, 0xa337, 0x2c20, 0x080c, 0xe2de, 0x005e,
+ 0x007e, 0x00be, 0x080c, 0xa93a, 0x0005, 0x080c, 0xa91e, 0x00b6,
+ 0x0046, 0x0056, 0x0076, 0x00c6, 0x0156, 0x2c20, 0x20a9, 0x0800,
+ 0x900e, 0x0016, 0x0036, 0x080c, 0x6693, 0x11a0, 0x0086, 0x9046,
+ 0x2828, 0x0046, 0x2021, 0x0001, 0x080c, 0xe684, 0x004e, 0x0096,
+ 0x904e, 0x080c, 0xa28c, 0x009e, 0x008e, 0x903e, 0x080c, 0xa337,
+ 0x080c, 0xe2de, 0x003e, 0x001e, 0x8108, 0x1f04, 0xe3c1, 0x015e,
+ 0x00ce, 0x007e, 0x005e, 0x004e, 0x00be, 0x080c, 0xa93a, 0x0005,
+ 0x0016, 0x00f6, 0x080c, 0xc978, 0x0198, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0046, 0x0180, 0xa800, 0x907d, 0x0138, 0xa803, 0x0000,
+ 0xab82, 0x080c, 0x6dee, 0x2f48, 0x0cb0, 0xab82, 0x080c, 0x6dee,
+ 0x00fe, 0x001e, 0x0005, 0xa800, 0x907d, 0x0130, 0xa803, 0x0000,
+ 0x080c, 0x6dee, 0x2f48, 0x0cb8, 0x080c, 0x6dee, 0x0c88, 0x00e6,
+ 0x0046, 0x0036, 0x2061, 0x1ddc, 0x9005, 0x1138, 0x2071, 0x1800,
+ 0x7454, 0x7074, 0x8001, 0x9402, 0x12f8, 0x2100, 0x9c06, 0x0188,
+ 0x6000, 0x9086, 0x0000, 0x0168, 0x6008, 0x9206, 0x1150, 0x6320,
+ 0x9386, 0x0009, 0x01b0, 0x6010, 0x91a0, 0x0004, 0x2424, 0x9406,
+ 0x0140, 0x9ce0, 0x001c, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1220,
+ 0x0c20, 0x9085, 0x0001, 0x0008, 0x9006, 0x003e, 0x004e, 0x00ee,
+ 0x0005, 0x631c, 0xd3c4, 0x1d68, 0x0c30, 0x0096, 0x0006, 0x080c,
+ 0x1047, 0x000e, 0x090c, 0x0d7d, 0xaae2, 0xa867, 0x010d, 0xa88e,
+ 0x0026, 0x2010, 0x080c, 0xc968, 0x2001, 0x0000, 0x0120, 0x2200,
+ 0x9080, 0x0017, 0x2004, 0x002e, 0xa87a, 0x9186, 0x0020, 0x0110,
+ 0xa8e3, 0xffff, 0xa986, 0xac76, 0xa87f, 0x0000, 0x2001, 0x198d,
+ 0x2004, 0xa882, 0x9006, 0xa802, 0xa86a, 0xa88a, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6dee, 0x012e, 0x009e, 0x0005, 0x6700, 0x9786,
+ 0x0000, 0x0158, 0x9786, 0x0001, 0x0140, 0x9786, 0x000a, 0x0128,
+ 0x9786, 0x0009, 0x0110, 0x9085, 0x0001, 0x0005, 0x00e6, 0x6010,
+ 0x9075, 0x0138, 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x9206, 0x00ee,
+ 0x0005, 0x9085, 0x0001, 0x0cd8, 0x0016, 0x6004, 0x908e, 0x001e,
+ 0x11a0, 0x8007, 0x6134, 0x918c, 0x00ff, 0x9105, 0x6036, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x6023, 0x0005, 0x2001, 0x1986, 0x2004,
+ 0x601a, 0x2009, 0x8020, 0x080c, 0x92b0, 0x001e, 0x0005, 0xa001,
+ 0xa001, 0x0005, 0x6024, 0xd0e4, 0x0158, 0xd0cc, 0x0118, 0x080c,
+ 0xccc6, 0x0030, 0x080c, 0xe6a0, 0x080c, 0x894e, 0x080c, 0xacb0,
+ 0x0005, 0x9280, 0x0008, 0x2004, 0x9084, 0x000f, 0x0002, 0xe4d5,
+ 0xe4d5, 0xe4d5, 0xe4d7, 0xe4d5, 0xe4d7, 0xe4d7, 0xe4d5, 0xe4d7,
+ 0xe4d5, 0xe4d5, 0xe4d5, 0xe4d5, 0xe4d5, 0x9006, 0x0005, 0x9085,
+ 0x0001, 0x0005, 0x9280, 0x0008, 0x2004, 0x9084, 0x000f, 0x0002,
+ 0xe4ee, 0xe4ee, 0xe4ee, 0xe4ee, 0xe4ee, 0xe4ee, 0xe4fb, 0xe4ee,
+ 0xe4ee, 0xe4ee, 0xe4ee, 0xe4ee, 0xe4ee, 0xe4ee, 0x6007, 0x003b,
+ 0x602f, 0x0009, 0x6017, 0x2a00, 0x6003, 0x0001, 0x2009, 0x8020,
+ 0x080c, 0x92b0, 0x0005, 0x0096, 0x00c6, 0x2260, 0x080c, 0xe6a0,
+ 0x604b, 0x0000, 0x6024, 0xc0f4, 0xc0e4, 0x6026, 0x603b, 0x0000,
+ 0x00ce, 0x00d6, 0x2268, 0x9186, 0x0007, 0x1904, 0xe554, 0x6814,
+ 0x9005, 0x0138, 0x2048, 0xa87c, 0xd0fc, 0x1118, 0x00de, 0x009e,
+ 0x08a8, 0x6007, 0x003a, 0x6003, 0x0001, 0x2009, 0x8020, 0x080c,
+ 0x92b0, 0x00c6, 0x2d60, 0x6100, 0x9186, 0x0002, 0x1904, 0xe5cb,
+ 0x6014, 0x9005, 0x1138, 0x6000, 0x9086, 0x0007, 0x190c, 0x0d7d,
+ 0x0804, 0xe5cb, 0x2048, 0x080c, 0xc97a, 0x1130, 0x0028, 0x2048,
+ 0xa800, 0x9005, 0x1de0, 0x2900, 0x2048, 0xa87c, 0x9084, 0x0003,
+ 0x9086, 0x0002, 0x1168, 0xa87c, 0xc0dc, 0xc0f4, 0xa87e, 0xa880,
+ 0xc0fc, 0xa882, 0x2009, 0x0043, 0x080c, 0xddde, 0x0804, 0xe5cb,
+ 0x2009, 0x0041, 0x0804, 0xe5c5, 0x9186, 0x0005, 0x15a0, 0x6814,
+ 0x2048, 0xa87c, 0xd0bc, 0x1120, 0x00de, 0x009e, 0x0804, 0xe4ee,
+ 0xd0b4, 0x0128, 0xd0fc, 0x090c, 0x0d7d, 0x0804, 0xe50f, 0x6007,
+ 0x003a, 0x6003, 0x0001, 0x2009, 0x8020, 0x080c, 0x92b0, 0x00c6,
+ 0x2d60, 0x6100, 0x9186, 0x0002, 0x0120, 0x9186, 0x0004, 0x1904,
+ 0xe5cb, 0x6814, 0x2048, 0xa97c, 0xc1f4, 0xc1dc, 0xa97e, 0xa980,
+ 0xc1fc, 0xc1bc, 0xa982, 0x00f6, 0x2c78, 0x080c, 0x1778, 0x00fe,
+ 0x2009, 0x0042, 0x04d0, 0x0036, 0x080c, 0x1047, 0x090c, 0x0d7d,
+ 0xa867, 0x010d, 0x9006, 0xa802, 0xa86a, 0xa88a, 0x2d18, 0xab8e,
+ 0xa887, 0x0045, 0x2c00, 0xa892, 0x6038, 0xa8a2, 0x2360, 0x6024,
+ 0xc0dd, 0x6026, 0x6010, 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x2004,
+ 0x635c, 0xab7a, 0xa876, 0x9006, 0xa87e, 0xa882, 0xad9a, 0xae96,
+ 0xa89f, 0x0001, 0x080c, 0x6dee, 0x2019, 0x0045, 0x6008, 0x2068,
+ 0x080c, 0xdfa1, 0x2d00, 0x600a, 0x6023, 0x0006, 0x6003, 0x0007,
+ 0x901e, 0x631a, 0x634a, 0x003e, 0x0038, 0x604b, 0x0000, 0x6003,
+ 0x0007, 0x080c, 0xddde, 0x00ce, 0x00de, 0x009e, 0x0005, 0x9186,
+ 0x0013, 0x1128, 0x6004, 0x9082, 0x0085, 0x2008, 0x00c2, 0x9186,
+ 0x0027, 0x1178, 0x080c, 0x967a, 0x0036, 0x0096, 0x6014, 0x2048,
+ 0x2019, 0x0004, 0x080c, 0xe3e8, 0x009e, 0x003e, 0x080c, 0x9738,
+ 0x0005, 0x9186, 0x0014, 0x0d70, 0x080c, 0xad6a, 0x0005, 0xe5fe,
+ 0xe5fc, 0xe5fc, 0xe5fc, 0xe5fc, 0xe5fc, 0xe5fe, 0xe5fc, 0xe5fc,
+ 0xe5fc, 0xe5fc, 0xe5fc, 0xe5fc, 0x080c, 0x0d7d, 0x6003, 0x000c,
+ 0x080c, 0x9738, 0x0005, 0x9182, 0x0092, 0x1220, 0x9182, 0x0085,
+ 0x0208, 0x001a, 0x080c, 0xad6a, 0x0005, 0xe61a, 0xe61a, 0xe61a,
+ 0xe61a, 0xe61c, 0xe63c, 0xe61a, 0xe61a, 0xe61a, 0xe61a, 0xe61a,
+ 0xe61a, 0xe61a, 0x080c, 0x0d7d, 0x00d6, 0x2c68, 0x080c, 0xac5a,
+ 0x01b0, 0x6003, 0x0001, 0x6007, 0x001e, 0x2009, 0x026e, 0x210c,
+ 0x613a, 0x2009, 0x026f, 0x210c, 0x613e, 0x600b, 0xffff, 0x6910,
+ 0x6112, 0x6023, 0x0004, 0x2009, 0x8020, 0x080c, 0x92b0, 0x2d60,
+ 0x080c, 0xacb0, 0x00de, 0x0005, 0x080c, 0xacb0, 0x0005, 0x00e6,
+ 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0ec, 0x00ee, 0x0005,
+ 0x2009, 0x1867, 0x210c, 0xd1ec, 0x05b0, 0x6003, 0x0002, 0x6024,
+ 0xc0e5, 0x6026, 0xd0cc, 0x0150, 0x2001, 0x1987, 0x2004, 0x604a,
+ 0x2009, 0x1867, 0x210c, 0xd1f4, 0x1520, 0x00a0, 0x2009, 0x1867,
+ 0x210c, 0xd1f4, 0x0128, 0x6024, 0xc0e4, 0x6026, 0x9006, 0x00d8,
+ 0x2001, 0x1987, 0x200c, 0x2001, 0x1985, 0x2004, 0x9100, 0x9080,
+ 0x000a, 0x604a, 0x6010, 0x00b6, 0x2058, 0xb8bc, 0x00be, 0x0008,
+ 0x2104, 0x9005, 0x0118, 0x9088, 0x0003, 0x0cd0, 0x2c0a, 0x600f,
+ 0x0000, 0x9085, 0x0001, 0x0005, 0x0016, 0x00c6, 0x00e6, 0x615c,
+ 0xb8bc, 0x2060, 0x8cff, 0x0180, 0x84ff, 0x1118, 0x605c, 0x9106,
+ 0x1138, 0x600c, 0x2072, 0x080c, 0x894e, 0x080c, 0xacb0, 0x0010,
+ 0x9cf0, 0x0003, 0x2e64, 0x0c70, 0x00ee, 0x00ce, 0x001e, 0x0005,
+ 0x00d6, 0x00b6, 0x6010, 0x2058, 0xb8bc, 0x2068, 0x9005, 0x0130,
+ 0x9c06, 0x0110, 0x680c, 0x0cd0, 0x600c, 0x680e, 0x00be, 0x00de,
+ 0x0005, 0x0026, 0x0036, 0x0156, 0x2011, 0x182c, 0x2204, 0x9084,
+ 0x00ff, 0x2019, 0x026e, 0x2334, 0x9636, 0x1508, 0x8318, 0x2334,
+ 0x2204, 0x9084, 0xff00, 0x9636, 0x11d0, 0x2011, 0x0270, 0x20a9,
+ 0x0004, 0x6010, 0x0096, 0x2048, 0x2019, 0x000a, 0x080c, 0xbca2,
+ 0x009e, 0x1168, 0x2011, 0x0274, 0x20a9, 0x0004, 0x6010, 0x0096,
+ 0x2048, 0x2019, 0x0006, 0x080c, 0xbca2, 0x009e, 0x1100, 0x015e,
+ 0x003e, 0x002e, 0x0005, 0x00e6, 0x2071, 0x1800, 0x080c, 0x6025,
+ 0x080c, 0x3011, 0x00ee, 0x0005, 0x0096, 0x0026, 0x080c, 0x1047,
+ 0x090c, 0x0d7d, 0xa85c, 0x9080, 0x001a, 0x20a0, 0x20a9, 0x000c,
+ 0xa860, 0x20e8, 0x9006, 0x4004, 0x9186, 0x0046, 0x1118, 0xa867,
+ 0x0136, 0x0038, 0xa867, 0x0138, 0x9186, 0x0041, 0x0110, 0xa87b,
+ 0x0001, 0x7038, 0x9084, 0xff00, 0x7240, 0x9294, 0xff00, 0x8007,
+ 0x9215, 0xaa9a, 0x9186, 0x0046, 0x1168, 0x7038, 0x9084, 0x00ff,
+ 0x723c, 0x9294, 0xff00, 0x9215, 0xaa9e, 0x723c, 0x9294, 0x00ff,
+ 0xaaa2, 0x0060, 0x7040, 0x9084, 0x00ff, 0x7244, 0x9294, 0xff00,
+ 0x9215, 0xaa9e, 0x7244, 0x9294, 0x00ff, 0xaaa2, 0x9186, 0x0046,
+ 0x1118, 0x9e90, 0x0012, 0x0010, 0x9e90, 0x001a, 0x2204, 0x8007,
+ 0xa8a6, 0x8210, 0x2204, 0x8007, 0xa8aa, 0x8210, 0x2204, 0x8007,
+ 0xa8ae, 0x8210, 0x2204, 0x8007, 0xa8b2, 0x8210, 0x9186, 0x0046,
+ 0x11b8, 0x9e90, 0x0016, 0x2204, 0x8007, 0xa8b6, 0x8210, 0x2204,
+ 0x8007, 0xa8ba, 0x8210, 0x2204, 0x8007, 0xa8be, 0x8210, 0x2204,
+ 0x8007, 0xa8c2, 0x8210, 0x2011, 0x0205, 0x2013, 0x0001, 0x00b0,
+ 0x9e90, 0x001e, 0x2204, 0x8007, 0xa8b6, 0x8210, 0x2204, 0x8007,
+ 0xa8ba, 0x2011, 0x0205, 0x2013, 0x0001, 0x2011, 0x0260, 0x2204,
+ 0x8007, 0xa8be, 0x8210, 0x2204, 0x8007, 0xa8c2, 0x9186, 0x0046,
+ 0x1118, 0x2011, 0x0262, 0x0010, 0x2011, 0x026a, 0x0146, 0x01d6,
+ 0x0036, 0x20a9, 0x0001, 0x2019, 0x0008, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0031, 0x20a0, 0x2204, 0x8007, 0x4004, 0x8210, 0x8319,
+ 0x1dd0, 0x003e, 0x01ce, 0x013e, 0x2011, 0x0205, 0x2013, 0x0000,
+ 0x002e, 0x080c, 0x6dee, 0x009e, 0x0005, 0x00e6, 0x6010, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0fc, 0x0108, 0x0011, 0x00ee, 0x0005,
+ 0xa880, 0xc0e5, 0xa882, 0x0005, 0x00e6, 0x00d6, 0x00c6, 0x0076,
+ 0x0066, 0x0056, 0x0046, 0x0026, 0x0016, 0x0126, 0x2091, 0x8000,
+ 0x2029, 0x19f2, 0x252c, 0x2021, 0x19f9, 0x2424, 0x2061, 0x1ddc,
+ 0x2071, 0x1800, 0x7654, 0x7074, 0x9606, 0x0578, 0x6720, 0x9786,
+ 0x0001, 0x0118, 0x9786, 0x0008, 0x1500, 0x2500, 0x9c06, 0x01e8,
+ 0x2400, 0x9c06, 0x01d0, 0x080c, 0xe476, 0x01b8, 0x080c, 0xe486,
+ 0x11a0, 0x6000, 0x9086, 0x0004, 0x1120, 0x0016, 0x080c, 0x1ac5,
+ 0x001e, 0x080c, 0xcb80, 0x1110, 0x080c, 0x326f, 0x080c, 0xcb91,
+ 0x1110, 0x080c, 0xb693, 0x080c, 0xaceb, 0x9ce0, 0x001c, 0x2001,
+ 0x181a, 0x2004, 0x9c02, 0x1208, 0x0858, 0x012e, 0x001e, 0x002e,
+ 0x004e, 0x005e, 0x006e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x0005,
+ 0x2001, 0x1810, 0x2004, 0xd0dc, 0x0005, 0x0006, 0x2001, 0x1837,
+ 0x2004, 0xd09c, 0x000e, 0x0005, 0x0006, 0x0036, 0x0046, 0x080c,
+ 0xd09b, 0x0168, 0x2019, 0xffff, 0x9005, 0x0128, 0x6010, 0x00b6,
+ 0x2058, 0xbba0, 0x00be, 0x2021, 0x0004, 0x080c, 0x4d09, 0x004e,
+ 0x003e, 0x000e, 0x6004, 0x9086, 0x0001, 0x1128, 0x080c, 0xa404,
+ 0x080c, 0xaceb, 0x9006, 0x0005, 0x00e6, 0x00c6, 0x00b6, 0x0046,
+ 0x2061, 0x1ddc, 0x2071, 0x1800, 0x7454, 0x7074, 0x8001, 0x9402,
+ 0x12d8, 0x2100, 0x9c06, 0x0168, 0x6000, 0x9086, 0x0000, 0x0148,
+ 0x6010, 0x2058, 0xb8a0, 0x9206, 0x1120, 0x6004, 0x9086, 0x0002,
+ 0x0140, 0x9ce0, 0x001c, 0x2001, 0x181a, 0x2004, 0x9c02, 0x1220,
+ 0x0c40, 0x9085, 0x0001, 0x0008, 0x9006, 0x004e, 0x00be, 0x00ce,
+ 0x00ee, 0x0005, 0x0126, 0x0006, 0x00e6, 0x0016, 0x2091, 0x8000,
+ 0x2071, 0x1840, 0xd5a4, 0x0118, 0x7004, 0x8000, 0x7006, 0xd5b4,
+ 0x0118, 0x7000, 0x8000, 0x7002, 0xd5ac, 0x0178, 0x2500, 0x9084,
+ 0x0007, 0x908e, 0x0003, 0x0148, 0x908e, 0x0004, 0x0130, 0x908e,
+ 0x0005, 0x0118, 0x2071, 0xfff6, 0x0089, 0x001e, 0x00ee, 0x000e,
+ 0x012e, 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000, 0x2071,
+ 0xffee, 0x0021, 0x00ee, 0x000e, 0x012e, 0x0005, 0x2e05, 0x8000,
+ 0x2077, 0x1220, 0x8e70, 0x2e05, 0x8000, 0x2077, 0x0005, 0x00e6,
+ 0x2071, 0xffec, 0x0c99, 0x00ee, 0x0005, 0x00e6, 0x2071, 0xfff0,
+ 0x0c69, 0x00ee, 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000,
+ 0x2071, 0x1840, 0x7014, 0x8000, 0x7016, 0x00ee, 0x000e, 0x012e,
+ 0x0005, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
+ 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000,
+ 0x8000, 0x3f07
+};
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2322ipx_length01 = 0xe0c2;
+#else
+unsigned short risc_code_length01 = 0xe0c2;
+#endif
+
+/*
+ *
+ */
+
+unsigned long rseqipx_code_addr01 = 0x0001c000 ;
+unsigned short rseqipx_code01[] = {
+0x000b, 0x0003, 0x0000, 0x09e6, 0x0001, 0xc000, 0x0008, 0x8064,
+ 0x0000, 0x0010, 0x0000, 0x8066, 0x0008, 0x0101, 0x0003, 0xc007,
+ 0x0008, 0x80e0, 0x0008, 0xff00, 0x0000, 0x80e2, 0x0008, 0xff00,
+ 0x0008, 0x0162, 0x0000, 0x8066, 0x0008, 0xa101, 0x000b, 0xc00f,
+ 0x0008, 0x0d02, 0x0000, 0x8060, 0x0000, 0x0400, 0x0003, 0x60c2,
+ 0x0003, 0x5817, 0x000b, 0x7ae3, 0x000b, 0x521c, 0x000b, 0xc813,
+ 0x0009, 0xbac0, 0x0000, 0x008a, 0x0003, 0x8813, 0x0000, 0x15fc,
+ 0x000b, 0xb013, 0x0009, 0xc4c0, 0x0000, 0x7000, 0x0001, 0xffa0,
+ 0x0000, 0x2000, 0x0003, 0x939b, 0x0008, 0x808c, 0x0000, 0x0001,
+ 0x0007, 0x0000, 0x0007, 0x0000, 0x0000, 0x40d4, 0x000a, 0x4047,
+ 0x0008, 0x808c, 0x0000, 0x0002, 0x0007, 0x0000, 0x0003, 0x082e,
+ 0x0000, 0x4022, 0x0003, 0x0034, 0x0008, 0x4122, 0x0009, 0xeac0,
+ 0x0008, 0xff00, 0x0009, 0xffe0, 0x0008, 0x0500, 0x000b, 0x0bc2,
+ 0x0002, 0x4447, 0x0003, 0x8bbf, 0x0008, 0x0bfe, 0x0001, 0x11a0,
+ 0x000b, 0x13a1, 0x0001, 0x0ca0, 0x000b, 0x13a1, 0x0001, 0x9180,
+ 0x0000, 0x0004, 0x0000, 0x8060, 0x0000, 0x0400, 0x0008, 0x7f62,
+ 0x0000, 0x8066, 0x0008, 0x0009, 0x000b, 0xc042, 0x0008, 0x808c,
+ 0x0008, 0x0000, 0x0008, 0x0060, 0x0008, 0x8062, 0x0000, 0x0004,
+ 0x0000, 0x8066, 0x0000, 0x0411, 0x0003, 0xc04a, 0x0000, 0x03fe,
+ 0x0001, 0x43e0, 0x0003, 0x8b9e, 0x0009, 0xc2c0, 0x0008, 0x00ff,
+ 0x0001, 0x02e0, 0x0003, 0x8b9e, 0x0001, 0x9180, 0x0008, 0x0005,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0000, 0x0019, 0x000b, 0xc059, 0x0002, 0x0240, 0x000b, 0x0b9b,
+ 0x0008, 0x00fc, 0x0003, 0x339e, 0x000a, 0x0244, 0x000b, 0x086b,
+ 0x000c, 0x01f5, 0x0001, 0x9180, 0x0000, 0x0007, 0x0008, 0x7f62,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0002, 0x0234, 0x0008, 0x7f04,
+ 0x0000, 0x8066, 0x0000, 0x040a, 0x000b, 0xc06a, 0x000a, 0x0248,
+ 0x000b, 0x0875, 0x0001, 0x9180, 0x0008, 0x0006, 0x0008, 0x7f62,
+ 0x0008, 0x8002, 0x0008, 0x0003, 0x0000, 0x8066, 0x0000, 0x020a,
+ 0x000b, 0xc074, 0x0000, 0x112a, 0x0008, 0x002e, 0x0008, 0x022c,
+ 0x0002, 0x3a44, 0x0003, 0x8813, 0x0008, 0x808c, 0x0000, 0x0002,
+ 0x0008, 0x1760, 0x0008, 0x8062, 0x0008, 0x000f, 0x0000, 0x8066,
+ 0x0008, 0x0011, 0x000b, 0xc081, 0x0008, 0x01fe, 0x0009, 0x42e0,
+ 0x000b, 0x8b8e, 0x0000, 0x00fe, 0x0001, 0x43e0, 0x000b, 0x8b8e,
+ 0x0000, 0x1734, 0x0000, 0x1530, 0x0008, 0x1632, 0x0008, 0x0d2a,
+ 0x0001, 0x9880, 0x0008, 0x0012, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x1e0a, 0x000b, 0xc093,
+ 0x0008, 0x808a, 0x0008, 0x0003, 0x0000, 0x1a60, 0x0008, 0x8062,
+ 0x0000, 0x0002, 0x0003, 0x5899, 0x0000, 0x8066, 0x0000, 0x3679,
+ 0x000b, 0xc09c, 0x000b, 0x589d, 0x0008, 0x8054, 0x0008, 0x0011,
+ 0x0000, 0x8074, 0x0008, 0x1010, 0x0008, 0x1efc, 0x0003, 0x3013,
+ 0x0004, 0x00a6, 0x0003, 0x0013, 0x0000, 0x1c60, 0x0000, 0x1b62,
+ 0x0000, 0x8066, 0x0008, 0x0231, 0x000b, 0xc0aa, 0x000b, 0x58ab,
+ 0x0008, 0x0140, 0x0000, 0x0242, 0x0002, 0x1f43, 0x0003, 0x88b5,
+ 0x0000, 0x0d44, 0x0008, 0x0d46, 0x0008, 0x0348, 0x0008, 0x044a,
+ 0x0003, 0x00b9, 0x0008, 0x0344, 0x0008, 0x0446, 0x0008, 0x0548,
+ 0x0000, 0x064a, 0x000a, 0x1948, 0x000b, 0x08bc, 0x0008, 0x0d4a,
+ 0x000b, 0x58bc, 0x0008, 0x8054, 0x0000, 0x0001, 0x0000, 0x8074,
+ 0x0008, 0x2020, 0x000f, 0x4000, 0x0000, 0x4820, 0x0008, 0x0bfe,
+ 0x0009, 0x10a0, 0x0003, 0x1123, 0x0001, 0x0ca0, 0x0003, 0x1123,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0009, 0x9080, 0x0000, 0x0008,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x0009, 0x000b, 0xc0cf,
+ 0x0001, 0x80e0, 0x0008, 0x0003, 0x000b, 0x8923, 0x0000, 0x49b4,
+ 0x0002, 0x4b4e, 0x000b, 0x892c, 0x0008, 0x808a, 0x0000, 0x0004,
+ 0x0000, 0x18fe, 0x0001, 0x10e0, 0x000b, 0x88dd, 0x0002, 0x192f,
+ 0x0008, 0x7f32, 0x0008, 0x15fe, 0x0001, 0x10e0, 0x000b, 0x88e2,
+ 0x0002, 0x162f, 0x0008, 0x7f2c, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0009, 0x9080, 0x0000, 0x0007, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0008, 0x0009, 0x0003, 0xc0e9, 0x000a, 0x004f, 0x000b, 0x891a,
+ 0x000a, 0x0040, 0x0003, 0x0904, 0x0002, 0x004e, 0x0003, 0x0904,
+ 0x0002, 0x0030, 0x0002, 0x7f2f, 0x0000, 0x7f00, 0x0000, 0x8066,
+ 0x0008, 0x000a, 0x000b, 0xc0f5, 0x0008, 0x1010, 0x0004, 0x01dc,
+ 0x000b, 0xb0fd, 0x000c, 0x0362, 0x000c, 0x01c6, 0x000b, 0x7814,
+ 0x0003, 0x0013, 0x0000, 0x0806, 0x0008, 0x8010, 0x0000, 0x001f,
+ 0x000c, 0x0362, 0x0000, 0x0310, 0x000c, 0x0362, 0x0003, 0x00fb,
+ 0x000a, 0x002f, 0x0000, 0x7f00, 0x0000, 0x8066, 0x0008, 0x000a,
+ 0x000b, 0xc108, 0x000c, 0x019f, 0x000a, 0x0040, 0x000b, 0x091d,
+ 0x000c, 0x020c, 0x0000, 0x8000, 0x0000, 0x0002, 0x0000, 0x8060,
+ 0x0000, 0x0400, 0x0009, 0x9080, 0x0008, 0x0006, 0x0008, 0x7f62,
+ 0x0000, 0x8066, 0x0008, 0x000a, 0x000b, 0xc116, 0x0000, 0x8072,
+ 0x0000, 0x4000, 0x0003, 0x00fb, 0x0008, 0x8010, 0x0008, 0x001e,
+ 0x000b, 0x011f, 0x0008, 0x8010, 0x0008, 0x001d, 0x000c, 0x0362,
+ 0x0008, 0x1010, 0x000c, 0x0362, 0x000b, 0x0014, 0x0002, 0x4b4e,
+ 0x0003, 0x0929, 0x0008, 0x808a, 0x0000, 0x0004, 0x000b, 0x6129,
+ 0x000f, 0x8000, 0x0008, 0x808a, 0x0000, 0x0004, 0x000b, 0x0014,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0009, 0x9080, 0x0008, 0x0011,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x0009, 0x0003, 0xc133,
+ 0x000a, 0x004f, 0x0003, 0x8990, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0009, 0x9080, 0x0008, 0x0005, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0008, 0x0009, 0x000b, 0xc13d, 0x0008, 0x0060, 0x0008, 0x8062,
+ 0x0000, 0x001f, 0x0000, 0x8066, 0x0000, 0x0209, 0x000b, 0xc143,
+ 0x000a, 0x014b, 0x000b, 0x0990, 0x0008, 0x8062, 0x0008, 0x000f,
+ 0x0000, 0x8066, 0x0000, 0x0211, 0x000b, 0xc14a, 0x0008, 0x01fe,
+ 0x0001, 0x02d0, 0x0003, 0x8990, 0x0004, 0x01a8, 0x000b, 0x0990,
+ 0x0008, 0x03a0, 0x0008, 0x8004, 0x0000, 0x0002, 0x0000, 0x8006,
+ 0x0000, 0x0043, 0x0008, 0x4908, 0x0008, 0x808a, 0x0000, 0x0004,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0009, 0x9080, 0x0008, 0x0000,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x041a, 0x0003, 0xc15f,
+ 0x000b, 0xe160, 0x0008, 0x4908, 0x0008, 0x480a, 0x0008, 0x808a,
+ 0x0000, 0x0004, 0x0008, 0x0060, 0x0008, 0x8062, 0x0008, 0x002b,
+ 0x0000, 0x8066, 0x0000, 0x0411, 0x0003, 0xc16a, 0x0008, 0x04fe,
+ 0x0009, 0x02a0, 0x0003, 0x9171, 0x0002, 0x0500, 0x000b, 0x098d,
+ 0x0003, 0x0172, 0x0000, 0x05fe, 0x0001, 0x03a0, 0x000b, 0x118d,
+ 0x0000, 0x0d0c, 0x0008, 0x0d0e, 0x0008, 0x0d10, 0x0000, 0x0d12,
+ 0x0008, 0x0060, 0x0008, 0x8062, 0x0000, 0x000d, 0x0000, 0x8066,
+ 0x0008, 0x0832, 0x0003, 0xc17d, 0x0000, 0x800a, 0x0000, 0x8005,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0009, 0x9080, 0x0008, 0x0011,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x0a12, 0x0003, 0xc187,
+ 0x0008, 0x5006, 0x0008, 0x100e, 0x0004, 0x01b3, 0x000b, 0x7814,
+ 0x0003, 0x0013, 0x0008, 0x0208, 0x0008, 0x030a, 0x0003, 0x0174,
+ 0x000c, 0x019f, 0x0008, 0x808a, 0x0000, 0x0004, 0x0008, 0x8010,
+ 0x0008, 0x0021, 0x000c, 0x0362, 0x0008, 0x1010, 0x000c, 0x0362,
+ 0x0000, 0x4810, 0x000c, 0x0362, 0x0008, 0x4910, 0x000c, 0x0362,
+ 0x0008, 0x808a, 0x0000, 0x0004, 0x000b, 0x0014, 0x0000, 0x8060,
+ 0x0000, 0x0400, 0x0009, 0x9080, 0x0000, 0x0002, 0x0008, 0x7f62,
+ 0x0000, 0x8066, 0x0008, 0xb40a, 0x0003, 0xc1a6, 0x000f, 0x4000,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x0a62, 0x0000, 0x8066,
+ 0x0000, 0x0411, 0x000b, 0xc1ad, 0x0002, 0x0210, 0x0001, 0xffc0,
+ 0x0000, 0x0007, 0x0009, 0x03e0, 0x000f, 0x4000, 0x0000, 0x8060,
+ 0x0000, 0x0400, 0x0001, 0x8380, 0x0000, 0x0002, 0x0009, 0x0a80,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0000, 0x0e0a, 0x0003, 0xc1bb,
+ 0x0002, 0x0300, 0x0001, 0xffc0, 0x0000, 0x0007, 0x0000, 0x7f06,
+ 0x0002, 0x0a00, 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x060a,
+ 0x000b, 0xc1c4, 0x000f, 0x4000, 0x0000, 0x0da0, 0x0008, 0x0da2,
+ 0x0008, 0x0da4, 0x0009, 0x8880, 0x0000, 0x0001, 0x0008, 0x7f62,
+ 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x8066, 0x0008, 0xa012,
+ 0x0000, 0x0da6, 0x0008, 0x0da8, 0x0000, 0x0daa, 0x0000, 0x0dac,
+ 0x0003, 0xc1d4, 0x0009, 0x8880, 0x0008, 0x0009, 0x0008, 0x7f62,
+ 0x0000, 0x8066, 0x0008, 0xa03a, 0x000b, 0xc1da, 0x000f, 0x4000,
+ 0x0009, 0x8880, 0x0008, 0x0005, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x0009, 0x000b, 0xc1e3,
+ 0x0008, 0x0060, 0x0008, 0x8062, 0x0000, 0x000d, 0x0000, 0x8066,
+ 0x0008, 0x0021, 0x000b, 0xc1e9, 0x0000, 0x00fe, 0x0001, 0x01d0,
+ 0x000b, 0x89f2, 0x0008, 0x02fe, 0x0009, 0x03d0, 0x0003, 0x09f2,
+ 0x0000, 0x0d06, 0x000f, 0x4000, 0x0000, 0x8006, 0x0000, 0x0001,
+ 0x000f, 0x4000, 0x0008, 0x0060, 0x0008, 0x8062, 0x0008, 0x002b,
+ 0x0000, 0x8066, 0x0008, 0xa041, 0x0003, 0xc1fa, 0x0002, 0x0243,
+ 0x000b, 0x8a01, 0x0000, 0x54ac, 0x0000, 0x55ae, 0x0008, 0x0da8,
+ 0x0000, 0x0daa, 0x0000, 0x50b0, 0x0000, 0x51b2, 0x0000, 0x0db4,
+ 0x0008, 0x0db6, 0x0008, 0x0060, 0x0008, 0x8062, 0x0000, 0x0007,
+ 0x0000, 0x8066, 0x0008, 0xa452, 0x0003, 0xc20a, 0x000f, 0x4000,
+ 0x000a, 0x3945, 0x000b, 0x8a16, 0x0000, 0x8072, 0x0008, 0x4040,
+ 0x0007, 0x0000, 0x000a, 0x3945, 0x0003, 0x8a14, 0x000f, 0x4000,
+ 0x0000, 0x8072, 0x0000, 0x4000, 0x0007, 0x0000, 0x0007, 0x0000,
+ 0x0007, 0x0000, 0x000a, 0x3945, 0x0003, 0x0a0e, 0x000b, 0x0216,
+ 0x000a, 0x3a40, 0x000b, 0x8817, 0x0001, 0xabd0, 0x0008, 0x0000,
+ 0x0000, 0x7f24, 0x000b, 0x5a21, 0x0008, 0x8054, 0x0000, 0x0002,
+ 0x0002, 0x1242, 0x0003, 0x0a67, 0x000a, 0x3a45, 0x000b, 0x0a56,
+ 0x000a, 0x1e10, 0x0000, 0x7f3c, 0x000b, 0x0a53, 0x0002, 0x1d00,
+ 0x0000, 0x7f3a, 0x0000, 0x0d60, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0008, 0x0009, 0x000b, 0xc231, 0x0008, 0x00fc, 0x000b, 0xb250,
+ 0x0000, 0x1c60, 0x0008, 0x8062, 0x0000, 0x0001, 0x0000, 0x8066,
+ 0x0008, 0x0009, 0x0003, 0xc239, 0x0008, 0x00fc, 0x000b, 0x3377,
+ 0x0000, 0x0038, 0x0008, 0x0060, 0x0008, 0x8062, 0x0000, 0x0019,
+ 0x0000, 0x8066, 0x0008, 0x0009, 0x0003, 0xc242, 0x0009, 0x80c0,
+ 0x0008, 0x00ff, 0x0008, 0x7f3e, 0x0000, 0x0d60, 0x0008, 0x0efe,
+ 0x0001, 0x1f80, 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x0009,
+ 0x000b, 0xc24c, 0x0008, 0x003a, 0x0000, 0x1dfe, 0x0003, 0x022d,
+ 0x0008, 0x0036, 0x0004, 0x00a6, 0x000b, 0x0267, 0x0000, 0x8074,
+ 0x0000, 0x2000, 0x000b, 0x0267, 0x0002, 0x3a44, 0x000b, 0x0ba4,
+ 0x0000, 0x8074, 0x0000, 0x1000, 0x0001, 0xadd0, 0x0008, 0x0000,
+ 0x0008, 0x7f0e, 0x0003, 0xb374, 0x0001, 0xa7d0, 0x0008, 0x0000,
+ 0x0000, 0x7f00, 0x0009, 0xa6d0, 0x0008, 0x0000, 0x0009, 0x00d0,
+ 0x0003, 0x8a77, 0x0000, 0x8074, 0x0008, 0x4040, 0x0003, 0x5a67,
+ 0x000b, 0x521c, 0x000a, 0x3a46, 0x0003, 0x8a77, 0x0002, 0x3a47,
+ 0x000b, 0x0a72, 0x0008, 0x8054, 0x0000, 0x0004, 0x0000, 0x8074,
+ 0x0000, 0x8000, 0x0003, 0x02d7, 0x0009, 0x92c0, 0x0000, 0x0fc8,
+ 0x000b, 0x0813, 0x000a, 0x1246, 0x0003, 0x8b6e, 0x0000, 0x1a60,
+ 0x0008, 0x8062, 0x0000, 0x0002, 0x0000, 0x8066, 0x0000, 0x367a,
+ 0x000b, 0xc27c, 0x0009, 0x92c0, 0x0008, 0x0780, 0x000b, 0x8b88,
+ 0x0002, 0x124b, 0x0003, 0x0a85, 0x0002, 0x2e4d, 0x0002, 0x2e4d,
+ 0x0003, 0x0b74, 0x000a, 0x3a46, 0x0003, 0x8a95, 0x000b, 0x5a87,
+ 0x0008, 0x8054, 0x0000, 0x0004, 0x000a, 0x1243, 0x0003, 0x0ad5,
+ 0x0008, 0x8010, 0x0000, 0x000d, 0x000c, 0x0362, 0x000a, 0x1948,
+ 0x0003, 0x0a92, 0x000c, 0x0357, 0x0000, 0x1810, 0x000c, 0x0362,
+ 0x000b, 0x02d5, 0x000a, 0x1948, 0x000b, 0x0a99, 0x000a, 0x1243,
+ 0x0003, 0x0b77, 0x000a, 0x194d, 0x0003, 0x0a9d, 0x000a, 0x1243,
+ 0x0003, 0x0b7e, 0x0003, 0x5a9d, 0x0008, 0x8054, 0x0000, 0x0004,
+ 0x000a, 0x192e, 0x0008, 0x7f32, 0x000a, 0x1947, 0x000b, 0x0acf,
+ 0x0002, 0x194f, 0x0003, 0x0aad, 0x000c, 0x0357, 0x0000, 0x1810,
+ 0x0004, 0x01dc, 0x0003, 0xb2c8, 0x000c, 0x0362, 0x000c, 0x01c6,
+ 0x000b, 0x02d5, 0x0000, 0x1a60, 0x0008, 0x8062, 0x0000, 0x001f,
+ 0x0000, 0x8066, 0x0008, 0x0009, 0x0003, 0xc2b2, 0x000a, 0x004c,
+ 0x0003, 0x8acf, 0x0000, 0x8060, 0x0000, 0x0400, 0x0001, 0x9880,
+ 0x0000, 0x0007, 0x0008, 0x7f62, 0x0000, 0x8066, 0x0000, 0x320a,
+ 0x000b, 0xc2bc, 0x0000, 0x8060, 0x0000, 0x0400, 0x0001, 0x9880,
+ 0x0008, 0x0012, 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x1e0a,
+ 0x000b, 0xc2c4, 0x0000, 0x1826, 0x0000, 0x1928, 0x000b, 0x02d5,
+ 0x0000, 0x0806, 0x0008, 0x8010, 0x0000, 0x001f, 0x000c, 0x0362,
+ 0x0000, 0x0310, 0x000c, 0x0362, 0x000b, 0x02d5, 0x000c, 0x0357,
+ 0x0008, 0x8010, 0x0000, 0x0001, 0x000c, 0x0362, 0x0000, 0x1810,
+ 0x000c, 0x0362, 0x0000, 0x8074, 0x0008, 0xf000, 0x0000, 0x0d30,
+ 0x0002, 0x3a42, 0x0003, 0x8add, 0x0000, 0x15fc, 0x000b, 0xb07a,
+ 0x0003, 0x0013, 0x0000, 0x8074, 0x0000, 0x0501, 0x0008, 0x8010,
+ 0x0008, 0x000c, 0x000c, 0x0362, 0x0003, 0x0013, 0x0009, 0xbbe0,
+ 0x0008, 0x0030, 0x0003, 0x8af9, 0x0000, 0x18fe, 0x0009, 0x3ce0,
+ 0x000b, 0x0af6, 0x0008, 0x15fe, 0x0009, 0x3ce0, 0x000b, 0x0af6,
+ 0x0008, 0x13fe, 0x0009, 0x3ce0, 0x000b, 0x8af2, 0x0004, 0x0350,
+ 0x0008, 0x0d26, 0x0003, 0x02f3, 0x000c, 0x0352, 0x0008, 0x8076,
+ 0x0000, 0x0040, 0x000b, 0x034d, 0x0008, 0x8076, 0x0008, 0x0041,
+ 0x000b, 0x034d, 0x0009, 0xbbe0, 0x0000, 0x0032, 0x000b, 0x8afe,
+ 0x0008, 0x3c1e, 0x000b, 0x034d, 0x0009, 0xbbe0, 0x0000, 0x003b,
+ 0x000b, 0x8b03, 0x0000, 0x3cdc, 0x000b, 0x034d, 0x0009, 0xbbe0,
+ 0x0008, 0x0035, 0x000b, 0x8b09, 0x0000, 0x8072, 0x0000, 0x8000,
+ 0x0003, 0x04b1, 0x0009, 0xbbe0, 0x0008, 0x0036, 0x0003, 0x0bd4,
+ 0x0009, 0xbbe0, 0x0000, 0x0037, 0x000b, 0x8b2e, 0x0000, 0x18fe,
+ 0x0009, 0x3ce0, 0x0003, 0x8af6, 0x0008, 0x8076, 0x0000, 0x0040,
+ 0x0000, 0x1a60, 0x0008, 0x8062, 0x0000, 0x000d, 0x0009, 0xa6d0,
+ 0x0008, 0x0000, 0x0008, 0x7f04, 0x0001, 0xa7d0, 0x0008, 0x0000,
+ 0x0000, 0x7f06, 0x0001, 0xa8d0, 0x0008, 0x0000, 0x0008, 0x7f08,
+ 0x0009, 0xa9d0, 0x0008, 0x0000, 0x0000, 0x7f0a, 0x0000, 0x8066,
+ 0x0000, 0x0422, 0x0003, 0xc325, 0x000c, 0x0357, 0x0008, 0x8054,
+ 0x0000, 0x0004, 0x0000, 0x8074, 0x0008, 0xf000, 0x0000, 0x8072,
+ 0x0000, 0x8000, 0x0003, 0x02d7, 0x0009, 0xbbe0, 0x0000, 0x0038,
+ 0x0003, 0x8b40, 0x0000, 0x18fe, 0x0009, 0x3ce0, 0x000b, 0x0b3d,
+ 0x0008, 0x15fe, 0x0009, 0x3ce0, 0x000b, 0x8aec, 0x000c, 0x0352,
+ 0x0008, 0x8076, 0x0000, 0x0040, 0x0000, 0x8072, 0x0000, 0x8000,
+ 0x0003, 0x039b, 0x0008, 0x8076, 0x0008, 0x0042, 0x000b, 0x034d,
+ 0x0009, 0xbbe0, 0x0000, 0x0016, 0x000b, 0x8b4d, 0x0000, 0x8074,
+ 0x0008, 0x0808, 0x0002, 0x3a44, 0x0003, 0x8816, 0x0000, 0x8074,
+ 0x0000, 0x0800, 0x0000, 0x8072, 0x0000, 0x8000, 0x000f, 0x8000,
+ 0x0003, 0x0013, 0x0000, 0x8072, 0x0000, 0x8000, 0x0003, 0x0013,
+ 0x0002, 0x1430, 0x000b, 0x0353, 0x000a, 0x3d30, 0x0000, 0x7f00,
+ 0x0001, 0xbc80, 0x0000, 0x0007, 0x0003, 0x035b, 0x000a, 0x1930,
+ 0x0000, 0x7f00, 0x0001, 0x9880, 0x0000, 0x0007, 0x0000, 0x8060,
+ 0x0000, 0x0400, 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x000a,
+ 0x000b, 0xc360, 0x000f, 0x4000, 0x000b, 0x2362, 0x0008, 0x0870,
+ 0x000f, 0x4000, 0x0009, 0xbac0, 0x0008, 0x0090, 0x000b, 0x0b6b,
+ 0x0000, 0x8074, 0x0000, 0x0706, 0x0003, 0x036d, 0x0000, 0x8074,
+ 0x0000, 0x0703, 0x000f, 0x4000, 0x0008, 0x8010, 0x0000, 0x0023,
+ 0x000b, 0x03a9, 0x0008, 0x8010, 0x0000, 0x0008, 0x000b, 0x03a9,
+ 0x0008, 0x8010, 0x0008, 0x0022, 0x000b, 0x03a9, 0x000c, 0x0357,
+ 0x0008, 0x8010, 0x0000, 0x0007, 0x000c, 0x0362, 0x0000, 0x1810,
+ 0x000c, 0x0362, 0x0003, 0x03b3, 0x000c, 0x0357, 0x0008, 0x8010,
+ 0x0008, 0x001b, 0x000c, 0x0362, 0x0000, 0x1810, 0x000c, 0x0362,
+ 0x0000, 0x8074, 0x0000, 0xf080, 0x0000, 0x0d30, 0x0003, 0x0013,
+ 0x0008, 0x8010, 0x0008, 0x0009, 0x000b, 0x03a9, 0x0008, 0x8010,
+ 0x0008, 0x0005, 0x000b, 0x03a9, 0x000a, 0x1648, 0x000b, 0x8888,
+ 0x0008, 0x808c, 0x0000, 0x0001, 0x0007, 0x0000, 0x0008, 0x8010,
+ 0x0000, 0x0004, 0x000a, 0x4143, 0x0003, 0x0888, 0x0002, 0x3a44,
+ 0x0003, 0x8813, 0x0008, 0x0d2a, 0x000b, 0x03a9, 0x0008, 0x8010,
+ 0x0008, 0x0003, 0x0003, 0x03ab, 0x0008, 0x8010, 0x0000, 0x000b,
+ 0x0003, 0x03ab, 0x0008, 0x8010, 0x0000, 0x0002, 0x0003, 0x03ab,
+ 0x0002, 0x3a47, 0x000b, 0x8a67, 0x0008, 0x8010, 0x0008, 0x0006,
+ 0x0003, 0x03ab, 0x0000, 0x8074, 0x0008, 0xf000, 0x000c, 0x0362,
+ 0x0004, 0x0365, 0x000a, 0x3a40, 0x000b, 0x0813, 0x0008, 0x8010,
+ 0x0008, 0x000c, 0x000c, 0x0362, 0x0003, 0x0013, 0x0000, 0x8074,
+ 0x0000, 0xf080, 0x0000, 0x0d30, 0x0002, 0x2e4d, 0x0002, 0x2e4d,
+ 0x000b, 0x0bbc, 0x0008, 0x8054, 0x0000, 0x0019, 0x0003, 0x0013,
+ 0x0008, 0x8054, 0x0008, 0x0009, 0x0003, 0x0013, 0x0002, 0x3a44,
+ 0x0003, 0x8813, 0x0003, 0x039e, 0x0008, 0x808c, 0x0008, 0x0000,
+ 0x0002, 0x4447, 0x0003, 0x0be8, 0x0001, 0xc0c0, 0x0008, 0x00ff,
+ 0x0009, 0xffe0, 0x0008, 0x00ff, 0x0003, 0x8bbf, 0x0001, 0xc1e0,
+ 0x0008, 0xffff, 0x0003, 0x8bbf, 0x0008, 0x8010, 0x0000, 0x0013,
+ 0x000c, 0x0362, 0x0000, 0x8074, 0x0008, 0x0202, 0x0003, 0x0013,
+ 0x000a, 0x3a40, 0x0003, 0x8be5, 0x0000, 0x8074, 0x0000, 0x0200,
+ 0x0000, 0x3d00, 0x0000, 0x3cfe, 0x0000, 0x8072, 0x0000, 0x8000,
+ 0x0001, 0x43e0, 0x0003, 0x8be3, 0x0000, 0x42fe, 0x0001, 0xffc0,
+ 0x0008, 0x00ff, 0x0009, 0x00e0, 0x000b, 0x0bbf, 0x0008, 0x0d08,
+ 0x000b, 0x0438, 0x0000, 0x8072, 0x0000, 0x8000, 0x0003, 0x0013,
+ 0x0004, 0x04ba, 0x0008, 0x808c, 0x0000, 0x0001, 0x0000, 0x04fc,
+ 0x000b, 0x349d, 0x0000, 0x0460, 0x0008, 0x8062, 0x0000, 0x0001,
+ 0x0000, 0x8066, 0x0008, 0x0009, 0x0003, 0xc3f2, 0x0000, 0x0004,
+ 0x0009, 0x80c0, 0x0008, 0x00ff, 0x0000, 0x7f00, 0x0001, 0x80e0,
+ 0x0000, 0x0004, 0x000b, 0x0c0c, 0x0001, 0x80e0, 0x0008, 0x0005,
+ 0x000b, 0x0c0c, 0x0001, 0x80e0, 0x0008, 0x0006, 0x000b, 0x0c0c,
+ 0x0001, 0x82c0, 0x0008, 0xff00, 0x0008, 0x7f04, 0x0009, 0x82e0,
+ 0x0008, 0x0600, 0x000b, 0x0c0c, 0x0009, 0x82e0, 0x0008, 0x0500,
+ 0x000b, 0x0c0c, 0x0009, 0x82e0, 0x0000, 0x0400, 0x000b, 0x8c9d,
+ 0x0009, 0xc4c0, 0x0000, 0x7000, 0x0009, 0xffe0, 0x0000, 0x1000,
+ 0x0003, 0x0c38, 0x0004, 0x04ab, 0x0002, 0x3941, 0x000b, 0x0c17,
+ 0x0000, 0x8072, 0x0000, 0x0400, 0x0003, 0x0013, 0x0000, 0x0460,
+ 0x0008, 0x80fe, 0x0008, 0x002b, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0008, 0x2209, 0x0003, 0xc41d, 0x0008, 0x11fc, 0x0003, 0x3433,
+ 0x0001, 0x9180, 0x0000, 0x0002, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0008, 0x7f62, 0x0000, 0x8066, 0x0008, 0x0609, 0x0003, 0xc427,
+ 0x0000, 0x42fe, 0x0001, 0xffc0, 0x0008, 0xff00, 0x0009, 0x03e0,
+ 0x0003, 0x8c30, 0x0000, 0x8072, 0x0000, 0x0400, 0x0003, 0x0052,
+ 0x0001, 0x9180, 0x0008, 0x0003, 0x000b, 0x041a, 0x0000, 0x8072,
+ 0x0000, 0x0400, 0x0008, 0x8010, 0x0000, 0x0010, 0x0003, 0x0490,
+ 0x0004, 0x04ab, 0x0002, 0x3941, 0x0003, 0x0c3e, 0x0000, 0x8072,
+ 0x0000, 0x0400, 0x0003, 0x0013, 0x0004, 0x0475, 0x0008, 0x11fc,
+ 0x0003, 0xb446, 0x0000, 0x8072, 0x0000, 0x0400, 0x0008, 0x8010,
+ 0x0000, 0x000e, 0x0003, 0x0490, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0000, 0x04fc, 0x0003, 0xb45b, 0x0008, 0x808c, 0x0008, 0x0000,
+ 0x0001, 0x9180, 0x0008, 0x0005, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0008, 0x0009, 0x000b, 0xc451, 0x0008, 0x0060, 0x0008, 0x8062,
+ 0x0008, 0x001b, 0x0008, 0x4304, 0x0008, 0x4206, 0x0000, 0x8066,
+ 0x0000, 0x0412, 0x0003, 0xc459, 0x0003, 0x0472, 0x0008, 0x808c,
+ 0x0000, 0x0001, 0x0000, 0x0460, 0x0008, 0x8062, 0x0008, 0x002b,
+ 0x0000, 0x8066, 0x0008, 0x0609, 0x000b, 0xc462, 0x0000, 0x8066,
+ 0x0008, 0x220a, 0x0003, 0xc465, 0x0000, 0x42fe, 0x0001, 0xffc0,
+ 0x0008, 0xff00, 0x0008, 0x7f04, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0001, 0x9180, 0x0000, 0x0002, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0008, 0x041a, 0x0003, 0xc471, 0x0000, 0x8072, 0x0000, 0x0400,
+ 0x0003, 0x0052, 0x0000, 0x8060, 0x0000, 0x0400, 0x0008, 0x6b62,
+ 0x0000, 0x8066, 0x0000, 0x0411, 0x000b, 0xc47a, 0x0008, 0x02fe,
+ 0x0009, 0x03e0, 0x000b, 0x8c80, 0x0000, 0x0d22, 0x000f, 0x4000,
+ 0x0009, 0x8280, 0x0000, 0x0002, 0x0001, 0x6b80, 0x0008, 0x7f62,
+ 0x0000, 0x8066, 0x0008, 0x2209, 0x000b, 0xc486, 0x000a, 0x0200,
+ 0x0001, 0xffc0, 0x0000, 0x0007, 0x0000, 0x7f06, 0x0008, 0x6b62,
+ 0x0000, 0x8066, 0x0008, 0x060a, 0x0003, 0xc48e, 0x000f, 0x4000,
+ 0x0002, 0x3a44, 0x0003, 0x8813, 0x000a, 0x2f44, 0x000a, 0x2f44,
+ 0x0003, 0x8b9e, 0x0008, 0x808a, 0x0008, 0x0003, 0x0000, 0x8074,
+ 0x0000, 0xf080, 0x000b, 0x5c99, 0x0008, 0x8054, 0x0000, 0x0019,
+ 0x0003, 0x0013, 0x0002, 0x3a44, 0x0003, 0x8813, 0x0008, 0x808c,
+ 0x0008, 0x0000, 0x0008, 0x8010, 0x0008, 0x0011, 0x000c, 0x0362,
+ 0x0000, 0x42fe, 0x0001, 0xffc0, 0x0008, 0x00ff, 0x0008, 0x7f10,
+ 0x000c, 0x0362, 0x0008, 0x4310, 0x0003, 0x03ab, 0x0002, 0x3941,
+ 0x0003, 0x0cae, 0x000f, 0x4000, 0x0000, 0x8072, 0x0008, 0x0404,
+ 0x000f, 0x4000, 0x0008, 0x8010, 0x0008, 0x0012, 0x000c, 0x0362,
+ 0x0004, 0x0475, 0x0000, 0x1110, 0x000c, 0x0362, 0x0008, 0x11fc,
+ 0x000b, 0xb4b4, 0x0003, 0x0013, 0x0009, 0xc2c0, 0x0008, 0x00ff,
+ 0x0000, 0x7f00, 0x0001, 0xc3c0, 0x0008, 0xff00, 0x0009, 0x00d0,
+ 0x0003, 0x0cdf, 0x0000, 0x0d0a, 0x0001, 0x8580, 0x0000, 0x1000,
+ 0x0008, 0x7f62, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x8066,
+ 0x0000, 0x0809, 0x0003, 0xc4c9, 0x0000, 0x04fc, 0x0003, 0x34d8,
+ 0x0000, 0x0460, 0x0008, 0x8062, 0x0000, 0x0004, 0x0000, 0x8066,
+ 0x0000, 0x0211, 0x0003, 0xc4d1, 0x0008, 0x01fe, 0x0009, 0x00e0,
+ 0x0003, 0x8cd8, 0x0008, 0x02fe, 0x0001, 0x43e0, 0x000b, 0x0cde,
+ 0x0002, 0x0500, 0x0000, 0x7f0a, 0x0009, 0xffe0, 0x0000, 0x0800,
+ 0x000b, 0x8cc2, 0x0008, 0x0d08, 0x000f, 0x4000, 0x0008, 0x43fe,
+ 0x0001, 0x3e80, 0x0000, 0x0d60, 0x0008, 0x7f62, 0x0000, 0x8066,
+ 0x0000, 0x0809, 0x000b, 0xc4e5, 0x0000, 0x8060, 0x0000, 0x0400,
+ 0x0001, 0x84c0, 0x0008, 0xff00, 0x0002, 0x7f70, 0x0009, 0xff80,
+ 0x0000, 0x1000, 0x0008, 0x7f62, 0x0000, 0x8066, 0x0000, 0x0809,
+ 0x0003, 0xc4f0, 0x000f, 0x4000, 0xe504, 0x3334
+};
+unsigned short rseqipx_code_length01 = 0x09e6;
+/*
+ *
+ */
+
+unsigned long xseqipx_code_addr01 = 0x0001e000 ;
+unsigned short xseqipx_code01[] = {
+0x0013, 0x0003, 0x0000, 0x10d6, 0x0001, 0xe000, 0x0005, 0x0032,
+ 0x0000, 0x0010, 0x0015, 0x0033, 0x0010, 0xbb39, 0x000b, 0x8007,
+ 0x0004, 0x010b, 0x0014, 0x011d, 0x0010, 0xc000, 0x0000, 0xc001,
+ 0x0000, 0xc0b0, 0x0010, 0xc0b1, 0x0010, 0xc0b2, 0x0000, 0xc0b3,
+ 0x0010, 0xc0b4, 0x0000, 0xc0b5, 0x0000, 0xc0b6, 0x0010, 0xc0b7,
+ 0x0010, 0xc0b8, 0x0000, 0xc0b9, 0x0000, 0xc0ba, 0x0000, 0xc0c2,
+ 0x0010, 0xc0c3, 0x0000, 0xc0c4, 0x0010, 0xc0c5, 0x0010, 0xc0c6,
+ 0x0000, 0xc0c7, 0x0000, 0xc0c8, 0x0010, 0xc0c9, 0x0010, 0xc0ca,
+ 0x0000, 0xc0cb, 0x0010, 0xc0cc, 0x0000, 0xc0cd, 0x0000, 0xc0ce,
+ 0x0010, 0xc0cf, 0x0015, 0x0039, 0x0010, 0xff00, 0x0015, 0x003a,
+ 0x0010, 0xff00, 0x0005, 0x00d0, 0x0010, 0xff00, 0x0015, 0x00d1,
+ 0x0010, 0xff00, 0x0012, 0x3a40, 0x000b, 0x1031, 0x0002, 0x7940,
+ 0x001b, 0x112f, 0x0002, 0x3a42, 0x001b, 0x1035, 0x0003, 0xb035,
+ 0x0013, 0xa1dc, 0x0002, 0x3a41, 0x001b, 0x1039, 0x0012, 0x7941,
+ 0x001b, 0x1311, 0x0003, 0xe055, 0x0012, 0xd042, 0x0003, 0x103f,
+ 0x0000, 0x75ff, 0x0002, 0xff41, 0x001b, 0x1055, 0x0000, 0x0cfe,
+ 0x0003, 0x6049, 0x0002, 0x3a44, 0x000b, 0x1049, 0x0011, 0x02e8,
+ 0x0010, 0x0000, 0x0013, 0x13a2, 0x0011, 0x02e8, 0x0010, 0x0005,
+ 0x0003, 0x1432, 0x0012, 0x3a46, 0x001b, 0x1055, 0x0012, 0xd042,
+ 0x0003, 0x1050, 0x0000, 0x75ff, 0x0012, 0xff40, 0x001b, 0x1055,
+ 0x0000, 0x12fe, 0x0013, 0x6055, 0x0001, 0x0fe8, 0x0010, 0x0000,
+ 0x0003, 0x163f, 0x0015, 0x0030, 0x0000, 0x0400, 0x0010, 0xc131,
+ 0x0015, 0x0033, 0x0010, 0xb211, 0x001b, 0x805a, 0x0010, 0xb2ff,
+ 0x0001, 0xb3e0, 0x001c, 0x10cd, 0x000b, 0xf02d, 0x0011, 0x3be8,
+ 0x0000, 0x0010, 0x001b, 0x1072, 0x0000, 0x0afe, 0x000b, 0x6066,
+ 0x0000, 0x3c0b, 0x0003, 0x006e, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0001, 0x0a88, 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0010, 0x3c0a, 0x000b, 0x806d, 0x0010, 0x3c0a, 0x0002, 0x0c00,
+ 0x0010, 0xff0c, 0x0013, 0x00ca, 0x0011, 0x3be8, 0x0010, 0x0012,
+ 0x000b, 0x1085, 0x0010, 0x08fe, 0x001b, 0x6079, 0x0010, 0x3c09,
+ 0x0013, 0x0081, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0888,
+ 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0x3c0a,
+ 0x000b, 0x8080, 0x0000, 0x3c08, 0x0002, 0x0c00, 0x0010, 0xff0c,
+ 0x0013, 0x00ca, 0x0011, 0x3be8, 0x0000, 0x0013, 0x001b, 0x108b,
+ 0x0000, 0x3cb0, 0x0004, 0x00dd, 0x0013, 0x00ca, 0x0011, 0x3be8,
+ 0x0000, 0x0019, 0x000b, 0x109e, 0x0010, 0x04fe, 0x001b, 0x6092,
+ 0x0010, 0x3c05, 0x0013, 0x009a, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x0488, 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0010, 0x3c0a, 0x001b, 0x8099, 0x0000, 0x3c04, 0x0002, 0x0c00,
+ 0x0010, 0xff0c, 0x0013, 0x00ca, 0x0011, 0x3be8, 0x0000, 0x0015,
+ 0x001b, 0x10aa, 0x0014, 0x0114, 0x0004, 0x0126, 0x0015, 0x0039,
+ 0x0000, 0x8000, 0x0017, 0x8000, 0x0004, 0x010b, 0x0014, 0x011d,
+ 0x0004, 0x00f6, 0x0013, 0x002d, 0x0011, 0x3be8, 0x0000, 0x0016,
+ 0x000b, 0x10bc, 0x0001, 0x0fe8, 0x0010, 0x0000, 0x0013, 0x10b6,
+ 0x0001, 0x0fe8, 0x0000, 0x0002, 0x0013, 0x10b6, 0x0015, 0x0039,
+ 0x0010, 0x1010, 0x0013, 0x00ca, 0x0015, 0x0039, 0x0000, 0x5040,
+ 0x0015, 0x00b8, 0x0000, 0x0008, 0x0004, 0x0867, 0x0013, 0x00ca,
+ 0x0011, 0x3be8, 0x0010, 0x0017, 0x000b, 0x10c1, 0x0010, 0x3cc3,
+ 0x0013, 0x00ca, 0x0011, 0x3be8, 0x0010, 0x0018, 0x001b, 0x10c6,
+ 0x0000, 0x3cc2, 0x0013, 0x00ca, 0x0005, 0x00ce, 0x0000, 0x0001,
+ 0x0000, 0x3bcf, 0x0004, 0x0829, 0x0015, 0x0039, 0x0000, 0x8000,
+ 0x0013, 0x002d, 0x0001, 0xb288, 0x0000, 0x0002, 0x0001, 0xc180,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x80d3,
+ 0x0002, 0xb200, 0x0011, 0xffc8, 0x0000, 0x0007, 0x0010, 0xffb2,
+ 0x0010, 0xc131, 0x0015, 0x0033, 0x0010, 0xb20a, 0x0001, 0xb0d0,
+ 0x000b, 0x80dc, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0xb088,
+ 0x0000, 0x0010, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb109,
+ 0x001b, 0x80e4, 0x0001, 0xb1e8, 0x0010, 0xffff, 0x0003, 0x10f5,
+ 0x0000, 0x11fe, 0x001b, 0x60ec, 0x0000, 0xb012, 0x0003, 0x00f4,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0x1188, 0x0010, 0x0003,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb00a, 0x001b, 0x80f3,
+ 0x0000, 0xb011, 0x0017, 0x4000, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0xbc88, 0x0000, 0x001f, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xc411, 0x000b, 0x80fd, 0x0011, 0xbc88, 0x0010, 0x0018,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xc609, 0x000b, 0x8103,
+ 0x0011, 0xbc88, 0x0000, 0x0037, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xc709, 0x000b, 0x8109, 0x0017, 0x4000, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0001, 0xbb88, 0x0000, 0x0001, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0x0269, 0x000b, 0x8112, 0x0017, 0x4000,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xbb88, 0x0000, 0x0001,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0x026a, 0x000b, 0x811b,
+ 0x0017, 0x4000, 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xbb88,
+ 0x0010, 0x000f, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0x0f59,
+ 0x000b, 0x8124, 0x0017, 0x4000, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0001, 0xbb88, 0x0010, 0x000f, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0010, 0x0f5a, 0x000b, 0x812d, 0x0017, 0x4000, 0x0000, 0xd0ff,
+ 0x0012, 0xff40, 0x000b, 0x1031, 0x0015, 0x00d1, 0x0010, 0x0101,
+ 0x0013, 0x9134, 0x0005, 0x0079, 0x0000, 0x0001, 0x0013, 0x9137,
+ 0x0015, 0x00d1, 0x0000, 0x0100, 0x0011, 0x02e8, 0x0000, 0x0002,
+ 0x0003, 0x1161, 0x0011, 0x02e8, 0x0000, 0x0001, 0x0003, 0x1179,
+ 0x0011, 0x02e8, 0x0000, 0x0004, 0x0003, 0x1197, 0x0011, 0x02e8,
+ 0x0010, 0x0003, 0x0003, 0x11c8, 0x0005, 0x0002, 0x0010, 0x0000,
+ 0x0000, 0xc00e, 0x0000, 0xc00d, 0x0010, 0xc003, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0001, 0xbd88, 0x0010, 0x0009, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0010, 0xc00a, 0x001b, 0x8152, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0010, 0xc00a, 0x000b, 0x8156, 0x0012, 0x3a45,
+ 0x0003, 0x115e, 0x0015, 0x003a, 0x0000, 0x2000, 0x0015, 0x003a,
+ 0x0010, 0x1010, 0x0014, 0x0853, 0x0012, 0xd042, 0x0013, 0x1031,
+ 0x0013, 0x0050, 0x0012, 0x7849, 0x0003, 0x11d6, 0x0010, 0x0dfe,
+ 0x0003, 0x6148, 0x0012, 0x0c10, 0x0010, 0xff0c, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x0d88, 0x0010, 0x0003, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb309, 0x001b, 0x816e, 0x0010, 0xb3fe,
+ 0x0013, 0x6176, 0x0010, 0xb30b, 0x0015, 0x0033, 0x0010, 0xc00a,
+ 0x000b, 0x8174, 0x0013, 0x01cb, 0x0000, 0xc00b, 0x0010, 0xc00a,
+ 0x0013, 0x01cb, 0x0000, 0x78b0, 0x0012, 0xb044, 0x0003, 0x11d6,
+ 0x0002, 0xb049, 0x0003, 0x11d6, 0x0010, 0x71ff, 0x0012, 0xff38,
+ 0x0010, 0xff71, 0x0010, 0x0dfe, 0x0013, 0x6146, 0x0012, 0x0c10,
+ 0x0010, 0xff0c, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb309,
+ 0x001b, 0x818c, 0x0010, 0xb3fe, 0x0013, 0x6194, 0x0000, 0xb309,
+ 0x0015, 0x0033, 0x0010, 0xc00a, 0x001b, 0x8192, 0x0013, 0x01cb,
+ 0x0010, 0xc009, 0x0000, 0xc008, 0x0013, 0x01cb, 0x0000, 0x78b0,
+ 0x0012, 0xb044, 0x0003, 0x11d6, 0x0002, 0xb049, 0x0003, 0x11d6,
+ 0x0010, 0x71ff, 0x0012, 0xff38, 0x0010, 0xff71, 0x0010, 0x0dfe,
+ 0x0013, 0x6146, 0x0012, 0x0c10, 0x0010, 0xff0c, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x0d88, 0x0010, 0x0003, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb309, 0x000b, 0x81aa, 0x0010, 0xb3fe,
+ 0x0003, 0x61b2, 0x0000, 0xb305, 0x0015, 0x0033, 0x0010, 0xc00a,
+ 0x001b, 0x81b0, 0x0003, 0x01b4, 0x0010, 0xc005, 0x0000, 0xc004,
+ 0x0002, 0x033f, 0x0002, 0xff27, 0x0000, 0x0db8, 0x0014, 0x0397,
+ 0x0000, 0x0db8, 0x0004, 0x0867, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0xbc88, 0x0010, 0x0000, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb309, 0x001b, 0x81c1, 0x0011, 0xb3e8, 0x0000, 0x0002,
+ 0x001b, 0x1146, 0x0005, 0x0002, 0x0010, 0x0005, 0x0003, 0x0148,
+ 0x0012, 0x7849, 0x0003, 0x11d6, 0x0003, 0x0148, 0x0000, 0x0db8,
+ 0x0012, 0x0345, 0x000b, 0x11d1, 0x0002, 0x033f, 0x0014, 0x0397,
+ 0x0013, 0x0146, 0x0002, 0x033f, 0x0002, 0xff27, 0x0014, 0x0397,
+ 0x0004, 0x0867, 0x0013, 0x0146, 0x0015, 0x00b8, 0x0000, 0x0001,
+ 0x0015, 0x003a, 0x0010, 0x0101, 0x0004, 0x0867, 0x0013, 0x0157,
+ 0x0001, 0x2bd8, 0x0010, 0x0000, 0x0000, 0xffba, 0x0003, 0xb1df,
+ 0x0005, 0x002a, 0x0000, 0x0002, 0x0001, 0xbac8, 0x0000, 0x0700,
+ 0x000b, 0x12cc, 0x0011, 0x15e8, 0x0000, 0x0002, 0x0013, 0x1242,
+ 0x0011, 0x15e8, 0x0000, 0x0001, 0x0013, 0x11ee, 0x0005, 0x0015,
+ 0x0010, 0x0000, 0x0013, 0x0225, 0x0005, 0x0015, 0x0010, 0x0000,
+ 0x0002, 0xba43, 0x0003, 0x1226, 0x0003, 0xb1f2, 0x0005, 0x002a,
+ 0x0000, 0x0004, 0x0012, 0xba42, 0x0003, 0x122c, 0x0012, 0x104b,
+ 0x001b, 0x1225, 0x0000, 0x1a30, 0x0005, 0x0031, 0x0000, 0x0002,
+ 0x0015, 0x0033, 0x0000, 0x1b2a, 0x001b, 0x81fe, 0x0011, 0x20d8,
+ 0x0010, 0x0000, 0x0000, 0xffb0, 0x0001, 0x21d8, 0x0010, 0x0000,
+ 0x0010, 0xffb1, 0x0001, 0x22d8, 0x0010, 0x0000, 0x0010, 0xffb2,
+ 0x0011, 0x23d8, 0x0010, 0x0000, 0x0000, 0xffb3, 0x0001, 0x24d8,
+ 0x0010, 0x0000, 0x0010, 0xffb4, 0x0011, 0x25d8, 0x0010, 0x0000,
+ 0x0000, 0xffb5, 0x0001, 0x28d8, 0x0010, 0x0000, 0x0010, 0xffb8,
+ 0x0011, 0x29d8, 0x0010, 0x0000, 0x0000, 0xffb9, 0x0000, 0x1a30,
+ 0x0005, 0x0031, 0x0000, 0x0007, 0x0015, 0x0033, 0x0010, 0xb032,
+ 0x001b, 0x821c, 0x0000, 0x1a30, 0x0005, 0x0031, 0x0010, 0x000f,
+ 0x0015, 0x0033, 0x0010, 0xb812, 0x000b, 0x8222, 0x0005, 0x0015,
+ 0x0010, 0x0000, 0x0013, 0x0035, 0x0000, 0x1efe, 0x0003, 0x623a,
+ 0x0014, 0x0271, 0x0000, 0x1efe, 0x000c, 0x6271, 0x0013, 0x0225,
+ 0x0000, 0x1a30, 0x0005, 0x0031, 0x0000, 0x0020, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x001b, 0x8231, 0x0002, 0xb02f, 0x0000, 0xffb0,
+ 0x0005, 0x0031, 0x0000, 0x0020, 0x0015, 0x0033, 0x0000, 0xb00a,
+ 0x001b, 0x8238, 0x0003, 0x01f9, 0x0015, 0x00b8, 0x0010, 0x0005,
+ 0x0004, 0x0867, 0x0000, 0x13b8, 0x0015, 0x003a, 0x0010, 0x0404,
+ 0x0004, 0x0867, 0x0013, 0x0225, 0x0005, 0x0015, 0x0000, 0x0001,
+ 0x0012, 0xba42, 0x0013, 0x1250, 0x0003, 0xb246, 0x0001, 0x2bd8,
+ 0x0010, 0x0000, 0x0012, 0xff4f, 0x001b, 0x11dc, 0x0002, 0xba43,
+ 0x001b, 0x122c, 0x0000, 0x1efe, 0x000c, 0x6271, 0x0013, 0x0225,
+ 0x0001, 0x28d8, 0x0010, 0x0000, 0x0010, 0xffb8, 0x0011, 0x29d8,
+ 0x0010, 0x0000, 0x0000, 0xffb9, 0x0014, 0x02e2, 0x0002, 0x3a42,
+ 0x001b, 0x1225, 0x0000, 0x1c30, 0x0015, 0x00ff, 0x0000, 0x0002,
+ 0x0002, 0x1f43, 0x001b, 0x1261, 0x0001, 0xff88, 0x0000, 0x0002,
+ 0x0003, 0x0263, 0x0001, 0xff88, 0x0000, 0x0004, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb011, 0x000b, 0x8266, 0x0000, 0xb0ff,
+ 0x0011, 0x16a0, 0x0000, 0xff16, 0x001b, 0x226d, 0x0002, 0xb100,
+ 0x0013, 0x026e, 0x0010, 0xb1ff, 0x0001, 0x17a0, 0x0010, 0xff17,
+ 0x0013, 0x022c, 0x0000, 0x16ff, 0x0001, 0x18a0, 0x0010, 0xff00,
+ 0x000b, 0x2278, 0x0002, 0x1700, 0x0003, 0x12cb, 0x0013, 0x0279,
+ 0x0010, 0x17ff, 0x0011, 0x19a0, 0x0003, 0x22cb, 0x0011, 0x00d0,
+ 0x0003, 0x12cb, 0x0000, 0x1c30, 0x0000, 0x1b31, 0x0015, 0x0033,
+ 0x0000, 0xb131, 0x000b, 0x8281, 0x0013, 0xb282, 0x0000, 0xb120,
+ 0x0010, 0xb221, 0x0002, 0x1f43, 0x000b, 0x128e, 0x0010, 0xc022,
+ 0x0000, 0xc023, 0x0000, 0xb324, 0x0000, 0xb425, 0x0010, 0xb3b5,
+ 0x0000, 0xb4b6, 0x0013, 0x0292, 0x0000, 0xb322, 0x0000, 0xb423,
+ 0x0000, 0xb524, 0x0010, 0xb625, 0x0003, 0xb292, 0x0005, 0x002a,
+ 0x0000, 0x0001, 0x0012, 0x1500, 0x0000, 0xff15, 0x0000, 0x16ff,
+ 0x0001, 0xb580, 0x0000, 0xff16, 0x001b, 0x229d, 0x0002, 0x1700,
+ 0x0013, 0x029e, 0x0010, 0x17ff, 0x0001, 0xb680, 0x0010, 0xff17,
+ 0x0012, 0x1e10, 0x0010, 0xff1e, 0x0013, 0x62cb, 0x0002, 0x1d00,
+ 0x0010, 0xff1d, 0x0010, 0xc030, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x000b, 0x82a9, 0x0010, 0xb0fe, 0x001b, 0x62ca,
+ 0x0000, 0x1c30, 0x0005, 0x0031, 0x0000, 0x0001, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x000b, 0x82b1, 0x0010, 0xb0fe, 0x001b, 0x62b7,
+ 0x0005, 0x00ce, 0x0010, 0x0005, 0x0013, 0x0829, 0x0010, 0xb01c,
+ 0x0000, 0x1c30, 0x0005, 0x0031, 0x0000, 0x0019, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x000b, 0x82bd, 0x0001, 0xb0c8, 0x0010, 0x00ff,
+ 0x0000, 0xff1f, 0x0010, 0xc030, 0x0011, 0xbe80, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x82c6, 0x0000, 0xb01d,
+ 0x0010, 0x1dff, 0x0003, 0x02a5, 0x0000, 0xb01b, 0x0017, 0x4000,
+ 0x0002, 0x3a41, 0x0013, 0x12d4, 0x0003, 0xb2ce, 0x0005, 0x002a,
+ 0x0000, 0x0004, 0x0005, 0x0015, 0x0010, 0x0000, 0x0013, 0x0225,
+ 0x0000, 0x1a30, 0x0005, 0x0031, 0x0000, 0x0002, 0x0015, 0x0033,
+ 0x0000, 0x1b2a, 0x001b, 0x82d9, 0x0015, 0x00b8, 0x0000, 0x0004,
+ 0x0004, 0x0867, 0x0000, 0x13b8, 0x0015, 0x003a, 0x0010, 0x0404,
+ 0x0004, 0x0867, 0x0013, 0x0039, 0x0002, 0x1e00, 0x0010, 0xff1e,
+ 0x0012, 0x1d10, 0x0010, 0xff1d, 0x0010, 0xc030, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x82ea, 0x0010, 0xb0fe,
+ 0x000b, 0x630f, 0x0000, 0x1cff, 0x0001, 0x1ae0, 0x0013, 0x12f9,
+ 0x0000, 0x1c30, 0x0005, 0x0031, 0x0010, 0x0000, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x000b, 0x82f5, 0x0010, 0xb0fe, 0x001b, 0x62f9,
+ 0x0000, 0x1aff, 0x0000, 0xff1c, 0x0000, 0x1c30, 0x0005, 0x0031,
+ 0x0000, 0x0019, 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x82ff,
+ 0x0001, 0xb0c8, 0x0010, 0x000f, 0x0000, 0xff1f, 0x0001, 0xbf80,
+ 0x0010, 0xff1d, 0x0010, 0xc030, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x001b, 0x8309, 0x0010, 0xb0fe, 0x000b, 0x630f,
+ 0x0005, 0x00ce, 0x0010, 0x0006, 0x0013, 0x0829, 0x0000, 0xb01b,
+ 0x0017, 0x4000, 0x0010, 0x79b0, 0x0000, 0xd0ff, 0x0012, 0xff40,
+ 0x001b, 0x1039, 0x0015, 0x00d1, 0x0010, 0x0101, 0x0013, 0x9317,
+ 0x0005, 0x0079, 0x0000, 0x0002, 0x0003, 0x931a, 0x0015, 0x00d1,
+ 0x0000, 0x0100, 0x0010, 0x13fe, 0x0003, 0x634f, 0x0012, 0xb04e,
+ 0x001b, 0x136f, 0x0012, 0x784a, 0x0013, 0x1375, 0x0000, 0x75ff,
+ 0x0011, 0xffc8, 0x0010, 0x1800, 0x000b, 0x1375, 0x0001, 0x0fe8,
+ 0x0000, 0x0001, 0x001b, 0x1333, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x1388, 0x0000, 0x000e, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0x8f0a, 0x000b, 0x8331, 0x0013, 0x037b, 0x0001, 0x0fe8,
+ 0x0000, 0x0002, 0x000b, 0x133e, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0005, 0x0031, 0x0000, 0x001a, 0x0015, 0x0033, 0x0010, 0xc00a,
+ 0x001b, 0x833c, 0x0013, 0x037b, 0x0001, 0x0fe8, 0x0010, 0x0000,
+ 0x0013, 0x1345, 0x0005, 0x00ce, 0x0000, 0x0007, 0x0010, 0x0fcf,
+ 0x0013, 0x0823, 0x0000, 0x13b8, 0x0002, 0x1045, 0x0003, 0x134d,
+ 0x0012, 0x103f, 0x0002, 0xff27, 0x0014, 0x0397, 0x0004, 0x0867,
+ 0x0003, 0x034f, 0x0012, 0x103f, 0x0014, 0x0397, 0x0015, 0x000f,
+ 0x0010, 0x0000, 0x0002, 0x3944, 0x0013, 0x1358, 0x0015, 0x0039,
+ 0x0000, 0x5040, 0x0015, 0x00b8, 0x0000, 0x0008, 0x0004, 0x0867,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xbd88, 0x0010, 0x000c,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xc00a, 0x001b, 0x835f,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xc00a, 0x001b, 0x8363,
+ 0x0010, 0xc014, 0x0000, 0xc013, 0x0000, 0xc010, 0x0002, 0x3a47,
+ 0x0013, 0x136e, 0x0015, 0x003a, 0x0000, 0x8000, 0x0015, 0x003a,
+ 0x0010, 0x4040, 0x0014, 0x082e, 0x0013, 0x0039, 0x0015, 0x00b8,
+ 0x0010, 0x0003, 0x0015, 0x003a, 0x0010, 0x0202, 0x0004, 0x0867,
+ 0x0003, 0x0367, 0x0015, 0x00b8, 0x0000, 0x0002, 0x0015, 0x003a,
+ 0x0010, 0x0202, 0x0004, 0x0867, 0x0003, 0x0367, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x1388, 0x0010, 0x0003, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x8382, 0x0011, 0x1388,
+ 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xc00a,
+ 0x001b, 0x8388, 0x0010, 0xb0fe, 0x0013, 0x638d, 0x0000, 0xb012,
+ 0x0003, 0x038f, 0x0010, 0xc012, 0x0010, 0xc011, 0x0012, 0x104b,
+ 0x0013, 0x1345, 0x0002, 0x103b, 0x0010, 0xff03, 0x0005, 0x0002,
+ 0x0010, 0x0000, 0x0000, 0xc00d, 0x0003, 0x0345, 0x0000, 0xffb0,
+ 0x0010, 0xc3b1, 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xb888,
+ 0x0010, 0x0011, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb012,
+ 0x001b, 0x83a0, 0x0017, 0x4000, 0x0012, 0x3a43, 0x0003, 0x13b1,
+ 0x0015, 0x003a, 0x0000, 0x0800, 0x0010, 0x0db0, 0x0013, 0x63b1,
+ 0x0000, 0x0bff, 0x0001, 0xb0e0, 0x0013, 0x13da, 0x0010, 0x09ff,
+ 0x0001, 0xb0e0, 0x0003, 0x13be, 0x0010, 0x05ff, 0x0001, 0xb0e0,
+ 0x0013, 0x13b5, 0x0000, 0xc00e, 0x0000, 0x05fe, 0x0013, 0x63bb,
+ 0x0000, 0x050d, 0x0005, 0x0002, 0x0000, 0x0004, 0x0014, 0x043c,
+ 0x0002, 0x3a47, 0x001b, 0x143b, 0x0003, 0x03d5, 0x0000, 0x09fe,
+ 0x0013, 0x63d7, 0x0000, 0x090d, 0x0005, 0x0002, 0x0000, 0x0001,
+ 0x0014, 0x0455, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0000, 0x0004, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xba09,
+ 0x000b, 0x83c8, 0x0011, 0x03c8, 0x0010, 0x000f, 0x0000, 0xffb6,
+ 0x0011, 0xb6e8, 0x0000, 0x0001, 0x0003, 0x14ec, 0x0011, 0xb6e8,
+ 0x0000, 0x0002, 0x0013, 0x150e, 0x0011, 0xb6e8, 0x0010, 0x0003,
+ 0x0013, 0x15fd, 0x0014, 0x082e, 0x0013, 0x043b, 0x0010, 0x0bfe,
+ 0x0013, 0x643b, 0x0010, 0x0b0d, 0x0005, 0x0002, 0x0000, 0x0002,
+ 0x0014, 0x0455, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0000, 0x0004, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xba09,
+ 0x001b, 0x83e4, 0x0000, 0xb930, 0x0005, 0x0031, 0x0010, 0x0021,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x83ea, 0x0001, 0xb0a8,
+ 0x0000, 0x199a, 0x0003, 0x23f0, 0x0005, 0x00b0, 0x0000, 0x1999,
+ 0x0012, 0xb050, 0x0000, 0xffb0, 0x0002, 0xff50, 0x0002, 0xff50,
+ 0x0001, 0xb080, 0x0000, 0xffb0, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x0d88, 0x0010, 0x0006, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb00a, 0x000b, 0x83fd, 0x0000, 0xb930, 0x0005, 0x0031,
+ 0x0000, 0x0019, 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x8403,
+ 0x0001, 0xb0c8, 0x0010, 0x00ff, 0x0001, 0xffe8, 0x0010, 0x0048,
+ 0x001b, 0x1464, 0x0005, 0x0002, 0x0010, 0x0006, 0x0012, 0x0c10,
+ 0x0010, 0xff0c, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb109,
+ 0x000b, 0x8414, 0x0000, 0xb10b, 0x001b, 0x6418, 0x0010, 0xb10a,
+ 0x0015, 0x0033, 0x0010, 0xc00a, 0x001b, 0x841a, 0x0002, 0x032b,
+ 0x0010, 0xff03, 0x0011, 0x0d88, 0x0010, 0x0011, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0010, 0x030a, 0x000b, 0x8422, 0x0000, 0x11fe,
+ 0x001b, 0x6427, 0x0000, 0x0d12, 0x0003, 0x0430, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0001, 0x1188, 0x0010, 0x0003, 0x0000, 0xff31,
+ 0x0010, 0x0db0, 0x0015, 0x0033, 0x0000, 0xb00a, 0x001b, 0x842f,
+ 0x0000, 0x0d11, 0x0013, 0x043b, 0x0000, 0x05fe, 0x0013, 0x643b,
+ 0x0005, 0x0002, 0x0000, 0x0004, 0x0000, 0x050d, 0x0014, 0x043c,
+ 0x0002, 0x3a47, 0x001b, 0x143b, 0x0014, 0x082e, 0x0003, 0x0049,
+ 0x0001, 0xc7c8, 0x0010, 0x0028, 0x001b, 0x1454, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x0d88, 0x0010, 0x000a, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x8446, 0x0002, 0xb04f,
+ 0x0003, 0x1454, 0x0001, 0x0fe8, 0x0010, 0x0000, 0x0003, 0x1452,
+ 0x0001, 0x0fe8, 0x0000, 0x0002, 0x0003, 0x1452, 0x0015, 0x003a,
+ 0x0010, 0x8080, 0x0013, 0x0454, 0x0015, 0x003a, 0x0010, 0x4040,
+ 0x0017, 0x4000, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0011, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0x0309,
+ 0x000b, 0x845c, 0x0011, 0x0d88, 0x0010, 0x0005, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb909, 0x001b, 0x8462, 0x0017, 0x4000,
+ 0x0005, 0x00b6, 0x0010, 0x0600, 0x0004, 0x062d, 0x0004, 0x04d6,
+ 0x0000, 0xb05a, 0x0000, 0xb15b, 0x0005, 0x0054, 0x0010, 0x0829,
+ 0x0010, 0x0d58, 0x0015, 0x0059, 0x0010, 0xffff, 0x0000, 0xb930,
+ 0x0005, 0x0031, 0x0010, 0x001e, 0x0015, 0x0033, 0x0000, 0xb009,
+ 0x000b, 0x8474, 0x0000, 0xb05c, 0x0005, 0x0031, 0x0000, 0x001f,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x847a, 0x0001, 0xb0c8,
+ 0x0010, 0x000f, 0x000b, 0x1481, 0x0015, 0x00ff, 0x0010, 0x0005,
+ 0x0013, 0x0489, 0x0002, 0xb040, 0x0003, 0x1486, 0x0015, 0x00ff,
+ 0x0000, 0x0004, 0x0013, 0x0489, 0x0001, 0xb0c8, 0x0010, 0x0006,
+ 0x0002, 0xff60, 0x0010, 0xffb2, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x0d88, 0x0000, 0x0019, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0010, 0xb109, 0x001b, 0x8491, 0x0012, 0xb170, 0x0011, 0xffc8,
+ 0x0010, 0xff00, 0x0011, 0xb2d0, 0x0010, 0xff60, 0x0002, 0xb045,
+ 0x0013, 0x149c, 0x0015, 0x00b2, 0x0000, 0x0002, 0x0003, 0x04a6,
+ 0x0002, 0xb046, 0x0003, 0x14a1, 0x0015, 0x00b2, 0x0000, 0x0001,
+ 0x0003, 0x04a6, 0x0015, 0x00b2, 0x0010, 0x0000, 0x0000, 0xc0b0,
+ 0x0010, 0xc0b1, 0x0003, 0x04ac, 0x0000, 0xb930, 0x0005, 0x0031,
+ 0x0010, 0x002b, 0x0015, 0x0033, 0x0000, 0xb011, 0x001b, 0x84ab,
+ 0x0010, 0xb16a, 0x0010, 0xb06b, 0x0000, 0xb261, 0x0015, 0x0044,
+ 0x0010, 0x0018, 0x0000, 0xb930, 0x0005, 0x0031, 0x0000, 0x0023,
+ 0x0015, 0x0033, 0x0000, 0x6241, 0x001b, 0x84b6, 0x0003, 0x94b7,
+ 0x0015, 0x00a0, 0x0000, 0x0020, 0x0012, 0xd041, 0x001b, 0x14ba,
+ 0x0015, 0x00d1, 0x0010, 0x0202, 0x0003, 0x94be, 0x0000, 0x75ff,
+ 0x0011, 0xffc8, 0x0000, 0x1804, 0x0001, 0xffd8, 0x0010, 0x0009,
+ 0x0013, 0x94c4, 0x0000, 0xff75, 0x0003, 0x94c6, 0x0015, 0x00d1,
+ 0x0000, 0x0200, 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xbd88,
+ 0x0000, 0x0008, 0x0000, 0xff31, 0x0015, 0x00b1, 0x0010, 0x07d0,
+ 0x0005, 0x00b0, 0x0010, 0x0009, 0x0015, 0x0033, 0x0000, 0xb012,
+ 0x000b, 0x84d4, 0x0013, 0x043b, 0x0000, 0xba30, 0x0005, 0x0031,
+ 0x0010, 0x0035, 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x84db,
+ 0x0002, 0xb040, 0x0003, 0x14e9, 0x0010, 0xb9b0, 0x0010, 0xb7b1,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88, 0x0000, 0x0013,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb012, 0x000b, 0x84e7,
+ 0x0003, 0x04eb, 0x0010, 0xc0b1, 0x0000, 0xc0b0, 0x0017, 0x4000,
+ 0x0005, 0x00b6, 0x0010, 0x0500, 0x0004, 0x062d, 0x0005, 0x0054,
+ 0x0010, 0x0889, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0000, 0x0002, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009,
+ 0x001b, 0x84f8, 0x0010, 0xb058, 0x0000, 0x0d59, 0x0000, 0xb930,
+ 0x0005, 0x0031, 0x0000, 0x0023, 0x0015, 0x0033, 0x0000, 0xb011,
+ 0x001b, 0x8500, 0x0010, 0xb15c, 0x0010, 0xb05d, 0x0005, 0x0031,
+ 0x0010, 0x002b, 0x0015, 0x0033, 0x0000, 0xb011, 0x000b, 0x8507,
+ 0x0000, 0xb15e, 0x0000, 0xb05f, 0x0013, 0x950a, 0x0015, 0x00a0,
+ 0x0010, 0x000c, 0x0013, 0x0612, 0x0005, 0x00b6, 0x0000, 0x0700,
+ 0x0004, 0x062d, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0009, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb709,
+ 0x001b, 0x8518, 0x0012, 0xb749, 0x0003, 0x151e, 0x0005, 0x0054,
+ 0x0010, 0x0889, 0x0003, 0x0520, 0x0005, 0x0054, 0x0010, 0x0898,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88, 0x0000, 0x0002,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x8527,
+ 0x0010, 0xb058, 0x0000, 0x0d59, 0x0001, 0xb9a8, 0x0010, 0x00f0,
+ 0x001b, 0x254e, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0005, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009,
+ 0x000b, 0x8534, 0x0001, 0xb0c8, 0x0000, 0xf700, 0x0000, 0xffb0,
+ 0x0011, 0xb0e8, 0x0000, 0xf100, 0x0003, 0x1595, 0x0011, 0xb0e8,
+ 0x0000, 0xf200, 0x0003, 0x159a, 0x0011, 0xb0e8, 0x0010, 0xf300,
+ 0x0013, 0x15bf, 0x0011, 0xb0e8, 0x0000, 0xf400, 0x0013, 0x15c4,
+ 0x0011, 0xb0e8, 0x0010, 0xf500, 0x0003, 0x1595, 0x0011, 0xb0e8,
+ 0x0010, 0xf600, 0x0013, 0x15d5, 0x0005, 0x00ce, 0x0010, 0x0009,
+ 0x0000, 0xb0cf, 0x0013, 0x0823, 0x0000, 0xb930, 0x0005, 0x0031,
+ 0x0000, 0x0025, 0x0015, 0x0033, 0x0000, 0xb039, 0x001b, 0x8553,
+ 0x0012, 0xb749, 0x0013, 0x1558, 0x0002, 0xb52c, 0x0000, 0xffb5,
+ 0x0000, 0xb162, 0x0000, 0xb063, 0x0005, 0x0031, 0x0000, 0x001f,
+ 0x0015, 0x0033, 0x0000, 0xb309, 0x000b, 0x855e, 0x0001, 0xb3c8,
+ 0x0010, 0x0003, 0x0003, 0x1566, 0x0010, 0xffb2, 0x0001, 0xffe8,
+ 0x0010, 0x0003, 0x000b, 0x1568, 0x0000, 0xc2b7, 0x0003, 0x05f1,
+ 0x0001, 0xb2e8, 0x0000, 0x0001, 0x0003, 0x156f, 0x0005, 0x00ce,
+ 0x0010, 0x000a, 0x0010, 0xb2cf, 0x0013, 0x0823, 0x0010, 0xb465,
+ 0x0010, 0xb667, 0x0015, 0x00b7, 0x0010, 0x0018, 0x0001, 0xb5c8,
+ 0x0010, 0x0300, 0x0013, 0x1594, 0x0012, 0xb548, 0x0003, 0x157b,
+ 0x0000, 0xb6ff, 0x0011, 0xb780, 0x0010, 0xffb7, 0x0002, 0xb549,
+ 0x0013, 0x1580, 0x0010, 0xb4ff, 0x0011, 0xb780, 0x0010, 0xffb7,
+ 0x0015, 0x0044, 0x0010, 0x0018, 0x0005, 0x0031, 0x0000, 0x002c,
+ 0x0015, 0x0033, 0x0000, 0x6841, 0x000b, 0x8586, 0x0015, 0x0044,
+ 0x0000, 0x0019, 0x0005, 0x0031, 0x0000, 0x0034, 0x0015, 0x0033,
+ 0x0000, 0x5029, 0x001b, 0x858d, 0x0015, 0x0044, 0x0000, 0x0008,
+ 0x0011, 0xb7c8, 0x0010, 0x0003, 0x0013, 0x1594, 0x0010, 0xff55,
+ 0x0003, 0x05f1, 0x0005, 0x00b5, 0x0000, 0x0008, 0x0015, 0x00b7,
+ 0x0010, 0x0018, 0x0003, 0x05f1, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x0d88, 0x0000, 0x000b, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb011, 0x000b, 0x85a1, 0x0010, 0xb1ff, 0x0001, 0xb0d0,
+ 0x0003, 0x15aa, 0x0005, 0x00b5, 0x0010, 0x0b02, 0x0010, 0xb062,
+ 0x0010, 0xb163, 0x0013, 0x05ac, 0x0005, 0x00b5, 0x0000, 0x0302,
+ 0x0015, 0x0065, 0x0010, 0x0012, 0x0005, 0x0067, 0x0000, 0x0008,
+ 0x0015, 0x006c, 0x0000, 0x7000, 0x0005, 0x006d, 0x0010, 0x0500,
+ 0x0015, 0x006f, 0x0010, 0x000a, 0x0015, 0x0044, 0x0000, 0x0001,
+ 0x0005, 0x0052, 0x0000, 0x2500, 0x0015, 0x0044, 0x0000, 0x0008,
+ 0x0015, 0x00b7, 0x0000, 0x0032, 0x0003, 0x05f1, 0x0005, 0x00b5,
+ 0x0010, 0x0028, 0x0015, 0x00b7, 0x0010, 0x0018, 0x0003, 0x05f1,
+ 0x0005, 0x00b5, 0x0000, 0x0100, 0x0005, 0x0067, 0x0000, 0x0008,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88, 0x0010, 0x0018,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x85cf,
+ 0x0001, 0xb0c8, 0x0010, 0x00ff, 0x0015, 0x00b7, 0x0000, 0x0020,
+ 0x0003, 0x05f1, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0005, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb609,
+ 0x000b, 0x85dc, 0x0001, 0xb6c8, 0x0010, 0xff00, 0x0000, 0xffb0,
+ 0x0015, 0x0033, 0x0000, 0xb00a, 0x001b, 0x85e2, 0x0001, 0xb6c8,
+ 0x0010, 0x00ff, 0x0012, 0xff10, 0x001b, 0x15eb, 0x0000, 0xffb5,
+ 0x0015, 0x00b7, 0x0010, 0x0018, 0x0003, 0x05f1, 0x0010, 0xff63,
+ 0x0005, 0x00b5, 0x0000, 0x0800, 0x0015, 0x00b7, 0x0010, 0x0018,
+ 0x0003, 0x05f1, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88,
+ 0x0010, 0x0009, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009,
+ 0x000b, 0x85f8, 0x0010, 0xb561, 0x0013, 0x95fa, 0x0010, 0xb7a0,
+ 0x0013, 0x0612, 0x0005, 0x00b6, 0x0010, 0x0300, 0x0004, 0x062d,
+ 0x0005, 0x0054, 0x0010, 0x0819, 0x0010, 0x0d58, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x0d88, 0x0000, 0x0002, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x860a, 0x0000, 0xb059,
+ 0x0013, 0x960c, 0x0010, 0xc0a0, 0x0010, 0x71ff, 0x0002, 0xff28,
+ 0x0010, 0xff71, 0x0013, 0x0612, 0x0012, 0xd041, 0x001b, 0x1612,
+ 0x0015, 0x00d1, 0x0010, 0x0202, 0x0000, 0x75ff, 0x0011, 0xffc8,
+ 0x0000, 0x1804, 0x0001, 0xffd8, 0x0010, 0x0009, 0x0013, 0x961b,
+ 0x0000, 0xff75, 0x0013, 0x961d, 0x0015, 0x00d1, 0x0000, 0x0200,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xbd88, 0x0000, 0x0008,
+ 0x0000, 0xff31, 0x0005, 0x00b0, 0x0010, 0x0009, 0x0015, 0x00b1,
+ 0x0010, 0x07d0, 0x0015, 0x0033, 0x0000, 0xb012, 0x001b, 0x862b,
+ 0x0013, 0x043b, 0x0015, 0x0044, 0x0000, 0x0008, 0x0005, 0x0098,
+ 0x0010, 0x0056, 0x0015, 0x0099, 0x0000, 0x9575, 0x0004, 0x07ea,
+ 0x0000, 0xb096, 0x0012, 0xb270, 0x0010, 0xff56, 0x0014, 0x080c,
+ 0x0010, 0xb052, 0x0010, 0xb153, 0x0000, 0xb6ff, 0x0011, 0xb2d0,
+ 0x0010, 0xff50, 0x0010, 0xb351, 0x0017, 0x4000, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0001, 0x1288, 0x0010, 0x0011, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0x1009, 0x000b, 0x8646, 0x0015, 0x000f,
+ 0x0000, 0x0001, 0x0010, 0xc014, 0x0000, 0x1213, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x1388, 0x0000, 0x0004, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xba09, 0x000b, 0x8652, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x1388, 0x0010, 0x0005, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0x1a09, 0x001b, 0x865a, 0x0012, 0x104b,
+ 0x001b, 0x1663, 0x0000, 0x1a30, 0x0005, 0x0031, 0x0000, 0x000b,
+ 0x0015, 0x0033, 0x0000, 0x1621, 0x000b, 0x8662, 0x0010, 0x15fe,
+ 0x000b, 0x6682, 0x0004, 0x06a9, 0x0002, 0x3a42, 0x000b, 0x16a8,
+ 0x0001, 0x10c8, 0x0010, 0x000f, 0x001b, 0x170b, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x1388, 0x0000, 0x0008, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x8672, 0x0011, 0xb0e8,
+ 0x0010, 0x0009, 0x0013, 0x1679, 0x0011, 0xb0e8, 0x0000, 0x0001,
+ 0x000b, 0x16a7, 0x0011, 0x1388, 0x0010, 0x000a, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x867e, 0x0002, 0xb04f,
+ 0x000b, 0x169e, 0x0003, 0x06a7, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x1388, 0x0010, 0x0003, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x000b, 0x8689, 0x0015, 0x0033, 0x0010, 0xc00a,
+ 0x000b, 0x868c, 0x0010, 0xb0fe, 0x0003, 0x6691, 0x0000, 0xb012,
+ 0x0013, 0x0693, 0x0010, 0xc012, 0x0010, 0xc011, 0x0015, 0x000f,
+ 0x0010, 0x0000, 0x0002, 0x3944, 0x0003, 0x169c, 0x0015, 0x0039,
+ 0x0000, 0x5040, 0x0015, 0x00b8, 0x0000, 0x0008, 0x0004, 0x0867,
+ 0x0000, 0xc013, 0x0003, 0x06a8, 0x0010, 0x02fe, 0x0013, 0x66a3,
+ 0x0015, 0x003a, 0x0010, 0x2020, 0x0003, 0x06a8, 0x0015, 0x003a,
+ 0x0000, 0x2000, 0x0015, 0x003a, 0x0010, 0x1010, 0x0014, 0x0853,
+ 0x0013, 0x0055, 0x0003, 0xb6a9, 0x0005, 0x002a, 0x0000, 0x0004,
+ 0x0000, 0xba30, 0x0005, 0x0031, 0x0010, 0x001b, 0x0015, 0x0033,
+ 0x0000, 0xb009, 0x001b, 0x86b1, 0x0000, 0xc02c, 0x0000, 0xb02d,
+ 0x0012, 0x104b, 0x0003, 0x16cc, 0x0000, 0x1a30, 0x0005, 0x0031,
+ 0x0000, 0x0023, 0x0015, 0x0033, 0x0000, 0xb129, 0x001b, 0x86bb,
+ 0x0000, 0xb120, 0x0010, 0xb221, 0x0000, 0xb322, 0x0000, 0xb423,
+ 0x0000, 0xb524, 0x0000, 0xc025, 0x0010, 0xb526, 0x0010, 0xc027,
+ 0x0010, 0xb516, 0x0010, 0xc017, 0x0000, 0xb518, 0x0000, 0xc019,
+ 0x0010, 0xc028, 0x0000, 0xc029, 0x0010, 0xc01e, 0x0013, 0x0702,
+ 0x0012, 0x1044, 0x0003, 0x16fc, 0x0002, 0x1034, 0x0000, 0xff10,
+ 0x0000, 0x1a30, 0x0005, 0x0031, 0x0000, 0x0002, 0x0015, 0x0033,
+ 0x0000, 0x1b29, 0x000b, 0x86d5, 0x0000, 0x1c30, 0x0000, 0x1b31,
+ 0x0015, 0x0033, 0x0000, 0xb131, 0x000b, 0x86da, 0x0002, 0x1f43,
+ 0x001b, 0x16e1, 0x0010, 0xb3b5, 0x0000, 0xb4b6, 0x0000, 0xc0b3,
+ 0x0010, 0xc0b4, 0x0000, 0xb120, 0x0010, 0xb221, 0x0000, 0xb322,
+ 0x0000, 0xb423, 0x0000, 0xb524, 0x0010, 0xb625, 0x0010, 0xb516,
+ 0x0000, 0xb617, 0x0000, 0x1826, 0x0000, 0x1927, 0x0000, 0x1a30,
+ 0x0005, 0x0031, 0x0010, 0x000f, 0x0015, 0x0033, 0x0000, 0xb011,
+ 0x001b, 0x86f0, 0x0000, 0xb028, 0x0000, 0xb129, 0x0012, 0x1e10,
+ 0x0010, 0xff1e, 0x0013, 0x6702, 0x0002, 0x1d00, 0x0010, 0xff1d,
+ 0x0014, 0x02a5, 0x0002, 0x3a42, 0x0003, 0x1702, 0x0003, 0x070a,
+ 0x0000, 0x1a30, 0x0005, 0x0031, 0x0000, 0x0002, 0x0015, 0x0033,
+ 0x0000, 0x1b79, 0x001b, 0x8701, 0x0003, 0xb702, 0x0005, 0x002a,
+ 0x0000, 0x0001, 0x0005, 0x0015, 0x0000, 0x0001, 0x0000, 0x1efe,
+ 0x0003, 0x670a, 0x0003, 0x0271, 0x0017, 0x4000, 0x0000, 0xba30,
+ 0x0005, 0x0031, 0x0010, 0x001b, 0x0015, 0x0033, 0x0010, 0xb051,
+ 0x001b, 0x8710, 0x0000, 0xb0a3, 0x0010, 0xb697, 0x0010, 0xb946,
+ 0x0015, 0x00a5, 0x0000, 0x0010, 0x0015, 0x0030, 0x0000, 0x0400,
+ 0x0011, 0x1388, 0x0000, 0x0002, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0000, 0xb509, 0x000b, 0x871d, 0x0014, 0x080c, 0x0004, 0x07fb,
+ 0x0012, 0xb470, 0x0010, 0xffb4, 0x0010, 0xb48e, 0x0010, 0xb08a,
+ 0x0010, 0xb18b, 0x0012, 0x104d, 0x0013, 0x1728, 0x0003, 0x0755,
+ 0x0012, 0x104b, 0x0003, 0x173b, 0x0005, 0x008c, 0x0010, 0x0829,
+ 0x0010, 0xc08d, 0x0001, 0xb2d8, 0x0010, 0x0600, 0x0010, 0xff88,
+ 0x0010, 0xb389, 0x0000, 0x1390, 0x0010, 0xb591, 0x0000, 0xc08f,
+ 0x0010, 0x1ab9, 0x0004, 0x04d6, 0x0003, 0x9736, 0x0010, 0xb092,
+ 0x0010, 0xb193, 0x0003, 0x9739, 0x0003, 0x0750, 0x0005, 0x008c,
+ 0x0000, 0x0809, 0x0015, 0x008d, 0x0000, 0x0008, 0x0001, 0xb2d8,
+ 0x0000, 0x0100, 0x0010, 0xff88, 0x0010, 0xb389, 0x0000, 0x1390,
+ 0x0010, 0xb591, 0x0000, 0xc08f, 0x0000, 0x1a30, 0x0005, 0x0031,
+ 0x0010, 0x000f, 0x0015, 0x0033, 0x0000, 0xb011, 0x000b, 0x874b,
+ 0x0013, 0x974c, 0x0000, 0xb192, 0x0000, 0xb093, 0x0013, 0x974f,
+ 0x0010, 0x19a1, 0x0000, 0x18a2, 0x0015, 0x00b1, 0x0010, 0x0096,
+ 0x0003, 0x07c6, 0x0000, 0xb590, 0x0010, 0x1391, 0x0001, 0x10c8,
+ 0x0010, 0x000f, 0x0001, 0xffe8, 0x0010, 0x0005, 0x0003, 0x177c,
+ 0x0001, 0xb2d8, 0x0000, 0x0700, 0x0010, 0xff88, 0x0010, 0xb389,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x1388, 0x0010, 0x0009,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009, 0x001b, 0x8767,
+ 0x0002, 0xb049, 0x0013, 0x176f, 0x0005, 0x008c, 0x0010, 0x0889,
+ 0x0015, 0x00b1, 0x0010, 0x0096, 0x0013, 0x0773, 0x0005, 0x008c,
+ 0x0010, 0x0898, 0x0015, 0x00b1, 0x0000, 0x0092, 0x0010, 0xc08d,
+ 0x0000, 0xc08f, 0x0013, 0x9775, 0x0000, 0xc092, 0x0010, 0xc093,
+ 0x0003, 0x9778, 0x0010, 0x19a1, 0x0000, 0x18a2, 0x0003, 0x07c6,
+ 0x0001, 0xb2d8, 0x0000, 0x0100, 0x0010, 0xff88, 0x0010, 0xb389,
+ 0x0005, 0x008c, 0x0010, 0x0880, 0x0015, 0x008d, 0x0000, 0x0008,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x1388, 0x0000, 0x000e,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb009, 0x000b, 0x878b,
+ 0x0010, 0xb08f, 0x0000, 0xb590, 0x0010, 0x1391, 0x0000, 0x1a30,
+ 0x0005, 0x0031, 0x0000, 0x000d, 0x0015, 0x0033, 0x0000, 0xb021,
+ 0x001b, 0x8794, 0x0003, 0x9795, 0x0010, 0xb392, 0x0010, 0xb293,
+ 0x0013, 0x9798, 0x0000, 0xb1a1, 0x0010, 0xb0a2, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x1388, 0x0000, 0x000b, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0010, 0xb211, 0x001b, 0x87a2, 0x0000, 0xb3ff,
+ 0x0001, 0xb080, 0x0000, 0xffb3, 0x000b, 0x27a9, 0x0002, 0xb200,
+ 0x0003, 0x07aa, 0x0010, 0xb2ff, 0x0011, 0xb180, 0x0010, 0xffb2,
+ 0x0011, 0x1388, 0x0000, 0x000b, 0x0000, 0xff31, 0x0015, 0x0033,
+ 0x0010, 0xb212, 0x000b, 0x87b1, 0x0015, 0x00b1, 0x0000, 0x0092,
+ 0x0002, 0x104c, 0x0003, 0x17c4, 0x0011, 0xc2e8, 0x0010, 0x000c,
+ 0x001b, 0x17bc, 0x0015, 0x00ff, 0x0000, 0x0800, 0x0013, 0x07c4,
+ 0x0011, 0xc2e8, 0x0000, 0x0020, 0x001b, 0x17c2, 0x0015, 0x00ff,
+ 0x0010, 0x1800, 0x0013, 0x07c4, 0x0015, 0x00ff, 0x0000, 0x1000,
+ 0x0011, 0xb1d0, 0x0010, 0xffb1, 0x0015, 0x009a, 0x0010, 0x0036,
+ 0x0005, 0x009b, 0x0000, 0x95d5, 0x0012, 0xd041, 0x000b, 0x17ca,
+ 0x0015, 0x00d1, 0x0010, 0x0202, 0x0013, 0x97ce, 0x0012, 0x104e,
+ 0x0003, 0x17d3, 0x0012, 0xb12f, 0x0010, 0xffb1, 0x0000, 0xb175,
+ 0x0003, 0x97d4, 0x0015, 0x00d1, 0x0000, 0x0200, 0x0001, 0x19c8,
+ 0x0010, 0xfff0, 0x000b, 0x17dd, 0x0015, 0x00b1, 0x0010, 0x07d0,
+ 0x0013, 0x07df, 0x0015, 0x00b1, 0x0000, 0x1b58, 0x0005, 0x00b0,
+ 0x0010, 0x0009, 0x0015, 0x0030, 0x0000, 0x0400, 0x0001, 0xbd88,
+ 0x0000, 0x000b, 0x0000, 0xff31, 0x0015, 0x0033, 0x0000, 0xb012,
+ 0x000b, 0x87e8, 0x0003, 0x06a8, 0x0000, 0xba30, 0x0005, 0x0031,
+ 0x0010, 0x0021, 0x0015, 0x0033, 0x0010, 0xb019, 0x001b, 0x87ef,
+ 0x0002, 0xb200, 0x0011, 0xffc8, 0x0010, 0x00ff, 0x0010, 0xffb2,
+ 0x0010, 0xb2b7, 0x0005, 0x0031, 0x0000, 0x0023, 0x0015, 0x0033,
+ 0x0010, 0xb20a, 0x000b, 0x87f9, 0x0017, 0x4000, 0x0000, 0xba30,
+ 0x0005, 0x0031, 0x0000, 0x0023, 0x0015, 0x0033, 0x0010, 0xb409,
+ 0x000b, 0x8800, 0x0002, 0xb400, 0x0011, 0xffc8, 0x0010, 0x00ff,
+ 0x0010, 0xffb4, 0x0010, 0xb4b7, 0x0005, 0x0031, 0x0000, 0x0023,
+ 0x0015, 0x0033, 0x0010, 0xb40a, 0x000b, 0x880a, 0x0017, 0x4000,
+ 0x0000, 0xba30, 0x0001, 0xc7c8, 0x0000, 0x0020, 0x000b, 0x1818,
+ 0x0005, 0x0031, 0x0010, 0x0028, 0x0015, 0x0033, 0x0010, 0xb209,
+ 0x000b, 0x8814, 0x0011, 0xb2c8, 0x0000, 0xff80, 0x0013, 0x181b,
+ 0x0010, 0xc4b0, 0x0010, 0xc5b1, 0x0003, 0x081d, 0x0010, 0xc6b1,
+ 0x0000, 0xc0b0, 0x0005, 0x0031, 0x0000, 0x0004, 0x0015, 0x0033,
+ 0x0010, 0xb211, 0x000b, 0x8821, 0x0017, 0x4000, 0x0015, 0x00b8,
+ 0x0010, 0x0009, 0x0015, 0x003a, 0x0010, 0x0707, 0x0004, 0x0867,
+ 0x0013, 0x002d, 0x0015, 0x00b8, 0x0010, 0x0009, 0x0015, 0x003a,
+ 0x0010, 0x0707, 0x0013, 0x0867, 0x0014, 0x0114, 0x0015, 0x0030,
+ 0x0000, 0x0400, 0x0011, 0x0d88, 0x0000, 0x0004, 0x0000, 0xff31,
+ 0x0015, 0x0033, 0x0000, 0xba09, 0x000b, 0x8836, 0x0004, 0x07ea,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88, 0x0000, 0x0010,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb20a, 0x000b, 0x883f,
+ 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x0d88, 0x0010, 0x0011,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0x0309, 0x000b, 0x8847,
+ 0x0002, 0x0327, 0x0010, 0xffb2, 0x0011, 0x0d88, 0x0010, 0x0011,
+ 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb20a, 0x001b, 0x884f,
+ 0x0015, 0x00b8, 0x0010, 0x0006, 0x0013, 0x0867, 0x0004, 0x0126,
+ 0x0004, 0x07ea, 0x0015, 0x0030, 0x0000, 0x0400, 0x0011, 0x1388,
+ 0x0000, 0x0010, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb20a,
+ 0x000b, 0x885c, 0x0012, 0x1027, 0x0010, 0xffb2, 0x0011, 0x1388,
+ 0x0010, 0x0011, 0x0000, 0xff31, 0x0015, 0x0033, 0x0010, 0xb20a,
+ 0x001b, 0x8864, 0x0015, 0x00b8, 0x0000, 0x0007, 0x0003, 0x4867,
+ 0x0000, 0xb838, 0x0017, 0x4000, 0xa2e7, 0x24ad
+};
+unsigned short xseqipx_code_length01 = 0x10d6;
diff --git a/drivers/scsi/qla2xxx/ql6312.c b/drivers/scsi/qla2xxx/ql6312.c
new file mode 100644
index 000000000000..59268eb80acd
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql6312.c
@@ -0,0 +1,102 @@
+/*
+ * QLogic ISP6312 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation (www.qlogic.com)
+ *
+ * Released under GPL v2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "qla_def.h"
+
+static char qla_driver_name[] = "qla6312";
+
+extern unsigned char fw2300flx_version[];
+extern unsigned char fw2300flx_version_str[];
+extern unsigned short fw2300flx_addr01;
+extern unsigned short fw2300flx_code01[];
+extern unsigned short fw2300flx_length01;
+
+static struct qla_fw_info qla_fw_tbl[] = {
+ {
+ .addressing = FW_INFO_ADDR_NORMAL,
+ .fwcode = &fw2300flx_code01[0],
+ .fwlen = &fw2300flx_length01,
+ .fwstart = &fw2300flx_addr01,
+ },
+ { FW_INFO_ADDR_NOMORE, },
+};
+
+static struct qla_board_info qla_board_tbl[] = {
+ {
+ .drv_name = qla_driver_name,
+ .isp_name = "ISP6312",
+ .fw_info = qla_fw_tbl,
+ },
+ {
+ .drv_name = qla_driver_name,
+ .isp_name = "ISP6322",
+ .fw_info = qla_fw_tbl,
+ },
+};
+
+static struct pci_device_id qla6312_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP6312,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl[0],
+ },
+ {
+ .vendor = PCI_VENDOR_ID_QLOGIC,
+ .device = PCI_DEVICE_ID_QLOGIC_ISP6322,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long)&qla_board_tbl[1],
+ },
+ {0, 0},
+};
+MODULE_DEVICE_TABLE(pci, qla6312_pci_tbl);
+
+static int __devinit
+qla6312_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ return qla2x00_probe_one(pdev,
+ (struct qla_board_info *)id->driver_data);
+}
+
+static void __devexit
+qla6312_remove_one(struct pci_dev *pdev)
+{
+ qla2x00_remove_one(pdev);
+}
+
+static struct pci_driver qla6312_pci_driver = {
+ .name = "qla6312",
+ .id_table = qla6312_pci_tbl,
+ .probe = qla6312_probe_one,
+ .remove = __devexit_p(qla6312_remove_one),
+};
+
+static int __init
+qla6312_init(void)
+{
+ return pci_module_init(&qla6312_pci_driver);
+}
+
+static void __exit
+qla6312_exit(void)
+{
+ pci_unregister_driver(&qla6312_pci_driver);
+}
+
+module_init(qla6312_init);
+module_exit(qla6312_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic ISP63xx FC-SCSI Host Bus Adapter driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff --git a/drivers/scsi/qla2xxx/ql6312_fw.c b/drivers/scsi/qla2xxx/ql6312_fw.c
new file mode 100644
index 000000000000..63d827d7da07
--- /dev/null
+++ b/drivers/scsi/qla2xxx/ql6312_fw.c
@@ -0,0 +1,7147 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+/*
+ * Firmware Version 3.03.08 (10:02 Nov 12, 2004)
+ */
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300flx_version = 3*1024+3;
+#else
+unsigned short risc_code_version = 3*1024+3;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned char fw2300flx_version_str[] = {3, 3, 8};
+#else
+unsigned char firmware_version[] = {3, 3, 8};
+#endif
+
+#ifdef UNIQUE_FW_NAME
+#define fw2300flx_VERSION_STRING "3.03.08"
+#else
+#define FW_VERSION_STRING "3.03.08"
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300flx_addr01 = 0x0800 ;
+#else
+unsigned short risc_code_addr01 = 0x0800 ;
+#endif
+
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300flx_code01[] = {
+#else
+unsigned short risc_code01[] = {
+#endif
+ 0x0470, 0x0000, 0x0000, 0xdd79, 0x0000, 0x0003, 0x0003, 0x0008,
+ 0x0317, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2032, 0x3030,
+ 0x3120, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3233, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x332e, 0x3033, 0x2e30, 0x3820, 0x2020, 0x2020, 0x2400, 0x20a9,
+ 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2200, 0x20a9, 0x000f,
+ 0x2001, 0x0000, 0x400f, 0x2091, 0x2400, 0x20a9, 0x000f, 0x2001,
+ 0x0000, 0x400f, 0x2091, 0x2600, 0x20a9, 0x000f, 0x2001, 0x0000,
+ 0x400f, 0x2091, 0x2800, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f,
+ 0x2091, 0x2a00, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091,
+ 0x2c00, 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2e00,
+ 0x20a9, 0x000f, 0x2001, 0x0000, 0x400f, 0x2091, 0x2000, 0x2001,
+ 0x0000, 0x20c1, 0x0004, 0x20c9, 0x1bff, 0x2059, 0x0000, 0x2b78,
+ 0x7883, 0x0004, 0x2089, 0x2cff, 0x2051, 0x1800, 0x2a70, 0x20e1,
+ 0x0001, 0x20e9, 0x0001, 0x2009, 0x0000, 0x080c, 0x0e75, 0x2029,
+ 0x2480, 0x2031, 0xffff, 0x2039, 0x2450, 0x2021, 0x0050, 0x20e9,
+ 0x0001, 0x20a1, 0x0000, 0x20a9, 0x0800, 0x900e, 0x4104, 0x20e9,
+ 0x0001, 0x20a1, 0x1000, 0x900e, 0x2001, 0x0cc0, 0x9084, 0x0fff,
+ 0x20a8, 0x4104, 0x2001, 0x0000, 0x9086, 0x0000, 0x0120, 0x21a8,
+ 0x4104, 0x8001, 0x1de0, 0x756a, 0x766e, 0x7766, 0x7472, 0x7476,
+ 0x00e6, 0x2071, 0x1aa2, 0x2472, 0x00ee, 0x20a1, 0x1cd0, 0x716c,
+ 0x810d, 0x810d, 0x810d, 0x810d, 0x918c, 0x000f, 0x2001, 0x0001,
+ 0x9112, 0x900e, 0x21a8, 0x4104, 0x8211, 0x1de0, 0x716c, 0x3400,
+ 0x8001, 0x9102, 0x0120, 0x0218, 0x20a8, 0x900e, 0x4104, 0x2009,
+ 0x1800, 0x810d, 0x810d, 0x810d, 0x810d, 0x810d, 0x918c, 0x001f,
+ 0x2001, 0x0001, 0x9112, 0x20e9, 0x0001, 0x20a1, 0x0800, 0x900e,
+ 0x20a9, 0x0800, 0x4104, 0x8211, 0x1dd8, 0x080c, 0x0f49, 0x080c,
+ 0x5f39, 0x080c, 0xa079, 0x080c, 0x1100, 0x080c, 0x12f8, 0x080c,
+ 0x1af5, 0x080c, 0x0d8c, 0x080c, 0x1085, 0x080c, 0x33e9, 0x080c,
+ 0x7518, 0x080c, 0x687e, 0x080c, 0x8215, 0x080c, 0x23bd, 0x080c,
+ 0x8526, 0x080c, 0x7b99, 0x080c, 0x21e9, 0x080c, 0x231d, 0x080c,
+ 0x23b2, 0x2091, 0x3009, 0x7883, 0x0000, 0x1004, 0x091d, 0x7880,
+ 0x9086, 0x0002, 0x1190, 0x7883, 0x4000, 0x7837, 0x4000, 0x7833,
+ 0x0010, 0x0e04, 0x0911, 0x2091, 0x5000, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x2071, 0x1800, 0x7003,
+ 0x0000, 0x2071, 0x1800, 0x7000, 0x908e, 0x0003, 0x1168, 0x080c,
+ 0x4be4, 0x080c, 0x3410, 0x080c, 0x7580, 0x080c, 0x6d2e, 0x080c,
+ 0x823e, 0x080c, 0x2c2c, 0x0c68, 0x000b, 0x0c88, 0x0940, 0x0941,
+ 0x0ad8, 0x093e, 0x0b8f, 0x0d8b, 0x0d8b, 0x0d8b, 0x080c, 0x0dfa,
+ 0x0005, 0x0126, 0x00f6, 0x2091, 0x8000, 0x7000, 0x9086, 0x0001,
+ 0x1904, 0x0aab, 0x080c, 0x0eb7, 0x080c, 0x7207, 0x0150, 0x080c,
+ 0x722a, 0x15a0, 0x2079, 0x0100, 0x7828, 0x9085, 0x1800, 0x782a,
+ 0x0468, 0x080c, 0x7127, 0x7000, 0x9086, 0x0001, 0x1904, 0x0aab,
+ 0x7094, 0x9086, 0x0028, 0x1904, 0x0aab, 0x080c, 0x81fe, 0x080c,
+ 0x81f0, 0x2001, 0x0161, 0x2003, 0x0001, 0x2079, 0x0100, 0x7827,
+ 0xffff, 0x7a28, 0x9295, 0x5e2f, 0x7a2a, 0x2011, 0x7076, 0x080c,
+ 0x82da, 0x2011, 0x7069, 0x080c, 0x83ae, 0x2011, 0x5d94, 0x080c,
+ 0x82da, 0x2011, 0x8030, 0x901e, 0x7392, 0x04d0, 0x080c, 0x5641,
+ 0x2079, 0x0100, 0x7844, 0x9005, 0x1904, 0x0aab, 0x2011, 0x5d94,
+ 0x080c, 0x82da, 0x2011, 0x7076, 0x080c, 0x82da, 0x2011, 0x7069,
+ 0x080c, 0x83ae, 0x2001, 0x0265, 0x2001, 0x0205, 0x2003, 0x0000,
+ 0x7840, 0x9084, 0xfffb, 0x7842, 0x2001, 0x197e, 0x2004, 0x9005,
+ 0x1140, 0x00c6, 0x2061, 0x0100, 0x080c, 0x5ee1, 0x00ce, 0x0804,
+ 0x0aab, 0x780f, 0x006b, 0x7a28, 0x080c, 0x720f, 0x0118, 0x9295,
+ 0x5e2f, 0x0010, 0x9295, 0x402f, 0x7a2a, 0x2011, 0x8010, 0x73d4,
+ 0x2001, 0x197f, 0x2003, 0x0001, 0x080c, 0x2a89, 0x080c, 0x4b1f,
+ 0x7244, 0xc284, 0x7246, 0x2001, 0x180c, 0x200c, 0xc1ac, 0xc1cc,
+ 0x2102, 0x080c, 0x9904, 0x2011, 0x0004, 0x080c, 0xbe47, 0x080c,
+ 0x66c2, 0x080c, 0x7207, 0x1120, 0x080c, 0x2af6, 0x02e0, 0x0400,
+ 0x080c, 0x5ee8, 0x0140, 0x7093, 0x0001, 0x70cf, 0x0000, 0x080c,
+ 0x580e, 0x0804, 0x0aab, 0x080c, 0x55db, 0xd094, 0x0188, 0x2011,
+ 0x180c, 0x2204, 0xc0cd, 0x2012, 0x080c, 0x55df, 0xd0d4, 0x1118,
+ 0x080c, 0x2af6, 0x1270, 0x2011, 0x180c, 0x2204, 0xc0bc, 0x0088,
+ 0x080c, 0x55df, 0xd0d4, 0x1db8, 0x2011, 0x180c, 0x2204, 0xc0bd,
+ 0x0040, 0x2011, 0x180c, 0x2204, 0xc0bd, 0x2012, 0x080c, 0x67bb,
+ 0x0008, 0x2012, 0x080c, 0x6781, 0x0120, 0x7a0c, 0xc2b4, 0x7a0e,
+ 0x00a8, 0x707b, 0x0000, 0x080c, 0x7207, 0x1130, 0x70ac, 0x9005,
+ 0x1168, 0x080c, 0xc28a, 0x0050, 0x080c, 0xc28a, 0x70d8, 0xd09c,
+ 0x1128, 0x70ac, 0x9005, 0x0110, 0x080c, 0x5ebe, 0x70e3, 0x0000,
+ 0x70df, 0x0000, 0x70a3, 0x0000, 0x080c, 0x2afe, 0x0228, 0x2011,
+ 0x0101, 0x2204, 0xc0c4, 0x2012, 0x72d8, 0x080c, 0x7207, 0x1178,
+ 0x9016, 0x0016, 0x2009, 0x0002, 0x2019, 0x1945, 0x211a, 0x001e,
+ 0x705b, 0xffff, 0x705f, 0x00ef, 0x707f, 0x0000, 0x0020, 0x2019,
+ 0x1945, 0x201b, 0x0000, 0x2079, 0x185b, 0x7804, 0xd0ac, 0x0108,
+ 0xc295, 0x72da, 0x080c, 0x7207, 0x0118, 0x9296, 0x0004, 0x0548,
+ 0x2011, 0x0001, 0x080c, 0xbe47, 0x70a7, 0x0000, 0x70ab, 0xffff,
+ 0x7003, 0x0002, 0x2079, 0x0100, 0x7827, 0x0003, 0x7828, 0x9085,
+ 0x0003, 0x782a, 0x00fe, 0x080c, 0x2f6c, 0x2011, 0x0005, 0x080c,
+ 0x9a0f, 0x080c, 0x8c10, 0x080c, 0x7207, 0x0148, 0x00c6, 0x2061,
+ 0x0100, 0x0016, 0x2009, 0x0002, 0x61e2, 0x001e, 0x00ce, 0x012e,
+ 0x0420, 0x70a7, 0x0000, 0x70ab, 0xffff, 0x7003, 0x0002, 0x00f6,
+ 0x2079, 0x0100, 0x7827, 0x0003, 0x7828, 0x9085, 0x0003, 0x782a,
+ 0x00fe, 0x2011, 0x0005, 0x080c, 0x9a0f, 0x080c, 0x8c10, 0x080c,
+ 0x7207, 0x0148, 0x00c6, 0x2061, 0x0100, 0x0016, 0x2009, 0x0002,
+ 0x61e2, 0x001e, 0x00ce, 0x00fe, 0x012e, 0x0005, 0x00c6, 0x00b6,
+ 0x080c, 0x7207, 0x1118, 0x20a9, 0x0800, 0x0010, 0x20a9, 0x0782,
+ 0x080c, 0x7207, 0x1110, 0x900e, 0x0010, 0x2009, 0x007e, 0x86ff,
+ 0x0138, 0x9180, 0x1000, 0x2004, 0x905d, 0x0110, 0xb800, 0xd0bc,
+ 0x090c, 0x3286, 0x8108, 0x1f04, 0x0abf, 0x707b, 0x0000, 0x707c,
+ 0x9084, 0x00ff, 0x707e, 0x70af, 0x0000, 0x00be, 0x00ce, 0x0005,
+ 0x00b6, 0x0126, 0x2091, 0x8000, 0x7000, 0x9086, 0x0002, 0x1904,
+ 0x0b8c, 0x70a8, 0x9086, 0xffff, 0x0130, 0x080c, 0x2f6c, 0x080c,
+ 0x8c10, 0x0804, 0x0b8c, 0x70d8, 0xd0ac, 0x1110, 0xd09c, 0x0540,
+ 0xd084, 0x0530, 0x0006, 0x2001, 0x0103, 0x2003, 0x002b, 0x000e,
+ 0xd08c, 0x01f0, 0x70dc, 0x9086, 0xffff, 0x01b0, 0x080c, 0x30f7,
+ 0x080c, 0x8c10, 0x70d8, 0xd094, 0x1904, 0x0b8c, 0x2011, 0x0001,
+ 0x080c, 0xc539, 0x0110, 0x2011, 0x0003, 0x901e, 0x080c, 0x3131,
+ 0x080c, 0x8c10, 0x0804, 0x0b8c, 0x70e0, 0x9005, 0x1904, 0x0b8c,
+ 0x70a4, 0x9005, 0x1904, 0x0b8c, 0x70d8, 0xd0a4, 0x0118, 0xd0b4,
+ 0x0904, 0x0b8c, 0x080c, 0x6781, 0x1904, 0x0b8c, 0x080c, 0x67d4,
+ 0x1904, 0x0b8c, 0x080c, 0x67bb, 0x01c0, 0x0156, 0x00c6, 0x20a9,
+ 0x007f, 0x900e, 0x0016, 0x080c, 0x649f, 0x1118, 0xb800, 0xd0ec,
+ 0x1138, 0x001e, 0x8108, 0x1f04, 0x0b32, 0x00ce, 0x015e, 0x0028,
+ 0x001e, 0x00ce, 0x015e, 0x0804, 0x0b8c, 0x0006, 0x2001, 0x0103,
+ 0x2003, 0x006b, 0x000e, 0x2011, 0x198b, 0x080c, 0x0fb9, 0x2011,
+ 0x19a5, 0x080c, 0x0fb9, 0x7030, 0xc08c, 0x7032, 0x7003, 0x0003,
+ 0x70ab, 0xffff, 0x080c, 0x0e99, 0x9006, 0x080c, 0x2717, 0x0036,
+ 0x0046, 0x2019, 0xffff, 0x2021, 0x0006, 0x080c, 0x4cbc, 0x004e,
+ 0x003e, 0x00f6, 0x2079, 0x0100, 0x080c, 0x722a, 0x0150, 0x080c,
+ 0x7207, 0x7828, 0x0118, 0x9084, 0xe1ff, 0x0010, 0x9084, 0xffdf,
+ 0x782a, 0x00fe, 0x2001, 0x19c0, 0x2004, 0x9086, 0x0005, 0x1120,
+ 0x2011, 0x0000, 0x080c, 0x9a0f, 0x2011, 0x0000, 0x080c, 0x9a19,
+ 0x080c, 0x8c10, 0x080c, 0x8ced, 0x012e, 0x00be, 0x0005, 0x0016,
+ 0x0046, 0x00f6, 0x0126, 0x2091, 0x8000, 0x2079, 0x0100, 0x7904,
+ 0x918c, 0xfffd, 0x7906, 0x2009, 0x00f7, 0x080c, 0x5ea7, 0x7940,
+ 0x918c, 0x0010, 0x7942, 0x7924, 0xd1b4, 0x0110, 0x7827, 0x0040,
+ 0xd19c, 0x0110, 0x7827, 0x0008, 0x0006, 0x0036, 0x0156, 0x2001,
+ 0x0100, 0x2004, 0x9086, 0x000a, 0x1904, 0x0c23, 0x7954, 0xd1ac,
+ 0x1904, 0x0c23, 0x2001, 0x197f, 0x2004, 0x9005, 0x1518, 0x080c,
+ 0x2b98, 0x1148, 0x2001, 0x0001, 0x080c, 0x2ab8, 0x2001, 0x0001,
+ 0x080c, 0x2a9b, 0x00b8, 0x080c, 0x2ba0, 0x1138, 0x9006, 0x080c,
+ 0x2ab8, 0x9006, 0x080c, 0x2a9b, 0x0068, 0x080c, 0x2ba8, 0x1d50,
+ 0x2001, 0x1970, 0x2004, 0xd0fc, 0x0108, 0x0020, 0x080c, 0x28b2,
+ 0x0804, 0x0d33, 0x080c, 0x7218, 0x0148, 0x080c, 0x722a, 0x1118,
+ 0x080c, 0x7513, 0x0050, 0x080c, 0x720f, 0x0dd0, 0x080c, 0x750e,
+ 0x080c, 0x7504, 0x080c, 0x7127, 0x0058, 0x080c, 0x7207, 0x0140,
+ 0x2009, 0x00f8, 0x080c, 0x5ea7, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x20a9, 0x09c4, 0x7820, 0xd09c, 0x1138, 0x080c, 0x7207, 0x0138,
+ 0x7824, 0xd0ac, 0x1904, 0x0d38, 0x1f04, 0x0c02, 0x0070, 0x7824,
+ 0x080c, 0x7221, 0x0118, 0xd0ac, 0x1904, 0x0d38, 0x9084, 0x1800,
+ 0x0d98, 0x7003, 0x0001, 0x0804, 0x0d38, 0x2001, 0x0001, 0x080c,
+ 0x2717, 0x0804, 0x0d5a, 0x2001, 0x197f, 0x2004, 0x9005, 0x1518,
+ 0x080c, 0x2b98, 0x1148, 0x2001, 0x0001, 0x080c, 0x2ab8, 0x2001,
+ 0x0001, 0x080c, 0x2a9b, 0x00b8, 0x080c, 0x2ba0, 0x1138, 0x9006,
+ 0x080c, 0x2ab8, 0x9006, 0x080c, 0x2a9b, 0x0068, 0x080c, 0x2ba8,
+ 0x1d50, 0x2001, 0x1970, 0x2004, 0xd0fc, 0x0108, 0x0020, 0x080c,
+ 0x28b2, 0x0804, 0x0d33, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x01f8, 0x7850, 0x9085, 0x0040, 0x7852, 0x7938, 0x7850, 0x9084,
+ 0xfbcf, 0x7852, 0x080c, 0x2bb0, 0x9085, 0x2000, 0x7852, 0x793a,
+ 0x20a9, 0x0046, 0x1d04, 0x0c62, 0x080c, 0x838e, 0x1f04, 0x0c62,
+ 0x7850, 0x9085, 0x0400, 0x9084, 0xdfbf, 0x7852, 0x793a, 0x0060,
+ 0x080c, 0x2cc2, 0x080c, 0x2cf5, 0x20a9, 0x003a, 0x1d04, 0x0c76,
+ 0x080c, 0x838e, 0x1f04, 0x0c76, 0x080c, 0x7218, 0x0148, 0x080c,
+ 0x722a, 0x1118, 0x080c, 0x7513, 0x0050, 0x080c, 0x720f, 0x0dd0,
+ 0x080c, 0x750e, 0x080c, 0x7504, 0x080c, 0x7127, 0x0020, 0x2009,
+ 0x00f8, 0x080c, 0x5ea7, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x0168, 0x20a9, 0x0028, 0xa001, 0x1f04, 0x0c9b, 0x7850, 0x9085,
+ 0x1400, 0x7852, 0x080c, 0x7207, 0x0158, 0x0030, 0x7850, 0xc0e5,
+ 0x7852, 0x080c, 0x7207, 0x0120, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x2021, 0xe678, 0x2019, 0xea60, 0x0d0c, 0x838e, 0x7820, 0xd09c,
+ 0x1590, 0x080c, 0x7207, 0x0904, 0x0d17, 0x7824, 0xd0ac, 0x1904,
+ 0x0d38, 0x080c, 0x722a, 0x1538, 0x0046, 0x2021, 0x0320, 0x8421,
+ 0x1df0, 0x004e, 0x7827, 0x1800, 0x080c, 0x2bb0, 0x7824, 0x9084,
+ 0x1800, 0x1168, 0x9484, 0x0fff, 0x1140, 0x2001, 0x1810, 0x2004,
+ 0x9084, 0x9000, 0x0110, 0x080c, 0x0d68, 0x8421, 0x1160, 0x1d04,
+ 0x0ce3, 0x080c, 0x838e, 0x080c, 0x750e, 0x080c, 0x7504, 0x7003,
+ 0x0001, 0x0804, 0x0d38, 0x8319, 0x1938, 0x2001, 0x0100, 0x2004,
+ 0x9086, 0x000a, 0x1140, 0x2001, 0x1810, 0x2004, 0x9084, 0x9000,
+ 0x0110, 0x080c, 0x0d68, 0x1d04, 0x0cff, 0x080c, 0x838e, 0x2009,
+ 0x1973, 0x2104, 0x9005, 0x0118, 0x8001, 0x200a, 0x1178, 0x200b,
+ 0x000a, 0x7827, 0x0048, 0x20a9, 0x0002, 0x080c, 0x2b91, 0x7924,
+ 0x080c, 0x2bb0, 0xd19c, 0x0110, 0x080c, 0x2a89, 0x00e0, 0x080c,
+ 0x7218, 0x1140, 0x94a2, 0x03e8, 0x1128, 0x080c, 0x71df, 0x7003,
+ 0x0001, 0x00b0, 0x7827, 0x1800, 0x080c, 0x2bb0, 0x7824, 0x080c,
+ 0x7221, 0x0110, 0xd0ac, 0x1160, 0x9084, 0x1800, 0x0904, 0x0ceb,
+ 0x7003, 0x0001, 0x0028, 0x2001, 0x0001, 0x080c, 0x2717, 0x00c0,
+ 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x1118, 0x7850, 0xc0e4,
+ 0x7852, 0x2009, 0x180c, 0x210c, 0xd19c, 0x1120, 0x7904, 0x918d,
+ 0x0002, 0x7906, 0x7827, 0x0048, 0x7828, 0x9085, 0x0028, 0x782a,
+ 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0120, 0x7850, 0x9085,
+ 0x0400, 0x7852, 0x2001, 0x197f, 0x2003, 0x0000, 0x9006, 0x78f2,
+ 0x015e, 0x003e, 0x000e, 0x012e, 0x00fe, 0x004e, 0x001e, 0x0005,
+ 0x0006, 0x0016, 0x0036, 0x0046, 0x00b6, 0x00c6, 0x00d6, 0x00e6,
+ 0x00f6, 0x0156, 0x0069, 0x0d0c, 0x838e, 0x015e, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x00be, 0x004e, 0x003e, 0x001e, 0x000e, 0x0005,
+ 0x00e6, 0x2071, 0x189c, 0x7004, 0x9086, 0x0001, 0x1110, 0x080c,
+ 0x3410, 0x00ee, 0x0005, 0x0005, 0x2a70, 0x2061, 0x1983, 0x2063,
+ 0x0003, 0x6007, 0x0003, 0x600b, 0x0008, 0x600f, 0x0317, 0x2001,
+ 0x1954, 0x900e, 0x2102, 0x7192, 0x2001, 0x0100, 0x2004, 0x9082,
+ 0x0002, 0x0218, 0x705b, 0xffff, 0x0008, 0x715a, 0x7063, 0xffff,
+ 0x717a, 0x717e, 0x080c, 0xc28a, 0x70e7, 0x00c0, 0x2061, 0x1944,
+ 0x6003, 0x0909, 0x6106, 0x600b, 0x8800, 0x600f, 0x0200, 0x6013,
+ 0x00ff, 0x6017, 0x000f, 0x611a, 0x601f, 0x07d0, 0x2061, 0x194c,
+ 0x6003, 0x8000, 0x6106, 0x610a, 0x600f, 0x0200, 0x6013, 0x00ff,
+ 0x6116, 0x601b, 0x0001, 0x611e, 0x2061, 0x1961, 0x6003, 0x514c,
+ 0x6007, 0x4f47, 0x600b, 0x4943, 0x600f, 0x2020, 0x2001, 0x182b,
+ 0x2102, 0x0005, 0x9016, 0x080c, 0x649f, 0x1178, 0xb804, 0x90c4,
+ 0x00ff, 0x98c6, 0x0006, 0x0128, 0x90c4, 0xff00, 0x98c6, 0x0600,
+ 0x1120, 0x9186, 0x0080, 0x0108, 0x8210, 0x8108, 0x9186, 0x0800,
+ 0x1d50, 0x2208, 0x0005, 0x2091, 0x8000, 0x2079, 0x0000, 0x000e,
+ 0x00f6, 0x0010, 0x2091, 0x8000, 0x0e04, 0x0dfc, 0x0006, 0x0016,
+ 0x2001, 0x8002, 0x0006, 0x2079, 0x0000, 0x000e, 0x7882, 0x7836,
+ 0x001e, 0x798e, 0x000e, 0x788a, 0x000e, 0x7886, 0x3900, 0x789a,
+ 0x7833, 0x0012, 0x2091, 0x5000, 0x0156, 0x00d6, 0x0036, 0x0026,
+ 0x2079, 0x0300, 0x2069, 0x1a7c, 0x7a08, 0x226a, 0x2069, 0x1a7d,
+ 0x7a18, 0x226a, 0x8d68, 0x7a1c, 0x226a, 0x782c, 0x2019, 0x1a8a,
+ 0x201a, 0x2019, 0x1a8d, 0x9016, 0x7808, 0xd09c, 0x0168, 0x7820,
+ 0x201a, 0x8210, 0x8318, 0x9386, 0x1aa2, 0x0108, 0x0ca8, 0x7808,
+ 0xd09c, 0x0110, 0x2011, 0xdead, 0x2019, 0x1a8b, 0x782c, 0x201a,
+ 0x8318, 0x221a, 0x7803, 0x0000, 0x2069, 0x1a5c, 0x901e, 0x20a9,
+ 0x0020, 0x7b26, 0x7a28, 0x226a, 0x8d68, 0x8318, 0x1f04, 0x0e49,
+ 0x002e, 0x003e, 0x00de, 0x015e, 0x2079, 0x1800, 0x7803, 0x0005,
+ 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x0180, 0x2001,
+ 0x19f1, 0x2004, 0x9005, 0x0128, 0x2001, 0x008b, 0x2004, 0xd0fc,
+ 0x0dd8, 0x2001, 0x008a, 0x2003, 0x0002, 0x2003, 0x1001, 0x080c,
+ 0x55ea, 0x1108, 0x0099, 0x0cd8, 0x0005, 0x918c, 0x03ff, 0x2001,
+ 0x0003, 0x2004, 0x9084, 0x0600, 0x1118, 0x918d, 0x6c00, 0x0010,
+ 0x918d, 0x6400, 0x2001, 0x017f, 0x2102, 0x0005, 0x0026, 0x0126,
+ 0x2011, 0x0080, 0x080c, 0x0f11, 0x20a9, 0x0900, 0x080c, 0x0f32,
+ 0x2011, 0x0040, 0x080c, 0x0f11, 0x20a9, 0x0900, 0x080c, 0x0f32,
+ 0x0c78, 0x0026, 0x080c, 0x0f1e, 0x1118, 0x2011, 0x0040, 0x0098,
+ 0x2011, 0x010e, 0x2214, 0x9294, 0x0007, 0x9296, 0x0007, 0x0118,
+ 0x2011, 0xa880, 0x0010, 0x2011, 0x6840, 0xd0e4, 0x70eb, 0x0000,
+ 0x1120, 0x70eb, 0x0fa0, 0x080c, 0x0f23, 0x002e, 0x0005, 0x0026,
+ 0x080c, 0x0f1e, 0x0128, 0xd0a4, 0x1138, 0x2011, 0xcdd5, 0x0010,
+ 0x2011, 0x0080, 0x080c, 0x0f23, 0x002e, 0x0005, 0x0026, 0x70eb,
+ 0x0000, 0x080c, 0x0f1e, 0x1148, 0x080c, 0x2ba8, 0x1118, 0x2011,
+ 0x8484, 0x0058, 0x2011, 0x8282, 0x0040, 0x080c, 0x2ba8, 0x1118,
+ 0x2011, 0xcdc5, 0x0010, 0x2011, 0xcac2, 0x080c, 0x0f23, 0x002e,
+ 0x0005, 0x00e6, 0x0006, 0x2071, 0x1800, 0xd0b4, 0x70e4, 0x1110,
+ 0xc0e4, 0x0048, 0x0006, 0x3b00, 0x9084, 0xff3f, 0x20d8, 0x000e,
+ 0x70eb, 0x0000, 0xc0e5, 0x0079, 0x000e, 0x00ee, 0x0005, 0x00e6,
+ 0x2071, 0x1800, 0xd0e4, 0x70e4, 0x1110, 0xc0dc, 0x0008, 0xc0dd,
+ 0x0011, 0x00ee, 0x0005, 0x70e6, 0x7000, 0x9084, 0x0007, 0x000b,
+ 0x0005, 0x0ee0, 0x0eb7, 0x0eb7, 0x0e99, 0x0ec6, 0x0eb7, 0x0eb7,
+ 0x0ec6, 0x0016, 0x3b08, 0x3a00, 0x9104, 0x918d, 0x00c0, 0x21d8,
+ 0x9084, 0xff3f, 0x9205, 0x20d0, 0x001e, 0x0005, 0x2001, 0x1839,
+ 0x2004, 0xd0dc, 0x0005, 0x9e86, 0x1800, 0x190c, 0x0dfa, 0x70e4,
+ 0xd0e4, 0x0108, 0xc2e5, 0x72e6, 0xd0e4, 0x1118, 0x9294, 0x00c0,
+ 0x0c01, 0x0005, 0x1d04, 0x0f32, 0x2091, 0x6000, 0x1f04, 0x0f32,
+ 0x0005, 0x890e, 0x810e, 0x810f, 0x9194, 0x003f, 0x918c, 0xffc0,
+ 0x0005, 0x0006, 0x2200, 0x914d, 0x894f, 0x894d, 0x894d, 0x000e,
+ 0x0005, 0x01d6, 0x0146, 0x0036, 0x0096, 0x2061, 0x188b, 0x600b,
+ 0x0000, 0x600f, 0x0000, 0x6003, 0x0000, 0x6007, 0x0000, 0x2009,
+ 0xffc0, 0x2105, 0x0006, 0x2001, 0xaaaa, 0x200f, 0x2019, 0x5555,
+ 0x9016, 0x2049, 0x0bff, 0xab02, 0xa001, 0xa001, 0xa800, 0x9306,
+ 0x1138, 0x2105, 0x9306, 0x0120, 0x8210, 0x99c8, 0x0400, 0x0c98,
+ 0x000e, 0x200f, 0x2001, 0x189b, 0x928a, 0x000e, 0x1638, 0x928a,
+ 0x0006, 0x2011, 0x0006, 0x1210, 0x2011, 0x0000, 0x2202, 0x9006,
+ 0x2008, 0x82ff, 0x01b0, 0x8200, 0x600a, 0x600f, 0xffff, 0x6003,
+ 0x0002, 0x6007, 0x0000, 0x0026, 0x2019, 0x0010, 0x9280, 0x0001,
+ 0x20e8, 0x21a0, 0x21a8, 0x4104, 0x8319, 0x1de0, 0x8211, 0x1da0,
+ 0x002e, 0x009e, 0x003e, 0x014e, 0x01de, 0x0005, 0x2011, 0x000e,
+ 0x08e8, 0x0016, 0x0026, 0x0096, 0x3348, 0x080c, 0x0f39, 0x2100,
+ 0x9300, 0x2098, 0x22e0, 0x009e, 0x002e, 0x001e, 0x0036, 0x3518,
+ 0x20a9, 0x0001, 0x4002, 0x8007, 0x4004, 0x8319, 0x1dd8, 0x003e,
+ 0x0005, 0x20e9, 0x0001, 0x71b4, 0x81ff, 0x11c0, 0x9006, 0x2009,
+ 0x0200, 0x20a9, 0x0002, 0x9298, 0x0018, 0x23a0, 0x4001, 0x2009,
+ 0x0700, 0x20a9, 0x0002, 0x9298, 0x0008, 0x23a0, 0x4001, 0x7078,
+ 0x8007, 0x717c, 0x810f, 0x20a9, 0x0002, 0x4001, 0x9298, 0x000c,
+ 0x23a0, 0x900e, 0x080c, 0x0dda, 0x2001, 0x0000, 0x810f, 0x20a9,
+ 0x0002, 0x4001, 0x0005, 0x89ff, 0x0140, 0xa804, 0xa807, 0x0000,
+ 0x0006, 0x080c, 0x1063, 0x009e, 0x0cb0, 0x0005, 0x00e6, 0x2071,
+ 0x1800, 0x080c, 0x10dc, 0x090c, 0x0dfa, 0x00ee, 0x0005, 0x0086,
+ 0x00e6, 0x0006, 0x0026, 0x0036, 0x0126, 0x2091, 0x8000, 0x00c9,
+ 0x2071, 0x1800, 0x73bc, 0x702c, 0x9016, 0x9045, 0x0158, 0x8210,
+ 0x9906, 0x090c, 0x0dfa, 0x2300, 0x9202, 0x0120, 0x1a0c, 0x0dfa,
+ 0xa000, 0x0c98, 0x012e, 0x003e, 0x002e, 0x000e, 0x00ee, 0x008e,
+ 0x0005, 0x0086, 0x00e6, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x190e, 0x7010, 0x9005, 0x0140, 0x7018, 0x9045, 0x0128, 0x9906,
+ 0x090c, 0x0dfa, 0xa000, 0x0cc8, 0x012e, 0x000e, 0x00ee, 0x008e,
+ 0x0005, 0x00e6, 0x2071, 0x1800, 0x0126, 0x2091, 0x8000, 0x70bc,
+ 0x8001, 0x0270, 0x70be, 0x702c, 0x2048, 0x9085, 0x0001, 0xa800,
+ 0x702e, 0xa803, 0x0000, 0xa807, 0x0000, 0x012e, 0x00ee, 0x0005,
+ 0x904e, 0x0cd8, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0x1800,
+ 0x70bc, 0x90ca, 0x0040, 0x0268, 0x8001, 0x70be, 0x702c, 0x2048,
+ 0xa800, 0x702e, 0xa803, 0x0000, 0xa807, 0x0000, 0x012e, 0x00ee,
+ 0x0005, 0x904e, 0x0cd8, 0x00e6, 0x0126, 0x2091, 0x8000, 0x0016,
+ 0x890e, 0x810e, 0x810f, 0x9184, 0x003f, 0xa862, 0x9184, 0xffc0,
+ 0xa85e, 0x001e, 0x0020, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc, 0x8000, 0x70be,
+ 0x080c, 0x81f0, 0x012e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9026,
+ 0x2009, 0x0000, 0x2049, 0x0400, 0x2900, 0x702e, 0x8940, 0x2800,
+ 0xa802, 0xa95e, 0xa863, 0x0001, 0x8420, 0x9886, 0x0440, 0x0120,
+ 0x2848, 0x9188, 0x0040, 0x0c90, 0x2071, 0x188b, 0x7000, 0x9005,
+ 0x11a0, 0x2001, 0x0492, 0xa802, 0x2048, 0x2009, 0x2480, 0x8940,
+ 0x2800, 0xa802, 0xa95e, 0xa863, 0x0001, 0x8420, 0x9886, 0x0800,
+ 0x0120, 0x2848, 0x9188, 0x0040, 0x0c90, 0x2071, 0x188b, 0x7104,
+ 0x7200, 0x82ff, 0x01d0, 0x7308, 0x8318, 0x831f, 0x831b, 0x831b,
+ 0x7312, 0x8319, 0x2001, 0x0800, 0xa802, 0x2048, 0x8900, 0xa802,
+ 0x2040, 0xa95e, 0xaa62, 0x8420, 0x2300, 0x9906, 0x0130, 0x2848,
+ 0x9188, 0x0040, 0x9291, 0x0000, 0x0c88, 0xa803, 0x0000, 0x2071,
+ 0x1800, 0x74ba, 0x74be, 0x0005, 0x00e6, 0x0016, 0x9984, 0xfc00,
+ 0x01e8, 0x908c, 0xf800, 0x1168, 0x9982, 0x0400, 0x02b8, 0x9982,
+ 0x0440, 0x0278, 0x9982, 0x0492, 0x0288, 0x9982, 0x0800, 0x1270,
+ 0x0040, 0x9982, 0x0800, 0x0250, 0x2071, 0x188b, 0x7010, 0x9902,
+ 0x1228, 0x9085, 0x0001, 0x001e, 0x00ee, 0x0005, 0x9006, 0x0cd8,
+ 0x00e6, 0x2071, 0x19f0, 0x7007, 0x0000, 0x9006, 0x701e, 0x7022,
+ 0x7002, 0x2071, 0x0000, 0x7010, 0x9085, 0x8044, 0x7012, 0x2071,
+ 0x0080, 0x9006, 0x0006, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x000e, 0x1158, 0x702b, 0x0060, 0x20a9, 0x0040, 0x7022, 0x1f04,
+ 0x111e, 0x702b, 0x0060, 0x702b, 0x0020, 0x20a9, 0x0040, 0x7022,
+ 0x1f04, 0x1127, 0x702b, 0x0020, 0x00ee, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x00e6, 0xa06f, 0x0000, 0x2071, 0x19f0, 0x701c, 0x9088,
+ 0x19fa, 0x280a, 0x8000, 0x9084, 0x003f, 0x701e, 0x7120, 0x9106,
+ 0x090c, 0x0dfa, 0x7004, 0x9005, 0x1128, 0x00f6, 0x2079, 0x0080,
+ 0x00a9, 0x00fe, 0x00ee, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x00e6, 0x2071, 0x19f0, 0x7004, 0x9005, 0x1128, 0x00f6, 0x2079,
+ 0x0080, 0x0021, 0x00fe, 0x00ee, 0x012e, 0x0005, 0x7004, 0x9086,
+ 0x0000, 0x1110, 0x7007, 0x0006, 0x7000, 0x0002, 0x1170, 0x116e,
+ 0x116e, 0x116e, 0x12e7, 0x12e7, 0x12e7, 0x12e7, 0x080c, 0x0dfa,
+ 0x701c, 0x7120, 0x9106, 0x1148, 0x792c, 0x9184, 0x0001, 0x1120,
+ 0xd1fc, 0x1110, 0x7007, 0x0000, 0x0005, 0x0096, 0x9180, 0x19fa,
+ 0x2004, 0x700a, 0x2048, 0x8108, 0x918c, 0x003f, 0x7122, 0x782b,
+ 0x0026, 0xa88c, 0x7802, 0xa890, 0x7806, 0xa894, 0x780a, 0xa898,
+ 0x780e, 0xa878, 0x700e, 0xa870, 0x7016, 0xa874, 0x701a, 0xa868,
+ 0x009e, 0xd084, 0x0120, 0x7007, 0x0001, 0x0029, 0x0005, 0x7007,
+ 0x0002, 0x00b1, 0x0005, 0x0016, 0x0026, 0x710c, 0x2011, 0x0040,
+ 0x9182, 0x0040, 0x1210, 0x2110, 0x9006, 0x700e, 0x7212, 0x8203,
+ 0x7812, 0x782b, 0x0020, 0x782b, 0x0041, 0x002e, 0x001e, 0x0005,
+ 0x0016, 0x0026, 0x0136, 0x0146, 0x0156, 0x7014, 0x20e0, 0x7018,
+ 0x2098, 0x20e9, 0x0000, 0x20a1, 0x0088, 0x782b, 0x0026, 0x710c,
+ 0x2011, 0x0040, 0x9182, 0x0040, 0x1210, 0x2110, 0x9006, 0x700e,
+ 0x22a8, 0x4006, 0x8203, 0x7812, 0x782b, 0x0020, 0x3300, 0x701a,
+ 0x782b, 0x0001, 0x015e, 0x014e, 0x013e, 0x002e, 0x001e, 0x0005,
+ 0x2009, 0x19f0, 0x2104, 0xc095, 0x200a, 0x080c, 0x114d, 0x0005,
+ 0x0016, 0x00e6, 0x2071, 0x19f0, 0x00f6, 0x2079, 0x0080, 0x792c,
+ 0xd1bc, 0x190c, 0x0df3, 0x782b, 0x0002, 0xd1fc, 0x0120, 0x918c,
+ 0x0700, 0x7004, 0x0023, 0x00fe, 0x00ee, 0x001e, 0x0005, 0x115e,
+ 0x1206, 0x123a, 0x0dfa, 0x0dfa, 0x12f3, 0x0dfa, 0x918c, 0x0700,
+ 0x1550, 0x0136, 0x0146, 0x0156, 0x7014, 0x20e8, 0x7018, 0x20a0,
+ 0x20e1, 0x0000, 0x2099, 0x0088, 0x782b, 0x0040, 0x7010, 0x20a8,
+ 0x4005, 0x3400, 0x701a, 0x015e, 0x014e, 0x013e, 0x700c, 0x9005,
+ 0x0578, 0x7800, 0x7802, 0x7804, 0x7806, 0x080c, 0x11a3, 0x0005,
+ 0x7008, 0x0096, 0x2048, 0xa86f, 0x0100, 0x009e, 0x7007, 0x0000,
+ 0x080c, 0x115e, 0x0005, 0x7008, 0x0096, 0x2048, 0xa86f, 0x0200,
+ 0x009e, 0x0ca0, 0x918c, 0x0700, 0x1150, 0x700c, 0x9005, 0x0180,
+ 0x7800, 0x7802, 0x7804, 0x7806, 0x080c, 0x11b8, 0x0005, 0x7008,
+ 0x0096, 0x2048, 0xa86f, 0x0200, 0x009e, 0x7007, 0x0000, 0x0080,
+ 0x0096, 0x7008, 0x2048, 0x7800, 0xa88e, 0x7804, 0xa892, 0x7808,
+ 0xa896, 0x780c, 0xa89a, 0xa86f, 0x0100, 0x009e, 0x7007, 0x0000,
+ 0x0096, 0x00d6, 0x7008, 0x2048, 0x2001, 0x18b7, 0x2004, 0x9906,
+ 0x1128, 0xa89c, 0x080f, 0x00de, 0x009e, 0x00a0, 0x00de, 0x009e,
+ 0x0096, 0x00d6, 0x7008, 0x2048, 0x0081, 0x0150, 0xa89c, 0x0086,
+ 0x2940, 0x080f, 0x008e, 0x00de, 0x009e, 0x080c, 0x114d, 0x0005,
+ 0x00de, 0x009e, 0x080c, 0x114d, 0x0005, 0xa8a8, 0xd08c, 0x0005,
+ 0x0096, 0xa0a0, 0x904d, 0x090c, 0x0dfa, 0xa06c, 0x908e, 0x0100,
+ 0x0130, 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4002, 0x080c,
+ 0x6adc, 0xa09f, 0x0000, 0xa0a3, 0x0000, 0x2848, 0x080c, 0x1063,
+ 0x009e, 0x0005, 0x00a6, 0xa0a0, 0x904d, 0x090c, 0x0dfa, 0xa06c,
+ 0x908e, 0x0100, 0x0128, 0xa87b, 0x0001, 0xa883, 0x0000, 0x00c0,
+ 0xa80c, 0x2050, 0xb004, 0x9005, 0x0198, 0xa80e, 0x2050, 0x8006,
+ 0x8006, 0x8007, 0x908c, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002,
+ 0xa076, 0xa172, 0xb000, 0xa07a, 0x2810, 0x080c, 0x112e, 0x00e8,
+ 0xa97c, 0xa894, 0x0016, 0x0006, 0x080c, 0x6adc, 0x000e, 0x001e,
+ 0xd1fc, 0x1138, 0xd1f4, 0x0128, 0x00c6, 0x2060, 0x080c, 0xa0e3,
+ 0x00ce, 0x7008, 0x2048, 0xa89f, 0x0000, 0xa8a3, 0x0000, 0x080c,
+ 0x1063, 0x7007, 0x0000, 0x080c, 0x114d, 0x00ae, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x782b, 0x1001, 0x7007, 0x0005, 0x7000, 0xc094,
+ 0x7002, 0x012e, 0x0005, 0x7007, 0x0000, 0x080c, 0x115e, 0x0005,
+ 0x0126, 0x2091, 0x2200, 0x2079, 0x0300, 0x2071, 0x1a3a, 0x7003,
+ 0x0000, 0x78bf, 0x00f6, 0x781b, 0x4800, 0x0419, 0x7803, 0x0003,
+ 0x780f, 0x0000, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0128,
+ 0x20a9, 0x0254, 0x2061, 0xdc42, 0x0020, 0x20a9, 0x0241, 0x2061,
+ 0xe0e8, 0x2c0d, 0x7912, 0xe104, 0x9ce0, 0x0002, 0x7916, 0x1f04,
+ 0x1319, 0x7807, 0x0007, 0x7803, 0x0000, 0x7803, 0x0001, 0x012e,
+ 0x0005, 0x00c6, 0x7803, 0x0000, 0x7808, 0xd09c, 0x0110, 0x7820,
+ 0x0cd8, 0x2001, 0x1a3b, 0x2003, 0x0000, 0x78ab, 0x0004, 0x78ac,
+ 0xd0ac, 0x1de8, 0x78ab, 0x0002, 0x7807, 0x0007, 0x7827, 0x0030,
+ 0x782b, 0x0400, 0x7827, 0x0031, 0x782b, 0x1a5c, 0x781f, 0xff00,
+ 0x781b, 0xb700, 0x2001, 0x0200, 0x2004, 0xd0dc, 0x0110, 0x781f,
+ 0x0303, 0x2061, 0x1a5c, 0x602f, 0x1cd0, 0x2001, 0x1819, 0x2004,
+ 0x9082, 0x1cd0, 0x6032, 0x603b, 0x1fc8, 0x2001, 0x32e9, 0xd0fc,
+ 0x190c, 0x0dfa, 0x2001, 0x0003, 0x2004, 0xd0d4, 0x1118, 0x783f,
+ 0x32e9, 0x0020, 0x9084, 0xc000, 0x783f, 0xb2e9, 0x00ce, 0x0005,
+ 0x0126, 0x2091, 0x2200, 0x7908, 0x9184, 0x0070, 0x190c, 0x0df3,
+ 0xd19c, 0x0158, 0x7820, 0x908c, 0xf000, 0x15e8, 0x908a, 0x0024,
+ 0x1a0c, 0x0dfa, 0x0023, 0x012e, 0x0005, 0x012e, 0x0005, 0x13ab,
+ 0x13ab, 0x13c2, 0x13c7, 0x13cb, 0x13d0, 0x13f8, 0x13fc, 0x140a,
+ 0x140e, 0x13ab, 0x149a, 0x149e, 0x150e, 0x13ab, 0x13ab, 0x13ab,
+ 0x13ab, 0x13ab, 0x13ab, 0x13ab, 0x13ab, 0x13ab, 0x13ab, 0x13ab,
+ 0x13ab, 0x13ab, 0x13d2, 0x13ab, 0x13ab, 0x13ab, 0x13ab, 0x13ab,
+ 0x13ab, 0x13af, 0x13ad, 0x080c, 0x0dfa, 0x080c, 0x0df3, 0x080c,
+ 0x1515, 0x2009, 0x1a52, 0x2104, 0x8000, 0x200a, 0x080c, 0x7c6d,
+ 0x080c, 0x19ff, 0x0005, 0x2009, 0x0048, 0x2060, 0x080c, 0xa15d,
+ 0x012e, 0x0005, 0x7004, 0xc085, 0xc0b5, 0x7006, 0x0005, 0x7004,
+ 0xc085, 0x7006, 0x0005, 0x080c, 0x1515, 0x080c, 0x166e, 0x0005,
+ 0x080c, 0x0dfa, 0x080c, 0x1515, 0x2060, 0x6014, 0x0096, 0x2048,
+ 0xa83b, 0xffff, 0x009e, 0x2009, 0x0048, 0x080c, 0xa15d, 0x2001,
+ 0x015d, 0x2003, 0x0000, 0x2009, 0x03e8, 0x8109, 0x0160, 0x2001,
+ 0x0201, 0x2004, 0x9005, 0x0dc8, 0x2001, 0x0218, 0x2004, 0xd0ec,
+ 0x1110, 0x080c, 0x151a, 0x2001, 0x0307, 0x2003, 0x8000, 0x0005,
+ 0x7004, 0xc095, 0x7006, 0x0005, 0x080c, 0x1515, 0x2060, 0x6014,
+ 0x0096, 0x2048, 0xa83b, 0xffff, 0x009e, 0x2009, 0x0048, 0x080c,
+ 0xa15d, 0x0005, 0x080c, 0x1515, 0x080c, 0x0dfa, 0x080c, 0x1515,
+ 0x080c, 0x1485, 0x7827, 0x0018, 0x79ac, 0xd1dc, 0x0540, 0x7827,
+ 0x0015, 0x7828, 0x782b, 0x0000, 0x9065, 0x0138, 0x2001, 0x020d,
+ 0x2003, 0x0050, 0x2003, 0x0020, 0x0400, 0x7004, 0x9005, 0x1180,
+ 0x78ab, 0x0004, 0x7827, 0x0018, 0x782b, 0x0000, 0xd1bc, 0x090c,
+ 0x0dfa, 0x2001, 0x020d, 0x2003, 0x0050, 0x2003, 0x0020, 0x0490,
+ 0x78ab, 0x0004, 0x7803, 0x0001, 0x080c, 0x149e, 0x0005, 0x7828,
+ 0x782b, 0x0000, 0x9065, 0x090c, 0x0dfa, 0x6014, 0x2048, 0x78ab,
+ 0x0004, 0x918c, 0x0700, 0x01a8, 0x080c, 0x7c6d, 0x080c, 0x19ff,
+ 0x080c, 0xbe37, 0x0158, 0xa9ac, 0xa936, 0xa9b0, 0xa93a, 0xa83f,
+ 0xffff, 0xa843, 0xffff, 0xa880, 0xc0bd, 0xa882, 0x080c, 0xba56,
+ 0x0005, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x6024,
+ 0x190c, 0xc223, 0x2029, 0x00c8, 0x8529, 0x0128, 0x2001, 0x0201,
+ 0x2004, 0x9005, 0x0dc8, 0x7dbc, 0x080c, 0xdbeb, 0xd5a4, 0x1118,
+ 0x080c, 0x151a, 0x0005, 0x080c, 0x7c6d, 0x080c, 0x19ff, 0x0005,
+ 0x781f, 0x0300, 0x7803, 0x0001, 0x0005, 0x0016, 0x0066, 0x0076,
+ 0x00f6, 0x2079, 0x0300, 0x7908, 0x918c, 0x0007, 0x9186, 0x0003,
+ 0x0120, 0x2001, 0x0016, 0x080c, 0x158b, 0x00fe, 0x007e, 0x006e,
+ 0x001e, 0x0005, 0x7004, 0xc09d, 0x7006, 0x0005, 0x7104, 0x9184,
+ 0x0004, 0x190c, 0x0dfa, 0xd184, 0x11b1, 0xd19c, 0x0180, 0xc19c,
+ 0x7106, 0x0016, 0x080c, 0x1651, 0x001e, 0x0148, 0x2001, 0x020d,
+ 0x2003, 0x0050, 0x2003, 0x0020, 0x080c, 0x151a, 0x0005, 0x81ff,
+ 0x190c, 0x0dfa, 0x0005, 0x2100, 0xc184, 0xc1b4, 0x7106, 0xd0b4,
+ 0x0016, 0x00e6, 0x1904, 0x1503, 0x2071, 0x0200, 0x080c, 0x1645,
+ 0x080c, 0x1651, 0x05a8, 0x6014, 0x9005, 0x05a8, 0x0096, 0x2048,
+ 0xa864, 0x009e, 0x9084, 0x00ff, 0x908e, 0x0029, 0x0160, 0x908e,
+ 0x0048, 0x1548, 0x601c, 0xd084, 0x11d8, 0x00f6, 0x2c78, 0x080c,
+ 0x16db, 0x00fe, 0x00a8, 0x00f6, 0x2c78, 0x080c, 0x1825, 0x00fe,
+ 0x2009, 0x01f4, 0x8109, 0x0160, 0x2001, 0x0201, 0x2004, 0x9005,
+ 0x0dc8, 0x2001, 0x0218, 0x2004, 0xd0ec, 0x1110, 0x0419, 0x0040,
+ 0x2001, 0x020d, 0x2003, 0x0020, 0x080c, 0x1329, 0x7803, 0x0001,
+ 0x00ee, 0x001e, 0x0005, 0x080c, 0x1651, 0x0dd0, 0x2001, 0x020d,
+ 0x2003, 0x0050, 0x2003, 0x0020, 0x0069, 0x0c90, 0x0031, 0x2060,
+ 0x2009, 0x0053, 0x080c, 0xa15d, 0x0005, 0x7808, 0xd09c, 0x0de8,
+ 0x7820, 0x0005, 0x080c, 0x1485, 0x00d6, 0x2069, 0x0200, 0x2009,
+ 0x01f4, 0x8109, 0x0510, 0x6804, 0x9005, 0x0dd8, 0x2001, 0x015d,
+ 0x2003, 0x0000, 0x79bc, 0xd1a4, 0x1528, 0x79b8, 0x918c, 0x0fff,
+ 0x0180, 0x9182, 0x0841, 0x1268, 0x9188, 0x0007, 0x918c, 0x0ff8,
+ 0x810c, 0x810c, 0x810c, 0x080c, 0x157d, 0x6827, 0x0001, 0x8109,
+ 0x1dd0, 0x04d9, 0x6827, 0x0002, 0x04c1, 0x6804, 0x9005, 0x1130,
+ 0x682c, 0xd0e4, 0x1500, 0x6804, 0x9005, 0x0de8, 0x79b8, 0xd1ec,
+ 0x1130, 0x08c0, 0x080c, 0x7c6d, 0x080c, 0x19ff, 0x0090, 0x7827,
+ 0x0015, 0x782b, 0x0000, 0x7827, 0x0018, 0x782b, 0x0000, 0x2001,
+ 0x020d, 0x2003, 0x0020, 0x2001, 0x0307, 0x2003, 0x0300, 0x7803,
+ 0x0001, 0x00de, 0x0005, 0x682c, 0x9084, 0x5400, 0x9086, 0x5400,
+ 0x0d30, 0x7827, 0x0015, 0x782b, 0x0000, 0x7803, 0x0001, 0x6800,
+ 0x9085, 0x1800, 0x6802, 0x00de, 0x0005, 0x6824, 0x9084, 0x0003,
+ 0x1de0, 0x0005, 0x2001, 0x0030, 0x2c08, 0x621c, 0x0021, 0x7830,
+ 0x9086, 0x0041, 0x0005, 0x00f6, 0x2079, 0x0300, 0x0006, 0x7808,
+ 0xd09c, 0x0140, 0x0016, 0x0026, 0x00c6, 0x080c, 0x1370, 0x00ce,
+ 0x002e, 0x001e, 0x000e, 0x0006, 0x7832, 0x7936, 0x7a3a, 0x781b,
+ 0x8080, 0x0059, 0x1118, 0x000e, 0x00fe, 0x0005, 0x000e, 0x792c,
+ 0x3900, 0x8000, 0x2004, 0x080c, 0x0dfa, 0x2009, 0x180c, 0x2104,
+ 0xc0f4, 0x200a, 0x2009, 0xff00, 0x8109, 0x0904, 0x1609, 0x7a18,
+ 0x9284, 0x0030, 0x0904, 0x1604, 0x9284, 0x0048, 0x9086, 0x0008,
+ 0x1904, 0x1604, 0x2001, 0x0109, 0x2004, 0xd08c, 0x01f0, 0x0006,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x0126, 0x2091, 0x2800,
+ 0x00f6, 0x0026, 0x0016, 0x2009, 0x1a55, 0x2104, 0x8000, 0x0208,
+ 0x200a, 0x080c, 0x8632, 0x001e, 0x002e, 0x00fe, 0x012e, 0x015e,
+ 0x014e, 0x013e, 0x01de, 0x01ce, 0x000e, 0x2001, 0x009b, 0x2004,
+ 0xd0fc, 0x01d0, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x00f6, 0x0016, 0x2009, 0x1a56, 0x2104, 0x8000, 0x0208,
+ 0x200a, 0x080c, 0x1dec, 0x001e, 0x00fe, 0x015e, 0x014e, 0x013e,
+ 0x01de, 0x01ce, 0x012e, 0x000e, 0x7818, 0xd0bc, 0x1904, 0x15b4,
+ 0x0005, 0x2001, 0x180c, 0x2004, 0xd0f4, 0x1528, 0x7a18, 0x9284,
+ 0x0030, 0x0508, 0x9284, 0x0048, 0x9086, 0x0008, 0x11e0, 0x2001,
+ 0x19ce, 0x2004, 0x9005, 0x01b8, 0x2001, 0x1a3d, 0x2004, 0x9086,
+ 0x0000, 0x0188, 0x2009, 0x1a54, 0x2104, 0x8000, 0x0208, 0x200a,
+ 0x080c, 0x96d4, 0x2009, 0x180c, 0x2104, 0xc0f5, 0x200a, 0x2009,
+ 0xff00, 0x0804, 0x15b4, 0x9085, 0x0001, 0x0005, 0x7832, 0x7936,
+ 0x7a3a, 0x781b, 0x8080, 0x080c, 0x15ad, 0x1108, 0x0005, 0x792c,
+ 0x3900, 0x8000, 0x2004, 0x080c, 0x0dfa, 0x7037, 0x0001, 0x7150,
+ 0x7037, 0x0002, 0x7050, 0x2060, 0xd1bc, 0x1110, 0x7054, 0x2060,
+ 0x0005, 0x0006, 0x0046, 0x00e6, 0x2071, 0x0200, 0x7037, 0x0002,
+ 0x7058, 0x9084, 0xff00, 0x8007, 0x9086, 0x00bc, 0x1158, 0x2021,
+ 0x1a53, 0x2404, 0x8000, 0x0208, 0x2022, 0x080c, 0x7c6d, 0x080c,
+ 0x19ff, 0x9006, 0x00ee, 0x004e, 0x000e, 0x0005, 0x0c11, 0x1108,
+ 0x0005, 0x00e6, 0x0016, 0x2071, 0x0200, 0x0879, 0x6124, 0xd1dc,
+ 0x01f8, 0x701c, 0xd08c, 0x0904, 0x16d0, 0x7017, 0x0000, 0x2001,
+ 0x0264, 0x2004, 0xd0bc, 0x0904, 0x16d0, 0x2001, 0x0268, 0x00c6,
+ 0x2064, 0x6104, 0x6038, 0x00ce, 0x918e, 0x0039, 0x1904, 0x16d0,
+ 0x9c06, 0x15f0, 0x0126, 0x2091, 0x2600, 0x080c, 0x7bb4, 0x012e,
+ 0x7358, 0x745c, 0x6014, 0x905d, 0x0598, 0x2b48, 0x6010, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x190c, 0xc1fe, 0xab42, 0xac3e,
+ 0x2001, 0x187d, 0x2004, 0xd0b4, 0x1170, 0x601c, 0xd0e4, 0x1158,
+ 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1120, 0xa83b,
+ 0x7fff, 0xa837, 0xffff, 0x080c, 0x1fe8, 0x1190, 0x080c, 0x1882,
+ 0x2a00, 0xa816, 0x0130, 0x2800, 0xa80e, 0x2c05, 0xa80a, 0x2c00,
+ 0xa812, 0x7037, 0x0020, 0x781f, 0x0300, 0x001e, 0x00ee, 0x0005,
+ 0x7037, 0x0050, 0x7037, 0x0020, 0x001e, 0x00ee, 0x080c, 0x151a,
+ 0x0005, 0x080c, 0x0dfa, 0x0016, 0x2009, 0x00a0, 0x8109, 0xa001,
+ 0xa001, 0xa001, 0x1dd8, 0x001e, 0x2ff0, 0x0126, 0x2091, 0x2200,
+ 0x0016, 0x00c6, 0x3e60, 0x6014, 0x2048, 0x2940, 0x903e, 0x2730,
+ 0xa864, 0x2068, 0xa81a, 0x9d84, 0x000f, 0x9088, 0x1fc8, 0x2165,
+ 0x0002, 0x1710, 0x175d, 0x1710, 0x1710, 0x1710, 0x173f, 0x1710,
+ 0x1714, 0x1709, 0x1754, 0x1710, 0x1710, 0x1710, 0x181a, 0x1728,
+ 0x171e, 0xa964, 0x918c, 0x00ff, 0x918e, 0x0048, 0x0904, 0x1754,
+ 0x9085, 0x0001, 0x0804, 0x1810, 0xa87c, 0xd0bc, 0x0dc8, 0xa890,
+ 0xa842, 0xa88c, 0xa83e, 0xa888, 0x0804, 0x1764, 0xa87c, 0xd0bc,
+ 0x0d78, 0xa890, 0xa842, 0xa88c, 0xa83e, 0xa888, 0x0804, 0x17b3,
+ 0xa87c, 0xd0bc, 0x0d28, 0xa890, 0xa842, 0xa88c, 0xa83e, 0xa804,
+ 0x9045, 0x090c, 0x0dfa, 0xa164, 0xa91a, 0x91ec, 0x000f, 0x9d80,
+ 0x1fc8, 0x2065, 0xa888, 0xd19c, 0x1904, 0x17b3, 0x0428, 0xa87c,
+ 0xd0ac, 0x0970, 0xa804, 0x9045, 0x090c, 0x0dfa, 0xa164, 0xa91a,
+ 0x91ec, 0x000f, 0x9d80, 0x1fc8, 0x2065, 0x9006, 0xa842, 0xa83e,
+ 0xd19c, 0x1904, 0x17b3, 0x0080, 0xa87c, 0xd0ac, 0x0904, 0x1710,
+ 0x9006, 0xa842, 0xa83e, 0x0804, 0x17b3, 0xa87c, 0xd0ac, 0x0904,
+ 0x1710, 0x9006, 0xa842, 0xa83e, 0x2c05, 0x908a, 0x0036, 0x1a0c,
+ 0x0dfa, 0x9082, 0x001b, 0x0002, 0x1787, 0x1787, 0x1789, 0x1787,
+ 0x1787, 0x1787, 0x178f, 0x1787, 0x1787, 0x1787, 0x1795, 0x1787,
+ 0x1787, 0x1787, 0x179b, 0x1787, 0x1787, 0x1787, 0x17a1, 0x1787,
+ 0x1787, 0x1787, 0x17a7, 0x1787, 0x1787, 0x1787, 0x17ad, 0x080c,
+ 0x0dfa, 0xa574, 0xa478, 0xa37c, 0xa280, 0x0804, 0x17f8, 0xa584,
+ 0xa488, 0xa38c, 0xa290, 0x0804, 0x17f8, 0xa594, 0xa498, 0xa39c,
+ 0xa2a0, 0x0804, 0x17f8, 0xa5a4, 0xa4a8, 0xa3ac, 0xa2b0, 0x0804,
+ 0x17f8, 0xa5b4, 0xa4b8, 0xa3bc, 0xa2c0, 0x0804, 0x17f8, 0xa5c4,
+ 0xa4c8, 0xa3cc, 0xa2d0, 0x0804, 0x17f8, 0xa5d4, 0xa4d8, 0xa3dc,
+ 0xa2e0, 0x0804, 0x17f8, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dfa,
+ 0x9082, 0x001b, 0x0002, 0x17d6, 0x17d4, 0x17d4, 0x17d4, 0x17d4,
+ 0x17d4, 0x17dd, 0x17d4, 0x17d4, 0x17d4, 0x17d4, 0x17d4, 0x17e4,
+ 0x17d4, 0x17d4, 0x17d4, 0x17d4, 0x17d4, 0x17eb, 0x17d4, 0x17d4,
+ 0x17d4, 0x17d4, 0x17d4, 0x17f2, 0x080c, 0x0dfa, 0xa56c, 0xa470,
+ 0xa774, 0xa678, 0xa37c, 0xa280, 0x00d8, 0xa584, 0xa488, 0xa78c,
+ 0xa690, 0xa394, 0xa298, 0x00a0, 0xa59c, 0xa4a0, 0xa7a4, 0xa6a8,
+ 0xa3ac, 0xa2b0, 0x0068, 0xa5b4, 0xa4b8, 0xa7bc, 0xa6c0, 0xa3c4,
+ 0xa2c8, 0x0030, 0xa5cc, 0xa4d0, 0xa7d4, 0xa6d8, 0xa3dc, 0xa2e0,
+ 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a, 0xa988, 0x8c60,
+ 0x2c1d, 0xa8ac, 0xaab0, 0xa836, 0xaa3a, 0x8109, 0xa916, 0x1160,
+ 0x3e60, 0x601c, 0xc085, 0x601e, 0xa87c, 0xc0dd, 0xa87e, 0x9006,
+ 0x00ce, 0x001e, 0x012e, 0x0005, 0x2800, 0xa80e, 0xab0a, 0x2c00,
+ 0xa812, 0x0c70, 0x0804, 0x1710, 0x0016, 0x2009, 0x00a0, 0x8109,
+ 0xa001, 0xa001, 0xa001, 0x1dd8, 0x001e, 0x2ff0, 0x0126, 0x2091,
+ 0x2200, 0x0016, 0x00c6, 0x3e60, 0x6014, 0x2048, 0x2940, 0xa80e,
+ 0x2061, 0x1fc3, 0xa813, 0x1fc3, 0x2c05, 0xa80a, 0xa964, 0xa91a,
+ 0xa87c, 0xd0ac, 0x090c, 0x0dfa, 0x9006, 0xa842, 0xa83e, 0x2c05,
+ 0x908a, 0x0034, 0x1a0c, 0x0dfa, 0xadcc, 0xacd0, 0xafd4, 0xaed8,
+ 0xabdc, 0xaae0, 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a,
+ 0xa8ac, 0xaab0, 0xa836, 0xaa3a, 0xa988, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0008, 0x1120, 0x8109, 0xa916, 0x0128, 0x0080, 0x918a,
+ 0x0002, 0xa916, 0x1160, 0x3e60, 0x601c, 0xc085, 0x601e, 0xa87c,
+ 0xc0dd, 0xa87e, 0x9006, 0x00ce, 0x001e, 0x012e, 0x0005, 0xa804,
+ 0x9045, 0x090c, 0x0dfa, 0xa80e, 0xa064, 0xa81a, 0x9084, 0x000f,
+ 0x9080, 0x1fc8, 0x2015, 0x82ff, 0x090c, 0x0dfa, 0xaa12, 0x2205,
+ 0xa80a, 0x0c08, 0x903e, 0x2730, 0xa880, 0xd0fc, 0x1190, 0x2d00,
+ 0x0002, 0x1977, 0x18d9, 0x18d9, 0x1977, 0x1977, 0x1971, 0x1977,
+ 0x18d9, 0x1928, 0x1928, 0x1928, 0x1977, 0x1977, 0x1977, 0x196e,
+ 0x1928, 0xc0fc, 0xa882, 0xab2c, 0xaa30, 0xad1c, 0xac20, 0xdd9c,
+ 0x0904, 0x1979, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dfa, 0x9082,
+ 0x001b, 0x0002, 0x18c5, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3,
+ 0x18c9, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18cd, 0x18c3,
+ 0x18c3, 0x18c3, 0x18c3, 0x18c3, 0x18d1, 0x18c3, 0x18c3, 0x18c3,
+ 0x18c3, 0x18c3, 0x18d5, 0x080c, 0x0dfa, 0xa774, 0xa678, 0x0804,
+ 0x1979, 0xa78c, 0xa690, 0x0804, 0x1979, 0xa7a4, 0xa6a8, 0x0804,
+ 0x1979, 0xa7bc, 0xa6c0, 0x0804, 0x1979, 0xa7d4, 0xa6d8, 0x0804,
+ 0x1979, 0x2c05, 0x908a, 0x0036, 0x1a0c, 0x0dfa, 0x9082, 0x001b,
+ 0x0002, 0x18fc, 0x18fc, 0x18fe, 0x18fc, 0x18fc, 0x18fc, 0x1904,
+ 0x18fc, 0x18fc, 0x18fc, 0x190a, 0x18fc, 0x18fc, 0x18fc, 0x1910,
+ 0x18fc, 0x18fc, 0x18fc, 0x1916, 0x18fc, 0x18fc, 0x18fc, 0x191c,
+ 0x18fc, 0x18fc, 0x18fc, 0x1922, 0x080c, 0x0dfa, 0xa574, 0xa478,
+ 0xa37c, 0xa280, 0x0804, 0x1979, 0xa584, 0xa488, 0xa38c, 0xa290,
+ 0x0804, 0x1979, 0xa594, 0xa498, 0xa39c, 0xa2a0, 0x0804, 0x1979,
+ 0xa5a4, 0xa4a8, 0xa3ac, 0xa2b0, 0x0804, 0x1979, 0xa5b4, 0xa4b8,
+ 0xa3bc, 0xa2c0, 0x0804, 0x1979, 0xa5c4, 0xa4c8, 0xa3cc, 0xa2d0,
+ 0x0804, 0x1979, 0xa5d4, 0xa4d8, 0xa3dc, 0xa2e0, 0x0804, 0x1979,
+ 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dfa, 0x9082, 0x001b, 0x0002,
+ 0x194b, 0x1949, 0x1949, 0x1949, 0x1949, 0x1949, 0x1952, 0x1949,
+ 0x1949, 0x1949, 0x1949, 0x1949, 0x1959, 0x1949, 0x1949, 0x1949,
+ 0x1949, 0x1949, 0x1960, 0x1949, 0x1949, 0x1949, 0x1949, 0x1949,
+ 0x1967, 0x080c, 0x0dfa, 0xa56c, 0xa470, 0xa774, 0xa678, 0xa37c,
+ 0xa280, 0x0438, 0xa584, 0xa488, 0xa78c, 0xa690, 0xa394, 0xa298,
+ 0x0400, 0xa59c, 0xa4a0, 0xa7a4, 0xa6a8, 0xa3ac, 0xa2b0, 0x00c8,
+ 0xa5b4, 0xa4b8, 0xa7bc, 0xa6c0, 0xa3c4, 0xa2c8, 0x0090, 0xa5cc,
+ 0xa4d0, 0xa7d4, 0xa6d8, 0xa3dc, 0xa2e0, 0x0058, 0x9d86, 0x000e,
+ 0x1130, 0x080c, 0x1f80, 0x1904, 0x1882, 0x900e, 0x0050, 0x080c,
+ 0x0dfa, 0xab2e, 0xaa32, 0xad1e, 0xac22, 0xaf26, 0xae2a, 0x080c,
+ 0x1f80, 0x0005, 0x6014, 0x2048, 0x6118, 0x810c, 0x810c, 0x810c,
+ 0x81ff, 0x1118, 0xa887, 0x0001, 0x0008, 0xa986, 0x601b, 0x0002,
+ 0xa874, 0x9084, 0x00ff, 0x9084, 0x0008, 0x0150, 0x00e9, 0x6000,
+ 0x9086, 0x0004, 0x1120, 0x2009, 0x0048, 0x080c, 0xa15d, 0x0005,
+ 0xa974, 0xd1dc, 0x1108, 0x0005, 0xa934, 0xa88c, 0x9106, 0x1158,
+ 0xa938, 0xa890, 0x9106, 0x1138, 0x601c, 0xc084, 0x601e, 0x2009,
+ 0x0048, 0x0804, 0xa15d, 0x0005, 0x0126, 0x00c6, 0x2091, 0x2200,
+ 0x00ce, 0x7908, 0x918c, 0x0007, 0x9186, 0x0000, 0x05b0, 0x9186,
+ 0x0003, 0x0598, 0x6020, 0x6023, 0x0000, 0x0006, 0x2031, 0x0008,
+ 0x00c6, 0x781f, 0x0808, 0x7808, 0xd09c, 0x0120, 0x080c, 0x1370,
+ 0x8631, 0x1db8, 0x00ce, 0x781f, 0x0800, 0x2031, 0x0168, 0x00c6,
+ 0x7808, 0xd09c, 0x190c, 0x1370, 0x00ce, 0x2001, 0x0038, 0x080c,
+ 0x1a87, 0x7930, 0x9186, 0x0040, 0x0160, 0x9186, 0x0042, 0x190c,
+ 0x0dfa, 0x2001, 0x001e, 0x8001, 0x1df0, 0x8631, 0x1d40, 0x080c,
+ 0x1a96, 0x000e, 0x6022, 0x012e, 0x0005, 0x080c, 0x1a83, 0x7827,
+ 0x0015, 0x7828, 0x9c06, 0x1db8, 0x782b, 0x0000, 0x0ca0, 0x00f6,
+ 0x2079, 0x0300, 0x7803, 0x0000, 0x78ab, 0x0004, 0x00fe, 0x080c,
+ 0x7207, 0x1188, 0x2001, 0x0138, 0x2003, 0x0000, 0x2001, 0x0160,
+ 0x2003, 0x0000, 0x2011, 0x012c, 0xa001, 0xa001, 0x8211, 0x1de0,
+ 0x0059, 0x0804, 0x72d2, 0x0479, 0x0039, 0x2001, 0x0160, 0x2502,
+ 0x2001, 0x0138, 0x2202, 0x0005, 0x00e6, 0x2071, 0x0200, 0x080c,
+ 0x2bbc, 0x2009, 0x003c, 0x080c, 0x230a, 0x2001, 0x015d, 0x2003,
+ 0x0000, 0x7000, 0x9084, 0x003c, 0x1de0, 0x080c, 0x81f0, 0x70a0,
+ 0x70a2, 0x7098, 0x709a, 0x709c, 0x709e, 0x2001, 0x020d, 0x2003,
+ 0x0020, 0x00f6, 0x2079, 0x0300, 0x080c, 0x1329, 0x7803, 0x0001,
+ 0x00fe, 0x00ee, 0x0005, 0x2001, 0x0138, 0x2014, 0x2003, 0x0000,
+ 0x2001, 0x0160, 0x202c, 0x2003, 0x0000, 0x080c, 0x7207, 0x1108,
+ 0x0005, 0x2021, 0x0260, 0x2001, 0x0141, 0x201c, 0xd3dc, 0x1168,
+ 0x2001, 0x0109, 0x201c, 0x939c, 0x0048, 0x1160, 0x2001, 0x0111,
+ 0x201c, 0x83ff, 0x1110, 0x8421, 0x1d70, 0x2001, 0x015d, 0x2003,
+ 0x0000, 0x0005, 0x0046, 0x2021, 0x0019, 0x2003, 0x0048, 0xa001,
+ 0xa001, 0x201c, 0x939c, 0x0048, 0x0120, 0x8421, 0x1db0, 0x004e,
+ 0x0c60, 0x004e, 0x0c40, 0x601c, 0xc084, 0x601e, 0x0005, 0x2c08,
+ 0x621c, 0x080c, 0x158b, 0x7930, 0x0005, 0x2c08, 0x621c, 0x080c,
+ 0x1636, 0x7930, 0x0005, 0x8001, 0x1df0, 0x0005, 0x2031, 0x0064,
+ 0x781c, 0x9084, 0x0007, 0x0170, 0x2001, 0x0038, 0x0c41, 0x9186,
+ 0x0040, 0x0904, 0x1af4, 0x2001, 0x001e, 0x0c69, 0x8631, 0x1d80,
+ 0x080c, 0x0dfa, 0x781f, 0x0202, 0x2001, 0x015d, 0x2003, 0x0000,
+ 0x2001, 0x0dac, 0x0c01, 0x781c, 0xd084, 0x0110, 0x0861, 0x04e0,
+ 0x2001, 0x0030, 0x0891, 0x9186, 0x0040, 0x0568, 0x781c, 0xd084,
+ 0x1da8, 0x781f, 0x0101, 0x2001, 0x0014, 0x0869, 0x2001, 0x0037,
+ 0x0821, 0x9186, 0x0040, 0x0140, 0x2001, 0x0030, 0x080c, 0x1a8d,
+ 0x9186, 0x0040, 0x190c, 0x0dfa, 0x00d6, 0x2069, 0x0200, 0x692c,
+ 0xd1f4, 0x1170, 0xd1c4, 0x0160, 0xd19c, 0x0130, 0x6800, 0x9085,
+ 0x1800, 0x6802, 0x00de, 0x0080, 0x6908, 0x9184, 0x0007, 0x1db0,
+ 0x00de, 0x781f, 0x0100, 0x791c, 0x9184, 0x0007, 0x090c, 0x0dfa,
+ 0xa001, 0xa001, 0x781f, 0x0200, 0x0005, 0x0126, 0x2091, 0x2400,
+ 0x2071, 0x1a3d, 0x2079, 0x0090, 0x012e, 0x0005, 0x9280, 0x0005,
+ 0x2004, 0x2048, 0xa97c, 0xd1dc, 0x1904, 0x1b89, 0xa964, 0x9184,
+ 0x0007, 0x0002, 0x1b12, 0x1b74, 0x1b29, 0x1b29, 0x1b29, 0x1b5c,
+ 0x1b3c, 0x1b2b, 0x918c, 0x00ff, 0x9186, 0x0008, 0x1170, 0xa87c,
+ 0xd0b4, 0x0904, 0x1da7, 0x9006, 0xa842, 0xa83e, 0xa988, 0x2900,
+ 0xa85a, 0xa813, 0x1fc3, 0x0804, 0x1b85, 0x9186, 0x0048, 0x0904,
+ 0x1b74, 0x080c, 0x0dfa, 0xa87c, 0xd0b4, 0x0904, 0x1da7, 0xa890,
+ 0xa842, 0xa83a, 0xa88c, 0xa83e, 0xa836, 0xa8ac, 0xa846, 0xa8b0,
+ 0xa84a, 0xa988, 0x0804, 0x1b7c, 0xa864, 0x9084, 0x00ff, 0x9086,
+ 0x001e, 0x1d38, 0xa87c, 0xd0b4, 0x0904, 0x1da7, 0xa890, 0xa842,
+ 0xa83a, 0xa88c, 0xa83e, 0xa836, 0xa8ac, 0xa846, 0xa8b0, 0xa84a,
+ 0xa804, 0xa85a, 0x2040, 0xa064, 0x9084, 0x000f, 0x9080, 0x1fc8,
+ 0x2005, 0xa812, 0xa988, 0x0448, 0x918c, 0x00ff, 0x9186, 0x0015,
+ 0x1540, 0xa87c, 0xd0b4, 0x0904, 0x1da7, 0xa804, 0xa85a, 0x2040,
+ 0xa064, 0x9084, 0x000f, 0x9080, 0x1fc8, 0x2005, 0xa812, 0xa988,
+ 0x9006, 0xa842, 0xa83e, 0x0088, 0xa87c, 0xd0b4, 0x0904, 0x1da7,
+ 0xa988, 0x9006, 0xa842, 0xa83e, 0x2900, 0xa85a, 0xa864, 0x9084,
+ 0x000f, 0x9080, 0x1fc8, 0x2005, 0xa812, 0xa916, 0xa87c, 0xc0dd,
+ 0xa87e, 0x0005, 0x00f6, 0x2079, 0x0090, 0x782c, 0xd0fc, 0x190c,
+ 0x1dec, 0x00e6, 0x2071, 0x1a3d, 0x7000, 0x9005, 0x1904, 0x1bf2,
+ 0x7206, 0x9280, 0x0005, 0x204c, 0x9280, 0x0004, 0x2004, 0x782b,
+ 0x0004, 0x00f6, 0x2079, 0x0200, 0x7803, 0x0040, 0x00fe, 0x00b6,
+ 0x2058, 0xb86c, 0x7836, 0xb890, 0x00be, 0x00f6, 0x2079, 0x0200,
+ 0x7803, 0x0040, 0xa001, 0xa001, 0xa001, 0xa001, 0xa001, 0xa001,
+ 0x781a, 0x2079, 0x0100, 0x8004, 0x78d6, 0x00fe, 0xa814, 0x2050,
+ 0xa858, 0x2040, 0xa810, 0x2060, 0xa064, 0x90ec, 0x000f, 0xa944,
+ 0x791a, 0x7116, 0xa848, 0x781e, 0x701a, 0x9006, 0x700e, 0x7012,
+ 0x7004, 0xa940, 0xa838, 0x9106, 0x1500, 0xa93c, 0xa834, 0x9106,
+ 0x11e0, 0x0006, 0x0016, 0xa938, 0xa834, 0x9105, 0x0118, 0x001e,
+ 0x000e, 0x0098, 0x001e, 0x000e, 0x8aff, 0x01c8, 0x0126, 0x2091,
+ 0x8000, 0x2009, 0x0306, 0x200b, 0x0808, 0x00d9, 0x0108, 0x00c9,
+ 0x012e, 0x9006, 0x00ee, 0x00fe, 0x0005, 0x0036, 0x0046, 0xab38,
+ 0xac34, 0x080c, 0x1fe8, 0x004e, 0x003e, 0x0d30, 0x0c98, 0x9085,
+ 0x0001, 0x0c80, 0x2009, 0x0306, 0x200b, 0x4800, 0x7027, 0x0000,
+ 0x0005, 0x0076, 0x0066, 0x0056, 0x0046, 0x0036, 0x0026, 0x8aff,
+ 0x0904, 0x1da0, 0x700c, 0x7214, 0x923a, 0x7010, 0x7218, 0x9203,
+ 0x0a04, 0x1d9f, 0x9705, 0x0904, 0x1d9f, 0x903e, 0x2730, 0xa880,
+ 0xd0fc, 0x1190, 0x2d00, 0x0002, 0x1d34, 0x1c74, 0x1c74, 0x1d34,
+ 0x1d34, 0x1d11, 0x1d34, 0x1c74, 0x1d18, 0x1cc3, 0x1cc3, 0x1d34,
+ 0x1d34, 0x1d34, 0x1d0b, 0x1cc3, 0xc0fc, 0xa882, 0xab2c, 0xaa30,
+ 0xad1c, 0xac20, 0xdd9c, 0x0904, 0x1d36, 0x2c05, 0x908a, 0x0034,
+ 0x1a0c, 0x0dfa, 0x9082, 0x001b, 0x0002, 0x1c60, 0x1c5e, 0x1c5e,
+ 0x1c5e, 0x1c5e, 0x1c5e, 0x1c64, 0x1c5e, 0x1c5e, 0x1c5e, 0x1c5e,
+ 0x1c5e, 0x1c68, 0x1c5e, 0x1c5e, 0x1c5e, 0x1c5e, 0x1c5e, 0x1c6c,
+ 0x1c5e, 0x1c5e, 0x1c5e, 0x1c5e, 0x1c5e, 0x1c70, 0x080c, 0x0dfa,
+ 0xa774, 0xa678, 0x0804, 0x1d36, 0xa78c, 0xa690, 0x0804, 0x1d36,
+ 0xa7a4, 0xa6a8, 0x0804, 0x1d36, 0xa7bc, 0xa6c0, 0x0804, 0x1d36,
+ 0xa7d4, 0xa6d8, 0x0804, 0x1d36, 0x2c05, 0x908a, 0x0036, 0x1a0c,
+ 0x0dfa, 0x9082, 0x001b, 0x0002, 0x1c97, 0x1c97, 0x1c99, 0x1c97,
+ 0x1c97, 0x1c97, 0x1c9f, 0x1c97, 0x1c97, 0x1c97, 0x1ca5, 0x1c97,
+ 0x1c97, 0x1c97, 0x1cab, 0x1c97, 0x1c97, 0x1c97, 0x1cb1, 0x1c97,
+ 0x1c97, 0x1c97, 0x1cb7, 0x1c97, 0x1c97, 0x1c97, 0x1cbd, 0x080c,
+ 0x0dfa, 0xa574, 0xa478, 0xa37c, 0xa280, 0x0804, 0x1d36, 0xa584,
+ 0xa488, 0xa38c, 0xa290, 0x0804, 0x1d36, 0xa594, 0xa498, 0xa39c,
+ 0xa2a0, 0x0804, 0x1d36, 0xa5a4, 0xa4a8, 0xa3ac, 0xa2b0, 0x0804,
+ 0x1d36, 0xa5b4, 0xa4b8, 0xa3bc, 0xa2c0, 0x0804, 0x1d36, 0xa5c4,
+ 0xa4c8, 0xa3cc, 0xa2d0, 0x0804, 0x1d36, 0xa5d4, 0xa4d8, 0xa3dc,
+ 0xa2e0, 0x0804, 0x1d36, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dfa,
+ 0x9082, 0x001b, 0x0002, 0x1ce6, 0x1ce4, 0x1ce4, 0x1ce4, 0x1ce4,
+ 0x1ce4, 0x1cee, 0x1ce4, 0x1ce4, 0x1ce4, 0x1ce4, 0x1ce4, 0x1cf6,
+ 0x1ce4, 0x1ce4, 0x1ce4, 0x1ce4, 0x1ce4, 0x1cfd, 0x1ce4, 0x1ce4,
+ 0x1ce4, 0x1ce4, 0x1ce4, 0x1d04, 0x080c, 0x0dfa, 0xa56c, 0xa470,
+ 0xa774, 0xa678, 0xa37c, 0xa280, 0x0804, 0x1d36, 0xa584, 0xa488,
+ 0xa78c, 0xa690, 0xa394, 0xa298, 0x0804, 0x1d36, 0xa59c, 0xa4a0,
+ 0xa7a4, 0xa6a8, 0xa3ac, 0xa2b0, 0x04c8, 0xa5b4, 0xa4b8, 0xa7bc,
+ 0xa6c0, 0xa3c4, 0xa2c8, 0x0490, 0xa5cc, 0xa4d0, 0xa7d4, 0xa6d8,
+ 0xa3dc, 0xa2e0, 0x0458, 0xa864, 0x9084, 0x00ff, 0x9086, 0x001e,
+ 0x1518, 0x080c, 0x1f80, 0x1904, 0x1c0f, 0x900e, 0x0804, 0x1da0,
+ 0xab64, 0x939c, 0x00ff, 0x9386, 0x0048, 0x1180, 0x00c6, 0x7004,
+ 0x2060, 0x6004, 0x9086, 0x0043, 0x00ce, 0x0904, 0x1cc3, 0xab9c,
+ 0x9016, 0xad8c, 0xac90, 0xaf94, 0xae98, 0x0040, 0x9386, 0x0008,
+ 0x0904, 0x1cc3, 0x080c, 0x0dfa, 0x080c, 0x0dfa, 0x2009, 0x030f,
+ 0x2104, 0xd0fc, 0x0530, 0x0066, 0x2009, 0x0306, 0x2104, 0x9084,
+ 0x0030, 0x15c8, 0x2031, 0x1000, 0x200b, 0x4000, 0x2600, 0x9302,
+ 0x928b, 0x0000, 0xa82e, 0xa932, 0x0278, 0x9105, 0x0168, 0x2011,
+ 0x0000, 0x2618, 0x2600, 0x9500, 0xa81e, 0x9481, 0x0000, 0xa822,
+ 0xa880, 0xc0fd, 0xa882, 0x0020, 0xa82f, 0x0000, 0xa833, 0x0000,
+ 0x006e, 0x7b12, 0x7a16, 0x7d02, 0x7c06, 0x7f0a, 0x7e0e, 0x782b,
+ 0x0001, 0x7000, 0x8000, 0x7002, 0xa83c, 0x9300, 0xa83e, 0xa840,
+ 0x9201, 0xa842, 0x700c, 0x9300, 0x700e, 0x7010, 0x9201, 0x7012,
+ 0x080c, 0x1f80, 0x0428, 0x2031, 0x0080, 0x9584, 0x007f, 0x0108,
+ 0x9632, 0x7124, 0x7000, 0x9086, 0x0000, 0x1198, 0xc185, 0x7126,
+ 0x2009, 0x0306, 0x2104, 0xd0b4, 0x1904, 0x1d46, 0x200b, 0x4040,
+ 0x2009, 0x1a57, 0x2104, 0x8000, 0x0a04, 0x1d46, 0x200a, 0x0804,
+ 0x1d46, 0xc18d, 0x7126, 0xd184, 0x1d58, 0x0804, 0x1d46, 0x9006,
+ 0x002e, 0x003e, 0x004e, 0x005e, 0x006e, 0x007e, 0x0005, 0x080c,
+ 0x0dfa, 0x0026, 0x2001, 0x0105, 0x2003, 0x0010, 0x782b, 0x0004,
+ 0x7003, 0x0000, 0x7004, 0x0016, 0x080c, 0x1c02, 0x001e, 0x2060,
+ 0x6014, 0x2048, 0x080c, 0xbe37, 0x0118, 0xa880, 0xc0bd, 0xa882,
+ 0x6020, 0x9086, 0x0006, 0x1180, 0x2061, 0x0100, 0x62c8, 0x2001,
+ 0x00fa, 0x8001, 0x1df0, 0x60c8, 0x9206, 0x1dc0, 0x60c4, 0xa89a,
+ 0x60c8, 0xa896, 0x7004, 0x2060, 0x00c6, 0x080c, 0xba56, 0x00ce,
+ 0x2001, 0x19ce, 0x2004, 0x9c06, 0x1160, 0x2009, 0x0040, 0x080c,
+ 0x230a, 0x080c, 0x9b88, 0x2011, 0x0000, 0x080c, 0x9a19, 0x080c,
+ 0x8ced, 0x002e, 0x0804, 0x1f30, 0x0126, 0x2091, 0x2400, 0xa858,
+ 0x2040, 0x792c, 0x782b, 0x0002, 0x9184, 0x0700, 0x1904, 0x1da9,
+ 0x7000, 0x0002, 0x1f30, 0x1dfe, 0x1e7e, 0x1f2e, 0x8001, 0x7002,
+ 0x7027, 0x0000, 0xd19c, 0x1158, 0x8aff, 0x0904, 0x1e4b, 0x080c,
+ 0x1c09, 0x0904, 0x1f30, 0x080c, 0x1c09, 0x0804, 0x1f30, 0x782b,
+ 0x0004, 0xd194, 0x0148, 0xa880, 0xc0fc, 0xa882, 0x8aff, 0x1518,
+ 0xa87c, 0xc0f5, 0xa87e, 0x00f8, 0x0026, 0x0036, 0xab3c, 0xaa40,
+ 0x0016, 0x7910, 0xa82c, 0x9100, 0xa82e, 0x7914, 0xa830, 0x9101,
+ 0xa832, 0x001e, 0x7810, 0x931a, 0x7814, 0x9213, 0x7800, 0xa81e,
+ 0x7804, 0xa822, 0xab3e, 0xaa42, 0x003e, 0x002e, 0x080c, 0x1f9b,
+ 0xa880, 0xc0fd, 0xa882, 0x2a00, 0xa816, 0x2800, 0xa85a, 0x2c00,
+ 0xa812, 0x7003, 0x0000, 0x2009, 0x0306, 0x200b, 0x4800, 0x7027,
+ 0x0000, 0x0804, 0x1f30, 0x00f6, 0x0026, 0x781c, 0x0006, 0x7818,
+ 0x0006, 0x2079, 0x0100, 0x7a14, 0x9284, 0x1984, 0x9085, 0x0012,
+ 0x7816, 0x0036, 0x2019, 0x1000, 0x8319, 0x090c, 0x0dfa, 0x7820,
+ 0xd0bc, 0x1dd0, 0x003e, 0x79c8, 0x000e, 0x9102, 0x001e, 0x0006,
+ 0x0016, 0x79c4, 0x000e, 0x9103, 0x78c6, 0x000e, 0x78ca, 0x9284,
+ 0x1984, 0x9085, 0x0012, 0x7816, 0x002e, 0x00fe, 0x782b, 0x0008,
+ 0x7003, 0x0000, 0x080c, 0x1c02, 0x0804, 0x1f30, 0x8001, 0x7002,
+ 0x7024, 0x8004, 0x7026, 0xd194, 0x0170, 0x782c, 0xd0fc, 0x1904,
+ 0x1df1, 0xd19c, 0x1904, 0x1f2c, 0x8aff, 0x0904, 0x1f30, 0x080c,
+ 0x1c09, 0x0804, 0x1f30, 0x0026, 0x0036, 0xab3c, 0xaa40, 0x080c,
+ 0x1f9b, 0xdd9c, 0x1904, 0x1eeb, 0x2c05, 0x908a, 0x0036, 0x1a0c,
+ 0x0dfa, 0x9082, 0x001b, 0x0002, 0x1ebf, 0x1ebf, 0x1ec1, 0x1ebf,
+ 0x1ebf, 0x1ebf, 0x1ec7, 0x1ebf, 0x1ebf, 0x1ebf, 0x1ecd, 0x1ebf,
+ 0x1ebf, 0x1ebf, 0x1ed3, 0x1ebf, 0x1ebf, 0x1ebf, 0x1ed9, 0x1ebf,
+ 0x1ebf, 0x1ebf, 0x1edf, 0x1ebf, 0x1ebf, 0x1ebf, 0x1ee5, 0x080c,
+ 0x0dfa, 0xa07c, 0x931a, 0xa080, 0x9213, 0x0804, 0x1e20, 0xa08c,
+ 0x931a, 0xa090, 0x9213, 0x0804, 0x1e20, 0xa09c, 0x931a, 0xa0a0,
+ 0x9213, 0x0804, 0x1e20, 0xa0ac, 0x931a, 0xa0b0, 0x9213, 0x0804,
+ 0x1e20, 0xa0bc, 0x931a, 0xa0c0, 0x9213, 0x0804, 0x1e20, 0xa0cc,
+ 0x931a, 0xa0d0, 0x9213, 0x0804, 0x1e20, 0xa0dc, 0x931a, 0xa0e0,
+ 0x9213, 0x0804, 0x1e20, 0x2c05, 0x908a, 0x0034, 0x1a0c, 0x0dfa,
+ 0x9082, 0x001b, 0x0002, 0x1f0e, 0x1f0c, 0x1f0c, 0x1f0c, 0x1f0c,
+ 0x1f0c, 0x1f14, 0x1f0c, 0x1f0c, 0x1f0c, 0x1f0c, 0x1f0c, 0x1f1a,
+ 0x1f0c, 0x1f0c, 0x1f0c, 0x1f0c, 0x1f0c, 0x1f20, 0x1f0c, 0x1f0c,
+ 0x1f0c, 0x1f0c, 0x1f0c, 0x1f26, 0x080c, 0x0dfa, 0xa07c, 0x931a,
+ 0xa080, 0x9213, 0x0804, 0x1e20, 0xa094, 0x931a, 0xa098, 0x9213,
+ 0x0804, 0x1e20, 0xa0ac, 0x931a, 0xa0b0, 0x9213, 0x0804, 0x1e20,
+ 0xa0c4, 0x931a, 0xa0c8, 0x9213, 0x0804, 0x1e20, 0xa0dc, 0x931a,
+ 0xa0e0, 0x9213, 0x0804, 0x1e20, 0x0804, 0x1e1c, 0x080c, 0x0dfa,
+ 0x012e, 0x0005, 0x00f6, 0x00e6, 0x2071, 0x1a3d, 0x7000, 0x9086,
+ 0x0000, 0x0904, 0x1f7b, 0x2079, 0x0090, 0x2009, 0x0207, 0x210c,
+ 0xd194, 0x01b8, 0x2009, 0x020c, 0x210c, 0x9184, 0x0003, 0x0188,
+ 0x080c, 0xdc34, 0x2001, 0x0133, 0x2004, 0x9005, 0x090c, 0x0dfa,
+ 0x0016, 0x2009, 0x0040, 0x080c, 0x230a, 0x001e, 0x2001, 0x020c,
+ 0x2102, 0x2009, 0x0206, 0x2104, 0x2009, 0x0203, 0x210c, 0x9106,
+ 0x1120, 0x2009, 0x0040, 0x080c, 0x230a, 0x782c, 0xd0fc, 0x09a8,
+ 0x080c, 0x1dec, 0x7000, 0x9086, 0x0000, 0x1978, 0x782b, 0x0004,
+ 0x782c, 0xd0ac, 0x1de8, 0x2009, 0x0040, 0x080c, 0x230a, 0x782b,
+ 0x0002, 0x7003, 0x0000, 0x080c, 0x1c02, 0x00ee, 0x00fe, 0x0005,
+ 0xa880, 0xd0fc, 0x11a8, 0x8c60, 0x2c05, 0x9005, 0x0110, 0x8a51,
+ 0x0005, 0xa004, 0x9005, 0x0168, 0xa85a, 0x2040, 0xa064, 0x9084,
+ 0x000f, 0x9080, 0x1fc8, 0x2065, 0x8cff, 0x090c, 0x0dfa, 0x8a51,
+ 0x0005, 0x2050, 0x0005, 0xa880, 0xd0fc, 0x11b8, 0x8a50, 0x8c61,
+ 0x2c05, 0x9005, 0x1190, 0x2800, 0x9906, 0x0120, 0xa000, 0x9005,
+ 0x1108, 0x2900, 0x2040, 0xa85a, 0xa064, 0x9084, 0x000f, 0x9080,
+ 0x1fd8, 0x2065, 0x8cff, 0x090c, 0x0dfa, 0x0005, 0x0000, 0x001d,
+ 0x0021, 0x0025, 0x0029, 0x002d, 0x0031, 0x0035, 0x0000, 0x001b,
+ 0x0021, 0x0027, 0x002d, 0x0033, 0x0000, 0x0000, 0x0023, 0x0000,
+ 0x0000, 0x1fbb, 0x1fb7, 0x0000, 0x0000, 0x1fc5, 0x0000, 0x1fbb,
+ 0x1fc2, 0x1fc2, 0x1fbf, 0x0000, 0x0000, 0x0000, 0x1fc5, 0x1fc2,
+ 0x0000, 0x1fbd, 0x1fbd, 0x0000, 0x0000, 0x1fc5, 0x0000, 0x1fbd,
+ 0x1fc3, 0x1fc3, 0x1fc3, 0x0000, 0x0000, 0x0000, 0x1fc5, 0x1fc3,
+ 0x00c6, 0x00d6, 0x0086, 0xab42, 0xac3e, 0xa888, 0x9055, 0x0904,
+ 0x21c7, 0x2940, 0xa064, 0x90ec, 0x000f, 0x9084, 0x00ff, 0x9086,
+ 0x0008, 0x1118, 0x2061, 0x1fc3, 0x00d0, 0x9de0, 0x1fc8, 0x9d86,
+ 0x0007, 0x0130, 0x9d86, 0x000e, 0x0118, 0x9d86, 0x000f, 0x1120,
+ 0xa08c, 0x9422, 0xa090, 0x931b, 0x2c05, 0x9065, 0x1140, 0x0310,
+ 0x0804, 0x21c7, 0xa004, 0x9045, 0x0904, 0x21c7, 0x08d8, 0x2c05,
+ 0x9005, 0x0904, 0x20af, 0xdd9c, 0x1904, 0x206b, 0x908a, 0x0036,
+ 0x1a0c, 0x0dfa, 0x9082, 0x001b, 0x0002, 0x2040, 0x2040, 0x2042,
+ 0x2040, 0x2040, 0x2040, 0x2048, 0x2040, 0x2040, 0x2040, 0x204e,
+ 0x2040, 0x2040, 0x2040, 0x2054, 0x2040, 0x2040, 0x2040, 0x205a,
+ 0x2040, 0x2040, 0x2040, 0x2060, 0x2040, 0x2040, 0x2040, 0x2066,
+ 0x080c, 0x0dfa, 0xa07c, 0x9422, 0xa080, 0x931b, 0x0804, 0x20a5,
+ 0xa08c, 0x9422, 0xa090, 0x931b, 0x0804, 0x20a5, 0xa09c, 0x9422,
+ 0xa0a0, 0x931b, 0x0804, 0x20a5, 0xa0ac, 0x9422, 0xa0b0, 0x931b,
+ 0x0804, 0x20a5, 0xa0bc, 0x9422, 0xa0c0, 0x931b, 0x0804, 0x20a5,
+ 0xa0cc, 0x9422, 0xa0d0, 0x931b, 0x0804, 0x20a5, 0xa0dc, 0x9422,
+ 0xa0e0, 0x931b, 0x04d0, 0x908a, 0x0034, 0x1a0c, 0x0dfa, 0x9082,
+ 0x001b, 0x0002, 0x208d, 0x208b, 0x208b, 0x208b, 0x208b, 0x208b,
+ 0x2092, 0x208b, 0x208b, 0x208b, 0x208b, 0x208b, 0x2097, 0x208b,
+ 0x208b, 0x208b, 0x208b, 0x208b, 0x209c, 0x208b, 0x208b, 0x208b,
+ 0x208b, 0x208b, 0x20a1, 0x080c, 0x0dfa, 0xa07c, 0x9422, 0xa080,
+ 0x931b, 0x0098, 0xa094, 0x9422, 0xa098, 0x931b, 0x0070, 0xa0ac,
+ 0x9422, 0xa0b0, 0x931b, 0x0048, 0xa0c4, 0x9422, 0xa0c8, 0x931b,
+ 0x0020, 0xa0dc, 0x9422, 0xa0e0, 0x931b, 0x0630, 0x2300, 0x9405,
+ 0x0160, 0x8a51, 0x0904, 0x21c7, 0x8c60, 0x0804, 0x2017, 0xa004,
+ 0x9045, 0x0904, 0x21c7, 0x0804, 0x1ff2, 0x8a51, 0x0904, 0x21c7,
+ 0x8c60, 0x2c05, 0x9005, 0x1158, 0xa004, 0x9045, 0x0904, 0x21c7,
+ 0xa064, 0x90ec, 0x000f, 0x9de0, 0x1fc8, 0x2c05, 0x2060, 0xa880,
+ 0xc0fc, 0xa882, 0x0804, 0x21bc, 0x2c05, 0x8422, 0x8420, 0x831a,
+ 0x9399, 0x0000, 0xac2e, 0xab32, 0xdd9c, 0x1904, 0x2159, 0x9082,
+ 0x001b, 0x0002, 0x20f5, 0x20f5, 0x20f7, 0x20f5, 0x20f5, 0x20f5,
+ 0x2105, 0x20f5, 0x20f5, 0x20f5, 0x2113, 0x20f5, 0x20f5, 0x20f5,
+ 0x2121, 0x20f5, 0x20f5, 0x20f5, 0x212f, 0x20f5, 0x20f5, 0x20f5,
+ 0x213d, 0x20f5, 0x20f5, 0x20f5, 0x214b, 0x080c, 0x0dfa, 0xa17c,
+ 0x2400, 0x9122, 0xa180, 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa074,
+ 0x9420, 0xa078, 0x9319, 0x0804, 0x21b7, 0xa18c, 0x2400, 0x9122,
+ 0xa190, 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa084, 0x9420, 0xa088,
+ 0x9319, 0x0804, 0x21b7, 0xa19c, 0x2400, 0x9122, 0xa1a0, 0x2300,
+ 0x911b, 0x0a0c, 0x0dfa, 0xa094, 0x9420, 0xa098, 0x9319, 0x0804,
+ 0x21b7, 0xa1ac, 0x2400, 0x9122, 0xa1b0, 0x2300, 0x911b, 0x0a0c,
+ 0x0dfa, 0xa0a4, 0x9420, 0xa0a8, 0x9319, 0x0804, 0x21b7, 0xa1bc,
+ 0x2400, 0x9122, 0xa1c0, 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa0b4,
+ 0x9420, 0xa0b8, 0x9319, 0x0804, 0x21b7, 0xa1cc, 0x2400, 0x9122,
+ 0xa1d0, 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa0c4, 0x9420, 0xa0c8,
+ 0x9319, 0x0804, 0x21b7, 0xa1dc, 0x2400, 0x9122, 0xa1e0, 0x2300,
+ 0x911b, 0x0a0c, 0x0dfa, 0xa0d4, 0x9420, 0xa0d8, 0x9319, 0x0804,
+ 0x21b7, 0x9082, 0x001b, 0x0002, 0x2177, 0x2175, 0x2175, 0x2175,
+ 0x2175, 0x2175, 0x2184, 0x2175, 0x2175, 0x2175, 0x2175, 0x2175,
+ 0x2191, 0x2175, 0x2175, 0x2175, 0x2175, 0x2175, 0x219e, 0x2175,
+ 0x2175, 0x2175, 0x2175, 0x2175, 0x21ab, 0x080c, 0x0dfa, 0xa17c,
+ 0x2400, 0x9122, 0xa180, 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa06c,
+ 0x9420, 0xa070, 0x9319, 0x0498, 0xa194, 0x2400, 0x9122, 0xa198,
+ 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa084, 0x9420, 0xa088, 0x9319,
+ 0x0430, 0xa1ac, 0x2400, 0x9122, 0xa1b0, 0x2300, 0x911b, 0x0a0c,
+ 0x0dfa, 0xa09c, 0x9420, 0xa0a0, 0x9319, 0x00c8, 0xa1c4, 0x2400,
+ 0x9122, 0xa1c8, 0x2300, 0x911b, 0x0a0c, 0x0dfa, 0xa0b4, 0x9420,
+ 0xa0b8, 0x9319, 0x0060, 0xa1dc, 0x2400, 0x9122, 0xa1e0, 0x2300,
+ 0x911b, 0x0a0c, 0x0dfa, 0xa0cc, 0x9420, 0xa0d0, 0x9319, 0xac1e,
+ 0xab22, 0xa880, 0xc0fd, 0xa882, 0x2800, 0xa85a, 0x2c00, 0xa812,
+ 0x2a00, 0xa816, 0x000e, 0x000e, 0x000e, 0x9006, 0x0028, 0x008e,
+ 0x00de, 0x00ce, 0x9085, 0x0001, 0x0005, 0x2001, 0x0005, 0x2004,
+ 0xd0bc, 0x190c, 0x0df3, 0x9084, 0x0007, 0x0002, 0x21e8, 0x1dec,
+ 0x21e8, 0x21de, 0x21e1, 0x21e4, 0x21e1, 0x21e4, 0x080c, 0x1dec,
+ 0x0005, 0x080c, 0x11e8, 0x0005, 0x080c, 0x1dec, 0x080c, 0x11e8,
+ 0x0005, 0x0126, 0x2091, 0x2600, 0x2079, 0x0200, 0x2071, 0x0260,
+ 0x2069, 0x1800, 0x7817, 0x0000, 0x789b, 0x0814, 0x78a3, 0x0406,
+ 0x789f, 0x0410, 0x2009, 0x013b, 0x200b, 0x0400, 0x781b, 0x0002,
+ 0x783b, 0x001f, 0x7837, 0x0020, 0x7803, 0x1600, 0x012e, 0x0005,
+ 0x2091, 0x2600, 0x781c, 0xd0a4, 0x190c, 0x2307, 0x7900, 0xd1dc,
+ 0x1118, 0x9084, 0x0006, 0x001a, 0x9084, 0x000e, 0x0002, 0x222f,
+ 0x2227, 0x7bb4, 0x2227, 0x2229, 0x2229, 0x2229, 0x2229, 0x7b9a,
+ 0x2227, 0x222b, 0x2227, 0x2229, 0x2227, 0x2229, 0x2227, 0x080c,
+ 0x0dfa, 0x0031, 0x0020, 0x080c, 0x7b9a, 0x080c, 0x7bb4, 0x0005,
+ 0x0006, 0x0016, 0x0026, 0x080c, 0xdc34, 0x7930, 0x9184, 0x0003,
+ 0x01c0, 0x2001, 0x19ce, 0x2004, 0x9005, 0x0170, 0x2001, 0x0133,
+ 0x2004, 0x9005, 0x090c, 0x0dfa, 0x00c6, 0x2001, 0x19ce, 0x2064,
+ 0x080c, 0xba56, 0x00ce, 0x00f8, 0x2009, 0x0040, 0x080c, 0x230a,
+ 0x00d0, 0x9184, 0x0014, 0x01a0, 0x6a00, 0x9286, 0x0003, 0x0160,
+ 0x080c, 0x7207, 0x1138, 0x080c, 0x7504, 0x080c, 0x5f2b, 0x080c,
+ 0x7127, 0x0010, 0x080c, 0x5dea, 0x080c, 0x7c63, 0x0041, 0x0018,
+ 0x9184, 0x9540, 0x1dc8, 0x002e, 0x001e, 0x000e, 0x0005, 0x00e6,
+ 0x0036, 0x0046, 0x0056, 0x2071, 0x1a3a, 0x080c, 0x19ff, 0x005e,
+ 0x004e, 0x003e, 0x00ee, 0x0005, 0x0126, 0x2091, 0x2e00, 0x2071,
+ 0x1800, 0x7128, 0x2001, 0x1947, 0x2102, 0x2001, 0x194f, 0x2102,
+ 0x2001, 0x013b, 0x2102, 0x2079, 0x0200, 0x2001, 0x0201, 0x789e,
+ 0x78a3, 0x0200, 0x9198, 0x0007, 0x831c, 0x831c, 0x831c, 0x9398,
+ 0x0005, 0x2320, 0x9182, 0x0204, 0x1230, 0x2011, 0x0008, 0x8423,
+ 0x8423, 0x8423, 0x0488, 0x9182, 0x024c, 0x1240, 0x2011, 0x0007,
+ 0x8403, 0x8003, 0x9400, 0x9400, 0x9420, 0x0430, 0x9182, 0x02bc,
+ 0x1238, 0x2011, 0x0006, 0x8403, 0x8003, 0x9400, 0x9420, 0x00e0,
+ 0x9182, 0x034c, 0x1230, 0x2011, 0x0005, 0x8403, 0x8003, 0x9420,
+ 0x0098, 0x9182, 0x042c, 0x1228, 0x2011, 0x0004, 0x8423, 0x8423,
+ 0x0058, 0x9182, 0x059c, 0x1228, 0x2011, 0x0003, 0x8403, 0x9420,
+ 0x0018, 0x2011, 0x0002, 0x8423, 0x9482, 0x0228, 0x8002, 0x8020,
+ 0x8301, 0x9402, 0x0110, 0x0208, 0x8321, 0x8217, 0x8203, 0x9405,
+ 0x789a, 0x012e, 0x0005, 0x0006, 0x00d6, 0x2069, 0x0200, 0x6814,
+ 0x9084, 0xffc0, 0x910d, 0x6916, 0x00de, 0x000e, 0x0005, 0x00d6,
+ 0x2069, 0x0200, 0x9005, 0x6810, 0x0110, 0xc0a5, 0x0008, 0xc0a4,
+ 0x6812, 0x00de, 0x0005, 0x0006, 0x00d6, 0x2069, 0x0200, 0x6810,
+ 0x9084, 0xfff8, 0x910d, 0x6912, 0x00de, 0x000e, 0x0005, 0x7938,
+ 0x080c, 0x0df3, 0x00f6, 0x2079, 0x0200, 0x7902, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0xa001, 0xa001, 0x7902, 0xa001, 0xa001, 0xa001,
+ 0xa001, 0xa001, 0xa001, 0x00fe, 0x0005, 0x0126, 0x2091, 0x2800,
+ 0x2061, 0x0100, 0x2071, 0x1800, 0x2009, 0x0000, 0x080c, 0x2bb6,
+ 0x080c, 0x2a89, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0558,
+ 0x6054, 0x8004, 0x8004, 0x8004, 0x8004, 0x9084, 0x000c, 0x6150,
+ 0x918c, 0xfff3, 0x9105, 0x6052, 0x6050, 0x9084, 0xb17f, 0x9085,
+ 0x2000, 0x6052, 0x2009, 0x1975, 0x2011, 0x1976, 0x6358, 0x939c,
+ 0x38f0, 0x2320, 0x080c, 0x2af6, 0x1238, 0x939d, 0x4003, 0x94a5,
+ 0x8603, 0x230a, 0x2412, 0x0030, 0x939d, 0x0203, 0x94a5, 0x8603,
+ 0x230a, 0x2412, 0x0050, 0x2001, 0x1975, 0x2003, 0x0700, 0x2001,
+ 0x1976, 0x2003, 0x0700, 0x080c, 0x2cc2, 0x9006, 0x080c, 0x2ab8,
+ 0x9006, 0x080c, 0x2a9b, 0x20a9, 0x0012, 0x1d04, 0x236d, 0x2091,
+ 0x6000, 0x1f04, 0x236d, 0x602f, 0x0100, 0x602f, 0x0000, 0x6050,
+ 0x9085, 0x0400, 0x9084, 0xdfff, 0x6052, 0x6024, 0x6026, 0x080c,
+ 0x27a7, 0x2009, 0x00ef, 0x6132, 0x6136, 0x080c, 0x27b7, 0x60e7,
+ 0x0000, 0x61ea, 0x60e3, 0x0002, 0x604b, 0xf7f7, 0x6043, 0x0000,
+ 0x602f, 0x0080, 0x602f, 0x0000, 0x6007, 0x149f, 0x60bb, 0x0000,
+ 0x20a9, 0x0018, 0x60bf, 0x0000, 0x1f04, 0x239a, 0x60bb, 0x0000,
+ 0x60bf, 0x0108, 0x60bf, 0x0012, 0x60bf, 0x0320, 0x60bf, 0x0018,
+ 0x601b, 0x00f0, 0x601f, 0x001e, 0x600f, 0x006b, 0x602b, 0x402f,
+ 0x012e, 0x0005, 0x00f6, 0x2079, 0x0140, 0x78c3, 0x0080, 0x78c3,
+ 0x0083, 0x78c3, 0x0000, 0x00fe, 0x0005, 0x2001, 0x1834, 0x2003,
+ 0x0000, 0x2001, 0x1833, 0x2003, 0x0001, 0x0005, 0x0126, 0x2091,
+ 0x2800, 0x0006, 0x0016, 0x0026, 0x6124, 0x9184, 0x5e2c, 0x1118,
+ 0x9184, 0x0007, 0x002a, 0x9195, 0x0004, 0x9284, 0x0007, 0x0002,
+ 0x23fa, 0x23e0, 0x23e3, 0x23e6, 0x23eb, 0x23ed, 0x23f1, 0x23f5,
+ 0x080c, 0x8563, 0x00b8, 0x080c, 0x8632, 0x00a0, 0x080c, 0x8632,
+ 0x080c, 0x8563, 0x0078, 0x0099, 0x0068, 0x080c, 0x8563, 0x0079,
+ 0x0048, 0x080c, 0x8632, 0x0059, 0x0028, 0x080c, 0x8632, 0x080c,
+ 0x8563, 0x0029, 0x002e, 0x001e, 0x000e, 0x012e, 0x0005, 0x00a6,
+ 0x6124, 0x6028, 0xd09c, 0x0118, 0xd19c, 0x1904, 0x2648, 0xd1f4,
+ 0x190c, 0x0df3, 0x080c, 0x7207, 0x0904, 0x2455, 0x080c, 0xc539,
+ 0x1120, 0x7000, 0x9086, 0x0003, 0x0570, 0x6024, 0x9084, 0x1800,
+ 0x0550, 0x080c, 0x722a, 0x0118, 0x080c, 0x7218, 0x1520, 0x6027,
+ 0x0020, 0x6043, 0x0000, 0x080c, 0xc539, 0x0168, 0x080c, 0x722a,
+ 0x1150, 0x2001, 0x197f, 0x2003, 0x0001, 0x6027, 0x1800, 0x080c,
+ 0x7076, 0x0804, 0x264b, 0x70a0, 0x9005, 0x1150, 0x70a3, 0x0001,
+ 0x00d6, 0x2069, 0x0140, 0x080c, 0x725e, 0x00de, 0x1904, 0x264b,
+ 0x080c, 0x750e, 0x0428, 0x080c, 0x722a, 0x1590, 0x6024, 0x9084,
+ 0x1800, 0x1108, 0x0468, 0x080c, 0x750e, 0x080c, 0x7504, 0x080c,
+ 0x5f2b, 0x080c, 0x7127, 0x0804, 0x2648, 0xd1ac, 0x1508, 0x6024,
+ 0xd0dc, 0x1170, 0xd0e4, 0x1178, 0xd0d4, 0x1190, 0xd0cc, 0x0130,
+ 0x7094, 0x9086, 0x0028, 0x1110, 0x080c, 0x73f3, 0x0804, 0x2648,
+ 0x080c, 0x7509, 0x0048, 0x2001, 0x1955, 0x2003, 0x0002, 0x0020,
+ 0x080c, 0x7359, 0x0804, 0x2648, 0x080c, 0x748d, 0x0804, 0x2648,
+ 0xd1ac, 0x0904, 0x2569, 0x080c, 0x7207, 0x11c0, 0x6027, 0x0020,
+ 0x0006, 0x0026, 0x0036, 0x080c, 0x7221, 0x1158, 0x080c, 0x7504,
+ 0x080c, 0x5f2b, 0x080c, 0x7127, 0x003e, 0x002e, 0x000e, 0x00ae,
+ 0x0005, 0x003e, 0x002e, 0x000e, 0x080c, 0x71df, 0x0016, 0x0046,
+ 0x00c6, 0x644c, 0x9486, 0xf0f0, 0x1138, 0x2061, 0x0100, 0x644a,
+ 0x6043, 0x0090, 0x6043, 0x0010, 0x74d6, 0x948c, 0xff00, 0x7038,
+ 0xd084, 0x0178, 0x9186, 0xf800, 0x1160, 0x7044, 0xd084, 0x1148,
+ 0xc085, 0x7046, 0x0036, 0x2418, 0x2011, 0x8016, 0x080c, 0x4b1f,
+ 0x003e, 0x080c, 0xc532, 0x1904, 0x2546, 0x9196, 0xff00, 0x05a8,
+ 0x705c, 0x9084, 0x00ff, 0x810f, 0x81ff, 0x0110, 0x9116, 0x0568,
+ 0x7130, 0xd184, 0x1550, 0x080c, 0x32e4, 0x0128, 0xc18d, 0x7132,
+ 0x080c, 0x67bb, 0x1510, 0x6240, 0x9294, 0x0010, 0x0130, 0x6248,
+ 0x9294, 0xff00, 0x9296, 0xff00, 0x01c0, 0x7030, 0xd08c, 0x0904,
+ 0x2546, 0x7038, 0xd08c, 0x1140, 0x2001, 0x180c, 0x200c, 0xd1ac,
+ 0x1904, 0x2546, 0xc1ad, 0x2102, 0x0036, 0x73d4, 0x2011, 0x8013,
+ 0x080c, 0x4b1f, 0x003e, 0x0804, 0x2546, 0x7038, 0xd08c, 0x1140,
+ 0x2001, 0x180c, 0x200c, 0xd1ac, 0x1904, 0x2546, 0xc1ad, 0x2102,
+ 0x0036, 0x73d4, 0x2011, 0x8013, 0x080c, 0x4b1f, 0x003e, 0x7130,
+ 0xc185, 0x7132, 0x2011, 0x185c, 0x220c, 0x00f0, 0x0016, 0x2009,
+ 0x0001, 0x2011, 0x0100, 0x080c, 0x84d1, 0x2019, 0x000e, 0x00c6,
+ 0x2061, 0x0000, 0x080c, 0xd801, 0x00ce, 0x9484, 0x00ff, 0x9080,
+ 0x32e9, 0x200d, 0x918c, 0xff00, 0x810f, 0x2120, 0x9006, 0x2009,
+ 0x000e, 0x080c, 0xd885, 0x001e, 0xd1ac, 0x1148, 0x0016, 0x2009,
+ 0x0002, 0x2019, 0x0004, 0x080c, 0x3156, 0x001e, 0x0078, 0x0156,
+ 0x00b6, 0x20a9, 0x007f, 0x900e, 0x080c, 0x649f, 0x1110, 0x080c,
+ 0x5f45, 0x8108, 0x1f04, 0x253c, 0x00be, 0x015e, 0x00ce, 0x004e,
+ 0x080c, 0xa069, 0x60e3, 0x0000, 0x001e, 0x2001, 0x1800, 0x2014,
+ 0x9296, 0x0004, 0x1170, 0xd19c, 0x11a0, 0x2011, 0x180c, 0x2214,
+ 0xd29c, 0x1120, 0x6204, 0x9295, 0x0002, 0x6206, 0x6228, 0xc29d,
+ 0x622a, 0x2003, 0x0001, 0x2001, 0x1825, 0x2003, 0x0000, 0x6027,
+ 0x0020, 0xd194, 0x0904, 0x2648, 0x0016, 0x6220, 0xd2b4, 0x0904,
+ 0x25f1, 0x080c, 0x835a, 0x080c, 0x9656, 0x6027, 0x0004, 0x00f6,
+ 0x2019, 0x19c8, 0x2304, 0x907d, 0x0904, 0x25c0, 0x7804, 0x9086,
+ 0x0032, 0x15f0, 0x00d6, 0x00c6, 0x00e6, 0x0096, 0x2069, 0x0140,
+ 0x782c, 0x685e, 0x7808, 0x685a, 0x6043, 0x0002, 0x2001, 0x0003,
+ 0x8001, 0x1df0, 0x6043, 0x0000, 0x2001, 0x003c, 0x8001, 0x1df0,
+ 0x080c, 0x2c98, 0x2001, 0x001e, 0x8001, 0x0240, 0x20a9, 0x0009,
+ 0x080c, 0x2b91, 0x6904, 0xd1dc, 0x1140, 0x0cb0, 0x2001, 0x0100,
+ 0x080c, 0x2c88, 0x9006, 0x080c, 0x2c88, 0x080c, 0x8b04, 0x080c,
+ 0x8c10, 0x7814, 0x2048, 0xa867, 0x0103, 0x2f60, 0x080c, 0xa0e3,
+ 0x009e, 0x00ee, 0x00ce, 0x00de, 0x00fe, 0x001e, 0x00ae, 0x0005,
+ 0x00fe, 0x00d6, 0x2069, 0x0140, 0x6804, 0x9084, 0x4000, 0x0110,
+ 0x080c, 0x2c98, 0x00de, 0x00c6, 0x2061, 0x19bf, 0x6028, 0x080c,
+ 0xc539, 0x0120, 0x909a, 0x0003, 0x1258, 0x0018, 0x909a, 0x00c8,
+ 0x1238, 0x8000, 0x602a, 0x00ce, 0x080c, 0x9632, 0x0804, 0x2647,
+ 0x2061, 0x0100, 0x62c0, 0x080c, 0x9eef, 0x2019, 0x19c8, 0x2304,
+ 0x9065, 0x0120, 0x2009, 0x0027, 0x080c, 0xa15d, 0x00ce, 0x0804,
+ 0x2647, 0xd2bc, 0x0904, 0x2634, 0x080c, 0x8367, 0x6014, 0x9084,
+ 0x1984, 0x9085, 0x0010, 0x6016, 0x6027, 0x0004, 0x00d6, 0x2069,
+ 0x0140, 0x6804, 0x9084, 0x4000, 0x0110, 0x080c, 0x2c98, 0x00de,
+ 0x00c6, 0x2061, 0x19bf, 0x6044, 0x080c, 0xc539, 0x0120, 0x909a,
+ 0x0003, 0x1628, 0x0018, 0x909a, 0x00c8, 0x1608, 0x8000, 0x6046,
+ 0x603c, 0x00ce, 0x9005, 0x0558, 0x2009, 0x07d0, 0x080c, 0x835f,
+ 0x9080, 0x0008, 0x2004, 0x9086, 0x0006, 0x1138, 0x6114, 0x918c,
+ 0x1984, 0x918d, 0x0012, 0x6116, 0x00d0, 0x6114, 0x918c, 0x1984,
+ 0x918d, 0x0016, 0x6116, 0x0098, 0x6027, 0x0004, 0x0080, 0x0036,
+ 0x2019, 0x0001, 0x080c, 0x999d, 0x003e, 0x2019, 0x19ce, 0x2304,
+ 0x9065, 0x0120, 0x2009, 0x004f, 0x080c, 0xa15d, 0x00ce, 0x001e,
+ 0xd19c, 0x0904, 0x2712, 0x7038, 0xd0ac, 0x1904, 0x26e7, 0x0016,
+ 0x0156, 0x6027, 0x0008, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x0904, 0x26c4, 0x6050, 0x9085, 0x0040, 0x6052, 0x6050, 0x9084,
+ 0xfbcf, 0x6052, 0x080c, 0x2bb0, 0x9085, 0x2000, 0x6052, 0x20a9,
+ 0x0012, 0x1d04, 0x2669, 0x080c, 0x838e, 0x1f04, 0x2669, 0x6050,
+ 0x9085, 0x0400, 0x9084, 0xdfbf, 0x6052, 0x20a9, 0x0028, 0xa001,
+ 0x1f04, 0x2677, 0x6150, 0x9185, 0x1400, 0x6052, 0x20a9, 0x0366,
+ 0x1d04, 0x2680, 0x080c, 0x838e, 0x6020, 0xd09c, 0x1138, 0x015e,
+ 0x6152, 0x001e, 0x6027, 0x0008, 0x0804, 0x2712, 0x080c, 0x2b78,
+ 0x1f04, 0x2680, 0x015e, 0x6152, 0x001e, 0x6027, 0x0008, 0x0016,
+ 0x6028, 0xc09c, 0x602a, 0x080c, 0xa069, 0x60e3, 0x0000, 0x080c,
+ 0xdc13, 0x080c, 0xdc2e, 0x080c, 0x55df, 0xd0fc, 0x1138, 0x080c,
+ 0xc532, 0x1120, 0x9085, 0x0001, 0x080c, 0x724e, 0x9006, 0x080c,
+ 0x2c88, 0x2009, 0x0002, 0x080c, 0x2bb6, 0x00e6, 0x2071, 0x1800,
+ 0x7003, 0x0004, 0x080c, 0x0ec6, 0x00ee, 0x6027, 0x0008, 0x080c,
+ 0x0b8f, 0x001e, 0x0804, 0x2712, 0x080c, 0x2cc2, 0x080c, 0x2cf5,
+ 0x6050, 0xc0e5, 0x6052, 0x20a9, 0x0367, 0x1f04, 0x26e5, 0x1d04,
+ 0x26cf, 0x080c, 0x838e, 0x6020, 0xd09c, 0x1db8, 0x00f6, 0x2079,
+ 0x0100, 0x080c, 0x2b06, 0x00fe, 0x1d80, 0x6050, 0xc0e4, 0x6052,
+ 0x6027, 0x0008, 0x015e, 0x001e, 0x0468, 0x015e, 0x001e, 0x0016,
+ 0x6028, 0xc09c, 0x602a, 0x080c, 0xa069, 0x60e3, 0x0000, 0x080c,
+ 0xdc13, 0x080c, 0xdc2e, 0x080c, 0x55df, 0xd0fc, 0x1138, 0x080c,
+ 0xc532, 0x1120, 0x9085, 0x0001, 0x080c, 0x724e, 0x9006, 0x080c,
+ 0x2c88, 0x2009, 0x0002, 0x080c, 0x2bb6, 0x00e6, 0x2071, 0x1800,
+ 0x7003, 0x0004, 0x080c, 0x0ec6, 0x00ee, 0x6027, 0x0008, 0x080c,
+ 0x0b8f, 0x001e, 0x918c, 0xffd0, 0x6126, 0x00ae, 0x0005, 0x0006,
+ 0x0016, 0x0026, 0x0036, 0x00e6, 0x00f6, 0x0126, 0x2091, 0x8000,
+ 0x2071, 0x1800, 0x71cc, 0x70ce, 0x9116, 0x0904, 0x2766, 0x81ff,
+ 0x01a0, 0x2009, 0x0000, 0x080c, 0x2bb6, 0x2011, 0x8011, 0x2019,
+ 0x010e, 0x231c, 0x939e, 0x0007, 0x1118, 0x2019, 0x0001, 0x0010,
+ 0x2019, 0x0000, 0x080c, 0x4b1f, 0x0448, 0x2001, 0x1980, 0x200c,
+ 0x81ff, 0x1140, 0x2001, 0x0109, 0x2004, 0xd0b4, 0x0118, 0x2019,
+ 0x0003, 0x0008, 0x2118, 0x2011, 0x8012, 0x080c, 0x4b1f, 0x080c,
+ 0x0ec6, 0x080c, 0x55df, 0xd0fc, 0x1188, 0x080c, 0xc532, 0x1170,
+ 0x00c6, 0x080c, 0x2802, 0x080c, 0x9904, 0x2061, 0x0100, 0x2019,
+ 0x0028, 0x2009, 0x0002, 0x080c, 0x3156, 0x00ce, 0x012e, 0x00fe,
+ 0x00ee, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005, 0x2028, 0x918c,
+ 0x00ff, 0x2130, 0x9094, 0xff00, 0x11f0, 0x2011, 0x1836, 0x2214,
+ 0xd2ac, 0x11c8, 0x81ff, 0x01e8, 0x2011, 0x181e, 0x2204, 0x9106,
+ 0x1190, 0x2011, 0x181f, 0x2214, 0x9294, 0xff00, 0x9584, 0xff00,
+ 0x9206, 0x1148, 0x2011, 0x181f, 0x2214, 0x9294, 0x00ff, 0x9584,
+ 0x00ff, 0x9206, 0x1120, 0x2500, 0x080c, 0x7ec0, 0x0048, 0x9584,
+ 0x00ff, 0x9080, 0x32e9, 0x200d, 0x918c, 0xff00, 0x810f, 0x9006,
+ 0x0005, 0x9080, 0x32e9, 0x200d, 0x918c, 0x00ff, 0x0005, 0x00d6,
+ 0x2069, 0x0140, 0x2001, 0x1817, 0x2003, 0x00ef, 0x20a9, 0x0010,
+ 0x9006, 0x6852, 0x6856, 0x1f04, 0x27b2, 0x00de, 0x0005, 0x0006,
+ 0x00d6, 0x0026, 0x2069, 0x0140, 0x2001, 0x1817, 0x2102, 0x8114,
+ 0x8214, 0x8214, 0x8214, 0x20a9, 0x0010, 0x6853, 0x0000, 0x9006,
+ 0x82ff, 0x1128, 0x9184, 0x000f, 0x9080, 0xe568, 0x2005, 0x6856,
+ 0x8211, 0x1f04, 0x27c7, 0x002e, 0x00de, 0x000e, 0x0005, 0x00c6,
+ 0x2061, 0x1800, 0x6030, 0x0110, 0xc09d, 0x0008, 0xc09c, 0x6032,
+ 0x00ce, 0x0005, 0x0156, 0x00d6, 0x0026, 0x0016, 0x0006, 0x2069,
+ 0x0140, 0x6980, 0x9116, 0x0180, 0x9112, 0x1230, 0x8212, 0x8210,
+ 0x22a8, 0x2001, 0x0402, 0x0018, 0x22a8, 0x2001, 0x0404, 0x680e,
+ 0x1f04, 0x27f7, 0x680f, 0x0000, 0x000e, 0x001e, 0x002e, 0x00de,
+ 0x015e, 0x0005, 0x080c, 0x55db, 0xd0c4, 0x0150, 0xd0a4, 0x0140,
+ 0x9006, 0x0046, 0x2020, 0x2009, 0x002e, 0x080c, 0xd885, 0x004e,
+ 0x0005, 0x00f6, 0x0016, 0x0026, 0x2079, 0x0140, 0x78c4, 0xd0dc,
+ 0x0904, 0x286e, 0x080c, 0x2af6, 0x0660, 0x9084, 0x0700, 0x908e,
+ 0x0600, 0x1120, 0x2011, 0x4000, 0x900e, 0x0458, 0x908e, 0x0500,
+ 0x1120, 0x2011, 0x8000, 0x900e, 0x0420, 0x908e, 0x0400, 0x1120,
+ 0x9016, 0x2009, 0x0001, 0x00e8, 0x908e, 0x0300, 0x1120, 0x9016,
+ 0x2009, 0x0002, 0x00b0, 0x908e, 0x0200, 0x1120, 0x9016, 0x2009,
+ 0x0004, 0x0078, 0x908e, 0x0100, 0x1548, 0x9016, 0x2009, 0x0008,
+ 0x0040, 0x9084, 0x0700, 0x908e, 0x0300, 0x1500, 0x2011, 0x0030,
+ 0x0058, 0x2300, 0x9080, 0x0020, 0x2018, 0x080c, 0x84ff, 0x928c,
+ 0xff00, 0x0110, 0x2011, 0x00ff, 0x2200, 0x8007, 0x9085, 0x004c,
+ 0x78c2, 0x2009, 0x0138, 0x220a, 0x080c, 0x7207, 0x1118, 0x2009,
+ 0x1945, 0x220a, 0x002e, 0x001e, 0x00fe, 0x0005, 0x78c3, 0x0000,
+ 0x0cc8, 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x2001,
+ 0x0170, 0x200c, 0x8000, 0x2014, 0x9184, 0x0003, 0x0110, 0x080c,
+ 0x0df3, 0x002e, 0x001e, 0x000e, 0x012e, 0x0005, 0x2001, 0x0171,
+ 0x2004, 0xd0dc, 0x0168, 0x2001, 0x0170, 0x200c, 0x918c, 0x00ff,
+ 0x918e, 0x004c, 0x1128, 0x200c, 0x918c, 0xff00, 0x810f, 0x0005,
+ 0x900e, 0x2001, 0x0227, 0x2004, 0x8007, 0x9084, 0x00ff, 0x8004,
+ 0x9108, 0x2001, 0x0226, 0x2004, 0x8007, 0x9084, 0x00ff, 0x8004,
+ 0x9108, 0x0005, 0x0018, 0x000c, 0x0018, 0x0020, 0x1000, 0x0800,
+ 0x1000, 0x1800, 0x0156, 0x0006, 0x0016, 0x0026, 0x00e6, 0x2001,
+ 0x1968, 0x2004, 0x908a, 0x0007, 0x1a0c, 0x0dfa, 0x0033, 0x00ee,
+ 0x002e, 0x001e, 0x000e, 0x015e, 0x0005, 0x28cc, 0x28ea, 0x290e,
+ 0x2910, 0x2939, 0x293b, 0x293d, 0x2001, 0x0001, 0x080c, 0x2717,
+ 0x080c, 0x2b6a, 0x2001, 0x196a, 0x2003, 0x0000, 0x7828, 0x9084,
+ 0xe1d7, 0x782a, 0x9006, 0x20a9, 0x0009, 0x080c, 0x2b12, 0x2001,
+ 0x1968, 0x2003, 0x0006, 0x2009, 0x001e, 0x2011, 0x293e, 0x080c,
+ 0x836c, 0x0005, 0x2009, 0x196d, 0x200b, 0x0000, 0x2001, 0x1972,
+ 0x2003, 0x0036, 0x2001, 0x1971, 0x2003, 0x002a, 0x2001, 0x196a,
+ 0x2003, 0x0001, 0x9006, 0x080c, 0x2a9b, 0x2001, 0xffff, 0x20a9,
+ 0x0009, 0x080c, 0x2b12, 0x2001, 0x1968, 0x2003, 0x0006, 0x2009,
+ 0x001e, 0x2011, 0x293e, 0x080c, 0x836c, 0x0005, 0x080c, 0x0dfa,
+ 0x2001, 0x1972, 0x2003, 0x0036, 0x2001, 0x196a, 0x2003, 0x0003,
+ 0x7a38, 0x9294, 0x0005, 0x9296, 0x0004, 0x0110, 0x9006, 0x0010,
+ 0x2001, 0x0001, 0x080c, 0x2a9b, 0x2001, 0x196e, 0x2003, 0x0000,
+ 0x2001, 0xffff, 0x20a9, 0x0009, 0x080c, 0x2b12, 0x2001, 0x1968,
+ 0x2003, 0x0006, 0x2009, 0x001e, 0x2011, 0x293e, 0x080c, 0x836c,
+ 0x0005, 0x080c, 0x0dfa, 0x080c, 0x0dfa, 0x0005, 0x0006, 0x0016,
+ 0x0026, 0x00e6, 0x00f6, 0x0156, 0x0126, 0x2091, 0x8000, 0x2079,
+ 0x0100, 0x2001, 0x196a, 0x2004, 0x908a, 0x0007, 0x1a0c, 0x0dfa,
+ 0x0043, 0x012e, 0x015e, 0x00fe, 0x00ee, 0x002e, 0x001e, 0x000e,
+ 0x0005, 0x2960, 0x2980, 0x29c0, 0x29f0, 0x2a14, 0x2a24, 0x2a26,
+ 0x080c, 0x2b06, 0x11b0, 0x7850, 0x9084, 0xefff, 0x7852, 0x2009,
+ 0x1970, 0x2104, 0x7a38, 0x9294, 0x0005, 0x9296, 0x0004, 0x0110,
+ 0xc08d, 0x0008, 0xc085, 0x200a, 0x2001, 0x1968, 0x2003, 0x0001,
+ 0x0030, 0x080c, 0x2a4a, 0x2001, 0xffff, 0x080c, 0x28db, 0x0005,
+ 0x080c, 0x2a28, 0x05e0, 0x2009, 0x1971, 0x2104, 0x8001, 0x200a,
+ 0x080c, 0x2b06, 0x1178, 0x7850, 0x9084, 0xefff, 0x7852, 0x7a38,
+ 0x9294, 0x0005, 0x9296, 0x0005, 0x0518, 0x2009, 0x1970, 0x2104,
+ 0xc085, 0x200a, 0x2009, 0x196d, 0x2104, 0x8000, 0x200a, 0x9086,
+ 0x0005, 0x0118, 0x080c, 0x2a30, 0x00c0, 0x200b, 0x0000, 0x7a38,
+ 0x9294, 0x0006, 0x9296, 0x0004, 0x0110, 0x9006, 0x0010, 0x2001,
+ 0x0001, 0x080c, 0x2ab8, 0x2001, 0x196a, 0x2003, 0x0002, 0x0028,
+ 0x2001, 0x1968, 0x2003, 0x0003, 0x0010, 0x080c, 0x28fd, 0x0005,
+ 0x080c, 0x2a28, 0x0560, 0x2009, 0x1971, 0x2104, 0x8001, 0x200a,
+ 0x080c, 0x2b06, 0x1168, 0x7850, 0x9084, 0xefff, 0x7852, 0x2001,
+ 0x1968, 0x2003, 0x0003, 0x2001, 0x1969, 0x2003, 0x0000, 0x00b8,
+ 0x2009, 0x1971, 0x2104, 0x9005, 0x1118, 0x080c, 0x2a6d, 0x0010,
+ 0x080c, 0x2a3d, 0x080c, 0x2a30, 0x2009, 0x196d, 0x200b, 0x0000,
+ 0x2001, 0x196a, 0x2003, 0x0001, 0x080c, 0x28fd, 0x0000, 0x0005,
+ 0x04b9, 0x0508, 0x080c, 0x2b06, 0x11b8, 0x7850, 0x9084, 0xefff,
+ 0x7852, 0x2009, 0x196e, 0x2104, 0x8000, 0x200a, 0x9086, 0x0007,
+ 0x0108, 0x0078, 0x2001, 0x1973, 0x2003, 0x000a, 0x2009, 0x1970,
+ 0x2104, 0xc0fd, 0x200a, 0x0038, 0x0419, 0x2001, 0x196a, 0x2003,
+ 0x0004, 0x080c, 0x2928, 0x0005, 0x0099, 0x0168, 0x080c, 0x2b06,
+ 0x1138, 0x7850, 0x9084, 0xefff, 0x7852, 0x080c, 0x2914, 0x0018,
+ 0x0079, 0x080c, 0x2928, 0x0005, 0x080c, 0x0dfa, 0x080c, 0x0dfa,
+ 0x2009, 0x1972, 0x2104, 0x8001, 0x200a, 0x090c, 0x2a89, 0x0005,
+ 0x7a38, 0x9294, 0x0005, 0x9296, 0x0005, 0x0110, 0x9006, 0x0010,
+ 0x2001, 0x0001, 0x080c, 0x2ab8, 0x0005, 0x7a38, 0x9294, 0x0006,
+ 0x9296, 0x0006, 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x080c,
+ 0x2a9b, 0x0005, 0x2009, 0x196d, 0x2104, 0x8000, 0x200a, 0x9086,
+ 0x0005, 0x0108, 0x0068, 0x200b, 0x0000, 0x7a38, 0x9294, 0x0006,
+ 0x9296, 0x0006, 0x0110, 0x9006, 0x0010, 0x2001, 0x0001, 0x04d9,
+ 0x7a38, 0x9294, 0x0005, 0x9296, 0x0005, 0x0110, 0x9006, 0x0010,
+ 0x2001, 0x0001, 0x080c, 0x2ab8, 0x0005, 0x0086, 0x2001, 0x1970,
+ 0x2004, 0x9084, 0x7fff, 0x090c, 0x0dfa, 0x2009, 0x196f, 0x2144,
+ 0x8846, 0x280a, 0x9844, 0x0dd8, 0xd08c, 0x1120, 0xd084, 0x1120,
+ 0x080c, 0x0dfa, 0x9006, 0x0010, 0x2001, 0x0001, 0x00a1, 0x008e,
+ 0x0005, 0x0006, 0x0156, 0x2001, 0x1968, 0x20a9, 0x0009, 0x2003,
+ 0x0000, 0x8000, 0x1f04, 0x2a8f, 0x2001, 0x196f, 0x2003, 0x8000,
+ 0x015e, 0x000e, 0x0005, 0x00f6, 0x2079, 0x0100, 0x9085, 0x0000,
+ 0x0158, 0x7838, 0x9084, 0xfff9, 0x9085, 0x0004, 0x783a, 0x2009,
+ 0x1975, 0x210c, 0x795a, 0x0050, 0x7838, 0x9084, 0xfffb, 0x9085,
+ 0x0006, 0x783a, 0x2009, 0x1976, 0x210c, 0x795a, 0x00fe, 0x0005,
+ 0x00f6, 0x2079, 0x0100, 0x9085, 0x0000, 0x0188, 0x7838, 0x9084,
+ 0xfffa, 0x9085, 0x0004, 0x783a, 0x2001, 0x0100, 0x2004, 0x9086,
+ 0x000a, 0x1120, 0x7850, 0x9084, 0xfff0, 0x7852, 0x0428, 0x7838,
+ 0x9084, 0xfffb, 0x9085, 0x0005, 0x783a, 0x2001, 0x0100, 0x2004,
+ 0x9086, 0x000a, 0x11c8, 0x7850, 0x9084, 0xfff0, 0x0016, 0x2009,
+ 0x017f, 0x210c, 0x918e, 0x0005, 0x0140, 0x2009, 0x0003, 0x210c,
+ 0x918c, 0x0600, 0x918e, 0x0400, 0x0118, 0x9085, 0x000a, 0x0010,
+ 0x9085, 0x0000, 0x001e, 0x7852, 0x00fe, 0x0005, 0x0006, 0x2001,
+ 0x0100, 0x2004, 0x9082, 0x0007, 0x000e, 0x0005, 0x0006, 0x2001,
+ 0x0100, 0x2004, 0x9082, 0x0009, 0x000e, 0x0005, 0x0156, 0x20a9,
+ 0x0064, 0x7820, 0x080c, 0x2bb0, 0xd09c, 0x1110, 0x1f04, 0x2b09,
+ 0x015e, 0x0005, 0x0126, 0x0016, 0x0006, 0x2091, 0x8000, 0x2001,
+ 0x0100, 0x2004, 0x9086, 0x000a, 0x0170, 0x7850, 0x9085, 0x0040,
+ 0x7852, 0x7850, 0x9084, 0xfbcf, 0x7852, 0x080c, 0x2bb0, 0x9085,
+ 0x2000, 0x7852, 0x0020, 0x080c, 0x2cc2, 0x080c, 0x2cf5, 0x000e,
+ 0x2008, 0x9186, 0x0000, 0x1118, 0x783b, 0x0007, 0x0090, 0x9186,
+ 0x0001, 0x1118, 0x783b, 0x0006, 0x0060, 0x9186, 0x0002, 0x1118,
+ 0x783b, 0x0005, 0x0030, 0x9186, 0x0003, 0x1118, 0x783b, 0x0004,
+ 0x0000, 0x0006, 0x1d04, 0x2b4a, 0x080c, 0x838e, 0x1f04, 0x2b4a,
+ 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0160, 0x7850, 0x9085,
+ 0x0400, 0x9084, 0xdfbf, 0x7852, 0x080c, 0x2bb0, 0x9085, 0x1000,
+ 0x7852, 0x0020, 0x7850, 0x9085, 0x1000, 0x7852, 0x000e, 0x001e,
+ 0x012e, 0x0005, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0128,
+ 0x7850, 0x9084, 0xffcf, 0x7852, 0x0010, 0x080c, 0x2cf5, 0x0005,
+ 0x0006, 0x0156, 0x00f6, 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854,
+ 0xd0ac, 0x1130, 0x7820, 0xd0e4, 0x1140, 0x1f04, 0x2b82, 0x0028,
+ 0x7854, 0xd08c, 0x1110, 0x1f04, 0x2b88, 0x00fe, 0x015e, 0x000e,
+ 0x0005, 0x1d04, 0x2b91, 0x080c, 0x838e, 0x1f04, 0x2b91, 0x0005,
+ 0x0006, 0x2001, 0x1974, 0x2004, 0x9086, 0x0000, 0x000e, 0x0005,
+ 0x0006, 0x2001, 0x1974, 0x2004, 0x9086, 0x0001, 0x000e, 0x0005,
+ 0x0006, 0x2001, 0x1974, 0x2004, 0x9086, 0x0002, 0x000e, 0x0005,
+ 0xa001, 0xa001, 0xa001, 0xa001, 0xa001, 0x0005, 0x0006, 0x2001,
+ 0x1980, 0x2102, 0x000e, 0x0005, 0x2009, 0x0171, 0x2104, 0xd0dc,
+ 0x0140, 0x2009, 0x0170, 0x2104, 0x200b, 0x0080, 0xa001, 0xa001,
+ 0x200a, 0x0005, 0x0036, 0x0046, 0x2001, 0x0141, 0x200c, 0x918c,
+ 0xff00, 0x9186, 0x2100, 0x0140, 0x9186, 0x2000, 0x0170, 0x9186,
+ 0x0100, 0x1904, 0x2c29, 0x0048, 0x0016, 0x2009, 0x1a5a, 0x2104,
+ 0x8000, 0x0208, 0x200a, 0x001e, 0x04f0, 0x2009, 0x00a2, 0x080c,
+ 0x0e75, 0x2019, 0x0160, 0x2324, 0x2011, 0x0003, 0x2009, 0x0169,
+ 0x2104, 0x9084, 0x0007, 0x210c, 0x918c, 0x0007, 0x910e, 0x1db0,
+ 0x9086, 0x0003, 0x1548, 0x2304, 0x0066, 0x0076, 0x2031, 0x0002,
+ 0x233c, 0x973e, 0x0148, 0x8631, 0x1dd8, 0x2031, 0x1a5b, 0x263c,
+ 0x8738, 0x0208, 0x2732, 0x2304, 0x007e, 0x006e, 0x9402, 0x02a0,
+ 0x19d0, 0x8211, 0x19d8, 0x84ff, 0x0170, 0x2001, 0x0141, 0x200c,
+ 0x918c, 0xff00, 0x9186, 0x0100, 0x0130, 0x2009, 0x180c, 0x2104,
+ 0xc0dd, 0x200a, 0x0008, 0x0421, 0x2001, 0x1959, 0x200c, 0x080c,
+ 0x0e75, 0x004e, 0x003e, 0x0005, 0x2001, 0x180c, 0x2004, 0xd0dc,
+ 0x01b0, 0x2001, 0x0160, 0x2004, 0x9005, 0x0140, 0x2001, 0x0141,
+ 0x2004, 0x9084, 0xff00, 0x9086, 0x0100, 0x1148, 0x0126, 0x2091,
+ 0x8000, 0x0016, 0x0026, 0x0021, 0x002e, 0x001e, 0x012e, 0x0005,
+ 0x00c6, 0x2061, 0x0100, 0x6014, 0x0006, 0x2001, 0x0161, 0x2003,
+ 0x0000, 0x6017, 0x0018, 0xa001, 0xa001, 0x602f, 0x0008, 0x6104,
+ 0x918e, 0x0010, 0x6106, 0x918e, 0x0010, 0x6106, 0x6017, 0x0040,
+ 0x04b9, 0x001e, 0x9184, 0x0003, 0x01e0, 0x0036, 0x0016, 0x2019,
+ 0x0141, 0x6124, 0x918c, 0x0028, 0x1120, 0x2304, 0x9084, 0x2800,
+ 0x0dc0, 0x001e, 0x919c, 0xffe4, 0x9184, 0x0001, 0x0118, 0x9385,
+ 0x0009, 0x6016, 0x9184, 0x0002, 0x0118, 0x9385, 0x0012, 0x6016,
+ 0x003e, 0x2001, 0x180c, 0x200c, 0xc1dc, 0x2102, 0x00ce, 0x0005,
+ 0x0016, 0x0026, 0x080c, 0x7221, 0x0108, 0xc0bc, 0x2009, 0x0140,
+ 0x2114, 0x9294, 0x0001, 0x9215, 0x220a, 0x002e, 0x001e, 0x0005,
+ 0x0016, 0x0026, 0x2009, 0x0140, 0x2114, 0x9294, 0x0001, 0x9285,
+ 0x1000, 0x200a, 0x220a, 0x002e, 0x001e, 0x0005, 0x0016, 0x0026,
+ 0x2009, 0x0140, 0x2114, 0x9294, 0x0001, 0x9215, 0x220a, 0x002e,
+ 0x001e, 0x0005, 0x0006, 0x0016, 0x2009, 0x0140, 0x2104, 0x1128,
+ 0x080c, 0x7221, 0x0110, 0xc0bc, 0x0008, 0xc0bd, 0x200a, 0x001e,
+ 0x000e, 0x0005, 0x0016, 0x0026, 0x0036, 0x00c6, 0x2061, 0x0100,
+ 0x6050, 0x9084, 0xfbff, 0x9085, 0x0040, 0x6052, 0x20a9, 0x0002,
+ 0x080c, 0x2b91, 0x6050, 0x9085, 0x0400, 0x9084, 0xff9f, 0x6052,
+ 0x20a9, 0x0005, 0x080c, 0x2b91, 0x6054, 0xd0bc, 0x090c, 0x0dfa,
+ 0x20a9, 0x0005, 0x080c, 0x2b91, 0x6054, 0xd0ac, 0x090c, 0x0dfa,
+ 0x2009, 0x1987, 0x9084, 0x7e00, 0x8007, 0x8004, 0x8004, 0x200a,
+ 0x00ce, 0x003e, 0x002e, 0x001e, 0x0005, 0x0006, 0x00c6, 0x2061,
+ 0x0100, 0x6050, 0xc0cd, 0x6052, 0x00ce, 0x000e, 0x0005, 0x2f6b,
+ 0x2f6b, 0x2d8f, 0x2d8f, 0x2d9b, 0x2d9b, 0x2da7, 0x2da7, 0x2db5,
+ 0x2db5, 0x2dc1, 0x2dc1, 0x2dcf, 0x2dcf, 0x2ddd, 0x2ddd, 0x2def,
+ 0x2def, 0x2dfb, 0x2dfb, 0x2e09, 0x2e09, 0x2e27, 0x2e27, 0x2e47,
+ 0x2e47, 0x2e17, 0x2e17, 0x2e37, 0x2e37, 0x2e55, 0x2e55, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2e67,
+ 0x2e67, 0x2e73, 0x2e73, 0x2e81, 0x2e81, 0x2e8f, 0x2e8f, 0x2e9f,
+ 0x2e9f, 0x2ead, 0x2ead, 0x2ebd, 0x2ebd, 0x2ecd, 0x2ecd, 0x2edf,
+ 0x2edf, 0x2eed, 0x2eed, 0x2efd, 0x2efd, 0x2f1f, 0x2f1f, 0x2f41,
+ 0x2f41, 0x2f0d, 0x2f0d, 0x2f30, 0x2f30, 0x2f50, 0x2f50, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded,
+ 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x2ded, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x23c6, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6,
+ 0x0136, 0x0146, 0x0156, 0x080c, 0x21cd, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x21cd, 0x080c, 0x23c6, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2208, 0x0804,
+ 0x2f63, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x080c, 0x23c6, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x21cd, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x21cd, 0x080c,
+ 0x23c6, 0x080c, 0x2208, 0x0804, 0x2f63, 0xa001, 0x0cf0, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x1370, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6,
+ 0x0136, 0x0146, 0x0156, 0x080c, 0x23c6, 0x080c, 0x1370, 0x0804,
+ 0x2f63, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x080c, 0x21cd, 0x080c, 0x1370, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x23c6, 0x080c, 0x1370, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x21cd, 0x080c, 0x23c6, 0x080c, 0x1370, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x21cd, 0x080c, 0x1370, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x1370, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x21cd, 0x080c,
+ 0x23c6, 0x080c, 0x1370, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x2871, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6,
+ 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c, 0x23c6, 0x0804,
+ 0x2f63, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x080c, 0x2871, 0x080c, 0x21cd, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x2871, 0x080c, 0x21cd, 0x080c, 0x23c6, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x2871, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c,
+ 0x23c6, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c,
+ 0x21cd, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c,
+ 0x21cd, 0x080c, 0x23c6, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x2871, 0x080c, 0x1370, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c,
+ 0x23c6, 0x080c, 0x1370, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c,
+ 0x21cd, 0x080c, 0x1370, 0x0804, 0x2f63, 0x0106, 0x0006, 0x0126,
+ 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c, 0x2871, 0x080c,
+ 0x23c6, 0x080c, 0x1370, 0x080c, 0x2208, 0x0804, 0x2f63, 0x0106,
+ 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156, 0x080c,
+ 0x2871, 0x080c, 0x21cd, 0x080c, 0x23c6, 0x080c, 0x1370, 0x0498,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2871, 0x080c, 0x21cd, 0x080c, 0x1370, 0x080c, 0x2208,
+ 0x0410, 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146,
+ 0x0156, 0x080c, 0x2871, 0x080c, 0x1370, 0x080c, 0x2208, 0x0098,
+ 0x0106, 0x0006, 0x0126, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x0156,
+ 0x080c, 0x2871, 0x080c, 0x21cd, 0x080c, 0x23c6, 0x080c, 0x1370,
+ 0x080c, 0x2208, 0x0000, 0x015e, 0x014e, 0x013e, 0x01de, 0x01ce,
+ 0x012e, 0x000e, 0x010e, 0x000d, 0x00b6, 0x00c6, 0x0026, 0x0046,
+ 0x9026, 0x080c, 0x6781, 0x1904, 0x3072, 0x72d8, 0x2001, 0x1954,
+ 0x2004, 0x9005, 0x1110, 0xd29c, 0x0148, 0xd284, 0x1138, 0xd2bc,
+ 0x1904, 0x3072, 0x080c, 0x3077, 0x0804, 0x3072, 0xd2cc, 0x1904,
+ 0x3072, 0x080c, 0x7207, 0x1120, 0x70ab, 0xffff, 0x0804, 0x3072,
+ 0xd294, 0x0120, 0x70ab, 0xffff, 0x0804, 0x3072, 0x080c, 0x32df,
+ 0x0160, 0x080c, 0xc539, 0x0128, 0x2001, 0x1817, 0x203c, 0x0804,
+ 0x3004, 0x70ab, 0xffff, 0x0804, 0x3072, 0x2001, 0x1817, 0x203c,
+ 0x7290, 0xd284, 0x0904, 0x3004, 0xd28c, 0x1904, 0x3004, 0x0036,
+ 0x73a8, 0x938e, 0xffff, 0x1110, 0x2019, 0x0001, 0x8314, 0x92e0,
+ 0x1c80, 0x2c04, 0x938c, 0x0001, 0x0120, 0x9084, 0xff00, 0x8007,
+ 0x0010, 0x9084, 0x00ff, 0x970e, 0x05a8, 0x908e, 0x0000, 0x0590,
+ 0x908e, 0x00ff, 0x1150, 0x7230, 0xd284, 0x1588, 0x7290, 0xc28d,
+ 0x7292, 0x70ab, 0xffff, 0x003e, 0x0478, 0x0026, 0x2011, 0x0010,
+ 0x080c, 0x67e7, 0x002e, 0x0118, 0x70ab, 0xffff, 0x0410, 0x900e,
+ 0x080c, 0x276e, 0x080c, 0x643f, 0x11c0, 0x080c, 0x67c3, 0x1168,
+ 0x7030, 0xd08c, 0x0130, 0xb800, 0xd0bc, 0x0138, 0x080c, 0x66bf,
+ 0x0120, 0x080c, 0x3090, 0x0148, 0x0028, 0x080c, 0x31d0, 0x080c,
+ 0x30bc, 0x0118, 0x8318, 0x0804, 0x2fb6, 0x73aa, 0x0010, 0x70ab,
+ 0xffff, 0x003e, 0x0804, 0x3072, 0x9780, 0x32e9, 0x203d, 0x97bc,
+ 0xff00, 0x873f, 0x2041, 0x007e, 0x70a8, 0x9096, 0xffff, 0x1118,
+ 0x900e, 0x28a8, 0x0050, 0x9812, 0x0220, 0x2008, 0x9802, 0x20a8,
+ 0x0020, 0x70ab, 0xffff, 0x0804, 0x3072, 0x2700, 0x0156, 0x0016,
+ 0x9106, 0x0904, 0x3067, 0x0026, 0x2011, 0x0010, 0x080c, 0x67e7,
+ 0x002e, 0x0120, 0x2009, 0xffff, 0x0804, 0x306f, 0xc484, 0x080c,
+ 0x649f, 0x0138, 0x080c, 0xc539, 0x1590, 0x080c, 0x643f, 0x15b8,
+ 0x0008, 0xc485, 0x080c, 0x67c3, 0x1130, 0x7030, 0xd08c, 0x01f8,
+ 0xb800, 0xd0bc, 0x11e0, 0x7290, 0xd28c, 0x0180, 0x080c, 0x67c3,
+ 0x9082, 0x0006, 0x02e0, 0xd484, 0x1118, 0x080c, 0x6463, 0x0028,
+ 0x080c, 0x325b, 0x01a0, 0x080c, 0x3286, 0x0088, 0x080c, 0x31d0,
+ 0x080c, 0xc539, 0x1160, 0x080c, 0x30bc, 0x0188, 0x0040, 0x080c,
+ 0xc539, 0x1118, 0x080c, 0x325b, 0x0110, 0x0451, 0x0140, 0x001e,
+ 0x8108, 0x015e, 0x1f04, 0x301d, 0x70ab, 0xffff, 0x0018, 0x001e,
+ 0x015e, 0x71aa, 0x004e, 0x002e, 0x00ce, 0x00be, 0x0005, 0x00c6,
+ 0x0016, 0x70ab, 0x0001, 0x2009, 0x007e, 0x080c, 0x643f, 0x1168,
+ 0xb813, 0x00ff, 0xb817, 0xfffe, 0x080c, 0x31d0, 0x04a9, 0x0128,
+ 0x70d8, 0xc0bd, 0x70da, 0x080c, 0xc28a, 0x001e, 0x00ce, 0x0005,
+ 0x0016, 0x0076, 0x00d6, 0x00c6, 0x2001, 0x1860, 0x2004, 0x9084,
+ 0x00ff, 0xb842, 0x080c, 0xa130, 0x01d0, 0x2b00, 0x6012, 0x080c,
+ 0xc2b3, 0x6023, 0x0001, 0x9006, 0x080c, 0x63dc, 0x2001, 0x0000,
+ 0x080c, 0x63f0, 0x0126, 0x2091, 0x8000, 0x70a4, 0x8000, 0x70a6,
+ 0x012e, 0x2009, 0x0004, 0x080c, 0xa15d, 0x9085, 0x0001, 0x00ce,
+ 0x00de, 0x007e, 0x001e, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6,
+ 0x2001, 0x1860, 0x2004, 0x9084, 0x00ff, 0xb842, 0x080c, 0xa130,
+ 0x0548, 0x2b00, 0x6012, 0xb800, 0xc0c4, 0xb802, 0xb8a0, 0x9086,
+ 0x007e, 0x0140, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x1110,
+ 0x080c, 0x318b, 0x080c, 0xc2b3, 0x6023, 0x0001, 0x9006, 0x080c,
+ 0x63dc, 0x2001, 0x0002, 0x080c, 0x63f0, 0x0126, 0x2091, 0x8000,
+ 0x70a4, 0x8000, 0x70a6, 0x012e, 0x2009, 0x0002, 0x080c, 0xa15d,
+ 0x9085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005, 0x00b6,
+ 0x00c6, 0x0026, 0x2009, 0x0080, 0x080c, 0x643f, 0x1140, 0xb813,
+ 0x00ff, 0xb817, 0xfffc, 0x0039, 0x0110, 0x70df, 0xffff, 0x002e,
+ 0x00ce, 0x00be, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x080c,
+ 0xa08d, 0x01d0, 0x2b00, 0x6012, 0x080c, 0xc2b3, 0x6023, 0x0001,
+ 0x9006, 0x080c, 0x63dc, 0x2001, 0x0002, 0x080c, 0x63f0, 0x0126,
+ 0x2091, 0x8000, 0x70e0, 0x8000, 0x70e2, 0x012e, 0x2009, 0x0002,
+ 0x080c, 0xa15d, 0x9085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e,
+ 0x0005, 0x00c6, 0x00d6, 0x0126, 0x2091, 0x8000, 0x2009, 0x007f,
+ 0x080c, 0x643f, 0x11b8, 0xb813, 0x00ff, 0xb817, 0xfffd, 0xb8bf,
+ 0x0004, 0x080c, 0xa08d, 0x0170, 0x2b00, 0x6012, 0x6316, 0x6023,
+ 0x0001, 0x620a, 0x080c, 0xc2b3, 0x2009, 0x0022, 0x080c, 0xa15d,
+ 0x9085, 0x0001, 0x012e, 0x00de, 0x00ce, 0x0005, 0x00e6, 0x00c6,
+ 0x0066, 0x0036, 0x0026, 0x00b6, 0x21f0, 0x080c, 0x880e, 0x080c,
+ 0x8798, 0x080c, 0x9f36, 0x080c, 0xb03a, 0x3e08, 0x2130, 0x81ff,
+ 0x0120, 0x20a9, 0x007e, 0x900e, 0x0018, 0x20a9, 0x007f, 0x900e,
+ 0x0016, 0x080c, 0x649f, 0x1140, 0x9686, 0x0002, 0x1118, 0xb800,
+ 0xd0bc, 0x1110, 0x080c, 0x5f45, 0x001e, 0x8108, 0x1f04, 0x3170,
+ 0x9686, 0x0001, 0x190c, 0x32b3, 0x00be, 0x002e, 0x003e, 0x006e,
+ 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x00c6, 0x0046, 0x0036, 0x0026,
+ 0x0016, 0x00b6, 0x6210, 0x2258, 0xbaa0, 0x0026, 0x2019, 0x0029,
+ 0x080c, 0x8803, 0x0076, 0x2039, 0x0000, 0x080c, 0x86f1, 0x2c08,
+ 0x080c, 0xd5f6, 0x007e, 0x001e, 0xba10, 0xbb14, 0xbcb0, 0x080c,
+ 0x5f45, 0xba12, 0xbb16, 0xbcb2, 0x00be, 0x001e, 0x002e, 0x003e,
+ 0x004e, 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x0006, 0x00b6, 0x6010,
+ 0x2058, 0xb8a0, 0x00be, 0x9086, 0x0080, 0x0150, 0x2071, 0x1800,
+ 0x70a4, 0x9005, 0x0110, 0x8001, 0x70a6, 0x000e, 0x00ee, 0x0005,
+ 0x2071, 0x1800, 0x70e0, 0x9005, 0x0dc0, 0x8001, 0x70e2, 0x0ca8,
+ 0xb800, 0xc08c, 0xb802, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x00b6,
+ 0x0046, 0x0036, 0x0026, 0x0016, 0x0156, 0x2178, 0x81ff, 0x1118,
+ 0x20a9, 0x0001, 0x0070, 0x080c, 0x55db, 0xd0c4, 0x0138, 0x0030,
+ 0x9006, 0x2020, 0x2009, 0x002d, 0x080c, 0xd885, 0x20a9, 0x0800,
+ 0x9016, 0x0026, 0x928e, 0x007e, 0x0904, 0x323a, 0x928e, 0x007f,
+ 0x0904, 0x323a, 0x928e, 0x0080, 0x05e8, 0x9288, 0x1000, 0x210c,
+ 0x81ff, 0x05c0, 0x8fff, 0x1148, 0x2001, 0x1966, 0x0006, 0x2003,
+ 0x0001, 0x04f1, 0x000e, 0x2003, 0x0000, 0x00b6, 0x00c6, 0x2158,
+ 0x2001, 0x0001, 0x080c, 0x678d, 0x00ce, 0x00be, 0x2019, 0x0029,
+ 0x080c, 0x8803, 0x0076, 0x2039, 0x0000, 0x080c, 0x86f1, 0x00b6,
+ 0x00c6, 0x0026, 0x2158, 0xba04, 0x9294, 0x00ff, 0x9286, 0x0006,
+ 0x1118, 0xb807, 0x0404, 0x0028, 0x2001, 0x0004, 0x8007, 0x9215,
+ 0xba06, 0x002e, 0x00ce, 0x00be, 0x0016, 0x2c08, 0x080c, 0xd5f6,
+ 0x001e, 0x007e, 0x002e, 0x8210, 0x1f04, 0x31f1, 0x015e, 0x001e,
+ 0x002e, 0x003e, 0x004e, 0x00be, 0x00ce, 0x00ee, 0x00fe, 0x0005,
+ 0x0046, 0x0026, 0x0016, 0x080c, 0x55db, 0xd0c4, 0x0140, 0xd0a4,
+ 0x0130, 0x9006, 0x2220, 0x2009, 0x0029, 0x080c, 0xd885, 0x001e,
+ 0x002e, 0x004e, 0x0005, 0x0016, 0x0026, 0x0036, 0x00c6, 0x7290,
+ 0x82ff, 0x01e8, 0x080c, 0x67bb, 0x11d0, 0x2100, 0x080c, 0x27a1,
+ 0x81ff, 0x01b8, 0x2019, 0x0001, 0x8314, 0x92e0, 0x1c80, 0x2c04,
+ 0xd384, 0x0120, 0x9084, 0xff00, 0x8007, 0x0010, 0x9084, 0x00ff,
+ 0x9116, 0x0138, 0x9096, 0x00ff, 0x0110, 0x8318, 0x0c68, 0x9085,
+ 0x0001, 0x00ce, 0x003e, 0x002e, 0x001e, 0x0005, 0x0016, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x0036, 0x2019, 0x0029, 0x00a9, 0x003e,
+ 0x9180, 0x1000, 0x2004, 0x9065, 0x0158, 0x0016, 0x00c6, 0x2061,
+ 0x1a8a, 0x001e, 0x6112, 0x080c, 0x318b, 0x001e, 0x080c, 0x6463,
+ 0x012e, 0x00ce, 0x001e, 0x0005, 0x0016, 0x0026, 0x2110, 0x080c,
+ 0x9bc9, 0x080c, 0xdb3d, 0x002e, 0x001e, 0x0005, 0x2001, 0x1836,
+ 0x2004, 0xd0cc, 0x0005, 0x00c6, 0x00b6, 0x080c, 0x7207, 0x1118,
+ 0x20a9, 0x0800, 0x0010, 0x20a9, 0x0782, 0x080c, 0x7207, 0x1110,
+ 0x900e, 0x0010, 0x2009, 0x007e, 0x9180, 0x1000, 0x2004, 0x905d,
+ 0x0130, 0x86ff, 0x0110, 0xb800, 0xd0bc, 0x090c, 0x6463, 0x8108,
+ 0x1f04, 0x32c4, 0x2061, 0x1800, 0x607b, 0x0000, 0x607c, 0x9084,
+ 0x00ff, 0x607e, 0x60af, 0x0000, 0x00be, 0x00ce, 0x0005, 0x2001,
+ 0x187d, 0x2004, 0xd0bc, 0x0005, 0x2011, 0x185c, 0x2214, 0xd2ec,
+ 0x0005, 0x7eef, 0x7de8, 0x7ce4, 0x80e2, 0x7be1, 0x80e0, 0x80dc,
+ 0x80da, 0x7ad9, 0x80d6, 0x80d5, 0x80d4, 0x80d3, 0x80d2, 0x80d1,
+ 0x79ce, 0x78cd, 0x80cc, 0x80cb, 0x80ca, 0x80c9, 0x80c7, 0x80c6,
+ 0x77c5, 0x76c3, 0x80bc, 0x80ba, 0x75b9, 0x80b6, 0x74b5, 0x73b4,
+ 0x72b3, 0x80b2, 0x80b1, 0x80ae, 0x71ad, 0x80ac, 0x70ab, 0x6faa,
+ 0x6ea9, 0x80a7, 0x6da6, 0x6ca5, 0x6ba3, 0x6a9f, 0x699e, 0x689d,
+ 0x809b, 0x8098, 0x6797, 0x6690, 0x658f, 0x6488, 0x6384, 0x6282,
+ 0x8081, 0x8080, 0x617c, 0x607a, 0x8079, 0x5f76, 0x8075, 0x8074,
+ 0x8073, 0x8072, 0x8071, 0x806e, 0x5e6d, 0x806c, 0x5d6b, 0x5c6a,
+ 0x5b69, 0x8067, 0x5a66, 0x5965, 0x5863, 0x575c, 0x565a, 0x5559,
+ 0x8056, 0x8055, 0x5454, 0x5353, 0x5252, 0x5151, 0x504e, 0x4f4d,
+ 0x804c, 0x804b, 0x4e4a, 0x4d49, 0x8047, 0x4c46, 0x8045, 0x8043,
+ 0x803c, 0x803a, 0x8039, 0x8036, 0x4b35, 0x8034, 0x4a33, 0x4932,
+ 0x4831, 0x802e, 0x472d, 0x462c, 0x452b, 0x442a, 0x4329, 0x4227,
+ 0x8026, 0x8025, 0x4123, 0x401f, 0x3f1e, 0x3e1d, 0x3d1b, 0x3c18,
+ 0x8017, 0x8010, 0x3b0f, 0x3a08, 0x8004, 0x3902, 0x8001, 0x8000,
+ 0x8000, 0x3800, 0x3700, 0x3600, 0x8000, 0x3500, 0x8000, 0x8000,
+ 0x8000, 0x3400, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x3300, 0x3200, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x3100, 0x3000, 0x8000, 0x8000, 0x2f00, 0x8000, 0x2e00, 0x2d00,
+ 0x2c00, 0x8000, 0x8000, 0x8000, 0x2b00, 0x8000, 0x2a00, 0x2900,
+ 0x2800, 0x8000, 0x2700, 0x2600, 0x2500, 0x2400, 0x2300, 0x2200,
+ 0x8000, 0x8000, 0x2100, 0x2000, 0x1f00, 0x1e00, 0x1d00, 0x1c00,
+ 0x8000, 0x8000, 0x1b00, 0x1a00, 0x8000, 0x1900, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x1800, 0x8000, 0x1700, 0x1600,
+ 0x1500, 0x8000, 0x1400, 0x1300, 0x1200, 0x1100, 0x1000, 0x0f00,
+ 0x8000, 0x8000, 0x0e00, 0x0d00, 0x0c00, 0x0b00, 0x0a00, 0x0900,
+ 0x8000, 0x8000, 0x0800, 0x0700, 0x8000, 0x0600, 0x8000, 0x8000,
+ 0x8000, 0x0500, 0x0400, 0x0300, 0x8000, 0x0200, 0x8000, 0x8000,
+ 0x8000, 0x0100, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x0000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x2071, 0x189c, 0x7003, 0x0002, 0x9006, 0x7016, 0x701a,
+ 0x704a, 0x704e, 0x700e, 0x7042, 0x7046, 0x703b, 0x18b8, 0x703f,
+ 0x18b8, 0x7007, 0x0001, 0x080c, 0x104a, 0x090c, 0x0dfa, 0x2900,
+ 0x706a, 0xa867, 0x0002, 0xa8ab, 0xdcb0, 0x080c, 0x104a, 0x090c,
+ 0x0dfa, 0x2900, 0x706e, 0xa867, 0x0002, 0xa8ab, 0xdcb0, 0x0005,
+ 0x2071, 0x189c, 0x7004, 0x0002, 0x3418, 0x3419, 0x342c, 0x3440,
+ 0x0005, 0x1004, 0x3429, 0x0e04, 0x3429, 0x2079, 0x0000, 0x0126,
+ 0x2091, 0x8000, 0x700c, 0x9005, 0x1128, 0x700f, 0x0001, 0x012e,
+ 0x0468, 0x0005, 0x012e, 0x0ce8, 0x2079, 0x0000, 0x2061, 0x18b6,
+ 0x2c4c, 0xa86c, 0x908e, 0x0100, 0x0128, 0x9086, 0x0200, 0x0904,
+ 0x3514, 0x0005, 0x7018, 0x2048, 0x2061, 0x1800, 0x701c, 0x0807,
+ 0x7014, 0x2048, 0xa864, 0x9094, 0x00ff, 0x9296, 0x0029, 0x1120,
+ 0xaa78, 0xd2fc, 0x0128, 0x0005, 0x9086, 0x0103, 0x0108, 0x0005,
+ 0x2079, 0x0000, 0x2061, 0x1800, 0x701c, 0x0807, 0x2061, 0x1800,
+ 0x7880, 0x908a, 0x0040, 0x1210, 0x61cc, 0x0042, 0x2100, 0x908a,
+ 0x003f, 0x1a04, 0x3511, 0x61cc, 0x0804, 0x34a6, 0x34e8, 0x3520,
+ 0x3511, 0x352a, 0x3534, 0x353a, 0x353e, 0x354e, 0x3552, 0x3568,
+ 0x356e, 0x3574, 0x357f, 0x358a, 0x3599, 0x35a8, 0x35b6, 0x35cd,
+ 0x35e8, 0x3511, 0x3691, 0x36cf, 0x3775, 0x3786, 0x37a9, 0x3511,
+ 0x3511, 0x3511, 0x37e1, 0x37fd, 0x3806, 0x3835, 0x383b, 0x3511,
+ 0x3881, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x388c, 0x3895,
+ 0x389d, 0x389f, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511,
+ 0x38cb, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x38e8, 0x395c,
+ 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x0002, 0x3986,
+ 0x3989, 0x39e8, 0x3a01, 0x3a31, 0x3ccf, 0x3511, 0x519f, 0x3511,
+ 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x3511, 0x3568,
+ 0x356e, 0x4249, 0x55ff, 0x425f, 0x522e, 0x527f, 0x538a, 0x3511,
+ 0x53ec, 0x5428, 0x5459, 0x5561, 0x5486, 0x54e1, 0x3511, 0x4263,
+ 0x4408, 0x441e, 0x4443, 0x44a8, 0x451c, 0x453c, 0x45b3, 0x460f,
+ 0x466b, 0x466e, 0x4693, 0x4741, 0x47a7, 0x47af, 0x48e1, 0x4a49,
+ 0x4a7d, 0x4cc7, 0x3511, 0x4ce5, 0x4da2, 0x4e78, 0x3511, 0x3511,
+ 0x3511, 0x3511, 0x4ede, 0x4ef9, 0x47af, 0x513f, 0x714c, 0x0000,
+ 0x2021, 0x4000, 0x080c, 0x4afb, 0x0126, 0x2091, 0x8000, 0x0e04,
+ 0x34f2, 0x0010, 0x012e, 0x0cc0, 0x7c36, 0x9486, 0x4000, 0x0118,
+ 0x7833, 0x0011, 0x0010, 0x7833, 0x0010, 0x7c82, 0x7986, 0x7a8a,
+ 0x7b8e, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c,
+ 0x11e0, 0x7007, 0x0001, 0x2091, 0x5000, 0x700f, 0x0000, 0x012e,
+ 0x0005, 0x2021, 0x4001, 0x08b0, 0x2021, 0x4002, 0x0898, 0x2021,
+ 0x4003, 0x0880, 0x2021, 0x4005, 0x0868, 0x2021, 0x4006, 0x0850,
+ 0x2039, 0x0001, 0x902e, 0x2520, 0x7b88, 0x7a8c, 0x7884, 0x7990,
+ 0x0804, 0x4b08, 0x2039, 0x0001, 0x902e, 0x2520, 0x7b88, 0x7a8c,
+ 0x7884, 0x7990, 0x0804, 0x4b0b, 0x7984, 0x7888, 0x2114, 0x200a,
+ 0x0804, 0x34e8, 0x7984, 0x2114, 0x0804, 0x34e8, 0x20e1, 0x0000,
+ 0x2099, 0x0021, 0x20e9, 0x0000, 0x20a1, 0x0021, 0x20a9, 0x001f,
+ 0x4003, 0x7984, 0x7a88, 0x7b8c, 0x0804, 0x34e8, 0x7884, 0x2060,
+ 0x0804, 0x359b, 0x2009, 0x0003, 0x2011, 0x0003, 0x2019, 0x0008,
+ 0x789b, 0x0317, 0x7893, 0xffff, 0x2001, 0x188d, 0x2004, 0x9005,
+ 0x0118, 0x7896, 0x0804, 0x34e8, 0x7897, 0x0001, 0x0804, 0x34e8,
+ 0x2039, 0x0001, 0x7d98, 0x7c9c, 0x0804, 0x3524, 0x2039, 0x0001,
+ 0x7d98, 0x7c9c, 0x0804, 0x352e, 0x79a0, 0x9182, 0x0040, 0x0210,
+ 0x0804, 0x351d, 0x2138, 0x7d98, 0x7c9c, 0x0804, 0x3524, 0x79a0,
+ 0x9182, 0x0040, 0x0210, 0x0804, 0x351d, 0x2138, 0x7d98, 0x7c9c,
+ 0x0804, 0x352e, 0x79a0, 0x9182, 0x0040, 0x0210, 0x0804, 0x351d,
+ 0x21e8, 0x7984, 0x7888, 0x20a9, 0x0001, 0x21a0, 0x4004, 0x0804,
+ 0x34e8, 0x2061, 0x0800, 0xe10c, 0x9006, 0x2c15, 0x9200, 0x8c60,
+ 0x8109, 0x1dd8, 0x2010, 0x9005, 0x0904, 0x34e8, 0x0804, 0x3517,
+ 0x79a0, 0x9182, 0x0040, 0x0210, 0x0804, 0x351d, 0x21e0, 0x20a9,
+ 0x0001, 0x7984, 0x2198, 0x4012, 0x0804, 0x34e8, 0x2069, 0x185b,
+ 0x7884, 0x7990, 0x911a, 0x1a04, 0x351d, 0x8019, 0x0904, 0x351d,
+ 0x684a, 0x6942, 0x788c, 0x6852, 0x7888, 0x6856, 0x9006, 0x685a,
+ 0x685e, 0x080c, 0x7535, 0x0804, 0x34e8, 0x2069, 0x185b, 0x7884,
+ 0x7994, 0x911a, 0x1a04, 0x351d, 0x8019, 0x0904, 0x351d, 0x684e,
+ 0x6946, 0x788c, 0x6862, 0x7888, 0x6866, 0x9006, 0x686a, 0x686e,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x68c1, 0x012e, 0x0804, 0x34e8,
+ 0x902e, 0x2520, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x351a,
+ 0x7984, 0x7b88, 0x7a8c, 0x20a9, 0x0005, 0x20e9, 0x0001, 0x20a1,
+ 0x18a4, 0x4101, 0x080c, 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x351a, 0x2009, 0x0020, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x080c,
+ 0x4b08, 0x701f, 0x360c, 0x0005, 0xa864, 0x2008, 0x9084, 0x00ff,
+ 0x9096, 0x0011, 0x0168, 0x9096, 0x0019, 0x0150, 0x9096, 0x0015,
+ 0x0138, 0x9096, 0x0048, 0x0120, 0x9096, 0x0029, 0x1904, 0x351a,
+ 0x810f, 0x918c, 0x00ff, 0x0904, 0x351a, 0x7112, 0x7010, 0x8001,
+ 0x0560, 0x7012, 0x080c, 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x351a, 0x2009, 0x0020, 0x7068, 0x2040, 0xa28c, 0xa390, 0xa494,
+ 0xa598, 0x9290, 0x0040, 0x9399, 0x0000, 0x94a1, 0x0000, 0x95a9,
+ 0x0000, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x080c, 0x4b08, 0x701f,
+ 0x364a, 0x0005, 0xa864, 0x9084, 0x00ff, 0x9096, 0x0002, 0x0120,
+ 0x9096, 0x000a, 0x1904, 0x351a, 0x0888, 0x7014, 0x2048, 0xa868,
+ 0xc0fd, 0xa86a, 0xa864, 0x9084, 0x00ff, 0x9096, 0x0029, 0x1160,
+ 0xc2fd, 0xaa7a, 0x080c, 0x6037, 0x0150, 0x0126, 0x2091, 0x8000,
+ 0xa87a, 0xa982, 0x012e, 0x0050, 0x080c, 0x6355, 0x1128, 0x7007,
+ 0x0003, 0x701f, 0x3676, 0x0005, 0x080c, 0x6d17, 0x0126, 0x2091,
+ 0x8000, 0x20a9, 0x0005, 0x20e1, 0x0001, 0x2099, 0x18a4, 0x400a,
+ 0x2100, 0x9210, 0x9399, 0x0000, 0x94a1, 0x0000, 0x95a9, 0x0000,
+ 0xa85c, 0x9080, 0x0019, 0x2009, 0x0020, 0x012e, 0xaf60, 0x0804,
+ 0x4b0b, 0x2091, 0x8000, 0x7837, 0x4000, 0x7833, 0x0010, 0x7883,
+ 0x4000, 0x7887, 0x4953, 0x788b, 0x5020, 0x788f, 0x2020, 0x2009,
+ 0x017f, 0x2104, 0x7892, 0x3f00, 0x7896, 0x2061, 0x0100, 0x6200,
+ 0x2061, 0x0200, 0x603c, 0x8007, 0x9205, 0x789a, 0x2009, 0x04fd,
+ 0x2104, 0x789e, 0x2091, 0x5000, 0x2091, 0x4080, 0x2001, 0x0089,
+ 0x2004, 0xd084, 0x0180, 0x2001, 0x19f1, 0x2004, 0x9005, 0x0128,
+ 0x2001, 0x008b, 0x2004, 0xd0fc, 0x0dd8, 0x2001, 0x008a, 0x2003,
+ 0x0002, 0x2003, 0x1001, 0x2071, 0x0080, 0x0804, 0x0427, 0x81ff,
+ 0x1904, 0x351a, 0x7984, 0x080c, 0x649f, 0x1904, 0x351d, 0x7e98,
+ 0x9684, 0x3fff, 0x9082, 0x4000, 0x1a04, 0x351d, 0x7c88, 0x7d8c,
+ 0x080c, 0x6602, 0x080c, 0x65d1, 0x0000, 0x1518, 0x2061, 0x1cd0,
+ 0x0126, 0x2091, 0x8000, 0x6000, 0x9086, 0x0000, 0x0148, 0x6014,
+ 0x904d, 0x0130, 0xa86c, 0x9406, 0x1118, 0xa870, 0x9506, 0x0150,
+ 0x012e, 0x9ce0, 0x0018, 0x2001, 0x1819, 0x2004, 0x9c02, 0x1a04,
+ 0x351a, 0x0c30, 0x080c, 0xba56, 0x012e, 0x0904, 0x351a, 0x0804,
+ 0x34e8, 0x900e, 0x2001, 0x0005, 0x080c, 0x6d17, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0xc133, 0x080c, 0x6ae9, 0x012e, 0x0804, 0x34e8,
+ 0x00a6, 0x2950, 0xb198, 0x080c, 0x649f, 0x1904, 0x3762, 0xb6a4,
+ 0x9684, 0x3fff, 0x9082, 0x4000, 0x16e8, 0xb49c, 0xb5a0, 0x080c,
+ 0x6602, 0x080c, 0x65d1, 0x1520, 0x2061, 0x1cd0, 0x0126, 0x2091,
+ 0x8000, 0x6000, 0x9086, 0x0000, 0x0148, 0x6014, 0x904d, 0x0130,
+ 0xa86c, 0x9406, 0x1118, 0xa870, 0x9506, 0x0158, 0x012e, 0x9ce0,
+ 0x0018, 0x2001, 0x1819, 0x2004, 0x9c02, 0x2009, 0x000d, 0x12b0,
+ 0x0c28, 0x080c, 0xba56, 0x012e, 0x2009, 0x0003, 0x0178, 0x00e0,
+ 0x900e, 0x2001, 0x0005, 0x080c, 0x6d17, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xc133, 0x080c, 0x6adc, 0x012e, 0x0070, 0xb097, 0x4005,
+ 0xb19a, 0x0010, 0xb097, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001,
+ 0x0030, 0x2a48, 0x00ae, 0x0005, 0xb097, 0x4000, 0x9006, 0x918d,
+ 0x0001, 0x2008, 0x2a48, 0x00ae, 0x0005, 0x81ff, 0x1904, 0x351a,
+ 0x080c, 0x4ad6, 0x0904, 0x351d, 0x080c, 0x6566, 0x0904, 0x351a,
+ 0x080c, 0x6608, 0x0904, 0x351a, 0x0804, 0x4533, 0x81ff, 0x1904,
+ 0x351a, 0x080c, 0x4af2, 0x0904, 0x351d, 0x080c, 0x6696, 0x0904,
+ 0x351a, 0x2019, 0x0005, 0x79a8, 0x080c, 0x6623, 0x0904, 0x351a,
+ 0x7888, 0x908a, 0x1000, 0x1a04, 0x351d, 0x8003, 0x800b, 0x810b,
+ 0x9108, 0x080c, 0x82e8, 0x7984, 0xd184, 0x1904, 0x34e8, 0x0804,
+ 0x4533, 0x0126, 0x2091, 0x8000, 0x81ff, 0x0118, 0x2009, 0x0001,
+ 0x0450, 0x2029, 0x07ff, 0x6458, 0x2400, 0x9506, 0x01f8, 0x2508,
+ 0x080c, 0x649f, 0x11d8, 0x080c, 0x6696, 0x1128, 0x2009, 0x0002,
+ 0x62bc, 0x2518, 0x00c0, 0x2019, 0x0004, 0x900e, 0x080c, 0x6623,
+ 0x1118, 0x2009, 0x0006, 0x0078, 0x7884, 0x908a, 0x1000, 0x1270,
+ 0x8003, 0x800b, 0x810b, 0x9108, 0x080c, 0x82e8, 0x8529, 0x1ae0,
+ 0x012e, 0x0804, 0x34e8, 0x012e, 0x0804, 0x351a, 0x012e, 0x0804,
+ 0x351d, 0x080c, 0x4ad6, 0x0904, 0x351d, 0x080c, 0x6566, 0x0904,
+ 0x351a, 0xbaa0, 0x2019, 0x0005, 0x00c6, 0x9066, 0x080c, 0x8803,
+ 0x0076, 0x903e, 0x080c, 0x86f1, 0x900e, 0x080c, 0xd5f6, 0x007e,
+ 0x00ce, 0x080c, 0x6602, 0x0804, 0x34e8, 0x080c, 0x4ad6, 0x0904,
+ 0x351d, 0x080c, 0x6602, 0x2208, 0x0804, 0x34e8, 0x0156, 0x00d6,
+ 0x00e6, 0x2069, 0x190e, 0x6810, 0x6914, 0x910a, 0x1208, 0x900e,
+ 0x6816, 0x9016, 0x901e, 0x20a9, 0x007e, 0x2069, 0x1000, 0x2d04,
+ 0x905d, 0x0118, 0xb84c, 0x0059, 0x9210, 0x8d68, 0x1f04, 0x3817,
+ 0x2300, 0x9218, 0x00ee, 0x00de, 0x015e, 0x0804, 0x34e8, 0x00f6,
+ 0x0016, 0x907d, 0x0138, 0x9006, 0x8000, 0x2f0c, 0x81ff, 0x0110,
+ 0x2178, 0x0cd0, 0x001e, 0x00fe, 0x0005, 0x2069, 0x190e, 0x6910,
+ 0x62b8, 0x0804, 0x34e8, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804,
+ 0x351a, 0x0126, 0x2091, 0x8000, 0x080c, 0x55ef, 0x0128, 0x2009,
+ 0x0007, 0x012e, 0x0804, 0x351a, 0x012e, 0x6158, 0x9190, 0x32e9,
+ 0x2215, 0x9294, 0x00ff, 0x6378, 0x83ff, 0x0108, 0x627c, 0x67d8,
+ 0x97c4, 0x000a, 0x98c6, 0x000a, 0x1118, 0x2031, 0x0001, 0x00e8,
+ 0x97c4, 0x0022, 0x98c6, 0x0022, 0x1118, 0x2031, 0x0003, 0x00a8,
+ 0x97c4, 0x0012, 0x98c6, 0x0012, 0x1118, 0x2031, 0x0002, 0x0068,
+ 0x080c, 0x7207, 0x1118, 0x2031, 0x0004, 0x0038, 0xd79c, 0x0120,
+ 0x2009, 0x0005, 0x0804, 0x351a, 0x9036, 0x7e9a, 0x7f9e, 0x0804,
+ 0x34e8, 0x6148, 0x624c, 0x2019, 0x195e, 0x231c, 0x2001, 0x195f,
+ 0x2004, 0x789a, 0x0804, 0x34e8, 0x0126, 0x2091, 0x8000, 0x6138,
+ 0x623c, 0x6340, 0x012e, 0x0804, 0x34e8, 0x080c, 0x4af2, 0x0904,
+ 0x351d, 0xba44, 0xbb38, 0x0804, 0x34e8, 0x080c, 0x0dfa, 0x080c,
+ 0x4af2, 0x2110, 0x0904, 0x351d, 0xb804, 0x908c, 0x00ff, 0x918e,
+ 0x0006, 0x0140, 0x9084, 0xff00, 0x9086, 0x0600, 0x2009, 0x0009,
+ 0x1904, 0x351a, 0x0126, 0x2091, 0x8000, 0x2019, 0x0005, 0x00c6,
+ 0x9066, 0x080c, 0x9bc9, 0x080c, 0x8803, 0x0076, 0x903e, 0x080c,
+ 0x86f1, 0x900e, 0x080c, 0xd5f6, 0x007e, 0x00ce, 0xb807, 0x0407,
+ 0x012e, 0x0804, 0x34e8, 0x6148, 0x624c, 0x7884, 0x604a, 0x7b88,
+ 0x634e, 0x2069, 0x185b, 0x831f, 0x9305, 0x6816, 0x788c, 0x2069,
+ 0x195e, 0x2d1c, 0x206a, 0x7e98, 0x9682, 0x0014, 0x1210, 0x2031,
+ 0x07d0, 0x2069, 0x195f, 0x2d04, 0x266a, 0x789a, 0x0804, 0x34e8,
+ 0x0126, 0x2091, 0x8000, 0x6138, 0x7884, 0x603a, 0x910e, 0xd1b4,
+ 0x190c, 0x0ee1, 0xd0c4, 0x01a8, 0x00d6, 0x78a8, 0x2009, 0x1975,
+ 0x200a, 0x78ac, 0x2011, 0x1976, 0x2012, 0x2069, 0x0100, 0x6838,
+ 0x9086, 0x0007, 0x1118, 0x2214, 0x6a5a, 0x0010, 0x210c, 0x695a,
+ 0x00de, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0168, 0x2011,
+ 0x0114, 0x220c, 0x7888, 0xd08c, 0x0118, 0x918d, 0x0080, 0x0010,
+ 0x918c, 0xff7f, 0x2112, 0x0060, 0x2011, 0x0116, 0x220c, 0x7888,
+ 0xd08c, 0x0118, 0x918d, 0x0040, 0x0010, 0x918c, 0xff7f, 0x2112,
+ 0x603c, 0x7988, 0x613e, 0x6140, 0x910d, 0x788c, 0x6042, 0x7a88,
+ 0x9294, 0x1000, 0x9205, 0x910e, 0xd1e4, 0x190c, 0x0ef7, 0x6040,
+ 0xd0cc, 0x0120, 0x78b0, 0x2011, 0x0114, 0x2012, 0x012e, 0x0804,
+ 0x34e8, 0x00f6, 0x2079, 0x1800, 0x7a38, 0xa898, 0x9084, 0xfebf,
+ 0x9215, 0xa89c, 0x9084, 0xfebf, 0x8002, 0x9214, 0x7838, 0x9084,
+ 0x0140, 0x9215, 0x7a3a, 0xa897, 0x4000, 0x900e, 0x9085, 0x0001,
+ 0x2001, 0x0000, 0x00fe, 0x0005, 0x7898, 0x9005, 0x01a8, 0x7888,
+ 0x9025, 0x0904, 0x351d, 0x788c, 0x902d, 0x0904, 0x351d, 0x900e,
+ 0x080c, 0x649f, 0x1120, 0xba44, 0xbb38, 0xbc46, 0xbd3a, 0x9186,
+ 0x07ff, 0x0190, 0x8108, 0x0ca0, 0x080c, 0x4af2, 0x0904, 0x351d,
+ 0x7888, 0x900d, 0x0904, 0x351d, 0x788c, 0x9005, 0x0904, 0x351d,
+ 0xba44, 0xb946, 0xbb38, 0xb83a, 0x0804, 0x34e8, 0x2011, 0xbc09,
+ 0x0010, 0x2011, 0xbc05, 0x080c, 0x55ef, 0x1904, 0x351a, 0x00c6,
+ 0x2061, 0x0100, 0x7984, 0x9186, 0x00ff, 0x1130, 0x2001, 0x1817,
+ 0x2004, 0x9085, 0xff00, 0x0088, 0x9182, 0x007f, 0x16e0, 0x9188,
+ 0x32e9, 0x210d, 0x918c, 0x00ff, 0x2001, 0x1817, 0x2004, 0x0026,
+ 0x9116, 0x002e, 0x0580, 0x810f, 0x9105, 0x0126, 0x2091, 0x8000,
+ 0x0006, 0x080c, 0xa08d, 0x000e, 0x0510, 0x602e, 0x620a, 0x7984,
+ 0x00b6, 0x080c, 0x6445, 0x2b08, 0x00be, 0x1500, 0x6112, 0x6023,
+ 0x0001, 0x080c, 0x4abf, 0x01d0, 0x9006, 0xa866, 0x7007, 0x0003,
+ 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x701f, 0x39e1, 0x2900, 0x6016,
+ 0x2009, 0x0032, 0x080c, 0xa15d, 0x012e, 0x00ce, 0x0005, 0x012e,
+ 0x00ce, 0x0804, 0x351a, 0x00ce, 0x0804, 0x351d, 0x080c, 0xa0e3,
+ 0x0cb0, 0xa830, 0x9086, 0x0100, 0x0904, 0x351a, 0x0804, 0x34e8,
+ 0x2061, 0x1a48, 0x0126, 0x2091, 0x8000, 0x6000, 0xd084, 0x0170,
+ 0x6104, 0x6208, 0x2061, 0x1800, 0x6350, 0x6070, 0x789a, 0x60bc,
+ 0x789e, 0x60b8, 0x78aa, 0x012e, 0x0804, 0x34e8, 0x900e, 0x2110,
+ 0x0c88, 0x81ff, 0x1904, 0x351a, 0x080c, 0x7207, 0x0904, 0x351a,
+ 0x0126, 0x2091, 0x8000, 0x6250, 0x6070, 0x9202, 0x0248, 0x9085,
+ 0x0001, 0x080c, 0x27d7, 0x080c, 0x580e, 0x012e, 0x0804, 0x34e8,
+ 0x012e, 0x0804, 0x351d, 0x0006, 0x0016, 0x00c6, 0x00e6, 0x2001,
+ 0x1981, 0x2070, 0x2061, 0x185b, 0x6008, 0x2072, 0x900e, 0x2011,
+ 0x1400, 0x080c, 0x84ff, 0x7206, 0x00ee, 0x00ce, 0x001e, 0x000e,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x81ff, 0x0128, 0x012e, 0x2021,
+ 0x400b, 0x0804, 0x34ea, 0x7884, 0xd0fc, 0x0148, 0x2001, 0x002a,
+ 0x2004, 0x9082, 0x00e1, 0x0288, 0x012e, 0x0804, 0x351d, 0x2001,
+ 0x002a, 0x2004, 0x2069, 0x185b, 0x6908, 0x9102, 0x1230, 0x012e,
+ 0x0804, 0x351d, 0x012e, 0x0804, 0x351a, 0x080c, 0xa062, 0x0dd0,
+ 0x7884, 0xd0fc, 0x0904, 0x3aac, 0x00c6, 0x080c, 0x4abf, 0x00ce,
+ 0x0d88, 0xa867, 0x0000, 0x7884, 0xa80a, 0x7898, 0xa80e, 0x789c,
+ 0xa812, 0x2001, 0x002e, 0x2004, 0xa81a, 0x2001, 0x002f, 0x2004,
+ 0xa81e, 0x2001, 0x0030, 0x2004, 0xa822, 0x2001, 0x0031, 0x2004,
+ 0xa826, 0x2001, 0x0034, 0x2004, 0xa82a, 0x2001, 0x0035, 0x2004,
+ 0xa82e, 0x2001, 0x002a, 0x2004, 0x9080, 0x0003, 0x9084, 0x00fc,
+ 0x8004, 0xa816, 0x080c, 0x3c32, 0x0928, 0x7014, 0x2048, 0xad2c,
+ 0xac28, 0xab1c, 0xaa18, 0xa930, 0xa808, 0xd0b4, 0x1120, 0x2029,
+ 0x0000, 0x2021, 0x0000, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f,
+ 0x9084, 0xffc0, 0x9080, 0x001b, 0x080c, 0x4b08, 0x701f, 0x3b6f,
+ 0x7023, 0x0001, 0x012e, 0x0005, 0x0046, 0x0086, 0x0096, 0x00a6,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x080c, 0x3a1b, 0x2001,
+ 0x1977, 0x2003, 0x0000, 0x2021, 0x000a, 0x2061, 0x0100, 0x6104,
+ 0x0016, 0x60bb, 0x0000, 0x60bf, 0x32e1, 0x60bf, 0x0012, 0x080c,
+ 0x3ca1, 0x080c, 0x3c60, 0x00f6, 0x00e6, 0x0086, 0x2940, 0x2071,
+ 0x1a3d, 0x2079, 0x0090, 0x00d6, 0x2069, 0x0000, 0x6884, 0xd0b4,
+ 0x0140, 0x2001, 0x0035, 0x2004, 0x780e, 0x2001, 0x0034, 0x2004,
+ 0x780a, 0x00de, 0x2011, 0x0001, 0x080c, 0x408d, 0x008e, 0x00ee,
+ 0x00fe, 0x080c, 0x3fba, 0x080c, 0x3e7f, 0x05b8, 0x2001, 0x020b,
+ 0x2004, 0x9084, 0x0140, 0x1db8, 0x080c, 0x4101, 0x00f6, 0x2079,
+ 0x0300, 0x78bc, 0x00fe, 0x908c, 0x0070, 0x1560, 0x2071, 0x0200,
+ 0x7037, 0x0000, 0x7050, 0x9084, 0xff00, 0x9086, 0x3200, 0x1510,
+ 0x7037, 0x0001, 0x7050, 0x9084, 0xff00, 0x9086, 0xe100, 0x11d0,
+ 0x7037, 0x0000, 0x7054, 0x7037, 0x0000, 0x715c, 0x9106, 0x1190,
+ 0x2001, 0x181f, 0x2004, 0x9106, 0x1168, 0x00c6, 0x2061, 0x0100,
+ 0x6024, 0x9084, 0x1e00, 0x00ce, 0x0138, 0x080c, 0x3e89, 0x080c,
+ 0x3c5b, 0x0058, 0x080c, 0x3c5b, 0x080c, 0x4025, 0x080c, 0x3fb0,
+ 0x2001, 0x020b, 0x2004, 0xd0e4, 0x0dd8, 0x2001, 0x032a, 0x2003,
+ 0x0004, 0x2061, 0x0100, 0x6027, 0x0002, 0x001e, 0x6106, 0x2011,
+ 0x020d, 0x2013, 0x0020, 0x60bb, 0x0000, 0x60bf, 0x0108, 0x60bf,
+ 0x0012, 0x2001, 0x0004, 0x200c, 0x918c, 0xfffd, 0x2102, 0x080c,
+ 0x12f8, 0x2009, 0x0028, 0x080c, 0x230a, 0x2001, 0x0227, 0x200c,
+ 0x2102, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e,
+ 0x008e, 0x004e, 0x2001, 0x1977, 0x2004, 0x9005, 0x1118, 0x012e,
+ 0x0804, 0x34e8, 0x012e, 0x2021, 0x400c, 0x0804, 0x34ea, 0x0016,
+ 0x0026, 0x0036, 0x0046, 0x0056, 0x0076, 0x0086, 0x0096, 0x00d6,
+ 0x0156, 0x7014, 0x2048, 0x7020, 0x20a8, 0x8000, 0x7022, 0xa804,
+ 0x9005, 0x0904, 0x3bcb, 0x2048, 0x1f04, 0x3b7f, 0x7068, 0x2040,
+ 0xa28c, 0xa390, 0xa494, 0xa598, 0xa930, 0xa808, 0xd0b4, 0x1120,
+ 0x2029, 0x0000, 0x2021, 0x0000, 0x0096, 0x7014, 0x2048, 0xa864,
+ 0x009e, 0x9086, 0x0103, 0x0170, 0x8906, 0x8006, 0x8007, 0x90bc,
+ 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b, 0x080c, 0x4b08, 0x701f,
+ 0x3b6f, 0x00b0, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x001b, 0x21a8, 0x27e0, 0x2098, 0x27e8, 0x20a0,
+ 0x0006, 0x080c, 0x0fae, 0x000e, 0x080c, 0x4b0b, 0x701f, 0x3b6f,
+ 0x015e, 0x00de, 0x009e, 0x008e, 0x007e, 0x005e, 0x004e, 0x003e,
+ 0x002e, 0x001e, 0x0005, 0x7014, 0x2048, 0xa864, 0x9086, 0x0103,
+ 0x1118, 0x701f, 0x3c30, 0x0450, 0x7014, 0x2048, 0xa868, 0xc0fd,
+ 0xa86a, 0x2009, 0x007f, 0x080c, 0x643f, 0x0110, 0x9006, 0x0030,
+ 0xb813, 0x00ff, 0xb817, 0xfffd, 0x080c, 0xc302, 0x015e, 0x00de,
+ 0x009e, 0x008e, 0x007e, 0x005e, 0x004e, 0x003e, 0x002e, 0x001e,
+ 0x0904, 0x351a, 0x0016, 0x0026, 0x0036, 0x0046, 0x0056, 0x0076,
+ 0x0086, 0x0096, 0x00d6, 0x0156, 0x701f, 0x3c02, 0x7007, 0x0003,
+ 0x0804, 0x3bc0, 0xa830, 0x9086, 0x0100, 0x2021, 0x400c, 0x0904,
+ 0x34ea, 0x0076, 0xad10, 0xac0c, 0xab24, 0xaa20, 0xa930, 0xa808,
+ 0xd0b4, 0x1120, 0x2029, 0x0000, 0x2021, 0x0000, 0x8906, 0x8006,
+ 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x001b, 0x21a8,
+ 0x27e0, 0x2098, 0x27e8, 0x20a0, 0x0006, 0x080c, 0x0fae, 0x000e,
+ 0x080c, 0x4b0b, 0x007e, 0x701f, 0x3b6f, 0x7023, 0x0001, 0x0005,
+ 0x0804, 0x34e8, 0x0156, 0x00c6, 0xa814, 0x908a, 0x001e, 0x0218,
+ 0xa833, 0x001e, 0x0010, 0xa832, 0x0078, 0x81ff, 0x0168, 0x0016,
+ 0x080c, 0x4abf, 0x001e, 0x0130, 0xa800, 0x2040, 0xa008, 0xa80a,
+ 0x2100, 0x0c58, 0x9006, 0x0010, 0x9085, 0x0001, 0x00ce, 0x015e,
+ 0x0005, 0x0006, 0x00f6, 0x2079, 0x0000, 0x7880, 0x9086, 0x0044,
+ 0x00fe, 0x000e, 0x0005, 0x2001, 0x1977, 0x2003, 0x0001, 0x0005,
+ 0x00f6, 0x00e6, 0x00c6, 0x2061, 0x0200, 0x2001, 0x1982, 0x2004,
+ 0x601a, 0x2061, 0x0100, 0x2001, 0x1981, 0x2004, 0x60ce, 0x6104,
+ 0xc1ac, 0x6106, 0x080c, 0x4abf, 0xa813, 0x0019, 0xa817, 0x0001,
+ 0x2900, 0xa85a, 0x2001, 0x002e, 0x2004, 0xa866, 0x2001, 0x002f,
+ 0x2004, 0xa86a, 0x2061, 0x0090, 0x2079, 0x0100, 0x2001, 0x1981,
+ 0x2004, 0x6036, 0x2009, 0x0040, 0x080c, 0x230a, 0x2001, 0x002a,
+ 0x2004, 0x9084, 0xfff8, 0xa86e, 0x601a, 0xa873, 0x0000, 0x601f,
+ 0x0000, 0x78ca, 0x9006, 0x600a, 0x600e, 0x00ce, 0x00ee, 0x00fe,
+ 0x0005, 0x00e6, 0x080c, 0x4abf, 0x2940, 0xa013, 0x0019, 0xa017,
+ 0x0001, 0x2800, 0xa05a, 0x2001, 0x0030, 0x2004, 0xa866, 0x2001,
+ 0x0031, 0x2004, 0xa86a, 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8,
+ 0xa86e, 0xa873, 0x0000, 0x2001, 0x032a, 0x2003, 0x0004, 0x2001,
+ 0x0300, 0x2003, 0x0000, 0x2001, 0x020d, 0x2003, 0x0000, 0x2001,
+ 0x0004, 0x200c, 0x918d, 0x0002, 0x2102, 0x00ee, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x81ff, 0x0148, 0x080c, 0x2ba8, 0x1130, 0x9006,
+ 0x080c, 0x2ab8, 0x9006, 0x080c, 0x2a9b, 0x7884, 0x9084, 0x0007,
+ 0x0002, 0x3cec, 0x3cfb, 0x3d0a, 0x3ce9, 0x3ce9, 0x3ce9, 0x3ce9,
+ 0x3ce9, 0x012e, 0x0804, 0x351d, 0x2001, 0x0100, 0x2004, 0x9086,
+ 0x000a, 0x0db8, 0x2009, 0x0114, 0x2104, 0x9085, 0x0800, 0x200a,
+ 0x080c, 0x3ed3, 0x00f0, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x0d40, 0x2009, 0x0114, 0x2104, 0x9085, 0x4000, 0x200a, 0x080c,
+ 0x3ed3, 0x0078, 0x080c, 0x7207, 0x1128, 0x012e, 0x2009, 0x0016,
+ 0x0804, 0x351a, 0x81ff, 0x0128, 0x012e, 0x2021, 0x400b, 0x0804,
+ 0x34ea, 0x0086, 0x0096, 0x00a6, 0x00b6, 0x00c6, 0x00d6, 0x00e6,
+ 0x00f6, 0x080c, 0x3a1b, 0x2009, 0x0101, 0x210c, 0x0016, 0x7ec8,
+ 0x7dcc, 0x9006, 0x2068, 0x2060, 0x2058, 0x080c, 0x41dc, 0x080c,
+ 0x412c, 0x903e, 0x2720, 0x00f6, 0x00e6, 0x0086, 0x2940, 0x2071,
+ 0x1a3d, 0x2079, 0x0090, 0x00d6, 0x2069, 0x0000, 0x6884, 0xd0b4,
+ 0x0120, 0x68d4, 0x780e, 0x68d0, 0x780a, 0x00de, 0x2011, 0x0001,
+ 0x080c, 0x408d, 0x080c, 0x2bb0, 0x080c, 0x2bb0, 0x080c, 0x2bb0,
+ 0x080c, 0x2bb0, 0x080c, 0x408d, 0x008e, 0x00ee, 0x00fe, 0x080c,
+ 0x3fba, 0x2009, 0x9c40, 0x8109, 0x11b0, 0x080c, 0x3e89, 0x2001,
+ 0x0004, 0x200c, 0x918c, 0xfffd, 0x2102, 0x001e, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e, 0x008e, 0x2009, 0x0017,
+ 0x080c, 0x351a, 0x0cf8, 0x2001, 0x020b, 0x2004, 0x9084, 0x0140,
+ 0x1d10, 0x00f6, 0x2079, 0x0000, 0x7884, 0x00fe, 0xd0bc, 0x0178,
+ 0x2001, 0x0201, 0x200c, 0x81ff, 0x0150, 0x080c, 0x3f98, 0x2d00,
+ 0x9c05, 0x9b05, 0x0120, 0x080c, 0x3e89, 0x0804, 0x3e29, 0x080c,
+ 0x4101, 0x080c, 0x4025, 0x080c, 0x3f7b, 0x080c, 0x3fb0, 0x00f6,
+ 0x2079, 0x0100, 0x7824, 0xd0ac, 0x0130, 0x8b58, 0x080c, 0x3e89,
+ 0x00fe, 0x0804, 0x3e29, 0x00fe, 0x080c, 0x3e7f, 0x1150, 0x8d68,
+ 0x2001, 0x0032, 0x2602, 0x2001, 0x0033, 0x2502, 0x080c, 0x3e89,
+ 0x0080, 0x87ff, 0x0138, 0x2001, 0x0201, 0x2004, 0x9005, 0x1908,
+ 0x8739, 0x0038, 0x2001, 0x1a3a, 0x2004, 0x9086, 0x0000, 0x1904,
+ 0x3d79, 0x2001, 0x032f, 0x2003, 0x00f6, 0x8631, 0x1208, 0x8529,
+ 0x2500, 0x9605, 0x0904, 0x3e29, 0x7884, 0xd0bc, 0x0128, 0x2d00,
+ 0x9c05, 0x9b05, 0x1904, 0x3e29, 0xa013, 0x0019, 0x2001, 0x032a,
+ 0x2003, 0x0004, 0x7884, 0xd0ac, 0x1148, 0x2001, 0x1a3a, 0x2003,
+ 0x0003, 0x2001, 0x032a, 0x2003, 0x0009, 0x0030, 0xa017, 0x0001,
+ 0x78b4, 0x9005, 0x0108, 0xa016, 0x2800, 0xa05a, 0x2009, 0x0040,
+ 0x080c, 0x230a, 0x2900, 0xa85a, 0xa813, 0x0019, 0x7884, 0xd0a4,
+ 0x1180, 0xa817, 0x0000, 0x00c6, 0x20a9, 0x0004, 0x2061, 0x0090,
+ 0x602b, 0x0008, 0x2001, 0x0203, 0x2004, 0x1f04, 0x3e00, 0x00ce,
+ 0x0030, 0xa817, 0x0001, 0x78b0, 0x9005, 0x0108, 0xa816, 0x00f6,
+ 0x00c6, 0x2079, 0x0100, 0x2061, 0x0090, 0x7827, 0x0002, 0x2001,
+ 0x002a, 0x2004, 0x9084, 0xfff8, 0x601a, 0x0006, 0x2001, 0x002b,
+ 0x2004, 0x601e, 0x78c6, 0x000e, 0x78ca, 0x00ce, 0x00fe, 0x0804,
+ 0x3d33, 0x001e, 0x00c6, 0x2001, 0x032a, 0x2003, 0x0004, 0x2061,
+ 0x0100, 0x6027, 0x0002, 0x6106, 0x2011, 0x020d, 0x2013, 0x0020,
+ 0x2001, 0x0004, 0x200c, 0x918c, 0xfffd, 0x2102, 0x080c, 0x12f8,
+ 0x7884, 0x9084, 0x0003, 0x9086, 0x0002, 0x0508, 0x2009, 0x0028,
+ 0x080c, 0x230a, 0x2001, 0x0227, 0x200c, 0x2102, 0x6050, 0x0006,
+ 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x000e, 0x0118, 0x9084,
+ 0xb7ef, 0x0020, 0x9084, 0xb7ff, 0x080c, 0x2cf5, 0x6052, 0x602f,
+ 0x0000, 0x604b, 0xf7f7, 0x6043, 0x0090, 0x6043, 0x0010, 0x00ce,
+ 0x2d08, 0x2c10, 0x2b18, 0x2b00, 0x9c05, 0x9d05, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x00be, 0x00ae, 0x009e, 0x008e, 0x1118, 0x012e,
+ 0x0804, 0x34e8, 0x012e, 0x2021, 0x400c, 0x0804, 0x34ea, 0x9085,
+ 0x0001, 0x1d04, 0x3e88, 0x2091, 0x6000, 0x8420, 0x9486, 0x0064,
+ 0x0005, 0x2001, 0x0105, 0x2003, 0x0010, 0x2001, 0x032a, 0x2003,
+ 0x0004, 0x2001, 0x1a3a, 0x2003, 0x0000, 0x0071, 0x2009, 0x0048,
+ 0x080c, 0x230a, 0x2001, 0x0227, 0x2024, 0x2402, 0x2001, 0x0109,
+ 0x2003, 0x4000, 0x9026, 0x0005, 0x00f6, 0x00e6, 0x2071, 0x1a3d,
+ 0x7000, 0x9086, 0x0000, 0x0520, 0x2079, 0x0090, 0x2009, 0x0206,
+ 0x2104, 0x2009, 0x0203, 0x210c, 0x9106, 0x1120, 0x2009, 0x0040,
+ 0x080c, 0x230a, 0x782c, 0xd0fc, 0x0d88, 0x080c, 0x4101, 0x7000,
+ 0x9086, 0x0000, 0x1d58, 0x782b, 0x0004, 0x782c, 0xd0ac, 0x1de8,
+ 0x2009, 0x0040, 0x080c, 0x230a, 0x782b, 0x0002, 0x7003, 0x0000,
+ 0x00ee, 0x00fe, 0x0005, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x15d0, 0x00f6, 0x2079, 0x0100, 0x2001, 0x1817, 0x200c, 0x7932,
+ 0x7936, 0x080c, 0x27b7, 0x080c, 0x2cc2, 0x080c, 0x2cf5, 0x784b,
+ 0xf7f7, 0x7843, 0x0090, 0x7843, 0x0010, 0x7850, 0xc0e5, 0x7852,
+ 0x2019, 0x61a8, 0x7820, 0xd09c, 0x0110, 0x8319, 0x1dd8, 0x7850,
+ 0xc0e4, 0x7852, 0x7827, 0x0048, 0x7843, 0x0040, 0x2019, 0x01f4,
+ 0xa001, 0xa001, 0x8319, 0x1de0, 0x2001, 0x0100, 0x080c, 0x2c88,
+ 0x7827, 0x0020, 0x7843, 0x0000, 0x9006, 0x080c, 0x2c88, 0x7827,
+ 0x0048, 0x00fe, 0x0005, 0x00f6, 0x2079, 0x0100, 0x2001, 0x1817,
+ 0x200c, 0x7932, 0x7936, 0x080c, 0x27b7, 0x7850, 0x9084, 0xfbff,
+ 0x9085, 0x0030, 0x7852, 0x2019, 0x01f4, 0x8319, 0x1df0, 0x9084,
+ 0xffcf, 0x9085, 0x2000, 0x7852, 0x20a9, 0x0046, 0x1d04, 0x3f2e,
+ 0x2091, 0x6000, 0x1f04, 0x3f2e, 0x7850, 0x9085, 0x0400, 0x9084,
+ 0xdfff, 0x7852, 0x2001, 0x0021, 0x2004, 0x9084, 0x0003, 0x9086,
+ 0x0001, 0x1120, 0x7850, 0x9084, 0xdfff, 0x7852, 0x784b, 0xf7f7,
+ 0x7843, 0x0090, 0x7843, 0x0010, 0x20a9, 0x0028, 0xa001, 0x1f04,
+ 0x3f4e, 0x7850, 0x9085, 0x1400, 0x7852, 0x2019, 0x61a8, 0x7854,
+ 0xa001, 0xa001, 0xd08c, 0x1110, 0x8319, 0x1dc8, 0x7827, 0x0048,
+ 0x7850, 0x9085, 0x0400, 0x7852, 0x7843, 0x0040, 0x2019, 0x01f4,
+ 0xa001, 0xa001, 0x8319, 0x1de0, 0x2001, 0x0100, 0x080c, 0x2c88,
+ 0x7827, 0x0020, 0x7843, 0x0000, 0x9006, 0x080c, 0x2c88, 0x7827,
+ 0x0048, 0x00fe, 0x0005, 0x7884, 0xd0ac, 0x11c8, 0x00f6, 0x00e6,
+ 0x2071, 0x1a3a, 0x2079, 0x0320, 0x2001, 0x0201, 0x2004, 0x9005,
+ 0x0160, 0x7000, 0x9086, 0x0000, 0x1140, 0x0051, 0xd0bc, 0x0108,
+ 0x8738, 0x7003, 0x0003, 0x782b, 0x0019, 0x00ee, 0x00fe, 0x0005,
+ 0x00f6, 0x2079, 0x0300, 0x78bc, 0x00fe, 0x908c, 0x0070, 0x0178,
+ 0x2009, 0x0032, 0x260a, 0x2009, 0x0033, 0x250a, 0xd0b4, 0x0108,
+ 0x8c60, 0xd0ac, 0x0108, 0x8d68, 0xd0a4, 0x0108, 0x8b58, 0x0005,
+ 0x00f6, 0x2079, 0x0200, 0x781c, 0xd084, 0x0110, 0x7837, 0x0050,
+ 0x00fe, 0x0005, 0x00e6, 0x2071, 0x0100, 0x2001, 0x1982, 0x2004,
+ 0x70e2, 0x080c, 0x3c51, 0x1188, 0x2001, 0x181f, 0x2004, 0x2009,
+ 0x181e, 0x210c, 0x918c, 0x00ff, 0x706e, 0x716a, 0x7066, 0x918d,
+ 0x3200, 0x7162, 0x7073, 0xe109, 0x0080, 0x702c, 0x9085, 0x0002,
+ 0x702e, 0x2009, 0x1817, 0x210c, 0x716e, 0x7063, 0x0100, 0x7166,
+ 0x719e, 0x706b, 0x0000, 0x7073, 0x0809, 0x7077, 0x0008, 0x7078,
+ 0x9080, 0x0100, 0x707a, 0x7080, 0x8000, 0x7082, 0x7087, 0xaaaa,
+ 0x9006, 0x708a, 0x708e, 0x707e, 0x70d6, 0x70ab, 0x0036, 0x70af,
+ 0x95d5, 0x7014, 0x9084, 0x1984, 0x9085, 0x0092, 0x7016, 0x080c,
+ 0x4101, 0x00f6, 0x2071, 0x1a3a, 0x2079, 0x0320, 0x00d6, 0x2069,
+ 0x0000, 0x6884, 0xd0b4, 0x0120, 0x689c, 0x780e, 0x6898, 0x780a,
+ 0x00de, 0x2009, 0x03e8, 0x8109, 0x1df0, 0x792c, 0xd1fc, 0x0110,
+ 0x782b, 0x0004, 0x2011, 0x0011, 0x080c, 0x408d, 0x2011, 0x0001,
+ 0x080c, 0x408d, 0x00fe, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x2071,
+ 0x1a3a, 0x2079, 0x0320, 0x792c, 0xd1fc, 0x0904, 0x408a, 0x782b,
+ 0x0002, 0x9026, 0xd19c, 0x1904, 0x4086, 0x7000, 0x0002, 0x408a,
+ 0x403b, 0x406b, 0x4086, 0xd1bc, 0x1170, 0xd1dc, 0x1190, 0x8001,
+ 0x7002, 0x2011, 0x0001, 0x080c, 0x408d, 0x0904, 0x408a, 0x080c,
+ 0x408d, 0x0804, 0x408a, 0x00f6, 0x2079, 0x0300, 0x78bf, 0x0000,
+ 0x00fe, 0x7810, 0x7914, 0x782b, 0x0004, 0x7812, 0x7916, 0x2001,
+ 0x0201, 0x200c, 0x81ff, 0x0de8, 0x080c, 0x3f98, 0x2009, 0x0001,
+ 0x00f6, 0x2079, 0x0300, 0x78b8, 0x00fe, 0xd0ec, 0x0110, 0x2009,
+ 0x0011, 0x792a, 0x00f8, 0x8001, 0x7002, 0x9184, 0x0880, 0x1140,
+ 0x782c, 0xd0fc, 0x1904, 0x402f, 0x2011, 0x0001, 0x00b1, 0x0090,
+ 0xa010, 0x9092, 0x0004, 0x9086, 0x0015, 0x1120, 0xa000, 0xa05a,
+ 0x2011, 0x0031, 0xa212, 0xd1dc, 0x1960, 0x0828, 0x782b, 0x0004,
+ 0x7003, 0x0000, 0x00ee, 0x00fe, 0x0005, 0xa014, 0x9005, 0x0550,
+ 0x8001, 0x0036, 0x0096, 0xa016, 0xa058, 0x2048, 0xa010, 0x2009,
+ 0x0031, 0x911a, 0x831c, 0x831c, 0x938a, 0x0007, 0x1a0c, 0x0dfa,
+ 0x9398, 0x40bb, 0x231d, 0x083f, 0x9080, 0x0004, 0x7a2a, 0x7100,
+ 0x8108, 0x7102, 0x009e, 0x003e, 0x908a, 0x0035, 0x1140, 0x0096,
+ 0xa058, 0x2048, 0xa804, 0xa05a, 0x2001, 0x0019, 0x009e, 0xa012,
+ 0x9085, 0x0001, 0x0005, 0x40f8, 0x40ef, 0x40e6, 0x40dd, 0x40d4,
+ 0x40cb, 0x40c2, 0xa964, 0x7902, 0xa968, 0x7906, 0xa96c, 0x7912,
+ 0xa970, 0x7916, 0x0005, 0xa974, 0x7902, 0xa978, 0x7906, 0xa97c,
+ 0x7912, 0xa980, 0x7916, 0x0005, 0xa984, 0x7902, 0xa988, 0x7906,
+ 0xa98c, 0x7912, 0xa990, 0x7916, 0x0005, 0xa994, 0x7902, 0xa998,
+ 0x7906, 0xa99c, 0x7912, 0xa9a0, 0x7916, 0x0005, 0xa9a4, 0x7902,
+ 0xa9a8, 0x7906, 0xa9ac, 0x7912, 0xa9b0, 0x7916, 0x0005, 0xa9b4,
+ 0x7902, 0xa9b8, 0x7906, 0xa9bc, 0x7912, 0xa9c0, 0x7916, 0x0005,
+ 0xa9c4, 0x7902, 0xa9c8, 0x7906, 0xa9cc, 0x7912, 0xa9d0, 0x7916,
+ 0x0005, 0x00f6, 0x00e6, 0x0086, 0x2071, 0x1a3d, 0x2079, 0x0090,
+ 0x792c, 0xd1fc, 0x01e8, 0x782b, 0x0002, 0x2940, 0x9026, 0x7000,
+ 0x0002, 0x4128, 0x4114, 0x411f, 0x8001, 0x7002, 0xd19c, 0x1180,
+ 0x2011, 0x0001, 0x080c, 0x408d, 0x190c, 0x408d, 0x0048, 0x8001,
+ 0x7002, 0x782c, 0xd0fc, 0x1d38, 0x2011, 0x0001, 0x080c, 0x408d,
+ 0x008e, 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0086,
+ 0x2061, 0x0200, 0x2001, 0x1982, 0x2004, 0x601a, 0x2061, 0x0100,
+ 0x2001, 0x1981, 0x2004, 0x60ce, 0x6104, 0xc1ac, 0x6106, 0x2001,
+ 0x002c, 0x2004, 0x9005, 0x0520, 0x2038, 0x2001, 0x002e, 0x2024,
+ 0x2001, 0x002f, 0x201c, 0x080c, 0x4abf, 0xa813, 0x0019, 0xaf16,
+ 0x2900, 0xa85a, 0x978a, 0x0007, 0x0220, 0x2138, 0x2009, 0x0007,
+ 0x0010, 0x2708, 0x903e, 0x0096, 0xa858, 0x2048, 0xa85c, 0x9080,
+ 0x0019, 0x009e, 0x080c, 0x41a4, 0x1d68, 0x2900, 0xa85a, 0x00d0,
+ 0x080c, 0x4abf, 0xa813, 0x0019, 0xa817, 0x0001, 0x2900, 0xa85a,
+ 0x2001, 0x002e, 0x2004, 0xa866, 0x2001, 0x002f, 0x2004, 0xa86a,
+ 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8, 0xa86e, 0x2001, 0x002b,
+ 0x2004, 0xa872, 0x2061, 0x0090, 0x2079, 0x0100, 0x2001, 0x1981,
+ 0x2004, 0x6036, 0x2009, 0x0040, 0x080c, 0x230a, 0x2001, 0x002a,
+ 0x2004, 0x9084, 0xfff8, 0x601a, 0x0006, 0x2001, 0x002b, 0x2004,
+ 0x601e, 0x78c6, 0x000e, 0x78ca, 0x9006, 0x600a, 0x600e, 0x008e,
+ 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x00e6, 0x2071, 0x0080, 0xaa60,
+ 0x22e8, 0x20a0, 0x20e1, 0x0000, 0x2099, 0x0088, 0x702b, 0x0026,
+ 0x7402, 0x7306, 0x9006, 0x700a, 0x700e, 0x810b, 0x810b, 0x21a8,
+ 0x810b, 0x7112, 0x702b, 0x0041, 0x702c, 0xd0fc, 0x0de8, 0x702b,
+ 0x0002, 0x702b, 0x0040, 0x4005, 0x7400, 0x7304, 0x87ff, 0x0190,
+ 0x0086, 0x0096, 0x2940, 0x0086, 0x080c, 0x4abf, 0x008e, 0xa058,
+ 0x00a6, 0x2050, 0x2900, 0xb006, 0xa05a, 0x00ae, 0x009e, 0x008e,
+ 0x9085, 0x0001, 0x00ee, 0x0005, 0x00e6, 0x2001, 0x002d, 0x2004,
+ 0x9005, 0x0528, 0x2038, 0x2001, 0x0030, 0x2024, 0x2001, 0x0031,
+ 0x201c, 0x080c, 0x4abf, 0x2940, 0xa813, 0x0019, 0xaf16, 0x2900,
+ 0xa85a, 0x978a, 0x0007, 0x0220, 0x2138, 0x2009, 0x0007, 0x0010,
+ 0x2708, 0x903e, 0x0096, 0xa858, 0x2048, 0xa85c, 0x9080, 0x0019,
+ 0x009e, 0x080c, 0x41a4, 0x1d68, 0x2900, 0xa85a, 0x00d8, 0x080c,
+ 0x4abf, 0x2940, 0xa013, 0x0019, 0xa017, 0x0001, 0x2800, 0xa05a,
+ 0x2001, 0x0030, 0x2004, 0xa066, 0x2001, 0x0031, 0x2004, 0xa06a,
+ 0x2001, 0x002a, 0x2004, 0x9084, 0xfff8, 0xa06e, 0x2001, 0x002b,
+ 0x2004, 0xa072, 0x2001, 0x032a, 0x2003, 0x0004, 0x7884, 0xd0ac,
+ 0x1180, 0x2001, 0x0101, 0x200c, 0x918d, 0x0200, 0x2102, 0xa017,
+ 0x0000, 0x2001, 0x1a3a, 0x2003, 0x0003, 0x2001, 0x032a, 0x2003,
+ 0x0009, 0x2001, 0x0300, 0x2003, 0x0000, 0x2001, 0x020d, 0x2003,
+ 0x0000, 0x2001, 0x0004, 0x200c, 0x918d, 0x0002, 0x2102, 0x00ee,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x20a9, 0x001b, 0x20a1, 0x1840,
+ 0x20e9, 0x0001, 0x9006, 0x4004, 0x2009, 0x013c, 0x200a, 0x012e,
+ 0x7880, 0x9086, 0x0052, 0x0108, 0x0005, 0x0804, 0x34e8, 0x7d98,
+ 0x7c9c, 0x0804, 0x35ea, 0x080c, 0x7207, 0x190c, 0x5ef0, 0x2069,
+ 0x185b, 0x2d00, 0x2009, 0x0030, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98,
+ 0x2039, 0x0001, 0x080c, 0x4b08, 0x701f, 0x4277, 0x0005, 0x080c,
+ 0x55ea, 0x1130, 0x3b00, 0x3a08, 0xc194, 0xc095, 0x20d8, 0x21d0,
+ 0x2069, 0x185b, 0x6800, 0x9005, 0x0904, 0x351d, 0x6804, 0xd094,
+ 0x00c6, 0x2061, 0x0100, 0x6104, 0x0138, 0x6200, 0x9292, 0x0005,
+ 0x0218, 0x918c, 0xffdf, 0x0010, 0x918d, 0x0020, 0x6106, 0x00ce,
+ 0xd08c, 0x00c6, 0x2061, 0x0100, 0x6104, 0x0118, 0x918d, 0x0010,
+ 0x0010, 0x918c, 0xffef, 0x6106, 0x00ce, 0xd084, 0x0158, 0x6a28,
+ 0x928a, 0x007f, 0x1a04, 0x351d, 0x9288, 0x32e9, 0x210d, 0x918c,
+ 0x00ff, 0x6162, 0xd0dc, 0x0130, 0x6828, 0x908a, 0x007f, 0x1a04,
+ 0x351d, 0x605a, 0x6888, 0x9084, 0x0030, 0x8004, 0x8004, 0x8004,
+ 0x8004, 0x0006, 0x2009, 0x1989, 0x9080, 0x28aa, 0x2005, 0x200a,
+ 0x000e, 0x2009, 0x198a, 0x9080, 0x28ae, 0x2005, 0x200a, 0x6808,
+ 0x908a, 0x0100, 0x0a04, 0x351d, 0x908a, 0x0841, 0x1a04, 0x351d,
+ 0x9084, 0x0007, 0x1904, 0x351d, 0x680c, 0x9005, 0x0904, 0x351d,
+ 0x6810, 0x9005, 0x0904, 0x351d, 0x6848, 0x6940, 0x910a, 0x1a04,
+ 0x351d, 0x8001, 0x0904, 0x351d, 0x684c, 0x6944, 0x910a, 0x1a04,
+ 0x351d, 0x8001, 0x0904, 0x351d, 0x2009, 0x1959, 0x200b, 0x0000,
+ 0x2001, 0x187d, 0x2004, 0xd0c4, 0x0140, 0x7884, 0x200a, 0x2009,
+ 0x017f, 0x200a, 0x3b00, 0xc085, 0x20d8, 0x6814, 0x908c, 0x00ff,
+ 0x614a, 0x8007, 0x9084, 0x00ff, 0x604e, 0x080c, 0x7535, 0x080c,
+ 0x688c, 0x080c, 0x68c1, 0x6808, 0x602a, 0x080c, 0x227c, 0x2009,
+ 0x0170, 0x200b, 0x0080, 0xa001, 0xa001, 0x200b, 0x0000, 0x0036,
+ 0x6b08, 0x080c, 0x2811, 0x003e, 0x6000, 0x9086, 0x0000, 0x1904,
+ 0x43f8, 0x6818, 0x691c, 0x6a20, 0x6b24, 0x8007, 0x810f, 0x8217,
+ 0x831f, 0x6016, 0x611a, 0x621e, 0x6322, 0x6c04, 0xd4f4, 0x0148,
+ 0x6830, 0x6934, 0x6a38, 0x6b3c, 0x8007, 0x810f, 0x8217, 0x831f,
+ 0x0010, 0x9084, 0xf0ff, 0x6006, 0x610a, 0x620e, 0x6312, 0x8007,
+ 0x810f, 0x8217, 0x831f, 0x20a9, 0x0004, 0x20a1, 0x198b, 0x20e9,
+ 0x0001, 0x4001, 0x20a9, 0x0004, 0x20a1, 0x19a5, 0x20e9, 0x0001,
+ 0x4001, 0x080c, 0x83e3, 0x00c6, 0x900e, 0x20a9, 0x0001, 0x6b70,
+ 0xd384, 0x01c8, 0x0020, 0x839d, 0x12b0, 0x3508, 0x8109, 0x080c,
+ 0x7af8, 0x6878, 0x6016, 0x6874, 0x2008, 0x9084, 0xff00, 0x8007,
+ 0x600a, 0x9184, 0x00ff, 0x6006, 0x8108, 0x1118, 0x6003, 0x0003,
+ 0x0010, 0x6003, 0x0001, 0x1f04, 0x4363, 0x00ce, 0x00c6, 0x2061,
+ 0x1974, 0x2063, 0x0001, 0x9006, 0x080c, 0x2ab8, 0x9006, 0x080c,
+ 0x2a9b, 0x0000, 0x00ce, 0x00e6, 0x2c70, 0x080c, 0x0ec6, 0x00ee,
+ 0x6888, 0xd0ec, 0x0198, 0x2001, 0x0100, 0x2004, 0x9086, 0x000a,
+ 0x0138, 0x2011, 0x0114, 0x2204, 0x9085, 0x0100, 0x2012, 0x0030,
+ 0x2011, 0x0114, 0x2204, 0x9085, 0x0180, 0x2012, 0x6a80, 0x9284,
+ 0x0030, 0x9086, 0x0030, 0x1128, 0x9294, 0xffcf, 0x9295, 0x0020,
+ 0x6a82, 0x2001, 0x1954, 0x6a80, 0x9294, 0x0030, 0x928e, 0x0000,
+ 0x0170, 0x928e, 0x0010, 0x0118, 0x928e, 0x0020, 0x0140, 0x2003,
+ 0xaaaa, 0x080c, 0x2886, 0x2001, 0x1945, 0x2102, 0x0008, 0x2102,
+ 0x00c6, 0x2061, 0x0100, 0x602f, 0x0040, 0x602f, 0x0000, 0x00ce,
+ 0x080c, 0x7207, 0x0128, 0x080c, 0x4ed2, 0x0110, 0x080c, 0x27d7,
+ 0x60d0, 0x9005, 0x01c0, 0x6003, 0x0001, 0x2009, 0x43e0, 0x00d0,
+ 0x080c, 0x7207, 0x1168, 0x2011, 0x7076, 0x080c, 0x82da, 0x2011,
+ 0x7069, 0x080c, 0x83ae, 0x080c, 0x7509, 0x080c, 0x7127, 0x0040,
+ 0x080c, 0x5dea, 0x0028, 0x6003, 0x0004, 0x2009, 0x43f8, 0x0010,
+ 0x0804, 0x34e8, 0x2001, 0x0170, 0x2004, 0x9084, 0x00ff, 0x9086,
+ 0x004c, 0x1118, 0x2091, 0x30bd, 0x0817, 0x2091, 0x303d, 0x0817,
+ 0x6000, 0x9086, 0x0000, 0x0904, 0x351a, 0x2069, 0x185b, 0x7890,
+ 0x6842, 0x7894, 0x6846, 0x2d00, 0x2009, 0x0030, 0x7a8c, 0x7b88,
+ 0x7c9c, 0x7d98, 0x2039, 0x0001, 0x0804, 0x4b0b, 0x9006, 0x080c,
+ 0x27d7, 0x81ff, 0x1904, 0x351a, 0x080c, 0x7207, 0x11b0, 0x080c,
+ 0x7504, 0x080c, 0x5f2b, 0x080c, 0x32e4, 0x0118, 0x6130, 0xc18d,
+ 0x6132, 0x080c, 0xc539, 0x0130, 0x080c, 0x722a, 0x1118, 0x080c,
+ 0x71df, 0x0038, 0x080c, 0x7127, 0x0020, 0x080c, 0x5ef0, 0x080c,
+ 0x5dea, 0x0804, 0x34e8, 0x81ff, 0x1904, 0x351a, 0x080c, 0x7207,
+ 0x1110, 0x0804, 0x351a, 0x6190, 0x81ff, 0x01a8, 0x704f, 0x0000,
+ 0x2001, 0x1c80, 0x2009, 0x0040, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98,
+ 0x0126, 0x2091, 0x8000, 0x2039, 0x0001, 0x080c, 0x4b0b, 0x701f,
+ 0x34e6, 0x012e, 0x0005, 0x704f, 0x0001, 0x00d6, 0x2069, 0x1c80,
+ 0x20a9, 0x0040, 0x20e9, 0x0001, 0x20a1, 0x1c80, 0x2019, 0xffff,
+ 0x4304, 0x6558, 0x9588, 0x32e9, 0x210d, 0x918c, 0x00ff, 0x216a,
+ 0x900e, 0x2011, 0x0002, 0x2100, 0x9506, 0x01a8, 0x080c, 0x649f,
+ 0x1190, 0xb814, 0x821c, 0x0238, 0x9398, 0x1c80, 0x9085, 0xff00,
+ 0x8007, 0x201a, 0x0038, 0x9398, 0x1c80, 0x2324, 0x94a4, 0xff00,
+ 0x9405, 0x201a, 0x8210, 0x8108, 0x9182, 0x0080, 0x1208, 0x0c18,
+ 0x8201, 0x8007, 0x2d0c, 0x9105, 0x206a, 0x00de, 0x20a9, 0x0040,
+ 0x20a1, 0x1c80, 0x2099, 0x1c80, 0x080c, 0x5e7b, 0x0804, 0x4450,
+ 0x080c, 0x4af2, 0x0904, 0x351d, 0x080c, 0x4abf, 0x1120, 0x2009,
+ 0x0002, 0x0804, 0x351a, 0x080c, 0x55db, 0xd0b4, 0x0558, 0x7884,
+ 0x908e, 0x007e, 0x0538, 0x908e, 0x007f, 0x0520, 0x908e, 0x0080,
+ 0x0508, 0x080c, 0x32df, 0x1148, 0xb800, 0xd08c, 0x11d8, 0xb804,
+ 0x9084, 0x00ff, 0x9086, 0x0006, 0x11a8, 0xa867, 0x0000, 0xa868,
+ 0xc0fd, 0xa86a, 0x080c, 0xc002, 0x1120, 0x2009, 0x0003, 0x0804,
+ 0x351a, 0x7007, 0x0003, 0x701f, 0x44de, 0x0005, 0x080c, 0x4af2,
+ 0x0904, 0x351d, 0x20a9, 0x002b, 0xb8b4, 0x20e0, 0xb8b8, 0x2098,
+ 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0002, 0x20a0, 0x4003, 0x20a9,
+ 0x0008, 0x9080, 0x0006, 0x20a0, 0xb8b4, 0x20e0, 0xb8b8, 0x9080,
+ 0x0006, 0x2098, 0x080c, 0x0fae, 0x0070, 0x20a9, 0x0004, 0xa85c,
+ 0x9080, 0x000a, 0x20a0, 0xb8b4, 0x20e0, 0xb8b8, 0x9080, 0x000a,
+ 0x2098, 0x080c, 0x0fae, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f,
+ 0x9084, 0xffc0, 0x9080, 0x0002, 0x2009, 0x002b, 0x7a8c, 0x7b88,
+ 0x7c9c, 0x7d98, 0x0804, 0x4b0b, 0x81ff, 0x1904, 0x351a, 0x080c,
+ 0x4ad6, 0x0904, 0x351d, 0x080c, 0x6611, 0x0904, 0x351a, 0x0058,
+ 0xa878, 0x9005, 0x0120, 0x2009, 0x0004, 0x0804, 0x351a, 0xa974,
+ 0xaa94, 0x0804, 0x34e8, 0x080c, 0x55e3, 0x0904, 0x34e8, 0x701f,
+ 0x4528, 0x7007, 0x0003, 0x0005, 0x81ff, 0x1904, 0x351a, 0x7888,
+ 0x908a, 0x1000, 0x1a04, 0x351d, 0x080c, 0x4af2, 0x0904, 0x351d,
+ 0x080c, 0x67c3, 0x0120, 0x080c, 0x67cb, 0x1904, 0x351d, 0x080c,
+ 0x6696, 0x0904, 0x351a, 0x2019, 0x0004, 0x900e, 0x080c, 0x6623,
+ 0x0904, 0x351a, 0x7984, 0x7a88, 0x04c9, 0x08a8, 0xa89c, 0x908a,
+ 0x1000, 0x12f8, 0x080c, 0x4af0, 0x01e0, 0x080c, 0x67c3, 0x0118,
+ 0x080c, 0x67cb, 0x11b0, 0x080c, 0x6696, 0x2009, 0x0002, 0x0168,
+ 0x2009, 0x0002, 0x2019, 0x0004, 0x080c, 0x6623, 0x2009, 0x0003,
+ 0x0120, 0xa998, 0xaa9c, 0x00d1, 0x0060, 0xa897, 0x4005, 0xa99a,
+ 0x0010, 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030,
+ 0x0005, 0xa897, 0x4000, 0x080c, 0x55e3, 0x0110, 0x9006, 0x0018,
+ 0x900e, 0x9085, 0x0001, 0x2001, 0x0000, 0x0005, 0x9186, 0x00ff,
+ 0x0110, 0x0071, 0x0060, 0x2029, 0x007e, 0x2061, 0x1800, 0x6458,
+ 0x2400, 0x9506, 0x0110, 0x2508, 0x0019, 0x8529, 0x1ec8, 0x0005,
+ 0x080c, 0x649f, 0x1138, 0x2200, 0x8003, 0x800b, 0x810b, 0x9108,
+ 0x080c, 0x82e8, 0x0005, 0x81ff, 0x1904, 0x351a, 0x798c, 0x2001,
+ 0x1958, 0x918c, 0x8000, 0x2102, 0x080c, 0x4ad6, 0x0904, 0x351d,
+ 0x080c, 0x67c3, 0x0120, 0x080c, 0x67cb, 0x1904, 0x351d, 0x080c,
+ 0x6566, 0x0904, 0x351a, 0x080c, 0x661a, 0x0904, 0x351a, 0x2001,
+ 0x1958, 0x2004, 0xd0fc, 0x1904, 0x34e8, 0x0804, 0x4533, 0xa9a0,
+ 0x2001, 0x1958, 0x918c, 0x8000, 0xc18d, 0x2102, 0x080c, 0x4ae3,
+ 0x01a0, 0x080c, 0x67c3, 0x0118, 0x080c, 0x67cb, 0x1170, 0x080c,
+ 0x6566, 0x2009, 0x0002, 0x0128, 0x080c, 0x661a, 0x1170, 0x2009,
+ 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e,
+ 0x9085, 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x2001,
+ 0x1958, 0x2004, 0xd0fc, 0x1128, 0x080c, 0x55e3, 0x0110, 0x9006,
+ 0x0018, 0x900e, 0x9085, 0x0001, 0x2001, 0x0000, 0x0005, 0x81ff,
+ 0x1904, 0x351a, 0x798c, 0x2001, 0x1957, 0x918c, 0x8000, 0x2102,
+ 0x080c, 0x4ad6, 0x0904, 0x351d, 0x080c, 0x67c3, 0x0120, 0x080c,
+ 0x67cb, 0x1904, 0x351d, 0x080c, 0x6566, 0x0904, 0x351a, 0x080c,
+ 0x6608, 0x0904, 0x351a, 0x2001, 0x1957, 0x2004, 0xd0fc, 0x1904,
+ 0x34e8, 0x0804, 0x4533, 0xa9a0, 0x2001, 0x1957, 0x918c, 0x8000,
+ 0xc18d, 0x2102, 0x080c, 0x4ae3, 0x01a0, 0x080c, 0x67c3, 0x0118,
+ 0x080c, 0x67cb, 0x1170, 0x080c, 0x6566, 0x2009, 0x0002, 0x0128,
+ 0x080c, 0x6608, 0x1170, 0x2009, 0x0003, 0xa897, 0x4005, 0xa99a,
+ 0x0010, 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030,
+ 0x0005, 0xa897, 0x4000, 0x2001, 0x1957, 0x2004, 0xd0fc, 0x1128,
+ 0x080c, 0x55e3, 0x0110, 0x9006, 0x0018, 0x900e, 0x9085, 0x0001,
+ 0x2001, 0x0000, 0x0005, 0x6100, 0x0804, 0x34e8, 0x080c, 0x4af2,
+ 0x0904, 0x351d, 0x080c, 0x55ef, 0x1904, 0x351a, 0x79a8, 0xd184,
+ 0x1158, 0xb834, 0x8007, 0x789e, 0xb830, 0x8007, 0x789a, 0xbb2c,
+ 0x831f, 0xba28, 0x8217, 0x0050, 0xb824, 0x8007, 0x789e, 0xb820,
+ 0x8007, 0x789a, 0xbb1c, 0x831f, 0xba18, 0x8217, 0xb900, 0x918c,
+ 0x0202, 0x0804, 0x34e8, 0x78a8, 0x909c, 0x0003, 0xd0ac, 0x1158,
+ 0xd0b4, 0x1148, 0x939a, 0x0003, 0x1a04, 0x351a, 0x6258, 0x7884,
+ 0x9206, 0x1904, 0x46eb, 0x2031, 0x1848, 0x2009, 0x013c, 0x2136,
+ 0x2001, 0x1840, 0x2009, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98,
+ 0x2039, 0x0001, 0x0006, 0x78a8, 0x9084, 0x0080, 0x11f8, 0x0006,
+ 0x0036, 0x2001, 0x1a57, 0x201c, 0x7b9a, 0x2003, 0x0000, 0x2001,
+ 0x1a58, 0x201c, 0x7b9e, 0x2003, 0x0000, 0x2001, 0x1a59, 0x201c,
+ 0x7ba2, 0x2003, 0x0000, 0x2001, 0x1a53, 0x201c, 0x7baa, 0x2003,
+ 0x0000, 0x003e, 0x000e, 0x000e, 0x0804, 0x4b0b, 0x000e, 0x2031,
+ 0x0000, 0x2061, 0x18b6, 0x2c44, 0xa66a, 0xa17a, 0xa772, 0xa076,
+ 0xa28e, 0xa392, 0xa496, 0xa59a, 0x080c, 0x112e, 0x7007, 0x0002,
+ 0x701f, 0x470b, 0x0005, 0x81ff, 0x1904, 0x351a, 0x080c, 0x4af2,
+ 0x0904, 0x351d, 0x080c, 0x67c3, 0x1904, 0x351a, 0x00c6, 0x080c,
+ 0x4abf, 0x00ce, 0x0904, 0x351a, 0xa867, 0x0000, 0xa868, 0xc0fd,
+ 0xa86a, 0x7ea8, 0x080c, 0xbfa8, 0x0904, 0x351a, 0x7007, 0x0003,
+ 0x701f, 0x472b, 0x0005, 0x080c, 0x4249, 0x0006, 0x0036, 0x2001,
+ 0x1a57, 0x201c, 0x7b9a, 0x2003, 0x0000, 0x2001, 0x1a58, 0x201c,
+ 0x7b9e, 0x2003, 0x0000, 0x2001, 0x1a59, 0x201c, 0x7ba2, 0x2003,
+ 0x0000, 0x2001, 0x1a53, 0x201c, 0x7baa, 0x2003, 0x0000, 0x003e,
+ 0x000e, 0x0804, 0x34e8, 0xa830, 0x9086, 0x0100, 0x0904, 0x351a,
+ 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080,
+ 0x001b, 0x2009, 0x000c, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x0804,
+ 0x4b0b, 0x9006, 0x080c, 0x27d7, 0x78a8, 0x9084, 0x00ff, 0x9086,
+ 0x00ff, 0x0118, 0x81ff, 0x1904, 0x351a, 0x080c, 0x7207, 0x0110,
+ 0x080c, 0x5ef0, 0x7888, 0x908a, 0x1000, 0x1a04, 0x351d, 0x7984,
+ 0x9186, 0x00ff, 0x0138, 0x9182, 0x007f, 0x1a04, 0x351d, 0x2100,
+ 0x080c, 0x27a1, 0x0026, 0x00c6, 0x0126, 0x2091, 0x8000, 0x2061,
+ 0x19d2, 0x601b, 0x0000, 0x601f, 0x0000, 0x6073, 0x0000, 0x6077,
+ 0x0000, 0x080c, 0x7207, 0x1158, 0x080c, 0x7504, 0x080c, 0x5f2b,
+ 0x9085, 0x0001, 0x080c, 0x724e, 0x080c, 0x7127, 0x00d0, 0x080c,
+ 0xa069, 0x2061, 0x0100, 0x2001, 0x1817, 0x2004, 0x9084, 0x00ff,
+ 0x810f, 0x9105, 0x604a, 0x6043, 0x0090, 0x6043, 0x0010, 0x2009,
+ 0x1971, 0x200b, 0x0000, 0x2009, 0x002d, 0x2011, 0x5e16, 0x080c,
+ 0x836c, 0x7984, 0x080c, 0x7207, 0x1110, 0x2009, 0x00ff, 0x7a88,
+ 0x080c, 0x4596, 0x012e, 0x00ce, 0x002e, 0x0804, 0x34e8, 0x7984,
+ 0x080c, 0x643f, 0x2b08, 0x1904, 0x351d, 0x0804, 0x34e8, 0x81ff,
+ 0x0120, 0x2009, 0x0001, 0x0804, 0x351a, 0x60d8, 0xd0ac, 0x1130,
+ 0xd09c, 0x1120, 0x2009, 0x0005, 0x0804, 0x351a, 0x080c, 0x4abf,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x351a, 0x7984, 0x9192, 0x0021,
+ 0x1a04, 0x351d, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080,
+ 0x0019, 0x702a, 0xaf60, 0x7736, 0x080c, 0x4b08, 0x701f, 0x47df,
+ 0x7880, 0x9086, 0x006e, 0x0110, 0x701f, 0x5084, 0x0005, 0x2009,
+ 0x0080, 0x080c, 0x649f, 0x1118, 0x080c, 0x67c3, 0x0120, 0x2021,
+ 0x400a, 0x0804, 0x34ea, 0x00d6, 0x0096, 0xa964, 0xaa6c, 0xab70,
+ 0xac74, 0xad78, 0xae7c, 0xa884, 0x90be, 0x0100, 0x0904, 0x4878,
+ 0x90be, 0x0112, 0x0904, 0x4878, 0x90be, 0x0113, 0x0904, 0x4878,
+ 0x90be, 0x0114, 0x0904, 0x4878, 0x90be, 0x0117, 0x0904, 0x4878,
+ 0x90be, 0x011a, 0x0904, 0x4878, 0x90be, 0x011c, 0x0904, 0x4878,
+ 0x90be, 0x0121, 0x0904, 0x485f, 0x90be, 0x0131, 0x0904, 0x485f,
+ 0x90be, 0x0171, 0x0904, 0x4878, 0x90be, 0x0173, 0x0904, 0x4878,
+ 0x90be, 0x01a1, 0x1128, 0xa894, 0x8007, 0xa896, 0x0804, 0x4883,
+ 0x90be, 0x0212, 0x0904, 0x486c, 0x90be, 0x0213, 0x05e8, 0x90be,
+ 0x0214, 0x0500, 0x90be, 0x0217, 0x0188, 0x90be, 0x021a, 0x1120,
+ 0xa89c, 0x8007, 0xa89e, 0x04e0, 0x90be, 0x021f, 0x05c8, 0x90be,
+ 0x0300, 0x05b0, 0x009e, 0x00de, 0x0804, 0x351d, 0x7028, 0x9080,
+ 0x0010, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9, 0x0007,
+ 0x080c, 0x48c1, 0x7028, 0x9080, 0x000e, 0x2098, 0x20a0, 0x7034,
+ 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x080c, 0x48c1, 0x00c8, 0x7028,
+ 0x9080, 0x000c, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9,
+ 0x0001, 0x080c, 0x48ce, 0x00b8, 0x7028, 0x9080, 0x000e, 0x2098,
+ 0x20a0, 0x7034, 0x20e0, 0x20e8, 0x20a9, 0x0001, 0x080c, 0x48ce,
+ 0x7028, 0x9080, 0x000c, 0x2098, 0x20a0, 0x7034, 0x20e0, 0x20e8,
+ 0x20a9, 0x0001, 0x04f1, 0x00c6, 0x080c, 0x4abf, 0x0550, 0xa868,
+ 0xc0fd, 0xa86a, 0xa867, 0x0119, 0x9006, 0xa882, 0xa87f, 0x0020,
+ 0xa88b, 0x0001, 0x810b, 0xa9ae, 0xa8b2, 0xaab6, 0xabba, 0xacbe,
+ 0xadc2, 0xa9c6, 0xa8ca, 0x00ce, 0x009e, 0x00de, 0xa866, 0xa822,
+ 0xa868, 0xc0fd, 0xa86a, 0xa804, 0x2048, 0x080c, 0xbfc3, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x351a, 0x7007, 0x0003, 0x701f, 0x48b8,
+ 0x0005, 0x00ce, 0x009e, 0x00de, 0x2009, 0x0002, 0x0804, 0x351a,
+ 0xa820, 0x9086, 0x8001, 0x1904, 0x34e8, 0x2009, 0x0004, 0x0804,
+ 0x351a, 0x0016, 0x0026, 0x3510, 0x20a9, 0x0002, 0x4002, 0x4104,
+ 0x4004, 0x8211, 0x1dc8, 0x002e, 0x001e, 0x0005, 0x0016, 0x0026,
+ 0x0036, 0x0046, 0x3520, 0x20a9, 0x0004, 0x4002, 0x4304, 0x4204,
+ 0x4104, 0x4004, 0x8421, 0x1db8, 0x004e, 0x003e, 0x002e, 0x001e,
+ 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x351a, 0x60d8,
+ 0xd0ac, 0x1160, 0xd09c, 0x0120, 0x2009, 0x0016, 0x0804, 0x351a,
+ 0xd09c, 0x1120, 0x2009, 0x0005, 0x0804, 0x351a, 0x7984, 0x78a8,
+ 0x2040, 0x080c, 0xa062, 0x1120, 0x9182, 0x007f, 0x0a04, 0x351d,
+ 0x9186, 0x00ff, 0x0904, 0x351d, 0x9182, 0x0800, 0x1a04, 0x351d,
+ 0x7a8c, 0x7b88, 0x6078, 0x9306, 0x1158, 0x607c, 0x924e, 0x0904,
+ 0x351d, 0x080c, 0xa062, 0x1120, 0x99cc, 0xff00, 0x0904, 0x351d,
+ 0x0126, 0x2091, 0x8000, 0x9386, 0xffff, 0x0178, 0x0026, 0x2011,
+ 0x8008, 0x080c, 0x67e7, 0x002e, 0x0140, 0x918d, 0x8000, 0x080c,
+ 0x6831, 0x1118, 0x2001, 0x4009, 0x0458, 0x080c, 0x49d9, 0x0560,
+ 0x90c6, 0x4000, 0x1170, 0x00c6, 0x0006, 0x900e, 0x080c, 0x66bf,
+ 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0x000e, 0x00ce,
+ 0x00b8, 0x90c6, 0x4007, 0x1110, 0x2408, 0x0090, 0x90c6, 0x4008,
+ 0x1118, 0x2708, 0x2610, 0x0060, 0x90c6, 0x4009, 0x1108, 0x0040,
+ 0x90c6, 0x4006, 0x1108, 0x0020, 0x2001, 0x4005, 0x2009, 0x000a,
+ 0x2020, 0x012e, 0x0804, 0x34ea, 0x2b00, 0x7026, 0x0016, 0x00b6,
+ 0x00c6, 0x00e6, 0x2c70, 0x080c, 0xa130, 0x0904, 0x49a6, 0x2b00,
+ 0x6012, 0x080c, 0xc2b3, 0x2e58, 0x00ee, 0x00e6, 0x00c6, 0x080c,
+ 0x4abf, 0x00ce, 0x2b70, 0x1158, 0x080c, 0xa0e3, 0x00ee, 0x00ce,
+ 0x00be, 0x001e, 0x012e, 0x2009, 0x0002, 0x0804, 0x351a, 0x900e,
+ 0xa966, 0xa96a, 0x2900, 0x6016, 0xa932, 0xa868, 0xc0fd, 0xd88c,
+ 0x0108, 0xc0f5, 0xa86a, 0x080c, 0x318b, 0x6023, 0x0001, 0x9006,
+ 0x080c, 0x63dc, 0x2001, 0x0002, 0x080c, 0x63f0, 0x2009, 0x0002,
+ 0x080c, 0xa15d, 0x78a8, 0xd094, 0x0138, 0x00ee, 0x7024, 0x00e6,
+ 0x2058, 0xb8bc, 0xc08d, 0xb8be, 0x9085, 0x0001, 0x00ee, 0x00ce,
+ 0x00be, 0x001e, 0x012e, 0x1120, 0x2009, 0x0003, 0x0804, 0x351a,
+ 0x7007, 0x0003, 0x701f, 0x49b5, 0x0005, 0xa830, 0x2008, 0x918e,
+ 0xdead, 0x1120, 0x2021, 0x4009, 0x0804, 0x34ea, 0x9086, 0x0100,
+ 0x7024, 0x2058, 0x1138, 0x2009, 0x0004, 0xba04, 0x9294, 0x00ff,
+ 0x0804, 0x552f, 0x900e, 0xa868, 0xd0f4, 0x1904, 0x34e8, 0x080c,
+ 0x66bf, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0x0804,
+ 0x34e8, 0x00e6, 0x00d6, 0x0096, 0x83ff, 0x0904, 0x4a21, 0x902e,
+ 0x080c, 0xa062, 0x0130, 0x9026, 0x20a9, 0x0800, 0x2071, 0x1000,
+ 0x0030, 0x2021, 0x007f, 0x20a9, 0x0781, 0x2071, 0x107f, 0x2e04,
+ 0x9005, 0x11b0, 0x2100, 0x9406, 0x15e8, 0x2428, 0x94ce, 0x007f,
+ 0x1120, 0x92ce, 0xfffd, 0x1528, 0x0030, 0x94ce, 0x0080, 0x1130,
+ 0x92ce, 0xfffc, 0x11f0, 0x93ce, 0x00ff, 0x11d8, 0xc5fd, 0x0450,
+ 0x2058, 0xbf10, 0x2700, 0x9306, 0x11b8, 0xbe14, 0x2600, 0x9206,
+ 0x1198, 0x2400, 0x9106, 0x1150, 0xd884, 0x0568, 0xd894, 0x1558,
+ 0x080c, 0x67c3, 0x1540, 0x2001, 0x4000, 0x0430, 0x2001, 0x4007,
+ 0x0418, 0x2001, 0x4006, 0x0400, 0x2400, 0x9106, 0x1158, 0xbe14,
+ 0x87ff, 0x1128, 0x86ff, 0x0948, 0x080c, 0xa062, 0x1930, 0x2001,
+ 0x4008, 0x0090, 0x8420, 0x8e70, 0x1f04, 0x49ef, 0x85ff, 0x1130,
+ 0x2001, 0x4009, 0x0048, 0x2001, 0x0001, 0x0030, 0x080c, 0x643f,
+ 0x1dd0, 0xbb12, 0xba16, 0x9006, 0x9005, 0x009e, 0x00de, 0x00ee,
+ 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x351a, 0x080c,
+ 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804, 0x351a, 0xa867, 0x0000,
+ 0xa868, 0xc0fd, 0xa86a, 0x7884, 0x9005, 0x0904, 0x351d, 0x9096,
+ 0x00ff, 0x0120, 0x9092, 0x0004, 0x1a04, 0x351d, 0x2010, 0x2918,
+ 0x080c, 0x3131, 0x1120, 0x2009, 0x0003, 0x0804, 0x351a, 0x7007,
+ 0x0003, 0x701f, 0x4a74, 0x0005, 0xa830, 0x9086, 0x0100, 0x1904,
+ 0x34e8, 0x2009, 0x0004, 0x0804, 0x351a, 0x7984, 0x080c, 0xa062,
+ 0x1120, 0x9182, 0x007f, 0x0a04, 0x351d, 0x9186, 0x00ff, 0x0904,
+ 0x351d, 0x9182, 0x0800, 0x1a04, 0x351d, 0x2001, 0x9400, 0x080c,
+ 0x558a, 0x1904, 0x351a, 0x0804, 0x34e8, 0xa998, 0x080c, 0xa062,
+ 0x1118, 0x9182, 0x007f, 0x0280, 0x9186, 0x00ff, 0x0168, 0x9182,
+ 0x0800, 0x1250, 0x2001, 0x9400, 0x080c, 0x558a, 0x11a8, 0x0060,
+ 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x900e, 0x9085,
+ 0x0001, 0x2001, 0x0000, 0x0005, 0x2009, 0x000a, 0x0c48, 0x080c,
+ 0x1031, 0x0198, 0x9006, 0xa802, 0x7014, 0x9005, 0x1120, 0x2900,
+ 0x7016, 0x701a, 0x0040, 0x7018, 0xa802, 0x0086, 0x2040, 0x2900,
+ 0xa006, 0x701a, 0x008e, 0x9085, 0x0001, 0x0005, 0x7984, 0x080c,
+ 0x649f, 0x1130, 0x7e88, 0x9684, 0x3fff, 0x9082, 0x4000, 0x0208,
+ 0x905e, 0x8bff, 0x0005, 0xa998, 0x080c, 0x649f, 0x1130, 0xae9c,
+ 0x9684, 0x3fff, 0x9082, 0x4000, 0x0208, 0x905e, 0x8bff, 0x0005,
+ 0xae98, 0x0008, 0x7e84, 0x2608, 0x080c, 0x649f, 0x1108, 0x0008,
+ 0x905e, 0x8bff, 0x0005, 0x0016, 0x7114, 0x81ff, 0x0128, 0x2148,
+ 0xa904, 0x080c, 0x1063, 0x0cc8, 0x7116, 0x711a, 0x001e, 0x0005,
+ 0x2031, 0x0001, 0x0010, 0x2031, 0x0000, 0x2061, 0x18b6, 0x2c44,
+ 0xa66a, 0xa17a, 0xa772, 0xa076, 0xa28e, 0xa392, 0xa496, 0xa59a,
+ 0x080c, 0x112e, 0x7007, 0x0002, 0x701f, 0x34e8, 0x0005, 0x00f6,
+ 0x0126, 0x2091, 0x8000, 0x2079, 0x0000, 0x2001, 0x18ae, 0x2004,
+ 0x9005, 0x1190, 0x0e04, 0x4b3c, 0x7a36, 0x7833, 0x0012, 0x7a82,
+ 0x7b86, 0x7c8a, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11e0, 0x0804, 0x4ba2, 0x0016, 0x0086, 0x0096, 0x00c6,
+ 0x00e6, 0x2071, 0x189c, 0x7044, 0x9005, 0x1540, 0x7148, 0x9182,
+ 0x0010, 0x0288, 0x7038, 0x2060, 0x080c, 0x1031, 0x0904, 0x4b9a,
+ 0xa84b, 0x0000, 0x2900, 0x7046, 0x2001, 0x0002, 0x9080, 0x1fc8,
+ 0x2005, 0xa846, 0x0098, 0x7038, 0x90e0, 0x0004, 0x2001, 0x18b8,
+ 0x9c82, 0x18f8, 0x0210, 0x2061, 0x18b8, 0x2c00, 0x703a, 0x7148,
+ 0x81ff, 0x1108, 0x703e, 0x8108, 0x714a, 0x0460, 0x7148, 0x8108,
+ 0x714a, 0x7044, 0x2040, 0xa144, 0x2105, 0x0016, 0x908a, 0x0036,
+ 0x1a0c, 0x0dfa, 0x2060, 0x001e, 0x8108, 0x2105, 0x9005, 0xa146,
+ 0x1520, 0x080c, 0x1031, 0x1130, 0x8109, 0xa946, 0x7148, 0x8109,
+ 0x714a, 0x00d8, 0x9006, 0xa806, 0xa84a, 0xa046, 0x2800, 0xa802,
+ 0x2900, 0xa006, 0x7046, 0x2001, 0x0002, 0x9080, 0x1fc8, 0x2005,
+ 0xa846, 0x0058, 0x2262, 0x6306, 0x640a, 0x00ee, 0x00ce, 0x009e,
+ 0x008e, 0x001e, 0x012e, 0x00fe, 0x0005, 0x2c00, 0x9082, 0x001b,
+ 0x0002, 0x4bc4, 0x4bc4, 0x4bc6, 0x4bc4, 0x4bc4, 0x4bc4, 0x4bca,
+ 0x4bc4, 0x4bc4, 0x4bc4, 0x4bce, 0x4bc4, 0x4bc4, 0x4bc4, 0x4bd2,
+ 0x4bc4, 0x4bc4, 0x4bc4, 0x4bd6, 0x4bc4, 0x4bc4, 0x4bc4, 0x4bda,
+ 0x4bc4, 0x4bc4, 0x4bc4, 0x4bdf, 0x080c, 0x0dfa, 0xa276, 0xa37a,
+ 0xa47e, 0x0898, 0xa286, 0xa38a, 0xa48e, 0x0878, 0xa296, 0xa39a,
+ 0xa49e, 0x0858, 0xa2a6, 0xa3aa, 0xa4ae, 0x0838, 0xa2b6, 0xa3ba,
+ 0xa4be, 0x0818, 0xa2c6, 0xa3ca, 0xa4ce, 0x0804, 0x4b9d, 0xa2d6,
+ 0xa3da, 0xa4de, 0x0804, 0x4b9d, 0x00e6, 0x2071, 0x189c, 0x7048,
+ 0x9005, 0x0904, 0x4c76, 0x0126, 0x2091, 0x8000, 0x0e04, 0x4c75,
+ 0x00f6, 0x2079, 0x0000, 0x00c6, 0x0096, 0x0086, 0x0076, 0x9006,
+ 0x2038, 0x7040, 0x2048, 0x9005, 0x0500, 0xa948, 0x2105, 0x0016,
+ 0x908a, 0x0036, 0x1a0c, 0x0dfa, 0x2060, 0x001e, 0x8108, 0x2105,
+ 0x9005, 0xa94a, 0x1904, 0x4c78, 0xa804, 0x9005, 0x090c, 0x0dfa,
+ 0x7042, 0x2938, 0x2040, 0xa003, 0x0000, 0x2001, 0x0002, 0x9080,
+ 0x1fc8, 0x2005, 0xa04a, 0x0804, 0x4c78, 0x703c, 0x2060, 0x2c14,
+ 0x6304, 0x6408, 0x650c, 0x2200, 0x7836, 0x7833, 0x0012, 0x7882,
+ 0x2300, 0x7886, 0x2400, 0x788a, 0x2091, 0x4080, 0x2001, 0x0089,
+ 0x2004, 0xd084, 0x190c, 0x11e0, 0x87ff, 0x0118, 0x2748, 0x080c,
+ 0x1063, 0x7048, 0x8001, 0x704a, 0x9005, 0x1170, 0x7040, 0x2048,
+ 0x9005, 0x0128, 0x080c, 0x1063, 0x9006, 0x7042, 0x7046, 0x703b,
+ 0x18b8, 0x703f, 0x18b8, 0x0420, 0x7040, 0x9005, 0x1508, 0x7238,
+ 0x2c00, 0x9206, 0x0148, 0x9c80, 0x0004, 0x90fa, 0x18f8, 0x0210,
+ 0x2001, 0x18b8, 0x703e, 0x00a0, 0x9006, 0x703e, 0x703a, 0x7044,
+ 0x9005, 0x090c, 0x0dfa, 0x2048, 0xa800, 0x9005, 0x1de0, 0x2900,
+ 0x7042, 0x2001, 0x0002, 0x9080, 0x1fc8, 0x2005, 0xa84a, 0x0000,
+ 0x007e, 0x008e, 0x009e, 0x00ce, 0x00fe, 0x012e, 0x00ee, 0x0005,
+ 0x2c00, 0x9082, 0x001b, 0x0002, 0x4c97, 0x4c97, 0x4c99, 0x4c97,
+ 0x4c97, 0x4c97, 0x4c9e, 0x4c97, 0x4c97, 0x4c97, 0x4ca3, 0x4c97,
+ 0x4c97, 0x4c97, 0x4ca8, 0x4c97, 0x4c97, 0x4c97, 0x4cad, 0x4c97,
+ 0x4c97, 0x4c97, 0x4cb2, 0x4c97, 0x4c97, 0x4c97, 0x4cb7, 0x080c,
+ 0x0dfa, 0xaa74, 0xab78, 0xac7c, 0x0804, 0x4c23, 0xaa84, 0xab88,
+ 0xac8c, 0x0804, 0x4c23, 0xaa94, 0xab98, 0xac9c, 0x0804, 0x4c23,
+ 0xaaa4, 0xaba8, 0xacac, 0x0804, 0x4c23, 0xaab4, 0xabb8, 0xacbc,
+ 0x0804, 0x4c23, 0xaac4, 0xabc8, 0xaccc, 0x0804, 0x4c23, 0xaad4,
+ 0xabd8, 0xacdc, 0x0804, 0x4c23, 0x0026, 0x080c, 0x55db, 0xd0c4,
+ 0x0120, 0x2011, 0x8014, 0x080c, 0x4b1f, 0x002e, 0x0005, 0x81ff,
+ 0x1904, 0x351a, 0x0126, 0x2091, 0x8000, 0x6030, 0xc08d, 0xc085,
+ 0xc0ac, 0x6032, 0x080c, 0x7207, 0x1158, 0x080c, 0x7504, 0x080c,
+ 0x5f2b, 0x9085, 0x0001, 0x080c, 0x724e, 0x080c, 0x7127, 0x0010,
+ 0x080c, 0x5dea, 0x012e, 0x0804, 0x34e8, 0x81ff, 0x0120, 0x2009,
+ 0x0001, 0x0804, 0x351a, 0x080c, 0x55ef, 0x0120, 0x2009, 0x0007,
+ 0x0804, 0x351a, 0x080c, 0x67bb, 0x0120, 0x2009, 0x0008, 0x0804,
+ 0x351a, 0x0026, 0x2011, 0x0010, 0x080c, 0x67e7, 0x002e, 0x0140,
+ 0x7984, 0x080c, 0x6831, 0x1120, 0x2009, 0x4009, 0x0804, 0x351a,
+ 0x7984, 0x080c, 0x643f, 0x1904, 0x351d, 0x080c, 0x4af2, 0x0904,
+ 0x351d, 0x2b00, 0x7026, 0x080c, 0x67c3, 0x7888, 0x1170, 0x9084,
+ 0x0005, 0x1158, 0x900e, 0x080c, 0x66bf, 0x1108, 0xc185, 0xb800,
+ 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x34e8, 0x080c, 0x4abf, 0x0904,
+ 0x351a, 0x9006, 0xa866, 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x080c,
+ 0xc061, 0x0904, 0x351a, 0x7888, 0xd094, 0x0118, 0xb8bc, 0xc08d,
+ 0xb8be, 0x7007, 0x0003, 0x701f, 0x4d87, 0x0005, 0x2061, 0x1800,
+ 0x080c, 0x55ef, 0x2009, 0x0007, 0x1560, 0x080c, 0x67bb, 0x0118,
+ 0x2009, 0x0008, 0x0430, 0xa998, 0x080c, 0x643f, 0x1530, 0x080c,
+ 0x4af0, 0x0518, 0x080c, 0x67c3, 0xa89c, 0x1168, 0x9084, 0x0005,
+ 0x1150, 0x900e, 0x080c, 0x66bf, 0x1108, 0xc185, 0xb800, 0xd0bc,
+ 0x0108, 0xc18d, 0x00d0, 0xa868, 0xc0fc, 0xa86a, 0x080c, 0xc061,
+ 0x11e0, 0xa89c, 0xd094, 0x0118, 0xb8bc, 0xc08d, 0xb8be, 0x2009,
+ 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e,
+ 0x9085, 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0xa99a,
+ 0x9006, 0x918d, 0x0001, 0x2008, 0x0005, 0x9006, 0x0005, 0xa830,
+ 0x2008, 0x918e, 0xdead, 0x1120, 0x2021, 0x4009, 0x0804, 0x34ea,
+ 0x9086, 0x0100, 0x7024, 0x2058, 0x1110, 0x0804, 0x552f, 0x900e,
+ 0x080c, 0x66bf, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d,
+ 0x0804, 0x34e8, 0x080c, 0x55ef, 0x0120, 0x2009, 0x0007, 0x0804,
+ 0x351a, 0x7f84, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x080c, 0x4abf,
+ 0x1120, 0x2009, 0x0002, 0x0804, 0x351a, 0x900e, 0x2130, 0x7126,
+ 0x7132, 0xa860, 0x20e8, 0x7036, 0xa85c, 0x9080, 0x0005, 0x702a,
+ 0x20a0, 0x080c, 0x649f, 0x1904, 0x4e25, 0x080c, 0x67c3, 0x0120,
+ 0x080c, 0x67cb, 0x1904, 0x4e25, 0x080c, 0x67bb, 0x1130, 0x080c,
+ 0x66bf, 0x1118, 0xd79c, 0x0904, 0x4e25, 0xd794, 0x1110, 0xd784,
+ 0x01a8, 0xb8b4, 0x20e0, 0xb8b8, 0x9080, 0x0006, 0x2098, 0x3400,
+ 0xd794, 0x0160, 0x20a9, 0x0008, 0x4003, 0x2098, 0x20a0, 0x3d00,
+ 0x20e0, 0x20a9, 0x0002, 0x080c, 0x48ce, 0x0048, 0x20a9, 0x0004,
+ 0x4003, 0x2098, 0x20a0, 0x3d00, 0x20e0, 0x080c, 0x48ce, 0x4104,
+ 0xd794, 0x0528, 0xb8b4, 0x20e0, 0xb8b8, 0x2060, 0x9c80, 0x0000,
+ 0x2098, 0x20a9, 0x0002, 0x4003, 0x9c80, 0x0003, 0x2098, 0x20a9,
+ 0x0001, 0x4005, 0x9c80, 0x0004, 0x2098, 0x3400, 0x20a9, 0x0002,
+ 0x4003, 0x2098, 0x20a0, 0x3d00, 0x20e0, 0x080c, 0x48c1, 0x9c80,
+ 0x0026, 0x2098, 0xb8b4, 0x20e0, 0x20a9, 0x0002, 0x4003, 0xd794,
+ 0x0110, 0x96b0, 0x000b, 0x96b0, 0x0005, 0x8108, 0x080c, 0xa062,
+ 0x0118, 0x9186, 0x0800, 0x0040, 0xd78c, 0x0120, 0x9186, 0x0800,
+ 0x0170, 0x0018, 0x9186, 0x007e, 0x0150, 0xd794, 0x0118, 0x9686,
+ 0x0020, 0x0010, 0x9686, 0x0028, 0x0150, 0x0804, 0x4dc1, 0x86ff,
+ 0x1120, 0x7124, 0x810b, 0x0804, 0x34e8, 0x7033, 0x0001, 0x7122,
+ 0x7024, 0x9600, 0x7026, 0x772e, 0x2061, 0x18b6, 0x2c44, 0xa06b,
+ 0x0000, 0xa67a, 0x7034, 0xa072, 0x7028, 0xa076, 0xa28e, 0xa392,
+ 0xa496, 0xa59a, 0x080c, 0x112e, 0x7007, 0x0002, 0x701f, 0x4e61,
+ 0x0005, 0x7030, 0x9005, 0x1180, 0x7120, 0x7028, 0x20a0, 0x772c,
+ 0x9036, 0x7034, 0x20e8, 0x2061, 0x18b6, 0x2c44, 0xa28c, 0xa390,
+ 0xa494, 0xa598, 0x0804, 0x4dc1, 0x7124, 0x810b, 0x0804, 0x34e8,
+ 0x2029, 0x007e, 0x7984, 0x7a88, 0x7b8c, 0x7c98, 0x9184, 0xff00,
+ 0x8007, 0x90e2, 0x0020, 0x0a04, 0x351d, 0x9502, 0x0a04, 0x351d,
+ 0x9184, 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x351d, 0x9502, 0x0a04,
+ 0x351d, 0x9284, 0xff00, 0x8007, 0x90e2, 0x0020, 0x0a04, 0x351d,
+ 0x9502, 0x0a04, 0x351d, 0x9284, 0x00ff, 0x90e2, 0x0020, 0x0a04,
+ 0x351d, 0x9502, 0x0a04, 0x351d, 0x9384, 0xff00, 0x8007, 0x90e2,
+ 0x0020, 0x0a04, 0x351d, 0x9502, 0x0a04, 0x351d, 0x9384, 0x00ff,
+ 0x90e2, 0x0020, 0x0a04, 0x351d, 0x9502, 0x0a04, 0x351d, 0x9484,
+ 0xff00, 0x8007, 0x90e2, 0x0020, 0x0a04, 0x351d, 0x9502, 0x0a04,
+ 0x351d, 0x9484, 0x00ff, 0x90e2, 0x0020, 0x0a04, 0x351d, 0x9502,
+ 0x0a04, 0x351d, 0x2061, 0x1961, 0x6102, 0x6206, 0x630a, 0x640e,
+ 0x0804, 0x34e8, 0x0006, 0x080c, 0x55db, 0xd0cc, 0x000e, 0x0005,
+ 0x0006, 0x080c, 0x55df, 0xd0bc, 0x000e, 0x0005, 0x6170, 0x7a84,
+ 0x6300, 0x82ff, 0x1118, 0x7986, 0x0804, 0x34e8, 0x83ff, 0x1904,
+ 0x351d, 0x2001, 0xfff0, 0x9200, 0x1a04, 0x351d, 0x2019, 0xffff,
+ 0x6074, 0x9302, 0x9200, 0x0a04, 0x351d, 0x7986, 0x6272, 0x0804,
+ 0x34e8, 0x080c, 0x55ef, 0x1904, 0x351a, 0x7c88, 0x7d84, 0x7e98,
+ 0x7f8c, 0x080c, 0x4abf, 0x0904, 0x351a, 0x900e, 0x901e, 0x7326,
+ 0x7332, 0xa860, 0x20e8, 0x7036, 0xa85c, 0x9080, 0x0003, 0x702a,
+ 0x20a0, 0x91d8, 0x1000, 0x2b5c, 0x8bff, 0x0178, 0x080c, 0x67c3,
+ 0x0118, 0x080c, 0x67cb, 0x1148, 0x20a9, 0x0001, 0xb814, 0x4004,
+ 0xb810, 0x4004, 0x4104, 0x9398, 0x0003, 0x8108, 0x9182, 0x0800,
+ 0x0120, 0x9386, 0x003c, 0x0170, 0x0c20, 0x83ff, 0x1148, 0x7224,
+ 0x900e, 0x2001, 0x0003, 0x080c, 0x84ff, 0x2208, 0x0804, 0x34e8,
+ 0x7033, 0x0001, 0x7122, 0x7024, 0x9300, 0x7026, 0x2061, 0x18b6,
+ 0x2c44, 0xa06b, 0x0000, 0xa37a, 0x7028, 0xa076, 0x7034, 0xa072,
+ 0xa48e, 0xa592, 0xa696, 0xa79a, 0x080c, 0x112e, 0x7007, 0x0002,
+ 0x701f, 0x4f53, 0x0005, 0x7030, 0x9005, 0x1178, 0x7120, 0x7028,
+ 0x20a0, 0x901e, 0x7034, 0x20e8, 0x2061, 0x18b6, 0x2c44, 0xa48c,
+ 0xa590, 0xa694, 0xa798, 0x0804, 0x4f11, 0x7224, 0x900e, 0x2001,
+ 0x0003, 0x080c, 0x84ff, 0x2208, 0x0804, 0x34e8, 0x00f6, 0x00e6,
+ 0x080c, 0x55ef, 0x2009, 0x0007, 0x1904, 0x4fe6, 0x2071, 0x189c,
+ 0x745c, 0x84ff, 0x2009, 0x000e, 0x1904, 0x4fe6, 0xac9c, 0xad98,
+ 0xaea4, 0xafa0, 0x0096, 0x080c, 0x104a, 0x2009, 0x0002, 0x0904,
+ 0x4fe6, 0x2900, 0x705e, 0x900e, 0x901e, 0x7356, 0x7362, 0xa860,
+ 0x7066, 0xa85c, 0x9080, 0x0003, 0x705a, 0x20a0, 0x91d8, 0x1000,
+ 0x2b5c, 0x8bff, 0x0178, 0x080c, 0x67c3, 0x0118, 0x080c, 0x67cb,
+ 0x1148, 0xb814, 0x20a9, 0x0001, 0x4004, 0xb810, 0x4004, 0x4104,
+ 0x9398, 0x0003, 0x8108, 0x9182, 0x0800, 0x0120, 0x9386, 0x003c,
+ 0x01e8, 0x0c20, 0x83ff, 0x11c0, 0x7254, 0x900e, 0x2001, 0x0003,
+ 0x080c, 0x84ff, 0x2208, 0x009e, 0xa897, 0x4000, 0xa99a, 0x715c,
+ 0x81ff, 0x090c, 0x0dfa, 0x2148, 0x080c, 0x1063, 0x9006, 0x705e,
+ 0x918d, 0x0001, 0x2008, 0x0418, 0x7063, 0x0001, 0x7152, 0x7054,
+ 0x9300, 0x7056, 0x2061, 0x18b7, 0x2c44, 0xa37a, 0x7058, 0xa076,
+ 0x7064, 0xa072, 0xa48e, 0xa592, 0xa696, 0xa79a, 0xa09f, 0x4ff2,
+ 0x000e, 0xa0a2, 0x080c, 0x112e, 0x9006, 0x0048, 0x009e, 0xa897,
+ 0x4005, 0xa99a, 0x900e, 0x9085, 0x0001, 0x2001, 0x0030, 0x00ee,
+ 0x00fe, 0x0005, 0x00f6, 0xa0a0, 0x904d, 0x090c, 0x0dfa, 0x00e6,
+ 0x2071, 0x189c, 0xa06c, 0x908e, 0x0100, 0x0138, 0xa87b, 0x0030,
+ 0xa883, 0x0000, 0xa897, 0x4002, 0x00d8, 0x7060, 0x9005, 0x1158,
+ 0x7150, 0x7058, 0x20a0, 0x901e, 0x7064, 0x20e8, 0xa48c, 0xa590,
+ 0xa694, 0xa798, 0x0428, 0xa87b, 0x0000, 0xa883, 0x0000, 0xa897,
+ 0x4000, 0x7254, 0x900e, 0x2001, 0x0003, 0x080c, 0x84ff, 0xaa9a,
+ 0x715c, 0x81ff, 0x090c, 0x0dfa, 0x2148, 0x080c, 0x1063, 0x705f,
+ 0x0000, 0xa0a0, 0x2048, 0x0126, 0x2091, 0x8000, 0x080c, 0x6ae9,
+ 0x012e, 0xa09f, 0x0000, 0xa0a3, 0x0000, 0x00ee, 0x00fe, 0x0005,
+ 0x91d8, 0x1000, 0x2b5c, 0x8bff, 0x0178, 0x080c, 0x67c3, 0x0118,
+ 0x080c, 0x67cb, 0x1148, 0xb814, 0x20a9, 0x0001, 0x4004, 0xb810,
+ 0x4004, 0x4104, 0x9398, 0x0003, 0x8108, 0x9182, 0x0800, 0x0120,
+ 0x9386, 0x003c, 0x0518, 0x0c20, 0x83ff, 0x11f0, 0x7154, 0x810c,
+ 0xa99a, 0xa897, 0x4000, 0x715c, 0x81ff, 0x090c, 0x0dfa, 0x2148,
+ 0x080c, 0x1063, 0x9006, 0x705e, 0x918d, 0x0001, 0x2008, 0xa0a0,
+ 0x2048, 0x0126, 0x2091, 0x8000, 0x080c, 0x6ae9, 0x012e, 0xa09f,
+ 0x0000, 0xa0a3, 0x0000, 0x0070, 0x7063, 0x0001, 0x7152, 0x7054,
+ 0x9300, 0x7056, 0xa37a, 0xa48e, 0xa592, 0xa696, 0xa79a, 0x080c,
+ 0x112e, 0x9006, 0x00ee, 0x0005, 0x0096, 0xa88c, 0x90be, 0x7000,
+ 0x0148, 0x90be, 0x7100, 0x0130, 0x90be, 0x7200, 0x0118, 0x009e,
+ 0x0804, 0x351d, 0xa884, 0xa988, 0x080c, 0x276e, 0x1518, 0x080c,
+ 0x643f, 0x1500, 0x7126, 0xbe12, 0xbd16, 0xae7c, 0x080c, 0x4abf,
+ 0x01c8, 0x080c, 0x4abf, 0x01b0, 0x009e, 0xa867, 0x0000, 0xa868,
+ 0xc0fd, 0xa86a, 0xa823, 0x0000, 0xa804, 0x2048, 0x080c, 0xbfe3,
+ 0x1120, 0x2009, 0x0003, 0x0804, 0x351a, 0x7007, 0x0003, 0x701f,
+ 0x50bf, 0x0005, 0x009e, 0x2009, 0x0002, 0x0804, 0x351a, 0x7124,
+ 0x080c, 0x3286, 0xa820, 0x9086, 0x8001, 0x1120, 0x2009, 0x0004,
+ 0x0804, 0x351a, 0x2900, 0x7022, 0xa804, 0x0096, 0x2048, 0x8906,
+ 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x009e, 0x9080,
+ 0x0002, 0x0076, 0x0006, 0x2098, 0x20a0, 0x27e0, 0x27e8, 0x20a9,
+ 0x002a, 0x080c, 0x0fae, 0xaa6c, 0xab70, 0xac74, 0xad78, 0x2061,
+ 0x18b6, 0x2c44, 0xa06b, 0x0000, 0xae64, 0xaf8c, 0x97c6, 0x7000,
+ 0x0118, 0x97c6, 0x7100, 0x1148, 0x96c2, 0x0004, 0x0600, 0x2009,
+ 0x0004, 0x000e, 0x007e, 0x0804, 0x4b0b, 0x97c6, 0x7200, 0x11b8,
+ 0x96c2, 0x0054, 0x02a0, 0x000e, 0x007e, 0x2061, 0x18b6, 0x2c44,
+ 0xa076, 0xa772, 0xa07b, 0x002a, 0xa28e, 0xa392, 0xa496, 0xa59a,
+ 0x080c, 0x112e, 0x7007, 0x0002, 0x701f, 0x511b, 0x0005, 0x000e,
+ 0x007e, 0x0804, 0x351d, 0x7020, 0x2048, 0xa804, 0x2048, 0xa804,
+ 0x2048, 0x8906, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0,
+ 0x9080, 0x0002, 0x2098, 0x20a0, 0x27e0, 0x27e8, 0x20a9, 0x002a,
+ 0x080c, 0x0fae, 0x2100, 0x2238, 0x2061, 0x18b6, 0x2c44, 0xa28c,
+ 0xa390, 0xa494, 0xa598, 0x2009, 0x002a, 0x0804, 0x4b0b, 0x81ff,
+ 0x1904, 0x351a, 0x798c, 0x2001, 0x1956, 0x918c, 0x8000, 0x2102,
+ 0x080c, 0x4ad6, 0x0904, 0x351d, 0x080c, 0x67c3, 0x0120, 0x080c,
+ 0x67cb, 0x1904, 0x351d, 0x080c, 0x6566, 0x0904, 0x351a, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x662c, 0x012e, 0x0904, 0x351a, 0x2001,
+ 0x1956, 0x2004, 0xd0fc, 0x1904, 0x34e8, 0x0804, 0x4533, 0xa9a0,
+ 0x2001, 0x1956, 0x918c, 0x8000, 0xc18d, 0x2102, 0x080c, 0x4ae3,
+ 0x01a0, 0x080c, 0x67c3, 0x0118, 0x080c, 0x67cb, 0x1170, 0x080c,
+ 0x6566, 0x2009, 0x0002, 0x0128, 0x080c, 0x662c, 0x1170, 0x2009,
+ 0x0003, 0xa897, 0x4005, 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e,
+ 0x9085, 0x0001, 0x2001, 0x0030, 0x0005, 0xa897, 0x4000, 0x2001,
+ 0x1956, 0x2004, 0xd0fc, 0x1128, 0x080c, 0x55e3, 0x0110, 0x9006,
+ 0x0018, 0x900e, 0x9085, 0x0001, 0x2001, 0x0000, 0x0005, 0x78a8,
+ 0xd08c, 0x1118, 0xd084, 0x0904, 0x44a8, 0x080c, 0x4af2, 0x0904,
+ 0x351d, 0x080c, 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804, 0x351a,
+ 0x080c, 0x67c3, 0x0130, 0x908e, 0x0004, 0x0118, 0x908e, 0x0005,
+ 0x15a0, 0x78a8, 0xd08c, 0x0120, 0xb800, 0xc08c, 0xb802, 0x0028,
+ 0x080c, 0x55db, 0xd0b4, 0x0904, 0x44e2, 0x7884, 0x908e, 0x007e,
+ 0x0904, 0x44e2, 0x908e, 0x007f, 0x0904, 0x44e2, 0x908e, 0x0080,
+ 0x0904, 0x44e2, 0xb800, 0xd08c, 0x1904, 0x44e2, 0xa867, 0x0000,
+ 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xc002, 0x1120, 0x2009, 0x0003,
+ 0x0804, 0x351a, 0x7007, 0x0003, 0x701f, 0x51e7, 0x0005, 0x080c,
+ 0x4af2, 0x0904, 0x351d, 0x0804, 0x44e2, 0x080c, 0x32df, 0x0108,
+ 0x0005, 0x2009, 0x1833, 0x210c, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x351a, 0x080c, 0x55ef, 0x0120, 0x2009, 0x0007, 0x0804,
+ 0x351a, 0x080c, 0x67bb, 0x0120, 0x2009, 0x0008, 0x0804, 0x351a,
+ 0xb89c, 0xd0a4, 0x1118, 0xd0ac, 0x1904, 0x44e2, 0x9006, 0xa866,
+ 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xc061, 0x1120, 0x2009,
+ 0x0003, 0x0804, 0x351a, 0x7007, 0x0003, 0x701f, 0x5220, 0x0005,
+ 0xa830, 0x9086, 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x552f,
+ 0x080c, 0x4af2, 0x0904, 0x351d, 0x0804, 0x51b9, 0x81ff, 0x2009,
+ 0x0001, 0x1904, 0x351a, 0x080c, 0x55ef, 0x2009, 0x0007, 0x1904,
+ 0x351a, 0x080c, 0x67bb, 0x0120, 0x2009, 0x0008, 0x0804, 0x351a,
+ 0x080c, 0x4af2, 0x0904, 0x351d, 0x080c, 0x67c3, 0x2009, 0x0009,
+ 0x1904, 0x351a, 0x080c, 0x4abf, 0x2009, 0x0002, 0x0904, 0x351a,
+ 0x9006, 0xa866, 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x7988, 0x9194,
+ 0xff00, 0x918c, 0x00ff, 0x9006, 0x82ff, 0x1128, 0xc0ed, 0xa952,
+ 0x798c, 0xa956, 0x0038, 0x928e, 0x0100, 0x1904, 0x351d, 0xc0e5,
+ 0xa952, 0xa956, 0xa83e, 0x080c, 0xc2b4, 0x2009, 0x0003, 0x0904,
+ 0x351a, 0x7007, 0x0003, 0x701f, 0x5276, 0x0005, 0xa830, 0x9086,
+ 0x0100, 0x2009, 0x0004, 0x0904, 0x351a, 0x0804, 0x34e8, 0x7aa8,
+ 0x9284, 0xc000, 0x0148, 0xd2ec, 0x01a0, 0x080c, 0x55ef, 0x1188,
+ 0x2009, 0x0014, 0x0804, 0x351a, 0xd2dc, 0x1578, 0x81ff, 0x2009,
+ 0x0001, 0x1904, 0x351a, 0x080c, 0x55ef, 0x2009, 0x0007, 0x1904,
+ 0x351a, 0xd2f4, 0x0138, 0x9284, 0x5000, 0xc0d5, 0x080c, 0x55b5,
+ 0x0804, 0x34e8, 0xd2fc, 0x0160, 0x080c, 0x4af2, 0x0904, 0x351d,
+ 0x7984, 0x9284, 0x9000, 0xc0d5, 0x080c, 0x558a, 0x0804, 0x34e8,
+ 0x080c, 0x4af2, 0x0904, 0x351d, 0xb804, 0x9084, 0x00ff, 0x9086,
+ 0x0006, 0x2009, 0x0009, 0x1904, 0x5365, 0x080c, 0x4abf, 0x2009,
+ 0x0002, 0x0904, 0x5365, 0xa85c, 0x9080, 0x001b, 0xaf60, 0x2009,
+ 0x0008, 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0x080c, 0x4b08, 0x701f,
+ 0x52d2, 0x0005, 0xa86c, 0x9086, 0x0500, 0x1138, 0xa870, 0x9005,
+ 0x1120, 0xa874, 0x9084, 0xff00, 0x0110, 0x1904, 0x351d, 0xa866,
+ 0xa832, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0x4af2, 0x1110, 0x0804,
+ 0x351d, 0x2009, 0x0043, 0x080c, 0xc31c, 0x2009, 0x0003, 0x0904,
+ 0x5365, 0x7007, 0x0003, 0x701f, 0x52f6, 0x0005, 0xa830, 0x9086,
+ 0x0100, 0x2009, 0x0004, 0x0904, 0x5365, 0x7984, 0x7aa8, 0x9284,
+ 0x1000, 0xe085, 0x080c, 0x558a, 0x0804, 0x34e8, 0x00c6, 0xaab0,
+ 0x9284, 0xc000, 0x0148, 0xd2ec, 0x0170, 0x080c, 0x55ef, 0x1158,
+ 0x2009, 0x0014, 0x0804, 0x5354, 0x2061, 0x1800, 0x080c, 0x55ef,
+ 0x2009, 0x0007, 0x15c8, 0xd2f4, 0x0130, 0x9284, 0x5000, 0xc0d5,
+ 0x080c, 0x55b5, 0x0058, 0xd2fc, 0x0180, 0x080c, 0x4af0, 0x0590,
+ 0xa998, 0x9284, 0x9000, 0xc0d5, 0x080c, 0x558a, 0xa87b, 0x0000,
+ 0xa883, 0x0000, 0xa897, 0x4000, 0x0438, 0x080c, 0x4af0, 0x0510,
+ 0x080c, 0x67c3, 0x2009, 0x0009, 0x11b8, 0xa8c4, 0x9086, 0x0500,
+ 0x11c8, 0xa8c8, 0x9005, 0x11b0, 0xa8cc, 0x9084, 0xff00, 0x1190,
+ 0x080c, 0x4af0, 0x1108, 0x0070, 0x2009, 0x004b, 0x080c, 0xc31c,
+ 0x2009, 0x0003, 0x0108, 0x0078, 0x0431, 0x19c0, 0xa897, 0x4005,
+ 0xa99a, 0x0010, 0xa897, 0x4006, 0x900e, 0x9085, 0x0001, 0x2001,
+ 0x0030, 0x00ce, 0x0005, 0x9006, 0x0ce0, 0x7aa8, 0xd2dc, 0x0904,
+ 0x351a, 0x0016, 0x7984, 0x9284, 0x1000, 0xc0fd, 0x080c, 0x558a,
+ 0x001e, 0x1904, 0x351a, 0x0804, 0x34e8, 0x00f6, 0x2d78, 0xaab0,
+ 0x0021, 0x00fe, 0x0005, 0xaab0, 0xc2d5, 0xd2dc, 0x0150, 0x0016,
+ 0xa998, 0x9284, 0x1400, 0xc0fd, 0x080c, 0x558a, 0x001e, 0x9085,
+ 0x0001, 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x351a,
+ 0x080c, 0x55ef, 0x0120, 0x2009, 0x0007, 0x0804, 0x351a, 0x7984,
+ 0x7ea8, 0x96b4, 0x00ff, 0x080c, 0x649f, 0x1904, 0x351d, 0x9186,
+ 0x007f, 0x0138, 0x080c, 0x67c3, 0x0120, 0x2009, 0x0009, 0x0804,
+ 0x351a, 0x080c, 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804, 0x351a,
+ 0xa867, 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x2001, 0x0100, 0x8007,
+ 0xa80a, 0x080c, 0xc01c, 0x1120, 0x2009, 0x0003, 0x0804, 0x351a,
+ 0x7007, 0x0003, 0x701f, 0x53c5, 0x0005, 0xa808, 0x8007, 0x9086,
+ 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x351a, 0xa8e0, 0xa866,
+ 0xa810, 0x8007, 0x9084, 0x00ff, 0x800c, 0xa814, 0x8007, 0x9084,
+ 0x00ff, 0x8004, 0x9080, 0x0002, 0x9108, 0x8906, 0x8006, 0x8007,
+ 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0004, 0x7a8c, 0x7b88,
+ 0x7c9c, 0x7d98, 0x0804, 0x4b0b, 0x080c, 0x4abf, 0x1120, 0x2009,
+ 0x0002, 0x0804, 0x351a, 0x7984, 0x9194, 0xff00, 0x918c, 0x00ff,
+ 0x8217, 0x82ff, 0x1118, 0x7023, 0x198b, 0x0040, 0x92c6, 0x0001,
+ 0x1118, 0x7023, 0x19a5, 0x0010, 0x0804, 0x351d, 0x2009, 0x001a,
+ 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080, 0x0019, 0xaf60,
+ 0x080c, 0x4b08, 0x701f, 0x5415, 0x0005, 0x2001, 0x182d, 0x2003,
+ 0x0001, 0xa85c, 0x9080, 0x0019, 0x2098, 0xa860, 0x20e0, 0x20a9,
+ 0x001a, 0x7020, 0x20a0, 0x20e9, 0x0001, 0x4003, 0x0804, 0x34e8,
+ 0x080c, 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804, 0x351a, 0x7984,
+ 0x9194, 0xff00, 0x918c, 0x00ff, 0x8217, 0x82ff, 0x1118, 0x2099,
+ 0x198b, 0x0040, 0x92c6, 0x0001, 0x1118, 0x2099, 0x19a5, 0x0010,
+ 0x0804, 0x351d, 0xa85c, 0x9080, 0x0019, 0x20a0, 0xa860, 0x20e8,
+ 0x20a9, 0x001a, 0x20e1, 0x0001, 0x4003, 0x2009, 0x001a, 0x7a8c,
+ 0x7b88, 0x7c9c, 0x7d98, 0xa85c, 0x9080, 0x0019, 0xaf60, 0x0804,
+ 0x4b0b, 0x7884, 0x908a, 0x1000, 0x1a04, 0x351d, 0x0126, 0x2091,
+ 0x8000, 0x8003, 0x800b, 0x810b, 0x9108, 0x00c6, 0x2061, 0x19d2,
+ 0x6142, 0x00ce, 0x012e, 0x0804, 0x34e8, 0x00c6, 0x080c, 0x7207,
+ 0x1160, 0x080c, 0x7504, 0x080c, 0x5f2b, 0x9085, 0x0001, 0x080c,
+ 0x724e, 0x080c, 0x7127, 0x080c, 0x0dfa, 0x2061, 0x1800, 0x6030,
+ 0xc09d, 0x6032, 0x080c, 0x5dea, 0x00ce, 0x0005, 0x00c6, 0x2001,
+ 0x1800, 0x2004, 0x908e, 0x0000, 0x0904, 0x351a, 0x7884, 0x9005,
+ 0x0188, 0x7888, 0x2061, 0x1974, 0x2c0c, 0x2062, 0x080c, 0x2b98,
+ 0x01a0, 0x080c, 0x2ba0, 0x0188, 0x080c, 0x2ba8, 0x0170, 0x2162,
+ 0x0804, 0x351d, 0x2061, 0x0100, 0x6038, 0x9086, 0x0007, 0x1118,
+ 0x2009, 0x0001, 0x0010, 0x2009, 0x0000, 0x7884, 0x9086, 0x0002,
+ 0x1568, 0x2061, 0x0100, 0x6028, 0xc09c, 0x602a, 0x0026, 0x2011,
+ 0x0003, 0x080c, 0x9a0f, 0x2011, 0x0002, 0x080c, 0x9a19, 0x002e,
+ 0x080c, 0x9927, 0x0036, 0x901e, 0x080c, 0x999d, 0x003e, 0x60e3,
+ 0x0000, 0x080c, 0xdc13, 0x080c, 0xdc2e, 0x9085, 0x0001, 0x080c,
+ 0x724e, 0x9006, 0x080c, 0x2c88, 0x2001, 0x1800, 0x2003, 0x0004,
+ 0x2001, 0x197f, 0x2003, 0x0000, 0x6027, 0x0008, 0x00ce, 0x0804,
+ 0x34e8, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804, 0x351a, 0x080c,
+ 0x55ef, 0x0120, 0x2009, 0x0007, 0x0804, 0x351a, 0x7984, 0x7ea8,
+ 0x96b4, 0x00ff, 0x080c, 0x649f, 0x1904, 0x351d, 0x9186, 0x007f,
+ 0x0138, 0x080c, 0x67c3, 0x0120, 0x2009, 0x0009, 0x0804, 0x351a,
+ 0x080c, 0x4abf, 0x1120, 0x2009, 0x0002, 0x0804, 0x351a, 0xa867,
+ 0x0000, 0xa868, 0xc0fd, 0xa86a, 0x080c, 0xc01f, 0x1120, 0x2009,
+ 0x0003, 0x0804, 0x351a, 0x7007, 0x0003, 0x701f, 0x5518, 0x0005,
+ 0xa830, 0x9086, 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x351a,
+ 0xa8e0, 0xa866, 0xa834, 0x8007, 0x800c, 0xa85c, 0x9080, 0x000c,
+ 0x7a8c, 0x7b88, 0x7c9c, 0x7d98, 0xaf60, 0x0804, 0x4b0b, 0xa898,
+ 0x9086, 0x000d, 0x1904, 0x351a, 0x2021, 0x4005, 0x0126, 0x2091,
+ 0x8000, 0x0e04, 0x553c, 0x0010, 0x012e, 0x0cc0, 0x7c36, 0x9486,
+ 0x4000, 0x0118, 0x7833, 0x0011, 0x0010, 0x7833, 0x0010, 0x7883,
+ 0x4005, 0xa998, 0x7986, 0xa9a4, 0x799a, 0xa9a8, 0x799e, 0x080c,
+ 0x4afb, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c,
+ 0x11e0, 0x7007, 0x0001, 0x2091, 0x5000, 0x700f, 0x0000, 0x012e,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x00c6, 0x2061, 0x19d2, 0x7984,
+ 0x6152, 0x614e, 0x6057, 0x0000, 0x604b, 0x0009, 0x7898, 0x606a,
+ 0x789c, 0x6066, 0x7888, 0x6062, 0x788c, 0x605e, 0x2001, 0x19e0,
+ 0x2044, 0x2001, 0x19e7, 0xa076, 0xa060, 0xa072, 0xa07b, 0x0001,
+ 0xa07f, 0x0002, 0xa06b, 0x0000, 0xa09f, 0x0000, 0x00ce, 0x012e,
+ 0x0804, 0x34e8, 0x0126, 0x2091, 0x8000, 0x00b6, 0x00c6, 0x90e4,
+ 0xc000, 0x0168, 0x0006, 0xd0d4, 0x0130, 0x0036, 0x2019, 0x0029,
+ 0x080c, 0x32a4, 0x003e, 0x080c, 0xbe84, 0x000e, 0x1198, 0xd0e4,
+ 0x0160, 0x9180, 0x1000, 0x2004, 0x905d, 0x0160, 0x080c, 0x5f45,
+ 0x080c, 0xa062, 0x0110, 0xb817, 0x0000, 0x9006, 0x00ce, 0x00be,
+ 0x012e, 0x0005, 0x9085, 0x0001, 0x0cc8, 0x0126, 0x2091, 0x8000,
+ 0x0156, 0x2010, 0x900e, 0x20a9, 0x0800, 0x0016, 0x9180, 0x1000,
+ 0x2004, 0x9005, 0x0188, 0x9186, 0x007e, 0x0170, 0x9186, 0x007f,
+ 0x0158, 0x9186, 0x0080, 0x0140, 0x9186, 0x00ff, 0x0128, 0x0026,
+ 0x2200, 0x080c, 0x558a, 0x002e, 0x001e, 0x8108, 0x1f04, 0x55bd,
+ 0x015e, 0x012e, 0x0005, 0x2001, 0x185c, 0x2004, 0x0005, 0x2001,
+ 0x187b, 0x2004, 0x0005, 0x0006, 0x2001, 0x1810, 0x2004, 0xd0d4,
+ 0x000e, 0x0005, 0x2001, 0x180e, 0x2004, 0xd0b4, 0x0005, 0x2001,
+ 0x1800, 0x2004, 0x9086, 0x0003, 0x0005, 0x0016, 0x00e6, 0x2071,
+ 0x189c, 0x7108, 0x910d, 0x710a, 0x00ee, 0x001e, 0x0005, 0x79a4,
+ 0x9182, 0x0081, 0x1a04, 0x351d, 0x810c, 0x0016, 0x080c, 0x4abf,
+ 0x0170, 0x080c, 0x0f39, 0x2100, 0x2238, 0x7d84, 0x7c88, 0x7b8c,
+ 0x7a90, 0x001e, 0x080c, 0x4b08, 0x701f, 0x561b, 0x0005, 0x2009,
+ 0x0002, 0x0804, 0x351a, 0x2079, 0x0000, 0x7d94, 0x7c98, 0x7ba8,
+ 0x7aac, 0x79a4, 0x810c, 0x2061, 0x18b6, 0x2c44, 0xa770, 0xa074,
+ 0x2071, 0x189c, 0x080c, 0x4b0b, 0x701f, 0x562f, 0x0005, 0x2061,
+ 0x18b6, 0x2c44, 0x0016, 0x0026, 0xa270, 0xa174, 0x080c, 0x0f41,
+ 0x002e, 0x001e, 0x080c, 0x0fee, 0x9006, 0xa802, 0xa806, 0x0804,
+ 0x34e8, 0x0126, 0x0156, 0x0136, 0x0146, 0x01c6, 0x01d6, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x2061, 0x0100, 0x2069, 0x0200, 0x2071,
+ 0x1800, 0x6044, 0xd0a4, 0x11e8, 0xd084, 0x0118, 0x080c, 0x57ea,
+ 0x0068, 0xd08c, 0x0118, 0x080c, 0x56f3, 0x0040, 0xd094, 0x0118,
+ 0x080c, 0x56c3, 0x0018, 0xd09c, 0x0108, 0x0099, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x01de, 0x01ce, 0x014e, 0x013e, 0x015e, 0x012e,
+ 0x0005, 0x0016, 0x6128, 0xd19c, 0x1110, 0xc19d, 0x612a, 0x001e,
+ 0x0c68, 0x0006, 0x7094, 0x9005, 0x000e, 0x0120, 0x7097, 0x0000,
+ 0x708f, 0x0000, 0x624c, 0x9286, 0xf0f0, 0x1150, 0x6048, 0x9086,
+ 0xf0f0, 0x0130, 0x624a, 0x6043, 0x0090, 0x6043, 0x0010, 0x0490,
+ 0x9294, 0xff00, 0x9296, 0xf700, 0x0178, 0x7138, 0xd1a4, 0x1160,
+ 0x6240, 0x9295, 0x0100, 0x6242, 0x9294, 0x0010, 0x0128, 0x2009,
+ 0x00f7, 0x080c, 0x5ea7, 0x00f0, 0x6040, 0x9084, 0x0010, 0x9085,
+ 0x0140, 0x6042, 0x6043, 0x0000, 0x7083, 0x0000, 0x709f, 0x0001,
+ 0x70c3, 0x0000, 0x70db, 0x0000, 0x2009, 0x1c80, 0x200b, 0x0000,
+ 0x7093, 0x0000, 0x7087, 0x000f, 0x2009, 0x000f, 0x2011, 0x5d8d,
+ 0x080c, 0x836c, 0x0005, 0x2001, 0x187d, 0x2004, 0xd08c, 0x0110,
+ 0x705b, 0xffff, 0x7084, 0x9005, 0x1528, 0x2011, 0x5d8d, 0x080c,
+ 0x82da, 0x6040, 0x9094, 0x0010, 0x9285, 0x0020, 0x6042, 0x20a9,
+ 0x00c8, 0x6044, 0xd08c, 0x1168, 0x1f04, 0x56d9, 0x6242, 0x7097,
+ 0x0000, 0x6040, 0x9094, 0x0010, 0x9285, 0x0080, 0x6042, 0x6242,
+ 0x0048, 0x6242, 0x7097, 0x0000, 0x708b, 0x0000, 0x9006, 0x080c,
+ 0x5f30, 0x0000, 0x0005, 0x7088, 0x908a, 0x0003, 0x1a0c, 0x0dfa,
+ 0x000b, 0x0005, 0x56fd, 0x574e, 0x57e9, 0x00f6, 0x0016, 0x6900,
+ 0x918c, 0x0800, 0x708b, 0x0001, 0x2001, 0x015d, 0x2003, 0x0000,
+ 0x6803, 0x00fc, 0x20a9, 0x0004, 0x6800, 0x9084, 0x00fc, 0x0120,
+ 0x1f04, 0x570c, 0x080c, 0x0dfa, 0x68a0, 0x68a2, 0x689c, 0x689e,
+ 0x6898, 0x689a, 0xa001, 0x918d, 0x1600, 0x6902, 0x001e, 0x6837,
+ 0x0020, 0x080c, 0x5f0c, 0x2079, 0x1c00, 0x7833, 0x1101, 0x7837,
+ 0x0000, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9, 0x0001, 0x20a1,
+ 0x1c0e, 0x20a9, 0x0004, 0x4003, 0x080c, 0x9eeb, 0x20e1, 0x0001,
+ 0x2099, 0x1c00, 0x20e9, 0x0000, 0x20a1, 0x0240, 0x20a9, 0x0014,
+ 0x4003, 0x60c3, 0x000c, 0x600f, 0x0000, 0x080c, 0x5dbe, 0x00fe,
+ 0x9006, 0x708e, 0x6043, 0x0008, 0x6042, 0x0005, 0x00f6, 0x708c,
+ 0x708f, 0x0000, 0x9025, 0x0904, 0x57c6, 0x6020, 0xd0b4, 0x1904,
+ 0x57c4, 0x719c, 0x81ff, 0x0904, 0x57b2, 0x9486, 0x000c, 0x1904,
+ 0x57bf, 0x9480, 0x0018, 0x8004, 0x20a8, 0x080c, 0x5f05, 0x2011,
+ 0x0260, 0x2019, 0x1c00, 0x220c, 0x2304, 0x9106, 0x11e8, 0x8210,
+ 0x8318, 0x1f04, 0x576b, 0x6043, 0x0004, 0x2061, 0x0140, 0x605b,
+ 0xbc94, 0x605f, 0xf0f0, 0x2061, 0x0100, 0x6043, 0x0006, 0x708b,
+ 0x0002, 0x7097, 0x0002, 0x2009, 0x07d0, 0x2011, 0x5d94, 0x080c,
+ 0x836c, 0x080c, 0x5f0c, 0x04c0, 0x080c, 0x5f05, 0x2079, 0x0260,
+ 0x7930, 0x918e, 0x1101, 0x1558, 0x7834, 0x9005, 0x1540, 0x7900,
+ 0x918c, 0x00ff, 0x1118, 0x7804, 0x9005, 0x0190, 0x080c, 0x5f05,
+ 0x2011, 0x026e, 0x2019, 0x1805, 0x20a9, 0x0004, 0x220c, 0x2304,
+ 0x9102, 0x0230, 0x11a0, 0x8210, 0x8318, 0x1f04, 0x57a6, 0x0078,
+ 0x709f, 0x0000, 0x080c, 0x5f05, 0x20e1, 0x0000, 0x2099, 0x0260,
+ 0x20e9, 0x0001, 0x20a1, 0x1c00, 0x20a9, 0x0014, 0x4003, 0x6043,
+ 0x0008, 0x6043, 0x0000, 0x0010, 0x00fe, 0x0005, 0x6040, 0x9085,
+ 0x0100, 0x6042, 0x6020, 0xd0b4, 0x1db8, 0x080c, 0x9eeb, 0x20e1,
+ 0x0001, 0x2099, 0x1c00, 0x20e9, 0x0000, 0x20a1, 0x0240, 0x20a9,
+ 0x0014, 0x4003, 0x60c3, 0x000c, 0x2011, 0x19c9, 0x2013, 0x0000,
+ 0x708f, 0x0000, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0x964d,
+ 0x08d8, 0x0005, 0x7094, 0x908a, 0x001d, 0x1a0c, 0x0dfa, 0x000b,
+ 0x0005, 0x581b, 0x582e, 0x5857, 0x5877, 0x589d, 0x58cc, 0x58f2,
+ 0x592a, 0x5950, 0x597e, 0x59b9, 0x59f1, 0x5a0f, 0x5a3a, 0x5a5c,
+ 0x5a77, 0x5a81, 0x5ab5, 0x5adb, 0x5b0a, 0x5b30, 0x5b68, 0x5bac,
+ 0x5be9, 0x5c0a, 0x5c63, 0x5c85, 0x5cb3, 0x5cb3, 0x00c6, 0x2061,
+ 0x1800, 0x6003, 0x0007, 0x2061, 0x0100, 0x6004, 0x9084, 0xfff9,
+ 0x6006, 0x00ce, 0x0005, 0x2061, 0x0140, 0x605b, 0xbc94, 0x605f,
+ 0xf0f0, 0x2061, 0x0100, 0x6043, 0x0002, 0x7097, 0x0001, 0x2009,
+ 0x07d0, 0x2011, 0x5d94, 0x080c, 0x836c, 0x0005, 0x00f6, 0x708c,
+ 0x9086, 0x0014, 0x1510, 0x6042, 0x6020, 0xd0b4, 0x11f0, 0x080c,
+ 0x5f05, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1102, 0x11a0, 0x7834,
+ 0x9005, 0x1188, 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005, 0x1110,
+ 0x70c3, 0x0001, 0x2011, 0x5d94, 0x080c, 0x82da, 0x7097, 0x0010,
+ 0x080c, 0x5a81, 0x0010, 0x708f, 0x0000, 0x00fe, 0x0005, 0x00f6,
+ 0x7097, 0x0003, 0x6043, 0x0004, 0x2011, 0x5d94, 0x080c, 0x82da,
+ 0x080c, 0x5e89, 0x2079, 0x0240, 0x7833, 0x1102, 0x7837, 0x0000,
+ 0x20a9, 0x0008, 0x9f88, 0x000e, 0x200b, 0x0000, 0x8108, 0x1f04,
+ 0x586c, 0x60c3, 0x0014, 0x080c, 0x5dbe, 0x00fe, 0x0005, 0x00f6,
+ 0x708c, 0x9005, 0x0500, 0x2011, 0x5d94, 0x080c, 0x82da, 0x9086,
+ 0x0014, 0x11b8, 0x080c, 0x5f05, 0x2079, 0x0260, 0x7a30, 0x9296,
+ 0x1102, 0x1178, 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128,
+ 0x70c0, 0x9005, 0x1110, 0x70c3, 0x0001, 0x7097, 0x0004, 0x0029,
+ 0x0010, 0x080c, 0x5ee1, 0x00fe, 0x0005, 0x00f6, 0x7097, 0x0005,
+ 0x080c, 0x5e89, 0x2079, 0x0240, 0x7833, 0x1103, 0x7837, 0x0000,
+ 0x080c, 0x5f05, 0x080c, 0x5ee8, 0x1170, 0x7080, 0x9005, 0x1158,
+ 0x7158, 0x9186, 0xffff, 0x0138, 0x2011, 0x0008, 0x080c, 0x5d41,
+ 0x0168, 0x080c, 0x5ebe, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099,
+ 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014,
+ 0x080c, 0x5dbe, 0x00fe, 0x0005, 0x00f6, 0x708c, 0x9005, 0x0500,
+ 0x2011, 0x5d94, 0x080c, 0x82da, 0x9086, 0x0014, 0x11b8, 0x080c,
+ 0x5f05, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1103, 0x1178, 0x7834,
+ 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005, 0x1110,
+ 0x70c3, 0x0001, 0x7097, 0x0006, 0x0029, 0x0010, 0x080c, 0x5ee1,
+ 0x00fe, 0x0005, 0x00f6, 0x7097, 0x0007, 0x080c, 0x5e89, 0x2079,
+ 0x0240, 0x7833, 0x1104, 0x7837, 0x0000, 0x080c, 0x5f05, 0x080c,
+ 0x5ee8, 0x11b8, 0x7080, 0x9005, 0x11a0, 0x7160, 0x9186, 0xffff,
+ 0x0180, 0x9180, 0x32e9, 0x200d, 0x918c, 0xff00, 0x810f, 0x2011,
+ 0x0008, 0x080c, 0x5d41, 0x0180, 0x080c, 0x4ed8, 0x0110, 0x080c,
+ 0x27d7, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9,
+ 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c, 0x5dbe,
+ 0x00fe, 0x0005, 0x00f6, 0x708c, 0x9005, 0x0500, 0x2011, 0x5d94,
+ 0x080c, 0x82da, 0x9086, 0x0014, 0x11b8, 0x080c, 0x5f05, 0x2079,
+ 0x0260, 0x7a30, 0x9296, 0x1104, 0x1178, 0x7834, 0x9005, 0x1160,
+ 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005, 0x1110, 0x70c3, 0x0001,
+ 0x7097, 0x0008, 0x0029, 0x0010, 0x080c, 0x5ee1, 0x00fe, 0x0005,
+ 0x00f6, 0x7097, 0x0009, 0x080c, 0x5e89, 0x2079, 0x0240, 0x7833,
+ 0x1105, 0x7837, 0x0100, 0x080c, 0x5ee8, 0x1150, 0x7080, 0x9005,
+ 0x1138, 0x080c, 0x5cb4, 0x1188, 0x9085, 0x0001, 0x080c, 0x27d7,
+ 0x20a9, 0x0008, 0x080c, 0x5f05, 0x20e1, 0x0000, 0x2099, 0x026e,
+ 0x20e9, 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c,
+ 0x5dbe, 0x0010, 0x080c, 0x580e, 0x00fe, 0x0005, 0x00f6, 0x708c,
+ 0x9005, 0x05a8, 0x2011, 0x5d94, 0x080c, 0x82da, 0x9086, 0x0014,
+ 0x1560, 0x080c, 0x5f05, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1105,
+ 0x1520, 0x7834, 0x9084, 0x0100, 0x2011, 0x0100, 0x921e, 0x1160,
+ 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005, 0x1110, 0x70c3, 0x0001,
+ 0x7097, 0x000a, 0x00b1, 0x0098, 0x9005, 0x1178, 0x7a38, 0xd2fc,
+ 0x0128, 0x70c0, 0x9005, 0x1110, 0x70c3, 0x0001, 0x7093, 0x0000,
+ 0x7097, 0x000e, 0x080c, 0x5a5c, 0x0010, 0x080c, 0x5ee1, 0x00fe,
+ 0x0005, 0x00f6, 0x7097, 0x000b, 0x2011, 0x1c0e, 0x20e9, 0x0001,
+ 0x22a0, 0x20a9, 0x0040, 0x2019, 0xffff, 0x4304, 0x080c, 0x5e89,
+ 0x2079, 0x0240, 0x7833, 0x1106, 0x7837, 0x0000, 0x080c, 0x5ee8,
+ 0x0118, 0x2013, 0x0000, 0x0020, 0x705c, 0x9085, 0x0100, 0x2012,
+ 0x20a9, 0x0040, 0x2009, 0x024e, 0x2011, 0x1c0e, 0x220e, 0x8210,
+ 0x8108, 0x9186, 0x0260, 0x1128, 0x6810, 0x8000, 0x6812, 0x2009,
+ 0x0240, 0x1f04, 0x59de, 0x60c3, 0x0084, 0x080c, 0x5dbe, 0x00fe,
+ 0x0005, 0x00f6, 0x708c, 0x9005, 0x01c0, 0x2011, 0x5d94, 0x080c,
+ 0x82da, 0x9086, 0x0084, 0x1178, 0x080c, 0x5f05, 0x2079, 0x0260,
+ 0x7a30, 0x9296, 0x1106, 0x1138, 0x7834, 0x9005, 0x1120, 0x7097,
+ 0x000c, 0x0029, 0x0010, 0x080c, 0x5ee1, 0x00fe, 0x0005, 0x00f6,
+ 0x7097, 0x000d, 0x080c, 0x5e89, 0x2079, 0x0240, 0x7833, 0x1107,
+ 0x7837, 0x0000, 0x080c, 0x5f05, 0x20a9, 0x0040, 0x2011, 0x026e,
+ 0x2009, 0x024e, 0x220e, 0x8210, 0x8108, 0x9186, 0x0260, 0x1150,
+ 0x6810, 0x8000, 0x6812, 0x2009, 0x0240, 0x6814, 0x8000, 0x6816,
+ 0x2011, 0x0260, 0x1f04, 0x5a22, 0x60c3, 0x0084, 0x080c, 0x5dbe,
+ 0x00fe, 0x0005, 0x00f6, 0x708c, 0x9005, 0x01e0, 0x2011, 0x5d94,
+ 0x080c, 0x82da, 0x9086, 0x0084, 0x1198, 0x080c, 0x5f05, 0x2079,
+ 0x0260, 0x7a30, 0x9296, 0x1107, 0x1158, 0x7834, 0x9005, 0x1140,
+ 0x7093, 0x0001, 0x080c, 0x5e5b, 0x7097, 0x000e, 0x0029, 0x0010,
+ 0x080c, 0x5ee1, 0x00fe, 0x0005, 0x918d, 0x0001, 0x080c, 0x5f30,
+ 0x7097, 0x000f, 0x708f, 0x0000, 0x2061, 0x0140, 0x605b, 0xbc85,
+ 0x605f, 0xb5b5, 0x2061, 0x0100, 0x6043, 0x0005, 0x6043, 0x0004,
+ 0x2009, 0x07d0, 0x2011, 0x5d94, 0x080c, 0x82ce, 0x0005, 0x708c,
+ 0x9005, 0x0130, 0x2011, 0x5d94, 0x080c, 0x82da, 0x7097, 0x0000,
+ 0x0005, 0x7097, 0x0011, 0x080c, 0x9eeb, 0x080c, 0x5f05, 0x20e1,
+ 0x0000, 0x2099, 0x0260, 0x20e9, 0x0000, 0x20a1, 0x0240, 0x748c,
+ 0x9480, 0x0018, 0x9080, 0x0007, 0x9084, 0x03f8, 0x8004, 0x20a8,
+ 0x4003, 0x080c, 0x5ee8, 0x11a0, 0x7178, 0x81ff, 0x0188, 0x900e,
+ 0x707c, 0x9084, 0x00ff, 0x0160, 0x080c, 0x276e, 0x9186, 0x007e,
+ 0x0138, 0x9186, 0x0080, 0x0120, 0x2011, 0x0008, 0x080c, 0x5d41,
+ 0x60c3, 0x0014, 0x080c, 0x5dbe, 0x0005, 0x00f6, 0x708c, 0x9005,
+ 0x0500, 0x2011, 0x5d94, 0x080c, 0x82da, 0x9086, 0x0014, 0x11b8,
+ 0x080c, 0x5f05, 0x2079, 0x0260, 0x7a30, 0x9296, 0x1103, 0x1178,
+ 0x7834, 0x9005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005,
+ 0x1110, 0x70c3, 0x0001, 0x7097, 0x0012, 0x0029, 0x0010, 0x708f,
+ 0x0000, 0x00fe, 0x0005, 0x00f6, 0x7097, 0x0013, 0x080c, 0x5e97,
+ 0x2079, 0x0240, 0x7833, 0x1103, 0x7837, 0x0000, 0x080c, 0x5f05,
+ 0x080c, 0x5ee8, 0x1170, 0x7080, 0x9005, 0x1158, 0x7158, 0x9186,
+ 0xffff, 0x0138, 0x2011, 0x0008, 0x080c, 0x5d41, 0x0168, 0x080c,
+ 0x5ebe, 0x20a9, 0x0008, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9,
+ 0x0000, 0x20a1, 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c, 0x5dbe,
+ 0x00fe, 0x0005, 0x00f6, 0x708c, 0x9005, 0x0500, 0x2011, 0x5d94,
+ 0x080c, 0x82da, 0x9086, 0x0014, 0x11b8, 0x080c, 0x5f05, 0x2079,
+ 0x0260, 0x7a30, 0x9296, 0x1104, 0x1178, 0x7834, 0x9005, 0x1160,
+ 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005, 0x1110, 0x70c3, 0x0001,
+ 0x7097, 0x0014, 0x0029, 0x0010, 0x708f, 0x0000, 0x00fe, 0x0005,
+ 0x00f6, 0x7097, 0x0015, 0x080c, 0x5e97, 0x2079, 0x0240, 0x7833,
+ 0x1104, 0x7837, 0x0000, 0x080c, 0x5f05, 0x080c, 0x5ee8, 0x11b8,
+ 0x7080, 0x9005, 0x11a0, 0x7160, 0x9186, 0xffff, 0x0180, 0x9180,
+ 0x32e9, 0x200d, 0x918c, 0xff00, 0x810f, 0x2011, 0x0008, 0x080c,
+ 0x5d41, 0x0180, 0x080c, 0x4ed8, 0x0110, 0x080c, 0x27d7, 0x20a9,
+ 0x0008, 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1,
+ 0x024e, 0x4003, 0x60c3, 0x0014, 0x080c, 0x5dbe, 0x00fe, 0x0005,
+ 0x00f6, 0x708c, 0x9005, 0x05f0, 0x2011, 0x5d94, 0x080c, 0x82da,
+ 0x9086, 0x0014, 0x15a8, 0x080c, 0x5f05, 0x2079, 0x0260, 0x7a30,
+ 0x9296, 0x1105, 0x1568, 0x7834, 0x9084, 0x0100, 0x2011, 0x0100,
+ 0x921e, 0x1168, 0x9085, 0x0001, 0x080c, 0x5f30, 0x7a38, 0xd2fc,
+ 0x0128, 0x70c0, 0x9005, 0x1110, 0x70c3, 0x0001, 0x0080, 0x9005,
+ 0x11b8, 0x7a38, 0xd2fc, 0x0128, 0x70c0, 0x9005, 0x1110, 0x70c3,
+ 0x0001, 0x9085, 0x0001, 0x080c, 0x5f30, 0x7093, 0x0000, 0x7a38,
+ 0xd2f4, 0x0110, 0x70db, 0x0008, 0x7097, 0x0016, 0x0029, 0x0010,
+ 0x708f, 0x0000, 0x00fe, 0x0005, 0x080c, 0x9eeb, 0x080c, 0x5f05,
+ 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9, 0x0000, 0x20a1, 0x0240,
+ 0x20a9, 0x000e, 0x4003, 0x2011, 0x026d, 0x2204, 0x9084, 0x0100,
+ 0x2011, 0x024d, 0x2012, 0x2011, 0x026e, 0x7097, 0x0017, 0x080c,
+ 0x5ee8, 0x1150, 0x7080, 0x9005, 0x1138, 0x080c, 0x5cb4, 0x1188,
+ 0x9085, 0x0001, 0x080c, 0x27d7, 0x20a9, 0x0008, 0x080c, 0x5f05,
+ 0x20e1, 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x20a1, 0x024e,
+ 0x4003, 0x60c3, 0x0014, 0x080c, 0x5dbe, 0x0010, 0x080c, 0x580e,
+ 0x0005, 0x00f6, 0x708c, 0x9005, 0x01d8, 0x2011, 0x5d94, 0x080c,
+ 0x82da, 0x9086, 0x0084, 0x1190, 0x080c, 0x5f05, 0x2079, 0x0260,
+ 0x7a30, 0x9296, 0x1106, 0x1150, 0x7834, 0x9005, 0x1138, 0x9006,
+ 0x080c, 0x5f30, 0x7097, 0x0018, 0x0029, 0x0010, 0x708f, 0x0000,
+ 0x00fe, 0x0005, 0x00f6, 0x7097, 0x0019, 0x080c, 0x5e97, 0x2079,
+ 0x0240, 0x7833, 0x1106, 0x7837, 0x0000, 0x080c, 0x5f05, 0x2009,
+ 0x026e, 0x2039, 0x1c0e, 0x20a9, 0x0040, 0x213e, 0x8738, 0x8108,
+ 0x9186, 0x0280, 0x1128, 0x6814, 0x8000, 0x6816, 0x2009, 0x0260,
+ 0x1f04, 0x5c1d, 0x2039, 0x1c0e, 0x080c, 0x5ee8, 0x11e8, 0x2728,
+ 0x2514, 0x8207, 0x9084, 0x00ff, 0x8000, 0x2018, 0x9294, 0x00ff,
+ 0x8007, 0x9205, 0x202a, 0x705c, 0x2310, 0x8214, 0x92a0, 0x1c0e,
+ 0x2414, 0x938c, 0x0001, 0x0118, 0x9294, 0xff00, 0x0018, 0x9294,
+ 0x00ff, 0x8007, 0x9215, 0x2222, 0x20a9, 0x0040, 0x2009, 0x024e,
+ 0x270e, 0x8738, 0x8108, 0x9186, 0x0260, 0x1128, 0x6810, 0x8000,
+ 0x6812, 0x2009, 0x0240, 0x1f04, 0x5c50, 0x60c3, 0x0084, 0x080c,
+ 0x5dbe, 0x00fe, 0x0005, 0x00f6, 0x708c, 0x9005, 0x01e0, 0x2011,
+ 0x5d94, 0x080c, 0x82da, 0x9086, 0x0084, 0x1198, 0x080c, 0x5f05,
+ 0x2079, 0x0260, 0x7a30, 0x9296, 0x1107, 0x1158, 0x7834, 0x9005,
+ 0x1140, 0x7093, 0x0001, 0x080c, 0x5e5b, 0x7097, 0x001a, 0x0029,
+ 0x0010, 0x708f, 0x0000, 0x00fe, 0x0005, 0x9085, 0x0001, 0x080c,
+ 0x5f30, 0x7097, 0x001b, 0x080c, 0x9eeb, 0x080c, 0x5f05, 0x2011,
+ 0x0260, 0x2009, 0x0240, 0x748c, 0x9480, 0x0018, 0x9080, 0x0007,
+ 0x9084, 0x03f8, 0x8004, 0x20a8, 0x220e, 0x8210, 0x8108, 0x9186,
+ 0x0260, 0x1150, 0x6810, 0x8000, 0x6812, 0x2009, 0x0240, 0x6814,
+ 0x8000, 0x6816, 0x2011, 0x0260, 0x1f04, 0x5c9c, 0x60c3, 0x0084,
+ 0x080c, 0x5dbe, 0x0005, 0x0005, 0x0086, 0x0096, 0x2029, 0x185c,
+ 0x252c, 0x20a9, 0x0008, 0x2041, 0x1c0e, 0x20e9, 0x0001, 0x28a0,
+ 0x080c, 0x5f05, 0x20e1, 0x0000, 0x2099, 0x026e, 0x4003, 0x20a9,
+ 0x0008, 0x2011, 0x0007, 0xd5d4, 0x0108, 0x9016, 0x2800, 0x9200,
+ 0x200c, 0x91a6, 0xffff, 0x1148, 0xd5d4, 0x0110, 0x8210, 0x0008,
+ 0x8211, 0x1f04, 0x5cce, 0x0804, 0x5d3d, 0x82ff, 0x1160, 0xd5d4,
+ 0x0120, 0x91a6, 0x3fff, 0x0d90, 0x0020, 0x91a6, 0x3fff, 0x0904,
+ 0x5d3d, 0x918d, 0xc000, 0x20a9, 0x0010, 0x2019, 0x0001, 0xd5d4,
+ 0x0110, 0x2019, 0x0010, 0x2120, 0xd5d4, 0x0110, 0x8423, 0x0008,
+ 0x8424, 0x1240, 0xd5d4, 0x0110, 0x8319, 0x0008, 0x8318, 0x1f04,
+ 0x5cf4, 0x04d8, 0x23a8, 0x2021, 0x0001, 0x8426, 0x8425, 0x1f04,
+ 0x5d06, 0x2328, 0x8529, 0x92be, 0x0007, 0x0158, 0x0006, 0x2039,
+ 0x0007, 0x2200, 0x973a, 0x000e, 0x27a8, 0x95a8, 0x0010, 0x1f04,
+ 0x5d15, 0x755a, 0x95c8, 0x32e9, 0x292d, 0x95ac, 0x00ff, 0x757e,
+ 0x6532, 0x6536, 0x0016, 0x2508, 0x080c, 0x27b7, 0x001e, 0x60e7,
+ 0x0000, 0x65ea, 0x2018, 0x2304, 0x9405, 0x201a, 0x7083, 0x0001,
+ 0x20e9, 0x0000, 0x20a1, 0x024e, 0x20e1, 0x0001, 0x2898, 0x20a9,
+ 0x0008, 0x4003, 0x9085, 0x0001, 0x0008, 0x9006, 0x009e, 0x008e,
+ 0x0005, 0x0156, 0x01c6, 0x01d6, 0x0136, 0x0146, 0x22a8, 0x20e1,
+ 0x0000, 0x2099, 0x026e, 0x20e9, 0x0000, 0x2011, 0x024e, 0x22a0,
+ 0x4003, 0x014e, 0x013e, 0x01de, 0x01ce, 0x015e, 0x2118, 0x9026,
+ 0x2001, 0x0007, 0x939a, 0x0010, 0x0218, 0x8420, 0x8001, 0x0cd0,
+ 0x2118, 0x84ff, 0x0120, 0x939a, 0x0010, 0x8421, 0x1de0, 0x2021,
+ 0x0001, 0x83ff, 0x0118, 0x8423, 0x8319, 0x1de8, 0x9238, 0x2029,
+ 0x026e, 0x9528, 0x2504, 0x942c, 0x11b8, 0x9405, 0x203a, 0x715a,
+ 0x91a0, 0x32e9, 0x242d, 0x95ac, 0x00ff, 0x757e, 0x6532, 0x6536,
+ 0x0016, 0x2508, 0x080c, 0x27b7, 0x001e, 0x60e7, 0x0000, 0x65ea,
+ 0x7083, 0x0001, 0x9084, 0x0000, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x7087, 0x0000, 0x00ee, 0x0005, 0x00e6, 0x00f6, 0x2079, 0x0100,
+ 0x2071, 0x0140, 0x080c, 0x5e4a, 0x080c, 0x9656, 0x7004, 0x9084,
+ 0x4000, 0x0110, 0x080c, 0x2c98, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x1825, 0x2073, 0x0000, 0x7840, 0x0026, 0x0016, 0x2009, 0x00f7,
+ 0x080c, 0x5ea7, 0x001e, 0x9094, 0x0010, 0x9285, 0x0080, 0x7842,
+ 0x7a42, 0x002e, 0x012e, 0x00fe, 0x00ee, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x2afe, 0x0228, 0x2011, 0x0101, 0x2204, 0xc0c5,
+ 0x2012, 0x2011, 0x19c9, 0x2013, 0x0000, 0x708f, 0x0000, 0x012e,
+ 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0x964d, 0x6144, 0xd184,
+ 0x0120, 0x7194, 0x918d, 0x2000, 0x0018, 0x7188, 0x918d, 0x1000,
+ 0x2011, 0x1971, 0x2112, 0x2009, 0x07d0, 0x2011, 0x5d94, 0x080c,
+ 0x836c, 0x0005, 0x0016, 0x0026, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xa069, 0x2009, 0x00f7, 0x080c, 0x5ea7, 0x2061, 0x19d2,
+ 0x900e, 0x611a, 0x611e, 0x6172, 0x6176, 0x2061, 0x1800, 0x6003,
+ 0x0001, 0x2061, 0x0100, 0x6043, 0x0090, 0x6043, 0x0010, 0x2009,
+ 0x1971, 0x200b, 0x0000, 0x2009, 0x002d, 0x2011, 0x5e16, 0x080c,
+ 0x82ce, 0x012e, 0x00ce, 0x002e, 0x001e, 0x0005, 0x00e6, 0x0006,
+ 0x0126, 0x2091, 0x8000, 0x0471, 0x2071, 0x0100, 0x080c, 0x9656,
+ 0x2071, 0x0140, 0x7004, 0x9084, 0x4000, 0x0110, 0x080c, 0x2c98,
+ 0x080c, 0x720f, 0x0188, 0x080c, 0x722a, 0x1170, 0x080c, 0x750e,
+ 0x0016, 0x080c, 0x2886, 0x2001, 0x1945, 0x2102, 0x001e, 0x080c,
+ 0x7509, 0x080c, 0x7127, 0x0050, 0x2009, 0x0001, 0x080c, 0x2bb6,
+ 0x2001, 0x0001, 0x080c, 0x2717, 0x080c, 0x5dea, 0x012e, 0x000e,
+ 0x00ee, 0x0005, 0x2001, 0x180e, 0x2004, 0xd0bc, 0x0158, 0x0026,
+ 0x0036, 0x2011, 0x8017, 0x2001, 0x1971, 0x201c, 0x080c, 0x4b1f,
+ 0x003e, 0x002e, 0x0005, 0x20a9, 0x0012, 0x20e9, 0x0001, 0x20a1,
+ 0x1c80, 0x080c, 0x5f05, 0x20e9, 0x0000, 0x2099, 0x026e, 0x0099,
+ 0x20a9, 0x0020, 0x080c, 0x5eff, 0x2099, 0x0260, 0x20a1, 0x1c92,
+ 0x0051, 0x20a9, 0x000e, 0x080c, 0x5f02, 0x2099, 0x0260, 0x20a1,
+ 0x1cb2, 0x0009, 0x0005, 0x0016, 0x0026, 0x3410, 0x3308, 0x2104,
+ 0x8007, 0x2012, 0x8108, 0x8210, 0x1f04, 0x5e7f, 0x002e, 0x001e,
+ 0x0005, 0x080c, 0x9eeb, 0x20e1, 0x0001, 0x2099, 0x1c00, 0x20e9,
+ 0x0000, 0x20a1, 0x0240, 0x20a9, 0x000c, 0x4003, 0x0005, 0x080c,
+ 0x9eeb, 0x080c, 0x5f05, 0x20e1, 0x0000, 0x2099, 0x0260, 0x20e9,
+ 0x0000, 0x20a1, 0x0240, 0x20a9, 0x000c, 0x4003, 0x0005, 0x00c6,
+ 0x0006, 0x2061, 0x0100, 0x810f, 0x2001, 0x1833, 0x2004, 0x9005,
+ 0x1138, 0x2001, 0x1817, 0x2004, 0x9084, 0x00ff, 0x9105, 0x0010,
+ 0x9185, 0x00f7, 0x604a, 0x000e, 0x00ce, 0x0005, 0x0016, 0x0046,
+ 0x080c, 0x67bf, 0x0158, 0x9006, 0x2020, 0x2009, 0x002a, 0x080c,
+ 0xd885, 0x2001, 0x180c, 0x200c, 0xc195, 0x2102, 0x2019, 0x002a,
+ 0x900e, 0x080c, 0x3156, 0x080c, 0xc539, 0x0140, 0x0036, 0x2019,
+ 0xffff, 0x2021, 0x0007, 0x080c, 0x4cbc, 0x003e, 0x004e, 0x001e,
+ 0x0005, 0x080c, 0x5dea, 0x7097, 0x0000, 0x708f, 0x0000, 0x0005,
+ 0x0006, 0x2001, 0x180c, 0x2004, 0xd09c, 0x0100, 0x000e, 0x0005,
+ 0x0006, 0x0016, 0x0126, 0x2091, 0x8000, 0x2001, 0x0101, 0x200c,
+ 0x918d, 0x0006, 0x2102, 0x012e, 0x001e, 0x000e, 0x0005, 0x2009,
+ 0x0001, 0x0020, 0x2009, 0x0002, 0x0008, 0x900e, 0x6814, 0x9084,
+ 0xffc0, 0x910d, 0x6916, 0x0005, 0x00f6, 0x0156, 0x0146, 0x01d6,
+ 0x9006, 0x20a9, 0x0080, 0x20e9, 0x0001, 0x20a1, 0x1c00, 0x4004,
+ 0x2079, 0x1c00, 0x7803, 0x2200, 0x7807, 0x00ef, 0x780f, 0x00ef,
+ 0x7813, 0x0138, 0x7823, 0xffff, 0x7827, 0xffff, 0x01de, 0x014e,
+ 0x015e, 0x00fe, 0x0005, 0x2001, 0x1800, 0x2003, 0x0001, 0x0005,
+ 0x2001, 0x197e, 0x0118, 0x2003, 0x0001, 0x0010, 0x2003, 0x0000,
+ 0x0005, 0x0156, 0x20a9, 0x0800, 0x2009, 0x1000, 0x9006, 0x200a,
+ 0x8108, 0x1f04, 0x5f3f, 0x015e, 0x0005, 0x00d6, 0x0036, 0x0156,
+ 0x0136, 0x0146, 0x2069, 0x185b, 0x9006, 0xb802, 0xb8be, 0xb807,
+ 0x0707, 0xb80a, 0xb80e, 0xb812, 0x9198, 0x32e9, 0x231d, 0x939c,
+ 0x00ff, 0xbb16, 0x0016, 0x0026, 0xb8b2, 0x080c, 0xa062, 0x1120,
+ 0x9192, 0x007e, 0x1208, 0xbbb2, 0x20a9, 0x0004, 0xb8b4, 0x20e8,
+ 0xb9b8, 0x9198, 0x0006, 0x9006, 0x23a0, 0x4004, 0x20a9, 0x0004,
+ 0x9198, 0x000a, 0x23a0, 0x4004, 0x002e, 0x001e, 0xb83e, 0xb842,
+ 0xb84e, 0xb852, 0xb856, 0xb85a, 0xb85e, 0xb862, 0xb866, 0xb86a,
+ 0xb86f, 0x0100, 0xb872, 0xb876, 0xb87a, 0xb88a, 0xb88e, 0xb893,
+ 0x0008, 0xb896, 0xb89a, 0xb89e, 0xb8ae, 0xb9a2, 0x0096, 0xb8a4,
+ 0x904d, 0x0110, 0x080c, 0x1063, 0xb8a7, 0x0000, 0x009e, 0x9006,
+ 0xb84a, 0x6810, 0xb83a, 0x680c, 0xb846, 0x6814, 0x9084, 0x00ff,
+ 0xb842, 0x014e, 0x013e, 0x015e, 0x003e, 0x00de, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0xa974, 0xae78, 0x9684, 0x3fff, 0x9082, 0x4000,
+ 0x1a04, 0x6015, 0x9182, 0x0800, 0x1a04, 0x6019, 0x2001, 0x180c,
+ 0x2004, 0x9084, 0x0003, 0x1904, 0x601f, 0x9188, 0x1000, 0x2104,
+ 0x905d, 0x0518, 0xb804, 0x9084, 0x00ff, 0x908e, 0x0006, 0x1508,
+ 0xb8a4, 0x900d, 0x1904, 0x6031, 0xb850, 0x900d, 0x1148, 0xa802,
+ 0x2900, 0xb852, 0xb84e, 0x080c, 0x8696, 0x9006, 0x012e, 0x0005,
+ 0x00a6, 0x2150, 0x2900, 0xb002, 0xa803, 0x0000, 0x00ae, 0xb852,
+ 0x0c90, 0x2001, 0x0005, 0x900e, 0x04b8, 0x2001, 0x0028, 0x900e,
+ 0x0498, 0x9082, 0x0006, 0x1290, 0x080c, 0xa062, 0x1160, 0xb8a0,
+ 0x9084, 0xff80, 0x1140, 0xb900, 0xd1fc, 0x0990, 0x2001, 0x0029,
+ 0x2009, 0x1000, 0x0408, 0x2001, 0x0028, 0x00a8, 0x2009, 0x180c,
+ 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0068, 0xd184, 0x0118,
+ 0x2001, 0x0004, 0x0040, 0x2001, 0x0029, 0xb900, 0xd1fc, 0x0118,
+ 0x2009, 0x1000, 0x0048, 0x900e, 0x0038, 0x2001, 0x0029, 0x900e,
+ 0x0018, 0x2001, 0x0029, 0x900e, 0x9005, 0x012e, 0x0005, 0x2001,
+ 0x180c, 0x2004, 0xd084, 0x19d0, 0x9188, 0x1000, 0x2104, 0x905d,
+ 0x09a8, 0x080c, 0x67c3, 0x1990, 0xb800, 0xd0bc, 0x0978, 0x0804,
+ 0x5fc8, 0x080c, 0x663b, 0x0904, 0x5fe1, 0x0804, 0x5fcc, 0x00b6,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0xa974, 0x9182, 0x0800, 0x1a04,
+ 0x60b5, 0x9188, 0x1000, 0x2104, 0x905d, 0x0904, 0x608d, 0xb8a0,
+ 0x9086, 0x007f, 0x0190, 0xa87c, 0xd0fc, 0x1178, 0x080c, 0x67cb,
+ 0x0160, 0xa994, 0x81ff, 0x0130, 0x908e, 0x0004, 0x0130, 0x908e,
+ 0x0005, 0x0118, 0x080c, 0x67c3, 0x1598, 0xa87c, 0xd0fc, 0x01e0,
+ 0xa894, 0x9005, 0x01c8, 0x2060, 0x0026, 0x2010, 0x080c, 0xbe25,
+ 0x002e, 0x1120, 0x2001, 0x0008, 0x0804, 0x60b7, 0x6020, 0x9086,
+ 0x000a, 0x0120, 0x2001, 0x0008, 0x0804, 0x60b7, 0x601a, 0x6003,
+ 0x0008, 0x2900, 0x6016, 0x0058, 0x080c, 0xa08d, 0x05e8, 0x2b00,
+ 0x6012, 0x2900, 0x6016, 0x600b, 0xffff, 0x6023, 0x000a, 0x2009,
+ 0x0003, 0x080c, 0xa15d, 0x9006, 0x0458, 0x2001, 0x0028, 0x0438,
+ 0x9082, 0x0006, 0x1290, 0x080c, 0xa062, 0x1160, 0xb8a0, 0x9084,
+ 0xff80, 0x1140, 0xb900, 0xd1fc, 0x0900, 0x2001, 0x0029, 0x2009,
+ 0x1000, 0x00a8, 0x2001, 0x0028, 0x0090, 0x2009, 0x180c, 0x210c,
+ 0xd18c, 0x0118, 0x2001, 0x0004, 0x0050, 0xd184, 0x0118, 0x2001,
+ 0x0004, 0x0028, 0x2001, 0x0029, 0x0010, 0x2001, 0x0029, 0x9005,
+ 0x012e, 0x00ee, 0x00be, 0x0005, 0x2001, 0x002c, 0x0cc0, 0x00f6,
+ 0x00b6, 0x0126, 0x2091, 0x8000, 0xa8e0, 0x9005, 0x1550, 0xa8dc,
+ 0x9082, 0x0101, 0x1630, 0xa8c8, 0x9005, 0x1518, 0xa8c4, 0x9082,
+ 0x0101, 0x12f8, 0xa974, 0x2079, 0x1800, 0x9182, 0x0800, 0x12e8,
+ 0x7830, 0x9084, 0x0003, 0x1130, 0xaa98, 0xab94, 0xa878, 0x9084,
+ 0x0007, 0x00ea, 0x7930, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038,
+ 0xd184, 0x0118, 0x2001, 0x0004, 0x0010, 0x2001, 0x0029, 0x900e,
+ 0x0038, 0x2001, 0x002c, 0x900e, 0x0018, 0x2001, 0x0029, 0x900e,
+ 0x9006, 0x0008, 0x9005, 0x012e, 0x00be, 0x00fe, 0x0005, 0x614c,
+ 0x6107, 0x611e, 0x614c, 0x614c, 0x614c, 0x614c, 0x614c, 0x2100,
+ 0x9082, 0x007e, 0x1278, 0x080c, 0x643f, 0x0148, 0x9046, 0xb810,
+ 0x9306, 0x1904, 0x6154, 0xb814, 0x9206, 0x15f0, 0x0028, 0xbb12,
+ 0xba16, 0x0010, 0x080c, 0x49d9, 0x0150, 0x04b0, 0x080c, 0x649f,
+ 0x1598, 0xb810, 0x9306, 0x1580, 0xb814, 0x9206, 0x1568, 0x080c,
+ 0xa08d, 0x0530, 0x2b00, 0x6012, 0x080c, 0xc2b3, 0x2900, 0x6016,
+ 0x600b, 0xffff, 0x6023, 0x000a, 0xa878, 0x9086, 0x0001, 0x1170,
+ 0x080c, 0x318b, 0x9006, 0x080c, 0x63dc, 0x2001, 0x0002, 0x080c,
+ 0x63f0, 0x2001, 0x0200, 0xb86e, 0xb893, 0x0002, 0x2009, 0x0003,
+ 0x080c, 0xa15d, 0x9006, 0x0068, 0x2001, 0x0001, 0x900e, 0x0038,
+ 0x2001, 0x002c, 0x900e, 0x0018, 0x2001, 0x0028, 0x900e, 0x9005,
+ 0x0000, 0x012e, 0x00be, 0x00fe, 0x0005, 0x00b6, 0x00f6, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0xa894, 0x90c6, 0x0015, 0x0904, 0x632d,
+ 0x90c6, 0x0056, 0x0904, 0x6331, 0x90c6, 0x0066, 0x0904, 0x6335,
+ 0x90c6, 0x0067, 0x0904, 0x6339, 0x90c6, 0x0068, 0x0904, 0x633d,
+ 0x90c6, 0x0071, 0x0904, 0x6341, 0x90c6, 0x0074, 0x0904, 0x6345,
+ 0x90c6, 0x007c, 0x0904, 0x6349, 0x90c6, 0x007e, 0x0904, 0x634d,
+ 0x90c6, 0x0037, 0x0904, 0x6351, 0x9016, 0x2079, 0x1800, 0xa974,
+ 0x9186, 0x00ff, 0x0904, 0x6328, 0x9182, 0x0800, 0x1a04, 0x6328,
+ 0x080c, 0x649f, 0x1198, 0xb804, 0x9084, 0x00ff, 0x9082, 0x0006,
+ 0x1268, 0xa894, 0x90c6, 0x006f, 0x0148, 0x080c, 0xa062, 0x1904,
+ 0x6311, 0xb8a0, 0x9084, 0xff80, 0x1904, 0x6311, 0xa894, 0x90c6,
+ 0x006f, 0x0158, 0x90c6, 0x005e, 0x0904, 0x6271, 0x90c6, 0x0064,
+ 0x0904, 0x629a, 0x2008, 0x0804, 0x6234, 0xa998, 0xa8b0, 0x2040,
+ 0x080c, 0xa062, 0x1120, 0x9182, 0x007f, 0x0a04, 0x6234, 0x9186,
+ 0x00ff, 0x0904, 0x6234, 0x9182, 0x0800, 0x1a04, 0x6234, 0xaaa0,
+ 0xab9c, 0x7878, 0x9306, 0x11a8, 0x787c, 0x0096, 0x924e, 0x1128,
+ 0x2208, 0x2310, 0x009e, 0x0804, 0x6234, 0x080c, 0xa062, 0x1140,
+ 0x99cc, 0xff00, 0x009e, 0x1128, 0x2208, 0x2310, 0x0804, 0x6234,
+ 0x009e, 0x080c, 0x49d9, 0x0904, 0x623d, 0x900e, 0x9016, 0x90c6,
+ 0x4000, 0x1558, 0x0006, 0x080c, 0x66bf, 0x1108, 0xc185, 0xb800,
+ 0xd0bc, 0x0108, 0xc18d, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0031, 0x20a0, 0xb8b4, 0x20e0, 0xb8b8, 0x9080, 0x0006,
+ 0x2098, 0x080c, 0x0fae, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0035, 0x20a0, 0xb8b4, 0x20e0, 0xb8b8, 0x9080, 0x000a,
+ 0x2098, 0x080c, 0x0fae, 0x000e, 0x00c8, 0x90c6, 0x4007, 0x1110,
+ 0x2408, 0x00a0, 0x90c6, 0x4008, 0x1118, 0x2708, 0x2610, 0x0070,
+ 0x90c6, 0x4009, 0x1108, 0x0050, 0x90c6, 0x4006, 0x0138, 0x2001,
+ 0x4005, 0x2009, 0x000a, 0x0010, 0x2001, 0x4006, 0xa896, 0xa99a,
+ 0xaa9e, 0x2001, 0x0030, 0x900e, 0x0470, 0x080c, 0xa08d, 0x1130,
+ 0x2001, 0x4005, 0x2009, 0x0003, 0x9016, 0x0c80, 0x2b00, 0x6012,
+ 0x080c, 0xc2b3, 0x2900, 0x6016, 0x6023, 0x0001, 0xa868, 0xd88c,
+ 0x0108, 0xc0f5, 0xa86a, 0x0126, 0x2091, 0x8000, 0x080c, 0x318b,
+ 0x012e, 0x9006, 0x080c, 0x63dc, 0x2001, 0x0002, 0x080c, 0x63f0,
+ 0x2009, 0x0002, 0x080c, 0xa15d, 0xa8b0, 0xd094, 0x0118, 0xb8bc,
+ 0xc08d, 0xb8be, 0x9006, 0x9005, 0x012e, 0x00ee, 0x00fe, 0x00be,
+ 0x0005, 0x080c, 0x55ef, 0x0118, 0x2009, 0x0007, 0x00f8, 0xa998,
+ 0xaeb0, 0x080c, 0x649f, 0x1904, 0x622f, 0x9186, 0x007f, 0x0130,
+ 0x080c, 0x67c3, 0x0118, 0x2009, 0x0009, 0x0080, 0x0096, 0x080c,
+ 0x1031, 0x1120, 0x009e, 0x2009, 0x0002, 0x0040, 0x2900, 0x009e,
+ 0xa806, 0x080c, 0xc01f, 0x19b0, 0x2009, 0x0003, 0x2001, 0x4005,
+ 0x0804, 0x6236, 0xa998, 0xaeb0, 0x080c, 0x649f, 0x1904, 0x622f,
+ 0x0096, 0x080c, 0x1031, 0x1128, 0x009e, 0x2009, 0x0002, 0x0804,
+ 0x62ee, 0x2900, 0x009e, 0xa806, 0x0096, 0x2048, 0x20a9, 0x002b,
+ 0xb8b4, 0x20e0, 0xb8b8, 0x2098, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0002, 0x20a0, 0x4003, 0x20a9, 0x0008, 0x9080, 0x0006, 0x20a0,
+ 0xbbb8, 0x9398, 0x0006, 0x2398, 0x080c, 0x0fae, 0x009e, 0xa87b,
+ 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0xd684, 0x1168, 0x080c,
+ 0x55db, 0xd0b4, 0x1118, 0xa89b, 0x000b, 0x00e0, 0xb800, 0xd08c,
+ 0x0118, 0xa89b, 0x000c, 0x00b0, 0x080c, 0x67c3, 0x0118, 0xa89b,
+ 0x0009, 0x0080, 0x080c, 0x55ef, 0x0118, 0xa89b, 0x0007, 0x0050,
+ 0x080c, 0xc002, 0x1904, 0x626a, 0x2009, 0x0003, 0x2001, 0x4005,
+ 0x0804, 0x6236, 0xa87b, 0x0030, 0xa897, 0x4005, 0xa804, 0x8006,
+ 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080, 0x0002,
+ 0x2009, 0x002b, 0xaaa0, 0xab9c, 0xaca8, 0xada4, 0x2031, 0x0000,
+ 0x2041, 0x1288, 0x080c, 0xa5e6, 0x1904, 0x626a, 0x2009, 0x0002,
+ 0x08e8, 0x2001, 0x0028, 0x900e, 0x0804, 0x626b, 0x2009, 0x180c,
+ 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038, 0xd184, 0x0118,
+ 0x2001, 0x0004, 0x0010, 0x2001, 0x0029, 0x900e, 0x0804, 0x626b,
+ 0x2001, 0x0029, 0x900e, 0x0804, 0x626b, 0x080c, 0x3718, 0x0804,
+ 0x626c, 0x080c, 0x5306, 0x0804, 0x626c, 0x080c, 0x455e, 0x0804,
+ 0x626c, 0x080c, 0x45d7, 0x0804, 0x626c, 0x080c, 0x4633, 0x0804,
+ 0x626c, 0x080c, 0x4a95, 0x0804, 0x626c, 0x080c, 0x4d3e, 0x0804,
+ 0x626c, 0x080c, 0x4f6e, 0x0804, 0x626c, 0x080c, 0x5167, 0x0804,
+ 0x626c, 0x080c, 0x3941, 0x0804, 0x626c, 0x00b6, 0xa974, 0xae78,
+ 0x9684, 0x3fff, 0x9082, 0x4000, 0x1618, 0x9182, 0x0800, 0x1268,
+ 0x9188, 0x1000, 0x2104, 0x905d, 0x0140, 0x080c, 0x67c3, 0x1148,
+ 0x00e9, 0x080c, 0x65ca, 0x9006, 0x00b0, 0x2001, 0x0028, 0x900e,
+ 0x0090, 0x9082, 0x0006, 0x1240, 0xb900, 0xd1fc, 0x0d88, 0x2001,
+ 0x0029, 0x2009, 0x1000, 0x0038, 0x2001, 0x0029, 0x900e, 0x0018,
+ 0x2001, 0x0029, 0x900e, 0x9005, 0x00be, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0xb850, 0x900d, 0x0150, 0x2900, 0x0096, 0x2148, 0xa802,
+ 0x009e, 0xa803, 0x0000, 0xb852, 0x012e, 0x0005, 0x2900, 0xb852,
+ 0xb84e, 0xa803, 0x0000, 0x0cc0, 0x0126, 0x2091, 0x8000, 0xb84c,
+ 0x9005, 0x0170, 0x00e6, 0x2071, 0x19bf, 0x7004, 0x9086, 0x0002,
+ 0x0168, 0x00ee, 0xb84c, 0xa802, 0x2900, 0xb84e, 0x012e, 0x0005,
+ 0x2900, 0xb852, 0xb84e, 0xa803, 0x0000, 0x0cc0, 0x701c, 0x9b06,
+ 0x1d80, 0xb84c, 0x00a6, 0x2050, 0xb000, 0xa802, 0x2900, 0xb002,
+ 0x00ae, 0x00ee, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0xb84c,
+ 0x904d, 0x0130, 0xa800, 0x9005, 0x1108, 0xb852, 0xb84e, 0x9905,
+ 0x012e, 0x0005, 0xb84c, 0x904d, 0x0130, 0xa800, 0x9005, 0x1108,
+ 0xb852, 0xb84e, 0x9905, 0x0005, 0x00b6, 0x0126, 0x00c6, 0x0026,
+ 0x2091, 0x8000, 0x6210, 0x2258, 0xba00, 0x9005, 0x0110, 0xc285,
+ 0x0008, 0xc284, 0xba02, 0x002e, 0x00ce, 0x012e, 0x00be, 0x0005,
+ 0x00b6, 0x0126, 0x00c6, 0x2091, 0x8000, 0x6210, 0x2258, 0xba04,
+ 0x0006, 0x9086, 0x0006, 0x1170, 0xb89c, 0xd0ac, 0x0158, 0x080c,
+ 0x67bf, 0x0140, 0x9284, 0xff00, 0x8007, 0x9086, 0x0007, 0x1110,
+ 0x2011, 0x0600, 0x000e, 0x9294, 0xff00, 0x9215, 0xba06, 0x0006,
+ 0x9086, 0x0006, 0x1120, 0xba90, 0x82ff, 0x090c, 0x0dfa, 0x000e,
+ 0x00ce, 0x012e, 0x00be, 0x0005, 0x00b6, 0x0126, 0x00c6, 0x2091,
+ 0x8000, 0x6210, 0x2258, 0xba04, 0x0006, 0x9086, 0x0006, 0x1168,
+ 0xb89c, 0xd0a4, 0x0150, 0x080c, 0x67bb, 0x1138, 0x9284, 0x00ff,
+ 0x9086, 0x0007, 0x1110, 0x2011, 0x0006, 0x000e, 0x9294, 0x00ff,
+ 0x8007, 0x9215, 0xba06, 0x00ce, 0x012e, 0x00be, 0x0005, 0x9182,
+ 0x0800, 0x0218, 0x9085, 0x0001, 0x0005, 0x00d6, 0x0026, 0x9190,
+ 0x1000, 0x2204, 0x905d, 0x1180, 0x0096, 0x080c, 0x1031, 0x2958,
+ 0x009e, 0x0160, 0x2b00, 0x2012, 0xb85c, 0xb8ba, 0xb860, 0xb8b6,
+ 0x9006, 0xb8a6, 0x080c, 0x5f45, 0x9006, 0x0010, 0x9085, 0x0001,
+ 0x002e, 0x00de, 0x0005, 0x00b6, 0x0096, 0x0126, 0x2091, 0x8000,
+ 0x0026, 0x9182, 0x0800, 0x0218, 0x9085, 0x0001, 0x0458, 0x00d6,
+ 0x9190, 0x1000, 0x2204, 0x905d, 0x0518, 0x2013, 0x0000, 0xb8a4,
+ 0x904d, 0x0110, 0x080c, 0x1063, 0x00d6, 0x00c6, 0xb8ac, 0x2060,
+ 0x8cff, 0x0168, 0x600c, 0x0006, 0x6014, 0x2048, 0x080c, 0xbe37,
+ 0x0110, 0x080c, 0x0fe3, 0x080c, 0xa0e3, 0x00ce, 0x0c88, 0x00ce,
+ 0x00de, 0x2b48, 0xb8b8, 0xb85e, 0xb8b4, 0xb862, 0x080c, 0x1073,
+ 0x00de, 0x9006, 0x002e, 0x012e, 0x009e, 0x00be, 0x0005, 0x0016,
+ 0x9182, 0x0800, 0x0218, 0x9085, 0x0001, 0x0030, 0x9188, 0x1000,
+ 0x2104, 0x905d, 0x0dc0, 0x9006, 0x001e, 0x0005, 0x00d6, 0x0156,
+ 0x0136, 0x0146, 0x9006, 0xb80a, 0xb80e, 0xb800, 0xc08c, 0xb802,
+ 0x080c, 0x7207, 0x1510, 0xb8a0, 0x9086, 0x007e, 0x0120, 0x080c,
+ 0xa062, 0x11d8, 0x0078, 0x7040, 0xd0e4, 0x01b8, 0x00c6, 0x2061,
+ 0x195a, 0x7048, 0x2062, 0x704c, 0x6006, 0x7050, 0x600a, 0x7054,
+ 0x600e, 0x00ce, 0x703c, 0x2069, 0x0140, 0x9005, 0x1110, 0x2001,
+ 0x0001, 0x6886, 0x2069, 0x1800, 0x68b2, 0x7040, 0xb85e, 0x7048,
+ 0xb862, 0x704c, 0xb866, 0x20e1, 0x0000, 0x2099, 0x0276, 0xb8b4,
+ 0x20e8, 0xb8b8, 0x9088, 0x000a, 0x21a0, 0x20a9, 0x0004, 0x4003,
+ 0x2099, 0x027a, 0x9088, 0x0006, 0x21a0, 0x20a9, 0x0004, 0x4003,
+ 0x2069, 0x0200, 0x6817, 0x0001, 0x7040, 0xb86a, 0x7144, 0xb96e,
+ 0x7048, 0xb872, 0x7050, 0xb876, 0x2069, 0x0200, 0x6817, 0x0000,
+ 0xb8a0, 0x9086, 0x007e, 0x1110, 0x7144, 0xb96e, 0x9182, 0x0211,
+ 0x1218, 0x2009, 0x0008, 0x0400, 0x9182, 0x0259, 0x1218, 0x2009,
+ 0x0007, 0x00d0, 0x9182, 0x02c1, 0x1218, 0x2009, 0x0006, 0x00a0,
+ 0x9182, 0x0349, 0x1218, 0x2009, 0x0005, 0x0070, 0x9182, 0x0421,
+ 0x1218, 0x2009, 0x0004, 0x0040, 0x9182, 0x0581, 0x1218, 0x2009,
+ 0x0003, 0x0010, 0x2009, 0x0002, 0xb992, 0x014e, 0x013e, 0x015e,
+ 0x00de, 0x0005, 0x0016, 0x0026, 0x00e6, 0x2071, 0x0260, 0x7034,
+ 0xb896, 0x703c, 0xb89a, 0x7054, 0xb89e, 0x0036, 0xbbbc, 0xc384,
+ 0xba00, 0x2009, 0x187b, 0x210c, 0xd0bc, 0x0120, 0xd1ec, 0x0110,
+ 0xc2ad, 0x0008, 0xc2ac, 0xd0c4, 0x0148, 0xd1e4, 0x0138, 0xc2bd,
+ 0xd0cc, 0x0128, 0xd38c, 0x1108, 0xc385, 0x0008, 0xc2bc, 0xba02,
+ 0xbbbe, 0x003e, 0x00ee, 0x002e, 0x001e, 0x0005, 0x0096, 0x0126,
+ 0x2091, 0x8000, 0xb8a4, 0x904d, 0x0578, 0xa900, 0x81ff, 0x15c0,
+ 0xaa04, 0x9282, 0x0010, 0x16c8, 0x0136, 0x0146, 0x01c6, 0x01d6,
+ 0x8906, 0x8006, 0x8007, 0x908c, 0x003f, 0x21e0, 0x9084, 0xffc0,
+ 0x9080, 0x0004, 0x2098, 0x2009, 0x0010, 0x20a9, 0x0001, 0x4002,
+ 0x9086, 0xffff, 0x0120, 0x8109, 0x1dd0, 0x080c, 0x0dfa, 0x3c00,
+ 0x20e8, 0x3300, 0x8001, 0x20a0, 0x4604, 0x8210, 0xaa06, 0x01de,
+ 0x01ce, 0x014e, 0x013e, 0x0060, 0x080c, 0x1031, 0x0170, 0x2900,
+ 0xb8a6, 0xa803, 0x0000, 0x080c, 0x665b, 0xa807, 0x0001, 0xae12,
+ 0x9085, 0x0001, 0x012e, 0x009e, 0x0005, 0x9006, 0x0cd8, 0x0126,
+ 0x2091, 0x8000, 0x0096, 0xb8a4, 0x904d, 0x0188, 0xa800, 0x9005,
+ 0x1150, 0x080c, 0x666a, 0x1158, 0xa804, 0x908a, 0x0002, 0x0218,
+ 0x8001, 0xa806, 0x0020, 0x080c, 0x1063, 0xb8a7, 0x0000, 0x009e,
+ 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x080c, 0x8696, 0x012e,
+ 0x0005, 0x901e, 0x0010, 0x2019, 0x0001, 0x900e, 0x0126, 0x2091,
+ 0x8000, 0xb84c, 0x2048, 0xb800, 0xd0dc, 0x1170, 0x89ff, 0x0500,
+ 0x83ff, 0x0120, 0xa878, 0x9606, 0x0158, 0x0030, 0xa86c, 0x9406,
+ 0x1118, 0xa870, 0x9506, 0x0120, 0x2908, 0xa800, 0x2048, 0x0c70,
+ 0x080c, 0x9a4e, 0xaa00, 0xb84c, 0x9906, 0x1110, 0xba4e, 0x0020,
+ 0x00a6, 0x2150, 0xb202, 0x00ae, 0x82ff, 0x1110, 0xb952, 0x89ff,
+ 0x012e, 0x0005, 0x9016, 0x0489, 0x1110, 0x2011, 0x0001, 0x0005,
+ 0x080c, 0x66bf, 0x0128, 0x080c, 0xbef4, 0x0010, 0x9085, 0x0001,
+ 0x0005, 0x080c, 0x66bf, 0x0128, 0x080c, 0xbe99, 0x0010, 0x9085,
+ 0x0001, 0x0005, 0x080c, 0x66bf, 0x0128, 0x080c, 0xbef1, 0x0010,
+ 0x9085, 0x0001, 0x0005, 0x080c, 0x66bf, 0x0128, 0x080c, 0xbeb8,
+ 0x0010, 0x9085, 0x0001, 0x0005, 0x080c, 0x66bf, 0x0128, 0x080c,
+ 0xbf37, 0x0010, 0x9085, 0x0001, 0x0005, 0xb8a4, 0x900d, 0x1118,
+ 0x9085, 0x0001, 0x0005, 0x0136, 0x01c6, 0xa800, 0x9005, 0x11b8,
+ 0x890e, 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184, 0xffc0,
+ 0x9080, 0x0004, 0x2098, 0x20a9, 0x0001, 0x2009, 0x0010, 0x4002,
+ 0x9606, 0x0128, 0x8109, 0x1dd8, 0x9085, 0x0001, 0x0008, 0x9006,
+ 0x01ce, 0x013e, 0x0005, 0x0146, 0x01d6, 0xa860, 0x20e8, 0xa85c,
+ 0x9080, 0x0004, 0x20a0, 0x20a9, 0x0010, 0x2009, 0xffff, 0x4104,
+ 0x01de, 0x014e, 0x0136, 0x01c6, 0xa800, 0x9005, 0x11b8, 0x890e,
+ 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184, 0xffc0, 0x9080,
+ 0x0004, 0x2098, 0x20a9, 0x0001, 0x2009, 0x0010, 0x4002, 0x9606,
+ 0x0128, 0x8109, 0x1dd8, 0x9085, 0x0001, 0x0068, 0x0146, 0x01d6,
+ 0x3300, 0x8001, 0x20a0, 0x3c00, 0x20e8, 0x2001, 0xffff, 0x4004,
+ 0x01de, 0x014e, 0x9006, 0x01ce, 0x013e, 0x0005, 0x0096, 0x0126,
+ 0x2091, 0x8000, 0xb8a4, 0x904d, 0x1128, 0x080c, 0x1031, 0x0168,
+ 0x2900, 0xb8a6, 0x080c, 0x665b, 0xa803, 0x0001, 0xa807, 0x0000,
+ 0x9085, 0x0001, 0x012e, 0x009e, 0x0005, 0x9006, 0x0cd8, 0x0096,
+ 0x0126, 0x2091, 0x8000, 0xb8a4, 0x904d, 0x0130, 0xb8a7, 0x0000,
+ 0x080c, 0x1063, 0x9085, 0x0001, 0x012e, 0x009e, 0x0005, 0xb89c,
+ 0xd0a4, 0x0005, 0x00b6, 0x00f6, 0x080c, 0x7207, 0x01b0, 0x71c0,
+ 0x81ff, 0x1198, 0x71d8, 0xd19c, 0x0180, 0x2001, 0x007e, 0x9080,
+ 0x1000, 0x2004, 0x905d, 0x0148, 0xb804, 0x9084, 0x00ff, 0x9086,
+ 0x0006, 0x1118, 0xb800, 0xc0ed, 0xb802, 0x2079, 0x185b, 0x7804,
+ 0x00d0, 0x0156, 0x20a9, 0x007f, 0x900e, 0x0016, 0x080c, 0x649f,
+ 0x1168, 0xb804, 0x9084, 0xff00, 0x8007, 0x9096, 0x0004, 0x0118,
+ 0x9086, 0x0006, 0x1118, 0xb800, 0xc0ed, 0xb802, 0x001e, 0x8108,
+ 0x1f04, 0x66e5, 0x015e, 0x080c, 0x6781, 0x0120, 0x2001, 0x195d,
+ 0x200c, 0x0030, 0x2079, 0x185b, 0x7804, 0x0030, 0x2009, 0x07d0,
+ 0x2011, 0x670f, 0x080c, 0x836c, 0x00fe, 0x00be, 0x0005, 0x00b6,
+ 0x2011, 0x670f, 0x080c, 0x82da, 0x080c, 0x6781, 0x01d8, 0x2001,
+ 0x107e, 0x2004, 0x2058, 0xb900, 0xc1ec, 0xb902, 0x080c, 0x67bf,
+ 0x0130, 0x2009, 0x07d0, 0x2011, 0x670f, 0x080c, 0x836c, 0x00e6,
+ 0x2071, 0x1800, 0x9006, 0x707a, 0x705c, 0x707e, 0x080c, 0x2f6c,
+ 0x00ee, 0x04b0, 0x0156, 0x00c6, 0x20a9, 0x007f, 0x900e, 0x0016,
+ 0x080c, 0x649f, 0x1538, 0xb800, 0xd0ec, 0x0520, 0x0046, 0xbaa0,
+ 0x2220, 0x9006, 0x2009, 0x0029, 0x080c, 0xd885, 0xb800, 0xc0e5,
+ 0xc0ec, 0xb802, 0x080c, 0x67bb, 0x2001, 0x0707, 0x1128, 0xb804,
+ 0x9084, 0x00ff, 0x9085, 0x0700, 0xb806, 0x2019, 0x0029, 0x080c,
+ 0x8803, 0x0076, 0x903e, 0x080c, 0x86f1, 0x900e, 0x080c, 0xd5f6,
+ 0x007e, 0x004e, 0x001e, 0x8108, 0x1f04, 0x6737, 0x00ce, 0x015e,
+ 0x00be, 0x0005, 0x00b6, 0x6010, 0x2058, 0xb800, 0xc0ec, 0xb802,
+ 0x00be, 0x0005, 0x7810, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0ac,
+ 0x0005, 0x6010, 0x00b6, 0x905d, 0x0108, 0xb800, 0x00be, 0xd0bc,
+ 0x0005, 0x00b6, 0x00f6, 0x2001, 0x107e, 0x2004, 0x905d, 0x0110,
+ 0xb800, 0xd0ec, 0x00fe, 0x00be, 0x0005, 0x0126, 0x0026, 0x2091,
+ 0x8000, 0x0006, 0xbaa0, 0x9290, 0x1000, 0x2204, 0x9b06, 0x190c,
+ 0x0dfa, 0x000e, 0xba00, 0x9005, 0x0110, 0xc2fd, 0x0008, 0xc2fc,
+ 0xba02, 0x002e, 0x012e, 0x0005, 0x2011, 0x1836, 0x2204, 0xd0cc,
+ 0x0138, 0x2001, 0x195b, 0x200c, 0x2011, 0x67b1, 0x080c, 0x836c,
+ 0x0005, 0x2011, 0x67b1, 0x080c, 0x82da, 0x2011, 0x1836, 0x2204,
+ 0xc0cc, 0x2012, 0x0005, 0x080c, 0x55db, 0xd0ac, 0x0005, 0x080c,
+ 0x55db, 0xd0a4, 0x0005, 0x0016, 0xb904, 0x9184, 0x00ff, 0x908e,
+ 0x0006, 0x001e, 0x0005, 0x0016, 0xb904, 0x9184, 0xff00, 0x8007,
+ 0x908e, 0x0006, 0x001e, 0x0005, 0x00b6, 0x00f6, 0x080c, 0xc539,
+ 0x0158, 0x70d8, 0x9084, 0x0028, 0x0138, 0x2001, 0x107f, 0x2004,
+ 0x905d, 0x0110, 0xb8bc, 0xd094, 0x00fe, 0x00be, 0x0005, 0x0006,
+ 0x0016, 0x0036, 0x0046, 0x0076, 0x00b6, 0x2001, 0x1817, 0x203c,
+ 0x9780, 0x32e9, 0x203d, 0x97bc, 0xff00, 0x873f, 0x9006, 0x2018,
+ 0x2008, 0x9284, 0x8000, 0x0110, 0x2019, 0x0001, 0x9294, 0x7fff,
+ 0x2100, 0x9706, 0x0190, 0x91a0, 0x1000, 0x2404, 0x905d, 0x0168,
+ 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x1138, 0x83ff, 0x0118,
+ 0xb89c, 0xd0a4, 0x0110, 0x8211, 0x0158, 0x8108, 0x83ff, 0x0120,
+ 0x9182, 0x0800, 0x0e28, 0x0068, 0x9182, 0x007e, 0x0e08, 0x0048,
+ 0x00be, 0x007e, 0x004e, 0x003e, 0x001e, 0x9085, 0x0001, 0x000e,
+ 0x0005, 0x00be, 0x007e, 0x004e, 0x003e, 0x001e, 0x9006, 0x000e,
+ 0x0005, 0x0046, 0x0056, 0x0076, 0x00b6, 0x2100, 0x9084, 0x7fff,
+ 0x9080, 0x1000, 0x2004, 0x905d, 0x0130, 0xb804, 0x9084, 0x00ff,
+ 0x9086, 0x0006, 0x0550, 0x9184, 0x8000, 0x0580, 0x2001, 0x1817,
+ 0x203c, 0x9780, 0x32e9, 0x203d, 0x97bc, 0xff00, 0x873f, 0x9006,
+ 0x2020, 0x2400, 0x9706, 0x01a0, 0x94a8, 0x1000, 0x2504, 0x905d,
+ 0x0178, 0xb804, 0x9084, 0x00ff, 0x9086, 0x0006, 0x1148, 0xb89c,
+ 0xd0a4, 0x0130, 0xb814, 0x9206, 0x1118, 0xb810, 0x9306, 0x0128,
+ 0x8420, 0x9482, 0x0800, 0x0e28, 0x0048, 0x918c, 0x7fff, 0x00be,
+ 0x007e, 0x005e, 0x004e, 0x9085, 0x0001, 0x0005, 0x918c, 0x7fff,
+ 0x00be, 0x007e, 0x005e, 0x004e, 0x9006, 0x0005, 0x2071, 0x190e,
+ 0x7003, 0x0001, 0x7007, 0x0000, 0x9006, 0x7012, 0x7016, 0x701a,
+ 0x701e, 0x700a, 0x7046, 0x0005, 0x0016, 0x00e6, 0x2071, 0x1921,
+ 0x900e, 0x710a, 0x080c, 0x55db, 0xd0fc, 0x1140, 0x080c, 0x55db,
+ 0x900e, 0xd09c, 0x0108, 0x8108, 0x7102, 0x0400, 0x2001, 0x187b,
+ 0x200c, 0x9184, 0x0007, 0x9006, 0x0002, 0x6896, 0x6896, 0x6896,
+ 0x6896, 0x6896, 0x68ad, 0x68bb, 0x6896, 0x7003, 0x0003, 0x2009,
+ 0x187c, 0x210c, 0x9184, 0xff00, 0x8007, 0x9005, 0x1110, 0x2001,
+ 0x0002, 0x7006, 0x0018, 0x7003, 0x0005, 0x0c88, 0x00ee, 0x001e,
+ 0x0005, 0x00e6, 0x2071, 0x0050, 0x684c, 0x9005, 0x1150, 0x00e6,
+ 0x2071, 0x190e, 0x7028, 0xc085, 0x702a, 0x00ee, 0x9085, 0x0001,
+ 0x0488, 0x6844, 0x9005, 0x0158, 0x080c, 0x7576, 0x6a60, 0x9200,
+ 0x7002, 0x6864, 0x9101, 0x7006, 0x9006, 0x7012, 0x7016, 0x6860,
+ 0x7002, 0x6864, 0x7006, 0x6868, 0x700a, 0x686c, 0x700e, 0x6844,
+ 0x9005, 0x1110, 0x7012, 0x7016, 0x684c, 0x701a, 0x701c, 0x9085,
+ 0x0040, 0x701e, 0x7037, 0x0019, 0x702b, 0x0001, 0x00e6, 0x2071,
+ 0x190e, 0x7028, 0xc084, 0x702a, 0x7007, 0x0001, 0x700b, 0x0000,
+ 0x00ee, 0x9006, 0x00ee, 0x0005, 0xa868, 0xd0fc, 0x11d8, 0x00e6,
+ 0x0026, 0x2001, 0x1921, 0x2004, 0x9005, 0x0904, 0x6aee, 0xa87c,
+ 0xd0bc, 0x1904, 0x6aee, 0xa978, 0xa874, 0x9105, 0x1904, 0x6aee,
+ 0x2001, 0x1921, 0x2004, 0x0002, 0x6aee, 0x6947, 0x6983, 0x6983,
+ 0x6aee, 0x6983, 0x0005, 0xa868, 0xd0fc, 0x1500, 0x00e6, 0x0026,
+ 0x2009, 0x1921, 0x210c, 0x81ff, 0x0904, 0x6aee, 0xa87c, 0xd0cc,
+ 0x0904, 0x6aee, 0xa880, 0x9084, 0x00ff, 0x9086, 0x0001, 0x1904,
+ 0x6aee, 0x9186, 0x0003, 0x0904, 0x6983, 0x9186, 0x0005, 0x0904,
+ 0x6983, 0xa84f, 0x8021, 0xa853, 0x0017, 0x0028, 0x0005, 0xa84f,
+ 0x8020, 0xa853, 0x0016, 0x2071, 0x190e, 0x701c, 0x9005, 0x1904,
+ 0x6ca2, 0x0e04, 0x6ced, 0x2071, 0x0000, 0xa84c, 0x7082, 0xa850,
+ 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0x2091, 0x4080,
+ 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x2071, 0x1800,
+ 0x2011, 0x0001, 0xa804, 0x900d, 0x702c, 0x1158, 0xa802, 0x2900,
+ 0x702e, 0x70bc, 0x9200, 0x70be, 0x080c, 0x81f0, 0x002e, 0x00ee,
+ 0x0005, 0x0096, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x009e, 0x0c58, 0xa84f, 0x0000, 0x00f6, 0x2079, 0x0050,
+ 0x2071, 0x190e, 0xa803, 0x0000, 0x7010, 0x9005, 0x1904, 0x6a72,
+ 0x782c, 0x908c, 0x0780, 0x190c, 0x6e16, 0x8004, 0x8004, 0x8004,
+ 0x9084, 0x0003, 0x0002, 0x69a1, 0x6a72, 0x69c6, 0x6a0d, 0x080c,
+ 0x0dfa, 0x2071, 0x1800, 0x2900, 0x7822, 0xa804, 0x900d, 0x1170,
+ 0x2071, 0x19d2, 0x703c, 0x9005, 0x1328, 0x2001, 0x1922, 0x2004,
+ 0x8005, 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c,
+ 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e,
+ 0x70bc, 0x9200, 0x70be, 0x080c, 0x81f0, 0x0c10, 0x2071, 0x1800,
+ 0x2900, 0x7822, 0xa804, 0x900d, 0x1580, 0x7824, 0x00e6, 0x2071,
+ 0x0040, 0x712c, 0xd19c, 0x1148, 0x2009, 0x182f, 0x210c, 0x918a,
+ 0x0040, 0x0218, 0x7022, 0x00ee, 0x0058, 0x00ee, 0x2048, 0x702c,
+ 0xa802, 0x2900, 0x702e, 0x70bc, 0x8000, 0x70be, 0x080c, 0x81f0,
+ 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd0a4, 0x19f0, 0x2071,
+ 0x19d2, 0x703c, 0x9005, 0x1328, 0x2001, 0x1922, 0x2004, 0x8005,
+ 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70bc,
+ 0x9200, 0x70be, 0x080c, 0x81f0, 0x0800, 0x0096, 0x00e6, 0x7824,
+ 0x2048, 0x2071, 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc,
+ 0x8000, 0x70be, 0x080c, 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x6e16, 0xd0a4, 0x1d60, 0x00ee, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x6e16, 0xd09c, 0x11a0, 0x009e, 0x2900, 0x7822, 0xa804, 0x900d,
+ 0x1560, 0x2071, 0x19d2, 0x703c, 0x9005, 0x1328, 0x2001, 0x1922,
+ 0x2004, 0x8005, 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x009e,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1170, 0x2071,
+ 0x19d2, 0x703c, 0x9005, 0x1328, 0x2001, 0x1922, 0x2004, 0x8005,
+ 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016,
+ 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8,
+ 0x702e, 0x70bc, 0x9200, 0x70be, 0x080c, 0x81f0, 0x00fe, 0x002e,
+ 0x00ee, 0x0005, 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d,
+ 0x711a, 0x0110, 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d,
+ 0x1904, 0x6ac7, 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd09c,
+ 0x1198, 0x701c, 0x904d, 0x0180, 0x7010, 0x8001, 0x7012, 0x1108,
+ 0x701a, 0xa800, 0x701e, 0x2900, 0x7822, 0x782c, 0x9094, 0x0780,
+ 0x190c, 0x6e16, 0xd09c, 0x0d68, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x6e16, 0xd0a4, 0x01b0, 0x00e6, 0x7824, 0x2048, 0x2071, 0x1800,
+ 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc, 0x8000, 0x70be, 0x080c,
+ 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd0a4, 0x1d60,
+ 0x00ee, 0x2071, 0x19d2, 0x703c, 0x9005, 0x1328, 0x2001, 0x1922,
+ 0x2004, 0x8005, 0x703e, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x00e6,
+ 0x2071, 0x1800, 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210,
+ 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70bc, 0x9200, 0x70be, 0x080c,
+ 0x81f0, 0x00ee, 0x0804, 0x6a82, 0xa868, 0xd0fc, 0x1904, 0x6b2a,
+ 0x0096, 0xa804, 0xa807, 0x0000, 0x904d, 0x190c, 0x0fe3, 0x009e,
+ 0x0018, 0xa868, 0xd0fc, 0x15f0, 0x00e6, 0x0026, 0xa84f, 0x0000,
+ 0x00f6, 0x2079, 0x0050, 0x2071, 0x1800, 0x70e8, 0x8001, 0x01d0,
+ 0x1678, 0x2071, 0x190e, 0xa803, 0x0000, 0x7010, 0x9005, 0x1904,
+ 0x6c20, 0x782c, 0x908c, 0x0780, 0x190c, 0x6e16, 0x8004, 0x8004,
+ 0x8004, 0x9084, 0x0003, 0x0002, 0x6b2b, 0x6c20, 0x6b46, 0x6bb3,
+ 0x080c, 0x0dfa, 0x70eb, 0x0fa0, 0x71e4, 0x8107, 0x9106, 0x9094,
+ 0x00c0, 0x9184, 0xff3f, 0x9205, 0x70e6, 0x3b08, 0x3a00, 0x9104,
+ 0x918d, 0x00c0, 0x21d8, 0x9084, 0xff3f, 0x9205, 0x20d0, 0x0888,
+ 0x70ea, 0x0878, 0x0005, 0x2071, 0x1800, 0x2900, 0x7822, 0xa804,
+ 0x900d, 0x1120, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x9016, 0x702c,
+ 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e,
+ 0x70bc, 0x9200, 0x70be, 0x080c, 0x81f0, 0x0c60, 0x2071, 0x1800,
+ 0x2900, 0x7822, 0xa804, 0x900d, 0x1904, 0x6ba2, 0x7830, 0x8007,
+ 0x9084, 0x001f, 0x9082, 0x0001, 0x1220, 0x00fe, 0x002e, 0x00ee,
+ 0x0005, 0x7824, 0x00e6, 0x2071, 0x0040, 0x712c, 0xd19c, 0x1148,
+ 0x2009, 0x182f, 0x210c, 0x918a, 0x0040, 0x0218, 0x7022, 0x00ee,
+ 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc,
+ 0x8000, 0x70be, 0x080c, 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x6e16, 0xd0a4, 0x19f0, 0x0e04, 0x6b99, 0x7838, 0x7938, 0x910e,
+ 0x1de0, 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de,
+ 0x2001, 0x191f, 0x200c, 0xc184, 0x2102, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x00fe, 0x002e, 0x00ee,
+ 0x0005, 0x2001, 0x191f, 0x200c, 0xc185, 0x2102, 0x00fe, 0x002e,
+ 0x00ee, 0x0005, 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210,
+ 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70bc, 0x9200, 0x70be, 0x080c,
+ 0x81f0, 0x0804, 0x6b59, 0x0096, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc, 0x8000, 0x70be,
+ 0x080c, 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd0a4,
+ 0x1d60, 0x00ee, 0x0e04, 0x6bf3, 0x7838, 0x7938, 0x910e, 0x1de0,
+ 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x7044,
+ 0xc084, 0x7046, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11e0, 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd09c,
+ 0x1170, 0x009e, 0x2900, 0x7822, 0xa804, 0x900d, 0x11e0, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x7044, 0xc085, 0x7046, 0x0c58, 0x009e,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1120, 0x00fe,
+ 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70bc,
+ 0x9200, 0x70be, 0x080c, 0x81f0, 0x00fe, 0x002e, 0x00ee, 0x0005,
+ 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110,
+ 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1904, 0x6c8d,
+ 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd09c, 0x11b0, 0x701c,
+ 0x904d, 0x0198, 0xa84c, 0x9005, 0x1180, 0x7010, 0x8001, 0x7012,
+ 0x1108, 0x701a, 0xa800, 0x701e, 0x2900, 0x7822, 0x782c, 0x9094,
+ 0x0780, 0x190c, 0x6e16, 0xd09c, 0x0d50, 0x782c, 0x9094, 0x0780,
+ 0x190c, 0x6e16, 0xd0a4, 0x05a8, 0x00e6, 0x7824, 0x2048, 0x2071,
+ 0x1800, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc, 0x8000, 0x70be,
+ 0x080c, 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c, 0x6e16, 0xd0a4,
+ 0x1d60, 0x00ee, 0x0e04, 0x6c86, 0x7838, 0x7938, 0x910e, 0x1de0,
+ 0x00d6, 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x7044,
+ 0xc084, 0x7046, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084,
+ 0x190c, 0x11e0, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x7044, 0xc085,
+ 0x7046, 0x00fe, 0x002e, 0x00ee, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x9016, 0x702c, 0x2148, 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff,
+ 0x1dc8, 0x702e, 0x70bc, 0x9200, 0x70be, 0x080c, 0x81f0, 0x00ee,
+ 0x0804, 0x6c30, 0x2071, 0x190e, 0xa803, 0x0000, 0x2908, 0x7010,
+ 0x8000, 0x7012, 0x7018, 0x904d, 0x711a, 0x0110, 0xa902, 0x0008,
+ 0x711e, 0x2148, 0xa804, 0x900d, 0x1128, 0x1e04, 0x6ccd, 0x002e,
+ 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148, 0xa904,
+ 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70bc, 0x9200,
+ 0x70be, 0x080c, 0x81f0, 0x0e04, 0x6cb7, 0x2071, 0x190e, 0x701c,
+ 0x2048, 0xa84c, 0x900d, 0x0d18, 0x2071, 0x0000, 0x7182, 0xa850,
+ 0x7032, 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0x2091, 0x4080,
+ 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x2071, 0x190e,
+ 0x080c, 0x6e02, 0x002e, 0x00ee, 0x0005, 0x2071, 0x190e, 0xa803,
+ 0x0000, 0x2908, 0x7010, 0x8000, 0x7012, 0x7018, 0x904d, 0x711a,
+ 0x0110, 0xa902, 0x0008, 0x711e, 0x2148, 0xa804, 0x900d, 0x1118,
+ 0x002e, 0x00ee, 0x0005, 0x2071, 0x1800, 0x9016, 0x702c, 0x2148,
+ 0xa904, 0xa802, 0x8210, 0x2900, 0x81ff, 0x1dc8, 0x702e, 0x70bc,
+ 0x9200, 0x70be, 0x080c, 0x81f0, 0x002e, 0x00ee, 0x0005, 0x0006,
+ 0xa87c, 0x0006, 0xa867, 0x0103, 0x20a9, 0x001c, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x001d, 0x20a0, 0x9006, 0x4004, 0x000e, 0x9084,
+ 0x00ff, 0xa87e, 0x000e, 0xa87a, 0xa982, 0x0005, 0x2071, 0x190e,
+ 0x7004, 0x0002, 0x6d3a, 0x6d3b, 0x6e01, 0x6d3b, 0x6d38, 0x6e01,
+ 0x080c, 0x0dfa, 0x0005, 0x2001, 0x1921, 0x2004, 0x0002, 0x6d45,
+ 0x6d45, 0x6d9a, 0x6d9b, 0x6d45, 0x6d9b, 0x0126, 0x2091, 0x8000,
+ 0x1e0c, 0x6e21, 0x701c, 0x904d, 0x01e0, 0xa84c, 0x9005, 0x01d8,
+ 0x0e04, 0x6d69, 0xa94c, 0x2071, 0x0000, 0x7182, 0xa850, 0x7032,
+ 0xa86c, 0x7086, 0x7036, 0xa870, 0x708a, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x2071, 0x190e, 0x080c,
+ 0x6e02, 0x012e, 0x0470, 0x2001, 0x005b, 0x2004, 0x9094, 0x0780,
+ 0x190c, 0x6e16, 0xd09c, 0x2071, 0x190e, 0x1510, 0x2071, 0x190e,
+ 0x700f, 0x0001, 0xa964, 0x9184, 0x00ff, 0x9086, 0x0003, 0x1130,
+ 0x810f, 0x918c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x2900, 0x00d6,
+ 0x2069, 0x0050, 0x6822, 0x00de, 0x2071, 0x190e, 0x701c, 0x2048,
+ 0x7010, 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a,
+ 0x012e, 0x0005, 0x0005, 0x00d6, 0x2008, 0x2069, 0x19d2, 0x683c,
+ 0x9005, 0x0760, 0x0158, 0x9186, 0x0003, 0x0540, 0x2001, 0x1814,
+ 0x2004, 0x2009, 0x1aa2, 0x210c, 0x9102, 0x1500, 0x0126, 0x2091,
+ 0x8000, 0x2069, 0x0050, 0x693c, 0x6838, 0x9106, 0x0190, 0x0e04,
+ 0x6dcd, 0x2069, 0x0000, 0x6837, 0x8040, 0x6833, 0x0012, 0x6883,
+ 0x8040, 0x2091, 0x4080, 0x2001, 0x0089, 0x2004, 0xd084, 0x190c,
+ 0x11e0, 0x2069, 0x19d2, 0x683f, 0xffff, 0x012e, 0x00de, 0x0126,
+ 0x2091, 0x8000, 0x1e0c, 0x6e82, 0x701c, 0x904d, 0x0540, 0x2001,
+ 0x005b, 0x2004, 0x9094, 0x0780, 0x15c9, 0xd09c, 0x1500, 0x2071,
+ 0x190e, 0x700f, 0x0001, 0xa964, 0x9184, 0x00ff, 0x9086, 0x0003,
+ 0x1130, 0x810f, 0x918c, 0x00ff, 0x8101, 0x0108, 0x710e, 0x2900,
+ 0x00d6, 0x2069, 0x0050, 0x6822, 0x00de, 0x701c, 0x2048, 0x7010,
+ 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a, 0x012e,
+ 0x0005, 0x0005, 0x0126, 0x2091, 0x8000, 0x701c, 0x904d, 0x0160,
+ 0x7010, 0x8001, 0x7012, 0xa800, 0x701e, 0x9005, 0x1108, 0x701a,
+ 0x012e, 0x080c, 0x1063, 0x0005, 0x012e, 0x0005, 0x2091, 0x8000,
+ 0x0e04, 0x6e18, 0x0006, 0x0016, 0x2001, 0x8004, 0x0006, 0x0804,
+ 0x0e03, 0x0096, 0x00f6, 0x2079, 0x0050, 0x7044, 0xd084, 0x01c0,
+ 0xc084, 0x7046, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069,
+ 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x00fe, 0x009e, 0x0005,
+ 0x782c, 0x9094, 0x0780, 0x1991, 0xd0a4, 0x0db8, 0x00e6, 0x2071,
+ 0x1800, 0x7824, 0x00e6, 0x2071, 0x0040, 0x712c, 0xd19c, 0x1148,
+ 0x2009, 0x182f, 0x210c, 0x918a, 0x0040, 0x0218, 0x7022, 0x00ee,
+ 0x0058, 0x00ee, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc,
+ 0x8000, 0x70be, 0x080c, 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x6e16, 0xd0a4, 0x19f0, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6,
+ 0x2069, 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080,
+ 0x2001, 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x00ee, 0x00fe,
+ 0x009e, 0x0005, 0x00f6, 0x2079, 0x0050, 0x7044, 0xd084, 0x01b8,
+ 0xc084, 0x7046, 0x7838, 0x7938, 0x910e, 0x1de0, 0x00d6, 0x2069,
+ 0x0000, 0x6836, 0x6833, 0x0013, 0x00de, 0x2091, 0x4080, 0x2001,
+ 0x0089, 0x2004, 0xd084, 0x190c, 0x11e0, 0x00fe, 0x0005, 0x782c,
+ 0x9094, 0x0780, 0x190c, 0x6e16, 0xd0a4, 0x0db8, 0x00e6, 0x2071,
+ 0x1800, 0x7824, 0x2048, 0x702c, 0xa802, 0x2900, 0x702e, 0x70bc,
+ 0x8000, 0x70be, 0x080c, 0x81f0, 0x782c, 0x9094, 0x0780, 0x190c,
+ 0x6e16, 0xd0a4, 0x1d70, 0x00d6, 0x2069, 0x0050, 0x693c, 0x2069,
+ 0x1921, 0x6808, 0x690a, 0x2069, 0x19d2, 0x9102, 0x1118, 0x683c,
+ 0x9005, 0x1328, 0x2001, 0x1922, 0x200c, 0x810d, 0x693e, 0x00de,
+ 0x00ee, 0x00fe, 0x0005, 0x7094, 0x908a, 0x0029, 0x1a0c, 0x0dfa,
+ 0x9082, 0x001d, 0x001b, 0x6027, 0x1e00, 0x0005, 0x6faa, 0x6f30,
+ 0x6f4c, 0x6f76, 0x6f99, 0x6fd9, 0x6feb, 0x6f4c, 0x6fc1, 0x6eeb,
+ 0x6f19, 0x6eea, 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0x9005,
+ 0x1180, 0x6808, 0x9005, 0x1518, 0x7097, 0x0028, 0x2069, 0x1967,
+ 0x2d04, 0x7002, 0x080c, 0x7359, 0x6028, 0x9085, 0x0600, 0x602a,
+ 0x00b0, 0x7097, 0x0028, 0x2069, 0x1967, 0x2d04, 0x7002, 0x6028,
+ 0x9085, 0x0600, 0x602a, 0x00e6, 0x0036, 0x0046, 0x0056, 0x2071,
+ 0x1a3a, 0x080c, 0x19ff, 0x005e, 0x004e, 0x003e, 0x00ee, 0x00de,
+ 0x0005, 0x00d6, 0x2069, 0x0200, 0x6804, 0x9005, 0x1178, 0x6808,
+ 0x9005, 0x1160, 0x7097, 0x0028, 0x2069, 0x1967, 0x2d04, 0x7002,
+ 0x080c, 0x73f3, 0x6028, 0x9085, 0x0600, 0x602a, 0x00de, 0x0005,
+ 0x0006, 0x2001, 0x0090, 0x080c, 0x2c88, 0x000e, 0x6124, 0xd1e4,
+ 0x1190, 0x080c, 0x7058, 0xd1d4, 0x1160, 0xd1dc, 0x1138, 0xd1cc,
+ 0x0150, 0x7097, 0x0020, 0x080c, 0x7058, 0x0028, 0x7097, 0x001d,
+ 0x0010, 0x7097, 0x001f, 0x0005, 0x2001, 0x0088, 0x080c, 0x2c88,
+ 0x6124, 0xd1cc, 0x11e8, 0xd1dc, 0x11c0, 0xd1e4, 0x1198, 0x9184,
+ 0x1e00, 0x11d8, 0x080c, 0x1a24, 0x60e3, 0x0001, 0x600c, 0xc0b4,
+ 0x600e, 0x080c, 0x7233, 0x2001, 0x0080, 0x080c, 0x2c88, 0x7097,
+ 0x0028, 0x0058, 0x7097, 0x001e, 0x0040, 0x7097, 0x001d, 0x0028,
+ 0x7097, 0x0020, 0x0010, 0x7097, 0x001f, 0x0005, 0x080c, 0x1a24,
+ 0x60e3, 0x0001, 0x600c, 0xc0b4, 0x600e, 0x080c, 0x7233, 0x2001,
+ 0x0080, 0x080c, 0x2c88, 0x6124, 0xd1d4, 0x1180, 0xd1dc, 0x1158,
+ 0xd1e4, 0x1130, 0x9184, 0x1e00, 0x1158, 0x7097, 0x0028, 0x0040,
+ 0x7097, 0x001e, 0x0028, 0x7097, 0x001d, 0x0010, 0x7097, 0x001f,
+ 0x0005, 0x2001, 0x00a0, 0x080c, 0x2c88, 0x6124, 0xd1dc, 0x1138,
+ 0xd1e4, 0x0138, 0x080c, 0x1a24, 0x7097, 0x001e, 0x0010, 0x7097,
+ 0x001d, 0x0005, 0x080c, 0x70db, 0x6124, 0xd1dc, 0x1188, 0x080c,
+ 0x7058, 0x0016, 0x080c, 0x1a24, 0x001e, 0xd1d4, 0x1128, 0xd1e4,
+ 0x0138, 0x7097, 0x001e, 0x0020, 0x7097, 0x001f, 0x080c, 0x7058,
+ 0x0005, 0x0006, 0x2001, 0x00a0, 0x080c, 0x2c88, 0x000e, 0x6124,
+ 0xd1d4, 0x1160, 0xd1cc, 0x1150, 0xd1dc, 0x1128, 0xd1e4, 0x0140,
+ 0x7097, 0x001e, 0x0028, 0x7097, 0x001d, 0x0010, 0x7097, 0x0021,
+ 0x0005, 0x080c, 0x70db, 0x6124, 0xd1d4, 0x1150, 0xd1dc, 0x1128,
+ 0xd1e4, 0x0140, 0x7097, 0x001e, 0x0028, 0x7097, 0x001d, 0x0010,
+ 0x7097, 0x001f, 0x0005, 0x0006, 0x2001, 0x0090, 0x080c, 0x2c88,
+ 0x000e, 0x6124, 0xd1d4, 0x1178, 0xd1cc, 0x1150, 0xd1dc, 0x1128,
+ 0xd1e4, 0x0158, 0x7097, 0x001e, 0x0040, 0x7097, 0x001d, 0x0028,
+ 0x7097, 0x0020, 0x0010, 0x7097, 0x001f, 0x0005, 0x0016, 0x00c6,
+ 0x00d6, 0x00e6, 0x0126, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071,
+ 0x1800, 0x2091, 0x8000, 0x080c, 0x7207, 0x11d8, 0x2001, 0x180c,
+ 0x200c, 0xd1b4, 0x01b0, 0xc1b4, 0x2102, 0x6027, 0x0200, 0x080c,
+ 0x2bb0, 0x6024, 0xd0cc, 0x0148, 0x2001, 0x00a0, 0x080c, 0x2c88,
+ 0x080c, 0x7504, 0x080c, 0x5f2b, 0x0428, 0x6028, 0xc0cd, 0x602a,
+ 0x0408, 0x080c, 0x7221, 0x0150, 0x080c, 0x7218, 0x1138, 0x2001,
+ 0x0001, 0x080c, 0x2717, 0x080c, 0x71df, 0x00a0, 0x080c, 0x70d8,
+ 0x0178, 0x2001, 0x0001, 0x080c, 0x2717, 0x7094, 0x9086, 0x001e,
+ 0x0120, 0x7094, 0x9086, 0x0022, 0x1118, 0x7097, 0x0025, 0x0010,
+ 0x7097, 0x0021, 0x012e, 0x00ee, 0x00de, 0x00ce, 0x001e, 0x0005,
+ 0x0026, 0x2011, 0x7069, 0x080c, 0x83ae, 0x002e, 0x0016, 0x0026,
+ 0x2009, 0x0064, 0x2011, 0x7069, 0x080c, 0x83a5, 0x002e, 0x001e,
+ 0x0005, 0x00e6, 0x00f6, 0x0016, 0x080c, 0x9656, 0x2071, 0x1800,
+ 0x080c, 0x7006, 0x001e, 0x00fe, 0x00ee, 0x0005, 0x0016, 0x0026,
+ 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x0126, 0x080c, 0x9656,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x2091, 0x8000,
+ 0x6028, 0xc09c, 0x602a, 0x2011, 0x0003, 0x080c, 0x9a0f, 0x2011,
+ 0x0002, 0x080c, 0x9a19, 0x080c, 0x9927, 0x080c, 0x835a, 0x0036,
+ 0x901e, 0x080c, 0x999d, 0x003e, 0x60e3, 0x0000, 0x080c, 0xdc13,
+ 0x080c, 0xdc2e, 0x2009, 0x0004, 0x080c, 0x2bb6, 0x080c, 0x2a89,
+ 0x2001, 0x1800, 0x2003, 0x0004, 0x6027, 0x0008, 0x2011, 0x7069,
+ 0x080c, 0x83ae, 0x080c, 0x7221, 0x0118, 0x9006, 0x080c, 0x2c88,
+ 0x080c, 0x0b8f, 0x2001, 0x0001, 0x080c, 0x2717, 0x012e, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e, 0x0005, 0x0026,
+ 0x00e6, 0x2011, 0x7076, 0x2071, 0x19d2, 0x701c, 0x9206, 0x1118,
+ 0x7018, 0x9005, 0x0110, 0x9085, 0x0001, 0x00ee, 0x002e, 0x0005,
+ 0x6020, 0xd09c, 0x0005, 0x6800, 0x9084, 0xfffe, 0x9086, 0x00c0,
+ 0x01b8, 0x2001, 0x00c0, 0x080c, 0x2c88, 0x0156, 0x20a9, 0x002d,
+ 0x1d04, 0x70e8, 0x2091, 0x6000, 0x1f04, 0x70e8, 0x015e, 0x00d6,
+ 0x2069, 0x1800, 0x6898, 0x8001, 0x0220, 0x0118, 0x689a, 0x00de,
+ 0x0005, 0x689b, 0x0014, 0x68e4, 0xd0dc, 0x0dc8, 0x6800, 0x9086,
+ 0x0001, 0x1da8, 0x080c, 0x83ba, 0x0c90, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x080c, 0x7513,
+ 0x2001, 0x1945, 0x2003, 0x0000, 0x9006, 0x7096, 0x60e2, 0x6886,
+ 0x080c, 0x27e2, 0x9006, 0x080c, 0x2c88, 0x080c, 0x5dea, 0x6027,
+ 0xffff, 0x602b, 0x182f, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6,
+ 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800,
+ 0x2001, 0x1955, 0x200c, 0x9186, 0x0000, 0x0158, 0x9186, 0x0001,
+ 0x0158, 0x9186, 0x0002, 0x0158, 0x9186, 0x0003, 0x0158, 0x0804,
+ 0x71cf, 0x7097, 0x0022, 0x0040, 0x7097, 0x0021, 0x0028, 0x7097,
+ 0x0023, 0x0010, 0x7097, 0x0024, 0x60e3, 0x0000, 0x6887, 0x0001,
+ 0x2001, 0x0001, 0x080c, 0x27e2, 0x0026, 0x080c, 0xa069, 0x002e,
+ 0x7000, 0x908e, 0x0004, 0x0118, 0x602b, 0x0028, 0x0010, 0x602b,
+ 0x0020, 0x0156, 0x0126, 0x2091, 0x8000, 0x20a9, 0x0005, 0x6024,
+ 0xd0ac, 0x0150, 0x012e, 0x015e, 0x080c, 0xc539, 0x0118, 0x9006,
+ 0x080c, 0x2cb2, 0x0804, 0x71db, 0x6800, 0x9084, 0x00a1, 0xc0bd,
+ 0x6802, 0x080c, 0x2bb0, 0x6904, 0xd1d4, 0x1140, 0x2001, 0x0100,
+ 0x080c, 0x2c88, 0x1f04, 0x7167, 0x080c, 0x725e, 0x012e, 0x015e,
+ 0x080c, 0x7218, 0x0538, 0x6044, 0x9005, 0x01f8, 0x2001, 0x0100,
+ 0x2004, 0x9086, 0x000a, 0x0158, 0x2011, 0x0114, 0x2204, 0x9085,
+ 0x0100, 0x2012, 0x6050, 0x0006, 0x9085, 0x0020, 0x6052, 0x080c,
+ 0x725e, 0x9006, 0x8001, 0x1df0, 0x2001, 0x0100, 0x2004, 0x9086,
+ 0x000a, 0x0140, 0x000e, 0x6052, 0x0028, 0x6804, 0xd0d4, 0x1110,
+ 0x080c, 0x725e, 0x080c, 0xc539, 0x0118, 0x9006, 0x080c, 0x2cb2,
+ 0x0016, 0x0026, 0x7000, 0x908e, 0x0004, 0x0130, 0x2009, 0x00c8,
+ 0x2011, 0x7076, 0x080c, 0x836c, 0x002e, 0x001e, 0x080c, 0x81e7,
+ 0x7034, 0xc085, 0x7036, 0x2001, 0x1955, 0x2003, 0x0004, 0x080c,
+ 0x6ed3, 0x080c, 0x7218, 0x0138, 0x6804, 0xd0d4, 0x1120, 0xd0dc,
+ 0x1100, 0x080c, 0x7509, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6,
+ 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800,
+ 0x080c, 0x81fe, 0x080c, 0x81f0, 0x080c, 0x7513, 0x2001, 0x1945,
+ 0x2003, 0x0000, 0x9006, 0x7096, 0x60e2, 0x6886, 0x080c, 0x27e2,
+ 0x9006, 0x080c, 0x2c88, 0x6043, 0x0090, 0x6043, 0x0010, 0x6027,
+ 0xffff, 0x602b, 0x182f, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0006,
+ 0x2001, 0x1954, 0x2004, 0x9086, 0xaaaa, 0x000e, 0x0005, 0x0006,
+ 0x080c, 0x55df, 0x9084, 0x0030, 0x9086, 0x0000, 0x000e, 0x0005,
+ 0x0006, 0x080c, 0x55df, 0x9084, 0x0030, 0x9086, 0x0030, 0x000e,
+ 0x0005, 0x0006, 0x080c, 0x55df, 0x9084, 0x0030, 0x9086, 0x0010,
+ 0x000e, 0x0005, 0x0006, 0x080c, 0x55df, 0x9084, 0x0030, 0x9086,
+ 0x0020, 0x000e, 0x0005, 0x0036, 0x0016, 0x2001, 0x180c, 0x2004,
+ 0x908c, 0x0013, 0x0180, 0x0020, 0x080c, 0x2802, 0x900e, 0x0028,
+ 0x080c, 0x67bb, 0x1dc8, 0x2009, 0x0002, 0x2019, 0x0028, 0x080c,
+ 0x3156, 0x9006, 0x0019, 0x001e, 0x003e, 0x0005, 0x00e6, 0x2071,
+ 0x180c, 0x2e04, 0x0130, 0x080c, 0xc532, 0x1128, 0x9085, 0x0010,
+ 0x0010, 0x9084, 0xffef, 0x2072, 0x00ee, 0x0005, 0x6050, 0x0006,
+ 0x60ec, 0x0006, 0x600c, 0x0006, 0x6004, 0x0006, 0x6028, 0x0006,
+ 0x2001, 0x0100, 0x2004, 0x9086, 0x000a, 0x0510, 0x0016, 0x6138,
+ 0x6050, 0x9084, 0xfbff, 0x9085, 0x2000, 0x6052, 0x613a, 0x20a9,
+ 0x0012, 0x1d04, 0x7279, 0x2091, 0x6000, 0x1f04, 0x7279, 0x602f,
+ 0x0100, 0x602f, 0x0000, 0x6050, 0x9085, 0x0400, 0x9084, 0xdfff,
+ 0x6052, 0x613a, 0x001e, 0x602f, 0x0040, 0x602f, 0x0000, 0x00a0,
+ 0x080c, 0x2cc2, 0x080c, 0x2cf5, 0x602f, 0x0100, 0x602f, 0x0000,
+ 0x602f, 0x0040, 0x602f, 0x0000, 0x20a9, 0x0002, 0x080c, 0x2b91,
+ 0x0026, 0x6027, 0x0040, 0x002e, 0x000e, 0x602a, 0x000e, 0x6006,
+ 0x000e, 0x600e, 0x000e, 0x60ee, 0x60e3, 0x0000, 0x6887, 0x0001,
+ 0x2001, 0x0001, 0x080c, 0x27e2, 0x2001, 0x00a0, 0x0006, 0x080c,
+ 0xc539, 0x000e, 0x0130, 0x080c, 0x2ca6, 0x9006, 0x080c, 0x2cb2,
+ 0x0010, 0x080c, 0x2c88, 0x000e, 0x6052, 0x6050, 0x0006, 0xc0e5,
+ 0x6052, 0x00f6, 0x2079, 0x0100, 0x080c, 0x2b06, 0x00fe, 0x000e,
+ 0x6052, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0x1800, 0x6020,
+ 0x9084, 0x0080, 0x0138, 0x2001, 0x180c, 0x200c, 0xc1c5, 0x2102,
+ 0x0804, 0x734b, 0x2001, 0x180c, 0x200c, 0xc1c4, 0x2102, 0x6028,
+ 0x9084, 0xe1ff, 0x602a, 0x6027, 0x0200, 0x2001, 0x0090, 0x080c,
+ 0x2c88, 0x20a9, 0x0366, 0x6024, 0xd0cc, 0x1518, 0x1d04, 0x72fb,
+ 0x2091, 0x6000, 0x1f04, 0x72fb, 0x2011, 0x0003, 0x080c, 0x9a0f,
+ 0x2011, 0x0002, 0x080c, 0x9a19, 0x080c, 0x9927, 0x901e, 0x080c,
+ 0x999d, 0x2001, 0x00a0, 0x080c, 0x2c88, 0x080c, 0x7504, 0x080c,
+ 0x5f2b, 0x080c, 0xc539, 0x0110, 0x080c, 0x0d68, 0x9085, 0x0001,
+ 0x0480, 0x080c, 0x1a24, 0x60e3, 0x0000, 0x2001, 0x0002, 0x080c,
+ 0x27e2, 0x60e2, 0x2001, 0x0080, 0x080c, 0x2c88, 0x20a9, 0x0366,
+ 0x6027, 0x1e00, 0x2009, 0x1e00, 0x080c, 0x2bb0, 0x6024, 0x910c,
+ 0x0138, 0x1d04, 0x7330, 0x2091, 0x6000, 0x1f04, 0x7330, 0x0820,
+ 0x6028, 0x9085, 0x1e00, 0x602a, 0x70b0, 0x9005, 0x1118, 0x6887,
+ 0x0001, 0x0008, 0x6886, 0x080c, 0xc539, 0x0110, 0x080c, 0x0d68,
+ 0x9006, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e, 0x015e,
+ 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2071, 0x1800, 0x7000, 0x9086, 0x0003, 0x1168,
+ 0x2001, 0x020b, 0x2004, 0x9084, 0x5540, 0x9086, 0x5540, 0x1128,
+ 0x2069, 0x1a50, 0x2d04, 0x8000, 0x206a, 0x2069, 0x0140, 0x6020,
+ 0x9084, 0x00c0, 0x0120, 0x6884, 0x9005, 0x1904, 0x73be, 0x2001,
+ 0x0088, 0x080c, 0x2c88, 0x9006, 0x60e2, 0x6886, 0x080c, 0x27e2,
+ 0x2069, 0x0200, 0x6804, 0x9005, 0x1118, 0x6808, 0x9005, 0x01c0,
+ 0x6028, 0x9084, 0xfbff, 0x602a, 0x6027, 0x0400, 0x2069, 0x1967,
+ 0x7000, 0x206a, 0x7097, 0x0026, 0x7003, 0x0001, 0x20a9, 0x0002,
+ 0x1d04, 0x73a0, 0x2091, 0x6000, 0x1f04, 0x73a0, 0x0804, 0x73eb,
+ 0x2069, 0x0140, 0x20a9, 0x0384, 0x6027, 0x1e00, 0x2009, 0x1e00,
+ 0x080c, 0x2bb0, 0x6024, 0x910c, 0x0508, 0x9084, 0x1a00, 0x11f0,
+ 0x1d04, 0x73ac, 0x2091, 0x6000, 0x1f04, 0x73ac, 0x2011, 0x0003,
+ 0x080c, 0x9a0f, 0x2011, 0x0002, 0x080c, 0x9a19, 0x080c, 0x9927,
+ 0x901e, 0x080c, 0x999d, 0x2001, 0x00a0, 0x080c, 0x2c88, 0x080c,
+ 0x7504, 0x080c, 0x5f2b, 0x9085, 0x0001, 0x00a8, 0x2001, 0x0080,
+ 0x080c, 0x2c88, 0x2069, 0x0140, 0x60e3, 0x0000, 0x70b0, 0x9005,
+ 0x1118, 0x6887, 0x0001, 0x0008, 0x6886, 0x2001, 0x0002, 0x080c,
+ 0x27e2, 0x60e2, 0x9006, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e,
+ 0x001e, 0x015e, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6,
+ 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2071, 0x1800, 0x6020, 0x9084,
+ 0x00c0, 0x01c8, 0x2011, 0x0003, 0x080c, 0x9a0f, 0x2011, 0x0002,
+ 0x080c, 0x9a19, 0x080c, 0x9927, 0x901e, 0x080c, 0x999d, 0x2069,
+ 0x0140, 0x2001, 0x00a0, 0x080c, 0x2c88, 0x080c, 0x7504, 0x080c,
+ 0x5f2b, 0x0804, 0x7485, 0x2001, 0x180c, 0x200c, 0xd1b4, 0x1160,
+ 0xc1b5, 0x2102, 0x080c, 0x705e, 0x2069, 0x0140, 0x2001, 0x0080,
+ 0x080c, 0x2c88, 0x60e3, 0x0000, 0x2069, 0x0200, 0x6804, 0x9005,
+ 0x1118, 0x6808, 0x9005, 0x0180, 0x6028, 0x9084, 0xfdff, 0x602a,
+ 0x6027, 0x0200, 0x2069, 0x1967, 0x7000, 0x206a, 0x7097, 0x0027,
+ 0x7003, 0x0001, 0x0804, 0x7485, 0x6027, 0x1e00, 0x2009, 0x1e00,
+ 0x080c, 0x2bb0, 0x6024, 0x910c, 0x01c8, 0x9084, 0x1c00, 0x11b0,
+ 0x1d04, 0x7444, 0x0006, 0x0016, 0x00c6, 0x00d6, 0x00e6, 0x080c,
+ 0x823e, 0x00ee, 0x00de, 0x00ce, 0x001e, 0x000e, 0x00e6, 0x2071,
+ 0x19d2, 0x7070, 0x00ee, 0x9005, 0x19f8, 0x00f8, 0x0026, 0x2011,
+ 0x7076, 0x080c, 0x82da, 0x2011, 0x7069, 0x080c, 0x83ae, 0x002e,
+ 0x2069, 0x0140, 0x60e3, 0x0000, 0x70b0, 0x9005, 0x1118, 0x6887,
+ 0x0001, 0x0008, 0x6886, 0x2001, 0x0002, 0x080c, 0x27e2, 0x60e2,
+ 0x2001, 0x180c, 0x200c, 0xc1b4, 0x2102, 0x00ee, 0x00de, 0x00ce,
+ 0x003e, 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016, 0x0026,
+ 0x0036, 0x0046, 0x00c6, 0x00e6, 0x2061, 0x0100, 0x2071, 0x1800,
+ 0x080c, 0xc532, 0x1904, 0x74f2, 0x7130, 0xd184, 0x1170, 0x080c,
+ 0x32e4, 0x0138, 0xc18d, 0x7132, 0x2011, 0x185c, 0x2214, 0xd2ac,
+ 0x1120, 0x7030, 0xd08c, 0x0904, 0x74f2, 0x2011, 0x185c, 0x220c,
+ 0x0438, 0x0016, 0x2019, 0x000e, 0x080c, 0xd801, 0x0156, 0x00b6,
+ 0x20a9, 0x007f, 0x900e, 0x9186, 0x007e, 0x01a0, 0x9186, 0x0080,
+ 0x0188, 0x080c, 0x649f, 0x1170, 0x2120, 0x9006, 0x0016, 0x2009,
+ 0x000e, 0x080c, 0xd885, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c,
+ 0x84d1, 0x001e, 0x8108, 0x1f04, 0x74bb, 0x00be, 0x015e, 0x001e,
+ 0xd1ac, 0x1148, 0x0016, 0x2009, 0x0002, 0x2019, 0x0004, 0x080c,
+ 0x3156, 0x001e, 0x0078, 0x0156, 0x00b6, 0x20a9, 0x007f, 0x900e,
+ 0x080c, 0x649f, 0x1110, 0x080c, 0x5f45, 0x8108, 0x1f04, 0x74e8,
+ 0x00be, 0x015e, 0x080c, 0x1a24, 0x080c, 0xa069, 0x60e3, 0x0000,
+ 0x080c, 0x5f2b, 0x080c, 0x7127, 0x00ee, 0x00ce, 0x004e, 0x003e,
+ 0x002e, 0x001e, 0x015e, 0x0005, 0x2001, 0x1955, 0x2003, 0x0001,
+ 0x0005, 0x2001, 0x1955, 0x2003, 0x0000, 0x0005, 0x2001, 0x1954,
+ 0x2003, 0xaaaa, 0x0005, 0x2001, 0x1954, 0x2003, 0x0000, 0x0005,
+ 0x2071, 0x18f8, 0x7003, 0x0000, 0x7007, 0x0000, 0x080c, 0x104a,
+ 0x090c, 0x0dfa, 0xa8ab, 0xdcb0, 0x2900, 0x704e, 0x080c, 0x104a,
+ 0x090c, 0x0dfa, 0xa8ab, 0xdcb0, 0x2900, 0x7052, 0xa867, 0x0000,
+ 0xa86b, 0x0001, 0xa89f, 0x0000, 0x0005, 0x00e6, 0x2071, 0x0040,
+ 0x6848, 0x9005, 0x1118, 0x9085, 0x0001, 0x04b0, 0x6840, 0x9005,
+ 0x0150, 0x04a1, 0x6a50, 0x9200, 0x7002, 0x6854, 0x9101, 0x7006,
+ 0x9006, 0x7012, 0x7016, 0x6850, 0x7002, 0x6854, 0x7006, 0x6858,
+ 0x700a, 0x685c, 0x700e, 0x6840, 0x9005, 0x1110, 0x7012, 0x7016,
+ 0x6848, 0x701a, 0x701c, 0x9085, 0x0040, 0x701e, 0x2001, 0x0019,
+ 0x7036, 0x702b, 0x0001, 0x2001, 0x0004, 0x200c, 0x918c, 0xfff7,
+ 0x918d, 0x8000, 0x2102, 0x00d6, 0x2069, 0x18f8, 0x6807, 0x0001,
+ 0x00de, 0x080c, 0x7afd, 0x9006, 0x00ee, 0x0005, 0x900e, 0x0156,
+ 0x20a9, 0x0006, 0x8003, 0x818d, 0x1f04, 0x757a, 0x015e, 0x0005,
+ 0x2079, 0x0040, 0x2071, 0x18f8, 0x7004, 0x0002, 0x7590, 0x7591,
+ 0x75c9, 0x7624, 0x7765, 0x758e, 0x758e, 0x778f, 0x080c, 0x0dfa,
+ 0x0005, 0x2079, 0x0040, 0x782c, 0x908c, 0x0780, 0x190c, 0x7b89,
+ 0xd0a4, 0x01f8, 0x7824, 0x2048, 0x9006, 0xa802, 0xa806, 0xa864,
+ 0x9084, 0x00ff, 0x908a, 0x0040, 0x0610, 0x00c0, 0x2001, 0x1800,
+ 0x200c, 0x9186, 0x0003, 0x1168, 0x7004, 0x0002, 0x75b9, 0x7593,
+ 0x75b9, 0x75b7, 0x75b9, 0x75b9, 0x75b9, 0x75b9, 0x75b9, 0x080c,
+ 0x7624, 0x782c, 0xd09c, 0x090c, 0x7afd, 0x0005, 0x9082, 0x005a,
+ 0x1218, 0x2100, 0x003b, 0x0c10, 0x080c, 0x765a, 0x0c90, 0x00e3,
+ 0x08e8, 0x0005, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x767c, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x7666, 0x765a,
+ 0x7864, 0x765a, 0x765a, 0x765a, 0x767c, 0x765a, 0x7666, 0x78a5,
+ 0x78e6, 0x792d, 0x7941, 0x765a, 0x765a, 0x767c, 0x7666, 0x765a,
+ 0x765a, 0x7739, 0x79ec, 0x7a07, 0x765a, 0x767c, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x772f, 0x7a07, 0x765a, 0x765a, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x7690, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x7b2d,
+ 0x765a, 0x765a, 0x765a, 0x765a, 0x765a, 0x76a4, 0x765a, 0x765a,
+ 0x765a, 0x765a, 0x765a, 0x765a, 0x2079, 0x0040, 0x7004, 0x9086,
+ 0x0003, 0x1198, 0x782c, 0x080c, 0x7b26, 0xd0a4, 0x0170, 0x7824,
+ 0x2048, 0x9006, 0xa802, 0xa806, 0xa864, 0x9084, 0x00ff, 0x908a,
+ 0x001a, 0x1210, 0x002b, 0x0c50, 0x00e9, 0x080c, 0x7afd, 0x0005,
+ 0x765a, 0x7666, 0x7850, 0x765a, 0x7666, 0x765a, 0x7666, 0x7666,
+ 0x765a, 0x7666, 0x7850, 0x7666, 0x7666, 0x7666, 0x7666, 0x7666,
+ 0x765a, 0x7666, 0x7850, 0x765a, 0x765a, 0x7666, 0x765a, 0x765a,
+ 0x765a, 0x7666, 0x00e6, 0x2071, 0x18f8, 0x2009, 0x0400, 0x0071,
+ 0x00ee, 0x0005, 0x2009, 0x1000, 0x0049, 0x0005, 0x2009, 0x2000,
+ 0x0029, 0x0005, 0x2009, 0x0800, 0x0009, 0x0005, 0x7007, 0x0001,
+ 0xa868, 0x9084, 0x00ff, 0x9105, 0xa86a, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6ae9, 0x012e, 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff,
+ 0x0d08, 0x8001, 0x1120, 0x7007, 0x0001, 0x0804, 0x780e, 0x7007,
+ 0x0003, 0x7012, 0x2900, 0x7016, 0x701a, 0x704b, 0x780e, 0x0005,
+ 0xa864, 0x8007, 0x9084, 0x00ff, 0x0968, 0x8001, 0x1120, 0x7007,
+ 0x0001, 0x0804, 0x7829, 0x7007, 0x0003, 0x7012, 0x2900, 0x7016,
+ 0x701a, 0x704b, 0x7829, 0x0005, 0xa864, 0x8007, 0x9084, 0x00ff,
+ 0x9086, 0x0001, 0x1904, 0x7662, 0x7007, 0x0001, 0x2009, 0x1833,
+ 0x210c, 0x81ff, 0x1904, 0x7706, 0xa994, 0x9186, 0x006f, 0x0188,
+ 0x9186, 0x0074, 0x15b0, 0x0026, 0x2011, 0x0010, 0x080c, 0x67e7,
+ 0x002e, 0x0578, 0x0016, 0xa998, 0x080c, 0x6831, 0x001e, 0x1548,
+ 0x0400, 0x080c, 0x7207, 0x0140, 0xa897, 0x4005, 0xa89b, 0x0016,
+ 0x2001, 0x0030, 0x900e, 0x0438, 0x0026, 0x2011, 0x8008, 0x080c,
+ 0x67e7, 0x002e, 0x01b0, 0x0016, 0x0026, 0x0036, 0xa998, 0xaaa0,
+ 0xab9c, 0x918d, 0x8000, 0x080c, 0x6831, 0x003e, 0x002e, 0x001e,
+ 0x1140, 0xa897, 0x4005, 0xa89b, 0x4009, 0x2001, 0x0030, 0x900e,
+ 0x0050, 0xa868, 0x9084, 0x00ff, 0xa86a, 0xa883, 0x0000, 0x080c,
+ 0x615d, 0x1108, 0x0005, 0x0126, 0x2091, 0x8000, 0xa867, 0x0139,
+ 0xa87a, 0xa982, 0x080c, 0x6ae9, 0x012e, 0x0ca0, 0xa994, 0x9186,
+ 0x0071, 0x0904, 0x76b4, 0x9186, 0x0064, 0x0904, 0x76b4, 0x9186,
+ 0x007c, 0x0904, 0x76b4, 0x9186, 0x0028, 0x0904, 0x76b4, 0x9186,
+ 0x0038, 0x0904, 0x76b4, 0x9186, 0x0078, 0x0904, 0x76b4, 0x9186,
+ 0x005f, 0x0904, 0x76b4, 0x9186, 0x0056, 0x0904, 0x76b4, 0xa897,
+ 0x4005, 0xa89b, 0x0001, 0x2001, 0x0030, 0x900e, 0x0860, 0xa87c,
+ 0x9084, 0x00c0, 0x9086, 0x00c0, 0x1120, 0x7007, 0x0001, 0x0804,
+ 0x7a1e, 0x2900, 0x7016, 0x701a, 0x20a9, 0x0004, 0xa860, 0x20e0,
+ 0xa85c, 0x9080, 0x0030, 0x2098, 0x7050, 0x2040, 0xa060, 0x20e8,
+ 0xa05c, 0x9080, 0x0023, 0x20a0, 0x4003, 0xa888, 0x7012, 0x9082,
+ 0x0401, 0x1a04, 0x766a, 0xaab4, 0x928a, 0x0002, 0x1a04, 0x766a,
+ 0x82ff, 0x1138, 0xa8b8, 0xa9bc, 0x9105, 0x0118, 0x2001, 0x77cc,
+ 0x0018, 0x9280, 0x77c2, 0x2005, 0x7056, 0x7010, 0x9015, 0x0904,
+ 0x77ad, 0x080c, 0x104a, 0x1118, 0x7007, 0x0004, 0x0005, 0x2900,
+ 0x7022, 0x7054, 0x2060, 0xe000, 0xa866, 0x7050, 0x2040, 0xa95c,
+ 0xe004, 0x9100, 0xa076, 0xa860, 0xa072, 0xe008, 0x920a, 0x1210,
+ 0x900e, 0x2200, 0x7112, 0xe20c, 0x8003, 0x800b, 0x9296, 0x0004,
+ 0x0108, 0x9108, 0xa17a, 0x810b, 0xa17e, 0x080c, 0x112e, 0xa06c,
+ 0x908e, 0x0100, 0x0170, 0x9086, 0x0200, 0x0118, 0x7007, 0x0007,
+ 0x0005, 0x7020, 0x2048, 0x080c, 0x1063, 0x7014, 0x2048, 0x0804,
+ 0x766a, 0x7020, 0x2048, 0x7018, 0xa802, 0xa807, 0x0000, 0x2908,
+ 0x2048, 0xa906, 0x711a, 0x0804, 0x7765, 0x7014, 0x2048, 0x7007,
+ 0x0001, 0xa8b4, 0x9005, 0x1128, 0xa8b8, 0xa9bc, 0x9105, 0x0108,
+ 0x00b9, 0xa864, 0x9084, 0x00ff, 0x9086, 0x001e, 0x0904, 0x7a1e,
+ 0x0804, 0x780e, 0x77c4, 0x77c8, 0x0002, 0x001d, 0x0007, 0x0004,
+ 0x000a, 0x001b, 0x0005, 0x0006, 0x000a, 0x001d, 0x0005, 0x0004,
+ 0x0076, 0x0066, 0xafb8, 0xaebc, 0xa804, 0x2050, 0xb0c0, 0xb0e2,
+ 0xb0bc, 0xb0de, 0xb0b8, 0xb0d2, 0xb0b4, 0xb0ce, 0xb6da, 0xb7d6,
+ 0xb0b0, 0xb0ca, 0xb0ac, 0xb0c6, 0xb0a8, 0xb0ba, 0xb0a4, 0xb0b6,
+ 0xb6c2, 0xb7be, 0xb0a0, 0xb0b2, 0xb09c, 0xb0ae, 0xb098, 0xb0a2,
+ 0xb094, 0xb09e, 0xb6aa, 0xb7a6, 0xb090, 0xb09a, 0xb08c, 0xb096,
+ 0xb088, 0xb08a, 0xb084, 0xb086, 0xb692, 0xb78e, 0xb080, 0xb082,
+ 0xb07c, 0xb07e, 0xb078, 0xb072, 0xb074, 0xb06e, 0xb67a, 0xb776,
+ 0xb004, 0x9055, 0x1958, 0x006e, 0x007e, 0x0005, 0x2009, 0x1833,
+ 0x210c, 0x81ff, 0x1178, 0x080c, 0x5fa7, 0x1108, 0x0005, 0x080c,
+ 0x6d17, 0x0126, 0x2091, 0x8000, 0x080c, 0xc12d, 0x080c, 0x6ae9,
+ 0x012e, 0x0ca0, 0x080c, 0xc532, 0x1d70, 0x2001, 0x0028, 0x900e,
+ 0x0c70, 0x2009, 0x1833, 0x210c, 0x81ff, 0x11d8, 0xa888, 0x9005,
+ 0x01e0, 0xa883, 0x0000, 0xa87c, 0xd0f4, 0x0120, 0x080c, 0x60bf,
+ 0x1138, 0x0005, 0x9006, 0xa87a, 0x080c, 0x6037, 0x1108, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0xa87a, 0xa982, 0x080c, 0x6ae9, 0x012e,
+ 0x0cb0, 0x2001, 0x0028, 0x900e, 0x0c98, 0x2001, 0x0000, 0x0c80,
+ 0x7018, 0xa802, 0x2908, 0x2048, 0xa906, 0x711a, 0x7010, 0x8001,
+ 0x7012, 0x0118, 0x7007, 0x0003, 0x0030, 0x7014, 0x2048, 0x7007,
+ 0x0001, 0x7048, 0x080f, 0x0005, 0x00b6, 0x7007, 0x0001, 0xa974,
+ 0xa878, 0x9084, 0x00ff, 0x9096, 0x0004, 0x0540, 0x20a9, 0x0001,
+ 0x9096, 0x0001, 0x0190, 0x900e, 0x20a9, 0x0800, 0x9096, 0x0002,
+ 0x0160, 0x9005, 0x11d8, 0xa974, 0x080c, 0x649f, 0x11b8, 0x0066,
+ 0xae80, 0x080c, 0x65af, 0x006e, 0x0088, 0x0046, 0x2011, 0x180c,
+ 0x2224, 0xc484, 0x2412, 0x004e, 0x00c6, 0x080c, 0x649f, 0x1110,
+ 0x080c, 0x66af, 0x8108, 0x1f04, 0x788d, 0x00ce, 0xa87c, 0xd084,
+ 0x1120, 0x080c, 0x1063, 0x00be, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6ae9, 0x012e, 0x00be, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x7007, 0x0001, 0x080c, 0x67bf, 0x0580, 0x2061, 0x1a48, 0x6100,
+ 0xd184, 0x0178, 0xa888, 0x9084, 0x00ff, 0x1550, 0x6000, 0xd084,
+ 0x0520, 0x6004, 0x9005, 0x1538, 0x6003, 0x0000, 0x600b, 0x0000,
+ 0x00c8, 0x2011, 0x0001, 0xa890, 0x9005, 0x1110, 0x2001, 0x001e,
+ 0x8000, 0x6016, 0xa888, 0x9084, 0x00ff, 0x0178, 0x6006, 0xa888,
+ 0x8007, 0x9084, 0x00ff, 0x0148, 0x600a, 0xa888, 0x8000, 0x1108,
+ 0xc28d, 0x6202, 0x012e, 0x0804, 0x7ae7, 0x012e, 0x0804, 0x7ae1,
+ 0x012e, 0x0804, 0x7adb, 0x012e, 0x0804, 0x7ade, 0x0126, 0x2091,
+ 0x8000, 0x7007, 0x0001, 0x080c, 0x67bf, 0x05e0, 0x2061, 0x1a48,
+ 0x6000, 0xd084, 0x05b8, 0x6204, 0x6308, 0xd08c, 0x1530, 0xac78,
+ 0x9484, 0x0003, 0x0170, 0xa988, 0x918c, 0x00ff, 0x8001, 0x1120,
+ 0x2100, 0x9210, 0x0620, 0x0028, 0x8001, 0x1508, 0x2100, 0x9212,
+ 0x02f0, 0x9484, 0x000c, 0x0188, 0xa988, 0x810f, 0x918c, 0x00ff,
+ 0x9082, 0x0004, 0x1120, 0x2100, 0x9318, 0x0288, 0x0030, 0x9082,
+ 0x0004, 0x1168, 0x2100, 0x931a, 0x0250, 0xa890, 0x9005, 0x0110,
+ 0x8000, 0x6016, 0x6206, 0x630a, 0x012e, 0x0804, 0x7ae7, 0x012e,
+ 0x0804, 0x7ae4, 0x012e, 0x0804, 0x7ae1, 0x0126, 0x2091, 0x8000,
+ 0x7007, 0x0001, 0x2061, 0x1a48, 0x6300, 0xd38c, 0x1120, 0x6308,
+ 0x8318, 0x0220, 0x630a, 0x012e, 0x0804, 0x7af5, 0x012e, 0x0804,
+ 0x7ae4, 0x00b6, 0x0126, 0x00c6, 0x2091, 0x8000, 0x7007, 0x0001,
+ 0xa87c, 0xd0ac, 0x0148, 0x00c6, 0x2061, 0x1a48, 0x6000, 0x9084,
+ 0xfcff, 0x6002, 0x00ce, 0x0440, 0xa888, 0x9005, 0x05d8, 0xa88c,
+ 0x9065, 0x0598, 0x2001, 0x1833, 0x2004, 0x9005, 0x0118, 0x080c,
+ 0xa113, 0x0068, 0x6017, 0xf400, 0x605b, 0x0000, 0xa97c, 0xd1a4,
+ 0x0110, 0xa980, 0x615a, 0x2009, 0x0041, 0x080c, 0xa15d, 0xa988,
+ 0x918c, 0xff00, 0x9186, 0x2000, 0x1138, 0x0026, 0x900e, 0x2011,
+ 0xfdff, 0x080c, 0x84d1, 0x002e, 0xa87c, 0xd0c4, 0x0148, 0x2061,
+ 0x1a48, 0x6000, 0xd08c, 0x1120, 0x6008, 0x8000, 0x0208, 0x600a,
+ 0x00ce, 0x012e, 0x00be, 0x0804, 0x7ae7, 0x00ce, 0x012e, 0x00be,
+ 0x0804, 0x7ae1, 0xa984, 0x9186, 0x002e, 0x0d30, 0x9186, 0x002d,
+ 0x0d18, 0x9186, 0x0045, 0x0510, 0x9186, 0x002a, 0x1130, 0x2001,
+ 0x180c, 0x200c, 0xc194, 0x2102, 0x08b8, 0x9186, 0x0020, 0x0158,
+ 0x9186, 0x0029, 0x1d10, 0xa974, 0x080c, 0x649f, 0x1968, 0xb800,
+ 0xc0e4, 0xb802, 0x0848, 0xa88c, 0x9065, 0x09b8, 0x6007, 0x0024,
+ 0x2001, 0x195e, 0x2004, 0x601a, 0x0804, 0x797c, 0xa88c, 0x9065,
+ 0x0960, 0x00e6, 0xa890, 0x9075, 0x2001, 0x1833, 0x2004, 0x9005,
+ 0x0150, 0x080c, 0xa113, 0x8eff, 0x0118, 0x2e60, 0x080c, 0xa113,
+ 0x00ee, 0x0804, 0x797c, 0x6024, 0xc0dc, 0xc0d5, 0x6026, 0x2e60,
+ 0x6007, 0x003a, 0xa8a0, 0x9005, 0x0130, 0x6007, 0x003b, 0xa8a4,
+ 0x602e, 0xa8a8, 0x6016, 0x6003, 0x0001, 0x080c, 0x8679, 0x080c,
+ 0x8c10, 0x00ee, 0x0804, 0x797c, 0x2061, 0x1a48, 0x6000, 0xd084,
+ 0x0190, 0xd08c, 0x1904, 0x7af5, 0x0126, 0x2091, 0x8000, 0x6204,
+ 0x8210, 0x0220, 0x6206, 0x012e, 0x0804, 0x7af5, 0x012e, 0xa883,
+ 0x0016, 0x0804, 0x7aee, 0xa883, 0x0007, 0x0804, 0x7aee, 0xa864,
+ 0x8007, 0x9084, 0x00ff, 0x0130, 0x8001, 0x1138, 0x7007, 0x0001,
+ 0x0069, 0x0005, 0x080c, 0x7662, 0x0040, 0x7007, 0x0003, 0x7012,
+ 0x2900, 0x7016, 0x701a, 0x704b, 0x7a1e, 0x0005, 0x00b6, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0x903e, 0x2061, 0x1800, 0x61cc, 0x81ff,
+ 0x1904, 0x7aa0, 0x6130, 0xd194, 0x1904, 0x7aca, 0xa878, 0x2070,
+ 0x9e82, 0x1cd0, 0x0a04, 0x7a94, 0x6064, 0x9e02, 0x1a04, 0x7a94,
+ 0x7120, 0x9186, 0x0006, 0x1904, 0x7a86, 0x7010, 0x905d, 0x0904,
+ 0x7aa0, 0xb800, 0xd0e4, 0x1904, 0x7ac4, 0x2061, 0x1a48, 0x6100,
+ 0x9184, 0x0301, 0x9086, 0x0001, 0x15a0, 0x7024, 0xd0dc, 0x1904,
+ 0x7acd, 0xa883, 0x0000, 0xa803, 0x0000, 0x2908, 0x7014, 0x9005,
+ 0x1198, 0x7116, 0xa87c, 0xd0f4, 0x1904, 0x7ad0, 0x080c, 0x55db,
+ 0xd09c, 0x1118, 0xa87c, 0xc0cc, 0xa87e, 0x2e60, 0x080c, 0x83f1,
+ 0x012e, 0x00ee, 0x00be, 0x0005, 0x2048, 0xa800, 0x9005, 0x1de0,
+ 0xa902, 0x2148, 0xa87c, 0xd0f4, 0x1904, 0x7ad0, 0x012e, 0x00ee,
+ 0x00be, 0x0005, 0x012e, 0x00ee, 0xa883, 0x0006, 0x00be, 0x0804,
+ 0x7aee, 0xd184, 0x0db8, 0xd1c4, 0x1190, 0x00a0, 0xa974, 0x080c,
+ 0x649f, 0x15d0, 0xb800, 0xd0e4, 0x15b8, 0x7120, 0x9186, 0x0007,
+ 0x1118, 0xa883, 0x0002, 0x0490, 0xa883, 0x0008, 0x0478, 0xa883,
+ 0x000e, 0x0460, 0xa883, 0x0017, 0x0448, 0xa883, 0x0035, 0x0430,
+ 0x080c, 0x55df, 0xd0fc, 0x01e8, 0xa878, 0x2070, 0x9e82, 0x1cd0,
+ 0x02c0, 0x6064, 0x9e02, 0x12a8, 0x7120, 0x9186, 0x0006, 0x1188,
+ 0x7010, 0x905d, 0x0170, 0xb800, 0xd0bc, 0x0158, 0x2039, 0x0001,
+ 0x7000, 0x9086, 0x0007, 0x1904, 0x7a2a, 0x7003, 0x0002, 0x0804,
+ 0x7a2a, 0xa883, 0x0028, 0x0010, 0xa883, 0x0029, 0x012e, 0x00ee,
+ 0x00be, 0x0420, 0xa883, 0x002a, 0x0cc8, 0xa883, 0x0045, 0x0cb0,
+ 0x2e60, 0x2019, 0x0002, 0x601b, 0x0014, 0x080c, 0xd440, 0x012e,
+ 0x00ee, 0x00be, 0x0005, 0x2009, 0x003e, 0x0058, 0x2009, 0x0004,
+ 0x0040, 0x2009, 0x0006, 0x0028, 0x2009, 0x0016, 0x0010, 0x2009,
+ 0x0001, 0xa884, 0x9084, 0xff00, 0x9105, 0xa886, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6ae9, 0x012e, 0x0005, 0x080c, 0x1063, 0x0005,
+ 0x00d6, 0x080c, 0x83e8, 0x00de, 0x0005, 0x00d6, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x0040, 0x702c, 0xd084, 0x01d8, 0x908c,
+ 0x0780, 0x190c, 0x7b89, 0xd09c, 0x11a8, 0x2071, 0x1800, 0x70bc,
+ 0x90ea, 0x0040, 0x0278, 0x8001, 0x70be, 0x702c, 0x2048, 0xa800,
+ 0x702e, 0x9006, 0xa802, 0xa806, 0x2071, 0x0040, 0x2900, 0x7022,
+ 0x702c, 0x0c28, 0x012e, 0x00ee, 0x00de, 0x0005, 0x0006, 0x9084,
+ 0x0780, 0x190c, 0x7b89, 0x000e, 0x0005, 0x00d6, 0x00c6, 0x0036,
+ 0x0026, 0x0016, 0x00b6, 0x7007, 0x0001, 0xaa74, 0x9282, 0x0004,
+ 0x1a04, 0x7b7a, 0xa97c, 0x9188, 0x1000, 0x2104, 0x905d, 0xb804,
+ 0xd284, 0x0140, 0x05e8, 0x8007, 0x9084, 0x00ff, 0x9084, 0x0006,
+ 0x1108, 0x04b0, 0x2b10, 0x080c, 0xa08d, 0x1118, 0x080c, 0xa130,
+ 0x05a8, 0x6212, 0xa874, 0x0002, 0x7b58, 0x7b5d, 0x7b60, 0x7b66,
+ 0x2019, 0x0002, 0x080c, 0xd801, 0x0060, 0x080c, 0xd79d, 0x0048,
+ 0x2019, 0x0002, 0xa980, 0x080c, 0xd7b8, 0x0018, 0xa980, 0x080c,
+ 0xd79d, 0x080c, 0xa0e3, 0xa887, 0x0000, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6ae9, 0x012e, 0x00be, 0x001e, 0x002e, 0x003e, 0x00ce,
+ 0x00de, 0x0005, 0xa887, 0x0006, 0x0c80, 0xa887, 0x0002, 0x0c68,
+ 0xa887, 0x0005, 0x0c50, 0xa887, 0x0004, 0x0c38, 0xa887, 0x0007,
+ 0x0c20, 0x2091, 0x8000, 0x0e04, 0x7b8b, 0x0006, 0x0016, 0x2001,
+ 0x8003, 0x0006, 0x0804, 0x0e03, 0x2001, 0x1833, 0x2004, 0x9005,
+ 0x0005, 0x0005, 0x00f6, 0x2079, 0x0300, 0x2001, 0x0200, 0x200c,
+ 0xc1e5, 0xc1dc, 0x2102, 0x2009, 0x0218, 0x210c, 0xd1ec, 0x1120,
+ 0x080c, 0x151a, 0x00fe, 0x0005, 0x2001, 0x020d, 0x2003, 0x0020,
+ 0x781f, 0x0300, 0x00fe, 0x0005, 0x781c, 0xd08c, 0x0904, 0x7c0b,
+ 0x68bc, 0x90aa, 0x0005, 0x0a04, 0x81e7, 0x7d44, 0x7c40, 0x9584,
+ 0x00f6, 0x1510, 0x9484, 0x7000, 0x0140, 0x908a, 0x2000, 0x1260,
+ 0x9584, 0x0700, 0x8007, 0x0804, 0x7c12, 0x7000, 0x9084, 0xff00,
+ 0x9086, 0x8100, 0x0da8, 0x00b0, 0x9484, 0x0fff, 0x1130, 0x7000,
+ 0x9084, 0xff00, 0x9086, 0x8100, 0x11c0, 0x080c, 0xdbeb, 0x080c,
+ 0x811c, 0x7817, 0x0140, 0x00a8, 0x9584, 0x0076, 0x1118, 0x080c,
+ 0x817a, 0x19c0, 0xd5a4, 0x0148, 0x0046, 0x0056, 0x080c, 0x7c6d,
+ 0x080c, 0x226f, 0x005e, 0x004e, 0x0020, 0x080c, 0xdbeb, 0x7817,
+ 0x0140, 0x080c, 0x7207, 0x0168, 0x2001, 0x0111, 0x2004, 0xd08c,
+ 0x0140, 0x688f, 0x0000, 0x2001, 0x0110, 0x2003, 0x0008, 0x2003,
+ 0x0000, 0x080c, 0x7c4e, 0x2001, 0x19c8, 0x2004, 0x9005, 0x090c,
+ 0x8c10, 0x0005, 0x0002, 0x7c24, 0x7f24, 0x7c1b, 0x7c1b, 0x7c1b,
+ 0x7c1b, 0x7c1b, 0x7c1b, 0x7817, 0x0140, 0x2001, 0x19c8, 0x2004,
+ 0x9005, 0x090c, 0x8c10, 0x0005, 0x7000, 0x908c, 0xff00, 0x9194,
+ 0xf000, 0x810f, 0x9484, 0x0fff, 0x688e, 0x9286, 0x2000, 0x1150,
+ 0x6800, 0x9086, 0x0001, 0x1118, 0x080c, 0x5641, 0x0070, 0x080c,
+ 0x7c8d, 0x0058, 0x9286, 0x3000, 0x1118, 0x080c, 0x7e5c, 0x0028,
+ 0x9286, 0x8000, 0x1110, 0x080c, 0x8043, 0x7817, 0x0140, 0x2001,
+ 0x19c8, 0x2004, 0x9005, 0x090c, 0x8c10, 0x0005, 0x2001, 0x1810,
+ 0x2004, 0xd08c, 0x0178, 0x2001, 0x1800, 0x2004, 0x9086, 0x0003,
+ 0x1148, 0x0026, 0x0036, 0x2011, 0x8048, 0x2518, 0x080c, 0x4b1f,
+ 0x003e, 0x002e, 0x0005, 0x0036, 0x0046, 0x0056, 0x00f6, 0x2079,
+ 0x0200, 0x2019, 0xfffe, 0x7c30, 0x0050, 0x0036, 0x0046, 0x0056,
+ 0x00f6, 0x2079, 0x0200, 0x7d44, 0x7c40, 0x2019, 0xffff, 0x2001,
+ 0x1810, 0x2004, 0xd08c, 0x0160, 0x2001, 0x1800, 0x2004, 0x9086,
+ 0x0003, 0x1130, 0x0026, 0x2011, 0x8048, 0x080c, 0x4b1f, 0x002e,
+ 0x00fe, 0x005e, 0x004e, 0x003e, 0x0005, 0x00b6, 0x00c6, 0x7010,
+ 0x9084, 0xff00, 0x8007, 0x9096, 0x0001, 0x0120, 0x9096, 0x0023,
+ 0x1904, 0x7e2d, 0x9186, 0x0023, 0x15c0, 0x080c, 0x80e1, 0x0904,
+ 0x7e2d, 0x6120, 0x9186, 0x0001, 0x0150, 0x9186, 0x0004, 0x0138,
+ 0x9186, 0x0008, 0x0120, 0x9186, 0x000a, 0x1904, 0x7e2d, 0x7124,
+ 0x610a, 0x7030, 0x908e, 0x0200, 0x1130, 0x2009, 0x0015, 0x080c,
+ 0xa15d, 0x0804, 0x7e2d, 0x908e, 0x0214, 0x0118, 0x908e, 0x0210,
+ 0x1130, 0x2009, 0x0015, 0x080c, 0xa15d, 0x0804, 0x7e2d, 0x908e,
+ 0x0100, 0x1904, 0x7e2d, 0x7034, 0x9005, 0x1904, 0x7e2d, 0x2009,
+ 0x0016, 0x080c, 0xa15d, 0x0804, 0x7e2d, 0x9186, 0x0022, 0x1904,
+ 0x7e2d, 0x7030, 0x908e, 0x0300, 0x1580, 0x68d8, 0xd0a4, 0x0528,
+ 0xc0b5, 0x68da, 0x7100, 0x918c, 0x00ff, 0x697a, 0x7004, 0x687e,
+ 0x00f6, 0x2079, 0x0100, 0x79e6, 0x78ea, 0x0006, 0x9084, 0x00ff,
+ 0x0016, 0x2008, 0x080c, 0x27b7, 0x7932, 0x7936, 0x001e, 0x000e,
+ 0x00fe, 0x080c, 0x276e, 0x695a, 0x703c, 0x00e6, 0x2071, 0x0140,
+ 0x7086, 0x2071, 0x1800, 0x70b2, 0x00ee, 0x7034, 0x9005, 0x1904,
+ 0x7e2d, 0x2009, 0x0017, 0x0804, 0x7ddd, 0x908e, 0x0400, 0x1190,
+ 0x7034, 0x9005, 0x1904, 0x7e2d, 0x080c, 0x7207, 0x0120, 0x2009,
+ 0x001d, 0x0804, 0x7ddd, 0x68d8, 0xc0a5, 0x68da, 0x2009, 0x0030,
+ 0x0804, 0x7ddd, 0x908e, 0x0500, 0x1140, 0x7034, 0x9005, 0x1904,
+ 0x7e2d, 0x2009, 0x0018, 0x0804, 0x7ddd, 0x908e, 0x2010, 0x1120,
+ 0x2009, 0x0019, 0x0804, 0x7ddd, 0x908e, 0x2110, 0x1120, 0x2009,
+ 0x001a, 0x0804, 0x7ddd, 0x908e, 0x5200, 0x1140, 0x7034, 0x9005,
+ 0x1904, 0x7e2d, 0x2009, 0x001b, 0x0804, 0x7ddd, 0x908e, 0x5000,
+ 0x1140, 0x7034, 0x9005, 0x1904, 0x7e2d, 0x2009, 0x001c, 0x0804,
+ 0x7ddd, 0x908e, 0x1300, 0x1120, 0x2009, 0x0034, 0x0804, 0x7ddd,
+ 0x908e, 0x1200, 0x1140, 0x7034, 0x9005, 0x1904, 0x7e2d, 0x2009,
+ 0x0024, 0x0804, 0x7ddd, 0x908c, 0xff00, 0x918e, 0x2400, 0x1170,
+ 0x2009, 0x002d, 0x2001, 0x1810, 0x2004, 0xd09c, 0x0904, 0x7ddd,
+ 0x080c, 0xcc07, 0x1904, 0x7e2d, 0x0804, 0x7ddb, 0x908c, 0xff00,
+ 0x918e, 0x5300, 0x1120, 0x2009, 0x002a, 0x0804, 0x7ddd, 0x908e,
+ 0x0f00, 0x1120, 0x2009, 0x0020, 0x0804, 0x7ddd, 0x908e, 0x6104,
+ 0x1528, 0x2029, 0x0205, 0x2011, 0x026d, 0x8208, 0x2204, 0x9082,
+ 0x0004, 0x8004, 0x8004, 0x20a8, 0x2011, 0x8015, 0x211c, 0x8108,
+ 0x0046, 0x2124, 0x080c, 0x4b1f, 0x004e, 0x8108, 0x0f04, 0x7da9,
+ 0x9186, 0x0280, 0x1d88, 0x2504, 0x8000, 0x202a, 0x2009, 0x0260,
+ 0x0c58, 0x202b, 0x0000, 0x2009, 0x0023, 0x0478, 0x908e, 0x6000,
+ 0x1118, 0x2009, 0x003f, 0x0448, 0x908e, 0x7800, 0x1118, 0x2009,
+ 0x0045, 0x0418, 0x908e, 0x1000, 0x1118, 0x2009, 0x004e, 0x00e8,
+ 0x908e, 0x6300, 0x1118, 0x2009, 0x004a, 0x00b8, 0x908c, 0xff00,
+ 0x918e, 0x5600, 0x1118, 0x2009, 0x004f, 0x0078, 0x908c, 0xff00,
+ 0x918e, 0x5700, 0x1118, 0x2009, 0x0050, 0x0038, 0x2009, 0x001d,
+ 0x6838, 0xd0d4, 0x0110, 0x2009, 0x004c, 0x0016, 0x2011, 0x0263,
+ 0x2204, 0x8211, 0x220c, 0x080c, 0x276e, 0x1904, 0x7e30, 0x080c,
+ 0x643f, 0x1904, 0x7e30, 0xbe12, 0xbd16, 0x001e, 0x0016, 0x080c,
+ 0x7207, 0x01c0, 0x68d8, 0xd08c, 0x1148, 0x7000, 0x9084, 0x00ff,
+ 0x1188, 0x7004, 0x9084, 0xff00, 0x1168, 0x0040, 0x6878, 0x9606,
+ 0x1148, 0x687c, 0x9506, 0x9084, 0xff00, 0x1120, 0x9584, 0x00ff,
+ 0xb8b2, 0x0080, 0xb8b0, 0x9005, 0x1168, 0x9186, 0x0046, 0x1150,
+ 0x6878, 0x9606, 0x1138, 0x687c, 0x9506, 0x9084, 0xff00, 0x1110,
+ 0x001e, 0x0098, 0x080c, 0xa08d, 0x01a8, 0x2b08, 0x6112, 0x6023,
+ 0x0004, 0x7120, 0x610a, 0x001e, 0x9186, 0x004c, 0x1110, 0x6023,
+ 0x000a, 0x0016, 0x001e, 0x080c, 0xa15d, 0x00ce, 0x00be, 0x0005,
+ 0x001e, 0x0cd8, 0x2001, 0x180e, 0x2004, 0xd0ec, 0x0120, 0x2011,
+ 0x8049, 0x080c, 0x4b1f, 0x080c, 0xa130, 0x0d90, 0x2b08, 0x6112,
+ 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x0016, 0x9186, 0x0017,
+ 0x0118, 0x9186, 0x0030, 0x1128, 0x6007, 0x0009, 0x6017, 0x2900,
+ 0x0020, 0x6007, 0x0051, 0x6017, 0x0000, 0x602f, 0x0009, 0x6003,
+ 0x0001, 0x080c, 0x86c1, 0x08a0, 0x080c, 0x8206, 0x1158, 0x080c,
+ 0x32ae, 0x1140, 0x7010, 0x9084, 0xff00, 0x8007, 0x908e, 0x0008,
+ 0x1108, 0x0009, 0x0005, 0x00b6, 0x00c6, 0x0046, 0x7000, 0x908c,
+ 0xff00, 0x810f, 0x9186, 0x0033, 0x11e8, 0x080c, 0x80e1, 0x0904,
+ 0x7ebc, 0x7124, 0x610a, 0x7030, 0x908e, 0x0200, 0x1140, 0x7034,
+ 0x9005, 0x15d0, 0x2009, 0x0015, 0x080c, 0xa15d, 0x04a8, 0x908e,
+ 0x0100, 0x1590, 0x7034, 0x9005, 0x1578, 0x2009, 0x0016, 0x080c,
+ 0xa15d, 0x0450, 0x9186, 0x0032, 0x1538, 0x7030, 0x908e, 0x1400,
+ 0x1518, 0x2009, 0x0038, 0x0016, 0x2011, 0x0263, 0x2204, 0x8211,
+ 0x220c, 0x080c, 0x276e, 0x11b8, 0x080c, 0x643f, 0x11a0, 0xbe12,
+ 0xbd16, 0x080c, 0xa08d, 0x0178, 0x2b08, 0x6112, 0x080c, 0xc2b3,
+ 0x6023, 0x0004, 0x7120, 0x610a, 0x001e, 0x080c, 0xa15d, 0x080c,
+ 0x8c10, 0x0010, 0x00ce, 0x001e, 0x004e, 0x00ce, 0x00be, 0x0005,
+ 0x00b6, 0x0046, 0x00e6, 0x00d6, 0x2028, 0x2130, 0x9696, 0x00ff,
+ 0x11b8, 0x9592, 0xfffc, 0x02a0, 0x9596, 0xfffd, 0x1120, 0x2009,
+ 0x007f, 0x0804, 0x7f1e, 0x9596, 0xfffe, 0x1120, 0x2009, 0x007e,
+ 0x0804, 0x7f1e, 0x9596, 0xfffc, 0x1118, 0x2009, 0x0080, 0x04f0,
+ 0x2011, 0x0000, 0x2019, 0x1836, 0x231c, 0xd3ac, 0x0130, 0x9026,
+ 0x20a9, 0x0800, 0x2071, 0x1000, 0x0030, 0x2021, 0x0081, 0x20a9,
+ 0x077f, 0x2071, 0x1081, 0x2e1c, 0x93dd, 0x0000, 0x1140, 0x82ff,
+ 0x11d0, 0x9496, 0x00ff, 0x01b8, 0x2410, 0xc2fd, 0x00a0, 0xbf10,
+ 0x2600, 0x9706, 0xb814, 0x1120, 0x9546, 0x1110, 0x2408, 0x00b0,
+ 0x9745, 0x1148, 0x94c6, 0x007e, 0x0130, 0x94c6, 0x007f, 0x0118,
+ 0x94c6, 0x0080, 0x1d20, 0x8420, 0x8e70, 0x1f04, 0x7ef3, 0x82ff,
+ 0x1118, 0x9085, 0x0001, 0x0018, 0xc2fc, 0x2208, 0x9006, 0x00de,
+ 0x00ee, 0x004e, 0x00be, 0x0005, 0x2001, 0x1836, 0x200c, 0x9184,
+ 0x0080, 0x0110, 0xd18c, 0x0138, 0x7000, 0x908c, 0xff00, 0x810f,
+ 0x9184, 0x000f, 0x004a, 0x7817, 0x0140, 0x2001, 0x19c8, 0x2004,
+ 0x9005, 0x090c, 0x8c10, 0x0005, 0x7f4c, 0x7f4c, 0x7f4c, 0x80f3,
+ 0x7f4c, 0x7f55, 0x7f80, 0x800e, 0x7f4c, 0x7f4c, 0x7f4c, 0x7f4c,
+ 0x7f4c, 0x7f4c, 0x7f4c, 0x7f4c, 0x7817, 0x0140, 0x2001, 0x19c8,
+ 0x2004, 0x9005, 0x090c, 0x8c10, 0x0005, 0x00b6, 0x7110, 0xd1bc,
+ 0x01e8, 0x7120, 0x2160, 0x9c8c, 0x0007, 0x11c0, 0x9c8a, 0x1cd0,
+ 0x02a8, 0x6864, 0x9c02, 0x1290, 0x7008, 0x9084, 0x00ff, 0x6110,
+ 0x2158, 0xb910, 0x9106, 0x1150, 0x700c, 0xb914, 0x9106, 0x1130,
+ 0x7124, 0x610a, 0x2009, 0x0046, 0x080c, 0xa15d, 0x7817, 0x0140,
+ 0x2001, 0x19c8, 0x2004, 0x9005, 0x090c, 0x8c10, 0x00be, 0x0005,
+ 0x00b6, 0x00c6, 0x9484, 0x0fff, 0x0904, 0x7fe4, 0x7110, 0xd1bc,
+ 0x1904, 0x7fe4, 0x7108, 0x700c, 0x2028, 0x918c, 0x00ff, 0x2130,
+ 0x9094, 0xff00, 0x15b0, 0x81ff, 0x15a0, 0x9080, 0x32e9, 0x200d,
+ 0x918c, 0xff00, 0x810f, 0x2001, 0x0080, 0x9106, 0x0904, 0x7fe4,
+ 0x080c, 0x643f, 0x1904, 0x7fe4, 0xbe12, 0xbd16, 0xb800, 0xd0ec,
+ 0x15d8, 0xba04, 0x9294, 0xff00, 0x9286, 0x0600, 0x11a0, 0x080c,
+ 0xa08d, 0x05e8, 0x2b08, 0x7028, 0x6046, 0x702c, 0x604a, 0x6112,
+ 0x6023, 0x0006, 0x7120, 0x610a, 0x7130, 0x6156, 0x2009, 0x0044,
+ 0x080c, 0xce65, 0x0408, 0x080c, 0x67c3, 0x1138, 0xb807, 0x0606,
+ 0x0c30, 0x190c, 0x7ec0, 0x11c0, 0x0898, 0x080c, 0xa08d, 0x2b08,
+ 0x0198, 0x6112, 0x6023, 0x0004, 0x7120, 0x610a, 0x9286, 0x0400,
+ 0x1118, 0x6007, 0x0005, 0x0010, 0x6007, 0x0001, 0x6003, 0x0001,
+ 0x080c, 0x86c1, 0x080c, 0x8c10, 0x7817, 0x0140, 0x2001, 0x19c8,
+ 0x2004, 0x9005, 0x090c, 0x8c10, 0x00ce, 0x00be, 0x0005, 0x2001,
+ 0x180e, 0x2004, 0xd0ec, 0x0120, 0x2011, 0x8049, 0x080c, 0x4b1f,
+ 0x080c, 0xa130, 0x0d48, 0x2b08, 0x6112, 0x6023, 0x0006, 0x7120,
+ 0x610a, 0x7130, 0x6156, 0x6017, 0xf300, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x080c, 0x8679, 0x080c, 0x8c10, 0x08b0, 0x00b6, 0x7110,
+ 0xd1bc, 0x01e8, 0x7020, 0x2060, 0x9c84, 0x0007, 0x11c0, 0x9c82,
+ 0x1cd0, 0x02a8, 0x6864, 0x9c02, 0x1290, 0x7008, 0x9084, 0x00ff,
+ 0x6110, 0x2158, 0xb910, 0x9106, 0x1150, 0x700c, 0xb914, 0x9106,
+ 0x1130, 0x7124, 0x610a, 0x2009, 0x0045, 0x080c, 0xa15d, 0x7817,
+ 0x0140, 0x2001, 0x19c8, 0x2004, 0x9005, 0x090c, 0x8c10, 0x00be,
+ 0x0005, 0x6120, 0x9186, 0x0002, 0x0128, 0x9186, 0x0005, 0x0110,
+ 0x9085, 0x0001, 0x0005, 0x080c, 0x8206, 0x1180, 0x080c, 0x32ae,
+ 0x1168, 0x7010, 0x9084, 0xff00, 0x8007, 0x9086, 0x0000, 0x1130,
+ 0x9184, 0x000f, 0x908a, 0x0006, 0x1208, 0x000b, 0x0005, 0x805d,
+ 0x805e, 0x805d, 0x805d, 0x80c3, 0x80d2, 0x0005, 0x00b6, 0x700c,
+ 0x7108, 0x080c, 0x276e, 0x1904, 0x80c1, 0x080c, 0x643f, 0x1904,
+ 0x80c1, 0xbe12, 0xbd16, 0x7110, 0xd1bc, 0x0540, 0x702c, 0xd084,
+ 0x1120, 0xb800, 0xd0bc, 0x1904, 0x80c1, 0x080c, 0x67c3, 0x0148,
+ 0x9086, 0x0004, 0x0130, 0x080c, 0x67cb, 0x0118, 0x9086, 0x0004,
+ 0x1588, 0x00c6, 0x080c, 0x80e1, 0x00ce, 0x05d8, 0x080c, 0xa08d,
+ 0x2b08, 0x05b8, 0x6112, 0x080c, 0xc2b3, 0x6023, 0x0002, 0x7120,
+ 0x610a, 0x2009, 0x0088, 0x080c, 0xa15d, 0x0458, 0x080c, 0x67c3,
+ 0x0148, 0x9086, 0x0004, 0x0130, 0x080c, 0x67cb, 0x0118, 0x9086,
+ 0x0004, 0x1180, 0x080c, 0xa08d, 0x2b08, 0x01d8, 0x6112, 0x080c,
+ 0xc2b3, 0x6023, 0x0005, 0x7120, 0x610a, 0x2009, 0x0088, 0x080c,
+ 0xa15d, 0x0078, 0x080c, 0xa08d, 0x2b08, 0x0158, 0x6112, 0x080c,
+ 0xc2b3, 0x6023, 0x0004, 0x7120, 0x610a, 0x2009, 0x0001, 0x080c,
+ 0xa15d, 0x00be, 0x0005, 0x7110, 0xd1bc, 0x0158, 0x00d1, 0x0148,
+ 0x080c, 0x8039, 0x1130, 0x7124, 0x610a, 0x2009, 0x0089, 0x080c,
+ 0xa15d, 0x0005, 0x7110, 0xd1bc, 0x0158, 0x0059, 0x0148, 0x080c,
+ 0x8039, 0x1130, 0x7124, 0x610a, 0x2009, 0x008a, 0x080c, 0xa15d,
+ 0x0005, 0x7020, 0x2060, 0x9c84, 0x0007, 0x1158, 0x9c82, 0x1cd0,
+ 0x0240, 0x2001, 0x1819, 0x2004, 0x9c02, 0x1218, 0x9085, 0x0001,
+ 0x0005, 0x9006, 0x0ce8, 0x00b6, 0x7110, 0xd1bc, 0x11d8, 0x7024,
+ 0x2060, 0x9c84, 0x0007, 0x11b0, 0x9c82, 0x1cd0, 0x0298, 0x6864,
+ 0x9c02, 0x1280, 0x7008, 0x9084, 0x00ff, 0x6110, 0x2158, 0xb910,
+ 0x9106, 0x1140, 0x700c, 0xb914, 0x9106, 0x1120, 0x2009, 0x0051,
+ 0x080c, 0xa15d, 0x7817, 0x0140, 0x2001, 0x19c8, 0x2004, 0x9005,
+ 0x090c, 0x8c10, 0x00be, 0x0005, 0x2031, 0x0105, 0x0069, 0x0005,
+ 0x2031, 0x0206, 0x0049, 0x0005, 0x2031, 0x0207, 0x0029, 0x0005,
+ 0x2031, 0x0213, 0x0009, 0x0005, 0x00c6, 0x0096, 0x00f6, 0x7000,
+ 0x9084, 0xf000, 0x9086, 0xc000, 0x05d0, 0x080c, 0xa08d, 0x05b8,
+ 0x0066, 0x00c6, 0x0046, 0x2011, 0x0263, 0x2204, 0x8211, 0x220c,
+ 0x080c, 0x276e, 0x15a0, 0x080c, 0x643f, 0x1588, 0xbe12, 0xbd16,
+ 0x2b00, 0x004e, 0x00ce, 0x6012, 0x080c, 0xc2b3, 0x080c, 0x1031,
+ 0x0510, 0x2900, 0x605a, 0x9006, 0xa802, 0xa866, 0xac6a, 0xa85c,
+ 0x90f8, 0x001b, 0x20a9, 0x000e, 0xa860, 0x20e8, 0x20e1, 0x0000,
+ 0x2fa0, 0x2e98, 0x4003, 0x006e, 0x6616, 0x6007, 0x003e, 0x6023,
+ 0x0001, 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x00fe,
+ 0x009e, 0x00ce, 0x0005, 0x080c, 0xa0e3, 0x006e, 0x0cc0, 0x004e,
+ 0x00ce, 0x0cc8, 0x00c6, 0x7000, 0x908c, 0xff00, 0x9184, 0xf000,
+ 0x810f, 0x9086, 0x2000, 0x1904, 0x81d1, 0x9186, 0x0022, 0x15f0,
+ 0x2001, 0x0111, 0x2004, 0x9005, 0x1904, 0x81d3, 0x7030, 0x908e,
+ 0x0400, 0x0904, 0x81d3, 0x908e, 0x6000, 0x05e8, 0x908e, 0x5400,
+ 0x05d0, 0x908e, 0x0300, 0x11d8, 0x2009, 0x1836, 0x210c, 0xd18c,
+ 0x1590, 0xd1a4, 0x1580, 0x080c, 0x6781, 0x0588, 0x68ac, 0x9084,
+ 0x00ff, 0x7100, 0x918c, 0x00ff, 0x9106, 0x1518, 0x687c, 0x69ac,
+ 0x918c, 0xff00, 0x9105, 0x7104, 0x9106, 0x11d8, 0x00e0, 0x2009,
+ 0x0103, 0x210c, 0xd1b4, 0x11a8, 0x908e, 0x5200, 0x09e8, 0x908e,
+ 0x0500, 0x09d0, 0x908e, 0x5000, 0x09b8, 0x0058, 0x9186, 0x0023,
+ 0x1140, 0x080c, 0x80e1, 0x0128, 0x6004, 0x9086, 0x0002, 0x0118,
+ 0x0000, 0x9006, 0x0010, 0x9085, 0x0001, 0x00ce, 0x0005, 0x7030,
+ 0x908e, 0x0300, 0x0118, 0x908e, 0x5200, 0x1d98, 0x2001, 0x1836,
+ 0x2004, 0x9084, 0x0009, 0x9086, 0x0008, 0x0d68, 0x0c50, 0x00f6,
+ 0x2079, 0x0200, 0x7800, 0xc0e5, 0xc0cc, 0x7802, 0x00fe, 0x0005,
+ 0x00f6, 0x2079, 0x1800, 0x7834, 0xd084, 0x1130, 0x2079, 0x0200,
+ 0x7800, 0x9085, 0x1200, 0x7802, 0x00fe, 0x0005, 0x00e6, 0x2071,
+ 0x1800, 0x7034, 0xc084, 0x7036, 0x00ee, 0x0005, 0x0016, 0x2001,
+ 0x1836, 0x200c, 0x9184, 0x0080, 0x0118, 0xd18c, 0x0118, 0x9006,
+ 0x001e, 0x0005, 0x9085, 0x0001, 0x0cd8, 0x2071, 0x19d2, 0x7003,
+ 0x0003, 0x700f, 0x0361, 0x9006, 0x701a, 0x7072, 0x7012, 0x7017,
+ 0x1cd0, 0x7007, 0x0000, 0x7026, 0x702b, 0x966c, 0x7032, 0x7037,
+ 0x96d4, 0x703f, 0xffff, 0x7042, 0x7047, 0x546d, 0x704a, 0x705b,
+ 0x8375, 0x080c, 0x104a, 0x090c, 0x0dfa, 0x2900, 0x703a, 0xa867,
+ 0x0003, 0xa86f, 0x0100, 0xa8ab, 0xdcb0, 0x0005, 0x2071, 0x19d2,
+ 0x1d04, 0x82c9, 0x2091, 0x6000, 0x700c, 0x8001, 0x700e, 0x1510,
+ 0x2001, 0x187d, 0x2004, 0xd0c4, 0x0158, 0x3a00, 0xd08c, 0x1140,
+ 0x20d1, 0x0000, 0x20d1, 0x0001, 0x20d1, 0x0000, 0x080c, 0x0dfa,
+ 0x700f, 0x0361, 0x7007, 0x0001, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x83ba, 0x7040, 0x900d, 0x0148, 0x8109, 0x7142, 0x1130, 0x7044,
+ 0x080f, 0x0018, 0x0126, 0x2091, 0x8000, 0x7024, 0x900d, 0x0188,
+ 0x7020, 0x8001, 0x7022, 0x1168, 0x7023, 0x0009, 0x8109, 0x7126,
+ 0x9186, 0x03e8, 0x1110, 0x7028, 0x080f, 0x81ff, 0x1110, 0x7028,
+ 0x080f, 0x7030, 0x900d, 0x0180, 0x702c, 0x8001, 0x702e, 0x1160,
+ 0x702f, 0x0009, 0x8109, 0x7132, 0x0128, 0x9184, 0x007f, 0x090c,
+ 0x9802, 0x0010, 0x7034, 0x080f, 0x703c, 0x9005, 0x0118, 0x0310,
+ 0x8001, 0x703e, 0x704c, 0x900d, 0x0168, 0x7048, 0x8001, 0x704a,
+ 0x1148, 0x704b, 0x0009, 0x8109, 0x714e, 0x1120, 0x7150, 0x714e,
+ 0x7058, 0x080f, 0x7018, 0x900d, 0x01d8, 0x0016, 0x7070, 0x900d,
+ 0x0158, 0x706c, 0x8001, 0x706e, 0x1138, 0x706f, 0x0009, 0x8109,
+ 0x7172, 0x1110, 0x7074, 0x080f, 0x001e, 0x7008, 0x8001, 0x700a,
+ 0x1138, 0x700b, 0x0009, 0x8109, 0x711a, 0x1110, 0x701c, 0x080f,
+ 0x012e, 0x7004, 0x0002, 0x82f1, 0x82f2, 0x830e, 0x00e6, 0x2071,
+ 0x19d2, 0x7018, 0x9005, 0x1120, 0x711a, 0x721e, 0x700b, 0x0009,
+ 0x00ee, 0x0005, 0x00e6, 0x0006, 0x2071, 0x19d2, 0x701c, 0x9206,
+ 0x1120, 0x701a, 0x701e, 0x7072, 0x7076, 0x000e, 0x00ee, 0x0005,
+ 0x00e6, 0x2071, 0x19d2, 0xb888, 0x9102, 0x0208, 0xb98a, 0x00ee,
+ 0x0005, 0x0005, 0x00b6, 0x7110, 0x080c, 0x649f, 0x1168, 0xb888,
+ 0x8001, 0x0250, 0xb88a, 0x1140, 0x0126, 0x2091, 0x8000, 0x0016,
+ 0x080c, 0x8c10, 0x001e, 0x012e, 0x8108, 0x9182, 0x0800, 0x0218,
+ 0x900e, 0x7007, 0x0002, 0x7112, 0x00be, 0x0005, 0x7014, 0x2060,
+ 0x0126, 0x2091, 0x8000, 0x6040, 0x9005, 0x0128, 0x8001, 0x6042,
+ 0x1110, 0x080c, 0xc144, 0x6018, 0x9005, 0x0528, 0x8001, 0x601a,
+ 0x1510, 0x6120, 0x9186, 0x0003, 0x0118, 0x9186, 0x0006, 0x11c8,
+ 0x080c, 0xbe37, 0x01b0, 0x6014, 0x2048, 0xa884, 0x908a, 0x199a,
+ 0x0280, 0x9082, 0x1999, 0xa886, 0x908a, 0x199a, 0x0210, 0x2001,
+ 0x1999, 0x8003, 0x800b, 0x810b, 0x9108, 0x611a, 0xa87c, 0xd0e4,
+ 0x0110, 0x080c, 0xbb23, 0x012e, 0x9c88, 0x0018, 0x7116, 0x2001,
+ 0x1819, 0x2004, 0x9102, 0x0220, 0x7017, 0x1cd0, 0x7007, 0x0000,
+ 0x0005, 0x00e6, 0x2071, 0x19d2, 0x7027, 0x07d0, 0x7023, 0x0009,
+ 0x00ee, 0x0005, 0x2001, 0x19db, 0x2003, 0x0000, 0x0005, 0x00e6,
+ 0x2071, 0x19d2, 0x7132, 0x702f, 0x0009, 0x00ee, 0x0005, 0x2011,
+ 0x19de, 0x2013, 0x0000, 0x0005, 0x00e6, 0x2071, 0x19d2, 0x711a,
+ 0x721e, 0x700b, 0x0009, 0x00ee, 0x0005, 0x0086, 0x0026, 0x7054,
+ 0x8000, 0x7056, 0x2001, 0x19e0, 0x2044, 0xa06c, 0x9086, 0x0000,
+ 0x0150, 0x7068, 0xa09a, 0x7064, 0xa096, 0x7060, 0xa092, 0x705c,
+ 0xa08e, 0x080c, 0x112e, 0x002e, 0x008e, 0x0005, 0x0006, 0x0016,
+ 0x0096, 0x00a6, 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x0156,
+ 0x080c, 0x823e, 0x015e, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be,
+ 0x00ae, 0x009e, 0x001e, 0x000e, 0x0005, 0x00e6, 0x2071, 0x19d2,
+ 0x7172, 0x7276, 0x706f, 0x0009, 0x00ee, 0x0005, 0x00e6, 0x0006,
+ 0x2071, 0x19d2, 0x7074, 0x9206, 0x1110, 0x7072, 0x7076, 0x000e,
+ 0x00ee, 0x0005, 0x2069, 0x1800, 0x69e4, 0xd1e4, 0x1518, 0x0026,
+ 0xd1ec, 0x0140, 0x6a50, 0x6870, 0x9202, 0x0288, 0x8117, 0x9294,
+ 0x00c0, 0x0088, 0x9184, 0x0007, 0x01a0, 0x8109, 0x9184, 0x0007,
+ 0x0110, 0x69e6, 0x0070, 0x8107, 0x9084, 0x0007, 0x910d, 0x8107,
+ 0x9106, 0x9094, 0x00c0, 0x9184, 0xff3f, 0x9205, 0x68e6, 0x080c,
+ 0x0f11, 0x002e, 0x0005, 0x00c6, 0x2061, 0x1a48, 0x00ce, 0x0005,
+ 0x9184, 0x000f, 0x8003, 0x8003, 0x8003, 0x9080, 0x1a48, 0x2060,
+ 0x0005, 0xa884, 0x908a, 0x199a, 0x1638, 0x9005, 0x1150, 0x00c6,
+ 0x2061, 0x1a48, 0x6014, 0x00ce, 0x9005, 0x1130, 0x2001, 0x001e,
+ 0x0018, 0x908e, 0xffff, 0x01b0, 0x8003, 0x800b, 0x810b, 0x9108,
+ 0x611a, 0xa87c, 0x908c, 0x00c0, 0x918e, 0x00c0, 0x0904, 0x847b,
+ 0xd0b4, 0x1168, 0xd0bc, 0x1904, 0x8454, 0x2009, 0x0006, 0x080c,
+ 0x84a8, 0x0005, 0x900e, 0x0c60, 0x2001, 0x1999, 0x08b0, 0xd0fc,
+ 0x0160, 0x908c, 0x0003, 0x0120, 0x918e, 0x0003, 0x1904, 0x84a2,
+ 0x908c, 0x2020, 0x918e, 0x2020, 0x01a8, 0x6024, 0xd0d4, 0x11e8,
+ 0x2009, 0x187d, 0x2104, 0xd084, 0x1138, 0x87ff, 0x1120, 0x2009,
+ 0x0043, 0x0804, 0xa15d, 0x0005, 0x87ff, 0x1de8, 0x2009, 0x0042,
+ 0x0804, 0xa15d, 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac,
+ 0x0d20, 0x6024, 0xc0cd, 0x6026, 0x0c00, 0xc0d4, 0x6026, 0xa890,
+ 0x602e, 0xa88c, 0x6032, 0x08e0, 0xd0fc, 0x0160, 0x908c, 0x0003,
+ 0x0120, 0x918e, 0x0003, 0x1904, 0x84a2, 0x908c, 0x2020, 0x918e,
+ 0x2020, 0x0170, 0x0076, 0x00f6, 0x2c78, 0x080c, 0x16db, 0x00fe,
+ 0x007e, 0x87ff, 0x1120, 0x2009, 0x0042, 0x080c, 0xa15d, 0x0005,
+ 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac, 0x0d58, 0x6124,
+ 0xc1cd, 0x6126, 0x0c38, 0xd0fc, 0x0188, 0x908c, 0x2020, 0x918e,
+ 0x2020, 0x01a8, 0x9084, 0x0003, 0x908e, 0x0002, 0x0148, 0x87ff,
+ 0x1120, 0x2009, 0x0041, 0x080c, 0xa15d, 0x0005, 0x00b9, 0x0ce8,
+ 0x87ff, 0x1dd8, 0x2009, 0x0043, 0x080c, 0xa15d, 0x0cb0, 0x6110,
+ 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1ac, 0x0d20, 0x6124, 0xc1cd,
+ 0x6126, 0x0c00, 0x2009, 0x0004, 0x0019, 0x0005, 0x2009, 0x0001,
+ 0x0096, 0x080c, 0xbe37, 0x0518, 0x6014, 0x2048, 0xa982, 0xa800,
+ 0x6016, 0x9186, 0x0001, 0x1188, 0xa97c, 0x918c, 0x8100, 0x918e,
+ 0x8100, 0x1158, 0x00c6, 0x2061, 0x1a48, 0x6200, 0xd28c, 0x1120,
+ 0x6204, 0x8210, 0x0208, 0x6206, 0x00ce, 0x080c, 0x6923, 0x6014,
+ 0x904d, 0x0076, 0x2039, 0x0000, 0x190c, 0x83f1, 0x007e, 0x009e,
+ 0x0005, 0x0156, 0x00c6, 0x2061, 0x1a48, 0x6000, 0x81ff, 0x0110,
+ 0x9205, 0x0008, 0x9204, 0x6002, 0x00ce, 0x015e, 0x0005, 0x6800,
+ 0xd08c, 0x1138, 0x6808, 0x9005, 0x0120, 0x8001, 0x680a, 0x9085,
+ 0x0001, 0x0005, 0x0126, 0x2091, 0x8000, 0x0036, 0x0046, 0x20a9,
+ 0x0010, 0x9006, 0x8004, 0x8086, 0x818e, 0x1208, 0x9200, 0x1f04,
+ 0x84f3, 0x8086, 0x818e, 0x004e, 0x003e, 0x012e, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x0076, 0x0156, 0x20a9, 0x0010, 0x9005, 0x01c8,
+ 0x911a, 0x12b8, 0x8213, 0x818d, 0x0228, 0x911a, 0x1220, 0x1f04,
+ 0x850a, 0x0028, 0x911a, 0x2308, 0x8210, 0x1f04, 0x850a, 0x0006,
+ 0x3200, 0x9084, 0xefff, 0x2080, 0x000e, 0x015e, 0x007e, 0x012e,
+ 0x0005, 0x0006, 0x3200, 0x9085, 0x1000, 0x0ca8, 0x0126, 0x2091,
+ 0x2800, 0x2079, 0x19bf, 0x012e, 0x00d6, 0x2069, 0x19bf, 0x6803,
+ 0x0005, 0x0156, 0x0146, 0x01d6, 0x20e9, 0x0000, 0x2069, 0x0200,
+ 0x080c, 0x9eeb, 0x0401, 0x080c, 0x9ed6, 0x00e9, 0x080c, 0x9ed9,
+ 0x00d1, 0x080c, 0x9edc, 0x00b9, 0x080c, 0x9edf, 0x00a1, 0x080c,
+ 0x9ee2, 0x0089, 0x080c, 0x9ee5, 0x0071, 0x080c, 0x9ee8, 0x0059,
+ 0x01de, 0x014e, 0x015e, 0x2069, 0x0004, 0x2d04, 0x9085, 0x8001,
+ 0x206a, 0x00de, 0x0005, 0x20a9, 0x0020, 0x20a1, 0x0240, 0x2001,
+ 0x0000, 0x4004, 0x0005, 0x00c6, 0x6027, 0x0001, 0x7804, 0x9084,
+ 0x0007, 0x0002, 0x8574, 0x8598, 0x85d9, 0x857a, 0x8598, 0x8574,
+ 0x8572, 0x8572, 0x080c, 0x0dfa, 0x080c, 0x835a, 0x080c, 0x8c10,
+ 0x00ce, 0x0005, 0x62c0, 0x82ff, 0x1110, 0x00ce, 0x0005, 0x2011,
+ 0x5d94, 0x080c, 0x82da, 0x7828, 0x9092, 0x00c8, 0x1228, 0x8000,
+ 0x782a, 0x080c, 0x5dd4, 0x0c88, 0x62c0, 0x080c, 0x9eef, 0x080c,
+ 0x5d94, 0x7807, 0x0003, 0x7827, 0x0000, 0x782b, 0x0000, 0x0c28,
+ 0x080c, 0x835a, 0x6220, 0xd2a4, 0x0170, 0xd2cc, 0x0160, 0x782b,
+ 0x0000, 0x7824, 0x9065, 0x090c, 0x0dfa, 0x2009, 0x0013, 0x080c,
+ 0xa15d, 0x00ce, 0x0005, 0x00c6, 0x7824, 0x9065, 0x090c, 0x0dfa,
+ 0x7828, 0x9092, 0xc350, 0x12c0, 0x8000, 0x782a, 0x00ce, 0x080c,
+ 0x2afe, 0x0278, 0x00c6, 0x7924, 0x2160, 0x6010, 0x906d, 0x090c,
+ 0x0dfa, 0x7807, 0x0000, 0x7827, 0x0000, 0x00ce, 0x080c, 0x8c10,
+ 0x0c00, 0x080c, 0x9632, 0x08e8, 0x2011, 0x0130, 0x2214, 0x080c,
+ 0x9eef, 0x080c, 0xdc28, 0x2009, 0x0014, 0x080c, 0xa15d, 0x00ce,
+ 0x0880, 0x2001, 0x19db, 0x2003, 0x0000, 0x62c0, 0x82ff, 0x1160,
+ 0x782b, 0x0000, 0x7824, 0x9065, 0x090c, 0x0dfa, 0x2009, 0x0013,
+ 0x080c, 0xa1af, 0x00ce, 0x0005, 0x00b6, 0x00c6, 0x00d6, 0x7824,
+ 0x9005, 0x090c, 0x0dfa, 0x7828, 0x9092, 0xc350, 0x1648, 0x8000,
+ 0x782a, 0x00de, 0x00ce, 0x00be, 0x080c, 0x2afe, 0x02f0, 0x00b6,
+ 0x00c6, 0x00d6, 0x781c, 0x905d, 0x090c, 0x0dfa, 0xb800, 0xc0dc,
+ 0xb802, 0x7924, 0x2160, 0x080c, 0xa0e3, 0xb93c, 0x81ff, 0x090c,
+ 0x0dfa, 0x8109, 0xb93e, 0x7807, 0x0000, 0x7827, 0x0000, 0x00de,
+ 0x00ce, 0x00be, 0x080c, 0x8c10, 0x0868, 0x080c, 0x9632, 0x0850,
+ 0x2011, 0x0130, 0x2214, 0x080c, 0x9eef, 0x080c, 0xdc28, 0x7824,
+ 0x9065, 0x2009, 0x0014, 0x080c, 0xa15d, 0x00de, 0x00ce, 0x00be,
+ 0x0804, 0x85ea, 0x00c6, 0x2001, 0x009b, 0x2004, 0xd0fc, 0x190c,
+ 0x1dec, 0x6024, 0x6027, 0x0002, 0xd0f4, 0x1580, 0x62c8, 0x60c4,
+ 0x9205, 0x1170, 0x783c, 0x9065, 0x0130, 0x2009, 0x0049, 0x080c,
+ 0xa15d, 0x00ce, 0x0005, 0x2011, 0x19de, 0x2013, 0x0000, 0x0cc8,
+ 0x793c, 0x81ff, 0x0dc0, 0x7944, 0x9192, 0x7530, 0x12f0, 0x8108,
+ 0x7946, 0x793c, 0x9188, 0x0008, 0x210c, 0x918e, 0x0006, 0x1138,
+ 0x6014, 0x9084, 0x1984, 0x9085, 0x0012, 0x6016, 0x0c10, 0x6014,
+ 0x9084, 0x1984, 0x9085, 0x0016, 0x6016, 0x08d8, 0x793c, 0x2160,
+ 0x2009, 0x004a, 0x080c, 0xa15d, 0x08a0, 0x7848, 0xc085, 0x784a,
+ 0x0880, 0x0006, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0x19bf, 0x6020, 0x8000, 0x6022, 0x6010,
+ 0x9005, 0x0148, 0x9080, 0x0003, 0x2102, 0x6112, 0x012e, 0x00ce,
+ 0x001e, 0x000e, 0x0005, 0x6116, 0x6112, 0x0cc0, 0x00d6, 0x2069,
+ 0x19bf, 0xb800, 0xd0d4, 0x0168, 0x6820, 0x8000, 0x6822, 0x9086,
+ 0x0001, 0x1110, 0x2b00, 0x681e, 0x00de, 0x0804, 0x8c10, 0x00de,
+ 0x0005, 0xc0d5, 0xb802, 0x6818, 0x9005, 0x0168, 0xb856, 0xb85b,
+ 0x0000, 0x0086, 0x0006, 0x2b00, 0x681a, 0x008e, 0xa05a, 0x008e,
+ 0x2069, 0x19bf, 0x0c08, 0xb856, 0xb85a, 0x2b00, 0x681a, 0x681e,
+ 0x08d8, 0x0006, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0x19bf, 0x6020, 0x8000, 0x6022, 0x6008,
+ 0x9005, 0x0148, 0x9080, 0x0003, 0x2102, 0x610a, 0x012e, 0x00ce,
+ 0x001e, 0x000e, 0x0005, 0x610e, 0x610a, 0x0cc0, 0x00c6, 0x600f,
+ 0x0000, 0x2c08, 0x2061, 0x19bf, 0x6034, 0x9005, 0x0130, 0x9080,
+ 0x0003, 0x2102, 0x6136, 0x00ce, 0x0005, 0x613a, 0x6136, 0x00ce,
+ 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x00b6, 0x0096, 0x0076,
+ 0x0066, 0x0056, 0x0036, 0x0026, 0x0016, 0x0006, 0x0126, 0x902e,
+ 0x2071, 0x19bf, 0x7638, 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff,
+ 0x0904, 0x876d, 0x6010, 0x2058, 0xb8a0, 0x9206, 0x1904, 0x8768,
+ 0x87ff, 0x0120, 0x6054, 0x9106, 0x1904, 0x8768, 0x703c, 0x9c06,
+ 0x1178, 0x0036, 0x2019, 0x0001, 0x080c, 0x999d, 0x7033, 0x0000,
+ 0x9006, 0x703e, 0x7042, 0x7046, 0x704a, 0x003e, 0x2029, 0x0001,
+ 0x7038, 0x9c36, 0x1110, 0x660c, 0x763a, 0x7034, 0x9c36, 0x1140,
+ 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x7036, 0x0010, 0x7037, 0x0000,
+ 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x600f, 0x0000, 0x080c, 0xbe37, 0x01f0, 0x6014, 0x2048, 0x6020,
+ 0x9086, 0x0003, 0x15b8, 0x6004, 0x9086, 0x0040, 0x090c, 0x9b78,
+ 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x0016, 0x0036, 0x0076,
+ 0x080c, 0xc12d, 0x080c, 0xdb2e, 0x080c, 0x6ae9, 0x007e, 0x003e,
+ 0x001e, 0x080c, 0xc022, 0x080c, 0xa113, 0x00ce, 0x0804, 0x8707,
+ 0x2c78, 0x600c, 0x2060, 0x0804, 0x8707, 0x85ff, 0x0120, 0x0036,
+ 0x080c, 0x8ced, 0x003e, 0x012e, 0x000e, 0x001e, 0x002e, 0x003e,
+ 0x005e, 0x006e, 0x007e, 0x009e, 0x00be, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x6020, 0x9086, 0x0006, 0x1158, 0x0016, 0x0036,
+ 0x0076, 0x080c, 0xdb2e, 0x080c, 0xd830, 0x007e, 0x003e, 0x001e,
+ 0x0890, 0x6020, 0x9086, 0x000a, 0x0904, 0x8752, 0x0804, 0x874b,
+ 0x0006, 0x0066, 0x0096, 0x00c6, 0x00d6, 0x00f6, 0x9036, 0x0126,
+ 0x2091, 0x8000, 0x2079, 0x19bf, 0x7838, 0x9065, 0x0904, 0x87ed,
+ 0x600c, 0x0006, 0x600f, 0x0000, 0x783c, 0x9c06, 0x1168, 0x0036,
+ 0x2019, 0x0001, 0x080c, 0x999d, 0x7833, 0x0000, 0x901e, 0x7b3e,
+ 0x7b42, 0x7b46, 0x7b4a, 0x003e, 0x080c, 0xbe37, 0x0548, 0x6014,
+ 0x2048, 0x6020, 0x9086, 0x0003, 0x1590, 0x3e08, 0x918e, 0x0002,
+ 0x1188, 0x6010, 0x9005, 0x0170, 0x00b6, 0x2058, 0xb800, 0x00be,
+ 0xd0bc, 0x0140, 0x6040, 0x9005, 0x11a8, 0x2001, 0x1960, 0x2004,
+ 0x6042, 0x0080, 0x6004, 0x9086, 0x0040, 0x090c, 0x9b78, 0xa867,
+ 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0x6adc, 0x080c, 0xc022,
+ 0x080c, 0xa113, 0x000e, 0x0804, 0x87a5, 0x7e3a, 0x7e36, 0x012e,
+ 0x00fe, 0x00de, 0x00ce, 0x009e, 0x006e, 0x000e, 0x0005, 0x6020,
+ 0x9086, 0x0006, 0x1118, 0x080c, 0xd830, 0x0c50, 0x6020, 0x9086,
+ 0x000a, 0x09f8, 0x08b8, 0x0016, 0x0026, 0x0086, 0x9046, 0x0099,
+ 0x080c, 0x88ee, 0x008e, 0x002e, 0x001e, 0x0005, 0x00f6, 0x0126,
+ 0x2079, 0x19bf, 0x2091, 0x8000, 0x080c, 0x8985, 0x080c, 0x8a15,
+ 0x012e, 0x00fe, 0x0005, 0x00b6, 0x0096, 0x00f6, 0x00e6, 0x00d6,
+ 0x00c6, 0x0066, 0x0016, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071,
+ 0x19bf, 0x7614, 0x2660, 0x2678, 0x8cff, 0x0904, 0x88b3, 0x6010,
+ 0x2058, 0xb8a0, 0x9206, 0x1904, 0x88ae, 0x88ff, 0x0120, 0x6054,
+ 0x9106, 0x1904, 0x88ae, 0x7024, 0x9c06, 0x1568, 0x2069, 0x0100,
+ 0x6820, 0xd0a4, 0x0110, 0xd0cc, 0x1508, 0x080c, 0x835a, 0x080c,
+ 0x9656, 0x68c3, 0x0000, 0x080c, 0x9b78, 0x7027, 0x0000, 0x0036,
+ 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100,
+ 0x080c, 0x2c88, 0x9006, 0x080c, 0x2c88, 0x2069, 0x0100, 0x6824,
+ 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x0028, 0x6003, 0x0009,
+ 0x630a, 0x0804, 0x88ae, 0x7014, 0x9c36, 0x1110, 0x660c, 0x7616,
+ 0x7010, 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x7012,
+ 0x0010, 0x7013, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x6014, 0x2048, 0x080c,
+ 0xbe37, 0x01e8, 0x6020, 0x9086, 0x0003, 0x1580, 0x080c, 0xc03f,
+ 0x1118, 0x080c, 0xaa81, 0x0098, 0xa867, 0x0103, 0xab7a, 0xa877,
+ 0x0000, 0x0016, 0x0036, 0x0086, 0x080c, 0xc12d, 0x080c, 0xdb2e,
+ 0x080c, 0x6ae9, 0x008e, 0x003e, 0x001e, 0x080c, 0xc022, 0x080c,
+ 0xa113, 0x080c, 0x9a4e, 0x00ce, 0x0804, 0x882c, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0x882c, 0x012e, 0x000e, 0x001e, 0x006e, 0x00ce,
+ 0x00de, 0x00ee, 0x00fe, 0x009e, 0x00be, 0x0005, 0x6020, 0x9086,
+ 0x0006, 0x1158, 0x0016, 0x0036, 0x0086, 0x080c, 0xdb2e, 0x080c,
+ 0xd830, 0x008e, 0x003e, 0x001e, 0x08d0, 0x080c, 0xaa81, 0x6020,
+ 0x9086, 0x0002, 0x1160, 0x6004, 0x0006, 0x9086, 0x0085, 0x000e,
+ 0x0904, 0x8894, 0x9086, 0x008b, 0x0904, 0x8894, 0x0840, 0x6020,
+ 0x9086, 0x0005, 0x1920, 0x6004, 0x0006, 0x9086, 0x0085, 0x000e,
+ 0x09c8, 0x9086, 0x008b, 0x09b0, 0x0804, 0x88a7, 0x00b6, 0x00a6,
+ 0x0096, 0x00c6, 0x0006, 0x0126, 0x2091, 0x8000, 0x9280, 0x1000,
+ 0x2004, 0x905d, 0x0904, 0x897e, 0x00f6, 0x00e6, 0x00d6, 0x0066,
+ 0x2071, 0x19bf, 0xbe54, 0x7018, 0x9b06, 0x1108, 0x761a, 0x701c,
+ 0x9b06, 0x1130, 0x86ff, 0x1118, 0x7018, 0x701e, 0x0008, 0x761e,
+ 0xb858, 0x904d, 0x0108, 0xae56, 0x96d5, 0x0000, 0x0110, 0x2900,
+ 0xb05a, 0xb857, 0x0000, 0xb85b, 0x0000, 0xb800, 0xc0d4, 0xc0dc,
+ 0xb802, 0x080c, 0x63d2, 0x0904, 0x897a, 0x7624, 0x86ff, 0x0904,
+ 0x8969, 0x9680, 0x0005, 0x2004, 0x9906, 0x15d8, 0x00d6, 0x2069,
+ 0x0100, 0x68c0, 0x9005, 0x0560, 0x080c, 0x835a, 0x080c, 0x9656,
+ 0x68c3, 0x0000, 0x080c, 0x9b78, 0x7027, 0x0000, 0x0036, 0x2069,
+ 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c,
+ 0x2c88, 0x9006, 0x080c, 0x2c88, 0x2069, 0x0100, 0x6824, 0xd084,
+ 0x0110, 0x6827, 0x0001, 0x003e, 0x00de, 0x00c6, 0xb83c, 0x9005,
+ 0x0110, 0x8001, 0xb83e, 0x2660, 0x080c, 0xa113, 0x00ce, 0x0048,
+ 0x00de, 0x00c6, 0x2660, 0x6003, 0x0009, 0x630a, 0x00ce, 0x0804,
+ 0x8921, 0x89ff, 0x0158, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000,
+ 0x080c, 0xc12d, 0x080c, 0xdb2e, 0x080c, 0x6ae9, 0x080c, 0x9a4e,
+ 0x0804, 0x8921, 0x006e, 0x00de, 0x00ee, 0x00fe, 0x012e, 0x000e,
+ 0x00ce, 0x009e, 0x00ae, 0x00be, 0x0005, 0x0096, 0x0006, 0x0066,
+ 0x00c6, 0x00d6, 0x9036, 0x7814, 0x9065, 0x0904, 0x89e8, 0x600c,
+ 0x0006, 0x600f, 0x0000, 0x7824, 0x9c06, 0x1580, 0x2069, 0x0100,
+ 0x6820, 0xd0a4, 0x0110, 0xd0cc, 0x1508, 0x080c, 0x835a, 0x080c,
+ 0x9656, 0x68c3, 0x0000, 0x080c, 0x9b78, 0x7827, 0x0000, 0x0036,
+ 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100,
+ 0x080c, 0x2c88, 0x9006, 0x080c, 0x2c88, 0x2069, 0x0100, 0x6824,
+ 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x0040, 0x080c, 0x6779,
+ 0x1520, 0x6003, 0x0009, 0x630a, 0x2c30, 0x00f8, 0x6014, 0x2048,
+ 0x080c, 0xbe35, 0x01b0, 0x6020, 0x9086, 0x0003, 0x1508, 0x080c,
+ 0xc03f, 0x1118, 0x080c, 0xaa81, 0x0060, 0x080c, 0x6779, 0x1168,
+ 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0x6ae9, 0x080c,
+ 0xc022, 0x080c, 0xa113, 0x080c, 0x9a4e, 0x000e, 0x0804, 0x898c,
+ 0x7e16, 0x7e12, 0x00de, 0x00ce, 0x006e, 0x000e, 0x009e, 0x0005,
+ 0x6020, 0x9086, 0x0006, 0x1118, 0x080c, 0xd830, 0x0c50, 0x080c,
+ 0xaa81, 0x6020, 0x9086, 0x0002, 0x1150, 0x6004, 0x0006, 0x9086,
+ 0x0085, 0x000e, 0x0990, 0x9086, 0x008b, 0x0978, 0x08d0, 0x6020,
+ 0x9086, 0x0005, 0x19b0, 0x6004, 0x0006, 0x9086, 0x0085, 0x000e,
+ 0x0d18, 0x9086, 0x008b, 0x0d00, 0x0860, 0x0006, 0x0066, 0x0096,
+ 0x00b6, 0x00c6, 0x00d6, 0x7818, 0x905d, 0x0904, 0x8a95, 0xb854,
+ 0x0006, 0x9006, 0xb856, 0xb85a, 0xb800, 0xc0d4, 0xc0dc, 0xb802,
+ 0x080c, 0x63d2, 0x0904, 0x8a92, 0x7e24, 0x86ff, 0x0904, 0x8a85,
+ 0x9680, 0x0005, 0x2004, 0x9906, 0x1904, 0x8a85, 0x00d6, 0x2069,
+ 0x0100, 0x68c0, 0x9005, 0x0904, 0x8a7c, 0x080c, 0x835a, 0x080c,
+ 0x9656, 0x68c3, 0x0000, 0x080c, 0x9b78, 0x7827, 0x0000, 0x0036,
+ 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100,
+ 0x080c, 0x2c88, 0x9006, 0x080c, 0x2c88, 0x2069, 0x0100, 0x6824,
+ 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x00de, 0x00c6, 0x3e08,
+ 0x918e, 0x0002, 0x1168, 0xb800, 0xd0bc, 0x0150, 0x9680, 0x0010,
+ 0x200c, 0x81ff, 0x1518, 0x2009, 0x1960, 0x210c, 0x2102, 0x00f0,
+ 0xb83c, 0x9005, 0x0110, 0x8001, 0xb83e, 0x2660, 0x600f, 0x0000,
+ 0x080c, 0xa113, 0x00ce, 0x0048, 0x00de, 0x00c6, 0x2660, 0x6003,
+ 0x0009, 0x630a, 0x00ce, 0x0804, 0x8a28, 0x89ff, 0x0138, 0xa867,
+ 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0x6ae9, 0x080c, 0x9a4e,
+ 0x0804, 0x8a28, 0x000e, 0x0804, 0x8a1c, 0x781e, 0x781a, 0x00de,
+ 0x00ce, 0x00be, 0x009e, 0x006e, 0x000e, 0x0005, 0x00e6, 0x00d6,
+ 0x0096, 0x0066, 0xb800, 0xd0dc, 0x01a0, 0xb84c, 0x904d, 0x0188,
+ 0xa878, 0x9606, 0x1170, 0x2071, 0x19bf, 0x7024, 0x9035, 0x0148,
+ 0x9080, 0x0005, 0x2004, 0x9906, 0x1120, 0xb800, 0xc0dc, 0xb802,
+ 0x0029, 0x006e, 0x009e, 0x00de, 0x00ee, 0x0005, 0x00f6, 0x2079,
+ 0x0100, 0x78c0, 0x9005, 0x1138, 0x00c6, 0x2660, 0x6003, 0x0009,
+ 0x630a, 0x00ce, 0x04b8, 0x080c, 0x9656, 0x78c3, 0x0000, 0x080c,
+ 0x9b78, 0x7027, 0x0000, 0x0036, 0x2079, 0x0140, 0x7b04, 0x9384,
+ 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2c88, 0x9006, 0x080c,
+ 0x2c88, 0x2079, 0x0100, 0x7824, 0xd084, 0x0110, 0x7827, 0x0001,
+ 0x080c, 0x9b78, 0x003e, 0x080c, 0x63d2, 0x00c6, 0xb83c, 0x9005,
+ 0x0110, 0x8001, 0xb83e, 0x2660, 0x080c, 0xa0e3, 0x00ce, 0xa867,
+ 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0xc12d, 0x080c, 0x6ae9,
+ 0x080c, 0x9a4e, 0x00fe, 0x0005, 0x00b6, 0x00e6, 0x00c6, 0x2011,
+ 0x0101, 0x2204, 0xc0c4, 0x2012, 0x2001, 0x180c, 0x2014, 0xc2e4,
+ 0x2202, 0x2071, 0x19bf, 0x7004, 0x9084, 0x0007, 0x0002, 0x8b21,
+ 0x8b25, 0x8b43, 0x8b6c, 0x8baa, 0x8b21, 0x8b3c, 0x8b1f, 0x080c,
+ 0x0dfa, 0x00ce, 0x00ee, 0x00be, 0x0005, 0x7024, 0x9065, 0x0148,
+ 0x7020, 0x8001, 0x7022, 0x600c, 0x9015, 0x0158, 0x7216, 0x600f,
+ 0x0000, 0x7007, 0x0000, 0x7027, 0x0000, 0x00ce, 0x00ee, 0x00be,
+ 0x0005, 0x7216, 0x7212, 0x0ca8, 0x7007, 0x0000, 0x7027, 0x0000,
+ 0x7020, 0x9005, 0x0070, 0x6010, 0x2058, 0x080c, 0x63d2, 0xb800,
+ 0xc0dc, 0xb802, 0x7007, 0x0000, 0x7027, 0x0000, 0x7020, 0x8001,
+ 0x7022, 0x1148, 0x2001, 0x180c, 0x2014, 0xd2ec, 0x1180, 0x00ce,
+ 0x00ee, 0x00be, 0x0005, 0xb854, 0x9015, 0x0120, 0x721e, 0x080c,
+ 0x8c10, 0x0ca8, 0x7218, 0x721e, 0x080c, 0x8c10, 0x0c80, 0xc2ec,
+ 0x2202, 0x080c, 0x8ced, 0x0c58, 0x7024, 0x9065, 0x05b8, 0x700c,
+ 0x9c06, 0x1160, 0x080c, 0x9a4e, 0x600c, 0x9015, 0x0120, 0x720e,
+ 0x600f, 0x0000, 0x0448, 0x720e, 0x720a, 0x0430, 0x7014, 0x9c06,
+ 0x1160, 0x080c, 0x9a4e, 0x600c, 0x9015, 0x0120, 0x7216, 0x600f,
+ 0x0000, 0x00d0, 0x7216, 0x7212, 0x00b8, 0x6020, 0x9086, 0x0003,
+ 0x1198, 0x6010, 0x2058, 0x080c, 0x63d2, 0xb800, 0xc0dc, 0xb802,
+ 0x080c, 0x9a4e, 0x701c, 0x9065, 0x0138, 0xb854, 0x9015, 0x0110,
+ 0x721e, 0x0010, 0x7218, 0x721e, 0x7027, 0x0000, 0x00ce, 0x00ee,
+ 0x00be, 0x0005, 0x7024, 0x9065, 0x0140, 0x080c, 0x9a4e, 0x600c,
+ 0x9015, 0x0158, 0x720e, 0x600f, 0x0000, 0x080c, 0x9b78, 0x7027,
+ 0x0000, 0x00ce, 0x00ee, 0x00be, 0x0005, 0x720e, 0x720a, 0x0ca8,
+ 0x00d6, 0x2069, 0x19bf, 0x6830, 0x9084, 0x0003, 0x0002, 0x8bcd,
+ 0x8bcf, 0x8bf3, 0x8bcb, 0x080c, 0x0dfa, 0x00de, 0x0005, 0x00c6,
+ 0x6840, 0x9086, 0x0001, 0x01b8, 0x683c, 0x9065, 0x0130, 0x600c,
+ 0x9015, 0x0170, 0x6a3a, 0x600f, 0x0000, 0x6833, 0x0000, 0x683f,
+ 0x0000, 0x2011, 0x19de, 0x2013, 0x0000, 0x00ce, 0x00de, 0x0005,
+ 0x683a, 0x6836, 0x0c90, 0x6843, 0x0000, 0x6838, 0x9065, 0x0d68,
+ 0x6003, 0x0003, 0x0c50, 0x00c6, 0x9006, 0x6842, 0x6846, 0x684a,
+ 0x683c, 0x9065, 0x0160, 0x600c, 0x9015, 0x0130, 0x6a3a, 0x600f,
+ 0x0000, 0x683f, 0x0000, 0x0018, 0x683e, 0x683a, 0x6836, 0x00ce,
+ 0x00de, 0x0005, 0x2001, 0x180c, 0x200c, 0xc1e5, 0x2102, 0x0005,
+ 0x2001, 0x180c, 0x200c, 0xd1ec, 0x0120, 0xc1ec, 0x2102, 0x080c,
+ 0x8ced, 0x2001, 0x19cb, 0x2004, 0x9086, 0x0001, 0x0d58, 0x00d6,
+ 0x2069, 0x19bf, 0x6804, 0x9084, 0x0007, 0x0002, 0x8c30, 0x8cd5,
+ 0x8cd5, 0x8cd5, 0x8cd5, 0x8cd7, 0x8cd5, 0x8c2e, 0x080c, 0x0dfa,
+ 0x6820, 0x9005, 0x1110, 0x00de, 0x0005, 0x00c6, 0x680c, 0x9065,
+ 0x0150, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000, 0x080c, 0x8d44,
+ 0x00ce, 0x00de, 0x0005, 0x6814, 0x9065, 0x0150, 0x6807, 0x0001,
+ 0x6826, 0x682b, 0x0000, 0x080c, 0x8d44, 0x00ce, 0x00de, 0x0005,
+ 0x00b6, 0x00e6, 0x6a1c, 0x92dd, 0x0000, 0x0904, 0x8cbf, 0xb84c,
+ 0x900d, 0x0118, 0xb888, 0x9005, 0x01a0, 0xb854, 0x905d, 0x0120,
+ 0x920e, 0x0904, 0x8cbf, 0x0028, 0x6818, 0x920e, 0x0904, 0x8cbf,
+ 0x2058, 0xb84c, 0x900d, 0x0d88, 0xb888, 0x9005, 0x1d70, 0x2b00,
+ 0x681e, 0xbb3c, 0xb838, 0x9302, 0x1e40, 0x080c, 0xa0ba, 0x0904,
+ 0x8cbf, 0x8318, 0xbb3e, 0x6116, 0x2b10, 0x6212, 0x0096, 0x2148,
+ 0xa880, 0x9084, 0x00ff, 0x605e, 0xa883, 0x0000, 0xa884, 0x009e,
+ 0x908a, 0x199a, 0x0210, 0x2001, 0x1999, 0x8003, 0x801b, 0x831b,
+ 0x9318, 0x631a, 0x6114, 0x0096, 0x2148, 0xa964, 0x009e, 0x918c,
+ 0x00ff, 0x918e, 0x0048, 0x0538, 0x00f6, 0x2c78, 0x2061, 0x0100,
+ 0xbab0, 0x629a, 0x2069, 0x0200, 0x2071, 0x0240, 0x080c, 0x9286,
+ 0x2069, 0x19bf, 0xbb00, 0xc3dd, 0xbb02, 0x6807, 0x0002, 0x2f18,
+ 0x6b26, 0x682b, 0x0000, 0x7823, 0x0003, 0x7803, 0x0001, 0x7807,
+ 0x0040, 0x00fe, 0x00ee, 0x00be, 0x00ce, 0x00de, 0x0005, 0x00ee,
+ 0x00be, 0x00ce, 0x0cd0, 0x6807, 0x0006, 0x2c18, 0x6b26, 0x6820,
+ 0x8001, 0x6822, 0x682b, 0x0000, 0x080c, 0x63d2, 0x080c, 0x9f0f,
+ 0x00ee, 0x00be, 0x00ce, 0x00de, 0x0005, 0x00de, 0x0005, 0x00c6,
+ 0x680c, 0x9065, 0x0138, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000,
+ 0x080c, 0x8d44, 0x00ce, 0x00de, 0x0005, 0x2001, 0x180c, 0x2014,
+ 0xc2ed, 0x2202, 0x00de, 0x00fe, 0x0005, 0x00f6, 0x00d6, 0x2069,
+ 0x19bf, 0x6830, 0x9086, 0x0000, 0x1548, 0x2001, 0x180c, 0x2014,
+ 0xd2e4, 0x0130, 0xc2e4, 0x2202, 0x080c, 0x8c1f, 0x2069, 0x19bf,
+ 0x2001, 0x180c, 0x200c, 0xd1c4, 0x11e0, 0x6838, 0x907d, 0x01b0,
+ 0x6a04, 0x9296, 0x0000, 0x1568, 0x6833, 0x0001, 0x683e, 0x6847,
+ 0x0000, 0x684b, 0x0000, 0x0126, 0x00f6, 0x2091, 0x2400, 0x002e,
+ 0x080c, 0x1b8a, 0x1158, 0x012e, 0x080c, 0x94b3, 0x00de, 0x00fe,
+ 0x0005, 0xc1c4, 0x2102, 0x080c, 0x72d2, 0x08f8, 0x012e, 0x6843,
+ 0x0000, 0x7803, 0x0002, 0x780c, 0x9015, 0x0140, 0x6a3a, 0x780f,
+ 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x0c40, 0x683a, 0x6836,
+ 0x0cc0, 0x6a04, 0x9296, 0x0006, 0x1904, 0x8ce5, 0x6a30, 0x9296,
+ 0x0000, 0x0950, 0x0804, 0x8ce5, 0x6020, 0x9084, 0x000f, 0x000b,
+ 0x0005, 0x8d58, 0x8d5d, 0x91b6, 0x924f, 0x8d5d, 0x91b6, 0x924f,
+ 0x8d58, 0x8d5d, 0x8d58, 0x8d58, 0x8d58, 0x8d58, 0x8d58, 0x8d58,
+ 0x080c, 0x8b04, 0x080c, 0x8c10, 0x0005, 0x00b6, 0x0156, 0x0136,
+ 0x0146, 0x01c6, 0x01d6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2069,
+ 0x0200, 0x2071, 0x0240, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0dfa,
+ 0x6110, 0x2158, 0xb9b0, 0x2c78, 0x2061, 0x0100, 0x619a, 0x908a,
+ 0x0040, 0x1a04, 0x8dc9, 0x005b, 0x00fe, 0x00ee, 0x00de, 0x00ce,
+ 0x01de, 0x01ce, 0x014e, 0x013e, 0x015e, 0x00be, 0x0005, 0x8f40,
+ 0x8f7b, 0x8fa4, 0x9047, 0x9068, 0x906e, 0x907b, 0x9083, 0x908f,
+ 0x9095, 0x90a6, 0x9095, 0x90fd, 0x9083, 0x9109, 0x910f, 0x908f,
+ 0x910f, 0x911b, 0x8dc7, 0x8dc7, 0x8dc7, 0x8dc7, 0x8dc7, 0x8dc7,
+ 0x8dc7, 0x8dc7, 0x8dc7, 0x8dc7, 0x8dc7, 0x9854, 0x9877, 0x9888,
+ 0x98a8, 0x98da, 0x907b, 0x8dc7, 0x907b, 0x9095, 0x8dc7, 0x8fa4,
+ 0x9047, 0x8dc7, 0x9c6f, 0x9095, 0x8dc7, 0x9c8b, 0x9095, 0x8dc7,
+ 0x908f, 0x8f3a, 0x8dea, 0x8dc7, 0x9ca7, 0x9d14, 0x9def, 0x8dc7,
+ 0x9dfc, 0x9078, 0x9e27, 0x8dc7, 0x98e4, 0x9e54, 0x8dc7, 0x080c,
+ 0x0dfa, 0x2100, 0x005b, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x01de,
+ 0x01ce, 0x014e, 0x013e, 0x015e, 0x00be, 0x0005, 0x8de8, 0x8de8,
+ 0x8de8, 0x8e11, 0x8ebd, 0x8ec8, 0x8de8, 0x8de8, 0x8de8, 0x8f0f,
+ 0x8f1b, 0x8e2c, 0x8de8, 0x8e47, 0x8e7b, 0x9fd6, 0xa01b, 0x9095,
+ 0x080c, 0x0dfa, 0x00d6, 0x0096, 0x080c, 0x912e, 0x7003, 0x2414,
+ 0x7007, 0x0018, 0x700b, 0x0800, 0x7814, 0x2048, 0xa83c, 0x700e,
+ 0xa850, 0x7022, 0xa854, 0x7026, 0x60c3, 0x0018, 0x080c, 0x962a,
+ 0x009e, 0x00de, 0x0005, 0x7810, 0x00b6, 0x2058, 0xb8a0, 0x00be,
+ 0x080c, 0xa062, 0x1118, 0x9084, 0xff80, 0x0110, 0x9085, 0x0001,
+ 0x0005, 0x00d6, 0x0096, 0x080c, 0x912e, 0x7003, 0x0500, 0x7814,
+ 0x2048, 0xa874, 0x700a, 0xa878, 0x700e, 0xa87c, 0x7012, 0xa880,
+ 0x7016, 0xa884, 0x701a, 0xa888, 0x701e, 0x60c3, 0x0010, 0x080c,
+ 0x962a, 0x009e, 0x00de, 0x0005, 0x00d6, 0x0096, 0x080c, 0x912e,
+ 0x7003, 0x0500, 0x7814, 0x2048, 0xa8cc, 0x700a, 0xa8d0, 0x700e,
+ 0xa8d4, 0x7012, 0xa8d8, 0x7016, 0xa8dc, 0x701a, 0xa8e0, 0x701e,
+ 0x60c3, 0x0010, 0x080c, 0x962a, 0x009e, 0x00de, 0x0005, 0x00d6,
+ 0x0096, 0x0126, 0x2091, 0x8000, 0x080c, 0x912e, 0x20e9, 0x0000,
+ 0x2001, 0x197b, 0x2003, 0x0000, 0x7814, 0x2048, 0xa814, 0x8003,
+ 0x60c2, 0xa830, 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x001b,
+ 0x2098, 0x2001, 0x197b, 0x0016, 0x200c, 0x2001, 0x0001, 0x080c,
+ 0x22ef, 0x080c, 0xcb69, 0x9006, 0x080c, 0x22ef, 0x001e, 0xa804,
+ 0x9005, 0x0110, 0x2048, 0x0c28, 0x04d9, 0x080c, 0x962a, 0x012e,
+ 0x009e, 0x00de, 0x0005, 0x00d6, 0x0096, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x9179, 0x20e9, 0x0000, 0x2001, 0x197b, 0x2003, 0x0000,
+ 0x7814, 0x2048, 0xa86f, 0x0200, 0xa873, 0x0000, 0xa814, 0x8003,
+ 0x60c2, 0xa830, 0x20a8, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x001b,
+ 0x2098, 0x2001, 0x197b, 0x0016, 0x200c, 0x080c, 0xcb69, 0x001e,
+ 0xa804, 0x9005, 0x0110, 0x2048, 0x0c60, 0x0051, 0x7814, 0x2048,
+ 0x080c, 0x0fe3, 0x080c, 0x962a, 0x012e, 0x009e, 0x00de, 0x0005,
+ 0x60c0, 0x8004, 0x9084, 0x0003, 0x9005, 0x0130, 0x9082, 0x0004,
+ 0x20a3, 0x0000, 0x8000, 0x1de0, 0x0005, 0x080c, 0x912e, 0x7003,
+ 0x7800, 0x7808, 0x8007, 0x700a, 0x60c3, 0x0008, 0x0804, 0x962a,
+ 0x00d6, 0x00e6, 0x080c, 0x9179, 0x7814, 0x9084, 0xff00, 0x2073,
+ 0x0200, 0x8e70, 0x8e70, 0x9095, 0x0010, 0x2272, 0x8e70, 0x2073,
+ 0x0034, 0x8e70, 0x2069, 0x1805, 0x20a9, 0x0004, 0x2d76, 0x8d68,
+ 0x8e70, 0x1f04, 0x8ede, 0x2069, 0x1801, 0x20a9, 0x0004, 0x2d76,
+ 0x8d68, 0x8e70, 0x1f04, 0x8ee7, 0x2069, 0x198b, 0x9086, 0xdf00,
+ 0x0110, 0x2069, 0x19a5, 0x20a9, 0x001a, 0x9e86, 0x0260, 0x1148,
+ 0x00c6, 0x2061, 0x0200, 0x6010, 0x8000, 0x6012, 0x00ce, 0x2071,
+ 0x0240, 0x2d04, 0x8007, 0x2072, 0x8d68, 0x8e70, 0x1f04, 0x8ef5,
+ 0x60c3, 0x004c, 0x080c, 0x962a, 0x00ee, 0x00de, 0x0005, 0x080c,
+ 0x912e, 0x7003, 0x6300, 0x7007, 0x0028, 0x7808, 0x700e, 0x60c3,
+ 0x0008, 0x0804, 0x962a, 0x00d6, 0x0026, 0x0016, 0x080c, 0x9179,
+ 0x7003, 0x0200, 0x7814, 0x700e, 0x00e6, 0x9ef0, 0x0004, 0x2009,
+ 0x0001, 0x2011, 0x000c, 0x2073, 0x0800, 0x8e70, 0x2073, 0x0000,
+ 0x00ee, 0x7206, 0x710a, 0x62c2, 0x080c, 0x962a, 0x001e, 0x002e,
+ 0x00de, 0x0005, 0x2001, 0x1817, 0x2004, 0x609a, 0x0804, 0x962a,
+ 0x080c, 0x912e, 0x7003, 0x5200, 0x2069, 0x185b, 0x6804, 0xd084,
+ 0x0130, 0x6828, 0x0016, 0x080c, 0x27a1, 0x710e, 0x001e, 0x20a9,
+ 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805, 0x20e9, 0x0000, 0x20a1,
+ 0x0250, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1801, 0x20a1, 0x0254,
+ 0x4003, 0x080c, 0xa062, 0x1120, 0xb8a0, 0x9082, 0x007f, 0x0248,
+ 0x2001, 0x181e, 0x2004, 0x7032, 0x2001, 0x181f, 0x2004, 0x7036,
+ 0x0030, 0x2001, 0x1817, 0x2004, 0x9084, 0x00ff, 0x7036, 0x60c3,
+ 0x001c, 0x0804, 0x962a, 0x080c, 0x912e, 0x7003, 0x0500, 0x080c,
+ 0xa062, 0x1120, 0xb8a0, 0x9082, 0x007f, 0x0248, 0x2001, 0x181e,
+ 0x2004, 0x700a, 0x2001, 0x181f, 0x2004, 0x700e, 0x0030, 0x2001,
+ 0x1817, 0x2004, 0x9084, 0x00ff, 0x700e, 0x20a9, 0x0004, 0x20e1,
+ 0x0001, 0x2099, 0x1805, 0x20e9, 0x0000, 0x20a1, 0x0250, 0x4003,
+ 0x60c3, 0x0010, 0x0804, 0x962a, 0x080c, 0x912e, 0x9006, 0x080c,
+ 0x678d, 0xb8a0, 0x9086, 0x007e, 0x1130, 0x7003, 0x0400, 0x620c,
+ 0xc2b4, 0x620e, 0x0058, 0x7814, 0x0096, 0x904d, 0x0120, 0x9006,
+ 0xa89a, 0xa8a6, 0xa8aa, 0x009e, 0x7003, 0x0300, 0xb8a0, 0x9086,
+ 0x007e, 0x1904, 0x900f, 0x00d6, 0x2069, 0x1944, 0x2001, 0x1836,
+ 0x2004, 0xd0a4, 0x0178, 0x6800, 0x700a, 0x6808, 0x9084, 0x2000,
+ 0x7012, 0x680c, 0x7016, 0x701f, 0x2710, 0x6818, 0x7022, 0x681c,
+ 0x7026, 0x0080, 0x6800, 0x700a, 0x6804, 0x700e, 0x6808, 0x080c,
+ 0x7207, 0x1118, 0x9084, 0x37ff, 0x0010, 0x9084, 0x3fff, 0x7012,
+ 0x680c, 0x7016, 0x00de, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099,
+ 0x1805, 0x20e9, 0x0000, 0x20a1, 0x0256, 0x4003, 0x20a9, 0x0004,
+ 0x2099, 0x1801, 0x20a1, 0x025a, 0x4003, 0x00d6, 0x080c, 0x9ed6,
+ 0x2069, 0x194c, 0x2071, 0x024e, 0x6800, 0xc0dd, 0x7002, 0x080c,
+ 0x55df, 0xd0e4, 0x0110, 0x680c, 0x700e, 0x00de, 0x04a0, 0x2001,
+ 0x1836, 0x2004, 0xd0a4, 0x0168, 0x0016, 0x2009, 0x0002, 0x60e0,
+ 0x9106, 0x0130, 0x2100, 0x60e3, 0x0000, 0x080c, 0x27e2, 0x61e2,
+ 0x001e, 0x20e1, 0x0001, 0x2099, 0x1944, 0x20e9, 0x0000, 0x20a1,
+ 0x024e, 0x20a9, 0x0008, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1805,
+ 0x20a1, 0x0256, 0x4003, 0x20a9, 0x0004, 0x2099, 0x1801, 0x20a1,
+ 0x025a, 0x4003, 0x080c, 0x9ed6, 0x20a1, 0x024e, 0x20a9, 0x0008,
+ 0x2099, 0x194c, 0x4003, 0x60c3, 0x0074, 0x0804, 0x962a, 0x080c,
+ 0x912e, 0x7003, 0x2010, 0x7007, 0x0014, 0x700b, 0x0800, 0x700f,
+ 0x2000, 0x9006, 0x00f6, 0x2079, 0x185b, 0x7904, 0x00fe, 0xd1ac,
+ 0x1110, 0x9085, 0x0020, 0x0010, 0x9085, 0x0010, 0x9085, 0x0002,
+ 0x00d6, 0x0804, 0x90de, 0x7026, 0x60c3, 0x0014, 0x0804, 0x962a,
+ 0x080c, 0x912e, 0x7003, 0x5000, 0x0804, 0x8fbe, 0x080c, 0x912e,
+ 0x7003, 0x2110, 0x7007, 0x0014, 0x60c3, 0x0014, 0x0804, 0x962a,
+ 0x080c, 0x9170, 0x0010, 0x080c, 0x9179, 0x7003, 0x0200, 0x60c3,
+ 0x0004, 0x0804, 0x962a, 0x080c, 0x9179, 0x7003, 0x0100, 0x700b,
+ 0x0003, 0x700f, 0x2a00, 0x60c3, 0x0008, 0x0804, 0x962a, 0x080c,
+ 0x9179, 0x7003, 0x0200, 0x0804, 0x8fbe, 0x080c, 0x9179, 0x7003,
+ 0x0100, 0x782c, 0x9005, 0x0110, 0x700a, 0x0010, 0x700b, 0x0003,
+ 0x7814, 0x700e, 0x60c3, 0x0008, 0x0804, 0x962a, 0x00d6, 0x080c,
+ 0x9179, 0x7003, 0x0210, 0x7007, 0x0014, 0x700b, 0x0800, 0xb894,
+ 0x9086, 0x0014, 0x1198, 0xb99c, 0x9184, 0x0030, 0x0190, 0xb998,
+ 0x9184, 0xc000, 0x1140, 0xd1ec, 0x0118, 0x700f, 0x2100, 0x0058,
+ 0x700f, 0x0100, 0x0040, 0x700f, 0x0400, 0x0028, 0x700f, 0x0700,
+ 0x0010, 0x700f, 0x0800, 0x00f6, 0x2079, 0x185b, 0x7904, 0x00fe,
+ 0xd1ac, 0x1110, 0x9085, 0x0020, 0x0010, 0x9085, 0x0010, 0x2009,
+ 0x187d, 0x210c, 0xd184, 0x1110, 0x9085, 0x0002, 0x0026, 0x2009,
+ 0x187b, 0x210c, 0xd1e4, 0x0150, 0xc0c5, 0xbabc, 0xd28c, 0x1108,
+ 0xc0cd, 0x9094, 0x0030, 0x9296, 0x0010, 0x0140, 0xd1ec, 0x0130,
+ 0x9094, 0x0030, 0x9296, 0x0010, 0x0108, 0xc0bd, 0x002e, 0x7026,
+ 0x60c3, 0x0014, 0x00de, 0x0804, 0x962a, 0x080c, 0x9179, 0x7003,
+ 0x0210, 0x7007, 0x0014, 0x700f, 0x0100, 0x60c3, 0x0014, 0x0804,
+ 0x962a, 0x080c, 0x9179, 0x7003, 0x0200, 0x0804, 0x8f44, 0x080c,
+ 0x9179, 0x7003, 0x0100, 0x700b, 0x0003, 0x700f, 0x2a00, 0x60c3,
+ 0x0008, 0x0804, 0x962a, 0x080c, 0x9179, 0x7003, 0x0100, 0x700b,
+ 0x000b, 0x60c3, 0x0008, 0x0804, 0x962a, 0x0026, 0x00d6, 0x0036,
+ 0x0046, 0x2019, 0x3200, 0x2021, 0x0800, 0x0040, 0x0026, 0x00d6,
+ 0x0036, 0x0046, 0x2019, 0x2200, 0x2021, 0x0100, 0x080c, 0x9eeb,
+ 0xb810, 0x9305, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x6878,
+ 0x700a, 0x687c, 0x700e, 0x9485, 0x0029, 0x7012, 0x004e, 0x003e,
+ 0x00de, 0x080c, 0x9618, 0x721a, 0x9f95, 0x0000, 0x7222, 0x7027,
+ 0xffff, 0x2071, 0x024c, 0x002e, 0x0005, 0x0026, 0x080c, 0x9eeb,
+ 0x7003, 0x02ff, 0x7007, 0xfffc, 0x00d6, 0x2069, 0x1800, 0x6878,
+ 0x700a, 0x687c, 0x700e, 0x00de, 0x7013, 0x2029, 0x0c10, 0x7003,
+ 0x0100, 0x7007, 0x0000, 0x700b, 0xfc02, 0x700f, 0x0000, 0x0005,
+ 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019, 0x3300, 0x2021, 0x0800,
+ 0x0040, 0x0026, 0x00d6, 0x0036, 0x0046, 0x2019, 0x2300, 0x2021,
+ 0x0100, 0x080c, 0x9eeb, 0xb810, 0x9305, 0x7002, 0xb814, 0x7006,
+ 0x2069, 0x1800, 0xb810, 0x9005, 0x1140, 0xb814, 0x9005, 0x1128,
+ 0x700b, 0x00ff, 0x700f, 0xfffe, 0x0020, 0x6878, 0x700a, 0x687c,
+ 0x700e, 0x0000, 0x9485, 0x0098, 0x7012, 0x004e, 0x003e, 0x00de,
+ 0x080c, 0x9618, 0x721a, 0x7a08, 0x7222, 0x2f10, 0x7226, 0x2071,
+ 0x024c, 0x002e, 0x0005, 0x080c, 0x9618, 0x721a, 0x7a08, 0x7222,
+ 0x7814, 0x7026, 0x2071, 0x024c, 0x002e, 0x0005, 0x00b6, 0x00c6,
+ 0x00d6, 0x00e6, 0x00f6, 0x2069, 0x0200, 0x2071, 0x0240, 0x6004,
+ 0x908a, 0x0085, 0x0a0c, 0x0dfa, 0x908a, 0x0092, 0x1a0c, 0x0dfa,
+ 0x6110, 0x2158, 0xb9b0, 0x2c78, 0x2061, 0x0100, 0x619a, 0x9082,
+ 0x0085, 0x0033, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be, 0x0005,
+ 0x91e7, 0x91f6, 0x9201, 0x91e5, 0x91e5, 0x91e5, 0x91e7, 0x91e5,
+ 0x91e5, 0x91e5, 0x91e5, 0x91e5, 0x91e5, 0x080c, 0x0dfa, 0x0411,
+ 0x60c3, 0x0000, 0x0026, 0x080c, 0x2afe, 0x0228, 0x2011, 0x0101,
+ 0x2204, 0xc0c5, 0x2012, 0x002e, 0x0804, 0x962a, 0x0431, 0x7808,
+ 0x700a, 0x7814, 0x700e, 0x7017, 0xffff, 0x60c3, 0x000c, 0x0804,
+ 0x962a, 0x04a1, 0x7003, 0x0003, 0x7007, 0x0300, 0x60c3, 0x0004,
+ 0x0804, 0x962a, 0x0026, 0x080c, 0x9eeb, 0xb810, 0x9085, 0x8100,
+ 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x6878, 0x700a, 0x687c,
+ 0x700e, 0x7013, 0x0009, 0x0804, 0x9149, 0x0026, 0x080c, 0x9eeb,
+ 0xb810, 0x9085, 0x8400, 0x7002, 0xb814, 0x7006, 0x2069, 0x1800,
+ 0x6878, 0x700a, 0x687c, 0x700e, 0x2001, 0x0099, 0x7a20, 0x9296,
+ 0x0005, 0x0108, 0xc0bc, 0x7012, 0x0804, 0x91ab, 0x0026, 0x080c,
+ 0x9eeb, 0xb810, 0x9085, 0x8500, 0x7002, 0xb814, 0x7006, 0x2069,
+ 0x1800, 0x6878, 0x700a, 0x687c, 0x700e, 0x2001, 0x0099, 0x7a20,
+ 0x9296, 0x0005, 0x0108, 0xc0bc, 0x7012, 0x0804, 0x91ab, 0x00b6,
+ 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2c78, 0x2069, 0x0200, 0x2071,
+ 0x0240, 0x7804, 0x908a, 0x0040, 0x0a0c, 0x0dfa, 0x908a, 0x0054,
+ 0x1a0c, 0x0dfa, 0x7910, 0x2158, 0xb9b0, 0x2061, 0x0100, 0x619a,
+ 0x9082, 0x0040, 0x0033, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x00be,
+ 0x0005, 0x9286, 0x9342, 0x9315, 0x9464, 0x9284, 0x9284, 0x9284,
+ 0x9284, 0x9284, 0x9284, 0x9284, 0x9a2b, 0x9a33, 0x9a3b, 0x9a43,
+ 0x9284, 0x9e33, 0x9284, 0x9a23, 0x080c, 0x0dfa, 0x0096, 0x780b,
+ 0xffff, 0x080c, 0x92f1, 0x7914, 0x2148, 0xa978, 0x7956, 0xae64,
+ 0x96b4, 0x00ff, 0x9686, 0x0008, 0x1148, 0xa8b4, 0x7032, 0xa8b8,
+ 0x7036, 0xa8bc, 0x703a, 0xa8c0, 0x703e, 0x0008, 0x7132, 0xa97c,
+ 0x9184, 0x000f, 0x1118, 0x2001, 0x0005, 0x0040, 0xd184, 0x0118,
+ 0x2001, 0x0004, 0x0018, 0x9084, 0x0006, 0x8004, 0x2010, 0x785c,
+ 0x9084, 0x00ff, 0x8007, 0x9205, 0x7042, 0xd1ac, 0x0158, 0x7047,
+ 0x0002, 0x9686, 0x0008, 0x1118, 0x080c, 0x181c, 0x0010, 0x080c,
+ 0x16db, 0x0050, 0xd1b4, 0x0118, 0x7047, 0x0001, 0x0028, 0x7047,
+ 0x0000, 0x9016, 0x2230, 0x0010, 0xaab0, 0xaeac, 0x726a, 0x766e,
+ 0x20a9, 0x0008, 0x20e9, 0x0000, 0xa860, 0x20e0, 0xa85c, 0x9080,
+ 0x0023, 0x2098, 0x20a1, 0x0252, 0x2069, 0x0200, 0x6813, 0x0018,
+ 0x4003, 0x6813, 0x0008, 0x60c3, 0x0020, 0x6017, 0x0009, 0x2001,
+ 0x19db, 0x2003, 0x07d0, 0x2001, 0x19da, 0x2003, 0x0009, 0x009e,
+ 0x0005, 0x6813, 0x0008, 0xba8c, 0x8210, 0xb8bc, 0xd084, 0x0128,
+ 0x7a4a, 0x7b14, 0x7b46, 0x722e, 0x732a, 0x9294, 0x00ff, 0xba8e,
+ 0x8217, 0x721a, 0xba10, 0x9295, 0x0600, 0x7202, 0xba14, 0x7206,
+ 0x2069, 0x1800, 0x6a78, 0x720a, 0x6a7c, 0x720e, 0x7013, 0x0829,
+ 0x2f10, 0x7222, 0x7027, 0xffff, 0x0005, 0x00d6, 0x0096, 0x0081,
+ 0x7814, 0x2048, 0xa890, 0x7002, 0xa88c, 0x7006, 0xa8b0, 0x700a,
+ 0xa8ac, 0x700e, 0x60c3, 0x000c, 0x009e, 0x00de, 0x0804, 0x962a,
+ 0x6813, 0x0008, 0xb810, 0x9085, 0x0500, 0x7002, 0xb814, 0x7006,
+ 0x2069, 0x1800, 0x6878, 0x700a, 0x687c, 0x700e, 0x7013, 0x0889,
+ 0x080c, 0x9618, 0x721a, 0x7a08, 0x7222, 0x2f10, 0x7226, 0x2071,
+ 0x024c, 0x0005, 0x00d6, 0x0096, 0x080c, 0x9442, 0x7814, 0x2048,
+ 0x080c, 0xbe35, 0x1130, 0x7814, 0x9084, 0x0700, 0x8007, 0x0033,
+ 0x0010, 0x9006, 0x001b, 0x009e, 0x00de, 0x0005, 0x9360, 0x93c9,
+ 0x93d9, 0x93ff, 0x940b, 0x941c, 0x9424, 0x935e, 0x080c, 0x0dfa,
+ 0x0016, 0x0036, 0xa97c, 0x918c, 0x0003, 0x0118, 0x9186, 0x0003,
+ 0x1198, 0xaba8, 0x7824, 0xd0cc, 0x1168, 0x7316, 0xa898, 0x701a,
+ 0xa894, 0x701e, 0x003e, 0x001e, 0x2001, 0x1989, 0x2004, 0x60c2,
+ 0x0804, 0x962a, 0xc3e5, 0x0c88, 0x9186, 0x0001, 0x190c, 0x0dfa,
+ 0xaba8, 0x7824, 0xd0cc, 0x1904, 0x93c6, 0x7316, 0xa898, 0x701a,
+ 0xa894, 0x701e, 0xa8a4, 0x7026, 0xa8ac, 0x702e, 0x2009, 0x0018,
+ 0x9384, 0x0300, 0x0570, 0xd3c4, 0x0110, 0xa8ac, 0x9108, 0xd3cc,
+ 0x0110, 0xa8a4, 0x9108, 0x6810, 0x9085, 0x0010, 0x6812, 0x2011,
+ 0x0258, 0x20e9, 0x0000, 0x22a0, 0x0156, 0x20a9, 0x0008, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x002c, 0x2098, 0x4003, 0x6810, 0x8000,
+ 0x6812, 0x2011, 0x0240, 0x22a0, 0x20a9, 0x0005, 0x4003, 0x6810,
+ 0xc084, 0x6812, 0x015e, 0x9184, 0x0003, 0x0118, 0x2019, 0x0245,
+ 0x201a, 0x61c2, 0x003e, 0x001e, 0x0804, 0x962a, 0xc3e5, 0x0804,
+ 0x9385, 0x2011, 0x0008, 0x2001, 0x180f, 0x2004, 0xd0a4, 0x0110,
+ 0x2011, 0x0028, 0x7824, 0xd0cc, 0x1110, 0x7216, 0x0470, 0x0ce8,
+ 0xc2e5, 0x2011, 0x0302, 0x0016, 0x782c, 0x701a, 0x7930, 0x711e,
+ 0x9105, 0x0108, 0xc2dd, 0x001e, 0x7824, 0xd0cc, 0x0108, 0xc2e5,
+ 0x7216, 0x7027, 0x0012, 0x702f, 0x0008, 0x7043, 0x7000, 0x7047,
+ 0x0500, 0x704f, 0x000a, 0x2069, 0x0200, 0x6813, 0x0009, 0x2071,
+ 0x0240, 0x700b, 0x2500, 0x60c3, 0x0032, 0x0804, 0x962a, 0x2011,
+ 0x0028, 0x7824, 0xd0cc, 0x1128, 0x7216, 0x60c3, 0x0018, 0x0804,
+ 0x962a, 0x0cd0, 0xc2e5, 0x2011, 0x0100, 0x7824, 0xd0cc, 0x0108,
+ 0xc2e5, 0x7216, 0x702f, 0x0008, 0x7858, 0x9084, 0x00ff, 0x7036,
+ 0x60c3, 0x0020, 0x0804, 0x962a, 0x2011, 0x0008, 0x7824, 0xd0cc,
+ 0x0108, 0xc2e5, 0x7216, 0x0c08, 0x0036, 0x7b14, 0x9384, 0xff00,
+ 0x7816, 0x9384, 0x00ff, 0x8001, 0x1138, 0x7824, 0xd0cc, 0x0108,
+ 0xc2e5, 0x7216, 0x003e, 0x0888, 0x0046, 0x2021, 0x0800, 0x0006,
+ 0x7824, 0xd0cc, 0x000e, 0x0108, 0xc4e5, 0x7416, 0x004e, 0x701e,
+ 0x003e, 0x0818, 0x00d6, 0x6813, 0x0008, 0xb810, 0x9085, 0x0700,
+ 0x7002, 0xb814, 0x7006, 0x2069, 0x1800, 0x6878, 0x700a, 0x687c,
+ 0x700e, 0x7824, 0xd0cc, 0x1168, 0x7013, 0x0898, 0x080c, 0x9618,
+ 0x721a, 0x7a08, 0x7222, 0x2f10, 0x7226, 0x2071, 0x024c, 0x00de,
+ 0x0005, 0x7013, 0x0889, 0x0c90, 0x0016, 0x7814, 0x9084, 0x0700,
+ 0x8007, 0x0013, 0x001e, 0x0005, 0x9474, 0x9474, 0x9476, 0x9474,
+ 0x9474, 0x9474, 0x9490, 0x9474, 0x080c, 0x0dfa, 0x7914, 0x918c,
+ 0x08ff, 0x918d, 0xf600, 0x7916, 0x2009, 0x0003, 0x00b9, 0x2069,
+ 0x185b, 0x6804, 0xd0bc, 0x0130, 0x682c, 0x9084, 0x00ff, 0x8007,
+ 0x7032, 0x0010, 0x7033, 0x3f00, 0x60c3, 0x0001, 0x0804, 0x962a,
+ 0x2009, 0x0003, 0x0019, 0x7033, 0x7f00, 0x0cb0, 0x0016, 0x080c,
+ 0x9eeb, 0x001e, 0xb810, 0x9085, 0x0100, 0x7002, 0xb814, 0x7006,
+ 0x2069, 0x1800, 0x6a78, 0x720a, 0x6a7c, 0x720e, 0x7013, 0x0888,
+ 0x918d, 0x0008, 0x7116, 0x080c, 0x9618, 0x721a, 0x7a08, 0x7222,
+ 0x2f10, 0x7226, 0x0005, 0x00b6, 0x0096, 0x00e6, 0x00d6, 0x00c6,
+ 0x0056, 0x0046, 0x0036, 0x2061, 0x0100, 0x2071, 0x1800, 0x7810,
+ 0x2058, 0xb8a0, 0x2028, 0xb910, 0xba14, 0x7378, 0x747c, 0x7820,
+ 0x90be, 0x0006, 0x0904, 0x9587, 0x90be, 0x000a, 0x1904, 0x9543,
+ 0xb8b0, 0x609e, 0x7814, 0x2048, 0xa87c, 0xd0fc, 0x0558, 0xaf90,
+ 0x9784, 0xff00, 0x9105, 0x6062, 0x873f, 0x9784, 0xff00, 0x0006,
+ 0x7814, 0x2048, 0xa878, 0xc0fc, 0x9005, 0x000e, 0x1160, 0xaf94,
+ 0x87ff, 0x0198, 0x2039, 0x0098, 0x9705, 0x6072, 0x7808, 0x6082,
+ 0x2f00, 0x6086, 0x0038, 0x9185, 0x2200, 0x6062, 0x6073, 0x0129,
+ 0x6077, 0x0000, 0xb8b0, 0x609e, 0x0050, 0x2039, 0x0029, 0x9705,
+ 0x6072, 0x0cc0, 0x9185, 0x0200, 0x6062, 0x6073, 0x2029, 0xa87c,
+ 0xd0fc, 0x0118, 0xaf94, 0x87ff, 0x1120, 0x2f00, 0x6082, 0x7808,
+ 0x6086, 0x6266, 0x636a, 0x646e, 0x6077, 0x0000, 0xb88c, 0x8000,
+ 0x9084, 0x00ff, 0xb88e, 0x8007, 0x607a, 0x607f, 0x0000, 0xa838,
+ 0x608a, 0xa834, 0x608e, 0xa848, 0x60c6, 0xa844, 0x60ca, 0xb86c,
+ 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0x080c, 0x9ed0, 0x2009,
+ 0x07d0, 0x60c4, 0x9084, 0xfff0, 0x9005, 0x0110, 0x2009, 0x1b58,
+ 0x080c, 0x835f, 0x003e, 0x004e, 0x005e, 0x00ce, 0x00de, 0x00ee,
+ 0x009e, 0x00be, 0x0005, 0x7804, 0x9086, 0x0040, 0x0904, 0x95c3,
+ 0x9185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x0809,
+ 0x6077, 0x0008, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xb88c, 0x8000,
+ 0x9084, 0x00ff, 0xb88e, 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00,
+ 0x6082, 0x7808, 0x6086, 0x7814, 0x2048, 0xa838, 0x608a, 0xa834,
+ 0x608e, 0xa848, 0x60c6, 0xa844, 0x60ca, 0xb86c, 0x60ce, 0xbab0,
+ 0x629e, 0x080c, 0x9ed0, 0x2009, 0x07d0, 0x60c4, 0x9084, 0xfff0,
+ 0x9005, 0x0110, 0x2009, 0x1b58, 0x080c, 0x835f, 0x003e, 0x004e,
+ 0x005e, 0x00ce, 0x00de, 0x00ee, 0x009e, 0x00be, 0x0005, 0x7814,
+ 0x2048, 0xa87c, 0x9084, 0x0003, 0x9086, 0x0002, 0x0904, 0x95df,
+ 0x9185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x0880,
+ 0x6077, 0x0008, 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007,
+ 0x607a, 0x7838, 0x607e, 0x2f00, 0x6086, 0x7808, 0x6082, 0xa890,
+ 0x608a, 0xa88c, 0x608e, 0xa8b0, 0x60c6, 0xa8ac, 0x60ca, 0xa8ac,
+ 0x7930, 0x9108, 0x7932, 0xa8b0, 0x792c, 0x9109, 0x792e, 0xb86c,
+ 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000, 0xbab0, 0x629e, 0x080c,
+ 0x9ead, 0x0804, 0x9573, 0xb8bc, 0xd084, 0x0148, 0xb88c, 0x7814,
+ 0x2048, 0xb88c, 0x784a, 0xa836, 0x2900, 0xa83a, 0xb046, 0x9185,
+ 0x0600, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073, 0x0829, 0x6077,
+ 0x0000, 0x60af, 0x9575, 0x60d7, 0x0000, 0x0804, 0x9556, 0x9185,
+ 0x0700, 0x6062, 0x6266, 0x636a, 0x646e, 0x7824, 0xd0cc, 0x7826,
+ 0x0118, 0x6073, 0x0889, 0x0010, 0x6073, 0x0898, 0x6077, 0x0000,
+ 0xb88c, 0x8000, 0x9084, 0x00ff, 0xb88e, 0x8007, 0x607a, 0x607f,
+ 0x0000, 0x2f00, 0x6086, 0x7808, 0x6082, 0xa838, 0x608a, 0xa834,
+ 0x608e, 0xa848, 0x60c6, 0xa844, 0x60ca, 0xb86c, 0x60ce, 0x60af,
+ 0x95d5, 0x60d7, 0x0000, 0xbab0, 0x629e, 0x7824, 0xd0cc, 0x0120,
+ 0x080c, 0x9ed0, 0x0804, 0x9573, 0x080c, 0x9ead, 0x0804, 0x9573,
+ 0x7a10, 0x00b6, 0x2258, 0xba8c, 0x8210, 0x9294, 0x00ff, 0xba8e,
+ 0x00be, 0x8217, 0x0005, 0x00d6, 0x2069, 0x19bf, 0x6843, 0x0001,
+ 0x00de, 0x0005, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x00f1, 0x080c,
+ 0x8351, 0x0005, 0x0016, 0x2001, 0x180c, 0x200c, 0x9184, 0x0600,
+ 0x9086, 0x0600, 0x0128, 0x0089, 0x080c, 0x8351, 0x001e, 0x0005,
+ 0xc1e5, 0x2001, 0x180c, 0x2102, 0x2001, 0x19c0, 0x2003, 0x0000,
+ 0x2001, 0x19c8, 0x2003, 0x0000, 0x0c88, 0x0006, 0x6014, 0x9084,
+ 0x1804, 0x9085, 0x0009, 0x6016, 0x000e, 0x0005, 0x0016, 0x00c6,
+ 0x0006, 0x2061, 0x0100, 0x61a4, 0x60a7, 0x95f5, 0x6014, 0x9084,
+ 0x1804, 0x9085, 0x0008, 0x6016, 0x000e, 0xa001, 0xa001, 0xa001,
+ 0x61a6, 0x00ce, 0x001e, 0x0005, 0x00c6, 0x00d6, 0x0016, 0x0026,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x080c, 0x7207, 0x11c0, 0x2001,
+ 0x19db, 0x2004, 0x9005, 0x15d0, 0x080c, 0x72d2, 0x1160, 0x2061,
+ 0x0100, 0x6020, 0xd0b4, 0x1120, 0x6024, 0xd084, 0x090c, 0x0dfa,
+ 0x080c, 0x8351, 0x0458, 0x00c6, 0x2061, 0x19bf, 0x00c8, 0x6904,
+ 0x9194, 0x4000, 0x0540, 0x0811, 0x080c, 0x2c98, 0x00c6, 0x2061,
+ 0x19bf, 0x6128, 0x9192, 0x0008, 0x1258, 0x8108, 0x612a, 0x6124,
+ 0x00ce, 0x81ff, 0x0198, 0x080c, 0x8351, 0x080c, 0x964d, 0x0070,
+ 0x6124, 0x91e5, 0x0000, 0x0140, 0x080c, 0xdc28, 0x080c, 0x835a,
+ 0x2009, 0x0014, 0x080c, 0xa15d, 0x00ce, 0x0000, 0x002e, 0x001e,
+ 0x00de, 0x00ce, 0x0005, 0x2001, 0x19db, 0x2004, 0x9005, 0x1db0,
+ 0x00c6, 0x2061, 0x19bf, 0x6128, 0x9192, 0x0003, 0x1e08, 0x8108,
+ 0x612a, 0x00ce, 0x080c, 0x8351, 0x080c, 0x5dea, 0x2009, 0x185a,
+ 0x2114, 0x8210, 0x220a, 0x0c10, 0x0096, 0x00c6, 0x00d6, 0x00e6,
+ 0x0016, 0x0026, 0x080c, 0x8367, 0x2071, 0x19bf, 0x713c, 0x81ff,
+ 0x0904, 0x974a, 0x2061, 0x0100, 0x2069, 0x0140, 0x080c, 0x7207,
+ 0x1190, 0x0036, 0x2019, 0x0002, 0x080c, 0x999d, 0x003e, 0x713c,
+ 0x2160, 0x080c, 0xdc28, 0x2009, 0x004a, 0x080c, 0xa15d, 0x080c,
+ 0x72d2, 0x0804, 0x974a, 0x080c, 0x9756, 0x0904, 0x974a, 0x6904,
+ 0xd1f4, 0x0904, 0x9751, 0x080c, 0x2c98, 0x00c6, 0x703c, 0x9065,
+ 0x090c, 0x0dfa, 0x6020, 0x00ce, 0x9086, 0x0006, 0x1568, 0x61c8,
+ 0x60c4, 0x9105, 0x1548, 0x2009, 0x180c, 0x2104, 0xd0d4, 0x0520,
+ 0x6214, 0x9294, 0x1800, 0x1128, 0x6224, 0x9294, 0x0002, 0x1550,
+ 0x0070, 0xc0d4, 0x200a, 0x0006, 0x2001, 0x0100, 0x2004, 0x9086,
+ 0x000a, 0x000e, 0x0120, 0xd0cc, 0x0110, 0x080c, 0x2bca, 0x6014,
+ 0x9084, 0xe7fd, 0x9085, 0x0010, 0x6016, 0x703c, 0x2060, 0x2009,
+ 0x0049, 0x080c, 0xa15d, 0x0070, 0x0036, 0x2019, 0x0001, 0x080c,
+ 0x999d, 0x003e, 0x713c, 0x2160, 0x080c, 0xdc28, 0x2009, 0x004a,
+ 0x080c, 0xa15d, 0x002e, 0x001e, 0x00ee, 0x00de, 0x00ce, 0x009e,
+ 0x0005, 0xd1ec, 0x1904, 0x9703, 0x0804, 0x9705, 0x00d6, 0x00c6,
+ 0x0096, 0x703c, 0x9065, 0x090c, 0x0dfa, 0x2001, 0x0306, 0x200c,
+ 0x9184, 0x0030, 0x0904, 0x97ff, 0x9184, 0x0048, 0x9086, 0x0008,
+ 0x1904, 0x97ff, 0x2009, 0x0206, 0x2104, 0x2009, 0x0203, 0x210c,
+ 0x9106, 0x1904, 0x97ff, 0x2009, 0x022a, 0x2104, 0x2009, 0x022f,
+ 0x210c, 0x9116, 0x9084, 0x03ff, 0x918c, 0x03ff, 0x9294, 0x0400,
+ 0x0110, 0x9102, 0x0030, 0x2010, 0x2100, 0x9202, 0x2009, 0x0228,
+ 0x9102, 0x9082, 0x0005, 0x0250, 0x2008, 0x2001, 0x013b, 0x2004,
+ 0x8004, 0x8004, 0x8004, 0x9102, 0x1a04, 0x97ff, 0x2009, 0x1a58,
+ 0x2104, 0x8000, 0x0208, 0x200a, 0x2069, 0x0100, 0x6914, 0x918c,
+ 0x0184, 0x918d, 0x0010, 0x6916, 0x69c8, 0x2011, 0x0020, 0x68c8,
+ 0x9106, 0x1570, 0x8211, 0x1dd8, 0x2001, 0x0306, 0x2003, 0x4800,
+ 0x2001, 0x009a, 0x2003, 0x0004, 0x2001, 0x1a3d, 0x2003, 0x0000,
+ 0x2001, 0x1a46, 0x2003, 0x0000, 0x6a88, 0x698c, 0x2200, 0x9105,
+ 0x1120, 0x2c10, 0x080c, 0x1afe, 0x0040, 0x6014, 0x2048, 0xaa3a,
+ 0xa936, 0x6ac4, 0x69c8, 0xa946, 0xaa4a, 0x0126, 0x00c6, 0x2091,
+ 0x2400, 0x002e, 0x080c, 0x1b8a, 0x190c, 0x0dfa, 0x012e, 0x0090,
+ 0x2009, 0x1a59, 0x2104, 0x8000, 0x0208, 0x200a, 0x69c8, 0x2011,
+ 0x0020, 0x8211, 0x1df0, 0x68c8, 0x9106, 0x1dc0, 0x69c4, 0x68c8,
+ 0x9105, 0x0160, 0x6824, 0xd08c, 0x0110, 0x6827, 0x0002, 0x7048,
+ 0xc085, 0x704a, 0x0079, 0x7048, 0xc084, 0x704a, 0x2009, 0x07d0,
+ 0x080c, 0x835f, 0x9006, 0x009e, 0x00ce, 0x00de, 0x0005, 0x9085,
+ 0x0001, 0x0cc8, 0x0026, 0x00e6, 0x2071, 0x19bf, 0x7048, 0xd084,
+ 0x01c0, 0x713c, 0x81ff, 0x01a8, 0x2071, 0x0100, 0x9188, 0x0008,
+ 0x2114, 0x928e, 0x0006, 0x1138, 0x7014, 0x9084, 0x1984, 0x9085,
+ 0x0012, 0x7016, 0x0030, 0x7014, 0x9084, 0x1984, 0x9085, 0x0016,
+ 0x7016, 0x00ee, 0x002e, 0x0005, 0x00b6, 0x00e6, 0x00d6, 0x00c6,
+ 0x0066, 0x0056, 0x0046, 0x0006, 0x0126, 0x2091, 0x8000, 0x6010,
+ 0x2058, 0xbca0, 0x2071, 0x19bf, 0x7018, 0x2058, 0x8bff, 0x0190,
+ 0xb8a0, 0x9406, 0x0118, 0xb854, 0x2058, 0x0cc0, 0x6014, 0x0096,
+ 0x2048, 0xac6c, 0xad70, 0xae78, 0x009e, 0x080c, 0x65d1, 0x0110,
+ 0x9085, 0x0001, 0x012e, 0x000e, 0x004e, 0x005e, 0x006e, 0x00ce,
+ 0x00de, 0x00ee, 0x00be, 0x0005, 0x080c, 0x912e, 0x7003, 0x1200,
+ 0x7838, 0x7012, 0x783c, 0x7016, 0x00c6, 0x7820, 0x9086, 0x0004,
+ 0x1148, 0x7810, 0x9005, 0x0130, 0x00b6, 0x2058, 0xb810, 0xb914,
+ 0x00be, 0x0020, 0x2061, 0x1800, 0x6078, 0x617c, 0x9084, 0x00ff,
+ 0x700a, 0x710e, 0x00ce, 0x60c3, 0x002c, 0x0804, 0x962a, 0x080c,
+ 0x912e, 0x7003, 0x0f00, 0x7808, 0xd09c, 0x0128, 0xb810, 0x9084,
+ 0x00ff, 0x700a, 0xb814, 0x700e, 0x60c3, 0x0008, 0x0804, 0x962a,
+ 0x0156, 0x080c, 0x9179, 0x7003, 0x0200, 0x2011, 0x1848, 0x63f0,
+ 0x2312, 0x20a9, 0x0006, 0x2011, 0x1840, 0x2019, 0x1841, 0x9ef0,
+ 0x0002, 0x2376, 0x8e70, 0x2276, 0x8e70, 0x9398, 0x0002, 0x9290,
+ 0x0002, 0x1f04, 0x9899, 0x60c3, 0x001c, 0x015e, 0x0804, 0x962a,
+ 0x0016, 0x0026, 0x080c, 0x9155, 0x080c, 0x9167, 0x9e80, 0x0004,
+ 0x20e9, 0x0000, 0x20a0, 0x7814, 0x0096, 0x2048, 0xa800, 0x2048,
+ 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0021, 0x2098, 0x009e, 0x7808,
+ 0x9088, 0x0002, 0x21a8, 0x9192, 0x0010, 0x1250, 0x4003, 0x9080,
+ 0x0004, 0x8003, 0x60c2, 0x080c, 0x962a, 0x002e, 0x001e, 0x0005,
+ 0x20a9, 0x0010, 0x4003, 0x080c, 0x9ed6, 0x20a1, 0x0240, 0x22a8,
+ 0x4003, 0x0c68, 0x080c, 0x912e, 0x7003, 0x6200, 0x7808, 0x700e,
+ 0x60c3, 0x0008, 0x0804, 0x962a, 0x0016, 0x0026, 0x080c, 0x912e,
+ 0x20e9, 0x0000, 0x20a1, 0x024c, 0x7814, 0x0096, 0x2048, 0xa800,
+ 0x2048, 0xa860, 0x20e0, 0xa85c, 0x9080, 0x0023, 0x2098, 0x009e,
+ 0x7808, 0x9088, 0x0002, 0x21a8, 0x4003, 0x8003, 0x60c2, 0x080c,
+ 0x962a, 0x002e, 0x001e, 0x0005, 0x00e6, 0x00c6, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x19bf, 0x700c, 0x2060, 0x8cff, 0x0178,
+ 0x080c, 0xc03f, 0x1110, 0x080c, 0xaa81, 0x600c, 0x0006, 0x080c,
+ 0xc2ab, 0x080c, 0xa0e3, 0x080c, 0x9a4e, 0x00ce, 0x0c78, 0x2c00,
+ 0x700e, 0x700a, 0x012e, 0x000e, 0x00ce, 0x00ee, 0x0005, 0x0126,
+ 0x0156, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0026, 0x0016,
+ 0x0006, 0x2091, 0x8000, 0x2001, 0x180c, 0x200c, 0x918c, 0xe7ff,
+ 0x2102, 0x2069, 0x0100, 0x2079, 0x0140, 0x2071, 0x19bf, 0x7024,
+ 0x2060, 0x8cff, 0x01f8, 0x080c, 0x9656, 0x6ac0, 0x68c3, 0x0000,
+ 0x080c, 0x835a, 0x00c6, 0x2061, 0x0100, 0x080c, 0x9eef, 0x00ce,
+ 0x20a9, 0x01f4, 0x0461, 0x2009, 0x0013, 0x080c, 0xa15d, 0x000e,
+ 0x001e, 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e,
+ 0x012e, 0x0005, 0x2001, 0x1800, 0x2004, 0x9096, 0x0001, 0x0d78,
+ 0x9096, 0x0004, 0x0d60, 0x080c, 0x835a, 0x6814, 0x9084, 0x0001,
+ 0x0110, 0x68a7, 0x95f5, 0x6817, 0x0008, 0x68c3, 0x0000, 0x2011,
+ 0x5d94, 0x080c, 0x82da, 0x20a9, 0x01f4, 0x0009, 0x08c0, 0x6824,
+ 0xd094, 0x0140, 0x6827, 0x0004, 0x7804, 0x9084, 0x4000, 0x190c,
+ 0x2c98, 0x0090, 0xd084, 0x0118, 0x6827, 0x0001, 0x0010, 0x1f04,
+ 0x997f, 0x7804, 0x9084, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c,
+ 0x2c88, 0x9006, 0x080c, 0x2c88, 0x0005, 0x0126, 0x0156, 0x00f6,
+ 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0026, 0x0016, 0x0006, 0x2091,
+ 0x8000, 0x2001, 0x180c, 0x200c, 0x918c, 0xdbff, 0x2102, 0x2069,
+ 0x0100, 0x2079, 0x0140, 0x2071, 0x19bf, 0x703c, 0x2060, 0x8cff,
+ 0x0904, 0x9a04, 0x9386, 0x0002, 0x1128, 0x6814, 0x9084, 0x0002,
+ 0x0904, 0x9a04, 0x68af, 0x95f5, 0x6817, 0x0010, 0x2009, 0x00fa,
+ 0x8109, 0x1df0, 0x69c6, 0x68cb, 0x0008, 0x080c, 0x8367, 0x080c,
+ 0x1f32, 0x2001, 0x0032, 0x6920, 0xd1bc, 0x0130, 0x8001, 0x1dd8,
+ 0x692c, 0x918d, 0x0008, 0x692e, 0x20a9, 0x03e8, 0x6824, 0xd094,
+ 0x0140, 0x6827, 0x0004, 0x7804, 0x9084, 0x4000, 0x190c, 0x2c98,
+ 0x0090, 0xd08c, 0x0118, 0x6827, 0x0002, 0x0010, 0x1f04, 0x99de,
+ 0x7804, 0x9084, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2c88,
+ 0x9006, 0x080c, 0x2c88, 0x6827, 0x4000, 0x6824, 0x83ff, 0x1120,
+ 0x2009, 0x0049, 0x080c, 0xa15d, 0x000e, 0x001e, 0x002e, 0x006e,
+ 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e, 0x012e, 0x0005, 0x00d6,
+ 0x0126, 0x2091, 0x8000, 0x2069, 0x19bf, 0x6a06, 0x012e, 0x00de,
+ 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000, 0x2069, 0x19bf, 0x6a32,
+ 0x012e, 0x00de, 0x0005, 0x080c, 0x92f1, 0x7854, 0x7032, 0x7042,
+ 0x7047, 0x1000, 0x00f8, 0x080c, 0x92f1, 0x7854, 0x7032, 0x7042,
+ 0x7047, 0x4000, 0x00b8, 0x080c, 0x92f1, 0x7854, 0x7032, 0x7042,
+ 0x7047, 0x2000, 0x0078, 0x080c, 0x92f1, 0x7854, 0x7032, 0x7042,
+ 0x7047, 0x0400, 0x0038, 0x080c, 0x92f1, 0x7854, 0x7032, 0x7042,
+ 0x7047, 0x0200, 0x60c3, 0x0020, 0x0804, 0x962a, 0x00e6, 0x2071,
+ 0x19bf, 0x7020, 0x9005, 0x0110, 0x8001, 0x7022, 0x00ee, 0x0005,
+ 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0076, 0x0066, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0x19bf, 0x7614, 0x2660, 0x2678, 0x2039,
+ 0x0001, 0x87ff, 0x0904, 0x9af3, 0x8cff, 0x0904, 0x9af3, 0x6020,
+ 0x9086, 0x0006, 0x1904, 0x9aee, 0x88ff, 0x0138, 0x2800, 0x9c06,
+ 0x1904, 0x9aee, 0x2039, 0x0000, 0x0050, 0x6010, 0x9b06, 0x1904,
+ 0x9aee, 0x85ff, 0x0120, 0x6054, 0x9106, 0x1904, 0x9aee, 0x7024,
+ 0x9c06, 0x15b0, 0x2069, 0x0100, 0x68c0, 0x9005, 0x1160, 0x6824,
+ 0xd084, 0x0148, 0x6827, 0x0001, 0x080c, 0x835a, 0x080c, 0x9b78,
+ 0x7027, 0x0000, 0x0428, 0x080c, 0x835a, 0x6820, 0xd0b4, 0x0110,
+ 0x68a7, 0x95f5, 0x6817, 0x0008, 0x68c3, 0x0000, 0x080c, 0x9b78,
+ 0x7027, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000,
+ 0x0138, 0x2001, 0x0100, 0x080c, 0x2c88, 0x9006, 0x080c, 0x2c88,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x7014, 0x9c36, 0x1110, 0x660c, 0x7616, 0x7010, 0x9c36, 0x1140,
+ 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x7012, 0x0010, 0x7013, 0x0000,
+ 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x89ff, 0x1168, 0x600f, 0x0000, 0x6014, 0x0096, 0x2048, 0x080c,
+ 0xbe35, 0x0110, 0x080c, 0xd830, 0x009e, 0x080c, 0xa113, 0x080c,
+ 0x9a4e, 0x88ff, 0x1190, 0x00ce, 0x0804, 0x9a69, 0x2c78, 0x600c,
+ 0x2060, 0x0804, 0x9a69, 0x9006, 0x012e, 0x000e, 0x006e, 0x007e,
+ 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601b, 0x0000, 0x00ce,
+ 0x98c5, 0x0001, 0x0c88, 0x00f6, 0x00e6, 0x00d6, 0x0096, 0x00c6,
+ 0x0066, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x19bf,
+ 0x7638, 0x2660, 0x2678, 0x8cff, 0x0904, 0x9b67, 0x6020, 0x9086,
+ 0x0006, 0x1904, 0x9b62, 0x87ff, 0x0128, 0x2700, 0x9c06, 0x1904,
+ 0x9b62, 0x0040, 0x6010, 0x9b06, 0x15e8, 0x85ff, 0x0118, 0x6054,
+ 0x9106, 0x15c0, 0x703c, 0x9c06, 0x1168, 0x0036, 0x2019, 0x0001,
+ 0x080c, 0x999d, 0x7033, 0x0000, 0x9006, 0x703e, 0x7042, 0x7046,
+ 0x704a, 0x003e, 0x7038, 0x9c36, 0x1110, 0x660c, 0x763a, 0x7034,
+ 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x7036, 0x0010,
+ 0x7037, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e,
+ 0x0008, 0x2678, 0x600f, 0x0000, 0x6014, 0x2048, 0x080c, 0xbe35,
+ 0x0110, 0x080c, 0xd830, 0x080c, 0xa113, 0x87ff, 0x1198, 0x00ce,
+ 0x0804, 0x9b13, 0x2c78, 0x600c, 0x2060, 0x0804, 0x9b13, 0x9006,
+ 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce, 0x009e, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x601b, 0x0000, 0x00ce, 0x97bd, 0x0001, 0x0c80,
+ 0x00e6, 0x2071, 0x19bf, 0x2001, 0x1800, 0x2004, 0x9086, 0x0002,
+ 0x1118, 0x7007, 0x0005, 0x0010, 0x7007, 0x0000, 0x00ee, 0x0005,
+ 0x00f6, 0x00e6, 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x19bf, 0x2c10, 0x7638, 0x2660, 0x2678, 0x8cff,
+ 0x0540, 0x2200, 0x9c06, 0x1508, 0x7038, 0x9c36, 0x1110, 0x660c,
+ 0x763a, 0x7034, 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00,
+ 0x7036, 0x0010, 0x7037, 0x0000, 0x660c, 0x2c00, 0x9f06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x6004, 0x9086, 0x0040,
+ 0x090c, 0x8b04, 0x9085, 0x0001, 0x0020, 0x2c78, 0x600c, 0x2060,
+ 0x08b0, 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce, 0x00ee, 0x00fe,
+ 0x0005, 0x0096, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0026,
+ 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0x19bf, 0x760c, 0x2660,
+ 0x2678, 0x8cff, 0x0904, 0x9c5e, 0x6010, 0x00b6, 0x2058, 0xb8a0,
+ 0x00be, 0x9206, 0x1904, 0x9c59, 0x7024, 0x9c06, 0x1520, 0x2069,
+ 0x0100, 0x68c0, 0x9005, 0x0904, 0x9c30, 0x080c, 0x9656, 0x68c3,
+ 0x0000, 0x080c, 0x9b78, 0x7027, 0x0000, 0x0036, 0x2069, 0x0140,
+ 0x6b04, 0x9384, 0x1000, 0x0138, 0x2001, 0x0100, 0x080c, 0x2c88,
+ 0x9006, 0x080c, 0x2c88, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110,
+ 0x6827, 0x0001, 0x003e, 0x700c, 0x9c36, 0x1110, 0x660c, 0x760e,
+ 0x7008, 0x9c36, 0x1140, 0x2c00, 0x9f36, 0x0118, 0x2f00, 0x700a,
+ 0x0010, 0x700b, 0x0000, 0x660c, 0x0066, 0x2c00, 0x9f06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x080c, 0xc02e, 0x1180,
+ 0x080c, 0x31b4, 0x080c, 0xc03f, 0x1518, 0x080c, 0xaa81, 0x0400,
+ 0x080c, 0x9b78, 0x6824, 0xd084, 0x09b0, 0x6827, 0x0001, 0x0898,
+ 0x080c, 0xc03f, 0x1118, 0x080c, 0xaa81, 0x0090, 0x6014, 0x2048,
+ 0x080c, 0xbe35, 0x0168, 0x6020, 0x9086, 0x0003, 0x1508, 0xa867,
+ 0x0103, 0xab7a, 0xa877, 0x0000, 0x080c, 0x6adc, 0x080c, 0xc022,
+ 0x080c, 0xc2ab, 0x080c, 0xa113, 0x080c, 0x9a4e, 0x00ce, 0x0804,
+ 0x9bd9, 0x2c78, 0x600c, 0x2060, 0x0804, 0x9bd9, 0x012e, 0x000e,
+ 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x009e, 0x0005,
+ 0x6020, 0x9086, 0x0006, 0x1d20, 0x080c, 0xd830, 0x0c08, 0x00d6,
+ 0x080c, 0x9179, 0x7003, 0x0200, 0x7007, 0x0014, 0x60c3, 0x0014,
+ 0x20e1, 0x0001, 0x2099, 0x1961, 0x20e9, 0x0000, 0x20a1, 0x0250,
+ 0x20a9, 0x0004, 0x4003, 0x7023, 0x0004, 0x7027, 0x7878, 0x080c,
+ 0x962a, 0x00de, 0x0005, 0x080c, 0x9179, 0x700b, 0x0800, 0x7814,
+ 0x9084, 0xff00, 0x700e, 0x7814, 0x9084, 0x00ff, 0x7022, 0x782c,
+ 0x7026, 0x7858, 0x9084, 0x00ff, 0x9085, 0x0200, 0x7002, 0x7858,
+ 0x9084, 0xff00, 0x8007, 0x7006, 0x60c2, 0x0804, 0x962a, 0x00b6,
+ 0x00d6, 0x0016, 0x00d6, 0x2f68, 0x2009, 0x0035, 0x080c, 0xc4b1,
+ 0x00de, 0x1904, 0x9d0c, 0x080c, 0x912e, 0x7003, 0x1300, 0x782c,
+ 0x080c, 0x9e12, 0x2068, 0x6820, 0x9086, 0x0003, 0x0560, 0x7810,
+ 0x2058, 0xbaa0, 0x080c, 0xa062, 0x11d8, 0x9286, 0x007e, 0x1128,
+ 0x700b, 0x00ff, 0x700f, 0xfffe, 0x0498, 0x9286, 0x007f, 0x1128,
+ 0x700b, 0x00ff, 0x700f, 0xfffd, 0x0458, 0x9284, 0xff80, 0x0180,
+ 0x9286, 0x0080, 0x1128, 0x700b, 0x00ff, 0x700f, 0xfffc, 0x0400,
+ 0x92d8, 0x1000, 0x2b5c, 0xb810, 0x700a, 0xb814, 0x700e, 0x00c0,
+ 0x6098, 0x700e, 0x00a8, 0x080c, 0xa062, 0x1130, 0x7810, 0x2058,
+ 0xb8a0, 0x9082, 0x007e, 0x0250, 0x00d6, 0x2069, 0x181e, 0x2d04,
+ 0x700a, 0x8d68, 0x2d04, 0x700e, 0x00de, 0x0010, 0x6034, 0x700e,
+ 0x7838, 0x7012, 0x783c, 0x7016, 0x60c3, 0x000c, 0x001e, 0x00de,
+ 0x080c, 0x962a, 0x00be, 0x0005, 0x781b, 0x0001, 0x7803, 0x0006,
+ 0x001e, 0x00de, 0x00be, 0x0005, 0x792c, 0x9180, 0x0008, 0x200c,
+ 0x9186, 0x0006, 0x01c0, 0x9186, 0x0003, 0x0904, 0x9d87, 0x9186,
+ 0x0005, 0x0904, 0x9d6f, 0x9186, 0x0004, 0x05d8, 0x9186, 0x0008,
+ 0x0904, 0x9d78, 0x7807, 0x0037, 0x782f, 0x0003, 0x7817, 0x1700,
+ 0x080c, 0x9def, 0x0005, 0x080c, 0x9db0, 0x00d6, 0x0026, 0x792c,
+ 0x2168, 0x2009, 0x4000, 0x6800, 0x0002, 0x9d50, 0x9d5b, 0x9d52,
+ 0x9d5b, 0x9d57, 0x9d50, 0x9d50, 0x9d5b, 0x9d5b, 0x9d5b, 0x9d5b,
+ 0x9d50, 0x9d50, 0x9d50, 0x9d50, 0x9d50, 0x9d5b, 0x9d50, 0x9d5b,
+ 0x080c, 0x0dfa, 0x6824, 0xd0e4, 0x0110, 0xd0cc, 0x0110, 0x900e,
+ 0x0010, 0x2009, 0x2000, 0x682c, 0x7022, 0x6830, 0x7026, 0x0804,
+ 0x9da9, 0x080c, 0x9db0, 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009,
+ 0x4000, 0x6a00, 0x9286, 0x0002, 0x1108, 0x900e, 0x04d0, 0x080c,
+ 0x9db0, 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x0488,
+ 0x04b9, 0x00d6, 0x0026, 0x792c, 0x2168, 0x2009, 0x4000, 0x9286,
+ 0x0005, 0x0118, 0x9286, 0x0002, 0x1108, 0x900e, 0x0410, 0x0441,
+ 0x00d6, 0x0026, 0x792c, 0x2168, 0x6814, 0x6924, 0xc185, 0x6926,
+ 0x0096, 0x2048, 0xa9ac, 0xa834, 0x9112, 0xa9b0, 0xa838, 0x009e,
+ 0x9103, 0x7022, 0x7226, 0x792c, 0x9180, 0x0000, 0x2004, 0x908e,
+ 0x0002, 0x0130, 0x908e, 0x0004, 0x0118, 0x2009, 0x4000, 0x0008,
+ 0x900e, 0x712a, 0x60c3, 0x0018, 0x002e, 0x00de, 0x0804, 0x962a,
+ 0x00b6, 0x0036, 0x0046, 0x0056, 0x0066, 0x080c, 0x9179, 0x9006,
+ 0x7003, 0x0200, 0x7938, 0x710a, 0x793c, 0x710e, 0x7810, 0x2058,
+ 0xb8a0, 0x080c, 0xa062, 0x1118, 0x9092, 0x007e, 0x0268, 0x00d6,
+ 0x2069, 0x181e, 0x2d2c, 0x8d68, 0x2d34, 0x90d8, 0x1000, 0x2b5c,
+ 0xbb10, 0xbc14, 0x00de, 0x0028, 0x901e, 0x6498, 0x2029, 0x0000,
+ 0x6634, 0x782c, 0x9080, 0x0008, 0x2004, 0x9086, 0x0003, 0x1128,
+ 0x7512, 0x7616, 0x731a, 0x741e, 0x0020, 0x7312, 0x7416, 0x751a,
+ 0x761e, 0x006e, 0x005e, 0x004e, 0x003e, 0x00be, 0x0005, 0x080c,
+ 0x9179, 0x7003, 0x0100, 0x782c, 0x700a, 0x7814, 0x700e, 0x700e,
+ 0x60c3, 0x0008, 0x0804, 0x962a, 0x080c, 0x9125, 0x7003, 0x1400,
+ 0x7838, 0x700a, 0x0079, 0x783c, 0x700e, 0x782c, 0x7012, 0x7830,
+ 0x7016, 0x7834, 0x9084, 0x00ff, 0x8007, 0x701a, 0x60c3, 0x0010,
+ 0x0804, 0x962a, 0x00e6, 0x2071, 0x0240, 0x0006, 0x00f6, 0x2078,
+ 0x7810, 0x00b6, 0x2058, 0xb8bc, 0xd084, 0x0120, 0x7844, 0x702a,
+ 0x7848, 0x702e, 0x00be, 0x00fe, 0x000e, 0x00ee, 0x0005, 0x080c,
+ 0x9170, 0x7003, 0x0100, 0x782c, 0x700a, 0x7814, 0x700e, 0x60c3,
+ 0x0008, 0x0804, 0x962a, 0x0021, 0x60c3, 0x0000, 0x0804, 0x962a,
+ 0x00d6, 0x080c, 0x9eeb, 0xb810, 0x9085, 0x0300, 0x7002, 0xb814,
+ 0x7006, 0x2069, 0x1800, 0x6878, 0x700a, 0x687c, 0x700e, 0x7013,
+ 0x0819, 0x080c, 0x9618, 0x721a, 0x2f10, 0x7222, 0x7a08, 0x7226,
+ 0x2071, 0x024c, 0x00de, 0x0005, 0x00a9, 0x7914, 0x712a, 0x60c3,
+ 0x0000, 0x60a7, 0x9575, 0x0026, 0x080c, 0x2afe, 0x0228, 0x2011,
+ 0x0101, 0x2204, 0xc0c5, 0x2012, 0x002e, 0x080c, 0x964d, 0x080c,
+ 0x8351, 0x0005, 0x0036, 0x0096, 0x00d6, 0x00e6, 0x7858, 0x2048,
+ 0xaa7c, 0x9296, 0x00c0, 0x9294, 0xfffd, 0xaa7e, 0xaa80, 0x9294,
+ 0x0300, 0xaa82, 0xa96c, 0x9194, 0x00ff, 0xab74, 0x9384, 0x00ff,
+ 0x908d, 0xc200, 0xa96e, 0x9384, 0xff00, 0x9215, 0xaa76, 0xa870,
+ 0xaa78, 0xa87a, 0xaa72, 0x00d6, 0x2069, 0x0200, 0x080c, 0x9eeb,
+ 0x00de, 0x20e9, 0x0000, 0x20a1, 0x0240, 0x20a9, 0x000a, 0xa860,
+ 0x20e0, 0xa85c, 0x9080, 0x001b, 0x2098, 0x4003, 0x60a3, 0x0035,
+ 0xaa68, 0x9294, 0x7000, 0x9286, 0x3000, 0x0110, 0x60a3, 0x0037,
+ 0x00ee, 0x00de, 0x009e, 0x003e, 0x0005, 0x900e, 0x7814, 0x0096,
+ 0x2048, 0xa87c, 0xd0fc, 0x01c0, 0x9084, 0x0003, 0x11a8, 0x2001,
+ 0x180c, 0x2004, 0xd0bc, 0x0180, 0x7824, 0xd0cc, 0x1168, 0xd0c4,
+ 0x1158, 0xa8a8, 0x9005, 0x1140, 0x2001, 0x180c, 0x200c, 0xc1d5,
+ 0x2102, 0x2009, 0x198a, 0x210c, 0x009e, 0x918d, 0x0092, 0x0010,
+ 0x2009, 0x0096, 0x60ab, 0x0036, 0x6116, 0x0005, 0x2009, 0x0009,
+ 0x00a0, 0x2009, 0x000a, 0x0088, 0x2009, 0x000b, 0x0070, 0x2009,
+ 0x000c, 0x0058, 0x2009, 0x000d, 0x0040, 0x2009, 0x000e, 0x0028,
+ 0x2009, 0x000f, 0x0010, 0x2009, 0x0008, 0x6912, 0x0005, 0x00d6,
+ 0x9290, 0x0018, 0x8214, 0x20e9, 0x0000, 0x2069, 0x0200, 0x6813,
+ 0x0000, 0x22a8, 0x9284, 0x00e0, 0x0128, 0x20a9, 0x0020, 0x9292,
+ 0x0020, 0x0008, 0x9016, 0x20a1, 0x0240, 0x9006, 0x4004, 0x82ff,
+ 0x0120, 0x6810, 0x8000, 0x6812, 0x0c60, 0x00de, 0x0005, 0x00d6,
+ 0x0096, 0x6014, 0x2048, 0xa878, 0x6056, 0x9006, 0xa836, 0xa83a,
+ 0xa99c, 0xa946, 0xa84a, 0x6023, 0x0003, 0x6007, 0x0040, 0x6003,
+ 0x0003, 0x600b, 0xffff, 0xa817, 0x0001, 0xa842, 0xa83e, 0x2900,
+ 0xa85a, 0xa813, 0x1fc6, 0x080c, 0x86de, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x8ced, 0x012e, 0x009e, 0x00de, 0x0005, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x00a6, 0x0096, 0x0066, 0x0126, 0x2091, 0x8000,
+ 0x2071, 0x19bf, 0x760c, 0x2660, 0x2678, 0x8cff, 0x0904, 0x9fc2,
+ 0x7024, 0x9c06, 0x1520, 0x2069, 0x0100, 0x68c0, 0x9005, 0x0904,
+ 0x9f94, 0x080c, 0x9656, 0x68c3, 0x0000, 0x080c, 0x9b78, 0x7027,
+ 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0x9384, 0x1000, 0x0138,
+ 0x2001, 0x0100, 0x080c, 0x2c88, 0x9006, 0x080c, 0x2c88, 0x2069,
+ 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x700c,
+ 0x9c36, 0x1110, 0x660c, 0x760e, 0x7008, 0x9c36, 0x1140, 0x2c00,
+ 0x9f36, 0x0118, 0x2f00, 0x700a, 0x0010, 0x700b, 0x0000, 0x660c,
+ 0x0066, 0x2c00, 0x9f06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f,
+ 0x0000, 0x080c, 0xc02e, 0x1180, 0x080c, 0x31b4, 0x080c, 0xc03f,
+ 0x1518, 0x080c, 0xaa81, 0x0400, 0x080c, 0x9b78, 0x6824, 0xd084,
+ 0x09b0, 0x6827, 0x0001, 0x0898, 0x080c, 0xc03f, 0x1118, 0x080c,
+ 0xaa81, 0x0090, 0x6014, 0x2048, 0x080c, 0xbe35, 0x0168, 0x6020,
+ 0x9086, 0x0003, 0x1520, 0xa867, 0x0103, 0xab7a, 0xa877, 0x0000,
+ 0x080c, 0x6ae9, 0x080c, 0xc022, 0x080c, 0xc2ab, 0x080c, 0xa113,
+ 0x080c, 0x9a4e, 0x00ce, 0x0804, 0x9f45, 0x2c78, 0x600c, 0x2060,
+ 0x0804, 0x9f45, 0x700f, 0x0000, 0x700b, 0x0000, 0x012e, 0x006e,
+ 0x009e, 0x00ae, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x6020,
+ 0x9086, 0x0006, 0x1d08, 0x080c, 0xd830, 0x08f0, 0x00d6, 0x0156,
+ 0x080c, 0x9179, 0x7a14, 0x82ff, 0x0138, 0x7003, 0x0100, 0x700b,
+ 0x0003, 0x60c3, 0x0008, 0x0490, 0x7003, 0x0200, 0x7007, 0x0000,
+ 0x2069, 0x1800, 0x901e, 0x6800, 0x9086, 0x0004, 0x1110, 0xc38d,
+ 0x0060, 0x080c, 0x7207, 0x1110, 0xc3ad, 0x0008, 0xc3a5, 0x6ad8,
+ 0xd29c, 0x1110, 0xd2ac, 0x0108, 0xc39d, 0x730e, 0x2011, 0x1848,
+ 0x63f0, 0x2312, 0x20a9, 0x0006, 0x2011, 0x1840, 0x2019, 0x1841,
+ 0x2071, 0x0250, 0x2376, 0x8e70, 0x2276, 0x8e70, 0x9398, 0x0002,
+ 0x9290, 0x0002, 0x1f04, 0xa00a, 0x60c3, 0x0020, 0x080c, 0x962a,
+ 0x015e, 0x00de, 0x0005, 0x0156, 0x080c, 0x9179, 0x7a14, 0x82ff,
+ 0x0168, 0x9286, 0xffff, 0x0118, 0x9282, 0x000e, 0x1238, 0x7003,
+ 0x0100, 0x700b, 0x0003, 0x60c3, 0x0008, 0x0488, 0x7003, 0x0200,
+ 0x7007, 0x001c, 0x700f, 0x0001, 0x2011, 0x1995, 0x2204, 0x8007,
+ 0x701a, 0x8210, 0x2204, 0x8007, 0x701e, 0x0421, 0x1120, 0xb8a0,
+ 0x9082, 0x007f, 0x0248, 0x2001, 0x181e, 0x2004, 0x7022, 0x2001,
+ 0x181f, 0x2004, 0x7026, 0x0030, 0x2001, 0x1817, 0x2004, 0x9084,
+ 0x00ff, 0x7026, 0x20a9, 0x0004, 0x20e1, 0x0001, 0x2099, 0x1805,
+ 0x20e9, 0x0000, 0x20a1, 0x0256, 0x4003, 0x60c3, 0x001c, 0x015e,
+ 0x0804, 0x962a, 0x0006, 0x2001, 0x1836, 0x2004, 0xd0ac, 0x000e,
+ 0x0005, 0x2011, 0x0003, 0x080c, 0x9a0f, 0x2011, 0x0002, 0x080c,
+ 0x9a19, 0x080c, 0x9927, 0x0036, 0x901e, 0x080c, 0x999d, 0x003e,
+ 0x0005, 0x2071, 0x188b, 0x7000, 0x9005, 0x0140, 0x2001, 0x0976,
+ 0x2071, 0x1800, 0x7072, 0x7076, 0x7067, 0xffe0, 0x2071, 0x1800,
+ 0x7070, 0x7052, 0x7057, 0x1cd0, 0x0005, 0x00e6, 0x0126, 0x2071,
+ 0x1800, 0x2091, 0x8000, 0x7550, 0x9582, 0x0010, 0x0608, 0x7054,
+ 0x2060, 0x6000, 0x9086, 0x0000, 0x0148, 0x9ce0, 0x0018, 0x7064,
+ 0x9c02, 0x1208, 0x0cb0, 0x2061, 0x1cd0, 0x0c98, 0x6003, 0x0008,
+ 0x8529, 0x7552, 0x9ca8, 0x0018, 0x7064, 0x9502, 0x1230, 0x7556,
+ 0x9085, 0x0001, 0x012e, 0x00ee, 0x0005, 0x7057, 0x1cd0, 0x0cc0,
+ 0x9006, 0x0cc0, 0x00e6, 0x2071, 0x1800, 0x7550, 0x9582, 0x0010,
+ 0x0600, 0x7054, 0x2060, 0x6000, 0x9086, 0x0000, 0x0148, 0x9ce0,
+ 0x0018, 0x7064, 0x9c02, 0x1208, 0x0cb0, 0x2061, 0x1cd0, 0x0c98,
+ 0x6003, 0x0008, 0x8529, 0x7552, 0x9ca8, 0x0018, 0x7064, 0x9502,
+ 0x1228, 0x7556, 0x9085, 0x0001, 0x00ee, 0x0005, 0x7057, 0x1cd0,
+ 0x0cc8, 0x9006, 0x0cc8, 0x9c82, 0x1cd0, 0x0a0c, 0x0dfa, 0x2001,
+ 0x1819, 0x2004, 0x9c02, 0x1a0c, 0x0dfa, 0x9006, 0x6006, 0x600a,
+ 0x600e, 0x6016, 0x601a, 0x6012, 0x6023, 0x0000, 0x6003, 0x0000,
+ 0x601e, 0x6056, 0x605a, 0x6026, 0x602a, 0x602e, 0x6032, 0x6036,
+ 0x603a, 0x603e, 0x6042, 0x2061, 0x1800, 0x6050, 0x8000, 0x6052,
+ 0x9086, 0x0001, 0x0108, 0x0005, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x8c10, 0x012e, 0x0cc0, 0x0006, 0x6000, 0x9086, 0x0000, 0x01b0,
+ 0x601c, 0xd084, 0x190c, 0x19b4, 0x6017, 0x0000, 0x6023, 0x0007,
+ 0x2001, 0x195e, 0x2004, 0x0006, 0x9082, 0x0051, 0x000e, 0x0208,
+ 0x8004, 0x601a, 0x080c, 0xdae2, 0x6043, 0x0000, 0x000e, 0x0005,
+ 0x00e6, 0x0126, 0x2071, 0x1800, 0x2091, 0x8000, 0x7550, 0x9582,
+ 0x0001, 0x0608, 0x7054, 0x2060, 0x6000, 0x9086, 0x0000, 0x0148,
+ 0x9ce0, 0x0018, 0x7064, 0x9c02, 0x1208, 0x0cb0, 0x2061, 0x1cd0,
+ 0x0c98, 0x6003, 0x0008, 0x8529, 0x7552, 0x9ca8, 0x0018, 0x7064,
+ 0x9502, 0x1230, 0x7556, 0x9085, 0x0001, 0x012e, 0x00ee, 0x0005,
+ 0x7057, 0x1cd0, 0x0cc0, 0x9006, 0x0cc0, 0x6020, 0x9084, 0x000f,
+ 0x0002, 0xa170, 0xa179, 0xa194, 0xa1af, 0xc55f, 0xc57c, 0xc597,
+ 0xa170, 0xa179, 0xa170, 0xa1cb, 0xa170, 0xa170, 0xa170, 0xa170,
+ 0x9186, 0x0013, 0x1128, 0x080c, 0x8b04, 0x080c, 0x8c10, 0x0005,
+ 0x0005, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c, 0x0dfa, 0x0013,
+ 0x006e, 0x0005, 0xa192, 0xa8f8, 0xaac8, 0xa192, 0xab56, 0xa4ae,
+ 0xa192, 0xa192, 0xa87a, 0xb0fa, 0xa192, 0xa192, 0xa192, 0xa192,
+ 0xa192, 0xa192, 0x080c, 0x0dfa, 0x0066, 0x6000, 0x90b2, 0x0016,
+ 0x1a0c, 0x0dfa, 0x0013, 0x006e, 0x0005, 0xa1ad, 0xb7e1, 0xa1ad,
+ 0xa1ad, 0xa1ad, 0xa1ad, 0xa1ad, 0xa1ad, 0xb778, 0xb963, 0xa1ad,
+ 0xb822, 0xb8a1, 0xb822, 0xb8a1, 0xa1ad, 0x080c, 0x0dfa, 0x6000,
+ 0x9082, 0x0016, 0x1a0c, 0x0dfa, 0x6000, 0x0002, 0xa1c9, 0xb141,
+ 0xb226, 0xb356, 0xb505, 0xa1c9, 0xa1c9, 0xa1c9, 0xb115, 0xb704,
+ 0xb707, 0xa1c9, 0xa1c9, 0xa1c9, 0xa1c9, 0xb736, 0xa1c9, 0xa1c9,
+ 0xa1c9, 0x080c, 0x0dfa, 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c,
+ 0x0dfa, 0x0013, 0x006e, 0x0005, 0xa1e4, 0xa1e4, 0xa227, 0xa2c6,
+ 0xa35b, 0xa1e4, 0xa1e4, 0xa1e4, 0xa1e6, 0xa1e4, 0xa1e4, 0xa1e4,
+ 0xa1e4, 0xa1e4, 0xa1e4, 0xa1e4, 0x080c, 0x0dfa, 0x9186, 0x004c,
+ 0x0588, 0x9186, 0x0003, 0x190c, 0x0dfa, 0x0096, 0x601c, 0xc0ed,
+ 0x601e, 0x6003, 0x0003, 0x6106, 0x6014, 0x2048, 0xa87c, 0x9084,
+ 0xa000, 0xc0b5, 0xa87e, 0xa8ac, 0xa846, 0xa8b0, 0xa84a, 0x9006,
+ 0xa836, 0xa83a, 0xa884, 0x9092, 0x199a, 0x0210, 0x2001, 0x1999,
+ 0x8003, 0x8013, 0x8213, 0x9210, 0x621a, 0x009e, 0x2c10, 0x080c,
+ 0x1afe, 0x080c, 0x86de, 0x0126, 0x2091, 0x8000, 0x080c, 0x8ced,
+ 0x012e, 0x0005, 0x6010, 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00,
+ 0x080c, 0xa37d, 0x080c, 0xc551, 0x6003, 0x0007, 0x0005, 0x00d6,
+ 0x0096, 0x00f6, 0x2079, 0x1800, 0x7a8c, 0x6014, 0x2048, 0xa87c,
+ 0xd0ec, 0x1110, 0x9290, 0x0018, 0xac78, 0xc4fc, 0x0046, 0xa8e0,
+ 0x9005, 0x1140, 0xa8dc, 0x921a, 0x0140, 0x0220, 0xa87b, 0x0007,
+ 0x2010, 0x0028, 0xa87b, 0x0015, 0x0010, 0xa87b, 0x0000, 0x8214,
+ 0xa883, 0x0000, 0xaa02, 0x0006, 0x0016, 0x0026, 0x00c6, 0x00d6,
+ 0x00e6, 0x00f6, 0x2400, 0x9005, 0x1108, 0x009a, 0x2100, 0x9086,
+ 0x0015, 0x1118, 0x2001, 0x0001, 0x0038, 0x2100, 0x9086, 0x0016,
+ 0x0118, 0x2001, 0x0001, 0x002a, 0x94a4, 0x0007, 0x8423, 0x9405,
+ 0x0002, 0xa28e, 0xa28e, 0xa289, 0xa28c, 0xa28e, 0xa286, 0xa279,
+ 0xa279, 0xa279, 0xa279, 0xa279, 0xa279, 0xa279, 0xa279, 0xa279,
+ 0xa279, 0x00fe, 0x00ee, 0x00de, 0x00ce, 0x002e, 0x001e, 0x000e,
+ 0x004e, 0x00fe, 0x009e, 0x00de, 0x080c, 0x0dfa, 0x080c, 0xad39,
+ 0x0028, 0x080c, 0xae5c, 0x0010, 0x080c, 0xaf4b, 0x00fe, 0x00ee,
+ 0x00de, 0x00ce, 0x002e, 0x001e, 0x2c00, 0xa896, 0x000e, 0x080c,
+ 0xa43b, 0x0530, 0xa804, 0xa80e, 0x00a6, 0x2050, 0xb100, 0x00ae,
+ 0x8006, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084, 0xffc0, 0x9080,
+ 0x0002, 0xaacc, 0xabd0, 0xacd4, 0xadd8, 0x2031, 0x0000, 0x2041,
+ 0x12a2, 0x080c, 0xa5e6, 0x0160, 0x000e, 0x9005, 0x0120, 0x00fe,
+ 0x009e, 0x00de, 0x0005, 0x00fe, 0x009e, 0x00de, 0x0804, 0xa0e3,
+ 0x2001, 0x002c, 0x900e, 0x080c, 0xa4a1, 0x0c70, 0x91b6, 0x0015,
+ 0x0170, 0x91b6, 0x0016, 0x0158, 0x91b2, 0x0047, 0x0a0c, 0x0dfa,
+ 0x91b2, 0x0050, 0x1a0c, 0x0dfa, 0x9182, 0x0047, 0x00ca, 0x2001,
+ 0x0109, 0x2004, 0xd08c, 0x0198, 0x0126, 0x2091, 0x2800, 0x0006,
+ 0x0016, 0x0026, 0x080c, 0x8632, 0x002e, 0x001e, 0x000e, 0x012e,
+ 0xa001, 0x6000, 0x9086, 0x0002, 0x1110, 0x0804, 0xa227, 0x0005,
+ 0xa2f9, 0xa2f9, 0xa2fb, 0xa331, 0xa2f9, 0xa2f9, 0xa2f9, 0xa2f9,
+ 0xa344, 0x080c, 0x0dfa, 0x00d6, 0x0016, 0x0096, 0x080c, 0x8bc0,
+ 0x080c, 0x8ced, 0x6003, 0x0004, 0x6114, 0x2148, 0xa87c, 0xd0fc,
+ 0x01c0, 0xa878, 0xc0fc, 0x9005, 0x1158, 0xa894, 0x9005, 0x0140,
+ 0x2001, 0x0000, 0x900e, 0x080c, 0xa4a1, 0x080c, 0xa0e3, 0x00a8,
+ 0x6003, 0x0002, 0xa8a4, 0xa9a8, 0x9105, 0x1178, 0xa8ae, 0xa8b2,
+ 0x0c78, 0xa87f, 0x0020, 0xa88c, 0xa88a, 0xa8a4, 0xa8ae, 0xa8a8,
+ 0xa8b2, 0xa8c7, 0x0000, 0xa8cb, 0x0000, 0x009e, 0x001e, 0x00de,
+ 0x0005, 0x080c, 0x8bc0, 0x00d6, 0x0096, 0x6114, 0x2148, 0x080c,
+ 0xbe37, 0x0120, 0xa87b, 0x0006, 0x080c, 0x6ae9, 0x009e, 0x00de,
+ 0x080c, 0xa0e3, 0x0804, 0x8ced, 0x080c, 0x8bc0, 0x080c, 0x318b,
+ 0x080c, 0xc54e, 0x00d6, 0x0096, 0x6114, 0x2148, 0x080c, 0xbe37,
+ 0x0120, 0xa87b, 0x0029, 0x080c, 0x6ae9, 0x009e, 0x00de, 0x080c,
+ 0xa0e3, 0x0804, 0x8ced, 0x9182, 0x0047, 0x0002, 0xa36b, 0xa36d,
+ 0xa36b, 0xa36b, 0xa36b, 0xa36b, 0xa36b, 0xa36b, 0xa36b, 0xa36b,
+ 0xa36b, 0xa36b, 0xa36d, 0x080c, 0x0dfa, 0x00d6, 0x0096, 0x080c,
+ 0x1582, 0x6114, 0x2148, 0xa87b, 0x0000, 0xa883, 0x0000, 0x080c,
+ 0x6ae9, 0x009e, 0x00de, 0x0804, 0xa0e3, 0x0026, 0x0036, 0x0056,
+ 0x0066, 0x0096, 0x00a6, 0x00f6, 0x0006, 0x080c, 0x1031, 0x000e,
+ 0x090c, 0x0dfa, 0xa960, 0x21e8, 0xa95c, 0x9188, 0x0019, 0x21a0,
+ 0x900e, 0x20a9, 0x0020, 0x4104, 0xa87a, 0x2079, 0x1800, 0x798c,
+ 0x9188, 0x0018, 0x918c, 0x0fff, 0xa972, 0xac76, 0x2950, 0x00a6,
+ 0x2001, 0x0205, 0x2003, 0x0000, 0x901e, 0x2029, 0x0001, 0x9182,
+ 0x0034, 0x1228, 0x2011, 0x001f, 0x080c, 0xb9e8, 0x04c0, 0x2130,
+ 0x2009, 0x0034, 0x2011, 0x001f, 0x080c, 0xb9e8, 0x96b2, 0x0034,
+ 0xb004, 0x904d, 0x0110, 0x080c, 0x0fe3, 0x080c, 0x1031, 0x01d0,
+ 0x8528, 0xa867, 0x0110, 0xa86b, 0x0000, 0x2920, 0xb406, 0x968a,
+ 0x003d, 0x1230, 0x2608, 0x2011, 0x001b, 0x080c, 0xb9e8, 0x00b8,
+ 0x96b2, 0x003c, 0x2009, 0x003c, 0x2950, 0x2011, 0x001b, 0x080c,
+ 0xb9e8, 0x0c18, 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f,
+ 0x95ad, 0x0050, 0xb566, 0xb070, 0xc0fd, 0xb072, 0x0048, 0x2001,
+ 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f, 0x95ad, 0x0050, 0xb566,
+ 0x2a48, 0xa804, 0xa807, 0x0000, 0x0006, 0x080c, 0x6ae9, 0x000e,
+ 0x2048, 0x9005, 0x1db0, 0x00fe, 0x00ae, 0x009e, 0x006e, 0x005e,
+ 0x003e, 0x002e, 0x0005, 0x00d6, 0x00f6, 0x0096, 0x0006, 0x080c,
+ 0x1031, 0x000e, 0x090c, 0x0dfa, 0xa960, 0x21e8, 0xa95c, 0x9188,
+ 0x0019, 0x21a0, 0x900e, 0x20a9, 0x0020, 0x4104, 0xaa66, 0xa87a,
+ 0x2079, 0x1800, 0x798c, 0x810c, 0x9188, 0x000c, 0x9182, 0x001a,
+ 0x0210, 0x2009, 0x001a, 0x21a8, 0x810b, 0xa972, 0xac76, 0x2e98,
+ 0xa85c, 0x9080, 0x001f, 0x20a0, 0x2001, 0x0205, 0x200c, 0x918d,
+ 0x0080, 0x2102, 0x4003, 0x2003, 0x0000, 0x080c, 0x6ae9, 0x009e,
+ 0x00fe, 0x00de, 0x0005, 0x0016, 0x00d6, 0x00f6, 0x0096, 0x0016,
+ 0x2001, 0x0205, 0x200c, 0x918d, 0x0080, 0x2102, 0x001e, 0x2079,
+ 0x0200, 0x2e98, 0xa87c, 0xd0ec, 0x0118, 0x9e80, 0x000c, 0x2098,
+ 0x2021, 0x003e, 0x901e, 0x9282, 0x0020, 0x0218, 0x2011, 0x0020,
+ 0x2018, 0x9486, 0x003e, 0x1170, 0x0096, 0x080c, 0x1031, 0x2900,
+ 0x009e, 0x05c0, 0xa806, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x0002, 0x20a0, 0x3300, 0x908e, 0x0260, 0x0140, 0x2009, 0x0280,
+ 0x9102, 0x920a, 0x0218, 0x2010, 0x2100, 0x9318, 0x2200, 0x9402,
+ 0x1228, 0x2400, 0x9202, 0x2410, 0x9318, 0x9006, 0x2020, 0x22a8,
+ 0xa800, 0x9200, 0xa802, 0x20e1, 0x0000, 0x4003, 0x83ff, 0x0180,
+ 0x3300, 0x9086, 0x0280, 0x1130, 0x7814, 0x8000, 0x9085, 0x0080,
+ 0x7816, 0x2e98, 0x2310, 0x84ff, 0x0904, 0xa450, 0x0804, 0xa452,
+ 0x9085, 0x0001, 0x7817, 0x0000, 0x009e, 0x00fe, 0x00de, 0x001e,
+ 0x0005, 0x00d6, 0x0036, 0x0096, 0x6314, 0x2348, 0xa87a, 0xa982,
+ 0x080c, 0x6adc, 0x009e, 0x003e, 0x00de, 0x0005, 0x91b6, 0x0015,
+ 0x1118, 0x080c, 0xa0e3, 0x0030, 0x91b6, 0x0016, 0x190c, 0x0dfa,
+ 0x080c, 0xa0e3, 0x0005, 0x20a9, 0x000e, 0x20e1, 0x0000, 0x2e98,
+ 0x6014, 0x0096, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x20a0, 0x009e,
+ 0x4003, 0x0136, 0x9080, 0x001b, 0x20a0, 0x2011, 0x0006, 0x20a9,
+ 0x0001, 0x3418, 0x8318, 0x23a0, 0x4003, 0x3318, 0x8318, 0x2398,
+ 0x8211, 0x1db8, 0x2011, 0x0006, 0x013e, 0x20a0, 0x3318, 0x8318,
+ 0x2398, 0x4003, 0x3418, 0x8318, 0x23a0, 0x8211, 0x1db8, 0x0096,
+ 0x080c, 0xbe37, 0x0130, 0x6014, 0x2048, 0xa807, 0x0000, 0xa867,
+ 0x0103, 0x009e, 0x0804, 0xa0e3, 0x0096, 0x00d6, 0x0036, 0x7330,
+ 0x9386, 0x0200, 0x11a8, 0x6010, 0x00b6, 0x2058, 0xb8bf, 0x0000,
+ 0x00be, 0x6014, 0x9005, 0x0130, 0x2048, 0xa807, 0x0000, 0xa867,
+ 0x0103, 0xab32, 0x080c, 0xa0e3, 0x003e, 0x00de, 0x009e, 0x0005,
+ 0x0011, 0x1d48, 0x0cc8, 0x0006, 0x0016, 0x080c, 0xc539, 0x0188,
+ 0x6014, 0x9005, 0x1170, 0x600b, 0x0003, 0x601b, 0x0000, 0x6043,
+ 0x0000, 0x2009, 0x0022, 0x080c, 0xa8d0, 0x9006, 0x001e, 0x000e,
+ 0x0005, 0x9085, 0x0001, 0x0cd0, 0x0096, 0x0016, 0x20a9, 0x0014,
+ 0x9e80, 0x000c, 0x20e1, 0x0000, 0x2098, 0x6014, 0x2048, 0xa860,
+ 0x20e8, 0xa85c, 0x9080, 0x0002, 0x20a0, 0x4003, 0x2001, 0x0205,
+ 0x2003, 0x0001, 0x2099, 0x0260, 0x20a9, 0x0016, 0x4003, 0x20a9,
+ 0x000a, 0xa804, 0x2048, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0002,
+ 0x20a0, 0x4003, 0x2001, 0x0205, 0x2003, 0x0002, 0x2099, 0x0260,
+ 0x20a9, 0x0020, 0x4003, 0x2003, 0x0000, 0x6014, 0x2048, 0xa800,
+ 0x2048, 0xa867, 0x0103, 0x080c, 0xa0e3, 0x001e, 0x009e, 0x0005,
+ 0x0096, 0x0016, 0x900e, 0x7030, 0x9086, 0x0100, 0x0140, 0x7038,
+ 0x9084, 0x00ff, 0x800c, 0x703c, 0x9084, 0x00ff, 0x8004, 0x9080,
+ 0x0004, 0x9108, 0x810b, 0x2011, 0x0002, 0x2019, 0x000c, 0x6014,
+ 0x2048, 0x080c, 0xb9e8, 0x080c, 0xbe37, 0x0140, 0x6014, 0x2048,
+ 0xa807, 0x0000, 0xa864, 0xa8e2, 0xa867, 0x0103, 0x080c, 0xa0e3,
+ 0x001e, 0x009e, 0x0005, 0x0016, 0x0096, 0x7030, 0x9086, 0x0100,
+ 0x1118, 0x2009, 0x0004, 0x0010, 0x7034, 0x800c, 0x810b, 0x2011,
+ 0x000c, 0x2019, 0x000c, 0x6014, 0x2048, 0xa804, 0x0096, 0x9005,
+ 0x0108, 0x2048, 0x080c, 0xb9e8, 0x009e, 0x080c, 0xbe37, 0x0148,
+ 0xa804, 0x9005, 0x1158, 0xa807, 0x0000, 0xa864, 0xa8e2, 0xa867,
+ 0x0103, 0x080c, 0xa0e3, 0x009e, 0x001e, 0x0005, 0x0086, 0x2040,
+ 0xa030, 0x8007, 0x9086, 0x0100, 0x1118, 0x080c, 0xaa81, 0x00e0,
+ 0xa034, 0x8007, 0x800c, 0x8806, 0x8006, 0x8007, 0x90bc, 0x003f,
+ 0x9084, 0xffc0, 0x9080, 0x000c, 0xa87b, 0x0000, 0xa883, 0x0000,
+ 0xa897, 0x4000, 0xaaa0, 0xab9c, 0xaca8, 0xada4, 0x2031, 0x0000,
+ 0x2041, 0x1288, 0x0019, 0x0d08, 0x008e, 0x0898, 0x0096, 0x0006,
+ 0x080c, 0x1031, 0x000e, 0x01b0, 0xa8ab, 0x0dcb, 0xa876, 0x000e,
+ 0xa8a2, 0x0006, 0xae6a, 0x2800, 0xa89e, 0xa97a, 0xaf72, 0xaa8e,
+ 0xab92, 0xac96, 0xad9a, 0x0086, 0x2940, 0x080c, 0x112e, 0x008e,
+ 0x9085, 0x0001, 0x009e, 0x0005, 0x00e6, 0x00d6, 0x0026, 0x7008,
+ 0x9084, 0x00ff, 0x6210, 0x00b6, 0x2258, 0xba10, 0x00be, 0x9206,
+ 0x1520, 0x700c, 0x6210, 0x00b6, 0x2258, 0xba14, 0x00be, 0x9206,
+ 0x11e0, 0x6043, 0x0000, 0x2c68, 0x0016, 0x2009, 0x0035, 0x080c,
+ 0xc4b1, 0x001e, 0x1158, 0x622c, 0x2268, 0x2071, 0x026c, 0x6b20,
+ 0x9386, 0x0003, 0x0130, 0x9386, 0x0006, 0x0128, 0x080c, 0xa0e3,
+ 0x0020, 0x0039, 0x0010, 0x080c, 0xa705, 0x002e, 0x00de, 0x00ee,
+ 0x0005, 0x0096, 0x6814, 0x2048, 0x9186, 0x0015, 0x0904, 0xa6ed,
+ 0x918e, 0x0016, 0x1904, 0xa703, 0x700c, 0x908c, 0xff00, 0x9186,
+ 0x1700, 0x0120, 0x9186, 0x0300, 0x1904, 0xa6c7, 0x89ff, 0x1138,
+ 0x6800, 0x9086, 0x000f, 0x0904, 0xa6aa, 0x0804, 0xa701, 0x6808,
+ 0x9086, 0xffff, 0x1904, 0xa6ef, 0xa87c, 0x9084, 0x0060, 0x9086,
+ 0x0020, 0x1128, 0xa83c, 0xa940, 0x9105, 0x1904, 0xa6ef, 0x6824,
+ 0xd084, 0x1904, 0xa6ef, 0xd0b4, 0x0158, 0x0016, 0x2001, 0x195e,
+ 0x200c, 0x6018, 0x9102, 0x9082, 0x0005, 0x001e, 0x1a04, 0xa6ef,
+ 0x080c, 0xc022, 0x685c, 0xa882, 0xa87c, 0xc0dc, 0xc0f4, 0xc0d4,
+ 0xa87e, 0x0026, 0x900e, 0x6a18, 0x2001, 0x000a, 0x080c, 0x84ff,
+ 0xa884, 0x920a, 0x0208, 0x8011, 0xaa86, 0x82ff, 0x002e, 0x1138,
+ 0x00c6, 0x2d60, 0x080c, 0xbb4a, 0x00ce, 0x0804, 0xa701, 0x00c6,
+ 0xa868, 0xd0fc, 0x1118, 0x080c, 0x5fa7, 0x0010, 0x080c, 0x6355,
+ 0x00ce, 0x1904, 0xa6ef, 0x00c6, 0x2d60, 0x080c, 0xa0e3, 0x00ce,
+ 0x0804, 0xa701, 0x00c6, 0x080c, 0xa130, 0x0198, 0x6017, 0x0000,
+ 0x6810, 0x6012, 0x080c, 0xc2b3, 0x6023, 0x0003, 0x6904, 0x00c6,
+ 0x2d60, 0x080c, 0xa0e3, 0x00ce, 0x080c, 0xa15d, 0x00ce, 0x0804,
+ 0xa701, 0x2001, 0x1960, 0x2004, 0x6842, 0x00ce, 0x04d0, 0x7008,
+ 0x9086, 0x000b, 0x11c8, 0x6010, 0x00b6, 0x2058, 0xb900, 0xc1bc,
+ 0xb902, 0x00be, 0x00c6, 0x2d60, 0xa87b, 0x0003, 0x080c, 0xc4f3,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c, 0x8679,
+ 0x080c, 0x8c10, 0x00ce, 0x00e8, 0x700c, 0x9086, 0x2a00, 0x1138,
+ 0x2001, 0x1960, 0x2004, 0x6842, 0x00a0, 0x0479, 0x00a0, 0x89ff,
+ 0x090c, 0x0dfa, 0x00c6, 0x00d6, 0x2d60, 0xa867, 0x0103, 0xa87b,
+ 0x0003, 0x080c, 0x6904, 0x080c, 0xc022, 0x080c, 0xa113, 0x00de,
+ 0x00ce, 0x080c, 0xa0e3, 0x009e, 0x0005, 0x9186, 0x0015, 0x1128,
+ 0x2001, 0x1960, 0x2004, 0x6842, 0x0068, 0x918e, 0x0016, 0x1160,
+ 0x00c6, 0x2d00, 0x2060, 0x080c, 0xdae2, 0x080c, 0x84a6, 0x080c,
+ 0xa0e3, 0x00ce, 0x080c, 0xa0e3, 0x0005, 0x0026, 0x0036, 0x0046,
+ 0x7228, 0xacb0, 0xabac, 0xd2f4, 0x0130, 0x2001, 0x1960, 0x2004,
+ 0x6842, 0x0804, 0xa77f, 0x00c6, 0x2d60, 0x080c, 0xba49, 0x00ce,
+ 0x6804, 0x9086, 0x0050, 0x1168, 0x00c6, 0x2d00, 0x2060, 0x6003,
+ 0x0001, 0x6007, 0x0050, 0x080c, 0x8679, 0x080c, 0x8c10, 0x00ce,
+ 0x04f0, 0x6800, 0x9086, 0x000f, 0x01a8, 0x89ff, 0x090c, 0x0dfa,
+ 0x6800, 0x9086, 0x0004, 0x1190, 0xa87c, 0xd0ac, 0x0178, 0xa843,
+ 0x0fff, 0xa83f, 0x0fff, 0xa880, 0xc0fc, 0xa882, 0x2001, 0x0001,
+ 0x6832, 0x0400, 0x2001, 0x0007, 0x6832, 0x00e0, 0xa87c, 0xd0b4,
+ 0x1150, 0xd0ac, 0x0db8, 0x6824, 0xd0f4, 0x1d48, 0xa838, 0xa934,
+ 0x9105, 0x0d80, 0x0c20, 0xd2ec, 0x1d68, 0x7024, 0x9306, 0x1118,
+ 0x7020, 0x9406, 0x0d38, 0x7020, 0x683e, 0x7024, 0x683a, 0x2001,
+ 0x0005, 0x6832, 0x080c, 0xc1aa, 0x080c, 0x8c10, 0x0010, 0x080c,
+ 0xa0e3, 0x004e, 0x003e, 0x002e, 0x0005, 0x00e6, 0x00d6, 0x0026,
+ 0x7008, 0x9084, 0x00ff, 0x6210, 0x00b6, 0x2258, 0xba10, 0x00be,
+ 0x9206, 0x1904, 0xa7ea, 0x700c, 0x6210, 0x00b6, 0x2258, 0xba14,
+ 0x00be, 0x9206, 0x1904, 0xa7ea, 0x6038, 0x2068, 0x6824, 0xc0dc,
+ 0x6826, 0x6a20, 0x9286, 0x0007, 0x0904, 0xa7ea, 0x9286, 0x0002,
+ 0x0904, 0xa7ea, 0x9286, 0x0000, 0x05e8, 0x6808, 0x633c, 0x9306,
+ 0x15c8, 0x2071, 0x026c, 0x9186, 0x0015, 0x0570, 0x918e, 0x0016,
+ 0x1100, 0x00c6, 0x6038, 0x2060, 0x6104, 0x9186, 0x004b, 0x01c0,
+ 0x9186, 0x004c, 0x01a8, 0x9186, 0x004d, 0x0190, 0x9186, 0x004e,
+ 0x0178, 0x9186, 0x0052, 0x0160, 0x6014, 0x0096, 0x2048, 0x080c,
+ 0xbe37, 0x090c, 0x0dfa, 0xa87b, 0x0003, 0x009e, 0x080c, 0xc4f3,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c, 0x8679,
+ 0x080c, 0x8c10, 0x00ce, 0x0030, 0x6038, 0x2070, 0x2001, 0x1960,
+ 0x2004, 0x7042, 0x080c, 0xa0e3, 0x002e, 0x00de, 0x00ee, 0x0005,
+ 0x00b6, 0x0096, 0x00f6, 0x6014, 0x2048, 0x6010, 0x2058, 0x91b6,
+ 0x0015, 0x0130, 0xba08, 0xbb0c, 0xbc00, 0xc48c, 0xbc02, 0x0460,
+ 0x0096, 0x0156, 0x0036, 0x0026, 0x2b48, 0x9e90, 0x0010, 0x2019,
+ 0x000a, 0x20a9, 0x0004, 0x080c, 0xb0d0, 0x002e, 0x003e, 0x015e,
+ 0x009e, 0x1904, 0xa859, 0x0096, 0x0156, 0x0036, 0x0026, 0x2b48,
+ 0x9e90, 0x0014, 0x2019, 0x0006, 0x20a9, 0x0004, 0x080c, 0xb0d0,
+ 0x002e, 0x003e, 0x015e, 0x009e, 0x15a0, 0x7238, 0xba0a, 0x733c,
+ 0xbb0e, 0xbc00, 0xc48d, 0xbc02, 0xa804, 0x9005, 0x1128, 0x00fe,
+ 0x009e, 0x00be, 0x0804, 0xa4e7, 0x0096, 0x2048, 0xaa12, 0xab16,
+ 0xac0a, 0x009e, 0x8006, 0x8006, 0x8007, 0x90bc, 0x003f, 0x9084,
+ 0xffc0, 0x9080, 0x0002, 0x2009, 0x002b, 0xaaa0, 0xab9c, 0xaca8,
+ 0xada4, 0x2031, 0x0000, 0x2041, 0x1288, 0x080c, 0xa5e6, 0x0130,
+ 0x00fe, 0x009e, 0x080c, 0xa0e3, 0x00be, 0x0005, 0x080c, 0xaa81,
+ 0x0cb8, 0x2b78, 0x00f6, 0x080c, 0x318b, 0x080c, 0xc54e, 0x00fe,
+ 0x00c6, 0x080c, 0xa08d, 0x2f00, 0x6012, 0x6017, 0x0000, 0x6023,
+ 0x0001, 0x6007, 0x0001, 0x6003, 0x0001, 0x2001, 0x0007, 0x080c,
+ 0x63f0, 0x080c, 0x641c, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x00ce,
+ 0x0804, 0xa82c, 0x2100, 0x91b2, 0x0053, 0x1a0c, 0x0dfa, 0x91b2,
+ 0x0040, 0x1a04, 0xa8e2, 0x0002, 0xa8d0, 0xa8d0, 0xa8c6, 0xa8d0,
+ 0xa8d0, 0xa8d0, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4,
+ 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4,
+ 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4,
+ 0xa8c4, 0xa8c4, 0xa8c4, 0xa8d0, 0xa8c4, 0xa8d0, 0xa8d0, 0xa8c4,
+ 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c6, 0xa8c4, 0xa8c4, 0xa8c4,
+ 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8d0, 0xa8d0,
+ 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4, 0xa8c4,
+ 0xa8c4, 0xa8d0, 0xa8c4, 0xa8c4, 0x080c, 0x0dfa, 0x0066, 0x00b6,
+ 0x6610, 0x2658, 0xb8bc, 0xc08c, 0xb8be, 0x00be, 0x006e, 0x0000,
+ 0x6003, 0x0001, 0x6106, 0x9186, 0x0032, 0x0118, 0x080c, 0x86c1,
+ 0x0010, 0x080c, 0x8679, 0x0126, 0x2091, 0x8000, 0x080c, 0x8c10,
+ 0x012e, 0x0005, 0x2600, 0x0002, 0xa8f6, 0xa8f6, 0xa8f6, 0xa8d0,
+ 0xa8d0, 0xa8f6, 0xa8f6, 0xa8f6, 0xa8f6, 0xa8d0, 0xa8f6, 0xa8d0,
+ 0xa8f6, 0xa8d0, 0xa8f6, 0xa8f6, 0xa8f6, 0xa8f6, 0x080c, 0x0dfa,
+ 0x6004, 0x90b2, 0x0053, 0x1a0c, 0x0dfa, 0x91b6, 0x0013, 0x0904,
+ 0xa9ba, 0x91b6, 0x0027, 0x1904, 0xa975, 0x080c, 0x8b04, 0x6004,
+ 0x080c, 0xc02e, 0x01b0, 0x080c, 0xc03f, 0x01a8, 0x908e, 0x0021,
+ 0x0904, 0xa972, 0x908e, 0x0022, 0x1130, 0x080c, 0xa513, 0x0904,
+ 0xa96e, 0x0804, 0xa96f, 0x908e, 0x003d, 0x0904, 0xa972, 0x0804,
+ 0xa968, 0x080c, 0x31b4, 0x2001, 0x0007, 0x080c, 0x63f0, 0x6010,
+ 0x00b6, 0x2058, 0xb9a0, 0x00be, 0x080c, 0xaa81, 0x9186, 0x007e,
+ 0x1148, 0x2001, 0x1836, 0x2014, 0xc285, 0x080c, 0x7207, 0x1108,
+ 0xc2ad, 0x2202, 0x0036, 0x0026, 0x2019, 0x0028, 0x2110, 0x080c,
+ 0xdb3d, 0x002e, 0x003e, 0x0016, 0x0026, 0x0036, 0x2110, 0x2019,
+ 0x0028, 0x080c, 0x8803, 0x0076, 0x903e, 0x080c, 0x86f1, 0x6010,
+ 0x00b6, 0x905d, 0x0100, 0x00be, 0x2c08, 0x080c, 0xd5f6, 0x007e,
+ 0x003e, 0x002e, 0x001e, 0x080c, 0xc54e, 0x0016, 0x080c, 0xc2ab,
+ 0x080c, 0xa0e3, 0x001e, 0x080c, 0x3286, 0x080c, 0x8c10, 0x0030,
+ 0x080c, 0xc2ab, 0x080c, 0xa0e3, 0x080c, 0x8c10, 0x0005, 0x080c,
+ 0xaa81, 0x0cb0, 0x080c, 0xaabd, 0x0c98, 0x9186, 0x0014, 0x1db0,
+ 0x080c, 0x8b04, 0x6004, 0x908e, 0x0022, 0x1118, 0x080c, 0xa513,
+ 0x0d68, 0x080c, 0x318b, 0x080c, 0xc54e, 0x080c, 0xc02e, 0x1190,
+ 0x080c, 0x31b4, 0x6010, 0x00b6, 0x2058, 0xb9a0, 0x00be, 0x080c,
+ 0xaa81, 0x9186, 0x007e, 0x1128, 0x2001, 0x1836, 0x200c, 0xc185,
+ 0x2102, 0x0870, 0x080c, 0xc03f, 0x1118, 0x080c, 0xaa81, 0x0840,
+ 0x6004, 0x908e, 0x0032, 0x1160, 0x00e6, 0x00f6, 0x2071, 0x189c,
+ 0x2079, 0x0000, 0x080c, 0x351a, 0x00fe, 0x00ee, 0x0804, 0xa968,
+ 0x6004, 0x908e, 0x0021, 0x0d48, 0x908e, 0x0022, 0x090c, 0xaa81,
+ 0x0804, 0xa968, 0x90b2, 0x0040, 0x1a04, 0xaa6a, 0x2008, 0x0002,
+ 0xaa02, 0xaa03, 0xaa06, 0xaa09, 0xaa0c, 0xaa0f, 0xaa00, 0xaa00,
+ 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00,
+ 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00,
+ 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa12, 0xaa1f,
+ 0xaa00, 0xaa21, 0xaa1f, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00,
+ 0xaa1f, 0xaa1f, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xaa00,
+ 0xaa00, 0xaa00, 0xaa51, 0xaa1f, 0xaa00, 0xaa1b, 0xaa00, 0xaa00,
+ 0xaa00, 0xaa1c, 0xaa00, 0xaa00, 0xaa00, 0xaa1f, 0xaa48, 0xaa00,
+ 0x080c, 0x0dfa, 0x00e0, 0x2001, 0x000b, 0x0420, 0x2001, 0x0003,
+ 0x0408, 0x2001, 0x0005, 0x00f0, 0x2001, 0x0001, 0x00d8, 0x2001,
+ 0x0009, 0x00c0, 0x080c, 0x8b04, 0x6003, 0x0005, 0x080c, 0xc551,
+ 0x080c, 0x8c10, 0x0070, 0x0018, 0x0010, 0x080c, 0x63f0, 0x0804,
+ 0xaa62, 0x080c, 0x8b04, 0x080c, 0xc551, 0x6003, 0x0004, 0x080c,
+ 0x8c10, 0x0005, 0x080c, 0x63f0, 0x080c, 0x8b04, 0x6003, 0x0002,
+ 0x0036, 0x2019, 0x1866, 0x2304, 0x9084, 0xff00, 0x1120, 0x2001,
+ 0x195e, 0x201c, 0x0040, 0x8007, 0x909a, 0x0004, 0x0ec0, 0x8003,
+ 0x801b, 0x831b, 0x9318, 0x631a, 0x003e, 0x080c, 0x8c10, 0x0c08,
+ 0x080c, 0x8b04, 0x080c, 0xc2ab, 0x080c, 0xa0e3, 0x080c, 0x8c10,
+ 0x08c0, 0x00e6, 0x00f6, 0x2071, 0x189c, 0x2079, 0x0000, 0x080c,
+ 0x351a, 0x00fe, 0x00ee, 0x080c, 0x8b04, 0x080c, 0xa0e3, 0x080c,
+ 0x8c10, 0x0838, 0x080c, 0x8b04, 0x6003, 0x0002, 0x080c, 0xc551,
+ 0x0804, 0x8c10, 0x2600, 0x2008, 0x0002, 0xaa7f, 0xaa7f, 0xaa7f,
+ 0xaa62, 0xaa62, 0xaa7f, 0xaa7f, 0xaa7f, 0xaa7f, 0xaa62, 0xaa7f,
+ 0xaa62, 0xaa7f, 0xaa62, 0xaa7f, 0xaa7f, 0xaa7f, 0xaa7f, 0x080c,
+ 0x0dfa, 0x00e6, 0x0096, 0x0026, 0x0016, 0x080c, 0xbe37, 0x0568,
+ 0x6014, 0x2048, 0xa864, 0x9086, 0x0139, 0x11a8, 0xa894, 0x9086,
+ 0x0056, 0x1148, 0x080c, 0x5375, 0x0130, 0x2001, 0x0000, 0x900e,
+ 0x2011, 0x4000, 0x0028, 0x2001, 0x0030, 0x900e, 0x2011, 0x4005,
+ 0x080c, 0xc418, 0x0090, 0xa868, 0xd0fc, 0x0178, 0xa807, 0x0000,
+ 0x0016, 0x6004, 0x908e, 0x0021, 0x0168, 0x908e, 0x003d, 0x0150,
+ 0x001e, 0xa867, 0x0103, 0xa833, 0x0100, 0x001e, 0x002e, 0x009e,
+ 0x00ee, 0x0005, 0x001e, 0x0009, 0x0cc0, 0x0096, 0x6014, 0x2048,
+ 0xa800, 0x2048, 0xa867, 0x0103, 0xa823, 0x8001, 0x009e, 0x0005,
+ 0x00b6, 0x6610, 0x2658, 0xb804, 0x9084, 0x00ff, 0x90b2, 0x000c,
+ 0x1a0c, 0x0dfa, 0x6604, 0x96b6, 0x004d, 0x1120, 0x080c, 0xc337,
+ 0x0804, 0xab45, 0x6604, 0x96b6, 0x0043, 0x1120, 0x080c, 0xc380,
+ 0x0804, 0xab45, 0x6604, 0x96b6, 0x004b, 0x1120, 0x080c, 0xc3ac,
+ 0x0804, 0xab45, 0x6604, 0x96b6, 0x0033, 0x1120, 0x080c, 0xc2cd,
+ 0x0804, 0xab45, 0x6604, 0x96b6, 0x0028, 0x1120, 0x080c, 0xc07d,
+ 0x0804, 0xab45, 0x6604, 0x96b6, 0x0029, 0x1120, 0x080c, 0xc0be,
+ 0x0804, 0xab45, 0x6604, 0x96b6, 0x001f, 0x1118, 0x080c, 0xa4bb,
+ 0x04e0, 0x6604, 0x96b6, 0x0000, 0x1118, 0x080c, 0xa7f0, 0x04a8,
+ 0x6604, 0x96b6, 0x0022, 0x1118, 0x080c, 0xa4f4, 0x0470, 0x6604,
+ 0x96b6, 0x0035, 0x1118, 0x080c, 0xa604, 0x0438, 0x6604, 0x96b6,
+ 0x0039, 0x1118, 0x080c, 0xa785, 0x0400, 0x6604, 0x96b6, 0x003d,
+ 0x1118, 0x080c, 0xa52c, 0x00c8, 0x6604, 0x96b6, 0x0044, 0x1118,
+ 0x080c, 0xa568, 0x0090, 0x6604, 0x96b6, 0x0049, 0x1118, 0x080c,
+ 0xa593, 0x0058, 0x91b6, 0x0015, 0x1110, 0x0063, 0x0030, 0x91b6,
+ 0x0016, 0x1128, 0x00be, 0x0804, 0xae05, 0x00be, 0x0005, 0x080c,
+ 0xa178, 0x0cd8, 0xab62, 0xab65, 0xab62, 0xaba9, 0xab62, 0xad39,
+ 0xae12, 0xab62, 0xab62, 0xaddf, 0xab62, 0xadf3, 0x0096, 0x080c,
+ 0x1582, 0x6014, 0x2048, 0xa800, 0x2048, 0xa867, 0x0103, 0x009e,
+ 0x0804, 0xa0e3, 0xa001, 0xa001, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x708c, 0x9086, 0x0074, 0x1540, 0x080c, 0xd5c7, 0x11b0, 0x6010,
+ 0x00b6, 0x2058, 0x7030, 0xd08c, 0x0128, 0xb800, 0xd0bc, 0x0110,
+ 0xc0c5, 0xb802, 0x00e9, 0x00be, 0x2001, 0x0006, 0x080c, 0x63f0,
+ 0x080c, 0x31b4, 0x080c, 0xa0e3, 0x0088, 0x2001, 0x000a, 0x080c,
+ 0x63f0, 0x080c, 0x31b4, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c,
+ 0x86c1, 0x080c, 0x8c10, 0x0010, 0x080c, 0xad24, 0x00ee, 0x0005,
+ 0x00d6, 0xb800, 0xd084, 0x0158, 0x9006, 0x080c, 0x63dc, 0x2069,
+ 0x185b, 0x6804, 0x0020, 0x2001, 0x0006, 0x080c, 0x641c, 0x00de,
+ 0x0005, 0x00b6, 0x0096, 0x00d6, 0x2011, 0x1823, 0x2204, 0x9086,
+ 0x0074, 0x1904, 0xacfb, 0x6010, 0x2058, 0xbaa0, 0x9286, 0x007e,
+ 0x1120, 0x080c, 0xaf56, 0x0804, 0xac60, 0x00d6, 0x080c, 0x7207,
+ 0x0198, 0x0026, 0x2011, 0x0010, 0x080c, 0x67e7, 0x002e, 0x05c8,
+ 0x080c, 0x55ef, 0x1540, 0x6014, 0x2048, 0xa807, 0x0000, 0xa867,
+ 0x0103, 0xa833, 0xdead, 0x00f8, 0x0026, 0x2011, 0x8008, 0x080c,
+ 0x67e7, 0x002e, 0x0530, 0x6014, 0x2048, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0039, 0x1140, 0x2001, 0x0030, 0x900e, 0x2011, 0x4009,
+ 0x080c, 0xc418, 0x0040, 0x6014, 0x2048, 0xa807, 0x0000, 0xa867,
+ 0x0103, 0xa833, 0xdead, 0x6010, 0x2058, 0xb9a0, 0x0016, 0x080c,
+ 0x31b4, 0x080c, 0xa0e3, 0x001e, 0x080c, 0x3286, 0x00de, 0x0804,
+ 0xacfe, 0x00de, 0x080c, 0xaf4b, 0x6010, 0x2058, 0xbaa0, 0x9286,
+ 0x0080, 0x1510, 0x6014, 0x9005, 0x01a8, 0x2048, 0xa864, 0x9084,
+ 0x00ff, 0x9086, 0x0039, 0x1140, 0x2001, 0x0000, 0x900e, 0x2011,
+ 0x4000, 0x080c, 0xc418, 0x0030, 0xa807, 0x0000, 0xa867, 0x0103,
+ 0xa833, 0x0200, 0x2001, 0x0006, 0x080c, 0x63f0, 0x080c, 0x31b4,
+ 0x080c, 0xa0e3, 0x0804, 0xacfe, 0x080c, 0xad0c, 0x6014, 0x9005,
+ 0x0190, 0x2048, 0xa868, 0xd0f4, 0x01e8, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0039, 0x1d08, 0x2001, 0x0000, 0x900e, 0x2011, 0x4000,
+ 0x080c, 0xc418, 0x08f8, 0x080c, 0xad02, 0x0160, 0x9006, 0x080c,
+ 0x63dc, 0x2001, 0x0004, 0x080c, 0x641c, 0x2001, 0x0007, 0x080c,
+ 0x63f0, 0x08a0, 0x2001, 0x0004, 0x080c, 0x63f0, 0x6003, 0x0001,
+ 0x6007, 0x0003, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0804, 0xacfe,
+ 0xb85c, 0xd0e4, 0x01d0, 0x080c, 0xc24d, 0x080c, 0x7207, 0x0118,
+ 0xd0dc, 0x1904, 0xac22, 0x2011, 0x1836, 0x2204, 0xc0ad, 0x2012,
+ 0x2001, 0x0002, 0x00f6, 0x2079, 0x0100, 0x78e3, 0x0000, 0x080c,
+ 0x27e2, 0x78e2, 0x00fe, 0x0804, 0xac22, 0x080c, 0xc28a, 0x2011,
+ 0x1836, 0x2204, 0xc0a5, 0x2012, 0x0006, 0x080c, 0xd720, 0x000e,
+ 0x1904, 0xac22, 0xc0b5, 0x2012, 0x2001, 0x0006, 0x080c, 0x63f0,
+ 0x9006, 0x080c, 0x63dc, 0x00c6, 0x2001, 0x180f, 0x2004, 0xd09c,
+ 0x0520, 0x00f6, 0x2079, 0x0100, 0x00e6, 0x2071, 0x1800, 0x700c,
+ 0x9084, 0x00ff, 0x78e6, 0x707a, 0x7010, 0x78ea, 0x707e, 0x908c,
+ 0x00ff, 0x00ee, 0x780c, 0xc0b5, 0x780e, 0x00fe, 0x080c, 0x27b7,
+ 0x00f6, 0x2100, 0x900e, 0x080c, 0x276e, 0x795a, 0x00fe, 0x9186,
+ 0x0081, 0x01d8, 0x2009, 0x0081, 0x00c8, 0x2009, 0x00ef, 0x00f6,
+ 0x2079, 0x0100, 0x79ea, 0x7932, 0x7936, 0x780c, 0xc0b5, 0x780e,
+ 0x00fe, 0x080c, 0x27b7, 0x00f6, 0x2079, 0x1800, 0x797e, 0x2100,
+ 0x900e, 0x080c, 0x276e, 0x795a, 0x00fe, 0x8108, 0x080c, 0x643f,
+ 0x2b00, 0x00ce, 0x1904, 0xac22, 0x6012, 0x2009, 0x180f, 0x210c,
+ 0xd19c, 0x0150, 0x2009, 0x027c, 0x210c, 0x918c, 0x00ff, 0xb912,
+ 0x2009, 0x027d, 0x210c, 0xb916, 0x2001, 0x0002, 0x080c, 0x63f0,
+ 0x6023, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x86c1,
+ 0x080c, 0x8c10, 0x0018, 0x080c, 0xaa81, 0x0431, 0x00de, 0x009e,
+ 0x00be, 0x0005, 0x2001, 0x1810, 0x2004, 0xd0a4, 0x0120, 0x2001,
+ 0x185c, 0x2004, 0xd0ac, 0x0005, 0x00e6, 0x080c, 0xdb96, 0x0190,
+ 0x2071, 0x0260, 0x7108, 0x720c, 0x918c, 0x00ff, 0x1118, 0x9284,
+ 0xff00, 0x0140, 0x6010, 0x2058, 0xb8a0, 0x9084, 0xff80, 0x1110,
+ 0xb912, 0xba16, 0x00ee, 0x0005, 0x2030, 0x2001, 0x0007, 0x080c,
+ 0x63f0, 0x080c, 0x55ef, 0x1120, 0x2001, 0x0007, 0x080c, 0x641c,
+ 0x080c, 0x31b4, 0x6020, 0x9086, 0x000a, 0x1108, 0x0005, 0x0804,
+ 0xa0e3, 0x00b6, 0x00e6, 0x0026, 0x0016, 0x2071, 0x1800, 0x708c,
+ 0x9086, 0x0014, 0x1904, 0xadd6, 0x00d6, 0x080c, 0x7207, 0x0198,
+ 0x0026, 0x2011, 0x0010, 0x080c, 0x67e7, 0x002e, 0x05c8, 0x080c,
+ 0x55ef, 0x1540, 0x6014, 0x2048, 0xa807, 0x0000, 0xa867, 0x0103,
+ 0xa833, 0xdead, 0x00f8, 0x0026, 0x2011, 0x8008, 0x080c, 0x67e7,
+ 0x002e, 0x0530, 0x6014, 0x2048, 0xa864, 0x9084, 0x00ff, 0x9086,
+ 0x0039, 0x1140, 0x2001, 0x0030, 0x900e, 0x2011, 0x4009, 0x080c,
+ 0xc418, 0x0040, 0x6014, 0x2048, 0xa807, 0x0000, 0xa867, 0x0103,
+ 0xa833, 0xdead, 0x6010, 0x2058, 0xb9a0, 0x0016, 0x080c, 0x31b4,
+ 0x080c, 0xa0e3, 0x001e, 0x080c, 0x3286, 0x00de, 0x0804, 0xadda,
+ 0x00de, 0x080c, 0x55ef, 0x1170, 0x6014, 0x9005, 0x1158, 0x0036,
+ 0x0046, 0x6010, 0x2058, 0xbba0, 0x2021, 0x0006, 0x080c, 0x4cbc,
+ 0x004e, 0x003e, 0x00d6, 0x6010, 0x2058, 0x080c, 0x653a, 0x080c,
+ 0xab98, 0x00de, 0x080c, 0xb01c, 0x1588, 0x6010, 0x2058, 0xb890,
+ 0x9005, 0x0560, 0x2001, 0x0006, 0x080c, 0x63f0, 0x0096, 0x6014,
+ 0x904d, 0x01d0, 0xa864, 0x9084, 0x00ff, 0x9086, 0x0039, 0x1140,
+ 0x2001, 0x0000, 0x900e, 0x2011, 0x4000, 0x080c, 0xc418, 0x0060,
+ 0xa864, 0x9084, 0x00ff, 0x9086, 0x0029, 0x0130, 0xa807, 0x0000,
+ 0xa867, 0x0103, 0xa833, 0x0200, 0x009e, 0x080c, 0x31b4, 0x6020,
+ 0x9086, 0x000a, 0x0138, 0x080c, 0xa0e3, 0x0020, 0x080c, 0xaa81,
+ 0x080c, 0xad24, 0x001e, 0x002e, 0x00ee, 0x00be, 0x0005, 0x2011,
+ 0x1823, 0x2204, 0x9086, 0x0014, 0x1160, 0x2001, 0x0002, 0x080c,
+ 0x63f0, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x86c1, 0x0804,
+ 0x8c10, 0x0804, 0xad24, 0x2030, 0x2011, 0x1823, 0x2204, 0x9086,
+ 0x0004, 0x1148, 0x96b6, 0x000b, 0x1120, 0x2001, 0x0007, 0x080c,
+ 0x63f0, 0x0804, 0xa0e3, 0x0804, 0xad24, 0x0002, 0xab62, 0xae1d,
+ 0xab62, 0xae5c, 0xab62, 0xaf07, 0xae12, 0xab62, 0xab62, 0xaf1a,
+ 0xab62, 0xaf2a, 0x6604, 0x9686, 0x0003, 0x0904, 0xad39, 0x96b6,
+ 0x001e, 0x1110, 0x080c, 0xa0e3, 0x0005, 0x00b6, 0x00d6, 0x00c6,
+ 0x080c, 0xaf3a, 0x11a0, 0x9006, 0x080c, 0x63dc, 0x080c, 0x318b,
+ 0x080c, 0xc54e, 0x2001, 0x0002, 0x080c, 0x63f0, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0408, 0x2009,
+ 0x026e, 0x2104, 0x9086, 0x0009, 0x1160, 0x6010, 0x2058, 0xb840,
+ 0x9084, 0x00ff, 0x9005, 0x0170, 0x8001, 0xb842, 0x601b, 0x000a,
+ 0x0078, 0x2009, 0x026f, 0x2104, 0x9084, 0xff00, 0x9086, 0x1900,
+ 0x1108, 0x08a0, 0x080c, 0x318b, 0x080c, 0xc54e, 0x080c, 0xad24,
+ 0x00ce, 0x00de, 0x00be, 0x0005, 0x0096, 0x00b6, 0x0026, 0x9016,
+ 0x080c, 0xaf48, 0x00d6, 0x2069, 0x1954, 0x2d04, 0x9005, 0x0168,
+ 0x6010, 0x2058, 0xb8a0, 0x9086, 0x007e, 0x1138, 0x2069, 0x181f,
+ 0x2d04, 0x8000, 0x206a, 0x00de, 0x0010, 0x00de, 0x0088, 0x9006,
+ 0x080c, 0x63dc, 0x2001, 0x0002, 0x080c, 0x63f0, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0804, 0xaed7,
+ 0x080c, 0xbe37, 0x01b0, 0x6014, 0x2048, 0xa864, 0x2010, 0x9086,
+ 0x0139, 0x1138, 0x6007, 0x0016, 0x2001, 0x0002, 0x080c, 0xc472,
+ 0x00b0, 0x6014, 0x2048, 0xa864, 0xd0fc, 0x0118, 0x2001, 0x0001,
+ 0x0ca8, 0x2001, 0x180e, 0x2004, 0xd0dc, 0x0148, 0x6010, 0x2058,
+ 0xb840, 0x9084, 0x00ff, 0x9005, 0x1110, 0x9006, 0x0c38, 0x080c,
+ 0xaa81, 0x2009, 0x026e, 0x2134, 0x96b4, 0x00ff, 0x9686, 0x0005,
+ 0x0510, 0x9686, 0x000b, 0x01c8, 0x2009, 0x026f, 0x2104, 0x9084,
+ 0xff00, 0x1118, 0x9686, 0x0009, 0x01b0, 0x9086, 0x1900, 0x1168,
+ 0x9686, 0x0009, 0x0180, 0x2001, 0x0004, 0x080c, 0x63f0, 0x2001,
+ 0x0028, 0x601a, 0x6007, 0x0052, 0x0010, 0x080c, 0xad24, 0x002e,
+ 0x00be, 0x009e, 0x0005, 0x9286, 0x0139, 0x0160, 0x6014, 0x2048,
+ 0x080c, 0xbe37, 0x0140, 0xa864, 0x9086, 0x0139, 0x0118, 0xa868,
+ 0xd0fc, 0x0108, 0x0c50, 0x6010, 0x2058, 0xb840, 0x9084, 0x00ff,
+ 0x9005, 0x0138, 0x8001, 0xb842, 0x601b, 0x000a, 0x6007, 0x0016,
+ 0x08f0, 0xb8a0, 0x9086, 0x007e, 0x1138, 0x00e6, 0x2071, 0x1800,
+ 0x080c, 0x5ebe, 0x00ee, 0x0010, 0x080c, 0x318b, 0x0870, 0x080c,
+ 0xaf48, 0x1160, 0x2001, 0x0004, 0x080c, 0x63f0, 0x6003, 0x0001,
+ 0x6007, 0x0003, 0x080c, 0x86c1, 0x0804, 0x8c10, 0x080c, 0xaa81,
+ 0x0804, 0xad24, 0x0469, 0x1160, 0x2001, 0x0008, 0x080c, 0x63f0,
+ 0x6003, 0x0001, 0x6007, 0x0005, 0x080c, 0x86c1, 0x0804, 0x8c10,
+ 0x0804, 0xad24, 0x00e9, 0x1160, 0x2001, 0x000a, 0x080c, 0x63f0,
+ 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x86c1, 0x0804, 0x8c10,
+ 0x0804, 0xad24, 0x2009, 0x026e, 0x2104, 0x9086, 0x0003, 0x1138,
+ 0x2009, 0x026f, 0x2104, 0x9084, 0xff00, 0x9086, 0x2a00, 0x0005,
+ 0x9085, 0x0001, 0x0005, 0x00b6, 0x00c6, 0x0016, 0x6110, 0x2158,
+ 0x080c, 0x64ae, 0x001e, 0x00ce, 0x00be, 0x0005, 0x00b6, 0x00f6,
+ 0x00e6, 0x00d6, 0x0036, 0x0016, 0x6010, 0x2058, 0x2009, 0x1836,
+ 0x2104, 0x9085, 0x0003, 0x200a, 0x080c, 0xafee, 0x0560, 0x2009,
+ 0x1836, 0x2104, 0xc0cd, 0x200a, 0x080c, 0x67bf, 0x0158, 0x9006,
+ 0x2020, 0x2009, 0x002a, 0x080c, 0xd885, 0x2001, 0x180c, 0x200c,
+ 0xc195, 0x2102, 0x2019, 0x002a, 0x2009, 0x0001, 0x080c, 0x3156,
+ 0x00e6, 0x2071, 0x1800, 0x080c, 0x2f6c, 0x00ee, 0x00c6, 0x0156,
+ 0x20a9, 0x0781, 0x2009, 0x007f, 0x080c, 0x3286, 0x8108, 0x1f04,
+ 0xaf8c, 0x015e, 0x00ce, 0x080c, 0xaf4b, 0x2071, 0x0260, 0x2079,
+ 0x0200, 0x7817, 0x0001, 0x2001, 0x1836, 0x200c, 0xc1c5, 0x7018,
+ 0xd0fc, 0x0110, 0xd0dc, 0x0118, 0x7038, 0xd0dc, 0x1108, 0xc1c4,
+ 0x7817, 0x0000, 0x2001, 0x1836, 0x2102, 0x2079, 0x0100, 0x2e04,
+ 0x9084, 0x00ff, 0x2069, 0x181e, 0x206a, 0x78e6, 0x0006, 0x8e70,
+ 0x2e04, 0x2069, 0x181f, 0x206a, 0x78ea, 0x7832, 0x7836, 0x2010,
+ 0x9084, 0xff00, 0x001e, 0x9105, 0x2009, 0x182b, 0x200a, 0x2200,
+ 0x9084, 0x00ff, 0x2008, 0x080c, 0x27b7, 0x080c, 0x7207, 0x0170,
+ 0x2071, 0x0260, 0x2069, 0x195a, 0x7048, 0x206a, 0x704c, 0x6806,
+ 0x7050, 0x680a, 0x7054, 0x680e, 0x080c, 0xc24d, 0x0040, 0x2001,
+ 0x0006, 0x080c, 0x63f0, 0x080c, 0x31b4, 0x080c, 0xa0e3, 0x001e,
+ 0x003e, 0x00de, 0x00ee, 0x00fe, 0x00be, 0x0005, 0x0096, 0x0026,
+ 0x0036, 0x00e6, 0x0156, 0x2019, 0x182b, 0x231c, 0x83ff, 0x01f0,
+ 0x2071, 0x0260, 0x7200, 0x9294, 0x00ff, 0x7004, 0x9084, 0xff00,
+ 0x9205, 0x9306, 0x1198, 0x2011, 0x0276, 0x20a9, 0x0004, 0x2b48,
+ 0x2019, 0x000a, 0x080c, 0xb0d0, 0x1148, 0x2011, 0x027a, 0x20a9,
+ 0x0004, 0x2019, 0x0006, 0x080c, 0xb0d0, 0x1100, 0x015e, 0x00ee,
+ 0x003e, 0x002e, 0x009e, 0x0005, 0x00e6, 0x2071, 0x0260, 0x7034,
+ 0x9086, 0x0014, 0x11a8, 0x7038, 0x9086, 0x0800, 0x1188, 0x703c,
+ 0xd0ec, 0x0160, 0x9084, 0x0f00, 0x9086, 0x0100, 0x1138, 0x7054,
+ 0xd0a4, 0x1110, 0xd0ac, 0x0110, 0x9006, 0x0010, 0x9085, 0x0001,
+ 0x00ee, 0x0005, 0x00e6, 0x0096, 0x00c6, 0x0076, 0x0056, 0x0046,
+ 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2029, 0x19c8, 0x252c,
+ 0x2021, 0x19ce, 0x2424, 0x2061, 0x1cd0, 0x2071, 0x1800, 0x7250,
+ 0x7070, 0x9202, 0x1a04, 0xb0a8, 0x080c, 0xd8b6, 0x0904, 0xb0a1,
+ 0x6720, 0x9786, 0x0007, 0x0904, 0xb0a1, 0x2500, 0x9c06, 0x0904,
+ 0xb0a1, 0x2400, 0x9c06, 0x05e8, 0x3e08, 0x9186, 0x0002, 0x1148,
+ 0x6010, 0x9005, 0x0130, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc,
+ 0x1580, 0x00c6, 0x6000, 0x9086, 0x0004, 0x1110, 0x080c, 0x19b4,
+ 0x9786, 0x000a, 0x0148, 0x080c, 0xc03f, 0x1130, 0x00ce, 0x080c,
+ 0xaa81, 0x080c, 0xa113, 0x00e8, 0x6014, 0x2048, 0x080c, 0xbe37,
+ 0x01a8, 0x9786, 0x0003, 0x1530, 0xa867, 0x0103, 0xa87c, 0xd0cc,
+ 0x0130, 0x0096, 0xa878, 0x2048, 0x080c, 0x0fe3, 0x009e, 0xab7a,
+ 0xa877, 0x0000, 0x080c, 0x6adc, 0x080c, 0xc022, 0x080c, 0xa113,
+ 0x00ce, 0x9ce0, 0x0018, 0x7064, 0x9c02, 0x1210, 0x0804, 0xb04f,
+ 0x012e, 0x000e, 0x002e, 0x004e, 0x005e, 0x007e, 0x00ce, 0x009e,
+ 0x00ee, 0x0005, 0x9786, 0x0006, 0x1118, 0x080c, 0xd830, 0x0c30,
+ 0x9786, 0x000a, 0x09e0, 0x0880, 0x220c, 0x2304, 0x9106, 0x1130,
+ 0x8210, 0x8318, 0x1f04, 0xb0bc, 0x9006, 0x0005, 0x2304, 0x9102,
+ 0x0218, 0x2001, 0x0001, 0x0008, 0x9006, 0x918d, 0x0001, 0x0005,
+ 0x0136, 0x01c6, 0x0016, 0x8906, 0x8006, 0x8007, 0x908c, 0x003f,
+ 0x21e0, 0x9084, 0xffc0, 0x9300, 0x2098, 0x3518, 0x20a9, 0x0001,
+ 0x220c, 0x4002, 0x910e, 0x1140, 0x8210, 0x8319, 0x1dc8, 0x9006,
+ 0x001e, 0x01ce, 0x013e, 0x0005, 0x220c, 0x9102, 0x0218, 0x2001,
+ 0x0001, 0x0010, 0x2001, 0x0000, 0x918d, 0x0001, 0x001e, 0x01ce,
+ 0x013e, 0x0005, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0dfa, 0x080c,
+ 0xc02e, 0x0120, 0x080c, 0xc03f, 0x0168, 0x0028, 0x080c, 0x31b4,
+ 0x080c, 0xc03f, 0x0138, 0x080c, 0x8b04, 0x080c, 0xa0e3, 0x080c,
+ 0x8c10, 0x0005, 0x080c, 0xaa81, 0x0cb0, 0x9182, 0x0054, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xb131, 0xb131, 0xb131,
+ 0xb131, 0xb131, 0xb131, 0xb131, 0xb131, 0xb131, 0xb131, 0xb131,
+ 0xb133, 0xb133, 0xb133, 0xb133, 0xb131, 0xb131, 0xb131, 0xb133,
+ 0xb131, 0x080c, 0x0dfa, 0x600b, 0xffff, 0x6003, 0x0001, 0x6106,
+ 0x080c, 0x8679, 0x0126, 0x2091, 0x8000, 0x080c, 0x8c10, 0x012e,
+ 0x0005, 0x9186, 0x0013, 0x1128, 0x6004, 0x9082, 0x0040, 0x0804,
+ 0xb1e8, 0x9186, 0x0027, 0x1520, 0x080c, 0x8b04, 0x080c, 0x318b,
+ 0x080c, 0xc54e, 0x0096, 0x6114, 0x2148, 0x080c, 0xbe37, 0x0198,
+ 0x080c, 0xc03f, 0x1118, 0x080c, 0xaa81, 0x0068, 0xa867, 0x0103,
+ 0xa87b, 0x0029, 0xa877, 0x0000, 0xa97c, 0xc1c5, 0xa97e, 0x080c,
+ 0x6ae9, 0x080c, 0xc022, 0x009e, 0x080c, 0xa0e3, 0x0804, 0x8c10,
+ 0x9186, 0x0014, 0x1120, 0x6004, 0x9082, 0x0040, 0x04a0, 0x9186,
+ 0x0046, 0x0150, 0x9186, 0x0045, 0x0138, 0x9186, 0x0053, 0x0120,
+ 0x9186, 0x0048, 0x190c, 0x0dfa, 0x2001, 0x0109, 0x2004, 0xd084,
+ 0x0508, 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x0036,
+ 0x00f6, 0x00e6, 0x00c6, 0x2079, 0x19bf, 0x2071, 0x1800, 0x2061,
+ 0x0100, 0x080c, 0x8563, 0x00ce, 0x00ee, 0x00fe, 0x003e, 0x002e,
+ 0x001e, 0x000e, 0x012e, 0xa001, 0x6000, 0x9086, 0x0002, 0x1110,
+ 0x0804, 0xb226, 0x0005, 0x0002, 0xb1c2, 0xb1c0, 0xb1c0, 0xb1c0,
+ 0xb1c0, 0xb1c0, 0xb1c0, 0xb1c0, 0xb1c0, 0xb1c0, 0xb1c0, 0xb1dd,
+ 0xb1dd, 0xb1dd, 0xb1dd, 0xb1c0, 0xb1dd, 0xb1c0, 0xb1dd, 0xb1c0,
+ 0x080c, 0x0dfa, 0x080c, 0x8b04, 0x0096, 0x6114, 0x2148, 0x080c,
+ 0xbe37, 0x0168, 0xa867, 0x0103, 0xa87b, 0x0006, 0xa877, 0x0000,
+ 0xa880, 0xc0ec, 0xa882, 0x080c, 0x6ae9, 0x080c, 0xc022, 0x009e,
+ 0x080c, 0xa0e3, 0x080c, 0x8c10, 0x0005, 0x080c, 0x8b04, 0x080c,
+ 0xc03f, 0x090c, 0xaa81, 0x080c, 0xa0e3, 0x080c, 0x8c10, 0x0005,
+ 0x0002, 0xb1ff, 0xb1fd, 0xb1fd, 0xb1fd, 0xb1fd, 0xb1fd, 0xb1fd,
+ 0xb1fd, 0xb1fd, 0xb1fd, 0xb1fd, 0xb216, 0xb216, 0xb216, 0xb216,
+ 0xb1fd, 0xb220, 0xb1fd, 0xb216, 0xb1fd, 0x080c, 0x0dfa, 0x0096,
+ 0x080c, 0x8b04, 0x6014, 0x2048, 0x2001, 0x1960, 0x2004, 0x6042,
+ 0xa97c, 0xd1ac, 0x0140, 0x6003, 0x0004, 0xa87c, 0x9085, 0x0400,
+ 0xa87e, 0x009e, 0x0005, 0x6003, 0x0002, 0x0cb8, 0x080c, 0x8b04,
+ 0x080c, 0xc551, 0x080c, 0xc556, 0x6003, 0x000f, 0x0804, 0x8c10,
+ 0x080c, 0x8b04, 0x080c, 0xa0e3, 0x0804, 0x8c10, 0x9182, 0x0054,
+ 0x1220, 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xb242, 0xb242,
+ 0xb242, 0xb242, 0xb242, 0xb244, 0xb321, 0xb242, 0xb355, 0xb242,
+ 0xb242, 0xb242, 0xb242, 0xb242, 0xb242, 0xb242, 0xb242, 0xb242,
+ 0xb242, 0xb355, 0x080c, 0x0dfa, 0x00b6, 0x0096, 0x6114, 0x2148,
+ 0x7644, 0x96b4, 0x0fff, 0x86ff, 0x1528, 0x6010, 0x2058, 0xb800,
+ 0xd0bc, 0x1904, 0xb310, 0xa87b, 0x0000, 0xa867, 0x0103, 0xae76,
+ 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938, 0x9115, 0x190c, 0xb4ee,
+ 0x080c, 0x6904, 0x6210, 0x2258, 0xba3c, 0x82ff, 0x0110, 0x8211,
+ 0xba3e, 0x7044, 0xd0e4, 0x1904, 0xb2f4, 0x080c, 0xa0e3, 0x009e,
+ 0x00be, 0x0005, 0x968c, 0x0c00, 0x0150, 0x6010, 0x2058, 0xb800,
+ 0xd0bc, 0x1904, 0xb2f8, 0x7348, 0xab92, 0x734c, 0xab8e, 0x968c,
+ 0x00ff, 0x9186, 0x0002, 0x0508, 0x9186, 0x0028, 0x1118, 0xa87b,
+ 0x001c, 0x00e8, 0xd6dc, 0x01a0, 0xa87b, 0x0015, 0xa87c, 0xd0ac,
+ 0x0170, 0xa938, 0xaa34, 0x2100, 0x9205, 0x0148, 0x7048, 0x9106,
+ 0x1118, 0x704c, 0x9206, 0x0118, 0xa992, 0xaa8e, 0xc6dc, 0x0038,
+ 0xd6d4, 0x0118, 0xa87b, 0x0007, 0x0010, 0xa87b, 0x0000, 0xa867,
+ 0x0103, 0xae76, 0x901e, 0xd6c4, 0x01d8, 0x9686, 0x0100, 0x1130,
+ 0x7064, 0x9005, 0x1118, 0xc6c4, 0x0804, 0xb24b, 0x735c, 0xab86,
+ 0x83ff, 0x0170, 0x938a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036,
+ 0x2308, 0x2019, 0x0018, 0x2011, 0x0025, 0x080c, 0xb9e8, 0x003e,
+ 0xd6cc, 0x0904, 0xb260, 0x7154, 0xa98a, 0x81ff, 0x0904, 0xb260,
+ 0x9192, 0x0021, 0x1278, 0x8304, 0x9098, 0x0018, 0x2011, 0x0029,
+ 0x080c, 0xb9e8, 0x2011, 0x0205, 0x2013, 0x0000, 0x080c, 0xc4de,
+ 0x0804, 0xb260, 0xa868, 0xd0fc, 0x0120, 0x2009, 0x0020, 0xa98a,
+ 0x0c50, 0x00a6, 0x2950, 0x080c, 0xb987, 0x00ae, 0x080c, 0xc4de,
+ 0x080c, 0xb9d8, 0x0804, 0xb262, 0x080c, 0xc137, 0x0804, 0xb26f,
+ 0xa87c, 0xd0ac, 0x0904, 0xb27b, 0xa880, 0xd0bc, 0x1904, 0xb27b,
+ 0x7348, 0xa838, 0x9306, 0x11c8, 0x734c, 0xa834, 0x931e, 0x0904,
+ 0xb27b, 0xd6d4, 0x0190, 0xab38, 0x9305, 0x0904, 0xb27b, 0x0068,
+ 0xa87c, 0xd0ac, 0x0904, 0xb253, 0xa838, 0xa934, 0x9105, 0x0904,
+ 0xb253, 0xa880, 0xd0bc, 0x1904, 0xb253, 0x080c, 0xc171, 0x0804,
+ 0xb26f, 0x0096, 0x00f6, 0x6003, 0x0003, 0x6007, 0x0043, 0x2079,
+ 0x026c, 0x7c04, 0x7b00, 0x7e0c, 0x7d08, 0x6014, 0x2048, 0xa87c,
+ 0xd0ac, 0x0140, 0x6003, 0x0002, 0x00fe, 0x009e, 0x0005, 0x2130,
+ 0x2228, 0x0058, 0x2400, 0xa9ac, 0x910a, 0x2300, 0xaab0, 0x9213,
+ 0x2600, 0x9102, 0x2500, 0x9203, 0x0e90, 0xac36, 0xab3a, 0xae46,
+ 0xad4a, 0x00fe, 0x6043, 0x0000, 0x2c10, 0x080c, 0x1afe, 0x080c,
+ 0x86de, 0x080c, 0x8ced, 0x009e, 0x0005, 0x0005, 0x9182, 0x0054,
+ 0x1220, 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xb372, 0xb372,
+ 0xb372, 0xb372, 0xb372, 0xb374, 0xb40a, 0xb372, 0xb372, 0xb421,
+ 0xb4b1, 0xb372, 0xb372, 0xb372, 0xb372, 0xb4c6, 0xb372, 0xb372,
+ 0xb372, 0xb372, 0x080c, 0x0dfa, 0x0076, 0x00a6, 0x00e6, 0x0096,
+ 0x2071, 0x0260, 0x6114, 0x2150, 0x7644, 0xb676, 0x96b4, 0x0fff,
+ 0xb77c, 0xc7e5, 0xb77e, 0x6210, 0x00b6, 0x2258, 0xba3c, 0x82ff,
+ 0x0110, 0x8211, 0xba3e, 0x00be, 0x86ff, 0x0904, 0xb405, 0x9694,
+ 0xff00, 0x9284, 0x0c00, 0x0120, 0x7048, 0xb092, 0x704c, 0xb08e,
+ 0x9284, 0x0300, 0x0904, 0xb405, 0x080c, 0x1031, 0x090c, 0x0dfa,
+ 0x2900, 0xb07a, 0xb77c, 0xc7cd, 0xb77e, 0xa867, 0x0103, 0xb068,
+ 0xa86a, 0xb06c, 0xa86e, 0xb070, 0xa872, 0xae76, 0x968c, 0x0c00,
+ 0x0120, 0x7348, 0xab92, 0x734c, 0xab8e, 0x968c, 0x00ff, 0x9186,
+ 0x0002, 0x0180, 0x9186, 0x0028, 0x1118, 0xa87b, 0x001c, 0x0060,
+ 0xd6dc, 0x0118, 0xa87b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0xa87b,
+ 0x0007, 0x0010, 0xa87b, 0x0000, 0xaf7e, 0xb080, 0xa882, 0xb084,
+ 0xa886, 0x901e, 0xd6c4, 0x0190, 0x735c, 0xab86, 0x83ff, 0x0170,
+ 0x938a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019,
+ 0x0018, 0x2011, 0x0025, 0x080c, 0xb9e8, 0x003e, 0xd6cc, 0x01e8,
+ 0x7154, 0xa98a, 0x81ff, 0x01c8, 0x9192, 0x0021, 0x1260, 0x8304,
+ 0x9098, 0x0018, 0x2011, 0x0029, 0x080c, 0xb9e8, 0x2011, 0x0205,
+ 0x2013, 0x0000, 0x0050, 0xb068, 0xd0fc, 0x0120, 0x2009, 0x0020,
+ 0xa98a, 0x0c68, 0x2950, 0x080c, 0xb987, 0x009e, 0x00ee, 0x00ae,
+ 0x007e, 0x0005, 0x00f6, 0x00a6, 0x6003, 0x0003, 0x2079, 0x026c,
+ 0x7c04, 0x7b00, 0x7e0c, 0x7d08, 0x6014, 0x2050, 0xb436, 0xb33a,
+ 0xb646, 0xb54a, 0x00ae, 0x00fe, 0x2c10, 0x080c, 0x1afe, 0x0804,
+ 0x9623, 0x6003, 0x0002, 0x6004, 0x9086, 0x0040, 0x11c8, 0x0096,
+ 0x6014, 0x2048, 0xa87c, 0xd0ac, 0x0160, 0x601c, 0xd084, 0x1130,
+ 0x00f6, 0x2c00, 0x2078, 0x080c, 0x16db, 0x00fe, 0x6003, 0x0004,
+ 0x0010, 0x6003, 0x0002, 0x009e, 0x080c, 0x8b04, 0x080c, 0x8c10,
+ 0x0096, 0x2001, 0x1960, 0x2004, 0x6042, 0x080c, 0x8bc0, 0x080c,
+ 0x8ced, 0x6114, 0x2148, 0xa97c, 0xd1e4, 0x0904, 0xb4ac, 0xd1cc,
+ 0x05c8, 0xa978, 0xa868, 0xd0fc, 0x0540, 0x0016, 0xa87c, 0x0006,
+ 0xa880, 0x0006, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0019, 0x20a0,
+ 0x810e, 0x810e, 0x810f, 0x9184, 0x003f, 0x20e0, 0x9184, 0xffc0,
+ 0x9080, 0x0019, 0x2098, 0x0156, 0x20a9, 0x0020, 0x4003, 0x015e,
+ 0x000e, 0xa882, 0x000e, 0xc0cc, 0xa87e, 0x001e, 0xa874, 0x0006,
+ 0x2148, 0x080c, 0x0fe3, 0x001e, 0x0458, 0x0016, 0x080c, 0x0fe3,
+ 0x009e, 0xa87c, 0xc0cc, 0xa87e, 0xa974, 0x0016, 0x080c, 0xb9d8,
+ 0x001e, 0x00f0, 0xa867, 0x0103, 0xa974, 0x9184, 0x00ff, 0x90b6,
+ 0x0002, 0x0180, 0x9086, 0x0028, 0x1118, 0xa87b, 0x001c, 0x0060,
+ 0xd1dc, 0x0118, 0xa87b, 0x0015, 0x0038, 0xd1d4, 0x0118, 0xa87b,
+ 0x0007, 0x0010, 0xa87b, 0x0000, 0x0016, 0x080c, 0x6904, 0x001e,
+ 0xd1e4, 0x1120, 0x080c, 0xa0e3, 0x009e, 0x0005, 0x080c, 0xc137,
+ 0x0cd8, 0x6004, 0x9086, 0x0040, 0x1120, 0x080c, 0x8b04, 0x080c,
+ 0x8c10, 0x2019, 0x0001, 0x080c, 0x999d, 0x6003, 0x0002, 0x080c,
+ 0xc556, 0x080c, 0x8bc0, 0x080c, 0x8ced, 0x0005, 0x6004, 0x9086,
+ 0x0040, 0x1120, 0x080c, 0x8b04, 0x080c, 0x8c10, 0x2019, 0x0001,
+ 0x080c, 0x999d, 0x080c, 0x8bc0, 0x080c, 0x318b, 0x080c, 0xc54e,
+ 0x0096, 0x6114, 0x2148, 0x080c, 0xbe37, 0x0150, 0xa867, 0x0103,
+ 0xa87b, 0x0029, 0xa877, 0x0000, 0x080c, 0x6ae9, 0x080c, 0xc022,
+ 0x009e, 0x080c, 0xa0e3, 0x080c, 0x8ced, 0x0005, 0xa87b, 0x0015,
+ 0xd1fc, 0x0180, 0xa87b, 0x0007, 0x8002, 0x8000, 0x810a, 0x9189,
+ 0x0000, 0x0006, 0x0016, 0x2009, 0x1a51, 0x2104, 0x8000, 0x200a,
+ 0x001e, 0x000e, 0xa992, 0xa88e, 0x0005, 0x9182, 0x0054, 0x1220,
+ 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xb521, 0xb521, 0xb521,
+ 0xb521, 0xb521, 0xb523, 0xb521, 0xb521, 0xb5c9, 0xb521, 0xb521,
+ 0xb521, 0xb521, 0xb521, 0xb521, 0xb521, 0xb521, 0xb521, 0xb521,
+ 0xb6fb, 0x080c, 0x0dfa, 0x0076, 0x00a6, 0x00e6, 0x0096, 0x2071,
+ 0x0260, 0x6114, 0x2150, 0x7644, 0xb676, 0x96b4, 0x0fff, 0xb77c,
+ 0xc7e5, 0xb77e, 0x6210, 0x00b6, 0x2258, 0xba3c, 0x82ff, 0x0110,
+ 0x8211, 0xba3e, 0x00be, 0x86ff, 0x0904, 0xb5c2, 0x9694, 0xff00,
+ 0x9284, 0x0c00, 0x0120, 0x7048, 0xb092, 0x704c, 0xb08e, 0x9284,
+ 0x0300, 0x0904, 0xb5c2, 0x9686, 0x0100, 0x1130, 0x7064, 0x9005,
+ 0x1118, 0xc6c4, 0xb676, 0x0c38, 0x080c, 0x1031, 0x090c, 0x0dfa,
+ 0x2900, 0xb07a, 0xb77c, 0x97bd, 0x0200, 0xb77e, 0xa867, 0x0103,
+ 0xb068, 0xa86a, 0xb06c, 0xa86e, 0xb070, 0xa872, 0x7044, 0x9084,
+ 0xf000, 0x9635, 0xae76, 0x968c, 0x0c00, 0x0120, 0x7348, 0xab92,
+ 0x734c, 0xab8e, 0x968c, 0x00ff, 0x9186, 0x0002, 0x0180, 0x9186,
+ 0x0028, 0x1118, 0xa87b, 0x001c, 0x0060, 0xd6dc, 0x0118, 0xa87b,
+ 0x0015, 0x0038, 0xd6d4, 0x0118, 0xa87b, 0x0007, 0x0010, 0xa87b,
+ 0x0000, 0xaf7e, 0xb080, 0xa882, 0xb084, 0xa886, 0x901e, 0xd6c4,
+ 0x0190, 0x735c, 0xab86, 0x83ff, 0x0170, 0x938a, 0x0009, 0x0210,
+ 0x2019, 0x0008, 0x0036, 0x2308, 0x2019, 0x0018, 0x2011, 0x0025,
+ 0x080c, 0xb9e8, 0x003e, 0xd6cc, 0x01e8, 0x7154, 0xa98a, 0x81ff,
+ 0x01c8, 0x9192, 0x0021, 0x1260, 0x8304, 0x9098, 0x0018, 0x2011,
+ 0x0029, 0x080c, 0xb9e8, 0x2011, 0x0205, 0x2013, 0x0000, 0x0050,
+ 0xb068, 0xd0fc, 0x0120, 0x2009, 0x0020, 0xa98a, 0x0c68, 0x2950,
+ 0x080c, 0xb987, 0x080c, 0x1982, 0x009e, 0x00ee, 0x00ae, 0x007e,
+ 0x0005, 0x2001, 0x1960, 0x2004, 0x6042, 0x0096, 0x6114, 0x2148,
+ 0xa83c, 0xa940, 0x9105, 0x1118, 0xa87c, 0xc0dc, 0xa87e, 0x6003,
+ 0x0002, 0xa97c, 0xd1e4, 0x0904, 0xb6f6, 0x6043, 0x0000, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1500, 0xd1cc, 0x0904,
+ 0xb6c5, 0xa978, 0xa868, 0xd0fc, 0x0904, 0xb686, 0x0016, 0xa87c,
+ 0x0006, 0xa880, 0x0006, 0x00a6, 0x2150, 0xb174, 0x9184, 0x00ff,
+ 0x90b6, 0x0002, 0x0904, 0xb653, 0x9086, 0x0028, 0x1904, 0xb63f,
+ 0xa87b, 0x001c, 0xb07b, 0x001c, 0x0804, 0xb65b, 0x6024, 0xd0f4,
+ 0x11d0, 0xa838, 0xaa34, 0x9205, 0x09c8, 0xa838, 0xaa90, 0x9206,
+ 0x1120, 0xa88c, 0xaa34, 0x9206, 0x0988, 0x6024, 0xd0d4, 0x1148,
+ 0xa9ac, 0xa834, 0x9102, 0x603a, 0xa9b0, 0xa838, 0x9103, 0x603e,
+ 0x6024, 0xc0f5, 0x6026, 0x6010, 0x00b6, 0x2058, 0xb83c, 0x8000,
+ 0xb83e, 0x00be, 0x9006, 0xa876, 0xa892, 0xa88e, 0xa87c, 0xc0e4,
+ 0xa87e, 0xd0cc, 0x0140, 0xc0cc, 0xa87e, 0x0096, 0xa878, 0x2048,
+ 0x080c, 0x0fe3, 0x009e, 0x080c, 0xc171, 0x0804, 0xb6f6, 0xd1dc,
+ 0x0158, 0xa87b, 0x0015, 0xb07b, 0x0015, 0x080c, 0xc401, 0x0118,
+ 0xb174, 0xc1dc, 0xb176, 0x0078, 0xd1d4, 0x0128, 0xa87b, 0x0007,
+ 0xb07b, 0x0007, 0x0040, 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938,
+ 0x9115, 0x190c, 0xb4ee, 0xa87c, 0xb07e, 0xa890, 0xb092, 0xa88c,
+ 0xb08e, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0019, 0x20a0, 0x20a9,
+ 0x0020, 0x8a06, 0x8006, 0x8007, 0x9094, 0x003f, 0x22e0, 0x9084,
+ 0xffc0, 0x9080, 0x0019, 0x2098, 0x4003, 0x00ae, 0x000e, 0xa882,
+ 0x000e, 0xc0cc, 0xa87e, 0x080c, 0xc4de, 0x001e, 0xa874, 0x0006,
+ 0x2148, 0x080c, 0x0fe3, 0x001e, 0x0804, 0xb6f2, 0x0016, 0x00a6,
+ 0x2150, 0xb174, 0x9184, 0x00ff, 0x90b6, 0x0002, 0x01e0, 0x9086,
+ 0x0028, 0x1128, 0xa87b, 0x001c, 0xb07b, 0x001c, 0x00e0, 0xd1dc,
+ 0x0158, 0xa87b, 0x0015, 0xb07b, 0x0015, 0x080c, 0xc401, 0x0118,
+ 0xb174, 0xc1dc, 0xb176, 0x0078, 0xd1d4, 0x0128, 0xa87b, 0x0007,
+ 0xb07b, 0x0007, 0x0040, 0xa87c, 0xd0ac, 0x0128, 0xa834, 0xa938,
+ 0x9115, 0x190c, 0xb4ee, 0xa890, 0xb092, 0xa88c, 0xb08e, 0xa87c,
+ 0xb07e, 0x00ae, 0x080c, 0x0fe3, 0x009e, 0x080c, 0xc4de, 0xa974,
+ 0x0016, 0x080c, 0xb9d8, 0x001e, 0x0468, 0xa867, 0x0103, 0xa974,
+ 0x9184, 0x00ff, 0x90b6, 0x0002, 0x01b0, 0x9086, 0x0028, 0x1118,
+ 0xa87b, 0x001c, 0x00d0, 0xd1dc, 0x0148, 0xa87b, 0x0015, 0x080c,
+ 0xc401, 0x0118, 0xa974, 0xc1dc, 0xa976, 0x0078, 0xd1d4, 0x0118,
+ 0xa87b, 0x0007, 0x0050, 0xa87b, 0x0000, 0xa87c, 0xd0ac, 0x0128,
+ 0xa834, 0xa938, 0x9115, 0x190c, 0xb4ee, 0xa974, 0x0016, 0x080c,
+ 0x6904, 0x001e, 0xd1e4, 0x1120, 0x080c, 0xa0e3, 0x009e, 0x0005,
+ 0x080c, 0xc137, 0x0cd8, 0x6114, 0x0096, 0x2148, 0xa97c, 0xd1e4,
+ 0x190c, 0x19a0, 0x009e, 0x0005, 0x080c, 0x8b04, 0x0010, 0x080c,
+ 0x8bc0, 0x080c, 0xbe37, 0x01f0, 0x0096, 0x6114, 0x2148, 0x080c,
+ 0xc03f, 0x1118, 0x080c, 0xaa81, 0x00a0, 0xa867, 0x0103, 0x2009,
+ 0x180c, 0x210c, 0xd18c, 0x11b8, 0xd184, 0x1190, 0x6108, 0xa97a,
+ 0x918e, 0x0029, 0x1110, 0x080c, 0xdb2e, 0xa877, 0x0000, 0x080c,
+ 0x6ae9, 0x009e, 0x080c, 0xa0e3, 0x080c, 0x8c10, 0x0804, 0x8ced,
+ 0xa87b, 0x0004, 0x0c90, 0xa87b, 0x0004, 0x0c78, 0x9182, 0x0054,
+ 0x1220, 0x9182, 0x0040, 0x0208, 0x000a, 0x0005, 0xb752, 0xb752,
+ 0xb752, 0xb752, 0xb752, 0xb754, 0xb752, 0xb752, 0xb752, 0xb752,
+ 0xb752, 0xb752, 0xb752, 0xb752, 0xb752, 0xb752, 0xb752, 0xb752,
+ 0xb752, 0xb752, 0x080c, 0x0dfa, 0x080c, 0x55e3, 0x01f8, 0x6014,
+ 0x7144, 0x918c, 0x0fff, 0x9016, 0xd1c4, 0x0118, 0x7264, 0x9294,
+ 0x00ff, 0x0096, 0x904d, 0x0188, 0xa87b, 0x0000, 0xa864, 0x9086,
+ 0x0139, 0x0128, 0xa867, 0x0103, 0xa976, 0xaa96, 0x0030, 0xa897,
+ 0x4000, 0xa99a, 0xaa9e, 0x080c, 0x6ae9, 0x009e, 0x0804, 0xa0e3,
+ 0x9182, 0x0085, 0x0002, 0xb78a, 0xb788, 0xb788, 0xb796, 0xb788,
+ 0xb788, 0xb788, 0xb788, 0xb788, 0xb788, 0xb788, 0xb788, 0xb788,
+ 0x080c, 0x0dfa, 0x6003, 0x0001, 0x6106, 0x080c, 0x8679, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x8c10, 0x012e, 0x0005, 0x0026, 0x0056,
+ 0x00d6, 0x00e6, 0x2071, 0x0260, 0x7224, 0x6216, 0x7220, 0x080c,
+ 0xbe25, 0x01f8, 0x2268, 0x6800, 0x9086, 0x0000, 0x01d0, 0x6010,
+ 0x6d10, 0x952e, 0x11b0, 0x00c6, 0x2d60, 0x00d6, 0x080c, 0xba49,
+ 0x00de, 0x00ce, 0x0158, 0x702c, 0xd084, 0x1118, 0x080c, 0xba13,
+ 0x0010, 0x6803, 0x0002, 0x6007, 0x0086, 0x0028, 0x080c, 0xba35,
+ 0x0d90, 0x6007, 0x0087, 0x6003, 0x0001, 0x080c, 0x8679, 0x080c,
+ 0x8c10, 0x7220, 0x080c, 0xbe25, 0x0178, 0x6810, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0bc, 0x0140, 0x6824, 0xd0ec, 0x0128, 0x00c6,
+ 0x2d60, 0x080c, 0xc171, 0x00ce, 0x00ee, 0x00de, 0x005e, 0x002e,
+ 0x0005, 0x9186, 0x0013, 0x1160, 0x6004, 0x908a, 0x0085, 0x0a0c,
+ 0x0dfa, 0x908a, 0x0092, 0x1a0c, 0x0dfa, 0x9082, 0x0085, 0x00e2,
+ 0x9186, 0x0027, 0x0120, 0x9186, 0x0014, 0x190c, 0x0dfa, 0x080c,
+ 0x8b04, 0x0096, 0x6014, 0x2048, 0x080c, 0xbe37, 0x0140, 0xa867,
+ 0x0103, 0xa877, 0x0000, 0xa87b, 0x0029, 0x080c, 0x6ae9, 0x009e,
+ 0x080c, 0xa113, 0x0804, 0x8c10, 0xb819, 0xb81b, 0xb81b, 0xb819,
+ 0xb819, 0xb819, 0xb819, 0xb819, 0xb819, 0xb819, 0xb819, 0xb819,
+ 0xb819, 0x080c, 0x0dfa, 0x080c, 0x8b04, 0x080c, 0xa113, 0x080c,
+ 0x8c10, 0x0005, 0x9186, 0x0013, 0x1128, 0x6004, 0x9082, 0x0085,
+ 0x2008, 0x04b8, 0x9186, 0x0027, 0x11f8, 0x080c, 0x8b04, 0x080c,
+ 0x318b, 0x080c, 0xc54e, 0x0096, 0x6014, 0x2048, 0x080c, 0xbe37,
+ 0x0150, 0xa867, 0x0103, 0xa877, 0x0000, 0xa87b, 0x0029, 0x080c,
+ 0x6ae9, 0x080c, 0xc022, 0x009e, 0x080c, 0xa0e3, 0x080c, 0x8c10,
+ 0x0005, 0x080c, 0xa178, 0x0ce0, 0x9186, 0x0014, 0x1dd0, 0x080c,
+ 0x8b04, 0x0096, 0x6014, 0x2048, 0x080c, 0xbe37, 0x0d60, 0xa867,
+ 0x0103, 0xa877, 0x0000, 0xa87b, 0x0006, 0xa880, 0xc0ec, 0xa882,
+ 0x08f0, 0x0002, 0xb871, 0xb86f, 0xb86f, 0xb86f, 0xb86f, 0xb86f,
+ 0xb889, 0xb86f, 0xb86f, 0xb86f, 0xb86f, 0xb86f, 0xb86f, 0x080c,
+ 0x0dfa, 0x080c, 0x8b04, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186,
+ 0x0039, 0x0118, 0x9186, 0x0035, 0x1118, 0x2001, 0x195e, 0x0010,
+ 0x2001, 0x195f, 0x2004, 0x601a, 0x6003, 0x000c, 0x080c, 0x8c10,
+ 0x0005, 0x080c, 0x8b04, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186,
+ 0x0039, 0x0118, 0x9186, 0x0035, 0x1118, 0x2001, 0x195e, 0x0010,
+ 0x2001, 0x195f, 0x2004, 0x601a, 0x6003, 0x000e, 0x080c, 0x8c10,
+ 0x0005, 0x9182, 0x0092, 0x1220, 0x9182, 0x0085, 0x0208, 0x0012,
+ 0x0804, 0xa178, 0xb8b7, 0xb8b7, 0xb8b7, 0xb8b7, 0xb8b9, 0xb906,
+ 0xb8b7, 0xb8b7, 0xb8b7, 0xb8b7, 0xb8b7, 0xb8b7, 0xb8b7, 0x080c,
+ 0x0dfa, 0x0096, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc,
+ 0x0168, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039, 0x0118,
+ 0x9186, 0x0035, 0x1118, 0x009e, 0x0804, 0xb91a, 0x080c, 0xbe37,
+ 0x1118, 0x080c, 0xc022, 0x0068, 0x6014, 0x2048, 0xa87c, 0xd0e4,
+ 0x1110, 0x080c, 0xc022, 0xa867, 0x0103, 0x080c, 0xc519, 0x080c,
+ 0x6ae9, 0x00d6, 0x2c68, 0x080c, 0xa08d, 0x01d0, 0x6003, 0x0001,
+ 0x6007, 0x001e, 0x600b, 0xffff, 0x2009, 0x026e, 0x210c, 0x613a,
+ 0x2009, 0x026f, 0x210c, 0x613e, 0x6910, 0x6112, 0x080c, 0xc2b3,
+ 0x6954, 0x6156, 0x6023, 0x0001, 0x080c, 0x8679, 0x080c, 0x8c10,
+ 0x2d60, 0x00de, 0x080c, 0xa0e3, 0x009e, 0x0005, 0x6010, 0x00b6,
+ 0x2058, 0xb800, 0x00be, 0xd0bc, 0x05a0, 0x6034, 0x908c, 0xff00,
+ 0x810f, 0x9186, 0x0035, 0x0130, 0x9186, 0x001e, 0x0118, 0x9186,
+ 0x0039, 0x1538, 0x00d6, 0x2c68, 0x080c, 0xc4b1, 0x11f0, 0x080c,
+ 0xa08d, 0x01d8, 0x6106, 0x6003, 0x0001, 0x6023, 0x0001, 0x6910,
+ 0x6112, 0x692c, 0x612e, 0x6930, 0x6132, 0x6934, 0x918c, 0x00ff,
+ 0x6136, 0x6938, 0x613a, 0x693c, 0x613e, 0x6954, 0x6156, 0x080c,
+ 0xc2b3, 0x080c, 0x8679, 0x080c, 0x8c10, 0x2d60, 0x00de, 0x0804,
+ 0xa0e3, 0x0096, 0x6014, 0x2048, 0x080c, 0xbe37, 0x01c8, 0xa867,
+ 0x0103, 0xa880, 0xd0b4, 0x0128, 0xc0ec, 0xa882, 0xa87b, 0x0006,
+ 0x0048, 0xd0bc, 0x0118, 0xa87b, 0x0002, 0x0020, 0xa87b, 0x0005,
+ 0x080c, 0xc133, 0xa877, 0x0000, 0x080c, 0x6ae9, 0x080c, 0xc022,
+ 0x009e, 0x0804, 0xa0e3, 0x0016, 0x0096, 0x6014, 0x2048, 0x080c,
+ 0xbe37, 0x0140, 0xa867, 0x0103, 0xa87b, 0x0028, 0xa877, 0x0000,
+ 0x080c, 0x6ae9, 0x009e, 0x001e, 0x9186, 0x0013, 0x0148, 0x9186,
+ 0x0014, 0x0130, 0x9186, 0x0027, 0x0118, 0x080c, 0xa178, 0x0030,
+ 0x080c, 0x8b04, 0x080c, 0xa113, 0x080c, 0x8c10, 0x0005, 0x0056,
+ 0x0066, 0x0096, 0x00a6, 0x2029, 0x0001, 0x9182, 0x0101, 0x1208,
+ 0x0010, 0x2009, 0x0100, 0x2130, 0x8304, 0x9098, 0x0018, 0x2009,
+ 0x0020, 0x2011, 0x0029, 0x080c, 0xb9e8, 0x96b2, 0x0020, 0xb004,
+ 0x904d, 0x0110, 0x080c, 0x0fe3, 0x080c, 0x1031, 0x0520, 0x8528,
+ 0xa867, 0x0110, 0xa86b, 0x0000, 0x2920, 0xb406, 0x968a, 0x003d,
+ 0x1228, 0x2608, 0x2011, 0x001b, 0x0499, 0x00a8, 0x96b2, 0x003c,
+ 0x2009, 0x003c, 0x2950, 0x2011, 0x001b, 0x0451, 0x0c28, 0x2001,
+ 0x0205, 0x2003, 0x0000, 0x00ae, 0x852f, 0x95ad, 0x0003, 0xb566,
+ 0x95ac, 0x0000, 0x0048, 0x2001, 0x0205, 0x2003, 0x0000, 0x00ae,
+ 0x852f, 0x95ad, 0x0003, 0xb566, 0x009e, 0x006e, 0x005e, 0x0005,
+ 0x00a6, 0x89ff, 0x0158, 0xa804, 0x9055, 0x0130, 0xa807, 0x0000,
+ 0x080c, 0x6ae9, 0x2a48, 0x0cb8, 0x080c, 0x6ae9, 0x00ae, 0x0005,
+ 0x00f6, 0x2079, 0x0200, 0x7814, 0x9085, 0x0080, 0x7816, 0xd184,
+ 0x0108, 0x8108, 0x810c, 0x20a9, 0x0001, 0xa860, 0x20e8, 0xa85c,
+ 0x9200, 0x20a0, 0x20e1, 0x0000, 0x2300, 0x9e00, 0x2098, 0x4003,
+ 0x8318, 0x9386, 0x0020, 0x1148, 0x2018, 0x2300, 0x9e00, 0x2098,
+ 0x7814, 0x8000, 0x9085, 0x0080, 0x7816, 0x8109, 0x1d80, 0x7817,
+ 0x0000, 0x00fe, 0x0005, 0x6920, 0x9186, 0x0003, 0x0118, 0x9186,
+ 0x0002, 0x11d0, 0x00c6, 0x00d6, 0x00e6, 0x2d60, 0x0096, 0x6014,
+ 0x2048, 0x080c, 0xbe37, 0x0150, 0x2001, 0x0006, 0xa980, 0xc1d5,
+ 0x080c, 0x6d17, 0x080c, 0x6adc, 0x080c, 0xc022, 0x009e, 0x080c,
+ 0xa113, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x702c, 0xd084,
+ 0x1170, 0x6008, 0x2060, 0x6020, 0x9086, 0x0002, 0x1140, 0x6104,
+ 0x9186, 0x0085, 0x0118, 0x9186, 0x008b, 0x1108, 0x9006, 0x00ce,
+ 0x0005, 0x0066, 0x0126, 0x2091, 0x8000, 0x2031, 0x0001, 0x6020,
+ 0x9084, 0x000f, 0x0083, 0x012e, 0x006e, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x0066, 0x2031, 0x0000, 0x6020, 0x9084, 0x000f, 0x001b,
+ 0x006e, 0x012e, 0x0005, 0xba84, 0xba84, 0xba7f, 0xbaa6, 0xba72,
+ 0xba7f, 0xbaa6, 0xba7f, 0xba72, 0xba72, 0xba7f, 0xba7f, 0xba7f,
+ 0xba72, 0xba72, 0x080c, 0x0dfa, 0x0036, 0x2019, 0x0010, 0x080c,
+ 0xd440, 0x6023, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005, 0x9006,
+ 0x0005, 0x9085, 0x0001, 0x0005, 0x0096, 0x86ff, 0x11d8, 0x6014,
+ 0x2048, 0x080c, 0xbe37, 0x01c0, 0xa864, 0x9086, 0x0139, 0x1128,
+ 0xa87b, 0x0005, 0xa883, 0x0000, 0x0028, 0x900e, 0x2001, 0x0005,
+ 0x080c, 0x6d17, 0x080c, 0xc133, 0x080c, 0x6adc, 0x080c, 0xa113,
+ 0x9085, 0x0001, 0x009e, 0x0005, 0x9006, 0x0ce0, 0x6000, 0x908a,
+ 0x0016, 0x1a0c, 0x0dfa, 0x0002, 0xbabc, 0xbaec, 0xbabe, 0xbb0d,
+ 0xbae7, 0xbabc, 0xba7f, 0xba84, 0xba84, 0xba7f, 0xba7f, 0xba7f,
+ 0xba7f, 0xba7f, 0xba7f, 0xba7f, 0x080c, 0x0dfa, 0x86ff, 0x1520,
+ 0x6020, 0x9086, 0x0006, 0x0500, 0x0096, 0x6014, 0x2048, 0x080c,
+ 0xbe37, 0x0168, 0xa87c, 0xd0cc, 0x0140, 0x0096, 0xc0cc, 0xa87e,
+ 0xa878, 0x2048, 0x080c, 0x0fe3, 0x009e, 0x080c, 0xc133, 0x009e,
+ 0x080c, 0xc4f3, 0x6007, 0x0085, 0x6003, 0x000b, 0x6023, 0x0002,
+ 0x080c, 0x8679, 0x080c, 0x8c10, 0x9085, 0x0001, 0x0005, 0x0066,
+ 0x080c, 0x19b4, 0x006e, 0x0890, 0x00e6, 0x2071, 0x19bf, 0x7024,
+ 0x9c06, 0x1120, 0x080c, 0x9927, 0x00ee, 0x0840, 0x6020, 0x9084,
+ 0x000f, 0x9086, 0x0006, 0x1150, 0x0086, 0x0096, 0x2049, 0x0001,
+ 0x2c40, 0x080c, 0x9a58, 0x009e, 0x008e, 0x0010, 0x080c, 0x9824,
+ 0x00ee, 0x1904, 0xbabe, 0x0804, 0xba7f, 0x0036, 0x00e6, 0x2071,
+ 0x19bf, 0x703c, 0x9c06, 0x1138, 0x901e, 0x080c, 0x999d, 0x00ee,
+ 0x003e, 0x0804, 0xbabe, 0x080c, 0x9b88, 0x00ee, 0x003e, 0x1904,
+ 0xbabe, 0x0804, 0xba7f, 0x00c6, 0x6020, 0x9084, 0x000f, 0x0013,
+ 0x00ce, 0x0005, 0xbb40, 0xbc0b, 0xbd75, 0xbb4a, 0xa113, 0xbb40,
+ 0xd432, 0xc55b, 0xbc0b, 0xbb39, 0xbe01, 0xbb39, 0xbb39, 0xbb39,
+ 0xbb39, 0x080c, 0x0dfa, 0x080c, 0xc03f, 0x1110, 0x080c, 0xaa81,
+ 0x0005, 0x080c, 0x8b04, 0x080c, 0x8c10, 0x0804, 0xa0e3, 0x601b,
+ 0x0001, 0x0005, 0x080c, 0xbe37, 0x0130, 0x6014, 0x0096, 0x2048,
+ 0x2c00, 0xa896, 0x009e, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0dfa,
+ 0x0002, 0xbb69, 0xbb6b, 0xbb8f, 0xbba3, 0xbbc9, 0xbb69, 0xbb40,
+ 0xbb40, 0xbb40, 0xbba3, 0xbba3, 0xbb69, 0xbb69, 0xbb69, 0xbb69,
+ 0xbbad, 0x080c, 0x0dfa, 0x00e6, 0x6014, 0x0096, 0x2048, 0xa880,
+ 0xc0b5, 0xa882, 0x009e, 0x2071, 0x19bf, 0x7024, 0x9c06, 0x01a0,
+ 0x080c, 0x9824, 0x080c, 0xc4f3, 0x6007, 0x0085, 0x6003, 0x000b,
+ 0x6023, 0x0002, 0x2001, 0x195f, 0x2004, 0x601a, 0x080c, 0x8679,
+ 0x080c, 0x8c10, 0x00ee, 0x0005, 0x601b, 0x0001, 0x0cd8, 0x0096,
+ 0x6014, 0x2048, 0xa880, 0xc0b5, 0xa882, 0x009e, 0x080c, 0xc4f3,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x6023, 0x0002, 0x080c, 0x8679,
+ 0x080c, 0x8c10, 0x0005, 0x0096, 0x601b, 0x0001, 0x6014, 0x2048,
+ 0xa880, 0xc0b5, 0xa882, 0x009e, 0x0005, 0x080c, 0x55e3, 0x01b8,
+ 0x6014, 0x0096, 0x904d, 0x0190, 0xa864, 0xa867, 0x0103, 0xa87b,
+ 0x0006, 0x9086, 0x0139, 0x1150, 0xa867, 0x0139, 0xa87b, 0x0030,
+ 0xa897, 0x4005, 0xa89b, 0x0004, 0x080c, 0x6ae9, 0x009e, 0x0804,
+ 0xa0e3, 0x6014, 0x0096, 0x904d, 0x05c8, 0xa97c, 0xd1e4, 0x05b0,
+ 0x2001, 0x180f, 0x2004, 0xd0c4, 0x0110, 0x009e, 0x0005, 0xa884,
+ 0x009e, 0x8003, 0x800b, 0x810b, 0x9108, 0x611a, 0x2001, 0x0030,
+ 0x2c08, 0x080c, 0x158b, 0x2001, 0x030c, 0x2004, 0x9086, 0x0041,
+ 0x11a0, 0x6014, 0x0096, 0x904d, 0x090c, 0x0dfa, 0xa880, 0xd0f4,
+ 0x1130, 0xc0f5, 0xa882, 0x009e, 0x601b, 0x0002, 0x0070, 0x009e,
+ 0x2001, 0x0037, 0x2c08, 0x080c, 0x158b, 0x6000, 0x9086, 0x0004,
+ 0x1120, 0x2009, 0x0048, 0x080c, 0xa15d, 0x0005, 0x009e, 0x080c,
+ 0x19b4, 0x0804, 0xbb8f, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0dfa,
+ 0x000b, 0x0005, 0xbc22, 0xbb47, 0xbc24, 0xbc22, 0xbc24, 0xbc24,
+ 0xbb41, 0xbc22, 0xbb3b, 0xbb3b, 0xbc22, 0xbc22, 0xbc22, 0xbc22,
+ 0xbc22, 0xbc22, 0x080c, 0x0dfa, 0x6010, 0x00b6, 0x2058, 0xb804,
+ 0x9084, 0x00ff, 0x00be, 0x908a, 0x000c, 0x1a0c, 0x0dfa, 0x00b6,
+ 0x0013, 0x00be, 0x0005, 0xbc3f, 0xbd0c, 0xbc41, 0xbc81, 0xbc41,
+ 0xbc81, 0xbc41, 0xbc4f, 0xbc3f, 0xbc81, 0xbc3f, 0xbc70, 0x080c,
+ 0x0dfa, 0x6004, 0x908e, 0x0016, 0x05c0, 0x908e, 0x0004, 0x05a8,
+ 0x908e, 0x0002, 0x0590, 0x908e, 0x0052, 0x0904, 0xbd08, 0x6004,
+ 0x080c, 0xc03f, 0x0904, 0xbd25, 0x908e, 0x0004, 0x1110, 0x080c,
+ 0x31b4, 0x908e, 0x0021, 0x0904, 0xbd29, 0x908e, 0x0022, 0x0904,
+ 0xbd70, 0x908e, 0x003d, 0x0904, 0xbd29, 0x908e, 0x0039, 0x0904,
+ 0xbd2d, 0x908e, 0x0035, 0x0904, 0xbd2d, 0x908e, 0x001e, 0x0178,
+ 0x908e, 0x0001, 0x1140, 0x6010, 0x2058, 0xb804, 0x9084, 0x00ff,
+ 0x9086, 0x0006, 0x0110, 0x080c, 0x318b, 0x080c, 0xaa81, 0x0804,
+ 0xa113, 0x00c6, 0x00d6, 0x6104, 0x9186, 0x0016, 0x0904, 0xbcf9,
+ 0x9186, 0x0002, 0x1904, 0xbcce, 0x2001, 0x1836, 0x2004, 0xd08c,
+ 0x11c8, 0x080c, 0x7207, 0x11b0, 0x080c, 0xc539, 0x0138, 0x080c,
+ 0x722a, 0x1120, 0x080c, 0x7105, 0x0804, 0xbd59, 0x2001, 0x1955,
+ 0x2003, 0x0001, 0x2001, 0x1800, 0x2003, 0x0001, 0x080c, 0x7127,
+ 0x0804, 0xbd59, 0x6010, 0x2058, 0x2001, 0x1836, 0x2004, 0xd0ac,
+ 0x1904, 0xbd59, 0xb8a0, 0x9084, 0xff80, 0x1904, 0xbd59, 0xb840,
+ 0x9084, 0x00ff, 0x9005, 0x0190, 0x8001, 0xb842, 0x6017, 0x0000,
+ 0x6023, 0x0007, 0x601b, 0x0398, 0x6043, 0x0000, 0x080c, 0xa08d,
+ 0x0128, 0x2b00, 0x6012, 0x6023, 0x0001, 0x0458, 0x00de, 0x00ce,
+ 0x6004, 0x908e, 0x0002, 0x11a0, 0x6010, 0x2058, 0xb8a0, 0x9086,
+ 0x007e, 0x1170, 0x2009, 0x1836, 0x2104, 0xc085, 0x200a, 0x00e6,
+ 0x2071, 0x1800, 0x080c, 0x5ebe, 0x00ee, 0x080c, 0xaa81, 0x0030,
+ 0x080c, 0xaa81, 0x080c, 0x318b, 0x080c, 0xc54e, 0x00e6, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x31b4, 0x012e, 0x00ee, 0x080c, 0xa113,
+ 0x0005, 0x2001, 0x0002, 0x080c, 0x63f0, 0x6003, 0x0001, 0x6007,
+ 0x0002, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x00de, 0x00ce, 0x0c80,
+ 0x080c, 0x31b4, 0x0804, 0xbc7d, 0x00c6, 0x00d6, 0x6104, 0x9186,
+ 0x0016, 0x0d38, 0x6010, 0x2058, 0xb840, 0x9084, 0x00ff, 0x9005,
+ 0x0904, 0xbcce, 0x8001, 0xb842, 0x6003, 0x0001, 0x080c, 0x86c1,
+ 0x080c, 0x8c10, 0x00de, 0x00ce, 0x0898, 0x080c, 0xaa81, 0x0804,
+ 0xbc7f, 0x080c, 0xaabd, 0x0804, 0xbc7f, 0x00d6, 0x2c68, 0x6104,
+ 0x080c, 0xc4b1, 0x00de, 0x0118, 0x080c, 0xa0e3, 0x0408, 0x6004,
+ 0x8007, 0x6134, 0x918c, 0x00ff, 0x9105, 0x6036, 0x6007, 0x0085,
+ 0x6003, 0x000b, 0x6023, 0x0002, 0x603c, 0x600a, 0x2001, 0x195f,
+ 0x2004, 0x601a, 0x602c, 0x2c08, 0x2060, 0x6024, 0xd0b4, 0x0108,
+ 0xc085, 0xc0b5, 0x6026, 0x2160, 0x080c, 0x8679, 0x080c, 0x8c10,
+ 0x0005, 0x00de, 0x00ce, 0x080c, 0xaa81, 0x080c, 0x318b, 0x00e6,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x31b4, 0x6017, 0x0000, 0x6023,
+ 0x0007, 0x601b, 0x0398, 0x6043, 0x0000, 0x012e, 0x00ee, 0x0005,
+ 0x080c, 0xa513, 0x1904, 0xbd25, 0x0005, 0x6000, 0x908a, 0x0016,
+ 0x1a0c, 0x0dfa, 0x0096, 0x00d6, 0x001b, 0x00de, 0x009e, 0x0005,
+ 0xbd90, 0xbd90, 0xbd90, 0xbd90, 0xbd90, 0xbd90, 0xbd90, 0xbd90,
+ 0xbd90, 0xbb40, 0xbd90, 0xbb47, 0xbd92, 0xbb47, 0xbdac, 0xbd90,
+ 0x080c, 0x0dfa, 0x6004, 0x9086, 0x008b, 0x01b0, 0x6034, 0x908c,
+ 0xff00, 0x810f, 0x9186, 0x0035, 0x1130, 0x602c, 0x9080, 0x0009,
+ 0x200c, 0xc185, 0x2102, 0x6007, 0x008b, 0x6003, 0x000d, 0x080c,
+ 0x8679, 0x080c, 0x8c10, 0x0005, 0x080c, 0xc52d, 0x0118, 0x080c,
+ 0xc540, 0x0010, 0x080c, 0xc54e, 0x080c, 0xc022, 0x080c, 0xbe37,
+ 0x0570, 0x080c, 0x318b, 0x080c, 0xbe37, 0x0168, 0x6014, 0x2048,
+ 0xa867, 0x0103, 0xa87b, 0x0006, 0xa877, 0x0000, 0xa880, 0xc0ed,
+ 0xa882, 0x080c, 0x6ae9, 0x2c68, 0x080c, 0xa08d, 0x0150, 0x6810,
+ 0x6012, 0x080c, 0xc2b3, 0x00c6, 0x2d60, 0x080c, 0xa113, 0x00ce,
+ 0x0008, 0x2d60, 0x6017, 0x0000, 0x6023, 0x0001, 0x6007, 0x0001,
+ 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x00c8, 0x080c,
+ 0xc52d, 0x0138, 0x6034, 0x9086, 0x4000, 0x1118, 0x080c, 0x318b,
+ 0x08d0, 0x6034, 0x908c, 0xff00, 0x810f, 0x9186, 0x0039, 0x0118,
+ 0x9186, 0x0035, 0x1118, 0x080c, 0x318b, 0x0868, 0x080c, 0xa113,
+ 0x0005, 0x6000, 0x908a, 0x0016, 0x1a0c, 0x0dfa, 0x0002, 0xbe17,
+ 0xbe17, 0xbe19, 0xbe19, 0xbe19, 0xbe17, 0xbe17, 0xa113, 0xbe17,
+ 0xbe17, 0xbe17, 0xbe17, 0xbe17, 0xbe17, 0xbe17, 0xbe17, 0x080c,
+ 0x0dfa, 0x080c, 0x9b88, 0x6114, 0x0096, 0x2148, 0xa87b, 0x0006,
+ 0x080c, 0x6ae9, 0x009e, 0x0804, 0xa0e3, 0x9284, 0x0007, 0x1158,
+ 0x9282, 0x1cd0, 0x0240, 0x2001, 0x1819, 0x2004, 0x9202, 0x1218,
+ 0x9085, 0x0001, 0x0005, 0x9006, 0x0ce8, 0x0096, 0x0028, 0x0096,
+ 0x0006, 0x6014, 0x2048, 0x000e, 0x0006, 0x9984, 0xf000, 0x9086,
+ 0xf000, 0x0110, 0x080c, 0x10dc, 0x000e, 0x009e, 0x0005, 0x00e6,
+ 0x00c6, 0x0036, 0x0006, 0x0126, 0x2091, 0x8000, 0x2061, 0x1cd0,
+ 0x2071, 0x1800, 0x7350, 0x7070, 0x9302, 0x1640, 0x6020, 0x9206,
+ 0x11f8, 0x080c, 0xc539, 0x0180, 0x9286, 0x0001, 0x1168, 0x6004,
+ 0x9086, 0x0004, 0x1148, 0x080c, 0x318b, 0x080c, 0xc54e, 0x00c6,
+ 0x080c, 0xa113, 0x00ce, 0x0060, 0x080c, 0xc22d, 0x0148, 0x080c,
+ 0xc03f, 0x1110, 0x080c, 0xaa81, 0x00c6, 0x080c, 0xa0e3, 0x00ce,
+ 0x9ce0, 0x0018, 0x7064, 0x9c02, 0x1208, 0x08a0, 0x012e, 0x000e,
+ 0x003e, 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x00c6, 0x0016, 0x9188,
+ 0x1000, 0x210c, 0x81ff, 0x0128, 0x2061, 0x1a8a, 0x6112, 0x080c,
+ 0x318b, 0x9006, 0x0010, 0x9085, 0x0001, 0x001e, 0x00ce, 0x00ee,
+ 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xa08d, 0x01b0,
+ 0x6656, 0x2b00, 0x6012, 0x080c, 0x55e3, 0x0118, 0x080c, 0xbf66,
+ 0x0168, 0x080c, 0xc2b3, 0x6023, 0x0003, 0x2009, 0x004b, 0x080c,
+ 0xa15d, 0x9085, 0x0001, 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0xbaa0, 0x080c, 0xa130, 0x0560,
+ 0x6057, 0x0000, 0x2b00, 0x6012, 0x080c, 0xc2b3, 0x6023, 0x0003,
+ 0x0016, 0x080c, 0x8803, 0x0076, 0x903e, 0x080c, 0x86f1, 0x2c08,
+ 0x080c, 0xd5f6, 0x007e, 0x001e, 0xd184, 0x0128, 0x080c, 0xa0e3,
+ 0x9085, 0x0001, 0x0070, 0x080c, 0x55e3, 0x0128, 0xd18c, 0x1170,
+ 0x080c, 0xbf66, 0x0148, 0x2009, 0x004c, 0x080c, 0xa15d, 0x9085,
+ 0x0001, 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x2900, 0x6016,
+ 0x0c90, 0x2009, 0x004d, 0x0010, 0x2009, 0x004e, 0x00f6, 0x00c6,
+ 0x0046, 0x0016, 0x080c, 0xa08d, 0x2c78, 0x05a0, 0x7e56, 0x2b00,
+ 0x7812, 0x7823, 0x0003, 0x0016, 0x2021, 0x0005, 0x080c, 0xbf78,
+ 0x001e, 0x9186, 0x004d, 0x0118, 0x9186, 0x004e, 0x0148, 0x2001,
+ 0x1958, 0x200c, 0xd1fc, 0x0168, 0x2f60, 0x080c, 0xa0e3, 0x00d0,
+ 0x2001, 0x1957, 0x200c, 0xd1fc, 0x0120, 0x2f60, 0x080c, 0xa0e3,
+ 0x0088, 0x2f60, 0x080c, 0x55e3, 0x0138, 0xd18c, 0x1118, 0x04f1,
+ 0x0148, 0x0010, 0x2900, 0x7816, 0x001e, 0x0016, 0x080c, 0xa15d,
+ 0x9085, 0x0001, 0x001e, 0x004e, 0x00ce, 0x00fe, 0x0005, 0x00f6,
+ 0x00c6, 0x0046, 0x080c, 0xa08d, 0x2c78, 0x0508, 0x7e56, 0x2b00,
+ 0x7812, 0x7823, 0x0003, 0x0096, 0x2021, 0x0004, 0x0489, 0x009e,
+ 0x2001, 0x1956, 0x200c, 0xd1fc, 0x0120, 0x2f60, 0x080c, 0xa0e3,
+ 0x0060, 0x2f60, 0x080c, 0x55e3, 0x0120, 0xd18c, 0x1160, 0x0071,
+ 0x0130, 0x2009, 0x0052, 0x080c, 0xa15d, 0x9085, 0x0001, 0x004e,
+ 0x00ce, 0x00fe, 0x0005, 0x2900, 0x7816, 0x0c98, 0x00c6, 0x080c,
+ 0x4abf, 0x00ce, 0x1120, 0x080c, 0xa0e3, 0x9006, 0x0005, 0xa867,
+ 0x0000, 0xa86b, 0x8000, 0x2900, 0x6016, 0x9085, 0x0001, 0x0005,
+ 0x0096, 0x0076, 0x0126, 0x2091, 0x8000, 0x080c, 0x65d3, 0x0158,
+ 0x2001, 0xbf7d, 0x0006, 0x900e, 0x2400, 0x080c, 0x6d17, 0x080c,
+ 0x6ae9, 0x000e, 0x0807, 0x2418, 0x080c, 0x8a9e, 0xbaa0, 0x0086,
+ 0x2041, 0x0001, 0x2039, 0x0001, 0x2608, 0x080c, 0x881b, 0x008e,
+ 0x080c, 0x86f1, 0x2f08, 0x2648, 0x080c, 0xd5f6, 0xb93c, 0x81ff,
+ 0x090c, 0x88ee, 0x080c, 0x8c10, 0x012e, 0x007e, 0x009e, 0x0005,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xa08d, 0x0190, 0x660a,
+ 0x2b08, 0x6112, 0x080c, 0xc2b3, 0x6023, 0x0001, 0x2900, 0x6016,
+ 0x2009, 0x001f, 0x080c, 0xa15d, 0x9085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0x9006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0xa130, 0x01b8, 0x660a, 0x2b08, 0x6112, 0x080c, 0xc2b3, 0x6023,
+ 0x0008, 0x2900, 0x6016, 0x00f6, 0x2c78, 0x080c, 0x16db, 0x00fe,
+ 0x2009, 0x0021, 0x080c, 0xa15d, 0x9085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0x9006, 0x0cd8, 0x2009, 0x003d, 0x00c6, 0x0126, 0x0016,
+ 0x2091, 0x8000, 0x080c, 0xa08d, 0x0198, 0x660a, 0x2b08, 0x6112,
+ 0x080c, 0xc2b3, 0x6023, 0x0001, 0x2900, 0x6016, 0x001e, 0x0016,
+ 0x080c, 0xa15d, 0x9085, 0x0001, 0x001e, 0x012e, 0x00ce, 0x0005,
+ 0x9006, 0x0cd0, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xa130,
+ 0x0188, 0x2b08, 0x6112, 0x080c, 0xc2b3, 0x6023, 0x0001, 0x2900,
+ 0x6016, 0x2009, 0x0000, 0x080c, 0xa15d, 0x9085, 0x0001, 0x012e,
+ 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x2009, 0x0044, 0x0830, 0x2009,
+ 0x0049, 0x0818, 0x0026, 0x00b6, 0x6210, 0x2258, 0xba3c, 0x82ff,
+ 0x0110, 0x8211, 0xba3e, 0x00be, 0x002e, 0x0005, 0x0006, 0x0016,
+ 0x6004, 0x908e, 0x0002, 0x0140, 0x908e, 0x0003, 0x0128, 0x908e,
+ 0x0004, 0x0110, 0x9085, 0x0001, 0x001e, 0x000e, 0x0005, 0x0006,
+ 0x0086, 0x0096, 0x6020, 0x9086, 0x0004, 0x01a8, 0x6014, 0x904d,
+ 0x080c, 0xbe37, 0x0180, 0xa864, 0x9086, 0x0139, 0x0170, 0x6020,
+ 0x90c6, 0x0003, 0x0140, 0x90c6, 0x0002, 0x0128, 0xa868, 0xd0fc,
+ 0x0110, 0x9006, 0x0010, 0x9085, 0x0001, 0x009e, 0x008e, 0x000e,
+ 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xa130, 0x0198,
+ 0x2b08, 0x6112, 0x080c, 0xc2b3, 0x6023, 0x0001, 0x2900, 0x6016,
+ 0x080c, 0x318b, 0x2009, 0x0028, 0x080c, 0xa15d, 0x9085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x9186, 0x0015, 0x11a8,
+ 0x2011, 0x1823, 0x2204, 0x9086, 0x0074, 0x1178, 0x00b6, 0x080c,
+ 0xad0c, 0x00be, 0x080c, 0xaf4b, 0x6003, 0x0001, 0x6007, 0x0029,
+ 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0078, 0x6014, 0x0096, 0x2048,
+ 0xa868, 0x009e, 0xd0fc, 0x0148, 0x2001, 0x0001, 0x080c, 0xc472,
+ 0x080c, 0xaa81, 0x080c, 0xa0e3, 0x0005, 0x0096, 0x6014, 0x904d,
+ 0x090c, 0x0dfa, 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4005,
+ 0xa89b, 0x0004, 0xa867, 0x0139, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x6ae9, 0x012e, 0x009e, 0x080c, 0xa0e3, 0x0c30, 0x0096, 0x9186,
+ 0x0016, 0x1128, 0x2001, 0x0004, 0x080c, 0x63f0, 0x00e8, 0x9186,
+ 0x0015, 0x1510, 0x2011, 0x1823, 0x2204, 0x9086, 0x0014, 0x11e0,
+ 0x6010, 0x00b6, 0x2058, 0x080c, 0x653a, 0x00be, 0x080c, 0xb01c,
+ 0x1198, 0x6010, 0x00b6, 0x2058, 0xb890, 0x00be, 0x9005, 0x0160,
+ 0x2001, 0x0006, 0x080c, 0x63f0, 0x6014, 0x2048, 0xa868, 0xd0fc,
+ 0x0170, 0x080c, 0xa4e7, 0x0048, 0x6014, 0x2048, 0xa868, 0xd0fc,
+ 0x0528, 0x080c, 0xaa81, 0x080c, 0xa0e3, 0x009e, 0x0005, 0x6014,
+ 0x6310, 0x2358, 0x904d, 0x090c, 0x0dfa, 0xa87b, 0x0000, 0xa883,
+ 0x0000, 0xa897, 0x4000, 0x900e, 0x080c, 0x66bf, 0x1108, 0xc185,
+ 0xb800, 0xd0bc, 0x0108, 0xc18d, 0xa99a, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6ae9, 0x012e, 0x080c, 0xa0e3, 0x08f8, 0x6014, 0x904d,
+ 0x090c, 0x0dfa, 0xa87b, 0x0030, 0xa883, 0x0000, 0xa897, 0x4005,
+ 0xa89b, 0x0004, 0xa867, 0x0139, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x6ae9, 0x012e, 0x080c, 0xa0e3, 0x0840, 0xa878, 0x9086, 0x0005,
+ 0x1108, 0x0009, 0x0005, 0xa880, 0xc0ad, 0xa882, 0x0005, 0x6043,
+ 0x0000, 0x6017, 0x0000, 0x6003, 0x0001, 0x6007, 0x0050, 0x080c,
+ 0x8679, 0x080c, 0x8c10, 0x0005, 0x00c6, 0x6010, 0x00b6, 0x2058,
+ 0xb800, 0x00be, 0xd0bc, 0x0120, 0x6020, 0x9084, 0x000f, 0x0013,
+ 0x00ce, 0x0005, 0xbb40, 0xc163, 0xc163, 0xc166, 0xd8d4, 0xd8ef,
+ 0xd8f2, 0xbb40, 0xbb40, 0xbb40, 0xbb40, 0xbb40, 0xbb40, 0xbb40,
+ 0xbb40, 0x080c, 0x0dfa, 0xa001, 0xa001, 0x0005, 0x0096, 0x6014,
+ 0x904d, 0x0118, 0xa87c, 0xd0e4, 0x1110, 0x009e, 0x0010, 0x009e,
+ 0x0005, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0550,
+ 0x2001, 0x1833, 0x2004, 0x9005, 0x1540, 0x00f6, 0x2c78, 0x080c,
+ 0xa08d, 0x0508, 0x7810, 0x6012, 0x080c, 0xc2b3, 0x7820, 0x9086,
+ 0x0003, 0x0128, 0x7808, 0x603a, 0x2f00, 0x603e, 0x0020, 0x7808,
+ 0x603e, 0x2f00, 0x603a, 0x602e, 0x6023, 0x0001, 0x6007, 0x0035,
+ 0x6003, 0x0001, 0x7954, 0x6156, 0x080c, 0x8679, 0x080c, 0x8c10,
+ 0x2f60, 0x00fe, 0x0005, 0x2f60, 0x00fe, 0x2001, 0x1960, 0x2004,
+ 0x6042, 0x0005, 0x0016, 0x0096, 0x6814, 0x2048, 0xa87c, 0xd0e4,
+ 0x0180, 0xc0e4, 0xa87e, 0xa877, 0x0000, 0xa893, 0x0000, 0xa88f,
+ 0x0000, 0xd0cc, 0x0130, 0xc0cc, 0xa87e, 0xa878, 0x2048, 0x080c,
+ 0x0fe3, 0x6830, 0x6036, 0x908e, 0x0001, 0x0148, 0x6803, 0x0002,
+ 0x9086, 0x0005, 0x0170, 0x9006, 0x602e, 0x6032, 0x00d0, 0x681c,
+ 0xc085, 0x681e, 0x6803, 0x0004, 0x6824, 0xc0f4, 0x9085, 0x0c00,
+ 0x6826, 0x6814, 0x2048, 0xa8ac, 0x6938, 0x9102, 0xa8b0, 0x693c,
+ 0x9103, 0x1e48, 0x683c, 0x602e, 0x6838, 0x9084, 0xfffc, 0x683a,
+ 0x6032, 0x2d00, 0x603a, 0x6808, 0x603e, 0x6910, 0x6112, 0x6954,
+ 0x6156, 0x6023, 0x0001, 0x6007, 0x0039, 0x6003, 0x0001, 0x080c,
+ 0x8679, 0x080c, 0x8c10, 0x009e, 0x001e, 0x0005, 0x6024, 0xd0d4,
+ 0x0510, 0xd0f4, 0x11f8, 0x6038, 0x940a, 0x603c, 0x9303, 0x0230,
+ 0x9105, 0x0120, 0x6024, 0xc0d4, 0xc0f5, 0x0098, 0x643a, 0x633e,
+ 0xac3e, 0xab42, 0x0046, 0x0036, 0x2400, 0xacac, 0x9402, 0xa836,
+ 0x2300, 0xabb0, 0x9303, 0xa83a, 0x003e, 0x004e, 0x6024, 0xc0d4,
+ 0x0000, 0x6026, 0x0005, 0xd0f4, 0x1138, 0xa83c, 0x603a, 0xa840,
+ 0x603e, 0x6024, 0xc0f5, 0x6026, 0x0005, 0x0006, 0x0016, 0x6004,
+ 0x908e, 0x0034, 0x01b8, 0x908e, 0x0035, 0x01a0, 0x908e, 0x0036,
+ 0x0188, 0x908e, 0x0037, 0x0170, 0x908e, 0x0038, 0x0158, 0x908e,
+ 0x0039, 0x0140, 0x908e, 0x003a, 0x0128, 0x908e, 0x003b, 0x0110,
+ 0x9085, 0x0001, 0x001e, 0x000e, 0x0005, 0x0006, 0x0016, 0x0026,
+ 0x0036, 0x00e6, 0x2001, 0x195a, 0x200c, 0x8000, 0x2014, 0x2001,
+ 0x0032, 0x080c, 0x84ff, 0x2001, 0x195e, 0x82ff, 0x1110, 0x2011,
+ 0x0014, 0x2202, 0x2001, 0x195c, 0x200c, 0x8000, 0x2014, 0x2071,
+ 0x1944, 0x711a, 0x721e, 0x2001, 0x0064, 0x080c, 0x84ff, 0x2001,
+ 0x195f, 0x82ff, 0x1110, 0x2011, 0x0014, 0x2202, 0x2001, 0x1960,
+ 0x9288, 0x000a, 0x2102, 0x2001, 0x1a6b, 0x2102, 0x2001, 0x0032,
+ 0x080c, 0x158b, 0x080c, 0x67a4, 0x00ee, 0x003e, 0x002e, 0x001e,
+ 0x000e, 0x0005, 0x0006, 0x0016, 0x00e6, 0x2001, 0x195e, 0x2003,
+ 0x0028, 0x2001, 0x195f, 0x2003, 0x0014, 0x2071, 0x1944, 0x701b,
+ 0x0000, 0x701f, 0x07d0, 0x2001, 0x1960, 0x2009, 0x001e, 0x2102,
+ 0x2001, 0x1a6b, 0x2102, 0x2001, 0x0032, 0x080c, 0x158b, 0x00ee,
+ 0x001e, 0x000e, 0x0005, 0x0096, 0x6058, 0x904d, 0x0110, 0x080c,
+ 0x1063, 0x009e, 0x0005, 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0xa08d, 0x0180, 0x2b08, 0x6112, 0x0ca9, 0x6023, 0x0001,
+ 0x2900, 0x6016, 0x2009, 0x0033, 0x080c, 0xa15d, 0x9085, 0x0001,
+ 0x012e, 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x0096, 0x00e6, 0x00f6,
+ 0x2071, 0x1800, 0x9186, 0x0015, 0x1500, 0x708c, 0x9086, 0x0018,
+ 0x11e0, 0x6014, 0x2048, 0xaa3c, 0xd2e4, 0x1160, 0x2c78, 0x080c,
+ 0x8e03, 0x01d8, 0x7078, 0xaa50, 0x9206, 0x1160, 0x707c, 0xaa54,
+ 0x9206, 0x1140, 0x6210, 0x00b6, 0x2258, 0xbaa0, 0x00be, 0x900e,
+ 0x080c, 0x31d4, 0x080c, 0xa4e7, 0x0020, 0x080c, 0xaa81, 0x080c,
+ 0xa0e3, 0x00fe, 0x00ee, 0x009e, 0x0005, 0x705c, 0xaa54, 0x9206,
+ 0x0d48, 0x0c80, 0x00c6, 0x0126, 0x2091, 0x8000, 0x080c, 0xa08d,
+ 0x0188, 0x2b08, 0x6112, 0x080c, 0xc2b3, 0x6023, 0x0001, 0x2900,
+ 0x6016, 0x2009, 0x004d, 0x080c, 0xa15d, 0x9085, 0x0001, 0x012e,
+ 0x00ce, 0x0005, 0x9006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x0016, 0x080c, 0xa08d, 0x0180, 0x2b08, 0x6112, 0x080c, 0xc2b3,
+ 0x6023, 0x0001, 0x2900, 0x6016, 0x001e, 0x080c, 0xa15d, 0x9085,
+ 0x0001, 0x012e, 0x00ce, 0x0005, 0x001e, 0x9006, 0x0cd0, 0x0016,
+ 0x0026, 0x0036, 0x0046, 0x0056, 0x0066, 0x0096, 0x00e6, 0x00f6,
+ 0x2071, 0x1800, 0x9186, 0x0015, 0x1568, 0x718c, 0x6014, 0x2048,
+ 0xa814, 0x8003, 0x9106, 0x1530, 0x20e1, 0x0000, 0x2001, 0x1978,
+ 0x2003, 0x0000, 0x6014, 0x2048, 0xa830, 0x20a8, 0x8906, 0x8006,
+ 0x8007, 0x9094, 0x003f, 0x22e8, 0x9084, 0xffc0, 0x9080, 0x001b,
+ 0x20a0, 0x2001, 0x1978, 0x0016, 0x200c, 0x080c, 0xcb1d, 0x001e,
+ 0xa804, 0x9005, 0x0110, 0x2048, 0x0c38, 0x6014, 0x2048, 0xa867,
+ 0x0103, 0x0010, 0x080c, 0xaa81, 0x080c, 0xa0e3, 0x00fe, 0x00ee,
+ 0x009e, 0x006e, 0x005e, 0x004e, 0x003e, 0x002e, 0x001e, 0x0005,
+ 0x0096, 0x00e6, 0x00f6, 0x2071, 0x1800, 0x9186, 0x0015, 0x11b8,
+ 0x708c, 0x9086, 0x0004, 0x1198, 0x6014, 0x2048, 0x2c78, 0x080c,
+ 0x8e03, 0x01a8, 0x7078, 0xaa74, 0x9206, 0x1130, 0x707c, 0xaa78,
+ 0x9206, 0x1110, 0x080c, 0x318b, 0x080c, 0xa4e7, 0x0020, 0x080c,
+ 0xaa81, 0x080c, 0xa0e3, 0x00fe, 0x00ee, 0x009e, 0x0005, 0x705c,
+ 0xaa78, 0x9206, 0x0d78, 0x0c80, 0x0096, 0x00e6, 0x00f6, 0x2071,
+ 0x1800, 0x9186, 0x0015, 0x1550, 0x708c, 0x9086, 0x0004, 0x1530,
+ 0x6014, 0x2048, 0x2c78, 0x080c, 0x8e03, 0x05f0, 0x7078, 0xaacc,
+ 0x9206, 0x1180, 0x707c, 0xaad0, 0x9206, 0x1160, 0x080c, 0x318b,
+ 0x0016, 0xa998, 0xaab0, 0x9284, 0x1000, 0xc0fd, 0x080c, 0x558a,
+ 0x001e, 0x0010, 0x080c, 0x5375, 0x080c, 0xbe37, 0x0508, 0xa87b,
+ 0x0000, 0xa883, 0x0000, 0xa897, 0x4000, 0x0080, 0x080c, 0xbe37,
+ 0x01b8, 0x6014, 0x2048, 0x080c, 0x5375, 0x1d70, 0xa87b, 0x0030,
+ 0xa883, 0x0000, 0xa897, 0x4005, 0xa89b, 0x0004, 0x0126, 0x2091,
+ 0x8000, 0xa867, 0x0139, 0x080c, 0x6ae9, 0x012e, 0x080c, 0xa0e3,
+ 0x00fe, 0x00ee, 0x009e, 0x0005, 0x705c, 0xaad0, 0x9206, 0x0930,
+ 0x0888, 0x0016, 0x0026, 0xa87c, 0xd0ac, 0x0178, 0xa938, 0xaa34,
+ 0x2100, 0x9205, 0x0150, 0xa890, 0x9106, 0x1118, 0xa88c, 0x9206,
+ 0x0120, 0xa992, 0xaa8e, 0x9085, 0x0001, 0x002e, 0x001e, 0x0005,
+ 0x00b6, 0x00d6, 0x0036, 0x080c, 0xbe37, 0x0904, 0xc46e, 0x0096,
+ 0x6314, 0x2348, 0xa87a, 0xa982, 0x929e, 0x4000, 0x1580, 0x6310,
+ 0x00c6, 0x2358, 0x2009, 0x0000, 0xa868, 0xd0f4, 0x1140, 0x080c,
+ 0x66bf, 0x1108, 0xc185, 0xb800, 0xd0bc, 0x0108, 0xc18d, 0xaa96,
+ 0xa99a, 0x20a9, 0x0004, 0xa860, 0x20e8, 0xa85c, 0x9080, 0x0031,
+ 0x20a0, 0xb8b4, 0x20e0, 0xb8b8, 0x9080, 0x0006, 0x2098, 0x080c,
+ 0x0fae, 0x20a9, 0x0004, 0xa85c, 0x9080, 0x0035, 0x20a0, 0xb8b8,
+ 0x9080, 0x000a, 0x2098, 0x080c, 0x0fae, 0x00ce, 0x0090, 0xaa96,
+ 0x3918, 0x9398, 0x0007, 0x231c, 0x6004, 0x9086, 0x0016, 0x0110,
+ 0xa89b, 0x0004, 0xaba2, 0x6310, 0x2358, 0xb804, 0x9084, 0x00ff,
+ 0xa89e, 0x080c, 0x6adc, 0x6017, 0x0000, 0x009e, 0x003e, 0x00de,
+ 0x00be, 0x0005, 0x0026, 0x0036, 0x0046, 0x00b6, 0x0096, 0x00f6,
+ 0x6214, 0x2248, 0x6210, 0x2258, 0x2079, 0x0260, 0x9096, 0x0000,
+ 0x11a0, 0xb814, 0x9084, 0x00ff, 0x900e, 0x080c, 0x276e, 0x2118,
+ 0x831f, 0x939c, 0xff00, 0x7838, 0x9084, 0x00ff, 0x931d, 0x7c3c,
+ 0x2011, 0x8018, 0x080c, 0x4b1f, 0x00a8, 0x9096, 0x0001, 0x1148,
+ 0x89ff, 0x0180, 0xa89b, 0x000d, 0x7838, 0xa8a6, 0x783c, 0xa8aa,
+ 0x0048, 0x9096, 0x0002, 0x1130, 0xa89b, 0x000d, 0x7838, 0xa8a6,
+ 0x783c, 0xa8aa, 0x00fe, 0x009e, 0x00be, 0x004e, 0x003e, 0x002e,
+ 0x0005, 0x00c6, 0x0026, 0x0016, 0x9186, 0x0035, 0x0110, 0x6a38,
+ 0x0008, 0x6a2c, 0x080c, 0xbe25, 0x01f0, 0x2260, 0x6120, 0x9186,
+ 0x0003, 0x0118, 0x9186, 0x0006, 0x1190, 0x6838, 0x9206, 0x0140,
+ 0x683c, 0x9206, 0x1160, 0x6108, 0x6838, 0x9106, 0x1140, 0x0020,
+ 0x6008, 0x693c, 0x9106, 0x1118, 0x6010, 0x6910, 0x9106, 0x001e,
+ 0x002e, 0x00ce, 0x0005, 0x9085, 0x0001, 0x0cc8, 0xa974, 0xd1cc,
+ 0x0188, 0x918c, 0x00ff, 0x918e, 0x0002, 0x1160, 0xa9a8, 0x918c,
+ 0x0f00, 0x810f, 0x918e, 0x0001, 0x1128, 0xa834, 0xa938, 0x9115,
+ 0x190c, 0xb4ee, 0x0005, 0x0036, 0x2019, 0x0001, 0x0010, 0x0036,
+ 0x901e, 0x0499, 0x01e0, 0x080c, 0xbe37, 0x01c8, 0x080c, 0xc022,
+ 0x6037, 0x4000, 0x6014, 0x6017, 0x0000, 0x0096, 0x2048, 0xa87c,
+ 0x080c, 0xc03f, 0x1118, 0x080c, 0xaa81, 0x0040, 0xa867, 0x0103,
+ 0xa877, 0x0000, 0x83ff, 0x1129, 0x080c, 0x6ae9, 0x009e, 0x003e,
+ 0x0005, 0xa880, 0xd0b4, 0x0128, 0xa87b, 0x0006, 0xc0ec, 0xa882,
+ 0x0048, 0xd0bc, 0x0118, 0xa87b, 0x0002, 0x0020, 0xa87b, 0x0005,
+ 0x080c, 0xc133, 0xa877, 0x0000, 0x0005, 0x2001, 0x1810, 0x2004,
+ 0xd0ec, 0x0005, 0x0006, 0x2001, 0x1810, 0x2004, 0xd0f4, 0x000e,
+ 0x0005, 0x0006, 0x2001, 0x1810, 0x2004, 0xd0e4, 0x000e, 0x0005,
+ 0x0036, 0x0046, 0x6010, 0x00b6, 0x2058, 0xbba0, 0x00be, 0x2021,
+ 0x0007, 0x080c, 0x4cbc, 0x004e, 0x003e, 0x0005, 0x0c51, 0x1d81,
+ 0x0005, 0x2001, 0x195e, 0x2004, 0x601a, 0x0005, 0x2001, 0x1960,
+ 0x2004, 0x6042, 0x0005, 0x080c, 0xa0e3, 0x0804, 0x8c10, 0x00b6,
+ 0x0066, 0x6000, 0x90b2, 0x0016, 0x1a0c, 0x0dfa, 0x001b, 0x006e,
+ 0x00be, 0x0005, 0xc57a, 0xcc7a, 0xcdd5, 0xc57a, 0xc57a, 0xc57a,
+ 0xc57a, 0xc57a, 0xc5b1, 0xce59, 0xc57a, 0xc57a, 0xc57a, 0xc57a,
+ 0xc57a, 0xc57a, 0x080c, 0x0dfa, 0x0066, 0x6000, 0x90b2, 0x0016,
+ 0x1a0c, 0x0dfa, 0x0013, 0x006e, 0x0005, 0xc595, 0xd3cb, 0xc595,
+ 0xc595, 0xc595, 0xc595, 0xc595, 0xc595, 0xd378, 0xd41f, 0xc595,
+ 0xda0f, 0xda45, 0xda0f, 0xda45, 0xc595, 0x080c, 0x0dfa, 0x6000,
+ 0x9082, 0x0016, 0x1a0c, 0x0dfa, 0x6000, 0x000a, 0x0005, 0xc5af,
+ 0xd037, 0xd129, 0xd14c, 0xd20c, 0xc5af, 0xd2eb, 0xd294, 0xce65,
+ 0xd34e, 0xd363, 0xc5af, 0xc5af, 0xc5af, 0xc5af, 0xc5af, 0x080c,
+ 0x0dfa, 0x91b2, 0x0053, 0x1a0c, 0x0dfa, 0x2100, 0x91b2, 0x0040,
+ 0x1a04, 0xca1b, 0x0002, 0xc5fb, 0xc7e9, 0xc5fb, 0xc5fb, 0xc5fb,
+ 0xc7f2, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb,
+ 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb,
+ 0xc5fb, 0xc5fb, 0xc5fd, 0xc660, 0xc66f, 0xc6d3, 0xc6fe, 0xc776,
+ 0xc7d4, 0xc5fb, 0xc5fb, 0xc7f5, 0xc5fb, 0xc5fb, 0xc80a, 0xc817,
+ 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb, 0xc8bd, 0xc5fb, 0xc5fb,
+ 0xc8d1, 0xc5fb, 0xc5fb, 0xc88c, 0xc5fb, 0xc5fb, 0xc5fb, 0xc8e9,
+ 0xc5fb, 0xc5fb, 0xc5fb, 0xc966, 0xc5fb, 0xc5fb, 0xc5fb, 0xc5fb,
+ 0xc5fb, 0xc5fb, 0xc9e3, 0x080c, 0x0dfa, 0x080c, 0x6781, 0x1150,
+ 0x2001, 0x1836, 0x2004, 0xd0cc, 0x1128, 0x9084, 0x0009, 0x9086,
+ 0x0008, 0x1140, 0x6007, 0x0009, 0x602f, 0x0009, 0x6017, 0x0000,
+ 0x0804, 0xc7e2, 0x080c, 0x676a, 0x00e6, 0x00c6, 0x0036, 0x0026,
+ 0x0016, 0x6210, 0x2258, 0xbaa0, 0x0026, 0x2019, 0x0029, 0x080c,
+ 0x8803, 0x0076, 0x903e, 0x080c, 0x86f1, 0x2c08, 0x080c, 0xd5f6,
+ 0x007e, 0x001e, 0x001e, 0x002e, 0x003e, 0x00ce, 0x00ee, 0x6610,
+ 0x2658, 0x080c, 0x64ae, 0xbe04, 0x9684, 0x00ff, 0x9082, 0x0006,
+ 0x1268, 0x0016, 0x0026, 0x6210, 0x00b6, 0x2258, 0xbaa0, 0x00be,
+ 0x2c08, 0x080c, 0xdbbd, 0x002e, 0x001e, 0x1178, 0x080c, 0xd529,
+ 0x1904, 0xc6cb, 0x080c, 0xd4c5, 0x1120, 0x6007, 0x0008, 0x0804,
+ 0xc7e2, 0x6007, 0x0009, 0x0804, 0xc7e2, 0x080c, 0xd720, 0x0128,
+ 0x080c, 0xd529, 0x0d78, 0x0804, 0xc6cb, 0x6017, 0x1900, 0x0c88,
+ 0x080c, 0x32ae, 0x1904, 0xca18, 0x6106, 0x080c, 0xd47a, 0x6007,
+ 0x0006, 0x0804, 0xc7e2, 0x6007, 0x0007, 0x0804, 0xc7e2, 0x080c,
+ 0xda81, 0x1904, 0xca18, 0x080c, 0x32ae, 0x1904, 0xca18, 0x00d6,
+ 0x6610, 0x2658, 0xbe04, 0x9684, 0x00ff, 0x9082, 0x0006, 0x1220,
+ 0x2001, 0x0001, 0x080c, 0x63dc, 0x96b4, 0xff00, 0x8637, 0x9686,
+ 0x0006, 0x0188, 0x9686, 0x0004, 0x0170, 0xbe04, 0x96b4, 0x00ff,
+ 0x9686, 0x0006, 0x0140, 0x9686, 0x0004, 0x0128, 0x9686, 0x0005,
+ 0x0110, 0x00de, 0x0480, 0x00e6, 0x2071, 0x0260, 0x7034, 0x9084,
+ 0x0003, 0x1140, 0x7034, 0x9082, 0x0014, 0x0220, 0x7030, 0x9084,
+ 0x0003, 0x0130, 0x00ee, 0x6017, 0x0000, 0x602f, 0x0007, 0x00b0,
+ 0x00ee, 0x080c, 0xd58c, 0x1190, 0x9686, 0x0006, 0x1140, 0x0026,
+ 0x6210, 0x2258, 0xbaa0, 0x900e, 0x080c, 0x31d4, 0x002e, 0x080c,
+ 0x653a, 0x6007, 0x000a, 0x00de, 0x0804, 0xc7e2, 0x6007, 0x000b,
+ 0x00de, 0x0804, 0xc7e2, 0x080c, 0x318b, 0x080c, 0xc54e, 0x6007,
+ 0x0001, 0x0804, 0xc7e2, 0x080c, 0xda81, 0x1904, 0xca18, 0x080c,
+ 0x32ae, 0x1904, 0xca18, 0x2071, 0x0260, 0x7034, 0x90b4, 0x0003,
+ 0x1948, 0x90b2, 0x0014, 0x0a30, 0x7030, 0x9084, 0x0003, 0x1910,
+ 0x6610, 0x2658, 0xbe04, 0x9686, 0x0707, 0x09e8, 0x0026, 0x6210,
+ 0x2258, 0xbaa0, 0x900e, 0x080c, 0x31d4, 0x002e, 0x6007, 0x000c,
+ 0x2001, 0x0001, 0x080c, 0xdb9d, 0x0804, 0xc7e2, 0x080c, 0x6781,
+ 0x1140, 0x2001, 0x1836, 0x2004, 0x9084, 0x0009, 0x9086, 0x0008,
+ 0x1110, 0x0804, 0xc60a, 0x080c, 0x676a, 0x6610, 0x2658, 0xbe04,
+ 0x9684, 0x00ff, 0x9082, 0x0006, 0x06c0, 0x1138, 0x0026, 0x2001,
+ 0x0006, 0x080c, 0x641c, 0x002e, 0x0050, 0x96b4, 0xff00, 0x8637,
+ 0x9686, 0x0004, 0x0120, 0x9686, 0x0006, 0x1904, 0xc6cb, 0x080c,
+ 0xd599, 0x1120, 0x6007, 0x000e, 0x0804, 0xc7e2, 0x0046, 0x6410,
+ 0x2458, 0xbca0, 0x0046, 0x080c, 0x318b, 0x080c, 0xc54e, 0x004e,
+ 0x0016, 0x9006, 0x2009, 0x185c, 0x210c, 0x0048, 0x2009, 0x0029,
+ 0x080c, 0xd885, 0x6010, 0x2058, 0xb800, 0xc0e5, 0xb802, 0x001e,
+ 0x004e, 0x6007, 0x0001, 0x0804, 0xc7e2, 0x2001, 0x0001, 0x080c,
+ 0x63dc, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019,
+ 0x1805, 0x2011, 0x0270, 0x080c, 0xb0bc, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x9005, 0x0168, 0x96b4, 0xff00, 0x8637, 0x9682, 0x0004,
+ 0x0a04, 0xc6cb, 0x9682, 0x0007, 0x0a04, 0xc727, 0x0804, 0xc6cb,
+ 0x6017, 0x1900, 0x6007, 0x0009, 0x0804, 0xc7e2, 0x080c, 0x6781,
+ 0x1140, 0x2001, 0x1836, 0x2004, 0x9084, 0x0009, 0x9086, 0x0008,
+ 0x1110, 0x0804, 0xc60a, 0x080c, 0x676a, 0x6610, 0x2658, 0xbe04,
+ 0x9684, 0x00ff, 0x0006, 0x9086, 0x0001, 0x000e, 0x0170, 0x9082,
+ 0x0006, 0x0690, 0x0150, 0x96b4, 0xff00, 0x8637, 0x9686, 0x0004,
+ 0x0120, 0x9686, 0x0006, 0x1904, 0xc6cb, 0x080c, 0xd5c7, 0x1130,
+ 0x080c, 0xd4c5, 0x1118, 0x6007, 0x0010, 0x04e0, 0x0046, 0x6410,
+ 0x2458, 0xbca0, 0x0046, 0x080c, 0x318b, 0x080c, 0xc54e, 0x004e,
+ 0x0016, 0x9006, 0x2009, 0x185c, 0x210c, 0x0048, 0x2009, 0x0029,
+ 0x080c, 0xd885, 0x6010, 0x2058, 0xb800, 0xc0e5, 0xb802, 0x001e,
+ 0x004e, 0x6007, 0x0001, 0x00f0, 0x080c, 0xd720, 0x0140, 0x96b4,
+ 0xff00, 0x8637, 0x9686, 0x0006, 0x0980, 0x0804, 0xc6cb, 0x6017,
+ 0x1900, 0x6007, 0x0009, 0x0070, 0x080c, 0x32ae, 0x1904, 0xca18,
+ 0x080c, 0xda81, 0x1904, 0xca18, 0x080c, 0xcbb8, 0x1904, 0xc6cb,
+ 0x6007, 0x0012, 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10,
+ 0x0005, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c,
+ 0x8c10, 0x0cb0, 0x6007, 0x0005, 0x0c68, 0x080c, 0xda81, 0x1904,
+ 0xca18, 0x080c, 0x32ae, 0x1904, 0xca18, 0x080c, 0xcbb8, 0x1904,
+ 0xc6cb, 0x6007, 0x0020, 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c,
+ 0x8c10, 0x0005, 0x080c, 0x32ae, 0x1904, 0xca18, 0x6007, 0x0023,
+ 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0005, 0x080c,
+ 0xda81, 0x1904, 0xca18, 0x080c, 0x32ae, 0x1904, 0xca18, 0x080c,
+ 0xcbb8, 0x1904, 0xc6cb, 0x0016, 0x0026, 0x00e6, 0x2071, 0x0260,
+ 0x2c08, 0x2011, 0x181f, 0x2214, 0x703c, 0x9206, 0x11e0, 0x2011,
+ 0x181e, 0x2214, 0x7038, 0x9084, 0x00ff, 0x9206, 0x11a0, 0x7240,
+ 0x080c, 0xbe25, 0x0570, 0x2260, 0x6008, 0x9086, 0xffff, 0x0120,
+ 0x7244, 0x6008, 0x9206, 0x1528, 0x6020, 0x9086, 0x0007, 0x1508,
+ 0x080c, 0xa0e3, 0x04a0, 0x7244, 0x9286, 0xffff, 0x0180, 0x2c08,
+ 0x080c, 0xbe25, 0x01b0, 0x2260, 0x7240, 0x6008, 0x9206, 0x1188,
+ 0x6010, 0x9190, 0x0004, 0x2214, 0x9206, 0x01b8, 0x0050, 0x7240,
+ 0x2c08, 0x9006, 0x080c, 0xd857, 0x1180, 0x7244, 0x9286, 0xffff,
+ 0x01b0, 0x2160, 0x6007, 0x0026, 0x6017, 0x1700, 0x7214, 0x9296,
+ 0xffff, 0x1180, 0x6007, 0x0025, 0x0068, 0x6020, 0x9086, 0x0007,
+ 0x1d80, 0x6004, 0x9086, 0x0024, 0x1110, 0x080c, 0xa0e3, 0x2160,
+ 0x6007, 0x0025, 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10,
+ 0x00ee, 0x002e, 0x001e, 0x0005, 0x2001, 0x0001, 0x080c, 0x63dc,
+ 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019, 0x1805,
+ 0x2011, 0x0276, 0x080c, 0xb0bc, 0x003e, 0x002e, 0x001e, 0x015e,
+ 0x0120, 0x6007, 0x0031, 0x0804, 0xc7e2, 0x080c, 0xad24, 0x080c,
+ 0x7207, 0x1190, 0x0006, 0x0026, 0x0036, 0x080c, 0x7221, 0x1138,
+ 0x080c, 0x7504, 0x080c, 0x5f2b, 0x080c, 0x7127, 0x0010, 0x080c,
+ 0x71df, 0x003e, 0x002e, 0x000e, 0x0005, 0x080c, 0x32ae, 0x1904,
+ 0xca18, 0x080c, 0xcbb8, 0x1904, 0xc6cb, 0x6106, 0x080c, 0xcbd4,
+ 0x1120, 0x6007, 0x002b, 0x0804, 0xc7e2, 0x6007, 0x002c, 0x0804,
+ 0xc7e2, 0x080c, 0xda81, 0x1904, 0xca18, 0x080c, 0x32ae, 0x1904,
+ 0xca18, 0x080c, 0xcbb8, 0x1904, 0xc6cb, 0x6106, 0x080c, 0xcbd9,
+ 0x1120, 0x6007, 0x002e, 0x0804, 0xc7e2, 0x6007, 0x002f, 0x0804,
+ 0xc7e2, 0x080c, 0x32ae, 0x1904, 0xca18, 0x00e6, 0x00d6, 0x00c6,
+ 0x6010, 0x2058, 0xb904, 0x9184, 0x00ff, 0x9086, 0x0006, 0x0158,
+ 0x9184, 0xff00, 0x8007, 0x9086, 0x0006, 0x0128, 0x00ce, 0x00de,
+ 0x00ee, 0x0804, 0xc7e9, 0x080c, 0x55df, 0xd0e4, 0x0904, 0xc963,
+ 0x2071, 0x026c, 0x7010, 0x603a, 0x7014, 0x603e, 0x7108, 0x720c,
+ 0x080c, 0x67bf, 0x0140, 0x6010, 0x2058, 0xb810, 0x9106, 0x1118,
+ 0xb814, 0x9206, 0x0510, 0x080c, 0x67bb, 0x15b8, 0x2069, 0x1800,
+ 0x687c, 0x9206, 0x1590, 0x6878, 0x9106, 0x1578, 0x7210, 0x080c,
+ 0xbe25, 0x0590, 0x080c, 0xcaa3, 0x0578, 0x080c, 0xd901, 0x0560,
+ 0x622e, 0x6007, 0x0036, 0x6003, 0x0001, 0x080c, 0x8679, 0x080c,
+ 0x8c10, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x7214, 0x9286, 0xffff,
+ 0x0150, 0x080c, 0xbe25, 0x01c0, 0x9280, 0x0002, 0x2004, 0x7110,
+ 0x9106, 0x1190, 0x08e0, 0x7210, 0x2c08, 0x9085, 0x0001, 0x080c,
+ 0xd857, 0x2c10, 0x2160, 0x0140, 0x0890, 0x6007, 0x0037, 0x602f,
+ 0x0009, 0x6017, 0x1500, 0x08b8, 0x6007, 0x0037, 0x602f, 0x0003,
+ 0x6017, 0x1700, 0x0880, 0x6007, 0x0012, 0x0868, 0x080c, 0x32ae,
+ 0x1904, 0xca18, 0x6010, 0x2058, 0xb804, 0x9084, 0xff00, 0x8007,
+ 0x9086, 0x0006, 0x1904, 0xc7e9, 0x00e6, 0x00d6, 0x00c6, 0x080c,
+ 0x55df, 0xd0e4, 0x0904, 0xc9db, 0x2069, 0x1800, 0x2071, 0x026c,
+ 0x7008, 0x603a, 0x720c, 0x623e, 0x9286, 0xffff, 0x1150, 0x7208,
+ 0x00c6, 0x2c08, 0x9085, 0x0001, 0x080c, 0xd857, 0x2c10, 0x00ce,
+ 0x05e8, 0x080c, 0xbe25, 0x05d0, 0x7108, 0x9280, 0x0002, 0x2004,
+ 0x9106, 0x15a0, 0x00c6, 0x0026, 0x2260, 0x080c, 0xba49, 0x002e,
+ 0x00ce, 0x7118, 0x918c, 0xff00, 0x810f, 0x9186, 0x0001, 0x0178,
+ 0x9186, 0x0005, 0x0118, 0x9186, 0x0007, 0x1198, 0x9280, 0x0005,
+ 0x2004, 0x9005, 0x0170, 0x080c, 0xcaa3, 0x0904, 0xc95c, 0x0056,
+ 0x7510, 0x7614, 0x080c, 0xd91a, 0x005e, 0x00ce, 0x00de, 0x00ee,
+ 0x0005, 0x6007, 0x003b, 0x602f, 0x0009, 0x6017, 0x2a00, 0x6003,
+ 0x0001, 0x080c, 0x8679, 0x080c, 0x8c10, 0x0c78, 0x6007, 0x003b,
+ 0x602f, 0x0003, 0x6017, 0x0300, 0x6003, 0x0001, 0x080c, 0x8679,
+ 0x080c, 0x8c10, 0x0c10, 0x6007, 0x003b, 0x602f, 0x000b, 0x6017,
+ 0x0000, 0x0804, 0xc933, 0x00e6, 0x0026, 0x080c, 0x6781, 0x0550,
+ 0x080c, 0x676a, 0x080c, 0xdaf3, 0x1518, 0x2071, 0x1800, 0x70d8,
+ 0x9085, 0x0003, 0x70da, 0x00f6, 0x2079, 0x0100, 0x72ac, 0x9284,
+ 0x00ff, 0x707a, 0x78e6, 0x9284, 0xff00, 0x727c, 0x9205, 0x707e,
+ 0x78ea, 0x00fe, 0x70e3, 0x0000, 0x080c, 0x67bf, 0x0120, 0x2011,
+ 0x19d8, 0x2013, 0x07d0, 0xd0ac, 0x1128, 0x080c, 0x2f6c, 0x0010,
+ 0x080c, 0xdb25, 0x002e, 0x00ee, 0x080c, 0xa0e3, 0x0804, 0xc7e8,
+ 0x080c, 0xa0e3, 0x0005, 0x2600, 0x0002, 0xca2f, 0xca2f, 0xca2f,
+ 0xca2f, 0xca2f, 0xca31, 0xca2f, 0xca2f, 0xca2f, 0xca2f, 0xca4e,
+ 0xca2f, 0xca2f, 0xca2f, 0xca60, 0xca6d, 0xca9e, 0xca2f, 0x080c,
+ 0x0dfa, 0x080c, 0xda81, 0x1d20, 0x080c, 0x32ae, 0x1d08, 0x080c,
+ 0xcbb8, 0x1148, 0x7038, 0x6016, 0x6007, 0x0045, 0x6003, 0x0001,
+ 0x080c, 0x86c1, 0x0005, 0x080c, 0x318b, 0x080c, 0xc54e, 0x6007,
+ 0x0001, 0x6003, 0x0001, 0x080c, 0x86c1, 0x0005, 0x080c, 0xda81,
+ 0x1938, 0x080c, 0x32ae, 0x1920, 0x080c, 0xcbb8, 0x1d60, 0x703c,
+ 0x6016, 0x6007, 0x004a, 0x6003, 0x0001, 0x080c, 0x86c1, 0x0005,
+ 0x080c, 0xcac0, 0x0904, 0xca18, 0x6007, 0x004e, 0x6003, 0x0001,
+ 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0005, 0x6007, 0x004f, 0x6017,
+ 0x0000, 0x7134, 0x918c, 0x00ff, 0x81ff, 0x0508, 0x9186, 0x0001,
+ 0x1160, 0x7140, 0x2001, 0x1995, 0x2004, 0x9106, 0x11b0, 0x7144,
+ 0x2001, 0x1996, 0x2004, 0x9106, 0x0190, 0x9186, 0x0002, 0x1168,
+ 0x2011, 0x0276, 0x20a9, 0x0004, 0x6010, 0x0096, 0x2048, 0x2019,
+ 0x000a, 0x080c, 0xb0d0, 0x009e, 0x0110, 0x6017, 0x0001, 0x6003,
+ 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0005, 0x6007, 0x0050,
+ 0x703c, 0x6016, 0x0ca0, 0x0016, 0x00e6, 0x2071, 0x0260, 0x00b6,
+ 0x00c6, 0x2260, 0x6010, 0x2058, 0xb8bc, 0xd084, 0x0150, 0x7128,
+ 0x6044, 0x9106, 0x1120, 0x712c, 0x6048, 0x9106, 0x0110, 0x9006,
+ 0x0010, 0x9085, 0x0001, 0x00ce, 0x00be, 0x00ee, 0x001e, 0x0005,
+ 0x0016, 0x0096, 0x0086, 0x00e6, 0x01c6, 0x01d6, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0x1800, 0x708c, 0x908a, 0x00f9, 0x16e8, 0x20e1,
+ 0x0000, 0x2001, 0x1978, 0x2003, 0x0000, 0x080c, 0x104a, 0x05a0,
+ 0x2900, 0x6016, 0x708c, 0x8004, 0xa816, 0x908a, 0x001e, 0x02d0,
+ 0xa833, 0x001e, 0x20a9, 0x001e, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x001b, 0x20a0, 0x2001, 0x1978, 0x0016, 0x200c, 0x0471, 0x001e,
+ 0x2940, 0x080c, 0x104a, 0x01c0, 0x2900, 0xa006, 0x2100, 0x81ff,
+ 0x0180, 0x0c18, 0xa832, 0x20a8, 0xa860, 0x20e8, 0xa85c, 0x9080,
+ 0x001b, 0x20a0, 0x2001, 0x1978, 0x0016, 0x200c, 0x00b1, 0x001e,
+ 0x0000, 0x9085, 0x0001, 0x0048, 0x2071, 0x1800, 0x708f, 0x0000,
+ 0x6014, 0x2048, 0x080c, 0x0fe3, 0x9006, 0x012e, 0x01de, 0x01ce,
+ 0x00ee, 0x008e, 0x009e, 0x001e, 0x0005, 0x0006, 0x0016, 0x0026,
+ 0x0036, 0x00c6, 0x918c, 0xffff, 0x11a8, 0x080c, 0x22e3, 0x2099,
+ 0x026c, 0x2001, 0x0014, 0x3518, 0x9312, 0x1218, 0x23a8, 0x4003,
+ 0x00f8, 0x20a8, 0x4003, 0x22a8, 0x8108, 0x080c, 0x22e3, 0x2099,
+ 0x0260, 0x0ca8, 0x080c, 0x22e3, 0x2061, 0x1978, 0x6004, 0x2098,
+ 0x6008, 0x3518, 0x9312, 0x1218, 0x23a8, 0x4003, 0x0048, 0x20a8,
+ 0x4003, 0x22a8, 0x8108, 0x080c, 0x22e3, 0x2099, 0x0260, 0x0ca8,
+ 0x2061, 0x1978, 0x2019, 0x0280, 0x3300, 0x931e, 0x0110, 0x6006,
+ 0x0020, 0x2001, 0x0260, 0x6006, 0x8108, 0x2162, 0x9292, 0x0021,
+ 0x9296, 0xffff, 0x620a, 0x00ce, 0x003e, 0x002e, 0x001e, 0x000e,
+ 0x0005, 0x0006, 0x0016, 0x0026, 0x0036, 0x00c6, 0x81ff, 0x11b8,
+ 0x080c, 0x22fb, 0x20a1, 0x024c, 0x2001, 0x0014, 0x3518, 0x9312,
+ 0x1218, 0x23a8, 0x4003, 0x0418, 0x20a8, 0x4003, 0x82ff, 0x01f8,
+ 0x22a8, 0x8108, 0x080c, 0x22fb, 0x20a1, 0x0240, 0x0c98, 0x080c,
+ 0x22fb, 0x2061, 0x197b, 0x6004, 0x20a0, 0x6008, 0x3518, 0x9312,
+ 0x1218, 0x23a8, 0x4003, 0x0058, 0x20a8, 0x4003, 0x82ff, 0x0138,
+ 0x22a8, 0x8108, 0x080c, 0x22fb, 0x20a1, 0x0240, 0x0c98, 0x2061,
+ 0x197b, 0x2019, 0x0260, 0x3400, 0x931e, 0x0110, 0x6006, 0x0020,
+ 0x2001, 0x0240, 0x6006, 0x8108, 0x2162, 0x9292, 0x0021, 0x9296,
+ 0xffff, 0x620a, 0x00ce, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005,
+ 0x00b6, 0x0066, 0x6610, 0x2658, 0xbe04, 0x96b4, 0xff00, 0x8637,
+ 0x9686, 0x0006, 0x0170, 0x9686, 0x0004, 0x0158, 0xbe04, 0x96b4,
+ 0x00ff, 0x9686, 0x0006, 0x0128, 0x9686, 0x0004, 0x0110, 0x9085,
+ 0x0001, 0x006e, 0x00be, 0x0005, 0x00d6, 0x080c, 0xcc50, 0x00de,
+ 0x0005, 0x00d6, 0x080c, 0xcc5d, 0x1520, 0x680c, 0x908c, 0xff00,
+ 0x6820, 0x9084, 0x00ff, 0x9115, 0x6216, 0x6824, 0x602e, 0xd1e4,
+ 0x0130, 0x9006, 0x080c, 0xdb9d, 0x2009, 0x0001, 0x0078, 0xd1ec,
+ 0x0180, 0x6920, 0x918c, 0x00ff, 0x6824, 0x080c, 0x276e, 0x1148,
+ 0x2001, 0x0001, 0x080c, 0xdb9d, 0x2110, 0x900e, 0x080c, 0x31d4,
+ 0x0018, 0x9085, 0x0001, 0x0008, 0x9006, 0x00de, 0x0005, 0x00b6,
+ 0x00c6, 0x080c, 0xa130, 0x05a8, 0x0016, 0x0026, 0x00c6, 0x2011,
+ 0x0263, 0x2204, 0x8211, 0x220c, 0x080c, 0x276e, 0x1578, 0x080c,
+ 0x643f, 0x1560, 0xbe12, 0xbd16, 0x00ce, 0x002e, 0x001e, 0x2b00,
+ 0x6012, 0x080c, 0xda81, 0x11d8, 0x080c, 0x32ae, 0x11c0, 0x080c,
+ 0xcbb8, 0x0510, 0x2001, 0x0007, 0x080c, 0x63f0, 0x2001, 0x0007,
+ 0x080c, 0x641c, 0x6017, 0x0000, 0x6023, 0x0001, 0x6007, 0x0001,
+ 0x6003, 0x0001, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x0010, 0x080c,
+ 0xa0e3, 0x9085, 0x0001, 0x00ce, 0x00be, 0x0005, 0x080c, 0xa0e3,
+ 0x00ce, 0x002e, 0x001e, 0x0ca8, 0x080c, 0xa0e3, 0x9006, 0x0c98,
+ 0x2069, 0x026d, 0x6800, 0x9082, 0x0010, 0x1228, 0x6017, 0x0000,
+ 0x9085, 0x0001, 0x0008, 0x9006, 0x0005, 0x6017, 0x0000, 0x2069,
+ 0x026c, 0x6808, 0x9084, 0xff00, 0x9086, 0x0800, 0x1190, 0x6904,
+ 0x9186, 0x0018, 0x0118, 0x9186, 0x0014, 0x1158, 0x810f, 0x6800,
+ 0x9084, 0x00ff, 0x910d, 0x615a, 0x908e, 0x0014, 0x0110, 0x908e,
+ 0x0010, 0x0005, 0x6004, 0x90b2, 0x0053, 0x1a0c, 0x0dfa, 0x91b6,
+ 0x0013, 0x1130, 0x2008, 0x91b2, 0x0040, 0x1a04, 0xcda5, 0x0092,
+ 0x91b6, 0x0027, 0x0120, 0x91b6, 0x0014, 0x190c, 0x0dfa, 0x2001,
+ 0x0007, 0x080c, 0x641c, 0x080c, 0x8b04, 0x080c, 0xa113, 0x080c,
+ 0x8c10, 0x0005, 0xccda, 0xccdc, 0xccda, 0xccda, 0xccda, 0xccdc,
+ 0xcceb, 0xcd9e, 0xcd3d, 0xcd9e, 0xcd4f, 0xcd9e, 0xcceb, 0xcd9e,
+ 0xcd96, 0xcd9e, 0xcd96, 0xcd9e, 0xcd9e, 0xccda, 0xccda, 0xccda,
+ 0xccda, 0xccda, 0xccda, 0xccda, 0xccda, 0xccda, 0xccda, 0xccda,
+ 0xccdc, 0xccda, 0xcd9e, 0xccda, 0xccda, 0xcd9e, 0xccda, 0xcd9b,
+ 0xcd9e, 0xccda, 0xccda, 0xccda, 0xccda, 0xcd9e, 0xcd9e, 0xccda,
+ 0xcd9e, 0xcd9e, 0xccda, 0xcce6, 0xccda, 0xccda, 0xccda, 0xccda,
+ 0xcd9a, 0xcd9e, 0xccda, 0xccda, 0xcd9e, 0xcd9e, 0xccda, 0xccda,
+ 0xccda, 0xccda, 0x080c, 0x0dfa, 0x080c, 0x8b04, 0x080c, 0xc551,
+ 0x6003, 0x0002, 0x080c, 0x8c10, 0x0804, 0xcda4, 0x9006, 0x080c,
+ 0x63dc, 0x0804, 0xcd9e, 0x080c, 0x67bb, 0x1904, 0xcd9e, 0x9006,
+ 0x080c, 0x63dc, 0x6010, 0x2058, 0xb810, 0x9086, 0x00ff, 0x1140,
+ 0x00f6, 0x2079, 0x1800, 0x78a4, 0x8000, 0x78a6, 0x00fe, 0x0428,
+ 0x6010, 0x2058, 0xb8b0, 0x9005, 0x1178, 0x080c, 0xc539, 0x1904,
+ 0xcd9e, 0x0036, 0x0046, 0xbba0, 0x2021, 0x0007, 0x080c, 0x4cbc,
+ 0x004e, 0x003e, 0x0804, 0xcd9e, 0x080c, 0x32df, 0x1904, 0xcd9e,
+ 0x2001, 0x1800, 0x2004, 0x9086, 0x0002, 0x1138, 0x00f6, 0x2079,
+ 0x1800, 0x78a4, 0x8000, 0x78a6, 0x00fe, 0x2001, 0x0002, 0x080c,
+ 0x63f0, 0x080c, 0x8b04, 0x6023, 0x0001, 0x6003, 0x0001, 0x6007,
+ 0x0002, 0x080c, 0x86c1, 0x080c, 0x8c10, 0x6110, 0x2158, 0x2009,
+ 0x0001, 0x080c, 0x82e8, 0x0804, 0xcda4, 0x6610, 0x2658, 0xbe04,
+ 0x96b4, 0xff00, 0x8637, 0x9686, 0x0006, 0x0904, 0xcd9e, 0x9686,
+ 0x0004, 0x0904, 0xcd9e, 0x2001, 0x0004, 0x0804, 0xcd9c, 0x2001,
+ 0x1800, 0x2004, 0x9086, 0x0003, 0x1158, 0x0036, 0x0046, 0x6010,
+ 0x2058, 0xbba0, 0x2021, 0x0006, 0x080c, 0x4cbc, 0x004e, 0x003e,
+ 0x2001, 0x0006, 0x080c, 0xcdc2, 0x6610, 0x2658, 0xbe04, 0x0066,
+ 0x96b4, 0xff00, 0x8637, 0x9686, 0x0006, 0x006e, 0x0168, 0x2001,
+ 0x0006, 0x080c, 0x641c, 0x9284, 0x00ff, 0x908e, 0x0007, 0x1120,
+ 0x2001, 0x0006, 0x080c, 0x63f0, 0x080c, 0x67bb, 0x11f8, 0x2001,
+ 0x1836, 0x2004, 0xd0a4, 0x01d0, 0xbe04, 0x96b4, 0x00ff, 0x9686,
+ 0x0006, 0x01a0, 0x00f6, 0x2079, 0x1800, 0x78a4, 0x8000, 0x78a6,
+ 0x00fe, 0x0804, 0xcd25, 0x2001, 0x0004, 0x0030, 0x2001, 0x0006,
+ 0x0449, 0x0020, 0x0018, 0x0010, 0x080c, 0x641c, 0x080c, 0x8b04,
+ 0x080c, 0xa0e3, 0x080c, 0x8c10, 0x0005, 0x2600, 0x0002, 0xcdb9,
+ 0xcdb9, 0xcdb9, 0xcdb9, 0xcdb9, 0xcdbb, 0xcdb9, 0xcdb9, 0xcdb9,
+ 0xcdb9, 0xcdbb, 0xcdb9, 0xcdb9, 0xcdb9, 0xcdbb, 0xcdbb, 0xcdbb,
+ 0xcdbb, 0x080c, 0x0dfa, 0x080c, 0x8b04, 0x080c, 0xa0e3, 0x080c,
+ 0x8c10, 0x0005, 0x0016, 0x00b6, 0x00d6, 0x6110, 0x2158, 0xb900,
+ 0xd184, 0x0138, 0x080c, 0x63f0, 0x9006, 0x080c, 0x63dc, 0x080c,
+ 0x31b4, 0x00de, 0x00be, 0x001e, 0x0005, 0x6610, 0x2658, 0xb804,
+ 0x9084, 0xff00, 0x8007, 0x90b2, 0x000c, 0x1a0c, 0x0dfa, 0x91b6,
+ 0x0015, 0x1110, 0x003b, 0x0028, 0x91b6, 0x0016, 0x190c, 0x0dfa,
+ 0x006b, 0x0005, 0xab62, 0xab62, 0xab62, 0xab62, 0xce57, 0xab62,
+ 0xce41, 0xce02, 0xab62, 0xab62, 0xab62, 0xab62, 0xab62, 0xab62,
+ 0xab62, 0xab62, 0xce57, 0xab62, 0xce41, 0xce48, 0xab62, 0xab62,
+ 0xab62, 0xab62, 0x00f6, 0x080c, 0x67bb, 0x11d8, 0x080c, 0xc539,
+ 0x11c0, 0x6010, 0x905d, 0x01a8, 0xb8b0, 0x9005, 0x0190, 0x9006,
+ 0x080c, 0x63dc, 0x2001, 0x0002, 0x080c, 0x63f0, 0x6023, 0x0001,
+ 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x86c1, 0x080c, 0x8c10,
+ 0x00f0, 0x2011, 0x0263, 0x2204, 0x8211, 0x220c, 0x080c, 0x276e,
+ 0x11b0, 0x080c, 0x649f, 0x0118, 0x080c, 0xa0e3, 0x0080, 0xb810,
+ 0x0006, 0xb814, 0x0006, 0xb8b0, 0x0006, 0x080c, 0x5f45, 0x000e,
+ 0xb8b2, 0x000e, 0xb816, 0x000e, 0xb812, 0x080c, 0xa0e3, 0x00fe,
+ 0x0005, 0x6604, 0x96b6, 0x001e, 0x1110, 0x080c, 0xa0e3, 0x0005,
+ 0x080c, 0xaf48, 0x1148, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c,
+ 0x86c1, 0x080c, 0x8c10, 0x0010, 0x080c, 0xa0e3, 0x0005, 0x0804,
+ 0xa0e3, 0x6004, 0x908a, 0x0053, 0x1a0c, 0x0dfa, 0x080c, 0x8b04,
+ 0x080c, 0xa113, 0x080c, 0x8c10, 0x0005, 0x9182, 0x0040, 0x0002,
+ 0xce7c, 0xce7c, 0xce7c, 0xce7c, 0xce7e, 0xce7c, 0xce7c, 0xce7c,
+ 0xce7c, 0xce7c, 0xce7c, 0xce7c, 0xce7c, 0xce7c, 0xce7c, 0xce7c,
+ 0xce7c, 0xce7c, 0xce7c, 0xce7c, 0x080c, 0x0dfa, 0x0096, 0x00b6,
+ 0x00d6, 0x00e6, 0x00f6, 0x0046, 0x0026, 0x6210, 0x2258, 0xb8ac,
+ 0x9005, 0x11a8, 0x6106, 0x2071, 0x0260, 0x7444, 0x94a4, 0xff00,
+ 0x0904, 0xcee4, 0x080c, 0xdb91, 0x1170, 0x9486, 0x2000, 0x1158,
+ 0x2009, 0x0001, 0x2011, 0x0200, 0x080c, 0x84d1, 0x0020, 0x9026,
+ 0x080c, 0xdac6, 0x0c38, 0x080c, 0x1031, 0x090c, 0x0dfa, 0x6003,
+ 0x0007, 0xa867, 0x010d, 0x9006, 0xa802, 0xa86a, 0xac8a, 0x2c00,
+ 0xa88e, 0x6008, 0xa8e2, 0x6010, 0x2058, 0xb8a0, 0x7130, 0xa97a,
+ 0x0016, 0xa876, 0xa87f, 0x0000, 0xa883, 0x0000, 0xa887, 0x0036,
+ 0x080c, 0x6ae9, 0x001e, 0x080c, 0xdb91, 0x1904, 0xcf44, 0x9486,
+ 0x2000, 0x1130, 0x2019, 0x0017, 0x080c, 0xd801, 0x0804, 0xcf44,
+ 0x9486, 0x0200, 0x1120, 0x080c, 0xd79d, 0x0804, 0xcf44, 0x9486,
+ 0x0400, 0x0120, 0x9486, 0x1000, 0x1904, 0xcf44, 0x2019, 0x0002,
+ 0x080c, 0xd7b8, 0x0804, 0xcf44, 0x2069, 0x1a48, 0x6a00, 0xd284,
+ 0x0904, 0xcfae, 0x9284, 0x0300, 0x1904, 0xcfa7, 0x6804, 0x9005,
+ 0x0904, 0xcf8f, 0x2d78, 0x6003, 0x0007, 0x080c, 0x104a, 0x0904,
+ 0xcf50, 0x7800, 0xd08c, 0x1118, 0x7804, 0x8001, 0x7806, 0x6017,
+ 0x0000, 0x2001, 0x180f, 0x2004, 0xd084, 0x1904, 0xcfb2, 0x9006,
+ 0xa802, 0xa867, 0x0116, 0xa86a, 0x6008, 0xa8e2, 0x2c00, 0xa87a,
+ 0x6010, 0x2058, 0xb8a0, 0x7130, 0xa9b6, 0xa876, 0xb928, 0xa9ba,
+ 0xb92c, 0xa9be, 0xb930, 0xa9c2, 0xb934, 0xa9c6, 0xa883, 0x003d,
+ 0x7044, 0x9084, 0x0003, 0x9080, 0xcf4c, 0x2005, 0xa87e, 0x20a9,
+ 0x000a, 0x2001, 0x0270, 0xaa5c, 0x9290, 0x0021, 0x2009, 0x0205,
+ 0x200b, 0x0080, 0x20e1, 0x0000, 0xab60, 0x23e8, 0x2098, 0x22a0,
+ 0x4003, 0x200b, 0x0000, 0x2001, 0x027a, 0x200c, 0xa9b2, 0x8000,
+ 0x200c, 0xa9ae, 0x080c, 0x6ae9, 0x002e, 0x004e, 0x00fe, 0x00ee,
+ 0x00de, 0x00be, 0x009e, 0x0005, 0x0000, 0x0080, 0x0040, 0x0000,
+ 0x2001, 0x1810, 0x2004, 0xd084, 0x0120, 0x080c, 0x1031, 0x1904,
+ 0xcef9, 0x6017, 0xf100, 0x6003, 0x0001, 0x6007, 0x0041, 0x080c,
+ 0x8679, 0x080c, 0x8c10, 0x0c00, 0x2069, 0x0260, 0x6848, 0x9084,
+ 0xff00, 0x9086, 0x1200, 0x1198, 0x686c, 0x9084, 0x00ff, 0x0016,
+ 0x6114, 0x918c, 0xf700, 0x910d, 0x6116, 0x001e, 0x6003, 0x0001,
+ 0x6007, 0x0043, 0x080c, 0x8679, 0x080c, 0x8c10, 0x0828, 0x6868,
+ 0x602e, 0x686c, 0x6032, 0x6017, 0xf200, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x080c, 0x8679, 0x080c, 0x8c10, 0x0804, 0xcf44, 0x2001,
+ 0x180e, 0x2004, 0xd0ec, 0x0120, 0x2011, 0x8049, 0x080c, 0x4b1f,
+ 0x6017, 0xf300, 0x0010, 0x6017, 0xf100, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x080c, 0x8679, 0x080c, 0x8c10, 0x0804, 0xcf44, 0x6017,
+ 0xf500, 0x0c98, 0x6017, 0xf600, 0x0804, 0xcf64, 0x6017, 0xf200,
+ 0x0804, 0xcf64, 0xa867, 0x0146, 0xa86b, 0x0000, 0x6008, 0xa886,
+ 0x2c00, 0xa87a, 0x7044, 0x9084, 0x0003, 0x9080, 0xcf4c, 0x2005,
+ 0xa87e, 0x2928, 0x6010, 0x2058, 0xb8a0, 0xa876, 0xb828, 0xa88a,
+ 0xb82c, 0xa88e, 0xb830, 0xa892, 0xb834, 0xa896, 0xa883, 0x003d,
+ 0x2009, 0x0205, 0x2104, 0x9085, 0x0080, 0x200a, 0x20e1, 0x0000,
+ 0x2011, 0x0210, 0x2214, 0x9294, 0x0fff, 0xaaa2, 0x9282, 0x0111,
+ 0x1a0c, 0x0dfa, 0x8210, 0x821c, 0x2001, 0x026c, 0x2098, 0xa860,
+ 0x20e8, 0xa85c, 0x9080, 0x0029, 0x20a0, 0x2011, 0xd02e, 0x2041,
+ 0x0001, 0x223d, 0x9784, 0x00ff, 0x9322, 0x1208, 0x2300, 0x20a8,
+ 0x4003, 0x931a, 0x0530, 0x8210, 0xd7fc, 0x1130, 0x8d68, 0x2d0a,
+ 0x2001, 0x0260, 0x2098, 0x0c68, 0x2950, 0x080c, 0x104a, 0x0170,
+ 0x2900, 0xb002, 0xa867, 0x0147, 0xa86b, 0x0000, 0xa860, 0x20e8,
+ 0xa85c, 0x9080, 0x001b, 0x20a0, 0x8840, 0x08d8, 0x2548, 0xa800,
+ 0x902d, 0x0118, 0x080c, 0x1063, 0x0cc8, 0x080c, 0x1063, 0x0804,
+ 0xcf50, 0x2548, 0x8847, 0x9885, 0x0046, 0xa866, 0x2009, 0x0205,
+ 0x200b, 0x0000, 0x080c, 0xd830, 0x0804, 0xcf44, 0x8010, 0x0004,
+ 0x801a, 0x0006, 0x8018, 0x0008, 0x8016, 0x000a, 0x8014, 0x9186,
+ 0x0013, 0x1160, 0x6004, 0x908a, 0x0054, 0x1a0c, 0x0dfa, 0x9082,
+ 0x0040, 0x0a0c, 0x0dfa, 0x2008, 0x0804, 0xd0e0, 0x9186, 0x0051,
+ 0x0108, 0x00c0, 0x2001, 0x0109, 0x2004, 0xd084, 0x0904, 0xd090,
+ 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x080c, 0x8563,
+ 0x002e, 0x001e, 0x000e, 0x012e, 0x6000, 0x9086, 0x0002, 0x1580,
+ 0x0804, 0xd129, 0x9186, 0x0027, 0x0530, 0x9186, 0x0048, 0x0128,
+ 0x9186, 0x0014, 0x0500, 0x190c, 0x0dfa, 0x2001, 0x0109, 0x2004,
+ 0xd084, 0x01f0, 0x00c6, 0x0126, 0x2091, 0x2800, 0x00c6, 0x2061,
+ 0x0100, 0x0006, 0x0016, 0x0026, 0x080c, 0x8563, 0x002e, 0x001e,
+ 0x000e, 0x00ce, 0x012e, 0x00ce, 0x6000, 0x9086, 0x0004, 0x190c,
+ 0x0dfa, 0x0804, 0xd20c, 0x6004, 0x9082, 0x0040, 0x2008, 0x001a,
+ 0x080c, 0xa178, 0x0005, 0xd0a7, 0xd0a9, 0xd0a9, 0xd0d0, 0xd0a7,
+ 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7,
+ 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0xd0a7, 0x080c,
+ 0x0dfa, 0x080c, 0x8b04, 0x080c, 0x8c10, 0x0036, 0x0096, 0x6014,
+ 0x904d, 0x01d8, 0x080c, 0xbe37, 0x01c0, 0x6003, 0x0002, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x1178, 0x2019, 0x0004,
+ 0x080c, 0xd830, 0x6017, 0x0000, 0x6018, 0x9005, 0x1120, 0x2001,
+ 0x195f, 0x2004, 0x601a, 0x6003, 0x0007, 0x009e, 0x003e, 0x0005,
+ 0x0096, 0x080c, 0x8b04, 0x080c, 0x8c10, 0x080c, 0xbe37, 0x0120,
+ 0x6014, 0x2048, 0x080c, 0x1063, 0x080c, 0xa113, 0x009e, 0x0005,
+ 0x0002, 0xd0f5, 0xd10c, 0xd0f7, 0xd123, 0xd0f5, 0xd0f5, 0xd0f5,
+ 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5,
+ 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5, 0xd0f5, 0x080c, 0x0dfa, 0x0096,
+ 0x080c, 0x8b04, 0x6014, 0x2048, 0xa87c, 0xd0b4, 0x0138, 0x6003,
+ 0x0007, 0x2009, 0x0043, 0x080c, 0xa15d, 0x0010, 0x6003, 0x0004,
+ 0x080c, 0x8c10, 0x009e, 0x0005, 0x080c, 0x8b04, 0x080c, 0xbe37,
+ 0x0138, 0x6114, 0x0096, 0x2148, 0xa97c, 0x009e, 0xd1ec, 0x1138,
+ 0x080c, 0x84a6, 0x080c, 0xa0e3, 0x080c, 0x8c10, 0x0005, 0x080c,
+ 0xda8a, 0x0db0, 0x0cc8, 0x080c, 0x8b04, 0x2009, 0x0041, 0x0804,
+ 0xd294, 0x9182, 0x0040, 0x0002, 0xd140, 0xd142, 0xd140, 0xd140,
+ 0xd140, 0xd140, 0xd140, 0xd140, 0xd140, 0xd140, 0xd140, 0xd140,
+ 0xd140, 0xd140, 0xd140, 0xd140, 0xd140, 0xd143, 0xd140, 0xd140,
+ 0x080c, 0x0dfa, 0x0005, 0x00d6, 0x080c, 0x84a6, 0x00de, 0x080c,
+ 0xdae2, 0x080c, 0xa0e3, 0x0005, 0x9182, 0x0040, 0x0002, 0xd163,
+ 0xd163, 0xd163, 0xd163, 0xd163, 0xd163, 0xd163, 0xd163, 0xd163,
+ 0xd165, 0xd1d4, 0xd163, 0xd163, 0xd163, 0xd163, 0xd1d4, 0xd163,
+ 0xd163, 0xd163, 0xd163, 0x080c, 0x0dfa, 0x2001, 0x0105, 0x2004,
+ 0x9084, 0x1800, 0x01c8, 0x2001, 0x0132, 0x200c, 0x2001, 0x0131,
+ 0x2004, 0x9105, 0x1904, 0xd1d4, 0x2009, 0x180c, 0x2104, 0xd0d4,
+ 0x0904, 0xd1d4, 0xc0d4, 0x200a, 0x2009, 0x0105, 0x2104, 0x9084,
+ 0xe7fd, 0x9085, 0x0010, 0x200a, 0x2001, 0x187b, 0x2004, 0xd0e4,
+ 0x1528, 0x603b, 0x0000, 0x080c, 0x8bc0, 0x6014, 0x0096, 0x2048,
+ 0xa87c, 0xd0fc, 0x0188, 0x908c, 0x0003, 0x918e, 0x0002, 0x0508,
+ 0x2001, 0x180c, 0x2004, 0xd0d4, 0x11e0, 0x080c, 0x8ced, 0x2009,
+ 0x0041, 0x009e, 0x0804, 0xd294, 0x080c, 0x8ced, 0x6003, 0x0007,
+ 0x601b, 0x0000, 0x080c, 0x84a6, 0x009e, 0x0005, 0x2001, 0x0100,
+ 0x2004, 0x9082, 0x0005, 0x0aa8, 0x2001, 0x011f, 0x2004, 0x603a,
+ 0x0890, 0x2001, 0x180c, 0x200c, 0xc1d4, 0x2102, 0xd1cc, 0x0110,
+ 0x080c, 0x2bca, 0x080c, 0x8ced, 0x6014, 0x2048, 0xa97c, 0xd1ec,
+ 0x1130, 0x080c, 0x84a6, 0x080c, 0xa0e3, 0x009e, 0x0005, 0x080c,
+ 0xda8a, 0x0db8, 0x009e, 0x0005, 0x2001, 0x180c, 0x200c, 0xc1d4,
+ 0x2102, 0x0036, 0x080c, 0x8bc0, 0x080c, 0x8ced, 0x6014, 0x0096,
+ 0x2048, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0bc, 0x0188,
+ 0xa87c, 0x9084, 0x0003, 0x9086, 0x0002, 0x0140, 0xa8ac, 0x6330,
+ 0x931a, 0x6332, 0xa8b0, 0x632c, 0x931b, 0x632e, 0x6003, 0x0002,
+ 0x0080, 0x2019, 0x0004, 0x080c, 0xd830, 0x6018, 0x9005, 0x1128,
+ 0x2001, 0x195f, 0x2004, 0x8003, 0x601a, 0x6017, 0x0000, 0x6003,
+ 0x0007, 0x009e, 0x003e, 0x0005, 0x9182, 0x0040, 0x0002, 0xd223,
+ 0xd223, 0xd223, 0xd223, 0xd223, 0xd223, 0xd223, 0xd223, 0xd225,
+ 0xd223, 0xd223, 0xd223, 0xd223, 0xd223, 0xd223, 0xd223, 0xd223,
+ 0xd223, 0xd223, 0xd270, 0x080c, 0x0dfa, 0x6014, 0x0096, 0x2048,
+ 0xa834, 0xaa38, 0x6110, 0x00b6, 0x2158, 0xb900, 0x00be, 0xd1bc,
+ 0x1190, 0x920d, 0x1518, 0xa87c, 0xd0fc, 0x0128, 0x2009, 0x0041,
+ 0x009e, 0x0804, 0xd294, 0x6003, 0x0007, 0x601b, 0x0000, 0x080c,
+ 0x84a6, 0x009e, 0x0005, 0x6124, 0xd1f4, 0x1d58, 0x0006, 0x0046,
+ 0xacac, 0x9422, 0xa9b0, 0x2200, 0x910b, 0x6030, 0x9420, 0x6432,
+ 0x602c, 0x9109, 0x612e, 0x004e, 0x000e, 0x08d8, 0x6110, 0x00b6,
+ 0x2158, 0xb900, 0x00be, 0xd1bc, 0x1178, 0x2009, 0x180e, 0x210c,
+ 0xd19c, 0x0118, 0x6003, 0x0007, 0x0010, 0x6003, 0x0006, 0x00e9,
+ 0x080c, 0x84a8, 0x009e, 0x0005, 0x6003, 0x0002, 0x009e, 0x0005,
+ 0x6024, 0xd0f4, 0x0128, 0x080c, 0x1582, 0x1904, 0xd225, 0x0005,
+ 0x6014, 0x0096, 0x2048, 0xa834, 0xa938, 0x009e, 0x9105, 0x1120,
+ 0x080c, 0x1582, 0x1904, 0xd225, 0x0005, 0xd2fc, 0x0140, 0x8002,
+ 0x8000, 0x8212, 0x9291, 0x0000, 0x2009, 0x0009, 0x0010, 0x2009,
+ 0x0015, 0xaa9a, 0xa896, 0x0005, 0x9182, 0x0040, 0x0208, 0x0062,
+ 0x9186, 0x0013, 0x0120, 0x9186, 0x0014, 0x190c, 0x0dfa, 0x6024,
+ 0xd0dc, 0x090c, 0x0dfa, 0x0005, 0xd2b8, 0xd2c4, 0xd2d0, 0xd2dc,
+ 0xd2b8, 0xd2b8, 0xd2b8, 0xd2b8, 0xd2bf, 0xd2ba, 0xd2ba, 0xd2b8,
+ 0xd2b8, 0xd2b8, 0xd2b8, 0xd2ba, 0xd2b8, 0xd2ba, 0xd2b8, 0xd2bf,
+ 0x080c, 0x0dfa, 0x6024, 0xd0dc, 0x090c, 0x0dfa, 0x0005, 0x6014,
+ 0x9005, 0x190c, 0x0dfa, 0x0005, 0x6003, 0x0001, 0x6106, 0x080c,
+ 0x8679, 0x0126, 0x2091, 0x8000, 0x080c, 0x8c10, 0x012e, 0x0005,
+ 0x6003, 0x0001, 0x6106, 0x080c, 0x8679, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x8c10, 0x012e, 0x0005, 0x6003, 0x0003, 0x6106, 0x2c10,
+ 0x080c, 0x1afe, 0x0126, 0x2091, 0x8000, 0x080c, 0x86de, 0x080c,
+ 0x8ced, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x0036, 0x0096,
+ 0x9182, 0x0040, 0x0023, 0x009e, 0x003e, 0x012e, 0x0005, 0xd30b,
+ 0xd30d, 0xd31f, 0xd339, 0xd30b, 0xd30b, 0xd30b, 0xd30b, 0xd30b,
+ 0xd30b, 0xd30b, 0xd30b, 0xd30b, 0xd30b, 0xd30b, 0xd30b, 0xd30b,
+ 0xd30b, 0xd30b, 0xd30b, 0x080c, 0x0dfa, 0x6014, 0x2048, 0xa87c,
+ 0xd0fc, 0x01f8, 0x909c, 0x0003, 0x939e, 0x0003, 0x01d0, 0x6003,
+ 0x0001, 0x6106, 0x080c, 0x8679, 0x080c, 0x8c10, 0x0470, 0x6014,
+ 0x2048, 0xa87c, 0xd0fc, 0x0168, 0x909c, 0x0003, 0x939e, 0x0003,
+ 0x0140, 0x6003, 0x0001, 0x6106, 0x080c, 0x8679, 0x080c, 0x8c10,
+ 0x00e0, 0x901e, 0x6316, 0x631a, 0x2019, 0x0004, 0x080c, 0xd830,
+ 0x00a0, 0x6014, 0x2048, 0xa87c, 0xd0fc, 0x0d98, 0x909c, 0x0003,
+ 0x939e, 0x0003, 0x0d70, 0x6003, 0x0003, 0x6106, 0x2c10, 0x080c,
+ 0x1afe, 0x080c, 0x86de, 0x080c, 0x8ced, 0x0005, 0x080c, 0x8b04,
+ 0x6114, 0x81ff, 0x0158, 0x0096, 0x2148, 0x080c, 0xdb2e, 0x0036,
+ 0x2019, 0x0029, 0x080c, 0xd830, 0x003e, 0x009e, 0x080c, 0xa113,
+ 0x080c, 0x8c10, 0x0005, 0x080c, 0x8bc0, 0x6114, 0x81ff, 0x0158,
+ 0x0096, 0x2148, 0x080c, 0xdb2e, 0x0036, 0x2019, 0x0029, 0x080c,
+ 0xd830, 0x003e, 0x009e, 0x080c, 0xa113, 0x080c, 0x8ced, 0x0005,
+ 0x9182, 0x0085, 0x0002, 0xd38a, 0xd388, 0xd388, 0xd396, 0xd388,
+ 0xd388, 0xd388, 0xd388, 0xd388, 0xd388, 0xd388, 0xd388, 0xd388,
+ 0x080c, 0x0dfa, 0x6003, 0x000b, 0x6106, 0x080c, 0x8679, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x8c10, 0x012e, 0x0005, 0x0026, 0x00e6,
+ 0x080c, 0xda81, 0x0118, 0x080c, 0xa0e3, 0x0450, 0x2071, 0x0260,
+ 0x7224, 0x6216, 0x2001, 0x180e, 0x2004, 0xd0e4, 0x0150, 0x6010,
+ 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00, 0x2011, 0x014e, 0x080c,
+ 0xa403, 0x7220, 0x080c, 0xd6d6, 0x0118, 0x6007, 0x0086, 0x0040,
+ 0x6007, 0x0087, 0x7224, 0x9296, 0xffff, 0x1110, 0x6007, 0x0086,
+ 0x6003, 0x0001, 0x080c, 0x8679, 0x080c, 0x8c10, 0x080c, 0x8ced,
+ 0x00ee, 0x002e, 0x0005, 0x9186, 0x0013, 0x1160, 0x6004, 0x908a,
+ 0x0085, 0x0a0c, 0x0dfa, 0x908a, 0x0092, 0x1a0c, 0x0dfa, 0x9082,
+ 0x0085, 0x00a2, 0x9186, 0x0027, 0x0130, 0x9186, 0x0014, 0x0118,
+ 0x080c, 0xa178, 0x0050, 0x2001, 0x0007, 0x080c, 0x641c, 0x080c,
+ 0x8b04, 0x080c, 0xa113, 0x080c, 0x8c10, 0x0005, 0xd3fb, 0xd3fd,
+ 0xd3fd, 0xd3fb, 0xd3fb, 0xd3fb, 0xd3fb, 0xd3fb, 0xd3fb, 0xd3fb,
+ 0xd3fb, 0xd3fb, 0xd3fb, 0x080c, 0x0dfa, 0x080c, 0x8b04, 0x080c,
+ 0xa113, 0x080c, 0x8c10, 0x0005, 0x9182, 0x0085, 0x0a0c, 0x0dfa,
+ 0x9182, 0x0092, 0x1a0c, 0x0dfa, 0x9182, 0x0085, 0x0002, 0xd41c,
+ 0xd41c, 0xd41c, 0xd41e, 0xd41c, 0xd41c, 0xd41c, 0xd41c, 0xd41c,
+ 0xd41c, 0xd41c, 0xd41c, 0xd41c, 0x080c, 0x0dfa, 0x0005, 0x9186,
+ 0x0013, 0x0148, 0x9186, 0x0014, 0x0130, 0x9186, 0x0027, 0x0118,
+ 0x080c, 0xa178, 0x0030, 0x080c, 0x8b04, 0x080c, 0xa113, 0x080c,
+ 0x8c10, 0x0005, 0x0036, 0x080c, 0xdae2, 0x6043, 0x0000, 0x2019,
+ 0x000b, 0x0031, 0x6023, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005,
+ 0x0126, 0x0036, 0x2091, 0x8000, 0x0086, 0x2c40, 0x0096, 0x904e,
+ 0x080c, 0x9a58, 0x009e, 0x008e, 0x1550, 0x0076, 0x2c38, 0x080c,
+ 0x9b03, 0x007e, 0x1520, 0x6000, 0x9086, 0x0000, 0x0500, 0x6020,
+ 0x9086, 0x0007, 0x01e0, 0x0096, 0x601c, 0xd084, 0x0140, 0x080c,
+ 0xdae2, 0x080c, 0xc551, 0x080c, 0x19b4, 0x6023, 0x0007, 0x6014,
+ 0x2048, 0x080c, 0xbe37, 0x0110, 0x080c, 0xd830, 0x009e, 0x6017,
+ 0x0000, 0x080c, 0xdae2, 0x6023, 0x0007, 0x080c, 0xc551, 0x003e,
+ 0x012e, 0x0005, 0x00f6, 0x00c6, 0x00b6, 0x0036, 0x0156, 0x2079,
+ 0x0260, 0x7938, 0x783c, 0x080c, 0x276e, 0x15c8, 0x0016, 0x00c6,
+ 0x080c, 0x649f, 0x1590, 0x001e, 0x00c6, 0x2160, 0x080c, 0xc54e,
+ 0x00ce, 0x002e, 0x0026, 0x0016, 0x2019, 0x0029, 0x080c, 0x9bc9,
+ 0x080c, 0x8803, 0x0076, 0x903e, 0x080c, 0x86f1, 0x007e, 0x001e,
+ 0x0076, 0x903e, 0x080c, 0xd5f6, 0x007e, 0x0026, 0xba04, 0x9294,
+ 0xff00, 0x8217, 0x9286, 0x0006, 0x0118, 0x9286, 0x0004, 0x1118,
+ 0xbaa0, 0x080c, 0x3248, 0x002e, 0xbcb0, 0x001e, 0x080c, 0x5f45,
+ 0xbe12, 0xbd16, 0xbcb2, 0x9006, 0x0010, 0x00ce, 0x001e, 0x015e,
+ 0x003e, 0x00be, 0x00ce, 0x00fe, 0x0005, 0x00c6, 0x00d6, 0x00b6,
+ 0x0016, 0x2009, 0x1823, 0x2104, 0x9086, 0x0074, 0x1904, 0xd51e,
+ 0x2069, 0x0260, 0x6944, 0x9182, 0x0100, 0x06e0, 0x6940, 0x9184,
+ 0x8000, 0x0904, 0xd51b, 0x2001, 0x1954, 0x2004, 0x9005, 0x1140,
+ 0x6010, 0x2058, 0xb8b0, 0x9005, 0x0118, 0x9184, 0x0800, 0x0598,
+ 0x6948, 0x918a, 0x0001, 0x0648, 0x080c, 0xdb96, 0x0118, 0x6978,
+ 0xd1fc, 0x11b8, 0x2009, 0x0205, 0x200b, 0x0001, 0x693c, 0x81ff,
+ 0x1198, 0x6944, 0x9182, 0x0100, 0x02a8, 0x6940, 0x81ff, 0x1178,
+ 0x6948, 0x918a, 0x0001, 0x0288, 0x6950, 0x918a, 0x0001, 0x0298,
+ 0x00d0, 0x6017, 0x0100, 0x00a0, 0x6017, 0x0300, 0x0088, 0x6017,
+ 0x0500, 0x0070, 0x6017, 0x0700, 0x0058, 0x6017, 0x0900, 0x0040,
+ 0x6017, 0x0b00, 0x0028, 0x6017, 0x0f00, 0x0010, 0x6017, 0x2d00,
+ 0x9085, 0x0001, 0x0008, 0x9006, 0x001e, 0x00be, 0x00de, 0x00ce,
+ 0x0005, 0x00c6, 0x00b6, 0x0026, 0x0036, 0x0156, 0x6210, 0x2258,
+ 0xbb04, 0x9394, 0x00ff, 0x9286, 0x0006, 0x0180, 0x9286, 0x0004,
+ 0x0168, 0x9394, 0xff00, 0x8217, 0x9286, 0x0006, 0x0138, 0x9286,
+ 0x0004, 0x0120, 0x080c, 0x64ae, 0x0804, 0xd585, 0x2011, 0x0276,
+ 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019, 0x000a, 0x080c, 0xb0d0,
+ 0x009e, 0x15a0, 0x2011, 0x027a, 0x20a9, 0x0004, 0x0096, 0x2b48,
+ 0x2019, 0x0006, 0x080c, 0xb0d0, 0x009e, 0x1540, 0x0046, 0x0016,
+ 0xbaa0, 0x2220, 0x9006, 0x2009, 0x185c, 0x210c, 0x0038, 0x2009,
+ 0x0029, 0x080c, 0xd885, 0xb800, 0xc0e5, 0xb802, 0x2019, 0x0029,
+ 0x080c, 0x8803, 0x0076, 0x2039, 0x0000, 0x080c, 0x86f1, 0x2c08,
+ 0x080c, 0xd5f6, 0x007e, 0x2001, 0x0007, 0x080c, 0x641c, 0x2001,
+ 0x0007, 0x080c, 0x63f0, 0x001e, 0x004e, 0x9006, 0x015e, 0x003e,
+ 0x002e, 0x00be, 0x00ce, 0x0005, 0x00d6, 0x2069, 0x026e, 0x6800,
+ 0x9086, 0x0800, 0x0118, 0x6017, 0x0000, 0x0008, 0x9006, 0x00de,
+ 0x0005, 0x00b6, 0x00f6, 0x0016, 0x0026, 0x0036, 0x0156, 0x2079,
+ 0x026c, 0x7930, 0x7834, 0x080c, 0x276e, 0x11d0, 0x080c, 0x649f,
+ 0x11b8, 0x2011, 0x0270, 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019,
+ 0x000a, 0x080c, 0xb0d0, 0x009e, 0x1158, 0x2011, 0x0274, 0x20a9,
+ 0x0004, 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xb0d0, 0x009e,
+ 0x015e, 0x003e, 0x002e, 0x001e, 0x00fe, 0x00be, 0x0005, 0x00b6,
+ 0x0006, 0x0016, 0x0026, 0x0036, 0x0156, 0x2011, 0x0263, 0x2204,
+ 0x8211, 0x220c, 0x080c, 0x276e, 0x11d0, 0x080c, 0x649f, 0x11b8,
+ 0x2011, 0x0276, 0x20a9, 0x0004, 0x0096, 0x2b48, 0x2019, 0x000a,
+ 0x080c, 0xb0d0, 0x009e, 0x1158, 0x2011, 0x027a, 0x20a9, 0x0004,
+ 0x0096, 0x2b48, 0x2019, 0x0006, 0x080c, 0xb0d0, 0x009e, 0x015e,
+ 0x003e, 0x002e, 0x001e, 0x000e, 0x00be, 0x0005, 0x00e6, 0x00c6,
+ 0x0086, 0x0076, 0x0066, 0x0056, 0x0046, 0x0026, 0x0126, 0x2091,
+ 0x8000, 0x2740, 0x2029, 0x19c8, 0x252c, 0x2021, 0x19ce, 0x2424,
+ 0x2061, 0x1cd0, 0x2071, 0x1800, 0x7650, 0x7070, 0x81ff, 0x0150,
+ 0x0006, 0x9186, 0x1a8a, 0x000e, 0x0128, 0x8001, 0x9602, 0x1a04,
+ 0xd68f, 0x0018, 0x9606, 0x0904, 0xd68f, 0x2100, 0x9c06, 0x0904,
+ 0xd686, 0x080c, 0xd8c6, 0x1904, 0xd686, 0x080c, 0xdbb3, 0x0904,
+ 0xd686, 0x080c, 0xd8b6, 0x0904, 0xd686, 0x6720, 0x9786, 0x0001,
+ 0x1148, 0x080c, 0x32df, 0x0904, 0xd6aa, 0x6004, 0x9086, 0x0000,
+ 0x1904, 0xd6aa, 0x9786, 0x0004, 0x0904, 0xd6aa, 0x9786, 0x0007,
+ 0x0904, 0xd686, 0x2500, 0x9c06, 0x0904, 0xd686, 0x2400, 0x9c06,
+ 0x05e8, 0x88ff, 0x0118, 0x6054, 0x9906, 0x15c0, 0x0096, 0x6000,
+ 0x9086, 0x0004, 0x1120, 0x0016, 0x080c, 0x19b4, 0x001e, 0x9786,
+ 0x000a, 0x0148, 0x080c, 0xc03f, 0x1130, 0x080c, 0xaa81, 0x009e,
+ 0x080c, 0xa113, 0x0418, 0x6014, 0x2048, 0x080c, 0xbe37, 0x01d8,
+ 0x9786, 0x0003, 0x1570, 0xa867, 0x0103, 0xa87c, 0xd0cc, 0x0130,
+ 0x0096, 0xa878, 0x2048, 0x080c, 0x0fe3, 0x009e, 0xab7a, 0xa877,
+ 0x0000, 0x080c, 0xdb2e, 0x0016, 0x080c, 0xc12d, 0x080c, 0x6adc,
+ 0x001e, 0x080c, 0xc022, 0x009e, 0x080c, 0xa113, 0x9ce0, 0x0018,
+ 0x2001, 0x1819, 0x2004, 0x9c02, 0x1210, 0x0804, 0xd60a, 0x012e,
+ 0x002e, 0x004e, 0x005e, 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee,
+ 0x0005, 0x9786, 0x0006, 0x1150, 0x9386, 0x0005, 0x0128, 0x080c,
+ 0xdb2e, 0x080c, 0xd830, 0x08f8, 0x009e, 0x0c00, 0x9786, 0x000a,
+ 0x0968, 0x0808, 0x81ff, 0x09d0, 0x9180, 0x0001, 0x2004, 0x9086,
+ 0x0018, 0x0130, 0x9180, 0x0001, 0x2004, 0x9086, 0x002d, 0x1970,
+ 0x6000, 0x9086, 0x0002, 0x1950, 0x080c, 0xc02e, 0x0130, 0x080c,
+ 0xc03f, 0x1920, 0x080c, 0xaa81, 0x0038, 0x080c, 0x31b4, 0x080c,
+ 0xc03f, 0x1110, 0x080c, 0xaa81, 0x080c, 0xa113, 0x0804, 0xd686,
+ 0xa864, 0x9084, 0x00ff, 0x9086, 0x0039, 0x0005, 0x00c6, 0x00e6,
+ 0x0016, 0x2c08, 0x2170, 0x9006, 0x080c, 0xd857, 0x001e, 0x0120,
+ 0x6020, 0x9084, 0x000f, 0x001b, 0x00ee, 0x00ce, 0x0005, 0xd6f5,
+ 0xd6f5, 0xd6f5, 0xd6f5, 0xd6f5, 0xd6f5, 0xd6f7, 0xd6f5, 0xd6f5,
+ 0xd6f5, 0xd6f5, 0xa113, 0xa113, 0xd6f5, 0x9006, 0x0005, 0x0036,
+ 0x0046, 0x0016, 0x7010, 0x00b6, 0x2058, 0xbca0, 0x00be, 0x2c00,
+ 0x2009, 0x0020, 0x080c, 0xd885, 0x001e, 0x004e, 0x2019, 0x0002,
+ 0x080c, 0xd440, 0x003e, 0x9085, 0x0001, 0x0005, 0x0096, 0x080c,
+ 0xbe37, 0x0140, 0x6014, 0x904d, 0x080c, 0xba56, 0x687b, 0x0005,
+ 0x080c, 0x6ae9, 0x009e, 0x080c, 0xa113, 0x9085, 0x0001, 0x0005,
+ 0x2001, 0x0001, 0x080c, 0x63dc, 0x0156, 0x0016, 0x0026, 0x0036,
+ 0x20a9, 0x0004, 0x2019, 0x1805, 0x2011, 0x0276, 0x080c, 0xb0bc,
+ 0x003e, 0x002e, 0x001e, 0x015e, 0x9005, 0x0005, 0x00f6, 0x00e6,
+ 0x00c6, 0x0086, 0x0076, 0x0066, 0x00b6, 0x0126, 0x2091, 0x8000,
+ 0x2740, 0x2061, 0x1cd0, 0x2079, 0x0001, 0x8fff, 0x0904, 0xd790,
+ 0x2071, 0x1800, 0x7650, 0x7070, 0x8001, 0x9602, 0x1a04, 0xd790,
+ 0x88ff, 0x0120, 0x2800, 0x9c06, 0x1590, 0x2078, 0x080c, 0xd8b6,
+ 0x0570, 0x2400, 0x9c06, 0x0558, 0x6720, 0x9786, 0x0006, 0x1538,
+ 0x9786, 0x0007, 0x0520, 0x88ff, 0x1140, 0x6010, 0x9b06, 0x11f8,
+ 0x85ff, 0x0118, 0x6054, 0x9106, 0x11d0, 0x0096, 0x601c, 0xd084,
+ 0x0140, 0x080c, 0xdae2, 0x080c, 0xc551, 0x080c, 0x19b4, 0x6023,
+ 0x0007, 0x6014, 0x2048, 0x080c, 0xbe37, 0x0120, 0x0046, 0x080c,
+ 0xd830, 0x004e, 0x009e, 0x080c, 0xa113, 0x88ff, 0x1198, 0x9ce0,
+ 0x0018, 0x2001, 0x1819, 0x2004, 0x9c02, 0x1210, 0x0804, 0xd745,
+ 0x9006, 0x012e, 0x00be, 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee,
+ 0x00fe, 0x0005, 0x98c5, 0x0001, 0x0ca0, 0x00b6, 0x0076, 0x0056,
+ 0x0086, 0x9046, 0x2029, 0x0001, 0x2c20, 0x2019, 0x0002, 0x6210,
+ 0x2258, 0x0096, 0x904e, 0x080c, 0x9a58, 0x009e, 0x008e, 0x903e,
+ 0x080c, 0x9b03, 0x080c, 0xd736, 0x005e, 0x007e, 0x00be, 0x0005,
+ 0x00b6, 0x0046, 0x0056, 0x0076, 0x00c6, 0x0156, 0x2c20, 0x2128,
+ 0x20a9, 0x007f, 0x900e, 0x0016, 0x0036, 0x080c, 0x649f, 0x1190,
+ 0x0056, 0x0086, 0x9046, 0x2508, 0x2029, 0x0001, 0x0096, 0x904e,
+ 0x080c, 0x9a58, 0x009e, 0x008e, 0x903e, 0x080c, 0x9b03, 0x080c,
+ 0xd736, 0x005e, 0x003e, 0x001e, 0x8108, 0x1f04, 0xd7c3, 0x015e,
+ 0x00ce, 0x007e, 0x005e, 0x004e, 0x00be, 0x0005, 0x00b6, 0x0076,
+ 0x0056, 0x6210, 0x2258, 0x0086, 0x9046, 0x2029, 0x0001, 0x2019,
+ 0x0048, 0x0096, 0x904e, 0x080c, 0x9a58, 0x009e, 0x008e, 0x903e,
+ 0x080c, 0x9b03, 0x2c20, 0x080c, 0xd736, 0x005e, 0x007e, 0x00be,
+ 0x0005, 0x00b6, 0x0046, 0x0056, 0x0076, 0x00c6, 0x0156, 0x2c20,
+ 0x20a9, 0x0800, 0x900e, 0x0016, 0x0036, 0x080c, 0x649f, 0x11a0,
+ 0x0086, 0x9046, 0x2828, 0x0046, 0x2021, 0x0001, 0x080c, 0xdac6,
+ 0x004e, 0x0096, 0x904e, 0x080c, 0x9a58, 0x009e, 0x008e, 0x903e,
+ 0x080c, 0x9b03, 0x080c, 0xd736, 0x003e, 0x001e, 0x8108, 0x1f04,
+ 0xd80b, 0x015e, 0x00ce, 0x007e, 0x005e, 0x004e, 0x00be, 0x0005,
+ 0x0016, 0x00f6, 0x080c, 0xbe35, 0x0198, 0xa864, 0x9084, 0x00ff,
+ 0x9086, 0x0046, 0x0180, 0xa800, 0x907d, 0x0138, 0xa803, 0x0000,
+ 0xab82, 0x080c, 0x6ae9, 0x2f48, 0x0cb0, 0xab82, 0x080c, 0x6ae9,
+ 0x00fe, 0x001e, 0x0005, 0xa800, 0x907d, 0x0130, 0xa803, 0x0000,
+ 0x080c, 0x6ae9, 0x2f48, 0x0cb8, 0x080c, 0x6ae9, 0x0c88, 0x00e6,
+ 0x0046, 0x0036, 0x2061, 0x1cd0, 0x9005, 0x1138, 0x2071, 0x1800,
+ 0x7450, 0x7070, 0x8001, 0x9402, 0x12d8, 0x2100, 0x9c06, 0x0168,
+ 0x6000, 0x9086, 0x0000, 0x0148, 0x6008, 0x9206, 0x1130, 0x6010,
+ 0x91a0, 0x0004, 0x2424, 0x9406, 0x0140, 0x9ce0, 0x0018, 0x2001,
+ 0x1819, 0x2004, 0x9c02, 0x1220, 0x0c40, 0x9085, 0x0001, 0x0008,
+ 0x9006, 0x003e, 0x004e, 0x00ee, 0x0005, 0x0096, 0x0006, 0x080c,
+ 0x1031, 0x000e, 0x090c, 0x0dfa, 0xaae2, 0xa867, 0x010d, 0xa88e,
+ 0x0026, 0x2010, 0x080c, 0xbe25, 0x2001, 0x0000, 0x0120, 0x2200,
+ 0x9080, 0x0015, 0x2004, 0x002e, 0xa87a, 0x9186, 0x0020, 0x0110,
+ 0xa8e3, 0xffff, 0xa986, 0xac76, 0xa87f, 0x0000, 0x2001, 0x1966,
+ 0x2004, 0xa882, 0x9006, 0xa802, 0xa86a, 0xa88a, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6ae9, 0x012e, 0x009e, 0x0005, 0x6700, 0x9786,
+ 0x0000, 0x0158, 0x9786, 0x0001, 0x0140, 0x9786, 0x000a, 0x0128,
+ 0x9786, 0x0009, 0x0110, 0x9085, 0x0001, 0x0005, 0x00e6, 0x6010,
+ 0x9075, 0x0138, 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x9206, 0x00ee,
+ 0x0005, 0x9085, 0x0001, 0x0cd8, 0x0016, 0x6004, 0x908e, 0x001e,
+ 0x11a0, 0x8007, 0x6134, 0x918c, 0x00ff, 0x9105, 0x6036, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x6023, 0x0005, 0x2001, 0x195f, 0x2004,
+ 0x601a, 0x080c, 0x8679, 0x080c, 0x8c10, 0x001e, 0x0005, 0xa001,
+ 0xa001, 0x0005, 0x6024, 0xd0e4, 0x0158, 0xd0cc, 0x0118, 0x080c,
+ 0xc171, 0x0030, 0x080c, 0xdae2, 0x080c, 0x84a6, 0x080c, 0xa0e3,
+ 0x0005, 0x9280, 0x0008, 0x2004, 0x9084, 0x000f, 0x0002, 0xd915,
+ 0xd915, 0xd915, 0xd917, 0xd915, 0xd917, 0xd917, 0xd915, 0xd917,
+ 0xd915, 0xd915, 0xd915, 0xd915, 0xd915, 0x9006, 0x0005, 0x9085,
+ 0x0001, 0x0005, 0x9280, 0x0008, 0x2004, 0x9084, 0x000f, 0x0002,
+ 0xd92e, 0xd92e, 0xd92e, 0xd92e, 0xd92e, 0xd92e, 0xd93b, 0xd92e,
+ 0xd92e, 0xd92e, 0xd92e, 0xd92e, 0xd92e, 0xd92e, 0x6007, 0x003b,
+ 0x602f, 0x0009, 0x6017, 0x2a00, 0x6003, 0x0001, 0x080c, 0x8679,
+ 0x080c, 0x8c10, 0x0005, 0x0096, 0x00c6, 0x2260, 0x080c, 0xdae2,
+ 0x6043, 0x0000, 0x6024, 0xc0f4, 0xc0e4, 0x6026, 0x603b, 0x0000,
+ 0x00ce, 0x00d6, 0x2268, 0x9186, 0x0007, 0x1904, 0xd994, 0x6814,
+ 0x9005, 0x0138, 0x2048, 0xa87c, 0xd0fc, 0x1118, 0x00de, 0x009e,
+ 0x08a8, 0x6007, 0x003a, 0x6003, 0x0001, 0x080c, 0x8679, 0x080c,
+ 0x8c10, 0x00c6, 0x2d60, 0x6100, 0x9186, 0x0002, 0x1904, 0xda0b,
+ 0x6014, 0x9005, 0x1138, 0x6000, 0x9086, 0x0007, 0x190c, 0x0dfa,
+ 0x0804, 0xda0b, 0x2048, 0x080c, 0xbe37, 0x1130, 0x0028, 0x2048,
+ 0xa800, 0x9005, 0x1de0, 0x2900, 0x2048, 0xa87c, 0x9084, 0x0003,
+ 0x9086, 0x0002, 0x1168, 0xa87c, 0xc0dc, 0xc0f4, 0xa87e, 0xa880,
+ 0xc0fc, 0xa882, 0x2009, 0x0043, 0x080c, 0xd294, 0x0804, 0xda0b,
+ 0x2009, 0x0041, 0x0804, 0xda05, 0x9186, 0x0005, 0x15a0, 0x6814,
+ 0x2048, 0xa87c, 0xd0bc, 0x1120, 0x00de, 0x009e, 0x0804, 0xd92e,
+ 0xd0b4, 0x0128, 0xd0fc, 0x090c, 0x0dfa, 0x0804, 0xd94f, 0x6007,
+ 0x003a, 0x6003, 0x0001, 0x080c, 0x8679, 0x080c, 0x8c10, 0x00c6,
+ 0x2d60, 0x6100, 0x9186, 0x0002, 0x0120, 0x9186, 0x0004, 0x1904,
+ 0xda0b, 0x6814, 0x2048, 0xa97c, 0xc1f4, 0xc1dc, 0xa97e, 0xa980,
+ 0xc1fc, 0xc1bc, 0xa982, 0x00f6, 0x2c78, 0x080c, 0x16db, 0x00fe,
+ 0x2009, 0x0042, 0x04d0, 0x0036, 0x080c, 0x1031, 0x090c, 0x0dfa,
+ 0xa867, 0x010d, 0x9006, 0xa802, 0xa86a, 0xa88a, 0x2d18, 0xab8e,
+ 0xa887, 0x0045, 0x2c00, 0xa892, 0x6038, 0xa8a2, 0x2360, 0x6024,
+ 0xc0dd, 0x6026, 0x6010, 0x00b6, 0x2058, 0xb8a0, 0x00be, 0x2004,
+ 0x6354, 0xab7a, 0xa876, 0x9006, 0xa87e, 0xa882, 0xad9a, 0xae96,
+ 0xa89f, 0x0001, 0x080c, 0x6ae9, 0x2019, 0x0045, 0x6008, 0x2068,
+ 0x080c, 0xd440, 0x2d00, 0x600a, 0x6023, 0x0006, 0x6003, 0x0007,
+ 0x901e, 0x631a, 0x6342, 0x003e, 0x0038, 0x6043, 0x0000, 0x6003,
+ 0x0007, 0x080c, 0xd294, 0x00ce, 0x00de, 0x009e, 0x0005, 0x9186,
+ 0x0013, 0x1128, 0x6004, 0x9082, 0x0085, 0x2008, 0x00c2, 0x9186,
+ 0x0027, 0x1178, 0x080c, 0x8b04, 0x0036, 0x0096, 0x6014, 0x2048,
+ 0x2019, 0x0004, 0x080c, 0xd830, 0x009e, 0x003e, 0x080c, 0x8c10,
+ 0x0005, 0x9186, 0x0014, 0x0d70, 0x080c, 0xa178, 0x0005, 0xda3e,
+ 0xda3c, 0xda3c, 0xda3c, 0xda3c, 0xda3c, 0xda3e, 0xda3c, 0xda3c,
+ 0xda3c, 0xda3c, 0xda3c, 0xda3c, 0x080c, 0x0dfa, 0x080c, 0x8b04,
+ 0x6003, 0x000c, 0x080c, 0x8c10, 0x0005, 0x9182, 0x0092, 0x1220,
+ 0x9182, 0x0085, 0x0208, 0x001a, 0x080c, 0xa178, 0x0005, 0xda5c,
+ 0xda5c, 0xda5c, 0xda5c, 0xda5e, 0xda7e, 0xda5c, 0xda5c, 0xda5c,
+ 0xda5c, 0xda5c, 0xda5c, 0xda5c, 0x080c, 0x0dfa, 0x00d6, 0x2c68,
+ 0x080c, 0xa08d, 0x01b0, 0x6003, 0x0001, 0x6007, 0x001e, 0x2009,
+ 0x026e, 0x210c, 0x613a, 0x2009, 0x026f, 0x210c, 0x613e, 0x600b,
+ 0xffff, 0x6910, 0x6112, 0x6023, 0x0004, 0x080c, 0x8679, 0x080c,
+ 0x8c10, 0x2d60, 0x080c, 0xa0e3, 0x00de, 0x0005, 0x080c, 0xa0e3,
+ 0x0005, 0x00e6, 0x6010, 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0ec,
+ 0x00ee, 0x0005, 0x2009, 0x187b, 0x210c, 0xd1ec, 0x05b0, 0x6003,
+ 0x0002, 0x6024, 0xc0e5, 0x6026, 0xd0cc, 0x0150, 0x2001, 0x1960,
+ 0x2004, 0x6042, 0x2009, 0x187b, 0x210c, 0xd1f4, 0x1520, 0x00a0,
+ 0x2009, 0x187b, 0x210c, 0xd1f4, 0x0128, 0x6024, 0xc0e4, 0x6026,
+ 0x9006, 0x00d8, 0x2001, 0x1960, 0x200c, 0x2001, 0x195e, 0x2004,
+ 0x9100, 0x9080, 0x000a, 0x6042, 0x6010, 0x00b6, 0x2058, 0xb8ac,
+ 0x00be, 0x0008, 0x2104, 0x9005, 0x0118, 0x9088, 0x0003, 0x0cd0,
+ 0x2c0a, 0x600f, 0x0000, 0x9085, 0x0001, 0x0005, 0x0016, 0x00c6,
+ 0x00e6, 0x6154, 0xb8ac, 0x2060, 0x8cff, 0x0180, 0x84ff, 0x1118,
+ 0x6054, 0x9106, 0x1138, 0x600c, 0x2072, 0x080c, 0x84a6, 0x080c,
+ 0xa0e3, 0x0010, 0x9cf0, 0x0003, 0x2e64, 0x0c70, 0x00ee, 0x00ce,
+ 0x001e, 0x0005, 0x00d6, 0x00b6, 0x6010, 0x2058, 0xb8ac, 0x2068,
+ 0x9005, 0x0130, 0x9c06, 0x0110, 0x680c, 0x0cd0, 0x600c, 0x680e,
+ 0x00be, 0x00de, 0x0005, 0x0026, 0x0036, 0x0156, 0x2011, 0x182b,
+ 0x2204, 0x9084, 0x00ff, 0x2019, 0x026e, 0x2334, 0x9636, 0x1508,
+ 0x8318, 0x2334, 0x2204, 0x9084, 0xff00, 0x9636, 0x11d0, 0x2011,
+ 0x0270, 0x20a9, 0x0004, 0x6010, 0x0096, 0x2048, 0x2019, 0x000a,
+ 0x080c, 0xb0d0, 0x009e, 0x1168, 0x2011, 0x0274, 0x20a9, 0x0004,
+ 0x6010, 0x0096, 0x2048, 0x2019, 0x0006, 0x080c, 0xb0d0, 0x009e,
+ 0x1100, 0x015e, 0x003e, 0x002e, 0x0005, 0x00e6, 0x2071, 0x1800,
+ 0x080c, 0x5ebe, 0x080c, 0x2f6c, 0x00ee, 0x0005, 0x00e6, 0x6010,
+ 0x00b6, 0x2058, 0xb800, 0x00be, 0xd0fc, 0x0108, 0x0011, 0x00ee,
+ 0x0005, 0xa880, 0xc0e5, 0xa882, 0x0005, 0x00e6, 0x00d6, 0x00c6,
+ 0x0076, 0x0066, 0x0056, 0x0046, 0x0026, 0x0016, 0x0126, 0x2091,
+ 0x8000, 0x2029, 0x19c8, 0x252c, 0x2021, 0x19ce, 0x2424, 0x2061,
+ 0x1cd0, 0x2071, 0x1800, 0x7650, 0x7070, 0x9606, 0x0578, 0x6720,
+ 0x9786, 0x0001, 0x0118, 0x9786, 0x0008, 0x1500, 0x2500, 0x9c06,
+ 0x01e8, 0x2400, 0x9c06, 0x01d0, 0x080c, 0xd8b6, 0x01b8, 0x080c,
+ 0xd8c6, 0x11a0, 0x6000, 0x9086, 0x0004, 0x1120, 0x0016, 0x080c,
+ 0x19b4, 0x001e, 0x080c, 0xc02e, 0x1110, 0x080c, 0x31b4, 0x080c,
+ 0xc03f, 0x1110, 0x080c, 0xaa81, 0x080c, 0xa113, 0x9ce0, 0x0018,
+ 0x2001, 0x1819, 0x2004, 0x9c02, 0x1208, 0x0858, 0x012e, 0x001e,
+ 0x002e, 0x004e, 0x005e, 0x006e, 0x007e, 0x00ce, 0x00de, 0x00ee,
+ 0x0005, 0x2001, 0x1810, 0x2004, 0xd0dc, 0x0005, 0x0006, 0x2001,
+ 0x1836, 0x2004, 0xd09c, 0x000e, 0x0005, 0x0006, 0x0036, 0x0046,
+ 0x080c, 0xc539, 0x0168, 0x2019, 0xffff, 0x9005, 0x0128, 0x6010,
+ 0x00b6, 0x2058, 0xbba0, 0x00be, 0x2021, 0x0004, 0x080c, 0x4cbc,
+ 0x004e, 0x003e, 0x000e, 0x6004, 0x9086, 0x0001, 0x1128, 0x080c,
+ 0x9bc9, 0x080c, 0xa113, 0x9006, 0x0005, 0x00e6, 0x00c6, 0x00b6,
+ 0x0046, 0x2061, 0x1cd0, 0x2071, 0x1800, 0x7450, 0x7070, 0x8001,
+ 0x9402, 0x12d8, 0x2100, 0x9c06, 0x0168, 0x6000, 0x9086, 0x0000,
+ 0x0148, 0x6010, 0x2058, 0xb8a0, 0x9206, 0x1120, 0x6004, 0x9086,
+ 0x0002, 0x0140, 0x9ce0, 0x0018, 0x2001, 0x1819, 0x2004, 0x9c02,
+ 0x1220, 0x0c40, 0x9085, 0x0001, 0x0008, 0x9006, 0x004e, 0x00be,
+ 0x00ce, 0x00ee, 0x0005, 0x0126, 0x0006, 0x00e6, 0x0016, 0x2091,
+ 0x8000, 0x2071, 0x1840, 0xd5a4, 0x0118, 0x7054, 0x8000, 0x7056,
+ 0xd5b4, 0x0118, 0x7050, 0x8000, 0x7052, 0xd5ac, 0x0178, 0x2500,
+ 0x9084, 0x0007, 0x908e, 0x0003, 0x0148, 0x908e, 0x0004, 0x0130,
+ 0x908e, 0x0005, 0x0118, 0x2071, 0x184a, 0x0089, 0x001e, 0x00ee,
+ 0x000e, 0x012e, 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000,
+ 0x2071, 0x1842, 0x0021, 0x00ee, 0x000e, 0x012e, 0x0005, 0x2e04,
+ 0x8000, 0x2072, 0x1220, 0x8e70, 0x2e04, 0x8000, 0x2072, 0x0005,
+ 0x00e6, 0x2071, 0x1840, 0x0c99, 0x00ee, 0x0005, 0x00e6, 0x2071,
+ 0x1844, 0x0c69, 0x00ee, 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091,
+ 0x8000, 0x2071, 0x1840, 0x7064, 0x8000, 0x7066, 0x00ee, 0x000e,
+ 0x012e, 0x0005, 0x0003, 0x000b, 0x04a6, 0x0000, 0xc000, 0x0001,
+ 0x8064, 0x0008, 0x0010, 0x0000, 0x8066, 0x0000, 0x0101, 0x0008,
+ 0x4407, 0x0003, 0x8060, 0x0000, 0x0400, 0x0000, 0x580d, 0x000b,
+ 0x798e, 0x0003, 0x50db, 0x000b, 0x4c0a, 0x0003, 0xbac0, 0x0009,
+ 0x008a, 0x0000, 0x0c0a, 0x000b, 0x15fe, 0x0008, 0x340a, 0x0003,
+ 0xc4c0, 0x0009, 0x7000, 0x0000, 0xffa0, 0x0001, 0x2000, 0x0000,
+ 0x1627, 0x0003, 0x808c, 0x0008, 0x0001, 0x0000, 0x0000, 0x0007,
+ 0x4047, 0x000a, 0x808c, 0x0008, 0x0002, 0x0000, 0x0821, 0x0003,
+ 0x4022, 0x0000, 0x0022, 0x000b, 0x4122, 0x0008, 0x4447, 0x0002,
+ 0x0e4f, 0x000b, 0x0bfe, 0x0008, 0x11a0, 0x0001, 0x122d, 0x000b,
+ 0x0ca0, 0x0001, 0x122d, 0x000b, 0x9180, 0x0001, 0x0004, 0x0000,
+ 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000,
+ 0x0009, 0x0008, 0x4430, 0x000b, 0x808c, 0x0008, 0x0000, 0x0008,
+ 0x0060, 0x0008, 0x8062, 0x0008, 0x0004, 0x0000, 0x8066, 0x0000,
+ 0x0411, 0x0000, 0x4438, 0x0003, 0x03fe, 0x0000, 0x43e0, 0x0001,
+ 0x0e2a, 0x000b, 0xc2c0, 0x0009, 0x00ff, 0x0008, 0x02e0, 0x0001,
+ 0x0e2a, 0x000b, 0x9180, 0x0001, 0x0005, 0x0008, 0x8060, 0x0000,
+ 0x0400, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000, 0x0019, 0x0000,
+ 0x4447, 0x000b, 0x0240, 0x0002, 0x0a27, 0x000b, 0x00fe, 0x0000,
+ 0x322a, 0x000b, 0x112a, 0x0000, 0x002e, 0x0008, 0x022c, 0x0008,
+ 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x808c, 0x0008, 0x0002, 0x0000,
+ 0x1760, 0x0008, 0x8062, 0x0008, 0x000f, 0x0008, 0x8066, 0x0000,
+ 0x0011, 0x0008, 0x4458, 0x0003, 0x01fe, 0x0008, 0x42e0, 0x0009,
+ 0x0e1d, 0x0003, 0x00fe, 0x0000, 0x43e0, 0x0001, 0x0e1d, 0x0003,
+ 0x1734, 0x0000, 0x1530, 0x0000, 0x1632, 0x0008, 0x0d2a, 0x0008,
+ 0x9880, 0x0001, 0x0010, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000,
+ 0x7f62, 0x0008, 0x8066, 0x0000, 0x1e0a, 0x0008, 0x446a, 0x000b,
+ 0x808a, 0x0008, 0x0003, 0x0008, 0x1a60, 0x0000, 0x8062, 0x0008,
+ 0x0002, 0x0000, 0x5870, 0x000b, 0x8066, 0x0000, 0x3679, 0x0000,
+ 0x4473, 0x0003, 0x5874, 0x0003, 0x3efe, 0x0008, 0x7f4f, 0x0002,
+ 0x087a, 0x000b, 0x0d00, 0x0000, 0x0082, 0x0004, 0x8054, 0x0008,
+ 0x0011, 0x0008, 0x8074, 0x0000, 0x1010, 0x0008, 0x1efe, 0x0000,
+ 0x300a, 0x000b, 0x00b8, 0x0004, 0x000a, 0x000b, 0x00fe, 0x0000,
+ 0x348a, 0x000b, 0x1a60, 0x0000, 0x8062, 0x0008, 0x0007, 0x0000,
+ 0x8066, 0x0000, 0x0231, 0x0008, 0x4489, 0x0003, 0x03fe, 0x0000,
+ 0x04d0, 0x0001, 0x0cb0, 0x0003, 0x82c0, 0x0001, 0x1f00, 0x0000,
+ 0xffa0, 0x0001, 0x0400, 0x0000, 0x089f, 0x0003, 0x14b0, 0x0003,
+ 0x01fe, 0x0008, 0x0580, 0x0009, 0x7f06, 0x0000, 0x02fe, 0x0008,
+ 0xffc0, 0x0001, 0x00ff, 0x0008, 0x0690, 0x0001, 0x109f, 0x0003,
+ 0x7f08, 0x0008, 0x84c0, 0x0001, 0xff00, 0x0008, 0x08b0, 0x000b,
+ 0x00fe, 0x0000, 0x34a6, 0x0003, 0x8072, 0x0000, 0x1010, 0x0008,
+ 0x3944, 0x0002, 0x08a1, 0x000b, 0x00aa, 0x000b, 0x8072, 0x0000,
+ 0x2020, 0x0008, 0x3945, 0x000a, 0x08a6, 0x0003, 0x3946, 0x000a,
+ 0x0cb7, 0x000b, 0x0000, 0x0007, 0x3943, 0x000a, 0x08b7, 0x0003,
+ 0x00aa, 0x000b, 0x00fe, 0x0000, 0x34b5, 0x000b, 0x8072, 0x0000,
+ 0x1000, 0x0000, 0x00b7, 0x000b, 0x8072, 0x0000, 0x2000, 0x0000,
+ 0x4000, 0x000f, 0x1c60, 0x0000, 0x1b62, 0x0000, 0x8066, 0x0000,
+ 0x0231, 0x0008, 0x44bc, 0x0003, 0x58bd, 0x0003, 0x0140, 0x0008,
+ 0x0242, 0x0000, 0x1f43, 0x0002, 0x0ccb, 0x0003, 0x0d44, 0x0000,
+ 0x0d46, 0x0008, 0x0348, 0x0008, 0x044a, 0x0008, 0x030a, 0x0008,
+ 0x040c, 0x0000, 0x0d06, 0x0000, 0x0d08, 0x0008, 0x00cf, 0x000b,
+ 0x0344, 0x0008, 0x0446, 0x0008, 0x0548, 0x0008, 0x064a, 0x0000,
+ 0x58cf, 0x0003, 0x3efe, 0x0008, 0x7f4f, 0x0002, 0x08d6, 0x000b,
+ 0x8000, 0x0000, 0x0001, 0x0000, 0x0082, 0x0004, 0x8054, 0x0008,
+ 0x0001, 0x0000, 0x8074, 0x0000, 0x2020, 0x0008, 0x4000, 0x000f,
+ 0x3a40, 0x000a, 0x0c0d, 0x0003, 0x2b24, 0x0008, 0x2b24, 0x0008,
+ 0x58df, 0x000b, 0x8054, 0x0008, 0x0002, 0x0000, 0x1242, 0x0002,
+ 0x092d, 0x000b, 0x3a45, 0x000a, 0x091c, 0x0003, 0x8072, 0x0000,
+ 0x1000, 0x0000, 0x3945, 0x000a, 0x08ec, 0x000b, 0x8072, 0x0000,
+ 0x3010, 0x0000, 0x1e10, 0x000a, 0x7f3c, 0x0000, 0x0917, 0x000b,
+ 0x1d00, 0x0002, 0x7f3a, 0x0000, 0x0d60, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x0009, 0x0008, 0x44f5, 0x000b, 0x00fe, 0x0000,
+ 0x3514, 0x000b, 0x1c60, 0x0000, 0x8062, 0x0008, 0x0001, 0x0000,
+ 0x8066, 0x0000, 0x0009, 0x0008, 0x44fd, 0x0003, 0x00fe, 0x0000,
+ 0x3204, 0x000b, 0x0038, 0x0000, 0x0060, 0x0008, 0x8062, 0x0008,
+ 0x0019, 0x0000, 0x8066, 0x0000, 0x0009, 0x0008, 0x4506, 0x0003,
+ 0x80c0, 0x0009, 0x00ff, 0x0008, 0x7f3e, 0x0008, 0x0d60, 0x0000,
+ 0x0efe, 0x0008, 0x1f80, 0x0001, 0x7f62, 0x0008, 0x8066, 0x0000,
+ 0x0009, 0x0008, 0x4510, 0x000b, 0x003a, 0x0008, 0x1dfe, 0x0000,
+ 0x00f1, 0x0003, 0x0036, 0x0008, 0x00b8, 0x0004, 0x012d, 0x0003,
+ 0x8074, 0x0000, 0x2000, 0x0000, 0x8072, 0x0000, 0x2000, 0x0000,
+ 0x012d, 0x0003, 0x3a44, 0x0002, 0x0a30, 0x000b, 0x8074, 0x0000,
+ 0x1000, 0x0000, 0x8072, 0x0000, 0x1000, 0x0000, 0x2d0e, 0x0000,
+ 0x2d0e, 0x0000, 0x3601, 0x0003, 0x26fe, 0x0008, 0x26fe, 0x0008,
+ 0x2700, 0x0008, 0x2700, 0x0008, 0x00d0, 0x0009, 0x0d3f, 0x0003,
+ 0x8074, 0x0000, 0x4040, 0x0008, 0x592d, 0x000b, 0x50db, 0x000b,
+ 0x3a46, 0x000a, 0x0d3f, 0x0003, 0x3a47, 0x0002, 0x093a, 0x000b,
+ 0x8054, 0x0008, 0x0004, 0x0000, 0x8074, 0x0000, 0x8000, 0x0000,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x0182, 0x0003, 0x92c0, 0x0009,
+ 0x0fc8, 0x0000, 0x080a, 0x0003, 0x1246, 0x000a, 0x0dfb, 0x000b,
+ 0x1a60, 0x0000, 0x8062, 0x0008, 0x0002, 0x0000, 0x8066, 0x0000,
+ 0x362a, 0x0000, 0x4544, 0x0003, 0x2000, 0x0000, 0x2000, 0x0000,
+ 0x2102, 0x0000, 0x2102, 0x0000, 0x2204, 0x0000, 0x2204, 0x0000,
+ 0x2306, 0x0000, 0x2306, 0x0000, 0x2408, 0x0000, 0x2408, 0x0000,
+ 0x250a, 0x0000, 0x250a, 0x0000, 0x260c, 0x0000, 0x260c, 0x0000,
+ 0x270e, 0x0000, 0x270e, 0x0000, 0x2810, 0x0000, 0x2810, 0x0000,
+ 0x2912, 0x0000, 0x2912, 0x0000, 0x1a60, 0x0000, 0x8062, 0x0008,
+ 0x0007, 0x0000, 0x8066, 0x0000, 0x0052, 0x0000, 0x455e, 0x000b,
+ 0x92c0, 0x0009, 0x0780, 0x0008, 0x0e17, 0x0003, 0x124b, 0x0002,
+ 0x0967, 0x0003, 0x2e4d, 0x0002, 0x2e4d, 0x0002, 0x0a01, 0x0003,
+ 0x3a46, 0x000a, 0x0d74, 0x0003, 0x5969, 0x000b, 0x8054, 0x0008,
+ 0x0004, 0x0000, 0x1243, 0x000a, 0x097e, 0x000b, 0x8010, 0x0008,
+ 0x000d, 0x0000, 0x01ef, 0x0004, 0x1810, 0x0000, 0x01ef, 0x0004,
+ 0x017e, 0x0003, 0x194d, 0x000a, 0x0978, 0x000b, 0x1243, 0x000a,
+ 0x0a0b, 0x0003, 0x5978, 0x000b, 0x8054, 0x0008, 0x0004, 0x0000,
+ 0x01e4, 0x000c, 0x1810, 0x0000, 0x01ef, 0x0004, 0x8074, 0x0000,
+ 0xf000, 0x0008, 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000,
+ 0x3a42, 0x0002, 0x0d88, 0x0003, 0x15fe, 0x0008, 0x3451, 0x000b,
+ 0x000a, 0x000b, 0x8074, 0x0000, 0x0501, 0x0000, 0x8010, 0x0008,
+ 0x000c, 0x0008, 0x01ef, 0x0004, 0x000a, 0x000b, 0xbbe0, 0x0009,
+ 0x0030, 0x0008, 0x0d9e, 0x000b, 0x18fe, 0x0000, 0x3ce0, 0x0009,
+ 0x099b, 0x0003, 0x15fe, 0x0008, 0x3ce0, 0x0009, 0x099b, 0x0003,
+ 0x01df, 0x0004, 0x8076, 0x0008, 0x0040, 0x0000, 0x01dc, 0x000b,
+ 0x8076, 0x0008, 0x0041, 0x0008, 0x01dc, 0x000b, 0xbbe0, 0x0009,
+ 0x0032, 0x0000, 0x0da3, 0x0003, 0x3c1e, 0x0008, 0x01dc, 0x000b,
+ 0xbbe0, 0x0009, 0x0037, 0x0000, 0x0dc1, 0x000b, 0x18fe, 0x0000,
+ 0x3ce0, 0x0009, 0x0d9b, 0x000b, 0x8076, 0x0008, 0x0040, 0x0000,
+ 0x1a60, 0x0000, 0x8062, 0x0008, 0x000d, 0x0000, 0x2604, 0x0008,
+ 0x2604, 0x0008, 0x2706, 0x0008, 0x2706, 0x0008, 0x2808, 0x0000,
+ 0x2808, 0x0000, 0x290a, 0x0000, 0x290a, 0x0000, 0x8066, 0x0000,
+ 0x0422, 0x0000, 0x45b8, 0x0003, 0x01e4, 0x000c, 0x8054, 0x0008,
+ 0x0004, 0x0000, 0x8074, 0x0000, 0xf000, 0x0008, 0x8072, 0x0000,
+ 0xb000, 0x0000, 0x0182, 0x0003, 0xbbe0, 0x0009, 0x0038, 0x0000,
+ 0x0dd3, 0x000b, 0x18fe, 0x0000, 0x3ce0, 0x0009, 0x09d0, 0x0003,
+ 0x15fe, 0x0008, 0x3ce0, 0x0009, 0x0d97, 0x000b, 0x01df, 0x0004,
+ 0x8076, 0x0008, 0x0040, 0x0000, 0x8072, 0x0000, 0x8000, 0x0000,
+ 0x0227, 0x0003, 0x8076, 0x0008, 0x0042, 0x0008, 0x01dc, 0x000b,
+ 0xbbe0, 0x0009, 0x0016, 0x0000, 0x0ddc, 0x000b, 0x3a44, 0x0002,
+ 0x0c0c, 0x000b, 0x8072, 0x0000, 0x8000, 0x0000, 0x8000, 0x000f,
+ 0x000a, 0x000b, 0x8072, 0x0000, 0x8000, 0x0000, 0x000a, 0x000b,
+ 0x3d30, 0x000a, 0x7f00, 0x0000, 0xbc80, 0x0001, 0x0007, 0x0000,
+ 0x01e8, 0x0003, 0x1930, 0x000a, 0x7f00, 0x0000, 0x9880, 0x0001,
+ 0x0007, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x000a, 0x0008, 0x45ed, 0x0003, 0x4000, 0x000f,
+ 0x21ef, 0x0003, 0x0870, 0x0008, 0x4000, 0x000f, 0xbac0, 0x0009,
+ 0x0090, 0x0008, 0x09f8, 0x0003, 0x8074, 0x0000, 0x0706, 0x0000,
+ 0x01fa, 0x0003, 0x8074, 0x0000, 0x0703, 0x0000, 0x4000, 0x000f,
+ 0x8010, 0x0008, 0x0023, 0x0000, 0x0235, 0x0003, 0x8010, 0x0008,
+ 0x0008, 0x0000, 0x0235, 0x0003, 0x8010, 0x0008, 0x0022, 0x0008,
+ 0x0235, 0x0003, 0x01e4, 0x000c, 0x8010, 0x0008, 0x0007, 0x0000,
+ 0x01ef, 0x0004, 0x1810, 0x0000, 0x01ef, 0x0004, 0x0241, 0x0003,
+ 0x01e4, 0x000c, 0x8010, 0x0008, 0x001b, 0x0008, 0x01ef, 0x0004,
+ 0x1810, 0x0000, 0x01ef, 0x0004, 0x8074, 0x0000, 0xf080, 0x0000,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000, 0x000a, 0x000b,
+ 0x8010, 0x0008, 0x0009, 0x0008, 0x0235, 0x0003, 0x8010, 0x0008,
+ 0x0005, 0x0008, 0x0235, 0x0003, 0x808c, 0x0008, 0x0001, 0x0000,
+ 0x8010, 0x0008, 0x0004, 0x0000, 0x4143, 0x000a, 0x085f, 0x0003,
+ 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x0d2a, 0x0008, 0x0235, 0x0003,
+ 0x8010, 0x0008, 0x0003, 0x0008, 0x0239, 0x0003, 0x8010, 0x0008,
+ 0x000b, 0x0000, 0x0239, 0x0003, 0x8010, 0x0008, 0x0002, 0x0000,
+ 0x0239, 0x0003, 0x3a47, 0x0002, 0x0d2d, 0x0003, 0x8010, 0x0008,
+ 0x0006, 0x0008, 0x0239, 0x0003, 0x8074, 0x0000, 0xf000, 0x0008,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x01ef, 0x0004, 0x01f2, 0x0004,
+ 0x3a40, 0x000a, 0x080a, 0x0003, 0x8010, 0x0008, 0x000c, 0x0008,
+ 0x01ef, 0x0004, 0x000a, 0x000b, 0x8074, 0x0000, 0xf080, 0x0000,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000, 0x2e4d, 0x0002,
+ 0x2e4d, 0x0002, 0x0a4c, 0x0003, 0x8054, 0x0008, 0x0019, 0x0000,
+ 0x000a, 0x000b, 0x8054, 0x0008, 0x0009, 0x0008, 0x000a, 0x000b,
+ 0x3a44, 0x0002, 0x0c0a, 0x000b, 0x022a, 0x000b, 0x15b6, 0xf4ac,
+ 0x0003, 0x000b, 0x0480, 0x0000, 0xc000, 0x0001, 0x8064, 0x0008,
+ 0x0010, 0x0000, 0x8066, 0x0000, 0x0101, 0x0008, 0xc007, 0x0003,
+ 0x8060, 0x0000, 0x0400, 0x0000, 0x580d, 0x000b, 0x7977, 0x0003,
+ 0x50db, 0x000b, 0xc80a, 0x0003, 0xbac0, 0x0009, 0x008a, 0x0000,
+ 0x880a, 0x000b, 0x15fe, 0x0008, 0xb00a, 0x0003, 0xc4c0, 0x0009,
+ 0x7000, 0x0000, 0xffa0, 0x0001, 0x2000, 0x0000, 0x9214, 0x0003,
+ 0x808c, 0x0008, 0x0001, 0x0000, 0x0000, 0x0007, 0x4047, 0x000a,
+ 0x808c, 0x0008, 0x0002, 0x0000, 0x0821, 0x0003, 0x4022, 0x0000,
+ 0x0022, 0x000b, 0x4122, 0x0008, 0x4447, 0x0002, 0x8a3c, 0x0003,
+ 0x0bfe, 0x0008, 0x11a0, 0x0001, 0x121a, 0x0003, 0x0ca0, 0x0001,
+ 0x121a, 0x0003, 0x9180, 0x0001, 0x0004, 0x0000, 0x8060, 0x0000,
+ 0x0400, 0x0000, 0x7f62, 0x0008, 0x8066, 0x0000, 0x0009, 0x0008,
+ 0xc030, 0x000b, 0x808c, 0x0008, 0x0000, 0x0008, 0x0060, 0x0008,
+ 0x8062, 0x0008, 0x0004, 0x0000, 0x8066, 0x0000, 0x0411, 0x0000,
+ 0xc038, 0x0003, 0x03fe, 0x0000, 0x43e0, 0x0001, 0x8a17, 0x0003,
+ 0xc2c0, 0x0009, 0x00ff, 0x0008, 0x02e0, 0x0001, 0x8a17, 0x0003,
+ 0x9180, 0x0001, 0x0005, 0x0008, 0x8060, 0x0000, 0x0400, 0x0000,
+ 0x7f62, 0x0008, 0x8066, 0x0000, 0x0019, 0x0000, 0xc047, 0x000b,
+ 0x0240, 0x0002, 0x0a14, 0x000b, 0x00fe, 0x0000, 0x3217, 0x0003,
+ 0x112a, 0x0000, 0x002e, 0x0008, 0x022c, 0x0008, 0x3a44, 0x0002,
+ 0x880a, 0x000b, 0x808c, 0x0008, 0x0002, 0x0000, 0x1760, 0x0008,
+ 0x8062, 0x0008, 0x000f, 0x0008, 0x8066, 0x0000, 0x0011, 0x0008,
+ 0xc058, 0x0003, 0x01fe, 0x0008, 0x42e0, 0x0009, 0x8a0a, 0x0003,
+ 0x00fe, 0x0000, 0x43e0, 0x0001, 0x8a0a, 0x0003, 0x1734, 0x0000,
+ 0x1530, 0x0000, 0x1632, 0x0008, 0x0d2a, 0x0008, 0x9880, 0x0001,
+ 0x0010, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x1e0a, 0x0008, 0xc06a, 0x000b, 0x808a, 0x0008,
+ 0x0003, 0x0008, 0x1a60, 0x0000, 0x8062, 0x0008, 0x0002, 0x0000,
+ 0x5870, 0x000b, 0x8066, 0x0000, 0x3679, 0x0000, 0xc073, 0x0003,
+ 0x5874, 0x0003, 0x3efe, 0x0008, 0x7f4f, 0x0002, 0x087a, 0x000b,
+ 0x0d00, 0x0000, 0x0082, 0x0004, 0x8054, 0x0008, 0x0011, 0x0008,
+ 0x8074, 0x0000, 0x1010, 0x0008, 0x1efe, 0x0000, 0x300a, 0x000b,
+ 0x00b8, 0x0004, 0x000a, 0x000b, 0x00fe, 0x0000, 0xb08a, 0x000b,
+ 0x1a60, 0x0000, 0x8062, 0x0008, 0x0007, 0x0000, 0x8066, 0x0000,
+ 0x0231, 0x0008, 0xc089, 0x0003, 0x03fe, 0x0000, 0x04d0, 0x0001,
+ 0x88b0, 0x0003, 0x82c0, 0x0001, 0x1f00, 0x0000, 0xffa0, 0x0001,
+ 0x0400, 0x0000, 0x089f, 0x0003, 0x90b0, 0x0003, 0x01fe, 0x0008,
+ 0x0580, 0x0009, 0x7f06, 0x0000, 0x02fe, 0x0008, 0xffc0, 0x0001,
+ 0x00ff, 0x0008, 0x0690, 0x0001, 0x109f, 0x0003, 0x7f08, 0x0008,
+ 0x84c0, 0x0001, 0xff00, 0x0008, 0x08b0, 0x000b, 0x00fe, 0x0000,
+ 0xb0a6, 0x0003, 0x8072, 0x0000, 0x1010, 0x0008, 0x3944, 0x0002,
+ 0x08a1, 0x000b, 0x00aa, 0x000b, 0x8072, 0x0000, 0x2020, 0x0008,
+ 0x3945, 0x000a, 0x08a6, 0x0003, 0x3946, 0x000a, 0x88b7, 0x000b,
+ 0x0000, 0x0007, 0x3943, 0x000a, 0x08b7, 0x0003, 0x00aa, 0x000b,
+ 0x00fe, 0x0000, 0xb0b5, 0x000b, 0x8072, 0x0000, 0x1000, 0x0000,
+ 0x00b7, 0x000b, 0x8072, 0x0000, 0x2000, 0x0000, 0x4000, 0x000f,
+ 0x1c60, 0x0000, 0x1b62, 0x0000, 0x8066, 0x0000, 0x0231, 0x0008,
+ 0xc0bc, 0x0003, 0x58bd, 0x0003, 0x0140, 0x0008, 0x0242, 0x0000,
+ 0x1f43, 0x0002, 0x88cb, 0x0003, 0x0d44, 0x0000, 0x0d46, 0x0008,
+ 0x0348, 0x0008, 0x044a, 0x0008, 0x030a, 0x0008, 0x040c, 0x0000,
+ 0x0d06, 0x0000, 0x0d08, 0x0008, 0x00cf, 0x000b, 0x0344, 0x0008,
+ 0x0446, 0x0008, 0x0548, 0x0008, 0x064a, 0x0000, 0x58cf, 0x0003,
+ 0x3efe, 0x0008, 0x7f4f, 0x0002, 0x08d6, 0x000b, 0x8000, 0x0000,
+ 0x0001, 0x0000, 0x0082, 0x0004, 0x8054, 0x0008, 0x0001, 0x0000,
+ 0x8074, 0x0000, 0x2020, 0x0008, 0x4000, 0x000f, 0x3a40, 0x000a,
+ 0x880d, 0x0003, 0xabd0, 0x0001, 0x0000, 0x0008, 0x7f24, 0x0000,
+ 0x58e0, 0x000b, 0x8054, 0x0008, 0x0002, 0x0000, 0x1242, 0x0002,
+ 0x0930, 0x000b, 0x3a45, 0x000a, 0x091d, 0x000b, 0x8072, 0x0000,
+ 0x1000, 0x0000, 0x3945, 0x000a, 0x08ed, 0x0003, 0x8072, 0x0000,
+ 0x3010, 0x0000, 0x1e10, 0x000a, 0x7f3c, 0x0000, 0x0918, 0x000b,
+ 0x1d00, 0x0002, 0x7f3a, 0x0000, 0x0d60, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x0009, 0x0008, 0xc0f6, 0x000b, 0x00fe, 0x0000,
+ 0xb115, 0x0003, 0x1c60, 0x0000, 0x8062, 0x0008, 0x0001, 0x0000,
+ 0x8066, 0x0000, 0x0009, 0x0008, 0xc0fe, 0x0003, 0x00fe, 0x0000,
+ 0x31f1, 0x000b, 0x0038, 0x0000, 0x0060, 0x0008, 0x8062, 0x0008,
+ 0x0019, 0x0000, 0x8066, 0x0000, 0x0009, 0x0008, 0xc107, 0x000b,
+ 0x80c0, 0x0009, 0x00ff, 0x0008, 0x7f3e, 0x0008, 0x0d60, 0x0000,
+ 0x0efe, 0x0008, 0x1f80, 0x0001, 0x7f62, 0x0008, 0x8066, 0x0000,
+ 0x0009, 0x0008, 0xc111, 0x0003, 0x003a, 0x0008, 0x1dfe, 0x0000,
+ 0x00f2, 0x0003, 0x0036, 0x0008, 0x00b8, 0x0004, 0x0130, 0x0003,
+ 0x8074, 0x0000, 0x2000, 0x0000, 0x8072, 0x0000, 0x2000, 0x0000,
+ 0x0130, 0x0003, 0x3a44, 0x0002, 0x0a1d, 0x000b, 0x8074, 0x0000,
+ 0x1000, 0x0000, 0x8072, 0x0000, 0x1000, 0x0000, 0xadd0, 0x0001,
+ 0x0000, 0x0008, 0x7f0e, 0x0008, 0xb1ee, 0x000b, 0xa7d0, 0x0001,
+ 0x0000, 0x0008, 0x7f00, 0x0000, 0xa6d0, 0x0009, 0x0000, 0x0008,
+ 0x00d0, 0x0009, 0x8942, 0x0003, 0x8074, 0x0000, 0x4040, 0x0008,
+ 0x5930, 0x000b, 0x50db, 0x000b, 0x3a46, 0x000a, 0x8942, 0x0003,
+ 0x3a47, 0x0002, 0x093d, 0x0003, 0x8054, 0x0008, 0x0004, 0x0000,
+ 0x8074, 0x0000, 0x8000, 0x0000, 0x8072, 0x0000, 0x3000, 0x0008,
+ 0x016b, 0x000b, 0x92c0, 0x0009, 0x0fc8, 0x0000, 0x080a, 0x0003,
+ 0x1246, 0x000a, 0x89e8, 0x0003, 0x1a60, 0x0000, 0x8062, 0x0008,
+ 0x0002, 0x0000, 0x8066, 0x0000, 0x367a, 0x0000, 0xc147, 0x0003,
+ 0x92c0, 0x0009, 0x0780, 0x0008, 0x8a04, 0x000b, 0x124b, 0x0002,
+ 0x0950, 0x000b, 0x2e4d, 0x0002, 0x2e4d, 0x0002, 0x09ee, 0x000b,
+ 0x3a46, 0x000a, 0x895d, 0x000b, 0x5952, 0x0003, 0x8054, 0x0008,
+ 0x0004, 0x0000, 0x1243, 0x000a, 0x0967, 0x0003, 0x8010, 0x0008,
+ 0x000d, 0x0000, 0x01dc, 0x0004, 0x1810, 0x0000, 0x01dc, 0x0004,
+ 0x0167, 0x000b, 0x194d, 0x000a, 0x0961, 0x0003, 0x1243, 0x000a,
+ 0x09f8, 0x0003, 0x5961, 0x0003, 0x8054, 0x0008, 0x0004, 0x0000,
+ 0x01d1, 0x000c, 0x1810, 0x0000, 0x01dc, 0x0004, 0x8074, 0x0000,
+ 0xf000, 0x0008, 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000,
+ 0x3a42, 0x0002, 0x8971, 0x0003, 0x15fe, 0x0008, 0xb051, 0x000b,
+ 0x000a, 0x000b, 0x8074, 0x0000, 0x0501, 0x0000, 0x8010, 0x0008,
+ 0x000c, 0x0008, 0x01dc, 0x0004, 0x000a, 0x000b, 0xbbe0, 0x0009,
+ 0x0030, 0x0008, 0x8987, 0x0003, 0x18fe, 0x0000, 0x3ce0, 0x0009,
+ 0x0984, 0x000b, 0x15fe, 0x0008, 0x3ce0, 0x0009, 0x0984, 0x000b,
+ 0x01cc, 0x000c, 0x8076, 0x0008, 0x0040, 0x0000, 0x01c9, 0x0003,
+ 0x8076, 0x0008, 0x0041, 0x0008, 0x01c9, 0x0003, 0xbbe0, 0x0009,
+ 0x0032, 0x0000, 0x898c, 0x000b, 0x3c1e, 0x0008, 0x01c9, 0x0003,
+ 0xbbe0, 0x0009, 0x0037, 0x0000, 0x89ae, 0x000b, 0x18fe, 0x0000,
+ 0x3ce0, 0x0009, 0x8984, 0x0003, 0x8076, 0x0008, 0x0040, 0x0000,
+ 0x1a60, 0x0000, 0x8062, 0x0008, 0x000d, 0x0000, 0xa6d0, 0x0009,
+ 0x0000, 0x0008, 0x7f04, 0x0008, 0xa7d0, 0x0001, 0x0000, 0x0008,
+ 0x7f06, 0x0000, 0xa8d0, 0x0001, 0x0000, 0x0008, 0x7f08, 0x0008,
+ 0xa9d0, 0x0009, 0x0000, 0x0008, 0x7f0a, 0x0000, 0x8066, 0x0000,
+ 0x0422, 0x0000, 0xc1a5, 0x0003, 0x01d1, 0x000c, 0x8054, 0x0008,
+ 0x0004, 0x0000, 0x8074, 0x0000, 0xf000, 0x0008, 0x8072, 0x0000,
+ 0xb000, 0x0000, 0x016b, 0x000b, 0xbbe0, 0x0009, 0x0038, 0x0000,
+ 0x89c0, 0x0003, 0x18fe, 0x0000, 0x3ce0, 0x0009, 0x09bd, 0x000b,
+ 0x15fe, 0x0008, 0x3ce0, 0x0009, 0x8980, 0x000b, 0x01cc, 0x000c,
+ 0x8076, 0x0008, 0x0040, 0x0000, 0x8072, 0x0000, 0x8000, 0x0000,
+ 0x0214, 0x0003, 0x8076, 0x0008, 0x0042, 0x0008, 0x01c9, 0x0003,
+ 0xbbe0, 0x0009, 0x0016, 0x0000, 0x89c9, 0x0003, 0x3a44, 0x0002,
+ 0x880c, 0x000b, 0x8072, 0x0000, 0x8000, 0x0000, 0x8000, 0x000f,
+ 0x000a, 0x000b, 0x8072, 0x0000, 0x8000, 0x0000, 0x000a, 0x000b,
+ 0x3d30, 0x000a, 0x7f00, 0x0000, 0xbc80, 0x0001, 0x0007, 0x0000,
+ 0x01d5, 0x000b, 0x1930, 0x000a, 0x7f00, 0x0000, 0x9880, 0x0001,
+ 0x0007, 0x0000, 0x8060, 0x0000, 0x0400, 0x0000, 0x7f62, 0x0008,
+ 0x8066, 0x0000, 0x000a, 0x0008, 0xc1da, 0x000b, 0x4000, 0x000f,
+ 0x21dc, 0x0003, 0x0870, 0x0008, 0x4000, 0x000f, 0xbac0, 0x0009,
+ 0x0090, 0x0008, 0x09e5, 0x0003, 0x8074, 0x0000, 0x0706, 0x0000,
+ 0x01e7, 0x0003, 0x8074, 0x0000, 0x0703, 0x0000, 0x4000, 0x000f,
+ 0x8010, 0x0008, 0x0023, 0x0000, 0x0222, 0x0003, 0x8010, 0x0008,
+ 0x0008, 0x0000, 0x0222, 0x0003, 0x8010, 0x0008, 0x0022, 0x0008,
+ 0x0222, 0x0003, 0x01d1, 0x000c, 0x8010, 0x0008, 0x0007, 0x0000,
+ 0x01dc, 0x0004, 0x1810, 0x0000, 0x01dc, 0x0004, 0x022e, 0x0003,
+ 0x01d1, 0x000c, 0x8010, 0x0008, 0x001b, 0x0008, 0x01dc, 0x0004,
+ 0x1810, 0x0000, 0x01dc, 0x0004, 0x8074, 0x0000, 0xf080, 0x0000,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000, 0x000a, 0x000b,
+ 0x8010, 0x0008, 0x0009, 0x0008, 0x0222, 0x0003, 0x8010, 0x0008,
+ 0x0005, 0x0008, 0x0222, 0x0003, 0x808c, 0x0008, 0x0001, 0x0000,
+ 0x8010, 0x0008, 0x0004, 0x0000, 0x4143, 0x000a, 0x085f, 0x0003,
+ 0x3a44, 0x0002, 0x880a, 0x000b, 0x0d2a, 0x0008, 0x0222, 0x0003,
+ 0x8010, 0x0008, 0x0003, 0x0008, 0x0226, 0x000b, 0x8010, 0x0008,
+ 0x000b, 0x0000, 0x0226, 0x000b, 0x8010, 0x0008, 0x0002, 0x0000,
+ 0x0226, 0x000b, 0x3a47, 0x0002, 0x8930, 0x0003, 0x8010, 0x0008,
+ 0x0006, 0x0008, 0x0226, 0x000b, 0x8074, 0x0000, 0xf000, 0x0008,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x01dc, 0x0004, 0x01df, 0x0004,
+ 0x3a40, 0x000a, 0x080a, 0x0003, 0x8010, 0x0008, 0x000c, 0x0008,
+ 0x01dc, 0x0004, 0x000a, 0x000b, 0x8074, 0x0000, 0xf080, 0x0000,
+ 0x8072, 0x0000, 0x3000, 0x0008, 0x0d30, 0x0000, 0x2e4d, 0x0002,
+ 0x2e4d, 0x0002, 0x0a39, 0x000b, 0x8054, 0x0008, 0x0019, 0x0000,
+ 0x000a, 0x000b, 0x8054, 0x0008, 0x0009, 0x0008, 0x000a, 0x000b,
+ 0x3a44, 0x0002, 0x880a, 0x000b, 0x0217, 0x0003, 0xf4e5, 0xf482,
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
+ 0x6870
+};
+#ifdef UNIQUE_FW_NAME
+unsigned short fw2300flx_length01 = 0xdd79;
+#else
+unsigned short risc_code_length01 = 0xdd79;
+#endif
+
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
new file mode 100644
index 000000000000..dcc33daa5913
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -0,0 +1,1158 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+#include <linux/delay.h>
+
+static int qla_uprintf(char **, char *, ...);
+
+/**
+ * qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
+ * @ha: HA context
+ * @hardware_locked: Called with the hardware_lock
+ */
+void
+qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt, timer;
+ uint32_t risc_address;
+ uint16_t mb0, mb2;
+
+ uint32_t stat;
+ device_reg_t __iomem *reg = ha->iobase;
+ uint16_t __iomem *dmp_reg;
+ unsigned long flags;
+ struct qla2300_fw_dump *fw;
+ uint32_t dump_size, data_ram_cnt;
+
+ risc_address = data_ram_cnt = 0;
+ mb0 = mb2 = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (ha->fw_dump != NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Firmware has been previously dumped (%p) -- ignoring "
+ "request...\n", ha->fw_dump);
+ goto qla2300_fw_dump_failed;
+ }
+
+ /* Allocate (large) dump buffer. */
+ dump_size = sizeof(struct qla2300_fw_dump);
+ dump_size += (ha->fw_memory_size - 0x11000) * sizeof(uint16_t);
+ ha->fw_dump_order = get_order(dump_size);
+ ha->fw_dump = (struct qla2300_fw_dump *) __get_free_pages(GFP_ATOMIC,
+ ha->fw_dump_order);
+ if (ha->fw_dump == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocated memory for firmware dump (%d/%d).\n",
+ ha->fw_dump_order, dump_size);
+ goto qla2300_fw_dump_failed;
+ }
+ fw = ha->fw_dump;
+
+ rval = QLA_SUCCESS;
+ fw->hccr = RD_REG_WORD(&reg->hccr);
+
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ if (IS_QLA2300(ha)) {
+ for (cnt = 30000;
+ (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ } else {
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ udelay(10);
+ }
+
+ if (rval == QLA_SUCCESS) {
+ dmp_reg = (uint16_t __iomem *)(reg + 0);
+ for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++)
+ fw->pbiu_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x10);
+ for (cnt = 0; cnt < sizeof(fw->risc_host_reg) / 2; cnt++)
+ fw->risc_host_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x40);
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x40);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->resp_dma_reg) / 2; cnt++)
+ fw->resp_dma_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x50);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++)
+ fw->dma_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x00);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0xA0);
+ for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++)
+ fw->risc_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2000);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp0_reg) / 2; cnt++)
+ fw->risc_gp0_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2200);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp1_reg) / 2; cnt++)
+ fw->risc_gp1_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2400);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp2_reg) / 2; cnt++)
+ fw->risc_gp2_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2600);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp3_reg) / 2; cnt++)
+ fw->risc_gp3_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2800);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp4_reg) / 2; cnt++)
+ fw->risc_gp4_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2A00);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp5_reg) / 2; cnt++)
+ fw->risc_gp5_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2C00);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp6_reg) / 2; cnt++)
+ fw->risc_gp6_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2E00);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp7_reg) / 2; cnt++)
+ fw->risc_gp7_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x10);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->frame_buf_hdw_reg) / 2; cnt++)
+ fw->frame_buf_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->fpm_b0_reg) / 2; cnt++)
+ fw->fpm_b0_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x30);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->fpm_b1_reg) / 2; cnt++)
+ fw->fpm_b1_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ /* Reset RISC. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->ctrl_status) &
+ CSR_ISP_SOFT_RESET) == 0)
+ break;
+
+ udelay(10);
+ }
+ }
+
+ if (!IS_QLA2300(ha)) {
+ for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ }
+
+ if (rval == QLA_SUCCESS) {
+ /* Get RISC SRAM. */
+ risc_address = 0x800;
+ WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ }
+ for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS;
+ cnt++, risc_address++) {
+ WRT_MAILBOX_REG(ha, reg, 1, (uint16_t)risc_address);
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ } else if (stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ }
+
+ /* clear this intr; it wasn't a mailbox intr */
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ fw->risc_ram[cnt] = mb2;
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ if (rval == QLA_SUCCESS) {
+ /* Get stack SRAM. */
+ risc_address = 0x10000;
+ WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_EXTENDED);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ }
+ for (cnt = 0; cnt < sizeof(fw->stack_ram) / 2 && rval == QLA_SUCCESS;
+ cnt++, risc_address++) {
+ WRT_MAILBOX_REG(ha, reg, 1, LSW(risc_address));
+ WRT_MAILBOX_REG(ha, reg, 8, MSW(risc_address));
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ } else if (stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ }
+
+ /* clear this intr; it wasn't a mailbox intr */
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ fw->stack_ram[cnt] = mb2;
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ if (rval == QLA_SUCCESS) {
+ /* Get data SRAM. */
+ risc_address = 0x11000;
+ data_ram_cnt = ha->fw_memory_size - risc_address + 1;
+ WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_EXTENDED);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ }
+ for (cnt = 0; cnt < data_ram_cnt && rval == QLA_SUCCESS;
+ cnt++, risc_address++) {
+ WRT_MAILBOX_REG(ha, reg, 1, LSW(risc_address));
+ WRT_MAILBOX_REG(ha, reg, 8, MSW(risc_address));
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ } else if (stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ }
+
+ /* clear this intr; it wasn't a mailbox intr */
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ fw->data_ram[cnt] = mb2;
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to dump firmware (%x)!!!\n", rval);
+
+ free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order);
+ ha->fw_dump = NULL;
+ } else {
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump saved to temp buffer (%ld/%p).\n",
+ ha->host_no, ha->fw_dump);
+ }
+
+qla2300_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/**
+ * qla2300_ascii_fw_dump() - Converts a binary firmware dump to ASCII.
+ * @ha: HA context
+ */
+void
+qla2300_ascii_fw_dump(scsi_qla_host_t *ha)
+{
+ uint32_t cnt;
+ char *uiter;
+ char fw_info[30];
+ struct qla2300_fw_dump *fw;
+ uint32_t data_ram_cnt;
+
+ uiter = ha->fw_dump_buffer;
+ fw = ha->fw_dump;
+
+ qla_uprintf(&uiter, "%s Firmware Version %s\n", ha->model_number,
+ qla2x00_get_fw_version_str(ha, fw_info));
+
+ qla_uprintf(&uiter, "\n[==>BEG]\n");
+
+ qla_uprintf(&uiter, "HCCR Register:\n%04x\n\n", fw->hccr);
+
+ qla_uprintf(&uiter, "PBIU Registers:");
+ for (cnt = 0; cnt < sizeof (fw->pbiu_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->pbiu_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nReqQ-RspQ-Risc2Host Status registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_host_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_host_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nMailbox Registers:");
+ for (cnt = 0; cnt < sizeof (fw->mailbox_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->mailbox_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nAuto Request Response DMA Registers:");
+ for (cnt = 0; cnt < sizeof (fw->resp_dma_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->resp_dma_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nDMA Registers:");
+ for (cnt = 0; cnt < sizeof (fw->dma_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->dma_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC Hardware Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_hdw_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_hdw_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP0 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp0_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp0_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP1 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp1_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp1_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP2 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp2_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp2_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP3 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp3_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp3_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP4 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp4_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp4_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP5 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp5_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp5_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP6 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp6_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp6_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP7 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp7_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp7_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nFrame Buffer Hardware Registers:");
+ for (cnt = 0; cnt < sizeof (fw->frame_buf_hdw_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->frame_buf_hdw_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nFPM B0 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->fpm_b0_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->fpm_b0_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nFPM B1 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->fpm_b1_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->fpm_b1_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nCode RAM Dump:");
+ for (cnt = 0; cnt < sizeof (fw->risc_ram) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n%04x: ", cnt + 0x0800);
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_ram[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nStack RAM Dump:");
+ for (cnt = 0; cnt < sizeof (fw->stack_ram) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n%05x: ", cnt + 0x10000);
+ }
+ qla_uprintf(&uiter, "%04x ", fw->stack_ram[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nData RAM Dump:");
+ data_ram_cnt = ha->fw_memory_size - 0x11000 + 1;
+ for (cnt = 0; cnt < data_ram_cnt; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n%05x: ", cnt + 0x11000);
+ }
+ qla_uprintf(&uiter, "%04x ", fw->data_ram[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\n[<==END] ISP Debug Dump.");
+}
+
+/**
+ * qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware.
+ * @ha: HA context
+ * @hardware_locked: Called with the hardware_lock
+ */
+void
+qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt, timer;
+ uint16_t risc_address;
+ uint16_t mb0, mb2;
+ device_reg_t __iomem *reg = ha->iobase;
+ uint16_t __iomem *dmp_reg;
+ unsigned long flags;
+ struct qla2100_fw_dump *fw;
+
+ risc_address = 0;
+ mb0 = mb2 = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (ha->fw_dump != NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Firmware has been previously dumped (%p) -- ignoring "
+ "request...\n", ha->fw_dump);
+ goto qla2100_fw_dump_failed;
+ }
+
+ /* Allocate (large) dump buffer. */
+ ha->fw_dump_order = get_order(sizeof(struct qla2100_fw_dump));
+ ha->fw_dump = (struct qla2100_fw_dump *) __get_free_pages(GFP_ATOMIC,
+ ha->fw_dump_order);
+ if (ha->fw_dump == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocated memory for firmware dump (%d/%Zd).\n",
+ ha->fw_dump_order, sizeof(struct qla2100_fw_dump));
+ goto qla2100_fw_dump_failed;
+ }
+ fw = ha->fw_dump;
+
+ rval = QLA_SUCCESS;
+ fw->hccr = RD_REG_WORD(&reg->hccr);
+
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ for (cnt = 30000; (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ if (rval == QLA_SUCCESS) {
+ dmp_reg = (uint16_t __iomem *)(reg + 0);
+ for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++)
+ fw->pbiu_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x10);
+ for (cnt = 0; cnt < ha->mbx_count; cnt++) {
+ if (cnt == 8) {
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0xe0);
+ }
+ fw->mailbox_reg[cnt] = RD_REG_WORD(dmp_reg++);
+ }
+
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x20);
+ for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++)
+ fw->dma_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x00);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0xA0);
+ for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++)
+ fw->risc_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2000);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp0_reg) / 2; cnt++)
+ fw->risc_gp0_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2100);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp1_reg) / 2; cnt++)
+ fw->risc_gp1_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2200);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp2_reg) / 2; cnt++)
+ fw->risc_gp2_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2300);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp3_reg) / 2; cnt++)
+ fw->risc_gp3_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2400);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp4_reg) / 2; cnt++)
+ fw->risc_gp4_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2500);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp5_reg) / 2; cnt++)
+ fw->risc_gp5_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2600);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp6_reg) / 2; cnt++)
+ fw->risc_gp6_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->pcr, 0x2700);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->risc_gp7_reg) / 2; cnt++)
+ fw->risc_gp7_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x10);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->frame_buf_hdw_reg) / 2; cnt++)
+ fw->frame_buf_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->fpm_b0_reg) / 2; cnt++)
+ fw->fpm_b0_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x30);
+ dmp_reg = (uint16_t __iomem *)((uint8_t __iomem *)reg + 0x80);
+ for (cnt = 0; cnt < sizeof(fw->fpm_b1_reg) / 2; cnt++)
+ fw->fpm_b1_reg[cnt] = RD_REG_WORD(dmp_reg++);
+
+ /* Reset the ISP. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+ }
+
+ for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+
+ /* Pause RISC. */
+ if (rval == QLA_SUCCESS && (IS_QLA2200(ha) || (IS_QLA2100(ha) &&
+ (RD_REG_WORD(&reg->mctr) & (BIT_1 | BIT_0)) != 0))) {
+
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ for (cnt = 30000;
+ (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ if (rval == QLA_SUCCESS) {
+ /* Set memory configuration and timing. */
+ if (IS_QLA2100(ha))
+ WRT_REG_WORD(&reg->mctr, 0xf1);
+ else
+ WRT_REG_WORD(&reg->mctr, 0xf2);
+ RD_REG_WORD(&reg->mctr); /* PCI Posting. */
+
+ /* Release RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ }
+ }
+
+ if (rval == QLA_SUCCESS) {
+ /* Get RISC SRAM. */
+ risc_address = 0x1000;
+ WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ }
+ for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS;
+ cnt++, risc_address++) {
+ WRT_MAILBOX_REG(ha, reg, 1, risc_address);
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+
+ for (timer = 6000000; timer != 0; timer--) {
+ /* Check for pending interrupts. */
+ if (RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) {
+ if (RD_REG_WORD(&reg->semaphore) & BIT_0) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ }
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ fw->risc_ram[cnt] = mb2;
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to dump firmware (%x)!!!\n", rval);
+
+ free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order);
+ ha->fw_dump = NULL;
+ } else {
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump saved to temp buffer (%ld/%p).\n",
+ ha->host_no, ha->fw_dump);
+ }
+
+qla2100_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/**
+ * qla2100_ascii_fw_dump() - Converts a binary firmware dump to ASCII.
+ * @ha: HA context
+ */
+void
+qla2100_ascii_fw_dump(scsi_qla_host_t *ha)
+{
+ uint32_t cnt;
+ char *uiter;
+ char fw_info[30];
+ struct qla2100_fw_dump *fw;
+
+ uiter = ha->fw_dump_buffer;
+ fw = ha->fw_dump;
+
+ qla_uprintf(&uiter, "%s Firmware Version %s\n", ha->model_number,
+ qla2x00_get_fw_version_str(ha, fw_info));
+
+ qla_uprintf(&uiter, "\n[==>BEG]\n");
+
+ qla_uprintf(&uiter, "HCCR Register:\n%04x\n\n", fw->hccr);
+
+ qla_uprintf(&uiter, "PBIU Registers:");
+ for (cnt = 0; cnt < sizeof (fw->pbiu_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->pbiu_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nMailbox Registers:");
+ for (cnt = 0; cnt < sizeof (fw->mailbox_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->mailbox_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nDMA Registers:");
+ for (cnt = 0; cnt < sizeof (fw->dma_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->dma_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC Hardware Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_hdw_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_hdw_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP0 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp0_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp0_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP1 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp1_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp1_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP2 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp2_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp2_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP3 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp3_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp3_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP4 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp4_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp4_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP5 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp5_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp5_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP6 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp6_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp6_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC GP7 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->risc_gp7_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_gp7_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nFrame Buffer Hardware Registers:");
+ for (cnt = 0; cnt < sizeof (fw->frame_buf_hdw_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->frame_buf_hdw_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nFPM B0 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->fpm_b0_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->fpm_b0_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nFPM B1 Registers:");
+ for (cnt = 0; cnt < sizeof (fw->fpm_b1_reg) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n");
+ }
+ qla_uprintf(&uiter, "%04x ", fw->fpm_b1_reg[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\nRISC SRAM:");
+ for (cnt = 0; cnt < sizeof (fw->risc_ram) / 2; cnt++) {
+ if (cnt % 8 == 0) {
+ qla_uprintf(&uiter, "\n%04x: ", cnt + 0x1000);
+ }
+ qla_uprintf(&uiter, "%04x ", fw->risc_ram[cnt]);
+ }
+
+ qla_uprintf(&uiter, "\n\n[<==END] ISP Debug Dump.");
+
+ return;
+}
+
+static int
+qla_uprintf(char **uiter, char *fmt, ...)
+{
+ int iter, len;
+ char buf[128];
+ va_list args;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ for (iter = 0; iter < len; iter++, *uiter += 1)
+ *uiter[0] = buf[iter];
+
+ return (len);
+}
+
+//FIXME
+
+/****************************************************************************/
+/* Driver Debug Functions. */
+/****************************************************************************/
+
+void
+qla2x00_dump_regs(scsi_qla_host_t *ha)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+
+ printk("Mailbox registers:\n");
+ printk("scsi(%ld): mbox 0 0x%04x \n",
+ ha->host_no, RD_MAILBOX_REG(ha, reg, 0));
+ printk("scsi(%ld): mbox 1 0x%04x \n",
+ ha->host_no, RD_MAILBOX_REG(ha, reg, 1));
+ printk("scsi(%ld): mbox 2 0x%04x \n",
+ ha->host_no, RD_MAILBOX_REG(ha, reg, 2));
+ printk("scsi(%ld): mbox 3 0x%04x \n",
+ ha->host_no, RD_MAILBOX_REG(ha, reg, 3));
+ printk("scsi(%ld): mbox 4 0x%04x \n",
+ ha->host_no, RD_MAILBOX_REG(ha, reg, 4));
+ printk("scsi(%ld): mbox 5 0x%04x \n",
+ ha->host_no, RD_MAILBOX_REG(ha, reg, 5));
+}
+
+
+void
+qla2x00_dump_buffer(uint8_t * b, uint32_t size)
+{
+ uint32_t cnt;
+ uint8_t c;
+
+ printk(" 0 1 2 3 4 5 6 7 8 9 "
+ "Ah Bh Ch Dh Eh Fh\n");
+ printk("----------------------------------------"
+ "----------------------\n");
+
+ for (cnt = 0; cnt < size;) {
+ c = *b++;
+ printk("%02x",(uint32_t) c);
+ cnt++;
+ if (!(cnt % 16))
+ printk("\n");
+ else
+ printk(" ");
+ }
+ if (cnt % 16)
+ printk("\n");
+}
+
+/**************************************************************************
+ * qla2x00_print_scsi_cmd
+ * Dumps out info about the scsi cmd and srb.
+ * Input
+ * cmd : struct scsi_cmnd
+ **************************************************************************/
+void
+qla2x00_print_scsi_cmd(struct scsi_cmnd * cmd)
+{
+ int i;
+ struct scsi_qla_host *ha;
+ srb_t *sp;
+
+ ha = (struct scsi_qla_host *)cmd->device->host->hostdata;
+
+ sp = (srb_t *) cmd->SCp.ptr;
+ printk("SCSI Command @=0x%p, Handle=0x%p\n", cmd, cmd->host_scribble);
+ printk(" chan=0x%02x, target=0x%02x, lun=0x%02x, cmd_len=0x%02x\n",
+ cmd->device->channel, cmd->device->id, cmd->device->lun,
+ cmd->cmd_len);
+ printk(" CDB: ");
+ for (i = 0; i < cmd->cmd_len; i++) {
+ printk("0x%02x ", cmd->cmnd[i]);
+ }
+ printk("\n seg_cnt=%d, allowed=%d, retries=%d, "
+ "serial_number_at_timeout=0x%lx\n",
+ cmd->use_sg, cmd->allowed, cmd->retries,
+ cmd->serial_number_at_timeout);
+ printk(" request buffer=0x%p, request buffer len=0x%x\n",
+ cmd->request_buffer, cmd->request_bufflen);
+ printk(" tag=%d, transfersize=0x%x\n",
+ cmd->tag, cmd->transfersize);
+ printk(" serial_number=%lx, SP=%p\n", cmd->serial_number, sp);
+ printk(" data direction=%d\n", cmd->sc_data_direction);
+
+ if (!sp)
+ return;
+
+ printk(" sp flags=0x%x\n", sp->flags);
+ printk(" r_start=0x%lx, u_start=0x%lx, f_start=0x%lx, state=%d\n",
+ sp->r_start, sp->u_start, sp->f_start, sp->state);
+
+ printk(" e_start= 0x%lx, ext_history=%d, fo retry=%d, loopid=%x, "
+ "port path=%d\n", sp->e_start, sp->ext_history, sp->fo_retry_cnt,
+ sp->lun_queue->fclun->fcport->loop_id,
+ sp->lun_queue->fclun->fcport->cur_path);
+}
+
+#if defined(QL_DEBUG_ROUTINES)
+/*
+ * qla2x00_formatted_dump_buffer
+ * Prints string plus buffer.
+ *
+ * Input:
+ * string = Null terminated string (no newline at end).
+ * buffer = buffer address.
+ * wd_size = word size 8, 16, 32 or 64 bits
+ * count = number of words.
+ */
+void
+qla2x00_formatted_dump_buffer(char *string, uint8_t * buffer,
+ uint8_t wd_size, uint32_t count)
+{
+ uint32_t cnt;
+ uint16_t *buf16;
+ uint32_t *buf32;
+
+ if (strcmp(string, "") != 0)
+ printk("%s\n",string);
+
+ switch (wd_size) {
+ case 8:
+ printk(" 0 1 2 3 4 5 6 7 "
+ "8 9 Ah Bh Ch Dh Eh Fh\n");
+ printk("-----------------------------------------"
+ "-------------------------------------\n");
+
+ for (cnt = 1; cnt <= count; cnt++, buffer++) {
+ printk("%02x",*buffer);
+ if (cnt % 16 == 0)
+ printk("\n");
+ else
+ printk(" ");
+ }
+ if (cnt % 16 != 0)
+ printk("\n");
+ break;
+ case 16:
+ printk(" 0 2 4 6 8 Ah "
+ " Ch Eh\n");
+ printk("-----------------------------------------"
+ "-------------\n");
+
+ buf16 = (uint16_t *) buffer;
+ for (cnt = 1; cnt <= count; cnt++, buf16++) {
+ printk("%4x",*buf16);
+
+ if (cnt % 8 == 0)
+ printk("\n");
+ else if (*buf16 < 10)
+ printk(" ");
+ else
+ printk(" ");
+ }
+ if (cnt % 8 != 0)
+ printk("\n");
+ break;
+ case 32:
+ printk(" 0 4 8 Ch\n");
+ printk("------------------------------------------\n");
+
+ buf32 = (uint32_t *) buffer;
+ for (cnt = 1; cnt <= count; cnt++, buf32++) {
+ printk("%8x", *buf32);
+
+ if (cnt % 4 == 0)
+ printk("\n");
+ else if (*buf32 < 10)
+ printk(" ");
+ else
+ printk(" ");
+ }
+ if (cnt % 4 != 0)
+ printk("\n");
+ break;
+ default:
+ break;
+ }
+}
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
new file mode 100644
index 000000000000..d7f56c761418
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -0,0 +1,233 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+/*
+ * Driver debug definitions.
+ */
+/* #define QL_DEBUG_LEVEL_1 */ /* Output register accesses to COM1 */
+/* #define QL_DEBUG_LEVEL_2 */ /* Output error msgs to COM1 */
+/* #define QL_DEBUG_LEVEL_3 */ /* Output function trace msgs to COM1 */
+/* #define QL_DEBUG_LEVEL_4 */ /* Output NVRAM trace msgs to COM1 */
+/* #define QL_DEBUG_LEVEL_5 */ /* Output ring trace msgs to COM1 */
+/* #define QL_DEBUG_LEVEL_6 */ /* Output WATCHDOG timer trace to COM1 */
+/* #define QL_DEBUG_LEVEL_7 */ /* Output RISC load trace msgs to COM1 */
+/* #define QL_DEBUG_LEVEL_8 */ /* Output ring saturation msgs to COM1 */
+/* #define QL_DEBUG_LEVEL_9 */ /* Output IOCTL trace msgs */
+/* #define QL_DEBUG_LEVEL_10 */ /* Output IOCTL error msgs */
+/* #define QL_DEBUG_LEVEL_11 */ /* Output Mbx Cmd trace msgs */
+/* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */
+/* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */
+/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
+/*
+ * Local Macro Definitions.
+ */
+#if defined(QL_DEBUG_LEVEL_1) || defined(QL_DEBUG_LEVEL_2) || \
+ defined(QL_DEBUG_LEVEL_3) || defined(QL_DEBUG_LEVEL_4) || \
+ defined(QL_DEBUG_LEVEL_5) || defined(QL_DEBUG_LEVEL_6) || \
+ defined(QL_DEBUG_LEVEL_7) || defined(QL_DEBUG_LEVEL_8) || \
+ defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_10) || \
+ defined(QL_DEBUG_LEVEL_11) || defined(QL_DEBUG_LEVEL_12) || \
+ defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14)
+ #define QL_DEBUG_ROUTINES
+#endif
+
+/*
+* Macros use for debugging the driver.
+*/
+#undef ENTER_TRACE
+#if defined(ENTER_TRACE)
+#define ENTER(x) do { printk("qla2100 : Entering %s()\n", x); } while (0)
+#define LEAVE(x) do { printk("qla2100 : Leaving %s()\n", x); } while (0)
+#define ENTER_INTR(x) do { printk("qla2100 : Entering %s()\n", x); } while (0)
+#define LEAVE_INTR(x) do { printk("qla2100 : Leaving %s()\n", x); } while (0)
+#else
+#define ENTER(x) do {} while (0)
+#define LEAVE(x) do {} while (0)
+#define ENTER_INTR(x) do {} while (0)
+#define LEAVE_INTR(x) do {} while (0)
+#endif
+
+#if DEBUG_QLA2100
+#define DEBUG(x) do {x;} while (0);
+#else
+#define DEBUG(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_1)
+#define DEBUG1(x) do {x;} while (0);
+#else
+#define DEBUG1(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_2)
+#define DEBUG2(x) do {x;} while (0);
+#define DEBUG2_3(x) do {x;} while (0);
+#define DEBUG2_3_11(x) do {x;} while (0);
+#define DEBUG2_9_10(x) do {x;} while (0);
+#define DEBUG2_11(x) do {x;} while (0);
+#else
+#define DEBUG2(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_3)
+#define DEBUG3(x) do {x;} while (0);
+#define DEBUG2_3(x) do {x;} while (0);
+#define DEBUG2_3_11(x) do {x;} while (0);
+#define DEBUG3_11(x) do {x;} while (0);
+#else
+#define DEBUG3(x) do {} while (0);
+ #if !defined(QL_DEBUG_LEVEL_2)
+ #define DEBUG2_3(x) do {} while (0);
+ #endif
+#endif
+
+#if defined(QL_DEBUG_LEVEL_4)
+#define DEBUG4(x) do {x;} while (0);
+#else
+#define DEBUG4(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_5)
+#define DEBUG5(x) do {x;} while (0);
+#else
+#define DEBUG5(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_7)
+#define DEBUG7(x) do {x;} while (0);
+#else
+#define DEBUG7(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_9)
+#define DEBUG9(x) do {x;} while (0);
+#define DEBUG9_10(x) do {x;} while (0);
+#define DEBUG2_9_10(x) do {x;} while (0);
+#else
+#define DEBUG9(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_10)
+#define DEBUG10(x) do {x;} while (0);
+#define DEBUG2_9_10(x) do {x;} while (0);
+#define DEBUG9_10(x) do {x;} while (0);
+#else
+#define DEBUG10(x) do {} while (0);
+ #if !defined(DEBUG2_9_10)
+ #define DEBUG2_9_10(x) do {} while (0);
+ #endif
+ #if !defined(DEBUG9_10)
+ #define DEBUG9_10(x) do {} while (0);
+ #endif
+#endif
+
+#if defined(QL_DEBUG_LEVEL_11)
+#define DEBUG11(x) do{x;} while(0);
+#if !defined(DEBUG2_11)
+#define DEBUG2_11(x) do{x;} while(0);
+#endif
+#if !defined(DEBUG2_3_11)
+#define DEBUG2_3_11(x) do{x;} while(0);
+#endif
+#if !defined(DEBUG3_11)
+#define DEBUG3_11(x) do{x;} while(0);
+#endif
+#else
+#define DEBUG11(x) do{} while(0);
+ #if !defined(QL_DEBUG_LEVEL_2)
+ #define DEBUG2_11(x) do{} while(0);
+ #if !defined(QL_DEBUG_LEVEL_3)
+ #define DEBUG2_3_11(x) do{} while(0);
+ #endif
+ #endif
+ #if !defined(QL_DEBUG_LEVEL_3)
+ #define DEBUG3_11(x) do{} while(0);
+ #endif
+#endif
+
+#if defined(QL_DEBUG_LEVEL_12)
+#define DEBUG12(x) do {x;} while (0);
+#else
+#define DEBUG12(x) do {} while (0);
+#endif
+
+#if defined(QL_DEBUG_LEVEL_13)
+#define DEBUG13(x) do {x;} while (0)
+#else
+#define DEBUG13(x) do {} while (0)
+#endif
+
+#if defined(QL_DEBUG_LEVEL_14)
+#define DEBUG14(x) do {x;} while (0)
+#else
+#define DEBUG14(x) do {} while (0)
+#endif
+
+/*
+ * Firmware Dump structure definition
+ */
+#define FW_DUMP_SIZE_128K 0xBC000
+#define FW_DUMP_SIZE_512K 0x2FC000
+#define FW_DUMP_SIZE_1M 0x5FC000
+
+struct qla2300_fw_dump {
+ uint16_t hccr;
+ uint16_t pbiu_reg[8];
+ uint16_t risc_host_reg[8];
+ uint16_t mailbox_reg[32];
+ uint16_t resp_dma_reg[32];
+ uint16_t dma_reg[48];
+ uint16_t risc_hdw_reg[16];
+ uint16_t risc_gp0_reg[16];
+ uint16_t risc_gp1_reg[16];
+ uint16_t risc_gp2_reg[16];
+ uint16_t risc_gp3_reg[16];
+ uint16_t risc_gp4_reg[16];
+ uint16_t risc_gp5_reg[16];
+ uint16_t risc_gp6_reg[16];
+ uint16_t risc_gp7_reg[16];
+ uint16_t frame_buf_hdw_reg[64];
+ uint16_t fpm_b0_reg[64];
+ uint16_t fpm_b1_reg[64];
+ uint16_t risc_ram[0xf800];
+ uint16_t stack_ram[0x1000];
+ uint16_t data_ram[1];
+};
+
+struct qla2100_fw_dump {
+ uint16_t hccr;
+ uint16_t pbiu_reg[8];
+ uint16_t mailbox_reg[32];
+ uint16_t dma_reg[48];
+ uint16_t risc_hdw_reg[16];
+ uint16_t risc_gp0_reg[16];
+ uint16_t risc_gp1_reg[16];
+ uint16_t risc_gp2_reg[16];
+ uint16_t risc_gp3_reg[16];
+ uint16_t risc_gp4_reg[16];
+ uint16_t risc_gp5_reg[16];
+ uint16_t risc_gp6_reg[16];
+ uint16_t risc_gp7_reg[16];
+ uint16_t frame_buf_hdw_reg[16];
+ uint16_t fpm_b0_reg[64];
+ uint16_t fpm_b1_reg[64];
+ uint16_t risc_ram[0xf000];
+};
+
+
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
new file mode 100644
index 000000000000..36ae03173a5e
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -0,0 +1,2497 @@
+/********************************************************************************
+* QLOGIC LINUX SOFTWARE
+*
+* QLogic ISP2x00 device driver for Linux 2.6.x
+* Copyright (C) 2003-2004 QLogic Corporation
+* (www.qlogic.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, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+**
+******************************************************************************/
+
+#ifndef __QLA_DEF_H
+#define __QLA_DEF_H
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dmapool.h>
+#include <linux/mempool.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+
+/* XXX(hch): move to pci_ids.h */
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP2300
+#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300
+#endif
+
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP2312
+#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312
+#endif
+
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP2322
+#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322
+#endif
+
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP6312
+#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312
+#endif
+
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP6322
+#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322
+#endif
+
+#if defined(CONFIG_SCSI_QLA21XX) || defined(CONFIG_SCSI_QLA21XX_MODULE)
+#define IS_QLA2100(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2100)
+#else
+#define IS_QLA2100(ha) 0
+#endif
+
+#if defined(CONFIG_SCSI_QLA22XX) || defined(CONFIG_SCSI_QLA22XX_MODULE)
+#define IS_QLA2200(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2200)
+#else
+#define IS_QLA2200(ha) 0
+#endif
+
+#if defined(CONFIG_SCSI_QLA2300) || defined(CONFIG_SCSI_QLA2300_MODULE)
+#define IS_QLA2300(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2300)
+#define IS_QLA2312(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2312)
+#else
+#define IS_QLA2300(ha) 0
+#define IS_QLA2312(ha) 0
+#endif
+
+#if defined(CONFIG_SCSI_QLA2322) || defined(CONFIG_SCSI_QLA2322_MODULE)
+#define IS_QLA2322(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2322)
+#else
+#define IS_QLA2322(ha) 0
+#endif
+
+#if defined(CONFIG_SCSI_QLA6312) || defined(CONFIG_SCSI_QLA6312_MODULE)
+#define IS_QLA6312(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP6312)
+#define IS_QLA6322(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP6322)
+#else
+#define IS_QLA6312(ha) 0
+#define IS_QLA6322(ha) 0
+#endif
+
+#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
+ IS_QLA6312(ha) || IS_QLA6322(ha))
+
+/*
+ * Only non-ISP2[12]00 have extended addressing support in the firmware.
+ */
+#define HAS_EXTENDED_IDS(ha) (!IS_QLA2100(ha) && !IS_QLA2200(ha))
+
+/*
+ * We have MAILBOX_REGISTER_COUNT sized arrays in a few places,
+ * but that's fine as we don't look at the last 24 ones for
+ * ISP2100 HBAs.
+ */
+#define MAILBOX_REGISTER_COUNT_2100 8
+#define MAILBOX_REGISTER_COUNT 32
+
+#define QLA2200A_RISC_ROM_VER 4
+#define FPM_2300 6
+#define FPM_2310 7
+
+#include "qla_settings.h"
+
+/*
+ * Data bit definitions
+ */
+#define BIT_0 0x1
+#define BIT_1 0x2
+#define BIT_2 0x4
+#define BIT_3 0x8
+#define BIT_4 0x10
+#define BIT_5 0x20
+#define BIT_6 0x40
+#define BIT_7 0x80
+#define BIT_8 0x100
+#define BIT_9 0x200
+#define BIT_10 0x400
+#define BIT_11 0x800
+#define BIT_12 0x1000
+#define BIT_13 0x2000
+#define BIT_14 0x4000
+#define BIT_15 0x8000
+#define BIT_16 0x10000
+#define BIT_17 0x20000
+#define BIT_18 0x40000
+#define BIT_19 0x80000
+#define BIT_20 0x100000
+#define BIT_21 0x200000
+#define BIT_22 0x400000
+#define BIT_23 0x800000
+#define BIT_24 0x1000000
+#define BIT_25 0x2000000
+#define BIT_26 0x4000000
+#define BIT_27 0x8000000
+#define BIT_28 0x10000000
+#define BIT_29 0x20000000
+#define BIT_30 0x40000000
+#define BIT_31 0x80000000
+
+#define LSB(x) ((uint8_t)(x))
+#define MSB(x) ((uint8_t)((uint16_t)(x) >> 8))
+
+#define LSW(x) ((uint16_t)(x))
+#define MSW(x) ((uint16_t)((uint32_t)(x) >> 16))
+
+#define LSD(x) ((uint32_t)((uint64_t)(x)))
+#define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
+
+
+/*
+ * I/O register
+*/
+
+#define RD_REG_BYTE(addr) readb(addr)
+#define RD_REG_WORD(addr) readw(addr)
+#define RD_REG_DWORD(addr) readl(addr)
+#define RD_REG_BYTE_RELAXED(addr) readb_relaxed(addr)
+#define RD_REG_WORD_RELAXED(addr) readw_relaxed(addr)
+#define RD_REG_DWORD_RELAXED(addr) readl_relaxed(addr)
+#define WRT_REG_BYTE(addr, data) writeb(data,addr)
+#define WRT_REG_WORD(addr, data) writew(data,addr)
+#define WRT_REG_DWORD(addr, data) writel(data,addr)
+
+/*
+ * Fibre Channel device definitions.
+ */
+#define WWN_SIZE 8 /* Size of WWPN, WWN & WWNN */
+#define MAX_FIBRE_DEVICES 512
+#define MAX_FIBRE_LUNS 256
+#define MAX_RSCN_COUNT 32
+#define MAX_HOST_COUNT 16
+
+/*
+ * Host adapter default definitions.
+ */
+#define MAX_BUSES 1 /* We only have one bus today */
+#define MAX_TARGETS_2100 MAX_FIBRE_DEVICES
+#define MAX_TARGETS_2200 MAX_FIBRE_DEVICES
+#define MAX_TARGETS MAX_FIBRE_DEVICES
+#define MIN_LUNS 8
+#define MAX_LUNS MAX_FIBRE_LUNS
+#define MAX_CMDS_PER_LUN 255
+
+/*
+ * Fibre Channel device definitions.
+ */
+#define SNS_LAST_LOOP_ID_2100 0xfe
+#define SNS_LAST_LOOP_ID_2300 0x7ff
+
+#define LAST_LOCAL_LOOP_ID 0x7d
+#define SNS_FL_PORT 0x7e
+#define FABRIC_CONTROLLER 0x7f
+#define SIMPLE_NAME_SERVER 0x80
+#define SNS_FIRST_LOOP_ID 0x81
+#define MANAGEMENT_SERVER 0xfe
+#define BROADCAST 0xff
+
+#define RESERVED_LOOP_ID(x) ((x > LAST_LOCAL_LOOP_ID && \
+ x < SNS_FIRST_LOOP_ID) || \
+ x == MANAGEMENT_SERVER || \
+ x == BROADCAST)
+
+/*
+ * Timeout timer counts in seconds
+ */
+#define PORT_RETRY_TIME 2
+#define LOOP_DOWN_TIMEOUT 60
+#define LOOP_DOWN_TIME 255 /* 240 */
+#define LOOP_DOWN_RESET (LOOP_DOWN_TIME - 30)
+
+/* Maximum outstanding commands in ISP queues (1-65535) */
+#define MAX_OUTSTANDING_COMMANDS 1024
+
+/* ISP request and response entry counts (37-65535) */
+#define REQUEST_ENTRY_CNT_2100 128 /* Number of request entries. */
+#define REQUEST_ENTRY_CNT_2200 2048 /* Number of request entries. */
+#define REQUEST_ENTRY_CNT_2XXX_EXT_MEM 4096 /* Number of request entries. */
+#define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/
+#define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/
+
+/*
+ * SCSI Request Block
+ */
+typedef struct srb {
+ struct list_head list;
+
+ struct scsi_qla_host *ha; /* HA the SP is queued on */
+
+ struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
+
+ struct timer_list timer; /* Command timer */
+ atomic_t ref_count; /* Reference count for this structure */
+ uint16_t flags;
+
+ /* Request state */
+ uint16_t state;
+
+ /* Target/LUN queue pointers. */
+ struct os_tgt *tgt_queue; /* ptr to visible ha's target */
+ struct os_lun *lun_queue; /* ptr to visible ha's lun */
+ struct fc_lun *fclun; /* FC LUN context pointer. */
+
+ /* Timing counts. */
+ unsigned long e_start; /* Start of extend timeout */
+ unsigned long r_start; /* Start of request */
+ unsigned long u_start; /* When sent to RISC */
+ unsigned long f_start; /* When placed in FO queue*/
+
+ /* Single transfer DMA context */
+ dma_addr_t dma_handle;
+
+ uint32_t request_sense_length;
+ uint8_t *request_sense_ptr;
+
+ int ext_history;
+
+ /* Suspend delay */
+ int delay;
+
+ /* Raw completion info for use by failover ? */
+ uint8_t fo_retry_cnt; /* Retry count this request */
+ uint8_t err_id; /* error id */
+#define SRB_ERR_PORT 1 /* Request failed -- "port down" */
+#define SRB_ERR_LOOP 2 /* Request failed -- "loop down" */
+#define SRB_ERR_DEVICE 3 /* Request failed -- "device error" */
+#define SRB_ERR_OTHER 4
+
+ /* SRB magic number */
+ uint16_t magic;
+#define SRB_MAGIC 0x10CB
+} srb_t;
+
+/*
+ * SRB flag definitions
+ */
+#define SRB_TIMEOUT BIT_0 /* Command timed out */
+#define SRB_DMA_VALID BIT_1 /* Command sent to ISP */
+#define SRB_WATCHDOG BIT_2 /* Command on watchdog list */
+#define SRB_ABORT_PENDING BIT_3 /* Command abort sent to device */
+
+#define SRB_ABORTED BIT_4 /* Command aborted command already */
+#define SRB_RETRY BIT_5 /* Command needs retrying */
+#define SRB_GOT_SENSE BIT_6 /* Command has sense data */
+#define SRB_FAILOVER BIT_7 /* Command in failover state */
+
+#define SRB_BUSY BIT_8 /* Command is in busy retry state */
+#define SRB_FO_CANCEL BIT_9 /* Command don't need to do failover */
+#define SRB_IOCTL BIT_10 /* IOCTL command. */
+#define SRB_TAPE BIT_11 /* FCP2 (Tape) command. */
+
+/*
+ * SRB state definitions
+ */
+#define SRB_FREE_STATE 0 /* returned back */
+#define SRB_PENDING_STATE 1 /* queued in LUN Q */
+#define SRB_ACTIVE_STATE 2 /* in Active Array */
+#define SRB_DONE_STATE 3 /* queued in Done Queue */
+#define SRB_RETRY_STATE 4 /* in Retry Queue */
+#define SRB_SUSPENDED_STATE 5 /* in suspended state */
+#define SRB_NO_QUEUE_STATE 6 /* is in between states */
+#define SRB_ACTIVE_TIMEOUT_STATE 7 /* in Active Array but timed out */
+#define SRB_FAILOVER_STATE 8 /* in Failover Queue */
+#define SRB_SCSI_RETRY_STATE 9 /* in Scsi Retry Queue */
+
+
+/*
+ * ISP I/O Register Set structure definitions.
+ */
+typedef volatile struct {
+ volatile uint16_t flash_address; /* Flash BIOS address */
+ volatile uint16_t flash_data; /* Flash BIOS data */
+ uint16_t unused_1[1]; /* Gap */
+ volatile uint16_t ctrl_status; /* Control/Status */
+#define CSR_FLASH_64K_BANK BIT_3 /* Flash upper 64K bank select */
+#define CSR_FLASH_ENABLE BIT_1 /* Flash BIOS Read/Write enable */
+#define CSR_ISP_SOFT_RESET BIT_0 /* ISP soft reset */
+
+ volatile uint16_t ictrl; /* Interrupt control */
+#define ICR_EN_INT BIT_15 /* ISP enable interrupts. */
+#define ICR_EN_RISC BIT_3 /* ISP enable RISC interrupts. */
+
+ volatile uint16_t istatus; /* Interrupt status */
+#define ISR_RISC_INT BIT_3 /* RISC interrupt */
+
+ volatile uint16_t semaphore; /* Semaphore */
+ volatile uint16_t nvram; /* NVRAM register. */
+#define NVR_DESELECT 0
+#define NVR_BUSY BIT_15
+#define NVR_WRT_ENABLE BIT_14 /* Write enable */
+#define NVR_PR_ENABLE BIT_13 /* Protection register enable */
+#define NVR_DATA_IN BIT_3
+#define NVR_DATA_OUT BIT_2
+#define NVR_SELECT BIT_1
+#define NVR_CLOCK BIT_0
+
+ union {
+ struct {
+ volatile uint16_t mailbox0;
+ volatile uint16_t mailbox1;
+ volatile uint16_t mailbox2;
+ volatile uint16_t mailbox3;
+ volatile uint16_t mailbox4;
+ volatile uint16_t mailbox5;
+ volatile uint16_t mailbox6;
+ volatile uint16_t mailbox7;
+ uint16_t unused_2[59]; /* Gap */
+ } __attribute__((packed)) isp2100;
+ struct {
+ /* Request Queue */
+ volatile uint16_t req_q_in; /* In-Pointer */
+ volatile uint16_t req_q_out; /* Out-Pointer */
+ /* Response Queue */
+ volatile uint16_t rsp_q_in; /* In-Pointer */
+ volatile uint16_t rsp_q_out; /* Out-Pointer */
+
+ /* RISC to Host Status */
+ volatile uint32_t host_status;
+#define HSR_RISC_INT BIT_15 /* RISC interrupt */
+#define HSR_RISC_PAUSED BIT_8 /* RISC Paused */
+
+ /* Host to Host Semaphore */
+ volatile uint16_t host_semaphore;
+ uint16_t unused_3[17]; /* Gap */
+ volatile uint16_t mailbox0;
+ volatile uint16_t mailbox1;
+ volatile uint16_t mailbox2;
+ volatile uint16_t mailbox3;
+ volatile uint16_t mailbox4;
+ volatile uint16_t mailbox5;
+ volatile uint16_t mailbox6;
+ volatile uint16_t mailbox7;
+ volatile uint16_t mailbox8;
+ volatile uint16_t mailbox9;
+ volatile uint16_t mailbox10;
+ volatile uint16_t mailbox11;
+ volatile uint16_t mailbox12;
+ volatile uint16_t mailbox13;
+ volatile uint16_t mailbox14;
+ volatile uint16_t mailbox15;
+ volatile uint16_t mailbox16;
+ volatile uint16_t mailbox17;
+ volatile uint16_t mailbox18;
+ volatile uint16_t mailbox19;
+ volatile uint16_t mailbox20;
+ volatile uint16_t mailbox21;
+ volatile uint16_t mailbox22;
+ volatile uint16_t mailbox23;
+ volatile uint16_t mailbox24;
+ volatile uint16_t mailbox25;
+ volatile uint16_t mailbox26;
+ volatile uint16_t mailbox27;
+ volatile uint16_t mailbox28;
+ volatile uint16_t mailbox29;
+ volatile uint16_t mailbox30;
+ volatile uint16_t mailbox31;
+ volatile uint16_t fb_cmd;
+ uint16_t unused_4[10]; /* Gap */
+ } __attribute__((packed)) isp2300;
+ } u;
+
+ volatile uint16_t fpm_diag_config;
+ uint16_t unused_5[0x6]; /* Gap */
+ volatile uint16_t pcr; /* Processor Control Register. */
+ uint16_t unused_6[0x5]; /* Gap */
+ volatile uint16_t mctr; /* Memory Configuration and Timing. */
+ uint16_t unused_7[0x3]; /* Gap */
+ volatile uint16_t fb_cmd_2100; /* Unused on 23XX */
+ uint16_t unused_8[0x3]; /* Gap */
+ volatile uint16_t hccr; /* Host command & control register. */
+#define HCCR_HOST_INT BIT_7 /* Host interrupt bit */
+#define HCCR_RISC_PAUSE BIT_5 /* Pause mode bit */
+ /* HCCR commands */
+#define HCCR_RESET_RISC 0x1000 /* Reset RISC */
+#define HCCR_PAUSE_RISC 0x2000 /* Pause RISC */
+#define HCCR_RELEASE_RISC 0x3000 /* Release RISC from reset. */
+#define HCCR_SET_HOST_INT 0x5000 /* Set host interrupt */
+#define HCCR_CLR_HOST_INT 0x6000 /* Clear HOST interrupt */
+#define HCCR_CLR_RISC_INT 0x7000 /* Clear RISC interrupt */
+#define HCCR_DISABLE_PARITY_PAUSE 0x4001 /* Disable parity error RISC pause. */
+#define HCCR_ENABLE_PARITY 0xA000 /* Enable PARITY interrupt */
+
+ uint16_t unused_9[5]; /* Gap */
+ volatile uint16_t gpiod; /* GPIO Data register. */
+ volatile uint16_t gpioe; /* GPIO Enable register. */
+#define GPIO_LED_MASK 0x00C0
+#define GPIO_LED_GREEN_OFF_AMBER_OFF 0x0000
+#define GPIO_LED_GREEN_ON_AMBER_OFF 0x0040
+#define GPIO_LED_GREEN_OFF_AMBER_ON 0x0080
+#define GPIO_LED_GREEN_ON_AMBER_ON 0x00C0
+
+ union {
+ struct {
+ uint16_t unused_10[8]; /* Gap */
+ volatile uint16_t mailbox8;
+ volatile uint16_t mailbox9;
+ volatile uint16_t mailbox10;
+ volatile uint16_t mailbox11;
+ volatile uint16_t mailbox12;
+ volatile uint16_t mailbox13;
+ volatile uint16_t mailbox14;
+ volatile uint16_t mailbox15;
+ volatile uint16_t mailbox16;
+ volatile uint16_t mailbox17;
+ volatile uint16_t mailbox18;
+ volatile uint16_t mailbox19;
+ volatile uint16_t mailbox20;
+ volatile uint16_t mailbox21;
+ volatile uint16_t mailbox22;
+ volatile uint16_t mailbox23; /* Also probe reg. */
+ } __attribute__((packed)) isp2200;
+ } u_end;
+} device_reg_t;
+
+#define ISP_REQ_Q_IN(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox4 : \
+ &(reg)->u.isp2300.req_q_in)
+#define ISP_REQ_Q_OUT(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox4 : \
+ &(reg)->u.isp2300.req_q_out)
+#define ISP_RSP_Q_IN(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox5 : \
+ &(reg)->u.isp2300.rsp_q_in)
+#define ISP_RSP_Q_OUT(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox5 : \
+ &(reg)->u.isp2300.rsp_q_out)
+
+#define MAILBOX_REG(ha, reg, num) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ (num < 8 ? \
+ &(reg)->u.isp2100.mailbox0 + (num) : \
+ &(reg)->u_end.isp2200.mailbox8 + (num) - 8) : \
+ &(reg)->u.isp2300.mailbox0 + (num))
+#define RD_MAILBOX_REG(ha, reg, num) \
+ RD_REG_WORD(MAILBOX_REG(ha, reg, num))
+#define WRT_MAILBOX_REG(ha, reg, num, data) \
+ WRT_REG_WORD(MAILBOX_REG(ha, reg, num), data)
+
+#define FB_CMD_REG(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->fb_cmd_2100 : \
+ &(reg)->u.isp2300.fb_cmd)
+#define RD_FB_CMD_REG(ha, reg) \
+ RD_REG_WORD(FB_CMD_REG(ha, reg))
+#define WRT_FB_CMD_REG(ha, reg, data) \
+ WRT_REG_WORD(FB_CMD_REG(ha, reg), data)
+
+typedef struct {
+ uint32_t out_mb; /* outbound from driver */
+ uint32_t in_mb; /* Incoming from RISC */
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ long buf_size;
+ void *bufp;
+ uint32_t tov;
+ uint8_t flags;
+#define MBX_DMA_IN BIT_0
+#define MBX_DMA_OUT BIT_1
+#define IOCTL_CMD BIT_2
+} mbx_cmd_t;
+
+#define MBX_TOV_SECONDS 30
+
+/*
+ * ISP product identification definitions in mailboxes after reset.
+ */
+#define PROD_ID_1 0x4953
+#define PROD_ID_2 0x0000
+#define PROD_ID_2a 0x5020
+#define PROD_ID_3 0x2020
+
+/*
+ * ISP mailbox Self-Test status codes
+ */
+#define MBS_FRM_ALIVE 0 /* Firmware Alive. */
+#define MBS_CHKSUM_ERR 1 /* Checksum Error. */
+#define MBS_BUSY 4 /* Busy. */
+
+/*
+ * ISP mailbox command complete status codes
+ */
+#define MBS_COMMAND_COMPLETE 0x4000
+#define MBS_INVALID_COMMAND 0x4001
+#define MBS_HOST_INTERFACE_ERROR 0x4002
+#define MBS_TEST_FAILED 0x4003
+#define MBS_COMMAND_ERROR 0x4005
+#define MBS_COMMAND_PARAMETER_ERROR 0x4006
+#define MBS_PORT_ID_USED 0x4007
+#define MBS_LOOP_ID_USED 0x4008
+#define MBS_ALL_IDS_IN_USE 0x4009
+#define MBS_NOT_LOGGED_IN 0x400A
+
+/*
+ * ISP mailbox asynchronous event status codes
+ */
+#define MBA_ASYNC_EVENT 0x8000 /* Asynchronous event. */
+#define MBA_RESET 0x8001 /* Reset Detected. */
+#define MBA_SYSTEM_ERR 0x8002 /* System Error. */
+#define MBA_REQ_TRANSFER_ERR 0x8003 /* Request Transfer Error. */
+#define MBA_RSP_TRANSFER_ERR 0x8004 /* Response Transfer Error. */
+#define MBA_WAKEUP_THRES 0x8005 /* Request Queue Wake-up. */
+#define MBA_LIP_OCCURRED 0x8010 /* Loop Initialization Procedure */
+ /* occurred. */
+#define MBA_LOOP_UP 0x8011 /* FC Loop UP. */
+#define MBA_LOOP_DOWN 0x8012 /* FC Loop Down. */
+#define MBA_LIP_RESET 0x8013 /* LIP reset occurred. */
+#define MBA_PORT_UPDATE 0x8014 /* Port Database update. */
+#define MBA_RSCN_UPDATE 0x8015 /* Register State Chg Notification. */
+#define MBA_LIP_F8 0x8016 /* Received a LIP F8. */
+#define MBA_LOOP_INIT_ERR 0x8017 /* Loop Initialization Error. */
+#define MBA_FABRIC_AUTH_REQ 0x801b /* Fabric Authentication Required. */
+#define MBA_SCSI_COMPLETION 0x8020 /* SCSI Command Complete. */
+#define MBA_CTIO_COMPLETION 0x8021 /* CTIO Complete. */
+#define MBA_IP_COMPLETION 0x8022 /* IP Transmit Command Complete. */
+#define MBA_IP_RECEIVE 0x8023 /* IP Received. */
+#define MBA_IP_BROADCAST 0x8024 /* IP Broadcast Received. */
+#define MBA_IP_LOW_WATER_MARK 0x8025 /* IP Low Water Mark reached. */
+#define MBA_IP_RCV_BUFFER_EMPTY 0x8026 /* IP receive buffer queue empty. */
+#define MBA_IP_HDR_DATA_SPLIT 0x8027 /* IP header/data splitting feature */
+ /* used. */
+#define MBA_POINT_TO_POINT 0x8030 /* Point to point mode. */
+#define MBA_CMPLT_1_16BIT 0x8031 /* Completion 1 16bit IOSB. */
+#define MBA_CMPLT_2_16BIT 0x8032 /* Completion 2 16bit IOSB. */
+#define MBA_CMPLT_3_16BIT 0x8033 /* Completion 3 16bit IOSB. */
+#define MBA_CMPLT_4_16BIT 0x8034 /* Completion 4 16bit IOSB. */
+#define MBA_CMPLT_5_16BIT 0x8035 /* Completion 5 16bit IOSB. */
+#define MBA_CHG_IN_CONNECTION 0x8036 /* Change in connection mode. */
+#define MBA_RIO_RESPONSE 0x8040 /* RIO response queue update. */
+#define MBA_ZIO_RESPONSE 0x8040 /* ZIO response queue update. */
+#define MBA_CMPLT_2_32BIT 0x8042 /* Completion 2 32bit IOSB. */
+#define MBA_BYPASS_NOTIFICATION 0x8043 /* Auto bypass notification. */
+#define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */
+#define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */
+
+/*
+ * Firmware options 1, 2, 3.
+ */
+#define FO1_AE_ON_LIPF8 BIT_0
+#define FO1_AE_ALL_LIP_RESET BIT_1
+#define FO1_CTIO_RETRY BIT_3
+#define FO1_DISABLE_LIP_F7_SW BIT_4
+#define FO1_DISABLE_100MS_LOS_WAIT BIT_5
+#define FO1_DISABLE_GPIO6_7 BIT_6
+#define FO1_AE_ON_LOOP_INIT_ERR BIT_7
+#define FO1_SET_EMPHASIS_SWING BIT_8
+#define FO1_AE_AUTO_BYPASS BIT_9
+#define FO1_ENABLE_PURE_IOCB BIT_10
+#define FO1_AE_PLOGI_RJT BIT_11
+#define FO1_ENABLE_ABORT_SEQUENCE BIT_12
+#define FO1_AE_QUEUE_FULL BIT_13
+
+#define FO2_ENABLE_ATIO_TYPE_3 BIT_0
+#define FO2_REV_LOOPBACK BIT_1
+
+#define FO3_ENABLE_EMERG_IOCB BIT_0
+#define FO3_AE_RND_ERROR BIT_1
+
+/*
+ * ISP mailbox commands
+ */
+#define MBC_LOAD_RAM 1 /* Load RAM. */
+#define MBC_EXECUTE_FIRMWARE 2 /* Execute firmware. */
+#define MBC_WRITE_RAM_WORD 4 /* Write RAM word. */
+#define MBC_READ_RAM_WORD 5 /* Read RAM word. */
+#define MBC_MAILBOX_REGISTER_TEST 6 /* Wrap incoming mailboxes */
+#define MBC_VERIFY_CHECKSUM 7 /* Verify checksum. */
+#define MBC_GET_FIRMWARE_VERSION 8 /* Get firmware revision. */
+#define MBC_LOAD_RISC_RAM 9 /* Load RAM command. */
+#define MBC_DUMP_RISC_RAM 0xa /* Dump RAM command. */
+#define MBC_LOAD_RISC_RAM_EXTENDED 0xb /* Load RAM extended. */
+#define MBC_DUMP_RISC_RAM_EXTENDED 0xc /* Dump RAM extended. */
+#define MBC_WRITE_RAM_WORD_EXTENDED 0xd /* Write RAM word extended */
+#define MBC_READ_RAM_EXTENDED 0xf /* Read RAM extended. */
+#define MBC_IOCB_COMMAND 0x12 /* Execute IOCB command. */
+#define MBC_ABORT_COMMAND 0x15 /* Abort IOCB command. */
+#define MBC_ABORT_DEVICE 0x16 /* Abort device (ID/LUN). */
+#define MBC_ABORT_TARGET 0x17 /* Abort target (ID). */
+#define MBC_RESET 0x18 /* Reset. */
+#define MBC_GET_ADAPTER_LOOP_ID 0x20 /* Get loop id of ISP2200. */
+#define MBC_GET_RETRY_COUNT 0x22 /* Get f/w retry cnt/delay. */
+#define MBC_DISABLE_VI 0x24 /* Disable VI operation. */
+#define MBC_ENABLE_VI 0x25 /* Enable VI operation. */
+#define MBC_GET_FIRMWARE_OPTION 0x28 /* Get Firmware Options. */
+#define MBC_SET_FIRMWARE_OPTION 0x38 /* Set Firmware Options. */
+#define MBC_LOOP_PORT_BYPASS 0x40 /* Loop Port Bypass. */
+#define MBC_LOOP_PORT_ENABLE 0x41 /* Loop Port Enable. */
+#define MBC_GET_RESOURCE_COUNTS 0x42 /* Get Resource Counts. */
+#define MBC_NON_PARTICIPATE 0x43 /* Non-Participating Mode. */
+#define MBC_DIAGNOSTIC_ECHO 0x44 /* Diagnostic echo. */
+#define MBC_DIAGNOSTIC_LOOP_BACK 0x45 /* Diagnostic loop back. */
+#define MBC_ONLINE_SELF_TEST 0x46 /* Online self-test. */
+#define MBC_ENHANCED_GET_PORT_DATABASE 0x47 /* Get port database + login */
+#define MBC_RESET_LINK_STATUS 0x52 /* Reset Link Error Status */
+#define MBC_IOCB_COMMAND_A64 0x54 /* Execute IOCB command (64) */
+#define MBC_SEND_RNID_ELS 0x57 /* Send RNID ELS request */
+#define MBC_SET_RNID_PARAMS 0x59 /* Set RNID parameters */
+#define MBC_GET_RNID_PARAMS 0x5a /* Data Rate */
+#define MBC_DATA_RATE 0x5d /* Get RNID parameters */
+#define MBC_INITIALIZE_FIRMWARE 0x60 /* Initialize firmware */
+#define MBC_INITIATE_LIP 0x62 /* Initiate Loop */
+ /* Initialization Procedure */
+#define MBC_GET_FC_AL_POSITION_MAP 0x63 /* Get FC_AL Position Map. */
+#define MBC_GET_PORT_DATABASE 0x64 /* Get Port Database. */
+#define MBC_CLEAR_ACA 0x65 /* Clear ACA. */
+#define MBC_TARGET_RESET 0x66 /* Target Reset. */
+#define MBC_CLEAR_TASK_SET 0x67 /* Clear Task Set. */
+#define MBC_ABORT_TASK_SET 0x68 /* Abort Task Set. */
+#define MBC_GET_FIRMWARE_STATE 0x69 /* Get firmware state. */
+#define MBC_GET_PORT_NAME 0x6a /* Get port name. */
+#define MBC_GET_LINK_STATUS 0x6b /* Get port link status. */
+#define MBC_LIP_RESET 0x6c /* LIP reset. */
+#define MBC_SEND_SNS_COMMAND 0x6e /* Send Simple Name Server */
+ /* commandd. */
+#define MBC_LOGIN_FABRIC_PORT 0x6f /* Login fabric port. */
+#define MBC_SEND_CHANGE_REQUEST 0x70 /* Send Change Request. */
+#define MBC_LOGOUT_FABRIC_PORT 0x71 /* Logout fabric port. */
+#define MBC_LIP_FULL_LOGIN 0x72 /* Full login LIP. */
+#define MBC_LOGIN_LOOP_PORT 0x74 /* Login Loop Port. */
+#define MBC_PORT_NODE_NAME_LIST 0x75 /* Get port/node name list. */
+#define MBC_INITIALIZE_RECEIVE_QUEUE 0x77 /* Initialize receive queue */
+#define MBC_UNLOAD_IP 0x79 /* Shutdown IP */
+#define MBC_GET_ID_LIST 0x7C /* Get Port ID list. */
+#define MBC_SEND_LFA_COMMAND 0x7D /* Send Loop Fabric Address */
+#define MBC_LUN_RESET 0x7E /* Send LUN reset */
+
+/* Firmware return data sizes */
+#define FCAL_MAP_SIZE 128
+
+/* Mailbox bit definitions for out_mb and in_mb */
+#define MBX_31 BIT_31
+#define MBX_30 BIT_30
+#define MBX_29 BIT_29
+#define MBX_28 BIT_28
+#define MBX_27 BIT_27
+#define MBX_26 BIT_26
+#define MBX_25 BIT_25
+#define MBX_24 BIT_24
+#define MBX_23 BIT_23
+#define MBX_22 BIT_22
+#define MBX_21 BIT_21
+#define MBX_20 BIT_20
+#define MBX_19 BIT_19
+#define MBX_18 BIT_18
+#define MBX_17 BIT_17
+#define MBX_16 BIT_16
+#define MBX_15 BIT_15
+#define MBX_14 BIT_14
+#define MBX_13 BIT_13
+#define MBX_12 BIT_12
+#define MBX_11 BIT_11
+#define MBX_10 BIT_10
+#define MBX_9 BIT_9
+#define MBX_8 BIT_8
+#define MBX_7 BIT_7
+#define MBX_6 BIT_6
+#define MBX_5 BIT_5
+#define MBX_4 BIT_4
+#define MBX_3 BIT_3
+#define MBX_2 BIT_2
+#define MBX_1 BIT_1
+#define MBX_0 BIT_0
+
+/*
+ * Firmware state codes from get firmware state mailbox command
+ */
+#define FSTATE_CONFIG_WAIT 0
+#define FSTATE_WAIT_AL_PA 1
+#define FSTATE_WAIT_LOGIN 2
+#define FSTATE_READY 3
+#define FSTATE_LOSS_OF_SYNC 4
+#define FSTATE_ERROR 5
+#define FSTATE_REINIT 6
+#define FSTATE_NON_PART 7
+
+#define FSTATE_CONFIG_CORRECT 0
+#define FSTATE_P2P_RCV_LIP 1
+#define FSTATE_P2P_CHOOSE_LOOP 2
+#define FSTATE_P2P_RCV_UNIDEN_LIP 3
+#define FSTATE_FATAL_ERROR 4
+#define FSTATE_LOOP_BACK_CONN 5
+
+/*
+ * Port Database structure definition
+ * Little endian except where noted.
+ */
+#define PORT_DATABASE_SIZE 128 /* bytes */
+typedef struct {
+ uint8_t options;
+ uint8_t control;
+ uint8_t master_state;
+ uint8_t slave_state;
+ uint8_t reserved[2];
+ uint8_t hard_address;
+ uint8_t reserved_1;
+ uint8_t port_id[4];
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint16_t execution_throttle;
+ uint16_t execution_count;
+ uint8_t reset_count;
+ uint8_t reserved_2;
+ uint16_t resource_allocation;
+ uint16_t current_allocation;
+ uint16_t queue_head;
+ uint16_t queue_tail;
+ uint16_t transmit_execution_list_next;
+ uint16_t transmit_execution_list_previous;
+ uint16_t common_features;
+ uint16_t total_concurrent_sequences;
+ uint16_t RO_by_information_category;
+ uint8_t recipient;
+ uint8_t initiator;
+ uint16_t receive_data_size;
+ uint16_t concurrent_sequences;
+ uint16_t open_sequences_per_exchange;
+ uint16_t lun_abort_flags;
+ uint16_t lun_stop_flags;
+ uint16_t stop_queue_head;
+ uint16_t stop_queue_tail;
+ uint16_t port_retry_timer;
+ uint16_t next_sequence_id;
+ uint16_t frame_count;
+ uint16_t PRLI_payload_length;
+ uint8_t prli_svc_param_word_0[2]; /* Big endian */
+ /* Bits 15-0 of word 0 */
+ uint8_t prli_svc_param_word_3[2]; /* Big endian */
+ /* Bits 15-0 of word 3 */
+ uint16_t loop_id;
+ uint16_t extended_lun_info_list_pointer;
+ uint16_t extended_lun_stop_list_pointer;
+} port_database_t;
+
+/*
+ * Port database slave/master states
+ */
+#define PD_STATE_DISCOVERY 0
+#define PD_STATE_WAIT_DISCOVERY_ACK 1
+#define PD_STATE_PORT_LOGIN 2
+#define PD_STATE_WAIT_PORT_LOGIN_ACK 3
+#define PD_STATE_PROCESS_LOGIN 4
+#define PD_STATE_WAIT_PROCESS_LOGIN_ACK 5
+#define PD_STATE_PORT_LOGGED_IN 6
+#define PD_STATE_PORT_UNAVAILABLE 7
+#define PD_STATE_PROCESS_LOGOUT 8
+#define PD_STATE_WAIT_PROCESS_LOGOUT_ACK 9
+#define PD_STATE_PORT_LOGOUT 10
+#define PD_STATE_WAIT_PORT_LOGOUT_ACK 11
+
+
+/*
+ * ISP Initialization Control Block.
+ * Little endian except where noted.
+ */
+#define ICB_VERSION 1
+typedef struct {
+ uint8_t version;
+ uint8_t reserved_1;
+
+ /*
+ * LSB BIT 0 = Enable Hard Loop Id
+ * LSB BIT 1 = Enable Fairness
+ * LSB BIT 2 = Enable Full-Duplex
+ * LSB BIT 3 = Enable Fast Posting
+ * LSB BIT 4 = Enable Target Mode
+ * LSB BIT 5 = Disable Initiator Mode
+ * LSB BIT 6 = Enable ADISC
+ * LSB BIT 7 = Enable Target Inquiry Data
+ *
+ * MSB BIT 0 = Enable PDBC Notify
+ * MSB BIT 1 = Non Participating LIP
+ * MSB BIT 2 = Descending Loop ID Search
+ * MSB BIT 3 = Acquire Loop ID in LIPA
+ * MSB BIT 4 = Stop PortQ on Full Status
+ * MSB BIT 5 = Full Login after LIP
+ * MSB BIT 6 = Node Name Option
+ * MSB BIT 7 = Ext IFWCB enable bit
+ */
+ uint8_t firmware_options[2];
+
+ uint16_t frame_payload_size;
+ uint16_t max_iocb_allocation;
+ uint16_t execution_throttle;
+ uint8_t retry_count;
+ uint8_t retry_delay; /* unused */
+ uint8_t port_name[WWN_SIZE]; /* Big endian. */
+ uint16_t hard_address;
+ uint8_t inquiry_data;
+ uint8_t login_timeout;
+ uint8_t node_name[WWN_SIZE]; /* Big endian. */
+
+ uint16_t request_q_outpointer;
+ uint16_t response_q_inpointer;
+ uint16_t request_q_length;
+ uint16_t response_q_length;
+ uint32_t request_q_address[2];
+ uint32_t response_q_address[2];
+
+ uint16_t lun_enables;
+ uint8_t command_resource_count;
+ uint8_t immediate_notify_resource_count;
+ uint16_t timeout;
+ uint8_t reserved_2[2];
+
+ /*
+ * LSB BIT 0 = Timer Operation mode bit 0
+ * LSB BIT 1 = Timer Operation mode bit 1
+ * LSB BIT 2 = Timer Operation mode bit 2
+ * LSB BIT 3 = Timer Operation mode bit 3
+ * LSB BIT 4 = Init Config Mode bit 0
+ * LSB BIT 5 = Init Config Mode bit 1
+ * LSB BIT 6 = Init Config Mode bit 2
+ * LSB BIT 7 = Enable Non part on LIHA failure
+ *
+ * MSB BIT 0 = Enable class 2
+ * MSB BIT 1 = Enable ACK0
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 = FC Tape Enable
+ * MSB BIT 5 = Enable FC Confirm
+ * MSB BIT 6 = Enable command queuing in target mode
+ * MSB BIT 7 = No Logo On Link Down
+ */
+ uint8_t add_firmware_options[2];
+
+ uint8_t response_accumulation_timer;
+ uint8_t interrupt_delay_timer;
+
+ /*
+ * LSB BIT 0 = Enable Read xfr_rdy
+ * LSB BIT 1 = Soft ID only
+ * LSB BIT 2 =
+ * LSB BIT 3 =
+ * LSB BIT 4 = FCP RSP Payload [0]
+ * LSB BIT 5 = FCP RSP Payload [1] / Sbus enable - 2200
+ * LSB BIT 6 = Enable Out-of-Order frame handling
+ * LSB BIT 7 = Disable Automatic PLOGI on Local Loop
+ *
+ * MSB BIT 0 = Sbus enable - 2300
+ * MSB BIT 1 =
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 =
+ * MSB BIT 5 = enable 50 ohm termination
+ * MSB BIT 6 = Data Rate (2300 only)
+ * MSB BIT 7 = Data Rate (2300 only)
+ */
+ uint8_t special_options[2];
+
+ uint8_t reserved_3[26];
+} init_cb_t;
+
+/*
+ * Get Link Status mailbox command return buffer.
+ */
+typedef struct {
+ uint32_t link_fail_cnt;
+ uint32_t loss_sync_cnt;
+ uint32_t loss_sig_cnt;
+ uint32_t prim_seq_err_cnt;
+ uint32_t inval_xmit_word_cnt;
+ uint32_t inval_crc_cnt;
+} link_stat_t;
+
+/*
+ * NVRAM Command values.
+ */
+#define NV_START_BIT BIT_2
+#define NV_WRITE_OP (BIT_26+BIT_24)
+#define NV_READ_OP (BIT_26+BIT_25)
+#define NV_ERASE_OP (BIT_26+BIT_25+BIT_24)
+#define NV_MASK_OP (BIT_26+BIT_25+BIT_24)
+#define NV_DELAY_COUNT 10
+
+/*
+ * QLogic ISP2100, ISP2200 and ISP2300 NVRAM structure definition.
+ */
+typedef struct {
+ /*
+ * NVRAM header
+ */
+ uint8_t id[4];
+ uint8_t nvram_version;
+ uint8_t reserved_0;
+
+ /*
+ * NVRAM RISC parameter block
+ */
+ uint8_t parameter_block_version;
+ uint8_t reserved_1;
+
+ /*
+ * LSB BIT 0 = Enable Hard Loop Id
+ * LSB BIT 1 = Enable Fairness
+ * LSB BIT 2 = Enable Full-Duplex
+ * LSB BIT 3 = Enable Fast Posting
+ * LSB BIT 4 = Enable Target Mode
+ * LSB BIT 5 = Disable Initiator Mode
+ * LSB BIT 6 = Enable ADISC
+ * LSB BIT 7 = Enable Target Inquiry Data
+ *
+ * MSB BIT 0 = Enable PDBC Notify
+ * MSB BIT 1 = Non Participating LIP
+ * MSB BIT 2 = Descending Loop ID Search
+ * MSB BIT 3 = Acquire Loop ID in LIPA
+ * MSB BIT 4 = Stop PortQ on Full Status
+ * MSB BIT 5 = Full Login after LIP
+ * MSB BIT 6 = Node Name Option
+ * MSB BIT 7 = Ext IFWCB enable bit
+ */
+ uint8_t firmware_options[2];
+
+ uint16_t frame_payload_size;
+ uint16_t max_iocb_allocation;
+ uint16_t execution_throttle;
+ uint8_t retry_count;
+ uint8_t retry_delay; /* unused */
+ uint8_t port_name[WWN_SIZE]; /* Big endian. */
+ uint16_t hard_address;
+ uint8_t inquiry_data;
+ uint8_t login_timeout;
+ uint8_t node_name[WWN_SIZE]; /* Big endian. */
+
+ /*
+ * LSB BIT 0 = Timer Operation mode bit 0
+ * LSB BIT 1 = Timer Operation mode bit 1
+ * LSB BIT 2 = Timer Operation mode bit 2
+ * LSB BIT 3 = Timer Operation mode bit 3
+ * LSB BIT 4 = Init Config Mode bit 0
+ * LSB BIT 5 = Init Config Mode bit 1
+ * LSB BIT 6 = Init Config Mode bit 2
+ * LSB BIT 7 = Enable Non part on LIHA failure
+ *
+ * MSB BIT 0 = Enable class 2
+ * MSB BIT 1 = Enable ACK0
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 = FC Tape Enable
+ * MSB BIT 5 = Enable FC Confirm
+ * MSB BIT 6 = Enable command queuing in target mode
+ * MSB BIT 7 = No Logo On Link Down
+ */
+ uint8_t add_firmware_options[2];
+
+ uint8_t response_accumulation_timer;
+ uint8_t interrupt_delay_timer;
+
+ /*
+ * LSB BIT 0 = Enable Read xfr_rdy
+ * LSB BIT 1 = Soft ID only
+ * LSB BIT 2 =
+ * LSB BIT 3 =
+ * LSB BIT 4 = FCP RSP Payload [0]
+ * LSB BIT 5 = FCP RSP Payload [1] / Sbus enable - 2200
+ * LSB BIT 6 = Enable Out-of-Order frame handling
+ * LSB BIT 7 = Disable Automatic PLOGI on Local Loop
+ *
+ * MSB BIT 0 = Sbus enable - 2300
+ * MSB BIT 1 =
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 =
+ * MSB BIT 5 = enable 50 ohm termination
+ * MSB BIT 6 = Data Rate (2300 only)
+ * MSB BIT 7 = Data Rate (2300 only)
+ */
+ uint8_t special_options[2];
+
+ /* Reserved for expanded RISC parameter block */
+ uint8_t reserved_2[22];
+
+ /*
+ * LSB BIT 0 = Tx Sensitivity 1G bit 0
+ * LSB BIT 1 = Tx Sensitivity 1G bit 1
+ * LSB BIT 2 = Tx Sensitivity 1G bit 2
+ * LSB BIT 3 = Tx Sensitivity 1G bit 3
+ * LSB BIT 4 = Rx Sensitivity 1G bit 0
+ * LSB BIT 5 = Rx Sensitivity 1G bit 1
+ * LSB BIT 6 = Rx Sensitivity 1G bit 2
+ * LSB BIT 7 = Rx Sensitivity 1G bit 3
+ *
+ * MSB BIT 0 = Tx Sensitivity 2G bit 0
+ * MSB BIT 1 = Tx Sensitivity 2G bit 1
+ * MSB BIT 2 = Tx Sensitivity 2G bit 2
+ * MSB BIT 3 = Tx Sensitivity 2G bit 3
+ * MSB BIT 4 = Rx Sensitivity 2G bit 0
+ * MSB BIT 5 = Rx Sensitivity 2G bit 1
+ * MSB BIT 6 = Rx Sensitivity 2G bit 2
+ * MSB BIT 7 = Rx Sensitivity 2G bit 3
+ *
+ * LSB BIT 0 = Output Swing 1G bit 0
+ * LSB BIT 1 = Output Swing 1G bit 1
+ * LSB BIT 2 = Output Swing 1G bit 2
+ * LSB BIT 3 = Output Emphasis 1G bit 0
+ * LSB BIT 4 = Output Emphasis 1G bit 1
+ * LSB BIT 5 = Output Swing 2G bit 0
+ * LSB BIT 6 = Output Swing 2G bit 1
+ * LSB BIT 7 = Output Swing 2G bit 2
+ *
+ * MSB BIT 0 = Output Emphasis 2G bit 0
+ * MSB BIT 1 = Output Emphasis 2G bit 1
+ * MSB BIT 2 = Output Enable
+ * MSB BIT 3 =
+ * MSB BIT 4 =
+ * MSB BIT 5 =
+ * MSB BIT 6 =
+ * MSB BIT 7 =
+ */
+ uint8_t seriallink_options[4];
+
+ /*
+ * NVRAM host parameter block
+ *
+ * LSB BIT 0 = Enable spinup delay
+ * LSB BIT 1 = Disable BIOS
+ * LSB BIT 2 = Enable Memory Map BIOS
+ * LSB BIT 3 = Enable Selectable Boot
+ * LSB BIT 4 = Disable RISC code load
+ * LSB BIT 5 = Set cache line size 1
+ * LSB BIT 6 = PCI Parity Disable
+ * LSB BIT 7 = Enable extended logging
+ *
+ * MSB BIT 0 = Enable 64bit addressing
+ * MSB BIT 1 = Enable lip reset
+ * MSB BIT 2 = Enable lip full login
+ * MSB BIT 3 = Enable target reset
+ * MSB BIT 4 = Enable database storage
+ * MSB BIT 5 = Enable cache flush read
+ * MSB BIT 6 = Enable database load
+ * MSB BIT 7 = Enable alternate WWN
+ */
+ uint8_t host_p[2];
+
+ uint8_t boot_node_name[WWN_SIZE];
+ uint8_t boot_lun_number;
+ uint8_t reset_delay;
+ uint8_t port_down_retry_count;
+ uint8_t boot_id_number;
+ uint16_t max_luns_per_target;
+ uint8_t fcode_boot_port_name[WWN_SIZE];
+ uint8_t alternate_port_name[WWN_SIZE];
+ uint8_t alternate_node_name[WWN_SIZE];
+
+ /*
+ * BIT 0 = Selective Login
+ * BIT 1 = Alt-Boot Enable
+ * BIT 2 =
+ * BIT 3 = Boot Order List
+ * BIT 4 =
+ * BIT 5 = Selective LUN
+ * BIT 6 =
+ * BIT 7 = unused
+ */
+ uint8_t efi_parameters;
+
+ uint8_t link_down_timeout;
+
+ uint8_t adapter_id_0[4];
+ uint8_t adapter_id_1[4];
+ uint8_t adapter_id_2[4];
+ uint8_t adapter_id_3[4];
+
+ uint8_t alt1_boot_node_name[WWN_SIZE];
+ uint16_t alt1_boot_lun_number;
+ uint8_t alt2_boot_node_name[WWN_SIZE];
+ uint16_t alt2_boot_lun_number;
+ uint8_t alt3_boot_node_name[WWN_SIZE];
+ uint16_t alt3_boot_lun_number;
+ uint8_t alt4_boot_node_name[WWN_SIZE];
+ uint16_t alt4_boot_lun_number;
+ uint8_t alt5_boot_node_name[WWN_SIZE];
+ uint16_t alt5_boot_lun_number;
+ uint8_t alt6_boot_node_name[WWN_SIZE];
+ uint16_t alt6_boot_lun_number;
+ uint8_t alt7_boot_node_name[WWN_SIZE];
+ uint16_t alt7_boot_lun_number;
+
+ uint8_t reserved_3[2];
+
+ /* Offset 200-215 : Model Number */
+ uint8_t model_number[16];
+
+ /* OEM related items */
+ uint8_t oem_specific[16];
+
+ /*
+ * NVRAM Adapter Features offset 232-239
+ *
+ * LSB BIT 0 = External GBIC
+ * LSB BIT 1 = Risc RAM parity
+ * LSB BIT 2 = Buffer Plus Module
+ * LSB BIT 3 = Multi Chip Adapter
+ * LSB BIT 4 = Internal connector
+ * LSB BIT 5 =
+ * LSB BIT 6 =
+ * LSB BIT 7 =
+ *
+ * MSB BIT 0 =
+ * MSB BIT 1 =
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 =
+ * MSB BIT 5 =
+ * MSB BIT 6 =
+ * MSB BIT 7 =
+ */
+ uint8_t adapter_features[2];
+
+ uint8_t reserved_4[16];
+
+ /* Subsystem vendor ID for ISP2200 */
+ uint16_t subsystem_vendor_id_2200;
+
+ /* Subsystem device ID for ISP2200 */
+ uint16_t subsystem_device_id_2200;
+
+ uint8_t reserved_5;
+ uint8_t checksum;
+} nvram_t;
+
+/*
+ * ISP queue - response queue entry definition.
+ */
+typedef struct {
+ uint8_t data[60];
+ uint32_t signature;
+#define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */
+} response_t;
+
+typedef union {
+ uint16_t extended;
+ struct {
+ uint8_t reserved;
+ uint8_t standard;
+ } id;
+} target_id_t;
+
+#define SET_TARGET_ID(ha, to, from) \
+do { \
+ if (HAS_EXTENDED_IDS(ha)) \
+ to.extended = cpu_to_le16(from); \
+ else \
+ to.id.standard = (uint8_t)from; \
+} while (0)
+
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define COMMAND_TYPE 0x11 /* Command entry */
+#define MAX_CMDSZ 16 /* SCSI maximum CDB size. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ target_id_t target; /* SCSI ID */
+ uint16_t lun; /* SCSI LUN */
+ uint16_t control_flags; /* Control flags. */
+#define CF_WRITE BIT_6
+#define CF_READ BIT_5
+#define CF_SIMPLE_TAG BIT_3
+#define CF_ORDERED_TAG BIT_2
+#define CF_HEAD_TAG BIT_1
+ uint16_t reserved_1;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t byte_count; /* Total byte count. */
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+} cmd_entry_t;
+
+/*
+ * ISP queue - 64-Bit addressing, command entry structure definition.
+ */
+#define COMMAND_A64_TYPE 0x19 /* Command A64 entry */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ target_id_t target; /* SCSI ID */
+ uint16_t lun; /* SCSI LUN */
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved_1;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t byte_count; /* Total byte count. */
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+} cmd_a64_entry_t, request_t;
+
+/*
+ * ISP queue - continuation entry structure definition.
+ */
+#define CONTINUE_TYPE 0x02 /* Continuation entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved;
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+ uint32_t dseg_4_address; /* Data segment 4 address. */
+ uint32_t dseg_4_length; /* Data segment 4 length. */
+ uint32_t dseg_5_address; /* Data segment 5 address. */
+ uint32_t dseg_5_length; /* Data segment 5 length. */
+ uint32_t dseg_6_address; /* Data segment 6 address. */
+ uint32_t dseg_6_length; /* Data segment 6 length. */
+} cont_entry_t;
+
+/*
+ * ISP queue - 64-Bit addressing, continuation entry structure definition.
+ */
+#define CONTINUE_A64_TYPE 0x0A /* Continuation A64 entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address [2]; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address[2]; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+ uint32_t dseg_4_address[2]; /* Data segment 4 address. */
+ uint32_t dseg_4_length; /* Data segment 4 length. */
+} cont_a64_entry_t;
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+#define STATUS_TYPE 0x03 /* Status entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ uint16_t scsi_status; /* SCSI status. */
+ uint16_t comp_status; /* Completion status. */
+ uint16_t state_flags; /* State flags. */
+ uint16_t status_flags; /* Status flags. */
+ uint16_t rsp_info_len; /* Response Info Length. */
+ uint16_t req_sense_length; /* Request sense data length. */
+ uint32_t residual_length; /* Residual transfer length. */
+ uint8_t rsp_info[8]; /* FCP response information. */
+ uint8_t req_sense_data[32]; /* Request sense data. */
+} sts_entry_t;
+
+/*
+ * Status entry entry status
+ */
+#define RF_INV_E_ORDER BIT_5 /* Invalid entry order. */
+#define RF_INV_E_COUNT BIT_4 /* Invalid entry count. */
+#define RF_INV_E_PARAM BIT_3 /* Invalid entry parameter. */
+#define RF_INV_E_TYPE BIT_2 /* Invalid entry type. */
+#define RF_BUSY BIT_1 /* Busy */
+
+/*
+ * Status entry SCSI status bit definitions.
+ */
+#define SS_MASK 0xfff /* Reserved bits BIT_12-BIT_15*/
+#define SS_RESIDUAL_UNDER BIT_11
+#define SS_RESIDUAL_OVER BIT_10
+#define SS_SENSE_LEN_VALID BIT_9
+#define SS_RESPONSE_INFO_LEN_VALID BIT_8
+
+#define SS_RESERVE_CONFLICT (BIT_4 | BIT_3)
+#define SS_BUSY_CONDITION BIT_3
+#define SS_CONDITION_MET BIT_2
+#define SS_CHECK_CONDITION BIT_1
+
+/*
+ * Status entry completion status
+ */
+#define CS_COMPLETE 0x0 /* No errors */
+#define CS_INCOMPLETE 0x1 /* Incomplete transfer of cmd. */
+#define CS_DMA 0x2 /* A DMA direction error. */
+#define CS_TRANSPORT 0x3 /* Transport error. */
+#define CS_RESET 0x4 /* SCSI bus reset occurred */
+#define CS_ABORTED 0x5 /* System aborted command. */
+#define CS_TIMEOUT 0x6 /* Timeout error. */
+#define CS_DATA_OVERRUN 0x7 /* Data overrun. */
+
+#define CS_DATA_UNDERRUN 0x15 /* Data Underrun. */
+#define CS_QUEUE_FULL 0x1C /* Queue Full. */
+#define CS_PORT_UNAVAILABLE 0x28 /* Port unavailable */
+ /* (selection timeout) */
+#define CS_PORT_LOGGED_OUT 0x29 /* Port Logged Out */
+#define CS_PORT_CONFIG_CHG 0x2A /* Port Configuration Changed */
+#define CS_PORT_BUSY 0x2B /* Port Busy */
+#define CS_COMPLETE_CHKCOND 0x30 /* Error? */
+#define CS_BAD_PAYLOAD 0x80 /* Driver defined */
+#define CS_UNKNOWN 0x81 /* Driver defined */
+#define CS_RETRY 0x82 /* Driver defined */
+#define CS_LOOP_DOWN_ABORT 0x83 /* Driver defined */
+
+/*
+ * Status entry status flags
+ */
+#define SF_ABTS_TERMINATED BIT_10
+#define SF_LOGOUT_SENT BIT_13
+
+/*
+ * ISP queue - status continuation entry structure definition.
+ */
+#define STATUS_CONT_TYPE 0x10 /* Status continuation entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint8_t data[60]; /* data */
+} sts_cont_entry_t;
+
+/*
+ * ISP queue - RIO Type 1 status entry (32 bit I/O entry handles)
+ * structure definition.
+ */
+#define STATUS_TYPE_21 0x21 /* Status entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle[15]; /* System handles. */
+} sts21_entry_t;
+
+/*
+ * ISP queue - RIO Type 2 status entry (16 bit I/O entry handles)
+ * structure definition.
+ */
+#define STATUS_TYPE_22 0x22 /* Status entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint16_t handle[30]; /* System handles. */
+} sts22_entry_t;
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+#define MARKER_TYPE 0x04 /* Marker entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t sys_define_2; /* System defined. */
+ target_id_t target; /* SCSI ID */
+ uint8_t modifier; /* Modifier (7-0). */
+#define MK_SYNC_ID_LUN 0 /* Synchronize ID/LUN */
+#define MK_SYNC_ID 1 /* Synchronize ID */
+#define MK_SYNC_ALL 2 /* Synchronize all ID/LUN */
+#define MK_SYNC_LIP 3 /* Synchronize all ID/LUN, */
+ /* clear port changed, */
+ /* use sequence number. */
+ uint8_t reserved_1;
+ uint16_t sequence_number; /* Sequence number of event */
+ uint16_t lun; /* SCSI LUN */
+ uint8_t reserved_2[48];
+} mrk_entry_t;
+
+/*
+ * ISP queue - Management Server entry structure definition.
+ */
+#define MS_IOCB_TYPE 0x29 /* Management Server IOCB entry */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle1; /* System handle. */
+ target_id_t loop_id;
+ uint16_t status;
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved2;
+ uint16_t timeout;
+ uint16_t cmd_dsd_count;
+ uint16_t total_dsd_count;
+ uint8_t type;
+ uint8_t r_ctl;
+ uint16_t rx_id;
+ uint16_t reserved3;
+ uint32_t handle2;
+ uint32_t rsp_bytecount;
+ uint32_t req_bytecount;
+ uint32_t dseg_req_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_req_length; /* Data segment 0 length. */
+ uint32_t dseg_rsp_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_rsp_length; /* Data segment 1 length. */
+} ms_iocb_entry_t;
+
+
+/*
+ * ISP queue - Mailbox Command entry structure definition.
+ */
+#define MBX_IOCB_TYPE 0x39
+struct mbx_entry {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_define1;
+ /* Use sys_define1 for source type */
+#define SOURCE_SCSI 0x00
+#define SOURCE_IP 0x01
+#define SOURCE_VI 0x02
+#define SOURCE_SCTP 0x03
+#define SOURCE_MP 0x04
+#define SOURCE_MPIOCTL 0x05
+#define SOURCE_ASYNC_IOCB 0x07
+
+ uint8_t entry_status;
+
+ uint32_t handle;
+ target_id_t loop_id;
+
+ uint16_t status;
+ uint16_t state_flags;
+ uint16_t status_flags;
+
+ uint32_t sys_define2[2];
+
+ uint16_t mb0;
+ uint16_t mb1;
+ uint16_t mb2;
+ uint16_t mb3;
+ uint16_t mb6;
+ uint16_t mb7;
+ uint16_t mb9;
+ uint16_t mb10;
+ uint32_t reserved_2[2];
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+};
+
+/*
+ * ISP request and response queue entry sizes
+ */
+#define RESPONSE_ENTRY_SIZE (sizeof(response_t))
+#define REQUEST_ENTRY_SIZE (sizeof(request_t))
+
+
+/*
+ * 24 bit port ID type definition.
+ */
+typedef union {
+ uint32_t b24 : 24;
+
+ struct {
+ uint8_t d_id[3];
+ uint8_t rsvd_1;
+ } r;
+
+ struct {
+ uint8_t al_pa;
+ uint8_t area;
+ uint8_t domain;
+ uint8_t rsvd_1;
+ } b;
+} port_id_t;
+#define INVALID_PORT_ID 0xFFFFFF
+
+/*
+ * Switch info gathering structure.
+ */
+typedef struct {
+ port_id_t d_id;
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint32_t type;
+#define SW_TYPE_IP BIT_1
+#define SW_TYPE_SCSI BIT_0
+} sw_info_t;
+
+/*
+ * Inquiry command structure.
+ */
+#define INQ_DATA_SIZE 36
+
+/*
+ * Inquiry mailbox IOCB packet definition.
+ */
+typedef struct {
+ union {
+ cmd_a64_entry_t cmd;
+ sts_entry_t rsp;
+ } p;
+ uint8_t inq[INQ_DATA_SIZE];
+} inq_cmd_rsp_t;
+
+/*
+ * Report LUN command structure.
+ */
+#define CHAR_TO_SHORT(a, b) (uint16_t)((uint8_t)b << 8 | (uint8_t)a)
+
+typedef struct {
+ uint32_t len;
+ uint32_t rsrv;
+} rpt_hdr_t;
+
+typedef struct {
+ struct {
+ uint8_t b : 6;
+ uint8_t address_method : 2;
+ } msb;
+ uint8_t lsb;
+ uint8_t unused[6];
+} rpt_lun_t;
+
+typedef struct {
+ rpt_hdr_t hdr;
+ rpt_lun_t lst[MAX_LUNS];
+} rpt_lun_lst_t;
+
+/*
+ * Report Lun mailbox IOCB packet definition.
+ */
+typedef struct {
+ union {
+ cmd_a64_entry_t cmd;
+ sts_entry_t rsp;
+ } p;
+ rpt_lun_lst_t list;
+} rpt_lun_cmd_rsp_t;
+
+/*
+ * SCSI Target Queue structure
+ */
+typedef struct os_tgt {
+ struct os_lun *olun[MAX_LUNS]; /* LUN context pointer. */
+ struct fc_port *fcport;
+ unsigned long flags;
+ uint8_t port_down_retry_count;
+ uint32_t down_timer;
+ struct scsi_qla_host *ha;
+
+ /* Persistent binding information */
+ port_id_t d_id;
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+} os_tgt_t;
+
+/*
+ * SCSI Target Queue flags
+ */
+#define TQF_ONLINE 0 /* Device online to OS. */
+#define TQF_SUSPENDED 1
+#define TQF_RETRY_CMDS 2
+
+/*
+ * SCSI LUN Queue structure
+ */
+typedef struct os_lun {
+ struct fc_lun *fclun; /* FC LUN context pointer. */
+ spinlock_t q_lock; /* Lun Lock */
+
+ unsigned long q_flag;
+#define LUN_MPIO_RESET_CNTS 1 /* Lun */
+#define LUN_MPIO_BUSY 2 /* Lun is changing paths */
+#define LUN_EXEC_DELAYED 7 /* Lun execution is delayed */
+
+ u_long q_timeout; /* total command timeouts */
+ atomic_t q_timer; /* suspend timer */
+ uint32_t q_count; /* current count */
+ uint32_t q_max; /* maxmum count lun can be suspended */
+ uint8_t q_state; /* lun State */
+#define LUN_STATE_READY 1 /* lun is ready for i/o */
+#define LUN_STATE_RUN 2 /* lun has a timer running */
+#define LUN_STATE_WAIT 3 /* lun is suspended */
+#define LUN_STATE_TIMEOUT 4 /* lun has timed out */
+
+ u_long io_cnt; /* total xfer count since boot */
+ u_long out_cnt; /* total outstanding IO count */
+ u_long w_cnt; /* total writes */
+ u_long r_cnt; /* total reads */
+ u_long avg_time; /* */
+} os_lun_t;
+
+
+/* LUN BitMask structure definition, array of 32bit words,
+ * 1 bit per lun. When bit == 1, the lun is masked.
+ * Most significant bit of mask[0] is lun 0, bit 24 is lun 7.
+ */
+typedef struct lun_bit_mask {
+ /* Must allocate at least enough bits to accomodate all LUNs */
+#if ((MAX_FIBRE_LUNS & 0x7) == 0)
+ uint8_t mask[MAX_FIBRE_LUNS >> 3];
+#else
+ uint8_t mask[(MAX_FIBRE_LUNS + 8) >> 3];
+#endif
+} lun_bit_mask_t;
+
+/*
+ * Fibre channel port type.
+ */
+ typedef enum {
+ FCT_UNKNOWN,
+ FCT_RSCN,
+ FCT_SWITCH,
+ FCT_BROADCAST,
+ FCT_INITIATOR,
+ FCT_TARGET
+} fc_port_type_t;
+
+/*
+ * Fibre channel port structure.
+ */
+typedef struct fc_port {
+ struct list_head list;
+ struct list_head fcluns;
+
+ struct scsi_qla_host *ha;
+ struct scsi_qla_host *vis_ha; /* only used when suspending lun */
+
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ port_id_t d_id;
+ uint16_t loop_id;
+ uint16_t old_loop_id;
+
+ fc_port_type_t port_type;
+
+ atomic_t state;
+ uint32_t flags;
+
+ os_tgt_t *tgt_queue;
+ uint16_t os_target_id;
+
+ uint16_t iodesc_idx_sent;
+
+ int port_login_retry_count;
+ int login_retry;
+ atomic_t port_down_timer;
+
+ uint8_t device_type;
+ uint8_t unused;
+
+ uint8_t mp_byte; /* multi-path byte (not used) */
+ uint8_t cur_path; /* current path id */
+
+ lun_bit_mask_t lun_mask;
+} fc_port_t;
+
+/*
+ * Fibre channel port/lun states.
+ */
+#define FCS_UNCONFIGURED 1
+#define FCS_DEVICE_DEAD 2
+#define FCS_DEVICE_LOST 3
+#define FCS_ONLINE 4
+#define FCS_NOT_SUPPORTED 5
+#define FCS_FAILOVER 6
+#define FCS_FAILOVER_FAILED 7
+
+/*
+ * FC port flags.
+ */
+#define FCF_FABRIC_DEVICE BIT_0
+#define FCF_LOGIN_NEEDED BIT_1
+#define FCF_FO_MASKED BIT_2
+#define FCF_FAILOVER_NEEDED BIT_3
+#define FCF_RESET_NEEDED BIT_4
+#define FCF_PERSISTENT_BOUND BIT_5
+#define FCF_TAPE_PRESENT BIT_6
+#define FCF_FARP_DONE BIT_7
+#define FCF_FARP_FAILED BIT_8
+#define FCF_FARP_REPLY_NEEDED BIT_9
+#define FCF_AUTH_REQ BIT_10
+#define FCF_SEND_AUTH_REQ BIT_11
+#define FCF_RECEIVE_AUTH_REQ BIT_12
+#define FCF_AUTH_SUCCESS BIT_13
+#define FCF_RLC_SUPPORT BIT_14
+#define FCF_CONFIG BIT_15 /* Needed? */
+#define FCF_RESCAN_NEEDED BIT_16
+#define FCF_XP_DEVICE BIT_17
+#define FCF_MSA_DEVICE BIT_18
+#define FCF_EVA_DEVICE BIT_19
+#define FCF_MSA_PORT_ACTIVE BIT_20
+#define FCF_FAILBACK_DISABLE BIT_21
+#define FCF_FAILOVER_DISABLE BIT_22
+#define FCF_DSXXX_DEVICE BIT_23
+#define FCF_AA_EVA_DEVICE BIT_24
+
+/* No loop ID flag. */
+#define FC_NO_LOOP_ID 0x1000
+
+/*
+ * Fibre channel LUN structure.
+ */
+typedef struct fc_lun {
+ struct list_head list;
+
+ fc_port_t *fcport;
+ fc_port_t *o_fcport;
+ uint16_t lun;
+ atomic_t state;
+ uint8_t device_type;
+
+ uint8_t max_path_retries;
+ uint32_t flags;
+} fc_lun_t;
+
+#define FLF_VISIBLE_LUN BIT_0
+#define FLF_ACTIVE_LUN BIT_1
+
+/*
+ * FC-CT interface
+ *
+ * NOTE: All structures are big-endian in form.
+ */
+
+#define CT_REJECT_RESPONSE 0x8001
+#define CT_ACCEPT_RESPONSE 0x8002
+
+#define NS_N_PORT_TYPE 0x01
+#define NS_NL_PORT_TYPE 0x02
+#define NS_NX_PORT_TYPE 0x7F
+
+#define GA_NXT_CMD 0x100
+#define GA_NXT_REQ_SIZE (16 + 4)
+#define GA_NXT_RSP_SIZE (16 + 620)
+
+#define GID_PT_CMD 0x1A1
+#define GID_PT_REQ_SIZE (16 + 4)
+#define GID_PT_RSP_SIZE (16 + (MAX_FIBRE_DEVICES * 4))
+
+#define GPN_ID_CMD 0x112
+#define GPN_ID_REQ_SIZE (16 + 4)
+#define GPN_ID_RSP_SIZE (16 + 8)
+
+#define GNN_ID_CMD 0x113
+#define GNN_ID_REQ_SIZE (16 + 4)
+#define GNN_ID_RSP_SIZE (16 + 8)
+
+#define GFT_ID_CMD 0x117
+#define GFT_ID_REQ_SIZE (16 + 4)
+#define GFT_ID_RSP_SIZE (16 + 32)
+
+#define RFT_ID_CMD 0x217
+#define RFT_ID_REQ_SIZE (16 + 4 + 32)
+#define RFT_ID_RSP_SIZE 16
+
+#define RFF_ID_CMD 0x21F
+#define RFF_ID_REQ_SIZE (16 + 4 + 2 + 1 + 1)
+#define RFF_ID_RSP_SIZE 16
+
+#define RNN_ID_CMD 0x213
+#define RNN_ID_REQ_SIZE (16 + 4 + 8)
+#define RNN_ID_RSP_SIZE 16
+
+#define RSNN_NN_CMD 0x239
+#define RSNN_NN_REQ_SIZE (16 + 8 + 1 + 255)
+#define RSNN_NN_RSP_SIZE 16
+
+/* CT command header -- request/response common fields */
+struct ct_cmd_hdr {
+ uint8_t revision;
+ uint8_t in_id[3];
+ uint8_t gs_type;
+ uint8_t gs_subtype;
+ uint8_t options;
+ uint8_t reserved;
+};
+
+/* CT command request */
+struct ct_sns_req {
+ struct ct_cmd_hdr header;
+ uint16_t command;
+ uint16_t max_rsp_size;
+ uint8_t fragment_id;
+ uint8_t reserved[3];
+
+ union {
+ /* GA_NXT, GPN_ID, GNN_ID, GFT_ID */
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ } port_id;
+
+ struct {
+ uint8_t port_type;
+ uint8_t domain;
+ uint8_t area;
+ uint8_t reserved;
+ } gid_pt;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ uint8_t fc4_types[32];
+ } rft_id;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ uint16_t reserved2;
+ uint8_t fc4_feature;
+ uint8_t fc4_type;
+ } rff_id;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ uint8_t node_name[8];
+ } rnn_id;
+
+ struct {
+ uint8_t node_name[8];
+ uint8_t name_len;
+ uint8_t sym_node_name[255];
+ } rsnn_nn;
+ } req;
+};
+
+/* CT command response header */
+struct ct_rsp_hdr {
+ struct ct_cmd_hdr header;
+ uint16_t response;
+ uint16_t residual;
+ uint8_t fragment_id;
+ uint8_t reason_code;
+ uint8_t explanation_code;
+ uint8_t vendor_unique;
+};
+
+struct ct_sns_gid_pt_data {
+ uint8_t control_byte;
+ uint8_t port_id[3];
+};
+
+struct ct_sns_rsp {
+ struct ct_rsp_hdr header;
+
+ union {
+ struct {
+ uint8_t port_type;
+ uint8_t port_id[3];
+ uint8_t port_name[8];
+ uint8_t sym_port_name_len;
+ uint8_t sym_port_name[255];
+ uint8_t node_name[8];
+ uint8_t sym_node_name_len;
+ uint8_t sym_node_name[255];
+ uint8_t init_proc_assoc[8];
+ uint8_t node_ip_addr[16];
+ uint8_t class_of_service[4];
+ uint8_t fc4_types[32];
+ uint8_t ip_address[16];
+ uint8_t fabric_port_name[8];
+ uint8_t reserved;
+ uint8_t hard_address[3];
+ } ga_nxt;
+
+ struct {
+ struct ct_sns_gid_pt_data entries[MAX_FIBRE_DEVICES];
+ } gid_pt;
+
+ struct {
+ uint8_t port_name[8];
+ } gpn_id;
+
+ struct {
+ uint8_t node_name[8];
+ } gnn_id;
+
+ struct {
+ uint8_t fc4_types[32];
+ } gft_id;
+ } rsp;
+};
+
+struct ct_sns_pkt {
+ union {
+ struct ct_sns_req req;
+ struct ct_sns_rsp rsp;
+ } p;
+};
+
+/*
+ * SNS command structures -- for 2200 compatability.
+ */
+#define RFT_ID_SNS_SCMD_LEN 22
+#define RFT_ID_SNS_CMD_SIZE 60
+#define RFT_ID_SNS_DATA_SIZE 16
+
+#define RNN_ID_SNS_SCMD_LEN 10
+#define RNN_ID_SNS_CMD_SIZE 36
+#define RNN_ID_SNS_DATA_SIZE 16
+
+#define GA_NXT_SNS_SCMD_LEN 6
+#define GA_NXT_SNS_CMD_SIZE 28
+#define GA_NXT_SNS_DATA_SIZE (620 + 16)
+
+#define GID_PT_SNS_SCMD_LEN 6
+#define GID_PT_SNS_CMD_SIZE 28
+#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES * 4 + 16)
+
+#define GPN_ID_SNS_SCMD_LEN 6
+#define GPN_ID_SNS_CMD_SIZE 28
+#define GPN_ID_SNS_DATA_SIZE (8 + 16)
+
+#define GNN_ID_SNS_SCMD_LEN 6
+#define GNN_ID_SNS_CMD_SIZE 28
+#define GNN_ID_SNS_DATA_SIZE (8 + 16)
+
+struct sns_cmd_pkt {
+ union {
+ struct {
+ uint16_t buffer_length;
+ uint16_t reserved_1;
+ uint32_t buffer_address[2];
+ uint16_t subcommand_length;
+ uint16_t reserved_2;
+ uint16_t subcommand;
+ uint16_t size;
+ uint32_t reserved_3;
+ uint8_t param[36];
+ } cmd;
+
+ uint8_t rft_data[RFT_ID_SNS_DATA_SIZE];
+ uint8_t rnn_data[RNN_ID_SNS_DATA_SIZE];
+ uint8_t gan_data[GA_NXT_SNS_DATA_SIZE];
+ uint8_t gid_data[GID_PT_SNS_DATA_SIZE];
+ uint8_t gpn_data[GPN_ID_SNS_DATA_SIZE];
+ uint8_t gnn_data[GNN_ID_SNS_DATA_SIZE];
+ } p;
+};
+
+/* IO descriptors */
+#define MAX_IO_DESCRIPTORS 32
+
+#define ABORT_IOCB_CB 0
+#define ADISC_PORT_IOCB_CB 1
+#define LOGOUT_PORT_IOCB_CB 2
+#define LOGIN_PORT_IOCB_CB 3
+#define LAST_IOCB_CB 4
+
+#define IODESC_INVALID_INDEX 0xFFFF
+#define IODESC_ADISC_NEEDED 0xFFFE
+#define IODESC_LOGIN_NEEDED 0xFFFD
+
+struct io_descriptor {
+ uint16_t used:1;
+ uint16_t idx:11;
+ uint16_t cb_idx:4;
+
+ struct timer_list timer;
+
+ struct scsi_qla_host *ha;
+
+ port_id_t d_id;
+ fc_port_t *remote_fcport;
+
+ uint32_t signature;
+};
+
+struct qla_fw_info {
+ unsigned short addressing; /* addressing method used to load fw */
+#define FW_INFO_ADDR_NORMAL 0
+#define FW_INFO_ADDR_EXTENDED 1
+#define FW_INFO_ADDR_NOMORE 0xffff
+ unsigned short *fwcode; /* pointer to FW array */
+ unsigned short *fwlen; /* number of words in array */
+ unsigned short *fwstart; /* start address for F/W */
+ unsigned long *lfwstart; /* start address (long) for F/W */
+};
+
+struct qla_board_info {
+ char *drv_name;
+
+ char isp_name[8];
+ struct qla_fw_info *fw_info;
+};
+
+/* Return data from MBC_GET_ID_LIST call. */
+struct gid_list_info {
+ uint8_t al_pa;
+ uint8_t area;
+ uint8_t domain;
+ uint8_t loop_id_2100; /* ISP2100/ISP2200 -- 4 bytes. */
+ uint16_t loop_id; /* ISP23XX -- 6 bytes. */
+};
+#define GID_LIST_SIZE (sizeof(struct gid_list_info) * MAX_FIBRE_DEVICES)
+
+/*
+ * Linux Host Adapter structure
+ */
+typedef struct scsi_qla_host {
+ struct list_head list;
+
+ /* Commonly used flags and state information. */
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
+
+ unsigned long host_no;
+ unsigned long instance;
+
+ volatile struct {
+ uint32_t init_done :1;
+ uint32_t online :1;
+ uint32_t mbox_int :1;
+ uint32_t mbox_busy :1;
+ uint32_t rscn_queue_overflow :1;
+ uint32_t reset_active :1;
+
+ uint32_t management_server_logged_in :1;
+ uint32_t process_response_queue :1;
+
+ uint32_t disable_risc_code_load :1;
+ uint32_t enable_64bit_addressing :1;
+ uint32_t enable_lip_reset :1;
+ uint32_t enable_lip_full_login :1;
+ uint32_t enable_target_reset :1;
+ uint32_t enable_led_scheme :1;
+ } flags;
+
+ atomic_t loop_state;
+#define LOOP_TIMEOUT 1
+#define LOOP_DOWN 2
+#define LOOP_UP 3
+#define LOOP_UPDATE 4
+#define LOOP_READY 5
+#define LOOP_DEAD 6
+
+ unsigned long dpc_flags;
+#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
+#define RESET_ACTIVE 1
+#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
+#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
+#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
+#define LOOP_RESYNC_ACTIVE 5
+#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
+#define RSCN_UPDATE 7 /* Perform an RSCN update. */
+#define MAILBOX_RETRY 8
+#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */
+#define FAILOVER_EVENT_NEEDED 10
+#define FAILOVER_EVENT 11
+#define FAILOVER_NEEDED 12
+#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */
+#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */
+#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */
+#define ABORT_QUEUES_NEEDED 16
+#define RELOGIN_NEEDED 17
+#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */
+#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */
+#define ISP_ABORT_RETRY 20 /* ISP aborted. */
+#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */
+#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */
+#define IOCTL_ERROR_RECOVERY 23
+#define LOOP_RESET_NEEDED 24
+
+ uint32_t device_flags;
+#define DFLG_LOCAL_DEVICES BIT_0
+#define DFLG_RETRY_LOCAL_DEVICES BIT_1
+#define DFLG_FABRIC_DEVICES BIT_2
+#define SWITCH_FOUND BIT_3
+#define DFLG_NO_CABLE BIT_4
+
+ /* SRB cache. */
+#define SRB_MIN_REQ 128
+ mempool_t *srb_mempool;
+
+ /* This spinlock is used to protect "io transactions", you must
+ * aquire it before doing any IO to the card, eg with RD_REG*() and
+ * WRT_REG*() for the duration of your entire commandtransaction.
+ *
+ * This spinlock is of lower priority than the io request lock.
+ */
+
+ spinlock_t hardware_lock ____cacheline_aligned;
+
+ device_reg_t __iomem *iobase; /* Base I/O address */
+ unsigned long pio_address;
+ unsigned long pio_length;
+#define MIN_IOBASE_LEN 0x100
+
+ /* ISP ring lock, rings, and indexes */
+ dma_addr_t request_dma; /* Physical address. */
+ request_t *request_ring; /* Base virtual address */
+ request_t *request_ring_ptr; /* Current address. */
+ uint16_t req_ring_index; /* Current index. */
+ uint16_t req_q_cnt; /* Number of available entries. */
+ uint16_t request_q_length;
+
+ dma_addr_t response_dma; /* Physical address. */
+ response_t *response_ring; /* Base virtual address */
+ response_t *response_ring_ptr; /* Current address. */
+ uint16_t rsp_ring_index; /* Current index. */
+ uint16_t response_q_length;
+
+ uint16_t (*calc_request_entries)(uint16_t);
+ void (*build_scsi_iocbs)(srb_t *, cmd_entry_t *, uint16_t);
+
+ /* Outstandings ISP commands. */
+ srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
+ uint32_t current_outstanding_cmd;
+ srb_t *status_srb; /* Status continuation entry. */
+
+ /*
+ * Need to hold the list_lock with irq's disabled in order to access
+ * the following list.
+ *
+ * This list_lock is of lower priority than the host_lock.
+ */
+ spinlock_t list_lock ____cacheline_aligned;
+ /* lock to guard lists which
+ * hold srb_t's */
+ struct list_head retry_queue; /* watchdog queue */
+ struct list_head done_queue; /* job on done queue */
+ struct list_head failover_queue; /* failover list link. */
+ struct list_head scsi_retry_queue; /* SCSI retry queue */
+ struct list_head pending_queue; /* SCSI command pending queue */
+
+ unsigned long done_q_cnt;
+ unsigned long pending_in_q;
+ uint32_t retry_q_cnt;
+ uint32_t scsi_retry_q_cnt;
+ uint32_t failover_cnt;
+
+ unsigned long last_irq_cpu; /* cpu where we got our last irq */
+
+ uint16_t revision;
+ uint8_t ports;
+ u_long actthreads;
+ u_long ipreq_cnt;
+ u_long qthreads;
+
+ uint32_t total_isr_cnt; /* Interrupt count */
+ uint32_t total_isp_aborts; /* controller err cnt */
+ uint32_t total_lip_cnt; /* LIP cnt */
+ uint32_t total_dev_errs; /* device error cnt */
+ uint32_t total_ios; /* IO cnt */
+ uint64_t total_bytes; /* xfr byte cnt */
+ uint32_t total_mbx_timeout; /* mailbox timeout cnt */
+ uint32_t total_loop_resync; /* loop resyn cnt */
+ uint32_t dropped_frame_error_cnt;
+
+ /* ISP configuration data. */
+ uint16_t loop_id; /* Host adapter loop id */
+ uint16_t fb_rev;
+
+ port_id_t d_id; /* Host adapter port id */
+ uint16_t max_public_loop_ids;
+ uint16_t min_external_loopid; /* First external loop Id */
+
+ uint16_t link_data_rate; /* F/W operating speed */
+
+ uint8_t current_topology;
+ uint8_t prev_topology;
+#define ISP_CFG_NL 1
+#define ISP_CFG_N 2
+#define ISP_CFG_FL 4
+#define ISP_CFG_F 8
+
+ uint8_t operating_mode; /* F/W operating mode */
+#define LOOP 0
+#define P2P 1
+#define LOOP_P2P 2
+#define P2P_LOOP 3
+
+ uint8_t marker_needed;
+ uint8_t sns_retry_cnt;
+ uint8_t mem_err;
+
+ uint8_t interrupts_on;
+
+ /* HBA serial number */
+ uint8_t serial0;
+ uint8_t serial1;
+ uint8_t serial2;
+
+ /* NVRAM configuration data */
+ uint16_t nvram_base;
+
+ uint16_t loop_reset_delay;
+ uint16_t minimum_timeout;
+ uint8_t retry_count;
+ uint8_t login_timeout;
+ uint16_t r_a_tov;
+ int port_down_retry_count;
+ uint8_t loop_down_timeout;
+ uint8_t mbx_count;
+ uint16_t max_probe_luns;
+ uint16_t max_luns;
+ uint16_t max_targets;
+ uint16_t last_loop_id;
+
+ uint32_t login_retry_count;
+
+ /* Fibre Channel Device List. */
+ struct list_head fcports;
+ struct list_head rscn_fcports;
+
+ struct io_descriptor io_descriptors[MAX_IO_DESCRIPTORS];
+ uint16_t iodesc_signature;
+
+ /* OS target queue pointers. */
+ os_tgt_t *otgt[MAX_FIBRE_DEVICES];
+
+ /* RSCN queue. */
+ uint32_t rscn_queue[MAX_RSCN_COUNT];
+ uint8_t rscn_in_ptr;
+ uint8_t rscn_out_ptr;
+
+ /* SNS command interfaces. */
+ ms_iocb_entry_t *ms_iocb;
+ dma_addr_t ms_iocb_dma;
+ struct ct_sns_pkt *ct_sns;
+ dma_addr_t ct_sns_dma;
+ /* SNS command interfaces for 2200. */
+ struct sns_cmd_pkt *sns_cmd;
+ dma_addr_t sns_cmd_dma;
+
+ pid_t dpc_pid;
+ int dpc_should_die;
+ struct completion dpc_inited;
+ struct completion dpc_exited;
+ struct semaphore *dpc_wait;
+ uint8_t dpc_active; /* DPC routine is active */
+
+ /* Timeout timers. */
+ uint8_t queue_restart_timer;
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+
+ uint32_t timer_active;
+ struct timer_list timer;
+
+ dma_addr_t gid_list_dma;
+ struct gid_list_info *gid_list;
+
+ dma_addr_t rlc_rsp_dma;
+ rpt_lun_cmd_rsp_t *rlc_rsp;
+
+ /* Small DMA pool allocations -- maximum 256 bytes in length. */
+#define DMA_POOL_SIZE 256
+ struct dma_pool *s_dma_pool;
+
+ dma_addr_t init_cb_dma;
+ init_cb_t *init_cb;
+
+ dma_addr_t iodesc_pd_dma;
+ port_database_t *iodesc_pd;
+
+ /* These are used by mailbox operations. */
+ volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT];
+
+ mbx_cmd_t *mcp;
+ unsigned long mbx_cmd_flags;
+#define MBX_INTERRUPT 1
+#define MBX_INTR_WAIT 2
+#define MBX_UPDATE_FLASH_ACTIVE 3
+
+ spinlock_t mbx_reg_lock; /* Mbx Cmd Register Lock */
+
+ struct semaphore mbx_cmd_sem; /* Serialialize mbx access */
+ struct semaphore mbx_intr_sem; /* Used for completion notification */
+
+ uint32_t mbx_flags;
+#define MBX_IN_PROGRESS BIT_0
+#define MBX_BUSY BIT_1 /* Got the Access */
+#define MBX_SLEEPING_ON_SEM BIT_2
+#define MBX_POLLING_FOR_COMP BIT_3
+#define MBX_COMPLETED BIT_4
+#define MBX_TIMEDOUT BIT_5
+#define MBX_ACCESS_TIMEDOUT BIT_6
+
+ mbx_cmd_t mc;
+
+ uint8_t *cmdline;
+
+ uint32_t failover_type;
+ uint32_t failback_delay;
+ unsigned long cfg_flags;
+#define CFG_ACTIVE 0 /* CFG during a failover, event update, or ioctl */
+#define CFG_FAILOVER 1 /* CFG during path change */
+
+ uint32_t binding_type;
+#define BIND_BY_PORT_NAME 0
+#define BIND_BY_PORT_ID 1
+
+ /* Basic firmware related information. */
+ struct qla_board_info *brd_info;
+ uint16_t fw_major_version;
+ uint16_t fw_minor_version;
+ uint16_t fw_subminor_version;
+ uint16_t fw_attributes;
+ uint32_t fw_memory_size;
+ uint32_t fw_transfer_size;
+
+ uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
+ uint8_t fw_seriallink_options[4];
+
+ /* Firmware dump information. */
+ void *fw_dump;
+ int fw_dump_order;
+ int fw_dump_reading;
+ char *fw_dump_buffer;
+ int fw_dump_buffer_len;
+
+ uint8_t host_str[16];
+ uint16_t pci_attr;
+
+ uint16_t product_id[4];
+
+ uint8_t model_number[16+1];
+#define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ char *model_desc;
+
+ uint8_t node_name[WWN_SIZE];
+ uint8_t nvram_version;
+ uint32_t isp_abort_cnt;
+
+ /* Adapter I/O statistics for failover */
+ uint64_t IosRequested;
+ uint64_t BytesRequested;
+ uint64_t IosExecuted;
+ uint64_t BytesExecuted;
+
+ /* Needed for BEACON */
+ uint16_t beacon_blink_led;
+ uint16_t beacon_green_on;
+} scsi_qla_host_t;
+
+
+/*
+ * Macros to help code, maintain, etc.
+ */
+#define LOOP_TRANSITION(ha) \
+ (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+
+#define LOOP_NOT_READY(ha) \
+ ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \
+ test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || \
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \
+ test_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags)) || \
+ atomic_read(&ha->loop_state) == LOOP_DOWN)
+
+#define LOOP_RDY(ha) (!LOOP_NOT_READY(ha))
+
+#define TGT_Q(ha, t) (ha->otgt[t])
+#define LUN_Q(ha, t, l) (TGT_Q(ha, t)->olun[l])
+#define GET_LU_Q(ha, t, l) ((TGT_Q(ha,t) != NULL)? TGT_Q(ha, t)->olun[l] : NULL)
+
+#define to_qla_host(x) ((scsi_qla_host_t *) (x)->hostdata)
+
+#define qla_printk(level, ha, format, arg...) \
+ dev_printk(level , &((ha)->pdev->dev) , format , ## arg)
+
+/*
+ * qla2x00 local function return status codes
+ */
+#define MBS_MASK 0x3fff
+
+#define QLA_SUCCESS (MBS_COMMAND_COMPLETE & MBS_MASK)
+#define QLA_INVALID_COMMAND (MBS_INVALID_COMMAND & MBS_MASK)
+#define QLA_INTERFACE_ERROR (MBS_HOST_INTERFACE_ERROR & MBS_MASK)
+#define QLA_TEST_FAILED (MBS_TEST_FAILED & MBS_MASK)
+#define QLA_COMMAND_ERROR (MBS_COMMAND_ERROR & MBS_MASK)
+#define QLA_PARAMETER_ERROR (MBS_COMMAND_PARAMETER_ERROR & MBS_MASK)
+#define QLA_PORT_ID_USED (MBS_PORT_ID_USED & MBS_MASK)
+#define QLA_LOOP_ID_USED (MBS_LOOP_ID_USED & MBS_MASK)
+#define QLA_ALL_IDS_IN_USE (MBS_ALL_IDS_IN_USE & MBS_MASK)
+#define QLA_NOT_LOGGED_IN (MBS_NOT_LOGGED_IN & MBS_MASK)
+
+#define QLA_FUNCTION_TIMEOUT 0x100
+#define QLA_FUNCTION_PARAMETER_ERROR 0x101
+#define QLA_FUNCTION_FAILED 0x102
+#define QLA_MEMORY_ALLOC_FAILED 0x103
+#define QLA_LOCK_TIMEOUT 0x104
+#define QLA_ABORTED 0x105
+#define QLA_SUSPENDED 0x106
+#define QLA_BUSY 0x107
+#define QLA_RSCNS_HANDLED 0x108
+
+/*
+* Stat info for all adpaters
+*/
+struct _qla2x00stats {
+ unsigned long mboxtout; /* mailbox timeouts */
+ unsigned long mboxerr; /* mailbox errors */
+ unsigned long ispAbort; /* ISP aborts */
+ unsigned long debugNo;
+ unsigned long loop_resync;
+ unsigned long outarray_full;
+ unsigned long retry_q_cnt;
+};
+
+#define NVRAM_DELAY() udelay(10)
+
+#define INVALID_HANDLE (MAX_OUTSTANDING_COMMANDS+1)
+
+/*
+ * Flash support definitions
+ */
+#define FLASH_IMAGE_SIZE 131072
+
+#include "qla_gbl.h"
+#include "qla_dbg.h"
+#include "qla_inline.h"
+#include "qla_listops.h"
+
+/*
+* String arrays
+*/
+#define LINESIZE 256
+#define MAXARGS 26
+
+#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
+#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual)
+#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
+#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
+#define CMD_ACTUAL_SNSLEN(Cmnd) ((Cmnd)->SCp.Message)
+#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
+
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_devtbl.h b/drivers/scsi/qla2xxx/qla_devtbl.h
new file mode 100644
index 000000000000..4de48019454f
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_devtbl.h
@@ -0,0 +1,110 @@
+#define QLA_MODEL_NAMES 0x32
+
+/*
+ * Adapter model names.
+ */
+static char *qla2x00_model_name[QLA_MODEL_NAMES] = {
+ "QLA2340", /* 0x100 */
+ "QLA2342", /* 0x101 */
+ "QLA2344", /* 0x102 */
+ "QCP2342", /* 0x103 */
+ "QSB2340", /* 0x104 */
+ "QSB2342", /* 0x105 */
+ "QLA2310", /* 0x106 */
+ "QLA2332", /* 0x107 */
+ "QCP2332", /* 0x108 */
+ "QCP2340", /* 0x109 */
+ "QLA2342", /* 0x10a */
+ "QCP2342", /* 0x10b */
+ "QLA2350", /* 0x10c */
+ "QLA2352", /* 0x10d */
+ "QLA2352", /* 0x10e */
+ "HPQ SVS", /* 0x10f */
+ "HPQ SVS", /* 0x110 */
+ " ", /* 0x111 */
+ " ", /* 0x112 */
+ " ", /* 0x113 */
+ " ", /* 0x114 */
+ "QLA2360", /* 0x115 */
+ "QLA2362", /* 0x116 */
+ "QLE2360", /* 0x117 */
+ "QLE2362", /* 0x118 */
+ "QLA200", /* 0x119 */
+ "QLA200C", /* 0x11a */
+ "QLA200P", /* 0x11b */
+ "QLA200P", /* 0x11c */
+ " ", /* 0x11d */
+ " ", /* 0x11e */
+ " ", /* 0x11f */
+ " ", /* 0x120 */
+ " ", /* 0x121 */
+ " ", /* 0x122 */
+ " ", /* 0x123 */
+ " ", /* 0x124 */
+ " ", /* 0x125 */
+ " ", /* 0x126 */
+ " ", /* 0x127 */
+ " ", /* 0x128 */
+ " ", /* 0x129 */
+ " ", /* 0x12a */
+ " ", /* 0x12b */
+ " ", /* 0x12c */
+ " ", /* 0x12d */
+ " ", /* 0x12e */
+ "QLA210", /* 0x12f */
+ "EMC 250", /* 0x130 */
+ "HP A7538A" /* 0x131 */
+};
+
+static char *qla2x00_model_desc[QLA_MODEL_NAMES] = {
+ "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x100 */
+ "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x101 */
+ "133MHz PCI-X to 2Gb FC, Quad Channel", /* 0x102 */
+ " ", /* 0x103 */
+ " ", /* 0x104 */
+ " ", /* 0x105 */
+ " ", /* 0x106 */
+ " ", /* 0x107 */
+ " ", /* 0x108 */
+ " ", /* 0x109 */
+ " ", /* 0x10a */
+ " ", /* 0x10b */
+ "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x10c */
+ "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10d */
+ " ", /* 0x10e */
+ "HPQ SVS HBA- Initiator device", /* 0x10f */
+ "HPQ SVS HBA- Target device", /* 0x110 */
+ " ", /* 0x111 */
+ " ", /* 0x112 */
+ " ", /* 0x113 */
+ " ", /* 0x114 */
+ "133MHz PCI-X to 2Gb FC Single Channel", /* 0x115 */
+ "133MHz PCI-X to 2Gb FC Dual Channel", /* 0x116 */
+ "PCI-Express to 2Gb FC, Single Channel", /* 0x117 */
+ "PCI-Express to 2Gb FC, Dual Channel", /* 0x118 */
+ "133MHz PCI-X to 2Gb FC Optical", /* 0x119 */
+ "133MHz PCI-X to 2Gb FC Copper", /* 0x11a */
+ "133MHz PCI-X to 2Gb FC SFP", /* 0x11b */
+ "133MHz PCI-X to 2Gb FC SFP", /* 0x11c */
+ " ", /* 0x11d */
+ " ", /* 0x11e */
+ " ", /* 0x11f */
+ " ", /* 0x120 */
+ " ", /* 0x121 */
+ " ", /* 0x122 */
+ " ", /* 0x123 */
+ " ", /* 0x124 */
+ " ", /* 0x125 */
+ " ", /* 0x126 */
+ " ", /* 0x127 */
+ " ", /* 0x128 */
+ " ", /* 0x129 */
+ " ", /* 0x12a */
+ " ", /* 0x12b */
+ " ", /* 0x12c */
+ " ", /* 0x12d */
+ " ", /* 0x12e */
+ "133MHz PCI-X to 2Gb FC SFF", /* 0x12f */
+ "133MHz PCI-X to 2Gb FC SFF", /* 0x130 */
+ "HP 1p2g QLA2340" /* 0x131 */
+};
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
new file mode 100644
index 000000000000..5adf2af7ba64
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -0,0 +1,257 @@
+/********************************************************************************
+* QLOGIC LINUX SOFTWARE
+*
+* QLogic ISP2x00 device driver for Linux 2.6.x
+* Copyright (C) 2003-2004 QLogic Corporation
+* (www.qlogic.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, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+******************************************************************************
+* Global include file.
+******************************************************************************/
+
+
+#ifndef __QLA_GBL_H
+#define __QLA_GBL_H
+
+#include <linux/interrupt.h>
+
+extern void qla2x00_remove_one(struct pci_dev *);
+extern int qla2x00_probe_one(struct pci_dev *, struct qla_board_info *);
+
+/*
+ * Global Function Prototypes in qla_init.c source file.
+ */
+extern int qla2x00_initialize_adapter(scsi_qla_host_t *);
+extern fc_port_t *qla2x00_alloc_fcport(scsi_qla_host_t *, int);
+
+extern int qla2x00_loop_resync(scsi_qla_host_t *);
+
+extern int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
+extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
+extern int qla2x00_local_device_login(scsi_qla_host_t *, uint16_t);
+
+extern void qla2x00_restart_queues(scsi_qla_host_t *, uint8_t);
+
+extern void qla2x00_rescan_fcports(scsi_qla_host_t *);
+
+extern void qla2x00_tgt_free(scsi_qla_host_t *ha, uint16_t t);
+
+extern int qla2x00_abort_isp(scsi_qla_host_t *);
+
+/*
+ * Global Data in qla_os.c source file.
+ */
+extern char qla2x00_version_str[];
+
+extern int num_hosts;
+extern int apiHBAInstance;
+
+extern struct _qla2x00stats qla2x00_stats;
+extern int ql2xretrycount;
+extern int ql2xlogintimeout;
+extern int qlport_down_retry;
+extern int ql2xmaxqdepth;
+extern int displayConfig;
+extern int ql2xplogiabsentdevice;
+extern int ql2xenablezio;
+extern int ql2xintrdelaytimer;
+extern int ql2xloginretrycount;
+
+extern int ConfigRequired;
+
+extern int Bind;
+extern int ql2xsuspendcount;
+#if defined(MODULE)
+extern char *ql2xopts;
+#endif
+
+extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
+
+extern void qla2x00_cmd_timeout(srb_t *);
+
+extern int __qla2x00_suspend_lun(scsi_qla_host_t *, os_lun_t *, int, int, int);
+
+extern void qla2x00_done(scsi_qla_host_t *);
+extern void qla2x00_next(scsi_qla_host_t *);
+extern void qla2x00_flush_failover_q(scsi_qla_host_t *, os_lun_t *);
+extern void qla2x00_reset_lun_fo_counts(scsi_qla_host_t *, os_lun_t *);
+
+extern void qla2x00_extend_timeout(struct scsi_cmnd *, int);
+
+extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int);
+extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *);
+
+extern void qla2x00_abort_queues(scsi_qla_host_t *, uint8_t);
+
+extern void qla2x00_blink_led(scsi_qla_host_t *);
+
+extern int qla2x00_down_timeout(struct semaphore *, unsigned long);
+
+/*
+ * Global Function Prototypes in qla_iocb.c source file.
+ */
+extern void qla2x00_isp_cmd(scsi_qla_host_t *);
+
+extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
+extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
+extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
+extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t);
+extern int qla2x00_start_scsi(srb_t *sp);
+int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
+int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
+
+/*
+ * Global Function Prototypes in qla_mbx.c source file.
+ */
+extern int
+qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t);
+
+extern int
+qla2x00_load_ram_ext(scsi_qla_host_t *, dma_addr_t, uint32_t, uint16_t);
+
+extern int
+qla2x00_execute_fw(scsi_qla_host_t *);
+
+extern void
+qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *,
+ uint16_t *, uint16_t *, uint16_t *, uint32_t *);
+
+extern int
+qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_set_fw_options(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_mbx_reg_test(scsi_qla_host_t *);
+
+extern int
+qla2x00_verify_checksum(scsi_qla_host_t *);
+
+extern int
+qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
+
+extern int
+qla2x00_abort_command(scsi_qla_host_t *, srb_t *);
+
+#if USE_ABORT_TGT
+extern int
+qla2x00_abort_target(fc_port_t *fcport);
+#endif
+
+extern int
+qla2x00_target_reset(scsi_qla_host_t *, uint16_t, uint16_t);
+
+extern int
+qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
+ uint8_t *, uint16_t *);
+
+extern int
+qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
+
+extern int
+qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
+
+extern int
+qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
+
+extern int
+qla2x00_get_firmware_state(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_get_port_name(scsi_qla_host_t *, uint16_t, uint8_t *, uint8_t);
+
+extern int
+qla2x00_lip_reset(scsi_qla_host_t *);
+
+extern int
+qla2x00_send_sns(scsi_qla_host_t *, dma_addr_t, uint16_t, size_t);
+
+extern int
+qla2x00_login_fabric(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t,
+ uint16_t *, uint8_t);
+
+extern int
+qla2x00_login_local_device(scsi_qla_host_t *, uint16_t, uint16_t *, uint8_t);
+
+extern int
+qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id);
+
+extern int
+qla2x00_full_login_lip(scsi_qla_host_t *ha);
+
+extern int
+qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
+
+extern int
+qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *,
+ uint16_t *);
+
+extern int
+qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
+
+/*
+ * Global Function Prototypes in qla_isr.c source file.
+ */
+extern irqreturn_t qla2100_intr_handler(int, void *, struct pt_regs *);
+extern irqreturn_t qla2300_intr_handler(int, void *, struct pt_regs *);
+extern void qla2x00_process_response_queue(struct scsi_qla_host *);
+
+/*
+ * Global Function Prototypes in qla_sup.c source file.
+ */
+extern void qla2x00_lock_nvram_access(scsi_qla_host_t *);
+extern void qla2x00_unlock_nvram_access(scsi_qla_host_t *);
+extern void qla2x00_release_nvram_protection(scsi_qla_host_t *);
+extern uint16_t qla2x00_get_nvram_word(scsi_qla_host_t *, uint32_t);
+extern void qla2x00_write_nvram_word(scsi_qla_host_t *, uint32_t, uint16_t);
+/*
+ * Global Function Prototypes in qla_dbg.c source file.
+ */
+extern void qla2100_fw_dump(scsi_qla_host_t *, int);
+extern void qla2300_fw_dump(scsi_qla_host_t *, int);
+extern void qla2100_ascii_fw_dump(scsi_qla_host_t *);
+extern void qla2300_ascii_fw_dump(scsi_qla_host_t *);
+extern void qla2x00_dump_regs(scsi_qla_host_t *);
+extern void qla2x00_dump_buffer(uint8_t *, uint32_t);
+extern void qla2x00_print_scsi_cmd(struct scsi_cmnd *);
+
+/*
+ * Global Function Prototypes in qla_gs.c source file.
+ */
+extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *);
+extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_gnn_id(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_rft_id(scsi_qla_host_t *);
+extern int qla2x00_rff_id(scsi_qla_host_t *);
+extern int qla2x00_rnn_id(scsi_qla_host_t *);
+extern int qla2x00_rsnn_nn(scsi_qla_host_t *);
+
+/*
+ * Global Function Prototypes in qla_rscn.c source file.
+ */
+extern fc_port_t *qla2x00_alloc_rscn_fcport(scsi_qla_host_t *, int);
+extern int qla2x00_handle_port_rscn(scsi_qla_host_t *, uint32_t, fc_port_t *,
+ int);
+extern void qla2x00_process_iodesc(scsi_qla_host_t *, struct mbx_entry *);
+extern void qla2x00_cancel_io_descriptors(scsi_qla_host_t *);
+
+/*
+ * Global Function Prototypes in qla_xioctl.c source file.
+ */
+#define qla2x00_enqueue_aen(ha, cmd, mode) do { } while (0)
+#define qla2x00_alloc_ioctl_mem(ha) (0)
+#define qla2x00_free_ioctl_mem(ha) do { } while (0)
+
+#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
new file mode 100644
index 000000000000..531dad95896c
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -0,0 +1,1059 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+static inline ms_iocb_entry_t *
+qla2x00_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
+
+static inline struct ct_sns_req *
+qla2x00_prep_ct_req(struct ct_sns_req *, uint16_t, uint16_t);
+
+static inline struct sns_cmd_pkt *
+qla2x00_prep_sns_cmd(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t);
+
+static int qla2x00_sns_ga_nxt(scsi_qla_host_t *, fc_port_t *);
+static int qla2x00_sns_gid_pt(scsi_qla_host_t *, sw_info_t *);
+static int qla2x00_sns_gpn_id(scsi_qla_host_t *, sw_info_t *);
+static int qla2x00_sns_gnn_id(scsi_qla_host_t *, sw_info_t *);
+static int qla2x00_sns_rft_id(scsi_qla_host_t *);
+static int qla2x00_sns_rnn_id(scsi_qla_host_t *);
+
+/**
+ * qla2x00_prep_ms_iocb() - Prepare common MS IOCB fields for SNS CT query.
+ * @ha: HA context
+ * @req_size: request size in bytes
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's ms_iocb.
+ */
+static inline ms_iocb_entry_t *
+qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
+{
+ ms_iocb_entry_t *ms_pkt;
+
+ ms_pkt = ha->ms_iocb;
+ memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
+
+ ms_pkt->entry_type = MS_IOCB_TYPE;
+ ms_pkt->entry_count = 1;
+ SET_TARGET_ID(ha, ms_pkt->loop_id, SIMPLE_NAME_SERVER);
+ ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG);
+ ms_pkt->timeout = __constant_cpu_to_le16(25);
+ ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
+ ms_pkt->total_dsd_count = __constant_cpu_to_le16(2);
+ ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size);
+ ms_pkt->req_bytecount = cpu_to_le32(req_size);
+
+ ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
+
+ ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount;
+
+ return (ms_pkt);
+}
+
+/**
+ * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
+ * @ct_req: CT request buffer
+ * @cmd: GS command
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the intitialized @ct_req.
+ */
+static inline struct ct_sns_req *
+qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size)
+{
+ memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+
+ ct_req->header.revision = 0x01;
+ ct_req->header.gs_type = 0xFC;
+ ct_req->header.gs_subtype = 0x02;
+ ct_req->command = cpu_to_be16(cmd);
+ ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+
+ return (ct_req);
+}
+
+
+/**
+ * qla2x00_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
+ * @ha: HA context
+ * @fcport: fcport entry to updated
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+ int rval;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ return (qla2x00_sns_ga_nxt(ha, fcport));
+ }
+
+ /* Issue GA_NXT */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, GA_NXT_REQ_SIZE, GA_NXT_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD,
+ GA_NXT_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = fcport->d_id.b.area;
+ ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n",
+ ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, "
+ "ga_nxt_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Populate fc_port_t entry. */
+ fcport->d_id.b.domain = ct_rsp->rsp.ga_nxt.port_id[0];
+ fcport->d_id.b.area = ct_rsp->rsp.ga_nxt.port_id[1];
+ fcport->d_id.b.al_pa = ct_rsp->rsp.ga_nxt.port_id[2];
+
+ memcpy(fcport->node_name, ct_rsp->rsp.ga_nxt.node_name,
+ WWN_SIZE);
+ memcpy(fcport->port_name, ct_rsp->rsp.ga_nxt.port_name,
+ WWN_SIZE);
+
+ if (ct_rsp->rsp.ga_nxt.port_type != NS_N_PORT_TYPE &&
+ ct_rsp->rsp.ga_nxt.port_type != NS_NL_PORT_TYPE)
+ fcport->d_id.b.domain = 0xf0;
+
+ DEBUG2_3(printk("scsi(%ld): GA_NXT entry - "
+ "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "portid=%02x%02x%02x.\n",
+ ha->host_no,
+ fcport->node_name[0], fcport->node_name[1],
+ fcport->node_name[2], fcport->node_name[3],
+ fcport->node_name[4], fcport->node_name[5],
+ fcport->node_name[6], fcport->node_name[7],
+ fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * NOTE: Non-Nx_Ports are not requested.
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
+{
+ int rval;
+ uint16_t i;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ struct ct_sns_gid_pt_data *gid_data;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ return (qla2x00_sns_gid_pt(ha, list));
+ }
+
+ gid_data = NULL;
+
+ /* Issue GID_PT */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, GID_PT_REQ_SIZE, GID_PT_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD,
+ GID_PT_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_type */
+ ct_req->req.gid_pt.port_type = NS_NX_PORT_TYPE;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n",
+ ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, "
+ "gid_pt_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Set port IDs in switch info list. */
+ for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ gid_data = &ct_rsp->rsp.gid_pt.entries[i];
+ list[i].d_id.b.domain = gid_data->port_id[0];
+ list[i].d_id.b.area = gid_data->port_id[1];
+ list[i].d_id.b.al_pa = gid_data->port_id[2];
+
+ /* Last one exit. */
+ if (gid_data->control_byte & BIT_7) {
+ list[i].d_id.b.rsvd_1 = gid_data->control_byte;
+ break;
+ }
+ }
+
+ /*
+ * If we've used all available slots, then the switch is
+ * reporting back more devices than we can handle with this
+ * single call. Return a failed status, and let GA_NXT handle
+ * the overload.
+ */
+ if (i == MAX_FIBRE_DEVICES)
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_gpn_id() - SNS Get Port Name (GPN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+{
+ int rval;
+ uint16_t i;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ return (qla2x00_sns_gpn_id(ha, list));
+ }
+
+ for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ /* Issue GPN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, GPN_ID_REQ_SIZE,
+ GPN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GPN_ID_CMD,
+ GPN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
+ ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed "
+ "(%d).\n", ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected "
+ "request, gpn_id_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save portname */
+ memcpy(list[i].port_name,
+ ct_rsp->rsp.gpn_id.port_name, WWN_SIZE);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_gnn_id() - SNS Get Node Name (GNN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
+{
+ int rval;
+ uint16_t i;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ return (qla2x00_sns_gnn_id(ha, list));
+ }
+
+ for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ /* Issue GNN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, GNN_ID_REQ_SIZE,
+ GNN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GNN_ID_CMD,
+ GNN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
+ ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed "
+ "(%d).\n", ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected "
+ "request, gnn_id_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save nodename */
+ memcpy(list[i].node_name,
+ ct_rsp->rsp.gnn_id.node_name, WWN_SIZE);
+
+ DEBUG2_3(printk("scsi(%ld): GID_PT entry - "
+ "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "portid=%02x%02x%02x.\n",
+ ha->host_no,
+ list[i].node_name[0], list[i].node_name[1],
+ list[i].node_name[2], list[i].node_name[3],
+ list[i].node_name[4], list[i].node_name[5],
+ list[i].node_name[6], list[i].node_name[7],
+ list[i].port_name[0], list[i].port_name[1],
+ list[i].port_name[2], list[i].port_name[3],
+ list[i].port_name[4], list[i].port_name[5],
+ list[i].port_name[6], list[i].port_name[7],
+ list[i].d_id.b.domain, list[i].d_id.b.area,
+ list[i].d_id.b.al_pa));
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rft_id(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ return (qla2x00_sns_rft_id(ha));
+ }
+
+ /* Issue RFT_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, RFT_ID_REQ_SIZE, RFT_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD,
+ RFT_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id, FC-4 types */
+ ct_req->req.rft_id.port_id[0] = ha->d_id.b.domain;
+ ct_req->req.rft_id.port_id[1] = ha->d_id.b.area;
+ ct_req->req.rft_id.port_id[2] = ha->d_id.b.al_pa;
+
+ ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n",
+ ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected "
+ "request, rft_id_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rff_id() - SNS Register FC-4 Features (RFF_ID) supported by the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rff_id(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ DEBUG2(printk("scsi(%ld): RFF_ID call unsupported on "
+ "ISP2100/ISP2200.\n", ha->host_no));
+ return (QLA_SUCCESS);
+ }
+
+ /* Issue RFF_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, RFF_ID_REQ_SIZE, RFF_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD,
+ RFF_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
+ ct_req->req.rff_id.port_id[0] = ha->d_id.b.domain;
+ ct_req->req.rff_id.port_id[1] = ha->d_id.b.area;
+ ct_req->req.rff_id.port_id[2] = ha->d_id.b.al_pa;
+
+ ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n",
+ ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): RFF_ID failed, rejected "
+ "request, rff_id_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rnn_id(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ return (qla2x00_sns_rnn_id(ha));
+ }
+
+ /* Issue RNN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, RNN_ID_REQ_SIZE, RNN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RNN_ID_CMD,
+ RNN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id, node_name */
+ ct_req->req.rnn_id.port_id[0] = ha->d_id.b.domain;
+ ct_req->req.rnn_id.port_id[1] = ha->d_id.b.area;
+ ct_req->req.rnn_id.port_id[2] = ha->d_id.b.al_pa;
+
+ memcpy(ct_req->req.rnn_id.node_name, ha->init_cb->node_name, WWN_SIZE);
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n",
+ ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected "
+ "request, rnn_id_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rsnn_nn() - SNS Register Symbolic Node Name (RSNN_NN) of the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rsnn_nn(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint8_t *snn;
+ uint8_t version[20];
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ DEBUG2(printk("scsi(%ld): RSNN_ID call unsupported on "
+ "ISP2100/ISP2200.\n", ha->host_no));
+ return (QLA_SUCCESS);
+ }
+
+ /* Issue RSNN_NN */
+ /* Prepare common MS IOCB */
+ /* Request size adjusted after CT preparation */
+ ms_pkt = qla2x00_prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD,
+ RSNN_NN_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- node_name, symbolic node_name, size */
+ memcpy(ct_req->req.rsnn_nn.node_name, ha->init_cb->node_name, WWN_SIZE);
+
+ /* Prepare the Symbolic Node Name */
+ /* Board type */
+ snn = ct_req->req.rsnn_nn.sym_node_name;
+ strcpy(snn, ha->model_number);
+ /* Firmware version */
+ strcat(snn, " FW:v");
+ sprintf(version, "%d.%02d.%02d", ha->fw_major_version,
+ ha->fw_minor_version, ha->fw_subminor_version);
+ strcat(snn, version);
+ /* Driver version */
+ strcat(snn, " DVR:v");
+ strcat(snn, qla2x00_version_str);
+
+ /* Calculate SNN length */
+ ct_req->req.rsnn_nn.name_len = (uint8_t)strlen(snn);
+
+ /* Update MS IOCB request */
+ ms_pkt->req_bytecount =
+ cpu_to_le32(24 + 1 + ct_req->req.rsnn_nn.name_len);
+ ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n",
+ ha->host_no, rval));
+ } else if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ DEBUG2_3(printk("scsi(%ld): RSNN_NN failed, rejected "
+ "request, rsnn_id_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr)));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+
+/**
+ * qla2x00_prep_sns_cmd() - Prepare common SNS command request fields for query.
+ * @ha: HA context
+ * @cmd: GS command
+ * @scmd_len: Subcommand length
+ * @data_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's sns_cmd.
+ */
+static inline struct sns_cmd_pkt *
+qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len,
+ uint16_t data_size)
+{
+ uint16_t wc;
+ struct sns_cmd_pkt *sns_cmd;
+
+ sns_cmd = ha->sns_cmd;
+ memset(sns_cmd, 0, sizeof(struct sns_cmd_pkt));
+ wc = data_size / 2; /* Size in 16bit words. */
+ sns_cmd->p.cmd.buffer_length = cpu_to_le16(wc);
+ sns_cmd->p.cmd.buffer_address[0] = cpu_to_le32(LSD(ha->sns_cmd_dma));
+ sns_cmd->p.cmd.buffer_address[1] = cpu_to_le32(MSD(ha->sns_cmd_dma));
+ sns_cmd->p.cmd.subcommand_length = cpu_to_le16(scmd_len);
+ sns_cmd->p.cmd.subcommand = cpu_to_le16(cmd);
+ wc = (data_size - 16) / 4; /* Size in 32bit words. */
+ sns_cmd->p.cmd.size = cpu_to_le16(wc);
+
+ return (sns_cmd);
+}
+
+/**
+ * qla2x00_sns_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
+ * @ha: HA context
+ * @fcport: fcport entry to updated
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+ int rval;
+
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue GA_NXT. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(ha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN,
+ GA_NXT_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id. */
+ sns_cmd->p.cmd.param[0] = fcport->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = fcport->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = fcport->d_id.b.domain;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GA_NXT Send SNS failed (%d).\n",
+ ha->host_no, rval));
+ } else if (sns_cmd->p.gan_data[8] != 0x80 ||
+ sns_cmd->p.gan_data[9] != 0x02) {
+ DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, "
+ "ga_nxt_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gan_data, 16));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Populate fc_port_t entry. */
+ fcport->d_id.b.domain = sns_cmd->p.gan_data[17];
+ fcport->d_id.b.area = sns_cmd->p.gan_data[18];
+ fcport->d_id.b.al_pa = sns_cmd->p.gan_data[19];
+
+ memcpy(fcport->node_name, &sns_cmd->p.gan_data[284], WWN_SIZE);
+ memcpy(fcport->port_name, &sns_cmd->p.gan_data[20], WWN_SIZE);
+
+ if (sns_cmd->p.gan_data[16] != NS_N_PORT_TYPE &&
+ sns_cmd->p.gan_data[16] != NS_NL_PORT_TYPE)
+ fcport->d_id.b.domain = 0xf0;
+
+ DEBUG2_3(printk("scsi(%ld): GA_NXT entry - "
+ "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "portid=%02x%02x%02x.\n",
+ ha->host_no,
+ fcport->node_name[0], fcport->node_name[1],
+ fcport->node_name[2], fcport->node_name[3],
+ fcport->node_name[4], fcport->node_name[5],
+ fcport->node_name[6], fcport->node_name[7],
+ fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_gid_pt() - SNS scan for fabric devices via GID_PT command.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * NOTE: Non-Nx_Ports are not requested.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
+{
+ int rval;
+
+ uint16_t i;
+ uint8_t *entry;
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue GID_PT. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(ha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
+ GID_PT_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_type. */
+ sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GID_PT Send SNS failed (%d).\n",
+ ha->host_no, rval));
+ } else if (sns_cmd->p.gid_data[8] != 0x80 ||
+ sns_cmd->p.gid_data[9] != 0x02) {
+ DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, "
+ "gid_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gid_data, 16));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Set port IDs in switch info list. */
+ for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ entry = &sns_cmd->p.gid_data[(i * 4) + 16];
+ list[i].d_id.b.domain = entry[1];
+ list[i].d_id.b.area = entry[2];
+ list[i].d_id.b.al_pa = entry[3];
+
+ /* Last one exit. */
+ if (entry[0] & BIT_7) {
+ list[i].d_id.b.rsvd_1 = entry[0];
+ break;
+ }
+ }
+
+ /*
+ * If we've used all available slots, then the switch is
+ * reporting back more devices that we can handle with this
+ * single call. Return a failed status, and let GA_NXT handle
+ * the overload.
+ */
+ if (i == MAX_FIBRE_DEVICES)
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_gpn_id() - SNS Get Port Name (GPN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+{
+ int rval;
+
+ uint16_t i;
+ struct sns_cmd_pkt *sns_cmd;
+
+ for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ /* Issue GPN_ID */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(ha, GPN_ID_CMD,
+ GPN_ID_SNS_SCMD_LEN, GPN_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id. */
+ sns_cmd->p.cmd.param[0] = list[i].d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = list[i].d_id.b.area;
+ sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(ha, ha->sns_cmd_dma,
+ GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GPN_ID Send SNS failed "
+ "(%d).\n", ha->host_no, rval));
+ } else if (sns_cmd->p.gpn_data[8] != 0x80 ||
+ sns_cmd->p.gpn_data[9] != 0x02) {
+ DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected "
+ "request, gpn_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gpn_data, 16));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save portname */
+ memcpy(list[i].port_name, &sns_cmd->p.gpn_data[16],
+ WWN_SIZE);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_gnn_id() - SNS Get Node Name (GNN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
+{
+ int rval;
+
+ uint16_t i;
+ struct sns_cmd_pkt *sns_cmd;
+
+ for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ /* Issue GNN_ID */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(ha, GNN_ID_CMD,
+ GNN_ID_SNS_SCMD_LEN, GNN_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id. */
+ sns_cmd->p.cmd.param[0] = list[i].d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = list[i].d_id.b.area;
+ sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(ha, ha->sns_cmd_dma,
+ GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): GNN_ID Send SNS failed "
+ "(%d).\n", ha->host_no, rval));
+ } else if (sns_cmd->p.gnn_data[8] != 0x80 ||
+ sns_cmd->p.gnn_data[9] != 0x02) {
+ DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected "
+ "request, gnn_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gnn_data, 16));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save nodename */
+ memcpy(list[i].node_name, &sns_cmd->p.gnn_data[16],
+ WWN_SIZE);
+
+ DEBUG2_3(printk("scsi(%ld): GID_PT entry - "
+ "nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "portid=%02x%02x%02x.\n",
+ ha->host_no,
+ list[i].node_name[0], list[i].node_name[1],
+ list[i].node_name[2], list[i].node_name[3],
+ list[i].node_name[4], list[i].node_name[5],
+ list[i].node_name[6], list[i].node_name[7],
+ list[i].port_name[0], list[i].port_name[1],
+ list[i].port_name[2], list[i].port_name[3],
+ list[i].port_name[4], list[i].port_name[5],
+ list[i].port_name[6], list[i].port_name[7],
+ list[i].d_id.b.domain, list[i].d_id.b.area,
+ list[i].d_id.b.al_pa));
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_snd_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
+ * @ha: HA context
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_rft_id(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue RFT_ID. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(ha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN,
+ RFT_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id, FC-4 types */
+ sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = ha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = ha->d_id.b.domain;
+
+ sns_cmd->p.cmd.param[5] = 0x01; /* FCP-3 */
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): RFT_ID Send SNS failed (%d).\n",
+ ha->host_no, rval));
+ } else if (sns_cmd->p.rft_data[8] != 0x80 ||
+ sns_cmd->p.rft_data[9] != 0x02) {
+ DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected request, "
+ "rft_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rft_data, 16));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
+ * HBA.
+ * @ha: HA context
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_rnn_id(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue RNN_ID. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(ha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN,
+ RNN_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id, nodename. */
+ sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = ha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = ha->d_id.b.domain;
+
+ sns_cmd->p.cmd.param[4] = ha->init_cb->node_name[7];
+ sns_cmd->p.cmd.param[5] = ha->init_cb->node_name[6];
+ sns_cmd->p.cmd.param[6] = ha->init_cb->node_name[5];
+ sns_cmd->p.cmd.param[7] = ha->init_cb->node_name[4];
+ sns_cmd->p.cmd.param[8] = ha->init_cb->node_name[3];
+ sns_cmd->p.cmd.param[9] = ha->init_cb->node_name[2];
+ sns_cmd->p.cmd.param[10] = ha->init_cb->node_name[1];
+ sns_cmd->p.cmd.param[11] = ha->init_cb->node_name[0];
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3(printk("scsi(%ld): RNN_ID Send SNS failed (%d).\n",
+ ha->host_no, rval));
+ } else if (sns_cmd->p.rnn_data[8] != 0x80 ||
+ sns_cmd->p.rnn_data[9] != 0x02) {
+ DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected request, "
+ "rnn_rsp:\n", ha->host_no));
+ DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rnn_data, 16));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
new file mode 100644
index 000000000000..1ab5d92c3868
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -0,0 +1,3908 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+#include <linux/delay.h>
+
+#include "qla_devtbl.h"
+
+/* XXX(hch): this is ugly, but we don't want to pull in exioctl.h */
+#ifndef EXT_IS_LUN_BIT_SET
+#define EXT_IS_LUN_BIT_SET(P,L) \
+ (((P)->mask[L/8] & (0x80 >> (L%8)))?1:0)
+#define EXT_SET_LUN_BIT(P,L) \
+ ((P)->mask[L/8] |= (0x80 >> (L%8)))
+#endif
+
+/*
+* QLogic ISP2x00 Hardware Support Function Prototypes.
+*/
+static int qla2x00_pci_config(scsi_qla_host_t *);
+static int qla2x00_isp_firmware(scsi_qla_host_t *);
+static void qla2x00_reset_chip(scsi_qla_host_t *);
+static int qla2x00_chip_diag(scsi_qla_host_t *);
+static void qla2x00_resize_request_q(scsi_qla_host_t *);
+static int qla2x00_setup_chip(scsi_qla_host_t *);
+static void qla2x00_init_response_q_entries(scsi_qla_host_t *);
+static int qla2x00_init_rings(scsi_qla_host_t *);
+static int qla2x00_fw_ready(scsi_qla_host_t *);
+static int qla2x00_configure_hba(scsi_qla_host_t *);
+static int qla2x00_nvram_config(scsi_qla_host_t *);
+static void qla2x00_init_tgt_map(scsi_qla_host_t *);
+static int qla2x00_configure_loop(scsi_qla_host_t *);
+static int qla2x00_configure_local_loop(scsi_qla_host_t *);
+static void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *);
+static void qla2x00_lun_discovery(scsi_qla_host_t *, fc_port_t *);
+static int qla2x00_rpt_lun_discovery(scsi_qla_host_t *, fc_port_t *,
+ inq_cmd_rsp_t *, dma_addr_t);
+static int qla2x00_report_lun(scsi_qla_host_t *, fc_port_t *);
+static fc_lun_t *qla2x00_cfg_lun(scsi_qla_host_t *, fc_port_t *, uint16_t,
+ inq_cmd_rsp_t *, dma_addr_t);
+static fc_lun_t * qla2x00_add_lun(fc_port_t *, uint16_t);
+static int qla2x00_inquiry(scsi_qla_host_t *, fc_port_t *, uint16_t,
+ inq_cmd_rsp_t *, dma_addr_t);
+static int qla2x00_configure_fabric(scsi_qla_host_t *);
+static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *);
+static int qla2x00_device_resync(scsi_qla_host_t *);
+static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
+ uint16_t *);
+static void qla2x00_config_os(scsi_qla_host_t *ha);
+static uint16_t qla2x00_fcport_bind(scsi_qla_host_t *ha, fc_port_t *fcport);
+static os_lun_t * qla2x00_fclun_bind(scsi_qla_host_t *, fc_port_t *,
+ fc_lun_t *);
+static void qla2x00_lun_free(scsi_qla_host_t *, uint16_t, uint16_t);
+
+static int qla2x00_restart_isp(scsi_qla_host_t *);
+static void qla2x00_reset_adapter(scsi_qla_host_t *);
+static os_tgt_t *qla2x00_tgt_alloc(scsi_qla_host_t *, uint16_t);
+static os_lun_t *qla2x00_lun_alloc(scsi_qla_host_t *, uint16_t, uint16_t);
+
+/****************************************************************************/
+/* QLogic ISP2x00 Hardware Support Functions. */
+/****************************************************************************/
+
+/*
+* qla2x00_initialize_adapter
+* Initialize board.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+int
+qla2x00_initialize_adapter(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint8_t restart_risc = 0;
+ uint8_t retry;
+ uint32_t wait_time;
+
+ /* Clear adapter flags. */
+ ha->flags.online = 0;
+ ha->flags.reset_active = 0;
+ atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ ha->device_flags = 0;
+ ha->sns_retry_cnt = 0;
+ ha->dpc_flags = 0;
+ ha->failback_delay = 0;
+ ha->flags.management_server_logged_in = 0;
+ ha->marker_needed = 0;
+ ha->mbx_flags = 0;
+ ha->isp_abort_cnt = 0;
+ ha->beacon_blink_led = 0;
+
+ rval = qla2x00_pci_config(ha);
+ if (rval) {
+ DEBUG2(printk("scsi(%ld): Unable to configure PCI space=n",
+ ha->host_no));
+ return (rval);
+ }
+
+ qla2x00_reset_chip(ha);
+
+ /* Initialize target map database. */
+ qla2x00_init_tgt_map(ha);
+
+ qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n");
+ qla2x00_nvram_config(ha);
+
+ qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n");
+
+ retry = 10;
+ /*
+ * Try to configure the loop.
+ */
+ do {
+ restart_risc = 0;
+
+ /* If firmware needs to be loaded */
+ if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) {
+ if ((rval = qla2x00_chip_diag(ha)) == QLA_SUCCESS) {
+ rval = qla2x00_setup_chip(ha);
+ }
+ }
+
+ if (rval == QLA_SUCCESS &&
+ (rval = qla2x00_init_rings(ha)) == QLA_SUCCESS) {
+check_fw_ready_again:
+ /*
+ * Wait for a successful LIP up to a maximum
+ * of (in seconds): RISC login timeout value,
+ * RISC retry count value, and port down retry
+ * value OR a minimum of 4 seconds OR If no
+ * cable, only 5 seconds.
+ */
+ rval = qla2x00_fw_ready(ha);
+ if (rval == QLA_SUCCESS) {
+ clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+
+ /*
+ * Wait at most MAX_TARGET RSCNs for a stable
+ * link.
+ */
+ wait_time = 256;
+ do {
+ clear_bit(LOOP_RESYNC_NEEDED,
+ &ha->dpc_flags);
+ rval = qla2x00_configure_loop(ha);
+
+ if (test_and_clear_bit(ISP_ABORT_NEEDED,
+ &ha->dpc_flags)) {
+ restart_risc = 1;
+ break;
+ }
+
+ /*
+ * If loop state change while we were
+ * discoverying devices then wait for
+ * LIP to complete
+ */
+
+ if (atomic_read(&ha->loop_state) ==
+ LOOP_DOWN && retry--) {
+ goto check_fw_ready_again;
+ }
+ wait_time--;
+ } while (!atomic_read(&ha->loop_down_timer) &&
+ retry &&
+ wait_time &&
+ (test_bit(LOOP_RESYNC_NEEDED,
+ &ha->dpc_flags)));
+
+ if (wait_time == 0)
+ rval = QLA_FUNCTION_FAILED;
+ if (ha->mem_err)
+ restart_risc = 1;
+ } else if (ha->device_flags & DFLG_NO_CABLE)
+ /* If no cable, then all is good. */
+ rval = QLA_SUCCESS;
+ }
+ } while (restart_risc && retry--);
+
+ if (rval == QLA_SUCCESS) {
+ clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ ha->marker_needed = 1;
+ qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
+ ha->marker_needed = 0;
+
+ ha->flags.online = 1;
+ } else {
+ DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_pci_config() - Setup device PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_pci_config(scsi_qla_host_t *ha)
+{
+ uint16_t w, mwi;
+ unsigned long flags = 0;
+ uint32_t cnt;
+
+ qla_printk(KERN_INFO, ha, "Configuring PCI space...\n");
+
+ /*
+ * Turn on PCI master; for system BIOSes that don't turn it on by
+ * default.
+ */
+ pci_set_master(ha->pdev);
+ mwi = 0;
+ if (pci_set_mwi(ha->pdev))
+ mwi = PCI_COMMAND_INVALIDATE;
+ pci_read_config_word(ha->pdev, PCI_REVISION_ID, &ha->revision);
+
+ if (!ha->iobase)
+ return (QLA_FUNCTION_FAILED);
+
+ /*
+ * We want to respect framework's setting of PCI configuration space
+ * command register and also want to make sure that all bits of
+ * interest to us are properly set in command register.
+ */
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &w);
+ w |= mwi | (PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+
+ /* Get PCI bus information. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->pci_attr = RD_REG_WORD(&ha->iobase->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha)) {
+ pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80);
+
+ /* PCI Specification Revision 2.3 changes */
+ if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ /* Command Register - Reset Interrupt Disable. */
+ w &= ~PCI_COMMAND_INTX_DISABLE;
+
+ /*
+ * If this is a 2300 card and not 2312, reset the
+ * COMMAND_INVALIDATE due to a bug in the 2300. Unfortunately,
+ * the 2310 also reports itself as a 2300 so we need to get the
+ * fb revision level -- a 6 indicates it really is a 2300 and
+ * not a 2310.
+ */
+ if (IS_QLA2300(ha)) {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Pause RISC. */
+ WRT_REG_WORD(&ha->iobase->hccr, HCCR_PAUSE_RISC);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&ha->iobase->hccr) &
+ HCCR_RISC_PAUSE) != 0)
+ break;
+
+ udelay(10);
+ }
+
+ /* Select FPM registers. */
+ WRT_REG_WORD(&ha->iobase->ctrl_status, 0x20);
+ RD_REG_WORD(&ha->iobase->ctrl_status);
+
+ /* Get the fb rev level */
+ ha->fb_rev = RD_FB_CMD_REG(ha, ha->iobase);
+
+ if (ha->fb_rev == FPM_2300)
+ w &= ~PCI_COMMAND_INVALIDATE;
+
+ /* Deselect FPM registers. */
+ WRT_REG_WORD(&ha->iobase->ctrl_status, 0x0);
+ RD_REG_WORD(&ha->iobase->ctrl_status);
+
+ /* Release RISC module. */
+ WRT_REG_WORD(&ha->iobase->hccr, HCCR_RELEASE_RISC);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&ha->iobase->hccr) &
+ HCCR_RISC_PAUSE) == 0)
+ break;
+
+ udelay(10);
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+ }
+
+ pci_write_config_word(ha->pdev, PCI_COMMAND, w);
+
+ /* Reset expansion ROM address decode enable */
+ pci_read_config_word(ha->pdev, PCI_ROM_ADDRESS, &w);
+ w &= ~PCI_ROM_ADDRESS_ENABLE;
+ pci_write_config_word(ha->pdev, PCI_ROM_ADDRESS, w);
+
+ return (QLA_SUCCESS);
+}
+
+/**
+ * qla2x00_isp_firmware() - Choose firmware image.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_isp_firmware(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ /* Assume loading risc code */
+ rval = QLA_FUNCTION_FAILED;
+
+ if (ha->flags.disable_risc_code_load) {
+ DEBUG2(printk("scsi(%ld): RISC CODE NOT loaded\n",
+ ha->host_no));
+ qla_printk(KERN_INFO, ha, "RISC CODE NOT loaded\n");
+
+ /* Verify checksum of loaded RISC code. */
+ rval = qla2x00_verify_checksum(ha);
+ }
+
+ if (rval) {
+ DEBUG2_3(printk("scsi(%ld): **** Load RISC code ****\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_reset_chip() - Reset ISP chip.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static void
+qla2x00_reset_chip(scsi_qla_host_t *ha)
+{
+ unsigned long flags = 0;
+ device_reg_t __iomem *reg = ha->iobase;
+ uint32_t cnt;
+ unsigned long mbx_flags = 0;
+ uint16_t cmd;
+
+ /* Disable ISP interrupts. */
+ qla2x00_disable_intrs(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Turn off master enable */
+ cmd = 0;
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &cmd);
+ cmd &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, cmd);
+
+ if (!IS_QLA2100(ha)) {
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ if (IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->hccr) &
+ HCCR_RISC_PAUSE) != 0)
+ break;
+ udelay(100);
+ }
+ } else {
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ udelay(10);
+ }
+
+ /* Select FPM registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* FPM Soft Reset. */
+ WRT_REG_WORD(&reg->fpm_diag_config, 0x100);
+ RD_REG_WORD(&reg->fpm_diag_config); /* PCI Posting. */
+
+ /* Toggle Fpm Reset. */
+ if (!IS_QLA2200(ha)) {
+ WRT_REG_WORD(&reg->fpm_diag_config, 0x0);
+ RD_REG_WORD(&reg->fpm_diag_config); /* PCI Posting. */
+ }
+
+ /* Select frame buffer registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0x10);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* Reset frame buffer FIFOs. */
+ if (IS_QLA2200(ha)) {
+ WRT_FB_CMD_REG(ha, reg, 0xa000);
+ RD_FB_CMD_REG(ha, reg); /* PCI Posting. */
+ } else {
+ WRT_FB_CMD_REG(ha, reg, 0x00fc);
+
+ /* Read back fb_cmd until zero or 3 seconds max */
+ for (cnt = 0; cnt < 3000; cnt++) {
+ if ((RD_FB_CMD_REG(ha, reg) & 0xff) == 0)
+ break;
+ udelay(100);
+ }
+ }
+
+ /* Select RISC module registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* Reset RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+
+ /* Release RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ }
+
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_HOST_INT);
+
+ /* Reset ISP chip. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+
+ /* Wait for RISC to recover from reset. */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ /*
+ * It is necessary to for a delay here since the card doesn't
+ * respond to PCI reads during a reset. On some architectures
+ * this will result in an MCA.
+ */
+ udelay(20);
+ for (cnt = 30000; cnt; cnt--) {
+ if ((RD_REG_WORD(&reg->ctrl_status) &
+ CSR_ISP_SOFT_RESET) == 0)
+ break;
+ udelay(100);
+ }
+ } else
+ udelay(10);
+
+ /* Reset RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+
+ WRT_REG_WORD(&reg->semaphore, 0);
+
+ /* Release RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)))
+ spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags);
+
+ if (RD_MAILBOX_REG(ha, reg, 0) != MBS_BUSY) {
+ if (!(test_bit(ABORT_ISP_ACTIVE,
+ &ha->dpc_flags)))
+ spin_unlock_irqrestore(
+ &ha->mbx_reg_lock, mbx_flags);
+ break;
+ }
+
+ if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)))
+ spin_unlock_irqrestore(&ha->mbx_reg_lock,
+ mbx_flags);
+
+ udelay(100);
+ }
+ } else
+ udelay(100);
+
+ /* Turn on master enable */
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, cmd);
+
+ /* Disable RISC pause on FPM parity error. */
+ if (!IS_QLA2100(ha)) {
+ WRT_REG_WORD(&reg->hccr, HCCR_DISABLE_PARITY_PAUSE);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/**
+ * qla2x00_chip_diag() - Test chip for proper operation.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_chip_diag(scsi_qla_host_t *ha)
+{
+ int rval;
+ device_reg_t __iomem *reg = ha->iobase;
+ unsigned long flags = 0;
+ uint16_t data;
+ uint32_t cnt;
+ uint16_t mb[5];
+
+ /* Assume a failed state */
+ rval = QLA_FUNCTION_FAILED;
+
+ DEBUG3(printk("scsi(%ld): Testing device at %lx.\n",
+ ha->host_no, (u_long)&reg->flash_address));
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Reset ISP chip. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+
+ /*
+ * We need to have a delay here since the card will not respond while
+ * in reset causing an MCA on some architectures.
+ */
+ udelay(20);
+ data = qla2x00_debounce_register(&reg->ctrl_status);
+ for (cnt = 6000000 ; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) {
+ udelay(5);
+ data = RD_REG_WORD(&reg->ctrl_status);
+ barrier();
+ }
+
+ if (!cnt)
+ goto chip_diag_failed;
+
+ DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n",
+ ha->host_no));
+
+ /* Reset RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+
+ /* Workaround for QLA2312 PCI parity error */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ data = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 0));
+ for (cnt = 6000000; cnt && (data == MBS_BUSY); cnt--) {
+ udelay(5);
+ data = RD_MAILBOX_REG(ha, reg, 0);
+ barrier();
+ }
+ } else
+ udelay(10);
+
+ if (!cnt)
+ goto chip_diag_failed;
+
+ /* Check product ID of chip */
+ DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", ha->host_no));
+
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+ mb[3] = RD_MAILBOX_REG(ha, reg, 3);
+ mb[4] = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 4));
+ if (mb[1] != PROD_ID_1 || (mb[2] != PROD_ID_2 && mb[2] != PROD_ID_2a) ||
+ mb[3] != PROD_ID_3) {
+ qla_printk(KERN_WARNING, ha,
+ "Wrong product ID = 0x%x,0x%x,0x%x\n", mb[1], mb[2], mb[3]);
+
+ goto chip_diag_failed;
+ }
+ ha->product_id[0] = mb[1];
+ ha->product_id[1] = mb[2];
+ ha->product_id[2] = mb[3];
+ ha->product_id[3] = mb[4];
+
+ /* Adjust fw RISC transfer size */
+ if (ha->request_q_length > 1024)
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024;
+ else
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE *
+ ha->request_q_length;
+
+ if (IS_QLA2200(ha) &&
+ RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) {
+ /* Limit firmware transfer size with a 2200A */
+ DEBUG3(printk("scsi(%ld): Found QLA2200A chip.\n",
+ ha->host_no));
+
+ ha->fw_transfer_size = 128;
+ }
+
+ /* Wrap Incoming Mailboxes Test. */
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", ha->host_no));
+ rval = qla2x00_mbx_reg_test(ha);
+ if (rval) {
+ DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha,
+ "Failed mailbox send register test\n");
+ }
+ else {
+ /* Flag a successful rval */
+ rval = QLA_SUCCESS;
+ }
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+chip_diag_failed:
+ if (rval)
+ DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED "
+ "****\n", ha->host_no));
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (rval);
+}
+
+/**
+ * qla2x00_resize_request_q() - Resize request queue given available ISP memory.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static void
+qla2x00_resize_request_q(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint16_t fw_iocb_cnt = 0;
+ uint16_t request_q_length = REQUEST_ENTRY_CNT_2XXX_EXT_MEM;
+ dma_addr_t request_dma;
+ request_t *request_ring;
+
+ /* Valid only on recent ISPs. */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return;
+
+ /* Retrieve IOCB counts available to the firmware. */
+ rval = qla2x00_get_resource_cnts(ha, NULL, NULL, NULL, &fw_iocb_cnt);
+ if (rval)
+ return;
+ /* No point in continuing if current settings are sufficient. */
+ if (fw_iocb_cnt < 1024)
+ return;
+ if (ha->request_q_length >= request_q_length)
+ return;
+
+ /* Attempt to claim larger area for request queue. */
+ request_ring = dma_alloc_coherent(&ha->pdev->dev,
+ (request_q_length + 1) * sizeof(request_t), &request_dma,
+ GFP_KERNEL);
+ if (request_ring == NULL)
+ return;
+
+ /* Resize successful, report extensions. */
+ qla_printk(KERN_INFO, ha, "Extended memory detected (%d KB)...\n",
+ (ha->fw_memory_size + 1) / 1024);
+ qla_printk(KERN_INFO, ha, "Resizing request queue depth "
+ "(%d -> %d)...\n", ha->request_q_length, request_q_length);
+
+ /* Clear old allocations. */
+ dma_free_coherent(&ha->pdev->dev,
+ (ha->request_q_length + 1) * sizeof(request_t), ha->request_ring,
+ ha->request_dma);
+
+ /* Begin using larger queue. */
+ ha->request_q_length = request_q_length;
+ ha->request_ring = request_ring;
+ ha->request_dma = request_dma;
+}
+
+/**
+ * qla2x00_setup_chip() - Load and start RISC firmware.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_setup_chip(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint16_t cnt;
+ uint16_t *risc_code;
+ unsigned long risc_address;
+ unsigned long risc_code_size;
+ int num;
+ int i;
+ uint16_t *req_ring;
+ struct qla_fw_info *fw_iter;
+
+ rval = QLA_SUCCESS;
+
+ /* Load firmware sequences */
+ fw_iter = ha->brd_info->fw_info;
+ while (fw_iter->addressing != FW_INFO_ADDR_NOMORE) {
+ risc_code = fw_iter->fwcode;
+ risc_code_size = *fw_iter->fwlen;
+
+ if (fw_iter->addressing == FW_INFO_ADDR_NORMAL) {
+ risc_address = *fw_iter->fwstart;
+ } else {
+ /* Extended address */
+ risc_address = *fw_iter->lfwstart;
+ }
+
+ num = 0;
+ rval = 0;
+ while (risc_code_size > 0 && !rval) {
+ cnt = (uint16_t)(ha->fw_transfer_size >> 1);
+ if (cnt > risc_code_size)
+ cnt = risc_code_size;
+
+ DEBUG7(printk("scsi(%ld): Loading risc segment@ "
+ "addr %p, number of bytes 0x%x, offset 0x%lx.\n",
+ ha->host_no, risc_code, cnt, risc_address));
+
+ req_ring = (uint16_t *)ha->request_ring;
+ for (i = 0; i < cnt; i++)
+ req_ring[i] = cpu_to_le16(risc_code[i]);
+
+ if (fw_iter->addressing == FW_INFO_ADDR_NORMAL) {
+ rval = qla2x00_load_ram(ha,
+ ha->request_dma, risc_address, cnt);
+ } else {
+ rval = qla2x00_load_ram_ext(ha,
+ ha->request_dma, risc_address, cnt);
+ }
+ if (rval) {
+ DEBUG(printk("scsi(%ld): [ERROR] Failed to "
+ "load segment %d of firmware\n",
+ ha->host_no, num));
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to load "
+ "segment %d of firmware\n", num);
+
+ qla2x00_dump_regs(ha);
+ break;
+ }
+
+ risc_code += cnt;
+ risc_address += cnt;
+ risc_code_size -= cnt;
+ num++;
+ }
+
+ /* Next firmware sequence */
+ fw_iter++;
+ }
+
+ /* Verify checksum of loaded RISC code. */
+ if (!rval) {
+ DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC "
+ "code.\n", ha->host_no));
+
+ rval = qla2x00_verify_checksum(ha);
+ if (rval == QLA_SUCCESS) {
+ /* Start firmware execution. */
+ DEBUG(printk("scsi(%ld): Checksum OK, start "
+ "firmware.\n", ha->host_no));
+
+ rval = qla2x00_execute_fw(ha);
+ /* Retrieve firmware information. */
+ if (rval == QLA_SUCCESS && ha->fw_major_version == 0) {
+ qla2x00_get_fw_version(ha,
+ &ha->fw_major_version,
+ &ha->fw_minor_version,
+ &ha->fw_subminor_version,
+ &ha->fw_attributes, &ha->fw_memory_size);
+ qla2x00_resize_request_q(ha);
+ }
+ } else {
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld): ISP Firmware failed checksum.\n",
+ ha->host_no));
+ }
+ }
+
+ if (rval) {
+ DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_init_response_q_entries() - Initializes response queue entries.
+ * @ha: HA context
+ *
+ * Beginning of request ring has initialization control block already built
+ * by nvram config routine.
+ *
+ * Returns 0 on success.
+ */
+static void
+qla2x00_init_response_q_entries(scsi_qla_host_t *ha)
+{
+ uint16_t cnt;
+ response_t *pkt;
+
+ pkt = ha->response_ring_ptr;
+ for (cnt = 0; cnt < ha->response_q_length; cnt++) {
+ pkt->signature = RESPONSE_PROCESSED;
+ pkt++;
+ }
+
+}
+
+/**
+ * qla2x00_update_fw_options() - Read and process firmware options.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static void
+qla2x00_update_fw_options(scsi_qla_host_t *ha)
+{
+ uint16_t swing, emphasis, tx_sens, rx_sens;
+
+ memset(ha->fw_options, 0, sizeof(ha->fw_options));
+ qla2x00_get_fw_options(ha, ha->fw_options);
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return;
+
+ /* Serial Link options. */
+ DEBUG3(printk("scsi(%ld): Serial link options:\n",
+ ha->host_no));
+ DEBUG3(qla2x00_dump_buffer((uint8_t *)&ha->fw_seriallink_options,
+ sizeof(ha->fw_seriallink_options)));
+
+ ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
+ if (ha->fw_seriallink_options[3] & BIT_2) {
+ ha->fw_options[1] |= FO1_SET_EMPHASIS_SWING;
+
+ /* 1G settings */
+ swing = ha->fw_seriallink_options[2] & (BIT_2 | BIT_1 | BIT_0);
+ emphasis = (ha->fw_seriallink_options[2] &
+ (BIT_4 | BIT_3)) >> 3;
+ tx_sens = ha->fw_seriallink_options[0] &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ rx_sens = (ha->fw_seriallink_options[0] &
+ (BIT_7 | BIT_6 | BIT_5 | BIT_4)) >> 4;
+ ha->fw_options[10] = (emphasis << 14) | (swing << 8);
+ if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
+ if (rx_sens == 0x0)
+ rx_sens = 0x3;
+ ha->fw_options[10] |= (tx_sens << 4) | rx_sens;
+ } else if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ ha->fw_options[10] |= BIT_5 |
+ ((rx_sens & (BIT_1 | BIT_0)) << 2) |
+ (tx_sens & (BIT_1 | BIT_0));
+
+ /* 2G settings */
+ swing = (ha->fw_seriallink_options[2] &
+ (BIT_7 | BIT_6 | BIT_5)) >> 5;
+ emphasis = ha->fw_seriallink_options[3] & (BIT_1 | BIT_0);
+ tx_sens = ha->fw_seriallink_options[1] &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ rx_sens = (ha->fw_seriallink_options[1] &
+ (BIT_7 | BIT_6 | BIT_5 | BIT_4)) >> 4;
+ ha->fw_options[11] = (emphasis << 14) | (swing << 8);
+ if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
+ if (rx_sens == 0x0)
+ rx_sens = 0x3;
+ ha->fw_options[11] |= (tx_sens << 4) | rx_sens;
+ } else if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ ha->fw_options[11] |= BIT_5 |
+ ((rx_sens & (BIT_1 | BIT_0)) << 2) |
+ (tx_sens & (BIT_1 | BIT_0));
+ }
+
+ /* FCP2 options. */
+ /* Return command IOCBs without waiting for an ABTS to complete. */
+ ha->fw_options[3] |= BIT_13;
+
+ /* LED scheme. */
+ if (ha->flags.enable_led_scheme)
+ ha->fw_options[2] |= BIT_12;
+
+ /* Update firmware options. */
+ qla2x00_set_fw_options(ha, ha->fw_options);
+}
+
+/**
+ * qla2x00_init_rings() - Initializes firmware.
+ * @ha: HA context
+ *
+ * Beginning of request ring has initialization control block already built
+ * by nvram config routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_init_rings(scsi_qla_host_t *ha)
+{
+ int rval;
+ unsigned long flags = 0;
+ int cnt;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Clear outstanding commands array. */
+ for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+ ha->outstanding_cmds[cnt] = NULL;
+
+ ha->current_outstanding_cmd = 0;
+
+ /* Clear RSCN queue. */
+ ha->rscn_in_ptr = 0;
+ ha->rscn_out_ptr = 0;
+
+ /* Initialize firmware. */
+ ha->request_ring_ptr = ha->request_ring;
+ ha->req_ring_index = 0;
+ ha->req_q_cnt = ha->request_q_length;
+ ha->response_ring_ptr = ha->response_ring;
+ ha->rsp_ring_index = 0;
+
+ /* Setup ring parameters in initialization control block. */
+ ha->init_cb->request_q_outpointer = __constant_cpu_to_le16(0);
+ ha->init_cb->response_q_inpointer = __constant_cpu_to_le16(0);
+ ha->init_cb->request_q_length = cpu_to_le16(ha->request_q_length);
+ ha->init_cb->response_q_length = cpu_to_le16(ha->response_q_length);
+ ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma));
+ ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma));
+ ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma));
+ ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma));
+
+ /* Initialize response queue entries */
+ qla2x00_init_response_q_entries(ha);
+
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), 0);
+ WRT_REG_WORD(ISP_REQ_Q_OUT(ha, reg), 0);
+ WRT_REG_WORD(ISP_RSP_Q_IN(ha, reg), 0);
+ WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), 0);
+ RD_REG_WORD(ISP_RSP_Q_OUT(ha, reg)); /* PCI Posting. */
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Update any ISP specific firmware options before initialization. */
+ qla2x00_update_fw_options(ha);
+
+ DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no));
+ rval = qla2x00_init_firmware(ha, sizeof(init_cb_t));
+ if (rval) {
+ DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n",
+ ha->host_no));
+ } else {
+ DEBUG3(printk("scsi(%ld): Init firmware -- success.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_fw_ready() - Waits for firmware ready.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_fw_ready(scsi_qla_host_t *ha)
+{
+ int rval;
+ unsigned long wtime, mtime;
+ uint16_t min_wait; /* Minimum wait time if loop is down */
+ uint16_t wait_time; /* Wait time if loop is coming ready */
+ uint16_t fw_state;
+
+ rval = QLA_SUCCESS;
+
+ /* 20 seconds for loop down. */
+ min_wait = 20;
+
+ /*
+ * Firmware should take at most one RATOV to login, plus 5 seconds for
+ * our own processing.
+ */
+ if ((wait_time = (ha->retry_count*ha->login_timeout) + 5) < min_wait) {
+ wait_time = min_wait;
+ }
+
+ /* Min wait time if loop down */
+ mtime = jiffies + (min_wait * HZ);
+
+ /* wait time before firmware ready */
+ wtime = jiffies + (wait_time * HZ);
+
+ /* Wait for ISP to finish LIP */
+ if (!ha->flags.init_done)
+ qla_printk(KERN_INFO, ha, "Waiting for LIP to complete...\n");
+
+ DEBUG3(printk("scsi(%ld): Waiting for LIP to complete...\n",
+ ha->host_no));
+
+ do {
+ rval = qla2x00_get_firmware_state(ha, &fw_state);
+ if (rval == QLA_SUCCESS) {
+ if (fw_state < FSTATE_LOSS_OF_SYNC) {
+ ha->device_flags &= ~DFLG_NO_CABLE;
+ }
+ if (fw_state == FSTATE_READY) {
+ DEBUG(printk("scsi(%ld): F/W Ready - OK \n",
+ ha->host_no));
+
+ qla2x00_get_retry_cnt(ha, &ha->retry_count,
+ &ha->login_timeout, &ha->r_a_tov);
+
+ rval = QLA_SUCCESS;
+ break;
+ }
+
+ rval = QLA_FUNCTION_FAILED;
+
+ if (atomic_read(&ha->loop_down_timer) &&
+ (fw_state >= FSTATE_LOSS_OF_SYNC ||
+ fw_state == FSTATE_WAIT_AL_PA)) {
+ /* Loop down. Timeout on min_wait for states
+ * other than Wait for Login.
+ */
+ if (time_after_eq(jiffies, mtime)) {
+ qla_printk(KERN_INFO, ha,
+ "Cable is unplugged...\n");
+
+ ha->device_flags |= DFLG_NO_CABLE;
+ break;
+ }
+ }
+ } else {
+ /* Mailbox cmd failed. Timeout on min_wait. */
+ if (time_after_eq(jiffies, mtime))
+ break;
+ }
+
+ if (time_after_eq(jiffies, wtime))
+ break;
+
+ /* Delay for a while */
+ msleep(500);
+
+ DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
+ ha->host_no, fw_state, jiffies));
+ } while (1);
+
+ DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
+ ha->host_no, fw_state, jiffies));
+
+ if (rval) {
+ DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n",
+ ha->host_no));
+ }
+
+ return (rval);
+}
+
+/*
+* qla2x00_configure_hba
+* Setup adapter context.
+*
+* Input:
+* ha = adapter state pointer.
+*
+* Returns:
+* 0 = success
+*
+* Context:
+* Kernel context.
+*/
+static int
+qla2x00_configure_hba(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint16_t loop_id;
+ uint16_t topo;
+ uint8_t al_pa;
+ uint8_t area;
+ uint8_t domain;
+ char connect_type[22];
+
+ /* Get host addresses. */
+ rval = qla2x00_get_adapter_id(ha,
+ &loop_id, &al_pa, &area, &domain, &topo);
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "ERROR -- Unable to get host loop ID.\n");
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ return (rval);
+ }
+
+ if (topo == 4) {
+ qla_printk(KERN_INFO, ha,
+ "Cannot get topology - retrying.\n");
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ ha->loop_id = loop_id;
+
+ /* initialize */
+ ha->min_external_loopid = SNS_FIRST_LOOP_ID;
+ ha->operating_mode = LOOP;
+
+ switch (topo) {
+ case 0:
+ DEBUG3(printk("scsi(%ld): HBA in NL topology.\n",
+ ha->host_no));
+ ha->current_topology = ISP_CFG_NL;
+ strcpy(connect_type, "(Loop)");
+ break;
+
+ case 1:
+ DEBUG3(printk("scsi(%ld): HBA in FL topology.\n",
+ ha->host_no));
+ ha->current_topology = ISP_CFG_FL;
+ strcpy(connect_type, "(FL_Port)");
+ break;
+
+ case 2:
+ DEBUG3(printk("scsi(%ld): HBA in N P2P topology.\n",
+ ha->host_no));
+ ha->operating_mode = P2P;
+ ha->current_topology = ISP_CFG_N;
+ strcpy(connect_type, "(N_Port-to-N_Port)");
+ break;
+
+ case 3:
+ DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n",
+ ha->host_no));
+ ha->operating_mode = P2P;
+ ha->current_topology = ISP_CFG_F;
+ strcpy(connect_type, "(F_Port)");
+ break;
+
+ default:
+ DEBUG3(printk("scsi(%ld): HBA in unknown topology %x. "
+ "Using NL.\n",
+ ha->host_no, topo));
+ ha->current_topology = ISP_CFG_NL;
+ strcpy(connect_type, "(Loop)");
+ break;
+ }
+
+ /* Save Host port and loop ID. */
+ /* byte order - Big Endian */
+ ha->d_id.b.domain = domain;
+ ha->d_id.b.area = area;
+ ha->d_id.b.al_pa = al_pa;
+
+ if (!ha->flags.init_done)
+ qla_printk(KERN_INFO, ha,
+ "Topology - %s, Host Loop address 0x%x\n",
+ connect_type, ha->loop_id);
+
+ if (rval) {
+ DEBUG2_3(printk("scsi(%ld): FAILED.\n", ha->host_no));
+ } else {
+ DEBUG3(printk("scsi(%ld): exiting normally.\n", ha->host_no));
+ }
+
+ return(rval);
+}
+
+/*
+* NVRAM configuration for ISP 2xxx
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Output:
+* initialization control block in response_ring
+* host adapters parameters in host adapter block
+*
+* Returns:
+* 0 = success.
+*/
+static int
+qla2x00_nvram_config(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint8_t chksum = 0;
+ uint16_t cnt;
+ uint8_t *dptr1, *dptr2;
+ init_cb_t *icb = ha->init_cb;
+ nvram_t *nv = (nvram_t *)ha->request_ring;
+ uint16_t *wptr = (uint16_t *)ha->request_ring;
+ device_reg_t __iomem *reg = ha->iobase;
+ uint8_t timer_mode;
+
+ rval = QLA_SUCCESS;
+
+ /* Determine NVRAM starting address. */
+ ha->nvram_base = 0;
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha))
+ if ((RD_REG_WORD(&reg->ctrl_status) >> 14) == 1)
+ ha->nvram_base = 0x80;
+
+ /* Get NVRAM data and calculate checksum. */
+ qla2x00_lock_nvram_access(ha);
+ for (cnt = 0; cnt < sizeof(nvram_t)/2; cnt++) {
+ *wptr = cpu_to_le16(qla2x00_get_nvram_word(ha,
+ (cnt+ha->nvram_base)));
+ chksum += (uint8_t)*wptr;
+ chksum += (uint8_t)(*wptr >> 8);
+ wptr++;
+ }
+ qla2x00_unlock_nvram_access(ha);
+
+ DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+ DEBUG5(qla2x00_dump_buffer((uint8_t *)ha->request_ring,
+ sizeof(nvram_t)));
+
+ /* Bad NVRAM data, set defaults parameters. */
+ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' ||
+ nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) {
+ /* Reset NVRAM data. */
+ qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: "
+ "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0],
+ nv->nvram_version);
+ qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet "
+ "invalid -- WWPN) defaults.\n");
+
+ /*
+ * Set default initialization control block.
+ */
+ memset(nv, 0, sizeof(nvram_t));
+ nv->parameter_block_version = ICB_VERSION;
+
+ if (IS_QLA23XX(ha)) {
+ nv->firmware_options[0] = BIT_2 | BIT_1;
+ nv->firmware_options[1] = BIT_7 | BIT_5;
+ nv->add_firmware_options[0] = BIT_5;
+ nv->add_firmware_options[1] = BIT_5 | BIT_4;
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ nv->special_options[1] = BIT_7;
+ } else if (IS_QLA2200(ha)) {
+ nv->firmware_options[0] = BIT_2 | BIT_1;
+ nv->firmware_options[1] = BIT_7 | BIT_5;
+ nv->add_firmware_options[0] = BIT_5;
+ nv->add_firmware_options[1] = BIT_5 | BIT_4;
+ nv->frame_payload_size = __constant_cpu_to_le16(1024);
+ } else if (IS_QLA2100(ha)) {
+ nv->firmware_options[0] = BIT_3 | BIT_1;
+ nv->firmware_options[1] = BIT_5;
+ nv->frame_payload_size = __constant_cpu_to_le16(1024);
+ }
+
+ nv->max_iocb_allocation = __constant_cpu_to_le16(256);
+ nv->execution_throttle = __constant_cpu_to_le16(16);
+ nv->retry_count = 8;
+ nv->retry_delay = 1;
+
+ nv->port_name[0] = 33;
+ nv->port_name[3] = 224;
+ nv->port_name[4] = 139;
+
+ nv->login_timeout = 4;
+
+ /*
+ * Set default host adapter parameters
+ */
+ nv->host_p[1] = BIT_2;
+ nv->reset_delay = 5;
+ nv->port_down_retry_count = 8;
+ nv->max_luns_per_target = __constant_cpu_to_le16(8);
+ nv->link_down_timeout = 60;
+
+ rval = 1;
+ }
+
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+ /*
+ * The SN2 does not provide BIOS emulation which means you can't change
+ * potentially bogus BIOS settings. Force the use of default settings
+ * for link rate and frame size. Hope that the rest of the settings
+ * are valid.
+ */
+ if (ia64_platform_is("sn2")) {
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ if (IS_QLA23XX(ha))
+ nv->special_options[1] = BIT_7;
+ }
+#endif
+
+ /* Reset Initialization control block */
+ memset(icb, 0, sizeof(init_cb_t));
+
+ /*
+ * Setup driver NVRAM options.
+ */
+ nv->firmware_options[0] |= (BIT_6 | BIT_1);
+ nv->firmware_options[0] &= ~(BIT_5 | BIT_4);
+ nv->firmware_options[1] |= (BIT_5 | BIT_0);
+ nv->firmware_options[1] &= ~BIT_4;
+
+ if (IS_QLA23XX(ha)) {
+ nv->firmware_options[0] |= BIT_2;
+ nv->firmware_options[0] &= ~BIT_3;
+
+ if (IS_QLA2300(ha)) {
+ if (ha->fb_rev == FPM_2310) {
+ strcpy(ha->model_number, "QLA2310");
+ } else {
+ strcpy(ha->model_number, "QLA2300");
+ }
+ } else {
+ if (rval == 0 &&
+ memcmp(nv->model_number, BINZERO,
+ sizeof(nv->model_number)) != 0) {
+ char *st, *en;
+
+ strncpy(ha->model_number, nv->model_number,
+ sizeof(nv->model_number));
+ st = en = ha->model_number;
+ en += sizeof(nv->model_number) - 1;
+ while (en > st) {
+ if (*en != 0x20 && *en != 0x00)
+ break;
+ *en-- = '\0';
+ }
+ } else {
+ uint16_t index;
+
+ index = (ha->pdev->subsystem_device & 0xff);
+ if (index < QLA_MODEL_NAMES) {
+ strcpy(ha->model_number,
+ qla2x00_model_name[index]);
+ ha->model_desc =
+ qla2x00_model_desc[index];
+ } else {
+ strcpy(ha->model_number, "QLA23xx");
+ }
+ }
+ }
+ } else if (IS_QLA2200(ha)) {
+ nv->firmware_options[0] |= BIT_2;
+ /*
+ * 'Point-to-point preferred, else loop' is not a safe
+ * connection mode setting.
+ */
+ if ((nv->add_firmware_options[0] & (BIT_6 | BIT_5 | BIT_4)) ==
+ (BIT_5 | BIT_4)) {
+ /* Force 'loop preferred, else point-to-point'. */
+ nv->add_firmware_options[0] &= ~(BIT_6 | BIT_5 | BIT_4);
+ nv->add_firmware_options[0] |= BIT_5;
+ }
+ strcpy(ha->model_number, "QLA22xx");
+ } else /*if (IS_QLA2100(ha))*/ {
+ strcpy(ha->model_number, "QLA2100");
+ }
+
+ /*
+ * Copy over NVRAM RISC parameter block to initialization control block.
+ */
+ dptr1 = (uint8_t *)icb;
+ dptr2 = (uint8_t *)&nv->parameter_block_version;
+ cnt = (uint8_t *)&icb->request_q_outpointer - (uint8_t *)&icb->version;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ /* Copy 2nd half. */
+ dptr1 = (uint8_t *)icb->add_firmware_options;
+ cnt = (uint8_t *)icb->reserved_3 - (uint8_t *)icb->add_firmware_options;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ /* Prepare nodename */
+ if ((icb->firmware_options[1] & BIT_6) == 0) {
+ /*
+ * Firmware will apply the following mask if the nodename was
+ * not provided.
+ */
+ memcpy(icb->node_name, icb->port_name, WWN_SIZE);
+ icb->node_name[0] &= 0xF0;
+ }
+
+ /*
+ * Set host adapter parameters.
+ */
+ ha->nvram_version = nv->nvram_version;
+
+ ha->flags.disable_risc_code_load = ((nv->host_p[0] & BIT_4) ? 1 : 0);
+ /* Always load RISC code on non ISP2[12]00 chips. */
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha))
+ ha->flags.disable_risc_code_load = 0;
+ ha->flags.enable_lip_reset = ((nv->host_p[1] & BIT_1) ? 1 : 0);
+ ha->flags.enable_lip_full_login = ((nv->host_p[1] & BIT_2) ? 1 : 0);
+ ha->flags.enable_target_reset = ((nv->host_p[1] & BIT_3) ? 1 : 0);
+ ha->flags.enable_led_scheme = ((nv->efi_parameters & BIT_3) ? 1 : 0);
+
+ ha->operating_mode =
+ (icb->add_firmware_options[0] & (BIT_6 | BIT_5 | BIT_4)) >> 4;
+
+ memcpy(ha->fw_seriallink_options, nv->seriallink_options,
+ sizeof(ha->fw_seriallink_options));
+
+ /* save HBA serial number */
+ ha->serial0 = icb->port_name[5];
+ ha->serial1 = icb->port_name[6];
+ ha->serial2 = icb->port_name[7];
+ memcpy(ha->node_name, icb->node_name, WWN_SIZE);
+
+ icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+
+ ha->retry_count = nv->retry_count;
+
+ /* Set minimum login_timeout to 4 seconds. */
+ if (nv->login_timeout < ql2xlogintimeout)
+ nv->login_timeout = ql2xlogintimeout;
+ if (nv->login_timeout < 4)
+ nv->login_timeout = 4;
+ ha->login_timeout = nv->login_timeout;
+ icb->login_timeout = nv->login_timeout;
+
+ /* Set minimum RATOV to 200 tenths of a second. */
+ ha->r_a_tov = 200;
+
+ ha->minimum_timeout =
+ (ha->login_timeout * ha->retry_count) + nv->port_down_retry_count;
+ ha->loop_reset_delay = nv->reset_delay;
+
+ /* Will get the value from NVRAM. */
+ ha->loop_down_timeout = LOOP_DOWN_TIMEOUT;
+
+ /* Link Down Timeout = 0:
+ *
+ * When Port Down timer expires we will start returning
+ * I/O's to OS with "DID_NO_CONNECT".
+ *
+ * Link Down Timeout != 0:
+ *
+ * The driver waits for the link to come up after link down
+ * before returning I/Os to OS with "DID_NO_CONNECT".
+ */
+ if (nv->link_down_timeout == 0) {
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - ha->loop_down_timeout);
+ } else {
+ ha->link_down_timeout = nv->link_down_timeout;
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - ha->link_down_timeout);
+ }
+
+ ha->max_luns = MAX_LUNS;
+ ha->max_probe_luns = le16_to_cpu(nv->max_luns_per_target);
+ if (ha->max_probe_luns == 0)
+ ha->max_probe_luns = MIN_LUNS;
+
+ /*
+ * Need enough time to try and get the port back.
+ */
+ ha->port_down_retry_count = nv->port_down_retry_count;
+ if (qlport_down_retry)
+ ha->port_down_retry_count = qlport_down_retry;
+ /* Set login_retry_count */
+ ha->login_retry_count = nv->retry_count;
+ if (ha->port_down_retry_count == nv->port_down_retry_count &&
+ ha->port_down_retry_count > 3)
+ ha->login_retry_count = ha->port_down_retry_count;
+ else if (ha->port_down_retry_count > (int)ha->login_retry_count)
+ ha->login_retry_count = ha->port_down_retry_count;
+ if (ql2xloginretrycount)
+ ha->login_retry_count = ql2xloginretrycount;
+
+ ha->binding_type = Bind;
+ if (ha->binding_type != BIND_BY_PORT_NAME &&
+ ha->binding_type != BIND_BY_PORT_ID) {
+ qla_printk(KERN_WARNING, ha,
+ "Invalid binding type specified (%d), "
+ "defaulting to BIND_BY_PORT_NAME!!!\n", ha->binding_type);
+
+ ha->binding_type = BIND_BY_PORT_NAME;
+ }
+
+ icb->lun_enables = __constant_cpu_to_le16(0);
+ icb->command_resource_count = 0;
+ icb->immediate_notify_resource_count = 0;
+ icb->timeout = __constant_cpu_to_le16(0);
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ /* Enable RIO */
+ icb->firmware_options[0] &= ~BIT_3;
+ icb->add_firmware_options[0] &=
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ icb->add_firmware_options[0] |= BIT_2;
+ icb->response_accumulation_timer = 3;
+ icb->interrupt_delay_timer = 5;
+
+ ha->flags.process_response_queue = 1;
+ } else {
+ /* Enable ZIO -- Support mode 5 only. */
+ timer_mode = icb->add_firmware_options[0] &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ icb->add_firmware_options[0] &=
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ if (ql2xenablezio)
+ timer_mode = BIT_2 | BIT_0;
+ if (timer_mode == (BIT_2 | BIT_0)) {
+ DEBUG2(printk("scsi(%ld): ZIO enabled; timer delay "
+ "(%d).\n", ha->host_no, ql2xintrdelaytimer));
+ qla_printk(KERN_INFO, ha,
+ "ZIO enabled; timer delay (%d).\n",
+ ql2xintrdelaytimer);
+
+ icb->add_firmware_options[0] |= timer_mode;
+ icb->interrupt_delay_timer = ql2xintrdelaytimer;
+ ha->flags.process_response_queue = 1;
+ }
+ }
+
+ if (rval) {
+ DEBUG2_3(printk(KERN_WARNING
+ "scsi(%ld): NVRAM configuration failed!\n", ha->host_no));
+ }
+ return (rval);
+}
+
+/*
+* qla2x00_init_tgt_map
+* Initializes target map.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Output:
+* TGT_Q initialized
+*/
+static void
+qla2x00_init_tgt_map(scsi_qla_host_t *ha)
+{
+ uint32_t t;
+
+ for (t = 0; t < MAX_TARGETS; t++)
+ TGT_Q(ha, t) = (os_tgt_t *)NULL;
+}
+
+/**
+ * qla2x00_alloc_fcport() - Allocate a generic fcport.
+ * @ha: HA context
+ * @flags: allocation flags
+ *
+ * Returns a pointer to the allocated fcport, or NULL, if none available.
+ */
+fc_port_t *
+qla2x00_alloc_fcport(scsi_qla_host_t *ha, int flags)
+{
+ fc_port_t *fcport;
+
+ fcport = kmalloc(sizeof(fc_port_t), flags);
+ if (fcport == NULL)
+ return (fcport);
+
+ /* Setup fcport template structure. */
+ memset(fcport, 0, sizeof (fc_port_t));
+ fcport->ha = ha;
+ fcport->port_type = FCT_UNKNOWN;
+ fcport->loop_id = FC_NO_LOOP_ID;
+ fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
+ atomic_set(&fcport->state, FCS_UNCONFIGURED);
+ fcport->flags = FCF_RLC_SUPPORT;
+ INIT_LIST_HEAD(&fcport->fcluns);
+
+ return (fcport);
+}
+
+/*
+ * qla2x00_configure_loop
+ * Updates Fibre Channel Device Database with what is actually on loop.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ * 1 = error.
+ * 2 = database was full and device was not configured.
+ */
+static int
+qla2x00_configure_loop(scsi_qla_host_t *ha)
+{
+ int rval;
+ unsigned long flags, save_flags;
+
+ rval = QLA_SUCCESS;
+
+ /* Get Initiator ID */
+ if (test_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags)) {
+ rval = qla2x00_configure_hba(ha);
+ if (rval != QLA_SUCCESS) {
+ DEBUG(printk("scsi(%ld): Unable to configure HBA.\n",
+ ha->host_no));
+ return (rval);
+ }
+ }
+
+ save_flags = flags = ha->dpc_flags;
+ DEBUG(printk("scsi(%ld): Configure loop -- dpc flags =0x%lx\n",
+ ha->host_no, flags));
+
+ /*
+ * If we have both an RSCN and PORT UPDATE pending then handle them
+ * both at the same time.
+ */
+ clear_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ clear_bit(RSCN_UPDATE, &ha->dpc_flags);
+ ha->mem_err = 0 ;
+
+ /* Determine what we need to do */
+ if (ha->current_topology == ISP_CFG_FL &&
+ (test_bit(LOCAL_LOOP_UPDATE, &flags))) {
+
+ ha->flags.rscn_queue_overflow = 1;
+ set_bit(RSCN_UPDATE, &flags);
+
+ } else if (ha->current_topology == ISP_CFG_F &&
+ (test_bit(LOCAL_LOOP_UPDATE, &flags))) {
+
+ ha->flags.rscn_queue_overflow = 1;
+ set_bit(RSCN_UPDATE, &flags);
+ clear_bit(LOCAL_LOOP_UPDATE, &flags);
+
+ } else if (!ha->flags.online ||
+ (test_bit(ABORT_ISP_ACTIVE, &flags))) {
+
+ ha->flags.rscn_queue_overflow = 1;
+ set_bit(RSCN_UPDATE, &flags);
+ set_bit(LOCAL_LOOP_UPDATE, &flags);
+ }
+
+ if (test_bit(LOCAL_LOOP_UPDATE, &flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ rval = qla2x00_configure_local_loop(ha);
+ }
+ }
+
+ if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ rval = qla2x00_configure_fabric(ha);
+ }
+ }
+
+ if (rval == QLA_SUCCESS) {
+ if (atomic_read(&ha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ qla2x00_config_os(ha);
+ atomic_set(&ha->loop_state, LOOP_READY);
+
+ DEBUG(printk("scsi(%ld): LOOP READY\n", ha->host_no));
+ }
+ }
+
+ if (rval) {
+ DEBUG2_3(printk("%s(%ld): *** FAILED ***\n",
+ __func__, ha->host_no));
+ } else {
+ DEBUG3(printk("%s: exiting normally\n", __func__));
+ }
+
+ /* Restore state if a resync event occured during processing */
+ if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
+ set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ if (test_bit(RSCN_UPDATE, &save_flags))
+ set_bit(RSCN_UPDATE, &ha->dpc_flags);
+ }
+
+ return (rval);
+}
+
+
+
+/*
+ * qla2x00_configure_local_loop
+ * Updates Fibre Channel Device Database with local loop devices.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ */
+static int
+qla2x00_configure_local_loop(scsi_qla_host_t *ha)
+{
+ int rval, rval2;
+ int found_devs;
+ int found;
+ fc_port_t *fcport, *new_fcport;
+
+ uint16_t index;
+ uint16_t entries;
+ char *id_iter;
+ uint16_t loop_id;
+ uint8_t domain, area, al_pa;
+
+ found_devs = 0;
+ new_fcport = NULL;
+ entries = MAX_FIBRE_DEVICES;
+
+ DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", ha->host_no));
+ DEBUG3(qla2x00_get_fcal_position_map(ha, NULL));
+
+ /* Get list of logged in devices. */
+ memset(ha->gid_list, 0, GID_LIST_SIZE);
+ rval = qla2x00_get_id_list(ha, ha->gid_list, ha->gid_list_dma,
+ &entries);
+ if (rval != QLA_SUCCESS)
+ goto cleanup_allocation;
+
+ DEBUG3(printk("scsi(%ld): Entries in ID list (%d)\n",
+ ha->host_no, entries));
+ DEBUG3(qla2x00_dump_buffer((uint8_t *)ha->gid_list,
+ entries * sizeof(struct gid_list_info)));
+
+ /* Allocate temporary fcport for any new fcports discovered. */
+ new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ rval = QLA_MEMORY_ALLOC_FAILED;
+ goto cleanup_allocation;
+ }
+ new_fcport->flags &= ~FCF_FABRIC_DEVICE;
+
+ /*
+ * Mark local devices that were present with FCF_DEVICE_LOST for now.
+ */
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (atomic_read(&fcport->state) == FCS_ONLINE &&
+ fcport->port_type != FCT_BROADCAST &&
+ (fcport->flags & FCF_FABRIC_DEVICE) == 0) {
+
+ DEBUG(printk("scsi(%ld): Marking port lost, "
+ "loop_id=0x%04x\n",
+ ha->host_no, fcport->loop_id));
+
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ fcport->flags &= ~FCF_FARP_DONE;
+ }
+ }
+
+ /* Add devices to port list. */
+ id_iter = (char *)ha->gid_list;
+ for (index = 0; index < entries; index++) {
+ domain = ((struct gid_list_info *)id_iter)->domain;
+ area = ((struct gid_list_info *)id_iter)->area;
+ al_pa = ((struct gid_list_info *)id_iter)->al_pa;
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ loop_id = (uint16_t)
+ ((struct gid_list_info *)id_iter)->loop_id_2100;
+ id_iter += 4;
+ } else {
+ loop_id = le16_to_cpu(
+ ((struct gid_list_info *)id_iter)->loop_id);
+ id_iter += 6;
+ }
+
+ /* Bypass reserved domain fields. */
+ if ((domain & 0xf0) == 0xf0)
+ continue;
+
+ /* Bypass if not same domain and area of adapter. */
+ if (area != ha->d_id.b.area || domain != ha->d_id.b.domain)
+ continue;
+
+ /* Bypass invalid local loop ID. */
+ if (loop_id > LAST_LOCAL_LOOP_ID)
+ continue;
+
+ /* Fill in member data. */
+ new_fcport->d_id.b.domain = domain;
+ new_fcport->d_id.b.area = area;
+ new_fcport->d_id.b.al_pa = al_pa;
+ new_fcport->loop_id = loop_id;
+ rval2 = qla2x00_get_port_database(ha, new_fcport, 0);
+ if (rval2 != QLA_SUCCESS) {
+ DEBUG2(printk("scsi(%ld): Failed to retrieve fcport "
+ "information -- get_port_database=%x, "
+ "loop_id=0x%04x\n",
+ ha->host_no, rval2, new_fcport->loop_id));
+ continue;
+ }
+
+ /* Check for matching device in port list. */
+ found = 0;
+ fcport = NULL;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (memcmp(new_fcport->port_name, fcport->port_name,
+ WWN_SIZE))
+ continue;
+
+ fcport->flags &= ~(FCF_FABRIC_DEVICE |
+ FCF_PERSISTENT_BOUND);
+ fcport->loop_id = new_fcport->loop_id;
+ fcport->port_type = new_fcport->port_type;
+ fcport->d_id.b24 = new_fcport->d_id.b24;
+ memcpy(fcport->node_name, new_fcport->node_name,
+ WWN_SIZE);
+
+ found++;
+ break;
+ }
+
+ if (!found) {
+ /* New device, add to fcports list. */
+ new_fcport->flags &= ~FCF_PERSISTENT_BOUND;
+ list_add_tail(&new_fcport->list, &ha->fcports);
+
+ /* Allocate a new replacement fcport. */
+ fcport = new_fcport;
+ new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ rval = QLA_MEMORY_ALLOC_FAILED;
+ goto cleanup_allocation;
+ }
+ new_fcport->flags &= ~FCF_FABRIC_DEVICE;
+ }
+
+ qla2x00_update_fcport(ha, fcport);
+
+ found_devs++;
+ }
+
+cleanup_allocation:
+ if (new_fcport)
+ kfree(new_fcport);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(printk("scsi(%ld): Configure local loop error exit: "
+ "rval=%x\n", ha->host_no, rval));
+ }
+
+ if (found_devs) {
+ ha->device_flags |= DFLG_LOCAL_DEVICES;
+ ha->device_flags &= ~DFLG_RETRY_LOCAL_DEVICES;
+ }
+
+ return (rval);
+}
+
+static void
+qla2x00_probe_for_all_luns(scsi_qla_host_t *ha)
+{
+ fc_port_t *fcport;
+
+ qla2x00_mark_all_devices_lost(ha);
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ qla2x00_update_fcport(ha, fcport);
+ }
+}
+
+/*
+ * qla2x00_update_fcport
+ * Updates device on list.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fcport = port structure pointer.
+ *
+ * Return:
+ * 0 - Success
+ * BIT_0 - error
+ *
+ * Context:
+ * Kernel context.
+ */
+static void
+qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+ uint16_t index;
+ unsigned long flags;
+ srb_t *sp;
+
+ fcport->ha = ha;
+ fcport->login_retry = 0;
+ fcport->port_login_retry_count = ha->port_down_retry_count *
+ PORT_RETRY_TIME;
+ atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
+ PORT_RETRY_TIME);
+ fcport->flags &= ~FCF_LOGIN_NEEDED;
+
+ /*
+ * Check for outstanding cmd on tape Bypass LUN discovery if active
+ * command on tape.
+ */
+ if (fcport->flags & FCF_TAPE_PRESENT) {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ if ((sp = ha->outstanding_cmds[index]) != 0) {
+ if (sp->fclun->fcport == fcport) {
+ atomic_set(&fcport->state, FCS_ONLINE);
+ spin_unlock_irqrestore(
+ &ha->hardware_lock, flags);
+ return;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+
+ /* Do LUN discovery. */
+ if (fcport->port_type == FCT_INITIATOR ||
+ fcport->port_type == FCT_BROADCAST) {
+ fcport->device_type = TYPE_PROCESSOR;
+ } else {
+ qla2x00_lun_discovery(ha, fcport);
+ }
+ atomic_set(&fcport->state, FCS_ONLINE);
+}
+
+/*
+ * qla2x00_lun_discovery
+ * Issue SCSI inquiry command for LUN discovery.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * fcport: FC port structure pointer.
+ *
+ * Context:
+ * Kernel context.
+ */
+static void
+qla2x00_lun_discovery(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+ inq_cmd_rsp_t *inq;
+ dma_addr_t inq_dma;
+ uint16_t lun;
+
+ inq = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &inq_dma);
+ if (inq == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - INQ\n");
+ return;
+ }
+
+ /* Always add a fc_lun_t structure for lun 0 -- mid-layer requirement */
+ qla2x00_add_lun(fcport, 0);
+
+ /* If report LUN works, exit. */
+ if (qla2x00_rpt_lun_discovery(ha, fcport, inq, inq_dma) !=
+ QLA_SUCCESS) {
+ for (lun = 0; lun < ha->max_probe_luns; lun++) {
+ /* Configure LUN. */
+ qla2x00_cfg_lun(ha, fcport, lun, inq, inq_dma);
+ }
+ }
+
+ dma_pool_free(ha->s_dma_pool, inq, inq_dma);
+}
+
+/*
+ * qla2x00_rpt_lun_discovery
+ * Issue SCSI report LUN command for LUN discovery.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * fcport: FC port structure pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_rpt_lun_discovery(scsi_qla_host_t *ha, fc_port_t *fcport,
+ inq_cmd_rsp_t *inq, dma_addr_t inq_dma)
+{
+ int rval;
+ uint32_t len, cnt;
+ uint16_t lun;
+
+ /* Assume a failed status */
+ rval = QLA_FUNCTION_FAILED;
+
+ /* No point in continuing if the device doesn't support RLC */
+ if ((fcport->flags & FCF_RLC_SUPPORT) == 0)
+ return (rval);
+
+ rval = qla2x00_report_lun(ha, fcport);
+ if (rval != QLA_SUCCESS)
+ return (rval);
+
+ /* Configure LUN list. */
+ len = be32_to_cpu(ha->rlc_rsp->list.hdr.len);
+ len /= 8;
+ for (cnt = 0; cnt < len; cnt++) {
+ lun = CHAR_TO_SHORT(ha->rlc_rsp->list.lst[cnt].lsb,
+ ha->rlc_rsp->list.lst[cnt].msb.b);
+
+ DEBUG3(printk("scsi(%ld): RLC lun = (%d)\n", ha->host_no, lun));
+
+ /* We only support 0 through MAX_LUNS-1 range */
+ if (lun < MAX_LUNS) {
+ qla2x00_cfg_lun(ha, fcport, lun, inq, inq_dma);
+ }
+ }
+ atomic_set(&fcport->state, FCS_ONLINE);
+
+ return (rval);
+}
+
+/*
+ * qla2x00_report_lun
+ * Issue SCSI report LUN command.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * fcport: FC port structure pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_report_lun(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+ int rval;
+ uint16_t retries;
+ uint16_t comp_status;
+ uint16_t scsi_status;
+ rpt_lun_cmd_rsp_t *rlc;
+ dma_addr_t rlc_dma;
+
+ rval = QLA_FUNCTION_FAILED;
+ rlc = ha->rlc_rsp;
+ rlc_dma = ha->rlc_rsp_dma;
+
+ for (retries = 3; retries; retries--) {
+ memset(rlc, 0, sizeof(rpt_lun_cmd_rsp_t));
+ rlc->p.cmd.entry_type = COMMAND_A64_TYPE;
+ rlc->p.cmd.entry_count = 1;
+ SET_TARGET_ID(ha, rlc->p.cmd.target, fcport->loop_id);
+ rlc->p.cmd.control_flags =
+ __constant_cpu_to_le16(CF_READ | CF_SIMPLE_TAG);
+ rlc->p.cmd.scsi_cdb[0] = REPORT_LUNS;
+ rlc->p.cmd.scsi_cdb[8] = MSB(sizeof(rpt_lun_lst_t));
+ rlc->p.cmd.scsi_cdb[9] = LSB(sizeof(rpt_lun_lst_t));
+ rlc->p.cmd.dseg_count = __constant_cpu_to_le16(1);
+ rlc->p.cmd.timeout = __constant_cpu_to_le16(10);
+ rlc->p.cmd.byte_count =
+ __constant_cpu_to_le32(sizeof(rpt_lun_lst_t));
+ rlc->p.cmd.dseg_0_address[0] = cpu_to_le32(
+ LSD(rlc_dma + sizeof(sts_entry_t)));
+ rlc->p.cmd.dseg_0_address[1] = cpu_to_le32(
+ MSD(rlc_dma + sizeof(sts_entry_t)));
+ rlc->p.cmd.dseg_0_length =
+ __constant_cpu_to_le32(sizeof(rpt_lun_lst_t));
+
+ rval = qla2x00_issue_iocb(ha, rlc, rlc_dma,
+ sizeof(rpt_lun_cmd_rsp_t));
+
+ comp_status = le16_to_cpu(rlc->p.rsp.comp_status);
+ scsi_status = le16_to_cpu(rlc->p.rsp.scsi_status);
+
+ if (rval != QLA_SUCCESS || comp_status != CS_COMPLETE ||
+ scsi_status & SS_CHECK_CONDITION) {
+
+ /* Device underrun, treat as OK. */
+ if (rval == QLA_SUCCESS &&
+ comp_status == CS_DATA_UNDERRUN &&
+ scsi_status & SS_RESIDUAL_UNDER) {
+
+ rval = QLA_SUCCESS;
+ break;
+ }
+
+ DEBUG(printk("scsi(%ld): RLC failed to issue iocb! "
+ "fcport=[%04x/%p] rval=%x cs=%x ss=%x\n",
+ ha->host_no, fcport->loop_id, fcport, rval,
+ comp_status, scsi_status));
+
+ rval = QLA_FUNCTION_FAILED;
+ if (scsi_status & SS_CHECK_CONDITION) {
+ DEBUG2(printk("scsi(%ld): RLC "
+ "SS_CHECK_CONDITION Sense Data "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ha->host_no,
+ rlc->p.rsp.req_sense_data[0],
+ rlc->p.rsp.req_sense_data[1],
+ rlc->p.rsp.req_sense_data[2],
+ rlc->p.rsp.req_sense_data[3],
+ rlc->p.rsp.req_sense_data[4],
+ rlc->p.rsp.req_sense_data[5],
+ rlc->p.rsp.req_sense_data[6],
+ rlc->p.rsp.req_sense_data[7]));
+ if (rlc->p.rsp.req_sense_data[2] ==
+ ILLEGAL_REQUEST) {
+ fcport->flags &= ~(FCF_RLC_SUPPORT);
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_cfg_lun
+ * Configures LUN into fcport LUN list.
+ *
+ * Input:
+ * fcport: FC port structure pointer.
+ * lun: LUN number.
+ *
+ * Context:
+ * Kernel context.
+ */
+static fc_lun_t *
+qla2x00_cfg_lun(scsi_qla_host_t *ha, fc_port_t *fcport, uint16_t lun,
+ inq_cmd_rsp_t *inq, dma_addr_t inq_dma)
+{
+ fc_lun_t *fclun;
+ uint8_t device_type;
+
+ /* Bypass LUNs that failed. */
+ if (qla2x00_inquiry(ha, fcport, lun, inq, inq_dma) != QLA_SUCCESS) {
+ DEBUG2(printk("scsi(%ld): Failed inquiry - loop id=0x%04x "
+ "lun=%d\n", ha->host_no, fcport->loop_id, lun));
+
+ return (NULL);
+ }
+ device_type = (inq->inq[0] & 0x1f);
+ switch (device_type) {
+ case TYPE_DISK:
+ case TYPE_PROCESSOR:
+ case TYPE_WORM:
+ case TYPE_ROM:
+ case TYPE_SCANNER:
+ case TYPE_MOD:
+ case TYPE_MEDIUM_CHANGER:
+ case TYPE_ENCLOSURE:
+ case 0x20:
+ case 0x0C:
+ break;
+ case TYPE_TAPE:
+ fcport->flags |= FCF_TAPE_PRESENT;
+ break;
+ default:
+ DEBUG2(printk("scsi(%ld): Unsupported lun type -- "
+ "loop id=0x%04x lun=%d type=%x\n",
+ ha->host_no, fcport->loop_id, lun, device_type));
+ return (NULL);
+ }
+
+ fcport->device_type = device_type;
+ fclun = qla2x00_add_lun(fcport, lun);
+
+ if (fclun != NULL) {
+ atomic_set(&fcport->state, FCS_ONLINE);
+ }
+
+ return (fclun);
+}
+
+/*
+ * qla2x00_add_lun
+ * Adds LUN to database
+ *
+ * Input:
+ * fcport: FC port structure pointer.
+ * lun: LUN number.
+ *
+ * Context:
+ * Kernel context.
+ */
+static fc_lun_t *
+qla2x00_add_lun(fc_port_t *fcport, uint16_t lun)
+{
+ int found;
+ fc_lun_t *fclun;
+
+ if (fcport == NULL) {
+ DEBUG(printk("scsi: Unable to add lun to NULL port\n"));
+ return (NULL);
+ }
+
+ /* Allocate LUN if not already allocated. */
+ found = 0;
+ list_for_each_entry(fclun, &fcport->fcluns, list) {
+ if (fclun->lun == lun) {
+ found++;
+ break;
+ }
+ }
+ if (found)
+ return (NULL);
+
+ fclun = kmalloc(sizeof(fc_lun_t), GFP_ATOMIC);
+ if (fclun == NULL) {
+ printk(KERN_WARNING
+ "%s(): Memory Allocation failed - FCLUN\n",
+ __func__);
+ return (NULL);
+ }
+
+ /* Setup LUN structure. */
+ memset(fclun, 0, sizeof(fc_lun_t));
+ fclun->lun = lun;
+ fclun->fcport = fcport;
+ fclun->o_fcport = fcport;
+ fclun->device_type = fcport->device_type;
+ atomic_set(&fcport->state, FCS_UNCONFIGURED);
+
+ list_add_tail(&fclun->list, &fcport->fcluns);
+
+ return (fclun);
+}
+
+/*
+ * qla2x00_inquiry
+ * Issue SCSI inquiry command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fcport = FC port structure pointer.
+ *
+ * Return:
+ * 0 - Success
+ * BIT_0 - error
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_inquiry(scsi_qla_host_t *ha,
+ fc_port_t *fcport, uint16_t lun, inq_cmd_rsp_t *inq, dma_addr_t inq_dma)
+{
+ int rval;
+ uint16_t retries;
+ uint16_t comp_status;
+ uint16_t scsi_status;
+
+ rval = QLA_FUNCTION_FAILED;
+
+ for (retries = 3; retries; retries--) {
+ memset(inq, 0, sizeof(inq_cmd_rsp_t));
+ inq->p.cmd.entry_type = COMMAND_A64_TYPE;
+ inq->p.cmd.entry_count = 1;
+ inq->p.cmd.lun = cpu_to_le16(lun);
+ SET_TARGET_ID(ha, inq->p.cmd.target, fcport->loop_id);
+ inq->p.cmd.control_flags =
+ __constant_cpu_to_le16(CF_READ | CF_SIMPLE_TAG);
+ inq->p.cmd.scsi_cdb[0] = INQUIRY;
+ inq->p.cmd.scsi_cdb[4] = INQ_DATA_SIZE;
+ inq->p.cmd.dseg_count = __constant_cpu_to_le16(1);
+ inq->p.cmd.timeout = __constant_cpu_to_le16(10);
+ inq->p.cmd.byte_count =
+ __constant_cpu_to_le32(INQ_DATA_SIZE);
+ inq->p.cmd.dseg_0_address[0] = cpu_to_le32(
+ LSD(inq_dma + sizeof(sts_entry_t)));
+ inq->p.cmd.dseg_0_address[1] = cpu_to_le32(
+ MSD(inq_dma + sizeof(sts_entry_t)));
+ inq->p.cmd.dseg_0_length =
+ __constant_cpu_to_le32(INQ_DATA_SIZE);
+
+ DEBUG5(printk("scsi(%ld): Lun Inquiry - fcport=[%04x/%p],"
+ " lun (%d)\n",
+ ha->host_no, fcport->loop_id, fcport, lun));
+
+ rval = qla2x00_issue_iocb(ha, inq, inq_dma,
+ sizeof(inq_cmd_rsp_t));
+
+ comp_status = le16_to_cpu(inq->p.rsp.comp_status);
+ scsi_status = le16_to_cpu(inq->p.rsp.scsi_status);
+
+ DEBUG5(printk("scsi(%ld): lun (%d) inquiry - "
+ "inq[0]= 0x%x, comp status 0x%x, scsi status 0x%x, "
+ "rval=%d\n",
+ ha->host_no, lun, inq->inq[0], comp_status, scsi_status,
+ rval));
+
+ if (rval != QLA_SUCCESS || comp_status != CS_COMPLETE ||
+ scsi_status & SS_CHECK_CONDITION) {
+
+ DEBUG(printk("scsi(%ld): INQ failed to issue iocb! "
+ "fcport=[%04x/%p] rval=%x cs=%x ss=%x\n",
+ ha->host_no, fcport->loop_id, fcport, rval,
+ comp_status, scsi_status));
+
+ if (rval == QLA_SUCCESS)
+ rval = QLA_FUNCTION_FAILED;
+
+ if (scsi_status & SS_CHECK_CONDITION) {
+ DEBUG2(printk("scsi(%ld): INQ "
+ "SS_CHECK_CONDITION Sense Data "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ha->host_no,
+ inq->p.rsp.req_sense_data[0],
+ inq->p.rsp.req_sense_data[1],
+ inq->p.rsp.req_sense_data[2],
+ inq->p.rsp.req_sense_data[3],
+ inq->p.rsp.req_sense_data[4],
+ inq->p.rsp.req_sense_data[5],
+ inq->p.rsp.req_sense_data[6],
+ inq->p.rsp.req_sense_data[7]));
+ }
+
+ /* Device underrun drop LUN. */
+ if (comp_status == CS_DATA_UNDERRUN &&
+ scsi_status & SS_RESIDUAL_UNDER) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ return (rval);
+}
+
+
+/*
+ * qla2x00_configure_fabric
+ * Setup SNS devices with loop ID's.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ * BIT_0 = error
+ */
+static int
+qla2x00_configure_fabric(scsi_qla_host_t *ha)
+{
+ int rval, rval2;
+ fc_port_t *fcport, *fcptemp;
+ uint16_t next_loopid;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ LIST_HEAD(new_fcports);
+
+ /* If FL port exists, then SNS is present */
+ rval = qla2x00_get_port_name(ha, SNS_FL_PORT, NULL, 0);
+ if (rval != QLA_SUCCESS) {
+ DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL "
+ "Port\n", ha->host_no));
+
+ ha->device_flags &= ~SWITCH_FOUND;
+ return (QLA_SUCCESS);
+ }
+
+ /* Mark devices that need re-synchronization. */
+ rval2 = qla2x00_device_resync(ha);
+ if (rval2 == QLA_RSCNS_HANDLED) {
+ /* No point doing the scan, just continue. */
+ return (QLA_SUCCESS);
+ }
+ do {
+ /* Ensure we are logged into the SNS. */
+ qla2x00_login_fabric(ha, SIMPLE_NAME_SERVER, 0xff, 0xff, 0xfc,
+ mb, BIT_1 | BIT_0);
+ if (mb[0] != MBS_COMMAND_COMPLETE) {
+ DEBUG2(qla_printk(KERN_INFO, ha,
+ "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
+ "mb[2]=%x mb[6]=%x mb[7]=%x\n", SIMPLE_NAME_SERVER,
+ mb[0], mb[1], mb[2], mb[6], mb[7]));
+ return (QLA_SUCCESS);
+ }
+
+ if (test_and_clear_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags)) {
+ if (qla2x00_rft_id(ha)) {
+ /* EMPTY */
+ DEBUG2(printk("scsi(%ld): Register FC-4 "
+ "TYPE failed.\n", ha->host_no));
+ }
+ if (qla2x00_rff_id(ha)) {
+ /* EMPTY */
+ DEBUG2(printk("scsi(%ld): Register FC-4 "
+ "Features failed.\n", ha->host_no));
+ }
+ if (qla2x00_rnn_id(ha)) {
+ /* EMPTY */
+ DEBUG2(printk("scsi(%ld): Register Node Name "
+ "failed.\n", ha->host_no));
+ } else if (qla2x00_rsnn_nn(ha)) {
+ /* EMPTY */
+ DEBUG2(printk("scsi(%ld): Register Symbolic "
+ "Node Name failed.\n", ha->host_no));
+ }
+ }
+
+ rval = qla2x00_find_all_fabric_devs(ha, &new_fcports);
+ if (rval != QLA_SUCCESS)
+ break;
+
+ /*
+ * Logout all previous fabric devices marked lost, except
+ * tape devices.
+ */
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ break;
+
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
+ continue;
+
+ if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
+ qla2x00_mark_device_lost(ha, fcport,
+ ql2xplogiabsentdevice);
+ if (fcport->loop_id != FC_NO_LOOP_ID &&
+ (fcport->flags & FCF_TAPE_PRESENT) == 0 &&
+ fcport->port_type != FCT_INITIATOR &&
+ fcport->port_type != FCT_BROADCAST) {
+
+ qla2x00_fabric_logout(ha,
+ fcport->loop_id);
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+ }
+ }
+
+ /* Starting free loop ID. */
+ next_loopid = ha->min_external_loopid;
+
+ /*
+ * Scan through our port list and login entries that need to be
+ * logged in.
+ */
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (atomic_read(&ha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ break;
+
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
+ (fcport->flags & FCF_LOGIN_NEEDED) == 0)
+ continue;
+
+ if (fcport->loop_id == FC_NO_LOOP_ID) {
+ fcport->loop_id = next_loopid;
+ rval = qla2x00_find_new_loop_id(ha, fcport);
+ if (rval != QLA_SUCCESS) {
+ /* Ran out of IDs to use */
+ break;
+ }
+ }
+
+ /* Login and update database */
+ qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
+ }
+
+ /* Exit if out of loop IDs. */
+ if (rval != QLA_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Login and add the new devices to our port list.
+ */
+ list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
+ if (atomic_read(&ha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ break;
+
+ /* Find a new loop ID to use. */
+ fcport->loop_id = next_loopid;
+ rval = qla2x00_find_new_loop_id(ha, fcport);
+ if (rval != QLA_SUCCESS) {
+ /* Ran out of IDs to use */
+ break;
+ }
+
+ /* Login and update database */
+ qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
+
+ /* Remove device from the new list and add it to DB */
+ list_del(&fcport->list);
+ list_add_tail(&fcport->list, &ha->fcports);
+ }
+ } while (0);
+
+ /* Free all new device structures not processed. */
+ list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ }
+
+ if (rval) {
+ DEBUG2(printk("scsi(%ld): Configure fabric error exit: "
+ "rval=%d\n", ha->host_no, rval));
+ }
+
+ return (rval);
+}
+
+
+/*
+ * qla2x00_find_all_fabric_devs
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * dev = database device entry pointer.
+ *
+ * Returns:
+ * 0 = success.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
+{
+ int rval;
+ uint16_t loop_id;
+ fc_port_t *fcport, *new_fcport, *fcptemp;
+ int found;
+
+ sw_info_t *swl;
+ int swl_idx;
+ int first_dev, last_dev;
+ port_id_t wrap, nxt_d_id;
+
+ rval = QLA_SUCCESS;
+
+ /* Try GID_PT to get device list, else GAN. */
+ swl = kmalloc(sizeof(sw_info_t) * MAX_FIBRE_DEVICES, GFP_ATOMIC);
+ if (swl == NULL) {
+ /*EMPTY*/
+ DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback "
+ "on GA_NXT\n", ha->host_no));
+ } else {
+ memset(swl, 0, sizeof(sw_info_t) * MAX_FIBRE_DEVICES);
+ if (qla2x00_gid_pt(ha, swl) != QLA_SUCCESS) {
+ kfree(swl);
+ swl = NULL;
+ } else if (qla2x00_gpn_id(ha, swl) != QLA_SUCCESS) {
+ kfree(swl);
+ swl = NULL;
+ } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) {
+ kfree(swl);
+ swl = NULL;
+ }
+ }
+ swl_idx = 0;
+
+ /* Allocate temporary fcport for any new fcports discovered. */
+ new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ if (swl)
+ kfree(swl);
+ return (QLA_MEMORY_ALLOC_FAILED);
+ }
+ new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
+
+ /* Set start port ID scan at adapter ID. */
+ first_dev = 1;
+ last_dev = 0;
+
+ /* Starting free loop ID. */
+ loop_id = ha->min_external_loopid;
+
+ for (; loop_id <= ha->last_loop_id; loop_id++) {
+ if (RESERVED_LOOP_ID(loop_id))
+ continue;
+
+ if (atomic_read(&ha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ break;
+
+ if (swl != NULL) {
+ if (last_dev) {
+ wrap.b24 = new_fcport->d_id.b24;
+ } else {
+ new_fcport->d_id.b24 = swl[swl_idx].d_id.b24;
+ memcpy(new_fcport->node_name,
+ swl[swl_idx].node_name, WWN_SIZE);
+ memcpy(new_fcport->port_name,
+ swl[swl_idx].port_name, WWN_SIZE);
+
+ if (swl[swl_idx].d_id.b.rsvd_1 != 0) {
+ last_dev = 1;
+ }
+ swl_idx++;
+ }
+ } else {
+ /* Send GA_NXT to the switch */
+ rval = qla2x00_ga_nxt(ha, new_fcport);
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "SNS scan failed -- assuming zero-entry "
+ "result...\n");
+ list_for_each_entry_safe(fcport, fcptemp,
+ new_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ }
+ rval = QLA_SUCCESS;
+ break;
+ }
+ }
+
+ /* If wrap on switch device list, exit. */
+ if (first_dev) {
+ wrap.b24 = new_fcport->d_id.b24;
+ first_dev = 0;
+ } else if (new_fcport->d_id.b24 == wrap.b24) {
+ DEBUG2(printk("scsi(%ld): device wrap (%02x%02x%02x)\n",
+ ha->host_no, new_fcport->d_id.b.domain,
+ new_fcport->d_id.b.area, new_fcport->d_id.b.al_pa));
+ break;
+ }
+
+ /* Bypass if host adapter. */
+ if (new_fcport->d_id.b24 == ha->d_id.b24)
+ continue;
+
+ /* Bypass reserved domain fields. */
+ if ((new_fcport->d_id.b.domain & 0xf0) == 0xf0)
+ continue;
+
+ /* Locate matching device in database. */
+ found = 0;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (memcmp(new_fcport->port_name, fcport->port_name,
+ WWN_SIZE))
+ continue;
+
+ found++;
+
+ /*
+ * If address the same and state FCS_ONLINE, nothing
+ * changed.
+ */
+ if (fcport->d_id.b24 == new_fcport->d_id.b24 &&
+ atomic_read(&fcport->state) == FCS_ONLINE) {
+ break;
+ }
+
+ /*
+ * If device was not a fabric device before.
+ */
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) {
+ fcport->d_id.b24 = new_fcport->d_id.b24;
+ fcport->loop_id = FC_NO_LOOP_ID;
+ fcport->flags |= (FCF_FABRIC_DEVICE |
+ FCF_LOGIN_NEEDED);
+ fcport->flags &= ~FCF_PERSISTENT_BOUND;
+ break;
+ }
+
+ /*
+ * Port ID changed or device was marked to be updated;
+ * Log it out if still logged in and mark it for
+ * relogin later.
+ */
+ fcport->d_id.b24 = new_fcport->d_id.b24;
+ fcport->flags |= FCF_LOGIN_NEEDED;
+ if (fcport->loop_id != FC_NO_LOOP_ID &&
+ (fcport->flags & FCF_TAPE_PRESENT) == 0 &&
+ fcport->port_type != FCT_INITIATOR &&
+ fcport->port_type != FCT_BROADCAST) {
+ qla2x00_fabric_logout(ha, fcport->loop_id);
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+
+ break;
+ }
+
+ if (found)
+ continue;
+
+ /* If device was not in our fcports list, then add it. */
+ list_add_tail(&new_fcport->list, new_fcports);
+
+ /* Allocate a new replacement fcport. */
+ nxt_d_id.b24 = new_fcport->d_id.b24;
+ new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ if (swl)
+ kfree(swl);
+ return (QLA_MEMORY_ALLOC_FAILED);
+ }
+ new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
+ new_fcport->d_id.b24 = nxt_d_id.b24;
+ }
+
+ if (swl)
+ kfree(swl);
+
+ if (new_fcport)
+ kfree(new_fcport);
+
+ if (!list_empty(new_fcports))
+ ha->device_flags |= DFLG_FABRIC_DEVICES;
+
+ return (rval);
+}
+
+/*
+ * qla2x00_find_new_loop_id
+ * Scan through our port list and find a new usable loop ID.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * dev: port structure pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
+{
+ int rval;
+ int found;
+ fc_port_t *fcport;
+ uint16_t first_loop_id;
+
+ rval = QLA_SUCCESS;
+
+ /* Save starting loop ID. */
+ first_loop_id = dev->loop_id;
+
+ for (;;) {
+ /* Skip loop ID if already used by adapter. */
+ if (dev->loop_id == ha->loop_id) {
+ dev->loop_id++;
+ }
+
+ /* Skip reserved loop IDs. */
+ while (RESERVED_LOOP_ID(dev->loop_id)) {
+ dev->loop_id++;
+ }
+
+ /* Reset loop ID if passed the end. */
+ if (dev->loop_id > ha->last_loop_id) {
+ /* first loop ID. */
+ dev->loop_id = ha->min_external_loopid;
+ }
+
+ /* Check for loop ID being already in use. */
+ found = 0;
+ fcport = NULL;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (fcport->loop_id == dev->loop_id && fcport != dev) {
+ /* ID possibly in use */
+ found++;
+ break;
+ }
+ }
+
+ /* If not in use then it is free to use. */
+ if (!found) {
+ break;
+ }
+
+ /* ID in use. Try next value. */
+ dev->loop_id++;
+
+ /* If wrap around. No free ID to use. */
+ if (dev->loop_id == first_loop_id) {
+ dev->loop_id = FC_NO_LOOP_ID;
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_device_resync
+ * Marks devices in the database that needs resynchronization.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_device_resync(scsi_qla_host_t *ha)
+{
+ int rval;
+ int rval2;
+ uint32_t mask;
+ fc_port_t *fcport;
+ uint32_t rscn_entry;
+ uint8_t rscn_out_iter;
+ uint8_t format;
+ port_id_t d_id;
+
+ rval = QLA_RSCNS_HANDLED;
+
+ while (ha->rscn_out_ptr != ha->rscn_in_ptr ||
+ ha->flags.rscn_queue_overflow) {
+
+ rscn_entry = ha->rscn_queue[ha->rscn_out_ptr];
+ format = MSB(MSW(rscn_entry));
+ d_id.b.domain = LSB(MSW(rscn_entry));
+ d_id.b.area = MSB(LSW(rscn_entry));
+ d_id.b.al_pa = LSB(LSW(rscn_entry));
+
+ DEBUG(printk("scsi(%ld): RSCN queue entry[%d] = "
+ "[%02x/%02x%02x%02x].\n",
+ ha->host_no, ha->rscn_out_ptr, format, d_id.b.domain,
+ d_id.b.area, d_id.b.al_pa));
+
+ ha->rscn_out_ptr++;
+ if (ha->rscn_out_ptr == MAX_RSCN_COUNT)
+ ha->rscn_out_ptr = 0;
+
+ /* Skip duplicate entries. */
+ for (rscn_out_iter = ha->rscn_out_ptr;
+ !ha->flags.rscn_queue_overflow &&
+ rscn_out_iter != ha->rscn_in_ptr;
+ rscn_out_iter = (rscn_out_iter ==
+ (MAX_RSCN_COUNT - 1)) ? 0: rscn_out_iter + 1) {
+
+ if (rscn_entry != ha->rscn_queue[rscn_out_iter])
+ break;
+
+ DEBUG(printk("scsi(%ld): Skipping duplicate RSCN queue "
+ "entry found at [%d].\n", ha->host_no,
+ rscn_out_iter));
+
+ ha->rscn_out_ptr = rscn_out_iter;
+ }
+
+ /* Queue overflow, set switch default case. */
+ if (ha->flags.rscn_queue_overflow) {
+ DEBUG(printk("scsi(%ld): device_resync: rscn "
+ "overflow.\n", ha->host_no));
+
+ format = 3;
+ ha->flags.rscn_queue_overflow = 0;
+ }
+
+ switch (format) {
+ case 0:
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) &&
+ !IS_QLA6312(ha) && !IS_QLA6322(ha) &&
+ ha->flags.init_done) {
+ /* Handle port RSCN via asyncronous IOCBs */
+ rval2 = qla2x00_handle_port_rscn(ha, rscn_entry,
+ NULL, 0);
+ if (rval2 == QLA_SUCCESS)
+ continue;
+ }
+ mask = 0xffffff;
+ break;
+ case 1:
+ mask = 0xffff00;
+ break;
+ case 2:
+ mask = 0xff0000;
+ break;
+ default:
+ mask = 0x0;
+ d_id.b24 = 0;
+ ha->rscn_out_ptr = ha->rscn_in_ptr;
+ break;
+ }
+
+ rval = QLA_SUCCESS;
+
+ /* Abort any outstanding IO descriptors. */
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha))
+ qla2x00_cancel_io_descriptors(ha);
+
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
+ (fcport->d_id.b24 & mask) != d_id.b24 ||
+ fcport->port_type == FCT_BROADCAST)
+ continue;
+
+ if (atomic_read(&fcport->state) == FCS_ONLINE) {
+ if (format != 3 ||
+ fcport->port_type != FCT_INITIATOR) {
+ atomic_set(&fcport->state,
+ FCS_DEVICE_LOST);
+ }
+ }
+ fcport->flags &= ~FCF_FARP_DONE;
+ }
+ }
+ return (rval);
+}
+
+/*
+ * qla2x00_fabric_dev_login
+ * Login fabric target device and update FC port database.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * fcport: port structure list pointer.
+ * next_loopid: contains value of a new loop ID that can be used
+ * by the next login attempt.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport,
+ uint16_t *next_loopid)
+{
+ int rval;
+ int retry;
+
+ rval = QLA_SUCCESS;
+ retry = 0;
+
+ rval = qla2x00_fabric_login(ha, fcport, next_loopid);
+ if (rval == QLA_SUCCESS) {
+ rval = qla2x00_get_port_database(ha, fcport, 0);
+ if (rval != QLA_SUCCESS) {
+ qla2x00_fabric_logout(ha, fcport->loop_id);
+ } else {
+ qla2x00_update_fcport(ha, fcport);
+ }
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_fabric_login
+ * Issue fabric login command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * device = pointer to FC device type structure.
+ *
+ * Returns:
+ * 0 - Login successfully
+ * 1 - Login failed
+ * 2 - Initiator device
+ * 3 - Fatal error
+ */
+int
+qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
+ uint16_t *next_loopid)
+{
+ int rval;
+ int retry;
+ uint16_t tmp_loopid;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+
+ retry = 0;
+ tmp_loopid = 0;
+
+ for (;;) {
+ DEBUG(printk("scsi(%ld): Trying Fabric Login w/loop id 0x%04x "
+ "for port %02x%02x%02x.\n",
+ ha->host_no, fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa));
+
+ /* Login fcport on switch. */
+ qla2x00_login_fabric(ha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa, mb, BIT_0);
+ if (mb[0] == MBS_PORT_ID_USED) {
+ /*
+ * Device has another loop ID. The firmware team
+ * recommends us to perform an implicit login with the
+ * specified ID again. The ID we just used is save here
+ * so we return with an ID that can be tried by the
+ * next login.
+ */
+ retry++;
+ tmp_loopid = fcport->loop_id;
+ fcport->loop_id = mb[1];
+
+ DEBUG(printk("Fabric Login: port in use - next "
+ "loop id=0x%04x, port Id=%02x%02x%02x.\n",
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa));
+
+ } else if (mb[0] == MBS_COMMAND_COMPLETE) {
+ /*
+ * Login succeeded.
+ */
+ if (retry) {
+ /* A retry occurred before. */
+ *next_loopid = tmp_loopid;
+ } else {
+ /*
+ * No retry occurred before. Just increment the
+ * ID value for next login.
+ */
+ *next_loopid = (fcport->loop_id + 1);
+ }
+
+ if (mb[1] & BIT_0) {
+ fcport->port_type = FCT_INITIATOR;
+ } else {
+ fcport->port_type = FCT_TARGET;
+ if (mb[1] & BIT_1) {
+ fcport->flags |= FCF_TAPE_PRESENT;
+ }
+ }
+
+ rval = QLA_SUCCESS;
+ break;
+ } else if (mb[0] == MBS_LOOP_ID_USED) {
+ /*
+ * Loop ID already used, try next loop ID.
+ */
+ fcport->loop_id++;
+ rval = qla2x00_find_new_loop_id(ha, fcport);
+ if (rval != QLA_SUCCESS) {
+ /* Ran out of loop IDs to use */
+ break;
+ }
+ } else if (mb[0] == MBS_COMMAND_ERROR) {
+ /*
+ * Firmware possibly timed out during login. If NO
+ * retries are left to do then the device is declared
+ * dead.
+ */
+ *next_loopid = fcport->loop_id;
+ qla2x00_fabric_logout(ha, fcport->loop_id);
+ qla2x00_mark_device_lost(ha, fcport, 1);
+
+ rval = 1;
+ break;
+ } else {
+ /*
+ * unrecoverable / not handled error
+ */
+ DEBUG2(printk("%s(%ld): failed=%x port_id=%02x%02x%02x "
+ "loop_id=%x jiffies=%lx.\n",
+ __func__, ha->host_no, mb[0],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa, fcport->loop_id, jiffies));
+
+ *next_loopid = fcport->loop_id;
+ qla2x00_fabric_logout(ha, fcport->loop_id);
+ fcport->loop_id = FC_NO_LOOP_ID;
+ atomic_set(&fcport->state, FCS_DEVICE_DEAD);
+
+ rval = 3;
+ break;
+ }
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_local_device_login
+ * Issue local device login command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = loop id of device to login to.
+ *
+ * Returns (Where's the #define!!!!):
+ * 0 - Login successfully
+ * 1 - Login failed
+ * 3 - Fatal error
+ */
+int
+qla2x00_local_device_login(scsi_qla_host_t *ha, uint16_t loop_id)
+{
+ int rval;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+
+ memset(mb, 0, sizeof(mb));
+ rval = qla2x00_login_local_device(ha, loop_id, mb, BIT_0);
+ if (rval == QLA_SUCCESS) {
+ /* Interrogate mailbox registers for any errors */
+ if (mb[0] == MBS_COMMAND_ERROR)
+ rval = 1;
+ else if (mb[0] == MBS_COMMAND_PARAMETER_ERROR)
+ /* device not in PCB table */
+ rval = 3;
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_loop_resync
+ * Resync with fibre channel devices.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success
+ */
+int
+qla2x00_loop_resync(scsi_qla_host_t *ha)
+{
+ int rval;
+ uint32_t wait_time;
+
+ rval = QLA_SUCCESS;
+
+ atomic_set(&ha->loop_state, LOOP_UPDATE);
+ qla2x00_stats.loop_resync++;
+ clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ if (ha->flags.online) {
+ if (!(rval = qla2x00_fw_ready(ha))) {
+ /* Wait at most MAX_TARGET RSCNs for a stable link. */
+ wait_time = 256;
+ do {
+ /* v2.19.05b6 */
+ atomic_set(&ha->loop_state, LOOP_UPDATE);
+
+ /*
+ * Issue marker command only when we are going
+ * to start the I/O .
+ */
+ ha->marker_needed = 1;
+
+ /* Remap devices on Loop. */
+ clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+
+ qla2x00_configure_loop(ha);
+ wait_time--;
+ } while (!atomic_read(&ha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) &&
+ wait_time &&
+ (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)));
+ }
+ qla2x00_restart_queues(ha, 1);
+ }
+
+ if (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ if (rval) {
+ DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_restart_queues
+ * Restart device queues.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Context:
+ * Kernel/Interrupt context.
+ */
+void
+qla2x00_restart_queues(scsi_qla_host_t *ha, uint8_t flush)
+{
+ srb_t *sp;
+ int retry_q_cnt = 0;
+ int pending_q_cnt = 0;
+ struct list_head *list, *temp;
+ unsigned long flags = 0;
+
+ clear_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
+
+ /* start pending queue */
+ pending_q_cnt = ha->qthreads;
+ if (flush) {
+ spin_lock_irqsave(&ha->list_lock,flags);
+ list_for_each_safe(list, temp, &ha->pending_queue) {
+ sp = list_entry(list, srb_t, list);
+
+ if ((sp->flags & SRB_TAPE))
+ continue;
+
+ /*
+ * When time expire return request back to OS as BUSY
+ */
+ __del_from_pending_queue(ha, sp);
+ sp->cmd->result = DID_BUS_BUSY << 16;
+ sp->cmd->host_scribble = (unsigned char *)NULL;
+ __add_to_done_queue(ha, sp);
+ }
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+ } else {
+ if (!list_empty(&ha->pending_queue))
+ qla2x00_next(ha);
+ }
+
+ /*
+ * Clear out our retry queue
+ */
+ if (flush) {
+ spin_lock_irqsave(&ha->list_lock, flags);
+ retry_q_cnt = ha->retry_q_cnt;
+ list_for_each_safe(list, temp, &ha->retry_queue) {
+ sp = list_entry(list, srb_t, list);
+ /* when time expire return request back to OS as BUSY */
+ __del_from_retry_queue(ha, sp);
+ sp->cmd->result = DID_BUS_BUSY << 16;
+ sp->cmd->host_scribble = (unsigned char *)NULL;
+ __add_to_done_queue(ha, sp);
+ }
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+
+ DEBUG2(printk("%s(%ld): callback %d commands.\n",
+ __func__,
+ ha->host_no,
+ retry_q_cnt);)
+ }
+
+ DEBUG2(printk("%s(%ld): active=%ld, retry=%d, pending=%d, "
+ "done=%ld, scsi retry=%d commands.\n",
+ __func__,
+ ha->host_no,
+ ha->actthreads,
+ ha->retry_q_cnt,
+ pending_q_cnt,
+ ha->done_q_cnt,
+ ha->scsi_retry_q_cnt);)
+
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+}
+
+void
+qla2x00_rescan_fcports(scsi_qla_host_t *ha)
+{
+ int rescan_done;
+ fc_port_t *fcport;
+
+ rescan_done = 0;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if ((fcport->flags & FCF_RESCAN_NEEDED) == 0)
+ continue;
+
+ qla2x00_update_fcport(ha, fcport);
+ fcport->flags &= ~FCF_RESCAN_NEEDED;
+
+ rescan_done = 1;
+ }
+ qla2x00_probe_for_all_luns(ha);
+
+ /* Update OS target and lun structures if necessary. */
+ if (rescan_done) {
+ qla2x00_config_os(ha);
+ }
+}
+
+
+/*
+ * qla2x00_config_os
+ * Setup OS target and LUN structures.
+ *
+ * Input:
+ * ha = adapter state pointer.
+ *
+ * Context:
+ * Kernel context.
+ */
+static void
+qla2x00_config_os(scsi_qla_host_t *ha)
+{
+ fc_port_t *fcport;
+ fc_lun_t *fclun;
+ os_tgt_t *tq;
+ uint16_t tgt;
+
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ if ((tq = TGT_Q(ha, tgt)) == NULL)
+ continue;
+
+ clear_bit(TQF_ONLINE, &tq->flags);
+ }
+
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (atomic_read(&fcport->state) != FCS_ONLINE ||
+ fcport->port_type == FCT_INITIATOR ||
+ fcport->port_type == FCT_BROADCAST) {
+ fcport->os_target_id = MAX_TARGETS;
+ continue;
+ }
+
+ if (fcport->flags & FCF_FO_MASKED) {
+ continue;
+ }
+
+ /* Bind FC port to OS target number. */
+ if (qla2x00_fcport_bind(ha, fcport) == MAX_TARGETS) {
+ continue;
+ }
+
+ /* Bind FC LUN to OS LUN number. */
+ list_for_each_entry(fclun, &fcport->fcluns, list) {
+ qla2x00_fclun_bind(ha, fcport, fclun);
+ }
+ }
+}
+
+/*
+ * qla2x00_fcport_bind
+ * Locates a target number for FC port.
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * fcport = FC port structure pointer.
+ *
+ * Returns:
+ * target number
+ *
+ * Context:
+ * Kernel context.
+ */
+static uint16_t
+qla2x00_fcport_bind(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+ int found;
+ uint16_t tgt;
+ os_tgt_t *tq;
+
+ /* Check for persistent binding. */
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ if ((tq = TGT_Q(ha, tgt)) == NULL)
+ continue;
+
+ found = 0;
+ switch (ha->binding_type) {
+ case BIND_BY_PORT_ID:
+ if (fcport->d_id.b24 == tq->d_id.b24) {
+ memcpy(tq->node_name, fcport->node_name,
+ WWN_SIZE);
+ memcpy(tq->port_name, fcport->port_name,
+ WWN_SIZE);
+ found++;
+ }
+ break;
+ case BIND_BY_PORT_NAME:
+ if (memcmp(fcport->port_name, tq->port_name,
+ WWN_SIZE) == 0) {
+ /*
+ * In case of persistent binding, update the
+ * WWNN.
+ */
+ memcpy(tq->node_name, fcport->node_name,
+ WWN_SIZE);
+ found++;
+ }
+ break;
+ }
+ if (found)
+ break;
+ }
+
+ /* TODO: honor the ConfigRequired flag */
+ if (tgt == MAX_TARGETS) {
+ /* Check if targetID 0 available. */
+ tgt = 0;
+
+ if (TGT_Q(ha, tgt) != NULL) {
+ /* Locate first free target for device. */
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ if (TGT_Q(ha, tgt) == NULL) {
+ break;
+ }
+ }
+ }
+ if (tgt != MAX_TARGETS) {
+ if ((tq = qla2x00_tgt_alloc(ha, tgt)) != NULL) {
+ memcpy(tq->node_name, fcport->node_name,
+ WWN_SIZE);
+ memcpy(tq->port_name, fcport->port_name,
+ WWN_SIZE);
+ tq->d_id.b24 = fcport->d_id.b24;
+ }
+ }
+ }
+
+ /* Reset target numbers incase it changed. */
+ fcport->os_target_id = tgt;
+ if (tgt != MAX_TARGETS && tq != NULL) {
+ DEBUG2(printk("scsi(%ld): Assigning target ID=%02d @ %p to "
+ "loop id=0x%04x, port state=0x%x, port down retry=%d\n",
+ ha->host_no, tgt, tq, fcport->loop_id,
+ atomic_read(&fcport->state),
+ atomic_read(&fcport->port_down_timer)));
+
+ fcport->tgt_queue = tq;
+ fcport->flags |= FCF_PERSISTENT_BOUND;
+ tq->fcport = fcport;
+ set_bit(TQF_ONLINE, &tq->flags);
+ tq->port_down_retry_count = ha->port_down_retry_count;
+ }
+
+ if (tgt == MAX_TARGETS) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to bind fcport, loop_id=%x\n", fcport->loop_id);
+ }
+
+ return (tgt);
+}
+
+/*
+ * qla2x00_fclun_bind
+ * Binds all FC device LUNS to OS LUNS.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * fcport: FC port structure pointer.
+ *
+ * Returns:
+ * target number
+ *
+ * Context:
+ * Kernel context.
+ */
+static os_lun_t *
+qla2x00_fclun_bind(scsi_qla_host_t *ha, fc_port_t *fcport, fc_lun_t *fclun)
+{
+ os_lun_t *lq;
+ uint16_t tgt;
+ uint16_t lun;
+
+ tgt = fcport->os_target_id;
+ lun = fclun->lun;
+
+ /* Allocate LUNs */
+ if (lun >= MAX_LUNS) {
+ DEBUG2(printk("scsi(%ld): Unable to bind lun, invalid "
+ "lun=(%x).\n", ha->host_no, lun));
+ return (NULL);
+ }
+
+ /* Always alloc LUN 0 so kernel will scan past LUN 0. */
+ if (lun != 0 && (EXT_IS_LUN_BIT_SET(&(fcport->lun_mask), lun))) {
+ return (NULL);
+ }
+
+ if ((lq = qla2x00_lun_alloc(ha, tgt, lun)) == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to bind fclun, loop_id=%x lun=%x\n",
+ fcport->loop_id, lun);
+ return (NULL);
+ }
+
+ lq->fclun = fclun;
+
+ return (lq);
+}
+
+/*
+ * qla2x00_tgt_alloc
+ * Allocate and pre-initialize target queue.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * t = SCSI target number.
+ *
+ * Returns:
+ * NULL = failure
+ *
+ * Context:
+ * Kernel context.
+ */
+static os_tgt_t *
+qla2x00_tgt_alloc(scsi_qla_host_t *ha, uint16_t tgt)
+{
+ os_tgt_t *tq;
+
+ /*
+ * If SCSI addressing OK, allocate TGT queue and lock.
+ */
+ if (tgt >= MAX_TARGETS) {
+ DEBUG2(printk("scsi(%ld): Unable to allocate target, invalid "
+ "target number %d.\n", ha->host_no, tgt));
+ return (NULL);
+ }
+
+ tq = TGT_Q(ha, tgt);
+ if (tq == NULL) {
+ tq = kmalloc(sizeof(os_tgt_t), GFP_ATOMIC);
+ if (tq != NULL) {
+ DEBUG2(printk("scsi(%ld): Alloc Target %d @ %p\n",
+ ha->host_no, tgt, tq));
+
+ memset(tq, 0, sizeof(os_tgt_t));
+ tq->ha = ha;
+
+ TGT_Q(ha, tgt) = tq;
+ }
+ }
+ if (tq != NULL) {
+ tq->port_down_retry_count = ha->port_down_retry_count;
+ } else {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate target.\n");
+ ha->mem_err++;
+ }
+
+ return (tq);
+}
+
+/*
+ * qla2x00_tgt_free
+ * Frees target and LUN queues.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * t = SCSI target number.
+ *
+ * Context:
+ * Kernel context.
+ */
+void
+qla2x00_tgt_free(scsi_qla_host_t *ha, uint16_t tgt)
+{
+ os_tgt_t *tq;
+ uint16_t lun;
+
+ /*
+ * If SCSI addressing OK, allocate TGT queue and lock.
+ */
+ if (tgt >= MAX_TARGETS) {
+ DEBUG2(printk("scsi(%ld): Unable to de-allocate target, "
+ "invalid target number %d.\n", ha->host_no, tgt));
+
+ return;
+ }
+
+ tq = TGT_Q(ha, tgt);
+ if (tq != NULL) {
+ TGT_Q(ha, tgt) = NULL;
+
+ /* Free LUN structures. */
+ for (lun = 0; lun < MAX_LUNS; lun++)
+ qla2x00_lun_free(ha, tgt, lun);
+
+ kfree(tq);
+ }
+
+ return;
+}
+
+/*
+ * qla2x00_lun_alloc
+ * Allocate and initialize LUN queue.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * t = SCSI target number.
+ * l = LUN number.
+ *
+ * Returns:
+ * NULL = failure
+ *
+ * Context:
+ * Kernel context.
+ */
+static os_lun_t *
+qla2x00_lun_alloc(scsi_qla_host_t *ha, uint16_t tgt, uint16_t lun)
+{
+ os_lun_t *lq;
+
+ /*
+ * If SCSI addressing OK, allocate LUN queue.
+ */
+ if (tgt >= MAX_TARGETS || lun >= MAX_LUNS || TGT_Q(ha, tgt) == NULL) {
+ DEBUG2(printk("scsi(%ld): Unable to allocate lun, invalid "
+ "parameter.\n", ha->host_no));
+
+ return (NULL);
+ }
+
+ lq = LUN_Q(ha, tgt, lun);
+ if (lq == NULL) {
+ lq = kmalloc(sizeof(os_lun_t), GFP_ATOMIC);
+ if (lq != NULL) {
+ DEBUG2(printk("scsi(%ld): Alloc Lun %d @ tgt %d.\n",
+ ha->host_no, lun, tgt));
+
+ memset(lq, 0, sizeof(os_lun_t));
+ LUN_Q(ha, tgt, lun) = lq;
+
+ /*
+ * The following lun queue initialization code
+ * must be duplicated in alloc_ioctl_mem function
+ * for ioctl_lq.
+ */
+ lq->q_state = LUN_STATE_READY;
+ spin_lock_init(&lq->q_lock);
+ }
+ }
+
+ if (lq == NULL) {
+ qla_printk(KERN_WARNING, ha, "Unable to allocate lun.\n");
+ }
+
+ return (lq);
+}
+
+/*
+ * qla2x00_lun_free
+ * Frees LUN queue.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * t = SCSI target number.
+ *
+ * Context:
+ * Kernel context.
+ */
+static void
+qla2x00_lun_free(scsi_qla_host_t *ha, uint16_t tgt, uint16_t lun)
+{
+ os_lun_t *lq;
+
+ /*
+ * If SCSI addressing OK, allocate TGT queue and lock.
+ */
+ if (tgt >= MAX_TARGETS || lun >= MAX_LUNS) {
+ DEBUG2(printk("scsi(%ld): Unable to deallocate lun, invalid "
+ "parameter.\n", ha->host_no));
+
+ return;
+ }
+
+ if (TGT_Q(ha, tgt) != NULL && (lq = LUN_Q(ha, tgt, lun)) != NULL) {
+ LUN_Q(ha, tgt, lun) = NULL;
+ kfree(lq);
+ }
+
+ return;
+}
+
+/*
+* qla2x00_abort_isp
+* Resets ISP and aborts all outstanding commands.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+int
+qla2x00_abort_isp(scsi_qla_host_t *ha)
+{
+ unsigned long flags = 0;
+ uint16_t cnt;
+ srb_t *sp;
+ uint8_t status = 0;
+
+ if (ha->flags.online) {
+ ha->flags.online = 0;
+ clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ qla2x00_stats.ispAbort++;
+ ha->total_isp_aborts++; /* used by ioctl */
+ ha->sns_retry_cnt = 0;
+
+ qla_printk(KERN_INFO, ha,
+ "Performing ISP error recovery - ha= %p.\n", ha);
+ qla2x00_reset_chip(ha);
+
+ atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ qla2x00_mark_all_devices_lost(ha);
+ } else {
+ if (!atomic_read(&ha->loop_down_timer))
+ atomic_set(&ha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ }
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ /* Requeue all commands in outstanding command list. */
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = ha->outstanding_cmds[cnt];
+ if (sp) {
+ ha->outstanding_cmds[cnt] = NULL;
+ if (ha->actthreads)
+ ha->actthreads--;
+ sp->lun_queue->out_cnt--;
+
+ /*
+ * Set the cmd host_byte status depending on
+ * whether the scsi_error_handler is
+ * active or not.
+ */
+ if (sp->flags & SRB_TAPE) {
+ sp->cmd->result = DID_NO_CONNECT << 16;
+ } else {
+ if (ha->host->eh_active != EH_ACTIVE)
+ sp->cmd->result =
+ DID_BUS_BUSY << 16;
+ else
+ sp->cmd->result =
+ DID_RESET << 16;
+ }
+ sp->flags = 0;
+ sp->cmd->host_scribble = (unsigned char *)NULL;
+ add_to_done_queue(ha, sp);
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ qla2x00_nvram_config(ha);
+
+ if (!qla2x00_restart_isp(ha)) {
+ clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+
+ if (!atomic_read(&ha->loop_down_timer)) {
+ /*
+ * Issue marker command only when we are going
+ * to start the I/O .
+ */
+ ha->marker_needed = 1;
+ }
+
+ ha->flags.online = 1;
+
+ /* Enable ISP interrupts. */
+ qla2x00_enable_intrs(ha);
+
+ /* v2.19.5b6 Return all commands */
+ qla2x00_abort_queues(ha, 1);
+
+ /* Restart queues that may have been stopped. */
+ qla2x00_restart_queues(ha, 1);
+ ha->isp_abort_cnt = 0;
+ clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ } else { /* failed the ISP abort */
+ ha->flags.online = 1;
+ if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) {
+ if (ha->isp_abort_cnt == 0) {
+ qla_printk(KERN_WARNING, ha,
+ "ISP error recovery failed - "
+ "board disabled\n");
+ /*
+ * The next call disables the board
+ * completely.
+ */
+ qla2x00_reset_adapter(ha);
+ qla2x00_abort_queues(ha, 0);
+ ha->flags.online = 0;
+ clear_bit(ISP_ABORT_RETRY,
+ &ha->dpc_flags);
+ status = 0;
+ } else { /* schedule another ISP abort */
+ ha->isp_abort_cnt--;
+ DEBUG(printk("qla%ld: ISP abort - "
+ "retry remainning %d\n",
+ ha->host_no, ha->isp_abort_cnt);)
+ status = 1;
+ }
+ } else {
+ ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
+ DEBUG(printk("qla2x00(%ld): ISP error recovery "
+ "- retrying (%d) more times\n",
+ ha->host_no, ha->isp_abort_cnt);)
+ set_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ status = 1;
+ }
+ }
+
+ }
+
+ if (status) {
+ qla_printk(KERN_INFO, ha,
+ "qla2x00_abort_isp: **** FAILED ****\n");
+ } else {
+ DEBUG(printk(KERN_INFO
+ "qla2x00_abort_isp(%ld): exiting.\n",
+ ha->host_no);)
+ }
+
+ return(status);
+}
+
+/*
+* qla2x00_restart_isp
+* restarts the ISP after a reset
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+static int
+qla2x00_restart_isp(scsi_qla_host_t *ha)
+{
+ uint8_t status = 0;
+ device_reg_t __iomem *reg = ha->iobase;
+ unsigned long flags = 0;
+ uint32_t wait_time;
+
+ /* If firmware needs to be loaded */
+ if (qla2x00_isp_firmware(ha)) {
+ ha->flags.online = 0;
+ if (!(status = qla2x00_chip_diag(ha))) {
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ status = qla2x00_setup_chip(ha);
+ goto done;
+ }
+
+ reg = ha->iobase;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Disable SRAM, Instruction RAM and GP RAM parity. */
+ WRT_REG_WORD(&reg->hccr, (HCCR_ENABLE_PARITY + 0x0));
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ status = qla2x00_setup_chip(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Enable proper parity */
+ if (IS_QLA2300(ha))
+ /* SRAM parity */
+ WRT_REG_WORD(&reg->hccr,
+ (HCCR_ENABLE_PARITY + 0x1));
+ else
+ /* SRAM, Instruction RAM and GP RAM parity */
+ WRT_REG_WORD(&reg->hccr,
+ (HCCR_ENABLE_PARITY + 0x7));
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+ }
+
+ done:
+ if (!status && !(status = qla2x00_init_rings(ha))) {
+ clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ if (!(status = qla2x00_fw_ready(ha))) {
+ DEBUG(printk("%s(): Start configure loop, "
+ "status = %d\n",
+ __func__,
+ status);)
+ ha->flags.online = 1;
+ /* Wait at most MAX_TARGET RSCNs for a stable link. */
+ wait_time = 256;
+ do {
+ clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ qla2x00_configure_loop(ha);
+ wait_time--;
+ } while (!atomic_read(&ha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) &&
+ wait_time &&
+ (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)));
+ }
+
+ /* if no cable then assume it's good */
+ if ((ha->device_flags & DFLG_NO_CABLE))
+ status = 0;
+
+ DEBUG(printk("%s(): Configure loop done, status = 0x%x\n",
+ __func__,
+ status);)
+ }
+ return (status);
+}
+
+/*
+* qla2x00_reset_adapter
+* Reset adapter.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+static void
+qla2x00_reset_adapter(scsi_qla_host_t *ha)
+{
+ unsigned long flags = 0;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ ha->flags.online = 0;
+ qla2x00_disable_intrs(ha);
+
+ /* Reset RISC processor. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
new file mode 100644
index 000000000000..07c11330f9a3
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -0,0 +1,292 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+
+static __inline__ uint16_t qla2x00_debounce_register(volatile uint16_t __iomem *);
+/*
+ * qla2x00_debounce_register
+ * Debounce register.
+ *
+ * Input:
+ * port = register address.
+ *
+ * Returns:
+ * register value.
+ */
+static __inline__ uint16_t
+qla2x00_debounce_register(volatile uint16_t __iomem *addr)
+{
+ volatile uint16_t first;
+ volatile uint16_t second;
+
+ do {
+ first = RD_REG_WORD(addr);
+ barrier();
+ cpu_relax();
+ second = RD_REG_WORD(addr);
+ } while (first != second);
+
+ return (first);
+}
+
+static __inline__ int qla2x00_normalize_dma_addr(
+ dma_addr_t *e_addr, uint32_t *e_len,
+ dma_addr_t *ne_addr, uint32_t *ne_len);
+
+/**
+ * qla2x00_normalize_dma_addr() - Normalize an DMA address.
+ * @e_addr: Raw DMA address
+ * @e_len: Raw DMA length
+ * @ne_addr: Normalized second DMA address
+ * @ne_len: Normalized second DMA length
+ *
+ * If the address does not span a 4GB page boundary, the contents of @ne_addr
+ * and @ne_len are undefined. @e_len is updated to reflect a normalization.
+ *
+ * Example:
+ *
+ * ffffabc0ffffeeee (e_addr) start of DMA address
+ * 0000000020000000 (e_len) length of DMA transfer
+ * ffffabc11fffeeed end of DMA transfer
+ *
+ * Is the 4GB boundary crossed?
+ *
+ * ffffabc0ffffeeee (e_addr)
+ * ffffabc11fffeeed (e_addr + e_len - 1)
+ * 00000001e0000003 ((e_addr ^ (e_addr + e_len - 1))
+ * 0000000100000000 ((e_addr ^ (e_addr + e_len - 1)) & ~(0xffffffff)
+ *
+ * Compute start of second DMA segment:
+ *
+ * ffffabc0ffffeeee (e_addr)
+ * ffffabc1ffffeeee (0x100000000 + e_addr)
+ * ffffabc100000000 (0x100000000 + e_addr) & ~(0xffffffff)
+ * ffffabc100000000 (ne_addr)
+ *
+ * Compute length of second DMA segment:
+ *
+ * 00000000ffffeeee (e_addr & 0xffffffff)
+ * 0000000000001112 (0x100000000 - (e_addr & 0xffffffff))
+ * 000000001fffeeee (e_len - (0x100000000 - (e_addr & 0xffffffff))
+ * 000000001fffeeee (ne_len)
+ *
+ * Adjust length of first DMA segment
+ *
+ * 0000000020000000 (e_len)
+ * 0000000000001112 (e_len - ne_len)
+ * 0000000000001112 (e_len)
+ *
+ * Returns non-zero if the specified address was normalized, else zero.
+ */
+static __inline__ int
+qla2x00_normalize_dma_addr(
+ dma_addr_t *e_addr, uint32_t *e_len,
+ dma_addr_t *ne_addr, uint32_t *ne_len)
+{
+ int normalized;
+
+ normalized = 0;
+ if ((*e_addr ^ (*e_addr + *e_len - 1)) & ~(0xFFFFFFFFULL)) {
+ /* Compute normalized crossed address and len */
+ *ne_addr = (0x100000000ULL + *e_addr) & ~(0xFFFFFFFFULL);
+ *ne_len = *e_len - (0x100000000ULL - (*e_addr & 0xFFFFFFFFULL));
+ *e_len -= *ne_len;
+
+ normalized++;
+ }
+ return (normalized);
+}
+
+static __inline__ void qla2x00_poll(scsi_qla_host_t *);
+static inline void
+qla2x00_poll(scsi_qla_host_t *ha)
+{
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ qla2100_intr_handler(0, ha, NULL);
+ else
+ qla2300_intr_handler(0, ha, NULL);
+}
+
+
+static __inline__ void qla2x00_enable_intrs(scsi_qla_host_t *);
+static __inline__ void qla2x00_disable_intrs(scsi_qla_host_t *);
+
+static inline void
+qla2x00_enable_intrs(scsi_qla_host_t *ha)
+{
+ unsigned long flags = 0;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->interrupts_on = 1;
+ /* enable risc and host interrupts */
+ WRT_REG_WORD(&reg->ictrl, ICR_EN_INT | ICR_EN_RISC);
+ RD_REG_WORD(&reg->ictrl);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+}
+
+static inline void
+qla2x00_disable_intrs(scsi_qla_host_t *ha)
+{
+ unsigned long flags = 0;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->interrupts_on = 0;
+ /* disable risc and host interrupts */
+ WRT_REG_WORD(&reg->ictrl, 0);
+ RD_REG_WORD(&reg->ictrl);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+
+static __inline__ int qla2x00_is_wwn_zero(uint8_t *);
+
+/*
+ * qla2x00_is_wwn_zero - Check for zero node name
+ *
+ * Input:
+ * wwn = Pointer to WW name to check
+ *
+ * Returns:
+ * 1 if name is 0x00 else 0
+ *
+ * Context:
+ * Kernel context.
+ */
+static __inline__ int
+qla2x00_is_wwn_zero(uint8_t *wwn)
+{
+ int cnt;
+
+ for (cnt = 0; cnt < WWN_SIZE ; cnt++, wwn++) {
+ if (*wwn != 0)
+ break;
+ }
+ /* if zero return 1 */
+ if (cnt == WWN_SIZE)
+ return (1);
+ else
+ return (0);
+}
+
+static __inline__ uint8_t
+qla2x00_suspend_lun(scsi_qla_host_t *, os_lun_t *, int, int);
+static __inline__ uint8_t
+qla2x00_delay_lun(scsi_qla_host_t *, os_lun_t *, int);
+
+static __inline__ uint8_t
+qla2x00_suspend_lun(scsi_qla_host_t *ha, os_lun_t *lq, int time, int count)
+{
+ return (__qla2x00_suspend_lun(ha, lq, time, count, 0));
+}
+
+static __inline__ uint8_t
+qla2x00_delay_lun(scsi_qla_host_t *ha, os_lun_t *lq, int time)
+{
+ return (__qla2x00_suspend_lun(ha, lq, time, 1, 1));
+}
+
+static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *);
+/*
+ * This routine will wait for fabric devices for
+ * the reset delay.
+ */
+static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *ha)
+{
+ uint16_t fw_state;
+
+ qla2x00_get_firmware_state(ha, &fw_state);
+}
+
+/**
+ * qla2x00_issue_marker() - Issue a Marker IOCB if necessary.
+ * @ha: HA context
+ * @ha_locked: is function called with the hardware lock
+ *
+ * Returns non-zero if a failure occured, else zero.
+ */
+static inline int
+qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked)
+{
+ /* Send marker if required */
+ if (ha->marker_needed != 0) {
+ if (ha_locked) {
+ if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
+ QLA_SUCCESS)
+ return (QLA_FUNCTION_FAILED);
+ } else {
+ if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
+ QLA_SUCCESS)
+ return (QLA_FUNCTION_FAILED);
+ }
+ ha->marker_needed = 0;
+ }
+ return (QLA_SUCCESS);
+}
+
+static __inline__ void qla2x00_add_timer_to_cmd(srb_t *, int);
+static __inline__ void qla2x00_delete_timer_from_cmd(srb_t *);
+
+/**************************************************************************
+* qla2x00_add_timer_to_cmd
+*
+* Description:
+* Creates a timer for the specified command. The timeout is usually
+* the command time from kernel minus 2 secs.
+*
+* Input:
+* sp - pointer to validate
+*
+* Returns:
+* None.
+**************************************************************************/
+static inline void
+qla2x00_add_timer_to_cmd(srb_t *sp, int timeout)
+{
+ init_timer(&sp->timer);
+ sp->timer.expires = jiffies + timeout * HZ;
+ sp->timer.data = (unsigned long) sp;
+ sp->timer.function = (void (*) (unsigned long))qla2x00_cmd_timeout;
+ add_timer(&sp->timer);
+}
+
+/**************************************************************************
+* qla2x00_delete_timer_from_cmd
+*
+* Description:
+* Delete the timer for the specified command.
+*
+* Input:
+* sp - pointer to validate
+*
+* Returns:
+* None.
+**************************************************************************/
+static inline void
+qla2x00_delete_timer_from_cmd(srb_t *sp)
+{
+ if (sp->timer.function != NULL) {
+ del_timer(&sp->timer);
+ sp->timer.function = NULL;
+ sp->timer.data = (unsigned long) NULL;
+ }
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
new file mode 100644
index 000000000000..ec066074c722
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -0,0 +1,633 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+#include "qla_def.h"
+
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi_tcq.h>
+
+static inline uint16_t qla2x00_get_cmd_direction(struct scsi_cmnd *cmd);
+static inline cont_entry_t *qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *);
+static inline cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *);
+static request_t *qla2x00_req_pkt(scsi_qla_host_t *ha);
+
+/**
+ * qla2x00_get_cmd_direction() - Determine control_flag data direction.
+ * @cmd: SCSI command
+ *
+ * Returns the proper CF_* direction based on CDB.
+ */
+static inline uint16_t
+qla2x00_get_cmd_direction(struct scsi_cmnd *cmd)
+{
+ uint16_t cflags;
+
+ cflags = 0;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ cflags = CF_WRITE;
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ cflags = CF_READ;
+ return (cflags);
+}
+
+/**
+ * qla2x00_calc_iocbs_32() - Determine number of Command Type 2 and
+ * Continuation Type 0 IOCBs to allocate.
+ *
+ * @dsds: number of data segment decriptors needed
+ *
+ * Returns the number of IOCB entries needed to store @dsds.
+ */
+uint16_t
+qla2x00_calc_iocbs_32(uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > 3) {
+ iocbs += (dsds - 3) / 7;
+ if ((dsds - 3) % 7)
+ iocbs++;
+ }
+ return (iocbs);
+}
+
+/**
+ * qla2x00_calc_iocbs_64() - Determine number of Command Type 3 and
+ * Continuation Type 1 IOCBs to allocate.
+ *
+ * @dsds: number of data segment decriptors needed
+ *
+ * Returns the number of IOCB entries needed to store @dsds.
+ */
+uint16_t
+qla2x00_calc_iocbs_64(uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > 2) {
+ iocbs += (dsds - 2) / 5;
+ if ((dsds - 2) % 5)
+ iocbs++;
+ }
+ return (iocbs);
+}
+
+/**
+ * qla2x00_prep_cont_type0_iocb() - Initialize a Continuation Type 0 IOCB.
+ * @ha: HA context
+ *
+ * Returns a pointer to the Continuation Type 0 IOCB packet.
+ */
+static inline cont_entry_t *
+qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
+{
+ cont_entry_t *cont_pkt;
+
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == ha->request_q_length) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else {
+ ha->request_ring_ptr++;
+ }
+
+ cont_pkt = (cont_entry_t *)ha->request_ring_ptr;
+
+ /* Load packet defaults. */
+ *((uint32_t *)(&cont_pkt->entry_type)) =
+ __constant_cpu_to_le32(CONTINUE_TYPE);
+
+ return (cont_pkt);
+}
+
+/**
+ * qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
+ * @ha: HA context
+ *
+ * Returns a pointer to the continuation type 1 IOCB packet.
+ */
+static inline cont_a64_entry_t *
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha)
+{
+ cont_a64_entry_t *cont_pkt;
+
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == ha->request_q_length) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else {
+ ha->request_ring_ptr++;
+ }
+
+ cont_pkt = (cont_a64_entry_t *)ha->request_ring_ptr;
+
+ /* Load packet defaults. */
+ *((uint32_t *)(&cont_pkt->entry_type)) =
+ __constant_cpu_to_le32(CONTINUE_A64_TYPE);
+
+ return (cont_pkt);
+}
+
+/**
+ * qla2x00_build_scsi_iocbs_32() - Build IOCB command utilizing 32bit
+ * capable IOCB types.
+ *
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 2 IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ scsi_qla_host_t *ha;
+ struct scsi_cmnd *cmd;
+
+ cmd = sp->cmd;
+
+ /* Update entry type to indicate Command Type 2 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_TYPE);
+
+ /* No data transfer */
+ if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return;
+ }
+
+ ha = sp->ha;
+
+ cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd));
+
+ /* Three DSDs are available in the Command Type 2 IOCB */
+ avail_dsds = 3;
+ cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
+
+ /* Load data segments */
+ if (cmd->use_sg != 0) {
+ struct scatterlist *cur_seg;
+ struct scatterlist *end_seg;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ end_seg = cur_seg + tot_dsds;
+ while (cur_seg < end_seg) {
+ cont_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Seven DSDs are available in the Continuation
+ * Type 0 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type0_iocb(ha);
+ cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
+ avail_dsds = 7;
+ }
+
+ *cur_dsd++ = cpu_to_le32(sg_dma_address(cur_seg));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
+ avail_dsds--;
+
+ cur_seg++;
+ }
+ } else {
+ dma_addr_t req_dma;
+ struct page *page;
+ unsigned long offset;
+
+ page = virt_to_page(cmd->request_buffer);
+ offset = ((unsigned long)cmd->request_buffer & ~PAGE_MASK);
+ req_dma = pci_map_page(ha->pdev, page, offset,
+ cmd->request_bufflen, cmd->sc_data_direction);
+
+ sp->dma_handle = req_dma;
+
+ *cur_dsd++ = cpu_to_le32(req_dma);
+ *cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
+ }
+}
+
+/**
+ * qla2x00_build_scsi_iocbs_64() - Build IOCB command utilizing 64bit
+ * capable IOCB types.
+ *
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 3 IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ scsi_qla_host_t *ha;
+ struct scsi_cmnd *cmd;
+
+ cmd = sp->cmd;
+
+ /* Update entry type to indicate Command Type 3 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_A64_TYPE);
+
+ /* No data transfer */
+ if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return;
+ }
+
+ ha = sp->ha;
+
+ cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd));
+
+ /* Two DSDs are available in the Command Type 3 IOCB */
+ avail_dsds = 2;
+ cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
+
+ /* Load data segments */
+ if (cmd->use_sg != 0) {
+ struct scatterlist *cur_seg;
+ struct scatterlist *end_seg;
+
+ cur_seg = (struct scatterlist *)cmd->request_buffer;
+ end_seg = cur_seg + tot_dsds;
+ while (cur_seg < end_seg) {
+ dma_addr_t sle_dma;
+ cont_a64_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Five DSDs are available in the Continuation
+ * Type 1 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
+ cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
+ avail_dsds = 5;
+ }
+
+ sle_dma = sg_dma_address(cur_seg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
+ avail_dsds--;
+
+ cur_seg++;
+ }
+ } else {
+ dma_addr_t req_dma;
+ struct page *page;
+ unsigned long offset;
+
+ page = virt_to_page(cmd->request_buffer);
+ offset = ((unsigned long)cmd->request_buffer & ~PAGE_MASK);
+ req_dma = pci_map_page(ha->pdev, page, offset,
+ cmd->request_bufflen, cmd->sc_data_direction);
+
+ sp->dma_handle = req_dma;
+
+ *cur_dsd++ = cpu_to_le32(LSD(req_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(req_dma));
+ *cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
+ }
+}
+
+/**
+ * qla2x00_start_scsi() - Send a SCSI command to the ISP
+ * @sp: command to send to the ISP
+ *
+ * Returns non-zero if a failure occured, else zero.
+ */
+int
+qla2x00_start_scsi(srb_t *sp)
+{
+ int ret;
+ unsigned long flags;
+ scsi_qla_host_t *ha;
+ fc_lun_t *fclun;
+ struct scsi_cmnd *cmd;
+ uint32_t *clr_ptr;
+ uint32_t index;
+ uint32_t handle;
+ cmd_entry_t *cmd_pkt;
+ uint32_t timeout;
+ struct scatterlist *sg;
+ uint16_t cnt;
+ uint16_t req_cnt;
+ uint16_t tot_dsds;
+ device_reg_t __iomem *reg;
+ char tag[2];
+
+ /* Setup device pointers. */
+ ret = 0;
+ fclun = sp->lun_queue->fclun;
+ ha = fclun->fcport->ha;
+ reg = ha->iobase;
+ cmd = sp->cmd;
+
+ /* Send marker if required */
+ if (ha->marker_needed != 0) {
+ if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ return (QLA_FUNCTION_FAILED);
+ }
+ ha->marker_needed = 0;
+ }
+
+ /* Acquire ring specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Check for room in outstanding command list. */
+ handle = ha->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (ha->outstanding_cmds[handle] == 0)
+ break;
+ }
+ if (index == MAX_OUTSTANDING_COMMANDS)
+ goto queuing_error;
+
+ /* Calculate the number of request entries needed. */
+ req_cnt = (ha->calc_request_entries)(cmd->request->nr_hw_segments);
+ if (ha->req_q_cnt < (req_cnt + 2)) {
+ cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg));
+ if (ha->req_ring_index < cnt)
+ ha->req_q_cnt = cnt - ha->req_ring_index;
+ else
+ ha->req_q_cnt = ha->request_q_length -
+ (ha->req_ring_index - cnt);
+ }
+ if (ha->req_q_cnt < (req_cnt + 2))
+ goto queuing_error;
+
+ /* Finally, we have enough space, now perform mappings. */
+ tot_dsds = 0;
+ if (cmd->use_sg) {
+ sg = (struct scatterlist *) cmd->request_buffer;
+ tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg,
+ cmd->sc_data_direction);
+ if (tot_dsds == 0)
+ goto queuing_error;
+ } else if (cmd->request_bufflen) {
+ tot_dsds++;
+ }
+ req_cnt = (ha->calc_request_entries)(tot_dsds);
+
+ /* Build command packet */
+ ha->current_outstanding_cmd = handle;
+ ha->outstanding_cmds[handle] = sp;
+ sp->ha = ha;
+ sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ ha->req_q_cnt -= req_cnt;
+
+ cmd_pkt = (cmd_entry_t *)ha->request_ring_ptr;
+ cmd_pkt->handle = handle;
+ /* Zero out remaining portion of packet. */
+ clr_ptr = (uint32_t *)cmd_pkt + 2;
+ memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+ cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+ /* Set target ID */
+ SET_TARGET_ID(ha, cmd_pkt->target, fclun->fcport->loop_id);
+
+ /* Set LUN number*/
+ cmd_pkt->lun = cpu_to_le16(fclun->lun);
+
+ /* Update tagged queuing modifier */
+ cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG);
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case MSG_HEAD_TAG:
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_HEAD_TAG);
+ break;
+ case MSG_ORDERED_TAG:
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_ORDERED_TAG);
+ break;
+ }
+ }
+
+ /*
+ * Allocate at least 5 (+ QLA_CMD_TIMER_DELTA) seconds for RISC timeout.
+ */
+ timeout = (uint32_t)(cmd->timeout_per_command / HZ);
+ if (timeout > 65535)
+ cmd_pkt->timeout = __constant_cpu_to_le16(0);
+ else if (timeout > 25)
+ cmd_pkt->timeout = cpu_to_le16((uint16_t)timeout -
+ (5 + QLA_CMD_TIMER_DELTA));
+ else
+ cmd_pkt->timeout = cpu_to_le16((uint16_t)timeout);
+
+ /* Load SCSI command packet. */
+ memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len);
+ cmd_pkt->byte_count = cpu_to_le32((uint32_t)cmd->request_bufflen);
+
+ /* Build IOCB segments */
+ (ha->build_scsi_iocbs)(sp, cmd_pkt, tot_dsds);
+
+ /* Set total data segment count. */
+ cmd_pkt->entry_count = (uint8_t)req_cnt;
+ wmb();
+
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == ha->request_q_length) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ ha->actthreads++;
+ ha->total_ios++;
+ sp->lun_queue->out_cnt++;
+ sp->flags |= SRB_DMA_VALID;
+ sp->state = SRB_ACTIVE_STATE;
+ sp->u_start = jiffies;
+
+ /* Set chip new ring index. */
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return (QLA_SUCCESS);
+
+queuing_error:
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (QLA_FUNCTION_FAILED);
+}
+
+/**
+ * qla2x00_marker() - Send a marker IOCB to the firmware.
+ * @ha: HA context
+ * @loop_id: loop ID
+ * @lun: LUN
+ * @type: marker modifier
+ *
+ * Can be called from both normal and interrupt context.
+ *
+ * Returns non-zero if a failure occured, else zero.
+ */
+int
+__qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
+ uint8_t type)
+{
+ mrk_entry_t *pkt;
+
+ pkt = (mrk_entry_t *)qla2x00_req_pkt(ha);
+ if (pkt == NULL) {
+ DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
+
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ pkt->entry_type = MARKER_TYPE;
+ pkt->modifier = type;
+
+ if (type != MK_SYNC_ALL) {
+ pkt->lun = cpu_to_le16(lun);
+ SET_TARGET_ID(ha, pkt->target, loop_id);
+ }
+ wmb();
+
+ /* Issue command to ISP */
+ qla2x00_isp_cmd(ha);
+
+ return (QLA_SUCCESS);
+}
+
+int
+qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
+ uint8_t type)
+{
+ int ret;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ret = __qla2x00_marker(ha, loop_id, lun, type);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (ret);
+}
+
+/**
+ * qla2x00_req_pkt() - Retrieve a request packet from the request ring.
+ * @ha: HA context
+ *
+ * Note: The caller must hold the hardware lock before calling this routine.
+ *
+ * Returns NULL if function failed, else, a pointer to the request packet.
+ */
+static request_t *
+qla2x00_req_pkt(scsi_qla_host_t *ha)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+ request_t *pkt = NULL;
+ uint16_t cnt;
+ uint32_t *dword_ptr;
+ uint32_t timer;
+ uint16_t req_cnt = 1;
+
+ /* Wait 1 second for slot. */
+ for (timer = HZ; timer; timer--) {
+ if ((req_cnt + 2) >= ha->req_q_cnt) {
+ /* Calculate number of free request entries. */
+ cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg));
+ if (ha->req_ring_index < cnt)
+ ha->req_q_cnt = cnt - ha->req_ring_index;
+ else
+ ha->req_q_cnt = ha->request_q_length -
+ (ha->req_ring_index - cnt);
+ }
+ /* If room for request in request ring. */
+ if ((req_cnt + 2) < ha->req_q_cnt) {
+ ha->req_q_cnt--;
+ pkt = ha->request_ring_ptr;
+
+ /* Zero out packet. */
+ dword_ptr = (uint32_t *)pkt;
+ for (cnt = 0; cnt < REQUEST_ENTRY_SIZE / 4; cnt++)
+ *dword_ptr++ = 0;
+
+ /* Set system defined field. */
+ pkt->sys_define = (uint8_t)ha->req_ring_index;
+
+ /* Set entry count. */
+ pkt->entry_count = 1;
+
+ break;
+ }
+
+ /* Release ring specific lock */
+ spin_unlock(&ha->hardware_lock);
+
+ udelay(2); /* 2 us */
+
+ /* Check for pending interrupts. */
+ /* During init we issue marker directly */
+ if (!ha->marker_needed)
+ qla2x00_poll(ha);
+
+ spin_lock_irq(&ha->hardware_lock);
+ }
+ if (!pkt) {
+ DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
+ }
+
+ return (pkt);
+}
+
+/**
+ * qla2x00_isp_cmd() - Modify the request ring pointer.
+ * @ha: HA context
+ *
+ * Note: The caller must hold the hardware lock before calling this routine.
+ */
+void
+qla2x00_isp_cmd(scsi_qla_host_t *ha)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+
+ DEBUG5(printk("%s(): IOCB data:\n", __func__));
+ DEBUG5(qla2x00_dump_buffer(
+ (uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
+
+ /* Adjust ring index. */
+ ha->req_ring_index++;
+ if (ha->req_ring_index == ha->request_q_length) {
+ ha->req_ring_index = 0;
+ ha->request_ring_ptr = ha->request_ring;
+ } else
+ ha->request_ring_ptr++;
+
+ /* Set chip new ring index. */
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
new file mode 100644
index 000000000000..603d4c683c6c
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -0,0 +1,1464 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
+static void qla2x00_async_event(scsi_qla_host_t *, uint32_t);
+static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t);
+void qla2x00_process_response_queue(struct scsi_qla_host *);
+static void qla2x00_status_entry(scsi_qla_host_t *, sts_entry_t *);
+static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
+static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *);
+static void qla2x00_ms_entry(scsi_qla_host_t *, ms_iocb_entry_t *);
+
+static int qla2x00_check_sense(struct scsi_cmnd *cp, os_lun_t *);
+
+/**
+ * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
+ * @irq:
+ * @dev_id: SCSI driver HA context
+ * @regs:
+ *
+ * Called by system whenever the host adapter generates an interrupt.
+ *
+ * Returns handled flag.
+ */
+irqreturn_t
+qla2100_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ scsi_qla_host_t *ha;
+ device_reg_t __iomem *reg;
+ int status;
+ unsigned long flags;
+ unsigned long iter;
+ uint32_t mbx;
+
+ ha = (scsi_qla_host_t *) dev_id;
+ if (!ha) {
+ printk(KERN_INFO
+ "%s(): NULL host pointer\n", __func__);
+ return (IRQ_NONE);
+ }
+
+ reg = ha->iobase;
+ status = 0;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (iter = 50; iter--; ) {
+ if ((RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) == 0)
+ break;
+
+ if (RD_REG_WORD(&reg->semaphore) & BIT_0) {
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+
+ /* Get mailbox data. */
+ mbx = RD_MAILBOX_REG(ha, reg, 0);
+ if (mbx > 0x3fff && mbx < 0x8000) {
+ qla2x00_mbx_completion(ha, (uint16_t)mbx);
+ status |= MBX_INTERRUPT;
+ } else if (mbx > 0x7fff && mbx < 0xc000) {
+ qla2x00_async_event(ha, mbx);
+ } else {
+ /*EMPTY*/
+ DEBUG2(printk("scsi(%ld): Unrecognized "
+ "interrupt type (%d)\n",
+ ha->host_no, mbx));
+ }
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ RD_REG_WORD(&reg->semaphore);
+ } else {
+ qla2x00_process_response_queue(ha);
+
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ qla2x00_next(ha);
+ ha->last_irq_cpu = _smp_processor_id();
+ ha->total_isr_cnt++;
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ spin_lock_irqsave(&ha->mbx_reg_lock, flags);
+
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ up(&ha->mbx_intr_sem);
+
+ spin_unlock_irqrestore(&ha->mbx_reg_lock, flags);
+ }
+
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+
+ return (IRQ_HANDLED);
+}
+
+/**
+ * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
+ * @irq:
+ * @dev_id: SCSI driver HA context
+ * @regs:
+ *
+ * Called by system whenever the host adapter generates an interrupt.
+ *
+ * Returns handled flag.
+ */
+irqreturn_t
+qla2300_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ scsi_qla_host_t *ha;
+ device_reg_t __iomem *reg;
+ int status;
+ unsigned long flags;
+ unsigned long iter;
+ uint32_t stat;
+ uint32_t mbx;
+ uint16_t hccr;
+
+ ha = (scsi_qla_host_t *) dev_id;
+ if (!ha) {
+ printk(KERN_INFO
+ "%s(): NULL host pointer\n", __func__);
+ return (IRQ_NONE);
+ }
+
+ reg = ha->iobase;
+ status = 0;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (iter = 50; iter--; ) {
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_PAUSED) {
+ hccr = RD_REG_WORD(&reg->hccr);
+ if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
+ qla_printk(KERN_INFO, ha,
+ "Parity error -- HCCR=%x.\n", hccr);
+ else
+ qla_printk(KERN_INFO, ha,
+ "RISC paused -- HCCR=%x\n", hccr);
+
+ /*
+ * Issue a "HARD" reset in order for the RISC
+ * interrupt bit to be cleared. Schedule a big
+ * hammmer to get out of the RISC PAUSED state.
+ */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr);
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ break;
+ } else if ((stat & HSR_RISC_INT) == 0)
+ break;
+
+ mbx = MSW(stat);
+ switch (stat & 0xff) {
+ case 0x13:
+ qla2x00_process_response_queue(ha);
+ break;
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla2x00_mbx_completion(ha, (uint16_t)mbx);
+ status |= MBX_INTERRUPT;
+
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ break;
+ case 0x12:
+ qla2x00_async_event(ha, mbx);
+ break;
+ case 0x15:
+ mbx = mbx << 16 | MBA_CMPLT_1_16BIT;
+ qla2x00_async_event(ha, mbx);
+ break;
+ case 0x16:
+ mbx = mbx << 16 | MBA_SCSI_COMPLETION;
+ qla2x00_async_event(ha, mbx);
+ break;
+ default:
+ DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
+ "(%d)\n",
+ ha->host_no, stat & 0xff));
+ break;
+ }
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD_RELAXED(&reg->hccr);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ qla2x00_next(ha);
+ ha->last_irq_cpu = _smp_processor_id();
+ ha->total_isr_cnt++;
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ spin_lock_irqsave(&ha->mbx_reg_lock, flags);
+
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ up(&ha->mbx_intr_sem);
+
+ spin_unlock_irqrestore(&ha->mbx_reg_lock, flags);
+ }
+
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+
+ return (IRQ_HANDLED);
+}
+
+/**
+ * qla2x00_mbx_completion() - Process mailbox command completions.
+ * @ha: SCSI driver HA context
+ * @mb0: Mailbox0 register
+ */
+static void
+qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
+{
+ uint16_t cnt;
+ uint16_t __iomem *wptr;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ /* Load return mailbox registers. */
+ ha->flags.mbox_int = 1;
+ ha->mailbox_out[0] = mb0;
+ wptr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 1);
+
+ for (cnt = 1; cnt < ha->mbx_count; cnt++) {
+ if (IS_QLA2200(ha) && cnt == 8)
+ wptr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 8);
+ if (cnt == 4 || cnt == 5)
+ ha->mailbox_out[cnt] = qla2x00_debounce_register(wptr);
+ else
+ ha->mailbox_out[cnt] = RD_REG_WORD(wptr);
+
+ wptr++;
+ }
+
+ if (ha->mcp) {
+ DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
+ __func__, ha->host_no, ha->mcp->mb[0]));
+ } else {
+ DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
+ __func__, ha->host_no));
+ }
+}
+
+/**
+ * qla2x00_async_event() - Process aynchronous events.
+ * @ha: SCSI driver HA context
+ * @mb0: Mailbox0 register
+ */
+static void
+qla2x00_async_event(scsi_qla_host_t *ha, uint32_t mbx)
+{
+ static char *link_speeds[5] = { "1", "2", "4", "?", "10" };
+ char *link_speed;
+ uint16_t mb[4];
+ uint16_t handle_cnt;
+ uint16_t cnt;
+ uint32_t handles[5];
+ device_reg_t __iomem *reg = ha->iobase;
+ uint32_t rscn_entry, host_pid;
+ uint8_t rscn_queue_index;
+
+ /* Setup to process RIO completion. */
+ handle_cnt = 0;
+ mb[0] = LSW(mbx);
+ switch (mb[0]) {
+ case MBA_SCSI_COMPLETION:
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ handles[0] = le32_to_cpu(
+ ((uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16)) |
+ RD_MAILBOX_REG(ha, reg, 1));
+ else
+ handles[0] = le32_to_cpu(
+ ((uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16)) |
+ MSW(mbx));
+ handle_cnt = 1;
+ break;
+ case MBA_CMPLT_1_16BIT:
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
+ else
+ handles[0] = MSW(mbx);
+ handle_cnt = 1;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_2_16BIT:
+ handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
+ handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
+ handle_cnt = 2;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_3_16BIT:
+ handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
+ handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
+ handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3);
+ handle_cnt = 3;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_4_16BIT:
+ handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
+ handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
+ handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3);
+ handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
+ handle_cnt = 4;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_5_16BIT:
+ handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
+ handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
+ handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3);
+ handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
+ handles[4] = (uint32_t)RD_MAILBOX_REG(ha, reg, 7);
+ handle_cnt = 5;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_2_32BIT:
+ handles[0] = le32_to_cpu(
+ ((uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16)) |
+ RD_MAILBOX_REG(ha, reg, 1));
+ handles[1] = le32_to_cpu(
+ ((uint32_t)(RD_MAILBOX_REG(ha, reg, 7) << 16)) |
+ RD_MAILBOX_REG(ha, reg, 6));
+ handle_cnt = 2;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ default:
+ break;
+ }
+
+ switch (mb[0]) {
+ case MBA_SCSI_COMPLETION: /* Fast Post */
+ if (!ha->flags.online)
+ break;
+
+ for (cnt = 0; cnt < handle_cnt; cnt++)
+ qla2x00_process_completed_request(ha, handles[cnt]);
+ break;
+
+ case MBA_RESET: /* Reset */
+ DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no));
+
+ set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ break;
+
+ case MBA_SYSTEM_ERR: /* System Error */
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+ mb[3] = RD_MAILBOX_REG(ha, reg, 3);
+
+ qla_printk(KERN_INFO, ha,
+ "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
+ mb[1], mb[2], mb[3]);
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ qla2100_fw_dump(ha, 1);
+ else
+ qla2300_fw_dump(ha, 1);
+
+ if (mb[1] == 0) {
+ qla_printk(KERN_INFO, ha,
+ "Unrecoverable Hardware Error: adapter marked "
+ "OFFLINE!\n");
+ ha->flags.online = 0;
+ } else
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ break;
+
+ case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
+ DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ break;
+
+ case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
+ DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ break;
+
+ case MBA_WAKEUP_THRES: /* Request Queue Wake-up */
+ DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n",
+ ha->host_no));
+ break;
+
+ case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+
+ DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no,
+ mb[1]));
+ qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]);
+
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(ha);
+ }
+
+ set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
+
+ ha->flags.management_server_logged_in = 0;
+
+ /* Update AEN queue. */
+ qla2x00_enqueue_aen(ha, MBA_LIP_OCCURRED, NULL);
+
+ ha->total_lip_cnt++;
+ break;
+
+ case MBA_LOOP_UP: /* Loop Up Event */
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+
+ ha->link_data_rate = 0;
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ link_speed = link_speeds[0];
+ } else {
+ link_speed = link_speeds[3];
+ if (mb[1] < 5)
+ link_speed = link_speeds[mb[1]];
+ ha->link_data_rate = mb[1];
+ }
+
+ DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n",
+ ha->host_no, link_speed));
+ qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n",
+ link_speed);
+
+ ha->flags.management_server_logged_in = 0;
+
+ /* Update AEN queue. */
+ qla2x00_enqueue_aen(ha, MBA_LOOP_UP, NULL);
+ break;
+
+ case MBA_LOOP_DOWN: /* Loop Down Event */
+ DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN.\n",
+ ha->host_no));
+ qla_printk(KERN_INFO, ha, "LOOP DOWN detected.\n");
+
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
+ ha->device_flags |= DFLG_NO_CABLE;
+ qla2x00_mark_all_devices_lost(ha);
+ }
+
+ ha->flags.management_server_logged_in = 0;
+ ha->link_data_rate = 0;
+
+ /* Update AEN queue. */
+ qla2x00_enqueue_aen(ha, MBA_LOOP_DOWN, NULL);
+ break;
+
+ case MBA_LIP_RESET: /* LIP reset occurred */
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+
+ DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
+ ha->host_no, mb[1]));
+ qla_printk(KERN_INFO, ha,
+ "LIP reset occured (%x).\n", mb[1]);
+
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(ha);
+ }
+
+ set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+
+ ha->operating_mode = LOOP;
+ ha->flags.management_server_logged_in = 0;
+
+ /* Update AEN queue. */
+ qla2x00_enqueue_aen(ha, MBA_LIP_RESET, NULL);
+
+ ha->total_lip_cnt++;
+ break;
+
+ case MBA_POINT_TO_POINT: /* Point-to-Point */
+ if (IS_QLA2100(ha))
+ break;
+
+ DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n",
+ ha->host_no));
+
+ /*
+ * Until there's a transition from loop down to loop up, treat
+ * this as loop down only.
+ */
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&ha->loop_down_timer))
+ atomic_set(&ha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(ha);
+ }
+
+ if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
+ set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ }
+ set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
+ break;
+
+ case MBA_CHG_IN_CONNECTION: /* Change in connection mode */
+ if (IS_QLA2100(ha))
+ break;
+
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+
+ DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection "
+ "received.\n",
+ ha->host_no));
+ qla_printk(KERN_INFO, ha,
+ "Configuration change detected: value=%x.\n", mb[1]);
+
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ atomic_set(&ha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&ha->loop_down_timer))
+ atomic_set(&ha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(ha);
+ }
+
+ set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ break;
+
+ case MBA_PORT_UPDATE: /* Port database update */
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+
+ /*
+ * If a single remote port just logged into (or logged out of)
+ * us, create a new entry in our rscn fcports list and handle
+ * the event like an RSCN.
+ */
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA6312(ha) &&
+ !IS_QLA6322(ha) && ha->flags.init_done && mb[1] != 0xffff &&
+ ((ha->operating_mode == P2P && mb[1] != 0) ||
+ (ha->operating_mode != P2P && mb[1] !=
+ SNS_FIRST_LOOP_ID)) && (mb[2] == 6 || mb[2] == 7)) {
+ int rval;
+ fc_port_t *rscn_fcport;
+
+ /* Create new fcport for login. */
+ rscn_fcport = qla2x00_alloc_rscn_fcport(ha, GFP_ATOMIC);
+ if (rscn_fcport) {
+ DEBUG14(printk("scsi(%ld): Port Update -- "
+ "creating RSCN fcport %p for %x/%x.\n",
+ ha->host_no, rscn_fcport, mb[1], mb[2]));
+
+ rscn_fcport->loop_id = mb[1];
+ rscn_fcport->d_id.b24 = INVALID_PORT_ID;
+ atomic_set(&rscn_fcport->state,
+ FCS_DEVICE_LOST);
+ list_add_tail(&rscn_fcport->list,
+ &ha->rscn_fcports);
+
+ rval = qla2x00_handle_port_rscn(ha, 0,
+ rscn_fcport, 1);
+ if (rval == QLA_SUCCESS)
+ break;
+ } else {
+ DEBUG14(printk("scsi(%ld): Port Update -- "
+ "-- unable to allocate RSCN fcport "
+ "login.\n", ha->host_no));
+ }
+ }
+
+ /*
+ * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET
+ * event etc. earlier indicating loop is down) then process
+ * it. Otherwise ignore it and Wait for RSCN to come in.
+ */
+ atomic_set(&ha->loop_down_timer, 0);
+ if (atomic_read(&ha->loop_state) != LOOP_DOWN &&
+ atomic_read(&ha->loop_state) != LOOP_DEAD) {
+ DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE "
+ "ignored.\n", ha->host_no));
+ break;
+ }
+
+ DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
+ ha->host_no));
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): Port database changed %04x %04x.\n",
+ ha->host_no, mb[1], mb[2]));
+
+ /*
+ * Mark all devices as missing so we will login again.
+ */
+ atomic_set(&ha->loop_state, LOOP_UP);
+
+ qla2x00_mark_all_devices_lost(ha);
+
+ ha->flags.rscn_queue_overflow = 1;
+
+ set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+
+ /* Update AEN queue. */
+ qla2x00_enqueue_aen(ha, MBA_PORT_UPDATE, NULL);
+ break;
+
+ case MBA_RSCN_UPDATE: /* State Change Registration */
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+
+ DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
+ ha->host_no));
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): RSCN database changed -- %04x %04x.\n",
+ ha->host_no, mb[1], mb[2]));
+
+ rscn_entry = (mb[1] << 16) | mb[2];
+ host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) |
+ ha->d_id.b.al_pa;
+ if (rscn_entry == host_pid) {
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): Ignoring RSCN update to local host "
+ "port ID (%06x)\n",
+ ha->host_no, host_pid));
+ break;
+ }
+
+ rscn_queue_index = ha->rscn_in_ptr + 1;
+ if (rscn_queue_index == MAX_RSCN_COUNT)
+ rscn_queue_index = 0;
+ if (rscn_queue_index != ha->rscn_out_ptr) {
+ ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry;
+ ha->rscn_in_ptr = rscn_queue_index;
+ } else {
+ ha->flags.rscn_queue_overflow = 1;
+ }
+
+ atomic_set(&ha->loop_state, LOOP_UPDATE);
+ atomic_set(&ha->loop_down_timer, 0);
+ ha->flags.management_server_logged_in = 0;
+
+ set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ set_bit(RSCN_UPDATE, &ha->dpc_flags);
+
+ /* Update AEN queue. */
+ qla2x00_enqueue_aen(ha, MBA_RSCN_UPDATE, &mb[0]);
+ break;
+
+ /* case MBA_RIO_RESPONSE: */
+ case MBA_ZIO_RESPONSE:
+ DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n",
+ ha->host_no));
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): [R|Z]IO update completion.\n",
+ ha->host_no));
+
+ qla2x00_process_response_queue(ha);
+ break;
+ }
+}
+
+/**
+ * qla2x00_process_completed_request() - Process a Fast Post response.
+ * @ha: SCSI driver HA context
+ * @index: SRB index
+ */
+static void
+qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
+{
+ srb_t *sp;
+
+ /* Validate handle. */
+ if (index >= MAX_OUTSTANDING_COMMANDS) {
+ DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n",
+ ha->host_no, index));
+ qla_printk(KERN_WARNING, ha,
+ "Invalid SCSI completion handle %d.\n", index);
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ return;
+ }
+
+ sp = ha->outstanding_cmds[index];
+ if (sp) {
+ /* Free outstanding command slot. */
+ ha->outstanding_cmds[index] = NULL;
+
+ if (ha->actthreads)
+ ha->actthreads--;
+ sp->lun_queue->out_cnt--;
+ CMD_COMPL_STATUS(sp->cmd) = 0L;
+ CMD_SCSI_STATUS(sp->cmd) = 0L;
+
+ /* Save ISP completion status */
+ sp->cmd->result = DID_OK << 16;
+ sp->fo_retry_cnt = 0;
+ add_to_done_queue(ha, sp);
+ } else {
+ DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha,
+ "Invalid ISP SCSI completion handle\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ }
+}
+
+/**
+ * qla2x00_process_response_queue() - Process response queue entries.
+ * @ha: SCSI driver HA context
+ */
+void
+qla2x00_process_response_queue(struct scsi_qla_host *ha)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+ sts_entry_t *pkt;
+ uint16_t handle_cnt;
+ uint16_t cnt;
+
+ if (!ha->flags.online)
+ return;
+
+ while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (sts_entry_t *)ha->response_ring_ptr;
+
+ ha->rsp_ring_index++;
+ if (ha->rsp_ring_index == ha->response_q_length) {
+ ha->rsp_ring_index = 0;
+ ha->response_ring_ptr = ha->response_ring;
+ } else {
+ ha->response_ring_ptr++;
+ }
+
+ if (pkt->entry_status != 0) {
+ DEBUG3(printk(KERN_INFO
+ "scsi(%ld): Process error entry.\n", ha->host_no));
+
+ qla2x00_error_entry(ha, pkt);
+ ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
+ wmb();
+ continue;
+ }
+
+ switch (pkt->entry_type) {
+ case STATUS_TYPE:
+ qla2x00_status_entry(ha, pkt);
+ break;
+ case STATUS_TYPE_21:
+ handle_cnt = ((sts21_entry_t *)pkt)->handle_count;
+ for (cnt = 0; cnt < handle_cnt; cnt++) {
+ qla2x00_process_completed_request(ha,
+ ((sts21_entry_t *)pkt)->handle[cnt]);
+ }
+ break;
+ case STATUS_TYPE_22:
+ handle_cnt = ((sts22_entry_t *)pkt)->handle_count;
+ for (cnt = 0; cnt < handle_cnt; cnt++) {
+ qla2x00_process_completed_request(ha,
+ ((sts22_entry_t *)pkt)->handle[cnt]);
+ }
+ break;
+ case STATUS_CONT_TYPE:
+ qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt);
+ break;
+ case MS_IOCB_TYPE:
+ qla2x00_ms_entry(ha, (ms_iocb_entry_t *)pkt);
+ break;
+ case MBX_IOCB_TYPE:
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) &&
+ !IS_QLA6312(ha) && !IS_QLA6322(ha)) {
+ if (pkt->sys_define == SOURCE_ASYNC_IOCB) {
+ qla2x00_process_iodesc(ha,
+ (struct mbx_entry *)pkt);
+ } else {
+ /* MBX IOCB Type Not Supported. */
+ DEBUG4(printk(KERN_WARNING
+ "scsi(%ld): Received unknown MBX "
+ "IOCB response pkt type=%x "
+ "source=%x entry status=%x.\n",
+ ha->host_no, pkt->entry_type,
+ pkt->sys_define,
+ pkt->entry_status));
+ }
+ break;
+ }
+ /* Fallthrough. */
+ default:
+ /* Type Not Supported. */
+ DEBUG4(printk(KERN_WARNING
+ "scsi(%ld): Received unknown response pkt type %x "
+ "entry status=%x.\n",
+ ha->host_no, pkt->entry_type, pkt->entry_status));
+ break;
+ }
+ ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
+ wmb();
+ }
+
+ /* Adjust ring index */
+ WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), ha->rsp_ring_index);
+}
+
+/**
+ * qla2x00_status_entry() - Process a Status IOCB entry.
+ * @ha: SCSI driver HA context
+ * @pkt: Entry pointer
+ */
+static void
+qla2x00_status_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
+{
+ int ret;
+ unsigned b, t, l;
+ srb_t *sp;
+ os_lun_t *lq;
+ os_tgt_t *tq;
+ fc_port_t *fcport;
+ struct scsi_cmnd *cp;
+ uint16_t comp_status;
+ uint16_t scsi_status;
+ uint8_t lscsi_status;
+ int32_t resid;
+ uint8_t sense_sz = 0;
+ uint16_t rsp_info_len;
+
+ /* Fast path completion. */
+ if (le16_to_cpu(pkt->comp_status) == CS_COMPLETE &&
+ (le16_to_cpu(pkt->scsi_status) & SS_MASK) == 0) {
+ qla2x00_process_completed_request(ha, pkt->handle);
+
+ return;
+ }
+
+ /* Validate handle. */
+ if (pkt->handle < MAX_OUTSTANDING_COMMANDS) {
+ sp = ha->outstanding_cmds[pkt->handle];
+ ha->outstanding_cmds[pkt->handle] = NULL;
+ } else
+ sp = NULL;
+
+ if (sp == NULL) {
+ DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (ha->dpc_wait && !ha->dpc_active)
+ up(ha->dpc_wait);
+
+ return;
+ }
+ cp = sp->cmd;
+ if (cp == NULL) {
+ DEBUG2(printk("scsi(%ld): Command already returned back to OS "
+ "pkt->handle=%d sp=%p sp->state:%d\n",
+ ha->host_no, pkt->handle, sp, sp->state));
+ qla_printk(KERN_WARNING, ha,
+ "Command is NULL: already returned to OS (sp=%p)\n", sp);
+
+ return;
+ }
+
+ if (ha->actthreads)
+ ha->actthreads--;
+
+ if (sp->lun_queue == NULL) {
+ DEBUG2(printk("scsi(%ld): Status Entry invalid lun pointer.\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha,
+ "Status Entry invalid lun pointer.\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (ha->dpc_wait && !ha->dpc_active)
+ up(ha->dpc_wait);
+
+ return;
+ }
+
+ sp->lun_queue->out_cnt--;
+
+ comp_status = le16_to_cpu(pkt->comp_status);
+ /* Mask of reserved bits 12-15, before we examine the scsi status */
+ scsi_status = le16_to_cpu(pkt->scsi_status) & SS_MASK;
+ lscsi_status = scsi_status & STATUS_MASK;
+
+ CMD_ENTRY_STATUS(cp) = pkt->entry_status;
+ CMD_COMPL_STATUS(cp) = comp_status;
+ CMD_SCSI_STATUS(cp) = scsi_status;
+
+ /* Generate LU queue on cntrl, target, LUN */
+ b = cp->device->channel;
+ t = cp->device->id;
+ l = cp->device->lun,
+
+ tq = sp->tgt_queue;
+ lq = sp->lun_queue;
+
+ /*
+ * If loop is in transient state Report DID_BUS_BUSY
+ */
+ if ((comp_status != CS_COMPLETE || scsi_status != 0)) {
+ if (!(sp->flags & (SRB_IOCTL | SRB_TAPE)) &&
+ (atomic_read(&ha->loop_down_timer) ||
+ atomic_read(&ha->loop_state) != LOOP_READY)) {
+
+ DEBUG2(printk("scsi(%ld:%d:%d:%d): Loop Not Ready - "
+ "pid=%lx.\n",
+ ha->host_no, b, t, l, cp->serial_number));
+
+ qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT);
+ add_to_retry_queue(ha, sp);
+ return;
+ }
+ }
+
+ /* Check for any FCP transport errors. */
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+ rsp_info_len = le16_to_cpu(pkt->rsp_info_len);
+ if (rsp_info_len > 3 && pkt->rsp_info[3]) {
+ DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol "
+ "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..."
+ "retrying command\n", ha->host_no, b, t, l,
+ rsp_info_len, pkt->rsp_info[0], pkt->rsp_info[1],
+ pkt->rsp_info[2], pkt->rsp_info[3],
+ pkt->rsp_info[4], pkt->rsp_info[5],
+ pkt->rsp_info[6], pkt->rsp_info[7]));
+
+ cp->result = DID_BUS_BUSY << 16;
+ add_to_done_queue(ha, sp);
+ return;
+ }
+ }
+
+ /*
+ * Based on Host and scsi status generate status code for Linux
+ */
+ switch (comp_status) {
+ case CS_COMPLETE:
+ if (scsi_status == 0) {
+ cp->result = DID_OK << 16;
+ break;
+ }
+ if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) {
+ resid = le32_to_cpu(pkt->residual_length);
+ cp->resid = resid;
+ CMD_RESID_LEN(cp) = resid;
+ }
+ if (lscsi_status == SS_BUSY_CONDITION) {
+ cp->result = DID_BUS_BUSY << 16 | lscsi_status;
+ break;
+ }
+
+ cp->result = DID_OK << 16 | lscsi_status;
+
+ if (lscsi_status != SS_CHECK_CONDITION)
+ break;
+
+ /*
+ * Copy Sense Data into sense buffer
+ */
+ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer));
+
+ if (!(scsi_status & SS_SENSE_LEN_VALID))
+ break;
+
+ if (le16_to_cpu(pkt->req_sense_length) <
+ sizeof(cp->sense_buffer))
+ sense_sz = le16_to_cpu(pkt->req_sense_length);
+ else
+ sense_sz = sizeof(cp->sense_buffer);
+
+ CMD_ACTUAL_SNSLEN(cp) = sense_sz;
+ sp->request_sense_length = sense_sz;
+ sp->request_sense_ptr = cp->sense_buffer;
+
+ if (sp->request_sense_length > 32)
+ sense_sz = 32;
+
+ memcpy(cp->sense_buffer, pkt->req_sense_data, sense_sz);
+
+ sp->request_sense_ptr += sense_sz;
+ sp->request_sense_length -= sense_sz;
+ if (sp->request_sense_length != 0)
+ ha->status_srb = sp;
+
+ if (!(sp->flags & (SRB_IOCTL | SRB_TAPE)) &&
+ qla2x00_check_sense(cp, lq) == QLA_SUCCESS) {
+ /* Throw away status_cont if any */
+ ha->status_srb = NULL;
+ add_to_scsi_retry_queue(ha, sp);
+ return;
+ }
+
+ DEBUG5(printk("%s(): Check condition Sense data, "
+ "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n",
+ __func__, ha->host_no, b, t, l, cp,
+ cp->serial_number));
+ if (sense_sz)
+ DEBUG5(qla2x00_dump_buffer(cp->sense_buffer,
+ CMD_ACTUAL_SNSLEN(cp)));
+ break;
+
+ case CS_DATA_UNDERRUN:
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x.\n",
+ ha->host_no, t, l, comp_status, scsi_status));
+
+ resid = le32_to_cpu(pkt->residual_length);
+ if (scsi_status & SS_RESIDUAL_UNDER) {
+ cp->resid = resid;
+ CMD_RESID_LEN(cp) = resid;
+ }
+
+ /*
+ * Check to see if SCSI Status is non zero. If so report SCSI
+ * Status.
+ */
+ if (lscsi_status != 0) {
+ if (lscsi_status == SS_BUSY_CONDITION) {
+ cp->result = DID_BUS_BUSY << 16 |
+ lscsi_status;
+ break;
+ }
+
+ cp->result = DID_OK << 16 | lscsi_status;
+
+ if (lscsi_status != SS_CHECK_CONDITION)
+ break;
+
+ /* Copy Sense Data into sense buffer */
+ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer));
+
+ if (!(scsi_status & SS_SENSE_LEN_VALID))
+ break;
+
+ if (le16_to_cpu(pkt->req_sense_length) <
+ sizeof(cp->sense_buffer))
+ sense_sz = le16_to_cpu(pkt->req_sense_length);
+ else
+ sense_sz = sizeof(cp->sense_buffer);
+
+ CMD_ACTUAL_SNSLEN(cp) = sense_sz;
+ sp->request_sense_length = sense_sz;
+ sp->request_sense_ptr = cp->sense_buffer;
+
+ if (sp->request_sense_length > 32)
+ sense_sz = 32;
+
+ memcpy(cp->sense_buffer, pkt->req_sense_data, sense_sz);
+
+ sp->request_sense_ptr += sense_sz;
+ sp->request_sense_length -= sense_sz;
+ if (sp->request_sense_length != 0)
+ ha->status_srb = sp;
+
+ if (!(sp->flags & (SRB_IOCTL | SRB_TAPE)) &&
+ (qla2x00_check_sense(cp, lq) == QLA_SUCCESS)) {
+ ha->status_srb = NULL;
+ add_to_scsi_retry_queue(ha, sp);
+ return;
+ }
+ DEBUG5(printk("%s(): Check condition Sense data, "
+ "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n",
+ __func__, ha->host_no, b, t, l, cp,
+ cp->serial_number));
+ if (sense_sz)
+ DEBUG5(qla2x00_dump_buffer(cp->sense_buffer,
+ CMD_ACTUAL_SNSLEN(cp)));
+ } else {
+ /*
+ * If RISC reports underrun and target does not report
+ * it then we must have a lost frame, so tell upper
+ * layer to retry it by reporting a bus busy.
+ */
+ if (!(scsi_status & SS_RESIDUAL_UNDER)) {
+ DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
+ "frame(s) detected (%x of %x bytes)..."
+ "retrying command.\n",
+ ha->host_no, b, t, l, resid,
+ cp->request_bufflen));
+
+ cp->result = DID_BUS_BUSY << 16;
+ ha->dropped_frame_error_cnt++;
+ break;
+ }
+
+ /* Handle mid-layer underflow */
+ if ((unsigned)(cp->request_bufflen - resid) <
+ cp->underflow) {
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): Mid-layer underflow "
+ "detected (%x of %x bytes)...returning "
+ "error status.\n",
+ ha->host_no, b, t, l, resid,
+ cp->request_bufflen);
+
+ cp->result = DID_ERROR << 16;
+ break;
+ }
+
+ /* Everybody online, looking good... */
+ cp->result = DID_OK << 16;
+ }
+ break;
+
+ case CS_DATA_OVERRUN:
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n",
+ ha->host_no, t, l, comp_status, scsi_status));
+ DEBUG2(printk(KERN_INFO
+ "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3],
+ cp->cmnd[4], cp->cmnd[5]));
+ DEBUG2(printk(KERN_INFO
+ "PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR "
+ "status!\n",
+ cp->serial_number, cp->request_bufflen,
+ le32_to_cpu(pkt->residual_length)));
+
+ cp->result = DID_ERROR << 16;
+ break;
+
+ case CS_PORT_LOGGED_OUT:
+ case CS_PORT_CONFIG_CHG:
+ case CS_PORT_BUSY:
+ case CS_INCOMPLETE:
+ case CS_PORT_UNAVAILABLE:
+ /*
+ * If the port is in Target Down state, return all IOs for this
+ * Target with DID_NO_CONNECT ELSE Queue the IOs in the
+ * retry_queue.
+ */
+ fcport = sp->fclun->fcport;
+ DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down "
+ "pid=%ld, compl status=0x%x, port state=0x%x\n",
+ ha->host_no, t, l, cp->serial_number, comp_status,
+ atomic_read(&fcport->state)));
+
+ if ((sp->flags & (SRB_IOCTL | SRB_TAPE)) ||
+ atomic_read(&fcport->state) == FCS_DEVICE_DEAD) {
+ cp->result = DID_NO_CONNECT << 16;
+ if (atomic_read(&ha->loop_state) == LOOP_DOWN)
+ sp->err_id = SRB_ERR_LOOP;
+ else
+ sp->err_id = SRB_ERR_PORT;
+ add_to_done_queue(ha, sp);
+ } else {
+ qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT);
+ add_to_retry_queue(ha, sp);
+ }
+
+ if (atomic_read(&fcport->state) == FCS_ONLINE) {
+ qla2x00_mark_device_lost(ha, fcport, 1);
+ }
+
+ return;
+ break;
+
+ case CS_RESET:
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld): RESET status detected 0x%x-0x%x.\n",
+ ha->host_no, comp_status, scsi_status));
+
+ if (sp->flags & (SRB_IOCTL | SRB_TAPE)) {
+ cp->result = DID_RESET << 16;
+ } else {
+ qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT);
+ add_to_retry_queue(ha, sp);
+ return;
+ }
+ break;
+
+ case CS_ABORTED:
+ /*
+ * hv2.19.12 - DID_ABORT does not retry the request if we
+ * aborted this request then abort otherwise it must be a
+ * reset.
+ */
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld): ABORT status detected 0x%x-0x%x.\n",
+ ha->host_no, comp_status, scsi_status));
+
+ cp->result = DID_RESET << 16;
+ break;
+
+ case CS_TIMEOUT:
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x "
+ "sflags=%x.\n", ha->host_no, b, t, l, comp_status,
+ scsi_status, le16_to_cpu(pkt->status_flags)));
+
+ cp->result = DID_BUS_BUSY << 16;
+
+ fcport = lq->fclun->fcport;
+
+ /* Check to see if logout occurred */
+ if ((le16_to_cpu(pkt->status_flags) & SF_LOGOUT_SENT)) {
+ qla2x00_mark_device_lost(ha, fcport, 1);
+ }
+ break;
+
+ case CS_QUEUE_FULL:
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld): QUEUE FULL status detected 0x%x-0x%x.\n",
+ ha->host_no, comp_status, scsi_status));
+
+ /* SCSI Mid-Layer handles device queue full */
+
+ cp->result = DID_OK << 16 | lscsi_status;
+
+ /* TODO: ??? */
+ /* Adjust queue depth */
+ ret = scsi_track_queue_full(cp->device,
+ sp->lun_queue->out_cnt - 1);
+ if (ret) {
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): Queue depth adjusted to %d.\n",
+ ha->host_no, cp->device->channel, cp->device->id,
+ cp->device->lun, ret);
+ }
+ break;
+
+ default:
+ DEBUG3(printk("scsi(%ld): Error detected (unknown status) "
+ "0x%x-0x%x.\n",
+ ha->host_no, comp_status, scsi_status));
+ qla_printk(KERN_INFO, ha,
+ "Unknown status detected 0x%x-0x%x.\n",
+ comp_status, scsi_status);
+
+ cp->result = DID_ERROR << 16;
+ break;
+ }
+
+ /* Place command on done queue. */
+ if (ha->status_srb == NULL)
+ add_to_done_queue(ha, sp);
+}
+
+/**
+ * qla2x00_status_cont_entry() - Process a Status Continuations entry.
+ * @ha: SCSI driver HA context
+ * @pkt: Entry pointer
+ *
+ * Extended sense data.
+ */
+static void
+qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
+{
+ uint8_t sense_sz = 0;
+ srb_t *sp = ha->status_srb;
+ struct scsi_cmnd *cp;
+
+ if (sp != NULL && sp->request_sense_length != 0) {
+ cp = sp->cmd;
+ if (cp == NULL) {
+ DEBUG2(printk("%s(): Cmd already returned back to OS "
+ "sp=%p sp->state:%d\n", __func__, sp, sp->state));
+ qla_printk(KERN_INFO, ha,
+ "cmd is NULL: already returned to OS (sp=%p)\n",
+ sp);
+
+ ha->status_srb = NULL;
+ return;
+ }
+
+ if (sp->request_sense_length > sizeof(pkt->data)) {
+ sense_sz = sizeof(pkt->data);
+ } else {
+ sense_sz = sp->request_sense_length;
+ }
+
+ /* Move sense data. */
+ memcpy(sp->request_sense_ptr, pkt->data, sense_sz);
+ DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz));
+
+ sp->request_sense_ptr += sense_sz;
+ sp->request_sense_length -= sense_sz;
+
+ /* Place command on done queue. */
+ if (sp->request_sense_length == 0) {
+ add_to_done_queue(ha, sp);
+ ha->status_srb = NULL;
+ }
+ }
+}
+
+/**
+ * qla2x00_error_entry() - Process an error entry.
+ * @ha: SCSI driver HA context
+ * @pkt: Entry pointer
+ */
+static void
+qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
+{
+ srb_t *sp;
+
+#if defined(QL_DEBUG_LEVEL_2)
+ if (pkt->entry_status & RF_INV_E_ORDER)
+ qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
+ else if (pkt->entry_status & RF_INV_E_COUNT)
+ qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__);
+ else if (pkt->entry_status & RF_INV_E_PARAM)
+ qla_printk(KERN_ERR, ha,
+ "%s: Invalid Entry Parameter\n", __func__);
+ else if (pkt->entry_status & RF_INV_E_TYPE)
+ qla_printk(KERN_ERR, ha, "%s: Invalid Entry Type\n", __func__);
+ else if (pkt->entry_status & RF_BUSY)
+ qla_printk(KERN_ERR, ha, "%s: Busy\n", __func__);
+ else
+ qla_printk(KERN_ERR, ha, "%s: UNKNOWN flag error\n", __func__);
+#endif
+
+ /* Validate handle. */
+ if (pkt->handle < MAX_OUTSTANDING_COMMANDS)
+ sp = ha->outstanding_cmds[pkt->handle];
+ else
+ sp = NULL;
+
+ if (sp) {
+ /* Free outstanding command slot. */
+ ha->outstanding_cmds[pkt->handle] = NULL;
+ if (ha->actthreads)
+ ha->actthreads--;
+ sp->lun_queue->out_cnt--;
+
+ /* Bad payload or header */
+ if (pkt->entry_status &
+ (RF_INV_E_ORDER | RF_INV_E_COUNT |
+ RF_INV_E_PARAM | RF_INV_E_TYPE)) {
+ sp->cmd->result = DID_ERROR << 16;
+ } else if (pkt->entry_status & RF_BUSY) {
+ sp->cmd->result = DID_BUS_BUSY << 16;
+ } else {
+ sp->cmd->result = DID_ERROR << 16;
+ }
+ /* Place command on done queue. */
+ add_to_done_queue(ha, sp);
+
+ } else if (pkt->entry_type == COMMAND_A64_TYPE ||
+ pkt->entry_type == COMMAND_TYPE) {
+ DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha,
+ "Error entry - invalid handle\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (ha->dpc_wait && !ha->dpc_active)
+ up(ha->dpc_wait);
+ }
+}
+
+/**
+ * qla2x00_ms_entry() - Process a Management Server entry.
+ * @ha: SCSI driver HA context
+ * @index: Response queue out pointer
+ */
+static void
+qla2x00_ms_entry(scsi_qla_host_t *ha, ms_iocb_entry_t *pkt)
+{
+ srb_t *sp;
+
+ DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n",
+ __func__, ha->host_no, pkt, pkt->handle1));
+
+ /* Validate handle. */
+ if (pkt->handle1 < MAX_OUTSTANDING_COMMANDS)
+ sp = ha->outstanding_cmds[pkt->handle1];
+ else
+ sp = NULL;
+
+ if (sp == NULL) {
+ DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha, "MS entry - invalid handle\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ return;
+ }
+
+ CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->status);
+ CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status;
+
+ /* Free outstanding command slot. */
+ ha->outstanding_cmds[pkt->handle1] = NULL;
+
+ add_to_done_queue(ha, sp);
+}
+
+/**
+ * qla2x00_check_sense() - Perform any sense data interrogation.
+ * @cp: SCSI Command
+ * @lq: Lun queue
+ *
+ * Returns QLA_SUCCESS if the lun queue is suspended, else
+ * QLA_FUNCTION_FAILED (lun queue not suspended).
+ */
+static int
+qla2x00_check_sense(struct scsi_cmnd *cp, os_lun_t *lq)
+{
+ scsi_qla_host_t *ha;
+ srb_t *sp;
+ fc_port_t *fcport;
+
+ ha = (scsi_qla_host_t *) cp->device->host->hostdata;
+ if ((cp->sense_buffer[0] & 0x70) != 0x70) {
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ sp = (srb_t * )CMD_SP(cp);
+ sp->flags |= SRB_GOT_SENSE;
+
+ switch (cp->sense_buffer[2] & 0xf) {
+ case RECOVERED_ERROR:
+ cp->result = DID_OK << 16;
+ cp->sense_buffer[0] = 0;
+ break;
+
+ case NOT_READY:
+ fcport = lq->fclun->fcport;
+
+ /*
+ * Suspend the lun only for hard disk device type.
+ */
+ if ((fcport->flags & FCF_TAPE_PRESENT) == 0 &&
+ lq->q_state != LUN_STATE_TIMEOUT) {
+ /*
+ * If target is in process of being ready then suspend
+ * lun for 6 secs and retry all the commands.
+ */
+ if (cp->sense_buffer[12] == 0x4 &&
+ cp->sense_buffer[13] == 0x1) {
+
+ /* Suspend the lun for 6 secs */
+ qla2x00_suspend_lun(ha, lq, 6,
+ ql2xsuspendcount);
+
+ return (QLA_SUCCESS);
+ }
+ }
+ break;
+ }
+
+ return (QLA_FUNCTION_FAILED);
+}
diff --git a/drivers/scsi/qla2xxx/qla_listops.h b/drivers/scsi/qla2xxx/qla_listops.h
new file mode 100644
index 000000000000..5da034f61afa
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_listops.h
@@ -0,0 +1,351 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+/* Management functions for various lists */
+
+/* __add_to_done_queue()
+ *
+ * Place SRB command on done queue.
+ *
+ * Input:
+ * ha = host pointer
+ * sp = srb pointer.
+ * Locking:
+ * this function assumes the ha->list_lock is already taken
+ */
+static inline void
+__add_to_done_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ /*
+ if (sp->state != SRB_NO_QUEUE_STATE &&
+ sp->state != SRB_ACTIVE_STATE)
+ BUG();
+ */
+
+ /* Place block on done queue */
+ sp->cmd->host_scribble = (unsigned char *) NULL;
+ sp->state = SRB_DONE_STATE;
+ list_add_tail(&sp->list,&ha->done_queue);
+ ha->done_q_cnt++;
+ sp->ha = ha;
+}
+
+static inline void
+__add_to_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ /*
+ if( sp->state != SRB_NO_QUEUE_STATE &&
+ sp->state != SRB_ACTIVE_STATE)
+ BUG();
+ */
+
+ /* Place block on retry queue */
+ list_add_tail(&sp->list,&ha->retry_queue);
+ ha->retry_q_cnt++;
+ sp->flags |= SRB_WATCHDOG;
+ sp->state = SRB_RETRY_STATE;
+ sp->ha = ha;
+}
+
+static inline void
+__add_to_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ /*
+ if( sp->state != SRB_NO_QUEUE_STATE &&
+ sp->state != SRB_ACTIVE_STATE)
+ BUG();
+ */
+
+ /* Place block on retry queue */
+ list_add_tail(&sp->list,&ha->scsi_retry_queue);
+ ha->scsi_retry_q_cnt++;
+ sp->state = SRB_SCSI_RETRY_STATE;
+ sp->ha = ha;
+}
+
+static inline void
+add_to_done_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+ __add_to_done_queue(ha,sp);
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+static inline void
+add_to_free_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ mempool_free(sp, ha->srb_mempool);
+}
+
+static inline void
+add_to_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+ __add_to_retry_queue(ha,sp);
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+static inline void
+add_to_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+ __add_to_scsi_retry_queue(ha,sp);
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+/*
+ * __del_from_retry_queue
+ * Function used to remove a command block from the
+ * watchdog timer queue.
+ *
+ * Note: Must insure that command is on watchdog
+ * list before calling del_from_retry_queue
+ * if (sp->flags & SRB_WATCHDOG)
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = srb pointer.
+ * Locking:
+ * this function assumes the list_lock is already taken
+ */
+static inline void
+__del_from_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ list_del_init(&sp->list);
+
+ sp->flags &= ~(SRB_WATCHDOG | SRB_BUSY);
+ sp->state = SRB_NO_QUEUE_STATE;
+ ha->retry_q_cnt--;
+}
+
+/*
+ * __del_from_scsi_retry_queue
+ * Function used to remove a command block from the
+ * scsi retry queue.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = srb pointer.
+ * Locking:
+ * this function assumes the list_lock is already taken
+ */
+static inline void
+__del_from_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ list_del_init(&sp->list);
+
+ ha->scsi_retry_q_cnt--;
+ sp->state = SRB_NO_QUEUE_STATE;
+}
+
+/*
+ * del_from_retry_queue
+ * Function used to remove a command block from the
+ * watchdog timer queue.
+ *
+ * Note: Must insure that command is on watchdog
+ * list before calling del_from_retry_queue
+ * if (sp->flags & SRB_WATCHDOG)
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = srb pointer.
+ * Locking:
+ * this function takes and releases the list_lock
+ */
+static inline void
+del_from_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ /* if (unlikely(!(sp->flags & SRB_WATCHDOG)))
+ BUG();*/
+ spin_lock_irqsave(&ha->list_lock, flags);
+
+ /* if (unlikely(list_empty(&ha->retry_queue)))
+ BUG();*/
+
+ __del_from_retry_queue(ha,sp);
+
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+/*
+ * del_from_scsi_retry_queue
+ * Function used to remove a command block from the
+ * scsi retry queue.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = srb pointer.
+ * Locking:
+ * this function takes and releases the list_lock
+ */
+static inline void
+del_from_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+
+ /* if (unlikely(list_empty(&ha->scsi_retry_queue)))
+ BUG();*/
+
+ __del_from_scsi_retry_queue(ha,sp);
+
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+/*
+ * __add_to_pending_queue
+ * Add the standard SCB job to the bottom of standard SCB commands.
+ *
+ * Input:
+ * COMPLETE!!!
+ * q = SCSI LU pointer.
+ * sp = srb pointer.
+ * SCSI_LU_Q lock must be already obtained.
+ */
+static inline int
+__add_to_pending_queue(struct scsi_qla_host *ha, srb_t * sp)
+{
+ int empty;
+ /*
+ if( sp->state != SRB_NO_QUEUE_STATE &&
+ sp->state != SRB_FREE_STATE &&
+ sp->state != SRB_ACTIVE_STATE)
+ BUG();
+ */
+
+ empty = list_empty(&ha->pending_queue);
+ list_add_tail(&sp->list, &ha->pending_queue);
+ ha->qthreads++;
+ sp->state = SRB_PENDING_STATE;
+
+ return (empty);
+}
+
+static inline void
+__add_to_pending_queue_head(struct scsi_qla_host *ha, srb_t * sp)
+{
+ /*
+ if( sp->state != SRB_NO_QUEUE_STATE &&
+ sp->state != SRB_FREE_STATE &&
+ sp->state != SRB_ACTIVE_STATE)
+ BUG();
+ */
+
+ list_add(&sp->list, &ha->pending_queue);
+ ha->qthreads++;
+ sp->state = SRB_PENDING_STATE;
+}
+
+static inline int
+add_to_pending_queue(struct scsi_qla_host *ha, srb_t *sp)
+{
+ int empty;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+ empty = __add_to_pending_queue(ha, sp);
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+
+ return (empty);
+}
+static inline void
+add_to_pending_queue_head(struct scsi_qla_host *ha, srb_t *sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+ __add_to_pending_queue_head(ha, sp);
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+static inline void
+__del_from_pending_queue(struct scsi_qla_host *ha, srb_t *sp)
+{
+ list_del_init(&sp->list);
+ ha->qthreads--;
+ sp->state = SRB_NO_QUEUE_STATE;
+}
+
+/*
+ * Failover Stuff.
+ */
+static inline void
+__add_to_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ /*
+ if( sp->state != SRB_NO_QUEUE_STATE &&
+ sp->state != SRB_ACTIVE_STATE)
+ BUG();
+ */
+
+ list_add_tail(&sp->list,&ha->failover_queue);
+ ha->failover_cnt++;
+ sp->state = SRB_FAILOVER_STATE;
+ sp->ha = ha;
+}
+
+static inline void add_to_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+
+ __add_to_failover_queue(ha,sp);
+
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+static inline void __del_from_failover_queue(struct scsi_qla_host * ha, srb_t *
+ sp)
+{
+ ha->failover_cnt--;
+ list_del_init(&sp->list);
+ sp->state = SRB_NO_QUEUE_STATE;
+}
+
+static inline void del_from_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+
+ __del_from_failover_queue(ha,sp);
+
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+static inline void
+del_from_pending_queue(struct scsi_qla_host * ha, srb_t * sp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+
+ __del_from_pending_queue(ha,sp);
+
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
new file mode 100644
index 000000000000..c04fbcd75235
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -0,0 +1,1950 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+#include <linux/delay.h>
+
+static void
+qla2x00_mbx_sem_timeout(unsigned long data)
+{
+ struct semaphore *sem_ptr = (struct semaphore *)data;
+
+ DEBUG11(printk("qla2x00_sem_timeout: entered.\n");)
+
+ if (sem_ptr != NULL) {
+ up(sem_ptr);
+ }
+
+ DEBUG11(printk("qla2x00_mbx_sem_timeout: exiting.\n");)
+}
+
+/*
+ * qla2x00_mailbox_command
+ * Issue mailbox command and waits for completion.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * mcp = driver internal mbx struct pointer.
+ *
+ * Output:
+ * mb[MAX_MAILBOX_REGISTER_COUNT] = returned mailbox data.
+ *
+ * Returns:
+ * 0 : QLA_SUCCESS = cmd performed success
+ * 1 : QLA_FUNCTION_FAILED (error encountered)
+ * 6 : QLA_FUNCTION_TIMEOUT (timeout condition encountered)
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp)
+{
+ int rval;
+ unsigned long flags = 0;
+ device_reg_t __iomem *reg = ha->iobase;
+ struct timer_list tmp_intr_timer;
+ uint8_t abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ uint8_t io_lock_on = ha->flags.init_done;
+ uint16_t command;
+ uint16_t *iptr;
+ uint16_t __iomem *optr;
+ uint32_t cnt;
+ uint32_t mboxes;
+ unsigned long mbx_flags = 0;
+ unsigned long wait_time;
+
+ rval = QLA_SUCCESS;
+
+ DEBUG11(printk("qla2x00_mailbox_command(%ld): entered.\n",
+ ha->host_no);)
+ /*
+ * Wait for active mailbox commands to finish by waiting at most
+ * tov seconds. This is to serialize actual issuing of mailbox cmds
+ * during non ISP abort time.
+ */
+ if (!abort_active) {
+ if (qla2x00_down_timeout(&ha->mbx_cmd_sem, mcp->tov * HZ)) {
+ /* Timeout occurred. Return error. */
+ DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): cmd "
+ "access timeout. Exiting.\n", ha->host_no);)
+ return QLA_FUNCTION_TIMEOUT;
+ }
+ }
+
+ ha->flags.mbox_busy = 1;
+ /* Save mailbox command for debug */
+ ha->mcp = mcp;
+
+ /* Try to get mailbox register access */
+ if (!abort_active)
+ spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags);
+
+ DEBUG11(printk("scsi%d: prepare to issue mbox cmd=0x%x.\n",
+ (int)ha->host_no, mcp->mb[0]);)
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Load mailbox registers. */
+ optr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 0);
+
+ iptr = mcp->mb;
+ command = mcp->mb[0];
+ mboxes = mcp->out_mb;
+
+ for (cnt = 0; cnt < ha->mbx_count; cnt++) {
+ if (IS_QLA2200(ha) && cnt == 8)
+ optr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 8);
+ if (mboxes & BIT_0)
+ WRT_REG_WORD(optr, *iptr);
+
+ mboxes >>= 1;
+ optr++;
+ iptr++;
+ }
+
+#if defined(QL_DEBUG_LEVEL_1)
+ printk("qla2x00_mailbox_command: Loaded MBX registers "
+ "(displayed in bytes) = \n");
+ qla2x00_dump_buffer((uint8_t *)mcp->mb, 16);
+ printk("\n");
+ qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16);
+ printk("\n");
+ qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8);
+ printk("\n");
+ printk("qla2x00_mailbox_command: I/O address = %lx.\n",
+ (u_long)optr);
+ qla2x00_dump_regs(ha);
+#endif
+
+ /* Issue set host interrupt command to send cmd out. */
+ ha->flags.mbox_int = 0;
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ /* Unlock mbx registers and wait for interrupt */
+
+ DEBUG11(printk("qla2x00_mailbox_command: going to unlock irq & "
+ "waiting for interrupt. jiffies=%lx.\n", jiffies);)
+
+ /* Wait for mbx cmd completion until timeout */
+
+ if (!abort_active && io_lock_on) {
+ /* sleep on completion semaphore */
+ DEBUG11(printk("qla2x00_mailbox_command(%ld): "
+ "INTERRUPT MODE. Initializing timer.\n",
+ ha->host_no);)
+
+ init_timer(&tmp_intr_timer);
+ tmp_intr_timer.data = (unsigned long)&ha->mbx_intr_sem;
+ tmp_intr_timer.expires = jiffies + mcp->tov * HZ;
+ tmp_intr_timer.function =
+ (void (*)(unsigned long))qla2x00_mbx_sem_timeout;
+
+ DEBUG11(printk("qla2x00_mailbox_command(%ld): "
+ "Adding timer.\n", ha->host_no);)
+ add_timer(&tmp_intr_timer);
+
+ DEBUG11(printk("qla2x00_mailbox_command: going to "
+ "unlock & sleep. time=0x%lx.\n", jiffies);)
+
+ set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
+
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (!abort_active)
+ spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
+
+ /* Wait for either the timer to expire
+ * or the mbox completion interrupt
+ */
+ down(&ha->mbx_intr_sem);
+
+ DEBUG11(printk("qla2x00_mailbox_command:"
+ "waking up."
+ "time=0x%lx\n", jiffies);)
+ clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
+
+ /* delete the timer */
+ del_timer(&tmp_intr_timer);
+ } else {
+
+ DEBUG3_11(printk("qla2x00_mailbox_command(%ld): cmd=%x "
+ "POLLING MODE.\n", ha->host_no, command);)
+
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (!abort_active)
+ spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
+
+ wait_time = jiffies + mcp->tov * HZ; /* wait at most tov secs */
+ while (!ha->flags.mbox_int) {
+ if (time_after(jiffies, wait_time))
+ break;
+
+ /* Check for pending interrupts. */
+ qla2x00_poll(ha);
+
+ udelay(10); /* v4.27 */
+ } /* while */
+ }
+
+ if (!abort_active)
+ spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags);
+
+ /* Check whether we timed out */
+ if (ha->flags.mbox_int) {
+ uint16_t *iptr2;
+
+ DEBUG3_11(printk("qla2x00_mailbox_cmd: cmd %x completed.\n",
+ command);)
+
+ /* Got interrupt. Clear the flag. */
+ ha->flags.mbox_int = 0;
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ if (ha->mailbox_out[0] != MBS_COMMAND_COMPLETE) {
+ qla2x00_stats.mboxerr++;
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ /* Load return mailbox registers. */
+ iptr2 = mcp->mb;
+ iptr = (uint16_t *)&ha->mailbox_out[0];
+ mboxes = mcp->in_mb;
+ for (cnt = 0; cnt < ha->mbx_count; cnt++) {
+ if (mboxes & BIT_0)
+ *iptr2 = *iptr;
+
+ mboxes >>= 1;
+ iptr2++;
+ iptr++;
+ }
+ } else {
+
+#if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) || \
+ defined(QL_DEBUG_LEVEL_11)
+ printk("qla2x00_mailbox_command(%ld): **** MB Command Timeout "
+ "for cmd %x ****\n", ha->host_no, command);
+ printk("qla2x00_mailbox_command: icontrol=%x jiffies=%lx\n",
+ RD_REG_WORD(&reg->ictrl), jiffies);
+ printk("qla2x00_mailbox_command: *** mailbox[0] = 0x%x ***\n",
+ RD_REG_WORD(optr));
+ qla2x00_dump_regs(ha);
+#endif
+
+ qla2x00_stats.mboxtout++;
+ ha->total_mbx_timeout++;
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+
+ if (!abort_active)
+ spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
+
+ ha->flags.mbox_busy = 0;
+
+ /* Clean up */
+ ha->mcp = NULL;
+
+ if (!abort_active) {
+ DEBUG11(printk("qla2x00_mailbox_cmd: checking for additional "
+ "resp interrupt.\n");)
+
+ /* polling mode for non isp_abort commands. */
+ qla2x00_poll(ha);
+ }
+
+ if (rval == QLA_FUNCTION_TIMEOUT) {
+ if (!io_lock_on || (mcp->flags & IOCTL_CMD)) {
+ /* not in dpc. schedule it for dpc to take over. */
+ DEBUG(printk("qla2x00_mailbox_command(%ld): timeout "
+ "schedule isp_abort_needed.\n",
+ ha->host_no);)
+ DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): "
+ "timeout schedule isp_abort_needed.\n",
+ ha->host_no);)
+ qla_printk(KERN_WARNING, ha,
+ "Mailbox command timeout occured. Scheduling ISP "
+ "abort.\n");
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (ha->dpc_wait && !ha->dpc_active)
+ up(ha->dpc_wait);
+
+ } else if (!abort_active) {
+
+ /* call abort directly since we are in the DPC thread */
+ DEBUG(printk("qla2x00_mailbox_command(%ld): timeout "
+ "calling abort_isp\n", ha->host_no);)
+ DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): "
+ "timeout calling abort_isp\n", ha->host_no);)
+ qla_printk(KERN_WARNING, ha,
+ "Mailbox command timeout occured. Issuing ISP "
+ "abort.\n");
+
+ set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (qla2x00_abort_isp(ha)) {
+ /* failed. retry later. */
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+
+ DEBUG(printk("qla2x00_mailbox_command: finished "
+ "abort_isp\n");)
+ DEBUG2_3_11(printk("qla2x00_mailbox_command: finished "
+ "abort_isp\n");)
+ }
+ }
+
+ /* Allow next mbx cmd to come in. */
+ if (!abort_active)
+ up(&ha->mbx_cmd_sem);
+
+ if (rval) {
+ DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): **** FAILED. "
+ "mbx0=%x, mbx1=%x, mbx2=%x, cmd=%x ****\n",
+ ha->host_no, mcp->mb[0], mcp->mb[1], mcp->mb[2], command);)
+ } else {
+ DEBUG11(printk("qla2x00_mailbox_command(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ DEBUG11(printk("qla2x00_mailbox_command(%ld): exiting.\n",
+ ha->host_no);)
+
+ return rval;
+}
+
+/*
+ * qla2x00_load_ram
+ * Load adapter RAM using DMA.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint16_t risc_addr,
+ uint16_t risc_code_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint32_t req_len;
+ dma_addr_t nml_dma;
+ uint32_t nml_len;
+ uint32_t normalized;
+
+ DEBUG11(printk("qla2x00_load_ram(%ld): entered.\n",
+ ha->host_no);)
+
+ req_len = risc_code_size;
+ nml_dma = 0;
+ nml_len = 0;
+
+ normalized = qla2x00_normalize_dma_addr(&req_dma, &req_len, &nml_dma,
+ &nml_len);
+
+ /* Load first segment */
+ mcp->mb[0] = MBC_LOAD_RISC_RAM;
+ mcp->mb[1] = risc_addr;
+ mcp->mb[2] = MSW(req_dma);
+ mcp->mb[3] = LSW(req_dma);
+ mcp->mb[4] = (uint16_t)req_len;
+ mcp->mb[6] = MSW(MSD(req_dma));
+ mcp->mb[7] = LSW(MSD(req_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Load second segment - if necessary */
+ if (normalized && (rval == QLA_SUCCESS)) {
+ mcp->mb[0] = MBC_LOAD_RISC_RAM;
+ mcp->mb[1] = risc_addr + (uint16_t)req_len;
+ mcp->mb[2] = MSW(nml_dma);
+ mcp->mb[3] = LSW(nml_dma);
+ mcp->mb[4] = (uint16_t)nml_len;
+ mcp->mb[6] = MSW(MSD(nml_dma));
+ mcp->mb[7] = LSW(MSD(nml_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+ }
+
+ if (rval == QLA_SUCCESS) {
+ /* Empty */
+ DEBUG11(printk("qla2x00_load_ram(%ld): done.\n", ha->host_no);)
+ } else {
+ /* Empty */
+ DEBUG2_3_11(printk("qla2x00_load_ram(%ld): failed. rval=%x "
+ "mb[0]=%x.\n", ha->host_no, rval, mcp->mb[0]);)
+ }
+ return rval;
+}
+
+/*
+ * qla2x00_load_ram_ext
+ * Load adapter extended RAM using DMA.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_load_ram_ext(scsi_qla_host_t *ha, dma_addr_t req_dma,
+ uint32_t risc_addr, uint16_t risc_code_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint32_t req_len;
+ dma_addr_t nml_dma;
+ uint32_t nml_len;
+ uint32_t normalized;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+ req_len = risc_code_size;
+ nml_dma = 0;
+ nml_len = 0;
+
+ normalized = qla2x00_normalize_dma_addr(&req_dma, &req_len, &nml_dma,
+ &nml_len);
+
+ /* Load first segment */
+ mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[2] = MSW(req_dma);
+ mcp->mb[3] = LSW(req_dma);
+ mcp->mb[4] = (uint16_t)req_len;
+ mcp->mb[6] = MSW(MSD(req_dma));
+ mcp->mb[7] = LSW(MSD(req_dma));
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Load second segment - if necessary */
+ if (normalized && (rval == QLA_SUCCESS)) {
+ risc_addr += req_len;
+ mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[2] = MSW(nml_dma);
+ mcp->mb[3] = LSW(nml_dma);
+ mcp->mb[4] = (uint16_t)nml_len;
+ mcp->mb[6] = MSW(MSD(nml_dma));
+ mcp->mb[7] = LSW(MSD(nml_dma));
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n",
+ __func__, ha->host_no, rval, mcp->mb[0]));
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_execute_fw
+ * Start adapter firmware.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_execute_fw(scsi_qla_host_t *ha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_execute_fw(%ld): entered.\n", ha->host_no);)
+
+ mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
+ mcp->mb[1] = *ha->brd_info->fw_info[0].fwstart;
+ mcp->out_mb = MBX_1|MBX_0;
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ mcp->mb[2] = 0;
+ mcp->out_mb |= MBX_2;
+ }
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ DEBUG11(printk("qla2x00_execute_fw(%ld): done.\n", ha->host_no);)
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_fw_version
+ * Get firmware version.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * major: pointer for major number.
+ * minor: pointer for minor number.
+ * subminor: pointer for subminor number.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+void
+qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
+ uint16_t *subminor, uint16_t *attributes, uint32_t *memory)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+ mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->flags = 0;
+ mcp->tov = 30;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Return mailbox data. */
+ *major = mcp->mb[1];
+ *minor = mcp->mb[2];
+ *subminor = mcp->mb[3];
+ *attributes = mcp->mb[6];
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ *memory = 0x1FFFF; /* Defaults to 128KB. */
+ else
+ *memory = (mcp->mb[5] << 16) | mcp->mb[4];
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
+ ha->host_no, rval));
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ }
+}
+
+/*
+ * qla2x00_get_fw_options
+ * Set firmware options.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fwopt = pointer for firmware options.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+ mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
+ ha->host_no, rval));
+ } else {
+ fwopts[1] = mcp->mb[1];
+ fwopts[2] = mcp->mb[2];
+ fwopts[3] = mcp->mb[3];
+
+ DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ }
+
+ return rval;
+}
+
+
+/*
+ * qla2x00_set_fw_options
+ * Set firmware options.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fwopt = pointer for firmware options.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+ mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
+ mcp->mb[1] = fwopts[1];
+ mcp->mb[2] = fwopts[2];
+ mcp->mb[3] = fwopts[3];
+ mcp->mb[10] = fwopts[10];
+ mcp->mb[11] = fwopts[11];
+ mcp->mb[12] = 0; /* Undocumented, but used */
+ mcp->out_mb = MBX_12|MBX_11|MBX_10|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
+ ha->host_no, rval));
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_mbx_reg_test
+ * Mailbox register wrap test.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", ha->host_no);)
+
+ mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
+ mcp->mb[1] = 0xAAAA;
+ mcp->mb[2] = 0x5555;
+ mcp->mb[3] = 0xAA55;
+ mcp->mb[4] = 0x55AA;
+ mcp->mb[5] = 0xA5A5;
+ mcp->mb[6] = 0x5A5A;
+ mcp->mb[7] = 0x2525;
+ mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ if (mcp->mb[1] != 0xAAAA || mcp->mb[2] != 0x5555 ||
+ mcp->mb[3] != 0xAA55 || mcp->mb[4] != 0x55AA)
+ rval = QLA_FUNCTION_FAILED;
+ if (mcp->mb[5] != 0xA5A5 || mcp->mb[6] != 0x5A5A ||
+ mcp->mb[7] != 0x2525)
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_verify_checksum
+ * Verify firmware checksum.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_verify_checksum(scsi_qla_host_t *ha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_verify_checksum(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_VERIFY_CHECKSUM;
+ mcp->mb[1] = *ha->brd_info->fw_info[0].fwstart;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_verify_checksum(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_verify_checksum(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_issue_iocb
+ * Issue IOCB using mailbox command
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * buffer = buffer pointer.
+ * phys_addr = physical address of buffer.
+ * size = size of buffer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_issue_iocb(scsi_qla_host_t *ha, void* buffer, dma_addr_t phys_addr,
+ size_t size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ mcp->mb[0] = MBC_IOCB_COMMAND_A64;
+ mcp->mb[1] = 0;
+ mcp->mb[2] = MSW(phys_addr);
+ mcp->mb[3] = LSW(phys_addr);
+ mcp->mb[6] = MSW(MSD(phys_addr));
+ mcp->mb[7] = LSW(MSD(phys_addr));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x",
+ ha->host_no,rval);)
+ DEBUG2(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x",
+ ha->host_no,rval);)
+ } else {
+ /*EMPTY*/
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_abort_command
+ * Abort command aborts a specified IOCB.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = SB structure pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp)
+{
+ unsigned long flags = 0;
+ fc_port_t *fcport;
+ int rval;
+ uint32_t handle;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", ha->host_no);)
+
+ fcport = sp->fclun->fcport;
+
+ if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
+ atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
+ return 1;
+ }
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
+ if (ha->outstanding_cmds[handle] == sp)
+ break;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (handle == MAX_OUTSTANDING_COMMANDS) {
+ /* command not found */
+ return QLA_FUNCTION_FAILED;
+ }
+
+ mcp->mb[0] = MBC_ABORT_COMMAND;
+ if (HAS_EXTENDED_IDS(ha))
+ mcp->mb[1] = fcport->loop_id;
+ else
+ mcp->mb[1] = fcport->loop_id << 8;
+ mcp->mb[2] = (uint16_t)handle;
+ mcp->mb[3] = (uint16_t)(handle >> 16);
+ mcp->mb[6] = (uint16_t)sp->fclun->lun;
+ mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ sp->flags |= SRB_ABORT_PENDING;
+ DEBUG11(printk("qla2x00_abort_command(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+#if USE_ABORT_TGT
+/*
+ * qla2x00_abort_target
+ * Issue abort target mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_abort_target(fc_port_t *fcport)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_abort_target(%ld): entered.\n",
+ fcport->ha->host_no);)
+
+ if (fcport == NULL) {
+ /* no target to abort */
+ return 0;
+ }
+
+ mcp->mb[0] = MBC_ABORT_TARGET;
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(fcport->ha)) {
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = fcport->loop_id << 8;
+ }
+ mcp->mb[2] = fcport->ha->loop_reset_delay;
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(fcport->ha, mcp);
+
+ /* Issue marker command. */
+ fcport->ha->marker_needed = 1;
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("qla2x00_abort_target(%ld): failed=%x.\n",
+ fcport->ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_abort_target(%ld): done.\n",
+ fcport->ha->host_no);)
+ }
+
+ return rval;
+}
+#endif
+
+/*
+ * qla2x00_target_reset
+ * Issue target reset mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_target_reset(scsi_qla_host_t *ha, uint16_t b, uint16_t t)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ os_tgt_t *tgt;
+
+ DEBUG11(printk("qla2x00_target_reset(%ld): entered.\n", ha->host_no);)
+
+ tgt = TGT_Q(ha, t);
+ if (tgt->fcport == NULL) {
+ /* no target to abort */
+ return 0;
+ }
+ if (atomic_read(&tgt->fcport->state) != FCS_ONLINE) {
+ /* target not online */
+ return 0;
+ }
+
+ mcp->mb[0] = MBC_TARGET_RESET;
+ if (HAS_EXTENDED_IDS(ha))
+ mcp->mb[1] = tgt->fcport->loop_id;
+ else
+ mcp->mb[1] = tgt->fcport->loop_id << 8;
+ mcp->mb[2] = ha->loop_reset_delay;
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_target_reset(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_target_reset(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_adapter_id
+ * Get adapter ID and topology.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * id = pointer for loop ID.
+ * al_pa = pointer for AL_PA.
+ * area = pointer for area.
+ * domain = pointer for domain.
+ * top = pointer for topology.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
+ uint8_t *area, uint8_t *domain, uint16_t *top)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Return data. */
+ *id = mcp->mb[1];
+ *al_pa = LSB(mcp->mb[2]);
+ *area = MSB(mcp->mb[2]);
+ *domain = LSB(mcp->mb[3]);
+ *top = mcp->mb[6];
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_retry_cnt
+ * Get current firmware login retry count and delay.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * retry_cnt = pointer to login retry count.
+ * tov = pointer to login timeout value.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
+ uint16_t *r_a_tov)
+{
+ int rval;
+ uint16_t ratov;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_GET_RETRY_COUNT;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n",
+ ha->host_no, mcp->mb[0]);)
+ } else {
+ /* Convert returned data and check our values. */
+ *r_a_tov = mcp->mb[3] / 2;
+ ratov = (mcp->mb[3]/2) / 10; /* mb[3] value is in 100ms */
+ if (mcp->mb[1] * ratov > (*retry_cnt) * (*tov)) {
+ /* Update to the larger values */
+ *retry_cnt = (uint8_t)mcp->mb[1];
+ *tov = ratov;
+ }
+
+ DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d "
+ "ratov=%d.\n", ha->host_no, mcp->mb[3], ratov);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_init_firmware
+ * Initialize adapter firmware.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * dptr = Initialization control block pointer.
+ * size = size of initialization control block.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_INITIALIZE_FIRMWARE;
+ mcp->mb[2] = MSW(ha->init_cb_dma);
+ mcp->mb[3] = LSW(ha->init_cb_dma);
+ mcp->mb[4] = 0;
+ mcp->mb[5] = 0;
+ mcp->mb[6] = MSW(MSD(ha->init_cb_dma));
+ mcp->mb[7] = LSW(MSD(ha->init_cb_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_5|MBX_4|MBX_0;
+ mcp->buf_size = size;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = 30;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x "
+ "mb0=%x.\n",
+ ha->host_no, rval, mcp->mb[0]);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_port_database
+ * Issue normal/enhanced get port database mailbox command
+ * and copy device name as necessary.
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * dev = structure pointer.
+ * opt = enhanced cmd option byte.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ port_database_t *pd;
+ dma_addr_t pd_dma;
+
+ DEBUG11(printk("qla2x00_get_port_database(%ld): entered.\n",
+ ha->host_no);)
+
+ pd = dma_pool_alloc(ha->s_dma_pool, GFP_ATOMIC, &pd_dma);
+ if (pd == NULL) {
+ DEBUG2_3_11(printk("qla2x00_get_port_database(%ld): **** "
+ "Mem Alloc Failed ****", ha->host_no);)
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(pd, 0, PORT_DATABASE_SIZE);
+
+ if (opt != 0)
+ mcp->mb[0] = MBC_ENHANCED_GET_PORT_DATABASE;
+ else
+ mcp->mb[0] = MBC_GET_PORT_DATABASE;
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = fcport->loop_id << 8 | opt;
+ }
+ mcp->mb[2] = MSW(pd_dma);
+ mcp->mb[3] = LSW(pd_dma);
+ mcp->mb[6] = MSW(MSD(pd_dma));
+ mcp->mb[7] = LSW(MSD(pd_dma));
+
+ mcp->in_mb = MBX_0;
+ mcp->buf_size = PORT_DATABASE_SIZE;
+ mcp->flags = MBX_DMA_IN;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ rval = qla2x00_mailbox_command(ha, mcp);
+ if (rval != QLA_SUCCESS)
+ goto gpd_error_out;
+
+ /* Check for logged in state. */
+ if (pd->master_state != PD_STATE_PORT_LOGGED_IN &&
+ pd->slave_state != PD_STATE_PORT_LOGGED_IN) {
+ rval = QLA_FUNCTION_FAILED;
+ goto gpd_error_out;
+ }
+
+ /* Names are little-endian. */
+ memcpy(fcport->node_name, pd->node_name, WWN_SIZE);
+ memcpy(fcport->port_name, pd->port_name, WWN_SIZE);
+
+ /* Get port_id of device. */
+ fcport->d_id.b.al_pa = pd->port_id[2];
+ fcport->d_id.b.area = pd->port_id[3];
+ fcport->d_id.b.domain = pd->port_id[0];
+ fcport->d_id.b.rsvd_1 = 0;
+
+ /* Check for device require authentication. */
+ pd->common_features & BIT_5 ? (fcport->flags |= FCF_AUTH_REQ) :
+ (fcport->flags &= ~FCF_AUTH_REQ);
+
+ /* If not target must be initiator or unknown type. */
+ if ((pd->prli_svc_param_word_3[0] & BIT_4) == 0)
+ fcport->port_type = FCT_INITIATOR;
+ else
+ fcport->port_type = FCT_TARGET;
+
+gpd_error_out:
+ dma_pool_free(ha->s_dma_pool, pd, pd_dma);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_get_port_database(%ld): "
+ "failed=%x.\n", ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_get_port_database(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_firmware_state
+ * Get adapter firmware state.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * dptr = pointer for firmware state.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *dptr)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Return firmware state. */
+ *dptr = mcp->mb[1];
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): "
+ "failed=%x.\n", ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_port_name
+ * Issue get port name mailbox command.
+ * Returned name is in big endian format.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = loop ID of device.
+ * name = pointer for name.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
+ uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_GET_PORT_NAME;
+ mcp->out_mb = MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = loop_id << 8 | opt;
+ }
+
+ mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ if (name != NULL) {
+ /* This function returns name in big endian. */
+ name[0] = LSB(mcp->mb[2]);
+ name[1] = MSB(mcp->mb[2]);
+ name[2] = LSB(mcp->mb[3]);
+ name[3] = MSB(mcp->mb[3]);
+ name[4] = LSB(mcp->mb[6]);
+ name[5] = MSB(mcp->mb[6]);
+ name[6] = LSB(mcp->mb[7]);
+ name[7] = MSB(mcp->mb[7]);
+ }
+
+ DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_lip_reset
+ * Issue LIP reset mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_lip_reset(scsi_qla_host_t *ha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_lip_reset(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_LIP_RESET;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = 0x00ff;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = 0xff00;
+ }
+ mcp->mb[2] = ha->loop_reset_delay;
+ mcp->mb[3] = 0;
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_lip_reset(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_lip_reset(%ld): done.\n", ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_send_sns
+ * Send SNS command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sns = pointer for command.
+ * cmd_size = command size.
+ * buf_size = response/command size.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
+ uint16_t cmd_size, size_t buf_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n",
+ ha->host_no);)
+
+ DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total "
+ "tov=%d.\n", ha->retry_count, ha->login_timeout, mcp->tov);)
+
+ mcp->mb[0] = MBC_SEND_SNS_COMMAND;
+ mcp->mb[1] = cmd_size;
+ mcp->mb[2] = MSW(sns_phys_address);
+ mcp->mb[3] = LSW(sns_phys_address);
+ mcp->mb[6] = MSW(MSD(sns_phys_address));
+ mcp->mb[7] = LSW(MSD(sns_phys_address));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0|MBX_1;
+ mcp->buf_size = buf_size;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
+ "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]);)
+ DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
+ "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_login_fabric
+ * Issue login fabric port mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * domain = device domain.
+ * area = device area.
+ * al_pa = device AL_PA.
+ * status = pointer for return status.
+ * opt = command options.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+ uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", ha->host_no);)
+
+ mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = (loop_id << 8) | opt;
+ }
+ mcp->mb[2] = domain;
+ mcp->mb[3] = area << 8 | al_pa;
+
+ mcp->in_mb = MBX_7|MBX_6|MBX_2|MBX_1|MBX_0;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Return mailbox statuses. */
+ if (mb != NULL) {
+ mb[0] = mcp->mb[0];
+ mb[1] = mcp->mb[1];
+ mb[2] = mcp->mb[2];
+ mb[6] = mcp->mb[6];
+ mb[7] = mcp->mb[7];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /* RLU tmp code: need to change main mailbox_command function to
+ * return ok even when the mailbox completion value is not
+ * SUCCESS. The caller needs to be responsible to interpret
+ * the return values of this mailbox command if we're not
+ * to change too much of the existing code.
+ */
+ if (mcp->mb[0] == 0x4001 || mcp->mb[0] == 0x4002 ||
+ mcp->mb[0] == 0x4003 || mcp->mb[0] == 0x4005 ||
+ mcp->mb[0] == 0x4006)
+ rval = QLA_SUCCESS;
+
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x "
+ "mb[0]=%x mb[1]=%x mb[2]=%x.\n", ha->host_no, rval,
+ mcp->mb[0], mcp->mb[1], mcp->mb[2]);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_login_local_device
+ * Issue login loop port mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * opt = command options.
+ *
+ * Returns:
+ * Return status code.
+ *
+ * Context:
+ * Kernel context.
+ *
+ */
+int
+qla2x00_login_local_device(scsi_qla_host_t *ha, uint16_t loop_id,
+ uint16_t *mb_ret, uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG3(printk("%s(%ld): entered.\n", __func__, ha->host_no);)
+
+ mcp->mb[0] = MBC_LOGIN_LOOP_PORT;
+ if (HAS_EXTENDED_IDS(ha))
+ mcp->mb[1] = loop_id;
+ else
+ mcp->mb[1] = loop_id << 8;
+ mcp->mb[2] = opt;
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_7|MBX_6|MBX_1|MBX_0;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ /* Return mailbox statuses. */
+ if (mb_ret != NULL) {
+ mb_ret[0] = mcp->mb[0];
+ mb_ret[1] = mcp->mb[1];
+ mb_ret[6] = mcp->mb[6];
+ mb_ret[7] = mcp->mb[7];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /* AV tmp code: need to change main mailbox_command function to
+ * return ok even when the mailbox completion value is not
+ * SUCCESS. The caller needs to be responsible to interpret
+ * the return values of this mailbox command if we're not
+ * to change too much of the existing code.
+ */
+ if (mcp->mb[0] == 0x4005 || mcp->mb[0] == 0x4006)
+ rval = QLA_SUCCESS;
+
+ DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
+ "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
+ mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);)
+ DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
+ "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
+ mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);)
+ } else {
+ /*EMPTY*/
+ DEBUG3(printk("%s(%ld): done.\n", __func__, ha->host_no);)
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_fabric_logout
+ * Issue logout fabric port mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
+ mcp->out_mb = MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = loop_id << 8;
+ }
+
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x "
+ "mbx1=%x.\n", ha->host_no, rval, mcp->mb[1]);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_full_login_lip
+ * Issue full login LIP mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_full_login_lip(scsi_qla_host_t *ha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
+ ha->host_no);)
+
+ mcp->mb[0] = MBC_LIP_FULL_LOGIN;
+ mcp->mb[1] = 0;
+ mcp->mb[2] = 0;
+ mcp->mb[3] = 0;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n",
+ ha->instance, rval);)
+ } else {
+ /*EMPTY*/
+ DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_id_list
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
+ uint16_t *entries)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n",
+ ha->host_no);)
+
+ if (id_list == NULL)
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_GET_ID_LIST;
+ mcp->mb[1] = MSW(id_list_dma);
+ mcp->mb[2] = LSW(id_list_dma);
+ mcp->mb[3] = MSW(MSD(id_list_dma));
+ mcp->mb[6] = LSW(MSD(id_list_dma));
+ mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n",
+ ha->host_no, rval);)
+ } else {
+ *entries = mcp->mb[1];
+ DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n",
+ ha->host_no);)
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_resource_cnts
+ * Get current firmware resource counts.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
+ uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt, uint16_t *orig_iocb_cnt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+
+ mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__,
+ ha->host_no, mcp->mb[0]);)
+ } else {
+ DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
+ "mb7=%x mb10=%x.\n", __func__, ha->host_no,
+ mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
+ mcp->mb[10]));
+
+ if (cur_xchg_cnt)
+ *cur_xchg_cnt = mcp->mb[3];
+ if (orig_xchg_cnt)
+ *orig_xchg_cnt = mcp->mb[6];
+ if (cur_iocb_cnt)
+ *cur_iocb_cnt = mcp->mb[7];
+ if (orig_iocb_cnt)
+ *orig_iocb_cnt = mcp->mb[10];
+ }
+
+ return (rval);
+}
+
+#if defined(QL_DEBUG_LEVEL_3)
+/*
+ * qla2x00_get_fcal_position_map
+ * Get FCAL (LILP) position map using mailbox command
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * pos_map = buffer pointer (can be NULL).
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ char *pmap;
+ dma_addr_t pmap_dma;
+
+ pmap = dma_pool_alloc(ha->s_dma_pool, GFP_ATOMIC, &pmap_dma);
+ if (pmap == NULL) {
+ DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****",
+ __func__, ha->host_no));
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(pmap, 0, FCAL_MAP_SIZE);
+
+ mcp->mb[0] = MBC_GET_FC_AL_POSITION_MAP;
+ mcp->mb[2] = MSW(pmap_dma);
+ mcp->mb[3] = LSW(pmap_dma);
+ mcp->mb[6] = MSW(MSD(pmap_dma));
+ mcp->mb[7] = LSW(MSD(pmap_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->buf_size = FCAL_MAP_SIZE;
+ mcp->flags = MBX_DMA_IN;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ rval = qla2x00_mailbox_command(ha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map "
+ "size (%x)\n", __func__, ha->host_no, mcp->mb[0],
+ mcp->mb[1], (unsigned)pmap[0]));
+ DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1));
+
+ if (pos_map)
+ memcpy(pos_map, pmap, FCAL_MAP_SIZE);
+ }
+ dma_pool_free(ha->s_dma_pool, pmap, pmap_dma);
+
+ if (rval != QLA_SUCCESS) {
+ DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
+ ha->host_no, rval));
+ } else {
+ DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ }
+
+ return rval;
+}
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
new file mode 100644
index 000000000000..b5863d8769e0
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -0,0 +1,4456 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+/*
+ * Driver version
+ */
+char qla2x00_version_str[40];
+
+/*
+ * SRB allocation cache
+ */
+char srb_cachep_name[16];
+kmem_cache_t *srb_cachep;
+
+/*
+ * Stats for all adpaters.
+ */
+struct _qla2x00stats qla2x00_stats;
+
+/*
+ * Ioctl related information.
+ */
+int num_hosts;
+int apiHBAInstance;
+
+/*
+ * Module parameter information and variables
+ */
+int ql2xmaxqdepth;
+module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xmaxqdepth,
+ "Maximum queue depth to report for target devices.");
+
+int ql2xlogintimeout = 20;
+module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xlogintimeout,
+ "Login timeout value in seconds.");
+
+int qlport_down_retry;
+module_param(qlport_down_retry, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(qlport_down_retry,
+ "Maximum number of command retries to a port that returns"
+ "a PORT-DOWN status.");
+
+int ql2xretrycount = 20;
+module_param(ql2xretrycount, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xretrycount,
+ "Maximum number of mid-layer retries allowed for a command. "
+ "Default value is 20, ");
+
+int displayConfig;
+module_param(displayConfig, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(displayConfig,
+ "If 1 then display the configuration used in /etc/modprobe.conf.");
+
+int ql2xplogiabsentdevice;
+module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xplogiabsentdevice,
+ "Option to enable PLOGI to devices that are not present after "
+ "a Fabric scan. This is needed for several broken switches."
+ "Default is 0 - no PLOGI. 1 - perfom PLOGI.");
+
+int ql2xenablezio = 0;
+module_param(ql2xenablezio, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xenablezio,
+ "Option to enable ZIO:If 1 then enable it otherwise"
+ " use the default set in the NVRAM."
+ " Default is 0 : disabled");
+
+int ql2xintrdelaytimer = 10;
+module_param(ql2xintrdelaytimer, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xintrdelaytimer,
+ "ZIO: Waiting time for Firmware before it generates an "
+ "interrupt to the host to notify completion of request.");
+
+int ConfigRequired;
+module_param(ConfigRequired, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ConfigRequired,
+ "If 1, then only configured devices passed in through the"
+ "ql2xopts parameter will be presented to the OS");
+
+int Bind = BIND_BY_PORT_NAME;
+module_param(Bind, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(Bind,
+ "Target persistent binding method: "
+ "0 by Portname (default); 1 by PortID; 2 by Nodename. ");
+
+int ql2xsuspendcount = SUSPEND_COUNT;
+module_param(ql2xsuspendcount, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xsuspendcount,
+ "Number of 6-second suspend iterations to perform while a "
+ "target returns a <NOT READY> status. Default is 10 "
+ "iterations.");
+
+int ql2xdoinitscan = 1;
+module_param(ql2xdoinitscan, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xdoinitscan,
+ "Signal mid-layer to perform scan after driver load: 0 -- no "
+ "signal sent to mid-layer.");
+
+int ql2xloginretrycount = 0;
+module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xloginretrycount,
+ "Specify an alternate value for the NVRAM login retry count.");
+
+/*
+ * Proc structures and functions
+ */
+struct info_str {
+ char *buffer;
+ int length;
+ off_t offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *, char *, int);
+static int copy_info(struct info_str *, char *, ...);
+
+static void qla2x00_free_device(scsi_qla_host_t *);
+
+static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha);
+
+/*
+ * SCSI host template entry points
+ */
+static int qla2xxx_slave_configure(struct scsi_device * device);
+static int qla2x00_queuecommand(struct scsi_cmnd *cmd,
+ void (*fn)(struct scsi_cmnd *));
+static int qla2xxx_eh_abort(struct scsi_cmnd *);
+static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
+static int qla2x00_loop_reset(scsi_qla_host_t *ha);
+static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *);
+
+static int qla2x00_proc_info(struct Scsi_Host *, char *, char **,
+ off_t, int, int);
+
+static struct scsi_host_template qla2x00_driver_template = {
+ .module = THIS_MODULE,
+ .name = "qla2xxx",
+ .proc_name = "qla2xxx",
+ .proc_info = qla2x00_proc_info,
+ .queuecommand = qla2x00_queuecommand,
+
+ .eh_abort_handler = qla2xxx_eh_abort,
+ .eh_device_reset_handler = qla2xxx_eh_device_reset,
+ .eh_bus_reset_handler = qla2xxx_eh_bus_reset,
+ .eh_host_reset_handler = qla2xxx_eh_host_reset,
+
+ .slave_configure = qla2xxx_slave_configure,
+
+ .this_id = -1,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = SG_ALL,
+
+ /*
+ * The RISC allows for each command to transfer (2^32-1) bytes of data,
+ * which equates to 0x800000 sectors.
+ */
+ .max_sectors = 0xFFFF,
+};
+
+static struct scsi_transport_template *qla2xxx_transport_template = NULL;
+
+static void qla2x00_display_fc_names(scsi_qla_host_t *);
+
+/* TODO Convert to inlines
+ *
+ * Timer routines
+ */
+#define WATCH_INTERVAL 1 /* number of seconds */
+
+static void qla2x00_timer(scsi_qla_host_t *);
+
+static __inline__ void qla2x00_start_timer(scsi_qla_host_t *,
+ void *, unsigned long);
+static __inline__ void qla2x00_restart_timer(scsi_qla_host_t *, unsigned long);
+static __inline__ void qla2x00_stop_timer(scsi_qla_host_t *);
+
+static inline void
+qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval)
+{
+ init_timer(&ha->timer);
+ ha->timer.expires = jiffies + interval * HZ;
+ ha->timer.data = (unsigned long)ha;
+ ha->timer.function = (void (*)(unsigned long))func;
+ add_timer(&ha->timer);
+ ha->timer_active = 1;
+}
+
+static inline void
+qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval)
+{
+ mod_timer(&ha->timer, jiffies + interval * HZ);
+}
+
+static __inline__ void
+qla2x00_stop_timer(scsi_qla_host_t *ha)
+{
+ del_timer_sync(&ha->timer);
+ ha->timer_active = 0;
+}
+
+void qla2x00_cmd_timeout(srb_t *);
+
+static __inline__ void qla2x00_callback(scsi_qla_host_t *, struct scsi_cmnd *);
+static __inline__ void sp_put(struct scsi_qla_host * ha, srb_t *sp);
+static __inline__ void sp_get(struct scsi_qla_host * ha, srb_t *sp);
+static __inline__ void
+qla2x00_delete_from_done_queue(scsi_qla_host_t *, srb_t *);
+
+/*
+* qla2x00_callback
+* Returns the completed SCSI command to LINUX.
+*
+* Input:
+* ha -- Host adapter structure
+* cmd -- SCSI mid-level command structure.
+* Returns:
+* None
+* Note:From failover point of view we always get the sp
+* from vis_ha pool in queuecommand.So when we put it
+* back to the pool it has to be the vis_ha.
+* So rely on struct scsi_cmnd to get the vis_ha and not on sp.
+*/
+static inline void
+qla2x00_callback(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
+{
+ srb_t *sp = (srb_t *) CMD_SP(cmd);
+ scsi_qla_host_t *vis_ha;
+ os_lun_t *lq;
+ int got_sense;
+ unsigned long cpu_flags = 0;
+
+ cmd->host_scribble = (unsigned char *) NULL;
+ vis_ha = (scsi_qla_host_t *) cmd->device->host->hostdata;
+
+ if (sp == NULL) {
+ qla_printk(KERN_INFO, ha,
+ "%s(): **** CMD derives a NULL SP\n",
+ __func__);
+ DEBUG2(BUG();)
+ return;
+ }
+
+ /*
+ * If command status is not DID_BUS_BUSY then go ahead and freed sp.
+ */
+ /*
+ * Cancel command timeout
+ */
+ qla2x00_delete_timer_from_cmd(sp);
+
+ /*
+ * Put SP back in the free queue
+ */
+ sp->cmd = NULL;
+ CMD_SP(cmd) = NULL;
+ lq = sp->lun_queue;
+ got_sense = (sp->flags & SRB_GOT_SENSE)? 1: 0;
+ add_to_free_queue(vis_ha, sp);
+
+ if (host_byte(cmd->result) == DID_OK) {
+ /* device ok */
+ ha->total_bytes += cmd->bufflen;
+ if (!got_sense) {
+ /* If lun was suspended then clear retry count */
+ spin_lock_irqsave(&lq->q_lock, cpu_flags);
+ if (!test_bit(LUN_EXEC_DELAYED, &lq->q_flag))
+ lq->q_state = LUN_STATE_READY;
+ spin_unlock_irqrestore(&lq->q_lock, cpu_flags);
+ }
+ } else if (host_byte(cmd->result) == DID_ERROR) {
+ /* device error */
+ ha->total_dev_errs++;
+ }
+
+ /* Call the mid-level driver interrupt handler */
+ (*(cmd)->scsi_done)(cmd);
+}
+
+/**************************************************************************
+* sp_put
+*
+* Description:
+* Decrement reference count and call the callback if we're the last
+* owner of the specified sp. Will get the host_lock before calling
+* the callback.
+*
+* Input:
+* ha - pointer to the scsi_qla_host_t where the callback is to occur.
+* sp - pointer to srb_t structure to use.
+*
+* Returns:
+*
+**************************************************************************/
+static inline void
+sp_put(struct scsi_qla_host * ha, srb_t *sp)
+{
+ if (atomic_read(&sp->ref_count) == 0) {
+ qla_printk(KERN_INFO, ha,
+ "%s(): **** SP->ref_count not zero\n",
+ __func__);
+ DEBUG2(BUG();)
+
+ return;
+ }
+
+ if (!atomic_dec_and_test(&sp->ref_count)) {
+ return;
+ }
+
+ qla2x00_callback(ha, sp->cmd);
+}
+
+/**************************************************************************
+* sp_get
+*
+* Description:
+* Increment reference count of the specified sp.
+*
+* Input:
+* sp - pointer to srb_t structure to use.
+*
+* Returns:
+*
+**************************************************************************/
+static inline void
+sp_get(struct scsi_qla_host * ha, srb_t *sp)
+{
+ atomic_inc(&sp->ref_count);
+
+ if (atomic_read(&sp->ref_count) > 2) {
+ qla_printk(KERN_INFO, ha,
+ "%s(): **** SP->ref_count greater than two\n",
+ __func__);
+ DEBUG2(BUG();)
+
+ return;
+ }
+}
+
+static inline void
+qla2x00_delete_from_done_queue(scsi_qla_host_t *dest_ha, srb_t *sp)
+{
+ /* remove command from done list */
+ list_del_init(&sp->list);
+ dest_ha->done_q_cnt--;
+ sp->state = SRB_NO_QUEUE_STATE;
+
+ if (sp->flags & SRB_DMA_VALID) {
+ sp->flags &= ~SRB_DMA_VALID;
+
+ /* Release memory used for this I/O */
+ if (sp->cmd->use_sg) {
+ pci_unmap_sg(dest_ha->pdev, sp->cmd->request_buffer,
+ sp->cmd->use_sg, sp->cmd->sc_data_direction);
+ } else if (sp->cmd->request_bufflen) {
+ pci_unmap_page(dest_ha->pdev, sp->dma_handle,
+ sp->cmd->request_bufflen,
+ sp->cmd->sc_data_direction);
+ }
+ }
+}
+
+static int qla2x00_do_dpc(void *data);
+
+static void qla2x00_rst_aen(scsi_qla_host_t *);
+
+static uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);
+static void qla2x00_mem_free(scsi_qla_host_t *ha);
+static int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);
+static void qla2x00_free_sp_pool(scsi_qla_host_t *ha);
+static srb_t *qla2x00_get_new_sp(scsi_qla_host_t *ha);
+
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, char *, loff_t,
+ size_t);
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, char *, loff_t,
+ size_t);
+static struct bin_attribute sysfs_fw_dump_attr = {
+ .attr = {
+ .name = "fw_dump",
+ .mode = S_IRUSR | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_fw_dump,
+ .write = qla2x00_sysfs_write_fw_dump,
+};
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, char *, loff_t,
+ size_t);
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, char *, loff_t,
+ size_t);
+static struct bin_attribute sysfs_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUSR | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(nvram_t),
+ .read = qla2x00_sysfs_read_nvram,
+ .write = qla2x00_sysfs_write_nvram,
+};
+
+/* -------------------------------------------------------------------------- */
+
+
+/* SysFS attributes. */
+static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+
+ if (ha->fw_dump_reading == 0)
+ return 0;
+ if (off > ha->fw_dump_buffer_len)
+ return 0;
+ if (off + count > ha->fw_dump_buffer_len)
+ count = ha->fw_dump_buffer_len - off;
+
+ memcpy(buf, &ha->fw_dump_buffer[off], count);
+
+ return (count);
+}
+
+static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ int reading;
+ uint32_t dump_size;
+
+ if (off != 0)
+ return (0);
+
+ reading = simple_strtol(buf, NULL, 10);
+ switch (reading) {
+ case 0:
+ if (ha->fw_dump_reading == 1) {
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump cleared on (%ld).\n",
+ ha->host_no);
+
+ vfree(ha->fw_dump_buffer);
+ free_pages((unsigned long)ha->fw_dump,
+ ha->fw_dump_order);
+
+ ha->fw_dump_reading = 0;
+ ha->fw_dump_buffer = NULL;
+ ha->fw_dump = NULL;
+ }
+ break;
+ case 1:
+ if (ha->fw_dump != NULL && !ha->fw_dump_reading) {
+ ha->fw_dump_reading = 1;
+
+ dump_size = FW_DUMP_SIZE_1M;
+ if (ha->fw_memory_size < 0x20000)
+ dump_size = FW_DUMP_SIZE_128K;
+ else if (ha->fw_memory_size < 0x80000)
+ dump_size = FW_DUMP_SIZE_512K;
+ ha->fw_dump_buffer = (char *)vmalloc(dump_size);
+ if (ha->fw_dump_buffer == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for firmware "
+ "dump buffer (%d).\n", dump_size);
+
+ ha->fw_dump_reading = 0;
+ return (count);
+ }
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump ready for read on (%ld).\n",
+ ha->host_no);
+ memset(ha->fw_dump_buffer, 0, dump_size);
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ qla2100_ascii_fw_dump(ha);
+ else
+ qla2300_ascii_fw_dump(ha);
+ ha->fw_dump_buffer_len = strlen(ha->fw_dump_buffer);
+ }
+ break;
+ }
+ return (count);
+}
+
+static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ uint16_t *witer;
+ unsigned long flags;
+ uint16_t cnt;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != sizeof(nvram_t))
+ return 0;
+
+ /* Read NVRAM. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ qla2x00_lock_nvram_access(ha);
+ witer = (uint16_t *)buf;
+ for (cnt = 0; cnt < count / 2; cnt++) {
+ *witer = cpu_to_le16(qla2x00_get_nvram_word(ha,
+ cnt+ha->nvram_base));
+ witer++;
+ }
+ qla2x00_unlock_nvram_access(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (count);
+}
+
+static ssize_t qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ uint8_t *iter;
+ uint16_t *witer;
+ unsigned long flags;
+ uint16_t cnt;
+ uint8_t chksum;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != sizeof(nvram_t))
+ return 0;
+
+ /* Checksum NVRAM. */
+ iter = (uint8_t *)buf;
+ chksum = 0;
+ for (cnt = 0; cnt < count - 1; cnt++)
+ chksum += *iter++;
+ chksum = ~chksum + 1;
+ *iter = chksum;
+
+ /* Write NVRAM. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ qla2x00_lock_nvram_access(ha);
+ qla2x00_release_nvram_protection(ha);
+ witer = (uint16_t *)buf;
+ for (cnt = 0; cnt < count / 2; cnt++) {
+ qla2x00_write_nvram_word(ha, cnt+ha->nvram_base,
+ cpu_to_le16(*witer));
+ witer++;
+ }
+ qla2x00_unlock_nvram_access(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (count);
+}
+
+/* -------------------------------------------------------------------------- */
+static char *
+qla2x00_get_pci_info_str(struct scsi_qla_host *ha, char *str)
+{
+ static char *pci_bus_modes[] = {
+ "33", "66", "100", "133",
+ };
+ uint16_t pci_bus;
+
+ strcpy(str, "PCI");
+ pci_bus = (ha->pci_attr & (BIT_9 | BIT_10)) >> 9;
+ if (pci_bus) {
+ strcat(str, "-X (");
+ strcat(str, pci_bus_modes[pci_bus]);
+ } else {
+ pci_bus = (ha->pci_attr & BIT_8) >> 8;
+ strcat(str, " (");
+ strcat(str, pci_bus_modes[pci_bus]);
+ }
+ strcat(str, " MHz)");
+
+ return (str);
+}
+
+char *
+qla2x00_get_fw_version_str(struct scsi_qla_host *ha, char *str)
+{
+ char un_str[10];
+
+ sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
+ ha->fw_minor_version,
+ ha->fw_subminor_version);
+
+ if (ha->fw_attributes & BIT_9) {
+ strcat(str, "FLX");
+ return (str);
+ }
+
+ switch (ha->fw_attributes & 0xFF) {
+ case 0x7:
+ strcat(str, "EF");
+ break;
+ case 0x17:
+ strcat(str, "TP");
+ break;
+ case 0x37:
+ strcat(str, "IP");
+ break;
+ case 0x77:
+ strcat(str, "VI");
+ break;
+ default:
+ sprintf(un_str, "(%x)", ha->fw_attributes);
+ strcat(str, un_str);
+ break;
+ }
+ if (ha->fw_attributes & 0x100)
+ strcat(str, "X");
+
+ return (str);
+}
+
+/**************************************************************************
+* qla2x00_queuecommand
+*
+* Description:
+* Queue a command to the controller.
+*
+* Input:
+* cmd - pointer to Scsi cmd structure
+* fn - pointer to Scsi done function
+*
+* Returns:
+* 0 - Always
+*
+* Note:
+* The mid-level driver tries to ensures that queuecommand never gets invoked
+* concurrently with itself or the interrupt handler (although the
+* interrupt handler may call this routine as part of request-completion
+* handling).
+**************************************************************************/
+static int
+qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
+{
+ fc_port_t *fcport;
+ os_lun_t *lq;
+ os_tgt_t *tq;
+ scsi_qla_host_t *ha, *ha2;
+ srb_t *sp;
+ struct Scsi_Host *host;
+ unsigned int b, t, l;
+ unsigned long handle;
+ int was_empty;
+
+
+ host = cmd->device->host;
+ ha = (scsi_qla_host_t *) host->hostdata;
+ was_empty = 1;
+
+ cmd->scsi_done = fn;
+
+ spin_unlock_irq(ha->host->host_lock);
+
+ /*
+ * Allocate a command packet from the "sp" pool. If we cant get back
+ * one then let scsi layer come back later.
+ */
+ if ((sp = qla2x00_get_new_sp(ha)) == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't allocate memory for sp - retried.\n");
+
+ spin_lock_irq(ha->host->host_lock);
+
+ return (1);
+ }
+
+ sp->cmd = cmd;
+ CMD_SP(cmd) = (void *)sp;
+
+ sp->flags = 0;
+ if (CMD_RESID_LEN(cmd) & SRB_IOCTL) {
+ /* Need to set sp->flags */
+ sp->flags |= SRB_IOCTL;
+ CMD_RESID_LEN(cmd) = 0; /* Clear it since no more use. */
+ }
+
+ sp->fo_retry_cnt = 0;
+ sp->err_id = 0;
+
+ /* Generate LU queue on bus, target, LUN */
+ b = cmd->device->channel;
+ t = cmd->device->id;
+ l = cmd->device->lun;
+
+ /*
+ * Start Command Timer. Typically it will be 2 seconds less than what
+ * is requested by the Host such that we can return the IO before
+ * aborts are called.
+ */
+ if ((cmd->timeout_per_command / HZ) > QLA_CMD_TIMER_DELTA)
+ qla2x00_add_timer_to_cmd(sp,
+ (cmd->timeout_per_command / HZ) - QLA_CMD_TIMER_DELTA);
+ else
+ qla2x00_add_timer_to_cmd(sp, cmd->timeout_per_command / HZ);
+
+ if (l >= ha->max_luns) {
+ cmd->result = DID_NO_CONNECT << 16;
+ sp->err_id = SRB_ERR_PORT;
+
+ spin_lock_irq(ha->host->host_lock);
+
+ sp_put(ha, sp);
+
+ return (0);
+ }
+
+ if ((tq = (os_tgt_t *) TGT_Q(ha, t)) != NULL &&
+ (lq = (os_lun_t *) LUN_Q(ha, t, l)) != NULL) {
+ fcport = lq->fclun->fcport;
+ ha2 = fcport->ha;
+ } else {
+ lq = NULL;
+ fcport = NULL;
+ ha2 = ha;
+ }
+
+ /* Set an invalid handle until we issue the command to ISP */
+ /* then we will set the real handle value. */
+ handle = INVALID_HANDLE;
+ cmd->host_scribble = (unsigned char *)handle;
+
+ /* Bookkeeping information */
+ sp->r_start = jiffies; /* Time the request was recieved. */
+ sp->u_start = 0;
+
+ /* Setup device queue pointers. */
+ sp->tgt_queue = tq;
+ sp->lun_queue = lq;
+
+ /*
+ * NOTE : q is NULL
+ *
+ * 1. When device is added from persistent binding but has not been
+ * discovered yet.The state of loopid == PORT_AVAIL.
+ * 2. When device is never found on the bus.(loopid == UNUSED)
+ *
+ * IF Device Queue is not created, or device is not in a valid state
+ * and link down error reporting is enabled, reject IO.
+ */
+ if (fcport == NULL) {
+ DEBUG3(printk("scsi(%ld:%2d:%2d): port unavailable\n",
+ ha->host_no,t,l));
+
+ cmd->result = DID_NO_CONNECT << 16;
+ sp->err_id = SRB_ERR_PORT;
+
+ spin_lock_irq(ha->host->host_lock);
+
+ sp_put(ha, sp);
+
+ return (0);
+ }
+
+ /* Only modify the allowed count if the target is a *non* tape device */
+ if ((fcport->flags & FCF_TAPE_PRESENT) == 0) {
+ sp->flags &= ~SRB_TAPE;
+ if (cmd->allowed < ql2xretrycount) {
+ cmd->allowed = ql2xretrycount;
+ }
+ } else
+ sp->flags |= SRB_TAPE;
+
+ DEBUG5(printk("scsi(%ld:%2d:%2d): (queuecmd) queue sp = %p, "
+ "flags=0x%x fo retry=%d, pid=%ld\n",
+ ha->host_no, t, l, sp, sp->flags, sp->fo_retry_cnt,
+ cmd->serial_number));
+ DEBUG5(qla2x00_print_scsi_cmd(cmd));
+
+ sp->fclun = lq->fclun;
+ sp->ha = ha2;
+
+ if (cmd->sc_data_direction == DMA_BIDIRECTIONAL &&
+ cmd->request_bufflen != 0) {
+
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld): Incorrect data direction - transfer "
+ "length=%d, direction=%d, pid=%ld, opcode=%x\n",
+ ha->host_no, cmd->request_bufflen, cmd->sc_data_direction,
+ cmd->serial_number, cmd->cmnd[0]));
+ }
+
+ /* Final pre-check :
+ *
+ * Either PORT_DOWN_TIMER OR LINK_DOWN_TIMER Expired.
+ */
+ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
+ atomic_read(&ha2->loop_state) == LOOP_DEAD) {
+ /*
+ * Add the command to the done-queue for later failover
+ * processing.
+ */
+ cmd->result = DID_NO_CONNECT << 16;
+ if (atomic_read(&ha2->loop_state) == LOOP_DOWN)
+ sp->err_id = SRB_ERR_LOOP;
+ else
+ sp->err_id = SRB_ERR_PORT;
+
+ add_to_done_queue(ha, sp);
+ qla2x00_done(ha);
+
+ spin_lock_irq(ha->host->host_lock);
+ return (0);
+ }
+
+ if (tq && test_bit(TQF_SUSPENDED, &tq->flags) &&
+ (sp->flags & SRB_TAPE) == 0) {
+ /* If target suspended put incoming I/O in retry_q. */
+ qla2x00_extend_timeout(sp->cmd, 10);
+ add_to_scsi_retry_queue(ha, sp);
+ } else
+ was_empty = add_to_pending_queue(ha, sp);
+
+ if ((IS_QLA2100(ha) || IS_QLA2200(ha)) && ha->flags.online) {
+ if (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ qla2x00_process_response_queue(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+ }
+
+ /* We submit to the hardware if:
+ *
+ * 1) we're on the cpu the irq's arrive on or
+ * 2) there are very few io's outstanding.
+ *
+ * In all other cases we'll let an irq pick up our IO and submit it
+ * to the controller to improve affinity.
+ */
+ if (_smp_processor_id() == ha->last_irq_cpu || was_empty)
+ qla2x00_next(ha);
+
+ spin_lock_irq(ha->host->host_lock);
+
+ return (0);
+}
+
+/*
+ * qla2x00_eh_wait_on_command
+ * Waits for the command to be returned by the Firmware for some
+ * max time.
+ *
+ * Input:
+ * ha = actual ha whose done queue will contain the command
+ * returned by firmware.
+ * cmd = Scsi Command to wait on.
+ * flag = Abort/Reset(Bus or Device Reset)
+ *
+ * Return:
+ * Not Found : 0
+ * Found : 1
+ */
+static int
+qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
+{
+#define ABORT_POLLING_PERIOD HZ
+#define ABORT_WAIT_TIME ((10 * HZ) / (ABORT_POLLING_PERIOD))
+
+ int found = 0;
+ int done = 0;
+ srb_t *rp = NULL;
+ struct list_head *list, *temp;
+ u_long max_wait_time = ABORT_WAIT_TIME;
+
+ do {
+ /* Check on done queue */
+ spin_lock(&ha->list_lock);
+ list_for_each_safe(list, temp, &ha->done_queue) {
+ rp = list_entry(list, srb_t, list);
+
+ /*
+ * Found command. Just exit and wait for the cmd sent
+ * to OS.
+ */
+ if (cmd == rp->cmd) {
+ found++;
+ DEBUG3(printk("%s: found in done queue.\n",
+ __func__);)
+ break;
+ }
+ }
+ spin_unlock(&ha->list_lock);
+
+ /* Complete the cmd right away. */
+ if (found) {
+ qla2x00_delete_from_done_queue(ha, rp);
+ sp_put(ha, rp);
+ done++;
+ break;
+ }
+
+ spin_unlock_irq(ha->host->host_lock);
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(ABORT_POLLING_PERIOD);
+
+ spin_lock_irq(ha->host->host_lock);
+
+ } while ((max_wait_time--));
+
+ if (done)
+ DEBUG2(printk(KERN_INFO "%s: found cmd=%p.\n", __func__, cmd));
+
+ return (done);
+}
+
+/*
+ * qla2x00_wait_for_hba_online
+ * Wait till the HBA is online after going through
+ * <= MAX_RETRIES_OF_ISP_ABORT or
+ * finally HBA is disabled ie marked offline
+ *
+ * Input:
+ * ha - pointer to host adapter structure
+ *
+ * Note:
+ * Does context switching-Release SPIN_LOCK
+ * (if any) before calling this routine.
+ *
+ * Return:
+ * Success (Adapter is online) : 0
+ * Failed (Adapter is offline/disabled) : 1
+ */
+static int
+qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
+{
+ int return_status;
+ unsigned long wait_online;
+
+ wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+ while (((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) ||
+ test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &ha->dpc_flags) ||
+ ha->dpc_active) && time_before(jiffies, wait_online)) {
+
+ msleep(1000);
+ }
+ if (ha->flags.online)
+ return_status = QLA_SUCCESS;
+ else
+ return_status = QLA_FUNCTION_FAILED;
+
+ DEBUG2(printk("%s return_status=%d\n",__func__,return_status));
+
+ return (return_status);
+}
+
+/*
+ * qla2x00_wait_for_loop_ready
+ * Wait for MAX_LOOP_TIMEOUT(5 min) value for loop
+ * to be in LOOP_READY state.
+ * Input:
+ * ha - pointer to host adapter structure
+ *
+ * Note:
+ * Does context switching-Release SPIN_LOCK
+ * (if any) before calling this routine.
+ *
+ *
+ * Return:
+ * Success (LOOP_READY) : 0
+ * Failed (LOOP_NOT_READY) : 1
+ */
+static inline int
+qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
+{
+ int return_status = QLA_SUCCESS;
+ unsigned long loop_timeout ;
+
+ /* wait for 5 min at the max for loop to be ready */
+ loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+
+ while ((!atomic_read(&ha->loop_down_timer) &&
+ atomic_read(&ha->loop_state) == LOOP_DOWN) ||
+ test_bit(CFG_ACTIVE, &ha->cfg_flags) ||
+ atomic_read(&ha->loop_state) != LOOP_READY) {
+ msleep(1000);
+ if (time_after_eq(jiffies, loop_timeout)) {
+ return_status = QLA_FUNCTION_FAILED;
+ break;
+ }
+ }
+ return (return_status);
+}
+
+/**************************************************************************
+* qla2xxx_eh_abort
+*
+* Description:
+* The abort function will abort the specified command.
+*
+* Input:
+* cmd = Linux SCSI command packet to be aborted.
+*
+* Returns:
+* Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+int
+qla2xxx_eh_abort(struct scsi_cmnd *cmd)
+{
+ int i;
+ int return_status = FAILED;
+ os_lun_t *q;
+ scsi_qla_host_t *ha;
+ scsi_qla_host_t *vis_ha;
+ srb_t *sp;
+ srb_t *rp;
+ struct list_head *list, *temp;
+ struct Scsi_Host *host;
+ uint8_t found = 0;
+ unsigned int b, t, l;
+
+ /* Get the SCSI request ptr */
+ sp = (srb_t *) CMD_SP(cmd);
+
+ /*
+ * If sp is NULL, command is already returned.
+ * sp is NULLED just before we call back scsi_done
+ *
+ */
+ if ((sp == NULL)) {
+ /* no action - we don't have command */
+ qla_printk(KERN_INFO, to_qla_host(cmd->device->host),
+ "qla2xxx_eh_abort: cmd already done sp=%p\n", sp);
+ DEBUG(printk("qla2xxx_eh_abort: cmd already done sp=%p\n", sp);)
+ return SUCCESS;
+ }
+ if (sp) {
+ DEBUG(printk("qla2xxx_eh_abort: refcount %i \n",
+ atomic_read(&sp->ref_count));)
+ }
+
+ vis_ha = (scsi_qla_host_t *) cmd->device->host->hostdata;
+ ha = (scsi_qla_host_t *)cmd->device->host->hostdata;
+
+ host = ha->host;
+
+ /* Generate LU queue on bus, target, LUN */
+ b = cmd->device->channel;
+ t = cmd->device->id;
+ l = cmd->device->lun;
+ q = GET_LU_Q(vis_ha, t, l);
+
+ qla_printk(KERN_INFO, ha,
+ "%s scsi(%ld:%d:%d:%d): cmd_timeout_in_sec=0x%x.\n", __func__,
+ ha->host_no, (int)b, (int)t, (int)l,
+ cmd->timeout_per_command / HZ);
+
+ /*
+ * if no LUN queue then something is very wrong!!!
+ */
+ if (q == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "qla2x00: (%x:%x:%x) No LUN queue.\n", b, t, l);
+
+ /* no action - we don't have command */
+ return FAILED;
+ }
+
+ DEBUG2(printk("scsi(%ld): ABORTing cmd=%p sp=%p jiffies = 0x%lx, "
+ "timeout=%x, dpc_flags=%lx, vis_ha->dpc_flags=%lx q->flag=%lx\n",
+ ha->host_no, cmd, sp, jiffies, cmd->timeout_per_command / HZ,
+ ha->dpc_flags, vis_ha->dpc_flags, q->q_flag));
+ DEBUG2(qla2x00_print_scsi_cmd(cmd));
+
+ spin_unlock_irq(ha->host->host_lock);
+ if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ DEBUG2(printk("%s failed:board disabled\n", __func__);)
+ spin_lock_irq(ha->host->host_lock);
+ return FAILED;
+ }
+ spin_lock_irq(ha->host->host_lock);
+
+ /* Search done queue */
+ spin_lock(&ha->list_lock);
+ list_for_each_safe(list, temp, &ha->done_queue) {
+ rp = list_entry(list, srb_t, list);
+
+ if (cmd != rp->cmd)
+ continue;
+
+ /*
+ * Found command.Remove it from done list.
+ * And proceed to post completion to scsi mid layer.
+ */
+ return_status = SUCCESS;
+ found++;
+ qla2x00_delete_from_done_queue(ha, sp);
+
+ break;
+ } /* list_for_each_safe() */
+ spin_unlock(&ha->list_lock);
+
+ /*
+ * Return immediately if the aborted command was already in the done
+ * queue
+ */
+ if (found) {
+ qla_printk(KERN_INFO, ha,
+ "qla2xxx_eh_abort: Returning completed command=%p sp=%p\n",
+ cmd, sp);
+ sp_put(ha, sp);
+ return (return_status);
+ }
+
+
+ /*
+ * See if this command is in the retry queue
+ */
+ DEBUG3(printk("qla2xxx_eh_abort: searching sp %p in retry "
+ "queue.\n", sp);)
+
+ spin_lock(&ha->list_lock);
+ list_for_each_safe(list, temp, &ha->retry_queue) {
+ rp = list_entry(list, srb_t, list);
+
+ if (cmd != rp->cmd)
+ continue;
+
+
+ DEBUG2(printk("qla2xxx_eh_abort: found "
+ "in retry queue. SP=%p\n", sp);)
+
+ __del_from_retry_queue(ha, rp);
+ cmd->result = DID_ABORT << 16;
+ __add_to_done_queue(ha, rp);
+
+ return_status = SUCCESS;
+ found++;
+
+ break;
+
+ }
+ spin_unlock(&ha->list_lock);
+
+
+ /*
+ * Our SP pointer points at the command we want to remove from the
+ * pending queue providing we haven't already sent it to the adapter.
+ */
+ if (!found) {
+ DEBUG3(printk("qla2xxx_eh_abort: searching sp %p "
+ "in pending queue.\n", sp);)
+
+ spin_lock(&vis_ha->list_lock);
+ list_for_each_safe(list, temp, &vis_ha->pending_queue) {
+ rp = list_entry(list, srb_t, list);
+
+ if (rp->cmd != cmd)
+ continue;
+
+ /* Remove srb from LUN queue. */
+ rp->flags |= SRB_ABORTED;
+
+ DEBUG2(printk("qla2xxx_eh_abort: Cmd in pending queue."
+ " serial_number %ld.\n",
+ sp->cmd->serial_number);)
+
+ __del_from_pending_queue(vis_ha, rp);
+ cmd->result = DID_ABORT << 16;
+
+ __add_to_done_queue(vis_ha, rp);
+
+ return_status = SUCCESS;
+
+ found++;
+ break;
+ } /* list_for_each_safe() */
+ spin_unlock(&vis_ha->list_lock);
+ } /*End of if !found */
+
+ if (!found) { /* find the command in our active list */
+ DEBUG3(printk("qla2xxx_eh_abort: searching sp %p "
+ "in outstanding queue.\n", sp);)
+
+ spin_lock(&ha->hardware_lock);
+ for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
+ sp = ha->outstanding_cmds[i];
+
+ if (sp == NULL)
+ continue;
+
+ if (sp->cmd != cmd)
+ continue;
+
+ DEBUG2(printk("qla2xxx_eh_abort(%ld): aborting sp %p "
+ "from RISC. pid=%ld sp->state=%x q->q_flag=%lx\n",
+ ha->host_no, sp, sp->cmd->serial_number,
+ sp->state, q->q_flag);)
+ DEBUG(qla2x00_print_scsi_cmd(cmd);)
+
+ /* Get a reference to the sp and drop the lock.*/
+ sp_get(ha, sp);
+
+ spin_unlock(&ha->hardware_lock);
+ spin_unlock_irq(ha->host->host_lock);
+
+ if (qla2x00_abort_command(ha, sp)) {
+ DEBUG2(printk("qla2xxx_eh_abort: abort_command "
+ "mbx failed.\n");)
+ return_status = FAILED;
+ } else {
+ DEBUG3(printk("qla2xxx_eh_abort: abort_command "
+ " mbx success.\n");)
+ return_status = SUCCESS;
+ }
+
+ sp_put(ha,sp);
+
+ spin_lock_irq(ha->host->host_lock);
+ spin_lock(&ha->hardware_lock);
+
+ /*
+ * Regardless of mailbox command status, go check on
+ * done queue just in case the sp is already done.
+ */
+ break;
+
+ }/*End of for loop */
+ spin_unlock(&ha->hardware_lock);
+
+ } /*End of if !found */
+
+ /* Waiting for our command in done_queue to be returned to OS.*/
+ if (qla2x00_eh_wait_on_command(ha, cmd) != 0) {
+ DEBUG2(printk("qla2xxx_eh_abort: cmd returned back to OS.\n");)
+ return_status = SUCCESS;
+ }
+
+ if (return_status == FAILED) {
+ qla_printk(KERN_INFO, ha,
+ "qla2xxx_eh_abort Exiting: status=Failed\n");
+ return FAILED;
+ }
+
+ DEBUG2(printk("qla2xxx_eh_abort: Exiting. return_status=0x%x.\n",
+ return_status));
+
+ return return_status;
+}
+
+/**************************************************************************
+* qla2x00_eh_wait_for_pending_target_commands
+*
+* Description:
+* Waits for all the commands to come back from the specified target.
+*
+* Input:
+* ha - pointer to scsi_qla_host structure.
+* t - target
+* Returns:
+* Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+static int
+qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t)
+{
+ int cnt;
+ int status;
+ srb_t *sp;
+ struct scsi_cmnd *cmd;
+
+ status = 0;
+
+ /*
+ * Waiting for all commands for the designated target in the active
+ * array
+ */
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ spin_lock(&ha->hardware_lock);
+ sp = ha->outstanding_cmds[cnt];
+ if (sp) {
+ cmd = sp->cmd;
+ spin_unlock(&ha->hardware_lock);
+ if (cmd->device->id == t) {
+ if (!qla2x00_eh_wait_on_command(ha, cmd)) {
+ status = 1;
+ break;
+ }
+ }
+ }
+ else {
+ spin_unlock(&ha->hardware_lock);
+ }
+ }
+ return (status);
+}
+
+
+/**************************************************************************
+* qla2xxx_eh_device_reset
+*
+* Description:
+* The device reset function will reset the target and abort any
+* executing commands.
+*
+* NOTE: The use of SP is undefined within this context. Do *NOT*
+* attempt to use this value, even if you determine it is
+* non-null.
+*
+* Input:
+* cmd = Linux SCSI command packet of the command that cause the
+* bus device reset.
+*
+* Returns:
+* SUCCESS/FAILURE (defined as macro in scsi.h).
+*
+**************************************************************************/
+int
+qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+{
+ int return_status;
+ unsigned int b, t, l;
+ scsi_qla_host_t *ha;
+ os_tgt_t *tq;
+ os_lun_t *lq;
+ fc_port_t *fcport_to_reset;
+ srb_t *rp;
+ struct list_head *list, *temp;
+
+ return_status = FAILED;
+ if (cmd == NULL) {
+ printk(KERN_INFO
+ "%s(): **** SCSI mid-layer passing in NULL cmd\n",
+ __func__);
+
+ return (return_status);
+ }
+
+ b = cmd->device->channel;
+ t = cmd->device->id;
+ l = cmd->device->lun;
+ ha = (scsi_qla_host_t *)cmd->device->host->hostdata;
+
+ tq = TGT_Q(ha, t);
+ if (tq == NULL) {
+ qla_printk(KERN_INFO, ha,
+ "%s(): **** CMD derives a NULL TGT_Q\n", __func__);
+
+ return (return_status);
+ }
+ lq = (os_lun_t *)LUN_Q(ha, t, l);
+ if (lq == NULL) {
+ printk(KERN_INFO
+ "%s(): **** CMD derives a NULL LUN_Q\n", __func__);
+
+ return (return_status);
+ }
+ fcport_to_reset = lq->fclun->fcport;
+
+ /* If we are coming in from the back-door, stall I/O until complete. */
+ if (!cmd->device->host->eh_active)
+ set_bit(TQF_SUSPENDED, &tq->flags);
+
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): DEVICE RESET ISSUED.\n", ha->host_no, b, t, l);
+
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld): DEVICE_RESET cmd=%p jiffies = 0x%lx, timeout=%x, "
+ "dpc_flags=%lx, status=%x allowed=%d cmd.state=%x\n",
+ ha->host_no, cmd, jiffies, cmd->timeout_per_command / HZ,
+ ha->dpc_flags, cmd->result, cmd->allowed, cmd->state));
+
+ /* Clear commands from the retry queue. */
+ spin_lock(&ha->list_lock);
+ list_for_each_safe(list, temp, &ha->retry_queue) {
+ rp = list_entry(list, srb_t, list);
+
+ if (t != rp->cmd->device->id)
+ continue;
+
+ DEBUG2(printk(KERN_INFO
+ "qla2xxx_eh_reset: found in retry queue. SP=%p\n", rp));
+
+ __del_from_retry_queue(ha, rp);
+ rp->cmd->result = DID_RESET << 16;
+ __add_to_done_queue(ha, rp);
+ }
+ spin_unlock(&ha->list_lock);
+
+ spin_unlock_irq(ha->host->host_lock);
+
+ if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ DEBUG2(printk(KERN_INFO
+ "%s failed:board disabled\n",__func__));
+
+ spin_lock_irq(ha->host->host_lock);
+ goto eh_dev_reset_done;
+ }
+
+ if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
+ if (qla2x00_device_reset(ha, fcport_to_reset) == 0) {
+ return_status = SUCCESS;
+ }
+
+#if defined(LOGOUT_AFTER_DEVICE_RESET)
+ if (return_status == SUCCESS) {
+ if (fcport_to_reset->flags & FC_FABRIC_DEVICE) {
+ qla2x00_fabric_logout(ha,
+ fcport_to_reset->loop_id);
+ qla2x00_mark_device_lost(ha, fcport_to_reset);
+ }
+ }
+#endif
+ } else {
+ DEBUG2(printk(KERN_INFO
+ "%s failed: loop not ready\n",__func__);)
+ }
+
+ spin_lock_irq(ha->host->host_lock);
+
+ if (return_status == FAILED) {
+ DEBUG3(printk("%s(%ld): device reset failed\n",
+ __func__, ha->host_no));
+ qla_printk(KERN_INFO, ha, "%s: device reset failed\n",
+ __func__);
+
+ goto eh_dev_reset_done;
+ }
+
+ /*
+ * If we are coming down the EH path, wait for all commands to
+ * complete for the device.
+ */
+ if (cmd->device->host->eh_active) {
+ if (qla2x00_eh_wait_for_pending_target_commands(ha, t))
+ return_status = FAILED;
+
+ if (return_status == FAILED) {
+ DEBUG3(printk("%s(%ld): failed while waiting for "
+ "commands\n", __func__, ha->host_no));
+ qla_printk(KERN_INFO, ha,
+ "%s: failed while waiting for commands\n",
+ __func__);
+
+ goto eh_dev_reset_done;
+ }
+ }
+
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n",
+ ha->host_no, b, t, l);
+
+eh_dev_reset_done:
+
+ if (!cmd->device->host->eh_active)
+ clear_bit(TQF_SUSPENDED, &tq->flags);
+
+ return (return_status);
+}
+
+/**************************************************************************
+* qla2x00_eh_wait_for_pending_commands
+*
+* Description:
+* Waits for all the commands to come back from the specified host.
+*
+* Input:
+* ha - pointer to scsi_qla_host structure.
+*
+* Returns:
+* 1 : SUCCESS
+* 0 : FAILED
+*
+* Note:
+**************************************************************************/
+static int
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha)
+{
+ int cnt;
+ int status;
+ srb_t *sp;
+ struct scsi_cmnd *cmd;
+
+ status = 1;
+
+ /*
+ * Waiting for all commands for the designated target in the active
+ * array
+ */
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ spin_lock(&ha->hardware_lock);
+ sp = ha->outstanding_cmds[cnt];
+ if (sp) {
+ cmd = sp->cmd;
+ spin_unlock(&ha->hardware_lock);
+ status = qla2x00_eh_wait_on_command(ha, cmd);
+ if (status == 0)
+ break;
+ }
+ else {
+ spin_unlock(&ha->hardware_lock);
+ }
+ }
+ return (status);
+}
+
+
+/**************************************************************************
+* qla2xxx_eh_bus_reset
+*
+* Description:
+* The bus reset function will reset the bus and abort any executing
+* commands.
+*
+* Input:
+* cmd = Linux SCSI command packet of the command that cause the
+* bus reset.
+*
+* Returns:
+* SUCCESS/FAILURE (defined as macro in scsi.h).
+*
+**************************************************************************/
+int
+qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *ha;
+ srb_t *sp;
+ int rval = FAILED;
+
+ ha = (scsi_qla_host_t *) cmd->device->host->hostdata;
+ sp = (srb_t *) CMD_SP(cmd);
+
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): LOOP RESET ISSUED.\n", ha->host_no,
+ cmd->device->channel, cmd->device->id, cmd->device->lun);
+
+ spin_unlock_irq(ha->host->host_lock);
+
+ if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ DEBUG2(printk("%s failed:board disabled\n",__func__));
+ spin_lock_irq(ha->host->host_lock);
+ return FAILED;
+ }
+
+ if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
+ if (qla2x00_loop_reset(ha) == QLA_SUCCESS)
+ rval = SUCCESS;
+ }
+
+ spin_lock_irq(ha->host->host_lock);
+ if (rval == FAILED)
+ goto out;
+
+ /* Waiting for our command in done_queue to be returned to OS.*/
+ if (cmd->device->host->eh_active)
+ if (!qla2x00_eh_wait_for_pending_commands(ha))
+ rval = FAILED;
+
+ out:
+ qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+ (rval == FAILED) ? "failed" : "succeded");
+
+ return rval;
+}
+
+/**************************************************************************
+* qla2xxx_eh_host_reset
+*
+* Description:
+* The reset function will reset the Adapter.
+*
+* Input:
+* cmd = Linux SCSI command packet of the command that cause the
+* adapter reset.
+*
+* Returns:
+* Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+int
+qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *ha = (scsi_qla_host_t *)cmd->device->host->hostdata;
+ int rval = SUCCESS;
+
+ /* Display which one we're actually resetting for debug. */
+ DEBUG(printk("qla2xxx_eh_host_reset:Resetting scsi(%ld).\n",
+ ha->host_no));
+
+ /*
+ * Now issue reset.
+ */
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): ADAPTER RESET issued.\n", ha->host_no,
+ cmd->device->channel, cmd->device->id, cmd->device->lun);
+
+ spin_unlock_irq(ha->host->host_lock);
+
+ if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ goto board_disabled;
+
+ /*
+ * Fixme-may be dpc thread is active and processing
+ * loop_resync,so wait a while for it to
+ * be completed and then issue big hammer.Otherwise
+ * it may cause I/O failure as big hammer marks the
+ * devices as lost kicking of the port_down_timer
+ * while dpc is stuck for the mailbox to complete.
+ */
+ /* Blocking call-Does context switching if loop is Not Ready */
+ qla2x00_wait_for_loop_ready(ha);
+ set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ if (qla2x00_abort_isp(ha)) {
+ clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ /* failed. schedule dpc to try */
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+
+ if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ goto board_disabled;
+ }
+
+ clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+
+ spin_lock_irq(ha->host->host_lock);
+ if (rval == FAILED)
+ goto out;
+
+ /* Waiting for our command in done_queue to be returned to OS.*/
+ if (!qla2x00_eh_wait_for_pending_commands(ha))
+ rval = FAILED;
+
+ out:
+ qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+ (rval == FAILED) ? "failed" : "succeded");
+
+ return rval;
+
+ board_disabled:
+ spin_lock_irq(ha->host->host_lock);
+
+ qla_printk(KERN_INFO, ha, "%s: failed:board disabled\n", __func__);
+ return FAILED;
+}
+
+
+/*
+* qla2x00_loop_reset
+* Issue loop reset.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+static int
+qla2x00_loop_reset(scsi_qla_host_t *ha)
+{
+ int status = QLA_SUCCESS;
+ uint16_t t;
+ os_tgt_t *tq;
+
+ if (ha->flags.enable_lip_reset) {
+ status = qla2x00_lip_reset(ha);
+ }
+
+ if (status == QLA_SUCCESS && ha->flags.enable_target_reset) {
+ for (t = 0; t < MAX_FIBRE_DEVICES; t++) {
+ if ((tq = TGT_Q(ha, t)) == NULL)
+ continue;
+
+ if (tq->fcport == NULL)
+ continue;
+
+ status = qla2x00_target_reset(ha, 0, t);
+ if (status != QLA_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ if (status == QLA_SUCCESS &&
+ ((!ha->flags.enable_target_reset &&
+ !ha->flags.enable_lip_reset) ||
+ ha->flags.enable_lip_full_login)) {
+
+ status = qla2x00_full_login_lip(ha);
+ }
+
+ /* Issue marker command only when we are going to start the I/O */
+ ha->marker_needed = 1;
+
+ if (status) {
+ /* Empty */
+ DEBUG2_3(printk("%s(%ld): **** FAILED ****\n",
+ __func__,
+ ha->host_no);)
+ } else {
+ /* Empty */
+ DEBUG3(printk("%s(%ld): exiting normally.\n",
+ __func__,
+ ha->host_no);)
+ }
+
+ return(status);
+}
+
+/*
+ * qla2x00_device_reset
+ * Issue bus device reset message to the target.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * t = SCSI ID.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_device_reset(scsi_qla_host_t *ha, fc_port_t *reset_fcport)
+{
+ /* Abort Target command will clear Reservation */
+ return qla2x00_abort_target(reset_fcport);
+}
+
+/**************************************************************************
+* qla2xxx_slave_configure
+*
+* Description:
+**************************************************************************/
+int
+qla2xxx_slave_configure(struct scsi_device *sdev)
+{
+ scsi_qla_host_t *ha = to_qla_host(sdev->host);
+ int queue_depth;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ queue_depth = 16;
+ else
+ queue_depth = 32;
+
+ if (sdev->tagged_supported) {
+ if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
+ queue_depth = ql2xmaxqdepth;
+
+ ql2xmaxqdepth = queue_depth;
+
+ scsi_activate_tcq(sdev, queue_depth);
+
+ qla_printk(KERN_INFO, ha,
+ "scsi(%d:%d:%d:%d): Enabled tagged queuing, queue "
+ "depth %d.\n",
+ sdev->host->host_no, sdev->channel, sdev->id, sdev->lun,
+ sdev->queue_depth);
+ } else {
+ scsi_adjust_queue_depth(sdev, 0 /* TCQ off */,
+ sdev->host->hostt->cmd_per_lun /* 3 */);
+ }
+
+ return (0);
+}
+
+/**
+ * qla2x00_config_dma_addressing() - Configure OS DMA addressing method.
+ * @ha: HA context
+ *
+ * At exit, the @ha's flags.enable_64bit_addressing set to indicated
+ * supported addressing method.
+ */
+static void
+qla2x00_config_dma_addressing(scsi_qla_host_t *ha)
+{
+ /* Assume 32bit DMA address */
+ ha->flags.enable_64bit_addressing = 0;
+ ha->calc_request_entries = qla2x00_calc_iocbs_32;
+ ha->build_scsi_iocbs = qla2x00_build_scsi_iocbs_32;
+
+ /*
+ * Given the two variants pci_set_dma_mask(), allow the compiler to
+ * assist in setting the proper dma mask.
+ */
+ if (sizeof(dma_addr_t) > 4) {
+ if (pci_set_dma_mask(ha->pdev, DMA_64BIT_MASK) == 0) {
+ ha->flags.enable_64bit_addressing = 1;
+ ha->calc_request_entries = qla2x00_calc_iocbs_64;
+ ha->build_scsi_iocbs = qla2x00_build_scsi_iocbs_64;
+
+ if (pci_set_consistent_dma_mask(ha->pdev,
+ DMA_64BIT_MASK)) {
+ qla_printk(KERN_DEBUG, ha,
+ "Failed to set 64 bit PCI consistent mask; "
+ "using 32 bit.\n");
+ pci_set_consistent_dma_mask(ha->pdev,
+ DMA_32BIT_MASK);
+ }
+ } else {
+ qla_printk(KERN_DEBUG, ha,
+ "Failed to set 64 bit PCI DMA mask, falling back "
+ "to 32 bit MASK.\n");
+ pci_set_dma_mask(ha->pdev, DMA_32BIT_MASK);
+ }
+ } else {
+ pci_set_dma_mask(ha->pdev, DMA_32BIT_MASK);
+ }
+}
+
+static int
+qla2x00_iospace_config(scsi_qla_host_t *ha)
+{
+ unsigned long pio, pio_len, pio_flags;
+ unsigned long mmio, mmio_len, mmio_flags;
+
+ /* We only need PIO for Flash operations on ISP2312 v2 chips. */
+ pio = pci_resource_start(ha->pdev, 0);
+ pio_len = pci_resource_len(ha->pdev, 0);
+ pio_flags = pci_resource_flags(ha->pdev, 0);
+ if (pio_flags & IORESOURCE_IO) {
+ if (pio_len < MIN_IOBASE_LEN) {
+ qla_printk(KERN_WARNING, ha,
+ "Invalid PCI I/O region size (%s)...\n",
+ pci_name(ha->pdev));
+ pio = 0;
+ }
+ } else {
+ qla_printk(KERN_WARNING, ha,
+ "region #0 not a PIO resource (%s)...\n",
+ pci_name(ha->pdev));
+ pio = 0;
+ }
+
+ /* Use MMIO operations for all accesses. */
+ mmio = pci_resource_start(ha->pdev, 1);
+ mmio_len = pci_resource_len(ha->pdev, 1);
+ mmio_flags = pci_resource_flags(ha->pdev, 1);
+
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ qla_printk(KERN_ERR, ha,
+ "region #0 not an MMIO resource (%s), aborting\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+ if (mmio_len < MIN_IOBASE_LEN) {
+ qla_printk(KERN_ERR, ha,
+ "Invalid PCI mem region size (%s), aborting\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ if (pci_request_regions(ha->pdev, ha->brd_info->drv_name)) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to reserve PIO/MMIO regions (%s)\n",
+ pci_name(ha->pdev));
+
+ goto iospace_error_exit;
+ }
+
+ ha->pio_address = pio;
+ ha->pio_length = pio_len;
+ ha->iobase = ioremap(mmio, MIN_IOBASE_LEN);
+ if (!ha->iobase) {
+ qla_printk(KERN_ERR, ha,
+ "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev));
+
+ goto iospace_error_exit;
+ }
+
+ return (0);
+
+iospace_error_exit:
+ return (-ENOMEM);
+}
+
+/*
+ * PCI driver interface
+ */
+int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info)
+{
+ int ret;
+ device_reg_t __iomem *reg;
+ struct Scsi_Host *host;
+ scsi_qla_host_t *ha;
+ unsigned long flags = 0;
+ unsigned long wait_switch = 0;
+ char pci_info[20];
+ char fw_str[30];
+
+ if (pci_enable_device(pdev))
+ return -1;
+
+ host = scsi_host_alloc(&qla2x00_driver_template,
+ sizeof(scsi_qla_host_t));
+ if (host == NULL) {
+ printk(KERN_WARNING
+ "qla2xxx: Couldn't allocate host from scsi layer!\n");
+ goto probe_disable_device;
+ }
+
+ /* Clear our data area */
+ ha = (scsi_qla_host_t *)host->hostdata;
+ memset(ha, 0, sizeof(scsi_qla_host_t));
+
+ ha->pdev = pdev;
+ ha->host = host;
+ ha->host_no = host->host_no;
+ ha->brd_info = brd_info;
+ sprintf(ha->host_str, "%s_%ld", ha->brd_info->drv_name, ha->host_no);
+
+ /* Configure PCI I/O space */
+ ret = qla2x00_iospace_config(ha);
+ if (ret != 0) {
+ goto probe_failed;
+ }
+
+ /* Sanitize the information from PCI BIOS. */
+ host->irq = pdev->irq;
+
+ qla_printk(KERN_INFO, ha,
+ "Found an %s, irq %d, iobase 0x%p\n", ha->brd_info->isp_name,
+ host->irq, ha->iobase);
+
+ spin_lock_init(&ha->hardware_lock);
+
+ /* 4.23 Initialize /proc/scsi/qla2x00 counters */
+ ha->actthreads = 0;
+ ha->qthreads = 0;
+ ha->total_isr_cnt = 0;
+ ha->total_isp_aborts = 0;
+ ha->total_lip_cnt = 0;
+ ha->total_dev_errs = 0;
+ ha->total_ios = 0;
+ ha->total_bytes = 0;
+
+ ha->prev_topology = 0;
+ ha->ports = MAX_BUSES;
+
+ if (IS_QLA2100(ha)) {
+ ha->max_targets = MAX_TARGETS_2100;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
+ ha->request_q_length = REQUEST_ENTRY_CNT_2100;
+ ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
+ ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
+ host->sg_tablesize = 32;
+ } else if (IS_QLA2200(ha)) {
+ ha->max_targets = MAX_TARGETS_2200;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ ha->request_q_length = REQUEST_ENTRY_CNT_2200;
+ ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
+ ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
+ } else /*if (IS_QLA2300(ha))*/ {
+ ha->max_targets = MAX_TARGETS_2200;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ ha->request_q_length = REQUEST_ENTRY_CNT_2200;
+ ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
+ ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ }
+ host->can_queue = ha->request_q_length + 128;
+
+ /* load the F/W, read paramaters, and init the H/W */
+ ha->instance = num_hosts;
+
+ init_MUTEX(&ha->mbx_cmd_sem);
+ init_MUTEX_LOCKED(&ha->mbx_intr_sem);
+
+ INIT_LIST_HEAD(&ha->list);
+ INIT_LIST_HEAD(&ha->fcports);
+ INIT_LIST_HEAD(&ha->rscn_fcports);
+ INIT_LIST_HEAD(&ha->done_queue);
+ INIT_LIST_HEAD(&ha->retry_queue);
+ INIT_LIST_HEAD(&ha->scsi_retry_queue);
+ INIT_LIST_HEAD(&ha->pending_queue);
+
+ /*
+ * These locks are used to prevent more than one CPU
+ * from modifying the queue at the same time. The
+ * higher level "host_lock" will reduce most
+ * contention for these locks.
+ */
+ spin_lock_init(&ha->mbx_reg_lock);
+ spin_lock_init(&ha->list_lock);
+
+ ha->dpc_pid = -1;
+ init_completion(&ha->dpc_inited);
+ init_completion(&ha->dpc_exited);
+
+ qla2x00_config_dma_addressing(ha);
+ if (qla2x00_mem_alloc(ha)) {
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to allocate memory for adapter\n");
+
+ goto probe_failed;
+ }
+
+ if (qla2x00_initialize_adapter(ha) &&
+ !(ha->device_flags & DFLG_NO_CABLE)) {
+
+ qla_printk(KERN_WARNING, ha,
+ "Failed to initialize adapter\n");
+
+ DEBUG2(printk("scsi(%ld): Failed to initialize adapter - "
+ "Adapter flags %x.\n",
+ ha->host_no, ha->device_flags));
+
+ goto probe_failed;
+ }
+
+ /*
+ * Startup the kernel thread for this host adapter
+ */
+ ha->dpc_should_die = 0;
+ ha->dpc_pid = kernel_thread(qla2x00_do_dpc, ha, 0);
+ if (ha->dpc_pid < 0) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to start DPC thread!\n");
+
+ goto probe_failed;
+ }
+ wait_for_completion(&ha->dpc_inited);
+
+ host->this_id = 255;
+ host->cmd_per_lun = 3;
+ host->max_cmd_len = MAX_CMDSZ;
+ host->max_channel = ha->ports - 1;
+ host->max_lun = ha->max_luns;
+ BUG_ON(qla2xxx_transport_template == NULL);
+ host->transportt = qla2xxx_transport_template;
+ host->unique_id = ha->instance;
+ host->max_id = ha->max_targets;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ ret = request_irq(host->irq, qla2100_intr_handler,
+ SA_INTERRUPT|SA_SHIRQ, ha->brd_info->drv_name, ha);
+ else
+ ret = request_irq(host->irq, qla2300_intr_handler,
+ SA_INTERRUPT|SA_SHIRQ, ha->brd_info->drv_name, ha);
+ if (ret != 0) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to reserve interrupt %d already in use.\n",
+ host->irq);
+ goto probe_failed;
+ }
+
+ /* Initialized the timer */
+ qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL);
+
+ DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
+ ha->host_no, ha));
+
+ reg = ha->iobase;
+
+ /* Disable ISP interrupts. */
+ qla2x00_disable_intrs(ha);
+
+ /* Ensure mailbox registers are free. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_HOST_INT);
+
+ /* Enable proper parity */
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha)) {
+ if (IS_QLA2300(ha))
+ /* SRAM parity */
+ WRT_REG_WORD(&reg->hccr, (HCCR_ENABLE_PARITY + 0x1));
+ else
+ /* SRAM, Instruction RAM and GP RAM parity */
+ WRT_REG_WORD(&reg->hccr, (HCCR_ENABLE_PARITY + 0x7));
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Enable chip interrupts. */
+ qla2x00_enable_intrs(ha);
+
+ /* v2.19.5b6 */
+ /*
+ * Wait around max loop_reset_delay secs for the devices to come
+ * on-line. We don't want Linux scanning before we are ready.
+ *
+ */
+ for (wait_switch = jiffies + (ha->loop_reset_delay * HZ);
+ time_before(jiffies,wait_switch) &&
+ !(ha->device_flags & (DFLG_NO_CABLE | DFLG_FABRIC_DEVICES))
+ && (ha->device_flags & SWITCH_FOUND) ;) {
+
+ qla2x00_check_fabric_devices(ha);
+
+ msleep(10);
+ }
+
+ pci_set_drvdata(pdev, ha);
+ ha->flags.init_done = 1;
+ num_hosts++;
+
+ /* List the target we have found */
+ if (displayConfig) {
+ qla2x00_display_fc_names(ha);
+ }
+
+ if (scsi_add_host(host, &pdev->dev))
+ goto probe_failed;
+
+ sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
+ sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
+
+ qla_printk(KERN_INFO, ha, "\n"
+ " QLogic Fibre Channel HBA Driver: %s\n"
+ " QLogic %s - %s\n"
+ " %s: %s @ %s hdma%c, host#=%ld, fw=%s\n", qla2x00_version_str,
+ ha->model_number, ha->model_desc ? ha->model_desc: "",
+ ha->brd_info->isp_name, qla2x00_get_pci_info_str(ha, pci_info),
+ pci_name(ha->pdev), ha->flags.enable_64bit_addressing ? '+': '-',
+ ha->host_no, qla2x00_get_fw_version_str(ha, fw_str));
+
+ if (ql2xdoinitscan)
+ scsi_scan_host(host);
+
+ return 0;
+
+probe_failed:
+ qla2x00_free_device(ha);
+
+ scsi_host_put(host);
+
+probe_disable_device:
+ pci_disable_device(pdev);
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(qla2x00_probe_one);
+
+void qla2x00_remove_one(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *ha;
+
+ ha = pci_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&ha->host->shost_gendev.kobj,
+ &sysfs_fw_dump_attr);
+ sysfs_remove_bin_file(&ha->host->shost_gendev.kobj, &sysfs_nvram_attr);
+
+ scsi_remove_host(ha->host);
+
+ qla2x00_free_device(ha);
+
+ scsi_host_put(ha->host);
+
+ pci_set_drvdata(pdev, NULL);
+}
+EXPORT_SYMBOL_GPL(qla2x00_remove_one);
+
+static void
+qla2x00_free_device(scsi_qla_host_t *ha)
+{
+ int ret;
+
+ /* Abort any outstanding IO descriptors. */
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha))
+ qla2x00_cancel_io_descriptors(ha);
+
+ /* turn-off interrupts on the card */
+ if (ha->interrupts_on)
+ qla2x00_disable_intrs(ha);
+
+ /* Disable timer */
+ if (ha->timer_active)
+ qla2x00_stop_timer(ha);
+
+ /* Kill the kernel thread for this host */
+ if (ha->dpc_pid >= 0) {
+ ha->dpc_should_die = 1;
+ wmb();
+ ret = kill_proc(ha->dpc_pid, SIGHUP, 1);
+ if (ret) {
+ qla_printk(KERN_ERR, ha,
+ "Unable to signal DPC thread -- (%d)\n", ret);
+
+ /* TODO: SOMETHING MORE??? */
+ } else {
+ wait_for_completion(&ha->dpc_exited);
+ }
+ }
+
+ qla2x00_mem_free(ha);
+
+
+ ha->flags.online = 0;
+
+ /* Detach interrupts */
+ if (ha->pdev->irq)
+ free_irq(ha->pdev->irq, ha);
+
+ /* release io space registers */
+ if (ha->iobase)
+ iounmap(ha->iobase);
+ pci_release_regions(ha->pdev);
+
+ pci_disable_device(ha->pdev);
+}
+
+
+/*
+ * The following support functions are adopted to handle
+ * the re-entrant qla2x00_proc_info correctly.
+ */
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->offset + info->length)
+ len = info->offset + info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+
+ if (info->pos < info->offset) {
+ off_t partial;
+
+ partial = info->offset - info->pos;
+ data += partial;
+ info->pos += partial;
+ len -= partial;
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer, data, len);
+ info->pos += len;
+ info->buffer += len;
+ }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+
+ return (len);
+}
+
+/*************************************************************************
+* qla2x00_proc_info
+*
+* Description:
+* Return information to handle /proc support for the driver.
+*
+* inout : decides the direction of the dataflow and the meaning of the
+* variables
+* buffer: If inout==0 data is being written to it else read from it
+* (ptr to a page buffer)
+* *start: If inout==0 start of the valid data in the buffer
+* offset: If inout==0 starting offset from the beginning of all
+* possible data to return.
+* length: If inout==0 max number of bytes to be written into the buffer
+* else number of bytes in "buffer"
+* Returns:
+* < 0: error. errno value.
+* >= 0: sizeof data returned.
+*************************************************************************/
+int
+qla2x00_proc_info(struct Scsi_Host *shost, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ struct info_str info;
+ int retval = -EINVAL;
+ os_lun_t *up;
+ os_tgt_t *tq;
+ unsigned int t, l;
+ uint32_t tmp_sn;
+ uint32_t *flags;
+ uint8_t *loop_state;
+ scsi_qla_host_t *ha;
+ char fw_info[30];
+
+ DEBUG3(printk(KERN_INFO
+ "Entering proc_info buff_in=%p, offset=0x%lx, length=0x%x\n",
+ buffer, offset, length);)
+
+ ha = (scsi_qla_host_t *) shost->hostdata;
+
+ if (inout) {
+ /* Has data been written to the file? */
+ DEBUG3(printk(
+ "%s: has data been written to the file. \n",
+ __func__);)
+
+ return -ENOSYS;
+ }
+
+ if (start) {
+ *start = buffer;
+ }
+
+ info.buffer = buffer;
+ info.length = length;
+ info.offset = offset;
+ info.pos = 0;
+
+ /* start building the print buffer */
+ copy_info(&info,
+ "QLogic PCI to Fibre Channel Host Adapter for %s:\n"
+ " Firmware version %s, ",
+ ha->model_number, qla2x00_get_fw_version_str(ha, fw_info));
+
+ copy_info(&info, "Driver version %s\n", qla2x00_version_str);
+
+ tmp_sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) |
+ ha->serial1;
+ copy_info(&info, "ISP: %s, Serial# %c%05d\n",
+ ha->brd_info->isp_name, ('A' + tmp_sn/100000), (tmp_sn%100000));
+
+ copy_info(&info,
+ "Request Queue = 0x%llx, Response Queue = 0x%llx\n",
+ (unsigned long long)ha->request_dma,
+ (unsigned long long)ha->response_dma);
+
+ copy_info(&info,
+ "Request Queue count = %d, Response Queue count = %d\n",
+ ha->request_q_length, ha->response_q_length);
+
+ copy_info(&info,
+ "Total number of active commands = %ld\n",
+ ha->actthreads);
+
+ copy_info(&info,
+ "Total number of interrupts = %ld\n",
+ (long)ha->total_isr_cnt);
+
+ copy_info(&info,
+ " Device queue depth = 0x%x\n",
+ (ql2xmaxqdepth == 0) ? 16 : ql2xmaxqdepth);
+
+ copy_info(&info,
+ "Number of free request entries = %d\n", ha->req_q_cnt);
+
+ copy_info(&info,
+ "Number of mailbox timeouts = %ld\n", ha->total_mbx_timeout);
+
+ copy_info(&info,
+ "Number of ISP aborts = %ld\n", ha->total_isp_aborts);
+
+ copy_info(&info,
+ "Number of loop resyncs = %ld\n", ha->total_loop_resync);
+
+ copy_info(&info,
+ "Number of retries for empty slots = %ld\n",
+ qla2x00_stats.outarray_full);
+
+ copy_info(&info,
+ "Number of reqs in pending_q= %ld, retry_q= %d, "
+ "done_q= %ld, scsi_retry_q= %d\n",
+ ha->qthreads, ha->retry_q_cnt,
+ ha->done_q_cnt, ha->scsi_retry_q_cnt);
+
+
+ flags = (uint32_t *) &ha->flags;
+
+ if (atomic_read(&ha->loop_state) == LOOP_DOWN) {
+ loop_state = "DOWN";
+ } else if (atomic_read(&ha->loop_state) == LOOP_UP) {
+ loop_state = "UP";
+ } else if (atomic_read(&ha->loop_state) == LOOP_READY) {
+ loop_state = "READY";
+ } else if (atomic_read(&ha->loop_state) == LOOP_TIMEOUT) {
+ loop_state = "TIMEOUT";
+ } else if (atomic_read(&ha->loop_state) == LOOP_UPDATE) {
+ loop_state = "UPDATE";
+ } else {
+ loop_state = "UNKNOWN";
+ }
+
+ copy_info(&info,
+ "Host adapter:loop state = <%s>, flags = 0x%lx\n",
+ loop_state , *flags);
+
+ copy_info(&info, "Dpc flags = 0x%lx\n", ha->dpc_flags);
+
+ copy_info(&info, "MBX flags = 0x%x\n", ha->mbx_flags);
+
+ copy_info(&info, "Link down Timeout = %3.3d\n",
+ ha->link_down_timeout);
+
+ copy_info(&info, "Port down retry = %3.3d\n",
+ ha->port_down_retry_count);
+
+ copy_info(&info, "Login retry count = %3.3d\n",
+ ha->login_retry_count);
+
+ copy_info(&info,
+ "Commands retried with dropped frame(s) = %d\n",
+ ha->dropped_frame_error_cnt);
+
+ copy_info(&info,
+ "Product ID = %04x %04x %04x %04x\n", ha->product_id[0],
+ ha->product_id[1], ha->product_id[2], ha->product_id[3]);
+
+ copy_info(&info, "\n");
+
+ /* 2.25 node/port display to proc */
+ /* Display the node name for adapter */
+ copy_info(&info, "\nSCSI Device Information:\n");
+ copy_info(&info,
+ "scsi-qla%d-adapter-node="
+ "%02x%02x%02x%02x%02x%02x%02x%02x;\n",
+ (int)ha->instance,
+ ha->init_cb->node_name[0],
+ ha->init_cb->node_name[1],
+ ha->init_cb->node_name[2],
+ ha->init_cb->node_name[3],
+ ha->init_cb->node_name[4],
+ ha->init_cb->node_name[5],
+ ha->init_cb->node_name[6],
+ ha->init_cb->node_name[7]);
+
+ /* display the port name for adapter */
+ copy_info(&info,
+ "scsi-qla%d-adapter-port="
+ "%02x%02x%02x%02x%02x%02x%02x%02x;\n",
+ (int)ha->instance,
+ ha->init_cb->port_name[0],
+ ha->init_cb->port_name[1],
+ ha->init_cb->port_name[2],
+ ha->init_cb->port_name[3],
+ ha->init_cb->port_name[4],
+ ha->init_cb->port_name[5],
+ ha->init_cb->port_name[6],
+ ha->init_cb->port_name[7]);
+
+ /* Print out device port names */
+ for (t = 0; t < MAX_FIBRE_DEVICES; t++) {
+ if ((tq = TGT_Q(ha, t)) == NULL)
+ continue;
+
+ copy_info(&info,
+ "scsi-qla%d-target-%d="
+ "%02x%02x%02x%02x%02x%02x%02x%02x;\n",
+ (int)ha->instance, t,
+ tq->port_name[0], tq->port_name[1],
+ tq->port_name[2], tq->port_name[3],
+ tq->port_name[4], tq->port_name[5],
+ tq->port_name[6], tq->port_name[7]);
+ }
+
+ copy_info(&info, "\nSCSI LUN Information:\n");
+ copy_info(&info,
+ "(Id:Lun) * - indicates lun is not registered with the OS.\n");
+
+ /* scan for all equipment stats */
+ for (t = 0; t < MAX_FIBRE_DEVICES; t++) {
+ /* scan all luns */
+ for (l = 0; l < ha->max_luns; l++) {
+ up = (os_lun_t *) GET_LU_Q(ha, t, l);
+
+ if (up == NULL) {
+ continue;
+ }
+ if (up->fclun == NULL) {
+ continue;
+ }
+
+ copy_info(&info,
+ "(%2d:%2d): Total reqs %ld,",
+ t,l,up->io_cnt);
+
+ copy_info(&info,
+ " Pending reqs %ld,",
+ up->out_cnt);
+
+ if (up->io_cnt < 4) {
+ copy_info(&info,
+ " flags 0x%x*,",
+ (int)up->q_flag);
+ } else {
+ copy_info(&info,
+ " flags 0x%x,",
+ (int)up->q_flag);
+ }
+
+ copy_info(&info,
+ " %ld:%d:%02x %02x",
+ up->fclun->fcport->ha->instance,
+ up->fclun->fcport->cur_path,
+ up->fclun->fcport->loop_id,
+ up->fclun->device_type);
+
+ copy_info(&info, "\n");
+
+ if (info.pos >= info.offset + info.length) {
+ /* No need to continue */
+ goto profile_stop;
+ }
+ }
+
+ if (info.pos >= info.offset + info.length) {
+ /* No need to continue */
+ break;
+ }
+ }
+
+profile_stop:
+
+ retval = info.pos > info.offset ? info.pos - info.offset : 0;
+
+ DEBUG3(printk(KERN_INFO
+ "Exiting proc_info: info.pos=%d, offset=0x%lx, "
+ "length=0x%x\n", info.pos, offset, length);)
+
+ return (retval);
+}
+
+/*
+* qla2x00_display_fc_names
+* This routine will the node names of the different devices found
+* after port inquiry.
+*
+* Input:
+* cmd = SCSI command structure
+*
+* Returns:
+* None.
+*/
+static void
+qla2x00_display_fc_names(scsi_qla_host_t *ha)
+{
+ uint16_t tgt;
+ os_tgt_t *tq;
+
+ /* Display the node name for adapter */
+ qla_printk(KERN_INFO, ha,
+ "scsi-qla%d-adapter-node=%02x%02x%02x%02x%02x%02x%02x%02x\\;\n",
+ (int)ha->instance,
+ ha->init_cb->node_name[0],
+ ha->init_cb->node_name[1],
+ ha->init_cb->node_name[2],
+ ha->init_cb->node_name[3],
+ ha->init_cb->node_name[4],
+ ha->init_cb->node_name[5],
+ ha->init_cb->node_name[6],
+ ha->init_cb->node_name[7]);
+
+ /* display the port name for adapter */
+ qla_printk(KERN_INFO, ha,
+ "scsi-qla%d-adapter-port=%02x%02x%02x%02x%02x%02x%02x%02x\\;\n",
+ (int)ha->instance,
+ ha->init_cb->port_name[0],
+ ha->init_cb->port_name[1],
+ ha->init_cb->port_name[2],
+ ha->init_cb->port_name[3],
+ ha->init_cb->port_name[4],
+ ha->init_cb->port_name[5],
+ ha->init_cb->port_name[6],
+ ha->init_cb->port_name[7]);
+
+ /* Print out device port names */
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ if ((tq = ha->otgt[tgt]) == NULL)
+ continue;
+
+ if (tq->fcport == NULL)
+ continue;
+
+ switch (ha->binding_type) {
+ case BIND_BY_PORT_NAME:
+ qla_printk(KERN_INFO, ha,
+ "scsi-qla%d-tgt-%d-di-0-port="
+ "%02x%02x%02x%02x%02x%02x%02x%02x\\;\n",
+ (int)ha->instance,
+ tgt,
+ tq->port_name[0],
+ tq->port_name[1],
+ tq->port_name[2],
+ tq->port_name[3],
+ tq->port_name[4],
+ tq->port_name[5],
+ tq->port_name[6],
+ tq->port_name[7]);
+
+ break;
+
+ case BIND_BY_PORT_ID:
+ qla_printk(KERN_INFO, ha,
+ "scsi-qla%d-tgt-%d-di-0-pid="
+ "%02x%02x%02x\\;\n",
+ (int)ha->instance,
+ tgt,
+ tq->d_id.b.domain,
+ tq->d_id.b.area,
+ tq->d_id.b.al_pa);
+ break;
+ }
+
+#if VSA
+ qla_printk(KERN_INFO, ha,
+ "scsi-qla%d-target-%d-vsa=01;\n", (int)ha->instance, tgt);
+#endif
+ }
+}
+
+/*
+ * qla2x00_suspend_lun
+ * Suspend lun and start port down timer
+ *
+ * Input:
+ * ha = visable adapter block pointer.
+ * lq = lun queue
+ * cp = Scsi command pointer
+ * time = time in seconds
+ * count = number of times to let time expire
+ * delay_lun = non-zero, if lun should be delayed rather than suspended
+ *
+ * Return:
+ * QLA_SUCCESS -- suspended lun
+ * QLA_FUNCTION_FAILED -- Didn't suspend lun
+ *
+ * Context:
+ * Interrupt context.
+ */
+int
+__qla2x00_suspend_lun(scsi_qla_host_t *ha,
+ os_lun_t *lq, int time, int count, int delay_lun)
+{
+ int rval;
+ srb_t *sp;
+ struct list_head *list, *temp;
+ unsigned long flags;
+
+ rval = QLA_SUCCESS;
+
+ /* if the lun_q is already suspended then don't do it again */
+ if (lq->q_state == LUN_STATE_READY ||lq->q_state == LUN_STATE_RUN) {
+
+ spin_lock_irqsave(&lq->q_lock, flags);
+ if (lq->q_state == LUN_STATE_READY) {
+ lq->q_max = count;
+ lq->q_count = 0;
+ }
+ /* Set the suspend time usually 6 secs */
+ atomic_set(&lq->q_timer, time);
+
+ /* now suspend the lun */
+ lq->q_state = LUN_STATE_WAIT;
+
+ if (delay_lun) {
+ set_bit(LUN_EXEC_DELAYED, &lq->q_flag);
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): Delay lun execution for %d secs, "
+ "count=%d, max count=%d, state=%d\n",
+ ha->host_no,
+ time,
+ lq->q_count, lq->q_max, lq->q_state));
+ } else {
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): Suspend lun for %d secs, count=%d, "
+ "max count=%d, state=%d\n",
+ ha->host_no,
+ time,
+ lq->q_count, lq->q_max, lq->q_state));
+ }
+ spin_unlock_irqrestore(&lq->q_lock, flags);
+
+ /*
+ * Remove all pending commands from request queue and put them
+ * in the scsi_retry queue.
+ */
+ spin_lock_irqsave(&ha->list_lock, flags);
+ list_for_each_safe(list, temp, &ha->pending_queue) {
+ sp = list_entry(list, srb_t, list);
+ if (sp->lun_queue != lq)
+ continue;
+
+ __del_from_pending_queue(ha, sp);
+
+ if (sp->cmd->allowed < count)
+ sp->cmd->allowed = count;
+ __add_to_scsi_retry_queue(ha, sp);
+
+ } /* list_for_each_safe */
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+ rval = QLA_SUCCESS;
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_mark_device_lost Updates fcport state when device goes offline.
+ *
+ * Input: ha = adapter block pointer. fcport = port structure pointer.
+ *
+ * Return: None.
+ *
+ * Context:
+ */
+void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
+ int do_login)
+{
+ /*
+ * We may need to retry the login, so don't change the state of the
+ * port but do the retries.
+ */
+ if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD)
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+
+ if (!do_login)
+ return;
+
+ if (fcport->login_retry == 0) {
+ fcport->login_retry = ha->login_retry_count;
+ set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
+
+ DEBUG(printk("scsi(%ld): Port login retry: "
+ "%02x%02x%02x%02x%02x%02x%02x%02x, "
+ "id = 0x%04x retry cnt=%d\n",
+ ha->host_no,
+ fcport->port_name[0],
+ fcport->port_name[1],
+ fcport->port_name[2],
+ fcport->port_name[3],
+ fcport->port_name[4],
+ fcport->port_name[5],
+ fcport->port_name[6],
+ fcport->port_name[7],
+ fcport->loop_id,
+ fcport->login_retry));
+ }
+}
+
+/*
+ * qla2x00_mark_all_devices_lost
+ * Updates fcport state when device goes offline.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fcport = port structure pointer.
+ *
+ * Return:
+ * None.
+ *
+ * Context:
+ */
+void
+qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha)
+{
+ fc_port_t *fcport;
+
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ /*
+ * No point in marking the device as lost, if the device is
+ * already DEAD.
+ */
+ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
+ continue;
+
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ }
+}
+
+/*
+* qla2x00_mem_alloc
+* Allocates adapter memory.
+*
+* Returns:
+* 0 = success.
+* 1 = failure.
+*/
+static uint8_t
+qla2x00_mem_alloc(scsi_qla_host_t *ha)
+{
+ char name[16];
+ uint8_t status = 1;
+ int retry= 10;
+
+ do {
+ /*
+ * This will loop only once if everything goes well, else some
+ * number of retries will be performed to get around a kernel
+ * bug where available mem is not allocated until after a
+ * little delay and a retry.
+ */
+ ha->request_ring = dma_alloc_coherent(&ha->pdev->dev,
+ (ha->request_q_length + 1) * sizeof(request_t),
+ &ha->request_dma, GFP_KERNEL);
+ if (ha->request_ring == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - request_ring\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ ha->response_ring = dma_alloc_coherent(&ha->pdev->dev,
+ (ha->response_q_length + 1) * sizeof(response_t),
+ &ha->response_dma, GFP_KERNEL);
+ if (ha->response_ring == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - response_ring\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
+ &ha->gid_list_dma, GFP_KERNEL);
+ if (ha->gid_list == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - gid_list\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ ha->rlc_rsp = dma_alloc_coherent(&ha->pdev->dev,
+ sizeof(rpt_lun_cmd_rsp_t), &ha->rlc_rsp_dma, GFP_KERNEL);
+ if (ha->rlc_rsp == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - rlc");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ snprintf(name, sizeof(name), "qla2xxx_%ld", ha->host_no);
+ ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+ DMA_POOL_SIZE, 8, 0);
+ if (ha->s_dma_pool == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - s_dma_pool\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ /* get consistent memory allocated for init control block */
+ ha->init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->init_cb_dma);
+ if (ha->init_cb == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - init_cb\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+ memset(ha->init_cb, 0, sizeof(init_cb_t));
+
+ /* Get consistent memory allocated for Get Port Database cmd */
+ ha->iodesc_pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->iodesc_pd_dma);
+ if (ha->iodesc_pd == NULL) {
+ /* error */
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - iodesc_pd\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+ memset(ha->iodesc_pd, 0, PORT_DATABASE_SIZE);
+
+ /* Allocate ioctl related memory. */
+ if (qla2x00_alloc_ioctl_mem(ha)) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - ioctl_mem\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ if (qla2x00_allocate_sp_pool(ha)) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - "
+ "qla2x00_allocate_sp_pool()\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+
+ /* Allocate memory for SNS commands */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ /* Get consistent memory allocated for SNS commands */
+ ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
+ sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma,
+ GFP_KERNEL);
+ if (ha->sns_cmd == NULL) {
+ /* error */
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - sns_cmd\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+ memset(ha->sns_cmd, 0, sizeof(struct sns_cmd_pkt));
+ } else {
+ /* Get consistent memory allocated for MS IOCB */
+ ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->ms_iocb_dma);
+ if (ha->ms_iocb == NULL) {
+ /* error */
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - ms_iocb\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+ memset(ha->ms_iocb, 0, sizeof(ms_iocb_entry_t));
+
+ /*
+ * Get consistent memory allocated for CT SNS
+ * commands
+ */
+ ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
+ sizeof(struct ct_sns_pkt), &ha->ct_sns_dma,
+ GFP_KERNEL);
+ if (ha->ct_sns == NULL) {
+ /* error */
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - ct_sns\n");
+
+ qla2x00_mem_free(ha);
+ msleep(100);
+
+ continue;
+ }
+ memset(ha->ct_sns, 0, sizeof(struct ct_sns_pkt));
+ }
+
+ /* Done all allocations without any error. */
+ status = 0;
+
+ } while (retry-- && status != 0);
+
+ if (status) {
+ printk(KERN_WARNING
+ "%s(): **** FAILED ****\n", __func__);
+ }
+
+ return(status);
+}
+
+/*
+* qla2x00_mem_free
+* Frees all adapter allocated memory.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+static void
+qla2x00_mem_free(scsi_qla_host_t *ha)
+{
+ uint32_t t;
+ struct list_head *fcpl, *fcptemp;
+ fc_port_t *fcport;
+ struct list_head *fcll, *fcltemp;
+ fc_lun_t *fclun;
+ unsigned long wtime;/* max wait time if mbx cmd is busy. */
+
+ if (ha == NULL) {
+ /* error */
+ DEBUG2(printk("%s(): ERROR invalid ha pointer.\n", __func__));
+ return;
+ }
+
+ /* Free the target queues */
+ for (t = 0; t < MAX_TARGETS; t++) {
+ qla2x00_tgt_free(ha, t);
+ }
+
+ /* Make sure all other threads are stopped. */
+ wtime = 60 * HZ;
+ while (ha->dpc_wait && wtime) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ wtime = schedule_timeout(wtime);
+ }
+
+ /* free ioctl memory */
+ qla2x00_free_ioctl_mem(ha);
+
+ /* free sp pool */
+ qla2x00_free_sp_pool(ha);
+
+ if (ha->sns_cmd)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
+ ha->sns_cmd, ha->sns_cmd_dma);
+
+ if (ha->ct_sns)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
+ ha->ct_sns, ha->ct_sns_dma);
+
+ if (ha->ms_iocb)
+ dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
+
+ if (ha->iodesc_pd)
+ dma_pool_free(ha->s_dma_pool, ha->iodesc_pd, ha->iodesc_pd_dma);
+
+ if (ha->init_cb)
+ dma_pool_free(ha->s_dma_pool, ha->init_cb, ha->init_cb_dma);
+
+ if (ha->s_dma_pool)
+ dma_pool_destroy(ha->s_dma_pool);
+
+ if (ha->rlc_rsp)
+ dma_free_coherent(&ha->pdev->dev,
+ sizeof(rpt_lun_cmd_rsp_t), ha->rlc_rsp,
+ ha->rlc_rsp_dma);
+
+ if (ha->gid_list)
+ dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
+ ha->gid_list_dma);
+
+ if (ha->response_ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (ha->response_q_length + 1) * sizeof(response_t),
+ ha->response_ring, ha->response_dma);
+
+ if (ha->request_ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (ha->request_q_length + 1) * sizeof(request_t),
+ ha->request_ring, ha->request_dma);
+
+ ha->sns_cmd = NULL;
+ ha->sns_cmd_dma = 0;
+ ha->ct_sns = NULL;
+ ha->ct_sns_dma = 0;
+ ha->ms_iocb = NULL;
+ ha->ms_iocb_dma = 0;
+ ha->iodesc_pd = NULL;
+ ha->iodesc_pd_dma = 0;
+ ha->init_cb = NULL;
+ ha->init_cb_dma = 0;
+
+ ha->s_dma_pool = NULL;
+
+ ha->rlc_rsp = NULL;
+ ha->rlc_rsp_dma = 0;
+ ha->gid_list = NULL;
+ ha->gid_list_dma = 0;
+
+ ha->response_ring = NULL;
+ ha->response_dma = 0;
+ ha->request_ring = NULL;
+ ha->request_dma = 0;
+
+ list_for_each_safe(fcpl, fcptemp, &ha->fcports) {
+ fcport = list_entry(fcpl, fc_port_t, list);
+
+ /* fc luns */
+ list_for_each_safe(fcll, fcltemp, &fcport->fcluns) {
+ fclun = list_entry(fcll, fc_lun_t, list);
+
+ list_del_init(&fclun->list);
+ kfree(fclun);
+ }
+
+ /* fc ports */
+ list_del_init(&fcport->list);
+ kfree(fcport);
+ }
+ INIT_LIST_HEAD(&ha->fcports);
+
+ if (ha->fw_dump)
+ free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order);
+
+ if (ha->fw_dump_buffer)
+ vfree(ha->fw_dump_buffer);
+
+ ha->fw_dump = NULL;
+ ha->fw_dump_reading = 0;
+ ha->fw_dump_buffer = NULL;
+}
+
+/*
+ * qla2x00_allocate_sp_pool
+ * This routine is called during initialization to allocate
+ * memory for local srb_t.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Context:
+ * Kernel context.
+ *
+ * Note: Sets the ref_count for non Null sp to one.
+ */
+static int
+qla2x00_allocate_sp_pool(scsi_qla_host_t *ha)
+{
+ int rval;
+
+ rval = QLA_SUCCESS;
+ ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab,
+ mempool_free_slab, srb_cachep);
+ if (ha->srb_mempool == NULL) {
+ qla_printk(KERN_INFO, ha, "Unable to allocate SRB mempool.\n");
+ rval = QLA_FUNCTION_FAILED;
+ }
+ return (rval);
+}
+
+/*
+ * This routine frees all adapter allocated memory.
+ *
+ */
+static void
+qla2x00_free_sp_pool( scsi_qla_host_t *ha)
+{
+ if (ha->srb_mempool) {
+ mempool_destroy(ha->srb_mempool);
+ ha->srb_mempool = NULL;
+ }
+}
+
+/**************************************************************************
+* qla2x00_do_dpc
+* This kernel thread is a task that is schedule by the interrupt handler
+* to perform the background processing for interrupts.
+*
+* Notes:
+* This task always run in the context of a kernel thread. It
+* is kick-off by the driver's detect code and starts up
+* up one per adapter. It immediately goes to sleep and waits for
+* some fibre event. When either the interrupt handler or
+* the timer routine detects a event it will one of the task
+* bits then wake us up.
+**************************************************************************/
+static int
+qla2x00_do_dpc(void *data)
+{
+ DECLARE_MUTEX_LOCKED(sem);
+ scsi_qla_host_t *ha;
+ fc_port_t *fcport;
+ os_lun_t *q;
+ srb_t *sp;
+ uint8_t status;
+ unsigned long flags = 0;
+ struct list_head *list, *templist;
+ int dead_cnt, online_cnt;
+ int retry_cmds = 0;
+ uint16_t next_loopid;
+ int t;
+ os_tgt_t *tq;
+
+ ha = (scsi_qla_host_t *)data;
+
+ lock_kernel();
+
+ daemonize("%s_dpc", ha->host_str);
+ allow_signal(SIGHUP);
+
+ ha->dpc_wait = &sem;
+
+ set_user_nice(current, -20);
+
+ unlock_kernel();
+
+ complete(&ha->dpc_inited);
+
+ while (1) {
+ DEBUG3(printk("qla2x00: DPC handler sleeping\n"));
+
+ if (down_interruptible(&sem))
+ break;
+
+ if (ha->dpc_should_die)
+ break;
+
+ DEBUG3(printk("qla2x00: DPC handler waking up\n"));
+
+ /* Initialization not yet finished. Don't do anything yet. */
+ if (!ha->flags.init_done || ha->dpc_active)
+ continue;
+
+ DEBUG3(printk("scsi(%ld): DPC handler\n", ha->host_no));
+
+ ha->dpc_active = 1;
+
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+
+ /* Process commands in retry queue */
+ if (test_and_clear_bit(PORT_RESTART_NEEDED, &ha->dpc_flags)) {
+ DEBUG(printk("scsi(%ld): DPC checking retry_q. "
+ "total=%d\n",
+ ha->host_no, ha->retry_q_cnt));
+
+ spin_lock_irqsave(&ha->list_lock, flags);
+ dead_cnt = online_cnt = 0;
+ list_for_each_safe(list, templist, &ha->retry_queue) {
+ sp = list_entry(list, srb_t, list);
+ q = sp->lun_queue;
+ DEBUG3(printk("scsi(%ld): pid=%ld sp=%p, "
+ "spflags=0x%x, q_flag= 0x%lx\n",
+ ha->host_no, sp->cmd->serial_number, sp,
+ sp->flags, q->q_flag));
+
+ if (q == NULL)
+ continue;
+ fcport = q->fclun->fcport;
+
+ if (atomic_read(&fcport->state) ==
+ FCS_DEVICE_DEAD ||
+ atomic_read(&fcport->ha->loop_state) == LOOP_DEAD) {
+
+ __del_from_retry_queue(ha, sp);
+ sp->cmd->result = DID_NO_CONNECT << 16;
+ if (atomic_read(&fcport->ha->loop_state) ==
+ LOOP_DOWN)
+ sp->err_id = SRB_ERR_LOOP;
+ else
+ sp->err_id = SRB_ERR_PORT;
+ sp->cmd->host_scribble =
+ (unsigned char *) NULL;
+ __add_to_done_queue(ha, sp);
+ dead_cnt++;
+ } else if (atomic_read(&fcport->state) !=
+ FCS_DEVICE_LOST) {
+
+ __del_from_retry_queue(ha, sp);
+ sp->cmd->result = DID_BUS_BUSY << 16;
+ sp->cmd->host_scribble =
+ (unsigned char *) NULL;
+ __add_to_done_queue(ha, sp);
+ online_cnt++;
+ }
+ } /* list_for_each_safe() */
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+
+ DEBUG(printk("scsi(%ld): done processing retry queue "
+ "- dead=%d, online=%d\n ",
+ ha->host_no, dead_cnt, online_cnt));
+ }
+
+ /* Process commands in scsi retry queue */
+ if (test_and_clear_bit(SCSI_RESTART_NEEDED, &ha->dpc_flags)) {
+ /*
+ * Any requests we want to delay for some period is put
+ * in the scsi retry queue with a delay added. The
+ * timer will schedule a "scsi_restart_needed" every
+ * second as long as there are requests in the scsi
+ * queue.
+ */
+ DEBUG(printk("scsi(%ld): DPC checking scsi "
+ "retry_q.total=%d\n",
+ ha->host_no, ha->scsi_retry_q_cnt));
+
+ online_cnt = 0;
+ spin_lock_irqsave(&ha->list_lock, flags);
+ list_for_each_safe(list, templist,
+ &ha->scsi_retry_queue) {
+
+ sp = list_entry(list, srb_t, list);
+ q = sp->lun_queue;
+ tq = sp->tgt_queue;
+
+ DEBUG3(printk("scsi(%ld): scsi_retry_q: "
+ "pid=%ld sp=%p, spflags=0x%x, "
+ "q_flag= 0x%lx,q_state=%d\n",
+ ha->host_no, sp->cmd->serial_number,
+ sp, sp->flags, q->q_flag, q->q_state));
+
+ /* Was this lun suspended */
+ if (q->q_state != LUN_STATE_WAIT) {
+ online_cnt++;
+ __del_from_scsi_retry_queue(ha, sp);
+
+ if (test_bit(TQF_RETRY_CMDS,
+ &tq->flags)) {
+ qla2x00_extend_timeout(sp->cmd,
+ (sp->cmd->timeout_per_command / HZ) - QLA_CMD_TIMER_DELTA);
+ __add_to_pending_queue(ha, sp);
+ retry_cmds++;
+ } else
+ __add_to_retry_queue(ha, sp);
+ }
+
+ /* Was this command suspended for N secs */
+ if (sp->delay != 0) {
+ sp->delay--;
+ if (sp->delay == 0) {
+ online_cnt++;
+ __del_from_scsi_retry_queue(
+ ha, sp);
+ __add_to_retry_queue(ha,sp);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+
+ /* Clear all Target Unsuspended bits */
+ for (t = 0; t < ha->max_targets; t++) {
+ if ((tq = ha->otgt[t]) == NULL)
+ continue;
+
+ if (test_bit(TQF_RETRY_CMDS, &tq->flags))
+ clear_bit(TQF_RETRY_CMDS, &tq->flags);
+ }
+ if (retry_cmds)
+ qla2x00_next(ha);
+
+ DEBUG(if (online_cnt > 0))
+ DEBUG(printk("scsi(%ld): dpc() found scsi reqs to "
+ "restart= %d\n",
+ ha->host_no, online_cnt));
+ }
+
+ if (ha->flags.mbox_busy) {
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+
+ ha->dpc_active = 0;
+ continue;
+ }
+
+ if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+
+ DEBUG(printk("scsi(%ld): dpc: sched "
+ "qla2x00_abort_isp ha = %p\n",
+ ha->host_no, ha));
+ if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
+ &ha->dpc_flags))) {
+
+ if (qla2x00_abort_isp(ha)) {
+ /* failed. retry later */
+ set_bit(ISP_ABORT_NEEDED,
+ &ha->dpc_flags);
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ }
+ DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
+ ha->host_no));
+ }
+
+ if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
+ (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) {
+
+ DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n",
+ ha->host_no));
+
+ qla2x00_rst_aen(ha);
+ clear_bit(RESET_ACTIVE, &ha->dpc_flags);
+ }
+
+ /* Retry each device up to login retry count */
+ if ((test_and_clear_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) &&
+ atomic_read(&ha->loop_state) != LOOP_DOWN) {
+
+ DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
+ ha->host_no));
+
+ next_loopid = 0;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ /*
+ * If the port is not ONLINE then try to login
+ * to it if we haven't run out of retries.
+ */
+ if (atomic_read(&fcport->state) != FCS_ONLINE &&
+ fcport->login_retry) {
+
+ fcport->login_retry--;
+ if (fcport->flags & FCF_FABRIC_DEVICE) {
+ if (fcport->flags &
+ FCF_TAPE_PRESENT)
+ qla2x00_fabric_logout(
+ ha,
+ fcport->loop_id);
+ status = qla2x00_fabric_login(
+ ha, fcport, &next_loopid);
+ } else
+ status =
+ qla2x00_local_device_login(
+ ha, fcport->loop_id);
+
+ if (status == QLA_SUCCESS) {
+ fcport->old_loop_id = fcport->loop_id;
+
+ DEBUG(printk("scsi(%ld): port login OK: logged in ID 0x%x\n",
+ ha->host_no, fcport->loop_id));
+
+ fcport->port_login_retry_count =
+ ha->port_down_retry_count * PORT_RETRY_TIME;
+ atomic_set(&fcport->state, FCS_ONLINE);
+ atomic_set(&fcport->port_down_timer,
+ ha->port_down_retry_count * PORT_RETRY_TIME);
+
+ fcport->login_retry = 0;
+ } else if (status == 1) {
+ set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
+ /* retry the login again */
+ DEBUG(printk("scsi(%ld): Retrying %d login again loop_id 0x%x\n",
+ ha->host_no,
+ fcport->login_retry, fcport->loop_id));
+ } else {
+ fcport->login_retry = 0;
+ }
+ }
+ if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ break;
+ }
+ DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
+ ha->host_no));
+ }
+
+ if ((test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags)) &&
+ atomic_read(&ha->loop_state) != LOOP_DOWN) {
+
+ clear_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags);
+ DEBUG(printk("scsi(%ld): qla2x00_login_retry()\n",
+ ha->host_no));
+
+ set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+
+ DEBUG(printk("scsi(%ld): qla2x00_login_retry - end\n",
+ ha->host_no));
+ }
+
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+
+ DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
+ ha->host_no));
+
+ if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
+ &ha->dpc_flags))) {
+
+ qla2x00_loop_resync(ha);
+
+ clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags);
+ }
+
+ DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n",
+ ha->host_no));
+ }
+
+
+ if (test_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags)) {
+ DEBUG(printk("scsi(%ld): qla2x00_restart_queues()\n",
+ ha->host_no));
+
+ qla2x00_restart_queues(ha, 0);
+
+ DEBUG(printk("scsi(%ld): qla2x00_restart_queues - end\n",
+ ha->host_no));
+ }
+
+ if (test_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags)) {
+
+ DEBUG(printk("scsi(%ld): qla2x00_abort_queues()\n",
+ ha->host_no));
+
+ qla2x00_abort_queues(ha, 0);
+
+ DEBUG(printk("scsi(%ld): qla2x00_abort_queues - end\n",
+ ha->host_no));
+ }
+
+ if (test_and_clear_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags)) {
+
+ DEBUG(printk("scsi(%ld): Rescan flagged fcports...\n",
+ ha->host_no));
+
+ qla2x00_rescan_fcports(ha);
+
+ DEBUG(printk("scsi(%ld): Rescan flagged fcports..."
+ "end.\n",
+ ha->host_no));
+ }
+
+
+ if (!ha->interrupts_on)
+ qla2x00_enable_intrs(ha);
+
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+
+ ha->dpc_active = 0;
+ } /* End of while(1) */
+
+ DEBUG(printk("scsi(%ld): DPC handler exiting\n", ha->host_no));
+
+ /*
+ * Make sure that nobody tries to wake us up again.
+ */
+ ha->dpc_wait = NULL;
+ ha->dpc_active = 0;
+
+ complete_and_exit(&ha->dpc_exited, 0);
+}
+
+/*
+ * qla2x00_abort_queues
+ * Abort all commands on queues on device
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Context:
+ * Interrupt context.
+ */
+void
+qla2x00_abort_queues(scsi_qla_host_t *ha, uint8_t doneqflg)
+{
+
+ srb_t *sp;
+ struct list_head *list, *temp;
+ unsigned long flags;
+
+ clear_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags);
+
+ /* Return all commands device queues. */
+ spin_lock_irqsave(&ha->list_lock,flags);
+ list_for_each_safe(list, temp, &ha->pending_queue) {
+ sp = list_entry(list, srb_t, list);
+
+ if (sp->flags & SRB_ABORTED)
+ continue;
+
+ /* Remove srb from LUN queue. */
+ __del_from_pending_queue(ha, sp);
+
+ /* Set ending status. */
+ sp->cmd->result = DID_BUS_BUSY << 16;
+
+ __add_to_done_queue(ha, sp);
+ }
+ spin_unlock_irqrestore(&ha->list_lock, flags);
+}
+
+/*
+* qla2x00_rst_aen
+* Processes asynchronous reset.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+static void
+qla2x00_rst_aen(scsi_qla_host_t *ha)
+{
+ if (ha->flags.online && !ha->flags.reset_active &&
+ !atomic_read(&ha->loop_down_timer) &&
+ !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
+ do {
+ clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+
+ /*
+ * Issue marker command only when we are going to start
+ * the I/O.
+ */
+ ha->marker_needed = 1;
+ } while (!atomic_read(&ha->loop_down_timer) &&
+ (test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags)));
+ }
+}
+
+
+/*
+ * This routine will allocate SP from the free queue
+ * input:
+ * scsi_qla_host_t *
+ * output:
+ * srb_t * or NULL
+ */
+static srb_t *
+qla2x00_get_new_sp(scsi_qla_host_t *ha)
+{
+ srb_t *sp;
+
+ sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
+ if (sp)
+ atomic_set(&sp->ref_count, 1);
+ return (sp);
+}
+
+/**************************************************************************
+* qla2x00_timer
+*
+* Description:
+* One second timer
+*
+* Context: Interrupt
+***************************************************************************/
+static void
+qla2x00_timer(scsi_qla_host_t *ha)
+{
+ int t,l;
+ unsigned long cpu_flags = 0;
+ fc_port_t *fcport;
+ os_lun_t *lq;
+ os_tgt_t *tq;
+ int start_dpc = 0;
+ int index;
+ srb_t *sp;
+
+ /*
+ * We try and restart any request in the retry queue every second.
+ */
+ if (!list_empty(&ha->retry_queue)) {
+ set_bit(PORT_RESTART_NEEDED, &ha->dpc_flags);
+ start_dpc++;
+ }
+
+ /*
+ * We try and restart any request in the scsi_retry queue every second.
+ */
+ if (!list_empty(&ha->scsi_retry_queue)) {
+ set_bit(SCSI_RESTART_NEEDED, &ha->dpc_flags);
+ start_dpc++;
+ }
+
+ /*
+ * Ports - Port down timer.
+ *
+ * Whenever, a port is in the LOST state we start decrementing its port
+ * down timer every second until it reaches zero. Once it reaches zero
+ * the port it marked DEAD.
+ */
+ t = 0;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
+
+ if (atomic_read(&fcport->port_down_timer) == 0)
+ continue;
+
+ if (atomic_dec_and_test(&fcport->port_down_timer) != 0)
+ atomic_set(&fcport->state, FCS_DEVICE_DEAD);
+
+ DEBUG(printk("scsi(%ld): fcport-%d - port retry count: "
+ "%d remainning\n",
+ ha->host_no,
+ t, atomic_read(&fcport->port_down_timer)));
+ }
+ t++;
+ } /* End of for fcport */
+
+ /*
+ * LUNS - lun suspend timer.
+ *
+ * Whenever, a lun is suspended the timer starts decrementing its
+ * suspend timer every second until it reaches zero. Once it reaches
+ * zero the lun retry count is decremented.
+ */
+
+ /*
+ * FIXME(dg) - Need to convert this linear search of luns into a search
+ * of a list of suspended luns.
+ */
+ for (t = 0; t < ha->max_targets; t++) {
+ if ((tq = ha->otgt[t]) == NULL)
+ continue;
+
+ for (l = 0; l < ha->max_luns; l++) {
+ if ((lq = (os_lun_t *) tq->olun[l]) == NULL)
+ continue;
+
+ spin_lock_irqsave(&lq->q_lock, cpu_flags);
+ if (lq->q_state == LUN_STATE_WAIT &&
+ atomic_read(&lq->q_timer) != 0) {
+
+ if (atomic_dec_and_test(&lq->q_timer) != 0) {
+ /*
+ * A delay should immediately
+ * transition to a READY state
+ */
+ if (test_and_clear_bit(LUN_EXEC_DELAYED,
+ &lq->q_flag)) {
+ lq->q_state = LUN_STATE_READY;
+ }
+ else {
+ lq->q_count++;
+ if (lq->q_count == lq->q_max)
+ lq->q_state =
+ LUN_STATE_TIMEOUT;
+ else
+ lq->q_state =
+ LUN_STATE_RUN;
+ }
+ }
+ DEBUG3(printk("scsi(%ld): lun%d - timer %d, "
+ "count=%d, max=%d, state=%d\n",
+ ha->host_no,
+ l,
+ atomic_read(&lq->q_timer),
+ lq->q_count, lq->q_max, lq->q_state));
+ }
+ spin_unlock_irqrestore(&lq->q_lock, cpu_flags);
+ } /* End of for luns */
+ } /* End of for targets */
+
+ /* Loop down handler. */
+ if (atomic_read(&ha->loop_down_timer) > 0 &&
+ !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) && ha->flags.online) {
+
+ if (atomic_read(&ha->loop_down_timer) ==
+ ha->loop_down_abort_time) {
+
+ DEBUG(printk("scsi(%ld): Loop Down - aborting the "
+ "queues before time expire\n",
+ ha->host_no));
+
+ if (!IS_QLA2100(ha) && ha->link_down_timeout)
+ atomic_set(&ha->loop_state, LOOP_DEAD);
+
+ /* Schedule an ISP abort to return any tape commands. */
+ spin_lock_irqsave(&ha->hardware_lock, cpu_flags);
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS;
+ index++) {
+ sp = ha->outstanding_cmds[index];
+ if (!sp)
+ continue;
+ if (!(sp->fclun->fcport->flags &
+ FCF_TAPE_PRESENT))
+ continue;
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, cpu_flags);
+
+ set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags);
+ start_dpc++;
+ }
+
+ /* if the loop has been down for 4 minutes, reinit adapter */
+ if (atomic_dec_and_test(&ha->loop_down_timer) != 0) {
+ DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - "
+ "restarting queues.\n",
+ ha->host_no));
+
+ set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
+ start_dpc++;
+
+ if (!(ha->device_flags & DFLG_NO_CABLE)) {
+ DEBUG(printk("scsi(%ld): Loop down - "
+ "aborting ISP.\n",
+ ha->host_no));
+ qla_printk(KERN_WARNING, ha,
+ "Loop down - aborting ISP.\n");
+
+ set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ }
+ }
+ DEBUG3(printk("scsi(%ld): Loop Down - seconds remainning %d\n",
+ ha->host_no,
+ atomic_read(&ha->loop_down_timer)));
+ }
+
+ /*
+ * Done Q Handler -- dgFIXME This handler will kick off doneq if we
+ * haven't process it in 2 seconds.
+ */
+ if (!list_empty(&ha->done_queue))
+ qla2x00_done(ha);
+
+
+ /* Schedule the DPC routine if needed */
+ if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
+ start_dpc ||
+ test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
+ test_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
+ ha->dpc_wait && !ha->dpc_active) {
+
+ up(ha->dpc_wait);
+ }
+
+ qla2x00_restart_timer(ha, WATCH_INTERVAL);
+}
+
+/*
+ * qla2x00_extend_timeout
+ * This routine will extend the timeout to the specified value.
+ *
+ * Input:
+ * cmd = SCSI command structure
+ *
+ * Returns:
+ * None.
+ */
+void
+qla2x00_extend_timeout(struct scsi_cmnd *cmd, int timeout)
+{
+ srb_t *sp = (srb_t *) CMD_SP(cmd);
+ u_long our_jiffies = (timeout * HZ) + jiffies;
+
+ sp->ext_history= 0;
+ sp->e_start = jiffies;
+ if (cmd->eh_timeout.function) {
+ mod_timer(&cmd->eh_timeout,our_jiffies);
+ sp->ext_history |= 1;
+ }
+ if (sp->timer.function != NULL) {
+ /*
+ * Our internal timer should timeout before the midlayer has a
+ * chance begin the abort process
+ */
+ mod_timer(&sp->timer,our_jiffies - (QLA_CMD_TIMER_DELTA * HZ));
+
+ sp->ext_history |= 2;
+ }
+}
+
+/**************************************************************************
+* qla2x00_cmd_timeout
+*
+* Description:
+* Handles the command if it times out in any state.
+*
+* Input:
+* sp - pointer to validate
+*
+* Returns:
+* None.
+* Note:Need to add the support for if( sp->state == SRB_FAILOVER_STATE).
+**************************************************************************/
+void
+qla2x00_cmd_timeout(srb_t *sp)
+{
+ int t, l;
+ int processed;
+ scsi_qla_host_t *vis_ha, *dest_ha;
+ struct scsi_cmnd *cmd;
+ unsigned long flags, cpu_flags;
+ fc_port_t *fcport;
+
+ cmd = sp->cmd;
+ vis_ha = (scsi_qla_host_t *)cmd->device->host->hostdata;
+
+ DEBUG3(printk("cmd_timeout: Entering sp->state = %x\n", sp->state));
+
+ t = cmd->device->id;
+ l = cmd->device->lun;
+ fcport = sp->fclun->fcport;
+ dest_ha = sp->ha;
+
+ /*
+ * If IO is found either in retry Queue
+ * OR in Lun Queue
+ * Return this IO back to host
+ */
+ spin_lock_irqsave(&vis_ha->list_lock, flags);
+ processed = 0;
+ if (sp->state == SRB_PENDING_STATE) {
+ __del_from_pending_queue(vis_ha, sp);
+ DEBUG2(printk("scsi(%ld): Found in Pending queue pid %ld, "
+ "State = %x., fcport state=%d sjiffs=%lx njiffs=%lx\n",
+ vis_ha->host_no, cmd->serial_number, sp->state,
+ atomic_read(&fcport->state), sp->r_start, jiffies));
+
+ /*
+ * If FC_DEVICE is marked as dead return the cmd with
+ * DID_NO_CONNECT status. Otherwise set the host_byte to
+ * DID_BUS_BUSY to let the OS retry this cmd.
+ */
+ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
+ atomic_read(&fcport->ha->loop_state) == LOOP_DEAD) {
+ cmd->result = DID_NO_CONNECT << 16;
+ if (atomic_read(&fcport->ha->loop_state) == LOOP_DOWN)
+ sp->err_id = SRB_ERR_LOOP;
+ else
+ sp->err_id = SRB_ERR_PORT;
+ } else {
+ cmd->result = DID_BUS_BUSY << 16;
+ }
+ __add_to_done_queue(vis_ha, sp);
+ processed++;
+ }
+ spin_unlock_irqrestore(&vis_ha->list_lock, flags);
+
+ if (processed) {
+ qla2x00_done(vis_ha);
+ return;
+ }
+
+ spin_lock_irqsave(&dest_ha->list_lock, flags);
+ if ((sp->state == SRB_RETRY_STATE) ||
+ (sp->state == SRB_SCSI_RETRY_STATE)) {
+
+ DEBUG2(printk("scsi(%ld): Found in (Scsi) Retry queue or "
+ "failover Q pid %ld, State = %x., fcport state=%d "
+ "jiffies=%lx retried=%d\n",
+ dest_ha->host_no, cmd->serial_number, sp->state,
+ atomic_read(&fcport->state), jiffies, cmd->retries));
+
+ if ((sp->state == SRB_RETRY_STATE)) {
+ __del_from_retry_queue(dest_ha, sp);
+ } else if ((sp->state == SRB_SCSI_RETRY_STATE)) {
+ __del_from_scsi_retry_queue(dest_ha, sp);
+ }
+
+ /*
+ * If FC_DEVICE is marked as dead return the cmd with
+ * DID_NO_CONNECT status. Otherwise set the host_byte to
+ * DID_BUS_BUSY to let the OS retry this cmd.
+ */
+ if ((atomic_read(&fcport->state) == FCS_DEVICE_DEAD) ||
+ atomic_read(&dest_ha->loop_state) == LOOP_DEAD) {
+ qla2x00_extend_timeout(cmd, EXTEND_CMD_TIMEOUT);
+ cmd->result = DID_NO_CONNECT << 16;
+ if (atomic_read(&dest_ha->loop_state) == LOOP_DOWN)
+ sp->err_id = SRB_ERR_LOOP;
+ else
+ sp->err_id = SRB_ERR_PORT;
+ } else {
+ cmd->result = DID_BUS_BUSY << 16;
+ }
+
+ __add_to_done_queue(dest_ha, sp);
+ processed++;
+ }
+ spin_unlock_irqrestore(&dest_ha->list_lock, flags);
+
+ if (processed) {
+ qla2x00_done(dest_ha);
+ return;
+ }
+
+ spin_lock_irqsave(&dest_ha->list_lock, cpu_flags);
+ if (sp->state == SRB_DONE_STATE) {
+ /* IO in done_q -- leave it */
+ DEBUG(printk("scsi(%ld): Found in Done queue pid %ld sp=%p.\n",
+ dest_ha->host_no, cmd->serial_number, sp));
+ } else if (sp->state == SRB_SUSPENDED_STATE) {
+ DEBUG(printk("scsi(%ld): Found SP %p in suspended state "
+ "- pid %ld:\n",
+ dest_ha->host_no, sp, cmd->serial_number));
+ DEBUG(qla2x00_dump_buffer((uint8_t *)sp, sizeof(srb_t));)
+ } else if (sp->state == SRB_ACTIVE_STATE) {
+ /*
+ * IO is with ISP find the command in our active list.
+ */
+ spin_unlock_irqrestore(&dest_ha->list_lock, cpu_flags);
+ spin_lock_irqsave(&dest_ha->hardware_lock, flags);
+ if (sp == dest_ha->outstanding_cmds[
+ (unsigned long)sp->cmd->host_scribble]) {
+
+ DEBUG(printk("cmd_timeout: Found in ISP \n"));
+
+ if (sp->flags & SRB_TAPE) {
+ /*
+ * We cannot allow the midlayer error handler
+ * to wakeup and begin the abort process.
+ * Extend the timer so that the firmware can
+ * properly return the IOCB.
+ */
+ DEBUG(printk("cmd_timeout: Extending timeout "
+ "of FCP2 tape command!\n"));
+ qla2x00_extend_timeout(sp->cmd,
+ EXTEND_CMD_TIMEOUT);
+ }
+ sp->state = SRB_ACTIVE_TIMEOUT_STATE;
+ spin_unlock_irqrestore(&dest_ha->hardware_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dest_ha->hardware_lock, flags);
+ printk(KERN_INFO
+ "qla_cmd_timeout: State indicates it is with "
+ "ISP, But not in active array\n");
+ }
+ spin_lock_irqsave(&dest_ha->list_lock, cpu_flags);
+ } else if (sp->state == SRB_ACTIVE_TIMEOUT_STATE) {
+ DEBUG(printk("qla2100%ld: Found in Active timeout state"
+ "pid %ld, State = %x., \n",
+ dest_ha->host_no,
+ sp->cmd->serial_number, sp->state);)
+ } else {
+ /* EMPTY */
+ DEBUG2(printk("cmd_timeout%ld: LOST command state = "
+ "0x%x, sp=%p\n",
+ vis_ha->host_no, sp->state,sp);)
+
+ qla_printk(KERN_INFO, vis_ha,
+ "cmd_timeout: LOST command state = 0x%x\n", sp->state);
+ }
+ spin_unlock_irqrestore(&dest_ha->list_lock, cpu_flags);
+
+ DEBUG3(printk("cmd_timeout: Leaving\n");)
+}
+
+/**************************************************************************
+* qla2x00_done
+* Process completed commands.
+*
+* Input:
+* old_ha = adapter block pointer.
+*
+**************************************************************************/
+void
+qla2x00_done(scsi_qla_host_t *old_ha)
+{
+ os_lun_t *lq;
+ struct scsi_cmnd *cmd;
+ unsigned long flags = 0;
+ scsi_qla_host_t *ha;
+ scsi_qla_host_t *vis_ha;
+ int send_marker_once = 0;
+ srb_t *sp, *sptemp;
+ LIST_HEAD(local_sp_list);
+
+ /*
+ * Get into local queue such that we do not wind up calling done queue
+ * tasklet for the same IOs from DPC or any other place.
+ */
+ spin_lock_irqsave(&old_ha->list_lock, flags);
+ list_splice_init(&old_ha->done_queue, &local_sp_list);
+ spin_unlock_irqrestore(&old_ha->list_lock, flags);
+
+ /*
+ * All done commands are in the local queue, now do the call back.
+ */
+ list_for_each_entry_safe(sp, sptemp, &local_sp_list, list) {
+ old_ha->done_q_cnt--;
+ sp->state = SRB_NO_QUEUE_STATE;
+
+ /* remove command from local list */
+ list_del_init(&sp->list);
+
+ cmd = sp->cmd;
+ if (cmd == NULL)
+ continue;
+
+ vis_ha = (scsi_qla_host_t *)cmd->device->host->hostdata;
+ lq = sp->lun_queue;
+ ha = sp->ha;
+
+ if (sp->flags & SRB_DMA_VALID) {
+ sp->flags &= ~SRB_DMA_VALID;
+
+ /* Release memory used for this I/O */
+ if (cmd->use_sg) {
+ pci_unmap_sg(ha->pdev, cmd->request_buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ } else if (cmd->request_bufflen) {
+ pci_unmap_page(ha->pdev, sp->dma_handle,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+ }
+ }
+
+
+ switch (host_byte(cmd->result)) {
+ case DID_OK:
+ case DID_ERROR:
+ break;
+
+ case DID_RESET:
+ /*
+ * Set marker needed, so we don't have to
+ * send multiple markers
+ */
+ if (!send_marker_once) {
+ ha->marker_needed = 1;
+ send_marker_once++;
+ }
+
+ /*
+ * WORKAROUND
+ *
+ * A backdoor device-reset requires different
+ * error handling. This code differentiates
+ * between normal error handling and the
+ * backdoor method.
+ *
+ */
+ if (ha->host->eh_active != EH_ACTIVE)
+ cmd->result = DID_BUS_BUSY << 16;
+ break;
+
+
+ case DID_ABORT:
+ sp->flags &= ~SRB_ABORT_PENDING;
+ sp->flags |= SRB_ABORTED;
+
+ if (sp->flags & SRB_TIMEOUT)
+ cmd->result = DID_TIME_OUT << 16;
+
+ break;
+
+ default:
+ DEBUG2(printk("scsi(%ld:%d:%d) %s: did_error "
+ "= %d, comp-scsi= 0x%x-0x%x pid=%ld.\n",
+ vis_ha->host_no,
+ cmd->device->id, cmd->device->lun,
+ __func__,
+ host_byte(cmd->result),
+ CMD_COMPL_STATUS(cmd),
+ CMD_SCSI_STATUS(cmd), cmd->serial_number));
+ break;
+ }
+
+ /*
+ * Call the mid-level driver interrupt handler -- via sp_put()
+ */
+ sp_put(ha, sp);
+ } /* end of while */
+}
+
+/*
+ * qla2x00_process_response_queue_in_zio_mode
+ * Process response queue completion as fast as possible
+ * to achieve Zero Interrupt Opertions-ZIO
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Context:
+ * Kernel context.
+ */
+static inline void
+qla2x00_process_response_queue_in_zio_mode(scsi_qla_host_t *ha)
+{
+ unsigned long flags;
+
+ /* Check for unprocessed commands in response queue. */
+ if (!ha->flags.process_response_queue)
+ return;
+ if (!ha->flags.online)
+ return;
+ if (ha->response_ring_ptr->signature == RESPONSE_PROCESSED)
+ return;
+
+ spin_lock_irqsave(&ha->hardware_lock,flags);
+ qla2x00_process_response_queue(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/*
+ * qla2x00_next
+ * Retrieve and process next job in the LUN queue.
+ *
+ * Input:
+ * tq = SCSI target queue pointer.
+ * lq = SCSI LUN queue pointer.
+ * TGT_LOCK must be already obtained.
+ *
+ * Output:
+ * Releases TGT_LOCK upon exit.
+ *
+ * Context:
+ * Kernel/Interrupt context.
+ *
+ * Note: This routine will always try to start I/O from visible HBA.
+ */
+void
+qla2x00_next(scsi_qla_host_t *vis_ha)
+{
+ int rval;
+ unsigned long flags;
+ scsi_qla_host_t *dest_ha;
+ fc_port_t *fcport;
+ srb_t *sp, *sptemp;
+ LIST_HEAD(local_sp_list);
+
+ dest_ha = NULL;
+
+ spin_lock_irqsave(&vis_ha->list_lock, flags);
+ list_splice_init(&vis_ha->pending_queue, &local_sp_list);
+ vis_ha->qthreads = 0;
+ spin_unlock_irqrestore(&vis_ha->list_lock, flags);
+
+ list_for_each_entry_safe(sp, sptemp, &local_sp_list, list) {
+ list_del_init(&sp->list);
+ sp->state = SRB_NO_QUEUE_STATE;
+
+ fcport = sp->fclun->fcport;
+ dest_ha = fcport->ha;
+
+ /* If device is dead then send request back to OS */
+ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD) {
+ sp->cmd->result = DID_NO_CONNECT << 16;
+ if (atomic_read(&dest_ha->loop_state) == LOOP_DOWN)
+ sp->err_id = SRB_ERR_LOOP;
+ else
+ sp->err_id = SRB_ERR_PORT;
+
+ DEBUG3(printk("scsi(%ld): loop/port is down - pid=%ld, "
+ "sp=%p err_id=%d loopid=0x%x queued to dest HBA "
+ "scsi%ld.\n", dest_ha->host_no,
+ sp->cmd->serial_number, sp, sp->err_id,
+ fcport->loop_id, dest_ha->host_no));
+ /*
+ * Initiate a failover - done routine will initiate.
+ */
+ add_to_done_queue(vis_ha, sp);
+
+ continue;
+ }
+
+ /*
+ * SCSI Kluge: Whenever, we need to wait for an event such as
+ * loop down (i.e. loop_down_timer ) or port down (i.e. LUN
+ * request qeueue is suspended) then we will recycle new
+ * commands back to the SCSI layer. We do this because this is
+ * normally a temporary condition and we don't want the
+ * mid-level scsi.c driver to get upset and start aborting
+ * commands. The timeout value is extracted from the command
+ * minus 1-second and put on a retry queue (watchdog). Once the
+ * command timeout it is returned to the mid-level with a BUSY
+ * status, so the mid-level will retry it. This process
+ * continues until the LOOP DOWN time expires or the condition
+ * goes away.
+ */
+ if (!(sp->flags & (SRB_IOCTL | SRB_TAPE)) &&
+ (atomic_read(&fcport->state) != FCS_ONLINE ||
+ test_bit(ABORT_ISP_ACTIVE, &dest_ha->dpc_flags) ||
+ atomic_read(&dest_ha->loop_state) != LOOP_READY)) {
+
+ DEBUG3(printk("scsi(%ld): pid=%ld port=0x%x state=%d "
+ "loop state=%d, loop counter=0x%x "
+ "dpc_flags=0x%lx\n", sp->cmd->serial_number,
+ dest_ha->host_no, fcport->loop_id,
+ atomic_read(&fcport->state),
+ atomic_read(&dest_ha->loop_state),
+ atomic_read(&dest_ha->loop_down_timer),
+ dest_ha->dpc_flags));
+
+ qla2x00_extend_timeout(sp->cmd, EXTEND_CMD_TIMEOUT);
+ add_to_retry_queue(vis_ha, sp);
+
+ continue;
+ }
+
+ /*
+ * If this request's lun is suspended then put the request on
+ * the scsi_retry queue.
+ */
+ if (!(sp->flags & (SRB_IOCTL | SRB_TAPE)) &&
+ sp->lun_queue->q_state == LUN_STATE_WAIT) {
+ DEBUG3(printk("scsi(%ld): lun wait state - pid=%ld, "
+ "opcode=%d, allowed=%d, retries=%d\n",
+ dest_ha->host_no,
+ sp->cmd->serial_number,
+ sp->cmd->cmnd[0],
+ sp->cmd->allowed,
+ sp->cmd->retries));
+
+ add_to_scsi_retry_queue(vis_ha, sp);
+
+ continue;
+ }
+
+ sp->lun_queue->io_cnt++;
+
+ rval = qla2x00_start_scsi(sp);
+ if (rval != QLA_SUCCESS) {
+ /* Place request back on top of device queue */
+ /* add to the top of queue */
+ add_to_pending_queue_head(vis_ha, sp);
+
+ sp->lun_queue->io_cnt--;
+ }
+ }
+
+ if (!IS_QLA2100(vis_ha) && !IS_QLA2200(vis_ha)) {
+ /* Process response_queue if ZIO support is enabled*/
+ qla2x00_process_response_queue_in_zio_mode(vis_ha);
+
+ }
+}
+
+/* XXX(hch): crude hack to emulate a down_timeout() */
+int
+qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout)
+{
+ const unsigned int step = HZ/10;
+
+ do {
+ if (!down_trylock(sema))
+ return 0;
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout(step))
+ break;
+ } while ((timeout -= step) > 0);
+
+ return -ETIMEDOUT;
+}
+
+static void
+qla2xxx_get_port_id(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct fc_port *fc;
+
+ list_for_each_entry(fc, &ha->fcports, list) {
+ if (fc->os_target_id == starget->id) {
+ fc_starget_port_id(starget) = fc->d_id.b.domain << 16 |
+ fc->d_id.b.area << 8 |
+ fc->d_id.b.al_pa;
+ return;
+ }
+ }
+ fc_starget_port_id(starget) = -1;
+}
+
+static void
+qla2xxx_get_port_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct fc_port *fc;
+
+ list_for_each_entry(fc, &ha->fcports, list) {
+ if (fc->os_target_id == starget->id) {
+ fc_starget_port_name(starget) =
+ __be64_to_cpu(*(uint64_t *)fc->port_name);
+ return;
+ }
+ }
+ fc_starget_port_name(starget) = -1;
+}
+
+static void
+qla2xxx_get_node_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
+ struct fc_port *fc;
+
+ list_for_each_entry(fc, &ha->fcports, list) {
+ if (fc->os_target_id == starget->id) {
+ fc_starget_node_name(starget) =
+ __be64_to_cpu(*(uint64_t *)fc->node_name);
+ return;
+ }
+ }
+ fc_starget_node_name(starget) = -1;
+}
+
+static struct fc_function_template qla2xxx_transport_functions = {
+ .get_starget_port_id = qla2xxx_get_port_id,
+ .show_starget_port_id = 1,
+ .get_starget_port_name = qla2xxx_get_port_name,
+ .show_starget_port_name = 1,
+ .get_starget_node_name = qla2xxx_get_node_name,
+ .show_starget_node_name = 1,
+};
+
+/**
+ * qla2x00_module_init - Module initialization.
+ **/
+static int __init
+qla2x00_module_init(void)
+{
+ /* Allocate cache for SRBs. */
+ sprintf(srb_cachep_name, "qla2xxx_srbs");
+ srb_cachep = kmem_cache_create(srb_cachep_name, sizeof(srb_t), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (srb_cachep == NULL) {
+ printk(KERN_ERR
+ "qla2xxx: Unable to allocate SRB cache...Failing load!\n");
+ return -ENOMEM;
+ }
+
+ /* Derive version string. */
+ strcpy(qla2x00_version_str, QLA2XXX_VERSION);
+#if DEBUG_QLA2100
+ strcat(qla2x00_version_str, "-debug");
+#endif
+
+ qla2xxx_transport_template = fc_attach_transport(&qla2xxx_transport_functions);
+ if (!qla2xxx_transport_template)
+ return -ENODEV;
+
+ printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n");
+ return 0;
+}
+
+/**
+ * qla2x00_module_exit - Module cleanup.
+ **/
+static void __exit
+qla2x00_module_exit(void)
+{
+ /* Free SRBs cache. */
+ if (srb_cachep != NULL) {
+ if (kmem_cache_destroy(srb_cachep) != 0) {
+ printk(KERN_ERR
+ "qla2xxx: Unable to free SRB cache...Memory pools "
+ "still active?\n");
+ }
+ srb_cachep = NULL;
+ }
+
+ fc_release_transport(qla2xxx_transport_template);
+}
+
+module_init(qla2x00_module_init);
+module_exit(qla2x00_module_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
diff --git a/drivers/scsi/qla2xxx/qla_rscn.c b/drivers/scsi/qla2xxx/qla_rscn.c
new file mode 100644
index 000000000000..fb545b50fc2f
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_rscn.c
@@ -0,0 +1,1437 @@
+/*
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "qla_def.h"
+
+/**
+ * IO descriptor handle definitions.
+ *
+ * Signature form:
+ *
+ * |31------28|27-------------------12|11-------0|
+ * | Type | Rolling Signature | Index |
+ * |----------|-----------------------|----------|
+ *
+ **/
+
+#define HDL_TYPE_SCSI 0
+#define HDL_TYPE_ASYNC_IOCB 0x0A
+
+#define HDL_INDEX_BITS 12
+#define HDL_ITER_BITS 16
+#define HDL_TYPE_BITS 4
+
+#define HDL_INDEX_MASK ((1UL << HDL_INDEX_BITS) - 1)
+#define HDL_ITER_MASK ((1UL << HDL_ITER_BITS) - 1)
+#define HDL_TYPE_MASK ((1UL << HDL_TYPE_BITS) - 1)
+
+#define HDL_INDEX_SHIFT 0
+#define HDL_ITER_SHIFT (HDL_INDEX_SHIFT + HDL_INDEX_BITS)
+#define HDL_TYPE_SHIFT (HDL_ITER_SHIFT + HDL_ITER_BITS)
+
+/* Local Prototypes. */
+static inline uint32_t qla2x00_to_handle(uint16_t, uint16_t, uint16_t);
+static inline uint16_t qla2x00_handle_to_idx(uint32_t);
+static inline uint32_t qla2x00_iodesc_to_handle(struct io_descriptor *);
+static inline struct io_descriptor *qla2x00_handle_to_iodesc(scsi_qla_host_t *,
+ uint32_t);
+
+static inline struct io_descriptor *qla2x00_alloc_iodesc(scsi_qla_host_t *);
+static inline void qla2x00_free_iodesc(struct io_descriptor *);
+static inline void qla2x00_init_io_descriptors(scsi_qla_host_t *);
+
+static void qla2x00_iodesc_timeout(unsigned long);
+static inline void qla2x00_add_iodesc_timer(struct io_descriptor *);
+static inline void qla2x00_remove_iodesc_timer(struct io_descriptor *);
+
+static inline void qla2x00_update_login_fcport(scsi_qla_host_t *,
+ struct mbx_entry *, fc_port_t *);
+
+static int qla2x00_send_abort_iocb(scsi_qla_host_t *, struct io_descriptor *,
+ uint32_t, int);
+static int qla2x00_send_abort_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
+ struct mbx_entry *);
+
+static int qla2x00_send_adisc_iocb(scsi_qla_host_t *, struct io_descriptor *,
+ int);
+static int qla2x00_send_adisc_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
+ struct mbx_entry *);
+
+static int qla2x00_send_logout_iocb(scsi_qla_host_t *, struct io_descriptor *,
+ int);
+static int qla2x00_send_logout_iocb_cb(scsi_qla_host_t *,
+ struct io_descriptor *, struct mbx_entry *);
+
+static int qla2x00_send_login_iocb(scsi_qla_host_t *, struct io_descriptor *,
+ port_id_t *, int);
+static int qla2x00_send_login_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
+ struct mbx_entry *);
+
+/**
+ * Mailbox IOCB callback array.
+ **/
+static int (*iocb_function_cb_list[LAST_IOCB_CB])
+ (scsi_qla_host_t *, struct io_descriptor *, struct mbx_entry *) = {
+
+ qla2x00_send_abort_iocb_cb,
+ qla2x00_send_adisc_iocb_cb,
+ qla2x00_send_logout_iocb_cb,
+ qla2x00_send_login_iocb_cb,
+};
+
+
+/**
+ * Generic IO descriptor handle routines.
+ **/
+
+/**
+ * qla2x00_to_handle() - Create a descriptor handle.
+ * @type: descriptor type
+ * @iter: descriptor rolling signature
+ * @idx: index to the descriptor array
+ *
+ * Returns a composite handle based in the @type, @iter, and @idx.
+ */
+static inline uint32_t
+qla2x00_to_handle(uint16_t type, uint16_t iter, uint16_t idx)
+{
+ return ((uint32_t)(((uint32_t)type << HDL_TYPE_SHIFT) |
+ ((uint32_t)iter << HDL_ITER_SHIFT) |
+ ((uint32_t)idx << HDL_INDEX_SHIFT)));
+}
+
+/**
+ * qla2x00_handle_to_idx() - Retrive the index for a given handle.
+ * @handle: descriptor handle
+ *
+ * Returns the index specified by the @handle.
+ */
+static inline uint16_t
+qla2x00_handle_to_idx(uint32_t handle)
+{
+ return ((uint16_t)(((handle) >> HDL_INDEX_SHIFT) & HDL_INDEX_MASK));
+}
+
+/**
+ * qla2x00_iodesc_to_handle() - Convert an IO descriptor to a unique handle.
+ * @iodesc: io descriptor
+ *
+ * Returns a unique handle for @iodesc.
+ */
+static inline uint32_t
+qla2x00_iodesc_to_handle(struct io_descriptor *iodesc)
+{
+ uint32_t handle;
+
+ handle = qla2x00_to_handle(HDL_TYPE_ASYNC_IOCB,
+ ++iodesc->ha->iodesc_signature, iodesc->idx);
+ iodesc->signature = handle;
+
+ return (handle);
+}
+
+/**
+ * qla2x00_handle_to_iodesc() - Retrieve an IO descriptor given a unique handle.
+ * @ha: HA context
+ * @handle: handle to io descriptor
+ *
+ * Returns a pointer to the io descriptor, or NULL, if the io descriptor does
+ * not exist or the io descriptors signature does not @handle.
+ */
+static inline struct io_descriptor *
+qla2x00_handle_to_iodesc(scsi_qla_host_t *ha, uint32_t handle)
+{
+ uint16_t idx;
+ struct io_descriptor *iodesc;
+
+ idx = qla2x00_handle_to_idx(handle);
+ iodesc = &ha->io_descriptors[idx];
+ if (iodesc)
+ if (iodesc->signature != handle)
+ iodesc = NULL;
+
+ return (iodesc);
+}
+
+
+/**
+ * IO descriptor allocation routines.
+ **/
+
+/**
+ * qla2x00_alloc_iodesc() - Allocate an IO descriptor from the pool.
+ * @ha: HA context
+ *
+ * Returns a pointer to the allocated io descriptor, or NULL, if none available.
+ */
+static inline struct io_descriptor *
+qla2x00_alloc_iodesc(scsi_qla_host_t *ha)
+{
+ uint16_t iter;
+ struct io_descriptor *iodesc;
+
+ iodesc = NULL;
+ for (iter = 0; iter < MAX_IO_DESCRIPTORS; iter++) {
+ if (ha->io_descriptors[iter].used)
+ continue;
+
+ iodesc = &ha->io_descriptors[iter];
+ iodesc->used = 1;
+ iodesc->idx = iter;
+ init_timer(&iodesc->timer);
+ iodesc->ha = ha;
+ iodesc->signature = qla2x00_iodesc_to_handle(iodesc);
+ break;
+ }
+
+ return (iodesc);
+}
+
+/**
+ * qla2x00_free_iodesc() - Free an IO descriptor.
+ * @iodesc: io descriptor
+ *
+ * NOTE: The io descriptors timer *must* be stopped before it can be free'd.
+ */
+static inline void
+qla2x00_free_iodesc(struct io_descriptor *iodesc)
+{
+ iodesc->used = 0;
+ iodesc->signature = 0;
+}
+
+/**
+ * qla2x00_remove_iodesc_timer() - Remove an active timer from an IO descriptor.
+ * @iodesc: io descriptor
+ */
+static inline void
+qla2x00_remove_iodesc_timer(struct io_descriptor *iodesc)
+{
+ if (iodesc->timer.function != NULL) {
+ del_timer_sync(&iodesc->timer);
+ iodesc->timer.data = (unsigned long) NULL;
+ iodesc->timer.function = NULL;
+ }
+}
+
+/**
+ * qla2x00_init_io_descriptors() - Initialize the pool of IO descriptors.
+ * @ha: HA context
+ */
+static inline void
+qla2x00_init_io_descriptors(scsi_qla_host_t *ha)
+{
+ uint16_t iter;
+
+ for (iter = 0; iter < MAX_IO_DESCRIPTORS; iter++) {
+ if (!ha->io_descriptors[iter].used)
+ continue;
+
+ qla2x00_remove_iodesc_timer(&ha->io_descriptors[iter]);
+ qla2x00_free_iodesc(&ha->io_descriptors[iter]);
+ }
+}
+
+
+/**
+ * IO descriptor timer routines.
+ **/
+
+/**
+ * qla2x00_iodesc_timeout() - Timeout IO descriptor handler.
+ * @data: io descriptor
+ */
+static void
+qla2x00_iodesc_timeout(unsigned long data)
+{
+ struct io_descriptor *iodesc;
+
+ iodesc = (struct io_descriptor *) data;
+
+ DEBUG14(printk("scsi(%ld): IO descriptor timeout, index=%x "
+ "signature=%08x, scheduling ISP abort.\n", iodesc->ha->host_no,
+ iodesc->idx, iodesc->signature));
+
+ qla2x00_free_iodesc(iodesc);
+
+ qla_printk(KERN_WARNING, iodesc->ha,
+ "IO descriptor timeout. Scheduling ISP abort.\n");
+ set_bit(ISP_ABORT_NEEDED, &iodesc->ha->dpc_flags);
+}
+
+/**
+ * qla2x00_add_iodesc_timer() - Add and start a timer for an IO descriptor.
+ * @iodesc: io descriptor
+ *
+ * NOTE:
+ * The firmware shall timeout an outstanding mailbox IOCB in 2 * R_A_TOV (in
+ * tenths of a second) after it hits the wire. But, if there are any request
+ * resource contraints (i.e. during heavy I/O), exchanges can be held off for
+ * at most R_A_TOV. Therefore, the driver will wait 4 * R_A_TOV before
+ * scheduling a recovery (big hammer).
+ */
+static inline void
+qla2x00_add_iodesc_timer(struct io_descriptor *iodesc)
+{
+ unsigned long timeout;
+
+ timeout = (iodesc->ha->r_a_tov * 4) / 10;
+ init_timer(&iodesc->timer);
+ iodesc->timer.data = (unsigned long) iodesc;
+ iodesc->timer.expires = jiffies + (timeout * HZ);
+ iodesc->timer.function =
+ (void (*) (unsigned long)) qla2x00_iodesc_timeout;
+ add_timer(&iodesc->timer);
+}
+
+/**
+ * IO descriptor support routines.
+ **/
+
+/**
+ * qla2x00_update_login_fcport() - Update fcport data after login processing.
+ * @ha: HA context
+ * @mbxstat: Mailbox command status IOCB
+ * @fcport: port to update
+ */
+static inline void
+qla2x00_update_login_fcport(scsi_qla_host_t *ha, struct mbx_entry *mbxstat,
+ fc_port_t *fcport)
+{
+ if (le16_to_cpu(mbxstat->mb1) & BIT_0) {
+ fcport->port_type = FCT_INITIATOR;
+ } else {
+ fcport->port_type = FCT_TARGET;
+ if (le16_to_cpu(mbxstat->mb1) & BIT_1) {
+ fcport->flags |= FCF_TAPE_PRESENT;
+ }
+ }
+ fcport->login_retry = 0;
+ fcport->port_login_retry_count = ha->port_down_retry_count *
+ PORT_RETRY_TIME;
+ atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
+ PORT_RETRY_TIME);
+ fcport->flags |= FCF_FABRIC_DEVICE;
+ fcport->flags &= ~FCF_FAILOVER_NEEDED;
+ fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
+ atomic_set(&fcport->state, FCS_ONLINE);
+}
+
+
+/**
+ * Mailbox IOCB commands.
+ **/
+
+/**
+ * qla2x00_get_mbx_iocb_entry() - Retrieve an IOCB from the request queue.
+ * @ha: HA context
+ * @handle: handle to io descriptor
+ *
+ * Returns a pointer to the reqest entry, or NULL, if none were available.
+ */
+static inline struct mbx_entry *
+qla2x00_get_mbx_iocb_entry(scsi_qla_host_t *ha, uint32_t handle)
+{
+ uint16_t cnt;
+ device_reg_t __iomem *reg = ha->iobase;
+ struct mbx_entry *mbxentry;
+
+ mbxentry = NULL;
+
+ if (ha->req_q_cnt < 3) {
+ cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg));
+ if (ha->req_ring_index < cnt)
+ ha->req_q_cnt = cnt - ha->req_ring_index;
+ else
+ ha->req_q_cnt = ha->request_q_length -
+ (ha->req_ring_index - cnt);
+ }
+ if (ha->req_q_cnt >= 3) {
+ mbxentry = (struct mbx_entry *)ha->request_ring_ptr;
+
+ memset(mbxentry, 0, sizeof(struct mbx_entry));
+ mbxentry->entry_type = MBX_IOCB_TYPE;
+ mbxentry->entry_count = 1;
+ mbxentry->sys_define1 = SOURCE_ASYNC_IOCB;
+ mbxentry->handle = handle;
+ }
+ return (mbxentry);
+}
+
+/**
+ * qla2x00_send_abort_iocb() - Issue an abort IOCB to the firmware.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @handle_to_abort: firmware handle to abort
+ * @ha_locked: is function called with the hardware lock
+ *
+ * Returns QLA_SUCCESS if the IOCB was issued.
+ */
+static int
+qla2x00_send_abort_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ uint32_t handle_to_abort, int ha_locked)
+{
+ unsigned long flags = 0;
+ struct mbx_entry *mbxentry;
+
+ /* Send marker if required. */
+ if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
+ return (QLA_FUNCTION_FAILED);
+
+ if (!ha_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Build abort mailbox IOCB. */
+ mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
+ if (mbxentry == NULL) {
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (QLA_FUNCTION_FAILED);
+ }
+ mbxentry->mb0 = __constant_cpu_to_le16(MBC_ABORT_COMMAND);
+ mbxentry->mb1 = mbxentry->loop_id.extended =
+ cpu_to_le16(iodesc->remote_fcport->loop_id);
+ mbxentry->mb2 = LSW(handle_to_abort);
+ mbxentry->mb3 = MSW(handle_to_abort);
+ wmb();
+
+ qla2x00_add_iodesc_timer(iodesc);
+
+ /* Issue command to ISP. */
+ qla2x00_isp_cmd(ha);
+
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG14(printk("scsi(%ld): Sending Abort IOCB (%08x) to [%x], aborting "
+ "%08x.\n", ha->host_no, iodesc->signature,
+ iodesc->remote_fcport->loop_id, handle_to_abort));
+
+ return (QLA_SUCCESS);
+}
+
+/**
+ * qla2x00_send_abort_iocb_cb() - Abort IOCB callback.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @mbxstat: mailbox status IOCB
+ *
+ * Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
+ * will be used for a retry.
+ */
+static int
+qla2x00_send_abort_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ struct mbx_entry *mbxstat)
+{
+ DEBUG14(printk("scsi(%ld): Abort IOCB -- sent to [%x/%02x%02x%02x], "
+ "status=%x mb0=%x.\n", ha->host_no, iodesc->remote_fcport->loop_id,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area, iodesc->d_id.b.al_pa,
+ le16_to_cpu(mbxstat->status), le16_to_cpu(mbxstat->mb0)));
+
+ return (QLA_SUCCESS);
+}
+
+
+/**
+ * qla2x00_send_adisc_iocb() - Issue a Get Port Database IOCB to the firmware.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @ha_locked: is function called with the hardware lock
+ *
+ * Returns QLA_SUCCESS if the IOCB was issued.
+ */
+static int
+qla2x00_send_adisc_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ int ha_locked)
+{
+ unsigned long flags = 0;
+ struct mbx_entry *mbxentry;
+
+ /* Send marker if required. */
+ if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
+ return (QLA_FUNCTION_FAILED);
+
+ if (!ha_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Build Get Port Database IOCB. */
+ mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
+ if (mbxentry == NULL) {
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (QLA_FUNCTION_FAILED);
+ }
+ mbxentry->mb0 = __constant_cpu_to_le16(MBC_GET_PORT_DATABASE);
+ mbxentry->mb1 = mbxentry->loop_id.extended =
+ cpu_to_le16(iodesc->remote_fcport->loop_id);
+ mbxentry->mb2 = cpu_to_le16(MSW(LSD(ha->iodesc_pd_dma)));
+ mbxentry->mb3 = cpu_to_le16(LSW(LSD(ha->iodesc_pd_dma)));
+ mbxentry->mb6 = cpu_to_le16(MSW(MSD(ha->iodesc_pd_dma)));
+ mbxentry->mb7 = cpu_to_le16(LSW(MSD(ha->iodesc_pd_dma)));
+ mbxentry->mb10 = __constant_cpu_to_le16(BIT_0);
+ wmb();
+
+ qla2x00_add_iodesc_timer(iodesc);
+
+ /* Issue command to ISP. */
+ qla2x00_isp_cmd(ha);
+
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG14(printk("scsi(%ld): Sending Adisc IOCB (%08x) to [%x].\n",
+ ha->host_no, iodesc->signature, iodesc->remote_fcport->loop_id));
+
+ return (QLA_SUCCESS);
+}
+
+/**
+ * qla2x00_send_adisc_iocb_cb() - Get Port Database IOCB callback.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @mbxstat: mailbox status IOCB
+ *
+ * Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
+ * will be used for a retry.
+ */
+static int
+qla2x00_send_adisc_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ struct mbx_entry *mbxstat)
+{
+ fc_port_t *remote_fcport;
+
+ remote_fcport = iodesc->remote_fcport;
+
+ /* Ensure the port IDs are consistent. */
+ if (remote_fcport->d_id.b24 != iodesc->d_id.b24) {
+ DEBUG14(printk("scsi(%ld): Adisc IOCB -- ignoring, remote port "
+ "id changed from [%02x%02x%02x] to [%02x%02x%02x].\n",
+ ha->host_no, remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa));
+
+ return (QLA_SUCCESS);
+ }
+
+ /* Only process the last command. */
+ if (remote_fcport->iodesc_idx_sent != iodesc->idx) {
+ DEBUG14(printk("scsi(%ld): Adisc IOCB -- ignoring, sent to "
+ "[%02x%02x%02x], expected %x, received %x.\n", ha->host_no,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa, remote_fcport->iodesc_idx_sent,
+ iodesc->idx));
+
+ return (QLA_SUCCESS);
+ }
+
+ if (le16_to_cpu(mbxstat->status) == CS_COMPLETE) {
+ DEBUG14(printk("scsi(%ld): Adisc IOCB -- marking "
+ "[%x/%02x%02x%02x] online.\n", ha->host_no,
+ remote_fcport->loop_id, remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa));
+
+ atomic_set(&remote_fcport->state, FCS_ONLINE);
+ } else {
+ DEBUG14(printk("scsi(%ld): Adisc IOCB -- marking "
+ "[%x/%02x%02x%02x] lost, status=%x mb0=%x.\n", ha->host_no,
+ remote_fcport->loop_id, remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa,
+ le16_to_cpu(mbxstat->status), le16_to_cpu(mbxstat->mb0)));
+
+ if (atomic_read(&remote_fcport->state) != FCS_DEVICE_DEAD)
+ atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
+ }
+ remote_fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
+
+ return (QLA_SUCCESS);
+}
+
+
+/**
+ * qla2x00_send_logout_iocb() - Issue a fabric port logout IOCB to the firmware.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @ha_locked: is function called with the hardware lock
+ *
+ * Returns QLA_SUCCESS if the IOCB was issued.
+ */
+static int
+qla2x00_send_logout_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ int ha_locked)
+{
+ unsigned long flags = 0;
+ struct mbx_entry *mbxentry;
+
+ /* Send marker if required. */
+ if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
+ return (QLA_FUNCTION_FAILED);
+
+ if (!ha_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Build fabric port logout mailbox IOCB. */
+ mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
+ if (mbxentry == NULL) {
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (QLA_FUNCTION_FAILED);
+ }
+ mbxentry->mb0 = __constant_cpu_to_le16(MBC_LOGOUT_FABRIC_PORT);
+ mbxentry->mb1 = mbxentry->loop_id.extended =
+ cpu_to_le16(iodesc->remote_fcport->loop_id);
+ wmb();
+
+ qla2x00_add_iodesc_timer(iodesc);
+
+ /* Issue command to ISP. */
+ qla2x00_isp_cmd(ha);
+
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG14(printk("scsi(%ld): Sending Logout IOCB (%08x) to [%x].\n",
+ ha->host_no, iodesc->signature, iodesc->remote_fcport->loop_id));
+
+ return (QLA_SUCCESS);
+}
+
+/**
+ * qla2x00_send_logout_iocb_cb() - Fabric port logout IOCB callback.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @mbxstat: mailbox status IOCB
+ *
+ * Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
+ * will be used for a retry.
+ */
+static int
+qla2x00_send_logout_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ struct mbx_entry *mbxstat)
+{
+ DEBUG14(printk("scsi(%ld): Logout IOCB -- sent to [%x/%02x%02x%02x], "
+ "status=%x mb0=%x mb1=%x.\n", ha->host_no,
+ iodesc->remote_fcport->loop_id,
+ iodesc->remote_fcport->d_id.b.domain,
+ iodesc->remote_fcport->d_id.b.area,
+ iodesc->remote_fcport->d_id.b.al_pa, le16_to_cpu(mbxstat->status),
+ le16_to_cpu(mbxstat->mb0), le16_to_cpu(mbxstat->mb1)));
+
+ return (QLA_SUCCESS);
+}
+
+
+/**
+ * qla2x00_send_login_iocb() - Issue a fabric port login IOCB to the firmware.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @d_id: port id for device
+ * @ha_locked: is function called with the hardware lock
+ *
+ * Returns QLA_SUCCESS if the IOCB was issued.
+ */
+static int
+qla2x00_send_login_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ port_id_t *d_id, int ha_locked)
+{
+ unsigned long flags = 0;
+ struct mbx_entry *mbxentry;
+
+ /* Send marker if required. */
+ if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
+ return (QLA_FUNCTION_FAILED);
+
+ if (!ha_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Build fabric port login mailbox IOCB. */
+ mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
+ if (mbxentry == NULL) {
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (QLA_FUNCTION_FAILED);
+ }
+ mbxentry->mb0 = __constant_cpu_to_le16(MBC_LOGIN_FABRIC_PORT);
+ mbxentry->mb1 = mbxentry->loop_id.extended =
+ cpu_to_le16(iodesc->remote_fcport->loop_id);
+ mbxentry->mb2 = cpu_to_le16(d_id->b.domain);
+ mbxentry->mb3 = cpu_to_le16(d_id->b.area << 8 | d_id->b.al_pa);
+ mbxentry->mb10 = __constant_cpu_to_le16(BIT_0);
+ wmb();
+
+ qla2x00_add_iodesc_timer(iodesc);
+
+ /* Issue command to ISP. */
+ qla2x00_isp_cmd(ha);
+
+ if (!ha_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG14(printk("scsi(%ld): Sending Login IOCB (%08x) to "
+ "[%x/%02x%02x%02x].\n", ha->host_no, iodesc->signature,
+ iodesc->remote_fcport->loop_id, d_id->b.domain, d_id->b.area,
+ d_id->b.al_pa));
+
+ return (QLA_SUCCESS);
+}
+
+/**
+ * qla2x00_send_login_iocb_cb() - Fabric port logout IOCB callback.
+ * @ha: HA context
+ * @iodesc: io descriptor
+ * @mbxstat: mailbox status IOCB
+ *
+ * Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
+ * will be used for a retry.
+ */
+static int
+qla2x00_send_login_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
+ struct mbx_entry *mbxstat)
+{
+ int rval;
+ fc_port_t *fcport, *remote_fcport, *exist_fcport;
+ struct io_descriptor *abort_iodesc, *login_iodesc;
+ uint16_t status, mb[8];
+ uint16_t reuse;
+ uint16_t remote_loopid;
+ port_id_t remote_did, inuse_did;
+
+ remote_fcport = iodesc->remote_fcport;
+
+ /* Only process the last command. */
+ if (remote_fcport->iodesc_idx_sent != iodesc->idx) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- ignoring, sent to "
+ "[%02x%02x%02x], expected %x, received %x.\n",
+ ha->host_no, iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa, remote_fcport->iodesc_idx_sent,
+ iodesc->idx));
+
+ /* Free RSCN fcport resources. */
+ if (remote_fcport->port_type == FCT_RSCN) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- Freeing RSCN "
+ "fcport %p [%x/%02x%02x%02x] given ignored Login "
+ "IOCB.\n", ha->host_no, remote_fcport,
+ remote_fcport->loop_id,
+ remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area,
+ remote_fcport->d_id.b.al_pa));
+
+ list_del(&remote_fcport->list);
+ kfree(remote_fcport);
+ }
+ return (QLA_SUCCESS);
+ }
+
+ status = le16_to_cpu(mbxstat->status);
+ mb[0] = le16_to_cpu(mbxstat->mb0);
+ mb[1] = le16_to_cpu(mbxstat->mb1);
+ mb[2] = le16_to_cpu(mbxstat->mb2);
+ mb[6] = le16_to_cpu(mbxstat->mb6);
+ mb[7] = le16_to_cpu(mbxstat->mb7);
+
+ /* Good status? */
+ if ((status == CS_COMPLETE || status == CS_COMPLETE_CHKCOND) &&
+ mb[0] == MBS_COMMAND_COMPLETE) {
+
+ DEBUG14(printk("scsi(%ld): Login IOCB -- status=%x mb1=%x pn="
+ "%02x%02x%02x%02x%02x%02x%02x%02x.\n", ha->host_no, status,
+ mb[1], mbxstat->port_name[0], mbxstat->port_name[1],
+ mbxstat->port_name[2], mbxstat->port_name[3],
+ mbxstat->port_name[4], mbxstat->port_name[5],
+ mbxstat->port_name[6], mbxstat->port_name[7]));
+
+ memcpy(remote_fcport->node_name, mbxstat->node_name, WWN_SIZE);
+ memcpy(remote_fcport->port_name, mbxstat->port_name, WWN_SIZE);
+
+ /* Is the device already in our fcports list? */
+ if (remote_fcport->port_type != FCT_RSCN) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- marking "
+ "[%x/%02x%02x%02x] online.\n", ha->host_no,
+ remote_fcport->loop_id,
+ remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area,
+ remote_fcport->d_id.b.al_pa));
+
+ qla2x00_update_login_fcport(ha, mbxstat, remote_fcport);
+
+ return (QLA_SUCCESS);
+ }
+
+ /* Does the RSCN portname already exist in our fcports list? */
+ exist_fcport = NULL;
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (memcmp(remote_fcport->port_name, fcport->port_name,
+ WWN_SIZE) == 0) {
+ exist_fcport = fcport;
+ break;
+ }
+ }
+ if (exist_fcport != NULL) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- found RSCN "
+ "fcport in fcports list [%p].\n", ha->host_no,
+ exist_fcport));
+
+ /* Abort any ADISC that could have been sent. */
+ if (exist_fcport->iodesc_idx_sent != iodesc->idx &&
+ exist_fcport->iodesc_idx_sent <
+ MAX_IO_DESCRIPTORS &&
+ ha->io_descriptors[exist_fcport->iodesc_idx_sent].
+ cb_idx == ADISC_PORT_IOCB_CB) {
+
+ abort_iodesc = qla2x00_alloc_iodesc(ha);
+ if (abort_iodesc) {
+ DEBUG14(printk("scsi(%ld): Login IOCB "
+ "-- issuing abort to outstanding "
+ "Adisc [%x/%02x%02x%02x].\n",
+ ha->host_no, remote_fcport->loop_id,
+ exist_fcport->d_id.b.domain,
+ exist_fcport->d_id.b.area,
+ exist_fcport->d_id.b.al_pa));
+
+ abort_iodesc->cb_idx = ABORT_IOCB_CB;
+ abort_iodesc->d_id.b24 =
+ exist_fcport->d_id.b24;
+ abort_iodesc->remote_fcport =
+ exist_fcport;
+ exist_fcport->iodesc_idx_sent =
+ abort_iodesc->idx;
+ qla2x00_send_abort_iocb(ha,
+ abort_iodesc, ha->io_descriptors[
+ exist_fcport->iodesc_idx_sent].
+ signature, 1);
+ } else {
+ DEBUG14(printk("scsi(%ld): Login IOCB "
+ "-- unable to abort outstanding "
+ "Adisc [%x/%02x%02x%02x].\n",
+ ha->host_no, remote_fcport->loop_id,
+ exist_fcport->d_id.b.domain,
+ exist_fcport->d_id.b.area,
+ exist_fcport->d_id.b.al_pa));
+ }
+ }
+
+ /*
+ * If the existing fcport is waiting to send an ADISC
+ * or LOGIN, then reuse remote fcport (RSCN) to
+ * continue waiting.
+ */
+ reuse = 0;
+ remote_loopid = remote_fcport->loop_id;
+ remote_did.b24 = remote_fcport->d_id.b24;
+ if (exist_fcport->iodesc_idx_sent ==
+ IODESC_ADISC_NEEDED ||
+ exist_fcport->iodesc_idx_sent ==
+ IODESC_LOGIN_NEEDED) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- "
+ "existing fcport [%x/%02x%02x%02x] "
+ "waiting for IO descriptor, reuse RSCN "
+ "fcport.\n", ha->host_no,
+ exist_fcport->loop_id,
+ exist_fcport->d_id.b.domain,
+ exist_fcport->d_id.b.area,
+ exist_fcport->d_id.b.al_pa));
+
+ reuse++;
+ remote_fcport->iodesc_idx_sent =
+ exist_fcport->iodesc_idx_sent;
+ exist_fcport->iodesc_idx_sent =
+ IODESC_INVALID_INDEX;
+ remote_fcport->loop_id = exist_fcport->loop_id;
+ remote_fcport->d_id.b24 =
+ exist_fcport->d_id.b24;
+ }
+
+ /* Logout the old loopid. */
+ if (!reuse &&
+ exist_fcport->loop_id != remote_fcport->loop_id &&
+ exist_fcport->loop_id != FC_NO_LOOP_ID) {
+ login_iodesc = qla2x00_alloc_iodesc(ha);
+ if (login_iodesc) {
+ DEBUG14(printk("scsi(%ld): Login IOCB "
+ "-- issuing logout to free old "
+ "loop id [%x/%02x%02x%02x].\n",
+ ha->host_no, exist_fcport->loop_id,
+ exist_fcport->d_id.b.domain,
+ exist_fcport->d_id.b.area,
+ exist_fcport->d_id.b.al_pa));
+
+ login_iodesc->cb_idx =
+ LOGOUT_PORT_IOCB_CB;
+ login_iodesc->d_id.b24 =
+ exist_fcport->d_id.b24;
+ login_iodesc->remote_fcport =
+ exist_fcport;
+ exist_fcport->iodesc_idx_sent =
+ login_iodesc->idx;
+ qla2x00_send_logout_iocb(ha,
+ login_iodesc, 1);
+ } else {
+ /* Ran out of IO descriptiors. */
+ DEBUG14(printk("scsi(%ld): Login IOCB "
+ "-- unable to logout to free old "
+ "loop id [%x/%02x%02x%02x].\n",
+ ha->host_no, exist_fcport->loop_id,
+ exist_fcport->d_id.b.domain,
+ exist_fcport->d_id.b.area,
+ exist_fcport->d_id.b.al_pa));
+
+ exist_fcport->iodesc_idx_sent =
+ IODESC_INVALID_INDEX;
+ }
+
+ }
+
+ /* Update existing fcport with remote fcport info. */
+ DEBUG14(printk("scsi(%ld): Login IOCB -- marking "
+ "existing fcport [%x/%02x%02x%02x] online.\n",
+ ha->host_no, remote_loopid, remote_did.b.domain,
+ remote_did.b.area, remote_did.b.al_pa));
+
+ memcpy(exist_fcport->node_name,
+ remote_fcport->node_name, WWN_SIZE);
+ exist_fcport->loop_id = remote_loopid;
+ exist_fcport->d_id.b24 = remote_did.b24;
+ qla2x00_update_login_fcport(ha, mbxstat, exist_fcport);
+
+ /* Finally, free the remote (RSCN) fcport. */
+ if (!reuse) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- "
+ "Freeing RSCN fcport %p "
+ "[%x/%02x%02x%02x].\n", ha->host_no,
+ remote_fcport, remote_fcport->loop_id,
+ remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area,
+ remote_fcport->d_id.b.al_pa));
+
+ list_del(&remote_fcport->list);
+ kfree(remote_fcport);
+ }
+
+ return (QLA_SUCCESS);
+ }
+
+ /*
+ * A new device has been added, move the RSCN fcport to our
+ * fcports list.
+ */
+ DEBUG14(printk("scsi(%ld): Login IOCB -- adding RSCN fcport "
+ "[%x/%02x%02x%02x] to fcports list.\n", ha->host_no,
+ remote_fcport->loop_id, remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa));
+
+ list_del(&remote_fcport->list);
+ remote_fcport->flags = (FCF_RLC_SUPPORT | FCF_RESCAN_NEEDED);
+ qla2x00_update_login_fcport(ha, mbxstat, remote_fcport);
+ list_add_tail(&remote_fcport->list, &ha->fcports);
+ set_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags);
+ } else {
+ /* Handle login failure. */
+ if (remote_fcport->login_retry != 0) {
+ if (mb[0] == MBS_LOOP_ID_USED) {
+ inuse_did.b.domain = LSB(mb[1]);
+ inuse_did.b.area = MSB(mb[2]);
+ inuse_did.b.al_pa = LSB(mb[2]);
+
+ DEBUG14(printk("scsi(%ld): Login IOCB -- loop "
+ "id [%x] used by port id [%02x%02x%02x].\n",
+ ha->host_no, remote_fcport->loop_id,
+ inuse_did.b.domain, inuse_did.b.area,
+ inuse_did.b.al_pa));
+
+ if (remote_fcport->d_id.b24 ==
+ INVALID_PORT_ID) {
+ /*
+ * Invalid port id means we are trying
+ * to login to a remote port with just
+ * a loop id without knowing about the
+ * port id. Copy the port id and try
+ * again.
+ */
+ remote_fcport->d_id.b24 = inuse_did.b24;
+ iodesc->d_id.b24 = inuse_did.b24;
+ } else {
+ remote_fcport->loop_id++;
+ rval = qla2x00_find_new_loop_id(ha,
+ remote_fcport);
+ if (rval == QLA_FUNCTION_FAILED) {
+ /* No more loop ids. */
+ return (QLA_SUCCESS);
+ }
+ }
+ } else if (mb[0] == MBS_PORT_ID_USED) {
+ /*
+ * Device has another loop ID. The firmware
+ * group recommends the driver perform an
+ * implicit login with the specified ID.
+ */
+ DEBUG14(printk("scsi(%ld): Login IOCB -- port "
+ "id [%02x%02x%02x] already assigned to "
+ "loop id [%x].\n", ha->host_no,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa, mb[1]));
+
+ remote_fcport->loop_id = mb[1];
+
+ } else {
+ /* Unable to perform login, try again. */
+ DEBUG14(printk("scsi(%ld): Login IOCB -- "
+ "failed login [%x/%02x%02x%02x], status=%x "
+ "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
+ ha->host_no, remote_fcport->loop_id,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa, status, mb[0], mb[1],
+ mb[2], mb[6], mb[7]));
+ }
+
+ /* Reissue Login with the same IO descriptor. */
+ iodesc->signature =
+ qla2x00_iodesc_to_handle(iodesc);
+ iodesc->cb_idx = LOGIN_PORT_IOCB_CB;
+ iodesc->d_id.b24 = remote_fcport->d_id.b24;
+ remote_fcport->iodesc_idx_sent = iodesc->idx;
+ remote_fcport->login_retry--;
+
+ DEBUG14(printk("scsi(%ld): Login IOCB -- retrying "
+ "login to [%x/%02x%02x%02x] (%d).\n", ha->host_no,
+ remote_fcport->loop_id,
+ remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area,
+ remote_fcport->d_id.b.al_pa,
+ remote_fcport->login_retry));
+
+ qla2x00_send_login_iocb(ha, iodesc,
+ &remote_fcport->d_id, 1);
+
+ return (QLA_FUNCTION_FAILED);
+ } else {
+ /* No more logins, mark device dead. */
+ DEBUG14(printk("scsi(%ld): Login IOCB -- failed "
+ "login [%x/%02x%02x%02x] after retries, status=%x "
+ "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
+ ha->host_no, remote_fcport->loop_id,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa, status, mb[0], mb[1],
+ mb[2], mb[6], mb[7]));
+
+ atomic_set(&remote_fcport->state, FCS_DEVICE_DEAD);
+ if (remote_fcport->port_type == FCT_RSCN) {
+ DEBUG14(printk("scsi(%ld): Login IOCB -- "
+ "Freeing dead RSCN fcport %p "
+ "[%x/%02x%02x%02x].\n", ha->host_no,
+ remote_fcport, remote_fcport->loop_id,
+ remote_fcport->d_id.b.domain,
+ remote_fcport->d_id.b.area,
+ remote_fcport->d_id.b.al_pa));
+
+ list_del(&remote_fcport->list);
+ kfree(remote_fcport);
+ }
+ }
+ }
+
+ return (QLA_SUCCESS);
+}
+
+
+/**
+ * IO descriptor processing routines.
+ **/
+
+/**
+ * qla2x00_alloc_rscn_fcport() - Allocate an RSCN type fcport.
+ * @ha: HA context
+ * @flags: allocation flags
+ *
+ * Returns a pointer to the allocated RSCN fcport, or NULL, if none available.
+ */
+fc_port_t *
+qla2x00_alloc_rscn_fcport(scsi_qla_host_t *ha, int flags)
+{
+ fc_port_t *fcport;
+
+ fcport = qla2x00_alloc_fcport(ha, flags);
+ if (fcport == NULL)
+ return (fcport);
+
+ /* Setup RSCN fcport structure. */
+ fcport->port_type = FCT_RSCN;
+
+ return (fcport);
+}
+
+/**
+ * qla2x00_handle_port_rscn() - Handle port RSCN.
+ * @ha: HA context
+ * @rscn_entry: RSCN entry
+ * @fcport: fcport entry to updated
+ *
+ * Returns QLA_SUCCESS if the port RSCN was handled.
+ */
+int
+qla2x00_handle_port_rscn(scsi_qla_host_t *ha, uint32_t rscn_entry,
+ fc_port_t *known_fcport, int ha_locked)
+{
+ int rval;
+ port_id_t rscn_pid;
+ fc_port_t *fcport, *remote_fcport, *rscn_fcport;
+ struct io_descriptor *iodesc;
+
+ remote_fcport = NULL;
+ rscn_fcport = NULL;
+
+ /* Prepare port id based on incoming entries. */
+ if (known_fcport) {
+ rscn_pid.b24 = known_fcport->d_id.b24;
+ remote_fcport = known_fcport;
+
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- process RSCN for "
+ "fcport [%02x%02x%02x].\n", ha->host_no,
+ remote_fcport->d_id.b.domain, remote_fcport->d_id.b.area,
+ remote_fcport->d_id.b.al_pa));
+ } else {
+ rscn_pid.b.domain = LSB(MSW(rscn_entry));
+ rscn_pid.b.area = MSB(LSW(rscn_entry));
+ rscn_pid.b.al_pa = LSB(LSW(rscn_entry));
+
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- process RSCN for "
+ "port id [%02x%02x%02x].\n", ha->host_no,
+ rscn_pid.b.domain, rscn_pid.b.area, rscn_pid.b.al_pa));
+
+ /*
+ * Search fcport lists for a known entry at the specified port
+ * ID.
+ */
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (rscn_pid.b24 == fcport->d_id.b24) {
+ remote_fcport = fcport;
+ break;
+ }
+ }
+ list_for_each_entry(fcport, &ha->rscn_fcports, list) {
+ if (rscn_pid.b24 == fcport->d_id.b24) {
+ rscn_fcport = fcport;
+ break;
+ }
+ }
+ if (remote_fcport == NULL)
+ remote_fcport = rscn_fcport;
+ }
+
+ /*
+ * If the port is already in our fcport list and online, send an ADISC
+ * to see if it's still alive. Issue login if a new fcport or the known
+ * fcport is currently offline.
+ */
+ if (remote_fcport) {
+ /*
+ * No need to send request if the remote fcport is currently
+ * waiting for an available io descriptor.
+ */
+ if (known_fcport == NULL &&
+ (remote_fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
+ remote_fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED)) {
+ /*
+ * If previous waiting io descriptor is an ADISC, then
+ * the new RSCN may come from a new remote fcport being
+ * plugged into the same location.
+ */
+ if (remote_fcport->port_type == FCT_RSCN) {
+ remote_fcport->iodesc_idx_sent =
+ IODESC_LOGIN_NEEDED;
+ } else if (remote_fcport->iodesc_idx_sent ==
+ IODESC_ADISC_NEEDED) {
+ fc_port_t *new_fcport;
+
+ remote_fcport->iodesc_idx_sent =
+ IODESC_INVALID_INDEX;
+
+ /* Create new fcport for later login. */
+ new_fcport = qla2x00_alloc_rscn_fcport(ha,
+ ha_locked ? GFP_ATOMIC: GFP_KERNEL);
+ if (new_fcport) {
+ DEBUG14(printk("scsi(%ld): Handle RSCN "
+ "-- creating RSCN fcport %p for "
+ "future login.\n", ha->host_no,
+ new_fcport));
+
+ new_fcport->d_id.b24 =
+ remote_fcport->d_id.b24;
+ new_fcport->iodesc_idx_sent =
+ IODESC_LOGIN_NEEDED;
+
+ list_add_tail(&new_fcport->list,
+ &ha->rscn_fcports);
+ set_bit(IODESC_PROCESS_NEEDED,
+ &ha->dpc_flags);
+ } else {
+ DEBUG14(printk("scsi(%ld): Handle RSCN "
+ "-- unable to allocate RSCN fcport "
+ "for future login.\n",
+ ha->host_no));
+ }
+ }
+ return (QLA_SUCCESS);
+ }
+
+ /* Send ADISC if the fcport is online */
+ if (atomic_read(&remote_fcport->state) == FCS_ONLINE ||
+ remote_fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED) {
+
+ atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
+
+ iodesc = qla2x00_alloc_iodesc(ha);
+ if (iodesc == NULL) {
+ /* Mark fcport for later adisc processing */
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- not "
+ "enough IO descriptors for Adisc, flag "
+ "for later processing.\n", ha->host_no));
+
+ remote_fcport->iodesc_idx_sent =
+ IODESC_ADISC_NEEDED;
+ set_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
+
+ return (QLA_SUCCESS);
+ }
+
+ iodesc->cb_idx = ADISC_PORT_IOCB_CB;
+ iodesc->d_id.b24 = rscn_pid.b24;
+ iodesc->remote_fcport = remote_fcport;
+ remote_fcport->iodesc_idx_sent = iodesc->idx;
+ qla2x00_send_adisc_iocb(ha, iodesc, ha_locked);
+
+ return (QLA_SUCCESS);
+ } else if (remote_fcport->iodesc_idx_sent <
+ MAX_IO_DESCRIPTORS &&
+ ha->io_descriptors[remote_fcport->iodesc_idx_sent].cb_idx ==
+ ADISC_PORT_IOCB_CB) {
+ /*
+ * Receiving another RSCN while an ADISC is pending,
+ * abort the IOCB. Use the same descriptor for the
+ * abort.
+ */
+ uint32_t handle_to_abort;
+
+ iodesc = &ha->io_descriptors[
+ remote_fcport->iodesc_idx_sent];
+ qla2x00_remove_iodesc_timer(iodesc);
+ handle_to_abort = iodesc->signature;
+ iodesc->signature = qla2x00_iodesc_to_handle(iodesc);
+ iodesc->cb_idx = ABORT_IOCB_CB;
+ iodesc->d_id.b24 = remote_fcport->d_id.b24;
+ iodesc->remote_fcport = remote_fcport;
+ remote_fcport->iodesc_idx_sent = iodesc->idx;
+
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- issuing "
+ "abort to outstanding Adisc [%x/%02x%02x%02x].\n",
+ ha->host_no, remote_fcport->loop_id,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area,
+ iodesc->d_id.b.al_pa));
+
+ qla2x00_send_abort_iocb(ha, iodesc, handle_to_abort,
+ ha_locked);
+ }
+ }
+
+ /* We need to login to the remote port, find it. */
+ if (known_fcport) {
+ remote_fcport = known_fcport;
+ } else if (rscn_fcport && rscn_fcport->d_id.b24 != INVALID_PORT_ID &&
+ rscn_fcport->iodesc_idx_sent < MAX_IO_DESCRIPTORS &&
+ ha->io_descriptors[rscn_fcport->iodesc_idx_sent].cb_idx ==
+ LOGIN_PORT_IOCB_CB) {
+ /*
+ * Ignore duplicate RSCN on fcport which has already
+ * initiated a login IOCB.
+ */
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- ignoring, login "
+ "already sent to [%02x%02x%02x].\n", ha->host_no,
+ rscn_fcport->d_id.b.domain, rscn_fcport->d_id.b.area,
+ rscn_fcport->d_id.b.al_pa));
+
+ return (QLA_SUCCESS);
+ } else if (rscn_fcport && rscn_fcport->d_id.b24 != INVALID_PORT_ID &&
+ rscn_fcport != remote_fcport) {
+ /* Reuse same rscn fcport. */
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- reusing RSCN fcport "
+ "[%02x%02x%02x].\n", ha->host_no,
+ rscn_fcport->d_id.b.domain, rscn_fcport->d_id.b.area,
+ rscn_fcport->d_id.b.al_pa));
+
+ remote_fcport = rscn_fcport;
+ } else {
+ /* Create new fcport for later login. */
+ remote_fcport = qla2x00_alloc_rscn_fcport(ha,
+ ha_locked ? GFP_ATOMIC: GFP_KERNEL);
+ list_add_tail(&remote_fcport->list, &ha->rscn_fcports);
+ }
+ if (remote_fcport == NULL)
+ return (QLA_SUCCESS);
+
+ /* Prepare fcport for login. */
+ atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
+ remote_fcport->login_retry = 3; /* ha->login_retry_count; */
+ remote_fcport->d_id.b24 = rscn_pid.b24;
+
+ iodesc = qla2x00_alloc_iodesc(ha);
+ if (iodesc == NULL) {
+ /* Mark fcport for later adisc processing. */
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- not enough IO "
+ "descriptors for Login, flag for later processing.\n",
+ ha->host_no));
+
+ remote_fcport->iodesc_idx_sent = IODESC_LOGIN_NEEDED;
+ set_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
+
+ return (QLA_SUCCESS);
+ }
+
+ if (known_fcport == NULL || rscn_pid.b24 != INVALID_PORT_ID) {
+ remote_fcport->loop_id = ha->min_external_loopid;
+
+ rval = qla2x00_find_new_loop_id(ha, remote_fcport);
+ if (rval == QLA_FUNCTION_FAILED) {
+ /* No more loop ids, failed. */
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- no available "
+ "loop id to perform Login, failed.\n",
+ ha->host_no));
+
+ return (rval);
+ }
+ }
+
+ iodesc->cb_idx = LOGIN_PORT_IOCB_CB;
+ iodesc->d_id.b24 = rscn_pid.b24;
+ iodesc->remote_fcport = remote_fcport;
+ remote_fcport->iodesc_idx_sent = iodesc->idx;
+
+ DEBUG14(printk("scsi(%ld): Handle RSCN -- attempting login to "
+ "[%x/%02x%02x%02x].\n", ha->host_no, remote_fcport->loop_id,
+ iodesc->d_id.b.domain, iodesc->d_id.b.area, iodesc->d_id.b.al_pa));
+
+ qla2x00_send_login_iocb(ha, iodesc, &rscn_pid, ha_locked);
+
+ return (QLA_SUCCESS);
+}
+
+/**
+ * qla2x00_process_iodesc() - Complete IO descriptor processing.
+ * @ha: HA context
+ * @mbxstat: Mailbox IOCB status
+ */
+void
+qla2x00_process_iodesc(scsi_qla_host_t *ha, struct mbx_entry *mbxstat)
+{
+ int rval;
+ uint32_t signature;
+ fc_port_t *fcport;
+ struct io_descriptor *iodesc;
+
+ signature = mbxstat->handle;
+
+ DEBUG14(printk("scsi(%ld): Process IODesc -- processing %08x.\n",
+ ha->host_no, signature));
+
+ /* Retrieve proper IO descriptor. */
+ iodesc = qla2x00_handle_to_iodesc(ha, signature);
+ if (iodesc == NULL) {
+ DEBUG14(printk("scsi(%ld): Process IODesc -- ignoring, "
+ "incorrect signature %08x.\n", ha->host_no, signature));
+
+ return;
+ }
+
+ /* Stop IO descriptor timer. */
+ qla2x00_remove_iodesc_timer(iodesc);
+
+ /* Verify signature match. */
+ if (iodesc->signature != signature) {
+ DEBUG14(printk("scsi(%ld): Process IODesc -- ignoring, "
+ "signature mismatch, sent %08x, received %08x.\n",
+ ha->host_no, iodesc->signature, signature));
+
+ return;
+ }
+
+ /* Go with IOCB callback. */
+ rval = iocb_function_cb_list[iodesc->cb_idx](ha, iodesc, mbxstat);
+ if (rval != QLA_SUCCESS) {
+ /* IO descriptor reused by callback. */
+ return;
+ }
+
+ qla2x00_free_iodesc(iodesc);
+
+ if (test_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags)) {
+ /* Scan our fcports list for any RSCN requests. */
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ if (fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
+ fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED) {
+ qla2x00_handle_port_rscn(ha, 0, fcport, 1);
+ return;
+ }
+ }
+
+ /* Scan our RSCN fcports list for any RSCN requests. */
+ list_for_each_entry(fcport, &ha->rscn_fcports, list) {
+ if (fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
+ fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED) {
+ qla2x00_handle_port_rscn(ha, 0, fcport, 1);
+ return;
+ }
+ }
+ }
+ clear_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
+}
+
+/**
+ * qla2x00_cancel_io_descriptors() - Cancel all outstanding io descriptors.
+ * @ha: HA context
+ *
+ * This routine will also delete any RSCN entries related to the outstanding
+ * IO descriptors.
+ */
+void
+qla2x00_cancel_io_descriptors(scsi_qla_host_t *ha)
+{
+ fc_port_t *fcport, *fcptemp;
+
+ clear_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
+
+ /* Abort all IO descriptors. */
+ qla2x00_init_io_descriptors(ha);
+
+ /* Reset all pending IO descriptors in fcports list. */
+ list_for_each_entry(fcport, &ha->fcports, list) {
+ fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
+ }
+
+ /* Reset all pending IO descriptors in rscn fcports list. */
+ list_for_each_entry_safe(fcport, fcptemp, &ha->rscn_fcports, list) {
+ DEBUG14(printk("scsi(%ld): Cancel IOs -- Freeing RSCN fcport "
+ "%p [%x/%02x%02x%02x].\n", ha->host_no, fcport,
+ fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
+
+ list_del(&fcport->list);
+ kfree(fcport);
+ }
+}
diff --git a/drivers/scsi/qla2xxx/qla_settings.h b/drivers/scsi/qla2xxx/qla_settings.h
new file mode 100644
index 000000000000..c58f5faad9e1
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_settings.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+/*
+ * Compile time Options:
+ * 0 - Disable and 1 - Enable
+ */
+#define DEBUG_QLA2100 0 /* For Debug of qla2x00 */
+
+#define STOP_ON_RESET 0
+#define USE_ABORT_TGT 1 /* Use Abort Target mbx cmd */
+
+#define VSA 0 /* Volume Set Addressing */
+
+/* Failover options */
+#define MAX_RECOVERYTIME 10 /*
+ * Max suspend time for a lun recovery
+ * time
+ */
+#define MAX_FAILBACKTIME 5 /* Max suspend time before fail back */
+
+#define QLA_CMD_TIMER_DELTA 3
+
+/*
+ * When a lun is suspended for the "Not Ready" condition then it will suspend
+ * the lun for increments of 6 sec delays. SUSPEND_COUNT is that count.
+ */
+#define SUSPEND_COUNT 10 /* 6 secs * 10 retries = 60 secs */
+
+/*
+ * Defines the time in seconds that the driver extends the command timeout to
+ * get around the problem where the mid-layer only allows 5 retries for
+ * commands that return BUS_BUSY
+ */
+#define EXTEND_CMD_TIMEOUT 60
+
+#define MAX_RETRIES_OF_ISP_ABORT 5
+
+/* Max time to wait for the loop to be in LOOP_READY state */
+#define MAX_LOOP_TIMEOUT (60 * 5)
+#define EH_ACTIVE 1 /* Error handler active */
+
+/*
+ * Some vendor subsystems do not recover properly after a device reset. Define
+ * the following to force a logout after a successful device reset.
+ */
+#undef LOGOUT_AFTER_DEVICE_RESET
+
+#include "qla_version.h"
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
new file mode 100644
index 000000000000..0e75fbb77b6b
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -0,0 +1,296 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+
+#include "qla_def.h"
+
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t);
+static void qla2x00_nv_deselect(scsi_qla_host_t *);
+static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);
+
+/*
+ * NVRAM support routines
+ */
+
+/**
+ * qla2x00_lock_nvram_access() -
+ * @ha: HA context
+ */
+void
+qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
+{
+ uint16_t data;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
+ data = RD_REG_WORD(&reg->nvram);
+ while (data & NVR_BUSY) {
+ udelay(100);
+ data = RD_REG_WORD(&reg->nvram);
+ }
+
+ /* Lock resource */
+ WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
+ RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ udelay(5);
+ data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ while ((data & BIT_0) == 0) {
+ /* Lock failed */
+ udelay(100);
+ WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
+ RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ udelay(5);
+ data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ }
+ }
+}
+
+/**
+ * qla2x00_unlock_nvram_access() -
+ * @ha: HA context
+ */
+void
+qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
+ WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0);
+ RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ }
+}
+
+/**
+ * qla2x00_release_nvram_protection() -
+ * @ha: HA context
+ */
+void
+qla2x00_release_nvram_protection(scsi_qla_host_t *ha)
+{
+ device_reg_t *reg;
+ uint32_t word;
+
+ reg = ha->iobase;
+
+ /* Release NVRAM write protection. */
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ /* Write enable. */
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ qla2x00_nv_write(ha, 0);
+ qla2x00_nv_write(ha, 0);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Enable protection register. */
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Clear protection register (ffff is cleared). */
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Wait for NVRAM to become ready. */
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ do {
+ NVRAM_DELAY();
+ word = RD_REG_WORD(&reg->nvram);
+ } while ((word & NVR_DATA_IN) == 0);
+ }
+}
+
+/**
+ * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
+ * request routine to get the word from NVRAM.
+ * @ha: HA context
+ * @addr: Address in NVRAM to read
+ *
+ * Returns the word read from nvram @addr.
+ */
+uint16_t
+qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
+{
+ uint16_t data;
+ uint32_t nv_cmd;
+
+ nv_cmd = addr << 16;
+ nv_cmd |= NV_READ_OP;
+ data = qla2x00_nvram_request(ha, nv_cmd);
+
+ return (data);
+}
+
+/**
+ * qla2x00_write_nvram_word() - Write NVRAM data.
+ * @ha: HA context
+ * @addr: Address in NVRAM to write
+ * @data: word to program
+ */
+void
+qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
+{
+ int count;
+ uint16_t word;
+ uint32_t nv_cmd;
+ device_reg_t __iomem *reg = ha->iobase;
+
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ qla2x00_nv_write(ha, 0);
+ qla2x00_nv_write(ha, 0);
+
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Write data */
+ nv_cmd = (addr << 16) | NV_WRITE_OP;
+ nv_cmd |= data;
+ nv_cmd <<= 5;
+ for (count = 0; count < 27; count++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+
+ nv_cmd <<= 1;
+ }
+
+ qla2x00_nv_deselect(ha);
+
+ /* Wait for NVRAM to become ready */
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ do {
+ NVRAM_DELAY();
+ word = RD_REG_WORD(&reg->nvram);
+ } while ((word & NVR_DATA_IN) == 0);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Disable writes */
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ for (count = 0; count < 10; count++)
+ qla2x00_nv_write(ha, 0);
+
+ qla2x00_nv_deselect(ha);
+}
+
+/**
+ * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
+ * NVRAM.
+ * @ha: HA context
+ * @nv_cmd: NVRAM command
+ *
+ * Bit definitions for NVRAM command:
+ *
+ * Bit 26 = start bit
+ * Bit 25, 24 = opcode
+ * Bit 23-16 = address
+ * Bit 15-0 = write data
+ *
+ * Returns the word read from nvram @addr.
+ */
+static uint16_t
+qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd)
+{
+ uint8_t cnt;
+ device_reg_t __iomem *reg = ha->iobase;
+ uint16_t data = 0;
+ uint16_t reg_data;
+
+ /* Send command to NVRAM. */
+ nv_cmd <<= 5;
+ for (cnt = 0; cnt < 11; cnt++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+ nv_cmd <<= 1;
+ }
+
+ /* Read data from NVRAM. */
+ for (cnt = 0; cnt < 16; cnt++) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
+ NVRAM_DELAY();
+ data <<= 1;
+ reg_data = RD_REG_WORD(&reg->nvram);
+ if (reg_data & NVR_DATA_IN)
+ data |= BIT_0;
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ }
+
+ /* Deselect chip. */
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+
+ return (data);
+}
+
+/**
+ * qla2x00_nv_write() - Clean NVRAM operations.
+ * @ha: HA context
+ */
+static void
+qla2x00_nv_deselect(scsi_qla_host_t *ha)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
+ * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
+ * @ha: HA context
+ * @data: Serial interface selector
+ */
+static void
+qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data)
+{
+ device_reg_t __iomem *reg = ha->iobase;
+
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT| NVR_CLOCK |
+ NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
new file mode 100644
index 000000000000..73ff88b834b7
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2004 QLogic Corporation
+ * (www.qlogic.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ ******************************************************************************/
+/*
+ * Driver version
+ */
+#define QLA2XXX_VERSION "8.00.02b4-k"
+
+#define QLA_DRIVER_MAJOR_VER 8
+#define QLA_DRIVER_MINOR_VER 0
+#define QLA_DRIVER_PATCH_VER 2
+#define QLA_DRIVER_BETA_VER 4
diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c
new file mode 100644
index 000000000000..a1adb38f69bb
--- /dev/null
+++ b/drivers/scsi/qlogicfas.c
@@ -0,0 +1,230 @@
+/*
+ * Qlogic FAS408 ISA card driver
+ *
+ * Copyright 1994, Tom Zerucha.
+ * tz@execpc.com
+ *
+ * Redistributable under terms of the GNU General Public License
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * Check qlogicfas408.c for more credits and info.
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h> /* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "qlogicfas408.h"
+
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ * drain
+ */
+#define INT_TYPE 2
+
+static char qlogicfas_name[] = "qlogicfas";
+
+/*
+ * Look for qlogic card and init if found
+ */
+
+static struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host,
+ int qbase,
+ int qlirq)
+{
+ int qltyp; /* type of chip */
+ int qinitid;
+ struct Scsi_Host *hreg; /* registered host structure */
+ struct qlogicfas408_priv *priv;
+
+ /* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself
+ * decodes the address - I check 230 first since MIDI cards are
+ * typically at 0x330
+ *
+ * Theoretically, two Qlogic cards can coexist in the same system.
+ * This should work by simply using this as a loadable module for
+ * the second card, but I haven't tested this.
+ */
+
+ if (!qbase || qlirq == -1)
+ goto err;
+
+ if (!request_region(qbase, 0x10, qlogicfas_name)) {
+ printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name,
+ qbase);
+ goto err;
+ }
+
+ if (!qlogicfas408_detect(qbase, INT_TYPE)) {
+ printk(KERN_WARNING "%s: probe failed for %#x\n",
+ qlogicfas_name,
+ qbase);
+ goto err_release_mem;
+ }
+
+ printk(KERN_INFO "%s: Using preset base address of %03x,"
+ " IRQ %d\n", qlogicfas_name, qbase, qlirq);
+
+ qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
+ qinitid = host->this_id;
+ if (qinitid < 0)
+ qinitid = 7; /* if no ID, use 7 */
+
+ qlogicfas408_setup(qbase, qinitid, INT_TYPE);
+
+ hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
+ if (!hreg)
+ goto err_release_mem;
+ priv = get_priv_by_host(hreg);
+ hreg->io_port = qbase;
+ hreg->n_io_port = 16;
+ hreg->dma_channel = -1;
+ if (qlirq != -1)
+ hreg->irq = qlirq;
+ priv->qbase = qbase;
+ priv->qlirq = qlirq;
+ priv->qinitid = qinitid;
+ priv->shost = hreg;
+ priv->int_type = INT_TYPE;
+
+ sprintf(priv->qinfo,
+ "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
+ qltyp, qbase, qlirq, QL_TURBO_PDMA);
+ host->name = qlogicfas_name;
+
+ if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg))
+ goto free_scsi_host;
+
+ if (scsi_add_host(hreg, NULL))
+ goto free_interrupt;
+
+ scsi_scan_host(hreg);
+
+ return hreg;
+
+free_interrupt:
+ free_irq(qlirq, hreg);
+
+free_scsi_host:
+ scsi_host_put(hreg);
+
+err_release_mem:
+ release_region(qbase, 0x10);
+err:
+ return NULL;
+}
+
+#define MAX_QLOGICFAS 8
+static struct qlogicfas408_priv *cards;
+static int iobase[MAX_QLOGICFAS];
+static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 };
+module_param_array(iobase, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(iobase, "I/O address");
+MODULE_PARM_DESC(irq, "IRQ");
+
+static int __devinit qlogicfas_detect(Scsi_Host_Template *sht)
+{
+ struct Scsi_Host *shost;
+ struct qlogicfas408_priv *priv;
+ int num;
+
+ for (num = 0; num < MAX_QLOGICFAS; num++) {
+ shost = __qlogicfas_detect(sht, iobase[num], irq[num]);
+ if (shost == NULL) {
+ /* no more devices */
+ break;
+ }
+ priv = get_priv_by_host(shost);
+ priv->next = cards;
+ cards = priv;
+ }
+
+ return num;
+}
+
+static int qlogicfas_release(struct Scsi_Host *shost)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_host(shost);
+
+ if (shost->irq) {
+ qlogicfas408_disable_ints(priv);
+ free_irq(shost->irq, shost);
+ }
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_remove_host(shost);
+ scsi_host_put(shost);
+
+ return 0;
+}
+
+/*
+ * The driver template is also needed for PCMCIA
+ */
+static Scsi_Host_Template qlogicfas_driver_template = {
+ .module = THIS_MODULE,
+ .name = qlogicfas_name,
+ .proc_name = qlogicfas_name,
+ .info = qlogicfas408_info,
+ .queuecommand = qlogicfas408_queuecommand,
+ .eh_abort_handler = qlogicfas408_abort,
+ .eh_bus_reset_handler = qlogicfas408_bus_reset,
+ .eh_device_reset_handler= qlogicfas408_device_reset,
+ .eh_host_reset_handler = qlogicfas408_host_reset,
+ .bios_param = qlogicfas408_biosparam,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+static __init int qlogicfas_init(void)
+{
+ if (!qlogicfas_detect(&qlogicfas_driver_template)) {
+ /* no cards found */
+ printk(KERN_INFO "%s: no cards were found, please specify "
+ "I/O address and IRQ using iobase= and irq= "
+ "options", qlogicfas_name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static __exit void qlogicfas_exit(void)
+{
+ struct qlogicfas408_priv *priv;
+
+ for (priv = cards; priv != NULL; priv = priv->next)
+ qlogicfas_release(priv->shost);
+}
+
+MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
+MODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card");
+MODULE_LICENSE("GPL");
+module_init(qlogicfas_init);
+module_exit(qlogicfas_exit);
+
diff --git a/drivers/scsi/qlogicfas408.c b/drivers/scsi/qlogicfas408.c
new file mode 100644
index 000000000000..5b6ce0a88f08
--- /dev/null
+++ b/drivers/scsi/qlogicfas408.c
@@ -0,0 +1,637 @@
+/*----------------------------------------------------------------*/
+/*
+ Qlogic linux driver - work in progress. No Warranty express or implied.
+ Use at your own risk. Support Tort Reform so you won't have to read all
+ these silly disclaimers.
+
+ Copyright 1994, Tom Zerucha.
+ tz@execpc.com
+
+ Additional Code, and much appreciated help by
+ Michael A. Griffith
+ grif@cs.ucr.edu
+
+ Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+ help respectively, and for suffering through my foolishness during the
+ debugging process.
+
+ Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
+ (you can reference it, but it is incomplete and inaccurate in places)
+
+ Version 0.46 1/30/97 - kernel 1.2.0+
+
+ Functions as standalone, loadable, and PCMCIA driver, the latter from
+ Dave Hinds' PCMCIA package.
+
+ Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
+ SCSI driver cleanup and audit. This driver still needs work on the
+ following
+ - Non terminating hardware waits
+ - Some layering violations with its pcmcia stub
+
+ Redistributable under terms of the GNU General Public License
+
+ For the avoidance of doubt the "preferred form" of this code is one which
+ is in an open non patent encumbered format. Where cryptographic key signing
+ forms part of the process of creating an executable the information
+ including keys needed to generate an equivalently functional executable
+ are deemed to be part of the source code.
+
+*/
+
+#include <linux/module.h>
+#include <linux/blkdev.h> /* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "qlogicfas408.h"
+
+/*----------------------------------------------------------------*/
+static int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */
+static int qlcfg6 = SYNCXFRPD;
+static int qlcfg7 = SYNCOFFST;
+static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
+static int qlcfg9 = ((XTALFREQ + 4) / 5);
+static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
+
+/*----------------------------------------------------------------*/
+
+/*----------------------------------------------------------------*/
+/* local functions */
+/*----------------------------------------------------------------*/
+
+/* error recovery - reset everything */
+
+static void ql_zap(struct qlogicfas408_priv *priv)
+{
+ int x;
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+
+ x = inb(qbase + 0xd);
+ REG0;
+ outb(3, qbase + 3); /* reset SCSI */
+ outb(2, qbase + 3); /* reset chip */
+ if (x & 0x80)
+ REG1;
+}
+
+/*
+ * Do a pseudo-dma tranfer
+ */
+
+static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen)
+{
+ int j;
+ int qbase = priv->qbase;
+ j = 0;
+ if (phase & 1) { /* in */
+#if QL_TURBO_PDMA
+ rtrc(4)
+ /* empty fifo in large chunks */
+ if (reqlen >= 128 && (inb(qbase + 8) & 2)) { /* full */
+ insl(qbase + 4, request, 32);
+ reqlen -= 128;
+ request += 128;
+ }
+ while (reqlen >= 84 && !(j & 0xc0)) /* 2/3 */
+ if ((j = inb(qbase + 8)) & 4)
+ {
+ insl(qbase + 4, request, 21);
+ reqlen -= 84;
+ request += 84;
+ }
+ if (reqlen >= 44 && (inb(qbase + 8) & 8)) { /* 1/3 */
+ insl(qbase + 4, request, 11);
+ reqlen -= 44;
+ request += 44;
+ }
+#endif
+ /* until both empty and int (or until reclen is 0) */
+ rtrc(7)
+ j = 0;
+ while (reqlen && !((j & 0x10) && (j & 0xc0)))
+ {
+ /* while bytes to receive and not empty */
+ j &= 0xc0;
+ while (reqlen && !((j = inb(qbase + 8)) & 0x10))
+ {
+ *request++ = inb(qbase + 4);
+ reqlen--;
+ }
+ if (j & 0x10)
+ j = inb(qbase + 8);
+
+ }
+ } else { /* out */
+#if QL_TURBO_PDMA
+ rtrc(4)
+ if (reqlen >= 128 && inb(qbase + 8) & 0x10) { /* empty */
+ outsl(qbase + 4, request, 32);
+ reqlen -= 128;
+ request += 128;
+ }
+ while (reqlen >= 84 && !(j & 0xc0)) /* 1/3 */
+ if (!((j = inb(qbase + 8)) & 8)) {
+ outsl(qbase + 4, request, 21);
+ reqlen -= 84;
+ request += 84;
+ }
+ if (reqlen >= 40 && !(inb(qbase + 8) & 4)) { /* 2/3 */
+ outsl(qbase + 4, request, 10);
+ reqlen -= 40;
+ request += 40;
+ }
+#endif
+ /* until full and int (or until reclen is 0) */
+ rtrc(7)
+ j = 0;
+ while (reqlen && !((j & 2) && (j & 0xc0))) {
+ /* while bytes to send and not full */
+ while (reqlen && !((j = inb(qbase + 8)) & 2))
+ {
+ outb(*request++, qbase + 4);
+ reqlen--;
+ }
+ if (j & 2)
+ j = inb(qbase + 8);
+ }
+ }
+ /* maybe return reqlen */
+ return inb(qbase + 8) & 0xc0;
+}
+
+/*
+ * Wait for interrupt flag (polled - not real hardware interrupt)
+ */
+
+static int ql_wai(struct qlogicfas408_priv *priv)
+{
+ int k;
+ int qbase = priv->qbase;
+ unsigned long i;
+
+ k = 0;
+ i = jiffies + WATCHDOG;
+ while (time_before(jiffies, i) && !priv->qabort &&
+ !((k = inb(qbase + 4)) & 0xe0)) {
+ barrier();
+ cpu_relax();
+ }
+ if (time_after_eq(jiffies, i))
+ return (DID_TIME_OUT);
+ if (priv->qabort)
+ return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
+ if (k & 0x60)
+ ql_zap(priv);
+ if (k & 0x20)
+ return (DID_PARITY);
+ if (k & 0x40)
+ return (DID_ERROR);
+ return 0;
+}
+
+/*
+ * Initiate scsi command - queueing handler
+ * caller must hold host lock
+ */
+
+static void ql_icmd(Scsi_Cmnd * cmd)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+ unsigned int i;
+
+ priv->qabort = 0;
+
+ REG0;
+ /* clearing of interrupts and the fifo is needed */
+
+ inb(qbase + 5); /* clear interrupts */
+ if (inb(qbase + 5)) /* if still interrupting */
+ outb(2, qbase + 3); /* reset chip */
+ else if (inb(qbase + 7) & 0x1f)
+ outb(1, qbase + 3); /* clear fifo */
+ while (inb(qbase + 5)); /* clear ints */
+ REG1;
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ outb(0, qbase + 0xb); /* disable ints */
+ inb(qbase + 8); /* clear int bits */
+ REG0;
+ outb(0x40, qbase + 0xb); /* enable features */
+
+ /* configurables */
+ outb(qlcfgc, qbase + 0xc);
+ /* config: no reset interrupt, (initiator) bus id */
+ outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
+ outb(qlcfg7, qbase + 7);
+ outb(qlcfg6, qbase + 6);
+ /**/ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9 & 7, qbase + 9); /* prescaler */
+/* outb(0x99, qbase + 5); */
+ outb(cmd->device->id, qbase + 4);
+
+ for (i = 0; i < cmd->cmd_len; i++)
+ outb(cmd->cmnd[i], qbase + 2);
+
+ priv->qlcmd = cmd;
+ outb(0x41, qbase + 3); /* select and send command */
+}
+
+/*
+ * Process scsi command - usually after interrupt
+ */
+
+static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
+{
+ unsigned int i, j;
+ unsigned long k;
+ unsigned int result; /* ultimate return result */
+ unsigned int status; /* scsi returned status */
+ unsigned int message; /* scsi returned message */
+ unsigned int phase; /* recorded scsi phase */
+ unsigned int reqlen; /* total length of transfer */
+ struct scatterlist *sglist; /* scatter-gather list pointer */
+ unsigned int sgcount; /* sg counter */
+ char *buf;
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+
+ rtrc(1)
+ j = inb(qbase + 6);
+ i = inb(qbase + 5);
+ if (i == 0x20) {
+ return (DID_NO_CONNECT << 16);
+ }
+ i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
+ if (i != 0x18) {
+ printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
+ ql_zap(priv);
+ return (DID_BAD_INTR << 16);
+ }
+ j &= 7; /* j = inb( qbase + 7 ) >> 5; */
+
+ /* correct status is supposed to be step 4 */
+ /* it sometimes returns step 3 but with 0 bytes left to send */
+ /* We can try stuffing the FIFO with the max each time, but we will get a
+ sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+
+ if (j != 3 && j != 4) {
+ printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
+ j, i, inb(qbase + 7) & 0x1f);
+ ql_zap(priv);
+ return (DID_ERROR << 16);
+ }
+ result = DID_OK;
+ if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */
+ outb(1, qbase + 3); /* clear fifo */
+ /* note that request_bufflen is the total xfer size when sg is used */
+ reqlen = cmd->request_bufflen;
+ /* note that it won't work if transfers > 16M are requested */
+ if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
+ rtrc(2)
+ outb(reqlen, qbase); /* low-mid xfer cnt */
+ outb(reqlen >> 8, qbase + 1); /* low-mid xfer cnt */
+ outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
+ outb(0x90, qbase + 3); /* command do xfer */
+ /* PIO pseudo DMA to buffer or sglist */
+ REG1;
+ if (!cmd->use_sg)
+ ql_pdma(priv, phase, cmd->request_buffer,
+ cmd->request_bufflen);
+ else {
+ sgcount = cmd->use_sg;
+ sglist = cmd->request_buffer;
+ while (sgcount--) {
+ if (priv->qabort) {
+ REG0;
+ return ((priv->qabort == 1 ?
+ DID_ABORT : DID_RESET) << 16);
+ }
+ buf = page_address(sglist->page) + sglist->offset;
+ if (ql_pdma(priv, phase, buf, sglist->length))
+ break;
+ sglist++;
+ }
+ }
+ REG0;
+ rtrc(2)
+ /*
+ * Wait for irq (split into second state of irq handler
+ * if this can take time)
+ */
+ if ((k = ql_wai(priv)))
+ return (k << 16);
+ k = inb(qbase + 5); /* should be 0x10, bus service */
+ }
+
+ /*
+ * Enter Status (and Message In) Phase
+ */
+
+ k = jiffies + WATCHDOG;
+
+ while (time_before(jiffies, k) && !priv->qabort &&
+ !(inb(qbase + 4) & 6))
+ cpu_relax(); /* wait for status phase */
+
+ if (time_after_eq(jiffies, k)) {
+ ql_zap(priv);
+ return (DID_TIME_OUT << 16);
+ }
+
+ /* FIXME: timeout ?? */
+ while (inb(qbase + 5))
+ cpu_relax(); /* clear pending ints */
+
+ if (priv->qabort)
+ return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+
+ outb(0x11, qbase + 3); /* get status and message */
+ if ((k = ql_wai(priv)))
+ return (k << 16);
+ i = inb(qbase + 5); /* get chip irq stat */
+ j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
+ status = inb(qbase + 2);
+ message = inb(qbase + 2);
+
+ /*
+ * Should get function complete int if Status and message, else
+ * bus serv if only status
+ */
+ if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
+ printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
+ result = DID_ERROR;
+ }
+ outb(0x12, qbase + 3); /* done, disconnect */
+ rtrc(1)
+ if ((k = ql_wai(priv)))
+ return (k << 16);
+
+ /*
+ * Should get bus service interrupt and disconnect interrupt
+ */
+
+ i = inb(qbase + 5); /* should be bus service */
+ while (!priv->qabort && ((i & 0x20) != 0x20)) {
+ barrier();
+ cpu_relax();
+ i |= inb(qbase + 5);
+ }
+ rtrc(0)
+
+ if (priv->qabort)
+ return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+
+ return (result << 16) | (message << 8) | (status & STATUS_MASK);
+}
+
+/*
+ * Interrupt handler
+ */
+
+static void ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
+{
+ Scsi_Cmnd *icmd;
+ struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
+ struct qlogicfas408_priv *priv = get_priv_by_host(host);
+ int qbase = priv->qbase;
+ REG0;
+
+ if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
+ return;
+
+ if (priv->qlcmd == NULL) { /* no command to process? */
+ int i;
+ i = 16;
+ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
+ return;
+ }
+ icmd = priv->qlcmd;
+ icmd->result = ql_pcmd(icmd);
+ priv->qlcmd = NULL;
+ /*
+ * If result is CHECK CONDITION done calls qcommand to request
+ * sense
+ */
+ (icmd->scsi_done) (icmd);
+}
+
+irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *host = dev_id;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ ql_ihandl(irq, dev_id, regs);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Queued command
+ */
+
+int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ if (cmd->device->id == priv->qinitid) {
+ cmd->result = DID_BAD_TARGET << 16;
+ done(cmd);
+ return 0;
+ }
+
+ cmd->scsi_done = done;
+ /* wait for the last command's interrupt to finish */
+ while (priv->qlcmd != NULL) {
+ barrier();
+ cpu_relax();
+ }
+ ql_icmd(cmd);
+ return 0;
+}
+
+/*
+ * Return bios parameters
+ */
+
+int qlogicfas408_biosparam(struct scsi_device * disk,
+ struct block_device *dev,
+ sector_t capacity, int ip[])
+{
+/* This should mimic the DOS Qlogic driver's behavior exactly */
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
+#if 0
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Abort a command in progress
+ */
+
+int qlogicfas408_abort(Scsi_Cmnd * cmd)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ priv->qabort = 1;
+ ql_zap(priv);
+ return SUCCESS;
+}
+
+/*
+ * Reset SCSI bus
+ * FIXME: This function is invoked with cmd = NULL directly by
+ * the PCMCIA qlogic_stub code. This wants fixing
+ */
+
+int qlogicfas408_bus_reset(Scsi_Cmnd * cmd)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ priv->qabort = 2;
+ ql_zap(priv);
+ return SUCCESS;
+}
+
+/*
+ * Reset SCSI host controller
+ */
+
+int qlogicfas408_host_reset(Scsi_Cmnd * cmd)
+{
+ return FAILED;
+}
+
+/*
+ * Reset SCSI device
+ */
+
+int qlogicfas408_device_reset(Scsi_Cmnd * cmd)
+{
+ return FAILED;
+}
+
+/*
+ * Return info string
+ */
+
+const char *qlogicfas408_info(struct Scsi_Host *host)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_host(host);
+ return priv->qinfo;
+}
+
+/*
+ * Get type of chip
+ */
+
+int qlogicfas408_get_chip_type(int qbase, int int_type)
+{
+ REG1;
+ return inb(qbase + 0xe) & 0xf8;
+}
+
+/*
+ * Perform initialization tasks
+ */
+
+void qlogicfas408_setup(int qbase, int id, int int_type)
+{
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ REG0;
+ outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9, qbase + 9); /* prescaler */
+
+#if QL_RESET_AT_START
+ outb(3, qbase + 3);
+
+ REG1;
+ /* FIXME: timeout */
+ while (inb(qbase + 0xf) & 4)
+ cpu_relax();
+
+ REG0;
+#endif
+}
+
+/*
+ * Checks if this is a QLogic FAS 408
+ */
+
+int qlogicfas408_detect(int qbase, int int_type)
+{
+ REG1;
+ return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
+ ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
+}
+
+/*
+ * Disable interrupts
+ */
+
+void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
+{
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+
+ REG1;
+ outb(0, qbase + 0xb); /* disable ints */
+}
+
+/*
+ * Init and exit functions
+ */
+
+static int __init qlogicfas408_init(void)
+{
+ return 0;
+}
+
+static void __exit qlogicfas408_exit(void)
+{
+
+}
+
+MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
+MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
+MODULE_LICENSE("GPL");
+module_init(qlogicfas408_init);
+module_exit(qlogicfas408_exit);
+
+EXPORT_SYMBOL(qlogicfas408_info);
+EXPORT_SYMBOL(qlogicfas408_queuecommand);
+EXPORT_SYMBOL(qlogicfas408_abort);
+EXPORT_SYMBOL(qlogicfas408_bus_reset);
+EXPORT_SYMBOL(qlogicfas408_device_reset);
+EXPORT_SYMBOL(qlogicfas408_host_reset);
+EXPORT_SYMBOL(qlogicfas408_biosparam);
+EXPORT_SYMBOL(qlogicfas408_ihandl);
+EXPORT_SYMBOL(qlogicfas408_get_chip_type);
+EXPORT_SYMBOL(qlogicfas408_setup);
+EXPORT_SYMBOL(qlogicfas408_detect);
+EXPORT_SYMBOL(qlogicfas408_disable_ints);
+
diff --git a/drivers/scsi/qlogicfas408.h b/drivers/scsi/qlogicfas408.h
new file mode 100644
index 000000000000..f01cbd66c224
--- /dev/null
+++ b/drivers/scsi/qlogicfas408.h
@@ -0,0 +1,120 @@
+/* to be used by qlogicfas and qlogic_cs */
+#ifndef __QLOGICFAS408_H
+#define __QLOGICFAS408_H
+
+/*----------------------------------------------------------------*/
+/* Configuration */
+
+/* Set the following to max out the speed of the PIO PseudoDMA transfers,
+ again, 0 tends to be slower, but more stable. */
+
+#define QL_TURBO_PDMA 1
+
+/* This should be 1 to enable parity detection */
+
+#define QL_ENABLE_PARITY 1
+
+/* This will reset all devices when the driver is initialized (during bootup).
+ The other linux drivers don't do this, but the DOS drivers do, and after
+ using DOS or some kind of crash or lockup this will bring things back
+ without requiring a cold boot. It does take some time to recover from a
+ reset, so it is slower, and I have seen timeouts so that devices weren't
+ recognized when this was set. */
+
+#define QL_RESET_AT_START 0
+
+/* crystal frequency in megahertz (for offset 5 and 9)
+ Please set this for your card. Most Qlogic cards are 40 Mhz. The
+ Control Concepts ISA (not VLB) is 24 Mhz */
+
+#define XTALFREQ 40
+
+/**********/
+/* DANGER! modify these at your own risk */
+/* SLOWCABLE can usually be reset to zero if you have a clean setup and
+ proper termination. The rest are for synchronous transfers and other
+ advanced features if your device can transfer faster than 5Mb/sec.
+ If you are really curious, email me for a quick howto until I have
+ something official */
+/**********/
+
+/*****/
+/* config register 1 (offset 8) options */
+/* This needs to be set to 1 if your cabling is long or noisy */
+#define SLOWCABLE 1
+
+/*****/
+/* offset 0xc */
+/* This will set fast (10Mhz) synchronous timing when set to 1
+ For this to have an effect, FASTCLK must also be 1 */
+#define FASTSCSI 0
+
+/* This when set to 1 will set a faster sync transfer rate */
+#define FASTCLK 0 /*(XTALFREQ>25?1:0)*/
+
+/*****/
+/* offset 6 */
+/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
+ achievable data rate (assuming the rest of the system is capable
+ and set properly) */
+#define SYNCXFRPD 5 /*(XTALFREQ/5)*/
+
+/*****/
+/* offset 7 */
+/* This is the count of how many synchronous transfers can take place
+ i.e. how many reqs can occur before an ack is given.
+ The maximum value for this is 15, the upper bits can modify
+ REQ/ACK assertion and deassertion during synchronous transfers
+ If this is 0, the bus will only transfer asynchronously */
+#define SYNCOFFST 0
+/* for the curious, bits 7&6 control the deassertion delay in 1/2 cycles
+ of the 40Mhz clock. If FASTCLK is 1, specifying 01 (1/2) will
+ cause the deassertion to be early by 1/2 clock. Bits 5&4 control
+ the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
+
+/*----------------------------------------------------------------*/
+
+struct qlogicfas408_priv {
+ int qbase; /* Port */
+ int qinitid; /* initiator ID */
+ int qabort; /* Flag to cause an abort */
+ int qlirq; /* IRQ being used */
+ int int_type; /* type of irq, 2 for ISA board, 0 for PCMCIA */
+ char qinfo[80]; /* description */
+ Scsi_Cmnd *qlcmd; /* current command being processed */
+ struct Scsi_Host *shost; /* pointer back to host */
+ struct qlogicfas408_priv *next; /* next private struct */
+};
+
+/* The qlogic card uses two register maps - These macros select which one */
+#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
+#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | int_type, qbase + 0xd ))
+
+/* following is watchdog timeout in microseconds */
+#define WATCHDOG 5000000
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at and as a simple profiler) */
+
+#define rtrc(i) {}
+
+#define get_priv_by_cmd(x) (struct qlogicfas408_priv *)&((x)->device->host->hostdata[0])
+#define get_priv_by_host(x) (struct qlogicfas408_priv *)&((x)->hostdata[0])
+
+irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id, struct pt_regs *regs);
+int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *));
+int qlogicfas408_biosparam(struct scsi_device * disk,
+ struct block_device *dev,
+ sector_t capacity, int ip[]);
+int qlogicfas408_abort(Scsi_Cmnd * cmd);
+int qlogicfas408_bus_reset(Scsi_Cmnd * cmd);
+int qlogicfas408_host_reset(Scsi_Cmnd * cmd);
+int qlogicfas408_device_reset(Scsi_Cmnd * cmd);
+const char *qlogicfas408_info(struct Scsi_Host *host);
+int qlogicfas408_get_chip_type(int qbase, int int_type);
+void qlogicfas408_setup(int qbase, int id, int int_type);
+int qlogicfas408_detect(int qbase, int int_type);
+void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv);
+#endif /* __QLOGICFAS408_H */
+
diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c
new file mode 100644
index 000000000000..24c1174b0c2f
--- /dev/null
+++ b/drivers/scsi/qlogicfc.c
@@ -0,0 +1,2227 @@
+/*
+ * QLogic ISP2x00 SCSI-FCP
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+/* Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu> */
+
+/* This is a version of the isp1020 driver which was modified by
+ * Chris Loveland <cwl@iol.unh.edu> to support the isp2100 and isp2200
+ *
+ * Big endian support and dynamic DMA mapping added
+ * by Jakub Jelinek <jakub@redhat.com>.
+ *
+ * Conversion to final pci64 DMA interfaces
+ * by David S. Miller <davem@redhat.com>.
+ */
+
+/*
+ * $Date: 1995/09/22 02:23:15 $
+ * $Revision: 0.5 $
+ *
+ * $Log: isp1020.c,v $
+ * Revision 0.5 1995/09/22 02:23:15 root
+ * do auto request sense
+ *
+ * Revision 0.4 1995/08/07 04:44:33 root
+ * supply firmware with driver.
+ * numerous bug fixes/general cleanup of code.
+ *
+ * Revision 0.3 1995/07/16 16:15:39 root
+ * added reset/abort code.
+ *
+ * Revision 0.2 1995/06/29 03:14:19 root
+ * fixed biosparam.
+ * added queue protocol.
+ *
+ * Revision 0.1 1995/06/25 01:55:45 root
+ * Initial release.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#define pci64_dma_hi32(a) ((u32) (0xffffffff & (((u64)(a))>>32)))
+#define pci64_dma_lo32(a) ((u32) (0xffffffff & (((u64)(a)))))
+#define pci64_dma_build(hi,lo) \
+ ((dma_addr_t)(((u64)(lo))|(((u64)(hi))<<32)))
+
+/*
+ * With the qlogic interface, every queue slot can hold a SCSI
+ * command with up to 2 scatter/gather entries. If we need more
+ * than 2 entries, continuation entries can be used that hold
+ * another 5 entries each. Unlike for other drivers, this means
+ * that the maximum number of scatter/gather entries we can
+ * support at any given time is a function of the number of queue
+ * slots available. That is, host->can_queue and host->sg_tablesize
+ * are dynamic and _not_ independent. This all works fine because
+ * requests are queued serially and the scatter/gather limit is
+ * determined for each queue request anew.
+ */
+
+#define DATASEGS_PER_COMMAND 2
+#define DATASEGS_PER_CONT 5
+
+#define QLOGICFC_REQ_QUEUE_LEN 255 /* must be power of two - 1 */
+#define QLOGICFC_MAX_SG(ql) (DATASEGS_PER_COMMAND + (((ql) > 0) ? DATASEGS_PER_CONT*((ql) - 1) : 0))
+#define QLOGICFC_CMD_PER_LUN 8
+
+/* Configuration section **************************************************** */
+
+/* Set the following macro to 1 to reload the ISP2x00's firmware. This is
+ version 1.17.30 of the isp2100's firmware and version 2.00.40 of the
+ isp2200's firmware.
+*/
+
+#define USE_NVRAM_DEFAULTS 1
+
+#define ISP2x00_PORTDB 1
+
+/* Set the following to 1 to include fabric support, fabric support is
+ * currently not as well tested as the other aspects of the driver */
+
+#define ISP2x00_FABRIC 1
+
+/* Macros used for debugging */
+#define DEBUG_ISP2x00 0
+#define DEBUG_ISP2x00_INT 0
+#define DEBUG_ISP2x00_INTR 0
+#define DEBUG_ISP2x00_SETUP 0
+#define DEBUG_ISP2x00_FABRIC 0
+#define TRACE_ISP 0
+
+
+#define DEFAULT_LOOP_COUNT 1000000000
+
+#define ISP_TIMEOUT (2*HZ)
+/* End Configuration section ************************************************ */
+
+#include <linux/module.h>
+
+#if TRACE_ISP
+
+#define TRACE_BUF_LEN (32*1024)
+
+struct {
+ u_long next;
+ struct {
+ u_long time;
+ u_int index;
+ u_int addr;
+ u_char *name;
+ } buf[TRACE_BUF_LEN];
+} trace;
+
+#define TRACE(w, i, a) \
+{ \
+ unsigned long flags; \
+ \
+ save_flags(flags); \
+ cli(); \
+ trace.buf[trace.next].name = (w); \
+ trace.buf[trace.next].time = jiffies; \
+ trace.buf[trace.next].index = (i); \
+ trace.buf[trace.next].addr = (long) (a); \
+ trace.next = (trace.next + 1) & (TRACE_BUF_LEN - 1); \
+ restore_flags(flags); \
+}
+
+#else
+#define TRACE(w, i, a)
+#endif
+
+#if DEBUG_ISP2x00_FABRIC
+#define DEBUG_FABRIC(x) x
+#else
+#define DEBUG_FABRIC(x)
+#endif /* DEBUG_ISP2x00_FABRIC */
+
+
+#if DEBUG_ISP2x00
+#define ENTER(x) printk("isp2x00 : entering %s()\n", x);
+#define LEAVE(x) printk("isp2x00 : leaving %s()\n", x);
+#define DEBUG(x) x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif /* DEBUG_ISP2x00 */
+
+#if DEBUG_ISP2x00_INTR
+#define ENTER_INTR(x) printk("isp2x00 : entering %s()\n", x);
+#define LEAVE_INTR(x) printk("isp2x00 : leaving %s()\n", x);
+#define DEBUG_INTR(x) x
+#else
+#define ENTER_INTR(x)
+#define LEAVE_INTR(x)
+#define DEBUG_INTR(x)
+#endif /* DEBUG ISP2x00_INTR */
+
+
+#define ISP2100_REV_ID1 1
+#define ISP2100_REV_ID3 3
+#define ISP2200_REV_ID5 5
+
+/* host configuration and control registers */
+#define HOST_HCCR 0xc0 /* host command and control */
+
+/* pci bus interface registers */
+#define FLASH_BIOS_ADDR 0x00
+#define FLASH_BIOS_DATA 0x02
+#define ISP_CTRL_STATUS 0x06 /* configuration register #1 */
+#define PCI_INTER_CTL 0x08 /* pci interrupt control */
+#define PCI_INTER_STS 0x0a /* pci interrupt status */
+#define PCI_SEMAPHORE 0x0c /* pci semaphore */
+#define PCI_NVRAM 0x0e /* pci nvram interface */
+
+/* mailbox registers */
+#define MBOX0 0x10 /* mailbox 0 */
+#define MBOX1 0x12 /* mailbox 1 */
+#define MBOX2 0x14 /* mailbox 2 */
+#define MBOX3 0x16 /* mailbox 3 */
+#define MBOX4 0x18 /* mailbox 4 */
+#define MBOX5 0x1a /* mailbox 5 */
+#define MBOX6 0x1c /* mailbox 6 */
+#define MBOX7 0x1e /* mailbox 7 */
+
+/* mailbox command complete status codes */
+#define MBOX_COMMAND_COMPLETE 0x4000
+#define INVALID_COMMAND 0x4001
+#define HOST_INTERFACE_ERROR 0x4002
+#define TEST_FAILED 0x4003
+#define COMMAND_ERROR 0x4005
+#define COMMAND_PARAM_ERROR 0x4006
+#define PORT_ID_USED 0x4007
+#define LOOP_ID_USED 0x4008
+#define ALL_IDS_USED 0x4009
+
+/* async event status codes */
+#define RESET_DETECTED 0x8001
+#define SYSTEM_ERROR 0x8002
+#define REQUEST_TRANSFER_ERROR 0x8003
+#define RESPONSE_TRANSFER_ERROR 0x8004
+#define REQUEST_QUEUE_WAKEUP 0x8005
+#define LIP_OCCURRED 0x8010
+#define LOOP_UP 0x8011
+#define LOOP_DOWN 0x8012
+#define LIP_RECEIVED 0x8013
+#define PORT_DB_CHANGED 0x8014
+#define CHANGE_NOTIFICATION 0x8015
+#define SCSI_COMMAND_COMPLETE 0x8020
+#define POINT_TO_POINT_UP 0x8030
+#define CONNECTION_MODE 0x8036
+
+struct Entry_header {
+ u_char entry_type;
+ u_char entry_cnt;
+ u_char sys_def_1;
+ u_char flags;
+};
+
+/* entry header type commands */
+#define ENTRY_COMMAND 0x19
+#define ENTRY_CONTINUATION 0x0a
+
+#define ENTRY_STATUS 0x03
+#define ENTRY_MARKER 0x04
+
+
+/* entry header flag definitions */
+#define EFLAG_BUSY 2
+#define EFLAG_BAD_HEADER 4
+#define EFLAG_BAD_PAYLOAD 8
+
+struct dataseg {
+ u_int d_base;
+ u_int d_base_hi;
+ u_int d_count;
+};
+
+struct Command_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_char target_lun;
+ u_char target_id;
+ u_short expanded_lun;
+ u_short control_flags;
+ u_short rsvd2;
+ u_short time_out;
+ u_short segment_cnt;
+ u_char cdb[16];
+ u_int total_byte_cnt;
+ struct dataseg dataseg[DATASEGS_PER_COMMAND];
+};
+
+/* command entry control flag definitions */
+#define CFLAG_NODISC 0x01
+#define CFLAG_HEAD_TAG 0x02
+#define CFLAG_ORDERED_TAG 0x04
+#define CFLAG_SIMPLE_TAG 0x08
+#define CFLAG_TAR_RTN 0x10
+#define CFLAG_READ 0x20
+#define CFLAG_WRITE 0x40
+
+struct Continuation_Entry {
+ struct Entry_header hdr;
+ struct dataseg dataseg[DATASEGS_PER_CONT];
+};
+
+struct Marker_Entry {
+ struct Entry_header hdr;
+ u_int reserved;
+ u_char target_lun;
+ u_char target_id;
+ u_char modifier;
+ u_char expanded_lun;
+ u_char rsvds[52];
+};
+
+/* marker entry modifier definitions */
+#define SYNC_DEVICE 0
+#define SYNC_TARGET 1
+#define SYNC_ALL 2
+
+struct Status_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_short scsi_status;
+ u_short completion_status;
+ u_short state_flags;
+ u_short status_flags;
+ u_short res_info_len;
+ u_short req_sense_len;
+ u_int residual;
+ u_char res_info[8];
+ u_char req_sense_data[32];
+};
+
+/* status entry completion status definitions */
+#define CS_COMPLETE 0x0000
+#define CS_DMA_ERROR 0x0002
+#define CS_RESET_OCCURRED 0x0004
+#define CS_ABORTED 0x0005
+#define CS_TIMEOUT 0x0006
+#define CS_DATA_OVERRUN 0x0007
+#define CS_DATA_UNDERRUN 0x0015
+#define CS_QUEUE_FULL 0x001c
+#define CS_PORT_UNAVAILABLE 0x0028
+#define CS_PORT_LOGGED_OUT 0x0029
+#define CS_PORT_CONFIG_CHANGED 0x002a
+
+/* status entry state flag definitions */
+#define SF_SENT_CDB 0x0400
+#define SF_TRANSFERRED_DATA 0x0800
+#define SF_GOT_STATUS 0x1000
+
+/* status entry status flag definitions */
+#define STF_BUS_RESET 0x0008
+#define STF_DEVICE_RESET 0x0010
+#define STF_ABORTED 0x0020
+#define STF_TIMEOUT 0x0040
+
+/* interrupt control commands */
+#define ISP_EN_INT 0x8000
+#define ISP_EN_RISC 0x0008
+
+/* host control commands */
+#define HCCR_NOP 0x0000
+#define HCCR_RESET 0x1000
+#define HCCR_PAUSE 0x2000
+#define HCCR_RELEASE 0x3000
+#define HCCR_SINGLE_STEP 0x4000
+#define HCCR_SET_HOST_INTR 0x5000
+#define HCCR_CLEAR_HOST_INTR 0x6000
+#define HCCR_CLEAR_RISC_INTR 0x7000
+#define HCCR_BP_ENABLE 0x8000
+#define HCCR_BIOS_DISABLE 0x9000
+#define HCCR_TEST_MODE 0xf000
+
+#define RISC_BUSY 0x0004
+
+/* mailbox commands */
+#define MBOX_NO_OP 0x0000
+#define MBOX_LOAD_RAM 0x0001
+#define MBOX_EXEC_FIRMWARE 0x0002
+#define MBOX_DUMP_RAM 0x0003
+#define MBOX_WRITE_RAM_WORD 0x0004
+#define MBOX_READ_RAM_WORD 0x0005
+#define MBOX_MAILBOX_REG_TEST 0x0006
+#define MBOX_VERIFY_CHECKSUM 0x0007
+#define MBOX_ABOUT_FIRMWARE 0x0008
+#define MBOX_LOAD_RISC_RAM 0x0009
+#define MBOX_DUMP_RISC_RAM 0x000a
+#define MBOX_CHECK_FIRMWARE 0x000e
+#define MBOX_INIT_REQ_QUEUE 0x0010
+#define MBOX_INIT_RES_QUEUE 0x0011
+#define MBOX_EXECUTE_IOCB 0x0012
+#define MBOX_WAKE_UP 0x0013
+#define MBOX_STOP_FIRMWARE 0x0014
+#define MBOX_ABORT_IOCB 0x0015
+#define MBOX_ABORT_DEVICE 0x0016
+#define MBOX_ABORT_TARGET 0x0017
+#define MBOX_BUS_RESET 0x0018
+#define MBOX_STOP_QUEUE 0x0019
+#define MBOX_START_QUEUE 0x001a
+#define MBOX_SINGLE_STEP_QUEUE 0x001b
+#define MBOX_ABORT_QUEUE 0x001c
+#define MBOX_GET_DEV_QUEUE_STATUS 0x001d
+#define MBOX_GET_FIRMWARE_STATUS 0x001f
+#define MBOX_GET_INIT_SCSI_ID 0x0020
+#define MBOX_GET_RETRY_COUNT 0x0022
+#define MBOX_GET_TARGET_PARAMS 0x0028
+#define MBOX_GET_DEV_QUEUE_PARAMS 0x0029
+#define MBOX_SET_RETRY_COUNT 0x0032
+#define MBOX_SET_TARGET_PARAMS 0x0038
+#define MBOX_SET_DEV_QUEUE_PARAMS 0x0039
+#define MBOX_EXECUTE_IOCB64 0x0054
+#define MBOX_INIT_FIRMWARE 0x0060
+#define MBOX_GET_INIT_CB 0x0061
+#define MBOX_INIT_LIP 0x0062
+#define MBOX_GET_POS_MAP 0x0063
+#define MBOX_GET_PORT_DB 0x0064
+#define MBOX_CLEAR_ACA 0x0065
+#define MBOX_TARGET_RESET 0x0066
+#define MBOX_CLEAR_TASK_SET 0x0067
+#define MBOX_ABORT_TASK_SET 0x0068
+#define MBOX_GET_FIRMWARE_STATE 0x0069
+#define MBOX_GET_PORT_NAME 0x006a
+#define MBOX_SEND_SNS 0x006e
+#define MBOX_PORT_LOGIN 0x006f
+#define MBOX_SEND_CHANGE_REQUEST 0x0070
+#define MBOX_PORT_LOGOUT 0x0071
+
+/*
+ * Firmware if needed (note this is a hack, it belongs in a separate
+ * module.
+ */
+
+#ifdef CONFIG_SCSI_QLOGIC_FC_FIRMWARE
+#include "qlogicfc_asm.c"
+#else
+static unsigned short risc_code_addr01 = 0x1000 ;
+#endif
+
+/* Each element in mbox_param is an 8 bit bitmap where each bit indicates
+ if that mbox should be copied as input. For example 0x2 would mean
+ only copy mbox1. */
+
+static const u_char mbox_param[] =
+{
+ 0x01, /* MBOX_NO_OP */
+ 0x1f, /* MBOX_LOAD_RAM */
+ 0x03, /* MBOX_EXEC_FIRMWARE */
+ 0x1f, /* MBOX_DUMP_RAM */
+ 0x07, /* MBOX_WRITE_RAM_WORD */
+ 0x03, /* MBOX_READ_RAM_WORD */
+ 0xff, /* MBOX_MAILBOX_REG_TEST */
+ 0x03, /* MBOX_VERIFY_CHECKSUM */
+ 0x01, /* MBOX_ABOUT_FIRMWARE */
+ 0xff, /* MBOX_LOAD_RISC_RAM */
+ 0xff, /* MBOX_DUMP_RISC_RAM */
+ 0x00, /* 0x000b */
+ 0x00, /* 0x000c */
+ 0x00, /* 0x000d */
+ 0x01, /* MBOX_CHECK_FIRMWARE */
+ 0x00, /* 0x000f */
+ 0x1f, /* MBOX_INIT_REQ_QUEUE */
+ 0x2f, /* MBOX_INIT_RES_QUEUE */
+ 0x0f, /* MBOX_EXECUTE_IOCB */
+ 0x03, /* MBOX_WAKE_UP */
+ 0x01, /* MBOX_STOP_FIRMWARE */
+ 0x0f, /* MBOX_ABORT_IOCB */
+ 0x03, /* MBOX_ABORT_DEVICE */
+ 0x07, /* MBOX_ABORT_TARGET */
+ 0x03, /* MBOX_BUS_RESET */
+ 0x03, /* MBOX_STOP_QUEUE */
+ 0x03, /* MBOX_START_QUEUE */
+ 0x03, /* MBOX_SINGLE_STEP_QUEUE */
+ 0x03, /* MBOX_ABORT_QUEUE */
+ 0x03, /* MBOX_GET_DEV_QUEUE_STATUS */
+ 0x00, /* 0x001e */
+ 0x01, /* MBOX_GET_FIRMWARE_STATUS */
+ 0x01, /* MBOX_GET_INIT_SCSI_ID */
+ 0x00, /* 0x0021 */
+ 0x01, /* MBOX_GET_RETRY_COUNT */
+ 0x00, /* 0x0023 */
+ 0x00, /* 0x0024 */
+ 0x00, /* 0x0025 */
+ 0x00, /* 0x0026 */
+ 0x00, /* 0x0027 */
+ 0x03, /* MBOX_GET_TARGET_PARAMS */
+ 0x03, /* MBOX_GET_DEV_QUEUE_PARAMS */
+ 0x00, /* 0x002a */
+ 0x00, /* 0x002b */
+ 0x00, /* 0x002c */
+ 0x00, /* 0x002d */
+ 0x00, /* 0x002e */
+ 0x00, /* 0x002f */
+ 0x00, /* 0x0030 */
+ 0x00, /* 0x0031 */
+ 0x07, /* MBOX_SET_RETRY_COUNT */
+ 0x00, /* 0x0033 */
+ 0x00, /* 0x0034 */
+ 0x00, /* 0x0035 */
+ 0x00, /* 0x0036 */
+ 0x00, /* 0x0037 */
+ 0x0f, /* MBOX_SET_TARGET_PARAMS */
+ 0x0f, /* MBOX_SET_DEV_QUEUE_PARAMS */
+ 0x00, /* 0x003a */
+ 0x00, /* 0x003b */
+ 0x00, /* 0x003c */
+ 0x00, /* 0x003d */
+ 0x00, /* 0x003e */
+ 0x00, /* 0x003f */
+ 0x00, /* 0x0040 */
+ 0x00, /* 0x0041 */
+ 0x00, /* 0x0042 */
+ 0x00, /* 0x0043 */
+ 0x00, /* 0x0044 */
+ 0x00, /* 0x0045 */
+ 0x00, /* 0x0046 */
+ 0x00, /* 0x0047 */
+ 0x00, /* 0x0048 */
+ 0x00, /* 0x0049 */
+ 0x00, /* 0x004a */
+ 0x00, /* 0x004b */
+ 0x00, /* 0x004c */
+ 0x00, /* 0x004d */
+ 0x00, /* 0x004e */
+ 0x00, /* 0x004f */
+ 0x00, /* 0x0050 */
+ 0x00, /* 0x0051 */
+ 0x00, /* 0x0052 */
+ 0x00, /* 0x0053 */
+ 0xcf, /* MBOX_EXECUTE_IOCB64 */
+ 0x00, /* 0x0055 */
+ 0x00, /* 0x0056 */
+ 0x00, /* 0x0057 */
+ 0x00, /* 0x0058 */
+ 0x00, /* 0x0059 */
+ 0x00, /* 0x005a */
+ 0x00, /* 0x005b */
+ 0x00, /* 0x005c */
+ 0x00, /* 0x005d */
+ 0x00, /* 0x005e */
+ 0x00, /* 0x005f */
+ 0xff, /* MBOX_INIT_FIRMWARE */
+ 0xcd, /* MBOX_GET_INIT_CB */
+ 0x01, /* MBOX_INIT_LIP */
+ 0xcd, /* MBOX_GET_POS_MAP */
+ 0xcf, /* MBOX_GET_PORT_DB */
+ 0x03, /* MBOX_CLEAR_ACA */
+ 0x03, /* MBOX_TARGET_RESET */
+ 0x03, /* MBOX_CLEAR_TASK_SET */
+ 0x03, /* MBOX_ABORT_TASK_SET */
+ 0x01, /* MBOX_GET_FIRMWARE_STATE */
+ 0x03, /* MBOX_GET_PORT_NAME */
+ 0x00, /* 0x006b */
+ 0x00, /* 0x006c */
+ 0x00, /* 0x006d */
+ 0xcf, /* MBOX_SEND_SNS */
+ 0x0f, /* MBOX_PORT_LOGIN */
+ 0x03, /* MBOX_SEND_CHANGE_REQUEST */
+ 0x03, /* MBOX_PORT_LOGOUT */
+};
+
+#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short))
+
+
+struct id_name_map {
+ u64 wwn;
+ u_char loop_id;
+};
+
+struct sns_cb {
+ u_short len;
+ u_short res1;
+ u_int response_low;
+ u_int response_high;
+ u_short sub_len;
+ u_short res2;
+ u_char data[44];
+};
+
+/* address of instance of this struct is passed to adapter to initialize things
+ */
+struct init_cb {
+ u_char version;
+ u_char reseverd1[1];
+ u_short firm_opts;
+ u_short max_frame_len;
+ u_short max_iocb;
+ u_short exec_throttle;
+ u_char retry_cnt;
+ u_char retry_delay;
+ u_short node_name[4];
+ u_short hard_addr;
+ u_char reserved2[10];
+ u_short req_queue_out;
+ u_short res_queue_in;
+ u_short req_queue_len;
+ u_short res_queue_len;
+ u_int req_queue_addr_lo;
+ u_int req_queue_addr_high;
+ u_int res_queue_addr_lo;
+ u_int res_queue_addr_high;
+ /* the rest of this structure only applies to the isp2200 */
+ u_short lun_enables;
+ u_char cmd_resource_cnt;
+ u_char notify_resource_cnt;
+ u_short timeout;
+ u_short reserved3;
+ u_short add_firm_opts;
+ u_char res_accum_timer;
+ u_char irq_delay_timer;
+ u_short special_options;
+ u_short reserved4[13];
+};
+
+/*
+ * The result queue can be quite a bit smaller since continuation entries
+ * do not show up there:
+ */
+#define RES_QUEUE_LEN ((QLOGICFC_REQ_QUEUE_LEN + 1) / 8 - 1)
+#define QUEUE_ENTRY_LEN 64
+
+#if ISP2x00_FABRIC
+#define QLOGICFC_MAX_ID 0xff
+#else
+#define QLOGICFC_MAX_ID 0x7d
+#endif
+
+#define QLOGICFC_MAX_LUN 128
+#define QLOGICFC_MAX_LOOP_ID 0x7d
+
+/* the following connection options only apply to the 2200. i have only
+ * had success with LOOP_ONLY and P2P_ONLY.
+ */
+
+#define LOOP_ONLY 0
+#define P2P_ONLY 1
+#define LOOP_PREFERED 2
+#define P2P_PREFERED 3
+
+#define CONNECTION_PREFERENCE LOOP_ONLY
+
+/* adapter_state values */
+#define AS_FIRMWARE_DEAD -1
+#define AS_LOOP_DOWN 0
+#define AS_LOOP_GOOD 1
+#define AS_REDO_FABRIC_PORTDB 2
+#define AS_REDO_LOOP_PORTDB 4
+
+#define RES_SIZE ((RES_QUEUE_LEN + 1)*QUEUE_ENTRY_LEN)
+#define REQ_SIZE ((QLOGICFC_REQ_QUEUE_LEN + 1)*QUEUE_ENTRY_LEN)
+
+struct isp2x00_hostdata {
+ u_char revision;
+ struct pci_dev *pci_dev;
+ /* result and request queues (shared with isp2x00): */
+ u_int req_in_ptr; /* index of next request slot */
+ u_int res_out_ptr; /* index of next result slot */
+
+ /* this is here so the queues are nicely aligned */
+ long send_marker; /* do we need to send a marker? */
+
+ char * res;
+ char * req;
+ struct init_cb control_block;
+ int adapter_state;
+ unsigned long int tag_ages[QLOGICFC_MAX_ID + 1];
+ Scsi_Cmnd *handle_ptrs[QLOGICFC_REQ_QUEUE_LEN + 1];
+ unsigned long handle_serials[QLOGICFC_REQ_QUEUE_LEN + 1];
+ struct id_name_map port_db[QLOGICFC_MAX_ID + 1];
+ u_char mbox_done;
+ u64 wwn;
+ u_int port_id;
+ u_char queued;
+ u_char host_id;
+ struct timer_list explore_timer;
+ struct id_name_map tempmap[QLOGICFC_MAX_ID + 1];
+};
+
+
+/* queue length's _must_ be power of two: */
+#define QUEUE_DEPTH(in, out, ql) ((in - out) & (ql))
+#define REQ_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, \
+ QLOGICFC_REQ_QUEUE_LEN)
+#define RES_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, RES_QUEUE_LEN)
+
+static void isp2x00_enable_irqs(struct Scsi_Host *);
+static void isp2x00_disable_irqs(struct Scsi_Host *);
+static int isp2x00_init(struct Scsi_Host *);
+static int isp2x00_reset_hardware(struct Scsi_Host *);
+static int isp2x00_mbox_command(struct Scsi_Host *, u_short[]);
+static int isp2x00_return_status(Scsi_Cmnd *, struct Status_Entry *);
+static void isp2x00_intr_handler(int, void *, struct pt_regs *);
+static irqreturn_t do_isp2x00_intr_handler(int, void *, struct pt_regs *);
+static int isp2x00_make_portdb(struct Scsi_Host *);
+
+#if ISP2x00_FABRIC
+static int isp2x00_init_fabric(struct Scsi_Host *, struct id_name_map *, int);
+#endif
+
+#if USE_NVRAM_DEFAULTS
+static int isp2x00_get_nvram_defaults(struct Scsi_Host *, struct init_cb *);
+static u_short isp2x00_read_nvram_word(struct Scsi_Host *, u_short);
+#endif
+
+#if DEBUG_ISP2x00
+static void isp2x00_print_scsi_cmd(Scsi_Cmnd *);
+#endif
+
+#if DEBUG_ISP2x00_INTR
+static void isp2x00_print_status_entry(struct Status_Entry *);
+#endif
+
+static inline void isp2x00_enable_irqs(struct Scsi_Host *host)
+{
+ outw(ISP_EN_INT | ISP_EN_RISC, host->io_port + PCI_INTER_CTL);
+}
+
+
+static inline void isp2x00_disable_irqs(struct Scsi_Host *host)
+{
+ outw(0x0, host->io_port + PCI_INTER_CTL);
+}
+
+
+static int isp2x00_detect(Scsi_Host_Template * tmpt)
+{
+ int hosts = 0;
+ unsigned long wait_time;
+ struct Scsi_Host *host = NULL;
+ struct isp2x00_hostdata *hostdata;
+ struct pci_dev *pdev;
+ unsigned short device_ids[2];
+ dma_addr_t busaddr;
+ int i;
+
+
+ ENTER("isp2x00_detect");
+
+ device_ids[0] = PCI_DEVICE_ID_QLOGIC_ISP2100;
+ device_ids[1] = PCI_DEVICE_ID_QLOGIC_ISP2200;
+
+ tmpt->proc_name = "isp2x00";
+
+ for (i=0; i<2; i++){
+ pdev = NULL;
+ while ((pdev = pci_find_device(PCI_VENDOR_ID_QLOGIC, device_ids[i], pdev))) {
+ if (pci_enable_device(pdev))
+ continue;
+
+ /* Try to configure DMA attributes. */
+ if (pci_set_dma_mask(pdev, 0xffffffffffffffffULL) &&
+ pci_set_dma_mask(pdev, 0xffffffffULL))
+ continue;
+
+ host = scsi_register(tmpt, sizeof(struct isp2x00_hostdata));
+ if (!host) {
+ printk("qlogicfc%d : could not register host.\n", hosts);
+ continue;
+ }
+ scsi_set_device(host, &pdev->dev);
+ host->max_id = QLOGICFC_MAX_ID + 1;
+ host->max_lun = QLOGICFC_MAX_LUN;
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ memset(hostdata, 0, sizeof(struct isp2x00_hostdata));
+ hostdata->pci_dev = pdev;
+ hostdata->res = pci_alloc_consistent(pdev, RES_SIZE + REQ_SIZE, &busaddr);
+
+ if (!hostdata->res){
+ printk("qlogicfc%d : could not allocate memory for request and response queue.\n", hosts);
+ scsi_unregister(host);
+ continue;
+ }
+ hostdata->req = hostdata->res + (RES_QUEUE_LEN + 1)*QUEUE_ENTRY_LEN;
+ hostdata->queued = 0;
+ /* set up the control block */
+ hostdata->control_block.version = 0x1;
+ hostdata->control_block.firm_opts = cpu_to_le16(0x800e);
+ hostdata->control_block.max_frame_len = cpu_to_le16(2048);
+ hostdata->control_block.max_iocb = cpu_to_le16(QLOGICFC_REQ_QUEUE_LEN);
+ hostdata->control_block.exec_throttle = cpu_to_le16(QLOGICFC_CMD_PER_LUN);
+ hostdata->control_block.retry_delay = 5;
+ hostdata->control_block.retry_cnt = 1;
+ hostdata->control_block.node_name[0] = cpu_to_le16(0x0020);
+ hostdata->control_block.node_name[1] = cpu_to_le16(0xE000);
+ hostdata->control_block.node_name[2] = cpu_to_le16(0x008B);
+ hostdata->control_block.node_name[3] = cpu_to_le16(0x0000);
+ hostdata->control_block.hard_addr = cpu_to_le16(0x0003);
+ hostdata->control_block.req_queue_len = cpu_to_le16(QLOGICFC_REQ_QUEUE_LEN + 1);
+ hostdata->control_block.res_queue_len = cpu_to_le16(RES_QUEUE_LEN + 1);
+ hostdata->control_block.res_queue_addr_lo = cpu_to_le32(pci64_dma_lo32(busaddr));
+ hostdata->control_block.res_queue_addr_high = cpu_to_le32(pci64_dma_hi32(busaddr));
+ hostdata->control_block.req_queue_addr_lo = cpu_to_le32(pci64_dma_lo32(busaddr + RES_SIZE));
+ hostdata->control_block.req_queue_addr_high = cpu_to_le32(pci64_dma_hi32(busaddr + RES_SIZE));
+
+
+ hostdata->control_block.add_firm_opts |= cpu_to_le16(CONNECTION_PREFERENCE<<4);
+ hostdata->adapter_state = AS_LOOP_DOWN;
+ hostdata->explore_timer.data = 1;
+ hostdata->host_id = hosts;
+
+ if (isp2x00_init(host) || isp2x00_reset_hardware(host)) {
+ pci_free_consistent (pdev, RES_SIZE + REQ_SIZE, hostdata->res, busaddr);
+ scsi_unregister(host);
+ continue;
+ }
+ host->this_id = 0;
+
+ if (request_irq(host->irq, do_isp2x00_intr_handler, SA_INTERRUPT | SA_SHIRQ, "qlogicfc", host)) {
+ printk("qlogicfc%d : interrupt %d already in use\n",
+ hostdata->host_id, host->irq);
+ pci_free_consistent (pdev, RES_SIZE + REQ_SIZE, hostdata->res, busaddr);
+ scsi_unregister(host);
+ continue;
+ }
+ if (!request_region(host->io_port, 0xff, "qlogicfc")) {
+ printk("qlogicfc%d : i/o region 0x%lx-0x%lx already "
+ "in use\n",
+ hostdata->host_id, host->io_port, host->io_port + 0xff);
+ free_irq(host->irq, host);
+ pci_free_consistent (pdev, RES_SIZE + REQ_SIZE, hostdata->res, busaddr);
+ scsi_unregister(host);
+ continue;
+ }
+
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+ isp2x00_enable_irqs(host);
+ /* wait for the loop to come up */
+ for (wait_time = jiffies + 10 * HZ; time_before(jiffies, wait_time) && hostdata->adapter_state == AS_LOOP_DOWN;) {
+ barrier();
+ cpu_relax();
+ }
+ if (hostdata->adapter_state == AS_LOOP_DOWN) {
+ printk("qlogicfc%d : link is not up\n", hostdata->host_id);
+ }
+ hosts++;
+ hostdata->explore_timer.data = 0;
+ }
+ }
+
+
+ /* this busy loop should not be needed but the isp2x00 seems to need
+ some time before recognizing it is attached to a fabric */
+
+#if ISP2x00_FABRIC
+ if (hosts) {
+ for (wait_time = jiffies + 5 * HZ; time_before(jiffies, wait_time);) {
+ barrier();
+ cpu_relax();
+ }
+ }
+#endif
+
+ LEAVE("isp2x00_detect");
+
+ return hosts;
+}
+
+
+static int isp2x00_make_portdb(struct Scsi_Host *host)
+{
+
+ short param[8];
+ int i, j;
+ struct isp2x00_hostdata *hostdata;
+
+ isp2x00_disable_irqs(host);
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+ memset(hostdata->tempmap, 0, sizeof(hostdata->tempmap));
+
+#if ISP2x00_FABRIC
+ for (i = 0x81; i < QLOGICFC_MAX_ID; i++) {
+ param[0] = MBOX_PORT_LOGOUT;
+ param[1] = i << 8;
+ param[2] = 0;
+ param[3] = 0;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+
+ DEBUG_FABRIC(printk("qlogicfc%d : logout failed %x %x\n", hostdata->host_id, i, param[0]));
+ }
+ }
+#endif
+
+
+ param[0] = MBOX_GET_INIT_SCSI_ID;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] == MBOX_COMMAND_COMPLETE) {
+ hostdata->port_id = ((u_int) param[3]) << 16;
+ hostdata->port_id |= param[2];
+ hostdata->tempmap[0].loop_id = param[1];
+ hostdata->tempmap[0].wwn = hostdata->wwn;
+ }
+ else {
+ printk("qlogicfc%d : error getting scsi id.\n", hostdata->host_id);
+ }
+
+ for (i = 0; i <=QLOGICFC_MAX_ID; i++)
+ hostdata->tempmap[i].loop_id = hostdata->tempmap[0].loop_id;
+
+ for (i = 0, j = 1; i <= QLOGICFC_MAX_LOOP_ID; i++) {
+ param[0] = MBOX_GET_PORT_NAME;
+ param[1] = (i << 8) & 0xff00;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] == MBOX_COMMAND_COMPLETE) {
+ hostdata->tempmap[j].loop_id = i;
+ hostdata->tempmap[j].wwn = ((u64) (param[2] & 0xff)) << 56;
+ hostdata->tempmap[j].wwn |= ((u64) ((param[2] >> 8) & 0xff)) << 48;
+ hostdata->tempmap[j].wwn |= ((u64) (param[3] & 0xff)) << 40;
+ hostdata->tempmap[j].wwn |= ((u64) ((param[3] >> 8) & 0xff)) << 32;
+ hostdata->tempmap[j].wwn |= ((u64) (param[6] & 0xff)) << 24;
+ hostdata->tempmap[j].wwn |= ((u64) ((param[6] >> 8) & 0xff)) << 16;
+ hostdata->tempmap[j].wwn |= ((u64) (param[7] & 0xff)) << 8;
+ hostdata->tempmap[j].wwn |= ((u64) ((param[7] >> 8) & 0xff));
+
+ j++;
+
+ }
+ }
+
+
+#if ISP2x00_FABRIC
+ isp2x00_init_fabric(host, hostdata->tempmap, j);
+#endif
+
+ for (i = 0; i <= QLOGICFC_MAX_ID; i++) {
+ if (hostdata->tempmap[i].wwn != hostdata->port_db[i].wwn) {
+ for (j = 0; j <= QLOGICFC_MAX_ID; j++) {
+ if (hostdata->tempmap[j].wwn == hostdata->port_db[i].wwn) {
+ hostdata->port_db[i].loop_id = hostdata->tempmap[j].loop_id;
+ break;
+ }
+ }
+ if (j == QLOGICFC_MAX_ID + 1)
+ hostdata->port_db[i].loop_id = hostdata->tempmap[0].loop_id;
+
+ for (j = 0; j <= QLOGICFC_MAX_ID; j++) {
+ if (hostdata->port_db[j].wwn == hostdata->tempmap[i].wwn || !hostdata->port_db[j].wwn) {
+ break;
+ }
+ }
+ if (j == QLOGICFC_MAX_ID + 1)
+ printk("qlogicfc%d : Too many scsi devices, no more room in port map.\n", hostdata->host_id);
+ if (!hostdata->port_db[j].wwn) {
+ hostdata->port_db[j].loop_id = hostdata->tempmap[i].loop_id;
+ hostdata->port_db[j].wwn = hostdata->tempmap[i].wwn;
+ }
+ } else
+ hostdata->port_db[i].loop_id = hostdata->tempmap[i].loop_id;
+
+ }
+
+ isp2x00_enable_irqs(host);
+
+ return 0;
+}
+
+
+#if ISP2x00_FABRIC
+
+#define FABRIC_PORT 0x7e
+#define FABRIC_CONTROLLER 0x7f
+#define FABRIC_SNS 0x80
+
+int isp2x00_init_fabric(struct Scsi_Host *host, struct id_name_map *port_db, int cur_scsi_id)
+{
+
+ u_short param[8];
+ u64 wwn;
+ int done = 0;
+ u_short loop_id = 0x81;
+ u_short scsi_id = cur_scsi_id;
+ u_int port_id;
+ struct sns_cb *req;
+ u_char *sns_response;
+ dma_addr_t busaddr;
+ struct isp2x00_hostdata *hostdata;
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ DEBUG_FABRIC(printk("qlogicfc%d : Checking for a fabric.\n", hostdata->host_id));
+ param[0] = MBOX_GET_PORT_NAME;
+ param[1] = (u16)FABRIC_PORT << 8;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ DEBUG_FABRIC(printk("qlogicfc%d : fabric check result %x\n", hostdata->host_id, param[0]));
+ return 0;
+ }
+ printk("qlogicfc%d : Fabric found.\n", hostdata->host_id);
+
+ req = (struct sns_cb *)pci_alloc_consistent(hostdata->pci_dev, sizeof(*req) + 608, &busaddr);
+
+ if (!req){
+ printk("qlogicfc%d : Could not allocate DMA resources for fabric initialization\n", hostdata->host_id);
+ return 0;
+ }
+ sns_response = (u_char *)(req + 1);
+
+ if (hostdata->adapter_state & AS_REDO_LOOP_PORTDB){
+ memset(req, 0, sizeof(*req));
+
+ req->len = cpu_to_le16(8);
+ req->response_low = cpu_to_le32(pci64_dma_lo32(busaddr + sizeof(*req)));
+ req->response_high = cpu_to_le32(pci64_dma_hi32(busaddr + sizeof(*req)));
+ req->sub_len = cpu_to_le16(22);
+ req->data[0] = 0x17;
+ req->data[1] = 0x02;
+ req->data[8] = (u_char) (hostdata->port_id & 0xff);
+ req->data[9] = (u_char) (hostdata->port_id >> 8 & 0xff);
+ req->data[10] = (u_char) (hostdata->port_id >> 16 & 0xff);
+ req->data[13] = 0x01;
+ param[0] = MBOX_SEND_SNS;
+ param[1] = 30;
+ param[2] = pci64_dma_lo32(busaddr) >> 16;
+ param[3] = pci64_dma_lo32(busaddr);
+ param[6] = pci64_dma_hi32(busaddr) >> 16;
+ param[7] = pci64_dma_hi32(busaddr);
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE)
+ printk("qlogicfc%d : error sending RFC-4\n", hostdata->host_id);
+ }
+
+ port_id = hostdata->port_id;
+ while (!done) {
+ memset(req, 0, sizeof(*req));
+
+ req->len = cpu_to_le16(304);
+ req->response_low = cpu_to_le32(pci64_dma_lo32(busaddr + sizeof(*req)));
+ req->response_high = cpu_to_le32(pci64_dma_hi32(busaddr + sizeof(*req)));
+ req->sub_len = cpu_to_le16(6);
+ req->data[0] = 0x00;
+ req->data[1] = 0x01;
+ req->data[8] = (u_char) (port_id & 0xff);
+ req->data[9] = (u_char) (port_id >> 8 & 0xff);
+ req->data[10] = (u_char) (port_id >> 16 & 0xff);
+
+ param[0] = MBOX_SEND_SNS;
+ param[1] = 14;
+ param[2] = pci64_dma_lo32(busaddr) >> 16;
+ param[3] = pci64_dma_lo32(busaddr);
+ param[6] = pci64_dma_hi32(busaddr) >> 16;
+ param[7] = pci64_dma_hi32(busaddr);
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] == MBOX_COMMAND_COMPLETE) {
+ DEBUG_FABRIC(printk("qlogicfc%d : found node %02x%02x%02x%02x%02x%02x%02x%02x ", hostdata->host_id, sns_response[20], sns_response[21], sns_response[22], sns_response[23], sns_response[24], sns_response[25], sns_response[26], sns_response[27]));
+ DEBUG_FABRIC(printk(" port id: %02x%02x%02x\n", sns_response[17], sns_response[18], sns_response[19]));
+ port_id = ((u_int) sns_response[17]) << 16;
+ port_id |= ((u_int) sns_response[18]) << 8;
+ port_id |= ((u_int) sns_response[19]);
+ wwn = ((u64) sns_response[20]) << 56;
+ wwn |= ((u64) sns_response[21]) << 48;
+ wwn |= ((u64) sns_response[22]) << 40;
+ wwn |= ((u64) sns_response[23]) << 32;
+ wwn |= ((u64) sns_response[24]) << 24;
+ wwn |= ((u64) sns_response[25]) << 16;
+ wwn |= ((u64) sns_response[26]) << 8;
+ wwn |= ((u64) sns_response[27]);
+ if (hostdata->port_id >> 8 != port_id >> 8) {
+ DEBUG_FABRIC(printk("qlogicfc%d : adding a fabric port: %x\n", hostdata->host_id, port_id));
+ param[0] = MBOX_PORT_LOGIN;
+ param[1] = loop_id << 8;
+ param[2] = (u_short) (port_id >> 16);
+ param[3] = (u_short) (port_id);
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] == MBOX_COMMAND_COMPLETE) {
+ port_db[scsi_id].wwn = wwn;
+ port_db[scsi_id].loop_id = loop_id;
+ loop_id++;
+ scsi_id++;
+ } else {
+ printk("qlogicfc%d : Error performing port login %x\n", hostdata->host_id, param[0]);
+ DEBUG_FABRIC(printk("qlogicfc%d : loop_id: %x\n", hostdata->host_id, loop_id));
+ param[0] = MBOX_PORT_LOGOUT;
+ param[1] = loop_id << 8;
+ param[2] = 0;
+ param[3] = 0;
+
+ isp2x00_mbox_command(host, param);
+
+ }
+
+ }
+ if (hostdata->port_id == port_id)
+ done = 1;
+ } else {
+ printk("qlogicfc%d : Get All Next failed %x.\n", hostdata->host_id, param[0]);
+ pci_free_consistent(hostdata->pci_dev, sizeof(*req) + 608, req, busaddr);
+ return 0;
+ }
+ }
+
+ pci_free_consistent(hostdata->pci_dev, sizeof(*req) + 608, req, busaddr);
+ return 1;
+}
+
+#endif /* ISP2x00_FABRIC */
+
+
+static int isp2x00_release(struct Scsi_Host *host)
+{
+ struct isp2x00_hostdata *hostdata;
+ dma_addr_t busaddr;
+
+ ENTER("isp2x00_release");
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ outw(0x0, host->io_port + PCI_INTER_CTL);
+ free_irq(host->irq, host);
+
+ release_region(host->io_port, 0xff);
+
+ busaddr = pci64_dma_build(le32_to_cpu(hostdata->control_block.res_queue_addr_high),
+ le32_to_cpu(hostdata->control_block.res_queue_addr_lo));
+ pci_free_consistent(hostdata->pci_dev, RES_SIZE + REQ_SIZE, hostdata->res, busaddr);
+
+ LEAVE("isp2x00_release");
+
+ return 0;
+}
+
+
+static const char *isp2x00_info(struct Scsi_Host *host)
+{
+ static char buf[80];
+ struct isp2x00_hostdata *hostdata;
+ ENTER("isp2x00_info");
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+ sprintf(buf,
+ "QLogic ISP%04x SCSI on PCI bus %02x device %02x irq %d base 0x%lx",
+ hostdata->pci_dev->device, hostdata->pci_dev->bus->number, hostdata->pci_dev->devfn, host->irq,
+ host->io_port);
+
+
+ LEAVE("isp2x00_info");
+
+ return buf;
+}
+
+
+/*
+ * The middle SCSI layer ensures that queuecommand never gets invoked
+ * concurrently with itself or the interrupt handler (though the
+ * interrupt handler may call this routine as part of
+ * request-completion handling).
+ */
+static int isp2x00_queuecommand(Scsi_Cmnd * Cmnd, void (*done) (Scsi_Cmnd *))
+{
+ int i, sg_count, n, num_free;
+ u_int in_ptr, out_ptr;
+ struct dataseg *ds;
+ struct scatterlist *sg;
+ struct Command_Entry *cmd;
+ struct Continuation_Entry *cont;
+ struct Scsi_Host *host;
+ struct isp2x00_hostdata *hostdata;
+
+ ENTER("isp2x00_queuecommand");
+
+ host = Cmnd->device->host;
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+ Cmnd->scsi_done = done;
+
+ DEBUG(isp2x00_print_scsi_cmd(Cmnd));
+
+ if (hostdata->adapter_state & AS_REDO_FABRIC_PORTDB || hostdata->adapter_state & AS_REDO_LOOP_PORTDB) {
+ isp2x00_make_portdb(host);
+ hostdata->adapter_state = AS_LOOP_GOOD;
+ printk("qlogicfc%d : Port Database\n", hostdata->host_id);
+ for (i = 0; hostdata->port_db[i].wwn != 0; i++) {
+ printk("wwn: %08x%08x scsi_id: %x loop_id: ", (u_int) (hostdata->port_db[i].wwn >> 32), (u_int) hostdata->port_db[i].wwn, i);
+ if (hostdata->port_db[i].loop_id != hostdata->port_db[0].loop_id || i == 0)
+ printk("%x", hostdata->port_db[i].loop_id);
+ else
+ printk("Not Available");
+ printk("\n");
+ }
+ }
+ if (hostdata->adapter_state == AS_FIRMWARE_DEAD) {
+ printk("qlogicfc%d : The firmware is dead, just return.\n", hostdata->host_id);
+ host->max_id = 0;
+ return 0;
+ }
+
+ out_ptr = inw(host->io_port + MBOX4);
+ in_ptr = hostdata->req_in_ptr;
+
+ DEBUG(printk("qlogicfc%d : request queue depth %d\n", hostdata->host_id,
+ REQ_QUEUE_DEPTH(in_ptr, out_ptr)));
+
+ cmd = (struct Command_Entry *) &hostdata->req[in_ptr*QUEUE_ENTRY_LEN];
+ in_ptr = (in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN;
+ if (in_ptr == out_ptr) {
+ DEBUG(printk("qlogicfc%d : request queue overflow\n", hostdata->host_id));
+ return 1;
+ }
+ if (hostdata->send_marker) {
+ struct Marker_Entry *marker;
+
+ TRACE("queue marker", in_ptr, 0);
+
+ DEBUG(printk("qlogicfc%d : adding marker entry\n", hostdata->host_id));
+ marker = (struct Marker_Entry *) cmd;
+ memset(marker, 0, sizeof(struct Marker_Entry));
+
+ marker->hdr.entry_type = ENTRY_MARKER;
+ marker->hdr.entry_cnt = 1;
+ marker->modifier = SYNC_ALL;
+
+ hostdata->send_marker = 0;
+
+ if (((in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN) == out_ptr) {
+ outw(in_ptr, host->io_port + MBOX4);
+ hostdata->req_in_ptr = in_ptr;
+ DEBUG(printk("qlogicfc%d : request queue overflow\n", hostdata->host_id));
+ return 1;
+ }
+ cmd = (struct Command_Entry *) &hostdata->req[in_ptr*QUEUE_ENTRY_LEN];
+ in_ptr = (in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN;
+ }
+ TRACE("queue command", in_ptr, Cmnd);
+
+ memset(cmd, 0, sizeof(struct Command_Entry));
+
+ /* find a free handle mapping slot */
+ for (i = in_ptr; i != (in_ptr - 1) && hostdata->handle_ptrs[i]; i = ((i + 1) % (QLOGICFC_REQ_QUEUE_LEN + 1)));
+
+ if (!hostdata->handle_ptrs[i]) {
+ cmd->handle = cpu_to_le32(i);
+ hostdata->handle_ptrs[i] = Cmnd;
+ hostdata->handle_serials[i] = Cmnd->serial_number;
+ } else {
+ printk("qlogicfc%d : no handle slots, this should not happen.\n", hostdata->host_id);
+ printk("hostdata->queued is %x, in_ptr: %x\n", hostdata->queued, in_ptr);
+ for (i = 0; i <= QLOGICFC_REQ_QUEUE_LEN; i++){
+ if (!hostdata->handle_ptrs[i]){
+ printk("slot %d has %p\n", i, hostdata->handle_ptrs[i]);
+ }
+ }
+ return 1;
+ }
+
+ cmd->hdr.entry_type = ENTRY_COMMAND;
+ cmd->hdr.entry_cnt = 1;
+ cmd->target_lun = Cmnd->device->lun;
+ cmd->expanded_lun = cpu_to_le16(Cmnd->device->lun);
+#if ISP2x00_PORTDB
+ cmd->target_id = hostdata->port_db[Cmnd->device->id].loop_id;
+#else
+ cmd->target_id = Cmnd->target;
+#endif
+ cmd->total_byte_cnt = cpu_to_le32(Cmnd->request_bufflen);
+ cmd->time_out = 0;
+ memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len);
+
+ if (Cmnd->use_sg) {
+ sg = (struct scatterlist *) Cmnd->request_buffer;
+ sg_count = pci_map_sg(hostdata->pci_dev, sg, Cmnd->use_sg, scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ cmd->segment_cnt = cpu_to_le16(sg_count);
+ ds = cmd->dataseg;
+ /* fill in first two sg entries: */
+ n = sg_count;
+ if (n > DATASEGS_PER_COMMAND)
+ n = DATASEGS_PER_COMMAND;
+
+ for (i = 0; i < n; i++) {
+ ds[i].d_base = cpu_to_le32(pci64_dma_lo32(sg_dma_address(sg)));
+ ds[i].d_base_hi = cpu_to_le32(pci64_dma_hi32(sg_dma_address(sg)));
+ ds[i].d_count = cpu_to_le32(sg_dma_len(sg));
+ ++sg;
+ }
+ sg_count -= DATASEGS_PER_COMMAND;
+
+ while (sg_count > 0) {
+ ++cmd->hdr.entry_cnt;
+ cont = (struct Continuation_Entry *)
+ &hostdata->req[in_ptr*QUEUE_ENTRY_LEN];
+ memset(cont, 0, sizeof(struct Continuation_Entry));
+ in_ptr = (in_ptr + 1) & QLOGICFC_REQ_QUEUE_LEN;
+ if (in_ptr == out_ptr) {
+ DEBUG(printk("qlogicfc%d : unexpected request queue overflow\n", hostdata->host_id));
+ return 1;
+ }
+ TRACE("queue continuation", in_ptr, 0);
+ cont->hdr.entry_type = ENTRY_CONTINUATION;
+ ds = cont->dataseg;
+ n = sg_count;
+ if (n > DATASEGS_PER_CONT)
+ n = DATASEGS_PER_CONT;
+ for (i = 0; i < n; ++i) {
+ ds[i].d_base = cpu_to_le32(pci64_dma_lo32(sg_dma_address(sg)));
+ ds[i].d_base_hi = cpu_to_le32(pci64_dma_hi32(sg_dma_address(sg)));
+ ds[i].d_count = cpu_to_le32(sg_dma_len(sg));
+ ++sg;
+ }
+ sg_count -= n;
+ }
+ } else if (Cmnd->request_bufflen && Cmnd->sc_data_direction != PCI_DMA_NONE) {
+ struct page *page = virt_to_page(Cmnd->request_buffer);
+ unsigned long offset = offset_in_page(Cmnd->request_buffer);
+ dma_addr_t busaddr = pci_map_page(hostdata->pci_dev,
+ page, offset,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ Cmnd->SCp.dma_handle = busaddr;
+
+ cmd->dataseg[0].d_base = cpu_to_le32(pci64_dma_lo32(busaddr));
+ cmd->dataseg[0].d_base_hi = cpu_to_le32(pci64_dma_hi32(busaddr));
+ cmd->dataseg[0].d_count = cpu_to_le32(Cmnd->request_bufflen);
+ cmd->segment_cnt = cpu_to_le16(1);
+ } else {
+ cmd->dataseg[0].d_base = 0;
+ cmd->dataseg[0].d_base_hi = 0;
+ cmd->segment_cnt = cpu_to_le16(1); /* Shouldn't this be 0? */
+ }
+
+ if (Cmnd->sc_data_direction == SCSI_DATA_WRITE)
+ cmd->control_flags = cpu_to_le16(CFLAG_WRITE);
+ else
+ cmd->control_flags = cpu_to_le16(CFLAG_READ);
+
+ if (Cmnd->device->tagged_supported) {
+ if ((jiffies - hostdata->tag_ages[Cmnd->device->id]) > (2 * ISP_TIMEOUT)) {
+ cmd->control_flags |= cpu_to_le16(CFLAG_ORDERED_TAG);
+ hostdata->tag_ages[Cmnd->device->id] = jiffies;
+ } else
+ switch (Cmnd->tag) {
+ case HEAD_OF_QUEUE_TAG:
+ cmd->control_flags |= cpu_to_le16(CFLAG_HEAD_TAG);
+ break;
+ case ORDERED_QUEUE_TAG:
+ cmd->control_flags |= cpu_to_le16(CFLAG_ORDERED_TAG);
+ break;
+ default:
+ cmd->control_flags |= cpu_to_le16(CFLAG_SIMPLE_TAG);
+ break;
+ }
+ }
+ /*
+ * TEST_UNIT_READY commands from scsi_scan will fail due to "overlapped
+ * commands attempted" unless we setup at least a simple queue (midlayer
+ * will embelish this once it can do an INQUIRY command to the device)
+ */
+ else
+ cmd->control_flags |= cpu_to_le16(CFLAG_SIMPLE_TAG);
+ outw(in_ptr, host->io_port + MBOX4);
+ hostdata->req_in_ptr = in_ptr;
+
+ hostdata->queued++;
+
+ num_free = QLOGICFC_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr);
+ num_free = (num_free > 2) ? num_free - 2 : 0;
+ host->can_queue = host->host_busy + num_free;
+ if (host->can_queue > QLOGICFC_REQ_QUEUE_LEN)
+ host->can_queue = QLOGICFC_REQ_QUEUE_LEN;
+ host->sg_tablesize = QLOGICFC_MAX_SG(num_free);
+
+ LEAVE("isp2x00_queuecommand");
+
+ return 0;
+}
+
+
+/* we have received an event, such as a lip or an RSCN, which may mean that
+ * our port database is incorrect so the port database must be recreated.
+ */
+static void redo_port_db(unsigned long arg)
+{
+
+ struct Scsi_Host * host = (struct Scsi_Host *) arg;
+ struct isp2x00_hostdata * hostdata;
+ unsigned long flags;
+ int i;
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+ hostdata->explore_timer.data = 0;
+ del_timer(&hostdata->explore_timer);
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ if (hostdata->adapter_state & AS_REDO_FABRIC_PORTDB || hostdata->adapter_state & AS_REDO_LOOP_PORTDB) {
+ isp2x00_make_portdb(host);
+ printk("qlogicfc%d : Port Database\n", hostdata->host_id);
+ for (i = 0; hostdata->port_db[i].wwn != 0; i++) {
+ printk("wwn: %08x%08x scsi_id: %x loop_id: ", (u_int) (hostdata->port_db[i].wwn >> 32), (u_int) hostdata->port_db[i].wwn, i);
+ if (hostdata->port_db[i].loop_id != hostdata->port_db[0].loop_id || i == 0)
+ printk("%x", hostdata->port_db[i].loop_id);
+ else
+ printk("Not Available");
+ printk("\n");
+ }
+
+ for (i = 0; i < QLOGICFC_REQ_QUEUE_LEN; i++){
+ if (hostdata->handle_ptrs[i] && (hostdata->port_db[hostdata->handle_ptrs[i]->device->id].loop_id > QLOGICFC_MAX_LOOP_ID || hostdata->adapter_state & AS_REDO_LOOP_PORTDB)){
+ if (hostdata->port_db[hostdata->handle_ptrs[i]->device->id].loop_id != hostdata->port_db[0].loop_id){
+ Scsi_Cmnd *Cmnd = hostdata->handle_ptrs[i];
+
+ if (Cmnd->use_sg)
+ pci_unmap_sg(hostdata->pci_dev,
+ (struct scatterlist *)Cmnd->buffer,
+ Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ else if (Cmnd->request_bufflen &&
+ Cmnd->sc_data_direction != PCI_DMA_NONE) {
+ pci_unmap_page(hostdata->pci_dev,
+ Cmnd->SCp.dma_handle,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ }
+
+ hostdata->handle_ptrs[i]->result = DID_SOFT_ERROR << 16;
+
+ if (hostdata->handle_ptrs[i]->scsi_done){
+ (*hostdata->handle_ptrs[i]->scsi_done) (hostdata->handle_ptrs[i]);
+ }
+ else printk("qlogicfc%d : done is null?\n", hostdata->host_id);
+ hostdata->handle_ptrs[i] = NULL;
+ hostdata->handle_serials[i] = 0;
+ }
+ }
+ }
+
+ hostdata->adapter_state = AS_LOOP_GOOD;
+ }
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+}
+
+#define ASYNC_EVENT_INTERRUPT 0x01
+
+irqreturn_t do_isp2x00_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *host = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ isp2x00_intr_handler(irq, dev_id, regs);
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+void isp2x00_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ Scsi_Cmnd *Cmnd;
+ struct Status_Entry *sts;
+ struct Scsi_Host *host = dev_id;
+ struct isp2x00_hostdata *hostdata;
+ u_int in_ptr, out_ptr, handle, num_free;
+ u_short status;
+
+ ENTER_INTR("isp2x00_intr_handler");
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ DEBUG_INTR(printk("qlogicfc%d : interrupt on line %d\n", hostdata->host_id, irq));
+
+ if (!(inw(host->io_port + PCI_INTER_STS) & 0x08)) {
+ /* spurious interrupts can happen legally */
+ DEBUG_INTR(printk("qlogicfc%d : got spurious interrupt\n", hostdata->host_id));
+ return;
+ }
+ in_ptr = inw(host->io_port + MBOX5);
+ out_ptr = hostdata->res_out_ptr;
+
+ if ((inw(host->io_port + PCI_SEMAPHORE) & ASYNC_EVENT_INTERRUPT)) {
+ status = inw(host->io_port + MBOX0);
+
+ DEBUG_INTR(printk("qlogicfc%d : mbox completion status: %x\n",
+ hostdata->host_id, status));
+
+ switch (status) {
+ case LOOP_UP:
+ case POINT_TO_POINT_UP:
+ printk("qlogicfc%d : Link is Up\n", hostdata->host_id);
+ hostdata->adapter_state = AS_REDO_FABRIC_PORTDB | AS_REDO_LOOP_PORTDB;
+ break;
+ case LOOP_DOWN:
+ printk("qlogicfc%d : Link is Down\n", hostdata->host_id);
+ hostdata->adapter_state = AS_LOOP_DOWN;
+ break;
+ case CONNECTION_MODE:
+ printk("received CONNECTION_MODE irq %x\n", inw(host->io_port + MBOX1));
+ break;
+ case CHANGE_NOTIFICATION:
+ printk("qlogicfc%d : RSCN Received\n", hostdata->host_id);
+ if (hostdata->adapter_state == AS_LOOP_GOOD)
+ hostdata->adapter_state = AS_REDO_FABRIC_PORTDB;
+ break;
+ case LIP_OCCURRED:
+ case LIP_RECEIVED:
+ printk("qlogicfc%d : Loop Reinitialized\n", hostdata->host_id);
+ if (hostdata->adapter_state == AS_LOOP_GOOD)
+ hostdata->adapter_state = AS_REDO_LOOP_PORTDB;
+ break;
+ case SYSTEM_ERROR:
+ printk("qlogicfc%d : The firmware just choked.\n", hostdata->host_id);
+ hostdata->adapter_state = AS_FIRMWARE_DEAD;
+ break;
+ case SCSI_COMMAND_COMPLETE:
+ handle = inw(host->io_port + MBOX1) | (inw(host->io_port + MBOX2) << 16);
+ Cmnd = hostdata->handle_ptrs[handle];
+ hostdata->handle_ptrs[handle] = NULL;
+ hostdata->handle_serials[handle] = 0;
+ hostdata->queued--;
+ if (Cmnd != NULL) {
+ if (Cmnd->use_sg)
+ pci_unmap_sg(hostdata->pci_dev,
+ (struct scatterlist *)Cmnd->buffer,
+ Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ else if (Cmnd->request_bufflen &&
+ Cmnd->sc_data_direction != PCI_DMA_NONE)
+ pci_unmap_page(hostdata->pci_dev,
+ Cmnd->SCp.dma_handle,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ Cmnd->result = 0x0;
+ (*Cmnd->scsi_done) (Cmnd);
+ } else
+ printk("qlogicfc%d.c : got a null value out of handle_ptrs, this sucks\n", hostdata->host_id);
+ break;
+ case MBOX_COMMAND_COMPLETE:
+ case INVALID_COMMAND:
+ case HOST_INTERFACE_ERROR:
+ case TEST_FAILED:
+ case COMMAND_ERROR:
+ case COMMAND_PARAM_ERROR:
+ case PORT_ID_USED:
+ case LOOP_ID_USED:
+ case ALL_IDS_USED:
+ hostdata->mbox_done = 1;
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+ return;
+ default:
+ printk("qlogicfc%d : got an unknown status? %x\n", hostdata->host_id, status);
+ }
+ if ((hostdata->adapter_state & AS_REDO_LOOP_PORTDB || hostdata->adapter_state & AS_REDO_FABRIC_PORTDB) && hostdata->explore_timer.data == 0){
+ hostdata->explore_timer.function = redo_port_db;
+ hostdata->explore_timer.data = (unsigned long)host;
+ hostdata->explore_timer.expires = jiffies + (HZ/4);
+ init_timer(&hostdata->explore_timer);
+ add_timer(&hostdata->explore_timer);
+ }
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+ } else {
+ DEBUG_INTR(printk("qlogicfc%d : response queue update\n", hostdata->host_id));
+ DEBUG_INTR(printk("qlogicfc%d : response queue depth %d\n", hostdata->host_id, RES_QUEUE_DEPTH(in_ptr, out_ptr)));
+
+ while (out_ptr != in_ptr) {
+ unsigned le_hand;
+ sts = (struct Status_Entry *) &hostdata->res[out_ptr*QUEUE_ENTRY_LEN];
+ out_ptr = (out_ptr + 1) & RES_QUEUE_LEN;
+
+ TRACE("done", out_ptr, Cmnd);
+ DEBUG_INTR(isp2x00_print_status_entry(sts));
+ le_hand = le32_to_cpu(sts->handle);
+ if (sts->hdr.entry_type == ENTRY_STATUS && (Cmnd = hostdata->handle_ptrs[le_hand])) {
+ Cmnd->result = isp2x00_return_status(Cmnd, sts);
+ hostdata->queued--;
+
+ if (Cmnd->use_sg)
+ pci_unmap_sg(hostdata->pci_dev,
+ (struct scatterlist *)Cmnd->buffer, Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ else if (Cmnd->request_bufflen && Cmnd->sc_data_direction != PCI_DMA_NONE)
+ pci_unmap_page(hostdata->pci_dev,
+ Cmnd->SCp.dma_handle,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+
+ /*
+ * if any of the following are true we do not
+ * call scsi_done. if the status is CS_ABORTED
+ * we don't have to call done because the upper
+ * level should already know its aborted.
+ */
+ if (hostdata->handle_serials[le_hand] != Cmnd->serial_number
+ || le16_to_cpu(sts->completion_status) == CS_ABORTED){
+ hostdata->handle_serials[le_hand] = 0;
+ hostdata->handle_ptrs[le_hand] = NULL;
+ outw(out_ptr, host->io_port + MBOX5);
+ continue;
+ }
+ /*
+ * if we get back an error indicating the port
+ * is not there or if the link is down and
+ * this is a device that used to be there
+ * allow the command to timeout.
+ * the device may well be back in a couple of
+ * seconds.
+ */
+ if ((hostdata->adapter_state == AS_LOOP_DOWN || sts->completion_status == cpu_to_le16(CS_PORT_UNAVAILABLE) || sts->completion_status == cpu_to_le16(CS_PORT_LOGGED_OUT) || sts->completion_status == cpu_to_le16(CS_PORT_CONFIG_CHANGED)) && hostdata->port_db[Cmnd->device->id].wwn){
+ outw(out_ptr, host->io_port + MBOX5);
+ continue;
+ }
+ } else {
+ outw(out_ptr, host->io_port + MBOX5);
+ continue;
+ }
+
+ hostdata->handle_ptrs[le_hand] = NULL;
+
+ if (sts->completion_status == cpu_to_le16(CS_RESET_OCCURRED)
+ || (sts->status_flags & cpu_to_le16(STF_BUS_RESET)))
+ hostdata->send_marker = 1;
+
+ if (le16_to_cpu(sts->scsi_status) & 0x0200)
+ memcpy(Cmnd->sense_buffer, sts->req_sense_data,
+ sizeof(Cmnd->sense_buffer));
+
+ outw(out_ptr, host->io_port + MBOX5);
+
+ if (Cmnd->scsi_done != NULL) {
+ (*Cmnd->scsi_done) (Cmnd);
+ } else
+ printk("qlogicfc%d : Ouch, scsi done is NULL\n", hostdata->host_id);
+ }
+ hostdata->res_out_ptr = out_ptr;
+ }
+
+
+ out_ptr = inw(host->io_port + MBOX4);
+ in_ptr = hostdata->req_in_ptr;
+
+ num_free = QLOGICFC_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr);
+ num_free = (num_free > 2) ? num_free - 2 : 0;
+ host->can_queue = host->host_busy + num_free;
+ if (host->can_queue > QLOGICFC_REQ_QUEUE_LEN)
+ host->can_queue = QLOGICFC_REQ_QUEUE_LEN;
+ host->sg_tablesize = QLOGICFC_MAX_SG(num_free);
+
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+ LEAVE_INTR("isp2x00_intr_handler");
+}
+
+
+static int isp2x00_return_status(Scsi_Cmnd *Cmnd, struct Status_Entry *sts)
+{
+ int host_status = DID_ERROR;
+#if DEBUG_ISP2x00_INTR
+ static char *reason[] =
+ {
+ "DID_OK",
+ "DID_NO_CONNECT",
+ "DID_BUS_BUSY",
+ "DID_TIME_OUT",
+ "DID_BAD_TARGET",
+ "DID_ABORT",
+ "DID_PARITY",
+ "DID_ERROR",
+ "DID_RESET",
+ "DID_BAD_INTR"
+ };
+#endif /* DEBUG_ISP2x00_INTR */
+
+ ENTER("isp2x00_return_status");
+
+ DEBUG(printk("qlogicfc : completion status = 0x%04x\n",
+ le16_to_cpu(sts->completion_status)));
+
+ switch (le16_to_cpu(sts->completion_status)) {
+ case CS_COMPLETE:
+ host_status = DID_OK;
+ break;
+ case CS_DMA_ERROR:
+ host_status = DID_ERROR;
+ break;
+ case CS_RESET_OCCURRED:
+ host_status = DID_RESET;
+ break;
+ case CS_ABORTED:
+ host_status = DID_ABORT;
+ break;
+ case CS_TIMEOUT:
+ host_status = DID_TIME_OUT;
+ break;
+ case CS_DATA_OVERRUN:
+ host_status = DID_ERROR;
+ break;
+ case CS_DATA_UNDERRUN:
+ if (Cmnd->underflow <= (Cmnd->request_bufflen - le32_to_cpu(sts->residual)))
+ host_status = DID_OK;
+ else
+ host_status = DID_ERROR;
+ break;
+ case CS_PORT_UNAVAILABLE:
+ case CS_PORT_LOGGED_OUT:
+ case CS_PORT_CONFIG_CHANGED:
+ host_status = DID_BAD_TARGET;
+ break;
+ case CS_QUEUE_FULL:
+ host_status = DID_ERROR;
+ break;
+ default:
+ printk("qlogicfc : unknown completion status 0x%04x\n",
+ le16_to_cpu(sts->completion_status));
+ host_status = DID_ERROR;
+ break;
+ }
+
+ DEBUG_INTR(printk("qlogicfc : host status (%s) scsi status %x\n",
+ reason[host_status], le16_to_cpu(sts->scsi_status)));
+
+ LEAVE("isp2x00_return_status");
+
+ return (le16_to_cpu(sts->scsi_status) & STATUS_MASK) | (host_status << 16);
+}
+
+
+static int isp2x00_abort(Scsi_Cmnd * Cmnd)
+{
+ u_short param[8];
+ int i;
+ struct Scsi_Host *host;
+ struct isp2x00_hostdata *hostdata;
+ int return_status = SUCCESS;
+
+ ENTER("isp2x00_abort");
+
+ host = Cmnd->device->host;
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ for (i = 0; i < QLOGICFC_REQ_QUEUE_LEN; i++)
+ if (hostdata->handle_ptrs[i] == Cmnd)
+ break;
+
+ if (i == QLOGICFC_REQ_QUEUE_LEN){
+ return SUCCESS;
+ }
+
+ isp2x00_disable_irqs(host);
+
+ param[0] = MBOX_ABORT_IOCB;
+#if ISP2x00_PORTDB
+ param[1] = (((u_short) hostdata->port_db[Cmnd->device->id].loop_id) << 8) | Cmnd->device->lun;
+#else
+ param[1] = (((u_short) Cmnd->target) << 8) | Cmnd->lun;
+#endif
+ param[2] = i & 0xffff;
+ param[3] = i >> 16;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicfc%d : scsi abort failure: %x\n", hostdata->host_id, param[0]);
+ if (param[0] == 0x4005)
+ Cmnd->result = DID_ERROR << 16;
+ if (param[0] == 0x4006)
+ Cmnd->result = DID_BAD_TARGET << 16;
+ return_status = FAILED;
+ }
+
+ if (return_status != SUCCESS){
+ param[0] = MBOX_GET_FIRMWARE_STATE;
+ isp2x00_mbox_command(host, param);
+ printk("qlogicfc%d : abort failed\n", hostdata->host_id);
+ printk("qlogicfc%d : firmware status is %x %x\n", hostdata->host_id, param[0], param[1]);
+ }
+
+ isp2x00_enable_irqs(host);
+
+ LEAVE("isp2x00_abort");
+
+ return return_status;
+}
+
+
+static int isp2x00_biosparam(struct scsi_device *sdev, struct block_device *n,
+ sector_t capacity, int ip[])
+{
+ int size = capacity;
+
+ ENTER("isp2x00_biosparam");
+
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ if (ip[2] > 1024) {
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / (ip[0] * ip[1]);
+ }
+ LEAVE("isp2x00_biosparam");
+
+ return 0;
+}
+
+static int isp2x00_reset_hardware(struct Scsi_Host *host)
+{
+ u_short param[8];
+ struct isp2x00_hostdata *hostdata;
+ int loop_count;
+ dma_addr_t busaddr;
+
+ ENTER("isp2x00_reset_hardware");
+
+ hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ /*
+ * This cannot be right - PCI writes are posted
+ * (apparently this is hardware design flaw not software ?)
+ */
+
+ outw(0x01, host->io_port + ISP_CTRL_STATUS);
+ udelay(100);
+ outw(HCCR_RESET, host->io_port + HOST_HCCR);
+ udelay(100);
+ outw(HCCR_RELEASE, host->io_port + HOST_HCCR);
+ outw(HCCR_BIOS_DISABLE, host->io_port + HOST_HCCR);
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && inw(host->io_port + HOST_HCCR) == RISC_BUSY) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk("qlogicfc%d : reset_hardware loop timeout\n", hostdata->host_id);
+
+
+
+#if DEBUG_ISP2x00
+ printk("qlogicfc%d : mbox 0 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX0));
+ printk("qlogicfc%d : mbox 1 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX1));
+ printk("qlogicfc%d : mbox 2 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX2));
+ printk("qlogicfc%d : mbox 3 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX3));
+ printk("qlogicfc%d : mbox 4 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX4));
+ printk("qlogicfc%d : mbox 5 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX5));
+ printk("qlogicfc%d : mbox 6 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX6));
+ printk("qlogicfc%d : mbox 7 0x%04x \n", hostdata->host_id, inw(host->io_port + MBOX7));
+#endif /* DEBUG_ISP2x00 */
+
+ DEBUG(printk("qlogicfc%d : verifying checksum\n", hostdata->host_id));
+
+#if defined(CONFIG_SCSI_QLOGIC_FC_FIRMWARE)
+ {
+ int i;
+ unsigned short * risc_code = NULL;
+ unsigned short risc_code_len = 0;
+ if (hostdata->pci_dev->device == PCI_DEVICE_ID_QLOGIC_ISP2100){
+ risc_code = risc_code2100;
+ risc_code_len = risc_code_length2100;
+ }
+ else if (hostdata->pci_dev->device == PCI_DEVICE_ID_QLOGIC_ISP2200){
+ risc_code = risc_code2200;
+ risc_code_len = risc_code_length2200;
+ }
+
+ for (i = 0; i < risc_code_len; i++) {
+ param[0] = MBOX_WRITE_RAM_WORD;
+ param[1] = risc_code_addr01 + i;
+ param[2] = risc_code[i];
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicfc%d : firmware load failure\n", hostdata->host_id);
+ return 1;
+ }
+ }
+ }
+#endif /* RELOAD_FIRMWARE */
+
+ param[0] = MBOX_VERIFY_CHECKSUM;
+ param[1] = risc_code_addr01;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicfc%d : ram checksum failure\n", hostdata->host_id);
+ return 1;
+ }
+ DEBUG(printk("qlogicfc%d : executing firmware\n", hostdata->host_id));
+
+ param[0] = MBOX_EXEC_FIRMWARE;
+ param[1] = risc_code_addr01;
+
+ isp2x00_mbox_command(host, param);
+
+ param[0] = MBOX_ABOUT_FIRMWARE;
+
+ isp2x00_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicfc%d : about firmware failure\n", hostdata->host_id);
+ return 1;
+ }
+ DEBUG(printk("qlogicfc%d : firmware major revision %d\n", hostdata->host_id, param[1]));
+ DEBUG(printk("qlogicfc%d : firmware minor revision %d\n", hostdata->host_id, param[2]));
+
+#ifdef USE_NVRAM_DEFAULTS
+
+ if (isp2x00_get_nvram_defaults(host, &hostdata->control_block) != 0) {
+ printk("qlogicfc%d : Could not read from NVRAM\n", hostdata->host_id);
+ }
+#endif
+
+ hostdata->wwn = (u64) (cpu_to_le16(hostdata->control_block.node_name[0])) << 56;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[0]) & 0xff00) << 48;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[1]) & 0xff00) << 24;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[1]) & 0x00ff) << 48;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[2]) & 0x00ff) << 24;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[2]) & 0xff00) << 8;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[3]) & 0x00ff) << 8;
+ hostdata->wwn |= (u64) (cpu_to_le16(hostdata->control_block.node_name[3]) & 0xff00) >> 8;
+
+ /* FIXME: If the DMA transfer goes one way only, this should use
+ * PCI_DMA_TODEVICE and below as well.
+ */
+ busaddr = pci_map_page(hostdata->pci_dev,
+ virt_to_page(&hostdata->control_block),
+ offset_in_page(&hostdata->control_block),
+ sizeof(hostdata->control_block),
+ PCI_DMA_BIDIRECTIONAL);
+
+ param[0] = MBOX_INIT_FIRMWARE;
+ param[2] = (u_short) (pci64_dma_lo32(busaddr) >> 16);
+ param[3] = (u_short) (pci64_dma_lo32(busaddr) & 0xffff);
+ param[4] = 0;
+ param[5] = 0;
+ param[6] = (u_short) (pci64_dma_hi32(busaddr) >> 16);
+ param[7] = (u_short) (pci64_dma_hi32(busaddr) & 0xffff);
+ isp2x00_mbox_command(host, param);
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicfc%d.c: Ouch 0x%04x\n", hostdata->host_id, param[0]);
+ pci_unmap_page(hostdata->pci_dev, busaddr,
+ sizeof(hostdata->control_block),
+ PCI_DMA_BIDIRECTIONAL);
+ return 1;
+ }
+ param[0] = MBOX_GET_FIRMWARE_STATE;
+ isp2x00_mbox_command(host, param);
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicfc%d.c: 0x%04x\n", hostdata->host_id, param[0]);
+ pci_unmap_page(hostdata->pci_dev, busaddr,
+ sizeof(hostdata->control_block),
+ PCI_DMA_BIDIRECTIONAL);
+ return 1;
+ }
+
+ pci_unmap_page(hostdata->pci_dev, busaddr,
+ sizeof(hostdata->control_block),
+ PCI_DMA_BIDIRECTIONAL);
+ LEAVE("isp2x00_reset_hardware");
+
+ return 0;
+}
+
+#ifdef USE_NVRAM_DEFAULTS
+
+static int isp2x00_get_nvram_defaults(struct Scsi_Host *host, struct init_cb *control_block)
+{
+
+ u_short value;
+ if (isp2x00_read_nvram_word(host, 0) != 0x5349)
+ return 1;
+
+ value = isp2x00_read_nvram_word(host, 8);
+ control_block->node_name[0] = cpu_to_le16(isp2x00_read_nvram_word(host, 9));
+ control_block->node_name[1] = cpu_to_le16(isp2x00_read_nvram_word(host, 10));
+ control_block->node_name[2] = cpu_to_le16(isp2x00_read_nvram_word(host, 11));
+ control_block->node_name[3] = cpu_to_le16(isp2x00_read_nvram_word(host, 12));
+ control_block->hard_addr = cpu_to_le16(isp2x00_read_nvram_word(host, 13));
+
+ return 0;
+
+}
+
+#endif
+
+static int isp2x00_init(struct Scsi_Host *sh)
+{
+ u_long io_base;
+ struct isp2x00_hostdata *hostdata;
+ u_char revision;
+ u_int irq;
+ u_short command;
+ struct pci_dev *pdev;
+
+
+ ENTER("isp2x00_init");
+
+ hostdata = (struct isp2x00_hostdata *) sh->hostdata;
+ pdev = hostdata->pci_dev;
+
+ if (pci_read_config_word(pdev, PCI_COMMAND, &command)
+ || pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision)) {
+ printk("qlogicfc%d : error reading PCI configuration\n", hostdata->host_id);
+ return 1;
+ }
+ io_base = pci_resource_start(pdev, 0);
+ irq = pdev->irq;
+
+
+ if (pdev->vendor != PCI_VENDOR_ID_QLOGIC) {
+ printk("qlogicfc%d : 0x%04x is not QLogic vendor ID\n", hostdata->host_id,
+ pdev->vendor);
+ return 1;
+ }
+ if (pdev->device != PCI_DEVICE_ID_QLOGIC_ISP2100 && pdev->device != PCI_DEVICE_ID_QLOGIC_ISP2200) {
+ printk("qlogicfc%d : 0x%04x does not match ISP2100 or ISP2200 device id\n", hostdata->host_id,
+ pdev->device);
+ return 1;
+ }
+ if (!(command & PCI_COMMAND_IO) ||
+ !(pdev->resource[0].flags & IORESOURCE_IO)) {
+ printk("qlogicfc%d : i/o mapping is disabled\n", hostdata->host_id);
+ return 1;
+ }
+
+ pci_set_master(pdev);
+ if (revision != ISP2100_REV_ID1 && revision != ISP2100_REV_ID3 && revision != ISP2200_REV_ID5)
+ printk("qlogicfc%d : new isp2x00 revision ID (%d)\n", hostdata->host_id, revision);
+
+
+ hostdata->revision = revision;
+
+ sh->irq = irq;
+ sh->io_port = io_base;
+
+ LEAVE("isp2x00_init");
+
+ return 0;
+}
+
+#if USE_NVRAM_DEFAULTS
+
+#define NVRAM_DELAY() udelay(10) /* 10 microsecond delay */
+
+
+u_short isp2x00_read_nvram_word(struct Scsi_Host * host, u_short byte)
+{
+ int i;
+ u_short value, output, input;
+
+ outw(0x2, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ outw(0x3, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+
+ byte &= 0xff;
+ byte |= 0x0600;
+ for (i = 10; i >= 0; i--) {
+ output = ((byte >> i) & 0x1) ? 0x4 : 0x0;
+ outw(output | 0x2, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ outw(output | 0x3, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ outw(output | 0x2, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ }
+
+ for (i = 0xf, value = 0; i >= 0; i--) {
+ value <<= 1;
+ outw(0x3, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ input = inw(host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ outw(0x2, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+ if (input & 0x8)
+ value |= 1;
+ }
+
+ outw(0x0, host->io_port + PCI_NVRAM);
+ NVRAM_DELAY();
+
+ return value;
+}
+
+
+#endif /* USE_NVRAM_DEFAULTS */
+
+
+
+/*
+ * currently, this is only called during initialization or abort/reset,
+ * at which times interrupts are disabled, so polling is OK, I guess...
+ */
+static int isp2x00_mbox_command(struct Scsi_Host *host, u_short param[])
+{
+ int loop_count;
+ struct isp2x00_hostdata *hostdata = (struct isp2x00_hostdata *) host->hostdata;
+
+ if (mbox_param[param[0]] == 0 || hostdata->adapter_state == AS_FIRMWARE_DEAD)
+ return 1;
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && inw(host->io_port + HOST_HCCR) & 0x0080) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count) {
+ printk("qlogicfc%d : mbox_command loop timeout #1\n", hostdata->host_id);
+ param[0] = 0x4006;
+ hostdata->adapter_state = AS_FIRMWARE_DEAD;
+ return 1;
+ }
+ hostdata->mbox_done = 0;
+
+ if (mbox_param[param[0]] == 0)
+ printk("qlogicfc%d : invalid mbox command\n", hostdata->host_id);
+
+ if (mbox_param[param[0]] & 0x80)
+ outw(param[7], host->io_port + MBOX7);
+ if (mbox_param[param[0]] & 0x40)
+ outw(param[6], host->io_port + MBOX6);
+ if (mbox_param[param[0]] & 0x20)
+ outw(param[5], host->io_port + MBOX5);
+ if (mbox_param[param[0]] & 0x10)
+ outw(param[4], host->io_port + MBOX4);
+ if (mbox_param[param[0]] & 0x08)
+ outw(param[3], host->io_port + MBOX3);
+ if (mbox_param[param[0]] & 0x04)
+ outw(param[2], host->io_port + MBOX2);
+ if (mbox_param[param[0]] & 0x02)
+ outw(param[1], host->io_port + MBOX1);
+ if (mbox_param[param[0]] & 0x01)
+ outw(param[0], host->io_port + MBOX0);
+
+
+ outw(HCCR_SET_HOST_INTR, host->io_port + HOST_HCCR);
+
+ while (1) {
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && !(inw(host->io_port + PCI_INTER_STS) & 0x08)) {
+ barrier();
+ cpu_relax();
+ }
+
+ if (!loop_count) {
+ hostdata->adapter_state = AS_FIRMWARE_DEAD;
+ printk("qlogicfc%d : mbox_command loop timeout #2\n", hostdata->host_id);
+ break;
+ }
+ isp2x00_intr_handler(host->irq, host, NULL);
+
+ if (hostdata->mbox_done == 1)
+ break;
+
+ }
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && inw(host->io_port + MBOX0) == 0x04) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk("qlogicfc%d : mbox_command loop timeout #3\n", hostdata->host_id);
+
+ param[7] = inw(host->io_port + MBOX7);
+ param[6] = inw(host->io_port + MBOX6);
+ param[5] = inw(host->io_port + MBOX5);
+ param[4] = inw(host->io_port + MBOX4);
+ param[3] = inw(host->io_port + MBOX3);
+ param[2] = inw(host->io_port + MBOX2);
+ param[1] = inw(host->io_port + MBOX1);
+ param[0] = inw(host->io_port + MBOX0);
+
+
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+
+ if (inw(host->io_port + HOST_HCCR) & 0x0080) {
+ hostdata->adapter_state = AS_FIRMWARE_DEAD;
+ printk("qlogicfc%d : mbox op is still pending\n", hostdata->host_id);
+ }
+ return 0;
+}
+
+#if DEBUG_ISP2x00_INTR
+
+void isp2x00_print_status_entry(struct Status_Entry *status)
+{
+ printk("qlogicfc : entry count = 0x%02x, type = 0x%02x, flags = 0x%02x\n",
+ status->hdr.entry_cnt, status->hdr.entry_type, status->hdr.flags);
+ printk("qlogicfc : scsi status = 0x%04x, completion status = 0x%04x\n",
+ le16_to_cpu(status->scsi_status), le16_to_cpu(status->completion_status));
+ printk("qlogicfc : state flags = 0x%04x, status flags = 0x%04x\n",
+ le16_to_cpu(status->state_flags), le16_to_cpu(status->status_flags));
+ printk("qlogicfc : response info length = 0x%04x, request sense length = 0x%04x\n",
+ le16_to_cpu(status->res_info_len), le16_to_cpu(status->req_sense_len));
+ printk("qlogicfc : residual transfer length = 0x%08x, response = 0x%02x\n", le32_to_cpu(status->residual), status->res_info[3]);
+
+}
+
+#endif /* DEBUG_ISP2x00_INTR */
+
+
+#if DEBUG_ISP2x00
+
+void isp2x00_print_scsi_cmd(Scsi_Cmnd * cmd)
+{
+ int i;
+
+ printk("qlogicfc : target = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
+ cmd->target, cmd->lun, cmd->cmd_len);
+ printk("qlogicfc : command = ");
+ for (i = 0; i < cmd->cmd_len; i++)
+ printk("0x%02x ", cmd->cmnd[i]);
+ printk("\n");
+}
+
+#endif /* DEBUG_ISP2x00 */
+
+MODULE_LICENSE("GPL");
+
+static Scsi_Host_Template driver_template = {
+ .detect = isp2x00_detect,
+ .release = isp2x00_release,
+ .info = isp2x00_info,
+ .queuecommand = isp2x00_queuecommand,
+ .eh_abort_handler = isp2x00_abort,
+ .bios_param = isp2x00_biosparam,
+ .can_queue = QLOGICFC_REQ_QUEUE_LEN,
+ .this_id = -1,
+ .sg_tablesize = QLOGICFC_MAX_SG(QLOGICFC_REQ_QUEUE_LEN),
+ .cmd_per_lun = QLOGICFC_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/qlogicfc_asm.c b/drivers/scsi/qlogicfc_asm.c
new file mode 100644
index 000000000000..b1d45102d388
--- /dev/null
+++ b/drivers/scsi/qlogicfc_asm.c
@@ -0,0 +1,9751 @@
+/************************************************************************
+ * *
+ * --- ISP2100 Fabric Initiator/Target Firmware --- *
+ * with expanded LUN addressing *
+ * and FcTape (FCP-2) support *
+ * *
+ * *
+ ************************************************************************
+ Copyright (C) 2000 and 2001 Qlogic Corporation
+ (www.qlogic.com)
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+************************************************************************/
+
+/*
+ * Firmware Version 1.19.16 (10:36 Nov 02, 2000)
+ */
+
+static unsigned short risc_code_addr01 = 0x1000 ;
+
+static unsigned short risc_code_length2100 = 0x9260;
+static unsigned short risc_code2100[] = {
+ 0x0078, 0x102d, 0x0000, 0x9260, 0x0000, 0x0001, 0x0013, 0x0010,
+ 0x0017, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2031, 0x3939,
+ 0x3920, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3231, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x312e, 0x3139, 0x2020, 0x2020, 0x2400, 0x2091, 0x2000, 0x20c1,
+ 0x0021, 0x2039, 0xffff, 0x2019, 0xaaaa, 0x2760, 0x2069, 0x7fff,
+ 0x20c1, 0x0020, 0x2c2c, 0x2d34, 0x2762, 0x236a, 0x2c24, 0x2d04,
+ 0x266a, 0x2562, 0xa406, 0x00c0, 0x1052, 0x20c1, 0x0021, 0x2c2c,
+ 0x2362, 0x2c04, 0x2562, 0xa306, 0x0040, 0x1052, 0x20c1, 0x0020,
+ 0x2039, 0x8fff, 0x20a1, 0xaa00, 0x2708, 0x810d, 0x810d, 0x810d,
+ 0x810d, 0xa18c, 0x000f, 0x2001, 0x000a, 0xa112, 0xa00e, 0x21a8,
+ 0x41a4, 0x3400, 0x8211, 0x00c0, 0x105f, 0x2708, 0x3400, 0xa102,
+ 0x0040, 0x106f, 0x0048, 0x106f, 0x20a8, 0xa00e, 0x41a4, 0x20a1,
+ 0xa260, 0x2009, 0x0000, 0x20a9, 0x07a0, 0x41a4, 0x3400, 0x20c9,
+ 0xa7ff, 0x2059, 0x0000, 0x2b78, 0x7823, 0x0004, 0x2089, 0x255d,
+ 0x2051, 0xa300, 0x2a70, 0x775e, 0xa786, 0x8fff, 0x0040, 0x1092,
+ 0x705b, 0xca00, 0x7057, 0xc9f1, 0x7063, 0x0200, 0x7067, 0x0200,
+ 0x0078, 0x109a, 0x7057, 0xba01, 0x7063, 0x0100, 0x7067, 0x0100,
+ 0x705b, 0xba00, 0x1078, 0x12df, 0x1078, 0x13c0, 0x1078, 0x1569,
+ 0x1078, 0x1ca4, 0x1078, 0x4229, 0x1078, 0x74cf, 0x1078, 0x134b,
+ 0x1078, 0x2a3f, 0x1078, 0x4da2, 0x1078, 0x48b2, 0x1078, 0x57df,
+ 0x1078, 0x21f7, 0x1078, 0x5abf, 0x1078, 0x5369, 0x1078, 0x210d,
+ 0x1078, 0x21d4, 0x2091, 0x3009, 0x7823, 0x0000, 0x0090, 0x10cf,
+ 0x7820, 0xa086, 0x0002, 0x00c0, 0x10cf, 0x7823, 0x4000, 0x0068,
+ 0x10c7, 0x781b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x2a70,
+ 0x7003, 0x0000, 0x2001, 0x017f, 0x2003, 0x0000, 0x2a70, 0x7000,
+ 0xa08e, 0x0003, 0x00c0, 0x10ef, 0x1078, 0x35bc, 0x1078, 0x2a67,
+ 0x1078, 0x4df2, 0x1078, 0x4a75, 0x2009, 0x0100, 0x2104, 0xa082,
+ 0x0002, 0x0048, 0x10f3, 0x1078, 0x57fb, 0x0078, 0x10d6, 0x1079,
+ 0x10f7, 0x0078, 0x10dc, 0x1078, 0x6fa9, 0x0078, 0x10eb, 0x1101,
+ 0x1102, 0x11be, 0x10ff, 0x1246, 0x12dc, 0x12dd, 0x12de, 0x1078,
+ 0x1328, 0x007c, 0x127e, 0x0f7e, 0x2091, 0x8000, 0x7000, 0xa086,
+ 0x0001, 0x00c0, 0x1198, 0x1078, 0x3a43, 0x2079, 0x0100, 0x7844,
+ 0xa005, 0x00c0, 0x1198, 0x2011, 0x4129, 0x1078, 0x58d4, 0x1078,
+ 0x1ab1, 0x780f, 0x00ff, 0x7840, 0xa084, 0xfffb, 0x7842, 0x2011,
+ 0x8010, 0x73c0, 0x1078, 0x3579, 0x2001, 0xffff, 0x1078, 0x5975,
+ 0x7238, 0xc284, 0x723a, 0x2001, 0xa30c, 0x2014, 0xc2ac, 0x2202,
+ 0x1078, 0x6db5, 0x2011, 0x0004, 0x1078, 0x8a59, 0x1078, 0x47ce,
+ 0x1078, 0x4211, 0x0040, 0x1144, 0x7083, 0x0001, 0x70bb, 0x0000,
+ 0x1078, 0x3bf5, 0x0078, 0x1198, 0x1078, 0x4897, 0x0040, 0x114d,
+ 0x7a0c, 0xc2b4, 0x7a0e, 0x0078, 0x1159, 0x1078, 0x8ddf, 0x70c8,
+ 0xd09c, 0x00c0, 0x1159, 0x7094, 0xa005, 0x0040, 0x1159, 0x1078,
+ 0x41f5, 0x70d3, 0x0000, 0x70cf, 0x0000, 0x72c8, 0x2079, 0xa351,
+ 0x7804, 0xd0ac, 0x0040, 0x1165, 0xc295, 0x72ca, 0xa296, 0x0004,
+ 0x0040, 0x1186, 0x2011, 0x0001, 0x1078, 0x8a59, 0x708f, 0x0000,
+ 0x7093, 0xffff, 0x7003, 0x0002, 0x0f7f, 0x1078, 0x260d, 0x2011,
+ 0x0005, 0x1078, 0x6ef2, 0x1078, 0x6109, 0x0c7e, 0x2061, 0x0100,
+ 0x60e3, 0x0008, 0x0c7f, 0x127f, 0x0078, 0x119a, 0x708f, 0x0000,
+ 0x7093, 0xffff, 0x7003, 0x0002, 0x2011, 0x0005, 0x1078, 0x6ef2,
+ 0x1078, 0x6109, 0x0c7e, 0x2061, 0x0100, 0x60e3, 0x0008, 0x0c7f,
+ 0x0f7f, 0x127f, 0x007c, 0x0c7e, 0x20a9, 0x0082, 0x2009, 0x007e,
+ 0x017e, 0x027e, 0x037e, 0x2110, 0x027e, 0x2019, 0x0029, 0x1078,
+ 0x71e0, 0x027f, 0x1078, 0xa190, 0x037f, 0x027f, 0x017f, 0x1078,
+ 0x2921, 0x8108, 0x00f0, 0x11a0, 0x0c7f, 0x706b, 0x0000, 0x706c,
+ 0xa084, 0x00ff, 0x706e, 0x7097, 0x0000, 0x007c, 0x127e, 0x2091,
+ 0x8000, 0x7000, 0xa086, 0x0002, 0x00c0, 0x1244, 0x7090, 0xa086,
+ 0xffff, 0x0040, 0x11d1, 0x1078, 0x260d, 0x1078, 0x6109, 0x0078,
+ 0x1244, 0x70c8, 0xd09c, 0x0040, 0x11fd, 0xd084, 0x0040, 0x11fd,
+ 0x0f7e, 0x2079, 0x0100, 0x790c, 0xc1b5, 0x790e, 0x0f7f, 0xd08c,
+ 0x0040, 0x11fd, 0x70cc, 0xa086, 0xffff, 0x0040, 0x11f9, 0x1078,
+ 0x278a, 0x1078, 0x6109, 0x70c8, 0xd094, 0x00c0, 0x1244, 0x2011,
+ 0x0001, 0x2019, 0x0000, 0x1078, 0x27c2, 0x1078, 0x6109, 0x0078,
+ 0x1244, 0x70d0, 0xa005, 0x00c0, 0x1244, 0x708c, 0xa005, 0x00c0,
+ 0x1244, 0x1078, 0x4897, 0x00c0, 0x1244, 0x2001, 0xa352, 0x2004,
+ 0xd0ac, 0x0040, 0x1227, 0x157e, 0x0c7e, 0x20a9, 0x007f, 0x2009,
+ 0x0000, 0x017e, 0x1078, 0x4501, 0x00c0, 0x121a, 0x6000, 0xd0ec,
+ 0x00c0, 0x1222, 0x017f, 0x8108, 0x00f0, 0x1211, 0x0c7f, 0x157f,
+ 0x0078, 0x1227, 0x017f, 0x0c7f, 0x157f, 0x0078, 0x1244, 0x7003,
+ 0x0003, 0x7093, 0xffff, 0x2001, 0x0000, 0x1078, 0x2480, 0x1078,
+ 0x35f7, 0x2001, 0xa5ac, 0x2004, 0xa086, 0x0005, 0x00c0, 0x123c,
+ 0x2011, 0x0000, 0x1078, 0x6ef2, 0x2011, 0x0000, 0x1078, 0x6efc,
+ 0x1078, 0x6109, 0x1078, 0x61d3, 0x127f, 0x007c, 0x017e, 0x0f7e,
+ 0x127e, 0x2091, 0x8000, 0x2079, 0x0100, 0x2009, 0x00f7, 0x1078,
+ 0x41de, 0x7940, 0xa18c, 0x0010, 0x7942, 0x7924, 0xd1b4, 0x0040,
+ 0x125b, 0x7827, 0x0040, 0xd19c, 0x0040, 0x1260, 0x7827, 0x0008,
+ 0x007e, 0x037e, 0x157e, 0xa006, 0x1078, 0x5975, 0x7900, 0xa18a,
+ 0x0003, 0x0050, 0x1289, 0x7954, 0xd1ac, 0x00c0, 0x1289, 0x2009,
+ 0x00f8, 0x1078, 0x41de, 0x7843, 0x0090, 0x7843, 0x0010, 0x20a9,
+ 0x09c4, 0x7820, 0xd09c, 0x00c0, 0x1281, 0x7824, 0xd0ac, 0x00c0,
+ 0x12ca, 0x00f0, 0x1279, 0x2001, 0x0001, 0x1078, 0x2480, 0x0078,
+ 0x12d5, 0x7853, 0x0000, 0x782f, 0x0020, 0x20a9, 0x0050, 0x00e0,
+ 0x128f, 0x2091, 0x6000, 0x00f0, 0x128f, 0x7853, 0x0400, 0x782f,
+ 0x0000, 0x2009, 0x00f8, 0x1078, 0x41de, 0x20a9, 0x000e, 0x0005,
+ 0x00f0, 0x129f, 0x7853, 0x1400, 0x7843, 0x0090, 0x7843, 0x0010,
+ 0x2019, 0x61a8, 0x7854, 0x0005, 0x0005, 0xd08c, 0x0040, 0x12b4,
+ 0x7824, 0xd0ac, 0x00c0, 0x12ca, 0x8319, 0x00c0, 0x12aa, 0x2009,
+ 0xa331, 0x2104, 0x8000, 0x200a, 0xa084, 0xfff0, 0x0040, 0x12c4,
+ 0x200b, 0x0000, 0x1078, 0x251e, 0x2001, 0x0001, 0x1078, 0x2480,
+ 0x0078, 0x12d3, 0x2001, 0xa331, 0x2003, 0x0000, 0x7828, 0xc09d,
+ 0x782a, 0x7827, 0x0048, 0x7853, 0x0400, 0x157f, 0x037f, 0x007f,
+ 0x127f, 0x0f7f, 0x017f, 0x007c, 0x007c, 0x007c, 0x007c, 0x2a70,
+ 0x2009, 0x0100, 0x2104, 0xa082, 0x0002, 0x0048, 0x12eb, 0x704f,
+ 0xffff, 0x0078, 0x12ed, 0x704f, 0x0000, 0x7053, 0xffff, 0x706b,
+ 0x0000, 0x706f, 0x0000, 0x1078, 0x8ddf, 0x2061, 0xa58c, 0x6003,
+ 0x0909, 0x6007, 0x0000, 0x600b, 0x8800, 0x600f, 0x0200, 0x6013,
+ 0x00ff, 0x6017, 0x0003, 0x601b, 0x0000, 0x601f, 0x07d0, 0x2061,
+ 0xa594, 0x6003, 0x8000, 0x6007, 0x0000, 0x600b, 0x0000, 0x600f,
+ 0x0200, 0x6013, 0x00ff, 0x6017, 0x0000, 0x601b, 0x0001, 0x601f,
+ 0x0000, 0x2061, 0xa5a3, 0x6003, 0x514c, 0x6007, 0x4f47, 0x600b,
+ 0x4943, 0x600f, 0x2020, 0x2001, 0xa325, 0x2003, 0x0000, 0x007c,
+ 0x2091, 0x8000, 0x0068, 0x132a, 0x007e, 0x017e, 0x2079, 0x0000,
+ 0x7818, 0xd084, 0x00c0, 0x1330, 0x017f, 0x792e, 0x007f, 0x782a,
+ 0x007f, 0x7826, 0x3900, 0x783a, 0x7823, 0x8002, 0x781b, 0x0001,
+ 0x2091, 0x5000, 0x2091, 0x4080, 0x2079, 0xa300, 0x7803, 0x0005,
+ 0x0078, 0x1348, 0x007c, 0x2071, 0xa300, 0x7158, 0x712e, 0x2021,
+ 0x0001, 0xa190, 0x002d, 0xa298, 0x002d, 0x0048, 0x1361, 0x705c,
+ 0xa302, 0x00c8, 0x1361, 0x220a, 0x2208, 0x2310, 0x8420, 0x0078,
+ 0x1353, 0x200b, 0x0000, 0x74a6, 0x74aa, 0x007c, 0x0e7e, 0x127e,
+ 0x2091, 0x8000, 0x2071, 0xa300, 0x70a8, 0xa0ea, 0x0010, 0x00c8,
+ 0x1374, 0xa06e, 0x0078, 0x137e, 0x8001, 0x70aa, 0x702c, 0x2068,
+ 0x2d04, 0x702e, 0x206b, 0x0000, 0x6807, 0x0000, 0x127f, 0x0e7f,
+ 0x007c, 0x0e7e, 0x2071, 0xa300, 0x127e, 0x2091, 0x8000, 0x70a8,
+ 0x8001, 0x00c8, 0x138e, 0xa06e, 0x0078, 0x1397, 0x70aa, 0x702c,
+ 0x2068, 0x2d04, 0x702e, 0x206b, 0x0000, 0x6807, 0x0000, 0x127f,
+ 0x0e7f, 0x007c, 0x0e7e, 0x127e, 0x2091, 0x8000, 0x2071, 0xa300,
+ 0x702c, 0x206a, 0x2d00, 0x702e, 0x70a8, 0x8000, 0x70aa, 0x127f,
+ 0x0e7f, 0x007c, 0x8dff, 0x0040, 0x13b6, 0x6804, 0x6807, 0x0000,
+ 0x007e, 0x1078, 0x139a, 0x0d7f, 0x0078, 0x13aa, 0x007c, 0x0e7e,
+ 0x2071, 0xa300, 0x70a8, 0xa08a, 0x0010, 0xa00d, 0x0e7f, 0x007c,
+ 0x0e7e, 0x2071, 0xa5d0, 0x7007, 0x0000, 0x701b, 0x0000, 0x701f,
+ 0x0000, 0x2071, 0x0000, 0x7010, 0xa085, 0x8004, 0x7012, 0x0e7f,
+ 0x007c, 0x0e7e, 0x2270, 0x700b, 0x0000, 0x2071, 0xa5d0, 0x7018,
+ 0xa088, 0xa5d9, 0x220a, 0x8000, 0xa084, 0x0007, 0x701a, 0x7004,
+ 0xa005, 0x00c0, 0x13e9, 0x0f7e, 0x2079, 0x0010, 0x1078, 0x13fa,
+ 0x0f7f, 0x0e7f, 0x007c, 0x0e7e, 0x2071, 0xa5d0, 0x7004, 0xa005,
+ 0x00c0, 0x13f8, 0x0f7e, 0x2079, 0x0010, 0x1078, 0x13fa, 0x0f7f,
+ 0x0e7f, 0x007c, 0x7000, 0x0079, 0x13fd, 0x1401, 0x146b, 0x1488,
+ 0x1488, 0x7018, 0x711c, 0xa106, 0x00c0, 0x1409, 0x7007, 0x0000,
+ 0x007c, 0x0d7e, 0xa180, 0xa5d9, 0x2004, 0x700a, 0x2068, 0x8108,
+ 0xa18c, 0x0007, 0x711e, 0x7803, 0x0026, 0x6824, 0x7832, 0x6828,
+ 0x7836, 0x682c, 0x783a, 0x6830, 0x783e, 0x6810, 0x700e, 0x680c,
+ 0x7016, 0x6804, 0x0d7f, 0xd084, 0x0040, 0x142b, 0x7007, 0x0001,
+ 0x1078, 0x1430, 0x007c, 0x7007, 0x0002, 0x1078, 0x1446, 0x007c,
+ 0x017e, 0x027e, 0x710c, 0x2011, 0x0040, 0xa182, 0x0040, 0x00c8,
+ 0x143b, 0x2110, 0xa006, 0x700e, 0x7212, 0x8203, 0x7822, 0x7803,
+ 0x0020, 0x7803, 0x0041, 0x027f, 0x017f, 0x007c, 0x017e, 0x027e,
+ 0x137e, 0x147e, 0x157e, 0x7014, 0x2098, 0x20a1, 0x0014, 0x7803,
+ 0x0026, 0x710c, 0x2011, 0x0040, 0xa182, 0x0040, 0x00c8, 0x145a,
+ 0x2110, 0xa006, 0x700e, 0x22a8, 0x53a6, 0x8203, 0x7822, 0x7803,
+ 0x0020, 0x3300, 0x7016, 0x7803, 0x0001, 0x157f, 0x147f, 0x137f,
+ 0x027f, 0x017f, 0x007c, 0x137e, 0x147e, 0x157e, 0x2099, 0xa3f9,
+ 0x20a1, 0x0018, 0x20a9, 0x0008, 0x53a3, 0x7803, 0x0020, 0x127e,
+ 0x2091, 0x8000, 0x7803, 0x0041, 0x7007, 0x0003, 0x7000, 0xc084,
+ 0x7002, 0x700b, 0xa3f4, 0x127f, 0x157f, 0x147f, 0x137f, 0x007c,
+ 0x137e, 0x147e, 0x157e, 0x2001, 0xa428, 0x209c, 0x20a1, 0x0014,
+ 0x7803, 0x0026, 0x2001, 0xa429, 0x20ac, 0x53a6, 0x2099, 0xa42a,
+ 0x20a1, 0x0018, 0x20a9, 0x0008, 0x53a3, 0x7803, 0x0020, 0x127e,
+ 0x2091, 0x8000, 0x7803, 0x0001, 0x7007, 0x0004, 0x7000, 0xc08c,
+ 0x7002, 0x700b, 0xa425, 0x127f, 0x157f, 0x147f, 0x137f, 0x007c,
+ 0x017e, 0x0e7e, 0x2071, 0xa5d0, 0x0f7e, 0x2079, 0x0010, 0x7904,
+ 0x7803, 0x0002, 0xd1fc, 0x0040, 0x14c2, 0xa18c, 0x0700, 0x7004,
+ 0x1079, 0x14c6, 0x0f7f, 0x0e7f, 0x017f, 0x007c, 0x13fa, 0x14ce,
+ 0x14fb, 0x1523, 0x1556, 0x14cc, 0x0078, 0x14cc, 0xa18c, 0x0700,
+ 0x00c0, 0x14f4, 0x137e, 0x147e, 0x157e, 0x7014, 0x20a0, 0x2099,
+ 0x0014, 0x7803, 0x0040, 0x7010, 0x20a8, 0x53a5, 0x3400, 0x7016,
+ 0x157f, 0x147f, 0x137f, 0x700c, 0xa005, 0x0040, 0x1510, 0x1078,
+ 0x1430, 0x007c, 0x7008, 0xa080, 0x0002, 0x2003, 0x0100, 0x7007,
+ 0x0000, 0x1078, 0x13fa, 0x007c, 0x7008, 0xa080, 0x0002, 0x2003,
+ 0x0200, 0x0078, 0x14ef, 0xa18c, 0x0700, 0x00c0, 0x1506, 0x700c,
+ 0xa005, 0x0040, 0x1510, 0x1078, 0x1446, 0x007c, 0x7008, 0xa080,
+ 0x0002, 0x2003, 0x0200, 0x7007, 0x0000, 0x1078, 0x13fa, 0x007c,
+ 0x0d7e, 0x7008, 0x2068, 0x7830, 0x6826, 0x7834, 0x682a, 0x7838,
+ 0x682e, 0x783c, 0x6832, 0x680b, 0x0100, 0x0d7f, 0x7007, 0x0000,
+ 0x1078, 0x13fa, 0x007c, 0xa18c, 0x0700, 0x00c0, 0x1550, 0x137e,
+ 0x147e, 0x157e, 0x2001, 0xa3f7, 0x2004, 0xa080, 0x000d, 0x20a0,
+ 0x2099, 0x0014, 0x7803, 0x0040, 0x20a9, 0x0020, 0x53a5, 0x2001,
+ 0xa3f9, 0x2004, 0xd0bc, 0x0040, 0x1546, 0x2001, 0xa402, 0x2004,
+ 0xa080, 0x000d, 0x20a0, 0x20a9, 0x0020, 0x53a5, 0x157f, 0x147f,
+ 0x137f, 0x7007, 0x0000, 0x1078, 0x4e9b, 0x1078, 0x13fa, 0x007c,
+ 0x2011, 0x8003, 0x1078, 0x3579, 0x0078, 0x1554, 0xa18c, 0x0700,
+ 0x00c0, 0x1563, 0x2001, 0xa427, 0x2003, 0x0100, 0x7007, 0x0000,
+ 0x1078, 0x13fa, 0x007c, 0x2011, 0x8004, 0x1078, 0x3579, 0x0078,
+ 0x1567, 0x127e, 0x2091, 0x2100, 0x2079, 0x0030, 0x2071, 0xa5e1,
+ 0x7803, 0x0004, 0x7003, 0x0000, 0x700f, 0xa5e7, 0x7013, 0xa5e7,
+ 0x780f, 0x0076, 0x7803, 0x0004, 0x127f, 0x007c, 0x6934, 0xa184,
+ 0x0007, 0x0079, 0x1583, 0x158b, 0x15d1, 0x158b, 0x158b, 0x158b,
+ 0x15b6, 0x159a, 0x158f, 0xa085, 0x0001, 0x0078, 0x15eb, 0x684c,
+ 0xd0bc, 0x0040, 0x158b, 0x6860, 0x682e, 0x685c, 0x682a, 0x6858,
+ 0x0078, 0x15d9, 0xa18c, 0x00ff, 0xa186, 0x001e, 0x00c0, 0x158b,
+ 0x684c, 0xd0bc, 0x0040, 0x158b, 0x6860, 0x682e, 0x685c, 0x682a,
+ 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080,
+ 0x2015, 0x2004, 0x6832, 0x6858, 0x0078, 0x15e1, 0xa18c, 0x00ff,
+ 0xa186, 0x0015, 0x00c0, 0x158b, 0x684c, 0xd0ac, 0x0040, 0x158b,
+ 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080,
+ 0x2015, 0x2004, 0x6832, 0xa006, 0x682e, 0x682a, 0x6858, 0x0078,
+ 0x15e1, 0x684c, 0xd0ac, 0x0040, 0x158b, 0xa006, 0x682e, 0x682a,
+ 0x6858, 0xa18c, 0x000f, 0xa188, 0x2015, 0x210c, 0x6932, 0x2d08,
+ 0x691a, 0x6826, 0x684c, 0xc0dd, 0x684e, 0xa006, 0x680a, 0x697c,
+ 0x6912, 0x6980, 0x6916, 0x007c, 0x20e1, 0x0007, 0x20e1, 0x2000,
+ 0x2001, 0x020a, 0x2004, 0x82ff, 0x0040, 0x160e, 0xa280, 0x0004,
+ 0x0d7e, 0x206c, 0x684c, 0xd0dc, 0x00c0, 0x160a, 0x1078, 0x157e,
+ 0x0040, 0x160a, 0x0d7f, 0xa280, 0x0000, 0x2003, 0x0002, 0xa016,
+ 0x0078, 0x160e, 0x6808, 0x8000, 0x680a, 0x0d7f, 0x127e, 0x047e,
+ 0x037e, 0x027e, 0x2091, 0x2100, 0x027f, 0x037f, 0x047f, 0x7000,
+ 0xa005, 0x00c0, 0x1622, 0x7206, 0x2001, 0x1643, 0x007e, 0x2260,
+ 0x0078, 0x17be, 0x710c, 0x220a, 0x8108, 0x230a, 0x8108, 0x240a,
+ 0x8108, 0xa182, 0xa602, 0x0048, 0x162f, 0x2009, 0xa5e7, 0x710e,
+ 0x7010, 0xa102, 0xa082, 0x0009, 0x0040, 0x163a, 0xa080, 0x001b,
+ 0x00c0, 0x163d, 0x2009, 0x0138, 0x200a, 0x7000, 0xa005, 0x00c0,
+ 0x1643, 0x1078, 0x179f, 0x127f, 0x007c, 0x127e, 0x027e, 0x037e,
+ 0x0c7e, 0x007e, 0x2091, 0x2100, 0x007f, 0x047f, 0x037f, 0x027f,
+ 0x0d7e, 0x0c7e, 0x2460, 0x6110, 0x2168, 0x6a62, 0x6b5e, 0xa005,
+ 0x0040, 0x16cf, 0x6808, 0xa005, 0x0040, 0x173c, 0x7000, 0xa005,
+ 0x00c0, 0x1664, 0x0078, 0x16c4, 0x700c, 0x7110, 0xa106, 0x00c0,
+ 0x1745, 0x7004, 0xa406, 0x00c0, 0x16c4, 0x2001, 0x0005, 0x2004,
+ 0xd08c, 0x0040, 0x1681, 0x047e, 0x1078, 0x18e2, 0x047f, 0x2460,
+ 0x6010, 0xa080, 0x0002, 0x2004, 0xa005, 0x0040, 0x173c, 0x0078,
+ 0x165e, 0x2001, 0x0207, 0x2004, 0xd09c, 0x00c0, 0x166d, 0x7804,
+ 0xa084, 0x6000, 0x0040, 0x1692, 0xa086, 0x6000, 0x0040, 0x1692,
+ 0x0078, 0x166d, 0x7100, 0xa186, 0x0002, 0x00c0, 0x16b2, 0x0e7e,
+ 0x2b68, 0x6818, 0x2060, 0x1078, 0x1fea, 0x2804, 0xac70, 0x6034,
+ 0xd09c, 0x00c0, 0x16a7, 0x7108, 0x720c, 0x0078, 0x16a9, 0x7110,
+ 0x7214, 0x6810, 0xa100, 0x6812, 0x6814, 0xa201, 0x6816, 0x0e7f,
+ 0x0078, 0x16b6, 0xa186, 0x0001, 0x00c0, 0x16be, 0x7820, 0x6910,
+ 0xa100, 0x6812, 0x7824, 0x6914, 0xa101, 0x6816, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0x7004, 0x2060, 0x6100, 0xa18e, 0x0004, 0x00c0,
+ 0x1745, 0x2009, 0x0048, 0x1078, 0x756c, 0x0078, 0x1745, 0x6808,
+ 0xa005, 0x0040, 0x173c, 0x7000, 0xa005, 0x00c0, 0x16d9, 0x0078,
+ 0x173c, 0x700c, 0x7110, 0xa106, 0x00c0, 0x16e2, 0x7004, 0xa406,
+ 0x00c0, 0x173c, 0x2001, 0x0005, 0x2004, 0xd08c, 0x0040, 0x16f6,
+ 0x047e, 0x1078, 0x18e2, 0x047f, 0x2460, 0x6010, 0xa080, 0x0002,
+ 0x2004, 0xa005, 0x0040, 0x173c, 0x0078, 0x16d3, 0x2001, 0x0207,
+ 0x2004, 0xd09c, 0x00c0, 0x16e2, 0x2001, 0x0005, 0x2004, 0xd08c,
+ 0x00c0, 0x16e8, 0x7804, 0xa084, 0x6000, 0x0040, 0x170d, 0xa086,
+ 0x6000, 0x0040, 0x170d, 0x0078, 0x16e2, 0x7007, 0x0000, 0xa016,
+ 0x2218, 0x7000, 0xa08e, 0x0001, 0x0040, 0x172e, 0xa08e, 0x0002,
+ 0x00c0, 0x173c, 0x0c7e, 0x0e7e, 0x6818, 0x2060, 0x1078, 0x1fea,
+ 0x2804, 0xac70, 0x6034, 0xd09c, 0x00c0, 0x172a, 0x7308, 0x720c,
+ 0x0078, 0x172c, 0x7310, 0x7214, 0x0e7f, 0x0c7f, 0x7820, 0xa318,
+ 0x7824, 0xa211, 0x6810, 0xa300, 0x6812, 0x6814, 0xa201, 0x6816,
+ 0x7803, 0x0004, 0x7003, 0x0000, 0x6100, 0xa18e, 0x0004, 0x00c0,
+ 0x1745, 0x2009, 0x0048, 0x1078, 0x756c, 0x0c7f, 0x0d7f, 0x127f,
+ 0x007c, 0x0f7e, 0x0e7e, 0x027e, 0x037e, 0x047e, 0x1078, 0x1af7,
+ 0x027e, 0x2071, 0xa5e1, 0x7000, 0xa086, 0x0000, 0x0040, 0x1790,
+ 0x7004, 0xac06, 0x00c0, 0x1781, 0x2079, 0x0030, 0x7000, 0xa086,
+ 0x0003, 0x0040, 0x1781, 0x7804, 0xd0fc, 0x00c0, 0x177d, 0x2001,
+ 0x0207, 0x2004, 0xd09c, 0x00c0, 0x1763, 0x7803, 0x0004, 0x7804,
+ 0xd0ac, 0x00c0, 0x176f, 0x7803, 0x0002, 0x7803, 0x0009, 0x7003,
+ 0x0003, 0x7007, 0x0000, 0x0078, 0x1781, 0x1078, 0x18e2, 0x0078,
+ 0x1753, 0x157e, 0x20a9, 0x0009, 0x2009, 0xa5e7, 0x2104, 0xac06,
+ 0x00c0, 0x178b, 0x200a, 0xa188, 0x0003, 0x00f0, 0x1786, 0x157f,
+ 0x027f, 0x2001, 0x015d, 0x201c, 0x831a, 0x2302, 0x2001, 0x0138,
+ 0x2202, 0x047f, 0x037f, 0x027f, 0x0e7f, 0x0f7f, 0x007c, 0x700c,
+ 0x7110, 0xa106, 0x00c0, 0x17a7, 0x7003, 0x0000, 0x007c, 0x2104,
+ 0x7006, 0x2060, 0x8108, 0x211c, 0x8108, 0x2124, 0x8108, 0xa182,
+ 0xa602, 0x0048, 0x17b5, 0x2009, 0xa5e7, 0x7112, 0x700c, 0xa106,
+ 0x00c0, 0x17be, 0x2001, 0x0138, 0x2003, 0x0008, 0x8cff, 0x00c0,
+ 0x17c5, 0x1078, 0x1b22, 0x0078, 0x1823, 0x6010, 0x2068, 0x2d58,
+ 0x6828, 0xa406, 0x00c0, 0x17d0, 0x682c, 0xa306, 0x0040, 0x17fe,
+ 0x601c, 0xa086, 0x0008, 0x0040, 0x17fe, 0x6024, 0xd0f4, 0x00c0,
+ 0x17fa, 0xd0d4, 0x0040, 0x17f6, 0x6038, 0xa402, 0x6034, 0xa303,
+ 0x0040, 0x17e4, 0x00c8, 0x17f6, 0x643a, 0x6336, 0x6c2a, 0x6b2e,
+ 0x047e, 0x037e, 0x2400, 0x6c7c, 0xa402, 0x6812, 0x2300, 0x6b80,
+ 0xa303, 0x6816, 0x037f, 0x047f, 0x0078, 0x17fa, 0x1078, 0x8d8e,
+ 0x0040, 0x17c1, 0x1078, 0x2035, 0x00c0, 0x17c1, 0x0c7e, 0x7004,
+ 0x2060, 0x6024, 0xc0d4, 0x6026, 0x0c7f, 0x684c, 0xd0f4, 0x0040,
+ 0x180f, 0x6817, 0xffff, 0x6813, 0xffff, 0x0078, 0x17c1, 0x6824,
+ 0x2050, 0x6818, 0x2060, 0x6830, 0x2040, 0x6034, 0xa0cc, 0x000f,
+ 0x2009, 0x0011, 0x1078, 0x1824, 0x0040, 0x1822, 0x2009, 0x0001,
+ 0x1078, 0x1824, 0x2d58, 0x007c, 0x8aff, 0x0040, 0x18bb, 0xa03e,
+ 0x2730, 0x6850, 0xd0fc, 0x00c0, 0x1846, 0xd0f4, 0x00c0, 0x1856,
+ 0x0d7e, 0x2804, 0xac68, 0x2900, 0x0079, 0x1836, 0x189d, 0x185d,
+ 0x185d, 0x189d, 0x189d, 0x1895, 0x189d, 0x185d, 0x189d, 0x1863,
+ 0x1863, 0x189d, 0x189d, 0x189d, 0x188c, 0x1863, 0xc0fc, 0x6852,
+ 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0x0d7e, 0xd99c, 0x0040, 0x18a0,
+ 0x2804, 0xac68, 0x6f08, 0x6e0c, 0x0078, 0x18a0, 0xc0f4, 0x6852,
+ 0x6b6c, 0x6a70, 0x0d7e, 0x0078, 0x18a7, 0x6b08, 0x6a0c, 0x6d00,
+ 0x6c04, 0x0078, 0x18a0, 0x7b0c, 0xd3bc, 0x0040, 0x1884, 0x7004,
+ 0x0e7e, 0x2070, 0x701c, 0x0e7f, 0xa086, 0x0008, 0x00c0, 0x1884,
+ 0x7b08, 0xa39c, 0x0fff, 0x2d20, 0x0d7f, 0x0d7e, 0x6a14, 0x82ff,
+ 0x00c0, 0x187f, 0x6810, 0xa302, 0x0048, 0x187f, 0x6b10, 0x2011,
+ 0x0000, 0x2468, 0x0078, 0x1886, 0x6b10, 0x6a14, 0x6d00, 0x6c04,
+ 0x6f08, 0x6e0c, 0x0078, 0x18a0, 0x0d7f, 0x0d7e, 0x6834, 0xa084,
+ 0x00ff, 0xa086, 0x001e, 0x00c0, 0x189d, 0x0d7f, 0x1078, 0x1fd1,
+ 0x00c0, 0x1824, 0xa00e, 0x0078, 0x18bb, 0x0d7f, 0x1078, 0x1328,
+ 0x7b22, 0x7a26, 0x7d32, 0x7c36, 0x7f3a, 0x7e3e, 0x7902, 0x7000,
+ 0x8000, 0x7002, 0x0d7f, 0x6828, 0xa300, 0x682a, 0x682c, 0xa201,
+ 0x682e, 0x2300, 0x6b10, 0xa302, 0x6812, 0x2200, 0x6a14, 0xa203,
+ 0x6816, 0x1078, 0x1fd1, 0x007c, 0x1078, 0x1328, 0x1078, 0x1c52,
+ 0x7004, 0x2060, 0x0d7e, 0x6010, 0x2068, 0x7003, 0x0000, 0x1078,
+ 0x1ac6, 0x1078, 0x8a44, 0x0040, 0x18db, 0x6808, 0x8001, 0x680a,
+ 0x697c, 0x6912, 0x6980, 0x6916, 0x682b, 0xffff, 0x682f, 0xffff,
+ 0x6850, 0xc0bd, 0x6852, 0x0d7f, 0x1078, 0x8758, 0x0078, 0x1aad,
+ 0x1078, 0x1328, 0x127e, 0x2091, 0x2100, 0x007e, 0x017e, 0x2b68,
+ 0x6818, 0x2060, 0x7904, 0x7803, 0x0002, 0xa184, 0x0700, 0x00c0,
+ 0x18be, 0xa184, 0x0003, 0xa086, 0x0003, 0x0040, 0x18e0, 0x7000,
+ 0x0079, 0x18fa, 0x1902, 0x1904, 0x1a06, 0x1a84, 0x1a9b, 0x1902,
+ 0x1902, 0x1902, 0x1078, 0x1328, 0x8001, 0x7002, 0xa184, 0x0880,
+ 0x00c0, 0x1919, 0x8aff, 0x0040, 0x199b, 0x2009, 0x0001, 0x1078,
+ 0x1824, 0x0040, 0x1aad, 0x2009, 0x0001, 0x1078, 0x1824, 0x0078,
+ 0x1aad, 0x7803, 0x0004, 0x7003, 0x0000, 0xd1bc, 0x00c0, 0x1979,
+ 0x027e, 0x037e, 0x7808, 0xd0ec, 0x00c0, 0x1930, 0x7c20, 0x7d24,
+ 0x7e30, 0x7f34, 0x7803, 0x0009, 0x7003, 0x0004, 0x0078, 0x1932,
+ 0x1078, 0x1b9f, 0x6b28, 0x6a2c, 0x2400, 0x686e, 0xa31a, 0x2500,
+ 0x6872, 0xa213, 0x6b2a, 0x6a2e, 0x0c7e, 0x7004, 0x2060, 0x6024,
+ 0xd0f4, 0x00c0, 0x1945, 0x633a, 0x6236, 0x0c7f, 0x2400, 0x6910,
+ 0xa100, 0x6812, 0x2500, 0x6914, 0xa101, 0x6816, 0x037f, 0x027f,
+ 0x2600, 0x681e, 0x2700, 0x6822, 0x1078, 0x1fea, 0x2a00, 0x6826,
+ 0x2c00, 0x681a, 0x2800, 0x6832, 0x6850, 0xc0fd, 0x6852, 0x6808,
+ 0x8001, 0x680a, 0x00c0, 0x196e, 0x684c, 0xd0e4, 0x0040, 0x196e,
+ 0x7004, 0x2060, 0x2009, 0x0048, 0x1078, 0x756c, 0x7000, 0xa086,
+ 0x0004, 0x0040, 0x1aad, 0x7003, 0x0000, 0x1078, 0x179f, 0x0078,
+ 0x1aad, 0x057e, 0x7d0c, 0xd5bc, 0x00c0, 0x1980, 0x1078, 0xa20c,
+ 0x057f, 0x1078, 0x1ac6, 0x0f7e, 0x7004, 0x2078, 0x1078, 0x4893,
+ 0x0040, 0x198d, 0x7824, 0xc0f5, 0x7826, 0x0f7f, 0x682b, 0xffff,
+ 0x682f, 0xffff, 0x6808, 0x8001, 0x680a, 0x697c, 0x6912, 0x6980,
+ 0x6916, 0x0078, 0x1aad, 0x7004, 0x0c7e, 0x2060, 0x6024, 0x0c7f,
+ 0xd0f4, 0x0040, 0x19a8, 0x6808, 0x8001, 0x680a, 0x0078, 0x1aad,
+ 0x684c, 0xc0f5, 0x684e, 0x7814, 0xa005, 0x00c0, 0x19c0, 0x7003,
+ 0x0000, 0x6808, 0x8001, 0x680a, 0x00c0, 0x19bc, 0x7004, 0x2060,
+ 0x2009, 0x0048, 0x1078, 0x756c, 0x1078, 0x179f, 0x0078, 0x1aad,
+ 0x7814, 0x6910, 0xa102, 0x6812, 0x6914, 0xa183, 0x0000, 0x6816,
+ 0x7814, 0x7908, 0xa18c, 0x0fff, 0xa188, 0x0007, 0x8114, 0x8214,
+ 0x8214, 0xa10a, 0x8104, 0x8004, 0x8004, 0xa20a, 0x810b, 0x810b,
+ 0x810b, 0x1078, 0x1b4d, 0x7803, 0x0004, 0x780f, 0xffff, 0x7803,
+ 0x0001, 0x7804, 0xd0fc, 0x0040, 0x19e1, 0x7803, 0x0002, 0x7803,
+ 0x0004, 0x780f, 0x0076, 0x7004, 0x7007, 0x0000, 0x2060, 0x2009,
+ 0x0048, 0x1078, 0x756c, 0x1078, 0x1b81, 0x0040, 0x19bc, 0x7908,
+ 0xd1ec, 0x00c0, 0x19ff, 0x2009, 0x0009, 0x0078, 0x1a01, 0x2009,
+ 0x0019, 0x7902, 0x7003, 0x0003, 0x0078, 0x1aad, 0x8001, 0x7002,
+ 0xd194, 0x0040, 0x1a18, 0x7804, 0xd0fc, 0x00c0, 0x18ea, 0x8aff,
+ 0x0040, 0x1aad, 0x2009, 0x0001, 0x1078, 0x1824, 0x0078, 0x1aad,
+ 0xa184, 0x0880, 0x00c0, 0x1a25, 0x8aff, 0x0040, 0x1aad, 0x2009,
+ 0x0001, 0x1078, 0x1824, 0x0078, 0x1aad, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0xd1bc, 0x00c0, 0x1a65, 0x027e, 0x037e, 0x7808, 0xd0ec,
+ 0x00c0, 0x1a38, 0x7803, 0x0009, 0x7003, 0x0004, 0x0078, 0x1a3a,
+ 0x1078, 0x1b9f, 0x6b28, 0x6a2c, 0x1078, 0x1fea, 0x0d7e, 0x0f7e,
+ 0x2d78, 0x2804, 0xac68, 0x6034, 0xd09c, 0x00c0, 0x1a55, 0x6808,
+ 0x2008, 0xa31a, 0x680c, 0xa213, 0x7810, 0xa100, 0x7812, 0x690c,
+ 0x7814, 0xa101, 0x7816, 0x0078, 0x1a61, 0x6810, 0x2008, 0xa31a,
+ 0x6814, 0xa213, 0x7810, 0xa100, 0x7812, 0x6914, 0x7814, 0xa101,
+ 0x7816, 0x0f7f, 0x0d7f, 0x0078, 0x1934, 0x057e, 0x7d0c, 0x1078,
+ 0xa20c, 0x057f, 0x1078, 0x1ac6, 0x0f7e, 0x7004, 0x2078, 0x1078,
+ 0x4893, 0x0040, 0x1a76, 0x7824, 0xc0f5, 0x7826, 0x0f7f, 0x682b,
+ 0xffff, 0x682f, 0xffff, 0x6808, 0x8001, 0x680a, 0x697c, 0x6912,
+ 0x6980, 0x6916, 0x0078, 0x1aad, 0x7803, 0x0004, 0x7003, 0x0000,
+ 0x7004, 0xa00d, 0x0040, 0x1a97, 0x6808, 0x8001, 0x680a, 0x00c0,
+ 0x1a97, 0x7004, 0x2060, 0x2009, 0x0048, 0x1078, 0x756c, 0x1078,
+ 0x179f, 0x0078, 0x1aad, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004,
+ 0x2060, 0x6010, 0xa005, 0x0040, 0x1a97, 0x2068, 0x6808, 0x8000,
+ 0x680a, 0x6c28, 0x6b2c, 0x1078, 0x17be, 0x017f, 0x007f, 0x127f,
+ 0x007c, 0x127e, 0x2091, 0x2100, 0x7000, 0xa086, 0x0003, 0x00c0,
+ 0x1ac4, 0x700c, 0x7110, 0xa106, 0x0040, 0x1ac4, 0x20e1, 0x9028,
+ 0x700f, 0xa5e7, 0x7013, 0xa5e7, 0x127f, 0x007c, 0x0c7e, 0x1078,
+ 0x1af7, 0x20e1, 0x9028, 0x700c, 0x7110, 0xa106, 0x0040, 0x1aed,
+ 0x2104, 0xa005, 0x0040, 0x1ada, 0x2060, 0x6010, 0x2060, 0x6008,
+ 0x8001, 0x600a, 0xa188, 0x0003, 0xa182, 0xa602, 0x0048, 0x1ae2,
+ 0x2009, 0xa5e7, 0x7112, 0x700c, 0xa106, 0x00c0, 0x1acb, 0x2001,
+ 0x0138, 0x2003, 0x0008, 0x0078, 0x1acb, 0x2001, 0x015d, 0x200c,
+ 0x810a, 0x2102, 0x2001, 0x0138, 0x2202, 0x0c7f, 0x007c, 0x2001,
+ 0x0138, 0x2014, 0x2003, 0x0000, 0x2021, 0xb015, 0x2001, 0x0141,
+ 0x201c, 0xd3dc, 0x00c0, 0x1b14, 0x2001, 0x0109, 0x201c, 0xa39c,
+ 0x0048, 0x00c0, 0x1b14, 0x2001, 0x0111, 0x201c, 0x83ff, 0x00c0,
+ 0x1b14, 0x8421, 0x00c0, 0x1afe, 0x007c, 0x2011, 0x0201, 0x2009,
+ 0x003c, 0x2204, 0xa005, 0x00c0, 0x1b21, 0x8109, 0x00c0, 0x1b19,
+ 0x007c, 0x007c, 0x1078, 0x1b15, 0x0040, 0x1b4a, 0x7908, 0xd1ec,
+ 0x00c0, 0x1b3a, 0x1078, 0x1b81, 0x0040, 0x1b3a, 0x7803, 0x0009,
+ 0x7904, 0xd1fc, 0x0040, 0x1b30, 0x7803, 0x0006, 0x1078, 0x1b15,
+ 0x0040, 0x1b4a, 0x780c, 0xd0a4, 0x00c0, 0x1b4a, 0x7007, 0x0000,
+ 0x1078, 0x1b81, 0x0040, 0x1b4c, 0x7803, 0x0019, 0x7003, 0x0003,
+ 0x0078, 0x1b4c, 0x1078, 0x1ac6, 0x007c, 0x0e7e, 0x2071, 0x0200,
+ 0x7808, 0xa084, 0xf000, 0xa10d, 0x1078, 0x1af7, 0x2019, 0x5000,
+ 0x8319, 0x0040, 0x1b6b, 0x2001, 0xa602, 0x2004, 0xa086, 0x0000,
+ 0x0040, 0x1b6b, 0x2001, 0x0021, 0xd0fc, 0x0040, 0x1b58, 0x1078,
+ 0x1e5d, 0x0078, 0x1b56, 0x20e1, 0x7000, 0x7324, 0x7420, 0x7028,
+ 0x7028, 0x7426, 0x7037, 0x0001, 0x810f, 0x712e, 0x702f, 0x0100,
+ 0x7037, 0x0008, 0x7326, 0x7422, 0x2001, 0x0138, 0x2202, 0x0e7f,
+ 0x007c, 0x7908, 0xa18c, 0x0fff, 0xa182, 0x0009, 0x0048, 0x1b8c,
+ 0xa085, 0x0001, 0x0078, 0x1b9e, 0x2001, 0x020a, 0x81ff, 0x0040,
+ 0x1b97, 0x20e1, 0x6000, 0x200c, 0x200c, 0x200c, 0x200c, 0x20e1,
+ 0x7000, 0x200c, 0x200c, 0x7003, 0x0000, 0xa006, 0x007c, 0x7c20,
+ 0x7d24, 0x7e30, 0x7f34, 0x700c, 0x7110, 0xa106, 0x0040, 0x1c24,
+ 0x7004, 0x017e, 0x210c, 0xa106, 0x017f, 0x0040, 0x1c24, 0x0d7e,
+ 0x0c7e, 0x216c, 0x2d00, 0xa005, 0x0040, 0x1c22, 0x6824, 0xd0d4,
+ 0x00c0, 0x1c22, 0x6810, 0x2068, 0x6850, 0xd0fc, 0x0040, 0x1bec,
+ 0x8108, 0x2104, 0x6b2c, 0xa306, 0x00c0, 0x1c22, 0x8108, 0x2104,
+ 0x6a28, 0xa206, 0x00c0, 0x1c22, 0x6850, 0xc0fc, 0xc0f5, 0x6852,
+ 0x686c, 0x7822, 0x6870, 0x7826, 0x681c, 0x7832, 0x6820, 0x7836,
+ 0x6818, 0x2060, 0x6034, 0xd09c, 0x0040, 0x1be7, 0x6830, 0x2004,
+ 0xac68, 0x6808, 0x783a, 0x680c, 0x783e, 0x0078, 0x1c20, 0xa006,
+ 0x783a, 0x783e, 0x0078, 0x1c20, 0x8108, 0x2104, 0xa005, 0x00c0,
+ 0x1c22, 0x8108, 0x2104, 0xa005, 0x00c0, 0x1c22, 0x6850, 0xc0f5,
+ 0x6852, 0x6830, 0x2004, 0x6918, 0xa160, 0xa180, 0x000d, 0x2004,
+ 0xd09c, 0x00c0, 0x1c12, 0x6008, 0x7822, 0x686e, 0x600c, 0x7826,
+ 0x6872, 0x6000, 0x7832, 0x6004, 0x7836, 0xa006, 0x783a, 0x783e,
+ 0x0078, 0x1c20, 0x6010, 0x7822, 0x686e, 0x6014, 0x7826, 0x6872,
+ 0x6000, 0x7832, 0x6004, 0x7836, 0x6008, 0x783a, 0x600c, 0x783e,
+ 0x7803, 0x0011, 0x0c7f, 0x0d7f, 0x007c, 0x0f7e, 0x0e7e, 0x017e,
+ 0x027e, 0x2071, 0xa5e1, 0x2079, 0x0030, 0x2011, 0x0050, 0x7000,
+ 0xa086, 0x0000, 0x0040, 0x1c4d, 0x8211, 0x0040, 0x1c4b, 0x2001,
+ 0x0005, 0x2004, 0xd08c, 0x0040, 0x1c34, 0x7904, 0xa18c, 0x0780,
+ 0x017e, 0x1078, 0x18e2, 0x017f, 0x81ff, 0x00c0, 0x1c4b, 0x2011,
+ 0x0050, 0x0078, 0x1c2f, 0xa085, 0x0001, 0x027f, 0x017f, 0x0e7f,
+ 0x0f7f, 0x007c, 0x7803, 0x0004, 0x2009, 0x0064, 0x7804, 0xd0ac,
+ 0x0040, 0x1ca3, 0x8109, 0x00c0, 0x1c56, 0x2009, 0x0100, 0x210c,
+ 0xa18a, 0x0003, 0x1048, 0x1328, 0x1078, 0x1f75, 0x0e7e, 0x0f7e,
+ 0x2071, 0xa5d0, 0x2079, 0x0010, 0x7004, 0xa086, 0x0000, 0x0040,
+ 0x1c9b, 0x7800, 0x007e, 0x7820, 0x007e, 0x7830, 0x007e, 0x7834,
+ 0x007e, 0x7838, 0x007e, 0x783c, 0x007e, 0x7803, 0x0004, 0x7823,
+ 0x0000, 0x0005, 0x0005, 0x2079, 0x0030, 0x7804, 0xd0ac, 0x10c0,
+ 0x1328, 0x2079, 0x0010, 0x007f, 0x783e, 0x007f, 0x783a, 0x007f,
+ 0x7836, 0x007f, 0x7832, 0x007f, 0x7822, 0x007f, 0x7802, 0x0f7f,
+ 0x0e7f, 0x0078, 0x1ca1, 0x0f7f, 0x0e7f, 0x7804, 0xd0ac, 0x10c0,
+ 0x1328, 0x1078, 0x61d3, 0x007c, 0x0e7e, 0x2071, 0xa602, 0x7003,
+ 0x0000, 0x0e7f, 0x007c, 0x0d7e, 0xa280, 0x0004, 0x206c, 0x694c,
+ 0xd1dc, 0x00c0, 0x1d26, 0x6934, 0xa184, 0x0007, 0x0079, 0x1cb8,
+ 0x1cc0, 0x1d11, 0x1cc0, 0x1cc0, 0x1cc0, 0x1cf6, 0x1cd3, 0x1cc2,
+ 0x1078, 0x1328, 0x684c, 0xd0b4, 0x0040, 0x1e34, 0x6860, 0x682e,
+ 0x6816, 0x685c, 0x682a, 0x6812, 0x687c, 0x680a, 0x6880, 0x680e,
+ 0x6958, 0x0078, 0x1d19, 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e,
+ 0x00c0, 0x1cc0, 0x684c, 0xd0b4, 0x0040, 0x1e34, 0x6860, 0x682e,
+ 0x6816, 0x685c, 0x682a, 0x6812, 0x687c, 0x680a, 0x6880, 0x680e,
+ 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080,
+ 0x2015, 0x2004, 0x6832, 0x6958, 0x0078, 0x1d22, 0xa18c, 0x00ff,
+ 0xa186, 0x0015, 0x00c0, 0x1d26, 0x684c, 0xd0b4, 0x0040, 0x1e34,
+ 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080,
+ 0x2015, 0x2004, 0x6832, 0x6958, 0xa006, 0x682e, 0x682a, 0x0078,
+ 0x1d22, 0x684c, 0xd0b4, 0x0040, 0x18bc, 0x6958, 0xa006, 0x682e,
+ 0x682a, 0x2d00, 0x681a, 0x6834, 0xa084, 0x000f, 0xa080, 0x2015,
+ 0x2004, 0x6832, 0x6926, 0x684c, 0xc0dd, 0x684e, 0x0d7f, 0x007c,
+ 0x0f7e, 0x2079, 0x0020, 0x7804, 0xd0fc, 0x10c0, 0x1e5d, 0x0e7e,
+ 0x0d7e, 0x2071, 0xa602, 0x7000, 0xa005, 0x00c0, 0x1dab, 0x0c7e,
+ 0x7206, 0xa280, 0x0004, 0x205c, 0x7004, 0x2068, 0x7803, 0x0004,
+ 0x6818, 0x0d7e, 0x2068, 0x686c, 0x7812, 0x6890, 0x0f7e, 0x20e1,
+ 0x9040, 0x2079, 0x0200, 0x781a, 0x2079, 0x0100, 0x8004, 0x78d6,
+ 0x0f7f, 0x0d7f, 0x2b68, 0x6824, 0x2050, 0x6818, 0x2060, 0x6830,
+ 0x2040, 0x6034, 0xa0cc, 0x000f, 0x6908, 0x2001, 0x04fd, 0x2004,
+ 0xa086, 0x0007, 0x0040, 0x1d6d, 0xa184, 0x0007, 0x0040, 0x1d6d,
+ 0x017e, 0x2009, 0x0008, 0xa102, 0x017f, 0xa108, 0x791a, 0x7116,
+ 0x701e, 0x680c, 0xa081, 0x0000, 0x781e, 0x701a, 0xa006, 0x700e,
+ 0x7012, 0x7004, 0x692c, 0x6814, 0xa106, 0x00c0, 0x1d84, 0x6928,
+ 0x6810, 0xa106, 0x0040, 0x1d91, 0x037e, 0x047e, 0x6b14, 0x6c10,
+ 0x1078, 0x2035, 0x047f, 0x037f, 0x0040, 0x1d91, 0x0c7f, 0x0078,
+ 0x1dab, 0x8aff, 0x00c0, 0x1d99, 0x0c7f, 0xa085, 0x0001, 0x0078,
+ 0x1dab, 0x127e, 0x2091, 0x8000, 0x2079, 0x0020, 0x2009, 0x0001,
+ 0x1078, 0x1daf, 0x0040, 0x1da8, 0x2009, 0x0001, 0x1078, 0x1daf,
+ 0x127f, 0x0c7f, 0xa006, 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x077e,
+ 0x067e, 0x057e, 0x047e, 0x037e, 0x027e, 0x8aff, 0x0040, 0x1e2d,
+ 0x700c, 0x7214, 0xa23a, 0x7010, 0x7218, 0xa203, 0x0048, 0x1e2c,
+ 0xa705, 0x0040, 0x1e2c, 0xa03e, 0x2730, 0x6850, 0xd0fc, 0x00c0,
+ 0x1ddf, 0x0d7e, 0x2804, 0xac68, 0x2900, 0x0079, 0x1dcf, 0x1e0e,
+ 0x1def, 0x1def, 0x1e0e, 0x1e0e, 0x1e06, 0x1e0e, 0x1def, 0x1e0e,
+ 0x1df5, 0x1df5, 0x1e0e, 0x1e0e, 0x1e0e, 0x1dfd, 0x1df5, 0xc0fc,
+ 0x6852, 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0xd99c, 0x0040, 0x1e12,
+ 0x0d7e, 0x2804, 0xac68, 0x6f08, 0x6e0c, 0x0078, 0x1e11, 0x6b08,
+ 0x6a0c, 0x6d00, 0x6c04, 0x0078, 0x1e11, 0x6b10, 0x6a14, 0x6d00,
+ 0x6c04, 0x6f08, 0x6e0c, 0x0078, 0x1e11, 0x0d7f, 0x0d7e, 0x6834,
+ 0xa084, 0x00ff, 0xa086, 0x001e, 0x00c0, 0x1e0e, 0x0d7f, 0x1078,
+ 0x1fd1, 0x00c0, 0x1db5, 0xa00e, 0x0078, 0x1e2d, 0x0d7f, 0x1078,
+ 0x1328, 0x0d7f, 0x7b22, 0x7a26, 0x7d32, 0x7c36, 0x7f3a, 0x7e3e,
+ 0x7902, 0x7000, 0x8000, 0x7002, 0x6828, 0xa300, 0x682a, 0x682c,
+ 0xa201, 0x682e, 0x700c, 0xa300, 0x700e, 0x7010, 0xa201, 0x7012,
+ 0x1078, 0x1fd1, 0x0078, 0x1e2d, 0xa006, 0x027f, 0x037f, 0x047f,
+ 0x057f, 0x067f, 0x077f, 0x007c, 0x1078, 0x1328, 0x027e, 0x2001,
+ 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0x7004, 0x2060, 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8a44,
+ 0x0040, 0x1e4d, 0x6850, 0xc0bd, 0x6852, 0x0d7f, 0x1078, 0x8758,
+ 0x20e1, 0x9040, 0x1078, 0x719a, 0x2011, 0x0000, 0x1078, 0x6efc,
+ 0x1078, 0x61d3, 0x027f, 0x0078, 0x1f29, 0x127e, 0x2091, 0x2200,
+ 0x007e, 0x017e, 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x2079, 0x0020,
+ 0x2071, 0xa602, 0x2b68, 0x6818, 0x2060, 0x7904, 0x7803, 0x0002,
+ 0xa184, 0x0700, 0x00c0, 0x1e36, 0x7000, 0x0079, 0x1e77, 0x1f29,
+ 0x1e7b, 0x1ef6, 0x1f27, 0x8001, 0x7002, 0xd19c, 0x00c0, 0x1e8f,
+ 0x8aff, 0x0040, 0x1eae, 0x2009, 0x0001, 0x1078, 0x1daf, 0x0040,
+ 0x1f29, 0x2009, 0x0001, 0x1078, 0x1daf, 0x0078, 0x1f29, 0x7803,
+ 0x0004, 0xd194, 0x0040, 0x1e9f, 0x6850, 0xc0fc, 0x6852, 0x8aff,
+ 0x00c0, 0x1ea4, 0x684c, 0xc0f5, 0x684e, 0x0078, 0x1ea4, 0x1078,
+ 0x1fea, 0x6850, 0xc0fd, 0x6852, 0x2a00, 0x6826, 0x2c00, 0x681a,
+ 0x2800, 0x6832, 0x7003, 0x0000, 0x0078, 0x1f29, 0x711c, 0x81ff,
+ 0x0040, 0x1ec4, 0x7918, 0x7922, 0x7827, 0x0000, 0x7803, 0x0001,
+ 0x7000, 0x8000, 0x7002, 0x700c, 0xa100, 0x700e, 0x7010, 0xa081,
+ 0x0000, 0x7012, 0x0078, 0x1f29, 0x0f7e, 0x027e, 0x781c, 0x007e,
+ 0x7818, 0x007e, 0x2079, 0x0100, 0x7a14, 0xa284, 0x0004, 0xa085,
+ 0x0012, 0x7816, 0x037e, 0x2019, 0x1000, 0x8319, 0x1040, 0x1328,
+ 0x7820, 0xd0bc, 0x00c0, 0x1ed5, 0x037f, 0x79c8, 0x007f, 0xa102,
+ 0x017f, 0x007e, 0x017e, 0x79c4, 0x007f, 0xa103, 0x78c6, 0x007f,
+ 0x78ca, 0xa284, 0x0004, 0xa085, 0x0012, 0x7816, 0x027f, 0x0f7f,
+ 0x7803, 0x0008, 0x7003, 0x0000, 0x0078, 0x1f29, 0x8001, 0x7002,
+ 0xd194, 0x0040, 0x1f0b, 0x7804, 0xd0fc, 0x00c0, 0x1e6d, 0xd19c,
+ 0x00c0, 0x1f25, 0x8aff, 0x0040, 0x1f29, 0x2009, 0x0001, 0x1078,
+ 0x1daf, 0x0078, 0x1f29, 0x027e, 0x037e, 0x6b28, 0x6a2c, 0x1078,
+ 0x1fea, 0x0d7e, 0x2804, 0xac68, 0x6034, 0xd09c, 0x00c0, 0x1f1e,
+ 0x6808, 0xa31a, 0x680c, 0xa213, 0x0078, 0x1f22, 0x6810, 0xa31a,
+ 0x6814, 0xa213, 0x0d7f, 0x0078, 0x1e9f, 0x0078, 0x1e9f, 0x1078,
+ 0x1328, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x017f, 0x007f, 0x127f,
+ 0x007c, 0x0f7e, 0x0e7e, 0x2071, 0xa602, 0x7000, 0xa086, 0x0000,
+ 0x0040, 0x1f72, 0x2079, 0x0020, 0x017e, 0x2009, 0x0207, 0x210c,
+ 0xd194, 0x0040, 0x1f4f, 0x2009, 0x020c, 0x210c, 0xa184, 0x0003,
+ 0x0040, 0x1f4f, 0x20e1, 0x9040, 0x2001, 0x020c, 0x2102, 0x2009,
+ 0x0206, 0x2104, 0x2009, 0x0203, 0x210c, 0xa106, 0x00c0, 0x1f5a,
+ 0x20e1, 0x9040, 0x7804, 0xd0fc, 0x0040, 0x1f3d, 0x1078, 0x1e5d,
+ 0x7000, 0xa086, 0x0000, 0x00c0, 0x1f3d, 0x017f, 0x7803, 0x0004,
+ 0x7804, 0xd0ac, 0x00c0, 0x1f68, 0x20e1, 0x9040, 0x7803, 0x0002,
+ 0x7003, 0x0000, 0x0e7f, 0x0f7f, 0x007c, 0x027e, 0x0c7e, 0x0d7e,
+ 0x0e7e, 0x0f7e, 0x2071, 0xa602, 0x2079, 0x0020, 0x7000, 0xa086,
+ 0x0000, 0x0040, 0x1fae, 0x7004, 0x2060, 0x6010, 0x2068, 0x1078,
+ 0x8a44, 0x0040, 0x1f98, 0x6850, 0xc0b5, 0x6852, 0x680c, 0x7a1c,
+ 0xa206, 0x00c0, 0x1f98, 0x6808, 0x7a18, 0xa206, 0x0040, 0x1fb4,
+ 0x2001, 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0x7004, 0x2060, 0x1078, 0x8758, 0x20e1, 0x9040,
+ 0x1078, 0x719a, 0x2011, 0x0000, 0x1078, 0x6efc, 0x0f7f, 0x0e7f,
+ 0x0d7f, 0x0c7f, 0x027f, 0x007c, 0x6810, 0x6a14, 0xa205, 0x00c0,
+ 0x1f98, 0x684c, 0xc0dc, 0x684e, 0x2c10, 0x1078, 0x1cab, 0x2001,
+ 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0x2069, 0xa5ab, 0x6833, 0x0000, 0x683f, 0x0000, 0x0078,
+ 0x1fae, 0x8840, 0x2804, 0xa005, 0x00c0, 0x1fe5, 0x6004, 0xa005,
+ 0x0040, 0x1fe7, 0x681a, 0x2060, 0x6034, 0xa084, 0x000f, 0xa080,
+ 0x2015, 0x2044, 0x88ff, 0x1040, 0x1328, 0x8a51, 0x007c, 0x2051,
+ 0x0000, 0x007c, 0x8a50, 0x8841, 0x2804, 0xa005, 0x00c0, 0x2004,
+ 0x2c00, 0xad06, 0x0040, 0x1ff9, 0x6000, 0xa005, 0x00c0, 0x1ff9,
+ 0x2d00, 0x2060, 0x681a, 0x6034, 0xa084, 0x000f, 0xa080, 0x2025,
+ 0x2044, 0x88ff, 0x1040, 0x1328, 0x007c, 0x0000, 0x0011, 0x0015,
+ 0x0019, 0x001d, 0x0021, 0x0025, 0x0029, 0x0000, 0x000f, 0x0015,
+ 0x001b, 0x0021, 0x0027, 0x0000, 0x0000, 0x0000, 0x200a, 0x2006,
+ 0x0000, 0x0000, 0x2014, 0x0000, 0x200a, 0x0000, 0x2011, 0x200e,
+ 0x0000, 0x0000, 0x0000, 0x2014, 0x2011, 0x0000, 0x200c, 0x200c,
+ 0x0000, 0x0000, 0x2014, 0x0000, 0x200c, 0x0000, 0x2012, 0x2012,
+ 0x0000, 0x0000, 0x0000, 0x2014, 0x2012, 0x0a7e, 0x097e, 0x087e,
+ 0x6b2e, 0x6c2a, 0x6858, 0xa055, 0x0040, 0x20d8, 0x2d60, 0x6034,
+ 0xa0cc, 0x000f, 0xa9c0, 0x2015, 0xa986, 0x0007, 0x0040, 0x2050,
+ 0xa986, 0x000e, 0x0040, 0x2050, 0xa986, 0x000f, 0x00c0, 0x2054,
+ 0x605c, 0xa422, 0x6060, 0xa31a, 0x2804, 0xa045, 0x00c0, 0x2062,
+ 0x0050, 0x205c, 0x0078, 0x20d8, 0x6004, 0xa065, 0x0040, 0x20d8,
+ 0x0078, 0x203f, 0x2804, 0xa005, 0x0040, 0x2080, 0xac68, 0xd99c,
+ 0x00c0, 0x2070, 0x6808, 0xa422, 0x680c, 0xa31b, 0x0078, 0x2074,
+ 0x6810, 0xa422, 0x6814, 0xa31b, 0x0048, 0x209f, 0x2300, 0xa405,
+ 0x0040, 0x2086, 0x8a51, 0x0040, 0x20d8, 0x8840, 0x0078, 0x2062,
+ 0x6004, 0xa065, 0x0040, 0x20d8, 0x0078, 0x203f, 0x8a51, 0x0040,
+ 0x20d8, 0x8840, 0x2804, 0xa005, 0x00c0, 0x2099, 0x6004, 0xa065,
+ 0x0040, 0x20d8, 0x6034, 0xa0cc, 0x000f, 0xa9c0, 0x2015, 0x2804,
+ 0x2040, 0x2b68, 0x6850, 0xc0fc, 0x6852, 0x0078, 0x20cc, 0x8422,
+ 0x8420, 0x831a, 0xa399, 0x0000, 0x0d7e, 0x2b68, 0x6c6e, 0x6b72,
+ 0x0d7f, 0xd99c, 0x00c0, 0x20ba, 0x6908, 0x2400, 0xa122, 0x690c,
+ 0x2300, 0xa11b, 0x1048, 0x1328, 0x6800, 0xa420, 0x6804, 0xa319,
+ 0x0078, 0x20c6, 0x6910, 0x2400, 0xa122, 0x6914, 0x2300, 0xa11b,
+ 0x1048, 0x1328, 0x6800, 0xa420, 0x6804, 0xa319, 0x2b68, 0x6c1e,
+ 0x6b22, 0x6850, 0xc0fd, 0x6852, 0x2c00, 0x681a, 0x2800, 0x6832,
+ 0x2a00, 0x6826, 0x007f, 0x007f, 0x007f, 0xa006, 0x0078, 0x20dd,
+ 0x087f, 0x097f, 0x0a7f, 0xa085, 0x0001, 0x007c, 0x2001, 0x0005,
+ 0x2004, 0xa084, 0x0007, 0x0079, 0x20e5, 0x20ed, 0x20ee, 0x20f1,
+ 0x20f4, 0x20f9, 0x20fc, 0x2101, 0x2106, 0x007c, 0x1078, 0x1e5d,
+ 0x007c, 0x1078, 0x18e2, 0x007c, 0x1078, 0x18e2, 0x1078, 0x1e5d,
+ 0x007c, 0x1078, 0x14b0, 0x007c, 0x1078, 0x1e5d, 0x1078, 0x14b0,
+ 0x007c, 0x1078, 0x18e2, 0x1078, 0x14b0, 0x007c, 0x1078, 0x18e2,
+ 0x1078, 0x1e5d, 0x1078, 0x14b0, 0x007c, 0x127e, 0x2091, 0x2300,
+ 0x2079, 0x0200, 0x2071, 0xa880, 0x2069, 0xa300, 0x2009, 0x0004,
+ 0x7912, 0x7817, 0x0004, 0x1078, 0x24b5, 0x781b, 0x0002, 0x20e1,
+ 0x8700, 0x127f, 0x007c, 0x127e, 0x2091, 0x2300, 0x781c, 0xa084,
+ 0x0007, 0x0079, 0x212b, 0x214f, 0x2133, 0x2137, 0x213b, 0x2141,
+ 0x2145, 0x2149, 0x214d, 0x1078, 0x5372, 0x0078, 0x214f, 0x1078,
+ 0x53b3, 0x0078, 0x214f, 0x1078, 0x5372, 0x1078, 0x53b3, 0x0078,
+ 0x214f, 0x1078, 0x2151, 0x0078, 0x214f, 0x1078, 0x2151, 0x0078,
+ 0x214f, 0x1078, 0x2151, 0x0078, 0x214f, 0x1078, 0x2151, 0x127f,
+ 0x007c, 0x007e, 0x017e, 0x027e, 0x7930, 0xa184, 0x0003, 0x0040,
+ 0x215d, 0x20e1, 0x9040, 0x0078, 0x2186, 0xa184, 0x0030, 0x0040,
+ 0x216e, 0x6a00, 0xa286, 0x0003, 0x00c0, 0x2168, 0x0078, 0x216a,
+ 0x1078, 0x4171, 0x20e1, 0x9010, 0x0078, 0x2186, 0xa184, 0x00c0,
+ 0x0040, 0x2180, 0x0e7e, 0x037e, 0x047e, 0x057e, 0x2071, 0xa5e1,
+ 0x1078, 0x1ac6, 0x057f, 0x047f, 0x037f, 0x0e7f, 0x0078, 0x2186,
+ 0xa184, 0x0300, 0x0040, 0x2186, 0x20e1, 0x9020, 0x7932, 0x027f,
+ 0x017f, 0x007f, 0x007c, 0x017e, 0x0e7e, 0x0f7e, 0x2071, 0xa300,
+ 0x7128, 0x2001, 0xa58f, 0x2102, 0x2001, 0xa597, 0x2102, 0xa182,
+ 0x0211, 0x00c8, 0x219f, 0x2009, 0x0008, 0x0078, 0x21c9, 0xa182,
+ 0x0259, 0x00c8, 0x21a7, 0x2009, 0x0007, 0x0078, 0x21c9, 0xa182,
+ 0x02c1, 0x00c8, 0x21af, 0x2009, 0x0006, 0x0078, 0x21c9, 0xa182,
+ 0x0349, 0x00c8, 0x21b7, 0x2009, 0x0005, 0x0078, 0x21c9, 0xa182,
+ 0x0421, 0x00c8, 0x21bf, 0x2009, 0x0004, 0x0078, 0x21c9, 0xa182,
+ 0x0581, 0x00c8, 0x21c7, 0x2009, 0x0003, 0x0078, 0x21c9, 0x2009,
+ 0x0002, 0x2079, 0x0200, 0x7912, 0x7817, 0x0004, 0x1078, 0x24b5,
+ 0x0f7f, 0x0e7f, 0x017f, 0x007c, 0x127e, 0x2091, 0x2200, 0x2061,
+ 0x0100, 0x2071, 0xa300, 0x6024, 0x6026, 0x6053, 0x0030, 0x6033,
+ 0x00ef, 0x60e7, 0x0000, 0x60eb, 0x00ef, 0x60e3, 0x0008, 0x604b,
+ 0xf7f7, 0x6043, 0x0000, 0x602f, 0x0080, 0x602f, 0x0000, 0x6007,
+ 0x0eaf, 0x600f, 0x00ff, 0x602b, 0x002f, 0x127f, 0x007c, 0x2001,
+ 0xa32f, 0x2003, 0x0000, 0x2001, 0xa32e, 0x2003, 0x0001, 0x007c,
+ 0x127e, 0x2091, 0x2200, 0x007e, 0x017e, 0x027e, 0x6124, 0xa184,
+ 0x002c, 0x00c0, 0x220f, 0xa184, 0x0007, 0x0079, 0x2215, 0xa195,
+ 0x0004, 0xa284, 0x0007, 0x0079, 0x2215, 0x2241, 0x221d, 0x2221,
+ 0x2225, 0x222b, 0x222f, 0x2235, 0x223b, 0x1078, 0x5ad2, 0x0078,
+ 0x2241, 0x1078, 0x5bc1, 0x0078, 0x2241, 0x1078, 0x5bc1, 0x1078,
+ 0x5ad2, 0x0078, 0x2241, 0x1078, 0x2246, 0x0078, 0x2241, 0x1078,
+ 0x5ad2, 0x1078, 0x2246, 0x0078, 0x2241, 0x1078, 0x5bc1, 0x1078,
+ 0x2246, 0x0078, 0x2241, 0x1078, 0x5bc1, 0x1078, 0x5ad2, 0x1078,
+ 0x2246, 0x027f, 0x017f, 0x007f, 0x127f, 0x007c, 0x6124, 0xd1ac,
+ 0x0040, 0x2342, 0x017e, 0x047e, 0x0c7e, 0x644c, 0xa486, 0xf0f0,
+ 0x00c0, 0x2259, 0x2061, 0x0100, 0x644a, 0x6043, 0x0090, 0x6043,
+ 0x0010, 0x74c2, 0xa48c, 0xff00, 0x7034, 0xd084, 0x0040, 0x2271,
+ 0xa186, 0xf800, 0x00c0, 0x2271, 0x7038, 0xd084, 0x00c0, 0x2271,
+ 0xc085, 0x703a, 0x037e, 0x2418, 0x2011, 0x8016, 0x1078, 0x3579,
+ 0x037f, 0xa196, 0xff00, 0x0040, 0x22b3, 0x6030, 0xa084, 0x00ff,
+ 0x810f, 0xa116, 0x0040, 0x22b3, 0x7130, 0xd184, 0x00c0, 0x22b3,
+ 0x2011, 0xa352, 0x2214, 0xd2ec, 0x0040, 0x228e, 0xc18d, 0x7132,
+ 0x2011, 0xa352, 0x2214, 0xd2ac, 0x00c0, 0x22b3, 0x6240, 0xa294,
+ 0x0010, 0x0040, 0x229a, 0x6248, 0xa294, 0xff00, 0xa296, 0xff00,
+ 0x0040, 0x22b3, 0x7030, 0xd08c, 0x0040, 0x2305, 0x7034, 0xd08c,
+ 0x00c0, 0x22aa, 0x2001, 0xa30c, 0x200c, 0xd1ac, 0x00c0, 0x2305,
+ 0xc1ad, 0x2102, 0x037e, 0x73c0, 0x2011, 0x8013, 0x1078, 0x3579,
+ 0x037f, 0x0078, 0x2305, 0x7034, 0xd08c, 0x00c0, 0x22bf, 0x2001,
+ 0xa30c, 0x200c, 0xd1ac, 0x00c0, 0x2305, 0xc1ad, 0x2102, 0x037e,
+ 0x73c0, 0x2011, 0x8013, 0x1078, 0x3579, 0x037f, 0x7130, 0xc185,
+ 0x7132, 0x2011, 0xa352, 0x220c, 0xd1a4, 0x0040, 0x22e9, 0x017e,
+ 0x2009, 0x0001, 0x2011, 0x0100, 0x1078, 0x5a6d, 0x2019, 0x000e,
+ 0x1078, 0x9e3b, 0xa484, 0x00ff, 0xa080, 0x293f, 0x200c, 0xa18c,
+ 0xff00, 0x810f, 0x8127, 0xa006, 0x2009, 0x000e, 0x1078, 0x9ec0,
+ 0x017f, 0xd1ac, 0x00c0, 0x22f6, 0x017e, 0x2009, 0x0000, 0x2019,
+ 0x0004, 0x1078, 0x27e2, 0x017f, 0x0078, 0x2305, 0x157e, 0x20a9,
+ 0x007f, 0x2009, 0x0000, 0x1078, 0x4501, 0x00c0, 0x2301, 0x1078,
+ 0x4235, 0x8108, 0x00f0, 0x22fb, 0x157f, 0x0c7f, 0x047f, 0x0f7e,
+ 0x2079, 0xa5be, 0x783c, 0xa086, 0x0000, 0x0040, 0x2317, 0x6027,
+ 0x0004, 0x783f, 0x0000, 0x2079, 0x0140, 0x7803, 0x0000, 0x0f7f,
+ 0x2011, 0x0003, 0x1078, 0x6ef2, 0x2011, 0x0002, 0x1078, 0x6efc,
+ 0x1078, 0x6dda, 0x1078, 0x595a, 0x037e, 0x2019, 0x0000, 0x1078,
+ 0x6e6c, 0x037f, 0x60e3, 0x0000, 0x017f, 0x2001, 0xa300, 0x2014,
+ 0xa296, 0x0004, 0x00c0, 0x233a, 0xd19c, 0x00c0, 0x233a, 0x6228,
+ 0xc29d, 0x622a, 0x2003, 0x0001, 0x2001, 0xa321, 0x2003, 0x0000,
+ 0x6027, 0x0020, 0xd194, 0x0040, 0x2426, 0x0f7e, 0x2079, 0xa5be,
+ 0x783c, 0xa086, 0x0001, 0x00c0, 0x2366, 0x017e, 0x6027, 0x0004,
+ 0x783f, 0x0000, 0x2079, 0x0140, 0x7803, 0x1000, 0x7803, 0x0000,
+ 0x2079, 0xa5ab, 0x7807, 0x0000, 0x7833, 0x0000, 0x1078, 0x6109,
+ 0x1078, 0x61d3, 0x017f, 0x0f7f, 0x0078, 0x2426, 0x0f7f, 0x017e,
+ 0x3900, 0xa082, 0xa6cd, 0x00c8, 0x2371, 0x017e, 0x1078, 0x728a,
+ 0x017f, 0x6220, 0xd2b4, 0x0040, 0x23dc, 0x1078, 0x595a, 0x1078,
+ 0x6c41, 0x6027, 0x0004, 0x0f7e, 0x2019, 0xa5b4, 0x2304, 0xa07d,
+ 0x0040, 0x23b2, 0x7804, 0xa086, 0x0032, 0x00c0, 0x23b2, 0x0d7e,
+ 0x0c7e, 0x0e7e, 0x2069, 0x0140, 0x618c, 0x6288, 0x7818, 0x608e,
+ 0x7808, 0x608a, 0x6043, 0x0002, 0x2001, 0x0003, 0x8001, 0x00c0,
+ 0x2396, 0x6043, 0x0000, 0x6803, 0x1000, 0x6803, 0x0000, 0x618e,
+ 0x628a, 0x1078, 0x6010, 0x1078, 0x6109, 0x7810, 0x2070, 0x7037,
+ 0x0103, 0x2f60, 0x1078, 0x753d, 0x0e7f, 0x0c7f, 0x0d7f, 0x0f7f,
+ 0x017f, 0x007c, 0x0f7f, 0x0d7e, 0x2069, 0x0140, 0x6804, 0xa084,
+ 0x4000, 0x0040, 0x23bf, 0x6803, 0x1000, 0x6803, 0x0000, 0x0d7f,
+ 0x0c7e, 0x2061, 0xa5ab, 0x6028, 0xa09a, 0x00c8, 0x00c8, 0x23cf,
+ 0x8000, 0x602a, 0x0c7f, 0x1078, 0x6c33, 0x0078, 0x2425, 0x2019,
+ 0xa5b4, 0x2304, 0xa065, 0x0040, 0x23d9, 0x2009, 0x0027, 0x1078,
+ 0x756c, 0x0c7f, 0x0078, 0x2425, 0xd2bc, 0x0040, 0x2425, 0x1078,
+ 0x5967, 0x6017, 0x0010, 0x6027, 0x0004, 0x0d7e, 0x2069, 0x0140,
+ 0x6804, 0xa084, 0x4000, 0x0040, 0x23f1, 0x6803, 0x1000, 0x6803,
+ 0x0000, 0x0d7f, 0x0c7e, 0x2061, 0xa5ab, 0x6044, 0xa09a, 0x00c8,
+ 0x00c8, 0x2414, 0x8000, 0x6046, 0x603c, 0x0c7f, 0xa005, 0x0040,
+ 0x2425, 0x2009, 0x07d0, 0x1078, 0x595f, 0xa080, 0x0007, 0x2004,
+ 0xa086, 0x0006, 0x00c0, 0x2410, 0x6017, 0x0012, 0x0078, 0x2425,
+ 0x6017, 0x0016, 0x0078, 0x2425, 0x037e, 0x2019, 0x0001, 0x1078,
+ 0x6e6c, 0x037f, 0x2019, 0xa5ba, 0x2304, 0xa065, 0x0040, 0x2424,
+ 0x2009, 0x004f, 0x1078, 0x756c, 0x0c7f, 0x017f, 0xd19c, 0x0040,
+ 0x247c, 0x7034, 0xd0ac, 0x00c0, 0x2457, 0x017e, 0x157e, 0x6027,
+ 0x0008, 0x602f, 0x0020, 0x20a9, 0x000a, 0x00f0, 0x2435, 0x602f,
+ 0x0000, 0x6150, 0xa185, 0x1400, 0x6052, 0x20a9, 0x0320, 0x00e0,
+ 0x243f, 0x2091, 0x6000, 0x6020, 0xd09c, 0x00c0, 0x244e, 0x157f,
+ 0x6152, 0x017f, 0x6027, 0x0008, 0x0078, 0x247c, 0x1078, 0x250d,
+ 0x00f0, 0x243f, 0x157f, 0x6152, 0x017f, 0x6027, 0x0008, 0x017e,
+ 0x6028, 0xc09c, 0x602a, 0x2011, 0x0003, 0x1078, 0x6ef2, 0x2011,
+ 0x0002, 0x1078, 0x6efc, 0x1078, 0x6dda, 0x1078, 0x595a, 0x037e,
+ 0x2019, 0x0000, 0x1078, 0x6e6c, 0x037f, 0x60e3, 0x0000, 0x1078,
+ 0xa22a, 0x1078, 0xa248, 0x2001, 0xa300, 0x2003, 0x0004, 0x6027,
+ 0x0008, 0x1078, 0x1246, 0x017f, 0xa18c, 0xffd0, 0x6126, 0x007c,
+ 0x007e, 0x017e, 0x027e, 0x0e7e, 0x0f7e, 0x127e, 0x2091, 0x8000,
+ 0x2071, 0xa300, 0x71b8, 0x70ba, 0xa116, 0x0040, 0x24ae, 0x81ff,
+ 0x0040, 0x2498, 0x2011, 0x8011, 0x1078, 0x3579, 0x0078, 0x24ae,
+ 0x2011, 0x8012, 0x1078, 0x3579, 0x2001, 0xa371, 0x2004, 0xd0fc,
+ 0x00c0, 0x24ae, 0x037e, 0x0c7e, 0x2061, 0x0100, 0x2019, 0x0028,
+ 0x2009, 0x0000, 0x1078, 0x27e2, 0x0c7f, 0x037f, 0x127f, 0x0f7f,
+ 0x0e7f, 0x027f, 0x017f, 0x007f, 0x007c, 0x0c7e, 0x0f7e, 0x007e,
+ 0x027e, 0x2061, 0x0100, 0xa190, 0x24d1, 0x2204, 0x60f2, 0x2011,
+ 0x24de, 0x6000, 0xa082, 0x0003, 0x00c8, 0x24ca, 0x2001, 0x00ff,
+ 0x0078, 0x24cb, 0x2204, 0x60ee, 0x027f, 0x007f, 0x0f7f, 0x0c7f,
+ 0x007c, 0x0840, 0x0840, 0x0840, 0x0580, 0x0420, 0x0348, 0x02c0,
+ 0x0258, 0x0210, 0x01a8, 0x01a8, 0x01a8, 0x01a8, 0x0140, 0x00f8,
+ 0x00d0, 0x00b0, 0x00a0, 0x2028, 0xa18c, 0x00ff, 0x2130, 0xa094,
+ 0xff00, 0x00c0, 0x24ee, 0x81ff, 0x0040, 0x24f2, 0x1078, 0x5623,
+ 0x0078, 0x24f9, 0xa080, 0x293f, 0x200c, 0xa18c, 0xff00, 0x810f,
+ 0xa006, 0x007c, 0xa080, 0x293f, 0x200c, 0xa18c, 0x00ff, 0x007c,
+ 0x0c7e, 0x2061, 0xa300, 0x6030, 0x0040, 0x2509, 0xc09d, 0x0078,
+ 0x250a, 0xc09c, 0x6032, 0x0c7f, 0x007c, 0x007e, 0x157e, 0x0f7e,
+ 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854, 0xd08c, 0x00c0, 0x251a,
+ 0x00f0, 0x2514, 0x0f7f, 0x157f, 0x007f, 0x007c, 0x0c7e, 0x007e,
+ 0x2061, 0x0100, 0x6030, 0x007e, 0x6048, 0x007e, 0x60e4, 0x007e,
+ 0x60e8, 0x007e, 0x6050, 0x007e, 0x60f0, 0x007e, 0x60ec, 0x007e,
+ 0x600c, 0x007e, 0x6004, 0x007e, 0x6028, 0x007e, 0x60e0, 0x007e,
+ 0x602f, 0x0100, 0x602f, 0x0000, 0x0005, 0x0005, 0x0005, 0x0005,
+ 0x602f, 0x0040, 0x602f, 0x0000, 0x007f, 0x60e2, 0x007f, 0x602a,
+ 0x007f, 0x6006, 0x007f, 0x600e, 0x007f, 0x60ee, 0x007f, 0x60f2,
+ 0x007f, 0x6052, 0x007f, 0x60ea, 0x007f, 0x60e6, 0x007f, 0x604a,
+ 0x007f, 0x6032, 0x007f, 0x0c7f, 0x007c, 0x257d, 0x2581, 0x2585,
+ 0x258b, 0x2591, 0x2597, 0x259d, 0x25a5, 0x25ad, 0x25b3, 0x25b9,
+ 0x25c1, 0x25c9, 0x25d1, 0x25d9, 0x25e3, 0x25ed, 0x25ed, 0x25ed,
+ 0x25ed, 0x25ed, 0x25ed, 0x25ed, 0x25ed, 0x25ed, 0x25ed, 0x25ed,
+ 0x25ed, 0x25ed, 0x25ed, 0x25ed, 0x25ed, 0x107e, 0x007e, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078,
+ 0x2200, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078, 0x2200, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x1078, 0x20de, 0x0078, 0x2606, 0x107e,
+ 0x007e, 0x1078, 0x20de, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078,
+ 0x2200, 0x1078, 0x20de, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078,
+ 0x2200, 0x1078, 0x20de, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078,
+ 0x2123, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078, 0x2123, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x1078, 0x2200, 0x1078, 0x2123, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x1078, 0x2200, 0x1078, 0x2123, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x1078, 0x20de, 0x1078, 0x2123, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x1078, 0x20de, 0x1078, 0x2123, 0x0078,
+ 0x2606, 0x107e, 0x007e, 0x1078, 0x2200, 0x1078, 0x20de, 0x1078,
+ 0x2123, 0x0078, 0x2606, 0x107e, 0x007e, 0x1078, 0x2200, 0x1078,
+ 0x20de, 0x1078, 0x2123, 0x0078, 0x2606, 0x0005, 0x0078, 0x25ed,
+ 0xb084, 0x003c, 0x8004, 0x8004, 0x0079, 0x25f6, 0x2606, 0x2583,
+ 0x2587, 0x258d, 0x2593, 0x2599, 0x259f, 0x25a7, 0x25af, 0x25b5,
+ 0x25bb, 0x25c3, 0x25cb, 0x25d3, 0x25db, 0x25e5, 0x0008, 0x25f0,
+ 0x007f, 0x107f, 0x2091, 0x8001, 0x007c, 0x0c7e, 0x027e, 0x047e,
+ 0x2021, 0x0000, 0x1078, 0x4897, 0x00c0, 0x2705, 0x70c8, 0xd09c,
+ 0x0040, 0x2624, 0xd084, 0x00c0, 0x2624, 0xd0bc, 0x00c0, 0x2705,
+ 0x1078, 0x2709, 0x0078, 0x2705, 0xd094, 0x0040, 0x262b, 0x7093,
+ 0xffff, 0x0078, 0x2705, 0x2001, 0x010c, 0x203c, 0x7280, 0xd284,
+ 0x0040, 0x2694, 0xd28c, 0x00c0, 0x2694, 0x037e, 0x7390, 0xa38e,
+ 0xffff, 0x0040, 0x263e, 0x83ff, 0x00c0, 0x2640, 0x2019, 0x0001,
+ 0x8314, 0xa2e0, 0xa9c0, 0x2c04, 0xa38c, 0x0001, 0x0040, 0x264d,
+ 0xa084, 0xff00, 0x8007, 0x0078, 0x264f, 0xa084, 0x00ff, 0xa70e,
+ 0x0040, 0x2689, 0xa08e, 0x0000, 0x0040, 0x2689, 0xa08e, 0x00ff,
+ 0x00c0, 0x2666, 0x7230, 0xd284, 0x00c0, 0x268f, 0x7280, 0xc28d,
+ 0x7282, 0x7093, 0xffff, 0x037f, 0x0078, 0x2694, 0x2009, 0x0000,
+ 0x1078, 0x24e3, 0x1078, 0x4499, 0x00c0, 0x268c, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x00c0, 0x2683, 0x7030, 0xd08c, 0x0040,
+ 0x267d, 0x6000, 0xd0bc, 0x0040, 0x2683, 0x1078, 0x271f, 0x0040,
+ 0x268c, 0x0078, 0x2689, 0x1078, 0x2857, 0x1078, 0x274c, 0x0040,
+ 0x268c, 0x8318, 0x0078, 0x2640, 0x7392, 0x0078, 0x2691, 0x7093,
+ 0xffff, 0x037f, 0x0078, 0x2705, 0xa780, 0x293f, 0x203c, 0xa7bc,
+ 0xff00, 0x873f, 0x2041, 0x007e, 0x7090, 0xa096, 0xffff, 0x00c0,
+ 0x26a6, 0x2009, 0x0000, 0x28a8, 0x0078, 0x26b2, 0xa812, 0x0048,
+ 0x26ae, 0x2008, 0xa802, 0x20a8, 0x0078, 0x26b2, 0x7093, 0xffff,
+ 0x0078, 0x2705, 0x2700, 0x157e, 0x017e, 0xa106, 0x0040, 0x26f9,
+ 0xc484, 0x1078, 0x4501, 0x0040, 0x26c3, 0x1078, 0x4499, 0x00c0,
+ 0x2702, 0x0078, 0x26c4, 0xc485, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x00c0, 0x26d3, 0x7030, 0xd08c, 0x0040, 0x26f1, 0x6000,
+ 0xd0bc, 0x00c0, 0x26f1, 0x7280, 0xd28c, 0x0040, 0x26e9, 0x6004,
+ 0xa084, 0x00ff, 0xa082, 0x0006, 0x0048, 0x26f9, 0xd484, 0x00c0,
+ 0x26e5, 0x1078, 0x44bc, 0x0078, 0x26e7, 0x1078, 0x2921, 0x0078,
+ 0x26f9, 0x1078, 0x2857, 0x1078, 0x274c, 0x0040, 0x2702, 0x0078,
+ 0x26f9, 0x1078, 0x28ec, 0x0040, 0x26f9, 0x1078, 0x271f, 0x0040,
+ 0x2702, 0x017f, 0x8108, 0x157f, 0x00f0, 0x26b2, 0x7093, 0xffff,
+ 0x0078, 0x2705, 0x017f, 0x157f, 0x7192, 0x047f, 0x027f, 0x0c7f,
+ 0x007c, 0x0c7e, 0x017e, 0x7093, 0x0000, 0x2009, 0x007e, 0x1078,
+ 0x4499, 0x00c0, 0x271c, 0x1078, 0x2857, 0x1078, 0x274c, 0x0040,
+ 0x271c, 0x70c8, 0xc0bd, 0x70ca, 0x017f, 0x0c7f, 0x007c, 0x017e,
+ 0x077e, 0x0d7e, 0x0c7e, 0x2c68, 0x2001, 0xa356, 0x2004, 0xa084,
+ 0x00ff, 0x6842, 0x1078, 0x74d7, 0x0040, 0x2747, 0x2d00, 0x601a,
+ 0x601f, 0x0001, 0x2001, 0x0000, 0x1078, 0x442b, 0x2001, 0x0000,
+ 0x1078, 0x443f, 0x127e, 0x2091, 0x8000, 0x708c, 0x8000, 0x708e,
+ 0x127f, 0x2009, 0x0004, 0x1078, 0x756c, 0xa085, 0x0001, 0x0c7f,
+ 0x0d7f, 0x077f, 0x017f, 0x007c, 0x017e, 0x077e, 0x0d7e, 0x0c7e,
+ 0x2c68, 0x2001, 0xa356, 0x2004, 0xa084, 0x00ff, 0x6842, 0x1078,
+ 0x74d7, 0x0040, 0x2785, 0x2d00, 0x601a, 0x6800, 0xc0c4, 0x6802,
+ 0x68a0, 0xa086, 0x007e, 0x0040, 0x276e, 0x6804, 0xa084, 0x00ff,
+ 0xa086, 0x0006, 0x00c0, 0x276e, 0x1078, 0x2813, 0x601f, 0x0001,
+ 0x2001, 0x0000, 0x1078, 0x442b, 0x2001, 0x0002, 0x1078, 0x443f,
+ 0x127e, 0x2091, 0x8000, 0x708c, 0x8000, 0x708e, 0x127f, 0x2009,
+ 0x0002, 0x1078, 0x756c, 0xa085, 0x0001, 0x0c7f, 0x0d7f, 0x077f,
+ 0x017f, 0x007c, 0x0c7e, 0x027e, 0x2009, 0x0080, 0x1078, 0x4499,
+ 0x00c0, 0x2798, 0x1078, 0x279b, 0x0040, 0x2798, 0x70cf, 0xffff,
+ 0x027f, 0x0c7f, 0x007c, 0x017e, 0x077e, 0x0d7e, 0x0c7e, 0x2c68,
+ 0x1078, 0x74d7, 0x0040, 0x27bd, 0x2d00, 0x601a, 0x601f, 0x0001,
+ 0x2001, 0x0000, 0x1078, 0x442b, 0x2001, 0x0002, 0x1078, 0x443f,
+ 0x127e, 0x2091, 0x8000, 0x70d0, 0x8000, 0x70d2, 0x127f, 0x2009,
+ 0x0002, 0x1078, 0x756c, 0xa085, 0x0001, 0x0c7f, 0x0d7f, 0x077f,
+ 0x017f, 0x007c, 0x0c7e, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x2009,
+ 0x007f, 0x1078, 0x4499, 0x00c0, 0x27de, 0x2c68, 0x1078, 0x74d7,
+ 0x0040, 0x27de, 0x2d00, 0x601a, 0x6312, 0x601f, 0x0001, 0x620a,
+ 0x2009, 0x0022, 0x1078, 0x756c, 0xa085, 0x0001, 0x127f, 0x0d7f,
+ 0x0c7f, 0x007c, 0x0e7e, 0x0c7e, 0x067e, 0x037e, 0x027e, 0x1078,
+ 0x5d60, 0x1078, 0x5d02, 0x1078, 0x7ddf, 0x2130, 0x81ff, 0x0040,
+ 0x27f7, 0x20a9, 0x007e, 0x2009, 0x0000, 0x0078, 0x27fb, 0x20a9,
+ 0x007f, 0x2009, 0x0000, 0x017e, 0x1078, 0x4501, 0x00c0, 0x2804,
+ 0x1078, 0x471b, 0x1078, 0x4235, 0x017f, 0x8108, 0x00f0, 0x27fb,
+ 0x86ff, 0x00c0, 0x280d, 0x1078, 0x119b, 0x027f, 0x037f, 0x067f,
+ 0x0c7f, 0x0e7f, 0x007c, 0x0e7e, 0x0c7e, 0x037e, 0x027e, 0x017e,
+ 0x6218, 0x2270, 0x72a0, 0x027e, 0x2019, 0x0029, 0x1078, 0x5d53,
+ 0x077e, 0x2039, 0x0000, 0x1078, 0x5c78, 0x2c08, 0x1078, 0x9c38,
+ 0x077f, 0x017f, 0x2e60, 0x1078, 0x471b, 0x6210, 0x6314, 0x1078,
+ 0x4235, 0x6212, 0x6316, 0x017f, 0x027f, 0x037f, 0x0c7f, 0x0e7f,
+ 0x007c, 0x0e7e, 0x007e, 0x6018, 0xa080, 0x0028, 0x2004, 0xd0bc,
+ 0x00c0, 0x284d, 0x2071, 0xa300, 0x708c, 0xa005, 0x0040, 0x284a,
+ 0x8001, 0x708e, 0x007f, 0x0e7f, 0x007c, 0x2071, 0xa300, 0x70d0,
+ 0xa005, 0x0040, 0x284a, 0x8001, 0x70d2, 0x0078, 0x284a, 0x6000,
+ 0xc08c, 0x6002, 0x007c, 0x0f7e, 0x0e7e, 0x0c7e, 0x037e, 0x027e,
+ 0x017e, 0x157e, 0x2178, 0x81ff, 0x00c0, 0x286a, 0x20a9, 0x0001,
+ 0x0078, 0x2885, 0x2001, 0xa352, 0x2004, 0xd0c4, 0x0040, 0x2881,
+ 0xd0a4, 0x0040, 0x2881, 0x047e, 0x6018, 0xa080, 0x0028, 0x2024,
+ 0xa4a4, 0x00ff, 0x8427, 0xa006, 0x2009, 0x002d, 0x1078, 0x9ec0,
+ 0x047f, 0x20a9, 0x00ff, 0x2011, 0x0000, 0x027e, 0xa28e, 0x007e,
+ 0x0040, 0x28c9, 0xa28e, 0x007f, 0x0040, 0x28c9, 0xa28e, 0x0080,
+ 0x0040, 0x28c9, 0xa288, 0xa434, 0x210c, 0x81ff, 0x0040, 0x28c9,
+ 0x8fff, 0x1040, 0x28d5, 0x0c7e, 0x2160, 0x2001, 0x0001, 0x1078,
+ 0x48a2, 0x0c7f, 0x2019, 0x0029, 0x1078, 0x5d53, 0x077e, 0x2039,
+ 0x0000, 0x1078, 0x5c78, 0x0c7e, 0x027e, 0x2160, 0x6204, 0xa294,
+ 0x00ff, 0xa286, 0x0006, 0x00c0, 0x28b9, 0x6007, 0x0404, 0x0078,
+ 0x28be, 0x2001, 0x0004, 0x8007, 0xa215, 0x6206, 0x027f, 0x0c7f,
+ 0x017e, 0x2c08, 0x1078, 0x9c38, 0x017f, 0x077f, 0x2160, 0x1078,
+ 0x471b, 0x027f, 0x8210, 0x00f0, 0x2885, 0x157f, 0x017f, 0x027f,
+ 0x037f, 0x0c7f, 0x0e7f, 0x0f7f, 0x007c, 0x047e, 0x027e, 0x017e,
+ 0x2001, 0xa352, 0x2004, 0xd0c4, 0x0040, 0x28e8, 0xd0a4, 0x0040,
+ 0x28e8, 0xa006, 0x2220, 0x8427, 0x2009, 0x0029, 0x1078, 0x9ec0,
+ 0x017f, 0x027f, 0x047f, 0x007c, 0x017e, 0x027e, 0x037e, 0x0c7e,
+ 0x7280, 0x82ff, 0x0040, 0x291a, 0xa290, 0xa352, 0x2214, 0xd2ac,
+ 0x00c0, 0x291a, 0x2100, 0x1078, 0x24fa, 0x81ff, 0x0040, 0x291c,
+ 0x2019, 0x0001, 0x8314, 0xa2e0, 0xa9c0, 0x2c04, 0xd384, 0x0040,
+ 0x290e, 0xa084, 0xff00, 0x8007, 0x0078, 0x2910, 0xa084, 0x00ff,
+ 0xa116, 0x0040, 0x291c, 0xa096, 0x00ff, 0x0040, 0x291a, 0x8318,
+ 0x0078, 0x2902, 0xa085, 0x0001, 0x0c7f, 0x037f, 0x027f, 0x017f,
+ 0x007c, 0x017e, 0x0c7e, 0x127e, 0x2091, 0x8000, 0xa180, 0xa434,
+ 0x2004, 0xa065, 0x0040, 0x293b, 0x017e, 0x0c7e, 0x1078, 0x8ec0,
+ 0x017f, 0x1040, 0x1328, 0x611a, 0x1078, 0x2813, 0x1078, 0x753d,
+ 0x017f, 0x1078, 0x44bc, 0x127f, 0x0c7f, 0x017f, 0x007c, 0x7eef,
+ 0x7de8, 0x7ce4, 0x80e2, 0x7be1, 0x80e0, 0x80dc, 0x80da, 0x7ad9,
+ 0x80d6, 0x80d5, 0x80d4, 0x80d3, 0x80d2, 0x80d1, 0x79ce, 0x78cd,
+ 0x80cc, 0x80cb, 0x80ca, 0x80c9, 0x80c7, 0x80c6, 0x77c5, 0x76c3,
+ 0x80bc, 0x80ba, 0x75b9, 0x80b6, 0x74b5, 0x73b4, 0x72b3, 0x80b2,
+ 0x80b1, 0x80ae, 0x71ad, 0x80ac, 0x70ab, 0x6faa, 0x6ea9, 0x80a7,
+ 0x6da6, 0x6ca5, 0x6ba3, 0x6a9f, 0x699e, 0x689d, 0x809b, 0x8098,
+ 0x6797, 0x6690, 0x658f, 0x6488, 0x6384, 0x6282, 0x8081, 0x8080,
+ 0x617c, 0x607a, 0x8079, 0x5f76, 0x8075, 0x8074, 0x8073, 0x8072,
+ 0x8071, 0x806e, 0x5e6d, 0x806c, 0x5d6b, 0x5c6a, 0x5b69, 0x8067,
+ 0x5a66, 0x5965, 0x5863, 0x575c, 0x565a, 0x5559, 0x8056, 0x8055,
+ 0x5454, 0x5353, 0x5252, 0x5151, 0x504e, 0x4f4d, 0x804c, 0x804b,
+ 0x4e4a, 0x4d49, 0x8047, 0x4c46, 0x8045, 0x8043, 0x803c, 0x803a,
+ 0x8039, 0x8036, 0x4b35, 0x8034, 0x4a33, 0x4932, 0x4831, 0x802e,
+ 0x472d, 0x462c, 0x452b, 0x442a, 0x4329, 0x4227, 0x8026, 0x8025,
+ 0x4123, 0x401f, 0x3f1e, 0x3e1d, 0x3d1b, 0x3c18, 0x8017, 0x8010,
+ 0x3b0f, 0x3a08, 0x8004, 0x3902, 0x8001, 0x8000, 0x8000, 0x3800,
+ 0x3700, 0x3600, 0x8000, 0x3500, 0x8000, 0x8000, 0x8000, 0x3400,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x3300, 0x3200,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x3100, 0x3000,
+ 0x8000, 0x8000, 0x2f00, 0x8000, 0x2e00, 0x2d00, 0x2c00, 0x8000,
+ 0x8000, 0x8000, 0x2b00, 0x8000, 0x2a00, 0x2900, 0x2800, 0x8000,
+ 0x2700, 0x2600, 0x2500, 0x2400, 0x2300, 0x2200, 0x8000, 0x8000,
+ 0x2100, 0x2000, 0x1f00, 0x1e00, 0x1d00, 0x1c00, 0x8000, 0x8000,
+ 0x1b00, 0x1a00, 0x8000, 0x1900, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x1800, 0x8000, 0x1700, 0x1600, 0x1500, 0x8000,
+ 0x1400, 0x1300, 0x1200, 0x1100, 0x1000, 0x0f00, 0x8000, 0x8000,
+ 0x0e00, 0x0d00, 0x0c00, 0x0b00, 0x0a00, 0x0900, 0x8000, 0x8000,
+ 0x0800, 0x0700, 0x8000, 0x0600, 0x8000, 0x8000, 0x8000, 0x0500,
+ 0x0400, 0x0300, 0x8000, 0x0200, 0x8000, 0x8000, 0x8000, 0x0100,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x2071,
+ 0xa381, 0x7003, 0x0002, 0xa006, 0x7012, 0x7016, 0x703a, 0x703e,
+ 0x7033, 0xa391, 0x7037, 0xa391, 0x7007, 0x0001, 0x2061, 0xa3d1,
+ 0x6003, 0x0002, 0x007c, 0x0090, 0x2a66, 0x0068, 0x2a66, 0x2071,
+ 0xa381, 0x2b78, 0x7818, 0xd084, 0x00c0, 0x2a66, 0x2a60, 0x7820,
+ 0xa08e, 0x0069, 0x00c0, 0x2b56, 0x0079, 0x2aea, 0x007c, 0x2071,
+ 0xa381, 0x7004, 0x0079, 0x2a6c, 0x2a70, 0x2a71, 0x2a7b, 0x2a8d,
+ 0x007c, 0x0090, 0x2a7a, 0x0068, 0x2a7a, 0x2b78, 0x7818, 0xd084,
+ 0x0040, 0x2a99, 0x007c, 0x2b78, 0x2061, 0xa3d1, 0x6008, 0xa08e,
+ 0x0100, 0x0040, 0x2a88, 0xa086, 0x0200, 0x0040, 0x2b4e, 0x007c,
+ 0x7014, 0x2068, 0x2a60, 0x7018, 0x007a, 0x7010, 0x2068, 0x6834,
+ 0xa086, 0x0103, 0x0040, 0x2a95, 0x007c, 0x2a60, 0x2b78, 0x7018,
+ 0x007a, 0x2a60, 0x7820, 0xa08a, 0x0040, 0x00c8, 0x2aa2, 0x61b8,
+ 0x0079, 0x2aaa, 0x2100, 0xa08a, 0x003f, 0x00c8, 0x2b4a, 0x61b8,
+ 0x0079, 0x2aea, 0x2b2c, 0x2b5e, 0x2b66, 0x2b6a, 0x2b72, 0x2b78,
+ 0x2b7c, 0x2b88, 0x2b8c, 0x2b96, 0x2b9a, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b9e, 0x2b4a, 0x2bae, 0x2bc5, 0x2bdc, 0x2c58, 0x2c5d, 0x2c8a,
+ 0x2ce4, 0x2cf5, 0x2d13, 0x2d54, 0x2d5e, 0x2d6b, 0x2d7e, 0x2d9d,
+ 0x2da6, 0x2de3, 0x2de9, 0x2b4a, 0x2e05, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x2b4a, 0x2e0c, 0x2e16, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2e1e, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x2b4a, 0x2e30, 0x2e47, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x2b4a, 0x2e59, 0x2eb0, 0x2f0e, 0x2f1f, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x38f1, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x2b4a, 0x2b96, 0x2b9a, 0x2f36, 0x2b4a, 0x2f43, 0x397d,
+ 0x39da, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a,
+ 0x2b4a, 0x2b4a, 0x2f90, 0x30c5, 0x30e1, 0x30ed, 0x3150, 0x31a9,
+ 0x31b4, 0x31f3, 0x3202, 0x3211, 0x3214, 0x2f47, 0x3238, 0x3284,
+ 0x3291, 0x33a2, 0x34cd, 0x34f7, 0x3604, 0x3614, 0x3621, 0x365b,
+ 0x372a, 0x2b4a, 0x2b4a, 0x2b4a, 0x2b4a, 0x3792, 0x37ae, 0x3828,
+ 0x38e2, 0x713c, 0x0078, 0x2b2c, 0x2021, 0x4000, 0x1078, 0x3553,
+ 0x127e, 0x2091, 0x8000, 0x0068, 0x2b39, 0x7818, 0xd084, 0x0040,
+ 0x2b3c, 0x127f, 0x0078, 0x2b30, 0x7c22, 0x7926, 0x7a2a, 0x7b2e,
+ 0x781b, 0x0001, 0x2091, 0x4080, 0x7007, 0x0001, 0x2091, 0x5000,
+ 0x127f, 0x007c, 0x2021, 0x4001, 0x0078, 0x2b2e, 0x2021, 0x4002,
+ 0x0078, 0x2b2e, 0x2021, 0x4003, 0x0078, 0x2b2e, 0x2021, 0x4005,
+ 0x0078, 0x2b2e, 0x2021, 0x4006, 0x0078, 0x2b2e, 0xa02e, 0x2520,
+ 0x7b28, 0x7a2c, 0x7824, 0x7930, 0x0078, 0x3562, 0x7823, 0x0004,
+ 0x7824, 0x007a, 0xa02e, 0x2520, 0x7b28, 0x7a2c, 0x7824, 0x7930,
+ 0x0078, 0x3566, 0x7924, 0x7828, 0x2114, 0x200a, 0x0078, 0x2b2c,
+ 0x7924, 0x2114, 0x0078, 0x2b2c, 0x2099, 0x0009, 0x20a1, 0x0009,
+ 0x20a9, 0x0007, 0x53a3, 0x7924, 0x7a28, 0x7b2c, 0x0078, 0x2b2c,
+ 0x7824, 0x2060, 0x0078, 0x2ba0, 0x2009, 0x0001, 0x2011, 0x0013,
+ 0x2019, 0x0010, 0x783b, 0x0017, 0x0078, 0x2b2c, 0x7d38, 0x7c3c,
+ 0x0078, 0x2b60, 0x7d38, 0x7c3c, 0x0078, 0x2b6c, 0x2061, 0x1000,
+ 0x610c, 0xa006, 0x2c14, 0xa200, 0x8c60, 0x8109, 0x00c0, 0x2ba2,
+ 0x2010, 0xa005, 0x0040, 0x2b2c, 0x0078, 0x2b52, 0x2069, 0xa351,
+ 0x7824, 0x7930, 0xa11a, 0x00c8, 0x2b5a, 0x8019, 0x0040, 0x2b5a,
+ 0x684a, 0x6942, 0x782c, 0x6852, 0x7828, 0x6856, 0xa006, 0x685a,
+ 0x685e, 0x1078, 0x4dbd, 0x0078, 0x2b2c, 0x2069, 0xa351, 0x7824,
+ 0x7934, 0xa11a, 0x00c8, 0x2b5a, 0x8019, 0x0040, 0x2b5a, 0x684e,
+ 0x6946, 0x782c, 0x6862, 0x7828, 0x6866, 0xa006, 0x686a, 0x686e,
+ 0x1078, 0x494d, 0x0078, 0x2b2c, 0xa02e, 0x2520, 0x81ff, 0x00c0,
+ 0x2b56, 0x7924, 0x7b28, 0x7a2c, 0x20a9, 0x0005, 0x20a1, 0xa388,
+ 0x41a1, 0x1078, 0x3518, 0x0040, 0x2b56, 0x2009, 0x0020, 0x1078,
+ 0x3562, 0x701b, 0x2bf4, 0x007c, 0x6834, 0x2008, 0xa084, 0x00ff,
+ 0xa096, 0x0011, 0x0040, 0x2c00, 0xa096, 0x0019, 0x00c0, 0x2b56,
+ 0x810f, 0xa18c, 0x00ff, 0x0040, 0x2b56, 0x710e, 0x700c, 0x8001,
+ 0x0040, 0x2c31, 0x700e, 0x1078, 0x3518, 0x0040, 0x2b56, 0x2009,
+ 0x0020, 0x2061, 0xa3d1, 0x6224, 0x6328, 0x642c, 0x6530, 0xa290,
+ 0x0040, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x1078,
+ 0x3562, 0x701b, 0x2c24, 0x007c, 0x6834, 0xa084, 0x00ff, 0xa096,
+ 0x0002, 0x0040, 0x2c2f, 0xa096, 0x000a, 0x00c0, 0x2b56, 0x0078,
+ 0x2c06, 0x7010, 0x2068, 0x6838, 0xc0fd, 0x683a, 0x1078, 0x436e,
+ 0x00c0, 0x2c3f, 0x7007, 0x0003, 0x701b, 0x2c41, 0x007c, 0x1078,
+ 0x4a60, 0x127e, 0x2091, 0x8000, 0x20a9, 0x0005, 0x2099, 0xa388,
+ 0x530a, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+ 0x0000, 0xad80, 0x000d, 0x2009, 0x0020, 0x127f, 0x0078, 0x3566,
+ 0x61a0, 0x7824, 0x60a2, 0x0078, 0x2b2c, 0x2091, 0x8000, 0x7823,
+ 0x4000, 0x7827, 0x4953, 0x782b, 0x5020, 0x782f, 0x2020, 0x2009,
+ 0x017f, 0x2104, 0x7832, 0x3f00, 0x7836, 0x2061, 0x0100, 0x6200,
+ 0x2061, 0x0200, 0x603c, 0x8007, 0xa205, 0x783a, 0x2009, 0x04fd,
+ 0x2104, 0x783e, 0x781b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080,
+ 0x2071, 0x0010, 0x20c1, 0x00f0, 0xa08a, 0x0003, 0x00c8, 0x0427,
+ 0x0078, 0x0423, 0x81ff, 0x00c0, 0x2b56, 0x7924, 0x810f, 0xa18c,
+ 0x00ff, 0x1078, 0x4501, 0x00c0, 0x2b5a, 0x7e38, 0xa684, 0x3fff,
+ 0xa082, 0x4000, 0x0048, 0x2c9e, 0x0078, 0x2b5a, 0x7c28, 0x7d2c,
+ 0x1078, 0x46d6, 0xd28c, 0x00c0, 0x2ca9, 0x1078, 0x466a, 0x0078,
+ 0x2cab, 0x1078, 0x46a4, 0x00c0, 0x2cd5, 0x2061, 0xaa00, 0x127e,
+ 0x2091, 0x8000, 0x6000, 0xa086, 0x0000, 0x0040, 0x2cc3, 0x6010,
+ 0xa06d, 0x0040, 0x2cc3, 0x683c, 0xa406, 0x00c0, 0x2cc3, 0x6840,
+ 0xa506, 0x0040, 0x2cce, 0x127f, 0xace0, 0x0010, 0x2001, 0xa315,
+ 0x2004, 0xac02, 0x00c8, 0x2b56, 0x0078, 0x2caf, 0x1078, 0x8758,
+ 0x127f, 0x0040, 0x2b56, 0x0078, 0x2b2c, 0xa00e, 0x2001, 0x0005,
+ 0x1078, 0x4a60, 0x127e, 0x2091, 0x8000, 0x1078, 0x8cc0, 0x1078,
+ 0x4982, 0x127f, 0x0078, 0x2b2c, 0x81ff, 0x00c0, 0x2b56, 0x1078,
+ 0x3530, 0x0040, 0x2b5a, 0x1078, 0x45a7, 0x0040, 0x2b56, 0x1078,
+ 0x46e4, 0x0040, 0x2b56, 0x0078, 0x2b2c, 0x81ff, 0x00c0, 0x2b56,
+ 0x1078, 0x3542, 0x0040, 0x2b5a, 0x1078, 0x475f, 0x0040, 0x2b56,
+ 0x2019, 0x0005, 0x1078, 0x4705, 0x0040, 0x2b56, 0x7828, 0xa08a,
+ 0x1000, 0x00c8, 0x2b5a, 0x8003, 0x800b, 0x810b, 0xa108, 0x1078,
+ 0x58e1, 0x0078, 0x2b2c, 0x127e, 0x2091, 0x8000, 0x81ff, 0x0040,
+ 0x2d1d, 0x2009, 0x0001, 0x0078, 0x2d4e, 0x2029, 0x00ff, 0x644c,
+ 0x2400, 0xa506, 0x0040, 0x2d48, 0x2508, 0x1078, 0x4501, 0x00c0,
+ 0x2d48, 0x1078, 0x475f, 0x00c0, 0x2d33, 0x2009, 0x0002, 0x62a8,
+ 0x2518, 0x0078, 0x2d4e, 0x2019, 0x0004, 0x1078, 0x4705, 0x00c0,
+ 0x2d3d, 0x2009, 0x0006, 0x0078, 0x2d4e, 0x7824, 0xa08a, 0x1000,
+ 0x00c8, 0x2d51, 0x8003, 0x800b, 0x810b, 0xa108, 0x1078, 0x58e1,
+ 0x8529, 0x00c8, 0x2d20, 0x127f, 0x0078, 0x2b2c, 0x127f, 0x0078,
+ 0x2b56, 0x127f, 0x0078, 0x2b5a, 0x1078, 0x3530, 0x0040, 0x2b5a,
+ 0x1078, 0x461b, 0x1078, 0x46d6, 0x0078, 0x2b2c, 0x81ff, 0x00c0,
+ 0x2b56, 0x1078, 0x3530, 0x0040, 0x2b5a, 0x1078, 0x460a, 0x1078,
+ 0x46d6, 0x0078, 0x2b2c, 0x81ff, 0x00c0, 0x2b56, 0x1078, 0x3530,
+ 0x0040, 0x2b5a, 0x1078, 0x46a7, 0x0040, 0x2b56, 0x1078, 0x43c1,
+ 0x1078, 0x4663, 0x1078, 0x46d6, 0x0078, 0x2b2c, 0x1078, 0x3530,
+ 0x0040, 0x2b5a, 0x1078, 0x45a7, 0x0040, 0x2b56, 0x62a0, 0x2019,
+ 0x0005, 0x0c7e, 0x1078, 0x471b, 0x0c7f, 0x1078, 0x5d53, 0x077e,
+ 0x2039, 0x0000, 0x1078, 0x5c78, 0x2009, 0x0000, 0x1078, 0x9c38,
+ 0x077f, 0x1078, 0x46d6, 0x0078, 0x2b2c, 0x1078, 0x3530, 0x0040,
+ 0x2b5a, 0x1078, 0x46d6, 0x2208, 0x0078, 0x2b2c, 0x157e, 0x0d7e,
+ 0x0e7e, 0x2069, 0xa413, 0x6810, 0x6914, 0xa10a, 0x00c8, 0x2db2,
+ 0x2009, 0x0000, 0x6816, 0x2011, 0x0000, 0x2019, 0x0000, 0x20a9,
+ 0x00ff, 0x2069, 0xa434, 0x2d04, 0xa075, 0x0040, 0x2dc7, 0x704c,
+ 0x1078, 0x2dd1, 0xa210, 0x7080, 0x1078, 0x2dd1, 0xa318, 0x8d68,
+ 0x00f0, 0x2dbb, 0x2300, 0xa218, 0x0e7f, 0x0d7f, 0x157f, 0x0078,
+ 0x2b2c, 0x0f7e, 0x017e, 0xa07d, 0x0040, 0x2de0, 0x2001, 0x0000,
+ 0x8000, 0x2f0c, 0x81ff, 0x0040, 0x2de0, 0x2178, 0x0078, 0x2dd8,
+ 0x017f, 0x0f7f, 0x007c, 0x2069, 0xa413, 0x6910, 0x62a4, 0x0078,
+ 0x2b2c, 0x81ff, 0x00c0, 0x2b56, 0x614c, 0xa190, 0x293f, 0x2214,
+ 0xa294, 0x00ff, 0x606c, 0xa084, 0xff00, 0xa215, 0x6368, 0x67c8,
+ 0xd79c, 0x0040, 0x2dff, 0x2031, 0x0001, 0x0078, 0x2e01, 0x2031,
+ 0x0000, 0x7e3a, 0x7f3e, 0x0078, 0x2b2c, 0x613c, 0x6240, 0x2019,
+ 0xa5a0, 0x231c, 0x0078, 0x2b2c, 0x127e, 0x2091, 0x8000, 0x6134,
+ 0xa006, 0x2010, 0x2018, 0x127f, 0x0078, 0x2b2c, 0x1078, 0x3542,
+ 0x0040, 0x2b5a, 0x6244, 0x6338, 0x0078, 0x2b2c, 0x613c, 0x6240,
+ 0x7824, 0x603e, 0x7b28, 0x6342, 0x2069, 0xa351, 0x831f, 0xa305,
+ 0x6816, 0x782c, 0x2069, 0xa5a0, 0x2d1c, 0x206a, 0x0078, 0x2b2c,
+ 0x017e, 0x127e, 0x2091, 0x8000, 0x7824, 0x6036, 0xd094, 0x0040,
+ 0x2e43, 0x7828, 0xa085, 0x0001, 0x2009, 0xa5a9, 0x200a, 0x2001,
+ 0xffff, 0x1078, 0x5975, 0x127f, 0x017f, 0x0078, 0x2b2c, 0x1078,
+ 0x3542, 0x0040, 0x2b5a, 0x7828, 0xa00d, 0x0040, 0x2b5a, 0x782c,
+ 0xa005, 0x0040, 0x2b5a, 0x6244, 0x6146, 0x6338, 0x603a, 0x0078,
+ 0x2b2c, 0x2001, 0xa300, 0x2004, 0xa086, 0x0003, 0x00c0, 0x2b56,
+ 0x0c7e, 0x2061, 0x0100, 0x7924, 0x810f, 0xa18c, 0x00ff, 0xa196,
+ 0x00ff, 0x00c0, 0x2e70, 0x6030, 0xa085, 0xff00, 0x0078, 0x2e7f,
+ 0xa182, 0x007f, 0x00c8, 0x2ea9, 0xa188, 0x293f, 0x210c, 0xa18c,
+ 0x00ff, 0x6030, 0xa116, 0x0040, 0x2ea9, 0x810f, 0xa105, 0x127e,
+ 0x2091, 0x8000, 0x007e, 0x1078, 0x74d7, 0x007f, 0x0040, 0x2ea5,
+ 0x601a, 0x600b, 0xbc09, 0x601f, 0x0001, 0x1078, 0x3518, 0x0040,
+ 0x2eac, 0x6837, 0x0000, 0x7007, 0x0003, 0x6833, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x701b, 0x2f07, 0x2d00, 0x6012, 0x2009, 0x0032,
+ 0x1078, 0x756c, 0x127f, 0x0c7f, 0x007c, 0x127f, 0x0c7f, 0x0078,
+ 0x2b56, 0x0c7f, 0x0078, 0x2b5a, 0x1078, 0x753d, 0x0078, 0x2ea5,
+ 0x2001, 0xa300, 0x2004, 0xa086, 0x0003, 0x00c0, 0x2b56, 0x0c7e,
+ 0x2061, 0x0100, 0x7924, 0x810f, 0xa18c, 0x00ff, 0xa196, 0x00ff,
+ 0x00c0, 0x2ec7, 0x6030, 0xa085, 0xff00, 0x0078, 0x2ed6, 0xa182,
+ 0x007f, 0x00c8, 0x2f00, 0xa188, 0x293f, 0x210c, 0xa18c, 0x00ff,
+ 0x6030, 0xa116, 0x0040, 0x2f00, 0x810f, 0xa105, 0x127e, 0x2091,
+ 0x8000, 0x007e, 0x1078, 0x74d7, 0x007f, 0x0040, 0x2efc, 0x601a,
+ 0x600b, 0xbc05, 0x601f, 0x0001, 0x1078, 0x3518, 0x0040, 0x2f03,
+ 0x6837, 0x0000, 0x7007, 0x0003, 0x6833, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x701b, 0x2f07, 0x2d00, 0x6012, 0x2009, 0x0032, 0x1078,
+ 0x756c, 0x127f, 0x0c7f, 0x007c, 0x127f, 0x0c7f, 0x0078, 0x2b56,
+ 0x0c7f, 0x0078, 0x2b5a, 0x1078, 0x753d, 0x0078, 0x2efc, 0x6830,
+ 0xa086, 0x0100, 0x0040, 0x2b56, 0x0078, 0x2b2c, 0x2061, 0xa62d,
+ 0x127e, 0x2091, 0x8000, 0x6000, 0xd084, 0x0040, 0x2f1c, 0x6104,
+ 0x6208, 0x127f, 0x0078, 0x2b2c, 0x127f, 0x0078, 0x2b5a, 0x81ff,
+ 0x00c0, 0x2b56, 0x127e, 0x2091, 0x8000, 0x6244, 0x6060, 0xa202,
+ 0x0048, 0x2f33, 0xa085, 0x0001, 0x1078, 0x2500, 0x1078, 0x3bf5,
+ 0x127f, 0x0078, 0x2b2c, 0x127f, 0x0078, 0x2b5a, 0x127e, 0x2091,
+ 0x8000, 0x20a9, 0x0011, 0x2001, 0xa340, 0x20a0, 0xa006, 0x40a4,
+ 0x127f, 0x0078, 0x2b2c, 0x7d38, 0x7c3c, 0x0078, 0x2bde, 0x7824,
+ 0xa09c, 0x00ff, 0xa39a, 0x0003, 0x00c8, 0x2b56, 0x624c, 0xa084,
+ 0xff00, 0x8007, 0xa206, 0x00c0, 0x2f5f, 0x2001, 0xa340, 0x2009,
+ 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0078, 0x3566, 0x81ff,
+ 0x00c0, 0x2b56, 0x1078, 0x3542, 0x0040, 0x2b5a, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x00c0, 0x2b56, 0x0c7e, 0x1078, 0x3518,
+ 0x0c7f, 0x0040, 0x2b56, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a,
+ 0x1078, 0x8b85, 0x0040, 0x2b56, 0x7007, 0x0003, 0x701b, 0x2f81,
+ 0x007c, 0x6830, 0xa086, 0x0100, 0x0040, 0x2b56, 0xad80, 0x000e,
+ 0x2009, 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0078, 0x3566,
+ 0x1078, 0x3518, 0x0040, 0x2b56, 0x1078, 0x421a, 0x2009, 0x001c,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x3562, 0x701b, 0x2fa1,
+ 0x007c, 0xade8, 0x000d, 0x6800, 0xa005, 0x0040, 0x2b5a, 0x6804,
+ 0xd0ac, 0x0040, 0x2fae, 0xd0a4, 0x0040, 0x2b5a, 0xd094, 0x0040,
+ 0x2fb9, 0x0c7e, 0x2061, 0x0100, 0x6104, 0xa18c, 0xffdf, 0x6106,
+ 0x0c7f, 0xd08c, 0x0040, 0x2fc4, 0x0c7e, 0x2061, 0x0100, 0x6104,
+ 0xa18d, 0x0010, 0x6106, 0x0c7f, 0x2009, 0x0100, 0x210c, 0xa18a,
+ 0x0002, 0x0048, 0x2fd9, 0xd084, 0x0040, 0x2fd9, 0x6a28, 0xa28a,
+ 0x007f, 0x00c8, 0x2b5a, 0xa288, 0x293f, 0x210c, 0xa18c, 0x00ff,
+ 0x6152, 0xd0dc, 0x0040, 0x2fe2, 0x6828, 0xa08a, 0x007f, 0x00c8,
+ 0x2b5a, 0x604e, 0x6808, 0xa08a, 0x0100, 0x0048, 0x2b5a, 0xa08a,
+ 0x0841, 0x00c8, 0x2b5a, 0xa084, 0x0007, 0x00c0, 0x2b5a, 0x680c,
+ 0xa005, 0x0040, 0x2b5a, 0x6810, 0xa005, 0x0040, 0x2b5a, 0x6848,
+ 0x6940, 0xa10a, 0x00c8, 0x2b5a, 0x8001, 0x0040, 0x2b5a, 0x684c,
+ 0x6944, 0xa10a, 0x00c8, 0x2b5a, 0x8001, 0x0040, 0x2b5a, 0x6804,
+ 0xd0fc, 0x0040, 0x3038, 0x1078, 0x3518, 0x0040, 0x2b56, 0x2009,
+ 0x0014, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0xa290, 0x0038, 0xa399,
+ 0x0000, 0x1078, 0x3562, 0x701b, 0x301e, 0x007c, 0xade8, 0x000d,
+ 0x20a9, 0x0014, 0x2d98, 0x2069, 0xa36d, 0x2da0, 0x53a3, 0x7010,
+ 0xa0e8, 0x000d, 0x2001, 0xa371, 0x200c, 0xd1e4, 0x0040, 0x3038,
+ 0x0c7e, 0x2061, 0x0100, 0x6004, 0xa085, 0x0b00, 0x6006, 0x0c7f,
+ 0x20a9, 0x001c, 0x2d98, 0x2069, 0xa351, 0x2da0, 0x53a3, 0x6814,
+ 0xa08c, 0x00ff, 0x613e, 0x8007, 0xa084, 0x00ff, 0x6042, 0x1078,
+ 0x4dbd, 0x1078, 0x48dd, 0x1078, 0x494d, 0x6000, 0xa086, 0x0000,
+ 0x00c0, 0x30c3, 0x6808, 0x602a, 0x1078, 0x218b, 0x6818, 0x691c,
+ 0x6a20, 0x6b24, 0x8007, 0x810f, 0x8217, 0x831f, 0x6016, 0x611a,
+ 0x621e, 0x6322, 0x6c04, 0xd4f4, 0x0040, 0x3070, 0x6830, 0x6934,
+ 0x6a38, 0x6b3c, 0x8007, 0x810f, 0x8217, 0x831f, 0x0078, 0x3072,
+ 0xa084, 0xf0ff, 0x6006, 0x610a, 0x620e, 0x6312, 0x1078, 0x59a8,
+ 0x6904, 0xd1fc, 0x0040, 0x30a5, 0x0c7e, 0x2009, 0x0000, 0x20a9,
+ 0x0001, 0x6b70, 0xd384, 0x0040, 0x30a2, 0x0078, 0x308c, 0x839d,
+ 0x00c8, 0x30a2, 0x3508, 0x8109, 0x1078, 0x5364, 0x6878, 0x6016,
+ 0x6874, 0x2008, 0xa084, 0xff00, 0x8007, 0x600a, 0xa184, 0x00ff,
+ 0x6006, 0x8108, 0x00c0, 0x30a0, 0x6003, 0x0003, 0x0078, 0x30a2,
+ 0x6003, 0x0001, 0x00f0, 0x3087, 0x0c7f, 0x0c7e, 0x2061, 0x0100,
+ 0x602f, 0x0040, 0x602f, 0x0000, 0x0c7f, 0x1078, 0x3784, 0x0040,
+ 0x30b3, 0x1078, 0x2500, 0x60bc, 0xa005, 0x0040, 0x30bf, 0x6003,
+ 0x0001, 0x2091, 0x301d, 0x1078, 0x4171, 0x0078, 0x30c3, 0x6003,
+ 0x0004, 0x2091, 0x301d, 0x0078, 0x2b2c, 0x6000, 0xa086, 0x0000,
+ 0x0040, 0x2b56, 0x2069, 0xa351, 0x7830, 0x6842, 0x7834, 0x6846,
+ 0x6804, 0xd0fc, 0x0040, 0x30d8, 0x2009, 0x0030, 0x0078, 0x30da,
+ 0x2009, 0x001c, 0x2d00, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0078,
+ 0x3566, 0xa006, 0x1078, 0x2500, 0x81ff, 0x00c0, 0x2b56, 0x1078,
+ 0x421a, 0x1078, 0x4171, 0x0078, 0x2b2c, 0x81ff, 0x00c0, 0x2b56,
+ 0x6180, 0x81ff, 0x0040, 0x3107, 0x703f, 0x0000, 0x2001, 0xa9c0,
+ 0x2009, 0x0040, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x3566, 0x701b, 0x2b29, 0x127f, 0x007c, 0x703f,
+ 0x0001, 0x0d7e, 0x2069, 0xa9c0, 0x20a9, 0x0040, 0x20a1, 0xa9c0,
+ 0x2019, 0xffff, 0x43a4, 0x654c, 0xa588, 0x293f, 0x210c, 0xa18c,
+ 0x00ff, 0x216a, 0xa00e, 0x2011, 0x0002, 0x2100, 0xa506, 0x0040,
+ 0x3139, 0x1078, 0x4501, 0x00c0, 0x3139, 0x6014, 0x821c, 0x0048,
+ 0x3131, 0xa398, 0xa9c0, 0xa085, 0xff00, 0x8007, 0x201a, 0x0078,
+ 0x3138, 0xa398, 0xa9c0, 0x2324, 0xa4a4, 0xff00, 0xa405, 0x201a,
+ 0x8210, 0x8108, 0xa182, 0x0080, 0x00c8, 0x3140, 0x0078, 0x311d,
+ 0x8201, 0x8007, 0x2d0c, 0xa105, 0x206a, 0x0d7f, 0x20a9, 0x0040,
+ 0x20a1, 0xa9c0, 0x2099, 0xa9c0, 0x1078, 0x41be, 0x0078, 0x30f6,
+ 0x1078, 0x3542, 0x0040, 0x2b5a, 0x0c7e, 0x1078, 0x3518, 0x0c7f,
+ 0x00c0, 0x315e, 0x2009, 0x0002, 0x0078, 0x2b56, 0x2001, 0xa352,
+ 0x2004, 0xd0b4, 0x0040, 0x3185, 0x6000, 0xd08c, 0x00c0, 0x3185,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x3185, 0x6837,
+ 0x0000, 0x6838, 0xc0fd, 0x683a, 0x1078, 0x8bd9, 0x00c0, 0x317c,
+ 0x2009, 0x0003, 0x0078, 0x2b56, 0x7007, 0x0003, 0x701b, 0x3181,
+ 0x007c, 0x1078, 0x3542, 0x0040, 0x2b5a, 0x20a9, 0x002b, 0x2c98,
+ 0xade8, 0x0002, 0x2da0, 0x53a3, 0x20a9, 0x0004, 0xac80, 0x0006,
+ 0x2098, 0xad80, 0x0006, 0x20a0, 0x1078, 0x41be, 0x20a9, 0x0004,
+ 0xac80, 0x000a, 0x2098, 0xad80, 0x000a, 0x20a0, 0x1078, 0x41be,
+ 0x2d00, 0x2009, 0x002b, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0078,
+ 0x3566, 0x81ff, 0x00c0, 0x2b56, 0x1078, 0x3530, 0x0040, 0x2b5a,
+ 0x1078, 0x46ef, 0x0078, 0x2b2c, 0x81ff, 0x00c0, 0x2b56, 0x7828,
+ 0xa08a, 0x1000, 0x00c8, 0x2b5a, 0x1078, 0x3542, 0x0040, 0x2b5a,
+ 0x1078, 0x475f, 0x0040, 0x2b56, 0x2019, 0x0004, 0x1078, 0x4705,
+ 0x7924, 0x810f, 0x7a28, 0x1078, 0x31cf, 0x0078, 0x2b2c, 0xa186,
+ 0x00ff, 0x0040, 0x31d7, 0x1078, 0x31e7, 0x0078, 0x31e6, 0x2029,
+ 0x007e, 0x2061, 0xa300, 0x644c, 0x2400, 0xa506, 0x0040, 0x31e3,
+ 0x2508, 0x1078, 0x31e7, 0x8529, 0x00c8, 0x31dc, 0x007c, 0x1078,
+ 0x4501, 0x00c0, 0x31f2, 0x2200, 0x8003, 0x800b, 0x810b, 0xa108,
+ 0x1078, 0x58e1, 0x007c, 0x81ff, 0x00c0, 0x2b56, 0x1078, 0x3530,
+ 0x0040, 0x2b5a, 0x1078, 0x45a7, 0x0040, 0x2b56, 0x1078, 0x46fa,
+ 0x0078, 0x2b2c, 0x81ff, 0x00c0, 0x2b56, 0x1078, 0x3530, 0x0040,
+ 0x2b5a, 0x1078, 0x45a7, 0x0040, 0x2b56, 0x1078, 0x46e4, 0x0078,
+ 0x2b2c, 0x6100, 0x0078, 0x2b2c, 0x1078, 0x3542, 0x0040, 0x2b5a,
+ 0x2001, 0xa300, 0x2004, 0xa086, 0x0003, 0x00c0, 0x2b56, 0x0d7e,
+ 0xace8, 0x000a, 0x7924, 0xd184, 0x0040, 0x3228, 0xace8, 0x0006,
+ 0x680c, 0x8007, 0x783e, 0x6808, 0x8007, 0x783a, 0x6b04, 0x831f,
+ 0x6a00, 0x8217, 0x0d7f, 0x6100, 0xa18c, 0x0200, 0x0078, 0x2b2c,
+ 0xa006, 0x1078, 0x2500, 0x7824, 0xa084, 0x00ff, 0xa086, 0x00ff,
+ 0x0040, 0x3245, 0x81ff, 0x00c0, 0x2b56, 0x1078, 0x421a, 0x7828,
+ 0xa08a, 0x1000, 0x00c8, 0x2b5a, 0x7924, 0xa18c, 0xff00, 0x810f,
+ 0xa186, 0x00ff, 0x0040, 0x325b, 0xa182, 0x007f, 0x00c8, 0x2b5a,
+ 0x2100, 0x1078, 0x24fa, 0x027e, 0x0c7e, 0x127e, 0x2091, 0x8000,
+ 0x2061, 0xa5be, 0x601b, 0x0000, 0x601f, 0x0000, 0x2061, 0x0100,
+ 0x6030, 0xa084, 0x00ff, 0x810f, 0xa105, 0x604a, 0x6043, 0x0090,
+ 0x6043, 0x0010, 0x2009, 0x002d, 0x2011, 0x4196, 0x1078, 0x596c,
+ 0x7924, 0xa18c, 0xff00, 0x810f, 0x7a28, 0x1078, 0x31cf, 0x127f,
+ 0x0c7f, 0x027f, 0x0078, 0x2b2c, 0x7924, 0xa18c, 0xff00, 0x810f,
+ 0x0c7e, 0x1078, 0x4499, 0x2c08, 0x0c7f, 0x00c0, 0x2b5a, 0x0078,
+ 0x2b2c, 0x81ff, 0x0040, 0x3298, 0x2009, 0x0001, 0x0078, 0x2b56,
+ 0x60c8, 0xd09c, 0x00c0, 0x32a0, 0x2009, 0x0005, 0x0078, 0x2b56,
+ 0x1078, 0x3518, 0x00c0, 0x32a8, 0x2009, 0x0002, 0x0078, 0x2b56,
+ 0x7924, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x3562, 0x701b,
+ 0x32b2, 0x007c, 0x2009, 0x0080, 0x1078, 0x4501, 0x00c0, 0x32bf,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x0040, 0x32c3, 0x2021,
+ 0x400a, 0x0078, 0x2b2e, 0x0d7e, 0xade8, 0x000d, 0x6900, 0x6a08,
+ 0x6b0c, 0x6c10, 0x6d14, 0x6e18, 0x6820, 0xa0be, 0x0100, 0x0040,
+ 0x3336, 0xa0be, 0x0112, 0x0040, 0x3336, 0xa0be, 0x0113, 0x0040,
+ 0x3336, 0xa0be, 0x0114, 0x0040, 0x3336, 0xa0be, 0x0117, 0x0040,
+ 0x3336, 0xa0be, 0x011a, 0x0040, 0x3336, 0xa0be, 0x0121, 0x0040,
+ 0x332c, 0xa0be, 0x0131, 0x0040, 0x332c, 0xa0be, 0x0171, 0x0040,
+ 0x3336, 0xa0be, 0x0173, 0x0040, 0x3336, 0xa0be, 0x01a1, 0x00c0,
+ 0x32fe, 0x6830, 0x8007, 0x6832, 0x0078, 0x333c, 0xa0be, 0x0212,
+ 0x0040, 0x3332, 0xa0be, 0x0213, 0x0040, 0x3332, 0xa0be, 0x0214,
+ 0x0040, 0x3324, 0xa0be, 0x0217, 0x0040, 0x331e, 0xa0be, 0x021a,
+ 0x00c0, 0x3317, 0x6838, 0x8007, 0x683a, 0x0078, 0x3336, 0xa0be,
+ 0x0300, 0x0040, 0x3336, 0x0d7f, 0x0078, 0x2b5a, 0xad80, 0x0010,
+ 0x20a9, 0x0007, 0x1078, 0x337e, 0xad80, 0x000e, 0x20a9, 0x0001,
+ 0x1078, 0x337e, 0x0078, 0x3336, 0xad80, 0x000c, 0x1078, 0x338c,
+ 0x0078, 0x333c, 0xad80, 0x000e, 0x1078, 0x338c, 0xad80, 0x000c,
+ 0x20a9, 0x0001, 0x1078, 0x337e, 0x0c7e, 0x1078, 0x3518, 0x0040,
+ 0x336f, 0x6838, 0xc0fd, 0x683a, 0x6837, 0x0119, 0x6853, 0x0000,
+ 0x684f, 0x0020, 0x685b, 0x0001, 0x810b, 0x697e, 0x6883, 0x0000,
+ 0x6a86, 0x6b8a, 0x6c8e, 0x6d92, 0x6996, 0x689b, 0x0000, 0x0c7f,
+ 0x0d7f, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x6823, 0x0000,
+ 0x6804, 0x2068, 0x1078, 0x8ba1, 0x00c0, 0x336a, 0x2009, 0x0003,
+ 0x0078, 0x2b56, 0x7007, 0x0003, 0x701b, 0x3375, 0x007c, 0x0c7f,
+ 0x0d7f, 0x2009, 0x0002, 0x0078, 0x2b56, 0x6820, 0xa086, 0x8001,
+ 0x00c0, 0x2b2c, 0x2009, 0x0004, 0x0078, 0x2b56, 0x017e, 0x2008,
+ 0x2044, 0x8000, 0x204c, 0x8000, 0x290a, 0x8108, 0x280a, 0x8108,
+ 0x00f0, 0x3380, 0x017f, 0x007c, 0x017e, 0x0a7e, 0x0b7e, 0x2008,
+ 0x2044, 0x8000, 0x204c, 0x8000, 0x2054, 0x8000, 0x205c, 0x2b0a,
+ 0x8108, 0x2a0a, 0x8108, 0x290a, 0x8108, 0x280a, 0x0b7f, 0x0a7f,
+ 0x017f, 0x007c, 0x81ff, 0x0040, 0x33a9, 0x2009, 0x0001, 0x0078,
+ 0x2b56, 0x7924, 0x2140, 0xa18c, 0xff00, 0x810f, 0xa182, 0x0080,
+ 0x0048, 0x2b5a, 0xa182, 0x00ff, 0x00c8, 0x2b5a, 0x7a2c, 0x7b28,
+ 0x6068, 0xa306, 0x00c0, 0x33c4, 0x606c, 0xa24e, 0x0040, 0x2b5a,
+ 0xa9cc, 0xff00, 0x0040, 0x2b5a, 0x0c7e, 0x1078, 0x346d, 0x2c68,
+ 0x0c7f, 0x0040, 0x33fc, 0xa0c6, 0x4000, 0x00c0, 0x33e2, 0x0c7e,
+ 0x007e, 0x2d60, 0x2009, 0x0000, 0x1078, 0x47cb, 0x00c0, 0x33d9,
+ 0xc185, 0x6000, 0xd0bc, 0x0040, 0x33de, 0xc18d, 0x007f, 0x0c7f,
+ 0x0078, 0x33f9, 0xa0c6, 0x4007, 0x00c0, 0x33e9, 0x2408, 0x0078,
+ 0x33f9, 0xa0c6, 0x4008, 0x00c0, 0x33f1, 0x2708, 0x2610, 0x0078,
+ 0x33f9, 0xa0c6, 0x4009, 0x00c0, 0x33f7, 0x0078, 0x33f9, 0x2001,
+ 0x4006, 0x2020, 0x0078, 0x2b2e, 0x2d00, 0x7022, 0x017e, 0x0b7e,
+ 0x0c7e, 0x0e7e, 0x2c70, 0x1078, 0x74d7, 0x0040, 0x3442, 0x2d00,
+ 0x601a, 0x2001, 0xa356, 0x2004, 0xa084, 0x00ff, 0x6842, 0x2e58,
+ 0x0e7f, 0x0e7e, 0x0c7e, 0x1078, 0x3518, 0x0c7f, 0x2b70, 0x00c0,
+ 0x3423, 0x1078, 0x753d, 0x0e7f, 0x0c7f, 0x0b7f, 0x017f, 0x2009,
+ 0x0002, 0x0078, 0x2b56, 0x6837, 0x0000, 0x2d00, 0x6012, 0x6833,
+ 0x0000, 0x6838, 0xc0fd, 0x683a, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x2813, 0x127f, 0x601f, 0x0001, 0x2001, 0x0000, 0x1078, 0x442b,
+ 0x2001, 0x0002, 0x1078, 0x443f, 0x2009, 0x0002, 0x1078, 0x756c,
+ 0xa085, 0x0001, 0x0e7f, 0x0c7f, 0x0b7f, 0x017f, 0x00c0, 0x344c,
+ 0x2009, 0x0003, 0x0078, 0x2b56, 0x7007, 0x0003, 0x701b, 0x3451,
+ 0x007c, 0x6830, 0xa086, 0x0100, 0x7020, 0x2060, 0x00c0, 0x345f,
+ 0x2009, 0x0004, 0x6204, 0xa294, 0x00ff, 0x0078, 0x2b56, 0x2009,
+ 0x0000, 0x1078, 0x47cb, 0x00c0, 0x3466, 0xc185, 0x6000, 0xd0bc,
+ 0x0040, 0x346b, 0xc18d, 0x0078, 0x2b2c, 0x0e7e, 0x0d7e, 0x2029,
+ 0x0000, 0x2021, 0x0080, 0x20a9, 0x007f, 0x2071, 0xa4b4, 0x2e04,
+ 0xa005, 0x00c0, 0x3482, 0x2100, 0xa406, 0x00c0, 0x34b3, 0x2428,
+ 0x0078, 0x34b3, 0x2068, 0x6f10, 0x2700, 0xa306, 0x00c0, 0x34a4,
+ 0x6e14, 0x2600, 0xa206, 0x00c0, 0x34a4, 0x2400, 0xa106, 0x00c0,
+ 0x34a0, 0x2d60, 0xd884, 0x0040, 0x34c8, 0x6004, 0xa084, 0x00ff,
+ 0xa086, 0x0006, 0x00c0, 0x34c8, 0x2001, 0x4000, 0x0078, 0x34c9,
+ 0x2001, 0x4007, 0x0078, 0x34c9, 0x2400, 0xa106, 0x00c0, 0x34b3,
+ 0x6e14, 0x87ff, 0x00c0, 0x34af, 0x86ff, 0x0040, 0x347f, 0x2001,
+ 0x4008, 0x0078, 0x34c9, 0x8420, 0x8e70, 0x00f0, 0x3477, 0x85ff,
+ 0x00c0, 0x34c2, 0x2001, 0x4009, 0x0078, 0x34c9, 0x2001, 0x0001,
+ 0x0078, 0x34c9, 0x1078, 0x4499, 0x00c0, 0x34be, 0x6312, 0x6216,
+ 0xa006, 0xa005, 0x0d7f, 0x0e7f, 0x007c, 0x81ff, 0x00c0, 0x2b56,
+ 0x1078, 0x3518, 0x0040, 0x2b56, 0x6837, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x7824, 0xa005, 0x0040, 0x2b5a, 0xa096, 0x00ff, 0x0040,
+ 0x34e5, 0xa092, 0x0004, 0x00c8, 0x2b5a, 0x2010, 0x2d18, 0x1078,
+ 0x27c2, 0x0040, 0x2b56, 0x7007, 0x0003, 0x701b, 0x34f0, 0x007c,
+ 0x6830, 0xa086, 0x0100, 0x0040, 0x2b56, 0x0078, 0x2b2c, 0x7924,
+ 0xa18c, 0xff00, 0x810f, 0xa182, 0x0080, 0x0048, 0x2b5a, 0xa182,
+ 0x00ff, 0x00c8, 0x2b5a, 0x127e, 0x2091, 0x8000, 0x1078, 0x8a89,
+ 0x00c0, 0x3515, 0xa190, 0xa434, 0x2204, 0xa065, 0x0040, 0x3515,
+ 0x1078, 0x4235, 0x127f, 0x0078, 0x2b2c, 0x127f, 0x0078, 0x2b56,
+ 0x1078, 0x1381, 0x0040, 0x352f, 0xa006, 0x6802, 0x7010, 0xa005,
+ 0x00c0, 0x3527, 0x2d00, 0x7012, 0x7016, 0x0078, 0x352d, 0x7014,
+ 0x6802, 0x2060, 0x2d00, 0x6006, 0x7016, 0xad80, 0x000d, 0x007c,
+ 0x7924, 0x810f, 0xa18c, 0x00ff, 0x1078, 0x4501, 0x00c0, 0x353f,
+ 0x7e28, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0048, 0x3540, 0xa066,
+ 0x8cff, 0x007c, 0x7e24, 0x860f, 0xa18c, 0x00ff, 0x1078, 0x4501,
+ 0x00c0, 0x3550, 0xa6b4, 0x00ff, 0xa682, 0x4000, 0x0048, 0x3551,
+ 0xa066, 0x8cff, 0x007c, 0x017e, 0x7110, 0x81ff, 0x0040, 0x355e,
+ 0x2168, 0x6904, 0x1078, 0x139a, 0x0078, 0x3555, 0x7112, 0x7116,
+ 0x017f, 0x007c, 0x2031, 0x0001, 0x0078, 0x3568, 0x2031, 0x0000,
+ 0x2061, 0xa3d1, 0x6606, 0x6112, 0x600e, 0x6226, 0x632a, 0x642e,
+ 0x6532, 0x2c10, 0x1078, 0x13d1, 0x7007, 0x0002, 0x701b, 0x2b2c,
+ 0x007c, 0x0f7e, 0x127e, 0x2091, 0x8000, 0x2079, 0x0000, 0x2001,
+ 0xa38f, 0x2004, 0xa005, 0x00c0, 0x3594, 0x0068, 0x3594, 0x7818,
+ 0xd084, 0x00c0, 0x3594, 0x7a22, 0x7b26, 0x7c2a, 0x781b, 0x0001,
+ 0x2091, 0x4080, 0x0078, 0x35b9, 0x017e, 0x0c7e, 0x0e7e, 0x2071,
+ 0xa381, 0x7138, 0xa182, 0x0008, 0x0048, 0x35a2, 0x7030, 0x2060,
+ 0x0078, 0x35b3, 0x7030, 0xa0e0, 0x0008, 0xac82, 0xa3d1, 0x0048,
+ 0x35ab, 0x2061, 0xa391, 0x2c00, 0x7032, 0x81ff, 0x00c0, 0x35b1,
+ 0x7036, 0x8108, 0x713a, 0x2262, 0x6306, 0x640a, 0x0e7f, 0x0c7f,
+ 0x017f, 0x127f, 0x0f7f, 0x007c, 0x0e7e, 0x2071, 0xa381, 0x7038,
+ 0xa005, 0x0040, 0x35f5, 0x127e, 0x2091, 0x8000, 0x0068, 0x35f4,
+ 0x0f7e, 0x2079, 0x0000, 0x7818, 0xd084, 0x00c0, 0x35f3, 0x0c7e,
+ 0x7034, 0x2060, 0x2c04, 0x7822, 0x6004, 0x7826, 0x6008, 0x782a,
+ 0x781b, 0x0001, 0x2091, 0x4080, 0x7038, 0x8001, 0x703a, 0xa005,
+ 0x00c0, 0x35e9, 0x7033, 0xa391, 0x7037, 0xa391, 0x0c7f, 0x0078,
+ 0x35f3, 0xac80, 0x0008, 0xa0fa, 0xa3d1, 0x0048, 0x35f1, 0x2001,
+ 0xa391, 0x7036, 0x0c7f, 0x0f7f, 0x127f, 0x0e7f, 0x007c, 0x027e,
+ 0x2001, 0xa352, 0x2004, 0xd0c4, 0x0040, 0x3602, 0x2011, 0x8014,
+ 0x1078, 0x3579, 0x027f, 0x007c, 0x81ff, 0x00c0, 0x2b56, 0x127e,
+ 0x2091, 0x8000, 0x6030, 0xc08d, 0xc085, 0xc0ac, 0x6032, 0x1078,
+ 0x4171, 0x127f, 0x0078, 0x2b2c, 0x7824, 0x2008, 0xa18c, 0xfffd,
+ 0x00c0, 0x361f, 0x61d4, 0xa10d, 0x61d6, 0x0078, 0x2b2c, 0x0078,
+ 0x2b5a, 0x81ff, 0x00c0, 0x2b56, 0x6000, 0xa086, 0x0003, 0x00c0,
+ 0x2b56, 0x2001, 0xa352, 0x2004, 0xd0ac, 0x00c0, 0x2b56, 0x1078,
+ 0x3542, 0x0040, 0x2b5a, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006,
+ 0x00c0, 0x363e, 0x7828, 0xa005, 0x0040, 0x2b2c, 0x0c7e, 0x1078,
+ 0x3518, 0x0c7f, 0x0040, 0x2b56, 0x6837, 0x0000, 0x6833, 0x0000,
+ 0x6838, 0xc0fd, 0x683a, 0x1078, 0x8c4d, 0x0040, 0x2b56, 0x7007,
+ 0x0003, 0x701b, 0x3654, 0x007c, 0x6830, 0xa086, 0x0100, 0x0040,
+ 0x2b56, 0x0078, 0x2b2c, 0x2001, 0xa300, 0x2004, 0xa086, 0x0003,
+ 0x00c0, 0x2b56, 0x7f24, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x1078,
+ 0x3518, 0x0040, 0x2b56, 0x2009, 0x0000, 0x2031, 0x0000, 0x7023,
+ 0x0000, 0x702f, 0x0000, 0xad80, 0x0005, 0x7026, 0x20a0, 0x1078,
+ 0x4501, 0x00c0, 0x36d8, 0x6004, 0xa0c4, 0x00ff, 0xa8c6, 0x0006,
+ 0x0040, 0x3688, 0xa0c4, 0xff00, 0xa8c6, 0x0600, 0x00c0, 0x36d8,
+ 0x2001, 0xa352, 0x2004, 0xd0ac, 0x00c0, 0x3695, 0x1078, 0x47cb,
+ 0x00c0, 0x3695, 0xd79c, 0x0040, 0x36d8, 0xd794, 0x00c0, 0x369b,
+ 0xd784, 0x0040, 0x36a7, 0xac80, 0x0006, 0x2098, 0x3400, 0x20a9,
+ 0x0004, 0x53a3, 0x1078, 0x338c, 0xd794, 0x0040, 0x36b0, 0xac80,
+ 0x000a, 0x2098, 0x3400, 0x20a9, 0x0004, 0x53a3, 0x1078, 0x338c,
+ 0x21a2, 0xd794, 0x0040, 0x36d0, 0xac80, 0x0000, 0x2098, 0x94a0,
+ 0x20a9, 0x0002, 0x53a3, 0xac80, 0x0003, 0x20a6, 0x94a0, 0xac80,
+ 0x0004, 0x2098, 0x3400, 0x20a9, 0x0002, 0x53a3, 0x1078, 0x337e,
+ 0xac80, 0x0026, 0x2098, 0x20a9, 0x0002, 0x53a3, 0x0078, 0x36d1,
+ 0x94a0, 0xd794, 0x0040, 0x36d6, 0xa6b0, 0x000b, 0xa6b0, 0x0005,
+ 0x8108, 0xd78c, 0x0040, 0x36e2, 0xa186, 0x0100, 0x0040, 0x36f3,
+ 0x0078, 0x36e6, 0xa186, 0x007e, 0x0040, 0x36f3, 0xd794, 0x0040,
+ 0x36ed, 0xa686, 0x0020, 0x0078, 0x36ef, 0xa686, 0x0028, 0x0040,
+ 0x36fc, 0x0078, 0x3677, 0x86ff, 0x00c0, 0x36fa, 0x7120, 0x810b,
+ 0x0078, 0x2b2c, 0x702f, 0x0001, 0x711e, 0x7020, 0xa600, 0x7022,
+ 0x772a, 0x2061, 0xa3d1, 0x6007, 0x0000, 0x6612, 0x7024, 0x600e,
+ 0x6226, 0x632a, 0x642e, 0x6532, 0x2c10, 0x1078, 0x13d1, 0x7007,
+ 0x0002, 0x701b, 0x3714, 0x007c, 0x702c, 0xa005, 0x00c0, 0x3726,
+ 0x711c, 0x7024, 0x20a0, 0x7728, 0x2031, 0x0000, 0x2061, 0xa3d1,
+ 0x6224, 0x6328, 0x642c, 0x6530, 0x0078, 0x3677, 0x7120, 0x810b,
+ 0x0078, 0x2b2c, 0x2029, 0x007e, 0x7924, 0x7a28, 0x7b2c, 0x7c38,
+ 0xa184, 0xff00, 0x8007, 0xa0e2, 0x0020, 0x0048, 0x2b5a, 0xa502,
+ 0x0048, 0x2b5a, 0xa184, 0x00ff, 0xa0e2, 0x0020, 0x0048, 0x2b5a,
+ 0xa502, 0x0048, 0x2b5a, 0xa284, 0xff00, 0x8007, 0xa0e2, 0x0020,
+ 0x0048, 0x2b5a, 0xa502, 0x0048, 0x2b5a, 0xa284, 0x00ff, 0xa0e2,
+ 0x0020, 0x0048, 0x2b5a, 0xa502, 0x0048, 0x2b5a, 0xa384, 0xff00,
+ 0x8007, 0xa0e2, 0x0020, 0x0048, 0x2b5a, 0xa502, 0x0048, 0x2b5a,
+ 0xa384, 0x00ff, 0xa0e2, 0x0020, 0x0048, 0x2b5a, 0xa502, 0x0048,
+ 0x2b5a, 0xa484, 0xff00, 0x8007, 0xa0e2, 0x0020, 0x0048, 0x2b5a,
+ 0xa502, 0x0048, 0x2b5a, 0xa484, 0x00ff, 0xa0e2, 0x0020, 0x0048,
+ 0x2b5a, 0xa502, 0x0048, 0x2b5a, 0x2061, 0xa5a3, 0x6102, 0x6206,
+ 0x630a, 0x640e, 0x0078, 0x2b2c, 0x007e, 0x2001, 0xa352, 0x2004,
+ 0xd0cc, 0x007f, 0x007c, 0x007e, 0x2001, 0xa371, 0x2004, 0xd0bc,
+ 0x007f, 0x007c, 0x6160, 0x7a24, 0x6300, 0x82ff, 0x00c0, 0x379b,
+ 0x7926, 0x0078, 0x2b2c, 0x83ff, 0x00c0, 0x2b5a, 0x2001, 0xfff0,
+ 0xa200, 0x00c8, 0x2b5a, 0x2019, 0xffff, 0x6064, 0xa302, 0xa200,
+ 0x0048, 0x2b5a, 0x7926, 0x6262, 0x0078, 0x2b2c, 0x2001, 0xa300,
+ 0x2004, 0xa086, 0x0003, 0x00c0, 0x2b56, 0x7c28, 0x7d24, 0x7e38,
+ 0x7f2c, 0x1078, 0x3518, 0x0040, 0x2b56, 0x2009, 0x0000, 0x2019,
+ 0x0000, 0x7023, 0x0000, 0x702f, 0x0000, 0xad80, 0x0003, 0x7026,
+ 0x20a0, 0xa1e0, 0xa434, 0x2c64, 0x8cff, 0x0040, 0x37e8, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x0040, 0x37dd, 0x6004, 0xa084,
+ 0xff00, 0xa086, 0x0600, 0x00c0, 0x37e8, 0x6014, 0x20a2, 0x94a0,
+ 0x6010, 0x8007, 0xa105, 0x8007, 0x20a2, 0x94a0, 0xa398, 0x0002,
+ 0x8108, 0xa182, 0x00ff, 0x0040, 0x37f3, 0xa386, 0x002a, 0x0040,
+ 0x37fc, 0x0078, 0x37c9, 0x83ff, 0x00c0, 0x37fa, 0x7120, 0x810c,
+ 0x0078, 0x2b2c, 0x702f, 0x0001, 0x711e, 0x7020, 0xa300, 0x7022,
+ 0x2061, 0xa3d1, 0x6007, 0x0000, 0x6312, 0x7024, 0x600e, 0x6426,
+ 0x652a, 0x662e, 0x6732, 0x2c10, 0x1078, 0x13d1, 0x7007, 0x0002,
+ 0x701b, 0x3813, 0x007c, 0x702c, 0xa005, 0x00c0, 0x3824, 0x711c,
+ 0x7024, 0x20a0, 0x2019, 0x0000, 0x2061, 0xa3d1, 0x6424, 0x6528,
+ 0x662c, 0x6730, 0x0078, 0x37c9, 0x7120, 0x810c, 0x0078, 0x2b2c,
+ 0x81ff, 0x00c0, 0x2b56, 0x60c8, 0xd09c, 0x0040, 0x2b56, 0x1078,
+ 0x3518, 0x0040, 0x2b56, 0x7924, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x1078, 0x3562, 0x701b, 0x383d, 0x007c, 0x0d7e, 0xade8, 0x000d,
+ 0x6828, 0xa0be, 0x7000, 0x0040, 0x3850, 0xa0be, 0x7100, 0x0040,
+ 0x3850, 0xa0be, 0x7200, 0x0040, 0x3850, 0x0d7f, 0x0078, 0x2b5a,
+ 0x6820, 0x6924, 0x1078, 0x24e3, 0x00c0, 0x387b, 0x1078, 0x4499,
+ 0x00c0, 0x387b, 0x7122, 0x6612, 0x6516, 0x6e18, 0x0c7e, 0x1078,
+ 0x3518, 0x0040, 0x387b, 0x1078, 0x3518, 0x0040, 0x387b, 0x0c7f,
+ 0x0d7f, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x6823, 0x0000,
+ 0x6804, 0x2068, 0x1078, 0x8bbd, 0x0040, 0x2b56, 0x7007, 0x0003,
+ 0x701b, 0x387e, 0x007c, 0x0d7f, 0x0078, 0x2b56, 0x7120, 0x1078,
+ 0x2921, 0x6820, 0xa086, 0x8001, 0x0040, 0x2b56, 0x2d00, 0x701e,
+ 0x6804, 0xa080, 0x0002, 0x007e, 0x20a9, 0x002a, 0x2098, 0x20a0,
+ 0x1078, 0x41be, 0x007f, 0xade8, 0x000d, 0x6a08, 0x6b0c, 0x6c10,
+ 0x6d14, 0x2061, 0xa3d1, 0x6007, 0x0000, 0x6e00, 0x6f28, 0xa7c6,
+ 0x7000, 0x00c0, 0x38a5, 0x0078, 0x38a9, 0xa7c6, 0x7100, 0x00c0,
+ 0x38b1, 0xa6c2, 0x0004, 0x0048, 0x2b5a, 0x2009, 0x0004, 0x0078,
+ 0x3566, 0xa7c6, 0x7200, 0x00c0, 0x2b5a, 0xa6c2, 0x0054, 0x0048,
+ 0x2b5a, 0x600e, 0x6013, 0x002a, 0x6226, 0x632a, 0x642e, 0x6532,
+ 0x2c10, 0x1078, 0x13d1, 0x7007, 0x0002, 0x701b, 0x38c8, 0x007c,
+ 0x701c, 0x2068, 0x6804, 0xa080, 0x0001, 0x2004, 0xa080, 0x0002,
+ 0x007e, 0x20a9, 0x002a, 0x2098, 0x20a0, 0x1078, 0x41be, 0x007f,
+ 0x2009, 0x002a, 0x2061, 0xa3d1, 0x6224, 0x6328, 0x642c, 0x6530,
+ 0x0078, 0x3566, 0x81ff, 0x00c0, 0x2b56, 0x1078, 0x3530, 0x0040,
+ 0x2b5a, 0x1078, 0x45a7, 0x0040, 0x2b56, 0x1078, 0x4710, 0x0078,
+ 0x2b2c, 0x7824, 0xd084, 0x0040, 0x3150, 0x1078, 0x3542, 0x0040,
+ 0x2b5a, 0x0c7e, 0x1078, 0x3518, 0x0c7f, 0x00c0, 0x3903, 0x2009,
+ 0x0002, 0x0078, 0x2b56, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006,
+ 0x0040, 0x3910, 0xa08e, 0x0004, 0x0040, 0x3910, 0xa08e, 0x0005,
+ 0x00c0, 0x3934, 0x2001, 0xa352, 0x2004, 0xd0b4, 0x0040, 0x3185,
+ 0x6000, 0xd08c, 0x00c0, 0x3185, 0x6837, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x1078, 0x8bd9, 0x00c0, 0x3929, 0x2009, 0x0003, 0x0078,
+ 0x2b56, 0x7007, 0x0003, 0x701b, 0x392e, 0x007c, 0x1078, 0x3542,
+ 0x0040, 0x2b5a, 0x0078, 0x3185, 0x2009, 0xa32e, 0x210c, 0x81ff,
+ 0x0040, 0x393e, 0x2009, 0x0001, 0x0078, 0x2b56, 0x2001, 0xa300,
+ 0x2004, 0xa086, 0x0003, 0x0040, 0x3949, 0x2009, 0x0007, 0x0078,
+ 0x2b56, 0x2001, 0xa352, 0x2004, 0xd0ac, 0x0040, 0x3953, 0x2009,
+ 0x0008, 0x0078, 0x2b56, 0x609c, 0xd0a4, 0x00c0, 0x395a, 0xd0ac,
+ 0x00c0, 0x3185, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x1078, 0x8c4d, 0x00c0, 0x3969, 0x2009, 0x0003, 0x0078,
+ 0x2b56, 0x7007, 0x0003, 0x701b, 0x396e, 0x007c, 0x6830, 0xa086,
+ 0x0100, 0x00c0, 0x3977, 0x2009, 0x0004, 0x0078, 0x2b56, 0x1078,
+ 0x3542, 0x0040, 0x2b5a, 0x0078, 0x3912, 0x81ff, 0x2009, 0x0001,
+ 0x00c0, 0x2b56, 0x6000, 0xa086, 0x0003, 0x2009, 0x0007, 0x00c0,
+ 0x2b56, 0x2001, 0xa352, 0x2004, 0xd0ac, 0x2009, 0x0008, 0x00c0,
+ 0x2b56, 0x1078, 0x3542, 0x0040, 0x2b5a, 0x6004, 0xa084, 0x00ff,
+ 0xa086, 0x0006, 0x2009, 0x0009, 0x00c0, 0x2b56, 0x0c7e, 0x1078,
+ 0x3518, 0x0c7f, 0x2009, 0x0002, 0x0040, 0x2b56, 0x6837, 0x0000,
+ 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x7928, 0xa194, 0xff00,
+ 0xa18c, 0x00ff, 0xa006, 0x82ff, 0x00c0, 0x39bc, 0xc0ed, 0x6952,
+ 0x792c, 0x6956, 0x0078, 0x39c5, 0xa28e, 0x0100, 0x00c0, 0x2b5a,
+ 0xc0e5, 0x6853, 0x0000, 0x6857, 0x0000, 0x683e, 0x1078, 0x8df6,
+ 0x2009, 0x0003, 0x0040, 0x2b56, 0x7007, 0x0003, 0x701b, 0x39d1,
+ 0x007c, 0x6830, 0xa086, 0x0100, 0x2009, 0x0004, 0x0040, 0x2b56,
+ 0x0078, 0x2b2c, 0x81ff, 0x2009, 0x0001, 0x00c0, 0x2b56, 0x6000,
+ 0xa086, 0x0003, 0x2009, 0x0007, 0x00c0, 0x2b56, 0x1078, 0x3542,
+ 0x0040, 0x2b5a, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x2009,
+ 0x0009, 0x00c0, 0x2b56, 0x0c7e, 0x1078, 0x3518, 0x0c7f, 0x2009,
+ 0x0002, 0x0040, 0x2b56, 0xad80, 0x000f, 0x2009, 0x0008, 0x7a2c,
+ 0x7b28, 0x7c3c, 0x7d38, 0x1078, 0x3562, 0x701b, 0x3a08, 0x007c,
+ 0x0d7e, 0xade8, 0x000f, 0x6800, 0xa086, 0x0500, 0x00c0, 0x3a1b,
+ 0x6804, 0xa005, 0x00c0, 0x3a1b, 0x6808, 0xa084, 0xff00, 0x00c0,
+ 0x3a1b, 0x0078, 0x3a1e, 0x0d7f, 0x00c0, 0x2b5a, 0x0d7f, 0x6837,
+ 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x0c7e, 0x1078,
+ 0x3542, 0x00c0, 0x3a2e, 0x0c7f, 0x0078, 0x2b5a, 0x1078, 0x8e52,
+ 0x2009, 0x0003, 0x0c7f, 0x0040, 0x2b56, 0x7007, 0x0003, 0x701b,
+ 0x3a3a, 0x007c, 0x6830, 0xa086, 0x0100, 0x2009, 0x0004, 0x0040,
+ 0x2b56, 0x0078, 0x2b2c, 0x127e, 0x0c7e, 0x0e7e, 0x2061, 0x0100,
+ 0x2071, 0xa300, 0x6044, 0xd0a4, 0x00c0, 0x3a6c, 0xd084, 0x0040,
+ 0x3a55, 0x1078, 0x3bcc, 0x0078, 0x3a68, 0xd08c, 0x0040, 0x3a5c,
+ 0x1078, 0x3ae3, 0x0078, 0x3a68, 0xd094, 0x0040, 0x3a63, 0x1078,
+ 0x3ab7, 0x0078, 0x3a68, 0xd09c, 0x0040, 0x3a68, 0x1078, 0x3a76,
+ 0x0e7f, 0x0c7f, 0x127f, 0x007c, 0x017e, 0x6128, 0xd19c, 0x00c0,
+ 0x3a73, 0xc19d, 0x612a, 0x017f, 0x0078, 0x3a68, 0x624c, 0xa286,
+ 0xf0f0, 0x00c0, 0x3a87, 0x6048, 0xa086, 0xf0f0, 0x0040, 0x3a87,
+ 0x624a, 0x6043, 0x0090, 0x6043, 0x0010, 0x0078, 0x3ab6, 0xa294,
+ 0xff00, 0xa296, 0xf700, 0x0040, 0x3a9c, 0x7134, 0xd1a4, 0x00c0,
+ 0x3a9c, 0x6240, 0xa294, 0x0010, 0x0040, 0x3a9c, 0x2009, 0x00f7,
+ 0x1078, 0x41de, 0x0078, 0x3ab6, 0x6043, 0x0040, 0x6043, 0x0000,
+ 0x7073, 0x0000, 0x708b, 0x0001, 0x70af, 0x0000, 0x70cb, 0x0000,
+ 0x2009, 0xa9c0, 0x200b, 0x0000, 0x7083, 0x0000, 0x7077, 0x000f,
+ 0x2009, 0x000f, 0x2011, 0x4122, 0x1078, 0x596c, 0x007c, 0x157e,
+ 0x7074, 0xa005, 0x00c0, 0x3ae1, 0x2011, 0x4122, 0x1078, 0x58d4,
+ 0x6040, 0xa094, 0x0010, 0xa285, 0x0020, 0x6042, 0x20a9, 0x00c8,
+ 0x6044, 0xd08c, 0x00c0, 0x3ada, 0x00f0, 0x3ac8, 0x6242, 0x7087,
+ 0x0000, 0x6040, 0xa094, 0x0010, 0xa285, 0x0080, 0x6042, 0x6242,
+ 0x0078, 0x3ae1, 0x6242, 0x7087, 0x0000, 0x707b, 0x0000, 0x0078,
+ 0x3ae1, 0x157f, 0x007c, 0x7078, 0xa08a, 0x0003, 0x00c8, 0x3aec,
+ 0x1079, 0x3aef, 0x0078, 0x3aee, 0x1078, 0x1328, 0x007c, 0x3af2,
+ 0x3b41, 0x3bcb, 0x0f7e, 0x707b, 0x0001, 0x20e1, 0xa000, 0x20e1,
+ 0x8700, 0x1078, 0x218b, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2079,
+ 0xa800, 0x207b, 0x2200, 0x7807, 0x00ef, 0x780b, 0x0000, 0x780f,
+ 0x00ef, 0x7813, 0x0138, 0x7817, 0x0000, 0x781b, 0x0000, 0x781f,
+ 0x0000, 0x7823, 0xffff, 0x7827, 0xffff, 0x782b, 0x0000, 0x782f,
+ 0x0000, 0x2079, 0xa80c, 0x207b, 0x1101, 0x7807, 0x0000, 0x2099,
+ 0xa305, 0x20a1, 0xa80e, 0x20a9, 0x0004, 0x53a3, 0x2079, 0xa812,
+ 0x207b, 0x0000, 0x7807, 0x0000, 0x2099, 0xa800, 0x20a1, 0x020b,
+ 0x20a9, 0x0014, 0x53a6, 0x60c3, 0x000c, 0x600f, 0x0000, 0x1078,
+ 0x4158, 0x0f7f, 0x707f, 0x0000, 0x6043, 0x0008, 0x6043, 0x0000,
+ 0x007c, 0x0d7e, 0x707c, 0x707f, 0x0000, 0xa025, 0x0040, 0x3bb5,
+ 0x6020, 0xd0b4, 0x00c0, 0x3bb3, 0x7188, 0x81ff, 0x0040, 0x3ba2,
+ 0xa486, 0x000c, 0x00c0, 0x3bad, 0xa480, 0x0018, 0x8004, 0x20a8,
+ 0x2011, 0xa880, 0x2019, 0xa800, 0x220c, 0x2304, 0xa106, 0x00c0,
+ 0x3b79, 0x8210, 0x8318, 0x00f0, 0x3b5c, 0x6043, 0x0004, 0x608b,
+ 0xbc94, 0x608f, 0xf0f0, 0x6043, 0x0006, 0x707b, 0x0002, 0x7087,
+ 0x0002, 0x2009, 0x07d0, 0x2011, 0x4129, 0x1078, 0x596c, 0x0078,
+ 0x3bb3, 0x2069, 0xa880, 0x6930, 0xa18e, 0x1101, 0x00c0, 0x3bad,
+ 0x6834, 0xa005, 0x00c0, 0x3bad, 0x6900, 0xa18c, 0x00ff, 0x00c0,
+ 0x3b8d, 0x6804, 0xa005, 0x0040, 0x3ba2, 0x2011, 0xa88e, 0x2019,
+ 0xa305, 0x20a9, 0x0004, 0x220c, 0x2304, 0xa102, 0x0048, 0x3ba0,
+ 0x00c0, 0x3bad, 0x8210, 0x8318, 0x00f0, 0x3b93, 0x0078, 0x3bad,
+ 0x708b, 0x0000, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xa880,
+ 0x20a1, 0x020b, 0x20a9, 0x0014, 0x53a6, 0x6043, 0x0008, 0x6043,
+ 0x0000, 0x0078, 0x3bb5, 0x0d7f, 0x007c, 0x6020, 0xd0b4, 0x00c0,
+ 0x3bb3, 0x60c3, 0x000c, 0x2011, 0xa5b5, 0x2013, 0x0000, 0x707f,
+ 0x0000, 0x20e1, 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x1078,
+ 0x6c38, 0x0078, 0x3bb3, 0x007c, 0x7084, 0xa08a, 0x001d, 0x00c8,
+ 0x3bd5, 0x1079, 0x3bd8, 0x0078, 0x3bd7, 0x1078, 0x1328, 0x007c,
+ 0x3c02, 0x3c11, 0x3c40, 0x3c59, 0x3c85, 0x3cb1, 0x3cdd, 0x3d13,
+ 0x3d3f, 0x3d67, 0x3daa, 0x3dd4, 0x3df6, 0x3e0c, 0x3e32, 0x3e45,
+ 0x3e4e, 0x3e7e, 0x3eaa, 0x3ed6, 0x3f02, 0x3f38, 0x3f7d, 0x3fac,
+ 0x3fce, 0x4010, 0x4036, 0x404f, 0x4050, 0x0c7e, 0x2061, 0xa300,
+ 0x6003, 0x0007, 0x2061, 0x0100, 0x6004, 0xa084, 0xfff9, 0x6006,
+ 0x0c7f, 0x007c, 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043, 0x0002,
+ 0x7087, 0x0001, 0x2009, 0x07d0, 0x2011, 0x4129, 0x1078, 0x596c,
+ 0x007c, 0x0f7e, 0x707c, 0xa086, 0x0014, 0x00c0, 0x3c3e, 0x6043,
+ 0x0000, 0x6020, 0xd0b4, 0x00c0, 0x3c3e, 0x2079, 0xa880, 0x7a30,
+ 0xa296, 0x1102, 0x00c0, 0x3c3c, 0x7834, 0xa005, 0x00c0, 0x3c3c,
+ 0x7a38, 0xd2fc, 0x0040, 0x3c32, 0x70ac, 0xa005, 0x00c0, 0x3c32,
+ 0x70af, 0x0001, 0x2011, 0x4129, 0x1078, 0x58d4, 0x7087, 0x0010,
+ 0x1078, 0x3e4e, 0x0078, 0x3c3e, 0x1078, 0x4171, 0x0f7f, 0x007c,
+ 0x7087, 0x0003, 0x6043, 0x0004, 0x2011, 0x4129, 0x1078, 0x58d4,
+ 0x1078, 0x41c6, 0x20a3, 0x1102, 0x20a3, 0x0000, 0x20a9, 0x000a,
+ 0x20a3, 0x0000, 0x00f0, 0x3c50, 0x60c3, 0x0014, 0x1078, 0x4158,
+ 0x007c, 0x0f7e, 0x707c, 0xa005, 0x0040, 0x3c83, 0x2011, 0x4129,
+ 0x1078, 0x58d4, 0xa086, 0x0014, 0x00c0, 0x3c81, 0x2079, 0xa880,
+ 0x7a30, 0xa296, 0x1102, 0x00c0, 0x3c81, 0x7834, 0xa005, 0x00c0,
+ 0x3c81, 0x7a38, 0xd2fc, 0x0040, 0x3c7b, 0x70ac, 0xa005, 0x00c0,
+ 0x3c7b, 0x70af, 0x0001, 0x7087, 0x0004, 0x1078, 0x3c85, 0x0078,
+ 0x3c83, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x7087, 0x0005, 0x1078,
+ 0x41c6, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430, 0x2011, 0xa88e,
+ 0x1078, 0x4211, 0x00c0, 0x3ca3, 0x7070, 0xa005, 0x00c0, 0x3ca3,
+ 0x714c, 0xa186, 0xffff, 0x0040, 0x3ca3, 0x1078, 0x40ea, 0x0040,
+ 0x3ca3, 0x1078, 0x41f5, 0x20a9, 0x0008, 0x2298, 0x26a0, 0x53a6,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078, 0x4158,
+ 0x007c, 0x0f7e, 0x707c, 0xa005, 0x0040, 0x3cdb, 0x2011, 0x4129,
+ 0x1078, 0x58d4, 0xa086, 0x0014, 0x00c0, 0x3cd9, 0x2079, 0xa880,
+ 0x7a30, 0xa296, 0x1103, 0x00c0, 0x3cd9, 0x7834, 0xa005, 0x00c0,
+ 0x3cd9, 0x7a38, 0xd2fc, 0x0040, 0x3cd3, 0x70ac, 0xa005, 0x00c0,
+ 0x3cd3, 0x70af, 0x0001, 0x7087, 0x0006, 0x1078, 0x3cdd, 0x0078,
+ 0x3cdb, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x7087, 0x0007, 0x1078,
+ 0x41c6, 0x20a3, 0x1104, 0x20a3, 0x0000, 0x3430, 0x2011, 0xa88e,
+ 0x1078, 0x4211, 0x00c0, 0x3d05, 0x7070, 0xa005, 0x00c0, 0x3d05,
+ 0x7150, 0xa186, 0xffff, 0x0040, 0x3d05, 0xa180, 0x293f, 0x200c,
+ 0xa18c, 0xff00, 0x810f, 0x1078, 0x40ea, 0x0040, 0x3d05, 0x1078,
+ 0x378b, 0x0040, 0x3d05, 0x1078, 0x2500, 0x20a9, 0x0008, 0x2298,
+ 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x1078, 0x4158, 0x007c, 0x0f7e, 0x707c, 0xa005, 0x0040, 0x3d3d,
+ 0x2011, 0x4129, 0x1078, 0x58d4, 0xa086, 0x0014, 0x00c0, 0x3d3b,
+ 0x2079, 0xa880, 0x7a30, 0xa296, 0x1104, 0x00c0, 0x3d3b, 0x7834,
+ 0xa005, 0x00c0, 0x3d3b, 0x7a38, 0xd2fc, 0x0040, 0x3d35, 0x70ac,
+ 0xa005, 0x00c0, 0x3d35, 0x70af, 0x0001, 0x7087, 0x0008, 0x1078,
+ 0x3d3f, 0x0078, 0x3d3d, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x7087,
+ 0x0009, 0x1078, 0x41c6, 0x20a3, 0x1105, 0x20a3, 0x0100, 0x3430,
+ 0x1078, 0x4211, 0x00c0, 0x3d58, 0x7070, 0xa005, 0x00c0, 0x3d58,
+ 0x1078, 0x4051, 0x00c0, 0x3d62, 0xa085, 0x0001, 0x1078, 0x2500,
+ 0x20a9, 0x0008, 0x2099, 0xa88e, 0x26a0, 0x53a6, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078, 0x4158, 0x007c, 0x0f7e,
+ 0x707c, 0xa005, 0x0040, 0x3da8, 0x2011, 0x4129, 0x1078, 0x58d4,
+ 0xa086, 0x0014, 0x00c0, 0x3da6, 0x2079, 0xa880, 0x7a30, 0xa296,
+ 0x1105, 0x00c0, 0x3da6, 0x7834, 0x2011, 0x0100, 0xa21e, 0x00c0,
+ 0x3d91, 0x7a38, 0xd2fc, 0x0040, 0x3d8b, 0x70ac, 0xa005, 0x00c0,
+ 0x3d8b, 0x70af, 0x0001, 0x7087, 0x000a, 0x1078, 0x3daa, 0x0078,
+ 0x3da8, 0xa005, 0x00c0, 0x3da6, 0x7a38, 0xd2fc, 0x0040, 0x3d9e,
+ 0x70ac, 0xa005, 0x00c0, 0x3d9e, 0x70af, 0x0001, 0x7083, 0x0000,
+ 0x7087, 0x000e, 0x1078, 0x3e32, 0x0078, 0x3da8, 0x1078, 0x4171,
+ 0x0f7f, 0x007c, 0x7087, 0x000b, 0x2011, 0xa80e, 0x22a0, 0x20a9,
+ 0x0040, 0x2019, 0xffff, 0x43a4, 0x20a9, 0x0002, 0x2009, 0x0000,
+ 0x41a4, 0x1078, 0x41c6, 0x20a3, 0x1106, 0x20a3, 0x0000, 0x1078,
+ 0x4211, 0x0040, 0x3dc7, 0x2013, 0x0000, 0x0078, 0x3dcb, 0x6030,
+ 0xa085, 0x0100, 0x2012, 0x2298, 0x20a9, 0x0042, 0x53a6, 0x60c3,
+ 0x0084, 0x1078, 0x4158, 0x007c, 0x0f7e, 0x707c, 0xa005, 0x0040,
+ 0x3df4, 0x2011, 0x4129, 0x1078, 0x58d4, 0xa086, 0x0084, 0x00c0,
+ 0x3df2, 0x2079, 0xa880, 0x7a30, 0xa296, 0x1106, 0x00c0, 0x3df2,
+ 0x7834, 0xa005, 0x00c0, 0x3df2, 0x7087, 0x000c, 0x1078, 0x3df6,
+ 0x0078, 0x3df4, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x7087, 0x000d,
+ 0x1078, 0x41c6, 0x20a3, 0x1107, 0x20a3, 0x0000, 0x2099, 0xa88e,
+ 0x20a9, 0x0040, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0084, 0x1078, 0x4158, 0x007c, 0x0f7e, 0x707c, 0xa005, 0x0040,
+ 0x3e30, 0x2011, 0x4129, 0x1078, 0x58d4, 0xa086, 0x0084, 0x00c0,
+ 0x3e2e, 0x2079, 0xa880, 0x7a30, 0xa296, 0x1107, 0x00c0, 0x3e2e,
+ 0x7834, 0xa005, 0x00c0, 0x3e2e, 0x7083, 0x0001, 0x1078, 0x41b8,
+ 0x7087, 0x000e, 0x1078, 0x3e32, 0x0078, 0x3e30, 0x1078, 0x4171,
+ 0x0f7f, 0x007c, 0x7087, 0x000f, 0x707f, 0x0000, 0x608b, 0xbc85,
+ 0x608f, 0xb5b5, 0x6043, 0x0005, 0x6043, 0x0004, 0x2009, 0x07d0,
+ 0x2011, 0x4129, 0x1078, 0x58c7, 0x007c, 0x707c, 0xa005, 0x0040,
+ 0x3e4d, 0x2011, 0x4129, 0x1078, 0x58d4, 0x007c, 0x7087, 0x0011,
+ 0x1078, 0x4211, 0x00c0, 0x3e67, 0x7168, 0x81ff, 0x0040, 0x3e67,
+ 0x2009, 0x0000, 0x706c, 0xa084, 0x00ff, 0x1078, 0x24e3, 0xa186,
+ 0x0080, 0x0040, 0x3e67, 0x2011, 0xa88e, 0x1078, 0x40ea, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x2099, 0xa880, 0x20a1, 0x020b, 0x747c,
+ 0xa480, 0x0018, 0xa080, 0x0007, 0xa084, 0x03f8, 0x8004, 0x20a8,
+ 0x53a6, 0x60c3, 0x0014, 0x1078, 0x4158, 0x007c, 0x0f7e, 0x707c,
+ 0xa005, 0x0040, 0x3ea8, 0x2011, 0x4129, 0x1078, 0x58d4, 0xa086,
+ 0x0014, 0x00c0, 0x3ea6, 0x2079, 0xa880, 0x7a30, 0xa296, 0x1103,
+ 0x00c0, 0x3ea6, 0x7834, 0xa005, 0x00c0, 0x3ea6, 0x7a38, 0xd2fc,
+ 0x0040, 0x3ea0, 0x70ac, 0xa005, 0x00c0, 0x3ea0, 0x70af, 0x0001,
+ 0x7087, 0x0012, 0x1078, 0x3eaa, 0x0078, 0x3ea8, 0x1078, 0x4171,
+ 0x0f7f, 0x007c, 0x7087, 0x0013, 0x1078, 0x41d2, 0x20a3, 0x1103,
+ 0x20a3, 0x0000, 0x3430, 0x2011, 0xa88e, 0x1078, 0x4211, 0x00c0,
+ 0x3ec8, 0x7070, 0xa005, 0x00c0, 0x3ec8, 0x714c, 0xa186, 0xffff,
+ 0x0040, 0x3ec8, 0x1078, 0x40ea, 0x0040, 0x3ec8, 0x1078, 0x41f5,
+ 0x20a9, 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x0014, 0x1078, 0x4158, 0x007c, 0x0f7e, 0x707c,
+ 0xa005, 0x0040, 0x3f00, 0x2011, 0x4129, 0x1078, 0x58d4, 0xa086,
+ 0x0014, 0x00c0, 0x3efe, 0x2079, 0xa880, 0x7a30, 0xa296, 0x1104,
+ 0x00c0, 0x3efe, 0x7834, 0xa005, 0x00c0, 0x3efe, 0x7a38, 0xd2fc,
+ 0x0040, 0x3ef8, 0x70ac, 0xa005, 0x00c0, 0x3ef8, 0x70af, 0x0001,
+ 0x7087, 0x0014, 0x1078, 0x3f02, 0x0078, 0x3f00, 0x1078, 0x4171,
+ 0x0f7f, 0x007c, 0x7087, 0x0015, 0x1078, 0x41d2, 0x20a3, 0x1104,
+ 0x20a3, 0x0000, 0x3430, 0x2011, 0xa88e, 0x1078, 0x4211, 0x00c0,
+ 0x3f2a, 0x7070, 0xa005, 0x00c0, 0x3f2a, 0x7150, 0xa186, 0xffff,
+ 0x0040, 0x3f2a, 0xa180, 0x293f, 0x200c, 0xa18c, 0xff00, 0x810f,
+ 0x1078, 0x40ea, 0x0040, 0x3f2a, 0x1078, 0x378b, 0x0040, 0x3f2a,
+ 0x1078, 0x2500, 0x20a9, 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078, 0x4158, 0x007c,
+ 0x0f7e, 0x707c, 0xa005, 0x0040, 0x3f7b, 0x2011, 0x4129, 0x1078,
+ 0x58d4, 0xa086, 0x0014, 0x00c0, 0x3f79, 0x2079, 0xa880, 0x7a30,
+ 0xa296, 0x1105, 0x00c0, 0x3f79, 0x7834, 0x2011, 0x0100, 0xa21e,
+ 0x00c0, 0x3f5e, 0x7a38, 0xd2fc, 0x0040, 0x3f5c, 0x70ac, 0xa005,
+ 0x00c0, 0x3f5c, 0x70af, 0x0001, 0x0078, 0x3f6d, 0xa005, 0x00c0,
+ 0x3f79, 0x7a38, 0xd2fc, 0x0040, 0x3f6b, 0x70ac, 0xa005, 0x00c0,
+ 0x3f6b, 0x70af, 0x0001, 0x7083, 0x0000, 0x7a38, 0xd2f4, 0x0040,
+ 0x3f73, 0x70cb, 0x0008, 0x7087, 0x0016, 0x1078, 0x3f7d, 0x0078,
+ 0x3f7b, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x2099, 0xa880, 0x20a1, 0x020b, 0x20a9, 0x000e, 0x53a6,
+ 0x3430, 0x2011, 0xa88e, 0x7087, 0x0017, 0x1078, 0x4211, 0x00c0,
+ 0x3f9d, 0x7070, 0xa005, 0x00c0, 0x3f9d, 0x1078, 0x4051, 0x00c0,
+ 0x3fa7, 0xa085, 0x0001, 0x1078, 0x2500, 0x20a9, 0x0008, 0x2099,
+ 0xa88e, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0014, 0x1078, 0x4158, 0x007c, 0x0f7e, 0x707c, 0xa005, 0x0040,
+ 0x3fcc, 0x2011, 0x4129, 0x1078, 0x58d4, 0xa086, 0x0084, 0x00c0,
+ 0x3fca, 0x2079, 0xa880, 0x7a30, 0xa296, 0x1106, 0x00c0, 0x3fca,
+ 0x7834, 0xa005, 0x00c0, 0x3fca, 0x7087, 0x0018, 0x1078, 0x3fce,
+ 0x0078, 0x3fcc, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x7087, 0x0019,
+ 0x1078, 0x41d2, 0x20a3, 0x1106, 0x20a3, 0x0000, 0x3430, 0x2099,
+ 0xa88e, 0x2039, 0xa80e, 0x27a0, 0x20a9, 0x0040, 0x53a3, 0x1078,
+ 0x4211, 0x00c0, 0x4002, 0x2728, 0x2514, 0x8207, 0xa084, 0x00ff,
+ 0x8000, 0x2018, 0xa294, 0x00ff, 0x8007, 0xa205, 0x202a, 0x6030,
+ 0x2310, 0x8214, 0xa2a0, 0xa80e, 0x2414, 0xa38c, 0x0001, 0x0040,
+ 0x3ffd, 0xa294, 0xff00, 0x0078, 0x4000, 0xa294, 0x00ff, 0x8007,
+ 0xa215, 0x2222, 0x2798, 0x26a0, 0x20a9, 0x0040, 0x53a6, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0084, 0x1078, 0x4158, 0x007c,
+ 0x0f7e, 0x707c, 0xa005, 0x0040, 0x4034, 0x2011, 0x4129, 0x1078,
+ 0x58d4, 0xa086, 0x0084, 0x00c0, 0x4032, 0x2079, 0xa880, 0x7a30,
+ 0xa296, 0x1107, 0x00c0, 0x4032, 0x7834, 0xa005, 0x00c0, 0x4032,
+ 0x7083, 0x0001, 0x1078, 0x41b8, 0x7087, 0x001a, 0x1078, 0x4036,
+ 0x0078, 0x4034, 0x1078, 0x4171, 0x0f7f, 0x007c, 0x7087, 0x001b,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xa880, 0x20a1, 0x020b,
+ 0x747c, 0xa480, 0x0018, 0xa080, 0x0007, 0xa084, 0x03f8, 0x8004,
+ 0x20a8, 0x53a6, 0x60c3, 0x0084, 0x1078, 0x4158, 0x007c, 0x007c,
+ 0x007c, 0x087e, 0x097e, 0x2029, 0xa352, 0x252c, 0x20a9, 0x0008,
+ 0x2041, 0xa80e, 0x28a0, 0x2099, 0xa88e, 0x53a3, 0x20a9, 0x0008,
+ 0x2011, 0x0007, 0xd5d4, 0x0040, 0x4067, 0x2011, 0x0000, 0x2800,
+ 0xa200, 0x200c, 0xa1a6, 0xffff, 0x00c0, 0x4079, 0xd5d4, 0x0040,
+ 0x4074, 0x8210, 0x0078, 0x4075, 0x8211, 0x00f0, 0x4067, 0x0078,
+ 0x40e1, 0x82ff, 0x00c0, 0x408b, 0xd5d4, 0x0040, 0x4085, 0xa1a6,
+ 0x3fff, 0x0040, 0x4071, 0x0078, 0x4089, 0xa1a6, 0x3fff, 0x0040,
+ 0x40e1, 0xa18d, 0xc000, 0x20a9, 0x0010, 0x2019, 0x0001, 0xd5d4,
+ 0x0040, 0x4094, 0x2019, 0x0010, 0x2120, 0xd5d4, 0x0040, 0x409b,
+ 0x8423, 0x0078, 0x409c, 0x8424, 0x00c8, 0x40a9, 0xd5d4, 0x0040,
+ 0x40a4, 0x8319, 0x0078, 0x40a5, 0x8318, 0x00f0, 0x4095, 0x0078,
+ 0x40e1, 0x23a8, 0x2021, 0x0001, 0x8426, 0x8425, 0x00f0, 0x40ad,
+ 0x2328, 0x8529, 0xa2be, 0x0007, 0x0040, 0x40c1, 0x007e, 0x2039,
+ 0x0007, 0x2200, 0xa73a, 0x007f, 0x27a8, 0xa5a8, 0x0010, 0x00f0,
+ 0x40bd, 0x754e, 0xa5c8, 0x293f, 0x292c, 0xa5ac, 0x00ff, 0x6532,
+ 0x60e7, 0x0000, 0x65ea, 0x706b, 0x0000, 0x756e, 0x2018, 0x2304,
+ 0xa405, 0x201a, 0x7073, 0x0001, 0x26a0, 0x2898, 0x20a9, 0x0008,
+ 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0xa085, 0x0001, 0x0078,
+ 0x40e7, 0xa006, 0x0078, 0x40e7, 0xa006, 0x1078, 0x1328, 0x097f,
+ 0x087f, 0x007c, 0x2118, 0x2021, 0x0000, 0x2001, 0x0007, 0xa39a,
+ 0x0010, 0x0048, 0x40f7, 0x8420, 0x8001, 0x0078, 0x40ef, 0x2118,
+ 0x84ff, 0x0040, 0x4100, 0xa39a, 0x0010, 0x8421, 0x00c0, 0x40fb,
+ 0x2021, 0x0001, 0x83ff, 0x0040, 0x4109, 0x8423, 0x8319, 0x00c0,
+ 0x4105, 0xa238, 0x2704, 0xa42c, 0x00c0, 0x4121, 0xa405, 0x203a,
+ 0x714e, 0xa1a0, 0x293f, 0x242c, 0xa5ac, 0x00ff, 0x6532, 0x60e7,
+ 0x0000, 0x65ea, 0x706b, 0x0000, 0x756e, 0x7073, 0x0001, 0xa084,
+ 0x0000, 0x007c, 0x0e7e, 0x2071, 0xa300, 0x7077, 0x0000, 0x0e7f,
+ 0x007c, 0x0e7e, 0x0f7e, 0x2001, 0x0002, 0x1078, 0x5975, 0x2079,
+ 0x0100, 0x2071, 0x0140, 0x1078, 0x6c41, 0x7004, 0xa084, 0x4000,
+ 0x0040, 0x413e, 0x7003, 0x1000, 0x7003, 0x0000, 0x127e, 0x2091,
+ 0x8000, 0x2071, 0xa321, 0x2073, 0x0000, 0x7840, 0x027e, 0x017e,
+ 0x2009, 0x00f7, 0x1078, 0x41de, 0x017f, 0xa094, 0x0010, 0xa285,
+ 0x0080, 0x7842, 0x7a42, 0x027f, 0x127f, 0x0f7f, 0x0e7f, 0x007c,
+ 0x127e, 0x2091, 0x8000, 0x2011, 0xa5b5, 0x2013, 0x0000, 0x707f,
+ 0x0000, 0x127f, 0x20e1, 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575,
+ 0x1078, 0x6c38, 0x2009, 0x07d0, 0x2011, 0x4129, 0x1078, 0x596c,
+ 0x007c, 0x017e, 0x027e, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x2009,
+ 0x00f7, 0x1078, 0x41de, 0x2061, 0xa5be, 0x601b, 0x0000, 0x601f,
+ 0x0000, 0x2061, 0xa300, 0x6003, 0x0001, 0x2061, 0x0100, 0x6043,
+ 0x0090, 0x6043, 0x0010, 0x2009, 0x002d, 0x2011, 0x4196, 0x1078,
+ 0x58c7, 0x127f, 0x0c7f, 0x027f, 0x017f, 0x007c, 0x0e7e, 0x007e,
+ 0x127e, 0x2091, 0x8000, 0x2001, 0x0001, 0x1078, 0x5975, 0x2071,
+ 0x0100, 0x1078, 0x6c41, 0x2071, 0x0140, 0x7004, 0xa084, 0x4000,
+ 0x0040, 0x41ae, 0x7003, 0x1000, 0x7003, 0x0000, 0x2001, 0x0001,
+ 0x1078, 0x2480, 0x1078, 0x4171, 0x127f, 0x007f, 0x0e7f, 0x007c,
+ 0x20a9, 0x0040, 0x20a1, 0xa9c0, 0x2099, 0xa88e, 0x3304, 0x8007,
+ 0x20a2, 0x9398, 0x94a0, 0x00f0, 0x41be, 0x007c, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x2099, 0xa800, 0x20a1, 0x020b, 0x20a9, 0x000c,
+ 0x53a6, 0x007c, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xa880,
+ 0x20a1, 0x020b, 0x20a9, 0x000c, 0x53a6, 0x007c, 0x0c7e, 0x007e,
+ 0x2061, 0x0100, 0x810f, 0x2001, 0xa32e, 0x2004, 0xa005, 0x00c0,
+ 0x41ef, 0x6030, 0xa084, 0x00ff, 0xa105, 0x0078, 0x41f1, 0xa185,
+ 0x00f7, 0x604a, 0x007f, 0x0c7f, 0x007c, 0x017e, 0x047e, 0x2001,
+ 0xa352, 0x2004, 0xd0a4, 0x0040, 0x4208, 0xa006, 0x2020, 0x2009,
+ 0x002a, 0x1078, 0x9ec0, 0x2001, 0xa30c, 0x200c, 0xc195, 0x2102,
+ 0x2019, 0x002a, 0x2009, 0x0000, 0x1078, 0x27e2, 0x047f, 0x017f,
+ 0x007c, 0x007e, 0x2001, 0xa30c, 0x2004, 0xd09c, 0x0040, 0x4218,
+ 0x007f, 0x007c, 0x007e, 0x017e, 0x127e, 0x2091, 0x8000, 0x2001,
+ 0x0101, 0x200c, 0xa18d, 0x0006, 0x2102, 0x127f, 0x017f, 0x007f,
+ 0x007c, 0x157e, 0x20a9, 0x00ff, 0x2009, 0xa434, 0xa006, 0x200a,
+ 0x8108, 0x00f0, 0x422f, 0x157f, 0x007c, 0x0d7e, 0x037e, 0x157e,
+ 0x137e, 0x147e, 0x2069, 0xa351, 0xa006, 0x6002, 0x6007, 0x0707,
+ 0x600a, 0x600e, 0x6012, 0xa198, 0x293f, 0x231c, 0xa39c, 0x00ff,
+ 0x6316, 0x20a9, 0x0004, 0xac98, 0x0006, 0x23a0, 0x40a4, 0x20a9,
+ 0x0004, 0xac98, 0x000a, 0x23a0, 0x40a4, 0x603e, 0x6042, 0x604e,
+ 0x6052, 0x6056, 0x605a, 0x605e, 0x6062, 0x6066, 0x606a, 0x606e,
+ 0x6072, 0x6076, 0x607a, 0x607e, 0x6082, 0x6086, 0x608a, 0x608e,
+ 0x6092, 0x6096, 0x609a, 0x609e, 0x60ae, 0x61a2, 0x0d7e, 0x60a4,
+ 0xa06d, 0x0040, 0x4275, 0x1078, 0x139a, 0x60a7, 0x0000, 0x60a8,
+ 0xa06d, 0x0040, 0x427d, 0x1078, 0x139a, 0x60ab, 0x0000, 0x0d7f,
+ 0xa006, 0x604a, 0x6810, 0x603a, 0x680c, 0x6046, 0x6814, 0xa084,
+ 0x00ff, 0x6042, 0x147f, 0x137f, 0x157f, 0x037f, 0x0d7f, 0x007c,
+ 0x127e, 0x2091, 0x8000, 0x6944, 0x6e48, 0xa684, 0x3fff, 0xa082,
+ 0x4000, 0x00c8, 0x4361, 0xa18c, 0xff00, 0x810f, 0xa182, 0x00ff,
+ 0x00c8, 0x4367, 0x2001, 0xa30c, 0x2004, 0xa084, 0x0003, 0x0040,
+ 0x42c2, 0x2001, 0xa30c, 0x2004, 0xd084, 0x00c0, 0x4342, 0xa188,
+ 0xa434, 0x2104, 0xa065, 0x0040, 0x4342, 0x6004, 0xa084, 0x00ff,
+ 0xa08e, 0x0006, 0x00c0, 0x4342, 0x6000, 0xd0c4, 0x0040, 0x4342,
+ 0x0078, 0x42cf, 0xa188, 0xa434, 0x2104, 0xa065, 0x0040, 0x4326,
+ 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006, 0x00c0, 0x432c, 0x60a4,
+ 0xa00d, 0x0040, 0x42d7, 0x1078, 0x4749, 0x0040, 0x4320, 0x60a8,
+ 0xa00d, 0x0040, 0x42f1, 0x1078, 0x479a, 0x00c0, 0x42f1, 0x694c,
+ 0xd1fc, 0x00c0, 0x42e7, 0x1078, 0x441c, 0x0078, 0x431b, 0x1078,
+ 0x43d6, 0x694c, 0xd1ec, 0x00c0, 0x431b, 0x1078, 0x460a, 0x0078,
+ 0x431b, 0x694c, 0xa184, 0xa000, 0x0040, 0x430b, 0xd1ec, 0x0040,
+ 0x4304, 0xd1fc, 0x0040, 0x4300, 0x1078, 0x461b, 0x0078, 0x4307,
+ 0x1078, 0x461b, 0x0078, 0x430b, 0xd1fc, 0x0040, 0x430b, 0x1078,
+ 0x43d6, 0x0078, 0x431b, 0x6050, 0xa00d, 0x0040, 0x4316, 0x2d00,
+ 0x200a, 0x6803, 0x0000, 0x6052, 0x0078, 0x431b, 0x2d00, 0x6052,
+ 0x604e, 0x6803, 0x0000, 0x1078, 0x5c17, 0xa006, 0x127f, 0x007c,
+ 0x2001, 0x0005, 0x2009, 0x0000, 0x0078, 0x436b, 0x2001, 0x0028,
+ 0x2009, 0x0000, 0x0078, 0x436b, 0xa082, 0x0006, 0x00c8, 0x4342,
+ 0x60a0, 0xd0bc, 0x00c0, 0x433e, 0x6100, 0xd1fc, 0x0040, 0x42cf,
+ 0x2001, 0x0029, 0x2009, 0x1000, 0x0078, 0x436b, 0x2001, 0x0028,
+ 0x0078, 0x435d, 0x2009, 0xa30c, 0x210c, 0xd18c, 0x0040, 0x434c,
+ 0x2001, 0x0004, 0x0078, 0x435d, 0xd184, 0x0040, 0x4353, 0x2001,
+ 0x0004, 0x0078, 0x435d, 0x2001, 0x0029, 0x6100, 0xd1fc, 0x0040,
+ 0x435d, 0x2009, 0x1000, 0x0078, 0x436b, 0x2009, 0x0000, 0x0078,
+ 0x436b, 0x2001, 0x0029, 0x2009, 0x0000, 0x0078, 0x436b, 0x2001,
+ 0x0029, 0x2009, 0x0000, 0xa005, 0x127f, 0x007c, 0x6944, 0x6e48,
+ 0xa684, 0x3fff, 0xa082, 0x4000, 0x00c8, 0x43bb, 0xa18c, 0xff00,
+ 0x810f, 0xa182, 0x00ff, 0x00c8, 0x43a1, 0xa188, 0xa434, 0x2104,
+ 0xa065, 0x0040, 0x43a1, 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006,
+ 0x00c0, 0x43a7, 0x684c, 0xd0ec, 0x0040, 0x4394, 0x1078, 0x461b,
+ 0x1078, 0x43d6, 0x0078, 0x439c, 0x1078, 0x43d6, 0x684c, 0xd0fc,
+ 0x0040, 0x439c, 0x1078, 0x460a, 0x1078, 0x4663, 0xa006, 0x0078,
+ 0x43bf, 0x2001, 0x0028, 0x2009, 0x0000, 0x0078, 0x43bf, 0xa082,
+ 0x0006, 0x00c8, 0x43b5, 0x6100, 0xd1fc, 0x0040, 0x438a, 0x2001,
+ 0x0029, 0x2009, 0x1000, 0x0078, 0x43bf, 0x2001, 0x0029, 0x2009,
+ 0x0000, 0x0078, 0x43bf, 0x2001, 0x0029, 0x2009, 0x0000, 0xa005,
+ 0x007c, 0x127e, 0x2091, 0x8000, 0x6050, 0xa00d, 0x0040, 0x43cf,
+ 0x2d00, 0x200a, 0x6803, 0x0000, 0x6052, 0x127f, 0x007c, 0x2d00,
+ 0x6052, 0x604e, 0x6803, 0x0000, 0x0078, 0x43cd, 0x127e, 0x2091,
+ 0x8000, 0x604c, 0xa005, 0x0040, 0x43ec, 0x0e7e, 0x2071, 0xa5ab,
+ 0x7004, 0xa086, 0x0002, 0x0040, 0x43f3, 0x0e7f, 0x604c, 0x6802,
+ 0x2d00, 0x604e, 0x127f, 0x007c, 0x2d00, 0x6052, 0x604e, 0x6803,
+ 0x0000, 0x0078, 0x43ea, 0x701c, 0xac06, 0x00c0, 0x43e5, 0x604c,
+ 0x2070, 0x7000, 0x6802, 0x2d00, 0x7002, 0x0e7f, 0x127f, 0x007c,
+ 0x127e, 0x2091, 0x8000, 0x604c, 0xa06d, 0x0040, 0x440e, 0x6800,
+ 0xa005, 0x00c0, 0x440c, 0x6052, 0x604e, 0xad05, 0x127f, 0x007c,
+ 0x604c, 0xa06d, 0x0040, 0x441b, 0x6800, 0xa005, 0x00c0, 0x4419,
+ 0x6052, 0x604e, 0xad05, 0x007c, 0x6803, 0x0000, 0x6084, 0xa00d,
+ 0x0040, 0x4426, 0x2d00, 0x200a, 0x6086, 0x007c, 0x2d00, 0x6086,
+ 0x6082, 0x0078, 0x4425, 0x127e, 0x0c7e, 0x027e, 0x2091, 0x8000,
+ 0x6218, 0x2260, 0x6200, 0xa005, 0x0040, 0x4439, 0xc285, 0x0078,
+ 0x443a, 0xc284, 0x6202, 0x027f, 0x0c7f, 0x127f, 0x007c, 0x127e,
+ 0x0c7e, 0x2091, 0x8000, 0x6218, 0x2260, 0x6204, 0x007e, 0xa086,
+ 0x0006, 0x00c0, 0x445e, 0x609c, 0xd0ac, 0x0040, 0x445e, 0x2001,
+ 0xa352, 0x2004, 0xd0a4, 0x0040, 0x445e, 0xa284, 0xff00, 0x8007,
+ 0xa086, 0x0007, 0x00c0, 0x445e, 0x2011, 0x0600, 0x007f, 0xa294,
+ 0xff00, 0xa215, 0x6206, 0x007e, 0xa086, 0x0006, 0x00c0, 0x446e,
+ 0x6290, 0x82ff, 0x00c0, 0x446e, 0x1078, 0x1328, 0x007f, 0x0c7f,
+ 0x127f, 0x007c, 0x127e, 0x0c7e, 0x2091, 0x8000, 0x6218, 0x2260,
+ 0x6204, 0x007e, 0xa086, 0x0006, 0x00c0, 0x4490, 0x609c, 0xd0a4,
+ 0x0040, 0x4490, 0x2001, 0xa352, 0x2004, 0xd0ac, 0x00c0, 0x4490,
+ 0xa284, 0x00ff, 0xa086, 0x0007, 0x00c0, 0x4490, 0x2011, 0x0006,
+ 0x007f, 0xa294, 0x00ff, 0x8007, 0xa215, 0x6206, 0x0c7f, 0x127f,
+ 0x007c, 0x027e, 0xa182, 0x00ff, 0x0048, 0x44a2, 0xa085, 0x0001,
+ 0x0078, 0x44ba, 0xa190, 0xa434, 0x2204, 0xa065, 0x00c0, 0x44b9,
+ 0x017e, 0x0d7e, 0x1078, 0x1366, 0x2d60, 0x0d7f, 0x017f, 0x0040,
+ 0x449e, 0x2c00, 0x2012, 0x60a7, 0x0000, 0x60ab, 0x0000, 0x1078,
+ 0x4235, 0xa006, 0x027f, 0x007c, 0x127e, 0x2091, 0x8000, 0x027e,
+ 0xa182, 0x00ff, 0x0048, 0x44c8, 0xa085, 0x0001, 0x0078, 0x44fe,
+ 0x0d7e, 0xa190, 0xa434, 0x2204, 0xa06d, 0x0040, 0x44fc, 0x2013,
+ 0x0000, 0x0d7e, 0x0c7e, 0x2d60, 0x60a4, 0xa06d, 0x0040, 0x44da,
+ 0x1078, 0x139a, 0x60a8, 0xa06d, 0x0040, 0x44e0, 0x1078, 0x139a,
+ 0x0c7f, 0x0d7f, 0x0d7e, 0x0c7e, 0x68ac, 0x2060, 0x8cff, 0x0040,
+ 0x44f8, 0x600c, 0x007e, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040,
+ 0x44f3, 0x1078, 0x13aa, 0x1078, 0x753d, 0x0c7f, 0x0078, 0x44e6,
+ 0x0c7f, 0x0d7f, 0x1078, 0x139a, 0x0d7f, 0xa006, 0x027f, 0x127f,
+ 0x007c, 0x017e, 0xa182, 0x00ff, 0x0048, 0x450a, 0xa085, 0x0001,
+ 0x0078, 0x4511, 0xa188, 0xa434, 0x2104, 0xa065, 0x0040, 0x4506,
+ 0xa006, 0x017f, 0x007c, 0x0d7e, 0x157e, 0x137e, 0x147e, 0x600b,
+ 0x0000, 0x600f, 0x0000, 0x6000, 0xc08c, 0x6002, 0x2069, 0xa88e,
+ 0x6808, 0x605e, 0x6810, 0x6062, 0x6138, 0xa10a, 0x0048, 0x4529,
+ 0x603a, 0x6814, 0x6066, 0x2099, 0xa896, 0xac88, 0x000a, 0x21a0,
+ 0x20a9, 0x0004, 0x53a3, 0x2099, 0xa89a, 0xac88, 0x0006, 0x21a0,
+ 0x20a9, 0x0004, 0x53a3, 0x2069, 0xa8ae, 0x6808, 0x606a, 0x690c,
+ 0x616e, 0x6810, 0x6072, 0x6818, 0x6076, 0xa182, 0x0211, 0x00c8,
+ 0x454d, 0x2009, 0x0008, 0x0078, 0x4577, 0xa182, 0x0259, 0x00c8,
+ 0x4555, 0x2009, 0x0007, 0x0078, 0x4577, 0xa182, 0x02c1, 0x00c8,
+ 0x455d, 0x2009, 0x0006, 0x0078, 0x4577, 0xa182, 0x0349, 0x00c8,
+ 0x4565, 0x2009, 0x0005, 0x0078, 0x4577, 0xa182, 0x0421, 0x00c8,
+ 0x456d, 0x2009, 0x0004, 0x0078, 0x4577, 0xa182, 0x0581, 0x00c8,
+ 0x4575, 0x2009, 0x0003, 0x0078, 0x4577, 0x2009, 0x0002, 0x6192,
+ 0x147f, 0x137f, 0x157f, 0x0d7f, 0x007c, 0x017e, 0x027e, 0x0e7e,
+ 0x2071, 0xa88d, 0x2e04, 0x6896, 0x2071, 0xa88e, 0x7004, 0x689a,
+ 0x701c, 0x689e, 0x6a00, 0x2009, 0xa371, 0x210c, 0xd0bc, 0x0040,
+ 0x4597, 0xd1ec, 0x0040, 0x4597, 0xc2ad, 0x0078, 0x4598, 0xc2ac,
+ 0xd0c4, 0x0040, 0x45a1, 0xd1e4, 0x0040, 0x45a1, 0xc2bd, 0x0078,
+ 0x45a2, 0xc2bc, 0x6a02, 0x0e7f, 0x027f, 0x017f, 0x007c, 0x0d7e,
+ 0x127e, 0x2091, 0x8000, 0x60a4, 0xa06d, 0x0040, 0x45cb, 0x6900,
+ 0x81ff, 0x00c0, 0x45df, 0x6a04, 0xa282, 0x0010, 0x00c8, 0x45e4,
+ 0xad88, 0x0004, 0x20a9, 0x0010, 0x2104, 0xa086, 0xffff, 0x0040,
+ 0x45c6, 0x8108, 0x00f0, 0x45bc, 0x1078, 0x1328, 0x260a, 0x8210,
+ 0x6a06, 0x0078, 0x45df, 0x1078, 0x1381, 0x0040, 0x45e4, 0x2d00,
+ 0x60a6, 0x6803, 0x0000, 0xad88, 0x0004, 0x20a9, 0x0010, 0x200b,
+ 0xffff, 0x8108, 0x00f0, 0x45d7, 0x6807, 0x0001, 0x6e12, 0xa085,
+ 0x0001, 0x127f, 0x0d7f, 0x007c, 0xa006, 0x0078, 0x45e1, 0x127e,
+ 0x2091, 0x8000, 0x0d7e, 0x60a4, 0xa00d, 0x0040, 0x4607, 0x2168,
+ 0x6800, 0xa005, 0x00c0, 0x4603, 0x1078, 0x4749, 0x00c0, 0x4607,
+ 0x200b, 0xffff, 0x6804, 0xa08a, 0x0002, 0x0048, 0x4603, 0x8001,
+ 0x6806, 0x0078, 0x4607, 0x1078, 0x139a, 0x60a7, 0x0000, 0x0d7f,
+ 0x127f, 0x007c, 0x127e, 0x2091, 0x8000, 0x1078, 0x47af, 0x0078,
+ 0x4613, 0x1078, 0x43c1, 0x1078, 0x46a7, 0x00c0, 0x4611, 0x1078,
+ 0x4663, 0x127f, 0x007c, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x60a8,
+ 0xa06d, 0x0040, 0x463f, 0x6950, 0x81ff, 0x00c0, 0x4653, 0x6a54,
+ 0xa282, 0x0010, 0x00c8, 0x4660, 0xad88, 0x0018, 0x20a9, 0x0010,
+ 0x2104, 0xa086, 0xffff, 0x0040, 0x463a, 0x8108, 0x00f0, 0x4630,
+ 0x1078, 0x1328, 0x260a, 0x8210, 0x6a56, 0x0078, 0x4653, 0x1078,
+ 0x1381, 0x0040, 0x4660, 0x2d00, 0x60aa, 0x6853, 0x0000, 0xad88,
+ 0x0018, 0x20a9, 0x0010, 0x200b, 0xffff, 0x8108, 0x00f0, 0x464b,
+ 0x6857, 0x0001, 0x6e62, 0x0078, 0x4657, 0x1078, 0x441c, 0x1078,
+ 0x466d, 0x00c0, 0x4655, 0xa085, 0x0001, 0x127f, 0x0d7f, 0x007c,
+ 0xa006, 0x0078, 0x465d, 0x127e, 0x2091, 0x8000, 0x1078, 0x5c17,
+ 0x127f, 0x007c, 0xa01e, 0x0078, 0x466f, 0x2019, 0x0001, 0xa00e,
+ 0x127e, 0x2091, 0x8000, 0x604c, 0x2068, 0x6000, 0xd0dc, 0x00c0,
+ 0x468d, 0x8dff, 0x0040, 0x46a2, 0x83ff, 0x0040, 0x4685, 0x6848,
+ 0xa606, 0x0040, 0x4692, 0x0078, 0x468d, 0x683c, 0xa406, 0x00c0,
+ 0x468d, 0x6840, 0xa506, 0x0040, 0x4692, 0x2d08, 0x6800, 0x2068,
+ 0x0078, 0x4679, 0x6a00, 0x604c, 0xad06, 0x00c0, 0x469a, 0x624e,
+ 0x0078, 0x469d, 0xa180, 0x0000, 0x2202, 0x82ff, 0x00c0, 0x46a2,
+ 0x6152, 0x8dff, 0x127f, 0x007c, 0xa01e, 0x0078, 0x46a9, 0x2019,
+ 0x0001, 0xa00e, 0x6080, 0x2068, 0x8dff, 0x0040, 0x46d5, 0x83ff,
+ 0x0040, 0x46b8, 0x6848, 0xa606, 0x0040, 0x46c5, 0x0078, 0x46c0,
+ 0x683c, 0xa406, 0x00c0, 0x46c0, 0x6840, 0xa506, 0x0040, 0x46c5,
+ 0x2d08, 0x6800, 0x2068, 0x0078, 0x46ac, 0x6a00, 0x6080, 0xad06,
+ 0x00c0, 0x46cd, 0x6282, 0x0078, 0x46d0, 0xa180, 0x0000, 0x2202,
+ 0x82ff, 0x00c0, 0x46d5, 0x6186, 0x8dff, 0x007c, 0xa016, 0x1078,
+ 0x4742, 0x00c0, 0x46dd, 0x2011, 0x0001, 0x1078, 0x4793, 0x00c0,
+ 0x46e3, 0xa295, 0x0002, 0x007c, 0x1078, 0x47cb, 0x0040, 0x46ec,
+ 0x1078, 0x8b12, 0x0078, 0x46ee, 0xa085, 0x0001, 0x007c, 0x1078,
+ 0x47cb, 0x0040, 0x46f7, 0x1078, 0x8aaa, 0x0078, 0x46f9, 0xa085,
+ 0x0001, 0x007c, 0x1078, 0x47cb, 0x0040, 0x4702, 0x1078, 0x8af4,
+ 0x0078, 0x4704, 0xa085, 0x0001, 0x007c, 0x1078, 0x47cb, 0x0040,
+ 0x470d, 0x1078, 0x8ac6, 0x0078, 0x470f, 0xa085, 0x0001, 0x007c,
+ 0x1078, 0x47cb, 0x0040, 0x4718, 0x1078, 0x8b30, 0x0078, 0x471a,
+ 0xa085, 0x0001, 0x007c, 0x127e, 0x007e, 0x0d7e, 0x2091, 0x8000,
+ 0x6080, 0xa06d, 0x0040, 0x473a, 0x6800, 0x007e, 0x6837, 0x0103,
+ 0x6b4a, 0x6847, 0x0000, 0x1078, 0x8cb8, 0x007e, 0x6000, 0xd0fc,
+ 0x0040, 0x4734, 0x1078, 0xa18c, 0x007f, 0x1078, 0x4982, 0x007f,
+ 0x0078, 0x4721, 0x6083, 0x0000, 0x6087, 0x0000, 0x0d7f, 0x007f,
+ 0x127f, 0x007c, 0x60a4, 0xa00d, 0x00c0, 0x4749, 0xa085, 0x0001,
+ 0x007c, 0x0e7e, 0x2170, 0x7000, 0xa005, 0x00c0, 0x475c, 0x20a9,
+ 0x0010, 0xae88, 0x0004, 0x2104, 0xa606, 0x0040, 0x475c, 0x8108,
+ 0x00f0, 0x4753, 0xa085, 0x0001, 0xa006, 0x0e7f, 0x007c, 0x0d7e,
+ 0x127e, 0x2091, 0x8000, 0x60a4, 0xa06d, 0x00c0, 0x476d, 0x1078,
+ 0x1381, 0x0040, 0x477f, 0x2d00, 0x60a6, 0x6803, 0x0001, 0x6807,
+ 0x0000, 0xad88, 0x0004, 0x20a9, 0x0010, 0x200b, 0xffff, 0x8108,
+ 0x00f0, 0x4775, 0xa085, 0x0001, 0x127f, 0x0d7f, 0x007c, 0xa006,
+ 0x0078, 0x477c, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x60a4, 0xa06d,
+ 0x0040, 0x4790, 0x60a7, 0x0000, 0x1078, 0x139a, 0xa085, 0x0001,
+ 0x127f, 0x0d7f, 0x007c, 0x60a8, 0xa00d, 0x00c0, 0x479a, 0xa085,
+ 0x0001, 0x007c, 0x0e7e, 0x2170, 0x7050, 0xa005, 0x00c0, 0x47ad,
+ 0x20a9, 0x0010, 0xae88, 0x0018, 0x2104, 0xa606, 0x0040, 0x47ad,
+ 0x8108, 0x00f0, 0x47a4, 0xa085, 0x0001, 0x0e7f, 0x007c, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x4793, 0x00c0, 0x47c9, 0x200b, 0xffff,
+ 0x0d7e, 0x60a8, 0x2068, 0x6854, 0xa08a, 0x0002, 0x0048, 0x47c4,
+ 0x8001, 0x6856, 0x0078, 0x47c8, 0x1078, 0x139a, 0x60ab, 0x0000,
+ 0x0d7f, 0x127f, 0x007c, 0x609c, 0xd0a4, 0x007c, 0x0f7e, 0x71ac,
+ 0x81ff, 0x00c0, 0x47e9, 0x71c8, 0xd19c, 0x0040, 0x47e9, 0x2001,
+ 0x007e, 0xa080, 0xa434, 0x2004, 0xa07d, 0x0040, 0x47e9, 0x7804,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x47e9, 0x7800, 0xc0ed,
+ 0x7802, 0x2079, 0xa351, 0x7804, 0xd0a4, 0x0040, 0x480f, 0x157e,
+ 0x0c7e, 0x20a9, 0x007f, 0x2009, 0x0000, 0x017e, 0x1078, 0x4501,
+ 0x00c0, 0x4809, 0x6004, 0xa084, 0xff00, 0x8007, 0xa096, 0x0004,
+ 0x0040, 0x4806, 0xa086, 0x0006, 0x00c0, 0x4809, 0x6000, 0xc0ed,
+ 0x6002, 0x017f, 0x8108, 0x00f0, 0x47f5, 0x0c7f, 0x157f, 0x1078,
+ 0x4897, 0x0040, 0x4818, 0x2001, 0xa59f, 0x200c, 0x0078, 0x4820,
+ 0x2079, 0xa351, 0x7804, 0xd0a4, 0x0040, 0x4824, 0x2009, 0x07d0,
+ 0x2011, 0x4826, 0x1078, 0x596c, 0x0f7f, 0x007c, 0x2011, 0x4826,
+ 0x1078, 0x58d4, 0x1078, 0x4897, 0x0040, 0x484e, 0x2001, 0xa4b2,
+ 0x2004, 0xa080, 0x0000, 0x200c, 0xc1ec, 0x2102, 0x2001, 0xa352,
+ 0x2004, 0xd0a4, 0x0040, 0x4842, 0x2009, 0x07d0, 0x2011, 0x4826,
+ 0x1078, 0x596c, 0x0e7e, 0x2071, 0xa300, 0x706b, 0x0000, 0x706f,
+ 0x0000, 0x1078, 0x260d, 0x0e7f, 0x0078, 0x4886, 0x157e, 0x0c7e,
+ 0x20a9, 0x007f, 0x2009, 0x0000, 0x017e, 0x1078, 0x4501, 0x00c0,
+ 0x4880, 0x6000, 0xd0ec, 0x0040, 0x4880, 0x047e, 0x62a0, 0xa294,
+ 0x00ff, 0x8227, 0xa006, 0x2009, 0x0029, 0x1078, 0x9ec0, 0x6000,
+ 0xc0e5, 0xc0ec, 0x6002, 0x6004, 0xa084, 0x00ff, 0xa085, 0x0700,
+ 0x6006, 0x2019, 0x0029, 0x1078, 0x5d53, 0x077e, 0x2039, 0x0000,
+ 0x1078, 0x5c78, 0x2009, 0x0000, 0x1078, 0x9c38, 0x077f, 0x047f,
+ 0x017f, 0x8108, 0x00f0, 0x4854, 0x0c7f, 0x157f, 0x007c, 0x0c7e,
+ 0x6018, 0x2060, 0x6000, 0xc0ec, 0x6002, 0x0c7f, 0x007c, 0x7818,
+ 0x2004, 0xd0ac, 0x007c, 0x7818, 0x2004, 0xd0bc, 0x007c, 0x0f7e,
+ 0x2001, 0xa4b2, 0x2004, 0xa07d, 0x0040, 0x48a0, 0x7800, 0xd0ec,
+ 0x0f7f, 0x007c, 0x127e, 0x027e, 0x2091, 0x8000, 0x6200, 0xa005,
+ 0x0040, 0x48ad, 0xc2fd, 0x0078, 0x48ae, 0xc2fc, 0x6202, 0x027f,
+ 0x127f, 0x007c, 0x2071, 0xa413, 0x7003, 0x0001, 0x7007, 0x0000,
+ 0x7013, 0x0000, 0x7017, 0x0000, 0x701b, 0x0000, 0x701f, 0x0000,
+ 0x700b, 0x0000, 0x704b, 0x0001, 0x704f, 0x0000, 0x705b, 0x0020,
+ 0x705f, 0x0040, 0x707f, 0x0000, 0x2071, 0xa57c, 0x7003, 0xa413,
+ 0x7007, 0x0000, 0x700b, 0x0000, 0x700f, 0xa55c, 0x7013, 0x0020,
+ 0x7017, 0x0040, 0x7037, 0x0000, 0x007c, 0x017e, 0x0e7e, 0x2071,
+ 0xa534, 0xa00e, 0x7186, 0x718a, 0x7097, 0x0001, 0x2001, 0xa352,
+ 0x2004, 0xd0fc, 0x00c0, 0x48f7, 0x2001, 0xa352, 0x2004, 0xa00e,
+ 0xd09c, 0x0040, 0x48f4, 0x8108, 0x7102, 0x0078, 0x494a, 0x2001,
+ 0xa371, 0x200c, 0xa184, 0x000f, 0x2009, 0xa372, 0x210c, 0x0079,
+ 0x4901, 0x48ec, 0x4922, 0x492a, 0x4935, 0x493b, 0x48ec, 0x48ec,
+ 0x48ec, 0x4911, 0x48ec, 0x48ec, 0x48ec, 0x48ec, 0x48ec, 0x48ec,
+ 0x48ec, 0x7003, 0x0004, 0x137e, 0x147e, 0x157e, 0x2099, 0xa375,
+ 0x20a1, 0xa585, 0x20a9, 0x0004, 0x53a3, 0x157f, 0x147f, 0x137f,
+ 0x0078, 0x494a, 0x708f, 0x0005, 0x7007, 0x0122, 0x2001, 0x0002,
+ 0x0078, 0x4930, 0x708f, 0x0002, 0x7007, 0x0121, 0x2001, 0x0003,
+ 0x7002, 0x7097, 0x0001, 0x0078, 0x4947, 0x7007, 0x0122, 0x2001,
+ 0x0002, 0x0078, 0x493f, 0x7007, 0x0121, 0x2001, 0x0003, 0x7002,
+ 0xa006, 0x7096, 0x708e, 0xa184, 0xff00, 0x8007, 0x709a, 0xa184,
+ 0x00ff, 0x7092, 0x0e7f, 0x017f, 0x007c, 0x0e7e, 0x2071, 0xa413,
+ 0x684c, 0xa005, 0x00c0, 0x495b, 0x7028, 0xc085, 0x702a, 0xa085,
+ 0x0001, 0x0078, 0x4980, 0x6a60, 0x7236, 0x6b64, 0x733a, 0x6868,
+ 0x703e, 0x7076, 0x686c, 0x7042, 0x707a, 0x684c, 0x702e, 0x6844,
+ 0x7032, 0x2009, 0x000d, 0x200a, 0x700b, 0x0000, 0x8007, 0x8006,
+ 0x8006, 0xa08c, 0x003f, 0xa084, 0xffc0, 0xa210, 0x2100, 0xa319,
+ 0x726e, 0x7372, 0x7028, 0xc084, 0x702a, 0x7007, 0x0001, 0xa006,
+ 0x0e7f, 0x007c, 0x0e7e, 0x027e, 0x6838, 0xd0fc, 0x00c0, 0x49d8,
+ 0x6804, 0xa00d, 0x0040, 0x499e, 0x0d7e, 0x2071, 0xa300, 0xa016,
+ 0x702c, 0x2168, 0x6904, 0x206a, 0x8210, 0x2d00, 0x81ff, 0x00c0,
+ 0x4991, 0x702e, 0x70a8, 0xa200, 0x70aa, 0x0d7f, 0x2071, 0xa413,
+ 0x701c, 0xa005, 0x00c0, 0x49ea, 0x0068, 0x49e8, 0x2071, 0xa534,
+ 0x7200, 0x82ff, 0x0040, 0x49e8, 0x6934, 0xa186, 0x0103, 0x00c0,
+ 0x49fb, 0x6948, 0x6844, 0xa105, 0x00c0, 0x49db, 0x2009, 0x8020,
+ 0x2200, 0x0079, 0x49bb, 0x49e8, 0x49c0, 0x4a18, 0x4a26, 0x49e8,
+ 0x2071, 0x0000, 0x7018, 0xd084, 0x00c0, 0x49e8, 0x7122, 0x683c,
+ 0x7026, 0x6840, 0x702a, 0x701b, 0x0001, 0x2091, 0x4080, 0x2071,
+ 0xa300, 0x702c, 0x206a, 0x2d00, 0x702e, 0x70a8, 0x8000, 0x70aa,
+ 0x027f, 0x0e7f, 0x007c, 0x6844, 0xa086, 0x0100, 0x00c0, 0x49e8,
+ 0x6868, 0xa005, 0x00c0, 0x49e8, 0x2009, 0x8020, 0x0078, 0x49b8,
+ 0x2071, 0xa413, 0x2d08, 0x206b, 0x0000, 0x7010, 0x8000, 0x7012,
+ 0x7018, 0xa06d, 0x711a, 0x0040, 0x49f8, 0x6902, 0x0078, 0x49f9,
+ 0x711e, 0x0078, 0x49d8, 0xa18c, 0x00ff, 0xa186, 0x0017, 0x0040,
+ 0x4a09, 0xa186, 0x001e, 0x0040, 0x4a09, 0xa18e, 0x001f, 0x00c0,
+ 0x49e8, 0x684c, 0xd0cc, 0x0040, 0x49e8, 0x6850, 0xa084, 0x00ff,
+ 0xa086, 0x0001, 0x00c0, 0x49e8, 0x2009, 0x8021, 0x0078, 0x49b8,
+ 0x7084, 0x8008, 0xa092, 0x001e, 0x00c8, 0x49e8, 0x7186, 0xae90,
+ 0x0003, 0xa210, 0x683c, 0x2012, 0x0078, 0x4a36, 0x7084, 0x8008,
+ 0xa092, 0x000f, 0x00c8, 0x49e8, 0x7186, 0xae90, 0x0003, 0x8003,
+ 0xa210, 0x683c, 0x2012, 0x8210, 0x6840, 0x2012, 0x7088, 0xa10a,
+ 0x0048, 0x49cf, 0x718c, 0x7084, 0xa10a, 0x0048, 0x49cf, 0x2071,
+ 0x0000, 0x7018, 0xd084, 0x00c0, 0x49cf, 0x2071, 0xa534, 0x7000,
+ 0xa086, 0x0002, 0x00c0, 0x4a56, 0x1078, 0x4cd2, 0x2071, 0x0000,
+ 0x701b, 0x0001, 0x2091, 0x4080, 0x0078, 0x49cf, 0x1078, 0x4cfd,
+ 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0078, 0x49cf,
+ 0x007e, 0x684c, 0x007e, 0x6837, 0x0103, 0x20a9, 0x001c, 0xad80,
+ 0x0011, 0x20a0, 0x2001, 0x0000, 0x40a4, 0x007f, 0xa084, 0x00ff,
+ 0x684e, 0x007f, 0x684a, 0x6952, 0x007c, 0x2071, 0xa413, 0x7004,
+ 0x0079, 0x4a7a, 0x4a84, 0x4a95, 0x4ca3, 0x4ca4, 0x4ccb, 0x4cd1,
+ 0x4a85, 0x4c91, 0x4c32, 0x4cb4, 0x007c, 0x127e, 0x2091, 0x8000,
+ 0x0068, 0x4a94, 0x2009, 0x000d, 0x7030, 0x200a, 0x2091, 0x4080,
+ 0x7007, 0x0001, 0x700b, 0x0000, 0x127f, 0x2069, 0xa5be, 0x6844,
+ 0xa005, 0x0050, 0x4abd, 0x00c0, 0x4abd, 0x127e, 0x2091, 0x8000,
+ 0x2069, 0x0000, 0x6934, 0x2001, 0xa41f, 0x2004, 0xa10a, 0x0040,
+ 0x4ab8, 0x0068, 0x4abc, 0x2069, 0x0000, 0x6818, 0xd084, 0x00c0,
+ 0x4abc, 0x2009, 0x8040, 0x6922, 0x681b, 0x0001, 0x2091, 0x4080,
+ 0x2069, 0xa5be, 0x6847, 0xffff, 0x127f, 0x2069, 0xa300, 0x6844,
+ 0x6960, 0xa102, 0x2069, 0xa534, 0x688a, 0x6984, 0x701c, 0xa06d,
+ 0x0040, 0x4acf, 0x81ff, 0x0040, 0x4b17, 0x0078, 0x4ae5, 0x81ff,
+ 0x0040, 0x4be9, 0x2071, 0xa534, 0x7184, 0x7088, 0xa10a, 0x00c8,
+ 0x4ae5, 0x7190, 0x2071, 0xa5be, 0x7040, 0xa005, 0x0040, 0x4ae5,
+ 0x00d0, 0x4be9, 0x7142, 0x0078, 0x4be9, 0x2071, 0xa534, 0x718c,
+ 0x127e, 0x2091, 0x8000, 0x7084, 0xa10a, 0x0048, 0x4c06, 0x0068,
+ 0x4b9b, 0x2071, 0x0000, 0x7018, 0xd084, 0x00c0, 0x4b9b, 0x2001,
+ 0xffff, 0x2071, 0xa5be, 0x7042, 0x2071, 0xa534, 0x7000, 0xa086,
+ 0x0002, 0x00c0, 0x4b0d, 0x1078, 0x4cd2, 0x2071, 0x0000, 0x701b,
+ 0x0001, 0x2091, 0x4080, 0x0078, 0x4b9b, 0x1078, 0x4cfd, 0x2071,
+ 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0078, 0x4b9b, 0x2071,
+ 0xa534, 0x7000, 0xa005, 0x0040, 0x4bc8, 0x6934, 0xa186, 0x0103,
+ 0x00c0, 0x4b9e, 0x684c, 0xd0bc, 0x00c0, 0x4bc8, 0x6948, 0x6844,
+ 0xa105, 0x00c0, 0x4bbb, 0x2009, 0x8020, 0x2071, 0xa534, 0x7000,
+ 0x0079, 0x4b32, 0x4bc8, 0x4b80, 0x4b58, 0x4b6a, 0x4b37, 0x137e,
+ 0x147e, 0x157e, 0x2099, 0xa375, 0x20a1, 0xa585, 0x20a9, 0x0004,
+ 0x53a3, 0x157f, 0x147f, 0x137f, 0x2071, 0xa57c, 0xad80, 0x000f,
+ 0x700e, 0x7013, 0x0002, 0x7007, 0x0002, 0x700b, 0x0000, 0x2e10,
+ 0x1078, 0x13d1, 0x2071, 0xa413, 0x7007, 0x0009, 0x0078, 0x4be9,
+ 0x7084, 0x8008, 0xa092, 0x001e, 0x00c8, 0x4be9, 0xae90, 0x0003,
+ 0xa210, 0x683c, 0x2012, 0x7186, 0x2071, 0xa413, 0x1078, 0x4d5b,
+ 0x0078, 0x4be9, 0x7084, 0x8008, 0xa092, 0x000f, 0x00c8, 0x4be9,
+ 0xae90, 0x0003, 0x8003, 0xa210, 0x683c, 0x2012, 0x8210, 0x6840,
+ 0x2012, 0x7186, 0x2071, 0xa413, 0x1078, 0x4d5b, 0x0078, 0x4be9,
+ 0x127e, 0x2091, 0x8000, 0x0068, 0x4b9b, 0x2071, 0x0000, 0x7018,
+ 0xd084, 0x00c0, 0x4b9b, 0x7122, 0x683c, 0x7026, 0x6840, 0x702a,
+ 0x701b, 0x0001, 0x2091, 0x4080, 0x127f, 0x2071, 0xa413, 0x1078,
+ 0x4d5b, 0x0078, 0x4be9, 0x127f, 0x0078, 0x4be9, 0xa18c, 0x00ff,
+ 0xa186, 0x0017, 0x0040, 0x4bac, 0xa186, 0x001e, 0x0040, 0x4bac,
+ 0xa18e, 0x001f, 0x00c0, 0x4bc8, 0x684c, 0xd0cc, 0x0040, 0x4bc8,
+ 0x6850, 0xa084, 0x00ff, 0xa086, 0x0001, 0x00c0, 0x4bc8, 0x2009,
+ 0x8021, 0x0078, 0x4b2d, 0x6844, 0xa086, 0x0100, 0x00c0, 0x4bc8,
+ 0x6868, 0xa005, 0x00c0, 0x4bc8, 0x2009, 0x8020, 0x0078, 0x4b2d,
+ 0x2071, 0xa413, 0x1078, 0x4d6f, 0x0040, 0x4be9, 0x2071, 0xa413,
+ 0x700f, 0x0001, 0x6934, 0xa184, 0x00ff, 0xa086, 0x0003, 0x00c0,
+ 0x4be0, 0x810f, 0xa18c, 0x00ff, 0x8101, 0x0040, 0x4be0, 0x710e,
+ 0x7007, 0x0003, 0x1078, 0x4d8f, 0x7050, 0xa086, 0x0100, 0x0040,
+ 0x4ca4, 0x127e, 0x2091, 0x8000, 0x2071, 0xa413, 0x7008, 0xa086,
+ 0x0001, 0x00c0, 0x4c04, 0x0068, 0x4c04, 0x2009, 0x000d, 0x7030,
+ 0x200a, 0x2091, 0x4080, 0x700b, 0x0000, 0x7004, 0xa086, 0x0006,
+ 0x00c0, 0x4c04, 0x7007, 0x0001, 0x127f, 0x007c, 0x2071, 0xa413,
+ 0x1078, 0x4d6f, 0x0040, 0x4c2f, 0x2071, 0xa534, 0x7084, 0x700a,
+ 0x20a9, 0x0020, 0x2099, 0xa535, 0x20a1, 0xa55c, 0x53a3, 0x7087,
+ 0x0000, 0x2071, 0xa413, 0x2069, 0xa57c, 0x706c, 0x6826, 0x7070,
+ 0x682a, 0x7074, 0x682e, 0x7078, 0x6832, 0x2d10, 0x1078, 0x13d1,
+ 0x7007, 0x0008, 0x2001, 0xffff, 0x2071, 0xa5be, 0x7042, 0x127f,
+ 0x0078, 0x4be9, 0x2069, 0xa57c, 0x6808, 0xa08e, 0x0000, 0x0040,
+ 0x4c90, 0xa08e, 0x0200, 0x0040, 0x4c8e, 0xa08e, 0x0100, 0x00c0,
+ 0x4c90, 0x127e, 0x2091, 0x8000, 0x0068, 0x4c8b, 0x2069, 0x0000,
+ 0x6818, 0xd084, 0x00c0, 0x4c8b, 0x702c, 0x7130, 0x8108, 0xa102,
+ 0x0048, 0x4c59, 0xa00e, 0x7034, 0x706e, 0x7038, 0x7072, 0x0078,
+ 0x4c63, 0x706c, 0xa080, 0x0040, 0x706e, 0x00c8, 0x4c63, 0x7070,
+ 0xa081, 0x0000, 0x7072, 0x7132, 0x6936, 0x700b, 0x0000, 0x2001,
+ 0xa559, 0x2004, 0xa005, 0x00c0, 0x4c82, 0x6934, 0x2069, 0xa534,
+ 0x689c, 0x699e, 0x2069, 0xa5be, 0xa102, 0x00c0, 0x4c7b, 0x6844,
+ 0xa005, 0x00d0, 0x4c89, 0x2001, 0xa55a, 0x200c, 0x810d, 0x6946,
+ 0x0078, 0x4c89, 0x2009, 0x8040, 0x6922, 0x681b, 0x0001, 0x2091,
+ 0x4080, 0x7007, 0x0001, 0x127f, 0x0078, 0x4c90, 0x7007, 0x0005,
+ 0x007c, 0x701c, 0xa06d, 0x0040, 0x4ca2, 0x1078, 0x4d6f, 0x0040,
+ 0x4ca2, 0x7007, 0x0003, 0x1078, 0x4d8f, 0x7050, 0xa086, 0x0100,
+ 0x0040, 0x4ca4, 0x007c, 0x007c, 0x7050, 0xa09e, 0x0100, 0x00c0,
+ 0x4cad, 0x7007, 0x0004, 0x0078, 0x4ccb, 0xa086, 0x0200, 0x00c0,
+ 0x4cb3, 0x7007, 0x0005, 0x007c, 0x2001, 0xa57e, 0x2004, 0xa08e,
+ 0x0100, 0x00c0, 0x4cc0, 0x7007, 0x0001, 0x1078, 0x4d5b, 0x007c,
+ 0xa08e, 0x0000, 0x0040, 0x4cbf, 0xa08e, 0x0200, 0x00c0, 0x4cbf,
+ 0x7007, 0x0005, 0x007c, 0x1078, 0x4d25, 0x7006, 0x1078, 0x4d5b,
+ 0x007c, 0x007c, 0x0e7e, 0x157e, 0x2071, 0xa534, 0x7184, 0x81ff,
+ 0x0040, 0x4cfa, 0xa006, 0x7086, 0xae80, 0x0003, 0x2071, 0x0000,
+ 0x21a8, 0x2014, 0x7226, 0x8000, 0x0070, 0x4cf7, 0x2014, 0x722a,
+ 0x8000, 0x0070, 0x4cf7, 0x2014, 0x722e, 0x8000, 0x0070, 0x4cf7,
+ 0x2014, 0x723a, 0x8000, 0x0070, 0x4cf7, 0x2014, 0x723e, 0xa180,
+ 0x8030, 0x7022, 0x157f, 0x0e7f, 0x007c, 0x0e7e, 0x157e, 0x2071,
+ 0xa534, 0x7184, 0x81ff, 0x0040, 0x4d22, 0xa006, 0x7086, 0xae80,
+ 0x0003, 0x2071, 0x0000, 0x21a8, 0x2014, 0x7226, 0x8000, 0x2014,
+ 0x722a, 0x8000, 0x0070, 0x4d1b, 0x2014, 0x723a, 0x8000, 0x2014,
+ 0x723e, 0x0078, 0x4d1f, 0x2001, 0x8020, 0x0078, 0x4d21, 0x2001,
+ 0x8042, 0x7022, 0x157f, 0x0e7f, 0x007c, 0x702c, 0x7130, 0x8108,
+ 0xa102, 0x0048, 0x4d32, 0xa00e, 0x7034, 0x706e, 0x7038, 0x7072,
+ 0x0078, 0x4d3c, 0x706c, 0xa080, 0x0040, 0x706e, 0x00c8, 0x4d3c,
+ 0x7070, 0xa081, 0x0000, 0x7072, 0x7132, 0x700c, 0x8001, 0x700e,
+ 0x00c0, 0x4d52, 0x127e, 0x2091, 0x8000, 0x0068, 0x4d55, 0x2001,
+ 0x000d, 0x2102, 0x2091, 0x4080, 0x2001, 0x0001, 0x700b, 0x0000,
+ 0x127f, 0x007c, 0x2001, 0x0007, 0x007c, 0x2001, 0x0006, 0x700b,
+ 0x0001, 0x127f, 0x007c, 0x701c, 0xa06d, 0x0040, 0x4d6e, 0x127e,
+ 0x2091, 0x8000, 0x7010, 0x8001, 0x7012, 0x2d04, 0x701e, 0xa005,
+ 0x00c0, 0x4d6b, 0x701a, 0x127f, 0x1078, 0x139a, 0x007c, 0x2019,
+ 0x000d, 0x2304, 0x230c, 0xa10e, 0x0040, 0x4d7e, 0x2304, 0x230c,
+ 0xa10e, 0x0040, 0x4d7e, 0xa006, 0x0078, 0x4d8e, 0x732c, 0x8319,
+ 0x7130, 0xa102, 0x00c0, 0x4d88, 0x2300, 0xa005, 0x0078, 0x4d8e,
+ 0x0048, 0x4d8d, 0xa302, 0x0078, 0x4d8e, 0x8002, 0x007c, 0x2d00,
+ 0x7026, 0xa080, 0x000d, 0x7056, 0x7053, 0x0000, 0x127e, 0x2091,
+ 0x8000, 0x2009, 0xa5d0, 0x2104, 0xc08d, 0x200a, 0x127f, 0x1078,
+ 0x13eb, 0x007c, 0x2071, 0xa3e1, 0x7003, 0x0000, 0x7007, 0x0000,
+ 0x700f, 0x0000, 0x702b, 0x0001, 0x704f, 0x0000, 0x7053, 0x0001,
+ 0x705f, 0x0020, 0x7063, 0x0040, 0x7083, 0x0000, 0x708b, 0x0000,
+ 0x708f, 0x0001, 0x70bf, 0x0000, 0x007c, 0x0e7e, 0x2071, 0xa3e1,
+ 0x6848, 0xa005, 0x00c0, 0x4dcb, 0x7028, 0xc085, 0x702a, 0xa085,
+ 0x0001, 0x0078, 0x4df0, 0x6a50, 0x7236, 0x6b54, 0x733a, 0x6858,
+ 0x703e, 0x707a, 0x685c, 0x7042, 0x707e, 0x6848, 0x702e, 0x6840,
+ 0x7032, 0x2009, 0x000c, 0x200a, 0x8007, 0x8006, 0x8006, 0xa08c,
+ 0x003f, 0xa084, 0xffc0, 0xa210, 0x2100, 0xa319, 0x7272, 0x7376,
+ 0x7028, 0xc084, 0x702a, 0x7007, 0x0001, 0x700f, 0x0000, 0xa006,
+ 0x0e7f, 0x007c, 0x2b78, 0x2071, 0xa3e1, 0x7004, 0x1079, 0x4e50,
+ 0x700c, 0x0079, 0x4dfb, 0x4e00, 0x4df5, 0x4df5, 0x4df5, 0x4df5,
+ 0x007c, 0x700c, 0x0079, 0x4e04, 0x4e09, 0x4e4e, 0x4e4e, 0x4e4f,
+ 0x4e4f, 0x7830, 0x7930, 0xa106, 0x0040, 0x4e13, 0x7830, 0x7930,
+ 0xa106, 0x00c0, 0x4e39, 0x7030, 0xa10a, 0x0040, 0x4e39, 0x00c8,
+ 0x4e1b, 0x712c, 0xa10a, 0xa18a, 0x0002, 0x00c8, 0x4e3a, 0x1078,
+ 0x1366, 0x0040, 0x4e39, 0x2d00, 0x705a, 0x7063, 0x0040, 0x2001,
+ 0x0003, 0x7057, 0x0000, 0x127e, 0x007e, 0x2091, 0x8000, 0x2009,
+ 0xa5d0, 0x2104, 0xc085, 0x200a, 0x007f, 0x700e, 0x127f, 0x1078,
+ 0x13eb, 0x007c, 0x1078, 0x1366, 0x0040, 0x4e39, 0x2d00, 0x705a,
+ 0x1078, 0x1366, 0x00c0, 0x4e46, 0x0078, 0x4e25, 0x2d00, 0x7086,
+ 0x7063, 0x0080, 0x2001, 0x0004, 0x0078, 0x4e29, 0x007c, 0x007c,
+ 0x4e61, 0x4e62, 0x4e99, 0x4e9a, 0x4e4e, 0x4ed0, 0x4ed5, 0x4f0c,
+ 0x4f0d, 0x4f28, 0x4f29, 0x4f2a, 0x4f2b, 0x4f2c, 0x4f2d, 0x4fad,
+ 0x4fd7, 0x007c, 0x700c, 0x0079, 0x4e65, 0x4e6a, 0x4e6d, 0x4e7d,
+ 0x4e98, 0x4e98, 0x1078, 0x4e01, 0x007c, 0x127e, 0x8001, 0x700e,
+ 0x7058, 0x007e, 0x1078, 0x5348, 0x0040, 0x4e7a, 0x2091, 0x8000,
+ 0x1078, 0x4e01, 0x0d7f, 0x0078, 0x4e86, 0x127e, 0x8001, 0x700e,
+ 0x1078, 0x5348, 0x7058, 0x2068, 0x7084, 0x705a, 0x6803, 0x0000,
+ 0x6807, 0x0000, 0x6834, 0xa084, 0x00ff, 0xa08a, 0x0020, 0x00c8,
+ 0x4e95, 0x1079, 0x4eb0, 0x127f, 0x007c, 0x127f, 0x1078, 0x4f2e,
+ 0x007c, 0x007c, 0x007c, 0x0e7e, 0x2071, 0xa3e1, 0x700c, 0x0079,
+ 0x4ea1, 0x4ea6, 0x4ea6, 0x4ea6, 0x4ea8, 0x4eac, 0x0e7f, 0x007c,
+ 0x700f, 0x0001, 0x0078, 0x4eae, 0x700f, 0x0002, 0x0e7f, 0x007c,
+ 0x4f2e, 0x4f2e, 0x4f4a, 0x4f2e, 0x5080, 0x4f2e, 0x4f2e, 0x4f2e,
+ 0x4f2e, 0x4f2e, 0x4f4a, 0x50ca, 0x5117, 0x5170, 0x5186, 0x4f2e,
+ 0x4f2e, 0x4f66, 0x4f4a, 0x4f2e, 0x4f2e, 0x4f87, 0x5245, 0x5263,
+ 0x4f2e, 0x4f66, 0x4f2e, 0x4f2e, 0x4f2e, 0x4f2e, 0x4f7c, 0x5263,
+ 0x7020, 0x2068, 0x1078, 0x139a, 0x007c, 0x700c, 0x0079, 0x4ed8,
+ 0x4edd, 0x4ee0, 0x4ef0, 0x4f0b, 0x4f0b, 0x1078, 0x4e01, 0x007c,
+ 0x127e, 0x8001, 0x700e, 0x7058, 0x007e, 0x1078, 0x5348, 0x0040,
+ 0x4eed, 0x2091, 0x8000, 0x1078, 0x4e01, 0x0d7f, 0x0078, 0x4ef9,
+ 0x127e, 0x8001, 0x700e, 0x1078, 0x5348, 0x7058, 0x2068, 0x7084,
+ 0x705a, 0x6803, 0x0000, 0x6807, 0x0000, 0x6834, 0xa084, 0x00ff,
+ 0xa08a, 0x001a, 0x00c8, 0x4f08, 0x1079, 0x4f0e, 0x127f, 0x007c,
+ 0x127f, 0x1078, 0x4f2e, 0x007c, 0x007c, 0x007c, 0x4f2e, 0x4f4a,
+ 0x506a, 0x4f2e, 0x4f4a, 0x4f2e, 0x4f4a, 0x4f4a, 0x4f2e, 0x4f4a,
+ 0x506a, 0x4f4a, 0x4f4a, 0x4f4a, 0x4f4a, 0x4f4a, 0x4f2e, 0x4f4a,
+ 0x506a, 0x4f2e, 0x4f2e, 0x4f4a, 0x4f2e, 0x4f2e, 0x4f2e, 0x4f4a,
+ 0x007c, 0x007c, 0x007c, 0x007c, 0x007c, 0x007c, 0x7007, 0x0001,
+ 0x6838, 0xa084, 0x00ff, 0xc0d5, 0x683a, 0x127e, 0x2091, 0x8000,
+ 0x1078, 0x4982, 0x127f, 0x007c, 0x7007, 0x0001, 0x6838, 0xa084,
+ 0x00ff, 0xc0e5, 0x683a, 0x127e, 0x2091, 0x8000, 0x1078, 0x4982,
+ 0x127f, 0x007c, 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0ed,
+ 0x683a, 0x127e, 0x2091, 0x8000, 0x1078, 0x4982, 0x127f, 0x007c,
+ 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0dd, 0x683a, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x4982, 0x127f, 0x007c, 0x6834, 0x8007,
+ 0xa084, 0x00ff, 0x0040, 0x4f3c, 0x8001, 0x00c0, 0x4f73, 0x7007,
+ 0x0001, 0x0078, 0x5049, 0x7007, 0x0006, 0x7012, 0x2d00, 0x7016,
+ 0x701a, 0x704b, 0x5049, 0x007c, 0x684c, 0xa084, 0x00c0, 0xa086,
+ 0x00c0, 0x00c0, 0x4f87, 0x7007, 0x0001, 0x0078, 0x5280, 0x2d00,
+ 0x7016, 0x701a, 0x20a9, 0x0004, 0xa080, 0x0024, 0x2098, 0x20a1,
+ 0xa40c, 0x53a3, 0x6858, 0x7012, 0xa082, 0x0401, 0x00c8, 0x4f58,
+ 0x6884, 0xa08a, 0x0002, 0x00c8, 0x4f58, 0x82ff, 0x00c0, 0x4fa9,
+ 0x6888, 0x698c, 0xa105, 0x0040, 0x4fa9, 0x2001, 0x5019, 0x0078,
+ 0x4fac, 0xa280, 0x500f, 0x2004, 0x70c6, 0x7010, 0xa015, 0x0040,
+ 0x4ff7, 0x1078, 0x1366, 0x00c0, 0x4fb8, 0x7007, 0x000f, 0x007c,
+ 0x2d00, 0x7022, 0x70c4, 0x2060, 0x6000, 0x6836, 0x6004, 0xad00,
+ 0x7096, 0x6008, 0xa20a, 0x00c8, 0x4fc7, 0xa00e, 0x2200, 0x7112,
+ 0x620c, 0x8003, 0x800b, 0xa296, 0x0004, 0x0040, 0x4fd0, 0xa108,
+ 0x719a, 0x810b, 0x719e, 0xae90, 0x0022, 0x1078, 0x13d1, 0x7090,
+ 0xa08e, 0x0100, 0x0040, 0x4feb, 0xa086, 0x0200, 0x0040, 0x4fe3,
+ 0x7007, 0x0010, 0x007c, 0x7020, 0x2068, 0x1078, 0x139a, 0x7014,
+ 0x2068, 0x0078, 0x4f58, 0x7020, 0x2068, 0x7018, 0x6802, 0x6807,
+ 0x0000, 0x2d08, 0x2068, 0x6906, 0x711a, 0x0078, 0x4fad, 0x7014,
+ 0x2068, 0x7007, 0x0001, 0x6884, 0xa005, 0x00c0, 0x5006, 0x6888,
+ 0x698c, 0xa105, 0x0040, 0x5006, 0x1078, 0x501d, 0x6834, 0xa084,
+ 0x00ff, 0xa086, 0x001e, 0x0040, 0x5280, 0x0078, 0x5049, 0x5011,
+ 0x5015, 0x0002, 0x0011, 0x0007, 0x0004, 0x000a, 0x000f, 0x0005,
+ 0x0006, 0x000a, 0x0011, 0x0005, 0x0004, 0x0f7e, 0x0e7e, 0x0c7e,
+ 0x077e, 0x067e, 0x6f88, 0x6e8c, 0x6804, 0x2060, 0xacf0, 0x0021,
+ 0xacf8, 0x0027, 0x2009, 0x0005, 0x700c, 0x7816, 0x7008, 0x7812,
+ 0x7004, 0x7806, 0x7000, 0x7802, 0x7e0e, 0x7f0a, 0x8109, 0x0040,
+ 0x503f, 0xaef2, 0x0004, 0xaffa, 0x0006, 0x0078, 0x502c, 0x6004,
+ 0xa065, 0x00c0, 0x5026, 0x067f, 0x077f, 0x0c7f, 0x0e7f, 0x0f7f,
+ 0x007c, 0x2009, 0xa32e, 0x210c, 0x81ff, 0x00c0, 0x5064, 0x6838,
+ 0xa084, 0x00ff, 0x683a, 0x1078, 0x4290, 0x00c0, 0x5058, 0x007c,
+ 0x1078, 0x4a60, 0x127e, 0x2091, 0x8000, 0x1078, 0x8cb8, 0x1078,
+ 0x4982, 0x127f, 0x0078, 0x5057, 0x2001, 0x0028, 0x2009, 0x0000,
+ 0x0078, 0x5058, 0x7018, 0x6802, 0x2d08, 0x2068, 0x6906, 0x711a,
+ 0x7010, 0x8001, 0x7012, 0x0040, 0x5079, 0x7007, 0x0006, 0x0078,
+ 0x507f, 0x7014, 0x2068, 0x7007, 0x0001, 0x7048, 0x107a, 0x007c,
+ 0x7007, 0x0001, 0x6944, 0x810f, 0xa18c, 0x00ff, 0x6848, 0xa084,
+ 0x00ff, 0x20a9, 0x0001, 0xa096, 0x0001, 0x0040, 0x50a9, 0x2009,
+ 0x0000, 0x20a9, 0x00ff, 0xa096, 0x0002, 0x0040, 0x50a9, 0xa005,
+ 0x00c0, 0x50bc, 0x6944, 0x810f, 0xa18c, 0x00ff, 0x1078, 0x4501,
+ 0x00c0, 0x50bc, 0x067e, 0x6e50, 0x1078, 0x45e7, 0x067f, 0x0078,
+ 0x50bc, 0x047e, 0x2011, 0xa30c, 0x2224, 0xc484, 0xc48c, 0x2412,
+ 0x047f, 0x0c7e, 0x1078, 0x4501, 0x00c0, 0x50b8, 0x1078, 0x4782,
+ 0x8108, 0x00f0, 0x50b2, 0x0c7f, 0x684c, 0xd084, 0x00c0, 0x50c3,
+ 0x1078, 0x139a, 0x007c, 0x127e, 0x2091, 0x8000, 0x1078, 0x4982,
+ 0x127f, 0x007c, 0x127e, 0x2091, 0x8000, 0x7007, 0x0001, 0x2001,
+ 0xa352, 0x2004, 0xd0a4, 0x0040, 0x510e, 0x2061, 0xa62d, 0x6100,
+ 0xd184, 0x0040, 0x50ee, 0x6858, 0xa084, 0x00ff, 0x00c0, 0x5111,
+ 0x6000, 0xd084, 0x0040, 0x510e, 0x6004, 0xa005, 0x00c0, 0x5114,
+ 0x6003, 0x0000, 0x600b, 0x0000, 0x0078, 0x510b, 0x2011, 0x0001,
+ 0x6860, 0xa005, 0x00c0, 0x50f6, 0x2001, 0x001e, 0x8000, 0x6016,
+ 0x6858, 0xa084, 0x00ff, 0x0040, 0x510e, 0x6006, 0x6858, 0x8007,
+ 0xa084, 0x00ff, 0x0040, 0x510e, 0x600a, 0x6858, 0x8000, 0x00c0,
+ 0x510a, 0xc28d, 0x6202, 0x127f, 0x0078, 0x5337, 0x127f, 0x0078,
+ 0x532f, 0x127f, 0x0078, 0x5327, 0x127f, 0x0078, 0x532b, 0x127e,
+ 0x2091, 0x8000, 0x7007, 0x0001, 0x2001, 0xa352, 0x2004, 0xd0a4,
+ 0x0040, 0x516d, 0x2061, 0xa62d, 0x6000, 0xd084, 0x0040, 0x516d,
+ 0x6204, 0x6308, 0xd08c, 0x00c0, 0x515f, 0x6c48, 0xa484, 0x0003,
+ 0x0040, 0x5145, 0x6958, 0xa18c, 0x00ff, 0x8001, 0x00c0, 0x513e,
+ 0x2100, 0xa210, 0x0048, 0x516a, 0x0078, 0x5145, 0x8001, 0x00c0,
+ 0x516a, 0x2100, 0xa212, 0x0048, 0x516a, 0xa484, 0x000c, 0x0040,
+ 0x515f, 0x6958, 0x810f, 0xa18c, 0x00ff, 0xa082, 0x0004, 0x00c0,
+ 0x5157, 0x2100, 0xa318, 0x0048, 0x516a, 0x0078, 0x515f, 0xa082,
+ 0x0004, 0x00c0, 0x516a, 0x2100, 0xa31a, 0x0048, 0x516a, 0x6860,
+ 0xa005, 0x0040, 0x5165, 0x8000, 0x6016, 0x6206, 0x630a, 0x127f,
+ 0x0078, 0x5337, 0x127f, 0x0078, 0x5333, 0x127f, 0x0078, 0x532f,
+ 0x127e, 0x2091, 0x8000, 0x7007, 0x0001, 0x2061, 0xa62d, 0x6300,
+ 0xd38c, 0x00c0, 0x5180, 0x6308, 0x8318, 0x0048, 0x5183, 0x630a,
+ 0x127f, 0x0078, 0x5345, 0x127f, 0x0078, 0x5333, 0x127e, 0x0c7e,
+ 0x2091, 0x8000, 0x7007, 0x0001, 0x684c, 0xd0ac, 0x0040, 0x519a,
+ 0x0c7e, 0x2061, 0xa62d, 0x6000, 0xa084, 0xfcff, 0x6002, 0x0c7f,
+ 0x0078, 0x51c9, 0x6858, 0xa005, 0x0040, 0x51e0, 0x685c, 0xa065,
+ 0x0040, 0x51dc, 0x2001, 0xa32e, 0x2004, 0xa005, 0x0040, 0x51ac,
+ 0x1078, 0x8c01, 0x0078, 0x51ba, 0x6013, 0x0400, 0x6037, 0x0000,
+ 0x694c, 0xd1a4, 0x0040, 0x51b6, 0x6950, 0x6136, 0x2009, 0x0041,
+ 0x1078, 0x756c, 0x6958, 0xa18c, 0xff00, 0xa186, 0x2000, 0x00c0,
+ 0x51c9, 0x027e, 0x2009, 0x0000, 0x2011, 0xfdff, 0x1078, 0x5a6d,
+ 0x027f, 0x684c, 0xd0c4, 0x0040, 0x51d8, 0x2061, 0xa62d, 0x6000,
+ 0xd08c, 0x00c0, 0x51d8, 0x6008, 0x8000, 0x0048, 0x51dc, 0x600a,
+ 0x0c7f, 0x127f, 0x0078, 0x5337, 0x0c7f, 0x127f, 0x0078, 0x532f,
+ 0x6954, 0xa186, 0x0045, 0x0040, 0x5213, 0xa186, 0x002a, 0x00c0,
+ 0x51f0, 0x2001, 0xa30c, 0x200c, 0xc194, 0x2102, 0x0078, 0x51c9,
+ 0xa186, 0x0020, 0x0040, 0x5209, 0xa186, 0x0029, 0x0040, 0x51fc,
+ 0xa186, 0x002d, 0x00c0, 0x51dc, 0x6944, 0xa18c, 0xff00, 0x810f,
+ 0x1078, 0x4501, 0x00c0, 0x51c9, 0x6000, 0xc0e4, 0x6002, 0x0078,
+ 0x51c9, 0x685c, 0xa065, 0x0040, 0x51dc, 0x2001, 0xa5a1, 0x2004,
+ 0x6016, 0x0078, 0x51c9, 0x685c, 0xa065, 0x0040, 0x51dc, 0x0e7e,
+ 0x6860, 0xa075, 0x2001, 0xa32e, 0x2004, 0xa005, 0x0040, 0x522b,
+ 0x1078, 0x8c01, 0x8eff, 0x0040, 0x5228, 0x2e60, 0x1078, 0x8c01,
+ 0x0e7f, 0x0078, 0x51c9, 0x6024, 0xc0dc, 0xc0d5, 0x6026, 0x2e60,
+ 0x6007, 0x003a, 0x6870, 0xa005, 0x0040, 0x523c, 0x6007, 0x003b,
+ 0x6874, 0x602a, 0x6878, 0x6012, 0x6003, 0x0001, 0x1078, 0x5bf8,
+ 0x1078, 0x6109, 0x0e7f, 0x0078, 0x51c9, 0x2061, 0xa62d, 0x6000,
+ 0xd084, 0x0040, 0x525f, 0xd08c, 0x00c0, 0x5345, 0x2091, 0x8000,
+ 0x6204, 0x8210, 0x0048, 0x5259, 0x6206, 0x2091, 0x8001, 0x0078,
+ 0x5345, 0x2091, 0x8001, 0x6853, 0x0016, 0x0078, 0x533e, 0x6853,
+ 0x0007, 0x0078, 0x533e, 0x6834, 0x8007, 0xa084, 0x00ff, 0x00c0,
+ 0x526d, 0x1078, 0x4f3c, 0x0078, 0x527f, 0x2030, 0x8001, 0x00c0,
+ 0x5277, 0x7007, 0x0001, 0x1078, 0x5280, 0x0078, 0x527f, 0x7007,
+ 0x0006, 0x7012, 0x2d00, 0x7016, 0x701a, 0x704b, 0x5280, 0x007c,
+ 0x0e7e, 0x127e, 0x2091, 0x8000, 0x2009, 0xa32e, 0x210c, 0x81ff,
+ 0x00c0, 0x530b, 0x2009, 0xa30c, 0x210c, 0xd194, 0x00c0, 0x5315,
+ 0x6848, 0x2070, 0xae82, 0xaa00, 0x0048, 0x52fb, 0x2001, 0xa315,
+ 0x2004, 0xae02, 0x00c8, 0x52fb, 0x2061, 0xa62d, 0x6100, 0xa184,
+ 0x0301, 0xa086, 0x0001, 0x00c0, 0x52de, 0x711c, 0xa186, 0x0006,
+ 0x00c0, 0x52e6, 0x7018, 0xa005, 0x0040, 0x530b, 0x2004, 0xd0e4,
+ 0x00c0, 0x530f, 0x7024, 0xd0dc, 0x00c0, 0x5319, 0x6853, 0x0000,
+ 0x6803, 0x0000, 0x2d08, 0x7010, 0xa005, 0x00c0, 0x52ca, 0x7112,
+ 0x684c, 0xd0f4, 0x00c0, 0x531d, 0x2e60, 0x1078, 0x59b6, 0x127f,
+ 0x0e7f, 0x007c, 0x2068, 0x6800, 0xa005, 0x00c0, 0x52ca, 0x6902,
+ 0x2168, 0x684c, 0xd0f4, 0x00c0, 0x531d, 0x127f, 0x0e7f, 0x007c,
+ 0x127f, 0x0e7f, 0x6853, 0x0006, 0x0078, 0x533e, 0xd184, 0x0040,
+ 0x52d8, 0xd1c4, 0x00c0, 0x52ff, 0x0078, 0x5303, 0x6944, 0xa18c,
+ 0xff00, 0x810f, 0x1078, 0x4501, 0x00c0, 0x530f, 0x6000, 0xd0e4,
+ 0x00c0, 0x530f, 0x711c, 0xa186, 0x0007, 0x00c0, 0x52fb, 0x6853,
+ 0x0002, 0x0078, 0x5311, 0x6853, 0x0008, 0x0078, 0x5311, 0x6853,
+ 0x000e, 0x0078, 0x5311, 0x6853, 0x0017, 0x0078, 0x5311, 0x6853,
+ 0x0035, 0x0078, 0x5311, 0x6853, 0x0028, 0x0078, 0x5311, 0x6853,
+ 0x0029, 0x127f, 0x0e7f, 0x0078, 0x533e, 0x6853, 0x002a, 0x0078,
+ 0x5311, 0x6853, 0x0045, 0x0078, 0x5311, 0x2e60, 0x2019, 0x0002,
+ 0x6017, 0x0014, 0x1078, 0x9a6a, 0x127f, 0x0e7f, 0x007c, 0x2009,
+ 0x003e, 0x0078, 0x5339, 0x2009, 0x0004, 0x0078, 0x5339, 0x2009,
+ 0x0006, 0x0078, 0x5339, 0x2009, 0x0016, 0x0078, 0x5339, 0x2009,
+ 0x0001, 0x6854, 0xa084, 0xff00, 0xa105, 0x6856, 0x2091, 0x8000,
+ 0x1078, 0x4982, 0x2091, 0x8001, 0x007c, 0x1078, 0x139a, 0x007c,
+ 0x702c, 0x7130, 0x8108, 0xa102, 0x0048, 0x5355, 0xa00e, 0x7034,
+ 0x7072, 0x7038, 0x7076, 0x0078, 0x5361, 0x7070, 0xa080, 0x0040,
+ 0x7072, 0x00c8, 0x5361, 0x7074, 0xa081, 0x0000, 0x7076, 0xa085,
+ 0x0001, 0x7932, 0x7132, 0x007c, 0x0d7e, 0x1078, 0x59ad, 0x0d7f,
+ 0x007c, 0x0d7e, 0x2011, 0x0004, 0x2204, 0xa085, 0x8002, 0x2012,
+ 0x0d7f, 0x007c, 0x20e1, 0x0002, 0x3d08, 0x20e1, 0x2000, 0x3d00,
+ 0xa084, 0x7000, 0x0040, 0x5380, 0xa086, 0x1000, 0x00c0, 0x53ac,
+ 0x20e1, 0x0000, 0x3d00, 0xa094, 0xff00, 0x8217, 0xa084, 0xf000,
+ 0xa086, 0x3000, 0x00c0, 0x5390, 0x1078, 0x5570, 0x0078, 0x53a7,
+ 0x20e1, 0x0004, 0x3d60, 0xd1bc, 0x00c0, 0x5397, 0x3e60, 0xac84,
+ 0x000f, 0x00c0, 0x53ac, 0xac82, 0xaa00, 0x0048, 0x53ac, 0x6854,
+ 0xac02, 0x00c8, 0x53ac, 0x2009, 0x0047, 0x1078, 0x756c, 0x7a1c,
+ 0xd284, 0x00c0, 0x5372, 0x007c, 0xa016, 0x1078, 0x15ec, 0x0078,
+ 0x53a7, 0x0078, 0x53ac, 0x781c, 0xd08c, 0x0040, 0x53db, 0x157e,
+ 0x137e, 0x147e, 0x20e1, 0x3000, 0x3d20, 0x3e28, 0xa584, 0x0076,
+ 0x00c0, 0x53f1, 0xa484, 0x7000, 0xa086, 0x1000, 0x00c0, 0x53e0,
+ 0x1078, 0x540c, 0x0040, 0x53f1, 0x20e1, 0x3000, 0x7828, 0x7828,
+ 0x1078, 0x542a, 0x147f, 0x137f, 0x157f, 0x2009, 0xa5b3, 0x2104,
+ 0xa005, 0x00c0, 0x53dc, 0x007c, 0x1078, 0x6109, 0x0078, 0x53db,
+ 0xa484, 0x7000, 0x00c0, 0x53f1, 0x1078, 0x540c, 0x0040, 0x5403,
+ 0x7000, 0xa084, 0xff00, 0xa086, 0x8100, 0x0040, 0x53cc, 0x0078,
+ 0x5403, 0x1078, 0xa1ee, 0xd5a4, 0x0040, 0x53ff, 0x1078, 0x1af7,
+ 0x20e1, 0x9010, 0x2001, 0x0138, 0x2202, 0x0078, 0x5407, 0x1078,
+ 0x540c, 0x687f, 0x0000, 0x20e1, 0x3000, 0x7828, 0x7828, 0x147f,
+ 0x137f, 0x157f, 0x0078, 0x53db, 0xa484, 0x01ff, 0x687e, 0xa005,
+ 0x0040, 0x541e, 0xa080, 0x001f, 0xa084, 0x03f8, 0x80ac, 0x20e1,
+ 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0x007c, 0x20a9, 0x000c,
+ 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0xa085, 0x0001,
+ 0x0078, 0x541d, 0x7000, 0xa084, 0xff00, 0xa08c, 0xf000, 0x8007,
+ 0xa196, 0x0000, 0x00c0, 0x5437, 0x0078, 0x567c, 0x007c, 0xa196,
+ 0x2000, 0x00c0, 0x5448, 0x6900, 0xa18e, 0x0001, 0x00c0, 0x5444,
+ 0x1078, 0x3a43, 0x0078, 0x5436, 0x1078, 0x5450, 0x0078, 0x5436,
+ 0xa196, 0x8000, 0x00c0, 0x5436, 0x1078, 0x570c, 0x0078, 0x5436,
+ 0x0c7e, 0x7110, 0xa18c, 0xff00, 0x810f, 0xa196, 0x0001, 0x0040,
+ 0x545d, 0xa196, 0x0023, 0x00c0, 0x5568, 0xa08e, 0x0023, 0x00c0,
+ 0x5492, 0x1078, 0x57b2, 0x0040, 0x5568, 0x7124, 0x610a, 0x7030,
+ 0xa08e, 0x0200, 0x00c0, 0x5476, 0x7034, 0xa005, 0x00c0, 0x5568,
+ 0x2009, 0x0015, 0x1078, 0x756c, 0x0078, 0x5568, 0xa08e, 0x0214,
+ 0x0040, 0x547e, 0xa08e, 0x0210, 0x00c0, 0x5484, 0x2009, 0x0015,
+ 0x1078, 0x756c, 0x0078, 0x5568, 0xa08e, 0x0100, 0x00c0, 0x5568,
+ 0x7034, 0xa005, 0x00c0, 0x5568, 0x2009, 0x0016, 0x1078, 0x756c,
+ 0x0078, 0x5568, 0xa08e, 0x0022, 0x00c0, 0x5568, 0x7030, 0xa08e,
+ 0x0300, 0x00c0, 0x54a3, 0x7034, 0xa005, 0x00c0, 0x5568, 0x2009,
+ 0x0017, 0x0078, 0x5534, 0xa08e, 0x0500, 0x00c0, 0x54af, 0x7034,
+ 0xa005, 0x00c0, 0x5568, 0x2009, 0x0018, 0x0078, 0x5534, 0xa08e,
+ 0x2010, 0x00c0, 0x54b7, 0x2009, 0x0019, 0x0078, 0x5534, 0xa08e,
+ 0x2110, 0x00c0, 0x54bf, 0x2009, 0x001a, 0x0078, 0x5534, 0xa08e,
+ 0x5200, 0x00c0, 0x54cb, 0x7034, 0xa005, 0x00c0, 0x5568, 0x2009,
+ 0x001b, 0x0078, 0x5534, 0xa08e, 0x5000, 0x00c0, 0x54d7, 0x7034,
+ 0xa005, 0x00c0, 0x5568, 0x2009, 0x001c, 0x0078, 0x5534, 0xa08e,
+ 0x1300, 0x00c0, 0x54df, 0x2009, 0x0034, 0x0078, 0x5534, 0xa08e,
+ 0x1200, 0x00c0, 0x54eb, 0x7034, 0xa005, 0x00c0, 0x5568, 0x2009,
+ 0x0024, 0x0078, 0x5534, 0xa08c, 0xff00, 0xa18e, 0x2400, 0x00c0,
+ 0x54f5, 0x2009, 0x002d, 0x0078, 0x5534, 0xa08c, 0xff00, 0xa18e,
+ 0x5300, 0x00c0, 0x54ff, 0x2009, 0x002a, 0x0078, 0x5534, 0xa08e,
+ 0x0f00, 0x00c0, 0x5507, 0x2009, 0x0020, 0x0078, 0x5534, 0xa08e,
+ 0x5300, 0x00c0, 0x550d, 0x0078, 0x552a, 0xa08e, 0x6104, 0x00c0,
+ 0x552a, 0x2011, 0xa88d, 0x8208, 0x2204, 0xa082, 0x0004, 0x20a8,
+ 0x95ac, 0x95ac, 0x2011, 0x8015, 0x211c, 0x8108, 0x047e, 0x2124,
+ 0x1078, 0x3579, 0x047f, 0x8108, 0x00f0, 0x551a, 0x2009, 0x0023,
+ 0x0078, 0x5534, 0xa08e, 0x6000, 0x00c0, 0x5532, 0x2009, 0x003f,
+ 0x0078, 0x5534, 0x2009, 0x001d, 0x017e, 0x2011, 0xa883, 0x2204,
+ 0x8211, 0x220c, 0x1078, 0x24e3, 0x00c0, 0x556a, 0x1078, 0x4499,
+ 0x00c0, 0x556a, 0x6612, 0x6516, 0x86ff, 0x0040, 0x555a, 0x017f,
+ 0x017e, 0xa186, 0x0017, 0x00c0, 0x555a, 0x6868, 0xa606, 0x00c0,
+ 0x555a, 0x686c, 0xa506, 0xa084, 0xff00, 0x00c0, 0x555a, 0x6000,
+ 0xc0f5, 0x6002, 0x0c7e, 0x1078, 0x74d7, 0x0040, 0x556d, 0x017f,
+ 0x611a, 0x601f, 0x0004, 0x7120, 0x610a, 0x017f, 0x1078, 0x756c,
+ 0x0c7f, 0x007c, 0x017f, 0x0078, 0x5568, 0x0c7f, 0x0078, 0x556a,
+ 0x0c7e, 0x1078, 0x55d4, 0x00c0, 0x55d2, 0xa184, 0xff00, 0x8007,
+ 0xa086, 0x0008, 0x00c0, 0x55d2, 0xa28e, 0x0033, 0x00c0, 0x55a3,
+ 0x1078, 0x57b2, 0x0040, 0x55d2, 0x7124, 0x610a, 0x7030, 0xa08e,
+ 0x0200, 0x00c0, 0x5595, 0x7034, 0xa005, 0x00c0, 0x55d2, 0x2009,
+ 0x0015, 0x1078, 0x756c, 0x0078, 0x55d2, 0xa08e, 0x0100, 0x00c0,
+ 0x55d2, 0x7034, 0xa005, 0x00c0, 0x55d2, 0x2009, 0x0016, 0x1078,
+ 0x756c, 0x0078, 0x55d2, 0xa28e, 0x0032, 0x00c0, 0x55d2, 0x7030,
+ 0xa08e, 0x1400, 0x00c0, 0x55d2, 0x2009, 0x0038, 0x017e, 0x2011,
+ 0xa883, 0x2204, 0x8211, 0x220c, 0x1078, 0x24e3, 0x00c0, 0x55d1,
+ 0x1078, 0x4499, 0x00c0, 0x55d1, 0x6612, 0x6516, 0x0c7e, 0x1078,
+ 0x74d7, 0x0040, 0x55d0, 0x017f, 0x611a, 0x601f, 0x0004, 0x7120,
+ 0x610a, 0x017f, 0x1078, 0x756c, 0x1078, 0x6109, 0x0078, 0x55d2,
+ 0x0c7f, 0x017f, 0x0c7f, 0x007c, 0x0f7e, 0x0d7e, 0x027e, 0x017e,
+ 0x137e, 0x147e, 0x157e, 0x3c00, 0x007e, 0x2079, 0x0030, 0x2069,
+ 0x0200, 0x1078, 0x1c25, 0x00c0, 0x5615, 0x1078, 0x1b15, 0x0040,
+ 0x561f, 0x7908, 0xa18c, 0x1fff, 0xa182, 0x0011, 0x00c8, 0x561f,
+ 0x20a9, 0x000c, 0x20e1, 0x0000, 0x2ea0, 0x2099, 0x020a, 0x53a5,
+ 0x20e1, 0x2000, 0x2001, 0x020a, 0x2004, 0x7a0c, 0x7808, 0xa080,
+ 0x0007, 0xa084, 0x1ff8, 0xa08a, 0x0140, 0x10c8, 0x1328, 0x80ac,
+ 0x20e1, 0x6000, 0x2099, 0x020a, 0x53a5, 0x20e1, 0x7000, 0x6828,
+ 0x6828, 0x7803, 0x0004, 0xa294, 0x0070, 0x007f, 0x20e0, 0x157f,
+ 0x147f, 0x137f, 0x017f, 0x027f, 0x0d7f, 0x0f7f, 0x007c, 0xa085,
+ 0x0001, 0x0078, 0x5615, 0x047e, 0x0e7e, 0x0d7e, 0x2028, 0x2130,
+ 0xa696, 0x00ff, 0x00c0, 0x5644, 0xa596, 0xfffd, 0x00c0, 0x5634,
+ 0x2009, 0x007f, 0x0078, 0x5677, 0xa596, 0xfffe, 0x00c0, 0x563c,
+ 0x2009, 0x007e, 0x0078, 0x5677, 0xa596, 0xfffc, 0x00c0, 0x5644,
+ 0x2009, 0x0080, 0x0078, 0x5677, 0x2011, 0x0000, 0x2021, 0x0081,
+ 0x20a9, 0x007e, 0x2071, 0xa4b5, 0x2e1c, 0x83ff, 0x00c0, 0x5656,
+ 0x82ff, 0x00c0, 0x566b, 0x2410, 0x0078, 0x566b, 0x2368, 0x6f10,
+ 0x007e, 0x2100, 0xa706, 0x007f, 0x6b14, 0x00c0, 0x5665, 0xa346,
+ 0x00c0, 0x5665, 0x2408, 0x0078, 0x5677, 0x87ff, 0x00c0, 0x566b,
+ 0x83ff, 0x0040, 0x5650, 0x8420, 0x8e70, 0x00f0, 0x564c, 0x82ff,
+ 0x00c0, 0x5676, 0xa085, 0x0001, 0x0078, 0x5678, 0x2208, 0xa006,
+ 0x0d7f, 0x0e7f, 0x047f, 0x007c, 0xa084, 0x0007, 0x0079, 0x5681,
+ 0x007c, 0x5689, 0x5689, 0x5689, 0x57c8, 0x5689, 0x568a, 0x56a3,
+ 0x56f3, 0x007c, 0x7110, 0xd1bc, 0x0040, 0x56a2, 0x7120, 0x2160,
+ 0xac8c, 0x000f, 0x00c0, 0x56a2, 0xac8a, 0xaa00, 0x0048, 0x56a2,
+ 0x6854, 0xac02, 0x00c8, 0x56a2, 0x7124, 0x610a, 0x2009, 0x0046,
+ 0x1078, 0x756c, 0x007c, 0x0c7e, 0x7110, 0xd1bc, 0x00c0, 0x56f1,
+ 0x2011, 0xa883, 0x2204, 0x8211, 0x220c, 0x1078, 0x24e3, 0x00c0,
+ 0x56f1, 0x1078, 0x4499, 0x00c0, 0x56f1, 0x6612, 0x6516, 0x6000,
+ 0xd0ec, 0x00c0, 0x56f1, 0x6204, 0xa294, 0xff00, 0x8217, 0xa286,
+ 0x0006, 0x00c0, 0x56d6, 0x0c7e, 0x1078, 0x74d7, 0x017f, 0x0040,
+ 0x56f1, 0x611a, 0x601f, 0x0006, 0x7120, 0x610a, 0x7130, 0x6122,
+ 0x2009, 0x0044, 0x1078, 0x756c, 0x0078, 0x56f1, 0x0c7e, 0x1078,
+ 0x74d7, 0x017f, 0x0040, 0x56f1, 0x611a, 0x601f, 0x0004, 0x7120,
+ 0x610a, 0xa286, 0x0004, 0x00c0, 0x56e9, 0x6007, 0x0005, 0x0078,
+ 0x56eb, 0x6007, 0x0001, 0x6003, 0x0001, 0x1078, 0x5c45, 0x1078,
+ 0x6109, 0x0c7f, 0x007c, 0x7110, 0xd1bc, 0x0040, 0x570b, 0x7020,
+ 0x2060, 0xac84, 0x000f, 0x00c0, 0x570b, 0xac82, 0xaa00, 0x0048,
+ 0x570b, 0x6854, 0xac02, 0x00c8, 0x570b, 0x7124, 0x610a, 0x2009,
+ 0x0045, 0x1078, 0x756c, 0x007c, 0x7110, 0xa18c, 0xff00, 0x810f,
+ 0xa18e, 0x0000, 0x00c0, 0x571c, 0xa084, 0x000f, 0xa08a, 0x0006,
+ 0x00c8, 0x571c, 0x1079, 0x571d, 0x007c, 0x5723, 0x5724, 0x5723,
+ 0x5723, 0x5794, 0x57a3, 0x007c, 0x7110, 0xd1bc, 0x0040, 0x572c,
+ 0x702c, 0xd084, 0x0040, 0x5793, 0x700c, 0x7108, 0x1078, 0x24e3,
+ 0x00c0, 0x5793, 0x1078, 0x4499, 0x00c0, 0x5793, 0x6612, 0x6516,
+ 0x6204, 0x7110, 0xd1bc, 0x0040, 0x575e, 0xa28c, 0x00ff, 0xa186,
+ 0x0004, 0x0040, 0x5747, 0xa186, 0x0006, 0x00c0, 0x5784, 0x0c7e,
+ 0x1078, 0x57b2, 0x0c7f, 0x0040, 0x5793, 0x0c7e, 0x1078, 0x74d7,
+ 0x017f, 0x0040, 0x5793, 0x611a, 0x601f, 0x0002, 0x7120, 0x610a,
+ 0x2009, 0x0088, 0x1078, 0x756c, 0x0078, 0x5793, 0xa28c, 0x00ff,
+ 0xa186, 0x0006, 0x0040, 0x5773, 0xa186, 0x0004, 0x0040, 0x5773,
+ 0xa294, 0xff00, 0x8217, 0xa286, 0x0004, 0x0040, 0x5773, 0xa286,
+ 0x0006, 0x00c0, 0x5784, 0x0c7e, 0x1078, 0x74d7, 0x017f, 0x0040,
+ 0x5793, 0x611a, 0x601f, 0x0005, 0x7120, 0x610a, 0x2009, 0x0088,
+ 0x1078, 0x756c, 0x0078, 0x5793, 0x0c7e, 0x1078, 0x74d7, 0x017f,
+ 0x0040, 0x5793, 0x611a, 0x601f, 0x0004, 0x7120, 0x610a, 0x2009,
+ 0x0001, 0x1078, 0x756c, 0x007c, 0x7110, 0xd1bc, 0x0040, 0x57a2,
+ 0x1078, 0x57b2, 0x0040, 0x57a2, 0x7124, 0x610a, 0x2009, 0x0089,
+ 0x1078, 0x756c, 0x007c, 0x7110, 0xd1bc, 0x0040, 0x57b1, 0x1078,
+ 0x57b2, 0x0040, 0x57b1, 0x7124, 0x610a, 0x2009, 0x008a, 0x1078,
+ 0x756c, 0x007c, 0x7020, 0x2060, 0xac84, 0x000f, 0x00c0, 0x57c5,
+ 0xac82, 0xaa00, 0x0048, 0x57c5, 0x2001, 0xa315, 0x2004, 0xac02,
+ 0x00c8, 0x57c5, 0xa085, 0x0001, 0x007c, 0xa006, 0x0078, 0x57c4,
+ 0x7110, 0xd1bc, 0x00c0, 0x57de, 0x7024, 0x2060, 0xac84, 0x000f,
+ 0x00c0, 0x57de, 0xac82, 0xaa00, 0x0048, 0x57de, 0x6854, 0xac02,
+ 0x00c8, 0x57de, 0x2009, 0x0051, 0x1078, 0x756c, 0x007c, 0x2071,
+ 0xa5be, 0x7003, 0x0003, 0x700f, 0x0361, 0xa006, 0x701a, 0x7012,
+ 0x7017, 0xaa00, 0x7007, 0x0000, 0x7026, 0x702b, 0x6c4e, 0x7032,
+ 0x7037, 0x6ca0, 0x703b, 0x0002, 0x703f, 0x0000, 0x7043, 0xffff,
+ 0x7047, 0xffff, 0x007c, 0x2071, 0xa5be, 0x00e0, 0x58c1, 0x2091,
+ 0x6000, 0x700c, 0x8001, 0x700e, 0x00c0, 0x5873, 0x700f, 0x0361,
+ 0x7007, 0x0001, 0x127e, 0x2091, 0x8000, 0x7138, 0x8109, 0x713a,
+ 0x00c0, 0x5871, 0x703b, 0x0002, 0x2009, 0x0100, 0x2104, 0xa082,
+ 0x0003, 0x00c8, 0x5871, 0x703c, 0xa086, 0x0001, 0x00c0, 0x584e,
+ 0x0d7e, 0x2069, 0x0140, 0x6804, 0xa084, 0x4000, 0x0040, 0x582c,
+ 0x6803, 0x1000, 0x0078, 0x5833, 0x6804, 0xa084, 0x1000, 0x0040,
+ 0x5833, 0x6803, 0x0100, 0x6803, 0x0000, 0x703f, 0x0000, 0x2069,
+ 0xa5ab, 0x6804, 0xa082, 0x0006, 0x00c0, 0x5840, 0x6807, 0x0000,
+ 0x6830, 0xa082, 0x0003, 0x00c0, 0x5847, 0x6833, 0x0000, 0x1078,
+ 0x6109, 0x1078, 0x61d3, 0x0d7f, 0x0078, 0x5871, 0x0d7e, 0x2069,
+ 0xa300, 0x6944, 0x6860, 0xa102, 0x00c8, 0x5870, 0x2069, 0xa5ab,
+ 0x6804, 0xa086, 0x0000, 0x00c0, 0x5870, 0x6830, 0xa086, 0x0000,
+ 0x00c0, 0x5870, 0x703f, 0x0001, 0x6807, 0x0006, 0x6833, 0x0003,
+ 0x2069, 0x0100, 0x6830, 0x689e, 0x2069, 0x0140, 0x6803, 0x0600,
+ 0x0d7f, 0x0078, 0x5876, 0x127e, 0x2091, 0x8000, 0x7024, 0xa00d,
+ 0x0040, 0x588e, 0x7020, 0x8001, 0x7022, 0x00c0, 0x588e, 0x7023,
+ 0x0009, 0x8109, 0x7126, 0xa186, 0x03e8, 0x00c0, 0x5889, 0x7028,
+ 0x107a, 0x81ff, 0x00c0, 0x588e, 0x7028, 0x107a, 0x7030, 0xa00d,
+ 0x0040, 0x589f, 0x702c, 0x8001, 0x702e, 0x00c0, 0x589f, 0x702f,
+ 0x0009, 0x8109, 0x7132, 0x00c0, 0x589f, 0x7034, 0x107a, 0x7040,
+ 0xa005, 0x0040, 0x58a7, 0x0050, 0x58a7, 0x8001, 0x7042, 0x7044,
+ 0xa005, 0x0040, 0x58af, 0x0050, 0x58af, 0x8001, 0x7046, 0x7018,
+ 0xa00d, 0x0040, 0x58c0, 0x7008, 0x8001, 0x700a, 0x00c0, 0x58c0,
+ 0x700b, 0x0009, 0x8109, 0x711a, 0x00c0, 0x58c0, 0x701c, 0x107a,
+ 0x127f, 0x7004, 0x0079, 0x58c4, 0x58eb, 0x58ec, 0x5908, 0x0e7e,
+ 0x2071, 0xa5be, 0x7018, 0xa005, 0x00c0, 0x58d2, 0x711a, 0x721e,
+ 0x700b, 0x0009, 0x0e7f, 0x007c, 0x0e7e, 0x007e, 0x2071, 0xa5be,
+ 0x701c, 0xa206, 0x00c0, 0x58de, 0x701a, 0x701e, 0x007f, 0x0e7f,
+ 0x007c, 0x0e7e, 0x2071, 0xa5be, 0x6088, 0xa102, 0x0048, 0x58e9,
+ 0x618a, 0x0e7f, 0x007c, 0x007c, 0x7110, 0x1078, 0x4501, 0x00c0,
+ 0x58fe, 0x6088, 0x8001, 0x0048, 0x58fe, 0x608a, 0x00c0, 0x58fe,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x6109, 0x127f, 0x8108, 0xa182,
+ 0x00ff, 0x0048, 0x5906, 0xa00e, 0x7007, 0x0002, 0x7112, 0x007c,
+ 0x7014, 0x2060, 0x127e, 0x2091, 0x8000, 0x603c, 0xa005, 0x0040,
+ 0x5917, 0x8001, 0x603e, 0x00c0, 0x5917, 0x1078, 0x8cd7, 0x6014,
+ 0xa005, 0x0040, 0x5941, 0x8001, 0x6016, 0x00c0, 0x5941, 0x611c,
+ 0xa186, 0x0003, 0x0040, 0x5928, 0xa186, 0x0006, 0x00c0, 0x593f,
+ 0x6010, 0x2068, 0x6854, 0xa08a, 0x199a, 0x0048, 0x593f, 0xa082,
+ 0x1999, 0x6856, 0xa08a, 0x199a, 0x0048, 0x5938, 0x2001, 0x1999,
+ 0x8003, 0x800b, 0x810b, 0xa108, 0x6116, 0x0078, 0x5941, 0x1078,
+ 0x8810, 0x127f, 0xac88, 0x0010, 0x7116, 0x2001, 0xca00, 0xa102,
+ 0x0048, 0x594e, 0x7017, 0xaa00, 0x7007, 0x0000, 0x007c, 0x0e7e,
+ 0x2071, 0xa5be, 0x7027, 0x07d0, 0x7023, 0x0009, 0x703b, 0x0002,
+ 0x0e7f, 0x007c, 0x2001, 0xa5c7, 0x2003, 0x0000, 0x007c, 0x0e7e,
+ 0x2071, 0xa5be, 0x7132, 0x702f, 0x0009, 0x0e7f, 0x007c, 0x2011,
+ 0xa5ca, 0x2013, 0x0000, 0x007c, 0x0e7e, 0x2071, 0xa5be, 0x711a,
+ 0x721e, 0x700b, 0x0009, 0x0e7f, 0x007c, 0x027e, 0x0e7e, 0x0f7e,
+ 0x2079, 0xa300, 0x7a34, 0xd294, 0x0040, 0x59a4, 0x2071, 0xa5aa,
+ 0x2e14, 0xa0fe, 0x0000, 0x0040, 0x5991, 0xa0fe, 0x0001, 0x0040,
+ 0x5995, 0xa0fe, 0x0002, 0x00c0, 0x59a0, 0xa292, 0x0085, 0x0078,
+ 0x5997, 0xa292, 0x0005, 0x0078, 0x5997, 0xa292, 0x0002, 0x2272,
+ 0x0040, 0x599c, 0x00c8, 0x59a4, 0x2011, 0x8037, 0x1078, 0x3579,
+ 0x2011, 0xa5a9, 0x2204, 0x2072, 0x0f7f, 0x0e7f, 0x027f, 0x007c,
+ 0x0c7e, 0x2061, 0xa62d, 0x0c7f, 0x007c, 0xa184, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa080, 0xa62d, 0x2060, 0x007c, 0x6854, 0xa08a,
+ 0x199a, 0x0048, 0x59bd, 0x2001, 0x1999, 0xa005, 0x00c0, 0x59cc,
+ 0x0c7e, 0x2061, 0xa62d, 0x6014, 0x0c7f, 0xa005, 0x00c0, 0x59d1,
+ 0x2001, 0x001e, 0x0078, 0x59d1, 0xa08e, 0xffff, 0x00c0, 0x59d1,
+ 0xa006, 0x8003, 0x800b, 0x810b, 0xa108, 0x6116, 0x684c, 0xa08c,
+ 0x00c0, 0xa18e, 0x00c0, 0x0040, 0x5a24, 0xd0b4, 0x00c0, 0x59e8,
+ 0xd0bc, 0x00c0, 0x5a14, 0x2009, 0x0006, 0x1078, 0x5a43, 0x007c,
+ 0xd0fc, 0x0040, 0x59f3, 0xa084, 0x0003, 0x0040, 0x59f3, 0xa086,
+ 0x0003, 0x00c0, 0x5a3c, 0x6024, 0xd0d4, 0x0040, 0x59fd, 0xc0d4,
+ 0x6026, 0x6860, 0x602a, 0x685c, 0x602e, 0x2009, 0xa373, 0x2104,
+ 0xd084, 0x0040, 0x5a0f, 0x6118, 0xa188, 0x0027, 0x2104, 0xd08c,
+ 0x00c0, 0x5a0f, 0x2009, 0x0042, 0x1078, 0x756c, 0x007c, 0x2009,
+ 0x0043, 0x1078, 0x756c, 0x007c, 0xd0fc, 0x0040, 0x5a1f, 0xa084,
+ 0x0003, 0x0040, 0x5a1f, 0xa086, 0x0003, 0x00c0, 0x5a3c, 0x2009,
+ 0x0042, 0x1078, 0x756c, 0x007c, 0xd0fc, 0x0040, 0x5a32, 0xa084,
+ 0x0003, 0xa08e, 0x0002, 0x0040, 0x5a36, 0x2009, 0x0041, 0x1078,
+ 0x756c, 0x007c, 0x1078, 0x5a41, 0x0078, 0x5a31, 0x2009, 0x0043,
+ 0x1078, 0x756c, 0x0078, 0x5a31, 0x2009, 0x0004, 0x1078, 0x5a43,
+ 0x007c, 0x2009, 0x0001, 0x0d7e, 0x6010, 0xa0ec, 0xf000, 0x0040,
+ 0x5a6b, 0x2068, 0x6952, 0x6800, 0x6012, 0xa186, 0x0001, 0x00c0,
+ 0x5a65, 0x694c, 0xa18c, 0x8100, 0xa18e, 0x8100, 0x00c0, 0x5a65,
+ 0x0c7e, 0x2061, 0xa62d, 0x6200, 0xd28c, 0x00c0, 0x5a64, 0x6204,
+ 0x8210, 0x0048, 0x5a64, 0x6206, 0x0c7f, 0x1078, 0x4982, 0x6010,
+ 0xa06d, 0x10c0, 0x59b6, 0x0d7f, 0x007c, 0x157e, 0x0c7e, 0x2061,
+ 0xa62d, 0x6000, 0x81ff, 0x0040, 0x5a78, 0xa205, 0x0078, 0x5a79,
+ 0xa204, 0x6002, 0x0c7f, 0x157f, 0x007c, 0x6800, 0xd08c, 0x00c0,
+ 0x5a89, 0x6808, 0xa005, 0x0040, 0x5a89, 0x8001, 0x680a, 0xa085,
+ 0x0001, 0x007c, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, 0x818e,
+ 0x00c8, 0x5a93, 0xa200, 0x00f0, 0x5a8e, 0x8086, 0x818e, 0x007c,
+ 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x5ab9, 0xa11a, 0x00c8,
+ 0x5ab9, 0x8213, 0x818d, 0x0048, 0x5aac, 0xa11a, 0x00c8, 0x5aad,
+ 0x00f0, 0x5aa1, 0x0078, 0x5ab1, 0xa11a, 0x2308, 0x8210, 0x00f0,
+ 0x5aa1, 0x007e, 0x3200, 0xa084, 0xf7ff, 0x2080, 0x007f, 0x157f,
+ 0x007c, 0x007e, 0x3200, 0xa085, 0x0800, 0x0078, 0x5ab5, 0x127e,
+ 0x2091, 0x2200, 0x2079, 0xa5ab, 0x127f, 0x0d7e, 0x2069, 0xa5ab,
+ 0x6803, 0x0005, 0x2069, 0x0004, 0x2d04, 0xa085, 0x8001, 0x206a,
+ 0x0d7f, 0x007c, 0x0c7e, 0x6027, 0x0001, 0x7804, 0xa084, 0x0007,
+ 0x0079, 0x5ada, 0x5ae4, 0x5b09, 0x5b64, 0x5aea, 0x5b09, 0x5ae4,
+ 0x5ae2, 0x5ae2, 0x1078, 0x1328, 0x1078, 0x595a, 0x1078, 0x6109,
+ 0x0c7f, 0x007c, 0x62c0, 0x82ff, 0x00c0, 0x5af0, 0x0c7f, 0x007c,
+ 0x2011, 0x4129, 0x1078, 0x58d4, 0x7828, 0xa092, 0x00c8, 0x00c8,
+ 0x5aff, 0x8000, 0x782a, 0x1078, 0x4168, 0x0078, 0x5aee, 0x1078,
+ 0x4129, 0x7807, 0x0003, 0x7827, 0x0000, 0x782b, 0x0000, 0x0078,
+ 0x5aee, 0x1078, 0x595a, 0x3c00, 0x007e, 0x2011, 0x0209, 0x20e1,
+ 0x4000, 0x2214, 0x007f, 0x20e0, 0x82ff, 0x0040, 0x5b27, 0x62c0,
+ 0x82ff, 0x00c0, 0x5b27, 0x782b, 0x0000, 0x7824, 0xa065, 0x1040,
+ 0x1328, 0x2009, 0x0013, 0x1078, 0x756c, 0x0c7f, 0x007c, 0x3900,
+ 0xa082, 0xa6cd, 0x00c8, 0x5b2e, 0x1078, 0x728a, 0x0c7e, 0x7824,
+ 0xa065, 0x1040, 0x1328, 0x7804, 0xa086, 0x0004, 0x0040, 0x5ba9,
+ 0x7828, 0xa092, 0x2710, 0x00c8, 0x5b44, 0x8000, 0x782a, 0x0c7f,
+ 0x1078, 0x6c33, 0x0078, 0x5b25, 0x6104, 0xa186, 0x0003, 0x00c0,
+ 0x5b5b, 0x0e7e, 0x2071, 0xa300, 0x70d4, 0x0e7f, 0xd08c, 0x0040,
+ 0x5b5b, 0x0c7e, 0x0e7e, 0x2061, 0x0100, 0x2071, 0xa300, 0x1078,
+ 0x4171, 0x0e7f, 0x0c7f, 0x1078, 0xa241, 0x2009, 0x0014, 0x1078,
+ 0x756c, 0x0c7f, 0x0078, 0x5b25, 0x2001, 0xa5c7, 0x2003, 0x0000,
+ 0x62c0, 0x82ff, 0x00c0, 0x5b78, 0x782b, 0x0000, 0x7824, 0xa065,
+ 0x1040, 0x1328, 0x2009, 0x0013, 0x1078, 0x75c3, 0x0c7f, 0x007c,
+ 0x0c7e, 0x0d7e, 0x3900, 0xa082, 0xa6cd, 0x00c8, 0x5b81, 0x1078,
+ 0x728a, 0x7824, 0xa005, 0x1040, 0x1328, 0x781c, 0xa06d, 0x1040,
+ 0x1328, 0x6800, 0xc0dc, 0x6802, 0x7924, 0x2160, 0x1078, 0x753d,
+ 0x693c, 0x81ff, 0x1040, 0x1328, 0x8109, 0x693e, 0x6854, 0xa015,
+ 0x0040, 0x5b9d, 0x7a1e, 0x0078, 0x5b9f, 0x7918, 0x791e, 0x7807,
+ 0x0000, 0x7827, 0x0000, 0x0d7f, 0x0c7f, 0x1078, 0x6109, 0x0078,
+ 0x5b76, 0x6104, 0xa186, 0x0002, 0x0040, 0x5bb4, 0xa186, 0x0004,
+ 0x0040, 0x5bb4, 0x0078, 0x5b38, 0x7808, 0xac06, 0x0040, 0x5b38,
+ 0x1078, 0x6010, 0x1078, 0x5c45, 0x0c7f, 0x1078, 0x6109, 0x0078,
+ 0x5b25, 0x0c7e, 0x6027, 0x0002, 0x62c8, 0x82ff, 0x00c0, 0x5bdb,
+ 0x62c4, 0x82ff, 0x00c0, 0x5bdb, 0x793c, 0xa1e5, 0x0000, 0x0040,
+ 0x5bd5, 0x2009, 0x0049, 0x1078, 0x756c, 0x2011, 0xa5ca, 0x2013,
+ 0x0000, 0x0c7f, 0x007c, 0x3908, 0xa192, 0xa6cd, 0x00c8, 0x5be2,
+ 0x1078, 0x728a, 0x6017, 0x0010, 0x793c, 0x81ff, 0x0040, 0x5bd5,
+ 0x793c, 0xa188, 0x0007, 0x210c, 0xa18e, 0x0006, 0x00c0, 0x5bf4,
+ 0x6017, 0x0012, 0x0078, 0x5bd9, 0x6017, 0x0016, 0x0078, 0x5bd9,
+ 0x007e, 0x017e, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x600f, 0x0000,
+ 0x2c08, 0x2061, 0xa5ab, 0x6020, 0x8000, 0x6022, 0x6010, 0xa005,
+ 0x0040, 0x5c13, 0xa080, 0x0003, 0x2102, 0x6112, 0x127f, 0x0c7f,
+ 0x017f, 0x007f, 0x007c, 0x6116, 0x6112, 0x0078, 0x5c0e, 0x0d7e,
+ 0x2069, 0xa5ab, 0x6000, 0xd0d4, 0x0040, 0x5c2c, 0x6820, 0x8000,
+ 0x6822, 0xa086, 0x0001, 0x00c0, 0x5c27, 0x2c00, 0x681e, 0x6804,
+ 0xa084, 0x0007, 0x0079, 0x6111, 0xc0d5, 0x6002, 0x6818, 0xa005,
+ 0x0040, 0x5c3e, 0x6056, 0x605b, 0x0000, 0x007e, 0x2c00, 0x681a,
+ 0x0d7f, 0x685a, 0x2069, 0xa5ab, 0x0078, 0x5c1e, 0x6056, 0x605a,
+ 0x2c00, 0x681a, 0x681e, 0x0078, 0x5c1e, 0x007e, 0x017e, 0x0c7e,
+ 0x127e, 0x2091, 0x8000, 0x600f, 0x0000, 0x2c08, 0x2061, 0xa5ab,
+ 0x6020, 0x8000, 0x6022, 0x6008, 0xa005, 0x0040, 0x5c60, 0xa080,
+ 0x0003, 0x2102, 0x610a, 0x127f, 0x0c7f, 0x017f, 0x007f, 0x007c,
+ 0x610e, 0x610a, 0x0078, 0x5c5b, 0x0c7e, 0x600f, 0x0000, 0x2c08,
+ 0x2061, 0xa5ab, 0x6034, 0xa005, 0x0040, 0x5c74, 0xa080, 0x0003,
+ 0x2102, 0x6136, 0x0c7f, 0x007c, 0x613a, 0x6136, 0x0078, 0x5c72,
+ 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e, 0x027e, 0x017e, 0x007e,
+ 0x127e, 0x2071, 0xa5ab, 0x7638, 0x2660, 0x2678, 0x2091, 0x8000,
+ 0x8cff, 0x0040, 0x5ced, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206,
+ 0x00c0, 0x5ce8, 0x87ff, 0x0040, 0x5c99, 0x6020, 0xa106, 0x00c0,
+ 0x5ce8, 0x703c, 0xac06, 0x00c0, 0x5cab, 0x037e, 0x2019, 0x0001,
+ 0x1078, 0x6e6c, 0x7033, 0x0000, 0x703f, 0x0000, 0x7043, 0x0000,
+ 0x7047, 0x0000, 0x037f, 0x7038, 0xac36, 0x00c0, 0x5cb1, 0x660c,
+ 0x763a, 0x7034, 0xac36, 0x00c0, 0x5cbf, 0x2c00, 0xaf36, 0x0040,
+ 0x5cbd, 0x2f00, 0x7036, 0x0078, 0x5cbf, 0x7037, 0x0000, 0x660c,
+ 0x067e, 0x2c00, 0xaf06, 0x0040, 0x5cc8, 0x7e0e, 0x0078, 0x5cc9,
+ 0x2678, 0x600f, 0x0000, 0x1078, 0x8a44, 0x0040, 0x5ce3, 0x6010,
+ 0x2068, 0x601c, 0xa086, 0x0003, 0x00c0, 0x5cf7, 0x6837, 0x0103,
+ 0x6b4a, 0x6847, 0x0000, 0x1078, 0x8cb8, 0x1078, 0xa181, 0x1078,
+ 0x4982, 0x1078, 0x8bf4, 0x1078, 0x8c01, 0x0c7f, 0x0078, 0x5c88,
+ 0x2c78, 0x600c, 0x2060, 0x0078, 0x5c88, 0x127f, 0x007f, 0x017f,
+ 0x027f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x601c,
+ 0xa086, 0x0006, 0x00c0, 0x5cd6, 0x1078, 0xa181, 0x1078, 0x9e70,
+ 0x0078, 0x5ce3, 0x007e, 0x067e, 0x0c7e, 0x0d7e, 0x0f7e, 0x2031,
+ 0x0000, 0x127e, 0x2091, 0x8000, 0x2079, 0xa5ab, 0x7838, 0xa065,
+ 0x0040, 0x5d41, 0x600c, 0x007e, 0x600f, 0x0000, 0x783c, 0xac06,
+ 0x00c0, 0x5d28, 0x037e, 0x2019, 0x0001, 0x1078, 0x6e6c, 0x7833,
+ 0x0000, 0x783f, 0x0000, 0x7843, 0x0000, 0x7847, 0x0000, 0x037f,
+ 0x1078, 0x8a44, 0x0040, 0x5d3c, 0x6010, 0x2068, 0x601c, 0xa086,
+ 0x0003, 0x00c0, 0x5d4a, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000,
+ 0x1078, 0x4982, 0x1078, 0x8bf4, 0x1078, 0x8c01, 0x007f, 0x0078,
+ 0x5d0f, 0x7e3a, 0x7e36, 0x127f, 0x0f7f, 0x0d7f, 0x0c7f, 0x067f,
+ 0x007f, 0x007c, 0x601c, 0xa086, 0x0006, 0x00c0, 0x5d33, 0x1078,
+ 0x9e70, 0x0078, 0x5d3c, 0x017e, 0x027e, 0x087e, 0x2041, 0x0000,
+ 0x1078, 0x5d6d, 0x1078, 0x5e21, 0x087f, 0x027f, 0x017f, 0x007c,
+ 0x0f7e, 0x127e, 0x2079, 0xa5ab, 0x2091, 0x8000, 0x1078, 0x5ebc,
+ 0x1078, 0x5f32, 0x127f, 0x0f7f, 0x007c, 0x0f7e, 0x0e7e, 0x0d7e,
+ 0x0c7e, 0x067e, 0x017e, 0x007e, 0x127e, 0x2091, 0x8000, 0x2071,
+ 0xa5ab, 0x7614, 0x2660, 0x2678, 0x8cff, 0x0040, 0x5e01, 0x6018,
+ 0xa080, 0x0028, 0x2004, 0xa206, 0x00c0, 0x5dfc, 0x88ff, 0x0040,
+ 0x5d8d, 0x6020, 0xa106, 0x00c0, 0x5dfc, 0x7024, 0xac06, 0x00c0,
+ 0x5dbd, 0x2069, 0x0100, 0x68c0, 0xa005, 0x0040, 0x5db8, 0x1078,
+ 0x595a, 0x1078, 0x6c41, 0x68c3, 0x0000, 0x1078, 0x7188, 0x7027,
+ 0x0000, 0x037e, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040,
+ 0x5dad, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824,
+ 0xd084, 0x0040, 0x5db5, 0x6827, 0x0001, 0x037f, 0x0078, 0x5dbd,
+ 0x6003, 0x0009, 0x630a, 0x0078, 0x5dfc, 0x7014, 0xac36, 0x00c0,
+ 0x5dc3, 0x660c, 0x7616, 0x7010, 0xac36, 0x00c0, 0x5dd1, 0x2c00,
+ 0xaf36, 0x0040, 0x5dcf, 0x2f00, 0x7012, 0x0078, 0x5dd1, 0x7013,
+ 0x0000, 0x660c, 0x067e, 0x2c00, 0xaf06, 0x0040, 0x5dda, 0x7e0e,
+ 0x0078, 0x5ddb, 0x2678, 0x600f, 0x0000, 0x6010, 0x2068, 0x1078,
+ 0x8a44, 0x0040, 0x5df5, 0x601c, 0xa086, 0x0003, 0x00c0, 0x5e0a,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x8cb8, 0x1078,
+ 0xa181, 0x1078, 0x4982, 0x1078, 0x8bf4, 0x1078, 0x8c01, 0x1078,
+ 0x7045, 0x0c7f, 0x0078, 0x5d7c, 0x2c78, 0x600c, 0x2060, 0x0078,
+ 0x5d7c, 0x127f, 0x007f, 0x017f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f,
+ 0x0f7f, 0x007c, 0x601c, 0xa086, 0x0006, 0x00c0, 0x5e15, 0x1078,
+ 0xa181, 0x1078, 0x9e70, 0x0078, 0x5df5, 0x601c, 0xa086, 0x0002,
+ 0x00c0, 0x5df5, 0x6004, 0xa086, 0x0085, 0x0040, 0x5de8, 0x0078,
+ 0x5df5, 0x0c7e, 0x007e, 0x127e, 0x2091, 0x8000, 0xa280, 0xa434,
+ 0x2004, 0xa065, 0x0040, 0x5eb8, 0x0f7e, 0x0e7e, 0x0d7e, 0x067e,
+ 0x2071, 0xa5ab, 0x6654, 0x7018, 0xac06, 0x00c0, 0x5e38, 0x761a,
+ 0x701c, 0xac06, 0x00c0, 0x5e44, 0x86ff, 0x00c0, 0x5e43, 0x7018,
+ 0x701e, 0x0078, 0x5e44, 0x761e, 0x6058, 0xa07d, 0x0040, 0x5e49,
+ 0x7e56, 0xa6ed, 0x0000, 0x0040, 0x5e4f, 0x2f00, 0x685a, 0x6057,
+ 0x0000, 0x605b, 0x0000, 0x6000, 0xc0d4, 0xc0dc, 0x6002, 0x1078,
+ 0x4410, 0x0040, 0x5eb4, 0x7624, 0x86ff, 0x0040, 0x5ea2, 0xa680,
+ 0x0004, 0x2004, 0xad06, 0x00c0, 0x5ea2, 0x0d7e, 0x2069, 0x0100,
+ 0x68c0, 0xa005, 0x0040, 0x5e99, 0x1078, 0x595a, 0x1078, 0x6c41,
+ 0x68c3, 0x0000, 0x1078, 0x7188, 0x7027, 0x0000, 0x037e, 0x2069,
+ 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040, 0x5e82, 0x6803, 0x0100,
+ 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0040, 0x5e8a,
+ 0x6827, 0x0001, 0x037f, 0x0d7f, 0x0c7e, 0x603c, 0xa005, 0x0040,
+ 0x5e93, 0x8001, 0x603e, 0x2660, 0x1078, 0x8c01, 0x0c7f, 0x0078,
+ 0x5ea2, 0x0d7f, 0x0c7e, 0x2660, 0x6003, 0x0009, 0x630a, 0x0c7f,
+ 0x0078, 0x5e57, 0x8dff, 0x0040, 0x5eb0, 0x6837, 0x0103, 0x6b4a,
+ 0x6847, 0x0000, 0x1078, 0x8cb8, 0x1078, 0xa181, 0x1078, 0x4982,
+ 0x1078, 0x7045, 0x0078, 0x5e57, 0x067f, 0x0d7f, 0x0e7f, 0x0f7f,
+ 0x127f, 0x007f, 0x0c7f, 0x007c, 0x007e, 0x067e, 0x0c7e, 0x0d7e,
+ 0x2031, 0x0000, 0x7814, 0xa065, 0x0040, 0x5f16, 0x600c, 0x007e,
+ 0x600f, 0x0000, 0x7824, 0xac06, 0x00c0, 0x5efb, 0x2069, 0x0100,
+ 0x68c0, 0xa005, 0x0040, 0x5ef5, 0x1078, 0x595a, 0x1078, 0x6c41,
+ 0x68c3, 0x0000, 0x1078, 0x7188, 0x7827, 0x0000, 0x037e, 0x2069,
+ 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040, 0x5eea, 0x6803, 0x0100,
+ 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0040, 0x5ef2,
+ 0x6827, 0x0001, 0x037f, 0x0078, 0x5efb, 0x6003, 0x0009, 0x630a,
+ 0x2c30, 0x0078, 0x5f13, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040,
+ 0x5f0f, 0x601c, 0xa086, 0x0003, 0x00c0, 0x5f1d, 0x6837, 0x0103,
+ 0x6b4a, 0x6847, 0x0000, 0x1078, 0x4982, 0x1078, 0x8bf4, 0x1078,
+ 0x8c01, 0x1078, 0x7045, 0x007f, 0x0078, 0x5ec3, 0x7e16, 0x7e12,
+ 0x0d7f, 0x0c7f, 0x067f, 0x007f, 0x007c, 0x601c, 0xa086, 0x0006,
+ 0x00c0, 0x5f26, 0x1078, 0x9e70, 0x0078, 0x5f0f, 0x601c, 0xa086,
+ 0x0002, 0x00c0, 0x5f0f, 0x6004, 0xa086, 0x0085, 0x0040, 0x5f06,
+ 0x0078, 0x5f0f, 0x007e, 0x067e, 0x0c7e, 0x0d7e, 0x7818, 0xa065,
+ 0x0040, 0x5fa0, 0x6054, 0x007e, 0x6057, 0x0000, 0x605b, 0x0000,
+ 0x6000, 0xc0d4, 0xc0dc, 0x6002, 0x1078, 0x4410, 0x0040, 0x5f9d,
+ 0x7e24, 0x86ff, 0x0040, 0x5f8f, 0xa680, 0x0004, 0x2004, 0xad06,
+ 0x00c0, 0x5f8f, 0x0d7e, 0x2069, 0x0100, 0x68c0, 0xa005, 0x0040,
+ 0x5f86, 0x1078, 0x595a, 0x1078, 0x6c41, 0x68c3, 0x0000, 0x1078,
+ 0x7188, 0x7827, 0x0000, 0x037e, 0x2069, 0x0140, 0x6b04, 0xa384,
+ 0x1000, 0x0040, 0x5f6f, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069,
+ 0x0100, 0x6824, 0xd084, 0x0040, 0x5f77, 0x6827, 0x0001, 0x037f,
+ 0x0d7f, 0x0c7e, 0x603c, 0xa005, 0x0040, 0x5f80, 0x8001, 0x603e,
+ 0x2660, 0x1078, 0x8c01, 0x0c7f, 0x0078, 0x5f8f, 0x0d7f, 0x0c7e,
+ 0x2660, 0x6003, 0x0009, 0x630a, 0x0c7f, 0x0078, 0x5f44, 0x8dff,
+ 0x0040, 0x5f99, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078,
+ 0x4982, 0x1078, 0x7045, 0x0078, 0x5f44, 0x007f, 0x0078, 0x5f37,
+ 0x781e, 0x781a, 0x0d7f, 0x0c7f, 0x067f, 0x007f, 0x007c, 0x0e7e,
+ 0x0d7e, 0x067e, 0x6000, 0xd0dc, 0x0040, 0x5fc4, 0x604c, 0xa06d,
+ 0x0040, 0x5fc4, 0x6848, 0xa606, 0x00c0, 0x5fc4, 0x2071, 0xa5ab,
+ 0x7024, 0xa035, 0x0040, 0x5fc4, 0xa080, 0x0004, 0x2004, 0xad06,
+ 0x00c0, 0x5fc4, 0x1078, 0x5fc8, 0x067f, 0x0d7f, 0x0e7f, 0x007c,
+ 0x0f7e, 0x2079, 0x0100, 0x78c0, 0xa005, 0x00c0, 0x5fd7, 0x0c7e,
+ 0x2660, 0x6003, 0x0009, 0x630a, 0x0c7f, 0x0078, 0x600e, 0x1078,
+ 0x6c41, 0x78c3, 0x0000, 0x1078, 0x7188, 0x7027, 0x0000, 0x037e,
+ 0x2079, 0x0140, 0x7b04, 0xa384, 0x1000, 0x0040, 0x5feb, 0x7803,
+ 0x0100, 0x7803, 0x0000, 0x2079, 0x0100, 0x7824, 0xd084, 0x0040,
+ 0x5ff3, 0x7827, 0x0001, 0x1078, 0x7188, 0x037f, 0x1078, 0x4410,
+ 0x0c7e, 0x603c, 0xa005, 0x0040, 0x5fff, 0x8001, 0x603e, 0x2660,
+ 0x1078, 0x753d, 0x0c7f, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000,
+ 0x1078, 0x8cb8, 0x1078, 0x4982, 0x1078, 0x7045, 0x0f7f, 0x007c,
+ 0x0e7e, 0x0c7e, 0x2071, 0xa5ab, 0x7004, 0xa084, 0x0007, 0x0079,
+ 0x6019, 0x6023, 0x6026, 0x603f, 0x605b, 0x60a0, 0x6023, 0x6023,
+ 0x6021, 0x1078, 0x1328, 0x0c7f, 0x0e7f, 0x007c, 0x7024, 0xa065,
+ 0x0040, 0x6034, 0x7020, 0x8001, 0x7022, 0x600c, 0xa015, 0x0040,
+ 0x603b, 0x7216, 0x600f, 0x0000, 0x7007, 0x0000, 0x7027, 0x0000,
+ 0x0c7f, 0x0e7f, 0x007c, 0x7216, 0x7212, 0x0078, 0x6034, 0x6018,
+ 0x2060, 0x1078, 0x4410, 0x6000, 0xc0dc, 0x6002, 0x7020, 0x8001,
+ 0x7022, 0x0040, 0x6050, 0x6054, 0xa015, 0x0040, 0x6057, 0x721e,
+ 0x7007, 0x0000, 0x7027, 0x0000, 0x0c7f, 0x0e7f, 0x007c, 0x7218,
+ 0x721e, 0x0078, 0x6050, 0x7024, 0xa065, 0x0040, 0x609d, 0x700c,
+ 0xac06, 0x00c0, 0x6072, 0x1078, 0x7045, 0x600c, 0xa015, 0x0040,
+ 0x606e, 0x720e, 0x600f, 0x0000, 0x0078, 0x609b, 0x720e, 0x720a,
+ 0x0078, 0x609b, 0x7014, 0xac06, 0x00c0, 0x6085, 0x1078, 0x7045,
+ 0x600c, 0xa015, 0x0040, 0x6081, 0x7216, 0x600f, 0x0000, 0x0078,
+ 0x609b, 0x7216, 0x7212, 0x0078, 0x609b, 0x6018, 0x2060, 0x1078,
+ 0x4410, 0x6000, 0xc0dc, 0x6002, 0x1078, 0x7045, 0x701c, 0xa065,
+ 0x0040, 0x609b, 0x6054, 0xa015, 0x0040, 0x6099, 0x721e, 0x0078,
+ 0x609b, 0x7218, 0x721e, 0x7027, 0x0000, 0x0c7f, 0x0e7f, 0x007c,
+ 0x7024, 0xa065, 0x0040, 0x60ad, 0x1078, 0x7045, 0x600c, 0xa015,
+ 0x0040, 0x60b4, 0x720e, 0x600f, 0x0000, 0x1078, 0x7188, 0x7027,
+ 0x0000, 0x0c7f, 0x0e7f, 0x007c, 0x720e, 0x720a, 0x0078, 0x60ad,
+ 0x0d7e, 0x2069, 0xa5ab, 0x6830, 0xa084, 0x0003, 0x0079, 0x60c0,
+ 0x60c6, 0x60c8, 0x60ee, 0x60c6, 0x1078, 0x1328, 0x0d7f, 0x007c,
+ 0x0c7e, 0x6840, 0xa086, 0x0001, 0x0040, 0x60e4, 0x683c, 0xa065,
+ 0x0040, 0x60d9, 0x600c, 0xa015, 0x0040, 0x60e0, 0x6a3a, 0x600f,
+ 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x0c7f, 0x0d7f, 0x007c,
+ 0x683a, 0x6836, 0x0078, 0x60d9, 0x6843, 0x0000, 0x6838, 0xa065,
+ 0x0040, 0x60d9, 0x6003, 0x0003, 0x0078, 0x60d9, 0x0c7e, 0x6843,
+ 0x0000, 0x6847, 0x0000, 0x683c, 0xa065, 0x0040, 0x6106, 0x600c,
+ 0xa015, 0x0040, 0x6102, 0x6a3a, 0x600f, 0x0000, 0x683f, 0x0000,
+ 0x0078, 0x6106, 0x683f, 0x0000, 0x683a, 0x6836, 0x0c7f, 0x0d7f,
+ 0x007c, 0x0d7e, 0x2069, 0xa5ab, 0x6804, 0xa084, 0x0007, 0x0079,
+ 0x6111, 0x611b, 0x61c2, 0x61c2, 0x61c2, 0x61c2, 0x61c4, 0x61c2,
+ 0x6119, 0x1078, 0x1328, 0x6820, 0xa005, 0x00c0, 0x6121, 0x0d7f,
+ 0x007c, 0x0c7e, 0x680c, 0xa065, 0x0040, 0x6130, 0x6807, 0x0004,
+ 0x6826, 0x682b, 0x0000, 0x1078, 0x620a, 0x0c7f, 0x0d7f, 0x007c,
+ 0x6814, 0xa065, 0x0040, 0x613e, 0x6807, 0x0001, 0x6826, 0x682b,
+ 0x0000, 0x1078, 0x620a, 0x0c7f, 0x0d7f, 0x007c, 0x0e7e, 0x037e,
+ 0x6a1c, 0xa2f5, 0x0000, 0x0040, 0x61bd, 0x704c, 0xa00d, 0x0040,
+ 0x614d, 0x7088, 0xa005, 0x0040, 0x6165, 0x7054, 0xa075, 0x0040,
+ 0x6156, 0xa20e, 0x0040, 0x61bd, 0x0078, 0x615b, 0x6818, 0xa20e,
+ 0x0040, 0x61bd, 0x2070, 0x704c, 0xa00d, 0x0040, 0x614d, 0x7088,
+ 0xa005, 0x00c0, 0x614d, 0x2e00, 0x681e, 0x733c, 0x7038, 0xa302,
+ 0x00c8, 0x614d, 0x1078, 0x750c, 0x0040, 0x61bd, 0x8318, 0x733e,
+ 0x6112, 0x2e10, 0x621a, 0xa180, 0x0014, 0x2004, 0xa084, 0x00ff,
+ 0x6032, 0xa180, 0x0014, 0x2003, 0x0000, 0xa180, 0x0015, 0x2004,
+ 0xa08a, 0x199a, 0x0048, 0x6186, 0x2001, 0x1999, 0x8003, 0x801b,
+ 0x831b, 0xa318, 0x6316, 0x037f, 0x0f7e, 0x2c78, 0x71a0, 0xd1bc,
+ 0x0040, 0x619f, 0x7100, 0xd1f4, 0x0040, 0x619b, 0x7114, 0xa18c,
+ 0x00ff, 0x0078, 0x61a4, 0x2009, 0x0000, 0x0078, 0x61a4, 0xa1e0,
+ 0x293f, 0x2c0c, 0xa18c, 0x00ff, 0x2061, 0x0100, 0x619a, 0x1078,
+ 0x679b, 0x7300, 0xc3dd, 0x7302, 0x6807, 0x0002, 0x2f18, 0x6b26,
+ 0x682b, 0x0000, 0x781f, 0x0003, 0x7803, 0x0001, 0x7807, 0x0040,
+ 0x0f7f, 0x0e7f, 0x0c7f, 0x0d7f, 0x007c, 0x037f, 0x0e7f, 0x0c7f,
+ 0x0078, 0x61bb, 0x0d7f, 0x007c, 0x0c7e, 0x680c, 0xa065, 0x0040,
+ 0x61d0, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000, 0x1078, 0x620a,
+ 0x0c7f, 0x0d7f, 0x007c, 0x0f7e, 0x0d7e, 0x2069, 0xa5ab, 0x6830,
+ 0xa086, 0x0000, 0x00c0, 0x61f1, 0x6838, 0xa07d, 0x0040, 0x61f1,
+ 0x6833, 0x0001, 0x683e, 0x6847, 0x0000, 0x127e, 0x0f7e, 0x2091,
+ 0x2200, 0x027f, 0x1078, 0x1d28, 0x00c0, 0x61f4, 0x127f, 0x1078,
+ 0x6ae5, 0x0d7f, 0x0f7f, 0x007c, 0x127f, 0x6843, 0x0000, 0x7803,
+ 0x0002, 0x780c, 0xa015, 0x0040, 0x6206, 0x6a3a, 0x780f, 0x0000,
+ 0x6833, 0x0000, 0x683f, 0x0000, 0x0078, 0x61f1, 0x683a, 0x6836,
+ 0x0078, 0x6200, 0x601c, 0xa084, 0x000f, 0x1079, 0x6210, 0x007c,
+ 0x6219, 0x621e, 0x663f, 0x6758, 0x621e, 0x663f, 0x6758, 0x6219,
+ 0x621e, 0x1078, 0x6010, 0x1078, 0x6109, 0x007c, 0x157e, 0x137e,
+ 0x147e, 0x0c7e, 0x0f7e, 0x6004, 0xa08a, 0x0044, 0x10c8, 0x1328,
+ 0x6118, 0x2178, 0x79a0, 0xd1bc, 0x0040, 0x623b, 0x7900, 0xd1f4,
+ 0x0040, 0x6237, 0x7914, 0xa18c, 0x00ff, 0x0078, 0x6240, 0x2009,
+ 0x0000, 0x0078, 0x6240, 0xa1f8, 0x293f, 0x2f0c, 0xa18c, 0x00ff,
+ 0x2c78, 0x2061, 0x0100, 0x619a, 0xa08a, 0x0040, 0x00c8, 0x6292,
+ 0x1079, 0x6250, 0x0f7f, 0x0c7f, 0x147f, 0x137f, 0x157f, 0x007c,
+ 0x62f8, 0x6340, 0x6368, 0x6403, 0x6433, 0x643b, 0x6462, 0x6473,
+ 0x6484, 0x648c, 0x64a4, 0x648c, 0x650f, 0x6473, 0x6530, 0x6538,
+ 0x6484, 0x6538, 0x6549, 0x6290, 0x6290, 0x6290, 0x6290, 0x6290,
+ 0x6290, 0x6290, 0x6290, 0x6290, 0x6290, 0x6290, 0x6d05, 0x6d2a,
+ 0x6d3f, 0x6d62, 0x6d83, 0x6462, 0x6290, 0x6462, 0x648c, 0x6290,
+ 0x6368, 0x6403, 0x6290, 0x72ac, 0x648c, 0x6290, 0x72cc, 0x648c,
+ 0x6290, 0x6290, 0x62f3, 0x62a1, 0x6290, 0x72f1, 0x7368, 0x7450,
+ 0x6290, 0x7461, 0x645c, 0x747d, 0x6290, 0x6d98, 0x6290, 0x6290,
+ 0x1078, 0x1328, 0x2100, 0x1079, 0x629b, 0x0f7f, 0x0c7f, 0x147f,
+ 0x137f, 0x157f, 0x007c, 0x629f, 0x629f, 0x629f, 0x62d5, 0x1078,
+ 0x1328, 0x0d7e, 0x20a1, 0x020b, 0x1078, 0x6567, 0x7810, 0x2068,
+ 0x20a3, 0x2414, 0x20a3, 0x0018, 0x20a3, 0x0800, 0x683c, 0x20a2,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x6850, 0x20a2, 0x6854, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0018, 0x1078, 0x6c2d, 0x0d7f, 0x007c, 0x0d7e, 0x7818,
+ 0x2068, 0x68a0, 0xa082, 0x007e, 0x0048, 0x62d2, 0xa085, 0x0001,
+ 0x0d7f, 0x007c, 0xa006, 0x0078, 0x62d0, 0x0d7e, 0x20a1, 0x020b,
+ 0x1078, 0x6567, 0x20a3, 0x0500, 0x20a3, 0x0000, 0x7810, 0xa0e8,
+ 0x000f, 0x6808, 0x20a2, 0x680c, 0x20a2, 0x6810, 0x20a2, 0x6814,
+ 0x20a2, 0x6818, 0x20a2, 0x681c, 0x20a2, 0x60c3, 0x0010, 0x1078,
+ 0x6c2d, 0x0d7f, 0x007c, 0x6030, 0x609a, 0x1078, 0x6c2d, 0x007c,
+ 0x20a1, 0x020b, 0x1078, 0x6567, 0x20a3, 0x5200, 0x20a3, 0x0000,
+ 0x0d7e, 0x2069, 0xa351, 0x6804, 0xd084, 0x0040, 0x6312, 0x6828,
+ 0x20a3, 0x0000, 0x017e, 0x1078, 0x24fa, 0x21a2, 0x017f, 0x0d7f,
+ 0x0078, 0x6317, 0x0d7f, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a9,
+ 0x0004, 0x2099, 0xa305, 0x53a6, 0x20a9, 0x0004, 0x2099, 0xa301,
+ 0x53a6, 0x7818, 0xa080, 0x0028, 0x2004, 0xa082, 0x007f, 0x0048,
+ 0x6331, 0x2001, 0xa31a, 0x20a6, 0x2001, 0xa31b, 0x20a6, 0x0078,
+ 0x6337, 0x20a3, 0x0000, 0x6030, 0xa084, 0x00ff, 0x20a2, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c, 0x1078, 0x6c2d, 0x007c,
+ 0x20a1, 0x020b, 0x1078, 0x6567, 0x20a3, 0x0500, 0x20a3, 0x0000,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xa082, 0x007f, 0x0048, 0x6358,
+ 0x2001, 0xa31a, 0x20a6, 0x2001, 0xa31b, 0x20a6, 0x0078, 0x635e,
+ 0x20a3, 0x0000, 0x6030, 0xa084, 0x00ff, 0x20a2, 0x20a9, 0x0004,
+ 0x2099, 0xa305, 0x53a6, 0x60c3, 0x0010, 0x1078, 0x6c2d, 0x007c,
+ 0x20a1, 0x020b, 0x1078, 0x6567, 0x0c7e, 0x7818, 0x2060, 0x2001,
+ 0x0000, 0x1078, 0x48a2, 0x0c7f, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0xa086, 0x007e, 0x00c0, 0x6383, 0x20a3, 0x0400, 0x620c, 0xc2b4,
+ 0x620e, 0x0078, 0x6385, 0x20a3, 0x0300, 0x20a3, 0x0000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0xa086, 0x007e, 0x00c0, 0x63d2, 0x2099,
+ 0xa58c, 0x33a6, 0x9398, 0x33a6, 0x9398, 0x3304, 0xa084, 0x3fff,
+ 0x20a2, 0x9398, 0x33a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a9, 0x0004, 0x2099, 0xa305, 0x53a6,
+ 0x20a9, 0x0004, 0x2099, 0xa301, 0x53a6, 0x20a9, 0x0010, 0x20a3,
+ 0x0000, 0x00f0, 0x63af, 0x2099, 0xa594, 0x3304, 0xc0dd, 0x20a2,
+ 0x2001, 0xa371, 0x2004, 0xd0e4, 0x0040, 0x63ca, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x9398, 0x9398, 0x9398, 0x33a6, 0x20a9, 0x0004,
+ 0x0078, 0x63cc, 0x20a9, 0x0007, 0x20a3, 0x0000, 0x00f0, 0x63cc,
+ 0x0078, 0x63f2, 0x2099, 0xa58c, 0x20a9, 0x0008, 0x53a6, 0x20a9,
+ 0x0004, 0x2099, 0xa305, 0x53a6, 0x20a9, 0x0004, 0x2099, 0xa301,
+ 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x00f0, 0x63e3, 0x20a9,
+ 0x0008, 0x20a3, 0x0000, 0x00f0, 0x63e9, 0x2099, 0xa594, 0x20a9,
+ 0x0008, 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x00f0, 0x63f4,
+ 0x20a9, 0x000a, 0x20a3, 0x0000, 0x00f0, 0x63fa, 0x60c3, 0x0074,
+ 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x1078, 0x6567, 0x20a3,
+ 0x2010, 0x20a3, 0x0014, 0x20a3, 0x0800, 0x20a3, 0x2000, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x0f7e, 0x2079, 0xa351,
+ 0x7904, 0x0f7f, 0xd1ac, 0x00c0, 0x641f, 0xa085, 0x0020, 0xd1a4,
+ 0x0040, 0x6424, 0xa085, 0x0010, 0xa085, 0x0002, 0x0d7e, 0x0078,
+ 0x64ed, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x1078, 0x6567, 0x20a3,
+ 0x5000, 0x0078, 0x6385, 0x20a1, 0x020b, 0x1078, 0x6567, 0x20a3,
+ 0x2110, 0x20a3, 0x0014, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0014, 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x1078, 0x65ef,
+ 0x0078, 0x6466, 0x20a1, 0x020b, 0x1078, 0x65f8, 0x20a3, 0x0200,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0004,
+ 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x1078, 0x65f8, 0x20a3,
+ 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0003, 0x20a3, 0x2a00, 0x60c3,
+ 0x0008, 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x1078, 0x65f8,
+ 0x20a3, 0x0200, 0x0078, 0x6385, 0x20a1, 0x020b, 0x1078, 0x65f8,
+ 0x20a3, 0x0100, 0x20a3, 0x0000, 0x7828, 0xa005, 0x0040, 0x649b,
+ 0x20a2, 0x0078, 0x649d, 0x20a3, 0x0003, 0x7810, 0x20a2, 0x60c3,
+ 0x0008, 0x1078, 0x6c2d, 0x007c, 0x0d7e, 0x20a1, 0x020b, 0x1078,
+ 0x65f8, 0x20a3, 0x0210, 0x20a3, 0x0014, 0x20a3, 0x0800, 0x7818,
+ 0x2068, 0x6894, 0xa086, 0x0014, 0x00c0, 0x64ca, 0x6998, 0xa184,
+ 0xc000, 0x00c0, 0x64c6, 0xd1ec, 0x0040, 0x64c2, 0x20a3, 0x2100,
+ 0x0078, 0x64cc, 0x20a3, 0x0100, 0x0078, 0x64cc, 0x20a3, 0x0400,
+ 0x0078, 0x64cc, 0x20a3, 0x0700, 0xa006, 0x20a2, 0x20a2, 0x20a2,
+ 0x20a2, 0x20a2, 0x0f7e, 0x2079, 0xa351, 0x7904, 0x0f7f, 0xd1ac,
+ 0x00c0, 0x64dc, 0xa085, 0x0020, 0xd1a4, 0x0040, 0x64e1, 0xa085,
+ 0x0010, 0x2009, 0xa373, 0x210c, 0xd184, 0x0040, 0x64eb, 0x699c,
+ 0xd18c, 0x0040, 0x64ed, 0xa085, 0x0002, 0x027e, 0x2009, 0xa371,
+ 0x210c, 0xd1e4, 0x0040, 0x64fb, 0xc0c5, 0xa094, 0x0030, 0xa296,
+ 0x0010, 0x0040, 0x6505, 0xd1ec, 0x0040, 0x6505, 0xa094, 0x0030,
+ 0xa296, 0x0010, 0x0040, 0x6505, 0xc0bd, 0x027f, 0x20a2, 0x20a2,
+ 0x20a2, 0x60c3, 0x0014, 0x1078, 0x6c2d, 0x0d7f, 0x007c, 0x20a1,
+ 0x020b, 0x1078, 0x65f8, 0x20a3, 0x0210, 0x20a3, 0x0014, 0x20a3,
+ 0x0000, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x1078, 0x6c2d, 0x007c,
+ 0x20a1, 0x020b, 0x1078, 0x65f8, 0x20a3, 0x0200, 0x0078, 0x62fe,
+ 0x20a1, 0x020b, 0x1078, 0x65f8, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x0003, 0x20a3, 0x2a00, 0x60c3, 0x0008, 0x1078, 0x6c2d,
+ 0x007c, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x20a1, 0x020b, 0x1078,
+ 0x65f8, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0x000b, 0x20a3,
+ 0x0000, 0x60c3, 0x0008, 0x1078, 0x6c2d, 0x007c, 0x027e, 0x037e,
+ 0x047e, 0x2019, 0x3200, 0x2021, 0x0800, 0x0078, 0x656e, 0x027e,
+ 0x037e, 0x047e, 0x2019, 0x2200, 0x2021, 0x0100, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2014, 0xa286, 0x007e,
+ 0x00c0, 0x6581, 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffe, 0x0078,
+ 0x65b6, 0xa286, 0x007f, 0x00c0, 0x658d, 0x0d7e, 0xa385, 0x00ff,
+ 0x20a2, 0x20a3, 0xfffd, 0x0078, 0x65a4, 0xd2bc, 0x0040, 0x65ac,
+ 0xa286, 0x0080, 0x0d7e, 0x00c0, 0x659c, 0xa385, 0x00ff, 0x20a2,
+ 0x20a3, 0xfffc, 0x0078, 0x65a4, 0xa2e8, 0xa434, 0x2d6c, 0x6810,
+ 0xa305, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a, 0x2da6, 0x8d68,
+ 0x2da6, 0x0d7f, 0x0078, 0x65ba, 0x0d7e, 0xa2e8, 0xa434, 0x2d6c,
+ 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000,
+ 0x6230, 0x22a2, 0xa485, 0x0029, 0x20a2, 0x047f, 0x037f, 0x20a3,
+ 0x0000, 0x1078, 0x6c1c, 0x22a2, 0x20a3, 0x0000, 0x2fa2, 0x20a3,
+ 0xffff, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f, 0x007c, 0x027e,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x20a3, 0x02ff, 0x2011, 0xfffc,
+ 0x22a2, 0x0d7e, 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6, 0x0d7f,
+ 0x20a3, 0x2029, 0x20a3, 0x0000, 0x0078, 0x65c1, 0x20a3, 0x0100,
+ 0x20a3, 0x0000, 0x20a3, 0xfc02, 0x20a3, 0x0000, 0x007c, 0x027e,
+ 0x037e, 0x047e, 0x2019, 0x3300, 0x2021, 0x0800, 0x0078, 0x65ff,
+ 0x027e, 0x037e, 0x047e, 0x2019, 0x2300, 0x2021, 0x0100, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa092,
+ 0x007e, 0x0048, 0x661c, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810,
+ 0xa305, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a, 0x2da6, 0x8d68,
+ 0x2da6, 0x0d7f, 0x0078, 0x662a, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c,
+ 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000,
+ 0x6230, 0x22a2, 0xa485, 0x0098, 0x20a2, 0x20a3, 0x0000, 0x047f,
+ 0x037f, 0x1078, 0x6c1c, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2,
+ 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f, 0x007c, 0x0c7e,
+ 0x0f7e, 0x6004, 0xa08a, 0x0085, 0x1048, 0x1328, 0xa08a, 0x008c,
+ 0x10c8, 0x1328, 0x6118, 0x2178, 0x79a0, 0xd1bc, 0x0040, 0x665d,
+ 0x7900, 0xd1f4, 0x0040, 0x6659, 0x7914, 0xa18c, 0x00ff, 0x0078,
+ 0x6662, 0x2009, 0x0000, 0x0078, 0x6662, 0xa1f8, 0x293f, 0x2f0c,
+ 0xa18c, 0x00ff, 0x2c78, 0x2061, 0x0100, 0x619a, 0xa082, 0x0085,
+ 0x1079, 0x666d, 0x0f7f, 0x0c7f, 0x007c, 0x6676, 0x6681, 0x669c,
+ 0x6674, 0x6674, 0x6674, 0x6676, 0x1078, 0x1328, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x66af, 0x60c3, 0x0000, 0x1078, 0x6c2d, 0x147f,
+ 0x007c, 0x147e, 0x20a1, 0x020b, 0x1078, 0x66e3, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x7808, 0x20a2, 0x7810, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x000c,
+ 0x1078, 0x6c2d, 0x147f, 0x007c, 0x147e, 0x20a1, 0x020b, 0x1078,
+ 0x6724, 0x20a3, 0x0003, 0x20a3, 0x0300, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x0004, 0x1078, 0x6c2d, 0x147f, 0x007c, 0x027e,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0xa092, 0x007e, 0x0048, 0x66ce, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c,
+ 0x6810, 0xa085, 0x8100, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x66dd, 0x0d7e, 0xa0e8,
+ 0xa434, 0x2d6c, 0x6810, 0xa085, 0x8100, 0x20a2, 0x6814, 0x20a2,
+ 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x20a3, 0x0009, 0x20a3,
+ 0x0000, 0x0078, 0x65c1, 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xa092, 0x007e, 0x0048, 0x6702,
+ 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085, 0x8400, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6, 0x0d7f,
+ 0x0078, 0x6711, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085,
+ 0x8400, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230,
+ 0x22a2, 0x20a3, 0x0099, 0x20a3, 0x0000, 0x1078, 0x6c1c, 0x22a2,
+ 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x7a10, 0x22a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x027f, 0x007c, 0x027e, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa092, 0x007e, 0x0048,
+ 0x6743, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085, 0x8500,
+ 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6,
+ 0x0d7f, 0x0078, 0x6752, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810,
+ 0xa085, 0x8500, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000,
+ 0x6230, 0x22a2, 0x20a3, 0x0099, 0x20a3, 0x0000, 0x0078, 0x6715,
+ 0x0c7e, 0x0f7e, 0x2c78, 0x7804, 0xa08a, 0x0040, 0x1048, 0x1328,
+ 0xa08a, 0x0053, 0x10c8, 0x1328, 0x7918, 0x2160, 0x61a0, 0xd1bc,
+ 0x0040, 0x6777, 0x6100, 0xd1f4, 0x0040, 0x6773, 0x6114, 0xa18c,
+ 0x00ff, 0x0078, 0x677c, 0x2009, 0x0000, 0x0078, 0x677c, 0xa1e0,
+ 0x293f, 0x2c0c, 0xa18c, 0x00ff, 0x2061, 0x0100, 0x619a, 0xa082,
+ 0x0040, 0x1079, 0x6786, 0x0f7f, 0x0c7f, 0x007c, 0x679b, 0x68a9,
+ 0x684a, 0x6a59, 0x6799, 0x6799, 0x6799, 0x6799, 0x6799, 0x6799,
+ 0x6799, 0x6f5e, 0x6f6f, 0x6f80, 0x6f91, 0x6799, 0x748e, 0x6799,
+ 0x6f4d, 0x1078, 0x1328, 0x0d7e, 0x157e, 0x147e, 0x780b, 0xffff,
+ 0x20a1, 0x020b, 0x1078, 0x6806, 0x7910, 0x2168, 0x6948, 0x7922,
+ 0x21a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x694c, 0xa184, 0x000f,
+ 0x00c0, 0x67b6, 0x2001, 0x0005, 0x0078, 0x67c0, 0xd184, 0x0040,
+ 0x67bd, 0x2001, 0x0004, 0x0078, 0x67c0, 0xa084, 0x0006, 0x8004,
+ 0x017e, 0x2008, 0x7830, 0xa084, 0x00ff, 0x8007, 0xa105, 0x017f,
+ 0x20a2, 0xd1ac, 0x0040, 0x67d0, 0x20a3, 0x0002, 0x0078, 0x67dc,
+ 0xd1b4, 0x0040, 0x67d7, 0x20a3, 0x0001, 0x0078, 0x67dc, 0x20a3,
+ 0x0000, 0x2230, 0x0078, 0x67de, 0x6a80, 0x6e7c, 0x20a9, 0x0008,
+ 0xad80, 0x0017, 0x200c, 0x810f, 0x21a2, 0x8000, 0x00f0, 0x67e2,
+ 0x22a2, 0x26a2, 0x60c3, 0x0020, 0x20e1, 0x9080, 0x6014, 0xa084,
+ 0x0004, 0xa085, 0x0009, 0x6016, 0x2001, 0xa5c7, 0x2003, 0x07d0,
+ 0x2001, 0xa5c6, 0x2003, 0x0009, 0x2001, 0xa5cc, 0x2003, 0x0002,
+ 0x1078, 0x157e, 0x147f, 0x157f, 0x0d7f, 0x007c, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x7a18, 0xa280, 0x0023, 0x2014, 0x8210, 0xa294,
+ 0x00ff, 0x2202, 0x8217, 0x7818, 0xa080, 0x0028, 0x2004, 0xd0bc,
+ 0x0040, 0x682c, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085,
+ 0x0600, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a, 0x2da6, 0x8d68,
+ 0x2da6, 0x0d7f, 0x0078, 0x683b, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c,
+ 0x6810, 0xa085, 0x0600, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3,
+ 0x0000, 0x6130, 0x21a2, 0x20a3, 0x0829, 0x20a3, 0x0000, 0x22a2,
+ 0x20a3, 0x0000, 0x2fa2, 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x007c, 0x0d7e, 0x157e, 0x137e, 0x147e, 0x20a1, 0x020b,
+ 0x1078, 0x686a, 0x7810, 0x2068, 0x6860, 0x20a2, 0x685c, 0x20a2,
+ 0x6880, 0x20a2, 0x687c, 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2,
+ 0x20a2, 0x60c3, 0x000c, 0x1078, 0x6c2d, 0x147f, 0x137f, 0x157f,
+ 0x0d7f, 0x007c, 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0xd0bc, 0x0040, 0x6888, 0x0d7e, 0xa0e8,
+ 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0500, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6897,
+ 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0500, 0x20a2,
+ 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x20a3,
+ 0x0889, 0x20a3, 0x0000, 0x1078, 0x6c1c, 0x22a2, 0x20a3, 0x0000,
+ 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f,
+ 0x007c, 0x0d7e, 0x157e, 0x137e, 0x147e, 0x7810, 0xa06d, 0x1078,
+ 0x488f, 0x0040, 0x68bd, 0x684c, 0xa084, 0x2020, 0xa086, 0x2020,
+ 0x00c0, 0x68bd, 0x7824, 0xc0cd, 0x7826, 0x20a1, 0x020b, 0x1078,
+ 0x6a12, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x7810,
+ 0xa084, 0xf000, 0x00c0, 0x68d4, 0x7810, 0xa084, 0x0700, 0x8007,
+ 0x1079, 0x68dc, 0x0078, 0x68d7, 0xa006, 0x1079, 0x68dc, 0x147f,
+ 0x137f, 0x157f, 0x0d7f, 0x007c, 0x68e6, 0x697e, 0x6989, 0x69b3,
+ 0x69c7, 0x69e3, 0x69ee, 0x68e4, 0x1078, 0x1328, 0x017e, 0x037e,
+ 0x694c, 0xa18c, 0x0003, 0x0040, 0x68f1, 0xa186, 0x0003, 0x00c0,
+ 0x6900, 0x6b78, 0x7824, 0xd0cc, 0x0040, 0x68f7, 0xc3e5, 0x23a2,
+ 0x6868, 0x20a2, 0x6864, 0x20a2, 0x037f, 0x017f, 0x0078, 0x69be,
+ 0xa186, 0x0001, 0x10c0, 0x1328, 0x6b78, 0x7824, 0xd0cc, 0x0040,
+ 0x690a, 0xc3e5, 0x23a2, 0x6868, 0x20a2, 0x6864, 0x20a2, 0x22a2,
+ 0x6874, 0x20a2, 0x22a2, 0x687c, 0x20a2, 0x2009, 0x0018, 0xa384,
+ 0x0300, 0x0040, 0x6978, 0xd3c4, 0x0040, 0x6920, 0x687c, 0xa108,
+ 0xd3cc, 0x0040, 0x6925, 0x6874, 0xa108, 0x157e, 0x20a9, 0x000d,
+ 0xad80, 0x0020, 0x201c, 0x831f, 0x23a2, 0x8000, 0x00f0, 0x692a,
+ 0x157f, 0x22a2, 0x22a2, 0x22a2, 0xa184, 0x0003, 0x0040, 0x6978,
+ 0x20a1, 0x020b, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x007e, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0xd0bc, 0x0040, 0x6958, 0x0d7e, 0xa0e8,
+ 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6967,
+ 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2,
+ 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x007f,
+ 0x7b24, 0xd3cc, 0x0040, 0x6970, 0x20a3, 0x0889, 0x0078, 0x6972,
+ 0x20a3, 0x0898, 0x20a2, 0x1078, 0x6c1c, 0x22a2, 0x20a3, 0x0000,
+ 0x61c2, 0x037f, 0x017f, 0x1078, 0x6c2d, 0x007c, 0x2011, 0x0008,
+ 0x7824, 0xd0cc, 0x0040, 0x6985, 0xc2e5, 0x22a2, 0xa016, 0x0078,
+ 0x69bc, 0x2011, 0x0302, 0x7824, 0xd0cc, 0x0040, 0x6990, 0xc2e5,
+ 0x22a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x0012, 0x22a2,
+ 0x20a3, 0x0008, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x7000,
+ 0x20a3, 0x0500, 0x22a2, 0x20a3, 0x000a, 0x22a2, 0x22a2, 0x20a3,
+ 0x2500, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0032,
+ 0x1078, 0x6c2d, 0x007c, 0x2011, 0x0028, 0x7824, 0xd0cc, 0x0040,
+ 0x69ba, 0xc2e5, 0x22a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2,
+ 0x22a2, 0x22a2, 0x60c3, 0x0018, 0x1078, 0x6c2d, 0x007c, 0x2011,
+ 0x0100, 0x7824, 0xd0cc, 0x0040, 0x69ce, 0xc2e5, 0x22a2, 0xa016,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x0008, 0x22a2,
+ 0x7834, 0xa084, 0x00ff, 0x20a2, 0x22a2, 0x22a2, 0x60c3, 0x0020,
+ 0x1078, 0x6c2d, 0x007c, 0x2011, 0x0008, 0x7824, 0xd0cc, 0x0040,
+ 0x69ea, 0xc2e5, 0x22a2, 0xa016, 0x0078, 0x69bc, 0x037e, 0x7b10,
+ 0xa384, 0xff00, 0x7812, 0xa384, 0x00ff, 0x8001, 0x00c0, 0x6a01,
+ 0x7824, 0xd0cc, 0x0040, 0x69fd, 0xc2e5, 0x22a2, 0x037f, 0x0078,
+ 0x69bc, 0x047e, 0x2021, 0x0800, 0x007e, 0x7824, 0xd0cc, 0x007f,
+ 0x0040, 0x6a0b, 0xc4e5, 0x24a2, 0x047f, 0x22a2, 0x20a2, 0x037f,
+ 0x0078, 0x69be, 0x027e, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0xd0bc, 0x0040, 0x6a30, 0x0d7e, 0xa0e8,
+ 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2, 0x6814, 0x20a2,
+ 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x6a3f,
+ 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2,
+ 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x7824,
+ 0xd0cc, 0x0040, 0x6a47, 0x20a3, 0x0889, 0x0078, 0x6a49, 0x20a3,
+ 0x0898, 0x20a3, 0x0000, 0x1078, 0x6c1c, 0x22a2, 0x20a3, 0x0000,
+ 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x027f,
+ 0x007c, 0x0d7e, 0x157e, 0x137e, 0x147e, 0x017e, 0x037e, 0x7810,
+ 0xa084, 0x0700, 0x8007, 0x1079, 0x6a6c, 0x037f, 0x017f, 0x147f,
+ 0x137f, 0x157f, 0x0d7f, 0x007c, 0x6a74, 0x6a74, 0x6a76, 0x6a74,
+ 0x6a74, 0x6a74, 0x6a9b, 0x6a74, 0x1078, 0x1328, 0x7910, 0xa18c,
+ 0xf8ff, 0xa18d, 0x0600, 0x7912, 0x20a1, 0x020b, 0x2009, 0x0003,
+ 0x1078, 0x6aa5, 0x0d7e, 0x2069, 0xa351, 0x6804, 0xd0bc, 0x0040,
+ 0x6a90, 0x682c, 0xa084, 0x00ff, 0x8007, 0x20a2, 0x0078, 0x6a92,
+ 0x20a3, 0x3f00, 0x0d7f, 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0001,
+ 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x2009, 0x0003, 0x1078,
+ 0x6aa5, 0x20a3, 0x7f00, 0x0078, 0x6a93, 0x027e, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004, 0xd0bc, 0x0040,
+ 0x6ac3, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810, 0xa085, 0x0100,
+ 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a, 0x2da6, 0x8d68, 0x2da6,
+ 0x0d7f, 0x0078, 0x6ad2, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c, 0x6810,
+ 0xa085, 0x0100, 0x20a2, 0x6814, 0x20a2, 0x0d7f, 0x20a3, 0x0000,
+ 0x6230, 0x22a2, 0x20a3, 0x0888, 0xa18d, 0x0008, 0x21a2, 0x1078,
+ 0x6c1c, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x027f, 0x007c, 0x0e7e, 0x0d7e, 0x0c7e,
+ 0x057e, 0x047e, 0x037e, 0x2061, 0x0100, 0x2071, 0xa300, 0x6130,
+ 0x7818, 0x2068, 0x68a0, 0x2028, 0xd0bc, 0x00c0, 0x6afc, 0x6910,
+ 0x6a14, 0x6430, 0x0078, 0x6b00, 0x6910, 0x6a14, 0x7368, 0x746c,
+ 0x781c, 0xa086, 0x0006, 0x0040, 0x6b5f, 0xd5bc, 0x0040, 0x6b10,
+ 0xa185, 0x0100, 0x6062, 0x6266, 0x636a, 0x646e, 0x0078, 0x6b17,
+ 0xa185, 0x0100, 0x6062, 0x6266, 0x606b, 0x0000, 0x646e, 0x6073,
+ 0x0809, 0x6077, 0x0008, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e,
+ 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6082, 0x7808, 0x6086,
+ 0x7810, 0x2070, 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6,
+ 0x7008, 0x60ca, 0x686c, 0x60ce, 0x60ab, 0x0036, 0x60af, 0x95d5,
+ 0x60d7, 0x0000, 0xa582, 0x0080, 0x0048, 0x6b49, 0x6a00, 0xd2f4,
+ 0x0040, 0x6b47, 0x6a14, 0xa294, 0x00ff, 0x0078, 0x6b49, 0x2011,
+ 0x0000, 0x629e, 0x6017, 0x0016, 0x2009, 0x07d0, 0x60c4, 0xa084,
+ 0xfff0, 0xa005, 0x0040, 0x6b56, 0x2009, 0x1b58, 0x1078, 0x595f,
+ 0x037f, 0x047f, 0x057f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0x7810,
+ 0x2070, 0x704c, 0xa084, 0x0003, 0xa086, 0x0002, 0x0040, 0x6bb7,
+ 0xd5bc, 0x0040, 0x6b73, 0xa185, 0x0100, 0x6062, 0x6266, 0x636a,
+ 0x646e, 0x0078, 0x6b7a, 0xa185, 0x0100, 0x6062, 0x6266, 0x606b,
+ 0x0000, 0x646e, 0x6073, 0x0880, 0x6077, 0x0008, 0x688c, 0x8000,
+ 0xa084, 0x00ff, 0x688e, 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00,
+ 0x6086, 0x7808, 0x6082, 0x7060, 0x608a, 0x705c, 0x608e, 0x7080,
+ 0x60c6, 0x707c, 0x60ca, 0x707c, 0x792c, 0xa108, 0x792e, 0x7080,
+ 0x7928, 0xa109, 0x792a, 0x686c, 0x60ce, 0x60ab, 0x0036, 0x60af,
+ 0x95d5, 0x60d7, 0x0000, 0xa582, 0x0080, 0x0048, 0x6bb2, 0x6a00,
+ 0xd2f4, 0x0040, 0x6bb0, 0x6a14, 0xa294, 0x00ff, 0x0078, 0x6bb2,
+ 0x2011, 0x0000, 0x629e, 0x6017, 0x0012, 0x0078, 0x6b4c, 0xd5bc,
+ 0x0040, 0x6bc2, 0xa185, 0x0700, 0x6062, 0x6266, 0x636a, 0x646e,
+ 0x0078, 0x6bc9, 0xa185, 0x0700, 0x6062, 0x6266, 0x606b, 0x0000,
+ 0x646e, 0x1078, 0x488f, 0x0040, 0x6bdf, 0x0d7e, 0x7810, 0xa06d,
+ 0x684c, 0x0d7f, 0xa084, 0x2020, 0xa086, 0x2020, 0x00c0, 0x6bdf,
+ 0x7824, 0xc0cd, 0x7826, 0x6073, 0x0889, 0x0078, 0x6be1, 0x6073,
+ 0x0898, 0x6077, 0x0000, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e,
+ 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6086, 0x7808, 0x6082,
+ 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6, 0x7008, 0x60ca,
+ 0x686c, 0x60ce, 0x60ab, 0x0036, 0x60af, 0x95d5, 0x60d7, 0x0000,
+ 0xa582, 0x0080, 0x0048, 0x6c0f, 0x6a00, 0xd2f4, 0x0040, 0x6c0d,
+ 0x6a14, 0xa294, 0x00ff, 0x0078, 0x6c0f, 0x2011, 0x0000, 0x629e,
+ 0x7824, 0xd0cc, 0x0040, 0x6c18, 0x6017, 0x0016, 0x0078, 0x6b4c,
+ 0x6017, 0x0012, 0x0078, 0x6b4c, 0x7a18, 0xa280, 0x0023, 0x2014,
+ 0x8210, 0xa294, 0x00ff, 0x2202, 0x8217, 0x007c, 0x0d7e, 0x2069,
+ 0xa5ab, 0x6843, 0x0001, 0x0d7f, 0x007c, 0x20e1, 0x9080, 0x60a3,
+ 0x0056, 0x60a7, 0x9575, 0x1078, 0x6c38, 0x1078, 0x594f, 0x007c,
+ 0x007e, 0x6014, 0xa084, 0x0004, 0xa085, 0x0009, 0x6016, 0x007f,
+ 0x007c, 0x007e, 0x0c7e, 0x2061, 0x0100, 0x6014, 0xa084, 0x0004,
+ 0xa085, 0x0008, 0x6016, 0x0c7f, 0x007f, 0x007c, 0x0c7e, 0x0d7e,
+ 0x017e, 0x027e, 0x2061, 0x0100, 0x2069, 0x0140, 0x6904, 0xa194,
+ 0x4000, 0x0040, 0x6c89, 0x1078, 0x6c41, 0x6803, 0x1000, 0x6803,
+ 0x0000, 0x0c7e, 0x2061, 0xa5ab, 0x6128, 0xa192, 0x00c8, 0x00c8,
+ 0x6c76, 0x8108, 0x612a, 0x6124, 0x0c7f, 0x81ff, 0x0040, 0x6c84,
+ 0x1078, 0x594f, 0x1078, 0x6c38, 0x0078, 0x6c84, 0x6124, 0xa1e5,
+ 0x0000, 0x0040, 0x6c81, 0x1078, 0xa241, 0x2009, 0x0014, 0x1078,
+ 0x756c, 0x0c7f, 0x0078, 0x6c84, 0x027f, 0x017f, 0x0d7f, 0x0c7f,
+ 0x007c, 0x2001, 0xa5c7, 0x2004, 0xa005, 0x00c0, 0x6c84, 0x0c7e,
+ 0x2061, 0xa5ab, 0x6128, 0xa192, 0x0003, 0x00c8, 0x6c76, 0x8108,
+ 0x612a, 0x0c7f, 0x1078, 0x594f, 0x1078, 0x4171, 0x0078, 0x6c84,
+ 0x0c7e, 0x0d7e, 0x0e7e, 0x017e, 0x027e, 0x1078, 0x5967, 0x2071,
+ 0xa5ab, 0x713c, 0x81ff, 0x0040, 0x6cca, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x6904, 0xa194, 0x4000, 0x0040, 0x6cd0, 0x6803, 0x1000,
+ 0x6803, 0x0000, 0x037e, 0x2019, 0x0001, 0x1078, 0x6e6c, 0x037f,
+ 0x713c, 0x2160, 0x1078, 0xa241, 0x2009, 0x004a, 0x1078, 0x756c,
+ 0x0078, 0x6cca, 0x027f, 0x017f, 0x0e7f, 0x0d7f, 0x0c7f, 0x007c,
+ 0x0078, 0x6cba, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e, 0x057e, 0x047e,
+ 0x007e, 0x127e, 0x2091, 0x8000, 0x6018, 0x2068, 0x6ca0, 0x2071,
+ 0xa5ab, 0x7018, 0x2068, 0x8dff, 0x0040, 0x6cfc, 0x68a0, 0xa406,
+ 0x0040, 0x6cee, 0x6854, 0x2068, 0x0078, 0x6ce3, 0x6010, 0x2060,
+ 0x643c, 0x6540, 0x6e48, 0x2d60, 0x1078, 0x466a, 0x0040, 0x6cfc,
+ 0x1078, 0x7045, 0xa085, 0x0001, 0x127f, 0x007f, 0x047f, 0x057f,
+ 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0x20a1, 0x020b, 0x1078,
+ 0x6567, 0x20a3, 0x1200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x781c,
+ 0xa086, 0x0004, 0x00c0, 0x6d17, 0x6098, 0x0078, 0x6d18, 0x6030,
+ 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a9, 0x0010, 0xa006,
+ 0x20a2, 0x00f0, 0x6d20, 0x20a2, 0x20a2, 0x60c3, 0x002c, 0x1078,
+ 0x6c2d, 0x007c, 0x157e, 0x147e, 0x20a1, 0x020b, 0x1078, 0x6567,
+ 0x20a3, 0x0f00, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808, 0x20a2,
+ 0x60c3, 0x0008, 0x1078, 0x6c2d, 0x147f, 0x157f, 0x007c, 0x157e,
+ 0x147e, 0x20a1, 0x020b, 0x1078, 0x65f8, 0x20a3, 0x0200, 0x20a3,
+ 0x0000, 0x20a9, 0x0006, 0x2011, 0xa340, 0x2019, 0xa341, 0x23a6,
+ 0x22a6, 0xa398, 0x0002, 0xa290, 0x0002, 0x00f0, 0x6d4f, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c, 0x1078, 0x6c2d, 0x147f,
+ 0x157f, 0x007c, 0x157e, 0x147e, 0x017e, 0x027e, 0x20a1, 0x020b,
+ 0x1078, 0x65cf, 0x1078, 0x65e6, 0x7810, 0xa080, 0x0000, 0x2004,
+ 0xa080, 0x0015, 0x2098, 0x7808, 0xa088, 0x0002, 0x21a8, 0x53a6,
+ 0xa080, 0x0004, 0x8003, 0x60c2, 0x1078, 0x6c2d, 0x027f, 0x017f,
+ 0x147f, 0x157f, 0x007c, 0x157e, 0x147e, 0x20a1, 0x020b, 0x1078,
+ 0x6567, 0x20a3, 0x6200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808,
+ 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6c2d, 0x147f, 0x157f, 0x007c,
+ 0x157e, 0x147e, 0x017e, 0x027e, 0x20a1, 0x020b, 0x1078, 0x6567,
+ 0x7810, 0xa080, 0x0000, 0x2004, 0xa080, 0x0017, 0x2098, 0x7808,
+ 0xa088, 0x0002, 0x21a8, 0x53a6, 0x8003, 0x60c2, 0x1078, 0x6c2d,
+ 0x027f, 0x017f, 0x147f, 0x157f, 0x007c, 0x0e7e, 0x0c7e, 0x007e,
+ 0x127e, 0x2091, 0x8000, 0x2071, 0xa5ab, 0x700c, 0x2060, 0x8cff,
+ 0x0040, 0x6dd1, 0x1078, 0x8c3b, 0x00c0, 0x6dc8, 0x1078, 0x7a05,
+ 0x600c, 0x007e, 0x1078, 0x753d, 0x1078, 0x7045, 0x0c7f, 0x0078,
+ 0x6dbf, 0x700f, 0x0000, 0x700b, 0x0000, 0x127f, 0x007f, 0x0c7f,
+ 0x0e7f, 0x007c, 0x127e, 0x157e, 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e,
+ 0x027e, 0x017e, 0x007e, 0x2091, 0x8000, 0x2069, 0x0100, 0x2079,
+ 0x0140, 0x2071, 0xa5ab, 0x7024, 0x2060, 0x8cff, 0x0040, 0x6e2a,
+ 0x1078, 0x6c41, 0x68c3, 0x0000, 0x1078, 0x595a, 0x2009, 0x0013,
+ 0x1078, 0x756c, 0x20a9, 0x01f4, 0x6824, 0xd094, 0x0040, 0x6e0d,
+ 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x0040, 0x6e1f, 0x7803,
+ 0x1000, 0x7803, 0x0000, 0x0078, 0x6e1f, 0xd084, 0x0040, 0x6e14,
+ 0x6827, 0x0001, 0x0078, 0x6e16, 0x00f0, 0x6dfc, 0x7804, 0xa084,
+ 0x1000, 0x0040, 0x6e1f, 0x7803, 0x0100, 0x7803, 0x0000, 0x6824,
+ 0x007f, 0x017f, 0x027f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x157f,
+ 0x127f, 0x007c, 0x2001, 0xa300, 0x2004, 0xa096, 0x0001, 0x0040,
+ 0x6e62, 0xa096, 0x0004, 0x0040, 0x6e62, 0x6817, 0x0008, 0x68c3,
+ 0x0000, 0x2011, 0x4129, 0x1078, 0x58d4, 0x20a9, 0x01f4, 0x6824,
+ 0xd094, 0x0040, 0x6e50, 0x6827, 0x0004, 0x7804, 0xa084, 0x4000,
+ 0x0040, 0x6e62, 0x7803, 0x1000, 0x7803, 0x0000, 0x0078, 0x6e62,
+ 0xd084, 0x0040, 0x6e57, 0x6827, 0x0001, 0x0078, 0x6e59, 0x00f0,
+ 0x6e3f, 0x7804, 0xa084, 0x1000, 0x0040, 0x6e62, 0x7803, 0x0100,
+ 0x7803, 0x0000, 0x007f, 0x017f, 0x027f, 0x0c7f, 0x0d7f, 0x0e7f,
+ 0x0f7f, 0x157f, 0x127f, 0x007c, 0x127e, 0x157e, 0x0f7e, 0x0e7e,
+ 0x0d7e, 0x0c7e, 0x027e, 0x017e, 0x007e, 0x2091, 0x8000, 0x2069,
+ 0x0100, 0x2079, 0x0140, 0x2071, 0xa5ab, 0x703c, 0x2060, 0x8cff,
+ 0x0040, 0x6ee8, 0x6817, 0x0010, 0x2009, 0x00fa, 0x8109, 0x00c0,
+ 0x6e86, 0x68c7, 0x0000, 0x68cb, 0x0008, 0x1078, 0x5967, 0x1078,
+ 0x1f31, 0x047e, 0x057e, 0x2009, 0x017f, 0x212c, 0x200b, 0x00a5,
+ 0x2021, 0x0169, 0x2404, 0xa084, 0x000f, 0xa086, 0x0004, 0x00c0,
+ 0x6eb7, 0x68c7, 0x0000, 0x68cb, 0x0008, 0x0e7e, 0x0f7e, 0x2079,
+ 0x0020, 0x2071, 0xa602, 0x6814, 0xa084, 0x0004, 0xa085, 0x0012,
+ 0x6816, 0x7803, 0x0008, 0x7003, 0x0000, 0x0f7f, 0x0e7f, 0x250a,
+ 0x057f, 0x047f, 0xa39d, 0x0000, 0x00c0, 0x6ec2, 0x2009, 0x0049,
+ 0x1078, 0x756c, 0x20a9, 0x03e8, 0x6824, 0xd094, 0x0040, 0x6ed5,
+ 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x0040, 0x6ee7, 0x7803,
+ 0x1000, 0x7803, 0x0000, 0x0078, 0x6ee7, 0xd08c, 0x0040, 0x6edc,
+ 0x6827, 0x0002, 0x0078, 0x6ede, 0x00f0, 0x6ec4, 0x7804, 0xa084,
+ 0x1000, 0x0040, 0x6ee7, 0x7803, 0x0100, 0x7803, 0x0000, 0x6824,
+ 0x007f, 0x017f, 0x027f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x157f,
+ 0x127f, 0x007c, 0x0d7e, 0x127e, 0x2091, 0x8000, 0x2069, 0xa5ab,
+ 0x6a06, 0x127f, 0x0d7f, 0x007c, 0x0d7e, 0x127e, 0x2091, 0x8000,
+ 0x2069, 0xa5ab, 0x6a32, 0x127f, 0x0d7f, 0x007c, 0x0f7e, 0x0e7e,
+ 0x0c7e, 0x067e, 0x007e, 0x127e, 0x2071, 0xa5ab, 0x7614, 0x2660,
+ 0x2678, 0x2091, 0x8000, 0x8cff, 0x0040, 0x6f46, 0x601c, 0xa206,
+ 0x00c0, 0x6f41, 0x7014, 0xac36, 0x00c0, 0x6f20, 0x660c, 0x7616,
+ 0x7010, 0xac36, 0x00c0, 0x6f2e, 0x2c00, 0xaf36, 0x0040, 0x6f2c,
+ 0x2f00, 0x7012, 0x0078, 0x6f2e, 0x7013, 0x0000, 0x660c, 0x067e,
+ 0x2c00, 0xaf06, 0x0040, 0x6f37, 0x7e0e, 0x0078, 0x6f38, 0x2678,
+ 0x600f, 0x0000, 0x1078, 0x8c01, 0x1078, 0x7045, 0x0c7f, 0x0078,
+ 0x6f13, 0x2c78, 0x600c, 0x2060, 0x0078, 0x6f13, 0x127f, 0x007f,
+ 0x067f, 0x0c7f, 0x0e7f, 0x0f7f, 0x007c, 0x157e, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x6806, 0x7810, 0x20a2, 0xa006, 0x20a2, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a3, 0x1000, 0x0078, 0x6fa0, 0x157e, 0x147e,
+ 0x20a1, 0x020b, 0x1078, 0x6806, 0x7810, 0x20a2, 0xa006, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x4000, 0x0078, 0x6fa0, 0x157e,
+ 0x147e, 0x20a1, 0x020b, 0x1078, 0x6806, 0x7810, 0x20a2, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x2000, 0x0078, 0x6fa0,
+ 0x157e, 0x147e, 0x20a1, 0x020b, 0x1078, 0x6806, 0x7810, 0x20a2,
+ 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0400, 0x0078,
+ 0x6fa0, 0x157e, 0x147e, 0x20a1, 0x020b, 0x1078, 0x6806, 0x7810,
+ 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0200,
+ 0x1078, 0x7050, 0x60c3, 0x0020, 0x1078, 0x6c2d, 0x147f, 0x157f,
+ 0x007c, 0x127e, 0x0c7e, 0x2091, 0x8000, 0x2061, 0x0100, 0x6120,
+ 0xd1b4, 0x00c0, 0x6fb8, 0xd1bc, 0x00c0, 0x7002, 0x0078, 0x7042,
+ 0x2009, 0x017f, 0x200b, 0x00a1, 0x157e, 0x007e, 0x0d7e, 0x2069,
+ 0x0140, 0x20a9, 0x001e, 0x2009, 0x0169, 0x6804, 0xa084, 0x4000,
+ 0x0040, 0x6ff9, 0x6020, 0xd0b4, 0x0040, 0x6ff9, 0x6024, 0xd094,
+ 0x00c0, 0x6ff9, 0x2104, 0xa084, 0x000f, 0xa086, 0x0004, 0x00c0,
+ 0x6ff9, 0x00f0, 0x6fc5, 0x027e, 0x6198, 0xa18c, 0x00ff, 0x8107,
+ 0x6130, 0xa18c, 0x00ff, 0xa10d, 0x6088, 0x628c, 0x618e, 0x608b,
+ 0xbc91, 0x6043, 0x0001, 0x6043, 0x0000, 0x608a, 0x628e, 0x6024,
+ 0xd094, 0x00c0, 0x6ff8, 0x6a04, 0xa294, 0x4000, 0x00c0, 0x6fef,
+ 0x027f, 0x0d7f, 0x007f, 0x157f, 0x2009, 0x017f, 0x200b, 0x0000,
+ 0x0078, 0x7042, 0x2009, 0x017f, 0x200b, 0x00a1, 0x157e, 0x007e,
+ 0x0d7e, 0x2069, 0x0140, 0x20a9, 0x001e, 0x2009, 0x0169, 0x6804,
+ 0xa084, 0x4000, 0x0040, 0x703b, 0x6020, 0xd0bc, 0x0040, 0x703b,
+ 0x2104, 0xa084, 0x000f, 0xa086, 0x0004, 0x00c0, 0x703b, 0x00f0,
+ 0x700f, 0x027e, 0x6164, 0xa18c, 0x00ff, 0x8107, 0x6130, 0xa18c,
+ 0x00ff, 0xa10d, 0x6088, 0x628c, 0x608b, 0xbc91, 0x618e, 0x6043,
+ 0x0001, 0x6043, 0x0000, 0x608a, 0x628e, 0x6a04, 0xa294, 0x4000,
+ 0x00c0, 0x7035, 0x027f, 0x0d7f, 0x007f, 0x157f, 0x2009, 0x017f,
+ 0x200b, 0x0000, 0x0c7f, 0x127f, 0x007c, 0x0e7e, 0x2071, 0xa5ab,
+ 0x7020, 0xa005, 0x0040, 0x704e, 0x8001, 0x7022, 0x0e7f, 0x007c,
+ 0x20a9, 0x0008, 0x20a2, 0x00f0, 0x7052, 0x20a2, 0x20a2, 0x007c,
+ 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x077e, 0x067e, 0x007e, 0x127e,
+ 0x2091, 0x8000, 0x2071, 0xa5ab, 0x7614, 0x2660, 0x2678, 0x2039,
+ 0x0001, 0x87ff, 0x0040, 0x70f4, 0x8cff, 0x0040, 0x70f4, 0x601c,
+ 0xa086, 0x0006, 0x00c0, 0x70ef, 0x88ff, 0x0040, 0x707f, 0x2800,
+ 0xac06, 0x00c0, 0x70ef, 0x2039, 0x0000, 0x0078, 0x708a, 0x6018,
+ 0xa206, 0x00c0, 0x70ef, 0x85ff, 0x0040, 0x708a, 0x6020, 0xa106,
+ 0x00c0, 0x70ef, 0x7024, 0xac06, 0x00c0, 0x70ba, 0x2069, 0x0100,
+ 0x68c0, 0xa005, 0x0040, 0x70b5, 0x1078, 0x595a, 0x6817, 0x0008,
+ 0x68c3, 0x0000, 0x1078, 0x7188, 0x7027, 0x0000, 0x037e, 0x2069,
+ 0x0140, 0x6b04, 0xa384, 0x1000, 0x0040, 0x70aa, 0x6803, 0x0100,
+ 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0040, 0x70b2,
+ 0x6827, 0x0001, 0x037f, 0x0078, 0x70ba, 0x6003, 0x0009, 0x630a,
+ 0x0078, 0x70ef, 0x7014, 0xac36, 0x00c0, 0x70c0, 0x660c, 0x7616,
+ 0x7010, 0xac36, 0x00c0, 0x70ce, 0x2c00, 0xaf36, 0x0040, 0x70cc,
+ 0x2f00, 0x7012, 0x0078, 0x70ce, 0x7013, 0x0000, 0x660c, 0x067e,
+ 0x2c00, 0xaf06, 0x0040, 0x70d7, 0x7e0e, 0x0078, 0x70d8, 0x2678,
+ 0x89ff, 0x00c0, 0x70e7, 0x600f, 0x0000, 0x6010, 0x2068, 0x1078,
+ 0x8a44, 0x0040, 0x70e5, 0x1078, 0x9e70, 0x1078, 0x8c01, 0x1078,
+ 0x7045, 0x88ff, 0x00c0, 0x70fe, 0x0c7f, 0x0078, 0x7069, 0x2c78,
+ 0x600c, 0x2060, 0x0078, 0x7069, 0xa006, 0x127f, 0x007f, 0x067f,
+ 0x077f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f, 0x007c, 0x6017, 0x0000,
+ 0x0c7f, 0xa8c5, 0x0001, 0x0078, 0x70f5, 0x0f7e, 0x0e7e, 0x0d7e,
+ 0x0c7e, 0x067e, 0x027e, 0x007e, 0x127e, 0x2091, 0x8000, 0x2071,
+ 0xa5ab, 0x7638, 0x2660, 0x2678, 0x8cff, 0x0040, 0x7177, 0x601c,
+ 0xa086, 0x0006, 0x00c0, 0x7172, 0x87ff, 0x0040, 0x7125, 0x2700,
+ 0xac06, 0x00c0, 0x7172, 0x0078, 0x7130, 0x6018, 0xa206, 0x00c0,
+ 0x7172, 0x85ff, 0x0040, 0x7130, 0x6020, 0xa106, 0x00c0, 0x7172,
+ 0x703c, 0xac06, 0x00c0, 0x7142, 0x037e, 0x2019, 0x0001, 0x1078,
+ 0x6e6c, 0x7033, 0x0000, 0x703f, 0x0000, 0x7043, 0x0000, 0x7047,
+ 0x0000, 0x037f, 0x7038, 0xac36, 0x00c0, 0x7148, 0x660c, 0x763a,
+ 0x7034, 0xac36, 0x00c0, 0x7156, 0x2c00, 0xaf36, 0x0040, 0x7154,
+ 0x2f00, 0x7036, 0x0078, 0x7156, 0x7037, 0x0000, 0x660c, 0x067e,
+ 0x2c00, 0xaf06, 0x0040, 0x715f, 0x7e0e, 0x0078, 0x7160, 0x2678,
+ 0x600f, 0x0000, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x716a,
+ 0x1078, 0x9e70, 0x1078, 0x8c01, 0x87ff, 0x00c0, 0x7181, 0x0c7f,
+ 0x0078, 0x7114, 0x2c78, 0x600c, 0x2060, 0x0078, 0x7114, 0xa006,
+ 0x127f, 0x007f, 0x027f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f,
+ 0x007c, 0x6017, 0x0000, 0x0c7f, 0xa7bd, 0x0001, 0x0078, 0x7178,
+ 0x0e7e, 0x2071, 0xa5ab, 0x2001, 0xa300, 0x2004, 0xa086, 0x0002,
+ 0x00c0, 0x7196, 0x7007, 0x0005, 0x0078, 0x7198, 0x7007, 0x0000,
+ 0x0e7f, 0x007c, 0x0f7e, 0x0e7e, 0x0c7e, 0x067e, 0x027e, 0x007e,
+ 0x127e, 0x2091, 0x8000, 0x2071, 0xa5ab, 0x2c10, 0x7638, 0x2660,
+ 0x2678, 0x8cff, 0x0040, 0x71d8, 0x2200, 0xac06, 0x00c0, 0x71d3,
+ 0x7038, 0xac36, 0x00c0, 0x71b6, 0x660c, 0x763a, 0x7034, 0xac36,
+ 0x00c0, 0x71c4, 0x2c00, 0xaf36, 0x0040, 0x71c2, 0x2f00, 0x7036,
+ 0x0078, 0x71c4, 0x7037, 0x0000, 0x660c, 0x2c00, 0xaf06, 0x0040,
+ 0x71cc, 0x7e0e, 0x0078, 0x71cd, 0x2678, 0x600f, 0x0000, 0xa085,
+ 0x0001, 0x0078, 0x71d8, 0x2c78, 0x600c, 0x2060, 0x0078, 0x71a9,
+ 0x127f, 0x007f, 0x027f, 0x067f, 0x0c7f, 0x0e7f, 0x0f7f, 0x007c,
+ 0x0f7e, 0x0e7e, 0x0d7e, 0x0c7e, 0x067e, 0x007e, 0x127e, 0x2091,
+ 0x8000, 0x2071, 0xa5ab, 0x760c, 0x2660, 0x2678, 0x8cff, 0x0040,
+ 0x7279, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x00c0, 0x7274,
+ 0x7024, 0xac06, 0x00c0, 0x721f, 0x2069, 0x0100, 0x68c0, 0xa005,
+ 0x0040, 0x724d, 0x1078, 0x6c41, 0x68c3, 0x0000, 0x1078, 0x7188,
+ 0x7027, 0x0000, 0x037e, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000,
+ 0x0040, 0x7216, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100,
+ 0x6824, 0xd084, 0x0040, 0x721e, 0x6827, 0x0001, 0x037f, 0x700c,
+ 0xac36, 0x00c0, 0x7225, 0x660c, 0x760e, 0x7008, 0xac36, 0x00c0,
+ 0x7233, 0x2c00, 0xaf36, 0x0040, 0x7231, 0x2f00, 0x700a, 0x0078,
+ 0x7233, 0x700b, 0x0000, 0x660c, 0x067e, 0x2c00, 0xaf06, 0x0040,
+ 0x723c, 0x7e0e, 0x0078, 0x723d, 0x2678, 0x600f, 0x0000, 0x1078,
+ 0x8c27, 0x00c0, 0x7251, 0x1078, 0x2839, 0x1078, 0x8c3b, 0x00c0,
+ 0x726d, 0x1078, 0x7a05, 0x0078, 0x726d, 0x1078, 0x7188, 0x0078,
+ 0x721f, 0x1078, 0x8c3b, 0x00c0, 0x7259, 0x1078, 0x7a05, 0x0078,
+ 0x726d, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x726d, 0x601c,
+ 0xa086, 0x0003, 0x00c0, 0x7281, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x1078, 0x4982, 0x1078, 0x8bf4, 0x1078, 0x8c01, 0x1078,
+ 0x7045, 0x0c7f, 0x0078, 0x71ee, 0x2c78, 0x600c, 0x2060, 0x0078,
+ 0x71ee, 0x127f, 0x007f, 0x067f, 0x0c7f, 0x0d7f, 0x0e7f, 0x0f7f,
+ 0x007c, 0x601c, 0xa086, 0x0006, 0x00c0, 0x726d, 0x1078, 0x9e70,
+ 0x0078, 0x726d, 0x037e, 0x157e, 0x137e, 0x147e, 0x3908, 0xa006,
+ 0xa190, 0x0020, 0x221c, 0xa39e, 0x260c, 0x00c0, 0x729b, 0x8210,
+ 0x8000, 0x0078, 0x7292, 0xa005, 0x0040, 0x72a7, 0x20a9, 0x0020,
+ 0x2198, 0x8211, 0xa282, 0x0020, 0x20c8, 0x20a0, 0x53a3, 0x147f,
+ 0x137f, 0x157f, 0x037f, 0x007c, 0x0d7e, 0x20a1, 0x020b, 0x1078,
+ 0x65f8, 0x20a3, 0x0200, 0x20a3, 0x0014, 0x60c3, 0x0014, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x2099, 0xa5a3, 0x20a9, 0x0004, 0x53a6,
+ 0x20a3, 0x0004, 0x20a3, 0x7878, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x1078, 0x6c2d, 0x0d7f, 0x007c, 0x20a1, 0x020b, 0x1078, 0x65f8,
+ 0x20a3, 0x0214, 0x20a3, 0x0018, 0x20a3, 0x0800, 0x7810, 0xa084,
+ 0xff00, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x7810, 0xa084, 0x00ff, 0x20a2, 0x7828, 0x20a2,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0018, 0x1078, 0x6c2d,
+ 0x007c, 0x0d7e, 0x017e, 0x2f68, 0x2009, 0x0035, 0x1078, 0x8ef5,
+ 0x00c0, 0x7361, 0x20a1, 0x020b, 0x1078, 0x6567, 0x20a3, 0x1300,
+ 0x20a3, 0x0000, 0x7828, 0x2068, 0x681c, 0xa086, 0x0003, 0x0040,
+ 0x733d, 0x7818, 0xa080, 0x0028, 0x2014, 0xa286, 0x007e, 0x00c0,
+ 0x7317, 0x20a3, 0x00ff, 0x20a3, 0xfffe, 0x0078, 0x7352, 0xa286,
+ 0x007f, 0x00c0, 0x7321, 0x20a3, 0x00ff, 0x20a3, 0xfffd, 0x0078,
+ 0x7352, 0xd2bc, 0x0040, 0x7337, 0xa286, 0x0080, 0x00c0, 0x732e,
+ 0x20a3, 0x00ff, 0x20a3, 0xfffc, 0x0078, 0x7352, 0xa2e8, 0xa434,
+ 0x2d6c, 0x6810, 0x20a2, 0x6814, 0x20a2, 0x0078, 0x7352, 0x20a3,
+ 0x0000, 0x6098, 0x20a2, 0x0078, 0x7352, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xa082, 0x007e, 0x0048, 0x734e, 0x0d7e, 0x2069, 0xa31a,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x7352, 0x20a3, 0x0000,
+ 0x6030, 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x000c, 0x1078, 0x6c2d, 0x017f, 0x0d7f,
+ 0x007c, 0x7817, 0x0001, 0x7803, 0x0006, 0x017f, 0x0d7f, 0x007c,
+ 0x0d7e, 0x027e, 0x7928, 0x2168, 0x691c, 0xa186, 0x0006, 0x0040,
+ 0x738a, 0xa186, 0x0003, 0x0040, 0x73e5, 0xa186, 0x0005, 0x0040,
+ 0x73c8, 0xa186, 0x0004, 0x0040, 0x73b8, 0xa186, 0x0008, 0x0040,
+ 0x73d2, 0x7807, 0x0037, 0x7813, 0x1700, 0x1078, 0x7450, 0x027f,
+ 0x0d7f, 0x007c, 0x1078, 0x740d, 0x2009, 0x4000, 0x6800, 0x0079,
+ 0x7391, 0x73a4, 0x73b2, 0x73a6, 0x73b2, 0x73ad, 0x73a4, 0x73a4,
+ 0x73b2, 0x73b2, 0x73b2, 0x73b2, 0x73a4, 0x73a4, 0x73a4, 0x73a4,
+ 0x73a4, 0x73b2, 0x73a4, 0x73b2, 0x1078, 0x1328, 0x6824, 0xd0e4,
+ 0x0040, 0x73ad, 0xd0cc, 0x0040, 0x73b0, 0xa00e, 0x0078, 0x73b2,
+ 0x2009, 0x2000, 0x6828, 0x20a2, 0x682c, 0x20a2, 0x0078, 0x7403,
+ 0x1078, 0x740d, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000,
+ 0x6a00, 0xa286, 0x0002, 0x00c0, 0x73c6, 0xa00e, 0x0078, 0x7403,
+ 0x1078, 0x740d, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000,
+ 0x0078, 0x7403, 0x1078, 0x740d, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x2009, 0x4000, 0xa286, 0x0005, 0x0040, 0x73e2, 0xa286, 0x0002,
+ 0x00c0, 0x73e3, 0xa00e, 0x0078, 0x7403, 0x1078, 0x740d, 0x6810,
+ 0x2068, 0x697c, 0x6810, 0xa112, 0x6980, 0x6814, 0xa103, 0x20a2,
+ 0x22a2, 0x7928, 0xa180, 0x0000, 0x2004, 0xa08e, 0x0002, 0x0040,
+ 0x7401, 0xa08e, 0x0004, 0x0040, 0x7401, 0x2009, 0x4000, 0x0078,
+ 0x7403, 0x2009, 0x0000, 0x21a2, 0x20a3, 0x0000, 0x60c3, 0x0018,
+ 0x1078, 0x6c2d, 0x027f, 0x0d7f, 0x007c, 0x037e, 0x047e, 0x057e,
+ 0x067e, 0x20a1, 0x020b, 0x1078, 0x65f8, 0xa006, 0x20a3, 0x0200,
+ 0x20a2, 0x7934, 0x21a2, 0x7938, 0x21a2, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xa092, 0x007e, 0x0048, 0x7433, 0x0d7e, 0x2069, 0xa31a,
+ 0x2d2c, 0x8d68, 0x2d34, 0xa0e8, 0xa434, 0x2d6c, 0x6b10, 0x6c14,
+ 0x0d7f, 0x0078, 0x7439, 0x2019, 0x0000, 0x6498, 0x2029, 0x0000,
+ 0x6630, 0x7828, 0xa080, 0x0007, 0x2004, 0xa086, 0x0003, 0x00c0,
+ 0x7447, 0x25a2, 0x26a2, 0x23a2, 0x24a2, 0x0078, 0x744b, 0x23a2,
+ 0x24a2, 0x25a2, 0x26a2, 0x067f, 0x057f, 0x047f, 0x037f, 0x007c,
+ 0x20a1, 0x020b, 0x1078, 0x65f8, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x0009, 0x7810, 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6c2d,
+ 0x007c, 0x20a1, 0x020b, 0x1078, 0x655e, 0x20a3, 0x1400, 0x20a3,
+ 0x0000, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x7828, 0x20a2, 0x782c,
+ 0x20a2, 0x7830, 0xa084, 0x00ff, 0x8007, 0x20a2, 0x20a3, 0x0000,
+ 0x60c3, 0x0010, 0x1078, 0x6c2d, 0x007c, 0x20a1, 0x020b, 0x1078,
+ 0x65ef, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x7828, 0x20a2, 0x7810,
+ 0x20a2, 0x60c3, 0x0008, 0x1078, 0x6c2d, 0x007c, 0x147e, 0x20a1,
+ 0x020b, 0x1078, 0x7499, 0x60c3, 0x0000, 0x1078, 0x6c2d, 0x147f,
+ 0x007c, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0xd0bc, 0x0040, 0x74b6, 0x0d7e, 0xa0e8, 0xa434, 0x2d6c,
+ 0x6810, 0xa085, 0x0300, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xa31a,
+ 0x2da6, 0x8d68, 0x2da6, 0x0d7f, 0x0078, 0x74be, 0x20a3, 0x0300,
+ 0x6298, 0x22a2, 0x20a3, 0x0000, 0x6230, 0x22a2, 0x20a3, 0x0819,
+ 0x20a3, 0x0000, 0x1078, 0x6c1c, 0x22a2, 0x20a3, 0x0000, 0x2fa2,
+ 0x7a08, 0x22a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x007c, 0x2061,
+ 0xaa00, 0x2a70, 0x7060, 0x7046, 0x704b, 0xaa00, 0x007c, 0x0e7e,
+ 0x127e, 0x2071, 0xa300, 0x2091, 0x8000, 0x7544, 0xa582, 0x0010,
+ 0x0048, 0x7509, 0x7048, 0x2060, 0x6000, 0xa086, 0x0000, 0x0040,
+ 0x74f5, 0xace0, 0x0010, 0x7054, 0xac02, 0x00c8, 0x74f1, 0x0078,
+ 0x74e4, 0x2061, 0xaa00, 0x0078, 0x74e4, 0x6003, 0x0008, 0x8529,
+ 0x7546, 0xaca8, 0x0010, 0x7054, 0xa502, 0x00c8, 0x7505, 0x754a,
+ 0xa085, 0x0001, 0x127f, 0x0e7f, 0x007c, 0x704b, 0xaa00, 0x0078,
+ 0x7500, 0xa006, 0x0078, 0x7502, 0x0e7e, 0x2071, 0xa300, 0x7544,
+ 0xa582, 0x0010, 0x0048, 0x753a, 0x7048, 0x2060, 0x6000, 0xa086,
+ 0x0000, 0x0040, 0x7527, 0xace0, 0x0010, 0x7054, 0xac02, 0x00c8,
+ 0x7523, 0x0078, 0x7516, 0x2061, 0xaa00, 0x0078, 0x7516, 0x6003,
+ 0x0008, 0x8529, 0x7546, 0xaca8, 0x0010, 0x7054, 0xa502, 0x00c8,
+ 0x7536, 0x754a, 0xa085, 0x0001, 0x0e7f, 0x007c, 0x704b, 0xaa00,
+ 0x0078, 0x7532, 0xa006, 0x0078, 0x7534, 0xac82, 0xaa00, 0x1048,
+ 0x1328, 0x2001, 0xa315, 0x2004, 0xac02, 0x10c8, 0x1328, 0xa006,
+ 0x6006, 0x600a, 0x600e, 0x6012, 0x6016, 0x601a, 0x601f, 0x0000,
+ 0x6003, 0x0000, 0x6022, 0x6026, 0x602a, 0x602e, 0x6032, 0x6036,
+ 0x603a, 0x603e, 0x2061, 0xa300, 0x6044, 0x8000, 0x6046, 0xa086,
+ 0x0001, 0x0040, 0x7564, 0x007c, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x6109, 0x127f, 0x0078, 0x7563, 0x601c, 0xa084, 0x000f, 0x0079,
+ 0x7571, 0x757a, 0x758b, 0x75a7, 0x75c3, 0x8f2d, 0x8f49, 0x8f65,
+ 0x757a, 0x758b, 0xa186, 0x0013, 0x00c0, 0x7583, 0x1078, 0x6010,
+ 0x1078, 0x6109, 0x007c, 0xa18e, 0x0047, 0x00c0, 0x758a, 0xa016,
+ 0x1078, 0x15ec, 0x007c, 0x067e, 0x6000, 0xa0b2, 0x0010, 0x10c8,
+ 0x1328, 0x1079, 0x7595, 0x067f, 0x007c, 0x75a5, 0x7891, 0x7a34,
+ 0x75a5, 0x7ab8, 0x75df, 0x75a5, 0x75a5, 0x7823, 0x7e6d, 0x75a5,
+ 0x75a5, 0x75a5, 0x75a5, 0x75a5, 0x75a5, 0x1078, 0x1328, 0x067e,
+ 0x6000, 0xa0b2, 0x0010, 0x10c8, 0x1328, 0x1079, 0x75b1, 0x067f,
+ 0x007c, 0x75c1, 0x8522, 0x75c1, 0x75c1, 0x75c1, 0x75c1, 0x75c1,
+ 0x75c1, 0x84c5, 0x86a8, 0x75c1, 0x8552, 0x85d8, 0x8552, 0x85d8,
+ 0x75c1, 0x1078, 0x1328, 0x067e, 0x6000, 0xa0b2, 0x0010, 0x10c8,
+ 0x1328, 0x1079, 0x75cd, 0x067f, 0x007c, 0x75dd, 0x7eb4, 0x7f81,
+ 0x80c6, 0x8242, 0x75dd, 0x75dd, 0x75dd, 0x7e8d, 0x846d, 0x8471,
+ 0x75dd, 0x75dd, 0x75dd, 0x75dd, 0x84a1, 0x1078, 0x1328, 0xa1b6,
+ 0x0015, 0x00c0, 0x75e7, 0x1078, 0x753d, 0x0078, 0x75ed, 0xa1b6,
+ 0x0016, 0x10c0, 0x1328, 0x1078, 0x753d, 0x007c, 0x20a9, 0x000e,
+ 0x2e98, 0x6010, 0x20a0, 0x53a3, 0x20a9, 0x0006, 0x3310, 0x3420,
+ 0x9398, 0x94a0, 0x3318, 0x3428, 0x222e, 0x2326, 0xa290, 0x0002,
+ 0xa5a8, 0x0002, 0xa398, 0x0002, 0xa4a0, 0x0002, 0x00f0, 0x75fc,
+ 0x0e7e, 0x1078, 0x8a44, 0x0040, 0x7613, 0x6010, 0x2070, 0x7007,
+ 0x0000, 0x7037, 0x0103, 0x0e7f, 0x1078, 0x753d, 0x007c, 0x0d7e,
+ 0x037e, 0x7330, 0xa386, 0x0200, 0x00c0, 0x7624, 0x6018, 0x2068,
+ 0x6813, 0x00ff, 0x6817, 0xfffd, 0x6010, 0xa005, 0x0040, 0x762e,
+ 0x2068, 0x6807, 0x0000, 0x6837, 0x0103, 0x6b32, 0x1078, 0x753d,
+ 0x037f, 0x0d7f, 0x007c, 0x017e, 0x20a9, 0x002a, 0xae80, 0x000c,
+ 0x2098, 0x6010, 0xa080, 0x0002, 0x20a0, 0x53a3, 0x20a9, 0x002a,
+ 0x6010, 0xa080, 0x0001, 0x2004, 0xa080, 0x0002, 0x20a0, 0x53a3,
+ 0x0e7e, 0x6010, 0x2004, 0x2070, 0x7037, 0x0103, 0x0e7f, 0x1078,
+ 0x753d, 0x017f, 0x007c, 0x0e7e, 0x0d7e, 0x603f, 0x0000, 0x2c68,
+ 0x017e, 0x2009, 0x0035, 0x1078, 0x8ef5, 0x017f, 0x00c0, 0x766f,
+ 0x027e, 0x6228, 0x2268, 0x027f, 0x2071, 0xa88c, 0x6b1c, 0xa386,
+ 0x0003, 0x0040, 0x7673, 0xa386, 0x0006, 0x0040, 0x7677, 0x1078,
+ 0x753d, 0x0078, 0x7679, 0x1078, 0x767c, 0x0078, 0x7679, 0x1078,
+ 0x771e, 0x0d7f, 0x0e7f, 0x007c, 0x0f7e, 0x6810, 0x2078, 0xa186,
+ 0x0015, 0x0040, 0x7705, 0xa18e, 0x0016, 0x00c0, 0x771c, 0x700c,
+ 0xa084, 0xff00, 0xa086, 0x1700, 0x00c0, 0x76e0, 0x8fff, 0x0040,
+ 0x771a, 0x6808, 0xa086, 0xffff, 0x00c0, 0x7709, 0x784c, 0xa084,
+ 0x0060, 0xa086, 0x0020, 0x00c0, 0x76a7, 0x797c, 0x7810, 0xa106,
+ 0x00c0, 0x7709, 0x7980, 0x7814, 0xa106, 0x00c0, 0x7709, 0x1078,
+ 0x8bf4, 0x6830, 0x7852, 0x784c, 0xc0dc, 0xc0f4, 0xc0d4, 0x784e,
+ 0x027e, 0xa00e, 0x6a14, 0x2001, 0x000a, 0x1078, 0x5a98, 0x7854,
+ 0xa20a, 0x0048, 0x76bc, 0x8011, 0x7a56, 0x82ff, 0x027f, 0x00c0,
+ 0x76c8, 0x0c7e, 0x2d60, 0x1078, 0x8832, 0x0c7f, 0x0078, 0x771a,
+ 0x0c7e, 0x0d7e, 0x2f68, 0x6838, 0xd0fc, 0x00c0, 0x76d3, 0x1078,
+ 0x4290, 0x0078, 0x76d5, 0x1078, 0x436e, 0x0d7f, 0x0c7f, 0x00c0,
+ 0x7709, 0x0c7e, 0x2d60, 0x1078, 0x753d, 0x0c7f, 0x0078, 0x771a,
+ 0x7008, 0xa086, 0x000b, 0x00c0, 0x76fa, 0x6018, 0x200c, 0xc1bc,
+ 0x2102, 0x0c7e, 0x2d60, 0x7853, 0x0003, 0x6007, 0x0085, 0x6003,
+ 0x000b, 0x601f, 0x0002, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0c7f,
+ 0x0078, 0x771a, 0x700c, 0xa086, 0x2a00, 0x00c0, 0x7709, 0x2001,
+ 0xa5a2, 0x2004, 0x683e, 0x0078, 0x771a, 0x1078, 0x7739, 0x0078,
+ 0x771c, 0x8fff, 0x1040, 0x1328, 0x0c7e, 0x0d7e, 0x2d60, 0x2f68,
+ 0x684b, 0x0003, 0x1078, 0x8726, 0x1078, 0x8bf4, 0x1078, 0x8c01,
+ 0x0d7f, 0x0c7f, 0x1078, 0x753d, 0x0f7f, 0x007c, 0xa186, 0x0015,
+ 0x00c0, 0x7728, 0x2001, 0xa5a2, 0x2004, 0x683e, 0x0078, 0x7736,
+ 0xa18e, 0x0016, 0x00c0, 0x7738, 0x0c7e, 0x2d00, 0x2060, 0x1078,
+ 0xa134, 0x1078, 0x5a41, 0x1078, 0x753d, 0x0c7f, 0x1078, 0x753d,
+ 0x007c, 0x027e, 0x037e, 0x047e, 0x7228, 0x7c80, 0x7b7c, 0xd2f4,
+ 0x0040, 0x7748, 0x2001, 0xa5a2, 0x2004, 0x683e, 0x0078, 0x77ac,
+ 0x0c7e, 0x2d60, 0x1078, 0x874a, 0x0c7f, 0x6804, 0xa086, 0x0050,
+ 0x00c0, 0x7760, 0x0c7e, 0x2d00, 0x2060, 0x6003, 0x0001, 0x6007,
+ 0x0050, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0c7f, 0x0078, 0x77ac,
+ 0x6800, 0xa086, 0x000f, 0x0040, 0x7782, 0x8fff, 0x1040, 0x1328,
+ 0x6824, 0xd0dc, 0x00c0, 0x7782, 0x6800, 0xa086, 0x0004, 0x00c0,
+ 0x7787, 0x784c, 0xd0ac, 0x0040, 0x7787, 0x784c, 0xc0dc, 0xc0f4,
+ 0x784e, 0x7850, 0xc0f4, 0xc0fc, 0x7852, 0x2001, 0x0001, 0x682e,
+ 0x0078, 0x77a6, 0x2001, 0x0007, 0x682e, 0x0078, 0x77a6, 0x784c,
+ 0xd0b4, 0x00c0, 0x7794, 0xd0ac, 0x0040, 0x7782, 0x784c, 0xd0f4,
+ 0x00c0, 0x7782, 0x0078, 0x7775, 0xd2ec, 0x00c0, 0x7782, 0x7024,
+ 0xa306, 0x00c0, 0x779f, 0x7020, 0xa406, 0x0040, 0x7782, 0x7020,
+ 0x6836, 0x7024, 0x683a, 0x2001, 0x0005, 0x682e, 0x1078, 0x8d2b,
+ 0x1078, 0x6109, 0x0078, 0x77ae, 0x1078, 0x753d, 0x047f, 0x037f,
+ 0x027f, 0x007c, 0x0e7e, 0x0d7e, 0x027e, 0x6034, 0x2068, 0x6a1c,
+ 0xa286, 0x0007, 0x0040, 0x7806, 0xa286, 0x0002, 0x0040, 0x7806,
+ 0xa286, 0x0000, 0x0040, 0x7806, 0x6808, 0x6338, 0xa306, 0x00c0,
+ 0x7806, 0x2071, 0xa88c, 0xa186, 0x0015, 0x0040, 0x7800, 0xa18e,
+ 0x0016, 0x00c0, 0x77e8, 0x6030, 0xa084, 0x00ff, 0xa086, 0x0001,
+ 0x00c0, 0x77e8, 0x700c, 0xa086, 0x2a00, 0x00c0, 0x77e8, 0x6034,
+ 0xa080, 0x0009, 0x200c, 0xc1dd, 0xc1f5, 0x2102, 0x0078, 0x7800,
+ 0x0c7e, 0x6034, 0x2060, 0x6010, 0x2068, 0x1078, 0x8a44, 0x1040,
+ 0x1328, 0x6853, 0x0003, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f,
+ 0x0002, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0c7f, 0x0078, 0x7806,
+ 0x6034, 0x2068, 0x2001, 0xa5a2, 0x2004, 0x683e, 0x1078, 0x753d,
+ 0x027f, 0x0d7f, 0x0e7f, 0x007c, 0x0d7e, 0x20a9, 0x000e, 0x2e98,
+ 0x6010, 0x20a0, 0x53a3, 0xa1b6, 0x0015, 0x00c0, 0x7820, 0x6018,
+ 0x2068, 0x7038, 0x680a, 0x703c, 0x680e, 0x6800, 0xc08d, 0x6802,
+ 0x0d7f, 0x0078, 0x7608, 0x2100, 0xa1b2, 0x0044, 0x10c8, 0x1328,
+ 0xa1b2, 0x0040, 0x00c8, 0x7888, 0x0079, 0x782e, 0x787c, 0x7870,
+ 0x787c, 0x787c, 0x787c, 0x787c, 0x786e, 0x786e, 0x786e, 0x786e,
+ 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e,
+ 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e,
+ 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x787c, 0x786e, 0x787c,
+ 0x787c, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x787c, 0x786e,
+ 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e,
+ 0x787c, 0x787c, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e, 0x786e,
+ 0x786e, 0x786e, 0x786e, 0x787c, 0x786e, 0x786e, 0x1078, 0x1328,
+ 0x6003, 0x0001, 0x6106, 0x1078, 0x5c45, 0x127e, 0x2091, 0x8000,
+ 0x1078, 0x6109, 0x127f, 0x007c, 0x6003, 0x0001, 0x6106, 0x1078,
+ 0x5c45, 0x127e, 0x2091, 0x8000, 0x1078, 0x6109, 0x127f, 0x007c,
+ 0x2600, 0x0079, 0x788b, 0x788f, 0x788f, 0x788f, 0x787c, 0x1078,
+ 0x1328, 0x6004, 0xa0b2, 0x0044, 0x10c8, 0x1328, 0xa1b6, 0x0013,
+ 0x00c0, 0x78a1, 0xa0b2, 0x0040, 0x00c8, 0x79fb, 0x2008, 0x0079,
+ 0x7941, 0xa1b6, 0x0027, 0x00c0, 0x78fe, 0x1078, 0x6010, 0x6004,
+ 0x1078, 0x8c27, 0x0040, 0x78be, 0x1078, 0x8c3b, 0x0040, 0x78f6,
+ 0xa08e, 0x0021, 0x0040, 0x78fa, 0xa08e, 0x0022, 0x0040, 0x78f6,
+ 0xa08e, 0x003d, 0x0040, 0x78fa, 0x0078, 0x78f1, 0x1078, 0x2839,
+ 0x2001, 0x0007, 0x1078, 0x443f, 0x6018, 0xa080, 0x0028, 0x200c,
+ 0x1078, 0x7a05, 0xa186, 0x007e, 0x00c0, 0x78d3, 0x2001, 0xa332,
+ 0x2014, 0xc285, 0x2202, 0x017e, 0x027e, 0x037e, 0x2110, 0x2019,
+ 0x0028, 0x1078, 0x5d53, 0x077e, 0x2039, 0x0000, 0x1078, 0x5c78,
+ 0x0c7e, 0x6018, 0xa065, 0x0040, 0x78e7, 0x1078, 0x471b, 0x0c7f,
+ 0x2c08, 0x1078, 0x9c38, 0x077f, 0x037f, 0x027f, 0x017f, 0x1078,
+ 0x44bc, 0x1078, 0x753d, 0x1078, 0x6109, 0x007c, 0x1078, 0x7a05,
+ 0x0078, 0x78f1, 0x1078, 0x7a28, 0x0078, 0x78f1, 0xa186, 0x0014,
+ 0x00c0, 0x78f5, 0x1078, 0x6010, 0x1078, 0x2813, 0x1078, 0x8c27,
+ 0x00c0, 0x791d, 0x1078, 0x2839, 0x6018, 0xa080, 0x0028, 0x200c,
+ 0x1078, 0x7a05, 0xa186, 0x007e, 0x00c0, 0x791b, 0x2001, 0xa332,
+ 0x200c, 0xc185, 0x2102, 0x0078, 0x78f1, 0x1078, 0x8c3b, 0x00c0,
+ 0x7925, 0x1078, 0x7a05, 0x0078, 0x78f1, 0x6004, 0xa08e, 0x0032,
+ 0x00c0, 0x7936, 0x0e7e, 0x0f7e, 0x2071, 0xa381, 0x2079, 0x0000,
+ 0x1078, 0x2b56, 0x0f7f, 0x0e7f, 0x0078, 0x78f1, 0x6004, 0xa08e,
+ 0x0021, 0x0040, 0x7921, 0xa08e, 0x0022, 0x1040, 0x7a05, 0x0078,
+ 0x78f1, 0x7983, 0x7985, 0x7989, 0x798d, 0x7991, 0x7995, 0x7981,
+ 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981,
+ 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981,
+ 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981, 0x7999,
+ 0x79ab, 0x7981, 0x79ad, 0x79ab, 0x7981, 0x7981, 0x7981, 0x7981,
+ 0x7981, 0x79ab, 0x79ab, 0x7981, 0x7981, 0x7981, 0x7981, 0x7981,
+ 0x7981, 0x7981, 0x7981, 0x79de, 0x79ab, 0x7981, 0x79a5, 0x7981,
+ 0x7981, 0x7981, 0x79a7, 0x7981, 0x7981, 0x7981, 0x79ab, 0x7981,
+ 0x7981, 0x1078, 0x1328, 0x0078, 0x79ab, 0x2001, 0x000b, 0x0078,
+ 0x79b8, 0x2001, 0x0003, 0x0078, 0x79b8, 0x2001, 0x0005, 0x0078,
+ 0x79b8, 0x2001, 0x0001, 0x0078, 0x79b8, 0x2001, 0x0009, 0x0078,
+ 0x79b8, 0x1078, 0x6010, 0x6003, 0x0005, 0x2001, 0xa5a2, 0x2004,
+ 0x603e, 0x1078, 0x6109, 0x0078, 0x79b7, 0x0078, 0x79ab, 0x0078,
+ 0x79ab, 0x1078, 0x443f, 0x0078, 0x79f0, 0x1078, 0x6010, 0x6003,
+ 0x0004, 0x2001, 0xa5a0, 0x2004, 0x6016, 0x1078, 0x6109, 0x007c,
+ 0x1078, 0x443f, 0x1078, 0x6010, 0x2001, 0xa5a2, 0x2004, 0x603e,
+ 0x6003, 0x0002, 0x037e, 0x2019, 0xa35c, 0x2304, 0xa084, 0xff00,
+ 0x00c0, 0x79cf, 0x2019, 0xa5a0, 0x231c, 0x0078, 0x79d8, 0x8007,
+ 0xa09a, 0x0004, 0x0048, 0x79ca, 0x8003, 0x801b, 0x831b, 0xa318,
+ 0x6316, 0x037f, 0x1078, 0x6109, 0x0078, 0x79b7, 0x0e7e, 0x0f7e,
+ 0x2071, 0xa381, 0x2079, 0x0000, 0x1078, 0x2b56, 0x0f7f, 0x0e7f,
+ 0x1078, 0x6010, 0x1078, 0x753d, 0x1078, 0x6109, 0x0078, 0x79b7,
+ 0x1078, 0x6010, 0x6003, 0x0002, 0x2001, 0xa5a0, 0x2004, 0x6016,
+ 0x1078, 0x6109, 0x007c, 0x2600, 0x2008, 0x0079, 0x79ff, 0x7a03,
+ 0x7a03, 0x7a03, 0x79f0, 0x1078, 0x1328, 0x0e7e, 0x1078, 0x8a44,
+ 0x0040, 0x7a21, 0x6010, 0x2070, 0x7038, 0xd0fc, 0x0040, 0x7a21,
+ 0x7007, 0x0000, 0x017e, 0x6004, 0xa08e, 0x0021, 0x0040, 0x7a23,
+ 0xa08e, 0x003d, 0x0040, 0x7a23, 0x017f, 0x7037, 0x0103, 0x7033,
+ 0x0100, 0x0e7f, 0x007c, 0x017f, 0x1078, 0x7a28, 0x0078, 0x7a21,
+ 0x0e7e, 0xacf0, 0x0004, 0x2e74, 0x7000, 0x2070, 0x7037, 0x0103,
+ 0x7023, 0x8001, 0x0e7f, 0x007c, 0x0d7e, 0x6618, 0x2668, 0x6804,
+ 0xa084, 0x00ff, 0x0d7f, 0xa0b2, 0x000c, 0x10c8, 0x1328, 0x6604,
+ 0xa6b6, 0x0043, 0x00c0, 0x7a48, 0x1078, 0x8e6d, 0x0078, 0x7aa7,
+ 0x6604, 0xa6b6, 0x0033, 0x00c0, 0x7a51, 0x1078, 0x8e11, 0x0078,
+ 0x7aa7, 0x6604, 0xa6b6, 0x0028, 0x00c0, 0x7a5a, 0x1078, 0x8c6a,
+ 0x0078, 0x7aa7, 0x6604, 0xa6b6, 0x0029, 0x00c0, 0x7a63, 0x1078,
+ 0x8c84, 0x0078, 0x7aa7, 0x6604, 0xa6b6, 0x001f, 0x00c0, 0x7a6c,
+ 0x1078, 0x75ee, 0x0078, 0x7aa7, 0x6604, 0xa6b6, 0x0000, 0x00c0,
+ 0x7a75, 0x1078, 0x780c, 0x0078, 0x7aa7, 0x6604, 0xa6b6, 0x0022,
+ 0x00c0, 0x7a7e, 0x1078, 0x7617, 0x0078, 0x7aa7, 0x6604, 0xa6b6,
+ 0x0035, 0x00c0, 0x7a87, 0x1078, 0x7653, 0x0078, 0x7aa7, 0x6604,
+ 0xa6b6, 0x0039, 0x00c0, 0x7a90, 0x1078, 0x77b2, 0x0078, 0x7aa7,
+ 0x6604, 0xa6b6, 0x003d, 0x00c0, 0x7a99, 0x1078, 0x7633, 0x0078,
+ 0x7aa7, 0xa1b6, 0x0015, 0x00c0, 0x7aa1, 0x1079, 0x7aac, 0x0078,
+ 0x7aa7, 0xa1b6, 0x0016, 0x00c0, 0x7aa8, 0x1079, 0x7bfd, 0x007c,
+ 0x1078, 0x7583, 0x0078, 0x7aa7, 0x7ad0, 0x7ad3, 0x7ad0, 0x7b1e,
+ 0x7ad0, 0x7b91, 0x7c09, 0x7ad0, 0x7ad0, 0x7bd5, 0x7ad0, 0x7beb,
+ 0xa1b6, 0x0048, 0x0040, 0x7ac4, 0x20e1, 0x0005, 0x3d18, 0x3e20,
+ 0x2c10, 0x1078, 0x15ec, 0x007c, 0x0e7e, 0xacf0, 0x0004, 0x2e74,
+ 0x7000, 0x2070, 0x7037, 0x0103, 0x0e7f, 0x1078, 0x753d, 0x007c,
+ 0x0005, 0x0005, 0x007c, 0x0e7e, 0x2071, 0xa300, 0x707c, 0xa086,
+ 0x0074, 0x00c0, 0x7b07, 0x1078, 0x9c0c, 0x00c0, 0x7af9, 0x0d7e,
+ 0x6018, 0x2068, 0x7030, 0xd08c, 0x0040, 0x7aec, 0x6800, 0xd0bc,
+ 0x0040, 0x7aec, 0xc0c5, 0x6802, 0x1078, 0x7b0b, 0x0d7f, 0x2001,
+ 0x0006, 0x1078, 0x443f, 0x1078, 0x2839, 0x1078, 0x753d, 0x0078,
+ 0x7b09, 0x2001, 0x000a, 0x1078, 0x443f, 0x1078, 0x2839, 0x6003,
+ 0x0001, 0x6007, 0x0001, 0x1078, 0x5c45, 0x0078, 0x7b09, 0x1078,
+ 0x7b81, 0x0e7f, 0x007c, 0x6800, 0xd084, 0x0040, 0x7b1d, 0x2001,
+ 0x0000, 0x1078, 0x442b, 0x2069, 0xa351, 0x6804, 0xd0a4, 0x0040,
+ 0x7b1d, 0x2001, 0x0006, 0x1078, 0x4472, 0x007c, 0x0d7e, 0x2011,
+ 0xa31f, 0x2204, 0xa086, 0x0074, 0x00c0, 0x7b7d, 0x6018, 0x2068,
+ 0x6aa0, 0xa286, 0x007e, 0x00c0, 0x7b31, 0x1078, 0x7d17, 0x0078,
+ 0x7b7f, 0x1078, 0x7d0d, 0x6018, 0x2068, 0xa080, 0x0028, 0x2014,
+ 0xa286, 0x0080, 0x00c0, 0x7b55, 0x6813, 0x00ff, 0x6817, 0xfffc,
+ 0x6010, 0xa005, 0x0040, 0x7b4b, 0x2068, 0x6807, 0x0000, 0x6837,
+ 0x0103, 0x6833, 0x0200, 0x2001, 0x0006, 0x1078, 0x443f, 0x1078,
+ 0x2839, 0x1078, 0x753d, 0x0078, 0x7b7f, 0x0e7e, 0x2071, 0xa332,
+ 0x2e04, 0xd09c, 0x0040, 0x7b70, 0x2071, 0xa880, 0x7108, 0x720c,
+ 0xa18c, 0x00ff, 0x00c0, 0x7b68, 0xa284, 0xff00, 0x0040, 0x7b70,
+ 0x6018, 0x2070, 0x70a0, 0xd0bc, 0x00c0, 0x7b70, 0x7112, 0x7216,
+ 0x0e7f, 0x2001, 0x0004, 0x1078, 0x443f, 0x6003, 0x0001, 0x6007,
+ 0x0003, 0x1078, 0x5c45, 0x0078, 0x7b7f, 0x1078, 0x7b81, 0x0d7f,
+ 0x007c, 0x2001, 0xa300, 0x2004, 0xa086, 0x0003, 0x0040, 0x7b8c,
+ 0x2001, 0x0007, 0x1078, 0x443f, 0x1078, 0x2839, 0x1078, 0x753d,
+ 0x007c, 0x0e7e, 0x2071, 0xa300, 0x707c, 0xa086, 0x0014, 0x00c0,
+ 0x7bcf, 0x7000, 0xa086, 0x0003, 0x00c0, 0x7ba4, 0x6010, 0xa005,
+ 0x00c0, 0x7ba4, 0x1078, 0x35f7, 0x0d7e, 0x6018, 0x2068, 0x1078,
+ 0x457d, 0x1078, 0x7b0b, 0x0d7f, 0x1078, 0x7dba, 0x00c0, 0x7bcf,
+ 0x0d7e, 0x6018, 0x2068, 0x6890, 0x0d7f, 0xa005, 0x0040, 0x7bcf,
+ 0x2001, 0x0006, 0x1078, 0x443f, 0x0e7e, 0x6010, 0xa005, 0x0040,
+ 0x7bc8, 0x2070, 0x7007, 0x0000, 0x7037, 0x0103, 0x7033, 0x0200,
+ 0x0e7f, 0x1078, 0x2839, 0x1078, 0x753d, 0x0078, 0x7bd3, 0x1078,
+ 0x7a05, 0x1078, 0x7b81, 0x0e7f, 0x007c, 0x2011, 0xa31f, 0x2204,
+ 0xa086, 0x0014, 0x00c0, 0x7be8, 0x2001, 0x0002, 0x1078, 0x443f,
+ 0x6003, 0x0001, 0x6007, 0x0001, 0x1078, 0x5c45, 0x0078, 0x7bea,
+ 0x1078, 0x7b81, 0x007c, 0x2011, 0xa31f, 0x2204, 0xa086, 0x0004,
+ 0x00c0, 0x7bfa, 0x2001, 0x0007, 0x1078, 0x443f, 0x1078, 0x753d,
+ 0x0078, 0x7bfc, 0x1078, 0x7b81, 0x007c, 0x7ad0, 0x7c11, 0x7ad0,
+ 0x7c4e, 0x7ad0, 0x7cc0, 0x7c09, 0x7ad0, 0x7ad0, 0x7cd5, 0x7ad0,
+ 0x7ce8, 0x6604, 0xa6b6, 0x001e, 0x00c0, 0x7c10, 0x1078, 0x753d,
+ 0x007c, 0x0d7e, 0x0c7e, 0x1078, 0x7cfb, 0x00c0, 0x7c27, 0x2001,
+ 0x0000, 0x1078, 0x442b, 0x2001, 0x0002, 0x1078, 0x443f, 0x6003,
+ 0x0001, 0x6007, 0x0002, 0x1078, 0x5c45, 0x0078, 0x7c4b, 0x2009,
+ 0xa88e, 0x2104, 0xa086, 0x0009, 0x00c0, 0x7c3c, 0x6018, 0x2068,
+ 0x6840, 0xa084, 0x00ff, 0xa005, 0x0040, 0x7c49, 0x8001, 0x6842,
+ 0x6017, 0x000a, 0x0078, 0x7c4b, 0x2009, 0xa88f, 0x2104, 0xa084,
+ 0xff00, 0xa086, 0x1900, 0x00c0, 0x7c49, 0x1078, 0x753d, 0x0078,
+ 0x7c4b, 0x1078, 0x7b81, 0x0c7f, 0x0d7f, 0x007c, 0x1078, 0x7d0a,
+ 0x00c0, 0x7c62, 0x2001, 0x0000, 0x1078, 0x442b, 0x2001, 0x0002,
+ 0x1078, 0x443f, 0x6003, 0x0001, 0x6007, 0x0002, 0x1078, 0x5c45,
+ 0x0078, 0x7c8e, 0x1078, 0x7a05, 0x2009, 0xa88e, 0x2134, 0xa6b4,
+ 0x00ff, 0xa686, 0x0005, 0x0040, 0x7c8f, 0xa686, 0x000b, 0x0040,
+ 0x7c8c, 0x2009, 0xa88f, 0x2104, 0xa084, 0xff00, 0x00c0, 0x7c7c,
+ 0xa686, 0x0009, 0x0040, 0x7c8f, 0xa086, 0x1900, 0x00c0, 0x7c8c,
+ 0xa686, 0x0009, 0x0040, 0x7c8f, 0x2001, 0x0004, 0x1078, 0x443f,
+ 0x1078, 0x753d, 0x0078, 0x7c8e, 0x1078, 0x7b81, 0x007c, 0x0d7e,
+ 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x7c9d, 0x6838, 0xd0fc,
+ 0x0040, 0x7c9d, 0x0d7f, 0x0078, 0x7c8c, 0x6018, 0x2068, 0x6840,
+ 0xa084, 0x00ff, 0xa005, 0x0040, 0x7cae, 0x8001, 0x6842, 0x6017,
+ 0x000a, 0x6007, 0x0016, 0x0d7f, 0x0078, 0x7c8e, 0x68a0, 0xa086,
+ 0x007e, 0x00c0, 0x7cbb, 0x0e7e, 0x2071, 0xa300, 0x1078, 0x41f5,
+ 0x0e7f, 0x0078, 0x7cbd, 0x1078, 0x2813, 0x0d7f, 0x0078, 0x7c8c,
+ 0x1078, 0x7d0a, 0x00c0, 0x7cd0, 0x2001, 0x0004, 0x1078, 0x443f,
+ 0x6003, 0x0001, 0x6007, 0x0003, 0x1078, 0x5c45, 0x0078, 0x7cd4,
+ 0x1078, 0x7a05, 0x1078, 0x7b81, 0x007c, 0x1078, 0x7d0a, 0x00c0,
+ 0x7ce5, 0x2001, 0x0008, 0x1078, 0x443f, 0x6003, 0x0001, 0x6007,
+ 0x0005, 0x1078, 0x5c45, 0x0078, 0x7ce7, 0x1078, 0x7b81, 0x007c,
+ 0x1078, 0x7d0a, 0x00c0, 0x7cf8, 0x2001, 0x000a, 0x1078, 0x443f,
+ 0x6003, 0x0001, 0x6007, 0x0001, 0x1078, 0x5c45, 0x0078, 0x7cfa,
+ 0x1078, 0x7b81, 0x007c, 0x2009, 0xa88e, 0x2104, 0xa086, 0x0003,
+ 0x00c0, 0x7d09, 0x2009, 0xa88f, 0x2104, 0xa084, 0xff00, 0xa086,
+ 0x2a00, 0x007c, 0xa085, 0x0001, 0x007c, 0x0c7e, 0x017e, 0xac88,
+ 0x0006, 0x2164, 0x1078, 0x4513, 0x017f, 0x0c7f, 0x007c, 0x0f7e,
+ 0x0e7e, 0x0d7e, 0x037e, 0x017e, 0x6018, 0x2068, 0x2071, 0xa332,
+ 0x2e04, 0xa085, 0x0003, 0x2072, 0x1078, 0x7d8b, 0x0040, 0x7d50,
+ 0x2001, 0xa352, 0x2004, 0xd0a4, 0x0040, 0x7d39, 0xa006, 0x2020,
+ 0x2009, 0x002a, 0x1078, 0x9ec0, 0x2001, 0xa30c, 0x200c, 0xc195,
+ 0x2102, 0x2019, 0x002a, 0x2009, 0x0001, 0x1078, 0x27e2, 0x2071,
+ 0xa300, 0x1078, 0x260d, 0x0c7e, 0x157e, 0x20a9, 0x0081, 0x2009,
+ 0x007f, 0x1078, 0x2921, 0x8108, 0x00f0, 0x7d49, 0x157f, 0x0c7f,
+ 0x1078, 0x7d0d, 0x6813, 0x00ff, 0x6817, 0xfffe, 0x2071, 0xa880,
+ 0x2079, 0x0100, 0x2e04, 0xa084, 0x00ff, 0x2069, 0xa31a, 0x206a,
+ 0x78e6, 0x007e, 0x8e70, 0x2e04, 0x2069, 0xa31b, 0x206a, 0x78ea,
+ 0xa084, 0xff00, 0x017f, 0xa105, 0x2009, 0xa325, 0x200a, 0x2069,
+ 0xa88e, 0x2071, 0xa59c, 0x6810, 0x2072, 0x6814, 0x7006, 0x6818,
+ 0x700a, 0x681c, 0x700e, 0x1078, 0x8da9, 0x2001, 0x0006, 0x1078,
+ 0x443f, 0x1078, 0x2839, 0x1078, 0x753d, 0x017f, 0x037f, 0x0d7f,
+ 0x0e7f, 0x0f7f, 0x007c, 0x027e, 0x037e, 0x0e7e, 0x157e, 0x2019,
+ 0xa325, 0x231c, 0x83ff, 0x0040, 0x7db5, 0x2071, 0xa880, 0x2e14,
+ 0xa294, 0x00ff, 0x7004, 0xa084, 0xff00, 0xa205, 0xa306, 0x00c0,
+ 0x7db5, 0x2011, 0xa896, 0xad98, 0x000a, 0x20a9, 0x0004, 0x1078,
+ 0x7e55, 0x00c0, 0x7db5, 0x2011, 0xa89a, 0xad98, 0x0006, 0x20a9,
+ 0x0004, 0x1078, 0x7e55, 0x00c0, 0x7db5, 0x157f, 0x0e7f, 0x037f,
+ 0x027f, 0x007c, 0x0e7e, 0x2071, 0xa88c, 0x7004, 0xa086, 0x0014,
+ 0x00c0, 0x7ddd, 0x7008, 0xa086, 0x0800, 0x00c0, 0x7ddd, 0x700c,
+ 0xd0ec, 0x0040, 0x7ddb, 0xa084, 0x0f00, 0xa086, 0x0100, 0x00c0,
+ 0x7ddb, 0x7024, 0xd0a4, 0x00c0, 0x7dd8, 0xd0ac, 0x0040, 0x7ddb,
+ 0xa006, 0x0078, 0x7ddd, 0xa085, 0x0001, 0x0e7f, 0x007c, 0x0e7e,
+ 0x0d7e, 0x0c7e, 0x077e, 0x057e, 0x047e, 0x027e, 0x007e, 0x127e,
+ 0x2091, 0x8000, 0x2029, 0xa5b4, 0x252c, 0x2021, 0xa5ba, 0x2424,
+ 0x2061, 0xaa00, 0x2071, 0xa300, 0x7244, 0x7060, 0xa202, 0x00c8,
+ 0x7e43, 0x1078, 0x9ee5, 0x0040, 0x7e3b, 0x671c, 0xa786, 0x0001,
+ 0x0040, 0x7e3b, 0xa786, 0x0007, 0x0040, 0x7e3b, 0x2500, 0xac06,
+ 0x0040, 0x7e3b, 0x2400, 0xac06, 0x0040, 0x7e3b, 0x0c7e, 0x6000,
+ 0xa086, 0x0004, 0x00c0, 0x7e16, 0x1078, 0x1749, 0xa786, 0x0008,
+ 0x00c0, 0x7e25, 0x1078, 0x8c3b, 0x00c0, 0x7e25, 0x0c7f, 0x1078,
+ 0x7a05, 0x1078, 0x8c01, 0x0078, 0x7e3b, 0x6010, 0x2068, 0x1078,
+ 0x8a44, 0x0040, 0x7e38, 0xa786, 0x0003, 0x00c0, 0x7e4d, 0x6837,
+ 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0x4982, 0x1078, 0x8bf4,
+ 0x1078, 0x8c01, 0x0c7f, 0xace0, 0x0010, 0x7054, 0xac02, 0x00c8,
+ 0x7e43, 0x0078, 0x7df4, 0x127f, 0x007f, 0x027f, 0x047f, 0x057f,
+ 0x077f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0xa786, 0x0006, 0x00c0,
+ 0x7e2f, 0x1078, 0x9e70, 0x0078, 0x7e38, 0x220c, 0x2304, 0xa106,
+ 0x00c0, 0x7e60, 0x8210, 0x8318, 0x00f0, 0x7e55, 0xa006, 0x007c,
+ 0x2304, 0xa102, 0x0048, 0x7e68, 0x2001, 0x0001, 0x0078, 0x7e6a,
+ 0x2001, 0x0000, 0xa18d, 0x0001, 0x007c, 0x6004, 0xa08a, 0x0044,
+ 0x10c8, 0x1328, 0x1078, 0x8c27, 0x0040, 0x7e7c, 0x1078, 0x8c3b,
+ 0x0040, 0x7e89, 0x0078, 0x7e82, 0x1078, 0x2839, 0x1078, 0x8c3b,
+ 0x0040, 0x7e89, 0x1078, 0x6010, 0x1078, 0x753d, 0x1078, 0x6109,
+ 0x007c, 0x1078, 0x7a05, 0x0078, 0x7e82, 0xa182, 0x0040, 0x0079,
+ 0x7e91, 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea4,
+ 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea6, 0x7ea6, 0x7ea6, 0x7ea6,
+ 0x7ea4, 0x7ea4, 0x7ea4, 0x7ea6, 0x1078, 0x1328, 0x600b, 0xffff,
+ 0x6003, 0x0001, 0x6106, 0x1078, 0x5bf8, 0x127e, 0x2091, 0x8000,
+ 0x1078, 0x6109, 0x127f, 0x007c, 0xa186, 0x0013, 0x00c0, 0x7ebd,
+ 0x6004, 0xa082, 0x0040, 0x0079, 0x7f48, 0xa186, 0x0027, 0x00c0,
+ 0x7edf, 0x1078, 0x6010, 0x1078, 0x2813, 0x0d7e, 0x6110, 0x2168,
+ 0x1078, 0x8a44, 0x0040, 0x7ed9, 0x6837, 0x0103, 0x684b, 0x0029,
+ 0x6847, 0x0000, 0x694c, 0xc1c5, 0x694e, 0x1078, 0x4982, 0x1078,
+ 0x8bf4, 0x0d7f, 0x1078, 0x753d, 0x1078, 0x6109, 0x007c, 0xa186,
+ 0x0014, 0x00c0, 0x7ee8, 0x6004, 0xa082, 0x0040, 0x0079, 0x7f10,
+ 0xa186, 0x0046, 0x0040, 0x7ef4, 0xa186, 0x0045, 0x0040, 0x7ef4,
+ 0xa186, 0x0047, 0x10c0, 0x1328, 0x2001, 0x0109, 0x2004, 0xd084,
+ 0x0040, 0x7f0d, 0x127e, 0x2091, 0x2200, 0x007e, 0x017e, 0x027e,
+ 0x1078, 0x5ad2, 0x027f, 0x017f, 0x007f, 0x127f, 0x6000, 0xa086,
+ 0x0002, 0x00c0, 0x7f0d, 0x0078, 0x7f81, 0x1078, 0x7583, 0x007c,
+ 0x7f25, 0x7f23, 0x7f23, 0x7f23, 0x7f23, 0x7f23, 0x7f23, 0x7f23,
+ 0x7f23, 0x7f23, 0x7f23, 0x7f41, 0x7f41, 0x7f41, 0x7f41, 0x7f23,
+ 0x7f41, 0x7f23, 0x7f41, 0x1078, 0x1328, 0x1078, 0x6010, 0x0d7e,
+ 0x6110, 0x2168, 0x1078, 0x8a44, 0x0040, 0x7f3b, 0x6837, 0x0103,
+ 0x684b, 0x0006, 0x6847, 0x0000, 0x6850, 0xc0ec, 0x6852, 0x1078,
+ 0x4982, 0x1078, 0x8bf4, 0x0d7f, 0x1078, 0x753d, 0x1078, 0x6109,
+ 0x007c, 0x1078, 0x6010, 0x1078, 0x753d, 0x1078, 0x6109, 0x007c,
+ 0x7f5d, 0x7f5b, 0x7f5b, 0x7f5b, 0x7f5b, 0x7f5b, 0x7f5b, 0x7f5b,
+ 0x7f5b, 0x7f5b, 0x7f5b, 0x7f6f, 0x7f6f, 0x7f6f, 0x7f6f, 0x7f5b,
+ 0x7f7a, 0x7f5b, 0x7f6f, 0x1078, 0x1328, 0x1078, 0x6010, 0x2001,
+ 0xa5a2, 0x2004, 0x603e, 0x6003, 0x0002, 0x1078, 0x6109, 0x6010,
+ 0xa088, 0x0013, 0x2104, 0xa085, 0x0400, 0x200a, 0x007c, 0x1078,
+ 0x6010, 0x2001, 0xa5a2, 0x2004, 0x603e, 0x6003, 0x000f, 0x1078,
+ 0x6109, 0x007c, 0x1078, 0x6010, 0x1078, 0x753d, 0x1078, 0x6109,
+ 0x007c, 0xa182, 0x0040, 0x0079, 0x7f85, 0x7f98, 0x7f98, 0x7f98,
+ 0x7f98, 0x7f98, 0x7f9a, 0x8095, 0x80b7, 0x7f98, 0x7f98, 0x7f98,
+ 0x7f98, 0x7f98, 0x7f98, 0x7f98, 0x7f98, 0x7f98, 0x7f98, 0x7f98,
+ 0x1078, 0x1328, 0x0e7e, 0x0d7e, 0x603f, 0x0000, 0x2071, 0xa880,
+ 0x7124, 0x610a, 0x2071, 0xa88c, 0x6110, 0x2168, 0x7614, 0xa6b4,
+ 0x0fff, 0x86ff, 0x0040, 0x8058, 0xa68c, 0x0c00, 0x0040, 0x7fd1,
+ 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0f7f, 0x0040, 0x7fcd, 0x684c,
+ 0xd0ac, 0x0040, 0x7fcd, 0x6024, 0xd0dc, 0x00c0, 0x7fcd, 0x6850,
+ 0xd0bc, 0x00c0, 0x7fcd, 0x7318, 0x6814, 0xa306, 0x00c0, 0x806f,
+ 0x731c, 0x6810, 0xa306, 0x00c0, 0x806f, 0x7318, 0x6b62, 0x731c,
+ 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002, 0x0040, 0x8004, 0xa186,
+ 0x0028, 0x00c0, 0x7fe1, 0x1078, 0x8c15, 0x684b, 0x001c, 0x0078,
+ 0x8006, 0xd6dc, 0x0040, 0x7ffd, 0x684b, 0x0015, 0x684c, 0xd0ac,
+ 0x0040, 0x7ffb, 0x6914, 0x6a10, 0x2100, 0xa205, 0x0040, 0x7ffb,
+ 0x7018, 0xa106, 0x00c0, 0x7ff8, 0x701c, 0xa206, 0x0040, 0x7ffb,
+ 0x6962, 0x6a5e, 0xc6dc, 0x0078, 0x8006, 0xd6d4, 0x0040, 0x8004,
+ 0x684b, 0x0007, 0x0078, 0x8006, 0x684b, 0x0000, 0x6837, 0x0103,
+ 0x6e46, 0xa01e, 0xd6c4, 0x0040, 0x802f, 0xa686, 0x0100, 0x00c0,
+ 0x801a, 0x2001, 0xa899, 0x2004, 0xa005, 0x00c0, 0x801a, 0xc6c4,
+ 0x0078, 0x7fa9, 0x7328, 0x732c, 0x6b56, 0x83ff, 0x0040, 0x802f,
+ 0xa38a, 0x0009, 0x0048, 0x8026, 0x2019, 0x0008, 0x037e, 0x2308,
+ 0x2019, 0xa898, 0xad90, 0x0019, 0x1078, 0x8739, 0x037f, 0xd6cc,
+ 0x0040, 0x8085, 0x7124, 0x695a, 0x81ff, 0x0040, 0x8085, 0xa192,
+ 0x0021, 0x00c8, 0x8046, 0x2071, 0xa898, 0x831c, 0x2300, 0xae18,
+ 0xad90, 0x001d, 0x1078, 0x8739, 0x0078, 0x8085, 0x6838, 0xd0fc,
+ 0x0040, 0x804f, 0x2009, 0x0020, 0x695a, 0x0078, 0x803b, 0x0f7e,
+ 0x2d78, 0x1078, 0x86d1, 0x0f7f, 0x1078, 0x8726, 0x0078, 0x8087,
+ 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0f7f, 0x0040, 0x8075, 0x684c,
+ 0xd0ac, 0x0040, 0x8075, 0x6024, 0xd0dc, 0x00c0, 0x8075, 0x6850,
+ 0xd0bc, 0x00c0, 0x8075, 0x684c, 0xd0f4, 0x00c0, 0x8075, 0x1078,
+ 0x8cfa, 0x0d7f, 0x0e7f, 0x0078, 0x8094, 0x684b, 0x0000, 0x6837,
+ 0x0103, 0x6e46, 0x684c, 0xd0ac, 0x0040, 0x8085, 0x6810, 0x6914,
+ 0xa115, 0x0040, 0x8085, 0x1078, 0x8233, 0x1078, 0x4982, 0x6218,
+ 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x1078, 0x8cc4, 0x0d7f, 0x0e7f,
+ 0x00c0, 0x8094, 0x1078, 0x753d, 0x007c, 0x0f7e, 0x6003, 0x0003,
+ 0x2079, 0xa88c, 0x7c04, 0x7b00, 0x7e0c, 0x7d08, 0x6010, 0x2078,
+ 0x784c, 0xd0ac, 0x0040, 0x80a8, 0x6003, 0x0002, 0x0f7f, 0x007c,
+ 0x7c12, 0x7b16, 0x7e0a, 0x7d0e, 0x0f7f, 0x603f, 0x0000, 0x2c10,
+ 0x1078, 0x1cab, 0x1078, 0x5c64, 0x1078, 0x61d3, 0x007c, 0x2001,
+ 0xa5a2, 0x2004, 0x603e, 0x6003, 0x0004, 0x6110, 0x20e1, 0x0005,
+ 0x3d18, 0x3e20, 0x2c10, 0x1078, 0x15ec, 0x007c, 0xa182, 0x0040,
+ 0x0079, 0x80ca, 0x80dd, 0x80dd, 0x80dd, 0x80dd, 0x80dd, 0x80df,
+ 0x8182, 0x80dd, 0x80dd, 0x8198, 0x8209, 0x80dd, 0x80dd, 0x80dd,
+ 0x80dd, 0x8218, 0x80dd, 0x80dd, 0x80dd, 0x1078, 0x1328, 0x077e,
+ 0x0f7e, 0x0e7e, 0x0d7e, 0x2071, 0xa88c, 0x6110, 0x2178, 0x7614,
+ 0xa6b4, 0x0fff, 0x7e46, 0x7f4c, 0xc7e5, 0x7f4e, 0x6218, 0x2268,
+ 0x6a3c, 0x8211, 0x6a3e, 0x86ff, 0x0040, 0x817d, 0xa694, 0xff00,
+ 0xa284, 0x0c00, 0x0040, 0x8100, 0x7018, 0x7862, 0x701c, 0x785e,
+ 0xa284, 0x0300, 0x0040, 0x817d, 0x1078, 0x1381, 0x1040, 0x1328,
+ 0x2d00, 0x784a, 0x7f4c, 0xc7cd, 0x7f4e, 0x6837, 0x0103, 0x7838,
+ 0x683a, 0x783c, 0x683e, 0x7840, 0x6842, 0x6e46, 0xa68c, 0x0c00,
+ 0x0040, 0x811e, 0x7318, 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff,
+ 0xa186, 0x0002, 0x0040, 0x813a, 0xa186, 0x0028, 0x00c0, 0x812c,
+ 0x684b, 0x001c, 0x0078, 0x813c, 0xd6dc, 0x0040, 0x8133, 0x684b,
+ 0x0015, 0x0078, 0x813c, 0xd6d4, 0x0040, 0x813a, 0x684b, 0x0007,
+ 0x0078, 0x813c, 0x684b, 0x0000, 0x6f4e, 0x7850, 0x6852, 0x7854,
+ 0x6856, 0xa01e, 0xd6c4, 0x0040, 0x815a, 0x7328, 0x732c, 0x6b56,
+ 0x83ff, 0x0040, 0x815a, 0xa38a, 0x0009, 0x0048, 0x8151, 0x2019,
+ 0x0008, 0x037e, 0x2308, 0x2019, 0xa898, 0xad90, 0x0019, 0x1078,
+ 0x8739, 0x037f, 0xd6cc, 0x0040, 0x817d, 0x7124, 0x695a, 0x81ff,
+ 0x0040, 0x817d, 0xa192, 0x0021, 0x00c8, 0x8171, 0x2071, 0xa898,
+ 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x1078, 0x8739, 0x0078,
+ 0x817d, 0x7838, 0xd0fc, 0x0040, 0x817a, 0x2009, 0x0020, 0x695a,
+ 0x0078, 0x8166, 0x2d78, 0x1078, 0x86d1, 0x0d7f, 0x0e7f, 0x0f7f,
+ 0x077f, 0x007c, 0x0f7e, 0x6003, 0x0003, 0x2079, 0xa88c, 0x7c04,
+ 0x7b00, 0x7e0c, 0x7d08, 0x6010, 0x2078, 0x7c12, 0x7b16, 0x7e0a,
+ 0x7d0e, 0x0f7f, 0x2c10, 0x1078, 0x1cab, 0x1078, 0x6c26, 0x007c,
+ 0x0d7e, 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0f7f, 0x0040, 0x81a4,
+ 0x2001, 0xa5a2, 0x2004, 0x603e, 0x6003, 0x0002, 0x1078, 0x60b8,
+ 0x1078, 0x61d3, 0x6110, 0x2168, 0x694c, 0xd1e4, 0x0040, 0x8207,
+ 0xd1cc, 0x0040, 0x81de, 0x6948, 0x6838, 0xd0fc, 0x0040, 0x81d6,
+ 0x017e, 0x684c, 0x007e, 0x6850, 0x007e, 0xad90, 0x000d, 0xa198,
+ 0x000d, 0x2009, 0x0020, 0x157e, 0x21a8, 0x2304, 0x2012, 0x8318,
+ 0x8210, 0x00f0, 0x81c5, 0x157f, 0x007f, 0x6852, 0x007f, 0x684e,
+ 0x017f, 0x2168, 0x1078, 0x13aa, 0x0078, 0x8201, 0x017e, 0x1078,
+ 0x13aa, 0x0d7f, 0x1078, 0x8726, 0x0078, 0x8201, 0x6837, 0x0103,
+ 0x6944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x0040, 0x81fd, 0xa086,
+ 0x0028, 0x00c0, 0x81ef, 0x684b, 0x001c, 0x0078, 0x81ff, 0xd1dc,
+ 0x0040, 0x81f6, 0x684b, 0x0015, 0x0078, 0x81ff, 0xd1d4, 0x0040,
+ 0x81fd, 0x684b, 0x0007, 0x0078, 0x81ff, 0x684b, 0x0000, 0x1078,
+ 0x4982, 0x1078, 0x8cc4, 0x00c0, 0x8207, 0x1078, 0x753d, 0x0d7f,
+ 0x007c, 0x2019, 0x0001, 0x1078, 0x6e6c, 0x6003, 0x0002, 0x2001,
+ 0xa5a2, 0x2004, 0x603e, 0x1078, 0x60b8, 0x1078, 0x61d3, 0x007c,
+ 0x1078, 0x60b8, 0x1078, 0x2813, 0x0d7e, 0x6110, 0x2168, 0x1078,
+ 0x8a44, 0x0040, 0x822d, 0x6837, 0x0103, 0x684b, 0x0029, 0x6847,
+ 0x0000, 0x1078, 0x4982, 0x1078, 0x8bf4, 0x0d7f, 0x1078, 0x753d,
+ 0x1078, 0x61d3, 0x007c, 0x684b, 0x0015, 0xd1fc, 0x0040, 0x823f,
+ 0x684b, 0x0007, 0x8002, 0x8000, 0x810a, 0xa189, 0x0000, 0x6962,
+ 0x685e, 0x007c, 0xa182, 0x0040, 0x0079, 0x8246, 0x8259, 0x8259,
+ 0x8259, 0x8259, 0x8259, 0x825b, 0x8259, 0x8333, 0x833f, 0x8259,
+ 0x8259, 0x8259, 0x8259, 0x8259, 0x8259, 0x8259, 0x8259, 0x8259,
+ 0x8259, 0x1078, 0x1328, 0x077e, 0x0f7e, 0x0e7e, 0x0d7e, 0x2071,
+ 0xa88c, 0x6110, 0x2178, 0x7614, 0xa6b4, 0x0fff, 0x0f7e, 0x2c78,
+ 0x1078, 0x4893, 0x0f7f, 0x0040, 0x827e, 0xa684, 0x00ff, 0x00c0,
+ 0x827e, 0x6024, 0xd0f4, 0x00c0, 0x827a, 0x7808, 0xa086, 0x0000,
+ 0x00c0, 0x827e, 0x1078, 0x8cfa, 0x0078, 0x832e, 0x7e46, 0x7f4c,
+ 0xc7e5, 0x7f4e, 0x6218, 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x86ff,
+ 0x0040, 0x8323, 0xa694, 0xff00, 0xa284, 0x0c00, 0x0040, 0x8294,
+ 0x7018, 0x7862, 0x701c, 0x785e, 0xa284, 0x0300, 0x0040, 0x8320,
+ 0xa686, 0x0100, 0x00c0, 0x82a6, 0x2001, 0xa899, 0x2004, 0xa005,
+ 0x00c0, 0x82a6, 0xc6c4, 0x7e46, 0x0078, 0x8287, 0x1078, 0x1381,
+ 0x1040, 0x1328, 0x2d00, 0x784a, 0x7f4c, 0xa7bd, 0x0200, 0x7f4e,
+ 0x6837, 0x0103, 0x7838, 0x683a, 0x783c, 0x683e, 0x7840, 0x6842,
+ 0x6e46, 0xa68c, 0x0c00, 0x0040, 0x82c1, 0x7318, 0x6b62, 0x731c,
+ 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002, 0x0040, 0x82dd, 0xa186,
+ 0x0028, 0x00c0, 0x82cf, 0x684b, 0x001c, 0x0078, 0x82df, 0xd6dc,
+ 0x0040, 0x82d6, 0x684b, 0x0015, 0x0078, 0x82df, 0xd6d4, 0x0040,
+ 0x82dd, 0x684b, 0x0007, 0x0078, 0x82df, 0x684b, 0x0000, 0x6f4e,
+ 0x7850, 0x6852, 0x7854, 0x6856, 0xa01e, 0xd6c4, 0x0040, 0x82fd,
+ 0x7328, 0x732c, 0x6b56, 0x83ff, 0x0040, 0x82fd, 0xa38a, 0x0009,
+ 0x0048, 0x82f4, 0x2019, 0x0008, 0x037e, 0x2308, 0x2019, 0xa898,
+ 0xad90, 0x0019, 0x1078, 0x8739, 0x037f, 0xd6cc, 0x0040, 0x8320,
+ 0x7124, 0x695a, 0x81ff, 0x0040, 0x8320, 0xa192, 0x0021, 0x00c8,
+ 0x8314, 0x2071, 0xa898, 0x831c, 0x2300, 0xae18, 0xad90, 0x001d,
+ 0x1078, 0x8739, 0x0078, 0x8320, 0x7838, 0xd0fc, 0x0040, 0x831d,
+ 0x2009, 0x0020, 0x695a, 0x0078, 0x8309, 0x2d78, 0x1078, 0x86d1,
+ 0xd6dc, 0x00c0, 0x8326, 0xa006, 0x0078, 0x832c, 0x2001, 0x0001,
+ 0x2071, 0xa88c, 0x7218, 0x731c, 0x1078, 0x1645, 0x0d7f, 0x0e7f,
+ 0x0f7f, 0x077f, 0x007c, 0x2001, 0xa5a2, 0x2004, 0x603e, 0x20e1,
+ 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x1078, 0x15ec, 0x007c, 0x2001,
+ 0xa5a2, 0x2004, 0x603e, 0x0d7e, 0x6003, 0x0002, 0x6110, 0x2168,
+ 0x694c, 0xd1e4, 0x0040, 0x846b, 0x603f, 0x0000, 0x0f7e, 0x2c78,
+ 0x1078, 0x4893, 0x0f7f, 0x0040, 0x8385, 0x6814, 0x6910, 0xa115,
+ 0x0040, 0x8385, 0x6a60, 0xa206, 0x00c0, 0x8362, 0x685c, 0xa106,
+ 0x0040, 0x8385, 0x684c, 0xc0e4, 0x684e, 0x6847, 0x0000, 0x6863,
+ 0x0000, 0x685f, 0x0000, 0x6024, 0xd0f4, 0x00c0, 0x837a, 0x697c,
+ 0x6810, 0xa102, 0x603a, 0x6980, 0x6814, 0xa103, 0x6036, 0x6024,
+ 0xc0f5, 0x6026, 0x0d7e, 0x6018, 0x2068, 0x683c, 0x8000, 0x683e,
+ 0x0d7f, 0x1078, 0x8cfa, 0x0078, 0x846b, 0x694c, 0xd1cc, 0x0040,
+ 0x8430, 0x6948, 0x6838, 0xd0fc, 0x0040, 0x83ea, 0x017e, 0x684c,
+ 0x007e, 0x6850, 0x007e, 0x0f7e, 0x2178, 0x7944, 0xa184, 0x00ff,
+ 0xa0b6, 0x0002, 0x0040, 0x83bf, 0xa086, 0x0028, 0x00c0, 0x83a6,
+ 0x684b, 0x001c, 0x784b, 0x001c, 0x0078, 0x83ca, 0xd1dc, 0x0040,
+ 0x83b6, 0x684b, 0x0015, 0x784b, 0x0015, 0x1078, 0x8ea5, 0x0040,
+ 0x83b4, 0x7944, 0xc1dc, 0x7946, 0x0078, 0x83ca, 0xd1d4, 0x0040,
+ 0x83bf, 0x684b, 0x0007, 0x784b, 0x0007, 0x0078, 0x83ca, 0x684c,
+ 0xd0ac, 0x0040, 0x83ca, 0x6810, 0x6914, 0xa115, 0x0040, 0x83ca,
+ 0x1078, 0x8233, 0x6848, 0x784a, 0x6860, 0x7862, 0x685c, 0x785e,
+ 0xad90, 0x000d, 0xaf98, 0x000d, 0x2009, 0x0020, 0x157e, 0x21a8,
+ 0x2304, 0x2012, 0x8318, 0x8210, 0x00f0, 0x83d8, 0x157f, 0x0f7f,
+ 0x007f, 0x6852, 0x007f, 0x684e, 0x017f, 0x2168, 0x1078, 0x13aa,
+ 0x0078, 0x8465, 0x017e, 0x0f7e, 0x2178, 0x7944, 0xa184, 0x00ff,
+ 0xa0b6, 0x0002, 0x0040, 0x8417, 0xa086, 0x0028, 0x00c0, 0x83fe,
+ 0x684b, 0x001c, 0x784b, 0x001c, 0x0078, 0x8422, 0xd1dc, 0x0040,
+ 0x840e, 0x684b, 0x0015, 0x784b, 0x0015, 0x1078, 0x8ea5, 0x0040,
+ 0x840c, 0x7944, 0xc1dc, 0x7946, 0x0078, 0x8422, 0xd1d4, 0x0040,
+ 0x8417, 0x684b, 0x0007, 0x784b, 0x0007, 0x0078, 0x8422, 0x684c,
+ 0xd0ac, 0x0040, 0x8422, 0x6810, 0x6914, 0xa115, 0x0040, 0x8422,
+ 0x1078, 0x8233, 0x6860, 0x7862, 0x685c, 0x785e, 0x684c, 0x784e,
+ 0x0f7f, 0x1078, 0x13aa, 0x0d7f, 0x1078, 0x8726, 0x0078, 0x8465,
+ 0x6837, 0x0103, 0x6944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x0040,
+ 0x8456, 0xa086, 0x0028, 0x00c0, 0x8441, 0x684b, 0x001c, 0x0078,
+ 0x8463, 0xd1dc, 0x0040, 0x844f, 0x684b, 0x0015, 0x1078, 0x8ea5,
+ 0x0040, 0x844d, 0x6944, 0xc1dc, 0x6946, 0x0078, 0x8463, 0xd1d4,
+ 0x0040, 0x8456, 0x684b, 0x0007, 0x0078, 0x8463, 0x684b, 0x0000,
+ 0x684c, 0xd0ac, 0x0040, 0x8463, 0x6810, 0x6914, 0xa115, 0x0040,
+ 0x8463, 0x1078, 0x8233, 0x1078, 0x4982, 0x1078, 0x8cc4, 0x00c0,
+ 0x846b, 0x1078, 0x753d, 0x0d7f, 0x007c, 0x1078, 0x6010, 0x0078,
+ 0x8473, 0x1078, 0x60b8, 0x1078, 0x8a44, 0x0040, 0x8492, 0x0d7e,
+ 0x6110, 0x2168, 0x6837, 0x0103, 0x2009, 0xa30c, 0x210c, 0xd18c,
+ 0x00c0, 0x849d, 0xd184, 0x00c0, 0x8499, 0x6108, 0x694a, 0xa18e,
+ 0x0029, 0x00c0, 0x848d, 0x1078, 0xa181, 0x6847, 0x0000, 0x1078,
+ 0x4982, 0x0d7f, 0x1078, 0x753d, 0x1078, 0x6109, 0x1078, 0x61d3,
+ 0x007c, 0x684b, 0x0004, 0x0078, 0x848d, 0x684b, 0x0004, 0x0078,
+ 0x848d, 0xa182, 0x0040, 0x0079, 0x84a5, 0x84b8, 0x84b8, 0x84b8,
+ 0x84b8, 0x84b8, 0x84ba, 0x84b8, 0x84bd, 0x84b8, 0x84b8, 0x84b8,
+ 0x84b8, 0x84b8, 0x84b8, 0x84b8, 0x84b8, 0x84b8, 0x84b8, 0x84b8,
+ 0x1078, 0x1328, 0x1078, 0x753d, 0x007c, 0x007e, 0x027e, 0xa016,
+ 0x1078, 0x15ec, 0x027f, 0x007f, 0x007c, 0xa182, 0x0085, 0x0079,
+ 0x84c9, 0x84d2, 0x84d0, 0x84d0, 0x84de, 0x84d0, 0x84d0, 0x84d0,
+ 0x1078, 0x1328, 0x6003, 0x0001, 0x6106, 0x1078, 0x5bf8, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x6109, 0x127f, 0x007c, 0x027e, 0x057e,
+ 0x0d7e, 0x0e7e, 0x2071, 0xa880, 0x7224, 0x6212, 0x7220, 0x1078,
+ 0x8a30, 0x0040, 0x8503, 0x2268, 0x6800, 0xa086, 0x0000, 0x0040,
+ 0x8503, 0x6018, 0x6d18, 0xa52e, 0x00c0, 0x8503, 0x0c7e, 0x2d60,
+ 0x1078, 0x874a, 0x0c7f, 0x0040, 0x8503, 0x6803, 0x0002, 0x6007,
+ 0x0086, 0x0078, 0x8505, 0x6007, 0x0087, 0x6003, 0x0001, 0x1078,
+ 0x5bf8, 0x1078, 0x6109, 0x0f7e, 0x2278, 0x1078, 0x4893, 0x0f7f,
+ 0x0040, 0x851d, 0x6824, 0xd0ec, 0x0040, 0x851d, 0x0c7e, 0x2260,
+ 0x603f, 0x0000, 0x1078, 0x8cfa, 0x0c7f, 0x0e7f, 0x0d7f, 0x057f,
+ 0x027f, 0x007c, 0xa186, 0x0013, 0x00c0, 0x8533, 0x6004, 0xa08a,
+ 0x0085, 0x1048, 0x1328, 0xa08a, 0x008c, 0x10c8, 0x1328, 0xa082,
+ 0x0085, 0x0079, 0x8542, 0xa186, 0x0027, 0x0040, 0x853b, 0xa186,
+ 0x0014, 0x10c0, 0x1328, 0x1078, 0x6010, 0x1078, 0x8c01, 0x1078,
+ 0x6109, 0x007c, 0x8549, 0x854b, 0x854b, 0x8549, 0x8549, 0x8549,
+ 0x8549, 0x1078, 0x1328, 0x1078, 0x6010, 0x1078, 0x8c01, 0x1078,
+ 0x6109, 0x007c, 0xa186, 0x0013, 0x00c0, 0x855c, 0x6004, 0xa082,
+ 0x0085, 0x2008, 0x0078, 0x8597, 0xa186, 0x0027, 0x00c0, 0x857f,
+ 0x1078, 0x6010, 0x1078, 0x2813, 0x0d7e, 0x6010, 0x2068, 0x1078,
+ 0x8a44, 0x0040, 0x8575, 0x6837, 0x0103, 0x6847, 0x0000, 0x684b,
+ 0x0029, 0x1078, 0x4982, 0x1078, 0x8bf4, 0x0d7f, 0x1078, 0x753d,
+ 0x1078, 0x6109, 0x007c, 0x1078, 0x7583, 0x0078, 0x857a, 0xa186,
+ 0x0014, 0x00c0, 0x857b, 0x1078, 0x6010, 0x0d7e, 0x6010, 0x2068,
+ 0x1078, 0x8a44, 0x0040, 0x8575, 0x6837, 0x0103, 0x6847, 0x0000,
+ 0x684b, 0x0006, 0x6850, 0xc0ec, 0x6852, 0x0078, 0x8571, 0x0079,
+ 0x8599, 0x85a2, 0x85a0, 0x85a0, 0x85a0, 0x85a0, 0x85a0, 0x85bd,
+ 0x1078, 0x1328, 0x1078, 0x6010, 0x6030, 0xa08c, 0xff00, 0x810f,
+ 0xa186, 0x0039, 0x0040, 0x85b0, 0xa186, 0x0035, 0x00c0, 0x85b4,
+ 0x2001, 0xa5a0, 0x0078, 0x85b6, 0x2001, 0xa5a1, 0x2004, 0x6016,
+ 0x6003, 0x000c, 0x1078, 0x6109, 0x007c, 0x1078, 0x6010, 0x6030,
+ 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0040, 0x85cb, 0xa186,
+ 0x0035, 0x00c0, 0x85cf, 0x2001, 0xa5a0, 0x0078, 0x85d1, 0x2001,
+ 0xa5a1, 0x2004, 0x6016, 0x6003, 0x000e, 0x1078, 0x6109, 0x007c,
+ 0xa182, 0x008c, 0x00c8, 0x85e2, 0xa182, 0x0085, 0x0048, 0x85e2,
+ 0x0079, 0x85e5, 0x1078, 0x7583, 0x007c, 0x85ec, 0x85ec, 0x85ec,
+ 0x85ec, 0x85ee, 0x8643, 0x85ec, 0x1078, 0x1328, 0x0f7e, 0x2c78,
+ 0x1078, 0x4893, 0x0f7f, 0x0040, 0x8601, 0x6030, 0xa08c, 0xff00,
+ 0x810f, 0xa186, 0x0039, 0x0040, 0x865a, 0xa186, 0x0035, 0x0040,
+ 0x865a, 0x0d7e, 0x1078, 0x8bf4, 0x1078, 0x8a44, 0x0040, 0x8625,
+ 0x6010, 0x2068, 0x6837, 0x0103, 0x6850, 0xd0b4, 0x0040, 0x8616,
+ 0x684b, 0x0006, 0xc0ec, 0x6852, 0x0078, 0x8621, 0xd0bc, 0x0040,
+ 0x861d, 0x684b, 0x0002, 0x0078, 0x8621, 0x684b, 0x0005, 0x1078,
+ 0x8cc0, 0x6847, 0x0000, 0x1078, 0x4982, 0x2c68, 0x1078, 0x74d7,
+ 0x0040, 0x863e, 0x6003, 0x0001, 0x6007, 0x001e, 0x2009, 0xa88e,
+ 0x210c, 0x6136, 0x2009, 0xa88f, 0x210c, 0x613a, 0x6918, 0x611a,
+ 0x6920, 0x6122, 0x601f, 0x0001, 0x1078, 0x5bf8, 0x2d60, 0x1078,
+ 0x753d, 0x0d7f, 0x007c, 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0f7f,
+ 0x0040, 0x8680, 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0035,
+ 0x0040, 0x865a, 0xa186, 0x001e, 0x0040, 0x865a, 0xa186, 0x0039,
+ 0x00c0, 0x8680, 0x0d7e, 0x2c68, 0x1078, 0x8ef5, 0x00c0, 0x86a4,
+ 0x1078, 0x74d7, 0x0040, 0x867d, 0x6106, 0x6003, 0x0001, 0x601f,
+ 0x0001, 0x6918, 0x611a, 0x6928, 0x612a, 0x692c, 0x612e, 0x6930,
+ 0xa18c, 0x00ff, 0x6132, 0x6934, 0x6136, 0x6938, 0x613a, 0x6920,
+ 0x6122, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x2d60, 0x0078, 0x86a4,
+ 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x86a4, 0x6837,
+ 0x0103, 0x6850, 0xd0b4, 0x0040, 0x8693, 0xc0ec, 0x6852, 0x684b,
+ 0x0006, 0x0078, 0x869e, 0xd0bc, 0x0040, 0x869a, 0x684b, 0x0002,
+ 0x0078, 0x869e, 0x684b, 0x0005, 0x1078, 0x8cc0, 0x6847, 0x0000,
+ 0x1078, 0x4982, 0x1078, 0x8bf4, 0x0d7f, 0x1078, 0x753d, 0x007c,
+ 0x017e, 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x86b8,
+ 0x6837, 0x0103, 0x684b, 0x0028, 0x6847, 0x0000, 0x1078, 0x4982,
+ 0x0d7f, 0x017f, 0xa186, 0x0013, 0x0040, 0x86ca, 0xa186, 0x0014,
+ 0x0040, 0x86ca, 0xa186, 0x0027, 0x0040, 0x86ca, 0x1078, 0x7583,
+ 0x0078, 0x86d0, 0x1078, 0x6010, 0x1078, 0x8c01, 0x1078, 0x6109,
+ 0x007c, 0x057e, 0x067e, 0x0d7e, 0x0f7e, 0x2029, 0x0001, 0xa182,
+ 0x0101, 0x00c8, 0x86dd, 0x0078, 0x86df, 0x2009, 0x0100, 0x2130,
+ 0x2069, 0xa898, 0x831c, 0x2300, 0xad18, 0x2009, 0x0020, 0xaf90,
+ 0x001d, 0x1078, 0x8739, 0xa6b2, 0x0020, 0x7804, 0xa06d, 0x0040,
+ 0x86f3, 0x1078, 0x13aa, 0x1078, 0x1381, 0x0040, 0x871d, 0x8528,
+ 0x6837, 0x0110, 0x683b, 0x0000, 0x2d20, 0x7c06, 0xa68a, 0x003d,
+ 0x00c8, 0x8709, 0x2608, 0xad90, 0x000f, 0x1078, 0x8739, 0x0078,
+ 0x871d, 0xa6b2, 0x003c, 0x2009, 0x003c, 0x2d78, 0xad90, 0x000f,
+ 0x1078, 0x8739, 0x0078, 0x86f3, 0x0f7f, 0x852f, 0xa5ad, 0x0003,
+ 0x7d36, 0xa5ac, 0x0000, 0x0078, 0x8722, 0x0f7f, 0x852f, 0xa5ad,
+ 0x0003, 0x7d36, 0x0d7f, 0x067f, 0x057f, 0x007c, 0x0f7e, 0x8dff,
+ 0x0040, 0x8737, 0x6804, 0xa07d, 0x0040, 0x8735, 0x6807, 0x0000,
+ 0x1078, 0x4982, 0x2f68, 0x0078, 0x872a, 0x1078, 0x4982, 0x0f7f,
+ 0x007c, 0x157e, 0xa184, 0x0001, 0x0040, 0x873f, 0x8108, 0x810c,
+ 0x21a8, 0x2304, 0x8007, 0x2012, 0x8318, 0x8210, 0x00f0, 0x8741,
+ 0x157f, 0x007c, 0x067e, 0x127e, 0x2091, 0x8000, 0x2031, 0x0001,
+ 0x601c, 0xa084, 0x000f, 0x1079, 0x8766, 0x127f, 0x067f, 0x007c,
+ 0x127e, 0x2091, 0x8000, 0x067e, 0x2031, 0x0000, 0x601c, 0xa084,
+ 0x000f, 0x1079, 0x8766, 0x067f, 0x127f, 0x007c, 0x8780, 0x876e,
+ 0x877b, 0x879c, 0x876e, 0x877b, 0x879c, 0x877b, 0x1078, 0x1328,
+ 0x037e, 0x2019, 0x0010, 0x1078, 0x9a6a, 0x601f, 0x0006, 0x6003,
+ 0x0007, 0x037f, 0x007c, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c,
+ 0x0d7e, 0x86ff, 0x00c0, 0x8797, 0x6010, 0x2068, 0x1078, 0x8a44,
+ 0x0040, 0x8799, 0xa00e, 0x2001, 0x0005, 0x1078, 0x4a60, 0x1078,
+ 0x8cc0, 0x1078, 0x4982, 0x1078, 0x753d, 0xa085, 0x0001, 0x0d7f,
+ 0x007c, 0xa006, 0x0078, 0x8797, 0x6000, 0xa08a, 0x0010, 0x10c8,
+ 0x1328, 0x1079, 0x87a4, 0x007c, 0x87b4, 0x87d4, 0x87b6, 0x87f7,
+ 0x87d0, 0x87b4, 0x877b, 0x8780, 0x8780, 0x877b, 0x877b, 0x877b,
+ 0x877b, 0x877b, 0x877b, 0x877b, 0x1078, 0x1328, 0x86ff, 0x00c0,
+ 0x87cd, 0x0d7e, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x87c2,
+ 0x1078, 0x8cc0, 0x0d7f, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f,
+ 0x0002, 0x1078, 0x5bf8, 0x1078, 0x6109, 0xa085, 0x0001, 0x007c,
+ 0x1078, 0x1749, 0x0078, 0x87b6, 0x0e7e, 0x2071, 0xa5ab, 0x7024,
+ 0xac06, 0x00c0, 0x87dd, 0x1078, 0x6dda, 0x601c, 0xa084, 0x000f,
+ 0xa086, 0x0006, 0x00c0, 0x87ef, 0x087e, 0x097e, 0x2049, 0x0001,
+ 0x2c40, 0x1078, 0x7058, 0x097f, 0x087f, 0x0078, 0x87f1, 0x1078,
+ 0x6cd2, 0x0e7f, 0x00c0, 0x87b6, 0x1078, 0x877b, 0x007c, 0x037e,
+ 0x0e7e, 0x2071, 0xa5ab, 0x703c, 0xac06, 0x00c0, 0x8807, 0x2019,
+ 0x0000, 0x1078, 0x6e6c, 0x0e7f, 0x037f, 0x0078, 0x87b6, 0x1078,
+ 0x719a, 0x0e7f, 0x037f, 0x00c0, 0x87b6, 0x1078, 0x877b, 0x007c,
+ 0x0c7e, 0x601c, 0xa084, 0x000f, 0x1079, 0x8818, 0x0c7f, 0x007c,
+ 0x8827, 0x8895, 0x89cd, 0x8832, 0x8c01, 0x8827, 0x9a5b, 0x753d,
+ 0x8895, 0x1078, 0x8c3b, 0x00c0, 0x8827, 0x1078, 0x7a05, 0x007c,
+ 0x1078, 0x6010, 0x1078, 0x6109, 0x1078, 0x753d, 0x007c, 0x6017,
+ 0x0001, 0x007c, 0x6010, 0xa080, 0x0019, 0x2c02, 0x6000, 0xa08a,
+ 0x0010, 0x10c8, 0x1328, 0x1079, 0x883e, 0x007c, 0x884e, 0x8850,
+ 0x8872, 0x8884, 0x8891, 0x884e, 0x8827, 0x8827, 0x8827, 0x8884,
+ 0x8884, 0x884e, 0x884e, 0x884e, 0x884e, 0x888e, 0x1078, 0x1328,
+ 0x0e7e, 0x6010, 0x2070, 0x7050, 0xc0b5, 0x7052, 0x2071, 0xa5ab,
+ 0x7024, 0xac06, 0x0040, 0x886e, 0x1078, 0x6cd2, 0x6007, 0x0085,
+ 0x6003, 0x000b, 0x601f, 0x0002, 0x2001, 0xa5a1, 0x2004, 0x6016,
+ 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0e7f, 0x007c, 0x6017, 0x0001,
+ 0x0078, 0x886c, 0x0d7e, 0x6010, 0x2068, 0x6850, 0xc0b5, 0x6852,
+ 0x0d7f, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x1078,
+ 0x5bf8, 0x1078, 0x6109, 0x007c, 0x0d7e, 0x6017, 0x0001, 0x6010,
+ 0x2068, 0x6850, 0xc0b5, 0x6852, 0x0d7f, 0x007c, 0x1078, 0x753d,
+ 0x007c, 0x1078, 0x1749, 0x0078, 0x8872, 0x6000, 0xa08a, 0x0010,
+ 0x10c8, 0x1328, 0x1079, 0x889d, 0x007c, 0x88ad, 0x882f, 0x88af,
+ 0x88ad, 0x88af, 0x88af, 0x8828, 0x88ad, 0x8821, 0x8821, 0x88ad,
+ 0x88ad, 0x88ad, 0x88ad, 0x88ad, 0x88ad, 0x1078, 0x1328, 0x0d7e,
+ 0x6018, 0x2068, 0x6804, 0xa084, 0x00ff, 0x0d7f, 0xa08a, 0x000c,
+ 0x10c8, 0x1328, 0x1079, 0x88bd, 0x007c, 0x88c9, 0x8971, 0x88cb,
+ 0x890b, 0x88cb, 0x890b, 0x88cb, 0x88d8, 0x88c9, 0x890b, 0x88c9,
+ 0x88f5, 0x1078, 0x1328, 0x6004, 0xa08e, 0x0016, 0x0040, 0x8906,
+ 0xa08e, 0x0004, 0x0040, 0x8906, 0xa08e, 0x0002, 0x0040, 0x8906,
+ 0x6004, 0x1078, 0x8c3b, 0x0040, 0x898c, 0xa08e, 0x0021, 0x0040,
+ 0x8990, 0xa08e, 0x0022, 0x0040, 0x898c, 0xa08e, 0x003d, 0x0040,
+ 0x8990, 0xa08e, 0x0039, 0x0040, 0x8994, 0xa08e, 0x0035, 0x0040,
+ 0x8994, 0xa08e, 0x001e, 0x0040, 0x8908, 0xa08e, 0x0001, 0x00c0,
+ 0x8904, 0x0d7e, 0x6018, 0x2068, 0x6804, 0xa084, 0x00ff, 0x0d7f,
+ 0xa086, 0x0006, 0x0040, 0x8906, 0x1078, 0x2813, 0x1078, 0x7a05,
+ 0x1078, 0x8c01, 0x007c, 0x0c7e, 0x0d7e, 0x6104, 0xa186, 0x0016,
+ 0x0040, 0x8961, 0xa186, 0x0002, 0x00c0, 0x8934, 0x6018, 0x2068,
+ 0x68a0, 0xd0bc, 0x00c0, 0x89b8, 0x6840, 0xa084, 0x00ff, 0xa005,
+ 0x0040, 0x8934, 0x8001, 0x6842, 0x6013, 0x0000, 0x601f, 0x0007,
+ 0x6017, 0x0398, 0x1078, 0x74d7, 0x0040, 0x8934, 0x2d00, 0x601a,
+ 0x601f, 0x0001, 0x0078, 0x8961, 0x0d7f, 0x0c7f, 0x6004, 0xa08e,
+ 0x0002, 0x00c0, 0x8952, 0x6018, 0xa080, 0x0028, 0x2004, 0xa086,
+ 0x007e, 0x00c0, 0x8952, 0x2009, 0xa332, 0x2104, 0xc085, 0x200a,
+ 0x0e7e, 0x2071, 0xa300, 0x1078, 0x41f5, 0x0e7f, 0x1078, 0x7a05,
+ 0x0078, 0x8956, 0x1078, 0x7a05, 0x1078, 0x2813, 0x0e7e, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x2839, 0x127f, 0x0e7f, 0x1078, 0x8c01,
+ 0x007c, 0x2001, 0x0002, 0x1078, 0x443f, 0x6003, 0x0001, 0x6007,
+ 0x0002, 0x1078, 0x5c45, 0x1078, 0x6109, 0x0d7f, 0x0c7f, 0x0078,
+ 0x8960, 0x0c7e, 0x0d7e, 0x6104, 0xa186, 0x0016, 0x0040, 0x8961,
+ 0x6018, 0x2068, 0x6840, 0xa084, 0x00ff, 0xa005, 0x0040, 0x8934,
+ 0x8001, 0x6842, 0x6003, 0x0001, 0x1078, 0x5c45, 0x1078, 0x6109,
+ 0x0d7f, 0x0c7f, 0x0078, 0x8960, 0x1078, 0x7a05, 0x0078, 0x8908,
+ 0x1078, 0x7a28, 0x0078, 0x8908, 0x0d7e, 0x2c68, 0x6104, 0x1078,
+ 0x8ef5, 0x0d7f, 0x0040, 0x89a0, 0x1078, 0x753d, 0x0078, 0x89b7,
+ 0x6004, 0x8007, 0x6130, 0xa18c, 0x00ff, 0xa105, 0x6032, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x6038, 0x600a, 0x2001,
+ 0xa5a1, 0x2004, 0x6016, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x007c,
+ 0x0d7f, 0x0c7f, 0x1078, 0x7a05, 0x1078, 0x2813, 0x0e7e, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x2839, 0x6013, 0x0000, 0x601f, 0x0007,
+ 0x6017, 0x0398, 0x127f, 0x0e7f, 0x007c, 0x6000, 0xa08a, 0x0010,
+ 0x10c8, 0x1328, 0x1079, 0x89d5, 0x007c, 0x89e5, 0x89e5, 0x89e5,
+ 0x89e5, 0x89e5, 0x89e5, 0x89e5, 0x89e5, 0x89e5, 0x8827, 0x89e5,
+ 0x882f, 0x89e7, 0x882f, 0x89f5, 0x89e5, 0x1078, 0x1328, 0x6004,
+ 0xa086, 0x008b, 0x0040, 0x89f5, 0x6007, 0x008b, 0x6003, 0x000d,
+ 0x1078, 0x5bf8, 0x1078, 0x6109, 0x007c, 0x1078, 0x8bf4, 0x1078,
+ 0x8a44, 0x0040, 0x8a2d, 0x1078, 0x2813, 0x0d7e, 0x1078, 0x8a44,
+ 0x0040, 0x8a0f, 0x6010, 0x2068, 0x6837, 0x0103, 0x684b, 0x0006,
+ 0x6847, 0x0000, 0x6850, 0xc0ed, 0x6852, 0x1078, 0x4982, 0x2c68,
+ 0x1078, 0x74d7, 0x0040, 0x8a1d, 0x6818, 0x601a, 0x0c7e, 0x2d60,
+ 0x1078, 0x8c01, 0x0c7f, 0x0078, 0x8a1e, 0x2d60, 0x0d7f, 0x6013,
+ 0x0000, 0x601f, 0x0001, 0x6007, 0x0001, 0x6003, 0x0001, 0x1078,
+ 0x5c45, 0x1078, 0x6109, 0x0078, 0x8a2f, 0x1078, 0x8c01, 0x007c,
+ 0xa284, 0x000f, 0x00c0, 0x8a41, 0xa282, 0xaa00, 0x0048, 0x8a41,
+ 0x2001, 0xa315, 0x2004, 0xa202, 0x00c8, 0x8a41, 0xa085, 0x0001,
+ 0x007c, 0xa006, 0x0078, 0x8a40, 0x027e, 0x0e7e, 0x2071, 0xa300,
+ 0x6210, 0x7058, 0xa202, 0x0048, 0x8a56, 0x705c, 0xa202, 0x00c8,
+ 0x8a56, 0xa085, 0x0001, 0x0e7f, 0x027f, 0x007c, 0xa006, 0x0078,
+ 0x8a53, 0x0e7e, 0x0c7e, 0x037e, 0x007e, 0x127e, 0x2091, 0x8000,
+ 0x2061, 0xaa00, 0x2071, 0xa300, 0x7344, 0x7060, 0xa302, 0x00c8,
+ 0x8a83, 0x601c, 0xa206, 0x00c0, 0x8a7b, 0x1078, 0x8d66, 0x0040,
+ 0x8a7b, 0x1078, 0x8c3b, 0x00c0, 0x8a77, 0x1078, 0x7a05, 0x0c7e,
+ 0x1078, 0x753d, 0x0c7f, 0xace0, 0x0010, 0x7054, 0xac02, 0x00c8,
+ 0x8a83, 0x0078, 0x8a64, 0x127f, 0x007f, 0x037f, 0x0c7f, 0x0e7f,
+ 0x007c, 0x0e7e, 0x0c7e, 0x017e, 0xa188, 0xa434, 0x210c, 0x81ff,
+ 0x0040, 0x8aa1, 0x2061, 0xaa00, 0x2071, 0xa300, 0x017e, 0x1078,
+ 0x74d7, 0x017f, 0x0040, 0x8aa4, 0x611a, 0x1078, 0x2813, 0x1078,
+ 0x753d, 0xa006, 0x0078, 0x8aa6, 0xa085, 0x0001, 0x017f, 0x0c7f,
+ 0x0e7f, 0x007c, 0x0c7e, 0x057e, 0x127e, 0x2091, 0x8000, 0x0c7e,
+ 0x1078, 0x74d7, 0x057f, 0x0040, 0x8ac3, 0x6612, 0x651a, 0x601f,
+ 0x0003, 0x2009, 0x004b, 0x1078, 0x756c, 0xa085, 0x0001, 0x127f,
+ 0x057f, 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8abf, 0x0c7e, 0x057e,
+ 0x127e, 0x2091, 0x8000, 0x62a0, 0x0c7e, 0x1078, 0x74d7, 0x057f,
+ 0x0040, 0x8af1, 0x6013, 0x0000, 0x651a, 0x601f, 0x0003, 0x0c7e,
+ 0x2560, 0x1078, 0x471b, 0x0c7f, 0x1078, 0x5d53, 0x077e, 0x2039,
+ 0x0000, 0x1078, 0x5c78, 0x2c08, 0x1078, 0x9c38, 0x077f, 0x2009,
+ 0x004c, 0x1078, 0x756c, 0xa085, 0x0001, 0x127f, 0x057f, 0x0c7f,
+ 0x007c, 0xa006, 0x0078, 0x8aed, 0x0f7e, 0x0c7e, 0x047e, 0x0c7e,
+ 0x1078, 0x74d7, 0x2c78, 0x0c7f, 0x0040, 0x8b0e, 0x7e12, 0x2c00,
+ 0x781a, 0x781f, 0x0003, 0x2021, 0x0005, 0x1078, 0x8b4e, 0x2f60,
+ 0x2009, 0x004d, 0x1078, 0x756c, 0xa085, 0x0001, 0x047f, 0x0c7f,
+ 0x0f7f, 0x007c, 0x0f7e, 0x0c7e, 0x047e, 0x0c7e, 0x1078, 0x74d7,
+ 0x2c78, 0x0c7f, 0x0040, 0x8b2c, 0x7e12, 0x2c00, 0x781a, 0x781f,
+ 0x0003, 0x2021, 0x0005, 0x1078, 0x8b4e, 0x2f60, 0x2009, 0x004e,
+ 0x1078, 0x756c, 0xa085, 0x0001, 0x047f, 0x0c7f, 0x0f7f, 0x007c,
+ 0x0f7e, 0x0c7e, 0x047e, 0x0c7e, 0x1078, 0x74d7, 0x2c78, 0x0c7f,
+ 0x0040, 0x8b4a, 0x7e12, 0x2c00, 0x781a, 0x781f, 0x0003, 0x2021,
+ 0x0004, 0x1078, 0x8b4e, 0x2f60, 0x2009, 0x0052, 0x1078, 0x756c,
+ 0xa085, 0x0001, 0x047f, 0x0c7f, 0x0f7f, 0x007c, 0x097e, 0x077e,
+ 0x127e, 0x2091, 0x8000, 0x1078, 0x46a7, 0x0040, 0x8b5b, 0x2001,
+ 0x8b53, 0x0078, 0x8b61, 0x1078, 0x466d, 0x0040, 0x8b6a, 0x2001,
+ 0x8b5b, 0x007e, 0xa00e, 0x2400, 0x1078, 0x4a60, 0x1078, 0x4982,
+ 0x007f, 0x007a, 0x2418, 0x1078, 0x5fa7, 0x62a0, 0x087e, 0x2041,
+ 0x0001, 0x2039, 0x0001, 0x2608, 0x1078, 0x5d6d, 0x087f, 0x1078,
+ 0x5c78, 0x2f08, 0x2648, 0x1078, 0x9c38, 0x613c, 0x81ff, 0x1040,
+ 0x5e21, 0x127f, 0x077f, 0x097f, 0x007c, 0x0c7e, 0x127e, 0x2091,
+ 0x8000, 0x0c7e, 0x1078, 0x74d7, 0x017f, 0x0040, 0x8b9e, 0x660a,
+ 0x611a, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x001f, 0x1078,
+ 0x756c, 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078,
+ 0x8b9b, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078, 0x74d7,
+ 0x017f, 0x0040, 0x8bba, 0x660a, 0x611a, 0x601f, 0x0008, 0x2d00,
+ 0x6012, 0x2009, 0x0021, 0x1078, 0x756c, 0xa085, 0x0001, 0x127f,
+ 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8bb7, 0x0c7e, 0x127e, 0x2091,
+ 0x8000, 0x0c7e, 0x1078, 0x74d7, 0x017f, 0x0040, 0x8bd6, 0x660a,
+ 0x611a, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x003d, 0x1078,
+ 0x756c, 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078,
+ 0x8bd3, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078, 0x74d7,
+ 0x017f, 0x0040, 0x8bf1, 0x611a, 0x601f, 0x0001, 0x2d00, 0x6012,
+ 0x2009, 0x0000, 0x1078, 0x756c, 0xa085, 0x0001, 0x127f, 0x0c7f,
+ 0x007c, 0xa006, 0x0078, 0x8bee, 0x027e, 0x0d7e, 0x6218, 0x2268,
+ 0x6a3c, 0x82ff, 0x0040, 0x8bfe, 0x8211, 0x6a3e, 0x0d7f, 0x027f,
+ 0x007c, 0x007e, 0x6000, 0xa086, 0x0000, 0x0040, 0x8c13, 0x6013,
+ 0x0000, 0x601f, 0x0007, 0x2001, 0xa5a1, 0x2004, 0x6016, 0x1078,
+ 0xa134, 0x603f, 0x0000, 0x007f, 0x007c, 0x067e, 0x0c7e, 0x0d7e,
+ 0x2031, 0xa352, 0x2634, 0xd6e4, 0x0040, 0x8c23, 0x6618, 0x2660,
+ 0x6e48, 0x1078, 0x461b, 0x0d7f, 0x0c7f, 0x067f, 0x007c, 0x007e,
+ 0x017e, 0x6004, 0xa08e, 0x0002, 0x0040, 0x8c38, 0xa08e, 0x0003,
+ 0x0040, 0x8c38, 0xa08e, 0x0004, 0x0040, 0x8c38, 0xa085, 0x0001,
+ 0x017f, 0x007f, 0x007c, 0x007e, 0x0d7e, 0x6010, 0xa06d, 0x0040,
+ 0x8c48, 0x6838, 0xd0fc, 0x0040, 0x8c48, 0xa006, 0x0078, 0x8c4a,
+ 0xa085, 0x0001, 0x0d7f, 0x007f, 0x007c, 0x0c7e, 0x127e, 0x2091,
+ 0x8000, 0x0c7e, 0x1078, 0x74d7, 0x017f, 0x0040, 0x8c67, 0x611a,
+ 0x601f, 0x0001, 0x2d00, 0x6012, 0x1078, 0x2813, 0x2009, 0x0028,
+ 0x1078, 0x756c, 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006,
+ 0x0078, 0x8c64, 0xa186, 0x0015, 0x00c0, 0x8c7f, 0x2011, 0xa31f,
+ 0x2204, 0xa086, 0x0074, 0x00c0, 0x8c7f, 0x1078, 0x7d0d, 0x6003,
+ 0x0001, 0x6007, 0x0029, 0x1078, 0x5c45, 0x0078, 0x8c83, 0x1078,
+ 0x7a05, 0x1078, 0x753d, 0x007c, 0xa186, 0x0016, 0x00c0, 0x8c8e,
+ 0x2001, 0x0004, 0x1078, 0x443f, 0x0078, 0x8caf, 0xa186, 0x0015,
+ 0x00c0, 0x8cb3, 0x2011, 0xa31f, 0x2204, 0xa086, 0x0014, 0x00c0,
+ 0x8cb3, 0x0d7e, 0x6018, 0x2068, 0x1078, 0x457d, 0x0d7f, 0x1078,
+ 0x7dba, 0x00c0, 0x8cb3, 0x0d7e, 0x6018, 0x2068, 0x6890, 0x0d7f,
+ 0xa005, 0x0040, 0x8cb3, 0x2001, 0x0006, 0x1078, 0x443f, 0x1078,
+ 0x7608, 0x0078, 0x8cb7, 0x1078, 0x7a05, 0x1078, 0x753d, 0x007c,
+ 0x6848, 0xa086, 0x0005, 0x00c0, 0x8cbf, 0x1078, 0x8cc0, 0x007c,
+ 0x6850, 0xc0ad, 0x6852, 0x007c, 0x0e7e, 0x2071, 0xa88c, 0x7014,
+ 0xd0e4, 0x0040, 0x8cd5, 0x6013, 0x0000, 0x6003, 0x0001, 0x6007,
+ 0x0050, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0e7f, 0x007c, 0x0c7e,
+ 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0f7f, 0x0040, 0x8ce4, 0x601c,
+ 0xa084, 0x000f, 0x1079, 0x8ce6, 0x0c7f, 0x007c, 0x8827, 0x8cf1,
+ 0x8cf4, 0x8cf7, 0x9f00, 0x9f1c, 0x9f1f, 0x8827, 0x8827, 0x1078,
+ 0x1328, 0x0005, 0x0005, 0x007c, 0x0005, 0x0005, 0x007c, 0x1078,
+ 0x8cfa, 0x007c, 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0040, 0x8d29,
+ 0x1078, 0x74d7, 0x00c0, 0x8d0a, 0x2001, 0xa5a2, 0x2004, 0x783e,
+ 0x0078, 0x8d29, 0x7818, 0x601a, 0x781c, 0xa086, 0x0003, 0x0040,
+ 0x8d17, 0x7808, 0x6036, 0x2f00, 0x603a, 0x0078, 0x8d1b, 0x7808,
+ 0x603a, 0x2f00, 0x6036, 0x602a, 0x601f, 0x0001, 0x6007, 0x0035,
+ 0x6003, 0x0001, 0x7920, 0x6122, 0x1078, 0x5bf8, 0x1078, 0x6109,
+ 0x2f60, 0x0f7f, 0x007c, 0x017e, 0x0f7e, 0x682c, 0x6032, 0xa08e,
+ 0x0001, 0x0040, 0x8d3c, 0xa086, 0x0005, 0x0040, 0x8d40, 0xa006,
+ 0x602a, 0x602e, 0x0078, 0x8d51, 0x6824, 0xc0f4, 0xc0d5, 0x6826,
+ 0x6810, 0x2078, 0x787c, 0x6938, 0xa102, 0x7880, 0x6934, 0xa103,
+ 0x00c8, 0x8d37, 0x6834, 0x602a, 0x6838, 0xa084, 0xfffc, 0x683a,
+ 0x602e, 0x2d00, 0x6036, 0x6808, 0x603a, 0x6918, 0x611a, 0x6920,
+ 0x6122, 0x601f, 0x0001, 0x6007, 0x0039, 0x6003, 0x0001, 0x1078,
+ 0x5bf8, 0x6803, 0x0002, 0x0f7f, 0x017f, 0x007c, 0x007e, 0x017e,
+ 0x6004, 0xa08e, 0x0034, 0x0040, 0x8d8b, 0xa08e, 0x0035, 0x0040,
+ 0x8d8b, 0xa08e, 0x0036, 0x0040, 0x8d8b, 0xa08e, 0x0037, 0x0040,
+ 0x8d8b, 0xa08e, 0x0038, 0x0040, 0x8d8b, 0xa08e, 0x0039, 0x0040,
+ 0x8d8b, 0xa08e, 0x003a, 0x0040, 0x8d8b, 0xa08e, 0x003b, 0x0040,
+ 0x8d8b, 0xa085, 0x0001, 0x017f, 0x007f, 0x007c, 0x0f7e, 0x2c78,
+ 0x1078, 0x4893, 0x00c0, 0x8d98, 0xa085, 0x0001, 0x0078, 0x8da7,
+ 0x6024, 0xd0f4, 0x00c0, 0x8da6, 0xc0f5, 0x6026, 0x6010, 0x2078,
+ 0x7828, 0x603a, 0x782c, 0x6036, 0x1078, 0x1749, 0xa006, 0x0f7f,
+ 0x007c, 0x007e, 0x017e, 0x027e, 0x037e, 0x0e7e, 0x2001, 0xa59c,
+ 0x200c, 0x8000, 0x2014, 0x2001, 0x0032, 0x1078, 0x5a98, 0x2001,
+ 0xa5a0, 0x82ff, 0x00c0, 0x8dbe, 0x2011, 0x0002, 0x2202, 0x2001,
+ 0xa59e, 0x200c, 0x8000, 0x2014, 0x2071, 0xa58c, 0x711a, 0x721e,
+ 0x2001, 0x0064, 0x1078, 0x5a98, 0x2001, 0xa5a1, 0x82ff, 0x00c0,
+ 0x8dd3, 0x2011, 0x0002, 0x2202, 0x2009, 0xa5a2, 0xa280, 0x000a,
+ 0x200a, 0x0e7f, 0x037f, 0x027f, 0x017f, 0x007f, 0x007c, 0x007e,
+ 0x0e7e, 0x2001, 0xa5a0, 0x2003, 0x0028, 0x2001, 0xa5a1, 0x2003,
+ 0x0014, 0x2071, 0xa58c, 0x701b, 0x0000, 0x701f, 0x07d0, 0x2001,
+ 0xa5a2, 0x2003, 0x001e, 0x0e7f, 0x007f, 0x007c, 0x0c7e, 0x127e,
+ 0x2091, 0x8000, 0x0c7e, 0x1078, 0x74d7, 0x017f, 0x0040, 0x8e0e,
+ 0x611a, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x0033, 0x1078,
+ 0x756c, 0xa085, 0x0001, 0x127f, 0x0c7f, 0x007c, 0xa006, 0x0078,
+ 0x8e0b, 0x0d7e, 0x0e7e, 0x0f7e, 0x2071, 0xa300, 0xa186, 0x0015,
+ 0x00c0, 0x8e40, 0x707c, 0xa086, 0x0018, 0x00c0, 0x8e40, 0x6010,
+ 0x2068, 0x6a3c, 0xd2e4, 0x00c0, 0x8e34, 0x2c78, 0x1078, 0x62c6,
+ 0x0040, 0x8e48, 0x7068, 0x6a50, 0xa206, 0x00c0, 0x8e3c, 0x706c,
+ 0x6a54, 0xa206, 0x00c0, 0x8e3c, 0x6218, 0xa290, 0x0028, 0x2214,
+ 0x2009, 0x0000, 0x1078, 0x285b, 0x1078, 0x7608, 0x0078, 0x8e44,
+ 0x1078, 0x7a05, 0x1078, 0x753d, 0x0f7f, 0x0e7f, 0x0d7f, 0x007c,
+ 0x704c, 0xa080, 0x293f, 0x2004, 0x6a54, 0xa206, 0x0040, 0x8e34,
+ 0x0078, 0x8e3c, 0x0c7e, 0x127e, 0x2091, 0x8000, 0x0c7e, 0x1078,
+ 0x74d7, 0x017f, 0x0040, 0x8e6a, 0x611a, 0x601f, 0x0001, 0x2d00,
+ 0x6012, 0x2009, 0x0043, 0x1078, 0x756c, 0xa085, 0x0001, 0x127f,
+ 0x0c7f, 0x007c, 0xa006, 0x0078, 0x8e67, 0x0d7e, 0x0e7e, 0x0f7e,
+ 0x2071, 0xa300, 0xa186, 0x0015, 0x00c0, 0x8e93, 0x707c, 0xa086,
+ 0x0004, 0x00c0, 0x8e93, 0x6010, 0xa0e8, 0x000f, 0x2c78, 0x1078,
+ 0x62c6, 0x0040, 0x8e9b, 0x7068, 0x6a08, 0xa206, 0x00c0, 0x8e8f,
+ 0x706c, 0x6a0c, 0xa206, 0x00c0, 0x8e8f, 0x1078, 0x2813, 0x1078,
+ 0x7608, 0x0078, 0x8e97, 0x1078, 0x7a05, 0x1078, 0x753d, 0x0f7f,
+ 0x0e7f, 0x0d7f, 0x007c, 0x704c, 0xa080, 0x293f, 0x2004, 0x6a0c,
+ 0xa206, 0x0040, 0x8e8d, 0x0078, 0x8e8f, 0x017e, 0x027e, 0x684c,
+ 0xd0ac, 0x0040, 0x8ebd, 0x6914, 0x6a10, 0x2100, 0xa205, 0x0040,
+ 0x8ebd, 0x6860, 0xa106, 0x00c0, 0x8eb9, 0x685c, 0xa206, 0x0040,
+ 0x8ebd, 0x6962, 0x6a5e, 0xa085, 0x0001, 0x027f, 0x017f, 0x007c,
+ 0x0e7e, 0x127e, 0x2071, 0xa300, 0x2091, 0x8000, 0x7544, 0xa582,
+ 0x0001, 0x0048, 0x8ef2, 0x7048, 0x2060, 0x6000, 0xa086, 0x0000,
+ 0x0040, 0x8ede, 0xace0, 0x0010, 0x7054, 0xac02, 0x00c8, 0x8eda,
+ 0x0078, 0x8ecd, 0x2061, 0xaa00, 0x0078, 0x8ecd, 0x6003, 0x0008,
+ 0x8529, 0x7546, 0xaca8, 0x0010, 0x7054, 0xa502, 0x00c8, 0x8eee,
+ 0x754a, 0xa085, 0x0001, 0x127f, 0x0e7f, 0x007c, 0x704b, 0xaa00,
+ 0x0078, 0x8ee9, 0xa006, 0x0078, 0x8eeb, 0x0c7e, 0x027e, 0x017e,
+ 0xa186, 0x0035, 0x0040, 0x8eff, 0x6a34, 0x0078, 0x8f00, 0x6a28,
+ 0x1078, 0x8a30, 0x0040, 0x8f29, 0x2260, 0x611c, 0xa186, 0x0003,
+ 0x0040, 0x8f0e, 0xa186, 0x0006, 0x00c0, 0x8f25, 0x6834, 0xa206,
+ 0x0040, 0x8f1d, 0x6838, 0xa206, 0x00c0, 0x8f25, 0x6108, 0x6834,
+ 0xa106, 0x00c0, 0x8f25, 0x0078, 0x8f22, 0x6008, 0x6938, 0xa106,
+ 0x00c0, 0x8f25, 0x6018, 0x6918, 0xa106, 0x017f, 0x027f, 0x0c7f,
+ 0x007c, 0xa085, 0x0001, 0x0078, 0x8f25, 0x067e, 0x6000, 0xa0b2,
+ 0x0010, 0x10c8, 0x1328, 0x1079, 0x8f37, 0x067f, 0x007c, 0x8f47,
+ 0x93bb, 0x94d3, 0x8f47, 0x8f47, 0x8f47, 0x8f47, 0x8f47, 0x8f81,
+ 0x955e, 0x8f47, 0x8f47, 0x8f47, 0x8f47, 0x8f47, 0x8f47, 0x1078,
+ 0x1328, 0x067e, 0x6000, 0xa0b2, 0x0010, 0x10c8, 0x1328, 0x1079,
+ 0x8f53, 0x067f, 0x007c, 0x8f63, 0x99f6, 0x8f63, 0x8f63, 0x8f63,
+ 0x8f63, 0x8f63, 0x8f63, 0x99b4, 0x9a44, 0x8f63, 0xa053, 0xa087,
+ 0xa053, 0xa087, 0x8f63, 0x1078, 0x1328, 0x067e, 0x6000, 0xa0b2,
+ 0x0010, 0x10c8, 0x1328, 0x1079, 0x8f6f, 0x067f, 0x007c, 0x8f7f,
+ 0x969f, 0x976a, 0x9798, 0x9813, 0x8f7f, 0x9919, 0x98c1, 0x956a,
+ 0x9988, 0x999e, 0x8f7f, 0x8f7f, 0x8f7f, 0x8f7f, 0x8f7f, 0x1078,
+ 0x1328, 0xa1b2, 0x0044, 0x10c8, 0x1328, 0x2100, 0x0079, 0x8f88,
+ 0x8fc8, 0x919a, 0x8fc8, 0x8fc8, 0x8fc8, 0x91a2, 0x8fc8, 0x8fc8,
+ 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8,
+ 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fca,
+ 0x902d, 0x9038, 0x9081, 0x909c, 0x911b, 0x918b, 0x8fc8, 0x8fc8,
+ 0x91a6, 0x8fc8, 0x8fc8, 0x91b5, 0x91bc, 0x8fc8, 0x8fc8, 0x8fc8,
+ 0x8fc8, 0x8fc8, 0x91ea, 0x8fc8, 0x8fc8, 0x91f5, 0x8fc8, 0x8fc8,
+ 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x920a, 0x8fc8, 0x8fc8, 0x8fc8,
+ 0x9291, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x8fc8, 0x9305,
+ 0x1078, 0x1328, 0x1078, 0x4897, 0x00c0, 0x8fd7, 0x2001, 0xa332,
+ 0x2004, 0xa084, 0x0009, 0xa086, 0x0008, 0x00c0, 0x8fdf, 0x6007,
+ 0x0009, 0x602b, 0x0009, 0x6013, 0x0000, 0x0078, 0x9195, 0x1078,
+ 0x4887, 0x0e7e, 0x0c7e, 0x037e, 0x027e, 0x017e, 0x6218, 0x2270,
+ 0x72a0, 0x027e, 0x2019, 0x0029, 0x1078, 0x5d53, 0x077e, 0x2039,
+ 0x0000, 0x1078, 0x5c78, 0x2c08, 0x1078, 0x9c38, 0x077f, 0x017f,
+ 0x2e60, 0x1078, 0x471b, 0x017f, 0x027f, 0x037f, 0x0c7f, 0x0e7f,
+ 0x6618, 0x0c7e, 0x2660, 0x1078, 0x4513, 0x0c7f, 0xa6b0, 0x0001,
+ 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x0048, 0x901f, 0x1078,
+ 0x9b6c, 0x00c0, 0x907b, 0x1078, 0x9afd, 0x00c0, 0x901b, 0x6007,
+ 0x0008, 0x0078, 0x9195, 0x6007, 0x0009, 0x0078, 0x9195, 0x1078,
+ 0x9d45, 0x0040, 0x9029, 0x1078, 0x9b6c, 0x0040, 0x9013, 0x0078,
+ 0x907b, 0x6013, 0x1900, 0x0078, 0x901b, 0x6106, 0x1078, 0x9aa8,
+ 0x6007, 0x0006, 0x0078, 0x9195, 0x6007, 0x0007, 0x0078, 0x9195,
+ 0x1078, 0xa0bf, 0x00c0, 0x9340, 0x0d7e, 0x6618, 0x2668, 0x6e04,
+ 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0006, 0x0040, 0x905d, 0xa686,
+ 0x0004, 0x0040, 0x905d, 0x6e04, 0xa6b4, 0x00ff, 0xa686, 0x0006,
+ 0x0040, 0x905d, 0xa686, 0x0004, 0x0040, 0x905d, 0xa686, 0x0005,
+ 0x0040, 0x905d, 0x0d7f, 0x0078, 0x907b, 0x1078, 0x9bd2, 0x00c0,
+ 0x9076, 0xa686, 0x0006, 0x00c0, 0x906f, 0x027e, 0x6218, 0xa290,
+ 0x0028, 0x2214, 0x2009, 0x0000, 0x1078, 0x285b, 0x027f, 0x1078,
+ 0x457d, 0x6007, 0x000a, 0x0d7f, 0x0078, 0x9195, 0x6007, 0x000b,
+ 0x0d7f, 0x0078, 0x9195, 0x1078, 0x2813, 0x6007, 0x0001, 0x0078,
+ 0x9195, 0x1078, 0xa0bf, 0x00c0, 0x9340, 0x6618, 0x0d7e, 0x2668,
+ 0x6e04, 0x0d7f, 0xa686, 0x0707, 0x0040, 0x907b, 0x027e, 0x6218,
+ 0xa290, 0x0028, 0x2214, 0x2009, 0x0000, 0x1078, 0x285b, 0x027f,
+ 0x6007, 0x000c, 0x0078, 0x9195, 0x1078, 0x4897, 0x00c0, 0x90a9,
+ 0x2001, 0xa332, 0x2004, 0xa084, 0x0009, 0xa086, 0x0008, 0x00c0,
+ 0x90b1, 0x6007, 0x0009, 0x602b, 0x0009, 0x6013, 0x0000, 0x0078,
+ 0x9195, 0x1078, 0x4887, 0x6618, 0xa6b0, 0x0001, 0x2634, 0xa684,
+ 0x00ff, 0xa082, 0x0006, 0x0048, 0x90f5, 0xa6b4, 0xff00, 0x8637,
+ 0xa686, 0x0004, 0x0040, 0x90c8, 0xa686, 0x0006, 0x00c0, 0x907b,
+ 0x1078, 0x9be1, 0x00c0, 0x90d0, 0x6007, 0x000e, 0x0078, 0x9195,
+ 0x047e, 0x6418, 0xa4a0, 0x0028, 0x2424, 0xa4a4, 0x00ff, 0x8427,
+ 0x047e, 0x1078, 0x2813, 0x047f, 0x017e, 0xa006, 0x2009, 0xa352,
+ 0x210c, 0xd1a4, 0x0040, 0x90ef, 0x2009, 0x0029, 0x1078, 0x9ec0,
+ 0x6018, 0x0d7e, 0x2068, 0x6800, 0xc0e5, 0x6802, 0x0d7f, 0x017f,
+ 0x047f, 0x6007, 0x0001, 0x0078, 0x9195, 0x2001, 0x0001, 0x1078,
+ 0x442b, 0x157e, 0x017e, 0x027e, 0x037e, 0x20a9, 0x0004, 0x2019,
+ 0xa305, 0x2011, 0xa890, 0x1078, 0x7e55, 0x037f, 0x027f, 0x017f,
+ 0x157f, 0xa005, 0x0040, 0x9115, 0xa6b4, 0xff00, 0x8637, 0xa686,
+ 0x0006, 0x0040, 0x90c8, 0x0078, 0x907b, 0x6013, 0x1900, 0x6007,
+ 0x0009, 0x0078, 0x9195, 0x1078, 0x4897, 0x00c0, 0x9128, 0x2001,
+ 0xa332, 0x2004, 0xa084, 0x0009, 0xa086, 0x0008, 0x00c0, 0x9130,
+ 0x6007, 0x0009, 0x602b, 0x0009, 0x6013, 0x0000, 0x0078, 0x9195,
+ 0x1078, 0x4887, 0x6618, 0xa6b0, 0x0001, 0x2634, 0xa684, 0x00ff,
+ 0xa082, 0x0006, 0x0048, 0x9178, 0xa6b4, 0xff00, 0x8637, 0xa686,
+ 0x0004, 0x0040, 0x9147, 0xa686, 0x0006, 0x00c0, 0x907b, 0x1078,
+ 0x9c0c, 0x00c0, 0x9153, 0x1078, 0x9afd, 0x00c0, 0x9153, 0x6007,
+ 0x0010, 0x0078, 0x9195, 0x047e, 0x6418, 0xa4a0, 0x0028, 0x2424,
+ 0xa4a4, 0x00ff, 0x8427, 0x047e, 0x1078, 0x2813, 0x047f, 0x017e,
+ 0xa006, 0x2009, 0xa352, 0x210c, 0xd1a4, 0x0040, 0x9172, 0x2009,
+ 0x0029, 0x1078, 0x9ec0, 0x6018, 0x0d7e, 0x2068, 0x6800, 0xc0e5,
+ 0x6802, 0x0d7f, 0x017f, 0x047f, 0x6007, 0x0001, 0x0078, 0x9195,
+ 0x1078, 0x9d45, 0x0040, 0x9185, 0xa6b4, 0xff00, 0x8637, 0xa686,
+ 0x0006, 0x0040, 0x9147, 0x0078, 0x907b, 0x6013, 0x1900, 0x6007,
+ 0x0009, 0x0078, 0x9195, 0x1078, 0xa0bf, 0x00c0, 0x9340, 0x1078,
+ 0x9343, 0x00c0, 0x907b, 0x6007, 0x0012, 0x6003, 0x0001, 0x1078,
+ 0x5c45, 0x007c, 0x6007, 0x0001, 0x6003, 0x0001, 0x1078, 0x5c45,
+ 0x0078, 0x9199, 0x6007, 0x0005, 0x0078, 0x919c, 0x1078, 0xa0bf,
+ 0x00c0, 0x9340, 0x1078, 0x9343, 0x00c0, 0x907b, 0x6007, 0x0020,
+ 0x6003, 0x0001, 0x1078, 0x5c45, 0x007c, 0x6007, 0x0023, 0x6003,
+ 0x0001, 0x1078, 0x5c45, 0x007c, 0x1078, 0xa0bf, 0x00c0, 0x9340,
+ 0x1078, 0x9343, 0x00c0, 0x907b, 0x017e, 0x027e, 0x2011, 0xa890,
+ 0x2214, 0x2c08, 0x1078, 0x9e8c, 0x00c0, 0x91de, 0x2160, 0x6007,
+ 0x0026, 0x6013, 0x1700, 0x2011, 0xa889, 0x2214, 0xa296, 0xffff,
+ 0x00c0, 0x91e3, 0x6007, 0x0025, 0x0078, 0x91e3, 0x1078, 0x753d,
+ 0x2160, 0x6007, 0x0025, 0x6003, 0x0001, 0x1078, 0x5c45, 0x027f,
+ 0x017f, 0x007c, 0x6106, 0x1078, 0x9363, 0x6007, 0x002b, 0x0078,
+ 0x9195, 0x6007, 0x002c, 0x0078, 0x9195, 0x1078, 0xa0bf, 0x00c0,
+ 0x9340, 0x1078, 0x9343, 0x00c0, 0x907b, 0x6106, 0x1078, 0x9368,
+ 0x00c0, 0x9206, 0x6007, 0x002e, 0x0078, 0x9195, 0x6007, 0x002f,
+ 0x0078, 0x9195, 0x0e7e, 0x0d7e, 0x0c7e, 0x6018, 0xa080, 0x0001,
+ 0x200c, 0xa184, 0x00ff, 0xa086, 0x0006, 0x0040, 0x9223, 0xa184,
+ 0xff00, 0x8007, 0xa086, 0x0006, 0x0040, 0x9223, 0x0c7f, 0x0d7f,
+ 0x0e7f, 0x0078, 0x919a, 0x2001, 0xa371, 0x2004, 0xd0e4, 0x0040,
+ 0x928d, 0x2071, 0xa88c, 0x7010, 0x6036, 0x7014, 0x603a, 0x7108,
+ 0x720c, 0x2001, 0xa352, 0x2004, 0xd0a4, 0x0040, 0x9241, 0x6018,
+ 0x2068, 0x6810, 0xa106, 0x00c0, 0x9241, 0x6814, 0xa206, 0x0040,
+ 0x9265, 0x2001, 0xa352, 0x2004, 0xd0ac, 0x00c0, 0x9281, 0x2069,
+ 0xa300, 0x686c, 0xa206, 0x00c0, 0x9281, 0x6868, 0xa106, 0x00c0,
+ 0x9281, 0x7210, 0x1078, 0x8a30, 0x0040, 0x9287, 0x1078, 0x9f31,
+ 0x0040, 0x9287, 0x622a, 0x6007, 0x0036, 0x6003, 0x0001, 0x1078,
+ 0x5bf8, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0x7214, 0xa286, 0xffff,
+ 0x0040, 0x9277, 0x1078, 0x8a30, 0x0040, 0x9287, 0xa280, 0x0002,
+ 0x2004, 0x7110, 0xa106, 0x00c0, 0x9287, 0x0078, 0x9252, 0x7210,
+ 0x2c08, 0x1078, 0x9e8c, 0x2c10, 0x2160, 0x0040, 0x9287, 0x0078,
+ 0x9252, 0x6007, 0x0037, 0x6013, 0x1500, 0x0078, 0x925d, 0x6007,
+ 0x0037, 0x6013, 0x1700, 0x0078, 0x925d, 0x6007, 0x0012, 0x0078,
+ 0x925d, 0x6018, 0xa080, 0x0001, 0x2004, 0xa084, 0xff00, 0x8007,
+ 0xa086, 0x0006, 0x00c0, 0x919a, 0x0e7e, 0x0d7e, 0x0c7e, 0x2001,
+ 0xa371, 0x2004, 0xd0e4, 0x0040, 0x92fd, 0x2069, 0xa300, 0x2071,
+ 0xa88c, 0x7008, 0x6036, 0x720c, 0x623a, 0xa286, 0xffff, 0x00c0,
+ 0x92ba, 0x7208, 0x0c7e, 0x2c08, 0x1078, 0x9e8c, 0x2c10, 0x0c7f,
+ 0x0040, 0x92f1, 0x1078, 0x8a30, 0x0040, 0x92f1, 0x0c7e, 0x027e,
+ 0x2260, 0x1078, 0x874a, 0x027f, 0x0c7f, 0x7118, 0xa18c, 0xff00,
+ 0x810f, 0xa186, 0x0001, 0x0040, 0x92db, 0xa186, 0x0005, 0x0040,
+ 0x92d5, 0xa186, 0x0007, 0x00c0, 0x92e5, 0xa280, 0x0004, 0x2004,
+ 0xa005, 0x0040, 0x92e5, 0x057e, 0x7510, 0x7614, 0x1078, 0x9f46,
+ 0x057f, 0x0c7f, 0x0d7f, 0x0e7f, 0x007c, 0x6007, 0x003b, 0x602b,
+ 0x0009, 0x6013, 0x2a00, 0x6003, 0x0001, 0x1078, 0x5bf8, 0x0078,
+ 0x92e1, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013, 0x1700, 0x6003,
+ 0x0001, 0x1078, 0x5bf8, 0x0078, 0x92e1, 0x6007, 0x003b, 0x602b,
+ 0x000b, 0x6013, 0x0000, 0x0078, 0x925d, 0x0e7e, 0x027e, 0x1078,
+ 0x4897, 0x0040, 0x933a, 0x1078, 0x4887, 0x1078, 0xa148, 0x00c0,
+ 0x9338, 0x2071, 0xa300, 0x70c8, 0xc085, 0x70ca, 0x0f7e, 0x2079,
+ 0x0100, 0x7294, 0xa284, 0x00ff, 0x706a, 0x78e6, 0xa284, 0xff00,
+ 0x726c, 0xa205, 0x706e, 0x78ea, 0x0f7f, 0x70d3, 0x0000, 0x2001,
+ 0xa352, 0x2004, 0xd0a4, 0x0040, 0x9331, 0x2011, 0xa5c4, 0x2013,
+ 0x07d0, 0xd0ac, 0x00c0, 0x933a, 0x1078, 0x260d, 0x0078, 0x933a,
+ 0x1078, 0xa178, 0x027f, 0x0e7f, 0x1078, 0x753d, 0x0078, 0x9199,
+ 0x1078, 0x753d, 0x007c, 0x0d7e, 0x067e, 0x6618, 0x2668, 0x6e04,
+ 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0006, 0x0040, 0x9360, 0xa686,
+ 0x0004, 0x0040, 0x9360, 0x6e04, 0xa6b4, 0x00ff, 0xa686, 0x0006,
+ 0x0040, 0x9360, 0xa686, 0x0004, 0x0040, 0x9360, 0xa085, 0x0001,
+ 0x067f, 0x0d7f, 0x007c, 0x0d7e, 0x1078, 0x9397, 0x0d7f, 0x007c,
+ 0x0d7e, 0x1078, 0x93a6, 0x00c0, 0x9390, 0x680c, 0xa08c, 0xff00,
+ 0x6820, 0xa084, 0x00ff, 0xa115, 0x6212, 0x6824, 0x602a, 0xd1e4,
+ 0x0040, 0x937e, 0x2009, 0x0001, 0x0078, 0x938c, 0xd1ec, 0x0040,
+ 0x9390, 0x6920, 0xa18c, 0x00ff, 0x6824, 0x1078, 0x24e3, 0x00c0,
+ 0x9390, 0x2110, 0x2009, 0x0000, 0x1078, 0x285b, 0x0078, 0x9394,
+ 0xa085, 0x0001, 0x0078, 0x9395, 0xa006, 0x0d7f, 0x007c, 0x2069,
+ 0xa88d, 0x6800, 0xa082, 0x0010, 0x00c8, 0x93a4, 0x6013, 0x0000,
+ 0xa085, 0x0001, 0x0078, 0x93a5, 0xa006, 0x007c, 0x6013, 0x0000,
+ 0x2069, 0xa88c, 0x6808, 0xa084, 0xff00, 0xa086, 0x0800, 0x00c0,
+ 0x93ba, 0x6800, 0xa084, 0x00ff, 0xa08e, 0x0014, 0x0040, 0x93ba,
+ 0xa08e, 0x0010, 0x007c, 0x6004, 0xa0b2, 0x0044, 0x10c8, 0x1328,
+ 0xa1b6, 0x0013, 0x00c0, 0x93c7, 0x2008, 0x0079, 0x93da, 0xa1b6,
+ 0x0027, 0x0040, 0x93cf, 0xa1b6, 0x0014, 0x10c0, 0x1328, 0x2001,
+ 0x0007, 0x1078, 0x4472, 0x1078, 0x6010, 0x1078, 0x8c01, 0x1078,
+ 0x6109, 0x007c, 0x941a, 0x941c, 0x941a, 0x941a, 0x941a, 0x941c,
+ 0x9424, 0x94ae, 0x9471, 0x94ae, 0x9485, 0x94ae, 0x9424, 0x94ae,
+ 0x94a6, 0x94ae, 0x94a6, 0x94ae, 0x94ae, 0x941a, 0x941a, 0x941a,
+ 0x941a, 0x941a, 0x941a, 0x941a, 0x941a, 0x941a, 0x941a, 0x941a,
+ 0x941c, 0x941a, 0x94ae, 0x941a, 0x941a, 0x94ae, 0x941a, 0x94ae,
+ 0x94ae, 0x941a, 0x941a, 0x941a, 0x941a, 0x94ae, 0x94ae, 0x941a,
+ 0x94ae, 0x94ae, 0x941a, 0x941a, 0x941a, 0x941a, 0x941a, 0x941c,
+ 0x94ae, 0x94ae, 0x941a, 0x941a, 0x94ae, 0x94ae, 0x941a, 0x941a,
+ 0x941a, 0x941a, 0x1078, 0x1328, 0x1078, 0x6010, 0x6003, 0x0002,
+ 0x1078, 0x6109, 0x0078, 0x94b4, 0x0f7e, 0x2079, 0xa351, 0x7804,
+ 0x0f7f, 0xd0ac, 0x00c0, 0x94ae, 0x2001, 0x0000, 0x1078, 0x442b,
+ 0x6018, 0xa080, 0x0004, 0x2004, 0xa086, 0x00ff, 0x0040, 0x94ae,
+ 0x0c7e, 0x6018, 0x2060, 0x6000, 0xd0f4, 0x00c0, 0x9448, 0x6010,
+ 0xa005, 0x0040, 0x9448, 0x0c7f, 0x1078, 0x35f7, 0x0078, 0x94ae,
+ 0x0c7f, 0x2001, 0xa300, 0x2004, 0xa086, 0x0002, 0x00c0, 0x9457,
+ 0x0f7e, 0x2079, 0xa300, 0x788c, 0x8000, 0x788e, 0x0f7f, 0x2001,
+ 0x0002, 0x1078, 0x443f, 0x1078, 0x6010, 0x601f, 0x0001, 0x6003,
+ 0x0001, 0x6007, 0x0002, 0x1078, 0x5c45, 0x1078, 0x6109, 0x0c7e,
+ 0x6118, 0x2160, 0x2009, 0x0001, 0x1078, 0x58e1, 0x0c7f, 0x0078,
+ 0x94b4, 0x6618, 0x0d7e, 0x2668, 0x6e04, 0x0d7f, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0006, 0x0040, 0x94ae, 0xa686, 0x0004, 0x0040,
+ 0x94ae, 0x2001, 0x0004, 0x0078, 0x94ac, 0x2001, 0xa300, 0x2004,
+ 0xa086, 0x0003, 0x00c0, 0x948e, 0x1078, 0x35f7, 0x2001, 0x0006,
+ 0x1078, 0x94b5, 0x6618, 0x0d7e, 0x2668, 0x6e04, 0x0d7f, 0xa6b4,
+ 0xff00, 0x8637, 0xa686, 0x0006, 0x0040, 0x94ae, 0x2001, 0x0006,
+ 0x0078, 0x94ac, 0x2001, 0x0004, 0x0078, 0x94ac, 0x2001, 0x0006,
+ 0x1078, 0x94b5, 0x0078, 0x94ae, 0x1078, 0x4472, 0x1078, 0x6010,
+ 0x1078, 0x753d, 0x1078, 0x6109, 0x007c, 0x017e, 0x0d7e, 0x6118,
+ 0x2168, 0x6900, 0xd184, 0x0040, 0x94d0, 0x6104, 0xa18e, 0x000a,
+ 0x00c0, 0x94c8, 0x699c, 0xd1a4, 0x00c0, 0x94c8, 0x2001, 0x0007,
+ 0x1078, 0x443f, 0x2001, 0x0000, 0x1078, 0x442b, 0x1078, 0x2839,
+ 0x0d7f, 0x017f, 0x007c, 0x0d7e, 0x6618, 0x2668, 0x6804, 0xa084,
+ 0xff00, 0x8007, 0x0d7f, 0xa0b2, 0x000c, 0x10c8, 0x1328, 0xa1b6,
+ 0x0015, 0x00c0, 0x94e7, 0x1079, 0x94ee, 0x0078, 0x94ed, 0xa1b6,
+ 0x0016, 0x10c0, 0x1328, 0x1079, 0x94fa, 0x007c, 0x7ad0, 0x7ad0,
+ 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0, 0x9547, 0x9506, 0x7ad0, 0x7ad0,
+ 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0,
+ 0x9547, 0x954f, 0x7ad0, 0x7ad0, 0x7ad0, 0x7ad0, 0x0f7e, 0x2079,
+ 0xa351, 0x7804, 0xd0ac, 0x00c0, 0x952d, 0x6018, 0xa07d, 0x0040,
+ 0x952d, 0x7800, 0xd0f4, 0x00c0, 0x9519, 0x7810, 0xa005, 0x00c0,
+ 0x952d, 0x2001, 0x0000, 0x1078, 0x442b, 0x2001, 0x0002, 0x1078,
+ 0x443f, 0x601f, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x1078,
+ 0x5c45, 0x1078, 0x6109, 0x0078, 0x9545, 0x2011, 0xa883, 0x2204,
+ 0x8211, 0x220c, 0x1078, 0x24e3, 0x00c0, 0x9545, 0x0c7e, 0x1078,
+ 0x4501, 0x0040, 0x9540, 0x0c7f, 0x1078, 0x753d, 0x0078, 0x9545,
+ 0x1078, 0x4235, 0x0c7f, 0x1078, 0x753d, 0x0f7f, 0x007c, 0x6604,
+ 0xa6b6, 0x001e, 0x00c0, 0x954e, 0x1078, 0x753d, 0x007c, 0x1078,
+ 0x7d0a, 0x00c0, 0x955b, 0x6003, 0x0001, 0x6007, 0x0001, 0x1078,
+ 0x5c45, 0x0078, 0x955d, 0x1078, 0x753d, 0x007c, 0x6004, 0xa08a,
+ 0x0044, 0x10c8, 0x1328, 0x1078, 0x6010, 0x1078, 0x8c01, 0x1078,
+ 0x6109, 0x007c, 0xa182, 0x0040, 0x0079, 0x956e, 0x9581, 0x9581,
+ 0x9581, 0x9581, 0x9583, 0x9581, 0x9581, 0x9581, 0x9581, 0x9581,
+ 0x9581, 0x9581, 0x9581, 0x9581, 0x9581, 0x9581, 0x9581, 0x9581,
+ 0x9581, 0x1078, 0x1328, 0x0d7e, 0x0e7e, 0x0f7e, 0x157e, 0x047e,
+ 0x027e, 0x6218, 0xa280, 0x002b, 0x2004, 0xa005, 0x0040, 0x9594,
+ 0x2021, 0x0000, 0x1078, 0xa111, 0x6106, 0x2071, 0xa880, 0x7444,
+ 0xa4a4, 0xff00, 0x0040, 0x95eb, 0xa486, 0x2000, 0x00c0, 0x95a6,
+ 0x2009, 0x0001, 0x2011, 0x0200, 0x1078, 0x5a6d, 0x1078, 0x1381,
+ 0x1040, 0x1328, 0x6003, 0x0007, 0x2d00, 0x6837, 0x010d, 0x6803,
+ 0x0000, 0x683b, 0x0000, 0x6c5a, 0x2c00, 0x685e, 0x6008, 0x68b2,
+ 0x6018, 0x2078, 0x78a0, 0x8007, 0x7130, 0x694a, 0x017e, 0xa084,
+ 0xff00, 0x6846, 0x684f, 0x0000, 0x6857, 0x0036, 0x1078, 0x4982,
+ 0x017f, 0xa486, 0x2000, 0x00c0, 0x95d3, 0x2019, 0x0017, 0x1078,
+ 0x9e3b, 0x0078, 0x9645, 0xa486, 0x0400, 0x00c0, 0x95dd, 0x2019,
+ 0x0002, 0x1078, 0x9dec, 0x0078, 0x9645, 0xa486, 0x0200, 0x00c0,
+ 0x95e3, 0x1078, 0x9dd1, 0xa486, 0x1000, 0x00c0, 0x95e9, 0x1078,
+ 0x9e20, 0x0078, 0x9645, 0x2069, 0xa62d, 0x6a00, 0xd284, 0x0040,
+ 0x969b, 0xa284, 0x0300, 0x00c0, 0x9693, 0x6804, 0xa005, 0x0040,
+ 0x9683, 0x2d78, 0x6003, 0x0007, 0x1078, 0x1366, 0x0040, 0x964c,
+ 0x7800, 0xd08c, 0x00c0, 0x9607, 0x7804, 0x8001, 0x7806, 0x6013,
+ 0x0000, 0x6803, 0x0000, 0x6837, 0x0116, 0x683b, 0x0000, 0x6008,
+ 0x68b2, 0x2c00, 0x684a, 0x6018, 0x2078, 0x78a0, 0x8007, 0x7130,
+ 0x6986, 0x6846, 0x6853, 0x003d, 0x7244, 0xa294, 0x0003, 0xa286,
+ 0x0002, 0x00c0, 0x9627, 0x684f, 0x0040, 0x0078, 0x9631, 0xa286,
+ 0x0001, 0x00c0, 0x962f, 0x684f, 0x0080, 0x0078, 0x9631, 0x684f,
+ 0x0000, 0x20a9, 0x000a, 0x2001, 0xa890, 0xad90, 0x0015, 0x200c,
+ 0x810f, 0x2112, 0x8000, 0x8210, 0x00f0, 0x9637, 0x200c, 0x6982,
+ 0x8000, 0x200c, 0x697e, 0x1078, 0x4982, 0x027f, 0x047f, 0x157f,
+ 0x0f7f, 0x0e7f, 0x0d7f, 0x007c, 0x6013, 0x0100, 0x6003, 0x0001,
+ 0x6007, 0x0041, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0078, 0x9645,
+ 0x2069, 0xa892, 0x2d04, 0xa084, 0xff00, 0xa086, 0x1200, 0x00c0,
+ 0x9677, 0x2069, 0xa880, 0x686c, 0xa084, 0x00ff, 0x017e, 0x6110,
+ 0xa18c, 0x0700, 0xa10d, 0x6112, 0x017f, 0x6003, 0x0001, 0x6007,
+ 0x0043, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0078, 0x9645, 0x6013,
+ 0x0200, 0x6003, 0x0001, 0x6007, 0x0041, 0x1078, 0x5bf8, 0x1078,
+ 0x6109, 0x0078, 0x9645, 0x6013, 0x0300, 0x0078, 0x9689, 0x6013,
+ 0x0100, 0x6003, 0x0001, 0x6007, 0x0041, 0x1078, 0x5bf8, 0x1078,
+ 0x6109, 0x0078, 0x9645, 0x6013, 0x0500, 0x0078, 0x9689, 0x6013,
+ 0x0600, 0x0078, 0x9658, 0x6013, 0x0200, 0x0078, 0x9658, 0xa186,
+ 0x0013, 0x00c0, 0x96b1, 0x6004, 0xa08a, 0x0040, 0x1048, 0x1328,
+ 0xa08a, 0x0053, 0x10c8, 0x1328, 0xa082, 0x0040, 0x2008, 0x0079,
+ 0x9725, 0xa186, 0x0051, 0x0040, 0x96be, 0xa186, 0x0047, 0x00c0,
+ 0x96d7, 0x6004, 0xa086, 0x0041, 0x0040, 0x96e5, 0x2001, 0x0109,
+ 0x2004, 0xd084, 0x0040, 0x96e5, 0x127e, 0x2091, 0x2200, 0x007e,
+ 0x017e, 0x027e, 0x1078, 0x5ad2, 0x027f, 0x017f, 0x007f, 0x127f,
+ 0x6000, 0xa086, 0x0002, 0x00c0, 0x96e5, 0x0078, 0x976a, 0xa186,
+ 0x0027, 0x0040, 0x96df, 0xa186, 0x0014, 0x10c0, 0x1328, 0x6004,
+ 0xa082, 0x0040, 0x2008, 0x0079, 0x96e8, 0x1078, 0x7583, 0x007c,
+ 0x96fb, 0x96fd, 0x96fd, 0x96fb, 0x96fb, 0x96fb, 0x96fb, 0x96fb,
+ 0x96fb, 0x96fb, 0x96fb, 0x96fb, 0x96fb, 0x96fb, 0x96fb, 0x96fb,
+ 0x96fb, 0x96fb, 0x96fb, 0x1078, 0x1328, 0x1078, 0x6010, 0x1078,
+ 0x6109, 0x037e, 0x0d7e, 0x6010, 0xa06d, 0x0040, 0x9722, 0xad84,
+ 0xf000, 0x0040, 0x9722, 0x6003, 0x0002, 0x6018, 0x2004, 0xd0bc,
+ 0x00c0, 0x9722, 0x2019, 0x0004, 0x1078, 0x9e70, 0x6013, 0x0000,
+ 0x6014, 0xa005, 0x00c0, 0x9720, 0x2001, 0xa5a1, 0x2004, 0x6016,
+ 0x6003, 0x0007, 0x0d7f, 0x037f, 0x007c, 0x9738, 0x9757, 0x9741,
+ 0x9764, 0x9738, 0x9738, 0x9738, 0x9738, 0x9738, 0x9738, 0x9738,
+ 0x9738, 0x9738, 0x9738, 0x9738, 0x9738, 0x9738, 0x9738, 0x9738,
+ 0x1078, 0x1328, 0x6010, 0xa088, 0x0013, 0x2104, 0xa085, 0x0400,
+ 0x200a, 0x1078, 0x6010, 0x6010, 0xa080, 0x0013, 0x2004, 0xd0b4,
+ 0x0040, 0x9752, 0x6003, 0x0007, 0x2009, 0x0043, 0x1078, 0x756c,
+ 0x0078, 0x9754, 0x6003, 0x0002, 0x1078, 0x6109, 0x007c, 0x1078,
+ 0x6010, 0x1078, 0xa0c6, 0x00c0, 0x9761, 0x1078, 0x5a41, 0x1078,
+ 0x753d, 0x1078, 0x6109, 0x007c, 0x1078, 0x6010, 0x2009, 0x0041,
+ 0x0078, 0x98c1, 0xa182, 0x0040, 0x0079, 0x976e, 0x9781, 0x9783,
+ 0x9781, 0x9781, 0x9781, 0x9781, 0x9781, 0x9784, 0x9781, 0x9781,
+ 0x9781, 0x9781, 0x9781, 0x9781, 0x9781, 0x9781, 0x9781, 0x978f,
+ 0x9781, 0x1078, 0x1328, 0x007c, 0x6003, 0x0004, 0x6110, 0x20e1,
+ 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x1078, 0x15ec, 0x007c, 0x0d7e,
+ 0x1078, 0x5a41, 0x0d7f, 0x1078, 0xa134, 0x1078, 0x753d, 0x007c,
+ 0xa182, 0x0040, 0x0079, 0x979c, 0x97af, 0x97af, 0x97af, 0x97af,
+ 0x97af, 0x97af, 0x97af, 0x97b1, 0x97af, 0x97b4, 0x97df, 0x97af,
+ 0x97af, 0x97af, 0x97af, 0x97df, 0x97af, 0x97af, 0x97af, 0x1078,
+ 0x1328, 0x1078, 0x7583, 0x007c, 0x1078, 0x60b8, 0x1078, 0x61d3,
+ 0x6010, 0x0d7e, 0x2068, 0x684c, 0xd0fc, 0x0040, 0x97ca, 0xa08c,
+ 0x0003, 0xa18e, 0x0002, 0x0040, 0x97d2, 0x2009, 0x0041, 0x0d7f,
+ 0x0078, 0x98c1, 0x6003, 0x0007, 0x6017, 0x0000, 0x1078, 0x5a41,
+ 0x0d7f, 0x007c, 0x1078, 0xa0c6, 0x0040, 0x97d8, 0x0d7f, 0x007c,
+ 0x1078, 0x5a41, 0x1078, 0x753d, 0x0d7f, 0x0078, 0x97d1, 0x037e,
+ 0x1078, 0x60b8, 0x1078, 0x61d3, 0x6010, 0x0d7e, 0x2068, 0x6018,
+ 0x2004, 0xd0bc, 0x0040, 0x97ff, 0x684c, 0xa084, 0x0003, 0xa086,
+ 0x0002, 0x0040, 0x97fb, 0x687c, 0x632c, 0xa31a, 0x632e, 0x6880,
+ 0x6328, 0xa31b, 0x632a, 0x6003, 0x0002, 0x0078, 0x9810, 0x2019,
+ 0x0004, 0x1078, 0x9e70, 0x6014, 0xa005, 0x00c0, 0x980c, 0x2001,
+ 0xa5a1, 0x2004, 0x8003, 0x6016, 0x6013, 0x0000, 0x6003, 0x0007,
+ 0x0d7f, 0x037f, 0x007c, 0xa186, 0x0013, 0x00c0, 0x9821, 0x6004,
+ 0xa086, 0x0042, 0x10c0, 0x1328, 0x1078, 0x6010, 0x1078, 0x6109,
+ 0x007c, 0xa186, 0x0027, 0x0040, 0x9829, 0xa186, 0x0014, 0x00c0,
+ 0x9839, 0x6004, 0xa086, 0x0042, 0x10c0, 0x1328, 0x2001, 0x0007,
+ 0x1078, 0x4472, 0x1078, 0x6010, 0x1078, 0x8c01, 0x1078, 0x6109,
+ 0x007c, 0xa182, 0x0040, 0x0079, 0x983d, 0x9850, 0x9850, 0x9850,
+ 0x9850, 0x9850, 0x9850, 0x9850, 0x9852, 0x985e, 0x9850, 0x9850,
+ 0x9850, 0x9850, 0x9850, 0x9850, 0x9850, 0x9850, 0x9850, 0x9850,
+ 0x1078, 0x1328, 0x037e, 0x047e, 0x20e1, 0x0005, 0x3d18, 0x3e20,
+ 0x2c10, 0x1078, 0x15ec, 0x047f, 0x037f, 0x007c, 0x6010, 0x0d7e,
+ 0x2068, 0x6810, 0x6a14, 0x6118, 0x210c, 0xd1bc, 0x0040, 0x987d,
+ 0x6124, 0xd1f4, 0x00c0, 0x987d, 0x007e, 0x047e, 0x057e, 0x6c7c,
+ 0xa422, 0x6d80, 0x2200, 0xa52b, 0x602c, 0xa420, 0x642e, 0x6028,
+ 0xa529, 0x652a, 0x057f, 0x047f, 0x007f, 0xa20d, 0x00c0, 0x9891,
+ 0x684c, 0xd0fc, 0x0040, 0x9889, 0x2009, 0x0041, 0x0d7f, 0x0078,
+ 0x98c1, 0x6003, 0x0007, 0x6017, 0x0000, 0x1078, 0x5a41, 0x0d7f,
+ 0x007c, 0x007e, 0x0f7e, 0x2c78, 0x1078, 0x4893, 0x0f7f, 0x007f,
+ 0x0040, 0x989e, 0x6003, 0x0002, 0x0d7f, 0x007c, 0x2009, 0xa30d,
+ 0x210c, 0xd19c, 0x0040, 0x98a8, 0x6003, 0x0007, 0x0078, 0x98aa,
+ 0x6003, 0x0006, 0x1078, 0x98b0, 0x1078, 0x5a43, 0x0d7f, 0x007c,
+ 0xd2fc, 0x0040, 0x98bc, 0x8002, 0x8000, 0x8212, 0xa291, 0x0000,
+ 0x2009, 0x0009, 0x0078, 0x98be, 0x2009, 0x0015, 0x6a6a, 0x6866,
+ 0x007c, 0xa182, 0x0040, 0x0048, 0x98c7, 0x0079, 0x98d4, 0xa186,
+ 0x0013, 0x0040, 0x98cf, 0xa186, 0x0014, 0x10c0, 0x1328, 0x6024,
+ 0xd0dc, 0x1040, 0x1328, 0x007c, 0x98e7, 0x98ee, 0x98fa, 0x9906,
+ 0x98e7, 0x98e7, 0x98e7, 0x9915, 0x98e7, 0x98e9, 0x98e9, 0x98e7,
+ 0x98e7, 0x98e7, 0x98e7, 0x98e7, 0x98e7, 0x98e7, 0x98e7, 0x1078,
+ 0x1328, 0x6024, 0xd0dc, 0x1040, 0x1328, 0x007c, 0x6003, 0x0001,
+ 0x6106, 0x1078, 0x5bf8, 0x127e, 0x2091, 0x8000, 0x1078, 0x6109,
+ 0x127f, 0x007c, 0x6003, 0x0001, 0x6106, 0x1078, 0x5bf8, 0x127e,
+ 0x2091, 0x8000, 0x1078, 0x6109, 0x127f, 0x007c, 0x6003, 0x0003,
+ 0x6106, 0x2c10, 0x1078, 0x1cab, 0x127e, 0x2091, 0x8000, 0x1078,
+ 0x5c64, 0x1078, 0x61d3, 0x127f, 0x007c, 0xa016, 0x1078, 0x15ec,
+ 0x007c, 0x127e, 0x2091, 0x8000, 0x037e, 0x0d7e, 0xa182, 0x0040,
+ 0x1079, 0x9926, 0x0d7f, 0x037f, 0x127f, 0x007c, 0x9936, 0x9938,
+ 0x994d, 0x996c, 0x9936, 0x9936, 0x9936, 0x9984, 0x9936, 0x9936,
+ 0x9936, 0x9936, 0x9936, 0x9936, 0x9936, 0x9936, 0x1078, 0x1328,
+ 0x6010, 0x2068, 0x684c, 0xd0fc, 0x0040, 0x9962, 0xa09c, 0x0003,
+ 0xa39e, 0x0003, 0x0040, 0x9962, 0x6003, 0x0001, 0x6106, 0x1078,
+ 0x5bf8, 0x1078, 0x6109, 0x0078, 0x9987, 0x6010, 0x2068, 0x684c,
+ 0xd0fc, 0x0040, 0x9962, 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0040,
+ 0x9962, 0x6003, 0x0001, 0x6106, 0x1078, 0x5bf8, 0x1078, 0x6109,
+ 0x0078, 0x9987, 0x6013, 0x0000, 0x6017, 0x0000, 0x2019, 0x0004,
+ 0x1078, 0x9e70, 0x0078, 0x9987, 0x6010, 0x2068, 0x684c, 0xd0fc,
+ 0x0040, 0x9962, 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0040, 0x9962,
+ 0x6003, 0x0003, 0x6106, 0x2c10, 0x1078, 0x1cab, 0x1078, 0x5c64,
+ 0x1078, 0x61d3, 0x0078, 0x9987, 0xa016, 0x1078, 0x15ec, 0x007c,
+ 0x1078, 0x6010, 0x6110, 0x81ff, 0x0040, 0x9999, 0x0d7e, 0x2168,
+ 0x1078, 0xa181, 0x037e, 0x2019, 0x0029, 0x1078, 0x9e70, 0x037f,
+ 0x0d7f, 0x1078, 0x8c01, 0x1078, 0x6109, 0x007c, 0x1078, 0x60b8,
+ 0x6110, 0x81ff, 0x0040, 0x99af, 0x0d7e, 0x2168, 0x1078, 0xa181,
+ 0x037e, 0x2019, 0x0029, 0x1078, 0x9e70, 0x037f, 0x0d7f, 0x1078,
+ 0x8c01, 0x1078, 0x61d3, 0x007c, 0xa182, 0x0085, 0x0079, 0x99b8,
+ 0x99c1, 0x99bf, 0x99bf, 0x99cd, 0x99bf, 0x99bf, 0x99bf, 0x1078,
+ 0x1328, 0x6003, 0x000b, 0x6106, 0x1078, 0x5bf8, 0x127e, 0x2091,
+ 0x8000, 0x1078, 0x6109, 0x127f, 0x007c, 0x027e, 0x0e7e, 0x1078,
+ 0xa0bf, 0x0040, 0x99d7, 0x1078, 0x753d, 0x0078, 0x99f3, 0x2071,
+ 0xa880, 0x7224, 0x6212, 0x7220, 0x1078, 0x9d10, 0x0040, 0x99e4,
+ 0x6007, 0x0086, 0x0078, 0x99ed, 0x6007, 0x0087, 0x7224, 0xa296,
+ 0xffff, 0x00c0, 0x99ed, 0x6007, 0x0086, 0x6003, 0x0001, 0x1078,
+ 0x5bf8, 0x1078, 0x6109, 0x0e7f, 0x027f, 0x007c, 0xa186, 0x0013,
+ 0x00c0, 0x9a07, 0x6004, 0xa08a, 0x0085, 0x1048, 0x1328, 0xa08a,
+ 0x008c, 0x10c8, 0x1328, 0xa082, 0x0085, 0x0079, 0x9a1e, 0xa186,
+ 0x0027, 0x0040, 0x9a13, 0xa186, 0x0014, 0x0040, 0x9a13, 0x1078,
+ 0x7583, 0x0078, 0x9a1d, 0x2001, 0x0007, 0x1078, 0x4472, 0x1078,
+ 0x6010, 0x1078, 0x8c01, 0x1078, 0x6109, 0x007c, 0x9a25, 0x9a27,
+ 0x9a27, 0x9a25, 0x9a25, 0x9a25, 0x9a25, 0x1078, 0x1328, 0x1078,
+ 0x6010, 0x1078, 0x8c01, 0x1078, 0x6109, 0x007c, 0xa182, 0x0085,
+ 0x1048, 0x1328, 0xa182, 0x008c, 0x10c8, 0x1328, 0xa182, 0x0085,
+ 0x0079, 0x9a3a, 0x9a41, 0x9a41, 0x9a41, 0x9a43, 0x9a41, 0x9a41,
+ 0x9a41, 0x1078, 0x1328, 0x007c, 0xa186, 0x0013, 0x0040, 0x9a54,
+ 0xa186, 0x0014, 0x0040, 0x9a54, 0xa186, 0x0027, 0x0040, 0x9a54,
+ 0x1078, 0x7583, 0x0078, 0x9a5a, 0x1078, 0x6010, 0x1078, 0x8c01,
+ 0x1078, 0x6109, 0x007c, 0x037e, 0x1078, 0xa134, 0x603f, 0x0000,
+ 0x2019, 0x000b, 0x1078, 0x9a6a, 0x601f, 0x0006, 0x6003, 0x0007,
+ 0x037f, 0x007c, 0x127e, 0x037e, 0x2091, 0x8000, 0x087e, 0x2c40,
+ 0x097e, 0x2049, 0x0000, 0x1078, 0x7058, 0x097f, 0x087f, 0x00c0,
+ 0x9aa5, 0x077e, 0x2c38, 0x1078, 0x7105, 0x077f, 0x00c0, 0x9aa5,
+ 0x6000, 0xa086, 0x0000, 0x0040, 0x9aa5, 0x601c, 0xa086, 0x0007,
+ 0x0040, 0x9aa5, 0x0d7e, 0x6000, 0xa086, 0x0004, 0x00c0, 0x9a96,
+ 0x1078, 0xa134, 0x601f, 0x0007, 0x1078, 0x1749, 0x6010, 0x2068,
+ 0x1078, 0x8a44, 0x0040, 0x9a9e, 0x1078, 0x9e70, 0x0d7f, 0x6013,
+ 0x0000, 0x1078, 0xa134, 0x601f, 0x0007, 0x037f, 0x127f, 0x007c,
+ 0x0f7e, 0x0c7e, 0x037e, 0x157e, 0x2079, 0xa880, 0x7938, 0x783c,
+ 0x1078, 0x24e3, 0x00c0, 0x9af6, 0x017e, 0x0c7e, 0x1078, 0x4501,
+ 0x00c0, 0x9af6, 0x2011, 0xa890, 0xac98, 0x000a, 0x20a9, 0x0004,
+ 0x1078, 0x7e55, 0x00c0, 0x9af6, 0x017f, 0x027f, 0x027e, 0x017e,
+ 0x2019, 0x0029, 0x1078, 0x71e0, 0x1078, 0x5d53, 0x077e, 0x2039,
+ 0x0000, 0x1078, 0x5c78, 0x077f, 0x017f, 0x077e, 0x2039, 0x0000,
+ 0x1078, 0x9c38, 0x077f, 0x1078, 0x471b, 0x027e, 0x6204, 0xa294,
+ 0xff00, 0x8217, 0xa286, 0x0006, 0x0040, 0x9aea, 0xa286, 0x0004,
+ 0x00c0, 0x9aed, 0x62a0, 0x1078, 0x28d5, 0x027f, 0x017f, 0x1078,
+ 0x4235, 0x6612, 0x6516, 0xa006, 0x0078, 0x9af8, 0x0c7f, 0x017f,
+ 0x157f, 0x037f, 0x0c7f, 0x0f7f, 0x007c, 0x0c7e, 0x0d7e, 0x0e7e,
+ 0x017e, 0x2009, 0xa31f, 0x2104, 0xa086, 0x0074, 0x00c0, 0x9b60,
+ 0x2069, 0xa88e, 0x690c, 0xa182, 0x0100, 0x0048, 0x9b50, 0x6908,
+ 0xa184, 0x8000, 0x0040, 0x9b5c, 0x6018, 0x2070, 0x7010, 0xa084,
+ 0x00ff, 0x0040, 0x9b1f, 0x7000, 0xd0f4, 0x0040, 0x9b23, 0xa184,
+ 0x0800, 0x0040, 0x9b5c, 0x6910, 0xa18a, 0x0001, 0x0048, 0x9b54,
+ 0x6914, 0x2069, 0xa8ae, 0x6904, 0x81ff, 0x00c0, 0x9b48, 0x690c,
+ 0xa182, 0x0100, 0x0048, 0x9b50, 0x6908, 0x81ff, 0x00c0, 0x9b4c,
+ 0x6910, 0xa18a, 0x0001, 0x0048, 0x9b54, 0x6918, 0xa18a, 0x0001,
+ 0x0048, 0x9b5c, 0x0078, 0x9b66, 0x6013, 0x0100, 0x0078, 0x9b62,
+ 0x6013, 0x0300, 0x0078, 0x9b62, 0x6013, 0x0500, 0x0078, 0x9b62,
+ 0x6013, 0x0700, 0x0078, 0x9b62, 0x6013, 0x0900, 0x0078, 0x9b62,
+ 0x6013, 0x0b00, 0x0078, 0x9b62, 0x6013, 0x0f00, 0x0078, 0x9b62,
+ 0x6013, 0x2d00, 0xa085, 0x0001, 0x0078, 0x9b67, 0xa006, 0x017f,
+ 0x0e7f, 0x0d7f, 0x0c7f, 0x007c, 0x0c7e, 0x0d7e, 0x027e, 0x037e,
+ 0x157e, 0x6218, 0x2268, 0x6b04, 0xa394, 0x00ff, 0xa286, 0x0006,
+ 0x0040, 0x9b90, 0xa286, 0x0004, 0x0040, 0x9b90, 0xa394, 0xff00,
+ 0x8217, 0xa286, 0x0006, 0x0040, 0x9b90, 0xa286, 0x0004, 0x0040,
+ 0x9b90, 0x0c7e, 0x2d60, 0x1078, 0x4513, 0x0c7f, 0x0078, 0x9bcb,
+ 0x2011, 0xa896, 0xad98, 0x000a, 0x20a9, 0x0004, 0x1078, 0x7e55,
+ 0x00c0, 0x9bcc, 0x2011, 0xa89a, 0xad98, 0x0006, 0x20a9, 0x0004,
+ 0x1078, 0x7e55, 0x00c0, 0x9bcc, 0x047e, 0x017e, 0x6aa0, 0xa294,
+ 0x00ff, 0x8227, 0xa006, 0x2009, 0xa352, 0x210c, 0xd1a4, 0x0040,
+ 0x9bb8, 0x2009, 0x0029, 0x1078, 0x9ec0, 0x6800, 0xc0e5, 0x6802,
+ 0x2019, 0x0029, 0x1078, 0x5d53, 0x077e, 0x2039, 0x0000, 0x1078,
+ 0x5c78, 0x2c08, 0x1078, 0x9c38, 0x077f, 0x2001, 0x0007, 0x1078,
+ 0x4472, 0x017f, 0x047f, 0xa006, 0x157f, 0x037f, 0x027f, 0x0d7f,
+ 0x0c7f, 0x007c, 0x0d7e, 0x2069, 0xa88e, 0x6800, 0xa086, 0x0800,
+ 0x0040, 0x9bde, 0x6013, 0x0000, 0x0078, 0x9bdf, 0xa006, 0x0d7f,
+ 0x007c, 0x0c7e, 0x0f7e, 0x017e, 0x027e, 0x037e, 0x157e, 0x2079,
+ 0xa88c, 0x7930, 0x7834, 0x1078, 0x24e3, 0x00c0, 0x9c05, 0x1078,
+ 0x4501, 0x00c0, 0x9c05, 0x2011, 0xa890, 0xac98, 0x000a, 0x20a9,
+ 0x0004, 0x1078, 0x7e55, 0x00c0, 0x9c05, 0x2011, 0xa894, 0xac98,
+ 0x0006, 0x20a9, 0x0004, 0x1078, 0x7e55, 0x157f, 0x037f, 0x027f,
+ 0x017f, 0x0f7f, 0x0c7f, 0x007c, 0x0c7e, 0x007e, 0x017e, 0x027e,
+ 0x037e, 0x157e, 0x2011, 0xa883, 0x2204, 0x8211, 0x220c, 0x1078,
+ 0x24e3, 0x00c0, 0x9c31, 0x1078, 0x4501, 0x00c0, 0x9c31, 0x2011,
+ 0xa896, 0xac98, 0x000a, 0x20a9, 0x0004, 0x1078, 0x7e55, 0x00c0,
+ 0x9c31, 0x2011, 0xa89a, 0xac98, 0x0006, 0x20a9, 0x0004, 0x1078,
+ 0x7e55, 0x157f, 0x037f, 0x027f, 0x017f, 0x007f, 0x0c7f, 0x007c,
+ 0x0e7e, 0x0c7e, 0x087e, 0x077e, 0x067e, 0x057e, 0x047e, 0x027e,
+ 0x127e, 0x2091, 0x8000, 0x2740, 0x2029, 0xa5b4, 0x252c, 0x2021,
+ 0xa5ba, 0x2424, 0x2061, 0xaa00, 0x2071, 0xa300, 0x7644, 0x7060,
+ 0x81ff, 0x0040, 0x9c59, 0x8001, 0xa602, 0x00c8, 0x9cc3, 0x0078,
+ 0x9c5c, 0xa606, 0x0040, 0x9cc3, 0x2100, 0xac06, 0x0040, 0x9cb9,
+ 0x1078, 0x9ee5, 0x0040, 0x9cb9, 0x671c, 0xa786, 0x0001, 0x0040,
+ 0x9cde, 0xa786, 0x0004, 0x0040, 0x9cde, 0xa786, 0x0007, 0x0040,
+ 0x9cb9, 0x2500, 0xac06, 0x0040, 0x9cb9, 0x2400, 0xac06, 0x0040,
+ 0x9cb9, 0x1078, 0x9ef9, 0x00c0, 0x9cb9, 0x88ff, 0x0040, 0x9c84,
+ 0x6020, 0xa906, 0x00c0, 0x9cb9, 0x0d7e, 0x6000, 0xa086, 0x0004,
+ 0x00c0, 0x9c8e, 0x017e, 0x1078, 0x1749, 0x017f, 0xa786, 0x0008,
+ 0x00c0, 0x9c9d, 0x1078, 0x8c3b, 0x00c0, 0x9c9d, 0x1078, 0x7a05,
+ 0x0d7f, 0x1078, 0x8c01, 0x0078, 0x9cb9, 0x6010, 0x2068, 0x1078,
+ 0x8a44, 0x0040, 0x9cb6, 0xa786, 0x0003, 0x00c0, 0x9ccd, 0x6837,
+ 0x0103, 0x6b4a, 0x6847, 0x0000, 0x1078, 0xa181, 0x017e, 0x1078,
+ 0x8cb8, 0x1078, 0x4982, 0x017f, 0x1078, 0x8bf4, 0x0d7f, 0x1078,
+ 0x8c01, 0xace0, 0x0010, 0x2001, 0xa315, 0x2004, 0xac02, 0x00c8,
+ 0x9cc3, 0x0078, 0x9c4c, 0x127f, 0x027f, 0x047f, 0x057f, 0x067f,
+ 0x077f, 0x087f, 0x0c7f, 0x0e7f, 0x007c, 0xa786, 0x0006, 0x00c0,
+ 0x9ca7, 0xa386, 0x0005, 0x0040, 0x9cdb, 0x1078, 0xa181, 0x1078,
+ 0x9e70, 0x0078, 0x9cb6, 0x0d7f, 0x0078, 0x9cb9, 0x1078, 0x9ef9,
+ 0x00c0, 0x9cb9, 0x81ff, 0x0040, 0x9cb9, 0xa180, 0x0001, 0x2004,
+ 0xa086, 0x0018, 0x0040, 0x9cf3, 0xa180, 0x0001, 0x2004, 0xa086,
+ 0x002d, 0x00c0, 0x9cb9, 0x6000, 0xa086, 0x0002, 0x00c0, 0x9cb9,
+ 0x1078, 0x8c27, 0x0040, 0x9d04, 0x1078, 0x8c3b, 0x00c0, 0x9cb9,
+ 0x1078, 0x7a05, 0x0078, 0x9d0c, 0x1078, 0x2839, 0x1078, 0x8c3b,
+ 0x00c0, 0x9d0c, 0x1078, 0x7a05, 0x1078, 0x8c01, 0x0078, 0x9cb9,
+ 0x0c7e, 0x0e7e, 0x017e, 0x2c08, 0x2170, 0x1078, 0x9e8c, 0x017f,
+ 0x0040, 0x9d1f, 0x601c, 0xa084, 0x000f, 0x1079, 0x9d22, 0x0e7f,
+ 0x0c7f, 0x007c, 0x9d2a, 0x9d2a, 0x9d2a, 0x9d2a, 0x9d2a, 0x9d2a,
+ 0x9d2c, 0x9d2a, 0xa006, 0x007c, 0x047e, 0x017e, 0x7018, 0xa080,
+ 0x0028, 0x2024, 0xa4a4, 0x00ff, 0x8427, 0x2c00, 0x2009, 0x0020,
+ 0x1078, 0x9ec0, 0x017f, 0x047f, 0x037e, 0x2019, 0x0002, 0x1078,
+ 0x9a6a, 0x037f, 0xa085, 0x0001, 0x007c, 0x2001, 0x0001, 0x1078,
+ 0x442b, 0x157e, 0x017e, 0x027e, 0x037e, 0x20a9, 0x0004, 0x2019,
+ 0xa305, 0x2011, 0xa896, 0x1078, 0x7e55, 0x037f, 0x027f, 0x017f,
+ 0x157f, 0xa005, 0x007c, 0x0f7e, 0x0e7e, 0x0c7e, 0x087e, 0x077e,
+ 0x067e, 0x027e, 0x127e, 0x2091, 0x8000, 0x2740, 0x2061, 0xaa00,
+ 0x2079, 0x0001, 0x8fff, 0x0040, 0x9dc3, 0x2071, 0xa300, 0x7644,
+ 0x7060, 0x8001, 0xa602, 0x00c8, 0x9dc3, 0x88ff, 0x0040, 0x9d7e,
+ 0x2800, 0xac06, 0x00c0, 0x9db9, 0x2079, 0x0000, 0x1078, 0x9ee5,
+ 0x0040, 0x9db9, 0x2400, 0xac06, 0x0040, 0x9db9, 0x671c, 0xa786,
+ 0x0006, 0x00c0, 0x9db9, 0xa786, 0x0007, 0x0040, 0x9db9, 0x88ff,
+ 0x00c0, 0x9d9d, 0x6018, 0xa206, 0x00c0, 0x9db9, 0x85ff, 0x0040,
+ 0x9d9d, 0x6020, 0xa106, 0x00c0, 0x9db9, 0x0d7e, 0x6000, 0xa086,
+ 0x0004, 0x00c0, 0x9da9, 0x1078, 0xa134, 0x601f, 0x0007, 0x1078,
+ 0x1749, 0x6010, 0x2068, 0x1078, 0x8a44, 0x0040, 0x9db3, 0x047e,
+ 0x1078, 0x9e70, 0x047f, 0x0d7f, 0x1078, 0x8c01, 0x88ff, 0x00c0,
+ 0x9dcd, 0xace0, 0x0010, 0x2001, 0xa315, 0x2004, 0xac02, 0x00c8,
+ 0x9dc3, 0x0078, 0x9d6a, 0xa006, 0x127f, 0x027f, 0x067f, 0x077f,
+ 0x087f, 0x0c7f, 0x0e7f, 0x0f7f, 0x007c, 0xa8c5, 0x0001, 0x0078,
+ 0x9dc4, 0x077e, 0x057e, 0x087e, 0x2041, 0x0000, 0x2029, 0x0001,
+ 0x2c20, 0x2019, 0x0002, 0x6218, 0x097e, 0x2049, 0x0000, 0x1078,
+ 0x7058, 0x097f, 0x087f, 0x2039, 0x0000, 0x1078, 0x7105, 0x1078,
+ 0x9d5b, 0x057f, 0x077f, 0x007c, 0x027e, 0x047e, 0x057e, 0x077e,
+ 0x0c7e, 0x157e, 0x2c20, 0x2128, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0x017e, 0x037e, 0x1078, 0x4501, 0x00c0, 0x9e14, 0x2c10, 0x057e,
+ 0x087e, 0x2041, 0x0000, 0x2508, 0x2029, 0x0001, 0x097e, 0x2049,
+ 0x0000, 0x1078, 0x7058, 0x097f, 0x087f, 0x2039, 0x0000, 0x1078,
+ 0x7105, 0x1078, 0x9d5b, 0x057f, 0x037f, 0x017f, 0x8108, 0x00f0,
+ 0x9df8, 0x157f, 0x0c7f, 0x077f, 0x057f, 0x047f, 0x027f, 0x007c,
+ 0x077e, 0x057e, 0x6218, 0x087e, 0x2041, 0x0000, 0x2029, 0x0001,
+ 0x2019, 0x0048, 0x097e, 0x2049, 0x0000, 0x1078, 0x7058, 0x097f,
+ 0x087f, 0x2039, 0x0000, 0x1078, 0x7105, 0x2c20, 0x1078, 0x9d5b,
+ 0x057f, 0x077f, 0x007c, 0x027e, 0x047e, 0x057e, 0x077e, 0x0c7e,
+ 0x157e, 0x2c20, 0x20a9, 0x007f, 0x2009, 0x0000, 0x017e, 0x037e,
+ 0x1078, 0x4501, 0x00c0, 0x9e64, 0x2c10, 0x087e, 0x2041, 0x0000,
+ 0x2828, 0x047e, 0x2021, 0x0001, 0x1078, 0xa111, 0x047f, 0x097e,
+ 0x2049, 0x0000, 0x1078, 0x7058, 0x097f, 0x087f, 0x2039, 0x0000,
+ 0x1078, 0x7105, 0x1078, 0x9d5b, 0x037f, 0x017f, 0x8108, 0x00f0,
+ 0x9e46, 0x157f, 0x0c7f, 0x077f, 0x057f, 0x047f, 0x027f, 0x007c,
+ 0x017e, 0x0f7e, 0xad82, 0xca00, 0x0048, 0x9e89, 0xad82, 0xffff,
+ 0x00c8, 0x9e89, 0x6800, 0xa07d, 0x0040, 0x9e86, 0x6803, 0x0000,
+ 0x6b52, 0x1078, 0x4982, 0x2f68, 0x0078, 0x9e7a, 0x6b52, 0x1078,
+ 0x4982, 0x0f7f, 0x017f, 0x007c, 0x0e7e, 0x047e, 0x037e, 0x2061,
+ 0xaa00, 0x2071, 0xa300, 0x7444, 0x7060, 0x8001, 0xa402, 0x00c8,
+ 0x9ebb, 0x2100, 0xac06, 0x0040, 0x9ead, 0x6000, 0xa086, 0x0000,
+ 0x0040, 0x9ead, 0x6008, 0xa206, 0x00c0, 0x9ead, 0x6018, 0xa1a0,
+ 0x0006, 0x2424, 0xa406, 0x0040, 0x9eb7, 0xace0, 0x0010, 0x2001,
+ 0xa315, 0x2004, 0xac02, 0x00c8, 0x9ebb, 0x0078, 0x9e91, 0xa085,
+ 0x0001, 0x0078, 0x9ebc, 0xa006, 0x037f, 0x047f, 0x0e7f, 0x007c,
+ 0x0d7e, 0x007e, 0x1078, 0x1381, 0x007f, 0x1040, 0x1328, 0x6837,
+ 0x010d, 0x685e, 0x027e, 0x2010, 0x1078, 0x8a30, 0x2001, 0x0000,
+ 0x0040, 0x9ed6, 0x2200, 0xa080, 0x0008, 0x2004, 0x027f, 0x684a,
+ 0x6956, 0x6c46, 0x684f, 0x0000, 0xa006, 0x68b2, 0x6802, 0x683a,
+ 0x685a, 0x1078, 0x4982, 0x0d7f, 0x007c, 0x6700, 0xa786, 0x0000,
+ 0x0040, 0x9ef8, 0xa786, 0x0001, 0x0040, 0x9ef8, 0xa786, 0x000a,
+ 0x0040, 0x9ef8, 0xa786, 0x0009, 0x0040, 0x9ef8, 0xa085, 0x0001,
+ 0x007c, 0x0e7e, 0x6018, 0x2070, 0x70a0, 0xa206, 0x0e7f, 0x007c,
+ 0x017e, 0x6004, 0xa08e, 0x001e, 0x00c0, 0x9f1a, 0x8007, 0x6130,
+ 0xa18c, 0x00ff, 0xa105, 0x6032, 0x6007, 0x0085, 0x6003, 0x000b,
+ 0x601f, 0x0005, 0x2001, 0xa5a1, 0x2004, 0x6016, 0x1078, 0x5bf8,
+ 0x1078, 0x6109, 0x017f, 0x007c, 0x0005, 0x0005, 0x007c, 0x6024,
+ 0xd0e4, 0x0040, 0x9f30, 0xd0cc, 0x0040, 0x9f2a, 0x1078, 0x8cfa,
+ 0x0078, 0x9f30, 0x1078, 0xa134, 0x1078, 0x5a41, 0x1078, 0x753d,
+ 0x007c, 0xa280, 0x0007, 0x2004, 0xa084, 0x000f, 0x0079, 0x9f38,
+ 0x9f41, 0x9f41, 0x9f41, 0x9f43, 0x9f41, 0x9f43, 0x9f43, 0x9f41,
+ 0x9f43, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, 0xa280, 0x0007,
+ 0x2004, 0xa084, 0x000f, 0x0079, 0x9f4d, 0x9f56, 0x9f56, 0x9f56,
+ 0x9f56, 0x9f56, 0x9f56, 0x9f61, 0x9f56, 0x9f56, 0x6007, 0x003b,
+ 0x602b, 0x0009, 0x6013, 0x2a00, 0x6003, 0x0001, 0x1078, 0x5bf8,
+ 0x007c, 0x0c7e, 0x2260, 0x1078, 0xa134, 0x603f, 0x0000, 0x6024,
+ 0xc0f4, 0xc0cc, 0x6026, 0x0c7f, 0x0d7e, 0x2268, 0xa186, 0x0007,
+ 0x00c0, 0x9fc2, 0x6810, 0xa005, 0x0040, 0x9f7f, 0xa080, 0x0013,
+ 0x2004, 0xd0fc, 0x00c0, 0x9f7f, 0x0d7f, 0x0078, 0x9f56, 0x6007,
+ 0x003a, 0x6003, 0x0001, 0x1078, 0x5bf8, 0x1078, 0x6109, 0x0c7e,
+ 0x2d60, 0x6100, 0xa186, 0x0002, 0x00c0, 0xa050, 0x6010, 0xa005,
+ 0x00c0, 0x9f99, 0x6000, 0xa086, 0x0007, 0x10c0, 0x1328, 0x0078,
+ 0xa050, 0xa08c, 0xf000, 0x00c0, 0x9fa5, 0x0078, 0x9fa5, 0x2068,
+ 0x6800, 0xa005, 0x00c0, 0x9f9f, 0x2d00, 0xa080, 0x0013, 0x2004,
+ 0xa084, 0x0003, 0xa086, 0x0002, 0x00c0, 0x9fbe, 0x6010, 0x2068,
+ 0x684c, 0xc0dc, 0xc0f4, 0x684e, 0x6850, 0xc0f4, 0xc0fc, 0x6852,
+ 0x2009, 0x0043, 0x1078, 0x98c1, 0x0078, 0xa050, 0x2009, 0x0041,
+ 0x0078, 0xa04a, 0xa186, 0x0005, 0x00c0, 0xa009, 0x6810, 0xa080,
+ 0x0013, 0x2004, 0xd0bc, 0x00c0, 0x9fd0, 0x0d7f, 0x0078, 0x9f56,
+ 0xd0b4, 0x0040, 0x9fd8, 0xd0fc, 0x1040, 0x1328, 0x0078, 0x9f72,
+ 0x6007, 0x003a, 0x6003, 0x0001, 0x1078, 0x5bf8, 0x1078, 0x6109,
+ 0x0c7e, 0x2d60, 0x6100, 0xa186, 0x0002, 0x0040, 0x9feb, 0xa186,
+ 0x0004, 0x00c0, 0xa050, 0x2071, 0xa5e1, 0x7000, 0xa086, 0x0003,
+ 0x00c0, 0x9ff8, 0x7004, 0xac06, 0x00c0, 0x9ff8, 0x7003, 0x0000,
+ 0x6810, 0xa080, 0x0013, 0x200c, 0xc1f4, 0xc1dc, 0x2102, 0x8000,
+ 0x200c, 0xc1f4, 0xc1fc, 0xc1bc, 0x2102, 0x2009, 0x0042, 0x0078,
+ 0xa04a, 0x037e, 0x0d7e, 0x0d7e, 0x1078, 0x1381, 0x037f, 0x1040,
+ 0x1328, 0x6837, 0x010d, 0x6803, 0x0000, 0x683b, 0x0000, 0x685b,
+ 0x0000, 0x6b5e, 0x6857, 0x0045, 0x2c00, 0x6862, 0x6034, 0x6872,
+ 0x2360, 0x6024, 0xc0dd, 0x6026, 0x6018, 0xa080, 0x0028, 0x2004,
+ 0xa084, 0x00ff, 0x8007, 0x6320, 0x6b4a, 0x6846, 0x684f, 0x0000,
+ 0x6d6a, 0x6e66, 0x686f, 0x0001, 0x1078, 0x4982, 0x2019, 0x0045,
+ 0x6008, 0x2068, 0x1078, 0x9a6a, 0x2d00, 0x600a, 0x601f, 0x0006,
+ 0x6003, 0x0007, 0x6017, 0x0000, 0x603f, 0x0000, 0x0d7f, 0x037f,
+ 0x0078, 0xa051, 0x603f, 0x0000, 0x6003, 0x0007, 0x1078, 0x98c1,
+ 0x0c7f, 0x0d7f, 0x007c, 0xa186, 0x0013, 0x00c0, 0xa05d, 0x6004,
+ 0xa082, 0x0085, 0x2008, 0x0079, 0xa077, 0xa186, 0x0027, 0x00c0,
+ 0xa070, 0x1078, 0x6010, 0x037e, 0x0d7e, 0x6010, 0x2068, 0x2019,
+ 0x0004, 0x1078, 0x9e70, 0x0d7f, 0x037f, 0x1078, 0x6109, 0x007c,
+ 0xa186, 0x0014, 0x0040, 0xa061, 0x1078, 0x7583, 0x007c, 0xa080,
+ 0xa07e, 0xa07e, 0xa07e, 0xa07e, 0xa07e, 0xa080, 0x1078, 0x1328,
+ 0x1078, 0x6010, 0x6003, 0x000c, 0x1078, 0x6109, 0x007c, 0xa182,
+ 0x008c, 0x00c8, 0xa091, 0xa182, 0x0085, 0x0048, 0xa091, 0x0079,
+ 0xa094, 0x1078, 0x7583, 0x007c, 0xa09b, 0xa09b, 0xa09b, 0xa09b,
+ 0xa09d, 0xa0bc, 0xa09b, 0x1078, 0x1328, 0x0d7e, 0x2c68, 0x1078,
+ 0x74d7, 0x0040, 0xa0b7, 0x6003, 0x0001, 0x6007, 0x001e, 0x2009,
+ 0xa88e, 0x210c, 0x6136, 0x2009, 0xa88f, 0x210c, 0x613a, 0x600b,
+ 0xffff, 0x6918, 0x611a, 0x601f, 0x0004, 0x1078, 0x5bf8, 0x2d60,
+ 0x1078, 0x753d, 0x0d7f, 0x007c, 0x1078, 0x753d, 0x007c, 0x0e7e,
+ 0x6018, 0x2070, 0x7000, 0xd0ec, 0x0e7f, 0x007c, 0x6010, 0xa080,
+ 0x0013, 0x200c, 0xd1ec, 0x0040, 0xa110, 0x2001, 0xa371, 0x2004,
+ 0xd0ec, 0x0040, 0xa110, 0x6003, 0x0002, 0x6024, 0xc0e5, 0x6026,
+ 0xd1ac, 0x0040, 0xa0ee, 0x0f7e, 0x2c78, 0x1078, 0x488f, 0x0f7f,
+ 0x0040, 0xa0ee, 0x2001, 0xa5a2, 0x2004, 0x603e, 0x2009, 0xa371,
+ 0x210c, 0xd1f4, 0x00c0, 0xa10e, 0x0078, 0xa100, 0x2009, 0xa371,
+ 0x210c, 0xd1f4, 0x0040, 0xa0fa, 0x6024, 0xc0e4, 0x6026, 0xa006,
+ 0x0078, 0xa110, 0x2001, 0xa5a2, 0x200c, 0x8103, 0xa100, 0x603e,
+ 0x6018, 0xa088, 0x002b, 0x2104, 0xa005, 0x0040, 0xa10b, 0xa088,
+ 0x0003, 0x0078, 0xa103, 0x2c0a, 0x600f, 0x0000, 0xa085, 0x0001,
+ 0x007c, 0x017e, 0x0c7e, 0x0e7e, 0x6120, 0xa2f0, 0x002b, 0x2e04,
+ 0x2060, 0x8cff, 0x0040, 0xa130, 0x84ff, 0x00c0, 0xa123, 0x6020,
+ 0xa106, 0x00c0, 0xa12b, 0x600c, 0x2072, 0x1078, 0x5a41, 0x1078,
+ 0x753d, 0x0078, 0xa12d, 0xacf0, 0x0003, 0x2e64, 0x0078, 0xa119,
+ 0x0e7f, 0x0c7f, 0x017f, 0x007c, 0x0d7e, 0x6018, 0xa0e8, 0x002b,
+ 0x2d04, 0xa005, 0x0040, 0xa146, 0xac06, 0x0040, 0xa144, 0x2d04,
+ 0xa0e8, 0x0003, 0x0078, 0xa138, 0x600c, 0x206a, 0x0d7f, 0x007c,
+ 0x027e, 0x037e, 0x157e, 0x2011, 0xa325, 0x2204, 0xa084, 0x00ff,
+ 0x2019, 0xa88e, 0x2334, 0xa636, 0x00c0, 0xa174, 0x8318, 0x2334,
+ 0x2204, 0xa084, 0xff00, 0xa636, 0x00c0, 0xa174, 0x2011, 0xa890,
+ 0x6018, 0xa098, 0x000a, 0x20a9, 0x0004, 0x1078, 0x7e55, 0x00c0,
+ 0xa174, 0x2011, 0xa894, 0x6018, 0xa098, 0x0006, 0x20a9, 0x0004,
+ 0x1078, 0x7e55, 0x00c0, 0xa174, 0x157f, 0x037f, 0x027f, 0x007c,
+ 0x0e7e, 0x2071, 0xa300, 0x1078, 0x41f5, 0x1078, 0x260d, 0x0e7f,
+ 0x007c, 0x0e7e, 0x6018, 0x2070, 0x7000, 0xd0fc, 0x0040, 0xa18a,
+ 0x1078, 0xa18c, 0x0e7f, 0x007c, 0x6850, 0xc0e5, 0x6852, 0x007c,
+ 0x0e7e, 0x0c7e, 0x077e, 0x067e, 0x057e, 0x047e, 0x027e, 0x017e,
+ 0x127e, 0x2091, 0x8000, 0x2029, 0xa5b4, 0x252c, 0x2021, 0xa5ba,
+ 0x2424, 0x2061, 0xaa00, 0x2071, 0xa300, 0x7644, 0x7060, 0xa606,
+ 0x0040, 0xa1e4, 0x671c, 0xa786, 0x0001, 0x0040, 0xa1b3, 0xa786,
+ 0x0008, 0x00c0, 0xa1da, 0x2500, 0xac06, 0x0040, 0xa1da, 0x2400,
+ 0xac06, 0x0040, 0xa1da, 0x1078, 0x9ee5, 0x0040, 0xa1da, 0x1078,
+ 0x9ef9, 0x00c0, 0xa1da, 0x6000, 0xa086, 0x0004, 0x00c0, 0xa1cc,
+ 0x017e, 0x1078, 0x1749, 0x017f, 0x1078, 0x8c27, 0x00c0, 0xa1d2,
+ 0x1078, 0x2839, 0x1078, 0x8c3b, 0x00c0, 0xa1d8, 0x1078, 0x7a05,
+ 0x1078, 0x8c01, 0xace0, 0x0010, 0x2001, 0xa315, 0x2004, 0xac02,
+ 0x00c8, 0xa1e4, 0x0078, 0xa1a3, 0x127f, 0x017f, 0x027f, 0x047f,
+ 0x057f, 0x067f, 0x077f, 0x0c7f, 0x0e7f, 0x007c, 0x127e, 0x007e,
+ 0x0e7e, 0x2091, 0x8000, 0x2071, 0xa340, 0xd5a4, 0x0040, 0xa1fb,
+ 0x7034, 0x8000, 0x7036, 0xd5b4, 0x0040, 0xa201, 0x7030, 0x8000,
+ 0x7032, 0xd5ac, 0x0040, 0xa208, 0x2071, 0xa34a, 0x1078, 0xa237,
+ 0x0e7f, 0x007f, 0x127f, 0x007c, 0x127e, 0x007e, 0x0e7e, 0x2091,
+ 0x8000, 0x2071, 0xa340, 0xd5a4, 0x0040, 0xa219, 0x7034, 0x8000,
+ 0x7036, 0xd5b4, 0x0040, 0xa21f, 0x7030, 0x8000, 0x7032, 0xd5ac,
+ 0x0040, 0xa226, 0x2071, 0xa34a, 0x1078, 0xa237, 0x0e7f, 0x007f,
+ 0x127f, 0x007c, 0x127e, 0x007e, 0x0e7e, 0x2091, 0x8000, 0x2071,
+ 0xa342, 0x1078, 0xa237, 0x0e7f, 0x007f, 0x127f, 0x007c, 0x2e04,
+ 0x8000, 0x2072, 0x00c8, 0xa240, 0x8e70, 0x2e04, 0x8000, 0x2072,
+ 0x007c, 0x0e7e, 0x2071, 0xa340, 0x1078, 0xa237, 0x0e7f, 0x007c,
+ 0x0e7e, 0x2071, 0xa344, 0x1078, 0xa237, 0x0e7f, 0x007c, 0x0001,
+ 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100,
+ 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 0x6286
+};
+
+/************************************************************************
+ * *
+ * --- ISP2200 Initiator/Target Firmware --- *
+ * with Fabric (Public Loop), Point-point, and *
+ * expanded LUN addressing for FCTAPE *
+ * *
+ ************************************************************************
+ Copyright (C) 2000 and 2100 Qlogic Corporation
+ (www.qlogic.com)
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+************************************************************************/
+
+/*
+ * Firmware Version 2.01.27 (11:07 Dec 18, 2000)
+ */
+
+static unsigned short risc_code_length2200 = 0x9cbf;
+static unsigned short risc_code2200[] = {
+ 0x0470, 0x0000, 0x0000, 0x9cbf, 0x0000, 0x0002, 0x0001, 0x001b,
+ 0x0017, 0x2043, 0x4f50, 0x5952, 0x4947, 0x4854, 0x2031, 0x3939,
+ 0x3920, 0x514c, 0x4f47, 0x4943, 0x2043, 0x4f52, 0x504f, 0x5241,
+ 0x5449, 0x4f4e, 0x2049, 0x5350, 0x3232, 0x3030, 0x2046, 0x6972,
+ 0x6d77, 0x6172, 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030,
+ 0x322e, 0x3031, 0x2e32, 0x3720, 0x2020, 0x2020, 0x2400, 0x20c1,
+ 0x0005, 0x2001, 0x017f, 0x2003, 0x0000, 0x20c9, 0xb1ff, 0x2091,
+ 0x2000, 0x2059, 0x0000, 0x2b78, 0x7823, 0x0004, 0x2089, 0x27b5,
+ 0x2051, 0xad00, 0x2a70, 0x2029, 0xe400, 0x2031, 0xffff, 0x2039,
+ 0xe3e9, 0x2021, 0x0200, 0x0804, 0x1449, 0x20a1, 0xacbf, 0xa00e,
+ 0x20a9, 0x0741, 0x41a4, 0x3400, 0x755e, 0x7662, 0x775a, 0x7466,
+ 0x746a, 0x20a1, 0xb400, 0x7160, 0x810d, 0x810d, 0x810d, 0x810d,
+ 0xa18c, 0x000f, 0x2001, 0x000b, 0xa112, 0xa00e, 0x21a8, 0x41a4,
+ 0x3400, 0x8211, 0x1dd8, 0x7160, 0x3400, 0xa102, 0x0120, 0x0218,
+ 0x20a8, 0xa00e, 0x41a4, 0x3800, 0xd08c, 0x01d8, 0x2009, 0xad00,
+ 0x810d, 0x810d, 0x810d, 0x810d, 0xa18c, 0x000f, 0x2001, 0x0001,
+ 0xa112, 0x20a1, 0x1000, 0xa00e, 0x21a8, 0x41a4, 0x8211, 0x1de0,
+ 0x2009, 0xad00, 0x3400, 0xa102, 0x0120, 0x0218, 0x20a8, 0xa00e,
+ 0x41a4, 0x080c, 0x13fc, 0x080c, 0x1613, 0x080c, 0x17ac, 0x080c,
+ 0x1e67, 0x080c, 0x492e, 0x080c, 0x801a, 0x080c, 0x159c, 0x080c,
+ 0x2ce6, 0x080c, 0x5a01, 0x080c, 0x5045, 0x080c, 0x6487, 0x080c,
+ 0x236a, 0x080c, 0x6686, 0x080c, 0x5fae, 0x080c, 0x226b, 0x080c,
+ 0x2338, 0x2091, 0x3009, 0x7823, 0x0000, 0x1004, 0x10c5, 0x7820,
+ 0xa086, 0x0002, 0x1150, 0x7823, 0x4000, 0x0e04, 0x10bd, 0x781b,
+ 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x2a70, 0x7003, 0x0000,
+ 0x2a70, 0x7000, 0xa08e, 0x0003, 0x1158, 0x080c, 0x3c98, 0x080c,
+ 0x2d0d, 0x080c, 0x5a4f, 0x080c, 0x51f4, 0x080c, 0x64a2, 0x0c80,
+ 0x000b, 0x0c98, 0x10e4, 0x10e5, 0x1203, 0x10e2, 0x12cc, 0x13f9,
+ 0x13fa, 0x13fb, 0x080c, 0x14f6, 0x0005, 0x0126, 0x00f6, 0x2091,
+ 0x8000, 0x7000, 0xa086, 0x0001, 0x1904, 0x11d1, 0x080c, 0x1569,
+ 0x080c, 0x574f, 0x0150, 0x080c, 0x5775, 0x1580, 0x2079, 0x0100,
+ 0x7828, 0xa085, 0x1800, 0x782a, 0x0448, 0x080c, 0x569a, 0x7000,
+ 0xa086, 0x0001, 0x1904, 0x11d1, 0x7088, 0xa086, 0x0028, 0x1904,
+ 0x11d1, 0x2079, 0x0100, 0x7827, 0xffff, 0x7a28, 0xa295, 0x1e2f,
+ 0x7a2a, 0x2011, 0x566e, 0x080c, 0x650d, 0x2011, 0x567b, 0x080c,
+ 0x650d, 0x2011, 0x481b, 0x080c, 0x650d, 0x2011, 0x8030, 0x2019,
+ 0x0000, 0x7087, 0x0000, 0x080c, 0x1d0f, 0x00e8, 0x080c, 0x41d1,
+ 0x2079, 0x0100, 0x7844, 0xa005, 0x1904, 0x11d1, 0x2011, 0x481b,
+ 0x080c, 0x650d, 0x2011, 0x567b, 0x080c, 0x650d, 0x080c, 0x1d0f,
+ 0x2001, 0xaf8c, 0x2004, 0x780e, 0x7840, 0xa084, 0xfffb, 0x7842,
+ 0x2011, 0x8010, 0x73c8, 0x080c, 0x3c5c, 0x7238, 0xc284, 0x723a,
+ 0x2001, 0xad0c, 0x200c, 0xc1ac, 0x2102, 0x080c, 0x79bd, 0x2011,
+ 0x0004, 0x080c, 0x959c, 0x080c, 0x4f71, 0x080c, 0x574f, 0x0158,
+ 0x080c, 0x4917, 0x0140, 0x7087, 0x0001, 0x70c3, 0x0000, 0x080c,
+ 0x436e, 0x0804, 0x11d1, 0x080c, 0x502d, 0x0120, 0x7a0c, 0xc2b4,
+ 0x7a0e, 0x0050, 0x080c, 0x9937, 0x70d0, 0xd09c, 0x1128, 0x709c,
+ 0xa005, 0x0110, 0x080c, 0x48f5, 0x70db, 0x0000, 0x70d7, 0x0000,
+ 0x72d0, 0x080c, 0x574f, 0x1178, 0x2011, 0x0000, 0x0016, 0x080c,
+ 0x2744, 0x2019, 0xaf8e, 0x211a, 0x001e, 0x704f, 0xffff, 0x7053,
+ 0x00ef, 0x7073, 0x0000, 0x2079, 0xad51, 0x7804, 0xd0ac, 0x0108,
+ 0xc295, 0x72d2, 0x080c, 0x574f, 0x0118, 0xa296, 0x0004, 0x0508,
+ 0x2011, 0x0001, 0x080c, 0x959c, 0x7097, 0x0000, 0x709b, 0xffff,
+ 0x7003, 0x0002, 0x00fe, 0x080c, 0x28fa, 0x2011, 0x0005, 0x080c,
+ 0x7adf, 0x080c, 0x6c50, 0x080c, 0x574f, 0x0148, 0x00c6, 0x2061,
+ 0x0100, 0x0016, 0x080c, 0x2744, 0x61e2, 0x001e, 0x00ce, 0x012e,
+ 0x00d0, 0x7097, 0x0000, 0x709b, 0xffff, 0x7003, 0x0002, 0x2011,
+ 0x0005, 0x080c, 0x7adf, 0x080c, 0x6c50, 0x080c, 0x574f, 0x0148,
+ 0x00c6, 0x2061, 0x0100, 0x0016, 0x080c, 0x2744, 0x61e2, 0x001e,
+ 0x00ce, 0x00fe, 0x012e, 0x0005, 0x00c6, 0x080c, 0x574f, 0x1118,
+ 0x20a9, 0x0100, 0x0010, 0x20a9, 0x0082, 0x080c, 0x574f, 0x1118,
+ 0x2009, 0x0000, 0x0010, 0x2009, 0x007e, 0x0016, 0x0026, 0x0036,
+ 0x2110, 0x0026, 0x2019, 0x0029, 0x080c, 0x7cf4, 0x002e, 0x080c,
+ 0xac07, 0x003e, 0x002e, 0x001e, 0x080c, 0x2bc9, 0x8108, 0x1f04,
+ 0x11e5, 0x00ce, 0x706f, 0x0000, 0x7070, 0xa084, 0x00ff, 0x7072,
+ 0x709f, 0x0000, 0x0005, 0x0126, 0x2091, 0x8000, 0x7000, 0xa086,
+ 0x0002, 0x1904, 0x12ca, 0x7098, 0xa086, 0xffff, 0x0130, 0x080c,
+ 0x28fa, 0x080c, 0x6c50, 0x0804, 0x12ca, 0x70d0, 0xd0ac, 0x1110,
+ 0xd09c, 0x0540, 0xd084, 0x0530, 0x0006, 0x0016, 0x2001, 0x0103,
+ 0x2009, 0xaf8c, 0x210c, 0x2102, 0x001e, 0x000e, 0xd08c, 0x01d0,
+ 0x70d4, 0xa086, 0xffff, 0x0190, 0x080c, 0x2a56, 0x080c, 0x6c50,
+ 0x70d0, 0xd094, 0x1904, 0x12ca, 0x2011, 0x0001, 0x2019, 0x0000,
+ 0x080c, 0x2a8c, 0x080c, 0x6c50, 0x0804, 0x12ca, 0x70d8, 0xa005,
+ 0x1904, 0x12ca, 0x7094, 0xa005, 0x1904, 0x12ca, 0x70d0, 0xd0a4,
+ 0x0118, 0xd0b4, 0x0904, 0x12ca, 0x080c, 0x502d, 0x1904, 0x12ca,
+ 0x2001, 0xad52, 0x2004, 0xd0ac, 0x01c8, 0x0156, 0x00c6, 0x20a9,
+ 0x007f, 0x2009, 0x0000, 0x0016, 0x080c, 0x4cdc, 0x1118, 0x6000,
+ 0xd0ec, 0x1138, 0x001e, 0x8108, 0x1f04, 0x125b, 0x00ce, 0x015e,
+ 0x0028, 0x001e, 0x00ce, 0x015e, 0x0804, 0x12ca, 0x0006, 0x0016,
+ 0x2001, 0x0103, 0x2009, 0xaf8c, 0x210c, 0x2102, 0x001e, 0x000e,
+ 0xa006, 0x2009, 0x0700, 0x20a9, 0x0002, 0x20a1, 0xafb5, 0x40a1,
+ 0x706c, 0x8007, 0x7170, 0x810f, 0x20a9, 0x0002, 0x40a1, 0x2009,
+ 0x0000, 0x080c, 0x14dc, 0x2001, 0x0000, 0x810f, 0x20a9, 0x0002,
+ 0x40a1, 0xa006, 0x2009, 0x0200, 0x20a9, 0x0002, 0x20a1, 0xafc5,
+ 0x40a1, 0x7030, 0xc08c, 0x7032, 0x7003, 0x0003, 0x709b, 0xffff,
+ 0x080c, 0x1562, 0xa006, 0x080c, 0x261e, 0x080c, 0x3cce, 0x00f6,
+ 0x2079, 0x0100, 0x080c, 0x5775, 0x0150, 0x080c, 0x574f, 0x7828,
+ 0x0118, 0xa084, 0xe1ff, 0x0010, 0xa084, 0xffdf, 0x782a, 0x00fe,
+ 0x2001, 0xafc8, 0x2004, 0xa086, 0x0005, 0x1120, 0x2011, 0x0000,
+ 0x080c, 0x7adf, 0x2011, 0x0000, 0x080c, 0x7ae9, 0x080c, 0x6c50,
+ 0x080c, 0x6d0d, 0x012e, 0x0005, 0x0016, 0x0046, 0x00f6, 0x0126,
+ 0x2091, 0x8000, 0x2079, 0x0100, 0x2009, 0xad33, 0x2104, 0xa005,
+ 0x1110, 0x080c, 0x2770, 0x2009, 0x00f7, 0x080c, 0x48de, 0x7940,
+ 0xa18c, 0x0010, 0x7942, 0x7924, 0xd1b4, 0x0110, 0x7827, 0x0040,
+ 0xd19c, 0x0110, 0x7827, 0x0008, 0x0006, 0x0036, 0x0156, 0x7954,
+ 0xd1ac, 0x1904, 0x133a, 0x080c, 0x5761, 0x0158, 0x080c, 0x5775,
+ 0x1128, 0x2001, 0xaf9d, 0x2003, 0x0000, 0x0070, 0x080c, 0x5757,
+ 0x0dc0, 0x2001, 0xaf9d, 0x2003, 0xaaaa, 0x2001, 0xaf9e, 0x2003,
+ 0x0001, 0x080c, 0x569a, 0x0058, 0x080c, 0x574f, 0x0140, 0x2009,
+ 0x00f8, 0x080c, 0x48de, 0x7843, 0x0090, 0x7843, 0x0010, 0x20a9,
+ 0x09c4, 0x7820, 0xd09c, 0x1138, 0x080c, 0x574f, 0x0138, 0x7824,
+ 0xd0ac, 0x1904, 0x13e0, 0x1f04, 0x1319, 0x0070, 0x7824, 0x080c,
+ 0x576b, 0x0118, 0xd0ac, 0x1904, 0x13e0, 0xa084, 0x1800, 0x0d98,
+ 0x7003, 0x0001, 0x0804, 0x13e0, 0x2001, 0x0001, 0x080c, 0x261e,
+ 0x0804, 0x13ef, 0x7850, 0xa084, 0x0180, 0x7852, 0x782f, 0x0020,
+ 0x20a9, 0x0046, 0x1d04, 0x1342, 0x2091, 0x6000, 0x1f04, 0x1342,
+ 0x7850, 0xa084, 0x0180, 0xa085, 0x0400, 0x7852, 0x782f, 0x0000,
+ 0x080c, 0x5761, 0x0158, 0x080c, 0x5775, 0x1128, 0x2001, 0xaf9d,
+ 0x2003, 0x0000, 0x0070, 0x080c, 0x5757, 0x0dc0, 0x2001, 0xaf9d,
+ 0x2003, 0xaaaa, 0x2001, 0xaf9e, 0x2003, 0x0001, 0x080c, 0x569a,
+ 0x0020, 0x2009, 0x00f8, 0x080c, 0x48de, 0x20a9, 0x000e, 0xe000,
+ 0x1f04, 0x136f, 0x7850, 0xa084, 0x0180, 0xa085, 0x1400, 0x7852,
+ 0x080c, 0x574f, 0x0120, 0x7843, 0x0090, 0x7843, 0x0010, 0x2021,
+ 0xe678, 0x2019, 0xea60, 0x7820, 0xd09c, 0x1558, 0x080c, 0x574f,
+ 0x05b8, 0x7824, 0xd0ac, 0x1904, 0x13e0, 0x080c, 0x5775, 0x1508,
+ 0x0046, 0x2021, 0x0190, 0x8421, 0x1df0, 0x004e, 0x8421, 0x11c8,
+ 0x7827, 0x0048, 0x20a9, 0x01f4, 0x1d04, 0x139c, 0x2091, 0x6000,
+ 0x1f04, 0x139c, 0x7824, 0xa084, 0x0068, 0x15a8, 0x2001, 0xaf9d,
+ 0x2003, 0xaaaa, 0x2001, 0xaf9e, 0x2003, 0x0001, 0x7003, 0x0001,
+ 0x0478, 0x8319, 0x1980, 0x2009, 0xad33, 0x2104, 0x8000, 0x200a,
+ 0xa084, 0xfff0, 0x0120, 0x200b, 0x0000, 0x080c, 0x2770, 0x00d8,
+ 0x080c, 0x5761, 0x1140, 0xa4a2, 0x0064, 0x1128, 0x080c, 0x5726,
+ 0x7003, 0x0001, 0x00a8, 0x7827, 0x1800, 0xe000, 0xe000, 0x7824,
+ 0x080c, 0x576b, 0x0110, 0xd0ac, 0x1158, 0xa084, 0x1800, 0x09c8,
+ 0x7003, 0x0001, 0x0028, 0x2001, 0x0001, 0x080c, 0x261e, 0x0048,
+ 0x2001, 0xad33, 0x2003, 0x0000, 0x7827, 0x0048, 0x7828, 0xc09d,
+ 0x782a, 0x7850, 0xa084, 0x0180, 0xa085, 0x0400, 0x7852, 0x015e,
+ 0x003e, 0x000e, 0x080c, 0x1539, 0x012e, 0x00fe, 0x004e, 0x001e,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x2a70, 0x2001, 0xaf9d, 0x2003,
+ 0x0000, 0x7087, 0x0000, 0x2009, 0x0100, 0x2104, 0xa082, 0x0002,
+ 0x0218, 0x704f, 0xffff, 0x0010, 0x704f, 0x0000, 0x7057, 0xffff,
+ 0x706f, 0x0000, 0x7073, 0x0000, 0x080c, 0x9937, 0x2061, 0xaf8d,
+ 0x6003, 0x0909, 0x6007, 0x0000, 0x600b, 0x8800, 0x600f, 0x0200,
+ 0x6013, 0x00ff, 0x6017, 0x0003, 0x601b, 0x0000, 0x601f, 0x07d0,
+ 0x2061, 0xaf95, 0x6003, 0x8000, 0x6007, 0x0000, 0x600b, 0x0000,
+ 0x600f, 0x0200, 0x6013, 0x00ff, 0x6017, 0x0000, 0x601b, 0x0001,
+ 0x601f, 0x0000, 0x2061, 0xafa6, 0x6003, 0x514c, 0x6007, 0x4f47,
+ 0x600b, 0x4943, 0x600f, 0x2020, 0x2001, 0xad27, 0x2003, 0x0000,
+ 0x0005, 0x04a0, 0x2011, 0x0000, 0x81ff, 0x0570, 0xa186, 0x0001,
+ 0x1148, 0x2031, 0x8fff, 0x2039, 0xcc01, 0x2021, 0x0100, 0x2029,
+ 0xcc00, 0x00e8, 0xa186, 0x0002, 0x1118, 0x2011, 0x0000, 0x00b8,
+ 0xa186, 0x0005, 0x1118, 0x2011, 0x0001, 0x0088, 0xa186, 0x0009,
+ 0x1118, 0x2011, 0x0002, 0x0058, 0xa186, 0x000a, 0x1118, 0x2011,
+ 0x0002, 0x0028, 0xa186, 0x0055, 0x1110, 0x2011, 0x0003, 0x3800,
+ 0xa084, 0xfffc, 0xa205, 0x20c0, 0x0804, 0x104d, 0xa00e, 0x2011,
+ 0x0003, 0x2019, 0x1485, 0x0804, 0x14d6, 0x2019, 0xaaaa, 0x2061,
+ 0xffff, 0x2c14, 0x2362, 0xe000, 0xe000, 0x2c04, 0xa306, 0x2262,
+ 0x1110, 0xc1b5, 0xc1a5, 0x2011, 0x0000, 0x2019, 0x1498, 0x04f0,
+ 0x2019, 0xaaaa, 0x2061, 0xffff, 0x2c14, 0x2362, 0xe000, 0xe000,
+ 0x2c1c, 0x2061, 0x7fff, 0xe000, 0xe000, 0x2c04, 0x2061, 0xffff,
+ 0x2262, 0xa306, 0x0110, 0xc18d, 0x0008, 0xc185, 0x2011, 0x0002,
+ 0x2019, 0x14b3, 0x0418, 0x2061, 0xffff, 0x2019, 0xaaaa, 0x2c14,
+ 0x2362, 0xe000, 0xe000, 0x2c04, 0x2262, 0xa306, 0x1180, 0x2c14,
+ 0x2362, 0xe000, 0xe000, 0x2c1c, 0x2061, 0x7fff, 0x2c04, 0x2061,
+ 0xffff, 0x2262, 0xa306, 0x1110, 0xc195, 0x0008, 0xc19d, 0x2011,
+ 0x0001, 0x2019, 0x14d4, 0x0010, 0x0804, 0x144a, 0x3800, 0xa084,
+ 0xfffc, 0xa205, 0x20c0, 0x0837, 0x2011, 0x0000, 0x080c, 0x4cdc,
+ 0x1178, 0x6004, 0xa0c4, 0x00ff, 0xa8c6, 0x0006, 0x0128, 0xa0c4,
+ 0xff00, 0xa8c6, 0x0600, 0x1120, 0xa186, 0x0080, 0x0108, 0x8210,
+ 0x8108, 0xa186, 0x0100, 0x1d50, 0x2208, 0x0005, 0x2091, 0x8000,
+ 0x0e04, 0x14f8, 0x0006, 0x0016, 0x2079, 0x0000, 0x7818, 0xd084,
+ 0x1de8, 0x001e, 0x792e, 0x000e, 0x782a, 0x000e, 0x7826, 0x3900,
+ 0x783a, 0x7823, 0x8002, 0x781b, 0x0001, 0x2091, 0x5000, 0x0126,
+ 0x0156, 0x0146, 0x20a9, 0x0010, 0x20a1, 0xb0c8, 0x2091, 0x2000,
+ 0x40a1, 0x20a9, 0x0010, 0x2091, 0x2200, 0x40a1, 0x20a9, 0x0010,
+ 0x2091, 0x2400, 0x40a1, 0x20a9, 0x0010, 0x2091, 0x2600, 0x40a1,
+ 0x20a9, 0x0010, 0x2091, 0x2800, 0x40a1, 0x014e, 0x015e, 0x012e,
+ 0x2079, 0xad00, 0x7803, 0x0005, 0x2091, 0x4080, 0x04c9, 0x0cf8,
+ 0x0005, 0x0006, 0x080c, 0x1584, 0x1518, 0x00f6, 0x2079, 0xad23,
+ 0x2f04, 0x8000, 0x207a, 0xa082, 0x000f, 0x0258, 0xa006, 0x207a,
+ 0x2079, 0xad25, 0x2f04, 0xa084, 0x0001, 0xa086, 0x0001, 0x207a,
+ 0x0070, 0x2079, 0xad25, 0x2f7c, 0x8fff, 0x1128, 0x2001, 0x0c03,
+ 0x2003, 0x0040, 0x0020, 0x2001, 0x0c03, 0x2003, 0x00c0, 0x00fe,
+ 0x000e, 0x0005, 0x0409, 0x1120, 0x2001, 0x0c03, 0x2003, 0x0080,
+ 0x0005, 0x00d1, 0x1120, 0x2001, 0x0c03, 0x2003, 0x0040, 0x0005,
+ 0x0006, 0x0091, 0x1178, 0x2001, 0x0c03, 0x2003, 0x0040, 0x2009,
+ 0x0fff, 0x00a1, 0x2001, 0x0c03, 0x2003, 0x0080, 0x2009, 0x0fff,
+ 0x0069, 0x0c88, 0x000e, 0x0005, 0x00c6, 0x2061, 0x0c00, 0x2c04,
+ 0xa084, 0x00ff, 0xa086, 0x00aa, 0x00ce, 0x0005, 0x0156, 0x0126,
+ 0xa18c, 0x0fff, 0x21a8, 0x1d04, 0x1593, 0x2091, 0x6000, 0x1f04,
+ 0x1593, 0x012e, 0x015e, 0x0005, 0x2071, 0xad00, 0x715c, 0x712e,
+ 0x2021, 0x0001, 0xa190, 0x0030, 0xa298, 0x0030, 0x0240, 0x7060,
+ 0xa302, 0x1228, 0x220a, 0x2208, 0x2310, 0x8420, 0x0ca8, 0x3800,
+ 0xd08c, 0x0148, 0x7060, 0xa086, 0xad00, 0x0128, 0x7063, 0xad00,
+ 0x2011, 0x1000, 0x0c48, 0x200b, 0x0000, 0x74ae, 0x74b2, 0x0005,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0xad00, 0x70b0, 0xa0ea,
+ 0x0010, 0x0268, 0x8001, 0x70b2, 0x702c, 0x2068, 0x2d04, 0x702e,
+ 0x206b, 0x0000, 0x6807, 0x0000, 0x012e, 0x00ee, 0x0005, 0xa06e,
+ 0x0cd8, 0x00e6, 0x2071, 0xad00, 0x0126, 0x2091, 0x8000, 0x70b0,
+ 0x8001, 0x0260, 0x70b2, 0x702c, 0x2068, 0x2d04, 0x702e, 0x206b,
+ 0x0000, 0x6807, 0x0000, 0x012e, 0x00ee, 0x0005, 0xa06e, 0x0cd8,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x2071, 0xad00, 0x702c, 0x206a,
+ 0x2d00, 0x702e, 0x70b0, 0x8000, 0x70b2, 0x012e, 0x00ee, 0x0005,
+ 0x8dff, 0x0138, 0x6804, 0x6807, 0x0000, 0x0006, 0x0c49, 0x00de,
+ 0x0cb8, 0x0005, 0x00e6, 0x2071, 0xad00, 0x70b0, 0xa08a, 0x0010,
+ 0xa00d, 0x00ee, 0x0005, 0x00e6, 0x2071, 0xafec, 0x7007, 0x0000,
+ 0x701b, 0x0000, 0x701f, 0x0000, 0x2071, 0x0000, 0x7010, 0xa085,
+ 0x8004, 0x7012, 0x00ee, 0x0005, 0x00e6, 0x2270, 0x700b, 0x0000,
+ 0x2071, 0xafec, 0x7018, 0xa088, 0xaff5, 0x220a, 0x8000, 0xa084,
+ 0x0007, 0x701a, 0x7004, 0xa005, 0x1128, 0x00f6, 0x2079, 0x0010,
+ 0x0081, 0x00fe, 0x00ee, 0x0005, 0x00e6, 0x2071, 0xafec, 0x7004,
+ 0xa005, 0x1128, 0x00f6, 0x2079, 0x0010, 0x0019, 0x00fe, 0x00ee,
+ 0x0005, 0x7000, 0x0002, 0x164f, 0x16b3, 0x16d0, 0x16d0, 0x7018,
+ 0x711c, 0xa106, 0x1118, 0x7007, 0x0000, 0x0005, 0x00d6, 0xa180,
+ 0xaff5, 0x2004, 0x700a, 0x2068, 0x8108, 0xa18c, 0x0007, 0x711e,
+ 0x7803, 0x0026, 0x6824, 0x7832, 0x6828, 0x7836, 0x682c, 0x783a,
+ 0x6830, 0x783e, 0x6810, 0x700e, 0x680c, 0x7016, 0x6804, 0x00de,
+ 0xd084, 0x0120, 0x7007, 0x0001, 0x0029, 0x0005, 0x7007, 0x0002,
+ 0x00b1, 0x0005, 0x0016, 0x0026, 0x710c, 0x2011, 0x0040, 0xa182,
+ 0x0040, 0x1210, 0x2110, 0xa006, 0x700e, 0x7212, 0x8203, 0x7822,
+ 0x7803, 0x0020, 0x7803, 0x0041, 0x002e, 0x001e, 0x0005, 0x0016,
+ 0x0026, 0x0136, 0x0146, 0x0156, 0x7014, 0x2098, 0x20a1, 0x0014,
+ 0x7803, 0x0026, 0x710c, 0x2011, 0x0040, 0xa182, 0x0040, 0x1210,
+ 0x2110, 0xa006, 0x700e, 0x22a8, 0x53a6, 0x8203, 0x7822, 0x7803,
+ 0x0020, 0x3300, 0x7016, 0x7803, 0x0001, 0x015e, 0x014e, 0x013e,
+ 0x002e, 0x001e, 0x0005, 0x0136, 0x0146, 0x0156, 0x2099, 0xadf9,
+ 0x20a1, 0x0018, 0x20a9, 0x0008, 0x53a3, 0x7803, 0x0020, 0x0126,
+ 0x2091, 0x8000, 0x7803, 0x0041, 0x7007, 0x0003, 0x7000, 0xc084,
+ 0x7002, 0x700b, 0xadf4, 0x012e, 0x015e, 0x014e, 0x013e, 0x0005,
+ 0x0136, 0x0146, 0x0156, 0x2001, 0xae28, 0x209c, 0x20a1, 0x0014,
+ 0x7803, 0x0026, 0x2001, 0xae29, 0x20ac, 0x53a6, 0x2099, 0xae2a,
+ 0x20a1, 0x0018, 0x20a9, 0x0008, 0x53a3, 0x7803, 0x0020, 0x0126,
+ 0x2091, 0x8000, 0x7803, 0x0001, 0x7007, 0x0004, 0x7000, 0xc08c,
+ 0x7002, 0x700b, 0xae25, 0x012e, 0x015e, 0x014e, 0x013e, 0x0005,
+ 0x0016, 0x00e6, 0x2071, 0xafec, 0x00f6, 0x2079, 0x0010, 0x7904,
+ 0x7803, 0x0002, 0xd1fc, 0x0120, 0xa18c, 0x0700, 0x7004, 0x0023,
+ 0x00fe, 0x00ee, 0x001e, 0x0005, 0x1649, 0x1713, 0x1741, 0x176b,
+ 0x179b, 0x1712, 0x0cf8, 0xa18c, 0x0700, 0x1528, 0x0136, 0x0146,
+ 0x0156, 0x7014, 0x20a0, 0x2099, 0x0014, 0x7803, 0x0040, 0x7010,
+ 0x20a8, 0x53a5, 0x3400, 0x7016, 0x015e, 0x014e, 0x013e, 0x700c,
+ 0xa005, 0x0570, 0x7830, 0x7832, 0x7834, 0x7836, 0x080c, 0x167a,
+ 0x0005, 0x7008, 0xa080, 0x0002, 0x2003, 0x0100, 0x7007, 0x0000,
+ 0x080c, 0x1649, 0x0005, 0x7008, 0xa080, 0x0002, 0x2003, 0x0200,
+ 0x0ca8, 0xa18c, 0x0700, 0x1150, 0x700c, 0xa005, 0x0188, 0x7830,
+ 0x7832, 0x7834, 0x7836, 0x080c, 0x168f, 0x0005, 0x7008, 0xa080,
+ 0x0002, 0x2003, 0x0200, 0x7007, 0x0000, 0x080c, 0x1649, 0x0005,
+ 0x00d6, 0x7008, 0x2068, 0x7830, 0x6826, 0x7834, 0x682a, 0x7838,
+ 0x682e, 0x783c, 0x6832, 0x680b, 0x0100, 0x00de, 0x7007, 0x0000,
+ 0x080c, 0x1649, 0x0005, 0xa18c, 0x0700, 0x1540, 0x0136, 0x0146,
+ 0x0156, 0x2001, 0xadf7, 0x2004, 0xa080, 0x000d, 0x20a0, 0x2099,
+ 0x0014, 0x7803, 0x0040, 0x20a9, 0x0020, 0x53a5, 0x2001, 0xadf9,
+ 0x2004, 0xd0bc, 0x0148, 0x2001, 0xae02, 0x2004, 0xa080, 0x000d,
+ 0x20a0, 0x20a9, 0x0020, 0x53a5, 0x015e, 0x014e, 0x013e, 0x7007,
+ 0x0000, 0x080c, 0x5ae6, 0x080c, 0x1649, 0x0005, 0x2011, 0x8003,
+ 0x080c, 0x3c5c, 0x0cf8, 0xa18c, 0x0700, 0x1148, 0x2001, 0xae27,
+ 0x2003, 0x0100, 0x7007, 0x0000, 0x080c, 0x1649, 0x0005, 0x2011,
+ 0x8004, 0x080c, 0x3c5c, 0x0cf8, 0x0126, 0x2091, 0x2200, 0x2079,
+ 0x0030, 0x2071, 0xaffd, 0x7003, 0x0000, 0x700f, 0xb003, 0x7013,
+ 0xb003, 0x780f, 0x00f6, 0x7803, 0x0004, 0x012e, 0x0005, 0x6934,
+ 0xa184, 0x0007, 0x0002, 0x17cb, 0x1809, 0x17cb, 0x17cb, 0x17cb,
+ 0x17f1, 0x17d8, 0x17cf, 0xa085, 0x0001, 0x0804, 0x1823, 0x684c,
+ 0xd0bc, 0x0dc8, 0x6860, 0x682e, 0x685c, 0x682a, 0x6858, 0x04c8,
+ 0xa18c, 0x00ff, 0xa186, 0x001e, 0x1d70, 0x684c, 0xd0bc, 0x0d58,
+ 0x6860, 0x682e, 0x685c, 0x682a, 0x6804, 0x681a, 0xa080, 0x000d,
+ 0x2004, 0xa084, 0x000f, 0xa080, 0x2186, 0x2005, 0x6832, 0x6858,
+ 0x0440, 0xa18c, 0x00ff, 0xa186, 0x0015, 0x19a8, 0x684c, 0xd0ac,
+ 0x0990, 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f,
+ 0xa080, 0x2186, 0x2005, 0x6832, 0xa006, 0x682e, 0x682a, 0x6858,
+ 0x0080, 0x684c, 0xd0ac, 0x0904, 0x17cb, 0xa006, 0x682e, 0x682a,
+ 0x6858, 0xa18c, 0x000f, 0xa188, 0x2186, 0x210d, 0x6932, 0x2d08,
+ 0x691a, 0x6826, 0x684c, 0xc0dd, 0x684e, 0xa006, 0x680a, 0x697c,
+ 0x6912, 0x6980, 0x6916, 0x0005, 0x20e1, 0x0007, 0x20e1, 0x2000,
+ 0x2001, 0x020a, 0x2004, 0x82ff, 0x01a8, 0xa280, 0x0004, 0x00d6,
+ 0x206c, 0x684c, 0xd0dc, 0x1150, 0x080c, 0x17bf, 0x0138, 0x00de,
+ 0xa280, 0x0000, 0x2003, 0x0002, 0xa016, 0x0020, 0x6808, 0x8000,
+ 0x680a, 0x00de, 0x0126, 0x0046, 0x0036, 0x0026, 0x2091, 0x2200,
+ 0x002e, 0x003e, 0x004e, 0x7000, 0xa005, 0x01d0, 0x710c, 0x220a,
+ 0x8108, 0x230a, 0x8108, 0x240a, 0x8108, 0xa182, 0xb01e, 0x0210,
+ 0x2009, 0xb003, 0x710e, 0x7010, 0xa102, 0xa082, 0x0009, 0x0118,
+ 0xa080, 0x001b, 0x1118, 0x2009, 0x0138, 0x200a, 0x012e, 0x0005,
+ 0x7206, 0x2001, 0x1866, 0x0006, 0x2260, 0x0804, 0x197a, 0x0126,
+ 0x0026, 0x0036, 0x00c6, 0x0006, 0x2091, 0x2200, 0x000e, 0x004e,
+ 0x003e, 0x002e, 0x00d6, 0x00c6, 0x2460, 0x6110, 0x2168, 0x6a62,
+ 0x6b5e, 0xa005, 0x0904, 0x18c8, 0x6808, 0xa005, 0x0904, 0x18ff,
+ 0x7000, 0xa005, 0x1108, 0x0488, 0x700c, 0x7110, 0xa106, 0x1904,
+ 0x1907, 0x7004, 0xa406, 0x1548, 0x2001, 0x0005, 0x2004, 0xd08c,
+ 0x0168, 0x0046, 0x080c, 0x1a6c, 0x004e, 0x2460, 0x6010, 0xa080,
+ 0x0002, 0x2004, 0xa005, 0x0904, 0x18ff, 0x0c10, 0x2001, 0x0207,
+ 0x2004, 0xd09c, 0x1d48, 0x7804, 0xa084, 0x6000, 0x0120, 0xa086,
+ 0x6000, 0x0108, 0x0c08, 0x7818, 0x6812, 0x781c, 0x6816, 0x7803,
+ 0x0004, 0x7003, 0x0000, 0x7004, 0x2060, 0x6100, 0xa18e, 0x0004,
+ 0x1904, 0x1907, 0x2009, 0x0048, 0x080c, 0x80a7, 0x0804, 0x1907,
+ 0x6808, 0xa005, 0x05a0, 0x7000, 0xa005, 0x0588, 0x700c, 0x7110,
+ 0xa106, 0x1118, 0x7004, 0xa406, 0x1550, 0x2001, 0x0005, 0x2004,
+ 0xd08c, 0x0160, 0x0046, 0x080c, 0x1a6c, 0x004e, 0x2460, 0x6010,
+ 0xa080, 0x0002, 0x2004, 0xa005, 0x01d0, 0x0c28, 0x2001, 0x0207,
+ 0x2004, 0xd09c, 0x1d50, 0x2001, 0x0005, 0x2004, 0xd08c, 0x1d50,
+ 0x7804, 0xa084, 0x6000, 0x0118, 0xa086, 0x6000, 0x19f0, 0x7818,
+ 0x6812, 0x781c, 0x6816, 0x7803, 0x0004, 0x7003, 0x0000, 0x6100,
+ 0xa18e, 0x0004, 0x1120, 0x2009, 0x0048, 0x080c, 0x80a7, 0x00ce,
+ 0x00de, 0x012e, 0x0005, 0x00f6, 0x00e6, 0x0026, 0x0036, 0x0046,
+ 0x0056, 0x080c, 0x1d86, 0x0026, 0x0056, 0x2071, 0xaffd, 0x7000,
+ 0xa086, 0x0000, 0x0580, 0x7004, 0xac06, 0x11f8, 0x2079, 0x0030,
+ 0x7000, 0xa086, 0x0003, 0x01c8, 0x7804, 0xd0fc, 0x1198, 0x2001,
+ 0x0207, 0x2004, 0xd09c, 0x1dc0, 0x7803, 0x0004, 0x7804, 0xd0ac,
+ 0x1de8, 0x7803, 0x0002, 0x7803, 0x0009, 0x7003, 0x0003, 0x7007,
+ 0x0000, 0x0018, 0x080c, 0x1a6c, 0x08d0, 0x0156, 0x20a9, 0x0009,
+ 0x2009, 0xb003, 0x2104, 0xac06, 0x1108, 0x200a, 0xa188, 0x0003,
+ 0x1f04, 0x1942, 0x015e, 0x005e, 0x002e, 0x2001, 0x015d, 0x201c,
+ 0x831a, 0x2302, 0x2001, 0x0160, 0x2502, 0x2001, 0x0138, 0x2202,
+ 0x005e, 0x004e, 0x003e, 0x002e, 0x00ee, 0x00fe, 0x0005, 0x700c,
+ 0x7110, 0xa106, 0x0904, 0x19dd, 0x2104, 0x7006, 0x2060, 0x8108,
+ 0x211c, 0x8108, 0x2124, 0x8108, 0xa182, 0xb01e, 0x0210, 0x2009,
+ 0xb003, 0x7112, 0x700c, 0xa106, 0x1128, 0x080c, 0x2744, 0x2001,
+ 0x0138, 0x2102, 0x8cff, 0x0588, 0x6010, 0x2068, 0x2d58, 0x6828,
+ 0xa406, 0x1580, 0x682c, 0xa306, 0x1568, 0x7004, 0x2060, 0x6020,
+ 0xc0d4, 0x6022, 0x684c, 0xd0f4, 0x0128, 0x6817, 0xffff, 0x6813,
+ 0xffff, 0x00d8, 0x6850, 0xd0f4, 0x1130, 0x7803, 0x0004, 0x6810,
+ 0x781a, 0x6814, 0x781e, 0x6824, 0x2050, 0x6818, 0x2060, 0x6830,
+ 0x2040, 0x6034, 0xa0cc, 0x000f, 0x2009, 0x0011, 0x04c9, 0x0118,
+ 0x2009, 0x0001, 0x04a9, 0x2d58, 0x0005, 0x080c, 0x1ced, 0x0904,
+ 0x195f, 0x0cd0, 0x6020, 0xd0d4, 0x01b8, 0x6038, 0xa402, 0x6034,
+ 0xa303, 0x0108, 0x1288, 0x643a, 0x6336, 0x6c2a, 0x6b2e, 0x0046,
+ 0x0036, 0x2400, 0x6c7c, 0xa402, 0x6812, 0x2300, 0x6b80, 0xa303,
+ 0x6816, 0x003e, 0x004e, 0x0018, 0x080c, 0x98cb, 0x09f0, 0x601c,
+ 0xa08e, 0x0008, 0x0904, 0x1985, 0xa08e, 0x000a, 0x0904, 0x1985,
+ 0x080c, 0x21a6, 0x1990, 0x0804, 0x1985, 0x7003, 0x0000, 0x0005,
+ 0x8aff, 0x0904, 0x1a46, 0xa03e, 0x2730, 0x6850, 0xd0fc, 0x11b8,
+ 0xd0f4, 0x1528, 0x00d6, 0x2805, 0xac68, 0x2900, 0x0002, 0x1a30,
+ 0x1a15, 0x1a15, 0x1a30, 0x1a30, 0x1a29, 0x1a30, 0x1a15, 0x1a30,
+ 0x1a1a, 0x1a1a, 0x1a30, 0x1a30, 0x1a30, 0x1a21, 0x1a1a, 0x7803,
+ 0x0004, 0xc0fc, 0x6852, 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0x00d6,
+ 0xd99c, 0x0548, 0x2805, 0xac68, 0x6f08, 0x6e0c, 0x0420, 0xc0f4,
+ 0x6852, 0x6b6c, 0x6a70, 0x00d6, 0x0428, 0x6b08, 0x6a0c, 0x6d00,
+ 0x6c04, 0x00c8, 0x6b10, 0x6a14, 0x6d00, 0x6c04, 0x6f08, 0x6e0c,
+ 0x0090, 0x00de, 0x00d6, 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e,
+ 0x1138, 0x00de, 0x080c, 0x2148, 0x1904, 0x19e0, 0xa00e, 0x00b0,
+ 0x00de, 0x080c, 0x14f6, 0x7b22, 0x7a26, 0x7d32, 0x7c36, 0x7f3a,
+ 0x7e3e, 0x7902, 0x7000, 0x8000, 0x7002, 0x00de, 0x6828, 0xa300,
+ 0x682a, 0x682c, 0xa201, 0x682e, 0x080c, 0x2148, 0x0005, 0x080c,
+ 0x14f6, 0x080c, 0x1e1a, 0x7004, 0x2060, 0x00d6, 0x6010, 0x2068,
+ 0x7003, 0x0000, 0x080c, 0x1d22, 0x080c, 0x9596, 0x0170, 0x6808,
+ 0x8001, 0x680a, 0x697c, 0x6912, 0x6980, 0x6916, 0x682b, 0xffff,
+ 0x682f, 0xffff, 0x6850, 0xc0bd, 0x6852, 0x00de, 0x080c, 0x929c,
+ 0x0804, 0x1c5e, 0x080c, 0x14f6, 0x0126, 0x2091, 0x2200, 0x0006,
+ 0x0016, 0x2b68, 0x6818, 0x2060, 0x7904, 0x7803, 0x0002, 0xa184,
+ 0x0700, 0x1978, 0xa184, 0x0003, 0xa086, 0x0003, 0x0d58, 0x7000,
+ 0x0002, 0x1a89, 0x1a8f, 0x1b92, 0x1c39, 0x1c4d, 0x1a89, 0x1a89,
+ 0x1a89, 0x7804, 0xd09c, 0x1904, 0x1c5e, 0x080c, 0x14f6, 0x8001,
+ 0x7002, 0xa184, 0x0880, 0x1190, 0xd19c, 0x1904, 0x1b20, 0x8aff,
+ 0x0904, 0x1b20, 0x2009, 0x0001, 0x080c, 0x19e0, 0x0904, 0x1c5e,
+ 0x2009, 0x0001, 0x080c, 0x19e0, 0x0804, 0x1c5e, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0xd1bc, 0x1904, 0x1b00, 0x0026, 0x0036, 0x7c20,
+ 0x7d24, 0x7e30, 0x7f34, 0x7818, 0x6812, 0x781c, 0x6816, 0x2001,
+ 0x0201, 0x2004, 0xa005, 0x0140, 0x7808, 0xd0ec, 0x1128, 0x7803,
+ 0x0009, 0x7003, 0x0004, 0x0010, 0x080c, 0x1c62, 0x6b28, 0x6a2c,
+ 0x2400, 0x686e, 0xa31a, 0x2500, 0x6872, 0xa213, 0x6b2a, 0x6a2e,
+ 0x00c6, 0x7004, 0x2060, 0x6020, 0xd0f4, 0x1110, 0x633a, 0x6236,
+ 0x00ce, 0x003e, 0x002e, 0x6e1e, 0x6f22, 0x080c, 0x215e, 0x2a00,
+ 0x6826, 0x2c00, 0x681a, 0x2800, 0x6832, 0x6850, 0xc0fd, 0x6852,
+ 0x6808, 0x8001, 0x680a, 0x1148, 0x684c, 0xd0e4, 0x0130, 0x7004,
+ 0x2060, 0x2009, 0x0048, 0x080c, 0x80a7, 0x7000, 0xa086, 0x0004,
+ 0x0904, 0x1c5e, 0x7003, 0x0000, 0x080c, 0x195f, 0x0804, 0x1c5e,
+ 0x0056, 0x7d0c, 0xd5bc, 0x1110, 0x080c, 0xac73, 0x005e, 0x080c,
+ 0x1d22, 0x00f6, 0x7004, 0x2078, 0x080c, 0x5029, 0x0118, 0x7820,
+ 0xc0f5, 0x7822, 0x00fe, 0x682b, 0xffff, 0x682f, 0xffff, 0x6808,
+ 0x8001, 0x680a, 0x697c, 0x791a, 0x6980, 0x791e, 0x0804, 0x1c5e,
+ 0x7004, 0x00c6, 0x2060, 0x6020, 0x00ce, 0xd0f4, 0x0128, 0x6808,
+ 0x8001, 0x680a, 0x0804, 0x1c5e, 0x7818, 0x6812, 0x7a1c, 0x6a16,
+ 0xd19c, 0x0160, 0xa205, 0x0150, 0x7004, 0xa080, 0x0007, 0x2004,
+ 0xa084, 0xfffd, 0xa086, 0x0008, 0x1904, 0x1aa6, 0x684c, 0xc0f5,
+ 0x684e, 0x7814, 0xa005, 0x1180, 0x7003, 0x0000, 0x6808, 0x8001,
+ 0x680a, 0x1130, 0x7004, 0x2060, 0x2009, 0x0048, 0x080c, 0x80a7,
+ 0x080c, 0x195f, 0x0804, 0x1c5e, 0x7818, 0x6812, 0x781c, 0x6816,
+ 0x7814, 0x7908, 0xa18c, 0x0fff, 0xa188, 0x0007, 0x8114, 0x8214,
+ 0x8214, 0xa10a, 0x8104, 0x8004, 0x8004, 0xa20a, 0x810b, 0x810b,
+ 0x810b, 0x080c, 0x1da5, 0x7803, 0x0004, 0x780f, 0xffff, 0x7803,
+ 0x0001, 0x7804, 0xd0fc, 0x0de8, 0x7803, 0x0002, 0x7803, 0x0004,
+ 0x780f, 0x00f6, 0x7004, 0x7007, 0x0000, 0x2060, 0x2009, 0x0048,
+ 0x080c, 0x80a7, 0x080c, 0x1dd7, 0x0958, 0x7908, 0xd1ec, 0x1118,
+ 0x2009, 0x0009, 0x0010, 0x2009, 0x0019, 0x7902, 0x7003, 0x0003,
+ 0x0804, 0x1c5e, 0x8001, 0x7002, 0xd194, 0x01a8, 0x7804, 0xd0fc,
+ 0x1904, 0x1c2c, 0xd09c, 0x0130, 0x7804, 0xd0fc, 0x1904, 0x1a74,
+ 0xd09c, 0x11a8, 0x8aff, 0x0904, 0x1c5e, 0x2009, 0x0001, 0x080c,
+ 0x19e0, 0x0804, 0x1c5e, 0xa184, 0x0888, 0x1148, 0x8aff, 0x0904,
+ 0x1c5e, 0x2009, 0x0001, 0x080c, 0x19e0, 0x0804, 0x1c5e, 0x7818,
+ 0x6812, 0x7a1c, 0x6a16, 0xa205, 0x0904, 0x1b3e, 0x7803, 0x0004,
+ 0x7003, 0x0000, 0xd1bc, 0x1904, 0x1c0f, 0x6834, 0xa084, 0x00ff,
+ 0xa086, 0x0029, 0x1118, 0xd19c, 0x1904, 0x1b3e, 0x0026, 0x0036,
+ 0x7c20, 0x7d24, 0x7e30, 0x7f34, 0x7818, 0x6812, 0x781c, 0x6816,
+ 0x2001, 0x0201, 0x2004, 0xa005, 0x0140, 0x7808, 0xd0ec, 0x1128,
+ 0x7803, 0x0009, 0x7003, 0x0004, 0x0020, 0x0016, 0x080c, 0x1c62,
+ 0x001e, 0x6b28, 0x6a2c, 0x080c, 0x215e, 0x00d6, 0x2805, 0xac68,
+ 0x6034, 0xd09c, 0x1128, 0x6808, 0xa31a, 0x680c, 0xa213, 0x0020,
+ 0x6810, 0xa31a, 0x6814, 0xa213, 0x00de, 0xd194, 0x0904, 0x1ac8,
+ 0x2a00, 0x6826, 0x2c00, 0x681a, 0x2800, 0x6832, 0x6808, 0x8001,
+ 0x680a, 0x6b2a, 0x6a2e, 0x003e, 0x002e, 0x0804, 0x1b50, 0x0056,
+ 0x7d0c, 0x080c, 0xac73, 0x005e, 0x080c, 0x1d22, 0x00f6, 0x7004,
+ 0x2078, 0x080c, 0x5029, 0x0118, 0x7820, 0xc0f5, 0x7822, 0x00fe,
+ 0x682b, 0xffff, 0x682f, 0xffff, 0x6808, 0x8001, 0x680a, 0x697c,
+ 0x791a, 0x6980, 0x791e, 0x0490, 0x7804, 0xd09c, 0x0904, 0x1a74,
+ 0x7c20, 0x7824, 0xa405, 0x1904, 0x1a74, 0x7803, 0x0002, 0x0804,
+ 0x1bb7, 0x7803, 0x0004, 0x7003, 0x0000, 0x7004, 0xa00d, 0x0150,
+ 0x6808, 0x8001, 0x680a, 0x1130, 0x7004, 0x2060, 0x2009, 0x0048,
+ 0x080c, 0x80a7, 0x080c, 0x195f, 0x0088, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0x7004, 0x2060, 0x6010, 0xa005, 0x0da0, 0x2068, 0x6808,
+ 0x8000, 0x680a, 0x6c28, 0x6b2c, 0x080c, 0x197a, 0x001e, 0x000e,
+ 0x012e, 0x0005, 0x700c, 0x7110, 0xa106, 0x0904, 0x1ce1, 0x7004,
+ 0x0016, 0x210c, 0xa106, 0x001e, 0x0904, 0x1ce1, 0x00d6, 0x00c6,
+ 0x216c, 0x2d00, 0xa005, 0x0904, 0x1cdf, 0x6820, 0xd0d4, 0x1904,
+ 0x1cdf, 0x6810, 0x2068, 0x6850, 0xd0fc, 0x0558, 0x8108, 0x2104,
+ 0x6b2c, 0xa306, 0x1904, 0x1cdf, 0x8108, 0x2104, 0x6a28, 0xa206,
+ 0x1904, 0x1cdf, 0x6850, 0xc0fc, 0xc0f5, 0x6852, 0x686c, 0x7822,
+ 0x6870, 0x7826, 0x681c, 0x7832, 0x6820, 0x7836, 0x6818, 0x2060,
+ 0x6034, 0xd09c, 0x0150, 0x6830, 0x2005, 0x00d6, 0xac68, 0x6808,
+ 0x783a, 0x680c, 0x783e, 0x00de, 0x04a0, 0xa006, 0x783a, 0x783e,
+ 0x0480, 0x8108, 0x2104, 0xa005, 0x1590, 0x8108, 0x2104, 0xa005,
+ 0x1570, 0x6850, 0xc0f5, 0x6852, 0x6830, 0x2005, 0x6918, 0xa160,
+ 0xa180, 0x000d, 0x2004, 0xd09c, 0x1170, 0x6008, 0x7822, 0x686e,
+ 0x600c, 0x7826, 0x6872, 0x6000, 0x7832, 0x6004, 0x7836, 0xa006,
+ 0x783a, 0x783e, 0x0070, 0x6010, 0x7822, 0x686e, 0x6014, 0x7826,
+ 0x6872, 0x6000, 0x7832, 0x6004, 0x7836, 0x6008, 0x783a, 0x600c,
+ 0x783e, 0x6810, 0x781a, 0x6814, 0x781e, 0x7803, 0x0011, 0x00ce,
+ 0x00de, 0x0005, 0x2011, 0x0201, 0x2009, 0x003c, 0x2204, 0xa005,
+ 0x1118, 0x8109, 0x1dd8, 0x0005, 0x0005, 0x0ca1, 0x01e0, 0x7908,
+ 0xd1ec, 0x1160, 0x080c, 0x1dd7, 0x0148, 0x7803, 0x0009, 0x7904,
+ 0xd1fc, 0x0de8, 0x7803, 0x0006, 0x0c29, 0x0168, 0x780c, 0xd0a4,
+ 0x1150, 0x7007, 0x0000, 0x080c, 0x1dd7, 0x0140, 0x7803, 0x0019,
+ 0x7003, 0x0003, 0x0018, 0x00b1, 0xa085, 0x0001, 0x0005, 0x0126,
+ 0x2091, 0x2200, 0x7000, 0xa086, 0x0003, 0x1150, 0x700c, 0x7110,
+ 0xa106, 0x0130, 0x20e1, 0x9028, 0x700f, 0xb003, 0x7013, 0xb003,
+ 0x012e, 0x0005, 0x00c6, 0x080c, 0x574f, 0x1550, 0x2001, 0x0160,
+ 0x2003, 0x0000, 0x2001, 0x0138, 0x2003, 0x0000, 0x2011, 0x00c8,
+ 0xe000, 0xe000, 0x8211, 0x1de0, 0x080c, 0x1d7e, 0x700c, 0x7110,
+ 0xa106, 0x0190, 0x2104, 0xa005, 0x0130, 0x2060, 0x6010, 0x2060,
+ 0x6008, 0x8001, 0x600a, 0xa188, 0x0003, 0xa182, 0xb01e, 0x0210,
+ 0x2009, 0xb003, 0x7112, 0x0c50, 0x080c, 0x57d1, 0x00ce, 0x0005,
+ 0x04a9, 0x20e1, 0x9028, 0x700c, 0x7110, 0xa106, 0x01d0, 0x2104,
+ 0xa005, 0x0130, 0x2060, 0x6010, 0x2060, 0x6008, 0x8001, 0x600a,
+ 0xa188, 0x0003, 0xa182, 0xb01e, 0x0210, 0x2009, 0xb003, 0x7112,
+ 0x700c, 0xa106, 0x1d40, 0x080c, 0x2744, 0x2001, 0x0138, 0x2102,
+ 0x0c10, 0x2001, 0x015d, 0x200c, 0x810a, 0x2102, 0x2001, 0x0160,
+ 0x2502, 0x2001, 0x0138, 0x2202, 0x00ce, 0x0005, 0x20e1, 0x9028,
+ 0x2001, 0x015d, 0x200c, 0x810a, 0x2102, 0x0005, 0x2001, 0x0138,
+ 0x2014, 0x2003, 0x0000, 0x2001, 0x0160, 0x202c, 0x2003, 0x0000,
+ 0x2021, 0xb015, 0x2001, 0x0141, 0x201c, 0xd3dc, 0x1168, 0x2001,
+ 0x0109, 0x201c, 0xa39c, 0x0048, 0x1138, 0x2001, 0x0111, 0x201c,
+ 0x83ff, 0x1110, 0x8421, 0x1d70, 0x0005, 0x00e6, 0x2071, 0x0200,
+ 0x7808, 0xa084, 0xf000, 0xa10d, 0x08c9, 0x2019, 0x5000, 0x8319,
+ 0x0168, 0x2001, 0xb01e, 0x2004, 0xa086, 0x0000, 0x0138, 0x2001,
+ 0x0021, 0xd0fc, 0x0da0, 0x080c, 0x1ff4, 0x0c78, 0x20e1, 0x7000,
+ 0x7324, 0x7420, 0x7028, 0x7028, 0x7426, 0x7037, 0x0001, 0x810f,
+ 0x712e, 0x702f, 0x0100, 0x7037, 0x0008, 0x7326, 0x7422, 0x2001,
+ 0x0160, 0x2502, 0x2001, 0x0138, 0x2202, 0x00ee, 0x0005, 0x7908,
+ 0xa18c, 0x0fff, 0xa182, 0x0009, 0x0218, 0xa085, 0x0001, 0x0088,
+ 0x2001, 0x020a, 0x81ff, 0x0130, 0x20e1, 0x6000, 0x200c, 0x200c,
+ 0x200c, 0x200c, 0x20e1, 0x7000, 0x200c, 0x200c, 0x7003, 0x0000,
+ 0xa006, 0x0005, 0x00f6, 0x00e6, 0x0016, 0x0026, 0x2071, 0xaffd,
+ 0x2079, 0x0030, 0x2011, 0x0050, 0x7000, 0xa086, 0x0000, 0x01a8,
+ 0x8211, 0x0188, 0x2001, 0x0005, 0x2004, 0xd08c, 0x0dc8, 0x7904,
+ 0xa18c, 0x0780, 0x0016, 0x080c, 0x1a6c, 0x001e, 0x81ff, 0x1118,
+ 0x2011, 0x0050, 0x0c48, 0xa085, 0x0001, 0x002e, 0x001e, 0x00ee,
+ 0x00fe, 0x0005, 0x7803, 0x0004, 0x2009, 0x0064, 0x7804, 0xd0ac,
+ 0x0904, 0x1e66, 0x8109, 0x1dd0, 0x2009, 0x0100, 0x210c, 0xa18a,
+ 0x0003, 0x0a0c, 0x14f6, 0x080c, 0x20f2, 0x00e6, 0x00f6, 0x2071,
+ 0xafec, 0x2079, 0x0010, 0x7004, 0xa086, 0x0000, 0x0538, 0x7800,
+ 0x0006, 0x7820, 0x0006, 0x7830, 0x0006, 0x7834, 0x0006, 0x7838,
+ 0x0006, 0x783c, 0x0006, 0x7803, 0x0004, 0xe000, 0xe000, 0x2079,
+ 0x0030, 0x7804, 0xd0ac, 0x190c, 0x14f6, 0x2079, 0x0010, 0x000e,
+ 0x783e, 0x000e, 0x783a, 0x000e, 0x7836, 0x000e, 0x7832, 0x000e,
+ 0x7822, 0x000e, 0x7802, 0x00fe, 0x00ee, 0x0030, 0x00fe, 0x00ee,
+ 0x7804, 0xd0ac, 0x190c, 0x14f6, 0x080c, 0x6d0d, 0x0005, 0x00e6,
+ 0x2071, 0xb01e, 0x7003, 0x0000, 0x00ee, 0x0005, 0x00d6, 0xa280,
+ 0x0004, 0x206c, 0x694c, 0xd1dc, 0x1904, 0x1ee4, 0x6934, 0xa184,
+ 0x0007, 0x0002, 0x1e82, 0x1ecf, 0x1e82, 0x1e82, 0x1e82, 0x1eb6,
+ 0x1e95, 0x1e84, 0x080c, 0x14f6, 0x684c, 0xd0b4, 0x0904, 0x1fcc,
+ 0x6860, 0x682e, 0x6816, 0x685c, 0x682a, 0x6812, 0x687c, 0x680a,
+ 0x6880, 0x680e, 0x6958, 0x0804, 0x1ed7, 0x6834, 0xa084, 0x00ff,
+ 0xa086, 0x001e, 0x1d38, 0x684c, 0xd0b4, 0x0904, 0x1fcc, 0x6860,
+ 0x682e, 0x6816, 0x685c, 0x682a, 0x6812, 0x687c, 0x680a, 0x6880,
+ 0x680e, 0x6804, 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f,
+ 0xa080, 0x2186, 0x2005, 0x6832, 0x6958, 0x0450, 0xa18c, 0x00ff,
+ 0xa186, 0x0015, 0x1548, 0x684c, 0xd0b4, 0x0904, 0x1fcc, 0x6804,
+ 0x681a, 0xa080, 0x000d, 0x2004, 0xa084, 0x000f, 0xa080, 0x2186,
+ 0x2005, 0x6832, 0x6958, 0xa006, 0x682e, 0x682a, 0x0088, 0x684c,
+ 0xd0b4, 0x0904, 0x1a47, 0x6958, 0xa006, 0x682e, 0x682a, 0x2d00,
+ 0x681a, 0x6834, 0xa084, 0x000f, 0xa080, 0x2186, 0x2005, 0x6832,
+ 0x6926, 0x684c, 0xc0dd, 0x684e, 0x00de, 0x0005, 0x00f6, 0x2079,
+ 0x0020, 0x7804, 0xd0fc, 0x190c, 0x1ff4, 0x00e6, 0x00d6, 0x2071,
+ 0xb01e, 0x7000, 0xa005, 0x1904, 0x1f4c, 0x00c6, 0x7206, 0xa280,
+ 0x0004, 0x205c, 0x7004, 0x2068, 0x7803, 0x0004, 0x6818, 0x00d6,
+ 0x2068, 0x686c, 0x7812, 0x6890, 0x00f6, 0x20e1, 0x9040, 0x2079,
+ 0x0200, 0x781a, 0x2079, 0x0100, 0x8004, 0x78d6, 0x00fe, 0x00de,
+ 0x2b68, 0x6824, 0x2050, 0x6818, 0x2060, 0x6830, 0x2040, 0x6034,
+ 0xa0cc, 0x000f, 0x6908, 0x791a, 0x7116, 0x680c, 0x781e, 0x701a,
+ 0xa006, 0x700e, 0x7012, 0x7004, 0x692c, 0x6814, 0xa106, 0x1120,
+ 0x6928, 0x6810, 0xa106, 0x0158, 0x0036, 0x0046, 0x6b14, 0x6c10,
+ 0x080c, 0x21a6, 0x004e, 0x003e, 0x0110, 0x00ce, 0x00a8, 0x8aff,
+ 0x1120, 0x00ce, 0xa085, 0x0001, 0x0078, 0x0126, 0x2091, 0x8000,
+ 0x2079, 0x0020, 0x2009, 0x0001, 0x0059, 0x0118, 0x2009, 0x0001,
+ 0x0039, 0x012e, 0x00ce, 0xa006, 0x00de, 0x00ee, 0x00fe, 0x0005,
+ 0x0076, 0x0066, 0x0056, 0x0046, 0x0036, 0x0026, 0x8aff, 0x0904,
+ 0x1fc5, 0x700c, 0x7214, 0xa23a, 0x7010, 0x7218, 0xa203, 0x0a04,
+ 0x1fc4, 0xa705, 0x0904, 0x1fc4, 0xa03e, 0x2730, 0x6850, 0xd0fc,
+ 0x11a8, 0x00d6, 0x2805, 0xac68, 0x2900, 0x0002, 0x1fa7, 0x1f8c,
+ 0x1f8c, 0x1fa7, 0x1fa7, 0x1fa0, 0x1fa7, 0x1f8c, 0x1fa7, 0x1f91,
+ 0x1f91, 0x1fa7, 0x1fa7, 0x1fa7, 0x1f98, 0x1f91, 0xc0fc, 0x6852,
+ 0x6b6c, 0x6a70, 0x6d1c, 0x6c20, 0xd99c, 0x0528, 0x00d6, 0x2805,
+ 0xac68, 0x6f08, 0x6e0c, 0x00f0, 0x6b08, 0x6a0c, 0x6d00, 0x6c04,
+ 0x00c8, 0x6b10, 0x6a14, 0x6d00, 0x6c04, 0x6f08, 0x6e0c, 0x0090,
+ 0x00de, 0x00d6, 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e, 0x1138,
+ 0x00de, 0x080c, 0x2148, 0x1904, 0x1f56, 0xa00e, 0x00f0, 0x00de,
+ 0x080c, 0x14f6, 0x00de, 0x7b22, 0x7a26, 0x7d32, 0x7c36, 0x7f3a,
+ 0x7e3e, 0x7902, 0x7000, 0x8000, 0x7002, 0x6828, 0xa300, 0x682a,
+ 0x682c, 0xa201, 0x682e, 0x700c, 0xa300, 0x700e, 0x7010, 0xa201,
+ 0x7012, 0x080c, 0x2148, 0x0008, 0xa006, 0x002e, 0x003e, 0x004e,
+ 0x005e, 0x006e, 0x007e, 0x0005, 0x080c, 0x14f6, 0x0026, 0x2001,
+ 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0x7004, 0x2060, 0x00d6, 0x6010, 0x2068, 0x080c, 0x9596,
+ 0x0118, 0x6850, 0xc0bd, 0x6852, 0x00de, 0x080c, 0x929c, 0x20e1,
+ 0x9040, 0x080c, 0x7cb8, 0x2011, 0x0000, 0x080c, 0x7ae9, 0x080c,
+ 0x6d0d, 0x002e, 0x0804, 0x20ad, 0x0126, 0x2091, 0x2400, 0x0006,
+ 0x0016, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x2079, 0x0020, 0x2071,
+ 0xb01e, 0x2b68, 0x6818, 0x2060, 0x7904, 0x7803, 0x0002, 0xa184,
+ 0x0700, 0x1920, 0x7000, 0x0002, 0x20ad, 0x2010, 0x2080, 0x20ab,
+ 0x8001, 0x7002, 0xd19c, 0x1170, 0x8aff, 0x05d0, 0x2009, 0x0001,
+ 0x080c, 0x1f50, 0x0904, 0x20ad, 0x2009, 0x0001, 0x080c, 0x1f50,
+ 0x0804, 0x20ad, 0x7803, 0x0004, 0xd194, 0x0148, 0x6850, 0xc0fc,
+ 0x6852, 0x8aff, 0x11d8, 0x684c, 0xc0f5, 0x684e, 0x00b8, 0x0026,
+ 0x0036, 0x6b28, 0x6a2c, 0x7820, 0x686e, 0xa31a, 0x7824, 0x6872,
+ 0xa213, 0x7830, 0x681e, 0x7834, 0x6822, 0x6b2a, 0x6a2e, 0x003e,
+ 0x002e, 0x080c, 0x215e, 0x6850, 0xc0fd, 0x6852, 0x2a00, 0x6826,
+ 0x2c00, 0x681a, 0x2800, 0x6832, 0x7003, 0x0000, 0x0804, 0x20ad,
+ 0x00f6, 0x0026, 0x781c, 0x0006, 0x7818, 0x0006, 0x2079, 0x0100,
+ 0x7a14, 0xa284, 0x0184, 0xa085, 0x0012, 0x7816, 0x0036, 0x2019,
+ 0x1000, 0x8319, 0x090c, 0x14f6, 0x7820, 0xd0bc, 0x1dd0, 0x003e,
+ 0x79c8, 0x000e, 0xa102, 0x001e, 0x0006, 0x0016, 0x79c4, 0x000e,
+ 0xa103, 0x78c6, 0x000e, 0x78ca, 0xa284, 0x0184, 0xa085, 0x0012,
+ 0x7816, 0x002e, 0x00fe, 0x7803, 0x0008, 0x7003, 0x0000, 0x0468,
+ 0x8001, 0x7002, 0xd194, 0x0168, 0x7804, 0xd0fc, 0x1904, 0x2004,
+ 0xd19c, 0x11f8, 0x8aff, 0x0508, 0x2009, 0x0001, 0x080c, 0x1f50,
+ 0x00e0, 0x0026, 0x0036, 0x6b28, 0x6a2c, 0x080c, 0x215e, 0x00d6,
+ 0x2805, 0xac68, 0x6034, 0xd09c, 0x1128, 0x6808, 0xa31a, 0x680c,
+ 0xa213, 0x0020, 0x6810, 0xa31a, 0x6814, 0xa213, 0x00de, 0x0804,
+ 0x2033, 0x0804, 0x202f, 0x080c, 0x14f6, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x001e, 0x000e, 0x012e, 0x0005, 0x00f6, 0x00e6, 0x2071,
+ 0xb01e, 0x7000, 0xa086, 0x0000, 0x0590, 0x2079, 0x0020, 0x0016,
+ 0x2009, 0x0207, 0x210c, 0xd194, 0x0158, 0x2009, 0x020c, 0x210c,
+ 0xa184, 0x0003, 0x0128, 0x20e1, 0x9040, 0x2001, 0x020c, 0x2102,
+ 0x2009, 0x0206, 0x2104, 0x2009, 0x0203, 0x210c, 0xa106, 0x1110,
+ 0x20e1, 0x9040, 0x7804, 0xd0fc, 0x0d18, 0x080c, 0x1ff4, 0x7000,
+ 0xa086, 0x0000, 0x19e8, 0x001e, 0x7803, 0x0004, 0x7804, 0xd0ac,
+ 0x1de8, 0x20e1, 0x9040, 0x7803, 0x0002, 0x7003, 0x0000, 0x00ee,
+ 0x00fe, 0x0005, 0x0026, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2071,
+ 0xb01e, 0x2079, 0x0020, 0x7000, 0xa086, 0x0000, 0x0540, 0x7004,
+ 0x2060, 0x6010, 0x2068, 0x080c, 0x9596, 0x0158, 0x6850, 0xc0b5,
+ 0x6852, 0x680c, 0x7a1c, 0xa206, 0x1120, 0x6808, 0x7a18, 0xa206,
+ 0x01e0, 0x2001, 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803,
+ 0x0004, 0x7003, 0x0000, 0x7004, 0x2060, 0x080c, 0x929c, 0x20e1,
+ 0x9040, 0x080c, 0x7cb8, 0x2011, 0x0000, 0x080c, 0x7ae9, 0x00fe,
+ 0x00ee, 0x00de, 0x00ce, 0x002e, 0x0005, 0x6810, 0x6a14, 0xa205,
+ 0x1d00, 0x684c, 0xc0dc, 0x684e, 0x2c10, 0x080c, 0x1e6e, 0x2001,
+ 0x0105, 0x2003, 0x0010, 0x20e1, 0x9040, 0x7803, 0x0004, 0x7003,
+ 0x0000, 0x2069, 0xafc7, 0x6833, 0x0000, 0x683f, 0x0000, 0x08f8,
+ 0x8840, 0x2805, 0xa005, 0x1170, 0x6004, 0xa005, 0x0168, 0x681a,
+ 0x2060, 0x6034, 0xa084, 0x000f, 0xa080, 0x2186, 0x2045, 0x88ff,
+ 0x090c, 0x14f6, 0x8a51, 0x0005, 0x2050, 0x0005, 0x8a50, 0x8841,
+ 0x2805, 0xa005, 0x1190, 0x2c00, 0xad06, 0x0120, 0x6000, 0xa005,
+ 0x1108, 0x2d00, 0x2060, 0x681a, 0x6034, 0xa084, 0x000f, 0xa080,
+ 0x2196, 0x2045, 0x88ff, 0x090c, 0x14f6, 0x0005, 0x0000, 0x0011,
+ 0x0015, 0x0019, 0x001d, 0x0021, 0x0025, 0x0029, 0x0000, 0x000f,
+ 0x0015, 0x001b, 0x0021, 0x0027, 0x0000, 0x0000, 0x0000, 0x217b,
+ 0x2177, 0x0000, 0x0000, 0x2185, 0x0000, 0x217b, 0x0000, 0x2182,
+ 0x217f, 0x0000, 0x0000, 0x0000, 0x2185, 0x2182, 0x0000, 0x217d,
+ 0x217d, 0x0000, 0x0000, 0x2185, 0x0000, 0x217d, 0x0000, 0x2183,
+ 0x2183, 0x0000, 0x0000, 0x0000, 0x2185, 0x2183, 0x00a6, 0x0096,
+ 0x0086, 0x6b2e, 0x6c2a, 0x6858, 0xa055, 0x0904, 0x2237, 0x2d60,
+ 0x6034, 0xa0cc, 0x000f, 0xa9c0, 0x2186, 0xa986, 0x0007, 0x0130,
+ 0xa986, 0x000e, 0x0118, 0xa986, 0x000f, 0x1120, 0x605c, 0xa422,
+ 0x6060, 0xa31a, 0x2805, 0xa045, 0x1140, 0x0310, 0x0804, 0x2237,
+ 0x6004, 0xa065, 0x0904, 0x2237, 0x0c18, 0x2805, 0xa005, 0x01a8,
+ 0xac68, 0xd99c, 0x1128, 0x6808, 0xa422, 0x680c, 0xa31b, 0x0020,
+ 0x6810, 0xa422, 0x6814, 0xa31b, 0x0620, 0x2300, 0xa405, 0x0150,
+ 0x8a51, 0x0904, 0x2237, 0x8840, 0x0c40, 0x6004, 0xa065, 0x0904,
+ 0x2237, 0x0830, 0x8a51, 0x0904, 0x2237, 0x8840, 0x2805, 0xa005,
+ 0x1158, 0x6004, 0xa065, 0x0904, 0x2237, 0x6034, 0xa0cc, 0x000f,
+ 0xa9c0, 0x2186, 0x2805, 0x2040, 0x2b68, 0x6850, 0xc0fc, 0x6852,
+ 0x0458, 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x00d6, 0x2b68,
+ 0x6c6e, 0x6b72, 0x00de, 0xd99c, 0x1168, 0x6908, 0x2400, 0xa122,
+ 0x690c, 0x2300, 0xa11b, 0x0a0c, 0x14f6, 0x6800, 0xa420, 0x6804,
+ 0xa319, 0x0060, 0x6910, 0x2400, 0xa122, 0x6914, 0x2300, 0xa11b,
+ 0x0a0c, 0x14f6, 0x6800, 0xa420, 0x6804, 0xa319, 0x2b68, 0x6c1e,
+ 0x6b22, 0x6850, 0xc0fd, 0x6852, 0x2c00, 0x681a, 0x2800, 0x6832,
+ 0x2a00, 0x6826, 0x000e, 0x000e, 0x000e, 0xa006, 0x0028, 0x008e,
+ 0x009e, 0x00ae, 0xa085, 0x0001, 0x0005, 0x2001, 0x0005, 0x2004,
+ 0xa084, 0x0007, 0x0002, 0x224b, 0x224c, 0x224f, 0x2252, 0x2257,
+ 0x225a, 0x225f, 0x2264, 0x0005, 0x080c, 0x1ff4, 0x0005, 0x080c,
+ 0x1a6c, 0x0005, 0x080c, 0x1a6c, 0x080c, 0x1ff4, 0x0005, 0x080c,
+ 0x16f8, 0x0005, 0x080c, 0x1ff4, 0x080c, 0x16f8, 0x0005, 0x080c,
+ 0x1a6c, 0x080c, 0x16f8, 0x0005, 0x080c, 0x1a6c, 0x080c, 0x1ff4,
+ 0x080c, 0x16f8, 0x0005, 0x0126, 0x2091, 0x2600, 0x2079, 0x0200,
+ 0x2071, 0xb280, 0x2069, 0xad00, 0x2009, 0x0004, 0x7912, 0x7817,
+ 0x0004, 0x080c, 0x2651, 0x781b, 0x0002, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x20a9, 0x0080, 0x782f, 0x0000, 0x1f04, 0x2283, 0x20e1,
+ 0x9080, 0x783b, 0x001f, 0x20e1, 0x8700, 0x012e, 0x0005, 0x0126,
+ 0x2091, 0x2600, 0x781c, 0xd0a4, 0x190c, 0x2335, 0xa084, 0x0007,
+ 0x0002, 0x22b3, 0x22a1, 0x22a4, 0x22a7, 0x22ac, 0x22ae, 0x22b0,
+ 0x22b2, 0x080c, 0x5fb7, 0x0078, 0x080c, 0x5ff0, 0x0060, 0x080c,
+ 0x5fb7, 0x080c, 0x5ff0, 0x0038, 0x0041, 0x0028, 0x0031, 0x0018,
+ 0x0021, 0x0008, 0x0011, 0x012e, 0x0005, 0x0006, 0x0016, 0x0026,
+ 0x7930, 0xa184, 0x0003, 0x0118, 0x20e1, 0x9040, 0x04a0, 0xa184,
+ 0x0030, 0x01e0, 0x6a00, 0xa286, 0x0003, 0x1108, 0x00a0, 0x080c,
+ 0x574f, 0x1178, 0x2001, 0xaf9e, 0x2003, 0x0001, 0x2001, 0xad00,
+ 0x2003, 0x0001, 0xa085, 0x0001, 0x080c, 0x5793, 0x080c, 0x569a,
+ 0x0010, 0x080c, 0x485e, 0x20e1, 0x9010, 0x00a8, 0xa184, 0x00c0,
+ 0x0168, 0x00e6, 0x0036, 0x0046, 0x0056, 0x2071, 0xaffd, 0x080c,
+ 0x1d22, 0x005e, 0x004e, 0x003e, 0x00ee, 0x0028, 0xa184, 0x0300,
+ 0x0110, 0x20e1, 0x9020, 0x7932, 0x002e, 0x001e, 0x000e, 0x0005,
+ 0x0016, 0x00e6, 0x00f6, 0x2071, 0xad00, 0x7128, 0x2001, 0xaf90,
+ 0x2102, 0x2001, 0xaf98, 0x2102, 0xa182, 0x0211, 0x1218, 0x2009,
+ 0x0008, 0x0400, 0xa182, 0x0259, 0x1218, 0x2009, 0x0007, 0x00d0,
+ 0xa182, 0x02c1, 0x1218, 0x2009, 0x0006, 0x00a0, 0xa182, 0x0349,
+ 0x1218, 0x2009, 0x0005, 0x0070, 0xa182, 0x0421, 0x1218, 0x2009,
+ 0x0004, 0x0040, 0xa182, 0x0581, 0x1218, 0x2009, 0x0003, 0x0010,
+ 0x2009, 0x0002, 0x2079, 0x0200, 0x7912, 0x7817, 0x0004, 0x080c,
+ 0x2651, 0x00fe, 0x00ee, 0x001e, 0x0005, 0x7938, 0x080c, 0x14f6,
+ 0x0126, 0x2091, 0x2800, 0x2061, 0x0100, 0x2071, 0xad00, 0x6024,
+ 0x6026, 0x6053, 0x0030, 0x080c, 0x2690, 0x6050, 0xa084, 0xfe7f,
+ 0x6052, 0x2009, 0x00ef, 0x6132, 0x6136, 0x080c, 0x26a0, 0x60e7,
+ 0x0000, 0x61ea, 0x60e3, 0x0008, 0x604b, 0xf7f7, 0x6043, 0x0000,
+ 0x602f, 0x0080, 0x602f, 0x0000, 0x6007, 0x0e9f, 0x601b, 0x001e,
+ 0x600f, 0x00ff, 0x2001, 0xaf8c, 0x2003, 0x00ff, 0x602b, 0x002f,
+ 0x012e, 0x0005, 0x2001, 0xad31, 0x2003, 0x0000, 0x2001, 0xad30,
+ 0x2003, 0x0001, 0x0005, 0x0126, 0x2091, 0x2800, 0x0006, 0x0016,
+ 0x0026, 0x6124, 0xa184, 0x1e2c, 0x1118, 0xa184, 0x0007, 0x002a,
+ 0xa195, 0x0004, 0xa284, 0x0007, 0x0002, 0x23a7, 0x238d, 0x2390,
+ 0x2393, 0x2398, 0x239a, 0x239e, 0x23a2, 0x080c, 0x6699, 0x00b8,
+ 0x080c, 0x6774, 0x00a0, 0x080c, 0x6774, 0x080c, 0x6699, 0x0078,
+ 0x0099, 0x0068, 0x080c, 0x6699, 0x0079, 0x0048, 0x080c, 0x6774,
+ 0x0059, 0x0028, 0x080c, 0x6774, 0x080c, 0x6699, 0x0029, 0x002e,
+ 0x001e, 0x000e, 0x012e, 0x0005, 0x6124, 0xd19c, 0x1904, 0x25bf,
+ 0x080c, 0x574f, 0x0578, 0x7000, 0xa086, 0x0003, 0x0198, 0x6024,
+ 0xa084, 0x1800, 0x0178, 0x080c, 0x5775, 0x0118, 0x080c, 0x5761,
+ 0x1148, 0x6027, 0x0020, 0x6043, 0x0000, 0x2001, 0xaf9d, 0x2003,
+ 0xaaaa, 0x0458, 0x080c, 0x5775, 0x15d0, 0x6024, 0xa084, 0x1800,
+ 0x1108, 0x04a8, 0x2001, 0xaf9d, 0x2003, 0xaaaa, 0x2001, 0xaf9e,
+ 0x2003, 0x0001, 0x2001, 0xad00, 0x2003, 0x0001, 0x080c, 0x569a,
+ 0x0804, 0x25bf, 0xd1ac, 0x1518, 0x6024, 0xd0dc, 0x1170, 0xd0e4,
+ 0x1188, 0xd0d4, 0x11a0, 0xd0cc, 0x0130, 0x7088, 0xa086, 0x0028,
+ 0x1110, 0x080c, 0x58da, 0x0804, 0x25bf, 0x2001, 0xaf9e, 0x2003,
+ 0x0000, 0x0048, 0x2001, 0xaf9e, 0x2003, 0x0002, 0x0020, 0x080c,
+ 0x584d, 0x0804, 0x25bf, 0x080c, 0x597a, 0x0804, 0x25bf, 0xd1ac,
+ 0x0904, 0x2507, 0x080c, 0x574f, 0x11d8, 0x6027, 0x0020, 0x0006,
+ 0x0026, 0x0036, 0x080c, 0x576b, 0x1170, 0x2001, 0xaf9e, 0x2003,
+ 0x0001, 0x2001, 0xad00, 0x2003, 0x0001, 0x080c, 0x569a, 0x003e,
+ 0x002e, 0x000e, 0x0005, 0x003e, 0x002e, 0x000e, 0x080c, 0x5726,
+ 0x0016, 0x0046, 0x00c6, 0x644c, 0xa486, 0xf0f0, 0x1138, 0x2061,
+ 0x0100, 0x644a, 0x6043, 0x0090, 0x6043, 0x0010, 0x74ca, 0xa48c,
+ 0xff00, 0x7034, 0xd084, 0x0178, 0xa186, 0xf800, 0x1160, 0x7038,
+ 0xd084, 0x1148, 0xc085, 0x703a, 0x0036, 0x2418, 0x2011, 0x8016,
+ 0x080c, 0x3c5c, 0x003e, 0xa196, 0xff00, 0x05b8, 0x7050, 0xa084,
+ 0x00ff, 0x810f, 0xa116, 0x0588, 0x7130, 0xd184, 0x1570, 0x2011,
+ 0xad52, 0x2214, 0xd2ec, 0x0138, 0xc18d, 0x7132, 0x2011, 0xad52,
+ 0x2214, 0xd2ac, 0x1510, 0x6240, 0xa294, 0x0010, 0x0130, 0x6248,
+ 0xa294, 0xff00, 0xa296, 0xff00, 0x01c0, 0x7030, 0xd08c, 0x0904,
+ 0x24d2, 0x7034, 0xd08c, 0x1140, 0x2001, 0xad0c, 0x200c, 0xd1ac,
+ 0x1904, 0x24d2, 0xc1ad, 0x2102, 0x0036, 0x73c8, 0x2011, 0x8013,
+ 0x080c, 0x3c5c, 0x003e, 0x0804, 0x24d2, 0x7034, 0xd08c, 0x1140,
+ 0x2001, 0xad0c, 0x200c, 0xd1ac, 0x1904, 0x24d2, 0xc1ad, 0x2102,
+ 0x0036, 0x73c8, 0x2011, 0x8013, 0x080c, 0x3c5c, 0x003e, 0x7130,
+ 0xc185, 0x7132, 0x2011, 0xad52, 0x220c, 0xd1a4, 0x01d0, 0x0016,
+ 0x2009, 0x0001, 0x2011, 0x0100, 0x080c, 0x663f, 0x2019, 0x000e,
+ 0x080c, 0xa8eb, 0xa484, 0x00ff, 0xa080, 0x2be6, 0x200d, 0xa18c,
+ 0xff00, 0x810f, 0x8127, 0xa006, 0x2009, 0x000e, 0x080c, 0xa96c,
+ 0x001e, 0xd1ac, 0x1148, 0x0016, 0x2009, 0x0000, 0x2019, 0x0004,
+ 0x080c, 0x2aac, 0x001e, 0x0070, 0x0156, 0x20a9, 0x007f, 0x2009,
+ 0x0000, 0x080c, 0x4cdc, 0x1110, 0x080c, 0x493a, 0x8108, 0x1f04,
+ 0x24c9, 0x015e, 0x00ce, 0x004e, 0x2011, 0x0003, 0x080c, 0x7adf,
+ 0x2011, 0x0002, 0x080c, 0x7ae9, 0x080c, 0x79e1, 0x080c, 0x6581,
+ 0x0036, 0x2019, 0x0000, 0x080c, 0x7a64, 0x003e, 0x60e3, 0x0000,
+ 0x001e, 0x2001, 0xad00, 0x2014, 0xa296, 0x0004, 0x1128, 0xd19c,
+ 0x1118, 0x6228, 0xc29d, 0x622a, 0x2003, 0x0001, 0x2001, 0xad22,
+ 0x2003, 0x0000, 0x6027, 0x0020, 0x080c, 0x5775, 0x1140, 0x0016,
+ 0x2009, 0x07d0, 0x2011, 0x567b, 0x080c, 0x6593, 0x001e, 0xd194,
+ 0x0904, 0x25bf, 0x0016, 0x6220, 0xd2b4, 0x0904, 0x2570, 0x080c,
+ 0x6581, 0x080c, 0x7834, 0x6027, 0x0004, 0x00f6, 0x2019, 0xafd0,
+ 0x2304, 0xa07d, 0x0570, 0x7804, 0xa086, 0x0032, 0x1550, 0x00d6,
+ 0x00c6, 0x00e6, 0x2069, 0x0140, 0x618c, 0x6288, 0x7818, 0x608e,
+ 0x7808, 0x608a, 0x6043, 0x0002, 0x2001, 0x0003, 0x8001, 0x1df0,
+ 0x6043, 0x0000, 0x6803, 0x1000, 0x6803, 0x0000, 0x618e, 0x628a,
+ 0x080c, 0x6b73, 0x080c, 0x6c50, 0x7810, 0x2070, 0x7037, 0x0103,
+ 0x2f60, 0x080c, 0x8078, 0x00ee, 0x00ce, 0x00de, 0x00fe, 0x001e,
+ 0x0005, 0x00fe, 0x00d6, 0x2069, 0x0140, 0x6804, 0xa084, 0x4000,
+ 0x0120, 0x6803, 0x1000, 0x6803, 0x0000, 0x00de, 0x00c6, 0x2061,
+ 0xafc7, 0x6028, 0xa09a, 0x00c8, 0x1238, 0x8000, 0x602a, 0x00ce,
+ 0x080c, 0x7827, 0x0804, 0x25be, 0x2019, 0xafd0, 0x2304, 0xa065,
+ 0x0120, 0x2009, 0x0027, 0x080c, 0x80a7, 0x00ce, 0x0804, 0x25be,
+ 0xd2bc, 0x0904, 0x25be, 0x080c, 0x658e, 0x6014, 0xa084, 0x0184,
+ 0xa085, 0x0010, 0x6016, 0x6027, 0x0004, 0x00d6, 0x2069, 0x0140,
+ 0x6804, 0xa084, 0x4000, 0x0120, 0x6803, 0x1000, 0x6803, 0x0000,
+ 0x00de, 0x00c6, 0x2061, 0xafc7, 0x6044, 0xa09a, 0x00c8, 0x12f0,
+ 0x8000, 0x6046, 0x603c, 0x00ce, 0xa005, 0x0540, 0x2009, 0x07d0,
+ 0x080c, 0x6586, 0xa080, 0x0007, 0x2004, 0xa086, 0x0006, 0x1138,
+ 0x6114, 0xa18c, 0x0184, 0xa18d, 0x0012, 0x6116, 0x00b8, 0x6114,
+ 0xa18c, 0x0184, 0xa18d, 0x0016, 0x6116, 0x0080, 0x0036, 0x2019,
+ 0x0001, 0x080c, 0x7a64, 0x003e, 0x2019, 0xafd6, 0x2304, 0xa065,
+ 0x0120, 0x2009, 0x004f, 0x080c, 0x80a7, 0x00ce, 0x001e, 0xd19c,
+ 0x0904, 0x261a, 0x7034, 0xd0ac, 0x1560, 0x0016, 0x0156, 0x6027,
+ 0x0008, 0x602f, 0x0020, 0x20a9, 0x0006, 0x1d04, 0x25cd, 0x2091,
+ 0x6000, 0x1f04, 0x25cd, 0x602f, 0x0000, 0x6150, 0xa185, 0x1400,
+ 0x6052, 0x20a9, 0x0366, 0x1d04, 0x25db, 0x2091, 0x6000, 0x6020,
+ 0xd09c, 0x1130, 0x015e, 0x6152, 0x001e, 0x6027, 0x0008, 0x0490,
+ 0x080c, 0x2760, 0x1f04, 0x25db, 0x015e, 0x6152, 0x001e, 0x6027,
+ 0x0008, 0x0016, 0x6028, 0xc09c, 0x602a, 0x2011, 0x0003, 0x080c,
+ 0x7adf, 0x2011, 0x0002, 0x080c, 0x7ae9, 0x080c, 0x79e1, 0x080c,
+ 0x6581, 0x0036, 0x2019, 0x0000, 0x080c, 0x7a64, 0x003e, 0x60e3,
+ 0x0000, 0x080c, 0xac8d, 0x080c, 0xaca8, 0xa085, 0x0001, 0x080c,
+ 0x5793, 0x2001, 0xad00, 0x2003, 0x0004, 0x6027, 0x0008, 0x080c,
+ 0x12cc, 0x001e, 0xa18c, 0xffd0, 0x6126, 0x0005, 0x0006, 0x0016,
+ 0x0026, 0x00e6, 0x00f6, 0x0126, 0x2091, 0x8000, 0x2071, 0xad00,
+ 0x71c0, 0x70c2, 0xa116, 0x01f0, 0x81ff, 0x0128, 0x2011, 0x8011,
+ 0x080c, 0x3c5c, 0x00b8, 0x2011, 0x8012, 0x080c, 0x3c5c, 0x2001,
+ 0xad71, 0x2004, 0xd0fc, 0x1170, 0x0036, 0x00c6, 0x080c, 0x26eb,
+ 0x2061, 0x0100, 0x2019, 0x0028, 0x2009, 0x0000, 0x080c, 0x2aac,
+ 0x00ce, 0x003e, 0x012e, 0x00fe, 0x00ee, 0x002e, 0x001e, 0x000e,
+ 0x0005, 0x00c6, 0x00f6, 0x0006, 0x0026, 0x2061, 0x0100, 0xa190,
+ 0x2664, 0x2205, 0x60f2, 0x2011, 0x2671, 0x2205, 0x60ee, 0x002e,
+ 0x000e, 0x00fe, 0x00ce, 0x0005, 0x0840, 0x0840, 0x0840, 0x0580,
+ 0x0420, 0x0348, 0x02c0, 0x0258, 0x0210, 0x01a8, 0x01a8, 0x01a8,
+ 0x01a8, 0x0140, 0x00f8, 0x00d0, 0x00b0, 0x00a0, 0x2028, 0xa18c,
+ 0x00ff, 0x2130, 0xa094, 0xff00, 0x1110, 0x81ff, 0x0118, 0x080c,
+ 0x6278, 0x0038, 0xa080, 0x2be6, 0x200d, 0xa18c, 0xff00, 0x810f,
+ 0xa006, 0x0005, 0xa080, 0x2be6, 0x200d, 0xa18c, 0x00ff, 0x0005,
+ 0x00d6, 0x2069, 0x0140, 0x2001, 0xad14, 0x2003, 0x00ef, 0x20a9,
+ 0x0010, 0xa006, 0x6852, 0x6856, 0x1f04, 0x269b, 0x00de, 0x0005,
+ 0x0006, 0x00d6, 0x0026, 0x2069, 0x0140, 0x2001, 0xad14, 0x2102,
+ 0x8114, 0x8214, 0x8214, 0x8214, 0x20a9, 0x0010, 0x6853, 0x0000,
+ 0xa006, 0x82ff, 0x1128, 0xa184, 0x000f, 0xa080, 0xacae, 0x2005,
+ 0x6856, 0x8211, 0x1f04, 0x26b0, 0x002e, 0x00de, 0x000e, 0x0005,
+ 0x00c6, 0x2061, 0xad00, 0x6030, 0x0110, 0xc09d, 0x0008, 0xc09c,
+ 0x6032, 0x00ce, 0x0005, 0x0156, 0x00d6, 0x0026, 0x0016, 0x0006,
+ 0x2069, 0x0140, 0x6980, 0xa116, 0x0180, 0xa112, 0x1230, 0x8212,
+ 0x8210, 0x22a8, 0x2001, 0x0402, 0x0018, 0x22a8, 0x2001, 0x0404,
+ 0x680e, 0x1f04, 0x26e0, 0x680f, 0x0000, 0x000e, 0x001e, 0x002e,
+ 0x00de, 0x015e, 0x0005, 0x2001, 0xad52, 0x2004, 0xd0c4, 0x0150,
+ 0xd0a4, 0x0140, 0xa006, 0x0046, 0x2020, 0x2009, 0x002e, 0x080c,
+ 0xa96c, 0x004e, 0x0005, 0x00f6, 0x0016, 0x0026, 0x2079, 0x0140,
+ 0x78c4, 0xd0dc, 0x0548, 0xa084, 0x0700, 0xa08e, 0x0300, 0x1520,
+ 0x2011, 0x0000, 0x2009, 0x0002, 0x2300, 0xa080, 0x0020, 0x2018,
+ 0x2300, 0x080c, 0x6665, 0x2011, 0x0030, 0x2200, 0x8007, 0xa085,
+ 0x004c, 0x78c2, 0x2009, 0x0204, 0x210c, 0x2200, 0xa100, 0x2009,
+ 0x0138, 0x200a, 0x080c, 0x574f, 0x1118, 0x2009, 0xaf8e, 0x200a,
+ 0x002e, 0x001e, 0x00fe, 0x0005, 0x78c3, 0x0000, 0x0cc8, 0x0126,
+ 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x2001, 0x0170, 0x200c,
+ 0x8000, 0x2014, 0xa184, 0x0003, 0x0110, 0x0804, 0x1a6a, 0x002e,
+ 0x001e, 0x000e, 0x012e, 0x0005, 0x0006, 0x2001, 0x0100, 0x2004,
+ 0xa082, 0x0005, 0x000e, 0x0268, 0x2001, 0x0170, 0x200c, 0xa18c,
+ 0x00ff, 0xa18e, 0x004c, 0x1128, 0x200c, 0xa18c, 0xff00, 0x810f,
+ 0x0010, 0x2009, 0x0000, 0x2001, 0x0204, 0x2004, 0xa108, 0x0005,
+ 0x0006, 0x0156, 0x00f6, 0x2079, 0x0100, 0x20a9, 0x000a, 0x7854,
+ 0xd08c, 0x1110, 0x1f04, 0x2767, 0x00fe, 0x015e, 0x000e, 0x0005,
+ 0x0016, 0x00c6, 0x0006, 0x2061, 0x0100, 0x6030, 0x0006, 0x6048,
+ 0x0006, 0x60e4, 0x0006, 0x60e8, 0x0006, 0x6050, 0x0006, 0x60f0,
+ 0x0006, 0x60ec, 0x0006, 0x600c, 0x0006, 0x6004, 0x0006, 0x6028,
+ 0x0006, 0x60e0, 0x0006, 0x602f, 0x0100, 0x602f, 0x0000, 0xe000,
+ 0xe000, 0xe000, 0xe000, 0x602f, 0x0040, 0x602f, 0x0000, 0x000e,
+ 0x60e2, 0x000e, 0x602a, 0x000e, 0x6006, 0x000e, 0x600e, 0x000e,
+ 0x60ee, 0x000e, 0x60f2, 0x000e, 0x6052, 0x000e, 0x60ea, 0x000e,
+ 0x60e6, 0x000e, 0x604a, 0x000e, 0x6032, 0x6036, 0x2008, 0x080c,
+ 0x26a0, 0x000e, 0x00ce, 0x001e, 0x0005, 0x2845, 0x2849, 0x284d,
+ 0x2853, 0x2859, 0x285f, 0x2865, 0x286d, 0x2875, 0x287b, 0x2881,
+ 0x2889, 0x2891, 0x2899, 0x28a1, 0x28ab, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b7, 0x28b7, 0x28bc,
+ 0x28bc, 0x28c3, 0x28c3, 0x28ca, 0x28ca, 0x28d3, 0x28d3, 0x28da,
+ 0x28da, 0x28e3, 0x28e3, 0x28ec, 0x28ec, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5,
+ 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x28b5, 0x0106, 0x0006, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c,
+ 0x2373, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c, 0x2373, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x080c, 0x223d, 0x0804, 0x28f7, 0x0106,
+ 0x0006, 0x080c, 0x223d, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c,
+ 0x2373, 0x080c, 0x223d, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c,
+ 0x2373, 0x080c, 0x223d, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c,
+ 0x228f, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c, 0x228f, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x080c, 0x2373, 0x080c, 0x228f, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x080c, 0x2373, 0x080c, 0x228f, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x080c, 0x223d, 0x080c, 0x228f, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x080c, 0x223d, 0x080c, 0x228f, 0x0804,
+ 0x28f7, 0x0106, 0x0006, 0x080c, 0x2373, 0x080c, 0x223d, 0x080c,
+ 0x228f, 0x0804, 0x28f7, 0x0106, 0x0006, 0x080c, 0x2373, 0x080c,
+ 0x223d, 0x080c, 0x228f, 0x0804, 0x28f7, 0xe000, 0x0cf0, 0x0106,
+ 0x0006, 0x080c, 0x272f, 0x04d8, 0x0106, 0x0006, 0x080c, 0x272f,
+ 0x080c, 0x2373, 0x04a0, 0x0106, 0x0006, 0x080c, 0x272f, 0x080c,
+ 0x223d, 0x0468, 0x0106, 0x0006, 0x080c, 0x272f, 0x080c, 0x2373,
+ 0x080c, 0x223d, 0x0420, 0x0106, 0x0006, 0x080c, 0x272f, 0x080c,
+ 0x228f, 0x00e8, 0x0106, 0x0006, 0x080c, 0x272f, 0x080c, 0x2373,
+ 0x080c, 0x228f, 0x00a0, 0x0106, 0x0006, 0x080c, 0x272f, 0x080c,
+ 0x223d, 0x080c, 0x228f, 0x0058, 0x0106, 0x0006, 0x080c, 0x272f,
+ 0x080c, 0x2373, 0x080c, 0x223d, 0x080c, 0x228f, 0x0000, 0x000e,
+ 0x010e, 0x000d, 0x00c6, 0x0026, 0x0046, 0x2021, 0x0000, 0x080c,
+ 0x502d, 0x1904, 0x29d4, 0x72d0, 0x2001, 0xaf9d, 0x2004, 0xa005,
+ 0x1110, 0xd29c, 0x0148, 0xd284, 0x1138, 0xd2bc, 0x1904, 0x29d4,
+ 0x080c, 0x29d8, 0x0804, 0x29d4, 0x080c, 0x574f, 0x1120, 0x709b,
+ 0xffff, 0x0804, 0x29d4, 0xd294, 0x0120, 0x709b, 0xffff, 0x0804,
+ 0x29d4, 0x2001, 0xad14, 0x203c, 0x7284, 0xd284, 0x0904, 0x2976,
+ 0xd28c, 0x1904, 0x2976, 0x0036, 0x7398, 0xa38e, 0xffff, 0x1110,
+ 0x2019, 0x0001, 0x8314, 0xa2e0, 0xb3c0, 0x2c04, 0xa38c, 0x0001,
+ 0x0120, 0xa084, 0xff00, 0x8007, 0x0010, 0xa084, 0x00ff, 0xa70e,
+ 0x0560, 0xa08e, 0x0000, 0x0548, 0xa08e, 0x00ff, 0x1150, 0x7230,
+ 0xd284, 0x1538, 0x7284, 0xc28d, 0x7286, 0x709b, 0xffff, 0x003e,
+ 0x0428, 0x2009, 0x0000, 0x080c, 0x2676, 0x080c, 0x4c80, 0x11b8,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1150, 0x7030, 0xd08c,
+ 0x0118, 0x6000, 0xd0bc, 0x0120, 0x080c, 0x29eb, 0x0140, 0x0028,
+ 0x080c, 0x2b1a, 0x080c, 0x2a19, 0x0110, 0x8318, 0x0818, 0x739a,
+ 0x0010, 0x709b, 0xffff, 0x003e, 0x0804, 0x29d4, 0xa780, 0x2be6,
+ 0x203d, 0xa7bc, 0xff00, 0x873f, 0x2041, 0x007e, 0x7098, 0xa096,
+ 0xffff, 0x1120, 0x2009, 0x0000, 0x28a8, 0x0050, 0xa812, 0x0220,
+ 0x2008, 0xa802, 0x20a8, 0x0020, 0x709b, 0xffff, 0x0804, 0x29d4,
+ 0x2700, 0x0156, 0x0016, 0xa106, 0x05a0, 0xc484, 0x080c, 0x4cdc,
+ 0x0120, 0x080c, 0x4c80, 0x15a8, 0x0008, 0xc485, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x1130, 0x7030, 0xd08c, 0x01e8, 0x6000,
+ 0xd0bc, 0x11d0, 0x7284, 0xd28c, 0x0188, 0x6004, 0xa084, 0x00ff,
+ 0xa082, 0x0006, 0x02b0, 0xd484, 0x1118, 0x080c, 0x4c9f, 0x0028,
+ 0x080c, 0x2b9c, 0x0170, 0x080c, 0x2bc9, 0x0058, 0x080c, 0x2b1a,
+ 0x080c, 0x2a19, 0x0170, 0x0028, 0x080c, 0x2b9c, 0x0110, 0x0419,
+ 0x0140, 0x001e, 0x8108, 0x015e, 0x1f04, 0x2990, 0x709b, 0xffff,
+ 0x0018, 0x001e, 0x015e, 0x719a, 0x004e, 0x002e, 0x00ce, 0x0005,
+ 0x00c6, 0x0016, 0x709b, 0x0000, 0x2009, 0x007e, 0x080c, 0x4c80,
+ 0x1138, 0x080c, 0x2b1a, 0x04a9, 0x0118, 0x70d0, 0xc0bd, 0x70d2,
+ 0x001e, 0x00ce, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x2c68,
+ 0x2001, 0xad56, 0x2004, 0xa084, 0x00ff, 0x6842, 0x080c, 0x9807,
+ 0x01d8, 0x2d00, 0x601a, 0x080c, 0x9956, 0x601f, 0x0001, 0x2001,
+ 0x0000, 0x080c, 0x4c1e, 0x2001, 0x0000, 0x080c, 0x4c30, 0x0126,
+ 0x2091, 0x8000, 0x7094, 0x8000, 0x7096, 0x012e, 0x2009, 0x0004,
+ 0x080c, 0x80a7, 0xa085, 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e,
+ 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6, 0x2c68, 0x2001, 0xad56,
+ 0x2004, 0xa084, 0x00ff, 0x6842, 0x080c, 0x9807, 0x0550, 0x2d00,
+ 0x601a, 0x6800, 0xc0c4, 0x6802, 0x68a0, 0xa086, 0x007e, 0x0140,
+ 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1110, 0x080c, 0x2ad9,
+ 0x080c, 0x9956, 0x601f, 0x0001, 0x2001, 0x0000, 0x080c, 0x4c1e,
+ 0x2001, 0x0002, 0x080c, 0x4c30, 0x0126, 0x2091, 0x8000, 0x7094,
+ 0x8000, 0x7096, 0x012e, 0x2009, 0x0002, 0x080c, 0x80a7, 0xa085,
+ 0x0001, 0x00ce, 0x00de, 0x007e, 0x001e, 0x0005, 0x00c6, 0x0026,
+ 0x2009, 0x0080, 0x080c, 0x4c80, 0x1120, 0x0031, 0x0110, 0x70d7,
+ 0xffff, 0x002e, 0x00ce, 0x0005, 0x0016, 0x0076, 0x00d6, 0x00c6,
+ 0x2c68, 0x080c, 0x8022, 0x01d8, 0x2d00, 0x601a, 0x080c, 0x9956,
+ 0x601f, 0x0001, 0x2001, 0x0000, 0x080c, 0x4c1e, 0x2001, 0x0002,
+ 0x080c, 0x4c30, 0x0126, 0x2091, 0x8000, 0x70d8, 0x8000, 0x70da,
+ 0x012e, 0x2009, 0x0002, 0x080c, 0x80a7, 0xa085, 0x0001, 0x00ce,
+ 0x00de, 0x007e, 0x001e, 0x0005, 0x00c6, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x2009, 0x007f, 0x080c, 0x4c80, 0x1190, 0x2c68, 0x080c,
+ 0x8022, 0x0170, 0x2d00, 0x601a, 0x6312, 0x601f, 0x0001, 0x620a,
+ 0x080c, 0x9956, 0x2009, 0x0022, 0x080c, 0x80a7, 0xa085, 0x0001,
+ 0x012e, 0x00de, 0x00ce, 0x0005, 0x00e6, 0x00c6, 0x0066, 0x0036,
+ 0x0026, 0x080c, 0x68f3, 0x080c, 0x689d, 0x080c, 0x8a15, 0x2130,
+ 0x81ff, 0x0128, 0x20a9, 0x007e, 0x2009, 0x0000, 0x0020, 0x20a9,
+ 0x007f, 0x2009, 0x0000, 0x0016, 0x080c, 0x4cdc, 0x1120, 0x080c,
+ 0x4ecf, 0x080c, 0x493a, 0x001e, 0x8108, 0x1f04, 0x2ac3, 0x86ff,
+ 0x1110, 0x080c, 0x11d4, 0x002e, 0x003e, 0x006e, 0x00ce, 0x00ee,
+ 0x0005, 0x00e6, 0x00c6, 0x0036, 0x0026, 0x0016, 0x6218, 0x2270,
+ 0x72a0, 0x0026, 0x2019, 0x0029, 0x080c, 0x68e7, 0x0076, 0x2039,
+ 0x0000, 0x080c, 0x681d, 0x2c08, 0x080c, 0xa712, 0x007e, 0x001e,
+ 0x2e60, 0x080c, 0x4ecf, 0x6210, 0x6314, 0x080c, 0x493a, 0x6212,
+ 0x6316, 0x001e, 0x002e, 0x003e, 0x00ce, 0x00ee, 0x0005, 0x00e6,
+ 0x0006, 0x6018, 0xa080, 0x0028, 0x2004, 0xa086, 0x0080, 0x0150,
+ 0x2071, 0xad00, 0x7094, 0xa005, 0x0110, 0x8001, 0x7096, 0x000e,
+ 0x00ee, 0x0005, 0x2071, 0xad00, 0x70d8, 0xa005, 0x0dc0, 0x8001,
+ 0x70da, 0x0ca8, 0x6000, 0xc08c, 0x6002, 0x0005, 0x00f6, 0x00e6,
+ 0x00c6, 0x0036, 0x0026, 0x0016, 0x0156, 0x2178, 0x81ff, 0x1118,
+ 0x20a9, 0x0001, 0x0098, 0x2001, 0xad52, 0x2004, 0xd0c4, 0x0150,
+ 0xd0a4, 0x0140, 0xa006, 0x0046, 0x2020, 0x2009, 0x002d, 0x080c,
+ 0xa96c, 0x004e, 0x20a9, 0x00ff, 0x2011, 0x0000, 0x0026, 0xa28e,
+ 0x007e, 0x05c8, 0xa28e, 0x007f, 0x05b0, 0xa28e, 0x0080, 0x0598,
+ 0xa288, 0xae34, 0x210c, 0x81ff, 0x0570, 0x8fff, 0x05c1, 0x00c6,
+ 0x2160, 0x2001, 0x0001, 0x080c, 0x5037, 0x00ce, 0x2019, 0x0029,
+ 0x080c, 0x68e7, 0x0076, 0x2039, 0x0000, 0x080c, 0x681d, 0x00c6,
+ 0x0026, 0x2160, 0x6204, 0xa294, 0x00ff, 0xa286, 0x0006, 0x1118,
+ 0x6007, 0x0404, 0x0028, 0x2001, 0x0004, 0x8007, 0xa215, 0x6206,
+ 0x002e, 0x00ce, 0x0016, 0x2c08, 0x080c, 0xa712, 0x001e, 0x007e,
+ 0x2160, 0x080c, 0x4ecf, 0x002e, 0x8210, 0x1f04, 0x2b3e, 0x015e,
+ 0x001e, 0x002e, 0x003e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x0046,
+ 0x0026, 0x0016, 0x2001, 0xad52, 0x2004, 0xd0c4, 0x0148, 0xd0a4,
+ 0x0138, 0xa006, 0x2220, 0x8427, 0x2009, 0x0029, 0x080c, 0xa96c,
+ 0x001e, 0x002e, 0x004e, 0x0005, 0x0016, 0x0026, 0x0036, 0x00c6,
+ 0x7284, 0x82ff, 0x01f8, 0x2011, 0xad52, 0x2214, 0xd2ac, 0x11d0,
+ 0x2100, 0x080c, 0x268a, 0x81ff, 0x01b8, 0x2019, 0x0001, 0x8314,
+ 0xa2e0, 0xb3c0, 0x2c04, 0xd384, 0x0120, 0xa084, 0xff00, 0x8007,
+ 0x0010, 0xa084, 0x00ff, 0xa116, 0x0138, 0xa096, 0x00ff, 0x0110,
+ 0x8318, 0x0c68, 0xa085, 0x0001, 0x00ce, 0x003e, 0x002e, 0x001e,
+ 0x0005, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0xa180, 0xae34,
+ 0x2004, 0xa065, 0x0178, 0x0016, 0x00c6, 0x080c, 0x9807, 0x001e,
+ 0x090c, 0x14f6, 0x611a, 0x080c, 0x2ad9, 0x080c, 0x8078, 0x001e,
+ 0x080c, 0x4c9f, 0x012e, 0x00ce, 0x001e, 0x0005, 0x7eef, 0x7de8,
+ 0x7ce4, 0x80e2, 0x7be1, 0x80e0, 0x80dc, 0x80da, 0x7ad9, 0x80d6,
+ 0x80d5, 0x80d4, 0x80d3, 0x80d2, 0x80d1, 0x79ce, 0x78cd, 0x80cc,
+ 0x80cb, 0x80ca, 0x80c9, 0x80c7, 0x80c6, 0x77c5, 0x76c3, 0x80bc,
+ 0x80ba, 0x75b9, 0x80b6, 0x74b5, 0x73b4, 0x72b3, 0x80b2, 0x80b1,
+ 0x80ae, 0x71ad, 0x80ac, 0x70ab, 0x6faa, 0x6ea9, 0x80a7, 0x6da6,
+ 0x6ca5, 0x6ba3, 0x6a9f, 0x699e, 0x689d, 0x809b, 0x8098, 0x6797,
+ 0x6690, 0x658f, 0x6488, 0x6384, 0x6282, 0x8081, 0x8080, 0x617c,
+ 0x607a, 0x8079, 0x5f76, 0x8075, 0x8074, 0x8073, 0x8072, 0x8071,
+ 0x806e, 0x5e6d, 0x806c, 0x5d6b, 0x5c6a, 0x5b69, 0x8067, 0x5a66,
+ 0x5965, 0x5863, 0x575c, 0x565a, 0x5559, 0x8056, 0x8055, 0x5454,
+ 0x5353, 0x5252, 0x5151, 0x504e, 0x4f4d, 0x804c, 0x804b, 0x4e4a,
+ 0x4d49, 0x8047, 0x4c46, 0x8045, 0x8043, 0x803c, 0x803a, 0x8039,
+ 0x8036, 0x4b35, 0x8034, 0x4a33, 0x4932, 0x4831, 0x802e, 0x472d,
+ 0x462c, 0x452b, 0x442a, 0x4329, 0x4227, 0x8026, 0x8025, 0x4123,
+ 0x401f, 0x3f1e, 0x3e1d, 0x3d1b, 0x3c18, 0x8017, 0x8010, 0x3b0f,
+ 0x3a08, 0x8004, 0x3902, 0x8001, 0x8000, 0x8000, 0x3800, 0x3700,
+ 0x3600, 0x8000, 0x3500, 0x8000, 0x8000, 0x8000, 0x3400, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x3300, 0x3200, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x3100, 0x3000, 0x8000,
+ 0x8000, 0x2f00, 0x8000, 0x2e00, 0x2d00, 0x2c00, 0x8000, 0x8000,
+ 0x8000, 0x2b00, 0x8000, 0x2a00, 0x2900, 0x2800, 0x8000, 0x2700,
+ 0x2600, 0x2500, 0x2400, 0x2300, 0x2200, 0x8000, 0x8000, 0x2100,
+ 0x2000, 0x1f00, 0x1e00, 0x1d00, 0x1c00, 0x8000, 0x8000, 0x1b00,
+ 0x1a00, 0x8000, 0x1900, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x1800, 0x8000, 0x1700, 0x1600, 0x1500, 0x8000, 0x1400,
+ 0x1300, 0x1200, 0x1100, 0x1000, 0x0f00, 0x8000, 0x8000, 0x0e00,
+ 0x0d00, 0x0c00, 0x0b00, 0x0a00, 0x0900, 0x8000, 0x8000, 0x0800,
+ 0x0700, 0x8000, 0x0600, 0x8000, 0x8000, 0x8000, 0x0500, 0x0400,
+ 0x0300, 0x8000, 0x0200, 0x8000, 0x8000, 0x8000, 0x0100, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x2071, 0xad81,
+ 0x7003, 0x0002, 0xa006, 0x7012, 0x7016, 0x703a, 0x703e, 0x7033,
+ 0xad91, 0x7037, 0xad91, 0x7007, 0x0001, 0x2061, 0xadd1, 0x6003,
+ 0x0002, 0x0005, 0x1004, 0x2d0c, 0x0e04, 0x2d0c, 0x2071, 0xad81,
+ 0x2b78, 0x7818, 0xd084, 0x1140, 0x2a60, 0x7820, 0xa08e, 0x0069,
+ 0x1904, 0x2df1, 0x0804, 0x2d8a, 0x0005, 0x2071, 0xad81, 0x7004,
+ 0x0002, 0x2d15, 0x2d16, 0x2d1f, 0x2d30, 0x0005, 0x1004, 0x2d1e,
+ 0x0e04, 0x2d1e, 0x2b78, 0x7818, 0xd084, 0x01e8, 0x0005, 0x2b78,
+ 0x2061, 0xadd1, 0x6008, 0xa08e, 0x0100, 0x0128, 0xa086, 0x0200,
+ 0x0904, 0x2deb, 0x0005, 0x7014, 0x2068, 0x2a60, 0x7018, 0x0807,
+ 0x7010, 0x2068, 0x6834, 0xa086, 0x0103, 0x0108, 0x0005, 0x2a60,
+ 0x2b78, 0x7018, 0x0807, 0x2a60, 0x7820, 0xa08a, 0x0040, 0x1210,
+ 0x61c0, 0x0042, 0x2100, 0xa08a, 0x003f, 0x1a04, 0x2de8, 0x61c0,
+ 0x0804, 0x2d8a, 0x2dcc, 0x2df7, 0x2dff, 0x2e03, 0x2e0b, 0x2e11,
+ 0x2e15, 0x2e21, 0x2e24, 0x2e2e, 0x2e31, 0x2de8, 0x2de8, 0x2de8,
+ 0x2e34, 0x2de8, 0x2e43, 0x2e5a, 0x2e71, 0x2ee8, 0x2eed, 0x2f16,
+ 0x2f67, 0x2f78, 0x2f96, 0x2fcd, 0x2fd7, 0x2fe4, 0x2ff7, 0x3018,
+ 0x3021, 0x3057, 0x305d, 0x2de8, 0x3086, 0x2de8, 0x2de8, 0x2de8,
+ 0x2de8, 0x2de8, 0x308d, 0x3097, 0x2de8, 0x2de8, 0x2de8, 0x2de8,
+ 0x2de8, 0x2de8, 0x2de8, 0x2de8, 0x309f, 0x2de8, 0x2de8, 0x2de8,
+ 0x2de8, 0x2de8, 0x30b1, 0x30b9, 0x2de8, 0x2de8, 0x2de8, 0x2de8,
+ 0x2de8, 0x2de8, 0x0002, 0x30cb, 0x311f, 0x317a, 0x318a, 0x2de8,
+ 0x31a4, 0x35cb, 0x3fbb, 0x2de8, 0x2de8, 0x2de8, 0x2de8, 0x2de8,
+ 0x2de8, 0x2de8, 0x2de8, 0x2e2e, 0x2e31, 0x35cd, 0x2de8, 0x35da,
+ 0x403c, 0x4097, 0x40fb, 0x2de8, 0x415a, 0x4180, 0x419f, 0x2de8,
+ 0x2de8, 0x2de8, 0x2de8, 0x35de, 0x376b, 0x3785, 0x37a3, 0x3804,
+ 0x3858, 0x3863, 0x389a, 0x38a9, 0x38b8, 0x38bb, 0x38de, 0x3928,
+ 0x398e, 0x399b, 0x3a9c, 0x3bb3, 0x3bdc, 0x3cda, 0x3cfc, 0x3d08,
+ 0x3d41, 0x3e05, 0x2de8, 0x2de8, 0x2de8, 0x2de8, 0x3e6d, 0x3e88,
+ 0x3efa, 0x3fac, 0x713c, 0x0000, 0x2021, 0x4000, 0x080c, 0x3c39,
+ 0x0126, 0x2091, 0x8000, 0x0e04, 0x2dd8, 0x7818, 0xd084, 0x0110,
+ 0x012e, 0x0cb0, 0x7c22, 0x7926, 0x7a2a, 0x7b2e, 0x781b, 0x0001,
+ 0x2091, 0x4080, 0x7007, 0x0001, 0x2091, 0x5000, 0x012e, 0x0005,
+ 0x2021, 0x4001, 0x0c18, 0x2021, 0x4002, 0x0c00, 0x2021, 0x4003,
+ 0x08e8, 0x2021, 0x4005, 0x08d0, 0x2021, 0x4006, 0x08b8, 0xa02e,
+ 0x2520, 0x7b28, 0x7a2c, 0x7824, 0x7930, 0x0804, 0x3c46, 0x7823,
+ 0x0004, 0x7824, 0x0807, 0xa02e, 0x2520, 0x7b28, 0x7a2c, 0x7824,
+ 0x7930, 0x0804, 0x3c49, 0x7924, 0x7828, 0x2114, 0x200a, 0x0804,
+ 0x2dcc, 0x7924, 0x2114, 0x0804, 0x2dcc, 0x2099, 0x0009, 0x20a1,
+ 0x0009, 0x20a9, 0x0007, 0x53a3, 0x7924, 0x7a28, 0x7b2c, 0x0804,
+ 0x2dcc, 0x7824, 0x2060, 0x0090, 0x2009, 0x0002, 0x2011, 0x0001,
+ 0x2019, 0x001b, 0x783b, 0x0017, 0x0804, 0x2dcc, 0x7d38, 0x7c3c,
+ 0x0840, 0x7d38, 0x7c3c, 0x0888, 0x2061, 0x1000, 0xe10c, 0xa006,
+ 0x2c15, 0xa200, 0x8c60, 0x8109, 0x1dd8, 0x2010, 0xa005, 0x0904,
+ 0x2dcc, 0x0804, 0x2dee, 0x2069, 0xad51, 0x7824, 0x7930, 0xa11a,
+ 0x1a04, 0x2df4, 0x8019, 0x0904, 0x2df4, 0x684a, 0x6942, 0x782c,
+ 0x6852, 0x7828, 0x6856, 0xa006, 0x685a, 0x685e, 0x080c, 0x5a1c,
+ 0x0804, 0x2dcc, 0x2069, 0xad51, 0x7824, 0x7934, 0xa11a, 0x1a04,
+ 0x2df4, 0x8019, 0x0904, 0x2df4, 0x684e, 0x6946, 0x782c, 0x6862,
+ 0x7828, 0x6866, 0xa006, 0x686a, 0x686e, 0x080c, 0x50d9, 0x0804,
+ 0x2dcc, 0xa02e, 0x2520, 0x81ff, 0x1904, 0x2df1, 0x7924, 0x7b28,
+ 0x7a2c, 0x20a9, 0x0005, 0x20a1, 0xad88, 0x41a1, 0x080c, 0x3c05,
+ 0x0904, 0x2df1, 0x2009, 0x0020, 0x080c, 0x3c46, 0x701b, 0x2e89,
+ 0x0005, 0x6834, 0x2008, 0xa084, 0x00ff, 0xa096, 0x0011, 0x0120,
+ 0xa096, 0x0019, 0x1904, 0x2df1, 0x810f, 0xa18c, 0x00ff, 0x0904,
+ 0x2df1, 0x710e, 0x700c, 0x8001, 0x0528, 0x700e, 0x080c, 0x3c05,
+ 0x0904, 0x2df1, 0x2009, 0x0020, 0x2061, 0xadd1, 0x6224, 0x6328,
+ 0x642c, 0x6530, 0xa290, 0x0040, 0xa399, 0x0000, 0xa4a1, 0x0000,
+ 0xa5a9, 0x0000, 0x080c, 0x3c46, 0x701b, 0x2eb7, 0x0005, 0x6834,
+ 0xa084, 0x00ff, 0xa096, 0x0002, 0x0120, 0xa096, 0x000a, 0x1904,
+ 0x2df1, 0x08c0, 0x7010, 0x2068, 0x6838, 0xc0fd, 0x683a, 0x080c,
+ 0x4b7c, 0x1128, 0x7007, 0x0003, 0x701b, 0x2ed1, 0x0005, 0x080c,
+ 0x51df, 0x0126, 0x2091, 0x8000, 0x20a9, 0x0005, 0x2099, 0xad88,
+ 0x530a, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+ 0x0000, 0xad80, 0x000d, 0x2009, 0x0020, 0x012e, 0x0804, 0x3c49,
+ 0x61a8, 0x7824, 0x60aa, 0x0804, 0x2dcc, 0x2091, 0x8000, 0x7823,
+ 0x4000, 0x7827, 0x4953, 0x782b, 0x5020, 0x782f, 0x2020, 0x2009,
+ 0x017f, 0x2104, 0x7832, 0x3f00, 0x7836, 0x2061, 0x0100, 0x6200,
+ 0x2061, 0x0200, 0x603c, 0x8007, 0xa205, 0x783a, 0x2009, 0x04fd,
+ 0x2104, 0x783e, 0x781b, 0x0001, 0x2091, 0x5000, 0x2091, 0x4080,
+ 0x2071, 0x0010, 0x20c1, 0x00f0, 0x0804, 0x0427, 0x81ff, 0x1904,
+ 0x2df1, 0x7924, 0x810f, 0xa18c, 0x00ff, 0x080c, 0x4cdc, 0x1904,
+ 0x2df4, 0x7e38, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0210, 0x0804,
+ 0x2df4, 0x7c28, 0x7d2c, 0x080c, 0x4e96, 0xd28c, 0x1118, 0x080c,
+ 0x4e41, 0x0010, 0x080c, 0x4e6f, 0x1518, 0x2061, 0xb400, 0x0126,
+ 0x2091, 0x8000, 0x6000, 0xa086, 0x0000, 0x0148, 0x6010, 0xa06d,
+ 0x0130, 0x683c, 0xa406, 0x1118, 0x6840, 0xa506, 0x0150, 0x012e,
+ 0xace0, 0x0018, 0x2001, 0xad16, 0x2004, 0xac02, 0x1a04, 0x2df1,
+ 0x0c30, 0x080c, 0x929c, 0x012e, 0x0904, 0x2df1, 0x0804, 0x2dcc,
+ 0xa00e, 0x2001, 0x0005, 0x080c, 0x51df, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x9803, 0x080c, 0x510c, 0x012e, 0x0804, 0x2dcc, 0x81ff,
+ 0x1904, 0x2df1, 0x080c, 0x3c1a, 0x0904, 0x2df4, 0x080c, 0x4d96,
+ 0x0904, 0x2df1, 0x080c, 0x4ea2, 0x0904, 0x2df1, 0x0804, 0x2dcc,
+ 0x81ff, 0x1904, 0x2df1, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x080c,
+ 0x4f0d, 0x0904, 0x2df1, 0x2019, 0x0005, 0x080c, 0x4ebd, 0x0904,
+ 0x2df1, 0x7828, 0xa08a, 0x1000, 0x1a04, 0x2df4, 0x8003, 0x800b,
+ 0x810b, 0xa108, 0x080c, 0x6519, 0x0804, 0x2dcc, 0x0126, 0x2091,
+ 0x8000, 0x81ff, 0x0118, 0x2009, 0x0001, 0x0448, 0x2029, 0x00ff,
+ 0x644c, 0x2400, 0xa506, 0x01f0, 0x2508, 0x080c, 0x4cdc, 0x11d0,
+ 0x080c, 0x4f0d, 0x1128, 0x2009, 0x0002, 0x62b0, 0x2518, 0x00b8,
+ 0x2019, 0x0004, 0x080c, 0x4ebd, 0x1118, 0x2009, 0x0006, 0x0078,
+ 0x7824, 0xa08a, 0x1000, 0x1270, 0x8003, 0x800b, 0x810b, 0xa108,
+ 0x080c, 0x6519, 0x8529, 0x1ae8, 0x012e, 0x0804, 0x2dcc, 0x012e,
+ 0x0804, 0x2df1, 0x012e, 0x0804, 0x2df4, 0x080c, 0x3c1a, 0x0904,
+ 0x2df4, 0x080c, 0x4dfc, 0x080c, 0x4e96, 0x0804, 0x2dcc, 0x81ff,
+ 0x1904, 0x2df1, 0x080c, 0x3c1a, 0x0904, 0x2df4, 0x080c, 0x4ded,
+ 0x080c, 0x4e96, 0x0804, 0x2dcc, 0x81ff, 0x1904, 0x2df1, 0x080c,
+ 0x3c1a, 0x0904, 0x2df4, 0x080c, 0x4e71, 0x0904, 0x2df1, 0x080c,
+ 0x4bc0, 0x080c, 0x4e3a, 0x080c, 0x4e96, 0x0804, 0x2dcc, 0x080c,
+ 0x3c1a, 0x0904, 0x2df4, 0x080c, 0x4d96, 0x0904, 0x2df1, 0x62a0,
+ 0x2019, 0x0005, 0x00c6, 0x080c, 0x4ecf, 0x2061, 0x0000, 0x080c,
+ 0x68e7, 0x0076, 0x2039, 0x0000, 0x080c, 0x681d, 0x2009, 0x0000,
+ 0x080c, 0xa712, 0x007e, 0x00ce, 0x080c, 0x4e96, 0x0804, 0x2dcc,
+ 0x080c, 0x3c1a, 0x0904, 0x2df4, 0x080c, 0x4e96, 0x2208, 0x0804,
+ 0x2dcc, 0x0156, 0x00d6, 0x00e6, 0x2069, 0xae13, 0x6810, 0x6914,
+ 0xa10a, 0x1210, 0x2009, 0x0000, 0x6816, 0x2011, 0x0000, 0x2019,
+ 0x0000, 0x20a9, 0x007e, 0x2069, 0xae34, 0x2d04, 0xa075, 0x0130,
+ 0x704c, 0x0071, 0xa210, 0x7080, 0x0059, 0xa318, 0x8d68, 0x1f04,
+ 0x3035, 0x2300, 0xa218, 0x00ee, 0x00de, 0x015e, 0x0804, 0x2dcc,
+ 0x00f6, 0x0016, 0xa07d, 0x0140, 0x2001, 0x0000, 0x8000, 0x2f0c,
+ 0x81ff, 0x0110, 0x2178, 0x0cd0, 0x001e, 0x00fe, 0x0005, 0x2069,
+ 0xae13, 0x6910, 0x62ac, 0x0804, 0x2dcc, 0x81ff, 0x1904, 0x2df1,
+ 0x614c, 0xa190, 0x2be6, 0x2215, 0xa294, 0x00ff, 0x636c, 0x83ff,
+ 0x0108, 0x6270, 0x67d0, 0xd79c, 0x0118, 0x2031, 0x0001, 0x0090,
+ 0xd7ac, 0x0118, 0x2031, 0x0003, 0x0068, 0xd7a4, 0x0118, 0x2031,
+ 0x0002, 0x0040, 0x080c, 0x574f, 0x1118, 0x2031, 0x0004, 0x0010,
+ 0x2031, 0x0000, 0x7e3a, 0x7f3e, 0x0804, 0x2dcc, 0x613c, 0x6240,
+ 0x2019, 0xafa3, 0x231c, 0x0804, 0x2dcc, 0x0126, 0x2091, 0x8000,
+ 0x6134, 0xa006, 0x2010, 0x2018, 0x012e, 0x0804, 0x2dcc, 0x080c,
+ 0x3c2a, 0x0904, 0x2df4, 0x6244, 0x6338, 0x0804, 0x2dcc, 0x613c,
+ 0x6240, 0x7824, 0x603e, 0x7b28, 0x6342, 0x2069, 0xad51, 0x831f,
+ 0xa305, 0x6816, 0x782c, 0x2069, 0xafa3, 0x2d1c, 0x206a, 0x0804,
+ 0x2dcc, 0x0126, 0x2091, 0x8000, 0x7824, 0x6036, 0x012e, 0x0804,
+ 0x2dcc, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x7828, 0xa00d, 0x0904,
+ 0x2df4, 0x782c, 0xa005, 0x0904, 0x2df4, 0x6244, 0x6146, 0x6338,
+ 0x603a, 0x0804, 0x2dcc, 0x2001, 0xad00, 0x2004, 0xa086, 0x0003,
+ 0x1904, 0x2df1, 0x00c6, 0x2061, 0x0100, 0x7924, 0x810f, 0xa18c,
+ 0x00ff, 0xa196, 0x00ff, 0x1130, 0x2001, 0xad14, 0x2004, 0xa085,
+ 0xff00, 0x0078, 0xa182, 0x007f, 0x16a0, 0xa188, 0x2be6, 0x210d,
+ 0xa18c, 0x00ff, 0x2001, 0xad14, 0x2004, 0xa116, 0x0550, 0x810f,
+ 0xa105, 0x0126, 0x2091, 0x8000, 0x0006, 0x080c, 0x8022, 0x000e,
+ 0x01e0, 0x601a, 0x600b, 0xbc09, 0x601f, 0x0001, 0x080c, 0x3c05,
+ 0x01d8, 0x6837, 0x0000, 0x7007, 0x0003, 0x6833, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x701b, 0x3173, 0x2d00, 0x6012, 0x2009, 0x0032,
+ 0x080c, 0x80a7, 0x012e, 0x00ce, 0x0005, 0x012e, 0x00ce, 0x0804,
+ 0x2df1, 0x00ce, 0x0804, 0x2df4, 0x080c, 0x8078, 0x0cb0, 0x2001,
+ 0xad00, 0x2004, 0xa086, 0x0003, 0x1904, 0x2df1, 0x00c6, 0x2061,
+ 0x0100, 0x7924, 0x810f, 0xa18c, 0x00ff, 0xa196, 0x00ff, 0x1130,
+ 0x2001, 0xad14, 0x2004, 0xa085, 0xff00, 0x0078, 0xa182, 0x007f,
+ 0x16a0, 0xa188, 0x2be6, 0x210d, 0xa18c, 0x00ff, 0x2001, 0xad14,
+ 0x2004, 0xa116, 0x0550, 0x810f, 0xa105, 0x0126, 0x2091, 0x8000,
+ 0x0006, 0x080c, 0x8022, 0x000e, 0x01e0, 0x601a, 0x600b, 0xbc05,
+ 0x601f, 0x0001, 0x080c, 0x3c05, 0x01d8, 0x6837, 0x0000, 0x7007,
+ 0x0003, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x701b, 0x3173,
+ 0x2d00, 0x6012, 0x2009, 0x0032, 0x080c, 0x80a7, 0x012e, 0x00ce,
+ 0x0005, 0x012e, 0x00ce, 0x0804, 0x2df1, 0x00ce, 0x0804, 0x2df4,
+ 0x080c, 0x8078, 0x0cb0, 0x6830, 0xa086, 0x0100, 0x0904, 0x2df1,
+ 0x0804, 0x2dcc, 0x2061, 0xb048, 0x0126, 0x2091, 0x8000, 0x6000,
+ 0xd084, 0x0128, 0x6104, 0x6208, 0x012e, 0x0804, 0x2dcc, 0x012e,
+ 0x0804, 0x2df4, 0x81ff, 0x1904, 0x2df1, 0x080c, 0x574f, 0x0904,
+ 0x2df1, 0x0126, 0x2091, 0x8000, 0x6244, 0x6064, 0xa202, 0x0248,
+ 0xa085, 0x0001, 0x080c, 0x26c0, 0x080c, 0x436e, 0x012e, 0x0804,
+ 0x2dcc, 0x012e, 0x0804, 0x2df4, 0x0126, 0x2091, 0x8000, 0x7824,
+ 0xa084, 0x0007, 0x0002, 0x31b6, 0x31bf, 0x31c6, 0x31b3, 0x31b3,
+ 0x31b3, 0x31b3, 0x31b3, 0x012e, 0x0804, 0x2df4, 0x2009, 0x0114,
+ 0x2104, 0xa085, 0x0800, 0x200a, 0x080c, 0x332f, 0x0070, 0x2009,
+ 0x010b, 0x200b, 0x0010, 0x080c, 0x332f, 0x0038, 0x81ff, 0x0128,
+ 0x012e, 0x2021, 0x400b, 0x0804, 0x2dce, 0x0086, 0x0096, 0x00a6,
+ 0x00b6, 0x00c6, 0x00d6, 0x00e6, 0x00f6, 0x2009, 0x0101, 0x210c,
+ 0x0016, 0x2001, 0x0138, 0x200c, 0x2003, 0x0001, 0x0016, 0x2001,
+ 0x007a, 0x2034, 0x2001, 0x007b, 0x202c, 0xa006, 0x2048, 0x2050,
+ 0x2058, 0x080c, 0x3570, 0x080c, 0x34da, 0xa03e, 0x2720, 0x00f6,
+ 0x00e6, 0x00c6, 0x2d60, 0x2071, 0xb01e, 0x2079, 0x0020, 0x00d6,
+ 0x2069, 0x0000, 0x6824, 0xd0b4, 0x0140, 0x2001, 0x007d, 0x2004,
+ 0x783e, 0x2001, 0x007c, 0x2004, 0x783a, 0x00de, 0x2011, 0x0001,
+ 0x080c, 0x3486, 0x080c, 0x3486, 0x00ce, 0x00ee, 0x00fe, 0x080c,
+ 0x33d5, 0x080c, 0x34ae, 0x080c, 0x342b, 0x080c, 0x3394, 0x080c,
+ 0x33c5, 0x00f6, 0x2079, 0x0100, 0x7824, 0xd094, 0x0530, 0x7814,
+ 0xa084, 0x0184, 0xa085, 0x0010, 0x7816, 0x2079, 0x0140, 0x080c,
+ 0x330d, 0x1110, 0x00fe, 0x0430, 0x7804, 0xd0dc, 0x0dc0, 0x2079,
+ 0x0100, 0x7827, 0x0086, 0x7814, 0xa084, 0x0184, 0xa085, 0x0032,
+ 0x7816, 0x080c, 0x330d, 0x1110, 0x00fe, 0x00a0, 0x7824, 0xd0bc,
+ 0x0dc0, 0x7827, 0x0080, 0xa026, 0x7c16, 0x7824, 0xd0ac, 0x0130,
+ 0x8b58, 0x080c, 0x3317, 0x00fe, 0x0804, 0x32d7, 0x00fe, 0x080c,
+ 0x330d, 0x1150, 0x8948, 0x2001, 0x007a, 0x2602, 0x2001, 0x007b,
+ 0x2502, 0x080c, 0x3317, 0x0088, 0x87ff, 0x0140, 0x2001, 0x0201,
+ 0x2004, 0xa005, 0x1904, 0x3211, 0x8739, 0x0038, 0x2001, 0xaffd,
+ 0x2004, 0xa086, 0x0000, 0x1904, 0x3211, 0x2001, 0x0033, 0x2003,
+ 0x00f6, 0x8631, 0x1208, 0x8529, 0x2500, 0xa605, 0x0904, 0x32d7,
+ 0x7824, 0xd0bc, 0x0128, 0x2900, 0xaa05, 0xab05, 0x1904, 0x32d7,
+ 0x6033, 0x000d, 0x2001, 0x0030, 0x2003, 0x0004, 0x7824, 0xd0ac,
+ 0x1148, 0x2001, 0xaffd, 0x2003, 0x0003, 0x2001, 0x0030, 0x2003,
+ 0x0009, 0x0040, 0x6027, 0x0001, 0x2001, 0x0075, 0x2004, 0xa005,
+ 0x0108, 0x6026, 0x2c00, 0x601a, 0x20e1, 0x9040, 0x2d00, 0x681a,
+ 0x6833, 0x000d, 0x7824, 0xd0a4, 0x1180, 0x6827, 0x0000, 0x00c6,
+ 0x20a9, 0x0004, 0x2061, 0x0020, 0x6003, 0x0008, 0x2001, 0x0203,
+ 0x2004, 0x1f04, 0x32ac, 0x00ce, 0x0040, 0x6827, 0x0001, 0x2001,
+ 0x0074, 0x2004, 0xa005, 0x0108, 0x6826, 0x00f6, 0x00c6, 0x2079,
+ 0x0100, 0x2061, 0x0020, 0x7827, 0x0002, 0x2001, 0x0072, 0x2004,
+ 0xa084, 0xfff8, 0x601a, 0x0006, 0x2001, 0x0073, 0x2004, 0x601e,
+ 0x78c6, 0x000e, 0x78ca, 0x00ce, 0x00fe, 0x0804, 0x31ef, 0x2061,
+ 0x0100, 0x6027, 0x0002, 0x001e, 0x61e2, 0x001e, 0x6106, 0x7824,
+ 0xa084, 0x0003, 0xa086, 0x0002, 0x0188, 0x20e1, 0x9028, 0x6050,
+ 0xa084, 0xf7ef, 0x6052, 0x602f, 0x0000, 0x602c, 0xc0ac, 0x602e,
+ 0x604b, 0xf7f7, 0x6043, 0x0090, 0x6043, 0x0010, 0x2908, 0x2a10,
+ 0x2b18, 0x2b00, 0xaa05, 0xa905, 0x00fe, 0x00ee, 0x00de, 0x00ce,
+ 0x00be, 0x00ae, 0x009e, 0x008e, 0x1118, 0x012e, 0x0804, 0x2dcc,
+ 0x012e, 0x2021, 0x400c, 0x0804, 0x2dce, 0xa085, 0x0001, 0x1d04,
+ 0x3316, 0x2091, 0x6000, 0x8420, 0xa486, 0x0064, 0x0005, 0x2001,
+ 0x0105, 0x2003, 0x0010, 0x2001, 0x0030, 0x2003, 0x0004, 0x2001,
+ 0x0020, 0x2003, 0x0004, 0x2001, 0xaffd, 0x2003, 0x0000, 0x2001,
+ 0xb01e, 0x2003, 0x0000, 0x20e1, 0xf000, 0xa026, 0x0005, 0x00f6,
+ 0x2079, 0x0100, 0x2001, 0xad14, 0x200c, 0x7932, 0x7936, 0x080c,
+ 0x26a0, 0x7850, 0xa084, 0x0980, 0xa085, 0x0030, 0x7852, 0x2019,
+ 0x01f4, 0x8319, 0x1df0, 0xa084, 0x0980, 0x7852, 0x782c, 0xc0ad,
+ 0x782e, 0x20a9, 0x0046, 0x1d04, 0x334b, 0x2091, 0x6000, 0x1f04,
+ 0x334b, 0x7850, 0xa085, 0x0400, 0x7852, 0x2001, 0x0009, 0x2004,
+ 0xa084, 0x0003, 0xa086, 0x0001, 0x1118, 0x782c, 0xc0ac, 0x782e,
+ 0x784b, 0xf7f7, 0x7843, 0x0090, 0x7843, 0x0010, 0x20a9, 0x000e,
+ 0xe000, 0x1f04, 0x3368, 0x7850, 0xa085, 0x1400, 0x7852, 0x2019,
+ 0x61a8, 0x7854, 0xe000, 0xe000, 0xd08c, 0x1110, 0x8319, 0x1dc8,
+ 0x7827, 0x0048, 0x7850, 0xa085, 0x0400, 0x7852, 0x7843, 0x0040,
+ 0x2019, 0x01f4, 0xe000, 0xe000, 0x8319, 0x1de0, 0x2001, 0x0140,
+ 0x2003, 0x0100, 0x7827, 0x0020, 0x7843, 0x0000, 0x2003, 0x0000,
+ 0x7827, 0x0048, 0x00fe, 0x0005, 0x7824, 0xd0ac, 0x11c8, 0x00f6,
+ 0x00e6, 0x2071, 0xaffd, 0x2079, 0x0030, 0x2001, 0x0201, 0x2004,
+ 0xa005, 0x0160, 0x7000, 0xa086, 0x0000, 0x1140, 0x0051, 0xd0bc,
+ 0x0108, 0x8738, 0x7003, 0x0003, 0x7803, 0x0019, 0x00ee, 0x00fe,
+ 0x0005, 0x780c, 0xa08c, 0x0070, 0x0178, 0x2009, 0x007a, 0x260a,
+ 0x2009, 0x007b, 0x250a, 0xd0b4, 0x0108, 0x8a50, 0xd0ac, 0x0108,
+ 0x8948, 0xd0a4, 0x0108, 0x8b58, 0x0005, 0x00f6, 0x2079, 0x0200,
+ 0x781c, 0xd084, 0x0140, 0x20e1, 0x0007, 0x20e1, 0x2000, 0x2001,
+ 0x020a, 0x2004, 0x0ca8, 0x00fe, 0x0005, 0x00e6, 0x2071, 0x0100,
+ 0x2009, 0xad14, 0x210c, 0x716e, 0x7063, 0x0100, 0x7166, 0x719e,
+ 0x706b, 0x0000, 0x7073, 0x0809, 0x7077, 0x0008, 0x7078, 0xa080,
+ 0x0100, 0x707a, 0x7080, 0x8000, 0x7082, 0x7087, 0xaaaa, 0xa006,
+ 0x708a, 0x708e, 0x707e, 0x70d6, 0x70ab, 0x0036, 0x70af, 0x95d5,
+ 0x7027, 0x0080, 0x7014, 0xa084, 0x0184, 0xa085, 0x0032, 0x7016,
+ 0x080c, 0x34ae, 0x080c, 0x330d, 0x1110, 0x8421, 0x0028, 0x7024,
+ 0xd0bc, 0x0db0, 0x7027, 0x0080, 0x00f6, 0x00e6, 0x2071, 0xaffd,
+ 0x2079, 0x0030, 0x00d6, 0x2069, 0x0000, 0x6824, 0xd0b4, 0x0120,
+ 0x683c, 0x783e, 0x6838, 0x783a, 0x00de, 0x2011, 0x0011, 0x080c,
+ 0x3486, 0x2011, 0x0001, 0x080c, 0x3486, 0x00ee, 0x00fe, 0x7017,
+ 0x0000, 0x00ee, 0x0005, 0x00f6, 0x00e6, 0x2071, 0xaffd, 0x2079,
+ 0x0030, 0x7904, 0xd1fc, 0x0904, 0x3483, 0x7803, 0x0002, 0xa026,
+ 0xd19c, 0x1904, 0x347f, 0x7000, 0x0002, 0x3483, 0x3441, 0x3465,
+ 0x347f, 0xd1bc, 0x1150, 0xd1dc, 0x1150, 0x8001, 0x7002, 0x2011,
+ 0x0001, 0x04e1, 0x05c0, 0x04d1, 0x04b0, 0x780f, 0x0000, 0x7820,
+ 0x7924, 0x7803, 0x0004, 0x7822, 0x7926, 0x2001, 0x0201, 0x200c,
+ 0x81ff, 0x0de8, 0x080c, 0x33b1, 0x2009, 0x0001, 0x7808, 0xd0ec,
+ 0x0110, 0x2009, 0x0011, 0x7902, 0x00f0, 0x8001, 0x7002, 0xa184,
+ 0x0880, 0x1138, 0x7804, 0xd0fc, 0x1940, 0x2011, 0x0001, 0x00b1,
+ 0x0090, 0x6030, 0xa092, 0x0004, 0xa086, 0x0009, 0x1120, 0x6000,
+ 0x601a, 0x2011, 0x0025, 0x6232, 0xd1dc, 0x1988, 0x0870, 0x7803,
+ 0x0004, 0x7003, 0x0000, 0x00ee, 0x00fe, 0x0005, 0x6024, 0xa005,
+ 0x0520, 0x8001, 0x6026, 0x6018, 0x6130, 0xa140, 0x2804, 0x7832,
+ 0x8840, 0x2804, 0x7836, 0x8840, 0x2804, 0x7822, 0x8840, 0x2804,
+ 0x7826, 0x8840, 0x7a02, 0x7000, 0x8000, 0x7002, 0x6018, 0xa802,
+ 0xa08a, 0x0029, 0x1138, 0x6018, 0xa080, 0x0001, 0x2004, 0x601a,
+ 0x2001, 0x000d, 0x6032, 0xa085, 0x0001, 0x0005, 0x00f6, 0x00e6,
+ 0x00c6, 0x2071, 0xb01e, 0x2079, 0x0020, 0x7904, 0xd1fc, 0x01f0,
+ 0x7803, 0x0002, 0x2d60, 0xa026, 0x7000, 0x0002, 0x34d6, 0x34c1,
+ 0x34cd, 0x8001, 0x7002, 0xd19c, 0x1188, 0x2011, 0x0001, 0x080c,
+ 0x3486, 0x0160, 0x080c, 0x3486, 0x0048, 0x8001, 0x7002, 0x7804,
+ 0xd0fc, 0x1d30, 0x2011, 0x0001, 0x080c, 0x3486, 0x00ce, 0x00ee,
+ 0x00fe, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x2061, 0x0200, 0x601b,
+ 0x0004, 0x2061, 0x0100, 0x60cf, 0x0400, 0x6004, 0xc0ac, 0xa085,
+ 0x0200, 0x6006, 0x2001, 0x0074, 0x2004, 0xa005, 0x01f8, 0x2038,
+ 0x2001, 0x0076, 0x2024, 0x2001, 0x0077, 0x201c, 0x080c, 0x3c05,
+ 0x6833, 0x000d, 0x6f26, 0x2d00, 0x681a, 0xa78a, 0x0007, 0x0220,
+ 0x2138, 0x2009, 0x0007, 0x0010, 0x2708, 0xa03e, 0x6818, 0xa080,
+ 0x000d, 0x04a1, 0x1d90, 0x2d00, 0x681a, 0x0088, 0x080c, 0x3c05,
+ 0x6833, 0x000d, 0x2070, 0x6827, 0x0001, 0x2d00, 0x681a, 0x2001,
+ 0x0076, 0x2004, 0x2072, 0x2001, 0x0077, 0x2004, 0x7006, 0x2061,
+ 0x0020, 0x2079, 0x0100, 0x6013, 0x0400, 0x20e1, 0x9040, 0x2001,
+ 0x0072, 0x2004, 0xa084, 0xfff8, 0x700a, 0x601a, 0x0006, 0x2001,
+ 0x0073, 0x2004, 0x700e, 0x601e, 0x78c6, 0x000e, 0x78ca, 0xa006,
+ 0x603a, 0x603e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x00e6, 0x2071,
+ 0x0010, 0x20a0, 0x2099, 0x0014, 0x7003, 0x0026, 0x7432, 0x7336,
+ 0xa006, 0x703a, 0x703e, 0x810b, 0x810b, 0x21a8, 0x810b, 0x7122,
+ 0x7003, 0x0041, 0x7004, 0xd0fc, 0x0de8, 0x7003, 0x0002, 0x7003,
+ 0x0040, 0x53a5, 0x7430, 0x7334, 0x87ff, 0x0180, 0x00c6, 0x00d6,
+ 0x2d60, 0x00c6, 0x080c, 0x3c05, 0x00ce, 0x6018, 0x2070, 0x2d00,
+ 0x7006, 0x601a, 0x00de, 0x00ce, 0xa085, 0x0001, 0x00ee, 0x0005,
+ 0x00e6, 0x2001, 0x0075, 0x2004, 0xa005, 0x0508, 0x2038, 0x2001,
+ 0x0078, 0x2024, 0x2001, 0x0079, 0x201c, 0x080c, 0x3c05, 0x2d60,
+ 0x6833, 0x000d, 0x6f26, 0x2d00, 0x681a, 0xa78a, 0x0007, 0x0220,
+ 0x2138, 0x2009, 0x0007, 0x0010, 0x2708, 0xa03e, 0x6818, 0xa080,
+ 0x000d, 0x080c, 0x353e, 0x1d88, 0x2d00, 0x681a, 0x00e0, 0x080c,
+ 0x3c05, 0x2d60, 0x6033, 0x000d, 0x2070, 0x6027, 0x0001, 0x2c00,
+ 0x601a, 0x2001, 0x0078, 0x2004, 0x2072, 0x2001, 0x0079, 0x2004,
+ 0x7006, 0x2001, 0x0072, 0x2004, 0xa084, 0xfff8, 0x700a, 0x2001,
+ 0x0073, 0x2004, 0x700e, 0x2001, 0x0030, 0x2003, 0x0004, 0x7824,
+ 0xd0ac, 0x1178, 0x2001, 0x0101, 0x200c, 0xc1ed, 0x2102, 0x6027,
+ 0x0000, 0x2001, 0xaffd, 0x2003, 0x0003, 0x2001, 0x0030, 0x2003,
+ 0x0009, 0x00ee, 0x0005, 0x0804, 0x2dcc, 0x0126, 0x2091, 0x8000,
+ 0x20a9, 0x0011, 0x2001, 0xad40, 0x20a0, 0xa006, 0x40a4, 0x012e,
+ 0x0804, 0x2dcc, 0x7d38, 0x7c3c, 0x0804, 0x2e73, 0x080c, 0x3c05,
+ 0x0904, 0x2df1, 0x080c, 0x574f, 0x0110, 0x080c, 0x491f, 0x2009,
+ 0x001c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3c46, 0x701b,
+ 0x35f2, 0x0005, 0xade8, 0x000d, 0x6800, 0xa005, 0x0904, 0x2df4,
+ 0x6804, 0xd0ac, 0x0118, 0xd0a4, 0x0904, 0x2df4, 0xd094, 0x00c6,
+ 0x2061, 0x0100, 0x6104, 0x0138, 0x6200, 0xa292, 0x0005, 0x0218,
+ 0xa18c, 0xffdf, 0x0010, 0xa18d, 0x0020, 0x6106, 0x00ce, 0xd08c,
+ 0x00c6, 0x2061, 0x0100, 0x6104, 0x0118, 0xa18d, 0x0010, 0x0010,
+ 0xa18c, 0xffef, 0x6106, 0x00ce, 0x2009, 0x0100, 0x210c, 0xa18a,
+ 0x0002, 0x0268, 0xd084, 0x0158, 0x6a28, 0xa28a, 0x007f, 0x1a04,
+ 0x2df4, 0xa288, 0x2be6, 0x210d, 0xa18c, 0x00ff, 0x6156, 0xd0dc,
+ 0x0130, 0x6828, 0xa08a, 0x007f, 0x1a04, 0x2df4, 0x604e, 0x6808,
+ 0xa08a, 0x0100, 0x0a04, 0x2df4, 0xa08a, 0x0841, 0x1a04, 0x2df4,
+ 0xa084, 0x0007, 0x1904, 0x2df4, 0x680c, 0xa005, 0x0904, 0x2df4,
+ 0x6810, 0xa005, 0x0904, 0x2df4, 0x6848, 0x6940, 0xa10a, 0x1a04,
+ 0x2df4, 0x8001, 0x0904, 0x2df4, 0x684c, 0x6944, 0xa10a, 0x1a04,
+ 0x2df4, 0x8001, 0x0904, 0x2df4, 0x6804, 0xd0fc, 0x0560, 0x080c,
+ 0x3c05, 0x0904, 0x2df1, 0x2009, 0x0014, 0x7a2c, 0x7b28, 0x7c3c,
+ 0x7d38, 0xa290, 0x0038, 0xa399, 0x0000, 0x080c, 0x3c46, 0x701b,
+ 0x3672, 0x0005, 0xade8, 0x000d, 0x20a9, 0x0014, 0x2d98, 0x2069,
+ 0xad6d, 0x2da0, 0x53a3, 0x7010, 0xa0e8, 0x000d, 0x2001, 0xad71,
+ 0x200c, 0xd1e4, 0x0140, 0x00c6, 0x2061, 0x0100, 0x6004, 0xa085,
+ 0x0b00, 0x6006, 0x00ce, 0x20a9, 0x001c, 0x2d98, 0x2069, 0xad51,
+ 0x2da0, 0x53a3, 0x6814, 0xa08c, 0x00ff, 0x613e, 0x8007, 0xa084,
+ 0x00ff, 0x6042, 0x080c, 0x5a1c, 0x080c, 0x5070, 0x080c, 0x50d9,
+ 0x6000, 0xa086, 0x0000, 0x1904, 0x3755, 0x6808, 0x602a, 0x080c,
+ 0x22f8, 0x0006, 0x2001, 0x0100, 0x2004, 0xa082, 0x0005, 0x000e,
+ 0x0268, 0x2009, 0x0170, 0x200b, 0x0080, 0xe000, 0xe000, 0x200b,
+ 0x0000, 0x0036, 0x6b08, 0x080c, 0x26fb, 0x003e, 0x6818, 0x691c,
+ 0x6a20, 0x6b24, 0x8007, 0x810f, 0x8217, 0x831f, 0x6016, 0x611a,
+ 0x621e, 0x6322, 0x6c04, 0xd4f4, 0x0148, 0x6830, 0x6934, 0x6a38,
+ 0x6b3c, 0x8007, 0x810f, 0x8217, 0x831f, 0x0010, 0xa084, 0xf0ff,
+ 0x6006, 0x610a, 0x620e, 0x6312, 0x8007, 0x810f, 0x8217, 0x831f,
+ 0x20a9, 0x0004, 0x20a1, 0xafad, 0x40a1, 0x080c, 0x659c, 0x6904,
+ 0xd1fc, 0x0520, 0x00c6, 0x2009, 0x0000, 0x20a9, 0x0001, 0x6b70,
+ 0xd384, 0x01c8, 0x0020, 0x839d, 0x12b0, 0x3508, 0x8109, 0x080c,
+ 0x5fa9, 0x6878, 0x6016, 0x6874, 0x2008, 0xa084, 0xff00, 0x8007,
+ 0x600a, 0xa184, 0x00ff, 0x6006, 0x8108, 0x1118, 0x6003, 0x0003,
+ 0x0010, 0x6003, 0x0001, 0x1f04, 0x36f3, 0x00ce, 0x2069, 0xad51,
+ 0x2001, 0xaf9d, 0x6a80, 0xa294, 0x0030, 0xa28e, 0x0000, 0x0170,
+ 0xa28e, 0x0010, 0x0118, 0xa28e, 0x0020, 0x0140, 0x2003, 0xaaaa,
+ 0x080c, 0x2744, 0x2001, 0xaf8e, 0x2102, 0x0008, 0x2102, 0x00c6,
+ 0x2061, 0x0100, 0x602f, 0x0040, 0x602f, 0x0000, 0x00ce, 0x080c,
+ 0x574f, 0x0128, 0x080c, 0x3e5f, 0x0110, 0x080c, 0x26c0, 0x60c4,
+ 0xa005, 0x01b0, 0x6003, 0x0001, 0x2009, 0x373f, 0x00c0, 0x080c,
+ 0x574f, 0x1158, 0x2011, 0x566e, 0x080c, 0x650d, 0x2001, 0xaf9e,
+ 0x2003, 0x0000, 0x080c, 0x569a, 0x0040, 0x080c, 0x485e, 0x0028,
+ 0x6003, 0x0004, 0x2009, 0x3755, 0x0010, 0x0804, 0x2dcc, 0x2001,
+ 0x0100, 0x2004, 0xa082, 0x0005, 0x0258, 0x2001, 0x0170, 0x2004,
+ 0xa084, 0x00ff, 0xa086, 0x004c, 0x1118, 0x2091, 0x309d, 0x0817,
+ 0x2091, 0x301d, 0x0817, 0x6000, 0xa086, 0x0000, 0x0904, 0x2df1,
+ 0x2069, 0xad51, 0x7830, 0x6842, 0x7834, 0x6846, 0x6804, 0xd0fc,
+ 0x0118, 0x2009, 0x0030, 0x0010, 0x2009, 0x001c, 0x2d00, 0x7a2c,
+ 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3c49, 0xa006, 0x080c, 0x26c0,
+ 0x81ff, 0x1904, 0x2df1, 0x080c, 0x574f, 0x1178, 0x2001, 0xaf9e,
+ 0x2003, 0x0001, 0x2001, 0xad00, 0x2003, 0x0001, 0xa085, 0x0001,
+ 0x080c, 0x5793, 0x080c, 0x569a, 0x0020, 0x080c, 0x491f, 0x080c,
+ 0x485e, 0x0804, 0x2dcc, 0x81ff, 0x1904, 0x2df1, 0x080c, 0x574f,
+ 0x1110, 0x0804, 0x2df1, 0x6184, 0x81ff, 0x0198, 0x703f, 0x0000,
+ 0x2001, 0xb3c0, 0x2009, 0x0040, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x3c49, 0x701b, 0x2dca, 0x012e,
+ 0x0005, 0x703f, 0x0001, 0x00d6, 0x2069, 0xb3c0, 0x20a9, 0x0040,
+ 0x20a1, 0xb3c0, 0x2019, 0xffff, 0x43a4, 0x654c, 0xa588, 0x2be6,
+ 0x210d, 0xa18c, 0x00ff, 0x216a, 0xa00e, 0x2011, 0x0002, 0x2100,
+ 0xa506, 0x01a8, 0x080c, 0x4cdc, 0x1190, 0x6014, 0x821c, 0x0238,
+ 0xa398, 0xb3c0, 0xa085, 0xff00, 0x8007, 0x201a, 0x0038, 0xa398,
+ 0xb3c0, 0x2324, 0xa4a4, 0xff00, 0xa405, 0x201a, 0x8210, 0x8108,
+ 0xa182, 0x0080, 0x1208, 0x0c18, 0x8201, 0x8007, 0x2d0c, 0xa105,
+ 0x206a, 0x00de, 0x20a9, 0x0040, 0x20a1, 0xb3c0, 0x2099, 0xb3c0,
+ 0x080c, 0x48be, 0x0804, 0x37b0, 0x080c, 0x3c2a, 0x0904, 0x2df4,
+ 0x00c6, 0x080c, 0x3c05, 0x00ce, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x2df1, 0x2001, 0xad52, 0x2004, 0xd0b4, 0x01f0, 0x6000, 0xd08c,
+ 0x11d8, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x11a8, 0x6837,
+ 0x0000, 0x6838, 0xc0fd, 0x683a, 0x080c, 0x970b, 0x1120, 0x2009,
+ 0x0003, 0x0804, 0x2df1, 0x7007, 0x0003, 0x701b, 0x3830, 0x0005,
+ 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x20a9, 0x002b, 0x2c98, 0xade8,
+ 0x0002, 0x2da0, 0x53a3, 0x20a9, 0x0004, 0xac80, 0x0006, 0x2098,
+ 0xad80, 0x0006, 0x20a0, 0x080c, 0x48be, 0x20a9, 0x0004, 0xac80,
+ 0x000a, 0x2098, 0xad80, 0x000a, 0x20a0, 0x080c, 0x48be, 0x2d00,
+ 0x2009, 0x002b, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3c49,
+ 0x81ff, 0x1904, 0x2df1, 0x080c, 0x3c1a, 0x0904, 0x2df4, 0x080c,
+ 0x4eab, 0x0804, 0x2dcc, 0x81ff, 0x1904, 0x2df1, 0x7828, 0xa08a,
+ 0x1000, 0x1a04, 0x2df4, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x080c,
+ 0x4f0d, 0x0904, 0x2df1, 0x2019, 0x0004, 0x080c, 0x4ebd, 0x7924,
+ 0x810f, 0x7a28, 0x0011, 0x0804, 0x2dcc, 0xa186, 0x00ff, 0x0110,
+ 0x0071, 0x0060, 0x2029, 0x007e, 0x2061, 0xad00, 0x644c, 0x2400,
+ 0xa506, 0x0110, 0x2508, 0x0019, 0x8529, 0x1ec8, 0x0005, 0x080c,
+ 0x4cdc, 0x1138, 0x2200, 0x8003, 0x800b, 0x810b, 0xa108, 0x080c,
+ 0x6519, 0x0005, 0x81ff, 0x1904, 0x2df1, 0x080c, 0x3c1a, 0x0904,
+ 0x2df4, 0x080c, 0x4d96, 0x0904, 0x2df1, 0x080c, 0x4eb4, 0x0804,
+ 0x2dcc, 0x81ff, 0x1904, 0x2df1, 0x080c, 0x3c1a, 0x0904, 0x2df4,
+ 0x080c, 0x4d96, 0x0904, 0x2df1, 0x080c, 0x4ea2, 0x0804, 0x2dcc,
+ 0x6100, 0x0804, 0x2dcc, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x2001,
+ 0xad00, 0x2004, 0xa086, 0x0003, 0x1904, 0x2df1, 0x00d6, 0xace8,
+ 0x000a, 0x7924, 0xd184, 0x0110, 0xace8, 0x0006, 0x680c, 0x8007,
+ 0x783e, 0x6808, 0x8007, 0x783a, 0x6b04, 0x831f, 0x6a00, 0x8217,
+ 0x00de, 0x6100, 0xa18c, 0x0200, 0x0804, 0x2dcc, 0x7824, 0xa09c,
+ 0x00ff, 0xa39a, 0x0003, 0x1a04, 0x2df1, 0x624c, 0xa294, 0x00ff,
+ 0xa084, 0xff00, 0x8007, 0xa206, 0x1150, 0x2001, 0xad40, 0x2009,
+ 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3c49, 0x81ff,
+ 0x1904, 0x2df1, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x1904, 0x2df1, 0x00c6, 0x080c, 0x3c05,
+ 0x00ce, 0x0904, 0x2df1, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a,
+ 0x080c, 0x96b7, 0x0904, 0x2df1, 0x7007, 0x0003, 0x701b, 0x3919,
+ 0x0005, 0x6830, 0xa086, 0x0100, 0x0904, 0x2df1, 0xad80, 0x000e,
+ 0x2009, 0x000c, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3c49,
+ 0xa006, 0x080c, 0x26c0, 0x7824, 0xa084, 0x00ff, 0xa086, 0x00ff,
+ 0x0118, 0x81ff, 0x1904, 0x2df1, 0x080c, 0x574f, 0x0110, 0x080c,
+ 0x491f, 0x7828, 0xa08a, 0x1000, 0x1a04, 0x2df4, 0x7924, 0xa18c,
+ 0xff00, 0x810f, 0xa186, 0x00ff, 0x0138, 0xa182, 0x007f, 0x1a04,
+ 0x2df4, 0x2100, 0x080c, 0x268a, 0x0026, 0x00c6, 0x0126, 0x2091,
+ 0x8000, 0x2061, 0xafda, 0x601b, 0x0000, 0x601f, 0x0000, 0x080c,
+ 0x574f, 0x1178, 0x2001, 0xaf9e, 0x2003, 0x0001, 0x2001, 0xad00,
+ 0x2003, 0x0001, 0xa085, 0x0001, 0x080c, 0x5793, 0x080c, 0x569a,
+ 0x00a0, 0x2061, 0x0100, 0x2001, 0xad14, 0x2004, 0xa084, 0x00ff,
+ 0x810f, 0xa105, 0x604a, 0x6043, 0x0090, 0x6043, 0x0010, 0x2009,
+ 0x002d, 0x2011, 0x4883, 0x080c, 0x6593, 0x7924, 0xa18c, 0xff00,
+ 0x810f, 0x080c, 0x574f, 0x1110, 0x2009, 0x00ff, 0x7a28, 0x080c,
+ 0x387d, 0x012e, 0x00ce, 0x002e, 0x0804, 0x2dcc, 0x7924, 0xa18c,
+ 0xff00, 0x810f, 0x00c6, 0x080c, 0x4c80, 0x2c08, 0x00ce, 0x1904,
+ 0x2df4, 0x0804, 0x2dcc, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804,
+ 0x2df1, 0x60d0, 0xd0ac, 0x1130, 0xd09c, 0x1120, 0x2009, 0x0005,
+ 0x0804, 0x2df1, 0x080c, 0x3c05, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x2df1, 0x7924, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3c46,
+ 0x701b, 0x39bb, 0x0005, 0x2009, 0x0080, 0x080c, 0x4cdc, 0x1130,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x0120, 0x2021, 0x400a,
+ 0x0804, 0x2dce, 0x00d6, 0xade8, 0x000d, 0x6900, 0x6a08, 0x6b0c,
+ 0x6c10, 0x6d14, 0x6e18, 0x6820, 0xa0be, 0x0100, 0x0904, 0x3a32,
+ 0xa0be, 0x0112, 0x0904, 0x3a32, 0xa0be, 0x0113, 0x0904, 0x3a32,
+ 0xa0be, 0x0114, 0x0904, 0x3a32, 0xa0be, 0x0117, 0x0904, 0x3a32,
+ 0xa0be, 0x011a, 0x0904, 0x3a32, 0xa0be, 0x011c, 0x0904, 0x3a32,
+ 0xa0be, 0x0121, 0x05b0, 0xa0be, 0x0131, 0x0598, 0xa0be, 0x0171,
+ 0x05c8, 0xa0be, 0x0173, 0x05b0, 0xa0be, 0x01a1, 0x1120, 0x6830,
+ 0x8007, 0x6832, 0x04a8, 0xa0be, 0x0212, 0x0540, 0xa0be, 0x0213,
+ 0x0528, 0xa0be, 0x0214, 0x01b0, 0xa0be, 0x0217, 0x0168, 0xa0be,
+ 0x021a, 0x1120, 0x6838, 0x8007, 0x683a, 0x00e0, 0xa0be, 0x0300,
+ 0x01c8, 0x00de, 0x0804, 0x2df4, 0xad80, 0x0010, 0x20a9, 0x0007,
+ 0x080c, 0x3a78, 0xad80, 0x000e, 0x20a9, 0x0001, 0x080c, 0x3a78,
+ 0x0048, 0xad80, 0x000c, 0x080c, 0x3a86, 0x0050, 0xad80, 0x000e,
+ 0x080c, 0x3a86, 0xad80, 0x000c, 0x20a9, 0x0001, 0x080c, 0x3a78,
+ 0x00c6, 0x080c, 0x3c05, 0x0568, 0x6838, 0xc0fd, 0x683a, 0x6837,
+ 0x0119, 0x6853, 0x0000, 0x684f, 0x0020, 0x685b, 0x0001, 0x810b,
+ 0x697e, 0x6883, 0x0000, 0x6a86, 0x6b8a, 0x6c8e, 0x6d92, 0x6996,
+ 0x689b, 0x0000, 0x00ce, 0x00de, 0x6837, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x6823, 0x0000, 0x6804, 0x2068, 0x080c, 0x96d3, 0x1120,
+ 0x2009, 0x0003, 0x0804, 0x2df1, 0x7007, 0x0003, 0x701b, 0x3a6f,
+ 0x0005, 0x00ce, 0x00de, 0x2009, 0x0002, 0x0804, 0x2df1, 0x6820,
+ 0xa086, 0x8001, 0x1904, 0x2dcc, 0x2009, 0x0004, 0x0804, 0x2df1,
+ 0x0016, 0x2008, 0x2044, 0x8000, 0x204c, 0x8000, 0x290a, 0x8108,
+ 0x280a, 0x8108, 0x1f04, 0x3a7a, 0x001e, 0x0005, 0x0016, 0x00a6,
+ 0x00b6, 0x2008, 0x2044, 0x8000, 0x204c, 0x8000, 0x2054, 0x8000,
+ 0x205c, 0x2b0a, 0x8108, 0x2a0a, 0x8108, 0x290a, 0x8108, 0x280a,
+ 0x00be, 0x00ae, 0x001e, 0x0005, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x2df1, 0x7924, 0x2140, 0xa18c, 0xff00, 0x810f, 0x60d0,
+ 0xd0ac, 0x1120, 0xa182, 0x0080, 0x0a04, 0x2df4, 0xa182, 0x00ff,
+ 0x1a04, 0x2df4, 0x7a2c, 0x7b28, 0x606c, 0xa306, 0x1140, 0x6070,
+ 0xa24e, 0x0904, 0x2df4, 0xa9cc, 0xff00, 0x0904, 0x2df4, 0x00c6,
+ 0x080c, 0x3b58, 0x2c68, 0x00ce, 0x0538, 0xa0c6, 0x4000, 0x1180,
+ 0x00c6, 0x0006, 0x2d60, 0x2009, 0x0000, 0x080c, 0x4f6e, 0x1108,
+ 0xc185, 0x6000, 0xd0bc, 0x0108, 0xc18d, 0x000e, 0x00ce, 0x0088,
+ 0xa0c6, 0x4007, 0x1110, 0x2408, 0x0060, 0xa0c6, 0x4008, 0x1118,
+ 0x2708, 0x2610, 0x0030, 0xa0c6, 0x4009, 0x1108, 0x0010, 0x2001,
+ 0x4006, 0x2020, 0x0804, 0x2dce, 0x2d00, 0x7022, 0x0016, 0x00b6,
+ 0x00c6, 0x00e6, 0x2c70, 0x080c, 0x8022, 0x05d8, 0x2d00, 0x601a,
+ 0x080c, 0x9956, 0x2e58, 0x00ee, 0x00e6, 0x00c6, 0x080c, 0x3c05,
+ 0x00ce, 0x2b70, 0x1150, 0x080c, 0x8078, 0x00ee, 0x00ce, 0x00be,
+ 0x001e, 0x2009, 0x0002, 0x0804, 0x2df1, 0x6837, 0x0000, 0x683b,
+ 0x0000, 0x2d00, 0x6012, 0x6833, 0x0000, 0x6838, 0xc0fd, 0xd88c,
+ 0x0108, 0xc0f5, 0x683a, 0x0126, 0x2091, 0x8000, 0x080c, 0x2ad9,
+ 0x012e, 0x601f, 0x0001, 0x2001, 0x0000, 0x080c, 0x4c1e, 0x2001,
+ 0x0002, 0x080c, 0x4c30, 0x2009, 0x0002, 0x080c, 0x80a7, 0xa085,
+ 0x0001, 0x00ee, 0x00ce, 0x00be, 0x001e, 0x1120, 0x2009, 0x0003,
+ 0x0804, 0x2df1, 0x7007, 0x0003, 0x701b, 0x3b3f, 0x0005, 0x6830,
+ 0xa086, 0x0100, 0x7020, 0x2060, 0x1138, 0x2009, 0x0004, 0x6204,
+ 0xa294, 0x00ff, 0x0804, 0x2df1, 0x2009, 0x0000, 0x080c, 0x4f6e,
+ 0x1108, 0xc185, 0x6000, 0xd0bc, 0x0108, 0xc18d, 0x0804, 0x2dcc,
+ 0x00e6, 0x00d6, 0x2029, 0x0000, 0x2001, 0xad34, 0x2004, 0xd0ac,
+ 0x0138, 0x2021, 0x0000, 0x20a9, 0x00ff, 0x2071, 0xae34, 0x0030,
+ 0x2021, 0x0080, 0x20a9, 0x007f, 0x2071, 0xaeb4, 0x2e04, 0xa005,
+ 0x1130, 0x2100, 0xa406, 0x1548, 0x2428, 0xc5fd, 0x0430, 0x2068,
+ 0x6f10, 0x2700, 0xa306, 0x11b0, 0x6e14, 0x2600, 0xa206, 0x1190,
+ 0x2400, 0xa106, 0x1160, 0x2d60, 0xd884, 0x0540, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x1510, 0x2001, 0x4000, 0x0400, 0x2001,
+ 0x4007, 0x00e8, 0x2400, 0xa106, 0x1140, 0x6e14, 0x87ff, 0x1110,
+ 0x86ff, 0x09d0, 0x2001, 0x4008, 0x0090, 0x8420, 0x8e70, 0x1f04,
+ 0x3b6e, 0x85ff, 0x1130, 0x2001, 0x4009, 0x0048, 0x2001, 0x0001,
+ 0x0030, 0x080c, 0x4c80, 0x1dd0, 0x6312, 0x6216, 0xa006, 0xa005,
+ 0x00de, 0x00ee, 0x0005, 0x81ff, 0x1904, 0x2df1, 0x080c, 0x3c05,
+ 0x0904, 0x2df1, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x7824,
+ 0xa005, 0x0904, 0x2df4, 0xa096, 0x00ff, 0x0120, 0xa092, 0x0004,
+ 0x1a04, 0x2df4, 0x2010, 0x2d18, 0x080c, 0x2a8c, 0x0904, 0x2df1,
+ 0x7007, 0x0003, 0x701b, 0x3bd5, 0x0005, 0x6830, 0xa086, 0x0100,
+ 0x0904, 0x2df1, 0x0804, 0x2dcc, 0x7924, 0xa18c, 0xff00, 0x810f,
+ 0x60d0, 0xd0ac, 0x1120, 0xa182, 0x0080, 0x0a04, 0x2df4, 0xa182,
+ 0x00ff, 0x1a04, 0x2df4, 0x0126, 0x2091, 0x8000, 0x080c, 0x95c6,
+ 0x1188, 0xa190, 0xae34, 0x2204, 0xa065, 0x0160, 0x080c, 0x493a,
+ 0x2001, 0xad34, 0x2004, 0xd0ac, 0x0110, 0x6017, 0x0000, 0x012e,
+ 0x0804, 0x2dcc, 0x012e, 0x0804, 0x2df1, 0x080c, 0x15d9, 0x0188,
+ 0xa006, 0x6802, 0x7010, 0xa005, 0x1120, 0x2d00, 0x7012, 0x7016,
+ 0x0030, 0x7014, 0x6802, 0x2060, 0x2d00, 0x6006, 0x7016, 0xad80,
+ 0x000d, 0x0005, 0x7924, 0x810f, 0xa18c, 0x00ff, 0x080c, 0x4cdc,
+ 0x1130, 0x7e28, 0xa684, 0x3fff, 0xa082, 0x4000, 0x0208, 0xa066,
+ 0x8cff, 0x0005, 0x7e24, 0x860f, 0xa18c, 0x00ff, 0x080c, 0x4cdc,
+ 0x1128, 0xa6b4, 0x00ff, 0xa682, 0x4000, 0x0208, 0xa066, 0x8cff,
+ 0x0005, 0x0016, 0x7110, 0x81ff, 0x0128, 0x2168, 0x6904, 0x080c,
+ 0x15f0, 0x0cc8, 0x7112, 0x7116, 0x001e, 0x0005, 0x2031, 0x0001,
+ 0x0010, 0x2031, 0x0000, 0x2061, 0xadd1, 0x6606, 0x6112, 0x600e,
+ 0x6226, 0x632a, 0x642e, 0x6532, 0x2c10, 0x080c, 0x1624, 0x7007,
+ 0x0002, 0x701b, 0x2dcc, 0x0005, 0x00f6, 0x0126, 0x2091, 0x8000,
+ 0x2079, 0x0000, 0x2001, 0xad8f, 0x2004, 0xa005, 0x1168, 0x0e04,
+ 0x3c74, 0x7818, 0xd084, 0x1140, 0x7a22, 0x7b26, 0x7c2a, 0x781b,
+ 0x0001, 0x2091, 0x4080, 0x0408, 0x0016, 0x00c6, 0x00e6, 0x2071,
+ 0xad81, 0x7138, 0xa182, 0x0010, 0x0218, 0x7030, 0x2060, 0x0078,
+ 0x7030, 0xa0e0, 0x0004, 0xac82, 0xadd1, 0x0210, 0x2061, 0xad91,
+ 0x2c00, 0x7032, 0x81ff, 0x1108, 0x7036, 0x8108, 0x713a, 0x2262,
+ 0x6306, 0x640a, 0x00ee, 0x00ce, 0x001e, 0x012e, 0x00fe, 0x0005,
+ 0x00e6, 0x2071, 0xad81, 0x7038, 0xa005, 0x0570, 0x0126, 0x2091,
+ 0x8000, 0x0e04, 0x3ccb, 0x00f6, 0x2079, 0x0000, 0x7818, 0xd084,
+ 0x1508, 0x00c6, 0x7034, 0x2060, 0x2c04, 0x7822, 0x6004, 0x7826,
+ 0x6008, 0x782a, 0x781b, 0x0001, 0x2091, 0x4080, 0x7038, 0x8001,
+ 0x703a, 0xa005, 0x1130, 0x7033, 0xad91, 0x7037, 0xad91, 0x00ce,
+ 0x0048, 0xac80, 0x0004, 0xa0fa, 0xadd1, 0x0210, 0x2001, 0xad91,
+ 0x7036, 0x00ce, 0x00fe, 0x012e, 0x00ee, 0x0005, 0x0026, 0x2001,
+ 0xad52, 0x2004, 0xd0c4, 0x0120, 0x2011, 0x8014, 0x080c, 0x3c5c,
+ 0x002e, 0x0005, 0x81ff, 0x1904, 0x2df1, 0x0126, 0x2091, 0x8000,
+ 0x6030, 0xc08d, 0xc085, 0xc0ac, 0x6032, 0x080c, 0x574f, 0x1178,
+ 0x2001, 0xaf9e, 0x2003, 0x0001, 0x2001, 0xad00, 0x2003, 0x0001,
+ 0xa085, 0x0001, 0x080c, 0x5793, 0x080c, 0x569a, 0x0010, 0x080c,
+ 0x485e, 0x012e, 0x0804, 0x2dcc, 0x7824, 0x2008, 0xa18c, 0xfffd,
+ 0x1128, 0x61dc, 0xa10d, 0x61de, 0x0804, 0x2dcc, 0x0804, 0x2df4,
+ 0x81ff, 0x1904, 0x2df1, 0x6000, 0xa086, 0x0003, 0x1904, 0x2df1,
+ 0x2001, 0xad52, 0x2004, 0xd0ac, 0x1904, 0x2df1, 0x080c, 0x3c2a,
+ 0x0904, 0x2df4, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1120,
+ 0x7828, 0xa005, 0x0904, 0x2dcc, 0x00c6, 0x080c, 0x3c05, 0x00ce,
+ 0x0904, 0x2df1, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd,
+ 0x683a, 0x080c, 0x979c, 0x0904, 0x2df1, 0x7007, 0x0003, 0x701b,
+ 0x3d3a, 0x0005, 0x6830, 0xa086, 0x0100, 0x0904, 0x2df1, 0x0804,
+ 0x2dcc, 0x2001, 0xad00, 0x2004, 0xa086, 0x0003, 0x1904, 0x2df1,
+ 0x7f24, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3c05, 0x0904,
+ 0x2df1, 0x2009, 0x0000, 0x2031, 0x0000, 0x7023, 0x0000, 0x702f,
+ 0x0000, 0xad80, 0x0005, 0x7026, 0x20a0, 0x080c, 0x4cdc, 0x1904,
+ 0x3db4, 0x6004, 0xa0c4, 0x00ff, 0xa8c6, 0x0006, 0x0130, 0xa0c4,
+ 0xff00, 0xa8c6, 0x0600, 0x1904, 0x3db4, 0x2001, 0xad52, 0x2004,
+ 0xd0ac, 0x1128, 0x080c, 0x4f6e, 0x1110, 0xd79c, 0x05e8, 0xd794,
+ 0x1110, 0xd784, 0x0158, 0xac80, 0x0006, 0x2098, 0x3400, 0x20a9,
+ 0x0004, 0x53a3, 0x080c, 0x3a86, 0xd794, 0x0148, 0xac80, 0x000a,
+ 0x2098, 0x3400, 0x20a9, 0x0004, 0x53a3, 0x080c, 0x3a86, 0x21a2,
+ 0xd794, 0x01d8, 0xac80, 0x0000, 0x2098, 0x94a0, 0x20a9, 0x0002,
+ 0x53a3, 0xac80, 0x0003, 0x20a6, 0x94a0, 0xac80, 0x0004, 0x2098,
+ 0x3400, 0x20a9, 0x0002, 0x53a3, 0x080c, 0x3a78, 0xac80, 0x0026,
+ 0x2098, 0x20a9, 0x0002, 0x53a3, 0x0008, 0x94a0, 0xd794, 0x0110,
+ 0xa6b0, 0x000b, 0xa6b0, 0x0005, 0x8108, 0x2001, 0xad34, 0x2004,
+ 0xd0ac, 0x0118, 0xa186, 0x0100, 0x0040, 0xd78c, 0x0120, 0xa186,
+ 0x0100, 0x0170, 0x0018, 0xa186, 0x007e, 0x0150, 0xd794, 0x0118,
+ 0xa686, 0x0020, 0x0010, 0xa686, 0x0028, 0x0150, 0x0804, 0x3d5d,
+ 0x86ff, 0x1120, 0x7120, 0x810b, 0x0804, 0x2dcc, 0x702f, 0x0001,
+ 0x711e, 0x7020, 0xa600, 0x7022, 0x772a, 0x2061, 0xadd1, 0x6007,
+ 0x0000, 0x6612, 0x7024, 0x600e, 0x6226, 0x632a, 0x642e, 0x6532,
+ 0x2c10, 0x080c, 0x1624, 0x7007, 0x0002, 0x701b, 0x3df0, 0x0005,
+ 0x702c, 0xa005, 0x1170, 0x711c, 0x7024, 0x20a0, 0x7728, 0x2031,
+ 0x0000, 0x2061, 0xadd1, 0x6224, 0x6328, 0x642c, 0x6530, 0x0804,
+ 0x3d5d, 0x7120, 0x810b, 0x0804, 0x2dcc, 0x2029, 0x007e, 0x7924,
+ 0x7a28, 0x7b2c, 0x7c38, 0xa184, 0xff00, 0x8007, 0xa0e2, 0x0020,
+ 0x0a04, 0x2df4, 0xa502, 0x0a04, 0x2df4, 0xa184, 0x00ff, 0xa0e2,
+ 0x0020, 0x0a04, 0x2df4, 0xa502, 0x0a04, 0x2df4, 0xa284, 0xff00,
+ 0x8007, 0xa0e2, 0x0020, 0x0a04, 0x2df4, 0xa502, 0x0a04, 0x2df4,
+ 0xa284, 0x00ff, 0xa0e2, 0x0020, 0x0a04, 0x2df4, 0xa502, 0x0a04,
+ 0x2df4, 0xa384, 0xff00, 0x8007, 0xa0e2, 0x0020, 0x0a04, 0x2df4,
+ 0xa502, 0x0a04, 0x2df4, 0xa384, 0x00ff, 0xa0e2, 0x0020, 0x0a04,
+ 0x2df4, 0xa502, 0x0a04, 0x2df4, 0xa484, 0xff00, 0x8007, 0xa0e2,
+ 0x0020, 0x0a04, 0x2df4, 0xa502, 0x0a04, 0x2df4, 0xa484, 0x00ff,
+ 0xa0e2, 0x0020, 0x0a04, 0x2df4, 0xa502, 0x0a04, 0x2df4, 0x2061,
+ 0xafa6, 0x6102, 0x6206, 0x630a, 0x640e, 0x0804, 0x2dcc, 0x0006,
+ 0x2001, 0xad52, 0x2004, 0xd0cc, 0x000e, 0x0005, 0x0006, 0x2001,
+ 0xad71, 0x2004, 0xd0bc, 0x000e, 0x0005, 0x6164, 0x7a24, 0x6300,
+ 0x82ff, 0x1118, 0x7926, 0x0804, 0x2dcc, 0x83ff, 0x1904, 0x2df4,
+ 0x2001, 0xfff0, 0xa200, 0x1a04, 0x2df4, 0x2019, 0xffff, 0x6068,
+ 0xa302, 0xa200, 0x0a04, 0x2df4, 0x7926, 0x6266, 0x0804, 0x2dcc,
+ 0x2001, 0xad00, 0x2004, 0xa086, 0x0003, 0x1904, 0x2df1, 0x7c28,
+ 0x7d24, 0x7e38, 0x7f2c, 0x080c, 0x3c05, 0x0904, 0x2df1, 0x2009,
+ 0x0000, 0x2019, 0x0000, 0x7023, 0x0000, 0x702f, 0x0000, 0xad80,
+ 0x0003, 0x7026, 0x20a0, 0xa1e0, 0xae34, 0x2c64, 0x8cff, 0x01b8,
+ 0x6004, 0xa084, 0x00ff, 0xa086, 0x0006, 0x0130, 0x6004, 0xa084,
+ 0xff00, 0xa086, 0x0600, 0x1158, 0x6014, 0x20a2, 0x94a0, 0x6010,
+ 0x8007, 0xa105, 0x8007, 0x20a2, 0x94a0, 0xa398, 0x0002, 0x8108,
+ 0xa182, 0x00ff, 0x0120, 0xa386, 0x002a, 0x0148, 0x08e0, 0x83ff,
+ 0x1120, 0x7120, 0x810c, 0x0804, 0x2dcc, 0x702f, 0x0001, 0x711e,
+ 0x7020, 0xa300, 0x7022, 0x2061, 0xadd1, 0x6007, 0x0000, 0x6312,
+ 0x7024, 0x600e, 0x6426, 0x652a, 0x662e, 0x6732, 0x2c10, 0x080c,
+ 0x1624, 0x7007, 0x0002, 0x701b, 0x3ee6, 0x0005, 0x702c, 0xa005,
+ 0x1168, 0x711c, 0x7024, 0x20a0, 0x2019, 0x0000, 0x2061, 0xadd1,
+ 0x6424, 0x6528, 0x662c, 0x6730, 0x0804, 0x3ea3, 0x7120, 0x810c,
+ 0x0804, 0x2dcc, 0x81ff, 0x1904, 0x2df1, 0x60d0, 0xd0ac, 0x1118,
+ 0xd09c, 0x0904, 0x2df1, 0x080c, 0x3c05, 0x0904, 0x2df1, 0x7924,
+ 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x080c, 0x3c46, 0x701b, 0x3f11,
+ 0x0005, 0x00d6, 0xade8, 0x000d, 0x6828, 0xa0be, 0x7000, 0x0148,
+ 0xa0be, 0x7100, 0x0130, 0xa0be, 0x7200, 0x0118, 0x00de, 0x0804,
+ 0x2df4, 0x6820, 0x6924, 0x080c, 0x2676, 0x1510, 0x080c, 0x4c80,
+ 0x11f8, 0x7122, 0x6612, 0x6516, 0x6e18, 0x00c6, 0x080c, 0x3c05,
+ 0x01b8, 0x080c, 0x3c05, 0x01a0, 0x00ce, 0x00de, 0x6837, 0x0000,
+ 0x6838, 0xc0fd, 0x683a, 0x6823, 0x0000, 0x6804, 0x2068, 0x080c,
+ 0x96ef, 0x0904, 0x2df1, 0x7007, 0x0003, 0x701b, 0x3f4b, 0x0005,
+ 0x00de, 0x0804, 0x2df1, 0x7120, 0x080c, 0x2bc9, 0x6820, 0xa086,
+ 0x8001, 0x0904, 0x2df1, 0x2d00, 0x701e, 0x6804, 0xa080, 0x0002,
+ 0x0006, 0x20a9, 0x002a, 0x2098, 0x20a0, 0x080c, 0x48be, 0x000e,
+ 0xade8, 0x000d, 0x6a08, 0x6b0c, 0x6c10, 0x6d14, 0x2061, 0xadd1,
+ 0x6007, 0x0000, 0x6e00, 0x6f28, 0xa7c6, 0x7000, 0x1108, 0x0018,
+ 0xa7c6, 0x7100, 0x1140, 0xa6c2, 0x0004, 0x0a04, 0x2df4, 0x2009,
+ 0x0004, 0x0804, 0x3c49, 0xa7c6, 0x7200, 0x1904, 0x2df4, 0xa6c2,
+ 0x0054, 0x0a04, 0x2df4, 0x600e, 0x6013, 0x002a, 0x6226, 0x632a,
+ 0x642e, 0x6532, 0x2c10, 0x080c, 0x1624, 0x7007, 0x0002, 0x701b,
+ 0x3f92, 0x0005, 0x701c, 0x2068, 0x6804, 0xa080, 0x0001, 0x2004,
+ 0xa080, 0x0002, 0x0006, 0x20a9, 0x002a, 0x2098, 0x20a0, 0x080c,
+ 0x48be, 0x000e, 0x2009, 0x002a, 0x2061, 0xadd1, 0x6224, 0x6328,
+ 0x642c, 0x6530, 0x0804, 0x3c49, 0x81ff, 0x1904, 0x2df1, 0x080c,
+ 0x3c1a, 0x0904, 0x2df4, 0x080c, 0x4d96, 0x0904, 0x2df1, 0x080c,
+ 0x4ec6, 0x0804, 0x2dcc, 0x7824, 0xd084, 0x0904, 0x3804, 0x080c,
+ 0x3c2a, 0x0904, 0x2df4, 0x00c6, 0x080c, 0x3c05, 0x00ce, 0x1120,
+ 0x2009, 0x0002, 0x0804, 0x2df1, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x0128, 0xa08e, 0x0004, 0x0110, 0xa08e, 0x0005, 0x1508,
+ 0x2001, 0xad52, 0x2004, 0xd0b4, 0x0904, 0x3834, 0x6000, 0xd08c,
+ 0x1904, 0x3834, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x080c,
+ 0x970b, 0x1120, 0x2009, 0x0003, 0x0804, 0x2df1, 0x7007, 0x0003,
+ 0x701b, 0x3ff3, 0x0005, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x0804,
+ 0x3834, 0x2009, 0xad30, 0x210c, 0x81ff, 0x0120, 0x2009, 0x0001,
+ 0x0804, 0x2df1, 0x2001, 0xad00, 0x2004, 0xa086, 0x0003, 0x0120,
+ 0x2009, 0x0007, 0x0804, 0x2df1, 0x2001, 0xad52, 0x2004, 0xd0ac,
+ 0x0120, 0x2009, 0x0008, 0x0804, 0x2df1, 0x609c, 0xd0a4, 0x1118,
+ 0xd0ac, 0x1904, 0x3834, 0x6837, 0x0000, 0x6833, 0x0000, 0x6838,
+ 0xc0fd, 0x683a, 0x080c, 0x979c, 0x1120, 0x2009, 0x0003, 0x0804,
+ 0x2df1, 0x7007, 0x0003, 0x701b, 0x402e, 0x0005, 0x6830, 0xa086,
+ 0x0100, 0x1120, 0x2009, 0x0004, 0x0804, 0x2df1, 0x080c, 0x3c2a,
+ 0x0904, 0x2df4, 0x0804, 0x3fd8, 0x81ff, 0x2009, 0x0001, 0x1904,
+ 0x2df1, 0x6000, 0xa086, 0x0003, 0x2009, 0x0007, 0x1904, 0x2df1,
+ 0x2001, 0xad52, 0x2004, 0xd0ac, 0x2009, 0x0008, 0x1904, 0x2df1,
+ 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x6004, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x2009, 0x0009, 0x1904, 0x2df1, 0x00c6, 0x080c, 0x3c05,
+ 0x00ce, 0x2009, 0x0002, 0x0904, 0x2df1, 0x6837, 0x0000, 0x6833,
+ 0x0000, 0x6838, 0xc0fd, 0x683a, 0x7928, 0xa194, 0xff00, 0xa18c,
+ 0x00ff, 0xa006, 0x82ff, 0x1128, 0xc0ed, 0x6952, 0x792c, 0x6956,
+ 0x0048, 0xa28e, 0x0100, 0x1904, 0x2df4, 0xc0e5, 0x6853, 0x0000,
+ 0x6857, 0x0000, 0x683e, 0x080c, 0x9957, 0x2009, 0x0003, 0x0904,
+ 0x2df1, 0x7007, 0x0003, 0x701b, 0x408e, 0x0005, 0x6830, 0xa086,
+ 0x0100, 0x2009, 0x0004, 0x0904, 0x2df1, 0x0804, 0x2dcc, 0x81ff,
+ 0x2009, 0x0001, 0x1904, 0x2df1, 0x6000, 0xa086, 0x0003, 0x2009,
+ 0x0007, 0x1904, 0x2df1, 0x080c, 0x3c2a, 0x0904, 0x2df4, 0x6004,
+ 0xa084, 0x00ff, 0xa086, 0x0006, 0x2009, 0x0009, 0x1904, 0x2df1,
+ 0x00c6, 0x080c, 0x3c05, 0x00ce, 0x2009, 0x0002, 0x0904, 0x2df1,
+ 0xad80, 0x000f, 0x2009, 0x0008, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x080c, 0x3c46, 0x701b, 0x40c5, 0x0005, 0x00d6, 0xade8, 0x000f,
+ 0x6800, 0xa086, 0x0500, 0x1140, 0x6804, 0xa005, 0x1128, 0x6808,
+ 0xa084, 0xff00, 0x1108, 0x0018, 0x00de, 0x1904, 0x2df4, 0x00de,
+ 0x6837, 0x0000, 0x6833, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x00c6,
+ 0x080c, 0x3c2a, 0x1118, 0x00ce, 0x0804, 0x2df4, 0x080c, 0x99a6,
+ 0x2009, 0x0003, 0x00ce, 0x0904, 0x2df1, 0x7007, 0x0003, 0x701b,
+ 0x40f2, 0x0005, 0x6830, 0xa086, 0x0100, 0x2009, 0x0004, 0x0904,
+ 0x2df1, 0x0804, 0x2dcc, 0x81ff, 0x0120, 0x2009, 0x0001, 0x0804,
+ 0x2df1, 0x6000, 0xa086, 0x0003, 0x0120, 0x2009, 0x0007, 0x0804,
+ 0x2df1, 0x7e24, 0x860f, 0xa18c, 0x00ff, 0xa6b4, 0x00ff, 0x080c,
+ 0x4cdc, 0x1904, 0x2df4, 0xa186, 0x007f, 0x0150, 0x6004, 0xa084,
+ 0x00ff, 0xa086, 0x0006, 0x0120, 0x2009, 0x0009, 0x0804, 0x2df1,
+ 0x00c6, 0x080c, 0x3c05, 0x00ce, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x2df1, 0x6837, 0x0000, 0x6838, 0xc0fd, 0x683a, 0x080c, 0x9726,
+ 0x1120, 0x2009, 0x0003, 0x0804, 0x2df1, 0x7007, 0x0003, 0x701b,
+ 0x413a, 0x0005, 0x6808, 0x8007, 0xa086, 0x0100, 0x1120, 0x2009,
+ 0x0004, 0x0804, 0x2df1, 0x68b0, 0x6836, 0x6810, 0x8007, 0xa084,
+ 0x00ff, 0x808e, 0x6814, 0x8007, 0xa084, 0x00ff, 0x8086, 0xa080,
+ 0x0002, 0xa108, 0xad80, 0x0004, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38,
+ 0x0804, 0x3c49, 0x080c, 0x3c05, 0x1120, 0x2009, 0x0002, 0x0804,
+ 0x2df1, 0x7924, 0xa194, 0xff00, 0xa18c, 0x00ff, 0x8217, 0x82ff,
+ 0x0110, 0x0804, 0x2df4, 0x2009, 0x001a, 0x7a2c, 0x7b28, 0x7c3c,
+ 0x7d38, 0x080c, 0x3c46, 0x701b, 0x4176, 0x0005, 0xad80, 0x000d,
+ 0x2098, 0x20a9, 0x001a, 0x20a1, 0xafad, 0x53a3, 0x0804, 0x2dcc,
+ 0x080c, 0x3c05, 0x1120, 0x2009, 0x0002, 0x0804, 0x2df1, 0x7924,
+ 0xa194, 0xff00, 0xa18c, 0x00ff, 0x8217, 0x82ff, 0x0110, 0x0804,
+ 0x2df4, 0x2099, 0xafad, 0x20a0, 0x20a9, 0x001a, 0x53a3, 0x2009,
+ 0x001a, 0x7a2c, 0x7b28, 0x7c3c, 0x7d38, 0x0804, 0x3c49, 0x7824,
+ 0xa08a, 0x1000, 0x1a04, 0x2df4, 0x0126, 0x2091, 0x8000, 0x8003,
+ 0x800b, 0x810b, 0xa108, 0x00c6, 0x2061, 0xafda, 0x6142, 0x00ce,
+ 0x012e, 0x0804, 0x2dcc, 0x00c6, 0x080c, 0x574f, 0x1188, 0x2001,
+ 0xaf9e, 0x2003, 0x0001, 0x2001, 0xad00, 0x2003, 0x0001, 0xa085,
+ 0x0001, 0x080c, 0x5793, 0x080c, 0x569a, 0x080c, 0x14f6, 0x0038,
+ 0x2061, 0xad00, 0x6030, 0xc09d, 0x6032, 0x080c, 0x485e, 0x00ce,
+ 0x0005, 0x0126, 0x00c6, 0x00e6, 0x2061, 0x0100, 0x2071, 0xad00,
+ 0x6044, 0xd0a4, 0x11b0, 0xd084, 0x0118, 0x080c, 0x4348, 0x0068,
+ 0xd08c, 0x0118, 0x080c, 0x4269, 0x0040, 0xd094, 0x0118, 0x080c,
+ 0x423a, 0x0018, 0xd09c, 0x0108, 0x0061, 0x00ee, 0x00ce, 0x012e,
+ 0x0005, 0x0016, 0x6128, 0xd19c, 0x1110, 0xc19d, 0x612a, 0x001e,
+ 0x0ca0, 0x624c, 0xa286, 0xf0f0, 0x1150, 0x6048, 0xa086, 0xf0f0,
+ 0x0130, 0x624a, 0x6043, 0x0090, 0x6043, 0x0010, 0x0490, 0xa294,
+ 0xff00, 0xa296, 0xf700, 0x0178, 0x7134, 0xd1a4, 0x1160, 0x6240,
+ 0xa295, 0x0100, 0x6242, 0xa294, 0x0010, 0x0128, 0x2009, 0x00f7,
+ 0x080c, 0x48de, 0x00f0, 0x6040, 0xa084, 0x0010, 0xa085, 0x0040,
+ 0x6042, 0x6043, 0x0000, 0x7077, 0x0000, 0x7093, 0x0001, 0x70b7,
+ 0x0000, 0x70d3, 0x0000, 0x2009, 0xb3c0, 0x200b, 0x0000, 0x7087,
+ 0x0000, 0x707b, 0x000a, 0x2009, 0x000a, 0x2011, 0x4814, 0x080c,
+ 0x6593, 0x0005, 0x0156, 0x2001, 0xad73, 0x2004, 0xd08c, 0x0110,
+ 0x704f, 0xffff, 0x7078, 0xa005, 0x1510, 0x2011, 0x4814, 0x080c,
+ 0x650d, 0x6040, 0xa094, 0x0010, 0xa285, 0x0020, 0x6042, 0x20a9,
+ 0x00c8, 0x6044, 0xd08c, 0x1168, 0x1f04, 0x4251, 0x6242, 0x708b,
+ 0x0000, 0x6040, 0xa094, 0x0010, 0xa285, 0x0080, 0x6042, 0x6242,
+ 0x0030, 0x6242, 0x708b, 0x0000, 0x707f, 0x0000, 0x0000, 0x015e,
+ 0x0005, 0x707c, 0xa08a, 0x0003, 0x1210, 0x0023, 0x0010, 0x080c,
+ 0x14f6, 0x0005, 0x4275, 0x42c5, 0x4347, 0x00f6, 0x707f, 0x0001,
+ 0x20e1, 0xa000, 0xe000, 0x20e1, 0x8700, 0x080c, 0x22f8, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x2079, 0xb200, 0x207b, 0x2200, 0x7807,
+ 0x00ef, 0x780b, 0x0000, 0x780f, 0x00ef, 0x7813, 0x0138, 0x7817,
+ 0x0000, 0x781b, 0x0000, 0x781f, 0x0000, 0x7823, 0xffff, 0x7827,
+ 0xffff, 0x782b, 0x0000, 0x782f, 0x0000, 0x2079, 0xb20c, 0x207b,
+ 0x1101, 0x7807, 0x0000, 0x2099, 0xad05, 0x20a1, 0xb20e, 0x20a9,
+ 0x0004, 0x53a3, 0x2079, 0xb212, 0x207b, 0x0000, 0x7807, 0x0000,
+ 0x2099, 0xb200, 0x20a1, 0x020b, 0x20a9, 0x0014, 0x53a6, 0x60c3,
+ 0x000c, 0x600f, 0x0000, 0x080c, 0x4845, 0x00fe, 0x7083, 0x0000,
+ 0x6043, 0x0008, 0x6043, 0x0000, 0x0005, 0x00d6, 0x7080, 0x7083,
+ 0x0000, 0xa025, 0x0904, 0x432f, 0x6020, 0xd0b4, 0x1904, 0x432d,
+ 0x7190, 0x81ff, 0x0904, 0x431d, 0xa486, 0x000c, 0x1904, 0x4328,
+ 0xa480, 0x0018, 0x8004, 0x20a8, 0x2011, 0xb280, 0x2019, 0xb200,
+ 0x220c, 0x2304, 0xa106, 0x11b8, 0x8210, 0x8318, 0x1f04, 0x42e0,
+ 0x6043, 0x0004, 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043, 0x0006,
+ 0x707f, 0x0002, 0x708b, 0x0002, 0x2009, 0x07d0, 0x2011, 0x481b,
+ 0x080c, 0x6593, 0x0490, 0x2069, 0xb280, 0x6930, 0xa18e, 0x1101,
+ 0x1538, 0x6834, 0xa005, 0x1520, 0x6900, 0xa18c, 0x00ff, 0x1118,
+ 0x6804, 0xa005, 0x0190, 0x2011, 0xb28e, 0x2019, 0xad05, 0x20a9,
+ 0x0004, 0x220c, 0x2304, 0xa102, 0x0230, 0x1190, 0x8210, 0x8318,
+ 0x1f04, 0x4311, 0x0068, 0x7093, 0x0000, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x2099, 0xb280, 0x20a1, 0x020b, 0x20a9, 0x0014, 0x53a6,
+ 0x6043, 0x0008, 0x6043, 0x0000, 0x0010, 0x00de, 0x0005, 0x6040,
+ 0xa085, 0x0100, 0x6042, 0x6020, 0xd0b4, 0x1db8, 0x60c3, 0x000c,
+ 0x2011, 0xafd1, 0x2013, 0x0000, 0x7083, 0x0000, 0x20e1, 0x9080,
+ 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0x782b, 0x0c30, 0x0005,
+ 0x7088, 0xa08a, 0x001d, 0x1210, 0x0023, 0x0010, 0x080c, 0x14f6,
+ 0x0005, 0x437b, 0x438a, 0x43b2, 0x43cb, 0x43ef, 0x4417, 0x443b,
+ 0x446c, 0x4490, 0x44b8, 0x44ef, 0x4517, 0x4533, 0x4549, 0x4569,
+ 0x457c, 0x4584, 0x45b1, 0x45d5, 0x45fd, 0x4621, 0x4652, 0x468f,
+ 0x46be, 0x46da, 0x4719, 0x4739, 0x4752, 0x4753, 0x00c6, 0x2061,
+ 0xad00, 0x6003, 0x0007, 0x2061, 0x0100, 0x6004, 0xa084, 0xfff9,
+ 0x6006, 0x00ce, 0x0005, 0x608b, 0xbc94, 0x608f, 0xf0f0, 0x6043,
+ 0x0002, 0x708b, 0x0001, 0x2009, 0x07d0, 0x2011, 0x481b, 0x080c,
+ 0x6593, 0x0005, 0x00f6, 0x7080, 0xa086, 0x0014, 0x1508, 0x6043,
+ 0x0000, 0x6020, 0xd0b4, 0x11e0, 0x2079, 0xb280, 0x7a30, 0xa296,
+ 0x1102, 0x11a0, 0x7834, 0xa005, 0x1188, 0x7a38, 0xd2fc, 0x0128,
+ 0x70b4, 0xa005, 0x1110, 0x70b7, 0x0001, 0x2011, 0x481b, 0x080c,
+ 0x650d, 0x708b, 0x0010, 0x080c, 0x4584, 0x0010, 0x080c, 0x485e,
+ 0x00fe, 0x0005, 0x708b, 0x0003, 0x6043, 0x0004, 0x2011, 0x481b,
+ 0x080c, 0x650d, 0x080c, 0x48c6, 0x20a3, 0x1102, 0x20a3, 0x0000,
+ 0x20a9, 0x000a, 0x20a3, 0x0000, 0x1f04, 0x43c2, 0x60c3, 0x0014,
+ 0x080c, 0x4845, 0x0005, 0x00f6, 0x7080, 0xa005, 0x01f0, 0x2011,
+ 0x481b, 0x080c, 0x650d, 0xa086, 0x0014, 0x11a8, 0x2079, 0xb280,
+ 0x7a30, 0xa296, 0x1102, 0x1178, 0x7834, 0xa005, 0x1160, 0x7a38,
+ 0xd2fc, 0x0128, 0x70b4, 0xa005, 0x1110, 0x70b7, 0x0001, 0x708b,
+ 0x0004, 0x0029, 0x0010, 0x080c, 0x485e, 0x00fe, 0x0005, 0x708b,
+ 0x0005, 0x080c, 0x48c6, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430,
+ 0x2011, 0xb28e, 0x080c, 0x4917, 0x1160, 0x7074, 0xa005, 0x1148,
+ 0x714c, 0xa186, 0xffff, 0x0128, 0x080c, 0x47df, 0x0110, 0x080c,
+ 0x48f5, 0x20a9, 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0014, 0x080c, 0x4845, 0x0005, 0x00f6,
+ 0x7080, 0xa005, 0x01f0, 0x2011, 0x481b, 0x080c, 0x650d, 0xa086,
+ 0x0014, 0x11a8, 0x2079, 0xb280, 0x7a30, 0xa296, 0x1103, 0x1178,
+ 0x7834, 0xa005, 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70b4, 0xa005,
+ 0x1110, 0x70b7, 0x0001, 0x708b, 0x0006, 0x0029, 0x0010, 0x080c,
+ 0x485e, 0x00fe, 0x0005, 0x708b, 0x0007, 0x080c, 0x48c6, 0x20a3,
+ 0x1104, 0x20a3, 0x0000, 0x3430, 0x2011, 0xb28e, 0x080c, 0x4917,
+ 0x11a8, 0x7074, 0xa005, 0x1190, 0x7154, 0xa186, 0xffff, 0x0170,
+ 0xa180, 0x2be6, 0x200d, 0xa18c, 0xff00, 0x810f, 0x080c, 0x47df,
+ 0x0128, 0x080c, 0x3e66, 0x0110, 0x080c, 0x26c0, 0x20a9, 0x0008,
+ 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0014, 0x080c, 0x4845, 0x0005, 0x00f6, 0x7080, 0xa005, 0x01f0,
+ 0x2011, 0x481b, 0x080c, 0x650d, 0xa086, 0x0014, 0x11a8, 0x2079,
+ 0xb280, 0x7a30, 0xa296, 0x1104, 0x1178, 0x7834, 0xa005, 0x1160,
+ 0x7a38, 0xd2fc, 0x0128, 0x70b4, 0xa005, 0x1110, 0x70b7, 0x0001,
+ 0x708b, 0x0008, 0x0029, 0x0010, 0x080c, 0x485e, 0x00fe, 0x0005,
+ 0x708b, 0x0009, 0x080c, 0x48c6, 0x20a3, 0x1105, 0x20a3, 0x0100,
+ 0x3430, 0x080c, 0x4917, 0x1150, 0x7074, 0xa005, 0x1138, 0x080c,
+ 0x4754, 0x1170, 0xa085, 0x0001, 0x080c, 0x26c0, 0x20a9, 0x0008,
+ 0x2099, 0xb28e, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0014, 0x080c, 0x4845, 0x0010, 0x080c, 0x436e, 0x0005,
+ 0x00f6, 0x7080, 0xa005, 0x0588, 0x2011, 0x481b, 0x080c, 0x650d,
+ 0xa086, 0x0014, 0x1540, 0x2079, 0xb280, 0x7a30, 0xa296, 0x1105,
+ 0x1510, 0x7834, 0x2011, 0x0100, 0xa21e, 0x1160, 0x7a38, 0xd2fc,
+ 0x0128, 0x70b4, 0xa005, 0x1110, 0x70b7, 0x0001, 0x708b, 0x000a,
+ 0x00b1, 0x0098, 0xa005, 0x1178, 0x7a38, 0xd2fc, 0x0128, 0x70b4,
+ 0xa005, 0x1110, 0x70b7, 0x0001, 0x7087, 0x0000, 0x708b, 0x000e,
+ 0x080c, 0x4569, 0x0010, 0x080c, 0x485e, 0x00fe, 0x0005, 0x708b,
+ 0x000b, 0x2011, 0xb20e, 0x22a0, 0x20a9, 0x0040, 0x2019, 0xffff,
+ 0x43a4, 0x20a9, 0x0002, 0x2009, 0x0000, 0x41a4, 0x080c, 0x48c6,
+ 0x20a3, 0x1106, 0x20a3, 0x0000, 0x080c, 0x4917, 0x0118, 0x2013,
+ 0x0000, 0x0020, 0x7050, 0xa085, 0x0100, 0x2012, 0x2298, 0x20a9,
+ 0x0042, 0x53a6, 0x60c3, 0x0084, 0x080c, 0x4845, 0x0005, 0x00f6,
+ 0x7080, 0xa005, 0x01b0, 0x2011, 0x481b, 0x080c, 0x650d, 0xa086,
+ 0x0084, 0x1168, 0x2079, 0xb280, 0x7a30, 0xa296, 0x1106, 0x1138,
+ 0x7834, 0xa005, 0x1120, 0x708b, 0x000c, 0x0029, 0x0010, 0x080c,
+ 0x485e, 0x00fe, 0x0005, 0x708b, 0x000d, 0x080c, 0x48c6, 0x20a3,
+ 0x1107, 0x20a3, 0x0000, 0x2099, 0xb28e, 0x20a9, 0x0040, 0x53a6,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0084, 0x080c, 0x4845,
+ 0x0005, 0x00f6, 0x7080, 0xa005, 0x01d0, 0x2011, 0x481b, 0x080c,
+ 0x650d, 0xa086, 0x0084, 0x1188, 0x2079, 0xb280, 0x7a30, 0xa296,
+ 0x1107, 0x1158, 0x7834, 0xa005, 0x1140, 0x7087, 0x0001, 0x080c,
+ 0x48b8, 0x708b, 0x000e, 0x0029, 0x0010, 0x080c, 0x485e, 0x00fe,
+ 0x0005, 0x708b, 0x000f, 0x7083, 0x0000, 0x608b, 0xbc85, 0x608f,
+ 0xb5b5, 0x6043, 0x0005, 0x6043, 0x0004, 0x2009, 0x07d0, 0x2011,
+ 0x481b, 0x080c, 0x6501, 0x0005, 0x7080, 0xa005, 0x0120, 0x2011,
+ 0x481b, 0x080c, 0x650d, 0x0005, 0x708b, 0x0011, 0x080c, 0x4917,
+ 0x1188, 0x716c, 0x81ff, 0x0170, 0x2009, 0x0000, 0x7070, 0xa084,
+ 0x00ff, 0x080c, 0x2676, 0xa186, 0x0080, 0x0120, 0x2011, 0xb28e,
+ 0x080c, 0x47df, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xb280,
+ 0x20a1, 0x020b, 0x7480, 0xa480, 0x0018, 0xa080, 0x0007, 0xa084,
+ 0x03f8, 0x8004, 0x20a8, 0x53a6, 0x60c3, 0x0014, 0x080c, 0x4845,
+ 0x0005, 0x00f6, 0x7080, 0xa005, 0x01f0, 0x2011, 0x481b, 0x080c,
+ 0x650d, 0xa086, 0x0014, 0x11a8, 0x2079, 0xb280, 0x7a30, 0xa296,
+ 0x1103, 0x1178, 0x7834, 0xa005, 0x1160, 0x7a38, 0xd2fc, 0x0128,
+ 0x70b4, 0xa005, 0x1110, 0x70b7, 0x0001, 0x708b, 0x0012, 0x0029,
+ 0x0010, 0x080c, 0x485e, 0x00fe, 0x0005, 0x708b, 0x0013, 0x080c,
+ 0x48d2, 0x20a3, 0x1103, 0x20a3, 0x0000, 0x3430, 0x2011, 0xb28e,
+ 0x080c, 0x4917, 0x1160, 0x7074, 0xa005, 0x1148, 0x714c, 0xa186,
+ 0xffff, 0x0128, 0x080c, 0x47df, 0x0110, 0x080c, 0x48f5, 0x20a9,
+ 0x0008, 0x2298, 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x60c3, 0x0014, 0x080c, 0x4845, 0x0005, 0x00f6, 0x7080, 0xa005,
+ 0x01f0, 0x2011, 0x481b, 0x080c, 0x650d, 0xa086, 0x0014, 0x11a8,
+ 0x2079, 0xb280, 0x7a30, 0xa296, 0x1104, 0x1178, 0x7834, 0xa005,
+ 0x1160, 0x7a38, 0xd2fc, 0x0128, 0x70b4, 0xa005, 0x1110, 0x70b7,
+ 0x0001, 0x708b, 0x0014, 0x0029, 0x0010, 0x080c, 0x485e, 0x00fe,
+ 0x0005, 0x708b, 0x0015, 0x080c, 0x48d2, 0x20a3, 0x1104, 0x20a3,
+ 0x0000, 0x3430, 0x2011, 0xb28e, 0x080c, 0x4917, 0x11a8, 0x7074,
+ 0xa005, 0x1190, 0x7154, 0xa186, 0xffff, 0x0170, 0xa180, 0x2be6,
+ 0x200d, 0xa18c, 0xff00, 0x810f, 0x080c, 0x47df, 0x0128, 0x080c,
+ 0x3e66, 0x0110, 0x080c, 0x26c0, 0x20a9, 0x0008, 0x2298, 0x26a0,
+ 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x080c,
+ 0x4845, 0x0005, 0x00f6, 0x7080, 0xa005, 0x05b8, 0x2011, 0x481b,
+ 0x080c, 0x650d, 0xa086, 0x0014, 0x1570, 0x2079, 0xb280, 0x7a30,
+ 0xa296, 0x1105, 0x1540, 0x7834, 0x2011, 0x0100, 0xa21e, 0x1148,
+ 0x7a38, 0xd2fc, 0x0128, 0x70b4, 0xa005, 0x1110, 0x70b7, 0x0001,
+ 0x0060, 0xa005, 0x11c0, 0x7a38, 0xd2fc, 0x0128, 0x70b4, 0xa005,
+ 0x1110, 0x70b7, 0x0001, 0x7087, 0x0000, 0x7a38, 0xd2f4, 0x0138,
+ 0x2001, 0xad73, 0x2004, 0xd0a4, 0x1110, 0x70d3, 0x0008, 0x708b,
+ 0x0016, 0x0029, 0x0010, 0x080c, 0x485e, 0x00fe, 0x0005, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x2099, 0xb280, 0x20a1, 0x020b, 0x20a9,
+ 0x000e, 0x53a6, 0x3430, 0x2011, 0xb28e, 0x708b, 0x0017, 0x080c,
+ 0x4917, 0x1150, 0x7074, 0xa005, 0x1138, 0x080c, 0x4754, 0x1170,
+ 0xa085, 0x0001, 0x080c, 0x26c0, 0x20a9, 0x0008, 0x2099, 0xb28e,
+ 0x26a0, 0x53a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014,
+ 0x080c, 0x4845, 0x0010, 0x080c, 0x436e, 0x0005, 0x00f6, 0x7080,
+ 0xa005, 0x01b0, 0x2011, 0x481b, 0x080c, 0x650d, 0xa086, 0x0084,
+ 0x1168, 0x2079, 0xb280, 0x7a30, 0xa296, 0x1106, 0x1138, 0x7834,
+ 0xa005, 0x1120, 0x708b, 0x0018, 0x0029, 0x0010, 0x080c, 0x485e,
+ 0x00fe, 0x0005, 0x708b, 0x0019, 0x080c, 0x48d2, 0x20a3, 0x1106,
+ 0x20a3, 0x0000, 0x3430, 0x2099, 0xb28e, 0x2039, 0xb20e, 0x27a0,
+ 0x20a9, 0x0040, 0x53a3, 0x080c, 0x4917, 0x11e8, 0x2728, 0x2514,
+ 0x8207, 0xa084, 0x00ff, 0x8000, 0x2018, 0xa294, 0x00ff, 0x8007,
+ 0xa205, 0x202a, 0x7050, 0x2310, 0x8214, 0xa2a0, 0xb20e, 0x2414,
+ 0xa38c, 0x0001, 0x0118, 0xa294, 0xff00, 0x0018, 0xa294, 0x00ff,
+ 0x8007, 0xa215, 0x2222, 0x2798, 0x26a0, 0x20a9, 0x0040, 0x53a6,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0084, 0x080c, 0x4845,
+ 0x0005, 0x00f6, 0x7080, 0xa005, 0x01d0, 0x2011, 0x481b, 0x080c,
+ 0x650d, 0xa086, 0x0084, 0x1188, 0x2079, 0xb280, 0x7a30, 0xa296,
+ 0x1107, 0x1158, 0x7834, 0xa005, 0x1140, 0x7087, 0x0001, 0x080c,
+ 0x48b8, 0x708b, 0x001a, 0x0029, 0x0010, 0x080c, 0x485e, 0x00fe,
+ 0x0005, 0x708b, 0x001b, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099,
+ 0xb280, 0x20a1, 0x020b, 0x7480, 0xa480, 0x0018, 0xa080, 0x0007,
+ 0xa084, 0x03f8, 0x8004, 0x20a8, 0x53a6, 0x60c3, 0x0084, 0x080c,
+ 0x4845, 0x0005, 0x0005, 0x0005, 0x0086, 0x0096, 0x2029, 0xad52,
+ 0x252c, 0x20a9, 0x0008, 0x2041, 0xb20e, 0x28a0, 0x2099, 0xb28e,
+ 0x53a3, 0x20a9, 0x0008, 0x2011, 0x0007, 0xd5d4, 0x0110, 0x2011,
+ 0x0000, 0x2800, 0xa200, 0x200c, 0xa1a6, 0xffff, 0x1148, 0xd5d4,
+ 0x0110, 0x8210, 0x0008, 0x8211, 0x1f04, 0x4769, 0x0804, 0x47d7,
+ 0x82ff, 0x1160, 0xd5d4, 0x0120, 0xa1a6, 0x3fff, 0x0d90, 0x0020,
+ 0xa1a6, 0x3fff, 0x0904, 0x47d7, 0xa18d, 0xc000, 0x20a9, 0x0010,
+ 0x2019, 0x0001, 0xd5d4, 0x0110, 0x2019, 0x0010, 0x2120, 0xd5d4,
+ 0x0110, 0x8423, 0x0008, 0x8424, 0x1240, 0xd5d4, 0x0110, 0x8319,
+ 0x0008, 0x8318, 0x1f04, 0x478f, 0x04d0, 0x23a8, 0x2021, 0x0001,
+ 0x8426, 0x8425, 0x1f04, 0x47a1, 0x2328, 0x8529, 0xa2be, 0x0007,
+ 0x0158, 0x0006, 0x2039, 0x0007, 0x2200, 0xa73a, 0x000e, 0x27a8,
+ 0xa5a8, 0x0010, 0x1f04, 0x47b0, 0x754e, 0xa5c8, 0x2be6, 0x292d,
+ 0xa5ac, 0x00ff, 0x7572, 0x6532, 0x6536, 0x0016, 0x2508, 0x080c,
+ 0x26a0, 0x001e, 0x60e7, 0x0000, 0x65ea, 0x2018, 0x2304, 0xa405,
+ 0x201a, 0x7077, 0x0001, 0x26a0, 0x2898, 0x20a9, 0x0008, 0x53a6,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0xa085, 0x0001, 0x0028, 0xa006,
+ 0x0018, 0xa006, 0x080c, 0x14f6, 0x009e, 0x008e, 0x0005, 0x2118,
+ 0x2021, 0x0000, 0x2001, 0x0007, 0xa39a, 0x0010, 0x0218, 0x8420,
+ 0x8001, 0x0cd0, 0x2118, 0x84ff, 0x0120, 0xa39a, 0x0010, 0x8421,
+ 0x1de0, 0x2021, 0x0001, 0x83ff, 0x0118, 0x8423, 0x8319, 0x1de8,
+ 0xa238, 0x2704, 0xa42c, 0x11b8, 0xa405, 0x203a, 0x714e, 0xa1a0,
+ 0x2be6, 0x242d, 0xa5ac, 0x00ff, 0x7572, 0x6532, 0x6536, 0x0016,
+ 0x2508, 0x080c, 0x26a0, 0x001e, 0x60e7, 0x0000, 0x65ea, 0x7077,
+ 0x0001, 0xa084, 0x0000, 0x0005, 0x00e6, 0x2071, 0xad00, 0x707b,
+ 0x0000, 0x00ee, 0x0005, 0x00e6, 0x00f6, 0x2079, 0x0100, 0x2071,
+ 0x0140, 0x080c, 0x7834, 0x7004, 0xa084, 0x4000, 0x0120, 0x7003,
+ 0x1000, 0x7003, 0x0000, 0x0126, 0x2091, 0x8000, 0x2071, 0xad22,
+ 0x2073, 0x0000, 0x7840, 0x0026, 0x0016, 0x2009, 0x00f7, 0x080c,
+ 0x48de, 0x001e, 0xa094, 0x0010, 0xa285, 0x0080, 0x7842, 0x7a42,
+ 0x002e, 0x012e, 0x00fe, 0x00ee, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x2011, 0xafd1, 0x2013, 0x0000, 0x7083, 0x0000, 0x012e, 0x20e1,
+ 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x080c, 0x782b, 0x2009,
+ 0x07d0, 0x2011, 0x481b, 0x080c, 0x6593, 0x0005, 0x0016, 0x0026,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x2009, 0x00f7, 0x080c, 0x48de,
+ 0x2061, 0xafda, 0x601b, 0x0000, 0x601f, 0x0000, 0x2061, 0xad00,
+ 0x6003, 0x0001, 0x2061, 0x0100, 0x6043, 0x0090, 0x6043, 0x0010,
+ 0x2009, 0x002d, 0x2011, 0x4883, 0x080c, 0x6501, 0x012e, 0x00ce,
+ 0x002e, 0x001e, 0x0005, 0x00e6, 0x0006, 0x0126, 0x2091, 0x8000,
+ 0x2071, 0x0100, 0x080c, 0x7834, 0x2071, 0x0140, 0x7004, 0xa084,
+ 0x4000, 0x0120, 0x7003, 0x1000, 0x7003, 0x0000, 0x080c, 0x5757,
+ 0x01a8, 0x080c, 0x5775, 0x1190, 0x2001, 0xaf9d, 0x2003, 0xaaaa,
+ 0x0016, 0x080c, 0x2744, 0x2001, 0xaf8e, 0x2102, 0x001e, 0x2001,
+ 0xaf9e, 0x2003, 0x0000, 0x080c, 0x569a, 0x0030, 0x2001, 0x0001,
+ 0x080c, 0x261e, 0x080c, 0x485e, 0x012e, 0x000e, 0x00ee, 0x0005,
+ 0x20a9, 0x0040, 0x20a1, 0xb3c0, 0x2099, 0xb28e, 0x3304, 0x8007,
+ 0x20a2, 0x9398, 0x94a0, 0x1f04, 0x48be, 0x0005, 0x20e1, 0x9080,
+ 0x20e1, 0x4000, 0x2099, 0xb200, 0x20a1, 0x020b, 0x20a9, 0x000c,
+ 0x53a6, 0x0005, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x2099, 0xb280,
+ 0x20a1, 0x020b, 0x20a9, 0x000c, 0x53a6, 0x0005, 0x00c6, 0x0006,
+ 0x2061, 0x0100, 0x810f, 0x2001, 0xad30, 0x2004, 0xa005, 0x1138,
+ 0x2001, 0xad14, 0x2004, 0xa084, 0x00ff, 0xa105, 0x0010, 0xa185,
+ 0x00f7, 0x604a, 0x000e, 0x00ce, 0x0005, 0x0016, 0x0046, 0x2001,
+ 0xad52, 0x2004, 0xd0a4, 0x0158, 0xa006, 0x2020, 0x2009, 0x002a,
+ 0x080c, 0xa96c, 0x2001, 0xad0c, 0x200c, 0xc195, 0x2102, 0x2019,
+ 0x002a, 0x2009, 0x0000, 0x080c, 0x2aac, 0x004e, 0x001e, 0x0005,
+ 0x080c, 0x485e, 0x708b, 0x0000, 0x7083, 0x0000, 0x0005, 0x0006,
+ 0x2001, 0xad0c, 0x2004, 0xd09c, 0x0100, 0x000e, 0x0005, 0x0006,
+ 0x0016, 0x0126, 0x2091, 0x8000, 0x2001, 0x0101, 0x200c, 0xa18d,
+ 0x0006, 0x2102, 0x012e, 0x001e, 0x000e, 0x0005, 0x0156, 0x20a9,
+ 0x00ff, 0x2009, 0xae34, 0xa006, 0x200a, 0x8108, 0x1f04, 0x4934,
+ 0x015e, 0x0005, 0x00d6, 0x0036, 0x0156, 0x0136, 0x0146, 0x2069,
+ 0xad51, 0xa006, 0x6002, 0x6007, 0x0707, 0x600a, 0x600e, 0x6012,
+ 0xa198, 0x2be6, 0x231d, 0xa39c, 0x00ff, 0x6316, 0x20a9, 0x0004,
+ 0xac98, 0x0006, 0x23a0, 0x40a4, 0x20a9, 0x0004, 0xac98, 0x000a,
+ 0x23a0, 0x40a4, 0x603e, 0x6042, 0x604e, 0x6052, 0x6056, 0x605a,
+ 0x605e, 0x6062, 0x6066, 0x606a, 0x606e, 0x6072, 0x6076, 0x607a,
+ 0x607e, 0x6082, 0x6086, 0x608a, 0x608e, 0x6092, 0x6096, 0x609a,
+ 0x609e, 0x60ae, 0x61a2, 0x00d6, 0x60a4, 0xa06d, 0x0110, 0x080c,
+ 0x15f0, 0x60a7, 0x0000, 0x60a8, 0xa06d, 0x0110, 0x080c, 0x15f0,
+ 0x60ab, 0x0000, 0x00de, 0xa006, 0x604a, 0x6810, 0x603a, 0x680c,
+ 0x6046, 0x6814, 0xa084, 0x00ff, 0x6042, 0x014e, 0x013e, 0x015e,
+ 0x003e, 0x00de, 0x0005, 0x0126, 0x2091, 0x8000, 0x6944, 0x6e48,
+ 0xa684, 0x3fff, 0xa082, 0x4000, 0x1a04, 0x4a49, 0xa18c, 0xff00,
+ 0x810f, 0xa182, 0x00ff, 0x1a04, 0x4a4e, 0x2001, 0xad0c, 0x2004,
+ 0xa084, 0x0003, 0x01c0, 0x2001, 0xad0c, 0x2004, 0xd084, 0x1904,
+ 0x4a31, 0xa188, 0xae34, 0x2104, 0xa065, 0x0904, 0x4a31, 0x6004,
+ 0xa084, 0x00ff, 0xa08e, 0x0006, 0x1904, 0x4a31, 0x6000, 0xd0c4,
+ 0x0904, 0x4a31, 0x0068, 0xa188, 0xae34, 0x2104, 0xa065, 0x0904,
+ 0x4a15, 0x6004, 0xa084, 0x00ff, 0xa08e, 0x0006, 0x1904, 0x4a1a,
+ 0x60a4, 0xa00d, 0x0118, 0x080c, 0x4ef9, 0x05d0, 0x60a8, 0xa00d,
+ 0x0188, 0x080c, 0x4f43, 0x1170, 0x694c, 0xd1fc, 0x1118, 0x080c,
+ 0x4c11, 0x0448, 0x080c, 0x4bd3, 0x694c, 0xd1ec, 0x1520, 0x080c,
+ 0x4ded, 0x0408, 0x694c, 0xa184, 0xa000, 0x0178, 0xd1ec, 0x0140,
+ 0xd1fc, 0x0118, 0x080c, 0x4dfc, 0x0028, 0x080c, 0x4dfc, 0x0028,
+ 0xd1fc, 0x0118, 0x080c, 0x4bd3, 0x0070, 0x6050, 0xa00d, 0x0130,
+ 0x2d00, 0x200a, 0x6803, 0x0000, 0x6052, 0x0028, 0x2d00, 0x6052,
+ 0x604e, 0x6803, 0x0000, 0x080c, 0x67c5, 0xa006, 0x012e, 0x0005,
+ 0x2001, 0x0005, 0x2009, 0x0000, 0x04e8, 0x2001, 0x0028, 0x2009,
+ 0x0000, 0x04c0, 0xa082, 0x0006, 0x12a0, 0x2001, 0xad34, 0x2004,
+ 0xd0ac, 0x1160, 0x60a0, 0xd0bc, 0x1148, 0x6100, 0xd1fc, 0x0904,
+ 0x49d0, 0x2001, 0x0029, 0x2009, 0x1000, 0x0420, 0x2001, 0x0028,
+ 0x00a8, 0x2009, 0xad0c, 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004,
+ 0x0068, 0xd184, 0x0118, 0x2001, 0x0004, 0x0040, 0x2001, 0x0029,
+ 0x6100, 0xd1fc, 0x0118, 0x2009, 0x1000, 0x0060, 0x2009, 0x0000,
+ 0x0048, 0x2001, 0x0029, 0x2009, 0x0000, 0x0020, 0x2001, 0x0029,
+ 0x2009, 0x0000, 0xa005, 0x012e, 0x0005, 0x00e6, 0x0126, 0x2091,
+ 0x8000, 0x6844, 0x8007, 0xa084, 0x00ff, 0x2008, 0xa182, 0x00ff,
+ 0x1a04, 0x4aa8, 0xa188, 0xae34, 0x2104, 0xa065, 0x01c0, 0x6004,
+ 0xa084, 0x00ff, 0xa08e, 0x0006, 0x11a8, 0x2c70, 0x080c, 0x8022,
+ 0x05e8, 0x2e00, 0x601a, 0x2d00, 0x6012, 0x600b, 0xffff, 0x601f,
+ 0x000a, 0x2009, 0x0003, 0x080c, 0x80a7, 0xa006, 0x0460, 0x2001,
+ 0x0028, 0x0440, 0xa082, 0x0006, 0x1298, 0x2001, 0xad34, 0x2004,
+ 0xd0ac, 0x1158, 0x60a0, 0xd0bc, 0x1140, 0x6100, 0xd1fc, 0x09e8,
+ 0x2001, 0x0029, 0x2009, 0x1000, 0x00a8, 0x2001, 0x0028, 0x0090,
+ 0x2009, 0xad0c, 0x210c, 0xd18c, 0x0118, 0x2001, 0x0004, 0x0050,
+ 0xd184, 0x0118, 0x2001, 0x0004, 0x0028, 0x2001, 0x0029, 0x0010,
+ 0x2001, 0x0029, 0xa005, 0x012e, 0x00ee, 0x0005, 0x2001, 0x002c,
+ 0x0cc8, 0x00f6, 0x00e6, 0x0126, 0x2091, 0x8000, 0x2011, 0x0000,
+ 0x2079, 0xad00, 0x6944, 0xa18c, 0xff00, 0x810f, 0xa182, 0x00ff,
+ 0x1a04, 0x4b77, 0x2001, 0xad0c, 0x2004, 0xa084, 0x0003, 0x1904,
+ 0x4b65, 0x080c, 0x4cdc, 0x1180, 0x6004, 0xa084, 0x00ff, 0xa082,
+ 0x0006, 0x1250, 0x2001, 0xad34, 0x2004, 0xd0ac, 0x1904, 0x4b60,
+ 0x60a0, 0xd0bc, 0x1904, 0x4b60, 0x6864, 0xa0c6, 0x006f, 0x0118,
+ 0x2008, 0x0804, 0x4b28, 0x6968, 0x2140, 0xa18c, 0xff00, 0x810f,
+ 0x78d0, 0xd0ac, 0x1118, 0xa182, 0x0080, 0x06d0, 0xa182, 0x00ff,
+ 0x16b8, 0x6a70, 0x6b6c, 0x786c, 0xa306, 0x1160, 0x7870, 0xa24e,
+ 0x1118, 0x2208, 0x2310, 0x0460, 0xa9cc, 0xff00, 0x1118, 0x2208,
+ 0x2310, 0x0430, 0x080c, 0x3b58, 0x2c70, 0x0550, 0x2009, 0x0000,
+ 0x2011, 0x0000, 0xa0c6, 0x4000, 0x1160, 0x0006, 0x2e60, 0x080c,
+ 0x4f6e, 0x1108, 0xc185, 0x7000, 0xd0bc, 0x0108, 0xc18d, 0x000e,
+ 0x0088, 0xa0c6, 0x4007, 0x1110, 0x2408, 0x0060, 0xa0c6, 0x4008,
+ 0x1118, 0x2708, 0x2610, 0x0030, 0xa0c6, 0x4009, 0x1108, 0x0010,
+ 0x2001, 0x4006, 0x6866, 0x696a, 0x6a6e, 0x2001, 0x0030, 0x0458,
+ 0x080c, 0x8022, 0x1138, 0x2001, 0x4005, 0x2009, 0x0003, 0x2011,
+ 0x0000, 0x0c80, 0x2e00, 0x601a, 0x080c, 0x9956, 0x2d00, 0x6012,
+ 0x601f, 0x0001, 0xa006, 0xd88c, 0x0110, 0x2001, 0x4000, 0x683a,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x2ad9, 0x012e, 0x2001, 0x0000,
+ 0x080c, 0x4c1e, 0x2001, 0x0002, 0x080c, 0x4c30, 0x2009, 0x0002,
+ 0x080c, 0x80a7, 0xa006, 0xa005, 0x012e, 0x00ee, 0x00fe, 0x0005,
+ 0x2001, 0x0028, 0x2009, 0x0000, 0x0cb0, 0x2009, 0xad0c, 0x210c,
+ 0xd18c, 0x0118, 0x2001, 0x0004, 0x0038, 0xd184, 0x0118, 0x2001,
+ 0x0004, 0x0010, 0x2001, 0x0029, 0x2009, 0x0000, 0x0c20, 0x2001,
+ 0x0029, 0x2009, 0x0000, 0x08f8, 0x6944, 0x6e48, 0xa684, 0x3fff,
+ 0xa082, 0x4000, 0x16b8, 0xa18c, 0xff00, 0x810f, 0xa182, 0x00ff,
+ 0x12e0, 0xa188, 0xae34, 0x2104, 0xa065, 0x01b8, 0x6004, 0xa084,
+ 0x00ff, 0xa08e, 0x0006, 0x11b0, 0x684c, 0xd0ec, 0x0120, 0x080c,
+ 0x4dfc, 0x04c9, 0x0030, 0x04b9, 0x684c, 0xd0fc, 0x0110, 0x080c,
+ 0x4ded, 0x080c, 0x4e3a, 0xa006, 0x00c8, 0x2001, 0x0028, 0x2009,
+ 0x0000, 0x00a0, 0xa082, 0x0006, 0x1240, 0x6100, 0xd1fc, 0x0d20,
+ 0x2001, 0x0029, 0x2009, 0x1000, 0x0048, 0x2001, 0x0029, 0x2009,
+ 0x0000, 0x0020, 0x2001, 0x0029, 0x2009, 0x0000, 0xa005, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x6050, 0xa00d, 0x0138, 0x2d00, 0x200a,
+ 0x6803, 0x0000, 0x6052, 0x012e, 0x0005, 0x2d00, 0x6052, 0x604e,
+ 0x6803, 0x0000, 0x0cc0, 0x0126, 0x2091, 0x8000, 0x604c, 0xa005,
+ 0x0170, 0x00e6, 0x2071, 0xafc7, 0x7004, 0xa086, 0x0002, 0x0168,
+ 0x00ee, 0x604c, 0x6802, 0x2d00, 0x604e, 0x012e, 0x0005, 0x2d00,
+ 0x6052, 0x604e, 0x6803, 0x0000, 0x0cc0, 0x701c, 0xac06, 0x1d80,
+ 0x604c, 0x2070, 0x7000, 0x6802, 0x2d00, 0x7002, 0x00ee, 0x012e,
+ 0x0005, 0x0126, 0x2091, 0x8000, 0x604c, 0xa06d, 0x0130, 0x6800,
+ 0xa005, 0x1108, 0x6052, 0x604e, 0xad05, 0x012e, 0x0005, 0x604c,
+ 0xa06d, 0x0130, 0x6800, 0xa005, 0x1108, 0x6052, 0x604e, 0xad05,
+ 0x0005, 0x6803, 0x0000, 0x6084, 0xa00d, 0x0120, 0x2d00, 0x200a,
+ 0x6086, 0x0005, 0x2d00, 0x6086, 0x6082, 0x0cd8, 0x0126, 0x00c6,
+ 0x0026, 0x2091, 0x8000, 0x6218, 0x2260, 0x6200, 0xa005, 0x0110,
+ 0xc285, 0x0008, 0xc284, 0x6202, 0x002e, 0x00ce, 0x012e, 0x0005,
+ 0x0126, 0x00c6, 0x2091, 0x8000, 0x6218, 0x2260, 0x6204, 0x0006,
+ 0xa086, 0x0006, 0x1180, 0x609c, 0xd0ac, 0x0168, 0x2001, 0xad52,
+ 0x2004, 0xd0a4, 0x0140, 0xa284, 0xff00, 0x8007, 0xa086, 0x0007,
+ 0x1110, 0x2011, 0x0600, 0x000e, 0xa294, 0xff00, 0xa215, 0x6206,
+ 0x0006, 0xa086, 0x0006, 0x1128, 0x6290, 0x82ff, 0x1110, 0x080c,
+ 0x14f6, 0x000e, 0x00ce, 0x012e, 0x0005, 0x0126, 0x00c6, 0x2091,
+ 0x8000, 0x6218, 0x2260, 0x6204, 0x0006, 0xa086, 0x0006, 0x1178,
+ 0x609c, 0xd0a4, 0x0160, 0x2001, 0xad52, 0x2004, 0xd0ac, 0x1138,
+ 0xa284, 0x00ff, 0xa086, 0x0007, 0x1110, 0x2011, 0x0006, 0x000e,
+ 0xa294, 0x00ff, 0x8007, 0xa215, 0x6206, 0x00ce, 0x012e, 0x0005,
+ 0x0026, 0xa182, 0x00ff, 0x0218, 0xa085, 0x0001, 0x00b0, 0xa190,
+ 0xae34, 0x2204, 0xa065, 0x1180, 0x0016, 0x00d6, 0x080c, 0x15c0,
+ 0x2d60, 0x00de, 0x001e, 0x0d80, 0x2c00, 0x2012, 0x60a7, 0x0000,
+ 0x60ab, 0x0000, 0x080c, 0x493a, 0xa006, 0x002e, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x0026, 0xa182, 0x00ff, 0x0218, 0xa085, 0x0001,
+ 0x0480, 0x00d6, 0xa190, 0xae34, 0x2204, 0xa06d, 0x0540, 0x2013,
+ 0x0000, 0x00d6, 0x00c6, 0x2d60, 0x60a4, 0xa06d, 0x0110, 0x080c,
+ 0x15f0, 0x60a8, 0xa06d, 0x0110, 0x080c, 0x15f0, 0x00ce, 0x00de,
+ 0x00d6, 0x00c6, 0x68ac, 0x2060, 0x8cff, 0x0168, 0x600c, 0x0006,
+ 0x6010, 0x2068, 0x080c, 0x9596, 0x0110, 0x080c, 0x1600, 0x080c,
+ 0x8078, 0x00ce, 0x0c88, 0x00ce, 0x00de, 0x080c, 0x15f0, 0x00de,
+ 0xa006, 0x002e, 0x012e, 0x0005, 0x0016, 0xa182, 0x00ff, 0x0218,
+ 0xa085, 0x0001, 0x0030, 0xa188, 0xae34, 0x2104, 0xa065, 0x0dc0,
+ 0xa006, 0x001e, 0x0005, 0x00d6, 0x0156, 0x0136, 0x0146, 0x600b,
+ 0x0000, 0x600f, 0x0000, 0x6000, 0xc08c, 0x6002, 0x080c, 0x574f,
+ 0x1538, 0x60a0, 0xa086, 0x007e, 0x2069, 0xb290, 0x0130, 0x2001,
+ 0xad34, 0x2004, 0xd0ac, 0x11e0, 0x0098, 0x2d04, 0xd0e4, 0x01c0,
+ 0x00d6, 0x2069, 0xb28e, 0x00c6, 0x2061, 0xaf9f, 0x6810, 0x2062,
+ 0x6814, 0x6006, 0x6818, 0x600a, 0x681c, 0x600e, 0x00ce, 0x00de,
+ 0x8d69, 0x2d04, 0x2069, 0x0140, 0x6886, 0x2069, 0xad00, 0x68a2,
+ 0x2069, 0xb28e, 0x6808, 0x605e, 0x6810, 0x6062, 0x6138, 0xa10a,
+ 0x0208, 0x603a, 0x6814, 0x6066, 0x2099, 0xb296, 0xac88, 0x000a,
+ 0x21a0, 0x20a9, 0x0004, 0x53a3, 0x2099, 0xb29a, 0xac88, 0x0006,
+ 0x21a0, 0x20a9, 0x0004, 0x53a3, 0x2069, 0xb2ae, 0x6808, 0x606a,
+ 0x690c, 0x616e, 0x6810, 0x6072, 0x6818, 0x6076, 0xa182, 0x0211,
+ 0x1218, 0x2009, 0x0008, 0x0400, 0xa182, 0x0259, 0x1218, 0x2009,
+ 0x0007, 0x00d0, 0xa182, 0x02c1, 0x1218, 0x2009, 0x0006, 0x00a0,
+ 0xa182, 0x0349, 0x1218, 0x2009, 0x0005, 0x0070, 0xa182, 0x0421,
+ 0x1218, 0x2009, 0x0004, 0x0040, 0xa182, 0x0581, 0x1218, 0x2009,
+ 0x0003, 0x0010, 0x2009, 0x0002, 0x6192, 0x014e, 0x013e, 0x015e,
+ 0x00de, 0x0005, 0x0016, 0x0026, 0x00e6, 0x2071, 0xb28d, 0x2e04,
+ 0x6896, 0x2071, 0xb28e, 0x7004, 0x689a, 0x701c, 0x689e, 0x6a00,
+ 0x2009, 0xad71, 0x210c, 0xd0bc, 0x0120, 0xd1ec, 0x0110, 0xc2ad,
+ 0x0008, 0xc2ac, 0xd0c4, 0x0120, 0xd1e4, 0x0110, 0xc2bd, 0x0008,
+ 0xc2bc, 0x6a02, 0x00ee, 0x002e, 0x001e, 0x0005, 0x00d6, 0x0126,
+ 0x2091, 0x8000, 0x60a4, 0xa06d, 0x01c0, 0x6900, 0x81ff, 0x1540,
+ 0x6a04, 0xa282, 0x0010, 0x1648, 0xad88, 0x0004, 0x20a9, 0x0010,
+ 0x2104, 0xa086, 0xffff, 0x0128, 0x8108, 0x1f04, 0x4da8, 0x080c,
+ 0x14f6, 0x260a, 0x8210, 0x6a06, 0x0098, 0x080c, 0x15d9, 0x01a8,
+ 0x2d00, 0x60a6, 0x6803, 0x0000, 0xad88, 0x0004, 0x20a9, 0x0010,
+ 0x200b, 0xffff, 0x8108, 0x1f04, 0x4dc0, 0x6807, 0x0001, 0x6e12,
+ 0xa085, 0x0001, 0x012e, 0x00de, 0x0005, 0xa006, 0x0cd8, 0x0126,
+ 0x2091, 0x8000, 0x00d6, 0x60a4, 0xa00d, 0x01a0, 0x2168, 0x6800,
+ 0xa005, 0x1160, 0x080c, 0x4ef9, 0x1168, 0x200b, 0xffff, 0x6804,
+ 0xa08a, 0x0002, 0x0218, 0x8001, 0x6806, 0x0020, 0x080c, 0x15f0,
+ 0x60a7, 0x0000, 0x00de, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x4f56, 0x0010, 0x080c, 0x4bc0, 0x080c, 0x4e71, 0x1dd8,
+ 0x080c, 0x4e3a, 0x012e, 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000,
+ 0x60a8, 0xa06d, 0x01c0, 0x6950, 0x81ff, 0x1540, 0x6a54, 0xa282,
+ 0x0010, 0x1670, 0xad88, 0x0018, 0x20a9, 0x0010, 0x2104, 0xa086,
+ 0xffff, 0x0128, 0x8108, 0x1f04, 0x4e0e, 0x080c, 0x14f6, 0x260a,
+ 0x8210, 0x6a56, 0x0098, 0x080c, 0x15d9, 0x01d0, 0x2d00, 0x60aa,
+ 0x6853, 0x0000, 0xad88, 0x0018, 0x20a9, 0x0010, 0x200b, 0xffff,
+ 0x8108, 0x1f04, 0x4e26, 0x6857, 0x0001, 0x6e62, 0x0010, 0x080c,
+ 0x4c11, 0x0089, 0x1de0, 0xa085, 0x0001, 0x012e, 0x00de, 0x0005,
+ 0xa006, 0x0cd8, 0x0126, 0x2091, 0x8000, 0x080c, 0x67c5, 0x012e,
+ 0x0005, 0xa01e, 0x0010, 0x2019, 0x0001, 0xa00e, 0x0126, 0x2091,
+ 0x8000, 0x604c, 0x2068, 0x6000, 0xd0dc, 0x1170, 0x8dff, 0x01e8,
+ 0x83ff, 0x0120, 0x6848, 0xa606, 0x0158, 0x0030, 0x683c, 0xa406,
+ 0x1118, 0x6840, 0xa506, 0x0120, 0x2d08, 0x6800, 0x2068, 0x0c70,
+ 0x6a00, 0x604c, 0xad06, 0x1110, 0x624e, 0x0018, 0xa180, 0x0000,
+ 0x2202, 0x82ff, 0x1110, 0x6152, 0x8dff, 0x012e, 0x0005, 0xa01e,
+ 0x0010, 0x2019, 0x0001, 0xa00e, 0x6080, 0x2068, 0x8dff, 0x01e8,
+ 0x83ff, 0x0120, 0x6848, 0xa606, 0x0158, 0x0030, 0x683c, 0xa406,
+ 0x1118, 0x6840, 0xa506, 0x0120, 0x2d08, 0x6800, 0x2068, 0x0c70,
+ 0x6a00, 0x6080, 0xad06, 0x1110, 0x6282, 0x0018, 0xa180, 0x0000,
+ 0x2202, 0x82ff, 0x1110, 0x6186, 0x8dff, 0x0005, 0xa016, 0x080c,
+ 0x4ef3, 0x1110, 0x2011, 0x0001, 0x080c, 0x4f3d, 0x1110, 0xa295,
+ 0x0002, 0x0005, 0x080c, 0x4f6e, 0x0118, 0x080c, 0x964b, 0x0010,
+ 0xa085, 0x0001, 0x0005, 0x080c, 0x4f6e, 0x0118, 0x080c, 0x95e4,
+ 0x0010, 0xa085, 0x0001, 0x0005, 0x080c, 0x4f6e, 0x0118, 0x080c,
+ 0x962e, 0x0010, 0xa085, 0x0001, 0x0005, 0x080c, 0x4f6e, 0x0118,
+ 0x080c, 0x9600, 0x0010, 0xa085, 0x0001, 0x0005, 0x080c, 0x4f6e,
+ 0x0118, 0x080c, 0x9667, 0x0010, 0xa085, 0x0001, 0x0005, 0x0126,
+ 0x0006, 0x00d6, 0x2091, 0x8000, 0x6080, 0xa06d, 0x01a0, 0x6800,
+ 0x0006, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x97fd,
+ 0x0006, 0x6000, 0xd0fc, 0x0110, 0x080c, 0xac03, 0x000e, 0x080c,
+ 0x510c, 0x000e, 0x0c50, 0x6083, 0x0000, 0x6087, 0x0000, 0x00de,
+ 0x000e, 0x012e, 0x0005, 0x60a4, 0xa00d, 0x1118, 0xa085, 0x0001,
+ 0x0005, 0x00e6, 0x2170, 0x7000, 0xa005, 0x1160, 0x20a9, 0x0010,
+ 0xae88, 0x0004, 0x2104, 0xa606, 0x0128, 0x8108, 0x1f04, 0x4f02,
+ 0xa085, 0x0001, 0xa006, 0x00ee, 0x0005, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x60a4, 0xa06d, 0x1128, 0x080c, 0x15d9, 0x01a0, 0x2d00,
+ 0x60a6, 0x6803, 0x0001, 0x6807, 0x0000, 0xad88, 0x0004, 0x20a9,
+ 0x0010, 0x200b, 0xffff, 0x8108, 0x1f04, 0x4f21, 0xa085, 0x0001,
+ 0x012e, 0x00de, 0x0005, 0xa006, 0x0cd8, 0x00d6, 0x0126, 0x2091,
+ 0x8000, 0x60a4, 0xa06d, 0x0130, 0x60a7, 0x0000, 0x080c, 0x15f0,
+ 0xa085, 0x0001, 0x012e, 0x00de, 0x0005, 0x60a8, 0xa00d, 0x1118,
+ 0xa085, 0x0001, 0x0005, 0x00e6, 0x2170, 0x7050, 0xa005, 0x1160,
+ 0x20a9, 0x0010, 0xae88, 0x0018, 0x2104, 0xa606, 0x0128, 0x8108,
+ 0x1f04, 0x4f4c, 0xa085, 0x0001, 0x00ee, 0x0005, 0x0126, 0x2091,
+ 0x8000, 0x0c19, 0x1188, 0x200b, 0xffff, 0x00d6, 0x60a8, 0x2068,
+ 0x6854, 0xa08a, 0x0002, 0x0218, 0x8001, 0x6856, 0x0020, 0x080c,
+ 0x15f0, 0x60ab, 0x0000, 0x00de, 0x012e, 0x0005, 0x609c, 0xd0a4,
+ 0x0005, 0x00f6, 0x080c, 0x574f, 0x01b0, 0x71b4, 0x81ff, 0x1198,
+ 0x71d0, 0xd19c, 0x0180, 0x2001, 0x007e, 0xa080, 0xae34, 0x2004,
+ 0xa07d, 0x0148, 0x7804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x1118,
+ 0x7800, 0xc0ed, 0x7802, 0x2079, 0xad51, 0x7804, 0xd0a4, 0x01e8,
+ 0x0156, 0x00c6, 0x20a9, 0x007f, 0x2009, 0x0000, 0x0016, 0x080c,
+ 0x4cdc, 0x1168, 0x6004, 0xa084, 0xff00, 0x8007, 0xa096, 0x0004,
+ 0x0118, 0xa086, 0x0006, 0x1118, 0x6000, 0xc0ed, 0x6002, 0x001e,
+ 0x8108, 0x1f04, 0x4f96, 0x00ce, 0x015e, 0x080c, 0x502d, 0x0120,
+ 0x2001, 0xafa2, 0x200c, 0x0038, 0x2079, 0xad51, 0x7804, 0xd0a4,
+ 0x0130, 0x2009, 0x07d0, 0x2011, 0x4fc1, 0x080c, 0x6593, 0x00fe,
+ 0x0005, 0x2011, 0x4fc1, 0x080c, 0x650d, 0x080c, 0x502d, 0x01f0,
+ 0x2001, 0xaeb2, 0x2004, 0xa080, 0x0000, 0x200c, 0xc1ec, 0x2102,
+ 0x2001, 0xad52, 0x2004, 0xd0a4, 0x0130, 0x2009, 0x07d0, 0x2011,
+ 0x4fc1, 0x080c, 0x6593, 0x00e6, 0x2071, 0xad00, 0x706f, 0x0000,
+ 0x7073, 0x0000, 0x080c, 0x28fa, 0x00ee, 0x04b0, 0x0156, 0x00c6,
+ 0x20a9, 0x007f, 0x2009, 0x0000, 0x0016, 0x080c, 0x4cdc, 0x1530,
+ 0x6000, 0xd0ec, 0x0518, 0x0046, 0x62a0, 0xa294, 0x00ff, 0x8227,
+ 0xa006, 0x2009, 0x0029, 0x080c, 0xa96c, 0x6000, 0xc0e5, 0xc0ec,
+ 0x6002, 0x6004, 0xa084, 0x00ff, 0xa085, 0x0700, 0x6006, 0x2019,
+ 0x0029, 0x080c, 0x68e7, 0x0076, 0x2039, 0x0000, 0x080c, 0x681d,
+ 0x2009, 0x0000, 0x080c, 0xa712, 0x007e, 0x004e, 0x001e, 0x8108,
+ 0x1f04, 0x4fec, 0x00ce, 0x015e, 0x0005, 0x00c6, 0x6018, 0x2060,
+ 0x6000, 0xc0ec, 0x6002, 0x00ce, 0x0005, 0x7818, 0x2004, 0xd0ac,
+ 0x0005, 0x7818, 0x2004, 0xd0bc, 0x0005, 0x00f6, 0x2001, 0xaeb2,
+ 0x2004, 0xa07d, 0x0110, 0x7800, 0xd0ec, 0x00fe, 0x0005, 0x0126,
+ 0x0026, 0x2091, 0x8000, 0x6200, 0xa005, 0x0110, 0xc2fd, 0x0008,
+ 0xc2fc, 0x6202, 0x002e, 0x012e, 0x0005, 0x2071, 0xae13, 0x7003,
+ 0x0001, 0x7007, 0x0000, 0x7013, 0x0000, 0x7017, 0x0000, 0x701b,
+ 0x0000, 0x701f, 0x0000, 0x700b, 0x0000, 0x704b, 0x0001, 0x704f,
+ 0x0000, 0x705b, 0x0020, 0x705f, 0x0040, 0x707f, 0x0000, 0x2071,
+ 0xaf7c, 0x7003, 0xae13, 0x7007, 0x0000, 0x700b, 0x0000, 0x700f,
+ 0xaf5c, 0x7013, 0x0020, 0x7017, 0x0040, 0x7037, 0x0000, 0x0005,
+ 0x0016, 0x00e6, 0x2071, 0xaf34, 0xa00e, 0x7186, 0x718a, 0x7097,
+ 0x0001, 0x2001, 0xad52, 0x2004, 0xd0fc, 0x1150, 0x2001, 0xad52,
+ 0x2004, 0xa00e, 0xd09c, 0x0108, 0x8108, 0x7102, 0x0804, 0x50d6,
+ 0x2001, 0xad71, 0x200c, 0xa184, 0x000f, 0x2009, 0xad72, 0x210c,
+ 0x0002, 0x507e, 0x50b1, 0x50b8, 0x50c2, 0x50c7, 0x507e, 0x507e,
+ 0x507e, 0x50a1, 0x507e, 0x507e, 0x507e, 0x507e, 0x507e, 0x507e,
+ 0x507e, 0x7003, 0x0004, 0x0136, 0x0146, 0x0156, 0x2099, 0xad75,
+ 0x20a1, 0xaf85, 0x20a9, 0x0004, 0x53a3, 0x015e, 0x014e, 0x013e,
+ 0x0428, 0x708f, 0x0005, 0x7007, 0x0122, 0x2001, 0x0002, 0x0030,
+ 0x708f, 0x0002, 0x7007, 0x0121, 0x2001, 0x0003, 0x7002, 0x7097,
+ 0x0001, 0x0088, 0x7007, 0x0122, 0x2001, 0x0002, 0x0020, 0x7007,
+ 0x0121, 0x2001, 0x0003, 0x7002, 0xa006, 0x7096, 0x708e, 0xa184,
+ 0xff00, 0x8007, 0x709a, 0xa184, 0x00ff, 0x7092, 0x00ee, 0x001e,
+ 0x0005, 0x00e6, 0x2071, 0xae13, 0x684c, 0xa005, 0x1130, 0x7028,
+ 0xc085, 0x702a, 0xa085, 0x0001, 0x0428, 0x6a60, 0x7236, 0x6b64,
+ 0x733a, 0x6868, 0x703e, 0x7076, 0x686c, 0x7042, 0x707a, 0x684c,
+ 0x702e, 0x6844, 0x7032, 0x2009, 0x000d, 0x200a, 0x700b, 0x0000,
+ 0x8007, 0x8006, 0x8006, 0xa08c, 0x003f, 0xa084, 0xffc0, 0xa210,
+ 0x2100, 0xa319, 0x726e, 0x7372, 0x7028, 0xc084, 0x702a, 0x7007,
+ 0x0001, 0xa006, 0x00ee, 0x0005, 0x0156, 0x00e6, 0x0026, 0x6838,
+ 0xd0fc, 0x1904, 0x5165, 0x6804, 0xa00d, 0x0188, 0x00d6, 0x2071,
+ 0xad00, 0xa016, 0x702c, 0x2168, 0x6904, 0x206a, 0x8210, 0x2d00,
+ 0x81ff, 0x1dc8, 0x702e, 0x70b0, 0xa200, 0x70b2, 0x00de, 0x2071,
+ 0xae13, 0x701c, 0xa005, 0x1904, 0x5175, 0x20a9, 0x0032, 0x0f04,
+ 0x5173, 0x0e04, 0x512f, 0x2071, 0xaf34, 0x7200, 0x82ff, 0x05d8,
+ 0x6934, 0xa186, 0x0103, 0x1904, 0x5183, 0x6948, 0x6844, 0xa105,
+ 0x1540, 0x2009, 0x8020, 0x2200, 0x0002, 0x5173, 0x514a, 0x519b,
+ 0x51a7, 0x5173, 0x2071, 0x0000, 0x20a9, 0x0032, 0x0f04, 0x5173,
+ 0x7018, 0xd084, 0x1dd8, 0x7122, 0x683c, 0x7026, 0x6840, 0x702a,
+ 0x701b, 0x0001, 0x2091, 0x4080, 0x2071, 0xad00, 0x702c, 0x206a,
+ 0x2d00, 0x702e, 0x70b0, 0x8000, 0x70b2, 0x002e, 0x00ee, 0x015e,
+ 0x0005, 0x6844, 0xa086, 0x0100, 0x1130, 0x6868, 0xa005, 0x1118,
+ 0x2009, 0x8020, 0x0880, 0x2071, 0xae13, 0x2d08, 0x206b, 0x0000,
+ 0x7010, 0x8000, 0x7012, 0x7018, 0xa06d, 0x711a, 0x0110, 0x6902,
+ 0x0008, 0x711e, 0x0c10, 0xa18c, 0x00ff, 0xa186, 0x0017, 0x0130,
+ 0xa186, 0x001e, 0x0118, 0xa18e, 0x001f, 0x1d28, 0x684c, 0xd0cc,
+ 0x0d10, 0x6850, 0xa084, 0x00ff, 0xa086, 0x0001, 0x19e0, 0x2009,
+ 0x8021, 0x0804, 0x5143, 0x7084, 0x8008, 0xa092, 0x001e, 0x1a98,
+ 0x7186, 0xae90, 0x0003, 0xa210, 0x683c, 0x2012, 0x0078, 0x7084,
+ 0x8008, 0xa092, 0x000f, 0x1a38, 0x7186, 0xae90, 0x0003, 0x8003,
+ 0xa210, 0x683c, 0x2012, 0x8210, 0x6840, 0x2012, 0x7088, 0xa10a,
+ 0x0a04, 0x515c, 0x718c, 0x7084, 0xa10a, 0x0a04, 0x515c, 0x2071,
+ 0x0000, 0x7018, 0xd084, 0x1904, 0x515c, 0x2071, 0xaf34, 0x7000,
+ 0xa086, 0x0002, 0x1150, 0x080c, 0x5426, 0x2071, 0x0000, 0x701b,
+ 0x0001, 0x2091, 0x4080, 0x0804, 0x515c, 0x080c, 0x5450, 0x2071,
+ 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x0804, 0x515c, 0x0006,
+ 0x684c, 0x0006, 0x6837, 0x0103, 0x20a9, 0x001c, 0xad80, 0x0011,
+ 0x20a0, 0x2001, 0x0000, 0x40a4, 0x000e, 0xa084, 0x00ff, 0x684e,
+ 0x000e, 0x684a, 0x6952, 0x0005, 0x2071, 0xae13, 0x7004, 0x0002,
+ 0x5202, 0x5213, 0x5411, 0x5412, 0x541f, 0x5425, 0x5203, 0x5402,
+ 0x5398, 0x53ee, 0x0005, 0x0126, 0x2091, 0x8000, 0x0e04, 0x5212,
+ 0x2009, 0x000d, 0x7030, 0x200a, 0x2091, 0x4080, 0x7007, 0x0001,
+ 0x700b, 0x0000, 0x012e, 0x2069, 0xafda, 0x683c, 0xa005, 0x03f8,
+ 0x11f0, 0x0126, 0x2091, 0x8000, 0x2069, 0x0000, 0x6934, 0x2001,
+ 0xae1f, 0x2004, 0xa10a, 0x0170, 0x0e04, 0x5236, 0x2069, 0x0000,
+ 0x6818, 0xd084, 0x1158, 0x2009, 0x8040, 0x6922, 0x681b, 0x0001,
+ 0x2091, 0x4080, 0x2069, 0xafda, 0x683f, 0xffff, 0x012e, 0x2069,
+ 0xad00, 0x6844, 0x6964, 0xa102, 0x2069, 0xaf34, 0x688a, 0x6984,
+ 0x701c, 0xa06d, 0x0120, 0x81ff, 0x0904, 0x528c, 0x00a0, 0x81ff,
+ 0x0904, 0x5352, 0x2071, 0xaf34, 0x7184, 0x7088, 0xa10a, 0x1258,
+ 0x7190, 0x2071, 0xafda, 0x7038, 0xa005, 0x0128, 0x1b04, 0x5352,
+ 0x713a, 0x0804, 0x5352, 0x2071, 0xaf34, 0x718c, 0x0126, 0x2091,
+ 0x8000, 0x7084, 0xa10a, 0x0a04, 0x536d, 0x0e04, 0x530e, 0x2071,
+ 0x0000, 0x7018, 0xd084, 0x1904, 0x530e, 0x2001, 0xffff, 0x2071,
+ 0xafda, 0x703a, 0x2071, 0xaf34, 0x7000, 0xa086, 0x0002, 0x1150,
+ 0x080c, 0x5426, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080,
+ 0x0804, 0x530e, 0x080c, 0x5450, 0x2071, 0x0000, 0x701b, 0x0001,
+ 0x2091, 0x4080, 0x0804, 0x530e, 0x2071, 0xaf34, 0x7000, 0xa005,
+ 0x0904, 0x5334, 0x6934, 0xa186, 0x0103, 0x1904, 0x5311, 0x684c,
+ 0xd0bc, 0x1904, 0x5334, 0x6948, 0x6844, 0xa105, 0x1904, 0x5329,
+ 0x2009, 0x8020, 0x2071, 0xaf34, 0x7000, 0x0002, 0x5334, 0x52f4,
+ 0x52cc, 0x52de, 0x52ab, 0x0136, 0x0146, 0x0156, 0x2099, 0xad75,
+ 0x20a1, 0xaf85, 0x20a9, 0x0004, 0x53a3, 0x015e, 0x014e, 0x013e,
+ 0x2071, 0xaf7c, 0xad80, 0x000f, 0x700e, 0x7013, 0x0002, 0x7007,
+ 0x0002, 0x700b, 0x0000, 0x2e10, 0x080c, 0x1624, 0x2071, 0xae13,
+ 0x7007, 0x0009, 0x0804, 0x5352, 0x7084, 0x8008, 0xa092, 0x001e,
+ 0x1a04, 0x5352, 0xae90, 0x0003, 0xa210, 0x683c, 0x2012, 0x7186,
+ 0x2071, 0xae13, 0x080c, 0x54a7, 0x0804, 0x5352, 0x7084, 0x8008,
+ 0xa092, 0x000f, 0x1a04, 0x5352, 0xae90, 0x0003, 0x8003, 0xa210,
+ 0x683c, 0x2012, 0x8210, 0x6840, 0x2012, 0x7186, 0x2071, 0xae13,
+ 0x080c, 0x54a7, 0x0804, 0x5352, 0x0126, 0x2091, 0x8000, 0x0e04,
+ 0x530e, 0x2071, 0x0000, 0x7018, 0xd084, 0x1180, 0x7122, 0x683c,
+ 0x7026, 0x6840, 0x702a, 0x701b, 0x0001, 0x2091, 0x4080, 0x012e,
+ 0x2071, 0xae13, 0x080c, 0x54a7, 0x0804, 0x5352, 0x012e, 0x0804,
+ 0x5352, 0xa18c, 0x00ff, 0xa186, 0x0017, 0x0130, 0xa186, 0x001e,
+ 0x0118, 0xa18e, 0x001f, 0x11c0, 0x684c, 0xd0cc, 0x01a8, 0x6850,
+ 0xa084, 0x00ff, 0xa086, 0x0001, 0x1178, 0x2009, 0x8021, 0x0804,
+ 0x52a2, 0x6844, 0xa086, 0x0100, 0x1138, 0x6868, 0xa005, 0x1120,
+ 0x2009, 0x8020, 0x0804, 0x52a2, 0x2071, 0xae13, 0x080c, 0x54b9,
+ 0x01c8, 0x2071, 0xae13, 0x700f, 0x0001, 0x6934, 0xa184, 0x00ff,
+ 0xa086, 0x0003, 0x1130, 0x810f, 0xa18c, 0x00ff, 0x8101, 0x0108,
+ 0x710e, 0x7007, 0x0003, 0x080c, 0x54d2, 0x7050, 0xa086, 0x0100,
+ 0x0904, 0x5412, 0x0126, 0x2091, 0x8000, 0x2071, 0xae13, 0x7008,
+ 0xa086, 0x0001, 0x1180, 0x0e04, 0x536b, 0x2009, 0x000d, 0x7030,
+ 0x200a, 0x2091, 0x4080, 0x700b, 0x0000, 0x7004, 0xa086, 0x0006,
+ 0x1110, 0x7007, 0x0001, 0x012e, 0x0005, 0x2071, 0xae13, 0x080c,
+ 0x54b9, 0x0518, 0x2071, 0xaf34, 0x7084, 0x700a, 0x20a9, 0x0020,
+ 0x2099, 0xaf35, 0x20a1, 0xaf5c, 0x53a3, 0x7087, 0x0000, 0x2071,
+ 0xae13, 0x2069, 0xaf7c, 0x706c, 0x6826, 0x7070, 0x682a, 0x7074,
+ 0x682e, 0x7078, 0x6832, 0x2d10, 0x080c, 0x1624, 0x7007, 0x0008,
+ 0x2001, 0xffff, 0x2071, 0xafda, 0x703a, 0x012e, 0x0804, 0x5352,
+ 0x2069, 0xaf7c, 0x6808, 0xa08e, 0x0000, 0x0904, 0x53ed, 0xa08e,
+ 0x0200, 0x0904, 0x53eb, 0xa08e, 0x0100, 0x1904, 0x53ed, 0x0126,
+ 0x2091, 0x8000, 0x0e04, 0x53e9, 0x2069, 0x0000, 0x6818, 0xd084,
+ 0x15c0, 0x702c, 0x7130, 0x8108, 0xa102, 0x0230, 0xa00e, 0x7034,
+ 0x706e, 0x7038, 0x7072, 0x0048, 0x706c, 0xa080, 0x0040, 0x706e,
+ 0x1220, 0x7070, 0xa081, 0x0000, 0x7072, 0x7132, 0x6936, 0x700b,
+ 0x0000, 0x2001, 0xaf59, 0x2004, 0xa005, 0x1190, 0x6934, 0x2069,
+ 0xaf34, 0x689c, 0x699e, 0x2069, 0xafda, 0xa102, 0x1118, 0x683c,
+ 0xa005, 0x1368, 0x2001, 0xaf5a, 0x200c, 0x810d, 0x693e, 0x0038,
+ 0x2009, 0x8040, 0x6922, 0x681b, 0x0001, 0x2091, 0x4080, 0x7007,
+ 0x0001, 0x012e, 0x0010, 0x7007, 0x0005, 0x0005, 0x2001, 0xaf7e,
+ 0x2004, 0xa08e, 0x0100, 0x1128, 0x7007, 0x0001, 0x080c, 0x54a7,
+ 0x0005, 0xa08e, 0x0000, 0x0de0, 0xa08e, 0x0200, 0x1dc8, 0x7007,
+ 0x0005, 0x0005, 0x701c, 0xa06d, 0x0158, 0x080c, 0x54b9, 0x0140,
+ 0x7007, 0x0003, 0x080c, 0x54d2, 0x7050, 0xa086, 0x0100, 0x0110,
+ 0x0005, 0x0005, 0x7050, 0xa09e, 0x0100, 0x1118, 0x7007, 0x0004,
+ 0x0030, 0xa086, 0x0200, 0x1110, 0x7007, 0x0005, 0x0005, 0x080c,
+ 0x5475, 0x7006, 0x080c, 0x54a7, 0x0005, 0x0005, 0x00e6, 0x0156,
+ 0x2071, 0xaf34, 0x7184, 0x81ff, 0x0500, 0xa006, 0x7086, 0xae80,
+ 0x0003, 0x2071, 0x0000, 0x21a8, 0x2014, 0x7226, 0x8000, 0x0f04,
+ 0x544a, 0x2014, 0x722a, 0x8000, 0x0f04, 0x544a, 0x2014, 0x722e,
+ 0x8000, 0x0f04, 0x544a, 0x2014, 0x723a, 0x8000, 0x0f04, 0x544a,
+ 0x2014, 0x723e, 0xa180, 0x8030, 0x7022, 0x015e, 0x00ee, 0x0005,
+ 0x00e6, 0x0156, 0x2071, 0xaf34, 0x7184, 0x81ff, 0x01d8, 0xa006,
+ 0x7086, 0xae80, 0x0003, 0x2071, 0x0000, 0x21a8, 0x2014, 0x7226,
+ 0x8000, 0x2014, 0x722a, 0x8000, 0x0f04, 0x546c, 0x2014, 0x723a,
+ 0x8000, 0x2014, 0x723e, 0x0018, 0x2001, 0x8020, 0x0010, 0x2001,
+ 0x8042, 0x7022, 0x015e, 0x00ee, 0x0005, 0x702c, 0x7130, 0x8108,
+ 0xa102, 0x0230, 0xa00e, 0x7034, 0x706e, 0x7038, 0x7072, 0x0048,
+ 0x706c, 0xa080, 0x0040, 0x706e, 0x1220, 0x7070, 0xa081, 0x0000,
+ 0x7072, 0x7132, 0x700c, 0x8001, 0x700e, 0x1180, 0x0126, 0x2091,
+ 0x8000, 0x0e04, 0x54a1, 0x2001, 0x000d, 0x2102, 0x2091, 0x4080,
+ 0x2001, 0x0001, 0x700b, 0x0000, 0x012e, 0x0005, 0x2001, 0x0007,
+ 0x0005, 0x2001, 0x0006, 0x700b, 0x0001, 0x012e, 0x0005, 0x701c,
+ 0xa06d, 0x0170, 0x0126, 0x2091, 0x8000, 0x7010, 0x8001, 0x7012,
+ 0x2d04, 0x701e, 0xa005, 0x1108, 0x701a, 0x012e, 0x080c, 0x15f0,
+ 0x0005, 0x2019, 0x000d, 0x2304, 0x230c, 0xa10e, 0x0130, 0x2304,
+ 0x230c, 0xa10e, 0x0110, 0xa006, 0x0060, 0x732c, 0x8319, 0x7130,
+ 0xa102, 0x1118, 0x2300, 0xa005, 0x0020, 0x0210, 0xa302, 0x0008,
+ 0x8002, 0x0005, 0x2d00, 0x7026, 0xa080, 0x000d, 0x7056, 0x7053,
+ 0x0000, 0x0126, 0x2091, 0x8000, 0x2009, 0xafec, 0x2104, 0xc08d,
+ 0x200a, 0x012e, 0x080c, 0x163c, 0x0005, 0x7088, 0xa08a, 0x0029,
+ 0x1220, 0xa082, 0x001d, 0x0033, 0x0010, 0x080c, 0x14f6, 0x6027,
+ 0x1e00, 0x0005, 0x55c1, 0x555b, 0x5571, 0x5595, 0x55b4, 0x55e6,
+ 0x55f8, 0x5571, 0x55d2, 0x54ff, 0x552d, 0x54fe, 0x0005, 0x00d6,
+ 0x2069, 0x0200, 0x6804, 0xa005, 0x1180, 0x6808, 0xa005, 0x1518,
+ 0x708b, 0x0028, 0x2069, 0xafac, 0x2d04, 0x7002, 0x080c, 0x584d,
+ 0x6028, 0xa085, 0x0600, 0x602a, 0x00b0, 0x708b, 0x0028, 0x2069,
+ 0xafac, 0x2d04, 0x7002, 0x6028, 0xa085, 0x0600, 0x602a, 0x00e6,
+ 0x0036, 0x0046, 0x0056, 0x2071, 0xaffd, 0x080c, 0x1d22, 0x005e,
+ 0x004e, 0x003e, 0x00ee, 0x00de, 0x0005, 0x00d6, 0x2069, 0x0200,
+ 0x6804, 0xa005, 0x1180, 0x6808, 0xa005, 0x1518, 0x708b, 0x0028,
+ 0x2069, 0xafac, 0x2d04, 0x7002, 0x080c, 0x58da, 0x6028, 0xa085,
+ 0x0600, 0x602a, 0x00b0, 0x708b, 0x0028, 0x2069, 0xafac, 0x2d04,
+ 0x7002, 0x6028, 0xa085, 0x0600, 0x602a, 0x00e6, 0x0036, 0x0046,
+ 0x0056, 0x2071, 0xaffd, 0x080c, 0x1d22, 0x005e, 0x004e, 0x003e,
+ 0x00ee, 0x00de, 0x0005, 0x6803, 0x0090, 0x6124, 0xd1e4, 0x1180,
+ 0x080c, 0x5663, 0xd1d4, 0x1150, 0xd1dc, 0x1128, 0xd1cc, 0x0140,
+ 0x708b, 0x0020, 0x0028, 0x708b, 0x001d, 0x0010, 0x708b, 0x001f,
+ 0x0005, 0x6803, 0x0088, 0x6124, 0xd1cc, 0x11c8, 0xd1dc, 0x11a0,
+ 0xd1e4, 0x1178, 0xa184, 0x1e00, 0x11b8, 0x60e3, 0x0001, 0x600c,
+ 0xc0b4, 0x600e, 0x080c, 0x577f, 0x6803, 0x0080, 0x708b, 0x0028,
+ 0x0058, 0x708b, 0x001e, 0x0040, 0x708b, 0x001d, 0x0028, 0x708b,
+ 0x0020, 0x0010, 0x708b, 0x001f, 0x0005, 0x60e3, 0x0001, 0x600c,
+ 0xc0b4, 0x600e, 0x080c, 0x577f, 0x6803, 0x0080, 0x6124, 0xd1d4,
+ 0x1180, 0xd1dc, 0x1158, 0xd1e4, 0x1130, 0xa184, 0x1e00, 0x1158,
+ 0x708b, 0x0028, 0x0040, 0x708b, 0x001e, 0x0028, 0x708b, 0x001d,
+ 0x0010, 0x708b, 0x001f, 0x0005, 0x6803, 0x00a0, 0x6124, 0xd1dc,
+ 0x1128, 0xd1e4, 0x0128, 0x708b, 0x001e, 0x0010, 0x708b, 0x001d,
+ 0x0005, 0x080c, 0x568d, 0x6124, 0xd1dc, 0x1158, 0x080c, 0x5663,
+ 0xd1d4, 0x1128, 0xd1e4, 0x0128, 0x708b, 0x001e, 0x0010, 0x708b,
+ 0x001f, 0x0005, 0x6803, 0x00a0, 0x6124, 0xd1d4, 0x1160, 0xd1cc,
+ 0x1150, 0xd1dc, 0x1128, 0xd1e4, 0x0140, 0x708b, 0x001e, 0x0028,
+ 0x708b, 0x001d, 0x0010, 0x708b, 0x0021, 0x0005, 0x080c, 0x568d,
+ 0x6124, 0xd1d4, 0x1150, 0xd1dc, 0x1128, 0xd1e4, 0x0140, 0x708b,
+ 0x001e, 0x0028, 0x708b, 0x001d, 0x0010, 0x708b, 0x001f, 0x0005,
+ 0x6803, 0x0090, 0x6124, 0xd1d4, 0x1178, 0xd1cc, 0x1150, 0xd1dc,
+ 0x1128, 0xd1e4, 0x0158, 0x708b, 0x001e, 0x0040, 0x708b, 0x001d,
+ 0x0028, 0x708b, 0x0020, 0x0010, 0x708b, 0x001f, 0x0005, 0x0016,
+ 0x00c6, 0x00d6, 0x00e6, 0x0126, 0x2061, 0x0100, 0x2069, 0x0140,
+ 0x2071, 0xad00, 0x2091, 0x8000, 0x080c, 0x574f, 0x11e8, 0x2001,
+ 0xad0c, 0x200c, 0xd1b4, 0x01c0, 0xc1b4, 0x2102, 0x6027, 0x0200,
+ 0xe000, 0xe000, 0x6024, 0xd0cc, 0x0158, 0x6803, 0x00a0, 0x2001,
+ 0xaf9e, 0x2003, 0x0001, 0x2001, 0xad00, 0x2003, 0x0001, 0x0428,
+ 0x6028, 0xc0cd, 0x602a, 0x0408, 0x080c, 0x576b, 0x0150, 0x080c,
+ 0x5761, 0x1138, 0x2001, 0x0001, 0x080c, 0x261e, 0x080c, 0x5726,
+ 0x00a0, 0x080c, 0x568a, 0x0178, 0x2001, 0x0001, 0x080c, 0x261e,
+ 0x7088, 0xa086, 0x001e, 0x0120, 0x7088, 0xa086, 0x0022, 0x1118,
+ 0x708b, 0x0025, 0x0010, 0x708b, 0x0021, 0x012e, 0x00ee, 0x00de,
+ 0x00ce, 0x001e, 0x0005, 0x0016, 0x0026, 0x2009, 0x0064, 0x2011,
+ 0x566e, 0x080c, 0x6501, 0x002e, 0x001e, 0x0005, 0x00e6, 0x00f6,
+ 0x0016, 0x080c, 0x7834, 0x2071, 0xad00, 0x080c, 0x560f, 0x001e,
+ 0x00fe, 0x00ee, 0x0005, 0x2001, 0xad00, 0x2004, 0xa086, 0x0004,
+ 0x0140, 0x2001, 0xaf9d, 0x2003, 0xaaaa, 0x2001, 0xaf9e, 0x2003,
+ 0x0000, 0x0005, 0x6020, 0xd09c, 0x0005, 0x6803, 0x00c0, 0x0156,
+ 0x20a9, 0x002d, 0x1d04, 0x5692, 0x2091, 0x6000, 0x1f04, 0x5692,
+ 0x015e, 0x0005, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x2071, 0xad00, 0x2001, 0xaf9e, 0x200c, 0xa186, 0x0000,
+ 0x0158, 0xa186, 0x0001, 0x0158, 0xa186, 0x0002, 0x0158, 0xa186,
+ 0x0003, 0x0158, 0x0804, 0x5714, 0x708b, 0x0022, 0x0040, 0x708b,
+ 0x0021, 0x0028, 0x708b, 0x0023, 0x0020, 0x708b, 0x0024, 0x6043,
+ 0x0000, 0x60e3, 0x0000, 0x6887, 0x0001, 0x2001, 0x0001, 0x080c,
+ 0x26cb, 0x0026, 0x2011, 0x0003, 0x080c, 0x7adf, 0x2011, 0x0002,
+ 0x080c, 0x7ae9, 0x002e, 0x7000, 0xa08e, 0x0004, 0x0118, 0x602b,
+ 0x0028, 0x0010, 0x602b, 0x0020, 0x0156, 0x0126, 0x2091, 0x8000,
+ 0x20a9, 0x0005, 0x6024, 0xd0ac, 0x0118, 0x012e, 0x015e, 0x04d0,
+ 0x6800, 0xa084, 0x00a0, 0xc0bd, 0x6802, 0x6904, 0xd1d4, 0x1130,
+ 0x6803, 0x0100, 0x1f04, 0x56e2, 0x080c, 0x57a0, 0x012e, 0x015e,
+ 0x080c, 0x5761, 0x01a8, 0x6044, 0xa005, 0x0168, 0x6050, 0x0006,
+ 0xa085, 0x0020, 0x6052, 0x080c, 0x57a0, 0xa006, 0x8001, 0x1df0,
+ 0x000e, 0x6052, 0x0028, 0x6804, 0xd0d4, 0x1110, 0x080c, 0x57a0,
+ 0x2001, 0xaf9e, 0x2003, 0x0004, 0x080c, 0x54e5, 0x080c, 0x5761,
+ 0x0148, 0x6804, 0xd0d4, 0x1130, 0xd0dc, 0x1100, 0x2001, 0xaf9e,
+ 0x2003, 0x0000, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0xad00, 0x2001,
+ 0xaf9d, 0x2003, 0x0000, 0x2001, 0xaf8e, 0x2003, 0x0000, 0x708b,
+ 0x0000, 0x60e3, 0x0000, 0x6887, 0x0000, 0x2001, 0x0000, 0x080c,
+ 0x26cb, 0x6803, 0x0000, 0x6043, 0x0090, 0x6043, 0x0010, 0x6027,
+ 0xffff, 0x602b, 0x182f, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0006,
+ 0x2001, 0xaf9d, 0x2004, 0xa086, 0xaaaa, 0x000e, 0x0005, 0x0006,
+ 0x2001, 0xad71, 0x2004, 0xa084, 0x0030, 0xa086, 0x0000, 0x000e,
+ 0x0005, 0x0006, 0x2001, 0xad71, 0x2004, 0xa084, 0x0030, 0xa086,
+ 0x0030, 0x000e, 0x0005, 0x0006, 0x2001, 0xad71, 0x2004, 0xa084,
+ 0x0030, 0xa086, 0x0010, 0x000e, 0x0005, 0x0006, 0x2001, 0xad71,
+ 0x2004, 0xa084, 0x0030, 0xa086, 0x0020, 0x000e, 0x0005, 0x2001,
+ 0xad0c, 0x2004, 0xd0a4, 0x0170, 0x080c, 0x26eb, 0x0036, 0x0016,
+ 0x2009, 0x0000, 0x2019, 0x0028, 0x080c, 0x2aac, 0x001e, 0x003e,
+ 0xa006, 0x0009, 0x0005, 0x00e6, 0x2071, 0xad0c, 0x2e04, 0x0118,
+ 0xa085, 0x0010, 0x0010, 0xa084, 0xffef, 0x2072, 0x00ee, 0x0005,
+ 0x6050, 0x0006, 0x60f0, 0x0006, 0x60ec, 0x0006, 0x600c, 0x0006,
+ 0x6004, 0x0006, 0x6028, 0x0006, 0x602f, 0x0100, 0x602f, 0x0000,
+ 0x602f, 0x0040, 0x602f, 0x0000, 0x000e, 0x602a, 0x000e, 0x6006,
+ 0x000e, 0x600e, 0x000e, 0x60ee, 0x000e, 0x60f2, 0x60e3, 0x0000,
+ 0x6887, 0x0001, 0x2001, 0x0001, 0x080c, 0x26cb, 0x6800, 0xa084,
+ 0x00a0, 0xc0bd, 0x6802, 0x6803, 0x00a0, 0x000e, 0x6052, 0x6050,
+ 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6, 0x00e6,
+ 0x2061, 0x0100, 0x2069, 0x0140, 0x2071, 0xad00, 0x6020, 0xa084,
+ 0x0080, 0x0138, 0x2001, 0xad0c, 0x200c, 0xc1bd, 0x2102, 0x0804,
+ 0x5845, 0x2001, 0xad0c, 0x200c, 0xc1bc, 0x2102, 0x6028, 0xa084,
+ 0xe1ff, 0x602a, 0x6027, 0x0200, 0x6803, 0x0090, 0x20a9, 0x0384,
+ 0x6024, 0xd0cc, 0x1518, 0x1d04, 0x57f8, 0x2091, 0x6000, 0x1f04,
+ 0x57f8, 0x2011, 0x0003, 0x080c, 0x7adf, 0x2011, 0x0002, 0x080c,
+ 0x7ae9, 0x080c, 0x79e1, 0x080c, 0x6581, 0x2019, 0x0000, 0x080c,
+ 0x7a64, 0x6803, 0x00a0, 0x2001, 0xaf9e, 0x2003, 0x0001, 0x2001,
+ 0xad00, 0x2003, 0x0001, 0xa085, 0x0001, 0x0438, 0x60e3, 0x0000,
+ 0x2001, 0xaf8e, 0x2004, 0x080c, 0x26cb, 0x60e2, 0x6803, 0x0080,
+ 0x20a9, 0x0384, 0x6027, 0x1e00, 0x2009, 0x1e00, 0xe000, 0x6024,
+ 0xa10c, 0x0138, 0x1d04, 0x582a, 0x2091, 0x6000, 0x1f04, 0x582a,
+ 0x0840, 0x6028, 0xa085, 0x1e00, 0x602a, 0x70a0, 0xa005, 0x1118,
+ 0x6887, 0x0001, 0x0008, 0x6886, 0xa006, 0x00ee, 0x00de, 0x00ce,
+ 0x003e, 0x002e, 0x001e, 0x015e, 0x0005, 0x0156, 0x0016, 0x0026,
+ 0x0036, 0x00c6, 0x00d6, 0x00e6, 0x2061, 0x0100, 0x2071, 0xad00,
+ 0x2069, 0x0140, 0x6020, 0xa084, 0x00c0, 0x0120, 0x6884, 0xa005,
+ 0x1904, 0x58a1, 0x6803, 0x0088, 0x60e3, 0x0000, 0x6887, 0x0000,
+ 0x2001, 0x0000, 0x080c, 0x26cb, 0x2069, 0x0200, 0x6804, 0xa005,
+ 0x1118, 0x6808, 0xa005, 0x01c0, 0x6028, 0xa084, 0xfbff, 0x602a,
+ 0x6027, 0x0400, 0x2069, 0xafac, 0x7000, 0x206a, 0x708b, 0x0026,
+ 0x7003, 0x0001, 0x20a9, 0x0002, 0x1d04, 0x5884, 0x2091, 0x6000,
+ 0x1f04, 0x5884, 0x0804, 0x58d2, 0x2069, 0x0140, 0x20a9, 0x0384,
+ 0x6027, 0x1e00, 0x2009, 0x1e00, 0xe000, 0x6024, 0xa10c, 0x0530,
+ 0xa084, 0x1a00, 0x1518, 0x1d04, 0x5890, 0x2091, 0x6000, 0x1f04,
+ 0x5890, 0x2011, 0x0003, 0x080c, 0x7adf, 0x2011, 0x0002, 0x080c,
+ 0x7ae9, 0x080c, 0x79e1, 0x080c, 0x6581, 0x2019, 0x0000, 0x080c,
+ 0x7a64, 0x6803, 0x00a0, 0x2001, 0xaf9e, 0x2003, 0x0001, 0x2001,
+ 0xad00, 0x2003, 0x0001, 0xa085, 0x0001, 0x00a0, 0x6803, 0x0080,
+ 0x2069, 0x0140, 0x60e3, 0x0000, 0x70a0, 0xa005, 0x1118, 0x6887,
+ 0x0001, 0x0008, 0x6886, 0x2001, 0xaf8e, 0x2004, 0x080c, 0x26cb,
+ 0x60e2, 0xa006, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x00c6, 0x00d6,
+ 0x00e6, 0x2061, 0x0100, 0x2071, 0xad00, 0x6020, 0xa084, 0x00c0,
+ 0x01f0, 0x2011, 0x0003, 0x080c, 0x7adf, 0x2011, 0x0002, 0x080c,
+ 0x7ae9, 0x080c, 0x79e1, 0x080c, 0x6581, 0x2019, 0x0000, 0x080c,
+ 0x7a64, 0x2069, 0x0140, 0x6803, 0x00a0, 0x2001, 0xaf9e, 0x2003,
+ 0x0001, 0x2001, 0xad00, 0x2003, 0x0001, 0x0804, 0x5972, 0x2001,
+ 0xad0c, 0x200c, 0xd1b4, 0x1150, 0xc1b5, 0x2102, 0x080c, 0x5663,
+ 0x2069, 0x0140, 0x6803, 0x0080, 0x60e3, 0x0000, 0x2069, 0x0200,
+ 0x6804, 0xa005, 0x1118, 0x6808, 0xa005, 0x01b8, 0x6028, 0xa084,
+ 0xfdff, 0x602a, 0x6027, 0x0200, 0x2069, 0xafac, 0x7000, 0x206a,
+ 0x708b, 0x0027, 0x7003, 0x0001, 0x20a9, 0x0002, 0x1d04, 0x592e,
+ 0x2091, 0x6000, 0x1f04, 0x592e, 0x04e8, 0x6027, 0x1e00, 0x2009,
+ 0x1e00, 0xe000, 0x6024, 0xa10c, 0x01c8, 0xa084, 0x1c00, 0x11b0,
+ 0x1d04, 0x5935, 0x0006, 0x0016, 0x00c6, 0x00d6, 0x00e6, 0x080c,
+ 0x64a2, 0x00ee, 0x00de, 0x00ce, 0x001e, 0x000e, 0x00e6, 0x2071,
+ 0xafda, 0x7018, 0x00ee, 0xa005, 0x1d00, 0x01e0, 0x0026, 0x2011,
+ 0x566e, 0x080c, 0x650d, 0x002e, 0x2069, 0x0140, 0x60e3, 0x0000,
+ 0x70a0, 0xa005, 0x1118, 0x6887, 0x0001, 0x0008, 0x6886, 0x2001,
+ 0xaf8e, 0x2004, 0x080c, 0x26cb, 0x60e2, 0x2001, 0xad0c, 0x200c,
+ 0xc1b4, 0x2102, 0x00ee, 0x00de, 0x00ce, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0x0005, 0x0156, 0x0016, 0x0026, 0x0036, 0x0046, 0x00c6,
+ 0x00e6, 0x2061, 0x0100, 0x2071, 0xad00, 0x7130, 0xd184, 0x1180,
+ 0x2011, 0xad52, 0x2214, 0xd2ec, 0x0138, 0xc18d, 0x7132, 0x2011,
+ 0xad52, 0x2214, 0xd2ac, 0x1120, 0x7030, 0xd08c, 0x0904, 0x59df,
+ 0x7130, 0xc185, 0x7132, 0x2011, 0xad52, 0x220c, 0xd1a4, 0x0530,
+ 0x0016, 0x2009, 0x0001, 0x2011, 0x0100, 0x080c, 0x663f, 0x2019,
+ 0x000e, 0x080c, 0xa8eb, 0x0156, 0x20a9, 0x007f, 0x2009, 0x0000,
+ 0xa186, 0x007e, 0x0170, 0xa186, 0x0080, 0x0158, 0x080c, 0x4cdc,
+ 0x1140, 0x8127, 0xa006, 0x0016, 0x2009, 0x000e, 0x080c, 0xa96c,
+ 0x001e, 0x8108, 0x1f04, 0x59b0, 0x015e, 0x001e, 0xd1ac, 0x1148,
+ 0x0016, 0x2009, 0x0000, 0x2019, 0x0004, 0x080c, 0x2aac, 0x001e,
+ 0x0070, 0x0156, 0x20a9, 0x007f, 0x2009, 0x0000, 0x080c, 0x4cdc,
+ 0x1110, 0x080c, 0x493a, 0x8108, 0x1f04, 0x59d6, 0x015e, 0x2011,
+ 0x0003, 0x080c, 0x7adf, 0x2011, 0x0002, 0x080c, 0x7ae9, 0x080c,
+ 0x79e1, 0x080c, 0x6581, 0x0036, 0x2019, 0x0000, 0x080c, 0x7a64,
+ 0x003e, 0x60e3, 0x0000, 0x2001, 0xad00, 0x2003, 0x0001, 0x080c,
+ 0x569a, 0x00ee, 0x00ce, 0x004e, 0x003e, 0x002e, 0x001e, 0x015e,
+ 0x0005, 0x2071, 0xade1, 0x7003, 0x0000, 0x7007, 0x0000, 0x700f,
+ 0x0000, 0x702b, 0x0001, 0x704f, 0x0000, 0x7053, 0x0001, 0x705f,
+ 0x0020, 0x7063, 0x0040, 0x7083, 0x0000, 0x708b, 0x0000, 0x708f,
+ 0x0001, 0x70bf, 0x0000, 0x0005, 0x00e6, 0x2071, 0xade1, 0x6848,
+ 0xa005, 0x1130, 0x7028, 0xc085, 0x702a, 0xa085, 0x0001, 0x0428,
+ 0x6a50, 0x7236, 0x6b54, 0x733a, 0x6858, 0x703e, 0x707a, 0x685c,
+ 0x7042, 0x707e, 0x6848, 0x702e, 0x6840, 0x7032, 0x2009, 0x000c,
+ 0x200a, 0x8007, 0x8006, 0x8006, 0xa08c, 0x003f, 0xa084, 0xffc0,
+ 0xa210, 0x2100, 0xa319, 0x7272, 0x7376, 0x7028, 0xc084, 0x702a,
+ 0x7007, 0x0001, 0x700f, 0x0000, 0xa006, 0x00ee, 0x0005, 0x2b78,
+ 0x2071, 0xade1, 0x7004, 0x0043, 0x700c, 0x0002, 0x5a5b, 0x5a52,
+ 0x5a52, 0x5a52, 0x5a52, 0x0005, 0x5ab1, 0x5ab2, 0x5ae4, 0x5ae5,
+ 0x5aaf, 0x5b33, 0x5b38, 0x5b69, 0x5b6a, 0x5b85, 0x5b86, 0x5b87,
+ 0x5b88, 0x5b89, 0x5b8a, 0x5c40, 0x5c67, 0x700c, 0x0002, 0x5a74,
+ 0x5aaf, 0x5aaf, 0x5ab0, 0x5ab0, 0x7830, 0x7930, 0xa106, 0x0120,
+ 0x7830, 0x7930, 0xa106, 0x1510, 0x7030, 0xa10a, 0x01f8, 0x1210,
+ 0x712c, 0xa10a, 0xa18a, 0x0002, 0x12d0, 0x080c, 0x15c0, 0x01b0,
+ 0x2d00, 0x705a, 0x7063, 0x0040, 0x2001, 0x0003, 0x7057, 0x0000,
+ 0x0126, 0x0006, 0x2091, 0x8000, 0x2009, 0xafec, 0x2104, 0xc085,
+ 0x200a, 0x000e, 0x700e, 0x012e, 0x080c, 0x163c, 0x0005, 0x080c,
+ 0x15c0, 0x0de0, 0x2d00, 0x705a, 0x080c, 0x15c0, 0x1108, 0x0c10,
+ 0x2d00, 0x7086, 0x7063, 0x0080, 0x2001, 0x0004, 0x08f8, 0x0005,
+ 0x0005, 0x0005, 0x700c, 0x0002, 0x5ab9, 0x5abc, 0x5aca, 0x5ae3,
+ 0x5ae3, 0x080c, 0x5a6d, 0x0005, 0x0126, 0x8001, 0x700e, 0x7058,
+ 0x0006, 0x080c, 0x5f90, 0x0120, 0x2091, 0x8000, 0x080c, 0x5a6d,
+ 0x00de, 0x0048, 0x0126, 0x8001, 0x700e, 0x080c, 0x5f90, 0x7058,
+ 0x2068, 0x7084, 0x705a, 0x6803, 0x0000, 0x6807, 0x0000, 0x6834,
+ 0xa084, 0x00ff, 0xa08a, 0x003a, 0x1218, 0x00db, 0x012e, 0x0005,
+ 0x012e, 0x080c, 0x5b8b, 0x0005, 0x0005, 0x0005, 0x00e6, 0x2071,
+ 0xade1, 0x700c, 0x0002, 0x5af0, 0x5af0, 0x5af0, 0x5af2, 0x5af5,
+ 0x00ee, 0x0005, 0x700f, 0x0001, 0x0010, 0x700f, 0x0002, 0x00ee,
+ 0x0005, 0x5b8b, 0x5b8b, 0x5ba7, 0x5b8b, 0x5d22, 0x5b8b, 0x5b8b,
+ 0x5b8b, 0x5b8b, 0x5b8b, 0x5ba7, 0x5d64, 0x5da7, 0x5df0, 0x5e04,
+ 0x5b8b, 0x5b8b, 0x5bc3, 0x5ba7, 0x5b8b, 0x5b8b, 0x5c1d, 0x5ead,
+ 0x5ec8, 0x5b8b, 0x5bc3, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5c13,
+ 0x5ec8, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b,
+ 0x5b8b, 0x5b8b, 0x5bd7, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b,
+ 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b, 0x5b8b,
+ 0x5b8b, 0x5b8b, 0x5bec, 0x7020, 0x2068, 0x080c, 0x15f0, 0x0005,
+ 0x700c, 0x0002, 0x5b3f, 0x5b42, 0x5b50, 0x5b68, 0x5b68, 0x080c,
+ 0x5a6d, 0x0005, 0x0126, 0x8001, 0x700e, 0x7058, 0x0006, 0x080c,
+ 0x5f90, 0x0120, 0x2091, 0x8000, 0x080c, 0x5a6d, 0x00de, 0x0048,
+ 0x0126, 0x8001, 0x700e, 0x080c, 0x5f90, 0x7058, 0x2068, 0x7084,
+ 0x705a, 0x6803, 0x0000, 0x6807, 0x0000, 0x6834, 0xa084, 0x00ff,
+ 0xa08a, 0x001a, 0x1218, 0x003b, 0x012e, 0x0005, 0x012e, 0x0419,
+ 0x0005, 0x0005, 0x0005, 0x5b8b, 0x5ba7, 0x5d0e, 0x5b8b, 0x5ba7,
+ 0x5b8b, 0x5ba7, 0x5ba7, 0x5b8b, 0x5ba7, 0x5d0e, 0x5ba7, 0x5ba7,
+ 0x5ba7, 0x5ba7, 0x5ba7, 0x5b8b, 0x5ba7, 0x5d0e, 0x5b8b, 0x5b8b,
+ 0x5ba7, 0x5b8b, 0x5b8b, 0x5b8b, 0x5ba7, 0x0005, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x0005, 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff,
+ 0xc0d5, 0x683a, 0x0126, 0x2091, 0x8000, 0x080c, 0x510c, 0x012e,
+ 0x0005, 0x7007, 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0e5, 0x683a,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x510c, 0x012e, 0x0005, 0x7007,
+ 0x0001, 0x6838, 0xa084, 0x00ff, 0xc0ed, 0x683a, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x510c, 0x012e, 0x0005, 0x7007, 0x0001, 0x6838,
+ 0xa084, 0x00ff, 0xc0dd, 0x683a, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x510c, 0x012e, 0x0005, 0x6834, 0x8007, 0xa084, 0x00ff, 0x0988,
+ 0x8001, 0x1120, 0x7007, 0x0001, 0x0804, 0x5cd0, 0x7007, 0x0006,
+ 0x7012, 0x2d00, 0x7016, 0x701a, 0x704b, 0x5cd0, 0x0005, 0x6834,
+ 0x8007, 0xa084, 0x00ff, 0x0904, 0x5b99, 0x8001, 0x1120, 0x7007,
+ 0x0001, 0x0804, 0x5ced, 0x7007, 0x0006, 0x7012, 0x2d00, 0x7016,
+ 0x701a, 0x704b, 0x5ced, 0x0005, 0x6834, 0x8007, 0xa084, 0x00ff,
+ 0xa086, 0x0001, 0x1904, 0x5b99, 0x7007, 0x0001, 0x2009, 0xad30,
+ 0x210c, 0x81ff, 0x11a8, 0x6838, 0xa084, 0x00ff, 0x683a, 0x6853,
+ 0x0000, 0x080c, 0x4ab1, 0x1108, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x6837, 0x0139, 0x684a, 0x6952, 0x080c, 0x510c, 0x012e, 0x0ca0,
+ 0x2001, 0x0028, 0x0c90, 0x684c, 0xa084, 0x00c0, 0xa086, 0x00c0,
+ 0x1120, 0x7007, 0x0001, 0x0804, 0x5ee0, 0x2d00, 0x7016, 0x701a,
+ 0x20a9, 0x0004, 0xa080, 0x0024, 0x2098, 0x20a1, 0xae0c, 0x53a3,
+ 0x6858, 0x7012, 0xa082, 0x0401, 0x1a04, 0x5bb5, 0x6a84, 0xa28a,
+ 0x0002, 0x1a04, 0x5bb5, 0x82ff, 0x1138, 0x6888, 0x698c, 0xa105,
+ 0x0118, 0x2001, 0x5ca3, 0x0018, 0xa280, 0x5c99, 0x2005, 0x70c6,
+ 0x7010, 0xa015, 0x0904, 0x5c85, 0x080c, 0x15c0, 0x1118, 0x7007,
+ 0x000f, 0x0005, 0x2d00, 0x7022, 0x70c4, 0x2060, 0x2c05, 0x6836,
+ 0xe004, 0xad00, 0x7096, 0xe008, 0xa20a, 0x1210, 0xa00e, 0x2200,
+ 0x7112, 0xe20c, 0x8003, 0x800b, 0xa296, 0x0004, 0x0108, 0xa108,
+ 0x719a, 0x810b, 0x719e, 0xae90, 0x0022, 0x080c, 0x1624, 0x7090,
+ 0xa08e, 0x0100, 0x0170, 0xa086, 0x0200, 0x0118, 0x7007, 0x0010,
+ 0x0005, 0x7020, 0x2068, 0x080c, 0x15f0, 0x7014, 0x2068, 0x0804,
+ 0x5bb5, 0x7020, 0x2068, 0x7018, 0x6802, 0x6807, 0x0000, 0x2d08,
+ 0x2068, 0x6906, 0x711a, 0x0804, 0x5c40, 0x7014, 0x2068, 0x7007,
+ 0x0001, 0x6884, 0xa005, 0x1128, 0x6888, 0x698c, 0xa105, 0x0108,
+ 0x00b1, 0x6834, 0xa084, 0x00ff, 0xa086, 0x001e, 0x0904, 0x5ee0,
+ 0x04b8, 0x5c9b, 0x5c9f, 0x0002, 0x0011, 0x0007, 0x0004, 0x000a,
+ 0x000f, 0x0005, 0x0006, 0x000a, 0x0011, 0x0005, 0x0004, 0x00f6,
+ 0x00e6, 0x00c6, 0x0076, 0x0066, 0x6f88, 0x6e8c, 0x6804, 0x2060,
+ 0xacf0, 0x0021, 0xacf8, 0x0027, 0x2009, 0x0005, 0x700c, 0x7816,
+ 0x7008, 0x7812, 0x7004, 0x7806, 0x7000, 0x7802, 0x7e0e, 0x7f0a,
+ 0x8109, 0x0128, 0xaef2, 0x0004, 0xaffa, 0x0006, 0x0c78, 0x6004,
+ 0xa065, 0x1d30, 0x006e, 0x007e, 0x00ce, 0x00ee, 0x00fe, 0x0005,
+ 0x2009, 0xad30, 0x210c, 0x81ff, 0x1198, 0x6838, 0xa084, 0x00ff,
+ 0x683a, 0x080c, 0x4993, 0x1108, 0x0005, 0x080c, 0x51df, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x97fd, 0x080c, 0x510c, 0x012e, 0x0ca0,
+ 0x2001, 0x0028, 0x2009, 0x0000, 0x0c80, 0x2009, 0xad30, 0x210c,
+ 0x81ff, 0x11b0, 0x6858, 0xa005, 0x01b0, 0x6838, 0xa084, 0x00ff,
+ 0x683a, 0x6853, 0x0000, 0x080c, 0x4a55, 0x1108, 0x0005, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x51df, 0x080c, 0x510c, 0x012e, 0x0cb0,
+ 0x2001, 0x0028, 0x0ca0, 0x2001, 0x0000, 0x0c88, 0x7018, 0x6802,
+ 0x2d08, 0x2068, 0x6906, 0x711a, 0x7010, 0x8001, 0x7012, 0x0118,
+ 0x7007, 0x0006, 0x0030, 0x7014, 0x2068, 0x7007, 0x0001, 0x7048,
+ 0x080f, 0x0005, 0x7007, 0x0001, 0x6944, 0x810f, 0xa18c, 0x00ff,
+ 0x6848, 0xa084, 0x00ff, 0x20a9, 0x0001, 0xa096, 0x0001, 0x01b0,
+ 0x2009, 0x0000, 0x20a9, 0x00ff, 0xa096, 0x0002, 0x0178, 0xa005,
+ 0x11f0, 0x6944, 0x810f, 0xa18c, 0x00ff, 0x080c, 0x4cdc, 0x11b8,
+ 0x0066, 0x6e50, 0x080c, 0x4dcf, 0x006e, 0x0088, 0x0046, 0x2011,
+ 0xad0c, 0x2224, 0xc484, 0x2412, 0x004e, 0x00c6, 0x080c, 0x4cdc,
+ 0x1110, 0x080c, 0x4f2d, 0x8108, 0x1f04, 0x5d4e, 0x00ce, 0x684c,
+ 0xd084, 0x1118, 0x080c, 0x15f0, 0x0005, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x510c, 0x012e, 0x0005, 0x0126, 0x2091, 0x8000, 0x7007,
+ 0x0001, 0x2001, 0xad52, 0x2004, 0xd0a4, 0x0580, 0x2061, 0xb048,
+ 0x6100, 0xd184, 0x0178, 0x6858, 0xa084, 0x00ff, 0x1550, 0x6000,
+ 0xd084, 0x0520, 0x6004, 0xa005, 0x1538, 0x6003, 0x0000, 0x600b,
+ 0x0000, 0x00c8, 0x2011, 0x0001, 0x6860, 0xa005, 0x1110, 0x2001,
+ 0x001e, 0x8000, 0x6016, 0x6858, 0xa084, 0x00ff, 0x0178, 0x6006,
+ 0x6858, 0x8007, 0xa084, 0x00ff, 0x0148, 0x600a, 0x6858, 0x8000,
+ 0x1108, 0xc28d, 0x6202, 0x012e, 0x0804, 0x5f7f, 0x012e, 0x0804,
+ 0x5f79, 0x012e, 0x0804, 0x5f73, 0x012e, 0x0804, 0x5f76, 0x0126,
+ 0x2091, 0x8000, 0x7007, 0x0001, 0x2001, 0xad52, 0x2004, 0xd0a4,
+ 0x05e0, 0x2061, 0xb048, 0x6000, 0xd084, 0x05b8, 0x6204, 0x6308,
+ 0xd08c, 0x1530, 0x6c48, 0xa484, 0x0003, 0x0170, 0x6958, 0xa18c,
+ 0x00ff, 0x8001, 0x1120, 0x2100, 0xa210, 0x0620, 0x0028, 0x8001,
+ 0x1508, 0x2100, 0xa212, 0x02f0, 0xa484, 0x000c, 0x0188, 0x6958,
+ 0x810f, 0xa18c, 0x00ff, 0xa082, 0x0004, 0x1120, 0x2100, 0xa318,
+ 0x0288, 0x0030, 0xa082, 0x0004, 0x1168, 0x2100, 0xa31a, 0x0250,
+ 0x6860, 0xa005, 0x0110, 0x8000, 0x6016, 0x6206, 0x630a, 0x012e,
+ 0x0804, 0x5f7f, 0x012e, 0x0804, 0x5f7c, 0x012e, 0x0804, 0x5f79,
+ 0x0126, 0x2091, 0x8000, 0x7007, 0x0001, 0x2061, 0xb048, 0x6300,
+ 0xd38c, 0x1120, 0x6308, 0x8318, 0x0220, 0x630a, 0x012e, 0x0804,
+ 0x5f8d, 0x012e, 0x0804, 0x5f7c, 0x0126, 0x00c6, 0x2091, 0x8000,
+ 0x7007, 0x0001, 0x684c, 0xd0ac, 0x0148, 0x00c6, 0x2061, 0xb048,
+ 0x6000, 0xa084, 0xfcff, 0x6002, 0x00ce, 0x0448, 0x6858, 0xa005,
+ 0x05d0, 0x685c, 0xa065, 0x0598, 0x2001, 0xad30, 0x2004, 0xa005,
+ 0x0118, 0x080c, 0x974e, 0x0068, 0x6013, 0x0400, 0x6057, 0x0000,
+ 0x694c, 0xd1a4, 0x0110, 0x6950, 0x6156, 0x2009, 0x0041, 0x080c,
+ 0x80a7, 0x6958, 0xa18c, 0xff00, 0xa186, 0x2000, 0x1140, 0x0026,
+ 0x2009, 0x0000, 0x2011, 0xfdff, 0x080c, 0x663f, 0x002e, 0x684c,
+ 0xd0c4, 0x0148, 0x2061, 0xb048, 0x6000, 0xd08c, 0x1120, 0x6008,
+ 0x8000, 0x0208, 0x600a, 0x00ce, 0x012e, 0x0804, 0x5f7f, 0x00ce,
+ 0x012e, 0x0804, 0x5f79, 0x6954, 0xa186, 0x002e, 0x0d40, 0xa186,
+ 0x002d, 0x0d28, 0xa186, 0x0045, 0x0510, 0xa186, 0x002a, 0x1130,
+ 0x2001, 0xad0c, 0x200c, 0xc194, 0x2102, 0x08c8, 0xa186, 0x0020,
+ 0x0170, 0xa186, 0x0029, 0x1d18, 0x6944, 0xa18c, 0xff00, 0x810f,
+ 0x080c, 0x4cdc, 0x1960, 0x6000, 0xc0e4, 0x6002, 0x0840, 0x685c,
+ 0xa065, 0x09a8, 0x2001, 0xafa3, 0x2004, 0x6016, 0x0800, 0x685c,
+ 0xa065, 0x0968, 0x00e6, 0x6860, 0xa075, 0x2001, 0xad30, 0x2004,
+ 0xa005, 0x0150, 0x080c, 0x974e, 0x8eff, 0x0118, 0x2e60, 0x080c,
+ 0x974e, 0x00ee, 0x0804, 0x5e3f, 0x6020, 0xc0dc, 0xc0d5, 0x6022,
+ 0x2e60, 0x6007, 0x003a, 0x6870, 0xa005, 0x0130, 0x6007, 0x003b,
+ 0x6874, 0x602a, 0x6878, 0x6012, 0x6003, 0x0001, 0x080c, 0x67a8,
+ 0x080c, 0x6c50, 0x00ee, 0x0804, 0x5e3f, 0x2061, 0xb048, 0x6000,
+ 0xd084, 0x0190, 0xd08c, 0x1904, 0x5f8d, 0x0126, 0x2091, 0x8000,
+ 0x6204, 0x8210, 0x0220, 0x6206, 0x012e, 0x0804, 0x5f8d, 0x012e,
+ 0x6853, 0x0016, 0x0804, 0x5f86, 0x6853, 0x0007, 0x0804, 0x5f86,
+ 0x6834, 0x8007, 0xa084, 0x00ff, 0x1118, 0x080c, 0x5b99, 0x0078,
+ 0x2030, 0x8001, 0x1120, 0x7007, 0x0001, 0x0051, 0x0040, 0x7007,
+ 0x0006, 0x7012, 0x2d00, 0x7016, 0x701a, 0x704b, 0x5ee0, 0x0005,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x2009, 0xad30, 0x210c, 0x81ff,
+ 0x1904, 0x5f5b, 0x2009, 0xad0c, 0x210c, 0xd194, 0x1904, 0x5f63,
+ 0x6848, 0x2070, 0xae82, 0xb400, 0x0a04, 0x5f4f, 0x2001, 0xad16,
+ 0x2004, 0xae02, 0x1a04, 0x5f4f, 0x2061, 0xb048, 0x6100, 0xa184,
+ 0x0301, 0xa086, 0x0001, 0x15a8, 0x711c, 0xa186, 0x0006, 0x15b0,
+ 0x7018, 0xa005, 0x0904, 0x5f5b, 0x2004, 0xd0e4, 0x1904, 0x5f5e,
+ 0x7020, 0xd0dc, 0x1904, 0x5f66, 0x6853, 0x0000, 0x6803, 0x0000,
+ 0x2d08, 0x7010, 0xa005, 0x1158, 0x7112, 0x684c, 0xd0f4, 0x1904,
+ 0x5f69, 0x2e60, 0x080c, 0x65aa, 0x012e, 0x00ee, 0x0005, 0x2068,
+ 0x6800, 0xa005, 0x1de0, 0x6902, 0x2168, 0x684c, 0xd0f4, 0x15c8,
+ 0x012e, 0x00ee, 0x0005, 0x012e, 0x00ee, 0x6853, 0x0006, 0x0804,
+ 0x5f86, 0xd184, 0x0dc0, 0xd1c4, 0x11a8, 0x00b8, 0x6944, 0xa18c,
+ 0xff00, 0x810f, 0x080c, 0x4cdc, 0x11c8, 0x6000, 0xd0e4, 0x11b0,
+ 0x711c, 0xa186, 0x0007, 0x1118, 0x6853, 0x0002, 0x0088, 0x6853,
+ 0x0008, 0x0070, 0x6853, 0x000e, 0x0058, 0x6853, 0x0017, 0x0040,
+ 0x6853, 0x0035, 0x0028, 0x6853, 0x0028, 0x0010, 0x6853, 0x0029,
+ 0x012e, 0x00ee, 0x0418, 0x6853, 0x002a, 0x0cd0, 0x6853, 0x0045,
+ 0x0cb8, 0x2e60, 0x2019, 0x0002, 0x6017, 0x0014, 0x080c, 0xa566,
+ 0x012e, 0x00ee, 0x0005, 0x2009, 0x003e, 0x0058, 0x2009, 0x0004,
+ 0x0040, 0x2009, 0x0006, 0x0028, 0x2009, 0x0016, 0x0010, 0x2009,
+ 0x0001, 0x6854, 0xa084, 0xff00, 0xa105, 0x6856, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x510c, 0x012e, 0x0005, 0x080c, 0x15f0, 0x0005,
+ 0x702c, 0x7130, 0x8108, 0xa102, 0x0230, 0xa00e, 0x7034, 0x7072,
+ 0x7038, 0x7076, 0x0058, 0x7070, 0xa080, 0x0040, 0x7072, 0x1230,
+ 0x7074, 0xa081, 0x0000, 0x7076, 0xa085, 0x0001, 0x7932, 0x7132,
+ 0x0005, 0x00d6, 0x080c, 0x65a1, 0x00de, 0x0005, 0x00d6, 0x2011,
+ 0x0004, 0x2204, 0xa085, 0x8002, 0x2012, 0x00de, 0x0005, 0x20e1,
+ 0x0002, 0x3d08, 0x20e1, 0x2000, 0x3d00, 0xa084, 0x7000, 0x0118,
+ 0xa086, 0x1000, 0x1540, 0x20e1, 0x0000, 0x3d00, 0xa094, 0xff00,
+ 0x8217, 0xa084, 0xf000, 0xa086, 0x3000, 0x1118, 0x080c, 0x61c6,
+ 0x00b0, 0x20e1, 0x0004, 0x3d60, 0xd1bc, 0x1108, 0x3e60, 0xac84,
+ 0x0007, 0x1188, 0xac82, 0xb400, 0x0270, 0x6858, 0xac02, 0x1258,
+ 0x6120, 0xd1f4, 0x1160, 0x2009, 0x0047, 0x080c, 0x80a7, 0x7a1c,
+ 0xd284, 0x1968, 0x0005, 0xa016, 0x080c, 0x1824, 0x0cc0, 0x0cd8,
+ 0x781c, 0xd08c, 0x0500, 0x0156, 0x0136, 0x0146, 0x20e1, 0x3000,
+ 0x3d20, 0x3e28, 0xa584, 0x0076, 0x1530, 0xa484, 0x7000, 0xa086,
+ 0x1000, 0x11a8, 0x080c, 0x604e, 0x01f0, 0x20e1, 0x3000, 0x7828,
+ 0x7828, 0x080c, 0x606a, 0x014e, 0x013e, 0x015e, 0x2009, 0xafcf,
+ 0x2104, 0xa005, 0x1108, 0x0005, 0x080c, 0x6c50, 0x0ce0, 0xa484,
+ 0x7000, 0x1518, 0x0499, 0x01b8, 0x7000, 0xa084, 0xff00, 0xa086,
+ 0x8100, 0x0d18, 0x0080, 0xd5a4, 0x0158, 0x080c, 0x1d86, 0x20e1,
+ 0x9010, 0x2001, 0x0160, 0x2502, 0x2001, 0x0138, 0x2202, 0x0048,
+ 0x00e9, 0x6883, 0x0000, 0x080c, 0xac59, 0x20e1, 0x3000, 0x7828,
+ 0x7828, 0x014e, 0x013e, 0x015e, 0x08b0, 0x0081, 0x1130, 0x7000,
+ 0xa084, 0xff00, 0xa086, 0x8100, 0x1d70, 0x080c, 0xac59, 0x20e1,
+ 0x3000, 0x7828, 0x7828, 0x080c, 0x642d, 0x0c58, 0xa484, 0x01ff,
+ 0x6882, 0xa005, 0x0160, 0xa080, 0x001f, 0xa084, 0x03f8, 0x80ac,
+ 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0x0005, 0x20a9,
+ 0x000c, 0x20e1, 0x1000, 0x2ea0, 0x2099, 0x020a, 0x53a5, 0xa085,
+ 0x0001, 0x0ca0, 0x7000, 0xa084, 0xff00, 0xa08c, 0xf000, 0x8007,
+ 0xa196, 0x0000, 0x1118, 0x0804, 0x62cf, 0x0005, 0xa196, 0x2000,
+ 0x1148, 0x6900, 0xa18e, 0x0001, 0x1118, 0x080c, 0x41d1, 0x0ca8,
+ 0x0039, 0x0c98, 0xa196, 0x8000, 0x1d80, 0x080c, 0x6372, 0x0c68,
+ 0x00c6, 0x6a80, 0x82ff, 0x0904, 0x61c0, 0x7110, 0xa18c, 0xff00,
+ 0x810f, 0xa196, 0x0001, 0x0120, 0xa196, 0x0023, 0x1904, 0x61c0,
+ 0xa08e, 0x0023, 0x1570, 0x080c, 0x6408, 0x0904, 0x61c0, 0x7124,
+ 0x610a, 0x7030, 0xa08e, 0x0200, 0x1150, 0x7034, 0xa005, 0x1904,
+ 0x61c0, 0x2009, 0x0015, 0x080c, 0x80a7, 0x0804, 0x61c0, 0xa08e,
+ 0x0214, 0x0118, 0xa08e, 0x0210, 0x1130, 0x2009, 0x0015, 0x080c,
+ 0x80a7, 0x0804, 0x61c0, 0xa08e, 0x0100, 0x1904, 0x61c0, 0x7034,
+ 0xa005, 0x1904, 0x61c0, 0x2009, 0x0016, 0x080c, 0x80a7, 0x0804,
+ 0x61c0, 0xa08e, 0x0022, 0x1904, 0x61c0, 0x7030, 0xa08e, 0x0300,
+ 0x1580, 0x68d0, 0xd0a4, 0x0528, 0xc0b5, 0x68d2, 0x7100, 0xa18c,
+ 0x00ff, 0x696e, 0x7004, 0x6872, 0x00f6, 0x2079, 0x0100, 0x79e6,
+ 0x78ea, 0x0006, 0xa084, 0x00ff, 0x0016, 0x2008, 0x080c, 0x26a0,
+ 0x7932, 0x7936, 0x001e, 0x000e, 0x00fe, 0x080c, 0x2676, 0x694e,
+ 0x703c, 0x00e6, 0x2071, 0x0140, 0x7086, 0x2071, 0xad00, 0x70a2,
+ 0x00ee, 0x7034, 0xa005, 0x1904, 0x61c0, 0x2009, 0x0017, 0x0804,
+ 0x6193, 0xa08e, 0x0400, 0x1158, 0x7034, 0xa005, 0x1904, 0x61c0,
+ 0x68d0, 0xc0a5, 0x68d2, 0x2009, 0x0030, 0x0804, 0x6193, 0xa08e,
+ 0x0500, 0x1140, 0x7034, 0xa005, 0x1904, 0x61c0, 0x2009, 0x0018,
+ 0x0804, 0x6193, 0xa08e, 0x2010, 0x1120, 0x2009, 0x0019, 0x0804,
+ 0x6193, 0xa08e, 0x2110, 0x1120, 0x2009, 0x001a, 0x0804, 0x6193,
+ 0xa08e, 0x5200, 0x1140, 0x7034, 0xa005, 0x1904, 0x61c0, 0x2009,
+ 0x001b, 0x0804, 0x6193, 0xa08e, 0x5000, 0x1140, 0x7034, 0xa005,
+ 0x1904, 0x61c0, 0x2009, 0x001c, 0x0804, 0x6193, 0xa08e, 0x1300,
+ 0x1120, 0x2009, 0x0034, 0x0804, 0x6193, 0xa08e, 0x1200, 0x1140,
+ 0x7034, 0xa005, 0x1904, 0x61c0, 0x2009, 0x0024, 0x0804, 0x6193,
+ 0xa08c, 0xff00, 0xa18e, 0x2400, 0x1118, 0x2009, 0x002d, 0x04d8,
+ 0xa08c, 0xff00, 0xa18e, 0x5300, 0x1118, 0x2009, 0x002a, 0x0498,
+ 0xa08e, 0x0f00, 0x1118, 0x2009, 0x0020, 0x0468, 0xa08e, 0x5300,
+ 0x1108, 0x00d8, 0xa08e, 0x6104, 0x11c0, 0x2011, 0xb28d, 0x8208,
+ 0x2204, 0xa082, 0x0004, 0x20a8, 0x95ac, 0x95ac, 0x2011, 0x8015,
+ 0x211c, 0x8108, 0x0046, 0x2124, 0x080c, 0x3c5c, 0x004e, 0x8108,
+ 0x1f04, 0x6176, 0x2009, 0x0023, 0x0070, 0xa08e, 0x6000, 0x1118,
+ 0x2009, 0x003f, 0x0040, 0xa08e, 0x7800, 0x1118, 0x2009, 0x0045,
+ 0x0010, 0x2009, 0x001d, 0x0016, 0x2011, 0xb283, 0x2204, 0x8211,
+ 0x220c, 0x080c, 0x2676, 0x1530, 0x080c, 0x4c80, 0x1518, 0x6612,
+ 0x6516, 0x86ff, 0x0180, 0x001e, 0x0016, 0xa186, 0x0017, 0x1158,
+ 0x686c, 0xa606, 0x1140, 0x6870, 0xa506, 0xa084, 0xff00, 0x1118,
+ 0x6000, 0xc0f5, 0x6002, 0x00c6, 0x080c, 0x8022, 0x0168, 0x001e,
+ 0x611a, 0x601f, 0x0004, 0x7120, 0x610a, 0x001e, 0x080c, 0x80a7,
+ 0x00ce, 0x0005, 0x001e, 0x0ce0, 0x00ce, 0x0ce0, 0x00c6, 0x0046,
+ 0x080c, 0x6221, 0x1904, 0x621e, 0xa184, 0xff00, 0x8007, 0xa086,
+ 0x0008, 0x1904, 0x621e, 0xa28e, 0x0033, 0x11e8, 0x080c, 0x6408,
+ 0x0904, 0x621e, 0x7124, 0x610a, 0x7030, 0xa08e, 0x0200, 0x1140,
+ 0x7034, 0xa005, 0x15d8, 0x2009, 0x0015, 0x080c, 0x80a7, 0x04b0,
+ 0xa08e, 0x0100, 0x1598, 0x7034, 0xa005, 0x1580, 0x2009, 0x0016,
+ 0x080c, 0x80a7, 0x0458, 0xa28e, 0x0032, 0x1540, 0x7030, 0xa08e,
+ 0x1400, 0x1520, 0x2009, 0x0038, 0x0016, 0x2011, 0xb283, 0x2204,
+ 0x8211, 0x220c, 0x080c, 0x2676, 0x11c0, 0x080c, 0x4c80, 0x11a8,
+ 0x6612, 0x6516, 0x00c6, 0x080c, 0x8022, 0x0170, 0x001e, 0x611a,
+ 0x080c, 0x9956, 0x601f, 0x0004, 0x7120, 0x610a, 0x001e, 0x080c,
+ 0x80a7, 0x080c, 0x6c50, 0x0010, 0x00ce, 0x001e, 0x004e, 0x00ce,
+ 0x0005, 0x00f6, 0x00d6, 0x0026, 0x0016, 0x0136, 0x0146, 0x0156,
+ 0x3c00, 0x0006, 0x2079, 0x0030, 0x2069, 0x0200, 0x080c, 0x1df2,
+ 0x1590, 0x080c, 0x1ce2, 0x05c8, 0x04d9, 0x1130, 0x7908, 0xa18c,
+ 0x1fff, 0xa182, 0x0011, 0x1688, 0x20a9, 0x000c, 0x20e1, 0x0000,
+ 0x2ea0, 0x2099, 0x020a, 0x53a5, 0x20e1, 0x2000, 0x2001, 0x020a,
+ 0x2004, 0x7a0c, 0x7808, 0xa080, 0x0007, 0xa084, 0x1ff8, 0x0401,
+ 0x1120, 0xa08a, 0x0140, 0x1a0c, 0x14f6, 0x80ac, 0x20e1, 0x6000,
+ 0x2099, 0x020a, 0x53a5, 0x20e1, 0x7000, 0x6828, 0x6828, 0x7803,
+ 0x0004, 0xa294, 0x0070, 0x000e, 0x20e0, 0x015e, 0x014e, 0x013e,
+ 0x001e, 0x002e, 0x00de, 0x00fe, 0x0005, 0xa085, 0x0001, 0x0c98,
+ 0x0006, 0x2001, 0x0111, 0x2004, 0xa084, 0x0003, 0x000e, 0x0005,
+ 0x0046, 0x00e6, 0x00d6, 0x2028, 0x2130, 0xa696, 0x00ff, 0x1198,
+ 0xa596, 0xfffd, 0x1120, 0x2009, 0x007f, 0x0804, 0x62ca, 0xa596,
+ 0xfffe, 0x1118, 0x2009, 0x007e, 0x04e8, 0xa596, 0xfffc, 0x1118,
+ 0x2009, 0x0080, 0x04b8, 0x2011, 0x0000, 0x2019, 0xad34, 0x231c,
+ 0xd3ac, 0x0138, 0x2021, 0x0000, 0x20a9, 0x00ff, 0x2071, 0xae34,
+ 0x0030, 0x2021, 0x0081, 0x20a9, 0x007e, 0x2071, 0xaeb5, 0x2e1c,
+ 0x83ff, 0x1128, 0x82ff, 0x1198, 0x2410, 0xc2fd, 0x0080, 0x2368,
+ 0x6f10, 0x0006, 0x2100, 0xa706, 0x000e, 0x6b14, 0x1120, 0xa346,
+ 0x1110, 0x2408, 0x0078, 0x87ff, 0x1110, 0x83ff, 0x0d58, 0x8420,
+ 0x8e70, 0x1f04, 0x62a7, 0x82ff, 0x1118, 0xa085, 0x0001, 0x0018,
+ 0xc2fc, 0x2208, 0xa006, 0x00de, 0x00ee, 0x004e, 0x0005, 0xa084,
+ 0x0007, 0x000a, 0x0005, 0x62db, 0x62db, 0x62db, 0x641a, 0x62db,
+ 0x62dc, 0x62f1, 0x635d, 0x0005, 0x7110, 0xd1bc, 0x0188, 0x7120,
+ 0x2160, 0xac8c, 0x0007, 0x1160, 0xac8a, 0xb400, 0x0248, 0x6858,
+ 0xac02, 0x1230, 0x7124, 0x610a, 0x2009, 0x0046, 0x080c, 0x80a7,
+ 0x0005, 0x00c6, 0x7110, 0xd1bc, 0x1904, 0x6344, 0x2011, 0xb283,
+ 0x2204, 0x8211, 0x220c, 0x080c, 0x2676, 0x1904, 0x6344, 0x080c,
+ 0x4c80, 0x1904, 0x6344, 0x6612, 0x6516, 0x6000, 0xd0ec, 0x15e0,
+ 0x6204, 0xa294, 0xff00, 0x8217, 0xa286, 0x0006, 0x0160, 0x080c,
+ 0x574f, 0x11d0, 0x6204, 0xa294, 0x00ff, 0xa286, 0x0006, 0x11a0,
+ 0xa295, 0x0600, 0x6206, 0x00c6, 0x080c, 0x8022, 0x001e, 0x0530,
+ 0x611a, 0x601f, 0x0006, 0x7120, 0x610a, 0x7130, 0x6152, 0x2009,
+ 0x0044, 0x080c, 0x80a7, 0x00c0, 0x00c6, 0x080c, 0x8022, 0x001e,
+ 0x0198, 0x611a, 0x601f, 0x0004, 0x7120, 0x610a, 0xa286, 0x0004,
+ 0x1118, 0x6007, 0x0005, 0x0010, 0x6007, 0x0001, 0x6003, 0x0001,
+ 0x080c, 0x67ee, 0x080c, 0x6c50, 0x00ce, 0x0005, 0x00c6, 0x080c,
+ 0x9807, 0x001e, 0x0dc8, 0x611a, 0x601f, 0x0006, 0x7120, 0x610a,
+ 0x7130, 0x6152, 0x6013, 0x0300, 0x6003, 0x0001, 0x6007, 0x0041,
+ 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0c38, 0x7110, 0xd1bc, 0x0188,
+ 0x7020, 0x2060, 0xac84, 0x0007, 0x1160, 0xac82, 0xb400, 0x0248,
+ 0x6858, 0xac02, 0x1230, 0x7124, 0x610a, 0x2009, 0x0045, 0x080c,
+ 0x80a7, 0x0005, 0x7110, 0xa18c, 0xff00, 0x810f, 0xa18e, 0x0000,
+ 0x1130, 0xa084, 0x000f, 0xa08a, 0x0006, 0x1208, 0x000b, 0x0005,
+ 0x6386, 0x6387, 0x6386, 0x6386, 0x63f0, 0x63fc, 0x0005, 0x7110,
+ 0xd1bc, 0x0120, 0x702c, 0xd084, 0x0904, 0x63ef, 0x700c, 0x7108,
+ 0x080c, 0x2676, 0x1904, 0x63ef, 0x080c, 0x4c80, 0x1904, 0x63ef,
+ 0x6612, 0x6516, 0x6204, 0x7110, 0xd1bc, 0x01f8, 0xa28c, 0x00ff,
+ 0xa186, 0x0004, 0x0118, 0xa186, 0x0006, 0x15c8, 0x00c6, 0x080c,
+ 0x6408, 0x00ce, 0x0904, 0x63ef, 0x00c6, 0x080c, 0x8022, 0x001e,
+ 0x05f0, 0x611a, 0x080c, 0x9956, 0x601f, 0x0002, 0x7120, 0x610a,
+ 0x2009, 0x0088, 0x080c, 0x80a7, 0x0490, 0xa28c, 0x00ff, 0xa186,
+ 0x0006, 0x0160, 0xa186, 0x0004, 0x0148, 0xa294, 0xff00, 0x8217,
+ 0xa286, 0x0004, 0x0118, 0xa286, 0x0006, 0x1188, 0x00c6, 0x080c,
+ 0x8022, 0x001e, 0x01e0, 0x611a, 0x080c, 0x9956, 0x601f, 0x0005,
+ 0x7120, 0x610a, 0x2009, 0x0088, 0x080c, 0x80a7, 0x0080, 0x00c6,
+ 0x080c, 0x8022, 0x001e, 0x0158, 0x611a, 0x080c, 0x9956, 0x601f,
+ 0x0004, 0x7120, 0x610a, 0x2009, 0x0001, 0x080c, 0x80a7, 0x0005,
+ 0x7110, 0xd1bc, 0x0140, 0x00a1, 0x0130, 0x7124, 0x610a, 0x2009,
+ 0x0089, 0x080c, 0x80a7, 0x0005, 0x7110, 0xd1bc, 0x0140, 0x0041,
+ 0x0130, 0x7124, 0x610a, 0x2009, 0x008a, 0x080c, 0x80a7, 0x0005,
+ 0x7020, 0x2060, 0xac84, 0x0007, 0x1158, 0xac82, 0xb400, 0x0240,
+ 0x2001, 0xad16, 0x2004, 0xac02, 0x1218, 0xa085, 0x0001, 0x0005,
+ 0xa006, 0x0ce8, 0x7110, 0xd1bc, 0x1178, 0x7024, 0x2060, 0xac84,
+ 0x0007, 0x1150, 0xac82, 0xb400, 0x0238, 0x6858, 0xac02, 0x1220,
+ 0x2009, 0x0051, 0x080c, 0x80a7, 0x0005, 0x2031, 0x0105, 0x0069,
+ 0x0005, 0x2031, 0x0206, 0x0049, 0x0005, 0x2031, 0x0207, 0x0029,
+ 0x0005, 0x2031, 0x0213, 0x0009, 0x0005, 0x00c6, 0x00d6, 0x00f6,
+ 0x7000, 0xa084, 0xf000, 0xa086, 0xc000, 0x05b0, 0x080c, 0x8022,
+ 0x0598, 0x0066, 0x00c6, 0x0046, 0x2011, 0xb283, 0x2204, 0x8211,
+ 0x220c, 0x080c, 0x2676, 0x1580, 0x080c, 0x4c80, 0x1568, 0x6612,
+ 0x6516, 0x2c00, 0x004e, 0x00ce, 0x601a, 0x080c, 0x9956, 0x080c,
+ 0x15d9, 0x01f0, 0x2d00, 0x6056, 0x6803, 0x0000, 0x6837, 0x0000,
+ 0x6c3a, 0xadf8, 0x000f, 0x20a9, 0x000e, 0x2fa0, 0x2e98, 0x53a3,
+ 0x006e, 0x6612, 0x6007, 0x003e, 0x601f, 0x0001, 0x6003, 0x0001,
+ 0x080c, 0x67ee, 0x080c, 0x6c50, 0x00fe, 0x00de, 0x00ce, 0x0005,
+ 0x080c, 0x8078, 0x006e, 0x0cc0, 0x004e, 0x00ce, 0x0cc8, 0x2071,
+ 0xafda, 0x7003, 0x0003, 0x700f, 0x0361, 0xa006, 0x701a, 0x7012,
+ 0x7017, 0xb400, 0x7007, 0x0000, 0x7026, 0x702b, 0x7841, 0x7032,
+ 0x7037, 0x789d, 0x703b, 0xffff, 0x703f, 0xffff, 0x7042, 0x7047,
+ 0x41b3, 0x0005, 0x2071, 0xafda, 0x1d04, 0x64fc, 0x2091, 0x6000,
+ 0x700c, 0x8001, 0x700e, 0x1180, 0x700f, 0x0361, 0x7007, 0x0001,
+ 0x0126, 0x2091, 0x8000, 0x7040, 0xa00d, 0x0148, 0x8109, 0x7142,
+ 0x1130, 0x7044, 0x080f, 0x0018, 0x0126, 0x2091, 0x8000, 0x7024,
+ 0xa00d, 0x0188, 0x7020, 0x8001, 0x7022, 0x1168, 0x7023, 0x0009,
+ 0x8109, 0x7126, 0xa186, 0x03e8, 0x1110, 0x7028, 0x080f, 0x81ff,
+ 0x1110, 0x7028, 0x080f, 0x7030, 0xa00d, 0x0158, 0x702c, 0x8001,
+ 0x702e, 0x1138, 0x702f, 0x0009, 0x8109, 0x7132, 0x1110, 0x7034,
+ 0x080f, 0x7038, 0xa005, 0x0118, 0x0310, 0x8001, 0x703a, 0x703c,
+ 0xa005, 0x0118, 0x0310, 0x8001, 0x703e, 0x7018, 0xa00d, 0x0158,
+ 0x7008, 0x8001, 0x700a, 0x1138, 0x700b, 0x0009, 0x8109, 0x711a,
+ 0x1110, 0x701c, 0x080f, 0x012e, 0x7004, 0x0002, 0x6522, 0x6523,
+ 0x653b, 0x00e6, 0x2071, 0xafda, 0x7018, 0xa005, 0x1120, 0x711a,
+ 0x721e, 0x700b, 0x0009, 0x00ee, 0x0005, 0x00e6, 0x0006, 0x2071,
+ 0xafda, 0x701c, 0xa206, 0x1110, 0x701a, 0x701e, 0x000e, 0x00ee,
+ 0x0005, 0x00e6, 0x2071, 0xafda, 0x6088, 0xa102, 0x0208, 0x618a,
+ 0x00ee, 0x0005, 0x0005, 0x7110, 0x080c, 0x4cdc, 0x1158, 0x6088,
+ 0x8001, 0x0240, 0x608a, 0x1130, 0x0126, 0x2091, 0x8000, 0x080c,
+ 0x6c50, 0x012e, 0x8108, 0xa182, 0x00ff, 0x0218, 0xa00e, 0x7007,
+ 0x0002, 0x7112, 0x0005, 0x7014, 0x2060, 0x0126, 0x2091, 0x8000,
+ 0x603c, 0xa005, 0x0128, 0x8001, 0x603e, 0x1110, 0x080c, 0x9846,
+ 0x6014, 0xa005, 0x0500, 0x8001, 0x6016, 0x11e8, 0x611c, 0xa186,
+ 0x0003, 0x0118, 0xa186, 0x0006, 0x11a0, 0x6010, 0x2068, 0x6854,
+ 0xa08a, 0x199a, 0x0270, 0xa082, 0x1999, 0x6856, 0xa08a, 0x199a,
+ 0x0210, 0x2001, 0x1999, 0x8003, 0x800b, 0x810b, 0xa108, 0x6116,
+ 0x0010, 0x080c, 0x9350, 0x012e, 0xac88, 0x0018, 0x7116, 0x2001,
+ 0xe400, 0xa102, 0x0220, 0x7017, 0xb400, 0x7007, 0x0000, 0x0005,
+ 0x00e6, 0x2071, 0xafda, 0x7027, 0x07d0, 0x7023, 0x0009, 0x00ee,
+ 0x0005, 0x2001, 0xafe3, 0x2003, 0x0000, 0x0005, 0x00e6, 0x2071,
+ 0xafda, 0x7132, 0x702f, 0x0009, 0x00ee, 0x0005, 0x2011, 0xafe6,
+ 0x2013, 0x0000, 0x0005, 0x00e6, 0x2071, 0xafda, 0x711a, 0x721e,
+ 0x700b, 0x0009, 0x00ee, 0x0005, 0x00c6, 0x2061, 0xb048, 0x00ce,
+ 0x0005, 0xa184, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0xb048,
+ 0x2060, 0x0005, 0x6854, 0xa08a, 0x199a, 0x0210, 0x2001, 0x1999,
+ 0xa005, 0x1150, 0x00c6, 0x2061, 0xb048, 0x6014, 0x00ce, 0xa005,
+ 0x1138, 0x2001, 0x001e, 0x0020, 0xa08e, 0xffff, 0x1108, 0xa006,
+ 0x8003, 0x800b, 0x810b, 0xa108, 0x6116, 0x684c, 0xa08c, 0x00c0,
+ 0xa18e, 0x00c0, 0x05b0, 0xd0b4, 0x1138, 0xd0bc, 0x1528, 0x2009,
+ 0x0006, 0x080c, 0x661a, 0x0005, 0xd0fc, 0x0130, 0xa084, 0x0003,
+ 0x0118, 0xa086, 0x0003, 0x15c0, 0x6020, 0xd0d4, 0x0130, 0xc0d4,
+ 0x6022, 0x6860, 0x602a, 0x685c, 0x602e, 0x2009, 0xad73, 0x2104,
+ 0xd084, 0x0128, 0x2009, 0x0042, 0x080c, 0x80a7, 0x0005, 0x2009,
+ 0x0043, 0x080c, 0x80a7, 0x0005, 0xd0fc, 0x0130, 0xa084, 0x0003,
+ 0x0118, 0xa086, 0x0003, 0x11c0, 0x2009, 0x0042, 0x080c, 0x80a7,
+ 0x0005, 0xd0fc, 0x0150, 0xa084, 0x0003, 0xa08e, 0x0002, 0x0138,
+ 0x2009, 0x0041, 0x080c, 0x80a7, 0x0005, 0x0051, 0x0ce8, 0x2009,
+ 0x0043, 0x080c, 0x80a7, 0x0cc0, 0x2009, 0x0004, 0x0019, 0x0005,
+ 0x2009, 0x0001, 0x00d6, 0x6010, 0xa0ec, 0xf000, 0x01f0, 0x2068,
+ 0x6952, 0x6800, 0x6012, 0xa186, 0x0001, 0x1188, 0x694c, 0xa18c,
+ 0x8100, 0xa18e, 0x8100, 0x1158, 0x00c6, 0x2061, 0xb048, 0x6200,
+ 0xd28c, 0x1120, 0x6204, 0x8210, 0x0208, 0x6206, 0x00ce, 0x080c,
+ 0x510c, 0x6010, 0xa06d, 0x190c, 0x65aa, 0x00de, 0x0005, 0x0156,
+ 0x00c6, 0x2061, 0xb048, 0x6000, 0x81ff, 0x0110, 0xa205, 0x0008,
+ 0xa204, 0x6002, 0x00ce, 0x015e, 0x0005, 0x6800, 0xd08c, 0x1138,
+ 0x6808, 0xa005, 0x0120, 0x8001, 0x680a, 0xa085, 0x0001, 0x0005,
+ 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, 0x818e, 0x1208, 0xa200,
+ 0x1f04, 0x665c, 0x8086, 0x818e, 0x0005, 0x0156, 0x20a9, 0x0010,
+ 0xa005, 0x01b8, 0xa11a, 0x12a8, 0x8213, 0x818d, 0x0228, 0xa11a,
+ 0x1220, 0x1f04, 0x666c, 0x0028, 0xa11a, 0x2308, 0x8210, 0x1f04,
+ 0x666c, 0x0006, 0x3200, 0xa084, 0xefff, 0x2080, 0x000e, 0x015e,
+ 0x0005, 0x0006, 0x3200, 0xa085, 0x1000, 0x0cb8, 0x0126, 0x2091,
+ 0x2800, 0x2079, 0xafc7, 0x012e, 0x00d6, 0x2069, 0xafc7, 0x6803,
+ 0x0005, 0x2069, 0x0004, 0x2d04, 0xa085, 0x8001, 0x206a, 0x00de,
+ 0x0005, 0x00c6, 0x6027, 0x0001, 0x7804, 0xa084, 0x0007, 0x0002,
+ 0x66aa, 0x66cb, 0x671e, 0x66b0, 0x66cb, 0x66aa, 0x66a8, 0x66a8,
+ 0x080c, 0x14f6, 0x080c, 0x6581, 0x080c, 0x6c50, 0x00ce, 0x0005,
+ 0x62c0, 0x82ff, 0x1110, 0x00ce, 0x0005, 0x2011, 0x481b, 0x080c,
+ 0x650d, 0x7828, 0xa092, 0x00c8, 0x1228, 0x8000, 0x782a, 0x080c,
+ 0x4855, 0x0c88, 0x080c, 0x481b, 0x7807, 0x0003, 0x7827, 0x0000,
+ 0x782b, 0x0000, 0x0c40, 0x080c, 0x6581, 0x3c00, 0x0006, 0x2011,
+ 0x0209, 0x20e1, 0x4000, 0x2214, 0x000e, 0x20e0, 0x82ff, 0x0178,
+ 0x62c0, 0x82ff, 0x1160, 0x782b, 0x0000, 0x7824, 0xa065, 0x090c,
+ 0x14f6, 0x2009, 0x0013, 0x080c, 0x80a7, 0x00ce, 0x0005, 0x3900,
+ 0xa082, 0xb0e8, 0x1210, 0x080c, 0x7d8d, 0x00c6, 0x7824, 0xa065,
+ 0x090c, 0x14f6, 0x7804, 0xa086, 0x0004, 0x0904, 0x675e, 0x7828,
+ 0xa092, 0x2710, 0x1230, 0x8000, 0x782a, 0x00ce, 0x080c, 0x7827,
+ 0x0c20, 0x6104, 0xa186, 0x0003, 0x1188, 0x00e6, 0x2071, 0xad00,
+ 0x70dc, 0x00ee, 0xd08c, 0x0150, 0x00c6, 0x00e6, 0x2061, 0x0100,
+ 0x2071, 0xad00, 0x080c, 0x485e, 0x00ee, 0x00ce, 0x080c, 0xaca2,
+ 0x2009, 0x0014, 0x080c, 0x80a7, 0x00ce, 0x0838, 0x2001, 0xafe3,
+ 0x2003, 0x0000, 0x62c0, 0x82ff, 0x1160, 0x782b, 0x0000, 0x7824,
+ 0xa065, 0x090c, 0x14f6, 0x2009, 0x0013, 0x080c, 0x80fb, 0x00ce,
+ 0x0005, 0x00c6, 0x00d6, 0x3900, 0xa082, 0xb0e8, 0x1210, 0x080c,
+ 0x7d8d, 0x7824, 0xa005, 0x090c, 0x14f6, 0x781c, 0xa06d, 0x090c,
+ 0x14f6, 0x6800, 0xc0dc, 0x6802, 0x7924, 0x2160, 0x080c, 0x8078,
+ 0x693c, 0x81ff, 0x090c, 0x14f6, 0x8109, 0x693e, 0x6854, 0xa015,
+ 0x0110, 0x7a1e, 0x0010, 0x7918, 0x791e, 0x7807, 0x0000, 0x7827,
+ 0x0000, 0x00de, 0x00ce, 0x080c, 0x6c50, 0x0888, 0x6104, 0xa186,
+ 0x0002, 0x0128, 0xa186, 0x0004, 0x0110, 0x0804, 0x66f7, 0x7808,
+ 0xac06, 0x0904, 0x66f7, 0x080c, 0x6b73, 0x080c, 0x67ee, 0x00ce,
+ 0x080c, 0x6c50, 0x0804, 0x66e5, 0x00c6, 0x6027, 0x0002, 0x62c8,
+ 0x60c4, 0xa205, 0x1178, 0x793c, 0xa1e5, 0x0000, 0x0130, 0x2009,
+ 0x0049, 0x080c, 0x80a7, 0x00ce, 0x0005, 0x2011, 0xafe6, 0x2013,
+ 0x0000, 0x0cc8, 0x3908, 0xa192, 0xb0e8, 0x1210, 0x080c, 0x7d8d,
+ 0x793c, 0x81ff, 0x0d90, 0x793c, 0xa188, 0x0007, 0x210c, 0xa18e,
+ 0x0006, 0x1138, 0x6014, 0xa084, 0x0184, 0xa085, 0x0012, 0x6016,
+ 0x0c10, 0x6014, 0xa084, 0x0184, 0xa085, 0x0016, 0x6016, 0x08d8,
+ 0x0006, 0x0016, 0x00c6, 0x0126, 0x2091, 0x8000, 0x600f, 0x0000,
+ 0x2c08, 0x2061, 0xafc7, 0x6020, 0x8000, 0x6022, 0x6010, 0xa005,
+ 0x0148, 0xa080, 0x0003, 0x2102, 0x6112, 0x012e, 0x00ce, 0x001e,
+ 0x000e, 0x0005, 0x6116, 0x6112, 0x0cc0, 0x00d6, 0x2069, 0xafc7,
+ 0x6000, 0xd0d4, 0x0168, 0x6820, 0x8000, 0x6822, 0xa086, 0x0001,
+ 0x1110, 0x2c00, 0x681e, 0x6804, 0xa084, 0x0007, 0x0804, 0x6c56,
+ 0xc0d5, 0x6002, 0x6818, 0xa005, 0x0158, 0x6056, 0x605b, 0x0000,
+ 0x0006, 0x2c00, 0x681a, 0x00de, 0x685a, 0x2069, 0xafc7, 0x0c18,
+ 0x6056, 0x605a, 0x2c00, 0x681a, 0x681e, 0x08e8, 0x0006, 0x0016,
+ 0x00c6, 0x0126, 0x2091, 0x8000, 0x600f, 0x0000, 0x2c08, 0x2061,
+ 0xafc7, 0x6020, 0x8000, 0x6022, 0x6008, 0xa005, 0x0148, 0xa080,
+ 0x0003, 0x2102, 0x610a, 0x012e, 0x00ce, 0x001e, 0x000e, 0x0005,
+ 0x610e, 0x610a, 0x0cc0, 0x00c6, 0x600f, 0x0000, 0x2c08, 0x2061,
+ 0xafc7, 0x6034, 0xa005, 0x0130, 0xa080, 0x0003, 0x2102, 0x6136,
+ 0x00ce, 0x0005, 0x613a, 0x6136, 0x0cd8, 0x00f6, 0x00e6, 0x00d6,
+ 0x00c6, 0x0076, 0x0066, 0x0026, 0x0016, 0x0006, 0x0126, 0x2071,
+ 0xafc7, 0x7638, 0x2660, 0x2678, 0x2091, 0x8000, 0x8cff, 0x0904,
+ 0x6889, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x1904, 0x6884,
+ 0x87ff, 0x0120, 0x6050, 0xa106, 0x1904, 0x6884, 0x703c, 0xac06,
+ 0x1170, 0x0036, 0x2019, 0x0001, 0x080c, 0x7a64, 0x7033, 0x0000,
+ 0x703f, 0x0000, 0x7043, 0x0000, 0x7047, 0x0000, 0x003e, 0x7038,
+ 0xac36, 0x1110, 0x660c, 0x763a, 0x7034, 0xac36, 0x1140, 0x2c00,
+ 0xaf36, 0x0118, 0x2f00, 0x7036, 0x0010, 0x7037, 0x0000, 0x660c,
+ 0x0066, 0x2c00, 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f,
+ 0x0000, 0x080c, 0x9596, 0x0198, 0x6010, 0x2068, 0x601c, 0xa086,
+ 0x0003, 0x1510, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c,
+ 0x97fd, 0x080c, 0xabfa, 0x080c, 0x510c, 0x080c, 0x9742, 0x080c,
+ 0x974e, 0x00ce, 0x0804, 0x682e, 0x2c78, 0x600c, 0x2060, 0x0804,
+ 0x682e, 0x012e, 0x000e, 0x001e, 0x002e, 0x006e, 0x007e, 0x00ce,
+ 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601c, 0xa086, 0x0006, 0x19d0,
+ 0x080c, 0xabfa, 0x080c, 0xa91f, 0x0c10, 0x0006, 0x0066, 0x00c6,
+ 0x00d6, 0x00f6, 0x2031, 0x0000, 0x0126, 0x2091, 0x8000, 0x2079,
+ 0xafc7, 0x7838, 0xa065, 0x0558, 0x600c, 0x0006, 0x600f, 0x0000,
+ 0x783c, 0xac06, 0x1170, 0x0036, 0x2019, 0x0001, 0x080c, 0x7a64,
+ 0x7833, 0x0000, 0x783f, 0x0000, 0x7843, 0x0000, 0x7847, 0x0000,
+ 0x003e, 0x080c, 0x9596, 0x0178, 0x6010, 0x2068, 0x601c, 0xa086,
+ 0x0003, 0x11b0, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c,
+ 0x510c, 0x080c, 0x9742, 0x080c, 0x974e, 0x000e, 0x0898, 0x7e3a,
+ 0x7e36, 0x012e, 0x00fe, 0x00de, 0x00ce, 0x006e, 0x000e, 0x0005,
+ 0x601c, 0xa086, 0x0006, 0x1d30, 0x080c, 0xa91f, 0x0c60, 0x0016,
+ 0x0026, 0x0086, 0x2041, 0x0000, 0x0099, 0x080c, 0x69a9, 0x008e,
+ 0x002e, 0x001e, 0x0005, 0x00f6, 0x0126, 0x2079, 0xafc7, 0x2091,
+ 0x8000, 0x080c, 0x6a36, 0x080c, 0x6aa8, 0x012e, 0x00fe, 0x0005,
+ 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0066, 0x0016, 0x0006, 0x0126,
+ 0x2091, 0x8000, 0x2071, 0xafc7, 0x7614, 0x2660, 0x2678, 0x8cff,
+ 0x0904, 0x6985, 0x6018, 0xa080, 0x0028, 0x2004, 0xa206, 0x1904,
+ 0x6980, 0x88ff, 0x0120, 0x6050, 0xa106, 0x1904, 0x6980, 0x7024,
+ 0xac06, 0x1538, 0x2069, 0x0100, 0x68c0, 0xa005, 0x01f0, 0x080c,
+ 0x6581, 0x080c, 0x7834, 0x68c3, 0x0000, 0x080c, 0x7ca8, 0x7027,
+ 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120,
+ 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100, 0x6824, 0xd084,
+ 0x0110, 0x6827, 0x0001, 0x003e, 0x0020, 0x6003, 0x0009, 0x630a,
+ 0x04b8, 0x7014, 0xac36, 0x1110, 0x660c, 0x7616, 0x7010, 0xac36,
+ 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7012, 0x0010, 0x7013,
+ 0x0000, 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110, 0x7e0e, 0x0008,
+ 0x2678, 0x600f, 0x0000, 0x6010, 0x2068, 0x080c, 0x9596, 0x0188,
+ 0x601c, 0xa086, 0x0003, 0x1510, 0x6837, 0x0103, 0x6b4a, 0x6847,
+ 0x0000, 0x080c, 0x97fd, 0x080c, 0xabfa, 0x080c, 0x510c, 0x080c,
+ 0x9742, 0x080c, 0x974e, 0x080c, 0x7b88, 0x00ce, 0x0804, 0x690f,
+ 0x2c78, 0x600c, 0x2060, 0x0804, 0x690f, 0x012e, 0x000e, 0x001e,
+ 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601c, 0xa086,
+ 0x0006, 0x1128, 0x080c, 0xabfa, 0x080c, 0xa91f, 0x0c10, 0x601c,
+ 0xa086, 0x0002, 0x1128, 0x6004, 0xa086, 0x0085, 0x0968, 0x08c8,
+ 0x601c, 0xa086, 0x0005, 0x19a8, 0x6004, 0xa086, 0x0085, 0x0d50,
+ 0x0880, 0x00c6, 0x0006, 0x0126, 0x2091, 0x8000, 0xa280, 0xae34,
+ 0x2004, 0xa065, 0x0904, 0x6a32, 0x00f6, 0x00e6, 0x00d6, 0x0066,
+ 0x2071, 0xafc7, 0x6654, 0x7018, 0xac06, 0x1108, 0x761a, 0x701c,
+ 0xac06, 0x1130, 0x86ff, 0x1118, 0x7018, 0x701e, 0x0008, 0x761e,
+ 0x6058, 0xa07d, 0x0108, 0x7e56, 0xa6ed, 0x0000, 0x0110, 0x2f00,
+ 0x685a, 0x6057, 0x0000, 0x605b, 0x0000, 0x6000, 0xc0d4, 0xc0dc,
+ 0x6002, 0x080c, 0x4c07, 0x0904, 0x6a2e, 0x7624, 0x86ff, 0x05e8,
+ 0xa680, 0x0004, 0x2004, 0xad06, 0x15c0, 0x00d6, 0x2069, 0x0100,
+ 0x68c0, 0xa005, 0x0548, 0x080c, 0x6581, 0x080c, 0x7834, 0x68c3,
+ 0x0000, 0x080c, 0x7ca8, 0x7027, 0x0000, 0x0036, 0x2069, 0x0140,
+ 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803, 0x0100, 0x6803, 0x0000,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x00de, 0x00c6, 0x603c, 0xa005, 0x0110, 0x8001, 0x603e, 0x2660,
+ 0x080c, 0x974e, 0x00ce, 0x0048, 0x00de, 0x00c6, 0x2660, 0x6003,
+ 0x0009, 0x630a, 0x00ce, 0x0804, 0x69d9, 0x8dff, 0x0158, 0x6837,
+ 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x97fd, 0x080c, 0xabfa,
+ 0x080c, 0x510c, 0x080c, 0x7b88, 0x0804, 0x69d9, 0x006e, 0x00de,
+ 0x00ee, 0x00fe, 0x012e, 0x000e, 0x00ce, 0x0005, 0x0006, 0x0066,
+ 0x00c6, 0x00d6, 0x2031, 0x0000, 0x7814, 0xa065, 0x0904, 0x6a88,
+ 0x600c, 0x0006, 0x600f, 0x0000, 0x7824, 0xac06, 0x1540, 0x2069,
+ 0x0100, 0x68c0, 0xa005, 0x01f0, 0x080c, 0x6581, 0x080c, 0x7834,
+ 0x68c3, 0x0000, 0x080c, 0x7ca8, 0x7827, 0x0000, 0x0036, 0x2069,
+ 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803, 0x0100, 0x6803,
+ 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001,
+ 0x003e, 0x0028, 0x6003, 0x0009, 0x630a, 0x2c30, 0x00b0, 0x6010,
+ 0x2068, 0x080c, 0x9596, 0x0168, 0x601c, 0xa086, 0x0003, 0x11b8,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x510c, 0x080c,
+ 0x9742, 0x080c, 0x974e, 0x080c, 0x7b88, 0x000e, 0x0804, 0x6a3d,
+ 0x7e16, 0x7e12, 0x00de, 0x00ce, 0x006e, 0x000e, 0x0005, 0x601c,
+ 0xa086, 0x0006, 0x1118, 0x080c, 0xa91f, 0x0c58, 0x601c, 0xa086,
+ 0x0002, 0x1128, 0x6004, 0xa086, 0x0085, 0x09d0, 0x0c10, 0x601c,
+ 0xa086, 0x0005, 0x19f0, 0x6004, 0xa086, 0x0085, 0x0d60, 0x08c8,
+ 0x0006, 0x0066, 0x00c6, 0x00d6, 0x7818, 0xa065, 0x0904, 0x6b0e,
+ 0x6054, 0x0006, 0x6057, 0x0000, 0x605b, 0x0000, 0x6000, 0xc0d4,
+ 0xc0dc, 0x6002, 0x080c, 0x4c07, 0x0904, 0x6b0b, 0x7e24, 0x86ff,
+ 0x05e8, 0xa680, 0x0004, 0x2004, 0xad06, 0x15c0, 0x00d6, 0x2069,
+ 0x0100, 0x68c0, 0xa005, 0x0548, 0x080c, 0x6581, 0x080c, 0x7834,
+ 0x68c3, 0x0000, 0x080c, 0x7ca8, 0x7827, 0x0000, 0x0036, 0x2069,
+ 0x0140, 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803, 0x0100, 0x6803,
+ 0x0000, 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001,
+ 0x003e, 0x00de, 0x00c6, 0x603c, 0xa005, 0x0110, 0x8001, 0x603e,
+ 0x2660, 0x080c, 0x974e, 0x00ce, 0x0048, 0x00de, 0x00c6, 0x2660,
+ 0x6003, 0x0009, 0x630a, 0x00ce, 0x0804, 0x6aba, 0x8dff, 0x0138,
+ 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x510c, 0x080c,
+ 0x7b88, 0x0804, 0x6aba, 0x000e, 0x0804, 0x6aad, 0x781e, 0x781a,
+ 0x00de, 0x00ce, 0x006e, 0x000e, 0x0005, 0x00e6, 0x00d6, 0x0066,
+ 0x6000, 0xd0dc, 0x0188, 0x604c, 0xa06d, 0x0170, 0x6848, 0xa606,
+ 0x1158, 0x2071, 0xafc7, 0x7024, 0xa035, 0x0130, 0xa080, 0x0004,
+ 0x2004, 0xad06, 0x1108, 0x0021, 0x006e, 0x00de, 0x00ee, 0x0005,
+ 0x00f6, 0x2079, 0x0100, 0x78c0, 0xa005, 0x1138, 0x00c6, 0x2660,
+ 0x6003, 0x0009, 0x630a, 0x00ce, 0x04a0, 0x080c, 0x7834, 0x78c3,
+ 0x0000, 0x080c, 0x7ca8, 0x7027, 0x0000, 0x0036, 0x2079, 0x0140,
+ 0x7b04, 0xa384, 0x1000, 0x0120, 0x7803, 0x0100, 0x7803, 0x0000,
+ 0x2079, 0x0100, 0x7824, 0xd084, 0x0110, 0x7827, 0x0001, 0x080c,
+ 0x7ca8, 0x003e, 0x080c, 0x4c07, 0x00c6, 0x603c, 0xa005, 0x0110,
+ 0x8001, 0x603e, 0x2660, 0x080c, 0x8078, 0x00ce, 0x6837, 0x0103,
+ 0x6b4a, 0x6847, 0x0000, 0x080c, 0x97fd, 0x080c, 0x510c, 0x080c,
+ 0x7b88, 0x00fe, 0x0005, 0x00e6, 0x00c6, 0x2071, 0xafc7, 0x7004,
+ 0xa084, 0x0007, 0x0002, 0x6b85, 0x6b88, 0x6b9e, 0x6bb7, 0x6bf0,
+ 0x6b85, 0x6b83, 0x6b83, 0x080c, 0x14f6, 0x00ce, 0x00ee, 0x0005,
+ 0x7024, 0xa065, 0x0148, 0x7020, 0x8001, 0x7022, 0x600c, 0xa015,
+ 0x0150, 0x7216, 0x600f, 0x0000, 0x7007, 0x0000, 0x7027, 0x0000,
+ 0x00ce, 0x00ee, 0x0005, 0x7216, 0x7212, 0x0cb0, 0x6018, 0x2060,
+ 0x080c, 0x4c07, 0x6000, 0xc0dc, 0x6002, 0x7020, 0x8001, 0x7022,
+ 0x0120, 0x6054, 0xa015, 0x0140, 0x721e, 0x7007, 0x0000, 0x7027,
+ 0x0000, 0x00ce, 0x00ee, 0x0005, 0x7218, 0x721e, 0x0cb0, 0x7024,
+ 0xa065, 0x0598, 0x700c, 0xac06, 0x1160, 0x080c, 0x7b88, 0x600c,
+ 0xa015, 0x0120, 0x720e, 0x600f, 0x0000, 0x0428, 0x720e, 0x720a,
+ 0x0410, 0x7014, 0xac06, 0x1160, 0x080c, 0x7b88, 0x600c, 0xa015,
+ 0x0120, 0x7216, 0x600f, 0x0000, 0x00b0, 0x7216, 0x7212, 0x0098,
+ 0x6018, 0x2060, 0x080c, 0x4c07, 0x6000, 0xc0dc, 0x6002, 0x080c,
+ 0x7b88, 0x701c, 0xa065, 0x0138, 0x6054, 0xa015, 0x0110, 0x721e,
+ 0x0010, 0x7218, 0x721e, 0x7027, 0x0000, 0x00ce, 0x00ee, 0x0005,
+ 0x7024, 0xa065, 0x0140, 0x080c, 0x7b88, 0x600c, 0xa015, 0x0150,
+ 0x720e, 0x600f, 0x0000, 0x080c, 0x7ca8, 0x7027, 0x0000, 0x00ce,
+ 0x00ee, 0x0005, 0x720e, 0x720a, 0x0cb0, 0x00d6, 0x2069, 0xafc7,
+ 0x6830, 0xa084, 0x0003, 0x0002, 0x6c12, 0x6c14, 0x6c38, 0x6c10,
+ 0x080c, 0x14f6, 0x00de, 0x0005, 0x00c6, 0x6840, 0xa086, 0x0001,
+ 0x01b8, 0x683c, 0xa065, 0x0130, 0x600c, 0xa015, 0x0170, 0x6a3a,
+ 0x600f, 0x0000, 0x6833, 0x0000, 0x683f, 0x0000, 0x2011, 0xafe6,
+ 0x2013, 0x0000, 0x00ce, 0x00de, 0x0005, 0x683a, 0x6836, 0x0c90,
+ 0x6843, 0x0000, 0x6838, 0xa065, 0x0d68, 0x6003, 0x0003, 0x0c50,
+ 0x00c6, 0x6843, 0x0000, 0x6847, 0x0000, 0x683c, 0xa065, 0x0168,
+ 0x600c, 0xa015, 0x0130, 0x6a3a, 0x600f, 0x0000, 0x683f, 0x0000,
+ 0x0020, 0x683f, 0x0000, 0x683a, 0x6836, 0x00ce, 0x00de, 0x0005,
+ 0x00d6, 0x2069, 0xafc7, 0x6804, 0xa084, 0x0007, 0x0002, 0x6c61,
+ 0x6cfd, 0x6cfd, 0x6cfd, 0x6cfd, 0x6cff, 0x6c5f, 0x6c5f, 0x080c,
+ 0x14f6, 0x6820, 0xa005, 0x1110, 0x00de, 0x0005, 0x00c6, 0x680c,
+ 0xa065, 0x0150, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000, 0x080c,
+ 0x6d49, 0x00ce, 0x00de, 0x0005, 0x6814, 0xa065, 0x0150, 0x6807,
+ 0x0001, 0x6826, 0x682b, 0x0000, 0x080c, 0x6d49, 0x00ce, 0x00de,
+ 0x0005, 0x00e6, 0x0036, 0x6a1c, 0xa2f5, 0x0000, 0x0904, 0x6cf9,
+ 0x704c, 0xa00d, 0x0118, 0x7088, 0xa005, 0x01a0, 0x7054, 0xa075,
+ 0x0120, 0xa20e, 0x0904, 0x6cf9, 0x0028, 0x6818, 0xa20e, 0x0904,
+ 0x6cf9, 0x2070, 0x704c, 0xa00d, 0x0d88, 0x7088, 0xa005, 0x1d70,
+ 0x2e00, 0x681e, 0x733c, 0x7038, 0xa302, 0x1e40, 0x080c, 0x804f,
+ 0x0904, 0x6cf9, 0x8318, 0x733e, 0x6112, 0x2e10, 0x621a, 0xa180,
+ 0x0014, 0x2004, 0xa084, 0x00ff, 0x605a, 0xa180, 0x0014, 0x2003,
+ 0x0000, 0xa180, 0x0015, 0x2004, 0xa08a, 0x199a, 0x0210, 0x2001,
+ 0x1999, 0x8003, 0x801b, 0x831b, 0xa318, 0x6316, 0x003e, 0x00f6,
+ 0x2c78, 0x71a0, 0x2001, 0xad34, 0x2004, 0xd0ac, 0x1110, 0xd1bc,
+ 0x0150, 0x7100, 0xd1f4, 0x0120, 0x7114, 0xa18c, 0x00ff, 0x0040,
+ 0x2009, 0x0000, 0x0028, 0xa1e0, 0x2be6, 0x2c0d, 0xa18c, 0x00ff,
+ 0x2061, 0x0100, 0x619a, 0x080c, 0x736f, 0x7300, 0xc3dd, 0x7302,
+ 0x6807, 0x0002, 0x2f18, 0x6b26, 0x682b, 0x0000, 0x781f, 0x0003,
+ 0x7803, 0x0001, 0x7807, 0x0040, 0x00fe, 0x00ee, 0x00ce, 0x00de,
+ 0x0005, 0x003e, 0x00ee, 0x00ce, 0x0cd0, 0x00de, 0x0005, 0x00c6,
+ 0x680c, 0xa065, 0x0138, 0x6807, 0x0004, 0x6826, 0x682b, 0x0000,
+ 0x080c, 0x6d49, 0x00ce, 0x00de, 0x0005, 0x00f6, 0x00d6, 0x2069,
+ 0xafc7, 0x6830, 0xa086, 0x0000, 0x11c0, 0x2001, 0xad0c, 0x200c,
+ 0xd1bc, 0x1550, 0x6838, 0xa07d, 0x0180, 0x6833, 0x0001, 0x683e,
+ 0x6847, 0x0000, 0x0126, 0x00f6, 0x2091, 0x2400, 0x002e, 0x080c,
+ 0x1ee6, 0x1130, 0x012e, 0x080c, 0x76a5, 0x00de, 0x00fe, 0x0005,
+ 0x012e, 0xe000, 0x6843, 0x0000, 0x7803, 0x0002, 0x780c, 0xa015,
+ 0x0140, 0x6a3a, 0x780f, 0x0000, 0x6833, 0x0000, 0x683f, 0x0000,
+ 0x0c60, 0x683a, 0x6836, 0x0cc0, 0xc1bc, 0x2102, 0x080c, 0x57d1,
+ 0x0888, 0x601c, 0xa084, 0x000f, 0x000b, 0x0005, 0x6d57, 0x6d5c,
+ 0x7210, 0x732c, 0x6d5c, 0x7210, 0x732c, 0x6d57, 0x6d5c, 0x080c,
+ 0x6b73, 0x080c, 0x6c50, 0x0005, 0x0156, 0x0136, 0x0146, 0x00c6,
+ 0x00f6, 0x6004, 0xa08a, 0x0080, 0x1a0c, 0x14f6, 0x6118, 0x2178,
+ 0x79a0, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110, 0xd1bc, 0x0150,
+ 0x7900, 0xd1f4, 0x0120, 0x7914, 0xa18c, 0x00ff, 0x0040, 0x2009,
+ 0x0000, 0x0028, 0xa1f8, 0x2be6, 0x2f0d, 0xa18c, 0x00ff, 0x2c78,
+ 0x2061, 0x0100, 0x619a, 0xa08a, 0x0040, 0x1a04, 0x6dd0, 0x0033,
+ 0x00fe, 0x00ce, 0x014e, 0x013e, 0x015e, 0x0005, 0x6e7c, 0x6ec7,
+ 0x6ef4, 0x6fc1, 0x6fef, 0x6ff7, 0x701d, 0x702e, 0x703f, 0x7047,
+ 0x705d, 0x7047, 0x70b7, 0x702e, 0x70d8, 0x70e0, 0x703f, 0x70e0,
+ 0x70f1, 0x6dce, 0x6dce, 0x6dce, 0x6dce, 0x6dce, 0x6dce, 0x6dce,
+ 0x6dce, 0x6dce, 0x6dce, 0x6dce, 0x790d, 0x7932, 0x7947, 0x796a,
+ 0x798b, 0x701d, 0x6dce, 0x701d, 0x7047, 0x6dce, 0x6ef4, 0x6fc1,
+ 0x6dce, 0x7daa, 0x7047, 0x6dce, 0x7dca, 0x7047, 0x6dce, 0x703f,
+ 0x6e75, 0x6de0, 0x6dce, 0x7def, 0x7e64, 0x7f3b, 0x6dce, 0x7f4c,
+ 0x7018, 0x7f68, 0x6dce, 0x79a0, 0x7fc3, 0x6dce, 0x080c, 0x14f6,
+ 0x2100, 0x0033, 0x00fe, 0x00ce, 0x014e, 0x013e, 0x015e, 0x0005,
+ 0x6dde, 0x6dde, 0x6dde, 0x6e14, 0x6e32, 0x6e48, 0x080c, 0x14f6,
+ 0x00d6, 0x20a1, 0x020b, 0x080c, 0x710e, 0x7810, 0x2068, 0x20a3,
+ 0x2414, 0x20a3, 0x0018, 0x20a3, 0x0800, 0x683c, 0x20a2, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x6850,
+ 0x20a2, 0x6854, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3,
+ 0x0018, 0x080c, 0x7821, 0x00de, 0x0005, 0x00d6, 0x7818, 0x2068,
+ 0x68a0, 0x2069, 0xad00, 0x6ad0, 0xd2ac, 0x1110, 0xd0bc, 0x0110,
+ 0xa085, 0x0001, 0x00de, 0x0005, 0x00d6, 0x20a1, 0x020b, 0x080c,
+ 0x710e, 0x20a3, 0x0500, 0x20a3, 0x0000, 0x7810, 0xa0e8, 0x000f,
+ 0x6808, 0x20a2, 0x680c, 0x20a2, 0x6810, 0x20a2, 0x6814, 0x20a2,
+ 0x6818, 0x20a2, 0x681c, 0x20a2, 0x60c3, 0x0010, 0x080c, 0x7821,
+ 0x00de, 0x0005, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x710e,
+ 0x20a3, 0x7800, 0x20a3, 0x0000, 0x7808, 0x8007, 0x20a2, 0x20a3,
+ 0x0000, 0x60c3, 0x0008, 0x080c, 0x7821, 0x014e, 0x015e, 0x0005,
+ 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0200,
+ 0x20a3, 0x0000, 0x20a3, 0xdf10, 0x20a3, 0x0034, 0x2099, 0xad05,
+ 0x20a9, 0x0004, 0x53a6, 0x2099, 0xad01, 0x20a9, 0x0004, 0x53a6,
+ 0x2099, 0xafad, 0x20a9, 0x001a, 0x3304, 0x8007, 0x20a2, 0x9398,
+ 0x1f04, 0x6e64, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x004c,
+ 0x080c, 0x7821, 0x014e, 0x015e, 0x0005, 0x2001, 0xad14, 0x2004,
+ 0x609a, 0x080c, 0x7821, 0x0005, 0x20a1, 0x020b, 0x080c, 0x710e,
+ 0x20a3, 0x5200, 0x20a3, 0x0000, 0x00d6, 0x2069, 0xad51, 0x6804,
+ 0xd084, 0x0150, 0x6828, 0x20a3, 0x0000, 0x0016, 0x080c, 0x268a,
+ 0x21a2, 0x001e, 0x00de, 0x0028, 0x00de, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a9, 0x0004, 0x2099, 0xad05, 0x53a6, 0x20a9, 0x0004,
+ 0x2099, 0xad01, 0x53a6, 0x2001, 0xad34, 0x2004, 0xd0ac, 0x1138,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xa082, 0x007f, 0x0238, 0x2001,
+ 0xad1b, 0x20a6, 0x2001, 0xad1c, 0x20a6, 0x0040, 0x20a3, 0x0000,
+ 0x2001, 0xad14, 0x2004, 0xa084, 0x00ff, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x001c, 0x080c, 0x7821, 0x0005, 0x20a1,
+ 0x020b, 0x080c, 0x710e, 0x20a3, 0x0500, 0x20a3, 0x0000, 0x2001,
+ 0xad34, 0x2004, 0xd0ac, 0x1138, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0xa082, 0x007f, 0x0238, 0x2001, 0xad1b, 0x20a6, 0x2001, 0xad1c,
+ 0x20a6, 0x0040, 0x20a3, 0x0000, 0x2001, 0xad14, 0x2004, 0xa084,
+ 0x00ff, 0x20a2, 0x20a9, 0x0004, 0x2099, 0xad05, 0x53a6, 0x60c3,
+ 0x0010, 0x080c, 0x7821, 0x0005, 0x20a1, 0x020b, 0x080c, 0x710e,
+ 0x00c6, 0x7818, 0x2060, 0x2001, 0x0000, 0x080c, 0x5037, 0x00ce,
+ 0x7818, 0xa080, 0x0028, 0x2004, 0xa086, 0x007e, 0x1130, 0x20a3,
+ 0x0400, 0x620c, 0xc2b4, 0x620e, 0x0010, 0x20a3, 0x0300, 0x20a3,
+ 0x0000, 0x7818, 0xa080, 0x0028, 0x2004, 0xa086, 0x007e, 0x1904,
+ 0x6f83, 0x2001, 0xad34, 0x2004, 0xd0a4, 0x01c8, 0x2099, 0xaf8d,
+ 0x33a6, 0x9398, 0x20a3, 0x0000, 0x9398, 0x3304, 0xa084, 0x2000,
+ 0x20a2, 0x9398, 0x33a6, 0x9398, 0x20a3, 0x0000, 0x9398, 0x2001,
+ 0x2710, 0x20a2, 0x9398, 0x33a6, 0x9398, 0x33a6, 0x00d0, 0x2099,
+ 0xaf8d, 0x33a6, 0x9398, 0x33a6, 0x9398, 0x3304, 0x080c, 0x574f,
+ 0x1118, 0xa084, 0x37ff, 0x0010, 0xa084, 0x3fff, 0x20a2, 0x9398,
+ 0x33a6, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a9, 0x0004, 0x2099, 0xad05, 0x53a6, 0x20a9, 0x0004,
+ 0x2099, 0xad01, 0x53a6, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x1f04,
+ 0x6f5d, 0x20a9, 0x0008, 0x20a3, 0x0000, 0x1f04, 0x6f63, 0x2099,
+ 0xaf95, 0x3304, 0xc0dd, 0x20a2, 0x2001, 0xad71, 0x2004, 0xd0e4,
+ 0x0158, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x9398, 0x9398, 0x9398,
+ 0x33a6, 0x20a9, 0x0004, 0x0010, 0x20a9, 0x0007, 0x20a3, 0x0000,
+ 0x1f04, 0x6f7e, 0x0468, 0x2001, 0xad34, 0x2004, 0xd0a4, 0x0140,
+ 0x2001, 0xaf8e, 0x2004, 0x60e3, 0x0000, 0x080c, 0x26cb, 0x60e2,
+ 0x2099, 0xaf8d, 0x20a9, 0x0008, 0x53a6, 0x20a9, 0x0004, 0x2099,
+ 0xad05, 0x53a6, 0x20a9, 0x0004, 0x2099, 0xad01, 0x53a6, 0x20a9,
+ 0x0008, 0x20a3, 0x0000, 0x1f04, 0x6fa1, 0x20a9, 0x0008, 0x20a3,
+ 0x0000, 0x1f04, 0x6fa7, 0x2099, 0xaf95, 0x20a9, 0x0008, 0x53a6,
+ 0x20a9, 0x0008, 0x20a3, 0x0000, 0x1f04, 0x6fb2, 0x20a9, 0x000a,
+ 0x20a3, 0x0000, 0x1f04, 0x6fb8, 0x60c3, 0x0074, 0x080c, 0x7821,
+ 0x0005, 0x20a1, 0x020b, 0x080c, 0x710e, 0x20a3, 0x2010, 0x20a3,
+ 0x0014, 0x20a3, 0x0800, 0x20a3, 0x2000, 0xa006, 0x20a2, 0x20a2,
+ 0x20a2, 0x20a2, 0x20a2, 0x00f6, 0x2079, 0xad51, 0x7904, 0x00fe,
+ 0xd1ac, 0x1110, 0xa085, 0x0020, 0xd1a4, 0x0110, 0xa085, 0x0010,
+ 0xa085, 0x0002, 0x00d6, 0x0804, 0x7099, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0014, 0x080c, 0x7821, 0x0005, 0x20a1,
+ 0x020b, 0x080c, 0x710e, 0x20a3, 0x5000, 0x0804, 0x6f0f, 0x20a1,
+ 0x020b, 0x080c, 0x710e, 0x20a3, 0x2110, 0x20a3, 0x0014, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x080c, 0x7821, 0x0005,
+ 0x20a1, 0x020b, 0x080c, 0x71a2, 0x0020, 0x20a1, 0x020b, 0x080c,
+ 0x71aa, 0x20a3, 0x0200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x0004, 0x080c, 0x7821, 0x0005, 0x20a1, 0x020b,
+ 0x080c, 0x71aa, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0003,
+ 0x20a3, 0x2a00, 0x60c3, 0x0008, 0x080c, 0x7821, 0x0005, 0x20a1,
+ 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0200, 0x0804, 0x6f0f, 0x20a1,
+ 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x7828,
+ 0xa005, 0x0110, 0x20a2, 0x0010, 0x20a3, 0x0003, 0x7810, 0x20a2,
+ 0x60c3, 0x0008, 0x080c, 0x7821, 0x0005, 0x00d6, 0x20a1, 0x020b,
+ 0x080c, 0x71aa, 0x20a3, 0x0210, 0x20a3, 0x0014, 0x20a3, 0x0800,
+ 0x7818, 0x2068, 0x6894, 0xa086, 0x0014, 0x1178, 0x6998, 0xa184,
+ 0xc000, 0x1140, 0xd1ec, 0x0118, 0x20a3, 0x2100, 0x0040, 0x20a3,
+ 0x0100, 0x0028, 0x20a3, 0x0400, 0x0010, 0x20a3, 0x0700, 0xa006,
+ 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x00f6, 0x2079, 0xad51,
+ 0x7904, 0x00fe, 0xd1ac, 0x1110, 0xa085, 0x0020, 0xd1a4, 0x0110,
+ 0xa085, 0x0010, 0x2009, 0xad73, 0x210c, 0xd184, 0x1110, 0xa085,
+ 0x0002, 0x0026, 0x2009, 0xad71, 0x210c, 0xd1e4, 0x0130, 0xc0c5,
+ 0xa094, 0x0030, 0xa296, 0x0010, 0x0140, 0xd1ec, 0x0130, 0xa094,
+ 0x0030, 0xa296, 0x0010, 0x0108, 0xc0bd, 0x002e, 0x20a2, 0x20a2,
+ 0x20a2, 0x60c3, 0x0014, 0x080c, 0x7821, 0x00de, 0x0005, 0x20a1,
+ 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0210, 0x20a3, 0x0014, 0x20a3,
+ 0x0000, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x0014, 0x080c, 0x7821, 0x0005,
+ 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0200, 0x0804, 0x6e82,
+ 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x20a3, 0x0003, 0x20a3, 0x2a00, 0x60c3, 0x0008, 0x080c, 0x7821,
+ 0x0005, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x20a1, 0x020b, 0x080c,
+ 0x71aa, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0x000b, 0x20a3,
+ 0x0000, 0x60c3, 0x0008, 0x080c, 0x7821, 0x0005, 0x0026, 0x0036,
+ 0x0046, 0x2019, 0x3200, 0x2021, 0x0800, 0x0038, 0x0026, 0x0036,
+ 0x0046, 0x2019, 0x2200, 0x2021, 0x0100, 0x20e1, 0x9080, 0x20e1,
+ 0x4000, 0x7818, 0xa080, 0x0028, 0x2014, 0xa286, 0x007e, 0x11a0,
+ 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffe, 0x20a3, 0x0000, 0x2011,
+ 0xad14, 0x2214, 0x2001, 0xaf9d, 0x2004, 0xa005, 0x0118, 0x2011,
+ 0xad1c, 0x2214, 0x22a2, 0x04d0, 0xa286, 0x007f, 0x1138, 0x00d6,
+ 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffd, 0x00c8, 0x2001, 0xad34,
+ 0x2004, 0xd0ac, 0x1110, 0xd2bc, 0x01c8, 0xa286, 0x0080, 0x00d6,
+ 0x1130, 0xa385, 0x00ff, 0x20a2, 0x20a3, 0xfffc, 0x0040, 0xa2e8,
+ 0xae34, 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x2069,
+ 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0080, 0x00d6, 0xa2e8,
+ 0xae34, 0x2d6c, 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x00de,
+ 0x20a3, 0x0000, 0x2011, 0xad14, 0x2214, 0x22a2, 0xa485, 0x0029,
+ 0x20a2, 0x004e, 0x003e, 0x20a3, 0x0000, 0x080c, 0x7810, 0x22a2,
+ 0x20a3, 0x0000, 0x2fa2, 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x002e, 0x0005, 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000,
+ 0x20a3, 0x02ff, 0x2011, 0xfffc, 0x22a2, 0x00d6, 0x2069, 0xad1b,
+ 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x20a3, 0x2029, 0x20a3, 0x0000,
+ 0x08e0, 0x20a3, 0x0100, 0x20a3, 0x0000, 0x20a3, 0xfc02, 0x20a3,
+ 0x0000, 0x0005, 0x0026, 0x0036, 0x0046, 0x2019, 0x3300, 0x2021,
+ 0x0800, 0x0038, 0x0026, 0x0036, 0x0046, 0x2019, 0x2300, 0x2021,
+ 0x0100, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1118, 0xa092, 0x007e,
+ 0x02d8, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa305, 0x20a2,
+ 0x6814, 0x20a2, 0x6810, 0xa005, 0x1140, 0x6814, 0xa005, 0x1128,
+ 0x20a3, 0x00ff, 0x20a3, 0xfffe, 0x0028, 0x2069, 0xad1b, 0x2da6,
+ 0x8d68, 0x2da6, 0x00de, 0x0080, 0x00d6, 0xa0e8, 0xae34, 0x2d6c,
+ 0x6810, 0xa305, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000,
+ 0x2011, 0xad14, 0x2214, 0x22a2, 0xa485, 0x0098, 0x20a2, 0x20a3,
+ 0x0000, 0x004e, 0x003e, 0x080c, 0x7810, 0x22a2, 0x20a3, 0x0000,
+ 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x002e,
+ 0x0005, 0x080c, 0x7810, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2,
+ 0x7810, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x002e, 0x0005,
+ 0x00c6, 0x00f6, 0x6004, 0xa08a, 0x0085, 0x0a0c, 0x14f6, 0xa08a,
+ 0x008c, 0x1a0c, 0x14f6, 0x6118, 0x2178, 0x79a0, 0x2011, 0xad34,
+ 0x2214, 0xd2ac, 0x1110, 0xd1bc, 0x0150, 0x7900, 0xd1f4, 0x0120,
+ 0x7914, 0xa18c, 0x00ff, 0x0040, 0x2009, 0x0000, 0x0028, 0xa1f8,
+ 0x2be6, 0x2f0d, 0xa18c, 0x00ff, 0x2c78, 0x2061, 0x0100, 0x619a,
+ 0xa082, 0x0085, 0x001b, 0x00fe, 0x00ce, 0x0005, 0x7247, 0x7251,
+ 0x726c, 0x7245, 0x7245, 0x7245, 0x7247, 0x080c, 0x14f6, 0x0146,
+ 0x20a1, 0x020b, 0x04a1, 0x60c3, 0x0000, 0x080c, 0x7821, 0x014e,
+ 0x0005, 0x0146, 0x20a1, 0x020b, 0x080c, 0x72b8, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x7808, 0x20a2, 0x7810, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x000c,
+ 0x080c, 0x7821, 0x014e, 0x0005, 0x0146, 0x20a1, 0x020b, 0x080c,
+ 0x72f2, 0x20a3, 0x0003, 0x20a3, 0x0300, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x60c3, 0x0004, 0x080c, 0x7821, 0x014e, 0x0005, 0x0026,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1118, 0xa092, 0x007e, 0x0288,
+ 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x8100, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de,
+ 0x0088, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x8100,
+ 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xad14,
+ 0x2214, 0x22a2, 0x20a3, 0x0009, 0x20a3, 0x0000, 0x0804, 0x7175,
+ 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1118, 0xa092, 0x007e,
+ 0x0288, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x8400,
+ 0x20a2, 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6,
+ 0x00de, 0x0088, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085,
+ 0x8400, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011,
+ 0xad14, 0x2214, 0x22a2, 0x2001, 0x0099, 0x20a2, 0x20a3, 0x0000,
+ 0x0804, 0x7201, 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1118,
+ 0xa092, 0x007e, 0x0288, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810,
+ 0xa085, 0x8500, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6,
+ 0x8d68, 0x2da6, 0x00de, 0x0088, 0x00d6, 0xa0e8, 0xae34, 0x2d6c,
+ 0x6810, 0xa085, 0x8500, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3,
+ 0x0000, 0x2011, 0xad14, 0x2214, 0x22a2, 0x2001, 0x0099, 0x20a2,
+ 0x20a3, 0x0000, 0x0804, 0x7201, 0x00c6, 0x00f6, 0x2c78, 0x7804,
+ 0xa08a, 0x0040, 0x0a0c, 0x14f6, 0xa08a, 0x0053, 0x1a0c, 0x14f6,
+ 0x7918, 0x2160, 0x61a0, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110,
+ 0xd1bc, 0x0150, 0x6100, 0xd1f4, 0x0120, 0x6114, 0xa18c, 0x00ff,
+ 0x0040, 0x2009, 0x0000, 0x0028, 0xa1e0, 0x2be6, 0x2c0d, 0xa18c,
+ 0x00ff, 0x2061, 0x0100, 0x619a, 0xa082, 0x0040, 0x001b, 0x00fe,
+ 0x00ce, 0x0005, 0x736f, 0x747b, 0x7418, 0x761a, 0x736d, 0x736d,
+ 0x736d, 0x736d, 0x736d, 0x736d, 0x736d, 0x7b41, 0x7b51, 0x7b61,
+ 0x7b71, 0x736d, 0x7f79, 0x736d, 0x7b30, 0x080c, 0x14f6, 0x00d6,
+ 0x0156, 0x0146, 0x780b, 0xffff, 0x20a1, 0x020b, 0x080c, 0x73cf,
+ 0x7910, 0x2168, 0x6948, 0x7952, 0x21a2, 0xa016, 0x22a2, 0x22a2,
+ 0x22a2, 0x694c, 0xa184, 0x000f, 0x1118, 0x2001, 0x0005, 0x0040,
+ 0xd184, 0x0118, 0x2001, 0x0004, 0x0018, 0xa084, 0x0006, 0x8004,
+ 0x0016, 0x2008, 0x7858, 0xa084, 0x00ff, 0x8007, 0xa105, 0x001e,
+ 0x20a2, 0xd1ac, 0x0118, 0x20a3, 0x0002, 0x0048, 0xd1b4, 0x0118,
+ 0x20a3, 0x0001, 0x0020, 0x20a3, 0x0000, 0x2230, 0x0010, 0x6a80,
+ 0x6e7c, 0x20a9, 0x0008, 0x0136, 0xad88, 0x0017, 0x2198, 0x20a1,
+ 0x021b, 0x53a6, 0x013e, 0x20a1, 0x020b, 0x22a2, 0x26a2, 0x60c3,
+ 0x0020, 0x20e1, 0x9080, 0x6014, 0xa084, 0x0004, 0xa085, 0x0009,
+ 0x6016, 0x2001, 0xafe3, 0x2003, 0x07d0, 0x2001, 0xafe2, 0x2003,
+ 0x0009, 0x080c, 0x17bf, 0x014e, 0x015e, 0x00de, 0x0005, 0x20e1,
+ 0x9080, 0x20e1, 0x4000, 0x7a18, 0xa280, 0x0023, 0x2014, 0x8210,
+ 0xa294, 0x00ff, 0x2202, 0x8217, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0x2019, 0xad34, 0x231c, 0xd3ac, 0x1110, 0xd0bc, 0x0188, 0x00d6,
+ 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0600, 0x20a2, 0x6814,
+ 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088,
+ 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0600, 0x20a2,
+ 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2009, 0xad14, 0x210c,
+ 0x21a2, 0x20a3, 0x0829, 0x20a3, 0x0000, 0x22a2, 0x20a3, 0x0000,
+ 0x2fa2, 0x20a3, 0xffff, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x0005,
+ 0x00d6, 0x0156, 0x0136, 0x0146, 0x20a1, 0x020b, 0x00c1, 0x7810,
+ 0x2068, 0x6860, 0x20a2, 0x685c, 0x20a2, 0x6880, 0x20a2, 0x687c,
+ 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x60c3, 0x000c,
+ 0x080c, 0x7821, 0x014e, 0x013e, 0x015e, 0x00de, 0x0005, 0x0026,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028, 0x2004,
+ 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188, 0x00d6,
+ 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0500, 0x20a2, 0x6814,
+ 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de, 0x0088,
+ 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0500, 0x20a2,
+ 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xad14, 0x2214,
+ 0x22a2, 0x20a3, 0x0889, 0x20a3, 0x0000, 0x080c, 0x7810, 0x22a2,
+ 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x002e, 0x0005, 0x00d6, 0x0156, 0x0136, 0x0146, 0x7810,
+ 0xa06d, 0x080c, 0x5025, 0x0148, 0x684c, 0xa084, 0x2020, 0xa086,
+ 0x2020, 0x1118, 0x7820, 0xc0cd, 0x7822, 0x20a1, 0x020b, 0x080c,
+ 0x75d0, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x7810,
+ 0xa084, 0xf000, 0x1130, 0x7810, 0xa084, 0x0700, 0x8007, 0x0043,
+ 0x0010, 0xa006, 0x002b, 0x014e, 0x013e, 0x015e, 0x00de, 0x0005,
+ 0x74b2, 0x7547, 0x7550, 0x7579, 0x758c, 0x75a7, 0x75b0, 0x74b0,
+ 0x080c, 0x14f6, 0x0016, 0x0036, 0x694c, 0xa18c, 0x0003, 0x0118,
+ 0xa186, 0x0003, 0x1170, 0x6b78, 0x7820, 0xd0cc, 0x0108, 0xc3e5,
+ 0x23a2, 0x6868, 0x20a2, 0x6864, 0x20a2, 0x003e, 0x001e, 0x0804,
+ 0x7583, 0xa186, 0x0001, 0x190c, 0x14f6, 0x6b78, 0x7820, 0xd0cc,
+ 0x0108, 0xc3e5, 0x23a2, 0x6868, 0x20a2, 0x6864, 0x20a2, 0x22a2,
+ 0x6874, 0x20a2, 0x22a2, 0x687c, 0x20a2, 0x2009, 0x0018, 0xa384,
+ 0x0300, 0x0904, 0x7541, 0xd3c4, 0x0110, 0x687c, 0xa108, 0xd3cc,
+ 0x0110, 0x6874, 0xa108, 0x0156, 0x20a9, 0x000d, 0xad80, 0x0020,
+ 0x201c, 0x831f, 0x23a2, 0x8000, 0x1f04, 0x74f0, 0x015e, 0x22a2,
+ 0x22a2, 0x22a2, 0xa184, 0x0003, 0x0904, 0x7541, 0x20a1, 0x020b,
+ 0x20e1, 0x9080, 0x20e1, 0x4000, 0x0006, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188,
+ 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de,
+ 0x0088, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0700,
+ 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xad14,
+ 0x2214, 0x22a2, 0x000e, 0x7b20, 0xd3cc, 0x0118, 0x20a3, 0x0889,
+ 0x0010, 0x20a3, 0x0898, 0x20a2, 0x080c, 0x7810, 0x22a2, 0x20a3,
+ 0x0000, 0x61c2, 0x003e, 0x001e, 0x080c, 0x7821, 0x0005, 0x2011,
+ 0x0008, 0x7820, 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0xa016, 0x0488,
+ 0x2011, 0x0302, 0x7820, 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0xa016,
+ 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x0012, 0x22a2, 0x20a3, 0x0008,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x20a3, 0x7000, 0x20a3, 0x0500,
+ 0x22a2, 0x20a3, 0x000a, 0x22a2, 0x22a2, 0x20a3, 0x2500, 0x22a2,
+ 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0032, 0x080c, 0x7821,
+ 0x0005, 0x2011, 0x0028, 0x7820, 0xd0cc, 0x0108, 0xc2e5, 0x22a2,
+ 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x22a2, 0x60c3,
+ 0x0018, 0x080c, 0x7821, 0x0005, 0x2011, 0x0100, 0x7820, 0xd0cc,
+ 0x0108, 0xc2e5, 0x22a2, 0xa016, 0x22a2, 0x22a2, 0x22a2, 0x22a2,
+ 0x22a2, 0x20a3, 0x0008, 0x22a2, 0x7854, 0xa084, 0x00ff, 0x20a2,
+ 0x22a2, 0x22a2, 0x60c3, 0x0020, 0x080c, 0x7821, 0x0005, 0x2011,
+ 0x0008, 0x7820, 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0xa016, 0x0888,
+ 0x0036, 0x7b10, 0xa384, 0xff00, 0x7812, 0xa384, 0x00ff, 0x8001,
+ 0x1138, 0x7820, 0xd0cc, 0x0108, 0xc2e5, 0x22a2, 0x003e, 0x0808,
+ 0x0046, 0x2021, 0x0800, 0x0006, 0x7820, 0xd0cc, 0x000e, 0x0108,
+ 0xc4e5, 0x24a2, 0x004e, 0x22a2, 0x20a2, 0x003e, 0x0804, 0x7583,
+ 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188,
+ 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0700, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de,
+ 0x0088, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0700,
+ 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xad14,
+ 0x2214, 0x22a2, 0x7820, 0xd0cc, 0x0118, 0x20a3, 0x0889, 0x0010,
+ 0x20a3, 0x0898, 0x20a3, 0x0000, 0x080c, 0x7810, 0x22a2, 0x20a3,
+ 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x002e, 0x0005, 0x00d6, 0x0156, 0x0136, 0x0146, 0x0016, 0x0036,
+ 0x7810, 0xa084, 0x0700, 0x8007, 0x003b, 0x003e, 0x001e, 0x014e,
+ 0x013e, 0x015e, 0x00de, 0x0005, 0x7634, 0x7634, 0x7636, 0x7634,
+ 0x7634, 0x7634, 0x7658, 0x7634, 0x080c, 0x14f6, 0x7910, 0xa18c,
+ 0xf8ff, 0xa18d, 0x0600, 0x7912, 0x20a1, 0x020b, 0x2009, 0x0003,
+ 0x00f9, 0x00d6, 0x2069, 0xad51, 0x6804, 0xd0bc, 0x0130, 0x682c,
+ 0xa084, 0x00ff, 0x8007, 0x20a2, 0x0010, 0x20a3, 0x3f00, 0x00de,
+ 0x22a2, 0x22a2, 0x22a2, 0x60c3, 0x0001, 0x080c, 0x7821, 0x0005,
+ 0x20a1, 0x020b, 0x2009, 0x0003, 0x0019, 0x20a3, 0x7f00, 0x0c80,
+ 0x0026, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818, 0xa080, 0x0028,
+ 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110, 0xd0bc, 0x0188,
+ 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0100, 0x20a2,
+ 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6, 0x00de,
+ 0x0088, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085, 0x0100,
+ 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000, 0x2011, 0xad14,
+ 0x2214, 0x22a2, 0x20a3, 0x0888, 0xa18d, 0x0008, 0x21a2, 0x080c,
+ 0x7810, 0x22a2, 0x20a3, 0x0000, 0x7a08, 0x22a2, 0x2fa2, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x002e, 0x0005, 0x00e6, 0x00d6, 0x00c6,
+ 0x0056, 0x0046, 0x0036, 0x2061, 0x0100, 0x2071, 0xad00, 0x7150,
+ 0x7818, 0x2068, 0x68a0, 0x2028, 0x76d0, 0xd6ac, 0x1130, 0xd0bc,
+ 0x1120, 0x6910, 0x6a14, 0x7450, 0x0020, 0x6910, 0x6a14, 0x736c,
+ 0x7470, 0x781c, 0xa0be, 0x0006, 0x0904, 0x775b, 0xa0be, 0x000a,
+ 0x15e8, 0xa185, 0x0200, 0x6062, 0x6266, 0x636a, 0x646e, 0x6073,
+ 0x2029, 0x6077, 0x0000, 0x688c, 0x8000, 0xa084, 0x00ff, 0x688e,
+ 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6082, 0x7808, 0x6086,
+ 0x7810, 0x2070, 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6,
+ 0x7008, 0x60ca, 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000,
+ 0x609f, 0x0000, 0x080c, 0x8014, 0x2009, 0x07d0, 0x60c4, 0xa084,
+ 0xfff0, 0xa005, 0x0110, 0x2009, 0x1b58, 0x080c, 0x6586, 0x003e,
+ 0x004e, 0x005e, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x70d0, 0xd0ac,
+ 0x1110, 0xd5bc, 0x0138, 0xa185, 0x0100, 0x6062, 0x6266, 0x636a,
+ 0x646e, 0x0038, 0xa185, 0x0100, 0x6062, 0x6266, 0x606b, 0x0000,
+ 0x646e, 0x6073, 0x0809, 0x6077, 0x0008, 0x688c, 0x8000, 0xa084,
+ 0x00ff, 0x688e, 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6082,
+ 0x7808, 0x6086, 0x7810, 0x2070, 0x7014, 0x608a, 0x7010, 0x608e,
+ 0x700c, 0x60c6, 0x7008, 0x60ca, 0x686c, 0x60ce, 0x60af, 0x95d5,
+ 0x60d7, 0x0000, 0xa582, 0x0080, 0x0248, 0x6a00, 0xd2f4, 0x0120,
+ 0x6a14, 0xa294, 0x00ff, 0x0010, 0x2011, 0x0000, 0x629e, 0x080c,
+ 0x8014, 0x2009, 0x07d0, 0x60c4, 0xa084, 0xfff0, 0xa005, 0x0110,
+ 0x2009, 0x1b58, 0x080c, 0x6586, 0x003e, 0x004e, 0x005e, 0x00ce,
+ 0x00de, 0x00ee, 0x0005, 0x7810, 0x2070, 0x704c, 0xa084, 0x0003,
+ 0xa086, 0x0002, 0x0904, 0x77b1, 0x2001, 0xad34, 0x2004, 0xd0ac,
+ 0x1110, 0xd5bc, 0x0138, 0xa185, 0x0100, 0x6062, 0x6266, 0x636a,
+ 0x646e, 0x0038, 0xa185, 0x0100, 0x6062, 0x6266, 0x606b, 0x0000,
+ 0x646e, 0x6073, 0x0880, 0x6077, 0x0008, 0x688c, 0x8000, 0xa084,
+ 0x00ff, 0x688e, 0x8007, 0x607a, 0x7834, 0x607e, 0x2f00, 0x6086,
+ 0x7808, 0x6082, 0x7060, 0x608a, 0x705c, 0x608e, 0x7080, 0x60c6,
+ 0x707c, 0x60ca, 0x707c, 0x792c, 0xa108, 0x792e, 0x7080, 0x7928,
+ 0xa109, 0x792a, 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000,
+ 0xa582, 0x0080, 0x0248, 0x6a00, 0xd2f4, 0x0120, 0x6a14, 0xa294,
+ 0x00ff, 0x0010, 0x2011, 0x0000, 0x629e, 0x080c, 0x8011, 0x0804,
+ 0x7749, 0x2001, 0xad34, 0x2004, 0xd0ac, 0x1110, 0xd5bc, 0x0138,
+ 0xa185, 0x0700, 0x6062, 0x6266, 0x636a, 0x646e, 0x0038, 0xa185,
+ 0x0700, 0x6062, 0x6266, 0x606b, 0x0000, 0x646e, 0x080c, 0x5025,
+ 0x0180, 0x00d6, 0x7810, 0xa06d, 0x684c, 0x00de, 0xa084, 0x2020,
+ 0xa086, 0x2020, 0x1130, 0x7820, 0xc0cd, 0x7822, 0x6073, 0x0889,
+ 0x0010, 0x6073, 0x0898, 0x6077, 0x0000, 0x688c, 0x8000, 0xa084,
+ 0x00ff, 0x688e, 0x8007, 0x607a, 0x607f, 0x0000, 0x2f00, 0x6086,
+ 0x7808, 0x6082, 0x7014, 0x608a, 0x7010, 0x608e, 0x700c, 0x60c6,
+ 0x7008, 0x60ca, 0x686c, 0x60ce, 0x60af, 0x95d5, 0x60d7, 0x0000,
+ 0xa582, 0x0080, 0x0248, 0x6a00, 0xd2f4, 0x0120, 0x6a14, 0xa294,
+ 0x00ff, 0x0010, 0x2011, 0x0000, 0x629e, 0x7820, 0xd0cc, 0x0120,
+ 0x080c, 0x8014, 0x0804, 0x7749, 0x080c, 0x8011, 0x0804, 0x7749,
+ 0x7a18, 0xa280, 0x0023, 0x2014, 0x8210, 0xa294, 0x00ff, 0x2202,
+ 0x8217, 0x0005, 0x00d6, 0x2069, 0xafc7, 0x6843, 0x0001, 0x00de,
+ 0x0005, 0x20e1, 0x9080, 0x60a3, 0x0056, 0x60a7, 0x9575, 0x0019,
+ 0x080c, 0x6578, 0x0005, 0x0006, 0x6014, 0xa084, 0x0004, 0xa085,
+ 0x0009, 0x6016, 0x000e, 0x0005, 0x0006, 0x00c6, 0x2061, 0x0100,
+ 0x6014, 0xa084, 0x0004, 0xa085, 0x0008, 0x6016, 0x00ce, 0x000e,
+ 0x0005, 0x00c6, 0x00d6, 0x0016, 0x0026, 0x2061, 0x0100, 0x2069,
+ 0x0140, 0x080c, 0x574f, 0x1178, 0x2001, 0xafe3, 0x2004, 0xa005,
+ 0x1598, 0x080c, 0x57d1, 0x1118, 0x080c, 0x6578, 0x0468, 0x00c6,
+ 0x2061, 0xafc7, 0x00d8, 0x6904, 0xa194, 0x4000, 0x0550, 0x08a1,
+ 0x6803, 0x1000, 0x6803, 0x0000, 0x00c6, 0x2061, 0xafc7, 0x6128,
+ 0xa192, 0x00c8, 0x1258, 0x8108, 0x612a, 0x6124, 0x00ce, 0x81ff,
+ 0x0198, 0x080c, 0x6578, 0x080c, 0x782b, 0x0070, 0x6124, 0xa1e5,
+ 0x0000, 0x0140, 0x080c, 0xaca2, 0x2009, 0x0014, 0x080c, 0x80a7,
+ 0x080c, 0x6581, 0x00ce, 0x0000, 0x002e, 0x001e, 0x00de, 0x00ce,
+ 0x0005, 0x2001, 0xafe3, 0x2004, 0xa005, 0x1db0, 0x00c6, 0x2061,
+ 0xafc7, 0x6128, 0xa192, 0x0003, 0x1e08, 0x8108, 0x612a, 0x00ce,
+ 0x080c, 0x6578, 0x080c, 0x485e, 0x0c38, 0x00c6, 0x00d6, 0x00e6,
+ 0x0016, 0x0026, 0x080c, 0x658e, 0x2071, 0xafc7, 0x713c, 0x81ff,
+ 0x0570, 0x2061, 0x0100, 0x2069, 0x0140, 0x080c, 0x574f, 0x1188,
+ 0x0036, 0x2019, 0x0001, 0x080c, 0x7a64, 0x003e, 0x713c, 0x2160,
+ 0x080c, 0xaca2, 0x2009, 0x004a, 0x080c, 0x80a7, 0x080c, 0x57d1,
+ 0x00b0, 0x6904, 0xa194, 0x4000, 0x01c0, 0x6803, 0x1000, 0x6803,
+ 0x0000, 0x0036, 0x2019, 0x0001, 0x080c, 0x7a64, 0x003e, 0x713c,
+ 0x2160, 0x080c, 0xaca2, 0x2009, 0x004a, 0x080c, 0x80a7, 0x002e,
+ 0x001e, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x0c58, 0x00e6, 0x00d6,
+ 0x00c6, 0x0066, 0x0056, 0x0046, 0x0006, 0x0126, 0x2091, 0x8000,
+ 0x6018, 0x2068, 0x6ca0, 0x2071, 0xafc7, 0x7018, 0x2068, 0x8dff,
+ 0x0198, 0x68a0, 0xa406, 0x0118, 0x6854, 0x2068, 0x0cc0, 0x6010,
+ 0x2060, 0x643c, 0x6540, 0x6e48, 0x2d60, 0x080c, 0x4e41, 0x0120,
+ 0x080c, 0x7b88, 0xa085, 0x0001, 0x012e, 0x000e, 0x004e, 0x005e,
+ 0x006e, 0x00ce, 0x00de, 0x00ee, 0x0005, 0x20a1, 0x020b, 0x080c,
+ 0x710e, 0x20a3, 0x1200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x781c,
+ 0xa086, 0x0004, 0x1110, 0x6098, 0x0018, 0x2001, 0xad14, 0x2004,
+ 0x20a2, 0x7834, 0x20a2, 0x7838, 0x20a2, 0x20a9, 0x0010, 0xa006,
+ 0x20a2, 0x1f04, 0x7928, 0x20a2, 0x20a2, 0x60c3, 0x002c, 0x080c,
+ 0x7821, 0x0005, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x710e,
+ 0x20a3, 0x0f00, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808, 0x20a2,
+ 0x60c3, 0x0008, 0x080c, 0x7821, 0x014e, 0x015e, 0x0005, 0x0156,
+ 0x0146, 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0200, 0x20a3,
+ 0x0000, 0x20a9, 0x0006, 0x2011, 0xad40, 0x2019, 0xad41, 0x23a6,
+ 0x22a6, 0xa398, 0x0002, 0xa290, 0x0002, 0x1f04, 0x7957, 0x20a3,
+ 0x0000, 0x20a3, 0x0000, 0x60c3, 0x001c, 0x080c, 0x7821, 0x014e,
+ 0x015e, 0x0005, 0x0156, 0x0146, 0x0016, 0x0026, 0x20a1, 0x020b,
+ 0x080c, 0x7183, 0x080c, 0x7199, 0x7810, 0xa080, 0x0000, 0x2004,
+ 0xa080, 0x0015, 0x2098, 0x7808, 0xa088, 0x0002, 0x21a8, 0x53a6,
+ 0xa080, 0x0004, 0x8003, 0x60c2, 0x080c, 0x7821, 0x002e, 0x001e,
+ 0x014e, 0x015e, 0x0005, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c,
+ 0x710e, 0x20a3, 0x6200, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x7808,
+ 0x20a2, 0x60c3, 0x0008, 0x080c, 0x7821, 0x014e, 0x015e, 0x0005,
+ 0x0156, 0x0146, 0x0016, 0x0026, 0x20a1, 0x020b, 0x080c, 0x710e,
+ 0x7810, 0xa080, 0x0000, 0x2004, 0xa080, 0x0017, 0x2098, 0x7808,
+ 0xa088, 0x0002, 0x21a8, 0x53a6, 0x8003, 0x60c2, 0x080c, 0x7821,
+ 0x002e, 0x001e, 0x014e, 0x015e, 0x0005, 0x00e6, 0x00c6, 0x0006,
+ 0x0126, 0x2091, 0x8000, 0x2071, 0xafc7, 0x700c, 0x2060, 0x8cff,
+ 0x0178, 0x080c, 0x9789, 0x1110, 0x080c, 0x85f3, 0x600c, 0x0006,
+ 0x080c, 0x994e, 0x080c, 0x8078, 0x080c, 0x7b88, 0x00ce, 0x0c78,
+ 0x700f, 0x0000, 0x700b, 0x0000, 0x012e, 0x000e, 0x00ce, 0x00ee,
+ 0x0005, 0x0126, 0x0156, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0026,
+ 0x0016, 0x0006, 0x2091, 0x8000, 0x2069, 0x0100, 0x2079, 0x0140,
+ 0x2071, 0xafc7, 0x7024, 0x2060, 0x8cff, 0x05a0, 0x080c, 0x7834,
+ 0x68c3, 0x0000, 0x080c, 0x6581, 0x2009, 0x0013, 0x080c, 0x80a7,
+ 0x20a9, 0x01f4, 0x6824, 0xd094, 0x0158, 0x6827, 0x0004, 0x7804,
+ 0xa084, 0x4000, 0x01a0, 0x7803, 0x1000, 0x7803, 0x0000, 0x0078,
+ 0xd084, 0x0118, 0x6827, 0x0001, 0x0010, 0x1f04, 0x7a02, 0x7804,
+ 0xa084, 0x1000, 0x0120, 0x7803, 0x0100, 0x7803, 0x0000, 0x6824,
+ 0x000e, 0x001e, 0x002e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e,
+ 0x012e, 0x0005, 0x2001, 0xad00, 0x2004, 0xa096, 0x0001, 0x0550,
+ 0xa096, 0x0004, 0x0538, 0x6817, 0x0008, 0x68c3, 0x0000, 0x2011,
+ 0x481b, 0x080c, 0x650d, 0x20a9, 0x01f4, 0x6824, 0xd094, 0x0158,
+ 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x01a0, 0x7803, 0x1000,
+ 0x7803, 0x0000, 0x0078, 0xd084, 0x0118, 0x6827, 0x0001, 0x0010,
+ 0x1f04, 0x7a3d, 0x7804, 0xa084, 0x1000, 0x0120, 0x7803, 0x0100,
+ 0x7803, 0x0000, 0x000e, 0x001e, 0x002e, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x015e, 0x012e, 0x0005, 0x0126, 0x0156, 0x00f6, 0x00e6,
+ 0x00d6, 0x00c6, 0x0026, 0x0016, 0x0006, 0x2091, 0x8000, 0x2069,
+ 0x0100, 0x2079, 0x0140, 0x2071, 0xafc7, 0x703c, 0x2060, 0x8cff,
+ 0x0904, 0x7ad5, 0x6817, 0x0010, 0x2009, 0x00fa, 0x8109, 0x1df0,
+ 0x68c7, 0x0000, 0x68cb, 0x0008, 0x080c, 0x658e, 0x080c, 0x20b5,
+ 0x0046, 0x2009, 0x017f, 0x200b, 0x00a5, 0x2021, 0x0169, 0x2404,
+ 0xa084, 0x000f, 0xa086, 0x0004, 0x11b0, 0x68c7, 0x0000, 0x68cb,
+ 0x0008, 0x00e6, 0x00f6, 0x2079, 0x0020, 0x2071, 0xb01e, 0x6814,
+ 0xa084, 0x0184, 0xa085, 0x0012, 0x6816, 0x7803, 0x0008, 0x7003,
+ 0x0000, 0x00fe, 0x00ee, 0x200b, 0x0000, 0x004e, 0xa39d, 0x0000,
+ 0x1120, 0x2009, 0x0049, 0x080c, 0x80a7, 0x20a9, 0x03e8, 0x6824,
+ 0xd094, 0x0158, 0x6827, 0x0004, 0x7804, 0xa084, 0x4000, 0x01a0,
+ 0x7803, 0x1000, 0x7803, 0x0000, 0x0078, 0xd08c, 0x0118, 0x6827,
+ 0x0002, 0x0010, 0x1f04, 0x7ab7, 0x7804, 0xa084, 0x1000, 0x0120,
+ 0x7803, 0x0100, 0x7803, 0x0000, 0x6824, 0x000e, 0x001e, 0x002e,
+ 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x015e, 0x012e, 0x0005, 0x00d6,
+ 0x0126, 0x2091, 0x8000, 0x2069, 0xafc7, 0x6a06, 0x012e, 0x00de,
+ 0x0005, 0x00d6, 0x0126, 0x2091, 0x8000, 0x2069, 0xafc7, 0x6a32,
+ 0x012e, 0x00de, 0x0005, 0x00f6, 0x00e6, 0x00c6, 0x0066, 0x0006,
+ 0x0126, 0x2071, 0xafc7, 0x7614, 0x2660, 0x2678, 0x2091, 0x8000,
+ 0x8cff, 0x0538, 0x601c, 0xa206, 0x1500, 0x7014, 0xac36, 0x1110,
+ 0x660c, 0x7616, 0x7010, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118,
+ 0x2f00, 0x7012, 0x0010, 0x7013, 0x0000, 0x660c, 0x0066, 0x2c00,
+ 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x080c,
+ 0x974e, 0x080c, 0x7b88, 0x00ce, 0x08d8, 0x2c78, 0x600c, 0x2060,
+ 0x08b8, 0x012e, 0x000e, 0x006e, 0x00ce, 0x00ee, 0x00fe, 0x0005,
+ 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x73cf, 0x7810, 0x20a2,
+ 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x1000, 0x0804,
+ 0x7b80, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x73cf, 0x7810,
+ 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x4000,
+ 0x0478, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x73cf, 0x7810,
+ 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x2000,
+ 0x00f8, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x73cf, 0x7810,
+ 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0400,
+ 0x0078, 0x0156, 0x0146, 0x20a1, 0x020b, 0x080c, 0x73cf, 0x7810,
+ 0x20a2, 0xa006, 0x20a2, 0x20a2, 0x20a2, 0x20a2, 0x20a3, 0x0200,
+ 0x0089, 0x60c3, 0x0020, 0x080c, 0x7821, 0x014e, 0x015e, 0x0005,
+ 0x00e6, 0x2071, 0xafc7, 0x7020, 0xa005, 0x0110, 0x8001, 0x7022,
+ 0x00ee, 0x0005, 0x20a9, 0x0008, 0x20a2, 0x1f04, 0x7b94, 0x20a2,
+ 0x20a2, 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6, 0x0076, 0x0066,
+ 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0xafc7, 0x7614, 0x2660,
+ 0x2678, 0x2039, 0x0001, 0x87ff, 0x0904, 0x7c24, 0x8cff, 0x0904,
+ 0x7c24, 0x601c, 0xa086, 0x0006, 0x1904, 0x7c1f, 0x88ff, 0x0138,
+ 0x2800, 0xac06, 0x1904, 0x7c1f, 0x2039, 0x0000, 0x0050, 0x6018,
+ 0xa206, 0x1904, 0x7c1f, 0x85ff, 0x0120, 0x6050, 0xa106, 0x1904,
+ 0x7c1f, 0x7024, 0xac06, 0x1538, 0x2069, 0x0100, 0x68c0, 0xa005,
+ 0x01f0, 0x080c, 0x6581, 0x6817, 0x0008, 0x68c3, 0x0000, 0x080c,
+ 0x7ca8, 0x7027, 0x0000, 0x0036, 0x2069, 0x0140, 0x6b04, 0xa384,
+ 0x1000, 0x0120, 0x6803, 0x0100, 0x6803, 0x0000, 0x2069, 0x0100,
+ 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e, 0x0020, 0x6003,
+ 0x0009, 0x630a, 0x0460, 0x7014, 0xac36, 0x1110, 0x660c, 0x7616,
+ 0x7010, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7012,
+ 0x0010, 0x7013, 0x0000, 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x89ff, 0x1158, 0x600f, 0x0000, 0x6010,
+ 0x2068, 0x080c, 0x9596, 0x0110, 0x080c, 0xa91f, 0x080c, 0x974e,
+ 0x080c, 0x7b88, 0x88ff, 0x1190, 0x00ce, 0x0804, 0x7bab, 0x2c78,
+ 0x600c, 0x2060, 0x0804, 0x7bab, 0xa006, 0x012e, 0x000e, 0x006e,
+ 0x007e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x6017, 0x0000,
+ 0x00ce, 0xa8c5, 0x0001, 0x0c88, 0x00f6, 0x00e6, 0x00d6, 0x00c6,
+ 0x0066, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0xafc7,
+ 0x7638, 0x2660, 0x2678, 0x8cff, 0x0904, 0x7c98, 0x601c, 0xa086,
+ 0x0006, 0x1904, 0x7c93, 0x87ff, 0x0128, 0x2700, 0xac06, 0x1904,
+ 0x7c93, 0x0040, 0x6018, 0xa206, 0x15f0, 0x85ff, 0x0118, 0x6050,
+ 0xa106, 0x15c8, 0x703c, 0xac06, 0x1170, 0x0036, 0x2019, 0x0001,
+ 0x080c, 0x7a64, 0x7033, 0x0000, 0x703f, 0x0000, 0x7043, 0x0000,
+ 0x7047, 0x0000, 0x003e, 0x7038, 0xac36, 0x1110, 0x660c, 0x763a,
+ 0x7034, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x7036,
+ 0x0010, 0x7037, 0x0000, 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0x6010, 0x2068, 0x080c,
+ 0x9596, 0x0110, 0x080c, 0xa91f, 0x080c, 0x974e, 0x87ff, 0x1190,
+ 0x00ce, 0x0804, 0x7c43, 0x2c78, 0x600c, 0x2060, 0x0804, 0x7c43,
+ 0xa006, 0x012e, 0x000e, 0x002e, 0x006e, 0x00ce, 0x00de, 0x00ee,
+ 0x00fe, 0x0005, 0x6017, 0x0000, 0x00ce, 0xa7bd, 0x0001, 0x0c88,
+ 0x00e6, 0x2071, 0xafc7, 0x2001, 0xad00, 0x2004, 0xa086, 0x0002,
+ 0x1118, 0x7007, 0x0005, 0x0010, 0x7007, 0x0000, 0x00ee, 0x0005,
+ 0x00f6, 0x00e6, 0x00c6, 0x0066, 0x0026, 0x0006, 0x0126, 0x2091,
+ 0x8000, 0x2071, 0xafc7, 0x2c10, 0x7638, 0x2660, 0x2678, 0x8cff,
+ 0x0518, 0x2200, 0xac06, 0x11e0, 0x7038, 0xac36, 0x1110, 0x660c,
+ 0x763a, 0x7034, 0xac36, 0x1140, 0x2c00, 0xaf36, 0x0118, 0x2f00,
+ 0x7036, 0x0010, 0x7037, 0x0000, 0x660c, 0x2c00, 0xaf06, 0x0110,
+ 0x7e0e, 0x0008, 0x2678, 0x600f, 0x0000, 0xa085, 0x0001, 0x0020,
+ 0x2c78, 0x600c, 0x2060, 0x08d8, 0x012e, 0x000e, 0x002e, 0x006e,
+ 0x00ce, 0x00ee, 0x00fe, 0x0005, 0x00f6, 0x00e6, 0x00d6, 0x00c6,
+ 0x0066, 0x0006, 0x0126, 0x2091, 0x8000, 0x2071, 0xafc7, 0x760c,
+ 0x2660, 0x2678, 0x8cff, 0x0904, 0x7d7e, 0x6018, 0xa080, 0x0028,
+ 0x2004, 0xa206, 0x1904, 0x7d79, 0x7024, 0xac06, 0x1508, 0x2069,
+ 0x0100, 0x68c0, 0xa005, 0x0904, 0x7d55, 0x080c, 0x7834, 0x68c3,
+ 0x0000, 0x080c, 0x7ca8, 0x7027, 0x0000, 0x0036, 0x2069, 0x0140,
+ 0x6b04, 0xa384, 0x1000, 0x0120, 0x6803, 0x0100, 0x6803, 0x0000,
+ 0x2069, 0x0100, 0x6824, 0xd084, 0x0110, 0x6827, 0x0001, 0x003e,
+ 0x700c, 0xac36, 0x1110, 0x660c, 0x760e, 0x7008, 0xac36, 0x1140,
+ 0x2c00, 0xaf36, 0x0118, 0x2f00, 0x700a, 0x0010, 0x700b, 0x0000,
+ 0x660c, 0x0066, 0x2c00, 0xaf06, 0x0110, 0x7e0e, 0x0008, 0x2678,
+ 0x600f, 0x0000, 0x080c, 0x9778, 0x1158, 0x080c, 0x2aff, 0x080c,
+ 0x9789, 0x11f0, 0x080c, 0x85f3, 0x00d8, 0x080c, 0x7ca8, 0x08c0,
+ 0x080c, 0x9789, 0x1118, 0x080c, 0x85f3, 0x0090, 0x6010, 0x2068,
+ 0x080c, 0x9596, 0x0168, 0x601c, 0xa086, 0x0003, 0x11f8, 0x6837,
+ 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x510c, 0x080c, 0x9742,
+ 0x080c, 0x994e, 0x080c, 0x974e, 0x080c, 0x7b88, 0x00ce, 0x0804,
+ 0x7d02, 0x2c78, 0x600c, 0x2060, 0x0804, 0x7d02, 0x012e, 0x000e,
+ 0x006e, 0x00ce, 0x00de, 0x00ee, 0x00fe, 0x0005, 0x601c, 0xa086,
+ 0x0006, 0x1d30, 0x080c, 0xa91f, 0x0c18, 0x0036, 0x0156, 0x0136,
+ 0x0146, 0x3908, 0xa006, 0xa190, 0x0020, 0x221c, 0xa39e, 0x28f9,
+ 0x1118, 0x8210, 0x8000, 0x0cc8, 0xa005, 0x0138, 0x20a9, 0x0020,
+ 0x2198, 0xa110, 0x22a0, 0x22c8, 0x53a3, 0x014e, 0x013e, 0x015e,
+ 0x003e, 0x0005, 0x00d6, 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3,
+ 0x0200, 0x20a3, 0x0014, 0x60c3, 0x0014, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x2099, 0xafa6, 0x20a9, 0x0004, 0x53a6, 0x20a3, 0x0004,
+ 0x20a3, 0x7878, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x080c, 0x7821,
+ 0x00de, 0x0005, 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3, 0x0214,
+ 0x20a3, 0x0018, 0x20a3, 0x0800, 0x7810, 0xa084, 0xff00, 0x20a2,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x20a3, 0x0000,
+ 0x7810, 0xa084, 0x00ff, 0x20a2, 0x7828, 0x20a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x60c3, 0x0018, 0x080c, 0x7821, 0x0005, 0x00d6,
+ 0x0016, 0x2f68, 0x2009, 0x0035, 0x080c, 0x9a34, 0x1904, 0x7e5d,
+ 0x20a1, 0x020b, 0x080c, 0x710e, 0x20a3, 0x1300, 0x20a3, 0x0000,
+ 0x7828, 0x2068, 0x681c, 0xa086, 0x0003, 0x0580, 0x7818, 0xa080,
+ 0x0028, 0x2014, 0x2001, 0xad34, 0x2004, 0xd0ac, 0x11d0, 0xa286,
+ 0x007e, 0x1128, 0x20a3, 0x00ff, 0x20a3, 0xfffe, 0x04b8, 0xa286,
+ 0x007f, 0x1128, 0x20a3, 0x00ff, 0x20a3, 0xfffd, 0x0478, 0xd2bc,
+ 0x0180, 0xa286, 0x0080, 0x1128, 0x20a3, 0x00ff, 0x20a3, 0xfffc,
+ 0x0428, 0xa2e8, 0xae34, 0x2d6c, 0x6810, 0x20a2, 0x6814, 0x20a2,
+ 0x00e8, 0x20a3, 0x0000, 0x6098, 0x20a2, 0x00c0, 0x2001, 0xad34,
+ 0x2004, 0xd0ac, 0x1138, 0x7818, 0xa080, 0x0028, 0x2004, 0xa082,
+ 0x007e, 0x0240, 0x00d6, 0x2069, 0xad1b, 0x2da6, 0x8d68, 0x2da6,
+ 0x00de, 0x0020, 0x20a3, 0x0000, 0x6034, 0x20a2, 0x7834, 0x20a2,
+ 0x7838, 0x20a2, 0x20a3, 0x0000, 0x20a3, 0x0000, 0x60c3, 0x000c,
+ 0x080c, 0x7821, 0x001e, 0x00de, 0x0005, 0x7817, 0x0001, 0x7803,
+ 0x0006, 0x001e, 0x00de, 0x0005, 0x00d6, 0x0026, 0x7928, 0x2168,
+ 0x691c, 0xa186, 0x0006, 0x01c0, 0xa186, 0x0003, 0x0904, 0x7ed3,
+ 0xa186, 0x0005, 0x0904, 0x7ebc, 0xa186, 0x0004, 0x05b8, 0xa186,
+ 0x0008, 0x0904, 0x7ec4, 0x7807, 0x0037, 0x7813, 0x1700, 0x080c,
+ 0x7f3b, 0x002e, 0x00de, 0x0005, 0x080c, 0x7ef7, 0x2009, 0x4000,
+ 0x6800, 0x0002, 0x7e9d, 0x7ea8, 0x7e9f, 0x7ea8, 0x7ea4, 0x7e9d,
+ 0x7e9d, 0x7ea8, 0x7ea8, 0x7ea8, 0x7ea8, 0x7e9d, 0x7e9d, 0x7e9d,
+ 0x7e9d, 0x7e9d, 0x7ea8, 0x7e9d, 0x7ea8, 0x080c, 0x14f6, 0x6820,
+ 0xd0e4, 0x0110, 0xd0cc, 0x0110, 0xa00e, 0x0010, 0x2009, 0x2000,
+ 0x6828, 0x20a2, 0x682c, 0x20a2, 0x0804, 0x7eed, 0x080c, 0x7ef7,
+ 0x20a3, 0x0000, 0x20a3, 0x0000, 0x2009, 0x4000, 0x6a00, 0xa286,
+ 0x0002, 0x1108, 0xa00e, 0x0488, 0x04d1, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x2009, 0x4000, 0x0448, 0x0491, 0x20a3, 0x0000, 0x20a3,
+ 0x0000, 0x2009, 0x4000, 0xa286, 0x0005, 0x0118, 0xa286, 0x0002,
+ 0x1108, 0xa00e, 0x00d0, 0x0419, 0x6810, 0x2068, 0x697c, 0x6810,
+ 0xa112, 0x6980, 0x6814, 0xa103, 0x20a2, 0x22a2, 0x7928, 0xa180,
+ 0x0000, 0x2004, 0xa08e, 0x0002, 0x0130, 0xa08e, 0x0004, 0x0118,
+ 0x2009, 0x4000, 0x0010, 0x2009, 0x0000, 0x21a2, 0x20a3, 0x0000,
+ 0x60c3, 0x0018, 0x080c, 0x7821, 0x002e, 0x00de, 0x0005, 0x0036,
+ 0x0046, 0x0056, 0x0066, 0x20a1, 0x020b, 0x080c, 0x71aa, 0xa006,
+ 0x20a3, 0x0200, 0x20a2, 0x7934, 0x21a2, 0x7938, 0x21a2, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1118,
+ 0xa092, 0x007e, 0x0268, 0x00d6, 0x2069, 0xad1b, 0x2d2c, 0x8d68,
+ 0x2d34, 0xa0e8, 0xae34, 0x2d6c, 0x6b10, 0x6c14, 0x00de, 0x0030,
+ 0x2019, 0x0000, 0x6498, 0x2029, 0x0000, 0x6634, 0x7828, 0xa080,
+ 0x0007, 0x2004, 0xa086, 0x0003, 0x1128, 0x25a2, 0x26a2, 0x23a2,
+ 0x24a2, 0x0020, 0x23a2, 0x24a2, 0x25a2, 0x26a2, 0x006e, 0x005e,
+ 0x004e, 0x003e, 0x0005, 0x20a1, 0x020b, 0x080c, 0x71aa, 0x20a3,
+ 0x0100, 0x20a3, 0x0000, 0x20a3, 0x0009, 0x7810, 0x20a2, 0x60c3,
+ 0x0008, 0x080c, 0x7821, 0x0005, 0x20a1, 0x020b, 0x080c, 0x7106,
+ 0x20a3, 0x1400, 0x20a3, 0x0000, 0x7834, 0x20a2, 0x7838, 0x20a2,
+ 0x7828, 0x20a2, 0x782c, 0x20a2, 0x7830, 0xa084, 0x00ff, 0x8007,
+ 0x20a2, 0x20a3, 0x0000, 0x60c3, 0x0010, 0x080c, 0x7821, 0x0005,
+ 0x20a1, 0x020b, 0x080c, 0x71a2, 0x20a3, 0x0100, 0x20a3, 0x0000,
+ 0x7828, 0x20a2, 0x7810, 0x20a2, 0x60c3, 0x0008, 0x080c, 0x7821,
+ 0x0005, 0x0146, 0x20a1, 0x020b, 0x0031, 0x60c3, 0x0000, 0x080c,
+ 0x7821, 0x014e, 0x0005, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7818,
+ 0xa080, 0x0028, 0x2004, 0x2011, 0xad34, 0x2214, 0xd2ac, 0x1110,
+ 0xd0bc, 0x0188, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810, 0xa085,
+ 0x0300, 0x20a2, 0x6814, 0x20a2, 0x2069, 0xad1b, 0x2da6, 0x8d68,
+ 0x2da6, 0x00de, 0x0078, 0x00d6, 0xa0e8, 0xae34, 0x2d6c, 0x6810,
+ 0xa085, 0x0300, 0x20a2, 0x6814, 0x20a2, 0x00de, 0x20a3, 0x0000,
+ 0x6234, 0x22a2, 0x20a3, 0x0819, 0x20a3, 0x0000, 0x080c, 0x7810,
+ 0x22a2, 0x20a3, 0x0000, 0x2fa2, 0x7a08, 0x22a2, 0x20a3, 0x0000,
+ 0x20a3, 0x0000, 0x0005, 0x20a1, 0x020b, 0x0079, 0x7910, 0x21a2,
+ 0x20a3, 0x0000, 0x60c3, 0x0000, 0x20e1, 0x9080, 0x60a7, 0x9575,
+ 0x080c, 0x782b, 0x080c, 0x6578, 0x0005, 0x0156, 0x0136, 0x0036,
+ 0x00d6, 0x00e6, 0x20e1, 0x9080, 0x20e1, 0x4000, 0x7854, 0x2068,
+ 0xadf0, 0x000f, 0x7210, 0xa296, 0x00c0, 0xa294, 0xfffd, 0x7212,
+ 0x7214, 0xa294, 0x0300, 0x7216, 0x7100, 0xa194, 0x00ff, 0x7308,
+ 0xa384, 0x00ff, 0xa08d, 0xc200, 0x7102, 0xa384, 0xff00, 0xa215,
+ 0x720a, 0x7004, 0x720c, 0x700e, 0x7206, 0x20a9, 0x000a, 0x2e98,
+ 0x53a6, 0x60a3, 0x0035, 0x6a38, 0xa294, 0x7000, 0xa286, 0x3000,
+ 0x0110, 0x60a3, 0x0037, 0x00ee, 0x00de, 0x003e, 0x013e, 0x015e,
+ 0x0005, 0x2009, 0x0092, 0x0010, 0x2009, 0x0096, 0x60ab, 0x0036,
+ 0x6116, 0x0005, 0x2061, 0xb400, 0x2a70, 0x7064, 0x7046, 0x704b,
+ 0xb400, 0x0005, 0x00e6, 0x0126, 0x2071, 0xad00, 0x2091, 0x8000,
+ 0x7544, 0xa582, 0x0010, 0x0608, 0x7048, 0x2060, 0x6000, 0xa086,
+ 0x0000, 0x0148, 0xace0, 0x0018, 0x7058, 0xac02, 0x1208, 0x0cb0,
+ 0x2061, 0xb400, 0x0c98, 0x6003, 0x0008, 0x8529, 0x7546, 0xaca8,
+ 0x0018, 0x7058, 0xa502, 0x1230, 0x754a, 0xa085, 0x0001, 0x012e,
+ 0x00ee, 0x0005, 0x704b, 0xb400, 0x0cc0, 0xa006, 0x0cc0, 0x00e6,
+ 0x2071, 0xad00, 0x7544, 0xa582, 0x0010, 0x0600, 0x7048, 0x2060,
+ 0x6000, 0xa086, 0x0000, 0x0148, 0xace0, 0x0018, 0x7058, 0xac02,
+ 0x1208, 0x0cb0, 0x2061, 0xb400, 0x0c98, 0x6003, 0x0008, 0x8529,
+ 0x7546, 0xaca8, 0x0018, 0x7058, 0xa502, 0x1228, 0x754a, 0xa085,
+ 0x0001, 0x00ee, 0x0005, 0x704b, 0xb400, 0x0cc8, 0xa006, 0x0cc8,
+ 0xac82, 0xb400, 0x0a0c, 0x14f6, 0x2001, 0xad16, 0x2004, 0xac02,
+ 0x1a0c, 0x14f6, 0xa006, 0x6006, 0x600a, 0x600e, 0x6012, 0x6016,
+ 0x601a, 0x601f, 0x0000, 0x6003, 0x0000, 0x6052, 0x6056, 0x6022,
+ 0x6026, 0x602a, 0x602e, 0x6032, 0x6036, 0x603a, 0x603e, 0x2061,
+ 0xad00, 0x6044, 0x8000, 0x6046, 0xa086, 0x0001, 0x0108, 0x0005,
+ 0x0126, 0x2091, 0x8000, 0x080c, 0x6c50, 0x012e, 0x0cc0, 0x601c,
+ 0xa084, 0x000f, 0x0002, 0x80b6, 0x80c5, 0x80e0, 0x80fb, 0x9a61,
+ 0x9a7c, 0x9a97, 0x80b6, 0x80c5, 0x80b6, 0x8116, 0xa186, 0x0013,
+ 0x1128, 0x080c, 0x6b73, 0x080c, 0x6c50, 0x0005, 0xa18e, 0x0047,
+ 0x1118, 0xa016, 0x080c, 0x1824, 0x0005, 0x0066, 0x6000, 0xa0b2,
+ 0x0010, 0x1a0c, 0x14f6, 0x0013, 0x006e, 0x0005, 0x80de, 0x8478,
+ 0x862d, 0x80de, 0x86a2, 0x81cf, 0x80de, 0x80de, 0x840a, 0x8a91,
+ 0x80de, 0x80de, 0x80de, 0x80de, 0x80de, 0x80de, 0x080c, 0x14f6,
+ 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x14f6, 0x0013, 0x006e,
+ 0x0005, 0x80f9, 0x909a, 0x80f9, 0x80f9, 0x80f9, 0x80f9, 0x80f9,
+ 0x80f9, 0x9045, 0x9200, 0x80f9, 0x90c7, 0x913e, 0x90c7, 0x913e,
+ 0x80f9, 0x080c, 0x14f6, 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c,
+ 0x14f6, 0x0013, 0x006e, 0x0005, 0x8114, 0x8ad2, 0x8b98, 0x8cb8,
+ 0x8e12, 0x8114, 0x8114, 0x8114, 0x8aac, 0x8ff5, 0x8ff8, 0x8114,
+ 0x8114, 0x8114, 0x8114, 0x9022, 0x080c, 0x14f6, 0x0066, 0x6000,
+ 0xa0b2, 0x0010, 0x1a0c, 0x14f6, 0x0013, 0x006e, 0x0005, 0x812f,
+ 0x812f, 0x812f, 0x8152, 0x81a5, 0x812f, 0x812f, 0x812f, 0x8131,
+ 0x812f, 0x812f, 0x812f, 0x812f, 0x812f, 0x812f, 0x812f, 0x080c,
+ 0x14f6, 0xa186, 0x0003, 0x190c, 0x14f6, 0x00d6, 0x6003, 0x0003,
+ 0x6106, 0x6010, 0x2068, 0x684f, 0x0040, 0x687c, 0x680a, 0x6880,
+ 0x680e, 0x6813, 0x0000, 0x6817, 0x0000, 0x00de, 0x2c10, 0x080c,
+ 0x1e6e, 0x080c, 0x680b, 0x0126, 0x2091, 0x8000, 0x080c, 0x6d0d,
+ 0x012e, 0x0005, 0xa182, 0x0047, 0x0002, 0x815e, 0x815e, 0x8160,
+ 0x817f, 0x815e, 0x815e, 0x815e, 0x815e, 0x8191, 0x080c, 0x14f6,
+ 0x00d6, 0x0016, 0x080c, 0x6c05, 0x080c, 0x6d0d, 0x6003, 0x0004,
+ 0x6110, 0x2168, 0x6854, 0x8003, 0x800b, 0x810b, 0xa108, 0x6116,
+ 0x684f, 0x0020, 0x685c, 0x685a, 0x6874, 0x687e, 0x6878, 0x6882,
+ 0x6897, 0x0000, 0x689b, 0x0000, 0x001e, 0x00de, 0x0005, 0x080c,
+ 0x6c05, 0x00d6, 0x6110, 0x2168, 0x080c, 0x9596, 0x0120, 0x684b,
+ 0x0006, 0x080c, 0x510c, 0x00de, 0x080c, 0x8078, 0x080c, 0x6d0d,
+ 0x0005, 0x080c, 0x6c05, 0x080c, 0x2ad9, 0x00d6, 0x6110, 0x2168,
+ 0x080c, 0x9596, 0x0120, 0x684b, 0x0029, 0x080c, 0x510c, 0x00de,
+ 0x080c, 0x8078, 0x080c, 0x6d0d, 0x0005, 0xa182, 0x0047, 0x0002,
+ 0x81b3, 0x81c2, 0x81b1, 0x81b1, 0x81b1, 0x81b1, 0x81b1, 0x81b1,
+ 0x81b1, 0x080c, 0x14f6, 0x00d6, 0x6010, 0x2068, 0x684c, 0xc0f4,
+ 0x684e, 0x00de, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x080c,
+ 0x1824, 0x0005, 0x00d6, 0x6110, 0x2168, 0x684b, 0x0000, 0x6853,
+ 0x0000, 0x080c, 0x510c, 0x00de, 0x080c, 0x8078, 0x0005, 0xa1b6,
+ 0x0015, 0x1118, 0x080c, 0x8078, 0x0030, 0xa1b6, 0x0016, 0x190c,
+ 0x14f6, 0x080c, 0x8078, 0x0005, 0x20a9, 0x000e, 0x2e98, 0x6010,
+ 0x20a0, 0x53a3, 0x20a9, 0x0006, 0x3310, 0x3420, 0x9398, 0x94a0,
+ 0x3318, 0x3428, 0x222e, 0x2326, 0xa290, 0x0002, 0xa5a8, 0x0002,
+ 0xa398, 0x0002, 0xa4a0, 0x0002, 0x1f04, 0x81ea, 0x00e6, 0x080c,
+ 0x9596, 0x0130, 0x6010, 0x2070, 0x7007, 0x0000, 0x7037, 0x0103,
+ 0x00ee, 0x080c, 0x8078, 0x0005, 0x00d6, 0x0036, 0x7330, 0xa386,
+ 0x0200, 0x1130, 0x6018, 0x2068, 0x6813, 0x00ff, 0x6817, 0xfffd,
+ 0x6010, 0xa005, 0x0130, 0x2068, 0x6807, 0x0000, 0x6837, 0x0103,
+ 0x6b32, 0x080c, 0x8078, 0x003e, 0x00de, 0x0005, 0x0016, 0x20a9,
+ 0x002a, 0xae80, 0x000c, 0x2098, 0x6010, 0xa080, 0x0002, 0x20a0,
+ 0x53a3, 0x20a9, 0x002a, 0x6010, 0xa080, 0x0001, 0x2004, 0xa080,
+ 0x0002, 0x20a0, 0x53a3, 0x00e6, 0x6010, 0x2004, 0x2070, 0x7037,
+ 0x0103, 0x00ee, 0x080c, 0x8078, 0x001e, 0x0005, 0x0016, 0x2009,
+ 0x0000, 0x7030, 0xa086, 0x0100, 0x0140, 0x7038, 0xa084, 0x00ff,
+ 0x808e, 0x703c, 0xa084, 0x00ff, 0x8086, 0xa080, 0x0004, 0xa108,
+ 0x21a8, 0xae80, 0x000c, 0x2098, 0x6010, 0xa080, 0x0002, 0x20a0,
+ 0x080c, 0x48be, 0x00e6, 0x080c, 0x9596, 0x0140, 0x6010, 0x2070,
+ 0x7007, 0x0000, 0x7034, 0x70b2, 0x7037, 0x0103, 0x00ee, 0x080c,
+ 0x8078, 0x001e, 0x0005, 0x00e6, 0x00d6, 0x603f, 0x0000, 0x2c68,
+ 0x0016, 0x2009, 0x0035, 0x080c, 0x9a34, 0x001e, 0x1168, 0x0026,
+ 0x6228, 0x2268, 0x002e, 0x2071, 0xb28c, 0x6b1c, 0xa386, 0x0003,
+ 0x0130, 0xa386, 0x0006, 0x0128, 0x080c, 0x8078, 0x0020, 0x0031,
+ 0x0010, 0x080c, 0x8323, 0x00de, 0x00ee, 0x0005, 0x00f6, 0x6810,
+ 0x2078, 0xa186, 0x0015, 0x0904, 0x830c, 0xa18e, 0x0016, 0x1904,
+ 0x8321, 0x700c, 0xa084, 0xff00, 0xa086, 0x1700, 0x1904, 0x82eb,
+ 0x8fff, 0x0904, 0x831f, 0x6808, 0xa086, 0xffff, 0x1904, 0x830e,
+ 0x784c, 0xa084, 0x0060, 0xa086, 0x0020, 0x1150, 0x797c, 0x7810,
+ 0xa106, 0x1904, 0x830e, 0x7980, 0x7814, 0xa106, 0x1904, 0x830e,
+ 0x080c, 0x9742, 0x6858, 0x7852, 0x784c, 0xc0dc, 0xc0f4, 0xc0d4,
+ 0x784e, 0x0026, 0xa00e, 0x6a14, 0x2001, 0x000a, 0x080c, 0x6665,
+ 0x7854, 0xa20a, 0x0208, 0x8011, 0x7a56, 0x82ff, 0x002e, 0x1138,
+ 0x00c6, 0x2d60, 0x080c, 0x9374, 0x00ce, 0x0804, 0x831f, 0x00c6,
+ 0x00d6, 0x2f68, 0x6838, 0xd0fc, 0x1118, 0x080c, 0x4993, 0x0010,
+ 0x080c, 0x4b7c, 0x00de, 0x00ce, 0x1548, 0x00c6, 0x2d60, 0x080c,
+ 0x8078, 0x00ce, 0x04a0, 0x7008, 0xa086, 0x000b, 0x11a0, 0x6018,
+ 0x200c, 0xc1bc, 0x2102, 0x00c6, 0x2d60, 0x7853, 0x0003, 0x6007,
+ 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x080c, 0x67a8, 0x080c,
+ 0x6c50, 0x00ce, 0x00e0, 0x700c, 0xa086, 0x2a00, 0x1138, 0x2001,
+ 0xafa5, 0x2004, 0x683e, 0x0098, 0x0471, 0x0098, 0x8fff, 0x090c,
+ 0x14f6, 0x00c6, 0x00d6, 0x2d60, 0x2f68, 0x684b, 0x0003, 0x080c,
+ 0x926f, 0x080c, 0x9742, 0x080c, 0x974e, 0x00de, 0x00ce, 0x080c,
+ 0x8078, 0x00fe, 0x0005, 0xa186, 0x0015, 0x1128, 0x2001, 0xafa5,
+ 0x2004, 0x683e, 0x0068, 0xa18e, 0x0016, 0x1160, 0x00c6, 0x2d00,
+ 0x2060, 0x080c, 0xabb4, 0x080c, 0x6618, 0x080c, 0x8078, 0x00ce,
+ 0x080c, 0x8078, 0x0005, 0x0026, 0x0036, 0x0046, 0x7228, 0x7c80,
+ 0x7b7c, 0xd2f4, 0x0130, 0x2001, 0xafa5, 0x2004, 0x683e, 0x0804,
+ 0x839d, 0x00c6, 0x2d60, 0x080c, 0x928f, 0x00ce, 0x6804, 0xa086,
+ 0x0050, 0x1168, 0x00c6, 0x2d00, 0x2060, 0x6003, 0x0001, 0x6007,
+ 0x0050, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x00ce, 0x04f0, 0x6800,
+ 0xa086, 0x000f, 0x01c8, 0x8fff, 0x090c, 0x14f6, 0x6820, 0xd0dc,
+ 0x1198, 0x6800, 0xa086, 0x0004, 0x1198, 0x784c, 0xd0ac, 0x0180,
+ 0x784c, 0xc0dc, 0xc0f4, 0x784e, 0x7850, 0xc0f4, 0xc0fc, 0x7852,
+ 0x2001, 0x0001, 0x682e, 0x00e0, 0x2001, 0x0007, 0x682e, 0x00c0,
+ 0x784c, 0xd0b4, 0x1130, 0xd0ac, 0x0db8, 0x784c, 0xd0f4, 0x1da0,
+ 0x0c38, 0xd2ec, 0x1d88, 0x7024, 0xa306, 0x1118, 0x7020, 0xa406,
+ 0x0d58, 0x7020, 0x6836, 0x7024, 0x683a, 0x2001, 0x0005, 0x682e,
+ 0x080c, 0x9894, 0x080c, 0x6c50, 0x0010, 0x080c, 0x8078, 0x004e,
+ 0x003e, 0x002e, 0x0005, 0x00e6, 0x00d6, 0x0026, 0x6034, 0x2068,
+ 0x6a1c, 0xa286, 0x0007, 0x0904, 0x83ee, 0xa286, 0x0002, 0x05f0,
+ 0xa286, 0x0000, 0x05d8, 0x6808, 0x6338, 0xa306, 0x15b8, 0x2071,
+ 0xb28c, 0xa186, 0x0015, 0x0560, 0xa18e, 0x0016, 0x1190, 0x6030,
+ 0xa084, 0x00ff, 0xa086, 0x0001, 0x1160, 0x700c, 0xa086, 0x2a00,
+ 0x1140, 0x6034, 0xa080, 0x0008, 0x200c, 0xc1dd, 0xc1f5, 0x2102,
+ 0x00b8, 0x00c6, 0x6034, 0x2060, 0x6010, 0x2068, 0x080c, 0x9596,
+ 0x090c, 0x14f6, 0x6853, 0x0003, 0x6007, 0x0085, 0x6003, 0x000b,
+ 0x601f, 0x0002, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x00ce, 0x0030,
+ 0x6034, 0x2070, 0x2001, 0xafa5, 0x2004, 0x703e, 0x080c, 0x8078,
+ 0x002e, 0x00de, 0x00ee, 0x0005, 0x00d6, 0x20a9, 0x000e, 0x2e98,
+ 0x6010, 0x20a0, 0x53a3, 0xa1b6, 0x0015, 0x1148, 0x6018, 0x2068,
+ 0x7038, 0x680a, 0x703c, 0x680e, 0x6800, 0xc08d, 0x6802, 0x00de,
+ 0x0804, 0x81f6, 0x2100, 0xa1b2, 0x0080, 0x1a0c, 0x14f6, 0xa1b2,
+ 0x0040, 0x1a04, 0x846e, 0x0002, 0x8462, 0x8456, 0x8462, 0x8462,
+ 0x8462, 0x8462, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454,
+ 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454,
+ 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454,
+ 0x8454, 0x8454, 0x8454, 0x8462, 0x8454, 0x8462, 0x8462, 0x8454,
+ 0x8454, 0x8454, 0x8454, 0x8454, 0x8462, 0x8454, 0x8454, 0x8454,
+ 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8462, 0x8462,
+ 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454, 0x8454,
+ 0x8454, 0x8462, 0x8454, 0x8454, 0x080c, 0x14f6, 0x6003, 0x0001,
+ 0x6106, 0x080c, 0x67ee, 0x0126, 0x2091, 0x8000, 0x080c, 0x6c50,
+ 0x012e, 0x0005, 0x6003, 0x0001, 0x6106, 0x080c, 0x67ee, 0x0126,
+ 0x2091, 0x8000, 0x080c, 0x6c50, 0x012e, 0x0005, 0x2600, 0x0002,
+ 0x8462, 0x8476, 0x8476, 0x8462, 0x8462, 0x8476, 0x080c, 0x14f6,
+ 0x6004, 0xa0b2, 0x0080, 0x1a0c, 0x14f6, 0xa1b6, 0x0013, 0x0904,
+ 0x8518, 0xa1b6, 0x0027, 0x1904, 0x84de, 0x080c, 0x6b73, 0x6004,
+ 0x080c, 0x9778, 0x0188, 0x080c, 0x9789, 0x0904, 0x84d8, 0xa08e,
+ 0x0021, 0x0904, 0x84db, 0xa08e, 0x0022, 0x0904, 0x84d8, 0xa08e,
+ 0x003d, 0x0904, 0x84db, 0x04a8, 0x080c, 0x2aff, 0x2001, 0x0007,
+ 0x080c, 0x4c30, 0x6018, 0xa080, 0x0028, 0x200c, 0x080c, 0x85f3,
+ 0xa186, 0x007e, 0x1148, 0x2001, 0xad34, 0x2014, 0xc285, 0x080c,
+ 0x574f, 0x1108, 0xc2ad, 0x2202, 0x0016, 0x0026, 0x0036, 0x2110,
+ 0x2019, 0x0028, 0x080c, 0x68e7, 0x0076, 0x2039, 0x0000, 0x080c,
+ 0x681d, 0x00c6, 0x6018, 0xa065, 0x0110, 0x080c, 0x4ecf, 0x00ce,
+ 0x2c08, 0x080c, 0xa712, 0x007e, 0x003e, 0x002e, 0x001e, 0x080c,
+ 0x4c9f, 0x080c, 0x994e, 0x080c, 0x8078, 0x080c, 0x6c50, 0x0005,
+ 0x080c, 0x85f3, 0x0cb0, 0x080c, 0x8621, 0x0c98, 0xa186, 0x0014,
+ 0x1db0, 0x080c, 0x6b73, 0x080c, 0x2ad9, 0x080c, 0x9778, 0x1188,
+ 0x080c, 0x2aff, 0x6018, 0xa080, 0x0028, 0x200c, 0x080c, 0x85f3,
+ 0xa186, 0x007e, 0x1128, 0x2001, 0xad34, 0x200c, 0xc185, 0x2102,
+ 0x08c0, 0x080c, 0x9789, 0x1118, 0x080c, 0x85f3, 0x0890, 0x6004,
+ 0xa08e, 0x0032, 0x1158, 0x00e6, 0x00f6, 0x2071, 0xad81, 0x2079,
+ 0x0000, 0x080c, 0x2df1, 0x00fe, 0x00ee, 0x0818, 0x6004, 0xa08e,
+ 0x0021, 0x0d50, 0xa08e, 0x0022, 0x090c, 0x85f3, 0x0804, 0x84d1,
+ 0xa0b2, 0x0040, 0x1a04, 0x85db, 0x2008, 0x0002, 0x8560, 0x8561,
+ 0x8564, 0x8567, 0x856a, 0x856d, 0x855e, 0x855e, 0x855e, 0x855e,
+ 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e,
+ 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e,
+ 0x855e, 0x855e, 0x855e, 0x855e, 0x8570, 0x857f, 0x855e, 0x8581,
+ 0x857f, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x857f, 0x857f,
+ 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e, 0x855e,
+ 0x85bb, 0x857f, 0x855e, 0x857b, 0x855e, 0x855e, 0x855e, 0x857c,
+ 0x855e, 0x855e, 0x855e, 0x857f, 0x85b2, 0x855e, 0x080c, 0x14f6,
+ 0x00f0, 0x2001, 0x000b, 0x0460, 0x2001, 0x0003, 0x0448, 0x2001,
+ 0x0005, 0x0430, 0x2001, 0x0001, 0x0418, 0x2001, 0x0009, 0x0400,
+ 0x080c, 0x6b73, 0x6003, 0x0005, 0x2001, 0xafa5, 0x2004, 0x603e,
+ 0x080c, 0x6c50, 0x00a0, 0x0018, 0x0010, 0x080c, 0x4c30, 0x0804,
+ 0x85cc, 0x080c, 0x6b73, 0x2001, 0xafa3, 0x2004, 0x6016, 0x2001,
+ 0xafa5, 0x2004, 0x603e, 0x6003, 0x0004, 0x080c, 0x6c50, 0x0005,
+ 0x080c, 0x4c30, 0x080c, 0x6b73, 0x6003, 0x0002, 0x2001, 0xafa5,
+ 0x2004, 0x603e, 0x0036, 0x2019, 0xad5c, 0x2304, 0xa084, 0xff00,
+ 0x1120, 0x2001, 0xafa3, 0x201c, 0x0040, 0x8007, 0xa09a, 0x0004,
+ 0x0ec0, 0x8003, 0x801b, 0x831b, 0xa318, 0x6316, 0x003e, 0x080c,
+ 0x6c50, 0x08e8, 0x080c, 0x6b73, 0x080c, 0x994e, 0x080c, 0x8078,
+ 0x080c, 0x6c50, 0x08a0, 0x00e6, 0x00f6, 0x2071, 0xad81, 0x2079,
+ 0x0000, 0x080c, 0x2df1, 0x00fe, 0x00ee, 0x080c, 0x6b73, 0x080c,
+ 0x8078, 0x080c, 0x6c50, 0x0818, 0x080c, 0x6b73, 0x2001, 0xafa5,
+ 0x2004, 0x603e, 0x6003, 0x0002, 0x2001, 0xafa3, 0x2004, 0x6016,
+ 0x080c, 0x6c50, 0x0005, 0x2600, 0x2008, 0x0002, 0x85e6, 0x85e4,
+ 0x85e4, 0x85cc, 0x85cc, 0x85e4, 0x080c, 0x14f6, 0x080c, 0x6b73,
+ 0x00d6, 0x6010, 0x2068, 0x080c, 0x15f0, 0x00de, 0x080c, 0x8078,
+ 0x080c, 0x6c50, 0x0005, 0x00e6, 0x0026, 0x0016, 0x080c, 0x9596,
+ 0x0508, 0x6010, 0x2070, 0x7034, 0xa086, 0x0139, 0x1148, 0x2001,
+ 0x0030, 0x2009, 0x0000, 0x2011, 0x4005, 0x080c, 0x9a05, 0x0090,
+ 0x7038, 0xd0fc, 0x0178, 0x7007, 0x0000, 0x0016, 0x6004, 0xa08e,
+ 0x0021, 0x0160, 0xa08e, 0x003d, 0x0148, 0x001e, 0x7037, 0x0103,
+ 0x7033, 0x0100, 0x001e, 0x002e, 0x00ee, 0x0005, 0x001e, 0x0009,
+ 0x0cc8, 0x00e6, 0xacf0, 0x0004, 0x2e74, 0x7000, 0x2070, 0x7037,
+ 0x0103, 0x7023, 0x8001, 0x00ee, 0x0005, 0x00d6, 0x6618, 0x2668,
+ 0x6804, 0xa084, 0x00ff, 0x00de, 0xa0b2, 0x000c, 0x1a0c, 0x14f6,
+ 0x6604, 0xa6b6, 0x0043, 0x1120, 0x080c, 0x99c1, 0x0804, 0x8692,
+ 0x6604, 0xa6b6, 0x0033, 0x1120, 0x080c, 0x9971, 0x0804, 0x8692,
+ 0x6604, 0xa6b6, 0x0028, 0x1120, 0x080c, 0x97b9, 0x0804, 0x8692,
+ 0x6604, 0xa6b6, 0x0029, 0x1118, 0x080c, 0x97d0, 0x04d8, 0x6604,
+ 0xa6b6, 0x001f, 0x1118, 0x080c, 0x81dc, 0x04a0, 0x6604, 0xa6b6,
+ 0x0000, 0x1118, 0x080c, 0x83f4, 0x0468, 0x6604, 0xa6b6, 0x0022,
+ 0x1118, 0x080c, 0x8204, 0x0430, 0x6604, 0xa6b6, 0x0035, 0x1118,
+ 0x080c, 0x826b, 0x00f8, 0x6604, 0xa6b6, 0x0039, 0x1118, 0x080c,
+ 0x83a3, 0x00c0, 0x6604, 0xa6b6, 0x003d, 0x1118, 0x080c, 0x821e,
+ 0x0088, 0x6604, 0xa6b6, 0x0044, 0x1118, 0x080c, 0x823e, 0x0050,
+ 0xa1b6, 0x0015, 0x1110, 0x0053, 0x0028, 0xa1b6, 0x0016, 0x1118,
+ 0x0804, 0x883f, 0x0005, 0x080c, 0x80be, 0x0ce0, 0x86b9, 0x86bc,
+ 0x86b9, 0x86fe, 0x86b9, 0x87cc, 0x884d, 0x86b9, 0x86b9, 0x881b,
+ 0x86b9, 0x882f, 0xa1b6, 0x0048, 0x0140, 0x20e1, 0x0005, 0x3d18,
+ 0x3e20, 0x2c10, 0x080c, 0x1824, 0x0005, 0x00e6, 0xacf0, 0x0004,
+ 0x2e74, 0x7000, 0x2070, 0x7037, 0x0103, 0x00ee, 0x080c, 0x8078,
+ 0x0005, 0xe000, 0xe000, 0x0005, 0x00e6, 0x2071, 0xad00, 0x7080,
+ 0xa086, 0x0074, 0x1530, 0x080c, 0xa6e9, 0x11b0, 0x00d6, 0x6018,
+ 0x2068, 0x7030, 0xd08c, 0x0128, 0x6800, 0xd0bc, 0x0110, 0xc0c5,
+ 0x6802, 0x00d9, 0x00de, 0x2001, 0x0006, 0x080c, 0x4c30, 0x080c,
+ 0x2aff, 0x080c, 0x8078, 0x0078, 0x2001, 0x000a, 0x080c, 0x4c30,
+ 0x080c, 0x2aff, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x67ee,
+ 0x0010, 0x080c, 0x87bd, 0x00ee, 0x0005, 0x6800, 0xd084, 0x0168,
+ 0x2001, 0x0000, 0x080c, 0x4c1e, 0x2069, 0xad51, 0x6804, 0xd0a4,
+ 0x0120, 0x2001, 0x0006, 0x080c, 0x4c5d, 0x0005, 0x00d6, 0x2011,
+ 0xad20, 0x2204, 0xa086, 0x0074, 0x1904, 0x87ba, 0x6018, 0x2068,
+ 0x6aa0, 0xa286, 0x007e, 0x1120, 0x080c, 0x894d, 0x0804, 0x875e,
+ 0x080c, 0x8943, 0x6018, 0x2068, 0xa080, 0x0028, 0x2014, 0xa286,
+ 0x0080, 0x11c0, 0x6813, 0x00ff, 0x6817, 0xfffc, 0x6010, 0xa005,
+ 0x0138, 0x2068, 0x6807, 0x0000, 0x6837, 0x0103, 0x6833, 0x0200,
+ 0x2001, 0x0006, 0x080c, 0x4c30, 0x080c, 0x2aff, 0x080c, 0x8078,
+ 0x0804, 0x87bb, 0x00e6, 0x2071, 0xad34, 0x2e04, 0xd09c, 0x0188,
+ 0x2071, 0xb280, 0x7108, 0x720c, 0xa18c, 0x00ff, 0x1118, 0xa284,
+ 0xff00, 0x0138, 0x6018, 0x2070, 0x70a0, 0xd0bc, 0x1110, 0x7112,
+ 0x7216, 0x00ee, 0x6010, 0xa005, 0x0128, 0x2068, 0x6838, 0xd0f4,
+ 0x0108, 0x0880, 0x2001, 0x0004, 0x080c, 0x4c30, 0x6003, 0x0001,
+ 0x6007, 0x0003, 0x080c, 0x67ee, 0x0804, 0x87bb, 0x685c, 0xd0e4,
+ 0x01d0, 0x080c, 0x9903, 0x080c, 0x574f, 0x0110, 0xd0dc, 0x1900,
+ 0x2011, 0xad34, 0x2204, 0xc0ad, 0x2012, 0x2001, 0xaf8e, 0x2004,
+ 0x00f6, 0x2079, 0x0100, 0x78e3, 0x0000, 0x080c, 0x26cb, 0x78e2,
+ 0x00fe, 0x0804, 0x8728, 0x080c, 0x9937, 0x2011, 0xad34, 0x2204,
+ 0xc0a5, 0x2012, 0x0006, 0x080c, 0xa801, 0x000e, 0x1904, 0x8728,
+ 0xc0b5, 0x2012, 0x2001, 0x0000, 0x080c, 0x4c1e, 0x00c6, 0x2009,
+ 0x00ef, 0x00f6, 0x2079, 0x0100, 0x79ea, 0x7932, 0x7936, 0x00fe,
+ 0x080c, 0x26a0, 0x00f6, 0x2079, 0xad00, 0x7972, 0x2100, 0x2009,
+ 0x0000, 0x080c, 0x2676, 0x794e, 0x00fe, 0x8108, 0x080c, 0x4c80,
+ 0x2c00, 0x00ce, 0x1904, 0x8728, 0x601a, 0x2001, 0x0002, 0x080c,
+ 0x4c30, 0x601f, 0x0001, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c,
+ 0x67ee, 0x0008, 0x0011, 0x00de, 0x0005, 0x2001, 0xad00, 0x2004,
+ 0xa086, 0x0003, 0x0120, 0x2001, 0x0007, 0x080c, 0x4c30, 0x080c,
+ 0x2aff, 0x080c, 0x8078, 0x0005, 0x00e6, 0x0026, 0x0016, 0x2071,
+ 0xad00, 0x7080, 0xa086, 0x0014, 0x15f0, 0x7000, 0xa086, 0x0003,
+ 0x1128, 0x6010, 0xa005, 0x1110, 0x080c, 0x3cce, 0x00d6, 0x6018,
+ 0x2068, 0x080c, 0x4d72, 0x080c, 0x86ed, 0x00de, 0x080c, 0x89f7,
+ 0x1550, 0x00d6, 0x6018, 0x2068, 0x6890, 0x00de, 0xa005, 0x0518,
+ 0x2001, 0x0006, 0x080c, 0x4c30, 0x00e6, 0x6010, 0xa075, 0x01a8,
+ 0x7034, 0xa084, 0x00ff, 0xa086, 0x0039, 0x1148, 0x2001, 0x0000,
+ 0x2009, 0x0000, 0x2011, 0x4000, 0x080c, 0x9a05, 0x0030, 0x7007,
+ 0x0000, 0x7037, 0x0103, 0x7033, 0x0200, 0x00ee, 0x080c, 0x2aff,
+ 0x080c, 0x8078, 0x0020, 0x080c, 0x85f3, 0x080c, 0x87bd, 0x001e,
+ 0x002e, 0x00ee, 0x0005, 0x2011, 0xad20, 0x2204, 0xa086, 0x0014,
+ 0x1158, 0x2001, 0x0002, 0x080c, 0x4c30, 0x6003, 0x0001, 0x6007,
+ 0x0001, 0x080c, 0x67ee, 0x0010, 0x080c, 0x87bd, 0x0005, 0x2011,
+ 0xad20, 0x2204, 0xa086, 0x0004, 0x1138, 0x2001, 0x0007, 0x080c,
+ 0x4c30, 0x080c, 0x8078, 0x0010, 0x080c, 0x87bd, 0x0005, 0x000b,
+ 0x0005, 0x86b9, 0x8854, 0x86b9, 0x888a, 0x86b9, 0x88ff, 0x884d,
+ 0x86b9, 0x86b9, 0x8912, 0x86b9, 0x8922, 0x6604, 0xa6b6, 0x001e,
+ 0x1110, 0x080c, 0x8078, 0x0005, 0x00d6, 0x00c6, 0x080c, 0x8932,
+ 0x1178, 0x2001, 0x0000, 0x080c, 0x4c1e, 0x2001, 0x0002, 0x080c,
+ 0x4c30, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x67ee, 0x00f8,
+ 0x2009, 0xb28e, 0x2104, 0xa086, 0x0009, 0x1160, 0x6018, 0x2068,
+ 0x6840, 0xa084, 0x00ff, 0xa005, 0x0180, 0x8001, 0x6842, 0x6017,
+ 0x000a, 0x0068, 0x2009, 0xb28f, 0x2104, 0xa084, 0xff00, 0xa086,
+ 0x1900, 0x1118, 0x080c, 0x8078, 0x0010, 0x080c, 0x87bd, 0x00ce,
+ 0x00de, 0x0005, 0x080c, 0x8940, 0x00d6, 0x2069, 0xaf9d, 0x2d04,
+ 0xa005, 0x0168, 0x6018, 0x2068, 0x68a0, 0xa086, 0x007e, 0x1138,
+ 0x2069, 0xad1c, 0x2d04, 0x8000, 0x206a, 0x00de, 0x0010, 0x00de,
+ 0x0078, 0x2001, 0x0000, 0x080c, 0x4c1e, 0x2001, 0x0002, 0x080c,
+ 0x4c30, 0x6003, 0x0001, 0x6007, 0x0002, 0x080c, 0x67ee, 0x0428,
+ 0x080c, 0x85f3, 0x2009, 0xb28e, 0x2134, 0xa6b4, 0x00ff, 0xa686,
+ 0x0005, 0x01e0, 0xa686, 0x000b, 0x01b0, 0x2009, 0xb28f, 0x2104,
+ 0xa084, 0xff00, 0x1118, 0xa686, 0x0009, 0x0180, 0xa086, 0x1900,
+ 0x1150, 0xa686, 0x0009, 0x0150, 0x2001, 0x0004, 0x080c, 0x4c30,
+ 0x080c, 0x8078, 0x0010, 0x080c, 0x87bd, 0x0005, 0x00d6, 0x6010,
+ 0x2068, 0x080c, 0x9596, 0x0128, 0x6838, 0xd0fc, 0x0110, 0x00de,
+ 0x0c90, 0x6018, 0x2068, 0x6840, 0xa084, 0x00ff, 0xa005, 0x0140,
+ 0x8001, 0x6842, 0x6017, 0x000a, 0x6007, 0x0016, 0x00de, 0x0c28,
+ 0x68a0, 0xa086, 0x007e, 0x1138, 0x00e6, 0x2071, 0xad00, 0x080c,
+ 0x48f5, 0x00ee, 0x0010, 0x080c, 0x2ad9, 0x00de, 0x08a0, 0x080c,
+ 0x8940, 0x1158, 0x2001, 0x0004, 0x080c, 0x4c30, 0x6003, 0x0001,
+ 0x6007, 0x0003, 0x080c, 0x67ee, 0x0020, 0x080c, 0x85f3, 0x080c,
+ 0x87bd, 0x0005, 0x0469, 0x1158, 0x2001, 0x0008, 0x080c, 0x4c30,
+ 0x6003, 0x0001, 0x6007, 0x0005, 0x080c, 0x67ee, 0x0010, 0x080c,
+ 0x87bd, 0x0005, 0x00e9, 0x1158, 0x2001, 0x000a, 0x080c, 0x4c30,
+ 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x67ee, 0x0010, 0x080c,
+ 0x87bd, 0x0005, 0x2009, 0xb28e, 0x2104, 0xa086, 0x0003, 0x1138,
+ 0x2009, 0xb28f, 0x2104, 0xa084, 0xff00, 0xa086, 0x2a00, 0x0005,
+ 0xa085, 0x0001, 0x0005, 0x00c6, 0x0016, 0xac88, 0x0006, 0x2164,
+ 0x080c, 0x4ceb, 0x001e, 0x00ce, 0x0005, 0x00f6, 0x00e6, 0x00d6,
+ 0x0036, 0x0016, 0x6018, 0x2068, 0x2071, 0xad34, 0x2e04, 0xa085,
+ 0x0003, 0x2072, 0x080c, 0x89cc, 0x0538, 0x2001, 0xad52, 0x2004,
+ 0xd0a4, 0x0158, 0xa006, 0x2020, 0x2009, 0x002a, 0x080c, 0xa96c,
+ 0x2001, 0xad0c, 0x200c, 0xc195, 0x2102, 0x2019, 0x002a, 0x2009,
+ 0x0001, 0x080c, 0x2aac, 0x2071, 0xad00, 0x080c, 0x28fa, 0x00c6,
+ 0x0156, 0x20a9, 0x0081, 0x2009, 0x007f, 0x080c, 0x2bc9, 0x8108,
+ 0x1f04, 0x897d, 0x015e, 0x00ce, 0x080c, 0x8943, 0x6813, 0x00ff,
+ 0x6817, 0xfffe, 0x2071, 0xb280, 0x2079, 0x0100, 0x2e04, 0xa084,
+ 0x00ff, 0x2069, 0xad1b, 0x206a, 0x78e6, 0x0006, 0x8e70, 0x2e04,
+ 0x2069, 0xad1c, 0x206a, 0x78ea, 0x7832, 0x7836, 0x2010, 0xa084,
+ 0xff00, 0x001e, 0xa105, 0x2009, 0xad27, 0x200a, 0x2200, 0xa084,
+ 0x00ff, 0x2008, 0x080c, 0x26a0, 0x080c, 0x574f, 0x0170, 0x2069,
+ 0xb28e, 0x2071, 0xaf9f, 0x6810, 0x2072, 0x6814, 0x7006, 0x6818,
+ 0x700a, 0x681c, 0x700e, 0x080c, 0x9903, 0x0040, 0x2001, 0x0006,
+ 0x080c, 0x4c30, 0x080c, 0x2aff, 0x080c, 0x8078, 0x001e, 0x003e,
+ 0x00de, 0x00ee, 0x00fe, 0x0005, 0x0026, 0x0036, 0x00e6, 0x0156,
+ 0x2019, 0xad27, 0x231c, 0x83ff, 0x01e8, 0x2071, 0xb280, 0x2e14,
+ 0xa294, 0x00ff, 0x7004, 0xa084, 0xff00, 0xa205, 0xa306, 0x1190,
+ 0x2011, 0xb296, 0xad98, 0x000a, 0x20a9, 0x0004, 0x080c, 0x8a7c,
+ 0x1148, 0x2011, 0xb29a, 0xad98, 0x0006, 0x20a9, 0x0004, 0x080c,
+ 0x8a7c, 0x1100, 0x015e, 0x00ee, 0x003e, 0x002e, 0x0005, 0x00e6,
+ 0x2071, 0xb28c, 0x7004, 0xa086, 0x0014, 0x11a8, 0x7008, 0xa086,
+ 0x0800, 0x1188, 0x700c, 0xd0ec, 0x0160, 0xa084, 0x0f00, 0xa086,
+ 0x0100, 0x1138, 0x7024, 0xd0a4, 0x1110, 0xd0ac, 0x0110, 0xa006,
+ 0x0010, 0xa085, 0x0001, 0x00ee, 0x0005, 0x00e6, 0x00d6, 0x00c6,
+ 0x0076, 0x0056, 0x0046, 0x0026, 0x0006, 0x0126, 0x2091, 0x8000,
+ 0x2029, 0xafd0, 0x252c, 0x2021, 0xafd6, 0x2424, 0x2061, 0xb400,
+ 0x2071, 0xad00, 0x7244, 0x7064, 0xa202, 0x16f0, 0x080c, 0xa990,
+ 0x05a0, 0x671c, 0xa786, 0x0001, 0x0580, 0xa786, 0x0007, 0x0568,
+ 0x2500, 0xac06, 0x0550, 0x2400, 0xac06, 0x0538, 0x00c6, 0x6000,
+ 0xa086, 0x0004, 0x1110, 0x080c, 0x190b, 0xa786, 0x0008, 0x1148,
+ 0x080c, 0x9789, 0x1130, 0x00ce, 0x080c, 0x85f3, 0x080c, 0x974e,
+ 0x00a0, 0x6010, 0x2068, 0x080c, 0x9596, 0x0160, 0xa786, 0x0003,
+ 0x11e8, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000, 0x080c, 0x510c,
+ 0x080c, 0x9742, 0x080c, 0x974e, 0x00ce, 0xace0, 0x0018, 0x7058,
+ 0xac02, 0x1210, 0x0804, 0x8a2a, 0x012e, 0x000e, 0x002e, 0x004e,
+ 0x005e, 0x007e, 0x00ce, 0x00de, 0x00ee, 0x0005, 0xa786, 0x0006,
+ 0x1d00, 0x080c, 0xa91f, 0x0c30, 0x220c, 0x2304, 0xa106, 0x1130,
+ 0x8210, 0x8318, 0x1f04, 0x8a7c, 0xa006, 0x0005, 0x2304, 0xa102,
+ 0x0218, 0x2001, 0x0001, 0x0010, 0x2001, 0x0000, 0xa18d, 0x0001,
+ 0x0005, 0x6004, 0xa08a, 0x0080, 0x1a0c, 0x14f6, 0x080c, 0x9778,
+ 0x0120, 0x080c, 0x9789, 0x0168, 0x0028, 0x080c, 0x2aff, 0x080c,
+ 0x9789, 0x0138, 0x080c, 0x6b73, 0x080c, 0x8078, 0x080c, 0x6c50,
+ 0x0005, 0x080c, 0x85f3, 0x0cb0, 0xa182, 0x0040, 0x0002, 0x8ac2,
+ 0x8ac2, 0x8ac2, 0x8ac2, 0x8ac2, 0x8ac2, 0x8ac2, 0x8ac2, 0x8ac2,
+ 0x8ac2, 0x8ac2, 0x8ac4, 0x8ac4, 0x8ac4, 0x8ac4, 0x8ac2, 0x8ac2,
+ 0x8ac2, 0x8ac4, 0x080c, 0x14f6, 0x600b, 0xffff, 0x6003, 0x0001,
+ 0x6106, 0x080c, 0x67a8, 0x0126, 0x2091, 0x8000, 0x080c, 0x6c50,
+ 0x012e, 0x0005, 0xa186, 0x0013, 0x1128, 0x6004, 0xa082, 0x0040,
+ 0x0804, 0x8b5e, 0xa186, 0x0027, 0x11e8, 0x080c, 0x6b73, 0x080c,
+ 0x2ad9, 0x00d6, 0x6110, 0x2168, 0x080c, 0x9596, 0x0168, 0x6837,
+ 0x0103, 0x684b, 0x0029, 0x6847, 0x0000, 0x694c, 0xc1c5, 0x694e,
+ 0x080c, 0x510c, 0x080c, 0x9742, 0x00de, 0x080c, 0x8078, 0x080c,
+ 0x6c50, 0x0005, 0xa186, 0x0014, 0x1120, 0x6004, 0xa082, 0x0040,
+ 0x0428, 0xa186, 0x0046, 0x0138, 0xa186, 0x0045, 0x0120, 0xa186,
+ 0x0047, 0x190c, 0x14f6, 0x2001, 0x0109, 0x2004, 0xd084, 0x0198,
+ 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x080c, 0x6699,
+ 0x002e, 0x001e, 0x000e, 0x012e, 0xe000, 0x6000, 0xa086, 0x0002,
+ 0x1110, 0x0804, 0x8b98, 0x080c, 0x80be, 0x0005, 0x0002, 0x8b3c,
+ 0x8b3a, 0x8b3a, 0x8b3a, 0x8b3a, 0x8b3a, 0x8b3a, 0x8b3a, 0x8b3a,
+ 0x8b3a, 0x8b3a, 0x8b57, 0x8b57, 0x8b57, 0x8b57, 0x8b3a, 0x8b57,
+ 0x8b3a, 0x8b57, 0x080c, 0x14f6, 0x080c, 0x6b73, 0x00d6, 0x6110,
+ 0x2168, 0x080c, 0x9596, 0x0168, 0x6837, 0x0103, 0x684b, 0x0006,
+ 0x6847, 0x0000, 0x6850, 0xc0ec, 0x6852, 0x080c, 0x510c, 0x080c,
+ 0x9742, 0x00de, 0x080c, 0x8078, 0x080c, 0x6c50, 0x0005, 0x080c,
+ 0x6b73, 0x080c, 0x8078, 0x080c, 0x6c50, 0x0005, 0x0002, 0x8b74,
+ 0x8b72, 0x8b72, 0x8b72, 0x8b72, 0x8b72, 0x8b72, 0x8b72, 0x8b72,
+ 0x8b72, 0x8b72, 0x8b86, 0x8b86, 0x8b86, 0x8b86, 0x8b72, 0x8b91,
+ 0x8b72, 0x8b86, 0x080c, 0x14f6, 0x080c, 0x6b73, 0x2001, 0xafa5,
+ 0x2004, 0x603e, 0x6003, 0x0002, 0x080c, 0x6c50, 0x6010, 0xa088,
+ 0x0013, 0x2104, 0xa085, 0x0400, 0x200a, 0x0005, 0x080c, 0x6b73,
+ 0x2001, 0xafa5, 0x2004, 0x603e, 0x6003, 0x000f, 0x080c, 0x6c50,
+ 0x0005, 0x080c, 0x6b73, 0x080c, 0x8078, 0x080c, 0x6c50, 0x0005,
+ 0xa182, 0x0040, 0x0002, 0x8bae, 0x8bae, 0x8bae, 0x8bae, 0x8bae,
+ 0x8bb0, 0x8c88, 0x8ca9, 0x8bae, 0x8bae, 0x8bae, 0x8bae, 0x8bae,
+ 0x8bae, 0x8bae, 0x8bae, 0x8bae, 0x8bae, 0x8bae, 0x080c, 0x14f6,
+ 0x00e6, 0x00d6, 0x603f, 0x0000, 0x2071, 0xb280, 0x7124, 0x610a,
+ 0x2071, 0xb28c, 0x6110, 0x2168, 0x7614, 0xa6b4, 0x0fff, 0x86ff,
+ 0x0904, 0x8c54, 0xa68c, 0x0c00, 0x01e8, 0x00f6, 0x2c78, 0x080c,
+ 0x5029, 0x00fe, 0x0198, 0x684c, 0xd0ac, 0x0180, 0x6020, 0xd0dc,
+ 0x1168, 0x6850, 0xd0bc, 0x1150, 0x7318, 0x6814, 0xa306, 0x1904,
+ 0x8c66, 0x731c, 0x6810, 0xa306, 0x1904, 0x8c66, 0x7318, 0x6b62,
+ 0x731c, 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002, 0x0518, 0xa186,
+ 0x0028, 0x1128, 0x080c, 0x9767, 0x684b, 0x001c, 0x00e8, 0xd6dc,
+ 0x01a0, 0x684b, 0x0015, 0x684c, 0xd0ac, 0x0170, 0x6914, 0x6a10,
+ 0x2100, 0xa205, 0x0148, 0x7018, 0xa106, 0x1118, 0x701c, 0xa206,
+ 0x0118, 0x6962, 0x6a5e, 0xc6dc, 0x0038, 0xd6d4, 0x0118, 0x684b,
+ 0x0007, 0x0010, 0x684b, 0x0000, 0x6837, 0x0103, 0x6e46, 0xa01e,
+ 0xd6c4, 0x01f0, 0xa686, 0x0100, 0x1140, 0x2001, 0xb299, 0x2004,
+ 0xa005, 0x1118, 0xc6c4, 0x0804, 0x8bbf, 0x7328, 0x732c, 0x6b56,
+ 0x83ff, 0x0170, 0xa38a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036,
+ 0x2308, 0x2019, 0xb298, 0xad90, 0x0019, 0x080c, 0x927f, 0x003e,
+ 0xd6cc, 0x0904, 0x8c79, 0x7124, 0x695a, 0x81ff, 0x0904, 0x8c79,
+ 0xa192, 0x0021, 0x1250, 0x2071, 0xb298, 0x831c, 0x2300, 0xae18,
+ 0xad90, 0x001d, 0x080c, 0x927f, 0x04a0, 0x6838, 0xd0fc, 0x0120,
+ 0x2009, 0x0020, 0x695a, 0x0c78, 0x00f6, 0x2d78, 0x080c, 0x9224,
+ 0x00fe, 0x080c, 0x926f, 0x0438, 0x00f6, 0x2c78, 0x080c, 0x5029,
+ 0x00fe, 0x0188, 0x684c, 0xd0ac, 0x0170, 0x6020, 0xd0dc, 0x1158,
+ 0x6850, 0xd0bc, 0x1140, 0x684c, 0xd0f4, 0x1128, 0x080c, 0x9866,
+ 0x00de, 0x00ee, 0x00e0, 0x684b, 0x0000, 0x6837, 0x0103, 0x6e46,
+ 0x684c, 0xd0ac, 0x0130, 0x6810, 0x6914, 0xa115, 0x0110, 0x080c,
+ 0x8e04, 0x080c, 0x510c, 0x6218, 0x2268, 0x6a3c, 0x8211, 0x6a3e,
+ 0x080c, 0x9834, 0x00de, 0x00ee, 0x1110, 0x080c, 0x8078, 0x0005,
+ 0x00f6, 0x6003, 0x0003, 0x2079, 0xb28c, 0x7c04, 0x7b00, 0x7e0c,
+ 0x7d08, 0x6010, 0x2078, 0x784c, 0xd0ac, 0x0120, 0x6003, 0x0002,
+ 0x00fe, 0x0005, 0x7c12, 0x7b16, 0x7e0a, 0x7d0e, 0x00fe, 0x603f,
+ 0x0000, 0x2c10, 0x080c, 0x1e6e, 0x080c, 0x680b, 0x080c, 0x6d0d,
+ 0x0005, 0x2001, 0xafa5, 0x2004, 0x603e, 0x6003, 0x0004, 0x6110,
+ 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x080c, 0x1824, 0x0005,
+ 0xa182, 0x0040, 0x0002, 0x8cce, 0x8cce, 0x8cce, 0x8cce, 0x8cce,
+ 0x8cd0, 0x8d61, 0x8cce, 0x8cce, 0x8d77, 0x8ddb, 0x8cce, 0x8cce,
+ 0x8cce, 0x8cce, 0x8dea, 0x8cce, 0x8cce, 0x8cce, 0x080c, 0x14f6,
+ 0x0076, 0x00f6, 0x00e6, 0x00d6, 0x2071, 0xb28c, 0x6110, 0x2178,
+ 0x7614, 0xa6b4, 0x0fff, 0x7e46, 0x7f4c, 0xc7e5, 0x7f4e, 0x6218,
+ 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x86ff, 0x0904, 0x8d5c, 0xa694,
+ 0xff00, 0xa284, 0x0c00, 0x0120, 0x7018, 0x7862, 0x701c, 0x785e,
+ 0xa284, 0x0300, 0x0904, 0x8d5c, 0x080c, 0x15d9, 0x090c, 0x14f6,
+ 0x2d00, 0x784a, 0x7f4c, 0xc7cd, 0x7f4e, 0x6837, 0x0103, 0x7838,
+ 0x683a, 0x783c, 0x683e, 0x7840, 0x6842, 0x6e46, 0xa68c, 0x0c00,
+ 0x0120, 0x7318, 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff, 0xa186,
+ 0x0002, 0x0180, 0xa186, 0x0028, 0x1118, 0x684b, 0x001c, 0x0060,
+ 0xd6dc, 0x0118, 0x684b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0x684b,
+ 0x0007, 0x0010, 0x684b, 0x0000, 0x6f4e, 0x7850, 0x6852, 0x7854,
+ 0x6856, 0xa01e, 0xd6c4, 0x0198, 0x7328, 0x732c, 0x6b56, 0x83ff,
+ 0x0170, 0xa38a, 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308,
+ 0x2019, 0xb298, 0xad90, 0x0019, 0x080c, 0x927f, 0x003e, 0xd6cc,
+ 0x01d8, 0x7124, 0x695a, 0x81ff, 0x01b8, 0xa192, 0x0021, 0x1250,
+ 0x2071, 0xb298, 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x080c,
+ 0x927f, 0x0050, 0x7838, 0xd0fc, 0x0120, 0x2009, 0x0020, 0x695a,
+ 0x0c78, 0x2d78, 0x080c, 0x9224, 0x00de, 0x00ee, 0x00fe, 0x007e,
+ 0x0005, 0x00f6, 0x6003, 0x0003, 0x2079, 0xb28c, 0x7c04, 0x7b00,
+ 0x7e0c, 0x7d08, 0x6010, 0x2078, 0x7c12, 0x7b16, 0x7e0a, 0x7d0e,
+ 0x00fe, 0x2c10, 0x080c, 0x1e6e, 0x080c, 0x781a, 0x0005, 0x00d6,
+ 0x00f6, 0x2c78, 0x080c, 0x5029, 0x00fe, 0x0120, 0x2001, 0xafa5,
+ 0x2004, 0x603e, 0x6003, 0x0002, 0x080c, 0x6c05, 0x080c, 0x6d0d,
+ 0x6110, 0x2168, 0x694c, 0xd1e4, 0x0904, 0x8dd9, 0xd1cc, 0x0540,
+ 0x6948, 0x6838, 0xd0fc, 0x01e8, 0x0016, 0x684c, 0x0006, 0x6850,
+ 0x0006, 0xad90, 0x000d, 0xa198, 0x000d, 0x2009, 0x0020, 0x0156,
+ 0x21a8, 0x2304, 0x2012, 0x8318, 0x8210, 0x1f04, 0x8da1, 0x015e,
+ 0x000e, 0x6852, 0x000e, 0x684e, 0x001e, 0x2168, 0x080c, 0x1600,
+ 0x0418, 0x0016, 0x080c, 0x1600, 0x00de, 0x080c, 0x926f, 0x00e0,
+ 0x6837, 0x0103, 0x6944, 0xa184, 0x00ff, 0xa0b6, 0x0002, 0x0180,
+ 0xa086, 0x0028, 0x1118, 0x684b, 0x001c, 0x0060, 0xd1dc, 0x0118,
+ 0x684b, 0x0015, 0x0038, 0xd1d4, 0x0118, 0x684b, 0x0007, 0x0010,
+ 0x684b, 0x0000, 0x080c, 0x510c, 0x080c, 0x9834, 0x1110, 0x080c,
+ 0x8078, 0x00de, 0x0005, 0x2019, 0x0001, 0x080c, 0x7a64, 0x6003,
+ 0x0002, 0x2001, 0xafa5, 0x2004, 0x603e, 0x080c, 0x6c05, 0x080c,
+ 0x6d0d, 0x0005, 0x080c, 0x6c05, 0x080c, 0x2ad9, 0x00d6, 0x6110,
+ 0x2168, 0x080c, 0x9596, 0x0150, 0x6837, 0x0103, 0x684b, 0x0029,
+ 0x6847, 0x0000, 0x080c, 0x510c, 0x080c, 0x9742, 0x00de, 0x080c,
+ 0x8078, 0x080c, 0x6d0d, 0x0005, 0x684b, 0x0015, 0xd1fc, 0x0138,
+ 0x684b, 0x0007, 0x8002, 0x8000, 0x810a, 0xa189, 0x0000, 0x6962,
+ 0x685e, 0x0005, 0xa182, 0x0040, 0x0002, 0x8e28, 0x8e28, 0x8e28,
+ 0x8e28, 0x8e28, 0x8e2a, 0x8e28, 0x8ee3, 0x8eef, 0x8e28, 0x8e28,
+ 0x8e28, 0x8e28, 0x8e28, 0x8e28, 0x8e28, 0x8e28, 0x8e28, 0x8e28,
+ 0x080c, 0x14f6, 0x0076, 0x00f6, 0x00e6, 0x00d6, 0x2071, 0xb28c,
+ 0x6110, 0x2178, 0x7614, 0xa6b4, 0x0fff, 0x00f6, 0x2c78, 0x080c,
+ 0x5029, 0x00fe, 0x0150, 0xa684, 0x00ff, 0x1138, 0x6020, 0xd0f4,
+ 0x0120, 0x080c, 0x9866, 0x0804, 0x8ede, 0x7e46, 0x7f4c, 0xc7e5,
+ 0x7f4e, 0x6218, 0x2268, 0x6a3c, 0x8211, 0x6a3e, 0x86ff, 0x0904,
+ 0x8ed4, 0xa694, 0xff00, 0xa284, 0x0c00, 0x0120, 0x7018, 0x7862,
+ 0x701c, 0x785e, 0xa284, 0x0300, 0x0904, 0x8ed2, 0xa686, 0x0100,
+ 0x1140, 0x2001, 0xb299, 0x2004, 0xa005, 0x1118, 0xc6c4, 0x7e46,
+ 0x0c28, 0x080c, 0x15d9, 0x090c, 0x14f6, 0x2d00, 0x784a, 0x7f4c,
+ 0xa7bd, 0x0200, 0x7f4e, 0x6837, 0x0103, 0x7838, 0x683a, 0x783c,
+ 0x683e, 0x7840, 0x6842, 0x6e46, 0xa68c, 0x0c00, 0x0120, 0x7318,
+ 0x6b62, 0x731c, 0x6b5e, 0xa68c, 0x00ff, 0xa186, 0x0002, 0x0180,
+ 0xa186, 0x0028, 0x1118, 0x684b, 0x001c, 0x0060, 0xd6dc, 0x0118,
+ 0x684b, 0x0015, 0x0038, 0xd6d4, 0x0118, 0x684b, 0x0007, 0x0010,
+ 0x684b, 0x0000, 0x6f4e, 0x7850, 0x6852, 0x7854, 0x6856, 0xa01e,
+ 0xd6c4, 0x0198, 0x7328, 0x732c, 0x6b56, 0x83ff, 0x0170, 0xa38a,
+ 0x0009, 0x0210, 0x2019, 0x0008, 0x0036, 0x2308, 0x2019, 0xb298,
+ 0xad90, 0x0019, 0x080c, 0x927f, 0x003e, 0xd6cc, 0x01d8, 0x7124,
+ 0x695a, 0x81ff, 0x01b8, 0xa192, 0x0021, 0x1250, 0x2071, 0xb298,
+ 0x831c, 0x2300, 0xae18, 0xad90, 0x001d, 0x080c, 0x927f, 0x0050,
+ 0x7838, 0xd0fc, 0x0120, 0x2009, 0x0020, 0x695a, 0x0c78, 0x2d78,
+ 0x080c, 0x9224, 0xd6dc, 0x1110, 0xa006, 0x0030, 0x2001, 0x0001,
+ 0x2071, 0xb28c, 0x7218, 0x731c, 0x080c, 0x186f, 0x00de, 0x00ee,
+ 0x00fe, 0x007e, 0x0005, 0x2001, 0xafa5, 0x2004, 0x603e, 0x20e1,
+ 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x080c, 0x1824, 0x0005, 0x2001,
+ 0xafa5, 0x2004, 0x603e, 0x00d6, 0x6003, 0x0002, 0x6110, 0x2168,
+ 0x694c, 0xd1e4, 0x0904, 0x8ff3, 0x603f, 0x0000, 0x00f6, 0x2c78,
+ 0x080c, 0x5029, 0x00fe, 0x0548, 0x6814, 0x6910, 0xa115, 0x0528,
+ 0x6a60, 0xa206, 0x1118, 0x685c, 0xa106, 0x01f8, 0x684c, 0xc0e4,
+ 0x684e, 0x6847, 0x0000, 0x6863, 0x0000, 0x685f, 0x0000, 0x697c,
+ 0x6810, 0xa102, 0x603a, 0x6980, 0x6814, 0xa103, 0x6036, 0x6020,
+ 0xc0f5, 0x6022, 0x00d6, 0x6018, 0x2068, 0x683c, 0x8000, 0x683e,
+ 0x00de, 0x080c, 0x9866, 0x0804, 0x8ff3, 0x694c, 0xd1cc, 0x0904,
+ 0x8fc3, 0x6948, 0x6838, 0xd0fc, 0x0904, 0x8f88, 0x0016, 0x684c,
+ 0x0006, 0x6850, 0x0006, 0x00f6, 0x2178, 0x7944, 0xa184, 0x00ff,
+ 0xa0b6, 0x0002, 0x01e0, 0xa086, 0x0028, 0x1128, 0x684b, 0x001c,
+ 0x784b, 0x001c, 0x00e8, 0xd1dc, 0x0158, 0x684b, 0x0015, 0x784b,
+ 0x0015, 0x080c, 0x99ee, 0x0118, 0x7944, 0xc1dc, 0x7946, 0x0080,
+ 0xd1d4, 0x0128, 0x684b, 0x0007, 0x784b, 0x0007, 0x0048, 0x684c,
+ 0xd0ac, 0x0130, 0x6810, 0x6914, 0xa115, 0x0110, 0x080c, 0x8e04,
+ 0x6848, 0x784a, 0x6860, 0x7862, 0x685c, 0x785e, 0xad90, 0x000d,
+ 0xaf98, 0x000d, 0x2009, 0x0020, 0x0156, 0x21a8, 0x2304, 0x2012,
+ 0x8318, 0x8210, 0x1f04, 0x8f76, 0x015e, 0x00fe, 0x000e, 0x6852,
+ 0x000e, 0x684e, 0x001e, 0x2168, 0x080c, 0x1600, 0x0804, 0x8fee,
+ 0x0016, 0x00f6, 0x2178, 0x7944, 0xa184, 0x00ff, 0xa0b6, 0x0002,
+ 0x01e0, 0xa086, 0x0028, 0x1128, 0x684b, 0x001c, 0x784b, 0x001c,
+ 0x00e8, 0xd1dc, 0x0158, 0x684b, 0x0015, 0x784b, 0x0015, 0x080c,
+ 0x99ee, 0x0118, 0x7944, 0xc1dc, 0x7946, 0x0080, 0xd1d4, 0x0128,
+ 0x684b, 0x0007, 0x784b, 0x0007, 0x0048, 0x684c, 0xd0ac, 0x0130,
+ 0x6810, 0x6914, 0xa115, 0x0110, 0x080c, 0x8e04, 0x6860, 0x7862,
+ 0x685c, 0x785e, 0x684c, 0x784e, 0x00fe, 0x080c, 0x1600, 0x00de,
+ 0x080c, 0x926f, 0x0458, 0x6837, 0x0103, 0x6944, 0xa184, 0x00ff,
+ 0xa0b6, 0x0002, 0x01b0, 0xa086, 0x0028, 0x1118, 0x684b, 0x001c,
+ 0x00d8, 0xd1dc, 0x0148, 0x684b, 0x0015, 0x080c, 0x99ee, 0x0118,
+ 0x6944, 0xc1dc, 0x6946, 0x0080, 0xd1d4, 0x0118, 0x684b, 0x0007,
+ 0x0058, 0x684b, 0x0000, 0x684c, 0xd0ac, 0x0130, 0x6810, 0x6914,
+ 0xa115, 0x0110, 0x080c, 0x8e04, 0x080c, 0x510c, 0x080c, 0x9834,
+ 0x1110, 0x080c, 0x8078, 0x00de, 0x0005, 0x080c, 0x6b73, 0x0010,
+ 0x080c, 0x6c05, 0x080c, 0x9596, 0x01c0, 0x00d6, 0x6110, 0x2168,
+ 0x6837, 0x0103, 0x2009, 0xad0c, 0x210c, 0xd18c, 0x11c0, 0xd184,
+ 0x1198, 0x6108, 0x694a, 0xa18e, 0x0029, 0x1110, 0x080c, 0xabfa,
+ 0x6847, 0x0000, 0x080c, 0x510c, 0x00de, 0x080c, 0x8078, 0x080c,
+ 0x6c50, 0x080c, 0x6d0d, 0x0005, 0x684b, 0x0004, 0x0c88, 0x684b,
+ 0x0004, 0x0c70, 0xa182, 0x0040, 0x0002, 0x9038, 0x9038, 0x9038,
+ 0x9038, 0x9038, 0x903a, 0x9038, 0x903d, 0x9038, 0x9038, 0x9038,
+ 0x9038, 0x9038, 0x9038, 0x9038, 0x9038, 0x9038, 0x9038, 0x9038,
+ 0x080c, 0x14f6, 0x080c, 0x8078, 0x0005, 0x0006, 0x0026, 0xa016,
+ 0x080c, 0x1824, 0x002e, 0x000e, 0x0005, 0xa182, 0x0085, 0x0002,
+ 0x9051, 0x904f, 0x904f, 0x905d, 0x904f, 0x904f, 0x904f, 0x080c,
+ 0x14f6, 0x6003, 0x0001, 0x6106, 0x080c, 0x67a8, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x6c50, 0x012e, 0x0005, 0x0026, 0x0056, 0x00d6,
+ 0x00e6, 0x2071, 0xb280, 0x7224, 0x6212, 0x7220, 0x080c, 0x9586,
+ 0x01a0, 0x2268, 0x6800, 0xa086, 0x0000, 0x0178, 0x6018, 0x6d18,
+ 0xa52e, 0x1158, 0x00c6, 0x2d60, 0x080c, 0x928f, 0x00ce, 0x0128,
+ 0x6803, 0x0002, 0x6007, 0x0086, 0x0010, 0x6007, 0x0087, 0x6003,
+ 0x0001, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x00f6, 0x2278, 0x080c,
+ 0x5029, 0x00fe, 0x0150, 0x6820, 0xd0ec, 0x0138, 0x00c6, 0x2260,
+ 0x603f, 0x0000, 0x080c, 0x9866, 0x00ce, 0x00ee, 0x00de, 0x005e,
+ 0x002e, 0x0005, 0xa186, 0x0013, 0x1160, 0x6004, 0xa08a, 0x0085,
+ 0x0a0c, 0x14f6, 0xa08a, 0x008c, 0x1a0c, 0x14f6, 0xa082, 0x0085,
+ 0x0072, 0xa186, 0x0027, 0x0120, 0xa186, 0x0014, 0x190c, 0x14f6,
+ 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005, 0x90be,
+ 0x90c0, 0x90c0, 0x90be, 0x90be, 0x90be, 0x90be, 0x080c, 0x14f6,
+ 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005, 0xa186,
+ 0x0013, 0x1128, 0x6004, 0xa082, 0x0085, 0x2008, 0x04a8, 0xa186,
+ 0x0027, 0x11e8, 0x080c, 0x6b73, 0x080c, 0x2ad9, 0x00d6, 0x6010,
+ 0x2068, 0x080c, 0x9596, 0x0150, 0x6837, 0x0103, 0x6847, 0x0000,
+ 0x684b, 0x0029, 0x080c, 0x510c, 0x080c, 0x9742, 0x00de, 0x080c,
+ 0x8078, 0x080c, 0x6c50, 0x0005, 0x080c, 0x80be, 0x0ce0, 0xa186,
+ 0x0014, 0x1dd0, 0x080c, 0x6b73, 0x00d6, 0x6010, 0x2068, 0x080c,
+ 0x9596, 0x0d60, 0x6837, 0x0103, 0x6847, 0x0000, 0x684b, 0x0006,
+ 0x6850, 0xc0ec, 0x6852, 0x08f0, 0x0002, 0x910e, 0x910c, 0x910c,
+ 0x910c, 0x910c, 0x910c, 0x9126, 0x080c, 0x14f6, 0x080c, 0x6b73,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0118, 0xa186,
+ 0x0035, 0x1118, 0x2001, 0xafa3, 0x0010, 0x2001, 0xafa4, 0x2004,
+ 0x6016, 0x6003, 0x000c, 0x080c, 0x6c50, 0x0005, 0x080c, 0x6b73,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0118, 0xa186,
+ 0x0035, 0x1118, 0x2001, 0xafa3, 0x0010, 0x2001, 0xafa4, 0x2004,
+ 0x6016, 0x6003, 0x000e, 0x080c, 0x6c50, 0x0005, 0xa182, 0x008c,
+ 0x1220, 0xa182, 0x0085, 0x0208, 0x001a, 0x080c, 0x80be, 0x0005,
+ 0x914f, 0x914f, 0x914f, 0x914f, 0x9151, 0x91a4, 0x914f, 0x080c,
+ 0x14f6, 0x00d6, 0x00f6, 0x2c78, 0x080c, 0x5029, 0x00fe, 0x0168,
+ 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0039, 0x0118, 0xa186,
+ 0x0035, 0x1118, 0x00de, 0x0804, 0x91b7, 0x080c, 0x9742, 0x080c,
+ 0x9596, 0x01c8, 0x6010, 0x2068, 0x6837, 0x0103, 0x6850, 0xd0b4,
+ 0x0128, 0x684b, 0x0006, 0xc0ec, 0x6852, 0x0048, 0xd0bc, 0x0118,
+ 0x684b, 0x0002, 0x0020, 0x684b, 0x0005, 0x080c, 0x9803, 0x6847,
+ 0x0000, 0x080c, 0x510c, 0x2c68, 0x080c, 0x8022, 0x01c0, 0x6003,
+ 0x0001, 0x6007, 0x001e, 0x600b, 0xffff, 0x2009, 0xb28e, 0x210c,
+ 0x6136, 0x2009, 0xb28f, 0x210c, 0x613a, 0x6918, 0x611a, 0x080c,
+ 0x9956, 0x6950, 0x6152, 0x601f, 0x0001, 0x080c, 0x67a8, 0x2d60,
+ 0x080c, 0x8078, 0x00de, 0x0005, 0x00f6, 0x2c78, 0x080c, 0x5029,
+ 0x00fe, 0x0598, 0x6030, 0xa08c, 0xff00, 0x810f, 0xa186, 0x0035,
+ 0x0130, 0xa186, 0x001e, 0x0118, 0xa186, 0x0039, 0x1530, 0x00d6,
+ 0x2c68, 0x080c, 0x9a34, 0x1904, 0x91fc, 0x080c, 0x8022, 0x01d8,
+ 0x6106, 0x6003, 0x0001, 0x601f, 0x0001, 0x6918, 0x611a, 0x6928,
+ 0x612a, 0x692c, 0x612e, 0x6930, 0xa18c, 0x00ff, 0x6132, 0x6934,
+ 0x6136, 0x6938, 0x613a, 0x6950, 0x6152, 0x080c, 0x9956, 0x080c,
+ 0x67a8, 0x080c, 0x6c50, 0x2d60, 0x00f8, 0x00d6, 0x6010, 0x2068,
+ 0x080c, 0x9596, 0x01c8, 0x6837, 0x0103, 0x6850, 0xd0b4, 0x0128,
+ 0xc0ec, 0x6852, 0x684b, 0x0006, 0x0048, 0xd0bc, 0x0118, 0x684b,
+ 0x0002, 0x0020, 0x684b, 0x0005, 0x080c, 0x9803, 0x6847, 0x0000,
+ 0x080c, 0x510c, 0x080c, 0x9742, 0x00de, 0x080c, 0x8078, 0x0005,
+ 0x0016, 0x00d6, 0x6010, 0x2068, 0x080c, 0x9596, 0x0140, 0x6837,
+ 0x0103, 0x684b, 0x0028, 0x6847, 0x0000, 0x080c, 0x510c, 0x00de,
+ 0x001e, 0xa186, 0x0013, 0x0148, 0xa186, 0x0014, 0x0130, 0xa186,
+ 0x0027, 0x0118, 0x080c, 0x80be, 0x0030, 0x080c, 0x6b73, 0x080c,
+ 0x974e, 0x080c, 0x6c50, 0x0005, 0x0056, 0x0066, 0x00d6, 0x00f6,
+ 0x2029, 0x0001, 0xa182, 0x0101, 0x1208, 0x0010, 0x2009, 0x0100,
+ 0x2130, 0x2069, 0xb298, 0x831c, 0x2300, 0xad18, 0x2009, 0x0020,
+ 0xaf90, 0x001d, 0x080c, 0x927f, 0xa6b2, 0x0020, 0x7804, 0xa06d,
+ 0x0110, 0x080c, 0x1600, 0x080c, 0x15d9, 0x0500, 0x8528, 0x6837,
+ 0x0110, 0x683b, 0x0000, 0x2d20, 0x7c06, 0xa68a, 0x003d, 0x1228,
+ 0x2608, 0xad90, 0x000f, 0x0459, 0x0088, 0xa6b2, 0x003c, 0x2009,
+ 0x003c, 0x2d78, 0xad90, 0x000f, 0x0411, 0x0c28, 0x00fe, 0x852f,
+ 0xa5ad, 0x0003, 0x7d36, 0xa5ac, 0x0000, 0x0028, 0x00fe, 0x852f,
+ 0xa5ad, 0x0003, 0x7d36, 0x00de, 0x006e, 0x005e, 0x0005, 0x00f6,
+ 0x8dff, 0x0158, 0x6804, 0xa07d, 0x0130, 0x6807, 0x0000, 0x080c,
+ 0x510c, 0x2f68, 0x0cb8, 0x080c, 0x510c, 0x00fe, 0x0005, 0x0156,
+ 0xa184, 0x0001, 0x0108, 0x8108, 0x810c, 0x21a8, 0x2304, 0x8007,
+ 0x2012, 0x8318, 0x8210, 0x1f04, 0x9286, 0x015e, 0x0005, 0x0066,
+ 0x0126, 0x2091, 0x8000, 0x2031, 0x0001, 0x601c, 0xa084, 0x000f,
+ 0x0083, 0x012e, 0x006e, 0x0005, 0x0126, 0x2091, 0x8000, 0x0066,
+ 0x2031, 0x0000, 0x601c, 0xa084, 0x000f, 0x001b, 0x006e, 0x012e,
+ 0x0005, 0x92c3, 0x92c3, 0x92be, 0x92e5, 0x92b1, 0x92be, 0x92e5,
+ 0x92be, 0x080c, 0x14f6, 0x0036, 0x2019, 0x0010, 0x080c, 0xa566,
+ 0x601f, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005, 0xa006, 0x0005,
+ 0xa085, 0x0001, 0x0005, 0x00d6, 0x86ff, 0x11d8, 0x6010, 0x2068,
+ 0x080c, 0x9596, 0x01c0, 0x6834, 0xa086, 0x0139, 0x1128, 0x684b,
+ 0x0005, 0x6853, 0x0000, 0x0028, 0xa00e, 0x2001, 0x0005, 0x080c,
+ 0x51df, 0x080c, 0x9803, 0x080c, 0x510c, 0x080c, 0x8078, 0xa085,
+ 0x0001, 0x00de, 0x0005, 0xa006, 0x0ce0, 0x6000, 0xa08a, 0x0010,
+ 0x1a0c, 0x14f6, 0x000b, 0x0005, 0x92fc, 0x9319, 0x92fe, 0x9338,
+ 0x9316, 0x92fc, 0x92be, 0x92c3, 0x92c3, 0x92be, 0x92be, 0x92be,
+ 0x92be, 0x92be, 0x92be, 0x92be, 0x080c, 0x14f6, 0x86ff, 0x1198,
+ 0x00d6, 0x6010, 0x2068, 0x080c, 0x9596, 0x0110, 0x080c, 0x9803,
+ 0x00de, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x080c,
+ 0x67a8, 0x080c, 0x6c50, 0xa085, 0x0001, 0x0005, 0x080c, 0x190b,
+ 0x0c28, 0x00e6, 0x2071, 0xafc7, 0x7024, 0xac06, 0x1110, 0x080c,
+ 0x79e1, 0x601c, 0xa084, 0x000f, 0xa086, 0x0006, 0x1150, 0x0086,
+ 0x0096, 0x2049, 0x0001, 0x2c40, 0x080c, 0x7b9a, 0x009e, 0x008e,
+ 0x0010, 0x080c, 0x78de, 0x00ee, 0x1948, 0x080c, 0x92be, 0x0005,
+ 0x0036, 0x00e6, 0x2071, 0xafc7, 0x703c, 0xac06, 0x1140, 0x2019,
+ 0x0000, 0x080c, 0x7a64, 0x00ee, 0x003e, 0x0804, 0x92fe, 0x080c,
+ 0x7cb8, 0x00ee, 0x003e, 0x1904, 0x92fe, 0x080c, 0x92be, 0x0005,
+ 0x00c6, 0x601c, 0xa084, 0x000f, 0x0013, 0x00ce, 0x0005, 0x9369,
+ 0x93d3, 0x9501, 0x9374, 0x974e, 0x9369, 0xa558, 0x8078, 0x93d3,
+ 0x9362, 0x955f, 0x080c, 0x14f6, 0x080c, 0x9789, 0x1110, 0x080c,
+ 0x85f3, 0x0005, 0x080c, 0x6b73, 0x080c, 0x6c50, 0x080c, 0x8078,
+ 0x0005, 0x6017, 0x0001, 0x0005, 0x6010, 0xa080, 0x0019, 0x2c02,
+ 0x6000, 0xa08a, 0x0010, 0x1a0c, 0x14f6, 0x000b, 0x0005, 0x938f,
+ 0x9391, 0x93b1, 0x93c3, 0x93d0, 0x938f, 0x9369, 0x9369, 0x9369,
+ 0x93c3, 0x93c3, 0x938f, 0x938f, 0x938f, 0x938f, 0x93cd, 0x080c,
+ 0x14f6, 0x00e6, 0x6010, 0x2070, 0x7050, 0xc0b5, 0x7052, 0x2071,
+ 0xafc7, 0x7024, 0xac06, 0x0190, 0x080c, 0x78de, 0x6007, 0x0085,
+ 0x6003, 0x000b, 0x601f, 0x0002, 0x2001, 0xafa4, 0x2004, 0x6016,
+ 0x080c, 0x67a8, 0x080c, 0x6c50, 0x00ee, 0x0005, 0x6017, 0x0001,
+ 0x0cd8, 0x00d6, 0x6010, 0x2068, 0x6850, 0xc0b5, 0x6852, 0x00de,
+ 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x080c, 0x67a8,
+ 0x080c, 0x6c50, 0x0005, 0x00d6, 0x6017, 0x0001, 0x6010, 0x2068,
+ 0x6850, 0xc0b5, 0x6852, 0x00de, 0x0005, 0x080c, 0x8078, 0x0005,
+ 0x080c, 0x190b, 0x08f0, 0x6000, 0xa08a, 0x0010, 0x1a0c, 0x14f6,
+ 0x000b, 0x0005, 0x93ea, 0x9371, 0x93ec, 0x93ea, 0x93ec, 0x93ec,
+ 0x936a, 0x93ea, 0x9364, 0x9364, 0x93ea, 0x93ea, 0x93ea, 0x93ea,
+ 0x93ea, 0x93ea, 0x080c, 0x14f6, 0x00d6, 0x6018, 0x2068, 0x6804,
+ 0xa084, 0x00ff, 0x00de, 0xa08a, 0x000c, 0x1a0c, 0x14f6, 0x000b,
+ 0x0005, 0x9405, 0x94a7, 0x9407, 0x9441, 0x9407, 0x9441, 0x9407,
+ 0x9411, 0x9405, 0x9441, 0x9405, 0x942d, 0x080c, 0x14f6, 0x6004,
+ 0xa08e, 0x0016, 0x0588, 0xa08e, 0x0004, 0x0570, 0xa08e, 0x0002,
+ 0x0558, 0x6004, 0x080c, 0x9789, 0x0904, 0x94c0, 0xa08e, 0x0021,
+ 0x0904, 0x94c4, 0xa08e, 0x0022, 0x0904, 0x94c0, 0xa08e, 0x003d,
+ 0x0904, 0x94c4, 0xa08e, 0x0039, 0x0904, 0x94c8, 0xa08e, 0x0035,
+ 0x0904, 0x94c8, 0xa08e, 0x001e, 0x0188, 0xa08e, 0x0001, 0x1150,
+ 0x00d6, 0x6018, 0x2068, 0x6804, 0xa084, 0x00ff, 0x00de, 0xa086,
+ 0x0006, 0x0110, 0x080c, 0x2ad9, 0x080c, 0x85f3, 0x080c, 0x974e,
+ 0x0005, 0x00c6, 0x00d6, 0x6104, 0xa186, 0x0016, 0x0904, 0x9498,
+ 0xa186, 0x0002, 0x1518, 0x6018, 0x2068, 0x2001, 0xad34, 0x2004,
+ 0xd0ac, 0x1904, 0x94ea, 0x68a0, 0xd0bc, 0x1904, 0x94ea, 0x6840,
+ 0xa084, 0x00ff, 0xa005, 0x0190, 0x8001, 0x6842, 0x6013, 0x0000,
+ 0x601f, 0x0007, 0x6017, 0x0398, 0x603f, 0x0000, 0x080c, 0x8022,
+ 0x0128, 0x2d00, 0x601a, 0x601f, 0x0001, 0x0450, 0x00de, 0x00ce,
+ 0x6004, 0xa08e, 0x0002, 0x11a8, 0x6018, 0xa080, 0x0028, 0x2004,
+ 0xa086, 0x007e, 0x1170, 0x2009, 0xad34, 0x2104, 0xc085, 0x200a,
+ 0x00e6, 0x2071, 0xad00, 0x080c, 0x48f5, 0x00ee, 0x080c, 0x85f3,
+ 0x0020, 0x080c, 0x85f3, 0x080c, 0x2ad9, 0x00e6, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x2aff, 0x012e, 0x00ee, 0x080c, 0x974e, 0x0005,
+ 0x2001, 0x0002, 0x080c, 0x4c30, 0x6003, 0x0001, 0x6007, 0x0002,
+ 0x080c, 0x67ee, 0x080c, 0x6c50, 0x00de, 0x00ce, 0x0c80, 0x00c6,
+ 0x00d6, 0x6104, 0xa186, 0x0016, 0x0d58, 0x6018, 0x2068, 0x6840,
+ 0xa084, 0x00ff, 0xa005, 0x0904, 0x946e, 0x8001, 0x6842, 0x6003,
+ 0x0001, 0x080c, 0x67ee, 0x080c, 0x6c50, 0x00de, 0x00ce, 0x08b8,
+ 0x080c, 0x85f3, 0x0804, 0x943e, 0x080c, 0x8621, 0x0804, 0x943e,
+ 0x00d6, 0x2c68, 0x6104, 0x080c, 0x9a34, 0x00de, 0x0118, 0x080c,
+ 0x8078, 0x00b8, 0x6004, 0x8007, 0x6130, 0xa18c, 0x00ff, 0xa105,
+ 0x6032, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0002, 0x6038,
+ 0x600a, 0x2001, 0xafa4, 0x2004, 0x6016, 0x080c, 0x67a8, 0x080c,
+ 0x6c50, 0x0005, 0x00de, 0x00ce, 0x080c, 0x85f3, 0x080c, 0x2ad9,
+ 0x00e6, 0x0126, 0x2091, 0x8000, 0x080c, 0x2aff, 0x6013, 0x0000,
+ 0x601f, 0x0007, 0x6017, 0x0398, 0x603f, 0x0000, 0x012e, 0x00ee,
+ 0x0005, 0x6000, 0xa08a, 0x0010, 0x1a0c, 0x14f6, 0x000b, 0x0005,
+ 0x9518, 0x9518, 0x9518, 0x9518, 0x9518, 0x9518, 0x9518, 0x9518,
+ 0x9518, 0x9369, 0x9518, 0x9371, 0x951a, 0x9371, 0x9527, 0x9518,
+ 0x080c, 0x14f6, 0x6004, 0xa086, 0x008b, 0x0148, 0x6007, 0x008b,
+ 0x6003, 0x000d, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0005, 0x080c,
+ 0x9742, 0x080c, 0x9596, 0x0580, 0x080c, 0x2ad9, 0x00d6, 0x080c,
+ 0x9596, 0x0168, 0x6010, 0x2068, 0x6837, 0x0103, 0x684b, 0x0006,
+ 0x6847, 0x0000, 0x6850, 0xc0ed, 0x6852, 0x080c, 0x510c, 0x2c68,
+ 0x080c, 0x8022, 0x0150, 0x6818, 0x601a, 0x080c, 0x9956, 0x00c6,
+ 0x2d60, 0x080c, 0x974e, 0x00ce, 0x0008, 0x2d60, 0x00de, 0x6013,
+ 0x0000, 0x601f, 0x0001, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c,
+ 0x67ee, 0x080c, 0x6c50, 0x0010, 0x080c, 0x974e, 0x0005, 0x6000,
+ 0xa08a, 0x0010, 0x1a0c, 0x14f6, 0x000b, 0x0005, 0x9576, 0x9576,
+ 0x9576, 0x9578, 0x9579, 0x9576, 0x9576, 0x9576, 0x9576, 0x9576,
+ 0x9576, 0x9576, 0x9576, 0x9576, 0x9576, 0x9576, 0x080c, 0x14f6,
+ 0x0005, 0x080c, 0x7cb8, 0x190c, 0x14f6, 0x6110, 0x2168, 0x684b,
+ 0x0006, 0x080c, 0x510c, 0x080c, 0x8078, 0x0005, 0xa284, 0x0007,
+ 0x1158, 0xa282, 0xb400, 0x0240, 0x2001, 0xad16, 0x2004, 0xa202,
+ 0x1218, 0xa085, 0x0001, 0x0005, 0xa006, 0x0ce8, 0x0026, 0x6210,
+ 0xa294, 0xf000, 0x002e, 0x0005, 0x00e6, 0x00c6, 0x0036, 0x0006,
+ 0x0126, 0x2091, 0x8000, 0x2061, 0xb400, 0x2071, 0xad00, 0x7344,
+ 0x7064, 0xa302, 0x12a8, 0x601c, 0xa206, 0x1160, 0x080c, 0x98e3,
+ 0x0148, 0x080c, 0x9789, 0x1110, 0x080c, 0x85f3, 0x00c6, 0x080c,
+ 0x8078, 0x00ce, 0xace0, 0x0018, 0x7058, 0xac02, 0x1208, 0x0c38,
+ 0x012e, 0x000e, 0x003e, 0x00ce, 0x00ee, 0x0005, 0x00e6, 0x00c6,
+ 0x0016, 0xa188, 0xae34, 0x210c, 0x81ff, 0x0170, 0x2061, 0xb400,
+ 0x2071, 0xad00, 0x0016, 0x080c, 0x8022, 0x001e, 0x0138, 0x611a,
+ 0x080c, 0x2ad9, 0x080c, 0x8078, 0xa006, 0x0010, 0xa085, 0x0001,
+ 0x001e, 0x00ce, 0x00ee, 0x0005, 0x00c6, 0x0056, 0x0126, 0x2091,
+ 0x8000, 0x00c6, 0x080c, 0x8022, 0x005e, 0x0180, 0x6612, 0x651a,
+ 0x080c, 0x9956, 0x601f, 0x0003, 0x2009, 0x004b, 0x080c, 0x80a7,
+ 0xa085, 0x0001, 0x012e, 0x005e, 0x00ce, 0x0005, 0xa006, 0x0cd0,
+ 0x00c6, 0x0056, 0x0126, 0x2091, 0x8000, 0x62a0, 0x00c6, 0x080c,
+ 0x8022, 0x005e, 0x0508, 0x6013, 0x0000, 0x651a, 0x080c, 0x9956,
+ 0x601f, 0x0003, 0x00c6, 0x2560, 0x080c, 0x4ecf, 0x00ce, 0x080c,
+ 0x68e7, 0x0076, 0x2039, 0x0000, 0x080c, 0x681d, 0x2c08, 0x080c,
+ 0xa712, 0x007e, 0x2009, 0x004c, 0x080c, 0x80a7, 0xa085, 0x0001,
+ 0x012e, 0x005e, 0x00ce, 0x0005, 0xa006, 0x0cd0, 0x00f6, 0x00c6,
+ 0x0046, 0x00c6, 0x080c, 0x8022, 0x2c78, 0x00ce, 0x0180, 0x7e12,
+ 0x2c00, 0x781a, 0x781f, 0x0003, 0x2021, 0x0005, 0x080c, 0x9683,
+ 0x2f60, 0x2009, 0x004d, 0x080c, 0x80a7, 0xa085, 0x0001, 0x004e,
+ 0x00ce, 0x00fe, 0x0005, 0x00f6, 0x00c6, 0x0046, 0x00c6, 0x080c,
+ 0x8022, 0x2c78, 0x00ce, 0x0178, 0x7e12, 0x2c00, 0x781a, 0x781f,
+ 0x0003, 0x2021, 0x0005, 0x0439, 0x2f60, 0x2009, 0x004e, 0x080c,
+ 0x80a7, 0xa085, 0x0001, 0x004e, 0x00ce, 0x00fe, 0x0005, 0x00f6,
+ 0x00c6, 0x0046, 0x00c6, 0x080c, 0x8022, 0x2c78, 0x00ce, 0x0178,
+ 0x7e12, 0x2c00, 0x781a, 0x781f, 0x0003, 0x2021, 0x0004, 0x0059,
+ 0x2f60, 0x2009, 0x0052, 0x080c, 0x80a7, 0xa085, 0x0001, 0x004e,
+ 0x00ce, 0x00fe, 0x0005, 0x0096, 0x0076, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x4e71, 0x0118, 0x2001, 0x9688, 0x0028, 0x080c, 0x4e43,
+ 0x0158, 0x2001, 0x968e, 0x0006, 0xa00e, 0x2400, 0x080c, 0x51df,
+ 0x080c, 0x510c, 0x000e, 0x0807, 0x2418, 0x080c, 0x6b15, 0x62a0,
+ 0x0086, 0x2041, 0x0001, 0x2039, 0x0001, 0x2608, 0x080c, 0x6900,
+ 0x008e, 0x080c, 0x681d, 0x2f08, 0x2648, 0x080c, 0xa712, 0x613c,
+ 0x81ff, 0x090c, 0x69a9, 0x012e, 0x007e, 0x009e, 0x0005, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c, 0x8022, 0x001e, 0x0188,
+ 0x660a, 0x611a, 0x080c, 0x9956, 0x601f, 0x0001, 0x2d00, 0x6012,
+ 0x2009, 0x001f, 0x080c, 0x80a7, 0xa085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0xa006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x00c6,
+ 0x080c, 0x8022, 0x001e, 0x0188, 0x660a, 0x611a, 0x080c, 0x9956,
+ 0x601f, 0x0008, 0x2d00, 0x6012, 0x2009, 0x0021, 0x080c, 0x80a7,
+ 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006, 0x0cd8, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c, 0x8022, 0x001e, 0x0188,
+ 0x660a, 0x611a, 0x080c, 0x9956, 0x601f, 0x0001, 0x2d00, 0x6012,
+ 0x2009, 0x003d, 0x080c, 0x80a7, 0xa085, 0x0001, 0x012e, 0x00ce,
+ 0x0005, 0xa006, 0x0cd8, 0x00c6, 0x0126, 0x2091, 0x8000, 0x00c6,
+ 0x080c, 0x9807, 0x001e, 0x0180, 0x611a, 0x080c, 0x9956, 0x601f,
+ 0x0001, 0x2d00, 0x6012, 0x2009, 0x0000, 0x080c, 0x80a7, 0xa085,
+ 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006, 0x0cd8, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x00c6, 0x080c, 0x8022, 0x001e, 0x0188, 0x660a,
+ 0x611a, 0x080c, 0x9956, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009,
+ 0x0044, 0x080c, 0x80a7, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005,
+ 0xa006, 0x0cd8, 0x0026, 0x00d6, 0x6218, 0x2268, 0x6a3c, 0x82ff,
+ 0x0110, 0x8211, 0x6a3e, 0x00de, 0x002e, 0x0005, 0x0006, 0x6000,
+ 0xa086, 0x0000, 0x0190, 0x6013, 0x0000, 0x601f, 0x0007, 0x2001,
+ 0xafa3, 0x2004, 0x0006, 0xa082, 0x0051, 0x000e, 0x0208, 0x8004,
+ 0x6016, 0x080c, 0xabb4, 0x603f, 0x0000, 0x000e, 0x0005, 0x0066,
+ 0x00c6, 0x00d6, 0x2031, 0xad52, 0x2634, 0xd6e4, 0x0128, 0x6618,
+ 0x2660, 0x6e48, 0x080c, 0x4dfc, 0x00de, 0x00ce, 0x006e, 0x0005,
+ 0x0006, 0x0016, 0x6004, 0xa08e, 0x0002, 0x0140, 0xa08e, 0x0003,
+ 0x0128, 0xa08e, 0x0004, 0x0110, 0xa085, 0x0001, 0x001e, 0x000e,
+ 0x0005, 0x0006, 0x00d6, 0x6010, 0xa06d, 0x0148, 0x6834, 0xa086,
+ 0x0139, 0x0138, 0x6838, 0xd0fc, 0x0110, 0xa006, 0x0010, 0xa085,
+ 0x0001, 0x00de, 0x000e, 0x0005, 0x00c6, 0x0126, 0x2091, 0x8000,
+ 0x00c6, 0x080c, 0x8022, 0x001e, 0x0190, 0x611a, 0x080c, 0x9956,
+ 0x601f, 0x0001, 0x2d00, 0x6012, 0x080c, 0x2ad9, 0x2009, 0x0028,
+ 0x080c, 0x80a7, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006,
+ 0x0cd8, 0xa186, 0x0015, 0x1178, 0x2011, 0xad20, 0x2204, 0xa086,
+ 0x0074, 0x1148, 0x080c, 0x8943, 0x6003, 0x0001, 0x6007, 0x0029,
+ 0x080c, 0x67ee, 0x0020, 0x080c, 0x85f3, 0x080c, 0x8078, 0x0005,
+ 0xa186, 0x0016, 0x1128, 0x2001, 0x0004, 0x080c, 0x4c30, 0x00e8,
+ 0xa186, 0x0015, 0x11e8, 0x2011, 0xad20, 0x2204, 0xa086, 0x0014,
+ 0x11b8, 0x00d6, 0x6018, 0x2068, 0x080c, 0x4d72, 0x00de, 0x080c,
+ 0x89f7, 0x1170, 0x00d6, 0x6018, 0x2068, 0x6890, 0x00de, 0xa005,
+ 0x0138, 0x2001, 0x0006, 0x080c, 0x4c30, 0x080c, 0x81f6, 0x0020,
+ 0x080c, 0x85f3, 0x080c, 0x8078, 0x0005, 0x6848, 0xa086, 0x0005,
+ 0x1108, 0x0009, 0x0005, 0x6850, 0xc0ad, 0x6852, 0x0005, 0x00e6,
+ 0x0126, 0x2071, 0xad00, 0x2091, 0x8000, 0x7544, 0xa582, 0x0001,
+ 0x0608, 0x7048, 0x2060, 0x6000, 0xa086, 0x0000, 0x0148, 0xace0,
+ 0x0018, 0x7058, 0xac02, 0x1208, 0x0cb0, 0x2061, 0xb400, 0x0c98,
+ 0x6003, 0x0008, 0x8529, 0x7546, 0xaca8, 0x0018, 0x7058, 0xa502,
+ 0x1230, 0x754a, 0xa085, 0x0001, 0x012e, 0x00ee, 0x0005, 0x704b,
+ 0xb400, 0x0cc0, 0xa006, 0x0cc0, 0x00e6, 0x2071, 0xb28c, 0x7014,
+ 0xd0e4, 0x0150, 0x6013, 0x0000, 0x6003, 0x0001, 0x6007, 0x0050,
+ 0x080c, 0x67a8, 0x080c, 0x6c50, 0x00ee, 0x0005, 0x00c6, 0x00f6,
+ 0x2c78, 0x080c, 0x5029, 0x00fe, 0x0120, 0x601c, 0xa084, 0x000f,
+ 0x0013, 0x00ce, 0x0005, 0x9369, 0x985e, 0x9861, 0x9864, 0xa9a7,
+ 0xa9c2, 0xa9c5, 0x9369, 0x9369, 0x080c, 0x14f6, 0xe000, 0xe000,
+ 0x0005, 0xe000, 0xe000, 0x0005, 0x0009, 0x0005, 0x00f6, 0x2c78,
+ 0x080c, 0x5029, 0x0538, 0x080c, 0x8022, 0x1128, 0x2001, 0xafa5,
+ 0x2004, 0x783e, 0x00f8, 0x7818, 0x601a, 0x080c, 0x9956, 0x781c,
+ 0xa086, 0x0003, 0x0128, 0x7808, 0x6036, 0x2f00, 0x603a, 0x0020,
+ 0x7808, 0x603a, 0x2f00, 0x6036, 0x602a, 0x601f, 0x0001, 0x6007,
+ 0x0035, 0x6003, 0x0001, 0x7950, 0x6152, 0x080c, 0x67a8, 0x080c,
+ 0x6c50, 0x2f60, 0x00fe, 0x0005, 0x0016, 0x00f6, 0x682c, 0x6032,
+ 0xa08e, 0x0001, 0x0138, 0xa086, 0x0005, 0x0140, 0xa006, 0x602a,
+ 0x602e, 0x00a0, 0x6820, 0xc0f4, 0xc0d5, 0x6822, 0x6810, 0x2078,
+ 0x787c, 0x6938, 0xa102, 0x7880, 0x6934, 0xa103, 0x1e78, 0x6834,
+ 0x602a, 0x6838, 0xa084, 0xfffc, 0x683a, 0x602e, 0x2d00, 0x6036,
+ 0x6808, 0x603a, 0x6918, 0x611a, 0x6950, 0x6152, 0x601f, 0x0001,
+ 0x6007, 0x0039, 0x6003, 0x0001, 0x080c, 0x67a8, 0x6803, 0x0002,
+ 0x00fe, 0x001e, 0x0005, 0x00f6, 0x2c78, 0x080c, 0x5029, 0x1118,
+ 0xa085, 0x0001, 0x0070, 0x6020, 0xd0f4, 0x1150, 0xc0f5, 0x6022,
+ 0x6010, 0x2078, 0x7828, 0x603a, 0x782c, 0x6036, 0x080c, 0x190b,
+ 0xa006, 0x00fe, 0x0005, 0x0006, 0x0016, 0x6004, 0xa08e, 0x0034,
+ 0x01b8, 0xa08e, 0x0035, 0x01a0, 0xa08e, 0x0036, 0x0188, 0xa08e,
+ 0x0037, 0x0170, 0xa08e, 0x0038, 0x0158, 0xa08e, 0x0039, 0x0140,
+ 0xa08e, 0x003a, 0x0128, 0xa08e, 0x003b, 0x0110, 0xa085, 0x0001,
+ 0x001e, 0x000e, 0x0005, 0x0006, 0x0016, 0x0026, 0x0036, 0x00e6,
+ 0x2001, 0xaf9f, 0x200c, 0x8000, 0x2014, 0x2001, 0x0032, 0x080c,
+ 0x6665, 0x2001, 0xafa3, 0x82ff, 0x1110, 0x2011, 0x0002, 0x2202,
+ 0x2001, 0xafa1, 0x200c, 0x8000, 0x2014, 0x2071, 0xaf8d, 0x711a,
+ 0x721e, 0x2001, 0x0064, 0x080c, 0x6665, 0x2001, 0xafa4, 0x82ff,
+ 0x1110, 0x2011, 0x0002, 0x2202, 0x2009, 0xafa5, 0xa280, 0x000a,
+ 0x200a, 0x00ee, 0x003e, 0x002e, 0x001e, 0x000e, 0x0005, 0x0006,
+ 0x00e6, 0x2001, 0xafa3, 0x2003, 0x0028, 0x2001, 0xafa4, 0x2003,
+ 0x0014, 0x2071, 0xaf8d, 0x701b, 0x0000, 0x701f, 0x07d0, 0x2001,
+ 0xafa5, 0x2003, 0x001e, 0x00ee, 0x000e, 0x0005, 0x00d6, 0x6054,
+ 0xa06d, 0x0110, 0x080c, 0x15f0, 0x00de, 0x0005, 0x0005, 0x00c6,
+ 0x0126, 0x2091, 0x8000, 0x00c6, 0x080c, 0x8022, 0x001e, 0x0178,
+ 0x611a, 0x0ca1, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x0033,
+ 0x080c, 0x80a7, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006,
+ 0x0cd8, 0x00d6, 0x00e6, 0x00f6, 0x2071, 0xad00, 0xa186, 0x0015,
+ 0x1500, 0x7080, 0xa086, 0x0018, 0x11e0, 0x6010, 0x2068, 0x6a3c,
+ 0xd2e4, 0x1160, 0x2c78, 0x080c, 0x6e05, 0x01d8, 0x706c, 0x6a50,
+ 0xa206, 0x1160, 0x7070, 0x6a54, 0xa206, 0x1140, 0x6218, 0xa290,
+ 0x0028, 0x2214, 0x2009, 0x0000, 0x080c, 0x2b1e, 0x080c, 0x81f6,
+ 0x0020, 0x080c, 0x85f3, 0x080c, 0x8078, 0x00fe, 0x00ee, 0x00de,
+ 0x0005, 0x7050, 0x6a54, 0xa206, 0x0d48, 0x0c80, 0x00c6, 0x0126,
+ 0x2091, 0x8000, 0x00c6, 0x080c, 0x8022, 0x001e, 0x0180, 0x611a,
+ 0x080c, 0x9956, 0x601f, 0x0001, 0x2d00, 0x6012, 0x2009, 0x0043,
+ 0x080c, 0x80a7, 0xa085, 0x0001, 0x012e, 0x00ce, 0x0005, 0xa006,
+ 0x0cd8, 0x00d6, 0x00e6, 0x00f6, 0x2071, 0xad00, 0xa186, 0x0015,
+ 0x11c0, 0x7080, 0xa086, 0x0004, 0x11a0, 0x6010, 0xa0e8, 0x000f,
+ 0x2c78, 0x080c, 0x6e05, 0x01a8, 0x706c, 0x6a08, 0xa206, 0x1130,
+ 0x7070, 0x6a0c, 0xa206, 0x1110, 0x080c, 0x2ad9, 0x080c, 0x81f6,
+ 0x0020, 0x080c, 0x85f3, 0x080c, 0x8078, 0x00fe, 0x00ee, 0x00de,
+ 0x0005, 0x7050, 0x6a0c, 0xa206, 0x0d78, 0x0c80, 0x0016, 0x0026,
+ 0x684c, 0xd0ac, 0x0178, 0x6914, 0x6a10, 0x2100, 0xa205, 0x0150,
+ 0x6860, 0xa106, 0x1118, 0x685c, 0xa206, 0x0120, 0x6962, 0x6a5e,
+ 0xa085, 0x0001, 0x002e, 0x001e, 0x0005, 0x00d6, 0x0036, 0x6310,
+ 0x2368, 0x684a, 0x6952, 0xa29e, 0x4000, 0x1188, 0x00c6, 0x6318,
+ 0x2360, 0x2009, 0x0000, 0x080c, 0x4f6e, 0x1108, 0xc185, 0x6000,
+ 0xd0bc, 0x0108, 0xc18d, 0x6a66, 0x696a, 0x00ce, 0x0080, 0x6a66,
+ 0x3918, 0xa398, 0x0006, 0x231c, 0x686b, 0x0004, 0x6b72, 0x00c6,
+ 0x6318, 0x2360, 0x6004, 0xa084, 0x00ff, 0x686e, 0x00ce, 0x080c,
+ 0x510c, 0x003e, 0x00de, 0x0005, 0x00c6, 0x0026, 0x0016, 0xa186,
+ 0x0035, 0x0110, 0x6a34, 0x0008, 0x6a28, 0x080c, 0x9586, 0x01f0,
+ 0x2260, 0x611c, 0xa186, 0x0003, 0x0118, 0xa186, 0x0006, 0x1190,
+ 0x6834, 0xa206, 0x0140, 0x6838, 0xa206, 0x1160, 0x6108, 0x6834,
+ 0xa106, 0x1140, 0x0020, 0x6008, 0x6938, 0xa106, 0x1118, 0x6018,
+ 0x6918, 0xa106, 0x001e, 0x002e, 0x00ce, 0x0005, 0xa085, 0x0001,
+ 0x0cc8, 0x0066, 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x14f6, 0x0013,
+ 0x006e, 0x0005, 0x9a7a, 0x9eff, 0xa027, 0x9a7a, 0x9a7a, 0x9a7a,
+ 0x9a7a, 0x9a7a, 0x9ab2, 0xa0a3, 0x9a7a, 0x9a7a, 0x9a7a, 0x9a7a,
+ 0x9a7a, 0x9a7a, 0x080c, 0x14f6, 0x0066, 0x6000, 0xa0b2, 0x0010,
+ 0x1a0c, 0x14f6, 0x0013, 0x006e, 0x0005, 0x9a95, 0xa4fd, 0x9a95,
+ 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0x9a95, 0xa4c1, 0xa545, 0x9a95,
+ 0xaaea, 0xab1a, 0xaaea, 0xab1a, 0x9a95, 0x080c, 0x14f6, 0x0066,
+ 0x6000, 0xa0b2, 0x0010, 0x1a0c, 0x14f6, 0x0013, 0x006e, 0x0005,
+ 0x9ab0, 0xa1d8, 0xa295, 0xa2c2, 0xa346, 0x9ab0, 0xa433, 0xa3de,
+ 0xa0af, 0xa497, 0xa4ac, 0x9ab0, 0x9ab0, 0x9ab0, 0x9ab0, 0x9ab0,
+ 0x080c, 0x14f6, 0xa1b2, 0x0080, 0x1a0c, 0x14f6, 0x2100, 0xa1b2,
+ 0x0040, 0x1a04, 0x9e79, 0x0002, 0x9afc, 0x9cab, 0x9afc, 0x9afc,
+ 0x9afc, 0x9cb2, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc,
+ 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc,
+ 0x9afc, 0x9afc, 0x9afc, 0x9afe, 0x9b5a, 0x9b65, 0x9ba6, 0x9bc0,
+ 0x9c3e, 0x9c9c, 0x9afc, 0x9afc, 0x9cb5, 0x9afc, 0x9afc, 0x9cc4,
+ 0x9ccb, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9afc, 0x9d42, 0x9afc,
+ 0x9afc, 0x9d4d, 0x9afc, 0x9afc, 0x9d18, 0x9afc, 0x9afc, 0x9afc,
+ 0x9d61, 0x9afc, 0x9afc, 0x9afc, 0x9dd5, 0x9afc, 0x9afc, 0x9afc,
+ 0x9afc, 0x9afc, 0x9afc, 0x9e40, 0x080c, 0x14f6, 0x080c, 0x502d,
+ 0x1140, 0x2001, 0xad34, 0x2004, 0xa084, 0x0009, 0xa086, 0x0008,
+ 0x1140, 0x6007, 0x0009, 0x602b, 0x0009, 0x6013, 0x0000, 0x0804,
+ 0x9ca6, 0x080c, 0x501d, 0x00e6, 0x00c6, 0x0036, 0x0026, 0x0016,
+ 0x6218, 0x2270, 0x72a0, 0x0026, 0x2019, 0x0029, 0x080c, 0x68e7,
+ 0x0076, 0x2039, 0x0000, 0x080c, 0x681d, 0x2c08, 0x080c, 0xa712,
+ 0x007e, 0x001e, 0x2e60, 0x080c, 0x4ecf, 0x001e, 0x002e, 0x003e,
+ 0x00ce, 0x00ee, 0x6618, 0x00c6, 0x2660, 0x080c, 0x4ceb, 0x00ce,
+ 0xa6b0, 0x0001, 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x0278,
+ 0x080c, 0xa656, 0x1904, 0x9ba0, 0x080c, 0xa5f6, 0x1120, 0x6007,
+ 0x0008, 0x0804, 0x9ca6, 0x6007, 0x0009, 0x0804, 0x9ca6, 0x080c,
+ 0xa801, 0x0128, 0x080c, 0xa656, 0x0d78, 0x0804, 0x9ba0, 0x6013,
+ 0x1900, 0x0c88, 0x6106, 0x080c, 0xa5a6, 0x6007, 0x0006, 0x0804,
+ 0x9ca6, 0x6007, 0x0007, 0x0804, 0x9ca6, 0x080c, 0xab4e, 0x1904,
+ 0x9e76, 0x00d6, 0x6618, 0x2668, 0x6e04, 0xa6b4, 0xff00, 0x8637,
+ 0xa686, 0x0006, 0x0188, 0xa686, 0x0004, 0x0170, 0x6e04, 0xa6b4,
+ 0x00ff, 0xa686, 0x0006, 0x0140, 0xa686, 0x0004, 0x0128, 0xa686,
+ 0x0005, 0x0110, 0x00de, 0x00e0, 0x080c, 0xa6b4, 0x11a0, 0xa686,
+ 0x0006, 0x1150, 0x0026, 0x6218, 0xa290, 0x0028, 0x2214, 0x2009,
+ 0x0000, 0x080c, 0x2b1e, 0x002e, 0x080c, 0x4d72, 0x6007, 0x000a,
+ 0x00de, 0x0804, 0x9ca6, 0x6007, 0x000b, 0x00de, 0x0804, 0x9ca6,
+ 0x080c, 0x2ad9, 0x6007, 0x0001, 0x0804, 0x9ca6, 0x080c, 0xab4e,
+ 0x1904, 0x9e76, 0x6618, 0x00d6, 0x2668, 0x6e04, 0x00de, 0xa686,
+ 0x0707, 0x0d70, 0x0026, 0x6218, 0xa290, 0x0028, 0x2214, 0x2009,
+ 0x0000, 0x080c, 0x2b1e, 0x002e, 0x6007, 0x000c, 0x0804, 0x9ca6,
+ 0x080c, 0x502d, 0x1140, 0x2001, 0xad34, 0x2004, 0xa084, 0x0009,
+ 0xa086, 0x0008, 0x1110, 0x0804, 0x9b09, 0x080c, 0x501d, 0x6618,
+ 0xa6b0, 0x0001, 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x06e8,
+ 0x1138, 0x0026, 0x2001, 0x0006, 0x080c, 0x4c5d, 0x002e, 0x0050,
+ 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0004, 0x0120, 0xa686, 0x0006,
+ 0x1904, 0x9ba0, 0x080c, 0xa6c1, 0x1120, 0x6007, 0x000e, 0x0804,
+ 0x9ca6, 0x0046, 0x6418, 0xa4a0, 0x0028, 0x2424, 0xa4a4, 0x00ff,
+ 0x8427, 0x0046, 0x080c, 0x2ad9, 0x004e, 0x0016, 0xa006, 0x2009,
+ 0xad52, 0x210c, 0xd1a4, 0x0158, 0x2009, 0x0029, 0x080c, 0xa96c,
+ 0x6018, 0x00d6, 0x2068, 0x6800, 0xc0e5, 0x6802, 0x00de, 0x001e,
+ 0x004e, 0x6007, 0x0001, 0x0804, 0x9ca6, 0x2001, 0x0001, 0x080c,
+ 0x4c1e, 0x0156, 0x0016, 0x0026, 0x0036, 0x20a9, 0x0004, 0x2019,
+ 0xad05, 0x2011, 0xb290, 0x080c, 0x8a7c, 0x003e, 0x002e, 0x001e,
+ 0x015e, 0xa005, 0x0168, 0xa6b4, 0xff00, 0x8637, 0xa682, 0x0004,
+ 0x0a04, 0x9ba0, 0xa682, 0x0007, 0x0a04, 0x9bea, 0x0804, 0x9ba0,
+ 0x6013, 0x1900, 0x6007, 0x0009, 0x0804, 0x9ca6, 0x080c, 0x502d,
+ 0x1140, 0x2001, 0xad34, 0x2004, 0xa084, 0x0009, 0xa086, 0x0008,
+ 0x1110, 0x0804, 0x9b09, 0x080c, 0x501d, 0x6618, 0xa6b0, 0x0001,
+ 0x2634, 0xa684, 0x00ff, 0xa082, 0x0006, 0x06b0, 0xa6b4, 0xff00,
+ 0x8637, 0xa686, 0x0004, 0x0120, 0xa686, 0x0006, 0x1904, 0x9ba0,
+ 0x080c, 0xa6e9, 0x1130, 0x080c, 0xa5f6, 0x1118, 0x6007, 0x0010,
+ 0x04e8, 0x0046, 0x6418, 0xa4a0, 0x0028, 0x2424, 0xa4a4, 0x00ff,
+ 0x8427, 0x0046, 0x080c, 0x2ad9, 0x004e, 0x0016, 0xa006, 0x2009,
+ 0xad52, 0x210c, 0xd1a4, 0x0158, 0x2009, 0x0029, 0x080c, 0xa96c,
+ 0x6018, 0x00d6, 0x2068, 0x6800, 0xc0e5, 0x6802, 0x00de, 0x001e,
+ 0x004e, 0x6007, 0x0001, 0x00d0, 0x080c, 0xa801, 0x0140, 0xa6b4,
+ 0xff00, 0x8637, 0xa686, 0x0006, 0x0958, 0x0804, 0x9ba0, 0x6013,
+ 0x1900, 0x6007, 0x0009, 0x0050, 0x080c, 0xab4e, 0x1904, 0x9e76,
+ 0x080c, 0x9e98, 0x1904, 0x9ba0, 0x6007, 0x0012, 0x6003, 0x0001,
+ 0x080c, 0x67ee, 0x0005, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c,
+ 0x67ee, 0x0cc0, 0x6007, 0x0005, 0x0cc0, 0x080c, 0xab4e, 0x1904,
+ 0x9e76, 0x080c, 0x9e98, 0x1904, 0x9ba0, 0x6007, 0x0020, 0x6003,
+ 0x0001, 0x080c, 0x67ee, 0x0005, 0x6007, 0x0023, 0x6003, 0x0001,
+ 0x080c, 0x67ee, 0x0005, 0x080c, 0xab4e, 0x1904, 0x9e76, 0x080c,
+ 0x9e98, 0x1904, 0x9ba0, 0x0016, 0x0026, 0x2011, 0xb291, 0x2214,
+ 0xa286, 0xffff, 0x0190, 0x2c08, 0x080c, 0x9586, 0x01d8, 0x2260,
+ 0x2011, 0xb290, 0x2214, 0x6008, 0xa206, 0x11a0, 0x6018, 0xa190,
+ 0x0006, 0x2214, 0xa206, 0x01e0, 0x0068, 0x2011, 0xb290, 0x2214,
+ 0x2c08, 0x080c, 0xa940, 0x11a0, 0x2011, 0xb291, 0x2214, 0xa286,
+ 0xffff, 0x01a0, 0x2160, 0x6007, 0x0026, 0x6013, 0x1700, 0x2011,
+ 0xb289, 0x2214, 0xa296, 0xffff, 0x1160, 0x6007, 0x0025, 0x0048,
+ 0x601c, 0xa086, 0x0007, 0x1d70, 0x080c, 0x8078, 0x2160, 0x6007,
+ 0x0025, 0x6003, 0x0001, 0x080c, 0x67ee, 0x002e, 0x001e, 0x0005,
+ 0x2001, 0x0001, 0x080c, 0x4c1e, 0x0156, 0x0016, 0x0026, 0x0036,
+ 0x20a9, 0x0004, 0x2019, 0xad05, 0x2011, 0xb296, 0x080c, 0x8a7c,
+ 0x003e, 0x002e, 0x001e, 0x015e, 0x0120, 0x6007, 0x0031, 0x0804,
+ 0x9ca6, 0x080c, 0x87bd, 0x080c, 0x574f, 0x1158, 0x0006, 0x0026,
+ 0x0036, 0x080c, 0x576b, 0x0110, 0x080c, 0x5726, 0x003e, 0x002e,
+ 0x000e, 0x0005, 0x6106, 0x080c, 0x9eb4, 0x6007, 0x002b, 0x0804,
+ 0x9ca6, 0x6007, 0x002c, 0x0804, 0x9ca6, 0x080c, 0xab4e, 0x1904,
+ 0x9e76, 0x080c, 0x9e98, 0x1904, 0x9ba0, 0x6106, 0x080c, 0x9eb8,
+ 0x1120, 0x6007, 0x002e, 0x0804, 0x9ca6, 0x6007, 0x002f, 0x0804,
+ 0x9ca6, 0x00e6, 0x00d6, 0x00c6, 0x6018, 0xa080, 0x0001, 0x200c,
+ 0xa184, 0x00ff, 0xa086, 0x0006, 0x0158, 0xa184, 0xff00, 0x8007,
+ 0xa086, 0x0006, 0x0128, 0x00ce, 0x00de, 0x00ee, 0x0804, 0x9cab,
+ 0x2001, 0xad71, 0x2004, 0xd0e4, 0x0904, 0x9dd2, 0x2071, 0xb28c,
+ 0x7010, 0x6036, 0x7014, 0x603a, 0x7108, 0x720c, 0x2001, 0xad52,
+ 0x2004, 0xd0a4, 0x0140, 0x6018, 0x2068, 0x6810, 0xa106, 0x1118,
+ 0x6814, 0xa206, 0x01f8, 0x2001, 0xad52, 0x2004, 0xd0ac, 0x1580,
+ 0x2069, 0xad00, 0x6870, 0xa206, 0x1558, 0x686c, 0xa106, 0x1540,
+ 0x7210, 0x080c, 0x9586, 0x0548, 0x080c, 0xa9d4, 0x0530, 0x622a,
+ 0x6007, 0x0036, 0x6003, 0x0001, 0x080c, 0x67a8, 0x00ce, 0x00de,
+ 0x00ee, 0x0005, 0x7214, 0xa286, 0xffff, 0x0150, 0x080c, 0x9586,
+ 0x01a0, 0xa280, 0x0002, 0x2004, 0x7110, 0xa106, 0x1170, 0x0c08,
+ 0x7210, 0x2c08, 0x080c, 0xa940, 0x2c10, 0x2160, 0x0130, 0x08c8,
+ 0x6007, 0x0037, 0x6013, 0x1500, 0x08e8, 0x6007, 0x0037, 0x6013,
+ 0x1700, 0x08c0, 0x6007, 0x0012, 0x08a8, 0x6018, 0xa080, 0x0001,
+ 0x2004, 0xa084, 0xff00, 0x8007, 0xa086, 0x0006, 0x1904, 0x9cab,
+ 0x00e6, 0x00d6, 0x00c6, 0x2001, 0xad71, 0x2004, 0xd0e4, 0x0904,
+ 0x9e38, 0x2069, 0xad00, 0x2071, 0xb28c, 0x7008, 0x6036, 0x720c,
+ 0x623a, 0xa286, 0xffff, 0x1140, 0x7208, 0x00c6, 0x2c08, 0x080c,
+ 0xa940, 0x2c10, 0x00ce, 0x0588, 0x080c, 0x9586, 0x0570, 0x00c6,
+ 0x0026, 0x2260, 0x080c, 0x928f, 0x002e, 0x00ce, 0x7118, 0xa18c,
+ 0xff00, 0x810f, 0xa186, 0x0001, 0x0158, 0xa186, 0x0005, 0x0118,
+ 0xa186, 0x0007, 0x1178, 0xa280, 0x0004, 0x2004, 0xa005, 0x0150,
+ 0x0056, 0x7510, 0x7614, 0x080c, 0xa9eb, 0x005e, 0x00ce, 0x00de,
+ 0x00ee, 0x0005, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013, 0x2a00,
+ 0x6003, 0x0001, 0x080c, 0x67a8, 0x0c88, 0x6007, 0x003b, 0x602b,
+ 0x0009, 0x6013, 0x1700, 0x6003, 0x0001, 0x080c, 0x67a8, 0x0c30,
+ 0x6007, 0x003b, 0x602b, 0x000b, 0x6013, 0x0000, 0x0804, 0x9daa,
+ 0x00e6, 0x0026, 0x080c, 0x502d, 0x0558, 0x080c, 0x501d, 0x080c,
+ 0xabc5, 0x1520, 0x2071, 0xad00, 0x70d0, 0xc085, 0x70d2, 0x00f6,
+ 0x2079, 0x0100, 0x729c, 0xa284, 0x00ff, 0x706e, 0x78e6, 0xa284,
+ 0xff00, 0x7270, 0xa205, 0x7072, 0x78ea, 0x00fe, 0x70db, 0x0000,
+ 0x2001, 0xad52, 0x2004, 0xd0a4, 0x0120, 0x2011, 0xafe0, 0x2013,
+ 0x07d0, 0xd0ac, 0x1128, 0x080c, 0x28fa, 0x0010, 0x080c, 0xabf1,
+ 0x002e, 0x00ee, 0x080c, 0x8078, 0x0804, 0x9caa, 0x080c, 0x8078,
+ 0x0005, 0x2600, 0x0002, 0x9e81, 0x9e81, 0x9e81, 0x9e81, 0x9e81,
+ 0x9e83, 0x080c, 0x14f6, 0x080c, 0xab4e, 0x1d80, 0x0089, 0x1138,
+ 0x6007, 0x0045, 0x6003, 0x0001, 0x080c, 0x67ee, 0x0005, 0x080c,
+ 0x2ad9, 0x6007, 0x0001, 0x6003, 0x0001, 0x080c, 0x67ee, 0x0005,
+ 0x00d6, 0x0066, 0x6618, 0x2668, 0x6e04, 0xa6b4, 0xff00, 0x8637,
+ 0xa686, 0x0006, 0x0170, 0xa686, 0x0004, 0x0158, 0x6e04, 0xa6b4,
+ 0x00ff, 0xa686, 0x0006, 0x0128, 0xa686, 0x0004, 0x0110, 0xa085,
+ 0x0001, 0x006e, 0x00de, 0x0005, 0x00d6, 0x0449, 0x00de, 0x0005,
+ 0x00d6, 0x0491, 0x11f0, 0x680c, 0xa08c, 0xff00, 0x6820, 0xa084,
+ 0x00ff, 0xa115, 0x6212, 0x6824, 0x602a, 0xd1e4, 0x0118, 0x2009,
+ 0x0001, 0x0060, 0xd1ec, 0x0168, 0x6920, 0xa18c, 0x00ff, 0x6824,
+ 0x080c, 0x2676, 0x1130, 0x2110, 0x2009, 0x0000, 0x080c, 0x2b1e,
+ 0x0018, 0xa085, 0x0001, 0x0008, 0xa006, 0x00de, 0x0005, 0x2069,
+ 0xb28d, 0x6800, 0xa082, 0x0010, 0x1228, 0x6013, 0x0000, 0xa085,
+ 0x0001, 0x0008, 0xa006, 0x0005, 0x6013, 0x0000, 0x2069, 0xb28c,
+ 0x6808, 0xa084, 0xff00, 0xa086, 0x0800, 0x1140, 0x6800, 0xa084,
+ 0x00ff, 0xa08e, 0x0014, 0x0110, 0xa08e, 0x0010, 0x0005, 0x6004,
+ 0xa0b2, 0x0080, 0x1a0c, 0x14f6, 0xa1b6, 0x0013, 0x1130, 0x2008,
+ 0xa1b2, 0x0040, 0x1a04, 0x9ffb, 0x0092, 0xa1b6, 0x0027, 0x0120,
+ 0xa1b6, 0x0014, 0x190c, 0x14f6, 0x2001, 0x0007, 0x080c, 0x4c5d,
+ 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005, 0x9f5f,
+ 0x9f61, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f61, 0x9f6f, 0x9ff4, 0x9fbf,
+ 0x9ff4, 0x9fd0, 0x9ff4, 0x9f6f, 0x9ff4, 0x9fec, 0x9ff4, 0x9fec,
+ 0x9ff4, 0x9ff4, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f,
+ 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f61, 0x9f5f, 0x9ff4,
+ 0x9f5f, 0x9f5f, 0x9ff4, 0x9f5f, 0x9ff1, 0x9ff4, 0x9f5f, 0x9f5f,
+ 0x9f5f, 0x9f5f, 0x9ff4, 0x9ff4, 0x9f5f, 0x9ff4, 0x9ff4, 0x9f5f,
+ 0x9f69, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f, 0x9ff0, 0x9ff4, 0x9f5f,
+ 0x9f5f, 0x9ff4, 0x9ff4, 0x9f5f, 0x9f5f, 0x9f5f, 0x9f5f, 0x080c,
+ 0x14f6, 0x080c, 0x6b73, 0x6003, 0x0002, 0x080c, 0x6c50, 0x0804,
+ 0x9ffa, 0x2001, 0x0000, 0x080c, 0x4c1e, 0x0804, 0x9ff4, 0x00f6,
+ 0x2079, 0xad51, 0x7804, 0x00fe, 0xd0ac, 0x1904, 0x9ff4, 0x2001,
+ 0x0000, 0x080c, 0x4c1e, 0x6018, 0xa080, 0x0004, 0x2004, 0xa086,
+ 0x00ff, 0x1140, 0x00f6, 0x2079, 0xad00, 0x7894, 0x8000, 0x7896,
+ 0x00fe, 0x00e0, 0x00c6, 0x6018, 0x2060, 0x6000, 0xd0f4, 0x1140,
+ 0x6010, 0xa005, 0x0128, 0x00ce, 0x080c, 0x3cce, 0x0804, 0x9ff4,
+ 0x00ce, 0x2001, 0xad00, 0x2004, 0xa086, 0x0002, 0x1138, 0x00f6,
+ 0x2079, 0xad00, 0x7894, 0x8000, 0x7896, 0x00fe, 0x2001, 0x0002,
+ 0x080c, 0x4c30, 0x080c, 0x6b73, 0x601f, 0x0001, 0x6003, 0x0001,
+ 0x6007, 0x0002, 0x080c, 0x67ee, 0x080c, 0x6c50, 0x00c6, 0x6118,
+ 0x2160, 0x2009, 0x0001, 0x080c, 0x6519, 0x00ce, 0x04d8, 0x6618,
+ 0x00d6, 0x2668, 0x6e04, 0x00de, 0xa6b4, 0xff00, 0x8637, 0xa686,
+ 0x0006, 0x0550, 0xa686, 0x0004, 0x0538, 0x2001, 0x0004, 0x0410,
+ 0x2001, 0xad00, 0x2004, 0xa086, 0x0003, 0x1110, 0x080c, 0x3cce,
+ 0x2001, 0x0006, 0x0489, 0x6618, 0x00d6, 0x2668, 0x6e04, 0x00de,
+ 0xa6b4, 0xff00, 0x8637, 0xa686, 0x0006, 0x0170, 0x2001, 0x0006,
+ 0x0048, 0x2001, 0x0004, 0x0030, 0x2001, 0x0006, 0x00e9, 0x0020,
+ 0x0018, 0x0010, 0x080c, 0x4c5d, 0x080c, 0x6b73, 0x080c, 0x8078,
+ 0x080c, 0x6c50, 0x0005, 0x2600, 0x0002, 0xa003, 0xa003, 0xa003,
+ 0xa003, 0xa003, 0xa005, 0x080c, 0x14f6, 0x080c, 0x6b73, 0x080c,
+ 0x8078, 0x080c, 0x6c50, 0x0005, 0x0016, 0x00d6, 0x6118, 0x2168,
+ 0x6900, 0xd184, 0x0188, 0x6104, 0xa18e, 0x000a, 0x1128, 0x699c,
+ 0xd1a4, 0x1110, 0x2001, 0x0007, 0x080c, 0x4c30, 0x2001, 0x0000,
+ 0x080c, 0x4c1e, 0x080c, 0x2aff, 0x00de, 0x001e, 0x0005, 0x00d6,
+ 0x6618, 0x2668, 0x6804, 0xa084, 0xff00, 0x8007, 0x00de, 0xa0b2,
+ 0x000c, 0x1a0c, 0x14f6, 0xa1b6, 0x0015, 0x1110, 0x003b, 0x0028,
+ 0xa1b6, 0x0016, 0x190c, 0x14f6, 0x006b, 0x0005, 0x86b9, 0x86b9,
+ 0x86b9, 0x86b9, 0x86b9, 0x86b9, 0xa08f, 0xa056, 0x86b9, 0x86b9,
+ 0x86b9, 0x86b9, 0x86b9, 0x86b9, 0x86b9, 0x86b9, 0x86b9, 0x86b9,
+ 0xa08f, 0xa096, 0x86b9, 0x86b9, 0x86b9, 0x86b9, 0x00f6, 0x2079,
+ 0xad51, 0x7804, 0xd0ac, 0x11e0, 0x6018, 0xa07d, 0x01c8, 0x7800,
+ 0xd0f4, 0x1118, 0x7810, 0xa005, 0x1198, 0x2001, 0x0000, 0x080c,
+ 0x4c1e, 0x2001, 0x0002, 0x080c, 0x4c30, 0x601f, 0x0001, 0x6003,
+ 0x0001, 0x6007, 0x0002, 0x080c, 0x67ee, 0x080c, 0x6c50, 0x00a8,
+ 0x2011, 0xb283, 0x2204, 0x8211, 0x220c, 0x080c, 0x2676, 0x1168,
+ 0x00c6, 0x080c, 0x4cdc, 0x0120, 0x00ce, 0x080c, 0x8078, 0x0028,
+ 0x080c, 0x493a, 0x00ce, 0x080c, 0x8078, 0x00fe, 0x0005, 0x6604,
+ 0xa6b6, 0x001e, 0x1110, 0x080c, 0x8078, 0x0005, 0x080c, 0x8940,
+ 0x1138, 0x6003, 0x0001, 0x6007, 0x0001, 0x080c, 0x67ee, 0x0010,
+ 0x080c, 0x8078, 0x0005, 0x6004, 0xa08a, 0x0080, 0x1a0c, 0x14f6,
+ 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005, 0xa182,
+ 0x0040, 0x0002, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c7, 0xa0c5,
+ 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5,
+ 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0xa0c5, 0x080c, 0x14f6, 0x00d6,
+ 0x00e6, 0x00f6, 0x0156, 0x0046, 0x0026, 0x6218, 0xa280, 0x002b,
+ 0x2004, 0xa005, 0x0120, 0x2021, 0x0000, 0x080c, 0xab96, 0x6106,
+ 0x2071, 0xb280, 0x7444, 0xa4a4, 0xff00, 0x0904, 0xa129, 0xa486,
+ 0x2000, 0x1130, 0x2009, 0x0001, 0x2011, 0x0200, 0x080c, 0x663f,
+ 0x080c, 0x15d9, 0x090c, 0x14f6, 0x6003, 0x0007, 0x2d00, 0x6837,
+ 0x010d, 0x6803, 0x0000, 0x683b, 0x0000, 0x6c5a, 0x2c00, 0x685e,
+ 0x6008, 0x68b2, 0x6018, 0x2078, 0x78a0, 0x8007, 0x7130, 0x694a,
+ 0x0016, 0xa084, 0xff00, 0x6846, 0x684f, 0x0000, 0x6857, 0x0036,
+ 0x080c, 0x510c, 0x001e, 0xa486, 0x2000, 0x1130, 0x2019, 0x0017,
+ 0x080c, 0xa8eb, 0x0804, 0xa186, 0xa486, 0x0400, 0x1130, 0x2019,
+ 0x0002, 0x080c, 0xa89d, 0x0804, 0xa186, 0xa486, 0x0200, 0x1110,
+ 0x080c, 0xa882, 0xa486, 0x1000, 0x1110, 0x080c, 0xa8d0, 0x0804,
+ 0xa186, 0x2069, 0xb048, 0x6a00, 0xd284, 0x0904, 0xa1d5, 0xa284,
+ 0x0300, 0x1904, 0xa1cf, 0x6804, 0xa005, 0x0904, 0xa1c0, 0x2d78,
+ 0x6003, 0x0007, 0x080c, 0x15c0, 0x0904, 0xa18d, 0x7800, 0xd08c,
+ 0x1118, 0x7804, 0x8001, 0x7806, 0x6013, 0x0000, 0x6803, 0x0000,
+ 0x6837, 0x0116, 0x683b, 0x0000, 0x6008, 0x68b2, 0x2c00, 0x684a,
+ 0x6018, 0x2078, 0x78a0, 0x8007, 0x7130, 0x6986, 0x6846, 0x7928,
+ 0x698a, 0x792c, 0x698e, 0x7930, 0x6992, 0x7934, 0x6996, 0x6853,
+ 0x003d, 0x7244, 0xa294, 0x0003, 0xa286, 0x0002, 0x1118, 0x684f,
+ 0x0040, 0x0040, 0xa286, 0x0001, 0x1118, 0x684f, 0x0080, 0x0010,
+ 0x684f, 0x0000, 0x20a9, 0x000a, 0x2001, 0xb290, 0xad90, 0x0015,
+ 0x200c, 0x810f, 0x2112, 0x8000, 0x8210, 0x1f04, 0xa178, 0x200c,
+ 0x6982, 0x8000, 0x200c, 0x697e, 0x080c, 0x510c, 0x002e, 0x004e,
+ 0x015e, 0x00fe, 0x00ee, 0x00de, 0x0005, 0x6013, 0x0100, 0x6003,
+ 0x0001, 0x6007, 0x0041, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0c70,
+ 0x2069, 0xb292, 0x2d04, 0xa084, 0xff00, 0xa086, 0x1200, 0x11a8,
+ 0x2069, 0xb280, 0x686c, 0xa084, 0x00ff, 0x0016, 0x6110, 0xa18c,
+ 0x0700, 0xa10d, 0x6112, 0x001e, 0x6003, 0x0001, 0x6007, 0x0043,
+ 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0888, 0x6013, 0x0200, 0x6003,
+ 0x0001, 0x6007, 0x0041, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0830,
+ 0x6013, 0x0300, 0x0010, 0x6013, 0x0100, 0x6003, 0x0001, 0x6007,
+ 0x0041, 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0804, 0xa186, 0x6013,
+ 0x0500, 0x0c98, 0x6013, 0x0600, 0x0818, 0x6013, 0x0200, 0x0800,
+ 0xa186, 0x0013, 0x1170, 0x6004, 0xa08a, 0x0040, 0x0a0c, 0x14f6,
+ 0xa08a, 0x0053, 0x1a0c, 0x14f6, 0xa082, 0x0040, 0x2008, 0x0804,
+ 0xa252, 0xa186, 0x0051, 0x0138, 0xa186, 0x0047, 0x11d8, 0x6004,
+ 0xa086, 0x0041, 0x0518, 0x2001, 0x0109, 0x2004, 0xd084, 0x01f0,
+ 0x0126, 0x2091, 0x2800, 0x0006, 0x0016, 0x0026, 0x080c, 0x6699,
+ 0x002e, 0x001e, 0x000e, 0x012e, 0x6000, 0xa086, 0x0002, 0x1170,
+ 0x0804, 0xa295, 0xa186, 0x0027, 0x0120, 0xa186, 0x0014, 0x190c,
+ 0x14f6, 0x6004, 0xa082, 0x0040, 0x2008, 0x001a, 0x080c, 0x80be,
+ 0x0005, 0xa22c, 0xa22e, 0xa22e, 0xa22c, 0xa22c, 0xa22c, 0xa22c,
+ 0xa22c, 0xa22c, 0xa22c, 0xa22c, 0xa22c, 0xa22c, 0xa22c, 0xa22c,
+ 0xa22c, 0xa22c, 0xa22c, 0xa22c, 0x080c, 0x14f6, 0x080c, 0x6b73,
+ 0x080c, 0x6c50, 0x0036, 0x00d6, 0x6010, 0xa06d, 0x01c0, 0xad84,
+ 0xf000, 0x01a8, 0x6003, 0x0002, 0x6018, 0x2004, 0xd0bc, 0x1178,
+ 0x2019, 0x0004, 0x080c, 0xa91f, 0x6013, 0x0000, 0x6014, 0xa005,
+ 0x1120, 0x2001, 0xafa4, 0x2004, 0x6016, 0x6003, 0x0007, 0x00de,
+ 0x003e, 0x0005, 0x0002, 0xa266, 0xa283, 0xa26f, 0xa28f, 0xa266,
+ 0xa266, 0xa266, 0xa266, 0xa266, 0xa266, 0xa266, 0xa266, 0xa266,
+ 0xa266, 0xa266, 0xa266, 0xa266, 0xa266, 0xa266, 0x080c, 0x14f6,
+ 0x6010, 0xa088, 0x0013, 0x2104, 0xa085, 0x0400, 0x200a, 0x080c,
+ 0x6b73, 0x6010, 0xa080, 0x0013, 0x2004, 0xd0b4, 0x0138, 0x6003,
+ 0x0007, 0x2009, 0x0043, 0x080c, 0x80a7, 0x0010, 0x6003, 0x0002,
+ 0x080c, 0x6c50, 0x0005, 0x080c, 0x6b73, 0x080c, 0xab55, 0x1120,
+ 0x080c, 0x6618, 0x080c, 0x8078, 0x080c, 0x6c50, 0x0005, 0x080c,
+ 0x6b73, 0x2009, 0x0041, 0x0804, 0xa3de, 0xa182, 0x0040, 0x0002,
+ 0xa2ab, 0xa2ad, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ae,
+ 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab, 0xa2ab,
+ 0xa2ab, 0xa2b9, 0xa2ab, 0x080c, 0x14f6, 0x0005, 0x6003, 0x0004,
+ 0x6110, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10, 0x080c, 0x1824,
+ 0x0005, 0x00d6, 0x080c, 0x6618, 0x00de, 0x080c, 0xabb4, 0x080c,
+ 0x8078, 0x0005, 0xa182, 0x0040, 0x0002, 0xa2d8, 0xa2d8, 0xa2d8,
+ 0xa2d8, 0xa2d8, 0xa2d8, 0xa2d8, 0xa2da, 0xa2d8, 0xa2dd, 0xa316,
+ 0xa2d8, 0xa2d8, 0xa2d8, 0xa2d8, 0xa316, 0xa2d8, 0xa2d8, 0xa2d8,
+ 0x080c, 0x14f6, 0x080c, 0x80be, 0x0005, 0x2001, 0xad71, 0x2004,
+ 0xd0e4, 0x0158, 0x2001, 0x0100, 0x2004, 0xa082, 0x0005, 0x0228,
+ 0x2001, 0x011f, 0x2004, 0x6036, 0x0010, 0x6037, 0x0000, 0x080c,
+ 0x6c05, 0x080c, 0x6d0d, 0x6010, 0x00d6, 0x2068, 0x684c, 0xd0fc,
+ 0x0150, 0xa08c, 0x0003, 0xa18e, 0x0002, 0x0168, 0x2009, 0x0041,
+ 0x00de, 0x0804, 0xa3de, 0x6003, 0x0007, 0x6017, 0x0000, 0x080c,
+ 0x6618, 0x00de, 0x0005, 0x080c, 0xab55, 0x0110, 0x00de, 0x0005,
+ 0x080c, 0x6618, 0x080c, 0x8078, 0x00de, 0x0ca0, 0x0036, 0x080c,
+ 0x6c05, 0x080c, 0x6d0d, 0x6010, 0x00d6, 0x2068, 0x6018, 0x2004,
+ 0xd0bc, 0x0188, 0x684c, 0xa084, 0x0003, 0xa086, 0x0002, 0x0140,
+ 0x687c, 0x632c, 0xa31a, 0x632e, 0x6880, 0x6328, 0xa31b, 0x632a,
+ 0x6003, 0x0002, 0x0080, 0x2019, 0x0004, 0x080c, 0xa91f, 0x6014,
+ 0xa005, 0x1128, 0x2001, 0xafa4, 0x2004, 0x8003, 0x6016, 0x6013,
+ 0x0000, 0x6003, 0x0007, 0x00de, 0x003e, 0x0005, 0xa186, 0x0013,
+ 0x1150, 0x6004, 0xa086, 0x0042, 0x190c, 0x14f6, 0x080c, 0x6b73,
+ 0x080c, 0x6c50, 0x0005, 0xa186, 0x0027, 0x0118, 0xa186, 0x0014,
+ 0x1180, 0x6004, 0xa086, 0x0042, 0x190c, 0x14f6, 0x2001, 0x0007,
+ 0x080c, 0x4c5d, 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50,
+ 0x0005, 0xa182, 0x0040, 0x0002, 0xa37f, 0xa37f, 0xa37f, 0xa37f,
+ 0xa37f, 0xa37f, 0xa37f, 0xa381, 0xa38d, 0xa37f, 0xa37f, 0xa37f,
+ 0xa37f, 0xa37f, 0xa37f, 0xa37f, 0xa37f, 0xa37f, 0xa37f, 0x080c,
+ 0x14f6, 0x0036, 0x0046, 0x20e1, 0x0005, 0x3d18, 0x3e20, 0x2c10,
+ 0x080c, 0x1824, 0x004e, 0x003e, 0x0005, 0x6010, 0x00d6, 0x2068,
+ 0x6810, 0x6a14, 0x0006, 0x0046, 0x0056, 0x6c7c, 0xa422, 0x6d80,
+ 0x2200, 0xa52b, 0x602c, 0xa420, 0x642e, 0x6028, 0xa529, 0x652a,
+ 0x005e, 0x004e, 0x000e, 0xa20d, 0x1178, 0x684c, 0xd0fc, 0x0120,
+ 0x2009, 0x0041, 0x00de, 0x0490, 0x6003, 0x0007, 0x6017, 0x0000,
+ 0x080c, 0x6618, 0x00de, 0x0005, 0x0006, 0x00f6, 0x2c78, 0x080c,
+ 0x5029, 0x00fe, 0x000e, 0x0120, 0x6003, 0x0002, 0x00de, 0x0005,
+ 0x2009, 0xad0d, 0x210c, 0xd19c, 0x0118, 0x6003, 0x0007, 0x0010,
+ 0x6003, 0x0006, 0x0021, 0x080c, 0x661a, 0x00de, 0x0005, 0xd2fc,
+ 0x0140, 0x8002, 0x8000, 0x8212, 0xa291, 0x0000, 0x2009, 0x0009,
+ 0x0010, 0x2009, 0x0015, 0x6a6a, 0x6866, 0x0005, 0xa182, 0x0040,
+ 0x0208, 0x0062, 0xa186, 0x0013, 0x0120, 0xa186, 0x0014, 0x190c,
+ 0x14f6, 0x6020, 0xd0dc, 0x090c, 0x14f6, 0x0005, 0xa401, 0xa408,
+ 0xa414, 0xa420, 0xa401, 0xa401, 0xa401, 0xa42f, 0xa401, 0xa403,
+ 0xa403, 0xa401, 0xa401, 0xa401, 0xa401, 0xa403, 0xa401, 0xa403,
+ 0xa401, 0x080c, 0x14f6, 0x6020, 0xd0dc, 0x090c, 0x14f6, 0x0005,
+ 0x6003, 0x0001, 0x6106, 0x080c, 0x67a8, 0x0126, 0x2091, 0x8000,
+ 0x080c, 0x6c50, 0x012e, 0x0005, 0x6003, 0x0001, 0x6106, 0x080c,
+ 0x67a8, 0x0126, 0x2091, 0x8000, 0x080c, 0x6c50, 0x012e, 0x0005,
+ 0x6003, 0x0003, 0x6106, 0x2c10, 0x080c, 0x1e6e, 0x0126, 0x2091,
+ 0x8000, 0x080c, 0x680b, 0x080c, 0x6d0d, 0x012e, 0x0005, 0xa016,
+ 0x080c, 0x1824, 0x0005, 0x0126, 0x2091, 0x8000, 0x0036, 0x00d6,
+ 0xa182, 0x0040, 0x0023, 0x00de, 0x003e, 0x012e, 0x0005, 0xa44f,
+ 0xa451, 0xa463, 0xa47e, 0xa44f, 0xa44f, 0xa44f, 0xa493, 0xa44f,
+ 0xa44f, 0xa44f, 0xa44f, 0xa44f, 0xa44f, 0xa44f, 0xa44f, 0x080c,
+ 0x14f6, 0x6010, 0x2068, 0x684c, 0xd0fc, 0x01f8, 0xa09c, 0x0003,
+ 0xa39e, 0x0003, 0x01d0, 0x6003, 0x0001, 0x6106, 0x080c, 0x67a8,
+ 0x080c, 0x6c50, 0x0498, 0x6010, 0x2068, 0x684c, 0xd0fc, 0x0168,
+ 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0140, 0x6003, 0x0001, 0x6106,
+ 0x080c, 0x67a8, 0x080c, 0x6c50, 0x0408, 0x6013, 0x0000, 0x6017,
+ 0x0000, 0x2019, 0x0004, 0x080c, 0xa91f, 0x00c0, 0x6010, 0x2068,
+ 0x684c, 0xd0fc, 0x0d90, 0xa09c, 0x0003, 0xa39e, 0x0003, 0x0d68,
+ 0x6003, 0x0003, 0x6106, 0x2c10, 0x080c, 0x1e6e, 0x080c, 0x680b,
+ 0x080c, 0x6d0d, 0x0018, 0xa016, 0x080c, 0x1824, 0x0005, 0x080c,
+ 0x6b73, 0x6110, 0x81ff, 0x0158, 0x00d6, 0x2168, 0x080c, 0xabfa,
+ 0x0036, 0x2019, 0x0029, 0x080c, 0xa91f, 0x003e, 0x00de, 0x080c,
+ 0x974e, 0x080c, 0x6c50, 0x0005, 0x080c, 0x6c05, 0x6110, 0x81ff,
+ 0x0158, 0x00d6, 0x2168, 0x080c, 0xabfa, 0x0036, 0x2019, 0x0029,
+ 0x080c, 0xa91f, 0x003e, 0x00de, 0x080c, 0x974e, 0x080c, 0x6d0d,
+ 0x0005, 0xa182, 0x0085, 0x0002, 0xa4cd, 0xa4cb, 0xa4cb, 0xa4d9,
+ 0xa4cb, 0xa4cb, 0xa4cb, 0x080c, 0x14f6, 0x6003, 0x000b, 0x6106,
+ 0x080c, 0x67a8, 0x0126, 0x2091, 0x8000, 0x080c, 0x6c50, 0x012e,
+ 0x0005, 0x0026, 0x00e6, 0x080c, 0xab4e, 0x0118, 0x080c, 0x8078,
+ 0x00c8, 0x2071, 0xb280, 0x7224, 0x6212, 0x7220, 0x080c, 0xa7ce,
+ 0x0118, 0x6007, 0x0086, 0x0040, 0x6007, 0x0087, 0x7224, 0xa296,
+ 0xffff, 0x1110, 0x6007, 0x0086, 0x6003, 0x0001, 0x080c, 0x67a8,
+ 0x080c, 0x6c50, 0x00ee, 0x002e, 0x0005, 0xa186, 0x0013, 0x1160,
+ 0x6004, 0xa08a, 0x0085, 0x0a0c, 0x14f6, 0xa08a, 0x008c, 0x1a0c,
+ 0x14f6, 0xa082, 0x0085, 0x00a2, 0xa186, 0x0027, 0x0130, 0xa186,
+ 0x0014, 0x0118, 0x080c, 0x80be, 0x0050, 0x2001, 0x0007, 0x080c,
+ 0x4c5d, 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005,
+ 0xa527, 0xa529, 0xa529, 0xa527, 0xa527, 0xa527, 0xa527, 0x080c,
+ 0x14f6, 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005,
+ 0xa182, 0x0085, 0x0a0c, 0x14f6, 0xa182, 0x008c, 0x1a0c, 0x14f6,
+ 0xa182, 0x0085, 0x0002, 0xa542, 0xa542, 0xa542, 0xa544, 0xa542,
+ 0xa542, 0xa542, 0x080c, 0x14f6, 0x0005, 0xa186, 0x0013, 0x0148,
+ 0xa186, 0x0014, 0x0130, 0xa186, 0x0027, 0x0118, 0x080c, 0x80be,
+ 0x0030, 0x080c, 0x6b73, 0x080c, 0x974e, 0x080c, 0x6c50, 0x0005,
+ 0x0036, 0x080c, 0xabb4, 0x603f, 0x0000, 0x2019, 0x000b, 0x0031,
+ 0x601f, 0x0006, 0x6003, 0x0007, 0x003e, 0x0005, 0x0126, 0x0036,
+ 0x2091, 0x8000, 0x0086, 0x2c40, 0x0096, 0x2049, 0x0000, 0x080c,
+ 0x7b9a, 0x009e, 0x008e, 0x1578, 0x0076, 0x2c38, 0x080c, 0x7c34,
+ 0x007e, 0x1548, 0x6000, 0xa086, 0x0000, 0x0528, 0x601c, 0xa086,
+ 0x0007, 0x0508, 0x00d6, 0x6000, 0xa086, 0x0004, 0x1150, 0x080c,
+ 0xabb4, 0x601f, 0x0007, 0x2001, 0xafa3, 0x2004, 0x6016, 0x080c,
+ 0x190b, 0x6010, 0x2068, 0x080c, 0x9596, 0x0110, 0x080c, 0xa91f,
+ 0x00de, 0x6013, 0x0000, 0x080c, 0xabb4, 0x601f, 0x0007, 0x2001,
+ 0xafa3, 0x2004, 0x6016, 0x003e, 0x012e, 0x0005, 0x00f6, 0x00c6,
+ 0x0036, 0x0156, 0x2079, 0xb280, 0x7938, 0x783c, 0x080c, 0x2676,
+ 0x1904, 0xa5f1, 0x0016, 0x00c6, 0x080c, 0x4cdc, 0x15c0, 0x2011,
+ 0xb290, 0xac98, 0x000a, 0x20a9, 0x0004, 0x080c, 0x8a7c, 0x1578,
+ 0x001e, 0x002e, 0x0026, 0x0016, 0x2019, 0x0029, 0x080c, 0x7cf4,
+ 0x080c, 0x68e7, 0x0076, 0x2039, 0x0000, 0x080c, 0x681d, 0x007e,
+ 0x001e, 0x0076, 0x2039, 0x0000, 0x080c, 0xa712, 0x007e, 0x080c,
+ 0x4ecf, 0x0026, 0x6204, 0xa294, 0xff00, 0x8217, 0xa286, 0x0006,
+ 0x0118, 0xa286, 0x0004, 0x1118, 0x62a0, 0x080c, 0x2b87, 0x002e,
+ 0x001e, 0x080c, 0x493a, 0x6612, 0x6516, 0xa006, 0x0010, 0x00ce,
+ 0x001e, 0x015e, 0x003e, 0x00ce, 0x00fe, 0x0005, 0x00c6, 0x00d6,
+ 0x00e6, 0x0016, 0x2009, 0xad20, 0x2104, 0xa086, 0x0074, 0x1904,
+ 0xa64b, 0x2069, 0xb28e, 0x690c, 0xa182, 0x0100, 0x06c0, 0x6908,
+ 0xa184, 0x8000, 0x05e8, 0x2001, 0xaf9d, 0x2004, 0xa005, 0x1160,
+ 0x6018, 0x2070, 0x7010, 0xa084, 0x00ff, 0x0118, 0x7000, 0xd0f4,
+ 0x0118, 0xa184, 0x0800, 0x0560, 0x6910, 0xa18a, 0x0001, 0x0610,
+ 0x6914, 0x2069, 0xb2ae, 0x6904, 0x81ff, 0x1198, 0x690c, 0xa182,
+ 0x0100, 0x02a8, 0x6908, 0x81ff, 0x1178, 0x6910, 0xa18a, 0x0001,
+ 0x0288, 0x6918, 0xa18a, 0x0001, 0x0298, 0x00d0, 0x6013, 0x0100,
+ 0x00a0, 0x6013, 0x0300, 0x0088, 0x6013, 0x0500, 0x0070, 0x6013,
+ 0x0700, 0x0058, 0x6013, 0x0900, 0x0040, 0x6013, 0x0b00, 0x0028,
+ 0x6013, 0x0f00, 0x0010, 0x6013, 0x2d00, 0xa085, 0x0001, 0x0008,
+ 0xa006, 0x001e, 0x00ee, 0x00de, 0x00ce, 0x0005, 0x00c6, 0x00d6,
+ 0x0026, 0x0036, 0x0156, 0x6218, 0x2268, 0x6b04, 0xa394, 0x00ff,
+ 0xa286, 0x0006, 0x0190, 0xa286, 0x0004, 0x0178, 0xa394, 0xff00,
+ 0x8217, 0xa286, 0x0006, 0x0148, 0xa286, 0x0004, 0x0130, 0x00c6,
+ 0x2d60, 0x080c, 0x4ceb, 0x00ce, 0x04c0, 0x2011, 0xb296, 0xad98,
+ 0x000a, 0x20a9, 0x0004, 0x080c, 0x8a7c, 0x1580, 0x2011, 0xb29a,
+ 0xad98, 0x0006, 0x20a9, 0x0004, 0x080c, 0x8a7c, 0x1538, 0x0046,
+ 0x0016, 0x6aa0, 0xa294, 0x00ff, 0x8227, 0xa006, 0x2009, 0xad52,
+ 0x210c, 0xd1a4, 0x0138, 0x2009, 0x0029, 0x080c, 0xa96c, 0x6800,
+ 0xc0e5, 0x6802, 0x2019, 0x0029, 0x080c, 0x68e7, 0x0076, 0x2039,
+ 0x0000, 0x080c, 0x681d, 0x2c08, 0x080c, 0xa712, 0x007e, 0x2001,
+ 0x0007, 0x080c, 0x4c5d, 0x001e, 0x004e, 0xa006, 0x015e, 0x003e,
+ 0x002e, 0x00de, 0x00ce, 0x0005, 0x00d6, 0x2069, 0xb28e, 0x6800,
+ 0xa086, 0x0800, 0x0118, 0x6013, 0x0000, 0x0008, 0xa006, 0x00de,
+ 0x0005, 0x00c6, 0x00f6, 0x0016, 0x0026, 0x0036, 0x0156, 0x2079,
+ 0xb28c, 0x7930, 0x7834, 0x080c, 0x2676, 0x11a0, 0x080c, 0x4cdc,
+ 0x1188, 0x2011, 0xb290, 0xac98, 0x000a, 0x20a9, 0x0004, 0x080c,
+ 0x8a7c, 0x1140, 0x2011, 0xb294, 0xac98, 0x0006, 0x20a9, 0x0004,
+ 0x080c, 0x8a7c, 0x015e, 0x003e, 0x002e, 0x001e, 0x00fe, 0x00ce,
+ 0x0005, 0x00c6, 0x0006, 0x0016, 0x0026, 0x0036, 0x0156, 0x2011,
+ 0xb283, 0x2204, 0x8211, 0x220c, 0x080c, 0x2676, 0x11a0, 0x080c,
+ 0x4cdc, 0x1188, 0x2011, 0xb296, 0xac98, 0x000a, 0x20a9, 0x0004,
+ 0x080c, 0x8a7c, 0x1140, 0x2011, 0xb29a, 0xac98, 0x0006, 0x20a9,
+ 0x0004, 0x080c, 0x8a7c, 0x015e, 0x003e, 0x002e, 0x001e, 0x000e,
+ 0x00ce, 0x0005, 0x00e6, 0x00c6, 0x0086, 0x0076, 0x0066, 0x0056,
+ 0x0046, 0x0026, 0x0126, 0x2091, 0x8000, 0x2740, 0x2029, 0xafd0,
+ 0x252c, 0x2021, 0xafd6, 0x2424, 0x2061, 0xb400, 0x2071, 0xad00,
+ 0x7644, 0x7064, 0x81ff, 0x0128, 0x8001, 0xa602, 0x1a04, 0xa78e,
+ 0x0018, 0xa606, 0x0904, 0xa78e, 0x2100, 0xac06, 0x0904, 0xa785,
+ 0x080c, 0xa990, 0x0904, 0xa785, 0x671c, 0xa786, 0x0001, 0x0904,
+ 0xa7a5, 0xa786, 0x0004, 0x0904, 0xa7a5, 0xa786, 0x0007, 0x05e8,
+ 0x2500, 0xac06, 0x05d0, 0x2400, 0xac06, 0x05b8, 0x080c, 0xa9a0,
+ 0x15a0, 0x88ff, 0x0118, 0x6050, 0xa906, 0x1578, 0x00d6, 0x6000,
+ 0xa086, 0x0004, 0x1120, 0x0016, 0x080c, 0x190b, 0x001e, 0xa786,
+ 0x0008, 0x1148, 0x080c, 0x9789, 0x1130, 0x080c, 0x85f3, 0x00de,
+ 0x080c, 0x974e, 0x00d0, 0x6010, 0x2068, 0x080c, 0x9596, 0x0190,
+ 0xa786, 0x0003, 0x1528, 0x6837, 0x0103, 0x6b4a, 0x6847, 0x0000,
+ 0x080c, 0xabfa, 0x0016, 0x080c, 0x97fd, 0x080c, 0x510c, 0x001e,
+ 0x080c, 0x9742, 0x00de, 0x080c, 0x974e, 0xace0, 0x0018, 0x2001,
+ 0xad16, 0x2004, 0xac02, 0x1210, 0x0804, 0xa726, 0x012e, 0x002e,
+ 0x004e, 0x005e, 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee, 0x0005,
+ 0xa786, 0x0006, 0x19c0, 0xa386, 0x0005, 0x0128, 0x080c, 0xabfa,
+ 0x080c, 0xa91f, 0x08f8, 0x00de, 0x0c00, 0x080c, 0xa9a0, 0x19e8,
+ 0x81ff, 0x09d8, 0xa180, 0x0001, 0x2004, 0xa086, 0x0018, 0x0130,
+ 0xa180, 0x0001, 0x2004, 0xa086, 0x002d, 0x1978, 0x6000, 0xa086,
+ 0x0002, 0x1958, 0x080c, 0x9778, 0x0130, 0x080c, 0x9789, 0x1928,
+ 0x080c, 0x85f3, 0x0038, 0x080c, 0x2aff, 0x080c, 0x9789, 0x1110,
+ 0x080c, 0x85f3, 0x080c, 0x974e, 0x0804, 0xa785, 0x00c6, 0x00e6,
+ 0x0016, 0x2c08, 0x2170, 0x080c, 0xa940, 0x001e, 0x0120, 0x601c,
+ 0xa084, 0x000f, 0x001b, 0x00ee, 0x00ce, 0x0005, 0xa7e6, 0xa7e6,
+ 0xa7e6, 0xa7e6, 0xa7e6, 0xa7e6, 0xa7e8, 0xa7e6, 0xa006, 0x0005,
+ 0x0046, 0x0016, 0x7018, 0xa080, 0x0028, 0x2024, 0xa4a4, 0x00ff,
+ 0x8427, 0x2c00, 0x2009, 0x0020, 0x080c, 0xa96c, 0x001e, 0x004e,
+ 0x0036, 0x2019, 0x0002, 0x080c, 0xa566, 0x003e, 0xa085, 0x0001,
+ 0x0005, 0x2001, 0x0001, 0x080c, 0x4c1e, 0x0156, 0x0016, 0x0026,
+ 0x0036, 0x20a9, 0x0004, 0x2019, 0xad05, 0x2011, 0xb296, 0x080c,
+ 0x8a7c, 0x003e, 0x002e, 0x001e, 0x015e, 0xa005, 0x0005, 0x00f6,
+ 0x00e6, 0x00c6, 0x0086, 0x0076, 0x0066, 0x0026, 0x0126, 0x2091,
+ 0x8000, 0x2740, 0x2061, 0xb400, 0x2079, 0x0001, 0x8fff, 0x0904,
+ 0xa875, 0x2071, 0xad00, 0x7644, 0x7064, 0x8001, 0xa602, 0x1a04,
+ 0xa875, 0x88ff, 0x0128, 0x2800, 0xac06, 0x15b0, 0x2079, 0x0000,
+ 0x080c, 0xa990, 0x0588, 0x2400, 0xac06, 0x0570, 0x671c, 0xa786,
+ 0x0006, 0x1550, 0xa786, 0x0007, 0x0538, 0x88ff, 0x1140, 0x6018,
+ 0xa206, 0x1510, 0x85ff, 0x0118, 0x6050, 0xa106, 0x11e8, 0x00d6,
+ 0x6000, 0xa086, 0x0004, 0x1150, 0x080c, 0xabb4, 0x601f, 0x0007,
+ 0x2001, 0xafa3, 0x2004, 0x6016, 0x080c, 0x190b, 0x6010, 0x2068,
+ 0x080c, 0x9596, 0x0120, 0x0046, 0x080c, 0xa91f, 0x004e, 0x00de,
+ 0x080c, 0x974e, 0x88ff, 0x1198, 0xace0, 0x0018, 0x2001, 0xad16,
+ 0x2004, 0xac02, 0x1210, 0x0804, 0xa826, 0xa006, 0x012e, 0x002e,
+ 0x006e, 0x007e, 0x008e, 0x00ce, 0x00ee, 0x00fe, 0x0005, 0xa8c5,
+ 0x0001, 0x0ca0, 0x0076, 0x0056, 0x0086, 0x2041, 0x0000, 0x2029,
+ 0x0001, 0x2c20, 0x2019, 0x0002, 0x6218, 0x0096, 0x2049, 0x0000,
+ 0x080c, 0x7b9a, 0x009e, 0x008e, 0x2039, 0x0000, 0x080c, 0x7c34,
+ 0x080c, 0xa817, 0x005e, 0x007e, 0x0005, 0x0026, 0x0046, 0x0056,
+ 0x0076, 0x00c6, 0x0156, 0x2c20, 0x2128, 0x20a9, 0x007f, 0x2009,
+ 0x0000, 0x0016, 0x0036, 0x080c, 0x4cdc, 0x11b0, 0x2c10, 0x0056,
+ 0x0086, 0x2041, 0x0000, 0x2508, 0x2029, 0x0001, 0x0096, 0x2049,
+ 0x0000, 0x080c, 0x7b9a, 0x009e, 0x008e, 0x2039, 0x0000, 0x080c,
+ 0x7c34, 0x080c, 0xa817, 0x005e, 0x003e, 0x001e, 0x8108, 0x1f04,
+ 0xa8a9, 0x015e, 0x00ce, 0x007e, 0x005e, 0x004e, 0x002e, 0x0005,
+ 0x0076, 0x0056, 0x6218, 0x0086, 0x2041, 0x0000, 0x2029, 0x0001,
+ 0x2019, 0x0048, 0x0096, 0x2049, 0x0000, 0x080c, 0x7b9a, 0x009e,
+ 0x008e, 0x2039, 0x0000, 0x080c, 0x7c34, 0x2c20, 0x080c, 0xa817,
+ 0x005e, 0x007e, 0x0005, 0x0026, 0x0046, 0x0056, 0x0076, 0x00c6,
+ 0x0156, 0x2c20, 0x20a9, 0x007f, 0x2009, 0x0000, 0x0016, 0x0036,
+ 0x080c, 0x4cdc, 0x11c0, 0x2c10, 0x0086, 0x2041, 0x0000, 0x2828,
+ 0x0046, 0x2021, 0x0001, 0x080c, 0xab96, 0x004e, 0x0096, 0x2049,
+ 0x0000, 0x080c, 0x7b9a, 0x009e, 0x008e, 0x2039, 0x0000, 0x080c,
+ 0x7c34, 0x080c, 0xa817, 0x003e, 0x001e, 0x8108, 0x1f04, 0xa8f6,
+ 0x015e, 0x00ce, 0x007e, 0x005e, 0x004e, 0x002e, 0x0005, 0x0016,
+ 0x00f6, 0x3800, 0xd08c, 0x0130, 0xad82, 0x1000, 0x02b0, 0xad82,
+ 0xad00, 0x0230, 0xad82, 0xe400, 0x0280, 0xad82, 0xffff, 0x1268,
+ 0x6800, 0xa07d, 0x0138, 0x6803, 0x0000, 0x6b52, 0x080c, 0x510c,
+ 0x2f68, 0x0cb0, 0x6b52, 0x080c, 0x510c, 0x00fe, 0x001e, 0x0005,
+ 0x00e6, 0x0046, 0x0036, 0x2061, 0xb400, 0x2071, 0xad00, 0x7444,
+ 0x7064, 0x8001, 0xa402, 0x12d8, 0x2100, 0xac06, 0x0168, 0x6000,
+ 0xa086, 0x0000, 0x0148, 0x6008, 0xa206, 0x1130, 0x6018, 0xa1a0,
+ 0x0006, 0x2424, 0xa406, 0x0140, 0xace0, 0x0018, 0x2001, 0xad16,
+ 0x2004, 0xac02, 0x1220, 0x0c08, 0xa085, 0x0001, 0x0008, 0xa006,
+ 0x003e, 0x004e, 0x00ee, 0x0005, 0x00d6, 0x0006, 0x080c, 0x15d9,
+ 0x000e, 0x090c, 0x14f6, 0x6837, 0x010d, 0x685e, 0x0026, 0x2010,
+ 0x080c, 0x9586, 0x2001, 0x0000, 0x0120, 0x2200, 0xa080, 0x0014,
+ 0x2004, 0x002e, 0x684a, 0x6956, 0x6c46, 0x684f, 0x0000, 0xa006,
+ 0x68b2, 0x6802, 0x683a, 0x685a, 0x080c, 0x510c, 0x00de, 0x0005,
+ 0x6700, 0xa786, 0x0000, 0x0158, 0xa786, 0x0001, 0x0140, 0xa786,
+ 0x000a, 0x0128, 0xa786, 0x0009, 0x0110, 0xa085, 0x0001, 0x0005,
+ 0x00e6, 0x6018, 0x2070, 0x70a0, 0xa206, 0x00ee, 0x0005, 0x0016,
+ 0x6004, 0xa08e, 0x001e, 0x11a0, 0x8007, 0x6130, 0xa18c, 0x00ff,
+ 0xa105, 0x6032, 0x6007, 0x0085, 0x6003, 0x000b, 0x601f, 0x0005,
+ 0x2001, 0xafa4, 0x2004, 0x6016, 0x080c, 0x67a8, 0x080c, 0x6c50,
+ 0x001e, 0x0005, 0xe000, 0xe000, 0x0005, 0x6020, 0xd0e4, 0x0158,
+ 0xd0cc, 0x0118, 0x080c, 0x9866, 0x0030, 0x080c, 0xabb4, 0x080c,
+ 0x6618, 0x080c, 0x8078, 0x0005, 0xa280, 0x0007, 0x2004, 0xa084,
+ 0x000f, 0x0002, 0xa9e3, 0xa9e3, 0xa9e3, 0xa9e8, 0xa9e3, 0xa9e5,
+ 0xa9e5, 0xa9e3, 0xa9e5, 0xa006, 0x0005, 0x00c6, 0x2260, 0x00ce,
+ 0xa085, 0x0001, 0x0005, 0xa280, 0x0007, 0x2004, 0xa084, 0x000f,
+ 0x0002, 0xa9fa, 0xa9fa, 0xa9fa, 0xa9fa, 0xa9fa, 0xa9fa, 0xaa05,
+ 0xa9fa, 0xa9fa, 0x6007, 0x003b, 0x602b, 0x0009, 0x6013, 0x2a00,
+ 0x6003, 0x0001, 0x080c, 0x67a8, 0x0005, 0x00c6, 0x2260, 0x080c,
+ 0xabb4, 0x603f, 0x0000, 0x6020, 0xc0f4, 0xc0cc, 0x6022, 0x6037,
+ 0x0000, 0x00ce, 0x00d6, 0x2268, 0xa186, 0x0007, 0x1904, 0xaa60,
+ 0x6810, 0xa005, 0x0138, 0xa080, 0x0013, 0x2004, 0xd0fc, 0x1110,
+ 0x00de, 0x08c0, 0x6007, 0x003a, 0x6003, 0x0001, 0x080c, 0x67a8,
+ 0x080c, 0x6c50, 0x00c6, 0x2d60, 0x6100, 0xa186, 0x0002, 0x1904,
+ 0xaae7, 0x6010, 0xa005, 0x1138, 0x6000, 0xa086, 0x0007, 0x190c,
+ 0x14f6, 0x0804, 0xaae7, 0xa08c, 0xf000, 0x1130, 0x0028, 0x2068,
+ 0x6800, 0xa005, 0x1de0, 0x2d00, 0xa080, 0x0013, 0x2004, 0xa084,
+ 0x0003, 0xa086, 0x0002, 0x1180, 0x6010, 0x2068, 0x684c, 0xc0dc,
+ 0xc0f4, 0x684e, 0x6850, 0xc0f4, 0xc0fc, 0x6852, 0x2009, 0x0043,
+ 0x080c, 0xa3de, 0x0804, 0xaae7, 0x2009, 0x0041, 0x0804, 0xaae1,
+ 0xa186, 0x0005, 0x15f0, 0x6810, 0xa080, 0x0013, 0x2004, 0xd0bc,
+ 0x1118, 0x00de, 0x0804, 0xa9fa, 0xd0b4, 0x0128, 0xd0fc, 0x090c,
+ 0x14f6, 0x0804, 0xaa18, 0x6007, 0x003a, 0x6003, 0x0001, 0x080c,
+ 0x67a8, 0x080c, 0x6c50, 0x00c6, 0x2d60, 0x6100, 0xa186, 0x0002,
+ 0x0120, 0xa186, 0x0004, 0x1904, 0xaae7, 0x2071, 0xaffd, 0x7000,
+ 0xa086, 0x0003, 0x1128, 0x7004, 0xac06, 0x1110, 0x7003, 0x0000,
+ 0x6810, 0xa080, 0x0013, 0x200c, 0xc1f4, 0xc1dc, 0x2102, 0x8000,
+ 0x200c, 0xc1f4, 0xc1fc, 0xc1bc, 0x2102, 0x2009, 0x0042, 0x0804,
+ 0xaae1, 0x0036, 0x00d6, 0x00d6, 0x080c, 0x15d9, 0x003e, 0x090c,
+ 0x14f6, 0x6837, 0x010d, 0x6803, 0x0000, 0x683b, 0x0000, 0x685b,
+ 0x0000, 0x6b5e, 0x6857, 0x0045, 0x2c00, 0x6862, 0x6034, 0x6872,
+ 0x2360, 0x6020, 0xc0dd, 0x6022, 0x6018, 0xa080, 0x0028, 0x2004,
+ 0xa084, 0x00ff, 0x8007, 0x6350, 0x6b4a, 0x6846, 0x684f, 0x0000,
+ 0x6d6a, 0x6e66, 0x686f, 0x0001, 0x080c, 0x510c, 0x2019, 0x0045,
+ 0x6008, 0x2068, 0x080c, 0xa566, 0x2d00, 0x600a, 0x601f, 0x0006,
+ 0x6003, 0x0007, 0x6017, 0x0000, 0x603f, 0x0000, 0x00de, 0x003e,
+ 0x0038, 0x603f, 0x0000, 0x6003, 0x0007, 0x080c, 0xa3de, 0x00ce,
+ 0x00de, 0x0005, 0xa186, 0x0013, 0x1128, 0x6004, 0xa082, 0x0085,
+ 0x2008, 0x00c2, 0xa186, 0x0027, 0x1178, 0x080c, 0x6b73, 0x0036,
+ 0x00d6, 0x6010, 0x2068, 0x2019, 0x0004, 0x080c, 0xa91f, 0x00de,
+ 0x003e, 0x080c, 0x6c50, 0x0005, 0xa186, 0x0014, 0x0d70, 0x080c,
+ 0x80be, 0x0005, 0xab13, 0xab11, 0xab11, 0xab11, 0xab11, 0xab11,
+ 0xab13, 0x080c, 0x14f6, 0x080c, 0x6b73, 0x6003, 0x000c, 0x080c,
+ 0x6c50, 0x0005, 0xa182, 0x008c, 0x1220, 0xa182, 0x0085, 0x0208,
+ 0x001a, 0x080c, 0x80be, 0x0005, 0xab2b, 0xab2b, 0xab2b, 0xab2b,
+ 0xab2d, 0xab4b, 0xab2b, 0x080c, 0x14f6, 0x00d6, 0x2c68, 0x080c,
+ 0x8022, 0x01a0, 0x6003, 0x0001, 0x6007, 0x001e, 0x2009, 0xb28e,
+ 0x210c, 0x6136, 0x2009, 0xb28f, 0x210c, 0x613a, 0x600b, 0xffff,
+ 0x6918, 0x611a, 0x601f, 0x0004, 0x080c, 0x67a8, 0x2d60, 0x080c,
+ 0x8078, 0x00de, 0x0005, 0x080c, 0x8078, 0x0005, 0x00e6, 0x6018,
+ 0x2070, 0x7000, 0xd0ec, 0x00ee, 0x0005, 0x6010, 0xa080, 0x0013,
+ 0x200c, 0xd1ec, 0x05d0, 0x2001, 0xad71, 0x2004, 0xd0ec, 0x05a8,
+ 0x6003, 0x0002, 0x6020, 0xc0e5, 0x6022, 0xd1ac, 0x0180, 0x00f6,
+ 0x2c78, 0x080c, 0x5025, 0x00fe, 0x0150, 0x2001, 0xafa5, 0x2004,
+ 0x603e, 0x2009, 0xad71, 0x210c, 0xd1f4, 0x11e8, 0x0080, 0x2009,
+ 0xad71, 0x210c, 0xd1f4, 0x0128, 0x6020, 0xc0e4, 0x6022, 0xa006,
+ 0x00a0, 0x2001, 0xafa5, 0x200c, 0x8103, 0xa100, 0x603e, 0x6018,
+ 0xa088, 0x002b, 0x2104, 0xa005, 0x0118, 0xa088, 0x0003, 0x0cd0,
+ 0x2c0a, 0x600f, 0x0000, 0xa085, 0x0001, 0x0005, 0x0016, 0x00c6,
+ 0x00e6, 0x6150, 0xa2f0, 0x002b, 0x2e04, 0x2060, 0x8cff, 0x0180,
+ 0x84ff, 0x1118, 0x6050, 0xa106, 0x1138, 0x600c, 0x2072, 0x080c,
+ 0x6618, 0x080c, 0x8078, 0x0010, 0xacf0, 0x0003, 0x2e64, 0x0c70,
+ 0x00ee, 0x00ce, 0x001e, 0x0005, 0x00d6, 0x6018, 0xa0e8, 0x002b,
+ 0x2d04, 0xa005, 0x0140, 0xac06, 0x0120, 0x2d04, 0xa0e8, 0x0003,
+ 0x0cb8, 0x600c, 0x206a, 0x00de, 0x0005, 0x0026, 0x0036, 0x0156,
+ 0x2011, 0xad27, 0x2204, 0xa084, 0x00ff, 0x2019, 0xb28e, 0x2334,
+ 0xa636, 0x11d8, 0x8318, 0x2334, 0x2204, 0xa084, 0xff00, 0xa636,
+ 0x11a0, 0x2011, 0xb290, 0x6018, 0xa098, 0x000a, 0x20a9, 0x0004,
+ 0x080c, 0x8a7c, 0x1150, 0x2011, 0xb294, 0x6018, 0xa098, 0x0006,
+ 0x20a9, 0x0004, 0x080c, 0x8a7c, 0x1100, 0x015e, 0x003e, 0x002e,
+ 0x0005, 0x00e6, 0x2071, 0xad00, 0x080c, 0x48f5, 0x080c, 0x28fa,
+ 0x00ee, 0x0005, 0x00e6, 0x6018, 0x2070, 0x7000, 0xd0fc, 0x0108,
+ 0x0011, 0x00ee, 0x0005, 0x6850, 0xc0e5, 0x6852, 0x0005, 0x00e6,
+ 0x00c6, 0x0076, 0x0066, 0x0056, 0x0046, 0x0026, 0x0016, 0x0126,
+ 0x2091, 0x8000, 0x2029, 0xafd0, 0x252c, 0x2021, 0xafd6, 0x2424,
+ 0x2061, 0xb400, 0x2071, 0xad00, 0x7644, 0x7064, 0xa606, 0x0578,
+ 0x671c, 0xa786, 0x0001, 0x0118, 0xa786, 0x0008, 0x1500, 0x2500,
+ 0xac06, 0x01e8, 0x2400, 0xac06, 0x01d0, 0x080c, 0xa990, 0x01b8,
+ 0x080c, 0xa9a0, 0x11a0, 0x6000, 0xa086, 0x0004, 0x1120, 0x0016,
+ 0x080c, 0x190b, 0x001e, 0x080c, 0x9778, 0x1110, 0x080c, 0x2aff,
+ 0x080c, 0x9789, 0x1110, 0x080c, 0x85f3, 0x080c, 0x974e, 0xace0,
+ 0x0018, 0x2001, 0xad16, 0x2004, 0xac02, 0x1208, 0x0858, 0x012e,
+ 0x001e, 0x002e, 0x004e, 0x005e, 0x006e, 0x007e, 0x00ce, 0x00ee,
+ 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000, 0x2071, 0xad40,
+ 0xd5a4, 0x0118, 0x7034, 0x8000, 0x7036, 0xd5b4, 0x0118, 0x7030,
+ 0x8000, 0x7032, 0xd5ac, 0x0118, 0x2071, 0xad4a, 0x0451, 0x00ee,
+ 0x000e, 0x012e, 0x0005, 0x0126, 0x0006, 0x00e6, 0x2091, 0x8000,
+ 0x2071, 0xad40, 0xd5a4, 0x0118, 0x7034, 0x8000, 0x7036, 0xd5b4,
+ 0x0118, 0x7030, 0x8000, 0x7032, 0xd5ac, 0x0118, 0x2071, 0xad4a,
+ 0x0081, 0x00ee, 0x000e, 0x012e, 0x0005, 0x0126, 0x0006, 0x00e6,
+ 0x2091, 0x8000, 0x2071, 0xad42, 0x0021, 0x00ee, 0x000e, 0x012e,
+ 0x0005, 0x2e04, 0x8000, 0x2072, 0x1220, 0x8e70, 0x2e04, 0x8000,
+ 0x2072, 0x0005, 0x00e6, 0x2071, 0xad40, 0x0c99, 0x00ee, 0x0005,
+ 0x00e6, 0x2071, 0xad44, 0x0c69, 0x00ee, 0x0005, 0x0001, 0x0002,
+ 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,
+ 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 0x8529
+};
+
diff --git a/drivers/scsi/qlogicisp.c b/drivers/scsi/qlogicisp.c
new file mode 100644
index 000000000000..71d597a9b0b0
--- /dev/null
+++ b/drivers/scsi/qlogicisp.c
@@ -0,0 +1,1935 @@
+/*
+ * QLogic ISP1020 Intelligent SCSI Processor Driver (PCI)
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ * Copyright 1996, 1997 Michael A. Griffith <grif@acm.org>
+ * Copyright 2000, Jayson C. Vantuyl <vantuyl@csc.smsu.edu>
+ * and Bryon W. Roche <bryon@csc.smsu.edu>
+ *
+ * 64-bit addressing added by Kanoj Sarcar <kanoj@sgi.com>
+ * and Leo Dagum <dagum@sgi.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+/*
+ * With the qlogic interface, every queue slot can hold a SCSI
+ * command with up to 4 scatter/gather entries. If we need more
+ * than 4 entries, continuation entries can be used that hold
+ * another 7 entries each. Unlike for other drivers, this means
+ * that the maximum number of scatter/gather entries we can
+ * support at any given time is a function of the number of queue
+ * slots available. That is, host->can_queue and host->sg_tablesize
+ * are dynamic and _not_ independent. This all works fine because
+ * requests are queued serially and the scatter/gather limit is
+ * determined for each queue request anew.
+ */
+#define QLOGICISP_REQ_QUEUE_LEN 63 /* must be power of two - 1 */
+#define QLOGICISP_MAX_SG(ql) (4 + ((ql) > 0) ? 7*((ql) - 1) : 0)
+
+/* Configuration section *****************************************************/
+
+/* Set the following macro to 1 to reload the ISP1020's firmware. This is
+ the latest firmware provided by QLogic. This may be an earlier/later
+ revision than supplied by your board. */
+
+#define RELOAD_FIRMWARE 1
+
+/* Set the following macro to 1 to reload the ISP1020's defaults from nvram.
+ If you are not sure of your settings, leave this alone, the driver will
+ use a set of 'safe' defaults */
+
+#define USE_NVRAM_DEFAULTS 0
+
+/* Macros used for debugging */
+
+#define DEBUG_ISP1020 0
+#define DEBUG_ISP1020_INTR 0
+#define DEBUG_ISP1020_SETUP 0
+#define TRACE_ISP 0
+
+#define DEFAULT_LOOP_COUNT 1000000
+
+/* End Configuration section *************************************************/
+
+#include <linux/module.h>
+
+#if TRACE_ISP
+
+# define TRACE_BUF_LEN (32*1024)
+
+struct {
+ u_long next;
+ struct {
+ u_long time;
+ u_int index;
+ u_int addr;
+ u_char * name;
+ } buf[TRACE_BUF_LEN];
+} trace;
+
+#define TRACE(w, i, a) \
+{ \
+ unsigned long flags; \
+ \
+ trace.buf[trace.next].name = (w); \
+ trace.buf[trace.next].time = jiffies; \
+ trace.buf[trace.next].index = (i); \
+ trace.buf[trace.next].addr = (long) (a); \
+ trace.next = (trace.next + 1) & (TRACE_BUF_LEN - 1); \
+}
+
+#else
+# define TRACE(w, i, a)
+#endif
+
+#if DEBUG_ISP1020
+#define ENTER(x) printk("isp1020 : entering %s()\n", x);
+#define LEAVE(x) printk("isp1020 : leaving %s()\n", x);
+#define DEBUG(x) x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif /* DEBUG_ISP1020 */
+
+#if DEBUG_ISP1020_INTR
+#define ENTER_INTR(x) printk("isp1020 : entering %s()\n", x);
+#define LEAVE_INTR(x) printk("isp1020 : leaving %s()\n", x);
+#define DEBUG_INTR(x) x
+#else
+#define ENTER_INTR(x)
+#define LEAVE_INTR(x)
+#define DEBUG_INTR(x)
+#endif /* DEBUG ISP1020_INTR */
+
+#define ISP1020_REV_ID 1
+
+#define MAX_TARGETS 16
+#define MAX_LUNS 8
+
+/* host configuration and control registers */
+#define HOST_HCCR 0xc0 /* host command and control */
+
+/* pci bus interface registers */
+#define PCI_ID_LOW 0x00 /* vendor id */
+#define PCI_ID_HIGH 0x02 /* device id */
+#define ISP_CFG0 0x04 /* configuration register #0 */
+#define ISP_CFG0_HWMSK 0x000f /* Hardware revision mask */
+#define ISP_CFG0_1020 0x0001 /* ISP1020 */
+#define ISP_CFG0_1020A 0x0002 /* ISP1020A */
+#define ISP_CFG0_1040 0x0003 /* ISP1040 */
+#define ISP_CFG0_1040A 0x0004 /* ISP1040A */
+#define ISP_CFG0_1040B 0x0005 /* ISP1040B */
+#define ISP_CFG0_1040C 0x0006 /* ISP1040C */
+#define ISP_CFG1 0x06 /* configuration register #1 */
+#define ISP_CFG1_F128 0x0040 /* 128-byte FIFO threshold */
+#define ISP_CFG1_F64 0x0030 /* 128-byte FIFO threshold */
+#define ISP_CFG1_F32 0x0020 /* 128-byte FIFO threshold */
+#define ISP_CFG1_F16 0x0010 /* 128-byte FIFO threshold */
+#define ISP_CFG1_BENAB 0x0004 /* Global Bus burst enable */
+#define ISP_CFG1_SXP 0x0001 /* SXP register select */
+#define PCI_INTF_CTL 0x08 /* pci interface control */
+#define PCI_INTF_STS 0x0a /* pci interface status */
+#define PCI_SEMAPHORE 0x0c /* pci semaphore */
+#define PCI_NVRAM 0x0e /* pci nvram interface */
+#define CDMA_CONF 0x20 /* Command DMA Config */
+#define DDMA_CONF 0x40 /* Data DMA Config */
+#define DMA_CONF_SENAB 0x0008 /* SXP to DMA Data enable */
+#define DMA_CONF_RIRQ 0x0004 /* RISC interrupt enable */
+#define DMA_CONF_BENAB 0x0002 /* Bus burst enable */
+#define DMA_CONF_DIR 0x0001 /* DMA direction (0=fifo->host 1=host->fifo) */
+
+/* mailbox registers */
+#define MBOX0 0x70 /* mailbox 0 */
+#define MBOX1 0x72 /* mailbox 1 */
+#define MBOX2 0x74 /* mailbox 2 */
+#define MBOX3 0x76 /* mailbox 3 */
+#define MBOX4 0x78 /* mailbox 4 */
+#define MBOX5 0x7a /* mailbox 5 */
+#define MBOX6 0x7c /* mailbox 6 */
+#define MBOX7 0x7e /* mailbox 7 */
+
+/* mailbox command complete status codes */
+#define MBOX_COMMAND_COMPLETE 0x4000
+#define INVALID_COMMAND 0x4001
+#define HOST_INTERFACE_ERROR 0x4002
+#define TEST_FAILED 0x4003
+#define COMMAND_ERROR 0x4005
+#define COMMAND_PARAM_ERROR 0x4006
+
+/* async event status codes */
+#define ASYNC_SCSI_BUS_RESET 0x8001
+#define SYSTEM_ERROR 0x8002
+#define REQUEST_TRANSFER_ERROR 0x8003
+#define RESPONSE_TRANSFER_ERROR 0x8004
+#define REQUEST_QUEUE_WAKEUP 0x8005
+#define EXECUTION_TIMEOUT_RESET 0x8006
+
+#ifdef CONFIG_QL_ISP_A64
+#define IOCB_SEGS 2
+#define CONTINUATION_SEGS 5
+#define MAX_CONTINUATION_ENTRIES 254
+#else
+#define IOCB_SEGS 4
+#define CONTINUATION_SEGS 7
+#endif /* CONFIG_QL_ISP_A64 */
+
+struct Entry_header {
+ u_char entry_type;
+ u_char entry_cnt;
+ u_char sys_def_1;
+ u_char flags;
+};
+
+/* entry header type commands */
+#ifdef CONFIG_QL_ISP_A64
+#define ENTRY_COMMAND 9
+#define ENTRY_CONTINUATION 0xa
+#else
+#define ENTRY_COMMAND 1
+#define ENTRY_CONTINUATION 2
+#endif /* CONFIG_QL_ISP_A64 */
+
+#define ENTRY_STATUS 3
+#define ENTRY_MARKER 4
+#define ENTRY_EXTENDED_COMMAND 5
+
+/* entry header flag definitions */
+#define EFLAG_CONTINUATION 1
+#define EFLAG_BUSY 2
+#define EFLAG_BAD_HEADER 4
+#define EFLAG_BAD_PAYLOAD 8
+
+struct dataseg {
+ u_int d_base;
+#ifdef CONFIG_QL_ISP_A64
+ u_int d_base_hi;
+#endif
+ u_int d_count;
+};
+
+struct Command_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_char target_lun;
+ u_char target_id;
+ u_short cdb_length;
+ u_short control_flags;
+ u_short rsvd;
+ u_short time_out;
+ u_short segment_cnt;
+ u_char cdb[12];
+#ifdef CONFIG_QL_ISP_A64
+ u_int rsvd1;
+ u_int rsvd2;
+#endif
+ struct dataseg dataseg[IOCB_SEGS];
+};
+
+/* command entry control flag definitions */
+#define CFLAG_NODISC 0x01
+#define CFLAG_HEAD_TAG 0x02
+#define CFLAG_ORDERED_TAG 0x04
+#define CFLAG_SIMPLE_TAG 0x08
+#define CFLAG_TAR_RTN 0x10
+#define CFLAG_READ 0x20
+#define CFLAG_WRITE 0x40
+
+struct Ext_Command_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_char target_lun;
+ u_char target_id;
+ u_short cdb_length;
+ u_short control_flags;
+ u_short rsvd;
+ u_short time_out;
+ u_short segment_cnt;
+ u_char cdb[44];
+};
+
+struct Continuation_Entry {
+ struct Entry_header hdr;
+#ifndef CONFIG_QL_ISP_A64
+ u_int reserved;
+#endif
+ struct dataseg dataseg[CONTINUATION_SEGS];
+};
+
+struct Marker_Entry {
+ struct Entry_header hdr;
+ u_int reserved;
+ u_char target_lun;
+ u_char target_id;
+ u_char modifier;
+ u_char rsvd;
+ u_char rsvds[52];
+};
+
+/* marker entry modifier definitions */
+#define SYNC_DEVICE 0
+#define SYNC_TARGET 1
+#define SYNC_ALL 2
+
+struct Status_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_short scsi_status;
+ u_short completion_status;
+ u_short state_flags;
+ u_short status_flags;
+ u_short time;
+ u_short req_sense_len;
+ u_int residual;
+ u_char rsvd[8];
+ u_char req_sense_data[32];
+};
+
+/* status entry completion status definitions */
+#define CS_COMPLETE 0x0000
+#define CS_INCOMPLETE 0x0001
+#define CS_DMA_ERROR 0x0002
+#define CS_TRANSPORT_ERROR 0x0003
+#define CS_RESET_OCCURRED 0x0004
+#define CS_ABORTED 0x0005
+#define CS_TIMEOUT 0x0006
+#define CS_DATA_OVERRUN 0x0007
+#define CS_COMMAND_OVERRUN 0x0008
+#define CS_STATUS_OVERRUN 0x0009
+#define CS_BAD_MESSAGE 0x000a
+#define CS_NO_MESSAGE_OUT 0x000b
+#define CS_EXT_ID_FAILED 0x000c
+#define CS_IDE_MSG_FAILED 0x000d
+#define CS_ABORT_MSG_FAILED 0x000e
+#define CS_REJECT_MSG_FAILED 0x000f
+#define CS_NOP_MSG_FAILED 0x0010
+#define CS_PARITY_ERROR_MSG_FAILED 0x0011
+#define CS_DEVICE_RESET_MSG_FAILED 0x0012
+#define CS_ID_MSG_FAILED 0x0013
+#define CS_UNEXP_BUS_FREE 0x0014
+#define CS_DATA_UNDERRUN 0x0015
+
+/* status entry state flag definitions */
+#define SF_GOT_BUS 0x0100
+#define SF_GOT_TARGET 0x0200
+#define SF_SENT_CDB 0x0400
+#define SF_TRANSFERRED_DATA 0x0800
+#define SF_GOT_STATUS 0x1000
+#define SF_GOT_SENSE 0x2000
+
+/* status entry status flag definitions */
+#define STF_DISCONNECT 0x0001
+#define STF_SYNCHRONOUS 0x0002
+#define STF_PARITY_ERROR 0x0004
+#define STF_BUS_RESET 0x0008
+#define STF_DEVICE_RESET 0x0010
+#define STF_ABORTED 0x0020
+#define STF_TIMEOUT 0x0040
+#define STF_NEGOTIATION 0x0080
+
+/* interface control commands */
+#define ISP_RESET 0x0001
+#define ISP_EN_INT 0x0002
+#define ISP_EN_RISC 0x0004
+
+/* host control commands */
+#define HCCR_NOP 0x0000
+#define HCCR_RESET 0x1000
+#define HCCR_PAUSE 0x2000
+#define HCCR_RELEASE 0x3000
+#define HCCR_SINGLE_STEP 0x4000
+#define HCCR_SET_HOST_INTR 0x5000
+#define HCCR_CLEAR_HOST_INTR 0x6000
+#define HCCR_CLEAR_RISC_INTR 0x7000
+#define HCCR_BP_ENABLE 0x8000
+#define HCCR_BIOS_DISABLE 0x9000
+#define HCCR_TEST_MODE 0xf000
+
+#define RISC_BUSY 0x0004
+
+/* mailbox commands */
+#define MBOX_NO_OP 0x0000
+#define MBOX_LOAD_RAM 0x0001
+#define MBOX_EXEC_FIRMWARE 0x0002
+#define MBOX_DUMP_RAM 0x0003
+#define MBOX_WRITE_RAM_WORD 0x0004
+#define MBOX_READ_RAM_WORD 0x0005
+#define MBOX_MAILBOX_REG_TEST 0x0006
+#define MBOX_VERIFY_CHECKSUM 0x0007
+#define MBOX_ABOUT_FIRMWARE 0x0008
+#define MBOX_CHECK_FIRMWARE 0x000e
+#define MBOX_INIT_REQ_QUEUE 0x0010
+#define MBOX_INIT_RES_QUEUE 0x0011
+#define MBOX_EXECUTE_IOCB 0x0012
+#define MBOX_WAKE_UP 0x0013
+#define MBOX_STOP_FIRMWARE 0x0014
+#define MBOX_ABORT 0x0015
+#define MBOX_ABORT_DEVICE 0x0016
+#define MBOX_ABORT_TARGET 0x0017
+#define MBOX_BUS_RESET 0x0018
+#define MBOX_STOP_QUEUE 0x0019
+#define MBOX_START_QUEUE 0x001a
+#define MBOX_SINGLE_STEP_QUEUE 0x001b
+#define MBOX_ABORT_QUEUE 0x001c
+#define MBOX_GET_DEV_QUEUE_STATUS 0x001d
+#define MBOX_GET_FIRMWARE_STATUS 0x001f
+#define MBOX_GET_INIT_SCSI_ID 0x0020
+#define MBOX_GET_SELECT_TIMEOUT 0x0021
+#define MBOX_GET_RETRY_COUNT 0x0022
+#define MBOX_GET_TAG_AGE_LIMIT 0x0023
+#define MBOX_GET_CLOCK_RATE 0x0024
+#define MBOX_GET_ACT_NEG_STATE 0x0025
+#define MBOX_GET_ASYNC_DATA_SETUP_TIME 0x0026
+#define MBOX_GET_PCI_PARAMS 0x0027
+#define MBOX_GET_TARGET_PARAMS 0x0028
+#define MBOX_GET_DEV_QUEUE_PARAMS 0x0029
+#define MBOX_SET_INIT_SCSI_ID 0x0030
+#define MBOX_SET_SELECT_TIMEOUT 0x0031
+#define MBOX_SET_RETRY_COUNT 0x0032
+#define MBOX_SET_TAG_AGE_LIMIT 0x0033
+#define MBOX_SET_CLOCK_RATE 0x0034
+#define MBOX_SET_ACTIVE_NEG_STATE 0x0035
+#define MBOX_SET_ASYNC_DATA_SETUP_TIME 0x0036
+#define MBOX_SET_PCI_CONTROL_PARAMS 0x0037
+#define MBOX_SET_TARGET_PARAMS 0x0038
+#define MBOX_SET_DEV_QUEUE_PARAMS 0x0039
+#define MBOX_RETURN_BIOS_BLOCK_ADDR 0x0040
+#define MBOX_WRITE_FOUR_RAM_WORDS 0x0041
+#define MBOX_EXEC_BIOS_IOCB 0x0042
+
+#ifdef CONFIG_QL_ISP_A64
+#define MBOX_CMD_INIT_REQUEST_QUEUE_64 0x0052
+#define MBOX_CMD_INIT_RESPONSE_QUEUE_64 0x0053
+#endif /* CONFIG_QL_ISP_A64 */
+
+#include "qlogicisp_asm.c"
+
+#define PACKB(a, b) (((a)<<4)|(b))
+
+static const u_char mbox_param[] = {
+ PACKB(1, 1), /* MBOX_NO_OP */
+ PACKB(5, 5), /* MBOX_LOAD_RAM */
+ PACKB(2, 0), /* MBOX_EXEC_FIRMWARE */
+ PACKB(5, 5), /* MBOX_DUMP_RAM */
+ PACKB(3, 3), /* MBOX_WRITE_RAM_WORD */
+ PACKB(2, 3), /* MBOX_READ_RAM_WORD */
+ PACKB(6, 6), /* MBOX_MAILBOX_REG_TEST */
+ PACKB(2, 3), /* MBOX_VERIFY_CHECKSUM */
+ PACKB(1, 3), /* MBOX_ABOUT_FIRMWARE */
+ PACKB(0, 0), /* 0x0009 */
+ PACKB(0, 0), /* 0x000a */
+ PACKB(0, 0), /* 0x000b */
+ PACKB(0, 0), /* 0x000c */
+ PACKB(0, 0), /* 0x000d */
+ PACKB(1, 2), /* MBOX_CHECK_FIRMWARE */
+ PACKB(0, 0), /* 0x000f */
+ PACKB(5, 5), /* MBOX_INIT_REQ_QUEUE */
+ PACKB(6, 6), /* MBOX_INIT_RES_QUEUE */
+ PACKB(4, 4), /* MBOX_EXECUTE_IOCB */
+ PACKB(2, 2), /* MBOX_WAKE_UP */
+ PACKB(1, 6), /* MBOX_STOP_FIRMWARE */
+ PACKB(4, 4), /* MBOX_ABORT */
+ PACKB(2, 2), /* MBOX_ABORT_DEVICE */
+ PACKB(3, 3), /* MBOX_ABORT_TARGET */
+ PACKB(2, 2), /* MBOX_BUS_RESET */
+ PACKB(2, 3), /* MBOX_STOP_QUEUE */
+ PACKB(2, 3), /* MBOX_START_QUEUE */
+ PACKB(2, 3), /* MBOX_SINGLE_STEP_QUEUE */
+ PACKB(2, 3), /* MBOX_ABORT_QUEUE */
+ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_STATUS */
+ PACKB(0, 0), /* 0x001e */
+ PACKB(1, 3), /* MBOX_GET_FIRMWARE_STATUS */
+ PACKB(1, 2), /* MBOX_GET_INIT_SCSI_ID */
+ PACKB(1, 2), /* MBOX_GET_SELECT_TIMEOUT */
+ PACKB(1, 3), /* MBOX_GET_RETRY_COUNT */
+ PACKB(1, 2), /* MBOX_GET_TAG_AGE_LIMIT */
+ PACKB(1, 2), /* MBOX_GET_CLOCK_RATE */
+ PACKB(1, 2), /* MBOX_GET_ACT_NEG_STATE */
+ PACKB(1, 2), /* MBOX_GET_ASYNC_DATA_SETUP_TIME */
+ PACKB(1, 3), /* MBOX_GET_PCI_PARAMS */
+ PACKB(2, 4), /* MBOX_GET_TARGET_PARAMS */
+ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_PARAMS */
+ PACKB(0, 0), /* 0x002a */
+ PACKB(0, 0), /* 0x002b */
+ PACKB(0, 0), /* 0x002c */
+ PACKB(0, 0), /* 0x002d */
+ PACKB(0, 0), /* 0x002e */
+ PACKB(0, 0), /* 0x002f */
+ PACKB(2, 2), /* MBOX_SET_INIT_SCSI_ID */
+ PACKB(2, 2), /* MBOX_SET_SELECT_TIMEOUT */
+ PACKB(3, 3), /* MBOX_SET_RETRY_COUNT */
+ PACKB(2, 2), /* MBOX_SET_TAG_AGE_LIMIT */
+ PACKB(2, 2), /* MBOX_SET_CLOCK_RATE */
+ PACKB(2, 2), /* MBOX_SET_ACTIVE_NEG_STATE */
+ PACKB(2, 2), /* MBOX_SET_ASYNC_DATA_SETUP_TIME */
+ PACKB(3, 3), /* MBOX_SET_PCI_CONTROL_PARAMS */
+ PACKB(4, 4), /* MBOX_SET_TARGET_PARAMS */
+ PACKB(4, 4), /* MBOX_SET_DEV_QUEUE_PARAMS */
+ PACKB(0, 0), /* 0x003a */
+ PACKB(0, 0), /* 0x003b */
+ PACKB(0, 0), /* 0x003c */
+ PACKB(0, 0), /* 0x003d */
+ PACKB(0, 0), /* 0x003e */
+ PACKB(0, 0), /* 0x003f */
+ PACKB(1, 2), /* MBOX_RETURN_BIOS_BLOCK_ADDR */
+ PACKB(6, 1), /* MBOX_WRITE_FOUR_RAM_WORDS */
+ PACKB(2, 3) /* MBOX_EXEC_BIOS_IOCB */
+#ifdef CONFIG_QL_ISP_A64
+ ,PACKB(0, 0), /* 0x0043 */
+ PACKB(0, 0), /* 0x0044 */
+ PACKB(0, 0), /* 0x0045 */
+ PACKB(0, 0), /* 0x0046 */
+ PACKB(0, 0), /* 0x0047 */
+ PACKB(0, 0), /* 0x0048 */
+ PACKB(0, 0), /* 0x0049 */
+ PACKB(0, 0), /* 0x004a */
+ PACKB(0, 0), /* 0x004b */
+ PACKB(0, 0), /* 0x004c */
+ PACKB(0, 0), /* 0x004d */
+ PACKB(0, 0), /* 0x004e */
+ PACKB(0, 0), /* 0x004f */
+ PACKB(0, 0), /* 0x0050 */
+ PACKB(0, 0), /* 0x0051 */
+ PACKB(8, 8), /* MBOX_CMD_INIT_REQUEST_QUEUE_64 (0x0052) */
+ PACKB(8, 8) /* MBOX_CMD_INIT_RESPONSE_QUEUE_64 (0x0053) */
+#endif /* CONFIG_QL_ISP_A64 */
+};
+
+#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short))
+
+struct host_param {
+ u_short fifo_threshold;
+ u_short host_adapter_enable;
+ u_short initiator_scsi_id;
+ u_short bus_reset_delay;
+ u_short retry_count;
+ u_short retry_delay;
+ u_short async_data_setup_time;
+ u_short req_ack_active_negation;
+ u_short data_line_active_negation;
+ u_short data_dma_burst_enable;
+ u_short command_dma_burst_enable;
+ u_short tag_aging;
+ u_short selection_timeout;
+ u_short max_queue_depth;
+};
+
+/*
+ * Device Flags:
+ *
+ * Bit Name
+ * ---------
+ * 7 Disconnect Privilege
+ * 6 Parity Checking
+ * 5 Wide Data Transfers
+ * 4 Synchronous Data Transfers
+ * 3 Tagged Queuing
+ * 2 Automatic Request Sense
+ * 1 Stop Queue on Check Condition
+ * 0 Renegotiate on Error
+ */
+
+struct dev_param {
+ u_short device_flags;
+ u_short execution_throttle;
+ u_short synchronous_period;
+ u_short synchronous_offset;
+ u_short device_enable;
+ u_short reserved; /* pad */
+};
+
+/*
+ * The result queue can be quite a bit smaller since continuation entries
+ * do not show up there:
+ */
+#define RES_QUEUE_LEN ((QLOGICISP_REQ_QUEUE_LEN + 1) / 8 - 1)
+#define QUEUE_ENTRY_LEN 64
+#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
+
+struct isp_queue_entry {
+ char __opaque[QUEUE_ENTRY_LEN];
+};
+
+struct isp1020_hostdata {
+ void __iomem *memaddr;
+ u_char revision;
+ struct host_param host_param;
+ struct dev_param dev_param[MAX_TARGETS];
+ struct pci_dev *pci_dev;
+
+ struct isp_queue_entry *res_cpu; /* CPU-side address of response queue. */
+ struct isp_queue_entry *req_cpu; /* CPU-size address of request queue. */
+
+ /* result and request queues (shared with isp1020): */
+ u_int req_in_ptr; /* index of next request slot */
+ u_int res_out_ptr; /* index of next result slot */
+
+ /* this is here so the queues are nicely aligned */
+ long send_marker; /* do we need to send a marker? */
+
+ /* The cmd->handle has a fixed size, and is only 32-bits. We
+ * need to take care to handle 64-bit systems correctly thus what
+ * we actually place in cmd->handle is an index to the following
+ * table. Kudos to Matt Jacob for the technique. -DaveM
+ */
+ Scsi_Cmnd *cmd_slots[QLOGICISP_REQ_QUEUE_LEN + 1];
+
+ dma_addr_t res_dma; /* PCI side view of response queue */
+ dma_addr_t req_dma; /* PCI side view of request queue */
+};
+
+/* queue length's _must_ be power of two: */
+#define QUEUE_DEPTH(in, out, ql) ((in - out) & (ql))
+#define REQ_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, \
+ QLOGICISP_REQ_QUEUE_LEN)
+#define RES_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, RES_QUEUE_LEN)
+
+static void isp1020_enable_irqs(struct Scsi_Host *);
+static void isp1020_disable_irqs(struct Scsi_Host *);
+static int isp1020_init(struct Scsi_Host *);
+static int isp1020_reset_hardware(struct Scsi_Host *);
+static int isp1020_set_defaults(struct Scsi_Host *);
+static int isp1020_load_parameters(struct Scsi_Host *);
+static int isp1020_mbox_command(struct Scsi_Host *, u_short []);
+static int isp1020_return_status(struct Status_Entry *);
+static void isp1020_intr_handler(int, void *, struct pt_regs *);
+static irqreturn_t do_isp1020_intr_handler(int, void *, struct pt_regs *);
+
+#if USE_NVRAM_DEFAULTS
+static int isp1020_get_defaults(struct Scsi_Host *);
+static int isp1020_verify_nvram(struct Scsi_Host *);
+static u_short isp1020_read_nvram_word(struct Scsi_Host *, u_short);
+#endif
+
+#if DEBUG_ISP1020
+static void isp1020_print_scsi_cmd(Scsi_Cmnd *);
+#endif
+#if DEBUG_ISP1020_INTR
+static void isp1020_print_status_entry(struct Status_Entry *);
+#endif
+
+/* memaddr should be used to determine if memmapped port i/o is being used
+ * non-null memaddr == mmap'd
+ * JV 7-Jan-2000
+ */
+static inline u_short isp_inw(struct Scsi_Host *host, long offset)
+{
+ struct isp1020_hostdata *h = (struct isp1020_hostdata *)host->hostdata;
+ if (h->memaddr)
+ return readw(h->memaddr + offset);
+ else
+ return inw(host->io_port + offset);
+}
+
+static inline void isp_outw(u_short val, struct Scsi_Host *host, long offset)
+{
+ struct isp1020_hostdata *h = (struct isp1020_hostdata *)host->hostdata;
+ if (h->memaddr)
+ writew(val, h->memaddr + offset);
+ else
+ outw(val, host->io_port + offset);
+}
+
+static inline void isp1020_enable_irqs(struct Scsi_Host *host)
+{
+ isp_outw(ISP_EN_INT|ISP_EN_RISC, host, PCI_INTF_CTL);
+}
+
+
+static inline void isp1020_disable_irqs(struct Scsi_Host *host)
+{
+ isp_outw(0x0, host, PCI_INTF_CTL);
+}
+
+
+static int isp1020_detect(Scsi_Host_Template *tmpt)
+{
+ int hosts = 0;
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+ struct pci_dev *pdev = NULL;
+
+ ENTER("isp1020_detect");
+
+ tmpt->proc_name = "isp1020";
+
+ while ((pdev = pci_find_device(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1020, pdev)))
+ {
+ if (pci_enable_device(pdev))
+ continue;
+
+ host = scsi_register(tmpt, sizeof(struct isp1020_hostdata));
+ if (!host)
+ continue;
+
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ memset(hostdata, 0, sizeof(struct isp1020_hostdata));
+
+ hostdata->pci_dev = pdev;
+ scsi_set_device(host, &pdev->dev);
+
+ if (isp1020_init(host))
+ goto fail_and_unregister;
+
+ if (isp1020_reset_hardware(host)
+#if USE_NVRAM_DEFAULTS
+ || isp1020_get_defaults(host)
+#else
+ || isp1020_set_defaults(host)
+#endif /* USE_NVRAM_DEFAULTS */
+ || isp1020_load_parameters(host)) {
+ goto fail_uninit;
+ }
+
+ host->this_id = hostdata->host_param.initiator_scsi_id;
+ host->max_sectors = 64;
+
+ if (request_irq(host->irq, do_isp1020_intr_handler, SA_INTERRUPT | SA_SHIRQ,
+ "qlogicisp", host))
+ {
+ printk("qlogicisp : interrupt %d already in use\n",
+ host->irq);
+ goto fail_uninit;
+ }
+
+ isp_outw(0x0, host, PCI_SEMAPHORE);
+ isp_outw(HCCR_CLEAR_RISC_INTR, host, HOST_HCCR);
+ isp1020_enable_irqs(host);
+
+ hosts++;
+ continue;
+
+ fail_uninit:
+ iounmap(hostdata->memaddr);
+ release_region(host->io_port, 0xff);
+ fail_and_unregister:
+ if (hostdata->res_cpu)
+ pci_free_consistent(hostdata->pci_dev,
+ QSIZE(RES_QUEUE_LEN),
+ hostdata->res_cpu,
+ hostdata->res_dma);
+ if (hostdata->req_cpu)
+ pci_free_consistent(hostdata->pci_dev,
+ QSIZE(QLOGICISP_REQ_QUEUE_LEN),
+ hostdata->req_cpu,
+ hostdata->req_dma);
+ scsi_unregister(host);
+ }
+
+ LEAVE("isp1020_detect");
+
+ return hosts;
+}
+
+
+static int isp1020_release(struct Scsi_Host *host)
+{
+ struct isp1020_hostdata *hostdata;
+
+ ENTER("isp1020_release");
+
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ isp_outw(0x0, host, PCI_INTF_CTL);
+ free_irq(host->irq, host);
+
+ iounmap(hostdata->memaddr);
+
+ release_region(host->io_port, 0xff);
+
+ LEAVE("isp1020_release");
+
+ return 0;
+}
+
+
+static const char *isp1020_info(struct Scsi_Host *host)
+{
+ static char buf[80];
+ struct isp1020_hostdata *hostdata;
+
+ ENTER("isp1020_info");
+
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+ sprintf(buf,
+ "QLogic ISP1020 SCSI on PCI bus %02x device %02x irq %d %s base 0x%lx",
+ hostdata->pci_dev->bus->number, hostdata->pci_dev->devfn, host->irq,
+ (hostdata->memaddr ? "MEM" : "I/O"),
+ (hostdata->memaddr ? (unsigned long)hostdata->memaddr : host->io_port));
+
+ LEAVE("isp1020_info");
+
+ return buf;
+}
+
+
+/*
+ * The middle SCSI layer ensures that queuecommand never gets invoked
+ * concurrently with itself or the interrupt handler (though the
+ * interrupt handler may call this routine as part of
+ * request-completion handling).
+ */
+static int isp1020_queuecommand(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
+{
+ int i, n, num_free;
+ u_int in_ptr, out_ptr;
+ struct dataseg * ds;
+ struct scatterlist *sg;
+ struct Command_Entry *cmd;
+ struct Continuation_Entry *cont;
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+ dma_addr_t dma_addr;
+
+ ENTER("isp1020_queuecommand");
+
+ host = Cmnd->device->host;
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+ Cmnd->scsi_done = done;
+
+ DEBUG(isp1020_print_scsi_cmd(Cmnd));
+
+ out_ptr = isp_inw(host, + MBOX4);
+ in_ptr = hostdata->req_in_ptr;
+
+ DEBUG(printk("qlogicisp : request queue depth %d\n",
+ REQ_QUEUE_DEPTH(in_ptr, out_ptr)));
+
+ cmd = (struct Command_Entry *) &hostdata->req_cpu[in_ptr];
+ in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN;
+ if (in_ptr == out_ptr) {
+ printk("qlogicisp : request queue overflow\n");
+ return 1;
+ }
+
+ if (hostdata->send_marker) {
+ struct Marker_Entry *marker;
+
+ TRACE("queue marker", in_ptr, 0);
+
+ DEBUG(printk("qlogicisp : adding marker entry\n"));
+ marker = (struct Marker_Entry *) cmd;
+ memset(marker, 0, sizeof(struct Marker_Entry));
+
+ marker->hdr.entry_type = ENTRY_MARKER;
+ marker->hdr.entry_cnt = 1;
+ marker->modifier = SYNC_ALL;
+
+ hostdata->send_marker = 0;
+
+ if (((in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN) == out_ptr) {
+ isp_outw(in_ptr, host, MBOX4);
+ hostdata->req_in_ptr = in_ptr;
+ printk("qlogicisp : request queue overflow\n");
+ return 1;
+ }
+ cmd = (struct Command_Entry *) &hostdata->req_cpu[in_ptr];
+ in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN;
+ }
+
+ TRACE("queue command", in_ptr, Cmnd);
+
+ memset(cmd, 0, sizeof(struct Command_Entry));
+
+ cmd->hdr.entry_type = ENTRY_COMMAND;
+ cmd->hdr.entry_cnt = 1;
+
+ cmd->target_lun = Cmnd->device->lun;
+ cmd->target_id = Cmnd->device->id;
+ cmd->cdb_length = cpu_to_le16(Cmnd->cmd_len);
+ cmd->control_flags = cpu_to_le16(CFLAG_READ | CFLAG_WRITE);
+ cmd->time_out = cpu_to_le16(30);
+
+ memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len);
+
+ if (Cmnd->use_sg) {
+ int sg_count;
+
+ sg = (struct scatterlist *) Cmnd->request_buffer;
+ ds = cmd->dataseg;
+
+ sg_count = pci_map_sg(hostdata->pci_dev, sg, Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+
+ cmd->segment_cnt = cpu_to_le16(sg_count);
+
+ /* fill in first four sg entries: */
+ n = sg_count;
+ if (n > IOCB_SEGS)
+ n = IOCB_SEGS;
+ for (i = 0; i < n; i++) {
+ dma_addr = sg_dma_address(sg);
+ ds[i].d_base = cpu_to_le32((u32) dma_addr);
+#ifdef CONFIG_QL_ISP_A64
+ ds[i].d_base_hi = cpu_to_le32((u32) (dma_addr>>32));
+#endif /* CONFIG_QL_ISP_A64 */
+ ds[i].d_count = cpu_to_le32(sg_dma_len(sg));
+ ++sg;
+ }
+ sg_count -= IOCB_SEGS;
+
+ while (sg_count > 0) {
+ ++cmd->hdr.entry_cnt;
+ cont = (struct Continuation_Entry *)
+ &hostdata->req_cpu[in_ptr];
+ in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN;
+ if (in_ptr == out_ptr) {
+ printk("isp1020: unexpected request queue "
+ "overflow\n");
+ return 1;
+ }
+ TRACE("queue continuation", in_ptr, 0);
+ cont->hdr.entry_type = ENTRY_CONTINUATION;
+ cont->hdr.entry_cnt = 0;
+ cont->hdr.sys_def_1 = 0;
+ cont->hdr.flags = 0;
+#ifndef CONFIG_QL_ISP_A64
+ cont->reserved = 0;
+#endif
+ ds = cont->dataseg;
+ n = sg_count;
+ if (n > CONTINUATION_SEGS)
+ n = CONTINUATION_SEGS;
+ for (i = 0; i < n; ++i) {
+ dma_addr = sg_dma_address(sg);
+ ds[i].d_base = cpu_to_le32((u32) dma_addr);
+#ifdef CONFIG_QL_ISP_A64
+ ds[i].d_base_hi = cpu_to_le32((u32)(dma_addr>>32));
+#endif /* CONFIG_QL_ISP_A64 */
+ ds[i].d_count = cpu_to_le32(sg_dma_len(sg));
+ ++sg;
+ }
+ sg_count -= n;
+ }
+ } else if (Cmnd->request_bufflen) {
+ /*Cmnd->SCp.ptr = (char *)(unsigned long)*/
+ dma_addr = pci_map_single(hostdata->pci_dev,
+ Cmnd->request_buffer,
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ Cmnd->SCp.ptr = (char *)(unsigned long) dma_addr;
+
+ cmd->dataseg[0].d_base =
+ cpu_to_le32((u32) dma_addr);
+#ifdef CONFIG_QL_ISP_A64
+ cmd->dataseg[0].d_base_hi =
+ cpu_to_le32((u32) (dma_addr>>32));
+#endif /* CONFIG_QL_ISP_A64 */
+ cmd->dataseg[0].d_count =
+ cpu_to_le32((u32)Cmnd->request_bufflen);
+ cmd->segment_cnt = cpu_to_le16(1);
+ } else {
+ cmd->dataseg[0].d_base = 0;
+#ifdef CONFIG_QL_ISP_A64
+ cmd->dataseg[0].d_base_hi = 0;
+#endif /* CONFIG_QL_ISP_A64 */
+ cmd->dataseg[0].d_count = 0;
+ cmd->segment_cnt = cpu_to_le16(1); /* Shouldn't this be 0? */
+ }
+
+ /* Committed, record Scsi_Cmd so we can find it later. */
+ cmd->handle = in_ptr;
+ hostdata->cmd_slots[in_ptr] = Cmnd;
+
+ isp_outw(in_ptr, host, MBOX4);
+ hostdata->req_in_ptr = in_ptr;
+
+ num_free = QLOGICISP_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr);
+ host->can_queue = host->host_busy + num_free;
+ host->sg_tablesize = QLOGICISP_MAX_SG(num_free);
+
+ LEAVE("isp1020_queuecommand");
+
+ return 0;
+}
+
+
+#define ASYNC_EVENT_INTERRUPT 0x01
+
+irqreturn_t do_isp1020_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *host = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ isp1020_intr_handler(irq, dev_id, regs);
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+void isp1020_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ Scsi_Cmnd *Cmnd;
+ struct Status_Entry *sts;
+ struct Scsi_Host *host = dev_id;
+ struct isp1020_hostdata *hostdata;
+ u_int in_ptr, out_ptr;
+ u_short status;
+
+ ENTER_INTR("isp1020_intr_handler");
+
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ DEBUG_INTR(printk("qlogicisp : interrupt on line %d\n", irq));
+
+ if (!(isp_inw(host, PCI_INTF_STS) & 0x04)) {
+ /* spurious interrupts can happen legally */
+ DEBUG_INTR(printk("qlogicisp: got spurious interrupt\n"));
+ return;
+ }
+ in_ptr = isp_inw(host, MBOX5);
+ isp_outw(HCCR_CLEAR_RISC_INTR, host, HOST_HCCR);
+
+ if ((isp_inw(host, PCI_SEMAPHORE) & ASYNC_EVENT_INTERRUPT)) {
+ status = isp_inw(host, MBOX0);
+
+ DEBUG_INTR(printk("qlogicisp : mbox completion status: %x\n",
+ status));
+
+ switch (status) {
+ case ASYNC_SCSI_BUS_RESET:
+ case EXECUTION_TIMEOUT_RESET:
+ hostdata->send_marker = 1;
+ break;
+ case INVALID_COMMAND:
+ case HOST_INTERFACE_ERROR:
+ case COMMAND_ERROR:
+ case COMMAND_PARAM_ERROR:
+ printk("qlogicisp : bad mailbox return status\n");
+ break;
+ }
+ isp_outw(0x0, host, PCI_SEMAPHORE);
+ }
+ out_ptr = hostdata->res_out_ptr;
+
+ DEBUG_INTR(printk("qlogicisp : response queue update\n"));
+ DEBUG_INTR(printk("qlogicisp : response queue depth %d\n",
+ QUEUE_DEPTH(in_ptr, out_ptr, RES_QUEUE_LEN)));
+
+ while (out_ptr != in_ptr) {
+ u_int cmd_slot;
+
+ sts = (struct Status_Entry *) &hostdata->res_cpu[out_ptr];
+ out_ptr = (out_ptr + 1) & RES_QUEUE_LEN;
+
+ cmd_slot = sts->handle;
+ Cmnd = hostdata->cmd_slots[cmd_slot];
+ hostdata->cmd_slots[cmd_slot] = NULL;
+
+ TRACE("done", out_ptr, Cmnd);
+
+ if (le16_to_cpu(sts->completion_status) == CS_RESET_OCCURRED
+ || le16_to_cpu(sts->completion_status) == CS_ABORTED
+ || (le16_to_cpu(sts->status_flags) & STF_BUS_RESET))
+ hostdata->send_marker = 1;
+
+ if (le16_to_cpu(sts->state_flags) & SF_GOT_SENSE)
+ memcpy(Cmnd->sense_buffer, sts->req_sense_data,
+ sizeof(Cmnd->sense_buffer));
+
+ DEBUG_INTR(isp1020_print_status_entry(sts));
+
+ if (sts->hdr.entry_type == ENTRY_STATUS)
+ Cmnd->result = isp1020_return_status(sts);
+ else
+ Cmnd->result = DID_ERROR << 16;
+
+ if (Cmnd->use_sg)
+ pci_unmap_sg(hostdata->pci_dev,
+ (struct scatterlist *)Cmnd->buffer,
+ Cmnd->use_sg,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+ else if (Cmnd->request_bufflen)
+ pci_unmap_single(hostdata->pci_dev,
+#ifdef CONFIG_QL_ISP_A64
+ (dma_addr_t)((long)Cmnd->SCp.ptr),
+#else
+ (u32)((long)Cmnd->SCp.ptr),
+#endif
+ Cmnd->request_bufflen,
+ scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
+
+ isp_outw(out_ptr, host, MBOX5);
+ (*Cmnd->scsi_done)(Cmnd);
+ }
+ hostdata->res_out_ptr = out_ptr;
+
+ LEAVE_INTR("isp1020_intr_handler");
+}
+
+
+static int isp1020_return_status(struct Status_Entry *sts)
+{
+ int host_status = DID_ERROR;
+#if DEBUG_ISP1020_INTR
+ static char *reason[] = {
+ "DID_OK",
+ "DID_NO_CONNECT",
+ "DID_BUS_BUSY",
+ "DID_TIME_OUT",
+ "DID_BAD_TARGET",
+ "DID_ABORT",
+ "DID_PARITY",
+ "DID_ERROR",
+ "DID_RESET",
+ "DID_BAD_INTR"
+ };
+#endif /* DEBUG_ISP1020_INTR */
+
+ ENTER("isp1020_return_status");
+
+ DEBUG(printk("qlogicisp : completion status = 0x%04x\n",
+ le16_to_cpu(sts->completion_status)));
+
+ switch(le16_to_cpu(sts->completion_status)) {
+ case CS_COMPLETE:
+ host_status = DID_OK;
+ break;
+ case CS_INCOMPLETE:
+ if (!(le16_to_cpu(sts->state_flags) & SF_GOT_BUS))
+ host_status = DID_NO_CONNECT;
+ else if (!(le16_to_cpu(sts->state_flags) & SF_GOT_TARGET))
+ host_status = DID_BAD_TARGET;
+ else if (!(le16_to_cpu(sts->state_flags) & SF_SENT_CDB))
+ host_status = DID_ERROR;
+ else if (!(le16_to_cpu(sts->state_flags) & SF_TRANSFERRED_DATA))
+ host_status = DID_ERROR;
+ else if (!(le16_to_cpu(sts->state_flags) & SF_GOT_STATUS))
+ host_status = DID_ERROR;
+ else if (!(le16_to_cpu(sts->state_flags) & SF_GOT_SENSE))
+ host_status = DID_ERROR;
+ break;
+ case CS_DMA_ERROR:
+ case CS_TRANSPORT_ERROR:
+ host_status = DID_ERROR;
+ break;
+ case CS_RESET_OCCURRED:
+ host_status = DID_RESET;
+ break;
+ case CS_ABORTED:
+ host_status = DID_ABORT;
+ break;
+ case CS_TIMEOUT:
+ host_status = DID_TIME_OUT;
+ break;
+ case CS_DATA_OVERRUN:
+ case CS_COMMAND_OVERRUN:
+ case CS_STATUS_OVERRUN:
+ case CS_BAD_MESSAGE:
+ case CS_NO_MESSAGE_OUT:
+ case CS_EXT_ID_FAILED:
+ case CS_IDE_MSG_FAILED:
+ case CS_ABORT_MSG_FAILED:
+ case CS_NOP_MSG_FAILED:
+ case CS_PARITY_ERROR_MSG_FAILED:
+ case CS_DEVICE_RESET_MSG_FAILED:
+ case CS_ID_MSG_FAILED:
+ case CS_UNEXP_BUS_FREE:
+ host_status = DID_ERROR;
+ break;
+ case CS_DATA_UNDERRUN:
+ host_status = DID_OK;
+ break;
+ default:
+ printk("qlogicisp : unknown completion status 0x%04x\n",
+ le16_to_cpu(sts->completion_status));
+ host_status = DID_ERROR;
+ break;
+ }
+
+ DEBUG_INTR(printk("qlogicisp : host status (%s) scsi status %x\n",
+ reason[host_status], le16_to_cpu(sts->scsi_status)));
+
+ LEAVE("isp1020_return_status");
+
+ return (le16_to_cpu(sts->scsi_status) & STATUS_MASK) | (host_status << 16);
+}
+
+
+static int isp1020_biosparam(struct scsi_device *sdev, struct block_device *n,
+ sector_t capacity, int ip[])
+{
+ int size = capacity;
+
+ ENTER("isp1020_biosparam");
+
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ if (ip[2] > 1024) {
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / (ip[0] * ip[1]);
+#if 0
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+#endif
+ }
+
+ LEAVE("isp1020_biosparam");
+
+ return 0;
+}
+
+
+static int isp1020_reset_hardware(struct Scsi_Host *host)
+{
+ u_short param[6];
+ int loop_count;
+
+ ENTER("isp1020_reset_hardware");
+
+ isp_outw(ISP_RESET, host, PCI_INTF_CTL);
+ udelay(100);
+ isp_outw(HCCR_RESET, host, HOST_HCCR);
+ udelay(100);
+ isp_outw(HCCR_RELEASE, host, HOST_HCCR);
+ isp_outw(HCCR_BIOS_DISABLE, host, HOST_HCCR);
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && isp_inw(host, HOST_HCCR) == RISC_BUSY) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk("qlogicisp: reset_hardware loop timeout\n");
+
+ isp_outw(0, host, ISP_CFG1);
+
+#if DEBUG_ISP1020
+ printk("qlogicisp : mbox 0 0x%04x \n", isp_inw(host, MBOX0));
+ printk("qlogicisp : mbox 1 0x%04x \n", isp_inw(host, MBOX1));
+ printk("qlogicisp : mbox 2 0x%04x \n", isp_inw(host, MBOX2));
+ printk("qlogicisp : mbox 3 0x%04x \n", isp_inw(host, MBOX3));
+ printk("qlogicisp : mbox 4 0x%04x \n", isp_inw(host, MBOX4));
+ printk("qlogicisp : mbox 5 0x%04x \n", isp_inw(host, MBOX5));
+#endif /* DEBUG_ISP1020 */
+
+ param[0] = MBOX_NO_OP;
+ isp1020_mbox_command(host, param);
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : NOP test failed\n");
+ return 1;
+ }
+
+ DEBUG(printk("qlogicisp : loading risc ram\n"));
+
+#if RELOAD_FIRMWARE
+ for (loop_count = 0; loop_count < risc_code_length01; loop_count++) {
+ param[0] = MBOX_WRITE_RAM_WORD;
+ param[1] = risc_code_addr01 + loop_count;
+ param[2] = risc_code01[loop_count];
+ isp1020_mbox_command(host, param);
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : firmware load failure at %d\n",
+ loop_count);
+ return 1;
+ }
+ }
+#endif /* RELOAD_FIRMWARE */
+
+ DEBUG(printk("qlogicisp : verifying checksum\n"));
+
+ param[0] = MBOX_VERIFY_CHECKSUM;
+ param[1] = risc_code_addr01;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : ram checksum failure\n");
+ return 1;
+ }
+
+ DEBUG(printk("qlogicisp : executing firmware\n"));
+
+ param[0] = MBOX_EXEC_FIRMWARE;
+ param[1] = risc_code_addr01;
+
+ isp1020_mbox_command(host, param);
+
+ param[0] = MBOX_ABOUT_FIRMWARE;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : about firmware failure\n");
+ return 1;
+ }
+
+ DEBUG(printk("qlogicisp : firmware major revision %d\n", param[1]));
+ DEBUG(printk("qlogicisp : firmware minor revision %d\n", param[2]));
+
+ LEAVE("isp1020_reset_hardware");
+
+ return 0;
+}
+
+
+static int isp1020_init(struct Scsi_Host *sh)
+{
+ u_long io_base, mem_base, io_flags, mem_flags;
+ struct isp1020_hostdata *hostdata;
+ u_char revision;
+ u_int irq;
+ u_short command;
+ struct pci_dev *pdev;
+
+ ENTER("isp1020_init");
+
+ hostdata = (struct isp1020_hostdata *) sh->hostdata;
+ pdev = hostdata->pci_dev;
+
+ if (pci_read_config_word(pdev, PCI_COMMAND, &command)
+ || pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision))
+ {
+ printk("qlogicisp : error reading PCI configuration\n");
+ return 1;
+ }
+
+ io_base = pci_resource_start(pdev, 0);
+ mem_base = pci_resource_start(pdev, 1);
+ io_flags = pci_resource_flags(pdev, 0);
+ mem_flags = pci_resource_flags(pdev, 1);
+ irq = pdev->irq;
+
+ if (pdev->vendor != PCI_VENDOR_ID_QLOGIC) {
+ printk("qlogicisp : 0x%04x is not QLogic vendor ID\n",
+ pdev->vendor);
+ return 1;
+ }
+
+ if (pdev->device != PCI_DEVICE_ID_QLOGIC_ISP1020) {
+ printk("qlogicisp : 0x%04x does not match ISP1020 device id\n",
+ pdev->device);
+ return 1;
+ }
+
+#ifdef __alpha__
+ /* Force ALPHA to use bus I/O and not bus MEM.
+ This is to avoid having to use HAE_MEM registers,
+ which is broken on some platforms and with SMP. */
+ command &= ~PCI_COMMAND_MEMORY;
+#endif
+
+ sh->io_port = io_base;
+
+ if (!request_region(sh->io_port, 0xff, "qlogicisp")) {
+ printk("qlogicisp : i/o region 0x%lx-0x%lx already "
+ "in use\n",
+ sh->io_port, sh->io_port + 0xff);
+ return 1;
+ }
+
+ if ((command & PCI_COMMAND_MEMORY) &&
+ ((mem_flags & 1) == 0)) {
+ hostdata->memaddr = ioremap(mem_base, PAGE_SIZE);
+ if (!hostdata->memaddr) {
+ printk("qlogicisp : i/o remapping failed.\n");
+ goto out_release;
+ }
+ } else {
+ if (command & PCI_COMMAND_IO && (io_flags & 3) != 1) {
+ printk("qlogicisp : i/o mapping is disabled\n");
+ goto out_release;
+ }
+ hostdata->memaddr = NULL; /* zero to signify no i/o mapping */
+ mem_base = 0;
+ }
+
+ if (revision != ISP1020_REV_ID)
+ printk("qlogicisp : new isp1020 revision ID (%d)\n", revision);
+
+ if (isp_inw(sh, PCI_ID_LOW) != PCI_VENDOR_ID_QLOGIC
+ || isp_inw(sh, PCI_ID_HIGH) != PCI_DEVICE_ID_QLOGIC_ISP1020)
+ {
+ printk("qlogicisp : can't decode %s address space 0x%lx\n",
+ (io_base ? "I/O" : "MEM"),
+ (io_base ? io_base : mem_base));
+ goto out_unmap;
+ }
+
+ hostdata->revision = revision;
+
+ sh->irq = irq;
+ sh->max_id = MAX_TARGETS;
+ sh->max_lun = MAX_LUNS;
+
+ hostdata->res_cpu = pci_alloc_consistent(hostdata->pci_dev,
+ QSIZE(RES_QUEUE_LEN),
+ &hostdata->res_dma);
+ if (hostdata->res_cpu == NULL) {
+ printk("qlogicisp : can't allocate response queue\n");
+ goto out_unmap;
+ }
+
+ hostdata->req_cpu = pci_alloc_consistent(hostdata->pci_dev,
+ QSIZE(QLOGICISP_REQ_QUEUE_LEN),
+ &hostdata->req_dma);
+ if (hostdata->req_cpu == NULL) {
+ pci_free_consistent(hostdata->pci_dev,
+ QSIZE(RES_QUEUE_LEN),
+ hostdata->res_cpu,
+ hostdata->res_dma);
+ printk("qlogicisp : can't allocate request queue\n");
+ goto out_unmap;
+ }
+
+ pci_set_master(pdev);
+
+ LEAVE("isp1020_init");
+
+ return 0;
+
+out_unmap:
+ iounmap(hostdata->memaddr);
+out_release:
+ release_region(sh->io_port, 0xff);
+ return 1;
+}
+
+
+#if USE_NVRAM_DEFAULTS
+
+static int isp1020_get_defaults(struct Scsi_Host *host)
+{
+ int i;
+ u_short value;
+ struct isp1020_hostdata *hostdata =
+ (struct isp1020_hostdata *) host->hostdata;
+
+ ENTER("isp1020_get_defaults");
+
+ if (!isp1020_verify_nvram(host)) {
+ printk("qlogicisp : nvram checksum failure\n");
+ printk("qlogicisp : attempting to use default parameters\n");
+ return isp1020_set_defaults(host);
+ }
+
+ value = isp1020_read_nvram_word(host, 2);
+ hostdata->host_param.fifo_threshold = (value >> 8) & 0x03;
+ hostdata->host_param.host_adapter_enable = (value >> 11) & 0x01;
+ hostdata->host_param.initiator_scsi_id = (value >> 12) & 0x0f;
+
+ value = isp1020_read_nvram_word(host, 3);
+ hostdata->host_param.bus_reset_delay = value & 0xff;
+ hostdata->host_param.retry_count = value >> 8;
+
+ value = isp1020_read_nvram_word(host, 4);
+ hostdata->host_param.retry_delay = value & 0xff;
+ hostdata->host_param.async_data_setup_time = (value >> 8) & 0x0f;
+ hostdata->host_param.req_ack_active_negation = (value >> 12) & 0x01;
+ hostdata->host_param.data_line_active_negation = (value >> 13) & 0x01;
+ hostdata->host_param.data_dma_burst_enable = (value >> 14) & 0x01;
+ hostdata->host_param.command_dma_burst_enable = (value >> 15);
+
+ value = isp1020_read_nvram_word(host, 5);
+ hostdata->host_param.tag_aging = value & 0xff;
+
+ value = isp1020_read_nvram_word(host, 6);
+ hostdata->host_param.selection_timeout = value & 0xffff;
+
+ value = isp1020_read_nvram_word(host, 7);
+ hostdata->host_param.max_queue_depth = value & 0xffff;
+
+#if DEBUG_ISP1020_SETUP
+ printk("qlogicisp : fifo threshold=%d\n",
+ hostdata->host_param.fifo_threshold);
+ printk("qlogicisp : initiator scsi id=%d\n",
+ hostdata->host_param.initiator_scsi_id);
+ printk("qlogicisp : bus reset delay=%d\n",
+ hostdata->host_param.bus_reset_delay);
+ printk("qlogicisp : retry count=%d\n",
+ hostdata->host_param.retry_count);
+ printk("qlogicisp : retry delay=%d\n",
+ hostdata->host_param.retry_delay);
+ printk("qlogicisp : async data setup time=%d\n",
+ hostdata->host_param.async_data_setup_time);
+ printk("qlogicisp : req/ack active negation=%d\n",
+ hostdata->host_param.req_ack_active_negation);
+ printk("qlogicisp : data line active negation=%d\n",
+ hostdata->host_param.data_line_active_negation);
+ printk("qlogicisp : data DMA burst enable=%d\n",
+ hostdata->host_param.data_dma_burst_enable);
+ printk("qlogicisp : command DMA burst enable=%d\n",
+ hostdata->host_param.command_dma_burst_enable);
+ printk("qlogicisp : tag age limit=%d\n",
+ hostdata->host_param.tag_aging);
+ printk("qlogicisp : selection timeout limit=%d\n",
+ hostdata->host_param.selection_timeout);
+ printk("qlogicisp : max queue depth=%d\n",
+ hostdata->host_param.max_queue_depth);
+#endif /* DEBUG_ISP1020_SETUP */
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+
+ value = isp1020_read_nvram_word(host, 14 + i * 3);
+ hostdata->dev_param[i].device_flags = value & 0xff;
+ hostdata->dev_param[i].execution_throttle = value >> 8;
+
+ value = isp1020_read_nvram_word(host, 15 + i * 3);
+ hostdata->dev_param[i].synchronous_period = value & 0xff;
+ hostdata->dev_param[i].synchronous_offset = (value >> 8) & 0x0f;
+ hostdata->dev_param[i].device_enable = (value >> 12) & 0x01;
+
+#if DEBUG_ISP1020_SETUP
+ printk("qlogicisp : target 0x%02x\n", i);
+ printk("qlogicisp : device flags=0x%02x\n",
+ hostdata->dev_param[i].device_flags);
+ printk("qlogicisp : execution throttle=%d\n",
+ hostdata->dev_param[i].execution_throttle);
+ printk("qlogicisp : synchronous period=%d\n",
+ hostdata->dev_param[i].synchronous_period);
+ printk("qlogicisp : synchronous offset=%d\n",
+ hostdata->dev_param[i].synchronous_offset);
+ printk("qlogicisp : device enable=%d\n",
+ hostdata->dev_param[i].device_enable);
+#endif /* DEBUG_ISP1020_SETUP */
+ }
+
+ LEAVE("isp1020_get_defaults");
+
+ return 0;
+}
+
+
+#define ISP1020_NVRAM_LEN 0x40
+#define ISP1020_NVRAM_SIG1 0x5349
+#define ISP1020_NVRAM_SIG2 0x2050
+
+static int isp1020_verify_nvram(struct Scsi_Host *host)
+{
+ int i;
+ u_short value;
+ u_char checksum = 0;
+
+ for (i = 0; i < ISP1020_NVRAM_LEN; i++) {
+ value = isp1020_read_nvram_word(host, i);
+
+ switch (i) {
+ case 0:
+ if (value != ISP1020_NVRAM_SIG1) return 0;
+ break;
+ case 1:
+ if (value != ISP1020_NVRAM_SIG2) return 0;
+ break;
+ case 2:
+ if ((value & 0xff) != 0x02) return 0;
+ break;
+ }
+ checksum += value & 0xff;
+ checksum += value >> 8;
+ }
+
+ return (checksum == 0);
+}
+
+#define NVRAM_DELAY() udelay(2) /* 2 microsecond delay */
+
+
+u_short isp1020_read_nvram_word(struct Scsi_Host *host, u_short byte)
+{
+ int i;
+ u_short value, output, input;
+
+ byte &= 0x3f; byte |= 0x0180;
+
+ for (i = 8; i >= 0; i--) {
+ output = ((byte >> i) & 0x1) ? 0x4 : 0x0;
+ isp_outw(output | 0x2, host, PCI_NVRAM); NVRAM_DELAY();
+ isp_outw(output | 0x3, host, PCI_NVRAM); NVRAM_DELAY();
+ isp_outw(output | 0x2, host, PCI_NVRAM); NVRAM_DELAY();
+ }
+
+ for (i = 0xf, value = 0; i >= 0; i--) {
+ value <<= 1;
+ isp_outw(0x3, host, PCI_NVRAM); NVRAM_DELAY();
+ input = isp_inw(host, PCI_NVRAM); NVRAM_DELAY();
+ isp_outw(0x2, host, PCI_NVRAM); NVRAM_DELAY();
+ if (input & 0x8) value |= 1;
+ }
+
+ isp_outw(0x0, host, PCI_NVRAM); NVRAM_DELAY();
+
+ return value;
+}
+
+#endif /* USE_NVRAM_DEFAULTS */
+
+
+static int isp1020_set_defaults(struct Scsi_Host *host)
+{
+ struct isp1020_hostdata *hostdata =
+ (struct isp1020_hostdata *) host->hostdata;
+ int i;
+
+ ENTER("isp1020_set_defaults");
+
+ hostdata->host_param.fifo_threshold = 2;
+ hostdata->host_param.host_adapter_enable = 1;
+ hostdata->host_param.initiator_scsi_id = 7;
+ hostdata->host_param.bus_reset_delay = 3;
+ hostdata->host_param.retry_count = 0;
+ hostdata->host_param.retry_delay = 1;
+ hostdata->host_param.async_data_setup_time = 6;
+ hostdata->host_param.req_ack_active_negation = 1;
+ hostdata->host_param.data_line_active_negation = 1;
+ hostdata->host_param.data_dma_burst_enable = 1;
+ hostdata->host_param.command_dma_burst_enable = 1;
+ hostdata->host_param.tag_aging = 8;
+ hostdata->host_param.selection_timeout = 250;
+ hostdata->host_param.max_queue_depth = 256;
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+ hostdata->dev_param[i].device_flags = 0xfd;
+ hostdata->dev_param[i].execution_throttle = 16;
+ hostdata->dev_param[i].synchronous_period = 25;
+ hostdata->dev_param[i].synchronous_offset = 12;
+ hostdata->dev_param[i].device_enable = 1;
+ }
+
+ LEAVE("isp1020_set_defaults");
+
+ return 0;
+}
+
+
+static int isp1020_load_parameters(struct Scsi_Host *host)
+{
+ int i, k;
+#ifdef CONFIG_QL_ISP_A64
+ u_long queue_addr;
+ u_short param[8];
+#else
+ u_int queue_addr;
+ u_short param[6];
+#endif
+ u_short isp_cfg1, hwrev;
+ struct isp1020_hostdata *hostdata =
+ (struct isp1020_hostdata *) host->hostdata;
+
+ ENTER("isp1020_load_parameters");
+
+ hwrev = isp_inw(host, ISP_CFG0) & ISP_CFG0_HWMSK;
+ isp_cfg1 = ISP_CFG1_F64 | ISP_CFG1_BENAB;
+ if (hwrev == ISP_CFG0_1040A) {
+ /* Busted fifo, says mjacob. */
+ isp_cfg1 &= ISP_CFG1_BENAB;
+ }
+
+ isp_outw(isp_inw(host, ISP_CFG1) | isp_cfg1, host, ISP_CFG1);
+ isp_outw(isp_inw(host, CDMA_CONF) | DMA_CONF_BENAB, host, CDMA_CONF);
+ isp_outw(isp_inw(host, DDMA_CONF) | DMA_CONF_BENAB, host, DDMA_CONF);
+
+ param[0] = MBOX_SET_INIT_SCSI_ID;
+ param[1] = hostdata->host_param.initiator_scsi_id;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set initiator id failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_RETRY_COUNT;
+ param[1] = hostdata->host_param.retry_count;
+ param[2] = hostdata->host_param.retry_delay;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set retry count failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME;
+ param[1] = hostdata->host_param.async_data_setup_time;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : async data setup time failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_ACTIVE_NEG_STATE;
+ param[1] = (hostdata->host_param.req_ack_active_negation << 4)
+ | (hostdata->host_param.data_line_active_negation << 5);
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set active negation state failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_PCI_CONTROL_PARAMS;
+ param[1] = hostdata->host_param.data_dma_burst_enable << 1;
+ param[2] = hostdata->host_param.command_dma_burst_enable << 1;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set pci control parameter failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_TAG_AGE_LIMIT;
+ param[1] = hostdata->host_param.tag_aging;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set tag age limit failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_SELECT_TIMEOUT;
+ param[1] = hostdata->host_param.selection_timeout;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set selection timeout failure\n");
+ return 1;
+ }
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+
+ if (!hostdata->dev_param[i].device_enable)
+ continue;
+
+ param[0] = MBOX_SET_TARGET_PARAMS;
+ param[1] = i << 8;
+ param[2] = hostdata->dev_param[i].device_flags << 8;
+ param[3] = (hostdata->dev_param[i].synchronous_offset << 8)
+ | hostdata->dev_param[i].synchronous_period;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set target parameter failure\n");
+ return 1;
+ }
+
+ for (k = 0; k < MAX_LUNS; k++) {
+
+ param[0] = MBOX_SET_DEV_QUEUE_PARAMS;
+ param[1] = (i << 8) | k;
+ param[2] = hostdata->host_param.max_queue_depth;
+ param[3] = hostdata->dev_param[i].execution_throttle;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set device queue "
+ "parameter failure\n");
+ return 1;
+ }
+ }
+ }
+
+ queue_addr = hostdata->res_dma;
+#ifdef CONFIG_QL_ISP_A64
+ param[0] = MBOX_CMD_INIT_RESPONSE_QUEUE_64;
+#else
+ param[0] = MBOX_INIT_RES_QUEUE;
+#endif
+ param[1] = RES_QUEUE_LEN + 1;
+ param[2] = (u_short) (queue_addr >> 16);
+ param[3] = (u_short) (queue_addr & 0xffff);
+ param[4] = 0;
+ param[5] = 0;
+#ifdef CONFIG_QL_ISP_A64
+ param[6] = (u_short) (queue_addr >> 48);
+ param[7] = (u_short) (queue_addr >> 32);
+#endif
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set response queue failure\n");
+ return 1;
+ }
+
+ queue_addr = hostdata->req_dma;
+#ifdef CONFIG_QL_ISP_A64
+ param[0] = MBOX_CMD_INIT_REQUEST_QUEUE_64;
+#else
+ param[0] = MBOX_INIT_REQ_QUEUE;
+#endif
+ param[1] = QLOGICISP_REQ_QUEUE_LEN + 1;
+ param[2] = (u_short) (queue_addr >> 16);
+ param[3] = (u_short) (queue_addr & 0xffff);
+ param[4] = 0;
+
+#ifdef CONFIG_QL_ISP_A64
+ param[5] = 0;
+ param[6] = (u_short) (queue_addr >> 48);
+ param[7] = (u_short) (queue_addr >> 32);
+#endif
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : set request queue failure\n");
+ return 1;
+ }
+
+ LEAVE("isp1020_load_parameters");
+
+ return 0;
+}
+
+
+/*
+ * currently, this is only called during initialization or abort/reset,
+ * at which times interrupts are disabled, so polling is OK, I guess...
+ */
+static int isp1020_mbox_command(struct Scsi_Host *host, u_short param[])
+{
+ int loop_count;
+
+ if (mbox_param[param[0]] == 0)
+ return 1;
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && isp_inw(host, HOST_HCCR) & 0x0080) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk("qlogicisp: mbox_command loop timeout #1\n");
+
+ switch(mbox_param[param[0]] >> 4) {
+ case 8: isp_outw(param[7], host, MBOX7);
+ case 7: isp_outw(param[6], host, MBOX6);
+ case 6: isp_outw(param[5], host, MBOX5);
+ case 5: isp_outw(param[4], host, MBOX4);
+ case 4: isp_outw(param[3], host, MBOX3);
+ case 3: isp_outw(param[2], host, MBOX2);
+ case 2: isp_outw(param[1], host, MBOX1);
+ case 1: isp_outw(param[0], host, MBOX0);
+ }
+
+ isp_outw(0x0, host, PCI_SEMAPHORE);
+ isp_outw(HCCR_CLEAR_RISC_INTR, host, HOST_HCCR);
+ isp_outw(HCCR_SET_HOST_INTR, host, HOST_HCCR);
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && !(isp_inw(host, PCI_INTF_STS) & 0x04)) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk("qlogicisp: mbox_command loop timeout #2\n");
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && isp_inw(host, MBOX0) == 0x04) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk("qlogicisp: mbox_command loop timeout #3\n");
+
+ switch(mbox_param[param[0]] & 0xf) {
+ case 8: param[7] = isp_inw(host, MBOX7);
+ case 7: param[6] = isp_inw(host, MBOX6);
+ case 6: param[5] = isp_inw(host, MBOX5);
+ case 5: param[4] = isp_inw(host, MBOX4);
+ case 4: param[3] = isp_inw(host, MBOX3);
+ case 3: param[2] = isp_inw(host, MBOX2);
+ case 2: param[1] = isp_inw(host, MBOX1);
+ case 1: param[0] = isp_inw(host, MBOX0);
+ }
+
+ isp_outw(0x0, host, PCI_SEMAPHORE);
+ isp_outw(HCCR_CLEAR_RISC_INTR, host, HOST_HCCR);
+
+ return 0;
+}
+
+
+#if DEBUG_ISP1020_INTR
+
+void isp1020_print_status_entry(struct Status_Entry *status)
+{
+ int i;
+
+ printk("qlogicisp : entry count = 0x%02x, type = 0x%02x, flags = 0x%02x\n",
+ status->hdr.entry_cnt, status->hdr.entry_type, status->hdr.flags);
+ printk("qlogicisp : scsi status = 0x%04x, completion status = 0x%04x\n",
+ le16_to_cpu(status->scsi_status), le16_to_cpu(status->completion_status));
+ printk("qlogicisp : state flags = 0x%04x, status flags = 0x%04x\n",
+ le16_to_cpu(status->state_flags), le16_to_cpu(status->status_flags));
+ printk("qlogicisp : time = 0x%04x, request sense length = 0x%04x\n",
+ le16_to_cpu(status->time), le16_to_cpu(status->req_sense_len));
+ printk("qlogicisp : residual transfer length = 0x%08x\n",
+ le32_to_cpu(status->residual));
+
+ for (i = 0; i < le16_to_cpu(status->req_sense_len); i++)
+ printk("qlogicisp : sense data = 0x%02x\n", status->req_sense_data[i]);
+}
+
+#endif /* DEBUG_ISP1020_INTR */
+
+
+#if DEBUG_ISP1020
+
+void isp1020_print_scsi_cmd(Scsi_Cmnd *cmd)
+{
+ int i;
+
+ printk("qlogicisp : target = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
+ cmd->target, cmd->lun, cmd->cmd_len);
+ printk("qlogicisp : command = ");
+ for (i = 0; i < cmd->cmd_len; i++)
+ printk("0x%02x ", cmd->cmnd[i]);
+ printk("\n");
+}
+
+#endif /* DEBUG_ISP1020 */
+
+MODULE_LICENSE("GPL");
+
+static Scsi_Host_Template driver_template = {
+ .detect = isp1020_detect,
+ .release = isp1020_release,
+ .info = isp1020_info,
+ .queuecommand = isp1020_queuecommand,
+ .bios_param = isp1020_biosparam,
+ .can_queue = QLOGICISP_REQ_QUEUE_LEN,
+ .this_id = -1,
+ .sg_tablesize = QLOGICISP_MAX_SG(QLOGICISP_REQ_QUEUE_LEN),
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/qlogicisp_asm.c b/drivers/scsi/qlogicisp_asm.c
new file mode 100644
index 000000000000..9ea4beca4ac5
--- /dev/null
+++ b/drivers/scsi/qlogicisp_asm.c
@@ -0,0 +1,2034 @@
+/*
+ * Firmware Version 7.63.00 (12:07 Jan 27, 1999)
+ */
+static const unsigned short risc_code_version = 7*1024+63;
+
+static const unsigned short risc_code_addr01 = 0x1000 ;
+
+#if RELOAD_FIRMWARE
+
+static const unsigned short risc_code01[] = {
+ 0x0078, 0x103a, 0x0000, 0x3f14, 0x0000, 0x2043, 0x4f50, 0x5952,
+ 0x4947, 0x4854, 0x2031, 0x3939, 0x3520, 0x514c, 0x4f47, 0x4943,
+ 0x2043, 0x4f52, 0x504f, 0x5241, 0x5449, 0x4f4e, 0x2049, 0x5350,
+ 0x3130, 0x3230, 0x2049, 0x2f54, 0x2046, 0x6972, 0x6d77, 0x6172,
+ 0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030, 0x372e, 0x3633,
+ 0x2020, 0x2043, 0x7573, 0x746f, 0x6d65, 0x7220, 0x4e6f, 0x2e20,
+ 0x3030, 0x2050, 0x726f, 0x6475, 0x6374, 0x204e, 0x6f2e, 0x2020,
+ 0x3031, 0x2024, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x0048,
+ 0x1045, 0x0038, 0x104b, 0x0078, 0x1047, 0x0028, 0x104b, 0x20b9,
+ 0x1212, 0x0078, 0x104d, 0x20b9, 0x2222, 0x20c1, 0x0008, 0x2071,
+ 0x0010, 0x70c3, 0x0004, 0x20c9, 0x76ff, 0x2089, 0x1186, 0x70c7,
+ 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0007, 0x3f00,
+ 0x70d6, 0x20c1, 0x0008, 0x2019, 0x0000, 0x2009, 0xfeff, 0x2100,
+ 0x200b, 0xa5a5, 0xa1ec, 0x7fff, 0x2d64, 0x206b, 0x0a0a, 0xaddc,
+ 0x3fff, 0x2b54, 0x205b, 0x5050, 0x2114, 0xa286, 0xa5a5, 0x0040,
+ 0x10bf, 0xa386, 0x000f, 0x0040, 0x1085, 0x2c6a, 0x2a5a, 0x20c1,
+ 0x0000, 0x2019, 0x000f, 0x0078, 0x1065, 0x2c6a, 0x2a5a, 0x20c1,
+ 0x0008, 0x2009, 0x7fff, 0x2148, 0x2944, 0x204b, 0x0a0a, 0xa9bc,
+ 0x3fff, 0x2734, 0x203b, 0x5050, 0x2114, 0xa286, 0x0a0a, 0x0040,
+ 0x10a9, 0x284a, 0x263a, 0x20c1, 0x0004, 0x2009, 0x3fff, 0x2134,
+ 0x200b, 0x5050, 0x2114, 0xa286, 0x5050, 0x0040, 0x10aa, 0x0078,
+ 0x118e, 0x284a, 0x263a, 0x98c0, 0xa188, 0x1000, 0x212c, 0x200b,
+ 0xa5a5, 0x2114, 0xa286, 0xa5a5, 0x0040, 0x10bc, 0x250a, 0xa18a,
+ 0x1000, 0x98c1, 0x0078, 0x10c1, 0x250a, 0x0078, 0x10c1, 0x2c6a,
+ 0x2a5a, 0x2130, 0xa18a, 0x0040, 0x2128, 0xa1a2, 0x5000, 0x8424,
+ 0x8424, 0x8424, 0x8424, 0x8424, 0x8424, 0xa192, 0x7700, 0x2009,
+ 0x0000, 0x2001, 0x0031, 0x1078, 0x1c9d, 0x2218, 0x2079, 0x5000,
+ 0x2fa0, 0x2408, 0x2011, 0x0000, 0x20a9, 0x0040, 0x42a4, 0x8109,
+ 0x00c0, 0x10dc, 0x7ef2, 0x8528, 0x7de6, 0x7cea, 0x7bee, 0x7883,
+ 0x0000, 0x2031, 0x0030, 0x78cf, 0x0101, 0x780b, 0x0002, 0x780f,
+ 0x0002, 0x784f, 0x0003, 0x2069, 0x5040, 0x2001, 0x04fd, 0x2004,
+ 0xa082, 0x0005, 0x0048, 0x1104, 0x0038, 0x1100, 0x0078, 0x1108,
+ 0x681b, 0x003c, 0x0078, 0x110a, 0x00a8, 0x1108, 0x681b, 0x003c,
+ 0x681b, 0x0028, 0x6807, 0x0007, 0x680b, 0x00fa, 0x680f, 0x0008,
+ 0x6813, 0x0005, 0x6823, 0x0000, 0x6827, 0x0006, 0x6817, 0x0008,
+ 0x682b, 0x0000, 0x681f, 0x0019, 0x2069, 0x5280, 0x2011, 0x0020,
+ 0x2009, 0x0010, 0x680b, 0x080c, 0x680f, 0x0019, 0x6803, 0xfd00,
+ 0x6807, 0x0018, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, 0xa290, 0x0004,
+ 0x8109, 0x00c0, 0x1122, 0x2069, 0x5300, 0x2009, 0x0002, 0x20a9,
+ 0x0100, 0x6837, 0x0000, 0x680b, 0x0040, 0x7bf0, 0xa386, 0xfeff,
+ 0x00c0, 0x1148, 0x6817, 0x0100, 0x681f, 0x0064, 0x0078, 0x114c,
+ 0x6817, 0x0064, 0x681f, 0x0002, 0xade8, 0x0010, 0x0070, 0x1152,
+ 0x0078, 0x1139, 0x8109, 0x00c0, 0x1137, 0x1078, 0x21e9, 0x1078,
+ 0x46e9, 0x1078, 0x1946, 0x1078, 0x4bdf, 0x3200, 0xa085, 0x000d,
+ 0x2090, 0x70c3, 0x0000, 0x0090, 0x116c, 0x70c0, 0xa086, 0x0002,
+ 0x00c0, 0x116c, 0x1078, 0x1284, 0x1078, 0x1196, 0x78cc, 0xa005,
+ 0x00c0, 0x117a, 0x1078, 0x1cc6, 0x0010, 0x1180, 0x0068, 0x1180,
+ 0x1078, 0x20c8, 0x0010, 0x1180, 0x0068, 0x1180, 0x1078, 0x1a2b,
+ 0x00e0, 0x116c, 0x1078, 0x4a66, 0x0078, 0x116c, 0x118e, 0x1190,
+ 0x23ea, 0x23ea, 0x476a, 0x476a, 0x23ea, 0x23ea, 0x0078, 0x118e,
+ 0x0078, 0x1190, 0x0078, 0x1192, 0x0078, 0x1194, 0x0068, 0x1201,
+ 0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1201, 0x7814,
+ 0xa005, 0x00c0, 0x11a7, 0x0010, 0x1202, 0x0078, 0x1201, 0x2009,
+ 0x505b, 0x2104, 0xa005, 0x00c0, 0x1201, 0x2009, 0x5064, 0x200b,
+ 0x0000, 0x7914, 0xa186, 0x0042, 0x00c0, 0x11cc, 0x7816, 0x2009,
+ 0x5062, 0x2164, 0x200b, 0x0000, 0x6018, 0x70c6, 0x6014, 0x70ca,
+ 0x611c, 0xa18c, 0xff00, 0x6020, 0xa084, 0x00ff, 0xa105, 0x70ce,
+ 0x1078, 0x192b, 0x0078, 0x11ff, 0x7814, 0xa086, 0x0018, 0x00c0,
+ 0x11d3, 0x1078, 0x165a, 0x7817, 0x0000, 0x2009, 0x5062, 0x2104,
+ 0xa065, 0x0040, 0x11ef, 0x0c7e, 0x609c, 0x2060, 0x1078, 0x1996,
+ 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009, 0x000c, 0x6007,
+ 0x0103, 0x1078, 0x1907, 0x00c0, 0x11fb, 0x1078, 0x192b, 0x2009,
+ 0x5062, 0x200b, 0x0000, 0x2009, 0x505c, 0x2104, 0x200b, 0x0000,
+ 0xa005, 0x0040, 0x11ff, 0x2001, 0x4005, 0x0078, 0x1286, 0x0078,
+ 0x1284, 0x007c, 0x70c3, 0x0000, 0x70c7, 0x0000, 0x70cb, 0x0000,
+ 0x70cf, 0x0000, 0x70c0, 0xa0bc, 0xffc0, 0x00c0, 0x1252, 0x2038,
+ 0x0079, 0x1212, 0x1284, 0x12e5, 0x12a9, 0x12fe, 0x130d, 0x1313,
+ 0x12a0, 0x1748, 0x1317, 0x1298, 0x12ad, 0x12af, 0x12b1, 0x12b3,
+ 0x174d, 0x1298, 0x1329, 0x1360, 0x1672, 0x1742, 0x12b5, 0x1591,
+ 0x15ad, 0x15c9, 0x15f4, 0x154a, 0x1558, 0x156c, 0x1580, 0x13df,
+ 0x1298, 0x138d, 0x1393, 0x1398, 0x139d, 0x13a3, 0x13a8, 0x13ad,
+ 0x13b2, 0x13b7, 0x13bb, 0x13d0, 0x13dc, 0x1298, 0x1298, 0x1298,
+ 0x1298, 0x13eb, 0x13f4, 0x1403, 0x1429, 0x1433, 0x143a, 0x1480,
+ 0x148f, 0x149e, 0x14b0, 0x152a, 0x153a, 0x1298, 0x1298, 0x1298,
+ 0x1298, 0x153f, 0xa0bc, 0xffa0, 0x00c0, 0x1298, 0x2038, 0xa084,
+ 0x001f, 0x0079, 0x125b, 0x1786, 0x1789, 0x1799, 0x1298, 0x1298,
+ 0x18d8, 0x18f5, 0x1298, 0x1298, 0x1298, 0x18f9, 0x1901, 0x1298,
+ 0x1298, 0x1298, 0x1298, 0x12db, 0x12f4, 0x131f, 0x1356, 0x1668,
+ 0x1764, 0x1778, 0x1298, 0x1829, 0x1298, 0x18b4, 0x18be, 0x18c2,
+ 0x18d0, 0x1298, 0x1298, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0078,
+ 0x1286, 0x73ce, 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x0068,
+ 0x1287, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x00e0,
+ 0x128f, 0x00e0, 0x1291, 0x0068, 0x1291, 0x2091, 0x4080, 0x007c,
+ 0x70c3, 0x4001, 0x0078, 0x1287, 0x70c3, 0x4006, 0x0078, 0x1287,
+ 0x2099, 0x0041, 0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0078,
+ 0x1284, 0x70c4, 0x70c3, 0x0004, 0x007a, 0x0078, 0x1284, 0x0078,
+ 0x1284, 0x0078, 0x1284, 0x0078, 0x1284, 0x2091, 0x8000, 0x70c3,
+ 0x0000, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3,
+ 0x0007, 0x3f00, 0x70d6, 0x2079, 0x0000, 0x781b, 0x0001, 0x2031,
+ 0x0030, 0x2059, 0x1000, 0x2029, 0x0457, 0x2051, 0x0470, 0x2061,
+ 0x0472, 0x20b9, 0xffff, 0x20c1, 0x0000, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x0078, 0x0455, 0x1078, 0x1b36, 0x00c0, 0x129c, 0x75d8,
+ 0x74dc, 0x75da, 0x74de, 0x0078, 0x12e8, 0x2029, 0x0000, 0x2520,
+ 0x71d0, 0x73c8, 0x72cc, 0x70c4, 0x1078, 0x1a70, 0x0040, 0x1284,
+ 0x70c3, 0x4002, 0x0078, 0x1284, 0x1078, 0x1b36, 0x00c0, 0x129c,
+ 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x1301, 0x2029, 0x0000,
+ 0x2520, 0x71d0, 0x73c8, 0x72cc, 0x70c4, 0x1078, 0x1ad0, 0x0040,
+ 0x1284, 0x70c3, 0x4002, 0x0078, 0x1284, 0x71c4, 0x70c8, 0x2114,
+ 0x200a, 0x0078, 0x1282, 0x71c4, 0x2114, 0x0078, 0x1282, 0x70c7,
+ 0x0007, 0x70cb, 0x003f, 0x70cf, 0x0000, 0x0078, 0x1284, 0x1078,
+ 0x1b36, 0x00c0, 0x129c, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078,
+ 0x132c, 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d0,
+ 0x70c6, 0x72ca, 0x73ce, 0x74d2, 0xa005, 0x0040, 0x1350, 0x8001,
+ 0x7892, 0xa084, 0xfc00, 0x0040, 0x1345, 0x78cc, 0xa085, 0x0001,
+ 0x78ce, 0x2001, 0x4005, 0x0078, 0x1286, 0x7a9a, 0x7b9e, 0x7da2,
+ 0x7ea6, 0x7c96, 0x78cc, 0xa084, 0xfffc, 0x78ce, 0x0078, 0x1354,
+ 0x78cc, 0xa085, 0x0001, 0x78ce, 0x0078, 0x1284, 0x1078, 0x1b36,
+ 0x00c0, 0x129c, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078, 0x1363,
+ 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d4, 0x70c6,
+ 0x72ca, 0x73ce, 0x74d6, 0xa005, 0x0040, 0x1387, 0x8001, 0x78ae,
+ 0xa084, 0xfc00, 0x0040, 0x137c, 0x78cc, 0xa085, 0x0100, 0x78ce,
+ 0x2001, 0x4005, 0x0078, 0x1286, 0x7ab6, 0x7bba, 0x7dbe, 0x7ec2,
+ 0x7cb2, 0x78cc, 0xa084, 0xfcff, 0x78ce, 0x0078, 0x138b, 0x78cc,
+ 0xa085, 0x0100, 0x78ce, 0x0078, 0x1284, 0x2009, 0x5061, 0x210c,
+ 0x7aec, 0x0078, 0x1282, 0x2009, 0x5041, 0x210c, 0x0078, 0x1283,
+ 0x2009, 0x5042, 0x210c, 0x0078, 0x1283, 0x2061, 0x5040, 0x610c,
+ 0x6210, 0x0078, 0x1282, 0x2009, 0x5045, 0x210c, 0x0078, 0x1283,
+ 0x2009, 0x5046, 0x210c, 0x0078, 0x1283, 0x2009, 0x5048, 0x210c,
+ 0x0078, 0x1283, 0x2009, 0x5049, 0x210c, 0x0078, 0x1283, 0x7908,
+ 0x7a0c, 0x0078, 0x1282, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa0e8, 0x5280, 0x6a00, 0x6804, 0xa084, 0x0008,
+ 0x0040, 0x13cd, 0x6b08, 0x0078, 0x13ce, 0x6b0c, 0x0078, 0x1281,
+ 0x77c4, 0x1078, 0x1956, 0x2091, 0x8000, 0x6b1c, 0x6a14, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x1281, 0x794c, 0x0078, 0x1283, 0x77c4,
+ 0x1078, 0x1956, 0x2091, 0x8000, 0x6908, 0x6a18, 0x6b10, 0x2091,
+ 0x8001, 0x0078, 0x1281, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x127c,
+ 0x1078, 0x22c1, 0x0078, 0x1281, 0x71c4, 0xa182, 0x0010, 0x00c8,
+ 0x127c, 0x2011, 0x5041, 0x2204, 0x007e, 0x2112, 0x1078, 0x227a,
+ 0x017f, 0x0078, 0x1283, 0x71c4, 0x2011, 0x1421, 0x20a9, 0x0008,
+ 0x2204, 0xa106, 0x0040, 0x1413, 0x8210, 0x0070, 0x1411, 0x0078,
+ 0x1408, 0x0078, 0x127c, 0xa292, 0x1421, 0x027e, 0x2011, 0x5042,
+ 0x2204, 0x2112, 0x017f, 0x007e, 0x1078, 0x2286, 0x017f, 0x0078,
+ 0x1283, 0x03e8, 0x00fa, 0x01f4, 0x02ee, 0x0064, 0x0019, 0x0032,
+ 0x004b, 0x2061, 0x5040, 0x610c, 0x6210, 0x70c4, 0x600e, 0x70c8,
+ 0x6012, 0x0078, 0x1282, 0x2061, 0x5040, 0x6114, 0x70c4, 0x6016,
+ 0x0078, 0x1283, 0x2061, 0x5040, 0x71c4, 0x2011, 0x0004, 0x601f,
+ 0x0019, 0x2019, 0x1212, 0xa186, 0x0028, 0x0040, 0x145b, 0x2011,
+ 0x0005, 0x601f, 0x0019, 0x2019, 0x1212, 0xa186, 0x0032, 0x0040,
+ 0x145b, 0x2011, 0x0006, 0x601f, 0x000c, 0x2019, 0x2222, 0xa186,
+ 0x003c, 0x00c0, 0x127c, 0x6018, 0x007e, 0x611a, 0x7800, 0xa084,
+ 0x0001, 0x00c0, 0x1476, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+ 0x0048, 0x146e, 0x0038, 0x1472, 0x0078, 0x1476, 0x0028, 0x1472,
+ 0x0078, 0x1476, 0x2019, 0x2222, 0x0078, 0x1478, 0x2019, 0x1212,
+ 0x23b8, 0x1078, 0x2297, 0x1078, 0x4bdf, 0x017f, 0x0078, 0x1283,
+ 0x71c4, 0xa184, 0xffcf, 0x00c0, 0x127c, 0x2011, 0x5048, 0x2204,
+ 0x2112, 0x007e, 0x1078, 0x22b9, 0x017f, 0x0078, 0x1283, 0x71c4,
+ 0xa182, 0x0010, 0x00c8, 0x127c, 0x2011, 0x5049, 0x2204, 0x007e,
+ 0x2112, 0x1078, 0x22a8, 0x017f, 0x0078, 0x1283, 0x71c4, 0x72c8,
+ 0xa184, 0xfffd, 0x00c0, 0x127b, 0xa284, 0xfffd, 0x00c0, 0x127b,
+ 0x2100, 0x7908, 0x780a, 0x2200, 0x7a0c, 0x780e, 0x0078, 0x1282,
+ 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8,
+ 0x5280, 0x2019, 0x0000, 0x72c8, 0xa284, 0x0080, 0x0040, 0x14c6,
+ 0x6c14, 0x84ff, 0x00c0, 0x14c6, 0x6817, 0x0040, 0xa284, 0x0040,
+ 0x0040, 0x14d0, 0x6c10, 0x84ff, 0x00c0, 0x14d0, 0x6813, 0x0001,
+ 0x6800, 0x007e, 0xa226, 0x0040, 0x14f3, 0x6a02, 0xa484, 0x2000,
+ 0x0040, 0x14dc, 0xa39d, 0x0010, 0xa484, 0x1000, 0x0040, 0x14e2,
+ 0xa39d, 0x0008, 0xa484, 0x4000, 0x0040, 0x14f3, 0x810f, 0xa284,
+ 0x4000, 0x0040, 0x14ef, 0x1078, 0x22db, 0x0078, 0x14f3, 0x1078,
+ 0x22cd, 0x0078, 0x14f3, 0x72cc, 0x6808, 0xa206, 0x0040, 0x1522,
+ 0xa2a4, 0x00ff, 0x2061, 0x5040, 0x6118, 0xa186, 0x0028, 0x0040,
+ 0x1509, 0xa186, 0x0032, 0x0040, 0x150f, 0xa186, 0x003c, 0x0040,
+ 0x1515, 0xa482, 0x0064, 0x0048, 0x151f, 0x0078, 0x1519, 0xa482,
+ 0x0050, 0x0048, 0x151f, 0x0078, 0x1519, 0xa482, 0x0043, 0x0048,
+ 0x151f, 0x71c4, 0x71c6, 0x027f, 0x72ca, 0x0078, 0x127d, 0x6a0a,
+ 0xa39d, 0x000a, 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c, 0x71c4,
+ 0x0078, 0x1281, 0x77c4, 0x1078, 0x1956, 0x2091, 0x8000, 0x6a14,
+ 0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e, 0x2708,
+ 0x0078, 0x1281, 0x70c4, 0x794c, 0x784e, 0x0078, 0x1283, 0x71c4,
+ 0x72c8, 0x73cc, 0xa182, 0x0010, 0x00c8, 0x127c, 0x1078, 0x22e9,
+ 0x0078, 0x1281, 0x77c4, 0x1078, 0x1956, 0x2091, 0x8000, 0x6a08,
+ 0xa295, 0x0002, 0x6a0a, 0x2091, 0x8001, 0x2708, 0x0078, 0x1282,
+ 0x77c4, 0x1078, 0x1956, 0x2091, 0x8000, 0x6a08, 0xa294, 0xfff9,
+ 0x6a0a, 0x6804, 0xa005, 0x0040, 0x1567, 0x1078, 0x21b1, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x1282, 0x77c4, 0x1078, 0x1956, 0x2091,
+ 0x8000, 0x6a08, 0xa295, 0x0004, 0x6a0a, 0x6804, 0xa005, 0x0040,
+ 0x157b, 0x1078, 0x21b1, 0x2091, 0x8001, 0x2708, 0x0078, 0x1282,
+ 0x77c4, 0x2041, 0x0001, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091,
+ 0x8000, 0x1078, 0x1963, 0x2091, 0x8001, 0x2708, 0x6a08, 0x0078,
+ 0x1282, 0x77c4, 0x72c8, 0x73cc, 0x77c6, 0x72ca, 0x73ce, 0x1078,
+ 0x19c4, 0x00c0, 0x15a9, 0x6818, 0xa005, 0x0040, 0x15a9, 0x2708,
+ 0x1078, 0x22f9, 0x00c0, 0x15a9, 0x7817, 0x0015, 0x2091, 0x8001,
+ 0x007c, 0x2091, 0x8001, 0x0078, 0x1284, 0x77c4, 0x77c6, 0x2041,
+ 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078,
+ 0x1963, 0x2061, 0x5040, 0x606f, 0x0003, 0x6782, 0x6093, 0x000f,
+ 0x6073, 0x0000, 0x7817, 0x0016, 0x1078, 0x21b1, 0x2091, 0x8001,
+ 0x007c, 0x77c8, 0x77ca, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2091,
+ 0x8000, 0x2061, 0x5040, 0x606f, 0x0002, 0x6073, 0x0000, 0x6782,
+ 0x6093, 0x000f, 0x7817, 0x0017, 0x1078, 0x21b1, 0x2091, 0x8001,
+ 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010, 0x2091, 0x8000,
+ 0x1078, 0x1963, 0x70c8, 0x6836, 0x8738, 0xa784, 0x001f, 0x00c0,
+ 0x15e8, 0x2091, 0x8001, 0x007c, 0x78cc, 0xa084, 0x0003, 0x00c0,
+ 0x1618, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051,
+ 0x0008, 0x1078, 0x1956, 0x2091, 0x8000, 0x6808, 0xa80d, 0x690a,
+ 0x2091, 0x8001, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1601, 0xa7bc,
+ 0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1601,
+ 0x2091, 0x8000, 0x2069, 0x0100, 0x6830, 0xa084, 0x0040, 0x0040,
+ 0x1641, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0004,
+ 0x0040, 0x162e, 0x0070, 0x162e, 0x0078, 0x1625, 0x684b, 0x0009,
+ 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040, 0x163b, 0x0070,
+ 0x163b, 0x0078, 0x1632, 0x20a9, 0x00fa, 0x0070, 0x1641, 0x0078,
+ 0x163d, 0x2079, 0x5000, 0x7817, 0x0018, 0x2061, 0x5040, 0x606f,
+ 0x0001, 0x6073, 0x0000, 0x6093, 0x000f, 0x78cc, 0xa085, 0x0002,
+ 0x78ce, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b, 0x0048, 0x2091,
+ 0x8001, 0x007c, 0x78cc, 0xa084, 0xfffd, 0x78ce, 0xa084, 0x0001,
+ 0x00c0, 0x1664, 0x1078, 0x1a0e, 0x71c4, 0x71c6, 0x794a, 0x007c,
+ 0x1078, 0x1b36, 0x00c0, 0x129c, 0x75d8, 0x74dc, 0x75da, 0x74de,
+ 0x0078, 0x1675, 0x2029, 0x0000, 0x2520, 0x71c4, 0x73c8, 0x72cc,
+ 0x71c6, 0x73ca, 0x72ce, 0x2079, 0x5000, 0x2091, 0x8000, 0x1078,
+ 0x1911, 0x2091, 0x8001, 0x0040, 0x172c, 0x20a9, 0x0005, 0x20a1,
+ 0x5018, 0x2091, 0x8000, 0x41a1, 0x2091, 0x8001, 0x2009, 0x0020,
+ 0x1078, 0x190c, 0x0040, 0x1698, 0x1078, 0x192b, 0x0078, 0x172c,
+ 0x6004, 0xa084, 0xff00, 0x8007, 0x8009, 0x0040, 0x16fb, 0x0c7e,
+ 0x2c68, 0x2091, 0x8000, 0x1078, 0x1911, 0x2091, 0x8001, 0x0040,
+ 0x16cc, 0x2c00, 0x689e, 0x8109, 0x00c0, 0x16a0, 0x609f, 0x0000,
+ 0x0c7f, 0x0c7e, 0x7218, 0x731c, 0x7420, 0x7524, 0x2c68, 0x689c,
+ 0xa065, 0x0040, 0x16fa, 0x2009, 0x0020, 0x1078, 0x190c, 0x00c0,
+ 0x16e3, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0002, 0x00c0, 0x16cc,
+ 0x2d00, 0x6002, 0x0078, 0x16b2, 0x0c7f, 0x0c7e, 0x609c, 0x2060,
+ 0x1078, 0x1996, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009,
+ 0x000c, 0x6008, 0xa085, 0x0200, 0x600a, 0x1078, 0x1907, 0x1078,
+ 0x192b, 0x0078, 0x172c, 0x0c7f, 0x0c7e, 0x609c, 0x2060, 0x1078,
+ 0x1996, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009, 0x000c,
+ 0x6007, 0x0103, 0x601b, 0x0003, 0x1078, 0x1907, 0x1078, 0x192b,
+ 0x0078, 0x172c, 0x0c7f, 0x74c4, 0x73c8, 0x72cc, 0x6014, 0x2091,
+ 0x8000, 0x7817, 0x0012, 0x0e7e, 0x2071, 0x5040, 0x706f, 0x0005,
+ 0x7073, 0x0000, 0x7376, 0x727a, 0x747e, 0x7082, 0x7087, 0x0000,
+ 0x2c00, 0x708a, 0x708f, 0x0000, 0xa02e, 0x2530, 0x611c, 0x61a2,
+ 0xa184, 0x0060, 0x0040, 0x171e, 0x1078, 0x467f, 0x0e7f, 0x6596,
+ 0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000, 0x60b3, 0x0000, 0x1078,
+ 0x21b1, 0x2091, 0x8001, 0x007c, 0x70c3, 0x4005, 0x0078, 0x1287,
+ 0x20a9, 0x0005, 0x2099, 0x5018, 0x2091, 0x8000, 0x530a, 0x2091,
+ 0x8001, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+ 0x0000, 0x007c, 0x71c4, 0x70c7, 0x0000, 0x7906, 0x0078, 0x1284,
+ 0x71c4, 0x71c6, 0x2168, 0x0078, 0x174f, 0x2069, 0x1000, 0x690c,
+ 0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x00c0, 0x1751, 0xa285,
+ 0x0000, 0x00c0, 0x175f, 0x70c3, 0x4000, 0x0078, 0x1761, 0x70c3,
+ 0x4003, 0x70ca, 0x0078, 0x1287, 0x2011, 0x5067, 0x220c, 0x70c4,
+ 0x8003, 0x0048, 0x1771, 0x1078, 0x3b49, 0xa184, 0x7fff, 0x0078,
+ 0x1775, 0x1078, 0x3b3c, 0xa185, 0x8000, 0x2012, 0x0078, 0x1283,
+ 0x71c4, 0x1078, 0x3b33, 0x6100, 0x2001, 0x5067, 0x2004, 0xa084,
+ 0x8000, 0xa10d, 0x6204, 0x6308, 0x0078, 0x1281, 0x79e4, 0x0078,
+ 0x1283, 0x71c4, 0x71c6, 0x2198, 0x20a1, 0x0042, 0x20a9, 0x0004,
+ 0x53a3, 0x21a0, 0x2099, 0x0042, 0x20a9, 0x0004, 0x53a3, 0x0078,
+ 0x1284, 0x70c4, 0x2068, 0x2079, 0x5000, 0x2091, 0x8000, 0x1078,
+ 0x1911, 0x2091, 0x8001, 0x0040, 0x1825, 0x6007, 0x0001, 0x600b,
+ 0x0000, 0x602b, 0x0000, 0x601b, 0x0006, 0x6a10, 0xa28c, 0x000f,
+ 0xa284, 0x00f0, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0x6016,
+ 0xa284, 0x0800, 0x0040, 0x17c0, 0x601b, 0x000a, 0x0078, 0x17c6,
+ 0xa284, 0x1000, 0x0040, 0x17c6, 0x601b, 0x000c, 0xa284, 0x0300,
+ 0x0040, 0x17cf, 0x602b, 0x0001, 0x8004, 0x8004, 0x8004, 0xa085,
+ 0x0001, 0x601e, 0x6023, 0x0000, 0x6027, 0x0000, 0xa284, 0x0400,
+ 0x0040, 0x17dc, 0x602b, 0x0000, 0x20a9, 0x0006, 0xac80, 0x000b,
+ 0x20a0, 0xad80, 0x0005, 0x2098, 0x53a3, 0xa284, 0x0300, 0x00c0,
+ 0x17f1, 0x6046, 0x604a, 0x604e, 0x6052, 0x6096, 0x609a, 0x0078,
+ 0x17fb, 0x6800, 0x6046, 0x6804, 0x604a, 0x6e08, 0x664e, 0x6d0c,
+ 0x6552, 0x6596, 0x669a, 0x6014, 0x2091, 0x8000, 0x7817, 0x0042,
+ 0x2c08, 0x2061, 0x5040, 0x606f, 0x0005, 0x6073, 0x0000, 0x6077,
+ 0x0000, 0x607b, 0x0000, 0x607f, 0x0000, 0x6082, 0x618a, 0xa284,
+ 0x0400, 0x608e, 0x2091, 0x8001, 0x0e7e, 0x2071, 0x0020, 0x7007,
+ 0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x0e7f, 0x2091, 0x8000,
+ 0x1078, 0x21b1, 0x2091, 0x8001, 0x007c, 0x70c3, 0x4005, 0x0078,
+ 0x1287, 0x0c7e, 0x0d7e, 0x0e7e, 0x0f7e, 0x2091, 0x8000, 0x2071,
+ 0x5040, 0x2079, 0x0100, 0x2061, 0x0010, 0x70a0, 0xa06d, 0x0040,
+ 0x18aa, 0x6a04, 0xa294, 0x00ff, 0xa286, 0x0007, 0x0040, 0x1844,
+ 0xa286, 0x000f, 0x00c0, 0x18aa, 0x691c, 0xa184, 0x0080, 0x00c0,
+ 0x18aa, 0x6824, 0xa18c, 0xff00, 0xa085, 0x0019, 0x6826, 0x71b0,
+ 0x81ff, 0x0040, 0x1865, 0x0d7e, 0x2069, 0x0020, 0x6908, 0x6808,
+ 0xa106, 0x00c0, 0x1856, 0x690c, 0x680c, 0xa106, 0x00c0, 0x185b,
+ 0xa184, 0x00ff, 0x00c0, 0x185b, 0x0d7f, 0x78b8, 0xa084, 0x801f,
+ 0x00c0, 0x1865, 0x7848, 0xa085, 0x000c, 0x784a, 0x71b0, 0x81ff,
+ 0x0040, 0x1888, 0x70b3, 0x0000, 0x0d7e, 0x2069, 0x0020, 0x6807,
+ 0x0008, 0x6804, 0xa084, 0x0008, 0x00c0, 0x1879, 0x6807, 0x0008,
+ 0x6804, 0xa084, 0x0008, 0x00c0, 0x1880, 0x6807, 0x0002, 0x0d7f,
+ 0x61c4, 0x62c8, 0x63cc, 0x61c6, 0x62ca, 0x63ce, 0x0e7e, 0x2071,
+ 0x5000, 0x7266, 0x736a, 0xae80, 0x0019, 0x0e7f, 0x1078, 0x4598,
+ 0x78a3, 0x0000, 0x7858, 0xa084, 0xedff, 0x785a, 0x70b4, 0xa080,
+ 0x00da, 0x781a, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x2091, 0x8001,
+ 0x0078, 0x1284, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x2091, 0x8001,
+ 0x2001, 0x4005, 0x0078, 0x1286, 0x7980, 0x71c6, 0x71c4, 0xa182,
+ 0x0003, 0x00c8, 0x127c, 0x7982, 0x0078, 0x1284, 0x7980, 0x71c6,
+ 0x0078, 0x1284, 0x7974, 0x71c6, 0x71c4, 0x7976, 0x7978, 0x71ca,
+ 0x71c8, 0x797a, 0x797c, 0x71ce, 0x71cc, 0x797e, 0x0078, 0x1284,
+ 0x7974, 0x71c6, 0x7978, 0x71ca, 0x797c, 0x71ce, 0x0078, 0x1284,
+ 0x7900, 0x71c6, 0x71c4, 0x7902, 0x2001, 0x04fd, 0x2004, 0xa082,
+ 0x0005, 0x0048, 0x18e7, 0x0038, 0x18e9, 0x0078, 0x18f3, 0x00a8,
+ 0x18f3, 0xa18c, 0x0001, 0x00c0, 0x18f1, 0x20b9, 0x2222, 0x0078,
+ 0x18f3, 0x20b9, 0x1212, 0x0078, 0x1284, 0x7900, 0x71c6, 0x0078,
+ 0x1284, 0x2009, 0x5074, 0x2104, 0x70c6, 0x70c4, 0x200a, 0x0078,
+ 0x1284, 0x2009, 0x5074, 0x2104, 0x70c6, 0x0078, 0x1284, 0xac80,
+ 0x0001, 0x1078, 0x1af2, 0x007c, 0xac80, 0x0001, 0x1078, 0x1a92,
+ 0x007c, 0x7850, 0xa065, 0x0040, 0x1919, 0x2c04, 0x7852, 0x2063,
+ 0x0000, 0x007c, 0x0f7e, 0x2079, 0x5000, 0x7850, 0xa06d, 0x0040,
+ 0x1929, 0x2d04, 0x7852, 0x6803, 0x0000, 0x6807, 0x0000, 0x680b,
+ 0x0000, 0x0f7f, 0x007c, 0x2091, 0x8000, 0x0f7e, 0x2079, 0x5000,
+ 0x7850, 0x2062, 0x2c00, 0xa005, 0x00c0, 0x1938, 0x1078, 0x23ca,
+ 0x7852, 0x0f7f, 0x2091, 0x8001, 0x007c, 0x0f7e, 0x2079, 0x5000,
+ 0x7850, 0x206a, 0x2d00, 0x7852, 0x0f7f, 0x007c, 0x2011, 0x7700,
+ 0x7a52, 0x7bec, 0x8319, 0x0040, 0x1953, 0xa280, 0x0031, 0x2012,
+ 0x2010, 0x0078, 0x194a, 0x2013, 0x0000, 0x007c, 0xa784, 0x0f00,
+ 0x800b, 0xa784, 0x001f, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105,
+ 0xa0e8, 0x5300, 0x007c, 0x1078, 0x1956, 0x2900, 0x682a, 0x2a00,
+ 0x682e, 0x6808, 0xa084, 0xffef, 0xa80d, 0x690a, 0x2009, 0x5052,
+ 0x210c, 0x6804, 0xa005, 0x0040, 0x1995, 0xa116, 0x00c0, 0x1980,
+ 0x2060, 0x6000, 0x6806, 0x017e, 0x200b, 0x0000, 0x0078, 0x1983,
+ 0x2009, 0x0000, 0x017e, 0x6804, 0xa065, 0x0040, 0x1992, 0x6000,
+ 0x6806, 0x1078, 0x19a3, 0x1078, 0x1c42, 0x6810, 0x8001, 0x6812,
+ 0x00c0, 0x1983, 0x017f, 0x6902, 0x6906, 0x007c, 0xa065, 0x0040,
+ 0x19a2, 0x609c, 0x609f, 0x0000, 0x2008, 0x1078, 0x192b, 0x2100,
+ 0x0078, 0x1996, 0x007c, 0x6007, 0x0103, 0x608f, 0x0000, 0x20a9,
+ 0x001c, 0xac80, 0x0005, 0x20a0, 0x2001, 0x0000, 0x40a4, 0x6828,
+ 0x601a, 0x682c, 0x6022, 0x007c, 0x0e7e, 0x2071, 0x5040, 0x704c,
+ 0xa08c, 0x0200, 0x00c0, 0x19c2, 0xa088, 0x5080, 0x2d0a, 0x8000,
+ 0x704e, 0xa006, 0x0e7f, 0x007c, 0x1078, 0x1956, 0x2091, 0x8000,
+ 0x6804, 0x781e, 0xa065, 0x0040, 0x1a0d, 0x0078, 0x19d5, 0x2c00,
+ 0x781e, 0x6000, 0xa065, 0x0040, 0x1a0d, 0x600c, 0xa306, 0x00c0,
+ 0x19cf, 0x6010, 0xa206, 0x00c0, 0x19cf, 0x2c28, 0x2001, 0x5052,
+ 0x2004, 0xac06, 0x00c0, 0x19e6, 0x0078, 0x1a0b, 0x6804, 0xac06,
+ 0x00c0, 0x19f3, 0x6000, 0xa065, 0x6806, 0x00c0, 0x19fd, 0x6803,
+ 0x0000, 0x0078, 0x19fd, 0x6400, 0x781c, 0x2060, 0x6402, 0xa486,
+ 0x0000, 0x00c0, 0x19fd, 0x2c00, 0x6802, 0x2560, 0x1078, 0x19a3,
+ 0x601b, 0x0005, 0x6023, 0x0020, 0x1078, 0x1c42, 0x6810, 0x8001,
+ 0x1050, 0x23ca, 0x6812, 0xa085, 0xffff, 0x007c, 0x2039, 0x0000,
+ 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008, 0x2091, 0x8000,
+ 0x1078, 0x1963, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1a18, 0xa7bc,
+ 0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1a18,
+ 0x2091, 0x8001, 0x007c, 0x2061, 0x0000, 0x6018, 0xa084, 0x0001,
+ 0x00c0, 0x1a3c, 0x2091, 0x8000, 0x78e0, 0x78e3, 0x0000, 0x2091,
+ 0x8001, 0xa005, 0x00c0, 0x1a3d, 0x007c, 0xa08c, 0xfff0, 0x0040,
+ 0x1a43, 0x1078, 0x23ca, 0x0079, 0x1a45, 0x1a55, 0x1a58, 0x1a5e,
+ 0x1a62, 0x1a56, 0x1a66, 0x1a6c, 0x1a56, 0x1a56, 0x1c0c, 0x1c30,
+ 0x1c34, 0x1a56, 0x1a56, 0x1a56, 0x1a56, 0x007c, 0x1078, 0x23ca,
+ 0x1078, 0x1a0e, 0x2001, 0x8001, 0x0078, 0x1c3a, 0x2001, 0x8003,
+ 0x0078, 0x1c3a, 0x2001, 0x8004, 0x0078, 0x1c3a, 0x1078, 0x1a0e,
+ 0x2001, 0x8006, 0x0078, 0x1c3a, 0x2001, 0x8007, 0x0078, 0x1c3a,
+ 0x2030, 0x2138, 0xa782, 0x0021, 0x0048, 0x1a78, 0x2009, 0x0020,
+ 0x2600, 0x1078, 0x1a92, 0x00c0, 0x1a91, 0xa7ba, 0x0020, 0x0048,
+ 0x1a90, 0x0040, 0x1a90, 0x2708, 0xa6b0, 0x0020, 0xa290, 0x0040,
+ 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x0078, 0x1a72,
+ 0xa006, 0x007c, 0x81ff, 0x0040, 0x1acd, 0x2099, 0x0030, 0x20a0,
+ 0x700c, 0xa084, 0x00ff, 0x0040, 0x1aa4, 0x7007, 0x0004, 0x7004,
+ 0xa084, 0x0004, 0x00c0, 0x1a9f, 0x21a8, 0x7017, 0x0000, 0x810b,
+ 0x7112, 0x721a, 0x731e, 0x7422, 0x7526, 0x780c, 0xa085, 0x0001,
+ 0x7002, 0x7007, 0x0001, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+ 0x00c8, 0x1ac1, 0x2009, 0x0022, 0x2104, 0xa084, 0x4000, 0x00c0,
+ 0x1ab3, 0x7008, 0x800b, 0x00c8, 0x1ab3, 0x7007, 0x0002, 0xa08c,
+ 0x01e0, 0x00c0, 0x1acd, 0x53a5, 0xa006, 0x7003, 0x0000, 0x007c,
+ 0x2030, 0x2138, 0xa782, 0x0021, 0x0048, 0x1ad8, 0x2009, 0x0020,
+ 0x2600, 0x1078, 0x1af2, 0x00c0, 0x1af1, 0xa7ba, 0x0020, 0x0048,
+ 0x1af0, 0x0040, 0x1af0, 0x2708, 0xa6b0, 0x0020, 0xa290, 0x0040,
+ 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x0078, 0x1ad2,
+ 0xa006, 0x007c, 0x81ff, 0x0040, 0x1b33, 0x2098, 0x20a1, 0x0030,
+ 0x700c, 0xa084, 0x00ff, 0x0040, 0x1b04, 0x7007, 0x0004, 0x7004,
+ 0xa084, 0x0004, 0x00c0, 0x1aff, 0x21a8, 0x7017, 0x0000, 0x810b,
+ 0x7112, 0x721a, 0x731e, 0x7422, 0x7526, 0x780c, 0xa085, 0x0000,
+ 0x7002, 0x53a6, 0x7007, 0x0001, 0x2001, 0x04fd, 0x2004, 0xa082,
+ 0x0005, 0x00c8, 0x1b22, 0x2009, 0x0022, 0x2104, 0xa084, 0x4000,
+ 0x00c0, 0x1b14, 0x7010, 0xa084, 0xf000, 0x0040, 0x1b2b, 0x7007,
+ 0x0008, 0x0078, 0x1b2f, 0x7108, 0x8103, 0x00c8, 0x1b14, 0x7007,
+ 0x0002, 0xa184, 0x01e0, 0x7003, 0x0000, 0x007c, 0x2001, 0x04fd,
+ 0x2004, 0xa082, 0x0004, 0x00c8, 0x1b3f, 0x0078, 0x1b42, 0xa006,
+ 0x0078, 0x1b44, 0xa085, 0x0001, 0x007c, 0x0e7e, 0x2071, 0x5000,
+ 0x2d08, 0x7058, 0x6802, 0xa005, 0x00c0, 0x1b4f, 0x715e, 0x715a,
+ 0x0e7f, 0x007c, 0x2c08, 0x7858, 0x6002, 0xa005, 0x00c0, 0x1b59,
+ 0x795e, 0x795a, 0x007c, 0x2091, 0x8000, 0x6003, 0x0000, 0x2c08,
+ 0x785c, 0xa065, 0x00c0, 0x1b67, 0x795a, 0x0078, 0x1b68, 0x6102,
+ 0x795e, 0x2091, 0x8001, 0x1078, 0x21ce, 0x007c, 0x0e7e, 0x2071,
+ 0x5000, 0x7058, 0xa06d, 0x0040, 0x1b7c, 0x6800, 0x705a, 0xa005,
+ 0x00c0, 0x1b7b, 0x705e, 0x8dff, 0x0e7f, 0x007c, 0x0d7e, 0x0c7e,
+ 0x0f7e, 0x2079, 0x5000, 0xaf80, 0x0016, 0x2060, 0x6000, 0xa005,
+ 0x0040, 0x1bac, 0x2068, 0x6814, 0xa306, 0x00c0, 0x1b95, 0x6828,
+ 0xa084, 0x00ff, 0xa406, 0x0040, 0x1b98, 0x2d60, 0x0078, 0x1b86,
+ 0x6800, 0xa005, 0x6002, 0x00c0, 0x1ba4, 0xaf80, 0x0016, 0xac06,
+ 0x0040, 0x1ba3, 0x2c00, 0x785e, 0x0d7e, 0x689c, 0xa005, 0x0040,
+ 0x1bab, 0x1078, 0x1996, 0x007f, 0x0f7f, 0x0c7f, 0x0d7f, 0xa005,
+ 0x007c, 0x0d7e, 0x0c7e, 0x0f7e, 0x2079, 0x5000, 0xaf80, 0x0016,
+ 0x2060, 0x6000, 0xa005, 0x0040, 0x1bdb, 0x2068, 0x6814, 0xa084,
+ 0x00ff, 0xa306, 0x0040, 0x1bc7, 0x2d60, 0x0078, 0x1bb9, 0x6800,
+ 0xa005, 0x6002, 0x00c0, 0x1bd3, 0xaf80, 0x0016, 0xac06, 0x0040,
+ 0x1bd2, 0x2c00, 0x785e, 0x0d7e, 0x689c, 0xa005, 0x0040, 0x1bda,
+ 0x1078, 0x1996, 0x007f, 0x0f7f, 0x0c7f, 0x0d7f, 0xa005, 0x007c,
+ 0x0d7e, 0x0c7e, 0x0f7e, 0x2079, 0x5000, 0xaf80, 0x0016, 0x2060,
+ 0x6000, 0xa06d, 0x0040, 0x1c07, 0x6814, 0xa306, 0x0040, 0x1bf3,
+ 0x2d60, 0x0078, 0x1be8, 0x6800, 0xa005, 0x6002, 0x00c0, 0x1bff,
+ 0xaf80, 0x0016, 0xac06, 0x0040, 0x1bfe, 0x2c00, 0x785e, 0x0d7e,
+ 0x689c, 0xa005, 0x0040, 0x1c06, 0x1078, 0x1996, 0x007f, 0x0f7f,
+ 0x0c7f, 0x0d7f, 0xa005, 0x007c, 0x2091, 0x8000, 0x2069, 0x5040,
+ 0x6800, 0xa086, 0x0000, 0x0040, 0x1c1a, 0x2091, 0x8001, 0x78e3,
+ 0x0009, 0x007c, 0x6880, 0xa0bc, 0xff00, 0x2041, 0x0021, 0x2049,
+ 0x0004, 0x2051, 0x0010, 0x1078, 0x1963, 0x8738, 0xa784, 0x001f,
+ 0x00c0, 0x1c23, 0x2091, 0x8001, 0x2001, 0x800a, 0x0078, 0x1c3a,
+ 0x2001, 0x800c, 0x0078, 0x1c3a, 0x1078, 0x1a0e, 0x2001, 0x800d,
+ 0x0078, 0x1c3a, 0x70c2, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091,
+ 0x4080, 0x007c, 0x6004, 0x2c08, 0x2063, 0x0000, 0x7884, 0x8000,
+ 0x7886, 0x7888, 0xa005, 0x798a, 0x0040, 0x1c51, 0x2c02, 0x0078,
+ 0x1c52, 0x798e, 0x007c, 0x6807, 0x0103, 0x0c7e, 0x2061, 0x5000,
+ 0x2d08, 0x206b, 0x0000, 0x6084, 0x8000, 0x6086, 0x6088, 0xa005,
+ 0x618a, 0x0040, 0x1c66, 0x2d02, 0x0078, 0x1c67, 0x618e, 0x0c7f,
+ 0x007c, 0x1078, 0x1c7a, 0x0040, 0x1c79, 0x0c7e, 0x609c, 0xa065,
+ 0x0040, 0x1c74, 0x1078, 0x1996, 0x0c7f, 0x609f, 0x0000, 0x1078,
+ 0x192b, 0x007c, 0x788c, 0xa065, 0x0040, 0x1c8c, 0x2091, 0x8000,
+ 0x7884, 0x8001, 0x7886, 0x2c04, 0x788e, 0xa005, 0x00c0, 0x1c8a,
+ 0x788a, 0x8000, 0x2091, 0x8001, 0x007c, 0x20a9, 0x0010, 0xa006,
+ 0x8004, 0x8086, 0x818e, 0x00c8, 0x1c96, 0xa200, 0x0070, 0x1c9a,
+ 0x0078, 0x1c91, 0x8086, 0x818e, 0x007c, 0x157e, 0x20a9, 0x0010,
+ 0xa005, 0x0040, 0x1cc0, 0xa11a, 0x00c8, 0x1cc0, 0x8213, 0x818d,
+ 0x0048, 0x1cb1, 0xa11a, 0x00c8, 0x1cb2, 0x0070, 0x1cb8, 0x0078,
+ 0x1ca6, 0xa11a, 0x2308, 0x8210, 0x0070, 0x1cb8, 0x0078, 0x1ca6,
+ 0x007e, 0x3200, 0xa084, 0xf7ff, 0x2080, 0x007f, 0x157f, 0x007c,
+ 0x007e, 0x3200, 0xa085, 0x0800, 0x0078, 0x1cbc, 0x7994, 0x70d0,
+ 0xa106, 0x0040, 0x1d34, 0x2091, 0x8000, 0x2071, 0x0020, 0x7004,
+ 0xa005, 0x00c0, 0x1d34, 0x7008, 0x7208, 0xa206, 0x00c0, 0x1d34,
+ 0xa286, 0x0008, 0x00c0, 0x1d34, 0x2071, 0x0010, 0x1078, 0x1911,
+ 0x0040, 0x1d34, 0x7a9c, 0x7b98, 0x7ca4, 0x7da0, 0xa184, 0xff00,
+ 0x0040, 0x1d02, 0x2031, 0x0000, 0x810b, 0x86b5, 0x810b, 0x86b5,
+ 0x810b, 0x86b5, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x810b, 0x86b5,
+ 0x2100, 0xa210, 0x2600, 0xa319, 0xa4a1, 0x0000, 0xa5a9, 0x0000,
+ 0x0078, 0x1d0c, 0x8107, 0x8004, 0x8004, 0xa210, 0xa399, 0x0000,
+ 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x2009, 0x0020, 0x1078, 0x190c,
+ 0x2091, 0x8001, 0x0040, 0x1d2b, 0x1078, 0x192b, 0x78a8, 0x8000,
+ 0x78aa, 0xa086, 0x0002, 0x00c0, 0x1d34, 0x2091, 0x8000, 0x78e3,
+ 0x0002, 0x78ab, 0x0000, 0x78cc, 0xa085, 0x0003, 0x78ce, 0x2091,
+ 0x8001, 0x0078, 0x1d34, 0x78ab, 0x0000, 0x1078, 0x208b, 0x6004,
+ 0xa084, 0x000f, 0x0079, 0x1d39, 0x2071, 0x0010, 0x2091, 0x8001,
+ 0x007c, 0x1d49, 0x1d6b, 0x1d91, 0x1d49, 0x1dae, 0x1d58, 0x1f0d,
+ 0x1f28, 0x1d49, 0x1d65, 0x1d8b, 0x1df6, 0x1e63, 0x1eb3, 0x1ec5,
+ 0x1f24, 0x2039, 0x0400, 0x78dc, 0xa705, 0x78de, 0x6008, 0xa705,
+ 0x600a, 0x1078, 0x1fa6, 0x609c, 0x78da, 0x1078, 0x2073, 0x007c,
+ 0x78dc, 0xa084, 0x0100, 0x0040, 0x1d5f, 0x0078, 0x1d49, 0x601c,
+ 0xa085, 0x0080, 0x601e, 0x0078, 0x1d72, 0x1078, 0x1b36, 0x00c0,
+ 0x1d49, 0x1078, 0x20a5, 0x78dc, 0xa084, 0x0100, 0x0040, 0x1d72,
+ 0x0078, 0x1d49, 0x78df, 0x0000, 0x6004, 0x8007, 0xa084, 0x00ff,
+ 0x78d2, 0x8001, 0x609f, 0x0000, 0x0040, 0x1d88, 0x1078, 0x1fa6,
+ 0x0040, 0x1d88, 0x78dc, 0xa085, 0x0100, 0x78de, 0x0078, 0x1d8a,
+ 0x1078, 0x1fca, 0x007c, 0x1078, 0x1b36, 0x00c0, 0x1d49, 0x1078,
+ 0x20a1, 0x78dc, 0xa08c, 0x0e00, 0x00c0, 0x1d9a, 0xa084, 0x0100,
+ 0x00c0, 0x1d9c, 0x0078, 0x1d49, 0x1078, 0x1fa6, 0x00c0, 0x1dad,
+ 0x6104, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x1f63, 0xa186,
+ 0x000f, 0x0040, 0x1f63, 0x1078, 0x1fca, 0x007c, 0x78dc, 0xa084,
+ 0x0100, 0x0040, 0x1db5, 0x0078, 0x1d49, 0x78df, 0x0000, 0x6714,
+ 0x2011, 0x0001, 0x20a9, 0x0001, 0x6018, 0xa084, 0x00ff, 0xa005,
+ 0x0040, 0x1dd8, 0x2011, 0x0001, 0xa7bc, 0xff00, 0x20a9, 0x0020,
+ 0xa08e, 0x0001, 0x0040, 0x1dd8, 0x2039, 0x0000, 0x2011, 0x0002,
+ 0x20a9, 0x0100, 0xa08e, 0x0002, 0x0040, 0x1dd8, 0x0078, 0x1df3,
+ 0x1078, 0x1956, 0x2091, 0x8000, 0x682b, 0x0000, 0x682f, 0x0000,
+ 0x6808, 0xa084, 0xffde, 0x680a, 0xade8, 0x0010, 0x2091, 0x8001,
+ 0x0070, 0x1dec, 0x0078, 0x1dda, 0x8211, 0x0040, 0x1df3, 0x20a9,
+ 0x0100, 0x0078, 0x1dda, 0x1078, 0x192b, 0x007c, 0x2001, 0x5067,
+ 0x2004, 0xa084, 0x8000, 0x0040, 0x1f8b, 0x6114, 0x1078, 0x20c2,
+ 0x6900, 0xa184, 0x0001, 0x0040, 0x1e17, 0x6028, 0xa084, 0x00ff,
+ 0x00c0, 0x1f83, 0x6800, 0xa084, 0x0001, 0x0040, 0x1f8b, 0x6803,
+ 0x0000, 0x680b, 0x0000, 0x6807, 0x0000, 0x0078, 0x1f93, 0x2011,
+ 0x0001, 0x6020, 0xa084, 0x4000, 0x0040, 0x1e20, 0xa295, 0x0002,
+ 0x6020, 0xa084, 0x0100, 0x0040, 0x1e27, 0xa295, 0x0008, 0x601c,
+ 0xa084, 0x0002, 0x0040, 0x1e2e, 0xa295, 0x0004, 0x602c, 0xa08c,
+ 0x00ff, 0xa182, 0x0002, 0x0048, 0x1f8f, 0xa182, 0x001b, 0x00c8,
+ 0x1f8f, 0x0040, 0x1f8f, 0x690e, 0x602c, 0x8007, 0xa08c, 0x00ff,
+ 0xa182, 0x0002, 0x0048, 0x1f8f, 0xa182, 0x001b, 0x00c8, 0x1f8f,
+ 0x0040, 0x1f8f, 0x6912, 0x6030, 0xa005, 0x00c0, 0x1e51, 0x2001,
+ 0x001e, 0x8000, 0x6816, 0x6028, 0xa084, 0x00ff, 0x0040, 0x1f8b,
+ 0x6806, 0x6028, 0x8007, 0xa084, 0x00ff, 0x0040, 0x1f8b, 0x680a,
+ 0x6a02, 0x0078, 0x1f93, 0x2001, 0x5067, 0x2004, 0xa084, 0x8000,
+ 0x0040, 0x1f8b, 0x6114, 0x1078, 0x20c2, 0x2091, 0x8000, 0x6a04,
+ 0x6b08, 0x6418, 0xa484, 0x0003, 0x0040, 0x1e89, 0x6128, 0xa18c,
+ 0x00ff, 0x8001, 0x00c0, 0x1e82, 0x2100, 0xa210, 0x0048, 0x1eaf,
+ 0x0078, 0x1e89, 0x8001, 0x00c0, 0x1eaf, 0x2100, 0xa212, 0x0048,
+ 0x1eaf, 0xa484, 0x000c, 0x0040, 0x1ea3, 0x6128, 0x810f, 0xa18c,
+ 0x00ff, 0xa082, 0x0004, 0x00c0, 0x1e9b, 0x2100, 0xa318, 0x0048,
+ 0x1eaf, 0x0078, 0x1ea3, 0xa082, 0x0004, 0x00c0, 0x1eaf, 0x2100,
+ 0xa31a, 0x0048, 0x1eaf, 0x6030, 0xa005, 0x0040, 0x1ea9, 0x8000,
+ 0x6816, 0x6a06, 0x6b0a, 0x2091, 0x8001, 0x0078, 0x1f93, 0x2091,
+ 0x8001, 0x0078, 0x1f8f, 0x6114, 0x1078, 0x20c2, 0x2091, 0x8000,
+ 0x6b08, 0x8318, 0x0048, 0x1ec1, 0x6b0a, 0x2091, 0x8001, 0x0078,
+ 0x1fa2, 0x2091, 0x8001, 0x0078, 0x1f8f, 0x6024, 0x8007, 0xa084,
+ 0x00ff, 0x0040, 0x1ee3, 0xa086, 0x0080, 0x00c0, 0x1f0b, 0x20a9,
+ 0x0008, 0x2069, 0x7410, 0x2091, 0x8000, 0x6800, 0xa084, 0xfcff,
+ 0x6802, 0xade8, 0x0008, 0x0070, 0x1edf, 0x0078, 0x1ed5, 0x2091,
+ 0x8001, 0x0078, 0x1f93, 0x6028, 0xa015, 0x0040, 0x1f0b, 0x6114,
+ 0x1078, 0x20c2, 0x0d7e, 0xade8, 0x0007, 0x2091, 0x8000, 0x6800,
+ 0xa00d, 0x0040, 0x1f08, 0xa206, 0x0040, 0x1ef9, 0x2168, 0x0078,
+ 0x1eef, 0x0c7e, 0x2160, 0x6000, 0x6802, 0x1078, 0x192b, 0x0c7f,
+ 0x0d7f, 0x6808, 0x8000, 0x680a, 0x2091, 0x8001, 0x0078, 0x1fa2,
+ 0x2091, 0x8001, 0x0d7f, 0x0078, 0x1f8b, 0x6114, 0x1078, 0x20c2,
+ 0x6800, 0xa084, 0x0001, 0x0040, 0x1f7b, 0x2091, 0x8000, 0x6a04,
+ 0x8210, 0x0048, 0x1f20, 0x6a06, 0x2091, 0x8001, 0x0078, 0x1fa2,
+ 0x2091, 0x8001, 0x0078, 0x1f8f, 0x1078, 0x1b36, 0x00c0, 0x1d49,
+ 0x6114, 0x1078, 0x20c2, 0x60be, 0x6900, 0xa184, 0x0008, 0x0040,
+ 0x1f35, 0x6020, 0xa085, 0x0100, 0x6022, 0xa184, 0x0001, 0x0040,
+ 0x1f8b, 0xa184, 0x0100, 0x00c0, 0x1f77, 0xa184, 0x0200, 0x00c0,
+ 0x1f73, 0x681c, 0xa005, 0x00c0, 0x1f7f, 0x6004, 0xa084, 0x00ff,
+ 0xa086, 0x000f, 0x00c0, 0x1f4e, 0x1078, 0x20a5, 0x78df, 0x0000,
+ 0x6004, 0x8007, 0xa084, 0x00ff, 0x78d2, 0x8001, 0x609f, 0x0000,
+ 0x0040, 0x1f63, 0x1078, 0x1fa6, 0x0040, 0x1f63, 0x78dc, 0xa085,
+ 0x0100, 0x78de, 0x007c, 0x78d7, 0x0000, 0x78db, 0x0000, 0x6024,
+ 0xa084, 0xff00, 0x6026, 0x1078, 0x39aa, 0x0040, 0x1cc6, 0x1078,
+ 0x1b5b, 0x0078, 0x1cc6, 0x2009, 0x0017, 0x0078, 0x1f95, 0x2009,
+ 0x000e, 0x0078, 0x1f95, 0x2009, 0x0007, 0x0078, 0x1f95, 0x2009,
+ 0x0035, 0x0078, 0x1f95, 0x2009, 0x003e, 0x0078, 0x1f95, 0x2009,
+ 0x0004, 0x0078, 0x1f95, 0x2009, 0x0006, 0x0078, 0x1f95, 0x2009,
+ 0x0016, 0x0078, 0x1f95, 0x2009, 0x0001, 0x6024, 0xa084, 0xff00,
+ 0xa105, 0x6026, 0x2091, 0x8000, 0x1078, 0x1c42, 0x2091, 0x8001,
+ 0x0078, 0x1cc6, 0x1078, 0x192b, 0x0078, 0x1cc6, 0x78d4, 0xa06d,
+ 0x00c0, 0x1fb1, 0x2c00, 0x78d6, 0x78da, 0x609f, 0x0000, 0x0078,
+ 0x1fbd, 0x2c00, 0x689e, 0x609f, 0x0000, 0x78d6, 0x2d00, 0x6002,
+ 0x78d8, 0xad06, 0x00c0, 0x1fbd, 0x6002, 0x78d0, 0x8001, 0x78d2,
+ 0x00c0, 0x1fc9, 0x78dc, 0xa084, 0xfeff, 0x78de, 0x78d8, 0x2060,
+ 0xa006, 0x007c, 0xa02e, 0x2530, 0x611c, 0x61a2, 0xa184, 0xe1ff,
+ 0x601e, 0xa184, 0x0060, 0x0040, 0x1fd9, 0x0e7e, 0x1078, 0x467f,
+ 0x0e7f, 0x6596, 0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000, 0x60b3,
+ 0x0000, 0x6714, 0x1078, 0x1956, 0x2091, 0x8000, 0x60a0, 0xa084,
+ 0x8000, 0x00c0, 0x2000, 0x6808, 0xa084, 0x0001, 0x0040, 0x2000,
+ 0x2091, 0x8001, 0x1078, 0x19a3, 0x2091, 0x8000, 0x1078, 0x1c42,
+ 0x2091, 0x8001, 0x78d7, 0x0000, 0x78db, 0x0000, 0x0078, 0x2072,
+ 0x6024, 0xa096, 0x0001, 0x00c0, 0x2007, 0x8000, 0x6026, 0x6a10,
+ 0x6814, 0x2091, 0x8001, 0xa202, 0x0048, 0x2016, 0x0040, 0x2016,
+ 0x2039, 0x0200, 0x1078, 0x2073, 0x0078, 0x2072, 0x2c08, 0x2091,
+ 0x8000, 0x60a0, 0xa084, 0x8000, 0x0040, 0x2043, 0x6800, 0xa065,
+ 0x0040, 0x2048, 0x6a04, 0x0e7e, 0x2071, 0x5040, 0x7000, 0xa084,
+ 0x0001, 0x0040, 0x203d, 0x7048, 0xa206, 0x00c0, 0x203d, 0x6b04,
+ 0x231c, 0x2160, 0x6302, 0x2300, 0xa005, 0x00c0, 0x2038, 0x6902,
+ 0x2260, 0x6102, 0x0e7f, 0x0078, 0x204f, 0x2160, 0x6202, 0x6906,
+ 0x0e7f, 0x0078, 0x204f, 0x6800, 0xa065, 0x0040, 0x2048, 0x6102,
+ 0x6902, 0x00c0, 0x204c, 0x6906, 0x2160, 0x6003, 0x0000, 0x2160,
+ 0x60a0, 0xa084, 0x8000, 0x0040, 0x2059, 0x6808, 0xa084, 0xfffc,
+ 0x680a, 0x6810, 0x8000, 0x6812, 0x2091, 0x8001, 0x6808, 0xa08c,
+ 0x0040, 0x0040, 0x2068, 0xa086, 0x0040, 0x680a, 0x1078, 0x19b4,
+ 0x2091, 0x8000, 0x1078, 0x21b1, 0x2091, 0x8001, 0x78db, 0x0000,
+ 0x78d7, 0x0000, 0x007c, 0x6008, 0xa705, 0x600a, 0x2091, 0x8000,
+ 0x1078, 0x1c42, 0x2091, 0x8001, 0x78d8, 0xa065, 0x0040, 0x2086,
+ 0x609c, 0x78da, 0x609f, 0x0000, 0x0078, 0x2076, 0x78d7, 0x0000,
+ 0x78db, 0x0000, 0x007c, 0x7990, 0x7894, 0x8000, 0xa10a, 0x00c8,
+ 0x2092, 0xa006, 0x7896, 0x70d2, 0x7804, 0xa005, 0x0040, 0x20a0,
+ 0x8001, 0x7806, 0x00c0, 0x20a0, 0x0068, 0x20a0, 0x2091, 0x4080,
+ 0x007c, 0x2039, 0x20b9, 0x0078, 0x20a7, 0x2039, 0x20bf, 0x2704,
+ 0xa005, 0x0040, 0x20b8, 0xac00, 0x2068, 0x6b08, 0x6c0c, 0x6910,
+ 0x6a14, 0x690a, 0x6a0e, 0x6b12, 0x6c16, 0x8738, 0x0078, 0x20a7,
+ 0x007c, 0x0003, 0x0009, 0x000f, 0x0015, 0x001b, 0x0000, 0x0015,
+ 0x001b, 0x0000, 0x0c7e, 0x1078, 0x3b33, 0x2c68, 0x0c7f, 0x007c,
+ 0x0010, 0x2139, 0x0068, 0x2139, 0x2029, 0x0000, 0x78cb, 0x0000,
+ 0x788c, 0xa065, 0x0040, 0x2132, 0x2009, 0x5074, 0x2104, 0xa084,
+ 0x0001, 0x0040, 0x2100, 0x6004, 0xa086, 0x0103, 0x00c0, 0x2100,
+ 0x6018, 0xa005, 0x00c0, 0x2100, 0x6014, 0xa005, 0x00c0, 0x2100,
+ 0x0d7e, 0x2069, 0x0000, 0x6818, 0xa084, 0x0001, 0x00c0, 0x20ff,
+ 0x600c, 0x70c6, 0x6010, 0x70ca, 0x70c3, 0x8020, 0x681b, 0x0001,
+ 0x2091, 0x4080, 0x0d7f, 0x1078, 0x1c69, 0x0078, 0x2137, 0x0d7f,
+ 0x1078, 0x213a, 0x0040, 0x2132, 0x6204, 0xa294, 0x00ff, 0xa296,
+ 0x0003, 0x0040, 0x2112, 0x6204, 0xa296, 0x0110, 0x00c0, 0x2120,
+ 0x78cb, 0x0001, 0x6204, 0xa294, 0xff00, 0x8217, 0x8211, 0x0040,
+ 0x2120, 0x85ff, 0x00c0, 0x2132, 0x8210, 0xa202, 0x00c8, 0x2132,
+ 0x057e, 0x1078, 0x2149, 0x057f, 0x0040, 0x212d, 0x78e0, 0xa086,
+ 0x0003, 0x0040, 0x2132, 0x0078, 0x2120, 0x8528, 0x78c8, 0xa005,
+ 0x0040, 0x20d0, 0x85ff, 0x0040, 0x2139, 0x2091, 0x4080, 0x78b0,
+ 0x70d6, 0x007c, 0x7bac, 0x79b0, 0x70d4, 0xa102, 0x00c0, 0x2143,
+ 0x2300, 0xa005, 0x007c, 0x0048, 0x2147, 0xa302, 0x007c, 0x8002,
+ 0x007c, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8, 0x2163,
+ 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0, 0x2198,
+ 0x7008, 0x7208, 0xa206, 0x00c0, 0x2198, 0xa286, 0x0008, 0x00c0,
+ 0x2198, 0x2071, 0x0010, 0x1078, 0x219d, 0x2009, 0x0020, 0x6004,
+ 0xa086, 0x0103, 0x00c0, 0x2172, 0x6028, 0xa005, 0x00c0, 0x2172,
+ 0x2009, 0x000c, 0x1078, 0x1907, 0x0040, 0x218b, 0x78c4, 0x8000,
+ 0x78c6, 0xa086, 0x0002, 0x00c0, 0x2198, 0x2091, 0x8000, 0x78e3,
+ 0x0003, 0x78c7, 0x0000, 0x78cc, 0xa085, 0x0300, 0x78ce, 0x2091,
+ 0x8001, 0x0078, 0x2198, 0x78c7, 0x0000, 0x1078, 0x1c69, 0x79ac,
+ 0x78b0, 0x8000, 0xa10a, 0x00c8, 0x2196, 0xa006, 0x78b2, 0xa006,
+ 0x2071, 0x0010, 0x2091, 0x8001, 0x007c, 0x8107, 0x8004, 0x8004,
+ 0x7ab8, 0x7bb4, 0x7cc0, 0x7dbc, 0xa210, 0xa399, 0x0000, 0xa4a1,
+ 0x0000, 0xa5a9, 0x0000, 0x007c, 0x2009, 0x505b, 0x2091, 0x8000,
+ 0x200a, 0x0f7e, 0x0e7e, 0x2071, 0x5040, 0x7000, 0xa086, 0x0000,
+ 0x00c0, 0x21cb, 0x2009, 0x5012, 0x2104, 0xa005, 0x00c0, 0x21cb,
+ 0x2079, 0x0100, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x21cb, 0x0018,
+ 0x21cb, 0x781b, 0x004b, 0x0e7f, 0x0f7f, 0x007c, 0x0f7e, 0x0e7e,
+ 0x2071, 0x5040, 0x2091, 0x8000, 0x7000, 0xa086, 0x0000, 0x00c0,
+ 0x21e4, 0x2079, 0x0100, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x21e4,
+ 0x0018, 0x21e4, 0x781b, 0x004d, 0x2091, 0x8001, 0x0e7f, 0x0f7f,
+ 0x007c, 0x127e, 0x2091, 0x2300, 0x2071, 0x5040, 0x2079, 0x0100,
+ 0x784b, 0x000f, 0x0098, 0x21f7, 0x7838, 0x0078, 0x21f0, 0x20a9,
+ 0x0040, 0x7800, 0xa082, 0x0004, 0x0048, 0x2200, 0x20a9, 0x0060,
+ 0x789b, 0x0000, 0x78af, 0x0000, 0x78af, 0x0000, 0x0070, 0x220a,
+ 0x0078, 0x2202, 0x7800, 0xa082, 0x0004, 0x0048, 0x2219, 0x70b7,
+ 0x009b, 0x2019, 0x4da4, 0x1078, 0x2255, 0x702f, 0x8001, 0x0078,
+ 0x2225, 0x70b7, 0x0000, 0x2019, 0x4c1c, 0x1078, 0x2255, 0x2019,
+ 0x4c5b, 0x1078, 0x2255, 0x702f, 0x8000, 0x7003, 0x0000, 0x1078,
+ 0x235e, 0x7004, 0xa084, 0x000f, 0x017e, 0x2009, 0x04fd, 0x210c,
+ 0xa18a, 0x0005, 0x0048, 0x223a, 0x0038, 0x2240, 0xa085, 0x6280,
+ 0x0078, 0x2242, 0x0028, 0x2240, 0xa085, 0x6280, 0x0078, 0x2242,
+ 0xa085, 0x62c0, 0x017f, 0x7806, 0x780f, 0xb204, 0x7843, 0x00d8,
+ 0x7853, 0x0080, 0x780b, 0x0008, 0x7047, 0x0008, 0x7053, 0x507f,
+ 0x704f, 0x0000, 0x127f, 0x2000, 0x007c, 0x137e, 0x147e, 0x157e,
+ 0x047e, 0x20a1, 0x012b, 0x2304, 0xa005, 0x789a, 0x0040, 0x2275,
+ 0x8318, 0x2324, 0x8318, 0x2398, 0x24a8, 0xa484, 0xff00, 0x0040,
+ 0x226d, 0xa482, 0x0100, 0x20a9, 0x0100, 0x2020, 0x53a6, 0xa005,
+ 0x00c0, 0x2264, 0x3318, 0x0078, 0x225b, 0x047f, 0x157f, 0x147f,
+ 0x137f, 0x007c, 0xa18c, 0x000f, 0x2011, 0x0101, 0x2204, 0xa084,
+ 0xfff0, 0xa105, 0x2012, 0x1078, 0x235e, 0x007c, 0x2011, 0x0101,
+ 0x20a9, 0x0009, 0x810b, 0x0070, 0x228f, 0x0078, 0x228a, 0xa18c,
+ 0x0e00, 0x2204, 0xa084, 0xf1ff, 0xa105, 0x2012, 0x007c, 0x2009,
+ 0x0101, 0x20a9, 0x0005, 0x8213, 0x0070, 0x22a0, 0x0078, 0x229b,
+ 0xa294, 0x00e0, 0x2104, 0xa084, 0xff1f, 0xa205, 0x200a, 0x007c,
+ 0x2011, 0x0101, 0x20a9, 0x000c, 0x810b, 0x0070, 0x22b1, 0x0078,
+ 0x22ac, 0xa18c, 0xf000, 0x2204, 0xa084, 0x0fff, 0xa105, 0x2012,
+ 0x007c, 0x2011, 0x0102, 0x2204, 0xa084, 0xffcf, 0xa105, 0x2012,
+ 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e, 0x2061, 0x0100,
+ 0x609a, 0x62ac, 0x63ac, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080,
+ 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa084, 0xffdf,
+ 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0022, 0x0c7e,
+ 0x2061, 0x0100, 0x609a, 0x60a4, 0xa085, 0x0020, 0x60ae, 0x0c7f,
+ 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e, 0x2061, 0x0100,
+ 0x609a, 0x60a4, 0x62ae, 0x2010, 0x60a4, 0x63ae, 0x2018, 0x0c7f,
+ 0x007c, 0x2091, 0x8000, 0x0c7e, 0x0e7e, 0x6818, 0xa005, 0x0040,
+ 0x233c, 0x2061, 0x7400, 0x1078, 0x2344, 0x0040, 0x2328, 0x20a9,
+ 0x0000, 0x2061, 0x7300, 0x0c7e, 0x1078, 0x2344, 0x0040, 0x2318,
+ 0x0c7f, 0x8c60, 0x0070, 0x2316, 0x0078, 0x230b, 0x0078, 0x233c,
+ 0x007f, 0xa082, 0x7300, 0x2071, 0x5040, 0x7086, 0x7182, 0x2001,
+ 0x0004, 0x706e, 0x7093, 0x000f, 0x1078, 0x21ac, 0x0078, 0x2338,
+ 0x60c0, 0xa005, 0x00c0, 0x233c, 0x2071, 0x5040, 0x7182, 0x2c00,
+ 0x708a, 0x2001, 0x0006, 0x706e, 0x7093, 0x000f, 0x1078, 0x21ac,
+ 0x2001, 0x0000, 0x0078, 0x233e, 0x2001, 0x0001, 0x2091, 0x8001,
+ 0xa005, 0x0e7f, 0x0c7f, 0x007c, 0x2c04, 0xa005, 0x0040, 0x235b,
+ 0x2060, 0x600c, 0xa306, 0x00c0, 0x2358, 0x6010, 0xa206, 0x00c0,
+ 0x2358, 0x6014, 0xa106, 0x00c0, 0x2358, 0xa006, 0x0078, 0x235d,
+ 0x6000, 0x0078, 0x2345, 0xa085, 0x0001, 0x007c, 0x2011, 0x5041,
+ 0x220c, 0xa18c, 0x000f, 0x2011, 0x013b, 0x2204, 0xa084, 0x0100,
+ 0x0040, 0x2374, 0x2021, 0xff04, 0x2122, 0x810b, 0x810b, 0x810b,
+ 0x810b, 0xa18d, 0x0f00, 0x2104, 0x007c, 0x0e7e, 0x68e4, 0xa08c,
+ 0x0020, 0x0040, 0x23c8, 0xa084, 0x0006, 0x00c0, 0x23c8, 0x6014,
+ 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0f0, 0x5280,
+ 0x7004, 0xa084, 0x000a, 0x00c0, 0x23c8, 0x7108, 0xa194, 0xff00,
+ 0x0040, 0x23c8, 0xa18c, 0x00ff, 0x2001, 0x000c, 0xa106, 0x0040,
+ 0x23af, 0x2001, 0x0012, 0xa106, 0x0040, 0x23b3, 0x2001, 0x0014,
+ 0xa106, 0x0040, 0x23b7, 0x2001, 0x0019, 0xa106, 0x0040, 0x23bb,
+ 0x2001, 0x0032, 0xa106, 0x0040, 0x23bf, 0x0078, 0x23c3, 0x2009,
+ 0x0012, 0x0078, 0x23c5, 0x2009, 0x0014, 0x0078, 0x23c5, 0x2009,
+ 0x0019, 0x0078, 0x23c5, 0x2009, 0x0020, 0x0078, 0x23c5, 0x2009,
+ 0x003f, 0x0078, 0x23c5, 0x2011, 0x0000, 0x2100, 0xa205, 0x700a,
+ 0x0e7f, 0x007c, 0x0068, 0x23ca, 0x2091, 0x8000, 0x2071, 0x0000,
+ 0x007e, 0x7018, 0xa084, 0x0001, 0x00c0, 0x23d1, 0x007f, 0x2071,
+ 0x0010, 0x70ca, 0x007f, 0x70c6, 0x70c3, 0x8002, 0x70db, 0x073f,
+ 0x70df, 0x0000, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080,
+ 0x0078, 0x23e8, 0x107e, 0x007e, 0x127e, 0x2091, 0x2300, 0x7f3c,
+ 0x7e58, 0x7c30, 0x7d38, 0x77c2, 0x74c6, 0x76ca, 0x75ce, 0xa594,
+ 0x003f, 0xa49c, 0x0003, 0xa484, 0x000f, 0x0079, 0x23ff, 0x2411,
+ 0x2411, 0x2411, 0x274b, 0x3907, 0x240f, 0x2440, 0x244a, 0x240f,
+ 0x240f, 0x240f, 0x240f, 0x240f, 0x240f, 0x240f, 0x240f, 0x1078,
+ 0x23ca, 0x8507, 0xa084, 0x001f, 0x0079, 0x2416, 0x2454, 0x274b,
+ 0x2905, 0x2a02, 0x2a2a, 0x2cc3, 0x2f6e, 0x2fb1, 0x2ffc, 0x3081,
+ 0x3139, 0x31e2, 0x2440, 0x2827, 0x2f43, 0x2436, 0x3c78, 0x3c98,
+ 0x3e5e, 0x3e6a, 0x3f3f, 0x2436, 0x2436, 0x4012, 0x4016, 0x3c76,
+ 0x2436, 0x3dc9, 0x2436, 0x3b56, 0x244a, 0x2436, 0x1078, 0x23ca,
+ 0x0018, 0x23ef, 0x127f, 0x2091, 0x8001, 0x007f, 0x107f, 0x007c,
+ 0x2019, 0x4cfd, 0x1078, 0x2255, 0x702f, 0x0001, 0x781b, 0x004f,
+ 0x0078, 0x2438, 0x2019, 0x4c5b, 0x1078, 0x2255, 0x702f, 0x8000,
+ 0x781b, 0x00d5, 0x0078, 0x2438, 0x7242, 0x2009, 0x500f, 0x200b,
+ 0x0000, 0xa584, 0x0001, 0x00c0, 0x3b6a, 0x0040, 0x2471, 0x1078,
+ 0x23ca, 0x7003, 0x0000, 0x704b, 0x0000, 0x7043, 0x0000, 0x7037,
+ 0x0000, 0x1078, 0x38de, 0x0018, 0x23ef, 0x2009, 0x500f, 0x200b,
+ 0x0000, 0x7068, 0xa005, 0x00c0, 0x253c, 0x706c, 0xa084, 0x0007,
+ 0x0079, 0x247a, 0x2573, 0x2482, 0x248e, 0x24ab, 0x24cd, 0x251a,
+ 0x24f3, 0x2482, 0x1078, 0x38c6, 0x2009, 0x0048, 0x1078, 0x2e0f,
+ 0x00c0, 0x248c, 0x7003, 0x0004, 0x0078, 0x2438, 0x1078, 0x38c6,
+ 0x00c0, 0x24a9, 0x7080, 0x8007, 0x7882, 0x789b, 0x0010, 0x78ab,
+ 0x000c, 0x789b, 0x0060, 0x78ab, 0x0001, 0x785b, 0x0004, 0x2009,
+ 0x00e5, 0x1078, 0x2e03, 0x00c0, 0x24a9, 0x7003, 0x0004, 0x7093,
+ 0x000f, 0x0078, 0x2438, 0x1078, 0x38c6, 0x00c0, 0x24cb, 0x7180,
+ 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f, 0xa18d, 0x00c0,
+ 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002, 0x785b,
+ 0x0004, 0x2009, 0x00e5, 0x1078, 0x2e03, 0x00c0, 0x24cb, 0x7003,
+ 0x0004, 0x7093, 0x000f, 0x0078, 0x2438, 0x1078, 0x38c6, 0x00c0,
+ 0x24f1, 0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f,
+ 0xa18d, 0x00c0, 0x79aa, 0x78ab, 0x0020, 0x7184, 0x79aa, 0x78ab,
+ 0x000d, 0x789b, 0x0060, 0x78ab, 0x0004, 0x785b, 0x0004, 0x2009,
+ 0x00e5, 0x1078, 0x2e03, 0x00c0, 0x24f1, 0x7003, 0x0004, 0x7093,
+ 0x000f, 0x0078, 0x2438, 0x1078, 0x38c6, 0x00c0, 0x2518, 0x7180,
+ 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f, 0xa18d, 0x00c0,
+ 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002, 0x785b,
+ 0x0004, 0x2009, 0x00e5, 0x1078, 0x2e03, 0x00c0, 0x2518, 0x7088,
+ 0x708b, 0x0000, 0x2068, 0x704a, 0x7003, 0x0002, 0x7093, 0x000f,
+ 0x0078, 0x2438, 0x1078, 0x38c6, 0x00c0, 0x2438, 0x7088, 0x2068,
+ 0x6f14, 0x1078, 0x37bd, 0x2c50, 0x1078, 0x3978, 0x789b, 0x0010,
+ 0x6814, 0xa084, 0x001f, 0xa085, 0x0080, 0x78aa, 0x6e1c, 0x2041,
+ 0x0001, 0x708c, 0xa084, 0x0400, 0x2001, 0x0004, 0x0040, 0x253a,
+ 0x2001, 0x0006, 0x0078, 0x265b, 0x1078, 0x38c6, 0x00c0, 0x2438,
+ 0x789b, 0x0010, 0x7068, 0x2068, 0x6f14, 0x1078, 0x37bd, 0x2c50,
+ 0x1078, 0x3978, 0x6008, 0xa085, 0x0010, 0x600a, 0x6824, 0xa005,
+ 0x0040, 0x255a, 0xa082, 0x0006, 0x0048, 0x2558, 0x0078, 0x255a,
+ 0x6827, 0x0005, 0x6b14, 0xa39c, 0x001f, 0xa39d, 0x00c0, 0x7058,
+ 0xa084, 0x8000, 0x0040, 0x2568, 0xa684, 0x0001, 0x0040, 0x256a,
+ 0xa39c, 0xffbf, 0x7baa, 0x2031, 0x0020, 0x2041, 0x0001, 0x2001,
+ 0x0003, 0x0078, 0x265b, 0x0018, 0x23ef, 0x744c, 0xa485, 0x0000,
+ 0x0040, 0x258d, 0xa080, 0x5080, 0x2030, 0x7150, 0x8108, 0xa12a,
+ 0x0048, 0x2584, 0x2009, 0x5080, 0x2164, 0x6504, 0x85ff, 0x00c0,
+ 0x259e, 0x8421, 0x00c0, 0x257e, 0x7152, 0x7003, 0x0000, 0x704b,
+ 0x0000, 0x7040, 0xa005, 0x0040, 0x3b6a, 0x0078, 0x2438, 0x764c,
+ 0xa6b0, 0x5080, 0x7150, 0x2600, 0x0078, 0x2589, 0x7152, 0x2568,
+ 0x2558, 0x754a, 0x2c50, 0x6034, 0xa085, 0x0000, 0x00c0, 0x259b,
+ 0x6708, 0x773a, 0xa784, 0x033f, 0x0040, 0x25d4, 0xa784, 0x0021,
+ 0x00c0, 0x259b, 0xa784, 0x0002, 0x0040, 0x25bd, 0xa784, 0x0004,
+ 0x0040, 0x259b, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0008, 0x00c0,
+ 0x259b, 0xa784, 0x0010, 0x00c0, 0x259b, 0xa784, 0x0200, 0x00c0,
+ 0x259b, 0xa784, 0x0100, 0x0040, 0x25d4, 0x6018, 0xa005, 0x00c0,
+ 0x259b, 0xa7bc, 0xfeff, 0x670a, 0x6823, 0x0000, 0x6e1c, 0xa684,
+ 0x000e, 0x6118, 0x0040, 0x25e4, 0x601c, 0xa102, 0x0048, 0x25e7,
+ 0x0040, 0x25e7, 0x0078, 0x2597, 0x81ff, 0x00c0, 0x2597, 0x68c3,
+ 0x0000, 0xa784, 0x0080, 0x00c0, 0x25ef, 0x700c, 0x6022, 0xa7bc,
+ 0xff7f, 0x670a, 0x1078, 0x3978, 0x0018, 0x23ef, 0x789b, 0x0010,
+ 0xa046, 0x1078, 0x38c6, 0x00c0, 0x2438, 0x6b14, 0xa39c, 0x001f,
+ 0xa39d, 0x00c0, 0x7058, 0xa084, 0x8000, 0x0040, 0x260b, 0xa684,
+ 0x0001, 0x0040, 0x260d, 0xa39c, 0xffbf, 0xa684, 0x0010, 0x0040,
+ 0x2613, 0xa39d, 0x0020, 0x7baa, 0x8840, 0xa684, 0x000e, 0x00c0,
+ 0x261e, 0xa7bd, 0x0010, 0x670a, 0x0078, 0x2659, 0x7158, 0xa18c,
+ 0x0800, 0x0040, 0x33d7, 0x2011, 0x0020, 0xa684, 0x0008, 0x00c0,
+ 0x262f, 0x8210, 0xa684, 0x0002, 0x00c0, 0x262f, 0x8210, 0x7aaa,
+ 0x8840, 0x1078, 0x38de, 0x6a14, 0x610c, 0x8108, 0xa18c, 0x00ff,
+ 0xa1e0, 0x7300, 0x2c64, 0x8cff, 0x0040, 0x2650, 0x6014, 0xa206,
+ 0x00c0, 0x263a, 0x60b8, 0x8001, 0x60ba, 0x00c0, 0x2635, 0x0c7e,
+ 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078, 0x2573,
+ 0x1078, 0x38c6, 0x00c0, 0x2438, 0x2a60, 0x610e, 0x79aa, 0x8840,
+ 0x7132, 0x2001, 0x0001, 0x007e, 0x715c, 0xa184, 0x0018, 0x0040,
+ 0x2676, 0xa184, 0x0010, 0x0040, 0x2669, 0x1078, 0x35d6, 0x00c0,
+ 0x2699, 0xa184, 0x0008, 0x0040, 0x2676, 0x69a0, 0xa184, 0x0600,
+ 0x00c0, 0x2676, 0x1078, 0x34c7, 0x0078, 0x2699, 0x69a0, 0xa184,
+ 0x0800, 0x0040, 0x268d, 0x0c7e, 0x027e, 0x2960, 0x6000, 0xa085,
+ 0x2000, 0x6002, 0x6104, 0xa18d, 0x0010, 0x6106, 0x027f, 0x0c7f,
+ 0x1078, 0x35d6, 0x00c0, 0x2699, 0x69a0, 0xa184, 0x0200, 0x0040,
+ 0x2695, 0x1078, 0x3516, 0x0078, 0x2699, 0xa184, 0x0400, 0x00c0,
+ 0x2672, 0x69a0, 0xa184, 0x1000, 0x0040, 0x26a4, 0x6914, 0xa18c,
+ 0xff00, 0x810f, 0x1078, 0x22cd, 0x007f, 0x7002, 0xa68c, 0x00e0,
+ 0xa684, 0x0060, 0x0040, 0x26b2, 0xa086, 0x0060, 0x00c0, 0x26b2,
+ 0xa18d, 0x4000, 0x88ff, 0x0040, 0x26b7, 0xa18d, 0x0004, 0x795a,
+ 0x69b6, 0x789b, 0x0060, 0x2800, 0x78aa, 0x789b, 0x0061, 0x6818,
+ 0xa08d, 0x8000, 0xa084, 0x7fff, 0x691a, 0xa68c, 0x0080, 0x0040,
+ 0x26d6, 0x7097, 0x0000, 0xa08a, 0x000d, 0x0050, 0x26d4, 0xa08a,
+ 0x000c, 0x7196, 0x2001, 0x000c, 0x800c, 0x719a, 0x78aa, 0x8008,
+ 0x810c, 0x0040, 0x33dd, 0xa18c, 0x00f8, 0x00c0, 0x33dd, 0x157e,
+ 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000, 0x80ac,
+ 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x6814,
+ 0x8007, 0x7882, 0x6d94, 0x7dd6, 0x7dde, 0x6e98, 0x7ed2, 0x7eda,
+ 0x1078, 0x38c6, 0x00c0, 0x270d, 0x702c, 0x8003, 0x0048, 0x2706,
+ 0x2019, 0x4c5b, 0x1078, 0x2255, 0x702f, 0x8000, 0x7830, 0xa084,
+ 0x00c0, 0x00c0, 0x270d, 0x0098, 0x2715, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x38de, 0x0078, 0x2461, 0x7200, 0xa284, 0x0007,
+ 0xa086, 0x0001, 0x00c0, 0x2722, 0x781b, 0x004f, 0x1078, 0x38de,
+ 0x0078, 0x2733, 0x6ab4, 0xa295, 0x2000, 0x7a5a, 0x781b, 0x004f,
+ 0x1078, 0x38de, 0x7200, 0x2500, 0xa605, 0x0040, 0x2733, 0xa284,
+ 0x0007, 0x1079, 0x2741, 0xad80, 0x0009, 0x7036, 0xa284, 0x0007,
+ 0xa086, 0x0001, 0x00c0, 0x2438, 0x6018, 0x8000, 0x601a, 0x0078,
+ 0x2438, 0x2749, 0x48f7, 0x48f7, 0x48e6, 0x48f7, 0x2749, 0x48e6,
+ 0x2749, 0x1078, 0x23ca, 0x1078, 0x38c6, 0x0f7e, 0x2079, 0x5000,
+ 0x78cc, 0x0f7f, 0xa084, 0x0001, 0x0040, 0x276f, 0x706c, 0xa086,
+ 0x0001, 0x00c0, 0x275e, 0x706e, 0x0078, 0x2802, 0x706c, 0xa086,
+ 0x0005, 0x00c0, 0x276d, 0x7088, 0x2068, 0x681b, 0x0004, 0x6817,
+ 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x706f, 0x0000, 0x2011,
+ 0x0004, 0x716c, 0xa186, 0x0001, 0x0040, 0x2790, 0xa186, 0x0007,
+ 0x00c0, 0x2780, 0x2009, 0x5038, 0x200b, 0x0005, 0x0078, 0x2790,
+ 0x2009, 0x5013, 0x2104, 0x2009, 0x5012, 0x200a, 0x2009, 0x5038,
+ 0x200b, 0x0001, 0x706f, 0x0000, 0x7073, 0x0001, 0x0078, 0x2792,
+ 0x706f, 0x0000, 0x1078, 0x4633, 0x157e, 0x20a9, 0x0010, 0x2039,
+ 0x0000, 0x1078, 0x36b0, 0xa7b8, 0x0100, 0x0070, 0x27a1, 0x0078,
+ 0x2799, 0x157f, 0x7000, 0x0079, 0x27a5, 0x27d3, 0x27ba, 0x27ba,
+ 0x27ad, 0x27d3, 0x27d3, 0x27d3, 0x27d3, 0x2021, 0x505a, 0x2404,
+ 0xa005, 0x0040, 0x27d3, 0xad06, 0x00c0, 0x27ba, 0x6800, 0x2022,
+ 0x0078, 0x27ca, 0x6820, 0xa084, 0x0001, 0x00c0, 0x27c6, 0x6f14,
+ 0x1078, 0x37bd, 0x1078, 0x33ae, 0x0078, 0x27ca, 0x7060, 0x2060,
+ 0x6800, 0x6002, 0x6a1a, 0x6817, 0x0000, 0x6820, 0xa085, 0x0008,
+ 0x6822, 0x1078, 0x1c53, 0x2021, 0x7400, 0x1078, 0x280f, 0x2021,
+ 0x505a, 0x1078, 0x280f, 0x157e, 0x20a9, 0x0000, 0x2021, 0x7300,
+ 0x1078, 0x280f, 0x8420, 0x0070, 0x27e7, 0x0078, 0x27e0, 0x2061,
+ 0x5300, 0x2021, 0x0002, 0x20a9, 0x0100, 0x6018, 0x6110, 0x81ff,
+ 0x0040, 0x27f6, 0xa102, 0x0050, 0x27f6, 0x6012, 0x601b, 0x0000,
+ 0xace0, 0x0010, 0x0070, 0x27fe, 0x0078, 0x27ed, 0x8421, 0x00c0,
+ 0x27eb, 0x157f, 0x709c, 0xa084, 0x8000, 0x0040, 0x2809, 0x1078,
+ 0x39cc, 0x7003, 0x0000, 0x704b, 0x0000, 0x0078, 0x2438, 0x047e,
+ 0x2404, 0xa005, 0x0040, 0x2823, 0x2068, 0x6800, 0x007e, 0x6a1a,
+ 0x6817, 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x1078, 0x1c53,
+ 0x007f, 0x0078, 0x2811, 0x047f, 0x2023, 0x0000, 0x007c, 0xa282,
+ 0x0003, 0x0050, 0x282d, 0x1078, 0x23ca, 0x2300, 0x0079, 0x2830,
+ 0x2833, 0x28a6, 0x28c3, 0xa282, 0x0002, 0x0040, 0x2839, 0x1078,
+ 0x23ca, 0x706c, 0x706f, 0x0000, 0x7093, 0x0000, 0x0079, 0x2840,
+ 0x2848, 0x2848, 0x284a, 0x287e, 0x33e3, 0x2848, 0x287e, 0x2848,
+ 0x1078, 0x23ca, 0x7780, 0x1078, 0x36b0, 0x7780, 0xa7bc, 0x0f00,
+ 0x1078, 0x37bd, 0x6018, 0xa005, 0x0040, 0x2875, 0x2021, 0x7400,
+ 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x28de, 0x0040, 0x2875,
+ 0x157e, 0x20a9, 0x0000, 0x2021, 0x7300, 0x047e, 0x2009, 0x0004,
+ 0x2011, 0x0010, 0x1078, 0x28de, 0x047f, 0x0040, 0x2874, 0x8420,
+ 0x0070, 0x2874, 0x0078, 0x2865, 0x157f, 0x8738, 0xa784, 0x001f,
+ 0x00c0, 0x2850, 0x0078, 0x2461, 0x0078, 0x2461, 0x7780, 0x1078,
+ 0x37bd, 0x6018, 0xa005, 0x0040, 0x28a4, 0x2021, 0x7400, 0x2009,
+ 0x0005, 0x2011, 0x0020, 0x1078, 0x28de, 0x0040, 0x28a4, 0x157e,
+ 0x20a9, 0x0000, 0x2021, 0x7300, 0x047e, 0x2009, 0x0005, 0x2011,
+ 0x0020, 0x1078, 0x28de, 0x047f, 0x0040, 0x28a3, 0x8420, 0x0070,
+ 0x28a3, 0x0078, 0x2894, 0x157f, 0x0078, 0x2461, 0x2200, 0x0079,
+ 0x28a9, 0x28ac, 0x28ae, 0x28ae, 0x1078, 0x23ca, 0x2009, 0x0012,
+ 0x706c, 0xa086, 0x0002, 0x0040, 0x28b7, 0x2009, 0x000e, 0x6818,
+ 0xa084, 0x8000, 0x0040, 0x28bd, 0x691a, 0x706f, 0x0000, 0x7073,
+ 0x0001, 0x0078, 0x3854, 0x2200, 0x0079, 0x28c6, 0x28cb, 0x28ae,
+ 0x28c9, 0x1078, 0x23ca, 0x1078, 0x4633, 0x7000, 0xa086, 0x0001,
+ 0x00c0, 0x3373, 0x1078, 0x33c4, 0x6008, 0xa084, 0xffef, 0x600a,
+ 0x1078, 0x3366, 0x0040, 0x3373, 0x0078, 0x2573, 0x2404, 0xa005,
+ 0x0040, 0x2901, 0x2068, 0x2d04, 0x007e, 0x6814, 0xa706, 0x0040,
+ 0x28ed, 0x2d20, 0x007f, 0x0078, 0x28df, 0x007f, 0x2022, 0x691a,
+ 0x6817, 0x0000, 0x6820, 0xa205, 0x6822, 0x1078, 0x1c53, 0x6010,
+ 0x8001, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x33c4,
+ 0x007c, 0xa085, 0x0001, 0x0078, 0x2900, 0x2300, 0x0079, 0x2908,
+ 0x290d, 0x290b, 0x29a6, 0x1078, 0x23ca, 0x78ec, 0xa084, 0x0001,
+ 0x00c0, 0x2921, 0x7000, 0xa086, 0x0004, 0x00c0, 0x2919, 0x0078,
+ 0x2944, 0x1078, 0x33c4, 0x6008, 0xa084, 0xffef, 0x600a, 0x0078,
+ 0x3373, 0x78e4, 0xa005, 0x00d0, 0x2944, 0x0018, 0x2438, 0x2008,
+ 0xa084, 0x0030, 0x00c0, 0x2930, 0x781b, 0x004f, 0x0078, 0x2438,
+ 0x78ec, 0xa084, 0x0003, 0x0040, 0x292c, 0x2100, 0xa084, 0x0007,
+ 0x0079, 0x293a, 0x297d, 0x2988, 0x296e, 0x2942, 0x38b9, 0x38b9,
+ 0x2942, 0x2997, 0x1078, 0x23ca, 0x7000, 0xa086, 0x0004, 0x00c0,
+ 0x295e, 0x706c, 0xa086, 0x0002, 0x00c0, 0x2954, 0x2011, 0x0002,
+ 0x2019, 0x0000, 0x0078, 0x2827, 0x706c, 0xa086, 0x0006, 0x0040,
+ 0x294e, 0x706c, 0xa086, 0x0004, 0x0040, 0x294e, 0x79e4, 0xa184,
+ 0x0030, 0x0040, 0x2968, 0x78ec, 0xa084, 0x0003, 0x00c0, 0x296a,
+ 0x0078, 0x2f43, 0x2001, 0x0003, 0x0078, 0x2cd7, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x2975, 0x681b, 0x001d, 0x1078, 0x368f, 0x782b,
+ 0x3008, 0x781b, 0x0056, 0x0078, 0x2438, 0x6818, 0xa084, 0x8000,
+ 0x0040, 0x2984, 0x681b, 0x001d, 0x1078, 0x368f, 0x0078, 0x3884,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x298f, 0x681b, 0x001d, 0x1078,
+ 0x368f, 0x782b, 0x3008, 0x781b, 0x00d2, 0x0078, 0x2438, 0x6818,
+ 0xa084, 0x8000, 0x0040, 0x299e, 0x681b, 0x001d, 0x1078, 0x368f,
+ 0x782b, 0x3008, 0x781b, 0x0093, 0x0078, 0x2438, 0xa584, 0x000f,
+ 0x00c0, 0x29c3, 0x7000, 0x0079, 0x29ad, 0x2461, 0x29b7, 0x29b5,
+ 0x3373, 0x3373, 0x3373, 0x3373, 0x29b5, 0x1078, 0x23ca, 0x1078,
+ 0x33c4, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x3366, 0x0040,
+ 0x3373, 0x0078, 0x2573, 0x78e4, 0xa005, 0x00d0, 0x2944, 0x0018,
+ 0x2944, 0x2008, 0xa084, 0x0030, 0x00c0, 0x29d2, 0x781b, 0x004f,
+ 0x0078, 0x2438, 0x78ec, 0xa084, 0x0003, 0x0040, 0x29ce, 0x2100,
+ 0xa184, 0x0007, 0x0079, 0x29dc, 0x29ee, 0x29f2, 0x29e6, 0x29e4,
+ 0x38b9, 0x38b9, 0x29e4, 0x38af, 0x1078, 0x23ca, 0x1078, 0x3697,
+ 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2438, 0x1078, 0x3697,
+ 0x0078, 0x3884, 0x1078, 0x3697, 0x782b, 0x3008, 0x781b, 0x00d2,
+ 0x0078, 0x2438, 0x1078, 0x3697, 0x782b, 0x3008, 0x781b, 0x0093,
+ 0x0078, 0x2438, 0x2300, 0x0079, 0x2a05, 0x2a0a, 0x2a08, 0x2a0c,
+ 0x1078, 0x23ca, 0x0078, 0x3081, 0x681b, 0x0008, 0x78a3, 0x0000,
+ 0x79e4, 0xa184, 0x0030, 0x0040, 0x3081, 0x78ec, 0xa084, 0x0003,
+ 0x0040, 0x3081, 0xa184, 0x0007, 0x0079, 0x2a1e, 0x2a26, 0x29f2,
+ 0x296e, 0x3854, 0x38b9, 0x38b9, 0x2a26, 0x38af, 0x1078, 0x3868,
+ 0x0078, 0x2438, 0xa282, 0x0005, 0x0050, 0x2a30, 0x1078, 0x23ca,
+ 0x2300, 0x0079, 0x2a33, 0x2a36, 0x2c84, 0x2c92, 0x2200, 0x0079,
+ 0x2a39, 0x2a53, 0x2a40, 0x2a53, 0x2a3e, 0x2c69, 0x1078, 0x23ca,
+ 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa082, 0x0020, 0x0048,
+ 0x366b, 0xa08a, 0x0004, 0x00c8, 0x366b, 0x0079, 0x2a4f, 0x366b,
+ 0x366b, 0x366b, 0x3619, 0x789b, 0x0018, 0x79a8, 0xa184, 0x0080,
+ 0x0040, 0x2a64, 0x0078, 0x366b, 0x7000, 0xa005, 0x00c0, 0x2a5a,
+ 0x2011, 0x0004, 0x0078, 0x31f5, 0xa184, 0x00ff, 0xa08a, 0x0010,
+ 0x00c8, 0x366b, 0x0079, 0x2a6c, 0x2a7e, 0x2a7c, 0x2a96, 0x2a9a,
+ 0x2b55, 0x366b, 0x366b, 0x2b57, 0x366b, 0x366b, 0x2c65, 0x2c65,
+ 0x366b, 0x366b, 0x366b, 0x2c67, 0x1078, 0x23ca, 0xa684, 0x1000,
+ 0x0040, 0x2a8b, 0x2001, 0x0500, 0x8000, 0x8000, 0x783a, 0x781b,
+ 0x0091, 0x0078, 0x2438, 0x6818, 0xa084, 0x8000, 0x0040, 0x2a94,
+ 0x681b, 0x001d, 0x0078, 0x2a82, 0x0078, 0x3854, 0x681b, 0x001d,
+ 0x0078, 0x367b, 0x6920, 0x6922, 0xa684, 0x1800, 0x00c0, 0x2adb,
+ 0x6820, 0xa084, 0x0001, 0x00c0, 0x2ae1, 0x6818, 0xa086, 0x0008,
+ 0x00c0, 0x2aac, 0x681b, 0x0000, 0xa684, 0x0400, 0x0040, 0x2b51,
+ 0xa684, 0x0080, 0x0040, 0x2ad7, 0x7097, 0x0000, 0x6818, 0xa084,
+ 0x003f, 0xa08a, 0x000d, 0x0050, 0x2ad7, 0xa08a, 0x000c, 0x7196,
+ 0x2001, 0x000c, 0x800c, 0x719a, 0x789b, 0x0061, 0x78aa, 0x157e,
+ 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000, 0x80ac,
+ 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x781b,
+ 0x0058, 0x0078, 0x2438, 0xa684, 0x1000, 0x0040, 0x2ae1, 0x0078,
+ 0x2438, 0xa684, 0x0060, 0x0040, 0x2b4d, 0xa684, 0x0800, 0x0040,
+ 0x2b4d, 0xa684, 0x8000, 0x00c0, 0x2aef, 0x0078, 0x2b09, 0xa6b4,
+ 0x7fff, 0x7e5a, 0x6eb6, 0x789b, 0x0076, 0x7aac, 0x79ac, 0x78ac,
+ 0x801b, 0x00c8, 0x2afc, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291,
+ 0x0000, 0x6b98, 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303,
+ 0x68ae, 0xa684, 0x4000, 0x0040, 0x2b11, 0xa6b4, 0xbfff, 0x7e5a,
+ 0x6eb6, 0x7000, 0xa086, 0x0003, 0x00c0, 0x2b1e, 0x1078, 0x46e9,
+ 0x1078, 0x48e6, 0x781b, 0x0064, 0x0078, 0x2438, 0xa006, 0x1078,
+ 0x49ed, 0x6ab0, 0x69ac, 0x6c98, 0x6b94, 0x2200, 0xa105, 0x0040,
+ 0x2b2d, 0x2200, 0xa422, 0x2100, 0xa31b, 0x6caa, 0x7cd2, 0x7cda,
+ 0x6ba6, 0x7bd6, 0x7bde, 0x2300, 0xa405, 0x00c0, 0x2b3f, 0xa6b5,
+ 0x4000, 0x7e5a, 0x6eb6, 0x781b, 0x0064, 0x0078, 0x2438, 0x781b,
+ 0x0064, 0x2200, 0xa115, 0x00c0, 0x2b49, 0x1078, 0x48f7, 0x0078,
+ 0x2438, 0x1078, 0x4942, 0x0078, 0x2438, 0x781b, 0x0065, 0x0078,
+ 0x2438, 0x781b, 0x0058, 0x0078, 0x2438, 0x1078, 0x23ca, 0x0078,
+ 0x2bb8, 0x6920, 0xa184, 0x0100, 0x0040, 0x2b6f, 0xa18c, 0xfeff,
+ 0x6922, 0x0c7e, 0x7054, 0x2060, 0x6000, 0xa084, 0xefff, 0x6002,
+ 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x0078, 0x2ba7, 0xa184,
+ 0x0200, 0x0040, 0x2ba7, 0xa18c, 0xfdff, 0x6922, 0x0c7e, 0x7054,
+ 0x2060, 0x6000, 0xa084, 0xdfff, 0x6002, 0x6004, 0xa084, 0xffef,
+ 0x6006, 0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2ba7,
+ 0x1078, 0x37b9, 0x1078, 0x34c7, 0x88ff, 0x0040, 0x2ba7, 0x789b,
+ 0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684,
+ 0x0400, 0x00c0, 0x2ba1, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078,
+ 0x2438, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x7e58,
+ 0xa684, 0x0400, 0x00c0, 0x2bb0, 0x781b, 0x0058, 0x0078, 0x2438,
+ 0x781b, 0x0065, 0x0078, 0x2438, 0x0078, 0x3673, 0x0078, 0x3673,
+ 0x2019, 0x0000, 0x7990, 0xa18c, 0x0007, 0x0040, 0x2bb6, 0x789b,
+ 0x0010, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001, 0x00c0, 0x2bf6,
+ 0x2300, 0x7ca8, 0xa400, 0x2018, 0xa102, 0x0040, 0x2bee, 0x0048,
+ 0x2bd3, 0x0078, 0x2bf0, 0xa380, 0x0002, 0xa102, 0x00c8, 0x2bee,
+ 0x6920, 0xa18c, 0xfcff, 0x6922, 0x0c7e, 0x7054, 0x2060, 0x6000,
+ 0xa084, 0xefef, 0x6002, 0x6004, 0xa084, 0xffe5, 0x6006, 0x0c7f,
+ 0x7e58, 0xa6b4, 0xfffb, 0x7e5a, 0x0078, 0x2ba8, 0x0078, 0x2b59,
+ 0x24a8, 0x7aa8, 0x00f0, 0x2bf0, 0x0078, 0x2bc1, 0xa284, 0x00f0,
+ 0xa086, 0x0020, 0x00c0, 0x2c56, 0x8318, 0x8318, 0x2300, 0xa102,
+ 0x0040, 0x2c06, 0x0048, 0x2c06, 0x0078, 0x2c53, 0xa286, 0x0023,
+ 0x0040, 0x2bb6, 0x681c, 0xa084, 0xfff1, 0x681e, 0x7e58, 0xa684,
+ 0xfff1, 0xa085, 0x0010, 0x2030, 0x7e5a, 0x6008, 0xa085, 0x0010,
+ 0x600a, 0x0c7e, 0x7054, 0x2060, 0x6004, 0x2008, 0x2c48, 0x0c7f,
+ 0xa184, 0x0010, 0x0040, 0x2c2a, 0x1078, 0x37b9, 0x1078, 0x35d6,
+ 0x0078, 0x2c39, 0x0c7e, 0x7054, 0x2060, 0x6004, 0x2008, 0x2c48,
+ 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2ba7, 0x1078, 0x37b9, 0x1078,
+ 0x34c7, 0x88ff, 0x0040, 0x2ba7, 0x789b, 0x0060, 0x2800, 0x78aa,
+ 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2c4d, 0x782b,
+ 0x3008, 0x781b, 0x0056, 0x0078, 0x2438, 0x782b, 0x3008, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0x7aa8, 0x0078, 0x2bc1, 0x8318, 0x2300,
+ 0xa102, 0x0040, 0x2c5f, 0x0048, 0x2c5f, 0x0078, 0x2bc1, 0xa284,
+ 0x0080, 0x00c0, 0x367b, 0x0078, 0x3673, 0x0078, 0x367b, 0x0078,
+ 0x366b, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa08e, 0x0001,
+ 0x0040, 0x2c74, 0x1078, 0x23ca, 0x7aa8, 0xa294, 0x00ff, 0x78a8,
+ 0xa084, 0x00ff, 0xa08a, 0x0004, 0x00c8, 0x366b, 0x0079, 0x2c80,
+ 0x366b, 0x3414, 0x366b, 0x356b, 0xa282, 0x0000, 0x00c0, 0x2c8a,
+ 0x1078, 0x23ca, 0x1078, 0x368f, 0x782b, 0x3008, 0x781b, 0x0065,
+ 0x0078, 0x2438, 0xa282, 0x0003, 0x00c0, 0x2c98, 0x1078, 0x23ca,
+ 0xa484, 0x8000, 0x00c0, 0x2cbb, 0x706c, 0xa005, 0x0040, 0x2ca2,
+ 0x1078, 0x23ca, 0x6f14, 0x7782, 0xa7bc, 0x0f00, 0x1078, 0x37bd,
+ 0x6008, 0xa085, 0x0021, 0x600a, 0x8738, 0xa784, 0x001f, 0x00c0,
+ 0x2ca6, 0x1078, 0x3693, 0x706f, 0x0002, 0x2009, 0x5038, 0x200b,
+ 0x0009, 0x0078, 0x2cbd, 0x1078, 0x369f, 0x782b, 0x3008, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0xa282, 0x0004, 0x0050, 0x2cc9, 0x1078,
+ 0x23ca, 0x2300, 0x0079, 0x2ccc, 0x2ccf, 0x2db8, 0x2deb, 0xa286,
+ 0x0003, 0x0040, 0x2cd5, 0x1078, 0x23ca, 0x2001, 0x0000, 0x007e,
+ 0x68c0, 0xa005, 0x0040, 0x2cde, 0x7003, 0x0003, 0x68a0, 0xa084,
+ 0x2000, 0x0040, 0x2ce7, 0x6008, 0xa085, 0x0002, 0x600a, 0x007f,
+ 0x703e, 0x7000, 0xa084, 0x0007, 0x0079, 0x2cee, 0x2461, 0x2cf8,
+ 0x2cf8, 0x2eed, 0x2f29, 0x2461, 0x2f29, 0x2cf6, 0x1078, 0x23ca,
+ 0xa684, 0x1000, 0x00c0, 0x2d00, 0x1078, 0x4633, 0x0040, 0x2d92,
+ 0x7868, 0xa08c, 0x00ff, 0x0040, 0x2d48, 0xa186, 0x0008, 0x00c0,
+ 0x2d17, 0x1078, 0x33c4, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078,
+ 0x3366, 0x0040, 0x2d48, 0x1078, 0x4633, 0x0078, 0x2d2f, 0xa186,
+ 0x0028, 0x00c0, 0x2d48, 0x1078, 0x4633, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x6018, 0xa005, 0x0040, 0x2d2f, 0x8001, 0x601a, 0xa005,
+ 0x0040, 0x2d2f, 0x8001, 0xa005, 0x0040, 0x2d2f, 0x601e, 0x6820,
+ 0xa084, 0x0001, 0x0040, 0x2461, 0x6820, 0xa084, 0xfffe, 0x6822,
+ 0x7060, 0x0c7e, 0x2060, 0x6800, 0x6002, 0x0c7f, 0x6004, 0x6802,
+ 0xa005, 0x2d00, 0x00c0, 0x2d45, 0x6002, 0x6006, 0x0078, 0x2461,
+ 0x017e, 0x1078, 0x2e1c, 0x017f, 0xa684, 0xdf00, 0x681e, 0x682b,
+ 0x0000, 0x6f14, 0x81ff, 0x0040, 0x2d92, 0xa186, 0x0002, 0x00c0,
+ 0x2d92, 0xa684, 0x0800, 0x00c0, 0x2d65, 0xa684, 0x0060, 0x0040,
+ 0x2d65, 0x78d8, 0x7adc, 0x682e, 0x6a32, 0x6820, 0xa084, 0x0800,
+ 0x00c0, 0x2d92, 0x8717, 0xa294, 0x000f, 0x8213, 0x8213, 0x8213,
+ 0xa290, 0x5280, 0xa290, 0x0000, 0x221c, 0xa384, 0x0100, 0x00c0,
+ 0x2d7b, 0x0078, 0x2d81, 0x8210, 0x2204, 0xa085, 0x0018, 0x2012,
+ 0x8211, 0xa384, 0x0400, 0x0040, 0x2d8e, 0x68a0, 0xa084, 0x0100,
+ 0x00c0, 0x2d8e, 0x1078, 0x2ea0, 0x0078, 0x2461, 0x6008, 0xa085,
+ 0x0002, 0x600a, 0x6916, 0x6818, 0xa084, 0x8000, 0x0040, 0x2d9a,
+ 0x703c, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x1078, 0x33b5, 0x1078,
+ 0x33c4, 0x00c0, 0x2da7, 0x6008, 0xa084, 0xffef, 0x600a, 0x6820,
+ 0xa084, 0x0001, 0x00c0, 0x2db0, 0x1078, 0x33ae, 0x0078, 0x2db4,
+ 0x7060, 0x2060, 0x6800, 0x6002, 0x1078, 0x1c53, 0x0078, 0x2461,
+ 0xa282, 0x0004, 0x0048, 0x2dbe, 0x1078, 0x23ca, 0x2200, 0x0079,
+ 0x2dc1, 0x2dbc, 0x2dc5, 0x2dd2, 0x2dc5, 0x7000, 0xa086, 0x0005,
+ 0x0040, 0x2dce, 0x1078, 0x368f, 0x782b, 0x3008, 0x781b, 0x0065,
+ 0x0078, 0x2438, 0x7890, 0x8007, 0x8001, 0xa084, 0x0007, 0xa080,
+ 0x0018, 0x789a, 0x79a8, 0xa18c, 0x00ff, 0xa186, 0x0003, 0x0040,
+ 0x2de7, 0xa186, 0x0000, 0x0040, 0x2de7, 0x0078, 0x366b, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0x6820, 0xa085, 0x0004, 0x6822, 0x82ff,
+ 0x00c0, 0x2df6, 0x1078, 0x368f, 0x0078, 0x2dfd, 0x8211, 0x0040,
+ 0x2dfb, 0x1078, 0x23ca, 0x1078, 0x369f, 0x782b, 0x3008, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0x702c, 0x8003, 0x0048, 0x2e0d, 0x2019,
+ 0x4c5b, 0x1078, 0x2255, 0x702f, 0x8000, 0x1078, 0x38de, 0x7830,
+ 0xa084, 0x00c0, 0x00c0, 0x2e19, 0x0018, 0x2e19, 0x791a, 0xa006,
+ 0x007c, 0xa085, 0x0001, 0x007c, 0xa684, 0x0060, 0x00c0, 0x2e26,
+ 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2e9f, 0xa684, 0x0800,
+ 0x00c0, 0x2e48, 0x68b4, 0xa084, 0x4800, 0xa635, 0xa684, 0x0800,
+ 0x00c0, 0x2e48, 0x6998, 0x6a94, 0x692e, 0x6a32, 0x703c, 0xa005,
+ 0x00c0, 0x2e40, 0x2200, 0xa105, 0x0040, 0x2e47, 0x703f, 0x0015,
+ 0x7000, 0xa086, 0x0006, 0x0040, 0x2e47, 0x1078, 0x4633, 0x007c,
+ 0xa684, 0x0020, 0x0040, 0x2e6a, 0xa684, 0x4000, 0x0040, 0x2e56,
+ 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2e40, 0x68b4, 0xa084,
+ 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2e50, 0x703c, 0xa005,
+ 0x00c0, 0x2e64, 0x703f, 0x0015, 0x79d8, 0x7adc, 0x692e, 0x6a32,
+ 0x0078, 0x2e40, 0xa684, 0x4000, 0x0040, 0x2e74, 0x682f, 0x0000,
+ 0x6833, 0x0000, 0x0078, 0x2e40, 0x68b4, 0xa084, 0x4800, 0xa635,
+ 0xa684, 0x4000, 0x00c0, 0x2e6e, 0x703c, 0xa005, 0x00c0, 0x2e82,
+ 0x703f, 0x0015, 0x79d8, 0x7adc, 0x78d0, 0x80fb, 0x00c8, 0x2e89,
+ 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x692e, 0x6a32,
+ 0x2100, 0xa205, 0x00c0, 0x2e96, 0x0078, 0x2e40, 0x7000, 0xa086,
+ 0x0006, 0x0040, 0x2e9f, 0x1078, 0x49ed, 0x0078, 0x2e40, 0x007c,
+ 0x6008, 0xa085, 0x0200, 0x600a, 0xa384, 0x0200, 0x0040, 0x2eac,
+ 0x6008, 0xa085, 0x0002, 0x600a, 0x681b, 0x0006, 0x688f, 0x0000,
+ 0x6893, 0x0000, 0x6a30, 0x692c, 0x6a3e, 0x6942, 0x682f, 0x0003,
+ 0x6833, 0x0000, 0x6837, 0x0020, 0x6897, 0x0000, 0x689b, 0x0020,
+ 0x68b3, 0x0000, 0x68af, 0x0000, 0x7000, 0x0079, 0x2ec7, 0x2461,
+ 0x2ed1, 0x2eda, 0x2ecf, 0x2ecf, 0x2ecf, 0x2ecf, 0x2ecf, 0x1078,
+ 0x23ca, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2eda, 0x1078, 0x33ae,
+ 0x0078, 0x2ee0, 0x7060, 0x2c50, 0x2060, 0x6800, 0x6002, 0x2a60,
+ 0x2021, 0x505a, 0x2404, 0xa005, 0x0040, 0x2ee9, 0x2020, 0x0078,
+ 0x2ee2, 0x2d22, 0x206b, 0x0000, 0x007c, 0x1078, 0x33b5, 0x1078,
+ 0x33c4, 0x6008, 0xa084, 0xfdff, 0x600a, 0x682b, 0x0000, 0x789b,
+ 0x000e, 0x6f14, 0x6817, 0x0002, 0x1078, 0x4a35, 0xa684, 0x0800,
+ 0x0040, 0x2f06, 0x691c, 0xa18d, 0x2000, 0x691e, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x2f16, 0x7868, 0xa08c, 0x00ff, 0x0040, 0x2f14,
+ 0x681b, 0x001e, 0x0078, 0x2f16, 0x681b, 0x0000, 0x2021, 0x505a,
+ 0x2404, 0xad06, 0x0040, 0x2f1d, 0x7460, 0x6800, 0x2022, 0x68c3,
+ 0x0000, 0x6a3c, 0x6940, 0x6a32, 0x692e, 0x1078, 0x1c53, 0x0078,
+ 0x2461, 0x1078, 0x2e1c, 0x682b, 0x0000, 0x2001, 0x000e, 0x6f14,
+ 0x1078, 0x38e4, 0xa08c, 0x00ff, 0x6916, 0x6818, 0xa084, 0x8000,
+ 0x0040, 0x2f3c, 0x703c, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x706f,
+ 0x0000, 0x0078, 0x2461, 0x7000, 0xa005, 0x00c0, 0x2f49, 0x0078,
+ 0x2461, 0xa006, 0x1078, 0x4633, 0x6817, 0x0000, 0x681b, 0x0014,
+ 0xa68c, 0xdf00, 0x691e, 0x682b, 0x0000, 0x6820, 0xa085, 0x00ff,
+ 0x6822, 0x7000, 0x0079, 0x2f5c, 0x2461, 0x2f66, 0x2f66, 0x2f68,
+ 0x2f68, 0x2f68, 0x2f68, 0x2f64, 0x1078, 0x23ca, 0x1078, 0x33c4,
+ 0x6008, 0xa084, 0xffef, 0x600a, 0x0078, 0x337e, 0x2300, 0x0079,
+ 0x2f71, 0x2f74, 0x2f76, 0x2faf, 0x1078, 0x23ca, 0x7000, 0x0079,
+ 0x2f79, 0x2461, 0x2f83, 0x2f83, 0x2f9e, 0x2f83, 0x2fab, 0x2f9e,
+ 0x2f81, 0x1078, 0x23ca, 0xa684, 0x0060, 0xa086, 0x0060, 0x00c0,
+ 0x2f9a, 0xa6b4, 0xffdf, 0xa6b4, 0xbfff, 0xa6b5, 0x2000, 0x7e5a,
+ 0x681c, 0xa084, 0xffdf, 0x681e, 0x1078, 0x4633, 0x1078, 0x48f7,
+ 0x0078, 0x3854, 0xa684, 0x2000, 0x0040, 0x2f8d, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x2fab, 0x681b, 0x0015, 0xa684, 0x4000, 0x0040,
+ 0x2fab, 0x681b, 0x0007, 0x1078, 0x3868, 0x0078, 0x2438, 0x1078,
+ 0x23ca, 0x2300, 0x0079, 0x2fb4, 0x2fb7, 0x2fb9, 0x2fec, 0x1078,
+ 0x23ca, 0x7000, 0x0079, 0x2fbc, 0x2461, 0x2fc6, 0x2fc6, 0x2fe1,
+ 0x2fc6, 0x2fe8, 0x2fe1, 0x2fc4, 0x1078, 0x23ca, 0xa684, 0x0060,
+ 0xa086, 0x0060, 0x00c0, 0x2fdd, 0xa6b4, 0xffbf, 0xa6b4, 0xbfff,
+ 0xa6b5, 0x2000, 0x7e5a, 0x681c, 0xa084, 0xffbf, 0x681e, 0x1078,
+ 0x4633, 0x1078, 0x48f7, 0x0078, 0x3854, 0xa684, 0x2000, 0x0040,
+ 0x2fd0, 0x6818, 0xa084, 0x8000, 0x0040, 0x2fe8, 0x681b, 0x0007,
+ 0x781b, 0x00d2, 0x0078, 0x2438, 0x6820, 0xa085, 0x0004, 0x6822,
+ 0x1078, 0x381f, 0xa6b5, 0x0800, 0x1078, 0x368f, 0x782b, 0x3008,
+ 0x781b, 0x0065, 0x0078, 0x2438, 0x2300, 0x0079, 0x2fff, 0x3002,
+ 0x3004, 0x3006, 0x1078, 0x23ca, 0x0078, 0x367b, 0xa684, 0x0400,
+ 0x00c0, 0x302f, 0x79e4, 0xa184, 0x0020, 0x0040, 0x3016, 0x78ec,
+ 0xa084, 0x0003, 0x0040, 0x3016, 0x782b, 0x3009, 0x789b, 0x0060,
+ 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x79e4, 0xa184, 0x0020,
+ 0x0040, 0x3027, 0x78ec, 0xa084, 0x0003, 0x00c0, 0x302b, 0x2001,
+ 0x0014, 0x0078, 0x2cd7, 0xa184, 0x0007, 0x0079, 0x3067, 0x7a90,
+ 0xa294, 0x0007, 0x789b, 0x0060, 0x79a8, 0x81ff, 0x0040, 0x3065,
+ 0x789b, 0x0010, 0x7ba8, 0xa384, 0x0001, 0x00c0, 0x3056, 0x7ba8,
+ 0x7ba8, 0xa386, 0x0001, 0x00c0, 0x3049, 0x2009, 0xfff7, 0x0078,
+ 0x304f, 0xa386, 0x0003, 0x00c0, 0x3056, 0x2009, 0xffef, 0x0c7e,
+ 0x7054, 0x2060, 0x6004, 0xa104, 0x6006, 0x0c7f, 0x789b, 0x0060,
+ 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x782b, 0x3009, 0x6920,
+ 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x6922, 0x0078, 0x3854, 0x297d,
+ 0x2988, 0x3071, 0x3079, 0x306f, 0x306f, 0x3854, 0x3854, 0x1078,
+ 0x23ca, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x6922, 0x0078,
+ 0x385e, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x6922, 0x0078,
+ 0x3854, 0x79e4, 0xa184, 0x0030, 0x0040, 0x308b, 0x78ec, 0xa084,
+ 0x0003, 0x00c0, 0x30b2, 0x7000, 0xa086, 0x0004, 0x00c0, 0x30a5,
+ 0x706c, 0xa086, 0x0002, 0x00c0, 0x309b, 0x2011, 0x0002, 0x2019,
+ 0x0000, 0x0078, 0x2827, 0x706c, 0xa086, 0x0006, 0x0040, 0x3095,
+ 0x706c, 0xa086, 0x0004, 0x0040, 0x3095, 0x7000, 0xa086, 0x0000,
+ 0x0040, 0x2438, 0x6818, 0xa085, 0x8000, 0x681a, 0x2001, 0x0014,
+ 0x0078, 0x2cd7, 0xa184, 0x0007, 0x0079, 0x30b6, 0x3854, 0x3854,
+ 0x30be, 0x3854, 0x38b9, 0x38b9, 0x3854, 0x3854, 0xa684, 0x0080,
+ 0x0040, 0x30ed, 0x7194, 0x81ff, 0x0040, 0x30ed, 0xa182, 0x000d,
+ 0x00d0, 0x30ce, 0x7097, 0x0000, 0x0078, 0x30d3, 0xa182, 0x000c,
+ 0x7096, 0x2009, 0x000c, 0x789b, 0x0061, 0x79aa, 0x157e, 0x137e,
+ 0x147e, 0x7098, 0x8114, 0xa210, 0x729a, 0xa080, 0x000b, 0xad00,
+ 0x2098, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8108, 0x81ac, 0x53a6,
+ 0x147f, 0x137f, 0x157f, 0x0078, 0x385e, 0xa684, 0x0400, 0x00c0,
+ 0x312e, 0x6820, 0xa084, 0x0001, 0x0040, 0x385e, 0xa68c, 0x0060,
+ 0xa684, 0x0060, 0x0040, 0x3102, 0xa086, 0x0060, 0x00c0, 0x3102,
+ 0xa18d, 0x4000, 0xa18c, 0xfffb, 0x795a, 0x69b6, 0x789b, 0x0060,
+ 0x78ab, 0x0000, 0x789b, 0x0061, 0x6818, 0xa085, 0x8000, 0x681a,
+ 0x78aa, 0x8008, 0x810c, 0x0040, 0x33dd, 0xa18c, 0x00f8, 0x00c0,
+ 0x33dd, 0x157e, 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000,
+ 0x8000, 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f,
+ 0x157f, 0x6814, 0x8007, 0x7882, 0x0078, 0x385e, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x3135, 0x681b, 0x0008, 0x781b, 0x00c8, 0x0078,
+ 0x2438, 0x2300, 0x0079, 0x313c, 0x3141, 0x31e0, 0x313f, 0x1078,
+ 0x23ca, 0x7000, 0xa084, 0x0007, 0x0079, 0x3146, 0x2461, 0x3150,
+ 0x3185, 0x315b, 0x314e, 0x2461, 0x314e, 0x314e, 0x1078, 0x23ca,
+ 0x681c, 0xa084, 0x2000, 0x0040, 0x3169, 0x6008, 0xa085, 0x0002,
+ 0x600a, 0x0078, 0x3169, 0x68c0, 0xa005, 0x00c0, 0x3185, 0x6920,
+ 0xa18d, 0x0001, 0x6922, 0x68c3, 0x0001, 0x6800, 0x706a, 0x0078,
+ 0x317f, 0x6920, 0xa18d, 0x0001, 0x6922, 0x6800, 0x6006, 0xa005,
+ 0x00c0, 0x3173, 0x6002, 0x681c, 0xa084, 0x000e, 0x0040, 0x317f,
+ 0x7014, 0x68ba, 0x7130, 0xa188, 0x7300, 0x0078, 0x3181, 0x2009,
+ 0x7400, 0x2104, 0x6802, 0x2d0a, 0x7162, 0x6eb6, 0xa684, 0x0060,
+ 0x0040, 0x31de, 0xa684, 0x0800, 0x00c0, 0x3199, 0xa684, 0x7fff,
+ 0x68b6, 0x6894, 0x68a6, 0x6898, 0x68aa, 0x1078, 0x4633, 0x0078,
+ 0x31de, 0xa684, 0x0020, 0x0040, 0x31ae, 0x68c0, 0xa005, 0x0040,
+ 0x31a5, 0x1078, 0x4a35, 0x0078, 0x31a8, 0xa006, 0x1078, 0x49ed,
+ 0x79d8, 0x7adc, 0x69aa, 0x6aa6, 0x0078, 0x31b4, 0x1078, 0x37ca,
+ 0x69aa, 0x6aa6, 0x1078, 0x49ed, 0xa684, 0x8000, 0x0040, 0x31de,
+ 0xa684, 0x7fff, 0x68b6, 0x2001, 0x0076, 0x1078, 0x38e4, 0x2010,
+ 0x2001, 0x0078, 0x1078, 0x38e4, 0x2008, 0xa684, 0x0020, 0x00c0,
+ 0x31d6, 0x2001, 0x007a, 0x1078, 0x38e4, 0x801b, 0x00c8, 0x31d1,
+ 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100,
+ 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae, 0x0078, 0x2461,
+ 0x0078, 0x367b, 0x7037, 0x0000, 0xa282, 0x0006, 0x0050, 0x31ea,
+ 0x1078, 0x23ca, 0x7000, 0xa084, 0x0007, 0x10c0, 0x398a, 0x2300,
+ 0x0079, 0x31f2, 0x31f5, 0x321e, 0x3232, 0x2200, 0x0079, 0x31f8,
+ 0x321c, 0x367b, 0x31fe, 0x321c, 0x324e, 0x3290, 0x7003, 0x0005,
+ 0x2001, 0x7510, 0x2068, 0x704a, 0x157e, 0x20a9, 0x0031, 0x2003,
+ 0x0000, 0x8000, 0x0070, 0x320e, 0x0078, 0x3207, 0x157f, 0xad80,
+ 0x0009, 0x7036, 0x6817, 0x0000, 0x68b7, 0x0700, 0x6823, 0x0800,
+ 0x6827, 0x0003, 0x0078, 0x366b, 0x1078, 0x23ca, 0x7003, 0x0005,
+ 0x2001, 0x7510, 0x2068, 0x704a, 0xad80, 0x0009, 0x7036, 0x2200,
+ 0x0079, 0x322a, 0x367b, 0x3230, 0x3230, 0x324e, 0x3230, 0x367b,
+ 0x1078, 0x23ca, 0x7003, 0x0005, 0x2001, 0x7510, 0x2068, 0x704a,
+ 0xad80, 0x0009, 0x7036, 0x2200, 0x0079, 0x323e, 0x3246, 0x3244,
+ 0x3244, 0x3246, 0x3244, 0x3246, 0x1078, 0x23ca, 0x1078, 0x369f,
+ 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x7003, 0x0002,
+ 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x001f,
+ 0xa215, 0x2069, 0x7400, 0x2d04, 0x2d08, 0x7162, 0x2068, 0xa005,
+ 0x0040, 0x3269, 0x6814, 0xa206, 0x0040, 0x3285, 0x6800, 0x0078,
+ 0x325c, 0x7003, 0x0005, 0x2001, 0x7510, 0x2068, 0x704a, 0x7036,
+ 0x157e, 0x20a9, 0x0031, 0x2003, 0x0000, 0x8000, 0x0070, 0x327a,
+ 0x0078, 0x3273, 0x157f, 0xad80, 0x0009, 0x7036, 0x6a16, 0x68b7,
+ 0x0700, 0x6823, 0x0800, 0x6827, 0x0003, 0x6eb4, 0x7e5a, 0x6820,
+ 0xa084, 0x0c00, 0x0040, 0x32df, 0x1078, 0x3697, 0x0078, 0x32df,
+ 0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8,
+ 0xa484, 0x001f, 0xa215, 0x79a8, 0x79a8, 0xa18c, 0x00ff, 0xa1e8,
+ 0x7300, 0x2d04, 0x2d08, 0x7162, 0x2068, 0xa005, 0x0040, 0x32af,
+ 0x6814, 0xa206, 0x0040, 0x32ca, 0x6800, 0x0078, 0x32a2, 0x7003,
+ 0x0005, 0x2001, 0x7510, 0x2068, 0x704a, 0x157e, 0x20a9, 0x0031,
+ 0x2003, 0x0000, 0x8000, 0x0070, 0x32bf, 0x0078, 0x32b8, 0x157f,
+ 0xad80, 0x0009, 0x7036, 0x6a16, 0x68b7, 0x0700, 0x6823, 0x0800,
+ 0x6827, 0x0003, 0x6eb4, 0x7e5a, 0x6820, 0xa084, 0x0c00, 0x0040,
+ 0x32df, 0xa084, 0x0800, 0x0040, 0x32d9, 0x1078, 0x369b, 0x0078,
+ 0x32df, 0x1078, 0x3697, 0x708b, 0x0000, 0x0078, 0x32df, 0x027e,
+ 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x5280,
+ 0x2060, 0x7056, 0x6000, 0x705a, 0x6004, 0x705e, 0xa684, 0x0060,
+ 0x0040, 0x3337, 0x6b98, 0x6c94, 0x69ac, 0x68b0, 0xa105, 0x00c0,
+ 0x3319, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0xa6b4, 0xb7ff, 0x7e5a,
+ 0xa684, 0x0060, 0xa086, 0x0060, 0x0040, 0x3337, 0x68c0, 0xa005,
+ 0x0040, 0x3312, 0x7003, 0x0003, 0x682b, 0x0000, 0x1078, 0x48e6,
+ 0x0078, 0x3314, 0x1078, 0x48f7, 0xa6b5, 0x2000, 0x7e5a, 0x0078,
+ 0x3337, 0x68b0, 0xa31a, 0x2100, 0xa423, 0x2400, 0xa305, 0x0040,
+ 0x3337, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0x68b0, 0xa6b4, 0xbfff,
+ 0x7e5a, 0x007e, 0x68c0, 0xa005, 0x007f, 0x0040, 0x3335, 0x7003,
+ 0x0003, 0x1078, 0x48e6, 0x0078, 0x3337, 0x1078, 0x4942, 0x077f,
+ 0x1078, 0x37bd, 0x2009, 0x0065, 0xa684, 0x0004, 0x0040, 0x3358,
+ 0x78e4, 0xa084, 0x0030, 0x0040, 0x3350, 0x78ec, 0xa084, 0x0003,
+ 0x0040, 0x3350, 0x782b, 0x3008, 0x2009, 0x0065, 0x0078, 0x3358,
+ 0x0f7e, 0x2079, 0x5000, 0x1078, 0x4633, 0x0f7f, 0x0040, 0x2461,
+ 0x791a, 0x2d00, 0x704a, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003,
+ 0x8003, 0xa080, 0x5280, 0x2048, 0x0078, 0x2438, 0x6020, 0xa005,
+ 0x0040, 0x3372, 0x8001, 0x6022, 0x6008, 0xa085, 0x0008, 0x600a,
+ 0x7010, 0x6026, 0x007c, 0xa006, 0x1078, 0x4633, 0x6817, 0x0000,
+ 0x681b, 0x0001, 0x6823, 0x0040, 0x681f, 0x0100, 0x7000, 0xa084,
+ 0x0007, 0x0079, 0x3383, 0x2461, 0x338d, 0x338d, 0x33aa, 0x3395,
+ 0x3393, 0x3395, 0x338b, 0x1078, 0x23ca, 0x1078, 0x33b5, 0x1078,
+ 0x33ae, 0x1078, 0x1c53, 0x0078, 0x2461, 0x706c, 0x706f, 0x0000,
+ 0x7093, 0x0000, 0x0079, 0x339c, 0x33a6, 0x33a6, 0x33a4, 0x33a4,
+ 0x33a4, 0x33a6, 0x33a4, 0x33a6, 0x0079, 0x2840, 0x706f, 0x0000,
+ 0x0078, 0x2461, 0x681b, 0x0000, 0x0078, 0x2eed, 0x6800, 0xa005,
+ 0x00c0, 0x33b3, 0x6002, 0x6006, 0x007c, 0x6010, 0xa005, 0x0040,
+ 0x33be, 0x8001, 0x00d0, 0x33be, 0x1078, 0x23ca, 0x6012, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x007c, 0x6018, 0xa005, 0x0040, 0x33ca,
+ 0x8001, 0x601a, 0x007c, 0x1078, 0x38de, 0x681b, 0x0018, 0x0078,
+ 0x3401, 0x1078, 0x38de, 0x681b, 0x0019, 0x0078, 0x3401, 0x1078,
+ 0x38de, 0x681b, 0x001a, 0x0078, 0x3401, 0x1078, 0x38de, 0x681b,
+ 0x0003, 0x0078, 0x3401, 0x7780, 0x1078, 0x37bd, 0x7184, 0xa18c,
+ 0x00ff, 0xa1e8, 0x7300, 0x2d04, 0x2d08, 0x2068, 0xa005, 0x00c0,
+ 0x33f3, 0x0078, 0x2461, 0x6814, 0x7280, 0xa206, 0x0040, 0x33fb,
+ 0x6800, 0x0078, 0x33ec, 0x6800, 0x200a, 0x681b, 0x0005, 0x708b,
+ 0x0000, 0x1078, 0x33b5, 0x6820, 0xa084, 0x0001, 0x00c0, 0x340a,
+ 0x1078, 0x33ae, 0x1078, 0x33c4, 0x681f, 0x0000, 0x6823, 0x0020,
+ 0x1078, 0x1c53, 0x0078, 0x2461, 0xa282, 0x0003, 0x00c0, 0x366b,
+ 0x7da8, 0xa5ac, 0x00ff, 0x7ca8, 0xa4a4, 0x00ff, 0x6920, 0xa18d,
+ 0x0080, 0x6922, 0xa184, 0x0100, 0x0040, 0x3478, 0xa18c, 0xfeff,
+ 0x6922, 0xa4a4, 0x00ff, 0x0040, 0x3462, 0xa482, 0x000c, 0x0048,
+ 0x3435, 0x0040, 0x3435, 0x2021, 0x000c, 0x852b, 0x852b, 0x1078,
+ 0x372e, 0x0040, 0x343f, 0x1078, 0x3531, 0x0078, 0x346b, 0x1078,
+ 0x36e9, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078,
+ 0x3558, 0x0c7f, 0x6920, 0xa18d, 0x0100, 0x6922, 0x7e58, 0xa6b5,
+ 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x345c, 0x782b, 0x3008,
+ 0x781b, 0x0056, 0x0078, 0x2438, 0x782b, 0x3008, 0x781b, 0x0065,
+ 0x0078, 0x2438, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5, 0x6006,
+ 0x1078, 0x3558, 0x0c7f, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x3474,
+ 0x781b, 0x0058, 0x0078, 0x2438, 0x781b, 0x0065, 0x0078, 0x2438,
+ 0x0c7e, 0x7054, 0x2060, 0x6100, 0xa18c, 0x1000, 0x0040, 0x34b8,
+ 0x6208, 0x8217, 0xa294, 0x00ff, 0xa282, 0x000c, 0x0048, 0x348c,
+ 0x0040, 0x348c, 0x2011, 0x000c, 0x2400, 0xa202, 0x00c8, 0x3491,
+ 0x2220, 0x6208, 0xa294, 0x00ff, 0x7018, 0xa086, 0x0028, 0x00c0,
+ 0x34a1, 0xa282, 0x0019, 0x00c8, 0x34a7, 0x2011, 0x0019, 0x0078,
+ 0x34a7, 0xa282, 0x000c, 0x00c8, 0x34a7, 0x2011, 0x000c, 0x2200,
+ 0xa502, 0x00c8, 0x34ac, 0x2228, 0x1078, 0x36ed, 0x852b, 0x852b,
+ 0x1078, 0x372e, 0x0040, 0x34b8, 0x1078, 0x3531, 0x0078, 0x34bc,
+ 0x1078, 0x36e9, 0x1078, 0x3558, 0x7858, 0xa085, 0x0004, 0x785a,
+ 0x0c7f, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x0c7e,
+ 0x2960, 0x6000, 0xa084, 0x1000, 0x00c0, 0x34df, 0x6010, 0xa084,
+ 0x000f, 0x00c0, 0x34d9, 0x6104, 0xa18c, 0xfff5, 0x6106, 0x0c7f,
+ 0x007c, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078, 0x3506, 0x68a0,
+ 0xa084, 0x0200, 0x00c0, 0x34d9, 0x6208, 0xa294, 0x00ff, 0x7018,
+ 0xa086, 0x0028, 0x00c0, 0x34f4, 0xa282, 0x0019, 0x00c8, 0x34fa,
+ 0x2011, 0x0019, 0x0078, 0x34fa, 0xa282, 0x000c, 0x00c8, 0x34fa,
+ 0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff, 0xa382, 0x000c,
+ 0x0048, 0x3506, 0x0040, 0x3506, 0x2019, 0x000c, 0x78ab, 0x0001,
+ 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005,
+ 0x6820, 0xa085, 0x0100, 0x6822, 0x0c7f, 0x007c, 0x0c7e, 0x2960,
+ 0xa18c, 0xfff5, 0x6106, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078,
+ 0x3521, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa,
+ 0x7baa, 0xa8c0, 0x0005, 0x6820, 0xa085, 0x0100, 0x6822, 0x0c7f,
+ 0x007c, 0x0c7e, 0x7154, 0x2160, 0x1078, 0x3538, 0x0c7f, 0x007c,
+ 0x2008, 0xa084, 0xfff0, 0xa425, 0x7c86, 0x6018, 0x789a, 0x7cae,
+ 0x6412, 0x78a4, 0xa084, 0xfff8, 0xa18c, 0x0007, 0xa105, 0x78a6,
+ 0x6016, 0x788a, 0xa4a4, 0x000f, 0x8427, 0x8204, 0x8004, 0xa084,
+ 0x00ff, 0xa405, 0x600e, 0x6004, 0xa084, 0xfff5, 0x6006, 0x007c,
+ 0x0c7e, 0x7054, 0x2060, 0x1078, 0x355f, 0x0c7f, 0x007c, 0x6018,
+ 0x789a, 0x78a4, 0xa084, 0xfff0, 0x78a6, 0x6012, 0x7884, 0xa084,
+ 0xfff0, 0x7886, 0x007c, 0xa282, 0x0002, 0x00c0, 0x366b, 0x7aa8,
+ 0x6920, 0xa18d, 0x0080, 0x6922, 0xa184, 0x0200, 0x0040, 0x35b4,
+ 0xa18c, 0xfdff, 0x6922, 0xa294, 0x00ff, 0xa282, 0x0002, 0x00c8,
+ 0x366b, 0x1078, 0x35fd, 0x1078, 0x3558, 0xa980, 0x0001, 0x200c,
+ 0x1078, 0x37b9, 0x1078, 0x34c7, 0x88ff, 0x0040, 0x35a7, 0x789b,
+ 0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684,
+ 0x0400, 0x00c0, 0x35a1, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078,
+ 0x2438, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x7e58,
+ 0xa684, 0x0400, 0x00c0, 0x35b0, 0x781b, 0x0058, 0x0078, 0x2438,
+ 0x781b, 0x0065, 0x0078, 0x2438, 0xa282, 0x0002, 0x00c8, 0x35bc,
+ 0xa284, 0x0001, 0x0040, 0x35c6, 0x7154, 0xa188, 0x0000, 0x210c,
+ 0xa18c, 0x2000, 0x00c0, 0x35c6, 0x2011, 0x0000, 0x1078, 0x36db,
+ 0x1078, 0x35fd, 0x1078, 0x3558, 0x7858, 0xa085, 0x0004, 0x785a,
+ 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x0c7e, 0x027e,
+ 0x2960, 0x6000, 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, 0x35ed,
+ 0x6014, 0xa084, 0x0040, 0x00c0, 0x35eb, 0xa18c, 0xffef, 0x6106,
+ 0xa006, 0x0078, 0x35fa, 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab,
+ 0x0002, 0x78ab, 0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x6820, 0xa085,
+ 0x0200, 0x6822, 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7054, 0x2060,
+ 0x1078, 0x3604, 0x0c7f, 0x007c, 0x82ff, 0x0040, 0x3609, 0x2011,
+ 0x0040, 0x6018, 0xa080, 0x0002, 0x789a, 0x78a4, 0xa084, 0xffbf,
+ 0xa205, 0x78a6, 0x788a, 0x6016, 0x6004, 0xa084, 0xffef, 0x6006,
+ 0x007c, 0x007e, 0x7000, 0xa086, 0x0003, 0x0040, 0x3622, 0x007f,
+ 0x0078, 0x3625, 0x007f, 0x0078, 0x3667, 0xa684, 0x0020, 0x0040,
+ 0x3667, 0x7888, 0xa084, 0x0040, 0x0040, 0x3667, 0x7bb8, 0xa384,
+ 0x003f, 0x831b, 0x00c8, 0x3635, 0x8000, 0xa005, 0x0040, 0x364b,
+ 0x831b, 0x00c8, 0x363e, 0x8001, 0x0040, 0x3663, 0xa684, 0x4000,
+ 0x0040, 0x364b, 0x78b8, 0x801b, 0x00c8, 0x3647, 0x8000, 0xa084,
+ 0x003f, 0x00c0, 0x3663, 0xa6b4, 0xbfff, 0x7e5a, 0x79d8, 0x7adc,
+ 0x2001, 0x0001, 0xa108, 0x00c8, 0x3657, 0xa291, 0x0000, 0x79d2,
+ 0x79da, 0x7ad6, 0x7ade, 0x1078, 0x49ed, 0x781b, 0x0064, 0x1078,
+ 0x4872, 0x0078, 0x2438, 0x781b, 0x0064, 0x0078, 0x2438, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0x1078, 0x36a3, 0x782b, 0x3008, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0x1078, 0x368f, 0x782b, 0x3008, 0x781b,
+ 0x0065, 0x0078, 0x2438, 0x6827, 0x0002, 0x1078, 0x3697, 0x78e4,
+ 0xa084, 0x0030, 0x0040, 0x2461, 0x78ec, 0xa084, 0x0003, 0x0040,
+ 0x2461, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x2001,
+ 0x0005, 0x0078, 0x36a5, 0x2001, 0x000c, 0x0078, 0x36a5, 0x2001,
+ 0x0006, 0x0078, 0x36a5, 0x2001, 0x000d, 0x0078, 0x36a5, 0x2001,
+ 0x0009, 0x0078, 0x36a5, 0x2001, 0x0007, 0x789b, 0x0010, 0x78aa,
+ 0x789b, 0x0060, 0x78ab, 0x0001, 0xa6b5, 0x0004, 0x7e5a, 0x007c,
+ 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b, 0x8703, 0xa0e0,
+ 0x5280, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184, 0x000f, 0x0040,
+ 0x36c9, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004, 0xa085, 0x0008,
+ 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184, 0x0040, 0x0040,
+ 0x36d9, 0xa184, 0xffbf, 0x78a6, 0x6016, 0x6004, 0xa085, 0x0010,
+ 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab, 0x0001, 0x78ab,
+ 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060, 0x78ab, 0x0004,
+ 0x007c, 0x2021, 0x0000, 0x2029, 0x0032, 0x789b, 0x0010, 0x78ab,
+ 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa, 0x7caa, 0x789b,
+ 0x0060, 0x78ab, 0x0005, 0x007c, 0x157e, 0x8007, 0xa084, 0x00ff,
+ 0x8003, 0x8003, 0xa080, 0x0020, 0x789a, 0x79a4, 0xa18c, 0xfff0,
+ 0x2001, 0x5046, 0x2004, 0xa082, 0x0028, 0x0040, 0x3717, 0x2021,
+ 0x37a0, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078, 0x371d, 0x2021,
+ 0x37ac, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064, 0x2404,
+ 0xa084, 0xfff0, 0xa106, 0x0040, 0x372c, 0x8420, 0x2300, 0xa210,
+ 0x0070, 0x372c, 0x0078, 0x371f, 0x157f, 0x007c, 0x157e, 0x2009,
+ 0x5046, 0x210c, 0xa182, 0x0032, 0x0048, 0x3742, 0x0040, 0x3746,
+ 0x2009, 0x3792, 0x2019, 0x0011, 0x20a9, 0x000e, 0x2011, 0x0032,
+ 0x0078, 0x3758, 0xa182, 0x0028, 0x0040, 0x3750, 0x2009, 0x37a0,
+ 0x2019, 0x0014, 0x20a9, 0x000c, 0x2011, 0x0064, 0x0078, 0x3758,
+ 0x2009, 0x37ac, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064,
+ 0x2200, 0xa502, 0x0040, 0x3768, 0x0048, 0x3768, 0x8108, 0x2300,
+ 0xa210, 0x0070, 0x3765, 0x0078, 0x3758, 0x157f, 0xa006, 0x007c,
+ 0x157f, 0xa582, 0x0064, 0x00c8, 0x3777, 0x7808, 0xa085, 0x0070,
+ 0x780a, 0x7044, 0xa085, 0x0070, 0x7046, 0x0078, 0x3777, 0x78ec,
+ 0xa084, 0x0300, 0x0040, 0x377f, 0x2104, 0x0078, 0x3790, 0x2104,
+ 0xa09e, 0x1102, 0x00c0, 0x3790, 0x2001, 0x04fd, 0x2004, 0xa082,
+ 0x0005, 0x0048, 0x378f, 0x2001, 0x1201, 0x0078, 0x3790, 0x2104,
+ 0xa005, 0x007c, 0x1102, 0x3002, 0x3202, 0x4203, 0x4403, 0x5404,
+ 0x5604, 0x6605, 0x6805, 0x7806, 0x7a06, 0x0c07, 0x0c07, 0x0e07,
+ 0x3202, 0x4202, 0x5202, 0x6202, 0x7202, 0x6605, 0x7605, 0x7805,
+ 0x7a05, 0x7c05, 0x7e05, 0x7f05, 0x2202, 0x3202, 0x4202, 0x5202,
+ 0x5404, 0x6404, 0x7404, 0x7604, 0x7804, 0x7a04, 0x7c04, 0x7e04,
+ 0x7f04, 0x789b, 0x0010, 0xa046, 0x007c, 0xa784, 0x0f00, 0x800b,
+ 0xa784, 0x001f, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e0,
+ 0x5300, 0x007c, 0x79d8, 0x7adc, 0x78d0, 0x801b, 0x00c8, 0x37d1,
+ 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x007c, 0x0f7e,
+ 0x2079, 0x0100, 0x2009, 0x5040, 0x2091, 0x8000, 0x2104, 0x0079,
+ 0x37e1, 0x3817, 0x37eb, 0x37eb, 0x37eb, 0x37eb, 0x37eb, 0x37eb,
+ 0x381b, 0x1078, 0x23ca, 0x784b, 0x0004, 0x7848, 0xa084, 0x0004,
+ 0x00c0, 0x37ed, 0x784b, 0x0008, 0x7848, 0xa084, 0x0008, 0x00c0,
+ 0x37f4, 0x68b4, 0xa085, 0x4000, 0x68b6, 0x7858, 0xa085, 0x4000,
+ 0x785a, 0x7830, 0xa084, 0x0080, 0x00c0, 0x3817, 0x0018, 0x3817,
+ 0x681c, 0xa084, 0x0020, 0x00c0, 0x3815, 0x0e7e, 0x2071, 0x5040,
+ 0x1078, 0x3868, 0x0e7f, 0x0078, 0x3817, 0x781b, 0x00d2, 0x2091,
+ 0x8001, 0x0f7f, 0x007c, 0x1078, 0x3a42, 0x0078, 0x3817, 0x0c7e,
+ 0x6814, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0,
+ 0x5280, 0x6004, 0xa084, 0x000a, 0x00c0, 0x3852, 0x6108, 0xa194,
+ 0xff00, 0x0040, 0x3852, 0xa18c, 0x00ff, 0x2001, 0x0019, 0xa106,
+ 0x0040, 0x3841, 0x2001, 0x0032, 0xa106, 0x0040, 0x3845, 0x0078,
+ 0x3849, 0x2009, 0x0020, 0x0078, 0x384b, 0x2009, 0x003f, 0x0078,
+ 0x384b, 0x2011, 0x0000, 0x2100, 0xa205, 0x600a, 0x6004, 0xa085,
+ 0x0002, 0x6006, 0x0c7f, 0x007c, 0x781b, 0x0065, 0x0078, 0x2438,
+ 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2438, 0x781b, 0x0058,
+ 0x0078, 0x2438, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2438,
+ 0x2009, 0x5020, 0x210c, 0xa186, 0x0000, 0x0040, 0x387c, 0xa186,
+ 0x0001, 0x0040, 0x387f, 0x2009, 0x5038, 0x200b, 0x000b, 0x706f,
+ 0x0001, 0x781b, 0x0048, 0x007c, 0x781b, 0x00cc, 0x007c, 0x2009,
+ 0x5038, 0x200b, 0x000a, 0x007c, 0x2009, 0x5020, 0x210c, 0xa186,
+ 0x0000, 0x0040, 0x389f, 0xa186, 0x0001, 0x0040, 0x3899, 0x2009,
+ 0x5038, 0x200b, 0x000b, 0x706f, 0x0001, 0x781b, 0x0048, 0x0078,
+ 0x2438, 0x2009, 0x5038, 0x200b, 0x000a, 0x0078, 0x2438, 0x782b,
+ 0x3008, 0x781b, 0x00cc, 0x0078, 0x2438, 0x781b, 0x00d2, 0x0078,
+ 0x2438, 0x782b, 0x3008, 0x781b, 0x00d2, 0x0078, 0x2438, 0x781b,
+ 0x0093, 0x0078, 0x2438, 0x782b, 0x3008, 0x781b, 0x0093, 0x0078,
+ 0x2438, 0x6818, 0xa084, 0x8000, 0x0040, 0x38c0, 0x681b, 0x001d,
+ 0x706f, 0x0001, 0x781b, 0x0048, 0x0078, 0x2438, 0x007e, 0x7830,
+ 0xa084, 0x00c0, 0x00c0, 0x38dc, 0x7808, 0xa084, 0xfffc, 0x780a,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021, 0x0040,
+ 0x38dc, 0x7044, 0x780a, 0xa005, 0x007f, 0x007c, 0x7044, 0xa085,
+ 0x0002, 0x7046, 0x780a, 0x007c, 0x007e, 0x7830, 0xa084, 0x0040,
+ 0x00c0, 0x38e5, 0x0098, 0x38f0, 0x007f, 0x789a, 0x78ac, 0x007c,
+ 0x7808, 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005,
+ 0x78ec, 0xa084, 0x0021, 0x0040, 0x38ff, 0x0098, 0x38fd, 0x007f,
+ 0x789a, 0x78ac, 0x007e, 0x7044, 0x780a, 0x007f, 0x007c, 0x78ec,
+ 0xa084, 0x0002, 0x00c0, 0x461d, 0xa784, 0x007d, 0x00c0, 0x3913,
+ 0x2700, 0x1078, 0x23ca, 0xa784, 0x0001, 0x00c0, 0x2f43, 0xa784,
+ 0x0070, 0x0040, 0x3923, 0x0c7e, 0x2d60, 0x2f68, 0x1078, 0x2375,
+ 0x2d78, 0x2c68, 0x0c7f, 0xa784, 0x0008, 0x0040, 0x3930, 0x784b,
+ 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x2461, 0x0078, 0x3854,
+ 0xa784, 0x0004, 0x0040, 0x3963, 0x78b8, 0xa084, 0x4001, 0x0040,
+ 0x3963, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x2461,
+ 0x78e4, 0xa084, 0x0007, 0xa086, 0x0001, 0x00c0, 0x3963, 0x78c0,
+ 0xa085, 0x4800, 0x2030, 0x7e5a, 0x781b, 0x00d2, 0x0078, 0x2438,
+ 0x784b, 0x0008, 0x6818, 0xa084, 0x8000, 0x0040, 0x395f, 0x681b,
+ 0x0015, 0xa684, 0x4000, 0x0040, 0x395f, 0x681b, 0x0007, 0x1078,
+ 0x3868, 0x0078, 0x2438, 0x681b, 0x0003, 0x7858, 0xa084, 0x3f00,
+ 0x681e, 0x682f, 0x0000, 0x6833, 0x0000, 0x784b, 0x0008, 0x78ec,
+ 0xa084, 0x0003, 0x0040, 0x2944, 0x0018, 0x2438, 0x0078, 0x3673,
+ 0x6b14, 0x8307, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080,
+ 0x5280, 0x2060, 0x2048, 0x7056, 0x6000, 0x705a, 0x6004, 0x705e,
+ 0x2a60, 0x007c, 0x0079, 0x398c, 0x3994, 0x3995, 0x3994, 0x3997,
+ 0x3994, 0x3994, 0x3994, 0x399c, 0x007c, 0x1078, 0x33c4, 0x1078,
+ 0x4633, 0x7038, 0x600a, 0x007c, 0x70a0, 0xa005, 0x0040, 0x39a9,
+ 0x2068, 0x1078, 0x1b45, 0x1078, 0x45b5, 0x1078, 0x45bc, 0x70a3,
+ 0x0000, 0x007c, 0x0e7e, 0x2091, 0x8000, 0x2071, 0x5040, 0x7000,
+ 0xa086, 0x0007, 0x00c0, 0x39c0, 0x6110, 0x70bc, 0xa106, 0x00c0,
+ 0x39c0, 0x0e7f, 0x1078, 0x1b52, 0x1078, 0x39c6, 0xa006, 0x007c,
+ 0x2091, 0x8001, 0x0e7f, 0xa085, 0x0001, 0x007c, 0x0f7e, 0x0e7e,
+ 0x2071, 0x5040, 0x0078, 0x21d9, 0x785b, 0x0000, 0x70af, 0x000e,
+ 0x2009, 0x0100, 0x017e, 0x70a0, 0xa06d, 0x0040, 0x39db, 0x70a3,
+ 0x0000, 0x0078, 0x39e1, 0x70b3, 0x0000, 0x1078, 0x1b6e, 0x0040,
+ 0x39e7, 0x70ac, 0x6826, 0x1078, 0x3ac2, 0x0078, 0x39db, 0x017f,
+ 0x157e, 0x0c7e, 0x0d7e, 0x20a9, 0x0008, 0x2061, 0x7410, 0x6000,
+ 0xa105, 0x6002, 0x601c, 0xa06d, 0x0040, 0x39ff, 0x6800, 0x601e,
+ 0x1078, 0x193d, 0x6008, 0x8000, 0x600a, 0x0078, 0x39f2, 0x6018,
+ 0xa06d, 0x0040, 0x3a09, 0x6800, 0x601a, 0x1078, 0x193d, 0x0078,
+ 0x39ff, 0xace0, 0x0008, 0x0070, 0x3a0f, 0x0078, 0x39ef, 0x709c,
+ 0xa084, 0x8000, 0x0040, 0x3a16, 0x1078, 0x3b3c, 0x0d7f, 0x0c7f,
+ 0x157f, 0x007c, 0x127e, 0x2091, 0x2300, 0x6804, 0xa084, 0x000f,
+ 0x0079, 0x3a22, 0x3a32, 0x3a32, 0x3a32, 0x3a32, 0x3a32, 0x3a32,
+ 0x3a34, 0x3a3a, 0x3a32, 0x3a32, 0x3a32, 0x3a32, 0x3a32, 0x3a3c,
+ 0x3a32, 0x3a34, 0x1078, 0x23ca, 0x1078, 0x4466, 0x1078, 0x193d,
+ 0x0078, 0x3a40, 0x6827, 0x000b, 0x1078, 0x4466, 0x1078, 0x3ac2,
+ 0x127f, 0x007c, 0x127e, 0x2091, 0x2300, 0x0098, 0x3a5e, 0x7830,
+ 0xa084, 0x00c0, 0x00c0, 0x3a5e, 0x0d7e, 0x1078, 0x45c5, 0x2d00,
+ 0x682e, 0x2009, 0x0004, 0x2001, 0x0000, 0x6827, 0x0084, 0x1078,
+ 0x457e, 0x1078, 0x3ac2, 0x0d7f, 0x0078, 0x3a90, 0x7948, 0xa185,
+ 0x4000, 0x784a, 0x0098, 0x3a67, 0x794a, 0x0078, 0x3a4c, 0x7828,
+ 0xa086, 0x1834, 0x00c0, 0x3a70, 0xa185, 0x0004, 0x0078, 0x3a77,
+ 0x7828, 0xa186, 0x1814, 0x00c0, 0x3a64, 0xa185, 0x000c, 0x784a,
+ 0x789b, 0x000e, 0x78ab, 0x0002, 0x7858, 0xa084, 0x00ff, 0xa085,
+ 0x0400, 0x785a, 0x70b4, 0xa080, 0x0091, 0x781a, 0x6827, 0x0002,
+ 0x6827, 0x0084, 0x2009, 0x0004, 0x2001, 0x0000, 0x1078, 0x457e,
+ 0x127f, 0x007c, 0x0d7e, 0x6b14, 0x1078, 0x1be0, 0x0040, 0x3a9f,
+ 0x2068, 0x6827, 0x0002, 0x1078, 0x3ac2, 0x0078, 0x3a94, 0x0d7f,
+ 0x007c, 0x0d7e, 0x6b14, 0x6c28, 0xa4a4, 0x00ff, 0x1078, 0x1b7e,
+ 0x0040, 0x3aaf, 0x2068, 0x6827, 0x0002, 0x1078, 0x3ac2, 0x0d7f,
+ 0x007c, 0x0d7e, 0x6b14, 0xa39c, 0x00ff, 0x1078, 0x1bb1, 0x0040,
+ 0x3ac0, 0x2068, 0x6827, 0x0002, 0x1078, 0x3ac2, 0x0078, 0x3ab5,
+ 0x0d7f, 0x007c, 0x0c7e, 0x6914, 0x1078, 0x3b33, 0x6904, 0xa18c,
+ 0x00ff, 0xa186, 0x0006, 0x0040, 0x3add, 0xa186, 0x000d, 0x0040,
+ 0x3afc, 0xa186, 0x0017, 0x00c0, 0x3ad9, 0x1078, 0x193d, 0x0078,
+ 0x3adb, 0x1078, 0x1c55, 0x0c7f, 0x007c, 0x6004, 0x8001, 0x0048,
+ 0x3afa, 0x6006, 0x2009, 0x0000, 0xa684, 0x0001, 0x00c0, 0x3aea,
+ 0xa18d, 0x8000, 0xa684, 0x0004, 0x0040, 0x3af0, 0xa18d, 0x0002,
+ 0x691e, 0x6823, 0x0000, 0x7104, 0x810f, 0x6818, 0xa105, 0x681a,
+ 0x0078, 0x3ad9, 0x1078, 0x23ca, 0x6018, 0xa005, 0x00c0, 0x3b0b,
+ 0x6008, 0x8001, 0x0048, 0x3b0b, 0x600a, 0x601c, 0x6802, 0x2d00,
+ 0x601e, 0x0078, 0x3b21, 0xac88, 0x0006, 0x2104, 0xa005, 0x0040,
+ 0x3b14, 0x2008, 0x0078, 0x3b0d, 0x6802, 0x2d0a, 0x6008, 0x8001,
+ 0x0048, 0x3adb, 0x600a, 0x6018, 0x2068, 0x6800, 0x601a, 0x0078,
+ 0x3b05, 0x157e, 0x137e, 0x147e, 0x0c7e, 0x0d7e, 0x1078, 0x191a,
+ 0x2da0, 0x137f, 0x20a9, 0x0031, 0x53a3, 0x0c7f, 0x147f, 0x137f,
+ 0x157f, 0x0078, 0x3ad9, 0xa184, 0x001f, 0x8003, 0x8003, 0x8003,
+ 0xa080, 0x7410, 0x2060, 0x007c, 0x2019, 0x5051, 0x2304, 0xa085,
+ 0x0001, 0x201a, 0x2019, 0x0102, 0x2304, 0xa085, 0x0001, 0x201a,
+ 0x007c, 0x2019, 0x5051, 0x2304, 0xa084, 0xfffe, 0x201a, 0x2019,
+ 0x0102, 0x2304, 0xa084, 0xfffe, 0x201a, 0x007c, 0x7990, 0xa18c,
+ 0xfff8, 0x7992, 0x70b4, 0xa080, 0x00d8, 0x781a, 0x0078, 0x2438,
+ 0x70a3, 0x0000, 0x7003, 0x0000, 0x7043, 0x0001, 0x7037, 0x0000,
+ 0x0018, 0x23ef, 0x1078, 0x1b6e, 0x0040, 0x3b91, 0x2009, 0x500f,
+ 0x200b, 0x0000, 0x68bc, 0x2060, 0x6100, 0xa184, 0x0300, 0x0040,
+ 0x3b85, 0x6827, 0x000e, 0xa084, 0x0200, 0x0040, 0x3b81, 0x6827,
+ 0x0017, 0x1078, 0x3ac2, 0x0078, 0x3b60, 0x7000, 0xa086, 0x0007,
+ 0x00c0, 0x3be3, 0x2d00, 0x70a2, 0xad80, 0x000f, 0x7036, 0x0078,
+ 0x3b98, 0x7040, 0xa086, 0x0001, 0x0040, 0x2471, 0x0078, 0x2438,
+ 0x2031, 0x0000, 0x691c, 0xa184, 0x0002, 0x0040, 0x3ba1, 0xa6b5,
+ 0x0004, 0xa184, 0x00c0, 0x8003, 0x8003, 0x8007, 0xa080, 0x3c72,
+ 0x2004, 0xa635, 0x6820, 0xa084, 0x0400, 0x0040, 0x3bb9, 0x789b,
+ 0x0018, 0x78ab, 0x0003, 0x789b, 0x0081, 0x78ab, 0x0001, 0xa6b5,
+ 0x1000, 0x6820, 0xa084, 0x8000, 0x0040, 0x3bc5, 0xa6b5, 0x0400,
+ 0x789b, 0x000e, 0x6824, 0x8007, 0x78aa, 0xa684, 0x0200, 0x0040,
+ 0x3bdf, 0x682c, 0x78d2, 0x6830, 0x78d6, 0xa684, 0x0100, 0x0040,
+ 0x3bdd, 0x682c, 0xa084, 0x0001, 0x0040, 0x3bdd, 0x7888, 0xa084,
+ 0x0040, 0x0040, 0x3bdd, 0xa6b5, 0x8000, 0x1078, 0x45ad, 0x7e5a,
+ 0x6eb6, 0x0078, 0x45e4, 0x1078, 0x38c6, 0x00c0, 0x3c6c, 0x702c,
+ 0x8004, 0x0048, 0x3bf1, 0x2019, 0x4cfd, 0x1078, 0x2255, 0x702f,
+ 0x0001, 0x2011, 0x0001, 0x2031, 0x1000, 0x789b, 0x0018, 0x6814,
+ 0xa084, 0x001f, 0xa085, 0x0080, 0x78aa, 0x691c, 0xa184, 0x0002,
+ 0x0040, 0x3c0a, 0xa6b5, 0x0004, 0x78ab, 0x0020, 0x6828, 0x78aa,
+ 0xa290, 0x0002, 0x6820, 0xa084, 0x8000, 0x0040, 0x3c18, 0xa6b5,
+ 0x0400, 0x789b, 0x000e, 0x6824, 0x8007, 0x78aa, 0x0078, 0x3c26,
+ 0x681c, 0xa084, 0x8000, 0x00c0, 0x3c26, 0xa6b5, 0x0800, 0x6820,
+ 0xa084, 0x0100, 0x0040, 0x3c26, 0xa6b5, 0x4000, 0x681c, 0xa084,
+ 0x00c0, 0x8003, 0x8003, 0x8007, 0xa080, 0x3c72, 0x2004, 0xa635,
+ 0xa684, 0x0100, 0x0040, 0x3c40, 0x682c, 0xa084, 0x0001, 0x0040,
+ 0x3c40, 0x7888, 0xa084, 0x0040, 0x0040, 0x3c40, 0xa6b5, 0x8000,
+ 0x789b, 0x007e, 0x7eae, 0x6eb6, 0x6814, 0x8007, 0x78aa, 0x7882,
+ 0x7aaa, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3c6c, 0x0018, 0x3c6c,
+ 0x70b4, 0xa080, 0x00dd, 0x781a, 0x1078, 0x38de, 0xa684, 0x0200,
+ 0x0040, 0x3c60, 0x682c, 0x78d2, 0x6830, 0x78d6, 0x1078, 0x45ad,
+ 0x2d00, 0x70a2, 0x704a, 0x6810, 0x70be, 0x7003, 0x0007, 0xad80,
+ 0x000f, 0x7036, 0x0078, 0x2438, 0x1078, 0x1b45, 0x1078, 0x38de,
+ 0x0078, 0x2438, 0x0000, 0x0300, 0x0200, 0x0000, 0x1078, 0x23ca,
+ 0x2300, 0x0079, 0x3c7b, 0x3c7e, 0x3c7e, 0x3c80, 0x1078, 0x23ca,
+ 0x1078, 0x45bc, 0x6924, 0xa184, 0x00ff, 0xa086, 0x000a, 0x0040,
+ 0x3c92, 0xa184, 0xff00, 0xa085, 0x000a, 0x6826, 0x1078, 0x1b45,
+ 0x0078, 0x3b60, 0x2001, 0x000a, 0x1078, 0x454c, 0x0078, 0x3b60,
+ 0xa282, 0x0005, 0x0050, 0x3c9e, 0x1078, 0x23ca, 0x7000, 0xa084,
+ 0x0007, 0x10c0, 0x398a, 0x1078, 0x191a, 0x00c0, 0x3cbd, 0xa684,
+ 0x0004, 0x0040, 0x3caf, 0x2001, 0x2800, 0x0078, 0x3cb1, 0x2001,
+ 0x0800, 0x71b4, 0xa188, 0x0091, 0x789b, 0x000e, 0x78aa, 0x2031,
+ 0x0400, 0x7e5a, 0x791a, 0x0078, 0x2438, 0x6807, 0x0106, 0x680b,
+ 0x0000, 0x689f, 0x0000, 0x6827, 0x0000, 0xa386, 0x0002, 0x00c0,
+ 0x3cde, 0xa286, 0x0002, 0x00c0, 0x3cde, 0x78a0, 0xa005, 0x00c0,
+ 0x3cde, 0xa484, 0x8000, 0x00c0, 0x3cde, 0x78e4, 0xa084, 0x0008,
+ 0x0040, 0x3cde, 0xa6b5, 0x0008, 0x2019, 0x0000, 0x1078, 0x40d3,
+ 0x2d00, 0x70a2, 0x704a, 0x7003, 0x0007, 0x7037, 0x0000, 0x6824,
+ 0xa084, 0x0080, 0x0040, 0x3cf0, 0x1078, 0x4180, 0x0078, 0x2438,
+ 0x2300, 0x0079, 0x3cf3, 0x3cf6, 0x3d77, 0x3d96, 0x2200, 0x0079,
+ 0x3cf9, 0x3cfe, 0x3d0e, 0x3d34, 0x3d40, 0x3d63, 0x2029, 0x0001,
+ 0xa026, 0x2011, 0x0000, 0x1078, 0x428d, 0x0079, 0x3d07, 0x3d0c,
+ 0x2438, 0x3b60, 0x3d0c, 0x3d0c, 0x1078, 0x23ca, 0x7990, 0xa18c,
+ 0x0007, 0x00c0, 0x3d15, 0x2009, 0x0008, 0x2011, 0x0001, 0xa684,
+ 0x0004, 0x0040, 0x3d1d, 0x2011, 0x0003, 0x2220, 0xa12a, 0x2011,
+ 0x0001, 0x1078, 0x428d, 0x0079, 0x3d25, 0x3d2a, 0x2438, 0x3b60,
+ 0x3d32, 0x3d2c, 0x0078, 0x45ea, 0x70ab, 0x3d30, 0x0078, 0x2438,
+ 0x0078, 0x3d2a, 0x1078, 0x23ca, 0xa684, 0x0010, 0x0040, 0x3d3e,
+ 0x1078, 0x414f, 0x0040, 0x3d3e, 0x0078, 0x2438, 0x0078, 0x41bc,
+ 0x6000, 0xa084, 0x0002, 0x0040, 0x3d5d, 0x70b4, 0xa080, 0x00cd,
+ 0x781a, 0x0d7e, 0x1078, 0x45c5, 0x2d00, 0x682e, 0x6827, 0x0000,
+ 0x1078, 0x3ac2, 0x0d7f, 0x1078, 0x193d, 0x7003, 0x0000, 0x7037,
+ 0x0000, 0x704b, 0x0000, 0x0078, 0x3b60, 0xa684, 0x0004, 0x00c0,
+ 0x3d63, 0x0078, 0x45ea, 0x6000, 0xa084, 0x0004, 0x00c0, 0x3d75,
+ 0x6000, 0xa084, 0x0001, 0x0040, 0x3d75, 0x70ab, 0x3d75, 0x2001,
+ 0x0007, 0x1078, 0x4544, 0x0078, 0x45f0, 0x0078, 0x45ea, 0x2200,
+ 0x0079, 0x3d7a, 0x3d7f, 0x3d7f, 0x3d7f, 0x3d81, 0x3d7f, 0x1078,
+ 0x23ca, 0x70a7, 0x3d85, 0x0078, 0x45f6, 0x2011, 0x0018, 0x1078,
+ 0x4287, 0x0079, 0x3d8b, 0x3d90, 0x2438, 0x3b60, 0x3d92, 0x3d94,
+ 0x1078, 0x23ca, 0x1078, 0x23ca, 0x1078, 0x23ca, 0x2200, 0x0079,
+ 0x3d99, 0x3d9e, 0x3da0, 0x3da0, 0x3d9e, 0x3d9e, 0x1078, 0x23ca,
+ 0x78e4, 0xa084, 0x0008, 0x0040, 0x3db5, 0x70a7, 0x3da9, 0x0078,
+ 0x45f6, 0x2011, 0x0004, 0x1078, 0x4287, 0x0079, 0x3daf, 0x3db5,
+ 0x2438, 0x3b60, 0x3db5, 0x3dbf, 0x3dc3, 0x70ab, 0x3dbd, 0x2001,
+ 0x0003, 0x1078, 0x4544, 0x0078, 0x45f0, 0x0078, 0x45ea, 0x70ab,
+ 0x3db5, 0x0078, 0x2438, 0x70ab, 0x3dc7, 0x0078, 0x2438, 0x0078,
+ 0x3dbd, 0xa282, 0x0003, 0x0050, 0x3dcf, 0x1078, 0x23ca, 0xa386,
+ 0x0002, 0x00c0, 0x3de8, 0xa286, 0x0002, 0x00c0, 0x3dee, 0x78a0,
+ 0xa005, 0x00c0, 0x3dee, 0xa484, 0x8000, 0x00c0, 0x3dee, 0x78e4,
+ 0xa084, 0x0008, 0x0040, 0x3de8, 0xa6b5, 0x0008, 0x2019, 0x0000,
+ 0xa684, 0x0008, 0x0040, 0x3dee, 0x1078, 0x412c, 0x6810, 0x70be,
+ 0x7003, 0x0007, 0x2300, 0x0079, 0x3df5, 0x3df8, 0x3e25, 0x3e2d,
+ 0x2200, 0x0079, 0x3dfb, 0x3e00, 0x3dfe, 0x3e19, 0x1078, 0x23ca,
+ 0x7990, 0xa1ac, 0x0007, 0xa026, 0x2011, 0x0001, 0x1078, 0x428d,
+ 0x0079, 0x3e0a, 0x3e0f, 0x2438, 0x3b60, 0x3e17, 0x3e11, 0x0078,
+ 0x45ea, 0x70ab, 0x3e15, 0x0078, 0x2438, 0x0078, 0x3e0f, 0x1078,
+ 0x23ca, 0xa684, 0x0010, 0x0040, 0x3e23, 0x1078, 0x414f, 0x0040,
+ 0x3e23, 0x0078, 0x2438, 0x0078, 0x41bc, 0x2200, 0x0079, 0x3e28,
+ 0x3e2b, 0x3e2b, 0x3e2b, 0x1078, 0x23ca, 0x2200, 0x0079, 0x3e30,
+ 0x3e33, 0x3e35, 0x3e35, 0x1078, 0x23ca, 0x78e4, 0xa084, 0x0008,
+ 0x0040, 0x3e4a, 0x70a7, 0x3e3e, 0x0078, 0x45f6, 0x2011, 0x0004,
+ 0x1078, 0x4287, 0x0079, 0x3e44, 0x3e4a, 0x2438, 0x3b60, 0x3e4a,
+ 0x3e54, 0x3e58, 0x70ab, 0x3e52, 0x2001, 0x0003, 0x1078, 0x4544,
+ 0x0078, 0x45f0, 0x0078, 0x45ea, 0x70ab, 0x3e4a, 0x0078, 0x2438,
+ 0x70ab, 0x3e5c, 0x0078, 0x2438, 0x0078, 0x3e52, 0x2300, 0x0079,
+ 0x3e61, 0x3e66, 0x3e68, 0x3e64, 0x1078, 0x23ca, 0x70a4, 0x007a,
+ 0x70a4, 0x007a, 0xa282, 0x0002, 0x0050, 0x3e70, 0x1078, 0x23ca,
+ 0xa684, 0x0200, 0x0040, 0x3e7a, 0x1078, 0x45b5, 0x1078, 0x426f,
+ 0x1078, 0x45bc, 0x2300, 0x0079, 0x3e7d, 0x3e80, 0x3ea4, 0x3f0a,
+ 0xa286, 0x0001, 0x0040, 0x3e86, 0x1078, 0x23ca, 0xa684, 0x0200,
+ 0x0040, 0x3e8e, 0x1078, 0x45b5, 0x1078, 0x45bc, 0x2001, 0x0001,
+ 0x1078, 0x454c, 0x78b8, 0xa084, 0xc001, 0x0040, 0x3ea0, 0x7848,
+ 0xa085, 0x0008, 0x784a, 0x7848, 0xa084, 0x0008, 0x00c0, 0x3e9b,
+ 0x7003, 0x0000, 0x0078, 0x3b60, 0x2200, 0x0079, 0x3ea7, 0x3ea9,
+ 0x3eda, 0x70a7, 0x3ead, 0x0078, 0x45f6, 0x2011, 0x000d, 0x1078,
+ 0x4287, 0x0079, 0x3eb3, 0x3eba, 0x2438, 0x3b60, 0x3ec2, 0x3eca,
+ 0x3ed0, 0x3ed2, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+ 0x0078, 0x45e4, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+ 0x0078, 0x45e4, 0x70ab, 0x3ece, 0x0078, 0x2438, 0x0078, 0x3eba,
+ 0x1078, 0x23ca, 0x70ab, 0x3ed6, 0x0078, 0x2438, 0x1078, 0x45fc,
+ 0x0078, 0x2438, 0x70a7, 0x3ede, 0x0078, 0x45f6, 0x2011, 0x0012,
+ 0x1078, 0x4287, 0x0079, 0x3ee4, 0x3eea, 0x2438, 0x3b60, 0x3ef6,
+ 0x3efe, 0x3f04, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+ 0x70b4, 0xa080, 0x00a5, 0x781a, 0x0078, 0x2438, 0xa6b4, 0x00ff,
+ 0xa6b5, 0x0400, 0x6eb6, 0x7e5a, 0x0078, 0x45e4, 0x70ab, 0x3f02,
+ 0x0078, 0x2438, 0x0078, 0x3eea, 0x70ab, 0x3f08, 0x0078, 0x2438,
+ 0x0078, 0x3ef6, 0xa286, 0x0001, 0x0040, 0x3f10, 0x1078, 0x23ca,
+ 0x70a7, 0x3f14, 0x0078, 0x45f6, 0x2011, 0x0015, 0x1078, 0x4287,
+ 0x0079, 0x3f1a, 0x3f1f, 0x2438, 0x3b60, 0x3f2d, 0x3f39, 0xa6b4,
+ 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a, 0x783b, 0x1301, 0x70b4,
+ 0xa080, 0x00b5, 0x781a, 0x0078, 0x2438, 0xa6b4, 0x00ff, 0xa6b5,
+ 0x0400, 0x6eb6, 0x7e5a, 0x70b4, 0xa080, 0x00a5, 0x781a, 0x0078,
+ 0x2438, 0x70ab, 0x3f3d, 0x0078, 0x2438, 0x0078, 0x3f1f, 0xa282,
+ 0x0003, 0x0050, 0x3f45, 0x1078, 0x23ca, 0x2300, 0x0079, 0x3f48,
+ 0x3f4b, 0x3f82, 0x3fdd, 0xa286, 0x0001, 0x0040, 0x3f51, 0x1078,
+ 0x23ca, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x3f5e,
+ 0x1078, 0x3ac2, 0x7003, 0x0000, 0x0078, 0x3b60, 0x683b, 0x0000,
+ 0x6837, 0x0000, 0xa684, 0x0200, 0x0040, 0x3f6c, 0x1078, 0x45b5,
+ 0x1078, 0x426f, 0x1078, 0x45bc, 0x2001, 0x0001, 0x1078, 0x454c,
+ 0x78b8, 0xa084, 0xc001, 0x0040, 0x3f7e, 0x7848, 0xa085, 0x0008,
+ 0x784a, 0x7848, 0xa084, 0x0008, 0x00c0, 0x3f79, 0x7003, 0x0000,
+ 0x0078, 0x3b60, 0x2200, 0x0079, 0x3f85, 0x3f87, 0x3fb8, 0x70a7,
+ 0x3f8b, 0x0078, 0x45f6, 0x2011, 0x000d, 0x1078, 0x4287, 0x0079,
+ 0x3f91, 0x3f98, 0x2438, 0x3b60, 0x3fa0, 0x3fa8, 0x3fae, 0x3fb0,
+ 0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x45e4,
+ 0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x45e4,
+ 0x70ab, 0x3fac, 0x0078, 0x2438, 0x0078, 0x3f98, 0x1078, 0x23ca,
+ 0x70ab, 0x3fb4, 0x0078, 0x2438, 0x1078, 0x45fc, 0x0078, 0x2438,
+ 0x70a7, 0x3fbc, 0x0078, 0x45f6, 0x2011, 0x0005, 0x1078, 0x4287,
+ 0x0079, 0x3fc2, 0x3fc7, 0x2438, 0x3b60, 0x3fcf, 0x3fd7, 0xa6b4,
+ 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x45e4, 0xa6b4,
+ 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x45e4, 0x70ab,
+ 0x3fdb, 0x0078, 0x2438, 0x0078, 0x3fc7, 0xa286, 0x0001, 0x0040,
+ 0x3fe3, 0x1078, 0x23ca, 0x70a7, 0x3fe7, 0x0078, 0x45f6, 0x2011,
+ 0x0006, 0x1078, 0x4287, 0x0079, 0x3fed, 0x3ff2, 0x2438, 0x3b60,
+ 0x3ff8, 0x4002, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x45e4,
+ 0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0xa6b5, 0x4000, 0x7e5a,
+ 0x0078, 0x45e4, 0x70ab, 0x4006, 0x0078, 0x2438, 0x0078, 0x3ff2,
+ 0x2300, 0x0079, 0x400b, 0x4010, 0x400e, 0x400e, 0x1078, 0x23ca,
+ 0x1078, 0x23ca, 0x2300, 0x71a8, 0xa005, 0x017a, 0x6810, 0x70be,
+ 0xa282, 0x0003, 0x0050, 0x401e, 0x1078, 0x23ca, 0x2300, 0x0079,
+ 0x4021, 0x4024, 0x4037, 0x4059, 0x82ff, 0x00c0, 0x4029, 0x1078,
+ 0x23ca, 0xa684, 0x0200, 0x0040, 0x4031, 0x1078, 0x45b5, 0x1078,
+ 0x45bc, 0x2001, 0x0001, 0x1078, 0x454c, 0x0078, 0x2438, 0xa296,
+ 0x0002, 0x0040, 0x4040, 0x82ff, 0x0040, 0x4040, 0x1078, 0x23ca,
+ 0x70a7, 0x4044, 0x0078, 0x45f6, 0x2011, 0x0018, 0x1078, 0x4287,
+ 0x0079, 0x404a, 0x404f, 0x2438, 0x3b60, 0x4051, 0x4053, 0x0078,
+ 0x45e4, 0x0078, 0x45e4, 0x70ab, 0x4057, 0x0078, 0x2438, 0x0078,
+ 0x404f, 0x2200, 0x0079, 0x405c, 0x405e, 0x4077, 0x70a7, 0x4062,
+ 0x0078, 0x45f6, 0x2011, 0x0017, 0x1078, 0x4287, 0x0079, 0x4068,
+ 0x406d, 0x2438, 0x3b60, 0x406f, 0x4071, 0x0078, 0x45e4, 0x0078,
+ 0x45e4, 0x70ab, 0x4075, 0x0078, 0x2438, 0x0078, 0x406d, 0xa484,
+ 0x8000, 0x00c0, 0x40c1, 0xa684, 0x0100, 0x0040, 0x408b, 0x1078,
+ 0x45b5, 0x1078, 0x426f, 0x1078, 0x45bc, 0x7848, 0xa085, 0x000c,
+ 0x784a, 0x0078, 0x408f, 0x78d8, 0x78d2, 0x78dc, 0x78d6, 0xa6b4,
+ 0xefff, 0x7e5a, 0x70a7, 0x4096, 0x0078, 0x45f6, 0x2011, 0x000d,
+ 0x1078, 0x4287, 0x0079, 0x409c, 0x40a3, 0x2438, 0x3b60, 0x40a3,
+ 0x40b1, 0x40b7, 0x40b9, 0xa684, 0x0100, 0x0040, 0x40af, 0x1078,
+ 0x4573, 0x682c, 0x78d2, 0x6830, 0x78d6, 0x1078, 0x45ad, 0x0078,
+ 0x45e4, 0x70ab, 0x40b5, 0x0078, 0x2438, 0x0078, 0x40a3, 0x1078,
+ 0x23ca, 0x70ab, 0x40bd, 0x0078, 0x2438, 0x1078, 0x45fc, 0x0078,
+ 0x2438, 0x1078, 0x45bc, 0x70ab, 0x40cb, 0x2001, 0x0003, 0x1078,
+ 0x4544, 0x0078, 0x45f0, 0x1078, 0x45ad, 0x682c, 0x78d2, 0x6830,
+ 0x78d6, 0x0078, 0x45e4, 0x70b8, 0x6812, 0x70be, 0x8000, 0x70ba,
+ 0x681b, 0x0000, 0xa684, 0x0008, 0x0040, 0x40f6, 0x157e, 0x137e,
+ 0x147e, 0x7890, 0x8004, 0x8004, 0x8004, 0x8004, 0xa084, 0x000f,
+ 0x681a, 0x80ac, 0x789b, 0x0000, 0xaf80, 0x002b, 0x2098, 0xad80,
+ 0x000b, 0x20a0, 0x53a5, 0x147f, 0x137f, 0x157f, 0xa6c4, 0x0f00,
+ 0xa684, 0x0002, 0x00c0, 0x4102, 0x692c, 0x810d, 0x810d, 0x810d,
+ 0x0078, 0x410f, 0x789b, 0x0010, 0x79ac, 0x0078, 0x410f, 0x017e,
+ 0x2009, 0x0005, 0x2001, 0x3d00, 0x1078, 0x457e, 0x017f, 0xa184,
+ 0x001f, 0xa805, 0x6816, 0x1078, 0x3b33, 0x68be, 0xa684, 0x0004,
+ 0x0040, 0x4120, 0xa18c, 0xff00, 0x78a8, 0xa084, 0x00ff, 0xa105,
+ 0x682a, 0xa6b4, 0x00ff, 0x6000, 0xa084, 0x0008, 0x0040, 0x412a,
+ 0xa6b5, 0x4000, 0x6eb6, 0x007c, 0x157e, 0x137e, 0x147e, 0x6918,
+ 0x7890, 0x8004, 0x8004, 0x8004, 0x8004, 0xa084, 0x000f, 0x007e,
+ 0xa100, 0x681a, 0x007f, 0x8000, 0x8004, 0x0040, 0x414b, 0x20a8,
+ 0x8104, 0xa080, 0x000b, 0xad00, 0x20a0, 0x789b, 0x0000, 0xaf80,
+ 0x002b, 0x2098, 0x53a5, 0x147f, 0x137f, 0x157f, 0x007c, 0x682c,
+ 0xa084, 0x0020, 0x00c0, 0x4157, 0x620c, 0x0078, 0x4158, 0x6210,
+ 0x6b18, 0x2300, 0xa202, 0x0040, 0x4178, 0x2018, 0xa382, 0x000e,
+ 0x0048, 0x4168, 0x0040, 0x4168, 0x2019, 0x000e, 0x0078, 0x416c,
+ 0x7858, 0xa084, 0xffef, 0x785a, 0x783b, 0x1b01, 0x7893, 0x0000,
+ 0x7ba2, 0x70b4, 0xa080, 0x008e, 0x781a, 0xa085, 0x0001, 0x007c,
+ 0x7858, 0xa084, 0xffef, 0x785a, 0x7893, 0x0000, 0xa006, 0x007c,
+ 0x6904, 0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x418d, 0xa196,
+ 0x000f, 0x0040, 0x418d, 0x6807, 0x0117, 0x6914, 0x1078, 0x3b33,
+ 0x6100, 0x8104, 0x00c8, 0x41a8, 0x601c, 0xa005, 0x0040, 0x419c,
+ 0x2001, 0x0800, 0x0078, 0x41aa, 0x0d7e, 0x6824, 0x007e, 0x1078,
+ 0x45c5, 0x007f, 0x6826, 0x2d00, 0x682e, 0x1078, 0x3ac2, 0x0d7f,
+ 0x2001, 0x0200, 0x6826, 0x8007, 0x789b, 0x000e, 0x78aa, 0x6820,
+ 0xa085, 0x8000, 0x6822, 0x2031, 0x0400, 0x6eb6, 0x7e5a, 0x71b4,
+ 0xa188, 0x0091, 0x791a, 0x007c, 0xa6c4, 0x0f00, 0xa684, 0x0002,
+ 0x00c0, 0x41cf, 0x692c, 0x810d, 0x810d, 0x810d, 0xa184, 0x001f,
+ 0xa805, 0x6816, 0x1078, 0x3b33, 0x68be, 0x0078, 0x41d2, 0x6914,
+ 0x1078, 0x3b33, 0x6100, 0x8104, 0x00c8, 0x421c, 0xa184, 0x0300,
+ 0x0040, 0x41de, 0x6807, 0x0117, 0x0078, 0x41fc, 0x6004, 0xa005,
+ 0x00c0, 0x4205, 0x6807, 0x0117, 0x601c, 0xa005, 0x00c0, 0x41f2,
+ 0x0d7e, 0x1078, 0x45c5, 0x6827, 0x0034, 0x2d00, 0x682e, 0x1078,
+ 0x3ac2, 0x0d7f, 0xa684, 0x0004, 0x0040, 0x41fc, 0x2031, 0x0400,
+ 0x2001, 0x2800, 0x0078, 0x4200, 0x2031, 0x0400, 0x2001, 0x0800,
+ 0x71b4, 0xa188, 0x0091, 0x0078, 0x424a, 0x6018, 0xa005, 0x00c0,
+ 0x41f2, 0x601c, 0xa005, 0x00c0, 0x41f2, 0x689f, 0x0000, 0x6827,
+ 0x003d, 0xa684, 0x0001, 0x0040, 0x4258, 0xa6b5, 0x0800, 0x71b4,
+ 0xa188, 0x00ae, 0x0078, 0x4253, 0x6807, 0x0117, 0x2031, 0x0400,
+ 0x692c, 0xa18c, 0x00ff, 0xa186, 0x0012, 0x00c0, 0x422d, 0x2001,
+ 0x4265, 0x2009, 0x0001, 0x0078, 0x423e, 0xa186, 0x0003, 0x00c0,
+ 0x4237, 0x2001, 0x4266, 0x2009, 0x0012, 0x0078, 0x423e, 0x2001,
+ 0x0200, 0x71b4, 0xa188, 0x0091, 0x0078, 0x424a, 0x1078, 0x4598,
+ 0x78a3, 0x0000, 0x681c, 0xa085, 0x0040, 0x681e, 0x71b4, 0xa188,
+ 0x00da, 0xa006, 0x6826, 0x8007, 0x789b, 0x000e, 0x78aa, 0x6820,
+ 0xa085, 0x8000, 0x6822, 0x6eb6, 0x7e5a, 0x791a, 0x0078, 0x2438,
+ 0x6eb6, 0x1078, 0x3ac2, 0x6810, 0x70be, 0x7003, 0x0007, 0x70a3,
+ 0x0000, 0x704b, 0x0000, 0x0078, 0x2438, 0x0023, 0x0070, 0x0005,
+ 0x0000, 0x0a00, 0x0000, 0x0000, 0x0025, 0x0000, 0x0000, 0x683b,
+ 0x0000, 0x6837, 0x0000, 0xa684, 0x0200, 0x0040, 0x4286, 0x78b8,
+ 0xa08c, 0x001f, 0xa084, 0x8000, 0x0040, 0x427f, 0x8108, 0x78d8,
+ 0xa100, 0x6836, 0x78dc, 0xa081, 0x0000, 0x683a, 0x007c, 0x7990,
+ 0x810f, 0xa5ac, 0x0007, 0x2021, 0x0000, 0xa480, 0x0010, 0x789a,
+ 0x79a8, 0xa18c, 0x00ff, 0xa184, 0x0080, 0x00c0, 0x42b5, 0xa182,
+ 0x0020, 0x00c8, 0x42cf, 0xa182, 0x0012, 0x00c8, 0x4536, 0x2100,
+ 0x1079, 0x42a3, 0x007c, 0x4536, 0x447e, 0x4536, 0x4536, 0x42dc,
+ 0x42df, 0x4319, 0x434f, 0x4381, 0x4384, 0x4536, 0x4536, 0x433a,
+ 0x43a8, 0x43e2, 0x4536, 0x4536, 0x4409, 0xa18c, 0x001f, 0x6814,
+ 0xa084, 0x001f, 0xa106, 0x0040, 0x42cc, 0x70b4, 0xa080, 0x00cd,
+ 0x781a, 0x2001, 0x0014, 0x1078, 0x454c, 0x1078, 0x45bc, 0x7003,
+ 0x0000, 0x2001, 0x0002, 0x007c, 0x2001, 0x0000, 0x007c, 0xa182,
+ 0x0024, 0x00c8, 0x4536, 0xa184, 0x0003, 0x1079, 0x42a3, 0x007c,
+ 0x4536, 0x4536, 0x4536, 0x4536, 0x1078, 0x4536, 0x007c, 0x2200,
+ 0x0079, 0x42e2, 0x440c, 0x440c, 0x4306, 0x4306, 0x4306, 0x4306,
+ 0x4306, 0x4306, 0x4306, 0x4306, 0x4304, 0x4306, 0x42fb, 0x4306,
+ 0x4306, 0x4306, 0x4306, 0x4306, 0x430e, 0x4311, 0x440c, 0x4311,
+ 0x4306, 0x4306, 0x4306, 0x0c7e, 0x077e, 0x6f14, 0x1078, 0x36b0,
+ 0x077f, 0x0c7f, 0x0078, 0x4306, 0x1078, 0x44d1, 0x6827, 0x02b3,
+ 0x2009, 0x000b, 0x2001, 0x4800, 0x0078, 0x4440, 0x1078, 0x452b,
+ 0x007c, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4800, 0x0078,
+ 0x4428, 0x2d58, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0,
+ 0x4323, 0x6807, 0x0117, 0x6827, 0x0002, 0x1078, 0x45c5, 0x6827,
+ 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3a92, 0x1078,
+ 0x4466, 0x2b68, 0x1078, 0x3ac2, 0x0d7f, 0x1078, 0x3ac2, 0x2001,
+ 0x0002, 0x007c, 0x1078, 0x4466, 0x2001, 0x0017, 0x1078, 0x454c,
+ 0x70a3, 0x0000, 0x2009, 0x5038, 0x200b, 0x0006, 0x70af, 0x0017,
+ 0x2009, 0x0200, 0x1078, 0x39d2, 0x2001, 0x0001, 0x007c, 0x2200,
+ 0x0079, 0x4352, 0x440c, 0x443d, 0x443d, 0x443d, 0x4373, 0x444d,
+ 0x4379, 0x444d, 0x444d, 0x4450, 0x4450, 0x4455, 0x4455, 0x436b,
+ 0x436b, 0x443d, 0x443d, 0x444d, 0x443d, 0x4379, 0x440c, 0x4379,
+ 0x4379, 0x4379, 0x4379, 0x6827, 0x0084, 0x2009, 0x000b, 0x2001,
+ 0x4300, 0x0078, 0x445f, 0x2009, 0x000b, 0x2001, 0x4300, 0x0078,
+ 0x4440, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4300, 0x0078,
+ 0x4428, 0x2001, 0x0000, 0x007c, 0x2200, 0x0079, 0x4387, 0x440c,
+ 0x43a0, 0x43a0, 0x43a0, 0x43a0, 0x444d, 0x444d, 0x444d, 0x444d,
+ 0x444d, 0x444d, 0x444d, 0x444d, 0x43a0, 0x43a0, 0x43a0, 0x43a0,
+ 0x444d, 0x43a0, 0x43a0, 0x444d, 0x444d, 0x444d, 0x444d, 0x440c,
+ 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4300, 0x0078, 0x4428,
+ 0xa684, 0x0004, 0x00c0, 0x43bc, 0x6804, 0xa084, 0x00ff, 0xa086,
+ 0x0006, 0x00c0, 0x4536, 0x1078, 0x4466, 0x6807, 0x0117, 0x1078,
+ 0x3ac2, 0x2001, 0x0002, 0x007c, 0x6000, 0xa084, 0x0004, 0x0040,
+ 0x4536, 0x2d58, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0,
+ 0x43cb, 0x6807, 0x0117, 0x6827, 0x0002, 0x1078, 0x45c5, 0x6827,
+ 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3aa1, 0x1078,
+ 0x4466, 0x2b68, 0x1078, 0x3ac2, 0x0d7f, 0x1078, 0x3ac2, 0x2001,
+ 0x0002, 0x007c, 0x6000, 0xa084, 0x0004, 0x0040, 0x4536, 0x2d58,
+ 0x6a04, 0xa294, 0x00ff, 0xa286, 0x0006, 0x00c0, 0x43f1, 0x6807,
+ 0x0117, 0x6827, 0x0002, 0x2d58, 0x1078, 0x45c5, 0x6827, 0x0036,
+ 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3ab1, 0x1078, 0x4466,
+ 0x2b68, 0x1078, 0x3ac2, 0x0d7f, 0x1078, 0x3ac2, 0x2001, 0x0002,
+ 0x007c, 0x1078, 0x4536, 0x007c, 0x70b4, 0xa080, 0x00cd, 0x781a,
+ 0x2001, 0x0001, 0x1078, 0x454c, 0x1078, 0x45bc, 0x7003, 0x0000,
+ 0x2001, 0x0002, 0x007c, 0x1078, 0x457e, 0x1078, 0x45b5, 0x1078,
+ 0x426f, 0x1078, 0x4180, 0x1078, 0x45bc, 0x2001, 0x0001, 0x007c,
+ 0x1078, 0x457e, 0x1078, 0x45b5, 0x1078, 0x426f, 0x70b4, 0xa080,
+ 0x00cd, 0x781a, 0x2001, 0x0013, 0x1078, 0x454c, 0x1078, 0x45bc,
+ 0x7003, 0x0000, 0x2001, 0x0002, 0x007c, 0x1078, 0x4536, 0x007c,
+ 0x1078, 0x457e, 0x1078, 0x45b5, 0x1078, 0x426f, 0x1078, 0x4180,
+ 0x1078, 0x45bc, 0x2001, 0x0001, 0x007c, 0x2001, 0x0003, 0x007c,
+ 0x1078, 0x44d1, 0x2001, 0x0000, 0x007c, 0x0c7e, 0x077e, 0x6f14,
+ 0x1078, 0x36b0, 0x077f, 0x0c7f, 0x2001, 0x0000, 0x007c, 0x1078,
+ 0x457e, 0x1078, 0x4536, 0x2001, 0x0006, 0x007c, 0x6904, 0xa18c,
+ 0x00ff, 0xa186, 0x0007, 0x0040, 0x4471, 0xa186, 0x000f, 0x00c0,
+ 0x4475, 0x1078, 0x45b5, 0x1078, 0x426f, 0x70b4, 0xa080, 0x00cd,
+ 0x781a, 0x1078, 0x45bc, 0x7003, 0x0000, 0x007c, 0x7aa8, 0xa294,
+ 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004, 0x00c8, 0x4536,
+ 0x1079, 0x448b, 0x007c, 0x4536, 0x448f, 0x4536, 0x44df, 0xa282,
+ 0x0003, 0x0040, 0x4496, 0x1078, 0x4536, 0x007c, 0x7da8, 0xa5ac,
+ 0x00ff, 0x7ca8, 0xa4a4, 0x00ff, 0xa482, 0x000c, 0x0048, 0x44a4,
+ 0x0040, 0x44a4, 0x2021, 0x000c, 0x701c, 0xa502, 0x00c8, 0x44a9,
+ 0x751c, 0x1078, 0x451c, 0x852b, 0x852b, 0x1078, 0x372e, 0x0040,
+ 0x44b5, 0x1078, 0x44c3, 0x0078, 0x44b9, 0x1078, 0x4518, 0x1078,
+ 0x44d1, 0xa6b5, 0x1000, 0x7e5a, 0x70b4, 0xa080, 0x00b9, 0x781a,
+ 0x2001, 0x0004, 0x007c, 0x0c7e, 0x6914, 0x810f, 0xa18c, 0x000f,
+ 0x810b, 0x810b, 0x810b, 0xa1e0, 0x5280, 0x1078, 0x3538, 0x0c7f,
+ 0x007c, 0x0c7e, 0x6814, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003,
+ 0x8003, 0xa0e0, 0x5280, 0x1078, 0x355f, 0x0c7f, 0x007c, 0xa282,
+ 0x0002, 0x00c0, 0x4536, 0x7aa8, 0xa294, 0x00ff, 0xa284, 0xfffe,
+ 0x0040, 0x44ec, 0x2011, 0x0001, 0x1078, 0x450a, 0x1078, 0x44fc,
+ 0x1078, 0x44d1, 0xa6b5, 0x1000, 0x7e5a, 0x70b4, 0xa080, 0x00b9,
+ 0x781a, 0x2001, 0x0004, 0x007c, 0x0c7e, 0x6814, 0x8007, 0xa084,
+ 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0, 0x5280, 0x1078, 0x3604,
+ 0x0c7f, 0x007c, 0x789b, 0x0018, 0x78ab, 0x0001, 0x78ab, 0x0002,
+ 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0081, 0x78ab, 0x0004, 0x007c,
+ 0x2021, 0x0000, 0x2029, 0x0032, 0x789b, 0x0018, 0x78ab, 0x0001,
+ 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa, 0x7caa, 0x789b, 0x0081,
+ 0x78ab, 0x0005, 0x007c, 0x2001, 0x0003, 0x1078, 0x4544, 0x70b4,
+ 0xa080, 0x00b9, 0x781a, 0x2001, 0x0005, 0x007c, 0x2001, 0x0007,
+ 0x1078, 0x4544, 0xa6b5, 0x1000, 0x7e5a, 0x70b4, 0xa080, 0x00b9,
+ 0x781a, 0x2001, 0x0004, 0x007c, 0x789b, 0x0018, 0x78aa, 0x789b,
+ 0x0081, 0x78ab, 0x0001, 0x007c, 0x6904, 0xa18c, 0x00ff, 0xa196,
+ 0x0007, 0x0040, 0x455a, 0xa196, 0x000f, 0x0040, 0x455a, 0x1078,
+ 0x193d, 0x007c, 0x6924, 0xa194, 0x003f, 0x00c0, 0x4563, 0xa18c,
+ 0xffc0, 0xa105, 0x6826, 0x1078, 0x3ac2, 0x691c, 0xa184, 0x0100,
+ 0x0040, 0x4572, 0x1078, 0x1b7e, 0x6914, 0x1078, 0x3b33, 0x6204,
+ 0x8210, 0x6206, 0x007c, 0x692c, 0x6834, 0x682e, 0xa112, 0x6930,
+ 0x6838, 0x6832, 0xa11b, 0xa200, 0xa301, 0x007c, 0x0c7e, 0xade0,
+ 0x0018, 0x6003, 0x0070, 0x6106, 0x600b, 0x0000, 0x600f, 0x0a00,
+ 0x6013, 0x0000, 0x6017, 0x0000, 0x8007, 0x601a, 0x601f, 0x0000,
+ 0x6023, 0x0000, 0x0c7f, 0x6824, 0xa085, 0x0080, 0x6826, 0x007c,
+ 0x157e, 0x137e, 0x147e, 0x2098, 0xaf80, 0x002d, 0x20a0, 0x81ac,
+ 0x0040, 0x45a3, 0x53a6, 0xa184, 0x0001, 0x0040, 0x45a9, 0x3304,
+ 0x78be, 0x147f, 0x137f, 0x157f, 0x007c, 0x70b0, 0xa005, 0x10c0,
+ 0x23ca, 0x70b3, 0x8000, 0x0078, 0x48f7, 0x71b0, 0x81ff, 0x0040,
+ 0x45bb, 0x1078, 0x49ed, 0x007c, 0x71b0, 0x81ff, 0x0040, 0x45c4,
+ 0x70b3, 0x0000, 0x1078, 0x4633, 0x007c, 0x0c7e, 0x0d7e, 0x1078,
+ 0x191a, 0x0c7f, 0x157e, 0x137e, 0x147e, 0x2da0, 0x2c98, 0x20a9,
+ 0x0031, 0x53a3, 0x147f, 0x137f, 0x157f, 0x6807, 0x010d, 0x680b,
+ 0x0000, 0x7004, 0x8007, 0x681a, 0x6823, 0x0000, 0x681f, 0x0000,
+ 0x689f, 0x0000, 0x0c7f, 0x007c, 0x70b4, 0xa080, 0x0091, 0x781a,
+ 0x0078, 0x2438, 0x70b4, 0xa080, 0x0081, 0x781a, 0x0078, 0x2438,
+ 0x70b4, 0xa080, 0x00b9, 0x781a, 0x0078, 0x2438, 0x70b4, 0xa080,
+ 0x00c3, 0x781a, 0x0078, 0x2438, 0x6904, 0xa18c, 0x00ff, 0xa196,
+ 0x0007, 0x0040, 0x4609, 0xa196, 0x000f, 0x0040, 0x4609, 0x6807,
+ 0x0117, 0x2001, 0x0200, 0x6826, 0x8007, 0x789b, 0x000e, 0x78aa,
+ 0x6820, 0xa085, 0x8000, 0x6822, 0x2031, 0x0400, 0x6eb6, 0x7e5a,
+ 0x71b4, 0xa188, 0x0091, 0x791a, 0x007c, 0x1078, 0x45bc, 0x7848,
+ 0xa085, 0x000c, 0x784a, 0x70b4, 0xa080, 0x00cd, 0x781a, 0x2009,
+ 0x000b, 0x2001, 0x4400, 0x1078, 0x457e, 0x2001, 0x0013, 0x1078,
+ 0x454c, 0x0078, 0x3b60, 0x127e, 0x2091, 0x2200, 0x2049, 0x4633,
+ 0x7000, 0x7204, 0xa205, 0x720c, 0xa215, 0x7008, 0xa084, 0xfff7,
+ 0xa205, 0x0040, 0x4645, 0x0078, 0x464a, 0x7003, 0x0000, 0x127f,
+ 0x2000, 0x007c, 0x7000, 0xa084, 0x0001, 0x00c0, 0x4678, 0x7108,
+ 0x8103, 0x00c8, 0x4657, 0x1078, 0x477a, 0x0078, 0x464f, 0x700c,
+ 0xa08c, 0x00ff, 0x0040, 0x4678, 0x7004, 0x8004, 0x00c8, 0x466f,
+ 0x7014, 0xa005, 0x00c0, 0x466b, 0x7010, 0xa005, 0x0040, 0x466f,
+ 0xa102, 0x00c8, 0x464f, 0x7007, 0x0010, 0x0078, 0x4678, 0x8aff,
+ 0x0040, 0x4678, 0x1078, 0x49c4, 0x00c0, 0x4672, 0x0040, 0x464f,
+ 0x1078, 0x4703, 0x7003, 0x0000, 0x127f, 0x2000, 0x007c, 0x017e,
+ 0x6104, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x468b, 0xa18e,
+ 0x000f, 0x00c0, 0x468e, 0x6040, 0x0078, 0x468f, 0x6428, 0x017f,
+ 0x84ff, 0x0040, 0x46b9, 0x2c70, 0x7004, 0xa0bc, 0x000f, 0xa7b8,
+ 0x46c9, 0x273c, 0x87fb, 0x00c0, 0x46a7, 0x0048, 0x46a1, 0x1078,
+ 0x23ca, 0x609c, 0xa075, 0x0040, 0x46b9, 0x0078, 0x4694, 0x2704,
+ 0xae68, 0x6808, 0xa630, 0x680c, 0xa529, 0x8421, 0x0040, 0x46b9,
+ 0x8738, 0x2704, 0xa005, 0x00c0, 0x46a8, 0x709c, 0xa075, 0x00c0,
+ 0x4694, 0x007c, 0x0000, 0x0005, 0x0009, 0x000d, 0x0011, 0x0015,
+ 0x0019, 0x001d, 0x0000, 0x0003, 0x0009, 0x000f, 0x0015, 0x001b,
+ 0x0000, 0x0000, 0x46be, 0x46bb, 0x0000, 0x0000, 0x8000, 0x0000,
+ 0x46be, 0x0000, 0x46c6, 0x46c3, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x46c6, 0x0000, 0x46c1, 0x46c1, 0x0000, 0x0000, 0x8000, 0x0000,
+ 0x46c1, 0x0000, 0x46c7, 0x46c7, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x46c7, 0x127e, 0x2091, 0x2200, 0x2079, 0x5000, 0x2071, 0x0010,
+ 0x7007, 0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x2071, 0x0020,
+ 0x7007, 0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x2049, 0x0000,
+ 0x127f, 0x2000, 0x007c, 0x2049, 0x4703, 0x2019, 0x0000, 0x7004,
+ 0x8004, 0x00c8, 0x4756, 0x7007, 0x0012, 0x7108, 0x7008, 0xa106,
+ 0x00c0, 0x470d, 0xa184, 0x01e0, 0x0040, 0x4718, 0x1078, 0x23ca,
+ 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8, 0x4723, 0xa184,
+ 0x4000, 0x00c0, 0x470d, 0xa19c, 0x300c, 0xa386, 0x2004, 0x0040,
+ 0x4731, 0xa386, 0x0008, 0x0040, 0x473c, 0xa386, 0x200c, 0x00c0,
+ 0x470d, 0x7200, 0x8204, 0x0048, 0x473c, 0x730c, 0xa384, 0x00ff,
+ 0x0040, 0x473c, 0x1078, 0x23ca, 0x7007, 0x0012, 0x7000, 0xa084,
+ 0x0001, 0x00c0, 0x4756, 0x7008, 0xa084, 0x01e0, 0x00c0, 0x4756,
+ 0x7310, 0x7014, 0xa305, 0x0040, 0x4756, 0x710c, 0xa184, 0x0300,
+ 0x00c0, 0x4756, 0xa184, 0x00ff, 0x00c0, 0x4703, 0x7007, 0x0012,
+ 0x7007, 0x0008, 0x7004, 0xa084, 0x0008, 0x00c0, 0x475a, 0x7007,
+ 0x0012, 0x7108, 0x8103, 0x0048, 0x475f, 0x7003, 0x0000, 0x2049,
+ 0x0000, 0x007c, 0x107e, 0x007e, 0x127e, 0x157e, 0x2091, 0x2200,
+ 0x7108, 0x1078, 0x477a, 0x157f, 0x127f, 0x2091, 0x8001, 0x007f,
+ 0x107f, 0x007c, 0x7204, 0x7500, 0x730c, 0xa384, 0x0300, 0x00c0,
+ 0x47a1, 0xa184, 0x01e0, 0x00c0, 0x47c5, 0x7108, 0xa184, 0x01e0,
+ 0x00c0, 0x47c5, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8,
+ 0x4795, 0xa184, 0x4000, 0x00c0, 0x4785, 0xa184, 0x0007, 0x0079,
+ 0x4799, 0x47a3, 0x47b5, 0x47a1, 0x47b5, 0x47a1, 0x4801, 0x47a1,
+ 0x47ff, 0x1078, 0x23ca, 0x7004, 0xa084, 0x0010, 0xa085, 0x0002,
+ 0x7006, 0x8aff, 0x00c0, 0x47b0, 0x2049, 0x0000, 0x0078, 0x47b4,
+ 0x1078, 0x49c4, 0x00c0, 0x47b0, 0x007c, 0x7004, 0xa084, 0x0010,
+ 0xa085, 0x0002, 0x7006, 0x8aff, 0x00c0, 0x47c0, 0x0078, 0x47c4,
+ 0x1078, 0x49c4, 0x00c0, 0x47c0, 0x007c, 0x7007, 0x0012, 0x7108,
+ 0x00e0, 0x47c8, 0x2091, 0x6000, 0x00e0, 0x47cc, 0x2091, 0x6000,
+ 0x7007, 0x0012, 0x7007, 0x0008, 0x7004, 0xa084, 0x0008, 0x00c0,
+ 0x47d4, 0x7007, 0x0012, 0x7108, 0x8103, 0x0048, 0x47d9, 0x7003,
+ 0x0000, 0x7000, 0xa005, 0x00c0, 0x47ed, 0x7004, 0xa005, 0x00c0,
+ 0x47ed, 0x700c, 0xa005, 0x0040, 0x47ef, 0x0078, 0x47d0, 0x2049,
+ 0x0000, 0x1078, 0x37d7, 0x6818, 0xa084, 0x8000, 0x0040, 0x47fa,
+ 0x681b, 0x0002, 0x007c, 0x1078, 0x23ca, 0x1078, 0x23ca, 0x1078,
+ 0x485d, 0x7210, 0x7114, 0x700c, 0xa09c, 0x00ff, 0x2800, 0xa300,
+ 0xa211, 0xa189, 0x0000, 0x1078, 0x485d, 0x2704, 0x2c58, 0xac60,
+ 0x6308, 0x2200, 0xa322, 0x630c, 0x2100, 0xa31b, 0x2400, 0xa305,
+ 0x0040, 0x4824, 0x00c8, 0x4824, 0x8412, 0x8210, 0x830a, 0xa189,
+ 0x0000, 0x2b60, 0x0078, 0x480b, 0x2b60, 0x8a07, 0x007e, 0x6004,
+ 0xa084, 0x0008, 0x0040, 0x4830, 0xa7ba, 0x46c3, 0x0078, 0x4832,
+ 0xa7ba, 0x46bb, 0x007f, 0xa73d, 0x2c00, 0x6886, 0x6f8a, 0x6c92,
+ 0x6b8e, 0x7007, 0x0012, 0x1078, 0x4703, 0x007c, 0x8738, 0x2704,
+ 0xa005, 0x00c0, 0x4851, 0x609c, 0xa005, 0x0040, 0x485a, 0x2060,
+ 0x6004, 0xa084, 0x000f, 0xa080, 0x46c9, 0x203c, 0x87fb, 0x1040,
+ 0x23ca, 0x8a51, 0x0040, 0x4859, 0x7008, 0xa084, 0x0003, 0xa086,
+ 0x0003, 0x007c, 0x2051, 0x0000, 0x007c, 0x8a50, 0x8739, 0x2704,
+ 0xa004, 0x00c0, 0x4871, 0x6000, 0xa064, 0x00c0, 0x4868, 0x2d60,
+ 0x6004, 0xa084, 0x000f, 0xa080, 0x46d9, 0x203c, 0x87fb, 0x1040,
+ 0x23ca, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x6884,
+ 0x2060, 0x6888, 0x6b8c, 0x6c90, 0x8057, 0xaad4, 0x00ff, 0xa084,
+ 0x00ff, 0x007e, 0x6804, 0xa084, 0x0008, 0x007f, 0x0040, 0x488c,
+ 0xa0b8, 0x46c3, 0x0078, 0x488e, 0xa0b8, 0x46bb, 0x7e08, 0xa6b5,
+ 0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x489c,
+ 0xa18e, 0x000f, 0x00c0, 0x48a5, 0x681c, 0xa084, 0x0040, 0x0040,
+ 0x48ac, 0xa6b5, 0x0001, 0x0078, 0x48ac, 0x681c, 0xa084, 0x0040,
+ 0x0040, 0x48ac, 0xa6b5, 0x0001, 0x7007, 0x0004, 0x7004, 0xa084,
+ 0x0004, 0x00c0, 0x48ae, 0x2400, 0xa305, 0x00c0, 0x48b9, 0x0078,
+ 0x48df, 0x2c58, 0x2704, 0x6104, 0xac60, 0x6000, 0xa400, 0x701a,
+ 0x6004, 0xa301, 0x701e, 0xa184, 0x0008, 0x0040, 0x48cf, 0x6010,
+ 0xa081, 0x0000, 0x7022, 0x6014, 0xa081, 0x0000, 0x7026, 0x6208,
+ 0x2400, 0xa202, 0x7012, 0x620c, 0x2300, 0xa203, 0x7016, 0x7602,
+ 0x7007, 0x0001, 0x2b60, 0x1078, 0x483e, 0x0078, 0x48e1, 0x1078,
+ 0x49c4, 0x00c0, 0x48df, 0x127f, 0x2000, 0x007c, 0x127e, 0x0d7e,
+ 0x2091, 0x2200, 0x0d7f, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004,
+ 0x00c0, 0x48ed, 0x7003, 0x0008, 0x127f, 0x2000, 0x007c, 0x127e,
+ 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049, 0x48f7, 0x7007, 0x0004,
+ 0x7004, 0xa084, 0x0004, 0x00c0, 0x4900, 0x7e08, 0xa6b5, 0x000c,
+ 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4913, 0xa18e,
+ 0x000f, 0x00c0, 0x491e, 0x681c, 0xa084, 0x0040, 0x0040, 0x491a,
+ 0xa6b5, 0x0001, 0x6840, 0x2050, 0x0078, 0x4927, 0x681c, 0xa084,
+ 0x0020, 0x00c0, 0x4925, 0xa6b5, 0x0001, 0x6828, 0x2050, 0x2d60,
+ 0x6004, 0xa0bc, 0x000f, 0xa7b8, 0x46c9, 0x273c, 0x87fb, 0x00c0,
+ 0x493b, 0x0048, 0x4935, 0x1078, 0x23ca, 0x689c, 0xa065, 0x0040,
+ 0x493f, 0x0078, 0x4928, 0x1078, 0x49c4, 0x00c0, 0x493b, 0x127f,
+ 0x2000, 0x007c, 0x127e, 0x007e, 0x017e, 0x0d7e, 0x2091, 0x2200,
+ 0x0d7f, 0x037f, 0x047f, 0x7e08, 0xa6b5, 0x000c, 0x6904, 0xa18c,
+ 0x00ff, 0xa186, 0x0007, 0x0040, 0x4959, 0xa18e, 0x000f, 0x00c0,
+ 0x4962, 0x681c, 0xa084, 0x0040, 0x0040, 0x4969, 0xa6b5, 0x0001,
+ 0x0078, 0x4969, 0x681c, 0xa084, 0x0040, 0x0040, 0x4969, 0xa6b5,
+ 0x0001, 0x2049, 0x4942, 0x017e, 0x6904, 0xa18c, 0x00ff, 0xa186,
+ 0x0007, 0x0040, 0x4977, 0xa18e, 0x000f, 0x00c0, 0x497a, 0x6840,
+ 0x0078, 0x497b, 0x6828, 0x017f, 0xa055, 0x0040, 0x49c1, 0x2d70,
+ 0x2e60, 0x7004, 0xa0bc, 0x000f, 0xa7b8, 0x46c9, 0x273c, 0x87fb,
+ 0x00c0, 0x4995, 0x0048, 0x498e, 0x1078, 0x23ca, 0x709c, 0xa075,
+ 0x2060, 0x0040, 0x49c1, 0x0078, 0x4981, 0x2704, 0xae68, 0x6808,
+ 0xa422, 0x680c, 0xa31b, 0x0048, 0x49ae, 0x8a51, 0x00c0, 0x49a2,
+ 0x1078, 0x23ca, 0x8738, 0x2704, 0xa005, 0x00c0, 0x4996, 0x709c,
+ 0xa075, 0x2060, 0x0040, 0x49c1, 0x0078, 0x4981, 0x8422, 0x8420,
+ 0x831a, 0xa399, 0x0000, 0x6908, 0x2400, 0xa122, 0x690c, 0x2300,
+ 0xa11b, 0x00c8, 0x49bd, 0x1078, 0x23ca, 0x2071, 0x0020, 0x0078,
+ 0x48ac, 0x127f, 0x2000, 0x007c, 0x7008, 0xa084, 0x0003, 0xa086,
+ 0x0003, 0x0040, 0x49ec, 0x2704, 0xac08, 0x2104, 0x701a, 0x8108,
+ 0x2104, 0x701e, 0x8108, 0x2104, 0x7012, 0x8108, 0x2104, 0x7016,
+ 0x6004, 0xa084, 0x0008, 0x0040, 0x49e3, 0x8108, 0x2104, 0x7022,
+ 0x8108, 0x2104, 0x7026, 0x7602, 0x7004, 0xa084, 0x0010, 0xa085,
+ 0x0001, 0x7006, 0x1078, 0x483e, 0x007c, 0x127e, 0x007e, 0x0d7e,
+ 0x2091, 0x2200, 0x2049, 0x49ed, 0x0d7f, 0x087f, 0x7108, 0xa184,
+ 0x0003, 0x00c0, 0x4a17, 0x017e, 0x6904, 0xa18c, 0x00ff, 0xa186,
+ 0x0007, 0x0040, 0x4a07, 0xa18e, 0x000f, 0x00c0, 0x4a0a, 0x6840,
+ 0x0078, 0x4a0b, 0x6828, 0x017f, 0xa005, 0x0040, 0x4a25, 0x0078,
+ 0x464a, 0x0020, 0x4a17, 0x1078, 0x4801, 0x0078, 0x4a25, 0x00a0,
+ 0x4a1e, 0x7108, 0x1078, 0x477a, 0x0078, 0x49f6, 0x7007, 0x0010,
+ 0x00a0, 0x4a20, 0x7108, 0x1078, 0x477a, 0x7008, 0xa086, 0x0008,
+ 0x00c0, 0x49f6, 0x7000, 0xa005, 0x00c0, 0x49f6, 0x7003, 0x0000,
+ 0x2049, 0x0000, 0x127f, 0x2000, 0x007c, 0x127e, 0x147e, 0x137e,
+ 0x157e, 0x0c7e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049, 0x4a35,
+ 0xad80, 0x0011, 0x20a0, 0x2099, 0x0031, 0x700c, 0xa084, 0x00ff,
+ 0x682a, 0x7007, 0x0008, 0x7007, 0x0002, 0x7003, 0x0001, 0x0040,
+ 0x4a54, 0x8000, 0x80ac, 0x53a5, 0x7007, 0x0004, 0x7004, 0xa084,
+ 0x0004, 0x00c0, 0x4a56, 0x0c7f, 0x2049, 0x0000, 0x7003, 0x0000,
+ 0x157f, 0x137f, 0x147f, 0x127f, 0x2000, 0x007c, 0x2091, 0x6000,
+ 0x2091, 0x8000, 0x78cc, 0xa005, 0x0040, 0x4a7d, 0x7994, 0x70d0,
+ 0xa106, 0x00c0, 0x4a7d, 0x7804, 0xa005, 0x0040, 0x4a7d, 0x7807,
+ 0x0000, 0x0068, 0x4a7d, 0x2091, 0x4080, 0x7820, 0x8001, 0x7822,
+ 0x00c0, 0x4ad8, 0x7824, 0x7822, 0x2069, 0x5040, 0x6800, 0xa084,
+ 0x0007, 0x0040, 0x4a9b, 0xa086, 0x0002, 0x0040, 0x4a9b, 0x6834,
+ 0xa00d, 0x0040, 0x4a9b, 0x2104, 0xa005, 0x0040, 0x4a9b, 0x8001,
+ 0x200a, 0x0040, 0x4b80, 0x7848, 0xa005, 0x0040, 0x4aa9, 0x8001,
+ 0x784a, 0x00c0, 0x4aa9, 0x2009, 0x0102, 0x6844, 0x200a, 0x1078,
+ 0x21b1, 0x6890, 0xa005, 0x0040, 0x4ab5, 0x8001, 0x6892, 0x00c0,
+ 0x4ab5, 0x686f, 0x0000, 0x6873, 0x0001, 0x2061, 0x5300, 0x20a9,
+ 0x0100, 0x2009, 0x0002, 0x6034, 0xa005, 0x0040, 0x4acb, 0x8001,
+ 0x6036, 0x00c0, 0x4acb, 0x6010, 0xa005, 0x0040, 0x4acb, 0x017e,
+ 0x1078, 0x21b1, 0x017f, 0xace0, 0x0010, 0x0070, 0x4ad1, 0x0078,
+ 0x4abb, 0x8109, 0x0040, 0x4ad8, 0x20a9, 0x0100, 0x0078, 0x4abb,
+ 0x1078, 0x4ae5, 0x1078, 0x4b0a, 0x2009, 0x5051, 0x2104, 0x2009,
+ 0x0102, 0x200a, 0x2091, 0x8001, 0x007c, 0x7834, 0x8001, 0x7836,
+ 0x00c0, 0x4b09, 0x7838, 0x7836, 0x2091, 0x8000, 0x7844, 0xa005,
+ 0x00c0, 0x4af4, 0x2001, 0x0101, 0x8001, 0x7846, 0xa080, 0x7300,
+ 0x2040, 0x2004, 0xa065, 0x0040, 0x4b09, 0x6024, 0xa005, 0x0040,
+ 0x4b05, 0x8001, 0x6026, 0x0040, 0x4b39, 0x6000, 0x2c40, 0x0078,
+ 0x4afa, 0x007c, 0x7828, 0x8001, 0x782a, 0x00c0, 0x4b38, 0x782c,
+ 0x782a, 0x7830, 0xa005, 0x00c0, 0x4b17, 0x2001, 0x0200, 0x8001,
+ 0x7832, 0x8003, 0x8003, 0x8003, 0x8003, 0xa090, 0x5300, 0xa298,
+ 0x0002, 0x2304, 0xa084, 0x0008, 0x0040, 0x4b38, 0xa290, 0x0009,
+ 0x2204, 0xa005, 0x0040, 0x4b30, 0x8001, 0x2012, 0x00c0, 0x4b38,
+ 0x2304, 0xa084, 0xfff7, 0xa085, 0x0080, 0x201a, 0x1078, 0x21b1,
+ 0x007c, 0x2069, 0x5040, 0x6800, 0xa005, 0x0040, 0x4b43, 0x6848,
+ 0xac06, 0x0040, 0x4b80, 0x601b, 0x0006, 0x60b4, 0xa084, 0x3f00,
+ 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085, 0x0060, 0x6022, 0x6000,
+ 0x2042, 0x6714, 0x6f82, 0x1078, 0x1956, 0x6818, 0xa005, 0x0040,
+ 0x4b5b, 0x8001, 0x681a, 0x6808, 0xa084, 0xffef, 0x680a, 0x6810,
+ 0x8001, 0x00d0, 0x4b65, 0x1078, 0x23ca, 0x6812, 0x602f, 0x0000,
+ 0x6033, 0x0000, 0x2c68, 0x1078, 0x1c53, 0x2069, 0x5040, 0x7944,
+ 0xa184, 0x0100, 0x2001, 0x0006, 0x686e, 0x00c0, 0x4b7b, 0x6986,
+ 0x2001, 0x0004, 0x686e, 0x1078, 0x21ac, 0x2091, 0x8001, 0x007c,
+ 0x2069, 0x0100, 0x2009, 0x5040, 0x2104, 0xa084, 0x0007, 0x0040,
+ 0x4bdc, 0xa086, 0x0007, 0x00c0, 0x4b96, 0x0d7e, 0x2009, 0x5052,
+ 0x216c, 0x1078, 0x3a1a, 0x0d7f, 0x0078, 0x4bdc, 0x2009, 0x5052,
+ 0x2164, 0x1078, 0x2375, 0x601b, 0x0006, 0x6858, 0xa084, 0x3f00,
+ 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085, 0x0048, 0x6022, 0x602f,
+ 0x0000, 0x6033, 0x0000, 0x6830, 0xa084, 0x0040, 0x0040, 0x4bd0,
+ 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0004, 0x0040,
+ 0x4bbd, 0x0070, 0x4bbd, 0x0078, 0x4bb4, 0x684b, 0x0009, 0x20a9,
+ 0x0014, 0x6848, 0xa084, 0x0001, 0x0040, 0x4bca, 0x0070, 0x4bca,
+ 0x0078, 0x4bc1, 0x20a9, 0x00fa, 0x0070, 0x4bd0, 0x0078, 0x4bcc,
+ 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b, 0x0048, 0x2009, 0x505b,
+ 0x200b, 0x0007, 0x784c, 0x784a, 0x2091, 0x8001, 0x007c, 0x2079,
+ 0x5000, 0x1078, 0x4c0a, 0x1078, 0x4bee, 0x1078, 0x4bfc, 0x7833,
+ 0x0000, 0x7847, 0x0000, 0x784b, 0x0000, 0x007c, 0x2019, 0x0003,
+ 0x2011, 0x5046, 0x2204, 0xa086, 0x003c, 0x0040, 0x4bf9, 0x2019,
+ 0x0002, 0x7b2a, 0x7b2e, 0x007c, 0x2019, 0x0039, 0x2011, 0x5046,
+ 0x2204, 0xa086, 0x003c, 0x0040, 0x4c07, 0x2019, 0x0027, 0x7b36,
+ 0x7b3a, 0x007c, 0x2019, 0x3971, 0x2011, 0x5046, 0x2204, 0xa086,
+ 0x003c, 0x0040, 0x4c15, 0x2019, 0x2626, 0x7b22, 0x7b26, 0x783f,
+ 0x0000, 0x7843, 0x000a, 0x007c, 0x0020, 0x002b, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0014,
+ 0x0014, 0x9849, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014, 0x0014,
+ 0x0014, 0x0080, 0x000f, 0x0000, 0x0201, 0x0604, 0x0c08, 0x2120,
+ 0x4022, 0xf880, 0x0018, 0x300b, 0xa201, 0x0014, 0xa200, 0x0014,
+ 0xa200, 0x0214, 0x0000, 0x006c, 0x0002, 0x0014, 0x98d5, 0x009e,
+ 0x009b, 0xa202, 0x8838, 0x3806, 0x8839, 0x20c3, 0x0864, 0x9889,
+ 0x28c1, 0x9cb6, 0xa203, 0x300c, 0x2846, 0x8161, 0x846a, 0x8300,
+ 0x1856, 0x883a, 0x9865, 0x28f2, 0x9c95, 0x9858, 0x300c, 0x28e1,
+ 0x9c95, 0x2809, 0xa206, 0x64c0, 0x67a0, 0x6fc0, 0x1814, 0x883b,
+ 0x782c, 0x786d, 0x9879, 0x282b, 0xa207, 0x64a0, 0x67a0, 0x6fc0,
+ 0x1814, 0x883b, 0x7822, 0x883e, 0x987d, 0x8576, 0x8677, 0x206b,
+ 0x28c1, 0x9cb6, 0x2044, 0x2103, 0x20a2, 0x2081, 0x9865, 0xa209,
+ 0x2901, 0x9891, 0x0014, 0xa205, 0xa300, 0x1872, 0x879a, 0x883c,
+ 0x1fe2, 0xc601, 0xa20a, 0x856e, 0x0704, 0x9c95, 0x0014, 0xa204,
+ 0xa300, 0x3009, 0x19e2, 0xf868, 0x8176, 0x86eb, 0x85eb, 0x872e,
+ 0x87a9, 0x883f, 0x08e6, 0x9895, 0xf881, 0x9890, 0xc801, 0x0014,
+ 0xf8c1, 0x0016, 0x85b2, 0x80f0, 0x9532, 0xfb02, 0x1de2, 0x0014,
+ 0x8532, 0xf241, 0x0014, 0x1de2, 0x84a8, 0xd7a0, 0x1fe6, 0x0014,
+ 0xa208, 0x6043, 0x8008, 0x1dc1, 0x0016, 0x8300, 0x8160, 0x842a,
+ 0xf041, 0x3008, 0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011, 0x20d5,
+ 0x8822, 0x0016, 0x8000, 0x2847, 0x1011, 0x98c8, 0x8000, 0xa000,
+ 0x2802, 0x1011, 0x98ce, 0x9865, 0x283e, 0x1011, 0x98d2, 0xa20b,
+ 0x0017, 0x300c, 0xa300, 0x1de2, 0xdb81, 0x0014, 0x0210, 0x98df,
+ 0x0014, 0x26e0, 0x873a, 0xfb02, 0x19f2, 0x1fe2, 0x0014, 0xa20d,
+ 0x3806, 0x0210, 0x9cbb, 0x0704, 0x0000, 0x006c, 0x0002, 0x984f,
+ 0x0014, 0x009e, 0x00a0, 0x0017, 0x60ff, 0x300c, 0x8720, 0xa211,
+ 0x9cd0, 0x8772, 0x8837, 0x2101, 0x987a, 0x10d2, 0x78e2, 0x9cd3,
+ 0x9859, 0xd984, 0xf0e2, 0xf0a1, 0x98cd, 0x0014, 0x8831, 0xd166,
+ 0x8830, 0x800f, 0x9401, 0xb520, 0xc802, 0x8820, 0x987a, 0x2301,
+ 0x987a, 0x10d2, 0x78e4, 0x9cd3, 0x8821, 0x8820, 0x9859, 0xf123,
+ 0xf142, 0xf101, 0x98c6, 0x10d2, 0x70f6, 0x8832, 0x8203, 0x870c,
+ 0xd99e, 0x6001, 0x0014, 0x6845, 0x0214, 0xa21b, 0x9cd0, 0x2001,
+ 0x98c5, 0x8201, 0x1852, 0xd184, 0xd163, 0x8834, 0x8001, 0x988d,
+ 0x3027, 0x84a8, 0x1a56, 0x8833, 0x0014, 0xa218, 0x6981, 0x9cbc,
+ 0x6b2a, 0x6902, 0x1834, 0x989d, 0x1814, 0x8010, 0x8592, 0x8026,
+ 0x84b9, 0x7021, 0x0014, 0xa300, 0x69e1, 0x9ca9, 0x694b, 0xa213,
+ 0x1462, 0xa213, 0x8000, 0x16e1, 0x98b5, 0x8023, 0x16e1, 0x8001,
+ 0x10f1, 0x0016, 0x6969, 0xa214, 0x61c2, 0x8002, 0x14e1, 0x8004,
+ 0x16e1, 0x0101, 0x300a, 0x8827, 0x0014, 0xa217, 0x9cbc, 0x0014,
+ 0xa300, 0x8181, 0x842a, 0x84a8, 0x1ce6, 0x882c, 0x0016, 0xa212,
+ 0x9cd0, 0x10d2, 0x70e4, 0x0004, 0x8007, 0x9424, 0xcc1a, 0x9cd3,
+ 0x98c5, 0x8827, 0x300a, 0x0013, 0x8000, 0x84a4, 0x0016, 0x11c2,
+ 0x211e, 0x870e, 0xa21d, 0x0014, 0x878e, 0x0016, 0xa21c, 0x1035,
+ 0x9891, 0xa210, 0xa000, 0x8010, 0x8592, 0x853b, 0xd044, 0x8022,
+ 0x3807, 0x84bb, 0x98ea, 0x8021, 0x3807, 0x84b9, 0x300c, 0x817e,
+ 0x872b, 0x8772, 0x9891, 0x0000, 0x0020, 0x002b, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0014,
+ 0x0014, 0x9849, 0x0014, 0x0014, 0x98ea, 0x98d5, 0x0014, 0x0014,
+ 0x0014, 0x0080, 0x013f, 0x0000, 0x0201, 0x0604, 0x0c08, 0x2120,
+ 0x4022, 0xf880, 0x0018, 0x300b, 0xa201, 0x0014, 0xa200, 0x0014,
+ 0xa200, 0x0214, 0xa202, 0x8838, 0x3806, 0x8839, 0x20c3, 0x0864,
+ 0xa833, 0x28c1, 0x9cb6, 0xa203, 0x300c, 0x2846, 0x8161, 0x846a,
+ 0x8300, 0x1856, 0x883a, 0xa804, 0x28f2, 0x9c95, 0xa8f4, 0x300c,
+ 0x28e1, 0x9c95, 0x2809, 0xa206, 0x64c0, 0x67a0, 0x6fc0, 0x1814,
+ 0x883b, 0x782c, 0x786d, 0xa808, 0x282b, 0xa207, 0x64a0, 0x67a0,
+ 0x6fc0, 0x1814, 0x883b, 0x7822, 0x883e, 0xa802, 0x8576, 0x8677,
+ 0x206b, 0x28c1, 0x9cb6, 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8e0,
+ 0xa209, 0x2901, 0xa809, 0x0014, 0xa205, 0xa300, 0x1872, 0x879a,
+ 0x883c, 0x1fe2, 0xc601, 0xa20a, 0x856e, 0x0704, 0x9c95, 0x0014,
+ 0xa204, 0xa300, 0x3009, 0x19e2, 0xf868, 0x8176, 0x86eb, 0x85eb,
+ 0x872e, 0x87a9, 0x883f, 0x08e6, 0xa8f3, 0xf881, 0xa8ec, 0xc801,
+ 0x0014, 0xf8c1, 0x0016, 0x85b2, 0x80f0, 0x9532, 0xfb02, 0x1de2,
+ 0x0014, 0x8532, 0xf241, 0x0014, 0x1de2, 0x84a8, 0xd7a0, 0x1fe6,
+ 0x0014, 0xa208, 0x6043, 0x8008, 0x1dc1, 0x0016, 0x8300, 0x8160,
+ 0x842a, 0xf041, 0x3008, 0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011,
+ 0x20d5, 0x8822, 0x0016, 0x8000, 0x2847, 0x1011, 0xa8fc, 0x8000,
+ 0xa000, 0x2802, 0x1011, 0xa8fd, 0xa893, 0x283e, 0x1011, 0xa8fd,
+ 0xa20b, 0x0017, 0x300c, 0xa300, 0x1de2, 0xdb81, 0x0014, 0x0210,
+ 0xa801, 0x0014, 0x26e0, 0x873a, 0xfb02, 0x19f2, 0x1fe2, 0x0014,
+ 0xa20d, 0x3806, 0x0210, 0x9cbb, 0x0704, 0x0017, 0x60ff, 0x300c,
+ 0x8720, 0xa211, 0x9d6b, 0x8772, 0x8837, 0x2101, 0xa821, 0x10d2,
+ 0x78e2, 0x9d6e, 0xa8fc, 0xd984, 0xf0e2, 0xf0a1, 0xa86c, 0x0014,
+ 0x8831, 0xd166, 0x8830, 0x800f, 0x9401, 0xb520, 0xc802, 0x8820,
+ 0xa80f, 0x2301, 0xa80d, 0x10d2, 0x78e4, 0x9d6e, 0x8821, 0x8820,
+ 0xa8e6, 0xf123, 0xf142, 0xf101, 0xa84f, 0x10d2, 0x70f6, 0x8832,
+ 0x8203, 0x870c, 0xd99e, 0x6001, 0x0014, 0x6845, 0x0214, 0xa21b,
+ 0x9d6b, 0x2001, 0xa840, 0x8201, 0x1852, 0xd184, 0xd163, 0x8834,
+ 0x8001, 0xa801, 0x3027, 0x84a8, 0x1a56, 0x8833, 0x0014, 0xa218,
+ 0x6981, 0x9d57, 0x6b2a, 0x6902, 0x1834, 0xa805, 0x1814, 0x8010,
+ 0x8592, 0x8026, 0x84b9, 0x7021, 0x0014, 0xa300, 0x69e1, 0x9d44,
+ 0x694b, 0xa213, 0x1462, 0xa213, 0x8000, 0x16e1, 0xa80c, 0x8023,
+ 0x16e1, 0x8001, 0x10f1, 0x0016, 0x6969, 0xa214, 0x61c2, 0x8002,
+ 0x14e1, 0x8004, 0x16e1, 0x0101, 0x300a, 0x8827, 0x0014, 0xa217,
+ 0x9d57, 0x0014, 0xa300, 0x8181, 0x842a, 0x84a8, 0x1ce6, 0x882c,
+ 0x0016, 0xa212, 0x9d6b, 0x10d2, 0x70e4, 0x0004, 0x8007, 0x9424,
+ 0xcc1a, 0x9d6e, 0xa8f8, 0x8827, 0x300a, 0x0013, 0x8000, 0x84a4,
+ 0x0016, 0x11c2, 0x211e, 0x870e, 0xa21d, 0x0014, 0x878e, 0x0016,
+ 0xa21c, 0x1035, 0xa8b4, 0xa210, 0x3807, 0x300c, 0x817e, 0x872b,
+ 0x8772, 0xa8ad, 0x0000, 0x8ec6
+};
+
+#endif /* RELOAD_FIRMWARE */
+
+static const unsigned short risc_code_length01 = 0x3f14;
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
new file mode 100644
index 000000000000..a917ab7475ac
--- /dev/null
+++ b/drivers/scsi/qlogicpti.c
@@ -0,0 +1,1541 @@
+/* qlogicpti.c: Performance Technologies QlogicISP sbus card driver.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ *
+ * A lot of this driver was directly stolen from Erik H. Moe's PCI
+ * Qlogic ISP driver. Mucho kudos to him for this code.
+ *
+ * An even bigger kudos to John Grana at Performance Technologies
+ * for providing me with the hardware to write this driver, you rule
+ * John you really do.
+ *
+ * May, 2, 1997: Added support for QLGC,isp --jj
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include <asm/byteorder.h>
+
+#include "qlogicpti.h"
+
+#include <asm/sbus.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_request.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h>
+
+
+
+#define MAX_TARGETS 16
+#define MAX_LUNS 8 /* 32 for 1.31 F/W */
+
+#define DEFAULT_LOOP_COUNT 10000
+
+#include "qlogicpti_asm.c"
+
+static struct qlogicpti *qptichain = NULL;
+static DEFINE_SPINLOCK(qptichain_lock);
+static int qptis_running = 0;
+
+#define PACKB(a, b) (((a)<<4)|(b))
+
+static const u_char mbox_param[] = {
+ PACKB(1, 1), /* MBOX_NO_OP */
+ PACKB(5, 5), /* MBOX_LOAD_RAM */
+ PACKB(2, 0), /* MBOX_EXEC_FIRMWARE */
+ PACKB(5, 5), /* MBOX_DUMP_RAM */
+ PACKB(3, 3), /* MBOX_WRITE_RAM_WORD */
+ PACKB(2, 3), /* MBOX_READ_RAM_WORD */
+ PACKB(6, 6), /* MBOX_MAILBOX_REG_TEST */
+ PACKB(2, 3), /* MBOX_VERIFY_CHECKSUM */
+ PACKB(1, 3), /* MBOX_ABOUT_FIRMWARE */
+ PACKB(0, 0), /* 0x0009 */
+ PACKB(0, 0), /* 0x000a */
+ PACKB(0, 0), /* 0x000b */
+ PACKB(0, 0), /* 0x000c */
+ PACKB(0, 0), /* 0x000d */
+ PACKB(1, 2), /* MBOX_CHECK_FIRMWARE */
+ PACKB(0, 0), /* 0x000f */
+ PACKB(5, 5), /* MBOX_INIT_REQ_QUEUE */
+ PACKB(6, 6), /* MBOX_INIT_RES_QUEUE */
+ PACKB(4, 4), /* MBOX_EXECUTE_IOCB */
+ PACKB(2, 2), /* MBOX_WAKE_UP */
+ PACKB(1, 6), /* MBOX_STOP_FIRMWARE */
+ PACKB(4, 4), /* MBOX_ABORT */
+ PACKB(2, 2), /* MBOX_ABORT_DEVICE */
+ PACKB(3, 3), /* MBOX_ABORT_TARGET */
+ PACKB(2, 2), /* MBOX_BUS_RESET */
+ PACKB(2, 3), /* MBOX_STOP_QUEUE */
+ PACKB(2, 3), /* MBOX_START_QUEUE */
+ PACKB(2, 3), /* MBOX_SINGLE_STEP_QUEUE */
+ PACKB(2, 3), /* MBOX_ABORT_QUEUE */
+ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_STATUS */
+ PACKB(0, 0), /* 0x001e */
+ PACKB(1, 3), /* MBOX_GET_FIRMWARE_STATUS */
+ PACKB(1, 2), /* MBOX_GET_INIT_SCSI_ID */
+ PACKB(1, 2), /* MBOX_GET_SELECT_TIMEOUT */
+ PACKB(1, 3), /* MBOX_GET_RETRY_COUNT */
+ PACKB(1, 2), /* MBOX_GET_TAG_AGE_LIMIT */
+ PACKB(1, 2), /* MBOX_GET_CLOCK_RATE */
+ PACKB(1, 2), /* MBOX_GET_ACT_NEG_STATE */
+ PACKB(1, 2), /* MBOX_GET_ASYNC_DATA_SETUP_TIME */
+ PACKB(1, 3), /* MBOX_GET_SBUS_PARAMS */
+ PACKB(2, 4), /* MBOX_GET_TARGET_PARAMS */
+ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_PARAMS */
+ PACKB(0, 0), /* 0x002a */
+ PACKB(0, 0), /* 0x002b */
+ PACKB(0, 0), /* 0x002c */
+ PACKB(0, 0), /* 0x002d */
+ PACKB(0, 0), /* 0x002e */
+ PACKB(0, 0), /* 0x002f */
+ PACKB(2, 2), /* MBOX_SET_INIT_SCSI_ID */
+ PACKB(2, 2), /* MBOX_SET_SELECT_TIMEOUT */
+ PACKB(3, 3), /* MBOX_SET_RETRY_COUNT */
+ PACKB(2, 2), /* MBOX_SET_TAG_AGE_LIMIT */
+ PACKB(2, 2), /* MBOX_SET_CLOCK_RATE */
+ PACKB(2, 2), /* MBOX_SET_ACTIVE_NEG_STATE */
+ PACKB(2, 2), /* MBOX_SET_ASYNC_DATA_SETUP_TIME */
+ PACKB(3, 3), /* MBOX_SET_SBUS_CONTROL_PARAMS */
+ PACKB(4, 4), /* MBOX_SET_TARGET_PARAMS */
+ PACKB(4, 4), /* MBOX_SET_DEV_QUEUE_PARAMS */
+ PACKB(0, 0), /* 0x003a */
+ PACKB(0, 0), /* 0x003b */
+ PACKB(0, 0), /* 0x003c */
+ PACKB(0, 0), /* 0x003d */
+ PACKB(0, 0), /* 0x003e */
+ PACKB(0, 0), /* 0x003f */
+ PACKB(0, 0), /* 0x0040 */
+ PACKB(0, 0), /* 0x0041 */
+ PACKB(0, 0) /* 0x0042 */
+};
+
+#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short))
+
+/* queue length's _must_ be power of two: */
+#define QUEUE_DEPTH(in, out, ql) ((in - out) & (ql))
+#define REQ_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, \
+ QLOGICPTI_REQ_QUEUE_LEN)
+#define RES_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, RES_QUEUE_LEN)
+
+static inline void qlogicpti_enable_irqs(struct qlogicpti *qpti)
+{
+ sbus_writew(SBUS_CTRL_ERIRQ | SBUS_CTRL_GENAB,
+ qpti->qregs + SBUS_CTRL);
+}
+
+static inline void qlogicpti_disable_irqs(struct qlogicpti *qpti)
+{
+ sbus_writew(0, qpti->qregs + SBUS_CTRL);
+}
+
+static inline void set_sbus_cfg1(struct qlogicpti *qpti)
+{
+ u16 val;
+ u8 bursts = qpti->bursts;
+
+#if 0 /* It appears that at least PTI cards do not support
+ * 64-byte bursts and that setting the B64 bit actually
+ * is a nop and the chip ends up using the smallest burst
+ * size. -DaveM
+ */
+ if (sbus_can_burst64(qpti->sdev) && (bursts & DMA_BURST64)) {
+ val = (SBUS_CFG1_BENAB | SBUS_CFG1_B64);
+ } else
+#endif
+ if (bursts & DMA_BURST32) {
+ val = (SBUS_CFG1_BENAB | SBUS_CFG1_B32);
+ } else if (bursts & DMA_BURST16) {
+ val = (SBUS_CFG1_BENAB | SBUS_CFG1_B16);
+ } else if (bursts & DMA_BURST8) {
+ val = (SBUS_CFG1_BENAB | SBUS_CFG1_B8);
+ } else {
+ val = 0; /* No sbus bursts for you... */
+ }
+ sbus_writew(val, qpti->qregs + SBUS_CFG1);
+}
+
+static int qlogicpti_mbox_command(struct qlogicpti *qpti, u_short param[], int force)
+{
+ int loop_count;
+ u16 tmp;
+
+ if (mbox_param[param[0]] == 0)
+ return 1;
+
+ /* Set SBUS semaphore. */
+ tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE);
+ tmp |= SBUS_SEMAPHORE_LCK;
+ sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE);
+
+ /* Wait for host IRQ bit to clear. */
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_HIRQ)) {
+ barrier();
+ cpu_relax();
+ }
+ if (!loop_count)
+ printk(KERN_EMERG "qlogicpti: mbox_command loop timeout #1\n");
+
+ /* Write mailbox command registers. */
+ switch (mbox_param[param[0]] >> 4) {
+ case 6: sbus_writew(param[5], qpti->qregs + MBOX5);
+ case 5: sbus_writew(param[4], qpti->qregs + MBOX4);
+ case 4: sbus_writew(param[3], qpti->qregs + MBOX3);
+ case 3: sbus_writew(param[2], qpti->qregs + MBOX2);
+ case 2: sbus_writew(param[1], qpti->qregs + MBOX1);
+ case 1: sbus_writew(param[0], qpti->qregs + MBOX0);
+ }
+
+ /* Clear RISC interrupt. */
+ tmp = sbus_readw(qpti->qregs + HCCTRL);
+ tmp |= HCCTRL_CRIRQ;
+ sbus_writew(tmp, qpti->qregs + HCCTRL);
+
+ /* Clear SBUS semaphore. */
+ sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
+
+ /* Set HOST interrupt. */
+ tmp = sbus_readw(qpti->qregs + HCCTRL);
+ tmp |= HCCTRL_SHIRQ;
+ sbus_writew(tmp, qpti->qregs + HCCTRL);
+
+ /* Wait for HOST interrupt clears. */
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count &&
+ (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_CRIRQ))
+ udelay(20);
+ if (!loop_count)
+ printk(KERN_EMERG "qlogicpti: mbox_command[%04x] loop timeout #2\n",
+ param[0]);
+
+ /* Wait for SBUS semaphore to get set. */
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count &&
+ !(sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK)) {
+ udelay(20);
+
+ /* Workaround for some buggy chips. */
+ if (sbus_readw(qpti->qregs + MBOX0) & 0x4000)
+ break;
+ }
+ if (!loop_count)
+ printk(KERN_EMERG "qlogicpti: mbox_command[%04x] loop timeout #3\n",
+ param[0]);
+
+ /* Wait for MBOX busy condition to go away. */
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && (sbus_readw(qpti->qregs + MBOX0) == 0x04))
+ udelay(20);
+ if (!loop_count)
+ printk(KERN_EMERG "qlogicpti: mbox_command[%04x] loop timeout #4\n",
+ param[0]);
+
+ /* Read back output parameters. */
+ switch (mbox_param[param[0]] & 0xf) {
+ case 6: param[5] = sbus_readw(qpti->qregs + MBOX5);
+ case 5: param[4] = sbus_readw(qpti->qregs + MBOX4);
+ case 4: param[3] = sbus_readw(qpti->qregs + MBOX3);
+ case 3: param[2] = sbus_readw(qpti->qregs + MBOX2);
+ case 2: param[1] = sbus_readw(qpti->qregs + MBOX1);
+ case 1: param[0] = sbus_readw(qpti->qregs + MBOX0);
+ }
+
+ /* Clear RISC interrupt. */
+ tmp = sbus_readw(qpti->qregs + HCCTRL);
+ tmp |= HCCTRL_CRIRQ;
+ sbus_writew(tmp, qpti->qregs + HCCTRL);
+
+ /* Release SBUS semaphore. */
+ tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE);
+ tmp &= ~(SBUS_SEMAPHORE_LCK);
+ sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE);
+
+ /* We're done. */
+ return 0;
+}
+
+static inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti)
+{
+ int i;
+
+ qpti->host_param.initiator_scsi_id = qpti->scsi_id;
+ qpti->host_param.bus_reset_delay = 3;
+ qpti->host_param.retry_count = 0;
+ qpti->host_param.retry_delay = 5;
+ qpti->host_param.async_data_setup_time = 3;
+ qpti->host_param.req_ack_active_negation = 1;
+ qpti->host_param.data_line_active_negation = 1;
+ qpti->host_param.data_dma_burst_enable = 1;
+ qpti->host_param.command_dma_burst_enable = 1;
+ qpti->host_param.tag_aging = 8;
+ qpti->host_param.selection_timeout = 250;
+ qpti->host_param.max_queue_depth = 256;
+
+ for(i = 0; i < MAX_TARGETS; i++) {
+ /*
+ * disconnect, parity, arq, reneg on reset, and, oddly enough
+ * tags...the midlayer's notion of tagged support has to match
+ * our device settings, and since we base whether we enable a
+ * tag on a per-cmnd basis upon what the midlayer sez, we
+ * actually enable the capability here.
+ */
+ qpti->dev_param[i].device_flags = 0xcd;
+ qpti->dev_param[i].execution_throttle = 16;
+ if (qpti->ultra) {
+ qpti->dev_param[i].synchronous_period = 12;
+ qpti->dev_param[i].synchronous_offset = 8;
+ } else {
+ qpti->dev_param[i].synchronous_period = 25;
+ qpti->dev_param[i].synchronous_offset = 12;
+ }
+ qpti->dev_param[i].device_enable = 1;
+ }
+ /* this is very important to set! */
+ qpti->sbits = 1 << qpti->scsi_id;
+}
+
+static int qlogicpti_reset_hardware(struct Scsi_Host *host)
+{
+ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
+ u_short param[6];
+ unsigned short risc_code_addr;
+ int loop_count, i;
+ unsigned long flags;
+
+ risc_code_addr = 0x1000; /* all load addresses are at 0x1000 */
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
+
+ /* Only reset the scsi bus if it is not free. */
+ if (sbus_readw(qpti->qregs + CPU_PCTRL) & CPU_PCTRL_BSY) {
+ sbus_writew(CPU_ORIDE_RMOD, qpti->qregs + CPU_ORIDE);
+ sbus_writew(CPU_CMD_BRESET, qpti->qregs + CPU_CMD);
+ udelay(400);
+ }
+
+ sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL);
+ sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL);
+ sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL);
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && ((sbus_readw(qpti->qregs + MBOX0) & 0xff) == 0x04))
+ udelay(20);
+ if (!loop_count)
+ printk(KERN_EMERG "qlogicpti: reset_hardware loop timeout\n");
+
+ sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
+ set_sbus_cfg1(qpti);
+ qlogicpti_enable_irqs(qpti);
+
+ if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) {
+ qpti->ultra = 1;
+ sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA),
+ qpti->qregs + RISC_MTREG);
+ } else {
+ qpti->ultra = 0;
+ sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT),
+ qpti->qregs + RISC_MTREG);
+ }
+
+ /* reset adapter and per-device default values. */
+ /* do it after finding out whether we're ultra mode capable */
+ qlogicpti_set_hostdev_defaults(qpti);
+
+ /* Release the RISC processor. */
+ sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
+
+ /* Get RISC to start executing the firmware code. */
+ param[0] = MBOX_EXEC_FIRMWARE;
+ param[1] = risc_code_addr;
+ if (qlogicpti_mbox_command(qpti, param, 1)) {
+ printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ /* Set initiator scsi ID. */
+ param[0] = MBOX_SET_INIT_SCSI_ID;
+ param[1] = qpti->host_param.initiator_scsi_id;
+ if (qlogicpti_mbox_command(qpti, param, 1) ||
+ (param[0] != MBOX_COMMAND_COMPLETE)) {
+ printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ /* Initialize state of the queues, both hw and sw. */
+ qpti->req_in_ptr = qpti->res_out_ptr = 0;
+
+ param[0] = MBOX_INIT_RES_QUEUE;
+ param[1] = RES_QUEUE_LEN + 1;
+ param[2] = (u_short) (qpti->res_dvma >> 16);
+ param[3] = (u_short) (qpti->res_dvma & 0xffff);
+ param[4] = param[5] = 0;
+ if (qlogicpti_mbox_command(qpti, param, 1)) {
+ printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ param[0] = MBOX_INIT_REQ_QUEUE;
+ param[1] = QLOGICPTI_REQ_QUEUE_LEN + 1;
+ param[2] = (u_short) (qpti->req_dvma >> 16);
+ param[3] = (u_short) (qpti->req_dvma & 0xffff);
+ param[4] = param[5] = 0;
+ if (qlogicpti_mbox_command(qpti, param, 1)) {
+ printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ param[0] = MBOX_SET_RETRY_COUNT;
+ param[1] = qpti->host_param.retry_count;
+ param[2] = qpti->host_param.retry_delay;
+ qlogicpti_mbox_command(qpti, param, 0);
+
+ param[0] = MBOX_SET_TAG_AGE_LIMIT;
+ param[1] = qpti->host_param.tag_aging;
+ qlogicpti_mbox_command(qpti, param, 0);
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+ param[0] = MBOX_GET_DEV_QUEUE_PARAMS;
+ param[1] = (i << 8);
+ qlogicpti_mbox_command(qpti, param, 0);
+ }
+
+ param[0] = MBOX_GET_FIRMWARE_STATUS;
+ qlogicpti_mbox_command(qpti, param, 0);
+
+ param[0] = MBOX_SET_SELECT_TIMEOUT;
+ param[1] = qpti->host_param.selection_timeout;
+ qlogicpti_mbox_command(qpti, param, 0);
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+ param[0] = MBOX_SET_TARGET_PARAMS;
+ param[1] = (i << 8);
+ param[2] = (qpti->dev_param[i].device_flags << 8);
+ /*
+ * Since we're now loading 1.31 f/w, force narrow/async.
+ */
+ param[2] |= 0xc0;
+ param[3] = 0; /* no offset, we do not have sync mode yet */
+ qlogicpti_mbox_command(qpti, param, 0);
+ }
+
+ /*
+ * Always (sigh) do an initial bus reset (kicks f/w).
+ */
+ param[0] = MBOX_BUS_RESET;
+ param[1] = qpti->host_param.bus_reset_delay;
+ qlogicpti_mbox_command(qpti, param, 0);
+ qpti->send_marker = 1;
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 0;
+}
+
+#define PTI_RESET_LIMIT 400
+
+static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
+{
+ struct Scsi_Host *host = qpti->qhost;
+ unsigned short csum = 0;
+ unsigned short param[6];
+ unsigned short *risc_code, risc_code_addr, risc_code_length;
+ unsigned long flags;
+ int i, timeout;
+
+ risc_code = &sbus_risc_code01[0];
+ risc_code_addr = 0x1000; /* all f/w modules load at 0x1000 */
+ risc_code_length = sbus_risc_code_length01;
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* Verify the checksum twice, one before loading it, and once
+ * afterwards via the mailbox commands.
+ */
+ for (i = 0; i < risc_code_length; i++)
+ csum += risc_code[i];
+ if (csum) {
+ spin_unlock_irqrestore(host->host_lock, flags);
+ printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
+ qpti->qpti_id);
+ return 1;
+ }
+ sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL);
+ sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL);
+ sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL);
+ timeout = PTI_RESET_LIMIT;
+ while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET))
+ udelay(20);
+ if (!timeout) {
+ spin_unlock_irqrestore(host->host_lock, flags);
+ printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id);
+ return 1;
+ }
+
+ sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL);
+ mdelay(1);
+
+ sbus_writew((SBUS_CTRL_GENAB | SBUS_CTRL_ERIRQ), qpti->qregs + SBUS_CTRL);
+ set_sbus_cfg1(qpti);
+ sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
+
+ if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) {
+ qpti->ultra = 1;
+ sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA),
+ qpti->qregs + RISC_MTREG);
+ } else {
+ qpti->ultra = 0;
+ sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT),
+ qpti->qregs + RISC_MTREG);
+ }
+
+ sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
+
+ /* Pin lines are only stable while RISC is paused. */
+ sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
+ if (sbus_readw(qpti->qregs + CPU_PDIFF) & CPU_PDIFF_MODE)
+ qpti->differential = 1;
+ else
+ qpti->differential = 0;
+ sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
+
+ /* This shouldn't be necessary- we've reset things so we should be
+ running from the ROM now.. */
+
+ param[0] = MBOX_STOP_FIRMWARE;
+ param[1] = param[2] = param[3] = param[4] = param[5] = 0;
+ if (qlogicpti_mbox_command(qpti, param, 1)) {
+ printk(KERN_EMERG "qlogicpti%d: Cannot stop firmware for reload.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ /* Load it up.. */
+ for (i = 0; i < risc_code_length; i++) {
+ param[0] = MBOX_WRITE_RAM_WORD;
+ param[1] = risc_code_addr + i;
+ param[2] = risc_code[i];
+ if (qlogicpti_mbox_command(qpti, param, 1) ||
+ param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+ }
+
+ /* Reset the ISP again. */
+ sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL);
+ mdelay(1);
+
+ qlogicpti_enable_irqs(qpti);
+ sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
+ sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
+
+ /* Ask ISP to verify the checksum of the new code. */
+ param[0] = MBOX_VERIFY_CHECKSUM;
+ param[1] = risc_code_addr;
+ if (qlogicpti_mbox_command(qpti, param, 1) ||
+ (param[0] != MBOX_COMMAND_COMPLETE)) {
+ printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ /* Start using newly downloaded firmware. */
+ param[0] = MBOX_EXEC_FIRMWARE;
+ param[1] = risc_code_addr;
+ qlogicpti_mbox_command(qpti, param, 1);
+
+ param[0] = MBOX_ABOUT_FIRMWARE;
+ if (qlogicpti_mbox_command(qpti, param, 1) ||
+ (param[0] != MBOX_COMMAND_COMPLETE)) {
+ printk(KERN_EMERG "qlogicpti%d: AboutFirmware cmd fails.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ /* Snag the major and minor revisions from the result. */
+ qpti->fware_majrev = param[1];
+ qpti->fware_minrev = param[2];
+ qpti->fware_micrev = param[3];
+
+ /* Set the clock rate */
+ param[0] = MBOX_SET_CLOCK_RATE;
+ param[1] = qpti->clock;
+ if (qlogicpti_mbox_command(qpti, param, 1) ||
+ (param[0] != MBOX_COMMAND_COMPLETE)) {
+ printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n",
+ qpti->qpti_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 1;
+ }
+
+ if (qpti->is_pti != 0) {
+ /* Load scsi initiator ID and interrupt level into sbus static ram. */
+ param[0] = MBOX_WRITE_RAM_WORD;
+ param[1] = 0xff80;
+ param[2] = (unsigned short) qpti->scsi_id;
+ qlogicpti_mbox_command(qpti, param, 1);
+
+ param[0] = MBOX_WRITE_RAM_WORD;
+ param[1] = 0xff00;
+ param[2] = (unsigned short) 3;
+ qlogicpti_mbox_command(qpti, param, 1);
+ }
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 0;
+}
+
+static int qlogicpti_verify_tmon(struct qlogicpti *qpti)
+{
+ int curstat = sbus_readb(qpti->sreg);
+
+ curstat &= 0xf0;
+ if (!(curstat & SREG_FUSE) && (qpti->swsreg & SREG_FUSE))
+ printk("qlogicpti%d: Fuse returned to normal state.\n", qpti->qpti_id);
+ if (!(curstat & SREG_TPOWER) && (qpti->swsreg & SREG_TPOWER))
+ printk("qlogicpti%d: termpwr back to normal state.\n", qpti->qpti_id);
+ if (curstat != qpti->swsreg) {
+ int error = 0;
+ if (curstat & SREG_FUSE) {
+ error++;
+ printk("qlogicpti%d: Fuse is open!\n", qpti->qpti_id);
+ }
+ if (curstat & SREG_TPOWER) {
+ error++;
+ printk("qlogicpti%d: termpwr failure\n", qpti->qpti_id);
+ }
+ if (qpti->differential &&
+ (curstat & SREG_DSENSE) != SREG_DSENSE) {
+ error++;
+ printk("qlogicpti%d: You have a single ended device on a "
+ "differential bus! Please fix!\n", qpti->qpti_id);
+ }
+ qpti->swsreg = curstat;
+ return error;
+ }
+ return 0;
+}
+
+static irqreturn_t qpti_intr(int irq, void *dev_id, struct pt_regs *regs);
+
+static void __init qpti_chain_add(struct qlogicpti *qpti)
+{
+ spin_lock_irq(&qptichain_lock);
+ if (qptichain != NULL) {
+ struct qlogicpti *qlink = qptichain;
+
+ while(qlink->next)
+ qlink = qlink->next;
+ qlink->next = qpti;
+ } else {
+ qptichain = qpti;
+ }
+ qpti->next = NULL;
+ spin_unlock_irq(&qptichain_lock);
+}
+
+static void __init qpti_chain_del(struct qlogicpti *qpti)
+{
+ spin_lock_irq(&qptichain_lock);
+ if (qptichain == qpti) {
+ qptichain = qpti->next;
+ } else {
+ struct qlogicpti *qlink = qptichain;
+ while(qlink->next != qpti)
+ qlink = qlink->next;
+ qlink->next = qpti->next;
+ }
+ qpti->next = NULL;
+ spin_unlock_irq(&qptichain_lock);
+}
+
+static int __init qpti_map_regs(struct qlogicpti *qpti)
+{
+ struct sbus_dev *sdev = qpti->sdev;
+
+ qpti->qregs = sbus_ioremap(&sdev->resource[0], 0,
+ sdev->reg_addrs[0].reg_size,
+ "PTI Qlogic/ISP");
+ if (!qpti->qregs) {
+ printk("PTI: Qlogic/ISP registers are unmappable\n");
+ return -1;
+ }
+ if (qpti->is_pti) {
+ qpti->sreg = sbus_ioremap(&sdev->resource[0], (16 * 4096),
+ sizeof(unsigned char),
+ "PTI Qlogic/ISP statreg");
+ if (!qpti->sreg) {
+ printk("PTI: Qlogic/ISP status register is unmappable\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int __init qpti_register_irq(struct qlogicpti *qpti)
+{
+ struct sbus_dev *sdev = qpti->sdev;
+
+ qpti->qhost->irq = qpti->irq = sdev->irqs[0];
+
+ /* We used to try various overly-clever things to
+ * reduce the interrupt processing overhead on
+ * sun4c/sun4m when multiple PTI's shared the
+ * same IRQ. It was too complex and messy to
+ * sanely maintain.
+ */
+ if (request_irq(qpti->irq, qpti_intr,
+ SA_SHIRQ, "Qlogic/PTI", qpti))
+ goto fail;
+
+ printk("qpti%d: IRQ %s ", qpti->qpti_id, __irq_itoa(qpti->irq));
+
+ return 0;
+
+fail:
+ printk("qpti%d: Cannot acquire irq line\n", qpti->qpti_id);
+ return -1;
+}
+
+static void __init qpti_get_scsi_id(struct qlogicpti *qpti)
+{
+ qpti->scsi_id = prom_getintdefault(qpti->prom_node,
+ "initiator-id",
+ -1);
+ if (qpti->scsi_id == -1)
+ qpti->scsi_id = prom_getintdefault(qpti->prom_node,
+ "scsi-initiator-id",
+ -1);
+ if (qpti->scsi_id == -1)
+ qpti->scsi_id =
+ prom_getintdefault(qpti->sdev->bus->prom_node,
+ "scsi-initiator-id", 7);
+ qpti->qhost->this_id = qpti->scsi_id;
+ qpti->qhost->max_sectors = 64;
+
+ printk("SCSI ID %d ", qpti->scsi_id);
+}
+
+static void qpti_get_bursts(struct qlogicpti *qpti)
+{
+ struct sbus_dev *sdev = qpti->sdev;
+ u8 bursts, bmask;
+
+ bursts = prom_getintdefault(qpti->prom_node, "burst-sizes", 0xff);
+ bmask = prom_getintdefault(sdev->bus->prom_node,
+ "burst-sizes", 0xff);
+ if (bmask != 0xff)
+ bursts &= bmask;
+ if (bursts == 0xff ||
+ (bursts & DMA_BURST16) == 0 ||
+ (bursts & DMA_BURST32) == 0)
+ bursts = (DMA_BURST32 - 1);
+
+ qpti->bursts = bursts;
+}
+
+static void qpti_get_clock(struct qlogicpti *qpti)
+{
+ unsigned int cfreq;
+
+ /* Check for what the clock input to this card is.
+ * Default to 40Mhz.
+ */
+ cfreq = prom_getintdefault(qpti->prom_node,"clock-frequency",40000000);
+ qpti->clock = (cfreq + 500000)/1000000;
+ if (qpti->clock == 0) /* bullshit */
+ qpti->clock = 40;
+}
+
+/* The request and response queues must each be aligned
+ * on a page boundary.
+ */
+static int __init qpti_map_queues(struct qlogicpti *qpti)
+{
+ struct sbus_dev *sdev = qpti->sdev;
+
+#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
+ qpti->res_cpu = sbus_alloc_consistent(sdev,
+ QSIZE(RES_QUEUE_LEN),
+ &qpti->res_dvma);
+ if (qpti->res_cpu == NULL ||
+ qpti->res_dvma == 0) {
+ printk("QPTI: Cannot map response queue.\n");
+ return -1;
+ }
+
+ qpti->req_cpu = sbus_alloc_consistent(sdev,
+ QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+ &qpti->req_dvma);
+ if (qpti->req_cpu == NULL ||
+ qpti->req_dvma == 0) {
+ sbus_free_consistent(sdev, QSIZE(RES_QUEUE_LEN),
+ qpti->res_cpu, qpti->res_dvma);
+ printk("QPTI: Cannot map request queue.\n");
+ return -1;
+ }
+ memset(qpti->res_cpu, 0, QSIZE(RES_QUEUE_LEN));
+ memset(qpti->req_cpu, 0, QSIZE(QLOGICPTI_REQ_QUEUE_LEN));
+ return 0;
+}
+
+/* Detect all PTI Qlogic ISP's in the machine. */
+static int __init qlogicpti_detect(struct scsi_host_template *tpnt)
+{
+ struct qlogicpti *qpti;
+ struct Scsi_Host *qpti_host;
+ struct sbus_bus *sbus;
+ struct sbus_dev *sdev;
+ int nqptis = 0, nqptis_in_use = 0;
+
+ tpnt->proc_name = "qlogicpti";
+ for_each_sbus(sbus) {
+ for_each_sbusdev(sdev, sbus) {
+ /* Is this a red snapper? */
+ if (strcmp(sdev->prom_name, "ptisp") &&
+ strcmp(sdev->prom_name, "PTI,ptisp") &&
+ strcmp(sdev->prom_name, "QLGC,isp") &&
+ strcmp(sdev->prom_name, "SUNW,isp"))
+ continue;
+
+ /* Sometimes Antares cards come up not completely
+ * setup, and we get a report of a zero IRQ.
+ * Skip over them in such cases so we survive.
+ */
+ if (sdev->irqs[0] == 0) {
+ printk("qpti%d: Adapter reports no interrupt, "
+ "skipping over this card.", nqptis);
+ continue;
+ }
+
+ /* Yep, register and allocate software state. */
+ qpti_host = scsi_register(tpnt, sizeof(struct qlogicpti));
+ if (!qpti_host) {
+ printk("QPTI: Cannot register PTI Qlogic ISP SCSI host");
+ continue;
+ }
+ qpti = (struct qlogicpti *) qpti_host->hostdata;
+
+ /* We are wide capable, 16 targets. */
+ qpti_host->max_id = MAX_TARGETS;
+
+ /* Setup back pointers and misc. state. */
+ qpti->qhost = qpti_host;
+ qpti->sdev = sdev;
+ qpti->qpti_id = nqptis++;
+ qpti->prom_node = sdev->prom_node;
+ prom_getstring(qpti->prom_node, "name",
+ qpti->prom_name,
+ sizeof(qpti->prom_name));
+
+ /* This is not correct, actually. There's a switch
+ * on the PTI cards that put them into "emulation"
+ * mode- i.e., report themselves as QLGC,isp
+ * instead of PTI,ptisp. The only real substantive
+ * difference between non-pti and pti cards is
+ * the tmon register. Which is possibly even
+ * there for Qlogic cards, but non-functional.
+ */
+ qpti->is_pti = (strcmp (qpti->prom_name, "QLGC,isp") != 0);
+
+ qpti_chain_add(qpti);
+ if (qpti_map_regs(qpti) < 0)
+ goto fail_unlink;
+
+ if (qpti_register_irq(qpti) < 0)
+ goto fail_unmap_regs;
+
+ qpti_get_scsi_id(qpti);
+ qpti_get_bursts(qpti);
+ qpti_get_clock(qpti);
+
+ /* Clear out scsi_cmnd array. */
+ memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
+
+ if (qpti_map_queues(qpti) < 0)
+ goto fail_free_irq;
+
+ /* Load the firmware. */
+ if (qlogicpti_load_firmware(qpti))
+ goto fail_unmap_queues;
+ if (qpti->is_pti) {
+ /* Check the PTI status reg. */
+ if (qlogicpti_verify_tmon(qpti))
+ goto fail_unmap_queues;
+ }
+
+ /* Reset the ISP and init res/req queues. */
+ if (qlogicpti_reset_hardware(qpti_host))
+ goto fail_unmap_queues;
+
+ printk("(Firmware v%d.%d.%d)", qpti->fware_majrev,
+ qpti->fware_minrev, qpti->fware_micrev);
+ {
+ char buffer[60];
+
+ prom_getstring (qpti->prom_node,
+ "isp-fcode", buffer, 60);
+ if (buffer[0])
+ printk("(Firmware %s)", buffer);
+ if (prom_getbool(qpti->prom_node, "differential"))
+ qpti->differential = 1;
+ }
+
+ printk (" [%s Wide, using %s interface]\n",
+ (qpti->ultra ? "Ultra" : "Fast"),
+ (qpti->differential ? "differential" : "single ended"));
+
+ nqptis_in_use++;
+ continue;
+
+ fail_unmap_queues:
+#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
+ sbus_free_consistent(qpti->sdev,
+ QSIZE(RES_QUEUE_LEN),
+ qpti->res_cpu, qpti->res_dvma);
+ sbus_free_consistent(qpti->sdev,
+ QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+ qpti->req_cpu, qpti->req_dvma);
+#undef QSIZE
+ fail_free_irq:
+ free_irq(qpti->irq, qpti);
+
+ fail_unmap_regs:
+ sbus_iounmap(qpti->qregs,
+ qpti->sdev->reg_addrs[0].reg_size);
+ if (qpti->is_pti)
+ sbus_iounmap(qpti->sreg, sizeof(unsigned char));
+ fail_unlink:
+ qpti_chain_del(qpti);
+ scsi_unregister(qpti->qhost);
+ }
+ }
+ if (nqptis)
+ printk("QPTI: Total of %d PTI Qlogic/ISP hosts found, %d actually in use.\n",
+ nqptis, nqptis_in_use);
+ qptis_running = nqptis_in_use;
+ return nqptis;
+}
+
+static int qlogicpti_release(struct Scsi_Host *host)
+{
+ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
+
+ /* Remove visibility from IRQ handlers. */
+ qpti_chain_del(qpti);
+
+ /* Shut up the card. */
+ sbus_writew(0, qpti->qregs + SBUS_CTRL);
+
+ /* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */
+ free_irq(qpti->irq, qpti);
+
+#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
+ sbus_free_consistent(qpti->sdev,
+ QSIZE(RES_QUEUE_LEN),
+ qpti->res_cpu, qpti->res_dvma);
+ sbus_free_consistent(qpti->sdev,
+ QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+ qpti->req_cpu, qpti->req_dvma);
+#undef QSIZE
+
+ sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size);
+ if (qpti->is_pti)
+ sbus_iounmap(qpti->sreg, sizeof(unsigned char));
+
+ return 0;
+}
+
+const char *qlogicpti_info(struct Scsi_Host *host)
+{
+ static char buf[80];
+ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
+
+ sprintf(buf, "PTI Qlogic,ISP SBUS SCSI irq %s regs at %p",
+ __irq_itoa(qpti->qhost->irq), qpti->qregs);
+ return buf;
+}
+
+/* I am a certified frobtronicist. */
+static inline void marker_frob(struct Command_Entry *cmd)
+{
+ struct Marker_Entry *marker = (struct Marker_Entry *) cmd;
+
+ memset(marker, 0, sizeof(struct Marker_Entry));
+ marker->hdr.entry_cnt = 1;
+ marker->hdr.entry_type = ENTRY_MARKER;
+ marker->modifier = SYNC_ALL;
+ marker->rsvd = 0;
+}
+
+static inline void cmd_frob(struct Command_Entry *cmd, struct scsi_cmnd *Cmnd,
+ struct qlogicpti *qpti)
+{
+ memset(cmd, 0, sizeof(struct Command_Entry));
+ cmd->hdr.entry_cnt = 1;
+ cmd->hdr.entry_type = ENTRY_COMMAND;
+ cmd->target_id = Cmnd->device->id;
+ cmd->target_lun = Cmnd->device->lun;
+ cmd->cdb_length = Cmnd->cmd_len;
+ cmd->control_flags = 0;
+ if (Cmnd->device->tagged_supported) {
+ if (qpti->cmd_count[Cmnd->device->id] == 0)
+ qpti->tag_ages[Cmnd->device->id] = jiffies;
+ if ((jiffies - qpti->tag_ages[Cmnd->device->id]) > (5*HZ)) {
+ cmd->control_flags = CFLAG_ORDERED_TAG;
+ qpti->tag_ages[Cmnd->device->id] = jiffies;
+ } else
+ cmd->control_flags = CFLAG_SIMPLE_TAG;
+ }
+ if ((Cmnd->cmnd[0] == WRITE_6) ||
+ (Cmnd->cmnd[0] == WRITE_10) ||
+ (Cmnd->cmnd[0] == WRITE_12))
+ cmd->control_flags |= CFLAG_WRITE;
+ else
+ cmd->control_flags |= CFLAG_READ;
+ cmd->time_out = 30;
+ memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len);
+}
+
+/* Do it to it baby. */
+static inline int load_cmd(struct scsi_cmnd *Cmnd, struct Command_Entry *cmd,
+ struct qlogicpti *qpti, u_int in_ptr, u_int out_ptr)
+{
+ struct dataseg *ds;
+ struct scatterlist *sg;
+ int i, n;
+
+ if (Cmnd->use_sg) {
+ int sg_count;
+
+ sg = (struct scatterlist *) Cmnd->buffer;
+ sg_count = sbus_map_sg(qpti->sdev, sg, Cmnd->use_sg, Cmnd->sc_data_direction);
+
+ ds = cmd->dataseg;
+ cmd->segment_cnt = sg_count;
+
+ /* Fill in first four sg entries: */
+ n = sg_count;
+ if (n > 4)
+ n = 4;
+ for (i = 0; i < n; i++, sg++) {
+ ds[i].d_base = sg_dma_address(sg);
+ ds[i].d_count = sg_dma_len(sg);
+ }
+ sg_count -= 4;
+ while (sg_count > 0) {
+ struct Continuation_Entry *cont;
+
+ ++cmd->hdr.entry_cnt;
+ cont = (struct Continuation_Entry *) &qpti->req_cpu[in_ptr];
+ in_ptr = NEXT_REQ_PTR(in_ptr);
+ if (in_ptr == out_ptr)
+ return -1;
+
+ cont->hdr.entry_type = ENTRY_CONTINUATION;
+ cont->hdr.entry_cnt = 0;
+ cont->hdr.sys_def_1 = 0;
+ cont->hdr.flags = 0;
+ cont->reserved = 0;
+ ds = cont->dataseg;
+ n = sg_count;
+ if (n > 7)
+ n = 7;
+ for (i = 0; i < n; i++, sg++) {
+ ds[i].d_base = sg_dma_address(sg);
+ ds[i].d_count = sg_dma_len(sg);
+ }
+ sg_count -= n;
+ }
+ } else if (Cmnd->request_bufflen) {
+ Cmnd->SCp.ptr = (char *)(unsigned long)
+ sbus_map_single(qpti->sdev,
+ Cmnd->request_buffer,
+ Cmnd->request_bufflen,
+ Cmnd->sc_data_direction);
+
+ cmd->dataseg[0].d_base = (u32) ((unsigned long)Cmnd->SCp.ptr);
+ cmd->dataseg[0].d_count = Cmnd->request_bufflen;
+ cmd->segment_cnt = 1;
+ } else {
+ cmd->dataseg[0].d_base = 0;
+ cmd->dataseg[0].d_count = 0;
+ cmd->segment_cnt = 1; /* Shouldn't this be 0? */
+ }
+
+ /* Committed, record Scsi_Cmd so we can find it later. */
+ cmd->handle = in_ptr;
+ qpti->cmd_slots[in_ptr] = Cmnd;
+
+ qpti->cmd_count[Cmnd->device->id]++;
+ sbus_writew(in_ptr, qpti->qregs + MBOX4);
+ qpti->req_in_ptr = in_ptr;
+
+ return in_ptr;
+}
+
+static inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int out_ptr)
+{
+ /* Temporary workaround until bug is found and fixed (one bug has been found
+ already, but fixing it makes things even worse) -jj */
+ int num_free = QLOGICPTI_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64;
+ host->can_queue = host->host_busy + num_free;
+ host->sg_tablesize = QLOGICPTI_MAX_SG(num_free);
+}
+
+/*
+ * Until we scan the entire bus with inquiries, go throught this fella...
+ */
+static void ourdone(struct scsi_cmnd *Cmnd)
+{
+ struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->device->host->hostdata;
+ int tgt = Cmnd->device->id;
+ void (*done) (struct scsi_cmnd *);
+
+ /* This grot added by DaveM, blame him for ugliness.
+ * The issue is that in the 2.3.x driver we use the
+ * host_scribble portion of the scsi command as a
+ * completion linked list at interrupt service time,
+ * so we have to store the done function pointer elsewhere.
+ */
+ done = (void (*)(struct scsi_cmnd *))
+ (((unsigned long) Cmnd->SCp.Message)
+#ifdef __sparc_v9__
+ | ((unsigned long) Cmnd->SCp.Status << 32UL)
+#endif
+ );
+
+ if ((qpti->sbits & (1 << tgt)) == 0) {
+ int ok = host_byte(Cmnd->result) == DID_OK;
+ if (Cmnd->cmnd[0] == 0x12 && ok) {
+ unsigned char *iqd;
+
+ if (Cmnd->use_sg != 0)
+ BUG();
+
+ iqd = ((unsigned char *)Cmnd->buffer);
+
+ /* tags handled in midlayer */
+ /* enable sync mode? */
+ if (iqd[7] & 0x10) {
+ qpti->dev_param[tgt].device_flags |= 0x10;
+ } else {
+ qpti->dev_param[tgt].synchronous_offset = 0;
+ qpti->dev_param[tgt].synchronous_period = 0;
+ }
+ /* are we wide capable? */
+ if (iqd[7] & 0x20) {
+ qpti->dev_param[tgt].device_flags |= 0x20;
+ }
+ qpti->sbits |= (1 << tgt);
+ } else if (!ok) {
+ qpti->sbits |= (1 << tgt);
+ }
+ }
+ done(Cmnd);
+}
+
+static int qlogicpti_queuecommand(struct scsi_cmnd *Cmnd, void (*done)(struct scsi_cmnd *));
+
+static int qlogicpti_queuecommand_slow(struct scsi_cmnd *Cmnd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->device->host->hostdata;
+
+ /*
+ * done checking this host adapter?
+ * If not, then rewrite the command
+ * to finish through ourdone so we
+ * can peek at Inquiry data results.
+ */
+ if (qpti->sbits && qpti->sbits != 0xffff) {
+ /* See above about in ourdone this ugliness... */
+ Cmnd->SCp.Message = ((unsigned long)done) & 0xffffffff;
+#ifdef CONFIG_SPARC64
+ Cmnd->SCp.Status = ((unsigned long)done >> 32UL) & 0xffffffff;
+#endif
+ return qlogicpti_queuecommand(Cmnd, ourdone);
+ }
+
+ /*
+ * We've peeked at all targets for this bus- time
+ * to set parameters for devices for real now.
+ */
+ if (qpti->sbits == 0xffff) {
+ int i;
+ for(i = 0; i < MAX_TARGETS; i++) {
+ u_short param[6];
+ param[0] = MBOX_SET_TARGET_PARAMS;
+ param[1] = (i << 8);
+ param[2] = (qpti->dev_param[i].device_flags << 8);
+ if (qpti->dev_param[i].device_flags & 0x10) {
+ param[3] = (qpti->dev_param[i].synchronous_offset << 8) |
+ qpti->dev_param[i].synchronous_period;
+ } else {
+ param[3] = 0;
+ }
+ (void) qlogicpti_mbox_command(qpti, param, 0);
+ }
+ /*
+ * set to zero so any traverse through ourdone
+ * doesn't start the whole process again,
+ */
+ qpti->sbits = 0;
+ }
+
+ /* check to see if we're done with all adapters... */
+ for (qpti = qptichain; qpti != NULL; qpti = qpti->next) {
+ if (qpti->sbits) {
+ break;
+ }
+ }
+
+ /*
+ * if we hit the end of the chain w/o finding adapters still
+ * capability-configuring, then we're done with all adapters
+ * and can rock on..
+ */
+ if (qpti == NULL)
+ Cmnd->device->host->hostt->queuecommand = qlogicpti_queuecommand;
+
+ return qlogicpti_queuecommand(Cmnd, done);
+}
+
+/*
+ * The middle SCSI layer ensures that queuecommand never gets invoked
+ * concurrently with itself or the interrupt handler (though the
+ * interrupt handler may call this routine as part of
+ * request-completion handling).
+ *
+ * "This code must fly." -davem
+ */
+static int qlogicpti_queuecommand(struct scsi_cmnd *Cmnd, void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = Cmnd->device->host;
+ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
+ struct Command_Entry *cmd;
+ u_int out_ptr;
+ int in_ptr;
+
+ Cmnd->scsi_done = done;
+
+ in_ptr = qpti->req_in_ptr;
+ cmd = (struct Command_Entry *) &qpti->req_cpu[in_ptr];
+ out_ptr = sbus_readw(qpti->qregs + MBOX4);
+ in_ptr = NEXT_REQ_PTR(in_ptr);
+ if (in_ptr == out_ptr)
+ goto toss_command;
+
+ if (qpti->send_marker) {
+ marker_frob(cmd);
+ qpti->send_marker = 0;
+ if (NEXT_REQ_PTR(in_ptr) == out_ptr) {
+ sbus_writew(in_ptr, qpti->qregs + MBOX4);
+ qpti->req_in_ptr = in_ptr;
+ goto toss_command;
+ }
+ cmd = (struct Command_Entry *) &qpti->req_cpu[in_ptr];
+ in_ptr = NEXT_REQ_PTR(in_ptr);
+ }
+ cmd_frob(cmd, Cmnd, qpti);
+ if ((in_ptr = load_cmd(Cmnd, cmd, qpti, in_ptr, out_ptr)) == -1)
+ goto toss_command;
+
+ update_can_queue(host, in_ptr, out_ptr);
+
+ return 0;
+
+toss_command:
+ printk(KERN_EMERG "qlogicpti%d: request queue overflow\n",
+ qpti->qpti_id);
+
+ /* Unfortunately, unless you use the new EH code, which
+ * we don't, the midlayer will ignore the return value,
+ * which is insane. We pick up the pieces like this.
+ */
+ Cmnd->result = DID_BUS_BUSY;
+ done(Cmnd);
+ return 1;
+}
+
+static int qlogicpti_return_status(struct Status_Entry *sts, int id)
+{
+ int host_status = DID_ERROR;
+
+ switch (sts->completion_status) {
+ case CS_COMPLETE:
+ host_status = DID_OK;
+ break;
+ case CS_INCOMPLETE:
+ if (!(sts->state_flags & SF_GOT_BUS))
+ host_status = DID_NO_CONNECT;
+ else if (!(sts->state_flags & SF_GOT_TARGET))
+ host_status = DID_BAD_TARGET;
+ else if (!(sts->state_flags & SF_SENT_CDB))
+ host_status = DID_ERROR;
+ else if (!(sts->state_flags & SF_TRANSFERRED_DATA))
+ host_status = DID_ERROR;
+ else if (!(sts->state_flags & SF_GOT_STATUS))
+ host_status = DID_ERROR;
+ else if (!(sts->state_flags & SF_GOT_SENSE))
+ host_status = DID_ERROR;
+ break;
+ case CS_DMA_ERROR:
+ case CS_TRANSPORT_ERROR:
+ host_status = DID_ERROR;
+ break;
+ case CS_RESET_OCCURRED:
+ case CS_BUS_RESET:
+ host_status = DID_RESET;
+ break;
+ case CS_ABORTED:
+ host_status = DID_ABORT;
+ break;
+ case CS_TIMEOUT:
+ host_status = DID_TIME_OUT;
+ break;
+ case CS_DATA_OVERRUN:
+ case CS_COMMAND_OVERRUN:
+ case CS_STATUS_OVERRUN:
+ case CS_BAD_MESSAGE:
+ case CS_NO_MESSAGE_OUT:
+ case CS_EXT_ID_FAILED:
+ case CS_IDE_MSG_FAILED:
+ case CS_ABORT_MSG_FAILED:
+ case CS_NOP_MSG_FAILED:
+ case CS_PARITY_ERROR_MSG_FAILED:
+ case CS_DEVICE_RESET_MSG_FAILED:
+ case CS_ID_MSG_FAILED:
+ case CS_UNEXP_BUS_FREE:
+ host_status = DID_ERROR;
+ break;
+ case CS_DATA_UNDERRUN:
+ host_status = DID_OK;
+ break;
+ default:
+ printk(KERN_EMERG "qpti%d: unknown completion status 0x%04x\n",
+ id, sts->completion_status);
+ host_status = DID_ERROR;
+ break;
+ }
+
+ return (sts->scsi_status & STATUS_MASK) | (host_status << 16);
+}
+
+static struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti)
+{
+ struct scsi_cmnd *Cmnd, *done_queue = NULL;
+ struct Status_Entry *sts;
+ u_int in_ptr, out_ptr;
+
+ if (!(sbus_readw(qpti->qregs + SBUS_STAT) & SBUS_STAT_RINT))
+ return NULL;
+
+ in_ptr = sbus_readw(qpti->qregs + MBOX5);
+ sbus_writew(HCCTRL_CRIRQ, qpti->qregs + HCCTRL);
+ if (sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK) {
+ switch (sbus_readw(qpti->qregs + MBOX0)) {
+ case ASYNC_SCSI_BUS_RESET:
+ case EXECUTION_TIMEOUT_RESET:
+ qpti->send_marker = 1;
+ break;
+ case INVALID_COMMAND:
+ case HOST_INTERFACE_ERROR:
+ case COMMAND_ERROR:
+ case COMMAND_PARAM_ERROR:
+ break;
+ };
+ sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
+ }
+
+ /* This looks like a network driver! */
+ out_ptr = qpti->res_out_ptr;
+ while (out_ptr != in_ptr) {
+ u_int cmd_slot;
+
+ sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr];
+ out_ptr = NEXT_RES_PTR(out_ptr);
+
+ /* We store an index in the handle, not the pointer in
+ * some form. This avoids problems due to the fact
+ * that the handle provided is only 32-bits. -DaveM
+ */
+ cmd_slot = sts->handle;
+ Cmnd = qpti->cmd_slots[cmd_slot];
+ qpti->cmd_slots[cmd_slot] = NULL;
+
+ if (sts->completion_status == CS_RESET_OCCURRED ||
+ sts->completion_status == CS_ABORTED ||
+ (sts->status_flags & STF_BUS_RESET))
+ qpti->send_marker = 1;
+
+ if (sts->state_flags & SF_GOT_SENSE)
+ memcpy(Cmnd->sense_buffer, sts->req_sense_data,
+ sizeof(Cmnd->sense_buffer));
+
+ if (sts->hdr.entry_type == ENTRY_STATUS)
+ Cmnd->result =
+ qlogicpti_return_status(sts, qpti->qpti_id);
+ else
+ Cmnd->result = DID_ERROR << 16;
+
+ if (Cmnd->use_sg) {
+ sbus_unmap_sg(qpti->sdev,
+ (struct scatterlist *)Cmnd->buffer,
+ Cmnd->use_sg,
+ Cmnd->sc_data_direction);
+ } else {
+ sbus_unmap_single(qpti->sdev,
+ (__u32)((unsigned long)Cmnd->SCp.ptr),
+ Cmnd->request_bufflen,
+ Cmnd->sc_data_direction);
+ }
+ qpti->cmd_count[Cmnd->device->id]--;
+ sbus_writew(out_ptr, qpti->qregs + MBOX5);
+ Cmnd->host_scribble = (unsigned char *) done_queue;
+ done_queue = Cmnd;
+ }
+ qpti->res_out_ptr = out_ptr;
+
+ return done_queue;
+}
+
+static irqreturn_t qpti_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct qlogicpti *qpti = dev_id;
+ unsigned long flags;
+ struct scsi_cmnd *dq;
+
+ spin_lock_irqsave(qpti->qhost->host_lock, flags);
+ dq = qlogicpti_intr_handler(qpti);
+
+ if (dq != NULL) {
+ do {
+ struct scsi_cmnd *next;
+
+ next = (struct scsi_cmnd *) dq->host_scribble;
+ dq->scsi_done(dq);
+ dq = next;
+ } while (dq != NULL);
+ }
+ spin_unlock_irqrestore(qpti->qhost->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int qlogicpti_abort(struct scsi_cmnd *Cmnd)
+{
+ u_short param[6];
+ struct Scsi_Host *host = Cmnd->device->host;
+ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
+ int return_status = SUCCESS;
+ u32 cmd_cookie;
+ int i;
+
+ printk(KERN_WARNING "qlogicpti : Aborting cmd for tgt[%d] lun[%d]\n",
+ (int)Cmnd->device->id, (int)Cmnd->device->lun);
+
+ qlogicpti_disable_irqs(qpti);
+
+ /* Find the 32-bit cookie we gave to the firmware for
+ * this command.
+ */
+ for (i = 0; i < QLOGICPTI_REQ_QUEUE_LEN + 1; i++)
+ if (qpti->cmd_slots[i] == Cmnd)
+ break;
+ cmd_cookie = i;
+
+ param[0] = MBOX_ABORT;
+ param[1] = (((u_short) Cmnd->device->id) << 8) | Cmnd->device->lun;
+ param[2] = cmd_cookie >> 16;
+ param[3] = cmd_cookie & 0xffff;
+ if (qlogicpti_mbox_command(qpti, param, 0) ||
+ (param[0] != MBOX_COMMAND_COMPLETE)) {
+ printk(KERN_EMERG "qlogicpti : scsi abort failure: %x\n", param[0]);
+ return_status = FAILED;
+ }
+
+ qlogicpti_enable_irqs(qpti);
+
+ return return_status;
+}
+
+static int qlogicpti_reset(struct scsi_cmnd *Cmnd)
+{
+ u_short param[6];
+ struct Scsi_Host *host = Cmnd->device->host;
+ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
+ int return_status = SUCCESS;
+
+ printk(KERN_WARNING "qlogicpti : Resetting SCSI bus!\n");
+
+ qlogicpti_disable_irqs(qpti);
+
+ param[0] = MBOX_BUS_RESET;
+ param[1] = qpti->host_param.bus_reset_delay;
+ if (qlogicpti_mbox_command(qpti, param, 0) ||
+ (param[0] != MBOX_COMMAND_COMPLETE)) {
+ printk(KERN_EMERG "qlogicisp : scsi bus reset failure: %x\n", param[0]);
+ return_status = FAILED;
+ }
+
+ qlogicpti_enable_irqs(qpti);
+
+ return return_status;
+}
+
+static struct scsi_host_template driver_template = {
+ .detect = qlogicpti_detect,
+ .release = qlogicpti_release,
+ .info = qlogicpti_info,
+ .queuecommand = qlogicpti_queuecommand_slow,
+ .eh_abort_handler = qlogicpti_abort,
+ .eh_bus_reset_handler = qlogicpti_reset,
+ .can_queue = QLOGICPTI_REQ_QUEUE_LEN,
+ .this_id = 7,
+ .sg_tablesize = QLOGICPTI_MAX_SG(QLOGICPTI_REQ_QUEUE_LEN),
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
new file mode 100644
index 000000000000..6cd1c0771d29
--- /dev/null
+++ b/drivers/scsi/qlogicpti.h
@@ -0,0 +1,508 @@
+/* qlogicpti.h: Performance Technologies QlogicISP sbus card defines.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ */
+
+#ifndef _QLOGICPTI_H
+#define _QLOGICPTI_H
+
+/* Qlogic/SBUS controller registers. */
+#define SBUS_CFG1 0x006UL
+#define SBUS_CTRL 0x008UL
+#define SBUS_STAT 0x00aUL
+#define SBUS_SEMAPHORE 0x00cUL
+#define CMD_DMA_CTRL 0x022UL
+#define DATA_DMA_CTRL 0x042UL
+#define MBOX0 0x080UL
+#define MBOX1 0x082UL
+#define MBOX2 0x084UL
+#define MBOX3 0x086UL
+#define MBOX4 0x088UL
+#define MBOX5 0x08aUL
+#define CPU_CMD 0x214UL
+#define CPU_ORIDE 0x224UL
+#define CPU_PCTRL 0x272UL
+#define CPU_PDIFF 0x276UL
+#define RISC_PSR 0x420UL
+#define RISC_MTREG 0x42EUL
+#define HCCTRL 0x440UL
+
+/* SCSI parameters for this driver. */
+#define MAX_TARGETS 16
+#define MAX_LUNS 8
+
+/* With the qlogic interface, every queue slot can hold a SCSI
+ * command with up to 4 scatter/gather entries. If we need more
+ * than 4 entries, continuation entries can be used that hold
+ * another 7 entries each. Unlike for other drivers, this means
+ * that the maximum number of scatter/gather entries we can
+ * support at any given time is a function of the number of queue
+ * slots available. That is, host->can_queue and host->sg_tablesize
+ * are dynamic and _not_ independent. This all works fine because
+ * requests are queued serially and the scatter/gather limit is
+ * determined for each queue request anew.
+ */
+#define QLOGICPTI_REQ_QUEUE_LEN 255 /* must be power of two - 1 */
+#define QLOGICPTI_MAX_SG(ql) (4 + ((ql) > 0) ? 7*((ql) - 1) : 0)
+
+/* mailbox command complete status codes */
+#define MBOX_COMMAND_COMPLETE 0x4000
+#define INVALID_COMMAND 0x4001
+#define HOST_INTERFACE_ERROR 0x4002
+#define TEST_FAILED 0x4003
+#define COMMAND_ERROR 0x4005
+#define COMMAND_PARAM_ERROR 0x4006
+
+/* async event status codes */
+#define ASYNC_SCSI_BUS_RESET 0x8001
+#define SYSTEM_ERROR 0x8002
+#define REQUEST_TRANSFER_ERROR 0x8003
+#define RESPONSE_TRANSFER_ERROR 0x8004
+#define REQUEST_QUEUE_WAKEUP 0x8005
+#define EXECUTION_TIMEOUT_RESET 0x8006
+
+/* Am I fucking pedantic or what? */
+struct Entry_header {
+#ifdef __BIG_ENDIAN
+ u8 entry_cnt;
+ u8 entry_type;
+ u8 flags;
+ u8 sys_def_1;
+#else /* __LITTLE_ENDIAN */
+ u8 entry_type;
+ u8 entry_cnt;
+ u8 sys_def_1;
+ u8 flags;
+#endif
+};
+
+/* entry header type commands */
+#define ENTRY_COMMAND 1
+#define ENTRY_CONTINUATION 2
+#define ENTRY_STATUS 3
+#define ENTRY_MARKER 4
+#define ENTRY_EXTENDED_COMMAND 5
+
+/* entry header flag definitions */
+#define EFLAG_CONTINUATION 1
+#define EFLAG_BUSY 2
+#define EFLAG_BAD_HEADER 4
+#define EFLAG_BAD_PAYLOAD 8
+
+struct dataseg {
+ u32 d_base;
+ u32 d_count;
+};
+
+struct Command_Entry {
+ struct Entry_header hdr;
+ u32 handle;
+#ifdef __BIG_ENDIAN
+ u8 target_id;
+ u8 target_lun;
+#else /* __LITTLE_ENDIAN */
+ u8 target_lun;
+ u8 target_id;
+#endif
+ u16 cdb_length;
+ u16 control_flags;
+ u16 rsvd;
+ u16 time_out;
+ u16 segment_cnt;
+ u8 cdb[12];
+ struct dataseg dataseg[4];
+};
+
+/* command entry control flag definitions */
+#define CFLAG_NODISC 0x01
+#define CFLAG_HEAD_TAG 0x02
+#define CFLAG_ORDERED_TAG 0x04
+#define CFLAG_SIMPLE_TAG 0x08
+#define CFLAG_TAR_RTN 0x10
+#define CFLAG_READ 0x20
+#define CFLAG_WRITE 0x40
+
+struct Ext_Command_Entry {
+ struct Entry_header hdr;
+ u32 handle;
+#ifdef __BIG_ENDIAN
+ u8 target_id;
+ u8 target_lun;
+#else /* __LITTLE_ENDIAN */
+ u8 target_lun;
+ u8 target_id;
+#endif
+ u16 cdb_length;
+ u16 control_flags;
+ u16 rsvd;
+ u16 time_out;
+ u16 segment_cnt;
+ u8 cdb[44];
+};
+
+struct Continuation_Entry {
+ struct Entry_header hdr;
+ u32 reserved;
+ struct dataseg dataseg[7];
+};
+
+struct Marker_Entry {
+ struct Entry_header hdr;
+ u32 reserved;
+#ifdef __BIG_ENDIAN
+ u8 target_id;
+ u8 target_lun;
+#else /* __LITTLE_ENDIAN */
+ u8 target_lun;
+ u8 target_id;
+#endif
+#ifdef __BIG_ENDIAN
+ u8 rsvd;
+ u8 modifier;
+#else /* __LITTLE_ENDIAN */
+ u8 modifier;
+ u8 rsvd;
+#endif
+ u8 rsvds[52];
+};
+
+/* marker entry modifier definitions */
+#define SYNC_DEVICE 0
+#define SYNC_TARGET 1
+#define SYNC_ALL 2
+
+struct Status_Entry {
+ struct Entry_header hdr;
+ u32 handle;
+ u16 scsi_status;
+ u16 completion_status;
+ u16 state_flags;
+ u16 status_flags;
+ u16 time;
+ u16 req_sense_len;
+ u32 residual;
+ u8 rsvd[8];
+ u8 req_sense_data[32];
+};
+
+/* status entry completion status definitions */
+#define CS_COMPLETE 0x0000
+#define CS_INCOMPLETE 0x0001
+#define CS_DMA_ERROR 0x0002
+#define CS_TRANSPORT_ERROR 0x0003
+#define CS_RESET_OCCURRED 0x0004
+#define CS_ABORTED 0x0005
+#define CS_TIMEOUT 0x0006
+#define CS_DATA_OVERRUN 0x0007
+#define CS_COMMAND_OVERRUN 0x0008
+#define CS_STATUS_OVERRUN 0x0009
+#define CS_BAD_MESSAGE 0x000a
+#define CS_NO_MESSAGE_OUT 0x000b
+#define CS_EXT_ID_FAILED 0x000c
+#define CS_IDE_MSG_FAILED 0x000d
+#define CS_ABORT_MSG_FAILED 0x000e
+#define CS_REJECT_MSG_FAILED 0x000f
+#define CS_NOP_MSG_FAILED 0x0010
+#define CS_PARITY_ERROR_MSG_FAILED 0x0011
+#define CS_DEVICE_RESET_MSG_FAILED 0x0012
+#define CS_ID_MSG_FAILED 0x0013
+#define CS_UNEXP_BUS_FREE 0x0014
+#define CS_DATA_UNDERRUN 0x0015
+#define CS_BUS_RESET 0x001c
+
+/* status entry state flag definitions */
+#define SF_GOT_BUS 0x0100
+#define SF_GOT_TARGET 0x0200
+#define SF_SENT_CDB 0x0400
+#define SF_TRANSFERRED_DATA 0x0800
+#define SF_GOT_STATUS 0x1000
+#define SF_GOT_SENSE 0x2000
+
+/* status entry status flag definitions */
+#define STF_DISCONNECT 0x0001
+#define STF_SYNCHRONOUS 0x0002
+#define STF_PARITY_ERROR 0x0004
+#define STF_BUS_RESET 0x0008
+#define STF_DEVICE_RESET 0x0010
+#define STF_ABORTED 0x0020
+#define STF_TIMEOUT 0x0040
+#define STF_NEGOTIATION 0x0080
+
+/* mailbox commands */
+#define MBOX_NO_OP 0x0000
+#define MBOX_LOAD_RAM 0x0001
+#define MBOX_EXEC_FIRMWARE 0x0002
+#define MBOX_DUMP_RAM 0x0003
+#define MBOX_WRITE_RAM_WORD 0x0004
+#define MBOX_READ_RAM_WORD 0x0005
+#define MBOX_MAILBOX_REG_TEST 0x0006
+#define MBOX_VERIFY_CHECKSUM 0x0007
+#define MBOX_ABOUT_FIRMWARE 0x0008
+#define MBOX_CHECK_FIRMWARE 0x000e
+#define MBOX_INIT_REQ_QUEUE 0x0010
+#define MBOX_INIT_RES_QUEUE 0x0011
+#define MBOX_EXECUTE_IOCB 0x0012
+#define MBOX_WAKE_UP 0x0013
+#define MBOX_STOP_FIRMWARE 0x0014
+#define MBOX_ABORT 0x0015
+#define MBOX_ABORT_DEVICE 0x0016
+#define MBOX_ABORT_TARGET 0x0017
+#define MBOX_BUS_RESET 0x0018
+#define MBOX_STOP_QUEUE 0x0019
+#define MBOX_START_QUEUE 0x001a
+#define MBOX_SINGLE_STEP_QUEUE 0x001b
+#define MBOX_ABORT_QUEUE 0x001c
+#define MBOX_GET_DEV_QUEUE_STATUS 0x001d
+#define MBOX_GET_FIRMWARE_STATUS 0x001f
+#define MBOX_GET_INIT_SCSI_ID 0x0020
+#define MBOX_GET_SELECT_TIMEOUT 0x0021
+#define MBOX_GET_RETRY_COUNT 0x0022
+#define MBOX_GET_TAG_AGE_LIMIT 0x0023
+#define MBOX_GET_CLOCK_RATE 0x0024
+#define MBOX_GET_ACT_NEG_STATE 0x0025
+#define MBOX_GET_ASYNC_DATA_SETUP_TIME 0x0026
+#define MBOX_GET_SBUS_PARAMS 0x0027
+#define MBOX_GET_TARGET_PARAMS 0x0028
+#define MBOX_GET_DEV_QUEUE_PARAMS 0x0029
+#define MBOX_SET_INIT_SCSI_ID 0x0030
+#define MBOX_SET_SELECT_TIMEOUT 0x0031
+#define MBOX_SET_RETRY_COUNT 0x0032
+#define MBOX_SET_TAG_AGE_LIMIT 0x0033
+#define MBOX_SET_CLOCK_RATE 0x0034
+#define MBOX_SET_ACTIVE_NEG_STATE 0x0035
+#define MBOX_SET_ASYNC_DATA_SETUP_TIME 0x0036
+#define MBOX_SET_SBUS_CONTROL_PARAMS 0x0037
+#define MBOX_SET_TARGET_PARAMS 0x0038
+#define MBOX_SET_DEV_QUEUE_PARAMS 0x0039
+
+struct host_param {
+ u_short initiator_scsi_id;
+ u_short bus_reset_delay;
+ u_short retry_count;
+ u_short retry_delay;
+ u_short async_data_setup_time;
+ u_short req_ack_active_negation;
+ u_short data_line_active_negation;
+ u_short data_dma_burst_enable;
+ u_short command_dma_burst_enable;
+ u_short tag_aging;
+ u_short selection_timeout;
+ u_short max_queue_depth;
+};
+
+/*
+ * Device Flags:
+ *
+ * Bit Name
+ * ---------
+ * 7 Disconnect Privilege
+ * 6 Parity Checking
+ * 5 Wide Data Transfers
+ * 4 Synchronous Data Transfers
+ * 3 Tagged Queuing
+ * 2 Automatic Request Sense
+ * 1 Stop Queue on Check Condition
+ * 0 Renegotiate on Error
+ */
+
+struct dev_param {
+ u_short device_flags;
+ u_short execution_throttle;
+ u_short synchronous_period;
+ u_short synchronous_offset;
+ u_short device_enable;
+ u_short reserved; /* pad */
+};
+
+/*
+ * The result queue can be quite a bit smaller since continuation entries
+ * do not show up there:
+ */
+#define RES_QUEUE_LEN 255 /* Must be power of two - 1 */
+#define QUEUE_ENTRY_LEN 64
+
+#define NEXT_REQ_PTR(wheee) (((wheee) + 1) & QLOGICPTI_REQ_QUEUE_LEN)
+#define NEXT_RES_PTR(wheee) (((wheee) + 1) & RES_QUEUE_LEN)
+#define PREV_REQ_PTR(wheee) (((wheee) - 1) & QLOGICPTI_REQ_QUEUE_LEN)
+#define PREV_RES_PTR(wheee) (((wheee) - 1) & RES_QUEUE_LEN)
+
+struct pti_queue_entry {
+ char __opaque[QUEUE_ENTRY_LEN];
+};
+
+struct scsi_cmnd;
+
+/* Software state for the driver. */
+struct qlogicpti {
+ /* These are the hot elements in the cache, so they come first. */
+ void __iomem *qregs; /* Adapter registers */
+ struct pti_queue_entry *res_cpu; /* Ptr to RESPONSE bufs (CPU) */
+ struct pti_queue_entry *req_cpu; /* Ptr to REQUEST bufs (CPU) */
+
+ u_int req_in_ptr; /* index of next request slot */
+ u_int res_out_ptr; /* index of next result slot */
+ long send_marker; /* must we send a marker? */
+ struct sbus_dev *sdev;
+ unsigned long __pad;
+
+ int cmd_count[MAX_TARGETS];
+ unsigned long tag_ages[MAX_TARGETS];
+
+ /* The cmd->handler is only 32-bits, so that things work even on monster
+ * Ex000 sparc64 machines with >4GB of ram we just keep track of the
+ * scsi command pointers here. This is essentially what Matt Jacob does. -DaveM
+ */
+ struct scsi_cmnd *cmd_slots[QLOGICPTI_REQ_QUEUE_LEN + 1];
+
+ /* The rest of the elements are unimportant for performance. */
+ struct qlogicpti *next;
+ __u32 res_dvma; /* Ptr to RESPONSE bufs (DVMA)*/
+ __u32 req_dvma; /* Ptr to REQUEST bufs (DVMA) */
+ u_char fware_majrev, fware_minrev, fware_micrev;
+ struct Scsi_Host *qhost;
+ int qpti_id;
+ int scsi_id;
+ int prom_node;
+ char prom_name[64];
+ int irq;
+ char differential, ultra, clock;
+ unsigned char bursts;
+ struct host_param host_param;
+ struct dev_param dev_param[MAX_TARGETS];
+
+ void __iomem *sreg;
+#define SREG_TPOWER 0x80 /* State of termpwr */
+#define SREG_FUSE 0x40 /* State of on board fuse */
+#define SREG_PDISAB 0x20 /* Disable state for power on */
+#define SREG_DSENSE 0x10 /* Sense for differential */
+#define SREG_IMASK 0x0c /* Interrupt level */
+#define SREG_SPMASK 0x03 /* Mask for switch pack */
+ unsigned char swsreg;
+ unsigned int
+ gotirq : 1, /* this instance got an irq */
+ is_pti : 1, /* Non-zero if this is a PTI board. */
+ sbits : 16; /* syncmode known bits */
+};
+
+/* How to twiddle them bits... */
+
+/* SBUS config register one. */
+#define SBUS_CFG1_EPAR 0x0100 /* Enable parity checking */
+#define SBUS_CFG1_FMASK 0x00f0 /* Forth code cycle mask */
+#define SBUS_CFG1_BENAB 0x0004 /* Burst dvma enable */
+#define SBUS_CFG1_B64 0x0003 /* Enable 64byte bursts */
+#define SBUS_CFG1_B32 0x0002 /* Enable 32byte bursts */
+#define SBUS_CFG1_B16 0x0001 /* Enable 16byte bursts */
+#define SBUS_CFG1_B8 0x0008 /* Enable 8byte bursts */
+
+/* SBUS control register */
+#define SBUS_CTRL_EDIRQ 0x0020 /* Enable Data DVMA Interrupts */
+#define SBUS_CTRL_ECIRQ 0x0010 /* Enable Command DVMA Interrupts */
+#define SBUS_CTRL_ESIRQ 0x0008 /* Enable SCSI Processor Interrupts */
+#define SBUS_CTRL_ERIRQ 0x0004 /* Enable RISC Processor Interrupts */
+#define SBUS_CTRL_GENAB 0x0002 /* Global Interrupt Enable */
+#define SBUS_CTRL_RESET 0x0001 /* Soft Reset */
+
+/* SBUS status register */
+#define SBUS_STAT_DINT 0x0020 /* Data DVMA IRQ pending */
+#define SBUS_STAT_CINT 0x0010 /* Command DVMA IRQ pending */
+#define SBUS_STAT_SINT 0x0008 /* SCSI Processor IRQ pending */
+#define SBUS_STAT_RINT 0x0004 /* RISC Processor IRQ pending */
+#define SBUS_STAT_GINT 0x0002 /* Global IRQ pending */
+
+/* SBUS semaphore register */
+#define SBUS_SEMAPHORE_STAT 0x0002 /* Semaphore status bit */
+#define SBUS_SEMAPHORE_LCK 0x0001 /* Semaphore lock bit */
+
+/* DVMA control register */
+#define DMA_CTRL_CSUSPEND 0x0010 /* DMA channel suspend */
+#define DMA_CTRL_CCLEAR 0x0008 /* DMA channel clear and reset */
+#define DMA_CTRL_FCLEAR 0x0004 /* DMA fifo clear */
+#define DMA_CTRL_CIRQ 0x0002 /* DMA irq clear */
+#define DMA_CTRL_DMASTART 0x0001 /* DMA transfer start */
+
+/* SCSI processor override register */
+#define CPU_ORIDE_ETRIG 0x8000 /* External trigger enable */
+#define CPU_ORIDE_STEP 0x4000 /* Single step mode enable */
+#define CPU_ORIDE_BKPT 0x2000 /* Breakpoint reg enable */
+#define CPU_ORIDE_PWRITE 0x1000 /* SCSI pin write enable */
+#define CPU_ORIDE_OFORCE 0x0800 /* Force outputs on */
+#define CPU_ORIDE_LBACK 0x0400 /* SCSI loopback enable */
+#define CPU_ORIDE_PTEST 0x0200 /* Parity test enable */
+#define CPU_ORIDE_TENAB 0x0100 /* SCSI pins tristate enable */
+#define CPU_ORIDE_TPINS 0x0080 /* SCSI pins enable */
+#define CPU_ORIDE_FRESET 0x0008 /* FIFO reset */
+#define CPU_ORIDE_CTERM 0x0004 /* Command terminate */
+#define CPU_ORIDE_RREG 0x0002 /* Reset SCSI processor regs */
+#define CPU_ORIDE_RMOD 0x0001 /* Reset SCSI processor module */
+
+/* SCSI processor commands */
+#define CPU_CMD_BRESET 0x300b /* Reset SCSI bus */
+
+/* SCSI processor pin control register */
+#define CPU_PCTRL_PVALID 0x8000 /* Phase bits are valid */
+#define CPU_PCTRL_PHI 0x0400 /* Parity bit high */
+#define CPU_PCTRL_PLO 0x0200 /* Parity bit low */
+#define CPU_PCTRL_REQ 0x0100 /* REQ bus signal */
+#define CPU_PCTRL_ACK 0x0080 /* ACK bus signal */
+#define CPU_PCTRL_RST 0x0040 /* RST bus signal */
+#define CPU_PCTRL_BSY 0x0020 /* BSY bus signal */
+#define CPU_PCTRL_SEL 0x0010 /* SEL bus signal */
+#define CPU_PCTRL_ATN 0x0008 /* ATN bus signal */
+#define CPU_PCTRL_MSG 0x0004 /* MSG bus signal */
+#define CPU_PCTRL_CD 0x0002 /* CD bus signal */
+#define CPU_PCTRL_IO 0x0001 /* IO bus signal */
+
+/* SCSI processor differential pins register */
+#define CPU_PDIFF_SENSE 0x0200 /* Differential sense */
+#define CPU_PDIFF_MODE 0x0100 /* Differential mode */
+#define CPU_PDIFF_OENAB 0x0080 /* Outputs enable */
+#define CPU_PDIFF_PMASK 0x007c /* Differential control pins */
+#define CPU_PDIFF_TGT 0x0002 /* Target mode enable */
+#define CPU_PDIFF_INIT 0x0001 /* Initiator mode enable */
+
+/* RISC processor status register */
+#define RISC_PSR_FTRUE 0x8000 /* Force true */
+#define RISC_PSR_LCD 0x4000 /* Loop counter shows done status */
+#define RISC_PSR_RIRQ 0x2000 /* RISC irq status */
+#define RISC_PSR_TOFLOW 0x1000 /* Timer overflow (rollover) */
+#define RISC_PSR_AOFLOW 0x0800 /* Arithmetic overflow */
+#define RISC_PSR_AMSB 0x0400 /* Arithmetic big endian */
+#define RISC_PSR_ACARRY 0x0200 /* Arithmetic carry */
+#define RISC_PSR_AZERO 0x0100 /* Arithmetic zero */
+#define RISC_PSR_ULTRA 0x0020 /* Ultra mode */
+#define RISC_PSR_DIRQ 0x0010 /* DVMA interrupt */
+#define RISC_PSR_SIRQ 0x0008 /* SCSI processor interrupt */
+#define RISC_PSR_HIRQ 0x0004 /* Host interrupt */
+#define RISC_PSR_IPEND 0x0002 /* Interrupt pending */
+#define RISC_PSR_FFALSE 0x0001 /* Force false */
+
+/* RISC processor memory timing register */
+#define RISC_MTREG_P1DFLT 0x1200 /* Default read/write timing, pg1 */
+#define RISC_MTREG_P0DFLT 0x0012 /* Default read/write timing, pg0 */
+#define RISC_MTREG_P1ULTRA 0x2300 /* Ultra-mode rw timing, pg1 */
+#define RISC_MTREG_P0ULTRA 0x0023 /* Ultra-mode rw timing, pg0 */
+
+/* Host command/ctrl register */
+#define HCCTRL_NOP 0x0000 /* CMD: No operation */
+#define HCCTRL_RESET 0x1000 /* CMD: Reset RISC cpu */
+#define HCCTRL_PAUSE 0x2000 /* CMD: Pause RISC cpu */
+#define HCCTRL_REL 0x3000 /* CMD: Release paused RISC cpu */
+#define HCCTRL_STEP 0x4000 /* CMD: Single step RISC cpu */
+#define HCCTRL_SHIRQ 0x5000 /* CMD: Set host irq */
+#define HCCTRL_CHIRQ 0x6000 /* CMD: Clear host irq */
+#define HCCTRL_CRIRQ 0x7000 /* CMD: Clear RISC cpu irq */
+#define HCCTRL_BKPT 0x8000 /* CMD: Breakpoint enables change */
+#define HCCTRL_TMODE 0xf000 /* CMD: Enable test mode */
+#define HCCTRL_HIRQ 0x0080 /* Host IRQ pending */
+#define HCCTRL_RRIP 0x0040 /* RISC cpu reset in happening now */
+#define HCCTRL_RPAUSED 0x0020 /* RISC cpu is paused now */
+#define HCCTRL_EBENAB 0x0010 /* External breakpoint enable */
+#define HCCTRL_B1ENAB 0x0008 /* Breakpoint 1 enable */
+#define HCCTRL_B0ENAB 0x0004 /* Breakpoint 0 enable */
+
+/* For our interrupt engine. */
+#define for_each_qlogicpti(qp) \
+ for((qp) = qptichain; (qp); (qp) = (qp)->next)
+
+#endif /* !(_QLOGICPTI_H) */
diff --git a/drivers/scsi/qlogicpti_asm.c b/drivers/scsi/qlogicpti_asm.c
new file mode 100644
index 000000000000..1545b30681b4
--- /dev/null
+++ b/drivers/scsi/qlogicpti_asm.c
@@ -0,0 +1,1160 @@
+/* Version 1.31.00 ISP1000 Initiator RISC firmware */
+unsigned short sbus_risc_code01[] __initdata = {
+ 0x0078, 0x1030, 0x0000, 0x2419, 0x0000, 0x12ff, 0x2043, 0x4f50,
+ 0x5952, 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932,
+ 0x2c31, 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749,
+ 0x4320, 0x434f, 0x5250, 0x4f52, 0x4154, 0x494f, 0x4e00, 0x2049,
+ 0x5350, 0x3130, 0x3030, 0x2046, 0x6972, 0x6d77, 0x6172, 0x6520,
+ 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030, 0x312e, 0x3331, 0x2020,
+ 0x20b9, 0x1212, 0x20c1, 0x0008, 0x2071, 0x0010, 0x70c3, 0x0004,
+ 0x20c9, 0x3fff, 0x2089, 0x10c8, 0x70c7, 0x4953, 0x70cb, 0x5020,
+ 0x70cf, 0x2020, 0x70d3, 0x0001, 0x3f00, 0x70d6, 0x2031, 0x0030,
+ 0x2079, 0x3500, 0x7863, 0x0000, 0x2fa0, 0x2009, 0x0327, 0x2011,
+ 0x0000, 0x20a9, 0x0040, 0x42a4, 0x8109, 0x00c0, 0x1051, 0x789b,
+ 0x0101, 0x780b, 0x0002, 0x780f, 0x0002, 0x784f, 0x0bb8, 0x2069,
+ 0x3540, 0x00a8, 0x106a, 0x681b, 0x003c, 0x2009, 0x1313, 0x21b8,
+ 0x0078, 0x106c, 0x681b, 0x0028, 0x6807, 0x0007, 0x680b, 0x00fa,
+ 0x680f, 0x0008, 0x6813, 0x0005, 0x681f, 0x0000, 0x6823, 0x0006,
+ 0x6817, 0x0008, 0x6827, 0x0000, 0x2069, 0x3600, 0x2011, 0x0020,
+ 0x2009, 0x0010, 0x680b, 0x0c19, 0x680f, 0x0019, 0x6803, 0xdd00,
+ 0x6807, 0x001a, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, 0xa290, 0x0004,
+ 0x8109, 0x00c0, 0x1082, 0x2069, 0x3680, 0x20a9, 0x0080, 0x6837,
+ 0x0000, 0x680b, 0x0040, 0x6817, 0x0100, 0x681f, 0x0064, 0xade8,
+ 0x0010, 0x0070, 0x10a5, 0x0078, 0x1097, 0x1078, 0x1a38, 0x1078,
+ 0x2f3a, 0x1078, 0x1681, 0x1078, 0x33ba, 0x3200, 0xa085, 0x000d,
+ 0x2090, 0x70c3, 0x0000, 0x0090, 0x10bc, 0x70c0, 0xa086, 0x0002,
+ 0x00c0, 0x10bc, 0x1078, 0x11ba, 0x1078, 0x10ec, 0x1078, 0x1817,
+ 0x1078, 0x19a8, 0x1078, 0x327d, 0x1078, 0x177d, 0x0078, 0x10bc,
+ 0x10d0, 0x10d2, 0x1bc3, 0x1bc3, 0x2f98, 0x2f98, 0x1bc3, 0x1bc3,
+ 0x0078, 0x10d0, 0x0078, 0x10d2, 0x0078, 0x10d4, 0x0078, 0x10d6,
+ 0x7008, 0x800c, 0x00c8, 0x10e7, 0x7007, 0x0002, 0xa08c, 0x000c,
+ 0x00c0, 0x10e8, 0x8004, 0x8004, 0x00c8, 0x10e7, 0x087a, 0x097a,
+ 0x70c3, 0x4002, 0x0078, 0x11bd, 0x7814, 0xa005, 0x00c0, 0x10f4,
+ 0x0010, 0x1130, 0x0078, 0x112f, 0x2009, 0x3568, 0x2104, 0xa005,
+ 0x00c0, 0x112f, 0x7814, 0xa086, 0x0001, 0x00c0, 0x1101, 0x1078,
+ 0x1536, 0x7817, 0x0000, 0x2009, 0x356f, 0x2104, 0xa065, 0x0040,
+ 0x111d, 0x2009, 0x356a, 0x211c, 0x8108, 0x2114, 0x8108, 0x2104,
+ 0xa210, 0xa399, 0x0000, 0x2009, 0x001c, 0x6083, 0x0103, 0x1078,
+ 0x1611, 0x00c0, 0x1129, 0x1078, 0x1678, 0x2009, 0x356f, 0x200b,
+ 0x0000, 0x2009, 0x3569, 0x2104, 0x200b, 0x0000, 0xa005, 0x0040,
+ 0x112d, 0x2001, 0x4005, 0x0078, 0x11bc, 0x0078, 0x11ba, 0x007c,
+ 0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x0040, 0x1138, 0x007c,
+ 0x70c3, 0x0000, 0x70c7, 0x0000, 0x70cb, 0x0000, 0x70cf, 0x0000,
+ 0x70c0, 0xa0bc, 0xffc0, 0x00c0, 0x1188, 0x2038, 0x0079, 0x1148,
+ 0x11ba, 0x1205, 0x11d3, 0x1205, 0x1256, 0x1256, 0x11ca, 0x1590,
+ 0x1261, 0x11c6, 0x11d7, 0x11d9, 0x11db, 0x11dd, 0x1595, 0x11c6,
+ 0x1267, 0x1283, 0x1544, 0x158a, 0x11df, 0x146b, 0x148d, 0x14a7,
+ 0x14d0, 0x1424, 0x1432, 0x1446, 0x145a, 0x12ef, 0x11c6, 0x129f,
+ 0x12a6, 0x12ab, 0x12b0, 0x12b6, 0x12bb, 0x12c0, 0x12c5, 0x12ca,
+ 0x12ce, 0x12e3, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x12fb,
+ 0x1304, 0x1313, 0x1339, 0x1343, 0x134a, 0x1370, 0x137f, 0x138e,
+ 0x13a0, 0x1409, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x1419,
+ 0xa0bc, 0xffa0, 0x00c0, 0x11c6, 0x2038, 0xa084, 0x001f, 0x0079,
+ 0x1191, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6,
+ 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6,
+ 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6, 0x11c6,
+ 0x11c6, 0x11c6, 0x11c6, 0x15ed, 0x15f7, 0x15fb, 0x1609, 0x11c6,
+ 0x11c6, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0078, 0x11bc, 0x73ce,
+ 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x2061, 0x0000, 0x601b,
+ 0x0001, 0x2091, 0x5000, 0x2091, 0x4080, 0x007c, 0x70c3, 0x4001,
+ 0x0078, 0x11bd, 0x2099, 0x0041, 0x20a1, 0x0041, 0x20a9, 0x0005,
+ 0x53a3, 0x0078, 0x11ba, 0x70c4, 0x70c3, 0x0004, 0x007a, 0x0078,
+ 0x11ba, 0x0078, 0x11ba, 0x0078, 0x11ba, 0x0078, 0x11ba, 0x2091,
+ 0x8000, 0x70c3, 0x0000, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf,
+ 0x2020, 0x70d3, 0x0001, 0x3f00, 0x70d6, 0x2079, 0x0000, 0x781b,
+ 0x0001, 0x2031, 0x0030, 0x2059, 0x1000, 0x2029, 0x0457, 0x2051,
+ 0x0470, 0x2061, 0x0472, 0x20b9, 0xffff, 0x20c1, 0x0000, 0x2091,
+ 0x5000, 0x2091, 0x4080, 0x0078, 0x0455, 0x71d0, 0x72c8, 0x73cc,
+ 0x70c4, 0x20a0, 0x2098, 0x2031, 0x0030, 0x81ff, 0x0040, 0x11ba,
+ 0x7007, 0x0004, 0x731a, 0x721e, 0x2051, 0x0012, 0x2049, 0x1234,
+ 0x2041, 0x11ba, 0x7003, 0x0002, 0xa786, 0x0001, 0x00c0, 0x1226,
+ 0x2049, 0x1242, 0x2041, 0x124e, 0x7003, 0x0003, 0x7017, 0x0000,
+ 0x810b, 0x7112, 0x00c8, 0x122e, 0x7017, 0x0001, 0x7007, 0x0001,
+ 0xa786, 0x0001, 0x0040, 0x1242, 0x700c, 0xa084, 0x007f, 0x8004,
+ 0x2009, 0x0020, 0xa102, 0x0942, 0x094a, 0x20a8, 0x26a0, 0x53a6,
+ 0x0078, 0x10d8, 0x700c, 0xa084, 0x007f, 0x0040, 0x1242, 0x80ac,
+ 0x0048, 0x1242, 0x2698, 0x53a5, 0x0078, 0x10d8, 0x700c, 0xa084,
+ 0x007f, 0x80ac, 0x2698, 0x53a5, 0x0078, 0x11ba, 0x71c4, 0x70c8,
+ 0x2114, 0xa79e, 0x0004, 0x00c0, 0x125e, 0x200a, 0x72ca, 0x0078,
+ 0x11b9, 0x70c7, 0x0001, 0x70cb, 0x001f, 0x0078, 0x11ba, 0x70c4,
+ 0x72c8, 0x73cc, 0x74d0, 0x70c6, 0x72ca, 0x73ce, 0x74d2, 0xa005,
+ 0x0040, 0x127d, 0x8001, 0x7872, 0x7a7a, 0x7b7e, 0x7c76, 0x7898,
+ 0xa084, 0xfffc, 0x789a, 0x0078, 0x1281, 0x7898, 0xa085, 0x0001,
+ 0x789a, 0x0078, 0x11ba, 0x70c4, 0x72c8, 0x73cc, 0x74d4, 0x70c6,
+ 0x72ca, 0x73ce, 0x74d6, 0xa005, 0x0040, 0x1299, 0x8001, 0x7886,
+ 0x7a8e, 0x7b92, 0x7c8a, 0x7898, 0xa084, 0xfcff, 0x789a, 0x0078,
+ 0x129d, 0x7898, 0xa085, 0x0100, 0x789a, 0x0078, 0x11ba, 0x2009,
+ 0x3559, 0x210c, 0x2011, 0x0410, 0x0078, 0x11b8, 0x2009, 0x3541,
+ 0x210c, 0x0078, 0x11b9, 0x2009, 0x3542, 0x210c, 0x0078, 0x11b9,
+ 0x2061, 0x3540, 0x610c, 0x6210, 0x0078, 0x11b8, 0x2009, 0x3545,
+ 0x210c, 0x0078, 0x11b9, 0x2009, 0x3546, 0x210c, 0x0078, 0x11b9,
+ 0x2009, 0x3547, 0x210c, 0x0078, 0x11b9, 0x2009, 0x3548, 0x210c,
+ 0x0078, 0x11b9, 0x7908, 0x7a0c, 0x0078, 0x11b8, 0x71c4, 0x8107,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8, 0x3600, 0x6a00,
+ 0x6804, 0xa084, 0x0008, 0x0040, 0x12e0, 0x6b08, 0x0078, 0x12e1,
+ 0x6b0c, 0x0078, 0x11b7, 0x77c4, 0x1078, 0x1692, 0x2091, 0x8000,
+ 0x6b1c, 0x6a14, 0x2091, 0x8001, 0x2708, 0x0078, 0x11b7, 0x77c4,
+ 0x1078, 0x1692, 0x2091, 0x8000, 0x6908, 0x6a18, 0x6b10, 0x2091,
+ 0x8001, 0x0078, 0x11b7, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x11b2,
+ 0x1078, 0x1abc, 0x0078, 0x11b7, 0x71c4, 0xa182, 0x0010, 0x00c8,
+ 0x11b2, 0x2011, 0x3541, 0x2204, 0x007e, 0x2112, 0x1078, 0x1a75,
+ 0x017f, 0x0078, 0x11b9, 0x71c4, 0x2011, 0x1331, 0x20a9, 0x0008,
+ 0x2204, 0xa106, 0x0040, 0x1323, 0x8210, 0x0070, 0x1321, 0x0078,
+ 0x1318, 0x0078, 0x11b2, 0xa292, 0x1331, 0x027e, 0x2011, 0x3542,
+ 0x2204, 0x2112, 0x017f, 0x007e, 0x1078, 0x1a81, 0x017f, 0x0078,
+ 0x11b9, 0x03e8, 0x00fa, 0x01f4, 0x02ee, 0x0064, 0x0019, 0x0032,
+ 0x004b, 0x2061, 0x3540, 0x610c, 0x6210, 0x70c4, 0x600e, 0x70c8,
+ 0x6012, 0x0078, 0x11b8, 0x2061, 0x3540, 0x6114, 0x70c4, 0x6016,
+ 0x0078, 0x11b9, 0x71c4, 0x2011, 0x0004, 0x2019, 0x1212, 0xa186,
+ 0x0028, 0x0040, 0x1363, 0x2011, 0x0005, 0x2019, 0x1212, 0xa186,
+ 0x0032, 0x0040, 0x1363, 0x2011, 0x0006, 0x2019, 0x1313, 0xa186,
+ 0x003c, 0x00c0, 0x11b2, 0x2061, 0x3540, 0x6018, 0x007e, 0x611a,
+ 0x23b8, 0x1078, 0x1a92, 0x1078, 0x33ba, 0x017f, 0x0078, 0x11b9,
+ 0x71c4, 0xa184, 0xffcf, 0x00c0, 0x11b2, 0x2011, 0x3547, 0x2204,
+ 0x2112, 0x007e, 0x1078, 0x1ab4, 0x017f, 0x0078, 0x11b9, 0x71c4,
+ 0xa182, 0x0010, 0x00c8, 0x11b2, 0x2011, 0x3548, 0x2204, 0x007e,
+ 0x2112, 0x1078, 0x1aa3, 0x017f, 0x0078, 0x11b9, 0x71c4, 0x72c8,
+ 0xa184, 0xfffd, 0x00c0, 0x11b1, 0xa284, 0xfffd, 0x00c0, 0x11b1,
+ 0x2100, 0x7908, 0x780a, 0x2200, 0x7a0c, 0x780e, 0x0078, 0x11b8,
+ 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8,
+ 0x3600, 0x2019, 0x0000, 0x72c8, 0x6800, 0x007e, 0xa226, 0x0040,
+ 0x13cf, 0x6a02, 0xa484, 0x2000, 0x0040, 0x13b8, 0xa39d, 0x0010,
+ 0xa484, 0x1000, 0x0040, 0x13be, 0xa39d, 0x0008, 0xa484, 0x4000,
+ 0x0040, 0x13cf, 0x810f, 0xa284, 0x4000, 0x0040, 0x13cb, 0x1078,
+ 0x1ad6, 0x0078, 0x13cf, 0x1078, 0x1ac8, 0x0078, 0x13cf, 0x72cc,
+ 0x82ff, 0x0040, 0x1401, 0x6808, 0xa206, 0x0040, 0x1401, 0xa2a4,
+ 0x00ff, 0x2061, 0x3540, 0x6118, 0xa186, 0x0028, 0x0040, 0x13e8,
+ 0xa186, 0x0032, 0x0040, 0x13ee, 0xa186, 0x003c, 0x0040, 0x13f4,
+ 0xa482, 0x0064, 0x0048, 0x13fe, 0x0078, 0x13f8, 0xa482, 0x0050,
+ 0x0048, 0x13fe, 0x0078, 0x13f8, 0xa482, 0x0043, 0x0048, 0x13fe,
+ 0x71c4, 0x71c6, 0x027f, 0x72ca, 0x0078, 0x11b3, 0x6a0a, 0xa39d,
+ 0x000a, 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c, 0x71c4, 0x0078,
+ 0x11b7, 0x77c4, 0x1078, 0x1692, 0x2091, 0x8000, 0x6a14, 0x6b1c,
+ 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e, 0x2708, 0x0078,
+ 0x11b7, 0x71c4, 0x72c8, 0x73cc, 0xa182, 0x0010, 0x00c8, 0x11b2,
+ 0x1078, 0x1ae4, 0x0078, 0x11b7, 0x77c4, 0x1078, 0x1692, 0x2091,
+ 0x8000, 0x6a08, 0xa295, 0x0002, 0x6a0a, 0x2091, 0x8001, 0x2708,
+ 0x0078, 0x11b8, 0x77c4, 0x1078, 0x1692, 0x2091, 0x8000, 0x6a08,
+ 0xa294, 0xfff9, 0x6a0a, 0x6804, 0xa005, 0x0040, 0x1441, 0x1078,
+ 0x1a19, 0x2091, 0x8001, 0x2708, 0x0078, 0x11b8, 0x77c4, 0x1078,
+ 0x1692, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0004, 0x6a0a, 0x6804,
+ 0xa005, 0x0040, 0x1455, 0x1078, 0x1a19, 0x2091, 0x8001, 0x2708,
+ 0x0078, 0x11b8, 0x77c4, 0x2041, 0x0001, 0x2049, 0x0005, 0x2051,
+ 0x0020, 0x2091, 0x8000, 0x1078, 0x169f, 0x2091, 0x8001, 0x2708,
+ 0x6a08, 0x0078, 0x11b8, 0x77c4, 0x72c8, 0x73cc, 0x77c6, 0x72ca,
+ 0x73ce, 0x1078, 0x1718, 0x00c0, 0x1489, 0x6818, 0xa005, 0x0040,
+ 0x1483, 0x2708, 0x1078, 0x1af4, 0x00c0, 0x1483, 0x7817, 0xffff,
+ 0x2091, 0x8001, 0x007c, 0x2091, 0x8001, 0x2001, 0x4005, 0x0078,
+ 0x11bc, 0x2091, 0x8001, 0x0078, 0x11ba, 0x77c4, 0x77c6, 0x2041,
+ 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078,
+ 0x169f, 0x2061, 0x3540, 0x60a3, 0x0003, 0x67b6, 0x60a7, 0x0000,
+ 0x7817, 0xffff, 0x2091, 0x8001, 0x1078, 0x1a19, 0x007c, 0x77c8,
+ 0x77ca, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2091, 0x8000, 0x2061,
+ 0x3540, 0x60a3, 0x0002, 0x60a7, 0x0000, 0x67b6, 0x7817, 0xffff,
+ 0x1078, 0x1a19, 0x2091, 0x8001, 0x2041, 0x0021, 0x2049, 0x0004,
+ 0x2051, 0x0010, 0x2091, 0x8000, 0x1078, 0x169f, 0x70c8, 0x6836,
+ 0x8738, 0xa784, 0x0007, 0x00c0, 0x14c4, 0x2091, 0x8001, 0x007c,
+ 0x7898, 0xa084, 0x0003, 0x00c0, 0x14f4, 0x2039, 0x0000, 0x2041,
+ 0x0021, 0x2049, 0x0004, 0x2051, 0x0008, 0x1078, 0x1692, 0x2091,
+ 0x8000, 0x6808, 0xa80d, 0x690a, 0x2091, 0x8001, 0x8738, 0xa784,
+ 0x0007, 0x00c0, 0x14dd, 0xa7bc, 0xff00, 0x873f, 0x8738, 0x873f,
+ 0xa784, 0x0f00, 0x00c0, 0x14dd, 0x2091, 0x8000, 0x2069, 0x0100,
+ 0x6830, 0xa084, 0x0040, 0x0040, 0x151d, 0x684b, 0x0004, 0x20a9,
+ 0x0014, 0x6848, 0xa084, 0x0004, 0x0040, 0x150a, 0x0070, 0x150a,
+ 0x0078, 0x1501, 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084,
+ 0x0001, 0x0040, 0x1517, 0x0070, 0x1517, 0x0078, 0x150e, 0x20a9,
+ 0x00fa, 0x0070, 0x151d, 0x0078, 0x1519, 0x2079, 0x3500, 0x7817,
+ 0x0001, 0x2061, 0x3540, 0x60a3, 0x0001, 0x60a7, 0x0000, 0x60c3,
+ 0x000f, 0x7898, 0xa085, 0x0002, 0x789a, 0x6808, 0xa084, 0xfffd,
+ 0x680a, 0x681b, 0x0046, 0x2091, 0x8001, 0x007c, 0x7898, 0xa084,
+ 0xfffd, 0x789a, 0xa084, 0x0001, 0x00c0, 0x1540, 0x1078, 0x1760,
+ 0x71c4, 0x71c6, 0x794a, 0x007c, 0x74c4, 0x73c8, 0x72cc, 0x74c6,
+ 0x73ca, 0x72ce, 0x2079, 0x3500, 0x2009, 0x0040, 0x1078, 0x166f,
+ 0x0040, 0x1586, 0x1078, 0x163f, 0x0040, 0x155a, 0x1078, 0x1678,
+ 0x0078, 0x1586, 0x6010, 0x2091, 0x8001, 0x7817, 0xffff, 0x2009,
+ 0x3568, 0x200b, 0x0005, 0x8108, 0x200b, 0x0000, 0x8108, 0x230a,
+ 0x8108, 0x220a, 0x8108, 0x240a, 0x8108, 0x200a, 0x8108, 0x200b,
+ 0x0000, 0x8108, 0x2c0a, 0xa02e, 0x2530, 0x0e7e, 0x1078, 0x2f13,
+ 0x0e7f, 0x6592, 0x65a2, 0x6696, 0x66a6, 0x60ab, 0x0000, 0x60af,
+ 0x0000, 0x2091, 0x8001, 0x1078, 0x1a19, 0x007c, 0x70c3, 0x4005,
+ 0x0078, 0x11bd, 0x71c4, 0x70c7, 0x0000, 0x7906, 0x0078, 0x11ba,
+ 0x71c4, 0x71c6, 0x2168, 0x0078, 0x1597, 0x2069, 0x1000, 0x690c,
+ 0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x00c0, 0x1599, 0xa285,
+ 0x0000, 0x00c0, 0x15a7, 0x70c3, 0x4000, 0x0078, 0x15a9, 0x70c3,
+ 0x4003, 0x70ca, 0x0078, 0x11bd, 0x71c4, 0x72c8, 0x73cc, 0x2100,
+ 0xa184, 0xfffc, 0x00c0, 0x11c6, 0x2100, 0x0079, 0x15b7, 0x15ce,
+ 0x15e3, 0x15e5, 0x15e7, 0x70c3, 0x4003, 0x71ce, 0x72d2, 0x73d6,
+ 0x0078, 0x15ca, 0x70c3, 0x4000, 0x70cf, 0x0000, 0x70d3, 0x0000,
+ 0x70d7, 0x0000, 0x77c6, 0x71ca, 0x0078, 0x11ba, 0x2031, 0x15e9,
+ 0x2624, 0x8630, 0x2412, 0x2204, 0xa446, 0x00c0, 0x15bb, 0xa484,
+ 0xffff, 0x00c0, 0x15d0, 0x2031, 0x15e9, 0x8210, 0x8319, 0xa384,
+ 0xffff, 0x00c0, 0x15d0, 0x0078, 0x15c2, 0x0078, 0x15c2, 0x0078,
+ 0x15c2, 0x5555, 0xaaaa, 0xffff, 0x0000, 0x7960, 0x71c6, 0x71c4,
+ 0xa182, 0x0003, 0x00c8, 0x11b2, 0x7962, 0x0078, 0x11ba, 0x7960,
+ 0x71c6, 0x0078, 0x11ba, 0x7954, 0x71c6, 0x71c4, 0x7956, 0x7958,
+ 0x71ca, 0x71c8, 0x795a, 0x795c, 0x71ce, 0x71cc, 0x795e, 0x0078,
+ 0x11ba, 0x7954, 0x71c6, 0x7958, 0x71ca, 0x795c, 0x71ce, 0x0078,
+ 0x11ba, 0x700c, 0xa084, 0x007f, 0x0040, 0x161d, 0x7007, 0x0004,
+ 0x7004, 0xa084, 0x0004, 0x00c0, 0x1618, 0x7017, 0x0000, 0x7112,
+ 0x721a, 0x731e, 0x8108, 0x810c, 0x81a9, 0x8c98, 0x20a1, 0x0030,
+ 0x6080, 0x20a2, 0x53a6, 0x780c, 0xa085, 0x0000, 0x7002, 0x7007,
+ 0x0001, 0x7108, 0x8104, 0x00c8, 0x1631, 0x7007, 0x0002, 0xa184,
+ 0x000c, 0x710c, 0xa184, 0x0300, 0x7003, 0x0000, 0x007c, 0x700c,
+ 0xa084, 0x007f, 0x0040, 0x164b, 0x7007, 0x0004, 0x7004, 0xa084,
+ 0x0004, 0x00c0, 0x1646, 0x7017, 0x0000, 0x7112, 0x721a, 0x731e,
+ 0x2099, 0x0030, 0x8108, 0x81ac, 0x780c, 0xa085, 0x0001, 0x7002,
+ 0x7007, 0x0001, 0x7008, 0x800c, 0x00c8, 0x165a, 0x7007, 0x0002,
+ 0xa08c, 0x000c, 0x00c0, 0x166c, 0x710c, 0xa184, 0x0300, 0x00c0,
+ 0x166c, 0x2ca0, 0x53a5, 0xa006, 0x7003, 0x0000, 0x007c, 0x7850,
+ 0xa065, 0x0040, 0x1677, 0x2c04, 0x7852, 0x2063, 0x0000, 0x007c,
+ 0x0f7e, 0x2079, 0x3500, 0x7850, 0x2062, 0x2c00, 0x7852, 0x0f7f,
+ 0x007c, 0x2011, 0x4000, 0x7a52, 0x2019, 0x0410, 0x8319, 0x0040,
+ 0x168f, 0xa280, 0x002f, 0x2012, 0x2010, 0x0078, 0x1686, 0x2013,
+ 0x0000, 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784, 0x0007, 0x8003,
+ 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e8, 0x3680, 0x007c, 0x1078,
+ 0x1692, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808, 0xa084, 0xffef,
+ 0xa80d, 0x690a, 0x2009, 0x354f, 0x210c, 0x6804, 0xa005, 0x0040,
+ 0x16bc, 0xa116, 0x00c0, 0x16bc, 0x2060, 0x6000, 0x6806, 0x017e,
+ 0x200b, 0x0000, 0x0078, 0x16bf, 0x2009, 0x0000, 0x017e, 0x6804,
+ 0xa065, 0x0040, 0x16ce, 0x6000, 0x6806, 0x1078, 0x16df, 0x1078,
+ 0x17cb, 0x6810, 0x8001, 0x6812, 0x00c0, 0x16bf, 0x017f, 0x6902,
+ 0x6906, 0x007c, 0xa065, 0x0040, 0x16de, 0x6098, 0x609b, 0x0000,
+ 0x2008, 0x1078, 0x1678, 0x2100, 0x0078, 0x16d2, 0x007c, 0x6003,
+ 0x0103, 0x20a9, 0x001c, 0xac80, 0x0004, 0x20a0, 0x2001, 0x0000,
+ 0x40a4, 0x6828, 0x6016, 0x682c, 0x601e, 0x007c, 0x0e7e, 0x2071,
+ 0x3540, 0x7040, 0xa08c, 0x0080, 0x00c0, 0x16fc, 0xa088, 0x3580,
+ 0x2d0a, 0x8000, 0x7042, 0xa006, 0x0e7f, 0x007c, 0x0e7e, 0x2071,
+ 0x3540, 0x2009, 0x3580, 0x7240, 0x8221, 0x8211, 0x0048, 0x1716,
+ 0x2104, 0x8108, 0xad06, 0x00c0, 0x1705, 0x8119, 0x211e, 0x8108,
+ 0x8318, 0x8211, 0x00c8, 0x170e, 0x7442, 0xa006, 0x0e7f, 0x007c,
+ 0x1078, 0x1692, 0x2091, 0x8000, 0x6804, 0x781e, 0xa065, 0x0040,
+ 0x175f, 0x0078, 0x1729, 0x2c00, 0x781e, 0x6000, 0xa065, 0x0040,
+ 0x175f, 0x600c, 0xa306, 0x00c0, 0x1723, 0x6008, 0xa206, 0x00c0,
+ 0x1723, 0x2c28, 0x2001, 0x354f, 0x2004, 0xac06, 0x0040, 0x175f,
+ 0x6804, 0xac06, 0x00c0, 0x1746, 0x6000, 0x2060, 0x6806, 0xa005,
+ 0x00c0, 0x1746, 0x6803, 0x0000, 0x0078, 0x1750, 0x6400, 0x781c,
+ 0x2060, 0x6402, 0xa486, 0x0000, 0x00c0, 0x1750, 0x2c00, 0x6802,
+ 0x2560, 0x1078, 0x16df, 0x6017, 0x0005, 0x601f, 0x0020, 0x1078,
+ 0x17cb, 0x6810, 0x8001, 0x6812, 0x2001, 0xffff, 0xa005, 0x007c,
+ 0x2039, 0x0000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008,
+ 0x2091, 0x8000, 0x1078, 0x169f, 0x8738, 0xa784, 0x0007, 0x00c0,
+ 0x176a, 0xa7bc, 0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00,
+ 0x00c0, 0x176a, 0x2091, 0x8001, 0x007c, 0x2061, 0x0000, 0x6018,
+ 0xa084, 0x0001, 0x00c0, 0x178a, 0x78ac, 0x78af, 0x0000, 0xa005,
+ 0x00c0, 0x178b, 0x007c, 0xa08c, 0xfff0, 0x0040, 0x1791, 0x1078,
+ 0x1ba5, 0x0079, 0x1793, 0x17a3, 0x17a5, 0x17ab, 0x17af, 0x17a3,
+ 0x17b3, 0x17a3, 0x17a3, 0x17a3, 0x17a3, 0x17b9, 0x17bd, 0x17a3,
+ 0x17a3, 0x17a3, 0x17a3, 0x1078, 0x1ba5, 0x1078, 0x1760, 0x2001,
+ 0x8001, 0x0078, 0x17c3, 0x2001, 0x8003, 0x0078, 0x17c3, 0x2001,
+ 0x8004, 0x0078, 0x17c3, 0x1078, 0x1760, 0x2001, 0x8006, 0x0078,
+ 0x17c3, 0x2001, 0x800c, 0x0078, 0x17c3, 0x1078, 0x1760, 0x2001,
+ 0x800d, 0x0078, 0x17c3, 0x70c2, 0x2061, 0x0000, 0x601b, 0x0001,
+ 0x2091, 0x4080, 0x007c, 0x2c04, 0x6082, 0x2c08, 0x2063, 0x0000,
+ 0x7864, 0x8000, 0x7866, 0x7868, 0xa005, 0x796a, 0x0040, 0x17db,
+ 0x2c02, 0x0078, 0x17dc, 0x796e, 0x007c, 0x0c7e, 0x2061, 0x3500,
+ 0x6883, 0x0103, 0x2d08, 0x206b, 0x0000, 0x6064, 0x8000, 0x6066,
+ 0x6068, 0xa005, 0x616a, 0x0040, 0x17f0, 0x2d02, 0x0078, 0x17f1,
+ 0x616e, 0x0c7f, 0x007c, 0x1078, 0x1804, 0x0040, 0x1803, 0x0c7e,
+ 0x6098, 0xa065, 0x0040, 0x17fe, 0x1078, 0x16d2, 0x0c7f, 0x609b,
+ 0x0000, 0x1078, 0x1678, 0x007c, 0x786c, 0xa065, 0x0040, 0x1816,
+ 0x2091, 0x8000, 0x7864, 0x8001, 0x7866, 0x2c04, 0x786e, 0xa005,
+ 0x00c0, 0x1814, 0x786a, 0x8000, 0x2091, 0x8001, 0x007c, 0x7898,
+ 0xa005, 0x00c0, 0x1865, 0x7974, 0x70d0, 0x0005, 0x0005, 0x72d0,
+ 0xa206, 0x00c0, 0x181c, 0x2200, 0xa106, 0x00c0, 0x1833, 0x7804,
+ 0xa005, 0x0040, 0x1865, 0x7807, 0x0000, 0x0068, 0x1865, 0x2091,
+ 0x4080, 0x0078, 0x1865, 0x1078, 0x166f, 0x0040, 0x1865, 0x7a7c,
+ 0x7b78, 0x8107, 0x8004, 0x8004, 0xa210, 0xa399, 0x0000, 0x2009,
+ 0x0040, 0x1078, 0x163f, 0x0040, 0x185c, 0x1078, 0x1678, 0x7880,
+ 0x8000, 0x7882, 0xa086, 0x0002, 0x00c0, 0x1865, 0x2091, 0x8000,
+ 0x78af, 0x0002, 0x7883, 0x0000, 0x7898, 0xa085, 0x0003, 0x789a,
+ 0x2091, 0x8001, 0x0078, 0x1865, 0x7883, 0x0000, 0x1078, 0x1992,
+ 0x6000, 0xa084, 0x0007, 0x0079, 0x1866, 0x007c, 0x186e, 0x187d,
+ 0x189d, 0x186e, 0x18af, 0x186e, 0x186e, 0x186e, 0x2039, 0x0400,
+ 0x78a8, 0xa705, 0x78aa, 0x6004, 0xa705, 0x6006, 0x1078, 0x18ed,
+ 0x6018, 0x78a6, 0x1078, 0x197a, 0x007c, 0x78a8, 0xa084, 0x0100,
+ 0x0040, 0x1884, 0x0078, 0x186e, 0x78ab, 0x0000, 0x6000, 0x8007,
+ 0xa084, 0x00ff, 0x789e, 0x8001, 0x609b, 0x0000, 0x0040, 0x189a,
+ 0x1078, 0x18ed, 0x0040, 0x189a, 0x78a8, 0xa085, 0x0100, 0x78aa,
+ 0x0078, 0x189c, 0x1078, 0x1911, 0x007c, 0x78a8, 0xa08c, 0x0e00,
+ 0x00c0, 0x18a6, 0xa084, 0x0100, 0x00c0, 0x18a8, 0x0078, 0x186e,
+ 0x1078, 0x18ed, 0x00c0, 0x18ae, 0x1078, 0x1911, 0x007c, 0x78a8,
+ 0xa084, 0x0100, 0x0040, 0x18b6, 0x0078, 0x186e, 0x78ab, 0x0000,
+ 0x6710, 0x20a9, 0x0001, 0x6014, 0xa084, 0x00ff, 0xa005, 0x0040,
+ 0x18d3, 0xa7bc, 0xff00, 0x20a9, 0x0008, 0xa08e, 0x0001, 0x0040,
+ 0x18d3, 0x2039, 0x0000, 0x20a9, 0x0080, 0xa08e, 0x0002, 0x0040,
+ 0x18d3, 0x0078, 0x18ea, 0x1078, 0x1692, 0x2d00, 0x2091, 0x8000,
+ 0x682b, 0x0000, 0x682f, 0x0000, 0x6808, 0xa084, 0xffde, 0x680a,
+ 0x2d00, 0xa080, 0x0010, 0x2068, 0x2091, 0x8001, 0x0070, 0x18ea,
+ 0x0078, 0x18d6, 0x1078, 0x1678, 0x007c, 0x78a0, 0xa06d, 0x00c0,
+ 0x18f8, 0x2c00, 0x78a2, 0x78a6, 0x609b, 0x0000, 0x0078, 0x1904,
+ 0x2c00, 0x689a, 0x609b, 0x0000, 0x78a2, 0x2d00, 0x6002, 0x78a4,
+ 0xad06, 0x00c0, 0x1904, 0x6002, 0x789c, 0x8001, 0x789e, 0x00c0,
+ 0x1910, 0x78a8, 0xa084, 0x0000, 0x78aa, 0x78a4, 0x2060, 0xa006,
+ 0x007c, 0xa02e, 0x2530, 0x6118, 0xa184, 0x0060, 0x619e, 0x0040,
+ 0x191d, 0x0e7e, 0x1078, 0x2f13, 0x0e7f, 0x6592, 0x65a2, 0x6696,
+ 0x66a6, 0x60ab, 0x0000, 0x60af, 0x0000, 0x6710, 0x1078, 0x1692,
+ 0x2091, 0x8000, 0x6808, 0xa084, 0x0001, 0x0040, 0x193f, 0x2091,
+ 0x8001, 0x1078, 0x16df, 0x2091, 0x8000, 0x1078, 0x17cb, 0x2091,
+ 0x8001, 0x78a3, 0x0000, 0x78a7, 0x0000, 0x0078, 0x1979, 0x6020,
+ 0xa096, 0x0001, 0x00c0, 0x1946, 0x8000, 0x6022, 0x6a10, 0x6814,
+ 0x2091, 0x8001, 0xa202, 0x0048, 0x1955, 0x0040, 0x1955, 0x2039,
+ 0x0200, 0x1078, 0x197a, 0x0078, 0x1979, 0x2c08, 0x2091, 0x8000,
+ 0x6800, 0xa065, 0x0040, 0x195d, 0x6102, 0x6902, 0x00c0, 0x1961,
+ 0x6906, 0x2160, 0x6003, 0x0000, 0x6810, 0x8000, 0x6812, 0x2091,
+ 0x8001, 0x6808, 0xa08c, 0x0040, 0x0040, 0x1973, 0xa086, 0x0040,
+ 0x680a, 0x1078, 0x16ee, 0x1078, 0x1a19, 0x78a7, 0x0000, 0x78a3,
+ 0x0000, 0x007c, 0x6004, 0xa705, 0x6006, 0x2091, 0x8000, 0x1078,
+ 0x17cb, 0x2091, 0x8001, 0x78a4, 0xa065, 0x0040, 0x198d, 0x6098,
+ 0x78a6, 0x609b, 0x0000, 0x0078, 0x197d, 0x78a3, 0x0000, 0x78a7,
+ 0x0000, 0x007c, 0x7970, 0x7874, 0x8000, 0xa10a, 0x00c8, 0x1999,
+ 0xa006, 0x7876, 0x70d2, 0x7804, 0xa005, 0x0040, 0x19a7, 0x8001,
+ 0x7806, 0x00c0, 0x19a7, 0x0068, 0x19a7, 0x2091, 0x4080, 0x007c,
+ 0x0068, 0x19c2, 0x2029, 0x0000, 0x786c, 0xa065, 0x0040, 0x19bd,
+ 0x1078, 0x19c3, 0x0040, 0x19bd, 0x057e, 0x1078, 0x19d9, 0x057f,
+ 0x00c0, 0x19bd, 0x8528, 0x0078, 0x19ac, 0x85ff, 0x0040, 0x19c2,
+ 0x2091, 0x4080, 0x007c, 0x7b84, 0x7988, 0x72d4, 0x0005, 0x0005,
+ 0x70d4, 0xa206, 0x00c0, 0x19c5, 0x2200, 0xa102, 0x00c0, 0x19d3,
+ 0x2300, 0xa005, 0x007c, 0x0048, 0x19d7, 0xa302, 0x007c, 0x8002,
+ 0x007c, 0x1078, 0x1a0b, 0x2009, 0x001c, 0x6024, 0xa005, 0x0040,
+ 0x19e3, 0x2009, 0x0040, 0x1078, 0x1611, 0x0040, 0x19fc, 0x7894,
+ 0x8000, 0x7896, 0xa086, 0x0002, 0x00c0, 0x1a0a, 0x2091, 0x8000,
+ 0x78af, 0x0003, 0x7897, 0x0000, 0x7898, 0xa085, 0x0300, 0x789a,
+ 0x2091, 0x8001, 0x0078, 0x1a0a, 0x7897, 0x0000, 0x1078, 0x17f3,
+ 0x7984, 0x7888, 0x8000, 0xa10a, 0x00c8, 0x1a07, 0xa006, 0x788a,
+ 0x70d6, 0xa006, 0x007c, 0x8107, 0x8004, 0x8004, 0x7a90, 0x7b8c,
+ 0xa210, 0xa399, 0x0000, 0x007c, 0x2009, 0x3568, 0x2091, 0x8000,
+ 0x200a, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x3540, 0x2091, 0x8000,
+ 0x2104, 0xa086, 0x0000, 0x00c0, 0x1a34, 0x2009, 0x3512, 0x2104,
+ 0xa005, 0x00c0, 0x1a34, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x1a34,
+ 0x0018, 0x1a34, 0x781b, 0x0044, 0x2091, 0x8001, 0x0f7f, 0x007c,
+ 0x127e, 0x2091, 0x2300, 0x2071, 0x3540, 0x2079, 0x0100, 0x2019,
+ 0x2dd8, 0x20a1, 0x012b, 0x2304, 0xa005, 0x0040, 0x1a50, 0x789a,
+ 0x8318, 0x23ac, 0x8318, 0x2398, 0x53a6, 0x3318, 0x0078, 0x1a43,
+ 0x789b, 0x0020, 0x20a9, 0x0010, 0x78af, 0x0000, 0x78af, 0x0220,
+ 0x0070, 0x1a5c, 0x0078, 0x1a54, 0x7003, 0x0000, 0x1078, 0x1b5b,
+ 0x7004, 0xa084, 0x000f, 0xa085, 0x6280, 0x7806, 0x780f, 0x9200,
+ 0x7843, 0x00d8, 0x7853, 0x0080, 0x780b, 0x0038, 0x7047, 0x357f,
+ 0x7043, 0x0000, 0x127f, 0x2000, 0x007c, 0xa18c, 0x000f, 0x2011,
+ 0x0101, 0x2204, 0xa084, 0xfff0, 0xa105, 0x2012, 0x1078, 0x1b5b,
+ 0x007c, 0x2011, 0x0101, 0x20a9, 0x0009, 0x810b, 0x0070, 0x1a8a,
+ 0x0078, 0x1a85, 0xa18c, 0x0e00, 0x2204, 0xa084, 0xf1ff, 0xa105,
+ 0x2012, 0x007c, 0x2009, 0x0101, 0x20a9, 0x0005, 0x8213, 0x0070,
+ 0x1a9b, 0x0078, 0x1a96, 0xa294, 0x00e0, 0x2104, 0xa084, 0xff1f,
+ 0xa205, 0x200a, 0x007c, 0x2011, 0x0101, 0x20a9, 0x000c, 0x810b,
+ 0x0070, 0x1aac, 0x0078, 0x1aa7, 0xa18c, 0xf000, 0x2204, 0xa084,
+ 0x0fff, 0xa105, 0x2012, 0x007c, 0x2011, 0x0102, 0x2204, 0xa084,
+ 0xffcf, 0xa105, 0x2012, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020,
+ 0x0c7e, 0x2061, 0x0100, 0x609a, 0x62ac, 0x63ac, 0x0c7f, 0x007c,
+ 0x8103, 0x8003, 0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a,
+ 0x60a4, 0xa084, 0xffdf, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003,
+ 0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa085,
+ 0x0020, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020,
+ 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0x62ae, 0x2010, 0x60a4,
+ 0x63ae, 0x2018, 0x0c7f, 0x007c, 0x2091, 0x8000, 0x0c7e, 0x0e7e,
+ 0x6818, 0xa005, 0x0040, 0x1b39, 0x2061, 0x3f80, 0x1078, 0x1b41,
+ 0x0040, 0x1b27, 0x20a9, 0x0000, 0x2061, 0x3e80, 0x0c7e, 0x1078,
+ 0x1b41, 0x0040, 0x1b13, 0x0c7f, 0x8c60, 0x0070, 0x1b11, 0x0078,
+ 0x1b06, 0x0078, 0x1b39, 0x007f, 0xa082, 0x3e80, 0x2071, 0x3540,
+ 0x70ba, 0x601c, 0xa085, 0x0800, 0x601e, 0x71b6, 0x60a7, 0x0000,
+ 0x2001, 0x0004, 0x70a2, 0x1078, 0x1a14, 0x0078, 0x1b35, 0x2071,
+ 0x3540, 0x601c, 0xa085, 0x0800, 0x601e, 0x71b6, 0x60a7, 0x0000,
+ 0x2001, 0x0006, 0x70a2, 0x1078, 0x1a14, 0x2001, 0x0000, 0x0078,
+ 0x1b3b, 0x2001, 0x0001, 0x2091, 0x8001, 0xa005, 0x0e7f, 0x0c7f,
+ 0x007c, 0x2c04, 0xa005, 0x0040, 0x1b58, 0x2060, 0x600c, 0xa306,
+ 0x00c0, 0x1b55, 0x6008, 0xa206, 0x00c0, 0x1b55, 0x6010, 0xa106,
+ 0x00c0, 0x1b55, 0xa006, 0x0078, 0x1b5a, 0x6000, 0x0078, 0x1b42,
+ 0xa085, 0x0001, 0x007c, 0x2011, 0x3541, 0x220c, 0xa18c, 0x000f,
+ 0x2011, 0x013b, 0x2204, 0xa084, 0x0100, 0x0040, 0x1b6a, 0x2021,
+ 0xff80, 0x2122, 0x007c, 0x0e7e, 0x68e4, 0xa08c, 0x0020, 0x0040,
+ 0x1ba3, 0xa084, 0x0006, 0x00c0, 0x1ba3, 0x6010, 0x8007, 0xa084,
+ 0x000f, 0x8003, 0x8003, 0x8003, 0xa0f0, 0x3600, 0x7004, 0xa084,
+ 0x000a, 0x00c0, 0x1ba3, 0x7108, 0xa194, 0xff00, 0x0040, 0x1ba3,
+ 0xa18c, 0x00ff, 0x2001, 0x0019, 0xa106, 0x0040, 0x1b96, 0x2001,
+ 0x0032, 0xa106, 0x0040, 0x1b9a, 0x0078, 0x1b9e, 0x2009, 0x0020,
+ 0x0078, 0x1ba0, 0x2009, 0x003f, 0x0078, 0x1ba0, 0x2011, 0x0000,
+ 0x2100, 0xa205, 0x700a, 0x0e7f, 0x007c, 0x0068, 0x1ba5, 0x007e,
+ 0x2071, 0x0000, 0x7018, 0xa084, 0x0001, 0x00c0, 0x1baa, 0x007f,
+ 0x2e08, 0x2071, 0x0010, 0x70ca, 0x007f, 0x70c6, 0x70c3, 0x8002,
+ 0x2071, 0x0000, 0x701b, 0x0001, 0x2091, 0x4080, 0x007f, 0x2070,
+ 0x007f, 0x0078, 0x1bc1, 0x107e, 0x007e, 0x127e, 0x2091, 0x2300,
+ 0x7f3c, 0x7e58, 0x7c30, 0x7d38, 0xa594, 0x003f, 0xa484, 0x4000,
+ 0x0040, 0x1bd8, 0xa784, 0x007c, 0x00c0, 0x2d9c, 0x1078, 0x1ba5,
+ 0xa49c, 0x000f, 0xa382, 0x0004, 0x0050, 0x1be0, 0x1078, 0x1ba5,
+ 0x8507, 0xa084, 0x000f, 0x0079, 0x1be5, 0x1fea, 0x209a, 0x20c0,
+ 0x22e6, 0x256b, 0x25b3, 0x25ea, 0x2665, 0x26bf, 0x2744, 0x1c0b,
+ 0x1bf5, 0x1e53, 0x1f1d, 0x254a, 0x1bf5, 0x1078, 0x1ba5, 0x0018,
+ 0x1bc8, 0x127f, 0x2091, 0x8001, 0x007f, 0x107f, 0x007c, 0x7003,
+ 0x0000, 0x703f, 0x0000, 0x7030, 0xa005, 0x0040, 0x1c09, 0x7033,
+ 0x0000, 0x0018, 0x1bc8, 0x705c, 0xa005, 0x00c0, 0x1cb6, 0x70a0,
+ 0xa084, 0x0007, 0x0079, 0x1c14, 0x1cd6, 0x1c1c, 0x1c2a, 0x1c4b,
+ 0x1c71, 0x1c9d, 0x1c9b, 0x1c1c, 0x7808, 0xa084, 0xfffd, 0x780a,
+ 0x2009, 0x0046, 0x1078, 0x2412, 0x00c0, 0x1c28, 0x7003, 0x0004,
+ 0x0078, 0x1bf7, 0x1078, 0x2d5e, 0x00c0, 0x1c49, 0x70b4, 0x8007,
+ 0x789b, 0x007e, 0x78aa, 0x789b, 0x0010, 0x78ab, 0x000c, 0x789b,
+ 0x0060, 0x78ab, 0x0001, 0x785b, 0x0004, 0x2009, 0x00f7, 0x1078,
+ 0x2410, 0x00c0, 0x1c49, 0x7003, 0x0004, 0x70c3, 0x000f, 0x7033,
+ 0x3570, 0x0078, 0x1bf7, 0x1078, 0x2d5e, 0x00c0, 0x1c6f, 0x71b4,
+ 0x8107, 0x789b, 0x007e, 0x78aa, 0x789b, 0x0010, 0xa18c, 0x0007,
+ 0xa18d, 0x00c0, 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab,
+ 0x0002, 0x785b, 0x0004, 0x2009, 0x00f7, 0x1078, 0x2410, 0x00c0,
+ 0x1c6f, 0x7003, 0x0004, 0x70c3, 0x000f, 0x7033, 0x3570, 0x0078,
+ 0x1bf7, 0x1078, 0x2d5e, 0x00c0, 0x1c99, 0x71b4, 0x8107, 0x789b,
+ 0x007e, 0x78aa, 0x789b, 0x0010, 0xa18c, 0x0007, 0xa18d, 0x00c0,
+ 0x79aa, 0x78ab, 0x0020, 0x71b8, 0x79aa, 0x78ab, 0x000d, 0x789b,
+ 0x0060, 0x78ab, 0x0004, 0x785b, 0x0004, 0x2009, 0x00f7, 0x1078,
+ 0x2410, 0x00c0, 0x1c99, 0x7003, 0x0004, 0x70c3, 0x000f, 0x7033,
+ 0x3570, 0x0078, 0x1bf7, 0x0078, 0x1c4b, 0x1078, 0x2d5e, 0x00c0,
+ 0x1bf7, 0x70bc, 0x2068, 0x789b, 0x0010, 0x6f10, 0x1078, 0x2ca1,
+ 0x2c50, 0x6810, 0xa084, 0x0007, 0xa085, 0x0080, 0x78aa, 0x6e18,
+ 0x2041, 0x0001, 0x2001, 0x0004, 0x0078, 0x1dde, 0x1078, 0x2d5e,
+ 0x00c0, 0x1bf7, 0x789b, 0x0010, 0x705c, 0x2068, 0x6f10, 0x1078,
+ 0x2ca1, 0x2c50, 0x6008, 0xa085, 0x0010, 0x600a, 0x6810, 0xa084,
+ 0x0007, 0xa085, 0x0080, 0x78aa, 0x2031, 0x0020, 0x2041, 0x0001,
+ 0x1078, 0x2dc5, 0x2001, 0x0003, 0x0078, 0x1dc9, 0x0018, 0x1bc8,
+ 0x7440, 0xa485, 0x0000, 0x0040, 0x1cf0, 0xa080, 0x3580, 0x2030,
+ 0x7144, 0x8108, 0xa12a, 0x0048, 0x1ce7, 0x2009, 0x3580, 0x2164,
+ 0x6504, 0x85ff, 0x00c0, 0x1cfd, 0x8421, 0x00c0, 0x1ce1, 0x7146,
+ 0x7003, 0x0000, 0x703f, 0x0000, 0x0078, 0x1bf7, 0x7640, 0xa6b0,
+ 0x3580, 0x7144, 0x2600, 0x0078, 0x1cec, 0x7146, 0x2568, 0x2558,
+ 0x753e, 0x2c50, 0x6034, 0xa085, 0x0000, 0x00c0, 0x1cfa, 0x6708,
+ 0x7736, 0xa784, 0x013f, 0x0040, 0x1d2f, 0xa784, 0x0021, 0x00c0,
+ 0x1cfa, 0xa784, 0x0002, 0x0040, 0x1d1c, 0xa784, 0x0004, 0x0040,
+ 0x1cfa, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0008, 0x00c0, 0x1cfa,
+ 0xa784, 0x0010, 0x00c0, 0x1cfa, 0xa784, 0x0100, 0x0040, 0x1d2f,
+ 0x6018, 0xa005, 0x00c0, 0x1cfa, 0xa7bc, 0xfeff, 0x670a, 0x681f,
+ 0x0000, 0x6e18, 0xa684, 0x000e, 0x6118, 0x0040, 0x1d3f, 0x601c,
+ 0xa102, 0x0048, 0x1d42, 0x0040, 0x1d42, 0x0078, 0x1cf6, 0x81ff,
+ 0x00c0, 0x1cf6, 0xa784, 0x0080, 0x00c0, 0x1d48, 0x700c, 0x6022,
+ 0xa7bc, 0xff7f, 0x670a, 0x6b10, 0x8307, 0xa084, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa080, 0x3600, 0x2060, 0x2048, 0x704a, 0x6000,
+ 0x704e, 0x6004, 0x7052, 0x2a60, 0x0018, 0x1bc8, 0x789b, 0x0010,
+ 0xa046, 0x1078, 0x2d5e, 0x00c0, 0x1bf7, 0x6b10, 0xa39c, 0x0007,
+ 0xa39d, 0x00c0, 0x704c, 0xa084, 0x8000, 0x0040, 0x1d73, 0xa684,
+ 0x0001, 0x0040, 0x1d75, 0xa39c, 0xffbf, 0xa684, 0x0010, 0x0040,
+ 0x1d7b, 0xa39d, 0x0020, 0x7baa, 0x8840, 0xa684, 0x000e, 0x00c0,
+ 0x1d86, 0xa7bd, 0x0010, 0x670a, 0x0078, 0x1dc7, 0x714c, 0xa18c,
+ 0x0800, 0x0040, 0x2902, 0x2011, 0x0021, 0x8004, 0x8004, 0x0048,
+ 0x1d9d, 0x2011, 0x0022, 0x8004, 0x0048, 0x1d9d, 0x2011, 0x0020,
+ 0x8004, 0x0048, 0x1d9d, 0x0040, 0x1dc7, 0x7aaa, 0x8840, 0x1078,
+ 0x2d77, 0x6a10, 0x610c, 0x8108, 0xa18c, 0x00ff, 0xa1e0, 0x3e80,
+ 0x2c64, 0x8cff, 0x0040, 0x1dbe, 0x6010, 0xa206, 0x00c0, 0x1da8,
+ 0x60b4, 0x8001, 0x60b6, 0x00c0, 0x1da3, 0x0c7e, 0x2a60, 0x6008,
+ 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078, 0x1cd6, 0x1078, 0x2d5e,
+ 0x00c0, 0x1bf7, 0x2a60, 0x610e, 0x79aa, 0x8840, 0x712e, 0x2001,
+ 0x0001, 0x007e, 0x7150, 0xa184, 0x0018, 0x0040, 0x1ddd, 0xa184,
+ 0x0010, 0x0040, 0x1dd7, 0x1078, 0x2acc, 0x00c0, 0x1ddd, 0xa184,
+ 0x0008, 0x0040, 0x1ddd, 0x1078, 0x29e6, 0x007f, 0x7002, 0xa68c,
+ 0x0060, 0x88ff, 0x0040, 0x1de6, 0xa18d, 0x0004, 0x795a, 0x69b2,
+ 0x789b, 0x0060, 0x2800, 0x78aa, 0x789b, 0x0061, 0x6814, 0xa085,
+ 0x8000, 0x6816, 0x78aa, 0x157e, 0x137e, 0x147e, 0x20a1, 0x012c,
+ 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, 0x000a, 0x2098, 0x53a6,
+ 0x147f, 0x137f, 0x157f, 0x6810, 0x8007, 0x789b, 0x007e, 0x78aa,
+ 0x6d90, 0x7dd6, 0x7dde, 0x6e94, 0x7ed2, 0x7eda, 0x7830, 0xa084,
+ 0x00c0, 0x00c0, 0x1e15, 0x0098, 0x1e1d, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x2d77, 0x0078, 0x1bff, 0x7200, 0xa284, 0x0007,
+ 0xa086, 0x0001, 0x00c0, 0x1e2a, 0x781b, 0x0049, 0x1078, 0x2d77,
+ 0x0078, 0x1e3b, 0x6ab0, 0xa295, 0x2000, 0x7a5a, 0x781b, 0x0049,
+ 0x1078, 0x2d77, 0x7200, 0x2500, 0xa605, 0x0040, 0x1e3b, 0xa284,
+ 0x0007, 0x1079, 0x1e49, 0xad80, 0x0008, 0x7032, 0xa284, 0x0007,
+ 0xa086, 0x0001, 0x00c0, 0x1e47, 0x6018, 0x8000, 0x601a, 0x0078,
+ 0x1bf7, 0x1e51, 0x30f0, 0x30f0, 0x30df, 0x30f0, 0x1e51, 0x1e51,
+ 0x1e51, 0x1078, 0x1ba5, 0x7808, 0xa084, 0xfffd, 0x780a, 0x0f7e,
+ 0x2079, 0x3500, 0x7898, 0x0f7f, 0xa084, 0x0001, 0x0040, 0x1e79,
+ 0x70a0, 0xa086, 0x0001, 0x00c0, 0x1e68, 0x70a2, 0x0078, 0x1f01,
+ 0x70a0, 0xa086, 0x0005, 0x00c0, 0x1e77, 0x70bc, 0x2068, 0x6817,
+ 0x0004, 0x6813, 0x0000, 0x681c, 0xa085, 0x0008, 0x681e, 0x70a3,
+ 0x0000, 0x157e, 0x2011, 0x0004, 0x71a0, 0xa186, 0x0001, 0x0040,
+ 0x1e9b, 0xa186, 0x0007, 0x00c0, 0x1e8b, 0x2009, 0x352b, 0x200b,
+ 0x0005, 0x0078, 0x1e9b, 0x2009, 0x3513, 0x2104, 0x2009, 0x3512,
+ 0x200a, 0x2009, 0x352b, 0x200b, 0x0001, 0x70a3, 0x0000, 0x70a7,
+ 0x0001, 0x0078, 0x1e9d, 0x70a3, 0x0000, 0x1078, 0x2ec7, 0x20a9,
+ 0x0010, 0x2039, 0x0000, 0x1078, 0x2ba6, 0xa7b8, 0x0100, 0x0070,
+ 0x1eab, 0x0078, 0x1ea3, 0x7000, 0x2020, 0x0079, 0x1eaf, 0x1edd,
+ 0x1ec6, 0x1ec6, 0x1eb9, 0x1edd, 0x1edd, 0x1eb7, 0x1eb7, 0x1078,
+ 0x1ba5, 0x2021, 0x3557, 0x2404, 0xa005, 0x0040, 0x1ec6, 0xad06,
+ 0x00c0, 0x1ec6, 0x6800, 0x2022, 0x0078, 0x1ed6, 0x681c, 0xa084,
+ 0x0001, 0x00c0, 0x1ed2, 0x6f10, 0x1078, 0x2ca1, 0x1078, 0x28d9,
+ 0x0078, 0x1ed6, 0x7054, 0x2060, 0x6800, 0x6002, 0x6a16, 0x681c,
+ 0xa085, 0x0008, 0x681e, 0x1078, 0x17dd, 0x2021, 0x3f80, 0x1078,
+ 0x1f07, 0x2021, 0x3557, 0x1078, 0x1f07, 0x20a9, 0x0000, 0x2021,
+ 0x3e80, 0x1078, 0x1f07, 0x8420, 0x0070, 0x1ef0, 0x0078, 0x1ee9,
+ 0x20a9, 0x0080, 0x2061, 0x3680, 0x6018, 0x6110, 0xa102, 0x6012,
+ 0x601b, 0x0000, 0xace0, 0x0010, 0x0070, 0x1f00, 0x0078, 0x1ef4,
+ 0x157f, 0x7003, 0x0000, 0x703f, 0x0000, 0x0078, 0x1bf7, 0x047e,
+ 0x2404, 0xa005, 0x0040, 0x1f19, 0x2068, 0x6800, 0x007e, 0x6a16,
+ 0x681c, 0xa085, 0x0008, 0x681e, 0x1078, 0x17dd, 0x007f, 0x0078,
+ 0x1f09, 0x047f, 0x2023, 0x0000, 0x007c, 0xa282, 0x0003, 0x0050,
+ 0x1f23, 0x1078, 0x1ba5, 0x2300, 0x0079, 0x1f26, 0x1f29, 0x1f9c,
+ 0x1faa, 0xa282, 0x0002, 0x0040, 0x1f2f, 0x1078, 0x1ba5, 0x70a0,
+ 0x70a3, 0x0000, 0x70c3, 0x0000, 0x0079, 0x1f36, 0x1f3e, 0x1f3e,
+ 0x1f40, 0x1f74, 0x2908, 0x1f3e, 0x1f74, 0x1f3e, 0x1078, 0x1ba5,
+ 0x77b4, 0x1078, 0x2ba6, 0x77b4, 0xa7bc, 0x0f00, 0x1078, 0x2ca1,
+ 0x6018, 0xa005, 0x0040, 0x1f6b, 0x2021, 0x3f80, 0x2009, 0x0004,
+ 0x2011, 0x0010, 0x1078, 0x1fc5, 0x0040, 0x1f6b, 0x157e, 0x20a9,
+ 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009, 0x0004, 0x2011, 0x0010,
+ 0x1078, 0x1fc5, 0x047f, 0x0040, 0x1f6a, 0x8420, 0x0070, 0x1f6a,
+ 0x0078, 0x1f5b, 0x157f, 0x8738, 0xa784, 0x0007, 0x00c0, 0x1f46,
+ 0x0078, 0x1bff, 0x0078, 0x1bff, 0x77b4, 0x1078, 0x2ca1, 0x6018,
+ 0xa005, 0x0040, 0x1f9a, 0x2021, 0x3f80, 0x2009, 0x0005, 0x2011,
+ 0x0020, 0x1078, 0x1fc5, 0x0040, 0x1f9a, 0x157e, 0x20a9, 0x0000,
+ 0x2021, 0x3e80, 0x047e, 0x2009, 0x0005, 0x2011, 0x0020, 0x1078,
+ 0x1fc5, 0x047f, 0x0040, 0x1f99, 0x8420, 0x0070, 0x1f99, 0x0078,
+ 0x1f8a, 0x157f, 0x0078, 0x1bff, 0x2200, 0x0079, 0x1f9f, 0x1fa2,
+ 0x1fa4, 0x1fa4, 0x1078, 0x1ba5, 0x70a3, 0x0000, 0x70a7, 0x0001,
+ 0x0078, 0x1bf7, 0x2200, 0x0079, 0x1fad, 0x1fb2, 0x1fa4, 0x1fb0,
+ 0x1078, 0x1ba5, 0x1078, 0x241f, 0x7000, 0xa086, 0x0001, 0x00c0,
+ 0x28af, 0x1078, 0x28ef, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078,
+ 0x28a2, 0x0040, 0x28af, 0x0078, 0x1cd6, 0x2404, 0xa005, 0x0040,
+ 0x1fe6, 0x2068, 0x2d04, 0x007e, 0x6810, 0xa706, 0x0040, 0x1fd4,
+ 0x2d20, 0x007f, 0x0078, 0x1fc6, 0x007f, 0x2022, 0x6916, 0x681c,
+ 0xa205, 0x681e, 0x1078, 0x17dd, 0x6010, 0x8001, 0x6012, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x1078, 0x28ef, 0x007c, 0xa085, 0x0001,
+ 0x0078, 0x1fe5, 0x2300, 0x0079, 0x1fed, 0x1ff2, 0x1ff0, 0x2035,
+ 0x1078, 0x1ba5, 0x78e4, 0xa005, 0x00d0, 0x2015, 0x0018, 0x2015,
+ 0x2008, 0xa084, 0x0030, 0x00c0, 0x2001, 0x781b, 0x0049, 0x0078,
+ 0x1bf7, 0x78ec, 0xa084, 0x0003, 0x0040, 0x1ffd, 0x2100, 0xa084,
+ 0x0007, 0x0079, 0x200b, 0x2023, 0x2029, 0x201d, 0x2013, 0x2d58,
+ 0x2d58, 0x2013, 0x202f, 0x1078, 0x1ba5, 0x7000, 0xa005, 0x0040,
+ 0x1bff, 0x2001, 0x0003, 0x0078, 0x22fa, 0x1078, 0x2b89, 0x781b,
+ 0x0055, 0x0078, 0x1bf7, 0x1078, 0x2b89, 0x781b, 0x00dc, 0x0078,
+ 0x1bf7, 0x1078, 0x2b89, 0x781b, 0x00e3, 0x0078, 0x1bf7, 0x1078,
+ 0x2b89, 0x781b, 0x009d, 0x0078, 0x1bf7, 0xa584, 0x000f, 0x00c0,
+ 0x205f, 0x1078, 0x241f, 0x7000, 0x0079, 0x203e, 0x2046, 0x2053,
+ 0x2046, 0x28af, 0x2048, 0x28af, 0x2046, 0x2046, 0x1078, 0x1ba5,
+ 0x71a0, 0x70a3, 0x0000, 0xa186, 0x0004, 0x00c0, 0x2051, 0x0078,
+ 0x2908, 0x0078, 0x28af, 0x1078, 0x28ef, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x28a2, 0x0040, 0x28af, 0x0078, 0x1cd6, 0x78e4,
+ 0xa005, 0x00d0, 0x2015, 0x0018, 0x2015, 0x2008, 0xa084, 0x0030,
+ 0x00c0, 0x206e, 0x781b, 0x0049, 0x0078, 0x1bf7, 0x78ec, 0xa084,
+ 0x0003, 0x0040, 0x206a, 0x2100, 0xa184, 0x0007, 0x0079, 0x2078,
+ 0x2088, 0x208e, 0x2082, 0x2080, 0x2d58, 0x2d58, 0x2080, 0x2d50,
+ 0x1078, 0x1ba5, 0x1078, 0x2b91, 0x781b, 0x0055, 0x0078, 0x1bf7,
+ 0x1078, 0x2b91, 0x781b, 0x00dc, 0x0078, 0x1bf7, 0x1078, 0x2b91,
+ 0x781b, 0x00e3, 0x0078, 0x1bf7, 0x1078, 0x2b91, 0x781b, 0x009d,
+ 0x0078, 0x1bf7, 0x2300, 0x0079, 0x209d, 0x20a2, 0x20a0, 0x20a4,
+ 0x1078, 0x1ba5, 0x0078, 0x2665, 0x6817, 0x0008, 0x78a3, 0x0000,
+ 0x79e4, 0xa184, 0x0030, 0x0040, 0x2665, 0x78ec, 0xa084, 0x0003,
+ 0x0040, 0x2665, 0xa184, 0x0007, 0x0079, 0x20b6, 0x2023, 0x2029,
+ 0x201d, 0x2d30, 0x2d58, 0x2d58, 0x20be, 0x2d50, 0x1078, 0x1ba5,
+ 0xa282, 0x0005, 0x0050, 0x20c6, 0x1078, 0x1ba5, 0x2300, 0x0079,
+ 0x20c9, 0x20cc, 0x22ce, 0x22da, 0x2200, 0x0079, 0x20cf, 0x20d4,
+ 0x20d6, 0x20e9, 0x20d4, 0x22b3, 0x1078, 0x1ba5, 0x789b, 0x0018,
+ 0x78a8, 0xa084, 0x00ff, 0xa082, 0x0020, 0x0048, 0x2b6a, 0xa08a,
+ 0x0004, 0x00c8, 0x2b6a, 0x0079, 0x20e5, 0x2b6a, 0x2b6a, 0x2b6a,
+ 0x2b0c, 0x789b, 0x0018, 0x79a8, 0xa184, 0x0080, 0x0040, 0x20fe,
+ 0xa184, 0x0018, 0x0040, 0x20fa, 0x0078, 0x2b6a, 0x7000, 0xa005,
+ 0x00c0, 0x20f4, 0x2011, 0x0003, 0x0078, 0x2752, 0xa184, 0x00ff,
+ 0xa08a, 0x0010, 0x00c8, 0x2b6a, 0x0079, 0x2106, 0x2118, 0x2116,
+ 0x212e, 0x2130, 0x21c2, 0x2b6a, 0x2b6a, 0x21c4, 0x2b6a, 0x2b6a,
+ 0x22af, 0x22af, 0x2b6a, 0x2b6a, 0x2b6a, 0x22b1, 0x1078, 0x1ba5,
+ 0xa684, 0x1000, 0x0040, 0x2125, 0x2001, 0x0300, 0x8000, 0x8000,
+ 0x783a, 0x781b, 0x009a, 0x0078, 0x1bf7, 0x6814, 0xa084, 0x8000,
+ 0x0040, 0x212c, 0x6817, 0x0003, 0x0078, 0x2d30, 0x1078, 0x1ba5,
+ 0x691c, 0x691e, 0xa684, 0x1800, 0x00c0, 0x214a, 0x681c, 0xa084,
+ 0x0001, 0x00c0, 0x2152, 0x6814, 0xa086, 0x0008, 0x00c0, 0x2142,
+ 0x6817, 0x0000, 0xa684, 0x0400, 0x0040, 0x21be, 0x781b, 0x0058,
+ 0x0078, 0x1bf7, 0xa684, 0x1000, 0x0040, 0x2152, 0x781b, 0x0058,
+ 0x0078, 0x1bf7, 0xa684, 0x0060, 0x0040, 0x21ba, 0xa684, 0x0800,
+ 0x0040, 0x21ba, 0xa684, 0x8000, 0x00c0, 0x2160, 0x0078, 0x217a,
+ 0xa6b4, 0x7fff, 0x7e5a, 0x6eb2, 0x789b, 0x0074, 0x7aac, 0x79ac,
+ 0x78ac, 0x801b, 0x00c8, 0x216d, 0x8000, 0xa084, 0x003f, 0xa108,
+ 0xa291, 0x0000, 0x6b94, 0x2100, 0xa302, 0x68ae, 0x6b90, 0x2200,
+ 0xa303, 0x68aa, 0xa684, 0x4000, 0x0040, 0x2182, 0xa6b4, 0xbfff,
+ 0x7e5a, 0x6eb2, 0x7000, 0xa086, 0x0003, 0x00c0, 0x218f, 0x1078,
+ 0x2f3a, 0x1078, 0x30df, 0x781b, 0x0067, 0x0078, 0x1bf7, 0xa006,
+ 0x1078, 0x3194, 0x6aac, 0x69a8, 0x6c94, 0x6b90, 0x2200, 0xa105,
+ 0x0040, 0x219e, 0x2200, 0xa422, 0x2100, 0xa31b, 0x7cd2, 0x7bd6,
+ 0x2300, 0xa405, 0x00c0, 0x21ac, 0xa6b5, 0x4000, 0x7e5a, 0x6eb2,
+ 0x781b, 0x0067, 0x0078, 0x1bf7, 0x781b, 0x0067, 0x2200, 0xa115,
+ 0x00c0, 0x21b6, 0x1078, 0x30f0, 0x0078, 0x1bf7, 0x1078, 0x311d,
+ 0x0078, 0x1bf7, 0x781b, 0x006a, 0x0078, 0x1bf7, 0x781b, 0x0058,
+ 0x0078, 0x1bf7, 0x1078, 0x1ba5, 0x0078, 0x2221, 0x691c, 0xa184,
+ 0x0100, 0x0040, 0x21dc, 0xa18c, 0xfeff, 0x691e, 0x0c7e, 0x7048,
+ 0x2060, 0x6000, 0xa084, 0xefff, 0x6002, 0x6004, 0xa084, 0xfff5,
+ 0x6006, 0x0c7f, 0x0078, 0x2210, 0xa184, 0x0200, 0x0040, 0x2210,
+ 0xa18c, 0xfdff, 0x691e, 0x0c7e, 0x7048, 0x2060, 0x6000, 0xa084,
+ 0xdfff, 0x6002, 0x6004, 0xa084, 0xffef, 0x6006, 0x2008, 0x2c48,
+ 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2210, 0x1078, 0x2c9d, 0x1078,
+ 0x29e6, 0x88ff, 0x0040, 0x2210, 0x789b, 0x0060, 0x2800, 0x78aa,
+ 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x220c,
+ 0x781b, 0x0055, 0x0078, 0x1bf7, 0x781b, 0x0069, 0x0078, 0x1bf7,
+ 0x7e58, 0xa684, 0x0400, 0x00c0, 0x2219, 0x781b, 0x0058, 0x0078,
+ 0x1bf7, 0x781b, 0x006a, 0x0078, 0x1bf7, 0x0078, 0x2b70, 0x0078,
+ 0x2b70, 0x2019, 0x0000, 0x7990, 0xa18c, 0x0007, 0x0040, 0x221f,
+ 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001, 0x00c0,
+ 0x2244, 0x2300, 0x7ca8, 0xa400, 0x2018, 0xa102, 0x0040, 0x223c,
+ 0x0048, 0x223c, 0x0078, 0x223e, 0x0078, 0x21c6, 0x24a8, 0x7aa8,
+ 0x00f0, 0x223e, 0x0078, 0x222a, 0xa284, 0x00f0, 0xa086, 0x0020,
+ 0x00c0, 0x22a0, 0x8318, 0x8318, 0x2300, 0xa102, 0x0040, 0x2254,
+ 0x0048, 0x2254, 0x0078, 0x229d, 0xa286, 0x0023, 0x0040, 0x221f,
+ 0x6818, 0xa084, 0xfff1, 0x681a, 0x7e58, 0xa684, 0xfff1, 0xa085,
+ 0x0010, 0x2030, 0x7e5a, 0x6008, 0xa085, 0x0010, 0x600a, 0x0c7e,
+ 0x7048, 0x2060, 0x6004, 0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0010,
+ 0x0040, 0x2278, 0x1078, 0x2c9d, 0x1078, 0x2acc, 0x0078, 0x2287,
+ 0x0c7e, 0x7048, 0x2060, 0x6004, 0x2008, 0x2c48, 0x0c7f, 0xa184,
+ 0x0008, 0x0040, 0x2210, 0x1078, 0x2c9d, 0x1078, 0x29e6, 0x88ff,
+ 0x0040, 0x2210, 0x789b, 0x0060, 0x2800, 0x78aa, 0xa6b5, 0x0004,
+ 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2299, 0x781b, 0x0055, 0x0078,
+ 0x1bf7, 0x781b, 0x0069, 0x0078, 0x1bf7, 0x7aa8, 0x0078, 0x222a,
+ 0x8318, 0x2300, 0xa102, 0x0040, 0x22a9, 0x0048, 0x22a9, 0x0078,
+ 0x222a, 0xa284, 0x0080, 0x00c0, 0x2b76, 0x0078, 0x2b70, 0x0078,
+ 0x2b76, 0x0078, 0x2b6a, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff,
+ 0xa08e, 0x0001, 0x0040, 0x22be, 0x1078, 0x1ba5, 0x7aa8, 0xa294,
+ 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004, 0x00c8, 0x2b6a,
+ 0x0079, 0x22ca, 0x2b6a, 0x2939, 0x2b6a, 0x2a67, 0xa282, 0x0000,
+ 0x00c0, 0x22d4, 0x1078, 0x1ba5, 0x1078, 0x2b89, 0x781b, 0x0069,
+ 0x0078, 0x1bf7, 0xa282, 0x0003, 0x00c0, 0x22e0, 0x1078, 0x1ba5,
+ 0x1078, 0x2b99, 0x781b, 0x0069, 0x0078, 0x1bf7, 0xa282, 0x0004,
+ 0x0050, 0x22ec, 0x1078, 0x1ba5, 0x2300, 0x0079, 0x22ef, 0x22f2,
+ 0x23c9, 0x23fa, 0xa286, 0x0003, 0x0040, 0x22f8, 0x1078, 0x1ba5,
+ 0x2001, 0x0000, 0x703a, 0x7000, 0xa084, 0x0007, 0x0079, 0x2300,
+ 0x2308, 0x230a, 0x230a, 0x2508, 0x2530, 0x24d2, 0x2308, 0x2308,
+ 0x1078, 0x1ba5, 0xa684, 0x1000, 0x00c0, 0x2312, 0x1078, 0x2ec7,
+ 0x0040, 0x23a3, 0x7868, 0xa08c, 0x00ff, 0x0040, 0x235a, 0xa186,
+ 0x0008, 0x00c0, 0x2329, 0x1078, 0x28ef, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x28a2, 0x0040, 0x235a, 0x1078, 0x2ec7, 0x0078,
+ 0x2341, 0xa186, 0x0028, 0x00c0, 0x235a, 0x1078, 0x2ec7, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x6018, 0xa005, 0x0040, 0x2341, 0x8001,
+ 0x601a, 0xa005, 0x0040, 0x2341, 0x8001, 0xa005, 0x0040, 0x2341,
+ 0x601e, 0x681c, 0xa084, 0x0001, 0x0040, 0x1bff, 0x681c, 0xa084,
+ 0xfffe, 0x681e, 0x7054, 0x0c7e, 0x2060, 0x6800, 0x6002, 0x0c7f,
+ 0x6004, 0x6802, 0xa005, 0x2d00, 0x00c0, 0x2357, 0x6002, 0x6006,
+ 0x0078, 0x1bff, 0x017e, 0x1078, 0x241f, 0x017f, 0xa684, 0xdf00,
+ 0x681a, 0x6827, 0x0000, 0x6f10, 0x81ff, 0x0040, 0x23a3, 0xa186,
+ 0x0002, 0x00c0, 0x239b, 0xa684, 0x0800, 0x00c0, 0x2377, 0xa684,
+ 0x0060, 0x0040, 0x2377, 0x78d8, 0x7adc, 0x682e, 0x6a2a, 0x8717,
+ 0xa294, 0x000f, 0x8213, 0x8213, 0x8213, 0xa290, 0x3600, 0xa290,
+ 0x0000, 0x221c, 0xa384, 0x0100, 0x00c0, 0x2388, 0x0078, 0x238e,
+ 0x8210, 0x2204, 0xa085, 0x0018, 0x2012, 0x8211, 0xa384, 0x0400,
+ 0x0040, 0x239b, 0x689c, 0xa084, 0x0100, 0x00c0, 0x239b, 0x1078,
+ 0x2491, 0x0078, 0x1bff, 0xa186, 0x0018, 0x0040, 0x23a3, 0xa186,
+ 0x0014, 0x0040, 0x1bff, 0x6912, 0x6814, 0xa084, 0x8000, 0x0040,
+ 0x23ab, 0x7038, 0x6816, 0xa68c, 0xdf00, 0x691a, 0x1078, 0x28e0,
+ 0x1078, 0x28ef, 0x00c0, 0x23b8, 0x6008, 0xa084, 0xffef, 0x600a,
+ 0x681c, 0xa084, 0x0001, 0x00c0, 0x23c1, 0x1078, 0x28d9, 0x0078,
+ 0x23c5, 0x7054, 0x2060, 0x6800, 0x6002, 0x1078, 0x17dd, 0x0078,
+ 0x1bff, 0xa282, 0x0004, 0x0048, 0x23cf, 0x1078, 0x1ba5, 0x2200,
+ 0x0079, 0x23d2, 0x23d6, 0x23d8, 0x23e5, 0x23d8, 0x1078, 0x1ba5,
+ 0x7000, 0xa086, 0x0005, 0x0040, 0x23e1, 0x1078, 0x2b89, 0x781b,
+ 0x0069, 0x781b, 0x006a, 0x0078, 0x1bf7, 0x7890, 0x8007, 0x8001,
+ 0xa084, 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c, 0x00ff,
+ 0xa186, 0x0003, 0x0040, 0x23f6, 0x0078, 0x2b6a, 0x781b, 0x006a,
+ 0x0078, 0x1bf7, 0x681c, 0xa085, 0x0004, 0x681e, 0x82ff, 0x00c0,
+ 0x2405, 0x1078, 0x2b89, 0x0078, 0x240c, 0x8211, 0x0040, 0x240a,
+ 0x1078, 0x1ba5, 0x1078, 0x2b99, 0x781b, 0x0069, 0x0078, 0x1bf7,
+ 0x1078, 0x2d77, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x241c, 0x0018,
+ 0x241c, 0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, 0xa684,
+ 0x0060, 0x00c0, 0x2429, 0x682f, 0x0000, 0x682b, 0x0000, 0x0078,
+ 0x2490, 0xa684, 0x0800, 0x00c0, 0x2438, 0x68b0, 0xa084, 0x4800,
+ 0xa635, 0xa684, 0x0800, 0x00c0, 0x2438, 0x1078, 0x2ec7, 0x007c,
+ 0xa684, 0x0020, 0x0040, 0x2462, 0x78d0, 0x8003, 0x00c8, 0x2446,
+ 0xa006, 0x1078, 0x3194, 0x78d4, 0x1078, 0x31f9, 0xa684, 0x4000,
+ 0x0040, 0x2450, 0x682f, 0x0000, 0x682b, 0x0000, 0x0078, 0x2435,
+ 0x68b0, 0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x244a,
+ 0x7038, 0xa005, 0x00c0, 0x245c, 0x79d8, 0x7adc, 0x692e, 0x6a2a,
+ 0x0078, 0x2435, 0xa684, 0x4000, 0x0040, 0x246c, 0x682f, 0x0000,
+ 0x682b, 0x0000, 0x0078, 0x2435, 0x68b0, 0xa084, 0x4800, 0xa635,
+ 0xa684, 0x4000, 0x00c0, 0x2466, 0x7038, 0xa005, 0x00c0, 0x247a,
+ 0x703b, 0x0007, 0x79d8, 0x7adc, 0x78d0, 0x80f3, 0x00c8, 0x2481,
+ 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x692e, 0x6a2a,
+ 0x2100, 0xa205, 0x00c0, 0x248e, 0x0078, 0x2435, 0x1078, 0x3194,
+ 0x007c, 0xa384, 0x0200, 0x0040, 0x2499, 0x6008, 0xa085, 0x0002,
+ 0x600a, 0x6817, 0x0006, 0x6a28, 0x692c, 0x6a3a, 0x693e, 0x682b,
+ 0x0300, 0x682f, 0x0000, 0x6833, 0x2000, 0x6893, 0x0000, 0x6897,
+ 0x0020, 0x7000, 0x0079, 0x24ac, 0x24b4, 0x24b6, 0x24bf, 0x24b4,
+ 0x24b4, 0x24b4, 0x24b4, 0x24b4, 0x1078, 0x1ba5, 0x681c, 0xa084,
+ 0x0001, 0x00c0, 0x24bf, 0x1078, 0x28d9, 0x0078, 0x24c5, 0x7054,
+ 0x2c50, 0x2060, 0x6800, 0x6002, 0x2a60, 0x2021, 0x3557, 0x2404,
+ 0xa005, 0x0040, 0x24ce, 0x2020, 0x0078, 0x24c7, 0x2d22, 0x206b,
+ 0x0000, 0x007c, 0x77b4, 0x1078, 0x2ba6, 0xa7bc, 0x0f00, 0x1078,
+ 0x2ca1, 0x6018, 0xa005, 0x0040, 0x2501, 0x0d7e, 0x2001, 0x3f90,
+ 0x2068, 0x0d7f, 0x2021, 0x3f80, 0x2009, 0x0004, 0x2011, 0x0010,
+ 0x1078, 0x1fc5, 0x0040, 0x2501, 0x157e, 0x20a9, 0x0000, 0x2021,
+ 0x3e80, 0x047e, 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x1fc5,
+ 0x047f, 0x0040, 0x2500, 0x8420, 0x0070, 0x2500, 0x0078, 0x24f1,
+ 0x157f, 0x8738, 0xa784, 0x0007, 0x00c0, 0x24d7, 0x0078, 0x1bff,
+ 0x1078, 0x28e0, 0x1078, 0x28ef, 0x6827, 0x0000, 0x789b, 0x000e,
+ 0x6f10, 0x6813, 0x0002, 0x1078, 0x31ca, 0xa684, 0x0800, 0x0040,
+ 0x251d, 0x6918, 0xa18d, 0x2000, 0x691a, 0x6814, 0xa084, 0x8000,
+ 0x0040, 0x2524, 0x6817, 0x0000, 0x2021, 0x3557, 0x6800, 0x2022,
+ 0x6a38, 0x693c, 0x6a2a, 0x692e, 0x1078, 0x17dd, 0x0078, 0x1bff,
+ 0x1078, 0x241f, 0x6827, 0x0000, 0x789b, 0x000e, 0x6f10, 0x1078,
+ 0x2d7c, 0xa08c, 0x00ff, 0x6912, 0x6814, 0xa084, 0x8000, 0x0040,
+ 0x2543, 0x7038, 0x6816, 0xa68c, 0xdf00, 0x691a, 0x70a3, 0x0000,
+ 0x0078, 0x1bff, 0xa006, 0x1078, 0x2ec7, 0x6813, 0x0000, 0x6817,
+ 0x0001, 0xa68c, 0xdf00, 0x691a, 0x6827, 0x0000, 0x7000, 0x0079,
+ 0x2559, 0x2561, 0x2563, 0x2563, 0x2565, 0x2565, 0x2565, 0x2561,
+ 0x2561, 0x1078, 0x1ba5, 0x1078, 0x28ef, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x0078, 0x28ba, 0x2300, 0x0079, 0x256e, 0x2571, 0x2573,
+ 0x25b1, 0x1078, 0x1ba5, 0x7000, 0x0079, 0x2576, 0x257e, 0x2580,
+ 0x2580, 0x258b, 0x2580, 0x2592, 0x257e, 0x257e, 0x1078, 0x1ba5,
+ 0xa684, 0x2000, 0x00c0, 0x258b, 0xa6b5, 0x2000, 0x7e5a, 0x1078,
+ 0x30f0, 0x0078, 0x2d30, 0x6814, 0xa084, 0x8000, 0x0040, 0x2592,
+ 0x6817, 0x0007, 0x2009, 0x3518, 0x210c, 0xa186, 0x0000, 0x0040,
+ 0x25a7, 0xa186, 0x0001, 0x0040, 0x25ab, 0x2009, 0x352b, 0x200b,
+ 0x000b, 0x70a3, 0x0001, 0x781b, 0x0046, 0x0078, 0x1bf7, 0x781b,
+ 0x00dd, 0x0078, 0x1bf7, 0x2009, 0x352b, 0x200b, 0x000a, 0x0078,
+ 0x1bf7, 0x1078, 0x1ba5, 0x2300, 0x0079, 0x25b6, 0x25b9, 0x25bb,
+ 0x25de, 0x1078, 0x1ba5, 0x7000, 0x0079, 0x25be, 0x25c6, 0x25c8,
+ 0x25c8, 0x25d3, 0x25c8, 0x25da, 0x25c6, 0x25c6, 0x1078, 0x1ba5,
+ 0xa684, 0x2000, 0x00c0, 0x25d3, 0xa6b5, 0x2000, 0x7e5a, 0x1078,
+ 0x30f0, 0x0078, 0x2d30, 0x6814, 0xa084, 0x8000, 0x0040, 0x25da,
+ 0x6817, 0x0007, 0x781b, 0x00e4, 0x0078, 0x1bf7, 0x681c, 0xa085,
+ 0x0004, 0x681e, 0xa6b5, 0x0800, 0x1078, 0x2b89, 0x781b, 0x0069,
+ 0x0078, 0x1bf7, 0x2300, 0x0079, 0x25ed, 0x25f0, 0x25f2, 0x25f4,
+ 0x1078, 0x1ba5, 0x1078, 0x1ba5, 0xa684, 0x0400, 0x00c0, 0x2613,
+ 0x782b, 0x3009, 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb,
+ 0x785a, 0x79e4, 0xa184, 0x0020, 0x0040, 0x260b, 0x78ec, 0xa084,
+ 0x0003, 0x00c0, 0x260f, 0x2001, 0x0014, 0x0078, 0x22fa, 0xa184,
+ 0x0007, 0x0079, 0x264b, 0x7a90, 0xa294, 0x0007, 0x789b, 0x0060,
+ 0x79a8, 0x81ff, 0x0040, 0x2649, 0x789b, 0x0010, 0x7ba8, 0xa384,
+ 0x0001, 0x00c0, 0x263a, 0x7ba8, 0x7ba8, 0xa386, 0x0001, 0x00c0,
+ 0x262d, 0x2009, 0xfff7, 0x0078, 0x2633, 0xa386, 0x0003, 0x00c0,
+ 0x263a, 0x2009, 0xffef, 0x0c7e, 0x7048, 0x2060, 0x6004, 0xa104,
+ 0x6006, 0x0c7f, 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb,
+ 0x785a, 0x782b, 0x3009, 0x691c, 0xa18c, 0xfdff, 0xa18c, 0xfeff,
+ 0x691e, 0x0078, 0x2d30, 0x2023, 0x2029, 0x2655, 0x265d, 0x2653,
+ 0x2653, 0x2653, 0x2d30, 0x1078, 0x1ba5, 0x691c, 0xa18c, 0xfdff,
+ 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d38, 0x691c, 0xa18c, 0xfdff,
+ 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d30, 0x79e4, 0xa184, 0x0030,
+ 0x0040, 0x266f, 0x78ec, 0xa084, 0x0003, 0x00c0, 0x2677, 0x6814,
+ 0xa085, 0x8000, 0x6816, 0x2001, 0x0014, 0x0078, 0x22fa, 0xa184,
+ 0x0007, 0x0079, 0x267b, 0x2d30, 0x2d30, 0x2683, 0x2d30, 0x2d58,
+ 0x2d58, 0x2d30, 0x2d30, 0xa684, 0x0400, 0x00c0, 0x26b4, 0x681c,
+ 0xa084, 0x0001, 0x0040, 0x2d38, 0xa68c, 0x2060, 0xa18c, 0xfffb,
+ 0x795a, 0x69b2, 0x789b, 0x0060, 0x78ab, 0x0000, 0x789b, 0x0061,
+ 0x6814, 0xa085, 0x8000, 0x6816, 0x78aa, 0x157e, 0x137e, 0x147e,
+ 0x20a1, 0x012c, 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, 0x000a,
+ 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x6810, 0x8007, 0x789b,
+ 0x007e, 0x78aa, 0x0078, 0x2d38, 0x6814, 0xa084, 0x8000, 0x0040,
+ 0x26bb, 0x6817, 0x0008, 0x781b, 0x00d8, 0x0078, 0x1bf7, 0x2300,
+ 0x0079, 0x26c2, 0x26c7, 0x2742, 0x26c5, 0x1078, 0x1ba5, 0x7000,
+ 0xa084, 0x0007, 0x0079, 0x26cc, 0x26d4, 0x26d6, 0x26f2, 0x26d4,
+ 0x26d4, 0x24d2, 0x26d4, 0x26d4, 0x1078, 0x1ba5, 0x691c, 0xa18d,
+ 0x0001, 0x691e, 0x6800, 0x6006, 0xa005, 0x00c0, 0x26e0, 0x6002,
+ 0x6818, 0xa084, 0x000e, 0x0040, 0x26ec, 0x7014, 0x68b6, 0x712c,
+ 0xa188, 0x3e80, 0x0078, 0x26ee, 0x2009, 0x3f80, 0x2104, 0x6802,
+ 0x2d0a, 0x7156, 0x6eb2, 0xa684, 0x0060, 0x0040, 0x2740, 0xa684,
+ 0x0800, 0x00c0, 0x2704, 0xa684, 0x7fff, 0x68b2, 0x6890, 0x6894,
+ 0x1078, 0x2ec7, 0x0078, 0x2740, 0xa684, 0x0020, 0x0040, 0x2716,
+ 0xa006, 0x1078, 0x3194, 0x78d0, 0x8003, 0x00c8, 0x2712, 0x78d4,
+ 0x1078, 0x31f9, 0x79d8, 0x7adc, 0x0078, 0x271a, 0x1078, 0x2cae,
+ 0x1078, 0x3194, 0xa684, 0x8000, 0x0040, 0x2740, 0xa684, 0x7fff,
+ 0x68b2, 0x789b, 0x0074, 0x1078, 0x2d7c, 0x2010, 0x1078, 0x2d7c,
+ 0x2008, 0xa684, 0x0020, 0x00c0, 0x2738, 0x1078, 0x2d7c, 0x801b,
+ 0x00c8, 0x2733, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+ 0x6b94, 0x2100, 0xa302, 0x68ae, 0x6b90, 0x2200, 0xa303, 0x68aa,
+ 0x0078, 0x1bff, 0x0078, 0x2b76, 0x7033, 0x0000, 0xa282, 0x0005,
+ 0x0050, 0x274c, 0x1078, 0x1ba5, 0x2300, 0x0079, 0x274f, 0x2752,
+ 0x275c, 0x277f, 0x2200, 0x0079, 0x2755, 0x275a, 0x2b76, 0x275a,
+ 0x27a8, 0x27f9, 0x1078, 0x1ba5, 0x7000, 0xa086, 0x0001, 0x00c0,
+ 0x2769, 0x1078, 0x28ef, 0x1078, 0x2ec7, 0x7034, 0x600a, 0x0078,
+ 0x276e, 0x7000, 0xa086, 0x0003, 0x0040, 0x2763, 0x7003, 0x0005,
+ 0x2001, 0x3f90, 0x2068, 0x703e, 0x7032, 0x2200, 0x0079, 0x2778,
+ 0x2b76, 0x277d, 0x27a8, 0x277d, 0x2b76, 0x1078, 0x1ba5, 0x7000,
+ 0xa086, 0x0001, 0x00c0, 0x278c, 0x1078, 0x28ef, 0x1078, 0x2ec7,
+ 0x7034, 0x600a, 0x0078, 0x2791, 0x7000, 0xa086, 0x0003, 0x0040,
+ 0x2786, 0x7003, 0x0005, 0x2001, 0x3f90, 0x2068, 0x703e, 0x7032,
+ 0x2200, 0x0079, 0x279b, 0x27a2, 0x27a0, 0x27a2, 0x27a0, 0x27a2,
+ 0x1078, 0x1ba5, 0x1078, 0x2b99, 0x781b, 0x0069, 0x0078, 0x1bf7,
+ 0x7000, 0xa086, 0x0001, 0x00c0, 0x27b5, 0x1078, 0x28ef, 0x1078,
+ 0x2ec7, 0x7034, 0x600a, 0x0078, 0x27ba, 0x7000, 0xa086, 0x0003,
+ 0x0040, 0x27af, 0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b,
+ 0x0018, 0x7ca8, 0xa484, 0x0007, 0xa215, 0x2069, 0x3f80, 0x2d04,
+ 0x2d08, 0x7156, 0x2068, 0xa005, 0x0040, 0x27d5, 0x6810, 0xa206,
+ 0x0040, 0x27ee, 0x6800, 0x0078, 0x27c8, 0x7003, 0x0005, 0x2001,
+ 0x3f90, 0x2068, 0x703e, 0x7032, 0x157e, 0x20a9, 0x002f, 0x2003,
+ 0x0000, 0x8000, 0x0070, 0x27e6, 0x0078, 0x27df, 0x157f, 0x6a12,
+ 0x68b3, 0x0700, 0x681f, 0x0800, 0x6823, 0x0003, 0x6eb0, 0x7e5a,
+ 0x681c, 0xa084, 0x0c00, 0x0040, 0x284f, 0x1078, 0x2b91, 0x0078,
+ 0x284f, 0x7000, 0xa086, 0x0001, 0x00c0, 0x2806, 0x1078, 0x28ef,
+ 0x1078, 0x2ec7, 0x7034, 0x600a, 0x0078, 0x280b, 0x7000, 0xa086,
+ 0x0003, 0x0040, 0x2800, 0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00,
+ 0x789b, 0x0018, 0x7ca8, 0xa484, 0x0007, 0xa215, 0x79a8, 0x79a8,
+ 0xa18c, 0x00ff, 0xa1e8, 0x3e80, 0x2d04, 0x2d08, 0x7156, 0x2068,
+ 0xa005, 0x0040, 0x282a, 0x6810, 0xa206, 0x0040, 0x2843, 0x6800,
+ 0x0078, 0x281d, 0x7003, 0x0005, 0x2001, 0x3f90, 0x2068, 0x703e,
+ 0x7032, 0x157e, 0x20a9, 0x002f, 0x2003, 0x0000, 0x8000, 0x0070,
+ 0x283b, 0x0078, 0x2834, 0x157f, 0x6a12, 0x68b3, 0x0700, 0x681f,
+ 0x0800, 0x6823, 0x0003, 0x6eb0, 0x7e5a, 0x681c, 0xa084, 0x0c00,
+ 0x0040, 0x284f, 0x1078, 0x2b8d, 0x7e58, 0x0078, 0x284f, 0x027e,
+ 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600,
+ 0x2060, 0x704a, 0x6000, 0x704e, 0x6004, 0x7052, 0xa684, 0x0060,
+ 0x0040, 0x2886, 0x6b94, 0x6c90, 0x69a8, 0x68ac, 0xa105, 0x00c0,
+ 0x2874, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0xa6b4, 0xb7ff, 0x7e5a,
+ 0x1078, 0x30f0, 0x0078, 0x2886, 0x68ac, 0xa31a, 0x2100, 0xa423,
+ 0x2400, 0xa305, 0x0040, 0x2886, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde,
+ 0x68ac, 0xa6b4, 0xbfff, 0x7e5a, 0x1078, 0x311d, 0x077f, 0x1078,
+ 0x2ca1, 0x2009, 0x006a, 0xa684, 0x0008, 0x0040, 0x2891, 0x2009,
+ 0x0069, 0xa6b5, 0x2000, 0x7e5a, 0x791a, 0x2d00, 0x703e, 0x8207,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600, 0x2048,
+ 0x0078, 0x1bf7, 0x6020, 0xa005, 0x0040, 0x28ae, 0x8001, 0x6022,
+ 0x6008, 0xa085, 0x0008, 0x600a, 0x7010, 0x6026, 0x007c, 0xa006,
+ 0x1078, 0x2ec7, 0x6813, 0x0000, 0x6817, 0x0001, 0x681f, 0x0040,
+ 0x681b, 0x0100, 0x7000, 0xa084, 0x0007, 0x0079, 0x28bf, 0x28c7,
+ 0x28c9, 0x28c9, 0x28d5, 0x28d1, 0x28c7, 0x28c7, 0x28c7, 0x1078,
+ 0x1ba5, 0x1078, 0x28e0, 0x1078, 0x28d9, 0x1078, 0x17dd, 0x0078,
+ 0x1bff, 0x70a3, 0x0000, 0x0078, 0x1bff, 0x6817, 0x0000, 0x0078,
+ 0x2508, 0x6800, 0xa005, 0x00c0, 0x28de, 0x6002, 0x6006, 0x007c,
+ 0x6010, 0xa005, 0x0040, 0x28e9, 0x8001, 0x00d0, 0x28e9, 0x1078,
+ 0x1ba5, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x007c, 0x6018,
+ 0xa005, 0x0040, 0x28f5, 0x8001, 0x601a, 0x007c, 0x1078, 0x2d77,
+ 0x6817, 0x0018, 0x0078, 0x2926, 0x1078, 0x2d77, 0x6817, 0x0019,
+ 0x0078, 0x2926, 0x1078, 0x2d77, 0x6817, 0x001a, 0x0078, 0x2926,
+ 0x77b4, 0x1078, 0x2ca1, 0x71b8, 0xa18c, 0x00ff, 0xa1e8, 0x3e80,
+ 0x2d04, 0x2d08, 0x2068, 0xa005, 0x00c0, 0x2918, 0x0078, 0x1bff,
+ 0x6810, 0x72b4, 0xa206, 0x0040, 0x2920, 0x6800, 0x0078, 0x2911,
+ 0x6800, 0x200a, 0x6817, 0x0005, 0x70bf, 0x0000, 0x1078, 0x28e0,
+ 0x681c, 0xa084, 0x0001, 0x00c0, 0x292f, 0x1078, 0x28d9, 0x1078,
+ 0x28ef, 0x681b, 0x0000, 0x681f, 0x0020, 0x1078, 0x17dd, 0x0078,
+ 0x1bff, 0xa282, 0x0003, 0x00c0, 0x2b6a, 0x7da8, 0xa5ac, 0x00ff,
+ 0x7ea8, 0xa6b4, 0x00ff, 0x691c, 0xa18d, 0x0080, 0x691e, 0xa184,
+ 0x0100, 0x0040, 0x2999, 0xa18c, 0xfeff, 0x691e, 0xa6b4, 0x00ff,
+ 0x0040, 0x2983, 0xa682, 0x000f, 0x0048, 0x295a, 0x0040, 0x295a,
+ 0x2031, 0x000f, 0x852b, 0x852b, 0x1078, 0x2c24, 0x0040, 0x2964,
+ 0x1078, 0x2a33, 0x0078, 0x298c, 0x1078, 0x2bdf, 0x0c7e, 0x2960,
+ 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, 0x2a57, 0x0c7f, 0x691c,
+ 0xa18d, 0x0100, 0x691e, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684,
+ 0x0400, 0x00c0, 0x297f, 0x781b, 0x0055, 0x0078, 0x1bf7, 0x781b,
+ 0x0069, 0x0078, 0x1bf7, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5,
+ 0x6006, 0x1078, 0x2a57, 0x0c7f, 0x7e58, 0xa684, 0x0400, 0x00c0,
+ 0x2995, 0x781b, 0x0058, 0x0078, 0x1bf7, 0x781b, 0x006a, 0x0078,
+ 0x1bf7, 0x0c7e, 0x7048, 0x2060, 0x6100, 0xa18c, 0x1000, 0x0040,
+ 0x29d9, 0x6208, 0x8217, 0xa294, 0x00ff, 0xa282, 0x000f, 0x0048,
+ 0x29ad, 0x0040, 0x29ad, 0x2011, 0x000f, 0x2600, 0xa202, 0x00c8,
+ 0x29b2, 0x2230, 0x6208, 0xa294, 0x00ff, 0x7018, 0xa086, 0x0028,
+ 0x00c0, 0x29c2, 0xa282, 0x0019, 0x00c8, 0x29c8, 0x2011, 0x0019,
+ 0x0078, 0x29c8, 0xa282, 0x000c, 0x00c8, 0x29c8, 0x2011, 0x000c,
+ 0x2200, 0xa502, 0x00c8, 0x29cd, 0x2228, 0x1078, 0x2be3, 0x852b,
+ 0x852b, 0x1078, 0x2c24, 0x0040, 0x29d9, 0x1078, 0x2a33, 0x0078,
+ 0x29dd, 0x1078, 0x2bdf, 0x1078, 0x2a57, 0x7858, 0xa085, 0x0004,
+ 0x785a, 0x0c7f, 0x781b, 0x0069, 0x0078, 0x1bf7, 0x0c7e, 0x2960,
+ 0x6000, 0xa084, 0x1000, 0x00c0, 0x2a01, 0x6010, 0xa084, 0x000f,
+ 0x00c0, 0x29fb, 0xa18c, 0x0002, 0x00c0, 0x29fb, 0xa18c, 0xfff5,
+ 0x6106, 0x0c7f, 0x007c, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078,
+ 0x2a23, 0x6208, 0xa294, 0x00ff, 0x7018, 0xa086, 0x0028, 0x00c0,
+ 0x2a11, 0xa282, 0x0019, 0x00c8, 0x2a17, 0x2011, 0x0019, 0x0078,
+ 0x2a17, 0xa282, 0x000c, 0x00c8, 0x2a17, 0x2011, 0x000c, 0x6308,
+ 0x831f, 0xa39c, 0x00ff, 0xa382, 0x000f, 0x0048, 0x2a23, 0x0040,
+ 0x2a23, 0x2019, 0x000f, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab,
+ 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005, 0x681c, 0xa085, 0x0100,
+ 0x681e, 0x0c7f, 0x007c, 0x0c7e, 0x7148, 0x2160, 0x2008, 0xa084,
+ 0xfff0, 0xa635, 0x7e86, 0x6018, 0x789a, 0x7eae, 0x6612, 0x78a4,
+ 0xa084, 0xfff8, 0xa18c, 0x0007, 0xa105, 0x78a6, 0x6016, 0x788a,
+ 0xa6b4, 0x000f, 0x8637, 0x8204, 0x8004, 0xa084, 0x00ff, 0xa605,
+ 0x600e, 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x007c, 0x0c7e,
+ 0x7048, 0x2060, 0x6018, 0x789a, 0x78a4, 0xa084, 0xfff0, 0x78a6,
+ 0x6012, 0x7884, 0xa084, 0xfff0, 0x7886, 0x0c7f, 0x007c, 0xa282,
+ 0x0002, 0x00c0, 0x2b6a, 0x7aa8, 0x691c, 0xa18d, 0x0080, 0x691e,
+ 0xa184, 0x0200, 0x0040, 0x2aac, 0xa18c, 0xfdff, 0x691e, 0xa294,
+ 0x00ff, 0xa282, 0x0002, 0x00c8, 0x2b6a, 0x1078, 0x2af3, 0x1078,
+ 0x2a57, 0xa980, 0x0001, 0x200c, 0x1078, 0x2c9d, 0x1078, 0x29e6,
+ 0x88ff, 0x0040, 0x2a9f, 0x789b, 0x0060, 0x2800, 0x78aa, 0x7e58,
+ 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2a9b, 0x781b,
+ 0x0055, 0x0078, 0x1bf7, 0x781b, 0x0069, 0x0078, 0x1bf7, 0x7e58,
+ 0xa684, 0x0400, 0x00c0, 0x2aa8, 0x781b, 0x0058, 0x0078, 0x1bf7,
+ 0x781b, 0x006a, 0x0078, 0x1bf7, 0xa282, 0x0002, 0x00c8, 0x2ab4,
+ 0xa284, 0x0001, 0x0040, 0x2abe, 0x7148, 0xa188, 0x0000, 0x210c,
+ 0xa18c, 0x2000, 0x00c0, 0x2abe, 0x2011, 0x0000, 0x1078, 0x2bd1,
+ 0x1078, 0x2af3, 0x1078, 0x2a57, 0x7858, 0xa085, 0x0004, 0x785a,
+ 0x781b, 0x0069, 0x0078, 0x1bf7, 0x0c7e, 0x027e, 0x2960, 0x6000,
+ 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, 0x2ae3, 0x6014, 0xa084,
+ 0x0040, 0x00c0, 0x2ae1, 0xa18c, 0xffef, 0x6106, 0xa006, 0x0078,
+ 0x2af0, 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab,
+ 0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x681c, 0xa085, 0x0200, 0x681e,
+ 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7048, 0x2060, 0x82ff, 0x0040,
+ 0x2afb, 0x2011, 0x0040, 0x6018, 0xa080, 0x0002, 0x789a, 0x78a4,
+ 0xa084, 0xffbf, 0xa205, 0x78a6, 0x6016, 0x788a, 0x6004, 0xa084,
+ 0xffef, 0x6006, 0x0c7f, 0x007c, 0x007e, 0x7000, 0xa086, 0x0003,
+ 0x0040, 0x2b15, 0x007f, 0x0078, 0x2b18, 0x007f, 0x0078, 0x2b66,
+ 0xa684, 0x0020, 0x0040, 0x2b66, 0x7888, 0xa084, 0x0040, 0x0040,
+ 0x2b66, 0x78a8, 0x8001, 0x0040, 0x2b25, 0x7bb8, 0xa384, 0x003f,
+ 0x831b, 0x00c8, 0x2b2c, 0x8000, 0xa005, 0x0040, 0x2b4d, 0x831b,
+ 0x00c8, 0x2b35, 0x8001, 0x0040, 0x2b62, 0xa006, 0x1078, 0x3194,
+ 0x78b4, 0x1078, 0x31f9, 0x0078, 0x2b66, 0xa684, 0x4000, 0x0040,
+ 0x2b4d, 0x78b8, 0x801b, 0x00c8, 0x2b46, 0x8000, 0xa084, 0x003f,
+ 0x00c0, 0x2b62, 0xa6b4, 0xbfff, 0x7e5a, 0x79d8, 0x7adc, 0x2001,
+ 0x0001, 0xa108, 0x00c8, 0x2b56, 0xa291, 0x0000, 0x79d2, 0x79da,
+ 0x7ad6, 0x7ade, 0x1078, 0x3194, 0x781b, 0x0067, 0x1078, 0x305e,
+ 0x0078, 0x1bf7, 0x781b, 0x0067, 0x0078, 0x1bf7, 0x781b, 0x006a,
+ 0x0078, 0x1bf7, 0x1078, 0x2b9d, 0x781b, 0x0069, 0x0078, 0x1bf7,
+ 0x1078, 0x2b89, 0x781b, 0x0069, 0x0078, 0x1bf7, 0x6823, 0x0002,
+ 0x1078, 0x2b91, 0x691c, 0xa18d, 0x0020, 0x691e, 0x6814, 0xa084,
+ 0x8000, 0x0040, 0x2b85, 0x6817, 0x0005, 0x781b, 0x0069, 0x0078,
+ 0x1bf7, 0x2001, 0x0005, 0x0078, 0x2b9f, 0x2001, 0x000c, 0x0078,
+ 0x2b9f, 0x2001, 0x0006, 0x0078, 0x2b9f, 0x2001, 0x000d, 0x0078,
+ 0x2b9f, 0x2001, 0x0009, 0x0078, 0x2b9f, 0x2001, 0x0007, 0x789b,
+ 0x007f, 0x78aa, 0xa6b5, 0x0008, 0x7e5a, 0x007c, 0x077e, 0x873f,
+ 0xa7bc, 0x000f, 0x873b, 0x873b, 0x8703, 0xa0e0, 0x3600, 0xa7b8,
+ 0x0020, 0x7f9a, 0x79a4, 0xa184, 0x000f, 0x0040, 0x2bbf, 0xa184,
+ 0xfff0, 0x78a6, 0x6012, 0x6004, 0xa085, 0x0008, 0x6006, 0x8738,
+ 0x8738, 0x7f9a, 0x79a4, 0xa184, 0x0040, 0x0040, 0x2bcf, 0xa184,
+ 0xffbf, 0x78a6, 0x6016, 0x6004, 0xa085, 0x0010, 0x6006, 0x077f,
+ 0x007c, 0x789b, 0x0010, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab,
+ 0x0003, 0x7aaa, 0x789b, 0x0060, 0x78ab, 0x0004, 0x007c, 0x2031,
+ 0x0000, 0x2029, 0x0032, 0x789b, 0x0010, 0x78ab, 0x0001, 0x78ab,
+ 0x0003, 0x78ab, 0x0001, 0x7daa, 0x7eaa, 0x789b, 0x0060, 0x78ab,
+ 0x0005, 0x007c, 0x157e, 0x8007, 0xa084, 0x00ff, 0x8003, 0x8003,
+ 0xa080, 0x0020, 0x789a, 0x79a4, 0xa18c, 0xfff0, 0x2001, 0x3546,
+ 0x2004, 0xa082, 0x0028, 0x0040, 0x2c0d, 0x2021, 0x2c84, 0x2019,
+ 0x0014, 0x20a9, 0x000c, 0x0078, 0x2c13, 0x2021, 0x2c90, 0x2019,
+ 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064, 0x2404, 0xa084, 0xfff0,
+ 0xa106, 0x0040, 0x2c22, 0x8420, 0x2300, 0xa210, 0x0070, 0x2c22,
+ 0x0078, 0x2c15, 0x157f, 0x007c, 0x157e, 0x2011, 0x3546, 0x2214,
+ 0xa282, 0x0032, 0x0048, 0x2c38, 0x0040, 0x2c3c, 0x2021, 0x2c76,
+ 0x2019, 0x0011, 0x20a9, 0x000e, 0x2011, 0x0032, 0x0078, 0x2c4c,
+ 0xa282, 0x0028, 0x0040, 0x2c44, 0x2021, 0x2c84, 0x2019, 0x0014,
+ 0x20a9, 0x000c, 0x0078, 0x2c4a, 0x2021, 0x2c90, 0x2019, 0x0019,
+ 0x20a9, 0x000d, 0x2011, 0x0064, 0x2200, 0xa502, 0x0040, 0x2c5c,
+ 0x0048, 0x2c5c, 0x8420, 0x2300, 0xa210, 0x0070, 0x2c59, 0x0078,
+ 0x2c4c, 0x157f, 0xa006, 0x007c, 0x157f, 0xa582, 0x0064, 0x00c8,
+ 0x2c65, 0x7808, 0xa085, 0x0070, 0x780a, 0x78ec, 0xa084, 0x0300,
+ 0x0040, 0x2c73, 0x2404, 0xa09e, 0x1201, 0x00c0, 0x2c73, 0x2001,
+ 0x2101, 0x0078, 0x2c74, 0x2404, 0xa005, 0x007c, 0x1201, 0x3002,
+ 0x3202, 0x4203, 0x4403, 0x5404, 0x5604, 0x6605, 0x6805, 0x7806,
+ 0x7a06, 0x0a07, 0x0c07, 0x0e07, 0x3202, 0x4202, 0x5202, 0x6202,
+ 0x7202, 0x6605, 0x7605, 0x7805, 0x7a05, 0x7c05, 0x7e05, 0x7f05,
+ 0x2202, 0x3202, 0x4202, 0x5202, 0x5404, 0x6404, 0x7404, 0x7604,
+ 0x7804, 0x7a04, 0x7c04, 0x7e04, 0x7f04, 0x789b, 0x0010, 0xa046,
+ 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784, 0x0007, 0x8003, 0x8003,
+ 0x8003, 0x8003, 0xa105, 0xa0e0, 0x3680, 0x007c, 0x79d8, 0x7adc,
+ 0x78d0, 0x801b, 0x00c8, 0x2cb5, 0x8000, 0xa084, 0x003f, 0xa108,
+ 0xa291, 0x0000, 0x007c, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x3540,
+ 0x2091, 0x8000, 0x2104, 0x0079, 0x2cc5, 0x2cf7, 0x2ccf, 0x2ccf,
+ 0x2ccf, 0x2ccf, 0x2ccf, 0x2ccd, 0x2ccd, 0x1078, 0x1ba5, 0x784b,
+ 0x0004, 0x7848, 0xa084, 0x0004, 0x00c0, 0x2cd1, 0x784b, 0x0008,
+ 0x7848, 0xa084, 0x0008, 0x00c0, 0x2cd8, 0x68b0, 0xa085, 0x4000,
+ 0x68b2, 0x7858, 0xa085, 0x4000, 0x785a, 0x7830, 0xa084, 0x0080,
+ 0x00c0, 0x2cf7, 0x0018, 0x2cf7, 0x6818, 0xa084, 0x0020, 0x00c0,
+ 0x2cf5, 0x781b, 0x00dd, 0x0078, 0x2cf7, 0x781b, 0x00e4, 0x2091,
+ 0x8001, 0x0f7f, 0x007c, 0x0c7e, 0x6810, 0x8007, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa0e0, 0x3600, 0x6004, 0xa084, 0x000a,
+ 0x00c0, 0x2d2e, 0x6108, 0xa194, 0xff00, 0x0040, 0x2d2e, 0xa18c,
+ 0x00ff, 0x2001, 0x0019, 0xa106, 0x0040, 0x2d1d, 0x2001, 0x0032,
+ 0xa106, 0x0040, 0x2d21, 0x0078, 0x2d25, 0x2009, 0x0020, 0x0078,
+ 0x2d27, 0x2009, 0x003f, 0x0078, 0x2d27, 0x2011, 0x0000, 0x2100,
+ 0xa205, 0x600a, 0x6004, 0xa085, 0x0002, 0x6006, 0x0c7f, 0x007c,
+ 0x781b, 0x006a, 0x0078, 0x1bf7, 0x781b, 0x0069, 0x0078, 0x1bf7,
+ 0x781b, 0x0058, 0x0078, 0x1bf7, 0x781b, 0x0055, 0x0078, 0x1bf7,
+ 0x781b, 0x00dd, 0x0078, 0x1bf7, 0x781b, 0x00dc, 0x0078, 0x1bf7,
+ 0x781b, 0x00e4, 0x0078, 0x1bf7, 0x781b, 0x00e3, 0x0078, 0x1bf7,
+ 0x781b, 0x009e, 0x0078, 0x1bf7, 0x781b, 0x009d, 0x0078, 0x1bf7,
+ 0x70a3, 0x0001, 0x781b, 0x0046, 0x0078, 0x1bf7, 0x007e, 0x7830,
+ 0xa084, 0x00c0, 0x00c0, 0x2d75, 0x7808, 0xa084, 0xfffd, 0x780a,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021, 0x0040,
+ 0x2d75, 0x7808, 0xa085, 0x0002, 0x780a, 0x007f, 0x007c, 0x7808,
+ 0xa085, 0x0002, 0x780a, 0x007c, 0x7830, 0xa084, 0x0040, 0x00c0,
+ 0x2d7c, 0x0098, 0x2d85, 0x78ac, 0x007c, 0x7808, 0xa084, 0xfffd,
+ 0x780a, 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021,
+ 0x0040, 0x2d94, 0x0098, 0x2d92, 0x78ac, 0x007e, 0x7808, 0xa085,
+ 0x0002, 0x780a, 0x007f, 0x007c, 0xa784, 0x0070, 0x0040, 0x2da8,
+ 0x0c7e, 0x2d60, 0x2f68, 0x1078, 0x1b6b, 0x2d78, 0x2c68, 0x0c7f,
+ 0x6817, 0x0003, 0x7858, 0xa084, 0x3f00, 0x681a, 0x682f, 0x0000,
+ 0x682b, 0x0000, 0x784b, 0x0008, 0x78e4, 0xa005, 0x00d0, 0x2015,
+ 0xa084, 0x0020, 0x0040, 0x2015, 0x78ec, 0xa084, 0x0003, 0x0040,
+ 0x2015, 0x0018, 0x2015, 0x0078, 0x2b70, 0x0c7e, 0x6810, 0x8007,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600, 0x2060,
+ 0x2048, 0x704a, 0x6000, 0x704e, 0x6004, 0x7052, 0x0c7f, 0x007c,
+ 0x0020, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020,
+ 0x0000, 0x0020, 0x0062, 0x0009, 0x0014, 0x0014, 0x9847, 0x0014,
+ 0x0014, 0x98f5, 0x98e7, 0x0014, 0x0014, 0x0080, 0x00bf, 0x0100,
+ 0x0402, 0x2008, 0xf880, 0xa20a, 0x0014, 0x300b, 0xa20c, 0x0014,
+ 0xa200, 0x8838, 0x817e, 0x842a, 0x84a0, 0x3806, 0x8839, 0x28c2,
+ 0x9cc3, 0xa805, 0x0864, 0xa83b, 0x3008, 0x28c1, 0x9cc3, 0xa201,
+ 0x300c, 0x2847, 0x8161, 0x846a, 0x8000, 0x84a4, 0x1856, 0x883a,
+ 0xa808, 0x28e2, 0x9ca0, 0xa8f3, 0x0864, 0xa829, 0x300c, 0xa801,
+ 0x3008, 0x28e1, 0x9ca0, 0x280d, 0xa204, 0x64c0, 0x67a0, 0x6fc0,
+ 0x1814, 0x883b, 0x7023, 0x8576, 0x8677, 0xa80f, 0x786e, 0x883e,
+ 0xa80c, 0x282b, 0xa205, 0x64a0, 0x67a0, 0x6fc0, 0x1814, 0x883b,
+ 0x7023, 0x8576, 0x8677, 0xa801, 0x883e, 0x2069, 0x28c1, 0x9cc3,
+ 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8dc, 0xa207, 0x0014, 0xa203,
+ 0x8000, 0x84a8, 0x85a4, 0x1872, 0x849a, 0x883c, 0x1fe2, 0xf601,
+ 0xa208, 0x856e, 0x866f, 0x0704, 0x3008, 0x9ca0, 0x0014, 0xa202,
+ 0x8000, 0x85a4, 0x3009, 0x84a8, 0x19e2, 0xf848, 0x8174, 0x86eb,
+ 0x85eb, 0x872e, 0x87a9, 0x883f, 0x08e6, 0xa8f1, 0xf861, 0xa8e8,
+ 0xf801, 0x0014, 0xf881, 0x0016, 0x85b2, 0x80f0, 0x9532, 0xfaa2,
+ 0x1de2, 0x0014, 0x8532, 0xf221, 0x0014, 0x1de2, 0x84a8, 0xd6e0,
+ 0x1fe6, 0x0014, 0xa206, 0x6865, 0x817f, 0x842a, 0x1dc1, 0x8823,
+ 0x0016, 0x6042, 0x8008, 0xa8fa, 0x8000, 0x84a4, 0x8160, 0x842a,
+ 0xf021, 0x3008, 0x84a8, 0x1dc6, 0x20d7, 0x8822, 0x0016, 0x8000,
+ 0x2848, 0x1011, 0xa8fc, 0x3008, 0x8000, 0xa000, 0x2802, 0x1011,
+ 0xa8fd, 0xa887, 0x3008, 0x283d, 0x1011, 0xa8fd, 0xa209, 0x0017,
+ 0x300c, 0x8000, 0x85a4, 0x1de2, 0xdac1, 0x0014, 0x26e0, 0x873a,
+ 0xfaa2, 0x19f2, 0x1fe2, 0x0014, 0xa20b, 0x0014, 0xa20d, 0x817e,
+ 0x842a, 0x84a0, 0x3806, 0x0210, 0x9ccd, 0x0704, 0x0000, 0x127e,
+ 0x2091, 0x2200, 0x2049, 0x2ec7, 0x7000, 0x7204, 0xa205, 0x720c,
+ 0xa215, 0x7008, 0xa084, 0xfffd, 0xa205, 0x0040, 0x2ed9, 0x0078,
+ 0x2ede, 0x7003, 0x0000, 0x127f, 0x2000, 0x007c, 0x7000, 0xa084,
+ 0x0001, 0x00c0, 0x2f0c, 0x7108, 0x8104, 0x00c8, 0x2eeb, 0x1078,
+ 0x2fa8, 0x0078, 0x2ee3, 0x700c, 0xa08c, 0x007f, 0x0040, 0x2f0c,
+ 0x7004, 0x8004, 0x00c8, 0x2f03, 0x7014, 0xa005, 0x00c0, 0x2eff,
+ 0x7010, 0xa005, 0x0040, 0x2f03, 0xa102, 0x00c8, 0x2ee3, 0x7007,
+ 0x0010, 0x0078, 0x2f0c, 0x8aff, 0x0040, 0x2f0c, 0x1078, 0x316b,
+ 0x00c0, 0x2f06, 0x0040, 0x2ee3, 0x1078, 0x2f56, 0x7003, 0x0000,
+ 0x127f, 0x2000, 0x007c, 0x6424, 0x84ff, 0x0040, 0x2f30, 0x2c70,
+ 0x2039, 0x2f35, 0x2704, 0xae68, 0x680c, 0xa630, 0x6808, 0xa529,
+ 0x8421, 0x0040, 0x2f30, 0x8738, 0x2704, 0xa005, 0x00c0, 0x2f1b,
+ 0x7098, 0xa075, 0x0040, 0x2f30, 0x2039, 0x2f32, 0x0078, 0x2f1a,
+ 0x007c, 0x0000, 0x0004, 0x0008, 0x000c, 0x0010, 0x0014, 0x0018,
+ 0x001c, 0x0000, 0x127e, 0x2091, 0x2200, 0x2079, 0x3500, 0x2071,
+ 0x0010, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x2071,
+ 0x0020, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x2049,
+ 0x0000, 0x78b3, 0x0000, 0x127f, 0x2000, 0x007c, 0x2049, 0x2f56,
+ 0x7004, 0x8004, 0x00c8, 0x2f82, 0x7007, 0x0012, 0x7108, 0x7008,
+ 0xa106, 0x00c0, 0x2f5e, 0xa184, 0x0030, 0x0040, 0x2f6b, 0xa086,
+ 0x0030, 0x00c0, 0x2f5e, 0x7000, 0xa084, 0x0001, 0x00c0, 0x2f82,
+ 0x7008, 0xa084, 0x000c, 0x00c0, 0x2f80, 0x710c, 0xa184, 0x0300,
+ 0x00c0, 0x2f80, 0xa184, 0x007f, 0x00c0, 0x2f56, 0x0078, 0x2f82,
+ 0x6817, 0x0003, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004, 0xa084,
+ 0x0008, 0x00c0, 0x2f86, 0x7007, 0x0012, 0x7108, 0x8104, 0x0048,
+ 0x2f8b, 0x78b3, 0x0000, 0x7003, 0x0000, 0x2049, 0x0000, 0x007c,
+ 0x107e, 0x007e, 0x127e, 0x157e, 0x2091, 0x2200, 0x7108, 0x1078,
+ 0x2fa8, 0x157f, 0x127f, 0x2091, 0x8001, 0x007f, 0x107f, 0x007c,
+ 0x7204, 0x2118, 0x7108, 0x700c, 0xa084, 0x0300, 0x00c0, 0x2fea,
+ 0xa184, 0x000c, 0x00c0, 0x2fea, 0x8213, 0x8213, 0x8213, 0x8213,
+ 0xa284, 0x0100, 0xa10d, 0x810b, 0x810b, 0x810f, 0xa184, 0x0007,
+ 0x0079, 0x2fc2, 0x2fcc, 0x2fdc, 0x2fea, 0x2fdc, 0x2ffe, 0x2ffe,
+ 0x2fea, 0x2ffc, 0x1078, 0x1ba5, 0x7007, 0x0002, 0x8aff, 0x00c0,
+ 0x2fd5, 0x2049, 0x0000, 0x0078, 0x2fd9, 0x1078, 0x316b, 0x00c0,
+ 0x2fd5, 0x78b3, 0x0000, 0x007c, 0x7007, 0x0002, 0x8aff, 0x00c0,
+ 0x2fe3, 0x0078, 0x2fe7, 0x1078, 0x316b, 0x00c0, 0x2fe3, 0x78b3,
+ 0x0000, 0x007c, 0x7007, 0x0002, 0x1078, 0x2f56, 0x1078, 0x2cbb,
+ 0x6814, 0xa084, 0x8000, 0x0040, 0x2ff7, 0x6817, 0x0002, 0x007c,
+ 0x1078, 0x1ba5, 0x1078, 0x1ba5, 0x1078, 0x3050, 0x7210, 0x7114,
+ 0x700c, 0xa09c, 0x007f, 0x2800, 0xa300, 0xa211, 0xa189, 0x0000,
+ 0x78b0, 0xa005, 0x0040, 0x3010, 0x78b3, 0x0000, 0x0078, 0x3033,
+ 0x1078, 0x3050, 0x2704, 0x2c58, 0xac60, 0x630c, 0x2200, 0xa322,
+ 0x6308, 0x2100, 0xa31b, 0x2400, 0xa305, 0x0040, 0x3029, 0x00c8,
+ 0x3029, 0x8412, 0x8210, 0x830a, 0xa189, 0x0000, 0x2b60, 0x0078,
+ 0x3010, 0x2b60, 0x8a07, 0xa7ba, 0x2f32, 0xa73d, 0x2c00, 0x6882,
+ 0x6f86, 0x6c8e, 0x6b8a, 0x7007, 0x0012, 0x1078, 0x2f56, 0x007c,
+ 0x8738, 0x2704, 0xa005, 0x00c0, 0x3044, 0x6098, 0xa005, 0x0040,
+ 0x304d, 0x2060, 0x2039, 0x2f32, 0x8a51, 0x0040, 0x304c, 0x7008,
+ 0xa084, 0x00c0, 0xa086, 0x00c0, 0x007c, 0x2051, 0x0000, 0x007c,
+ 0x8a50, 0x8739, 0x2704, 0xa004, 0x00c0, 0x305d, 0x2039, 0x2f38,
+ 0x6000, 0xa064, 0x00c0, 0x305d, 0x2d60, 0x007c, 0x127e, 0x0d7e,
+ 0x2091, 0x2200, 0x0d7f, 0x6880, 0x2060, 0x6884, 0x6b88, 0x6c8c,
+ 0x8057, 0xaad4, 0x00ff, 0xa084, 0x00ff, 0xa0b8, 0x2f32, 0x7e08,
+ 0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040, 0x0040, 0x3079, 0xa6b5,
+ 0x0001, 0x0f7e, 0x2079, 0x0100, 0x7858, 0x0f7f, 0xa084, 0x0040,
+ 0x0040, 0x3088, 0xa684, 0x0001, 0x00c0, 0x3088, 0xa6b5, 0x0001,
+ 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x308a, 0x7000,
+ 0xa005, 0x0040, 0x3095, 0x1078, 0x1ba5, 0x2400, 0xa305, 0x00c0,
+ 0x309b, 0x0078, 0x30d8, 0x2c58, 0x2704, 0xac60, 0x6004, 0xa400,
+ 0x007e, 0x701a, 0x6000, 0xa301, 0x701e, 0x2009, 0x04fd, 0x2104,
+ 0xa086, 0x04fd, 0x007f, 0x00c0, 0x30c8, 0xa084, 0x0001, 0x0040,
+ 0x30c8, 0xa684, 0x0001, 0x00c0, 0x30c8, 0x7013, 0x0001, 0x7017,
+ 0x0000, 0x7602, 0x7007, 0x0001, 0x78b3, 0x0001, 0xa4a0, 0x0001,
+ 0xa399, 0x0000, 0x6004, 0xa400, 0x701a, 0x6000, 0xa301, 0x701e,
+ 0x620c, 0x2400, 0xa202, 0x7012, 0x6208, 0x2300, 0xa203, 0x7016,
+ 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078, 0x3038, 0x0078, 0x30da,
+ 0x1078, 0x316b, 0x00c0, 0x30d8, 0x127f, 0x2000, 0x007c, 0x127e,
+ 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x7007, 0x0004, 0x7004, 0xa084,
+ 0x0004, 0x00c0, 0x30e6, 0x7003, 0x0008, 0x127f, 0x2000, 0x007c,
+ 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049, 0x30f0, 0x7007,
+ 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x30f9, 0x7000, 0xa005,
+ 0x0040, 0x3104, 0x1078, 0x1ba5, 0x7e08, 0xa6b5, 0x000c, 0x6818,
+ 0xa084, 0x0040, 0x0040, 0x310e, 0xa6b5, 0x0001, 0x6824, 0xa005,
+ 0x0040, 0x311a, 0x2050, 0x2039, 0x2f35, 0x2d60, 0x1078, 0x316b,
+ 0x00c0, 0x3116, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e, 0x017e,
+ 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x037f, 0x047f, 0x7e08, 0xa6b5,
+ 0x000c, 0x6818, 0xa084, 0x0040, 0x0040, 0x3130, 0xa6b5, 0x0001,
+ 0x2049, 0x311d, 0x6824, 0xa055, 0x0040, 0x3168, 0x2d70, 0x2e60,
+ 0x2039, 0x2f35, 0x2704, 0xae68, 0x680c, 0xa422, 0x6808, 0xa31b,
+ 0x0048, 0x3155, 0x8a51, 0x00c0, 0x3147, 0x1078, 0x1ba5, 0x8738,
+ 0x2704, 0xa005, 0x00c0, 0x313b, 0x7098, 0xa075, 0x2060, 0x0040,
+ 0x3168, 0x2039, 0x2f32, 0x0078, 0x313a, 0x8422, 0x8420, 0x831a,
+ 0xa399, 0x0000, 0x690c, 0x2400, 0xa122, 0x6908, 0x2300, 0xa11b,
+ 0x00c8, 0x3164, 0x1078, 0x1ba5, 0x2071, 0x0020, 0x0078, 0x3088,
+ 0x127f, 0x2000, 0x007c, 0x7008, 0xa084, 0x00c0, 0xa086, 0x00c0,
+ 0x0040, 0x3193, 0x2704, 0xac08, 0x2104, 0x701e, 0x8108, 0x2104,
+ 0x701a, 0x8108, 0x2104, 0x7016, 0x8108, 0x2104, 0x7012, 0x0f7e,
+ 0x2079, 0x0100, 0x7858, 0x0f7f, 0xa084, 0x0040, 0x0040, 0x318e,
+ 0xa684, 0x0001, 0x00c0, 0x318e, 0xa6b5, 0x0001, 0x7602, 0x7007,
+ 0x0001, 0x1078, 0x3038, 0x007c, 0x127e, 0x007e, 0x0d7e, 0x2091,
+ 0x2200, 0x2049, 0x3194, 0x0d7f, 0x087f, 0x7108, 0xa184, 0x00c0,
+ 0x00c0, 0x31aa, 0x6824, 0xa005, 0x0040, 0x31ba, 0x0078, 0x2ede,
+ 0x0078, 0x31ba, 0x7108, 0x8104, 0x00c8, 0x31b2, 0x1078, 0x2fa8,
+ 0x0078, 0x319d, 0x7007, 0x0010, 0x7108, 0x8104, 0x00c8, 0x31b4,
+ 0x1078, 0x2fa8, 0x7008, 0xa086, 0x0002, 0x00c0, 0x319d, 0x7000,
+ 0xa005, 0x00c0, 0x319d, 0x7003, 0x0000, 0x2049, 0x0000, 0x127f,
+ 0x2000, 0x007c, 0x127e, 0x147e, 0x137e, 0x157e, 0x0d7e, 0x2091,
+ 0x2200, 0x0d7f, 0x2049, 0x31ca, 0xad80, 0x0010, 0x20a0, 0x2099,
+ 0x0031, 0x700c, 0xa084, 0x007f, 0x6826, 0x7007, 0x0008, 0x7007,
+ 0x0002, 0x7003, 0x0001, 0x0040, 0x31e8, 0x8000, 0x80ac, 0x53a5,
+ 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x31ea, 0x2049,
+ 0x0000, 0x7003, 0x0000, 0x157f, 0x137f, 0x147f, 0x127f, 0x2000,
+ 0x007c, 0x127e, 0x007e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049,
+ 0x31f9, 0x6880, 0x2060, 0x6884, 0x6b88, 0x6c8c, 0x8057, 0xaad4,
+ 0x00ff, 0xa084, 0x00ff, 0xa0b8, 0x2f32, 0x7e08, 0xa6b5, 0x0004,
+ 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x3212, 0x2c58,
+ 0x2704, 0xac60, 0x6004, 0xa400, 0x701a, 0x6000, 0xa301, 0x701e,
+ 0x7013, 0x0001, 0x7017, 0x0000, 0x7602, 0x7007, 0x0001, 0x007f,
+ 0x8007, 0x2009, 0x0031, 0x200a, 0x00a0, 0x322c, 0x7108, 0x7007,
+ 0x0002, 0x810c, 0x00c8, 0x322c, 0x810c, 0x0048, 0x3239, 0x0078,
+ 0x2fea, 0xa4a0, 0x0001, 0xa399, 0x0000, 0x6b8a, 0x6c8e, 0x7007,
+ 0x0004, 0x2049, 0x0000, 0x7003, 0x0000, 0x127f, 0x2000, 0x007c,
+ 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, 0x818e, 0x00c8, 0x3251,
+ 0xa200, 0x00f0, 0x324c, 0x8086, 0x818e, 0x007c, 0x157e, 0x20a9,
+ 0x0010, 0xa005, 0x0040, 0x3277, 0xa11a, 0x00c8, 0x3277, 0x8213,
+ 0x818d, 0x0048, 0x326a, 0xa11a, 0x00c8, 0x326b, 0x00f0, 0x325f,
+ 0x0078, 0x326f, 0xa11a, 0x2308, 0x8210, 0x00f0, 0x325f, 0x007e,
+ 0x3200, 0xa084, 0xf7ff, 0x2080, 0x007f, 0x157f, 0x007c, 0x007e,
+ 0x3200, 0xa085, 0x0800, 0x0078, 0x3273, 0x00e0, 0x32bf, 0x2091,
+ 0x6000, 0x7820, 0x8001, 0x7822, 0x00c0, 0x32b9, 0x7824, 0x7822,
+ 0x2091, 0x8000, 0x2069, 0x3540, 0x6800, 0xa084, 0x0007, 0x0040,
+ 0x32a1, 0xa086, 0x0002, 0x0040, 0x32a1, 0x6830, 0xa00d, 0x0040,
+ 0x32a1, 0x2104, 0xa005, 0x0040, 0x32a1, 0x8001, 0x200a, 0x0040,
+ 0x336f, 0x2061, 0x3680, 0x20a9, 0x0080, 0x6034, 0xa005, 0x0040,
+ 0x32b3, 0x8001, 0x6036, 0x00c0, 0x32b3, 0x6010, 0xa005, 0x0040,
+ 0x32b3, 0x1078, 0x1a19, 0xace0, 0x0010, 0x0070, 0x32b9, 0x0078,
+ 0x32a5, 0x1078, 0x32d4, 0x1078, 0x32c2, 0x1078, 0x32f9, 0x2091,
+ 0x8001, 0x007c, 0x783c, 0x8001, 0x783e, 0x00c0, 0x32d3, 0x7840,
+ 0x783e, 0x7848, 0xa005, 0x0040, 0x32d3, 0x8001, 0x784a, 0x00c0,
+ 0x32d3, 0x1078, 0x1a19, 0x007c, 0x7834, 0x8001, 0x7836, 0x00c0,
+ 0x32f8, 0x7838, 0x7836, 0x2091, 0x8000, 0x7844, 0xa005, 0x00c0,
+ 0x32e3, 0x2001, 0x0101, 0x8001, 0x7846, 0xa080, 0x3e80, 0x2040,
+ 0x2004, 0xa065, 0x0040, 0x32f8, 0x6020, 0xa005, 0x0040, 0x32f4,
+ 0x8001, 0x6022, 0x0040, 0x3328, 0x6000, 0x2c40, 0x0078, 0x32e9,
+ 0x007c, 0x7828, 0x8001, 0x782a, 0x00c0, 0x3327, 0x782c, 0x782a,
+ 0x7830, 0xa005, 0x00c0, 0x3306, 0x2001, 0x0080, 0x8001, 0x7832,
+ 0x8003, 0x8003, 0x8003, 0x8003, 0xa090, 0x3680, 0xa298, 0x0002,
+ 0x2304, 0xa084, 0x0008, 0x0040, 0x3327, 0xa290, 0x0009, 0x2204,
+ 0xa005, 0x0040, 0x331f, 0x8001, 0x2012, 0x00c0, 0x3327, 0x2304,
+ 0xa084, 0xfff7, 0xa085, 0x0080, 0x201a, 0x1078, 0x1a19, 0x007c,
+ 0x2069, 0x3540, 0x6800, 0xa005, 0x0040, 0x3332, 0x683c, 0xac06,
+ 0x0040, 0x336f, 0x6017, 0x0006, 0x60b0, 0xa084, 0x3f00, 0x601a,
+ 0x601c, 0xa084, 0x00ff, 0xa085, 0x0060, 0x601e, 0x6000, 0x2042,
+ 0x6710, 0x6fb6, 0x1078, 0x1692, 0x6818, 0xa005, 0x0040, 0x334a,
+ 0x8001, 0x681a, 0x6808, 0xa084, 0xffef, 0x680a, 0x6810, 0x8001,
+ 0x00d0, 0x3354, 0x1078, 0x1ba5, 0x6812, 0x602f, 0x0000, 0x602b,
+ 0x0000, 0x2c68, 0x1078, 0x17dd, 0x2069, 0x3540, 0x2001, 0x0006,
+ 0x68a2, 0x7944, 0xa184, 0x0100, 0x00c0, 0x336a, 0x69ba, 0x2001,
+ 0x0004, 0x68a2, 0x1078, 0x1a14, 0x2091, 0x8001, 0x007c, 0x2009,
+ 0x354f, 0x2164, 0x2069, 0x0100, 0x1078, 0x1b6b, 0x6017, 0x0006,
+ 0x6858, 0xa084, 0x3f00, 0x601a, 0x601c, 0xa084, 0x00ff, 0xa085,
+ 0x0048, 0x601e, 0x602f, 0x0000, 0x602b, 0x0000, 0x6830, 0xa084,
+ 0x0040, 0x0040, 0x33ab, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848,
+ 0xa084, 0x0004, 0x0040, 0x3398, 0x0070, 0x3398, 0x0078, 0x338f,
+ 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040,
+ 0x33a5, 0x0070, 0x33a5, 0x0078, 0x339c, 0x20a9, 0x00fa, 0x0070,
+ 0x33ab, 0x0078, 0x33a7, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b,
+ 0x0046, 0x2009, 0x3568, 0x200b, 0x0007, 0x784c, 0x784a, 0x2091,
+ 0x8001, 0x007c, 0x2079, 0x3500, 0x1078, 0x3403, 0x1078, 0x33cb,
+ 0x1078, 0x33e0, 0x1078, 0x33f5, 0x7833, 0x0000, 0x7847, 0x0000,
+ 0x784b, 0x0000, 0x007c, 0x2019, 0x000a, 0x2011, 0x3546, 0x2204,
+ 0xa086, 0x0032, 0x0040, 0x33dd, 0x2019, 0x000c, 0x2204, 0xa086,
+ 0x003c, 0x0040, 0x33dd, 0x2019, 0x0008, 0x7b2a, 0x7b2e, 0x007c,
+ 0x2019, 0x0030, 0x2011, 0x3546, 0x2204, 0xa086, 0x0032, 0x0040,
+ 0x33f2, 0x2019, 0x0039, 0x2204, 0xa086, 0x003c, 0x0040, 0x33f2,
+ 0x2019, 0x0027, 0x7b36, 0x7b3a, 0x007c, 0x2019, 0x000d, 0x2011,
+ 0x3546, 0x2204, 0xa086, 0x003c, 0x0040, 0x3400, 0x2019, 0x000a,
+ 0x7b3e, 0x7b42, 0x007c, 0x2019, 0x2faf, 0x2011, 0x3546, 0x2204,
+ 0xa086, 0x0032, 0x0040, 0x3415, 0x2019, 0x3971, 0x2204, 0xa086,
+ 0x003c, 0x0040, 0x3415, 0x2019, 0x2626, 0x7b22, 0x7b26, 0x007c,
+ 0x92a7
+};
+unsigned short sbus_risc_code_length01 = 0x2419;
diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c
new file mode 100644
index 000000000000..69009f853a49
--- /dev/null
+++ b/drivers/scsi/sata_nv.c
@@ -0,0 +1,570 @@
+/*
+ * sata_nv.c - NVIDIA nForce SATA
+ *
+ * Copyright 2004 NVIDIA Corp. All rights reserved.
+ * Copyright 2004 Andrew Chew
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ * 0.06
+ * - Added generic SATA support by using a pci_device_id that filters on
+ * the IDE storage class code.
+ *
+ * 0.03
+ * - Fixed a bug where the hotplug handlers for non-CK804/MCP04 were using
+ * mmio_base, which is only set for the CK804/MCP04 case.
+ *
+ * 0.02
+ * - Added support for CK804 SATA controller.
+ *
+ * 0.01
+ * - Initial revision.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "sata_nv"
+#define DRV_VERSION "0.6"
+
+#define NV_PORTS 2
+#define NV_PIO_MASK 0x1f
+#define NV_MWDMA_MASK 0x07
+#define NV_UDMA_MASK 0x7f
+#define NV_PORT0_SCR_REG_OFFSET 0x00
+#define NV_PORT1_SCR_REG_OFFSET 0x40
+
+#define NV_INT_STATUS 0x10
+#define NV_INT_STATUS_CK804 0x440
+#define NV_INT_STATUS_PDEV_INT 0x01
+#define NV_INT_STATUS_PDEV_PM 0x02
+#define NV_INT_STATUS_PDEV_ADDED 0x04
+#define NV_INT_STATUS_PDEV_REMOVED 0x08
+#define NV_INT_STATUS_SDEV_INT 0x10
+#define NV_INT_STATUS_SDEV_PM 0x20
+#define NV_INT_STATUS_SDEV_ADDED 0x40
+#define NV_INT_STATUS_SDEV_REMOVED 0x80
+#define NV_INT_STATUS_PDEV_HOTPLUG (NV_INT_STATUS_PDEV_ADDED | \
+ NV_INT_STATUS_PDEV_REMOVED)
+#define NV_INT_STATUS_SDEV_HOTPLUG (NV_INT_STATUS_SDEV_ADDED | \
+ NV_INT_STATUS_SDEV_REMOVED)
+#define NV_INT_STATUS_HOTPLUG (NV_INT_STATUS_PDEV_HOTPLUG | \
+ NV_INT_STATUS_SDEV_HOTPLUG)
+
+#define NV_INT_ENABLE 0x11
+#define NV_INT_ENABLE_CK804 0x441
+#define NV_INT_ENABLE_PDEV_MASK 0x01
+#define NV_INT_ENABLE_PDEV_PM 0x02
+#define NV_INT_ENABLE_PDEV_ADDED 0x04
+#define NV_INT_ENABLE_PDEV_REMOVED 0x08
+#define NV_INT_ENABLE_SDEV_MASK 0x10
+#define NV_INT_ENABLE_SDEV_PM 0x20
+#define NV_INT_ENABLE_SDEV_ADDED 0x40
+#define NV_INT_ENABLE_SDEV_REMOVED 0x80
+#define NV_INT_ENABLE_PDEV_HOTPLUG (NV_INT_ENABLE_PDEV_ADDED | \
+ NV_INT_ENABLE_PDEV_REMOVED)
+#define NV_INT_ENABLE_SDEV_HOTPLUG (NV_INT_ENABLE_SDEV_ADDED | \
+ NV_INT_ENABLE_SDEV_REMOVED)
+#define NV_INT_ENABLE_HOTPLUG (NV_INT_ENABLE_PDEV_HOTPLUG | \
+ NV_INT_ENABLE_SDEV_HOTPLUG)
+
+#define NV_INT_CONFIG 0x12
+#define NV_INT_CONFIG_METHD 0x01 // 0 = INT, 1 = SMI
+
+// For PCI config register 20
+#define NV_MCP_SATA_CFG_20 0x50
+#define NV_MCP_SATA_CFG_20_SATA_SPACE_EN 0x04
+
+static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static irqreturn_t nv_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs);
+static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static void nv_host_stop (struct ata_host_set *host_set);
+static void nv_enable_hotplug(struct ata_probe_ent *probe_ent);
+static void nv_disable_hotplug(struct ata_host_set *host_set);
+static void nv_check_hotplug(struct ata_host_set *host_set);
+static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent);
+static void nv_disable_hotplug_ck804(struct ata_host_set *host_set);
+static void nv_check_hotplug_ck804(struct ata_host_set *host_set);
+
+enum nv_host_type
+{
+ GENERIC,
+ NFORCE2,
+ NFORCE3,
+ CK804
+};
+
+static struct pci_device_id nv_pci_tbl[] = {
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC },
+ { 0, } /* terminate list */
+};
+
+#define NV_HOST_FLAGS_SCR_MMIO 0x00000001
+
+struct nv_host_desc
+{
+ enum nv_host_type host_type;
+ void (*enable_hotplug)(struct ata_probe_ent *probe_ent);
+ void (*disable_hotplug)(struct ata_host_set *host_set);
+ void (*check_hotplug)(struct ata_host_set *host_set);
+
+};
+static struct nv_host_desc nv_device_tbl[] = {
+ {
+ .host_type = GENERIC,
+ .enable_hotplug = NULL,
+ .disable_hotplug= NULL,
+ .check_hotplug = NULL,
+ },
+ {
+ .host_type = NFORCE2,
+ .enable_hotplug = nv_enable_hotplug,
+ .disable_hotplug= nv_disable_hotplug,
+ .check_hotplug = nv_check_hotplug,
+ },
+ {
+ .host_type = NFORCE3,
+ .enable_hotplug = nv_enable_hotplug,
+ .disable_hotplug= nv_disable_hotplug,
+ .check_hotplug = nv_check_hotplug,
+ },
+ { .host_type = CK804,
+ .enable_hotplug = nv_enable_hotplug_ck804,
+ .disable_hotplug= nv_disable_hotplug_ck804,
+ .check_hotplug = nv_check_hotplug_ck804,
+ },
+};
+
+struct nv_host
+{
+ struct nv_host_desc *host_desc;
+ unsigned long host_flags;
+};
+
+static struct pci_driver nv_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = nv_pci_tbl,
+ .probe = nv_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template nv_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations nv_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .exec_command = ata_exec_command,
+ .check_status = ata_check_status,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = sata_phy_reset,
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+ .eng_timeout = ata_eng_timeout,
+ .irq_handler = nv_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .scr_read = nv_scr_read,
+ .scr_write = nv_scr_write,
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+ .host_stop = nv_host_stop,
+};
+
+/* FIXME: The hardware provides the necessary SATA PHY controls
+ * to support ATA_FLAG_SATA_RESET. However, it is currently
+ * necessary to disable that flag, to solve misdetection problems.
+ * See http://bugme.osdl.org/show_bug.cgi?id=3352 for more info.
+ *
+ * This problem really needs to be investigated further. But in the
+ * meantime, we avoid ATA_FLAG_SATA_RESET to get people working.
+ */
+static struct ata_port_info nv_port_info = {
+ .sht = &nv_sht,
+ .host_flags = ATA_FLAG_SATA |
+ /* ATA_FLAG_SATA_RESET | */
+ ATA_FLAG_SRST |
+ ATA_FLAG_NO_LEGACY,
+ .pio_mask = NV_PIO_MASK,
+ .mwdma_mask = NV_MWDMA_MASK,
+ .udma_mask = NV_UDMA_MASK,
+ .port_ops = &nv_ops,
+};
+
+MODULE_AUTHOR("NVIDIA");
+MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, nv_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static irqreturn_t nv_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ struct nv_host *host = host_set->private_data;
+ unsigned int i;
+ unsigned int handled = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host_set->lock, flags);
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *ap;
+
+ ap = host_set->ports[i];
+ if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN)))
+ handled += ata_host_intr(ap, qc);
+ }
+
+ }
+
+ if (host->host_desc->check_hotplug)
+ host->host_desc->check_hotplug(host_set);
+
+ spin_unlock_irqrestore(&host_set->lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+
+static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+
+ if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
+ return readl((void*)ap->ioaddr.scr_addr + (sc_reg * 4));
+ else
+ return inl(ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
+ if (sc_reg > SCR_CONTROL)
+ return;
+
+ if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
+ writel(val, (void*)ap->ioaddr.scr_addr + (sc_reg * 4));
+ else
+ outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void nv_host_stop (struct ata_host_set *host_set)
+{
+ struct nv_host *host = host_set->private_data;
+
+ // Disable hotplug event interrupts.
+ if (host->host_desc->disable_hotplug)
+ host->host_desc->disable_hotplug(host_set);
+
+ kfree(host);
+}
+
+static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version = 0;
+ struct nv_host *host;
+ struct ata_port_info *ppi;
+ struct ata_probe_ent *probe_ent;
+ int pci_dev_busy = 0;
+ int rc;
+ u32 bar;
+
+ // Make sure this is a SATA controller by counting the number of bars
+ // (NVIDIA SATA controllers will always have six bars). Otherwise,
+ // it's an IDE controller and we ignore it.
+ for (bar=0; bar<6; bar++)
+ if (pci_resource_start(pdev, bar) == 0)
+ return -ENODEV;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ goto err_out;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out_disable;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ rc = -ENOMEM;
+
+ ppi = &nv_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+ if (!probe_ent)
+ goto err_out_regions;
+
+ host = kmalloc(sizeof(struct nv_host), GFP_KERNEL);
+ if (!host)
+ goto err_out_free_ent;
+
+ memset(host, 0, sizeof(struct nv_host));
+ host->host_desc = &nv_device_tbl[ent->driver_data];
+
+ probe_ent->private_data = host;
+
+ if (pci_resource_flags(pdev, 5) & IORESOURCE_MEM)
+ host->host_flags |= NV_HOST_FLAGS_SCR_MMIO;
+
+ if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) {
+ unsigned long base;
+
+ probe_ent->mmio_base = ioremap(pci_resource_start(pdev, 5),
+ pci_resource_len(pdev, 5));
+ if (probe_ent->mmio_base == NULL) {
+ rc = -EIO;
+ goto err_out_free_host;
+ }
+
+ base = (unsigned long)probe_ent->mmio_base;
+
+ probe_ent->port[0].scr_addr =
+ base + NV_PORT0_SCR_REG_OFFSET;
+ probe_ent->port[1].scr_addr =
+ base + NV_PORT1_SCR_REG_OFFSET;
+ } else {
+
+ probe_ent->port[0].scr_addr =
+ pci_resource_start(pdev, 5) | NV_PORT0_SCR_REG_OFFSET;
+ probe_ent->port[1].scr_addr =
+ pci_resource_start(pdev, 5) | NV_PORT1_SCR_REG_OFFSET;
+ }
+
+ pci_set_master(pdev);
+
+ rc = ata_device_add(probe_ent);
+ if (rc != NV_PORTS)
+ goto err_out_iounmap;
+
+ // Enable hotplug event interrupts.
+ if (host->host_desc->enable_hotplug)
+ host->host_desc->enable_hotplug(probe_ent);
+
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_iounmap:
+ if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO)
+ iounmap(probe_ent->mmio_base);
+err_out_free_host:
+ kfree(host);
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out_disable:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+err_out:
+ return rc;
+}
+
+static void nv_enable_hotplug(struct ata_probe_ent *probe_ent)
+{
+ u8 intr_mask;
+
+ outb(NV_INT_STATUS_HOTPLUG,
+ probe_ent->port[0].scr_addr + NV_INT_STATUS);
+
+ intr_mask = inb(probe_ent->port[0].scr_addr + NV_INT_ENABLE);
+ intr_mask |= NV_INT_ENABLE_HOTPLUG;
+
+ outb(intr_mask, probe_ent->port[0].scr_addr + NV_INT_ENABLE);
+}
+
+static void nv_disable_hotplug(struct ata_host_set *host_set)
+{
+ u8 intr_mask;
+
+ intr_mask = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
+
+ intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
+
+ outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE);
+}
+
+static void nv_check_hotplug(struct ata_host_set *host_set)
+{
+ u8 intr_status;
+
+ intr_status = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS);
+
+ // Clear interrupt status.
+ outb(0xff, host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS);
+
+ if (intr_status & NV_INT_STATUS_HOTPLUG) {
+ if (intr_status & NV_INT_STATUS_PDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device added\n");
+
+ if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device removed\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device added\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device removed\n");
+ }
+}
+
+static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent)
+{
+ struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
+ u8 intr_mask;
+ u8 regval;
+
+ pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, &regval);
+ regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
+ pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
+
+ writeb(NV_INT_STATUS_HOTPLUG, probe_ent->mmio_base + NV_INT_STATUS_CK804);
+
+ intr_mask = readb(probe_ent->mmio_base + NV_INT_ENABLE_CK804);
+ intr_mask |= NV_INT_ENABLE_HOTPLUG;
+
+ writeb(intr_mask, probe_ent->mmio_base + NV_INT_ENABLE_CK804);
+}
+
+static void nv_disable_hotplug_ck804(struct ata_host_set *host_set)
+{
+ struct pci_dev *pdev = to_pci_dev(host_set->dev);
+ u8 intr_mask;
+ u8 regval;
+
+ intr_mask = readb(host_set->mmio_base + NV_INT_ENABLE_CK804);
+
+ intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
+
+ writeb(intr_mask, host_set->mmio_base + NV_INT_ENABLE_CK804);
+
+ pci_read_config_byte(pdev, NV_MCP_SATA_CFG_20, &regval);
+ regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
+ pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval);
+}
+
+static void nv_check_hotplug_ck804(struct ata_host_set *host_set)
+{
+ u8 intr_status;
+
+ intr_status = readb(host_set->mmio_base + NV_INT_STATUS_CK804);
+
+ // Clear interrupt status.
+ writeb(0xff, host_set->mmio_base + NV_INT_STATUS_CK804);
+
+ if (intr_status & NV_INT_STATUS_HOTPLUG) {
+ if (intr_status & NV_INT_STATUS_PDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device added\n");
+
+ if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device removed\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device added\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device removed\n");
+ }
+}
+
+static int __init nv_init(void)
+{
+ return pci_module_init(&nv_pci_driver);
+}
+
+static void __exit nv_exit(void)
+{
+ pci_unregister_driver(&nv_pci_driver);
+}
+
+module_init(nv_init);
+module_exit(nv_exit);
diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c
new file mode 100644
index 000000000000..19a13e3590f4
--- /dev/null
+++ b/drivers/scsi/sata_promise.c
@@ -0,0 +1,682 @@
+/*
+ * sata_promise.c - Promise SATA
+ *
+ * Maintained by: Jeff Garzik <jgarzik@pobox.com>
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+#include "sata_promise.h"
+
+#define DRV_NAME "sata_promise"
+#define DRV_VERSION "1.01"
+
+
+enum {
+ PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */
+ PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */
+ PDC_TBG_MODE = 0x41, /* TBG mode */
+ PDC_FLASH_CTL = 0x44, /* Flash control register */
+ PDC_PCI_CTL = 0x48, /* PCI control and status register */
+ PDC_GLOBAL_CTL = 0x48, /* Global control/status (per port) */
+ PDC_CTLSTAT = 0x60, /* IDE control and status (per port) */
+ PDC_SATA_PLUG_CSR = 0x6C, /* SATA Plug control/status reg */
+ PDC_SLEW_CTL = 0x470, /* slew rate control reg */
+
+ PDC_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) |
+ (1<<8) | (1<<9) | (1<<10),
+
+ board_2037x = 0, /* FastTrak S150 TX2plus */
+ board_20319 = 1, /* FastTrak S150 TX4 */
+
+ PDC_HAS_PATA = (1 << 1), /* PDC20375 has PATA */
+
+ PDC_RESET = (1 << 11), /* HDMA reset */
+};
+
+
+struct pdc_port_priv {
+ u8 *pkt;
+ dma_addr_t pkt_dma;
+};
+
+static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
+static void pdc_eng_timeout(struct ata_port *ap);
+static int pdc_port_start(struct ata_port *ap);
+static void pdc_port_stop(struct ata_port *ap);
+static void pdc_phy_reset(struct ata_port *ap);
+static void pdc_qc_prep(struct ata_queued_cmd *qc);
+static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf);
+static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf);
+static void pdc_irq_clear(struct ata_port *ap);
+static int pdc_qc_issue_prot(struct ata_queued_cmd *qc);
+
+static Scsi_Host_Template pdc_ata_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations pdc_ata_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = pdc_tf_load_mmio,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = pdc_exec_command_mmio,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = pdc_phy_reset,
+ .qc_prep = pdc_qc_prep,
+ .qc_issue = pdc_qc_issue_prot,
+ .eng_timeout = pdc_eng_timeout,
+ .irq_handler = pdc_interrupt,
+ .irq_clear = pdc_irq_clear,
+ .scr_read = pdc_sata_scr_read,
+ .scr_write = pdc_sata_scr_write,
+ .port_start = pdc_port_start,
+ .port_stop = pdc_port_stop,
+};
+
+static struct ata_port_info pdc_port_info[] = {
+ /* board_2037x */
+ {
+ .sht = &pdc_ata_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SRST | ATA_FLAG_MMIO,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &pdc_ata_ops,
+ },
+
+ /* board_20319 */
+ {
+ .sht = &pdc_ata_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SRST | ATA_FLAG_MMIO,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &pdc_ata_ops,
+ },
+};
+
+static struct pci_device_id pdc_ata_pci_tbl[] = {
+ { PCI_VENDOR_ID_PROMISE, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2037x },
+ { PCI_VENDOR_ID_PROMISE, 0x3373, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2037x },
+ { PCI_VENDOR_ID_PROMISE, 0x3375, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2037x },
+ { PCI_VENDOR_ID_PROMISE, 0x3376, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2037x },
+ { PCI_VENDOR_ID_PROMISE, 0x3574, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2037x },
+ { PCI_VENDOR_ID_PROMISE, 0x3d75, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2037x },
+
+ { PCI_VENDOR_ID_PROMISE, 0x3318, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_20319 },
+ { PCI_VENDOR_ID_PROMISE, 0x3319, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_20319 },
+ { PCI_VENDOR_ID_PROMISE, 0x3d18, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_20319 },
+
+ { } /* terminate list */
+};
+
+
+static struct pci_driver pdc_ata_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = pdc_ata_pci_tbl,
+ .probe = pdc_ata_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+
+static int pdc_port_start(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct pdc_port_priv *pp;
+ int rc;
+
+ rc = ata_port_start(ap);
+ if (rc)
+ return rc;
+
+ pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ pp->pkt = dma_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL);
+ if (!pp->pkt) {
+ rc = -ENOMEM;
+ goto err_out_kfree;
+ }
+
+ ap->private_data = pp;
+
+ return 0;
+
+err_out_kfree:
+ kfree(pp);
+err_out:
+ ata_port_stop(ap);
+ return rc;
+}
+
+
+static void pdc_port_stop(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct pdc_port_priv *pp = ap->private_data;
+
+ ap->private_data = NULL;
+ dma_free_coherent(dev, 128, pp->pkt, pp->pkt_dma);
+ kfree(pp);
+ ata_port_stop(ap);
+}
+
+
+static void pdc_reset_port(struct ata_port *ap)
+{
+ void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_CTLSTAT;
+ unsigned int i;
+ u32 tmp;
+
+ for (i = 11; i > 0; i--) {
+ tmp = readl(mmio);
+ if (tmp & PDC_RESET)
+ break;
+
+ udelay(100);
+
+ tmp |= PDC_RESET;
+ writel(tmp, mmio);
+ }
+
+ tmp &= ~PDC_RESET;
+ writel(tmp, mmio);
+ readl(mmio); /* flush */
+}
+
+static void pdc_phy_reset(struct ata_port *ap)
+{
+ pdc_reset_port(ap);
+ sata_phy_reset(ap);
+}
+
+static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+ return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg,
+ u32 val)
+{
+ if (sc_reg > SCR_CONTROL)
+ return;
+ writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void pdc_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct pdc_port_priv *pp = qc->ap->private_data;
+ unsigned int i;
+
+ VPRINTK("ENTER\n");
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ ata_qc_prep(qc);
+ /* fall through */
+
+ case ATA_PROT_NODATA:
+ i = pdc_pkt_header(&qc->tf, qc->ap->prd_dma,
+ qc->dev->devno, pp->pkt);
+
+ if (qc->tf.flags & ATA_TFLAG_LBA48)
+ i = pdc_prep_lba48(&qc->tf, pp->pkt, i);
+ else
+ i = pdc_prep_lba28(&qc->tf, pp->pkt, i);
+
+ pdc_pkt_footer(&qc->tf, pp->pkt, i);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void pdc_eng_timeout(struct ata_port *ap)
+{
+ u8 drv_stat;
+ struct ata_queued_cmd *qc;
+
+ DPRINTK("ENTER\n");
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (!qc) {
+ printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+ ap->id);
+ goto out;
+ }
+
+ /* hack alert! We cannot use the supplied completion
+ * function from inside the ->eh_strategy_handler() thread.
+ * libata is the only user of ->eh_strategy_handler() in
+ * any kernel, so the default scsi_done() assumes it is
+ * not being called from the SCSI EH.
+ */
+ qc->scsidone = scsi_finish_command;
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_NODATA:
+ printk(KERN_ERR "ata%u: command timeout\n", ap->id);
+ ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR);
+ break;
+
+ default:
+ drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+
+ printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n",
+ ap->id, qc->tf.command, drv_stat);
+
+ ata_qc_complete(qc, drv_stat);
+ break;
+ }
+
+out:
+ DPRINTK("EXIT\n");
+}
+
+static inline unsigned int pdc_host_intr( struct ata_port *ap,
+ struct ata_queued_cmd *qc)
+{
+ u8 status;
+ unsigned int handled = 0, have_err = 0;
+ u32 tmp;
+ void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL;
+
+ tmp = readl(mmio);
+ if (tmp & PDC_ERR_MASK) {
+ have_err = 1;
+ pdc_reset_port(ap);
+ }
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_NODATA:
+ status = ata_wait_idle(ap);
+ if (have_err)
+ status |= ATA_ERR;
+ ata_qc_complete(qc, status);
+ handled = 1;
+ break;
+
+ default:
+ ap->stats.idle_irq++;
+ break;
+ }
+
+ return handled;
+}
+
+static void pdc_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ void *mmio = host_set->mmio_base;
+
+ readl(mmio + PDC_INT_SEQMASK);
+}
+
+static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ struct ata_port *ap;
+ u32 mask = 0;
+ unsigned int i, tmp;
+ unsigned int handled = 0;
+ void *mmio_base;
+
+ VPRINTK("ENTER\n");
+
+ if (!host_set || !host_set->mmio_base) {
+ VPRINTK("QUICK EXIT\n");
+ return IRQ_NONE;
+ }
+
+ mmio_base = host_set->mmio_base;
+
+ /* reading should also clear interrupts */
+ mask = readl(mmio_base + PDC_INT_SEQMASK);
+
+ if (mask == 0xffffffff) {
+ VPRINTK("QUICK EXIT 2\n");
+ return IRQ_NONE;
+ }
+ mask &= 0xffff; /* only 16 tags possible */
+ if (!mask) {
+ VPRINTK("QUICK EXIT 3\n");
+ return IRQ_NONE;
+ }
+
+ spin_lock(&host_set->lock);
+
+ writel(mask, mmio_base + PDC_INT_SEQMASK);
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ VPRINTK("port %u\n", i);
+ ap = host_set->ports[i];
+ tmp = mask & (1 << (i + 1));
+ if (tmp && ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN)))
+ handled += pdc_host_intr(ap, qc);
+ }
+ }
+
+ spin_unlock(&host_set->lock);
+
+ VPRINTK("EXIT\n");
+
+ return IRQ_RETVAL(handled);
+}
+
+static inline void pdc_packet_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct pdc_port_priv *pp = ap->private_data;
+ unsigned int port_no = ap->port_no;
+ u8 seq = (u8) (port_no + 1);
+
+ VPRINTK("ENTER, ap %p\n", ap);
+
+ writel(0x00000001, ap->host_set->mmio_base + (seq * 4));
+ readl(ap->host_set->mmio_base + (seq * 4)); /* flush */
+
+ pp->pkt[2] = seq;
+ wmb(); /* flush PRD, pkt writes */
+ writel(pp->pkt_dma, (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
+ readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); /* flush */
+}
+
+static int pdc_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_NODATA:
+ pdc_packet_start(qc);
+ return 0;
+
+ case ATA_PROT_ATAPI_DMA:
+ BUG();
+ break;
+
+ default:
+ break;
+ }
+
+ return ata_qc_issue_prot(qc);
+}
+
+static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ WARN_ON (tf->protocol == ATA_PROT_DMA ||
+ tf->protocol == ATA_PROT_NODATA);
+ ata_tf_load(ap, tf);
+}
+
+
+static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ WARN_ON (tf->protocol == ATA_PROT_DMA ||
+ tf->protocol == ATA_PROT_NODATA);
+ ata_exec_command(ap, tf);
+}
+
+
+static void pdc_ata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+ port->cmd_addr = base;
+ port->data_addr = base;
+ port->feature_addr =
+ port->error_addr = base + 0x4;
+ port->nsect_addr = base + 0x8;
+ port->lbal_addr = base + 0xc;
+ port->lbam_addr = base + 0x10;
+ port->lbah_addr = base + 0x14;
+ port->device_addr = base + 0x18;
+ port->command_addr =
+ port->status_addr = base + 0x1c;
+ port->altstatus_addr =
+ port->ctl_addr = base + 0x38;
+}
+
+
+static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+{
+ void *mmio = pe->mmio_base;
+ u32 tmp;
+
+ /*
+ * Except for the hotplug stuff, this is voodoo from the
+ * Promise driver. Label this entire section
+ * "TODO: figure out why we do this"
+ */
+
+ /* change FIFO_SHD to 8 dwords, enable BMR_BURST */
+ tmp = readl(mmio + PDC_FLASH_CTL);
+ tmp |= 0x12000; /* bit 16 (fifo 8 dw) and 13 (bmr burst?) */
+ writel(tmp, mmio + PDC_FLASH_CTL);
+
+ /* clear plug/unplug flags for all ports */
+ tmp = readl(mmio + PDC_SATA_PLUG_CSR);
+ writel(tmp | 0xff, mmio + PDC_SATA_PLUG_CSR);
+
+ /* mask plug/unplug ints */
+ tmp = readl(mmio + PDC_SATA_PLUG_CSR);
+ writel(tmp | 0xff0000, mmio + PDC_SATA_PLUG_CSR);
+
+ /* reduce TBG clock to 133 Mhz. */
+ tmp = readl(mmio + PDC_TBG_MODE);
+ tmp &= ~0x30000; /* clear bit 17, 16*/
+ tmp |= 0x10000; /* set bit 17:16 = 0:1 */
+ writel(tmp, mmio + PDC_TBG_MODE);
+
+ readl(mmio + PDC_TBG_MODE); /* flush */
+ msleep(10);
+
+ /* adjust slew rate control register. */
+ tmp = readl(mmio + PDC_SLEW_CTL);
+ tmp &= 0xFFFFF03F; /* clear bit 11 ~ 6 */
+ tmp |= 0x00000900; /* set bit 11-9 = 100b , bit 8-6 = 100 */
+ writel(tmp, mmio + PDC_SLEW_CTL);
+}
+
+static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ unsigned long base;
+ void *mmio_base;
+ unsigned int board_idx = (unsigned int) ent->driver_data;
+ int pci_dev_busy = 0;
+ int rc;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ /*
+ * If this driver happens to only be useful on Apple's K2, then
+ * we should check that here as it has a normal Serverworks ID
+ */
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ mmio_base = ioremap(pci_resource_start(pdev, 3),
+ pci_resource_len(pdev, 3));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+ base = (unsigned long) mmio_base;
+
+ probe_ent->sht = pdc_port_info[board_idx].sht;
+ probe_ent->host_flags = pdc_port_info[board_idx].host_flags;
+ probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask;
+ probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask;
+ probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask;
+ probe_ent->port_ops = pdc_port_info[board_idx].port_ops;
+
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+
+ pdc_ata_setup_port(&probe_ent->port[0], base + 0x200);
+ pdc_ata_setup_port(&probe_ent->port[1], base + 0x280);
+
+ probe_ent->port[0].scr_addr = base + 0x400;
+ probe_ent->port[1].scr_addr = base + 0x500;
+
+ /* notice 4-port boards */
+ switch (board_idx) {
+ case board_20319:
+ probe_ent->n_ports = 4;
+
+ pdc_ata_setup_port(&probe_ent->port[2], base + 0x300);
+ pdc_ata_setup_port(&probe_ent->port[3], base + 0x380);
+
+ probe_ent->port[2].scr_addr = base + 0x600;
+ probe_ent->port[3].scr_addr = base + 0x700;
+ break;
+ case board_2037x:
+ probe_ent->n_ports = 2;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ pci_set_master(pdev);
+
+ /* initialize adapter */
+ pdc_host_init(board_idx, probe_ent);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+
+static int __init pdc_ata_init(void)
+{
+ return pci_module_init(&pdc_ata_pci_driver);
+}
+
+
+static void __exit pdc_ata_exit(void)
+{
+ pci_unregister_driver(&pdc_ata_pci_driver);
+}
+
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("Promise SATA TX2/TX4 low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, pdc_ata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(pdc_ata_init);
+module_exit(pdc_ata_exit);
diff --git a/drivers/scsi/sata_promise.h b/drivers/scsi/sata_promise.h
new file mode 100644
index 000000000000..6e7e96b9ee13
--- /dev/null
+++ b/drivers/scsi/sata_promise.h
@@ -0,0 +1,154 @@
+/*
+ * sata_promise.h - Promise SATA common definitions and inline funcs
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#ifndef __SATA_PROMISE_H__
+#define __SATA_PROMISE_H__
+
+#include <linux/ata.h>
+
+enum pdc_packet_bits {
+ PDC_PKT_READ = (1 << 2),
+ PDC_PKT_NODATA = (1 << 3),
+
+ PDC_PKT_SIZEMASK = (1 << 7) | (1 << 6) | (1 << 5),
+ PDC_PKT_CLEAR_BSY = (1 << 4),
+ PDC_PKT_WAIT_DRDY = (1 << 3) | (1 << 4),
+ PDC_LAST_REG = (1 << 3),
+
+ PDC_REG_DEVCTL = (1 << 3) | (1 << 2) | (1 << 1),
+};
+
+static inline unsigned int pdc_pkt_header(struct ata_taskfile *tf,
+ dma_addr_t sg_table,
+ unsigned int devno, u8 *buf)
+{
+ u8 dev_reg;
+ u32 *buf32 = (u32 *) buf;
+
+ /* set control bits (byte 0), zero delay seq id (byte 3),
+ * and seq id (byte 2)
+ */
+ switch (tf->protocol) {
+ case ATA_PROT_DMA:
+ if (!(tf->flags & ATA_TFLAG_WRITE))
+ buf32[0] = cpu_to_le32(PDC_PKT_READ);
+ else
+ buf32[0] = 0;
+ break;
+
+ case ATA_PROT_NODATA:
+ buf32[0] = cpu_to_le32(PDC_PKT_NODATA);
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ buf32[1] = cpu_to_le32(sg_table); /* S/G table addr */
+ buf32[2] = 0; /* no next-packet */
+
+ if (devno == 0)
+ dev_reg = ATA_DEVICE_OBS;
+ else
+ dev_reg = ATA_DEVICE_OBS | ATA_DEV1;
+
+ /* select device */
+ buf[12] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE;
+ buf[13] = dev_reg;
+
+ /* device control register */
+ buf[14] = (1 << 5) | PDC_REG_DEVCTL;
+ buf[15] = tf->ctl;
+
+ return 16; /* offset of next byte */
+}
+
+static inline unsigned int pdc_pkt_footer(struct ata_taskfile *tf, u8 *buf,
+ unsigned int i)
+{
+ if (tf->flags & ATA_TFLAG_DEVICE) {
+ buf[i++] = (1 << 5) | ATA_REG_DEVICE;
+ buf[i++] = tf->device;
+ }
+
+ /* and finally the command itself; also includes end-of-pkt marker */
+ buf[i++] = (1 << 5) | PDC_LAST_REG | ATA_REG_CMD;
+ buf[i++] = tf->command;
+
+ return i;
+}
+
+static inline unsigned int pdc_prep_lba28(struct ata_taskfile *tf, u8 *buf, unsigned int i)
+{
+ /* the "(1 << 5)" should be read "(count << 5)" */
+
+ /* ATA command block registers */
+ buf[i++] = (1 << 5) | ATA_REG_FEATURE;
+ buf[i++] = tf->feature;
+
+ buf[i++] = (1 << 5) | ATA_REG_NSECT;
+ buf[i++] = tf->nsect;
+
+ buf[i++] = (1 << 5) | ATA_REG_LBAL;
+ buf[i++] = tf->lbal;
+
+ buf[i++] = (1 << 5) | ATA_REG_LBAM;
+ buf[i++] = tf->lbam;
+
+ buf[i++] = (1 << 5) | ATA_REG_LBAH;
+ buf[i++] = tf->lbah;
+
+ return i;
+}
+
+static inline unsigned int pdc_prep_lba48(struct ata_taskfile *tf, u8 *buf, unsigned int i)
+{
+ /* the "(2 << 5)" should be read "(count << 5)" */
+
+ /* ATA command block registers */
+ buf[i++] = (2 << 5) | ATA_REG_FEATURE;
+ buf[i++] = tf->hob_feature;
+ buf[i++] = tf->feature;
+
+ buf[i++] = (2 << 5) | ATA_REG_NSECT;
+ buf[i++] = tf->hob_nsect;
+ buf[i++] = tf->nsect;
+
+ buf[i++] = (2 << 5) | ATA_REG_LBAL;
+ buf[i++] = tf->hob_lbal;
+ buf[i++] = tf->lbal;
+
+ buf[i++] = (2 << 5) | ATA_REG_LBAM;
+ buf[i++] = tf->hob_lbam;
+ buf[i++] = tf->lbam;
+
+ buf[i++] = (2 << 5) | ATA_REG_LBAH;
+ buf[i++] = tf->hob_lbah;
+ buf[i++] = tf->lbah;
+
+ return i;
+}
+
+
+#endif /* __SATA_PROMISE_H__ */
diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c
new file mode 100644
index 000000000000..dfd362104717
--- /dev/null
+++ b/drivers/scsi/sata_qstor.c
@@ -0,0 +1,718 @@
+/*
+ * sata_qstor.c - Pacific Digital Corporation QStor SATA
+ *
+ * Maintained by: Mark Lord <mlord@pobox.com>
+ *
+ * Copyright 2005 Pacific Digital Corporation.
+ * (OSL/GPL code release authorized by Jalil Fadavi).
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <asm/io.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "sata_qstor"
+#define DRV_VERSION "0.04"
+
+enum {
+ QS_PORTS = 4,
+ QS_MAX_PRD = LIBATA_MAX_PRD,
+ QS_CPB_ORDER = 6,
+ QS_CPB_BYTES = (1 << QS_CPB_ORDER),
+ QS_PRD_BYTES = QS_MAX_PRD * 16,
+ QS_PKT_BYTES = QS_CPB_BYTES + QS_PRD_BYTES,
+
+ QS_DMA_BOUNDARY = ~0UL,
+
+ /* global register offsets */
+ QS_HCF_CNFG3 = 0x0003, /* host configuration offset */
+ QS_HID_HPHY = 0x0004, /* host physical interface info */
+ QS_HCT_CTRL = 0x00e4, /* global interrupt mask offset */
+ QS_HST_SFF = 0x0100, /* host status fifo offset */
+ QS_HVS_SERD3 = 0x0393, /* PHY enable offset */
+
+ /* global control bits */
+ QS_HPHY_64BIT = (1 << 1), /* 64-bit bus detected */
+ QS_CNFG3_GSRST = 0x01, /* global chip reset */
+ QS_SERD3_PHY_ENA = 0xf0, /* PHY detection ENAble*/
+
+ /* per-channel register offsets */
+ QS_CCF_CPBA = 0x0710, /* chan CPB base address */
+ QS_CCF_CSEP = 0x0718, /* chan CPB separation factor */
+ QS_CFC_HUFT = 0x0800, /* host upstream fifo threshold */
+ QS_CFC_HDFT = 0x0804, /* host downstream fifo threshold */
+ QS_CFC_DUFT = 0x0808, /* dev upstream fifo threshold */
+ QS_CFC_DDFT = 0x080c, /* dev downstream fifo threshold */
+ QS_CCT_CTR0 = 0x0900, /* chan control-0 offset */
+ QS_CCT_CTR1 = 0x0901, /* chan control-1 offset */
+ QS_CCT_CFF = 0x0a00, /* chan command fifo offset */
+
+ /* channel control bits */
+ QS_CTR0_REG = (1 << 1), /* register mode (vs. pkt mode) */
+ QS_CTR0_CLER = (1 << 2), /* clear channel errors */
+ QS_CTR1_RDEV = (1 << 1), /* sata phy/comms reset */
+ QS_CTR1_RCHN = (1 << 4), /* reset channel logic */
+ QS_CCF_RUN_PKT = 0x107, /* RUN a new dma PKT */
+
+ /* pkt sub-field headers */
+ QS_HCB_HDR = 0x01, /* Host Control Block header */
+ QS_DCB_HDR = 0x02, /* Device Control Block header */
+
+ /* pkt HCB flag bits */
+ QS_HF_DIRO = (1 << 0), /* data DIRection Out */
+ QS_HF_DAT = (1 << 3), /* DATa pkt */
+ QS_HF_IEN = (1 << 4), /* Interrupt ENable */
+ QS_HF_VLD = (1 << 5), /* VaLiD pkt */
+
+ /* pkt DCB flag bits */
+ QS_DF_PORD = (1 << 2), /* Pio OR Dma */
+ QS_DF_ELBA = (1 << 3), /* Extended LBA (lba48) */
+
+ /* PCI device IDs */
+ board_2068_idx = 0, /* QStor 4-port SATA/RAID */
+};
+
+typedef enum { qs_state_idle, qs_state_pkt, qs_state_mmio } qs_state_t;
+
+struct qs_port_priv {
+ u8 *pkt;
+ dma_addr_t pkt_dma;
+ qs_state_t state;
+};
+
+static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static irqreturn_t qs_intr (int irq, void *dev_instance, struct pt_regs *regs);
+static int qs_port_start(struct ata_port *ap);
+static void qs_host_stop(struct ata_host_set *host_set);
+static void qs_port_stop(struct ata_port *ap);
+static void qs_phy_reset(struct ata_port *ap);
+static void qs_qc_prep(struct ata_queued_cmd *qc);
+static int qs_qc_issue(struct ata_queued_cmd *qc);
+static int qs_check_atapi_dma(struct ata_queued_cmd *qc);
+static void qs_bmdma_stop(struct ata_port *ap);
+static u8 qs_bmdma_status(struct ata_port *ap);
+static void qs_irq_clear(struct ata_port *ap);
+static void qs_eng_timeout(struct ata_port *ap);
+
+static Scsi_Host_Template qs_ata_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = QS_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ //FIXME .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .use_clustering = ENABLE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = QS_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+};
+
+static struct ata_port_operations qs_ata_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .check_atapi_dma = qs_check_atapi_dma,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = qs_phy_reset,
+ .qc_prep = qs_qc_prep,
+ .qc_issue = qs_qc_issue,
+ .eng_timeout = qs_eng_timeout,
+ .irq_handler = qs_intr,
+ .irq_clear = qs_irq_clear,
+ .scr_read = qs_scr_read,
+ .scr_write = qs_scr_write,
+ .port_start = qs_port_start,
+ .port_stop = qs_port_stop,
+ .host_stop = qs_host_stop,
+ .bmdma_stop = qs_bmdma_stop,
+ .bmdma_status = qs_bmdma_status,
+};
+
+static struct ata_port_info qs_port_info[] = {
+ /* board_2068_idx */
+ {
+ .sht = &qs_ata_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SATA_RESET |
+ //FIXME ATA_FLAG_SRST |
+ ATA_FLAG_MMIO,
+ .pio_mask = 0x10, /* pio4 */
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &qs_ata_ops,
+ },
+};
+
+static struct pci_device_id qs_ata_pci_tbl[] = {
+ { PCI_VENDOR_ID_PDC, 0x2068, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_2068_idx },
+
+ { } /* terminate list */
+};
+
+static struct pci_driver qs_ata_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = qs_ata_pci_tbl,
+ .probe = qs_ata_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static int qs_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ return 1; /* ATAPI DMA not supported */
+}
+
+static void qs_bmdma_stop(struct ata_port *ap)
+{
+ /* nothing */
+}
+
+static u8 qs_bmdma_status(struct ata_port *ap)
+{
+ return 0;
+}
+
+static void qs_irq_clear(struct ata_port *ap)
+{
+ /* nothing */
+}
+
+static inline void qs_enter_reg_mode(struct ata_port *ap)
+{
+ u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+ writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
+ readb(chan + QS_CCT_CTR0); /* flush */
+}
+
+static inline void qs_reset_channel_logic(struct ata_port *ap)
+{
+ u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+ writeb(QS_CTR1_RCHN, chan + QS_CCT_CTR1);
+ readb(chan + QS_CCT_CTR0); /* flush */
+ qs_enter_reg_mode(ap);
+}
+
+static void qs_phy_reset(struct ata_port *ap)
+{
+ struct qs_port_priv *pp = ap->private_data;
+
+ pp->state = qs_state_idle;
+ qs_reset_channel_logic(ap);
+ sata_phy_reset(ap);
+}
+
+static void qs_eng_timeout(struct ata_port *ap)
+{
+ struct qs_port_priv *pp = ap->private_data;
+
+ if (pp->state != qs_state_idle) /* healthy paranoia */
+ pp->state = qs_state_mmio;
+ qs_reset_channel_logic(ap);
+ ata_eng_timeout(ap);
+}
+
+static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return ~0U;
+ return readl((void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8)));
+}
+
+static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+ if (sc_reg > SCR_CONTROL)
+ return;
+ writel(val, (void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8)));
+}
+
+static void qs_fill_sg(struct ata_queued_cmd *qc)
+{
+ struct scatterlist *sg = qc->sg;
+ struct ata_port *ap = qc->ap;
+ struct qs_port_priv *pp = ap->private_data;
+ unsigned int nelem;
+ u8 *prd = pp->pkt + QS_CPB_BYTES;
+
+ assert(sg != NULL);
+ assert(qc->n_elem > 0);
+
+ for (nelem = 0; nelem < qc->n_elem; nelem++,sg++) {
+ u64 addr;
+ u32 len;
+
+ addr = sg_dma_address(sg);
+ *(__le64 *)prd = cpu_to_le64(addr);
+ prd += sizeof(u64);
+
+ len = sg_dma_len(sg);
+ *(__le32 *)prd = cpu_to_le32(len);
+ prd += sizeof(u64);
+
+ VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", nelem,
+ (unsigned long long)addr, len);
+ }
+}
+
+static void qs_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct qs_port_priv *pp = qc->ap->private_data;
+ u8 dflags = QS_DF_PORD, *buf = pp->pkt;
+ u8 hflags = QS_HF_DAT | QS_HF_IEN | QS_HF_VLD;
+ u64 addr;
+
+ VPRINTK("ENTER\n");
+
+ qs_enter_reg_mode(qc->ap);
+ if (qc->tf.protocol != ATA_PROT_DMA) {
+ ata_qc_prep(qc);
+ return;
+ }
+
+ qs_fill_sg(qc);
+
+ if ((qc->tf.flags & ATA_TFLAG_WRITE))
+ hflags |= QS_HF_DIRO;
+ if ((qc->tf.flags & ATA_TFLAG_LBA48))
+ dflags |= QS_DF_ELBA;
+
+ /* host control block (HCB) */
+ buf[ 0] = QS_HCB_HDR;
+ buf[ 1] = hflags;
+ *(__le32 *)(&buf[ 4]) = cpu_to_le32(qc->nsect * ATA_SECT_SIZE);
+ *(__le32 *)(&buf[ 8]) = cpu_to_le32(qc->n_elem);
+ addr = ((u64)pp->pkt_dma) + QS_CPB_BYTES;
+ *(__le64 *)(&buf[16]) = cpu_to_le64(addr);
+
+ /* device control block (DCB) */
+ buf[24] = QS_DCB_HDR;
+ buf[28] = dflags;
+
+ /* frame information structure (FIS) */
+ ata_tf_to_fis(&qc->tf, &buf[32], 0);
+}
+
+static inline void qs_packet_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+ VPRINTK("ENTER, ap %p\n", ap);
+
+ writeb(QS_CTR0_CLER, chan + QS_CCT_CTR0);
+ wmb(); /* flush PRDs and pkt to memory */
+ writel(QS_CCF_RUN_PKT, chan + QS_CCT_CFF);
+ readl(chan + QS_CCT_CFF); /* flush */
+}
+
+static int qs_qc_issue(struct ata_queued_cmd *qc)
+{
+ struct qs_port_priv *pp = qc->ap->private_data;
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+
+ pp->state = qs_state_pkt;
+ qs_packet_start(qc);
+ return 0;
+
+ case ATA_PROT_ATAPI_DMA:
+ BUG();
+ break;
+
+ default:
+ break;
+ }
+
+ pp->state = qs_state_mmio;
+ return ata_qc_issue_prot(qc);
+}
+
+static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set)
+{
+ unsigned int handled = 0;
+ u8 sFFE;
+ u8 __iomem *mmio_base = host_set->mmio_base;
+
+ do {
+ u32 sff0 = readl(mmio_base + QS_HST_SFF);
+ u32 sff1 = readl(mmio_base + QS_HST_SFF + 4);
+ u8 sEVLD = (sff1 >> 30) & 0x01; /* valid flag */
+ sFFE = sff1 >> 31; /* empty flag */
+
+ if (sEVLD) {
+ u8 sDST = sff0 >> 16; /* dev status */
+ u8 sHST = sff1 & 0x3f; /* host status */
+ unsigned int port_no = (sff1 >> 8) & 0x03;
+ struct ata_port *ap = host_set->ports[port_no];
+
+ DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n",
+ sff1, sff0, port_no, sHST, sDST);
+ handled = 1;
+ if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+ struct qs_port_priv *pp = ap->private_data;
+ if (!pp || pp->state != qs_state_pkt)
+ continue;
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+ switch (sHST) {
+ case 0: /* sucessful CPB */
+ case 3: /* device error */
+ pp->state = qs_state_idle;
+ qs_enter_reg_mode(qc->ap);
+ ata_qc_complete(qc, sDST);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ } while (!sFFE);
+ return handled;
+}
+
+static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set)
+{
+ unsigned int handled = 0, port_no;
+
+ for (port_no = 0; port_no < host_set->n_ports; ++port_no) {
+ struct ata_port *ap;
+ ap = host_set->ports[port_no];
+ if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+ struct qs_port_priv *pp = ap->private_data;
+ if (!pp || pp->state != qs_state_mmio)
+ continue;
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+
+ /* check main status, clearing INTRQ */
+ u8 status = ata_chk_status(ap);
+ if ((status & ATA_BUSY))
+ continue;
+ DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
+ ap->id, qc->tf.protocol, status);
+
+ /* complete taskfile transaction */
+ pp->state = qs_state_idle;
+ ata_qc_complete(qc, status);
+ handled = 1;
+ }
+ }
+ }
+ return handled;
+}
+
+static irqreturn_t qs_intr(int irq, void *dev_instance, struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ unsigned int handled = 0;
+
+ VPRINTK("ENTER\n");
+
+ spin_lock(&host_set->lock);
+ handled = qs_intr_pkt(host_set) | qs_intr_mmio(host_set);
+ spin_unlock(&host_set->lock);
+
+ VPRINTK("EXIT\n");
+
+ return IRQ_RETVAL(handled);
+}
+
+static void qs_ata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+ port->cmd_addr =
+ port->data_addr = base + 0x400;
+ port->error_addr =
+ port->feature_addr = base + 0x408; /* hob_feature = 0x409 */
+ port->nsect_addr = base + 0x410; /* hob_nsect = 0x411 */
+ port->lbal_addr = base + 0x418; /* hob_lbal = 0x419 */
+ port->lbam_addr = base + 0x420; /* hob_lbam = 0x421 */
+ port->lbah_addr = base + 0x428; /* hob_lbah = 0x429 */
+ port->device_addr = base + 0x430;
+ port->status_addr =
+ port->command_addr = base + 0x438;
+ port->altstatus_addr =
+ port->ctl_addr = base + 0x440;
+ port->scr_addr = base + 0xc00;
+}
+
+static int qs_port_start(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct qs_port_priv *pp;
+ void __iomem *mmio_base = ap->host_set->mmio_base;
+ void __iomem *chan = mmio_base + (ap->port_no * 0x4000);
+ u64 addr;
+ int rc;
+
+ rc = ata_port_start(ap);
+ if (rc)
+ return rc;
+ qs_enter_reg_mode(ap);
+ pp = kcalloc(1, sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ pp->pkt = dma_alloc_coherent(dev, QS_PKT_BYTES, &pp->pkt_dma,
+ GFP_KERNEL);
+ if (!pp->pkt) {
+ rc = -ENOMEM;
+ goto err_out_kfree;
+ }
+ memset(pp->pkt, 0, QS_PKT_BYTES);
+ ap->private_data = pp;
+
+ addr = (u64)pp->pkt_dma;
+ writel((u32) addr, chan + QS_CCF_CPBA);
+ writel((u32)(addr >> 32), chan + QS_CCF_CPBA + 4);
+ return 0;
+
+err_out_kfree:
+ kfree(pp);
+err_out:
+ ata_port_stop(ap);
+ return rc;
+}
+
+static void qs_port_stop(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct qs_port_priv *pp = ap->private_data;
+
+ if (pp != NULL) {
+ ap->private_data = NULL;
+ if (pp->pkt != NULL)
+ dma_free_coherent(dev, QS_PKT_BYTES, pp->pkt,
+ pp->pkt_dma);
+ kfree(pp);
+ }
+ ata_port_stop(ap);
+}
+
+static void qs_host_stop(struct ata_host_set *host_set)
+{
+ void __iomem *mmio_base = host_set->mmio_base;
+
+ writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
+ writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
+}
+
+static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+{
+ void __iomem *mmio_base = pe->mmio_base;
+ unsigned int port_no;
+
+ writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
+ writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
+
+ /* reset each channel in turn */
+ for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+ u8 __iomem *chan = mmio_base + (port_no * 0x4000);
+ writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1);
+ writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
+ readb(chan + QS_CCT_CTR0); /* flush */
+ }
+ writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */
+
+ for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+ u8 __iomem *chan = mmio_base + (port_no * 0x4000);
+ /* set FIFO depths to same settings as Windows driver */
+ writew(32, chan + QS_CFC_HUFT);
+ writew(32, chan + QS_CFC_HDFT);
+ writew(10, chan + QS_CFC_DUFT);
+ writew( 8, chan + QS_CFC_DDFT);
+ /* set CPB size in bytes, as a power of two */
+ writeb(QS_CPB_ORDER, chan + QS_CCF_CSEP);
+ }
+ writeb(1, mmio_base + QS_HCT_CTRL); /* enable host interrupts */
+}
+
+/*
+ * The QStor understands 64-bit buses, and uses 64-bit fields
+ * for DMA pointers regardless of bus width. We just have to
+ * make sure our DMA masks are set appropriately for whatever
+ * bridge lies between us and the QStor, and then the DMA mapping
+ * code will ensure we only ever "see" appropriate buffer addresses.
+ * If we're 32-bit limited somewhere, then our 64-bit fields will
+ * just end up with zeros in the upper 32-bits, without any special
+ * logic required outside of this routine (below).
+ */
+static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
+{
+ u32 bus_info = readl(mmio_base + QS_HID_HPHY);
+ int rc, have_64bit_bus = (bus_info & QS_HPHY_64BIT);
+
+ if (have_64bit_bus &&
+ !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+ if (rc) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME
+ "(%s): 64-bit DMA enable failed\n",
+ pci_name(pdev));
+ return rc;
+ }
+ }
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME
+ "(%s): 32-bit DMA enable failed\n",
+ pci_name(pdev));
+ return rc;
+ }
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME
+ "(%s): 32-bit consistent DMA enable failed\n",
+ pci_name(pdev));
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int qs_ata_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ void __iomem *mmio_base;
+ unsigned int board_idx = (unsigned int) ent->driver_data;
+ int rc, port_no;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc)
+ goto err_out;
+
+ if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) {
+ rc = -ENODEV;
+ goto err_out_regions;
+ }
+
+ mmio_base = ioremap(pci_resource_start(pdev, 4),
+ pci_resource_len(pdev, 4));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ rc = qs_set_dma_masks(pdev, mmio_base);
+ if (rc)
+ goto err_out_iounmap;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_iounmap;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ probe_ent->sht = qs_port_info[board_idx].sht;
+ probe_ent->host_flags = qs_port_info[board_idx].host_flags;
+ probe_ent->pio_mask = qs_port_info[board_idx].pio_mask;
+ probe_ent->mwdma_mask = qs_port_info[board_idx].mwdma_mask;
+ probe_ent->udma_mask = qs_port_info[board_idx].udma_mask;
+ probe_ent->port_ops = qs_port_info[board_idx].port_ops;
+
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+ probe_ent->n_ports = QS_PORTS;
+
+ for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
+ unsigned long chan = (unsigned long)mmio_base +
+ (port_no * 0x4000);
+ qs_ata_setup_port(&probe_ent->port[port_no], chan);
+ }
+
+ pci_set_master(pdev);
+
+ /* initialize adapter */
+ qs_host_init(board_idx, probe_ent);
+
+ rc = ata_device_add(probe_ent);
+ kfree(probe_ent);
+ if (rc != QS_PORTS)
+ goto err_out_iounmap;
+ return 0;
+
+err_out_iounmap:
+ iounmap(mmio_base);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ pci_disable_device(pdev);
+ return rc;
+}
+
+static int __init qs_ata_init(void)
+{
+ return pci_module_init(&qs_ata_pci_driver);
+}
+
+static void __exit qs_ata_exit(void)
+{
+ pci_unregister_driver(&qs_ata_pci_driver);
+}
+
+MODULE_AUTHOR("Mark Lord");
+MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(qs_ata_init);
+module_exit(qs_ata_exit);
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
new file mode 100644
index 000000000000..f0489dc302a0
--- /dev/null
+++ b/drivers/scsi/sata_sil.c
@@ -0,0 +1,494 @@
+/*
+ * sata_sil.c - Silicon Image SATA
+ *
+ * Maintained by: Jeff Garzik <jgarzik@pobox.com>
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2003 Red Hat, Inc.
+ * Copyright 2003 Benjamin Herrenschmidt
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "sata_sil"
+#define DRV_VERSION "0.9"
+
+enum {
+ sil_3112 = 0,
+ sil_3114 = 1,
+
+ SIL_FIFO_R0 = 0x40,
+ SIL_FIFO_W0 = 0x41,
+ SIL_FIFO_R1 = 0x44,
+ SIL_FIFO_W1 = 0x45,
+ SIL_FIFO_R2 = 0x240,
+ SIL_FIFO_W2 = 0x241,
+ SIL_FIFO_R3 = 0x244,
+ SIL_FIFO_W3 = 0x245,
+
+ SIL_SYSCFG = 0x48,
+ SIL_MASK_IDE0_INT = (1 << 22),
+ SIL_MASK_IDE1_INT = (1 << 23),
+ SIL_MASK_IDE2_INT = (1 << 24),
+ SIL_MASK_IDE3_INT = (1 << 25),
+ SIL_MASK_2PORT = SIL_MASK_IDE0_INT | SIL_MASK_IDE1_INT,
+ SIL_MASK_4PORT = SIL_MASK_2PORT |
+ SIL_MASK_IDE2_INT | SIL_MASK_IDE3_INT,
+
+ SIL_IDE2_BMDMA = 0x200,
+
+ SIL_INTR_STEERING = (1 << 1),
+ SIL_QUIRK_MOD15WRITE = (1 << 0),
+ SIL_QUIRK_UDMA5MAX = (1 << 1),
+};
+
+static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
+static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static void sil_post_set_mode (struct ata_port *ap);
+
+static struct pci_device_id sil_pci_tbl[] = {
+ { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+ { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+ { 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+ { 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 },
+ { 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+ { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+ { } /* terminate list */
+};
+
+
+/* TODO firmware versions should be added - eric */
+static const struct sil_drivelist {
+ const char * product;
+ unsigned int quirk;
+} sil_blacklist [] = {
+ { "ST320012AS", SIL_QUIRK_MOD15WRITE },
+ { "ST330013AS", SIL_QUIRK_MOD15WRITE },
+ { "ST340017AS", SIL_QUIRK_MOD15WRITE },
+ { "ST360015AS", SIL_QUIRK_MOD15WRITE },
+ { "ST380013AS", SIL_QUIRK_MOD15WRITE },
+ { "ST380023AS", SIL_QUIRK_MOD15WRITE },
+ { "ST3120023AS", SIL_QUIRK_MOD15WRITE },
+ { "ST3160023AS", SIL_QUIRK_MOD15WRITE },
+ { "ST3120026AS", SIL_QUIRK_MOD15WRITE },
+ { "ST3200822AS", SIL_QUIRK_MOD15WRITE },
+ { "ST340014ASL", SIL_QUIRK_MOD15WRITE },
+ { "ST360014ASL", SIL_QUIRK_MOD15WRITE },
+ { "ST380011ASL", SIL_QUIRK_MOD15WRITE },
+ { "ST3120022ASL", SIL_QUIRK_MOD15WRITE },
+ { "ST3160021ASL", SIL_QUIRK_MOD15WRITE },
+ { "Maxtor 4D060H3", SIL_QUIRK_UDMA5MAX },
+ { }
+};
+
+static struct pci_driver sil_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = sil_pci_tbl,
+ .probe = sil_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template sil_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations sil_ops = {
+ .port_disable = ata_port_disable,
+ .dev_config = sil_dev_config,
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = sata_phy_reset,
+ .post_set_mode = sil_post_set_mode,
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+ .eng_timeout = ata_eng_timeout,
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .scr_read = sil_scr_read,
+ .scr_write = sil_scr_write,
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static struct ata_port_info sil_port_info[] = {
+ /* sil_3112 */
+ {
+ .sht = &sil_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SRST | ATA_FLAG_MMIO,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x3f, /* udma0-5 */
+ .port_ops = &sil_ops,
+ }, /* sil_3114 */
+ {
+ .sht = &sil_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SRST | ATA_FLAG_MMIO,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x3f, /* udma0-5 */
+ .port_ops = &sil_ops,
+ },
+};
+
+/* per-port register offsets */
+/* TODO: we can probably calculate rather than use a table */
+static const struct {
+ unsigned long tf; /* ATA taskfile register block */
+ unsigned long ctl; /* ATA control/altstatus register block */
+ unsigned long bmdma; /* DMA register block */
+ unsigned long scr; /* SATA control register block */
+ unsigned long sien; /* SATA Interrupt Enable register */
+ unsigned long xfer_mode;/* data transfer mode register */
+} sil_port[] = {
+ /* port 0 ... */
+ { 0x80, 0x8A, 0x00, 0x100, 0x148, 0xb4 },
+ { 0xC0, 0xCA, 0x08, 0x180, 0x1c8, 0xf4 },
+ { 0x280, 0x28A, 0x200, 0x300, 0x348, 0x2b4 },
+ { 0x2C0, 0x2CA, 0x208, 0x380, 0x3c8, 0x2f4 },
+ /* ... port 3 */
+};
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("low-level driver for Silicon Image SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, sil_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static unsigned char sil_get_device_cache_line(struct pci_dev *pdev)
+{
+ u8 cache_line = 0;
+ pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line);
+ return cache_line;
+}
+
+static void sil_post_set_mode (struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ struct ata_device *dev;
+ void *addr = host_set->mmio_base + sil_port[ap->port_no].xfer_mode;
+ u32 tmp, dev_mode[2];
+ unsigned int i;
+
+ for (i = 0; i < 2; i++) {
+ dev = &ap->device[i];
+ if (!ata_dev_present(dev))
+ dev_mode[i] = 0; /* PIO0/1/2 */
+ else if (dev->flags & ATA_DFLAG_PIO)
+ dev_mode[i] = 1; /* PIO3/4 */
+ else
+ dev_mode[i] = 3; /* UDMA */
+ /* value 2 indicates MDMA */
+ }
+
+ tmp = readl(addr);
+ tmp &= ~((1<<5) | (1<<4) | (1<<1) | (1<<0));
+ tmp |= dev_mode[0];
+ tmp |= (dev_mode[1] << 4);
+ writel(tmp, addr);
+ readl(addr); /* flush */
+}
+
+static inline unsigned long sil_scr_addr(struct ata_port *ap, unsigned int sc_reg)
+{
+ unsigned long offset = ap->ioaddr.scr_addr;
+
+ switch (sc_reg) {
+ case SCR_STATUS:
+ return offset + 4;
+ case SCR_ERROR:
+ return offset + 8;
+ case SCR_CONTROL:
+ return offset;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ return 0;
+}
+
+static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ void *mmio = (void *) sil_scr_addr(ap, sc_reg);
+ if (mmio)
+ return readl(mmio);
+ return 0xffffffffU;
+}
+
+static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+ void *mmio = (void *) sil_scr_addr(ap, sc_reg);
+ if (mmio)
+ writel(val, mmio);
+}
+
+/**
+ * sil_dev_config - Apply device/host-specific errata fixups
+ * @ap: Port containing device to be examined
+ * @dev: Device to be examined
+ *
+ * After the IDENTIFY [PACKET] DEVICE step is complete, and a
+ * device is known to be present, this function is called.
+ * We apply two errata fixups which are specific to Silicon Image,
+ * a Seagate and a Maxtor fixup.
+ *
+ * For certain Seagate devices, we must limit the maximum sectors
+ * to under 8K.
+ *
+ * For certain Maxtor devices, we must not program the drive
+ * beyond udma5.
+ *
+ * Both fixups are unfairly pessimistic. As soon as I get more
+ * information on these errata, I will create a more exhaustive
+ * list, and apply the fixups to only the specific
+ * devices/hosts/firmwares that need it.
+ *
+ * 20040111 - Seagate drives affected by the Mod15Write bug are blacklisted
+ * The Maxtor quirk is in the blacklist, but I'm keeping the original
+ * pessimistic fix for the following reasons...
+ * - There seems to be less info on it, only one device gleaned off the
+ * Windows driver, maybe only one is affected. More info would be greatly
+ * appreciated.
+ * - But then again UDMA5 is hardly anything to complain about
+ */
+static void sil_dev_config(struct ata_port *ap, struct ata_device *dev)
+{
+ unsigned int n, quirks = 0;
+ unsigned char model_num[40];
+ const char *s;
+ unsigned int len;
+
+ ata_dev_id_string(dev->id, model_num, ATA_ID_PROD_OFS,
+ sizeof(model_num));
+ s = &model_num[0];
+ len = strnlen(s, sizeof(model_num));
+
+ /* ATAPI specifies that empty space is blank-filled; remove blanks */
+ while ((len > 0) && (s[len - 1] == ' '))
+ len--;
+
+ for (n = 0; sil_blacklist[n].product; n++)
+ if (!memcmp(sil_blacklist[n].product, s,
+ strlen(sil_blacklist[n].product))) {
+ quirks = sil_blacklist[n].quirk;
+ break;
+ }
+
+ /* limit requests to 15 sectors */
+ if (quirks & SIL_QUIRK_MOD15WRITE) {
+ printk(KERN_INFO "ata%u(%u): applying Seagate errata fix\n",
+ ap->id, dev->devno);
+ ap->host->max_sectors = 15;
+ ap->host->hostt->max_sectors = 15;
+ dev->flags |= ATA_DFLAG_LOCK_SECTORS;
+ return;
+ }
+
+ /* limit to udma5 */
+ if (quirks & SIL_QUIRK_UDMA5MAX) {
+ printk(KERN_INFO "ata%u(%u): applying Maxtor errata fix %s\n",
+ ap->id, dev->devno, s);
+ ap->udma_mask &= ATA_UDMA5;
+ return;
+ }
+}
+
+static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ unsigned long base;
+ void *mmio_base;
+ int rc;
+ unsigned int i;
+ int pci_dev_busy = 0;
+ u32 tmp, irq_mask;
+ u8 cls;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ /*
+ * If this driver happens to only be useful on Apple's K2, then
+ * we should check that here as it has a normal Serverworks ID
+ */
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ INIT_LIST_HEAD(&probe_ent->node);
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops;
+ probe_ent->sht = sil_port_info[ent->driver_data].sht;
+ probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2;
+ probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask;
+ probe_ent->mwdma_mask = sil_port_info[ent->driver_data].mwdma_mask;
+ probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask;
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->host_flags = sil_port_info[ent->driver_data].host_flags;
+
+ mmio_base = ioremap(pci_resource_start(pdev, 5),
+ pci_resource_len(pdev, 5));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+
+ probe_ent->mmio_base = mmio_base;
+
+ base = (unsigned long) mmio_base;
+
+ for (i = 0; i < probe_ent->n_ports; i++) {
+ probe_ent->port[i].cmd_addr = base + sil_port[i].tf;
+ probe_ent->port[i].altstatus_addr =
+ probe_ent->port[i].ctl_addr = base + sil_port[i].ctl;
+ probe_ent->port[i].bmdma_addr = base + sil_port[i].bmdma;
+ probe_ent->port[i].scr_addr = base + sil_port[i].scr;
+ ata_std_ports(&probe_ent->port[i]);
+ }
+
+ /* Initialize FIFO PCI bus arbitration */
+ cls = sil_get_device_cache_line(pdev);
+ if (cls) {
+ cls >>= 3;
+ cls++; /* cls = (line_size/8)+1 */
+ writeb(cls, mmio_base + SIL_FIFO_R0);
+ writeb(cls, mmio_base + SIL_FIFO_W0);
+ writeb(cls, mmio_base + SIL_FIFO_R1);
+ writeb(cls, mmio_base + SIL_FIFO_W2);
+ } else
+ printk(KERN_WARNING DRV_NAME "(%s): cache line size not set. Driver may not function\n",
+ pci_name(pdev));
+
+ if (ent->driver_data == sil_3114) {
+ irq_mask = SIL_MASK_4PORT;
+
+ /* flip the magic "make 4 ports work" bit */
+ tmp = readl(mmio_base + SIL_IDE2_BMDMA);
+ if ((tmp & SIL_INTR_STEERING) == 0)
+ writel(tmp | SIL_INTR_STEERING,
+ mmio_base + SIL_IDE2_BMDMA);
+
+ } else {
+ irq_mask = SIL_MASK_2PORT;
+ }
+
+ /* make sure IDE0/1/2/3 interrupts are not masked */
+ tmp = readl(mmio_base + SIL_SYSCFG);
+ if (tmp & irq_mask) {
+ tmp &= ~irq_mask;
+ writel(tmp, mmio_base + SIL_SYSCFG);
+ readl(mmio_base + SIL_SYSCFG); /* flush */
+ }
+
+ /* mask all SATA phy-related interrupts */
+ /* TODO: unmask bit 6 (SError N bit) for hotplug */
+ for (i = 0; i < probe_ent->n_ports; i++)
+ writel(0, mmio_base + sil_port[i].sien);
+
+ pci_set_master(pdev);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+static int __init sil_init(void)
+{
+ return pci_module_init(&sil_pci_driver);
+}
+
+static void __exit sil_exit(void)
+{
+ pci_unregister_driver(&sil_pci_driver);
+}
+
+
+module_init(sil_init);
+module_exit(sil_exit);
diff --git a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c
new file mode 100644
index 000000000000..5105ddd08447
--- /dev/null
+++ b/drivers/scsi/sata_sis.c
@@ -0,0 +1,286 @@
+/*
+ * sata_sis.c - Silicon Integrated Systems SATA
+ *
+ * Maintained by: Uwe Koziolek
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2004 Uwe Koziolek
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "sata_sis"
+#define DRV_VERSION "0.5"
+
+enum {
+ sis_180 = 0,
+ SIS_SCR_PCI_BAR = 5,
+
+ /* PCI configuration registers */
+ SIS_GENCTL = 0x54, /* IDE General Control register */
+ SIS_SCR_BASE = 0xc0, /* sata0 phy SCR registers */
+ SIS_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */
+
+ /* random bits */
+ SIS_FLAG_CFGSCR = (1 << 30), /* host flag: SCRs via PCI cfg */
+
+ GENCTL_IOMAPPED_SCR = (1 << 26), /* if set, SCRs are in IO space */
+};
+
+static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+
+static struct pci_device_id sis_pci_tbl[] = {
+ { PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 },
+ { PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 },
+ { } /* terminate list */
+};
+
+
+static struct pci_driver sis_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = sis_pci_tbl,
+ .probe = sis_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template sis_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = ATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations sis_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = sata_phy_reset,
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+ .eng_timeout = ata_eng_timeout,
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .scr_read = sis_scr_read,
+ .scr_write = sis_scr_write,
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static struct ata_port_info sis_port_info = {
+ .sht = &sis_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+ ATA_FLAG_NO_LEGACY,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x7,
+ .udma_mask = 0x7f,
+ .port_ops = &sis_ops,
+};
+
+
+MODULE_AUTHOR("Uwe Koziolek");
+MODULE_DESCRIPTION("low-level driver for Silicon Integratad Systems SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, sis_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg)
+{
+ unsigned int addr = SIS_SCR_BASE + (4 * sc_reg);
+
+ if (port_no)
+ addr += SIS_SATA1_OFS;
+ return addr;
+}
+
+static u32 sis_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+ unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg);
+ u32 val;
+
+ if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */
+ return 0xffffffff;
+ pci_read_config_dword(pdev, cfg_addr, &val);
+ return val;
+}
+
+static void sis_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+ unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr);
+
+ if (scr == SCR_ERROR) /* doesn't exist in PCI cfg space */
+ return;
+ pci_write_config_dword(pdev, cfg_addr, val);
+}
+
+static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+
+ if (ap->flags & SIS_FLAG_CFGSCR)
+ return sis_scr_cfg_read(ap, sc_reg);
+ return inl(ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+ if (sc_reg > SCR_CONTROL)
+ return;
+
+ if (ap->flags & SIS_FLAG_CFGSCR)
+ sis_scr_cfg_write(ap, sc_reg, val);
+ else
+ outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+ u16 pci_command;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+ if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+ pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+ }
+}
+
+static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct ata_probe_ent *probe_ent = NULL;
+ int rc;
+ u32 genctl;
+ struct ata_port_info *ppi;
+ int pci_dev_busy = 0;
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ ppi = &sis_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+ if (!probe_ent) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ /* check and see if the SCRs are in IO space or PCI cfg space */
+ pci_read_config_dword(pdev, SIS_GENCTL, &genctl);
+ if ((genctl & GENCTL_IOMAPPED_SCR) == 0)
+ probe_ent->host_flags |= SIS_FLAG_CFGSCR;
+
+ /* if hardware thinks SCRs are in IO space, but there are
+ * no IO resources assigned, change to PCI cfg space.
+ */
+ if ((!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) &&
+ ((pci_resource_start(pdev, SIS_SCR_PCI_BAR) == 0) ||
+ (pci_resource_len(pdev, SIS_SCR_PCI_BAR) < 128))) {
+ genctl &= ~GENCTL_IOMAPPED_SCR;
+ pci_write_config_dword(pdev, SIS_GENCTL, genctl);
+ probe_ent->host_flags |= SIS_FLAG_CFGSCR;
+ }
+
+ if (!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) {
+ probe_ent->port[0].scr_addr =
+ pci_resource_start(pdev, SIS_SCR_PCI_BAR);
+ probe_ent->port[1].scr_addr =
+ pci_resource_start(pdev, SIS_SCR_PCI_BAR) + 64;
+ }
+
+ pci_set_master(pdev);
+ pci_enable_intx(pdev);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_regions:
+ pci_release_regions(pdev);
+
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+
+}
+
+static int __init sis_init(void)
+{
+ return pci_module_init(&sis_pci_driver);
+}
+
+static void __exit sis_exit(void)
+{
+ pci_unregister_driver(&sis_pci_driver);
+}
+
+module_init(sis_init);
+module_exit(sis_exit);
+
diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c
new file mode 100644
index 000000000000..8d1a5d25c053
--- /dev/null
+++ b/drivers/scsi/sata_svw.c
@@ -0,0 +1,483 @@
+/*
+ * sata_svw.c - ServerWorks / Apple K2 SATA
+ *
+ * Maintained by: Benjamin Herrenschmidt <benh@kernel.crashing.org> and
+ * Jeff Garzik <jgarzik@pobox.com>
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *
+ * Bits from Jeff Garzik, Copyright RedHat, Inc.
+ *
+ * This driver probably works with non-Apple versions of the
+ * Broadcom chipset...
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#ifdef CONFIG_PPC_OF
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#endif /* CONFIG_PPC_OF */
+
+#define DRV_NAME "sata_svw"
+#define DRV_VERSION "1.05"
+
+/* Taskfile registers offsets */
+#define K2_SATA_TF_CMD_OFFSET 0x00
+#define K2_SATA_TF_DATA_OFFSET 0x00
+#define K2_SATA_TF_ERROR_OFFSET 0x04
+#define K2_SATA_TF_NSECT_OFFSET 0x08
+#define K2_SATA_TF_LBAL_OFFSET 0x0c
+#define K2_SATA_TF_LBAM_OFFSET 0x10
+#define K2_SATA_TF_LBAH_OFFSET 0x14
+#define K2_SATA_TF_DEVICE_OFFSET 0x18
+#define K2_SATA_TF_CMDSTAT_OFFSET 0x1c
+#define K2_SATA_TF_CTL_OFFSET 0x20
+
+/* DMA base */
+#define K2_SATA_DMA_CMD_OFFSET 0x30
+
+/* SCRs base */
+#define K2_SATA_SCR_STATUS_OFFSET 0x40
+#define K2_SATA_SCR_ERROR_OFFSET 0x44
+#define K2_SATA_SCR_CONTROL_OFFSET 0x48
+
+/* Others */
+#define K2_SATA_SICR1_OFFSET 0x80
+#define K2_SATA_SICR2_OFFSET 0x84
+#define K2_SATA_SIM_OFFSET 0x88
+
+/* Port stride */
+#define K2_SATA_PORT_OFFSET 0x100
+
+
+static u32 k2_sata_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+ return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void k2_sata_scr_write (struct ata_port *ap, unsigned int sc_reg,
+ u32 val)
+{
+ if (sc_reg > SCR_CONTROL)
+ return;
+ writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void k2_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+ if (tf->ctl != ap->last_ctl) {
+ writeb(tf->ctl, ioaddr->ctl_addr);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ writew(tf->feature | (((u16)tf->hob_feature) << 8), ioaddr->feature_addr);
+ writew(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr);
+ writew(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr);
+ writew(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr);
+ writew(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr);
+ } else if (is_addr) {
+ writew(tf->feature, ioaddr->feature_addr);
+ writew(tf->nsect, ioaddr->nsect_addr);
+ writew(tf->lbal, ioaddr->lbal_addr);
+ writew(tf->lbam, ioaddr->lbam_addr);
+ writew(tf->lbah, ioaddr->lbah_addr);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE)
+ writeb(tf->device, ioaddr->device_addr);
+
+ ata_wait_idle(ap);
+}
+
+
+static void k2_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u16 nsect, lbal, lbam, lbah;
+
+ nsect = tf->nsect = readw(ioaddr->nsect_addr);
+ lbal = tf->lbal = readw(ioaddr->lbal_addr);
+ lbam = tf->lbam = readw(ioaddr->lbam_addr);
+ lbah = tf->lbah = readw(ioaddr->lbah_addr);
+ tf->device = readw(ioaddr->device_addr);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ tf->hob_feature = readw(ioaddr->error_addr) >> 8;
+ tf->hob_nsect = nsect >> 8;
+ tf->hob_lbal = lbal >> 8;
+ tf->hob_lbam = lbam >> 8;
+ tf->hob_lbah = lbah >> 8;
+ }
+}
+
+/**
+ * k2_bmdma_setup_mmio - Set up PCI IDE BMDMA transaction (MMIO)
+ * @qc: Info associated with this ATA transaction.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void k2_bmdma_setup_mmio (struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ u8 dmactl;
+ void *mmio = (void *) ap->ioaddr.bmdma_addr;
+ /* load PRD table addr. */
+ mb(); /* make sure PRD table writes are visible to controller */
+ writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS);
+
+ /* specify data direction, triple-check start bit is clear */
+ dmactl = readb(mmio + ATA_DMA_CMD);
+ dmactl &= ~(ATA_DMA_WR | ATA_DMA_START);
+ if (!rw)
+ dmactl |= ATA_DMA_WR;
+ writeb(dmactl, mmio + ATA_DMA_CMD);
+
+ /* issue r/w command if this is not a ATA DMA command*/
+ if (qc->tf.protocol != ATA_PROT_DMA)
+ ap->ops->exec_command(ap, &qc->tf);
+}
+
+/**
+ * k2_bmdma_start_mmio - Start a PCI IDE BMDMA transaction (MMIO)
+ * @qc: Info associated with this ATA transaction.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+
+static void k2_bmdma_start_mmio (struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void *mmio = (void *) ap->ioaddr.bmdma_addr;
+ u8 dmactl;
+
+ /* start host DMA transaction */
+ dmactl = readb(mmio + ATA_DMA_CMD);
+ writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD);
+ /* There is a race condition in certain SATA controllers that can
+ be seen when the r/w command is given to the controller before the
+ host DMA is started. On a Read command, the controller would initiate
+ the command to the drive even before it sees the DMA start. When there
+ are very fast drives connected to the controller, or when the data request
+ hits in the drive cache, there is the possibility that the drive returns a part
+ or all of the requested data to the controller before the DMA start is issued.
+ In this case, the controller would become confused as to what to do with the data.
+ In the worst case when all the data is returned back to the controller, the
+ controller could hang. In other cases it could return partial data returning
+ in data corruption. This problem has been seen in PPC systems and can also appear
+ on an system with very fast disks, where the SATA controller is sitting behind a
+ number of bridges, and hence there is significant latency between the r/w command
+ and the start command. */
+ /* issue r/w command if the access is to ATA*/
+ if (qc->tf.protocol == ATA_PROT_DMA)
+ ap->ops->exec_command(ap, &qc->tf);
+}
+
+
+static u8 k2_stat_check_status(struct ata_port *ap)
+{
+ return readl((void *) ap->ioaddr.status_addr);
+}
+
+#ifdef CONFIG_PPC_OF
+/*
+ * k2_sata_proc_info
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start,
+ off_t offset, int count, int inout)
+{
+ struct ata_port *ap;
+ struct device_node *np;
+ int len, index;
+
+ /* Find the ata_port */
+ ap = (struct ata_port *) &shost->hostdata[0];
+ if (ap == NULL)
+ return 0;
+
+ /* Find the OF node for the PCI device proper */
+ np = pci_device_to_OF_node(to_pci_dev(ap->host_set->dev));
+ if (np == NULL)
+ return 0;
+
+ /* Match it to a port node */
+ index = (ap == ap->host_set->ports[0]) ? 0 : 1;
+ for (np = np->child; np != NULL; np = np->sibling) {
+ u32 *reg = (u32 *)get_property(np, "reg", NULL);
+ if (!reg)
+ continue;
+ if (index == *reg)
+ break;
+ }
+ if (np == NULL)
+ return 0;
+
+ len = sprintf(page, "devspec: %s\n", np->full_name);
+
+ return len;
+}
+#endif /* CONFIG_PPC_OF */
+
+
+static Scsi_Host_Template k2_sata_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+#ifdef CONFIG_PPC_OF
+ .proc_info = k2_sata_proc_info,
+#endif
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+
+static struct ata_port_operations k2_sata_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = k2_sata_tf_load,
+ .tf_read = k2_sata_tf_read,
+ .check_status = k2_stat_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = sata_phy_reset,
+ .bmdma_setup = k2_bmdma_setup_mmio,
+ .bmdma_start = k2_bmdma_start_mmio,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+ .eng_timeout = ata_eng_timeout,
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .scr_read = k2_sata_scr_read,
+ .scr_write = k2_sata_scr_write,
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static void k2_sata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+ port->cmd_addr = base + K2_SATA_TF_CMD_OFFSET;
+ port->data_addr = base + K2_SATA_TF_DATA_OFFSET;
+ port->feature_addr =
+ port->error_addr = base + K2_SATA_TF_ERROR_OFFSET;
+ port->nsect_addr = base + K2_SATA_TF_NSECT_OFFSET;
+ port->lbal_addr = base + K2_SATA_TF_LBAL_OFFSET;
+ port->lbam_addr = base + K2_SATA_TF_LBAM_OFFSET;
+ port->lbah_addr = base + K2_SATA_TF_LBAH_OFFSET;
+ port->device_addr = base + K2_SATA_TF_DEVICE_OFFSET;
+ port->command_addr =
+ port->status_addr = base + K2_SATA_TF_CMDSTAT_OFFSET;
+ port->altstatus_addr =
+ port->ctl_addr = base + K2_SATA_TF_CTL_OFFSET;
+ port->bmdma_addr = base + K2_SATA_DMA_CMD_OFFSET;
+ port->scr_addr = base + K2_SATA_SCR_STATUS_OFFSET;
+}
+
+
+static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ unsigned long base;
+ void *mmio_base;
+ int pci_dev_busy = 0;
+ int rc;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ /*
+ * If this driver happens to only be useful on Apple's K2, then
+ * we should check that here as it has a normal Serverworks ID
+ */
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+ /*
+ * Check if we have resources mapped at all (second function may
+ * have been disabled by firmware)
+ */
+ if (pci_resource_len(pdev, 5) == 0)
+ return -ENODEV;
+
+ /* Request PCI regions */
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ mmio_base = ioremap(pci_resource_start(pdev, 5),
+ pci_resource_len(pdev, 5));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+ base = (unsigned long) mmio_base;
+
+ /* Clear a magic bit in SCR1 according to Darwin, those help
+ * some funky seagate drives (though so far, those were already
+ * set by the firmware on the machines I had access to
+ */
+ writel(readl(mmio_base + K2_SATA_SICR1_OFFSET) & ~0x00040000,
+ mmio_base + K2_SATA_SICR1_OFFSET);
+
+ /* Clear SATA error & interrupts we don't use */
+ writel(0xffffffff, mmio_base + K2_SATA_SCR_ERROR_OFFSET);
+ writel(0x0, mmio_base + K2_SATA_SIM_OFFSET);
+
+ probe_ent->sht = &k2_sata_sht;
+ probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+ ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO;
+ probe_ent->port_ops = &k2_sata_ops;
+ probe_ent->n_ports = 4;
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+
+ /* We don't care much about the PIO/UDMA masks, but the core won't like us
+ * if we don't fill these
+ */
+ probe_ent->pio_mask = 0x1f;
+ probe_ent->mwdma_mask = 0x7;
+ probe_ent->udma_mask = 0x7f;
+
+ /* We have 4 ports per PCI function */
+ k2_sata_setup_port(&probe_ent->port[0], base + 0 * K2_SATA_PORT_OFFSET);
+ k2_sata_setup_port(&probe_ent->port[1], base + 1 * K2_SATA_PORT_OFFSET);
+ k2_sata_setup_port(&probe_ent->port[2], base + 2 * K2_SATA_PORT_OFFSET);
+ k2_sata_setup_port(&probe_ent->port[3], base + 3 * K2_SATA_PORT_OFFSET);
+
+ pci_set_master(pdev);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+
+static struct pci_device_id k2_sata_pci_tbl[] = {
+ { 0x1166, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1166, 0x0241, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1166, 0x0242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { }
+};
+
+
+static struct pci_driver k2_sata_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = k2_sata_pci_tbl,
+ .probe = k2_sata_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+
+static int __init k2_sata_init(void)
+{
+ return pci_module_init(&k2_sata_pci_driver);
+}
+
+
+static void __exit k2_sata_exit(void)
+{
+ pci_unregister_driver(&k2_sata_pci_driver);
+}
+
+
+MODULE_AUTHOR("Benjamin Herrenschmidt");
+MODULE_DESCRIPTION("low-level driver for K2 SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, k2_sata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(k2_sata_init);
+module_exit(k2_sata_exit);
diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c
new file mode 100644
index 000000000000..70118650c461
--- /dev/null
+++ b/drivers/scsi/sata_sx4.c
@@ -0,0 +1,1503 @@
+/*
+ * sata_sx4.c - Promise SATA
+ *
+ * Maintained by: Jeff Garzik <jgarzik@pobox.com>
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2003-2004 Red Hat, Inc.
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+#include "sata_promise.h"
+
+#define DRV_NAME "sata_sx4"
+#define DRV_VERSION "0.7"
+
+
+enum {
+ PDC_PRD_TBL = 0x44, /* Direct command DMA table addr */
+
+ PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */
+ PDC_HDMA_PKT_SUBMIT = 0x100, /* Host DMA packet pointer addr */
+ PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */
+ PDC_HDMA_CTLSTAT = 0x12C, /* Host DMA control / status */
+
+ PDC_20621_SEQCTL = 0x400,
+ PDC_20621_SEQMASK = 0x480,
+ PDC_20621_GENERAL_CTL = 0x484,
+ PDC_20621_PAGE_SIZE = (32 * 1024),
+
+ /* chosen, not constant, values; we design our own DIMM mem map */
+ PDC_20621_DIMM_WINDOW = 0x0C, /* page# for 32K DIMM window */
+ PDC_20621_DIMM_BASE = 0x00200000,
+ PDC_20621_DIMM_DATA = (64 * 1024),
+ PDC_DIMM_DATA_STEP = (256 * 1024),
+ PDC_DIMM_WINDOW_STEP = (8 * 1024),
+ PDC_DIMM_HOST_PRD = (6 * 1024),
+ PDC_DIMM_HOST_PKT = (128 * 0),
+ PDC_DIMM_HPKT_PRD = (128 * 1),
+ PDC_DIMM_ATA_PKT = (128 * 2),
+ PDC_DIMM_APKT_PRD = (128 * 3),
+ PDC_DIMM_HEADER_SZ = PDC_DIMM_APKT_PRD + 128,
+ PDC_PAGE_WINDOW = 0x40,
+ PDC_PAGE_DATA = PDC_PAGE_WINDOW +
+ (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE),
+ PDC_PAGE_SET = PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE,
+
+ PDC_CHIP0_OFS = 0xC0000, /* offset of chip #0 */
+
+ PDC_20621_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) |
+ (1<<23),
+
+ board_20621 = 0, /* FastTrak S150 SX4 */
+
+ PDC_RESET = (1 << 11), /* HDMA reset */
+
+ PDC_MAX_HDMA = 32,
+ PDC_HDMA_Q_MASK = (PDC_MAX_HDMA - 1),
+
+ PDC_DIMM0_SPD_DEV_ADDRESS = 0x50,
+ PDC_DIMM1_SPD_DEV_ADDRESS = 0x51,
+ PDC_MAX_DIMM_MODULE = 0x02,
+ PDC_I2C_CONTROL_OFFSET = 0x48,
+ PDC_I2C_ADDR_DATA_OFFSET = 0x4C,
+ PDC_DIMM0_CONTROL_OFFSET = 0x80,
+ PDC_DIMM1_CONTROL_OFFSET = 0x84,
+ PDC_SDRAM_CONTROL_OFFSET = 0x88,
+ PDC_I2C_WRITE = 0x00000000,
+ PDC_I2C_READ = 0x00000040,
+ PDC_I2C_START = 0x00000080,
+ PDC_I2C_MASK_INT = 0x00000020,
+ PDC_I2C_COMPLETE = 0x00010000,
+ PDC_I2C_NO_ACK = 0x00100000,
+ PDC_DIMM_SPD_SUBADDRESS_START = 0x00,
+ PDC_DIMM_SPD_SUBADDRESS_END = 0x7F,
+ PDC_DIMM_SPD_ROW_NUM = 3,
+ PDC_DIMM_SPD_COLUMN_NUM = 4,
+ PDC_DIMM_SPD_MODULE_ROW = 5,
+ PDC_DIMM_SPD_TYPE = 11,
+ PDC_DIMM_SPD_FRESH_RATE = 12,
+ PDC_DIMM_SPD_BANK_NUM = 17,
+ PDC_DIMM_SPD_CAS_LATENCY = 18,
+ PDC_DIMM_SPD_ATTRIBUTE = 21,
+ PDC_DIMM_SPD_ROW_PRE_CHARGE = 27,
+ PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28,
+ PDC_DIMM_SPD_RAS_CAS_DELAY = 29,
+ PDC_DIMM_SPD_ACTIVE_PRECHARGE = 30,
+ PDC_DIMM_SPD_SYSTEM_FREQ = 126,
+ PDC_CTL_STATUS = 0x08,
+ PDC_DIMM_WINDOW_CTLR = 0x0C,
+ PDC_TIME_CONTROL = 0x3C,
+ PDC_TIME_PERIOD = 0x40,
+ PDC_TIME_COUNTER = 0x44,
+ PDC_GENERAL_CTLR = 0x484,
+ PCI_PLL_INIT = 0x8A531824,
+ PCI_X_TCOUNT = 0xEE1E5CFF
+};
+
+
+struct pdc_port_priv {
+ u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512];
+ u8 *pkt;
+ dma_addr_t pkt_dma;
+};
+
+struct pdc_host_priv {
+ void *dimm_mmio;
+
+ unsigned int doing_hdma;
+ unsigned int hdma_prod;
+ unsigned int hdma_cons;
+ struct {
+ struct ata_queued_cmd *qc;
+ unsigned int seq;
+ unsigned long pkt_ofs;
+ } hdma[32];
+};
+
+
+static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
+static void pdc_eng_timeout(struct ata_port *ap);
+static void pdc_20621_phy_reset (struct ata_port *ap);
+static int pdc_port_start(struct ata_port *ap);
+static void pdc_port_stop(struct ata_port *ap);
+static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
+static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf);
+static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf);
+static void pdc20621_host_stop(struct ata_host_set *host_set);
+static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe);
+static int pdc20621_detect_dimm(struct ata_probe_ent *pe);
+static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe,
+ u32 device, u32 subaddr, u32 *pdata);
+static int pdc20621_prog_dimm0(struct ata_probe_ent *pe);
+static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe);
+#ifdef ATA_VERBOSE_DEBUG
+static void pdc20621_get_from_dimm(struct ata_probe_ent *pe,
+ void *psource, u32 offset, u32 size);
+#endif
+static void pdc20621_put_to_dimm(struct ata_probe_ent *pe,
+ void *psource, u32 offset, u32 size);
+static void pdc20621_irq_clear(struct ata_port *ap);
+static int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc);
+
+
+static Scsi_Host_Template pdc_sata_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations pdc_20621_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = pdc_tf_load_mmio,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = pdc_exec_command_mmio,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = pdc_20621_phy_reset,
+ .qc_prep = pdc20621_qc_prep,
+ .qc_issue = pdc20621_qc_issue_prot,
+ .eng_timeout = pdc_eng_timeout,
+ .irq_handler = pdc20621_interrupt,
+ .irq_clear = pdc20621_irq_clear,
+ .port_start = pdc_port_start,
+ .port_stop = pdc_port_stop,
+ .host_stop = pdc20621_host_stop,
+};
+
+static struct ata_port_info pdc_port_info[] = {
+ /* board_20621 */
+ {
+ .sht = &pdc_sata_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_SRST | ATA_FLAG_MMIO,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x7f, /* udma0-6 ; FIXME */
+ .port_ops = &pdc_20621_ops,
+ },
+
+};
+
+static struct pci_device_id pdc_sata_pci_tbl[] = {
+ { PCI_VENDOR_ID_PROMISE, 0x6622, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_20621 },
+ { } /* terminate list */
+};
+
+
+static struct pci_driver pdc_sata_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = pdc_sata_pci_tbl,
+ .probe = pdc_sata_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+
+static void pdc20621_host_stop(struct ata_host_set *host_set)
+{
+ struct pdc_host_priv *hpriv = host_set->private_data;
+ void *dimm_mmio = hpriv->dimm_mmio;
+
+ iounmap(dimm_mmio);
+ kfree(hpriv);
+}
+
+static int pdc_port_start(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct pdc_port_priv *pp;
+ int rc;
+
+ rc = ata_port_start(ap);
+ if (rc)
+ return rc;
+
+ pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+ if (!pp) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ pp->pkt = dma_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL);
+ if (!pp->pkt) {
+ rc = -ENOMEM;
+ goto err_out_kfree;
+ }
+
+ ap->private_data = pp;
+
+ return 0;
+
+err_out_kfree:
+ kfree(pp);
+err_out:
+ ata_port_stop(ap);
+ return rc;
+}
+
+
+static void pdc_port_stop(struct ata_port *ap)
+{
+ struct device *dev = ap->host_set->dev;
+ struct pdc_port_priv *pp = ap->private_data;
+
+ ap->private_data = NULL;
+ dma_free_coherent(dev, 128, pp->pkt, pp->pkt_dma);
+ kfree(pp);
+ ata_port_stop(ap);
+}
+
+
+static void pdc_20621_phy_reset (struct ata_port *ap)
+{
+ VPRINTK("ENTER\n");
+ ap->cbl = ATA_CBL_SATA;
+ ata_port_probe(ap);
+ ata_bus_reset(ap);
+}
+
+static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
+ unsigned int portno,
+ unsigned int total_len)
+{
+ u32 addr;
+ unsigned int dw = PDC_DIMM_APKT_PRD >> 2;
+ u32 *buf32 = (u32 *) buf;
+
+ /* output ATA packet S/G table */
+ addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
+ (PDC_DIMM_DATA_STEP * portno);
+ VPRINTK("ATA sg addr 0x%x, %d\n", addr, addr);
+ buf32[dw] = cpu_to_le32(addr);
+ buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);
+
+ VPRINTK("ATA PSG @ %x == (0x%x, 0x%x)\n",
+ PDC_20621_DIMM_BASE +
+ (PDC_DIMM_WINDOW_STEP * portno) +
+ PDC_DIMM_APKT_PRD,
+ buf32[dw], buf32[dw + 1]);
+}
+
+static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf,
+ unsigned int portno,
+ unsigned int total_len)
+{
+ u32 addr;
+ unsigned int dw = PDC_DIMM_HPKT_PRD >> 2;
+ u32 *buf32 = (u32 *) buf;
+
+ /* output Host DMA packet S/G table */
+ addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
+ (PDC_DIMM_DATA_STEP * portno);
+
+ buf32[dw] = cpu_to_le32(addr);
+ buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);
+
+ VPRINTK("HOST PSG @ %x == (0x%x, 0x%x)\n",
+ PDC_20621_DIMM_BASE +
+ (PDC_DIMM_WINDOW_STEP * portno) +
+ PDC_DIMM_HPKT_PRD,
+ buf32[dw], buf32[dw + 1]);
+}
+
+static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf,
+ unsigned int devno, u8 *buf,
+ unsigned int portno)
+{
+ unsigned int i, dw;
+ u32 *buf32 = (u32 *) buf;
+ u8 dev_reg;
+
+ unsigned int dimm_sg = PDC_20621_DIMM_BASE +
+ (PDC_DIMM_WINDOW_STEP * portno) +
+ PDC_DIMM_APKT_PRD;
+ VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg);
+
+ i = PDC_DIMM_ATA_PKT;
+
+ /*
+ * Set up ATA packet
+ */
+ if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))
+ buf[i++] = PDC_PKT_READ;
+ else if (tf->protocol == ATA_PROT_NODATA)
+ buf[i++] = PDC_PKT_NODATA;
+ else
+ buf[i++] = 0;
+ buf[i++] = 0; /* reserved */
+ buf[i++] = portno + 1; /* seq. id */
+ buf[i++] = 0xff; /* delay seq. id */
+
+ /* dimm dma S/G, and next-pkt */
+ dw = i >> 2;
+ if (tf->protocol == ATA_PROT_NODATA)
+ buf32[dw] = 0;
+ else
+ buf32[dw] = cpu_to_le32(dimm_sg);
+ buf32[dw + 1] = 0;
+ i += 8;
+
+ if (devno == 0)
+ dev_reg = ATA_DEVICE_OBS;
+ else
+ dev_reg = ATA_DEVICE_OBS | ATA_DEV1;
+
+ /* select device */
+ buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE;
+ buf[i++] = dev_reg;
+
+ /* device control register */
+ buf[i++] = (1 << 5) | PDC_REG_DEVCTL;
+ buf[i++] = tf->ctl;
+
+ return i;
+}
+
+static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf,
+ unsigned int portno)
+{
+ unsigned int dw;
+ u32 tmp, *buf32 = (u32 *) buf;
+
+ unsigned int host_sg = PDC_20621_DIMM_BASE +
+ (PDC_DIMM_WINDOW_STEP * portno) +
+ PDC_DIMM_HOST_PRD;
+ unsigned int dimm_sg = PDC_20621_DIMM_BASE +
+ (PDC_DIMM_WINDOW_STEP * portno) +
+ PDC_DIMM_HPKT_PRD;
+ VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg);
+ VPRINTK("host_sg == 0x%x, %d\n", host_sg, host_sg);
+
+ dw = PDC_DIMM_HOST_PKT >> 2;
+
+ /*
+ * Set up Host DMA packet
+ */
+ if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))
+ tmp = PDC_PKT_READ;
+ else
+ tmp = 0;
+ tmp |= ((portno + 1 + 4) << 16); /* seq. id */
+ tmp |= (0xff << 24); /* delay seq. id */
+ buf32[dw + 0] = cpu_to_le32(tmp);
+ buf32[dw + 1] = cpu_to_le32(host_sg);
+ buf32[dw + 2] = cpu_to_le32(dimm_sg);
+ buf32[dw + 3] = 0;
+
+ VPRINTK("HOST PKT @ %x == (0x%x 0x%x 0x%x 0x%x)\n",
+ PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * portno) +
+ PDC_DIMM_HOST_PKT,
+ buf32[dw + 0],
+ buf32[dw + 1],
+ buf32[dw + 2],
+ buf32[dw + 3]);
+}
+
+static void pdc20621_dma_prep(struct ata_queued_cmd *qc)
+{
+ struct scatterlist *sg = qc->sg;
+ struct ata_port *ap = qc->ap;
+ struct pdc_port_priv *pp = ap->private_data;
+ void *mmio = ap->host_set->mmio_base;
+ struct pdc_host_priv *hpriv = ap->host_set->private_data;
+ void *dimm_mmio = hpriv->dimm_mmio;
+ unsigned int portno = ap->port_no;
+ unsigned int i, last, idx, total_len = 0, sgt_len;
+ u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ];
+
+ assert(qc->flags & ATA_QCFLAG_DMAMAP);
+
+ VPRINTK("ata%u: ENTER\n", ap->id);
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ /*
+ * Build S/G table
+ */
+ last = qc->n_elem;
+ idx = 0;
+ for (i = 0; i < last; i++) {
+ buf[idx++] = cpu_to_le32(sg_dma_address(&sg[i]));
+ buf[idx++] = cpu_to_le32(sg_dma_len(&sg[i]));
+ total_len += sg[i].length;
+ }
+ buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT);
+ sgt_len = idx * 4;
+
+ /*
+ * Build ATA, host DMA packets
+ */
+ pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len);
+ pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno);
+
+ pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len);
+ i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
+
+ if (qc->tf.flags & ATA_TFLAG_LBA48)
+ i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i);
+ else
+ i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i);
+
+ pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i);
+
+ /* copy three S/G tables and two packets to DIMM MMIO window */
+ memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP),
+ &pp->dimm_buf, PDC_DIMM_HEADER_SZ);
+ memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP) +
+ PDC_DIMM_HOST_PRD,
+ &pp->dimm_buf[PDC_DIMM_HEADER_SZ], sgt_len);
+
+ /* force host FIFO dump */
+ writel(0x00000001, mmio + PDC_20621_GENERAL_CTL);
+
+ readl(dimm_mmio); /* MMIO PCI posting flush */
+
+ VPRINTK("ata pkt buf ofs %u, prd size %u, mmio copied\n", i, sgt_len);
+}
+
+static void pdc20621_nodata_prep(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct pdc_port_priv *pp = ap->private_data;
+ void *mmio = ap->host_set->mmio_base;
+ struct pdc_host_priv *hpriv = ap->host_set->private_data;
+ void *dimm_mmio = hpriv->dimm_mmio;
+ unsigned int portno = ap->port_no;
+ unsigned int i;
+
+ VPRINTK("ata%u: ENTER\n", ap->id);
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
+
+ if (qc->tf.flags & ATA_TFLAG_LBA48)
+ i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i);
+ else
+ i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i);
+
+ pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i);
+
+ /* copy three S/G tables and two packets to DIMM MMIO window */
+ memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP),
+ &pp->dimm_buf, PDC_DIMM_HEADER_SZ);
+
+ /* force host FIFO dump */
+ writel(0x00000001, mmio + PDC_20621_GENERAL_CTL);
+
+ readl(dimm_mmio); /* MMIO PCI posting flush */
+
+ VPRINTK("ata pkt buf ofs %u, mmio copied\n", i);
+}
+
+static void pdc20621_qc_prep(struct ata_queued_cmd *qc)
+{
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ pdc20621_dma_prep(qc);
+ break;
+ case ATA_PROT_NODATA:
+ pdc20621_nodata_prep(qc);
+ break;
+ default:
+ break;
+ }
+}
+
+static void __pdc20621_push_hdma(struct ata_queued_cmd *qc,
+ unsigned int seq,
+ u32 pkt_ofs)
+{
+ struct ata_port *ap = qc->ap;
+ struct ata_host_set *host_set = ap->host_set;
+ void *mmio = host_set->mmio_base;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
+ readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */
+
+ writel(pkt_ofs, mmio + PDC_HDMA_PKT_SUBMIT);
+ readl(mmio + PDC_HDMA_PKT_SUBMIT); /* flush */
+}
+
+static void pdc20621_push_hdma(struct ata_queued_cmd *qc,
+ unsigned int seq,
+ u32 pkt_ofs)
+{
+ struct ata_port *ap = qc->ap;
+ struct pdc_host_priv *pp = ap->host_set->private_data;
+ unsigned int idx = pp->hdma_prod & PDC_HDMA_Q_MASK;
+
+ if (!pp->doing_hdma) {
+ __pdc20621_push_hdma(qc, seq, pkt_ofs);
+ pp->doing_hdma = 1;
+ return;
+ }
+
+ pp->hdma[idx].qc = qc;
+ pp->hdma[idx].seq = seq;
+ pp->hdma[idx].pkt_ofs = pkt_ofs;
+ pp->hdma_prod++;
+}
+
+static void pdc20621_pop_hdma(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct pdc_host_priv *pp = ap->host_set->private_data;
+ unsigned int idx = pp->hdma_cons & PDC_HDMA_Q_MASK;
+
+ /* if nothing on queue, we're done */
+ if (pp->hdma_prod == pp->hdma_cons) {
+ pp->doing_hdma = 0;
+ return;
+ }
+
+ __pdc20621_push_hdma(pp->hdma[idx].qc, pp->hdma[idx].seq,
+ pp->hdma[idx].pkt_ofs);
+ pp->hdma_cons++;
+}
+
+#ifdef ATA_VERBOSE_DEBUG
+static void pdc20621_dump_hdma(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ unsigned int port_no = ap->port_no;
+ struct pdc_host_priv *hpriv = ap->host_set->private_data;
+ void *dimm_mmio = hpriv->dimm_mmio;
+
+ dimm_mmio += (port_no * PDC_DIMM_WINDOW_STEP);
+ dimm_mmio += PDC_DIMM_HOST_PKT;
+
+ printk(KERN_ERR "HDMA[0] == 0x%08X\n", readl(dimm_mmio));
+ printk(KERN_ERR "HDMA[1] == 0x%08X\n", readl(dimm_mmio + 4));
+ printk(KERN_ERR "HDMA[2] == 0x%08X\n", readl(dimm_mmio + 8));
+ printk(KERN_ERR "HDMA[3] == 0x%08X\n", readl(dimm_mmio + 12));
+}
+#else
+static inline void pdc20621_dump_hdma(struct ata_queued_cmd *qc) { }
+#endif /* ATA_VERBOSE_DEBUG */
+
+static void pdc20621_packet_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ata_host_set *host_set = ap->host_set;
+ unsigned int port_no = ap->port_no;
+ void *mmio = host_set->mmio_base;
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ u8 seq = (u8) (port_no + 1);
+ unsigned int port_ofs;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ VPRINTK("ata%u: ENTER\n", ap->id);
+
+ wmb(); /* flush PRD, pkt writes */
+
+ port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no);
+
+ /* if writing, we (1) DMA to DIMM, then (2) do ATA command */
+ if (rw && qc->tf.protocol == ATA_PROT_DMA) {
+ seq += 4;
+
+ pdc20621_dump_hdma(qc);
+ pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT);
+ VPRINTK("queued ofs 0x%x (%u), seq %u\n",
+ port_ofs + PDC_DIMM_HOST_PKT,
+ port_ofs + PDC_DIMM_HOST_PKT,
+ seq);
+ } else {
+ writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
+ readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */
+
+ writel(port_ofs + PDC_DIMM_ATA_PKT,
+ (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
+ readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
+ VPRINTK("submitted ofs 0x%x (%u), seq %u\n",
+ port_ofs + PDC_DIMM_ATA_PKT,
+ port_ofs + PDC_DIMM_ATA_PKT,
+ seq);
+ }
+}
+
+static int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc)
+{
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_NODATA:
+ pdc20621_packet_start(qc);
+ return 0;
+
+ case ATA_PROT_ATAPI_DMA:
+ BUG();
+ break;
+
+ default:
+ break;
+ }
+
+ return ata_qc_issue_prot(qc);
+}
+
+static inline unsigned int pdc20621_host_intr( struct ata_port *ap,
+ struct ata_queued_cmd *qc,
+ unsigned int doing_hdma,
+ void *mmio)
+{
+ unsigned int port_no = ap->port_no;
+ unsigned int port_ofs =
+ PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no);
+ u8 status;
+ unsigned int handled = 0;
+
+ VPRINTK("ENTER\n");
+
+ if ((qc->tf.protocol == ATA_PROT_DMA) && /* read */
+ (!(qc->tf.flags & ATA_TFLAG_WRITE))) {
+
+ /* step two - DMA from DIMM to host */
+ if (doing_hdma) {
+ VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->id,
+ readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
+ /* get drive status; clear intr; complete txn */
+ ata_qc_complete(qc, ata_wait_idle(ap));
+ pdc20621_pop_hdma(qc);
+ }
+
+ /* step one - exec ATA command */
+ else {
+ u8 seq = (u8) (port_no + 1 + 4);
+ VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->id,
+ readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
+
+ /* submit hdma pkt */
+ pdc20621_dump_hdma(qc);
+ pdc20621_push_hdma(qc, seq,
+ port_ofs + PDC_DIMM_HOST_PKT);
+ }
+ handled = 1;
+
+ } else if (qc->tf.protocol == ATA_PROT_DMA) { /* write */
+
+ /* step one - DMA from host to DIMM */
+ if (doing_hdma) {
+ u8 seq = (u8) (port_no + 1);
+ VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->id,
+ readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
+
+ /* submit ata pkt */
+ writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
+ readl(mmio + PDC_20621_SEQCTL + (seq * 4));
+ writel(port_ofs + PDC_DIMM_ATA_PKT,
+ (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
+ readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
+ }
+
+ /* step two - execute ATA command */
+ else {
+ VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->id,
+ readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
+ /* get drive status; clear intr; complete txn */
+ ata_qc_complete(qc, ata_wait_idle(ap));
+ pdc20621_pop_hdma(qc);
+ }
+ handled = 1;
+
+ /* command completion, but no data xfer */
+ } else if (qc->tf.protocol == ATA_PROT_NODATA) {
+
+ status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+ DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status);
+ ata_qc_complete(qc, status);
+ handled = 1;
+
+ } else {
+ ap->stats.idle_irq++;
+ }
+
+ return handled;
+}
+
+static void pdc20621_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ void *mmio = host_set->mmio_base;
+
+ mmio += PDC_CHIP0_OFS;
+
+ readl(mmio + PDC_20621_SEQMASK);
+}
+
+static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ struct ata_port *ap;
+ u32 mask = 0;
+ unsigned int i, tmp, port_no;
+ unsigned int handled = 0;
+ void *mmio_base;
+
+ VPRINTK("ENTER\n");
+
+ if (!host_set || !host_set->mmio_base) {
+ VPRINTK("QUICK EXIT\n");
+ return IRQ_NONE;
+ }
+
+ mmio_base = host_set->mmio_base;
+
+ /* reading should also clear interrupts */
+ mmio_base += PDC_CHIP0_OFS;
+ mask = readl(mmio_base + PDC_20621_SEQMASK);
+ VPRINTK("mask == 0x%x\n", mask);
+
+ if (mask == 0xffffffff) {
+ VPRINTK("QUICK EXIT 2\n");
+ return IRQ_NONE;
+ }
+ mask &= 0xffff; /* only 16 tags possible */
+ if (!mask) {
+ VPRINTK("QUICK EXIT 3\n");
+ return IRQ_NONE;
+ }
+
+ spin_lock(&host_set->lock);
+
+ for (i = 1; i < 9; i++) {
+ port_no = i - 1;
+ if (port_no > 3)
+ port_no -= 4;
+ if (port_no >= host_set->n_ports)
+ ap = NULL;
+ else
+ ap = host_set->ports[port_no];
+ tmp = mask & (1 << i);
+ VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp);
+ if (tmp && ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN)))
+ handled += pdc20621_host_intr(ap, qc, (i > 4),
+ mmio_base);
+ }
+ }
+
+ spin_unlock(&host_set->lock);
+
+ VPRINTK("mask == 0x%x\n", mask);
+
+ VPRINTK("EXIT\n");
+
+ return IRQ_RETVAL(handled);
+}
+
+static void pdc_eng_timeout(struct ata_port *ap)
+{
+ u8 drv_stat;
+ struct ata_queued_cmd *qc;
+
+ DPRINTK("ENTER\n");
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (!qc) {
+ printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+ ap->id);
+ goto out;
+ }
+
+ /* hack alert! We cannot use the supplied completion
+ * function from inside the ->eh_strategy_handler() thread.
+ * libata is the only user of ->eh_strategy_handler() in
+ * any kernel, so the default scsi_done() assumes it is
+ * not being called from the SCSI EH.
+ */
+ qc->scsidone = scsi_finish_command;
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_NODATA:
+ printk(KERN_ERR "ata%u: command timeout\n", ap->id);
+ ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR);
+ break;
+
+ default:
+ drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+
+ printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n",
+ ap->id, qc->tf.command, drv_stat);
+
+ ata_qc_complete(qc, drv_stat);
+ break;
+ }
+
+out:
+ DPRINTK("EXIT\n");
+}
+
+static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ WARN_ON (tf->protocol == ATA_PROT_DMA ||
+ tf->protocol == ATA_PROT_NODATA);
+ ata_tf_load(ap, tf);
+}
+
+
+static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ WARN_ON (tf->protocol == ATA_PROT_DMA ||
+ tf->protocol == ATA_PROT_NODATA);
+ ata_exec_command(ap, tf);
+}
+
+
+static void pdc_sata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+ port->cmd_addr = base;
+ port->data_addr = base;
+ port->feature_addr =
+ port->error_addr = base + 0x4;
+ port->nsect_addr = base + 0x8;
+ port->lbal_addr = base + 0xc;
+ port->lbam_addr = base + 0x10;
+ port->lbah_addr = base + 0x14;
+ port->device_addr = base + 0x18;
+ port->command_addr =
+ port->status_addr = base + 0x1c;
+ port->altstatus_addr =
+ port->ctl_addr = base + 0x38;
+}
+
+
+#ifdef ATA_VERBOSE_DEBUG
+static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource,
+ u32 offset, u32 size)
+{
+ u32 window_size;
+ u16 idx;
+ u8 page_mask;
+ long dist;
+ void *mmio = pe->mmio_base;
+ struct pdc_host_priv *hpriv = pe->private_data;
+ void *dimm_mmio = hpriv->dimm_mmio;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ page_mask = 0x00;
+ window_size = 0x2000 * 4; /* 32K byte uchar size */
+ idx = (u16) (offset / window_size);
+
+ writel(0x01, mmio + PDC_GENERAL_CTLR);
+ readl(mmio + PDC_GENERAL_CTLR);
+ writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
+ readl(mmio + PDC_DIMM_WINDOW_CTLR);
+
+ offset -= (idx * window_size);
+ idx++;
+ dist = ((long) (window_size - (offset + size))) >= 0 ? size :
+ (long) (window_size - offset);
+ memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4),
+ dist);
+
+ psource += dist;
+ size -= dist;
+ for (; (long) size >= (long) window_size ;) {
+ writel(0x01, mmio + PDC_GENERAL_CTLR);
+ readl(mmio + PDC_GENERAL_CTLR);
+ writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
+ readl(mmio + PDC_DIMM_WINDOW_CTLR);
+ memcpy_fromio((char *) psource, (char *) (dimm_mmio),
+ window_size / 4);
+ psource += window_size;
+ size -= window_size;
+ idx ++;
+ }
+
+ if (size) {
+ writel(0x01, mmio + PDC_GENERAL_CTLR);
+ readl(mmio + PDC_GENERAL_CTLR);
+ writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
+ readl(mmio + PDC_DIMM_WINDOW_CTLR);
+ memcpy_fromio((char *) psource, (char *) (dimm_mmio),
+ size / 4);
+ }
+}
+#endif
+
+
+static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource,
+ u32 offset, u32 size)
+{
+ u32 window_size;
+ u16 idx;
+ u8 page_mask;
+ long dist;
+ void *mmio = pe->mmio_base;
+ struct pdc_host_priv *hpriv = pe->private_data;
+ void *dimm_mmio = hpriv->dimm_mmio;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ page_mask = 0x00;
+ window_size = 0x2000 * 4; /* 32K byte uchar size */
+ idx = (u16) (offset / window_size);
+
+ writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
+ readl(mmio + PDC_DIMM_WINDOW_CTLR);
+ offset -= (idx * window_size);
+ idx++;
+ dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size :
+ (long) (window_size - offset);
+ memcpy_toio((char *) (dimm_mmio + offset / 4), (char *) psource, dist);
+ writel(0x01, mmio + PDC_GENERAL_CTLR);
+ readl(mmio + PDC_GENERAL_CTLR);
+
+ psource += dist;
+ size -= dist;
+ for (; (long) size >= (long) window_size ;) {
+ writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
+ readl(mmio + PDC_DIMM_WINDOW_CTLR);
+ memcpy_toio((char *) (dimm_mmio), (char *) psource,
+ window_size / 4);
+ writel(0x01, mmio + PDC_GENERAL_CTLR);
+ readl(mmio + PDC_GENERAL_CTLR);
+ psource += window_size;
+ size -= window_size;
+ idx ++;
+ }
+
+ if (size) {
+ writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
+ readl(mmio + PDC_DIMM_WINDOW_CTLR);
+ memcpy_toio((char *) (dimm_mmio), (char *) psource, size / 4);
+ writel(0x01, mmio + PDC_GENERAL_CTLR);
+ readl(mmio + PDC_GENERAL_CTLR);
+ }
+}
+
+
+static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device,
+ u32 subaddr, u32 *pdata)
+{
+ void *mmio = pe->mmio_base;
+ u32 i2creg = 0;
+ u32 status;
+ u32 count =0;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ i2creg |= device << 24;
+ i2creg |= subaddr << 16;
+
+ /* Set the device and subaddress */
+ writel(i2creg, mmio + PDC_I2C_ADDR_DATA_OFFSET);
+ readl(mmio + PDC_I2C_ADDR_DATA_OFFSET);
+
+ /* Write Control to perform read operation, mask int */
+ writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT,
+ mmio + PDC_I2C_CONTROL_OFFSET);
+
+ for (count = 0; count <= 1000; count ++) {
+ status = readl(mmio + PDC_I2C_CONTROL_OFFSET);
+ if (status & PDC_I2C_COMPLETE) {
+ status = readl(mmio + PDC_I2C_ADDR_DATA_OFFSET);
+ break;
+ } else if (count == 1000)
+ return 0;
+ }
+
+ *pdata = (status >> 8) & 0x000000ff;
+ return 1;
+}
+
+
+static int pdc20621_detect_dimm(struct ata_probe_ent *pe)
+{
+ u32 data=0 ;
+ if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ PDC_DIMM_SPD_SYSTEM_FREQ, &data)) {
+ if (data == 100)
+ return 100;
+ } else
+ return 0;
+
+ if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) {
+ if(data <= 0x75)
+ return 133;
+ } else
+ return 0;
+
+ return 0;
+}
+
+
+static int pdc20621_prog_dimm0(struct ata_probe_ent *pe)
+{
+ u32 spd0[50];
+ u32 data = 0;
+ int size, i;
+ u8 bdimmsize;
+ void *mmio = pe->mmio_base;
+ static const struct {
+ unsigned int reg;
+ unsigned int ofs;
+ } pdc_i2c_read_data [] = {
+ { PDC_DIMM_SPD_TYPE, 11 },
+ { PDC_DIMM_SPD_FRESH_RATE, 12 },
+ { PDC_DIMM_SPD_COLUMN_NUM, 4 },
+ { PDC_DIMM_SPD_ATTRIBUTE, 21 },
+ { PDC_DIMM_SPD_ROW_NUM, 3 },
+ { PDC_DIMM_SPD_BANK_NUM, 17 },
+ { PDC_DIMM_SPD_MODULE_ROW, 5 },
+ { PDC_DIMM_SPD_ROW_PRE_CHARGE, 27 },
+ { PDC_DIMM_SPD_ROW_ACTIVE_DELAY, 28 },
+ { PDC_DIMM_SPD_RAS_CAS_DELAY, 29 },
+ { PDC_DIMM_SPD_ACTIVE_PRECHARGE, 30 },
+ { PDC_DIMM_SPD_CAS_LATENCY, 18 },
+ };
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ for(i=0; i<ARRAY_SIZE(pdc_i2c_read_data); i++)
+ pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ pdc_i2c_read_data[i].reg,
+ &spd0[pdc_i2c_read_data[i].ofs]);
+
+ data |= (spd0[4] - 8) | ((spd0[21] != 0) << 3) | ((spd0[3]-11) << 4);
+ data |= ((spd0[17] / 4) << 6) | ((spd0[5] / 2) << 7) |
+ ((((spd0[27] + 9) / 10) - 1) << 8) ;
+ data |= (((((spd0[29] > spd0[28])
+ ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10;
+ data |= ((spd0[30] - spd0[29] + 9) / 10 - 2) << 12;
+
+ if (spd0[18] & 0x08)
+ data |= ((0x03) << 14);
+ else if (spd0[18] & 0x04)
+ data |= ((0x02) << 14);
+ else if (spd0[18] & 0x01)
+ data |= ((0x01) << 14);
+ else
+ data |= (0 << 14);
+
+ /*
+ Calculate the size of bDIMMSize (power of 2) and
+ merge the DIMM size by program start/end address.
+ */
+
+ bdimmsize = spd0[4] + (spd0[5] / 2) + spd0[3] + (spd0[17] / 2) + 3;
+ size = (1 << bdimmsize) >> 20; /* size = xxx(MB) */
+ data |= (((size / 16) - 1) << 16);
+ data |= (0 << 23);
+ data |= 8;
+ writel(data, mmio + PDC_DIMM0_CONTROL_OFFSET);
+ readl(mmio + PDC_DIMM0_CONTROL_OFFSET);
+ return size;
+}
+
+
+static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe)
+{
+ u32 data, spd0;
+ int error, i;
+ void *mmio = pe->mmio_base;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ /*
+ Set To Default : DIMM Module Global Control Register (0x022259F1)
+ DIMM Arbitration Disable (bit 20)
+ DIMM Data/Control Output Driving Selection (bit12 - bit15)
+ Refresh Enable (bit 17)
+ */
+
+ data = 0x022259F1;
+ writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET);
+ readl(mmio + PDC_SDRAM_CONTROL_OFFSET);
+
+ /* Turn on for ECC */
+ pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ PDC_DIMM_SPD_TYPE, &spd0);
+ if (spd0 == 0x02) {
+ data |= (0x01 << 16);
+ writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET);
+ readl(mmio + PDC_SDRAM_CONTROL_OFFSET);
+ printk(KERN_ERR "Local DIMM ECC Enabled\n");
+ }
+
+ /* DIMM Initialization Select/Enable (bit 18/19) */
+ data &= (~(1<<18));
+ data |= (1<<19);
+ writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET);
+
+ error = 1;
+ for (i = 1; i <= 10; i++) { /* polling ~5 secs */
+ data = readl(mmio + PDC_SDRAM_CONTROL_OFFSET);
+ if (!(data & (1<<19))) {
+ error = 0;
+ break;
+ }
+ msleep(i*100);
+ }
+ return error;
+}
+
+
+static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe)
+{
+ int speed, size, length;
+ u32 addr,spd0,pci_status;
+ u32 tmp=0;
+ u32 time_period=0;
+ u32 tcount=0;
+ u32 ticks=0;
+ u32 clock=0;
+ u32 fparam=0;
+ void *mmio = pe->mmio_base;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ /* Initialize PLL based upon PCI Bus Frequency */
+
+ /* Initialize Time Period Register */
+ writel(0xffffffff, mmio + PDC_TIME_PERIOD);
+ time_period = readl(mmio + PDC_TIME_PERIOD);
+ VPRINTK("Time Period Register (0x40): 0x%x\n", time_period);
+
+ /* Enable timer */
+ writel(0x00001a0, mmio + PDC_TIME_CONTROL);
+ readl(mmio + PDC_TIME_CONTROL);
+
+ /* Wait 3 seconds */
+ msleep(3000);
+
+ /*
+ When timer is enabled, counter is decreased every internal
+ clock cycle.
+ */
+
+ tcount = readl(mmio + PDC_TIME_COUNTER);
+ VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount);
+
+ /*
+ If SX4 is on PCI-X bus, after 3 seconds, the timer counter
+ register should be >= (0xffffffff - 3x10^8).
+ */
+ if(tcount >= PCI_X_TCOUNT) {
+ ticks = (time_period - tcount);
+ VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks);
+
+ clock = (ticks / 300000);
+ VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock);
+
+ clock = (clock * 33);
+ VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock);
+
+ /* PLL F Param (bit 22:16) */
+ fparam = (1400000 / clock) - 2;
+ VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam);
+
+ /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */
+ pci_status = (0x8a001824 | (fparam << 16));
+ } else
+ pci_status = PCI_PLL_INIT;
+
+ /* Initialize PLL. */
+ VPRINTK("pci_status: 0x%x\n", pci_status);
+ writel(pci_status, mmio + PDC_CTL_STATUS);
+ readl(mmio + PDC_CTL_STATUS);
+
+ /*
+ Read SPD of DIMM by I2C interface,
+ and program the DIMM Module Controller.
+ */
+ if (!(speed = pdc20621_detect_dimm(pe))) {
+ printk(KERN_ERR "Detect Local DIMM Fail\n");
+ return 1; /* DIMM error */
+ }
+ VPRINTK("Local DIMM Speed = %d\n", speed);
+
+ /* Programming DIMM0 Module Control Register (index_CID0:80h) */
+ size = pdc20621_prog_dimm0(pe);
+ VPRINTK("Local DIMM Size = %dMB\n",size);
+
+ /* Programming DIMM Module Global Control Register (index_CID0:88h) */
+ if (pdc20621_prog_dimm_global(pe)) {
+ printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n");
+ return 1;
+ }
+
+#ifdef ATA_VERBOSE_DEBUG
+ {
+ u8 test_parttern1[40] = {0x55,0xAA,'P','r','o','m','i','s','e',' ',
+ 'N','o','t',' ','Y','e','t',' ','D','e','f','i','n','e','d',' ',
+ '1','.','1','0',
+ '9','8','0','3','1','6','1','2',0,0};
+ u8 test_parttern2[40] = {0};
+
+ pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40);
+ pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40);
+
+ pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40);
+ pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40);
+ printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
+ test_parttern2[1], &(test_parttern2[2]));
+ pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040,
+ 40);
+ printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
+ test_parttern2[1], &(test_parttern2[2]));
+
+ pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40);
+ pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40);
+ printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
+ test_parttern2[1], &(test_parttern2[2]));
+ }
+#endif
+
+ /* ECC initiliazation. */
+
+ pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS,
+ PDC_DIMM_SPD_TYPE, &spd0);
+ if (spd0 == 0x02) {
+ VPRINTK("Start ECC initialization\n");
+ addr = 0;
+ length = size * 1024 * 1024;
+ while (addr < length) {
+ pdc20621_put_to_dimm(pe, (void *) &tmp, addr,
+ sizeof(u32));
+ addr += sizeof(u32);
+ }
+ VPRINTK("Finish ECC initialization\n");
+ }
+ return 0;
+}
+
+
+static void pdc_20621_init(struct ata_probe_ent *pe)
+{
+ u32 tmp;
+ void *mmio = pe->mmio_base;
+
+ /* hard-code chip #0 */
+ mmio += PDC_CHIP0_OFS;
+
+ /*
+ * Select page 0x40 for our 32k DIMM window
+ */
+ tmp = readl(mmio + PDC_20621_DIMM_WINDOW) & 0xffff0000;
+ tmp |= PDC_PAGE_WINDOW; /* page 40h; arbitrarily selected */
+ writel(tmp, mmio + PDC_20621_DIMM_WINDOW);
+
+ /*
+ * Reset Host DMA
+ */
+ tmp = readl(mmio + PDC_HDMA_CTLSTAT);
+ tmp |= PDC_RESET;
+ writel(tmp, mmio + PDC_HDMA_CTLSTAT);
+ readl(mmio + PDC_HDMA_CTLSTAT); /* flush */
+
+ udelay(10);
+
+ tmp = readl(mmio + PDC_HDMA_CTLSTAT);
+ tmp &= ~PDC_RESET;
+ writel(tmp, mmio + PDC_HDMA_CTLSTAT);
+ readl(mmio + PDC_HDMA_CTLSTAT); /* flush */
+}
+
+static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ unsigned long base;
+ void *mmio_base, *dimm_mmio = NULL;
+ struct pdc_host_priv *hpriv = NULL;
+ unsigned int board_idx = (unsigned int) ent->driver_data;
+ int pci_dev_busy = 0;
+ int rc;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ /*
+ * If this driver happens to only be useful on Apple's K2, then
+ * we should check that here as it has a normal Serverworks ID
+ */
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ mmio_base = ioremap(pci_resource_start(pdev, 3),
+ pci_resource_len(pdev, 3));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+ base = (unsigned long) mmio_base;
+
+ hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+ if (!hpriv) {
+ rc = -ENOMEM;
+ goto err_out_iounmap;
+ }
+ memset(hpriv, 0, sizeof(*hpriv));
+
+ dimm_mmio = ioremap(pci_resource_start(pdev, 4),
+ pci_resource_len(pdev, 4));
+ if (!dimm_mmio) {
+ kfree(hpriv);
+ rc = -ENOMEM;
+ goto err_out_iounmap;
+ }
+
+ hpriv->dimm_mmio = dimm_mmio;
+
+ probe_ent->sht = pdc_port_info[board_idx].sht;
+ probe_ent->host_flags = pdc_port_info[board_idx].host_flags;
+ probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask;
+ probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask;
+ probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask;
+ probe_ent->port_ops = pdc_port_info[board_idx].port_ops;
+
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+
+ probe_ent->private_data = hpriv;
+ base += PDC_CHIP0_OFS;
+
+ probe_ent->n_ports = 4;
+ pdc_sata_setup_port(&probe_ent->port[0], base + 0x200);
+ pdc_sata_setup_port(&probe_ent->port[1], base + 0x280);
+ pdc_sata_setup_port(&probe_ent->port[2], base + 0x300);
+ pdc_sata_setup_port(&probe_ent->port[3], base + 0x380);
+
+ pci_set_master(pdev);
+
+ /* initialize adapter */
+ /* initialize local dimm */
+ if (pdc20621_dimm_init(probe_ent)) {
+ rc = -ENOMEM;
+ goto err_out_iounmap_dimm;
+ }
+ pdc_20621_init(probe_ent);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_iounmap_dimm: /* only get to this label if 20621 */
+ kfree(hpriv);
+ iounmap(dimm_mmio);
+err_out_iounmap:
+ iounmap(mmio_base);
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+
+static int __init pdc_sata_init(void)
+{
+ return pci_module_init(&pdc_sata_pci_driver);
+}
+
+
+static void __exit pdc_sata_exit(void)
+{
+ pci_unregister_driver(&pdc_sata_pci_driver);
+}
+
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("Promise SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(pdc_sata_init);
+module_exit(pdc_sata_exit);
diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c
new file mode 100644
index 000000000000..0bff4f475f26
--- /dev/null
+++ b/drivers/scsi/sata_uli.c
@@ -0,0 +1,287 @@
+/*
+ * sata_uli.c - ULi Electronics SATA
+ *
+ * The contents of this file are subject to the Open
+ * Software License version 1.1 that can be found at
+ * http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ * by reference.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU General Public License version 2 (the "GPL") as distributed
+ * in the kernel source COPYING file, in which case the provisions of
+ * the GPL are applicable instead of the above. If you wish to allow
+ * the use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under
+ * the OSL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "sata_uli"
+#define DRV_VERSION "0.5"
+
+enum {
+ uli_5289 = 0,
+ uli_5287 = 1,
+ uli_5281 = 2,
+
+ /* PCI configuration registers */
+ ULI5287_BASE = 0x90, /* sata0 phy SCR registers */
+ ULI5287_OFFS = 0x10, /* offset from sata0->sata1 phy regs */
+ ULI5281_BASE = 0x60, /* sata0 phy SCR registers */
+ ULI5281_OFFS = 0x60, /* offset from sata0->sata1 phy regs */
+};
+
+static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+
+static struct pci_device_id uli_pci_tbl[] = {
+ { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 },
+ { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 },
+ { PCI_VENDOR_ID_AL, 0x5281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5281 },
+ { } /* terminate list */
+};
+
+
+static struct pci_driver uli_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = uli_pci_tbl,
+ .probe = uli_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template uli_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations uli_ops = {
+ .port_disable = ata_port_disable,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .phy_reset = sata_phy_reset,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .eng_timeout = ata_eng_timeout,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
+ .scr_read = uli_scr_read,
+ .scr_write = uli_scr_write,
+
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static struct ata_port_info uli_port_info = {
+ .sht = &uli_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+ ATA_FLAG_NO_LEGACY,
+ .pio_mask = 0x03, //support pio mode 4 (FIXME)
+ .udma_mask = 0x7f, //support udma mode 6
+ .port_ops = &uli_ops,
+};
+
+
+MODULE_AUTHOR("Peer Chen");
+MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, uli_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg)
+{
+ return ap->ioaddr.scr_addr + (4 * sc_reg);
+}
+
+static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+ unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg);
+ u32 val;
+
+ pci_read_config_dword(pdev, cfg_addr, &val);
+ return val;
+}
+
+static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
+ unsigned int cfg_addr = get_scr_cfg_addr(ap, scr);
+
+ pci_write_config_dword(pdev, cfg_addr, val);
+}
+
+static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+
+ return uli_scr_cfg_read(ap, sc_reg);
+}
+
+static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+ if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0
+ return;
+
+ uli_scr_cfg_write(ap, sc_reg, val);
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+ u16 pci_command;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+ if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+ pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+ }
+}
+
+static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct ata_probe_ent *probe_ent;
+ struct ata_port_info *ppi;
+ int rc;
+ unsigned int board_idx = (unsigned int) ent->driver_data;
+ int pci_dev_busy = 0;
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ ppi = &uli_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+ if (!probe_ent) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ switch (board_idx) {
+ case uli_5287:
+ probe_ent->port[0].scr_addr = ULI5287_BASE;
+ probe_ent->port[1].scr_addr = ULI5287_BASE + ULI5287_OFFS;
+ probe_ent->n_ports = 4;
+
+ probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8;
+ probe_ent->port[2].altstatus_addr =
+ probe_ent->port[2].ctl_addr =
+ (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4;
+ probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16;
+ probe_ent->port[2].scr_addr = ULI5287_BASE + ULI5287_OFFS*4;
+
+ probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8;
+ probe_ent->port[3].altstatus_addr =
+ probe_ent->port[3].ctl_addr =
+ (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4;
+ probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24;
+ probe_ent->port[3].scr_addr = ULI5287_BASE + ULI5287_OFFS*5;
+
+ ata_std_ports(&probe_ent->port[2]);
+ ata_std_ports(&probe_ent->port[3]);
+ break;
+
+ case uli_5289:
+ probe_ent->port[0].scr_addr = ULI5287_BASE;
+ probe_ent->port[1].scr_addr = ULI5287_BASE + ULI5287_OFFS;
+ break;
+
+ case uli_5281:
+ probe_ent->port[0].scr_addr = ULI5281_BASE;
+ probe_ent->port[1].scr_addr = ULI5281_BASE + ULI5281_OFFS;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ pci_set_master(pdev);
+ pci_enable_intx(pdev);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_regions:
+ pci_release_regions(pdev);
+
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+
+}
+
+static int __init uli_init(void)
+{
+ return pci_module_init(&uli_pci_driver);
+}
+
+static void __exit uli_exit(void)
+{
+ pci_unregister_driver(&uli_pci_driver);
+}
+
+
+module_init(uli_init);
+module_exit(uli_exit);
diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c
new file mode 100644
index 000000000000..3a7830667277
--- /dev/null
+++ b/drivers/scsi/sata_via.c
@@ -0,0 +1,387 @@
+/*
+ sata_via.c - VIA Serial ATA controllers
+
+ Maintained by: Jeff Garzik <jgarzik@pobox.com>
+ Please ALWAYS copy linux-ide@vger.kernel.org
+ on emails.
+
+ Copyright 2003-2004 Red Hat, Inc. All rights reserved.
+ Copyright 2003-2004 Jeff Garzik
+
+ The contents of this file are subject to the Open
+ Software License version 1.1 that can be found at
+ http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ by reference.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the GNU General Public License version 2 (the "GPL") as distributed
+ in the kernel source COPYING file, in which case the provisions of
+ the GPL are applicable instead of the above. If you wish to allow
+ the use of your version of this file only under the terms of the
+ GPL and not to allow others to use your version of this file under
+ the OSL, indicate your decision by deleting the provisions above and
+ replace them with the notice and other provisions required by the GPL.
+ If you do not delete the provisions above, a recipient may use your
+ version of this file under either the OSL or the GPL.
+
+ ----------------------------------------------------------------------
+
+ To-do list:
+ * VT6421 PATA support
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME "sata_via"
+#define DRV_VERSION "1.1"
+
+enum board_ids_enum {
+ vt6420,
+ vt6421,
+};
+
+enum {
+ SATA_CHAN_ENAB = 0x40, /* SATA channel enable */
+ SATA_INT_GATE = 0x41, /* SATA interrupt gating */
+ SATA_NATIVE_MODE = 0x42, /* Native mode enable */
+ SATA_PATA_SHARING = 0x49, /* PATA/SATA sharing func ctrl */
+
+ PORT0 = (1 << 1),
+ PORT1 = (1 << 0),
+ ALL_PORTS = PORT0 | PORT1,
+ N_PORTS = 2,
+
+ NATIVE_MODE_ALL = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4),
+
+ SATA_EXT_PHY = (1 << 6), /* 0==use PATA, 1==ext phy */
+ SATA_2DEV = (1 << 5), /* SATA is master/slave */
+};
+
+static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+
+static struct pci_device_id svia_pci_tbl[] = {
+ { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 },
+ { 0x1106, 0x3249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6421 },
+
+ { } /* terminate list */
+};
+
+static struct pci_driver svia_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = svia_pci_tbl,
+ .probe = svia_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template svia_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+static struct ata_port_operations svia_sata_ops = {
+ .port_disable = ata_port_disable,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .phy_reset = sata_phy_reset,
+
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+
+ .eng_timeout = ata_eng_timeout,
+
+ .irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
+ .scr_read = svia_scr_read,
+ .scr_write = svia_scr_write,
+
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static struct ata_port_info svia_port_info = {
+ .sht = &svia_sht,
+ .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST | ATA_FLAG_NO_LEGACY,
+ .pio_mask = 0x1f,
+ .mwdma_mask = 0x07,
+ .udma_mask = 0x7f,
+ .port_ops = &svia_sata_ops,
+};
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, svia_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+ return inl(ap->ioaddr.scr_addr + (4 * sc_reg));
+}
+
+static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+ if (sc_reg > SCR_CONTROL)
+ return;
+ outl(val, ap->ioaddr.scr_addr + (4 * sc_reg));
+}
+
+static const unsigned int svia_bar_sizes[] = {
+ 8, 4, 8, 4, 16, 256
+};
+
+static const unsigned int vt6421_bar_sizes[] = {
+ 16, 16, 16, 16, 32, 128
+};
+
+static unsigned long svia_scr_addr(unsigned long addr, unsigned int port)
+{
+ return addr + (port * 128);
+}
+
+static unsigned long vt6421_scr_addr(unsigned long addr, unsigned int port)
+{
+ return addr + (port * 64);
+}
+
+static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
+ struct pci_dev *pdev,
+ unsigned int port)
+{
+ unsigned long reg_addr = pci_resource_start(pdev, port);
+ unsigned long bmdma_addr = pci_resource_start(pdev, 4) + (port * 8);
+ unsigned long scr_addr;
+
+ probe_ent->port[port].cmd_addr = reg_addr;
+ probe_ent->port[port].altstatus_addr =
+ probe_ent->port[port].ctl_addr = (reg_addr + 8) | ATA_PCI_CTL_OFS;
+ probe_ent->port[port].bmdma_addr = bmdma_addr;
+
+ scr_addr = vt6421_scr_addr(pci_resource_start(pdev, 5), port);
+ probe_ent->port[port].scr_addr = scr_addr;
+
+ ata_std_ports(&probe_ent->port[port]);
+}
+
+static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
+{
+ struct ata_probe_ent *probe_ent;
+ struct ata_port_info *ppi = &svia_port_info;
+
+ probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+ if (!probe_ent)
+ return NULL;
+
+ probe_ent->port[0].scr_addr =
+ svia_scr_addr(pci_resource_start(pdev, 5), 0);
+ probe_ent->port[1].scr_addr =
+ svia_scr_addr(pci_resource_start(pdev, 5), 1);
+
+ return probe_ent;
+}
+
+static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev)
+{
+ struct ata_probe_ent *probe_ent;
+ unsigned int i;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (!probe_ent)
+ return NULL;
+
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ probe_ent->sht = &svia_sht;
+ probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+ ATA_FLAG_NO_LEGACY;
+ probe_ent->port_ops = &svia_sata_ops;
+ probe_ent->n_ports = N_PORTS;
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->pio_mask = 0x1f;
+ probe_ent->mwdma_mask = 0x07;
+ probe_ent->udma_mask = 0x7f;
+
+ for (i = 0; i < N_PORTS; i++)
+ vt6421_init_addrs(probe_ent, pdev, i);
+
+ return probe_ent;
+}
+
+static void svia_configure(struct pci_dev *pdev)
+{
+ u8 tmp8;
+
+ pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8);
+ printk(KERN_INFO DRV_NAME "(%s): routed to hard irq line %d\n",
+ pci_name(pdev),
+ (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f);
+
+ /* make sure SATA channels are enabled */
+ pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8);
+ if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
+ printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channels (0x%x)\n",
+ pci_name(pdev), (int) tmp8);
+ tmp8 |= ALL_PORTS;
+ pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8);
+ }
+
+ /* make sure interrupts for each channel sent to us */
+ pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8);
+ if ((tmp8 & ALL_PORTS) != ALL_PORTS) {
+ printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel interrupts (0x%x)\n",
+ pci_name(pdev), (int) tmp8);
+ tmp8 |= ALL_PORTS;
+ pci_write_config_byte(pdev, SATA_INT_GATE, tmp8);
+ }
+
+ /* make sure native mode is enabled */
+ pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8);
+ if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) {
+ printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel native mode (0x%x)\n",
+ pci_name(pdev), (int) tmp8);
+ tmp8 |= NATIVE_MODE_ALL;
+ pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
+ }
+}
+
+static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ unsigned int i;
+ int rc;
+ struct ata_probe_ent *probe_ent;
+ int board_id = (int) ent->driver_data;
+ const int *bar_sizes;
+ int pci_dev_busy = 0;
+ u8 tmp8;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ if (board_id == vt6420) {
+ pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8);
+ if (tmp8 & SATA_2DEV) {
+ printk(KERN_ERR DRV_NAME "(%s): SATA master/slave not supported (0x%x)\n",
+ pci_name(pdev), (int) tmp8);
+ rc = -EIO;
+ goto err_out_regions;
+ }
+
+ bar_sizes = &svia_bar_sizes[0];
+ } else {
+ bar_sizes = &vt6421_bar_sizes[0];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++)
+ if ((pci_resource_start(pdev, i) == 0) ||
+ (pci_resource_len(pdev, i) < bar_sizes[i])) {
+ printk(KERN_ERR DRV_NAME "(%s): invalid PCI BAR %u (sz 0x%lx, val 0x%lx)\n",
+ pci_name(pdev), i,
+ pci_resource_start(pdev, i),
+ pci_resource_len(pdev, i));
+ rc = -ENODEV;
+ goto err_out_regions;
+ }
+
+ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ if (board_id == vt6420)
+ probe_ent = vt6420_init_probe_ent(pdev);
+ else
+ probe_ent = vt6421_init_probe_ent(pdev);
+
+ if (!probe_ent) {
+ printk(KERN_ERR DRV_NAME "(%s): out of memory\n",
+ pci_name(pdev));
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ svia_configure(pdev);
+
+ pci_set_master(pdev);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+static int __init svia_init(void)
+{
+ return pci_module_init(&svia_pci_driver);
+}
+
+static void __exit svia_exit(void)
+{
+ pci_unregister_driver(&svia_pci_driver);
+}
+
+module_init(svia_init);
+module_exit(svia_exit);
+
diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
new file mode 100644
index 000000000000..2c28f0ad73c2
--- /dev/null
+++ b/drivers/scsi/sata_vsc.c
@@ -0,0 +1,407 @@
+/*
+ * sata_vsc.c - Vitesse VSC7174 4 port DPA SATA
+ *
+ * Maintained by: Jeremy Higdon @ SGI
+ * Please ALWAYS copy linux-ide@vger.kernel.org
+ * on emails.
+ *
+ * Copyright 2004 SGI
+ *
+ * Bits from Jeff Garzik, Copyright RedHat, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "sata_vsc"
+#define DRV_VERSION "1.0"
+
+/* Interrupt register offsets (from chip base address) */
+#define VSC_SATA_INT_STAT_OFFSET 0x00
+#define VSC_SATA_INT_MASK_OFFSET 0x04
+
+/* Taskfile registers offsets */
+#define VSC_SATA_TF_CMD_OFFSET 0x00
+#define VSC_SATA_TF_DATA_OFFSET 0x00
+#define VSC_SATA_TF_ERROR_OFFSET 0x04
+#define VSC_SATA_TF_FEATURE_OFFSET 0x06
+#define VSC_SATA_TF_NSECT_OFFSET 0x08
+#define VSC_SATA_TF_LBAL_OFFSET 0x0c
+#define VSC_SATA_TF_LBAM_OFFSET 0x10
+#define VSC_SATA_TF_LBAH_OFFSET 0x14
+#define VSC_SATA_TF_DEVICE_OFFSET 0x18
+#define VSC_SATA_TF_STATUS_OFFSET 0x1c
+#define VSC_SATA_TF_COMMAND_OFFSET 0x1d
+#define VSC_SATA_TF_ALTSTATUS_OFFSET 0x28
+#define VSC_SATA_TF_CTL_OFFSET 0x29
+
+/* DMA base */
+#define VSC_SATA_UP_DESCRIPTOR_OFFSET 0x64
+#define VSC_SATA_UP_DATA_BUFFER_OFFSET 0x6C
+#define VSC_SATA_DMA_CMD_OFFSET 0x70
+
+/* SCRs base */
+#define VSC_SATA_SCR_STATUS_OFFSET 0x100
+#define VSC_SATA_SCR_ERROR_OFFSET 0x104
+#define VSC_SATA_SCR_CONTROL_OFFSET 0x108
+
+/* Port stride */
+#define VSC_SATA_PORT_OFFSET 0x200
+
+
+static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+ if (sc_reg > SCR_CONTROL)
+ return 0xffffffffU;
+ return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg,
+ u32 val)
+{
+ if (sc_reg > SCR_CONTROL)
+ return;
+ writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl)
+{
+ unsigned long mask_addr;
+ u8 mask;
+
+ mask_addr = (unsigned long) ap->host_set->mmio_base +
+ VSC_SATA_INT_MASK_OFFSET + ap->port_no;
+ mask = readb(mask_addr);
+ if (ctl & ATA_NIEN)
+ mask |= 0x80;
+ else
+ mask &= 0x7F;
+ writeb(mask, mask_addr);
+}
+
+
+static void vsc_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+ /*
+ * The only thing the ctl register is used for is SRST.
+ * That is not enabled or disabled via tf_load.
+ * However, if ATA_NIEN is changed, then we need to change the interrupt register.
+ */
+ if ((tf->ctl & ATA_NIEN) != (ap->last_ctl & ATA_NIEN)) {
+ ap->last_ctl = tf->ctl;
+ vsc_intr_mask_update(ap, tf->ctl & ATA_NIEN);
+ }
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ writew(tf->feature | (((u16)tf->hob_feature) << 8), ioaddr->feature_addr);
+ writew(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr);
+ writew(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr);
+ writew(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr);
+ writew(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr);
+ } else if (is_addr) {
+ writew(tf->feature, ioaddr->feature_addr);
+ writew(tf->nsect, ioaddr->nsect_addr);
+ writew(tf->lbal, ioaddr->lbal_addr);
+ writew(tf->lbam, ioaddr->lbam_addr);
+ writew(tf->lbah, ioaddr->lbah_addr);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE)
+ writeb(tf->device, ioaddr->device_addr);
+
+ ata_wait_idle(ap);
+}
+
+
+static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u16 nsect, lbal, lbam, lbah;
+
+ nsect = tf->nsect = readw(ioaddr->nsect_addr);
+ lbal = tf->lbal = readw(ioaddr->lbal_addr);
+ lbam = tf->lbam = readw(ioaddr->lbam_addr);
+ lbah = tf->lbah = readw(ioaddr->lbah_addr);
+ tf->device = readw(ioaddr->device_addr);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ tf->hob_feature = readb(ioaddr->error_addr);
+ tf->hob_nsect = nsect >> 8;
+ tf->hob_lbal = lbal >> 8;
+ tf->hob_lbam = lbam >> 8;
+ tf->hob_lbah = lbah >> 8;
+ }
+}
+
+
+/*
+ * vsc_sata_interrupt
+ *
+ * Read the interrupt register and process for the devices that have them pending.
+ */
+static irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct ata_host_set *host_set = dev_instance;
+ unsigned int i;
+ unsigned int handled = 0;
+ u32 int_status;
+
+ spin_lock(&host_set->lock);
+
+ int_status = readl(host_set->mmio_base + VSC_SATA_INT_STAT_OFFSET);
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ if (int_status & ((u32) 0xFF << (8 * i))) {
+ struct ata_port *ap;
+
+ ap = host_set->ports[i];
+ if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->active_tag);
+ if (qc && (!(qc->tf.ctl & ATA_NIEN)))
+ handled += ata_host_intr(ap, qc);
+ }
+ }
+ }
+
+ spin_unlock(&host_set->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+static Scsi_Host_Template vsc_sata_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .eh_strategy_handler = ata_scsi_error,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .max_sectors = ATA_MAX_SECTORS,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = ATA_DMA_BOUNDARY,
+ .slave_configure = ata_scsi_slave_config,
+ .bios_param = ata_std_bios_param,
+ .ordered_flush = 1,
+};
+
+
+static struct ata_port_operations vsc_sata_ops = {
+ .port_disable = ata_port_disable,
+ .tf_load = vsc_sata_tf_load,
+ .tf_read = vsc_sata_tf_read,
+ .exec_command = ata_exec_command,
+ .check_status = ata_check_status,
+ .dev_select = ata_std_dev_select,
+ .phy_reset = sata_phy_reset,
+ .bmdma_setup = ata_bmdma_setup,
+ .bmdma_start = ata_bmdma_start,
+ .bmdma_stop = ata_bmdma_stop,
+ .bmdma_status = ata_bmdma_status,
+ .qc_prep = ata_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
+ .eng_timeout = ata_eng_timeout,
+ .irq_handler = vsc_sata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+ .scr_read = vsc_sata_scr_read,
+ .scr_write = vsc_sata_scr_write,
+ .port_start = ata_port_start,
+ .port_stop = ata_port_stop,
+};
+
+static void __devinit vsc_sata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+ port->cmd_addr = base + VSC_SATA_TF_CMD_OFFSET;
+ port->data_addr = base + VSC_SATA_TF_DATA_OFFSET;
+ port->error_addr = base + VSC_SATA_TF_ERROR_OFFSET;
+ port->feature_addr = base + VSC_SATA_TF_FEATURE_OFFSET;
+ port->nsect_addr = base + VSC_SATA_TF_NSECT_OFFSET;
+ port->lbal_addr = base + VSC_SATA_TF_LBAL_OFFSET;
+ port->lbam_addr = base + VSC_SATA_TF_LBAM_OFFSET;
+ port->lbah_addr = base + VSC_SATA_TF_LBAH_OFFSET;
+ port->device_addr = base + VSC_SATA_TF_DEVICE_OFFSET;
+ port->status_addr = base + VSC_SATA_TF_STATUS_OFFSET;
+ port->command_addr = base + VSC_SATA_TF_COMMAND_OFFSET;
+ port->altstatus_addr = base + VSC_SATA_TF_ALTSTATUS_OFFSET;
+ port->ctl_addr = base + VSC_SATA_TF_CTL_OFFSET;
+ port->bmdma_addr = base + VSC_SATA_DMA_CMD_OFFSET;
+ port->scr_addr = base + VSC_SATA_SCR_STATUS_OFFSET;
+ writel(0, base + VSC_SATA_UP_DESCRIPTOR_OFFSET);
+ writel(0, base + VSC_SATA_UP_DATA_BUFFER_OFFSET);
+}
+
+
+static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static int printed_version;
+ struct ata_probe_ent *probe_ent = NULL;
+ unsigned long base;
+ int pci_dev_busy = 0;
+ void *mmio_base;
+ int rc;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ /*
+ * Check if we have needed resource mapped.
+ */
+ if (pci_resource_len(pdev, 0) == 0) {
+ rc = -ENODEV;
+ goto err_out;
+ }
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc) {
+ pci_dev_busy = 1;
+ goto err_out;
+ }
+
+ /*
+ * Use 32 bit DMA mask, because 64 bit address support is poor.
+ */
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc)
+ goto err_out_regions;
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (rc)
+ goto err_out_regions;
+
+ probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+ if (probe_ent == NULL) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+ memset(probe_ent, 0, sizeof(*probe_ent));
+ probe_ent->dev = pci_dev_to_dev(pdev);
+ INIT_LIST_HEAD(&probe_ent->node);
+
+ mmio_base = ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (mmio_base == NULL) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+ base = (unsigned long) mmio_base;
+
+ /*
+ * Due to a bug in the chip, the default cache line size can't be used
+ */
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80);
+
+ probe_ent->sht = &vsc_sata_sht;
+ probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET;
+ probe_ent->port_ops = &vsc_sata_ops;
+ probe_ent->n_ports = 4;
+ probe_ent->irq = pdev->irq;
+ probe_ent->irq_flags = SA_SHIRQ;
+ probe_ent->mmio_base = mmio_base;
+
+ /* We don't care much about the PIO/UDMA masks, but the core won't like us
+ * if we don't fill these
+ */
+ probe_ent->pio_mask = 0x1f;
+ probe_ent->mwdma_mask = 0x07;
+ probe_ent->udma_mask = 0x7f;
+
+ /* We have 4 ports per PCI function */
+ vsc_sata_setup_port(&probe_ent->port[0], base + 1 * VSC_SATA_PORT_OFFSET);
+ vsc_sata_setup_port(&probe_ent->port[1], base + 2 * VSC_SATA_PORT_OFFSET);
+ vsc_sata_setup_port(&probe_ent->port[2], base + 3 * VSC_SATA_PORT_OFFSET);
+ vsc_sata_setup_port(&probe_ent->port[3], base + 4 * VSC_SATA_PORT_OFFSET);
+
+ pci_set_master(pdev);
+
+ /*
+ * Config offset 0x98 is "Extended Control and Status Register 0"
+ * Default value is (1 << 28). All bits except bit 28 are reserved in
+ * DPA mode. If bit 28 is set, LED 0 reflects all ports' activity.
+ * If bit 28 is clear, each port has its own LED.
+ */
+ pci_write_config_dword(pdev, 0x98, 0);
+
+ /* FIXME: check ata_device_add return value */
+ ata_device_add(probe_ent);
+ kfree(probe_ent);
+
+ return 0;
+
+err_out_free_ent:
+ kfree(probe_ent);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+
+/*
+ * 0x1725/0x7174 is the Vitesse VSC-7174
+ * 0x8086/0x3200 is the Intel 31244, which is supposed to be identical
+ * compatibility is untested as of yet
+ */
+static struct pci_device_id vsc_sata_pci_tbl[] = {
+ { 0x1725, 0x7174, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },
+ { 0x8086, 0x3200, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },
+ { }
+};
+
+
+static struct pci_driver vsc_sata_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = vsc_sata_pci_tbl,
+ .probe = vsc_sata_init_one,
+ .remove = ata_pci_remove_one,
+};
+
+
+static int __init vsc_sata_init(void)
+{
+ return pci_module_init(&vsc_sata_pci_driver);
+}
+
+
+static void __exit vsc_sata_exit(void)
+{
+ pci_unregister_driver(&vsc_sata_pci_driver);
+}
+
+
+MODULE_AUTHOR("Jeremy Higdon");
+MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, vsc_sata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(vsc_sata_init);
+module_exit(vsc_sata_exit);
diff --git a/drivers/scsi/script_asm.pl b/drivers/scsi/script_asm.pl
new file mode 100644
index 000000000000..7d651d99afcb
--- /dev/null
+++ b/drivers/scsi/script_asm.pl
@@ -0,0 +1,984 @@
+#!/usr/bin/perl -s
+
+# NCR 53c810 script assembler
+# Sponsored by
+# iX Multiuser Multitasking Magazine
+#
+# Copyright 1993, Drew Eckhardt
+# Visionary Computing
+# (Unix and Linux consulting and custom programming)
+# drew@Colorado.EDU
+# +1 (303) 786-7975
+#
+# Support for 53c710 (via -ncr7x0_family switch) added by Richard
+# Hirst <richard@sleepie.demon.co.uk> - 15th March 1997
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You 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.
+#
+# TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+#
+
+#
+# Basically, I follow the NCR syntax documented in the NCR53c710
+# Programmer's guide, with the new instructions, registers, etc.
+# from the NCR53c810.
+#
+# Differences between this assembler and NCR's are that
+# 1. PASS, REL (data, JUMPs work fine), and the option to start a new
+# script, are unimplemented, since I didn't use them in my scripts.
+#
+# 2. I also emit a script_u.h file, which will undefine all of
+# the A_*, E_*, etc. symbols defined in the script. This
+# makes including multiple scripts in one program easier
+#
+# 3. This is a single pass assembler, which only emits
+# .h files.
+#
+
+
+# XXX - set these with command line options
+$debug = 0; # Print general debugging messages
+$debug_external = 0; # Print external/forward reference messages
+$list_in_array = 1; # Emit original SCRIPTS assembler in comments in
+ # script.h
+#$prefix; # (set by perl -s)
+ # define all arrays having this prefix so we
+ # don't have name space collisions after
+ # assembling this file in different ways for
+ # different host adapters
+
+# Constants
+
+
+# Table of the SCSI phase encodings
+%scsi_phases = (
+ 'DATA_OUT', 0x00_00_00_00, 'DATA_IN', 0x01_00_00_00, 'CMD', 0x02_00_00_00,
+ 'STATUS', 0x03_00_00_00, 'MSG_OUT', 0x06_00_00_00, 'MSG_IN', 0x07_00_00_00
+);
+
+# XXX - replace references to the *_810 constants with general constants
+# assigned at compile time based on chip type.
+
+# Table of operator encodings
+# XXX - NCR53c710 only implements
+# move (nop) = 0x00_00_00_00
+# or = 0x02_00_00_00
+# and = 0x04_00_00_00
+# add = 0x06_00_00_00
+
+if ($ncr7x0_family) {
+ %operators = (
+ '|', 0x02_00_00_00, 'OR', 0x02_00_00_00,
+ '&', 0x04_00_00_00, 'AND', 0x04_00_00_00,
+ '+', 0x06_00_00_00
+ );
+}
+else {
+ %operators = (
+ 'SHL', 0x01_00_00_00,
+ '|', 0x02_00_00_00, 'OR', 0x02_00_00_00,
+ 'XOR', 0x03_00_00_00,
+ '&', 0x04_00_00_00, 'AND', 0x04_00_00_00,
+ 'SHR', 0x05_00_00_00,
+ # Note : low bit of the operator bit should be set for add with
+ # carry.
+ '+', 0x06_00_00_00
+ );
+}
+
+# Table of register addresses
+
+if ($ncr7x0_family) {
+ %registers = (
+ 'SCNTL0', 0, 'SCNTL1', 1, 'SDID', 2, 'SIEN', 3,
+ 'SCID', 4, 'SXFER', 5, 'SODL', 6, 'SOCL', 7,
+ 'SFBR', 8, 'SIDL', 9, 'SBDL', 10, 'SBCL', 11,
+ 'DSTAT', 12, 'SSTAT0', 13, 'SSTAT1', 14, 'SSTAT2', 15,
+ 'DSA0', 16, 'DSA1', 17, 'DSA2', 18, 'DSA3', 19,
+ 'CTEST0', 20, 'CTEST1', 21, 'CTEST2', 22, 'CTEST3', 23,
+ 'CTEST4', 24, 'CTEST5', 25, 'CTEST6', 26, 'CTEST7', 27,
+ 'TEMP0', 28, 'TEMP1', 29, 'TEMP2', 30, 'TEMP3', 31,
+ 'DFIFO', 32, 'ISTAT', 33, 'CTEST8', 34, 'LCRC', 35,
+ 'DBC0', 36, 'DBC1', 37, 'DBC2', 38, 'DCMD', 39,
+ 'DNAD0', 40, 'DNAD1', 41, 'DNAD2', 42, 'DNAD3', 43,
+ 'DSP0', 44, 'DSP1', 45, 'DSP2', 46, 'DSP3', 47,
+ 'DSPS0', 48, 'DSPS1', 49, 'DSPS2', 50, 'DSPS3', 51,
+ 'SCRATCH0', 52, 'SCRATCH1', 53, 'SCRATCH2', 54, 'SCRATCH3', 55,
+ 'DMODE', 56, 'DIEN', 57, 'DWT', 58, 'DCNTL', 59,
+ 'ADDER0', 60, 'ADDER1', 61, 'ADDER2', 62, 'ADDER3', 63,
+ );
+}
+else {
+ %registers = (
+ 'SCNTL0', 0, 'SCNTL1', 1, 'SCNTL2', 2, 'SCNTL3', 3,
+ 'SCID', 4, 'SXFER', 5, 'SDID', 6, 'GPREG', 7,
+ 'SFBR', 8, 'SOCL', 9, 'SSID', 10, 'SBCL', 11,
+ 'DSTAT', 12, 'SSTAT0', 13, 'SSTAT1', 14, 'SSTAT2', 15,
+ 'DSA0', 16, 'DSA1', 17, 'DSA2', 18, 'DSA3', 19,
+ 'ISTAT', 20,
+ 'CTEST0', 24, 'CTEST1', 25, 'CTEST2', 26, 'CTEST3', 27,
+ 'TEMP0', 28, 'TEMP1', 29, 'TEMP2', 30, 'TEMP3', 31,
+ 'DFIFO', 32, 'CTEST4', 33, 'CTEST5', 34, 'CTEST6', 35,
+ 'DBC0', 36, 'DBC1', 37, 'DBC2', 38, 'DCMD', 39,
+ 'DNAD0', 40, 'DNAD1', 41, 'DNAD2', 42, 'DNAD3', 43,
+ 'DSP0', 44, 'DSP1', 45, 'DSP2', 46, 'DSP3', 47,
+ 'DSPS0', 48, 'DSPS1', 49, 'DSPS2', 50, 'DSPS3', 51,
+ 'SCRATCH0', 52, 'SCRATCH1', 53, 'SCRATCH2', 54, 'SCRATCH3', 55,
+ 'SCRATCHA0', 52, 'SCRATCHA1', 53, 'SCRATCHA2', 54, 'SCRATCHA3', 55,
+ 'DMODE', 56, 'DIEN', 57, 'DWT', 58, 'DCNTL', 59,
+ 'ADDER0', 60, 'ADDER1', 61, 'ADDER2', 62, 'ADDER3', 63,
+ 'SIEN0', 64, 'SIEN1', 65, 'SIST0', 66, 'SIST1', 67,
+ 'SLPAR', 68, 'MACNTL', 70, 'GPCNTL', 71,
+ 'STIME0', 72, 'STIME1', 73, 'RESPID', 74,
+ 'STEST0', 76, 'STEST1', 77, 'STEST2', 78, 'STEST3', 79,
+ 'SIDL', 80,
+ 'SODL', 84,
+ 'SBDL', 88,
+ 'SCRATCHB0', 92, 'SCRATCHB1', 93, 'SCRATCHB2', 94, 'SCRATCHB3', 95
+ );
+}
+
+# Parsing regular expressions
+$identifier = '[A-Za-z_][A-Za-z_0-9]*';
+$decnum = '-?\\d+';
+$hexnum = '0[xX][0-9A-Fa-f]+';
+$constant = "$hexnum|$decnum";
+
+# yucky - since we can't control grouping of # $constant, we need to
+# expand out each alternative for $value.
+
+$value = "$identifier|$identifier\\s*[+\-]\\s*$decnum|".
+ "$identifier\\s*[+-]\s*$hexnum|$constant";
+
+print STDERR "value regex = $value\n" if ($debug);
+
+$phase = join ('|', keys %scsi_phases);
+print STDERR "phase regex = $phase\n" if ($debug);
+$register = join ('|', keys %registers);
+
+# yucky - since %operators includes meta-characters which must
+# be escaped, I can't use the join() trick I used for the register
+# regex
+
+if ($ncr7x0_family) {
+ $operator = '\||OR|AND|\&|\+';
+}
+else {
+ $operator = '\||OR|AND|XOR|\&|\+';
+}
+
+# Global variables
+
+%symbol_values = (%registers) ; # Traditional symbol table
+
+%symbol_references = () ; # Table of symbol references, where
+ # the index is the symbol name,
+ # and the contents a white space
+ # delimited list of address,size
+ # tuples where size is in bytes.
+
+@code = (); # Array of 32 bit words for SIOP
+
+@entry = (); # Array of entry point names
+
+@label = (); # Array of label names
+
+@absolute = (); # Array of absolute names
+
+@relative = (); # Array of relative names
+
+@external = (); # Array of external names
+
+$address = 0; # Address of current instruction
+
+$lineno = 0; # Line number we are parsing
+
+$output = 'script.h'; # Output file
+$outputu = 'scriptu.h';
+
+# &patch ($address, $offset, $length, $value) patches $code[$address]
+# so that the $length bytes at $offset have $value added to
+# them.
+
+@inverted_masks = (0x00_00_00_00, 0x00_00_00_ff, 0x00_00_ff_ff, 0x00_ff_ff_ff,
+ 0xff_ff_ff_ff);
+
+sub patch {
+ local ($address, $offset, $length, $value) = @_;
+ if ($debug) {
+ print STDERR "Patching $address at offset $offset, length $length to $value\n";
+ printf STDERR "Old code : %08x\n", $code[$address];
+ }
+
+ $mask = ($inverted_masks[$length] << ($offset * 8));
+
+ $code[$address] = ($code[$address] & ~$mask) |
+ (($code[$address] & $mask) + ($value << ($offset * 8)) &
+ $mask);
+
+ printf STDERR "New code : %08x\n", $code[$address] if ($debug);
+}
+
+# &parse_value($value, $word, $offset, $length) where $value is
+# an identifier or constant, $word is the word offset relative to
+# $address, $offset is the starting byte within that word, and
+# $length is the length of the field in bytes.
+#
+# Side effects are that the bytes are combined into the @code array
+# relative to $address, and that the %symbol_references table is
+# updated as appropriate.
+
+sub parse_value {
+ local ($value, $word, $offset, $length) = @_;
+ local ($tmp);
+
+ $symbol = '';
+
+ if ($value =~ /^REL\s*\(\s*($identifier)\s*\)\s*(.*)/i) {
+ $relative = 'REL';
+ $symbol = $1;
+ $value = $2;
+print STDERR "Relative reference $symbol\n" if ($debug);
+ } elsif ($value =~ /^($identifier)\s*(.*)/) {
+ $relative = 'ABS';
+ $symbol = $1;
+ $value = $2;
+print STDERR "Absolute reference $symbol\n" if ($debug);
+ }
+
+ if ($symbol ne '') {
+print STDERR "Referencing symbol $1, length = $length in $_\n" if ($debug);
+ $tmp = ($address + $word) * 4 + $offset;
+ if ($symbol_references{$symbol} ne undef) {
+ $symbol_references{$symbol} =
+ "$symbol_references{$symbol} $relative,$tmp,$length";
+ } else {
+ if (!defined($symbol_values{$symbol})) {
+print STDERR "forward $1\n" if ($debug_external);
+ $forward{$symbol} = "line $lineno : $_";
+ }
+ $symbol_references{$symbol} = "$relative,$tmp,$length";
+ }
+ }
+
+ $value = eval $value;
+ &patch ($address + $word, $offset, $length, $value);
+}
+
+# &parse_conditional ($conditional) where $conditional is the conditional
+# clause from a transfer control instruction (RETURN, CALL, JUMP, INT).
+
+sub parse_conditional {
+ local ($conditional) = @_;
+ if ($conditional =~ /^\s*(IF|WHEN)\s*(.*)/i) {
+ $if = $1;
+ $conditional = $2;
+ if ($if =~ /WHEN/i) {
+ $allow_atn = 0;
+ $code[$address] |= 0x00_01_00_00;
+ $allow_atn = 0;
+ print STDERR "$0 : parsed WHEN\n" if ($debug);
+ } else {
+ $allow_atn = 1;
+ print STDERR "$0 : parsed IF\n" if ($debug);
+ }
+ } else {
+ die "$0 : syntax error in line $lineno : $_
+ expected IF or WHEN
+";
+ }
+
+ if ($conditional =~ /^NOT\s+(.*)$/i) {
+ $not = 'NOT ';
+ $other = 'OR';
+ $conditional = $1;
+ print STDERR "$0 : parsed NOT\n" if ($debug);
+ } else {
+ $code[$address] |= 0x00_08_00_00;
+ $not = '';
+ $other = 'AND'
+ }
+
+ $need_data = 0;
+ if ($conditional =~ /^ATN\s*(.*)/i) {#
+ die "$0 : syntax error in line $lineno : $_
+ WHEN conditional is incompatible with ATN
+" if (!$allow_atn);
+ $code[$address] |= 0x00_02_00_00;
+ $conditional = $1;
+ print STDERR "$0 : parsed ATN\n" if ($debug);
+ } elsif ($conditional =~ /^($phase)\s*(.*)/i) {
+ $phase_index = "\U$1\E";
+ $p = $scsi_phases{$phase_index};
+ $code[$address] |= $p | 0x00_02_00_00;
+ $conditional = $2;
+ print STDERR "$0 : parsed phase $phase_index\n" if ($debug);
+ } else {
+ $other = '';
+ $need_data = 1;
+ }
+
+print STDERR "Parsing conjunction, expecting $other\n" if ($debug);
+ if ($conditional =~ /^(AND|OR)\s*(.*)/i) {
+ $conjunction = $1;
+ $conditional = $2;
+ $need_data = 1;
+ die "$0 : syntax error in line $lineno : $_
+ Illegal use of $1. Valid uses are
+ ".$not."<phase> $1 data
+ ".$not."ATN $1 data
+" if ($other eq '');
+ die "$0 : syntax error in line $lineno : $_
+ Illegal use of $conjunction. Valid syntaxes are
+ NOT <phase>|ATN OR data
+ <phase>|ATN AND data
+" if ($conjunction !~ /\s*$other\s*/i);
+ print STDERR "$0 : parsed $1\n" if ($debug);
+ }
+
+ if ($need_data) {
+print STDERR "looking for data in $conditional\n" if ($debug);
+ if ($conditional=~ /^($value)\s*(.*)/i) {
+ $code[$address] |= 0x00_04_00_00;
+ $conditional = $2;
+ &parse_value($1, 0, 0, 1);
+ print STDERR "$0 : parsed data\n" if ($debug);
+ } else {
+ die "$0 : syntax error in line $lineno : $_
+ expected <data>.
+";
+ }
+ }
+
+ if ($conditional =~ /^\s*,\s*(.*)/) {
+ $conditional = $1;
+ if ($conditional =~ /^AND\s\s*MASK\s\s*($value)\s*(.*)/i) {
+ &parse_value ($1, 0, 1, 1);
+ print STDERR "$0 parsed AND MASK $1\n" if ($debug);
+ die "$0 : syntax error in line $lineno : $_
+ expected end of line, not \"$2\"
+" if ($2 ne '');
+ } else {
+ die "$0 : syntax error in line $lineno : $_
+ expected \",AND MASK <data>\", not \"$2\"
+";
+ }
+ } elsif ($conditional !~ /^\s*$/) {
+ die "$0 : syntax error in line $lineno : $_
+ expected end of line" . (($need_data) ? " or \"AND MASK <data>\"" : "") . "
+ not \"$conditional\"
+";
+ }
+}
+
+# Parse command line
+$output = shift;
+$outputu = shift;
+
+
+# Main loop
+while (<STDIN>) {
+ $lineno = $lineno + 1;
+ $list[$address] = $list[$address].$_;
+ s/;.*$//; # Strip comments
+
+
+ chop; # Leave new line out of error messages
+
+# Handle symbol definitions of the form label:
+ if (/^\s*($identifier)\s*:(.*)/) {
+ if (!defined($symbol_values{$1})) {
+ $symbol_values{$1} = $address * 4; # Address is an index into
+ delete $forward{$1}; # an array of longs
+ push (@label, $1);
+ $_ = $2;
+ } else {
+ die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
+ }
+ }
+
+# Handle symbol definitions of the form ABSOLUTE or RELATIVE identifier =
+# value
+ if (/^\s*(ABSOLUTE|RELATIVE)\s+(.*)/i) {
+ $is_absolute = $1;
+ $rest = $2;
+ foreach $rest (split (/\s*,\s*/, $rest)) {
+ if ($rest =~ /^($identifier)\s*=\s*($constant)\s*$/) {
+ local ($id, $cnst) = ($1, $2);
+ if ($symbol_values{$id} eq undef) {
+ $symbol_values{$id} = eval $cnst;
+ delete $forward{$id};
+ if ($is_absolute =~ /ABSOLUTE/i) {
+ push (@absolute , $id);
+ } else {
+ push (@relative, $id);
+ }
+ } else {
+ die "$0 : redefinition of symbol $id in line $lineno : $_\n";
+ }
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected <identifier> = <value>
+";
+ }
+ }
+ } elsif (/^\s*EXTERNAL\s+(.*)/i) {
+ $externals = $1;
+ foreach $external (split (/,/,$externals)) {
+ if ($external =~ /\s*($identifier)\s*$/) {
+ $external = $1;
+ push (@external, $external);
+ delete $forward{$external};
+ if (defined($symbol_values{$external})) {
+ die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
+ }
+ $symbol_values{$external} = $external;
+print STDERR "defined external $1 to $external\n" if ($debug_external);
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected <identifier>, got $external
+";
+ }
+ }
+# Process ENTRY identifier declarations
+ } elsif (/^\s*ENTRY\s+(.*)/i) {
+ if ($1 =~ /^($identifier)\s*$/) {
+ push (@entry, $1);
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected ENTRY <identifier>
+";
+ }
+# Process MOVE length, address, WITH|WHEN phase instruction
+ } elsif (/^\s*MOVE\s+(.*)/i) {
+ $rest = $1;
+ if ($rest =~ /^FROM\s+($value)\s*,\s*(WITH|WHEN)\s+($phase)\s*$/i) {
+ $transfer_addr = $1;
+ $with_when = $2;
+ $scsi_phase = $3;
+print STDERR "Parsing MOVE FROM $transfer_addr, $with_when $3\n" if ($debug);
+ $code[$address] = 0x18_00_00_00 | (($with_when =~ /WITH/i) ?
+ 0x00_00_00_00 : 0x08_00_00_00) | $scsi_phases{$scsi_phase};
+ &parse_value ($transfer_addr, 1, 0, 4);
+ $address += 2;
+ } elsif ($rest =~ /^($value)\s*,\s*(PTR\s+|)($value)\s*,\s*(WITH|WHEN)\s+($phase)\s*$/i) {
+ $transfer_len = $1;
+ $ptr = $2;
+ $transfer_addr = $3;
+ $with_when = $4;
+ $scsi_phase = $5;
+ $code[$address] = (($with_when =~ /WITH/i) ? 0x00_00_00_00 :
+ 0x08_00_00_00) | (($ptr =~ /PTR/i) ? (1 << 29) : 0) |
+ $scsi_phases{$scsi_phase};
+ &parse_value ($transfer_len, 0, 0, 3);
+ &parse_value ($transfer_addr, 1, 0, 4);
+ $address += 2;
+ } elsif ($rest =~ /^MEMORY\s+(.*)/i) {
+ $rest = $1;
+ $code[$address] = 0xc0_00_00_00;
+ if ($rest =~ /^($value)\s*,\s*($value)\s*,\s*($value)\s*$/) {
+ $count = $1;
+ $source = $2;
+ $dest = $3;
+print STDERR "Parsing MOVE MEMORY $count, $source, $dest\n" if ($debug);
+ &parse_value ($count, 0, 0, 3);
+ &parse_value ($source, 1, 0, 4);
+ &parse_value ($dest, 2, 0, 4);
+printf STDERR "Move memory instruction = %08x,%08x,%08x\n",
+ $code[$address], $code[$address+1], $code[$address +2] if
+ ($debug);
+ $address += 3;
+
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected <count>, <source>, <destination>
+"
+ }
+ } elsif ($1 =~ /^(.*)\s+(TO|SHL|SHR)\s+(.*)/i) {
+print STDERR "Parsing register to register move\n" if ($debug);
+ $src = $1;
+ $op = "\U$2\E";
+ $rest = $3;
+
+ $code[$address] = 0x40_00_00_00;
+
+ $force = ($op !~ /TO/i);
+
+
+print STDERR "Forcing register source \n" if ($force && $debug);
+
+ if (!$force && $src =~
+ /^($register)\s+(-|$operator)\s+($value)\s*$/i) {
+print STDERR "register operand data8 source\n" if ($debug);
+ $src_reg = "\U$1\E";
+ $op = "\U$2\E";
+ if ($op ne '-') {
+ $data8 = $3;
+ } else {
+ die "- is not implemented yet.\n"
+ }
+ } elsif ($src =~ /^($register)\s*$/i) {
+print STDERR "register source\n" if ($debug);
+ $src_reg = "\U$1\E";
+ # Encode register to register move as a register | 0
+ # move to register.
+ if (!$force) {
+ $op = '|';
+ }
+ $data8 = 0;
+ } elsif (!$force && $src =~ /^($value)\s*$/i) {
+print STDERR "data8 source\n" if ($debug);
+ $src_reg = undef;
+ $op = 'NONE';
+ $data8 = $1;
+ } else {
+ if (!$force) {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected <register>
+ <data8>
+ <register> <operand> <data8>
+";
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected <register>
+";
+ }
+ }
+ if ($rest =~ /^($register)\s*(.*)$/i) {
+ $dst_reg = "\U$1\E";
+ $rest = $2;
+ } else {
+ die
+"$0 : syntax error in $lineno : $_
+ expected <register>, got $rest
+";
+ }
+
+ if ($rest =~ /^WITH\s+CARRY\s*(.*)/i) {
+ $rest = $1;
+ if ($op eq '+') {
+ $code[$address] |= 0x01_00_00_00;
+ } else {
+ die
+"$0 : syntax error in $lineno : $_
+ WITH CARRY option is incompatible with the $op operator.
+";
+ }
+ }
+
+ if ($rest !~ /^\s*$/) {
+ die
+"$0 : syntax error in $lineno : $_
+ Expected end of line, got $rest
+";
+ }
+
+ print STDERR "source = $src_reg, data = $data8 , destination = $dst_reg\n"
+ if ($debug);
+ # Note that Move data8 to reg is encoded as a read-modify-write
+ # instruction.
+ if (($src_reg eq undef) || ($src_reg eq $dst_reg)) {
+ $code[$address] |= 0x38_00_00_00 |
+ ($registers{$dst_reg} << 16);
+ } elsif ($dst_reg =~ /SFBR/i) {
+ $code[$address] |= 0x30_00_00_00 |
+ ($registers{$src_reg} << 16);
+ } elsif ($src_reg =~ /SFBR/i) {
+ $code[$address] |= 0x28_00_00_00 |
+ ($registers{$dst_reg} << 16);
+ } else {
+ die
+"$0 : Illegal combination of registers in line $lineno : $_
+ Either source and destination registers must be the same,
+ or either source or destination register must be SFBR.
+";
+ }
+
+ $code[$address] |= $operators{$op};
+
+ &parse_value ($data8, 0, 1, 1);
+ $code[$address] |= $operators{$op};
+ $code[$address + 1] = 0x00_00_00_00;# Reserved
+ $address += 2;
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected (initiator) <length>, <address>, WHEN <phase>
+ (target) <length>, <address>, WITH <phase>
+ MEMORY <length>, <source>, <destination>
+ <expression> TO <register>
+";
+ }
+# Process SELECT {ATN|} id, fail_address
+ } elsif (/^\s*(SELECT|RESELECT)\s+(.*)/i) {
+ $rest = $2;
+ if ($rest =~ /^(ATN|)\s*($value)\s*,\s*($identifier)\s*$/i) {
+ $atn = $1;
+ $id = $2;
+ $alt_addr = $3;
+ $code[$address] = 0x40_00_00_00 |
+ (($atn =~ /ATN/i) ? 0x01_00_00_00 : 0);
+ $code[$address + 1] = 0x00_00_00_00;
+ &parse_value($id, 0, 2, 1);
+ &parse_value($alt_addr, 1, 0, 4);
+ $address += 2;
+ } elsif ($rest =~ /^(ATN|)\s*FROM\s+($value)\s*,\s*($identifier)\s*$/i) {
+ $atn = $1;
+ $addr = $2;
+ $alt_addr = $3;
+ $code[$address] = 0x42_00_00_00 |
+ (($atn =~ /ATN/i) ? 0x01_00_00_00 : 0);
+ $code[$address + 1] = 0x00_00_00_00;
+ &parse_value($addr, 0, 0, 3);
+ &parse_value($alt_addr, 1, 0, 4);
+ $address += 2;
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected SELECT id, alternate_address or
+ SELECT FROM address, alternate_address or
+ RESELECT id, alternate_address or
+ RESELECT FROM address, alternate_address
+";
+ }
+ } elsif (/^\s*WAIT\s+(.*)/i) {
+ $rest = $1;
+print STDERR "Parsing WAIT $rest\n" if ($debug);
+ if ($rest =~ /^DISCONNECT\s*$/i) {
+ $code[$address] = 0x48_00_00_00;
+ $code[$address + 1] = 0x00_00_00_00;
+ $address += 2;
+ } elsif ($rest =~ /^(RESELECT|SELECT)\s+($identifier)\s*$/i) {
+ $alt_addr = $2;
+ $code[$address] = 0x50_00_00_00;
+ &parse_value ($alt_addr, 1, 0, 4);
+ $address += 2;
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected (initiator) WAIT DISCONNECT or
+ (initiator) WAIT RESELECT alternate_address or
+ (target) WAIT SELECT alternate_address
+";
+ }
+# Handle SET and CLEAR instructions. Note that we should also do something
+# with this syntax to set target mode.
+ } elsif (/^\s*(SET|CLEAR)\s+(.*)/i) {
+ $set = $1;
+ $list = $2;
+ $code[$address] = ($set =~ /SET/i) ? 0x58_00_00_00 :
+ 0x60_00_00_00;
+ foreach $arg (split (/\s+AND\s+/i,$list)) {
+ if ($arg =~ /ATN/i) {
+ $code[$address] |= 0x00_00_00_08;
+ } elsif ($arg =~ /ACK/i) {
+ $code[$address] |= 0x00_00_00_40;
+ } elsif ($arg =~ /TARGET/i) {
+ $code[$address] |= 0x00_00_02_00;
+ } elsif ($arg =~ /CARRY/i) {
+ $code[$address] |= 0x00_00_04_00;
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected $set followed by a AND delimited list of one or
+ more strings from the list ACK, ATN, CARRY, TARGET.
+";
+ }
+ }
+ $code[$address + 1] = 0x00_00_00_00;
+ $address += 2;
+ } elsif (/^\s*(JUMP|CALL|INT)\s+(.*)/i) {
+ $instruction = $1;
+ $rest = $2;
+ if ($instruction =~ /JUMP/i) {
+ $code[$address] = 0x80_00_00_00;
+ } elsif ($instruction =~ /CALL/i) {
+ $code[$address] = 0x88_00_00_00;
+ } else {
+ $code[$address] = 0x98_00_00_00;
+ }
+print STDERR "parsing JUMP, rest = $rest\n" if ($debug);
+
+# Relative jump.
+ if ($rest =~ /^(REL\s*\(\s*$identifier\s*\))\s*(.*)/i) {
+ $addr = $1;
+ $rest = $2;
+print STDERR "parsing JUMP REL, addr = $addr, rest = $rest\n" if ($debug);
+ $code[$address] |= 0x00_80_00_00;
+ &parse_value($addr, 1, 0, 4);
+# Absolute jump, requires no more gunk
+ } elsif ($rest =~ /^($value)\s*(.*)/) {
+ $addr = $1;
+ $rest = $2;
+ &parse_value($addr, 1, 0, 4);
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected <address> or REL (address)
+";
+ }
+
+ if ($rest =~ /^,\s*(.*)/) {
+ &parse_conditional($1);
+ } elsif ($rest =~ /^\s*$/) {
+ $code[$address] |= (1 << 19);
+ } else {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected , <conditional> or end of line, got $1
+";
+ }
+
+ $address += 2;
+ } elsif (/^\s*(RETURN|INTFLY)\s*(.*)/i) {
+ $instruction = $1;
+ $conditional = $2;
+print STDERR "Parsing $instruction\n" if ($debug);
+ $code[$address] = ($instruction =~ /RETURN/i) ? 0x90_00_00_00 :
+ 0x98_10_00_00;
+ if ($conditional =~ /^,\s*(.*)/) {
+ $conditional = $1;
+ &parse_conditional ($conditional);
+ } elsif ($conditional !~ /^\s*$/) {
+ die
+"$0 : syntax error in line $lineno : $_
+ expected , <conditional>
+";
+ } else {
+ $code[$address] |= 0x00_08_00_00;
+ }
+
+ $code[$address + 1] = 0x00_00_00_00;
+ $address += 2;
+ } elsif (/^\s*DISCONNECT\s*$/) {
+ $code[$address] = 0x48_00_00_00;
+ $code[$address + 1] = 0x00_00_00_00;
+ $address += 2;
+# I'm not sure that I should be including this extension, but
+# what the hell?
+ } elsif (/^\s*NOP\s*$/i) {
+ $code[$address] = 0x80_88_00_00;
+ $code[$address + 1] = 0x00_00_00_00;
+ $address += 2;
+# Ignore lines consisting entirely of white space
+ } elsif (/^\s*$/) {
+ } else {
+ die
+"$0 : syntax error in line $lineno: $_
+ expected label:, ABSOLUTE, CLEAR, DISCONNECT, EXTERNAL, MOVE, RESELECT,
+ SELECT SET, or WAIT
+";
+ }
+}
+
+# Fill in label references
+
+@undefined = keys %forward;
+if ($#undefined >= 0) {
+ print STDERR "Undefined symbols : \n";
+ foreach $undef (@undefined) {
+ print STDERR "$undef in $forward{$undef}\n";
+ }
+ exit 1;
+}
+
+@label_patches = ();
+
+@external_patches = ();
+
+@absolute = sort @absolute;
+
+foreach $i (@absolute) {
+ foreach $j (split (/\s+/,$symbol_references{$i})) {
+ $j =~ /(REL|ABS),(.*),(.*)/;
+ $type = $1;
+ $address = $2;
+ $length = $3;
+ die
+"$0 : $symbol $i has invalid relative reference at address $address,
+ size $length\n"
+ if ($type eq 'REL');
+
+ &patch ($address / 4, $address % 4, $length, $symbol_values{$i});
+ }
+}
+
+foreach $external (@external) {
+print STDERR "checking external $external \n" if ($debug_external);
+ if ($symbol_references{$external} ne undef) {
+ for $reference (split(/\s+/,$symbol_references{$external})) {
+ $reference =~ /(REL|ABS),(.*),(.*)/;
+ $type = $1;
+ $address = $2;
+ $length = $3;
+
+ die
+"$0 : symbol $label is external, has invalid relative reference at $address,
+ size $length\n"
+ if ($type eq 'REL');
+
+ die
+"$0 : symbol $label has invalid reference at $address, size $length\n"
+ if ((($address % 4) !=0) || ($length != 4));
+
+ $symbol = $symbol_values{$external};
+ $add = $code[$address / 4];
+ if ($add eq 0) {
+ $code[$address / 4] = $symbol;
+ } else {
+ $add = sprintf ("0x%08x", $add);
+ $code[$address / 4] = "$symbol + $add";
+ }
+
+print STDERR "referenced external $external at $1\n" if ($debug_external);
+ }
+ }
+}
+
+foreach $label (@label) {
+ if ($symbol_references{$label} ne undef) {
+ for $reference (split(/\s+/,$symbol_references{$label})) {
+ $reference =~ /(REL|ABS),(.*),(.*)/;
+ $type = $1;
+ $address = $2;
+ $length = $3;
+
+ if ((($address % 4) !=0) || ($length != 4)) {
+ die "$0 : symbol $label has invalid reference at $1, size $2\n";
+ }
+
+ if ($type eq 'ABS') {
+ $code[$address / 4] += $symbol_values{$label};
+ push (@label_patches, $address / 4);
+ } else {
+#
+# - The address of the reference should be in the second and last word
+# of an instruction
+# - Relative jumps, etc. are relative to the DSP of the _next_ instruction
+#
+# So, we need to add four to the address of the reference, to get
+# the address of the next instruction, when computing the reference.
+
+ $tmp = $symbol_values{$label} -
+ ($address + 4);
+ die
+# Relative addressing is limited to 24 bits.
+"$0 : symbol $label is too far ($tmp) from $address to reference as
+ relative/\n" if (($tmp >= 0x80_00_00) || ($tmp < -0x80_00_00));
+ $code[$address / 4] = $tmp & 0x00_ff_ff_ff;
+ }
+ }
+ }
+}
+
+# Output SCRIPT[] array, one instruction per line. Optionally
+# print the original code too.
+
+open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n";
+open (OUTPUTU, ">$outputu") || die "$0 : can't open $outputu for writing\n";
+
+($_ = $0) =~ s:.*/::;
+print OUTPUT "/* DO NOT EDIT - Generated automatically by ".$_." */\n";
+print OUTPUT "static u32 ".$prefix."SCRIPT[] = {\n";
+$instructions = 0;
+for ($i = 0; $i < $#code; ) {
+ if ($list_in_array) {
+ printf OUTPUT "/*\n$list[$i]\nat 0x%08x : */", $i;
+ }
+ printf OUTPUT "\t0x%08x,", $code[$i];
+ printf STDERR "Address $i = %x\n", $code[$i] if ($debug);
+ if ($code[$i + 1] =~ /\s*($identifier)(.*)$/) {
+ push (@external_patches, $i+1, $1);
+ printf OUTPUT "0%s,", $2
+ } else {
+ printf OUTPUT "0x%08x,",$code[$i+1];
+ }
+
+ if (($code[$i] & 0xff_00_00_00) == 0xc0_00_00_00) {
+ if ($code[$i + 2] =~ /$identifier/) {
+ push (@external_patches, $i+2, $code[$i+2]);
+ printf OUTPUT "0,\n";
+ } else {
+ printf OUTPUT "0x%08x,\n",$code[$i+2];
+ }
+ $i += 3;
+ } else {
+ printf OUTPUT "\n";
+ $i += 2;
+ }
+ $instructions += 1;
+}
+print OUTPUT "};\n\n";
+
+foreach $i (@absolute) {
+ printf OUTPUT "#define A_$i\t0x%08x\n", $symbol_values{$i};
+ if (defined($prefix) && $prefix ne '') {
+ printf OUTPUT "#define A_".$i."_used ".$prefix."A_".$i."_used\n";
+ printf OUTPUTU "#undef A_".$i."_used\n";
+ }
+ printf OUTPUTU "#undef A_$i\n";
+
+ printf OUTPUT "static u32 A_".$i."_used\[\] __attribute((unused)) = {\n";
+printf STDERR "$i is used $symbol_references{$i}\n" if ($debug);
+ foreach $j (split (/\s+/,$symbol_references{$i})) {
+ $j =~ /(ABS|REL),(.*),(.*)/;
+ if ($1 eq 'ABS') {
+ $address = $2;
+ $length = $3;
+ printf OUTPUT "\t0x%08x,\n", $address / 4;
+ }
+ }
+ printf OUTPUT "};\n\n";
+}
+
+foreach $i (sort @entry) {
+ printf OUTPUT "#define Ent_$i\t0x%08x\n", $symbol_values{$i};
+ printf OUTPUTU "#undef Ent_$i\n", $symbol_values{$i};
+}
+
+#
+# NCR assembler outputs label patches in the form of indices into
+# the code.
+#
+printf OUTPUT "static u32 ".$prefix."LABELPATCHES[] __attribute((unused)) = {\n";
+for $patch (sort {$a <=> $b} @label_patches) {
+ printf OUTPUT "\t0x%08x,\n", $patch;
+}
+printf OUTPUT "};\n\n";
+
+$num_external_patches = 0;
+printf OUTPUT "static struct {\n\tu32\toffset;\n\tvoid\t\t*address;\n".
+ "} ".$prefix."EXTERNAL_PATCHES[] __attribute((unused)) = {\n";
+while ($ident = pop(@external_patches)) {
+ $off = pop(@external_patches);
+ printf OUTPUT "\t{0x%08x, &%s},\n", $off, $ident;
+ ++$num_external_patches;
+}
+printf OUTPUT "};\n\n";
+
+printf OUTPUT "static u32 ".$prefix."INSTRUCTIONS __attribute((unused))\t= %d;\n",
+ $instructions;
+printf OUTPUT "static u32 ".$prefix."PATCHES __attribute((unused))\t= %d;\n",
+ $#label_patches+1;
+printf OUTPUT "static u32 ".$prefix."EXTERNAL_PATCHES_LEN __attribute((unused))\t= %d;\n",
+ $num_external_patches;
+close OUTPUT;
+close OUTPUTU;
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
new file mode 100644
index 000000000000..2e7ab3ab0993
--- /dev/null
+++ b/drivers/scsi/scsi.c
@@ -0,0 +1,1375 @@
+/*
+ * scsi.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
+ * Copyright (C) 2002, 2003 Christoph Hellwig
+ *
+ * generic mid-level SCSI driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Bug correction thanks go to :
+ * Rik Faith <faith@cs.unc.edu>
+ * Tommy Thorn <tthorn>
+ * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
+ *
+ * Modified by Eric Youngdale eric@andante.org or ericy@gnu.ai.mit.edu to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Native multichannel, wide scsi, /proc/scsi and hot plugging
+ * support added by Michael Neuffer <mike@i-connect.net>
+ *
+ * Added request_module("scsi_hostadapter") for kerneld:
+ * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/modprobe.conf)
+ * Bjorn Ekwall <bj0rn@blox.se>
+ * (changed to kmod)
+ *
+ * Major improvements to the timeout, abort, and reset processing,
+ * as well as performance modifications for large queue depths by
+ * Leonard N. Zubkoff <lnz@dandelion.com>
+ *
+ * Converted cli() code to spinlocks, Ingo Molnar
+ *
+ * Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
+ *
+ * out_of_space hacks, D. Gilbert (dpg) 990608
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/kmod.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_request.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+
+/*
+ * Definitions and constants.
+ */
+
+#define MIN_RESET_DELAY (2*HZ)
+
+/* Do not call reset on error if we just did a reset within 15 sec. */
+#define MIN_RESET_PERIOD (15*HZ)
+
+/*
+ * Macro to determine the size of SCSI command. This macro takes vendor
+ * unique commands into account. SCSI commands in groups 6 and 7 are
+ * vendor unique and we will depend upon the command length being
+ * supplied correctly in cmd_len.
+ */
+#define CDB_SIZE(cmd) (((((cmd)->cmnd[0] >> 5) & 7) < 6) ? \
+ COMMAND_SIZE((cmd)->cmnd[0]) : (cmd)->cmd_len)
+
+/*
+ * Note - the initial logging level can be set here to log events at boot time.
+ * After the system is up, you may enable logging via the /proc interface.
+ */
+unsigned int scsi_logging_level;
+#if defined(CONFIG_SCSI_LOGGING)
+EXPORT_SYMBOL(scsi_logging_level);
+#endif
+
+const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = {
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications ",
+ "Unknown ",
+ "Unknown ",
+ "RAID ",
+ "Enclosure ",
+};
+EXPORT_SYMBOL(scsi_device_types);
+
+/*
+ * Function: scsi_allocate_request
+ *
+ * Purpose: Allocate a request descriptor.
+ *
+ * Arguments: device - device for which we want a request
+ * gfp_mask - allocation flags passed to kmalloc
+ *
+ * Lock status: No locks assumed to be held. This function is SMP-safe.
+ *
+ * Returns: Pointer to request block.
+ */
+struct scsi_request *scsi_allocate_request(struct scsi_device *sdev,
+ int gfp_mask)
+{
+ const int offset = ALIGN(sizeof(struct scsi_request), 4);
+ const int size = offset + sizeof(struct request);
+ struct scsi_request *sreq;
+
+ sreq = kmalloc(size, gfp_mask);
+ if (likely(sreq != NULL)) {
+ memset(sreq, 0, size);
+ sreq->sr_request = (struct request *)(((char *)sreq) + offset);
+ sreq->sr_device = sdev;
+ sreq->sr_host = sdev->host;
+ sreq->sr_magic = SCSI_REQ_MAGIC;
+ sreq->sr_data_direction = DMA_BIDIRECTIONAL;
+ }
+
+ return sreq;
+}
+EXPORT_SYMBOL(scsi_allocate_request);
+
+void __scsi_release_request(struct scsi_request *sreq)
+{
+ struct request *req = sreq->sr_request;
+
+ /* unlikely because the tag was usually ended earlier by the
+ * mid-layer. However, for layering reasons ULD's don't end
+ * the tag of commands they generate. */
+ if (unlikely(blk_rq_tagged(req))) {
+ unsigned long flags;
+ struct request_queue *q = req->q;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_queue_end_tag(q, req);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ }
+
+
+ if (likely(sreq->sr_command != NULL)) {
+ struct scsi_cmnd *cmd = sreq->sr_command;
+
+ sreq->sr_command = NULL;
+ scsi_next_command(cmd);
+ }
+}
+
+/*
+ * Function: scsi_release_request
+ *
+ * Purpose: Release a request descriptor.
+ *
+ * Arguments: sreq - request to release
+ *
+ * Lock status: No locks assumed to be held. This function is SMP-safe.
+ */
+void scsi_release_request(struct scsi_request *sreq)
+{
+ __scsi_release_request(sreq);
+ kfree(sreq);
+}
+EXPORT_SYMBOL(scsi_release_request);
+
+struct scsi_host_cmd_pool {
+ kmem_cache_t *slab;
+ unsigned int users;
+ char *name;
+ unsigned int slab_flags;
+ unsigned int gfp_mask;
+};
+
+static struct scsi_host_cmd_pool scsi_cmd_pool = {
+ .name = "scsi_cmd_cache",
+ .slab_flags = SLAB_HWCACHE_ALIGN,
+};
+
+static struct scsi_host_cmd_pool scsi_cmd_dma_pool = {
+ .name = "scsi_cmd_cache(DMA)",
+ .slab_flags = SLAB_HWCACHE_ALIGN|SLAB_CACHE_DMA,
+ .gfp_mask = __GFP_DMA,
+};
+
+static DECLARE_MUTEX(host_cmd_pool_mutex);
+
+static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost,
+ int gfp_mask)
+{
+ struct scsi_cmnd *cmd;
+
+ cmd = kmem_cache_alloc(shost->cmd_pool->slab,
+ gfp_mask | shost->cmd_pool->gfp_mask);
+
+ if (unlikely(!cmd)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&shost->free_list_lock, flags);
+ if (likely(!list_empty(&shost->free_list))) {
+ cmd = list_entry(shost->free_list.next,
+ struct scsi_cmnd, list);
+ list_del_init(&cmd->list);
+ }
+ spin_unlock_irqrestore(&shost->free_list_lock, flags);
+ }
+
+ return cmd;
+}
+
+/*
+ * Function: scsi_get_command()
+ *
+ * Purpose: Allocate and setup a scsi command block
+ *
+ * Arguments: dev - parent scsi device
+ * gfp_mask- allocator flags
+ *
+ * Returns: The allocated scsi command structure.
+ */
+struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, int gfp_mask)
+{
+ struct scsi_cmnd *cmd;
+
+ /* Bail if we can't get a reference to the device */
+ if (!get_device(&dev->sdev_gendev))
+ return NULL;
+
+ cmd = __scsi_get_command(dev->host, gfp_mask);
+
+ if (likely(cmd != NULL)) {
+ unsigned long flags;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->device = dev;
+ cmd->state = SCSI_STATE_UNUSED;
+ cmd->owner = SCSI_OWNER_NOBODY;
+ init_timer(&cmd->eh_timeout);
+ INIT_LIST_HEAD(&cmd->list);
+ spin_lock_irqsave(&dev->list_lock, flags);
+ list_add_tail(&cmd->list, &dev->cmd_list);
+ spin_unlock_irqrestore(&dev->list_lock, flags);
+ } else
+ put_device(&dev->sdev_gendev);
+
+ return cmd;
+}
+EXPORT_SYMBOL(scsi_get_command);
+
+/*
+ * Function: scsi_put_command()
+ *
+ * Purpose: Free a scsi command block
+ *
+ * Arguments: cmd - command block to free
+ *
+ * Returns: Nothing.
+ *
+ * Notes: The command must not belong to any lists.
+ */
+void scsi_put_command(struct scsi_cmnd *cmd)
+{
+ struct scsi_device *sdev = cmd->device;
+ struct Scsi_Host *shost = sdev->host;
+ unsigned long flags;
+
+ /* serious error if the command hasn't come from a device list */
+ spin_lock_irqsave(&cmd->device->list_lock, flags);
+ BUG_ON(list_empty(&cmd->list));
+ list_del_init(&cmd->list);
+ spin_unlock(&cmd->device->list_lock);
+ /* changing locks here, don't need to restore the irq state */
+ spin_lock(&shost->free_list_lock);
+ if (unlikely(list_empty(&shost->free_list))) {
+ list_add(&cmd->list, &shost->free_list);
+ cmd = NULL;
+ }
+ spin_unlock_irqrestore(&shost->free_list_lock, flags);
+
+ if (likely(cmd != NULL))
+ kmem_cache_free(shost->cmd_pool->slab, cmd);
+
+ put_device(&sdev->sdev_gendev);
+}
+EXPORT_SYMBOL(scsi_put_command);
+
+/*
+ * Function: scsi_setup_command_freelist()
+ *
+ * Purpose: Setup the command freelist for a scsi host.
+ *
+ * Arguments: shost - host to allocate the freelist for.
+ *
+ * Returns: Nothing.
+ */
+int scsi_setup_command_freelist(struct Scsi_Host *shost)
+{
+ struct scsi_host_cmd_pool *pool;
+ struct scsi_cmnd *cmd;
+
+ spin_lock_init(&shost->free_list_lock);
+ INIT_LIST_HEAD(&shost->free_list);
+
+ /*
+ * Select a command slab for this host and create it if not
+ * yet existant.
+ */
+ down(&host_cmd_pool_mutex);
+ pool = (shost->unchecked_isa_dma ? &scsi_cmd_dma_pool : &scsi_cmd_pool);
+ if (!pool->users) {
+ pool->slab = kmem_cache_create(pool->name,
+ sizeof(struct scsi_cmnd), 0,
+ pool->slab_flags, NULL, NULL);
+ if (!pool->slab)
+ goto fail;
+ }
+
+ pool->users++;
+ shost->cmd_pool = pool;
+ up(&host_cmd_pool_mutex);
+
+ /*
+ * Get one backup command for this host.
+ */
+ cmd = kmem_cache_alloc(shost->cmd_pool->slab,
+ GFP_KERNEL | shost->cmd_pool->gfp_mask);
+ if (!cmd)
+ goto fail2;
+ list_add(&cmd->list, &shost->free_list);
+ return 0;
+
+ fail2:
+ if (!--pool->users)
+ kmem_cache_destroy(pool->slab);
+ return -ENOMEM;
+ fail:
+ up(&host_cmd_pool_mutex);
+ return -ENOMEM;
+
+}
+
+/*
+ * Function: scsi_destroy_command_freelist()
+ *
+ * Purpose: Release the command freelist for a scsi host.
+ *
+ * Arguments: shost - host that's freelist is going to be destroyed
+ */
+void scsi_destroy_command_freelist(struct Scsi_Host *shost)
+{
+ while (!list_empty(&shost->free_list)) {
+ struct scsi_cmnd *cmd;
+
+ cmd = list_entry(shost->free_list.next, struct scsi_cmnd, list);
+ list_del_init(&cmd->list);
+ kmem_cache_free(shost->cmd_pool->slab, cmd);
+ }
+
+ down(&host_cmd_pool_mutex);
+ if (!--shost->cmd_pool->users)
+ kmem_cache_destroy(shost->cmd_pool->slab);
+ up(&host_cmd_pool_mutex);
+}
+
+#ifdef CONFIG_SCSI_LOGGING
+void scsi_log_send(struct scsi_cmnd *cmd)
+{
+ unsigned int level;
+ struct scsi_device *sdev;
+
+ /*
+ * If ML QUEUE log level is greater than or equal to:
+ *
+ * 1: nothing (match completion)
+ *
+ * 2: log opcode + command of all commands
+ *
+ * 3: same as 2 plus dump cmd address
+ *
+ * 4: same as 3 plus dump extra junk
+ */
+ if (unlikely(scsi_logging_level)) {
+ level = SCSI_LOG_LEVEL(SCSI_LOG_MLQUEUE_SHIFT,
+ SCSI_LOG_MLQUEUE_BITS);
+ if (level > 1) {
+ sdev = cmd->device;
+ printk(KERN_INFO "scsi <%d:%d:%d:%d> send ",
+ sdev->host->host_no, sdev->channel, sdev->id,
+ sdev->lun);
+ if (level > 2)
+ printk("0x%p ", cmd);
+ /*
+ * spaces to match disposition and cmd->result
+ * output in scsi_log_completion.
+ */
+ printk(" ");
+ scsi_print_command(cmd);
+ if (level > 3) {
+ printk(KERN_INFO "buffer = 0x%p, bufflen = %d,"
+ " done = 0x%p, queuecommand 0x%p\n",
+ cmd->buffer, cmd->bufflen,
+ cmd->done,
+ sdev->host->hostt->queuecommand);
+
+ }
+ }
+ }
+}
+
+void scsi_log_completion(struct scsi_cmnd *cmd, int disposition)
+{
+ unsigned int level;
+ struct scsi_device *sdev;
+
+ /*
+ * If ML COMPLETE log level is greater than or equal to:
+ *
+ * 1: log disposition, result, opcode + command, and conditionally
+ * sense data for failures or non SUCCESS dispositions.
+ *
+ * 2: same as 1 but for all command completions.
+ *
+ * 3: same as 2 plus dump cmd address
+ *
+ * 4: same as 3 plus dump extra junk
+ */
+ if (unlikely(scsi_logging_level)) {
+ level = SCSI_LOG_LEVEL(SCSI_LOG_MLCOMPLETE_SHIFT,
+ SCSI_LOG_MLCOMPLETE_BITS);
+ if (((level > 0) && (cmd->result || disposition != SUCCESS)) ||
+ (level > 1)) {
+ sdev = cmd->device;
+ printk(KERN_INFO "scsi <%d:%d:%d:%d> done ",
+ sdev->host->host_no, sdev->channel, sdev->id,
+ sdev->lun);
+ if (level > 2)
+ printk("0x%p ", cmd);
+ /*
+ * Dump truncated values, so we usually fit within
+ * 80 chars.
+ */
+ switch (disposition) {
+ case SUCCESS:
+ printk("SUCCESS");
+ break;
+ case NEEDS_RETRY:
+ printk("RETRY ");
+ break;
+ case ADD_TO_MLQUEUE:
+ printk("MLQUEUE");
+ break;
+ case FAILED:
+ printk("FAILED ");
+ break;
+ case TIMEOUT_ERROR:
+ /*
+ * If called via scsi_times_out.
+ */
+ printk("TIMEOUT");
+ break;
+ default:
+ printk("UNKNOWN");
+ }
+ printk(" %8x ", cmd->result);
+ scsi_print_command(cmd);
+ if (status_byte(cmd->result) & CHECK_CONDITION) {
+ /*
+ * XXX The print_sense formatting/prefix
+ * doesn't match this function.
+ */
+ scsi_print_sense("", cmd);
+ }
+ if (level > 3) {
+ printk(KERN_INFO "scsi host busy %d failed %d\n",
+ sdev->host->host_busy,
+ sdev->host->host_failed);
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Assign a serial number and pid to the request for error recovery
+ * and debugging purposes. Protected by the Host_Lock of host.
+ */
+static inline void scsi_cmd_get_serial(struct Scsi_Host *host, struct scsi_cmnd *cmd)
+{
+ cmd->serial_number = host->cmd_serial_number++;
+ if (cmd->serial_number == 0)
+ cmd->serial_number = host->cmd_serial_number++;
+
+ cmd->pid = host->cmd_pid++;
+ if (cmd->pid == 0)
+ cmd->pid = host->cmd_pid++;
+}
+
+/*
+ * Function: scsi_dispatch_command
+ *
+ * Purpose: Dispatch a command to the low-level driver.
+ *
+ * Arguments: cmd - command block we are dispatching.
+ *
+ * Notes:
+ */
+int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ unsigned long flags = 0;
+ unsigned long timeout;
+ int rtn = 0;
+
+ /* check if the device is still usable */
+ if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
+ /* in SDEV_DEL we error all commands. DID_NO_CONNECT
+ * returns an immediate error upwards, and signals
+ * that the device is no longer present */
+ cmd->result = DID_NO_CONNECT << 16;
+ atomic_inc(&cmd->device->iorequest_cnt);
+ scsi_done(cmd);
+ /* return 0 (because the command has been processed) */
+ goto out;
+ }
+
+ /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */
+ if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) {
+ /*
+ * in SDEV_BLOCK, the command is just put back on the device
+ * queue. The suspend state has already blocked the queue so
+ * future requests should not occur until the device
+ * transitions out of the suspend state.
+ */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
+
+ SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n"));
+
+ /*
+ * NOTE: rtn is still zero here because we don't need the
+ * queue to be plugged on return (it's already stopped)
+ */
+ goto out;
+ }
+
+ /*
+ * If SCSI-2 or lower, store the LUN value in cmnd.
+ */
+ if (cmd->device->scsi_level <= SCSI_2) {
+ cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |
+ (cmd->device->lun << 5 & 0xe0);
+ }
+
+ /*
+ * We will wait MIN_RESET_DELAY clock ticks after the last reset so
+ * we can avoid the drive not being ready.
+ */
+ timeout = host->last_reset + MIN_RESET_DELAY;
+
+ if (host->resetting && time_before(jiffies, timeout)) {
+ int ticks_remaining = timeout - jiffies;
+ /*
+ * NOTE: This may be executed from within an interrupt
+ * handler! This is bad, but for now, it'll do. The irq
+ * level of the interrupt handler has been masked out by the
+ * platform dependent interrupt handling code already, so the
+ * sti() here will not cause another call to the SCSI host's
+ * interrupt handler (assuming there is one irq-level per
+ * host).
+ */
+ while (--ticks_remaining >= 0)
+ mdelay(1 + 999 / HZ);
+ host->resetting = 0;
+ }
+
+ /*
+ * AK: unlikely race here: for some reason the timer could
+ * expire before the serial number is set up below.
+ */
+ scsi_add_timer(cmd, cmd->timeout_per_command, scsi_times_out);
+
+ scsi_log_send(cmd);
+
+ /*
+ * We will use a queued command if possible, otherwise we will
+ * emulate the queuing and calling of completion function ourselves.
+ */
+
+ cmd->state = SCSI_STATE_QUEUED;
+ cmd->owner = SCSI_OWNER_LOWLEVEL;
+
+ atomic_inc(&cmd->device->iorequest_cnt);
+
+ /*
+ * Before we queue this command, check if the command
+ * length exceeds what the host adapter can handle.
+ */
+ if (CDB_SIZE(cmd) > cmd->device->host->max_cmd_len) {
+ SCSI_LOG_MLQUEUE(3,
+ printk("queuecommand : command too long.\n"));
+ cmd->result = (DID_ABORT << 16);
+
+ scsi_done(cmd);
+ goto out;
+ }
+
+ spin_lock_irqsave(host->host_lock, flags);
+ scsi_cmd_get_serial(host, cmd);
+
+ if (unlikely(test_bit(SHOST_CANCEL, &host->shost_state))) {
+ cmd->result = (DID_NO_CONNECT << 16);
+ scsi_done(cmd);
+ } else {
+ rtn = host->hostt->queuecommand(cmd, scsi_done);
+ }
+ spin_unlock_irqrestore(host->host_lock, flags);
+ if (rtn) {
+ atomic_inc(&cmd->device->iodone_cnt);
+ scsi_queue_insert(cmd,
+ (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ?
+ rtn : SCSI_MLQUEUE_HOST_BUSY);
+ SCSI_LOG_MLQUEUE(3,
+ printk("queuecommand : request rejected\n"));
+ }
+
+ out:
+ SCSI_LOG_MLQUEUE(3, printk("leaving scsi_dispatch_cmnd()\n"));
+ return rtn;
+}
+
+/*
+ * Function: scsi_init_cmd_from_req
+ *
+ * Purpose: Queue a SCSI command
+ * Purpose: Initialize a struct scsi_cmnd from a struct scsi_request
+ *
+ * Arguments: cmd - command descriptor.
+ * sreq - Request from the queue.
+ *
+ * Lock status: None needed.
+ *
+ * Returns: Nothing.
+ *
+ * Notes: Mainly transfer data from the request structure to the
+ * command structure. The request structure is allocated
+ * using the normal memory allocator, and requests can pile
+ * up to more or less any depth. The command structure represents
+ * a consumable resource, as these are allocated into a pool
+ * when the SCSI subsystem initializes. The preallocation is
+ * required so that in low-memory situations a disk I/O request
+ * won't cause the memory manager to try and write out a page.
+ * The request structure is generally used by ioctls and character
+ * devices.
+ */
+void scsi_init_cmd_from_req(struct scsi_cmnd *cmd, struct scsi_request *sreq)
+{
+ sreq->sr_command = cmd;
+
+ cmd->owner = SCSI_OWNER_MIDLEVEL;
+ cmd->cmd_len = sreq->sr_cmd_len;
+ cmd->use_sg = sreq->sr_use_sg;
+
+ cmd->request = sreq->sr_request;
+ memcpy(cmd->data_cmnd, sreq->sr_cmnd, sizeof(cmd->data_cmnd));
+ cmd->serial_number = 0;
+ cmd->serial_number_at_timeout = 0;
+ cmd->bufflen = sreq->sr_bufflen;
+ cmd->buffer = sreq->sr_buffer;
+ cmd->retries = 0;
+ cmd->allowed = sreq->sr_allowed;
+ cmd->done = sreq->sr_done;
+ cmd->timeout_per_command = sreq->sr_timeout_per_command;
+ cmd->sc_data_direction = sreq->sr_data_direction;
+ cmd->sglist_len = sreq->sr_sglist_len;
+ cmd->underflow = sreq->sr_underflow;
+ cmd->sc_request = sreq;
+ memcpy(cmd->cmnd, sreq->sr_cmnd, sizeof(sreq->sr_cmnd));
+
+ /*
+ * Zero the sense buffer. Some host adapters automatically request
+ * sense on error. 0 is not a valid sense code.
+ */
+ memset(cmd->sense_buffer, 0, sizeof(sreq->sr_sense_buffer));
+ cmd->request_buffer = sreq->sr_buffer;
+ cmd->request_bufflen = sreq->sr_bufflen;
+ cmd->old_use_sg = cmd->use_sg;
+ if (cmd->cmd_len == 0)
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+ cmd->old_cmd_len = cmd->cmd_len;
+ cmd->sc_old_data_direction = cmd->sc_data_direction;
+ cmd->old_underflow = cmd->underflow;
+
+ /*
+ * Start the timer ticking.
+ */
+ cmd->internal_timeout = NORMAL_TIMEOUT;
+ cmd->abort_reason = 0;
+ cmd->result = 0;
+
+ SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_init_cmd_from_req()\n"));
+}
+
+/*
+ * Per-CPU I/O completion queue.
+ */
+static DEFINE_PER_CPU(struct list_head, scsi_done_q);
+
+/**
+ * scsi_done - Enqueue the finished SCSI command into the done queue.
+ * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives
+ * ownership back to SCSI Core -- i.e. the LLDD has finished with it.
+ *
+ * This function is the mid-level's (SCSI Core) interrupt routine, which
+ * regains ownership of the SCSI command (de facto) from a LLDD, and enqueues
+ * the command to the done queue for further processing.
+ *
+ * This is the producer of the done queue who enqueues at the tail.
+ *
+ * This function is interrupt context safe.
+ */
+void scsi_done(struct scsi_cmnd *cmd)
+{
+ /*
+ * We don't have to worry about this one timing out any more.
+ * If we are unable to remove the timer, then the command
+ * has already timed out. In which case, we have no choice but to
+ * let the timeout function run, as we have no idea where in fact
+ * that function could really be. It might be on another processor,
+ * etc, etc.
+ */
+ if (!scsi_delete_timer(cmd))
+ return;
+ __scsi_done(cmd);
+}
+
+/* Private entry to scsi_done() to complete a command when the timer
+ * isn't running --- used by scsi_times_out */
+void __scsi_done(struct scsi_cmnd *cmd)
+{
+ unsigned long flags;
+
+ /*
+ * Set the serial numbers back to zero
+ */
+ cmd->serial_number = 0;
+ cmd->serial_number_at_timeout = 0;
+ cmd->state = SCSI_STATE_BHQUEUE;
+ cmd->owner = SCSI_OWNER_BH_HANDLER;
+
+ atomic_inc(&cmd->device->iodone_cnt);
+ if (cmd->result)
+ atomic_inc(&cmd->device->ioerr_cnt);
+
+ /*
+ * Next, enqueue the command into the done queue.
+ * It is a per-CPU queue, so we just disable local interrupts
+ * and need no spinlock.
+ */
+ local_irq_save(flags);
+ list_add_tail(&cmd->eh_entry, &__get_cpu_var(scsi_done_q));
+ raise_softirq_irqoff(SCSI_SOFTIRQ);
+ local_irq_restore(flags);
+}
+
+/**
+ * scsi_softirq - Perform post-interrupt processing of finished SCSI commands.
+ *
+ * This is the consumer of the done queue.
+ *
+ * This is called with all interrupts enabled. This should reduce
+ * interrupt latency, stack depth, and reentrancy of the low-level
+ * drivers.
+ */
+static void scsi_softirq(struct softirq_action *h)
+{
+ int disposition;
+ LIST_HEAD(local_q);
+
+ local_irq_disable();
+ list_splice_init(&__get_cpu_var(scsi_done_q), &local_q);
+ local_irq_enable();
+
+ while (!list_empty(&local_q)) {
+ struct scsi_cmnd *cmd = list_entry(local_q.next,
+ struct scsi_cmnd, eh_entry);
+ list_del_init(&cmd->eh_entry);
+
+ disposition = scsi_decide_disposition(cmd);
+ scsi_log_completion(cmd, disposition);
+ switch (disposition) {
+ case SUCCESS:
+ scsi_finish_command(cmd);
+ break;
+ case NEEDS_RETRY:
+ scsi_retry_command(cmd);
+ break;
+ case ADD_TO_MLQUEUE:
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
+ break;
+ default:
+ if (!scsi_eh_scmd_add(cmd, 0))
+ scsi_finish_command(cmd);
+ }
+ }
+}
+
+/*
+ * Function: scsi_retry_command
+ *
+ * Purpose: Send a command back to the low level to be retried.
+ *
+ * Notes: This command is always executed in the context of the
+ * bottom half handler, or the error handler thread. Low
+ * level drivers should not become re-entrant as a result of
+ * this.
+ */
+int scsi_retry_command(struct scsi_cmnd *cmd)
+{
+ /*
+ * Restore the SCSI command state.
+ */
+ scsi_setup_cmd_retry(cmd);
+
+ /*
+ * Zero the sense information from the last time we tried
+ * this command.
+ */
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ return scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
+}
+
+/*
+ * Function: scsi_finish_command
+ *
+ * Purpose: Pass command off to upper layer for finishing of I/O
+ * request, waking processes that are waiting on results,
+ * etc.
+ */
+void scsi_finish_command(struct scsi_cmnd *cmd)
+{
+ struct scsi_device *sdev = cmd->device;
+ struct Scsi_Host *shost = sdev->host;
+ struct scsi_request *sreq;
+
+ scsi_device_unbusy(sdev);
+
+ /*
+ * Clear the flags which say that the device/host is no longer
+ * capable of accepting new commands. These are set in scsi_queue.c
+ * for both the queue full condition on a device, and for a
+ * host full condition on the host.
+ *
+ * XXX(hch): What about locking?
+ */
+ shost->host_blocked = 0;
+ sdev->device_blocked = 0;
+
+ /*
+ * If we have valid sense information, then some kind of recovery
+ * must have taken place. Make a note of this.
+ */
+ if (SCSI_SENSE_VALID(cmd))
+ cmd->result |= (DRIVER_SENSE << 24);
+
+ SCSI_LOG_MLCOMPLETE(4, printk("Notifying upper driver of completion "
+ "for device %d %x\n", sdev->id, cmd->result));
+
+ cmd->owner = SCSI_OWNER_HIGHLEVEL;
+ cmd->state = SCSI_STATE_FINISHED;
+
+ /*
+ * We can get here with use_sg=0, causing a panic in the upper level
+ */
+ cmd->use_sg = cmd->old_use_sg;
+
+ /*
+ * If there is an associated request structure, copy the data over
+ * before we call the completion function.
+ */
+ sreq = cmd->sc_request;
+ if (sreq) {
+ sreq->sr_result = sreq->sr_command->result;
+ if (sreq->sr_result) {
+ memcpy(sreq->sr_sense_buffer,
+ sreq->sr_command->sense_buffer,
+ sizeof(sreq->sr_sense_buffer));
+ }
+ }
+
+ cmd->done(cmd);
+}
+EXPORT_SYMBOL(scsi_finish_command);
+
+/*
+ * Function: scsi_adjust_queue_depth()
+ *
+ * Purpose: Allow low level drivers to tell us to change the queue depth
+ * on a specific SCSI device
+ *
+ * Arguments: sdev - SCSI Device in question
+ * tagged - Do we use tagged queueing (non-0) or do we treat
+ * this device as an untagged device (0)
+ * tags - Number of tags allowed if tagged queueing enabled,
+ * or number of commands the low level driver can
+ * queue up in non-tagged mode (as per cmd_per_lun).
+ *
+ * Returns: Nothing
+ *
+ * Lock Status: None held on entry
+ *
+ * Notes: Low level drivers may call this at any time and we will do
+ * the right thing depending on whether or not the device is
+ * currently active and whether or not it even has the
+ * command blocks built yet.
+ */
+void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags)
+{
+ unsigned long flags;
+
+ /*
+ * refuse to set tagged depth to an unworkable size
+ */
+ if (tags <= 0)
+ return;
+
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+
+ /* Check to see if the queue is managed by the block layer
+ * if it is, and we fail to adjust the depth, exit */
+ if (blk_queue_tagged(sdev->request_queue) &&
+ blk_queue_resize_tags(sdev->request_queue, tags) != 0)
+ goto out;
+
+ sdev->queue_depth = tags;
+ switch (tagged) {
+ case MSG_ORDERED_TAG:
+ sdev->ordered_tags = 1;
+ sdev->simple_tags = 1;
+ break;
+ case MSG_SIMPLE_TAG:
+ sdev->ordered_tags = 0;
+ sdev->simple_tags = 1;
+ break;
+ default:
+ printk(KERN_WARNING "(scsi%d:%d:%d:%d) "
+ "scsi_adjust_queue_depth, bad queue type, "
+ "disabled\n", sdev->host->host_no,
+ sdev->channel, sdev->id, sdev->lun);
+ case 0:
+ sdev->ordered_tags = sdev->simple_tags = 0;
+ sdev->queue_depth = tags;
+ break;
+ }
+ out:
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+}
+EXPORT_SYMBOL(scsi_adjust_queue_depth);
+
+/*
+ * Function: scsi_track_queue_full()
+ *
+ * Purpose: This function will track successive QUEUE_FULL events on a
+ * specific SCSI device to determine if and when there is a
+ * need to adjust the queue depth on the device.
+ *
+ * Arguments: sdev - SCSI Device in question
+ * depth - Current number of outstanding SCSI commands on
+ * this device, not counting the one returned as
+ * QUEUE_FULL.
+ *
+ * Returns: 0 - No change needed
+ * >0 - Adjust queue depth to this new depth
+ * -1 - Drop back to untagged operation using host->cmd_per_lun
+ * as the untagged command depth
+ *
+ * Lock Status: None held on entry
+ *
+ * Notes: Low level drivers may call this at any time and we will do
+ * "The Right Thing." We are interrupt context safe.
+ */
+int scsi_track_queue_full(struct scsi_device *sdev, int depth)
+{
+ if ((jiffies >> 4) == sdev->last_queue_full_time)
+ return 0;
+
+ sdev->last_queue_full_time = (jiffies >> 4);
+ if (sdev->last_queue_full_depth != depth) {
+ sdev->last_queue_full_count = 1;
+ sdev->last_queue_full_depth = depth;
+ } else {
+ sdev->last_queue_full_count++;
+ }
+
+ if (sdev->last_queue_full_count <= 10)
+ return 0;
+ if (sdev->last_queue_full_depth < 8) {
+ /* Drop back to untagged */
+ scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ return -1;
+ }
+
+ if (sdev->ordered_tags)
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);
+ else
+ scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth);
+ return depth;
+}
+EXPORT_SYMBOL(scsi_track_queue_full);
+
+/**
+ * scsi_device_get - get an addition reference to a scsi_device
+ * @sdev: device to get a reference to
+ *
+ * Gets a reference to the scsi_device and increments the use count
+ * of the underlying LLDD module. You must hold host_lock of the
+ * parent Scsi_Host or already have a reference when calling this.
+ */
+int scsi_device_get(struct scsi_device *sdev)
+{
+ if (sdev->sdev_state == SDEV_DEL || sdev->sdev_state == SDEV_CANCEL)
+ return -ENXIO;
+ if (!get_device(&sdev->sdev_gendev))
+ return -ENXIO;
+ if (!try_module_get(sdev->host->hostt->module)) {
+ put_device(&sdev->sdev_gendev);
+ return -ENXIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(scsi_device_get);
+
+/**
+ * scsi_device_put - release a reference to a scsi_device
+ * @sdev: device to release a reference on.
+ *
+ * Release a reference to the scsi_device and decrements the use count
+ * of the underlying LLDD module. The device is freed once the last
+ * user vanishes.
+ */
+void scsi_device_put(struct scsi_device *sdev)
+{
+ module_put(sdev->host->hostt->module);
+ put_device(&sdev->sdev_gendev);
+}
+EXPORT_SYMBOL(scsi_device_put);
+
+/* helper for shost_for_each_device, thus not documented */
+struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *shost,
+ struct scsi_device *prev)
+{
+ struct list_head *list = (prev ? &prev->siblings : &shost->__devices);
+ struct scsi_device *next = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (list->next != &shost->__devices) {
+ next = list_entry(list->next, struct scsi_device, siblings);
+ /* skip devices that we can't get a reference to */
+ if (!scsi_device_get(next))
+ break;
+ next = NULL;
+ list = list->next;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ if (prev)
+ scsi_device_put(prev);
+ return next;
+}
+EXPORT_SYMBOL(__scsi_iterate_devices);
+
+/**
+ * starget_for_each_device - helper to walk all devices of a target
+ * @starget: target whose devices we want to iterate over.
+ *
+ * This traverses over each devices of @shost. The devices have
+ * a reference that must be released by scsi_host_put when breaking
+ * out of the loop.
+ */
+void starget_for_each_device(struct scsi_target *starget, void * data,
+ void (*fn)(struct scsi_device *, void *))
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost) {
+ if ((sdev->channel == starget->channel) &&
+ (sdev->id == starget->id))
+ fn(sdev, data);
+ }
+}
+EXPORT_SYMBOL(starget_for_each_device);
+
+/**
+ * __scsi_device_lookup_by_target - find a device given the target (UNLOCKED)
+ * @starget: SCSI target pointer
+ * @lun: SCSI Logical Unit Number
+ *
+ * Looks up the scsi_device with the specified @lun for a give
+ * @starget. The returned scsi_device does not have an additional
+ * reference. You must hold the host's host_lock over this call and
+ * any access to the returned scsi_device.
+ *
+ * Note: The only reason why drivers would want to use this is because
+ * they're need to access the device list in irq context. Otherwise you
+ * really want to use scsi_device_lookup_by_target instead.
+ **/
+struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *starget,
+ uint lun)
+{
+ struct scsi_device *sdev;
+
+ list_for_each_entry(sdev, &starget->devices, same_target_siblings) {
+ if (sdev->lun ==lun)
+ return sdev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(__scsi_device_lookup_by_target);
+
+/**
+ * scsi_device_lookup_by_target - find a device given the target
+ * @starget: SCSI target pointer
+ * @lun: SCSI Logical Unit Number
+ *
+ * Looks up the scsi_device with the specified @channel, @id, @lun for a
+ * give host. The returned scsi_device has an additional reference that
+ * needs to be release with scsi_host_put once you're done with it.
+ **/
+struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget,
+ uint lun)
+{
+ struct scsi_device *sdev;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ sdev = __scsi_device_lookup_by_target(starget, lun);
+ if (sdev && scsi_device_get(sdev))
+ sdev = NULL;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ return sdev;
+}
+EXPORT_SYMBOL(scsi_device_lookup_by_target);
+
+/**
+ * scsi_device_lookup - find a device given the host (UNLOCKED)
+ * @shost: SCSI host pointer
+ * @channel: SCSI channel (zero if only one channel)
+ * @pun: SCSI target number (physical unit number)
+ * @lun: SCSI Logical Unit Number
+ *
+ * Looks up the scsi_device with the specified @channel, @id, @lun for a
+ * give host. The returned scsi_device does not have an additional reference.
+ * You must hold the host's host_lock over this call and any access to the
+ * returned scsi_device.
+ *
+ * Note: The only reason why drivers would want to use this is because
+ * they're need to access the device list in irq context. Otherwise you
+ * really want to use scsi_device_lookup instead.
+ **/
+struct scsi_device *__scsi_device_lookup(struct Scsi_Host *shost,
+ uint channel, uint id, uint lun)
+{
+ struct scsi_device *sdev;
+
+ list_for_each_entry(sdev, &shost->__devices, siblings) {
+ if (sdev->channel == channel && sdev->id == id &&
+ sdev->lun ==lun)
+ return sdev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(__scsi_device_lookup);
+
+/**
+ * scsi_device_lookup - find a device given the host
+ * @shost: SCSI host pointer
+ * @channel: SCSI channel (zero if only one channel)
+ * @id: SCSI target number (physical unit number)
+ * @lun: SCSI Logical Unit Number
+ *
+ * Looks up the scsi_device with the specified @channel, @id, @lun for a
+ * give host. The returned scsi_device has an additional reference that
+ * needs to be release with scsi_host_put once you're done with it.
+ **/
+struct scsi_device *scsi_device_lookup(struct Scsi_Host *shost,
+ uint channel, uint id, uint lun)
+{
+ struct scsi_device *sdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ sdev = __scsi_device_lookup(shost, channel, id, lun);
+ if (sdev && scsi_device_get(sdev))
+ sdev = NULL;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ return sdev;
+}
+EXPORT_SYMBOL(scsi_device_lookup);
+
+/**
+ * scsi_device_cancel - cancel outstanding IO to this device
+ * @sdev: Pointer to struct scsi_device
+ * @recovery: Boolean instructing function to recover device or not.
+ *
+ **/
+int scsi_device_cancel(struct scsi_device *sdev, int recovery)
+{
+ struct scsi_cmnd *scmd;
+ LIST_HEAD(active_list);
+ struct list_head *lh, *lh_sf;
+ unsigned long flags;
+
+ scsi_device_set_state(sdev, SDEV_CANCEL);
+
+ spin_lock_irqsave(&sdev->list_lock, flags);
+ list_for_each_entry(scmd, &sdev->cmd_list, list) {
+ if (scmd->request && scmd->request->rq_status != RQ_INACTIVE) {
+ /*
+ * If we are unable to remove the timer, it means
+ * that the command has already timed out or
+ * finished.
+ */
+ if (!scsi_delete_timer(scmd))
+ continue;
+ list_add_tail(&scmd->eh_entry, &active_list);
+ }
+ }
+ spin_unlock_irqrestore(&sdev->list_lock, flags);
+
+ if (!list_empty(&active_list)) {
+ list_for_each_safe(lh, lh_sf, &active_list) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ list_del_init(lh);
+ if (recovery) {
+ scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD);
+ } else {
+ scmd->result = (DID_ABORT << 16);
+ scsi_finish_command(scmd);
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(scsi_device_cancel);
+
+#ifdef CONFIG_HOTPLUG_CPU
+static int scsi_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (unsigned long)hcpu;
+
+ switch(action) {
+ case CPU_DEAD:
+ /* Drain scsi_done_q. */
+ local_irq_disable();
+ list_splice_init(&per_cpu(scsi_done_q, cpu),
+ &__get_cpu_var(scsi_done_q));
+ raise_softirq_irqoff(SCSI_SOFTIRQ);
+ local_irq_enable();
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __devinitdata scsi_cpu_nb = {
+ .notifier_call = scsi_cpu_notify,
+};
+
+#define register_scsi_cpu() register_cpu_notifier(&scsi_cpu_nb)
+#define unregister_scsi_cpu() unregister_cpu_notifier(&scsi_cpu_nb)
+#else
+#define register_scsi_cpu()
+#define unregister_scsi_cpu()
+#endif /* CONFIG_HOTPLUG_CPU */
+
+MODULE_DESCRIPTION("SCSI core");
+MODULE_LICENSE("GPL");
+
+module_param(scsi_logging_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels");
+
+static int __init init_scsi(void)
+{
+ int error, i;
+
+ error = scsi_init_queue();
+ if (error)
+ return error;
+ error = scsi_init_procfs();
+ if (error)
+ goto cleanup_queue;
+ error = scsi_init_devinfo();
+ if (error)
+ goto cleanup_procfs;
+ error = scsi_init_hosts();
+ if (error)
+ goto cleanup_devlist;
+ error = scsi_init_sysctl();
+ if (error)
+ goto cleanup_hosts;
+ error = scsi_sysfs_register();
+ if (error)
+ goto cleanup_sysctl;
+
+ for (i = 0; i < NR_CPUS; i++)
+ INIT_LIST_HEAD(&per_cpu(scsi_done_q, i));
+
+ devfs_mk_dir("scsi");
+ open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL);
+ register_scsi_cpu();
+ printk(KERN_NOTICE "SCSI subsystem initialized\n");
+ return 0;
+
+cleanup_sysctl:
+ scsi_exit_sysctl();
+cleanup_hosts:
+ scsi_exit_hosts();
+cleanup_devlist:
+ scsi_exit_devinfo();
+cleanup_procfs:
+ scsi_exit_procfs();
+cleanup_queue:
+ scsi_exit_queue();
+ printk(KERN_ERR "SCSI subsystem failed to initialize, error = %d\n",
+ -error);
+ return error;
+}
+
+static void __exit exit_scsi(void)
+{
+ scsi_sysfs_unregister();
+ scsi_exit_sysctl();
+ scsi_exit_hosts();
+ scsi_exit_devinfo();
+ devfs_remove("scsi");
+ scsi_exit_procfs();
+ scsi_exit_queue();
+ unregister_scsi_cpu();
+}
+
+subsys_initcall(init_scsi);
+module_exit(exit_scsi);
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
new file mode 100644
index 000000000000..cb6b5fbb7e14
--- /dev/null
+++ b/drivers/scsi/scsi.h
@@ -0,0 +1,109 @@
+/*
+ * scsi.h Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995, 1998, 1999 Eric Youngdale
+ * generic SCSI package header file by
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@andante.org to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+/*
+ * NOTE: this file only contains compatibility glue for old drivers. All
+ * these wrappers will be removed sooner or later. For new code please use
+ * the interfaces declared in the headers in include/scsi/
+ */
+
+#ifndef _SCSI_H
+#define _SCSI_H
+
+#include <linux/config.h> /* for CONFIG_SCSI_LOGGING */
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_request.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi.h>
+
+/*
+ * Some defs, in case these are not defined elsewhere.
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+struct Scsi_Host;
+struct scsi_cmnd;
+struct scsi_device;
+struct scsi_target;
+struct scatterlist;
+
+/*
+ * Legacy dma direction interfaces.
+ *
+ * This assumes the pci/sbus dma mapping flags have the same numercial
+ * values as the generic dma-mapping ones. Currently they have but there's
+ * no way to check. Better don't use these interfaces!
+ */
+#define SCSI_DATA_UNKNOWN (DMA_BIDIRECTIONAL)
+#define SCSI_DATA_WRITE (DMA_TO_DEVICE)
+#define SCSI_DATA_READ (DMA_FROM_DEVICE)
+#define SCSI_DATA_NONE (DMA_NONE)
+
+#define scsi_to_pci_dma_dir(scsi_dir) ((int)(scsi_dir))
+#define scsi_to_sbus_dma_dir(scsi_dir) ((int)(scsi_dir))
+
+/*
+ * Old names for debug prettyprinting functions.
+ */
+static inline void print_Scsi_Cmnd(struct scsi_cmnd *cmd)
+{
+ return scsi_print_command(cmd);
+}
+static inline void print_command(unsigned char *cdb)
+{
+ return __scsi_print_command(cdb);
+}
+static inline void print_sense(const char *devclass, struct scsi_cmnd *cmd)
+{
+ return scsi_print_sense(devclass, cmd);
+}
+static inline void print_req_sense(const char *devclass, struct scsi_request *req)
+{
+ return scsi_print_req_sense(devclass, req);
+}
+static inline void print_driverbyte(int scsiresult)
+{
+ return scsi_print_driverbyte(scsiresult);
+}
+static inline void print_hostbyte(int scsiresult)
+{
+ return scsi_print_hostbyte(scsiresult);
+}
+static inline void print_status(unsigned char status)
+{
+ return scsi_print_status(status);
+}
+static inline int print_msg(const unsigned char *msg)
+{
+ return scsi_print_msg(msg);
+}
+
+/*
+ * This is the crap from the old error handling code. We have it in a special
+ * place so that we can more easily delete it later on.
+ */
+#include "scsi_obsolete.h"
+
+/* obsolete typedef junk. */
+#include "scsi_typedefs.h"
+
+#endif /* _SCSI_H */
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
new file mode 100644
index 000000000000..e0208886b45e
--- /dev/null
+++ b/drivers/scsi/scsi_debug.c
@@ -0,0 +1,1976 @@
+/*
+ * linux/kernel/scsi_debug.c
+ * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ * Copyright (C) 1992 Eric Youngdale
+ * Simulate a host adapter with 2 disks attached. Do a lot of checking
+ * to make sure that we are not getting blocks mixed up, and PANIC if
+ * anything out of the ordinary is seen.
+ * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * This version is more generic, simulating a variable number of disk
+ * (or disk like devices) sharing a common amount of RAM
+ *
+ *
+ * For documentation see http://www.torque.net/sg/sdebug26.html
+ *
+ * D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
+ * dpg: work for devfs large number of disks [20010809]
+ * forked for lk 2.5 series [20011216, 20020101]
+ * use vmalloc() more inquiry+mode_sense [20020302]
+ * add timers for delayed responses [20020721]
+ * Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
+ * Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
+ * dpg: change style of boot options to "scsi_debug.num_tgts=2" and
+ * module options to "modprobe scsi_debug num_tgts=2" [20021221]
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/genhd.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
+
+#include <linux/blkdev.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <scsi/scsicam.h>
+
+#include <linux/stat.h>
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include "scsi_logging.h"
+#include "scsi_debug.h"
+
+#define SCSI_DEBUG_VERSION "1.75"
+static const char * scsi_debug_version_date = "20050113";
+
+/* Additional Sense Code (ASC) used */
+#define NO_ADDED_SENSE 0x0
+#define UNRECOVERED_READ_ERR 0x11
+#define INVALID_OPCODE 0x20
+#define ADDR_OUT_OF_RANGE 0x21
+#define INVALID_FIELD_IN_CDB 0x24
+#define POWERON_RESET 0x29
+#define SAVING_PARAMS_UNSUP 0x39
+#define THRESHHOLD_EXCEEDED 0x5d
+
+#define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
+
+/* Default values for driver parameters */
+#define DEF_NUM_HOST 1
+#define DEF_NUM_TGTS 1
+#define DEF_MAX_LUNS 1
+/* With these defaults, this driver will make 1 host with 1 target
+ * (id 0) containing 1 logical unit (lun 0). That is 1 device.
+ */
+#define DEF_DELAY 1
+#define DEF_DEV_SIZE_MB 8
+#define DEF_EVERY_NTH 0
+#define DEF_NUM_PARTS 0
+#define DEF_OPTS 0
+#define DEF_SCSI_LEVEL 5 /* INQUIRY, byte2 [5->SPC-3] */
+#define DEF_PTYPE 0
+#define DEF_D_SENSE 0
+
+/* bit mask values for scsi_debug_opts */
+#define SCSI_DEBUG_OPT_NOISE 1
+#define SCSI_DEBUG_OPT_MEDIUM_ERR 2
+#define SCSI_DEBUG_OPT_TIMEOUT 4
+#define SCSI_DEBUG_OPT_RECOVERED_ERR 8
+/* When "every_nth" > 0 then modulo "every_nth" commands:
+ * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
+ * - a RECOVERED_ERROR is simulated on successful read and write
+ * commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
+ *
+ * When "every_nth" < 0 then after "- every_nth" commands:
+ * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
+ * - a RECOVERED_ERROR is simulated on successful read and write
+ * commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
+ * This will continue until some other action occurs (e.g. the user
+ * writing a new value (other than -1 or 1) to every_nth via sysfs).
+ */
+
+/* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
+ * sector on read commands: */
+#define OPT_MEDIUM_ERR_ADDR 0x1234 /* that's sector 4660 in decimal */
+
+/* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
+ * or "peripheral device" addressing (value 0) */
+#define SAM2_LUN_ADDRESS_METHOD 0
+
+static int scsi_debug_add_host = DEF_NUM_HOST;
+static int scsi_debug_delay = DEF_DELAY;
+static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
+static int scsi_debug_every_nth = DEF_EVERY_NTH;
+static int scsi_debug_max_luns = DEF_MAX_LUNS;
+static int scsi_debug_num_parts = DEF_NUM_PARTS;
+static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
+static int scsi_debug_opts = DEF_OPTS;
+static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
+static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
+static int scsi_debug_dsense = DEF_D_SENSE;
+
+static int scsi_debug_cmnd_count = 0;
+
+#define DEV_READONLY(TGT) (0)
+#define DEV_REMOVEABLE(TGT) (0)
+
+static unsigned long sdebug_store_size; /* in bytes */
+static sector_t sdebug_capacity; /* in sectors */
+
+/* old BIOS stuff, kernel may get rid of them but some mode sense pages
+ may still need them */
+static int sdebug_heads; /* heads per disk */
+static int sdebug_cylinders_per; /* cylinders per surface */
+static int sdebug_sectors_per; /* sectors per cylinder */
+
+/* default sector size is 512 bytes, 2**9 bytes */
+#define POW2_SECT_SIZE 9
+#define SECT_SIZE (1 << POW2_SECT_SIZE)
+#define SECT_SIZE_PER(TGT) SECT_SIZE
+
+#define SDEBUG_MAX_PARTS 4
+
+#define SDEBUG_SENSE_LEN 32
+
+struct sdebug_dev_info {
+ struct list_head dev_list;
+ unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */
+ unsigned int channel;
+ unsigned int target;
+ unsigned int lun;
+ struct sdebug_host_info *sdbg_host;
+ char reset;
+ char used;
+};
+
+struct sdebug_host_info {
+ struct list_head host_list;
+ struct Scsi_Host *shost;
+ struct device dev;
+ struct list_head dev_info_list;
+};
+
+#define to_sdebug_host(d) \
+ container_of(d, struct sdebug_host_info, dev)
+
+static LIST_HEAD(sdebug_host_list);
+static DEFINE_SPINLOCK(sdebug_host_list_lock);
+
+typedef void (* done_funct_t) (struct scsi_cmnd *);
+
+struct sdebug_queued_cmd {
+ int in_use;
+ struct timer_list cmnd_timer;
+ done_funct_t done_funct;
+ struct scsi_cmnd * a_cmnd;
+ int scsi_result;
+};
+static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
+
+static Scsi_Host_Template sdebug_driver_template = {
+ .proc_info = scsi_debug_proc_info,
+ .name = "SCSI DEBUG",
+ .info = scsi_debug_info,
+ .slave_alloc = scsi_debug_slave_alloc,
+ .slave_configure = scsi_debug_slave_configure,
+ .slave_destroy = scsi_debug_slave_destroy,
+ .ioctl = scsi_debug_ioctl,
+ .queuecommand = scsi_debug_queuecommand,
+ .eh_abort_handler = scsi_debug_abort,
+ .eh_bus_reset_handler = scsi_debug_bus_reset,
+ .eh_device_reset_handler = scsi_debug_device_reset,
+ .eh_host_reset_handler = scsi_debug_host_reset,
+ .bios_param = scsi_debug_biosparam,
+ .can_queue = SCSI_DEBUG_CANQUEUE,
+ .this_id = 7,
+ .sg_tablesize = 64,
+ .cmd_per_lun = 3,
+ .max_sectors = 4096,
+ .unchecked_isa_dma = 0,
+ .use_clustering = DISABLE_CLUSTERING,
+ .module = THIS_MODULE,
+};
+
+static unsigned char * fake_storep; /* ramdisk storage */
+
+static int num_aborts = 0;
+static int num_dev_resets = 0;
+static int num_bus_resets = 0;
+static int num_host_resets = 0;
+
+static DEFINE_SPINLOCK(queued_arr_lock);
+static DEFINE_RWLOCK(atomic_rw);
+
+static char sdebug_proc_name[] = "scsi_debug";
+
+static int sdebug_driver_probe(struct device *);
+static int sdebug_driver_remove(struct device *);
+static struct bus_type pseudo_lld_bus;
+
+static struct device_driver sdebug_driverfs_driver = {
+ .name = sdebug_proc_name,
+ .bus = &pseudo_lld_bus,
+ .probe = sdebug_driver_probe,
+ .remove = sdebug_driver_remove,
+};
+
+static const int check_condition_result =
+ (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
+
+/* function declarations */
+static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
+ struct sdebug_dev_info * devip);
+static int resp_requests(struct scsi_cmnd * SCpnt,
+ struct sdebug_dev_info * devip);
+static int resp_readcap(struct scsi_cmnd * SCpnt,
+ struct sdebug_dev_info * devip);
+static int resp_mode_sense(struct scsi_cmnd * SCpnt, int target,
+ struct sdebug_dev_info * devip);
+static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
+ int num, struct sdebug_dev_info * devip);
+static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
+ int num, struct sdebug_dev_info * devip);
+static int resp_report_luns(struct scsi_cmnd * SCpnt,
+ struct sdebug_dev_info * devip);
+static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
+ int arr_len);
+static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
+ int max_arr_len);
+static void timer_intr_handler(unsigned long);
+static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
+static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
+ int asc, int asq);
+static int check_reset(struct scsi_cmnd * SCpnt,
+ struct sdebug_dev_info * devip);
+static int schedule_resp(struct scsi_cmnd * cmnd,
+ struct sdebug_dev_info * devip,
+ done_funct_t done, int scsi_result, int delta_jiff);
+static void __init sdebug_build_parts(unsigned char * ramp);
+static void __init init_all_queued(void);
+static void stop_all_queued(void);
+static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
+static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
+ const char * dev_id_str, int dev_id_str_len);
+static void do_create_driverfs_files(void);
+static void do_remove_driverfs_files(void);
+
+static int sdebug_add_adapter(void);
+static void sdebug_remove_adapter(void);
+static void sdebug_max_tgts_luns(void);
+
+static struct device pseudo_primary;
+static struct bus_type pseudo_lld_bus;
+
+
+static
+int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
+{
+ unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
+ int block, upper_blk, num, k;
+ int errsts = 0;
+ int target = SCpnt->device->id;
+ struct sdebug_dev_info * devip = NULL;
+ int inj_recovered = 0;
+
+ if (done == NULL)
+ return 0; /* assume mid level reprocessing command */
+
+ if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
+ printk(KERN_INFO "scsi_debug: cmd ");
+ for (k = 0, num = SCpnt->cmd_len; k < num; ++k)
+ printk("%02x ", (int)cmd[k]);
+ printk("\n");
+ }
+ if(target == sdebug_driver_template.this_id) {
+ printk(KERN_INFO "scsi_debug: initiator's id used as "
+ "target!\n");
+ return schedule_resp(SCpnt, NULL, done,
+ DID_NO_CONNECT << 16, 0);
+ }
+
+ if (SCpnt->device->lun >= scsi_debug_max_luns)
+ return schedule_resp(SCpnt, NULL, done,
+ DID_NO_CONNECT << 16, 0);
+ devip = devInfoReg(SCpnt->device);
+ if (NULL == devip)
+ return schedule_resp(SCpnt, NULL, done,
+ DID_NO_CONNECT << 16, 0);
+
+ if ((scsi_debug_every_nth != 0) &&
+ (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
+ scsi_debug_cmnd_count = 0;
+ if (scsi_debug_every_nth < -1)
+ scsi_debug_every_nth = -1;
+ if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
+ return 0; /* ignore command causing timeout */
+ else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
+ inj_recovered = 1; /* to reads and writes below */
+ }
+
+ switch (*cmd) {
+ case INQUIRY: /* mandatory, ignore unit attention */
+ errsts = resp_inquiry(SCpnt, target, devip);
+ break;
+ case REQUEST_SENSE: /* mandatory, ignore unit attention */
+ errsts = resp_requests(SCpnt, devip);
+ break;
+ case REZERO_UNIT: /* actually this is REWIND for SSC */
+ case START_STOP:
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ if ((errsts = check_reset(SCpnt, devip)))
+ break;
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: Medium removal %s\n",
+ cmd[4] ? "inhibited" : "enabled");
+ break;
+ case SEND_DIAGNOSTIC: /* mandatory */
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case TEST_UNIT_READY: /* mandatory */
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case RESERVE:
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case RESERVE_10:
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case RELEASE:
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case RELEASE_10:
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case READ_CAPACITY:
+ errsts = resp_readcap(SCpnt, devip);
+ break;
+ case READ_16:
+ case READ_12:
+ case READ_10:
+ case READ_6:
+ if ((errsts = check_reset(SCpnt, devip)))
+ break;
+ upper_blk = 0;
+ if ((*cmd) == READ_16) {
+ upper_blk = cmd[5] + (cmd[4] << 8) +
+ (cmd[3] << 16) + (cmd[2] << 24);
+ block = cmd[9] + (cmd[8] << 8) +
+ (cmd[7] << 16) + (cmd[6] << 24);
+ num = cmd[13] + (cmd[12] << 8) +
+ (cmd[11] << 16) + (cmd[10] << 24);
+ } else if ((*cmd) == READ_12) {
+ block = cmd[5] + (cmd[4] << 8) +
+ (cmd[3] << 16) + (cmd[2] << 24);
+ num = cmd[9] + (cmd[8] << 8) +
+ (cmd[7] << 16) + (cmd[6] << 24);
+ } else if ((*cmd) == READ_10) {
+ block = cmd[5] + (cmd[4] << 8) +
+ (cmd[3] << 16) + (cmd[2] << 24);
+ num = cmd[8] + (cmd[7] << 8);
+ } else {
+ block = cmd[3] + (cmd[2] << 8) +
+ ((cmd[1] & 0x1f) << 16);
+ num = cmd[4];
+ }
+ errsts = resp_read(SCpnt, upper_blk, block, num, devip);
+ if (inj_recovered && (0 == errsts)) {
+ mk_sense_buffer(devip, RECOVERED_ERROR,
+ THRESHHOLD_EXCEEDED, 0);
+ errsts = check_condition_result;
+ }
+ break;
+ case REPORT_LUNS: /* mandatory, ignore unit attention */
+ errsts = resp_report_luns(SCpnt, devip);
+ break;
+ case VERIFY: /* 10 byte SBC-2 command */
+ errsts = check_reset(SCpnt, devip);
+ break;
+ case WRITE_16:
+ case WRITE_12:
+ case WRITE_10:
+ case WRITE_6:
+ if ((errsts = check_reset(SCpnt, devip)))
+ break;
+ upper_blk = 0;
+ if ((*cmd) == WRITE_16) {
+ upper_blk = cmd[5] + (cmd[4] << 8) +
+ (cmd[3] << 16) + (cmd[2] << 24);
+ block = cmd[9] + (cmd[8] << 8) +
+ (cmd[7] << 16) + (cmd[6] << 24);
+ num = cmd[13] + (cmd[12] << 8) +
+ (cmd[11] << 16) + (cmd[10] << 24);
+ } else if ((*cmd) == WRITE_12) {
+ block = cmd[5] + (cmd[4] << 8) +
+ (cmd[3] << 16) + (cmd[2] << 24);
+ num = cmd[9] + (cmd[8] << 8) +
+ (cmd[7] << 16) + (cmd[6] << 24);
+ } else if ((*cmd) == WRITE_10) {
+ block = cmd[5] + (cmd[4] << 8) +
+ (cmd[3] << 16) + (cmd[2] << 24);
+ num = cmd[8] + (cmd[7] << 8);
+ } else {
+ block = cmd[3] + (cmd[2] << 8) +
+ ((cmd[1] & 0x1f) << 16);
+ num = cmd[4];
+ }
+ errsts = resp_write(SCpnt, upper_blk, block, num, devip);
+ if (inj_recovered && (0 == errsts)) {
+ mk_sense_buffer(devip, RECOVERED_ERROR,
+ THRESHHOLD_EXCEEDED, 0);
+ errsts = check_condition_result;
+ }
+ break;
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ errsts = resp_mode_sense(SCpnt, target, devip);
+ break;
+ case SYNCHRONIZE_CACHE:
+ errsts = check_reset(SCpnt, devip);
+ break;
+ default:
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
+ "supported\n", *cmd);
+ if ((errsts = check_reset(SCpnt, devip)))
+ break; /* Unit attention takes precedence */
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
+ errsts = check_condition_result;
+ break;
+ }
+ return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay);
+}
+
+static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
+{
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
+ printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
+ }
+ return -EINVAL;
+ /* return -ENOTTY; // correct return but upsets fdisk */
+}
+
+static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
+{
+ if (devip->reset) {
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: Reporting Unit "
+ "attention: power on reset\n");
+ devip->reset = 0;
+ mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
+ return check_condition_result;
+ }
+ return 0;
+}
+
+/* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
+static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
+ int arr_len)
+{
+ int k, req_len, act_len, len, active;
+ void * kaddr;
+ void * kaddr_off;
+ struct scatterlist * sgpnt;
+
+ if (0 == scp->request_bufflen)
+ return 0;
+ if (NULL == scp->request_buffer)
+ return (DID_ERROR << 16);
+ if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
+ (scp->sc_data_direction == DMA_FROM_DEVICE)))
+ return (DID_ERROR << 16);
+ if (0 == scp->use_sg) {
+ req_len = scp->request_bufflen;
+ act_len = (req_len < arr_len) ? req_len : arr_len;
+ memcpy(scp->request_buffer, arr, act_len);
+ scp->resid = req_len - act_len;
+ return 0;
+ }
+ sgpnt = (struct scatterlist *)scp->request_buffer;
+ active = 1;
+ for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
+ if (active) {
+ kaddr = (unsigned char *)
+ kmap_atomic(sgpnt->page, KM_USER0);
+ if (NULL == kaddr)
+ return (DID_ERROR << 16);
+ kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
+ len = sgpnt->length;
+ if ((req_len + len) > arr_len) {
+ active = 0;
+ len = arr_len - req_len;
+ }
+ memcpy(kaddr_off, arr + req_len, len);
+ kunmap_atomic(kaddr, KM_USER0);
+ act_len += len;
+ }
+ req_len += sgpnt->length;
+ }
+ scp->resid = req_len - act_len;
+ return 0;
+}
+
+/* Returns number of bytes fetched into 'arr' or -1 if error. */
+static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
+ int max_arr_len)
+{
+ int k, req_len, len, fin;
+ void * kaddr;
+ void * kaddr_off;
+ struct scatterlist * sgpnt;
+
+ if (0 == scp->request_bufflen)
+ return 0;
+ if (NULL == scp->request_buffer)
+ return -1;
+ if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
+ (scp->sc_data_direction == DMA_TO_DEVICE)))
+ return -1;
+ if (0 == scp->use_sg) {
+ req_len = scp->request_bufflen;
+ len = (req_len < max_arr_len) ? req_len : max_arr_len;
+ memcpy(arr, scp->request_buffer, len);
+ return len;
+ }
+ sgpnt = (struct scatterlist *)scp->request_buffer;
+ for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
+ kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
+ if (NULL == kaddr)
+ return -1;
+ kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
+ len = sgpnt->length;
+ if ((req_len + len) > max_arr_len) {
+ len = max_arr_len - req_len;
+ fin = 1;
+ }
+ memcpy(arr + req_len, kaddr_off, len);
+ kunmap_atomic(kaddr, KM_USER0);
+ if (fin)
+ return req_len + len;
+ req_len += sgpnt->length;
+ }
+ return req_len;
+}
+
+
+static const char * inq_vendor_id = "Linux ";
+static const char * inq_product_id = "scsi_debug ";
+static const char * inq_product_rev = "0004";
+
+static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
+ const char * dev_id_str, int dev_id_str_len)
+{
+ int num;
+
+ /* Two identification descriptors: */
+ /* T10 vendor identifier field format (faked) */
+ arr[0] = 0x2; /* ASCII */
+ arr[1] = 0x1;
+ arr[2] = 0x0;
+ memcpy(&arr[4], inq_vendor_id, 8);
+ memcpy(&arr[12], inq_product_id, 16);
+ memcpy(&arr[28], dev_id_str, dev_id_str_len);
+ num = 8 + 16 + dev_id_str_len;
+ arr[3] = num;
+ num += 4;
+ /* NAA IEEE registered identifier (faked) */
+ arr[num] = 0x1; /* binary */
+ arr[num + 1] = 0x3;
+ arr[num + 2] = 0x0;
+ arr[num + 3] = 0x8;
+ arr[num + 4] = 0x51; /* ieee company id=0x123456 (faked) */
+ arr[num + 5] = 0x23;
+ arr[num + 6] = 0x45;
+ arr[num + 7] = 0x60;
+ arr[num + 8] = (dev_id_num >> 24);
+ arr[num + 9] = (dev_id_num >> 16) & 0xff;
+ arr[num + 10] = (dev_id_num >> 8) & 0xff;
+ arr[num + 11] = dev_id_num & 0xff;
+ return num + 12;
+}
+
+
+#define SDEBUG_LONG_INQ_SZ 96
+#define SDEBUG_MAX_INQ_ARR_SZ 128
+
+static int resp_inquiry(struct scsi_cmnd * scp, int target,
+ struct sdebug_dev_info * devip)
+{
+ unsigned char pq_pdt;
+ unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
+ unsigned char *cmd = (unsigned char *)scp->cmnd;
+ int alloc_len;
+
+ alloc_len = (cmd[3] << 8) + cmd[4];
+ memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
+ pq_pdt = (scsi_debug_ptype & 0x1f);
+ arr[0] = pq_pdt;
+ if (0x2 & cmd[1]) { /* CMDDT bit set */
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
+ 0);
+ return check_condition_result;
+ } else if (0x1 & cmd[1]) { /* EVPD bit set */
+ int dev_id_num, len;
+ char dev_id_str[6];
+
+ dev_id_num = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
+ (devip->target * 1000) + devip->lun;
+ len = scnprintf(dev_id_str, 6, "%d", dev_id_num);
+ if (0 == cmd[2]) { /* supported vital product data pages */
+ arr[3] = 3;
+ arr[4] = 0x0; /* this page */
+ arr[5] = 0x80; /* unit serial number */
+ arr[6] = 0x83; /* device identification */
+ } else if (0x80 == cmd[2]) { /* unit serial number */
+ arr[1] = 0x80;
+ arr[3] = len;
+ memcpy(&arr[4], dev_id_str, len);
+ } else if (0x83 == cmd[2]) { /* device identification */
+ arr[1] = 0x83;
+ arr[3] = inquiry_evpd_83(&arr[4], dev_id_num,
+ dev_id_str, len);
+ } else {
+ /* Illegal request, invalid field in cdb */
+ mk_sense_buffer(devip, ILLEGAL_REQUEST,
+ INVALID_FIELD_IN_CDB, 0);
+ return check_condition_result;
+ }
+ return fill_from_dev_buffer(scp, arr,
+ min(alloc_len, SDEBUG_MAX_INQ_ARR_SZ));
+ }
+ /* drops through here for a standard inquiry */
+ arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
+ arr[2] = scsi_debug_scsi_level;
+ arr[3] = 2; /* response_data_format==2 */
+ arr[4] = SDEBUG_LONG_INQ_SZ - 5;
+ arr[6] = 0x1; /* claim: ADDR16 */
+ /* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
+ arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
+ memcpy(&arr[8], inq_vendor_id, 8);
+ memcpy(&arr[16], inq_product_id, 16);
+ memcpy(&arr[32], inq_product_rev, 4);
+ /* version descriptors (2 bytes each) follow */
+ arr[58] = 0x0; arr[59] = 0x40; /* SAM-2 */
+ arr[60] = 0x3; arr[61] = 0x0; /* SPC-3 */
+ if (scsi_debug_ptype == 0) {
+ arr[62] = 0x1; arr[63] = 0x80; /* SBC */
+ } else if (scsi_debug_ptype == 1) {
+ arr[62] = 0x2; arr[63] = 0x00; /* SSC */
+ }
+ return fill_from_dev_buffer(scp, arr,
+ min(alloc_len, SDEBUG_LONG_INQ_SZ));
+}
+
+static int resp_requests(struct scsi_cmnd * scp,
+ struct sdebug_dev_info * devip)
+{
+ unsigned char * sbuff;
+ unsigned char *cmd = (unsigned char *)scp->cmnd;
+ unsigned char arr[SDEBUG_SENSE_LEN];
+ int len = 18;
+
+ memset(arr, 0, SDEBUG_SENSE_LEN);
+ if (devip->reset == 1)
+ mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0);
+ sbuff = devip->sense_buff;
+ if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
+ /* DESC bit set and sense_buff in fixed format */
+ arr[0] = 0x72;
+ arr[1] = sbuff[2]; /* sense key */
+ arr[2] = sbuff[12]; /* asc */
+ arr[3] = sbuff[13]; /* ascq */
+ len = 8;
+ } else
+ memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
+ mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0);
+ return fill_from_dev_buffer(scp, arr, len);
+}
+
+#define SDEBUG_READCAP_ARR_SZ 8
+static int resp_readcap(struct scsi_cmnd * scp,
+ struct sdebug_dev_info * devip)
+{
+ unsigned char arr[SDEBUG_READCAP_ARR_SZ];
+ unsigned long capac;
+ int errsts;
+
+ if ((errsts = check_reset(scp, devip)))
+ return errsts;
+ memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
+ capac = (unsigned long)sdebug_capacity - 1;
+ arr[0] = (capac >> 24);
+ arr[1] = (capac >> 16) & 0xff;
+ arr[2] = (capac >> 8) & 0xff;
+ arr[3] = capac & 0xff;
+ arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
+ arr[7] = SECT_SIZE_PER(target) & 0xff;
+ return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
+}
+
+/* <<Following mode page info copied from ST318451LW>> */
+
+static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
+{ /* Read-Write Error Recovery page for mode_sense */
+ unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
+ 5, 0, 0xff, 0xff};
+
+ memcpy(p, err_recov_pg, sizeof(err_recov_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(err_recov_pg) - 2);
+ return sizeof(err_recov_pg);
+}
+
+static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
+{ /* Disconnect-Reconnect page for mode_sense */
+ unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+
+ memcpy(p, disconnect_pg, sizeof(disconnect_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(disconnect_pg) - 2);
+ return sizeof(disconnect_pg);
+}
+
+static int resp_format_pg(unsigned char * p, int pcontrol, int target)
+{ /* Format device page for mode_sense */
+ unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x40, 0, 0, 0};
+
+ memcpy(p, format_pg, sizeof(format_pg));
+ p[10] = (sdebug_sectors_per >> 8) & 0xff;
+ p[11] = sdebug_sectors_per & 0xff;
+ p[12] = (SECT_SIZE >> 8) & 0xff;
+ p[13] = SECT_SIZE & 0xff;
+ if (DEV_REMOVEABLE(target))
+ p[20] |= 0x20; /* should agree with INQUIRY */
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(format_pg) - 2);
+ return sizeof(format_pg);
+}
+
+static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
+{ /* Caching page for mode_sense */
+ unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
+ 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
+
+ memcpy(p, caching_pg, sizeof(caching_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(caching_pg) - 2);
+ return sizeof(caching_pg);
+}
+
+static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
+{ /* Control mode page for mode_sense */
+ unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0x2, 0x4b};
+
+ if (scsi_debug_dsense)
+ ctrl_m_pg[2] |= 0x4;
+ memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
+ return sizeof(ctrl_m_pg);
+}
+
+static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
+{ /* Informational Exceptions control mode page for mode_sense */
+ unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
+ 0, 0, 0x0, 0x0};
+ memcpy(p, iec_m_pg, sizeof(iec_m_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(iec_m_pg) - 2);
+ return sizeof(iec_m_pg);
+}
+
+#define SDEBUG_MAX_MSENSE_SZ 256
+
+static int resp_mode_sense(struct scsi_cmnd * scp, int target,
+ struct sdebug_dev_info * devip)
+{
+ unsigned char dbd;
+ int pcontrol, pcode, subpcode;
+ unsigned char dev_spec;
+ int alloc_len, msense_6, offset, len, errsts;
+ unsigned char * ap;
+ unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
+ unsigned char *cmd = (unsigned char *)scp->cmnd;
+
+ if ((errsts = check_reset(scp, devip)))
+ return errsts;
+ dbd = cmd[1] & 0x8;
+ pcontrol = (cmd[2] & 0xc0) >> 6;
+ pcode = cmd[2] & 0x3f;
+ subpcode = cmd[3];
+ msense_6 = (MODE_SENSE == cmd[0]);
+ alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
+ memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
+ if (0x3 == pcontrol) { /* Saving values not supported */
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
+ 0);
+ return check_condition_result;
+ }
+ dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
+ if (msense_6) {
+ arr[2] = dev_spec;
+ offset = 4;
+ } else {
+ arr[3] = dev_spec;
+ offset = 8;
+ }
+ ap = arr + offset;
+
+ if (0 != subpcode) { /* TODO: Control Extension page */
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
+ 0);
+ return check_condition_result;
+ }
+ switch (pcode) {
+ case 0x1: /* Read-Write error recovery page, direct access */
+ len = resp_err_recov_pg(ap, pcontrol, target);
+ offset += len;
+ break;
+ case 0x2: /* Disconnect-Reconnect page, all devices */
+ len = resp_disconnect_pg(ap, pcontrol, target);
+ offset += len;
+ break;
+ case 0x3: /* Format device page, direct access */
+ len = resp_format_pg(ap, pcontrol, target);
+ offset += len;
+ break;
+ case 0x8: /* Caching page, direct access */
+ len = resp_caching_pg(ap, pcontrol, target);
+ offset += len;
+ break;
+ case 0xa: /* Control Mode page, all devices */
+ len = resp_ctrl_m_pg(ap, pcontrol, target);
+ offset += len;
+ break;
+ case 0x1c: /* Informational Exceptions Mode page, all devices */
+ len = resp_iec_m_pg(ap, pcontrol, target);
+ offset += len;
+ break;
+ case 0x3f: /* Read all Mode pages */
+ len = resp_err_recov_pg(ap, pcontrol, target);
+ len += resp_disconnect_pg(ap + len, pcontrol, target);
+ len += resp_format_pg(ap + len, pcontrol, target);
+ len += resp_caching_pg(ap + len, pcontrol, target);
+ len += resp_ctrl_m_pg(ap + len, pcontrol, target);
+ len += resp_iec_m_pg(ap + len, pcontrol, target);
+ offset += len;
+ break;
+ default:
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
+ 0);
+ return check_condition_result;
+ }
+ if (msense_6)
+ arr[0] = offset - 1;
+ else {
+ arr[0] = ((offset - 2) >> 8) & 0xff;
+ arr[1] = (offset - 2) & 0xff;
+ }
+ return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
+}
+
+static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
+ int num, struct sdebug_dev_info * devip)
+{
+ unsigned long iflags;
+ int ret;
+
+ if (upper_blk || (block + num > sdebug_capacity)) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
+ 0);
+ return check_condition_result;
+ }
+ if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
+ (block <= OPT_MEDIUM_ERR_ADDR) &&
+ ((block + num) > OPT_MEDIUM_ERR_ADDR)) {
+ mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
+ 0);
+ /* claim unrecoverable read error */
+ return check_condition_result;
+ }
+ read_lock_irqsave(&atomic_rw, iflags);
+ ret = fill_from_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
+ num * SECT_SIZE);
+ read_unlock_irqrestore(&atomic_rw, iflags);
+ return ret;
+}
+
+static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
+ int num, struct sdebug_dev_info * devip)
+{
+ unsigned long iflags;
+ int res;
+
+ if (upper_blk || (block + num > sdebug_capacity)) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
+ 0);
+ return check_condition_result;
+ }
+
+ write_lock_irqsave(&atomic_rw, iflags);
+ res = fetch_to_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
+ num * SECT_SIZE);
+ write_unlock_irqrestore(&atomic_rw, iflags);
+ if (-1 == res)
+ return (DID_ERROR << 16);
+ else if ((res < (num * SECT_SIZE)) &&
+ (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
+ printk(KERN_INFO "scsi_debug: write: cdb indicated=%d, "
+ " IO sent=%d bytes\n", num * SECT_SIZE, res);
+ return 0;
+}
+
+#define SDEBUG_RLUN_ARR_SZ 128
+
+static int resp_report_luns(struct scsi_cmnd * scp,
+ struct sdebug_dev_info * devip)
+{
+ unsigned int alloc_len;
+ int lun_cnt, i, upper;
+ unsigned char *cmd = (unsigned char *)scp->cmnd;
+ int select_report = (int)cmd[2];
+ struct scsi_lun *one_lun;
+ unsigned char arr[SDEBUG_RLUN_ARR_SZ];
+
+ alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
+ if ((alloc_len < 16) || (select_report > 2)) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
+ 0);
+ return check_condition_result;
+ }
+ /* can produce response with up to 16k luns (lun 0 to lun 16383) */
+ memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
+ lun_cnt = scsi_debug_max_luns;
+ arr[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff;
+ arr[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff;
+ lun_cnt = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
+ sizeof(struct scsi_lun)), lun_cnt);
+ one_lun = (struct scsi_lun *) &arr[8];
+ for (i = 0; i < lun_cnt; i++) {
+ upper = (i >> 8) & 0x3f;
+ if (upper)
+ one_lun[i].scsi_lun[0] =
+ (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
+ one_lun[i].scsi_lun[1] = i & 0xff;
+ }
+ return fill_from_dev_buffer(scp, arr,
+ min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
+}
+
+/* When timer goes off this function is called. */
+static void timer_intr_handler(unsigned long indx)
+{
+ struct sdebug_queued_cmd * sqcp;
+ unsigned long iflags;
+
+ if (indx >= SCSI_DEBUG_CANQUEUE) {
+ printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
+ "large\n");
+ return;
+ }
+ spin_lock_irqsave(&queued_arr_lock, iflags);
+ sqcp = &queued_arr[(int)indx];
+ if (! sqcp->in_use) {
+ printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
+ "interrupt\n");
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ return;
+ }
+ sqcp->in_use = 0;
+ if (sqcp->done_funct) {
+ sqcp->a_cmnd->result = sqcp->scsi_result;
+ sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
+ }
+ sqcp->done_funct = NULL;
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+}
+
+static int scsi_debug_slave_alloc(struct scsi_device * sdp)
+{
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
+ sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+ return 0;
+}
+
+static int scsi_debug_slave_configure(struct scsi_device * sdp)
+{
+ struct sdebug_dev_info * devip;
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
+ sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+ if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
+ sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
+ devip = devInfoReg(sdp);
+ sdp->hostdata = devip;
+ if (sdp->host->cmd_per_lun)
+ scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
+ sdp->host->cmd_per_lun);
+ return 0;
+}
+
+static void scsi_debug_slave_destroy(struct scsi_device * sdp)
+{
+ struct sdebug_dev_info * devip =
+ (struct sdebug_dev_info *)sdp->hostdata;
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
+ sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+ if (devip) {
+ /* make this slot avaliable for re-use */
+ devip->used = 0;
+ sdp->hostdata = NULL;
+ }
+}
+
+static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
+{
+ struct sdebug_host_info * sdbg_host;
+ struct sdebug_dev_info * open_devip = NULL;
+ struct sdebug_dev_info * devip =
+ (struct sdebug_dev_info *)sdev->hostdata;
+
+ if (devip)
+ return devip;
+ sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
+ if(! sdbg_host) {
+ printk(KERN_ERR "Host info NULL\n");
+ return NULL;
+ }
+ list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
+ if ((devip->used) && (devip->channel == sdev->channel) &&
+ (devip->target == sdev->id) &&
+ (devip->lun == sdev->lun))
+ return devip;
+ else {
+ if ((!devip->used) && (!open_devip))
+ open_devip = devip;
+ }
+ }
+ if (NULL == open_devip) { /* try and make a new one */
+ open_devip = kmalloc(sizeof(*open_devip),GFP_KERNEL);
+ if (NULL == open_devip) {
+ printk(KERN_ERR "%s: out of memory at line %d\n",
+ __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ memset(open_devip, 0, sizeof(*open_devip));
+ open_devip->sdbg_host = sdbg_host;
+ list_add_tail(&open_devip->dev_list,
+ &sdbg_host->dev_info_list);
+ }
+ if (open_devip) {
+ open_devip->channel = sdev->channel;
+ open_devip->target = sdev->id;
+ open_devip->lun = sdev->lun;
+ open_devip->sdbg_host = sdbg_host;
+ open_devip->reset = 1;
+ open_devip->used = 1;
+ memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
+ if (scsi_debug_dsense)
+ open_devip->sense_buff[0] = 0x72;
+ else {
+ open_devip->sense_buff[0] = 0x70;
+ open_devip->sense_buff[7] = 0xa;
+ }
+ return open_devip;
+ }
+ return NULL;
+}
+
+static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
+ int asc, int asq)
+{
+ unsigned char * sbuff;
+
+ sbuff = devip->sense_buff;
+ memset(sbuff, 0, SDEBUG_SENSE_LEN);
+ if (scsi_debug_dsense) {
+ sbuff[0] = 0x72; /* descriptor, current */
+ sbuff[1] = key;
+ sbuff[2] = asc;
+ sbuff[3] = asq;
+ } else {
+ sbuff[0] = 0x70; /* fixed, current */
+ sbuff[2] = key;
+ sbuff[7] = 0xa; /* implies 18 byte sense buffer */
+ sbuff[12] = asc;
+ sbuff[13] = asq;
+ }
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: [sense_key,asc,ascq]: "
+ "[0x%x,0x%x,0x%x]\n", key, asc, asq);
+}
+
+static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
+{
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: abort\n");
+ ++num_aborts;
+ stop_queued_cmnd(SCpnt);
+ return SUCCESS;
+}
+
+static int scsi_debug_biosparam(struct scsi_device *sdev,
+ struct block_device * bdev, sector_t capacity, int *info)
+{
+ int res;
+ unsigned char *buf;
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: biosparam\n");
+ buf = scsi_bios_ptable(bdev);
+ if (buf) {
+ res = scsi_partsize(buf, capacity,
+ &info[2], &info[0], &info[1]);
+ kfree(buf);
+ if (! res)
+ return res;
+ }
+ info[0] = sdebug_heads;
+ info[1] = sdebug_sectors_per;
+ info[2] = sdebug_cylinders_per;
+ return 0;
+}
+
+static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
+{
+ struct sdebug_dev_info * devip;
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: device_reset\n");
+ ++num_dev_resets;
+ if (SCpnt) {
+ devip = devInfoReg(SCpnt->device);
+ if (devip)
+ devip->reset = 1;
+ }
+ return SUCCESS;
+}
+
+static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
+{
+ struct sdebug_host_info *sdbg_host;
+ struct sdebug_dev_info * dev_info;
+ struct scsi_device * sdp;
+ struct Scsi_Host * hp;
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: bus_reset\n");
+ ++num_bus_resets;
+ if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
+ sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
+ if (sdbg_host) {
+ list_for_each_entry(dev_info,
+ &sdbg_host->dev_info_list,
+ dev_list)
+ dev_info->reset = 1;
+ }
+ }
+ return SUCCESS;
+}
+
+static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
+{
+ struct sdebug_host_info * sdbg_host;
+ struct sdebug_dev_info * dev_info;
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: host_reset\n");
+ ++num_host_resets;
+ spin_lock(&sdebug_host_list_lock);
+ list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
+ list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
+ dev_list)
+ dev_info->reset = 1;
+ }
+ spin_unlock(&sdebug_host_list_lock);
+ stop_all_queued();
+ return SUCCESS;
+}
+
+/* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
+static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
+{
+ unsigned long iflags;
+ int k;
+ struct sdebug_queued_cmd * sqcp;
+
+ spin_lock_irqsave(&queued_arr_lock, iflags);
+ for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
+ sqcp = &queued_arr[k];
+ if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
+ del_timer_sync(&sqcp->cmnd_timer);
+ sqcp->in_use = 0;
+ sqcp->a_cmnd = NULL;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
+}
+
+/* Deletes (stops) timers of all queued commands */
+static void stop_all_queued(void)
+{
+ unsigned long iflags;
+ int k;
+ struct sdebug_queued_cmd * sqcp;
+
+ spin_lock_irqsave(&queued_arr_lock, iflags);
+ for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
+ sqcp = &queued_arr[k];
+ if (sqcp->in_use && sqcp->a_cmnd) {
+ del_timer_sync(&sqcp->cmnd_timer);
+ sqcp->in_use = 0;
+ sqcp->a_cmnd = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+}
+
+/* Initializes timers in queued array */
+static void __init init_all_queued(void)
+{
+ unsigned long iflags;
+ int k;
+ struct sdebug_queued_cmd * sqcp;
+
+ spin_lock_irqsave(&queued_arr_lock, iflags);
+ for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
+ sqcp = &queued_arr[k];
+ init_timer(&sqcp->cmnd_timer);
+ sqcp->in_use = 0;
+ sqcp->a_cmnd = NULL;
+ }
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+}
+
+static void __init sdebug_build_parts(unsigned char * ramp)
+{
+ struct partition * pp;
+ int starts[SDEBUG_MAX_PARTS + 2];
+ int sectors_per_part, num_sectors, k;
+ int heads_by_sects, start_sec, end_sec;
+
+ /* assume partition table already zeroed */
+ if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
+ return;
+ if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
+ scsi_debug_num_parts = SDEBUG_MAX_PARTS;
+ printk(KERN_WARNING "scsi_debug:build_parts: reducing "
+ "partitions to %d\n", SDEBUG_MAX_PARTS);
+ }
+ num_sectors = (int)(sdebug_store_size / SECT_SIZE);
+ sectors_per_part = (num_sectors - sdebug_sectors_per)
+ / scsi_debug_num_parts;
+ heads_by_sects = sdebug_heads * sdebug_sectors_per;
+ starts[0] = sdebug_sectors_per;
+ for (k = 1; k < scsi_debug_num_parts; ++k)
+ starts[k] = ((k * sectors_per_part) / heads_by_sects)
+ * heads_by_sects;
+ starts[scsi_debug_num_parts] = num_sectors;
+ starts[scsi_debug_num_parts + 1] = 0;
+
+ ramp[510] = 0x55; /* magic partition markings */
+ ramp[511] = 0xAA;
+ pp = (struct partition *)(ramp + 0x1be);
+ for (k = 0; starts[k + 1]; ++k, ++pp) {
+ start_sec = starts[k];
+ end_sec = starts[k + 1] - 1;
+ pp->boot_ind = 0;
+
+ pp->cyl = start_sec / heads_by_sects;
+ pp->head = (start_sec - (pp->cyl * heads_by_sects))
+ / sdebug_sectors_per;
+ pp->sector = (start_sec % sdebug_sectors_per) + 1;
+
+ pp->end_cyl = end_sec / heads_by_sects;
+ pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
+ / sdebug_sectors_per;
+ pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
+
+ pp->start_sect = start_sec;
+ pp->nr_sects = end_sec - start_sec + 1;
+ pp->sys_ind = 0x83; /* plain Linux partition */
+ }
+}
+
+static int schedule_resp(struct scsi_cmnd * cmnd,
+ struct sdebug_dev_info * devip,
+ done_funct_t done, int scsi_result, int delta_jiff)
+{
+ if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
+ if (scsi_result) {
+ struct scsi_device * sdp = cmnd->device;
+
+ printk(KERN_INFO "scsi_debug: <%u %u %u %u> "
+ "non-zero result=0x%x\n", sdp->host->host_no,
+ sdp->channel, sdp->id, sdp->lun, scsi_result);
+ }
+ }
+ if (cmnd && devip) {
+ /* simulate autosense by this driver */
+ if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
+ memcpy(cmnd->sense_buffer, devip->sense_buff,
+ (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
+ SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
+ }
+ if (delta_jiff <= 0) {
+ if (cmnd)
+ cmnd->result = scsi_result;
+ if (done)
+ done(cmnd);
+ return 0;
+ } else {
+ unsigned long iflags;
+ int k;
+ struct sdebug_queued_cmd * sqcp = NULL;
+
+ spin_lock_irqsave(&queued_arr_lock, iflags);
+ for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
+ sqcp = &queued_arr[k];
+ if (! sqcp->in_use)
+ break;
+ }
+ if (k >= SCSI_DEBUG_CANQUEUE) {
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
+ return 1; /* report busy to mid level */
+ }
+ sqcp->in_use = 1;
+ sqcp->a_cmnd = cmnd;
+ sqcp->scsi_result = scsi_result;
+ sqcp->done_funct = done;
+ sqcp->cmnd_timer.function = timer_intr_handler;
+ sqcp->cmnd_timer.data = k;
+ sqcp->cmnd_timer.expires = jiffies + delta_jiff;
+ add_timer(&sqcp->cmnd_timer);
+ spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ if (cmnd)
+ cmnd->result = 0;
+ return 0;
+ }
+}
+
+/* Set 'perm' (4th argument) to 0 to disable module_param's definition
+ * of sysfs parameters (which module_param doesn't yet support).
+ * Sysfs parameters defined explicitly below.
+ */
+module_param_named(add_host, scsi_debug_add_host, int, 0); /* perm=0644 */
+module_param_named(delay, scsi_debug_delay, int, 0); /* perm=0644 */
+module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, 0);
+module_param_named(dsense, scsi_debug_dsense, int, 0);
+module_param_named(every_nth, scsi_debug_every_nth, int, 0);
+module_param_named(max_luns, scsi_debug_max_luns, int, 0);
+module_param_named(num_parts, scsi_debug_num_parts, int, 0);
+module_param_named(num_tgts, scsi_debug_num_tgts, int, 0);
+module_param_named(opts, scsi_debug_opts, int, 0); /* perm=0644 */
+module_param_named(ptype, scsi_debug_ptype, int, 0);
+module_param_named(scsi_level, scsi_debug_scsi_level, int, 0);
+
+MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
+MODULE_DESCRIPTION("SCSI debug adapter driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SCSI_DEBUG_VERSION);
+
+MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
+MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
+MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs");
+MODULE_PARM_DESC(dsense, "use descriptor sense format(def: fixed)");
+MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
+MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate");
+MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
+MODULE_PARM_DESC(num_tgts, "number of SCSI targets per host to simulate");
+MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
+MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
+MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
+
+
+static char sdebug_info[256];
+
+static const char * scsi_debug_info(struct Scsi_Host * shp)
+{
+ sprintf(sdebug_info, "scsi_debug, version %s [%s], "
+ "dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
+ scsi_debug_version_date, scsi_debug_dev_size_mb,
+ scsi_debug_opts);
+ return sdebug_info;
+}
+
+/* scsi_debug_proc_info
+ * Used if the driver currently has no own support for /proc/scsi
+ */
+static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ int len, pos, begin;
+ int orig_length;
+
+ orig_length = length;
+
+ if (inout == 1) {
+ char arr[16];
+ int minLen = length > 15 ? 15 : length;
+
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ memcpy(arr, buffer, minLen);
+ arr[minLen] = '\0';
+ if (1 != sscanf(arr, "%d", &pos))
+ return -EINVAL;
+ scsi_debug_opts = pos;
+ if (scsi_debug_every_nth != 0)
+ scsi_debug_cmnd_count = 0;
+ return length;
+ }
+ begin = 0;
+ pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
+ "%s [%s]\n"
+ "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
+ "every_nth=%d(curr:%d)\n"
+ "delay=%d, max_luns=%d, scsi_level=%d\n"
+ "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
+ "number of aborts=%d, device_reset=%d, bus_resets=%d, "
+ "host_resets=%d\n",
+ SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
+ scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
+ scsi_debug_cmnd_count, scsi_debug_delay,
+ scsi_debug_max_luns, scsi_debug_scsi_level,
+ SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
+ num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if (len > length)
+ len = length;
+ return len;
+}
+
+static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
+}
+
+static ssize_t sdebug_delay_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int delay;
+ char work[20];
+
+ if (1 == sscanf(buf, "%10s", work)) {
+ if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
+ scsi_debug_delay = delay;
+ return count;
+ }
+ }
+ return -EINVAL;
+}
+DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
+ sdebug_delay_store);
+
+static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
+}
+
+static ssize_t sdebug_opts_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int opts;
+ char work[20];
+
+ if (1 == sscanf(buf, "%10s", work)) {
+ if (0 == strnicmp(work,"0x", 2)) {
+ if (1 == sscanf(&work[2], "%x", &opts))
+ goto opts_done;
+ } else {
+ if (1 == sscanf(work, "%d", &opts))
+ goto opts_done;
+ }
+ }
+ return -EINVAL;
+opts_done:
+ scsi_debug_opts = opts;
+ scsi_debug_cmnd_count = 0;
+ return count;
+}
+DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
+ sdebug_opts_store);
+
+static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
+}
+static ssize_t sdebug_ptype_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int n;
+
+ if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+ scsi_debug_ptype = n;
+ return count;
+ }
+ return -EINVAL;
+}
+DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
+
+static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
+}
+static ssize_t sdebug_dsense_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int n;
+
+ if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+ scsi_debug_dsense = n;
+ return count;
+ }
+ return -EINVAL;
+}
+DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
+ sdebug_dsense_store);
+
+static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
+}
+static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int n;
+
+ if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+ scsi_debug_num_tgts = n;
+ sdebug_max_tgts_luns();
+ return count;
+ }
+ return -EINVAL;
+}
+DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
+ sdebug_num_tgts_store);
+
+static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
+}
+DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
+
+static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
+}
+DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
+
+static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
+}
+static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int nth;
+
+ if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
+ scsi_debug_every_nth = nth;
+ scsi_debug_cmnd_count = 0;
+ return count;
+ }
+ return -EINVAL;
+}
+DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
+ sdebug_every_nth_store);
+
+static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
+}
+static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int n;
+
+ if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+ scsi_debug_max_luns = n;
+ sdebug_max_tgts_luns();
+ return count;
+ }
+ return -EINVAL;
+}
+DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
+ sdebug_max_luns_store);
+
+static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
+}
+DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
+
+static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
+}
+
+static ssize_t sdebug_add_host_store(struct device_driver * ddp,
+ const char * buf, size_t count)
+{
+ int delta_hosts;
+ char work[20];
+
+ if (1 != sscanf(buf, "%10s", work))
+ return -EINVAL;
+ { /* temporary hack around sscanf() problem with -ve nums */
+ int neg = 0;
+
+ if ('-' == *work)
+ neg = 1;
+ if (1 != sscanf(work + neg, "%d", &delta_hosts))
+ return -EINVAL;
+ if (neg)
+ delta_hosts = -delta_hosts;
+ }
+ if (delta_hosts > 0) {
+ do {
+ sdebug_add_adapter();
+ } while (--delta_hosts);
+ } else if (delta_hosts < 0) {
+ do {
+ sdebug_remove_adapter();
+ } while (++delta_hosts);
+ }
+ return count;
+}
+DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
+ sdebug_add_host_store);
+
+static void do_create_driverfs_files(void)
+{
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
+ driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
+}
+
+static void do_remove_driverfs_files(void)
+{
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
+}
+
+static int __init scsi_debug_init(void)
+{
+ unsigned long sz;
+ int host_to_add;
+ int k;
+
+ if (scsi_debug_dev_size_mb < 1)
+ scsi_debug_dev_size_mb = 1; /* force minimum 1 MB ramdisk */
+ sdebug_store_size = (unsigned long)scsi_debug_dev_size_mb * 1048576;
+ sdebug_capacity = sdebug_store_size / SECT_SIZE;
+
+ /* play around with geometry, don't waste too much on track 0 */
+ sdebug_heads = 8;
+ sdebug_sectors_per = 32;
+ if (scsi_debug_dev_size_mb >= 16)
+ sdebug_heads = 32;
+ else if (scsi_debug_dev_size_mb >= 256)
+ sdebug_heads = 64;
+ sdebug_cylinders_per = (unsigned long)sdebug_capacity /
+ (sdebug_sectors_per * sdebug_heads);
+ if (sdebug_cylinders_per >= 1024) {
+ /* other LLDs do this; implies >= 1GB ram disk ... */
+ sdebug_heads = 255;
+ sdebug_sectors_per = 63;
+ sdebug_cylinders_per = (unsigned long)sdebug_capacity /
+ (sdebug_sectors_per * sdebug_heads);
+ }
+
+ sz = sdebug_store_size;
+ fake_storep = vmalloc(sz);
+ if (NULL == fake_storep) {
+ printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
+ return -ENOMEM;
+ }
+ memset(fake_storep, 0, sz);
+ if (scsi_debug_num_parts > 0)
+ sdebug_build_parts(fake_storep);
+
+ init_all_queued();
+
+ device_register(&pseudo_primary);
+ bus_register(&pseudo_lld_bus);
+ driver_register(&sdebug_driverfs_driver);
+ do_create_driverfs_files();
+
+ sdebug_driver_template.proc_name = (char *)sdebug_proc_name;
+
+ host_to_add = scsi_debug_add_host;
+ scsi_debug_add_host = 0;
+
+ for (k = 0; k < host_to_add; k++) {
+ if (sdebug_add_adapter()) {
+ printk(KERN_ERR "scsi_debug_init: "
+ "sdebug_add_adapter failed k=%d\n", k);
+ break;
+ }
+ }
+
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
+ printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
+ scsi_debug_add_host);
+ }
+ return 0;
+}
+
+static void __exit scsi_debug_exit(void)
+{
+ int k = scsi_debug_add_host;
+
+ stop_all_queued();
+ for (; k; k--)
+ sdebug_remove_adapter();
+ do_remove_driverfs_files();
+ driver_unregister(&sdebug_driverfs_driver);
+ bus_unregister(&pseudo_lld_bus);
+ device_unregister(&pseudo_primary);
+
+ vfree(fake_storep);
+}
+
+device_initcall(scsi_debug_init);
+module_exit(scsi_debug_exit);
+
+void pseudo_0_release(struct device * dev)
+{
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
+}
+
+static struct device pseudo_primary = {
+ .bus_id = "pseudo_0",
+ .release = pseudo_0_release,
+};
+
+static int pseudo_lld_bus_match(struct device *dev,
+ struct device_driver *dev_driver)
+{
+ return 1;
+}
+
+static struct bus_type pseudo_lld_bus = {
+ .name = "pseudo",
+ .match = pseudo_lld_bus_match,
+};
+
+static void sdebug_release_adapter(struct device * dev)
+{
+ struct sdebug_host_info *sdbg_host;
+
+ sdbg_host = to_sdebug_host(dev);
+ kfree(sdbg_host);
+}
+
+static int sdebug_add_adapter(void)
+{
+ int k, devs_per_host;
+ int error = 0;
+ struct sdebug_host_info *sdbg_host;
+ struct sdebug_dev_info *sdbg_devinfo;
+ struct list_head *lh, *lh_sf;
+
+ sdbg_host = kmalloc(sizeof(*sdbg_host),GFP_KERNEL);
+
+ if (NULL == sdbg_host) {
+ printk(KERN_ERR "%s: out of memory at line %d\n",
+ __FUNCTION__, __LINE__);
+ return -ENOMEM;
+ }
+
+ memset(sdbg_host, 0, sizeof(*sdbg_host));
+ INIT_LIST_HEAD(&sdbg_host->dev_info_list);
+
+ devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
+ for (k = 0; k < devs_per_host; k++) {
+ sdbg_devinfo = kmalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
+ if (NULL == sdbg_devinfo) {
+ printk(KERN_ERR "%s: out of memory at line %d\n",
+ __FUNCTION__, __LINE__);
+ error = -ENOMEM;
+ goto clean;
+ }
+ memset(sdbg_devinfo, 0, sizeof(*sdbg_devinfo));
+ sdbg_devinfo->sdbg_host = sdbg_host;
+ list_add_tail(&sdbg_devinfo->dev_list,
+ &sdbg_host->dev_info_list);
+ }
+
+ spin_lock(&sdebug_host_list_lock);
+ list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
+ spin_unlock(&sdebug_host_list_lock);
+
+ sdbg_host->dev.bus = &pseudo_lld_bus;
+ sdbg_host->dev.parent = &pseudo_primary;
+ sdbg_host->dev.release = &sdebug_release_adapter;
+ sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
+
+ error = device_register(&sdbg_host->dev);
+
+ if (error)
+ goto clean;
+
+ ++scsi_debug_add_host;
+ return error;
+
+clean:
+ list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
+ sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
+ dev_list);
+ list_del(&sdbg_devinfo->dev_list);
+ kfree(sdbg_devinfo);
+ }
+
+ kfree(sdbg_host);
+ return error;
+}
+
+static void sdebug_remove_adapter(void)
+{
+ struct sdebug_host_info * sdbg_host = NULL;
+
+ spin_lock(&sdebug_host_list_lock);
+ if (!list_empty(&sdebug_host_list)) {
+ sdbg_host = list_entry(sdebug_host_list.prev,
+ struct sdebug_host_info, host_list);
+ list_del(&sdbg_host->host_list);
+ }
+ spin_unlock(&sdebug_host_list_lock);
+
+ if (!sdbg_host)
+ return;
+
+ device_unregister(&sdbg_host->dev);
+ --scsi_debug_add_host;
+}
+
+static int sdebug_driver_probe(struct device * dev)
+{
+ int error = 0;
+ struct sdebug_host_info *sdbg_host;
+ struct Scsi_Host *hpnt;
+
+ sdbg_host = to_sdebug_host(dev);
+
+ hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
+ if (NULL == hpnt) {
+ printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
+ error = -ENODEV;
+ return error;
+ }
+
+ sdbg_host->shost = hpnt;
+ *((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
+ if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
+ hpnt->max_id = scsi_debug_num_tgts + 1;
+ else
+ hpnt->max_id = scsi_debug_num_tgts;
+ hpnt->max_lun = scsi_debug_max_luns;
+
+ error = scsi_add_host(hpnt, &sdbg_host->dev);
+ if (error) {
+ printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
+ error = -ENODEV;
+ scsi_host_put(hpnt);
+ } else
+ scsi_scan_host(hpnt);
+
+
+ return error;
+}
+
+static int sdebug_driver_remove(struct device * dev)
+{
+ struct list_head *lh, *lh_sf;
+ struct sdebug_host_info *sdbg_host;
+ struct sdebug_dev_info *sdbg_devinfo;
+
+ sdbg_host = to_sdebug_host(dev);
+
+ if (!sdbg_host) {
+ printk(KERN_ERR "%s: Unable to locate host info\n",
+ __FUNCTION__);
+ return -ENODEV;
+ }
+
+ scsi_remove_host(sdbg_host->shost);
+
+ list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
+ sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
+ dev_list);
+ list_del(&sdbg_devinfo->dev_list);
+ kfree(sdbg_devinfo);
+ }
+
+ scsi_host_put(sdbg_host->shost);
+ return 0;
+}
+
+static void sdebug_max_tgts_luns(void)
+{
+ struct sdebug_host_info * sdbg_host;
+ struct Scsi_Host *hpnt;
+
+ spin_lock(&sdebug_host_list_lock);
+ list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
+ hpnt = sdbg_host->shost;
+ if ((hpnt->this_id >= 0) &&
+ (scsi_debug_num_tgts > hpnt->this_id))
+ hpnt->max_id = scsi_debug_num_tgts + 1;
+ else
+ hpnt->max_id = scsi_debug_num_tgts;
+ hpnt->max_lun = scsi_debug_max_luns;
+ }
+ spin_unlock(&sdebug_host_list_lock);
+}
diff --git a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h
new file mode 100644
index 000000000000..965dd5e760c1
--- /dev/null
+++ b/drivers/scsi/scsi_debug.h
@@ -0,0 +1,24 @@
+#ifndef _SCSI_DEBUG_H
+
+#include <linux/types.h>
+
+static int scsi_debug_slave_alloc(struct scsi_device *);
+static int scsi_debug_slave_configure(struct scsi_device *);
+static void scsi_debug_slave_destroy(struct scsi_device *);
+static int scsi_debug_queuecommand(struct scsi_cmnd *,
+ void (*done) (struct scsi_cmnd *));
+static int scsi_debug_ioctl(struct scsi_device *, int, void __user *);
+static int scsi_debug_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int[]);
+static int scsi_debug_abort(struct scsi_cmnd *);
+static int scsi_debug_bus_reset(struct scsi_cmnd *);
+static int scsi_debug_device_reset(struct scsi_cmnd *);
+static int scsi_debug_host_reset(struct scsi_cmnd *);
+static int scsi_debug_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+static const char * scsi_debug_info(struct Scsi_Host *);
+
+#define SCSI_DEBUG_CANQUEUE 255 /* needs to be >= 1 */
+
+#define SCSI_DEBUG_MAX_CMD_LEN 16
+
+#endif
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
new file mode 100644
index 000000000000..6121dc1bfada
--- /dev/null
+++ b/drivers/scsi/scsi_devinfo.c
@@ -0,0 +1,564 @@
+
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_devinfo.h>
+
+#include "scsi_priv.h"
+
+
+/*
+ * scsi_dev_info_list: structure to hold black/white listed devices.
+ */
+struct scsi_dev_info_list {
+ struct list_head dev_info_list;
+ char vendor[8];
+ char model[16];
+ unsigned flags;
+ unsigned compatible; /* for use with scsi_static_device_list entries */
+};
+
+
+static const char spaces[] = " "; /* 16 of them */
+static unsigned scsi_default_dev_flags;
+static LIST_HEAD(scsi_dev_info_list);
+static char scsi_dev_flags[256];
+
+/*
+ * scsi_static_device_list: deprecated list of devices that require
+ * settings that differ from the default, includes black-listed (broken)
+ * devices. The entries here are added to the tail of scsi_dev_info_list
+ * via scsi_dev_info_list_init.
+ *
+ * Do not add to this list, use the command line or proc interface to add
+ * to the scsi_dev_info_list. This table will eventually go away.
+ */
+static struct {
+ char *vendor;
+ char *model;
+ char *revision; /* revision known to be bad, unused */
+ unsigned flags;
+} scsi_static_device_list[] __initdata = {
+ /*
+ * The following devices are known not to tolerate a lun != 0 scan
+ * for one reason or another. Some will respond to all luns,
+ * others will lock up.
+ */
+ {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* locks up */
+ {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* locks up */
+ {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* locks up */
+ {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* locks up */
+ {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* responds to all lun */
+ {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */
+ {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* locks up */
+ {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* locks up */
+ {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* locks up */
+ {"MAXTOR", "MXT-1240S", "I1.2", BLIST_NOLUN}, /* locks up */
+ {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* locks up */
+ {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* locks up */
+ {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /* responds to all lun */
+ {"MICROTEK", "ScanMakerIII", "2.30", BLIST_NOLUN}, /* responds to all lun */
+ {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN},/* locks up */
+ {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* responds to all lun */
+ {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* locks up */
+ {"SUN", "SENA", NULL, BLIST_NOLUN}, /* responds to all luns */
+ /*
+ * The following causes a failed REQUEST SENSE on lun 1 for
+ * aha152x controller, which causes SCSI code to reset bus.
+ */
+ {"SANYO", "CRD-250S", "1.20", BLIST_NOLUN},
+ /*
+ * The following causes a failed REQUEST SENSE on lun 1 for
+ * aha152x controller, which causes SCSI code to reset bus.
+ */
+ {"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN},
+ {"SEAGATE", "ST296", "921", BLIST_NOLUN}, /* responds to all lun */
+ {"SEAGATE", "ST1581", "6538", BLIST_NOLUN}, /* responds to all lun */
+ {"SONY", "CD-ROM CDU-541", "4.3d", BLIST_NOLUN},
+ {"SONY", "CD-ROM CDU-55S", "1.0i", BLIST_NOLUN},
+ {"SONY", "CD-ROM CDU-561", "1.7x", BLIST_NOLUN},
+ {"SONY", "CD-ROM CDU-8012", NULL, BLIST_NOLUN},
+ {"SONY", "SDT-5000", "3.17", BLIST_SELECT_NO_ATN},
+ {"TANDBERG", "TDC 3600", "U07", BLIST_NOLUN}, /* locks up */
+ {"TEAC", "CD-R55S", "1.0H", BLIST_NOLUN}, /* locks up */
+ /*
+ * The following causes a failed REQUEST SENSE on lun 1 for
+ * seagate controller, which causes SCSI code to reset bus.
+ */
+ {"TEAC", "CD-ROM", "1.06", BLIST_NOLUN},
+ {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* responds to all lun */
+ /*
+ * The following causes a failed REQUEST SENSE on lun 1 for
+ * seagate controller, which causes SCSI code to reset bus.
+ */
+ {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
+ {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
+ {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
+ {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* locks up */
+ {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */
+ {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */
+ {"NEC", "D3856", "0009", BLIST_NOLUN},
+ {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* locks up */
+ {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* locks up */
+ {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* locks up */
+ {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */
+ {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
+ {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN},
+ {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */
+ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */
+ {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* locks up */
+ {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* locks up */
+
+ /*
+ * Other types of devices that have special flags.
+ * Note that all USB devices should have the BLIST_INQUIRY_36 flag.
+ */
+ {"3PARdata", "VV", NULL, BLIST_REPORTLUN2},
+ {"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN},
+ {"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN},
+ {"AFT PRO", "-IX CF", "0.0>", BLIST_FORCELUN},
+ {"BELKIN", "USB 2 HS-CF", "1.95", BLIST_FORCELUN | BLIST_INQUIRY_36},
+ {"CANON", "IPUBJD", NULL, BLIST_SPARSELUN},
+ {"CBOX3", "USB Storage-SMC", "300A", BLIST_FORCELUN | BLIST_INQUIRY_36},
+ {"CMD", "CRA-7280", NULL, BLIST_SPARSELUN}, /* CMD RAID Controller */
+ {"CNSI", "G7324", NULL, BLIST_SPARSELUN}, /* Chaparral G7324 RAID */
+ {"CNSi", "G8324", NULL, BLIST_SPARSELUN}, /* Chaparral G8324 RAID */
+ {"COMPAQ", "LOGICAL VOLUME", NULL, BLIST_FORCELUN},
+ {"COMPAQ", "CR3500", NULL, BLIST_FORCELUN},
+ {"COMPAQ", "MSA1000", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD},
+ {"COMPAQ", "MSA1000 VOLUME", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD},
+ {"COMPAQ", "HSV110", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD},
+ {"DDN", "SAN DataDirector", "*", BLIST_SPARSELUN},
+ {"DEC", "HSG80", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD},
+ {"DELL", "PV660F", NULL, BLIST_SPARSELUN},
+ {"DELL", "PV660F PSEUDO", NULL, BLIST_SPARSELUN},
+ {"DELL", "PSEUDO DEVICE .", NULL, BLIST_SPARSELUN}, /* Dell PV 530F */
+ {"DELL", "PV530F", NULL, BLIST_SPARSELUN},
+ {"DELL", "PERCRAID", NULL, BLIST_FORCELUN},
+ {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */
+ {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */
+ {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN},
+ {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN},
+ {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
+ {"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36},
+ {"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36},
+ {"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36},
+ {"HITACHI", "DF400", "*", BLIST_SPARSELUN},
+ {"HITACHI", "DF500", "*", BLIST_SPARSELUN},
+ {"HITACHI", "DF600", "*", BLIST_SPARSELUN},
+ {"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */
+ {"HP", "OPEN-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP XP Arrays */
+ {"HP", "NetRAID-4M", NULL, BLIST_FORCELUN},
+ {"HP", "HSV100", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD},
+ {"HP", "C1557A", NULL, BLIST_FORCELUN},
+ {"HP", "C3323-300", "4269", BLIST_NOTQ},
+ {"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN},
+ {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
+ {"IBM", "2105", NULL, BLIST_RETRY_HWERROR},
+ {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN},
+ {"IOMEGA", "Io20S *F", NULL, BLIST_KEY},
+ {"INSITE", "Floptical F*8I", NULL, BLIST_KEY},
+ {"INSITE", "I325VM", NULL, BLIST_KEY},
+ {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36},
+ {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN},
+ {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"MATSHITA", "DMC-LC5", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36},
+ {"MATSHITA", "DMC-LC40", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36},
+ {"Medion", "Flash XL MMC/SD", "2.6D", BLIST_FORCELUN},
+ {"MegaRAID", "LD", NULL, BLIST_FORCELUN},
+ {"MICROP", "4110", NULL, BLIST_NOTQ},
+ {"MYLEX", "DACARMRB", "*", BLIST_REPORTLUN2},
+ {"nCipher", "Fastness Crypto", NULL, BLIST_FORCELUN},
+ {"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"NAKAMICH", "MJ-5.16S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"NEC", "PD-1 ODX654P", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"NRC", "MBR-7", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"NRC", "MBR-7.4", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
+ {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN},
+ {"SanDisk", "ImageMate CF-SD1", NULL, BLIST_FORCELUN},
+ {"SEAGATE", "ST34555N", "0930", BLIST_NOTQ}, /* Chokes on tagged INQUIRY */
+ {"SEAGATE", "ST3390N", "9546", BLIST_NOTQ},
+ {"SGI", "RAID3", "*", BLIST_SPARSELUN},
+ {"SGI", "RAID5", "*", BLIST_SPARSELUN},
+ {"SGI", "TP9100", "*", BLIST_REPORTLUN2},
+ {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH},
+ {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36},
+ {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN},
+ {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */
+ {"ST650211", "CF", NULL, BLIST_RETRY_HWERROR},
+ {"SUN", "T300", "*", BLIST_SPARSELUN},
+ {"SUN", "T4", "*", BLIST_SPARSELUN},
+ {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN},
+ {"TOSHIBA", "CDROM", NULL, BLIST_ISROM},
+ {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM},
+ {"USB2.0", "SMARTMEDIA/XD", NULL, BLIST_FORCELUN | BLIST_INQUIRY_36},
+ {"WangDAT", "Model 2600", "01.7", BLIST_SELECT_NO_ATN},
+ {"WangDAT", "Model 3200", "02.2", BLIST_SELECT_NO_ATN},
+ {"WangDAT", "Model 1300", "02.4", BLIST_SELECT_NO_ATN},
+ {"WDC WD25", "00JB-00FUA0", NULL, BLIST_NOREPORTLUN},
+ {"XYRATEX", "RS", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
+ {"Zzyzx", "RocketStor 500S", NULL, BLIST_SPARSELUN},
+ {"Zzyzx", "RocketStor 2000", NULL, BLIST_SPARSELUN},
+ { NULL, NULL, NULL, 0 },
+};
+
+/*
+ * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into
+ * devinfo vendor and model strings.
+ */
+static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length,
+ char *from, int compatible)
+{
+ size_t from_length;
+
+ from_length = strlen(from);
+ strncpy(to, from, min(to_length, from_length));
+ if (from_length < to_length) {
+ if (compatible) {
+ /*
+ * NUL terminate the string if it is short.
+ */
+ to[from_length] = '\0';
+ } else {
+ /*
+ * space pad the string if it is short.
+ */
+ strncpy(&to[from_length], spaces,
+ to_length - from_length);
+ }
+ }
+ if (from_length > to_length)
+ printk(KERN_WARNING "%s: %s string '%s' is too long\n",
+ __FUNCTION__, name, from);
+}
+
+/**
+ * scsi_dev_info_list_add: add one dev_info list entry.
+ * @vendor: vendor string
+ * @model: model (product) string
+ * @strflags: integer string
+ * @flag: if strflags NULL, use this flag value
+ *
+ * Description:
+ * Create and add one dev_info entry for @vendor, @model, @strflags or
+ * @flag. If @compatible, add to the tail of the list, do not space
+ * pad, and set devinfo->compatible. The scsi_static_device_list entries
+ * are added with @compatible 1 and @clfags NULL.
+ *
+ * Returns: 0 OK, -error on failure.
+ **/
+static int scsi_dev_info_list_add(int compatible, char *vendor, char *model,
+ char *strflags, int flags)
+{
+ struct scsi_dev_info_list *devinfo;
+
+ devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL);
+ if (!devinfo) {
+ printk(KERN_ERR "%s: no memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ scsi_strcpy_devinfo("vendor", devinfo->vendor, sizeof(devinfo->vendor),
+ vendor, compatible);
+ scsi_strcpy_devinfo("model", devinfo->model, sizeof(devinfo->model),
+ model, compatible);
+
+ if (strflags)
+ devinfo->flags = simple_strtoul(strflags, NULL, 0);
+ else
+ devinfo->flags = flags;
+
+ devinfo->compatible = compatible;
+
+ if (compatible)
+ list_add_tail(&devinfo->dev_info_list, &scsi_dev_info_list);
+ else
+ list_add(&devinfo->dev_info_list, &scsi_dev_info_list);
+
+ return 0;
+}
+
+/**
+ * scsi_dev_info_list_add_str: parse dev_list and add to the
+ * scsi_dev_info_list.
+ * @dev_list: string of device flags to add
+ *
+ * Description:
+ * Parse dev_list, and add entries to the scsi_dev_info_list.
+ * dev_list is of the form "vendor:product:flag,vendor:product:flag".
+ * dev_list is modified via strsep. Can be called for command line
+ * addition, for proc or mabye a sysfs interface.
+ *
+ * Returns: 0 if OK, -error on failure.
+ **/
+static int scsi_dev_info_list_add_str(char *dev_list)
+{
+ char *vendor, *model, *strflags, *next;
+ char *next_check;
+ int res = 0;
+
+ next = dev_list;
+ if (next && next[0] == '"') {
+ /*
+ * Ignore both the leading and trailing quote.
+ */
+ next++;
+ next_check = ",\"";
+ } else {
+ next_check = ",";
+ }
+
+ /*
+ * For the leading and trailing '"' case, the for loop comes
+ * through the last time with vendor[0] == '\0'.
+ */
+ for (vendor = strsep(&next, ":"); vendor && (vendor[0] != '\0')
+ && (res == 0); vendor = strsep(&next, ":")) {
+ strflags = NULL;
+ model = strsep(&next, ":");
+ if (model)
+ strflags = strsep(&next, next_check);
+ if (!model || !strflags) {
+ printk(KERN_ERR "%s: bad dev info string '%s' '%s'"
+ " '%s'\n", __FUNCTION__, vendor, model,
+ strflags);
+ res = -EINVAL;
+ } else
+ res = scsi_dev_info_list_add(0 /* compatible */, vendor,
+ model, strflags, 0);
+ }
+ return res;
+}
+
+/**
+ * get_device_flags - get device specific flags from the dynamic device
+ * list. Called during scan time.
+ * @vendor: vendor name
+ * @model: model name
+ *
+ * Description:
+ * Search the scsi_dev_info_list for an entry matching @vendor and
+ * @model, if found, return the matching flags value, else return
+ * the host or global default settings.
+ **/
+int scsi_get_device_flags(struct scsi_device *sdev, unsigned char *vendor,
+ unsigned char *model)
+{
+ struct scsi_dev_info_list *devinfo;
+ unsigned int bflags;
+
+ bflags = sdev->sdev_bflags;
+ if (!bflags)
+ bflags = scsi_default_dev_flags;
+
+ list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) {
+ if (devinfo->compatible) {
+ /*
+ * Behave like the older version of get_device_flags.
+ */
+ size_t max;
+ /*
+ * XXX why skip leading spaces? If an odd INQUIRY
+ * value, that should have been part of the
+ * scsi_static_device_list[] entry, such as " FOO"
+ * rather than "FOO". Since this code is already
+ * here, and we don't know what device it is
+ * trying to work with, leave it as-is.
+ */
+ max = 8; /* max length of vendor */
+ while ((max > 0) && *vendor == ' ') {
+ max--;
+ vendor++;
+ }
+ /*
+ * XXX removing the following strlen() would be
+ * good, using it means that for a an entry not in
+ * the list, we scan every byte of every vendor
+ * listed in scsi_static_device_list[], and never match
+ * a single one (and still have to compare at
+ * least the first byte of each vendor).
+ */
+ if (memcmp(devinfo->vendor, vendor,
+ min(max, strlen(devinfo->vendor))))
+ continue;
+ /*
+ * Skip spaces again.
+ */
+ max = 16; /* max length of model */
+ while ((max > 0) && *model == ' ') {
+ max--;
+ model++;
+ }
+ if (memcmp(devinfo->model, model,
+ min(max, strlen(devinfo->model))))
+ continue;
+ return devinfo->flags;
+ } else {
+ if (!memcmp(devinfo->vendor, vendor,
+ sizeof(devinfo->vendor)) &&
+ !memcmp(devinfo->model, model,
+ sizeof(devinfo->model)))
+ return devinfo->flags;
+ }
+ }
+ return bflags;
+}
+
+#ifdef CONFIG_SCSI_PROC_FS
+/*
+ * proc_scsi_dev_info_read: dump the scsi_dev_info_list via
+ * /proc/scsi/device_info
+ */
+static int proc_scsi_devinfo_read(char *buffer, char **start,
+ off_t offset, int length)
+{
+ struct scsi_dev_info_list *devinfo;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) {
+ size = sprintf(buffer + len, "'%.8s' '%.16s' 0x%x\n",
+ devinfo->vendor, devinfo->model, devinfo->flags);
+ len += size;
+ pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+stop_output:
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+ return (len);
+}
+
+/*
+ * proc_scsi_dev_info_write: allow additions to the scsi_dev_info_list via
+ * /proc.
+ *
+ * Use: echo "vendor:model:flag" > /proc/scsi/device_info
+ *
+ * To add a black/white list entry for vendor and model with an integer
+ * value of flag to the scsi device info list.
+ */
+static int proc_scsi_devinfo_write(struct file *file, const char __user *buf,
+ unsigned long length, void *data)
+{
+ char *buffer;
+ int err = length;
+
+ if (!buf || length>PAGE_SIZE)
+ return -EINVAL;
+ if (!(buffer = (char *) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ if (copy_from_user(buffer, buf, length)) {
+ err =-EFAULT;
+ goto out;
+ }
+
+ if (length < PAGE_SIZE)
+ buffer[length] = '\0';
+ else if (buffer[PAGE_SIZE-1]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ scsi_dev_info_list_add_str(buffer);
+
+out:
+ free_page((unsigned long)buffer);
+ return err;
+}
+#endif /* CONFIG_SCSI_PROC_FS */
+
+module_param_string(dev_flags, scsi_dev_flags, sizeof(scsi_dev_flags), 0);
+MODULE_PARM_DESC(dev_flags,
+ "Given scsi_dev_flags=vendor:model:flags[,v:m:f] add black/white"
+ " list entries for vendor and model with an integer value of flags"
+ " to the scsi device info list");
+
+module_param_named(default_dev_flags, scsi_default_dev_flags, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(default_dev_flags,
+ "scsi default device flag integer value");
+
+/**
+ * scsi_dev_info_list_delete: called from scsi.c:exit_scsi to remove
+ * the scsi_dev_info_list.
+ **/
+void scsi_exit_devinfo(void)
+{
+ struct list_head *lh, *lh_next;
+ struct scsi_dev_info_list *devinfo;
+
+#ifdef CONFIG_SCSI_PROC_FS
+ remove_proc_entry("scsi/device_info", NULL);
+#endif
+
+ list_for_each_safe(lh, lh_next, &scsi_dev_info_list) {
+ devinfo = list_entry(lh, struct scsi_dev_info_list,
+ dev_info_list);
+ kfree(devinfo);
+ }
+}
+
+/**
+ * scsi_dev_list_init: set up the dynamic device list.
+ * @dev_list: string of device flags to add
+ *
+ * Description:
+ * Add command line @dev_list entries, then add
+ * scsi_static_device_list entries to the scsi device info list.
+ **/
+int __init scsi_init_devinfo(void)
+{
+#ifdef CONFIG_SCSI_PROC_FS
+ struct proc_dir_entry *p;
+#endif
+ int error, i;
+
+ error = scsi_dev_info_list_add_str(scsi_dev_flags);
+ if (error)
+ return error;
+
+ for (i = 0; scsi_static_device_list[i].vendor; i++) {
+ error = scsi_dev_info_list_add(1 /* compatibile */,
+ scsi_static_device_list[i].vendor,
+ scsi_static_device_list[i].model,
+ NULL,
+ scsi_static_device_list[i].flags);
+ if (error)
+ goto out;
+ }
+
+#ifdef CONFIG_SCSI_PROC_FS
+ p = create_proc_entry("scsi/device_info", 0, NULL);
+ if (!p) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ p->owner = THIS_MODULE;
+ p->get_info = proc_scsi_devinfo_read;
+ p->write_proc = proc_scsi_devinfo_write;
+#endif /* CONFIG_SCSI_PROC_FS */
+
+ out:
+ if (error)
+ scsi_exit_devinfo();
+ return error;
+}
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
new file mode 100644
index 000000000000..9bc597bd13ba
--- /dev/null
+++ b/drivers/scsi/scsi_error.c
@@ -0,0 +1,2050 @@
+/*
+ * scsi_error.c Copyright (C) 1997 Eric Youngdale
+ *
+ * SCSI error/timeout handling
+ * Initial versions: Eric Youngdale. Based upon conversations with
+ * Leonard Zubkoff and David Miller at Linux Expo,
+ * ideas originating from all over the place.
+ *
+ * Restructured scsi_unjam_host and associated functions.
+ * September 04, 2002 Mike Anderson (andmike@us.ibm.com)
+ *
+ * Forward port of Russell King's (rmk@arm.linux.org.uk) changes and
+ * minor cleanups.
+ * September 30, 2002 Mike Anderson (andmike@us.ibm.com)
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+#define SENSE_TIMEOUT (10*HZ)
+#define START_UNIT_TIMEOUT (30*HZ)
+
+/*
+ * These should *probably* be handled by the host itself.
+ * Since it is allowed to sleep, it probably should.
+ */
+#define BUS_RESET_SETTLE_TIME (10)
+#define HOST_RESET_SETTLE_TIME (10)
+
+/* called with shost->host_lock held */
+void scsi_eh_wakeup(struct Scsi_Host *shost)
+{
+ if (shost->host_busy == shost->host_failed) {
+ up(shost->eh_wait);
+ SCSI_LOG_ERROR_RECOVERY(5,
+ printk("Waking error handler thread\n"));
+ }
+}
+
+/**
+ * scsi_eh_scmd_add - add scsi cmd to error handling.
+ * @scmd: scmd to run eh on.
+ * @eh_flag: optional SCSI_EH flag.
+ *
+ * Return value:
+ * 0 on failure.
+ **/
+int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
+{
+ struct Scsi_Host *shost = scmd->device->host;
+ unsigned long flags;
+
+ if (shost->eh_wait == NULL)
+ return 0;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ scsi_eh_eflags_set(scmd, eh_flag);
+ /*
+ * FIXME: Can we stop setting owner and state.
+ */
+ scmd->owner = SCSI_OWNER_ERROR_HANDLER;
+ scmd->state = SCSI_STATE_FAILED;
+ /*
+ * Set the serial_number_at_timeout to the current
+ * serial_number
+ */
+ scmd->serial_number_at_timeout = scmd->serial_number;
+ list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
+ set_bit(SHOST_RECOVERY, &shost->shost_state);
+ shost->host_failed++;
+ scsi_eh_wakeup(shost);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return 1;
+}
+
+/**
+ * scsi_add_timer - Start timeout timer for a single scsi command.
+ * @scmd: scsi command that is about to start running.
+ * @timeout: amount of time to allow this command to run.
+ * @complete: timeout function to call if timer isn't canceled.
+ *
+ * Notes:
+ * This should be turned into an inline function. Each scsi command
+ * has its own timer, and as it is added to the queue, we set up the
+ * timer. When the command completes, we cancel the timer.
+ **/
+void scsi_add_timer(struct scsi_cmnd *scmd, int timeout,
+ void (*complete)(struct scsi_cmnd *))
+{
+
+ /*
+ * If the clock was already running for this command, then
+ * first delete the timer. The timer handling code gets rather
+ * confused if we don't do this.
+ */
+ if (scmd->eh_timeout.function)
+ del_timer(&scmd->eh_timeout);
+
+ scmd->eh_timeout.data = (unsigned long)scmd;
+ scmd->eh_timeout.expires = jiffies + timeout;
+ scmd->eh_timeout.function = (void (*)(unsigned long)) complete;
+
+ SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:"
+ " %d, (%p)\n", __FUNCTION__,
+ scmd, timeout, complete));
+
+ add_timer(&scmd->eh_timeout);
+}
+EXPORT_SYMBOL(scsi_add_timer);
+
+/**
+ * scsi_delete_timer - Delete/cancel timer for a given function.
+ * @scmd: Cmd that we are canceling timer for
+ *
+ * Notes:
+ * This should be turned into an inline function.
+ *
+ * Return value:
+ * 1 if we were able to detach the timer. 0 if we blew it, and the
+ * timer function has already started to run.
+ **/
+int scsi_delete_timer(struct scsi_cmnd *scmd)
+{
+ int rtn;
+
+ rtn = del_timer(&scmd->eh_timeout);
+
+ SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p,"
+ " rtn: %d\n", __FUNCTION__,
+ scmd, rtn));
+
+ scmd->eh_timeout.data = (unsigned long)NULL;
+ scmd->eh_timeout.function = NULL;
+
+ return rtn;
+}
+EXPORT_SYMBOL(scsi_delete_timer);
+
+/**
+ * scsi_times_out - Timeout function for normal scsi commands.
+ * @scmd: Cmd that is timing out.
+ *
+ * Notes:
+ * We do not need to lock this. There is the potential for a race
+ * only in that the normal completion handling might run, but if the
+ * normal completion function determines that the timer has already
+ * fired, then it mustn't do anything.
+ **/
+void scsi_times_out(struct scsi_cmnd *scmd)
+{
+ scsi_log_completion(scmd, TIMEOUT_ERROR);
+
+ if (scmd->device->host->hostt->eh_timed_out)
+ switch (scmd->device->host->hostt->eh_timed_out(scmd)) {
+ case EH_HANDLED:
+ __scsi_done(scmd);
+ return;
+ case EH_RESET_TIMER:
+ /* This allows a single retry even of a command
+ * with allowed == 0 */
+ if (scmd->retries++ > scmd->allowed)
+ break;
+ scsi_add_timer(scmd, scmd->timeout_per_command,
+ scsi_times_out);
+ return;
+ case EH_NOT_HANDLED:
+ break;
+ }
+
+ if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
+ panic("Error handler thread not present at %p %p %s %d",
+ scmd, scmd->device->host, __FILE__, __LINE__);
+ }
+}
+
+/**
+ * scsi_block_when_processing_errors - Prevent cmds from being queued.
+ * @sdev: Device on which we are performing recovery.
+ *
+ * Description:
+ * We block until the host is out of error recovery, and then check to
+ * see whether the host or the device is offline.
+ *
+ * Return value:
+ * 0 when dev was taken offline by error recovery. 1 OK to proceed.
+ **/
+int scsi_block_when_processing_errors(struct scsi_device *sdev)
+{
+ int online;
+
+ wait_event(sdev->host->host_wait, (!test_bit(SHOST_RECOVERY, &sdev->host->shost_state)));
+
+ online = scsi_device_online(sdev);
+
+ SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__,
+ online));
+
+ return online;
+}
+EXPORT_SYMBOL(scsi_block_when_processing_errors);
+
+#ifdef CONFIG_SCSI_LOGGING
+/**
+ * scsi_eh_prt_fail_stats - Log info on failures.
+ * @shost: scsi host being recovered.
+ * @work_q: Queue of scsi cmds to process.
+ **/
+static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
+ struct list_head *work_q)
+{
+ struct scsi_cmnd *scmd;
+ struct scsi_device *sdev;
+ int total_failures = 0;
+ int cmd_failed = 0;
+ int cmd_cancel = 0;
+ int devices_failed = 0;
+
+ shost_for_each_device(sdev, shost) {
+ list_for_each_entry(scmd, work_q, eh_entry) {
+ if (scmd->device == sdev) {
+ ++total_failures;
+ if (scsi_eh_eflags_chk(scmd,
+ SCSI_EH_CANCEL_CMD))
+ ++cmd_cancel;
+ else
+ ++cmd_failed;
+ }
+ }
+
+ if (cmd_cancel || cmd_failed) {
+ SCSI_LOG_ERROR_RECOVERY(3,
+ printk("%s: %d:%d:%d:%d cmds failed: %d,"
+ " cancel: %d\n",
+ __FUNCTION__, shost->host_no,
+ sdev->channel, sdev->id, sdev->lun,
+ cmd_failed, cmd_cancel));
+ cmd_cancel = 0;
+ cmd_failed = 0;
+ ++devices_failed;
+ }
+ }
+
+ SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d commands on %d"
+ " devices require eh work\n",
+ total_failures, devices_failed));
+}
+#endif
+
+/**
+ * scsi_check_sense - Examine scsi cmd sense
+ * @scmd: Cmd to have sense checked.
+ *
+ * Return value:
+ * SUCCESS or FAILED or NEEDS_RETRY
+ *
+ * Notes:
+ * When a deferred error is detected the current command has
+ * not been executed and needs retrying.
+ **/
+static int scsi_check_sense(struct scsi_cmnd *scmd)
+{
+ struct scsi_sense_hdr sshdr;
+
+ if (! scsi_command_normalize_sense(scmd, &sshdr))
+ return FAILED; /* no valid sense data */
+
+ if (scsi_sense_is_deferred(&sshdr))
+ return NEEDS_RETRY;
+
+ /*
+ * Previous logic looked for FILEMARK, EOM or ILI which are
+ * mainly associated with tapes and returned SUCCESS.
+ */
+ if (sshdr.response_code == 0x70) {
+ /* fixed format */
+ if (scmd->sense_buffer[2] & 0xe0)
+ return SUCCESS;
+ } else {
+ /*
+ * descriptor format: look for "stream commands sense data
+ * descriptor" (see SSC-3). Assume single sense data
+ * descriptor. Ignore ILI from SBC-2 READ LONG and WRITE LONG.
+ */
+ if ((sshdr.additional_length > 3) &&
+ (scmd->sense_buffer[8] == 0x4) &&
+ (scmd->sense_buffer[11] & 0xe0))
+ return SUCCESS;
+ }
+
+ switch (sshdr.sense_key) {
+ case NO_SENSE:
+ return SUCCESS;
+ case RECOVERED_ERROR:
+ return /* soft_error */ SUCCESS;
+
+ case ABORTED_COMMAND:
+ return NEEDS_RETRY;
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ /*
+ * if we are expecting a cc/ua because of a bus reset that we
+ * performed, treat this just as a retry. otherwise this is
+ * information that we should pass up to the upper-level driver
+ * so that we can deal with it there.
+ */
+ if (scmd->device->expecting_cc_ua) {
+ scmd->device->expecting_cc_ua = 0;
+ return NEEDS_RETRY;
+ }
+ /*
+ * if the device is in the process of becoming ready, we
+ * should retry.
+ */
+ if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
+ return NEEDS_RETRY;
+ /*
+ * if the device is not started, we need to wake
+ * the error handler to start the motor
+ */
+ if (scmd->device->allow_restart &&
+ (sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
+ return FAILED;
+ return SUCCESS;
+
+ /* these three are not supported */
+ case COPY_ABORTED:
+ case VOLUME_OVERFLOW:
+ case MISCOMPARE:
+ return SUCCESS;
+
+ case MEDIUM_ERROR:
+ return NEEDS_RETRY;
+
+ case HARDWARE_ERROR:
+ if (scmd->device->retry_hwerror)
+ return NEEDS_RETRY;
+ else
+ return SUCCESS;
+
+ case ILLEGAL_REQUEST:
+ case BLANK_CHECK:
+ case DATA_PROTECT:
+ default:
+ return SUCCESS;
+ }
+}
+
+/**
+ * scsi_eh_completed_normally - Disposition a eh cmd on return from LLD.
+ * @scmd: SCSI cmd to examine.
+ *
+ * Notes:
+ * This is *only* called when we are examining the status of commands
+ * queued during error recovery. the main difference here is that we
+ * don't allow for the possibility of retries here, and we are a lot
+ * more restrictive about what we consider acceptable.
+ **/
+static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
+{
+ /*
+ * first check the host byte, to see if there is anything in there
+ * that would indicate what we need to do.
+ */
+ if (host_byte(scmd->result) == DID_RESET) {
+ /*
+ * rats. we are already in the error handler, so we now
+ * get to try and figure out what to do next. if the sense
+ * is valid, we have a pretty good idea of what to do.
+ * if not, we mark it as FAILED.
+ */
+ return scsi_check_sense(scmd);
+ }
+ if (host_byte(scmd->result) != DID_OK)
+ return FAILED;
+
+ /*
+ * next, check the message byte.
+ */
+ if (msg_byte(scmd->result) != COMMAND_COMPLETE)
+ return FAILED;
+
+ /*
+ * now, check the status byte to see if this indicates
+ * anything special.
+ */
+ switch (status_byte(scmd->result)) {
+ case GOOD:
+ case COMMAND_TERMINATED:
+ return SUCCESS;
+ case CHECK_CONDITION:
+ return scsi_check_sense(scmd);
+ case CONDITION_GOOD:
+ case INTERMEDIATE_GOOD:
+ case INTERMEDIATE_C_GOOD:
+ /*
+ * who knows? FIXME(eric)
+ */
+ return SUCCESS;
+ case BUSY:
+ case QUEUE_FULL:
+ case RESERVATION_CONFLICT:
+ default:
+ return FAILED;
+ }
+ return FAILED;
+}
+
+/**
+ * scsi_eh_times_out - timeout function for error handling.
+ * @scmd: Cmd that is timing out.
+ *
+ * Notes:
+ * During error handling, the kernel thread will be sleeping waiting
+ * for some action to complete on the device. our only job is to
+ * record that it timed out, and to wake up the thread.
+ **/
+static void scsi_eh_times_out(struct scsi_cmnd *scmd)
+{
+ scsi_eh_eflags_set(scmd, SCSI_EH_REC_TIMEOUT);
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__,
+ scmd));
+
+ if (scmd->device->host->eh_action)
+ up(scmd->device->host->eh_action);
+}
+
+/**
+ * scsi_eh_done - Completion function for error handling.
+ * @scmd: Cmd that is done.
+ **/
+static void scsi_eh_done(struct scsi_cmnd *scmd)
+{
+ /*
+ * if the timeout handler is already running, then just set the
+ * flag which says we finished late, and return. we have no
+ * way of stopping the timeout handler from running, so we must
+ * always defer to it.
+ */
+ if (del_timer(&scmd->eh_timeout)) {
+ scmd->request->rq_status = RQ_SCSI_DONE;
+ scmd->owner = SCSI_OWNER_ERROR_HANDLER;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n",
+ __FUNCTION__, scmd, scmd->result));
+
+ if (scmd->device->host->eh_action)
+ up(scmd->device->host->eh_action);
+ }
+}
+
+/**
+ * scsi_send_eh_cmnd - send a cmd to a device as part of error recovery.
+ * @scmd: SCSI Cmd to send.
+ * @timeout: Timeout for cmd.
+ *
+ * Notes:
+ * The initialization of the structures is quite a bit different in
+ * this case, and furthermore, there is a different completion handler
+ * vs scsi_dispatch_cmd.
+ * Return value:
+ * SUCCESS or FAILED or NEEDS_RETRY
+ **/
+static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
+{
+ struct Scsi_Host *host = scmd->device->host;
+ DECLARE_MUTEX_LOCKED(sem);
+ unsigned long flags;
+ int rtn = SUCCESS;
+
+ /*
+ * we will use a queued command if possible, otherwise we will
+ * emulate the queuing and calling of completion function ourselves.
+ */
+ scmd->owner = SCSI_OWNER_LOWLEVEL;
+
+ if (scmd->device->scsi_level <= SCSI_2)
+ scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) |
+ (scmd->device->lun << 5 & 0xe0);
+
+ scsi_add_timer(scmd, timeout, scsi_eh_times_out);
+
+ /*
+ * set up the semaphore so we wait for the command to complete.
+ */
+ scmd->device->host->eh_action = &sem;
+ scmd->request->rq_status = RQ_SCSI_BUSY;
+
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ scsi_log_send(scmd);
+ host->hostt->queuecommand(scmd, scsi_eh_done);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+
+ down(&sem);
+ scsi_log_completion(scmd, SUCCESS);
+
+ scmd->device->host->eh_action = NULL;
+
+ /*
+ * see if timeout. if so, tell the host to forget about it.
+ * in other words, we don't want a callback any more.
+ */
+ if (scsi_eh_eflags_chk(scmd, SCSI_EH_REC_TIMEOUT)) {
+ scsi_eh_eflags_clr(scmd, SCSI_EH_REC_TIMEOUT);
+ scmd->owner = SCSI_OWNER_LOWLEVEL;
+
+ /*
+ * as far as the low level driver is
+ * concerned, this command is still active, so
+ * we must give the low level driver a chance
+ * to abort it. (db)
+ *
+ * FIXME(eric) - we are not tracking whether we could
+ * abort a timed out command or not. not sure how
+ * we should treat them differently anyways.
+ */
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ if (scmd->device->host->hostt->eh_abort_handler)
+ scmd->device->host->hostt->eh_abort_handler(scmd);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+
+ scmd->request->rq_status = RQ_SCSI_DONE;
+ scmd->owner = SCSI_OWNER_ERROR_HANDLER;
+
+ rtn = FAILED;
+ }
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd: %p, rtn:%x\n",
+ __FUNCTION__, scmd, rtn));
+
+ /*
+ * now examine the actual status codes to see whether the command
+ * actually did complete normally.
+ */
+ if (rtn == SUCCESS) {
+ rtn = scsi_eh_completed_normally(scmd);
+ SCSI_LOG_ERROR_RECOVERY(3,
+ printk("%s: scsi_eh_completed_normally %x\n",
+ __FUNCTION__, rtn));
+ switch (rtn) {
+ case SUCCESS:
+ case NEEDS_RETRY:
+ case FAILED:
+ break;
+ default:
+ rtn = FAILED;
+ break;
+ }
+ }
+
+ return rtn;
+}
+
+/**
+ * scsi_request_sense - Request sense data from a particular target.
+ * @scmd: SCSI cmd for request sense.
+ *
+ * Notes:
+ * Some hosts automatically obtain this information, others require
+ * that we obtain it on our own. This function will *not* return until
+ * the command either times out, or it completes.
+ **/
+static int scsi_request_sense(struct scsi_cmnd *scmd)
+{
+ static unsigned char generic_sense[6] =
+ {REQUEST_SENSE, 0, 0, 0, 252, 0};
+ unsigned char *scsi_result;
+ int saved_result;
+ int rtn;
+
+ memcpy(scmd->cmnd, generic_sense, sizeof(generic_sense));
+
+ scsi_result = kmalloc(252, GFP_ATOMIC | (scmd->device->host->hostt->unchecked_isa_dma) ? __GFP_DMA : 0);
+
+
+ if (unlikely(!scsi_result)) {
+ printk(KERN_ERR "%s: cannot allocate scsi_result.\n",
+ __FUNCTION__);
+ return FAILED;
+ }
+
+ /*
+ * zero the sense buffer. some host adapters automatically always
+ * request sense, so it is not a good idea that
+ * scmd->request_buffer and scmd->sense_buffer point to the same
+ * address (db). 0 is not a valid sense code.
+ */
+ memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
+ memset(scsi_result, 0, 252);
+
+ saved_result = scmd->result;
+ scmd->request_buffer = scsi_result;
+ scmd->request_bufflen = 252;
+ scmd->use_sg = 0;
+ scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+ scmd->sc_data_direction = DMA_FROM_DEVICE;
+ scmd->underflow = 0;
+
+ rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT);
+
+ /* last chance to have valid sense data */
+ if(!SCSI_SENSE_VALID(scmd)) {
+ memcpy(scmd->sense_buffer, scmd->request_buffer,
+ sizeof(scmd->sense_buffer));
+ }
+
+ kfree(scsi_result);
+
+ /*
+ * when we eventually call scsi_finish, we really wish to complete
+ * the original request, so let's restore the original data. (db)
+ */
+ scsi_setup_cmd_retry(scmd);
+ scmd->result = saved_result;
+ return rtn;
+}
+
+/**
+ * scsi_eh_finish_cmd - Handle a cmd that eh is finished with.
+ * @scmd: Original SCSI cmd that eh has finished.
+ * @done_q: Queue for processed commands.
+ *
+ * Notes:
+ * We don't want to use the normal command completion while we are are
+ * still handling errors - it may cause other commands to be queued,
+ * and that would disturb what we are doing. thus we really want to
+ * keep a list of pending commands for final completion, and once we
+ * are ready to leave error handling we handle completion for real.
+ **/
+static void scsi_eh_finish_cmd(struct scsi_cmnd *scmd,
+ struct list_head *done_q)
+{
+ scmd->device->host->host_failed--;
+ scmd->state = SCSI_STATE_BHQUEUE;
+
+ scsi_eh_eflags_clr_all(scmd);
+
+ /*
+ * set this back so that the upper level can correctly free up
+ * things.
+ */
+ scsi_setup_cmd_retry(scmd);
+ list_move_tail(&scmd->eh_entry, done_q);
+}
+
+/**
+ * scsi_eh_get_sense - Get device sense data.
+ * @work_q: Queue of commands to process.
+ * @done_q: Queue of proccessed commands..
+ *
+ * Description:
+ * See if we need to request sense information. if so, then get it
+ * now, so we have a better idea of what to do.
+ *
+ * Notes:
+ * This has the unfortunate side effect that if a shost adapter does
+ * not automatically request sense information, that we end up shutting
+ * it down before we request it.
+ *
+ * All drivers should request sense information internally these days,
+ * so for now all I have to say is tough noogies if you end up in here.
+ *
+ * XXX: Long term this code should go away, but that needs an audit of
+ * all LLDDs first.
+ **/
+static int scsi_eh_get_sense(struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+ int rtn;
+
+ list_for_each_safe(lh, lh_sf, work_q) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ if (scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD) ||
+ SCSI_SENSE_VALID(scmd))
+ continue;
+
+ SCSI_LOG_ERROR_RECOVERY(2, printk("%s: requesting sense"
+ " for id: %d\n",
+ current->comm,
+ scmd->device->id));
+ rtn = scsi_request_sense(scmd);
+ if (rtn != SUCCESS)
+ continue;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("sense requested for %p"
+ " result %x\n", scmd,
+ scmd->result));
+ SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense("bh", scmd));
+
+ rtn = scsi_decide_disposition(scmd);
+
+ /*
+ * if the result was normal, then just pass it along to the
+ * upper level.
+ */
+ if (rtn == SUCCESS)
+ /* we don't want this command reissued, just
+ * finished with the sense data, so set
+ * retries to the max allowed to ensure it
+ * won't get reissued */
+ scmd->retries = scmd->allowed;
+ else if (rtn != NEEDS_RETRY)
+ continue;
+
+ scsi_eh_finish_cmd(scmd, done_q);
+ }
+
+ return list_empty(work_q);
+}
+
+/**
+ * scsi_try_to_abort_cmd - Ask host to abort a running command.
+ * @scmd: SCSI cmd to abort from Lower Level.
+ *
+ * Notes:
+ * This function will not return until the user's completion function
+ * has been called. there is no timeout on this operation. if the
+ * author of the low-level driver wishes this operation to be timed,
+ * they can provide this facility themselves. helper functions in
+ * scsi_error.c can be supplied to make this easier to do.
+ **/
+static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn = FAILED;
+
+ if (!scmd->device->host->hostt->eh_abort_handler)
+ return rtn;
+
+ /*
+ * scsi_done was called just after the command timed out and before
+ * we had a chance to process it. (db)
+ */
+ if (scmd->serial_number == 0)
+ return SUCCESS;
+
+ scmd->owner = SCSI_OWNER_LOWLEVEL;
+
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ rtn = scmd->device->host->hostt->eh_abort_handler(scmd);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+
+ return rtn;
+}
+
+/**
+ * scsi_eh_tur - Send TUR to device.
+ * @scmd: Scsi cmd to send TUR
+ *
+ * Return value:
+ * 0 - Device is ready. 1 - Device NOT ready.
+ **/
+static int scsi_eh_tur(struct scsi_cmnd *scmd)
+{
+ static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+ int retry_cnt = 1, rtn;
+
+retry_tur:
+ memcpy(scmd->cmnd, tur_command, sizeof(tur_command));
+
+ /*
+ * zero the sense buffer. the scsi spec mandates that any
+ * untransferred sense data should be interpreted as being zero.
+ */
+ memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
+
+ scmd->request_buffer = NULL;
+ scmd->request_bufflen = 0;
+ scmd->use_sg = 0;
+ scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+ scmd->underflow = 0;
+ scmd->sc_data_direction = DMA_NONE;
+
+ rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT);
+
+ /*
+ * when we eventually call scsi_finish, we really wish to complete
+ * the original request, so let's restore the original data. (db)
+ */
+ scsi_setup_cmd_retry(scmd);
+
+ /*
+ * hey, we are done. let's look to see what happened.
+ */
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n",
+ __FUNCTION__, scmd, rtn));
+ if (rtn == SUCCESS)
+ return 0;
+ else if (rtn == NEEDS_RETRY)
+ if (retry_cnt--)
+ goto retry_tur;
+ return 1;
+}
+
+/**
+ * scsi_eh_abort_cmds - abort canceled commands.
+ * @shost: scsi host being recovered.
+ * @eh_done_q: list_head for processed commands.
+ *
+ * Decription:
+ * Try and see whether or not it makes sense to try and abort the
+ * running command. this only works out to be the case if we have one
+ * command that has timed out. if the command simply failed, it makes
+ * no sense to try and abort the command, since as far as the shost
+ * adapter is concerned, it isn't running.
+ **/
+static int scsi_eh_abort_cmds(struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+ int rtn;
+
+ list_for_each_safe(lh, lh_sf, work_q) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ if (!scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD))
+ continue;
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting cmd:"
+ "0x%p\n", current->comm,
+ scmd));
+ rtn = scsi_try_to_abort_cmd(scmd);
+ if (rtn == SUCCESS) {
+ scsi_eh_eflags_clr(scmd, SCSI_EH_CANCEL_CMD);
+ if (!scsi_device_online(scmd->device) ||
+ !scsi_eh_tur(scmd)) {
+ scsi_eh_finish_cmd(scmd, done_q);
+ }
+
+ } else
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting"
+ " cmd failed:"
+ "0x%p\n",
+ current->comm,
+ scmd));
+ }
+
+ return list_empty(work_q);
+}
+
+/**
+ * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
+ * @scmd: SCSI cmd used to send BDR
+ *
+ * Notes:
+ * There is no timeout for this operation. if this operation is
+ * unreliable for a given host, then the host itself needs to put a
+ * timer on it, and set the host back to a consistent state prior to
+ * returning.
+ **/
+static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn = FAILED;
+
+ if (!scmd->device->host->hostt->eh_device_reset_handler)
+ return rtn;
+
+ scmd->owner = SCSI_OWNER_LOWLEVEL;
+
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+
+ if (rtn == SUCCESS) {
+ scmd->device->was_reset = 1;
+ scmd->device->expecting_cc_ua = 1;
+ }
+
+ return rtn;
+}
+
+/**
+ * scsi_eh_try_stu - Send START_UNIT to device.
+ * @scmd: Scsi cmd to send START_UNIT
+ *
+ * Return value:
+ * 0 - Device is ready. 1 - Device NOT ready.
+ **/
+static int scsi_eh_try_stu(struct scsi_cmnd *scmd)
+{
+ static unsigned char stu_command[6] = {START_STOP, 0, 0, 0, 1, 0};
+ int rtn;
+
+ if (!scmd->device->allow_restart)
+ return 1;
+
+ memcpy(scmd->cmnd, stu_command, sizeof(stu_command));
+
+ /*
+ * zero the sense buffer. the scsi spec mandates that any
+ * untransferred sense data should be interpreted as being zero.
+ */
+ memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
+
+ scmd->request_buffer = NULL;
+ scmd->request_bufflen = 0;
+ scmd->use_sg = 0;
+ scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+ scmd->underflow = 0;
+ scmd->sc_data_direction = DMA_NONE;
+
+ rtn = scsi_send_eh_cmnd(scmd, START_UNIT_TIMEOUT);
+
+ /*
+ * when we eventually call scsi_finish, we really wish to complete
+ * the original request, so let's restore the original data. (db)
+ */
+ scsi_setup_cmd_retry(scmd);
+
+ /*
+ * hey, we are done. let's look to see what happened.
+ */
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n",
+ __FUNCTION__, scmd, rtn));
+ if (rtn == SUCCESS)
+ return 0;
+ return 1;
+}
+
+ /**
+ * scsi_eh_stu - send START_UNIT if needed
+ * @shost: scsi host being recovered.
+ * @eh_done_q: list_head for processed commands.
+ *
+ * Notes:
+ * If commands are failing due to not ready, initializing command required,
+ * try revalidating the device, which will end up sending a start unit.
+ **/
+static int scsi_eh_stu(struct Scsi_Host *shost,
+ struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd, *stu_scmd;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost) {
+ stu_scmd = NULL;
+ list_for_each_entry(scmd, work_q, eh_entry)
+ if (scmd->device == sdev && SCSI_SENSE_VALID(scmd) &&
+ scsi_check_sense(scmd) == FAILED ) {
+ stu_scmd = scmd;
+ break;
+ }
+
+ if (!stu_scmd)
+ continue;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending START_UNIT to sdev:"
+ " 0x%p\n", current->comm, sdev));
+
+ if (!scsi_eh_try_stu(stu_scmd)) {
+ if (!scsi_device_online(sdev) ||
+ !scsi_eh_tur(stu_scmd)) {
+ list_for_each_safe(lh, lh_sf, work_q) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ if (scmd->device == sdev)
+ scsi_eh_finish_cmd(scmd, done_q);
+ }
+ }
+ } else {
+ SCSI_LOG_ERROR_RECOVERY(3,
+ printk("%s: START_UNIT failed to sdev:"
+ " 0x%p\n", current->comm, sdev));
+ }
+ }
+
+ return list_empty(work_q);
+}
+
+
+/**
+ * scsi_eh_bus_device_reset - send bdr if needed
+ * @shost: scsi host being recovered.
+ * @eh_done_q: list_head for processed commands.
+ *
+ * Notes:
+ * Try a bus device reset. still, look to see whether we have multiple
+ * devices that are jammed or not - if we have multiple devices, it
+ * makes no sense to try bus_device_reset - we really would need to try
+ * a bus_reset instead.
+ **/
+static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
+ struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd, *bdr_scmd;
+ struct scsi_device *sdev;
+ int rtn;
+
+ shost_for_each_device(sdev, shost) {
+ bdr_scmd = NULL;
+ list_for_each_entry(scmd, work_q, eh_entry)
+ if (scmd->device == sdev) {
+ bdr_scmd = scmd;
+ break;
+ }
+
+ if (!bdr_scmd)
+ continue;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending BDR sdev:"
+ " 0x%p\n", current->comm,
+ sdev));
+ rtn = scsi_try_bus_device_reset(bdr_scmd);
+ if (rtn == SUCCESS) {
+ if (!scsi_device_online(sdev) ||
+ !scsi_eh_tur(bdr_scmd)) {
+ list_for_each_safe(lh, lh_sf,
+ work_q) {
+ scmd = list_entry(lh, struct
+ scsi_cmnd,
+ eh_entry);
+ if (scmd->device == sdev)
+ scsi_eh_finish_cmd(scmd,
+ done_q);
+ }
+ }
+ } else {
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: BDR"
+ " failed sdev:"
+ "0x%p\n",
+ current->comm,
+ sdev));
+ }
+ }
+
+ return list_empty(work_q);
+}
+
+/**
+ * scsi_try_bus_reset - ask host to perform a bus reset
+ * @scmd: SCSI cmd to send bus reset.
+ **/
+static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
+ __FUNCTION__));
+ scmd->owner = SCSI_OWNER_LOWLEVEL;
+ scmd->serial_number_at_timeout = scmd->serial_number;
+
+ if (!scmd->device->host->hostt->eh_bus_reset_handler)
+ return FAILED;
+
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+
+ if (rtn == SUCCESS) {
+ if (!scmd->device->host->hostt->skip_settle_delay)
+ ssleep(BUS_RESET_SETTLE_TIME);
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ scsi_report_bus_reset(scmd->device->host, scmd->device->channel);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+ }
+
+ return rtn;
+}
+
+/**
+ * scsi_try_host_reset - ask host adapter to reset itself
+ * @scmd: SCSI cmd to send hsot reset.
+ **/
+static int scsi_try_host_reset(struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ int rtn;
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
+ __FUNCTION__));
+ scmd->owner = SCSI_OWNER_LOWLEVEL;
+ scmd->serial_number_at_timeout = scmd->serial_number;
+
+ if (!scmd->device->host->hostt->eh_host_reset_handler)
+ return FAILED;
+
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+
+ if (rtn == SUCCESS) {
+ if (!scmd->device->host->hostt->skip_settle_delay)
+ ssleep(HOST_RESET_SETTLE_TIME);
+ spin_lock_irqsave(scmd->device->host->host_lock, flags);
+ scsi_report_bus_reset(scmd->device->host, scmd->device->channel);
+ spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+ }
+
+ return rtn;
+}
+
+/**
+ * scsi_eh_bus_reset - send a bus reset
+ * @shost: scsi host being recovered.
+ * @eh_done_q: list_head for processed commands.
+ **/
+static int scsi_eh_bus_reset(struct Scsi_Host *shost,
+ struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+ struct scsi_cmnd *chan_scmd;
+ unsigned int channel;
+ int rtn;
+
+ /*
+ * we really want to loop over the various channels, and do this on
+ * a channel by channel basis. we should also check to see if any
+ * of the failed commands are on soft_reset devices, and if so, skip
+ * the reset.
+ */
+
+ for (channel = 0; channel <= shost->max_channel; channel++) {
+ chan_scmd = NULL;
+ list_for_each_entry(scmd, work_q, eh_entry) {
+ if (channel == scmd->device->channel) {
+ chan_scmd = scmd;
+ break;
+ /*
+ * FIXME add back in some support for
+ * soft_reset devices.
+ */
+ }
+ }
+
+ if (!chan_scmd)
+ continue;
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending BRST chan:"
+ " %d\n", current->comm,
+ channel));
+ rtn = scsi_try_bus_reset(chan_scmd);
+ if (rtn == SUCCESS) {
+ list_for_each_safe(lh, lh_sf, work_q) {
+ scmd = list_entry(lh, struct scsi_cmnd,
+ eh_entry);
+ if (channel == scmd->device->channel)
+ if (!scsi_device_online(scmd->device) ||
+ !scsi_eh_tur(scmd))
+ scsi_eh_finish_cmd(scmd,
+ done_q);
+ }
+ } else {
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: BRST"
+ " failed chan: %d\n",
+ current->comm,
+ channel));
+ }
+ }
+ return list_empty(work_q);
+}
+
+/**
+ * scsi_eh_host_reset - send a host reset
+ * @work_q: list_head for processed commands.
+ * @done_q: list_head for processed commands.
+ **/
+static int scsi_eh_host_reset(struct list_head *work_q,
+ struct list_head *done_q)
+{
+ int rtn;
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+
+ if (!list_empty(work_q)) {
+ scmd = list_entry(work_q->next,
+ struct scsi_cmnd, eh_entry);
+
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending HRST\n"
+ , current->comm));
+
+ rtn = scsi_try_host_reset(scmd);
+ if (rtn == SUCCESS) {
+ list_for_each_safe(lh, lh_sf, work_q) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ if (!scsi_device_online(scmd->device) ||
+ (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) ||
+ !scsi_eh_tur(scmd))
+ scsi_eh_finish_cmd(scmd, done_q);
+ }
+ } else {
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: HRST"
+ " failed\n",
+ current->comm));
+ }
+ }
+ return list_empty(work_q);
+}
+
+/**
+ * scsi_eh_offline_sdevs - offline scsi devices that fail to recover
+ * @work_q: list_head for processed commands.
+ * @done_q: list_head for processed commands.
+ *
+ **/
+static void scsi_eh_offline_sdevs(struct list_head *work_q,
+ struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+
+ list_for_each_safe(lh, lh_sf, work_q) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ printk(KERN_INFO "scsi: Device offlined - not"
+ " ready after error recovery: host"
+ " %d channel %d id %d lun %d\n",
+ scmd->device->host->host_no,
+ scmd->device->channel,
+ scmd->device->id,
+ scmd->device->lun);
+ scsi_device_set_state(scmd->device, SDEV_OFFLINE);
+ if (scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD)) {
+ /*
+ * FIXME: Handle lost cmds.
+ */
+ }
+ scsi_eh_finish_cmd(scmd, done_q);
+ }
+ return;
+}
+
+/**
+ * scsi_decide_disposition - Disposition a cmd on return from LLD.
+ * @scmd: SCSI cmd to examine.
+ *
+ * Notes:
+ * This is *only* called when we are examining the status after sending
+ * out the actual data command. any commands that are queued for error
+ * recovery (e.g. test_unit_ready) do *not* come through here.
+ *
+ * When this routine returns failed, it means the error handler thread
+ * is woken. In cases where the error code indicates an error that
+ * doesn't require the error handler read (i.e. we don't need to
+ * abort/reset), this function should return SUCCESS.
+ **/
+int scsi_decide_disposition(struct scsi_cmnd *scmd)
+{
+ int rtn;
+
+ /*
+ * if the device is offline, then we clearly just pass the result back
+ * up to the top level.
+ */
+ if (!scsi_device_online(scmd->device)) {
+ SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report"
+ " as SUCCESS\n",
+ __FUNCTION__));
+ return SUCCESS;
+ }
+
+ /*
+ * first check the host byte, to see if there is anything in there
+ * that would indicate what we need to do.
+ */
+ switch (host_byte(scmd->result)) {
+ case DID_PASSTHROUGH:
+ /*
+ * no matter what, pass this through to the upper layer.
+ * nuke this special code so that it looks like we are saying
+ * did_ok.
+ */
+ scmd->result &= 0xff00ffff;
+ return SUCCESS;
+ case DID_OK:
+ /*
+ * looks good. drop through, and check the next byte.
+ */
+ break;
+ case DID_NO_CONNECT:
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ /*
+ * note - this means that we just report the status back
+ * to the top level driver, not that we actually think
+ * that it indicates SUCCESS.
+ */
+ return SUCCESS;
+ /*
+ * when the low level driver returns did_soft_error,
+ * it is responsible for keeping an internal retry counter
+ * in order to avoid endless loops (db)
+ *
+ * actually this is a bug in this function here. we should
+ * be mindful of the maximum number of retries specified
+ * and not get stuck in a loop.
+ */
+ case DID_SOFT_ERROR:
+ goto maybe_retry;
+ case DID_IMM_RETRY:
+ return NEEDS_RETRY;
+
+ case DID_ERROR:
+ if (msg_byte(scmd->result) == COMMAND_COMPLETE &&
+ status_byte(scmd->result) == RESERVATION_CONFLICT)
+ /*
+ * execute reservation conflict processing code
+ * lower down
+ */
+ break;
+ /* fallthrough */
+
+ case DID_BUS_BUSY:
+ case DID_PARITY:
+ goto maybe_retry;
+ case DID_TIME_OUT:
+ /*
+ * when we scan the bus, we get timeout messages for
+ * these commands if there is no device available.
+ * other hosts report did_no_connect for the same thing.
+ */
+ if ((scmd->cmnd[0] == TEST_UNIT_READY ||
+ scmd->cmnd[0] == INQUIRY)) {
+ return SUCCESS;
+ } else {
+ return FAILED;
+ }
+ case DID_RESET:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+
+ /*
+ * next, check the message byte.
+ */
+ if (msg_byte(scmd->result) != COMMAND_COMPLETE)
+ return FAILED;
+
+ /*
+ * check the status byte to see if this indicates anything special.
+ */
+ switch (status_byte(scmd->result)) {
+ case QUEUE_FULL:
+ /*
+ * the case of trying to send too many commands to a
+ * tagged queueing device.
+ */
+ case BUSY:
+ /*
+ * device can't talk to us at the moment. Should only
+ * occur (SAM-3) when the task queue is empty, so will cause
+ * the empty queue handling to trigger a stall in the
+ * device.
+ */
+ return ADD_TO_MLQUEUE;
+ case GOOD:
+ case COMMAND_TERMINATED:
+ case TASK_ABORTED:
+ return SUCCESS;
+ case CHECK_CONDITION:
+ rtn = scsi_check_sense(scmd);
+ if (rtn == NEEDS_RETRY)
+ goto maybe_retry;
+ /* if rtn == FAILED, we have no sense information;
+ * returning FAILED will wake the error handler thread
+ * to collect the sense and redo the decide
+ * disposition */
+ return rtn;
+ case CONDITION_GOOD:
+ case INTERMEDIATE_GOOD:
+ case INTERMEDIATE_C_GOOD:
+ case ACA_ACTIVE:
+ /*
+ * who knows? FIXME(eric)
+ */
+ return SUCCESS;
+
+ case RESERVATION_CONFLICT:
+ printk(KERN_INFO "scsi: reservation conflict: host"
+ " %d channel %d id %d lun %d\n",
+ scmd->device->host->host_no, scmd->device->channel,
+ scmd->device->id, scmd->device->lun);
+ return SUCCESS; /* causes immediate i/o error */
+ default:
+ return FAILED;
+ }
+ return FAILED;
+
+ maybe_retry:
+
+ /* we requeue for retry because the error was retryable, and
+ * the request was not marked fast fail. Note that above,
+ * even if the request is marked fast fail, we still requeue
+ * for queue congestion conditions (QUEUE_FULL or BUSY) */
+ if ((++scmd->retries) < scmd->allowed
+ && !blk_noretry_request(scmd->request)) {
+ return NEEDS_RETRY;
+ } else {
+ /*
+ * no more retries - report this one back to upper level.
+ */
+ return SUCCESS;
+ }
+}
+
+/**
+ * scsi_eh_lock_done - done function for eh door lock request
+ * @scmd: SCSI command block for the door lock request
+ *
+ * Notes:
+ * We completed the asynchronous door lock request, and it has either
+ * locked the door or failed. We must free the command structures
+ * associated with this request.
+ **/
+static void scsi_eh_lock_done(struct scsi_cmnd *scmd)
+{
+ struct scsi_request *sreq = scmd->sc_request;
+
+ scsi_release_request(sreq);
+}
+
+
+/**
+ * scsi_eh_lock_door - Prevent medium removal for the specified device
+ * @sdev: SCSI device to prevent medium removal
+ *
+ * Locking:
+ * We must be called from process context; scsi_allocate_request()
+ * may sleep.
+ *
+ * Notes:
+ * We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request on the
+ * head of the devices request queue, and continue.
+ *
+ * Bugs:
+ * scsi_allocate_request() may sleep waiting for existing requests to
+ * be processed. However, since we haven't kicked off any request
+ * processing for this host, this may deadlock.
+ *
+ * If scsi_allocate_request() fails for what ever reason, we
+ * completely forget to lock the door.
+ **/
+static void scsi_eh_lock_door(struct scsi_device *sdev)
+{
+ struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL);
+
+ if (unlikely(!sreq)) {
+ printk(KERN_ERR "%s: request allocate failed,"
+ "prevent media removal cmd not sent\n", __FUNCTION__);
+ return;
+ }
+
+ sreq->sr_cmnd[0] = ALLOW_MEDIUM_REMOVAL;
+ sreq->sr_cmnd[1] = 0;
+ sreq->sr_cmnd[2] = 0;
+ sreq->sr_cmnd[3] = 0;
+ sreq->sr_cmnd[4] = SCSI_REMOVAL_PREVENT;
+ sreq->sr_cmnd[5] = 0;
+ sreq->sr_data_direction = DMA_NONE;
+ sreq->sr_bufflen = 0;
+ sreq->sr_buffer = NULL;
+ sreq->sr_allowed = 5;
+ sreq->sr_done = scsi_eh_lock_done;
+ sreq->sr_timeout_per_command = 10 * HZ;
+ sreq->sr_cmd_len = COMMAND_SIZE(sreq->sr_cmnd[0]);
+
+ scsi_insert_special_req(sreq, 1);
+}
+
+
+/**
+ * scsi_restart_operations - restart io operations to the specified host.
+ * @shost: Host we are restarting.
+ *
+ * Notes:
+ * When we entered the error handler, we blocked all further i/o to
+ * this device. we need to 'reverse' this process.
+ **/
+static void scsi_restart_operations(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+
+ /*
+ * If the door was locked, we need to insert a door lock request
+ * onto the head of the SCSI request queue for the device. There
+ * is no point trying to lock the door of an off-line device.
+ */
+ shost_for_each_device(sdev, shost) {
+ if (scsi_device_online(sdev) && sdev->locked)
+ scsi_eh_lock_door(sdev);
+ }
+
+ /*
+ * next free up anything directly waiting upon the host. this
+ * will be requests for character device operations, and also for
+ * ioctls to queued block devices.
+ */
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
+ __FUNCTION__));
+
+ clear_bit(SHOST_RECOVERY, &shost->shost_state);
+
+ wake_up(&shost->host_wait);
+
+ /*
+ * finally we need to re-initiate requests that may be pending. we will
+ * have had everything blocked while error handling is taking place, and
+ * now that error recovery is done, we will need to ensure that these
+ * requests are started.
+ */
+ scsi_run_host_queues(shost);
+}
+
+/**
+ * scsi_eh_ready_devs - check device ready state and recover if not.
+ * @shost: host to be recovered.
+ * @eh_done_q: list_head for processed commands.
+ *
+ **/
+static void scsi_eh_ready_devs(struct Scsi_Host *shost,
+ struct list_head *work_q,
+ struct list_head *done_q)
+{
+ if (!scsi_eh_stu(shost, work_q, done_q))
+ if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
+ if (!scsi_eh_bus_reset(shost, work_q, done_q))
+ if (!scsi_eh_host_reset(work_q, done_q))
+ scsi_eh_offline_sdevs(work_q, done_q);
+}
+
+/**
+ * scsi_eh_flush_done_q - finish processed commands or retry them.
+ * @done_q: list_head of processed commands.
+ *
+ **/
+static void scsi_eh_flush_done_q(struct list_head *done_q)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+
+ list_for_each_safe(lh, lh_sf, done_q) {
+ scmd = list_entry(lh, struct scsi_cmnd, eh_entry);
+ list_del_init(lh);
+ if (scsi_device_online(scmd->device) &&
+ !blk_noretry_request(scmd->request) &&
+ (++scmd->retries < scmd->allowed)) {
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: flush"
+ " retry cmd: %p\n",
+ current->comm,
+ scmd));
+ scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
+ } else {
+ if (!scmd->result)
+ scmd->result |= (DRIVER_TIMEOUT << 24);
+ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: flush finish"
+ " cmd: %p\n",
+ current->comm, scmd));
+ scsi_finish_command(scmd);
+ }
+ }
+}
+
+/**
+ * scsi_unjam_host - Attempt to fix a host which has a cmd that failed.
+ * @shost: Host to unjam.
+ *
+ * Notes:
+ * When we come in here, we *know* that all commands on the bus have
+ * either completed, failed or timed out. we also know that no further
+ * commands are being sent to the host, so things are relatively quiet
+ * and we have freedom to fiddle with things as we wish.
+ *
+ * This is only the *default* implementation. it is possible for
+ * individual drivers to supply their own version of this function, and
+ * if the maintainer wishes to do this, it is strongly suggested that
+ * this function be taken as a template and modified. this function
+ * was designed to correctly handle problems for about 95% of the
+ * different cases out there, and it should always provide at least a
+ * reasonable amount of error recovery.
+ *
+ * Any command marked 'failed' or 'timeout' must eventually have
+ * scsi_finish_cmd() called for it. we do all of the retry stuff
+ * here, so when we restart the host after we return it should have an
+ * empty queue.
+ **/
+static void scsi_unjam_host(struct Scsi_Host *shost)
+{
+ unsigned long flags;
+ LIST_HEAD(eh_work_q);
+ LIST_HEAD(eh_done_q);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_splice_init(&shost->eh_cmd_q, &eh_work_q);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(shost, &eh_work_q));
+
+ if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))
+ if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))
+ scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
+
+ scsi_eh_flush_done_q(&eh_done_q);
+}
+
+/**
+ * scsi_error_handler - Handle errors/timeouts of SCSI cmds.
+ * @data: Host for which we are running.
+ *
+ * Notes:
+ * This is always run in the context of a kernel thread. The idea is
+ * that we start this thing up when the kernel starts up (one per host
+ * that we detect), and it immediately goes to sleep and waits for some
+ * event (i.e. failure). When this takes place, we have the job of
+ * trying to unjam the bus and restarting things.
+ **/
+int scsi_error_handler(void *data)
+{
+ struct Scsi_Host *shost = (struct Scsi_Host *) data;
+ int rtn;
+ DECLARE_MUTEX_LOCKED(sem);
+
+ /*
+ * Flush resources
+ */
+
+ daemonize("scsi_eh_%d", shost->host_no);
+
+ current->flags |= PF_NOFREEZE;
+
+ shost->eh_wait = &sem;
+ shost->ehandler = current;
+
+ /*
+ * Wake up the thread that created us.
+ */
+ SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent of"
+ " scsi_eh_%d\n",shost->host_no));
+
+ complete(shost->eh_notify);
+
+ while (1) {
+ /*
+ * If we get a signal, it means we are supposed to go
+ * away and die. This typically happens if the user is
+ * trying to unload a module.
+ */
+ SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler"
+ " scsi_eh_%d"
+ " sleeping\n",shost->host_no));
+
+ /*
+ * Note - we always use down_interruptible with the semaphore
+ * even if the module was loaded as part of the kernel. The
+ * reason is that down() will cause this thread to be counted
+ * in the load average as a running process, and down
+ * interruptible doesn't. Given that we need to allow this
+ * thread to die if the driver was loaded as a module, using
+ * semaphores isn't unreasonable.
+ */
+ down_interruptible(&sem);
+ if (shost->eh_kill)
+ break;
+
+ SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler"
+ " scsi_eh_%d waking"
+ " up\n",shost->host_no));
+
+ shost->eh_active = 1;
+
+ /*
+ * We have a host that is failing for some reason. Figure out
+ * what we need to do to get it up and online again (if we can).
+ * If we fail, we end up taking the thing offline.
+ */
+ if (shost->hostt->eh_strategy_handler)
+ rtn = shost->hostt->eh_strategy_handler(shost);
+ else
+ scsi_unjam_host(shost);
+
+ shost->eh_active = 0;
+
+ /*
+ * Note - if the above fails completely, the action is to take
+ * individual devices offline and flush the queue of any
+ * outstanding requests that may have been pending. When we
+ * restart, we restart any I/O to any other devices on the bus
+ * which are still online.
+ */
+ scsi_restart_operations(shost);
+
+ }
+
+ SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d"
+ " exiting\n",shost->host_no));
+
+ /*
+ * Make sure that nobody tries to wake us up again.
+ */
+ shost->eh_wait = NULL;
+
+ /*
+ * Knock this down too. From this point on, the host is flying
+ * without a pilot. If this is because the module is being unloaded,
+ * that's fine. If the user sent a signal to this thing, we are
+ * potentially in real danger.
+ */
+ shost->eh_active = 0;
+ shost->ehandler = NULL;
+
+ /*
+ * If anyone is waiting for us to exit (i.e. someone trying to unload
+ * a driver), then wake up that process to let them know we are on
+ * the way out the door.
+ */
+ complete_and_exit(shost->eh_notify, 0);
+ return 0;
+}
+
+/*
+ * Function: scsi_report_bus_reset()
+ *
+ * Purpose: Utility function used by low-level drivers to report that
+ * they have observed a bus reset on the bus being handled.
+ *
+ * Arguments: shost - Host in question
+ * channel - channel on which reset was observed.
+ *
+ * Returns: Nothing
+ *
+ * Lock status: Host lock must be held.
+ *
+ * Notes: This only needs to be called if the reset is one which
+ * originates from an unknown location. Resets originated
+ * by the mid-level itself don't need to call this, but there
+ * should be no harm.
+ *
+ * The main purpose of this is to make sure that a CHECK_CONDITION
+ * is properly treated.
+ */
+void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)
+{
+ struct scsi_device *sdev;
+
+ __shost_for_each_device(sdev, shost) {
+ if (channel == sdev->channel) {
+ sdev->was_reset = 1;
+ sdev->expecting_cc_ua = 1;
+ }
+ }
+}
+EXPORT_SYMBOL(scsi_report_bus_reset);
+
+/*
+ * Function: scsi_report_device_reset()
+ *
+ * Purpose: Utility function used by low-level drivers to report that
+ * they have observed a device reset on the device being handled.
+ *
+ * Arguments: shost - Host in question
+ * channel - channel on which reset was observed
+ * target - target on which reset was observed
+ *
+ * Returns: Nothing
+ *
+ * Lock status: Host lock must be held
+ *
+ * Notes: This only needs to be called if the reset is one which
+ * originates from an unknown location. Resets originated
+ * by the mid-level itself don't need to call this, but there
+ * should be no harm.
+ *
+ * The main purpose of this is to make sure that a CHECK_CONDITION
+ * is properly treated.
+ */
+void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target)
+{
+ struct scsi_device *sdev;
+
+ __shost_for_each_device(sdev, shost) {
+ if (channel == sdev->channel &&
+ target == sdev->id) {
+ sdev->was_reset = 1;
+ sdev->expecting_cc_ua = 1;
+ }
+ }
+}
+EXPORT_SYMBOL(scsi_report_device_reset);
+
+static void
+scsi_reset_provider_done_command(struct scsi_cmnd *scmd)
+{
+}
+
+/*
+ * Function: scsi_reset_provider
+ *
+ * Purpose: Send requested reset to a bus or device at any phase.
+ *
+ * Arguments: device - device to send reset to
+ * flag - reset type (see scsi.h)
+ *
+ * Returns: SUCCESS/FAILURE.
+ *
+ * Notes: This is used by the SCSI Generic driver to provide
+ * Bus/Device reset capability.
+ */
+int
+scsi_reset_provider(struct scsi_device *dev, int flag)
+{
+ struct scsi_cmnd *scmd = scsi_get_command(dev, GFP_KERNEL);
+ struct request req;
+ int rtn;
+
+ scmd->request = &req;
+ memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout));
+ scmd->request->rq_status = RQ_SCSI_BUSY;
+ scmd->state = SCSI_STATE_INITIALIZING;
+ scmd->owner = SCSI_OWNER_MIDLEVEL;
+
+ memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd));
+
+ scmd->scsi_done = scsi_reset_provider_done_command;
+ scmd->done = NULL;
+ scmd->buffer = NULL;
+ scmd->bufflen = 0;
+ scmd->request_buffer = NULL;
+ scmd->request_bufflen = 0;
+ scmd->internal_timeout = NORMAL_TIMEOUT;
+ scmd->abort_reason = DID_ABORT;
+
+ scmd->cmd_len = 0;
+
+ scmd->sc_data_direction = DMA_BIDIRECTIONAL;
+ scmd->sc_request = NULL;
+ scmd->sc_magic = SCSI_CMND_MAGIC;
+
+ init_timer(&scmd->eh_timeout);
+
+ /*
+ * Sometimes the command can get back into the timer chain,
+ * so use the pid as an identifier.
+ */
+ scmd->pid = 0;
+
+ switch (flag) {
+ case SCSI_TRY_RESET_DEVICE:
+ rtn = scsi_try_bus_device_reset(scmd);
+ if (rtn == SUCCESS)
+ break;
+ /* FALLTHROUGH */
+ case SCSI_TRY_RESET_BUS:
+ rtn = scsi_try_bus_reset(scmd);
+ if (rtn == SUCCESS)
+ break;
+ /* FALLTHROUGH */
+ case SCSI_TRY_RESET_HOST:
+ rtn = scsi_try_host_reset(scmd);
+ break;
+ default:
+ rtn = FAILED;
+ }
+
+ scsi_delete_timer(scmd);
+ scsi_next_command(scmd);
+ return rtn;
+}
+EXPORT_SYMBOL(scsi_reset_provider);
+
+/**
+ * scsi_normalize_sense - normalize main elements from either fixed or
+ * descriptor sense data format into a common format.
+ *
+ * @sense_buffer: byte array containing sense data returned by device
+ * @sb_len: number of valid bytes in sense_buffer
+ * @sshdr: pointer to instance of structure that common
+ * elements are written to.
+ *
+ * Notes:
+ * The "main elements" from sense data are: response_code, sense_key,
+ * asc, ascq and additional_length (only for descriptor format).
+ *
+ * Typically this function can be called after a device has
+ * responded to a SCSI command with the CHECK_CONDITION status.
+ *
+ * Return value:
+ * 1 if valid sense data information found, else 0;
+ **/
+int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+ struct scsi_sense_hdr *sshdr)
+{
+ if (!sense_buffer || !sb_len || (sense_buffer[0] & 0x70) != 0x70)
+ return 0;
+
+ memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
+
+ sshdr->response_code = (sense_buffer[0] & 0x7f);
+ if (sshdr->response_code >= 0x72) {
+ /*
+ * descriptor format
+ */
+ if (sb_len > 1)
+ sshdr->sense_key = (sense_buffer[1] & 0xf);
+ if (sb_len > 2)
+ sshdr->asc = sense_buffer[2];
+ if (sb_len > 3)
+ sshdr->ascq = sense_buffer[3];
+ if (sb_len > 7)
+ sshdr->additional_length = sense_buffer[7];
+ } else {
+ /*
+ * fixed format
+ */
+ if (sb_len > 2)
+ sshdr->sense_key = (sense_buffer[2] & 0xf);
+ if (sb_len > 7) {
+ sb_len = (sb_len < (sense_buffer[7] + 8)) ?
+ sb_len : (sense_buffer[7] + 8);
+ if (sb_len > 12)
+ sshdr->asc = sense_buffer[12];
+ if (sb_len > 13)
+ sshdr->ascq = sense_buffer[13];
+ }
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(scsi_normalize_sense);
+
+int scsi_request_normalize_sense(struct scsi_request *sreq,
+ struct scsi_sense_hdr *sshdr)
+{
+ return scsi_normalize_sense(sreq->sr_sense_buffer,
+ sizeof(sreq->sr_sense_buffer), sshdr);
+}
+EXPORT_SYMBOL(scsi_request_normalize_sense);
+
+int scsi_command_normalize_sense(struct scsi_cmnd *cmd,
+ struct scsi_sense_hdr *sshdr)
+{
+ return scsi_normalize_sense(cmd->sense_buffer,
+ sizeof(cmd->sense_buffer), sshdr);
+}
+EXPORT_SYMBOL(scsi_command_normalize_sense);
+
+/**
+ * scsi_sense_desc_find - search for a given descriptor type in
+ * descriptor sense data format.
+ *
+ * @sense_buffer: byte array of descriptor format sense data
+ * @sb_len: number of valid bytes in sense_buffer
+ * @desc_type: value of descriptor type to find
+ * (e.g. 0 -> information)
+ *
+ * Notes:
+ * only valid when sense data is in descriptor format
+ *
+ * Return value:
+ * pointer to start of (first) descriptor if found else NULL
+ **/
+const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
+ int desc_type)
+{
+ int add_sen_len, add_len, desc_len, k;
+ const u8 * descp;
+
+ if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7])))
+ return NULL;
+ if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73))
+ return NULL;
+ add_sen_len = (add_sen_len < (sb_len - 8)) ?
+ add_sen_len : (sb_len - 8);
+ descp = &sense_buffer[8];
+ for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
+ descp += desc_len;
+ add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
+ desc_len = add_len + 2;
+ if (descp[0] == desc_type)
+ return descp;
+ if (add_len < 0) // short descriptor ??
+ break;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(scsi_sense_desc_find);
+
+/**
+ * scsi_get_sense_info_fld - attempts to get information field from
+ * sense data (either fixed or descriptor format)
+ *
+ * @sense_buffer: byte array of sense data
+ * @sb_len: number of valid bytes in sense_buffer
+ * @info_out: pointer to 64 integer where 8 or 4 byte information
+ * field will be placed if found.
+ *
+ * Return value:
+ * 1 if information field found, 0 if not found.
+ **/
+int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
+ u64 * info_out)
+{
+ int j;
+ const u8 * ucp;
+ u64 ull;
+
+ if (sb_len < 7)
+ return 0;
+ switch (sense_buffer[0] & 0x7f) {
+ case 0x70:
+ case 0x71:
+ if (sense_buffer[0] & 0x80) {
+ *info_out = (sense_buffer[3] << 24) +
+ (sense_buffer[4] << 16) +
+ (sense_buffer[5] << 8) + sense_buffer[6];
+ return 1;
+ } else
+ return 0;
+ case 0x72:
+ case 0x73:
+ ucp = scsi_sense_desc_find(sense_buffer, sb_len,
+ 0 /* info desc */);
+ if (ucp && (0xa == ucp[1])) {
+ ull = 0;
+ for (j = 0; j < 8; ++j) {
+ if (j > 0)
+ ull <<= 8;
+ ull |= ucp[4 + j];
+ }
+ *info_out = ull;
+ return 1;
+ } else
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(scsi_get_sense_info_fld);
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
new file mode 100644
index 000000000000..68c9728dfbbb
--- /dev/null
+++ b/drivers/scsi/scsi_ioctl.c
@@ -0,0 +1,516 @@
+/*
+ * Changes:
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 08/23/2000
+ * - get rid of some verify_areas and use __copy*user and __get/put_user
+ * for the ones that remain
+ */
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_dbg.h>
+
+#include "scsi_logging.h"
+
+#define NORMAL_RETRIES 5
+#define IOCTL_NORMAL_TIMEOUT (10 * HZ)
+#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
+#define START_STOP_TIMEOUT (60 * HZ)
+#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
+#define READ_ELEMENT_STATUS_TIMEOUT (5 * 60 * HZ)
+#define READ_DEFECT_DATA_TIMEOUT (60 * HZ ) /* ZIP-250 on parallel port takes as long! */
+
+#define MAX_BUF PAGE_SIZE
+
+/*
+ * If we are told to probe a host, we will return 0 if the host is not
+ * present, 1 if the host is present, and will return an identifying
+ * string at *arg, if arg is non null, filling to the length stored at
+ * (int *) arg
+ */
+
+static int ioctl_probe(struct Scsi_Host *host, void __user *buffer)
+{
+ unsigned int len, slen;
+ const char *string;
+ int temp = host->hostt->present;
+
+ if (temp && buffer) {
+ if (get_user(len, (unsigned int __user *) buffer))
+ return -EFAULT;
+
+ if (host->hostt->info)
+ string = host->hostt->info(host);
+ else
+ string = host->hostt->name;
+ if (string) {
+ slen = strlen(string);
+ if (len > slen)
+ len = slen + 1;
+ if (copy_to_user(buffer, string, len))
+ return -EFAULT;
+ }
+ }
+ return temp;
+}
+
+/*
+
+ * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
+ * The IOCTL_NORMAL_TIMEOUT and NORMAL_RETRIES variables are used.
+ *
+ * dev is the SCSI device struct ptr, *(int *) arg is the length of the
+ * input data, if any, not including the command string & counts,
+ * *((int *)arg + 1) is the output buffer size in bytes.
+ *
+ * *(char *) ((int *) arg)[2] the actual command byte.
+ *
+ * Note that if more than MAX_BUF bytes are requested to be transferred,
+ * the ioctl will fail with error EINVAL.
+ *
+ * This size *does not* include the initial lengths that were passed.
+ *
+ * The SCSI command is read from the memory location immediately after the
+ * length words, and the input data is right after the command. The SCSI
+ * routines know the command size based on the opcode decode.
+ *
+ * The output area is then filled in starting from the command byte.
+ */
+
+static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
+ int timeout, int retries)
+{
+ struct scsi_request *sreq;
+ int result;
+ struct scsi_sense_hdr sshdr;
+
+ SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd));
+
+ sreq = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!sreq) {
+ printk(KERN_WARNING "SCSI internal ioctl failed, no memory\n");
+ return -ENOMEM;
+ }
+
+ sreq->sr_data_direction = DMA_NONE;
+ scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries);
+
+ SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", sreq->sr_result));
+
+ if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
+ (scsi_request_normalize_sense(sreq, &sshdr))) {
+ switch (sshdr.sense_key) {
+ case ILLEGAL_REQUEST:
+ if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
+ sdev->lockable = 0;
+ else
+ printk(KERN_INFO "ioctl_internal_command: "
+ "ILLEGAL REQUEST asc=0x%x ascq=0x%x\n",
+ sshdr.asc, sshdr.ascq);
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ if (sdev->removable && (cmd[0] != TEST_UNIT_READY)) {
+ printk(KERN_INFO "Device not ready. Make sure"
+ " there is a disc in the drive.\n");
+ break;
+ }
+ case UNIT_ATTENTION:
+ if (sdev->removable) {
+ sdev->changed = 1;
+ sreq->sr_result = 0; /* This is no longer considered an error */
+ break;
+ }
+ default: /* Fall through for non-removable media */
+ printk(KERN_INFO "ioctl_internal_command: <%d %d %d "
+ "%d> return code = %x\n",
+ sdev->host->host_no,
+ sdev->channel,
+ sdev->id,
+ sdev->lun,
+ sreq->sr_result);
+ scsi_print_req_sense(" ", sreq);
+ break;
+ }
+ }
+
+ result = sreq->sr_result;
+ SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
+ scsi_release_request(sreq);
+ return result;
+}
+
+int scsi_set_medium_removal(struct scsi_device *sdev, char state)
+{
+ char scsi_cmd[MAX_COMMAND_SIZE];
+ int ret;
+
+ if (!sdev->removable || !sdev->lockable)
+ return 0;
+
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = 0;
+ scsi_cmd[2] = 0;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = state;
+ scsi_cmd[5] = 0;
+
+ ret = ioctl_internal_command(sdev, scsi_cmd,
+ IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
+ if (ret == 0)
+ sdev->locked = (state == SCSI_REMOVAL_PREVENT);
+ return ret;
+}
+EXPORT_SYMBOL(scsi_set_medium_removal);
+
+/*
+ * This interface is deprecated - users should use the scsi generic (sg)
+ * interface instead, as this is a more flexible approach to performing
+ * generic SCSI commands on a device.
+ *
+ * The structure that we are passed should look like:
+ *
+ * struct sdata {
+ * unsigned int inlen; [i] Length of data to be written to device
+ * unsigned int outlen; [i] Length of data to be read from device
+ * unsigned char cmd[x]; [i] SCSI command (6 <= x <= 12).
+ * [o] Data read from device starts here.
+ * [o] On error, sense buffer starts here.
+ * unsigned char wdata[y]; [i] Data written to device starts here.
+ * };
+ * Notes:
+ * - The SCSI command length is determined by examining the 1st byte
+ * of the given command. There is no way to override this.
+ * - Data transfers are limited to PAGE_SIZE (4K on i386, 8K on alpha).
+ * - The length (x + y) must be at least OMAX_SB_LEN bytes long to
+ * accommodate the sense buffer when an error occurs.
+ * The sense buffer is truncated to OMAX_SB_LEN (16) bytes so that
+ * old code will not be surprised.
+ * - If a Unix error occurs (e.g. ENOMEM) then the user will receive
+ * a negative return and the Unix error code in 'errno'.
+ * If the SCSI command succeeds then 0 is returned.
+ * Positive numbers returned are the compacted SCSI error codes (4
+ * bytes in one int) where the lowest byte is the SCSI status.
+ * See the drivers/scsi/scsi.h file for more information on this.
+ *
+ */
+#define OMAX_SB_LEN 16 /* Old sense buffer length */
+
+int scsi_ioctl_send_command(struct scsi_device *sdev,
+ struct scsi_ioctl_command __user *sic)
+{
+ char *buf;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ char __user *cmd_in;
+ struct scsi_request *sreq;
+ unsigned char opcode;
+ unsigned int inlen, outlen, cmdlen;
+ unsigned int needed, buf_needed;
+ int timeout, retries, result;
+ int data_direction, gfp_mask = GFP_KERNEL;
+
+ if (!sic)
+ return -EINVAL;
+
+ if (sdev->host->unchecked_isa_dma)
+ gfp_mask |= GFP_DMA;
+
+ /*
+ * Verify that we can read at least this much.
+ */
+ if (!access_ok(VERIFY_READ, sic, sizeof(Scsi_Ioctl_Command)))
+ return -EFAULT;
+
+ if(__get_user(inlen, &sic->inlen))
+ return -EFAULT;
+
+ if(__get_user(outlen, &sic->outlen))
+ return -EFAULT;
+
+ /*
+ * We do not transfer more than MAX_BUF with this interface.
+ * If the user needs to transfer more data than this, they
+ * should use scsi_generics (sg) instead.
+ */
+ if (inlen > MAX_BUF)
+ return -EINVAL;
+ if (outlen > MAX_BUF)
+ return -EINVAL;
+
+ cmd_in = sic->data;
+ if(get_user(opcode, cmd_in))
+ return -EFAULT;
+
+ needed = buf_needed = (inlen > outlen ? inlen : outlen);
+ if (buf_needed) {
+ buf_needed = (buf_needed + 511) & ~511;
+ if (buf_needed > MAX_BUF)
+ buf_needed = MAX_BUF;
+ buf = kmalloc(buf_needed, gfp_mask);
+ if (!buf)
+ return -ENOMEM;
+ memset(buf, 0, buf_needed);
+ if (inlen == 0) {
+ data_direction = DMA_FROM_DEVICE;
+ } else if (outlen == 0 ) {
+ data_direction = DMA_TO_DEVICE;
+ } else {
+ /*
+ * Can this ever happen?
+ */
+ data_direction = DMA_BIDIRECTIONAL;
+ }
+
+ } else {
+ buf = NULL;
+ data_direction = DMA_NONE;
+ }
+
+ /*
+ * Obtain the command from the user's address space.
+ */
+ cmdlen = COMMAND_SIZE(opcode);
+
+ result = -EFAULT;
+
+ if (!access_ok(VERIFY_READ, cmd_in, cmdlen + inlen))
+ goto error;
+
+ if(__copy_from_user(cmd, cmd_in, cmdlen))
+ goto error;
+
+ /*
+ * Obtain the data to be sent to the device (if any).
+ */
+
+ if(copy_from_user(buf, cmd_in + cmdlen, inlen))
+ goto error;
+
+ switch (opcode) {
+ case SEND_DIAGNOSTIC:
+ case FORMAT_UNIT:
+ timeout = FORMAT_UNIT_TIMEOUT;
+ retries = 1;
+ break;
+ case START_STOP:
+ timeout = START_STOP_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case MOVE_MEDIUM:
+ timeout = MOVE_MEDIUM_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case READ_ELEMENT_STATUS:
+ timeout = READ_ELEMENT_STATUS_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case READ_DEFECT_DATA:
+ timeout = READ_DEFECT_DATA_TIMEOUT;
+ retries = 1;
+ break;
+ default:
+ timeout = IOCTL_NORMAL_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ }
+
+ sreq = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!sreq) {
+ result = -EINTR;
+ goto error;
+ }
+
+ sreq->sr_data_direction = data_direction;
+ scsi_wait_req(sreq, cmd, buf, needed, timeout, retries);
+
+ /*
+ * If there was an error condition, pass the info back to the user.
+ */
+ result = sreq->sr_result;
+ if (result) {
+ int sb_len = sizeof(sreq->sr_sense_buffer);
+
+ sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
+ if (copy_to_user(cmd_in, sreq->sr_sense_buffer, sb_len))
+ result = -EFAULT;
+ } else {
+ if (copy_to_user(cmd_in, buf, outlen))
+ result = -EFAULT;
+ }
+
+ scsi_release_request(sreq);
+error:
+ kfree(buf);
+ return result;
+}
+EXPORT_SYMBOL(scsi_ioctl_send_command);
+
+/*
+ * The scsi_ioctl_get_pci() function places into arg the value
+ * pci_dev::slot_name (8 characters) for the PCI device (if any).
+ * Returns: 0 on success
+ * -ENXIO if there isn't a PCI device pointer
+ * (could be because the SCSI driver hasn't been
+ * updated yet, or because it isn't a SCSI
+ * device)
+ * any copy_to_user() error on failure there
+ */
+static int scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg)
+{
+ struct device *dev = scsi_get_device(sdev->host);
+
+ if (!dev)
+ return -ENXIO;
+ return copy_to_user(arg, dev->bus_id, sizeof(dev->bus_id))? -EFAULT: 0;
+}
+
+
+/*
+ * the scsi_ioctl() function differs from most ioctls in that it does
+ * not take a major/minor number as the dev field. Rather, it takes
+ * a pointer to a scsi_devices[] element, a structure.
+ */
+int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+{
+ char scsi_cmd[MAX_COMMAND_SIZE];
+
+ /* No idea how this happens.... */
+ if (!sdev)
+ return -ENXIO;
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if (!scsi_block_when_processing_errors(sdev))
+ return -ENODEV;
+
+ /* Check for deprecated ioctls ... all the ioctls which don't
+ * follow the new unique numbering scheme are deprecated */
+ switch (cmd) {
+ case SCSI_IOCTL_SEND_COMMAND:
+ case SCSI_IOCTL_TEST_UNIT_READY:
+ case SCSI_IOCTL_BENCHMARK_COMMAND:
+ case SCSI_IOCTL_SYNC:
+ case SCSI_IOCTL_START_UNIT:
+ case SCSI_IOCTL_STOP_UNIT:
+ printk(KERN_WARNING "program %s is using a deprecated SCSI "
+ "ioctl, please convert it to SG_IO\n", current->comm);
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case SCSI_IOCTL_GET_IDLUN:
+ if (!access_ok(VERIFY_WRITE, arg, sizeof(struct scsi_idlun)))
+ return -EFAULT;
+
+ __put_user((sdev->id & 0xff)
+ + ((sdev->lun & 0xff) << 8)
+ + ((sdev->channel & 0xff) << 16)
+ + ((sdev->host->host_no & 0xff) << 24),
+ &((struct scsi_idlun __user *)arg)->dev_id);
+ __put_user(sdev->host->unique_id,
+ &((struct scsi_idlun __user *)arg)->host_unique_id);
+ return 0;
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ return put_user(sdev->host->host_no, (int __user *)arg);
+ case SCSI_IOCTL_PROBE_HOST:
+ return ioctl_probe(sdev->host, arg);
+ case SCSI_IOCTL_SEND_COMMAND:
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return scsi_ioctl_send_command(sdev, arg);
+ case SCSI_IOCTL_DOORLOCK:
+ return scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
+ case SCSI_IOCTL_DOORUNLOCK:
+ return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
+ case SCSI_IOCTL_TEST_UNIT_READY:
+ return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT,
+ NORMAL_RETRIES);
+ case SCSI_IOCTL_START_UNIT:
+ scsi_cmd[0] = START_STOP;
+ scsi_cmd[1] = 0;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 1;
+ return ioctl_internal_command(sdev, scsi_cmd,
+ START_STOP_TIMEOUT, NORMAL_RETRIES);
+ case SCSI_IOCTL_STOP_UNIT:
+ scsi_cmd[0] = START_STOP;
+ scsi_cmd[1] = 0;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 0;
+ return ioctl_internal_command(sdev, scsi_cmd,
+ START_STOP_TIMEOUT, NORMAL_RETRIES);
+ case SCSI_IOCTL_GET_PCI:
+ return scsi_ioctl_get_pci(sdev, arg);
+ default:
+ if (sdev->host->hostt->ioctl)
+ return sdev->host->hostt->ioctl(sdev, cmd, arg);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(scsi_ioctl);
+
+/*
+ * the scsi_nonblock_ioctl() function is designed for ioctls which may
+ * be executed even if the device is in recovery.
+ */
+int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
+ void __user *arg, struct file *filp)
+{
+ int val, result;
+
+ /* The first set of iocts may be executed even if we're doing
+ * error processing, as long as the device was opened
+ * non-blocking */
+ if (filp && filp->f_flags & O_NONBLOCK) {
+ if (test_bit(SHOST_RECOVERY,
+ &sdev->host->shost_state))
+ return -ENODEV;
+ } else if (!scsi_block_when_processing_errors(sdev))
+ return -ENODEV;
+
+ switch (cmd) {
+ case SG_SCSI_RESET:
+ result = get_user(val, (int __user *)arg);
+ if (result)
+ return result;
+ if (val == SG_SCSI_RESET_NOTHING)
+ return 0;
+ switch (val) {
+ case SG_SCSI_RESET_DEVICE:
+ val = SCSI_TRY_RESET_DEVICE;
+ break;
+ case SG_SCSI_RESET_BUS:
+ val = SCSI_TRY_RESET_BUS;
+ break;
+ case SG_SCSI_RESET_HOST:
+ val = SCSI_TRY_RESET_HOST;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return (scsi_reset_provider(sdev, val) ==
+ SUCCESS) ? 0 : -EIO;
+ }
+ return -ENODEV;
+}
+EXPORT_SYMBOL(scsi_nonblockable_ioctl);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
new file mode 100644
index 000000000000..7cbc4127fb5a
--- /dev/null
+++ b/drivers/scsi/scsi_lib.c
@@ -0,0 +1,2023 @@
+/*
+ * scsi_lib.c Copyright (C) 1999 Eric Youngdale
+ *
+ * SCSI queueing library.
+ * Initial versions: Eric Youngdale (eric@andante.org).
+ * Based upon conversations with large numbers
+ * of people at Linux Expo.
+ */
+
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/mempool.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_request.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+
+#define SG_MEMPOOL_NR (sizeof(scsi_sg_pools)/sizeof(struct scsi_host_sg_pool))
+#define SG_MEMPOOL_SIZE 32
+
+struct scsi_host_sg_pool {
+ size_t size;
+ char *name;
+ kmem_cache_t *slab;
+ mempool_t *pool;
+};
+
+#if (SCSI_MAX_PHYS_SEGMENTS < 32)
+#error SCSI_MAX_PHYS_SEGMENTS is too small
+#endif
+
+#define SP(x) { x, "sgpool-" #x }
+struct scsi_host_sg_pool scsi_sg_pools[] = {
+ SP(8),
+ SP(16),
+ SP(32),
+#if (SCSI_MAX_PHYS_SEGMENTS > 32)
+ SP(64),
+#if (SCSI_MAX_PHYS_SEGMENTS > 64)
+ SP(128),
+#if (SCSI_MAX_PHYS_SEGMENTS > 128)
+ SP(256),
+#if (SCSI_MAX_PHYS_SEGMENTS > 256)
+#error SCSI_MAX_PHYS_SEGMENTS is too large
+#endif
+#endif
+#endif
+#endif
+};
+#undef SP
+
+
+/*
+ * Function: scsi_insert_special_req()
+ *
+ * Purpose: Insert pre-formed request into request queue.
+ *
+ * Arguments: sreq - request that is ready to be queued.
+ * at_head - boolean. True if we should insert at head
+ * of queue, false if we should insert at tail.
+ *
+ * Lock status: Assumed that lock is not held upon entry.
+ *
+ * Returns: Nothing
+ *
+ * Notes: This function is called from character device and from
+ * ioctl types of functions where the caller knows exactly
+ * what SCSI command needs to be issued. The idea is that
+ * we merely inject the command into the queue (at the head
+ * for now), and then call the queue request function to actually
+ * process it.
+ */
+int scsi_insert_special_req(struct scsi_request *sreq, int at_head)
+{
+ /*
+ * Because users of this function are apt to reuse requests with no
+ * modification, we have to sanitise the request flags here
+ */
+ sreq->sr_request->flags &= ~REQ_DONTPREP;
+ blk_insert_request(sreq->sr_device->request_queue, sreq->sr_request,
+ at_head, sreq, 0);
+ return 0;
+}
+
+/*
+ * Function: scsi_queue_insert()
+ *
+ * Purpose: Insert a command in the midlevel queue.
+ *
+ * Arguments: cmd - command that we are adding to queue.
+ * reason - why we are inserting command to queue.
+ *
+ * Lock status: Assumed that lock is not held upon entry.
+ *
+ * Returns: Nothing.
+ *
+ * Notes: We do this for one of two cases. Either the host is busy
+ * and it cannot accept any more commands for the time being,
+ * or the device returned QUEUE_FULL and can accept no more
+ * commands.
+ * Notes: This could be called either from an interrupt context or a
+ * normal process context.
+ */
+int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct scsi_device *device = cmd->device;
+
+ SCSI_LOG_MLQUEUE(1,
+ printk("Inserting command %p into mlqueue\n", cmd));
+
+ /*
+ * We are inserting the command into the ml queue. First, we
+ * cancel the timer, so it doesn't time out.
+ */
+ scsi_delete_timer(cmd);
+
+ /*
+ * Next, set the appropriate busy bit for the device/host.
+ *
+ * If the host/device isn't busy, assume that something actually
+ * completed, and that we should be able to queue a command now.
+ *
+ * Note that the prior mid-layer assumption that any host could
+ * always queue at least one command is now broken. The mid-layer
+ * will implement a user specifiable stall (see
+ * scsi_host.max_host_blocked and scsi_device.max_device_blocked)
+ * if a command is requeued with no other commands outstanding
+ * either for the device or for the host.
+ */
+ if (reason == SCSI_MLQUEUE_HOST_BUSY)
+ host->host_blocked = host->max_host_blocked;
+ else if (reason == SCSI_MLQUEUE_DEVICE_BUSY)
+ device->device_blocked = device->max_device_blocked;
+
+ /*
+ * Register the fact that we own the thing for now.
+ */
+ cmd->state = SCSI_STATE_MLQUEUE;
+ cmd->owner = SCSI_OWNER_MIDLEVEL;
+
+ /*
+ * Decrement the counters, since these commands are no longer
+ * active on the host/device.
+ */
+ scsi_device_unbusy(device);
+
+ /*
+ * Insert this command at the head of the queue for it's device.
+ * It will go before all other commands that are already in the queue.
+ *
+ * NOTE: there is magic here about the way the queue is plugged if
+ * we have no outstanding commands.
+ *
+ * Although this *doesn't* plug the queue, it does call the request
+ * function. The SCSI request function detects the blocked condition
+ * and plugs the queue appropriately.
+ */
+ blk_insert_request(device->request_queue, cmd->request, 1, cmd, 1);
+ return 0;
+}
+
+/*
+ * Function: scsi_do_req
+ *
+ * Purpose: Queue a SCSI request
+ *
+ * Arguments: sreq - command descriptor.
+ * cmnd - actual SCSI command to be performed.
+ * buffer - data buffer.
+ * bufflen - size of data buffer.
+ * done - completion function to be run.
+ * timeout - how long to let it run before timeout.
+ * retries - number of retries we allow.
+ *
+ * Lock status: No locks held upon entry.
+ *
+ * Returns: Nothing.
+ *
+ * Notes: This function is only used for queueing requests for things
+ * like ioctls and character device requests - this is because
+ * we essentially just inject a request into the queue for the
+ * device.
+ *
+ * In order to support the scsi_device_quiesce function, we
+ * now inject requests on the *head* of the device queue
+ * rather than the tail.
+ */
+void scsi_do_req(struct scsi_request *sreq, const void *cmnd,
+ void *buffer, unsigned bufflen,
+ void (*done)(struct scsi_cmnd *),
+ int timeout, int retries)
+{
+ /*
+ * If the upper level driver is reusing these things, then
+ * we should release the low-level block now. Another one will
+ * be allocated later when this request is getting queued.
+ */
+ __scsi_release_request(sreq);
+
+ /*
+ * Our own function scsi_done (which marks the host as not busy,
+ * disables the timeout counter, etc) will be called by us or by the
+ * scsi_hosts[host].queuecommand() function needs to also call
+ * the completion function for the high level driver.
+ */
+ memcpy(sreq->sr_cmnd, cmnd, sizeof(sreq->sr_cmnd));
+ sreq->sr_bufflen = bufflen;
+ sreq->sr_buffer = buffer;
+ sreq->sr_allowed = retries;
+ sreq->sr_done = done;
+ sreq->sr_timeout_per_command = timeout;
+
+ if (sreq->sr_cmd_len == 0)
+ sreq->sr_cmd_len = COMMAND_SIZE(sreq->sr_cmnd[0]);
+
+ /*
+ * head injection *required* here otherwise quiesce won't work
+ */
+ scsi_insert_special_req(sreq, 1);
+}
+EXPORT_SYMBOL(scsi_do_req);
+
+static void scsi_wait_done(struct scsi_cmnd *cmd)
+{
+ struct request *req = cmd->request;
+ struct request_queue *q = cmd->device->request_queue;
+ unsigned long flags;
+
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ if (blk_rq_tagged(req))
+ blk_queue_end_tag(q, req);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (req->waiting)
+ complete(req->waiting);
+}
+
+/* This is the end routine we get to if a command was never attached
+ * to the request. Simply complete the request without changing
+ * rq_status; this will cause a DRIVER_ERROR. */
+static void scsi_wait_req_end_io(struct request *req)
+{
+ BUG_ON(!req->waiting);
+
+ complete(req->waiting);
+}
+
+void scsi_wait_req(struct scsi_request *sreq, const void *cmnd, void *buffer,
+ unsigned bufflen, int timeout, int retries)
+{
+ DECLARE_COMPLETION(wait);
+
+ sreq->sr_request->waiting = &wait;
+ sreq->sr_request->rq_status = RQ_SCSI_BUSY;
+ sreq->sr_request->end_io = scsi_wait_req_end_io;
+ scsi_do_req(sreq, cmnd, buffer, bufflen, scsi_wait_done,
+ timeout, retries);
+ wait_for_completion(&wait);
+ sreq->sr_request->waiting = NULL;
+ if (sreq->sr_request->rq_status != RQ_SCSI_DONE)
+ sreq->sr_result |= (DRIVER_ERROR << 24);
+
+ __scsi_release_request(sreq);
+}
+EXPORT_SYMBOL(scsi_wait_req);
+
+/*
+ * Function: scsi_init_cmd_errh()
+ *
+ * Purpose: Initialize cmd fields related to error handling.
+ *
+ * Arguments: cmd - command that is ready to be queued.
+ *
+ * Returns: Nothing
+ *
+ * Notes: This function has the job of initializing a number of
+ * fields related to error handling. Typically this will
+ * be called once for each command, as required.
+ */
+static int scsi_init_cmd_errh(struct scsi_cmnd *cmd)
+{
+ cmd->owner = SCSI_OWNER_MIDLEVEL;
+ cmd->serial_number = 0;
+ cmd->serial_number_at_timeout = 0;
+ cmd->abort_reason = 0;
+
+ memset(cmd->sense_buffer, 0, sizeof cmd->sense_buffer);
+
+ if (cmd->cmd_len == 0)
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+
+ /*
+ * We need saved copies of a number of fields - this is because
+ * error handling may need to overwrite these with different values
+ * to run different commands, and once error handling is complete,
+ * we will need to restore these values prior to running the actual
+ * command.
+ */
+ cmd->old_use_sg = cmd->use_sg;
+ cmd->old_cmd_len = cmd->cmd_len;
+ cmd->sc_old_data_direction = cmd->sc_data_direction;
+ cmd->old_underflow = cmd->underflow;
+ memcpy(cmd->data_cmnd, cmd->cmnd, sizeof(cmd->cmnd));
+ cmd->buffer = cmd->request_buffer;
+ cmd->bufflen = cmd->request_bufflen;
+ cmd->internal_timeout = NORMAL_TIMEOUT;
+ cmd->abort_reason = 0;
+
+ return 1;
+}
+
+/*
+ * Function: scsi_setup_cmd_retry()
+ *
+ * Purpose: Restore the command state for a retry
+ *
+ * Arguments: cmd - command to be restored
+ *
+ * Returns: Nothing
+ *
+ * Notes: Immediately prior to retrying a command, we need
+ * to restore certain fields that we saved above.
+ */
+void scsi_setup_cmd_retry(struct scsi_cmnd *cmd)
+{
+ memcpy(cmd->cmnd, cmd->data_cmnd, sizeof(cmd->data_cmnd));
+ cmd->request_buffer = cmd->buffer;
+ cmd->request_bufflen = cmd->bufflen;
+ cmd->use_sg = cmd->old_use_sg;
+ cmd->cmd_len = cmd->old_cmd_len;
+ cmd->sc_data_direction = cmd->sc_old_data_direction;
+ cmd->underflow = cmd->old_underflow;
+}
+
+void scsi_device_unbusy(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ shost->host_busy--;
+ if (unlikely(test_bit(SHOST_RECOVERY, &shost->shost_state) &&
+ shost->host_failed))
+ scsi_eh_wakeup(shost);
+ spin_unlock(shost->host_lock);
+ spin_lock(&sdev->sdev_lock);
+ sdev->device_busy--;
+ spin_unlock_irqrestore(&sdev->sdev_lock, flags);
+}
+
+/*
+ * Called for single_lun devices on IO completion. Clear starget_sdev_user,
+ * and call blk_run_queue for all the scsi_devices on the target -
+ * including current_sdev first.
+ *
+ * Called with *no* scsi locks held.
+ */
+static void scsi_single_lun_run(struct scsi_device *current_sdev)
+{
+ struct Scsi_Host *shost = current_sdev->host;
+ struct scsi_device *sdev, *tmp;
+ struct scsi_target *starget = scsi_target(current_sdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ starget->starget_sdev_user = NULL;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ /*
+ * Call blk_run_queue for all LUNs on the target, starting with
+ * current_sdev. We race with others (to set starget_sdev_user),
+ * but in most cases, we will be first. Ideally, each LU on the
+ * target would get some limited time or requests on the target.
+ */
+ blk_run_queue(current_sdev->request_queue);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (starget->starget_sdev_user)
+ goto out;
+ list_for_each_entry_safe(sdev, tmp, &starget->devices,
+ same_target_siblings) {
+ if (sdev == current_sdev)
+ continue;
+ if (scsi_device_get(sdev))
+ continue;
+
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ blk_run_queue(sdev->request_queue);
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ scsi_device_put(sdev);
+ }
+ out:
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+/*
+ * Function: scsi_run_queue()
+ *
+ * Purpose: Select a proper request queue to serve next
+ *
+ * Arguments: q - last request's queue
+ *
+ * Returns: Nothing
+ *
+ * Notes: The previous command was completely finished, start
+ * a new one if possible.
+ */
+static void scsi_run_queue(struct request_queue *q)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct Scsi_Host *shost = sdev->host;
+ unsigned long flags;
+
+ if (sdev->single_lun)
+ scsi_single_lun_run(sdev);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (!list_empty(&shost->starved_list) &&
+ !shost->host_blocked && !shost->host_self_blocked &&
+ !((shost->can_queue > 0) &&
+ (shost->host_busy >= shost->can_queue))) {
+ /*
+ * As long as shost is accepting commands and we have
+ * starved queues, call blk_run_queue. scsi_request_fn
+ * drops the queue_lock and can add us back to the
+ * starved_list.
+ *
+ * host_lock protects the starved_list and starved_entry.
+ * scsi_request_fn must get the host_lock before checking
+ * or modifying starved_list or starved_entry.
+ */
+ sdev = list_entry(shost->starved_list.next,
+ struct scsi_device, starved_entry);
+ list_del_init(&sdev->starved_entry);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ blk_run_queue(sdev->request_queue);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ if (unlikely(!list_empty(&sdev->starved_entry)))
+ /*
+ * sdev lost a race, and was put back on the
+ * starved list. This is unlikely but without this
+ * in theory we could loop forever.
+ */
+ break;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ blk_run_queue(q);
+}
+
+/*
+ * Function: scsi_requeue_command()
+ *
+ * Purpose: Handle post-processing of completed commands.
+ *
+ * Arguments: q - queue to operate on
+ * cmd - command that may need to be requeued.
+ *
+ * Returns: Nothing
+ *
+ * Notes: After command completion, there may be blocks left
+ * over which weren't finished by the previous command
+ * this can be for a number of reasons - the main one is
+ * I/O errors in the middle of the request, in which case
+ * we need to request the blocks that come after the bad
+ * sector.
+ */
+static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd)
+{
+ cmd->request->flags &= ~REQ_DONTPREP;
+ blk_insert_request(q, cmd->request, 1, cmd, 1);
+
+ scsi_run_queue(q);
+}
+
+void scsi_next_command(struct scsi_cmnd *cmd)
+{
+ struct request_queue *q = cmd->device->request_queue;
+
+ scsi_put_command(cmd);
+ scsi_run_queue(q);
+}
+
+void scsi_run_host_queues(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, shost)
+ scsi_run_queue(sdev->request_queue);
+}
+
+/*
+ * Function: scsi_end_request()
+ *
+ * Purpose: Post-processing of completed commands (usually invoked at end
+ * of upper level post-processing and scsi_io_completion).
+ *
+ * Arguments: cmd - command that is complete.
+ * uptodate - 1 if I/O indicates success, <= 0 for I/O error.
+ * bytes - number of bytes of completed I/O
+ * requeue - indicates whether we should requeue leftovers.
+ *
+ * Lock status: Assumed that lock is not held upon entry.
+ *
+ * Returns: cmd if requeue done or required, NULL otherwise
+ *
+ * Notes: This is called for block device requests in order to
+ * mark some number of sectors as complete.
+ *
+ * We are guaranteeing that the request queue will be goosed
+ * at some point during this call.
+ */
+static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate,
+ int bytes, int requeue)
+{
+ request_queue_t *q = cmd->device->request_queue;
+ struct request *req = cmd->request;
+ unsigned long flags;
+
+ /*
+ * If there are blocks left over at the end, set up the command
+ * to queue the remainder of them.
+ */
+ if (end_that_request_chunk(req, uptodate, bytes)) {
+ int leftover = (req->hard_nr_sectors << 9);
+
+ if (blk_pc_request(req))
+ leftover = req->data_len;
+
+ /* kill remainder if no retrys */
+ if (!uptodate && blk_noretry_request(req))
+ end_that_request_chunk(req, 0, leftover);
+ else {
+ if (requeue)
+ /*
+ * Bleah. Leftovers again. Stick the
+ * leftovers in the front of the
+ * queue, and goose the queue again.
+ */
+ scsi_requeue_command(q, cmd);
+
+ return cmd;
+ }
+ }
+
+ add_disk_randomness(req->rq_disk);
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ if (blk_rq_tagged(req))
+ blk_queue_end_tag(q, req);
+ end_that_request_last(req);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ /*
+ * This will goose the queue request function at the end, so we don't
+ * need to worry about launching another command.
+ */
+ scsi_next_command(cmd);
+ return NULL;
+}
+
+static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, int gfp_mask)
+{
+ struct scsi_host_sg_pool *sgp;
+ struct scatterlist *sgl;
+
+ BUG_ON(!cmd->use_sg);
+
+ switch (cmd->use_sg) {
+ case 1 ... 8:
+ cmd->sglist_len = 0;
+ break;
+ case 9 ... 16:
+ cmd->sglist_len = 1;
+ break;
+ case 17 ... 32:
+ cmd->sglist_len = 2;
+ break;
+#if (SCSI_MAX_PHYS_SEGMENTS > 32)
+ case 33 ... 64:
+ cmd->sglist_len = 3;
+ break;
+#if (SCSI_MAX_PHYS_SEGMENTS > 64)
+ case 65 ... 128:
+ cmd->sglist_len = 4;
+ break;
+#if (SCSI_MAX_PHYS_SEGMENTS > 128)
+ case 129 ... 256:
+ cmd->sglist_len = 5;
+ break;
+#endif
+#endif
+#endif
+ default:
+ return NULL;
+ }
+
+ sgp = scsi_sg_pools + cmd->sglist_len;
+ sgl = mempool_alloc(sgp->pool, gfp_mask);
+ if (sgl)
+ memset(sgl, 0, sgp->size);
+ return sgl;
+}
+
+static void scsi_free_sgtable(struct scatterlist *sgl, int index)
+{
+ struct scsi_host_sg_pool *sgp;
+
+ BUG_ON(index > SG_MEMPOOL_NR);
+
+ sgp = scsi_sg_pools + index;
+ mempool_free(sgl, sgp->pool);
+}
+
+/*
+ * Function: scsi_release_buffers()
+ *
+ * Purpose: Completion processing for block device I/O requests.
+ *
+ * Arguments: cmd - command that we are bailing.
+ *
+ * Lock status: Assumed that no lock is held upon entry.
+ *
+ * Returns: Nothing
+ *
+ * Notes: In the event that an upper level driver rejects a
+ * command, we must release resources allocated during
+ * the __init_io() function. Primarily this would involve
+ * the scatter-gather table, and potentially any bounce
+ * buffers.
+ */
+static void scsi_release_buffers(struct scsi_cmnd *cmd)
+{
+ struct request *req = cmd->request;
+
+ /*
+ * Free up any indirection buffers we allocated for DMA purposes.
+ */
+ if (cmd->use_sg)
+ scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
+ else if (cmd->request_buffer != req->buffer)
+ kfree(cmd->request_buffer);
+
+ /*
+ * Zero these out. They now point to freed memory, and it is
+ * dangerous to hang onto the pointers.
+ */
+ cmd->buffer = NULL;
+ cmd->bufflen = 0;
+ cmd->request_buffer = NULL;
+ cmd->request_bufflen = 0;
+}
+
+/*
+ * Function: scsi_io_completion()
+ *
+ * Purpose: Completion processing for block device I/O requests.
+ *
+ * Arguments: cmd - command that is finished.
+ *
+ * Lock status: Assumed that no lock is held upon entry.
+ *
+ * Returns: Nothing
+ *
+ * Notes: This function is matched in terms of capabilities to
+ * the function that created the scatter-gather list.
+ * In other words, if there are no bounce buffers
+ * (the normal case for most drivers), we don't need
+ * the logic to deal with cleaning up afterwards.
+ *
+ * We must do one of several things here:
+ *
+ * a) Call scsi_end_request. This will finish off the
+ * specified number of sectors. If we are done, the
+ * command block will be released, and the queue
+ * function will be goosed. If we are not done, then
+ * scsi_end_request will directly goose the queue.
+ *
+ * b) We can just use scsi_requeue_command() here. This would
+ * be used if we just wanted to retry, for example.
+ */
+void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
+ unsigned int block_bytes)
+{
+ int result = cmd->result;
+ int this_count = cmd->bufflen;
+ request_queue_t *q = cmd->device->request_queue;
+ struct request *req = cmd->request;
+ int clear_errors = 1;
+ struct scsi_sense_hdr sshdr;
+ int sense_valid = 0;
+ int sense_deferred = 0;
+
+ if (blk_complete_barrier_rq(q, req, good_bytes >> 9))
+ return;
+
+ /*
+ * Free up any indirection buffers we allocated for DMA purposes.
+ * For the case of a READ, we need to copy the data out of the
+ * bounce buffer and into the real buffer.
+ */
+ if (cmd->use_sg)
+ scsi_free_sgtable(cmd->buffer, cmd->sglist_len);
+ else if (cmd->buffer != req->buffer) {
+ if (rq_data_dir(req) == READ) {
+ unsigned long flags;
+ char *to = bio_kmap_irq(req->bio, &flags);
+ memcpy(to, cmd->buffer, cmd->bufflen);
+ bio_kunmap_irq(to, &flags);
+ }
+ kfree(cmd->buffer);
+ }
+
+ if (result) {
+ sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
+ if (sense_valid)
+ sense_deferred = scsi_sense_is_deferred(&sshdr);
+ }
+ if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
+ req->errors = result;
+ if (result) {
+ clear_errors = 0;
+ if (sense_valid && req->sense) {
+ /*
+ * SG_IO wants current and deferred errors
+ */
+ int len = 8 + cmd->sense_buffer[7];
+
+ if (len > SCSI_SENSE_BUFFERSIZE)
+ len = SCSI_SENSE_BUFFERSIZE;
+ memcpy(req->sense, cmd->sense_buffer, len);
+ req->sense_len = len;
+ }
+ } else
+ req->data_len = cmd->resid;
+ }
+
+ /*
+ * Zero these out. They now point to freed memory, and it is
+ * dangerous to hang onto the pointers.
+ */
+ cmd->buffer = NULL;
+ cmd->bufflen = 0;
+ cmd->request_buffer = NULL;
+ cmd->request_bufflen = 0;
+
+ /*
+ * Next deal with any sectors which we were able to correctly
+ * handle.
+ */
+ if (good_bytes >= 0) {
+ SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d bytes done.\n",
+ req->nr_sectors, good_bytes));
+ SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg));
+
+ if (clear_errors)
+ req->errors = 0;
+ /*
+ * If multiple sectors are requested in one buffer, then
+ * they will have been finished off by the first command.
+ * If not, then we have a multi-buffer command.
+ *
+ * If block_bytes != 0, it means we had a medium error
+ * of some sort, and that we want to mark some number of
+ * sectors as not uptodate. Thus we want to inhibit
+ * requeueing right here - we will requeue down below
+ * when we handle the bad sectors.
+ */
+ cmd = scsi_end_request(cmd, 1, good_bytes, result == 0);
+
+ /*
+ * If the command completed without error, then either finish off the
+ * rest of the command, or start a new one.
+ */
+ if (result == 0 || cmd == NULL ) {
+ return;
+ }
+ }
+ /*
+ * Now, if we were good little boys and girls, Santa left us a request
+ * sense buffer. We can extract information from this, so we
+ * can choose a block to remap, etc.
+ */
+ if (sense_valid && !sense_deferred) {
+ switch (sshdr.sense_key) {
+ case UNIT_ATTENTION:
+ if (cmd->device->removable) {
+ /* detected disc change. set a bit
+ * and quietly refuse further access.
+ */
+ cmd->device->changed = 1;
+ cmd = scsi_end_request(cmd, 0,
+ this_count, 1);
+ return;
+ } else {
+ /*
+ * Must have been a power glitch, or a
+ * bus reset. Could not have been a
+ * media change, so we just retry the
+ * request and see what happens.
+ */
+ scsi_requeue_command(q, cmd);
+ return;
+ }
+ break;
+ case ILLEGAL_REQUEST:
+ /*
+ * If we had an ILLEGAL REQUEST returned, then we may
+ * have performed an unsupported command. The only
+ * thing this should be would be a ten byte read where
+ * only a six byte read was supported. Also, on a
+ * system where READ CAPACITY failed, we may have read
+ * past the end of the disk.
+ */
+ if (cmd->device->use_10_for_rw &&
+ (cmd->cmnd[0] == READ_10 ||
+ cmd->cmnd[0] == WRITE_10)) {
+ cmd->device->use_10_for_rw = 0;
+ /*
+ * This will cause a retry with a 6-byte
+ * command.
+ */
+ scsi_requeue_command(q, cmd);
+ result = 0;
+ } else {
+ cmd = scsi_end_request(cmd, 0, this_count, 1);
+ return;
+ }
+ break;
+ case NOT_READY:
+ /*
+ * If the device is in the process of becoming ready,
+ * retry.
+ */
+ if (sshdr.asc == 0x04 && sshdr.ascq == 0x01) {
+ scsi_requeue_command(q, cmd);
+ return;
+ }
+ printk(KERN_INFO "Device %s not ready.\n",
+ req->rq_disk ? req->rq_disk->disk_name : "");
+ cmd = scsi_end_request(cmd, 0, this_count, 1);
+ return;
+ case VOLUME_OVERFLOW:
+ printk(KERN_INFO "Volume overflow <%d %d %d %d> CDB: ",
+ cmd->device->host->host_no,
+ (int)cmd->device->channel,
+ (int)cmd->device->id, (int)cmd->device->lun);
+ __scsi_print_command(cmd->data_cmnd);
+ scsi_print_sense("", cmd);
+ cmd = scsi_end_request(cmd, 0, block_bytes, 1);
+ return;
+ default:
+ break;
+ }
+ } /* driver byte != 0 */
+ if (host_byte(result) == DID_RESET) {
+ /*
+ * Third party bus reset or reset for error
+ * recovery reasons. Just retry the request
+ * and see what happens.
+ */
+ scsi_requeue_command(q, cmd);
+ return;
+ }
+ if (result) {
+ printk(KERN_INFO "SCSI error : <%d %d %d %d> return code "
+ "= 0x%x\n", cmd->device->host->host_no,
+ cmd->device->channel,
+ cmd->device->id,
+ cmd->device->lun, result);
+
+ if (driver_byte(result) & DRIVER_SENSE)
+ scsi_print_sense("", cmd);
+ /*
+ * Mark a single buffer as not uptodate. Queue the remainder.
+ * We sometimes get this cruft in the event that a medium error
+ * isn't properly reported.
+ */
+ block_bytes = req->hard_cur_sectors << 9;
+ if (!block_bytes)
+ block_bytes = req->data_len;
+ cmd = scsi_end_request(cmd, 0, block_bytes, 1);
+ }
+}
+EXPORT_SYMBOL(scsi_io_completion);
+
+/*
+ * Function: scsi_init_io()
+ *
+ * Purpose: SCSI I/O initialize function.
+ *
+ * Arguments: cmd - Command descriptor we wish to initialize
+ *
+ * Returns: 0 on success
+ * BLKPREP_DEFER if the failure is retryable
+ * BLKPREP_KILL if the failure is fatal
+ */
+static int scsi_init_io(struct scsi_cmnd *cmd)
+{
+ struct request *req = cmd->request;
+ struct scatterlist *sgpnt;
+ int count;
+
+ /*
+ * if this is a rq->data based REQ_BLOCK_PC, setup for a non-sg xfer
+ */
+ if ((req->flags & REQ_BLOCK_PC) && !req->bio) {
+ cmd->request_bufflen = req->data_len;
+ cmd->request_buffer = req->data;
+ req->buffer = req->data;
+ cmd->use_sg = 0;
+ return 0;
+ }
+
+ /*
+ * we used to not use scatter-gather for single segment request,
+ * but now we do (it makes highmem I/O easier to support without
+ * kmapping pages)
+ */
+ cmd->use_sg = req->nr_phys_segments;
+
+ /*
+ * if sg table allocation fails, requeue request later.
+ */
+ sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC);
+ if (unlikely(!sgpnt)) {
+ req->flags |= REQ_SPECIAL;
+ return BLKPREP_DEFER;
+ }
+
+ cmd->request_buffer = (char *) sgpnt;
+ cmd->request_bufflen = req->nr_sectors << 9;
+ if (blk_pc_request(req))
+ cmd->request_bufflen = req->data_len;
+ req->buffer = NULL;
+
+ /*
+ * Next, walk the list, and fill in the addresses and sizes of
+ * each segment.
+ */
+ count = blk_rq_map_sg(req->q, req, cmd->request_buffer);
+
+ /*
+ * mapped well, send it off
+ */
+ if (likely(count <= cmd->use_sg)) {
+ cmd->use_sg = count;
+ return 0;
+ }
+
+ printk(KERN_ERR "Incorrect number of segments after building list\n");
+ printk(KERN_ERR "counted %d, received %d\n", count, cmd->use_sg);
+ printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors,
+ req->current_nr_sectors);
+
+ /* release the command and kill it */
+ scsi_release_buffers(cmd);
+ scsi_put_command(cmd);
+ return BLKPREP_KILL;
+}
+
+static int scsi_prepare_flush_fn(request_queue_t *q, struct request *rq)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct scsi_driver *drv;
+
+ if (sdev->sdev_state == SDEV_RUNNING) {
+ drv = *(struct scsi_driver **) rq->rq_disk->private_data;
+
+ if (drv->prepare_flush)
+ return drv->prepare_flush(q, rq);
+ }
+
+ return 0;
+}
+
+static void scsi_end_flush_fn(request_queue_t *q, struct request *rq)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct request *flush_rq = rq->end_io_data;
+ struct scsi_driver *drv;
+
+ if (flush_rq->errors) {
+ printk("scsi: barrier error, disabling flush support\n");
+ blk_queue_ordered(q, QUEUE_ORDERED_NONE);
+ }
+
+ if (sdev->sdev_state == SDEV_RUNNING) {
+ drv = *(struct scsi_driver **) rq->rq_disk->private_data;
+ drv->end_flush(q, rq);
+ }
+}
+
+static int scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk,
+ sector_t *error_sector)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct scsi_driver *drv;
+
+ if (sdev->sdev_state != SDEV_RUNNING)
+ return -ENXIO;
+
+ drv = *(struct scsi_driver **) disk->private_data;
+ if (drv->issue_flush)
+ return drv->issue_flush(&sdev->sdev_gendev, error_sector);
+
+ return -EOPNOTSUPP;
+}
+
+static int scsi_prep_fn(struct request_queue *q, struct request *req)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct scsi_cmnd *cmd;
+ int specials_only = 0;
+
+ /*
+ * Just check to see if the device is online. If it isn't, we
+ * refuse to process any commands. The device must be brought
+ * online before trying any recovery commands
+ */
+ if (unlikely(!scsi_device_online(sdev))) {
+ printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline device\n",
+ sdev->host->host_no, sdev->id, sdev->lun);
+ return BLKPREP_KILL;
+ }
+ if (unlikely(sdev->sdev_state != SDEV_RUNNING)) {
+ /* OK, we're not in a running state don't prep
+ * user commands */
+ if (sdev->sdev_state == SDEV_DEL) {
+ /* Device is fully deleted, no commands
+ * at all allowed down */
+ printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to dead device\n",
+ sdev->host->host_no, sdev->id, sdev->lun);
+ return BLKPREP_KILL;
+ }
+ /* OK, we only allow special commands (i.e. not
+ * user initiated ones */
+ specials_only = sdev->sdev_state;
+ }
+
+ /*
+ * Find the actual device driver associated with this command.
+ * The SPECIAL requests are things like character device or
+ * ioctls, which did not originate from ll_rw_blk. Note that
+ * the special field is also used to indicate the cmd for
+ * the remainder of a partially fulfilled request that can
+ * come up when there is a medium error. We have to treat
+ * these two cases differently. We differentiate by looking
+ * at request->cmd, as this tells us the real story.
+ */
+ if (req->flags & REQ_SPECIAL) {
+ struct scsi_request *sreq = req->special;
+
+ if (sreq->sr_magic == SCSI_REQ_MAGIC) {
+ cmd = scsi_get_command(sreq->sr_device, GFP_ATOMIC);
+ if (unlikely(!cmd))
+ goto defer;
+ scsi_init_cmd_from_req(cmd, sreq);
+ } else
+ cmd = req->special;
+ } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
+
+ if(unlikely(specials_only)) {
+ if(specials_only == SDEV_QUIESCE ||
+ specials_only == SDEV_BLOCK)
+ return BLKPREP_DEFER;
+
+ printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to device being removed\n",
+ sdev->host->host_no, sdev->id, sdev->lun);
+ return BLKPREP_KILL;
+ }
+
+
+ /*
+ * Now try and find a command block that we can use.
+ */
+ if (!req->special) {
+ cmd = scsi_get_command(sdev, GFP_ATOMIC);
+ if (unlikely(!cmd))
+ goto defer;
+ } else
+ cmd = req->special;
+
+ /* pull a tag out of the request if we have one */
+ cmd->tag = req->tag;
+ } else {
+ blk_dump_rq_flags(req, "SCSI bad req");
+ return BLKPREP_KILL;
+ }
+
+ /* note the overloading of req->special. When the tag
+ * is active it always means cmd. If the tag goes
+ * back for re-queueing, it may be reset */
+ req->special = cmd;
+ cmd->request = req;
+
+ /*
+ * FIXME: drop the lock here because the functions below
+ * expect to be called without the queue lock held. Also,
+ * previously, we dequeued the request before dropping the
+ * lock. We hope REQ_STARTED prevents anything untoward from
+ * happening now.
+ */
+ if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
+ struct scsi_driver *drv;
+ int ret;
+
+ /*
+ * This will do a couple of things:
+ * 1) Fill in the actual SCSI command.
+ * 2) Fill in any other upper-level specific fields
+ * (timeout).
+ *
+ * If this returns 0, it means that the request failed
+ * (reading past end of disk, reading offline device,
+ * etc). This won't actually talk to the device, but
+ * some kinds of consistency checking may cause the
+ * request to be rejected immediately.
+ */
+
+ /*
+ * This sets up the scatter-gather table (allocating if
+ * required).
+ */
+ ret = scsi_init_io(cmd);
+ if (ret) /* BLKPREP_KILL return also releases the command */
+ return ret;
+
+ /*
+ * Initialize the actual SCSI command for this request.
+ */
+ drv = *(struct scsi_driver **)req->rq_disk->private_data;
+ if (unlikely(!drv->init_command(cmd))) {
+ scsi_release_buffers(cmd);
+ scsi_put_command(cmd);
+ return BLKPREP_KILL;
+ }
+ }
+
+ /*
+ * The request is now prepped, no need to come back here
+ */
+ req->flags |= REQ_DONTPREP;
+ return BLKPREP_OK;
+
+ defer:
+ /* If we defer, the elv_next_request() returns NULL, but the
+ * queue must be restarted, so we plug here if no returning
+ * command will automatically do that. */
+ if (sdev->device_busy == 0)
+ blk_plug_device(q);
+ return BLKPREP_DEFER;
+}
+
+/*
+ * scsi_dev_queue_ready: if we can send requests to sdev, return 1 else
+ * return 0.
+ *
+ * Called with the queue_lock held.
+ */
+static inline int scsi_dev_queue_ready(struct request_queue *q,
+ struct scsi_device *sdev)
+{
+ if (sdev->device_busy >= sdev->queue_depth)
+ return 0;
+ if (sdev->device_busy == 0 && sdev->device_blocked) {
+ /*
+ * unblock after device_blocked iterates to zero
+ */
+ if (--sdev->device_blocked == 0) {
+ SCSI_LOG_MLQUEUE(3,
+ printk("scsi%d (%d:%d) unblocking device at"
+ " zero depth\n", sdev->host->host_no,
+ sdev->id, sdev->lun));
+ } else {
+ blk_plug_device(q);
+ return 0;
+ }
+ }
+ if (sdev->device_blocked)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * scsi_host_queue_ready: if we can send requests to shost, return 1 else
+ * return 0. We must end up running the queue again whenever 0 is
+ * returned, else IO can hang.
+ *
+ * Called with host_lock held.
+ */
+static inline int scsi_host_queue_ready(struct request_queue *q,
+ struct Scsi_Host *shost,
+ struct scsi_device *sdev)
+{
+ if (test_bit(SHOST_RECOVERY, &shost->shost_state))
+ return 0;
+ if (shost->host_busy == 0 && shost->host_blocked) {
+ /*
+ * unblock after host_blocked iterates to zero
+ */
+ if (--shost->host_blocked == 0) {
+ SCSI_LOG_MLQUEUE(3,
+ printk("scsi%d unblocking host at zero depth\n",
+ shost->host_no));
+ } else {
+ blk_plug_device(q);
+ return 0;
+ }
+ }
+ if ((shost->can_queue > 0 && shost->host_busy >= shost->can_queue) ||
+ shost->host_blocked || shost->host_self_blocked) {
+ if (list_empty(&sdev->starved_entry))
+ list_add_tail(&sdev->starved_entry, &shost->starved_list);
+ return 0;
+ }
+
+ /* We're OK to process the command, so we can't be starved */
+ if (!list_empty(&sdev->starved_entry))
+ list_del_init(&sdev->starved_entry);
+
+ return 1;
+}
+
+/*
+ * Kill requests for a dead device
+ */
+static void scsi_kill_requests(request_queue_t *q)
+{
+ struct request *req;
+
+ while ((req = elv_next_request(q)) != NULL) {
+ blkdev_dequeue_request(req);
+ req->flags |= REQ_QUIET;
+ while (end_that_request_first(req, 0, req->nr_sectors))
+ ;
+ end_that_request_last(req);
+ }
+}
+
+/*
+ * Function: scsi_request_fn()
+ *
+ * Purpose: Main strategy routine for SCSI.
+ *
+ * Arguments: q - Pointer to actual queue.
+ *
+ * Returns: Nothing
+ *
+ * Lock status: IO request lock assumed to be held when called.
+ */
+static void scsi_request_fn(struct request_queue *q)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct Scsi_Host *shost;
+ struct scsi_cmnd *cmd;
+ struct request *req;
+
+ if (!sdev) {
+ printk("scsi: killing requests for dead queue\n");
+ scsi_kill_requests(q);
+ return;
+ }
+
+ if(!get_device(&sdev->sdev_gendev))
+ /* We must be tearing the block queue down already */
+ return;
+
+ /*
+ * To start with, we keep looping until the queue is empty, or until
+ * the host is no longer able to accept any more requests.
+ */
+ shost = sdev->host;
+ while (!blk_queue_plugged(q)) {
+ int rtn;
+ /*
+ * get next queueable request. We do this early to make sure
+ * that the request is fully prepared even if we cannot
+ * accept it.
+ */
+ req = elv_next_request(q);
+ if (!req || !scsi_dev_queue_ready(q, sdev))
+ break;
+
+ if (unlikely(!scsi_device_online(sdev))) {
+ printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline device\n",
+ sdev->host->host_no, sdev->id, sdev->lun);
+ blkdev_dequeue_request(req);
+ req->flags |= REQ_QUIET;
+ while (end_that_request_first(req, 0, req->nr_sectors))
+ ;
+ end_that_request_last(req);
+ continue;
+ }
+
+
+ /*
+ * Remove the request from the request list.
+ */
+ if (!(blk_queue_tagged(q) && !blk_queue_start_tag(q, req)))
+ blkdev_dequeue_request(req);
+ sdev->device_busy++;
+
+ spin_unlock(q->queue_lock);
+ spin_lock(shost->host_lock);
+
+ if (!scsi_host_queue_ready(q, shost, sdev))
+ goto not_ready;
+ if (sdev->single_lun) {
+ if (scsi_target(sdev)->starget_sdev_user &&
+ scsi_target(sdev)->starget_sdev_user != sdev)
+ goto not_ready;
+ scsi_target(sdev)->starget_sdev_user = sdev;
+ }
+ shost->host_busy++;
+
+ /*
+ * XXX(hch): This is rather suboptimal, scsi_dispatch_cmd will
+ * take the lock again.
+ */
+ spin_unlock_irq(shost->host_lock);
+
+ cmd = req->special;
+ if (unlikely(cmd == NULL)) {
+ printk(KERN_CRIT "impossible request in %s.\n"
+ "please mail a stack trace to "
+ "linux-scsi@vger.kernel.org",
+ __FUNCTION__);
+ BUG();
+ }
+
+ /*
+ * Finally, initialize any error handling parameters, and set up
+ * the timers for timeouts.
+ */
+ scsi_init_cmd_errh(cmd);
+
+ /*
+ * Dispatch the command to the low-level driver.
+ */
+ rtn = scsi_dispatch_cmd(cmd);
+ spin_lock_irq(q->queue_lock);
+ if(rtn) {
+ /* we're refusing the command; because of
+ * the way locks get dropped, we need to
+ * check here if plugging is required */
+ if(sdev->device_busy == 0)
+ blk_plug_device(q);
+
+ break;
+ }
+ }
+
+ goto out;
+
+ not_ready:
+ spin_unlock_irq(shost->host_lock);
+
+ /*
+ * lock q, handle tag, requeue req, and decrement device_busy. We
+ * must return with queue_lock held.
+ *
+ * Decrementing device_busy without checking it is OK, as all such
+ * cases (host limits or settings) should run the queue at some
+ * later time.
+ */
+ spin_lock_irq(q->queue_lock);
+ blk_requeue_request(q, req);
+ sdev->device_busy--;
+ if(sdev->device_busy == 0)
+ blk_plug_device(q);
+ out:
+ /* must be careful here...if we trigger the ->remove() function
+ * we cannot be holding the q lock */
+ spin_unlock_irq(q->queue_lock);
+ put_device(&sdev->sdev_gendev);
+ spin_lock_irq(q->queue_lock);
+}
+
+u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost)
+{
+ struct device *host_dev;
+ u64 bounce_limit = 0xffffffff;
+
+ if (shost->unchecked_isa_dma)
+ return BLK_BOUNCE_ISA;
+ /*
+ * Platforms with virtual-DMA translation
+ * hardware have no practical limit.
+ */
+ if (!PCI_DMA_BUS_IS_PHYS)
+ return BLK_BOUNCE_ANY;
+
+ host_dev = scsi_get_device(shost);
+ if (host_dev && host_dev->dma_mask)
+ bounce_limit = *host_dev->dma_mask;
+
+ return bounce_limit;
+}
+EXPORT_SYMBOL(scsi_calculate_bounce_limit);
+
+struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+ struct request_queue *q;
+
+ q = blk_init_queue(scsi_request_fn, &sdev->sdev_lock);
+ if (!q)
+ return NULL;
+
+ blk_queue_prep_rq(q, scsi_prep_fn);
+
+ blk_queue_max_hw_segments(q, shost->sg_tablesize);
+ blk_queue_max_phys_segments(q, SCSI_MAX_PHYS_SEGMENTS);
+ blk_queue_max_sectors(q, shost->max_sectors);
+ blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));
+ blk_queue_segment_boundary(q, shost->dma_boundary);
+ blk_queue_issue_flush_fn(q, scsi_issue_flush_fn);
+
+ /*
+ * ordered tags are superior to flush ordering
+ */
+ if (shost->ordered_tag)
+ blk_queue_ordered(q, QUEUE_ORDERED_TAG);
+ else if (shost->ordered_flush) {
+ blk_queue_ordered(q, QUEUE_ORDERED_FLUSH);
+ q->prepare_flush_fn = scsi_prepare_flush_fn;
+ q->end_flush_fn = scsi_end_flush_fn;
+ }
+
+ if (!shost->use_clustering)
+ clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
+ return q;
+}
+
+void scsi_free_queue(struct request_queue *q)
+{
+ blk_cleanup_queue(q);
+}
+
+/*
+ * Function: scsi_block_requests()
+ *
+ * Purpose: Utility function used by low-level drivers to prevent further
+ * commands from being queued to the device.
+ *
+ * Arguments: shost - Host in question
+ *
+ * Returns: Nothing
+ *
+ * Lock status: No locks are assumed held.
+ *
+ * Notes: There is no timer nor any other means by which the requests
+ * get unblocked other than the low-level driver calling
+ * scsi_unblock_requests().
+ */
+void scsi_block_requests(struct Scsi_Host *shost)
+{
+ shost->host_self_blocked = 1;
+}
+EXPORT_SYMBOL(scsi_block_requests);
+
+/*
+ * Function: scsi_unblock_requests()
+ *
+ * Purpose: Utility function used by low-level drivers to allow further
+ * commands from being queued to the device.
+ *
+ * Arguments: shost - Host in question
+ *
+ * Returns: Nothing
+ *
+ * Lock status: No locks are assumed held.
+ *
+ * Notes: There is no timer nor any other means by which the requests
+ * get unblocked other than the low-level driver calling
+ * scsi_unblock_requests().
+ *
+ * This is done as an API function so that changes to the
+ * internals of the scsi mid-layer won't require wholesale
+ * changes to drivers that use this feature.
+ */
+void scsi_unblock_requests(struct Scsi_Host *shost)
+{
+ shost->host_self_blocked = 0;
+ scsi_run_host_queues(shost);
+}
+EXPORT_SYMBOL(scsi_unblock_requests);
+
+int __init scsi_init_queue(void)
+{
+ int i;
+
+ for (i = 0; i < SG_MEMPOOL_NR; i++) {
+ struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;
+ int size = sgp->size * sizeof(struct scatterlist);
+
+ sgp->slab = kmem_cache_create(sgp->name, size, 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!sgp->slab) {
+ printk(KERN_ERR "SCSI: can't init sg slab %s\n",
+ sgp->name);
+ }
+
+ sgp->pool = mempool_create(SG_MEMPOOL_SIZE,
+ mempool_alloc_slab, mempool_free_slab,
+ sgp->slab);
+ if (!sgp->pool) {
+ printk(KERN_ERR "SCSI: can't init sg mempool %s\n",
+ sgp->name);
+ }
+ }
+
+ return 0;
+}
+
+void scsi_exit_queue(void)
+{
+ int i;
+
+ for (i = 0; i < SG_MEMPOOL_NR; i++) {
+ struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;
+ mempool_destroy(sgp->pool);
+ kmem_cache_destroy(sgp->slab);
+ }
+}
+/**
+ * __scsi_mode_sense - issue a mode sense, falling back from 10 to
+ * six bytes if necessary.
+ * @sreq: SCSI request to fill in with the MODE_SENSE
+ * @dbd: set if mode sense will allow block descriptors to be returned
+ * @modepage: mode page being requested
+ * @buffer: request buffer (may not be smaller than eight bytes)
+ * @len: length of request buffer.
+ * @timeout: command timeout
+ * @retries: number of retries before failing
+ * @data: returns a structure abstracting the mode header data
+ *
+ * Returns zero if unsuccessful, or the header offset (either 4
+ * or 8 depending on whether a six or ten byte command was
+ * issued) if successful.
+ **/
+int
+__scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage,
+ unsigned char *buffer, int len, int timeout, int retries,
+ struct scsi_mode_data *data) {
+ unsigned char cmd[12];
+ int use_10_for_ms;
+ int header_length;
+
+ memset(data, 0, sizeof(*data));
+ memset(&cmd[0], 0, 12);
+ cmd[1] = dbd & 0x18; /* allows DBD and LLBA bits */
+ cmd[2] = modepage;
+
+ retry:
+ use_10_for_ms = sreq->sr_device->use_10_for_ms;
+
+ if (use_10_for_ms) {
+ if (len < 8)
+ len = 8;
+
+ cmd[0] = MODE_SENSE_10;
+ cmd[8] = len;
+ header_length = 8;
+ } else {
+ if (len < 4)
+ len = 4;
+
+ cmd[0] = MODE_SENSE;
+ cmd[4] = len;
+ header_length = 4;
+ }
+
+ sreq->sr_cmd_len = 0;
+ memset(sreq->sr_sense_buffer, 0, sizeof(sreq->sr_sense_buffer));
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+
+ memset(buffer, 0, len);
+
+ scsi_wait_req(sreq, cmd, buffer, len, timeout, retries);
+
+ /* This code looks awful: what it's doing is making sure an
+ * ILLEGAL REQUEST sense return identifies the actual command
+ * byte as the problem. MODE_SENSE commands can return
+ * ILLEGAL REQUEST if the code page isn't supported */
+
+ if (use_10_for_ms && !scsi_status_is_good(sreq->sr_result) &&
+ (driver_byte(sreq->sr_result) & DRIVER_SENSE)) {
+ struct scsi_sense_hdr sshdr;
+
+ if (scsi_request_normalize_sense(sreq, &sshdr)) {
+ if ((sshdr.sense_key == ILLEGAL_REQUEST) &&
+ (sshdr.asc == 0x20) && (sshdr.ascq == 0)) {
+ /*
+ * Invalid command operation code
+ */
+ sreq->sr_device->use_10_for_ms = 0;
+ goto retry;
+ }
+ }
+ }
+
+ if(scsi_status_is_good(sreq->sr_result)) {
+ data->header_length = header_length;
+ if(use_10_for_ms) {
+ data->length = buffer[0]*256 + buffer[1] + 2;
+ data->medium_type = buffer[2];
+ data->device_specific = buffer[3];
+ data->longlba = buffer[4] & 0x01;
+ data->block_descriptor_length = buffer[6]*256
+ + buffer[7];
+ } else {
+ data->length = buffer[0] + 1;
+ data->medium_type = buffer[1];
+ data->device_specific = buffer[2];
+ data->block_descriptor_length = buffer[3];
+ }
+ }
+
+ return sreq->sr_result;
+}
+EXPORT_SYMBOL(__scsi_mode_sense);
+
+/**
+ * scsi_mode_sense - issue a mode sense, falling back from 10 to
+ * six bytes if necessary.
+ * @sdev: scsi device to send command to.
+ * @dbd: set if mode sense will disable block descriptors in the return
+ * @modepage: mode page being requested
+ * @buffer: request buffer (may not be smaller than eight bytes)
+ * @len: length of request buffer.
+ * @timeout: command timeout
+ * @retries: number of retries before failing
+ *
+ * Returns zero if unsuccessful, or the header offset (either 4
+ * or 8 depending on whether a six or ten byte command was
+ * issued) if successful.
+ **/
+int
+scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
+ unsigned char *buffer, int len, int timeout, int retries,
+ struct scsi_mode_data *data)
+{
+ struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL);
+ int ret;
+
+ if (!sreq)
+ return -1;
+
+ ret = __scsi_mode_sense(sreq, dbd, modepage, buffer, len,
+ timeout, retries, data);
+
+ scsi_release_request(sreq);
+
+ return ret;
+}
+EXPORT_SYMBOL(scsi_mode_sense);
+
+int
+scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries)
+{
+ struct scsi_request *sreq;
+ char cmd[] = {
+ TEST_UNIT_READY, 0, 0, 0, 0, 0,
+ };
+ int result;
+
+ sreq = scsi_allocate_request(sdev, GFP_KERNEL);
+ if (!sreq)
+ return -ENOMEM;
+
+ sreq->sr_data_direction = DMA_NONE;
+ scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries);
+
+ if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && sdev->removable) {
+ struct scsi_sense_hdr sshdr;
+
+ if ((scsi_request_normalize_sense(sreq, &sshdr)) &&
+ ((sshdr.sense_key == UNIT_ATTENTION) ||
+ (sshdr.sense_key == NOT_READY))) {
+ sdev->changed = 1;
+ sreq->sr_result = 0;
+ }
+ }
+ result = sreq->sr_result;
+ scsi_release_request(sreq);
+ return result;
+}
+EXPORT_SYMBOL(scsi_test_unit_ready);
+
+/**
+ * scsi_device_set_state - Take the given device through the device
+ * state model.
+ * @sdev: scsi device to change the state of.
+ * @state: state to change to.
+ *
+ * Returns zero if unsuccessful or an error if the requested
+ * transition is illegal.
+ **/
+int
+scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
+{
+ enum scsi_device_state oldstate = sdev->sdev_state;
+
+ if (state == oldstate)
+ return 0;
+
+ switch (state) {
+ case SDEV_CREATED:
+ /* There are no legal states that come back to
+ * created. This is the manually initialised start
+ * state */
+ goto illegal;
+
+ case SDEV_RUNNING:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_OFFLINE:
+ case SDEV_QUIESCE:
+ case SDEV_BLOCK:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SDEV_QUIESCE:
+ switch (oldstate) {
+ case SDEV_RUNNING:
+ case SDEV_OFFLINE:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SDEV_OFFLINE:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_RUNNING:
+ case SDEV_QUIESCE:
+ case SDEV_BLOCK:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SDEV_BLOCK:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_RUNNING:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SDEV_CANCEL:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_RUNNING:
+ case SDEV_OFFLINE:
+ case SDEV_BLOCK:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ case SDEV_DEL:
+ switch (oldstate) {
+ case SDEV_CANCEL:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
+ }
+ sdev->sdev_state = state;
+ return 0;
+
+ illegal:
+ SCSI_LOG_ERROR_RECOVERY(1,
+ dev_printk(KERN_ERR, &sdev->sdev_gendev,
+ "Illegal state transition %s->%s\n",
+ scsi_device_state_name(oldstate),
+ scsi_device_state_name(state))
+ );
+ return -EINVAL;
+}
+EXPORT_SYMBOL(scsi_device_set_state);
+
+/**
+ * scsi_device_quiesce - Block user issued commands.
+ * @sdev: scsi device to quiesce.
+ *
+ * This works by trying to transition to the SDEV_QUIESCE state
+ * (which must be a legal transition). When the device is in this
+ * state, only special requests will be accepted, all others will
+ * be deferred. Since special requests may also be requeued requests,
+ * a successful return doesn't guarantee the device will be
+ * totally quiescent.
+ *
+ * Must be called with user context, may sleep.
+ *
+ * Returns zero if unsuccessful or an error if not.
+ **/
+int
+scsi_device_quiesce(struct scsi_device *sdev)
+{
+ int err = scsi_device_set_state(sdev, SDEV_QUIESCE);
+ if (err)
+ return err;
+
+ scsi_run_queue(sdev->request_queue);
+ while (sdev->device_busy) {
+ msleep_interruptible(200);
+ scsi_run_queue(sdev->request_queue);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(scsi_device_quiesce);
+
+/**
+ * scsi_device_resume - Restart user issued commands to a quiesced device.
+ * @sdev: scsi device to resume.
+ *
+ * Moves the device from quiesced back to running and restarts the
+ * queues.
+ *
+ * Must be called with user context, may sleep.
+ **/
+void
+scsi_device_resume(struct scsi_device *sdev)
+{
+ if(scsi_device_set_state(sdev, SDEV_RUNNING))
+ return;
+ scsi_run_queue(sdev->request_queue);
+}
+EXPORT_SYMBOL(scsi_device_resume);
+
+static void
+device_quiesce_fn(struct scsi_device *sdev, void *data)
+{
+ scsi_device_quiesce(sdev);
+}
+
+void
+scsi_target_quiesce(struct scsi_target *starget)
+{
+ starget_for_each_device(starget, NULL, device_quiesce_fn);
+}
+EXPORT_SYMBOL(scsi_target_quiesce);
+
+static void
+device_resume_fn(struct scsi_device *sdev, void *data)
+{
+ scsi_device_resume(sdev);
+}
+
+void
+scsi_target_resume(struct scsi_target *starget)
+{
+ starget_for_each_device(starget, NULL, device_resume_fn);
+}
+EXPORT_SYMBOL(scsi_target_resume);
+
+/**
+ * scsi_internal_device_block - internal function to put a device
+ * temporarily into the SDEV_BLOCK state
+ * @sdev: device to block
+ *
+ * Block request made by scsi lld's to temporarily stop all
+ * scsi commands on the specified device. Called from interrupt
+ * or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_BLOCK state
+ * (which must be a legal transition). When the device is in this
+ * state, all commands are deferred until the scsi lld reenables
+ * the device with scsi_device_unblock or device_block_tmo fires.
+ * This routine assumes the host_lock is held on entry.
+ **/
+int
+scsi_internal_device_block(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ unsigned long flags;
+ int err = 0;
+
+ err = scsi_device_set_state(sdev, SDEV_BLOCK);
+ if (err)
+ return err;
+
+ /*
+ * The device has transitioned to SDEV_BLOCK. Stop the
+ * block layer from calling the midlayer with this device's
+ * request queue.
+ */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_block);
+
+/**
+ * scsi_internal_device_unblock - resume a device after a block request
+ * @sdev: device to resume
+ *
+ * Called by scsi lld's or the midlayer to restart the device queue
+ * for the previously suspended scsi device. Called from interrupt or
+ * normal process context.
+ *
+ * Returns zero if successful or error if not.
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_RUNNING state
+ * (which must be a legal transition) allowing the midlayer to
+ * goose the queue for this device. This routine assumes the
+ * host_lock is held upon entry.
+ **/
+int
+scsi_internal_device_unblock(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ int err;
+ unsigned long flags;
+
+ /*
+ * Try to transition the scsi device to SDEV_RUNNING
+ * and goose the device queue if successful.
+ */
+ err = scsi_device_set_state(sdev, SDEV_RUNNING);
+ if (err)
+ return err;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_unblock);
+
+static void
+device_block(struct scsi_device *sdev, void *data)
+{
+ scsi_internal_device_block(sdev);
+}
+
+static int
+target_block(struct device *dev, void *data)
+{
+ if (scsi_is_target_device(dev))
+ starget_for_each_device(to_scsi_target(dev), NULL,
+ device_block);
+ return 0;
+}
+
+void
+scsi_target_block(struct device *dev)
+{
+ if (scsi_is_target_device(dev))
+ starget_for_each_device(to_scsi_target(dev), NULL,
+ device_block);
+ else
+ device_for_each_child(dev, NULL, target_block);
+}
+EXPORT_SYMBOL_GPL(scsi_target_block);
+
+static void
+device_unblock(struct scsi_device *sdev, void *data)
+{
+ scsi_internal_device_unblock(sdev);
+}
+
+static int
+target_unblock(struct device *dev, void *data)
+{
+ if (scsi_is_target_device(dev))
+ starget_for_each_device(to_scsi_target(dev), NULL,
+ device_unblock);
+ return 0;
+}
+
+void
+scsi_target_unblock(struct device *dev)
+{
+ if (scsi_is_target_device(dev))
+ starget_for_each_device(to_scsi_target(dev), NULL,
+ device_unblock);
+ else
+ device_for_each_child(dev, NULL, target_unblock);
+}
+EXPORT_SYMBOL_GPL(scsi_target_unblock);
diff --git a/drivers/scsi/scsi_logging.h b/drivers/scsi/scsi_logging.h
new file mode 100644
index 000000000000..e1722ba94586
--- /dev/null
+++ b/drivers/scsi/scsi_logging.h
@@ -0,0 +1,82 @@
+#ifndef _SCSI_LOGGING_H
+#define _SCSI_LOGGING_H
+
+#include <linux/config.h>
+
+/*
+ * This defines the scsi logging feature. It is a means by which the user
+ * can select how much information they get about various goings on, and it
+ * can be really useful for fault tracing. The logging word is divided into
+ * 8 nibbles, each of which describes a loglevel. The division of things is
+ * somewhat arbitrary, and the division of the word could be changed if it
+ * were really needed for any reason. The numbers below are the only place
+ * where these are specified. For a first go-around, 3 bits is more than
+ * enough, since this gives 8 levels of logging (really 7, since 0 is always
+ * off). Cutting to 2 bits might be wise at some point.
+ */
+
+#define SCSI_LOG_ERROR_SHIFT 0
+#define SCSI_LOG_TIMEOUT_SHIFT 3
+#define SCSI_LOG_SCAN_SHIFT 6
+#define SCSI_LOG_MLQUEUE_SHIFT 9
+#define SCSI_LOG_MLCOMPLETE_SHIFT 12
+#define SCSI_LOG_LLQUEUE_SHIFT 15
+#define SCSI_LOG_LLCOMPLETE_SHIFT 18
+#define SCSI_LOG_HLQUEUE_SHIFT 21
+#define SCSI_LOG_HLCOMPLETE_SHIFT 24
+#define SCSI_LOG_IOCTL_SHIFT 27
+
+#define SCSI_LOG_ERROR_BITS 3
+#define SCSI_LOG_TIMEOUT_BITS 3
+#define SCSI_LOG_SCAN_BITS 3
+#define SCSI_LOG_MLQUEUE_BITS 3
+#define SCSI_LOG_MLCOMPLETE_BITS 3
+#define SCSI_LOG_LLQUEUE_BITS 3
+#define SCSI_LOG_LLCOMPLETE_BITS 3
+#define SCSI_LOG_HLQUEUE_BITS 3
+#define SCSI_LOG_HLCOMPLETE_BITS 3
+#define SCSI_LOG_IOCTL_BITS 3
+
+extern unsigned int scsi_logging_level;
+
+#ifdef CONFIG_SCSI_LOGGING
+
+#define SCSI_LOG_LEVEL(SHIFT, BITS) \
+ ((scsi_logging_level >> (SHIFT)) & ((1 << (BITS)) - 1))
+
+#define SCSI_CHECK_LOGGING(SHIFT, BITS, LEVEL, CMD) \
+{ \
+ if (unlikely((SCSI_LOG_LEVEL(SHIFT, BITS)) > (LEVEL))) \
+ (CMD); \
+}
+#else
+#define SCSI_CHECK_LOGGING(SHIFT, BITS, LEVEL, CMD)
+#endif /* CONFIG_SCSI_LOGGING */
+
+/*
+ * These are the macros that are actually used throughout the code to
+ * log events. If logging isn't enabled, they are no-ops and will be
+ * completely absent from the user's code.
+ */
+#define SCSI_LOG_ERROR_RECOVERY(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_ERROR_SHIFT, SCSI_LOG_ERROR_BITS, LEVEL,CMD);
+#define SCSI_LOG_TIMEOUT(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_TIMEOUT_SHIFT, SCSI_LOG_TIMEOUT_BITS, LEVEL,CMD);
+#define SCSI_LOG_SCAN_BUS(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_SCAN_SHIFT, SCSI_LOG_SCAN_BITS, LEVEL,CMD);
+#define SCSI_LOG_MLQUEUE(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_MLQUEUE_SHIFT, SCSI_LOG_MLQUEUE_BITS, LEVEL,CMD);
+#define SCSI_LOG_MLCOMPLETE(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_MLCOMPLETE_SHIFT, SCSI_LOG_MLCOMPLETE_BITS, LEVEL,CMD);
+#define SCSI_LOG_LLQUEUE(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_LLQUEUE_SHIFT, SCSI_LOG_LLQUEUE_BITS, LEVEL,CMD);
+#define SCSI_LOG_LLCOMPLETE(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_LLCOMPLETE_SHIFT, SCSI_LOG_LLCOMPLETE_BITS, LEVEL,CMD);
+#define SCSI_LOG_HLQUEUE(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_HLQUEUE_SHIFT, SCSI_LOG_HLQUEUE_BITS, LEVEL,CMD);
+#define SCSI_LOG_HLCOMPLETE(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_HLCOMPLETE_SHIFT, SCSI_LOG_HLCOMPLETE_BITS, LEVEL,CMD);
+#define SCSI_LOG_IOCTL(LEVEL,CMD) \
+ SCSI_CHECK_LOGGING(SCSI_LOG_IOCTL_SHIFT, SCSI_LOG_IOCTL_BITS, LEVEL,CMD);
+
+#endif /* _SCSI_LOGGING_H */
diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c
new file mode 100644
index 000000000000..489175833709
--- /dev/null
+++ b/drivers/scsi/scsi_module.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2003 Christoph Hellwig.
+ * Released under GPL v2.
+ *
+ * Support for old-style host templates.
+ *
+ * NOTE: Do not use this for new drivers ever.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <scsi/scsi_host.h>
+
+
+static int __init init_this_scsi_driver(void)
+{
+ struct scsi_host_template *sht = &driver_template;
+ struct Scsi_Host *shost;
+ struct list_head *l;
+ int error;
+
+ if (!sht->release) {
+ printk(KERN_ERR
+ "scsi HBA driver %s didn't set a release method.\n",
+ sht->name);
+ return -EINVAL;
+ }
+
+ sht->module = THIS_MODULE;
+ INIT_LIST_HEAD(&sht->legacy_hosts);
+
+ sht->detect(sht);
+ if (list_empty(&sht->legacy_hosts))
+ return -ENODEV;
+
+ list_for_each_entry(shost, &sht->legacy_hosts, sht_legacy_list) {
+ error = scsi_add_host(shost, NULL);
+ if (error)
+ goto fail;
+ scsi_scan_host(shost);
+ }
+ return 0;
+ fail:
+ l = &shost->sht_legacy_list;
+ while ((l = l->prev) != &sht->legacy_hosts)
+ scsi_remove_host(list_entry(l, struct Scsi_Host, sht_legacy_list));
+ return error;
+}
+
+static void __exit exit_this_scsi_driver(void)
+{
+ struct scsi_host_template *sht = &driver_template;
+ struct Scsi_Host *shost, *s;
+
+ list_for_each_entry(shost, &sht->legacy_hosts, sht_legacy_list)
+ scsi_remove_host(shost);
+ list_for_each_entry_safe(shost, s, &sht->legacy_hosts, sht_legacy_list)
+ sht->release(shost);
+
+ if (list_empty(&sht->legacy_hosts))
+ return;
+
+ printk(KERN_WARNING "%s did not call scsi_unregister\n", sht->name);
+ dump_stack();
+
+ list_for_each_entry_safe(shost, s, &sht->legacy_hosts, sht_legacy_list)
+ scsi_unregister(shost);
+}
+
+module_init(init_this_scsi_driver);
+module_exit(exit_this_scsi_driver);
diff --git a/drivers/scsi/scsi_obsolete.h b/drivers/scsi/scsi_obsolete.h
new file mode 100644
index 000000000000..abeacb996ea0
--- /dev/null
+++ b/drivers/scsi/scsi_obsolete.h
@@ -0,0 +1,106 @@
+/*
+ * scsi_obsolete.h Copyright (C) 1997 Eric Youngdale
+ *
+ */
+
+#ifndef _SCSI_OBSOLETE_H
+#define _SCSI_OBSOLETE_H
+
+/*
+ * These are the return codes for the abort and reset functions. The mid-level
+ * code uses these to decide what to do next. Each of the low level abort
+ * and reset functions must correctly indicate what it has done.
+ * The descriptions are written from the point of view of the mid-level code,
+ * so that the return code is telling the mid-level drivers exactly what
+ * the low level driver has already done, and what remains to be done.
+ */
+
+/* We did not do anything.
+ * Wait some more for this command to complete, and if this does not work,
+ * try something more serious. */
+#define SCSI_ABORT_SNOOZE 0
+
+/* This means that we were able to abort the command. We have already
+ * called the mid-level done function, and do not expect an interrupt that
+ * will lead to another call to the mid-level done function for this command */
+#define SCSI_ABORT_SUCCESS 1
+
+/* We called for an abort of this command, and we should get an interrupt
+ * when this succeeds. Thus we should not restore the timer for this
+ * command in the mid-level abort function. */
+#define SCSI_ABORT_PENDING 2
+
+/* Unable to abort - command is currently on the bus. Grin and bear it. */
+#define SCSI_ABORT_BUSY 3
+
+/* The command is not active in the low level code. Command probably
+ * finished. */
+#define SCSI_ABORT_NOT_RUNNING 4
+
+/* Something went wrong. The low level driver will indicate the correct
+ * error condition when it calls scsi_done, so the mid-level abort function
+ * can simply wait until this comes through */
+#define SCSI_ABORT_ERROR 5
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * Anyway, just wait a little more for the command in question, and hope that
+ * it eventually finishes. If it never finishes, the SCSI device could
+ * hang, so use this with caution. */
+#define SCSI_RESET_SNOOZE 0
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * We have given up on this ever completing. The mid-level code will
+ * request sense information to decide how to proceed from here. */
+#define SCSI_RESET_PUNT 1
+
+/* This means that we were able to reset the bus. We have restarted all of
+ * the commands that should be restarted, and we should be able to continue
+ * on normally from here. We do not expect any interrupts that will return
+ * DID_RESET to any of the other commands in the host_queue, and the mid-level
+ * code does not need to do anything special to keep the commands alive.
+ * If a hard reset was performed then all outstanding commands on the
+ * bus have been restarted. */
+#define SCSI_RESET_SUCCESS 2
+
+/* We called for a reset of this bus, and we should get an interrupt
+ * when this succeeds. Each command should get its own status
+ * passed up to scsi_done, but this has not happened yet.
+ * If a hard reset was performed, then we expect an interrupt
+ * for *each* of the outstanding commands that will have the
+ * effect of restarting the commands.
+ */
+#define SCSI_RESET_PENDING 3
+
+/* We did a reset, but do not expect an interrupt to signal DID_RESET.
+ * This tells the upper level code to request the sense info, and this
+ * should keep the command alive. */
+#define SCSI_RESET_WAKEUP 4
+
+/* The command is not active in the low level code. Command probably
+ finished. */
+#define SCSI_RESET_NOT_RUNNING 5
+
+/* Something went wrong, and we do not know how to fix it. */
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS 0x01
+#define SCSI_RESET_ASYNCHRONOUS 0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a hard reset.
+ */
+#define SCSI_RESET_BUS_RESET 0x100
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a host adapter reset.
+ */
+#define SCSI_RESET_HOST_RESET 0x200
+/*
+ * Used to mask off bits and to obtain the basic action that was
+ * performed.
+ */
+#define SCSI_RESET_ACTION 0xff
+
+#endif /* SCSI_OBSOLETE_H */
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
new file mode 100644
index 000000000000..aca3b39fe710
--- /dev/null
+++ b/drivers/scsi/scsi_priv.h
@@ -0,0 +1,165 @@
+#ifndef _SCSI_PRIV_H
+#define _SCSI_PRIV_H
+
+#include <linux/config.h>
+#include <linux/device.h>
+
+struct request_queue;
+struct scsi_cmnd;
+struct scsi_device;
+struct scsi_host_template;
+struct scsi_request;
+struct Scsi_Host;
+
+
+/*
+ * These are the values that the owner field can take.
+ * They are used as an indication of who the command belongs to.
+ */
+#define SCSI_OWNER_HIGHLEVEL 0x100
+#define SCSI_OWNER_MIDLEVEL 0x101
+#define SCSI_OWNER_LOWLEVEL 0x102
+#define SCSI_OWNER_ERROR_HANDLER 0x103
+#define SCSI_OWNER_BH_HANDLER 0x104
+#define SCSI_OWNER_NOBODY 0x105
+
+/*
+ * Magic values for certain scsi structs. Shouldn't ever be used.
+ */
+#define SCSI_CMND_MAGIC 0xE25C23A5
+#define SCSI_REQ_MAGIC 0x75F6D354
+
+/*
+ * Flag bit for the internal_timeout array
+ */
+#define NORMAL_TIMEOUT 0
+
+/*
+ * Scsi Error Handler Flags
+ */
+#define scsi_eh_eflags_chk(scp, flags) \
+ ((scp)->eh_eflags & (flags))
+#define scsi_eh_eflags_set(scp, flags) \
+ do { (scp)->eh_eflags |= (flags); } while(0)
+#define scsi_eh_eflags_clr(scp, flags) \
+ do { (scp)->eh_eflags &= ~(flags); } while(0)
+#define scsi_eh_eflags_clr_all(scp) \
+ (scp->eh_eflags = 0)
+
+#define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */
+#define SCSI_EH_REC_TIMEOUT 0x0002 /* EH retry timed out */
+
+#define SCSI_SENSE_VALID(scmd) \
+ (((scmd)->sense_buffer[0] & 0x70) == 0x70)
+
+/*
+ * Special value for scanning to specify scanning or rescanning of all
+ * possible channels, (target) ids, or luns on a given shost.
+ */
+#define SCAN_WILD_CARD ~0
+
+/* hosts.c */
+extern int scsi_init_hosts(void);
+extern void scsi_exit_hosts(void);
+
+/* scsi.c */
+extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd);
+extern int scsi_setup_command_freelist(struct Scsi_Host *shost);
+extern void scsi_destroy_command_freelist(struct Scsi_Host *shost);
+extern void scsi_done(struct scsi_cmnd *cmd);
+extern int scsi_retry_command(struct scsi_cmnd *cmd);
+extern int scsi_insert_special_req(struct scsi_request *sreq, int);
+extern void scsi_init_cmd_from_req(struct scsi_cmnd *cmd,
+ struct scsi_request *sreq);
+extern void __scsi_release_request(struct scsi_request *sreq);
+extern void __scsi_done(struct scsi_cmnd *cmd);
+#ifdef CONFIG_SCSI_LOGGING
+void scsi_log_send(struct scsi_cmnd *cmd);
+void scsi_log_completion(struct scsi_cmnd *cmd, int disposition);
+#else
+static inline void scsi_log_send(struct scsi_cmnd *cmd)
+ { };
+static inline void scsi_log_completion(struct scsi_cmnd *cmd, int disposition)
+ { };
+#endif
+
+/* scsi_devinfo.c */
+extern int scsi_get_device_flags(struct scsi_device *sdev,
+ unsigned char *vendor, unsigned char *model);
+extern int __init scsi_init_devinfo(void);
+extern void scsi_exit_devinfo(void);
+
+/* scsi_error.c */
+extern void scsi_times_out(struct scsi_cmnd *cmd);
+extern int scsi_error_handler(void *host);
+extern int scsi_decide_disposition(struct scsi_cmnd *cmd);
+extern void scsi_eh_wakeup(struct Scsi_Host *shost);
+extern int scsi_eh_scmd_add(struct scsi_cmnd *, int);
+
+/* scsi_lib.c */
+extern int scsi_maybe_unblock_host(struct scsi_device *sdev);
+extern void scsi_setup_cmd_retry(struct scsi_cmnd *cmd);
+extern void scsi_device_unbusy(struct scsi_device *sdev);
+extern int scsi_queue_insert(struct scsi_cmnd *cmd, int reason);
+extern void scsi_next_command(struct scsi_cmnd *cmd);
+extern void scsi_run_host_queues(struct Scsi_Host *shost);
+extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev);
+extern void scsi_free_queue(struct request_queue *q);
+extern int scsi_init_queue(void);
+extern void scsi_exit_queue(void);
+
+/* scsi_proc.c */
+#ifdef CONFIG_SCSI_PROC_FS
+extern void scsi_proc_hostdir_add(struct scsi_host_template *);
+extern void scsi_proc_hostdir_rm(struct scsi_host_template *);
+extern void scsi_proc_host_add(struct Scsi_Host *);
+extern void scsi_proc_host_rm(struct Scsi_Host *);
+extern int scsi_init_procfs(void);
+extern void scsi_exit_procfs(void);
+#else
+# define scsi_proc_hostdir_add(sht) do { } while (0)
+# define scsi_proc_hostdir_rm(sht) do { } while (0)
+# define scsi_proc_host_add(shost) do { } while (0)
+# define scsi_proc_host_rm(shost) do { } while (0)
+# define scsi_init_procfs() (0)
+# define scsi_exit_procfs() do { } while (0)
+#endif /* CONFIG_PROC_FS */
+
+/* scsi_scan.c */
+extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int,
+ unsigned int, unsigned int, int);
+extern void scsi_forget_host(struct Scsi_Host *);
+extern void scsi_rescan_device(struct device *);
+
+/* scsi_sysctl.c */
+#ifdef CONFIG_SYSCTL
+extern int scsi_init_sysctl(void);
+extern void scsi_exit_sysctl(void);
+#else
+# define scsi_init_sysctl() (0)
+# define scsi_exit_sysctl() do { } while (0)
+#endif /* CONFIG_SYSCTL */
+
+/* scsi_sysfs.c */
+extern void scsi_device_dev_release(struct device *);
+extern int scsi_sysfs_add_sdev(struct scsi_device *);
+extern int scsi_sysfs_add_host(struct Scsi_Host *);
+extern int scsi_sysfs_register(void);
+extern void scsi_sysfs_unregister(void);
+extern void scsi_sysfs_device_initialize(struct scsi_device *);
+extern int scsi_sysfs_target_initialize(struct scsi_device *);
+extern struct scsi_transport_template blank_transport_template;
+
+extern struct class sdev_class;
+extern struct bus_type scsi_bus_type;
+
+/*
+ * internal scsi timeout functions: for use by mid-layer and transport
+ * classes.
+ */
+
+#define SCSI_DEVICE_BLOCK_MAX_TIMEOUT (HZ*60)
+extern int scsi_internal_device_block(struct scsi_device *sdev);
+extern int scsi_internal_device_unblock(struct scsi_device *sdev);
+
+#endif /* _SCSI_PRIV_H */
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
new file mode 100644
index 000000000000..a50958b1b6ee
--- /dev/null
+++ b/drivers/scsi/scsi_proc.c
@@ -0,0 +1,336 @@
+/*
+ * linux/drivers/scsi/scsi_proc.c
+ *
+ * The functions in this file provide an interface between
+ * the PROC file system and the SCSI device drivers
+ * It is mainly used for debugging, statistics and to pass
+ * information directly to the lowlevel driver.
+ *
+ * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ * Version: 0.99.8 last change: 95/09/13
+ *
+ * generic command parser provided by:
+ * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
+ *
+ * generic_proc_info() support of xxxx_info() by:
+ * Michael A. Griffith <grif@acm.org>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+
+/* 4K page size, but our output routines, use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+static struct proc_dir_entry *proc_scsi;
+
+/* Protect sht->present and sht->proc_dir */
+static DECLARE_MUTEX(global_host_template_sem);
+
+static int proc_scsi_read(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ struct Scsi_Host *shost = data;
+ int n;
+
+ n = shost->hostt->proc_info(shost, buffer, start, offset, length, 0);
+ *eof = (n < length);
+
+ return n;
+}
+
+static int proc_scsi_write_proc(struct file *file, const char __user *buf,
+ unsigned long count, void *data)
+{
+ struct Scsi_Host *shost = data;
+ ssize_t ret = -ENOMEM;
+ char *page;
+ char *start;
+
+ if (count > PROC_BLOCK_SIZE)
+ return -EOVERFLOW;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (page) {
+ ret = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+ ret = shost->hostt->proc_info(shost, page, &start, 0, count, 1);
+ }
+out:
+ free_page((unsigned long)page);
+ return ret;
+}
+
+void scsi_proc_hostdir_add(struct scsi_host_template *sht)
+{
+ if (!sht->proc_info)
+ return;
+
+ down(&global_host_template_sem);
+ if (!sht->present++) {
+ sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
+ if (!sht->proc_dir)
+ printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
+ __FUNCTION__, sht->proc_name);
+ else
+ sht->proc_dir->owner = sht->module;
+ }
+ up(&global_host_template_sem);
+}
+
+void scsi_proc_hostdir_rm(struct scsi_host_template *sht)
+{
+ if (!sht->proc_info)
+ return;
+
+ down(&global_host_template_sem);
+ if (!--sht->present && sht->proc_dir) {
+ remove_proc_entry(sht->proc_name, proc_scsi);
+ sht->proc_dir = NULL;
+ }
+ up(&global_host_template_sem);
+}
+
+void scsi_proc_host_add(struct Scsi_Host *shost)
+{
+ struct scsi_host_template *sht = shost->hostt;
+ struct proc_dir_entry *p;
+ char name[10];
+
+ if (!sht->proc_dir)
+ return;
+
+ sprintf(name,"%d", shost->host_no);
+ p = create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR,
+ sht->proc_dir, proc_scsi_read, shost);
+ if (!p) {
+ printk(KERN_ERR "%s: Failed to register host %d in"
+ "%s\n", __FUNCTION__, shost->host_no,
+ sht->proc_name);
+ return;
+ }
+
+ p->write_proc = proc_scsi_write_proc;
+ p->owner = sht->module;
+}
+
+void scsi_proc_host_rm(struct Scsi_Host *shost)
+{
+ char name[10];
+
+ if (!shost->hostt->proc_dir)
+ return;
+
+ sprintf(name,"%d", shost->host_no);
+ remove_proc_entry(name, shost->hostt->proc_dir);
+}
+
+static int proc_print_scsidevice(struct device *dev, void *data)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct seq_file *s = data;
+ int i;
+
+ seq_printf(s,
+ "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
+ sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
+ for (i = 0; i < 8; i++) {
+ if (sdev->vendor[i] >= 0x20)
+ seq_printf(s, "%c", sdev->vendor[i]);
+ else
+ seq_printf(s, " ");
+ }
+
+ seq_printf(s, " Model: ");
+ for (i = 0; i < 16; i++) {
+ if (sdev->model[i] >= 0x20)
+ seq_printf(s, "%c", sdev->model[i]);
+ else
+ seq_printf(s, " ");
+ }
+
+ seq_printf(s, " Rev: ");
+ for (i = 0; i < 4; i++) {
+ if (sdev->rev[i] >= 0x20)
+ seq_printf(s, "%c", sdev->rev[i]);
+ else
+ seq_printf(s, " ");
+ }
+
+ seq_printf(s, "\n");
+
+ seq_printf(s, " Type: %s ",
+ sdev->type < MAX_SCSI_DEVICE_CODE ?
+ scsi_device_types[(int) sdev->type] : "Unknown ");
+ seq_printf(s, " ANSI"
+ " SCSI revision: %02x", (sdev->scsi_level - 1) ?
+ sdev->scsi_level - 1 : 1);
+ if (sdev->scsi_level == 2)
+ seq_printf(s, " CCS\n");
+ else
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
+{
+ struct Scsi_Host *shost;
+ int error = -ENXIO;
+
+ shost = scsi_host_lookup(host);
+ if (IS_ERR(shost))
+ return PTR_ERR(shost);
+
+ error = scsi_scan_host_selected(shost, channel, id, lun, 1);
+ scsi_host_put(shost);
+ return error;
+}
+
+static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
+{
+ struct scsi_device *sdev;
+ struct Scsi_Host *shost;
+ int error = -ENXIO;
+
+ shost = scsi_host_lookup(host);
+ if (IS_ERR(shost))
+ return PTR_ERR(shost);
+ sdev = scsi_device_lookup(shost, channel, id, lun);
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ error = 0;
+ }
+
+ scsi_host_put(shost);
+ return error;
+}
+
+static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
+ size_t length, loff_t *ppos)
+{
+ int host, channel, id, lun;
+ char *buffer, *p;
+ int err;
+
+ if (!buf || length > PAGE_SIZE)
+ return -EINVAL;
+
+ buffer = (char *)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ err = -EFAULT;
+ if (copy_from_user(buffer, buf, length))
+ goto out;
+
+ err = -EINVAL;
+ if (length < PAGE_SIZE)
+ buffer[length] = '\0';
+ else if (buffer[PAGE_SIZE-1])
+ goto out;
+
+ /*
+ * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ */
+ if (!strncmp("scsi add-single-device", buffer, 22)) {
+ p = buffer + 23;
+
+ host = simple_strtoul(p, &p, 0);
+ channel = simple_strtoul(p + 1, &p, 0);
+ id = simple_strtoul(p + 1, &p, 0);
+ lun = simple_strtoul(p + 1, &p, 0);
+
+ err = scsi_add_single_device(host, channel, id, lun);
+ if (err >= 0)
+ err = length;
+
+ /*
+ * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ */
+ } else if (!strncmp("scsi remove-single-device", buffer, 25)) {
+ p = buffer + 26;
+
+ host = simple_strtoul(p, &p, 0);
+ channel = simple_strtoul(p + 1, &p, 0);
+ id = simple_strtoul(p + 1, &p, 0);
+ lun = simple_strtoul(p + 1, &p, 0);
+
+ err = scsi_remove_single_device(host, channel, id, lun);
+ }
+
+ out:
+ free_page((unsigned long)buffer);
+ return err;
+}
+
+static int proc_scsi_show(struct seq_file *s, void *p)
+{
+ seq_printf(s, "Attached devices:\n");
+ bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice);
+ return 0;
+}
+
+static int proc_scsi_open(struct inode *inode, struct file *file)
+{
+ /*
+ * We don't really needs this for the write case but it doesn't
+ * harm either.
+ */
+ return single_open(file, proc_scsi_show, NULL);
+}
+
+static struct file_operations proc_scsi_operations = {
+ .open = proc_scsi_open,
+ .read = seq_read,
+ .write = proc_scsi_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int __init scsi_init_procfs(void)
+{
+ struct proc_dir_entry *pde;
+
+ proc_scsi = proc_mkdir("scsi", NULL);
+ if (!proc_scsi)
+ goto err1;
+
+ pde = create_proc_entry("scsi/scsi", 0, NULL);
+ if (!pde)
+ goto err2;
+ pde->proc_fops = &proc_scsi_operations;
+
+ return 0;
+
+err2:
+ remove_proc_entry("scsi", NULL);
+err1:
+ return -ENOMEM;
+}
+
+void scsi_exit_procfs(void)
+{
+ remove_proc_entry("scsi/scsi", NULL);
+ remove_proc_entry("scsi", NULL);
+}
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
new file mode 100644
index 000000000000..a8a37a338c02
--- /dev/null
+++ b/drivers/scsi/scsi_scan.c
@@ -0,0 +1,1473 @@
+/*
+ * scsi_scan.c
+ *
+ * Copyright (C) 2000 Eric Youngdale,
+ * Copyright (C) 2002 Patrick Mansfield
+ *
+ * The general scanning/probing algorithm is as follows, exceptions are
+ * made to it depending on device specific flags, compilation options, and
+ * global variable (boot or module load time) settings.
+ *
+ * A specific LUN is scanned via an INQUIRY command; if the LUN has a
+ * device attached, a Scsi_Device is allocated and setup for it.
+ *
+ * For every id of every channel on the given host:
+ *
+ * Scan LUN 0; if the target responds to LUN 0 (even if there is no
+ * device or storage attached to LUN 0):
+ *
+ * If LUN 0 has a device attached, allocate and setup a
+ * Scsi_Device for it.
+ *
+ * If target is SCSI-3 or up, issue a REPORT LUN, and scan
+ * all of the LUNs returned by the REPORT LUN; else,
+ * sequentially scan LUNs up until some maximum is reached,
+ * or a LUN is seen that cannot have a device attached to it.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <asm/semaphore.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_devinfo.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_request.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_eh.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+#define ALLOC_FAILURE_MSG KERN_ERR "%s: Allocation failure during" \
+ " SCSI scanning, some SCSI devices might not be configured\n"
+
+/*
+ * Default timeout
+ */
+#define SCSI_TIMEOUT (2*HZ)
+
+/*
+ * Prefix values for the SCSI id's (stored in driverfs name field)
+ */
+#define SCSI_UID_SER_NUM 'S'
+#define SCSI_UID_UNKNOWN 'Z'
+
+/*
+ * Return values of some of the scanning functions.
+ *
+ * SCSI_SCAN_NO_RESPONSE: no valid response received from the target, this
+ * includes allocation or general failures preventing IO from being sent.
+ *
+ * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is available
+ * on the given LUN.
+ *
+ * SCSI_SCAN_LUN_PRESENT: target responded, and a device is available on a
+ * given LUN.
+ */
+#define SCSI_SCAN_NO_RESPONSE 0
+#define SCSI_SCAN_TARGET_PRESENT 1
+#define SCSI_SCAN_LUN_PRESENT 2
+
+static char *scsi_null_device_strs = "nullnullnullnull";
+
+#define MAX_SCSI_LUNS 512
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+static unsigned int max_scsi_luns = MAX_SCSI_LUNS;
+#else
+static unsigned int max_scsi_luns = 1;
+#endif
+
+module_param_named(max_luns, max_scsi_luns, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(max_luns,
+ "last scsi LUN (should be between 1 and 2^32-1)");
+
+/*
+ * max_scsi_report_luns: the maximum number of LUNS that will be
+ * returned from the REPORT LUNS command. 8 times this value must
+ * be allocated. In theory this could be up to an 8 byte value, but
+ * in practice, the maximum number of LUNs suppored by any device
+ * is about 16k.
+ */
+static unsigned int max_scsi_report_luns = 511;
+
+module_param_named(max_report_luns, max_scsi_report_luns, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(max_report_luns,
+ "REPORT LUNS maximum number of LUNS received (should be"
+ " between 1 and 16384)");
+
+static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ+3;
+
+module_param_named(inq_timeout, scsi_inq_timeout, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(inq_timeout,
+ "Timeout (in seconds) waiting for devices to answer INQUIRY."
+ " Default is 5. Some non-compliant devices need more.");
+
+/**
+ * scsi_unlock_floptical - unlock device via a special MODE SENSE command
+ * @sreq: used to send the command
+ * @result: area to store the result of the MODE SENSE
+ *
+ * Description:
+ * Send a vendor specific MODE SENSE (not a MODE SELECT) command using
+ * @sreq to unlock a device, storing the (unused) results into result.
+ * Called for BLIST_KEY devices.
+ **/
+static void scsi_unlock_floptical(struct scsi_request *sreq,
+ unsigned char *result)
+{
+ unsigned char scsi_cmd[MAX_COMMAND_SIZE];
+
+ printk(KERN_NOTICE "scsi: unlocking floptical drive\n");
+ scsi_cmd[0] = MODE_SENSE;
+ scsi_cmd[1] = 0;
+ scsi_cmd[2] = 0x2e;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 0x2a; /* size */
+ scsi_cmd[5] = 0;
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+ scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3);
+}
+
+/**
+ * print_inquiry - printk the inquiry information
+ * @inq_result: printk this SCSI INQUIRY
+ *
+ * Description:
+ * printk the vendor, model, and other information found in the
+ * INQUIRY data in @inq_result.
+ *
+ * Notes:
+ * Remove this, and replace with a hotplug event that logs any
+ * relevant information.
+ **/
+static void print_inquiry(unsigned char *inq_result)
+{
+ int i;
+
+ printk(KERN_NOTICE " Vendor: ");
+ for (i = 8; i < 16; i++)
+ if (inq_result[i] >= 0x20 && i < inq_result[4] + 5)
+ printk("%c", inq_result[i]);
+ else
+ printk(" ");
+
+ printk(" Model: ");
+ for (i = 16; i < 32; i++)
+ if (inq_result[i] >= 0x20 && i < inq_result[4] + 5)
+ printk("%c", inq_result[i]);
+ else
+ printk(" ");
+
+ printk(" Rev: ");
+ for (i = 32; i < 36; i++)
+ if (inq_result[i] >= 0x20 && i < inq_result[4] + 5)
+ printk("%c", inq_result[i]);
+ else
+ printk(" ");
+
+ printk("\n");
+
+ i = inq_result[0] & 0x1f;
+
+ printk(KERN_NOTICE " Type: %s ",
+ i <
+ MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] :
+ "Unknown ");
+ printk(" ANSI SCSI revision: %02x",
+ inq_result[2] & 0x07);
+ if ((inq_result[2] & 0x07) == 1 && (inq_result[3] & 0x0f) == 1)
+ printk(" CCS\n");
+ else
+ printk("\n");
+}
+
+/**
+ * scsi_alloc_sdev - allocate and setup a scsi_Device
+ *
+ * Description:
+ * Allocate, initialize for io, and return a pointer to a scsi_Device.
+ * Stores the @shost, @channel, @id, and @lun in the scsi_Device, and
+ * adds scsi_Device to the appropriate list.
+ *
+ * Return value:
+ * scsi_Device pointer, or NULL on failure.
+ **/
+static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
+ unsigned int lun, void *hostdata)
+{
+ struct scsi_device *sdev;
+ int display_failure_msg = 1, ret;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+
+ sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size,
+ GFP_ATOMIC);
+ if (!sdev)
+ goto out;
+
+ memset(sdev, 0, sizeof(*sdev));
+ sdev->vendor = scsi_null_device_strs;
+ sdev->model = scsi_null_device_strs;
+ sdev->rev = scsi_null_device_strs;
+ sdev->host = shost;
+ sdev->id = starget->id;
+ sdev->lun = lun;
+ sdev->channel = starget->channel;
+ sdev->sdev_state = SDEV_CREATED;
+ INIT_LIST_HEAD(&sdev->siblings);
+ INIT_LIST_HEAD(&sdev->same_target_siblings);
+ INIT_LIST_HEAD(&sdev->cmd_list);
+ INIT_LIST_HEAD(&sdev->starved_entry);
+ spin_lock_init(&sdev->list_lock);
+
+ sdev->sdev_gendev.parent = get_device(&starget->dev);
+ sdev->sdev_target = starget;
+
+ /* usually NULL and set by ->slave_alloc instead */
+ sdev->hostdata = hostdata;
+
+ /* if the device needs this changing, it may do so in the
+ * slave_configure function */
+ sdev->max_device_blocked = SCSI_DEFAULT_DEVICE_BLOCKED;
+
+ /*
+ * Some low level driver could use device->type
+ */
+ sdev->type = -1;
+
+ /*
+ * Assume that the device will have handshaking problems,
+ * and then fix this field later if it turns out it
+ * doesn't
+ */
+ sdev->borken = 1;
+
+ spin_lock_init(&sdev->sdev_lock);
+ sdev->request_queue = scsi_alloc_queue(sdev);
+ if (!sdev->request_queue) {
+ /* release fn is set up in scsi_sysfs_device_initialise, so
+ * have to free and put manually here */
+ put_device(&starget->dev);
+ goto out;
+ }
+
+ sdev->request_queue->queuedata = sdev;
+ scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+
+ scsi_sysfs_device_initialize(sdev);
+
+ if (shost->hostt->slave_alloc) {
+ ret = shost->hostt->slave_alloc(sdev);
+ if (ret) {
+ /*
+ * if LLDD reports slave not present, don't clutter
+ * console with alloc failure messages
+
+
+ */
+ if (ret == -ENXIO)
+ display_failure_msg = 0;
+ goto out_device_destroy;
+ }
+ }
+
+ return sdev;
+
+out_device_destroy:
+ transport_destroy_device(&sdev->sdev_gendev);
+ scsi_free_queue(sdev->request_queue);
+ put_device(&sdev->sdev_gendev);
+out:
+ if (display_failure_msg)
+ printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+ return NULL;
+}
+
+static void scsi_target_dev_release(struct device *dev)
+{
+ struct device *parent = dev->parent;
+ struct scsi_target *starget = to_scsi_target(dev);
+ kfree(starget);
+ put_device(parent);
+}
+
+int scsi_is_target_device(const struct device *dev)
+{
+ return dev->release == scsi_target_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_target_device);
+
+static struct scsi_target *__scsi_find_target(struct device *parent,
+ int channel, uint id)
+{
+ struct scsi_target *starget, *found_starget = NULL;
+ struct Scsi_Host *shost = dev_to_shost(parent);
+ /*
+ * Search for an existing target for this sdev.
+ */
+ list_for_each_entry(starget, &shost->__targets, siblings) {
+ if (starget->id == id &&
+ starget->channel == channel) {
+ found_starget = starget;
+ break;
+ }
+ }
+ if (found_starget)
+ get_device(&found_starget->dev);
+
+ return found_starget;
+}
+
+static struct scsi_target *scsi_alloc_target(struct device *parent,
+ int channel, uint id)
+{
+ struct Scsi_Host *shost = dev_to_shost(parent);
+ struct device *dev = NULL;
+ unsigned long flags;
+ const int size = sizeof(struct scsi_target)
+ + shost->transportt->target_size;
+ struct scsi_target *starget = kmalloc(size, GFP_ATOMIC);
+ struct scsi_target *found_target;
+
+ if (!starget) {
+ printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
+ return NULL;
+ }
+ memset(starget, 0, size);
+ dev = &starget->dev;
+ device_initialize(dev);
+ starget->reap_ref = 1;
+ dev->parent = get_device(parent);
+ dev->release = scsi_target_dev_release;
+ sprintf(dev->bus_id, "target%d:%d:%d",
+ shost->host_no, channel, id);
+ starget->id = id;
+ starget->channel = channel;
+ INIT_LIST_HEAD(&starget->siblings);
+ INIT_LIST_HEAD(&starget->devices);
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ found_target = __scsi_find_target(parent, channel, id);
+ if (found_target)
+ goto found;
+
+ list_add_tail(&starget->siblings, &shost->__targets);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ /* allocate and add */
+ transport_setup_device(&starget->dev);
+ device_add(&starget->dev);
+ transport_add_device(&starget->dev);
+ return starget;
+
+ found:
+ found_target->reap_ref++;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ put_device(parent);
+ kfree(starget);
+ return found_target;
+}
+
+/**
+ * scsi_target_reap - check to see if target is in use and destroy if not
+ *
+ * @starget: target to be checked
+ *
+ * This is used after removing a LUN or doing a last put of the target
+ * it checks atomically that nothing is using the target and removes
+ * it if so.
+ */
+void scsi_target_reap(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+ list_del_init(&starget->siblings);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ device_del(&starget->dev);
+ transport_unregister_device(&starget->dev);
+ put_device(&starget->dev);
+ return;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+/**
+ * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
+ * @sreq: used to send the INQUIRY
+ * @inq_result: area to store the INQUIRY result
+ * @bflags: store any bflags found here
+ *
+ * Description:
+ * Probe the lun associated with @sreq using a standard SCSI INQUIRY;
+ *
+ * If the INQUIRY is successful, sreq->sr_result is zero and: the
+ * INQUIRY data is in @inq_result; the scsi_level and INQUIRY length
+ * are copied to the Scsi_Device at @sreq->sr_device (sdev);
+ * any flags value is stored in *@bflags.
+ **/
+static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
+ int *bflags)
+{
+ struct scsi_device *sdev = sreq->sr_device; /* a bit ugly */
+ unsigned char scsi_cmd[MAX_COMMAND_SIZE];
+ int first_inquiry_len, try_inquiry_len, next_inquiry_len;
+ int response_len = 0;
+ int pass, count;
+ struct scsi_sense_hdr sshdr;
+
+ *bflags = 0;
+
+ /* Perform up to 3 passes. The first pass uses a conservative
+ * transfer length of 36 unless sdev->inquiry_len specifies a
+ * different value. */
+ first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
+ try_inquiry_len = first_inquiry_len;
+ pass = 1;
+
+ next_pass:
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY pass %d "
+ "to host %d channel %d id %d lun %d, length %d\n",
+ pass, sdev->host->host_no, sdev->channel,
+ sdev->id, sdev->lun, try_inquiry_len));
+
+ /* Each pass gets up to three chances to ignore Unit Attention */
+ for (count = 0; count < 3; ++count) {
+ memset(scsi_cmd, 0, 6);
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[4] = (unsigned char) try_inquiry_len;
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+
+ memset(inq_result, 0, try_inquiry_len);
+ scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
+ try_inquiry_len,
+ HZ/2 + HZ*scsi_inq_timeout, 3);
+
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
+ "with code 0x%x\n",
+ sreq->sr_result ? "failed" : "successful",
+ sreq->sr_result));
+
+ if (sreq->sr_result) {
+ /*
+ * not-ready to ready transition [asc/ascq=0x28/0x0]
+ * or power-on, reset [asc/ascq=0x29/0x0], continue.
+ * INQUIRY should not yield UNIT_ATTENTION
+ * but many buggy devices do so anyway.
+ */
+ if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
+ scsi_request_normalize_sense(sreq, &sshdr)) {
+ if ((sshdr.sense_key == UNIT_ATTENTION) &&
+ ((sshdr.asc == 0x28) ||
+ (sshdr.asc == 0x29)) &&
+ (sshdr.ascq == 0))
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (sreq->sr_result == 0) {
+ response_len = (unsigned char) inq_result[4] + 5;
+ if (response_len > 255)
+ response_len = first_inquiry_len; /* sanity */
+
+ /*
+ * Get any flags for this device.
+ *
+ * XXX add a bflags to Scsi_Device, and replace the
+ * corresponding bit fields in Scsi_Device, so bflags
+ * need not be passed as an argument.
+ */
+ *bflags = scsi_get_device_flags(sdev, &inq_result[8],
+ &inq_result[16]);
+
+ /* When the first pass succeeds we gain information about
+ * what larger transfer lengths might work. */
+ if (pass == 1) {
+ if (BLIST_INQUIRY_36 & *bflags)
+ next_inquiry_len = 36;
+ else if (BLIST_INQUIRY_58 & *bflags)
+ next_inquiry_len = 58;
+ else if (sdev->inquiry_len)
+ next_inquiry_len = sdev->inquiry_len;
+ else
+ next_inquiry_len = response_len;
+
+ /* If more data is available perform the second pass */
+ if (next_inquiry_len > try_inquiry_len) {
+ try_inquiry_len = next_inquiry_len;
+ pass = 2;
+ goto next_pass;
+ }
+ }
+
+ } else if (pass == 2) {
+ printk(KERN_INFO "scsi scan: %d byte inquiry failed. "
+ "Consider BLIST_INQUIRY_36 for this device\n",
+ try_inquiry_len);
+
+ /* If this pass failed, the third pass goes back and transfers
+ * the same amount as we successfully got in the first pass. */
+ try_inquiry_len = first_inquiry_len;
+ pass = 3;
+ goto next_pass;
+ }
+
+ /* If the last transfer attempt got an error, assume the
+ * peripheral doesn't exist or is dead. */
+ if (sreq->sr_result)
+ return;
+
+ /* Don't report any more data than the device says is valid */
+ sdev->inquiry_len = min(try_inquiry_len, response_len);
+
+ /*
+ * XXX Abort if the response length is less than 36? If less than
+ * 32, the lookup of the device flags (above) could be invalid,
+ * and it would be possible to take an incorrect action - we do
+ * not want to hang because of a short INQUIRY. On the flip side,
+ * if the device is spun down or becoming ready (and so it gives a
+ * short INQUIRY), an abort here prevents any further use of the
+ * device, including spin up.
+ *
+ * Related to the above issue:
+ *
+ * XXX Devices (disk or all?) should be sent a TEST UNIT READY,
+ * and if not ready, sent a START_STOP to start (maybe spin up) and
+ * then send the INQUIRY again, since the INQUIRY can change after
+ * a device is initialized.
+ *
+ * Ideally, start a device if explicitly asked to do so. This
+ * assumes that a device is spun up on power on, spun down on
+ * request, and then spun up on request.
+ */
+
+ /*
+ * The scanning code needs to know the scsi_level, even if no
+ * device is attached at LUN 0 (SCSI_SCAN_TARGET_PRESENT) so
+ * non-zero LUNs can be scanned.
+ */
+ sdev->scsi_level = inq_result[2] & 0x07;
+ if (sdev->scsi_level >= 2 ||
+ (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1))
+ sdev->scsi_level++;
+
+ return;
+}
+
+/**
+ * scsi_add_lun - allocate and fully initialze a Scsi_Device
+ * @sdevscan: holds information to be stored in the new Scsi_Device
+ * @sdevnew: store the address of the newly allocated Scsi_Device
+ * @inq_result: holds the result of a previous INQUIRY to the LUN
+ * @bflags: black/white list flag
+ *
+ * Description:
+ * Allocate and initialize a Scsi_Device matching sdevscan. Optionally
+ * set fields based on values in *@bflags. If @sdevnew is not
+ * NULL, store the address of the new Scsi_Device in *@sdevnew (needed
+ * when scanning a particular LUN).
+ *
+ * Return:
+ * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device
+ * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
+ **/
+static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
+{
+ /*
+ * XXX do not save the inquiry, since it can change underneath us,
+ * save just vendor/model/rev.
+ *
+ * Rather than save it and have an ioctl that retrieves the saved
+ * value, have an ioctl that executes the same INQUIRY code used
+ * in scsi_probe_lun, let user level programs doing INQUIRY
+ * scanning run at their own risk, or supply a user level program
+ * that can correctly scan.
+ */
+ sdev->inquiry = kmalloc(sdev->inquiry_len, GFP_ATOMIC);
+ if (sdev->inquiry == NULL) {
+ return SCSI_SCAN_NO_RESPONSE;
+ }
+
+ memcpy(sdev->inquiry, inq_result, sdev->inquiry_len);
+ sdev->vendor = (char *) (sdev->inquiry + 8);
+ sdev->model = (char *) (sdev->inquiry + 16);
+ sdev->rev = (char *) (sdev->inquiry + 32);
+
+ if (*bflags & BLIST_ISROM) {
+ /*
+ * It would be better to modify sdev->type, and set
+ * sdev->removable, but then the print_inquiry() output
+ * would not show TYPE_ROM; if print_inquiry() is removed
+ * the issue goes away.
+ */
+ inq_result[0] = TYPE_ROM;
+ inq_result[1] |= 0x80; /* removable */
+ } else if (*bflags & BLIST_NO_ULD_ATTACH)
+ sdev->no_uld_attach = 1;
+
+ switch (sdev->type = (inq_result[0] & 0x1f)) {
+ case TYPE_TAPE:
+ case TYPE_DISK:
+ case TYPE_PRINTER:
+ case TYPE_MOD:
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ case TYPE_MEDIUM_CHANGER:
+ case TYPE_ENCLOSURE:
+ case TYPE_COMM:
+ sdev->writeable = 1;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ sdev->writeable = 0;
+ break;
+ default:
+ printk(KERN_INFO "scsi: unknown device type %d\n", sdev->type);
+ }
+
+ print_inquiry(inq_result);
+
+ /*
+ * For a peripheral qualifier (PQ) value of 1 (001b), the SCSI
+ * spec says: The device server is capable of supporting the
+ * specified peripheral device type on this logical unit. However,
+ * the physical device is not currently connected to this logical
+ * unit.
+ *
+ * The above is vague, as it implies that we could treat 001 and
+ * 011 the same. Stay compatible with previous code, and create a
+ * Scsi_Device for a PQ of 1
+ *
+ * Don't set the device offline here; rather let the upper
+ * level drivers eval the PQ to decide whether they should
+ * attach. So remove ((inq_result[0] >> 5) & 7) == 1 check.
+ */
+
+ sdev->inq_periph_qual = (inq_result[0] >> 5) & 7;
+ sdev->removable = (0x80 & inq_result[1]) >> 7;
+ sdev->lockable = sdev->removable;
+ sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2);
+
+ if (sdev->scsi_level >= SCSI_3 || (sdev->inquiry_len > 56 &&
+ inq_result[56] & 0x04))
+ sdev->ppr = 1;
+ if (inq_result[7] & 0x60)
+ sdev->wdtr = 1;
+ if (inq_result[7] & 0x10)
+ sdev->sdtr = 1;
+
+ sprintf(sdev->devfs_name, "scsi/host%d/bus%d/target%d/lun%d",
+ sdev->host->host_no, sdev->channel,
+ sdev->id, sdev->lun);
+
+ /*
+ * End driverfs/devfs code.
+ */
+
+ if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) &&
+ !(*bflags & BLIST_NOTQ))
+ sdev->tagged_supported = 1;
+ /*
+ * Some devices (Texel CD ROM drives) have handshaking problems
+ * when used with the Seagate controllers. borken is initialized
+ * to 1, and then set it to 0 here.
+ */
+ if ((*bflags & BLIST_BORKEN) == 0)
+ sdev->borken = 0;
+
+ /*
+ * Apparently some really broken devices (contrary to the SCSI
+ * standards) need to be selected without asserting ATN
+ */
+ if (*bflags & BLIST_SELECT_NO_ATN)
+ sdev->select_no_atn = 1;
+
+ /*
+ * Some devices may not want to have a start command automatically
+ * issued when a device is added.
+ */
+ if (*bflags & BLIST_NOSTARTONADD)
+ sdev->no_start_on_add = 1;
+
+ if (*bflags & BLIST_SINGLELUN)
+ sdev->single_lun = 1;
+
+
+ sdev->use_10_for_rw = 1;
+
+ if (*bflags & BLIST_MS_SKIP_PAGE_08)
+ sdev->skip_ms_page_8 = 1;
+
+ if (*bflags & BLIST_MS_SKIP_PAGE_3F)
+ sdev->skip_ms_page_3f = 1;
+
+ if (*bflags & BLIST_USE_10_BYTE_MS)
+ sdev->use_10_for_ms = 1;
+
+ /* set the device running here so that slave configure
+ * may do I/O */
+ scsi_device_set_state(sdev, SDEV_RUNNING);
+
+ if (*bflags & BLIST_MS_192_BYTES_FOR_3F)
+ sdev->use_192_bytes_for_3f = 1;
+
+ if (*bflags & BLIST_NOT_LOCKABLE)
+ sdev->lockable = 0;
+
+ if (*bflags & BLIST_RETRY_HWERROR)
+ sdev->retry_hwerror = 1;
+
+ transport_configure_device(&sdev->sdev_gendev);
+
+ if (sdev->host->hostt->slave_configure)
+ sdev->host->hostt->slave_configure(sdev);
+
+ /*
+ * Ok, the device is now all set up, we can
+ * register it and tell the rest of the kernel
+ * about it.
+ */
+ scsi_sysfs_add_sdev(sdev);
+
+ return SCSI_SCAN_LUN_PRESENT;
+}
+
+/**
+ * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it
+ * @starget: pointer to target device structure
+ * @lun: LUN of target device
+ * @sdevscan: probe the LUN corresponding to this Scsi_Device
+ * @sdevnew: store the value of any new Scsi_Device allocated
+ * @bflagsp: store bflags here if not NULL
+ *
+ * Description:
+ * Call scsi_probe_lun, if a LUN with an attached device is found,
+ * allocate and set it up by calling scsi_add_lun.
+ *
+ * Return:
+ * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device
+ * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is
+ * attached at the LUN
+ * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
+ **/
+static int scsi_probe_and_add_lun(struct scsi_target *starget,
+ uint lun, int *bflagsp,
+ struct scsi_device **sdevp, int rescan,
+ void *hostdata)
+{
+ struct scsi_device *sdev;
+ struct scsi_request *sreq;
+ unsigned char *result;
+ int bflags, res = SCSI_SCAN_NO_RESPONSE;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+
+ /*
+ * The rescan flag is used as an optimization, the first scan of a
+ * host adapter calls into here with rescan == 0.
+ */
+ if (rescan) {
+ sdev = scsi_device_lookup_by_target(starget, lun);
+ if (sdev) {
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
+ "scsi scan: device exists on %s\n",
+ sdev->sdev_gendev.bus_id));
+ if (sdevp)
+ *sdevp = sdev;
+ else
+ scsi_device_put(sdev);
+
+ if (bflagsp)
+ *bflagsp = scsi_get_device_flags(sdev,
+ sdev->vendor,
+ sdev->model);
+ return SCSI_SCAN_LUN_PRESENT;
+ }
+ }
+
+ sdev = scsi_alloc_sdev(starget, lun, hostdata);
+ if (!sdev)
+ goto out;
+ sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
+ if (!sreq)
+ goto out_free_sdev;
+ result = kmalloc(256, GFP_ATOMIC |
+ (shost->unchecked_isa_dma) ? __GFP_DMA : 0);
+ if (!result)
+ goto out_free_sreq;
+
+ scsi_probe_lun(sreq, result, &bflags);
+ if (sreq->sr_result)
+ goto out_free_result;
+
+ /*
+ * result contains valid SCSI INQUIRY data.
+ */
+ if ((result[0] >> 5) == 3) {
+ /*
+ * For a Peripheral qualifier 3 (011b), the SCSI
+ * spec says: The device server is not capable of
+ * supporting a physical device on this logical
+ * unit.
+ *
+ * For disks, this implies that there is no
+ * logical disk configured at sdev->lun, but there
+ * is a target id responding.
+ */
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
+ "scsi scan: peripheral qualifier of 3,"
+ " no device added\n"));
+ res = SCSI_SCAN_TARGET_PRESENT;
+ goto out_free_result;
+ }
+
+ res = scsi_add_lun(sdev, result, &bflags);
+ if (res == SCSI_SCAN_LUN_PRESENT) {
+ if (bflags & BLIST_KEY) {
+ sdev->lockable = 0;
+ scsi_unlock_floptical(sreq, result);
+ }
+ if (bflagsp)
+ *bflagsp = bflags;
+ }
+
+ out_free_result:
+ kfree(result);
+ out_free_sreq:
+ scsi_release_request(sreq);
+ out_free_sdev:
+ if (res == SCSI_SCAN_LUN_PRESENT) {
+ if (sdevp) {
+ scsi_device_get(sdev);
+ *sdevp = sdev;
+ }
+ } else {
+ if (sdev->host->hostt->slave_destroy)
+ sdev->host->hostt->slave_destroy(sdev);
+ transport_destroy_device(&sdev->sdev_gendev);
+ put_device(&sdev->sdev_gendev);
+ }
+ out:
+ return res;
+}
+
+/**
+ * scsi_sequential_lun_scan - sequentially scan a SCSI target
+ * @starget: pointer to target structure to scan
+ * @bflags: black/white list flag for LUN 0
+ * @lun0_res: result of scanning LUN 0
+ *
+ * Description:
+ * Generally, scan from LUN 1 (LUN 0 is assumed to already have been
+ * scanned) to some maximum lun until a LUN is found with no device
+ * attached. Use the bflags to figure out any oddities.
+ *
+ * Modifies sdevscan->lun.
+ **/
+static void scsi_sequential_lun_scan(struct scsi_target *starget,
+ int bflags, int lun0_res, int scsi_level,
+ int rescan)
+{
+ unsigned int sparse_lun, lun, max_dev_lun;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of"
+ "%s\n", starget->dev.bus_id));
+
+ max_dev_lun = min(max_scsi_luns, shost->max_lun);
+ /*
+ * If this device is known to support sparse multiple units,
+ * override the other settings, and scan all of them. Normally,
+ * SCSI-3 devices should be scanned via the REPORT LUNS.
+ */
+ if (bflags & BLIST_SPARSELUN) {
+ max_dev_lun = shost->max_lun;
+ sparse_lun = 1;
+ } else
+ sparse_lun = 0;
+
+ /*
+ * If not sparse lun and no device attached at LUN 0 do not scan
+ * any further.
+ */
+ if (!sparse_lun && (lun0_res != SCSI_SCAN_LUN_PRESENT))
+ return;
+
+ /*
+ * If less than SCSI_1_CSS, and no special lun scaning, stop
+ * scanning; this matches 2.4 behaviour, but could just be a bug
+ * (to continue scanning a SCSI_1_CSS device).
+ *
+ * This test is broken. We might not have any device on lun0 for
+ * a sparselun device, and if that's the case then how would we
+ * know the real scsi_level, eh? It might make sense to just not
+ * scan any SCSI_1 device for non-0 luns, but that check would best
+ * go into scsi_alloc_sdev() and just have it return null when asked
+ * to alloc an sdev for lun > 0 on an already found SCSI_1 device.
+ *
+ if ((sdevscan->scsi_level < SCSI_1_CCS) &&
+ ((bflags & (BLIST_FORCELUN | BLIST_SPARSELUN | BLIST_MAX5LUN))
+ == 0))
+ return;
+ */
+ /*
+ * If this device is known to support multiple units, override
+ * the other settings, and scan all of them.
+ */
+ if (bflags & BLIST_FORCELUN)
+ max_dev_lun = shost->max_lun;
+ /*
+ * REGAL CDC-4X: avoid hang after LUN 4
+ */
+ if (bflags & BLIST_MAX5LUN)
+ max_dev_lun = min(5U, max_dev_lun);
+ /*
+ * Do not scan SCSI-2 or lower device past LUN 7, unless
+ * BLIST_LARGELUN.
+ */
+ if (scsi_level < SCSI_3 && !(bflags & BLIST_LARGELUN))
+ max_dev_lun = min(8U, max_dev_lun);
+
+ /*
+ * We have already scanned LUN 0, so start at LUN 1. Keep scanning
+ * until we reach the max, or no LUN is found and we are not
+ * sparse_lun.
+ */
+ for (lun = 1; lun < max_dev_lun; ++lun)
+ if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
+ NULL) != SCSI_SCAN_LUN_PRESENT) &&
+ !sparse_lun)
+ return;
+}
+
+/**
+ * scsilun_to_int: convert a scsi_lun to an int
+ * @scsilun: struct scsi_lun to be converted.
+ *
+ * Description:
+ * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered
+ * integer, and return the result. The caller must check for
+ * truncation before using this function.
+ *
+ * Notes:
+ * The struct scsi_lun is assumed to be four levels, with each level
+ * effectively containing a SCSI byte-ordered (big endian) short; the
+ * addressing bits of each level are ignored (the highest two bits).
+ * For a description of the LUN format, post SCSI-3 see the SCSI
+ * Architecture Model, for SCSI-3 see the SCSI Controller Commands.
+ *
+ * Given a struct scsi_lun of: 0a 04 0b 03 00 00 00 00, this function returns
+ * the integer: 0x0b030a04
+ **/
+static int scsilun_to_int(struct scsi_lun *scsilun)
+{
+ int i;
+ unsigned int lun;
+
+ lun = 0;
+ for (i = 0; i < sizeof(lun); i += 2)
+ lun = lun | (((scsilun->scsi_lun[i] << 8) |
+ scsilun->scsi_lun[i + 1]) << (i * 8));
+ return lun;
+}
+
+/**
+ * scsi_report_lun_scan - Scan using SCSI REPORT LUN results
+ * @sdevscan: scan the host, channel, and id of this Scsi_Device
+ *
+ * Description:
+ * If @sdevscan is for a SCSI-3 or up device, send a REPORT LUN
+ * command, and scan the resulting list of LUNs by calling
+ * scsi_probe_and_add_lun.
+ *
+ * Modifies sdevscan->lun.
+ *
+ * Return:
+ * 0: scan completed (or no memory, so further scanning is futile)
+ * 1: no report lun scan, or not configured
+ **/
+static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
+ int rescan)
+{
+ char devname[64];
+ unsigned char scsi_cmd[MAX_COMMAND_SIZE];
+ unsigned int length;
+ unsigned int lun;
+ unsigned int num_luns;
+ unsigned int retries;
+ struct scsi_lun *lunp, *lun_data;
+ struct scsi_request *sreq;
+ u8 *data;
+ struct scsi_sense_hdr sshdr;
+ struct scsi_target *starget = scsi_target(sdev);
+
+ /*
+ * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
+ * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
+ * support more than 8 LUNs.
+ */
+ if ((bflags & BLIST_NOREPORTLUN) ||
+ sdev->scsi_level < SCSI_2 ||
+ (sdev->scsi_level < SCSI_3 &&
+ (!(bflags & BLIST_REPORTLUN2) || sdev->host->max_lun <= 8)) )
+ return 1;
+ if (bflags & BLIST_NOLUN)
+ return 0;
+
+ sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
+ if (!sreq)
+ goto out;
+
+ sprintf(devname, "host %d channel %d id %d",
+ sdev->host->host_no, sdev->channel, sdev->id);
+
+ /*
+ * Allocate enough to hold the header (the same size as one scsi_lun)
+ * plus the max number of luns we are requesting.
+ *
+ * Reallocating and trying again (with the exact amount we need)
+ * would be nice, but then we need to somehow limit the size
+ * allocated based on the available memory and the limits of
+ * kmalloc - we don't want a kmalloc() failure of a huge value to
+ * prevent us from finding any LUNs on this target.
+ */
+ length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun);
+ lun_data = kmalloc(length, GFP_ATOMIC |
+ (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0));
+ if (!lun_data)
+ goto out_release_request;
+
+ scsi_cmd[0] = REPORT_LUNS;
+
+ /*
+ * bytes 1 - 5: reserved, set to zero.
+ */
+ memset(&scsi_cmd[1], 0, 5);
+
+ /*
+ * bytes 6 - 9: length of the command.
+ */
+ scsi_cmd[6] = (unsigned char) (length >> 24) & 0xff;
+ scsi_cmd[7] = (unsigned char) (length >> 16) & 0xff;
+ scsi_cmd[8] = (unsigned char) (length >> 8) & 0xff;
+ scsi_cmd[9] = (unsigned char) length & 0xff;
+
+ scsi_cmd[10] = 0; /* reserved */
+ scsi_cmd[11] = 0; /* control */
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+
+ /*
+ * We can get a UNIT ATTENTION, for example a power on/reset, so
+ * retry a few times (like sd.c does for TEST UNIT READY).
+ * Experience shows some combinations of adapter/devices get at
+ * least two power on/resets.
+ *
+ * Illegal requests (for devices that do not support REPORT LUNS)
+ * should come through as a check condition, and will not generate
+ * a retry.
+ */
+ for (retries = 0; retries < 3; retries++) {
+ SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending"
+ " REPORT LUNS to %s (try %d)\n", devname,
+ retries));
+ scsi_wait_req(sreq, scsi_cmd, lun_data, length,
+ SCSI_TIMEOUT + 4*HZ, 3);
+ SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS"
+ " %s (try %d) result 0x%x\n", sreq->sr_result
+ ? "failed" : "successful", retries,
+ sreq->sr_result));
+ if (sreq->sr_result == 0)
+ break;
+ else if (scsi_request_normalize_sense(sreq, &sshdr)) {
+ if (sshdr.sense_key != UNIT_ATTENTION)
+ break;
+ }
+ }
+
+ if (sreq->sr_result) {
+ /*
+ * The device probably does not support a REPORT LUN command
+ */
+ kfree(lun_data);
+ scsi_release_request(sreq);
+ return 1;
+ }
+ scsi_release_request(sreq);
+
+ /*
+ * Get the length from the first four bytes of lun_data.
+ */
+ data = (u8 *) lun_data->scsi_lun;
+ length = ((data[0] << 24) | (data[1] << 16) |
+ (data[2] << 8) | (data[3] << 0));
+
+ num_luns = (length / sizeof(struct scsi_lun));
+ if (num_luns > max_scsi_report_luns) {
+ printk(KERN_WARNING "scsi: On %s only %d (max_scsi_report_luns)"
+ " of %d luns reported, try increasing"
+ " max_scsi_report_luns.\n", devname,
+ max_scsi_report_luns, num_luns);
+ num_luns = max_scsi_report_luns;
+ }
+
+ SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of"
+ " host %d channel %d id %d\n", sdev->host->host_no,
+ sdev->channel, sdev->id));
+
+ /*
+ * Scan the luns in lun_data. The entry at offset 0 is really
+ * the header, so start at 1 and go up to and including num_luns.
+ */
+ for (lunp = &lun_data[1]; lunp <= &lun_data[num_luns]; lunp++) {
+ lun = scsilun_to_int(lunp);
+
+ /*
+ * Check if the unused part of lunp is non-zero, and so
+ * does not fit in lun.
+ */
+ if (memcmp(&lunp->scsi_lun[sizeof(lun)], "\0\0\0\0", 4)) {
+ int i;
+
+ /*
+ * Output an error displaying the LUN in byte order,
+ * this differs from what linux would print for the
+ * integer LUN value.
+ */
+ printk(KERN_WARNING "scsi: %s lun 0x", devname);
+ data = (char *)lunp->scsi_lun;
+ for (i = 0; i < sizeof(struct scsi_lun); i++)
+ printk("%02x", data[i]);
+ printk(" has a LUN larger than currently supported.\n");
+ } else if (lun == 0) {
+ /*
+ * LUN 0 has already been scanned.
+ */
+ } else if (lun > sdev->host->max_lun) {
+ printk(KERN_WARNING "scsi: %s lun%d has a LUN larger"
+ " than allowed by the host adapter\n",
+ devname, lun);
+ } else {
+ int res;
+
+ res = scsi_probe_and_add_lun(starget,
+ lun, NULL, NULL, rescan, NULL);
+ if (res == SCSI_SCAN_NO_RESPONSE) {
+ /*
+ * Got some results, but now none, abort.
+ */
+ printk(KERN_ERR "scsi: Unexpected response"
+ " from %s lun %d while scanning, scan"
+ " aborted\n", devname, lun);
+ break;
+ }
+ }
+ }
+
+ kfree(lun_data);
+ return 0;
+
+ out_release_request:
+ scsi_release_request(sreq);
+ out:
+ /*
+ * We are out of memory, don't try scanning any further.
+ */
+ printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+ return 0;
+}
+
+struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun, void *hostdata)
+{
+ struct scsi_device *sdev;
+ struct device *parent = &shost->shost_gendev;
+ int res;
+ struct scsi_target *starget = scsi_alloc_target(parent, channel, id);
+
+ if (!starget)
+ return ERR_PTR(-ENOMEM);
+
+ down(&shost->scan_mutex);
+ res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
+ if (res != SCSI_SCAN_LUN_PRESENT)
+ sdev = ERR_PTR(-ENODEV);
+ up(&shost->scan_mutex);
+ scsi_target_reap(starget);
+ put_device(&starget->dev);
+
+ return sdev;
+}
+EXPORT_SYMBOL(__scsi_add_device);
+
+void scsi_rescan_device(struct device *dev)
+{
+ struct scsi_driver *drv;
+
+ if (!dev->driver)
+ return;
+
+ drv = to_scsi_driver(dev->driver);
+ if (try_module_get(drv->owner)) {
+ if (drv->rescan)
+ drv->rescan(dev);
+ module_put(drv->owner);
+ }
+}
+EXPORT_SYMBOL(scsi_rescan_device);
+
+/**
+ * scsi_scan_target - scan a target id, possibly including all LUNs on the
+ * target.
+ * @sdevsca: Scsi_Device handle for scanning
+ * @shost: host to scan
+ * @channel: channel to scan
+ * @id: target id to scan
+ *
+ * Description:
+ * Scan the target id on @shost, @channel, and @id. Scan at least LUN
+ * 0, and possibly all LUNs on the target id.
+ *
+ * Use the pre-allocated @sdevscan as a handle for the scanning. This
+ * function sets sdevscan->host, sdevscan->id and sdevscan->lun; the
+ * scanning functions modify sdevscan->lun.
+ *
+ * First try a REPORT LUN scan, if that does not scan the target, do a
+ * sequential scan of LUNs on the target id.
+ **/
+void scsi_scan_target(struct device *parent, unsigned int channel,
+ unsigned int id, unsigned int lun, int rescan)
+{
+ struct Scsi_Host *shost = dev_to_shost(parent);
+ int bflags = 0;
+ int res;
+ struct scsi_device *sdev = NULL;
+ struct scsi_target *starget;
+
+ if (shost->this_id == id)
+ /*
+ * Don't scan the host adapter
+ */
+ return;
+
+
+ starget = scsi_alloc_target(parent, channel, id);
+
+ if (!starget)
+ return;
+
+ get_device(&starget->dev);
+ if (lun != SCAN_WILD_CARD) {
+ /*
+ * Scan for a specific host/chan/id/lun.
+ */
+ scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);
+ goto out_reap;
+ }
+
+ /*
+ * Scan LUN 0, if there is some response, scan further. Ideally, we
+ * would not configure LUN 0 until all LUNs are scanned.
+ */
+ res = scsi_probe_and_add_lun(starget, 0, &bflags, &sdev, rescan, NULL);
+ if (res == SCSI_SCAN_LUN_PRESENT) {
+ if (scsi_report_lun_scan(sdev, bflags, rescan) != 0)
+ /*
+ * The REPORT LUN did not scan the target,
+ * do a sequential scan.
+ */
+ scsi_sequential_lun_scan(starget, bflags,
+ res, sdev->scsi_level, rescan);
+ } else if (res == SCSI_SCAN_TARGET_PRESENT) {
+ /*
+ * There's a target here, but lun 0 is offline so we
+ * can't use the report_lun scan. Fall back to a
+ * sequential lun scan with a bflags of SPARSELUN and
+ * a default scsi level of SCSI_2
+ */
+ scsi_sequential_lun_scan(starget, BLIST_SPARSELUN,
+ SCSI_SCAN_TARGET_PRESENT, SCSI_2, rescan);
+ }
+ if (sdev)
+ scsi_device_put(sdev);
+
+ out_reap:
+ /* now determine if the target has any children at all
+ * and if not, nuke it */
+ scsi_target_reap(starget);
+
+ put_device(&starget->dev);
+}
+EXPORT_SYMBOL(scsi_scan_target);
+
+static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
+ unsigned int id, unsigned int lun, int rescan)
+{
+ uint order_id;
+
+ if (id == SCAN_WILD_CARD)
+ for (id = 0; id < shost->max_id; ++id) {
+ /*
+ * XXX adapter drivers when possible (FCP, iSCSI)
+ * could modify max_id to match the current max,
+ * not the absolute max.
+ *
+ * XXX add a shost id iterator, so for example,
+ * the FC ID can be the same as a target id
+ * without a huge overhead of sparse id's.
+ */
+ if (shost->reverse_ordering)
+ /*
+ * Scan from high to low id.
+ */
+ order_id = shost->max_id - id - 1;
+ else
+ order_id = id;
+ scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan);
+ }
+ else
+ scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan);
+}
+
+int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
+ unsigned int id, unsigned int lun, int rescan)
+{
+ SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "%s: <%u:%u:%u:%u>\n",
+ __FUNCTION__, shost->host_no, channel, id, lun));
+
+ if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
+ ((id != SCAN_WILD_CARD) && (id > shost->max_id)) ||
+ ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
+ return -EINVAL;
+
+ down(&shost->scan_mutex);
+ if (channel == SCAN_WILD_CARD)
+ for (channel = 0; channel <= shost->max_channel; channel++)
+ scsi_scan_channel(shost, channel, id, lun, rescan);
+ else
+ scsi_scan_channel(shost, channel, id, lun, rescan);
+ up(&shost->scan_mutex);
+
+ return 0;
+}
+
+/**
+ * scsi_scan_host - scan the given adapter
+ * @shost: adapter to scan
+ **/
+void scsi_scan_host(struct Scsi_Host *shost)
+{
+ scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
+ SCAN_WILD_CARD, 0);
+}
+EXPORT_SYMBOL(scsi_scan_host);
+
+/**
+ * scsi_scan_single_target - scan the given SCSI target
+ * @shost: adapter to scan
+ * @chan: channel to scan
+ * @id: target id to scan
+ **/
+void scsi_scan_single_target(struct Scsi_Host *shost,
+ unsigned int chan, unsigned int id)
+{
+ scsi_scan_host_selected(shost, chan, id, SCAN_WILD_CARD, 1);
+}
+EXPORT_SYMBOL(scsi_scan_single_target);
+
+void scsi_forget_host(struct Scsi_Host *shost)
+{
+ struct scsi_target *starget, *tmp;
+ unsigned long flags;
+
+ /*
+ * Ok, this look a bit strange. We always look for the first device
+ * on the list as scsi_remove_device removes them from it - thus we
+ * also have to release the lock.
+ * We don't need to get another reference to the device before
+ * releasing the lock as we already own the reference from
+ * scsi_register_device that's release in scsi_remove_device. And
+ * after that we don't look at sdev anymore.
+ */
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_for_each_entry_safe(starget, tmp, &shost->__targets, siblings) {
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_remove_target(&starget->dev);
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+/*
+ * Function: scsi_get_host_dev()
+ *
+ * Purpose: Create a Scsi_Device that points to the host adapter itself.
+ *
+ * Arguments: SHpnt - Host that needs a Scsi_Device
+ *
+ * Lock status: None assumed.
+ *
+ * Returns: The Scsi_Device or NULL
+ *
+ * Notes:
+ * Attach a single Scsi_Device to the Scsi_Host - this should
+ * be made to look like a "pseudo-device" that points to the
+ * HA itself.
+ *
+ * Note - this device is not accessible from any high-level
+ * drivers (including generics), which is probably not
+ * optimal. We can add hooks later to attach
+ */
+struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+ struct scsi_target *starget;
+
+ starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id);
+ if (!starget)
+ return NULL;
+
+ sdev = scsi_alloc_sdev(starget, 0, NULL);
+ if (sdev) {
+ sdev->sdev_gendev.parent = get_device(&starget->dev);
+ sdev->borken = 0;
+ }
+ put_device(&starget->dev);
+ return sdev;
+}
+EXPORT_SYMBOL(scsi_get_host_dev);
+
+/*
+ * Function: scsi_free_host_dev()
+ *
+ * Purpose: Free a scsi_device that points to the host adapter itself.
+ *
+ * Arguments: SHpnt - Host that needs a Scsi_Device
+ *
+ * Lock status: None assumed.
+ *
+ * Returns: Nothing
+ *
+ * Notes:
+ */
+void scsi_free_host_dev(struct scsi_device *sdev)
+{
+ BUG_ON(sdev->id != sdev->host->this_id);
+
+ if (sdev->host->hostt->slave_destroy)
+ sdev->host->hostt->slave_destroy(sdev);
+ transport_destroy_device(&sdev->sdev_gendev);
+ put_device(&sdev->sdev_gendev);
+}
+EXPORT_SYMBOL(scsi_free_host_dev);
+
diff --git a/drivers/scsi/scsi_sysctl.c b/drivers/scsi/scsi_sysctl.c
new file mode 100644
index 000000000000..04d06c25132b
--- /dev/null
+++ b/drivers/scsi/scsi_sysctl.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2003 Christoph Hellwig.
+ * Released under GPL v2.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sysctl.h>
+
+#include "scsi_logging.h"
+
+
+static ctl_table scsi_table[] = {
+ { .ctl_name = DEV_SCSI_LOGGING_LEVEL,
+ .procname = "logging_level",
+ .data = &scsi_logging_level,
+ .maxlen = sizeof(scsi_logging_level),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec },
+ { }
+};
+
+static ctl_table scsi_dir_table[] = {
+ { .ctl_name = DEV_SCSI,
+ .procname = "scsi",
+ .mode = 0555,
+ .child = scsi_table },
+ { }
+};
+
+static ctl_table scsi_root_table[] = {
+ { .ctl_name = CTL_DEV,
+ .procname = "dev",
+ .mode = 0555,
+ .child = scsi_dir_table },
+ { }
+};
+
+static struct ctl_table_header *scsi_table_header;
+
+int __init scsi_init_sysctl(void)
+{
+ scsi_table_header = register_sysctl_table(scsi_root_table, 1);
+ if (!scsi_table_header)
+ return -ENOMEM;
+ return 0;
+}
+
+void scsi_exit_sysctl(void)
+{
+ unregister_sysctl_table(scsi_table_header);
+}
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
new file mode 100644
index 000000000000..134d3a3e4222
--- /dev/null
+++ b/drivers/scsi/scsi_sysfs.c
@@ -0,0 +1,816 @@
+/*
+ * scsi_sysfs.c
+ *
+ * SCSI sysfs interface routines.
+ *
+ * Created to pull SCSI mid layer sysfs routines into one file.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/device.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport.h>
+
+#include "scsi_priv.h"
+#include "scsi_logging.h"
+
+static struct {
+ enum scsi_device_state value;
+ char *name;
+} sdev_states[] = {
+ { SDEV_CREATED, "created" },
+ { SDEV_RUNNING, "running" },
+ { SDEV_CANCEL, "cancel" },
+ { SDEV_DEL, "deleted" },
+ { SDEV_QUIESCE, "quiesce" },
+ { SDEV_OFFLINE, "offline" },
+ { SDEV_BLOCK, "blocked" },
+};
+
+const char *scsi_device_state_name(enum scsi_device_state state)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) {
+ if (sdev_states[i].value == state) {
+ name = sdev_states[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+static int check_set(unsigned int *val, char *src)
+{
+ char *last;
+
+ if (strncmp(src, "-", 20) == 0) {
+ *val = SCAN_WILD_CARD;
+ } else {
+ /*
+ * Doesn't check for int overflow
+ */
+ *val = simple_strtoul(src, &last, 0);
+ if (*last != '\0')
+ return 1;
+ }
+ return 0;
+}
+
+static int scsi_scan(struct Scsi_Host *shost, const char *str)
+{
+ char s1[15], s2[15], s3[15], junk;
+ unsigned int channel, id, lun;
+ int res;
+
+ res = sscanf(str, "%10s %10s %10s %c", s1, s2, s3, &junk);
+ if (res != 3)
+ return -EINVAL;
+ if (check_set(&channel, s1))
+ return -EINVAL;
+ if (check_set(&id, s2))
+ return -EINVAL;
+ if (check_set(&lun, s3))
+ return -EINVAL;
+ res = scsi_scan_host_selected(shost, channel, id, lun, 1);
+ return res;
+}
+
+/*
+ * shost_show_function: macro to create an attr function that can be used to
+ * show a non-bit field.
+ */
+#define shost_show_function(name, field, format_string) \
+static ssize_t \
+show_##name (struct class_device *class_dev, char *buf) \
+{ \
+ struct Scsi_Host *shost = class_to_shost(class_dev); \
+ return snprintf (buf, 20, format_string, shost->field); \
+}
+
+/*
+ * shost_rd_attr: macro to create a function and attribute variable for a
+ * read only field.
+ */
+#define shost_rd_attr2(name, field, format_string) \
+ shost_show_function(name, field, format_string) \
+static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+#define shost_rd_attr(field, format_string) \
+shost_rd_attr2(field, field, format_string)
+
+/*
+ * Create the actual show/store functions and data structures.
+ */
+
+static ssize_t store_scan(struct class_device *class_dev, const char *buf,
+ size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ int res;
+
+ res = scsi_scan(shost, buf);
+ if (res == 0)
+ res = count;
+ return res;
+};
+static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan);
+
+shost_rd_attr(unique_id, "%u\n");
+shost_rd_attr(host_busy, "%hu\n");
+shost_rd_attr(cmd_per_lun, "%hd\n");
+shost_rd_attr(sg_tablesize, "%hu\n");
+shost_rd_attr(unchecked_isa_dma, "%d\n");
+shost_rd_attr2(proc_name, hostt->proc_name, "%s\n");
+
+static struct class_device_attribute *scsi_sysfs_shost_attrs[] = {
+ &class_device_attr_unique_id,
+ &class_device_attr_host_busy,
+ &class_device_attr_cmd_per_lun,
+ &class_device_attr_sg_tablesize,
+ &class_device_attr_unchecked_isa_dma,
+ &class_device_attr_proc_name,
+ &class_device_attr_scan,
+ NULL
+};
+
+static void scsi_device_cls_release(struct class_device *class_dev)
+{
+ struct scsi_device *sdev;
+
+ sdev = class_to_sdev(class_dev);
+ put_device(&sdev->sdev_gendev);
+}
+
+void scsi_device_dev_release(struct device *dev)
+{
+ struct scsi_device *sdev;
+ struct device *parent;
+ struct scsi_target *starget;
+ unsigned long flags;
+
+ parent = dev->parent;
+ sdev = to_scsi_device(dev);
+ starget = to_scsi_target(parent);
+
+ spin_lock_irqsave(sdev->host->host_lock, flags);
+ starget->reap_ref++;
+ list_del(&sdev->siblings);
+ list_del(&sdev->same_target_siblings);
+ list_del(&sdev->starved_entry);
+ spin_unlock_irqrestore(sdev->host->host_lock, flags);
+
+ if (sdev->request_queue) {
+ sdev->request_queue->queuedata = NULL;
+ scsi_free_queue(sdev->request_queue);
+ }
+
+ scsi_target_reap(scsi_target(sdev));
+
+ kfree(sdev->inquiry);
+ kfree(sdev);
+
+ if (parent)
+ put_device(parent);
+}
+
+struct class sdev_class = {
+ .name = "scsi_device",
+ .release = scsi_device_cls_release,
+};
+
+/* all probing is done in the individual ->probe routines */
+static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
+{
+ struct scsi_device *sdp = to_scsi_device(dev);
+ if (sdp->no_uld_attach)
+ return 0;
+ return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
+}
+
+struct bus_type scsi_bus_type = {
+ .name = "scsi",
+ .match = scsi_bus_match,
+};
+
+int scsi_sysfs_register(void)
+{
+ int error;
+
+ error = bus_register(&scsi_bus_type);
+ if (!error) {
+ error = class_register(&sdev_class);
+ if (error)
+ bus_unregister(&scsi_bus_type);
+ }
+
+ return error;
+}
+
+void scsi_sysfs_unregister(void)
+{
+ class_unregister(&sdev_class);
+ bus_unregister(&scsi_bus_type);
+}
+
+/*
+ * sdev_show_function: macro to create an attr function that can be used to
+ * show a non-bit field.
+ */
+#define sdev_show_function(field, format_string) \
+static ssize_t \
+sdev_show_##field (struct device *dev, char *buf) \
+{ \
+ struct scsi_device *sdev; \
+ sdev = to_scsi_device(dev); \
+ return snprintf (buf, 20, format_string, sdev->field); \
+} \
+
+/*
+ * sdev_rd_attr: macro to create a function and attribute variable for a
+ * read only field.
+ */
+#define sdev_rd_attr(field, format_string) \
+ sdev_show_function(field, format_string) \
+static DEVICE_ATTR(field, S_IRUGO, sdev_show_##field, NULL);
+
+
+/*
+ * sdev_rd_attr: create a function and attribute variable for a
+ * read/write field.
+ */
+#define sdev_rw_attr(field, format_string) \
+ sdev_show_function(field, format_string) \
+ \
+static ssize_t \
+sdev_store_##field (struct device *dev, const char *buf, size_t count) \
+{ \
+ struct scsi_device *sdev; \
+ sdev = to_scsi_device(dev); \
+ snscanf (buf, 20, format_string, &sdev->field); \
+ return count; \
+} \
+static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##field);
+
+/* Currently we don't export bit fields, but we might in future,
+ * so leave this code in */
+#if 0
+/*
+ * sdev_rd_attr: create a function and attribute variable for a
+ * read/write bit field.
+ */
+#define sdev_rw_attr_bit(field) \
+ sdev_show_function(field, "%d\n") \
+ \
+static ssize_t \
+sdev_store_##field (struct device *dev, const char *buf, size_t count) \
+{ \
+ int ret; \
+ struct scsi_device *sdev; \
+ ret = scsi_sdev_check_buf_bit(buf); \
+ if (ret >= 0) { \
+ sdev = to_scsi_device(dev); \
+ sdev->field = ret; \
+ ret = count; \
+ } \
+ return ret; \
+} \
+static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##field);
+
+/*
+ * scsi_sdev_check_buf_bit: return 0 if buf is "0", return 1 if buf is "1",
+ * else return -EINVAL.
+ */
+static int scsi_sdev_check_buf_bit(const char *buf)
+{
+ if ((buf[1] == '\0') || ((buf[1] == '\n') && (buf[2] == '\0'))) {
+ if (buf[0] == '1')
+ return 1;
+ else if (buf[0] == '0')
+ return 0;
+ else
+ return -EINVAL;
+ } else
+ return -EINVAL;
+}
+#endif
+/*
+ * Create the actual show/store functions and data structures.
+ */
+sdev_rd_attr (device_blocked, "%d\n");
+sdev_rd_attr (queue_depth, "%d\n");
+sdev_rd_attr (type, "%d\n");
+sdev_rd_attr (scsi_level, "%d\n");
+sdev_rd_attr (vendor, "%.8s\n");
+sdev_rd_attr (model, "%.16s\n");
+sdev_rd_attr (rev, "%.4s\n");
+
+static ssize_t
+sdev_show_timeout (struct device *dev, char *buf)
+{
+ struct scsi_device *sdev;
+ sdev = to_scsi_device(dev);
+ return snprintf (buf, 20, "%d\n", sdev->timeout / HZ);
+}
+
+static ssize_t
+sdev_store_timeout (struct device *dev, const char *buf, size_t count)
+{
+ struct scsi_device *sdev;
+ int timeout;
+ sdev = to_scsi_device(dev);
+ sscanf (buf, "%d\n", &timeout);
+ sdev->timeout = timeout * HZ;
+ return count;
+}
+static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout);
+
+static ssize_t
+store_rescan_field (struct device *dev, const char *buf, size_t count)
+{
+ scsi_rescan_device(dev);
+ return count;
+}
+static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
+
+static ssize_t sdev_store_delete(struct device *dev, const char *buf,
+ size_t count)
+{
+ scsi_remove_device(to_scsi_device(dev));
+ return count;
+};
+static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
+
+static ssize_t
+store_state_field(struct device *dev, const char *buf, size_t count)
+{
+ int i;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ enum scsi_device_state state = 0;
+
+ for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) {
+ const int len = strlen(sdev_states[i].name);
+ if (strncmp(sdev_states[i].name, buf, len) == 0 &&
+ buf[len] == '\n') {
+ state = sdev_states[i].value;
+ break;
+ }
+ }
+ if (!state)
+ return -EINVAL;
+
+ if (scsi_device_set_state(sdev, state))
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t
+show_state_field(struct device *dev, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ const char *name = scsi_device_state_name(sdev->sdev_state);
+
+ if (!name)
+ return -EINVAL;
+
+ return snprintf(buf, 20, "%s\n", name);
+}
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
+
+static ssize_t
+show_queue_type_field(struct device *dev, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ const char *name = "none";
+
+ if (sdev->ordered_tags)
+ name = "ordered";
+ else if (sdev->simple_tags)
+ name = "simple";
+
+ return snprintf(buf, 20, "%s\n", name);
+}
+
+static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL);
+
+static ssize_t
+show_iostat_counterbits(struct device *dev, char *buf)
+{
+ return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8);
+}
+
+static DEVICE_ATTR(iocounterbits, S_IRUGO, show_iostat_counterbits, NULL);
+
+#define show_sdev_iostat(field) \
+static ssize_t \
+show_iostat_##field(struct device *dev, char *buf) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ unsigned long long count = atomic_read(&sdev->field); \
+ return snprintf(buf, 20, "0x%llx\n", count); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_iostat_##field, NULL)
+
+show_sdev_iostat(iorequest_cnt);
+show_sdev_iostat(iodone_cnt);
+show_sdev_iostat(ioerr_cnt);
+
+
+/* Default template for device attributes. May NOT be modified */
+static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
+ &dev_attr_device_blocked,
+ &dev_attr_queue_depth,
+ &dev_attr_queue_type,
+ &dev_attr_type,
+ &dev_attr_scsi_level,
+ &dev_attr_vendor,
+ &dev_attr_model,
+ &dev_attr_rev,
+ &dev_attr_rescan,
+ &dev_attr_delete,
+ &dev_attr_state,
+ &dev_attr_timeout,
+ &dev_attr_iocounterbits,
+ &dev_attr_iorequest_cnt,
+ &dev_attr_iodone_cnt,
+ &dev_attr_ioerr_cnt,
+ NULL
+};
+
+static ssize_t sdev_store_queue_depth_rw(struct device *dev, const char *buf,
+ size_t count)
+{
+ int depth, retval;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+
+ if (!sht->change_queue_depth)
+ return -EINVAL;
+
+ depth = simple_strtoul(buf, NULL, 0);
+
+ if (depth < 1)
+ return -EINVAL;
+
+ retval = sht->change_queue_depth(sdev, depth);
+ if (retval < 0)
+ return retval;
+
+ return count;
+}
+
+static struct device_attribute sdev_attr_queue_depth_rw =
+ __ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth,
+ sdev_store_queue_depth_rw);
+
+static ssize_t sdev_store_queue_type_rw(struct device *dev, const char *buf,
+ size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int tag_type = 0, retval;
+ int prev_tag_type = scsi_get_tag_type(sdev);
+
+ if (!sdev->tagged_supported || !sht->change_queue_type)
+ return -EINVAL;
+
+ if (strncmp(buf, "ordered", 7) == 0)
+ tag_type = MSG_ORDERED_TAG;
+ else if (strncmp(buf, "simple", 6) == 0)
+ tag_type = MSG_SIMPLE_TAG;
+ else if (strncmp(buf, "none", 4) != 0)
+ return -EINVAL;
+
+ if (tag_type == prev_tag_type)
+ return count;
+
+ retval = sht->change_queue_type(sdev, tag_type);
+ if (retval < 0)
+ return retval;
+
+ return count;
+}
+
+static struct device_attribute sdev_attr_queue_type_rw =
+ __ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field,
+ sdev_store_queue_type_rw);
+
+static struct device_attribute *attr_changed_internally(
+ struct Scsi_Host *shost,
+ struct device_attribute * attr)
+{
+ if (!strcmp("queue_depth", attr->attr.name)
+ && shost->hostt->change_queue_depth)
+ return &sdev_attr_queue_depth_rw;
+ else if (!strcmp("queue_type", attr->attr.name)
+ && shost->hostt->change_queue_type)
+ return &sdev_attr_queue_type_rw;
+ return attr;
+}
+
+
+static struct device_attribute *attr_overridden(
+ struct device_attribute **attrs,
+ struct device_attribute *attr)
+{
+ int i;
+
+ if (!attrs)
+ return NULL;
+ for (i = 0; attrs[i]; i++)
+ if (!strcmp(attrs[i]->attr.name, attr->attr.name))
+ return attrs[i];
+ return NULL;
+}
+
+static int attr_add(struct device *dev, struct device_attribute *attr)
+{
+ struct device_attribute *base_attr;
+
+ /*
+ * Spare the caller from having to copy things it's not interested in.
+ */
+ base_attr = attr_overridden(scsi_sysfs_sdev_attrs, attr);
+ if (base_attr) {
+ /* extend permissions */
+ attr->attr.mode |= base_attr->attr.mode;
+
+ /* override null show/store with default */
+ if (!attr->show)
+ attr->show = base_attr->show;
+ if (!attr->store)
+ attr->store = base_attr->store;
+ }
+
+ return device_create_file(dev, attr);
+}
+
+/**
+ * scsi_sysfs_add_sdev - add scsi device to sysfs
+ * @sdev: scsi_device to add
+ *
+ * Return value:
+ * 0 on Success / non-zero on Failure
+ **/
+int scsi_sysfs_add_sdev(struct scsi_device *sdev)
+{
+ int error, i;
+
+ if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
+ return error;
+
+ error = device_add(&sdev->sdev_gendev);
+ if (error) {
+ put_device(sdev->sdev_gendev.parent);
+ printk(KERN_INFO "error 1\n");
+ return error;
+ }
+ error = class_device_add(&sdev->sdev_classdev);
+ if (error) {
+ printk(KERN_INFO "error 2\n");
+ goto clean_device;
+ }
+
+ /* take a reference for the sdev_classdev; this is
+ * released by the sdev_class .release */
+ get_device(&sdev->sdev_gendev);
+ if (sdev->host->hostt->sdev_attrs) {
+ for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) {
+ error = attr_add(&sdev->sdev_gendev,
+ sdev->host->hostt->sdev_attrs[i]);
+ if (error) {
+ scsi_remove_device(sdev);
+ goto out;
+ }
+ }
+ }
+
+ for (i = 0; scsi_sysfs_sdev_attrs[i]; i++) {
+ if (!attr_overridden(sdev->host->hostt->sdev_attrs,
+ scsi_sysfs_sdev_attrs[i])) {
+ struct device_attribute * attr =
+ attr_changed_internally(sdev->host,
+ scsi_sysfs_sdev_attrs[i]);
+ error = device_create_file(&sdev->sdev_gendev, attr);
+ if (error) {
+ scsi_remove_device(sdev);
+ goto out;
+ }
+ }
+ }
+
+ transport_add_device(&sdev->sdev_gendev);
+ out:
+ return error;
+
+ clean_device:
+ scsi_device_set_state(sdev, SDEV_CANCEL);
+
+ device_del(&sdev->sdev_gendev);
+ transport_destroy_device(&sdev->sdev_gendev);
+ put_device(&sdev->sdev_gendev);
+
+ return error;
+}
+
+/**
+ * scsi_remove_device - unregister a device from the scsi bus
+ * @sdev: scsi_device to unregister
+ **/
+void scsi_remove_device(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+
+ down(&shost->scan_mutex);
+ if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
+ goto out;
+
+ class_device_unregister(&sdev->sdev_classdev);
+ device_del(&sdev->sdev_gendev);
+ scsi_device_set_state(sdev, SDEV_DEL);
+ if (sdev->host->hostt->slave_destroy)
+ sdev->host->hostt->slave_destroy(sdev);
+ transport_unregister_device(&sdev->sdev_gendev);
+ put_device(&sdev->sdev_gendev);
+out:
+ up(&shost->scan_mutex);
+}
+EXPORT_SYMBOL(scsi_remove_device);
+
+void __scsi_remove_target(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+ struct scsi_device *sdev, *tmp;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ starget->reap_ref++;
+ list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
+ if (sdev->channel != starget->channel ||
+ sdev->id != starget->id)
+ continue;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_remove_device(sdev);
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_target_reap(starget);
+}
+
+/**
+ * scsi_remove_target - try to remove a target and all its devices
+ * @dev: generic starget or parent of generic stargets to be removed
+ *
+ * Note: This is slightly racy. It is possible that if the user
+ * requests the addition of another device then the target won't be
+ * removed.
+ */
+void scsi_remove_target(struct device *dev)
+{
+ struct device *rdev, *idev, *next;
+
+ if (scsi_is_target_device(dev)) {
+ __scsi_remove_target(to_scsi_target(dev));
+ return;
+ }
+
+ rdev = get_device(dev);
+ list_for_each_entry_safe(idev, next, &dev->children, node) {
+ if (scsi_is_target_device(idev))
+ __scsi_remove_target(to_scsi_target(idev));
+ }
+ put_device(rdev);
+}
+EXPORT_SYMBOL(scsi_remove_target);
+
+int scsi_register_driver(struct device_driver *drv)
+{
+ drv->bus = &scsi_bus_type;
+
+ return driver_register(drv);
+}
+EXPORT_SYMBOL(scsi_register_driver);
+
+int scsi_register_interface(struct class_interface *intf)
+{
+ intf->class = &sdev_class;
+
+ return class_interface_register(intf);
+}
+EXPORT_SYMBOL(scsi_register_interface);
+
+
+static struct class_device_attribute *class_attr_overridden(
+ struct class_device_attribute **attrs,
+ struct class_device_attribute *attr)
+{
+ int i;
+
+ if (!attrs)
+ return NULL;
+ for (i = 0; attrs[i]; i++)
+ if (!strcmp(attrs[i]->attr.name, attr->attr.name))
+ return attrs[i];
+ return NULL;
+}
+
+static int class_attr_add(struct class_device *classdev,
+ struct class_device_attribute *attr)
+{
+ struct class_device_attribute *base_attr;
+
+ /*
+ * Spare the caller from having to copy things it's not interested in.
+ */
+ base_attr = class_attr_overridden(scsi_sysfs_shost_attrs, attr);
+ if (base_attr) {
+ /* extend permissions */
+ attr->attr.mode |= base_attr->attr.mode;
+
+ /* override null show/store with default */
+ if (!attr->show)
+ attr->show = base_attr->show;
+ if (!attr->store)
+ attr->store = base_attr->store;
+ }
+
+ return class_device_create_file(classdev, attr);
+}
+
+/**
+ * scsi_sysfs_add_host - add scsi host to subsystem
+ * @shost: scsi host struct to add to subsystem
+ * @dev: parent struct device pointer
+ **/
+int scsi_sysfs_add_host(struct Scsi_Host *shost)
+{
+ int error, i;
+
+ if (shost->hostt->shost_attrs) {
+ for (i = 0; shost->hostt->shost_attrs[i]; i++) {
+ error = class_attr_add(&shost->shost_classdev,
+ shost->hostt->shost_attrs[i]);
+ if (error)
+ return error;
+ }
+ }
+
+ for (i = 0; scsi_sysfs_shost_attrs[i]; i++) {
+ if (!class_attr_overridden(shost->hostt->shost_attrs,
+ scsi_sysfs_shost_attrs[i])) {
+ error = class_device_create_file(&shost->shost_classdev,
+ scsi_sysfs_shost_attrs[i]);
+ if (error)
+ return error;
+ }
+ }
+
+ transport_register_device(&shost->shost_gendev);
+ return 0;
+}
+
+void scsi_sysfs_device_initialize(struct scsi_device *sdev)
+{
+ unsigned long flags;
+ struct Scsi_Host *shost = sdev->host;
+ struct scsi_target *starget = sdev->sdev_target;
+
+ device_initialize(&sdev->sdev_gendev);
+ sdev->sdev_gendev.bus = &scsi_bus_type;
+ sdev->sdev_gendev.release = scsi_device_dev_release;
+ sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d",
+ sdev->host->host_no, sdev->channel, sdev->id,
+ sdev->lun);
+
+ class_device_initialize(&sdev->sdev_classdev);
+ sdev->sdev_classdev.dev = &sdev->sdev_gendev;
+ sdev->sdev_classdev.class = &sdev_class;
+ snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
+ "%d:%d:%d:%d", sdev->host->host_no,
+ sdev->channel, sdev->id, sdev->lun);
+ sdev->scsi_level = SCSI_2;
+ transport_setup_device(&sdev->sdev_gendev);
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_add_tail(&sdev->same_target_siblings, &starget->devices);
+ list_add_tail(&sdev->siblings, &shost->__devices);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+int scsi_is_sdev_device(const struct device *dev)
+{
+ return dev->release == scsi_device_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_sdev_device);
+
+/* A blank transport template that is used in drivers that don't
+ * yet implement Transport Attributes */
+struct scsi_transport_template blank_transport_template = { { { {NULL, }, }, }, };
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
new file mode 100644
index 000000000000..35d1c1e8e345
--- /dev/null
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -0,0 +1,1665 @@
+/*
+ * FiberChannel transport specific attributes exported to sysfs.
+ *
+ * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ========
+ *
+ * Copyright (C) 2004-2005 James Smart, Emulex Corporation
+ * Rewrite for host, target, device, and remote port attributes,
+ * statistics, and service functions...
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include "scsi_priv.h"
+
+#define FC_PRINTK(x, l, f, a...) printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a)
+
+/*
+ * Redefine so that we can have same named attributes in the
+ * sdev/starget/host objects.
+ */
+#define FC_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \
+struct class_device_attribute class_device_attr_##_prefix##_##_name = \
+ __ATTR(_name,_mode,_show,_store)
+
+#define fc_enum_name_search(title, table_type, table) \
+static const char *get_fc_##title##_name(enum table_type table_key) \
+{ \
+ int i; \
+ char *name = NULL; \
+ \
+ for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \
+ if (table[i].value == table_key) { \
+ name = table[i].name; \
+ break; \
+ } \
+ } \
+ return name; \
+}
+
+#define fc_enum_name_match(title, table_type, table) \
+static int get_fc_##title##_match(const char *table_key, \
+ enum table_type *value) \
+{ \
+ int i; \
+ \
+ for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \
+ if (strncmp(table_key, table[i].name, \
+ table[i].matchlen) == 0) { \
+ *value = table[i].value; \
+ return 0; /* success */ \
+ } \
+ } \
+ return 1; /* failure */ \
+}
+
+
+/* Convert fc_port_type values to ascii string name */
+static struct {
+ enum fc_port_type value;
+ char *name;
+} fc_port_type_names[] = {
+ { FC_PORTTYPE_UNKNOWN, "Unknown" },
+ { FC_PORTTYPE_OTHER, "Other" },
+ { FC_PORTTYPE_NOTPRESENT, "Not Present" },
+ { FC_PORTTYPE_NPORT, "NPort (fabric via point-to-point)" },
+ { FC_PORTTYPE_NLPORT, "NLPort (fabric via loop)" },
+ { FC_PORTTYPE_LPORT, "LPort (private loop)" },
+ { FC_PORTTYPE_PTP, "Point-To-Point (direct nport connection" },
+};
+fc_enum_name_search(port_type, fc_port_type, fc_port_type_names)
+#define FC_PORTTYPE_MAX_NAMELEN 50
+
+
+/* Convert fc_port_state values to ascii string name */
+static struct {
+ enum fc_port_state value;
+ char *name;
+} fc_port_state_names[] = {
+ { FC_PORTSTATE_UNKNOWN, "Unknown" },
+ { FC_PORTSTATE_NOTPRESENT, "Not Present" },
+ { FC_PORTSTATE_ONLINE, "Online" },
+ { FC_PORTSTATE_OFFLINE, "Offline" },
+ { FC_PORTSTATE_BLOCKED, "Blocked" },
+ { FC_PORTSTATE_BYPASSED, "Bypassed" },
+ { FC_PORTSTATE_DIAGNOSTICS, "Diagnostics" },
+ { FC_PORTSTATE_LINKDOWN, "Linkdown" },
+ { FC_PORTSTATE_ERROR, "Error" },
+ { FC_PORTSTATE_LOOPBACK, "Loopback" },
+};
+fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
+#define FC_PORTSTATE_MAX_NAMELEN 20
+
+
+/* Convert fc_tgtid_binding_type values to ascii string name */
+static struct {
+ enum fc_tgtid_binding_type value;
+ char *name;
+ int matchlen;
+} fc_tgtid_binding_type_names[] = {
+ { FC_TGTID_BIND_NONE, "none", 4 },
+ { FC_TGTID_BIND_BY_WWPN, "wwpn (World Wide Port Name)", 4 },
+ { FC_TGTID_BIND_BY_WWNN, "wwnn (World Wide Node Name)", 4 },
+ { FC_TGTID_BIND_BY_ID, "port_id (FC Address)", 7 },
+};
+fc_enum_name_search(tgtid_bind_type, fc_tgtid_binding_type,
+ fc_tgtid_binding_type_names)
+fc_enum_name_match(tgtid_bind_type, fc_tgtid_binding_type,
+ fc_tgtid_binding_type_names)
+#define FC_BINDTYPE_MAX_NAMELEN 30
+
+
+#define fc_bitfield_name_search(title, table) \
+static ssize_t \
+get_fc_##title##_names(u32 table_key, char *buf) \
+{ \
+ char *prefix = ""; \
+ ssize_t len = 0; \
+ int i; \
+ \
+ for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \
+ if (table[i].value & table_key) { \
+ len += sprintf(buf + len, "%s%s", \
+ prefix, table[i].name); \
+ prefix = ", "; \
+ } \
+ } \
+ len += sprintf(buf + len, "\n"); \
+ return len; \
+}
+
+
+/* Convert FC_COS bit values to ascii string name */
+static struct {
+ u32 value;
+ char *name;
+} fc_cos_names[] = {
+ { FC_COS_CLASS1, "Class 1" },
+ { FC_COS_CLASS2, "Class 2" },
+ { FC_COS_CLASS3, "Class 3" },
+ { FC_COS_CLASS4, "Class 4" },
+ { FC_COS_CLASS6, "Class 6" },
+};
+fc_bitfield_name_search(cos, fc_cos_names)
+
+
+/* Convert FC_PORTSPEED bit values to ascii string name */
+static struct {
+ u32 value;
+ char *name;
+} fc_port_speed_names[] = {
+ { FC_PORTSPEED_1GBIT, "1 Gbit" },
+ { FC_PORTSPEED_2GBIT, "2 Gbit" },
+ { FC_PORTSPEED_4GBIT, "4 Gbit" },
+ { FC_PORTSPEED_10GBIT, "10 Gbit" },
+ { FC_PORTSPEED_NOT_NEGOTIATED, "Not Negotiated" },
+};
+fc_bitfield_name_search(port_speed, fc_port_speed_names)
+
+
+static int
+show_fc_fc4s (char *buf, u8 *fc4_list)
+{
+ int i, len=0;
+
+ for (i = 0; i < FC_FC4_LIST_SIZE; i++, fc4_list++)
+ len += sprintf(buf + len , "0x%02x ", *fc4_list);
+ len += sprintf(buf + len, "\n");
+ return len;
+}
+
+
+/* Convert FC_RPORT_ROLE bit values to ascii string name */
+static struct {
+ u32 value;
+ char *name;
+} fc_remote_port_role_names[] = {
+ { FC_RPORT_ROLE_FCP_TARGET, "FCP Target" },
+ { FC_RPORT_ROLE_FCP_INITIATOR, "FCP Initiator" },
+ { FC_RPORT_ROLE_IP_PORT, "IP Port" },
+};
+fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names)
+
+/*
+ * Define roles that are specific to port_id. Values are relative to ROLE_MASK.
+ */
+#define FC_WELLKNOWN_PORTID_MASK 0xfffff0
+#define FC_WELLKNOWN_ROLE_MASK 0x00000f
+#define FC_FPORT_PORTID 0x00000e
+#define FC_FABCTLR_PORTID 0x00000d
+#define FC_DIRSRVR_PORTID 0x00000c
+#define FC_TIMESRVR_PORTID 0x00000b
+#define FC_MGMTSRVR_PORTID 0x00000a
+
+
+static void fc_timeout_blocked_rport(void *data);
+static void fc_scsi_scan_rport(void *data);
+static void fc_rport_terminate(struct fc_rport *rport);
+
+/*
+ * Attribute counts pre object type...
+ * Increase these values if you add attributes
+ */
+#define FC_STARGET_NUM_ATTRS 3
+#define FC_RPORT_NUM_ATTRS 9
+#define FC_HOST_NUM_ATTRS 15
+
+struct fc_internal {
+ struct scsi_transport_template t;
+ struct fc_function_template *f;
+
+ /*
+ * For attributes : each object has :
+ * An array of the actual attributes structures
+ * An array of null-terminated pointers to the attribute
+ * structures - used for mid-layer interaction.
+ *
+ * The attribute containers for the starget and host are are
+ * part of the midlayer. As the remote port is specific to the
+ * fc transport, we must provide the attribute container.
+ */
+ struct class_device_attribute private_starget_attrs[
+ FC_STARGET_NUM_ATTRS];
+ struct class_device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1];
+
+ struct class_device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];
+ struct class_device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];
+
+ struct transport_container rport_attr_cont;
+ struct class_device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS];
+ struct class_device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1];
+};
+
+#define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t)
+
+static int fc_target_setup(struct device *dev)
+{
+ struct scsi_target *starget = to_scsi_target(dev);
+ struct fc_rport *rport = starget_to_rport(starget);
+
+ /*
+ * if parent is remote port, use values from remote port.
+ * Otherwise, this host uses the fc_transport, but not the
+ * remote port interface. As such, initialize to known non-values.
+ */
+ if (rport) {
+ fc_starget_node_name(starget) = rport->node_name;
+ fc_starget_port_name(starget) = rport->port_name;
+ fc_starget_port_id(starget) = rport->port_id;
+ } else {
+ fc_starget_node_name(starget) = -1;
+ fc_starget_port_name(starget) = -1;
+ fc_starget_port_id(starget) = -1;
+ }
+
+ return 0;
+}
+
+static DECLARE_TRANSPORT_CLASS(fc_transport_class,
+ "fc_transport",
+ fc_target_setup,
+ NULL,
+ NULL);
+
+static int fc_host_setup(struct device *dev)
+{
+ struct Scsi_Host *shost = dev_to_shost(dev);
+
+ /*
+ * Set default values easily detected by the midlayer as
+ * failure cases. The scsi lldd is responsible for initializing
+ * all transport attributes to valid values per host.
+ */
+ fc_host_node_name(shost) = -1;
+ fc_host_port_name(shost) = -1;
+ fc_host_supported_classes(shost) = FC_COS_UNSPECIFIED;
+ memset(fc_host_supported_fc4s(shost), 0,
+ sizeof(fc_host_supported_fc4s(shost)));
+ memset(fc_host_symbolic_name(shost), 0,
+ sizeof(fc_host_symbolic_name(shost)));
+ fc_host_supported_speeds(shost) = FC_PORTSPEED_UNKNOWN;
+ fc_host_maxframe_size(shost) = -1;
+ memset(fc_host_serial_number(shost), 0,
+ sizeof(fc_host_serial_number(shost)));
+
+ fc_host_port_id(shost) = -1;
+ fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+ memset(fc_host_active_fc4s(shost), 0,
+ sizeof(fc_host_active_fc4s(shost)));
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ fc_host_fabric_name(shost) = -1;
+
+ fc_host_tgtid_bind_type(shost) = FC_TGTID_BIND_BY_WWPN;
+
+ INIT_LIST_HEAD(&fc_host_rports(shost));
+ INIT_LIST_HEAD(&fc_host_rport_bindings(shost));
+ fc_host_next_rport_number(shost) = 0;
+ fc_host_next_target_id(shost) = 0;
+
+ return 0;
+}
+
+static DECLARE_TRANSPORT_CLASS(fc_host_class,
+ "fc_host",
+ fc_host_setup,
+ NULL,
+ NULL);
+
+/*
+ * Setup and Remove actions for remote ports are handled
+ * in the service functions below.
+ */
+static DECLARE_TRANSPORT_CLASS(fc_rport_class,
+ "fc_remote_ports",
+ NULL,
+ NULL,
+ NULL);
+
+/*
+ * Module Parameters
+ */
+
+/*
+ * dev_loss_tmo: the default number of seconds that the FC transport
+ * should insulate the loss of a remote port.
+ * The maximum will be capped by the value of SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
+ */
+static unsigned int fc_dev_loss_tmo = SCSI_DEVICE_BLOCK_MAX_TIMEOUT;
+
+module_param_named(dev_loss_tmo, fc_dev_loss_tmo, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(dev_loss_tmo,
+ "Maximum number of seconds that the FC transport should"
+ " insulate the loss of a remote port. Once this value is"
+ " exceeded, the scsi target is removed. Value should be"
+ " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT.");
+
+
+static __init int fc_transport_init(void)
+{
+ int error = transport_class_register(&fc_host_class);
+ if (error)
+ return error;
+ error = transport_class_register(&fc_rport_class);
+ if (error)
+ return error;
+ return transport_class_register(&fc_transport_class);
+}
+
+static void __exit fc_transport_exit(void)
+{
+ transport_class_unregister(&fc_transport_class);
+ transport_class_unregister(&fc_rport_class);
+ transport_class_unregister(&fc_host_class);
+}
+
+/*
+ * FC Remote Port Attribute Management
+ */
+
+#define fc_rport_show_function(field, format_string, sz, cast) \
+static ssize_t \
+show_fc_rport_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct fc_rport *rport = transport_class_to_rport(cdev); \
+ struct Scsi_Host *shost = rport_to_shost(rport); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ if (i->f->get_rport_##field) \
+ i->f->get_rport_##field(rport); \
+ return snprintf(buf, sz, format_string, cast rport->field); \
+}
+
+#define fc_rport_store_function(field) \
+static ssize_t \
+store_fc_rport_##field(struct class_device *cdev, const char *buf, \
+ size_t count) \
+{ \
+ int val; \
+ struct fc_rport *rport = transport_class_to_rport(cdev); \
+ struct Scsi_Host *shost = rport_to_shost(rport); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ val = simple_strtoul(buf, NULL, 0); \
+ i->f->set_rport_##field(rport, val); \
+ return count; \
+}
+
+#define fc_rport_rd_attr(field, format_string, sz) \
+ fc_rport_show_function(field, format_string, sz, ) \
+static FC_CLASS_DEVICE_ATTR(rport, field, S_IRUGO, \
+ show_fc_rport_##field, NULL)
+
+#define fc_rport_rd_attr_cast(field, format_string, sz, cast) \
+ fc_rport_show_function(field, format_string, sz, (cast)) \
+static FC_CLASS_DEVICE_ATTR(rport, field, S_IRUGO, \
+ show_fc_rport_##field, NULL)
+
+#define fc_rport_rw_attr(field, format_string, sz) \
+ fc_rport_show_function(field, format_string, sz, ) \
+ fc_rport_store_function(field) \
+static FC_CLASS_DEVICE_ATTR(rport, field, S_IRUGO | S_IWUSR, \
+ show_fc_rport_##field, \
+ store_fc_rport_##field)
+
+
+#define fc_private_rport_show_function(field, format_string, sz, cast) \
+static ssize_t \
+show_fc_rport_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct fc_rport *rport = transport_class_to_rport(cdev); \
+ return snprintf(buf, sz, format_string, cast rport->field); \
+}
+
+#define fc_private_rport_rd_attr(field, format_string, sz) \
+ fc_private_rport_show_function(field, format_string, sz, ) \
+static FC_CLASS_DEVICE_ATTR(rport, field, S_IRUGO, \
+ show_fc_rport_##field, NULL)
+
+#define fc_private_rport_rd_attr_cast(field, format_string, sz, cast) \
+ fc_private_rport_show_function(field, format_string, sz, (cast)) \
+static FC_CLASS_DEVICE_ATTR(rport, field, S_IRUGO, \
+ show_fc_rport_##field, NULL)
+
+
+#define fc_private_rport_rd_enum_attr(title, maxlen) \
+static ssize_t \
+show_fc_rport_##title (struct class_device *cdev, char *buf) \
+{ \
+ struct fc_rport *rport = transport_class_to_rport(cdev); \
+ const char *name; \
+ name = get_fc_##title##_name(rport->title); \
+ if (!name) \
+ return -EINVAL; \
+ return snprintf(buf, maxlen, "%s\n", name); \
+} \
+static FC_CLASS_DEVICE_ATTR(rport, title, S_IRUGO, \
+ show_fc_rport_##title, NULL)
+
+
+#define SETUP_RPORT_ATTRIBUTE_RD(field) \
+ i->private_rport_attrs[count] = class_device_attr_rport_##field; \
+ i->private_rport_attrs[count].attr.mode = S_IRUGO; \
+ i->private_rport_attrs[count].store = NULL; \
+ i->rport_attrs[count] = &i->private_rport_attrs[count]; \
+ if (i->f->show_rport_##field) \
+ count++
+
+#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(field) \
+ i->private_rport_attrs[count] = class_device_attr_rport_##field; \
+ i->private_rport_attrs[count].attr.mode = S_IRUGO; \
+ i->private_rport_attrs[count].store = NULL; \
+ i->rport_attrs[count] = &i->private_rport_attrs[count]; \
+ count++
+
+#define SETUP_RPORT_ATTRIBUTE_RW(field) \
+ i->private_rport_attrs[count] = class_device_attr_rport_##field; \
+ if (!i->f->set_rport_##field) { \
+ i->private_rport_attrs[count].attr.mode = S_IRUGO; \
+ i->private_rport_attrs[count].store = NULL; \
+ } \
+ i->rport_attrs[count] = &i->private_rport_attrs[count]; \
+ if (i->f->show_rport_##field) \
+ count++
+
+
+/* The FC Transport Remote Port Attributes: */
+
+/* Fixed Remote Port Attributes */
+
+fc_private_rport_rd_attr(maxframe_size, "%u bytes\n", 20);
+
+static ssize_t
+show_fc_rport_supported_classes (struct class_device *cdev, char *buf)
+{
+ struct fc_rport *rport = transport_class_to_rport(cdev);
+ if (rport->supported_classes == FC_COS_UNSPECIFIED)
+ return snprintf(buf, 20, "unspecified\n");
+ return get_fc_cos_names(rport->supported_classes, buf);
+}
+static FC_CLASS_DEVICE_ATTR(rport, supported_classes, S_IRUGO,
+ show_fc_rport_supported_classes, NULL);
+
+/* Dynamic Remote Port Attributes */
+
+fc_rport_rw_attr(dev_loss_tmo, "%d\n", 20);
+
+
+/* Private Remote Port Attributes */
+
+fc_private_rport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
+fc_private_rport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
+fc_private_rport_rd_attr(port_id, "0x%06x\n", 20);
+
+static ssize_t
+show_fc_rport_roles (struct class_device *cdev, char *buf)
+{
+ struct fc_rport *rport = transport_class_to_rport(cdev);
+
+ /* identify any roles that are port_id specific */
+ if ((rport->port_id != -1) &&
+ (rport->port_id & FC_WELLKNOWN_PORTID_MASK) ==
+ FC_WELLKNOWN_PORTID_MASK) {
+ switch (rport->port_id & FC_WELLKNOWN_ROLE_MASK) {
+ case FC_FPORT_PORTID:
+ return snprintf(buf, 30, "Fabric Port\n");
+ case FC_FABCTLR_PORTID:
+ return snprintf(buf, 30, "Fabric Controller\n");
+ case FC_DIRSRVR_PORTID:
+ return snprintf(buf, 30, "Directory Server\n");
+ case FC_TIMESRVR_PORTID:
+ return snprintf(buf, 30, "Time Server\n");
+ case FC_MGMTSRVR_PORTID:
+ return snprintf(buf, 30, "Management Server\n");
+ default:
+ return snprintf(buf, 30, "Unknown Fabric Entity\n");
+ }
+ } else {
+ if (rport->roles == FC_RPORT_ROLE_UNKNOWN)
+ return snprintf(buf, 20, "unknown\n");
+ return get_fc_remote_port_roles_names(rport->roles, buf);
+ }
+}
+static FC_CLASS_DEVICE_ATTR(rport, roles, S_IRUGO,
+ show_fc_rport_roles, NULL);
+
+fc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
+fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20);
+
+
+
+/*
+ * FC SCSI Target Attribute Management
+ */
+
+/*
+ * Note: in the target show function we recognize when the remote
+ * port is in the heirarchy and do not allow the driver to get
+ * involved in sysfs functions. The driver only gets involved if
+ * it's the "old" style that doesn't use rports.
+ */
+#define fc_starget_show_function(field, format_string, sz, cast) \
+static ssize_t \
+show_fc_starget_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ struct fc_rport *rport = starget_to_rport(starget); \
+ if (rport) \
+ fc_starget_##field(starget) = rport->field; \
+ else if (i->f->get_starget_##field) \
+ i->f->get_starget_##field(starget); \
+ return snprintf(buf, sz, format_string, \
+ cast fc_starget_##field(starget)); \
+}
+
+#define fc_starget_rd_attr(field, format_string, sz) \
+ fc_starget_show_function(field, format_string, sz, ) \
+static FC_CLASS_DEVICE_ATTR(starget, field, S_IRUGO, \
+ show_fc_starget_##field, NULL)
+
+#define fc_starget_rd_attr_cast(field, format_string, sz, cast) \
+ fc_starget_show_function(field, format_string, sz, (cast)) \
+static FC_CLASS_DEVICE_ATTR(starget, field, S_IRUGO, \
+ show_fc_starget_##field, NULL)
+
+#define SETUP_STARGET_ATTRIBUTE_RD(field) \
+ i->private_starget_attrs[count] = class_device_attr_starget_##field; \
+ i->private_starget_attrs[count].attr.mode = S_IRUGO; \
+ i->private_starget_attrs[count].store = NULL; \
+ i->starget_attrs[count] = &i->private_starget_attrs[count]; \
+ if (i->f->show_starget_##field) \
+ count++
+
+#define SETUP_STARGET_ATTRIBUTE_RW(field) \
+ i->private_starget_attrs[count] = class_device_attr_starget_##field; \
+ if (!i->f->set_starget_##field) { \
+ i->private_starget_attrs[count].attr.mode = S_IRUGO; \
+ i->private_starget_attrs[count].store = NULL; \
+ } \
+ i->starget_attrs[count] = &i->private_starget_attrs[count]; \
+ if (i->f->show_starget_##field) \
+ count++
+
+/* The FC Transport SCSI Target Attributes: */
+fc_starget_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
+fc_starget_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
+fc_starget_rd_attr(port_id, "0x%06x\n", 20);
+
+
+/*
+ * Host Attribute Management
+ */
+
+#define fc_host_show_function(field, format_string, sz, cast) \
+static ssize_t \
+show_fc_host_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ if (i->f->get_host_##field) \
+ i->f->get_host_##field(shost); \
+ return snprintf(buf, sz, format_string, cast fc_host_##field(shost)); \
+}
+
+#define fc_host_store_function(field) \
+static ssize_t \
+store_fc_host_##field(struct class_device *cdev, const char *buf, \
+ size_t count) \
+{ \
+ int val; \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ \
+ val = simple_strtoul(buf, NULL, 0); \
+ i->f->set_host_##field(shost, val); \
+ return count; \
+}
+
+#define fc_host_rd_attr(field, format_string, sz) \
+ fc_host_show_function(field, format_string, sz, ) \
+static FC_CLASS_DEVICE_ATTR(host, field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_host_rd_attr_cast(field, format_string, sz, cast) \
+ fc_host_show_function(field, format_string, sz, (cast)) \
+static FC_CLASS_DEVICE_ATTR(host, field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_host_rw_attr(field, format_string, sz) \
+ fc_host_show_function(field, format_string, sz, ) \
+ fc_host_store_function(field) \
+static FC_CLASS_DEVICE_ATTR(host, field, S_IRUGO | S_IWUSR, \
+ show_fc_host_##field, \
+ store_fc_host_##field)
+
+#define fc_host_rd_enum_attr(title, maxlen) \
+static ssize_t \
+show_fc_host_##title (struct class_device *cdev, char *buf) \
+{ \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ const char *name; \
+ if (i->f->get_host_##title) \
+ i->f->get_host_##title(shost); \
+ name = get_fc_##title##_name(fc_host_##title(shost)); \
+ if (!name) \
+ return -EINVAL; \
+ return snprintf(buf, maxlen, "%s\n", name); \
+} \
+static FC_CLASS_DEVICE_ATTR(host, title, S_IRUGO, show_fc_host_##title, NULL)
+
+#define SETUP_HOST_ATTRIBUTE_RD(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ if (i->f->show_host_##field) \
+ count++
+
+#define SETUP_HOST_ATTRIBUTE_RW(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ if (!i->f->set_host_##field) { \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ } \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ if (i->f->show_host_##field) \
+ count++
+
+
+#define fc_private_host_show_function(field, format_string, sz, cast) \
+static ssize_t \
+show_fc_host_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ return snprintf(buf, sz, format_string, cast fc_host_##field(shost)); \
+}
+
+#define fc_private_host_rd_attr(field, format_string, sz) \
+ fc_private_host_show_function(field, format_string, sz, ) \
+static FC_CLASS_DEVICE_ATTR(host, field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_private_host_rd_attr_cast(field, format_string, sz, cast) \
+ fc_private_host_show_function(field, format_string, sz, (cast)) \
+static FC_CLASS_DEVICE_ATTR(host, field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define SETUP_PRIVATE_HOST_ATTRIBUTE_RD(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ count++
+
+#define SETUP_PRIVATE_HOST_ATTRIBUTE_RW(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ count++
+
+
+/* Fixed Host Attributes */
+
+static ssize_t
+show_fc_host_supported_classes (struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+
+ if (fc_host_supported_classes(shost) == FC_COS_UNSPECIFIED)
+ return snprintf(buf, 20, "unspecified\n");
+
+ return get_fc_cos_names(fc_host_supported_classes(shost), buf);
+}
+static FC_CLASS_DEVICE_ATTR(host, supported_classes, S_IRUGO,
+ show_fc_host_supported_classes, NULL);
+
+static ssize_t
+show_fc_host_supported_fc4s (struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ return (ssize_t)show_fc_fc4s(buf, fc_host_supported_fc4s(shost));
+}
+static FC_CLASS_DEVICE_ATTR(host, supported_fc4s, S_IRUGO,
+ show_fc_host_supported_fc4s, NULL);
+
+static ssize_t
+show_fc_host_supported_speeds (struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+
+ if (fc_host_supported_speeds(shost) == FC_PORTSPEED_UNKNOWN)
+ return snprintf(buf, 20, "unknown\n");
+
+ return get_fc_port_speed_names(fc_host_supported_speeds(shost), buf);
+}
+static FC_CLASS_DEVICE_ATTR(host, supported_speeds, S_IRUGO,
+ show_fc_host_supported_speeds, NULL);
+
+
+fc_private_host_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
+fc_private_host_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
+fc_private_host_rd_attr(symbolic_name, "%s\n", (FC_SYMBOLIC_NAME_SIZE +1));
+fc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20);
+fc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1));
+
+
+/* Dynamic Host Attributes */
+
+static ssize_t
+show_fc_host_active_fc4s (struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
+
+ if (i->f->get_host_active_fc4s)
+ i->f->get_host_active_fc4s(shost);
+
+ return (ssize_t)show_fc_fc4s(buf, fc_host_active_fc4s(shost));
+}
+static FC_CLASS_DEVICE_ATTR(host, active_fc4s, S_IRUGO,
+ show_fc_host_active_fc4s, NULL);
+
+static ssize_t
+show_fc_host_speed (struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
+
+ if (i->f->get_host_speed)
+ i->f->get_host_speed(shost);
+
+ if (fc_host_speed(shost) == FC_PORTSPEED_UNKNOWN)
+ return snprintf(buf, 20, "unknown\n");
+
+ return get_fc_port_speed_names(fc_host_speed(shost), buf);
+}
+static FC_CLASS_DEVICE_ATTR(host, speed, S_IRUGO,
+ show_fc_host_speed, NULL);
+
+
+fc_host_rd_attr(port_id, "0x%06x\n", 20);
+fc_host_rd_enum_attr(port_type, FC_PORTTYPE_MAX_NAMELEN);
+fc_host_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
+fc_host_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
+
+
+/* Private Host Attributes */
+
+static ssize_t
+show_fc_private_host_tgtid_bind_type(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ const char *name;
+
+ name = get_fc_tgtid_bind_type_name(fc_host_tgtid_bind_type(shost));
+ if (!name)
+ return -EINVAL;
+ return snprintf(buf, FC_BINDTYPE_MAX_NAMELEN, "%s\n", name);
+}
+
+static ssize_t
+store_fc_private_host_tgtid_bind_type(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct fc_rport *rport, *next_rport;
+ enum fc_tgtid_binding_type val;
+ unsigned long flags;
+
+ if (get_fc_tgtid_bind_type_match(buf, &val))
+ return -EINVAL;
+
+ /* if changing bind type, purge all unused consistent bindings */
+ if (val != fc_host_tgtid_bind_type(shost)) {
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_for_each_entry_safe(rport, next_rport,
+ &fc_host_rport_bindings(shost), peers)
+ fc_rport_terminate(rport);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ }
+
+ fc_host_tgtid_bind_type(shost) = val;
+ return count;
+}
+
+static FC_CLASS_DEVICE_ATTR(host, tgtid_bind_type, S_IRUGO | S_IWUSR,
+ show_fc_private_host_tgtid_bind_type,
+ store_fc_private_host_tgtid_bind_type);
+
+/*
+ * Host Statistics Management
+ */
+
+/* Show a given an attribute in the statistics group */
+static ssize_t
+fc_stat_show(const struct class_device *cdev, char *buf, unsigned long offset)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
+ struct fc_host_statistics *stats;
+ ssize_t ret = -ENOENT;
+
+ if (offset > sizeof(struct fc_host_statistics) ||
+ offset % sizeof(u64) != 0)
+ WARN_ON(1);
+
+ if (i->f->get_fc_host_stats) {
+ stats = (i->f->get_fc_host_stats)(shost);
+ if (stats)
+ ret = snprintf(buf, 20, "0x%llx\n",
+ (unsigned long long)*(u64 *)(((u8 *) stats) + offset));
+ }
+ return ret;
+}
+
+
+/* generate a read-only statistics attribute */
+#define fc_host_statistic(name) \
+static ssize_t show_fcstat_##name(struct class_device *cd, char *buf) \
+{ \
+ return fc_stat_show(cd, buf, \
+ offsetof(struct fc_host_statistics, name)); \
+} \
+static FC_CLASS_DEVICE_ATTR(host, name, S_IRUGO, show_fcstat_##name, NULL)
+
+fc_host_statistic(seconds_since_last_reset);
+fc_host_statistic(tx_frames);
+fc_host_statistic(tx_words);
+fc_host_statistic(rx_frames);
+fc_host_statistic(rx_words);
+fc_host_statistic(lip_count);
+fc_host_statistic(nos_count);
+fc_host_statistic(error_frames);
+fc_host_statistic(dumped_frames);
+fc_host_statistic(link_failure_count);
+fc_host_statistic(loss_of_sync_count);
+fc_host_statistic(loss_of_signal_count);
+fc_host_statistic(prim_seq_protocol_err_count);
+fc_host_statistic(invalid_tx_word_count);
+fc_host_statistic(invalid_crc_count);
+fc_host_statistic(fcp_input_requests);
+fc_host_statistic(fcp_output_requests);
+fc_host_statistic(fcp_control_requests);
+fc_host_statistic(fcp_input_megabytes);
+fc_host_statistic(fcp_output_megabytes);
+
+static ssize_t
+fc_reset_statistics(struct class_device *cdev, const char *buf,
+ size_t count)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
+
+ /* ignore any data value written to the attribute */
+ if (i->f->reset_fc_host_stats) {
+ i->f->reset_fc_host_stats(shost);
+ return count;
+ }
+
+ return -ENOENT;
+}
+static FC_CLASS_DEVICE_ATTR(host, reset_statistics, S_IWUSR, NULL,
+ fc_reset_statistics);
+
+
+static struct attribute *fc_statistics_attrs[] = {
+ &class_device_attr_host_seconds_since_last_reset.attr,
+ &class_device_attr_host_tx_frames.attr,
+ &class_device_attr_host_tx_words.attr,
+ &class_device_attr_host_rx_frames.attr,
+ &class_device_attr_host_rx_words.attr,
+ &class_device_attr_host_lip_count.attr,
+ &class_device_attr_host_nos_count.attr,
+ &class_device_attr_host_error_frames.attr,
+ &class_device_attr_host_dumped_frames.attr,
+ &class_device_attr_host_link_failure_count.attr,
+ &class_device_attr_host_loss_of_sync_count.attr,
+ &class_device_attr_host_loss_of_signal_count.attr,
+ &class_device_attr_host_prim_seq_protocol_err_count.attr,
+ &class_device_attr_host_invalid_tx_word_count.attr,
+ &class_device_attr_host_invalid_crc_count.attr,
+ &class_device_attr_host_fcp_input_requests.attr,
+ &class_device_attr_host_fcp_output_requests.attr,
+ &class_device_attr_host_fcp_control_requests.attr,
+ &class_device_attr_host_fcp_input_megabytes.attr,
+ &class_device_attr_host_fcp_output_megabytes.attr,
+ &class_device_attr_host_reset_statistics.attr,
+ NULL
+};
+
+static struct attribute_group fc_statistics_group = {
+ .name = "statistics",
+ .attrs = fc_statistics_attrs,
+};
+
+static int fc_host_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct fc_internal *i;
+
+ if (!scsi_is_host_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &fc_host_class.class)
+ return 0;
+
+ i = to_fc_internal(shost->transportt);
+
+ return &i->t.host_attrs.ac == cont;
+}
+
+static int fc_target_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct fc_internal *i;
+
+ if (!scsi_is_target_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev->parent);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &fc_host_class.class)
+ return 0;
+
+ i = to_fc_internal(shost->transportt);
+
+ return &i->t.target_attrs.ac == cont;
+}
+
+static void fc_rport_dev_release(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_rport(dev);
+ put_device(dev->parent);
+ kfree(rport);
+}
+
+int scsi_is_fc_rport(const struct device *dev)
+{
+ return dev->release == fc_rport_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_fc_rport);
+
+static int fc_rport_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct fc_internal *i;
+
+ if (!scsi_is_fc_rport(dev))
+ return 0;
+
+ shost = dev_to_shost(dev->parent);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &fc_host_class.class)
+ return 0;
+
+ i = to_fc_internal(shost->transportt);
+
+ return &i->rport_attr_cont.ac == cont;
+}
+
+struct scsi_transport_template *
+fc_attach_transport(struct fc_function_template *ft)
+{
+ struct fc_internal *i = kmalloc(sizeof(struct fc_internal),
+ GFP_KERNEL);
+ int count;
+
+ if (unlikely(!i))
+ return NULL;
+
+ memset(i, 0, sizeof(struct fc_internal));
+
+ i->t.target_attrs.ac.attrs = &i->starget_attrs[0];
+ i->t.target_attrs.ac.class = &fc_transport_class.class;
+ i->t.target_attrs.ac.match = fc_target_match;
+ i->t.target_size = sizeof(struct fc_starget_attrs);
+ transport_container_register(&i->t.target_attrs);
+
+ i->t.host_attrs.ac.attrs = &i->host_attrs[0];
+ i->t.host_attrs.ac.class = &fc_host_class.class;
+ i->t.host_attrs.ac.match = fc_host_match;
+ i->t.host_size = sizeof(struct fc_host_attrs);
+ if (ft->get_fc_host_stats)
+ i->t.host_attrs.statistics = &fc_statistics_group;
+ transport_container_register(&i->t.host_attrs);
+
+ i->rport_attr_cont.ac.attrs = &i->rport_attrs[0];
+ i->rport_attr_cont.ac.class = &fc_rport_class.class;
+ i->rport_attr_cont.ac.match = fc_rport_match;
+ transport_container_register(&i->rport_attr_cont);
+
+ i->f = ft;
+
+ /* Transport uses the shost workq for scsi scanning */
+ i->t.create_work_queue = 1;
+
+ /*
+ * Setup SCSI Target Attributes.
+ */
+ count = 0;
+ SETUP_STARGET_ATTRIBUTE_RD(node_name);
+ SETUP_STARGET_ATTRIBUTE_RD(port_name);
+ SETUP_STARGET_ATTRIBUTE_RD(port_id);
+
+ BUG_ON(count > FC_STARGET_NUM_ATTRS);
+
+ i->starget_attrs[count] = NULL;
+
+
+ /*
+ * Setup SCSI Host Attributes.
+ */
+ count=0;
+ SETUP_HOST_ATTRIBUTE_RD(node_name);
+ SETUP_HOST_ATTRIBUTE_RD(port_name);
+ SETUP_HOST_ATTRIBUTE_RD(supported_classes);
+ SETUP_HOST_ATTRIBUTE_RD(supported_fc4s);
+ SETUP_HOST_ATTRIBUTE_RD(symbolic_name);
+ SETUP_HOST_ATTRIBUTE_RD(supported_speeds);
+ SETUP_HOST_ATTRIBUTE_RD(maxframe_size);
+ SETUP_HOST_ATTRIBUTE_RD(serial_number);
+
+ SETUP_HOST_ATTRIBUTE_RD(port_id);
+ SETUP_HOST_ATTRIBUTE_RD(port_type);
+ SETUP_HOST_ATTRIBUTE_RD(port_state);
+ SETUP_HOST_ATTRIBUTE_RD(active_fc4s);
+ SETUP_HOST_ATTRIBUTE_RD(speed);
+ SETUP_HOST_ATTRIBUTE_RD(fabric_name);
+
+ /* Transport-managed attributes */
+ SETUP_PRIVATE_HOST_ATTRIBUTE_RW(tgtid_bind_type);
+
+ BUG_ON(count > FC_HOST_NUM_ATTRS);
+
+ i->host_attrs[count] = NULL;
+
+ /*
+ * Setup Remote Port Attributes.
+ */
+ count=0;
+ SETUP_RPORT_ATTRIBUTE_RD(maxframe_size);
+ SETUP_RPORT_ATTRIBUTE_RD(supported_classes);
+ SETUP_RPORT_ATTRIBUTE_RW(dev_loss_tmo);
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(node_name);
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_name);
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_id);
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles);
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state);
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id);
+
+ BUG_ON(count > FC_RPORT_NUM_ATTRS);
+
+ i->rport_attrs[count] = NULL;
+
+ return &i->t;
+}
+EXPORT_SYMBOL(fc_attach_transport);
+
+void fc_release_transport(struct scsi_transport_template *t)
+{
+ struct fc_internal *i = to_fc_internal(t);
+
+ transport_container_unregister(&i->t.target_attrs);
+ transport_container_unregister(&i->t.host_attrs);
+ transport_container_unregister(&i->rport_attr_cont);
+
+ kfree(i);
+}
+EXPORT_SYMBOL(fc_release_transport);
+
+
+/**
+ * fc_remove_host - called to terminate any fc_transport-related elements
+ * for a scsi host.
+ * @rport: remote port to be unblocked.
+ *
+ * This routine is expected to be called immediately preceeding the
+ * a driver's call to scsi_remove_host().
+ *
+ * WARNING: A driver utilizing the fc_transport, which fails to call
+ * this routine prior to scsi_remote_host(), will leave dangling
+ * objects in /sys/class/fc_remote_ports. Access to any of these
+ * objects can result in a system crash !!!
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_remove_host(struct Scsi_Host *shost)
+{
+ struct fc_rport *rport, *next_rport;
+
+ /* Remove any remote ports */
+ list_for_each_entry_safe(rport, next_rport,
+ &fc_host_rports(shost), peers)
+ fc_rport_terminate(rport);
+ list_for_each_entry_safe(rport, next_rport,
+ &fc_host_rport_bindings(shost), peers)
+ fc_rport_terminate(rport);
+}
+EXPORT_SYMBOL(fc_remove_host);
+
+/**
+ * fc_rport_create - allocates and creates a remote FC port.
+ * @shost: scsi host the remote port is connected to.
+ * @channel: Channel on shost port connected to.
+ * @ids: The world wide names, fc address, and FC4 port
+ * roles for the remote port.
+ *
+ * Allocates and creates the remoter port structure, including the
+ * class and sysfs creation.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+struct fc_rport *
+fc_rport_create(struct Scsi_Host *shost, int channel,
+ struct fc_rport_identifiers *ids)
+{
+ struct fc_host_attrs *fc_host =
+ (struct fc_host_attrs *)shost->shost_data;
+ struct fc_internal *fci = to_fc_internal(shost->transportt);
+ struct fc_rport *rport;
+ struct device *dev;
+ unsigned long flags;
+ int error;
+ size_t size;
+
+ size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size);
+ rport = kmalloc(size, GFP_KERNEL);
+ if (unlikely(!rport)) {
+ printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
+ return NULL;
+ }
+ memset(rport, 0, size);
+
+ rport->maxframe_size = -1;
+ rport->supported_classes = FC_COS_UNSPECIFIED;
+ rport->dev_loss_tmo = fc_dev_loss_tmo;
+ memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name));
+ memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name));
+ rport->port_id = ids->port_id;
+ rport->roles = ids->roles;
+ rport->port_state = FC_PORTSTATE_ONLINE;
+ if (fci->f->dd_fcrport_size)
+ rport->dd_data = &rport[1];
+ rport->channel = channel;
+
+ INIT_WORK(&rport->dev_loss_work, fc_timeout_blocked_rport, rport);
+ INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ rport->number = fc_host->next_rport_number++;
+ if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)
+ rport->scsi_target_id = fc_host->next_target_id++;
+ else
+ rport->scsi_target_id = -1;
+ list_add_tail(&rport->peers, &fc_host_rports(shost));
+ get_device(&shost->shost_gendev);
+
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ dev = &rport->dev;
+ device_initialize(dev);
+ dev->parent = get_device(&shost->shost_gendev);
+ dev->release = fc_rport_dev_release;
+ sprintf(dev->bus_id, "rport-%d:%d-%d",
+ shost->host_no, channel, rport->number);
+ transport_setup_device(dev);
+
+ error = device_add(dev);
+ if (error) {
+ printk(KERN_ERR "FC Remote Port device_add failed\n");
+ goto delete_rport;
+ }
+ transport_add_device(dev);
+ transport_configure_device(dev);
+
+ if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)
+ /* initiate a scan of the target */
+ scsi_queue_work(shost, &rport->scan_work);
+
+ return rport;
+
+delete_rport:
+ transport_destroy_device(dev);
+ put_device(dev->parent);
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_del(&rport->peers);
+ put_device(&shost->shost_gendev);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ put_device(dev->parent);
+ kfree(rport);
+ return NULL;
+}
+
+/**
+ * fc_remote_port_add - notifies the fc transport of the existence
+ * of a remote FC port.
+ * @shost: scsi host the remote port is connected to.
+ * @channel: Channel on shost port connected to.
+ * @ids: The world wide names, fc address, and FC4 port
+ * roles for the remote port.
+ *
+ * The LLDD calls this routine to notify the transport of the existence
+ * of a remote port. The LLDD provides the unique identifiers (wwpn,wwn)
+ * of the port, it's FC address (port_id), and the FC4 roles that are
+ * active for the port.
+ *
+ * For ports that are FCP targets (aka scsi targets), the FC transport
+ * maintains consistent target id bindings on behalf of the LLDD.
+ * A consistent target id binding is an assignment of a target id to
+ * a remote port identifier, which persists while the scsi host is
+ * attached. The remote port can disappear, then later reappear, and
+ * it's target id assignment remains the same. This allows for shifts
+ * in FC addressing (if binding by wwpn or wwnn) with no apparent
+ * changes to the scsi subsystem which is based on scsi host number and
+ * target id values. Bindings are only valid during the attachment of
+ * the scsi host. If the host detaches, then later re-attaches, target
+ * id bindings may change.
+ *
+ * This routine is responsible for returning a remote port structure.
+ * The routine will search the list of remote ports it maintains
+ * internally on behalf of consistent target id mappings. If found, the
+ * remote port structure will be reused. Otherwise, a new remote port
+ * structure will be allocated.
+ *
+ * Whenever a remote port is allocated, a new fc_remote_port class
+ * device is created.
+ *
+ * Should not be called from interrupt context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+struct fc_rport *
+fc_remote_port_add(struct Scsi_Host *shost, int channel,
+ struct fc_rport_identifiers *ids)
+{
+ struct fc_rport *rport;
+ unsigned long flags;
+ int match = 0;
+
+ if (likely((ids->roles & FC_RPORT_ROLE_FCP_TARGET) &&
+ (fc_host_tgtid_bind_type(shost) != FC_TGTID_BIND_NONE))) {
+
+ /* search for a matching consistent binding */
+
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ list_for_each_entry(rport, &fc_host_rport_bindings(shost),
+ peers) {
+ if (rport->channel != channel)
+ continue;
+
+ switch (fc_host_tgtid_bind_type(shost)) {
+ case FC_TGTID_BIND_BY_WWPN:
+ if (rport->port_name == ids->port_name)
+ match = 1;
+ break;
+ case FC_TGTID_BIND_BY_WWNN:
+ if (rport->node_name == ids->node_name)
+ match = 1;
+ break;
+ case FC_TGTID_BIND_BY_ID:
+ if (rport->port_id == ids->port_id)
+ match = 1;
+ break;
+ case FC_TGTID_BIND_NONE: /* to keep compiler happy */
+ break;
+ }
+
+ if (match) {
+ list_move_tail(&rport->peers,
+ &fc_host_rports(shost));
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ if (match) {
+ memcpy(&rport->node_name, &ids->node_name,
+ sizeof(rport->node_name));
+ memcpy(&rport->port_name, &ids->port_name,
+ sizeof(rport->port_name));
+ rport->port_id = ids->port_id;
+ rport->roles = ids->roles;
+ rport->port_state = FC_PORTSTATE_ONLINE;
+
+ if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)
+ /* initiate a scan of the target */
+ scsi_queue_work(shost, &rport->scan_work);
+
+ return rport;
+ }
+ }
+
+ /* No consistent binding found - create new remote port entry */
+ rport = fc_rport_create(shost, channel, ids);
+
+ return rport;
+}
+EXPORT_SYMBOL(fc_remote_port_add);
+
+/*
+ * fc_rport_tgt_remove - Removes the scsi target on the remote port
+ * @rport: The remote port to be operated on
+ */
+static void
+fc_rport_tgt_remove(struct fc_rport *rport)
+{
+ struct Scsi_Host *shost = rport_to_shost(rport);
+
+ scsi_target_unblock(&rport->dev);
+
+ /* Stop anything on the workq */
+ if (!cancel_delayed_work(&rport->dev_loss_work))
+ flush_scheduled_work();
+ scsi_flush_work(shost);
+
+ scsi_remove_target(&rport->dev);
+}
+
+/*
+ * fc_rport_terminate - this routine tears down and deallocates a remote port.
+ * @rport: The remote port to be terminated
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ */
+static void
+fc_rport_terminate(struct fc_rport *rport)
+{
+ struct Scsi_Host *shost = rport_to_shost(rport);
+ struct device *dev = &rport->dev;
+ unsigned long flags;
+
+ fc_rport_tgt_remove(rport);
+
+ transport_remove_device(dev);
+ device_del(dev);
+ transport_destroy_device(dev);
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_del(&rport->peers);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ put_device(&shost->shost_gendev);
+}
+
+/**
+ * fc_remote_port_delete - notifies the fc transport that a remote
+ * port is no longer in existence.
+ * @rport: The remote port that no longer exists
+ *
+ * The LLDD calls this routine to notify the transport that a remote
+ * port is no longer part of the topology. Note: Although a port
+ * may no longer be part of the topology, it may persist in the remote
+ * ports displayed by the fc_host. This is done so that target id
+ * mappings (managed via the remote port structures), are always visible
+ * as long as the mapping is valid, regardless of port state,
+ *
+ * If the remote port is not an FCP Target, it will be fully torn down
+ * and deallocated, including the fc_remote_port class device.
+ *
+ * If the remote port is an FCP Target, the port structure will be
+ * marked as Not Present, but will remain as long as there is a valid
+ * SCSI target id mapping associated with the port structure. Validity
+ * is determined by the binding type. If binding by wwpn, then the port
+ * structure is always valid and will not be deallocated until the host
+ * is removed. If binding by wwnn, then the port structure is valid
+ * until another port with the same node name is found in the topology.
+ * If binding by port id (fc address), then the port structure is valid
+ * valid until another port with the same address is identified.
+ *
+ * Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_remote_port_delete(struct fc_rport *rport)
+{
+ struct Scsi_Host *shost = rport_to_shost(rport);
+ unsigned long flags;
+
+ /* If no scsi target id mapping or consistent binding type, delete it */
+ if ((rport->scsi_target_id == -1) ||
+ (fc_host_tgtid_bind_type(shost) == FC_TGTID_BIND_NONE)) {
+ fc_rport_terminate(rport);
+ return;
+ }
+
+ fc_rport_tgt_remove(rport);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_move_tail(&rport->peers, &fc_host_rport_bindings(shost));
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ /*
+ * Note: We do not remove or clear the hostdata area. This allows
+ * host-specific target data to persist along with the
+ * scsi_target_id. It's up to the host to manage it's hostdata area.
+ */
+
+ /*
+ * Reinitialize port attributes that may change if the port comes back.
+ */
+ rport->maxframe_size = -1;
+ rport->supported_classes = FC_COS_UNSPECIFIED;
+ rport->roles = FC_RPORT_ROLE_UNKNOWN;
+ rport->port_state = FC_PORTSTATE_NOTPRESENT;
+
+ /* remove the identifiers that aren't used in the consisting binding */
+ switch (fc_host_tgtid_bind_type(shost)) {
+ case FC_TGTID_BIND_BY_WWPN:
+ rport->node_name = -1;
+ rport->port_id = -1;
+ break;
+ case FC_TGTID_BIND_BY_WWNN:
+ rport->port_name = -1;
+ rport->port_id = -1;
+ break;
+ case FC_TGTID_BIND_BY_ID:
+ rport->node_name = -1;
+ rport->port_name = -1;
+ break;
+ case FC_TGTID_BIND_NONE: /* to keep compiler happy */
+ break;
+ }
+}
+EXPORT_SYMBOL(fc_remote_port_delete);
+
+/**
+ * fc_remote_port_rolechg - notifies the fc transport that the roles
+ * on a remote may have changed.
+ * @rport: The remote port that changed.
+ *
+ * The LLDD calls this routine to notify the transport that the roles
+ * on a remote port may have changed. The largest effect of this is
+ * if a port now becomes a FCP Target, it must be allocated a
+ * scsi target id. If the port is no longer a FCP target, any
+ * scsi target id value assigned to it will persist in case the
+ * role changes back to include FCP Target. No changes in the scsi
+ * midlayer will be invoked if the role changes (in the expectation
+ * that the role will be resumed. If it doesn't normal error processing
+ * will take place).
+ *
+ * Should not be called from interrupt context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_remote_port_rolechg(struct fc_rport *rport, u32 roles)
+{
+ struct Scsi_Host *shost = rport_to_shost(rport);
+ struct fc_host_attrs *fc_host =
+ (struct fc_host_attrs *)shost->shost_data;
+ unsigned long flags;
+ int create = 0;
+
+ rport->roles = roles;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ if ((rport->scsi_target_id == -1) &&
+ (rport->roles & FC_RPORT_ROLE_FCP_TARGET)) {
+ rport->scsi_target_id = fc_host->next_target_id++;
+ create = 1;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ if (create)
+ /* initiate a scan of the target */
+ scsi_queue_work(shost, &rport->scan_work);
+}
+EXPORT_SYMBOL(fc_remote_port_rolechg);
+
+/**
+ * fc_timeout_blocked_rport - Timeout handler for blocked remote port
+ * that fails to return in the alloted time.
+ * @data: scsi target that failed to reappear in the alloted time.
+ **/
+static void
+fc_timeout_blocked_rport(void *data)
+{
+ struct fc_rport *rport = (struct fc_rport *)data;
+
+ rport->port_state = FC_PORTSTATE_OFFLINE;
+
+ dev_printk(KERN_ERR, &rport->dev,
+ "blocked FC remote port time out: removing target\n");
+
+ /*
+ * As this only occurs if the remote port (scsi target)
+ * went away and didn't come back - we'll remove
+ * all attached scsi devices.
+ */
+ scsi_target_unblock(&rport->dev);
+ scsi_remove_target(&rport->dev);
+}
+
+/**
+ * fc_remote_port_block - temporarily block any scsi traffic to a remote port.
+ * @rport: remote port to be blocked.
+ *
+ * scsi lldd's with a FC transport call this routine to temporarily stop
+ * all scsi traffic to a remote port. If the port is not a SCSI target,
+ * no action is taken. If the port is a SCSI target, all attached devices
+ * are placed into a SDEV_BLOCK state and a timer is started. The timer is
+ * represents the maximum amount of time the port may be blocked. If the
+ * timer expires, the port is considered non-existent and the attached
+ * scsi devices will be removed.
+ *
+ * Called from interrupt or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ *
+ * The timeout and timer types are extracted from the fc transport
+ * attributes from the caller's rport pointer.
+ **/
+int
+fc_remote_port_block(struct fc_rport *rport)
+{
+ int timeout = rport->dev_loss_tmo;
+ struct work_struct *work = &rport->dev_loss_work;
+
+ if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
+ scsi_target_block(&rport->dev);
+
+ /* cap the length the devices can be blocked */
+ schedule_delayed_work(work, timeout * HZ);
+
+ rport->port_state = FC_PORTSTATE_BLOCKED;
+ return 0;
+}
+EXPORT_SYMBOL(fc_remote_port_block);
+
+/**
+ * fc_remote_port_unblock - restart any blocked scsi traffic to a remote port.
+ * @rport: remote port to be unblocked.
+ *
+ * scsi lld's with a FC transport call this routine to restart IO to all
+ * devices associated with the caller's scsi target following a fc_target_block
+ * request. Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+ void
+fc_remote_port_unblock(struct fc_rport *rport)
+{
+ struct work_struct *work = &rport->dev_loss_work;
+ struct Scsi_Host *shost = rport_to_shost(rport);
+
+ /*
+ * Stop the target timer first. Take no action on the del_timer
+ * failure as the state machine state change will validate the
+ * transaction.
+ */
+ if (!cancel_delayed_work(work))
+ flush_scheduled_work();
+
+ if (rport->port_state == FC_PORTSTATE_OFFLINE)
+ /*
+ * initiate a scan of the target as the target has
+ * been torn down.
+ */
+ scsi_queue_work(shost, &rport->scan_work);
+ else
+ scsi_target_unblock(&rport->dev);
+
+ rport->port_state = FC_PORTSTATE_ONLINE;
+}
+EXPORT_SYMBOL(fc_remote_port_unblock);
+
+/**
+ * fc_scsi_scan_rport - called to perform a scsi scan on a remote port.
+ * @data: remote port to be scanned.
+ **/
+static void
+fc_scsi_scan_rport(void *data)
+{
+ struct fc_rport *rport = (struct fc_rport *)data;
+
+ scsi_scan_target(&rport->dev, rport->channel, rport->scsi_target_id,
+ SCAN_WILD_CARD, 1);
+}
+
+
+MODULE_AUTHOR("Martin Hicks");
+MODULE_DESCRIPTION("FC Transport Attributes");
+MODULE_LICENSE("GPL");
+
+module_init(fc_transport_init);
+module_exit(fc_transport_exit);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
new file mode 100644
index 000000000000..8bb8222ea589
--- /dev/null
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -0,0 +1,388 @@
+/*
+ * iSCSI transport class definitions
+ *
+ * Copyright (C) IBM Corporation, 2004
+ * Copyright (C) Mike Christie, 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#define ISCSI_SESSION_ATTRS 20
+#define ISCSI_HOST_ATTRS 2
+
+struct iscsi_internal {
+ struct scsi_transport_template t;
+ struct iscsi_function_template *fnt;
+ /*
+ * We do not have any private or other attrs.
+ */
+ struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
+ struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
+};
+
+#define to_iscsi_internal(tmpl) container_of(tmpl, struct iscsi_internal, t)
+
+static DECLARE_TRANSPORT_CLASS(iscsi_transport_class,
+ "iscsi_transport",
+ NULL,
+ NULL,
+ NULL);
+
+static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
+ "iscsi_host",
+ NULL,
+ NULL,
+ NULL);
+/*
+ * iSCSI target and session attrs
+ */
+#define iscsi_session_show_fn(field, format) \
+ \
+static ssize_t \
+show_session_##field(struct class_device *cdev, char *buf) \
+{ \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+ \
+ if (i->fnt->get_##field) \
+ i->fnt->get_##field(starget); \
+ return snprintf(buf, 20, format"\n", iscsi_##field(starget)); \
+}
+
+#define iscsi_session_rd_attr(field, format) \
+ iscsi_session_show_fn(field, format) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL);
+
+iscsi_session_rd_attr(tpgt, "%hu");
+iscsi_session_rd_attr(tsih, "%2x");
+iscsi_session_rd_attr(max_recv_data_segment_len, "%u");
+iscsi_session_rd_attr(max_burst_len, "%u");
+iscsi_session_rd_attr(first_burst_len, "%u");
+iscsi_session_rd_attr(def_time2wait, "%hu");
+iscsi_session_rd_attr(def_time2retain, "%hu");
+iscsi_session_rd_attr(max_outstanding_r2t, "%hu");
+iscsi_session_rd_attr(erl, "%d");
+
+
+#define iscsi_session_show_bool_fn(field) \
+ \
+static ssize_t \
+show_session_bool_##field(struct class_device *cdev, char *buf) \
+{ \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+ \
+ if (i->fnt->get_##field) \
+ i->fnt->get_##field(starget); \
+ \
+ if (iscsi_##field(starget)) \
+ return sprintf(buf, "Yes\n"); \
+ return sprintf(buf, "No\n"); \
+}
+
+#define iscsi_session_rd_bool_attr(field) \
+ iscsi_session_show_bool_fn(field) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_bool_##field, NULL);
+
+iscsi_session_rd_bool_attr(initial_r2t);
+iscsi_session_rd_bool_attr(immediate_data);
+iscsi_session_rd_bool_attr(data_pdu_in_order);
+iscsi_session_rd_bool_attr(data_sequence_in_order);
+
+#define iscsi_session_show_digest_fn(field) \
+ \
+static ssize_t \
+show_##field(struct class_device *cdev, char *buf) \
+{ \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+ \
+ if (i->fnt->get_##field) \
+ i->fnt->get_##field(starget); \
+ \
+ if (iscsi_##field(starget)) \
+ return sprintf(buf, "CRC32C\n"); \
+ return sprintf(buf, "None\n"); \
+}
+
+#define iscsi_session_rd_digest_attr(field) \
+ iscsi_session_show_digest_fn(field) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+iscsi_session_rd_digest_attr(header_digest);
+iscsi_session_rd_digest_attr(data_digest);
+
+static ssize_t
+show_port(struct class_device *cdev, char *buf)
+{
+ struct scsi_target *starget = transport_class_to_starget(cdev);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+
+ if (i->fnt->get_port)
+ i->fnt->get_port(starget);
+
+ return snprintf(buf, 20, "%hu\n", ntohs(iscsi_port(starget)));
+}
+static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL);
+
+static ssize_t
+show_ip_address(struct class_device *cdev, char *buf)
+{
+ struct scsi_target *starget = transport_class_to_starget(cdev);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+
+ if (i->fnt->get_ip_address)
+ i->fnt->get_ip_address(starget);
+
+ if (iscsi_addr_type(starget) == AF_INET)
+ return sprintf(buf, "%u.%u.%u.%u\n",
+ NIPQUAD(iscsi_sin_addr(starget)));
+ else if(iscsi_addr_type(starget) == AF_INET6)
+ return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ NIP6(iscsi_sin6_addr(starget)));
+ return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL);
+
+static ssize_t
+show_isid(struct class_device *cdev, char *buf)
+{
+ struct scsi_target *starget = transport_class_to_starget(cdev);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+
+ if (i->fnt->get_isid)
+ i->fnt->get_isid(starget);
+
+ return sprintf(buf, "%02x%02x%02x%02x%02x%02x\n",
+ iscsi_isid(starget)[0], iscsi_isid(starget)[1],
+ iscsi_isid(starget)[2], iscsi_isid(starget)[3],
+ iscsi_isid(starget)[4], iscsi_isid(starget)[5]);
+}
+static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL);
+
+/*
+ * This is used for iSCSI names. Normally, we follow
+ * the transport class convention of having the lld
+ * set the field, but in these cases the value is
+ * too large.
+ */
+#define iscsi_session_show_str_fn(field) \
+ \
+static ssize_t \
+show_session_str_##field(struct class_device *cdev, char *buf) \
+{ \
+ ssize_t ret = 0; \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+ \
+ if (i->fnt->get_##field) \
+ ret = i->fnt->get_##field(starget, buf, PAGE_SIZE); \
+ return ret; \
+}
+
+#define iscsi_session_rd_str_attr(field) \
+ iscsi_session_show_str_fn(field) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_str_##field, NULL);
+
+iscsi_session_rd_str_attr(target_name);
+iscsi_session_rd_str_attr(target_alias);
+
+/*
+ * iSCSI host attrs
+ */
+
+/*
+ * Again, this is used for iSCSI names. Normally, we follow
+ * the transport class convention of having the lld set
+ * the field, but in these cases the value is too large.
+ */
+#define iscsi_host_show_str_fn(field) \
+ \
+static ssize_t \
+show_host_str_##field(struct class_device *cdev, char *buf) \
+{ \
+ int ret = 0; \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+ \
+ if (i->fnt->get_##field) \
+ ret = i->fnt->get_##field(shost, buf, PAGE_SIZE); \
+ return ret; \
+}
+
+#define iscsi_host_rd_str_attr(field) \
+ iscsi_host_show_str_fn(field) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_host_str_##field, NULL);
+
+iscsi_host_rd_str_attr(initiator_name);
+iscsi_host_rd_str_attr(initiator_alias);
+
+#define SETUP_SESSION_RD_ATTR(field) \
+ if (i->fnt->show_##field) { \
+ i->session_attrs[count] = &class_device_attr_##field; \
+ count++; \
+ }
+
+#define SETUP_HOST_RD_ATTR(field) \
+ if (i->fnt->show_##field) { \
+ i->host_attrs[count] = &class_device_attr_##field; \
+ count++; \
+ }
+
+static int iscsi_host_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct iscsi_internal *i;
+
+ if (!scsi_is_host_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &iscsi_host_class.class)
+ return 0;
+
+ i = to_iscsi_internal(shost->transportt);
+
+ return &i->t.host_attrs.ac == cont;
+}
+
+static int iscsi_target_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct iscsi_internal *i;
+
+ if (!scsi_is_target_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev->parent);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &iscsi_host_class.class)
+ return 0;
+
+ i = to_iscsi_internal(shost->transportt);
+
+ return &i->t.target_attrs.ac == cont;
+}
+
+struct scsi_transport_template *
+iscsi_attach_transport(struct iscsi_function_template *fnt)
+{
+ struct iscsi_internal *i = kmalloc(sizeof(struct iscsi_internal),
+ GFP_KERNEL);
+ int count = 0;
+
+ if (unlikely(!i))
+ return NULL;
+
+ memset(i, 0, sizeof(struct iscsi_internal));
+ i->fnt = fnt;
+
+ i->t.target_attrs.ac.attrs = &i->session_attrs[0];
+ i->t.target_attrs.ac.class = &iscsi_transport_class.class;
+ i->t.target_attrs.ac.match = iscsi_target_match;
+ transport_container_register(&i->t.target_attrs);
+ i->t.target_size = sizeof(struct iscsi_class_session);
+
+ SETUP_SESSION_RD_ATTR(tsih);
+ SETUP_SESSION_RD_ATTR(isid);
+ SETUP_SESSION_RD_ATTR(header_digest);
+ SETUP_SESSION_RD_ATTR(data_digest);
+ SETUP_SESSION_RD_ATTR(target_name);
+ SETUP_SESSION_RD_ATTR(target_alias);
+ SETUP_SESSION_RD_ATTR(port);
+ SETUP_SESSION_RD_ATTR(tpgt);
+ SETUP_SESSION_RD_ATTR(ip_address);
+ SETUP_SESSION_RD_ATTR(initial_r2t);
+ SETUP_SESSION_RD_ATTR(immediate_data);
+ SETUP_SESSION_RD_ATTR(max_recv_data_segment_len);
+ SETUP_SESSION_RD_ATTR(max_burst_len);
+ SETUP_SESSION_RD_ATTR(first_burst_len);
+ SETUP_SESSION_RD_ATTR(def_time2wait);
+ SETUP_SESSION_RD_ATTR(def_time2retain);
+ SETUP_SESSION_RD_ATTR(max_outstanding_r2t);
+ SETUP_SESSION_RD_ATTR(data_pdu_in_order);
+ SETUP_SESSION_RD_ATTR(data_sequence_in_order);
+ SETUP_SESSION_RD_ATTR(erl);
+
+ BUG_ON(count > ISCSI_SESSION_ATTRS);
+ i->session_attrs[count] = NULL;
+
+ i->t.host_attrs.ac.attrs = &i->host_attrs[0];
+ i->t.host_attrs.ac.class = &iscsi_host_class.class;
+ i->t.host_attrs.ac.match = iscsi_host_match;
+ transport_container_register(&i->t.host_attrs);
+ i->t.host_size = 0;
+
+ count = 0;
+ SETUP_HOST_RD_ATTR(initiator_name);
+ SETUP_HOST_RD_ATTR(initiator_alias);
+
+ BUG_ON(count > ISCSI_HOST_ATTRS);
+ i->host_attrs[count] = NULL;
+
+ return &i->t;
+}
+
+EXPORT_SYMBOL(iscsi_attach_transport);
+
+void iscsi_release_transport(struct scsi_transport_template *t)
+{
+ struct iscsi_internal *i = to_iscsi_internal(t);
+
+ transport_container_unregister(&i->t.target_attrs);
+ transport_container_unregister(&i->t.host_attrs);
+
+ kfree(i);
+}
+
+EXPORT_SYMBOL(iscsi_release_transport);
+
+static __init int iscsi_transport_init(void)
+{
+ int err = transport_class_register(&iscsi_transport_class);
+
+ if (err)
+ return err;
+ return transport_class_register(&iscsi_host_class);
+}
+
+static void __exit iscsi_transport_exit(void)
+{
+ transport_class_unregister(&iscsi_host_class);
+ transport_class_unregister(&iscsi_transport_class);
+}
+
+module_init(iscsi_transport_init);
+module_exit(iscsi_transport_exit);
+
+MODULE_AUTHOR("Mike Christie");
+MODULE_DESCRIPTION("iSCSI Transport Attributes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c
new file mode 100644
index 000000000000..303d7656f710
--- /dev/null
+++ b/drivers/scsi/scsi_transport_spi.c
@@ -0,0 +1,1020 @@
+/*
+ * Parallel SCSI (SPI) transport specific attributes exported to sysfs.
+ *
+ * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (c) 2004, 2005 James Bottomley <James.Bottomley@SteelEye.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
+ */
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <asm/semaphore.h>
+#include <scsi/scsi.h>
+#include "scsi_priv.h"
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_request.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+#define SPI_PRINTK(x, l, f, a...) dev_printk(l, &(x)->dev, f , ##a)
+
+#define SPI_NUM_ATTRS 10 /* increase this if you add attributes */
+#define SPI_OTHER_ATTRS 1 /* Increase this if you add "always
+ * on" attributes */
+#define SPI_HOST_ATTRS 1
+
+#define SPI_MAX_ECHO_BUFFER_SIZE 4096
+
+/* Private data accessors (keep these out of the header file) */
+#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_pending)
+#define spi_dv_sem(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_sem)
+
+struct spi_internal {
+ struct scsi_transport_template t;
+ struct spi_function_template *f;
+ /* The actual attributes */
+ struct class_device_attribute private_attrs[SPI_NUM_ATTRS];
+ /* The array of null terminated pointers to attributes
+ * needed by scsi_sysfs.c */
+ struct class_device_attribute *attrs[SPI_NUM_ATTRS + SPI_OTHER_ATTRS + 1];
+ struct class_device_attribute private_host_attrs[SPI_HOST_ATTRS];
+ struct class_device_attribute *host_attrs[SPI_HOST_ATTRS + 1];
+};
+
+#define to_spi_internal(tmpl) container_of(tmpl, struct spi_internal, t)
+
+static const int ppr_to_ps[] = {
+ /* The PPR values 0-6 are reserved, fill them in when
+ * the committee defines them */
+ -1, /* 0x00 */
+ -1, /* 0x01 */
+ -1, /* 0x02 */
+ -1, /* 0x03 */
+ -1, /* 0x04 */
+ -1, /* 0x05 */
+ -1, /* 0x06 */
+ 3125, /* 0x07 */
+ 6250, /* 0x08 */
+ 12500, /* 0x09 */
+ 25000, /* 0x0a */
+ 30300, /* 0x0b */
+ 50000, /* 0x0c */
+};
+/* The PPR values at which you calculate the period in ns by multiplying
+ * by 4 */
+#define SPI_STATIC_PPR 0x0c
+
+static int sprint_frac(char *dest, int value, int denom)
+{
+ int frac = value % denom;
+ int result = sprintf(dest, "%d", value / denom);
+
+ if (frac == 0)
+ return result;
+ dest[result++] = '.';
+
+ do {
+ denom /= 10;
+ sprintf(dest + result, "%d", frac / denom);
+ result++;
+ frac %= denom;
+ } while (frac);
+
+ dest[result++] = '\0';
+ return result;
+}
+
+static struct {
+ enum spi_signal_type value;
+ char *name;
+} signal_types[] = {
+ { SPI_SIGNAL_UNKNOWN, "unknown" },
+ { SPI_SIGNAL_SE, "SE" },
+ { SPI_SIGNAL_LVD, "LVD" },
+ { SPI_SIGNAL_HVD, "HVD" },
+};
+
+static inline const char *spi_signal_to_string(enum spi_signal_type type)
+{
+ int i;
+
+ for (i = 0; i < sizeof(signal_types)/sizeof(signal_types[0]); i++) {
+ if (type == signal_types[i].value)
+ return signal_types[i].name;
+ }
+ return NULL;
+}
+static inline enum spi_signal_type spi_signal_to_value(const char *name)
+{
+ int i, len;
+
+ for (i = 0; i < sizeof(signal_types)/sizeof(signal_types[0]); i++) {
+ len = strlen(signal_types[i].name);
+ if (strncmp(name, signal_types[i].name, len) == 0 &&
+ (name[len] == '\n' || name[len] == '\0'))
+ return signal_types[i].value;
+ }
+ return SPI_SIGNAL_UNKNOWN;
+}
+
+static int spi_host_setup(struct device *dev)
+{
+ struct Scsi_Host *shost = dev_to_shost(dev);
+
+ spi_signalling(shost) = SPI_SIGNAL_UNKNOWN;
+
+ return 0;
+}
+
+static DECLARE_TRANSPORT_CLASS(spi_host_class,
+ "spi_host",
+ spi_host_setup,
+ NULL,
+ NULL);
+
+static int spi_host_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct spi_internal *i;
+
+ if (!scsi_is_host_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &spi_host_class.class)
+ return 0;
+
+ i = to_spi_internal(shost->transportt);
+
+ return &i->t.host_attrs.ac == cont;
+}
+
+static int spi_device_configure(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_target *starget = sdev->sdev_target;
+
+ /* Populate the target capability fields with the values
+ * gleaned from the device inquiry */
+
+ spi_support_sync(starget) = scsi_device_sync(sdev);
+ spi_support_wide(starget) = scsi_device_wide(sdev);
+ spi_support_dt(starget) = scsi_device_dt(sdev);
+ spi_support_dt_only(starget) = scsi_device_dt_only(sdev);
+ spi_support_ius(starget) = scsi_device_ius(sdev);
+ spi_support_qas(starget) = scsi_device_qas(sdev);
+
+ return 0;
+}
+
+static int spi_setup_transport_attrs(struct device *dev)
+{
+ struct scsi_target *starget = to_scsi_target(dev);
+
+ spi_period(starget) = -1; /* illegal value */
+ spi_offset(starget) = 0; /* async */
+ spi_width(starget) = 0; /* narrow */
+ spi_iu(starget) = 0; /* no IU */
+ spi_dt(starget) = 0; /* ST */
+ spi_qas(starget) = 0;
+ spi_wr_flow(starget) = 0;
+ spi_rd_strm(starget) = 0;
+ spi_rti(starget) = 0;
+ spi_pcomp_en(starget) = 0;
+ spi_dv_pending(starget) = 0;
+ spi_initial_dv(starget) = 0;
+ init_MUTEX(&spi_dv_sem(starget));
+
+ return 0;
+}
+
+#define spi_transport_show_function(field, format_string) \
+ \
+static ssize_t \
+show_spi_transport_##field(struct class_device *cdev, char *buf) \
+{ \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct spi_transport_attrs *tp; \
+ struct spi_internal *i = to_spi_internal(shost->transportt); \
+ tp = (struct spi_transport_attrs *)&starget->starget_data; \
+ if (i->f->get_##field) \
+ i->f->get_##field(starget); \
+ return snprintf(buf, 20, format_string, tp->field); \
+}
+
+#define spi_transport_store_function(field, format_string) \
+static ssize_t \
+store_spi_transport_##field(struct class_device *cdev, const char *buf, \
+ size_t count) \
+{ \
+ int val; \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct spi_internal *i = to_spi_internal(shost->transportt); \
+ \
+ val = simple_strtoul(buf, NULL, 0); \
+ i->f->set_##field(starget, val); \
+ return count; \
+}
+
+#define spi_transport_rd_attr(field, format_string) \
+ spi_transport_show_function(field, format_string) \
+ spi_transport_store_function(field, format_string) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR, \
+ show_spi_transport_##field, \
+ store_spi_transport_##field);
+
+/* The Parallel SCSI Tranport Attributes: */
+spi_transport_rd_attr(offset, "%d\n");
+spi_transport_rd_attr(width, "%d\n");
+spi_transport_rd_attr(iu, "%d\n");
+spi_transport_rd_attr(dt, "%d\n");
+spi_transport_rd_attr(qas, "%d\n");
+spi_transport_rd_attr(wr_flow, "%d\n");
+spi_transport_rd_attr(rd_strm, "%d\n");
+spi_transport_rd_attr(rti, "%d\n");
+spi_transport_rd_attr(pcomp_en, "%d\n");
+
+static ssize_t
+store_spi_revalidate(struct class_device *cdev, const char *buf, size_t count)
+{
+ struct scsi_target *starget = transport_class_to_starget(cdev);
+
+ /* FIXME: we're relying on an awful lot of device internals
+ * here. We really need a function to get the first available
+ * child */
+ struct device *dev = container_of(starget->dev.children.next, struct device, node);
+ struct scsi_device *sdev = to_scsi_device(dev);
+ spi_dv_device(sdev);
+ return count;
+}
+static CLASS_DEVICE_ATTR(revalidate, S_IWUSR, NULL, store_spi_revalidate);
+
+/* Translate the period into ns according to the current spec
+ * for SDTR/PPR messages */
+static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf)
+
+{
+ struct scsi_target *starget = transport_class_to_starget(cdev);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct spi_transport_attrs *tp;
+ int len, picosec;
+ struct spi_internal *i = to_spi_internal(shost->transportt);
+
+ tp = (struct spi_transport_attrs *)&starget->starget_data;
+
+ if (i->f->get_period)
+ i->f->get_period(starget);
+
+ if (tp->period < 0 || tp->period > 0xff) {
+ picosec = -1;
+ } else if (tp->period <= SPI_STATIC_PPR) {
+ picosec = ppr_to_ps[tp->period];
+ } else {
+ picosec = tp->period * 4000;
+ }
+
+ if (picosec == -1) {
+ len = sprintf(buf, "reserved");
+ } else {
+ len = sprint_frac(buf, picosec, 1000);
+ }
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ return len;
+}
+
+static ssize_t
+store_spi_transport_period(struct class_device *cdev, const char *buf,
+ size_t count)
+{
+ struct scsi_target *starget = transport_class_to_starget(cdev);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct spi_internal *i = to_spi_internal(shost->transportt);
+ int j, picosec, period = -1;
+ char *endp;
+
+ picosec = simple_strtoul(buf, &endp, 10) * 1000;
+ if (*endp == '.') {
+ int mult = 100;
+ do {
+ endp++;
+ if (!isdigit(*endp))
+ break;
+ picosec += (*endp - '0') * mult;
+ mult /= 10;
+ } while (mult > 0);
+ }
+
+ for (j = 0; j <= SPI_STATIC_PPR; j++) {
+ if (ppr_to_ps[j] < picosec)
+ continue;
+ period = j;
+ break;
+ }
+
+ if (period == -1)
+ period = picosec / 4000;
+
+ if (period > 0xff)
+ period = 0xff;
+
+ i->f->set_period(starget, period);
+
+ return count;
+}
+
+static CLASS_DEVICE_ATTR(period, S_IRUGO | S_IWUSR,
+ show_spi_transport_period,
+ store_spi_transport_period);
+
+static ssize_t show_spi_host_signalling(struct class_device *cdev, char *buf)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct spi_internal *i = to_spi_internal(shost->transportt);
+
+ if (i->f->get_signalling)
+ i->f->get_signalling(shost);
+
+ return sprintf(buf, "%s\n", spi_signal_to_string(spi_signalling(shost)));
+}
+static ssize_t store_spi_host_signalling(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(cdev);
+ struct spi_internal *i = to_spi_internal(shost->transportt);
+ enum spi_signal_type type = spi_signal_to_value(buf);
+
+ if (type != SPI_SIGNAL_UNKNOWN)
+ i->f->set_signalling(shost, type);
+
+ return count;
+}
+static CLASS_DEVICE_ATTR(signalling, S_IRUGO | S_IWUSR,
+ show_spi_host_signalling,
+ store_spi_host_signalling);
+
+#define DV_SET(x, y) \
+ if(i->f->set_##x) \
+ i->f->set_##x(sdev->sdev_target, y)
+
+#define DV_LOOPS 3
+#define DV_TIMEOUT (10*HZ)
+#define DV_RETRIES 3 /* should only need at most
+ * two cc/ua clears */
+
+enum spi_compare_returns {
+ SPI_COMPARE_SUCCESS,
+ SPI_COMPARE_FAILURE,
+ SPI_COMPARE_SKIP_TEST,
+};
+
+
+/* This is for read/write Domain Validation: If the device supports
+ * an echo buffer, we do read/write tests to it */
+static enum spi_compare_returns
+spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
+ u8 *ptr, const int retries)
+{
+ struct scsi_device *sdev = sreq->sr_device;
+ int len = ptr - buffer;
+ int j, k, r;
+ unsigned int pattern = 0x0000ffff;
+
+ const char spi_write_buffer[] = {
+ WRITE_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0
+ };
+ const char spi_read_buffer[] = {
+ READ_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0
+ };
+
+ /* set up the pattern buffer. Doesn't matter if we spill
+ * slightly beyond since that's where the read buffer is */
+ for (j = 0; j < len; ) {
+
+ /* fill the buffer with counting (test a) */
+ for ( ; j < min(len, 32); j++)
+ buffer[j] = j;
+ k = j;
+ /* fill the buffer with alternating words of 0x0 and
+ * 0xffff (test b) */
+ for ( ; j < min(len, k + 32); j += 2) {
+ u16 *word = (u16 *)&buffer[j];
+
+ *word = (j & 0x02) ? 0x0000 : 0xffff;
+ }
+ k = j;
+ /* fill with crosstalk (alternating 0x5555 0xaaa)
+ * (test c) */
+ for ( ; j < min(len, k + 32); j += 2) {
+ u16 *word = (u16 *)&buffer[j];
+
+ *word = (j & 0x02) ? 0x5555 : 0xaaaa;
+ }
+ k = j;
+ /* fill with shifting bits (test d) */
+ for ( ; j < min(len, k + 32); j += 4) {
+ u32 *word = (unsigned int *)&buffer[j];
+ u32 roll = (pattern & 0x80000000) ? 1 : 0;
+
+ *word = pattern;
+ pattern = (pattern << 1) | roll;
+ }
+ /* don't bother with random data (test e) */
+ }
+
+ for (r = 0; r < retries; r++) {
+ sreq->sr_cmd_len = 0; /* wait_req to fill in */
+ sreq->sr_data_direction = DMA_TO_DEVICE;
+ scsi_wait_req(sreq, spi_write_buffer, buffer, len,
+ DV_TIMEOUT, DV_RETRIES);
+ if(sreq->sr_result || !scsi_device_online(sdev)) {
+ struct scsi_sense_hdr sshdr;
+
+ scsi_device_set_state(sdev, SDEV_QUIESCE);
+ if (scsi_request_normalize_sense(sreq, &sshdr)
+ && sshdr.sense_key == ILLEGAL_REQUEST
+ /* INVALID FIELD IN CDB */
+ && sshdr.asc == 0x24 && sshdr.ascq == 0x00)
+ /* This would mean that the drive lied
+ * to us about supporting an echo
+ * buffer (unfortunately some Western
+ * Digital drives do precisely this)
+ */
+ return SPI_COMPARE_SKIP_TEST;
+
+
+ SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result);
+ return SPI_COMPARE_FAILURE;
+ }
+
+ memset(ptr, 0, len);
+ sreq->sr_cmd_len = 0; /* wait_req to fill in */
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+ scsi_wait_req(sreq, spi_read_buffer, ptr, len,
+ DV_TIMEOUT, DV_RETRIES);
+ scsi_device_set_state(sdev, SDEV_QUIESCE);
+
+ if (memcmp(buffer, ptr, len) != 0)
+ return SPI_COMPARE_FAILURE;
+ }
+ return SPI_COMPARE_SUCCESS;
+}
+
+/* This is for the simplest form of Domain Validation: a read test
+ * on the inquiry data from the device */
+static enum spi_compare_returns
+spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer,
+ u8 *ptr, const int retries)
+{
+ int r;
+ const int len = sreq->sr_device->inquiry_len;
+ struct scsi_device *sdev = sreq->sr_device;
+ const char spi_inquiry[] = {
+ INQUIRY, 0, 0, 0, len, 0
+ };
+
+ for (r = 0; r < retries; r++) {
+ sreq->sr_cmd_len = 0; /* wait_req to fill in */
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+
+ memset(ptr, 0, len);
+
+ scsi_wait_req(sreq, spi_inquiry, ptr, len,
+ DV_TIMEOUT, DV_RETRIES);
+
+ if(sreq->sr_result || !scsi_device_online(sdev)) {
+ scsi_device_set_state(sdev, SDEV_QUIESCE);
+ return SPI_COMPARE_FAILURE;
+ }
+
+ /* If we don't have the inquiry data already, the
+ * first read gets it */
+ if (ptr == buffer) {
+ ptr += len;
+ --r;
+ continue;
+ }
+
+ if (memcmp(buffer, ptr, len) != 0)
+ /* failure */
+ return SPI_COMPARE_FAILURE;
+ }
+ return SPI_COMPARE_SUCCESS;
+}
+
+static enum spi_compare_returns
+spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr,
+ enum spi_compare_returns
+ (*compare_fn)(struct scsi_request *, u8 *, u8 *, int))
+{
+ struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt);
+ struct scsi_device *sdev = sreq->sr_device;
+ int period = 0, prevperiod = 0;
+ enum spi_compare_returns retval;
+
+
+ for (;;) {
+ int newperiod;
+ retval = compare_fn(sreq, buffer, ptr, DV_LOOPS);
+
+ if (retval == SPI_COMPARE_SUCCESS
+ || retval == SPI_COMPARE_SKIP_TEST)
+ break;
+
+ /* OK, retrain, fallback */
+ if (i->f->get_period)
+ i->f->get_period(sdev->sdev_target);
+ newperiod = spi_period(sdev->sdev_target);
+ period = newperiod > period ? newperiod : period;
+ if (period < 0x0d)
+ period++;
+ else
+ period += period >> 1;
+
+ if (unlikely(period > 0xff || period == prevperiod)) {
+ /* Total failure; set to async and return */
+ SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation Failure, dropping back to Asynchronous\n");
+ DV_SET(offset, 0);
+ return SPI_COMPARE_FAILURE;
+ }
+ SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation detected failure, dropping back\n");
+ DV_SET(period, period);
+ prevperiod = period;
+ }
+ return retval;
+}
+
+static int
+spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
+{
+ int l;
+
+ /* first off do a test unit ready. This can error out
+ * because of reservations or some other reason. If it
+ * fails, the device won't let us write to the echo buffer
+ * so just return failure */
+
+ const char spi_test_unit_ready[] = {
+ TEST_UNIT_READY, 0, 0, 0, 0, 0
+ };
+
+ const char spi_read_buffer_descriptor[] = {
+ READ_BUFFER, 0x0b, 0, 0, 0, 0, 0, 0, 4, 0
+ };
+
+
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_NONE;
+
+ /* We send a set of three TURs to clear any outstanding
+ * unit attention conditions if they exist (Otherwise the
+ * buffer tests won't be happy). If the TUR still fails
+ * (reservation conflict, device not ready, etc) just
+ * skip the write tests */
+ for (l = 0; ; l++) {
+ scsi_wait_req(sreq, spi_test_unit_ready, NULL, 0,
+ DV_TIMEOUT, DV_RETRIES);
+
+ if(sreq->sr_result) {
+ if(l >= 3)
+ return 0;
+ } else {
+ /* TUR succeeded */
+ break;
+ }
+ }
+
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
+
+ scsi_wait_req(sreq, spi_read_buffer_descriptor, buffer, 4,
+ DV_TIMEOUT, DV_RETRIES);
+
+ if (sreq->sr_result)
+ /* Device has no echo buffer */
+ return 0;
+
+ return buffer[3] + ((buffer[2] & 0x1f) << 8);
+}
+
+static void
+spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
+{
+ struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt);
+ struct scsi_device *sdev = sreq->sr_device;
+ int len = sdev->inquiry_len;
+ /* first set us up for narrow async */
+ DV_SET(offset, 0);
+ DV_SET(width, 0);
+
+ if (spi_dv_device_compare_inquiry(sreq, buffer, buffer, DV_LOOPS)
+ != SPI_COMPARE_SUCCESS) {
+ SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation Initial Inquiry Failed\n");
+ /* FIXME: should probably offline the device here? */
+ return;
+ }
+
+ /* test width */
+ if (i->f->set_width && sdev->wdtr) {
+ i->f->set_width(sdev->sdev_target, 1);
+
+ if (spi_dv_device_compare_inquiry(sreq, buffer,
+ buffer + len,
+ DV_LOOPS)
+ != SPI_COMPARE_SUCCESS) {
+ SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Wide Transfers Fail\n");
+ i->f->set_width(sdev->sdev_target, 0);
+ }
+ }
+
+ if (!i->f->set_period)
+ return;
+
+ /* device can't handle synchronous */
+ if(!sdev->ppr && !sdev->sdtr)
+ return;
+
+ /* see if the device has an echo buffer. If it does we can
+ * do the SPI pattern write tests */
+
+ len = 0;
+ if (sdev->ppr)
+ len = spi_dv_device_get_echo_buffer(sreq, buffer);
+
+ retry:
+
+ /* now set up to the maximum */
+ DV_SET(offset, 255);
+ DV_SET(period, 1);
+
+ if (len == 0) {
+ SPI_PRINTK(sdev->sdev_target, KERN_INFO, "Domain Validation skipping write tests\n");
+ spi_dv_retrain(sreq, buffer, buffer + len,
+ spi_dv_device_compare_inquiry);
+ return;
+ }
+
+ if (len > SPI_MAX_ECHO_BUFFER_SIZE) {
+ SPI_PRINTK(sdev->sdev_target, KERN_WARNING, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE);
+ len = SPI_MAX_ECHO_BUFFER_SIZE;
+ }
+
+ if (spi_dv_retrain(sreq, buffer, buffer + len,
+ spi_dv_device_echo_buffer)
+ == SPI_COMPARE_SKIP_TEST) {
+ /* OK, the stupid drive can't do a write echo buffer
+ * test after all, fall back to the read tests */
+ len = 0;
+ goto retry;
+ }
+}
+
+
+/** spi_dv_device - Do Domain Validation on the device
+ * @sdev: scsi device to validate
+ *
+ * Performs the domain validation on the given device in the
+ * current execution thread. Since DV operations may sleep,
+ * the current thread must have user context. Also no SCSI
+ * related locks that would deadlock I/O issued by the DV may
+ * be held.
+ */
+void
+spi_dv_device(struct scsi_device *sdev)
+{
+ struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL);
+ struct scsi_target *starget = sdev->sdev_target;
+ u8 *buffer;
+ const int len = SPI_MAX_ECHO_BUFFER_SIZE*2;
+
+ if (unlikely(!sreq))
+ return;
+
+ if (unlikely(scsi_device_get(sdev)))
+ goto out_free_req;
+
+ buffer = kmalloc(len, GFP_KERNEL);
+
+ if (unlikely(!buffer))
+ goto out_put;
+
+ memset(buffer, 0, len);
+
+ /* We need to verify that the actual device will quiesce; the
+ * later target quiesce is just a nice to have */
+ if (unlikely(scsi_device_quiesce(sdev)))
+ goto out_free;
+
+ scsi_target_quiesce(starget);
+
+ spi_dv_pending(starget) = 1;
+ down(&spi_dv_sem(starget));
+
+ SPI_PRINTK(starget, KERN_INFO, "Beginning Domain Validation\n");
+
+ spi_dv_device_internal(sreq, buffer);
+
+ SPI_PRINTK(starget, KERN_INFO, "Ending Domain Validation\n");
+
+ up(&spi_dv_sem(starget));
+ spi_dv_pending(starget) = 0;
+
+ scsi_target_resume(starget);
+
+ spi_initial_dv(starget) = 1;
+
+ out_free:
+ kfree(buffer);
+ out_put:
+ scsi_device_put(sdev);
+ out_free_req:
+ scsi_release_request(sreq);
+}
+EXPORT_SYMBOL(spi_dv_device);
+
+struct work_queue_wrapper {
+ struct work_struct work;
+ struct scsi_device *sdev;
+};
+
+static void
+spi_dv_device_work_wrapper(void *data)
+{
+ struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data;
+ struct scsi_device *sdev = wqw->sdev;
+
+ kfree(wqw);
+ spi_dv_device(sdev);
+ spi_dv_pending(sdev->sdev_target) = 0;
+ scsi_device_put(sdev);
+}
+
+
+/**
+ * spi_schedule_dv_device - schedule domain validation to occur on the device
+ * @sdev: The device to validate
+ *
+ * Identical to spi_dv_device() above, except that the DV will be
+ * scheduled to occur in a workqueue later. All memory allocations
+ * are atomic, so may be called from any context including those holding
+ * SCSI locks.
+ */
+void
+spi_schedule_dv_device(struct scsi_device *sdev)
+{
+ struct work_queue_wrapper *wqw =
+ kmalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC);
+
+ if (unlikely(!wqw))
+ return;
+
+ if (unlikely(spi_dv_pending(sdev->sdev_target))) {
+ kfree(wqw);
+ return;
+ }
+ /* Set pending early (dv_device doesn't check it, only sets it) */
+ spi_dv_pending(sdev->sdev_target) = 1;
+ if (unlikely(scsi_device_get(sdev))) {
+ kfree(wqw);
+ spi_dv_pending(sdev->sdev_target) = 0;
+ return;
+ }
+
+ INIT_WORK(&wqw->work, spi_dv_device_work_wrapper, wqw);
+ wqw->sdev = sdev;
+
+ schedule_work(&wqw->work);
+}
+EXPORT_SYMBOL(spi_schedule_dv_device);
+
+/**
+ * spi_display_xfer_agreement - Print the current target transfer agreement
+ * @starget: The target for which to display the agreement
+ *
+ * Each SPI port is required to maintain a transfer agreement for each
+ * other port on the bus. This function prints a one-line summary of
+ * the current agreement; more detailed information is available in sysfs.
+ */
+void spi_display_xfer_agreement(struct scsi_target *starget)
+{
+ struct spi_transport_attrs *tp;
+ tp = (struct spi_transport_attrs *)&starget->starget_data;
+
+ if (tp->offset > 0 && tp->period > 0) {
+ unsigned int picosec, kb100;
+ char *scsi = "FAST-?";
+ char tmp[8];
+
+ if (tp->period <= SPI_STATIC_PPR) {
+ picosec = ppr_to_ps[tp->period];
+ switch (tp->period) {
+ case 7: scsi = "FAST-320"; break;
+ case 8: scsi = "FAST-160"; break;
+ case 9: scsi = "FAST-80"; break;
+ case 10:
+ case 11: scsi = "FAST-40"; break;
+ case 12: scsi = "FAST-20"; break;
+ }
+ } else {
+ picosec = tp->period * 4000;
+ if (tp->period < 25)
+ scsi = "FAST-20";
+ else if (tp->period < 50)
+ scsi = "FAST-10";
+ else
+ scsi = "FAST-5";
+ }
+
+ kb100 = (10000000 + picosec / 2) / picosec;
+ if (tp->width)
+ kb100 *= 2;
+ sprint_frac(tmp, picosec, 1000);
+
+ dev_info(&starget->dev,
+ "%s %sSCSI %d.%d MB/s %s%s%s (%s ns, offset %d)\n",
+ scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
+ tp->dt ? "DT" : "ST", tp->iu ? " IU" : "",
+ tp->qas ? " QAS" : "", tmp, tp->offset);
+ } else {
+ dev_info(&starget->dev, "%sasynchronous.\n",
+ tp->width ? "wide " : "");
+ }
+}
+EXPORT_SYMBOL(spi_display_xfer_agreement);
+
+#define SETUP_ATTRIBUTE(field) \
+ i->private_attrs[count] = class_device_attr_##field; \
+ if (!i->f->set_##field) { \
+ i->private_attrs[count].attr.mode = S_IRUGO; \
+ i->private_attrs[count].store = NULL; \
+ } \
+ i->attrs[count] = &i->private_attrs[count]; \
+ if (i->f->show_##field) \
+ count++
+
+#define SETUP_HOST_ATTRIBUTE(field) \
+ i->private_host_attrs[count] = class_device_attr_##field; \
+ if (!i->f->set_##field) { \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ } \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ count++
+
+static int spi_device_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct scsi_device *sdev;
+ struct Scsi_Host *shost;
+
+ if (!scsi_is_sdev_device(dev))
+ return 0;
+
+ sdev = to_scsi_device(dev);
+ shost = sdev->host;
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &spi_host_class.class)
+ return 0;
+ /* Note: this class has no device attributes, so it has
+ * no per-HBA allocation and thus we don't need to distinguish
+ * the attribute containers for the device */
+ return 1;
+}
+
+static int spi_target_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct spi_internal *i;
+
+ if (!scsi_is_target_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev->parent);
+ if (!shost->transportt || shost->transportt->host_attrs.ac.class
+ != &spi_host_class.class)
+ return 0;
+
+ i = to_spi_internal(shost->transportt);
+
+ return &i->t.target_attrs.ac == cont;
+}
+
+static DECLARE_TRANSPORT_CLASS(spi_transport_class,
+ "spi_transport",
+ spi_setup_transport_attrs,
+ NULL,
+ NULL);
+
+static DECLARE_ANON_TRANSPORT_CLASS(spi_device_class,
+ spi_device_match,
+ spi_device_configure);
+
+struct scsi_transport_template *
+spi_attach_transport(struct spi_function_template *ft)
+{
+ struct spi_internal *i = kmalloc(sizeof(struct spi_internal),
+ GFP_KERNEL);
+ int count = 0;
+ if (unlikely(!i))
+ return NULL;
+
+ memset(i, 0, sizeof(struct spi_internal));
+
+
+ i->t.target_attrs.ac.class = &spi_transport_class.class;
+ i->t.target_attrs.ac.attrs = &i->attrs[0];
+ i->t.target_attrs.ac.match = spi_target_match;
+ transport_container_register(&i->t.target_attrs);
+ i->t.target_size = sizeof(struct spi_transport_attrs);
+ i->t.host_attrs.ac.class = &spi_host_class.class;
+ i->t.host_attrs.ac.attrs = &i->host_attrs[0];
+ i->t.host_attrs.ac.match = spi_host_match;
+ transport_container_register(&i->t.host_attrs);
+ i->t.host_size = sizeof(struct spi_host_attrs);
+ i->f = ft;
+
+ SETUP_ATTRIBUTE(period);
+ SETUP_ATTRIBUTE(offset);
+ SETUP_ATTRIBUTE(width);
+ SETUP_ATTRIBUTE(iu);
+ SETUP_ATTRIBUTE(dt);
+ SETUP_ATTRIBUTE(qas);
+ SETUP_ATTRIBUTE(wr_flow);
+ SETUP_ATTRIBUTE(rd_strm);
+ SETUP_ATTRIBUTE(rti);
+ SETUP_ATTRIBUTE(pcomp_en);
+
+ /* if you add an attribute but forget to increase SPI_NUM_ATTRS
+ * this bug will trigger */
+ BUG_ON(count > SPI_NUM_ATTRS);
+
+ i->attrs[count++] = &class_device_attr_revalidate;
+
+ i->attrs[count] = NULL;
+
+ count = 0;
+ SETUP_HOST_ATTRIBUTE(signalling);
+
+ BUG_ON(count > SPI_HOST_ATTRS);
+
+ i->host_attrs[count] = NULL;
+
+ return &i->t;
+}
+EXPORT_SYMBOL(spi_attach_transport);
+
+void spi_release_transport(struct scsi_transport_template *t)
+{
+ struct spi_internal *i = to_spi_internal(t);
+
+ transport_container_unregister(&i->t.target_attrs);
+ transport_container_unregister(&i->t.host_attrs);
+
+ kfree(i);
+}
+EXPORT_SYMBOL(spi_release_transport);
+
+static __init int spi_transport_init(void)
+{
+ int error = transport_class_register(&spi_transport_class);
+ if (error)
+ return error;
+ error = anon_transport_class_register(&spi_device_class);
+ return transport_class_register(&spi_host_class);
+}
+
+static void __exit spi_transport_exit(void)
+{
+ transport_class_unregister(&spi_transport_class);
+ anon_transport_class_unregister(&spi_device_class);
+ transport_class_unregister(&spi_host_class);
+}
+
+MODULE_AUTHOR("Martin Hicks");
+MODULE_DESCRIPTION("SPI Transport Attributes");
+MODULE_LICENSE("GPL");
+
+module_init(spi_transport_init);
+module_exit(spi_transport_exit);
diff --git a/drivers/scsi/scsi_typedefs.h b/drivers/scsi/scsi_typedefs.h
new file mode 100644
index 000000000000..6c431323581c
--- /dev/null
+++ b/drivers/scsi/scsi_typedefs.h
@@ -0,0 +1,6 @@
+
+typedef struct scsi_host_template Scsi_Host_Template;
+typedef struct scsi_device Scsi_Device;
+typedef struct scsi_cmnd Scsi_Cmnd;
+typedef struct scsi_request Scsi_Request;
+typedef struct scsi_pointer Scsi_Pointer;
diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c
new file mode 100644
index 000000000000..b78354fc4b17
--- /dev/null
+++ b/drivers/scsi/scsicam.c
@@ -0,0 +1,245 @@
+/*
+ * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc.
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@Colorado.EDU
+ * +1 (303) 786-7975
+ *
+ * For more information, please consult the SCSI-CAM draft.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+#include <asm/unaligned.h>
+
+#include <scsi/scsicam.h>
+
+
+static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds,
+ unsigned int *secs);
+
+unsigned char *scsi_bios_ptable(struct block_device *dev)
+{
+ unsigned char *res = kmalloc(66, GFP_KERNEL);
+ if (res) {
+ struct block_device *bdev = dev->bd_contains;
+ Sector sect;
+ void *data = read_dev_sector(bdev, 0, &sect);
+ if (data) {
+ memcpy(res, data + 0x1be, 66);
+ put_dev_sector(sect);
+ } else {
+ kfree(res);
+ res = NULL;
+ }
+ }
+ return res;
+}
+EXPORT_SYMBOL(scsi_bios_ptable);
+
+/*
+ * Function : int scsicam_bios_param (struct block_device *bdev, ector_t capacity, int *ip)
+ *
+ * Purpose : to determine the BIOS mapping used for a drive in a
+ * SCSI-CAM system, storing the results in ip as required
+ * by the HDIO_GETGEO ioctl().
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip)
+{
+ unsigned char *p;
+ int ret;
+
+ p = scsi_bios_ptable(bdev);
+ if (!p)
+ return -1;
+
+ /* try to infer mapping from partition table */
+ ret = scsi_partsize(p, (unsigned long)capacity, (unsigned int *)ip + 2,
+ (unsigned int *)ip + 0, (unsigned int *)ip + 1);
+ kfree(p);
+
+ if (ret == -1) {
+ /* pick some standard mapping with at most 1024 cylinders,
+ and at most 62 sectors per track - this works up to
+ 7905 MB */
+ ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2,
+ (unsigned int *)ip + 0, (unsigned int *)ip + 1);
+ }
+
+ /* if something went wrong, then apparently we have to return
+ a geometry with more than 1024 cylinders */
+ if (ret || ip[0] > 255 || ip[1] > 63) {
+ if ((capacity >> 11) > 65534) {
+ ip[0] = 255;
+ ip[1] = 63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ }
+
+ if (capacity > 65535*63*255)
+ ip[2] = 65535;
+ else
+ ip[2] = (unsigned long)capacity / (ip[0] * ip[1]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(scsicam_bios_param);
+
+/*
+ * Function : static int scsi_partsize(unsigned char *buf, unsigned long
+ * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine the BIOS mapping used to create the partition
+ * table, storing the results in *cyls, *hds, and *secs
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+int scsi_partsize(unsigned char *buf, unsigned long capacity,
+ unsigned int *cyls, unsigned int *hds, unsigned int *secs)
+{
+ struct partition *p = (struct partition *)buf, *largest = NULL;
+ int i, largest_cyl;
+ int cyl, ext_cyl, end_head, end_cyl, end_sector;
+ unsigned int logical_end, physical_end, ext_physical_end;
+
+
+ if (*(unsigned short *) (buf + 64) == 0xAA55) {
+ for (largest_cyl = -1, i = 0; i < 4; ++i, ++p) {
+ if (!p->sys_ind)
+ continue;
+#ifdef DEBUG
+ printk("scsicam_bios_param : partition %d has system \n",
+ i);
+#endif
+ cyl = p->cyl + ((p->sector & 0xc0) << 2);
+ if (cyl > largest_cyl) {
+ largest_cyl = cyl;
+ largest = p;
+ }
+ }
+ }
+ if (largest) {
+ end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
+ end_head = largest->end_head;
+ end_sector = largest->end_sector & 0x3f;
+
+ if (end_head + 1 == 0 || end_sector == 0)
+ return -1;
+
+#ifdef DEBUG
+ printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
+ end_head, end_cyl, end_sector);
+#endif
+
+ physical_end = end_cyl * (end_head + 1) * end_sector +
+ end_head * end_sector + end_sector;
+
+ /* This is the actual _sector_ number at the end */
+ logical_end = get_unaligned(&largest->start_sect)
+ + get_unaligned(&largest->nr_sects);
+
+ /* This is for >1023 cylinders */
+ ext_cyl = (logical_end - (end_head * end_sector + end_sector))
+ / (end_head + 1) / end_sector;
+ ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
+ end_head * end_sector + end_sector;
+
+#ifdef DEBUG
+ printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
+ ,logical_end, physical_end, ext_physical_end, ext_cyl);
+#endif
+
+ if ((logical_end == physical_end) ||
+ (end_cyl == 1023 && ext_physical_end == logical_end)) {
+ *secs = end_sector;
+ *hds = end_head + 1;
+ *cyls = capacity / ((end_head + 1) * end_sector);
+ return 0;
+ }
+#ifdef DEBUG
+ printk("scsicam_bios_param : logical (%u) != physical (%u)\n",
+ logical_end, physical_end);
+#endif
+ }
+ return -1;
+}
+EXPORT_SYMBOL(scsi_partsize);
+
+/*
+ * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
+ * unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine a near-optimal int 0x13 mapping for a
+ * SCSI disk in terms of lost space of size capacity, storing
+ * the results in *cyls, *hds, and *secs.
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ * Extracted from
+ *
+ * WORKING X3T9.2
+ * DRAFT 792D
+ *
+ *
+ * Revision 6
+ * 10-MAR-94
+ * Information technology -
+ * SCSI-2 Common access method
+ * transport and SCSI interface module
+ *
+ * ANNEX A :
+ *
+ * setsize() converts a read capacity value to int 13h
+ * head-cylinder-sector requirements. It minimizes the value for
+ * number of heads and maximizes the number of cylinders. This
+ * will support rather large disks before the number of heads
+ * will not fit in 4 bits (or 6 bits). This algorithm also
+ * minimizes the number of sectors that will be unused at the end
+ * of the disk while allowing for very large disks to be
+ * accommodated. This algorithm does not use physical geometry.
+ */
+
+static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds,
+ unsigned int *secs)
+{
+ unsigned int rv = 0;
+ unsigned long heads, sectors, cylinders, temp;
+
+ cylinders = 1024L; /* Set number of cylinders to max */
+ sectors = 62L; /* Maximize sectors per track */
+
+ temp = cylinders * sectors; /* Compute divisor for heads */
+ heads = capacity / temp; /* Compute value for number of heads */
+ if (capacity % temp) { /* If no remainder, done! */
+ heads++; /* Else, increment number of heads */
+ temp = cylinders * heads; /* Compute divisor for sectors */
+ sectors = capacity / temp; /* Compute value for sectors per
+ track */
+ if (capacity % temp) { /* If no remainder, done! */
+ sectors++; /* Else, increment number of sectors */
+ temp = heads * sectors; /* Compute divisor for cylinders */
+ cylinders = capacity / temp; /* Compute number of cylinders */
+ }
+ }
+ if (cylinders == 0)
+ rv = (unsigned) -1; /* Give error if 0 cylinders */
+
+ *cyls = (unsigned int) cylinders; /* Stuff return values */
+ *secs = (unsigned int) sectors;
+ *hds = (unsigned int) heads;
+ return (rv);
+}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
new file mode 100644
index 000000000000..19afb25e44d3
--- /dev/null
+++ b/drivers/scsi/sd.c
@@ -0,0 +1,1740 @@
+/*
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
+ *
+ * Linux scsi disk driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ * Modification history:
+ * - Drew Eckhardt <drew@colorado.edu> original
+ * - Eric Youngdale <eric@andante.org> add scatter-gather, multiple
+ * outstanding request, and other enhancements.
+ * Support loadable low-level scsi drivers.
+ * - Jirka Hanika <geo@ff.cuni.cz> support more scsi disks using
+ * eight major numbers.
+ * - Richard Gooch <rgooch@atnf.csiro.au> support devfs.
+ * - Torben Mathiasen <tmm@image.dk> Resource allocation fixes in
+ * sd_init and cleanups.
+ * - Alex Davis <letmein@erols.com> Fix problem where partition info
+ * not being read in sd_open. Fix problem where removable media
+ * could be ejected after sd_open.
+ * - Douglas Gilbert <dgilbert@interlog.com> cleanup for lk 2.5.x
+ * - Badari Pulavarty <pbadari@us.ibm.com>, Matthew Wilcox
+ * <willy@debian.org>, Kurt Garloff <garloff@suse.de>:
+ * Support 32k/1M disks.
+ *
+ * Logging policy (needs CONFIG_SCSI_LOGGING defined):
+ * - setting up transfer: SCSI_LOG_HLQUEUE levels 1 and 2
+ * - end of transfer (bh + scsi_lib): SCSI_LOG_HLCOMPLETE level 1
+ * - entering sd_ioctl: SCSI_LOG_IOCTL level 1
+ * - entering other commands: SCSI_LOG_HLQUEUE level 3
+ * Note: when the logging level is set by the user, it must be greater
+ * than the level indicated above to trigger output.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/bio.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+#include <linux/kref.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
+#include <scsi/scsicam.h>
+
+#include "scsi_logging.h"
+
+/*
+ * More than enough for everybody ;) The huge number of majors
+ * is a leftover from 16bit dev_t days, we don't really need that
+ * much numberspace.
+ */
+#define SD_MAJORS 16
+
+/*
+ * This is limited by the naming scheme enforced in sd_probe,
+ * add another character to it if you really need more disks.
+ */
+#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26)
+
+/*
+ * Time out in seconds for disks and Magneto-opticals (which are slower).
+ */
+#define SD_TIMEOUT (30 * HZ)
+#define SD_MOD_TIMEOUT (75 * HZ)
+
+/*
+ * Number of allowed retries
+ */
+#define SD_MAX_RETRIES 5
+#define SD_PASSTHROUGH_RETRIES 1
+
+static void scsi_disk_release(struct kref *kref);
+
+struct scsi_disk {
+ struct scsi_driver *driver; /* always &sd_template */
+ struct scsi_device *device;
+ struct kref kref;
+ struct gendisk *disk;
+ unsigned int openers; /* protected by BKL for now, yuck */
+ sector_t capacity; /* size in 512-byte sectors */
+ u32 index;
+ u8 media_present;
+ u8 write_prot;
+ unsigned WCE : 1; /* state of disk WCE bit */
+ unsigned RCD : 1; /* state of disk RCD bit, unused */
+};
+
+static DEFINE_IDR(sd_index_idr);
+static DEFINE_SPINLOCK(sd_index_lock);
+
+/* This semaphore is used to mediate the 0->1 reference get in the
+ * face of object destruction (i.e. we can't allow a get on an
+ * object after last put) */
+static DECLARE_MUTEX(sd_ref_sem);
+
+static int sd_revalidate_disk(struct gendisk *disk);
+static void sd_rw_intr(struct scsi_cmnd * SCpnt);
+
+static int sd_probe(struct device *);
+static int sd_remove(struct device *);
+static void sd_shutdown(struct device *dev);
+static void sd_rescan(struct device *);
+static int sd_init_command(struct scsi_cmnd *);
+static int sd_issue_flush(struct device *, sector_t *);
+static void sd_end_flush(request_queue_t *, struct request *);
+static int sd_prepare_flush(request_queue_t *, struct request *);
+static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
+ struct scsi_request *SRpnt, unsigned char *buffer);
+
+static struct scsi_driver sd_template = {
+ .owner = THIS_MODULE,
+ .gendrv = {
+ .name = "sd",
+ .probe = sd_probe,
+ .remove = sd_remove,
+ .shutdown = sd_shutdown,
+ },
+ .rescan = sd_rescan,
+ .init_command = sd_init_command,
+ .issue_flush = sd_issue_flush,
+ .prepare_flush = sd_prepare_flush,
+ .end_flush = sd_end_flush,
+};
+
+/*
+ * Device no to disk mapping:
+ *
+ * major disc2 disc p1
+ * |............|.............|....|....| <- dev_t
+ * 31 20 19 8 7 4 3 0
+ *
+ * Inside a major, we have 16k disks, however mapped non-
+ * contiguously. The first 16 disks are for major0, the next
+ * ones with major1, ... Disk 256 is for major0 again, disk 272
+ * for major1, ...
+ * As we stay compatible with our numbering scheme, we can reuse
+ * the well-know SCSI majors 8, 65--71, 136--143.
+ */
+static int sd_major(int major_idx)
+{
+ switch (major_idx) {
+ case 0:
+ return SCSI_DISK0_MAJOR;
+ case 1 ... 7:
+ return SCSI_DISK1_MAJOR + major_idx - 1;
+ case 8 ... 15:
+ return SCSI_DISK8_MAJOR + major_idx - 8;
+ default:
+ BUG();
+ return 0; /* shut up gcc */
+ }
+}
+
+#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,kref)
+
+static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
+{
+ return container_of(disk->private_data, struct scsi_disk, driver);
+}
+
+static struct scsi_disk *scsi_disk_get(struct gendisk *disk)
+{
+ struct scsi_disk *sdkp = NULL;
+
+ down(&sd_ref_sem);
+ if (disk->private_data == NULL)
+ goto out;
+ sdkp = scsi_disk(disk);
+ kref_get(&sdkp->kref);
+ if (scsi_device_get(sdkp->device))
+ goto out_put;
+ up(&sd_ref_sem);
+ return sdkp;
+
+ out_put:
+ kref_put(&sdkp->kref, scsi_disk_release);
+ sdkp = NULL;
+ out:
+ up(&sd_ref_sem);
+ return sdkp;
+}
+
+static void scsi_disk_put(struct scsi_disk *sdkp)
+{
+ struct scsi_device *sdev = sdkp->device;
+
+ down(&sd_ref_sem);
+ kref_put(&sdkp->kref, scsi_disk_release);
+ scsi_device_put(sdev);
+ up(&sd_ref_sem);
+}
+
+/**
+ * sd_init_command - build a scsi (read or write) command from
+ * information in the request structure.
+ * @SCpnt: pointer to mid-level's per scsi command structure that
+ * contains request and into which the scsi command is written
+ *
+ * Returns 1 if successful and 0 if error (or cannot be done now).
+ **/
+static int sd_init_command(struct scsi_cmnd * SCpnt)
+{
+ unsigned int this_count, timeout;
+ struct gendisk *disk;
+ sector_t block;
+ struct scsi_device *sdp = SCpnt->device;
+ struct request *rq = SCpnt->request;
+
+ timeout = sdp->timeout;
+
+ /*
+ * SG_IO from block layer already setup, just copy cdb basically
+ */
+ if (blk_pc_request(rq)) {
+ if (sizeof(rq->cmd) > sizeof(SCpnt->cmnd))
+ return 0;
+
+ memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd));
+ if (rq_data_dir(rq) == WRITE)
+ SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ else if (rq->data_len)
+ SCpnt->sc_data_direction = DMA_FROM_DEVICE;
+ else
+ SCpnt->sc_data_direction = DMA_NONE;
+
+ this_count = rq->data_len;
+ if (rq->timeout)
+ timeout = rq->timeout;
+
+ SCpnt->transfersize = rq->data_len;
+ SCpnt->allowed = SD_PASSTHROUGH_RETRIES;
+ goto queue;
+ }
+
+ /*
+ * we only do REQ_CMD and REQ_BLOCK_PC
+ */
+ if (!blk_fs_request(rq))
+ return 0;
+
+ disk = rq->rq_disk;
+ block = rq->sector;
+ this_count = SCpnt->request_bufflen >> 9;
+
+ SCSI_LOG_HLQUEUE(1, printk("sd_init_command: disk=%s, block=%llu, "
+ "count=%d\n", disk->disk_name,
+ (unsigned long long)block, this_count));
+
+ if (!sdp || !scsi_device_online(sdp) ||
+ block + rq->nr_sectors > get_capacity(disk)) {
+ SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n",
+ rq->nr_sectors));
+ SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
+ return 0;
+ }
+
+ if (sdp->changed) {
+ /*
+ * quietly refuse to do anything to a changed disc until
+ * the changed bit has been reset
+ */
+ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
+ return 0;
+ }
+ SCSI_LOG_HLQUEUE(2, printk("%s : block=%llu\n",
+ disk->disk_name, (unsigned long long)block));
+
+ /*
+ * If we have a 1K hardware sectorsize, prevent access to single
+ * 512 byte sectors. In theory we could handle this - in fact
+ * the scsi cdrom driver must be able to handle this because
+ * we typically use 1K blocksizes, and cdroms typically have
+ * 2K hardware sectorsizes. Of course, things are simpler
+ * with the cdrom, since it is read-only. For performance
+ * reasons, the filesystems should be able to handle this
+ * and not force the scsi disk driver to use bounce buffers
+ * for this.
+ */
+ if (sdp->sector_size == 1024) {
+ if ((block & 1) || (rq->nr_sectors & 1)) {
+ printk(KERN_ERR "sd: Bad block number requested");
+ return 0;
+ } else {
+ block = block >> 1;
+ this_count = this_count >> 1;
+ }
+ }
+ if (sdp->sector_size == 2048) {
+ if ((block & 3) || (rq->nr_sectors & 3)) {
+ printk(KERN_ERR "sd: Bad block number requested");
+ return 0;
+ } else {
+ block = block >> 2;
+ this_count = this_count >> 2;
+ }
+ }
+ if (sdp->sector_size == 4096) {
+ if ((block & 7) || (rq->nr_sectors & 7)) {
+ printk(KERN_ERR "sd: Bad block number requested");
+ return 0;
+ } else {
+ block = block >> 3;
+ this_count = this_count >> 3;
+ }
+ }
+ if (rq_data_dir(rq) == WRITE) {
+ if (!sdp->writeable) {
+ return 0;
+ }
+ SCpnt->cmnd[0] = WRITE_6;
+ SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ } else if (rq_data_dir(rq) == READ) {
+ SCpnt->cmnd[0] = READ_6;
+ SCpnt->sc_data_direction = DMA_FROM_DEVICE;
+ } else {
+ printk(KERN_ERR "sd: Unknown command %lx\n", rq->flags);
+/* overkill panic("Unknown sd command %lx\n", rq->flags); */
+ return 0;
+ }
+
+ SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
+ disk->disk_name, (rq_data_dir(rq) == WRITE) ?
+ "writing" : "reading", this_count, rq->nr_sectors));
+
+ SCpnt->cmnd[1] = 0;
+
+ if (block > 0xffffffff) {
+ SCpnt->cmnd[0] += READ_16 - READ_6;
+ SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
+ SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0;
+ SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0;
+ SCpnt->cmnd[5] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0;
+ SCpnt->cmnd[6] = (unsigned char) (block >> 24) & 0xff;
+ SCpnt->cmnd[7] = (unsigned char) (block >> 16) & 0xff;
+ SCpnt->cmnd[8] = (unsigned char) (block >> 8) & 0xff;
+ SCpnt->cmnd[9] = (unsigned char) block & 0xff;
+ SCpnt->cmnd[10] = (unsigned char) (this_count >> 24) & 0xff;
+ SCpnt->cmnd[11] = (unsigned char) (this_count >> 16) & 0xff;
+ SCpnt->cmnd[12] = (unsigned char) (this_count >> 8) & 0xff;
+ SCpnt->cmnd[13] = (unsigned char) this_count & 0xff;
+ SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0;
+ } else if ((this_count > 0xff) || (block > 0x1fffff) ||
+ SCpnt->device->use_10_for_rw) {
+ if (this_count > 0xffff)
+ this_count = 0xffff;
+
+ SCpnt->cmnd[0] += READ_10 - READ_6;
+ SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
+ SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
+ SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
+ SCpnt->cmnd[5] = (unsigned char) block & 0xff;
+ SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0;
+ SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
+ SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
+ } else {
+ if (this_count > 0xff)
+ this_count = 0xff;
+
+ SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ SCpnt->cmnd[2] = (unsigned char) ((block >> 8) & 0xff);
+ SCpnt->cmnd[3] = (unsigned char) block & 0xff;
+ SCpnt->cmnd[4] = (unsigned char) this_count;
+ SCpnt->cmnd[5] = 0;
+ }
+ SCpnt->request_bufflen = SCpnt->bufflen =
+ this_count * sdp->sector_size;
+
+ /*
+ * We shouldn't disconnect in the middle of a sector, so with a dumb
+ * host adapter, it's safe to assume that we can at least transfer
+ * this many bytes between each connect / disconnect.
+ */
+ SCpnt->transfersize = sdp->sector_size;
+ SCpnt->underflow = this_count << 9;
+ SCpnt->allowed = SD_MAX_RETRIES;
+
+queue:
+ SCpnt->timeout_per_command = timeout;
+
+ /*
+ * This is the completion routine we use. This is matched in terms
+ * of capability to this function.
+ */
+ SCpnt->done = sd_rw_intr;
+
+ /*
+ * This indicates that the command is ready from our end to be
+ * queued.
+ */
+ return 1;
+}
+
+/**
+ * sd_open - open a scsi disk device
+ * @inode: only i_rdev member may be used
+ * @filp: only f_mode and f_flags may be used
+ *
+ * Returns 0 if successful. Returns a negated errno value in case
+ * of error.
+ *
+ * Note: This can be called from a user context (e.g. fsck(1) )
+ * or from within the kernel (e.g. as a result of a mount(1) ).
+ * In the latter case @inode and @filp carry an abridged amount
+ * of information as noted above.
+ **/
+static int sd_open(struct inode *inode, struct file *filp)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct scsi_disk *sdkp;
+ struct scsi_device *sdev;
+ int retval;
+
+ if (!(sdkp = scsi_disk_get(disk)))
+ return -ENXIO;
+
+
+ SCSI_LOG_HLQUEUE(3, printk("sd_open: disk=%s\n", disk->disk_name));
+
+ sdev = sdkp->device;
+
+ /*
+ * If the device is in error recovery, wait until it is done.
+ * If the device is offline, then disallow any access to it.
+ */
+ retval = -ENXIO;
+ if (!scsi_block_when_processing_errors(sdev))
+ goto error_out;
+
+ if (sdev->removable || sdkp->write_prot)
+ check_disk_change(inode->i_bdev);
+
+ /*
+ * If the drive is empty, just let the open fail.
+ */
+ retval = -ENOMEDIUM;
+ if (sdev->removable && !sdkp->media_present &&
+ !(filp->f_flags & O_NDELAY))
+ goto error_out;
+
+ /*
+ * If the device has the write protect tab set, have the open fail
+ * if the user expects to be able to write to the thing.
+ */
+ retval = -EROFS;
+ if (sdkp->write_prot && (filp->f_mode & FMODE_WRITE))
+ goto error_out;
+
+ /*
+ * It is possible that the disk changing stuff resulted in
+ * the device being taken offline. If this is the case,
+ * report this to the user, and don't pretend that the
+ * open actually succeeded.
+ */
+ retval = -ENXIO;
+ if (!scsi_device_online(sdev))
+ goto error_out;
+
+ if (!sdkp->openers++ && sdev->removable) {
+ if (scsi_block_when_processing_errors(sdev))
+ scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
+ }
+
+ return 0;
+
+error_out:
+ scsi_disk_put(sdkp);
+ return retval;
+}
+
+/**
+ * sd_release - invoked when the (last) close(2) is called on this
+ * scsi disk.
+ * @inode: only i_rdev member may be used
+ * @filp: only f_mode and f_flags may be used
+ *
+ * Returns 0.
+ *
+ * Note: may block (uninterruptible) if error recovery is underway
+ * on this disk.
+ **/
+static int sd_release(struct inode *inode, struct file *filp)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct scsi_disk *sdkp = scsi_disk(disk);
+ struct scsi_device *sdev = sdkp->device;
+
+ SCSI_LOG_HLQUEUE(3, printk("sd_release: disk=%s\n", disk->disk_name));
+
+ if (!--sdkp->openers && sdev->removable) {
+ if (scsi_block_when_processing_errors(sdev))
+ scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
+ }
+
+ /*
+ * XXX and what if there are packets in flight and this close()
+ * XXX is followed by a "rmmod sd_mod"?
+ */
+ scsi_disk_put(sdkp);
+ return 0;
+}
+
+static int sd_hdio_getgeo(struct block_device *bdev, struct hd_geometry __user *loc)
+{
+ struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
+ struct scsi_device *sdp = sdkp->device;
+ struct Scsi_Host *host = sdp->host;
+ int diskinfo[4];
+
+ /* default to most commonly used values */
+ diskinfo[0] = 0x40; /* 1 << 6 */
+ diskinfo[1] = 0x20; /* 1 << 5 */
+ diskinfo[2] = sdkp->capacity >> 11;
+
+ /* override with calculated, extended default, or driver values */
+ if (host->hostt->bios_param)
+ host->hostt->bios_param(sdp, bdev, sdkp->capacity, diskinfo);
+ else
+ scsicam_bios_param(bdev, sdkp->capacity, diskinfo);
+
+ if (put_user(diskinfo[0], &loc->heads))
+ return -EFAULT;
+ if (put_user(diskinfo[1], &loc->sectors))
+ return -EFAULT;
+ if (put_user(diskinfo[2], &loc->cylinders))
+ return -EFAULT;
+ if (put_user((unsigned)get_start_sect(bdev),
+ (unsigned long __user *)&loc->start))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * sd_ioctl - process an ioctl
+ * @inode: only i_rdev/i_bdev members may be used
+ * @filp: only f_mode and f_flags may be used
+ * @cmd: ioctl command number
+ * @arg: this is third argument given to ioctl(2) system call.
+ * Often contains a pointer.
+ *
+ * Returns 0 if successful (some ioctls return postive numbers on
+ * success as well). Returns a negated errno value in case of error.
+ *
+ * Note: most ioctls are forward onto the block subsystem or further
+ * down in the scsi subsytem.
+ **/
+static int sd_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct block_device *bdev = inode->i_bdev;
+ struct gendisk *disk = bdev->bd_disk;
+ struct scsi_device *sdp = scsi_disk(disk)->device;
+ void __user *p = (void __user *)arg;
+ int error;
+
+ SCSI_LOG_IOCTL(1, printk("sd_ioctl: disk=%s, cmd=0x%x\n",
+ disk->disk_name, cmd));
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ error = scsi_nonblockable_ioctl(sdp, cmd, p, filp);
+ if (!scsi_block_when_processing_errors(sdp) || !error)
+ return error;
+
+ if (cmd == HDIO_GETGEO) {
+ if (!arg)
+ return -EINVAL;
+ return sd_hdio_getgeo(bdev, p);
+ }
+
+ /*
+ * Send SCSI addressing ioctls directly to mid level, send other
+ * ioctls to block level and then onto mid level if they can't be
+ * resolved.
+ */
+ switch (cmd) {
+ case SCSI_IOCTL_GET_IDLUN:
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ return scsi_ioctl(sdp, cmd, p);
+ default:
+ error = scsi_cmd_ioctl(filp, disk, cmd, p);
+ if (error != -ENOTTY)
+ return error;
+ }
+ return scsi_ioctl(sdp, cmd, p);
+}
+
+static void set_media_not_present(struct scsi_disk *sdkp)
+{
+ sdkp->media_present = 0;
+ sdkp->capacity = 0;
+ sdkp->device->changed = 1;
+}
+
+/**
+ * sd_media_changed - check if our medium changed
+ * @disk: kernel device descriptor
+ *
+ * Returns 0 if not applicable or no change; 1 if change
+ *
+ * Note: this function is invoked from the block subsystem.
+ **/
+static int sd_media_changed(struct gendisk *disk)
+{
+ struct scsi_disk *sdkp = scsi_disk(disk);
+ struct scsi_device *sdp = sdkp->device;
+ int retval;
+
+ SCSI_LOG_HLQUEUE(3, printk("sd_media_changed: disk=%s\n",
+ disk->disk_name));
+
+ if (!sdp->removable)
+ return 0;
+
+ /*
+ * If the device is offline, don't send any commands - just pretend as
+ * if the command failed. If the device ever comes back online, we
+ * can deal with it then. It is only because of unrecoverable errors
+ * that we would ever take a device offline in the first place.
+ */
+ if (!scsi_device_online(sdp))
+ goto not_present;
+
+ /*
+ * Using TEST_UNIT_READY enables differentiation between drive with
+ * no cartridge loaded - NOT READY, drive with changed cartridge -
+ * UNIT ATTENTION, or with same cartridge - GOOD STATUS.
+ *
+ * Drives that auto spin down. eg iomega jaz 1G, will be started
+ * by sd_spinup_disk() from sd_revalidate_disk(), which happens whenever
+ * sd_revalidate() is called.
+ */
+ retval = -ENODEV;
+ if (scsi_block_when_processing_errors(sdp))
+ retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES);
+
+ /*
+ * Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again.
+ */
+ if (retval)
+ goto not_present;
+
+ /*
+ * For removable scsi disk we have to recognise the presence
+ * of a disk in the drive. This is kept in the struct scsi_disk
+ * struct and tested at open ! Daniel Roche (dan@lectra.fr)
+ */
+ sdkp->media_present = 1;
+
+ retval = sdp->changed;
+ sdp->changed = 0;
+
+ return retval;
+
+not_present:
+ set_media_not_present(sdkp);
+ return 1;
+}
+
+static int sd_sync_cache(struct scsi_device *sdp)
+{
+ struct scsi_request *sreq;
+ int retries, res;
+
+ if (!scsi_device_online(sdp))
+ return -ENODEV;
+
+ sreq = scsi_allocate_request(sdp, GFP_KERNEL);
+ if (!sreq) {
+ printk("FAILED\n No memory for request\n");
+ return -ENOMEM;
+ }
+
+ sreq->sr_data_direction = DMA_NONE;
+ for (retries = 3; retries > 0; --retries) {
+ unsigned char cmd[10] = { 0 };
+
+ cmd[0] = SYNCHRONIZE_CACHE;
+ /*
+ * Leave the rest of the command zero to indicate
+ * flush everything.
+ */
+ scsi_wait_req(sreq, cmd, NULL, 0, SD_TIMEOUT, SD_MAX_RETRIES);
+ if (sreq->sr_result == 0)
+ break;
+ }
+
+ res = sreq->sr_result;
+ if (res) {
+ printk(KERN_WARNING "FAILED\n status = %x, message = %02x, "
+ "host = %d, driver = %02x\n ",
+ status_byte(res), msg_byte(res),
+ host_byte(res), driver_byte(res));
+ if (driver_byte(res) & DRIVER_SENSE)
+ scsi_print_req_sense("sd", sreq);
+ }
+
+ scsi_release_request(sreq);
+ return res;
+}
+
+static int sd_issue_flush(struct device *dev, sector_t *error_sector)
+{
+ struct scsi_device *sdp = to_scsi_device(dev);
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+
+ if (!sdkp)
+ return -ENODEV;
+
+ if (!sdkp->WCE)
+ return 0;
+
+ return sd_sync_cache(sdp);
+}
+
+static void sd_end_flush(request_queue_t *q, struct request *flush_rq)
+{
+ struct request *rq = flush_rq->end_io_data;
+ struct scsi_cmnd *cmd = rq->special;
+ unsigned int bytes = rq->hard_nr_sectors << 9;
+
+ if (!flush_rq->errors) {
+ spin_unlock(q->queue_lock);
+ scsi_io_completion(cmd, bytes, 0);
+ spin_lock(q->queue_lock);
+ } else if (blk_barrier_postflush(rq)) {
+ spin_unlock(q->queue_lock);
+ scsi_io_completion(cmd, 0, bytes);
+ spin_lock(q->queue_lock);
+ } else {
+ /*
+ * force journal abort of barriers
+ */
+ end_that_request_first(rq, -EOPNOTSUPP, rq->hard_nr_sectors);
+ end_that_request_last(rq);
+ }
+}
+
+static int sd_prepare_flush(request_queue_t *q, struct request *rq)
+{
+ struct scsi_device *sdev = q->queuedata;
+ struct scsi_disk *sdkp = dev_get_drvdata(&sdev->sdev_gendev);
+
+ if (sdkp->WCE) {
+ memset(rq->cmd, 0, sizeof(rq->cmd));
+ rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER;
+ rq->timeout = SD_TIMEOUT;
+ rq->cmd[0] = SYNCHRONIZE_CACHE;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void sd_rescan(struct device *dev)
+{
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+ sd_revalidate_disk(sdkp->disk);
+}
+
+
+#ifdef CONFIG_COMPAT
+/*
+ * This gets directly called from VFS. When the ioctl
+ * is not recognized we go back to the other translation paths.
+ */
+static long sd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct block_device *bdev = file->f_dentry->d_inode->i_bdev;
+ struct gendisk *disk = bdev->bd_disk;
+ struct scsi_device *sdev = scsi_disk(disk)->device;
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if (!scsi_block_when_processing_errors(sdev))
+ return -ENODEV;
+
+ if (sdev->host->hostt->compat_ioctl) {
+ int ret;
+
+ ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg);
+
+ return ret;
+ }
+
+ /*
+ * Let the static ioctl translation table take care of it.
+ */
+ return -ENOIOCTLCMD;
+}
+#endif
+
+static struct block_device_operations sd_fops = {
+ .owner = THIS_MODULE,
+ .open = sd_open,
+ .release = sd_release,
+ .ioctl = sd_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sd_compat_ioctl,
+#endif
+ .media_changed = sd_media_changed,
+ .revalidate_disk = sd_revalidate_disk,
+};
+
+/**
+ * sd_rw_intr - bottom half handler: called when the lower level
+ * driver has completed (successfully or otherwise) a scsi command.
+ * @SCpnt: mid-level's per command structure.
+ *
+ * Note: potentially run from within an ISR. Must not block.
+ **/
+static void sd_rw_intr(struct scsi_cmnd * SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->bufflen;
+ int good_bytes = (result == 0 ? this_count : 0);
+ sector_t block_sectors = 1;
+ u64 first_err_block;
+ sector_t error_sector;
+ struct scsi_sense_hdr sshdr;
+ int sense_valid = 0;
+ int sense_deferred = 0;
+ int info_valid;
+
+ if (result) {
+ sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr);
+ if (sense_valid)
+ sense_deferred = scsi_sense_is_deferred(&sshdr);
+ }
+
+#ifdef CONFIG_SCSI_LOGGING
+ SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: %s: res=0x%x\n",
+ SCpnt->request->rq_disk->disk_name, result));
+ if (sense_valid) {
+ SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: sb[respc,sk,asc,"
+ "ascq]=%x,%x,%x,%x\n", sshdr.response_code,
+ sshdr.sense_key, sshdr.asc, sshdr.ascq));
+ }
+#endif
+ /*
+ Handle MEDIUM ERRORs that indicate partial success. Since this is a
+ relatively rare error condition, no care is taken to avoid
+ unnecessary additional work such as memcpy's that could be avoided.
+ */
+
+ /*
+ * If SG_IO from block layer then set good_bytes to stop retries;
+ * else if errors, check them, and if necessary prepare for
+ * (partial) retries.
+ */
+ if (blk_pc_request(SCpnt->request))
+ good_bytes = this_count;
+ else if (driver_byte(result) != 0 &&
+ sense_valid && !sense_deferred) {
+ switch (sshdr.sense_key) {
+ case MEDIUM_ERROR:
+ if (!blk_fs_request(SCpnt->request))
+ break;
+ info_valid = scsi_get_sense_info_fld(
+ SCpnt->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ &first_err_block);
+ /*
+ * May want to warn and skip if following cast results
+ * in actual truncation (if sector_t < 64 bits)
+ */
+ error_sector = (sector_t)first_err_block;
+ if (SCpnt->request->bio != NULL)
+ block_sectors = bio_sectors(SCpnt->request->bio);
+ switch (SCpnt->device->sector_size) {
+ case 1024:
+ error_sector <<= 1;
+ if (block_sectors < 2)
+ block_sectors = 2;
+ break;
+ case 2048:
+ error_sector <<= 2;
+ if (block_sectors < 4)
+ block_sectors = 4;
+ break;
+ case 4096:
+ error_sector <<=3;
+ if (block_sectors < 8)
+ block_sectors = 8;
+ break;
+ case 256:
+ error_sector >>= 1;
+ break;
+ default:
+ break;
+ }
+
+ error_sector &= ~(block_sectors - 1);
+ good_bytes = (error_sector - SCpnt->request->sector) << 9;
+ if (good_bytes < 0 || good_bytes >= this_count)
+ good_bytes = 0;
+ break;
+
+ case RECOVERED_ERROR: /* an error occurred, but it recovered */
+ case NO_SENSE: /* LLDD got sense data */
+ /*
+ * Inform the user, but make sure that it's not treated
+ * as a hard error.
+ */
+ scsi_print_sense("sd", SCpnt);
+ SCpnt->result = 0;
+ memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ good_bytes = this_count;
+ break;
+
+ case ILLEGAL_REQUEST:
+ if (SCpnt->device->use_10_for_rw &&
+ (SCpnt->cmnd[0] == READ_10 ||
+ SCpnt->cmnd[0] == WRITE_10))
+ SCpnt->device->use_10_for_rw = 0;
+ if (SCpnt->device->use_10_for_ms &&
+ (SCpnt->cmnd[0] == MODE_SENSE_10 ||
+ SCpnt->cmnd[0] == MODE_SELECT_10))
+ SCpnt->device->use_10_for_ms = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+ /*
+ * This calls the generic completion function, now that we know
+ * how many actual sectors finished, and how many sectors we need
+ * to say have failed.
+ */
+ scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
+}
+
+static int media_not_present(struct scsi_disk *sdkp, struct scsi_request *srp)
+{
+ struct scsi_sense_hdr sshdr;
+
+ if (!srp->sr_result)
+ return 0;
+ if (!(driver_byte(srp->sr_result) & DRIVER_SENSE))
+ return 0;
+ /* not invoked for commands that could return deferred errors */
+ if (scsi_request_normalize_sense(srp, &sshdr)) {
+ if (sshdr.sense_key != NOT_READY &&
+ sshdr.sense_key != UNIT_ATTENTION)
+ return 0;
+ if (sshdr.asc != 0x3A) /* medium not present */
+ return 0;
+ }
+ set_media_not_present(sdkp);
+ return 1;
+}
+
+/*
+ * spinup disk - called only in sd_revalidate_disk()
+ */
+static void
+sd_spinup_disk(struct scsi_disk *sdkp, char *diskname,
+ struct scsi_request *SRpnt, unsigned char *buffer) {
+ unsigned char cmd[10];
+ unsigned long spintime_value = 0;
+ int retries, spintime;
+ unsigned int the_result;
+ struct scsi_sense_hdr sshdr;
+ int sense_valid = 0;
+
+ spintime = 0;
+
+ /* Spin up drives, as required. Only do this at boot time */
+ /* Spinup needs to be done for module loads too. */
+ do {
+ retries = 0;
+
+ do {
+ cmd[0] = TEST_UNIT_READY;
+ memset((void *) &cmd[1], 0, 9);
+
+ SRpnt->sr_cmd_len = 0;
+ memset(SRpnt->sr_sense_buffer, 0,
+ SCSI_SENSE_BUFFERSIZE);
+ SRpnt->sr_data_direction = DMA_NONE;
+
+ scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer,
+ 0/*512*/, SD_TIMEOUT, SD_MAX_RETRIES);
+
+ the_result = SRpnt->sr_result;
+ if (the_result)
+ sense_valid = scsi_request_normalize_sense(
+ SRpnt, &sshdr);
+ retries++;
+ } while (retries < 3 &&
+ (!scsi_status_is_good(the_result) ||
+ ((driver_byte(the_result) & DRIVER_SENSE) &&
+ sense_valid && sshdr.sense_key == UNIT_ATTENTION)));
+
+ /*
+ * If the drive has indicated to us that it doesn't have
+ * any media in it, don't bother with any of the rest of
+ * this crap.
+ */
+ if (media_not_present(sdkp, SRpnt))
+ return;
+
+ if ((driver_byte(the_result) & DRIVER_SENSE) == 0) {
+ /* no sense, TUR either succeeded or failed
+ * with a status error */
+ if(!spintime && !scsi_status_is_good(the_result))
+ printk(KERN_NOTICE "%s: Unit Not Ready, "
+ "error = 0x%x\n", diskname, the_result);
+ break;
+ }
+
+ /*
+ * The device does not want the automatic start to be issued.
+ */
+ if (sdkp->device->no_start_on_add) {
+ break;
+ }
+
+ /*
+ * If manual intervention is required, or this is an
+ * absent USB storage device, a spinup is meaningless.
+ */
+ if (sense_valid &&
+ sshdr.sense_key == NOT_READY &&
+ sshdr.asc == 4 && sshdr.ascq == 3) {
+ break; /* manual intervention required */
+
+ /*
+ * Issue command to spin up drive when not ready
+ */
+ } else if (sense_valid && sshdr.sense_key == NOT_READY) {
+ if (!spintime) {
+ printk(KERN_NOTICE "%s: Spinning up disk...",
+ diskname);
+ cmd[0] = START_STOP;
+ cmd[1] = 1; /* Return immediately */
+ memset((void *) &cmd[2], 0, 8);
+ cmd[4] = 1; /* Start spin cycle */
+ SRpnt->sr_cmd_len = 0;
+ memset(SRpnt->sr_sense_buffer, 0,
+ SCSI_SENSE_BUFFERSIZE);
+
+ SRpnt->sr_data_direction = DMA_NONE;
+ scsi_wait_req(SRpnt, (void *)cmd,
+ (void *) buffer, 0/*512*/,
+ SD_TIMEOUT, SD_MAX_RETRIES);
+ spintime_value = jiffies;
+ }
+ spintime = 1;
+ /* Wait 1 second for next try */
+ msleep(1000);
+ printk(".");
+ } else {
+ /* we don't understand the sense code, so it's
+ * probably pointless to loop */
+ if(!spintime) {
+ printk(KERN_NOTICE "%s: Unit Not Ready, "
+ "sense:\n", diskname);
+ scsi_print_req_sense("", SRpnt);
+ }
+ break;
+ }
+
+ } while (spintime &&
+ time_after(spintime_value + 100 * HZ, jiffies));
+
+ if (spintime) {
+ if (scsi_status_is_good(the_result))
+ printk("ready\n");
+ else
+ printk("not responding...\n");
+ }
+}
+
+/*
+ * read disk capacity
+ */
+static void
+sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
+ struct scsi_request *SRpnt, unsigned char *buffer) {
+ unsigned char cmd[16];
+ struct scsi_device *sdp = sdkp->device;
+ int the_result, retries;
+ int sector_size = 0;
+ int longrc = 0;
+ struct scsi_sense_hdr sshdr;
+ int sense_valid = 0;
+
+repeat:
+ retries = 3;
+ do {
+ if (longrc) {
+ memset((void *) cmd, 0, 16);
+ cmd[0] = SERVICE_ACTION_IN;
+ cmd[1] = SAI_READ_CAPACITY_16;
+ cmd[13] = 12;
+ memset((void *) buffer, 0, 12);
+ } else {
+ cmd[0] = READ_CAPACITY;
+ memset((void *) &cmd[1], 0, 9);
+ memset((void *) buffer, 0, 8);
+ }
+
+ SRpnt->sr_cmd_len = 0;
+ memset(SRpnt->sr_sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ SRpnt->sr_data_direction = DMA_FROM_DEVICE;
+
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
+ longrc ? 12 : 8, SD_TIMEOUT, SD_MAX_RETRIES);
+
+ if (media_not_present(sdkp, SRpnt))
+ return;
+
+ the_result = SRpnt->sr_result;
+ if (the_result)
+ sense_valid = scsi_request_normalize_sense(SRpnt,
+ &sshdr);
+ retries--;
+
+ } while (the_result && retries);
+
+ if (the_result && !longrc) {
+ printk(KERN_NOTICE "%s : READ CAPACITY failed.\n"
+ "%s : status=%x, message=%02x, host=%d, driver=%02x \n",
+ diskname, diskname,
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result));
+
+ if (driver_byte(the_result) & DRIVER_SENSE)
+ scsi_print_req_sense("sd", SRpnt);
+ else
+ printk("%s : sense not available. \n", diskname);
+
+ /* Set dirty bit for removable devices if not ready -
+ * sometimes drives will not report this properly. */
+ if (sdp->removable &&
+ sense_valid && sshdr.sense_key == NOT_READY)
+ sdp->changed = 1;
+
+ /* Either no media are present but the drive didn't tell us,
+ or they are present but the read capacity command fails */
+ /* sdkp->media_present = 0; -- not always correct */
+ sdkp->capacity = 0x200000; /* 1 GB - random */
+
+ return;
+ } else if (the_result && longrc) {
+ /* READ CAPACITY(16) has been failed */
+ printk(KERN_NOTICE "%s : READ CAPACITY(16) failed.\n"
+ "%s : status=%x, message=%02x, host=%d, driver=%02x \n",
+ diskname, diskname,
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result));
+ printk(KERN_NOTICE "%s : use 0xffffffff as device size\n",
+ diskname);
+
+ sdkp->capacity = 1 + (sector_t) 0xffffffff;
+ goto got_data;
+ }
+
+ if (!longrc) {
+ sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+ if (buffer[0] == 0xff && buffer[1] == 0xff &&
+ buffer[2] == 0xff && buffer[3] == 0xff) {
+ if(sizeof(sdkp->capacity) > 4) {
+ printk(KERN_NOTICE "%s : very big device. try to use"
+ " READ CAPACITY(16).\n", diskname);
+ longrc = 1;
+ goto repeat;
+ }
+ printk(KERN_ERR "%s: too big for this kernel. Use a "
+ "kernel compiled with support for large block "
+ "devices.\n", diskname);
+ sdkp->capacity = 0;
+ goto got_data;
+ }
+ sdkp->capacity = 1 + (((sector_t)buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3]);
+ } else {
+ sdkp->capacity = 1 + (((u64)buffer[0] << 56) |
+ ((u64)buffer[1] << 48) |
+ ((u64)buffer[2] << 40) |
+ ((u64)buffer[3] << 32) |
+ ((sector_t)buffer[4] << 24) |
+ ((sector_t)buffer[5] << 16) |
+ ((sector_t)buffer[6] << 8) |
+ (sector_t)buffer[7]);
+
+ sector_size = (buffer[8] << 24) |
+ (buffer[9] << 16) | (buffer[10] << 8) | buffer[11];
+ }
+
+ /* Some devices return the total number of sectors, not the
+ * highest sector number. Make the necessary adjustment. */
+ if (sdp->fix_capacity)
+ --sdkp->capacity;
+
+got_data:
+ if (sector_size == 0) {
+ sector_size = 512;
+ printk(KERN_NOTICE "%s : sector size 0 reported, "
+ "assuming 512.\n", diskname);
+ }
+
+ if (sector_size != 512 &&
+ sector_size != 1024 &&
+ sector_size != 2048 &&
+ sector_size != 4096 &&
+ sector_size != 256) {
+ printk(KERN_NOTICE "%s : unsupported sector size "
+ "%d.\n", diskname, sector_size);
+ /*
+ * The user might want to re-format the drive with
+ * a supported sectorsize. Once this happens, it
+ * would be relatively trivial to set the thing up.
+ * For this reason, we leave the thing in the table.
+ */
+ sdkp->capacity = 0;
+ /*
+ * set a bogus sector size so the normal read/write
+ * logic in the block layer will eventually refuse any
+ * request on this device without tripping over power
+ * of two sector size assumptions
+ */
+ sector_size = 512;
+ }
+ {
+ /*
+ * The msdos fs needs to know the hardware sector size
+ * So I have created this table. See ll_rw_blk.c
+ * Jacques Gelinas (Jacques@solucorp.qc.ca)
+ */
+ int hard_sector = sector_size;
+ sector_t sz = sdkp->capacity * (hard_sector/256);
+ request_queue_t *queue = sdp->request_queue;
+ sector_t mb;
+
+ blk_queue_hardsect_size(queue, hard_sector);
+ /* avoid 64-bit division on 32-bit platforms */
+ mb = sz >> 1;
+ sector_div(sz, 1250);
+ mb -= sz - 974;
+ sector_div(mb, 1950);
+
+ printk(KERN_NOTICE "SCSI device %s: "
+ "%llu %d-byte hdwr sectors (%llu MB)\n",
+ diskname, (unsigned long long)sdkp->capacity,
+ hard_sector, (unsigned long long)mb);
+ }
+
+ /* Rescale capacity to 512-byte units */
+ if (sector_size == 4096)
+ sdkp->capacity <<= 3;
+ else if (sector_size == 2048)
+ sdkp->capacity <<= 2;
+ else if (sector_size == 1024)
+ sdkp->capacity <<= 1;
+ else if (sector_size == 256)
+ sdkp->capacity >>= 1;
+
+ sdkp->device->sector_size = sector_size;
+}
+
+/* called with buffer of length 512 */
+static inline int
+sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage,
+ unsigned char *buffer, int len, struct scsi_mode_data *data)
+{
+ return __scsi_mode_sense(SRpnt, dbd, modepage, buffer, len,
+ SD_TIMEOUT, SD_MAX_RETRIES, data);
+}
+
+/*
+ * read write protect setting, if possible - called only in sd_revalidate_disk()
+ * called with buffer of length 512
+ */
+static void
+sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname,
+ struct scsi_request *SRpnt, unsigned char *buffer) {
+ int res;
+ struct scsi_mode_data data;
+
+ set_disk_ro(sdkp->disk, 0);
+ if (sdkp->device->skip_ms_page_3f) {
+ printk(KERN_NOTICE "%s: assuming Write Enabled\n", diskname);
+ return;
+ }
+
+ if (sdkp->device->use_192_bytes_for_3f) {
+ res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 192, &data);
+ } else {
+ /*
+ * First attempt: ask for all pages (0x3F), but only 4 bytes.
+ * We have to start carefully: some devices hang if we ask
+ * for more than is available.
+ */
+ res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4, &data);
+
+ /*
+ * Second attempt: ask for page 0 When only page 0 is
+ * implemented, a request for page 3F may return Sense Key
+ * 5: Illegal Request, Sense Code 24: Invalid field in
+ * CDB.
+ */
+ if (!scsi_status_is_good(res))
+ res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4, &data);
+
+ /*
+ * Third attempt: ask 255 bytes, as we did earlier.
+ */
+ if (!scsi_status_is_good(res))
+ res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255,
+ &data);
+ }
+
+ if (!scsi_status_is_good(res)) {
+ printk(KERN_WARNING
+ "%s: test WP failed, assume Write Enabled\n", diskname);
+ } else {
+ sdkp->write_prot = ((data.device_specific & 0x80) != 0);
+ set_disk_ro(sdkp->disk, sdkp->write_prot);
+ printk(KERN_NOTICE "%s: Write Protect is %s\n", diskname,
+ sdkp->write_prot ? "on" : "off");
+ printk(KERN_DEBUG "%s: Mode Sense: %02x %02x %02x %02x\n",
+ diskname, buffer[0], buffer[1], buffer[2], buffer[3]);
+ }
+}
+
+/*
+ * sd_read_cache_type - called only from sd_revalidate_disk()
+ * called with buffer of length 512
+ */
+static void
+sd_read_cache_type(struct scsi_disk *sdkp, char *diskname,
+ struct scsi_request *SRpnt, unsigned char *buffer) {
+ int len = 0, res;
+
+ const int dbd = 0; /* DBD */
+ const int modepage = 0x08; /* current values, cache page */
+ struct scsi_mode_data data;
+ struct scsi_sense_hdr sshdr;
+
+ if (sdkp->device->skip_ms_page_8)
+ goto defaults;
+
+ /* cautiously ask */
+ res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, 4, &data);
+
+ if (!scsi_status_is_good(res))
+ goto bad_sense;
+
+ /* that went OK, now ask for the proper length */
+ len = data.length;
+
+ /*
+ * We're only interested in the first three bytes, actually.
+ * But the data cache page is defined for the first 20.
+ */
+ if (len < 3)
+ goto bad_sense;
+ if (len > 20)
+ len = 20;
+
+ /* Take headers and block descriptors into account */
+ len += data.header_length + data.block_descriptor_length;
+
+ /* Get the data */
+ res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, len, &data);
+
+ if (scsi_status_is_good(res)) {
+ const char *types[] = {
+ "write through", "none", "write back",
+ "write back, no read (daft)"
+ };
+ int ct = 0;
+ int offset = data.header_length +
+ data.block_descriptor_length + 2;
+
+ sdkp->WCE = ((buffer[offset] & 0x04) != 0);
+ sdkp->RCD = ((buffer[offset] & 0x01) != 0);
+
+ ct = sdkp->RCD + 2*sdkp->WCE;
+
+ printk(KERN_NOTICE "SCSI device %s: drive cache: %s\n",
+ diskname, types[ct]);
+
+ return;
+ }
+
+bad_sense:
+ if (scsi_request_normalize_sense(SRpnt, &sshdr) &&
+ sshdr.sense_key == ILLEGAL_REQUEST &&
+ sshdr.asc == 0x24 && sshdr.ascq == 0x0)
+ printk(KERN_NOTICE "%s: cache data unavailable\n",
+ diskname); /* Invalid field in CDB */
+ else
+ printk(KERN_ERR "%s: asking for cache data failed\n",
+ diskname);
+
+defaults:
+ printk(KERN_ERR "%s: assuming drive cache: write through\n",
+ diskname);
+ sdkp->WCE = 0;
+ sdkp->RCD = 0;
+}
+
+/**
+ * sd_revalidate_disk - called the first time a new disk is seen,
+ * performs disk spin up, read_capacity, etc.
+ * @disk: struct gendisk we care about
+ **/
+static int sd_revalidate_disk(struct gendisk *disk)
+{
+ struct scsi_disk *sdkp = scsi_disk(disk);
+ struct scsi_device *sdp = sdkp->device;
+ struct scsi_request *sreq;
+ unsigned char *buffer;
+
+ SCSI_LOG_HLQUEUE(3, printk("sd_revalidate_disk: disk=%s\n", disk->disk_name));
+
+ /*
+ * If the device is offline, don't try and read capacity or any
+ * of the other niceties.
+ */
+ if (!scsi_device_online(sdp))
+ goto out;
+
+ sreq = scsi_allocate_request(sdp, GFP_KERNEL);
+ if (!sreq) {
+ printk(KERN_WARNING "(sd_revalidate_disk:) Request allocation "
+ "failure.\n");
+ goto out;
+ }
+
+ buffer = kmalloc(512, GFP_KERNEL | __GFP_DMA);
+ if (!buffer) {
+ printk(KERN_WARNING "(sd_revalidate_disk:) Memory allocation "
+ "failure.\n");
+ goto out_release_request;
+ }
+
+ /* defaults, until the device tells us otherwise */
+ sdp->sector_size = 512;
+ sdkp->capacity = 0;
+ sdkp->media_present = 1;
+ sdkp->write_prot = 0;
+ sdkp->WCE = 0;
+ sdkp->RCD = 0;
+
+ sd_spinup_disk(sdkp, disk->disk_name, sreq, buffer);
+
+ /*
+ * Without media there is no reason to ask; moreover, some devices
+ * react badly if we do.
+ */
+ if (sdkp->media_present) {
+ sd_read_capacity(sdkp, disk->disk_name, sreq, buffer);
+ if (sdp->removable)
+ sd_read_write_protect_flag(sdkp, disk->disk_name,
+ sreq, buffer);
+ sd_read_cache_type(sdkp, disk->disk_name, sreq, buffer);
+ }
+
+ set_capacity(disk, sdkp->capacity);
+ kfree(buffer);
+
+ out_release_request:
+ scsi_release_request(sreq);
+ out:
+ return 0;
+}
+
+/**
+ * sd_probe - called during driver initialization and whenever a
+ * new scsi device is attached to the system. It is called once
+ * for each scsi device (not just disks) present.
+ * @dev: pointer to device object
+ *
+ * Returns 0 if successful (or not interested in this scsi device
+ * (e.g. scanner)); 1 when there is an error.
+ *
+ * Note: this function is invoked from the scsi mid-level.
+ * This function sets up the mapping between a given
+ * <host,channel,id,lun> (found in sdp) and new device name
+ * (e.g. /dev/sda). More precisely it is the block device major
+ * and minor number that is chosen here.
+ *
+ * Assume sd_attach is not re-entrant (for time being)
+ * Also think about sd_attach() and sd_remove() running coincidentally.
+ **/
+static int sd_probe(struct device *dev)
+{
+ struct scsi_device *sdp = to_scsi_device(dev);
+ struct scsi_disk *sdkp;
+ struct gendisk *gd;
+ u32 index;
+ int error;
+
+ error = -ENODEV;
+ if ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD))
+ goto out;
+
+ SCSI_LOG_HLQUEUE(3, printk("sd_attach: scsi device: <%d,%d,%d,%d>\n",
+ sdp->host->host_no, sdp->channel, sdp->id, sdp->lun));
+
+ error = -ENOMEM;
+ sdkp = kmalloc(sizeof(*sdkp), GFP_KERNEL);
+ if (!sdkp)
+ goto out;
+
+ memset (sdkp, 0, sizeof(*sdkp));
+ kref_init(&sdkp->kref);
+
+ gd = alloc_disk(16);
+ if (!gd)
+ goto out_free;
+
+ if (!idr_pre_get(&sd_index_idr, GFP_KERNEL))
+ goto out_put;
+
+ spin_lock(&sd_index_lock);
+ error = idr_get_new(&sd_index_idr, NULL, &index);
+ spin_unlock(&sd_index_lock);
+
+ if (index >= SD_MAX_DISKS)
+ error = -EBUSY;
+ if (error)
+ goto out_put;
+
+ sdkp->device = sdp;
+ sdkp->driver = &sd_template;
+ sdkp->disk = gd;
+ sdkp->index = index;
+ sdkp->openers = 0;
+
+ if (!sdp->timeout) {
+ if (sdp->type == TYPE_DISK)
+ sdp->timeout = SD_TIMEOUT;
+ else
+ sdp->timeout = SD_MOD_TIMEOUT;
+ }
+
+ gd->major = sd_major((index & 0xf0) >> 4);
+ gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
+ gd->minors = 16;
+ gd->fops = &sd_fops;
+
+ if (index < 26) {
+ sprintf(gd->disk_name, "sd%c", 'a' + index % 26);
+ } else if (index < (26 + 1) * 26) {
+ sprintf(gd->disk_name, "sd%c%c",
+ 'a' + index / 26 - 1,'a' + index % 26);
+ } else {
+ const unsigned int m1 = (index / 26 - 1) / 26 - 1;
+ const unsigned int m2 = (index / 26 - 1) % 26;
+ const unsigned int m3 = index % 26;
+ sprintf(gd->disk_name, "sd%c%c%c",
+ 'a' + m1, 'a' + m2, 'a' + m3);
+ }
+
+ strcpy(gd->devfs_name, sdp->devfs_name);
+
+ gd->private_data = &sdkp->driver;
+
+ sd_revalidate_disk(gd);
+
+ gd->driverfs_dev = &sdp->sdev_gendev;
+ gd->flags = GENHD_FL_DRIVERFS;
+ if (sdp->removable)
+ gd->flags |= GENHD_FL_REMOVABLE;
+ gd->queue = sdkp->device->request_queue;
+
+ dev_set_drvdata(dev, sdkp);
+ add_disk(gd);
+
+ printk(KERN_NOTICE "Attached scsi %sdisk %s at scsi%d, channel %d, "
+ "id %d, lun %d\n", sdp->removable ? "removable " : "",
+ gd->disk_name, sdp->host->host_no, sdp->channel,
+ sdp->id, sdp->lun);
+
+ return 0;
+
+out_put:
+ put_disk(gd);
+out_free:
+ kfree(sdkp);
+out:
+ return error;
+}
+
+/**
+ * sd_remove - called whenever a scsi disk (previously recognized by
+ * sd_probe) is detached from the system. It is called (potentially
+ * multiple times) during sd module unload.
+ * @sdp: pointer to mid level scsi device object
+ *
+ * Note: this function is invoked from the scsi mid-level.
+ * This function potentially frees up a device name (e.g. /dev/sdc)
+ * that could be re-used by a subsequent sd_probe().
+ * This function is not called when the built-in sd driver is "exit-ed".
+ **/
+static int sd_remove(struct device *dev)
+{
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+
+ del_gendisk(sdkp->disk);
+ sd_shutdown(dev);
+ down(&sd_ref_sem);
+ kref_put(&sdkp->kref, scsi_disk_release);
+ up(&sd_ref_sem);
+
+ return 0;
+}
+
+/**
+ * scsi_disk_release - Called to free the scsi_disk structure
+ * @kref: pointer to embedded kref
+ *
+ * sd_ref_sem must be held entering this routine. Because it is
+ * called on last put, you should always use the scsi_disk_get()
+ * scsi_disk_put() helpers which manipulate the semaphore directly
+ * and never do a direct kref_put().
+ **/
+static void scsi_disk_release(struct kref *kref)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(kref);
+ struct gendisk *disk = sdkp->disk;
+
+ spin_lock(&sd_index_lock);
+ idr_remove(&sd_index_idr, sdkp->index);
+ spin_unlock(&sd_index_lock);
+
+ disk->private_data = NULL;
+
+ put_disk(disk);
+
+ kfree(sdkp);
+}
+
+/*
+ * Send a SYNCHRONIZE CACHE instruction down to the device through
+ * the normal SCSI command structure. Wait for the command to
+ * complete.
+ */
+static void sd_shutdown(struct device *dev)
+{
+ struct scsi_device *sdp = to_scsi_device(dev);
+ struct scsi_disk *sdkp = dev_get_drvdata(dev);
+
+ if (!sdkp)
+ return; /* this can happen */
+
+ if (!sdkp->WCE)
+ return;
+
+ printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n",
+ sdkp->disk->disk_name);
+ sd_sync_cache(sdp);
+}
+
+/**
+ * init_sd - entry point for this driver (both when built in or when
+ * a module).
+ *
+ * Note: this function registers this driver with the scsi mid-level.
+ **/
+static int __init init_sd(void)
+{
+ int majors = 0, i;
+
+ SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
+
+ for (i = 0; i < SD_MAJORS; i++)
+ if (register_blkdev(sd_major(i), "sd") == 0)
+ majors++;
+
+ if (!majors)
+ return -ENODEV;
+
+ return scsi_register_driver(&sd_template.gendrv);
+}
+
+/**
+ * exit_sd - exit point for this driver (when it is a module).
+ *
+ * Note: this function unregisters this driver from the scsi mid-level.
+ **/
+static void __exit exit_sd(void)
+{
+ int i;
+
+ SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
+
+ scsi_unregister_driver(&sd_template.gendrv);
+ for (i = 0; i < SD_MAJORS; i++)
+ unregister_blkdev(sd_major(i), "sd");
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Youngdale");
+MODULE_DESCRIPTION("SCSI disk (sd) driver");
+
+module_init(init_sd);
+module_exit(exit_sd);
diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c
new file mode 100644
index 000000000000..b362ff2811da
--- /dev/null
+++ b/drivers/scsi/seagate.c
@@ -0,0 +1,1675 @@
+/*
+ * seagate.c Copyright (C) 1992, 1993 Drew Eckhardt
+ * low level scsi driver for ST01/ST02, Future Domain TMC-885,
+ * TMC-950 by Drew Eckhardt <drew@colorado.edu>
+ *
+ * Note : TMC-880 boards don't work because they have two bits in
+ * the status register flipped, I'll fix this "RSN"
+ * [why do I have strong feeling that above message is from 1993? :-)
+ * pavel@ucw.cz]
+ *
+ * This card does all the I/O via memory mapped I/O, so there is no need
+ * to check or allocate a region of the I/O address space.
+ */
+
+/* 1996 - to use new read{b,w,l}, write{b,w,l}, and phys_to_virt
+ * macros, replaced assembler routines with C. There's probably a
+ * performance hit, but I only have a cdrom and can't tell. Define
+ * SEAGATE_USE_ASM if you want the old assembler code -- SJT
+ *
+ * 1998-jul-29 - created DPRINTK macros and made it work under
+ * linux 2.1.112, simplified some #defines etc. <pavel@ucw.cz>
+ *
+ * Aug 2000 - aeb - deleted seagate_st0x_biosparam(). It would try to
+ * read the physical disk geometry, a bad mistake. Of course it doesn't
+ * matter much what geometry one invents, but on large disks it
+ * returned 256 (or more) heads, causing all kind of failures.
+ * Of course this means that people might see a different geometry now,
+ * so boot parameters may be necessary in some cases.
+ */
+
+/*
+ * Configuration :
+ * To use without BIOS -DOVERRIDE=base_address -DCONTROLLER=FD or SEAGATE
+ * -DIRQ will override the default of 5.
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ * st0x=ADDRESS,IRQ (for a Seagate controller)
+ * or:
+ * tmc8xx=ADDRESS,IRQ (for a TMC-8xx or TMC-950 controller)
+ * eg:
+ * tmc8xx=0xC8000,15
+ *
+ * will configure the driver for a TMC-8xx style controller using IRQ 15
+ * with a base address of 0xC8000.
+ *
+ * -DARBITRATE
+ * Will cause the host adapter to arbitrate for the
+ * bus for better SCSI-II compatibility, rather than just
+ * waiting for BUS FREE and then doing its thing. Should
+ * let us do one command per Lun when I integrate my
+ * reorganization changes into the distribution sources.
+ *
+ * -DDEBUG=65535
+ * Will activate debug code.
+ *
+ * -DFAST or -DFAST32
+ * Will use blind transfers where possible
+ *
+ * -DPARITY
+ * This will enable parity.
+ *
+ * -DSEAGATE_USE_ASM
+ * Will use older seagate assembly code. should be (very small amount)
+ * Faster.
+ *
+ * -DSLOW_RATE=50
+ * Will allow compatibility with broken devices that don't
+ * handshake fast enough (ie, some CD ROM's) for the Seagate
+ * code.
+ *
+ * 50 is some number, It will let you specify a default
+ * transfer rate if handshaking isn't working correctly.
+ *
+ * -DOLDCNTDATASCEME There is a new sceme to set the CONTROL
+ * and DATA reigsters which complies more closely
+ * with the SCSI2 standard. This hopefully eliminates
+ * the need to swap the order these registers are
+ * 'messed' with. It makes the following two options
+ * obsolete. To reenable the old sceme define this.
+ *
+ * The following to options are patches from the SCSI.HOWTO
+ *
+ * -DSWAPSTAT This will swap the definitions for STAT_MSG and STAT_CD.
+ *
+ * -DSWAPCNTDATA This will swap the order that seagate.c messes with
+ * the CONTROL an DATA registers.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "seagate.h"
+
+#include <scsi/scsi_ioctl.h>
+
+#ifdef DEBUG
+#define DPRINTK( when, msg... ) do { if ( (DEBUG & (when)) == (when) ) printk( msg ); } while (0)
+#else
+#define DPRINTK( when, msg... ) do { } while (0)
+#endif
+#define DANY( msg... ) DPRINTK( 0xffff, msg );
+
+#ifndef IRQ
+#define IRQ 5
+#endif
+
+#ifdef FAST32
+#define FAST
+#endif
+
+#undef LINKED /* Linked commands are currently broken! */
+
+#if defined(OVERRIDE) && !defined(CONTROLLER)
+#error Please use -DCONTROLLER=SEAGATE or -DCONTROLLER=FD to override controller type
+#endif
+
+#ifndef __i386__
+#undef SEAGATE_USE_ASM
+#endif
+
+/*
+ Thanks to Brian Antoine for the example code in his Messy-Loss ST-01
+ driver, and Mitsugu Suzuki for information on the ST-01
+ SCSI host.
+*/
+
+/*
+ CONTROL defines
+*/
+
+#define CMD_RST 0x01
+#define CMD_SEL 0x02
+#define CMD_BSY 0x04
+#define CMD_ATTN 0x08
+#define CMD_START_ARB 0x10
+#define CMD_EN_PARITY 0x20
+#define CMD_INTR 0x40
+#define CMD_DRVR_ENABLE 0x80
+
+/*
+ STATUS
+*/
+#ifdef SWAPSTAT
+#define STAT_MSG 0x08
+#define STAT_CD 0x02
+#else
+#define STAT_MSG 0x02
+#define STAT_CD 0x08
+#endif
+
+#define STAT_BSY 0x01
+#define STAT_IO 0x04
+#define STAT_REQ 0x10
+#define STAT_SEL 0x20
+#define STAT_PARITY 0x40
+#define STAT_ARB_CMPL 0x80
+
+/*
+ REQUESTS
+*/
+
+#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+extern volatile int seagate_st0x_timeout;
+
+#ifdef PARITY
+#define BASE_CMD CMD_EN_PARITY
+#else
+#define BASE_CMD 0
+#endif
+
+/*
+ Debugging code
+*/
+
+#define PHASE_BUS_FREE 1
+#define PHASE_ARBITRATION 2
+#define PHASE_SELECTION 4
+#define PHASE_DATAIN 8
+#define PHASE_DATAOUT 0x10
+#define PHASE_CMDOUT 0x20
+#define PHASE_MSGIN 0x40
+#define PHASE_MSGOUT 0x80
+#define PHASE_STATUSIN 0x100
+#define PHASE_ETC (PHASE_DATAIN | PHASE_DATAOUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN)
+#define PRINT_COMMAND 0x200
+#define PHASE_EXIT 0x400
+#define PHASE_RESELECT 0x800
+#define DEBUG_FAST 0x1000
+#define DEBUG_SG 0x2000
+#define DEBUG_LINKED 0x4000
+#define DEBUG_BORKEN 0x8000
+
+/*
+ * Control options - these are timeouts specified in .01 seconds.
+ */
+
+/* 30, 20 work */
+#define ST0X_BUS_FREE_DELAY 25
+#define ST0X_SELECTION_DELAY 25
+
+#define SEAGATE 1 /* these determine the type of the controller */
+#define FD 2
+
+#define ST0X_ID_STR "Seagate ST-01/ST-02"
+#define FD_ID_STR "TMC-8XX/TMC-950"
+
+static int internal_command (unsigned char target, unsigned char lun,
+ const void *cmnd,
+ void *buff, int bufflen, int reselect);
+
+static int incommand; /* set if arbitration has finished
+ and we are in some command phase. */
+
+static unsigned int base_address = 0; /* Where the card ROM starts, used to
+ calculate memory mapped register
+ location. */
+
+static void __iomem *st0x_cr_sr; /* control register write, status
+ register read. 256 bytes in
+ length.
+ Read is status of SCSI BUS, as per
+ STAT masks. */
+
+static void __iomem *st0x_dr; /* data register, read write 256
+ bytes in length. */
+
+static volatile int st0x_aborted = 0; /* set when we are aborted, ie by a
+ time out, etc. */
+
+static unsigned char controller_type = 0; /* set to SEAGATE for ST0x
+ boards or FD for TMC-8xx
+ boards */
+static int irq = IRQ;
+
+module_param(base_address, uint, 0);
+module_param(controller_type, byte, 0);
+module_param(irq, int, 0);
+MODULE_LICENSE("GPL");
+
+
+#define retcode(result) (((result) << 16) | (message << 8) | status)
+#define STATUS ((u8) readb(st0x_cr_sr))
+#define DATA ((u8) readb(st0x_dr))
+#define WRITE_CONTROL(d) { writeb((d), st0x_cr_sr); }
+#define WRITE_DATA(d) { writeb((d), st0x_dr); }
+
+#ifndef OVERRIDE
+static unsigned int seagate_bases[] = {
+ 0xc8000, 0xca000, 0xcc000,
+ 0xce000, 0xdc000, 0xde000
+};
+
+typedef struct {
+ const unsigned char *signature;
+ unsigned offset;
+ unsigned length;
+ unsigned char type;
+} Signature;
+
+static Signature __initdata signatures[] = {
+ {"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+ {"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+
+/*
+ * The following two lines are NOT mistakes. One detects ROM revision
+ * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
+ * and this is not going to change, the "SEAGATE" and "SCSI" together
+ * are probably "good enough"
+ */
+
+ {"SEAGATE SCSI BIOS ", 16, 17, SEAGATE},
+ {"SEAGATE SCSI BIOS ", 17, 17, SEAGATE},
+
+/*
+ * However, future domain makes several incompatible SCSI boards, so specific
+ * signatures must be used.
+ */
+
+ {"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90", 5, 47, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90", 5, 47, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+ {"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
+ {"FUTURE DOMAIN TMC-950", 5, 21, FD},
+ /* Added for 2.2.16 by Matthias_Heidbrink@b.maus.de */
+ {"IBM F1 V1.2009/22/93", 5, 25, FD},
+};
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature))
+#endif /* n OVERRIDE */
+
+/*
+ * hostno stores the hostnumber, as told to us by the init routine.
+ */
+
+static int hostno = -1;
+static void seagate_reconnect_intr (int, void *, struct pt_regs *);
+static irqreturn_t do_seagate_reconnect_intr (int, void *, struct pt_regs *);
+
+#ifdef FAST
+static int fast = 1;
+#else
+#define fast 0
+#endif
+
+#ifdef SLOW_RATE
+/*
+ * Support for broken devices :
+ * The Seagate board has a handshaking problem. Namely, a lack
+ * thereof for slow devices. You can blast 600K/second through
+ * it if you are polling for each byte, more if you do a blind
+ * transfer. In the first case, with a fast device, REQ will
+ * transition high-low or high-low-high before your loop restarts
+ * and you'll have no problems. In the second case, the board
+ * will insert wait states for up to 13.2 usecs for REQ to
+ * transition low->high, and everything will work.
+ *
+ * However, there's nothing in the state machine that says
+ * you *HAVE* to see a high-low-high set of transitions before
+ * sending the next byte, and slow things like the Trantor CD ROMS
+ * will break because of this.
+ *
+ * So, we need to slow things down, which isn't as simple as it
+ * seems. We can't slow things down period, because then people
+ * who don't recompile their kernels will shoot me for ruining
+ * their performance. We need to do it on a case per case basis.
+ *
+ * The best for performance will be to, only for borken devices
+ * (this is stored on a per-target basis in the scsi_devices array)
+ *
+ * Wait for a low->high transition before continuing with that
+ * transfer. If we timeout, continue anyways. We don't need
+ * a long timeout, because REQ should only be asserted until the
+ * corresponding ACK is received and processed.
+ *
+ * Note that we can't use the system timer for this, because of
+ * resolution, and we *really* can't use the timer chip since
+ * gettimeofday() and the beeper routines use that. So,
+ * the best thing for us to do will be to calibrate a timing
+ * loop in the initialization code using the timer chip before
+ * gettimeofday() can screw with it.
+ *
+ * FIXME: this is broken (not borken :-). Empty loop costs less than
+ * loop with ISA access in it! -- pavel@ucw.cz
+ */
+
+static int borken_calibration = 0;
+
+static void __init borken_init (void)
+{
+ register int count = 0, start = jiffies + 1, stop = start + 25;
+
+ /* FIXME: There may be a better approach, this is a straight port for
+ now */
+ preempt_disable();
+ while (time_before (jiffies, start))
+ cpu_relax();
+ for (; time_before (jiffies, stop); ++count)
+ cpu_relax();
+ preempt_enable();
+
+/*
+ * Ok, we now have a count for .25 seconds. Convert to a
+ * count per second and divide by transfer rate in K. */
+
+ borken_calibration = (count * 4) / (SLOW_RATE * 1024);
+
+ if (borken_calibration < 1)
+ borken_calibration = 1;
+}
+
+static inline void borken_wait (void)
+{
+ register int count;
+
+ for (count = borken_calibration; count && (STATUS & STAT_REQ); --count)
+ cpu_relax();
+
+#if (DEBUG & DEBUG_BORKEN)
+ if (count)
+ printk ("scsi%d : borken timeout\n", hostno);
+#endif
+}
+
+#endif /* def SLOW_RATE */
+
+/* These beasts only live on ISA, and ISA means 8MHz. Each ULOOP()
+ * contains at least one ISA access, which takes more than 0.125
+ * usec. So if we loop 8 times time in usec, we are safe.
+ */
+
+#define ULOOP( i ) for (clock = i*8;;)
+#define TIMEOUT (!(clock--))
+
+int __init seagate_st0x_detect (Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *instance;
+ int i, j;
+ unsigned long cr, dr;
+
+ tpnt->proc_name = "seagate";
+/*
+ * First, we try for the manual override.
+ */
+ DANY ("Autodetecting ST0x / TMC-8xx\n");
+
+ if (hostno != -1) {
+ printk (KERN_ERR "seagate_st0x_detect() called twice?!\n");
+ return 0;
+ }
+
+/* If the user specified the controller type from the command line,
+ controller_type will be non-zero, so don't try to detect one */
+
+ if (!controller_type) {
+#ifdef OVERRIDE
+ base_address = OVERRIDE;
+ controller_type = CONTROLLER;
+
+ DANY ("Base address overridden to %x, controller type is %s\n",
+ base_address,
+ controller_type == SEAGATE ? "SEAGATE" : "FD");
+#else /* OVERRIDE */
+/*
+ * To detect this card, we simply look for the signature
+ * from the BIOS version notice in all the possible locations
+ * of the ROM's. This has a nice side effect of not trashing
+ * any register locations that might be used by something else.
+ *
+ * XXX - note that we probably should be probing the address
+ * space for the on-board RAM instead.
+ */
+
+ for (i = 0; i < (sizeof (seagate_bases) / sizeof (unsigned int)); ++i) {
+ void __iomem *p = ioremap(seagate_bases[i], 0x2000);
+ if (!p)
+ continue;
+ for (j = 0; j < NUM_SIGNATURES; ++j)
+ if (check_signature(p + signatures[j].offset, signatures[j].signature, signatures[j].length)) {
+ base_address = seagate_bases[i];
+ controller_type = signatures[j].type;
+ break;
+ }
+ iounmap(p);
+ }
+#endif /* OVERRIDE */
+ }
+ /* (! controller_type) */
+ tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+ tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
+
+ if (!base_address) {
+ printk(KERN_INFO "seagate: ST0x/TMC-8xx not detected.\n");
+ return 0;
+ }
+
+ cr = base_address + (controller_type == SEAGATE ? 0x1a00 : 0x1c00);
+ dr = cr + 0x200;
+ st0x_cr_sr = ioremap(cr, 0x100);
+ st0x_dr = ioremap(dr, 0x100);
+
+ DANY("%s detected. Base address = %x, cr = %x, dr = %x\n",
+ tpnt->name, base_address, cr, dr);
+
+ /*
+ * At all times, we will use IRQ 5. Should also check for IRQ3
+ * if we lose our first interrupt.
+ */
+ instance = scsi_register (tpnt, 0);
+ if (instance == NULL)
+ return 0;
+
+ hostno = instance->host_no;
+ if (request_irq (irq, do_seagate_reconnect_intr, SA_INTERRUPT, (controller_type == SEAGATE) ? "seagate" : "tmc-8xx", instance)) {
+ printk(KERN_ERR "scsi%d : unable to allocate IRQ%d\n", hostno, irq);
+ return 0;
+ }
+ instance->irq = irq;
+ instance->io_port = base_address;
+#ifdef SLOW_RATE
+ printk(KERN_INFO "Calibrating borken timer... ");
+ borken_init();
+ printk(" %d cycles per transfer\n", borken_calibration);
+#endif
+ printk (KERN_INFO "This is one second... ");
+ {
+ int clock;
+ ULOOP (1 * 1000 * 1000) {
+ STATUS;
+ if (TIMEOUT)
+ break;
+ }
+ }
+
+ printk ("done, %s options:"
+#ifdef ARBITRATE
+ " ARBITRATE"
+#endif
+#ifdef DEBUG
+ " DEBUG"
+#endif
+#ifdef FAST
+ " FAST"
+#ifdef FAST32
+ "32"
+#endif
+#endif
+#ifdef LINKED
+ " LINKED"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef SEAGATE_USE_ASM
+ " SEAGATE_USE_ASM"
+#endif
+#ifdef SLOW_RATE
+ " SLOW_RATE"
+#endif
+#ifdef SWAPSTAT
+ " SWAPSTAT"
+#endif
+#ifdef SWAPCNTDATA
+ " SWAPCNTDATA"
+#endif
+ "\n", tpnt->name);
+ return 1;
+}
+
+static const char *seagate_st0x_info (struct Scsi_Host *shpnt)
+{
+ static char buffer[64];
+
+ snprintf(buffer, 64, "%s at irq %d, address 0x%05X",
+ (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
+ irq, base_address);
+ return buffer;
+}
+
+/*
+ * These are our saved pointers for the outstanding command that is
+ * waiting for a reconnect
+ */
+
+static unsigned char current_target, current_lun;
+static unsigned char *current_cmnd, *current_data;
+static int current_nobuffs;
+static struct scatterlist *current_buffer;
+static int current_bufflen;
+
+#ifdef LINKED
+/*
+ * linked_connected indicates whether or not we are currently connected to
+ * linked_target, linked_lun and in an INFORMATION TRANSFER phase,
+ * using linked commands.
+ */
+
+static int linked_connected = 0;
+static unsigned char linked_target, linked_lun;
+#endif
+
+static void (*done_fn) (Scsi_Cmnd *) = NULL;
+static Scsi_Cmnd *SCint = NULL;
+
+/*
+ * These control whether or not disconnect / reconnect will be attempted,
+ * or are being attempted.
+ */
+
+#define NO_RECONNECT 0
+#define RECONNECT_NOW 1
+#define CAN_RECONNECT 2
+
+/*
+ * LINKED_RIGHT indicates that we are currently connected to the correct target
+ * for this command, LINKED_WRONG indicates that we are connected to the wrong
+ * target. Note that these imply CAN_RECONNECT and require defined(LINKED).
+ */
+
+#define LINKED_RIGHT 3
+#define LINKED_WRONG 4
+
+/*
+ * This determines if we are expecting to reconnect or not.
+ */
+
+static int should_reconnect = 0;
+
+/*
+ * The seagate_reconnect_intr routine is called when a target reselects the
+ * host adapter. This occurs on the interrupt triggered by the target
+ * asserting SEL.
+ */
+
+static irqreturn_t do_seagate_reconnect_intr(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave (dev->host_lock, flags);
+ seagate_reconnect_intr (irq, dev_id, regs);
+ spin_unlock_irqrestore (dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void seagate_reconnect_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ int temp;
+ Scsi_Cmnd *SCtmp;
+
+ DPRINTK (PHASE_RESELECT, "scsi%d : seagate_reconnect_intr() called\n", hostno);
+
+ if (!should_reconnect)
+ printk(KERN_WARNING "scsi%d: unexpected interrupt.\n", hostno);
+ else {
+ should_reconnect = 0;
+
+ DPRINTK (PHASE_RESELECT, "scsi%d : internal_command(%d, %08x, %08x, RECONNECT_NOW\n",
+ hostno, current_target, current_data, current_bufflen);
+
+ temp = internal_command (current_target, current_lun, current_cmnd, current_data, current_bufflen, RECONNECT_NOW);
+
+ if (msg_byte(temp) != DISCONNECT) {
+ if (done_fn) {
+ DPRINTK(PHASE_RESELECT, "scsi%d : done_fn(%d,%08x)", hostno, hostno, temp);
+ if (!SCint)
+ panic ("SCint == NULL in seagate");
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = temp;
+ done_fn(SCtmp);
+ } else
+ printk(KERN_ERR "done_fn() not defined.\n");
+ }
+ }
+}
+
+/*
+ * The seagate_st0x_queue_command() function provides a queued interface
+ * to the seagate SCSI driver. Basically, it just passes control onto the
+ * seagate_command() function, after fixing it so that the done_fn()
+ * is set to the one passed to the function. We have to be very careful,
+ * because there are some commands on some devices that do not disconnect,
+ * and if we simply call the done_fn when the command is done then another
+ * command is started and queue_command is called again... We end up
+ * overflowing the kernel stack, and this tends not to be such a good idea.
+ */
+
+static int recursion_depth = 0;
+
+static int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ int result, reconnect;
+ Scsi_Cmnd *SCtmp;
+
+ DANY ("seagate: que_command");
+ done_fn = done;
+ current_target = SCpnt->device->id;
+ current_lun = SCpnt->device->lun;
+ current_cmnd = SCpnt->cmnd;
+ current_data = (unsigned char *) SCpnt->request_buffer;
+ current_bufflen = SCpnt->request_bufflen;
+ SCint = SCpnt;
+ if (recursion_depth)
+ return 1;
+ recursion_depth++;
+ do {
+#ifdef LINKED
+ /*
+ * Set linked command bit in control field of SCSI command.
+ */
+
+ current_cmnd[SCpnt->cmd_len] |= 0x01;
+ if (linked_connected) {
+ DPRINTK (DEBUG_LINKED, "scsi%d : using linked commands, current I_T_L nexus is ", hostno);
+ if (linked_target == current_target && linked_lun == current_lun)
+ {
+ DPRINTK(DEBUG_LINKED, "correct\n");
+ reconnect = LINKED_RIGHT;
+ } else {
+ DPRINTK(DEBUG_LINKED, "incorrect\n");
+ reconnect = LINKED_WRONG;
+ }
+ } else
+#endif /* LINKED */
+ reconnect = CAN_RECONNECT;
+
+ result = internal_command(SCint->device->id, SCint->device->lun, SCint->cmnd,
+ SCint->request_buffer, SCint->request_bufflen, reconnect);
+ if (msg_byte(result) == DISCONNECT)
+ break;
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = result;
+ done_fn(SCtmp);
+ }
+ while (SCint);
+ recursion_depth--;
+ return 0;
+}
+
+static int internal_command (unsigned char target, unsigned char lun,
+ const void *cmnd, void *buff, int bufflen, int reselect)
+{
+ unsigned char *data = NULL;
+ struct scatterlist *buffer = NULL;
+ int clock, temp, nobuffs = 0, done = 0, len = 0;
+#ifdef DEBUG
+ int transfered = 0, phase = 0, newphase;
+#endif
+ register unsigned char status_read;
+ unsigned char tmp_data, tmp_control, status = 0, message = 0;
+ unsigned transfersize = 0, underflow = 0;
+#ifdef SLOW_RATE
+ int borken = (int) SCint->device->borken; /* Does the current target require
+ Very Slow I/O ? */
+#endif
+
+ incommand = 0;
+ st0x_aborted = 0;
+
+#if (DEBUG & PRINT_COMMAND)
+ printk("scsi%d : target = %d, command = ", hostno, target);
+ print_command((unsigned char *) cmnd);
+#endif
+
+#if (DEBUG & PHASE_RESELECT)
+ switch (reselect) {
+ case RECONNECT_NOW:
+ printk("scsi%d : reconnecting\n", hostno);
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT:
+ printk("scsi%d : connected, can reconnect\n", hostno);
+ break;
+ case LINKED_WRONG:
+ printk("scsi%d : connected to wrong target, can reconnect\n",
+ hostno);
+ break;
+#endif
+ case CAN_RECONNECT:
+ printk("scsi%d : allowed to reconnect\n", hostno);
+ break;
+ default:
+ printk("scsi%d : not allowed to reconnect\n", hostno);
+ }
+#endif
+
+ if (target == (controller_type == SEAGATE ? 7 : 6))
+ return DID_BAD_TARGET;
+
+ /*
+ * We work it differently depending on if this is is "the first time,"
+ * or a reconnect. If this is a reselect phase, then SEL will
+ * be asserted, and we must skip selection / arbitration phases.
+ */
+
+ switch (reselect) {
+ case RECONNECT_NOW:
+ DPRINTK (PHASE_RESELECT, "scsi%d : phase RESELECT \n", hostno);
+ /*
+ * At this point, we should find the logical or of our ID
+ * and the original target's ID on the BUS, with BSY, SEL,
+ * and I/O signals asserted.
+ *
+ * After ARBITRATION phase is completed, only SEL, BSY,
+ * and the target ID are asserted. A valid initiator ID
+ * is not on the bus until IO is asserted, so we must wait
+ * for that.
+ */
+ ULOOP (100 * 1000) {
+ temp = STATUS;
+ if ((temp & STAT_IO) && !(temp & STAT_BSY))
+ break;
+ if (TIMEOUT) {
+ DPRINTK (PHASE_RESELECT, "scsi%d : RESELECT timed out while waiting for IO .\n", hostno);
+ return (DID_BAD_INTR << 16);
+ }
+ }
+
+ /*
+ * After I/O is asserted by the target, we can read our ID
+ * and its ID off of the BUS.
+ */
+
+ if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40))) {
+ DPRINTK (PHASE_RESELECT, "scsi%d : detected reconnect request to different target.\n\tData bus = %d\n", hostno, temp);
+ return (DID_BAD_INTR << 16);
+ }
+
+ if (!(temp & (1 << current_target))) {
+ printk(KERN_WARNING "scsi%d : Unexpected reselect interrupt. Data bus = %d\n", hostno, temp);
+ return (DID_BAD_INTR << 16);
+ }
+
+ buffer = current_buffer;
+ cmnd = current_cmnd; /* WDE add */
+ data = current_data; /* WDE add */
+ len = current_bufflen; /* WDE add */
+ nobuffs = current_nobuffs;
+
+ /*
+ * We have determined that we have been selected. At this
+ * point, we must respond to the reselection by asserting
+ * BSY ourselves
+ */
+
+#if 1
+ WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+#else
+ WRITE_CONTROL (BASE_CMD | CMD_BSY);
+#endif
+
+ /*
+ * The target will drop SEL, and raise BSY, at which time
+ * we must drop BSY.
+ */
+
+ ULOOP (100 * 1000) {
+ if (!(STATUS & STAT_SEL))
+ break;
+ if (TIMEOUT) {
+ WRITE_CONTROL (BASE_CMD | CMD_INTR);
+ DPRINTK (PHASE_RESELECT, "scsi%d : RESELECT timed out while waiting for SEL.\n", hostno);
+ return (DID_BAD_INTR << 16);
+ }
+ }
+ WRITE_CONTROL (BASE_CMD);
+ /*
+ * At this point, we have connected with the target
+ * and can get on with our lives.
+ */
+ break;
+ case CAN_RECONNECT:
+#ifdef LINKED
+ /*
+ * This is a bletcherous hack, just as bad as the Unix #!
+ * interpreter stuff. If it turns out we are using the wrong
+ * I_T_L nexus, the easiest way to deal with it is to go into
+ * our INFORMATION TRANSFER PHASE code, send a ABORT
+ * message on MESSAGE OUT phase, and then loop back to here.
+ */
+connect_loop:
+#endif
+ DPRINTK (PHASE_BUS_FREE, "scsi%d : phase = BUS FREE \n", hostno);
+
+ /*
+ * BUS FREE PHASE
+ *
+ * On entry, we make sure that the BUS is in a BUS FREE
+ * phase, by insuring that both BSY and SEL are low for
+ * at least one bus settle delay. Several reads help
+ * eliminate wire glitch.
+ */
+
+#ifndef ARBITRATE
+#error FIXME: this is broken: we may not use jiffies here - we are under cli(). It will hardlock.
+ clock = jiffies + ST0X_BUS_FREE_DELAY;
+
+ while (((STATUS | STATUS | STATUS) & (STAT_BSY | STAT_SEL)) && (!st0x_aborted) && time_before (jiffies, clock))
+ cpu_relax();
+
+ if (time_after (jiffies, clock))
+ return retcode (DID_BUS_BUSY);
+ else if (st0x_aborted)
+ return retcode (st0x_aborted);
+#endif
+ DPRINTK (PHASE_SELECTION, "scsi%d : phase = SELECTION\n", hostno);
+
+ clock = jiffies + ST0X_SELECTION_DELAY;
+
+ /*
+ * Arbitration/selection procedure :
+ * 1. Disable drivers
+ * 2. Write HOST adapter address bit
+ * 3. Set start arbitration.
+ * 4. We get either ARBITRATION COMPLETE or SELECT at this
+ * point.
+ * 5. OR our ID and targets on bus.
+ * 6. Enable SCSI drivers and asserted SEL and ATTN
+ */
+
+#ifdef ARBITRATE
+ /* FIXME: verify host lock is always held here */
+ WRITE_CONTROL(0);
+ WRITE_DATA((controller_type == SEAGATE) ? 0x80 : 0x40);
+ WRITE_CONTROL(CMD_START_ARB);
+
+ ULOOP (ST0X_SELECTION_DELAY * 10000) {
+ status_read = STATUS;
+ if (status_read & STAT_ARB_CMPL)
+ break;
+ if (st0x_aborted) /* FIXME: What? We are going to do something even after abort? */
+ break;
+ if (TIMEOUT || (status_read & STAT_SEL)) {
+ printk(KERN_WARNING "scsi%d : arbitration lost or timeout.\n", hostno);
+ WRITE_CONTROL (BASE_CMD);
+ return retcode (DID_NO_CONNECT);
+ }
+ }
+ DPRINTK (PHASE_SELECTION, "scsi%d : arbitration complete\n", hostno);
+#endif
+
+ /*
+ * When the SCSI device decides that we're gawking at it,
+ * it will respond by asserting BUSY on the bus.
+ *
+ * Note : the Seagate ST-01/02 product manual says that we
+ * should twiddle the DATA register before the control
+ * register. However, this does not work reliably so we do
+ * it the other way around.
+ *
+ * Probably could be a problem with arbitration too, we
+ * really should try this with a SCSI protocol or logic
+ * analyzer to see what is going on.
+ */
+ tmp_data = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
+ tmp_control = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | (reselect ? CMD_ATTN : 0);
+
+ /* FIXME: verify host lock is always held here */
+#ifdef OLDCNTDATASCEME
+#ifdef SWAPCNTDATA
+ WRITE_CONTROL (tmp_control);
+ WRITE_DATA (tmp_data);
+#else
+ WRITE_DATA (tmp_data);
+ WRITE_CONTROL (tmp_control);
+#endif
+#else
+ tmp_control ^= CMD_BSY; /* This is guesswork. What used to be in driver */
+ WRITE_CONTROL (tmp_control); /* could never work: it sent data into control */
+ WRITE_DATA (tmp_data); /* register and control info into data. Hopefully */
+ tmp_control ^= CMD_BSY; /* fixed, but order of first two may be wrong. */
+ WRITE_CONTROL (tmp_control); /* -- pavel@ucw.cz */
+#endif
+
+ ULOOP (250 * 1000) {
+ if (st0x_aborted) {
+ /*
+ * If we have been aborted, and we have a
+ * command in progress, IE the target
+ * still has BSY asserted, then we will
+ * reset the bus, and notify the midlevel
+ * driver to expect sense.
+ */
+
+ WRITE_CONTROL (BASE_CMD);
+ if (STATUS & STAT_BSY) {
+ printk(KERN_WARNING "scsi%d : BST asserted after we've been aborted.\n", hostno);
+ seagate_st0x_bus_reset(NULL);
+ return retcode (DID_RESET);
+ }
+ return retcode (st0x_aborted);
+ }
+ if (STATUS & STAT_BSY)
+ break;
+ if (TIMEOUT) {
+ DPRINTK (PHASE_SELECTION, "scsi%d : NO CONNECT with target %d, stat = %x \n", hostno, target, STATUS);
+ return retcode (DID_NO_CONNECT);
+ }
+ }
+
+ /* Establish current pointers. Take into account scatter / gather */
+
+ if ((nobuffs = SCint->use_sg)) {
+#if (DEBUG & DEBUG_SG)
+ {
+ int i;
+ printk("scsi%d : scatter gather requested, using %d buffers.\n", hostno, nobuffs);
+ for (i = 0; i < nobuffs; ++i)
+ printk("scsi%d : buffer %d address = %p length = %d\n",
+ hostno, i,
+ page_address(buffer[i].page) + buffer[i].offset,
+ buffer[i].length);
+ }
+#endif
+
+ buffer = (struct scatterlist *) SCint->buffer;
+ len = buffer->length;
+ data = page_address(buffer->page) + buffer->offset;
+ } else {
+ DPRINTK (DEBUG_SG, "scsi%d : scatter gather not requested.\n", hostno);
+ buffer = NULL;
+ len = SCint->request_bufflen;
+ data = (unsigned char *) SCint->request_buffer;
+ }
+
+ DPRINTK (PHASE_DATAIN | PHASE_DATAOUT, "scsi%d : len = %d\n",
+ hostno, len);
+
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT:
+ break;
+ case LINKED_WRONG:
+ break;
+#endif
+ } /* end of switch(reselect) */
+
+ /*
+ * There are several conditions under which we wish to send a message :
+ * 1. When we are allowing disconnect / reconnect, and need to
+ * establish the I_T_L nexus via an IDENTIFY with the DiscPriv bit
+ * set.
+ *
+ * 2. When we are doing linked commands, are have the wrong I_T_L
+ * nexus established and want to send an ABORT message.
+ */
+
+ /* GCC does not like an ifdef inside a macro, so do it the hard way. */
+#ifdef LINKED
+ WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | (((reselect == CAN_RECONNECT)|| (reselect == LINKED_WRONG))? CMD_ATTN : 0));
+#else
+ WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | (((reselect == CAN_RECONNECT))? CMD_ATTN : 0));
+#endif
+
+ /*
+ * INFORMATION TRANSFER PHASE
+ *
+ * The nasty looking read / write inline assembler loops we use for
+ * DATAIN and DATAOUT phases are approximately 4-5 times as fast as
+ * the 'C' versions - since we're moving 1024 bytes of data, this
+ * really adds up.
+ *
+ * SJT: The nasty-looking assembler is gone, so it's slower.
+ *
+ */
+
+ DPRINTK (PHASE_ETC, "scsi%d : phase = INFORMATION TRANSFER\n", hostno);
+
+ incommand = 1;
+ transfersize = SCint->transfersize;
+ underflow = SCint->underflow;
+
+ /*
+ * Now, we poll the device for status information,
+ * and handle any requests it makes. Note that since we are unsure
+ * of how much data will be flowing across the system, etc and
+ * cannot make reasonable timeouts, that we will instead have the
+ * midlevel driver handle any timeouts that occur in this phase.
+ */
+
+ while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done) {
+#ifdef PARITY
+ if (status_read & STAT_PARITY) {
+ printk(KERN_ERR "scsi%d : got parity error\n", hostno);
+ st0x_aborted = DID_PARITY;
+ }
+#endif
+ if (status_read & STAT_REQ) {
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ if ((newphase = (status_read & REQ_MASK)) != phase) {
+ phase = newphase;
+ switch (phase) {
+ case REQ_DATAOUT:
+ printk ("scsi%d : phase = DATA OUT\n", hostno);
+ break;
+ case REQ_DATAIN:
+ printk ("scsi%d : phase = DATA IN\n", hostno);
+ break;
+ case REQ_CMDOUT:
+ printk
+ ("scsi%d : phase = COMMAND OUT\n", hostno);
+ break;
+ case REQ_STATIN:
+ printk ("scsi%d : phase = STATUS IN\n", hostno);
+ break;
+ case REQ_MSGOUT:
+ printk
+ ("scsi%d : phase = MESSAGE OUT\n", hostno);
+ break;
+ case REQ_MSGIN:
+ printk ("scsi%d : phase = MESSAGE IN\n", hostno);
+ break;
+ default:
+ printk ("scsi%d : phase = UNKNOWN\n", hostno);
+ st0x_aborted = DID_ERROR;
+ }
+ }
+#endif
+ switch (status_read & REQ_MASK) {
+ case REQ_DATAOUT:
+ /*
+ * If we are in fast mode, then we simply splat
+ * the data out in word-sized chunks as fast as
+ * we can.
+ */
+
+ if (!len) {
+#if 0
+ printk("scsi%d: underflow to target %d lun %d \n", hostno, target, lun);
+ st0x_aborted = DID_ERROR;
+ fast = 0;
+#endif
+ break;
+ }
+
+ if (fast && transfersize
+ && !(len % transfersize)
+ && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+ DPRINTK (DEBUG_FAST,
+ "scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n",
+ hostno, SCint->underflow,
+ SCint->transfersize, len,
+ data);
+
+ /* SJT: Start. Fast Write */
+#ifdef SEAGATE_USE_ASM
+ __asm__ ("cld\n\t"
+#ifdef FAST32
+ "shr $2, %%ecx\n\t"
+ "1:\t"
+ "lodsl\n\t"
+ "movl %%eax, (%%edi)\n\t"
+#else
+ "1:\t"
+ "lodsb\n\t"
+ "movb %%al, (%%edi)\n\t"
+#endif
+ "loop 1b;"
+ /* output */ :
+ /* input */ :"D" (st0x_dr),
+ "S"
+ (data),
+ "c" (SCint->transfersize)
+/* clobbered */
+ : "eax", "ecx",
+ "esi");
+#else /* SEAGATE_USE_ASM */
+ memcpy_toio(st0x_dr, data, transfersize);
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End */
+ len -= transfersize;
+ data += transfersize;
+ DPRINTK (DEBUG_FAST, "scsi%d : FAST transfer complete len = %d data = %08x\n", hostno, len, data);
+ } else {
+ /*
+ * We loop as long as we are in a
+ * data out phase, there is data to
+ * send, and BSY is still active.
+ */
+
+/* SJT: Start. Slow Write. */
+#ifdef SEAGATE_USE_ASM
+
+ int __dummy_1, __dummy_2;
+
+/*
+ * We loop as long as we are in a data out phase, there is data to send,
+ * and BSY is still active.
+ */
+/* Local variables : len = ecx , data = esi,
+ st0x_cr_sr = ebx, st0x_dr = edi
+*/
+ __asm__ (
+ /* Test for any data here at all. */
+ "orl %%ecx, %%ecx\n\t"
+ "jz 2f\n\t" "cld\n\t"
+/* "movl st0x_cr_sr, %%ebx\n\t" */
+/* "movl st0x_dr, %%edi\n\t" */
+ "1:\t"
+ "movb (%%ebx), %%al\n\t"
+ /* Test for BSY */
+ "test $1, %%al\n\t"
+ "jz 2f\n\t"
+ /* Test for data out phase - STATUS & REQ_MASK should be
+ REQ_DATAOUT, which is 0. */
+ "test $0xe, %%al\n\t"
+ "jnz 2f\n\t"
+ /* Test for REQ */
+ "test $0x10, %%al\n\t"
+ "jz 1b\n\t"
+ "lodsb\n\t"
+ "movb %%al, (%%edi)\n\t"
+ "loop 1b\n\t" "2:\n"
+ /* output */ :"=S" (data), "=c" (len),
+ "=b"
+ (__dummy_1),
+ "=D" (__dummy_2)
+/* input */
+ : "0" (data), "1" (len),
+ "2" (st0x_cr_sr),
+ "3" (st0x_dr)
+/* clobbered */
+ : "eax");
+#else /* SEAGATE_USE_ASM */
+ while (len) {
+ unsigned char stat;
+
+ stat = STATUS;
+ if (!(stat & STAT_BSY)
+ || ((stat & REQ_MASK) !=
+ REQ_DATAOUT))
+ break;
+ if (stat & STAT_REQ) {
+ WRITE_DATA (*data++);
+ --len;
+ }
+ }
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End. */
+ }
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = page_address(buffer->page) + buffer->offset;
+ DPRINTK (DEBUG_SG,
+ "scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+ }
+ break;
+
+ case REQ_DATAIN:
+#ifdef SLOW_RATE
+ if (borken) {
+#if (DEBUG & (PHASE_DATAIN))
+ transfered += len;
+#endif
+ for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN | STAT_REQ); --len) {
+ *data++ = DATA;
+ borken_wait();
+ }
+#if (DEBUG & (PHASE_DATAIN))
+ transfered -= len;
+#endif
+ } else
+#endif
+
+ if (fast && transfersize
+ && !(len % transfersize)
+ && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+ DPRINTK (DEBUG_FAST,
+ "scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n",
+ hostno, SCint->underflow,
+ SCint->transfersize, len,
+ data);
+
+/* SJT: Start. Fast Read */
+#ifdef SEAGATE_USE_ASM
+ __asm__ ("cld\n\t"
+#ifdef FAST32
+ "shr $2, %%ecx\n\t"
+ "1:\t"
+ "movl (%%esi), %%eax\n\t"
+ "stosl\n\t"
+#else
+ "1:\t"
+ "movb (%%esi), %%al\n\t"
+ "stosb\n\t"
+#endif
+ "loop 1b\n\t"
+ /* output */ :
+ /* input */ :"S" (st0x_dr),
+ "D"
+ (data),
+ "c" (SCint->transfersize)
+/* clobbered */
+ : "eax", "ecx",
+ "edi");
+#else /* SEAGATE_USE_ASM */
+ memcpy_fromio(data, st0x_dr, len);
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End */
+ len -= transfersize;
+ data += transfersize;
+#if (DEBUG & PHASE_DATAIN)
+ printk ("scsi%d: transfered += %d\n", hostno, transfersize);
+ transfered += transfersize;
+#endif
+
+ DPRINTK (DEBUG_FAST, "scsi%d : FAST transfer complete len = %d data = %08x\n", hostno, len, data);
+ } else {
+
+#if (DEBUG & PHASE_DATAIN)
+ printk ("scsi%d: transfered += %d\n", hostno, len);
+ transfered += len; /* Assume we'll transfer it all, then
+ subtract what we *didn't* transfer */
+#endif
+
+/*
+ * We loop as long as we are in a data in phase, there is room to read,
+ * and BSY is still active
+ */
+
+/* SJT: Start. */
+#ifdef SEAGATE_USE_ASM
+
+ int __dummy_3, __dummy_4;
+
+/* Dummy clobbering variables for the new gcc-2.95 */
+
+/*
+ * We loop as long as we are in a data in phase, there is room to read,
+ * and BSY is still active
+ */
+ /* Local variables : ecx = len, edi = data
+ esi = st0x_cr_sr, ebx = st0x_dr */
+ __asm__ (
+ /* Test for room to read */
+ "orl %%ecx, %%ecx\n\t"
+ "jz 2f\n\t" "cld\n\t"
+/* "movl st0x_cr_sr, %%esi\n\t" */
+/* "movl st0x_dr, %%ebx\n\t" */
+ "1:\t"
+ "movb (%%esi), %%al\n\t"
+ /* Test for BSY */
+ "test $1, %%al\n\t"
+ "jz 2f\n\t"
+ /* Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN,
+ = STAT_IO, which is 4. */
+ "movb $0xe, %%ah\n\t"
+ "andb %%al, %%ah\n\t"
+ "cmpb $0x04, %%ah\n\t"
+ "jne 2f\n\t"
+ /* Test for REQ */
+ "test $0x10, %%al\n\t"
+ "jz 1b\n\t"
+ "movb (%%ebx), %%al\n\t"
+ "stosb\n\t"
+ "loop 1b\n\t" "2:\n"
+ /* output */ :"=D" (data), "=c" (len),
+ "=S"
+ (__dummy_3),
+ "=b" (__dummy_4)
+/* input */
+ : "0" (data), "1" (len),
+ "2" (st0x_cr_sr),
+ "3" (st0x_dr)
+/* clobbered */
+ : "eax");
+#else /* SEAGATE_USE_ASM */
+ while (len) {
+ unsigned char stat;
+
+ stat = STATUS;
+ if (!(stat & STAT_BSY)
+ || ((stat & REQ_MASK) !=
+ REQ_DATAIN))
+ break;
+ if (stat & STAT_REQ) {
+ *data++ = DATA;
+ --len;
+ }
+ }
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End. */
+#if (DEBUG & PHASE_DATAIN)
+ printk ("scsi%d: transfered -= %d\n", hostno, len);
+ transfered -= len; /* Since we assumed all of Len got *
+ transfered, correct our mistake */
+#endif
+ }
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = page_address(buffer->page) + buffer->offset;
+ DPRINTK (DEBUG_SG, "scsi%d : next scatter-gather buffer len = %d address = %08x\n", hostno, len, data);
+ }
+ break;
+
+ case REQ_CMDOUT:
+ while (((status_read = STATUS) & STAT_BSY) &&
+ ((status_read & REQ_MASK) == REQ_CMDOUT))
+ if (status_read & STAT_REQ) {
+ WRITE_DATA (*(const unsigned char *) cmnd);
+ cmnd = 1 + (const unsigned char *)cmnd;
+#ifdef SLOW_RATE
+ if (borken)
+ borken_wait ();
+#endif
+ }
+ break;
+
+ case REQ_STATIN:
+ status = DATA;
+ break;
+
+ case REQ_MSGOUT:
+ /*
+ * We can only have sent a MSG OUT if we
+ * requested to do this by raising ATTN.
+ * So, we must drop ATTN.
+ */
+ WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE);
+ /*
+ * If we are reconnecting, then we must
+ * send an IDENTIFY message in response
+ * to MSGOUT.
+ */
+ switch (reselect) {
+ case CAN_RECONNECT:
+ WRITE_DATA (IDENTIFY (1, lun));
+ DPRINTK (PHASE_RESELECT | PHASE_MSGOUT, "scsi%d : sent IDENTIFY message.\n", hostno);
+ break;
+#ifdef LINKED
+ case LINKED_WRONG:
+ WRITE_DATA (ABORT);
+ linked_connected = 0;
+ reselect = CAN_RECONNECT;
+ goto connect_loop;
+ DPRINTK (PHASE_MSGOUT | DEBUG_LINKED, "scsi%d : sent ABORT message to cancel incorrect I_T_L nexus.\n", hostno);
+#endif /* LINKED */
+ DPRINTK (DEBUG_LINKED, "correct\n");
+ default:
+ WRITE_DATA (NOP);
+ printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
+ }
+ break;
+
+ case REQ_MSGIN:
+ switch (message = DATA) {
+ case DISCONNECT:
+ DANY("seagate: deciding to disconnect\n");
+ should_reconnect = 1;
+ current_data = data; /* WDE add */
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_nobuffs = nobuffs;
+#ifdef LINKED
+ linked_connected = 0;
+#endif
+ done = 1;
+ DPRINTK ((PHASE_RESELECT | PHASE_MSGIN), "scsi%d : disconnected.\n", hostno);
+ break;
+
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+#endif
+ case COMMAND_COMPLETE:
+ /*
+ * Note : we should check for underflow here.
+ */
+ DPRINTK(PHASE_MSGIN, "scsi%d : command complete.\n", hostno);
+ done = 1;
+ break;
+ case ABORT:
+ DPRINTK(PHASE_MSGIN, "scsi%d : abort message.\n", hostno);
+ done = 1;
+ break;
+ case SAVE_POINTERS:
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_data = data; /* WDE mod */
+ current_nobuffs = nobuffs;
+ DPRINTK (PHASE_MSGIN, "scsi%d : pointers saved.\n", hostno);
+ break;
+ case RESTORE_POINTERS:
+ buffer = current_buffer;
+ cmnd = current_cmnd;
+ data = current_data; /* WDE mod */
+ len = current_bufflen;
+ nobuffs = current_nobuffs;
+ DPRINTK(PHASE_MSGIN, "scsi%d : pointers restored.\n", hostno);
+ break;
+ default:
+
+ /*
+ * IDENTIFY distinguishes itself
+ * from the other messages by
+ * setting the high bit.
+ *
+ * Note : we need to handle at
+ * least one outstanding command
+ * per LUN, and need to hash the
+ * SCSI command for that I_T_L
+ * nexus based on the known ID
+ * (at this point) and LUN.
+ */
+
+ if (message & 0x80) {
+ DPRINTK (PHASE_MSGIN, "scsi%d : IDENTIFY message received from id %d, lun %d.\n", hostno, target, message & 7);
+ } else {
+ /*
+ * We should go into a
+ * MESSAGE OUT phase, and
+ * send a MESSAGE_REJECT
+ * if we run into a message
+ * that we don't like. The
+ * seagate driver needs
+ * some serious
+ * restructuring first
+ * though.
+ */
+ DPRINTK (PHASE_MSGIN, "scsi%d : unknown message %d from target %d.\n", hostno, message, target);
+ }
+ }
+ break;
+ default:
+ printk(KERN_ERR "scsi%d : unknown phase.\n", hostno);
+ st0x_aborted = DID_ERROR;
+ } /* end of switch (status_read & REQ_MASK) */
+#ifdef SLOW_RATE
+ /*
+ * I really don't care to deal with borken devices in
+ * each single byte transfer case (ie, message in,
+ * message out, status), so I'll do the wait here if
+ * necessary.
+ */
+ if(borken)
+ borken_wait();
+#endif
+
+ } /* if(status_read & STAT_REQ) ends */
+ } /* while(((status_read = STATUS)...) ends */
+
+ DPRINTK(PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT, "scsi%d : Transfered %d bytes\n", hostno, transfered);
+
+#if (DEBUG & PHASE_EXIT)
+#if 0 /* Doesn't work for scatter/gather */
+ printk("Buffer : \n");
+ for(i = 0; i < 20; ++i)
+ printk("%02x ", ((unsigned char *) data)[i]); /* WDE mod */
+ printk("\n");
+#endif
+ printk("scsi%d : status = ", hostno);
+ print_status(status);
+ printk(" message = %02x\n", message);
+#endif
+
+ /* We shouldn't reach this until *after* BSY has been deasserted */
+
+#ifdef LINKED
+ else
+ {
+ /*
+ * Fix the message byte so that unsuspecting high level drivers
+ * don't puke when they see a LINKED COMMAND message in place of
+ * the COMMAND COMPLETE they may be expecting. Shouldn't be
+ * necessary, but it's better to be on the safe side.
+ *
+ * A non LINKED* message byte will indicate that the command
+ * completed, and we are now disconnected.
+ */
+
+ switch (message) {
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ message = COMMAND_COMPLETE;
+ linked_target = current_target;
+ linked_lun = current_lun;
+ linked_connected = 1;
+ DPRINTK (DEBUG_LINKED, "scsi%d : keeping I_T_L nexus established for linked command.\n", hostno);
+ /* We also will need to adjust status to accommodate intermediate
+ conditions. */
+ if ((status == INTERMEDIATE_GOOD) || (status == INTERMEDIATE_C_GOOD))
+ status = GOOD;
+ break;
+ /*
+ * We should also handle what are "normal" termination
+ * messages here (ABORT, BUS_DEVICE_RESET?, and
+ * COMMAND_COMPLETE individually, and flake if things
+ * aren't right.
+ */
+ default:
+ DPRINTK (DEBUG_LINKED, "scsi%d : closing I_T_L nexus.\n", hostno);
+ linked_connected = 0;
+ }
+ }
+#endif /* LINKED */
+
+ if (should_reconnect) {
+ DPRINTK (PHASE_RESELECT, "scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n", hostno);
+ WRITE_CONTROL (BASE_CMD | CMD_INTR);
+ } else
+ WRITE_CONTROL (BASE_CMD);
+
+ return retcode (st0x_aborted);
+} /* end of internal_command */
+
+static int seagate_st0x_abort (Scsi_Cmnd * SCpnt)
+{
+ st0x_aborted = DID_ABORT;
+ return SUCCESS;
+}
+
+#undef ULOOP
+#undef TIMEOUT
+
+/*
+ * the seagate_st0x_reset function resets the SCSI bus
+ *
+ * May be called with SCpnt = NULL
+ */
+
+static int seagate_st0x_bus_reset(Scsi_Cmnd * SCpnt)
+{
+ /* No timeouts - this command is going to fail because it was reset. */
+ DANY ("scsi%d: Reseting bus... ", hostno);
+
+ /* assert RESET signal on SCSI bus. */
+ WRITE_CONTROL (BASE_CMD | CMD_RST);
+
+ udelay (20 * 1000);
+
+ WRITE_CONTROL (BASE_CMD);
+ st0x_aborted = DID_RESET;
+
+ DANY ("done.\n");
+ return SUCCESS;
+}
+
+static int seagate_st0x_host_reset(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+static int seagate_st0x_device_reset(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+static int seagate_st0x_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ release_region(shost->io_port, shost->n_io_port);
+ return 0;
+}
+
+static Scsi_Host_Template driver_template = {
+ .detect = seagate_st0x_detect,
+ .release = seagate_st0x_release,
+ .info = seagate_st0x_info,
+ .queuecommand = seagate_st0x_queue_command,
+ .eh_abort_handler = seagate_st0x_abort,
+ .eh_bus_reset_handler = seagate_st0x_bus_reset,
+ .eh_host_reset_handler = seagate_st0x_host_reset,
+ .eh_device_reset_handler = seagate_st0x_device_reset,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/seagate.h b/drivers/scsi/seagate.h
new file mode 100644
index 000000000000..e49e8ecfb54d
--- /dev/null
+++ b/drivers/scsi/seagate.h
@@ -0,0 +1,21 @@
+/*
+ * seagate.h Copyright (C) 1992 Drew Eckhardt
+ * low level scsi driver header for ST01/ST02 by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#ifndef _SEAGATE_H
+#define SEAGATE_H
+
+static int seagate_st0x_detect(Scsi_Host_Template *);
+static int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+static int seagate_st0x_abort(Scsi_Cmnd *);
+static const char *seagate_st0x_info(struct Scsi_Host *);
+static int seagate_st0x_bus_reset(Scsi_Cmnd *);
+static int seagate_st0x_device_reset(Scsi_Cmnd *);
+static int seagate_st0x_host_reset(Scsi_Cmnd *);
+
+#endif /* _SEAGATE_H */
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
new file mode 100644
index 000000000000..fd72d73bb244
--- /dev/null
+++ b/drivers/scsi/sg.c
@@ -0,0 +1,3092 @@
+/*
+ * History:
+ * Started: Aug 9 by Lawrence Foard (entropy@world.std.com),
+ * to allow user process control of SCSI devices.
+ * Development Sponsored by Killy Corp. NY NY
+ *
+ * Original driver (sg.c):
+ * Copyright (C) 1992 Lawrence Foard
+ * Version 2 and 3 extensions to driver:
+ * Copyright (C) 1998 - 2005 Douglas Gilbert
+ *
+ * Modified 19-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs 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, or (at your option)
+ * any later version.
+ *
+ */
+
+static int sg_version_num = 30532; /* 2 digits for each component */
+#define SG_VERSION_STR "3.5.32"
+
+/*
+ * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
+ * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
+ * the kernel/module needs to be built with CONFIG_SCSI_LOGGING
+ * (otherwise the macros compile to empty statements).
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <linux/moduleparam.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/cdev.h>
+#include <linux/seq_file.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+
+#include "scsi_logging.h"
+
+#ifdef CONFIG_SCSI_PROC_FS
+#include <linux/proc_fs.h>
+static char *sg_version_date = "20050117";
+
+static int sg_proc_init(void);
+static void sg_proc_cleanup(void);
+#endif
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif /* LINUX_VERSION_CODE */
+
+#define SG_ALLOW_DIO_DEF 0
+#define SG_ALLOW_DIO_CODE /* compile out by commenting this define */
+
+#define SG_MAX_DEVS 32768
+
+/*
+ * Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d)
+ * Then when using 32 bit integers x * m may overflow during the calculation.
+ * Replacing muldiv(x) by muldiv(x)=((x % d) * m) / d + int(x / d) * m
+ * calculates the same, but prevents the overflow when both m and d
+ * are "small" numbers (like HZ and USER_HZ).
+ * Of course an overflow is inavoidable if the result of muldiv doesn't fit
+ * in 32 bits.
+ */
+#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL))
+
+#define SG_DEFAULT_TIMEOUT MULDIV(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)
+
+int sg_big_buff = SG_DEF_RESERVED_SIZE;
+/* N.B. This variable is readable and writeable via
+ /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer
+ of this size (or less if there is not enough memory) will be reserved
+ for use by this file descriptor. [Deprecated usage: this variable is also
+ readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into
+ the kernel (i.e. it is not a module).] */
+static int def_reserved_size = -1; /* picks up init parameter */
+static int sg_allow_dio = SG_ALLOW_DIO_DEF;
+
+#define SG_SECTOR_SZ 512
+#define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
+
+#define SG_DEV_ARR_LUMP 32 /* amount to over allocate sg_dev_arr by */
+
+static int sg_add(struct class_device *);
+static void sg_remove(struct class_device *);
+
+static Scsi_Request *dummy_cmdp; /* only used for sizeof */
+
+static DEFINE_RWLOCK(sg_dev_arr_lock); /* Also used to lock
+ file descriptor list for device */
+
+static struct class_interface sg_interface = {
+ .add = sg_add,
+ .remove = sg_remove,
+};
+
+typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */
+ unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */
+ unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */
+ unsigned bufflen; /* Size of (aggregate) data buffer */
+ unsigned b_malloc_len; /* actual len malloc'ed in buffer */
+ void *buffer; /* Data buffer or scatter list (k_use_sg>0) */
+ char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */
+ unsigned char cmd_opcode; /* first byte of command */
+} Sg_scatter_hold;
+
+struct sg_device; /* forward declarations */
+struct sg_fd;
+
+typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
+ Scsi_Request *my_cmdp; /* != 0 when request with lower levels */
+ struct sg_request *nextrp; /* NULL -> tail request (slist) */
+ struct sg_fd *parentfp; /* NULL -> not in use */
+ Sg_scatter_hold data; /* hold buffer, perhaps scatter list */
+ sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */
+ unsigned char sense_b[sizeof (dummy_cmdp->sr_sense_buffer)];
+ char res_used; /* 1 -> using reserve buffer, 0 -> not ... */
+ char orphan; /* 1 -> drop on sight, 0 -> normal */
+ char sg_io_owned; /* 1 -> packet belongs to SG_IO */
+ volatile char done; /* 0->before bh, 1->before read, 2->read */
+} Sg_request;
+
+typedef struct sg_fd { /* holds the state of a file descriptor */
+ struct sg_fd *nextfp; /* NULL when last opened fd on this device */
+ struct sg_device *parentdp; /* owning device */
+ wait_queue_head_t read_wait; /* queue read until command done */
+ rwlock_t rq_list_lock; /* protect access to list in req_arr */
+ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */
+ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */
+ Sg_scatter_hold reserve; /* buffer held for this file descriptor */
+ unsigned save_scat_len; /* original length of trunc. scat. element */
+ Sg_request *headrp; /* head of request slist, NULL->empty */
+ struct fasync_struct *async_qp; /* used by asynchronous notification */
+ Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
+ char low_dma; /* as in parent but possibly overridden to 1 */
+ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */
+ volatile char closed; /* 1 -> fd closed but request(s) outstanding */
+ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */
+ char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */
+ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */
+ char mmap_called; /* 0 -> mmap() never called on this fd */
+} Sg_fd;
+
+typedef struct sg_device { /* holds the state of each scsi generic device */
+ struct scsi_device *device;
+ wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
+ int sg_tablesize; /* adapter's max scatter-gather table size */
+ Sg_fd *headfp; /* first open fd belonging to this device */
+ volatile char detached; /* 0->attached, 1->detached pending removal */
+ volatile char exclude; /* opened for exclusive access */
+ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
+ struct gendisk *disk;
+ struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg<n>] */
+} Sg_device;
+
+static int sg_fasync(int fd, struct file *filp, int mode);
+static void sg_cmd_done(Scsi_Cmnd * SCpnt); /* tasklet or soft irq callback */
+static int sg_start_req(Sg_request * srp);
+static void sg_finish_rem_req(Sg_request * srp);
+static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
+static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp,
+ int tablesize);
+static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
+ Sg_request * srp);
+static ssize_t sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,
+ int blocking, int read_only, Sg_request ** o_srp);
+static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
+ unsigned char *cmnd, int timeout, int blocking);
+static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,
+ int wr_xf, int *countp, unsigned char __user **up);
+static int sg_write_xfer(Sg_request * srp);
+static int sg_read_xfer(Sg_request * srp);
+static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer);
+static void sg_remove_scat(Sg_scatter_hold * schp);
+static void sg_build_reserve(Sg_fd * sfp, int req_size);
+static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size);
+static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp);
+static char *sg_page_malloc(int rqSz, int lowDma, int *retSzp);
+static void sg_page_free(char *buff, int size);
+static Sg_fd *sg_add_sfp(Sg_device * sdp, int dev);
+static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
+static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
+static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id);
+static Sg_request *sg_add_request(Sg_fd * sfp);
+static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
+static int sg_res_in_use(Sg_fd * sfp);
+static int sg_allow_access(unsigned char opcode, char dev_type);
+static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len);
+static Sg_device *sg_get_dev(int dev);
+static inline unsigned char *sg_scatg2virt(const struct scatterlist *sclp);
+#ifdef CONFIG_SCSI_PROC_FS
+static int sg_last_dev(void);
+#endif
+
+static Sg_device **sg_dev_arr = NULL;
+static int sg_dev_max;
+static int sg_nr_dev;
+
+#define SZ_SG_HEADER sizeof(struct sg_header)
+#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t)
+#define SZ_SG_IOVEC sizeof(sg_iovec_t)
+#define SZ_SG_REQ_INFO sizeof(sg_req_info_t)
+
+static int
+sg_open(struct inode *inode, struct file *filp)
+{
+ int dev = iminor(inode);
+ int flags = filp->f_flags;
+ Sg_device *sdp;
+ Sg_fd *sfp;
+ int res;
+ int retval;
+
+ nonseekable_open(inode, filp);
+ SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
+ sdp = sg_get_dev(dev);
+ if ((!sdp) || (!sdp->device))
+ return -ENXIO;
+ if (sdp->detached)
+ return -ENODEV;
+
+ /* This driver's module count bumped by fops_get in <linux/fs.h> */
+ /* Prevent the device driver from vanishing while we sleep */
+ retval = scsi_device_get(sdp->device);
+ if (retval)
+ return retval;
+
+ if (!((flags & O_NONBLOCK) ||
+ scsi_block_when_processing_errors(sdp->device))) {
+ retval = -ENXIO;
+ /* we are in error recovery for this device */
+ goto error_out;
+ }
+
+ if (flags & O_EXCL) {
+ if (O_RDONLY == (flags & O_ACCMODE)) {
+ retval = -EPERM; /* Can't lock it with read only access */
+ goto error_out;
+ }
+ if (sdp->headfp && (flags & O_NONBLOCK)) {
+ retval = -EBUSY;
+ goto error_out;
+ }
+ res = 0;
+ __wait_event_interruptible(sdp->o_excl_wait,
+ ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), res);
+ if (res) {
+ retval = res; /* -ERESTARTSYS because signal hit process */
+ goto error_out;
+ }
+ } else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */
+ if (flags & O_NONBLOCK) {
+ retval = -EBUSY;
+ goto error_out;
+ }
+ res = 0;
+ __wait_event_interruptible(sdp->o_excl_wait, (!sdp->exclude),
+ res);
+ if (res) {
+ retval = res; /* -ERESTARTSYS because signal hit process */
+ goto error_out;
+ }
+ }
+ if (sdp->detached) {
+ retval = -ENODEV;
+ goto error_out;
+ }
+ if (!sdp->headfp) { /* no existing opens on this device */
+ sdp->sgdebug = 0;
+ sdp->sg_tablesize = sdp->device->host->sg_tablesize;
+ }
+ if ((sfp = sg_add_sfp(sdp, dev)))
+ filp->private_data = sfp;
+ else {
+ if (flags & O_EXCL)
+ sdp->exclude = 0; /* undo if error */
+ retval = -ENOMEM;
+ goto error_out;
+ }
+ return 0;
+
+ error_out:
+ scsi_device_put(sdp->device);
+ return retval;
+}
+
+/* Following function was formerly called 'sg_close' */
+static int
+sg_release(struct inode *inode, struct file *filp)
+{
+ Sg_device *sdp;
+ Sg_fd *sfp;
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+ return -ENXIO;
+ SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name));
+ sg_fasync(-1, filp, 0); /* remove filp from async notification list */
+ if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */
+ if (!sdp->detached) {
+ scsi_device_put(sdp->device);
+ }
+ sdp->exclude = 0;
+ wake_up_interruptible(&sdp->o_excl_wait);
+ }
+ return 0;
+}
+
+static ssize_t
+sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
+{
+ int res;
+ Sg_device *sdp;
+ Sg_fd *sfp;
+ Sg_request *srp;
+ int req_pack_id = -1;
+ struct sg_header old_hdr;
+ sg_io_hdr_t new_hdr;
+ sg_io_hdr_t *hp;
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+ return -ENXIO;
+ SCSI_LOG_TIMEOUT(3, printk("sg_read: %s, count=%d\n",
+ sdp->disk->disk_name, (int) count));
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+ if (sfp->force_packid && (count >= SZ_SG_HEADER)) {
+ if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER))
+ return -EFAULT;
+ if (old_hdr.reply_len < 0) {
+ if (count >= SZ_SG_IO_HDR) {
+ if (__copy_from_user
+ (&new_hdr, buf, SZ_SG_IO_HDR))
+ return -EFAULT;
+ req_pack_id = new_hdr.pack_id;
+ }
+ } else
+ req_pack_id = old_hdr.pack_id;
+ }
+ srp = sg_get_rq_mark(sfp, req_pack_id);
+ if (!srp) { /* now wait on packet to arrive */
+ if (sdp->detached)
+ return -ENODEV;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ while (1) {
+ res = 0; /* following is a macro that beats race condition */
+ __wait_event_interruptible(sfp->read_wait,
+ (sdp->detached || (srp = sg_get_rq_mark(sfp, req_pack_id))),
+ res);
+ if (sdp->detached)
+ return -ENODEV;
+ if (0 == res)
+ break;
+ return res; /* -ERESTARTSYS because signal hit process */
+ }
+ }
+ if (srp->header.interface_id != '\0')
+ return sg_new_read(sfp, buf, count, srp);
+
+ hp = &srp->header;
+ memset(&old_hdr, 0, SZ_SG_HEADER);
+ old_hdr.reply_len = (int) hp->timeout;
+ old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */
+ old_hdr.pack_id = hp->pack_id;
+ old_hdr.twelve_byte =
+ ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0;
+ old_hdr.target_status = hp->masked_status;
+ old_hdr.host_status = hp->host_status;
+ old_hdr.driver_status = hp->driver_status;
+ if ((CHECK_CONDITION & hp->masked_status) ||
+ (DRIVER_SENSE & hp->driver_status))
+ memcpy(old_hdr.sense_buffer, srp->sense_b,
+ sizeof (old_hdr.sense_buffer));
+ switch (hp->host_status) {
+ /* This setup of 'result' is for backward compatibility and is best
+ ignored by the user who should use target, host + driver status */
+ case DID_OK:
+ case DID_PASSTHROUGH:
+ case DID_SOFT_ERROR:
+ old_hdr.result = 0;
+ break;
+ case DID_NO_CONNECT:
+ case DID_BUS_BUSY:
+ case DID_TIME_OUT:
+ old_hdr.result = EBUSY;
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ case DID_PARITY:
+ case DID_RESET:
+ case DID_BAD_INTR:
+ old_hdr.result = EIO;
+ break;
+ case DID_ERROR:
+ old_hdr.result = (srp->sense_b[0] == 0 &&
+ hp->masked_status == GOOD) ? 0 : EIO;
+ break;
+ default:
+ old_hdr.result = EIO;
+ break;
+ }
+
+ /* Now copy the result back to the user buffer. */
+ if (count >= SZ_SG_HEADER) {
+ if (__copy_to_user(buf, &old_hdr, SZ_SG_HEADER))
+ return -EFAULT;
+ buf += SZ_SG_HEADER;
+ if (count > old_hdr.reply_len)
+ count = old_hdr.reply_len;
+ if (count > SZ_SG_HEADER) {
+ if ((res =
+ sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)))
+ return -EFAULT;
+ }
+ } else
+ count = (old_hdr.result == 0) ? 0 : -EIO;
+ sg_finish_rem_req(srp);
+ return count;
+}
+
+static ssize_t
+sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp)
+{
+ sg_io_hdr_t *hp = &srp->header;
+ int err = 0;
+ int len;
+
+ if (count < SZ_SG_IO_HDR) {
+ err = -EINVAL;
+ goto err_out;
+ }
+ hp->sb_len_wr = 0;
+ if ((hp->mx_sb_len > 0) && hp->sbp) {
+ if ((CHECK_CONDITION & hp->masked_status) ||
+ (DRIVER_SENSE & hp->driver_status)) {
+ int sb_len = sizeof (dummy_cmdp->sr_sense_buffer);
+ sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len;
+ len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */
+ len = (len > sb_len) ? sb_len : len;
+ if (copy_to_user(hp->sbp, srp->sense_b, len)) {
+ err = -EFAULT;
+ goto err_out;
+ }
+ hp->sb_len_wr = len;
+ }
+ }
+ if (hp->masked_status || hp->host_status || hp->driver_status)
+ hp->info |= SG_INFO_CHECK;
+ if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) {
+ err = -EFAULT;
+ goto err_out;
+ }
+ err = sg_read_xfer(srp);
+ err_out:
+ sg_finish_rem_req(srp);
+ return (0 == err) ? count : err;
+}
+
+static ssize_t
+sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
+{
+ int mxsize, cmd_size, k;
+ int input_size, blocking;
+ unsigned char opcode;
+ Sg_device *sdp;
+ Sg_fd *sfp;
+ Sg_request *srp;
+ struct sg_header old_hdr;
+ sg_io_hdr_t *hp;
+ unsigned char cmnd[sizeof (dummy_cmdp->sr_cmnd)];
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+ return -ENXIO;
+ SCSI_LOG_TIMEOUT(3, printk("sg_write: %s, count=%d\n",
+ sdp->disk->disk_name, (int) count));
+ if (sdp->detached)
+ return -ENODEV;
+ if (!((filp->f_flags & O_NONBLOCK) ||
+ scsi_block_when_processing_errors(sdp->device)))
+ return -ENXIO;
+
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT; /* protects following copy_from_user()s + get_user()s */
+ if (count < SZ_SG_HEADER)
+ return -EIO;
+ if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER))
+ return -EFAULT;
+ blocking = !(filp->f_flags & O_NONBLOCK);
+ if (old_hdr.reply_len < 0)
+ return sg_new_write(sfp, buf, count, blocking, 0, NULL);
+ if (count < (SZ_SG_HEADER + 6))
+ return -EIO; /* The minimum scsi command length is 6 bytes. */
+
+ if (!(srp = sg_add_request(sfp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));
+ return -EDOM;
+ }
+ buf += SZ_SG_HEADER;
+ __get_user(opcode, buf);
+ if (sfp->next_cmd_len > 0) {
+ if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));
+ sfp->next_cmd_len = 0;
+ sg_remove_request(sfp, srp);
+ return -EIO;
+ }
+ cmd_size = sfp->next_cmd_len;
+ sfp->next_cmd_len = 0; /* reset so only this write() effected */
+ } else {
+ cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */
+ if ((opcode >= 0xc0) && old_hdr.twelve_byte)
+ cmd_size = 12;
+ }
+ SCSI_LOG_TIMEOUT(4, printk(
+ "sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size));
+/* Determine buffer size. */
+ input_size = count - cmd_size;
+ mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len;
+ mxsize -= SZ_SG_HEADER;
+ input_size -= SZ_SG_HEADER;
+ if (input_size < 0) {
+ sg_remove_request(sfp, srp);
+ return -EIO; /* User did not pass enough bytes for this command. */
+ }
+ hp = &srp->header;
+ hp->interface_id = '\0'; /* indicator of old interface tunnelled */
+ hp->cmd_len = (unsigned char) cmd_size;
+ hp->iovec_count = 0;
+ hp->mx_sb_len = 0;
+ if (input_size > 0)
+ hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ?
+ SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV;
+ else
+ hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE;
+ hp->dxfer_len = mxsize;
+ hp->dxferp = (char __user *)buf + cmd_size;
+ hp->sbp = NULL;
+ hp->timeout = old_hdr.reply_len; /* structure abuse ... */
+ hp->flags = input_size; /* structure abuse ... */
+ hp->pack_id = old_hdr.pack_id;
+ hp->usr_ptr = NULL;
+ if (__copy_from_user(cmnd, buf, cmd_size))
+ return -EFAULT;
+ /*
+ * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV,
+ * but is is possible that the app intended SG_DXFER_TO_DEV, because there
+ * is a non-zero input_size, so emit a warning.
+ */
+ if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV)
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "sg_write: data in/out %d/%d bytes for SCSI command 0x%x--"
+ "guessing data in;\n" KERN_WARNING " "
+ "program %s not setting count and/or reply_len properly\n",
+ old_hdr.reply_len - (int)SZ_SG_HEADER,
+ input_size, (unsigned int) cmnd[0],
+ current->comm);
+ k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking);
+ return (k < 0) ? k : count;
+}
+
+static ssize_t
+sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,
+ int blocking, int read_only, Sg_request ** o_srp)
+{
+ int k;
+ Sg_request *srp;
+ sg_io_hdr_t *hp;
+ unsigned char cmnd[sizeof (dummy_cmdp->sr_cmnd)];
+ int timeout;
+ unsigned long ul_timeout;
+
+ if (count < SZ_SG_IO_HDR)
+ return -EINVAL;
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT; /* protects following copy_from_user()s + get_user()s */
+
+ sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */
+ if (!(srp = sg_add_request(sfp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n"));
+ return -EDOM;
+ }
+ hp = &srp->header;
+ if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) {
+ sg_remove_request(sfp, srp);
+ return -EFAULT;
+ }
+ if (hp->interface_id != 'S') {
+ sg_remove_request(sfp, srp);
+ return -ENOSYS;
+ }
+ if (hp->flags & SG_FLAG_MMAP_IO) {
+ if (hp->dxfer_len > sfp->reserve.bufflen) {
+ sg_remove_request(sfp, srp);
+ return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */
+ }
+ if (hp->flags & SG_FLAG_DIRECT_IO) {
+ sg_remove_request(sfp, srp);
+ return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */
+ }
+ if (sg_res_in_use(sfp)) {
+ sg_remove_request(sfp, srp);
+ return -EBUSY; /* reserve buffer already being used */
+ }
+ }
+ ul_timeout = msecs_to_jiffies(srp->header.timeout);
+ timeout = (ul_timeout < INT_MAX) ? ul_timeout : INT_MAX;
+ if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) {
+ sg_remove_request(sfp, srp);
+ return -EMSGSIZE;
+ }
+ if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len)) {
+ sg_remove_request(sfp, srp);
+ return -EFAULT; /* protects following copy_from_user()s + get_user()s */
+ }
+ if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) {
+ sg_remove_request(sfp, srp);
+ return -EFAULT;
+ }
+ if (read_only &&
+ (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) {
+ sg_remove_request(sfp, srp);
+ return -EPERM;
+ }
+ k = sg_common_write(sfp, srp, cmnd, timeout, blocking);
+ if (k < 0)
+ return k;
+ if (o_srp)
+ *o_srp = srp;
+ return count;
+}
+
+static int
+sg_common_write(Sg_fd * sfp, Sg_request * srp,
+ unsigned char *cmnd, int timeout, int blocking)
+{
+ int k;
+ Scsi_Request *SRpnt;
+ Sg_device *sdp = sfp->parentdp;
+ sg_io_hdr_t *hp = &srp->header;
+ request_queue_t *q;
+
+ srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */
+ hp->status = 0;
+ hp->masked_status = 0;
+ hp->msg_status = 0;
+ hp->info = 0;
+ hp->host_status = 0;
+ hp->driver_status = 0;
+ hp->resid = 0;
+ SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n",
+ (int) cmnd[0], (int) hp->cmd_len));
+
+ if ((k = sg_start_req(srp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: start_req err=%d\n", k));
+ sg_finish_rem_req(srp);
+ return k; /* probably out of space --> ENOMEM */
+ }
+ if ((k = sg_write_xfer(srp))) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: write_xfer, bad address\n"));
+ sg_finish_rem_req(srp);
+ return k;
+ }
+ if (sdp->detached) {
+ sg_finish_rem_req(srp);
+ return -ENODEV;
+ }
+ SRpnt = scsi_allocate_request(sdp->device, GFP_ATOMIC);
+ if (SRpnt == NULL) {
+ SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n"));
+ sg_finish_rem_req(srp);
+ return -ENOMEM;
+ }
+
+ srp->my_cmdp = SRpnt;
+ q = SRpnt->sr_device->request_queue;
+ SRpnt->sr_request->rq_disk = sdp->disk;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_cmd_len = hp->cmd_len;
+ SRpnt->sr_use_sg = srp->data.k_use_sg;
+ SRpnt->sr_sglist_len = srp->data.sglist_len;
+ SRpnt->sr_bufflen = srp->data.bufflen;
+ SRpnt->sr_underflow = 0;
+ SRpnt->sr_buffer = srp->data.buffer;
+ switch (hp->dxfer_direction) {
+ case SG_DXFER_TO_FROM_DEV:
+ case SG_DXFER_FROM_DEV:
+ SRpnt->sr_data_direction = SCSI_DATA_READ;
+ break;
+ case SG_DXFER_TO_DEV:
+ SRpnt->sr_data_direction = SCSI_DATA_WRITE;
+ break;
+ case SG_DXFER_UNKNOWN:
+ SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN;
+ break;
+ default:
+ SRpnt->sr_data_direction = SCSI_DATA_NONE;
+ break;
+ }
+ SRpnt->upper_private_data = srp;
+ srp->data.k_use_sg = 0;
+ srp->data.sglist_len = 0;
+ srp->data.bufflen = 0;
+ srp->data.buffer = NULL;
+ hp->duration = jiffies; /* unit jiffies now, millisecs after done */
+/* Now send everything of to mid-level. The next time we hear about this
+ packet is when sg_cmd_done() is called (i.e. a callback). */
+ scsi_do_req(SRpnt, (void *) cmnd,
+ (void *) SRpnt->sr_buffer, hp->dxfer_len,
+ sg_cmd_done, timeout, SG_DEFAULT_RETRIES);
+ /* dxfer_len overwrites SRpnt->sr_bufflen, hence need for b_malloc_len */
+ return 0;
+}
+
+static int
+sg_srp_done(Sg_request *srp, Sg_fd *sfp)
+{
+ unsigned long iflags;
+ int done;
+
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ done = srp->done;
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return done;
+}
+
+static int
+sg_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd_in, unsigned long arg)
+{
+ void __user *p = (void __user *)arg;
+ int __user *ip = p;
+ int result, val, read_only;
+ Sg_device *sdp;
+ Sg_fd *sfp;
+ Sg_request *srp;
+ unsigned long iflags;
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+ return -ENXIO;
+ SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: %s, cmd=0x%x\n",
+ sdp->disk->disk_name, (int) cmd_in));
+ read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
+
+ switch (cmd_in) {
+ case SG_IO:
+ {
+ int blocking = 1; /* ignore O_NONBLOCK flag */
+
+ if (sdp->detached)
+ return -ENODEV;
+ if (!scsi_block_when_processing_errors(sdp->device))
+ return -ENXIO;
+ if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR))
+ return -EFAULT;
+ result =
+ sg_new_write(sfp, p, SZ_SG_IO_HDR,
+ blocking, read_only, &srp);
+ if (result < 0)
+ return result;
+ srp->sg_io_owned = 1;
+ while (1) {
+ result = 0; /* following macro to beat race condition */
+ __wait_event_interruptible(sfp->read_wait,
+ (sdp->detached || sfp->closed || sg_srp_done(srp, sfp)),
+ result);
+ if (sdp->detached)
+ return -ENODEV;
+ if (sfp->closed)
+ return 0; /* request packet dropped already */
+ if (0 == result)
+ break;
+ srp->orphan = 1;
+ return result; /* -ERESTARTSYS because signal hit process */
+ }
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ srp->done = 2;
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);
+ return (result < 0) ? result : 0;
+ }
+ case SG_SET_TIMEOUT:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ if (val < 0)
+ return -EIO;
+ if (val >= MULDIV (INT_MAX, USER_HZ, HZ))
+ val = MULDIV (INT_MAX, USER_HZ, HZ);
+ sfp->timeout_user = val;
+ sfp->timeout = MULDIV (val, HZ, USER_HZ);
+
+ return 0;
+ case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */
+ /* strange ..., for backward compatibility */
+ return sfp->timeout_user;
+ case SG_SET_FORCE_LOW_DMA:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ if (val) {
+ sfp->low_dma = 1;
+ if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {
+ val = (int) sfp->reserve.bufflen;
+ sg_remove_scat(&sfp->reserve);
+ sg_build_reserve(sfp, val);
+ }
+ } else {
+ if (sdp->detached)
+ return -ENODEV;
+ sfp->low_dma = sdp->device->host->unchecked_isa_dma;
+ }
+ return 0;
+ case SG_GET_LOW_DMA:
+ return put_user((int) sfp->low_dma, ip);
+ case SG_GET_SCSI_ID:
+ if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t)))
+ return -EFAULT;
+ else {
+ sg_scsi_id_t __user *sg_idp = p;
+
+ if (sdp->detached)
+ return -ENODEV;
+ __put_user((int) sdp->device->host->host_no,
+ &sg_idp->host_no);
+ __put_user((int) sdp->device->channel,
+ &sg_idp->channel);
+ __put_user((int) sdp->device->id, &sg_idp->scsi_id);
+ __put_user((int) sdp->device->lun, &sg_idp->lun);
+ __put_user((int) sdp->device->type, &sg_idp->scsi_type);
+ __put_user((short) sdp->device->host->cmd_per_lun,
+ &sg_idp->h_cmd_per_lun);
+ __put_user((short) sdp->device->queue_depth,
+ &sg_idp->d_queue_depth);
+ __put_user(0, &sg_idp->unused[0]);
+ __put_user(0, &sg_idp->unused[1]);
+ return 0;
+ }
+ case SG_SET_FORCE_PACK_ID:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ sfp->force_packid = val ? 1 : 0;
+ return 0;
+ case SG_GET_PACK_ID:
+ if (!access_ok(VERIFY_WRITE, ip, sizeof (int)))
+ return -EFAULT;
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (srp = sfp->headrp; srp; srp = srp->nextrp) {
+ if ((1 == srp->done) && (!srp->sg_io_owned)) {
+ read_unlock_irqrestore(&sfp->rq_list_lock,
+ iflags);
+ __put_user(srp->header.pack_id, ip);
+ return 0;
+ }
+ }
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ __put_user(-1, ip);
+ return 0;
+ case SG_GET_NUM_WAITING:
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) {
+ if ((1 == srp->done) && (!srp->sg_io_owned))
+ ++val;
+ }
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return put_user(val, ip);
+ case SG_GET_SG_TABLESIZE:
+ return put_user(sdp->sg_tablesize, ip);
+ case SG_SET_RESERVED_SIZE:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ if (val < 0)
+ return -EINVAL;
+ if (val != sfp->reserve.bufflen) {
+ if (sg_res_in_use(sfp) || sfp->mmap_called)
+ return -EBUSY;
+ sg_remove_scat(&sfp->reserve);
+ sg_build_reserve(sfp, val);
+ }
+ return 0;
+ case SG_GET_RESERVED_SIZE:
+ val = (int) sfp->reserve.bufflen;
+ return put_user(val, ip);
+ case SG_SET_COMMAND_Q:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ sfp->cmd_q = val ? 1 : 0;
+ return 0;
+ case SG_GET_COMMAND_Q:
+ return put_user((int) sfp->cmd_q, ip);
+ case SG_SET_KEEP_ORPHAN:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ sfp->keep_orphan = val;
+ return 0;
+ case SG_GET_KEEP_ORPHAN:
+ return put_user((int) sfp->keep_orphan, ip);
+ case SG_NEXT_CMD_LEN:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ sfp->next_cmd_len = (val > 0) ? val : 0;
+ return 0;
+ case SG_GET_VERSION_NUM:
+ return put_user(sg_version_num, ip);
+ case SG_GET_ACCESS_COUNT:
+ /* faked - we don't have a real access count anymore */
+ val = (sdp->device ? 1 : 0);
+ return put_user(val, ip);
+ case SG_GET_REQUEST_TABLE:
+ if (!access_ok(VERIFY_WRITE, p, SZ_SG_REQ_INFO * SG_MAX_QUEUE))
+ return -EFAULT;
+ else {
+ sg_req_info_t rinfo[SG_MAX_QUEUE];
+ Sg_request *srp;
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE;
+ ++val, srp = srp ? srp->nextrp : srp) {
+ memset(&rinfo[val], 0, SZ_SG_REQ_INFO);
+ if (srp) {
+ rinfo[val].req_state = srp->done + 1;
+ rinfo[val].problem =
+ srp->header.masked_status &
+ srp->header.host_status &
+ srp->header.driver_status;
+ rinfo[val].duration =
+ srp->done ? srp->header.duration :
+ jiffies_to_msecs(
+ jiffies - srp->header.duration);
+ rinfo[val].orphan = srp->orphan;
+ rinfo[val].sg_io_owned = srp->sg_io_owned;
+ rinfo[val].pack_id = srp->header.pack_id;
+ rinfo[val].usr_ptr = srp->header.usr_ptr;
+ }
+ }
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return (__copy_to_user(p, rinfo,
+ SZ_SG_REQ_INFO * SG_MAX_QUEUE) ? -EFAULT : 0);
+ }
+ case SG_EMULATED_HOST:
+ if (sdp->detached)
+ return -ENODEV;
+ return put_user(sdp->device->host->hostt->emulated, ip);
+ case SG_SCSI_RESET:
+ if (sdp->detached)
+ return -ENODEV;
+ if (filp->f_flags & O_NONBLOCK) {
+ if (test_bit(SHOST_RECOVERY,
+ &sdp->device->host->shost_state))
+ return -EBUSY;
+ } else if (!scsi_block_when_processing_errors(sdp->device))
+ return -EBUSY;
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ if (SG_SCSI_RESET_NOTHING == val)
+ return 0;
+ switch (val) {
+ case SG_SCSI_RESET_DEVICE:
+ val = SCSI_TRY_RESET_DEVICE;
+ break;
+ case SG_SCSI_RESET_BUS:
+ val = SCSI_TRY_RESET_BUS;
+ break;
+ case SG_SCSI_RESET_HOST:
+ val = SCSI_TRY_RESET_HOST;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return (scsi_reset_provider(sdp->device, val) ==
+ SUCCESS) ? 0 : -EIO;
+ case SCSI_IOCTL_SEND_COMMAND:
+ if (sdp->detached)
+ return -ENODEV;
+ if (read_only) {
+ unsigned char opcode = WRITE_6;
+ Scsi_Ioctl_Command __user *siocp = p;
+
+ if (copy_from_user(&opcode, siocp->data, 1))
+ return -EFAULT;
+ if (!sg_allow_access(opcode, sdp->device->type))
+ return -EPERM;
+ }
+ return scsi_ioctl_send_command(sdp->device, p);
+ case SG_SET_DEBUG:
+ result = get_user(val, ip);
+ if (result)
+ return result;
+ sdp->sgdebug = (char) val;
+ return 0;
+ case SCSI_IOCTL_GET_IDLUN:
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ case SCSI_IOCTL_PROBE_HOST:
+ case SG_GET_TRANSFORM:
+ if (sdp->detached)
+ return -ENODEV;
+ return scsi_ioctl(sdp->device, cmd_in, p);
+ default:
+ if (read_only)
+ return -EPERM; /* don't know so take safe approach */
+ return scsi_ioctl(sdp->device, cmd_in, p);
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
+{
+ Sg_device *sdp;
+ Sg_fd *sfp;
+ struct scsi_device *sdev;
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+ return -ENXIO;
+
+ sdev = sdp->device;
+ if (sdev->host->hostt->compat_ioctl) {
+ int ret;
+
+ ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
+
+ return ret;
+ }
+
+ return -ENOIOCTLCMD;
+}
+#endif
+
+static unsigned int
+sg_poll(struct file *filp, poll_table * wait)
+{
+ unsigned int res = 0;
+ Sg_device *sdp;
+ Sg_fd *sfp;
+ Sg_request *srp;
+ int count = 0;
+ unsigned long iflags;
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))
+ || sfp->closed)
+ return POLLERR;
+ poll_wait(filp, &sfp->read_wait, wait);
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (srp = sfp->headrp; srp; srp = srp->nextrp) {
+ /* if any read waiting, flag it */
+ if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned))
+ res = POLLIN | POLLRDNORM;
+ ++count;
+ }
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+
+ if (sdp->detached)
+ res |= POLLHUP;
+ else if (!sfp->cmd_q) {
+ if (0 == count)
+ res |= POLLOUT | POLLWRNORM;
+ } else if (count < SG_MAX_QUEUE)
+ res |= POLLOUT | POLLWRNORM;
+ SCSI_LOG_TIMEOUT(3, printk("sg_poll: %s, res=0x%x\n",
+ sdp->disk->disk_name, (int) res));
+ return res;
+}
+
+static int
+sg_fasync(int fd, struct file *filp, int mode)
+{
+ int retval;
+ Sg_device *sdp;
+ Sg_fd *sfp;
+
+ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
+ return -ENXIO;
+ SCSI_LOG_TIMEOUT(3, printk("sg_fasync: %s, mode=%d\n",
+ sdp->disk->disk_name, mode));
+
+ retval = fasync_helper(fd, filp, mode, &sfp->async_qp);
+ return (retval < 0) ? retval : 0;
+}
+
+static inline unsigned char *
+sg_scatg2virt(const struct scatterlist *sclp)
+{
+ return (sclp && sclp->page) ?
+ (unsigned char *) page_address(sclp->page) + sclp->offset : NULL;
+}
+
+/* When startFinish==1 increments page counts for pages other than the
+ first of scatter gather elements obtained from __get_free_pages().
+ When startFinish==0 decrements ... */
+static void
+sg_rb_correct4mmap(Sg_scatter_hold * rsv_schp, int startFinish)
+{
+ void *page_ptr;
+ struct page *page;
+ int k, m;
+
+ SCSI_LOG_TIMEOUT(3, printk("sg_rb_correct4mmap: startFinish=%d, scatg=%d\n",
+ startFinish, rsv_schp->k_use_sg));
+ /* N.B. correction _not_ applied to base page of each allocation */
+ if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */
+ struct scatterlist *sclp = rsv_schp->buffer;
+
+ for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) {
+ for (m = PAGE_SIZE; m < sclp->length; m += PAGE_SIZE) {
+ page_ptr = sg_scatg2virt(sclp) + m;
+ page = virt_to_page(page_ptr);
+ if (startFinish)
+ get_page(page);
+ else {
+ if (page_count(page) > 0)
+ __put_page(page);
+ }
+ }
+ }
+ } else { /* reserve buffer is just a single allocation */
+ for (m = PAGE_SIZE; m < rsv_schp->bufflen; m += PAGE_SIZE) {
+ page_ptr = (unsigned char *) rsv_schp->buffer + m;
+ page = virt_to_page(page_ptr);
+ if (startFinish)
+ get_page(page);
+ else {
+ if (page_count(page) > 0)
+ __put_page(page);
+ }
+ }
+ }
+}
+
+static struct page *
+sg_vma_nopage(struct vm_area_struct *vma, unsigned long addr, int *type)
+{
+ Sg_fd *sfp;
+ struct page *page = NOPAGE_SIGBUS;
+ void *page_ptr = NULL;
+ unsigned long offset;
+ Sg_scatter_hold *rsv_schp;
+
+ if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data)))
+ return page;
+ rsv_schp = &sfp->reserve;
+ offset = addr - vma->vm_start;
+ if (offset >= rsv_schp->bufflen)
+ return page;
+ SCSI_LOG_TIMEOUT(3, printk("sg_vma_nopage: offset=%lu, scatg=%d\n",
+ offset, rsv_schp->k_use_sg));
+ if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */
+ int k;
+ unsigned long sa = vma->vm_start;
+ unsigned long len;
+ struct scatterlist *sclp = rsv_schp->buffer;
+
+ for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end);
+ ++k, ++sclp) {
+ len = vma->vm_end - sa;
+ len = (len < sclp->length) ? len : sclp->length;
+ if (offset < len) {
+ page_ptr = sg_scatg2virt(sclp) + offset;
+ page = virt_to_page(page_ptr);
+ get_page(page); /* increment page count */
+ break;
+ }
+ sa += len;
+ offset -= len;
+ }
+ } else { /* reserve buffer is just a single allocation */
+ page_ptr = (unsigned char *) rsv_schp->buffer + offset;
+ page = virt_to_page(page_ptr);
+ get_page(page); /* increment page count */
+ }
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return page;
+}
+
+static struct vm_operations_struct sg_mmap_vm_ops = {
+ .nopage = sg_vma_nopage,
+};
+
+static int
+sg_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ Sg_fd *sfp;
+ unsigned long req_sz = vma->vm_end - vma->vm_start;
+ Sg_scatter_hold *rsv_schp;
+
+ if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data)))
+ return -ENXIO;
+ SCSI_LOG_TIMEOUT(3, printk("sg_mmap starting, vm_start=%p, len=%d\n",
+ (void *) vma->vm_start, (int) req_sz));
+ if (vma->vm_pgoff)
+ return -EINVAL; /* want no offset */
+ rsv_schp = &sfp->reserve;
+ if (req_sz > rsv_schp->bufflen)
+ return -ENOMEM; /* cannot map more than reserved buffer */
+
+ if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */
+ int k;
+ unsigned long sa = vma->vm_start;
+ unsigned long len;
+ struct scatterlist *sclp = rsv_schp->buffer;
+
+ for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end);
+ ++k, ++sclp) {
+ if (0 != sclp->offset)
+ return -EFAULT; /* non page aligned memory ?? */
+ len = vma->vm_end - sa;
+ len = (len < sclp->length) ? len : sclp->length;
+ sa += len;
+ }
+ } else { /* reserve buffer is just a single allocation */
+ if ((unsigned long) rsv_schp->buffer & (PAGE_SIZE - 1))
+ return -EFAULT; /* non page aligned memory ?? */
+ }
+ if (0 == sfp->mmap_called) {
+ sg_rb_correct4mmap(rsv_schp, 1); /* do only once per fd lifetime */
+ sfp->mmap_called = 1;
+ }
+ vma->vm_flags |= (VM_RESERVED | VM_IO);
+ vma->vm_private_data = sfp;
+ vma->vm_ops = &sg_mmap_vm_ops;
+ return 0;
+}
+
+/* This function is a "bottom half" handler that is called by the
+ * mid level when a command is completed (or has failed). */
+static void
+sg_cmd_done(Scsi_Cmnd * SCpnt)
+{
+ Scsi_Request *SRpnt = NULL;
+ Sg_device *sdp = NULL;
+ Sg_fd *sfp;
+ Sg_request *srp = NULL;
+ unsigned long iflags;
+
+ if (SCpnt && (SRpnt = SCpnt->sc_request))
+ srp = (Sg_request *) SRpnt->upper_private_data;
+ if (NULL == srp) {
+ printk(KERN_ERR "sg_cmd_done: NULL request\n");
+ if (SRpnt)
+ scsi_release_request(SRpnt);
+ return;
+ }
+ sfp = srp->parentfp;
+ if (sfp)
+ sdp = sfp->parentdp;
+ if ((NULL == sdp) || sdp->detached) {
+ printk(KERN_INFO "sg_cmd_done: device detached\n");
+ scsi_release_request(SRpnt);
+ return;
+ }
+
+ /* First transfer ownership of data buffers to sg_device object. */
+ srp->data.k_use_sg = SRpnt->sr_use_sg;
+ srp->data.sglist_len = SRpnt->sr_sglist_len;
+ srp->data.bufflen = SRpnt->sr_bufflen;
+ srp->data.buffer = SRpnt->sr_buffer;
+ /* now clear out request structure */
+ SRpnt->sr_use_sg = 0;
+ SRpnt->sr_sglist_len = 0;
+ SRpnt->sr_bufflen = 0;
+ SRpnt->sr_buffer = NULL;
+ SRpnt->sr_underflow = 0;
+ SRpnt->sr_request->rq_disk = NULL; /* "sg" _disowns_ request blk */
+
+ srp->my_cmdp = NULL;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n",
+ sdp->disk->disk_name, srp->header.pack_id, (int) SRpnt->sr_result));
+ srp->header.resid = SCpnt->resid;
+ /* N.B. unit of duration changes here from jiffies to millisecs */
+ srp->header.duration =
+ jiffies_to_msecs(jiffies - srp->header.duration);
+ if (0 != SRpnt->sr_result) {
+ struct scsi_sense_hdr sshdr;
+
+ memcpy(srp->sense_b, SRpnt->sr_sense_buffer,
+ sizeof (srp->sense_b));
+ srp->header.status = 0xff & SRpnt->sr_result;
+ srp->header.masked_status = status_byte(SRpnt->sr_result);
+ srp->header.msg_status = msg_byte(SRpnt->sr_result);
+ srp->header.host_status = host_byte(SRpnt->sr_result);
+ srp->header.driver_status = driver_byte(SRpnt->sr_result);
+ if ((sdp->sgdebug > 0) &&
+ ((CHECK_CONDITION == srp->header.masked_status) ||
+ (COMMAND_TERMINATED == srp->header.masked_status)))
+ print_req_sense("sg_cmd_done", SRpnt);
+
+ /* Following if statement is a patch supplied by Eric Youngdale */
+ if (driver_byte(SRpnt->sr_result) != 0
+ && scsi_command_normalize_sense(SCpnt, &sshdr)
+ && !scsi_sense_is_deferred(&sshdr)
+ && sshdr.sense_key == UNIT_ATTENTION
+ && sdp->device->removable) {
+ /* Detected possible disc change. Set the bit - this */
+ /* may be used if there are filesystems using this device */
+ sdp->device->changed = 1;
+ }
+ }
+ /* Rely on write phase to clean out srp status values, so no "else" */
+
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ if (sfp->closed) { /* whoops this fd already released, cleanup */
+ SCSI_LOG_TIMEOUT(1, printk("sg_cmd_done: already closed, freeing ...\n"));
+ sg_finish_rem_req(srp);
+ srp = NULL;
+ if (NULL == sfp->headrp) {
+ SCSI_LOG_TIMEOUT(1, printk("sg...bh: already closed, final cleanup\n"));
+ if (0 == sg_remove_sfp(sdp, sfp)) { /* device still present */
+ scsi_device_put(sdp->device);
+ }
+ sfp = NULL;
+ }
+ } else if (srp && srp->orphan) {
+ if (sfp->keep_orphan)
+ srp->sg_io_owned = 0;
+ else {
+ sg_finish_rem_req(srp);
+ srp = NULL;
+ }
+ }
+ if (sfp && srp) {
+ /* Now wake up any sg_read() that is waiting for this packet. */
+ kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ srp->done = 1;
+ wake_up_interruptible(&sfp->read_wait);
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ }
+}
+
+static struct file_operations sg_fops = {
+ .owner = THIS_MODULE,
+ .read = sg_read,
+ .write = sg_write,
+ .poll = sg_poll,
+ .ioctl = sg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sg_compat_ioctl,
+#endif
+ .open = sg_open,
+ .mmap = sg_mmap,
+ .release = sg_release,
+ .fasync = sg_fasync,
+};
+
+static struct class_simple * sg_sysfs_class;
+
+static int sg_sysfs_valid = 0;
+
+static int sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
+{
+ Sg_device *sdp;
+ unsigned long iflags;
+ void *old_sg_dev_arr = NULL;
+ int k, error;
+
+ sdp = kmalloc(sizeof(Sg_device), GFP_KERNEL);
+ if (!sdp) {
+ printk(KERN_WARNING "kmalloc Sg_device failure\n");
+ return -ENOMEM;
+ }
+
+ write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ if (unlikely(sg_nr_dev >= sg_dev_max)) { /* try to resize */
+ Sg_device **tmp_da;
+ int tmp_dev_max = sg_nr_dev + SG_DEV_ARR_LUMP;
+ write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+
+ tmp_da = kmalloc(tmp_dev_max * sizeof(Sg_device *), GFP_KERNEL);
+ if (unlikely(!tmp_da))
+ goto expand_failed;
+
+ write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ memset(tmp_da, 0, tmp_dev_max * sizeof(Sg_device *));
+ memcpy(tmp_da, sg_dev_arr, sg_dev_max * sizeof(Sg_device *));
+ old_sg_dev_arr = sg_dev_arr;
+ sg_dev_arr = tmp_da;
+ sg_dev_max = tmp_dev_max;
+ }
+
+ for (k = 0; k < sg_dev_max; k++)
+ if (!sg_dev_arr[k])
+ break;
+ if (unlikely(k >= SG_MAX_DEVS))
+ goto overflow;
+
+ memset(sdp, 0, sizeof(*sdp));
+ SCSI_LOG_TIMEOUT(3, printk("sg_alloc: dev=%d \n", k));
+ sprintf(disk->disk_name, "sg%d", k);
+ disk->first_minor = k;
+ sdp->disk = disk;
+ sdp->device = scsidp;
+ init_waitqueue_head(&sdp->o_excl_wait);
+ sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0;
+
+ sg_nr_dev++;
+ sg_dev_arr[k] = sdp;
+ write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ error = k;
+
+ out:
+ if (error < 0)
+ kfree(sdp);
+ kfree(old_sg_dev_arr);
+ return error;
+
+ expand_failed:
+ printk(KERN_WARNING "sg_alloc: device array cannot be resized\n");
+ error = -ENOMEM;
+ goto out;
+
+ overflow:
+ write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ printk(KERN_WARNING
+ "Unable to attach sg device <%d, %d, %d, %d> type=%d, minor "
+ "number exceeds %d\n", scsidp->host->host_no, scsidp->channel,
+ scsidp->id, scsidp->lun, scsidp->type, SG_MAX_DEVS - 1);
+ error = -ENODEV;
+ goto out;
+}
+
+static int
+sg_add(struct class_device *cl_dev)
+{
+ struct scsi_device *scsidp = to_scsi_device(cl_dev->dev);
+ struct gendisk *disk;
+ Sg_device *sdp = NULL;
+ struct cdev * cdev = NULL;
+ int error, k;
+
+ disk = alloc_disk(1);
+ if (!disk) {
+ printk(KERN_WARNING "alloc_disk failed\n");
+ return -ENOMEM;
+ }
+ disk->major = SCSI_GENERIC_MAJOR;
+
+ error = -ENOMEM;
+ cdev = cdev_alloc();
+ if (!cdev) {
+ printk(KERN_WARNING "cdev_alloc failed\n");
+ goto out;
+ }
+ cdev->owner = THIS_MODULE;
+ cdev->ops = &sg_fops;
+
+ error = sg_alloc(disk, scsidp);
+ if (error < 0) {
+ printk(KERN_WARNING "sg_alloc failed\n");
+ goto out;
+ }
+ k = error;
+ sdp = sg_dev_arr[k];
+
+ devfs_mk_cdev(MKDEV(SCSI_GENERIC_MAJOR, k),
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+ "%s/generic", scsidp->devfs_name);
+ error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, k), 1);
+ if (error) {
+ devfs_remove("%s/generic", scsidp->devfs_name);
+ goto out;
+ }
+ sdp->cdev = cdev;
+ if (sg_sysfs_valid) {
+ struct class_device * sg_class_member;
+
+ sg_class_member = class_simple_device_add(sg_sysfs_class,
+ MKDEV(SCSI_GENERIC_MAJOR, k),
+ cl_dev->dev, "%s",
+ disk->disk_name);
+ if (IS_ERR(sg_class_member))
+ printk(KERN_WARNING "sg_add: "
+ "class_simple_device_add failed\n");
+ class_set_devdata(sg_class_member, sdp);
+ error = sysfs_create_link(&scsidp->sdev_gendev.kobj,
+ &sg_class_member->kobj, "generic");
+ if (error)
+ printk(KERN_ERR "sg_add: unable to make symlink "
+ "'generic' back to sg%d\n", k);
+ } else
+ printk(KERN_WARNING "sg_add: sg_sys INvalid\n");
+
+ printk(KERN_NOTICE
+ "Attached scsi generic sg%d at scsi%d, channel"
+ " %d, id %d, lun %d, type %d\n", k,
+ scsidp->host->host_no, scsidp->channel, scsidp->id,
+ scsidp->lun, scsidp->type);
+
+ return 0;
+
+out:
+ put_disk(disk);
+ if (cdev)
+ cdev_del(cdev);
+ return error;
+}
+
+static void
+sg_remove(struct class_device *cl_dev)
+{
+ struct scsi_device *scsidp = to_scsi_device(cl_dev->dev);
+ Sg_device *sdp = NULL;
+ unsigned long iflags;
+ Sg_fd *sfp;
+ Sg_fd *tsfp;
+ Sg_request *srp;
+ Sg_request *tsrp;
+ int k, delay;
+
+ if (NULL == sg_dev_arr)
+ return;
+ delay = 0;
+ write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ for (k = 0; k < sg_dev_max; k++) {
+ sdp = sg_dev_arr[k];
+ if ((NULL == sdp) || (sdp->device != scsidp))
+ continue; /* dirty but lowers nesting */
+ if (sdp->headfp) {
+ sdp->detached = 1;
+ for (sfp = sdp->headfp; sfp; sfp = tsfp) {
+ tsfp = sfp->nextfp;
+ for (srp = sfp->headrp; srp; srp = tsrp) {
+ tsrp = srp->nextrp;
+ if (sfp->closed || (0 == sg_srp_done(srp, sfp)))
+ sg_finish_rem_req(srp);
+ }
+ if (sfp->closed) {
+ scsi_device_put(sdp->device);
+ __sg_remove_sfp(sdp, sfp);
+ } else {
+ delay = 1;
+ wake_up_interruptible(&sfp->read_wait);
+ kill_fasync(&sfp->async_qp, SIGPOLL,
+ POLL_HUP);
+ }
+ }
+ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k));
+ if (NULL == sdp->headfp) {
+ sg_dev_arr[k] = NULL;
+ }
+ } else { /* nothing active, simple case */
+ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k));
+ sg_dev_arr[k] = NULL;
+ }
+ sg_nr_dev--;
+ break;
+ }
+ write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+
+ if (sdp) {
+ sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic");
+ class_simple_device_remove(MKDEV(SCSI_GENERIC_MAJOR, k));
+ cdev_del(sdp->cdev);
+ sdp->cdev = NULL;
+ devfs_remove("%s/generic", scsidp->devfs_name);
+ put_disk(sdp->disk);
+ sdp->disk = NULL;
+ if (NULL == sdp->headfp)
+ kfree((char *) sdp);
+ }
+
+ if (delay)
+ msleep(10); /* dirty detach so delay device destruction */
+}
+
+/* Set 'perm' (4th argument) to 0 to disable module_param's definition
+ * of sysfs parameters (which module_param doesn't yet support).
+ * Sysfs parameters defined explicitly below.
+ */
+module_param_named(def_reserved_size, def_reserved_size, int, S_IRUGO);
+module_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR);
+
+MODULE_AUTHOR("Douglas Gilbert");
+MODULE_DESCRIPTION("SCSI generic (sg) driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SG_VERSION_STR);
+
+MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
+MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))");
+
+static int __init
+init_sg(void)
+{
+ int rc;
+
+ if (def_reserved_size >= 0)
+ sg_big_buff = def_reserved_size;
+
+ rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
+ SG_MAX_DEVS, "sg");
+ if (rc)
+ return rc;
+ sg_sysfs_class = class_simple_create(THIS_MODULE, "scsi_generic");
+ if ( IS_ERR(sg_sysfs_class) ) {
+ rc = PTR_ERR(sg_sysfs_class);
+ goto err_out;
+ }
+ sg_sysfs_valid = 1;
+ rc = scsi_register_interface(&sg_interface);
+ if (0 == rc) {
+#ifdef CONFIG_SCSI_PROC_FS
+ sg_proc_init();
+#endif /* CONFIG_SCSI_PROC_FS */
+ return 0;
+ }
+ class_simple_destroy(sg_sysfs_class);
+err_out:
+ unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS);
+ return rc;
+}
+
+static void __exit
+exit_sg(void)
+{
+#ifdef CONFIG_SCSI_PROC_FS
+ sg_proc_cleanup();
+#endif /* CONFIG_SCSI_PROC_FS */
+ scsi_unregister_interface(&sg_interface);
+ class_simple_destroy(sg_sysfs_class);
+ sg_sysfs_valid = 0;
+ unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
+ SG_MAX_DEVS);
+ if (sg_dev_arr != NULL) {
+ kfree((char *) sg_dev_arr);
+ sg_dev_arr = NULL;
+ }
+ sg_dev_max = 0;
+}
+
+static int
+sg_start_req(Sg_request * srp)
+{
+ int res;
+ Sg_fd *sfp = srp->parentfp;
+ sg_io_hdr_t *hp = &srp->header;
+ int dxfer_len = (int) hp->dxfer_len;
+ int dxfer_dir = hp->dxfer_direction;
+ Sg_scatter_hold *req_schp = &srp->data;
+ Sg_scatter_hold *rsv_schp = &sfp->reserve;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len));
+ if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE))
+ return 0;
+ if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) &&
+ (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) &&
+ (!sfp->parentdp->device->host->unchecked_isa_dma)) {
+ res = sg_build_direct(srp, sfp, dxfer_len);
+ if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */
+ return res;
+ }
+ if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen))
+ sg_link_reserve(sfp, srp, dxfer_len);
+ else {
+ res = sg_build_indirect(req_schp, sfp, dxfer_len);
+ if (res) {
+ sg_remove_scat(req_schp);
+ return res;
+ }
+ }
+ return 0;
+}
+
+static void
+sg_finish_rem_req(Sg_request * srp)
+{
+ Sg_fd *sfp = srp->parentfp;
+ Sg_scatter_hold *req_schp = &srp->data;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", (int) srp->res_used));
+ if (srp->res_used)
+ sg_unlink_reserve(sfp, srp);
+ else
+ sg_remove_scat(req_schp);
+ sg_remove_request(sfp, srp);
+}
+
+static int
+sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize)
+{
+ int ret_sz;
+ int elem_sz = sizeof (struct scatterlist);
+ int sg_bufflen = tablesize * elem_sz;
+ int mx_sc_elems = tablesize;
+
+ schp->buffer = sg_page_malloc(sg_bufflen, sfp->low_dma, &ret_sz);
+ if (!schp->buffer)
+ return -ENOMEM;
+ else if (ret_sz != sg_bufflen) {
+ sg_bufflen = ret_sz;
+ mx_sc_elems = sg_bufflen / elem_sz;
+ }
+ schp->sglist_len = sg_bufflen;
+ memset(schp->buffer, 0, sg_bufflen);
+ return mx_sc_elems; /* number of scat_gath elements allocated */
+}
+
+#ifdef SG_ALLOW_DIO_CODE
+/* vvvvvvvv following code borrowed from st driver's direct IO vvvvvvvvv */
+ /* hopefully this generic code will moved to a library */
+
+/* Pin down user pages and put them into a scatter gather list. Returns <= 0 if
+ - mapping of all pages not successful
+ - any page is above max_pfn
+ (i.e., either completely successful or fails)
+*/
+static int
+st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages,
+ unsigned long uaddr, size_t count, int rw,
+ unsigned long max_pfn)
+{
+ int res, i, j;
+ unsigned int nr_pages;
+ struct page **pages;
+
+ nr_pages = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ /* User attempted Overflow! */
+ if ((uaddr + count) < uaddr)
+ return -EINVAL;
+
+ /* Too big */
+ if (nr_pages > max_pages)
+ return -ENOMEM;
+
+ /* Hmm? */
+ if (count == 0)
+ return 0;
+
+ if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_ATOMIC)) == NULL)
+ return -ENOMEM;
+
+ /* Try to fault in all of the necessary pages */
+ down_read(&current->mm->mmap_sem);
+ /* rw==READ means read from drive, write into memory area */
+ res = get_user_pages(
+ current,
+ current->mm,
+ uaddr,
+ nr_pages,
+ rw == READ,
+ 0, /* don't force */
+ pages,
+ NULL);
+ up_read(&current->mm->mmap_sem);
+
+ /* Errors and no page mapped should return here */
+ if (res < nr_pages)
+ goto out_unmap;
+
+ for (i=0; i < nr_pages; i++) {
+ /* FIXME: flush superflous for rw==READ,
+ * probably wrong function for rw==WRITE
+ */
+ flush_dcache_page(pages[i]);
+ if (page_to_pfn(pages[i]) > max_pfn)
+ goto out_unlock;
+ /* ?? Is locking needed? I don't think so */
+ /* if (TestSetPageLocked(pages[i]))
+ goto out_unlock; */
+ }
+
+ /* Populate the scatter/gather list */
+ sgl[0].page = pages[0];
+ sgl[0].offset = uaddr & ~PAGE_MASK;
+ if (nr_pages > 1) {
+ sgl[0].length = PAGE_SIZE - sgl[0].offset;
+ count -= sgl[0].length;
+ for (i=1; i < nr_pages ; i++) {
+ sgl[i].offset = 0;
+ sgl[i].page = pages[i];
+ sgl[i].length = count < PAGE_SIZE ? count : PAGE_SIZE;
+ count -= PAGE_SIZE;
+ }
+ }
+ else {
+ sgl[0].length = count;
+ }
+
+ kfree(pages);
+ return nr_pages;
+
+ out_unlock:
+ /* for (j=0; j < i; j++)
+ unlock_page(pages[j]); */
+ res = 0;
+ out_unmap:
+ if (res > 0)
+ for (j=0; j < res; j++)
+ page_cache_release(pages[j]);
+ kfree(pages);
+ return res;
+}
+
+
+/* And unmap them... */
+static int
+st_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages,
+ int dirtied)
+{
+ int i;
+
+ for (i=0; i < nr_pages; i++) {
+ if (dirtied && !PageReserved(sgl[i].page))
+ SetPageDirty(sgl[i].page);
+ /* unlock_page(sgl[i].page); */
+ /* FIXME: cache flush missing for rw==READ
+ * FIXME: call the correct reference counting function
+ */
+ page_cache_release(sgl[i].page);
+ }
+
+ return 0;
+}
+
+/* ^^^^^^^^ above code borrowed from st driver's direct IO ^^^^^^^^^ */
+#endif
+
+
+/* Returns: -ve -> error, 0 -> done, 1 -> try indirect */
+static int
+sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len)
+{
+#ifdef SG_ALLOW_DIO_CODE
+ sg_io_hdr_t *hp = &srp->header;
+ Sg_scatter_hold *schp = &srp->data;
+ int sg_tablesize = sfp->parentdp->sg_tablesize;
+ struct scatterlist *sgl;
+ int mx_sc_elems, res;
+ struct scsi_device *sdev = sfp->parentdp->device;
+
+ if (((unsigned long)hp->dxferp &
+ queue_dma_alignment(sdev->request_queue)) != 0)
+ return 1;
+ mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize);
+ if (mx_sc_elems <= 0) {
+ return 1;
+ }
+ sgl = (struct scatterlist *)schp->buffer;
+ res = st_map_user_pages(sgl, mx_sc_elems, (unsigned long)hp->dxferp, dxfer_len,
+ (SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0, ULONG_MAX);
+ if (res <= 0)
+ return 1;
+ schp->k_use_sg = res;
+ schp->dio_in_use = 1;
+ hp->info |= SG_INFO_DIRECT_IO;
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+static int
+sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
+{
+ int ret_sz;
+ int blk_size = buff_size;
+ unsigned char *p = NULL;
+
+ if ((blk_size < 0) || (!sfp))
+ return -EFAULT;
+ if (0 == blk_size)
+ ++blk_size; /* don't know why */
+/* round request up to next highest SG_SECTOR_SZ byte boundary */
+ blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK);
+ SCSI_LOG_TIMEOUT(4, printk("sg_build_indirect: buff_size=%d, blk_size=%d\n",
+ buff_size, blk_size));
+ if (blk_size <= SG_SCATTER_SZ) {
+ p = sg_page_malloc(blk_size, sfp->low_dma, &ret_sz);
+ if (!p)
+ return -ENOMEM;
+ if (blk_size == ret_sz) { /* got it on the first attempt */
+ schp->k_use_sg = 0;
+ schp->buffer = p;
+ schp->bufflen = blk_size;
+ schp->b_malloc_len = blk_size;
+ return 0;
+ }
+ } else {
+ p = sg_page_malloc(SG_SCATTER_SZ, sfp->low_dma, &ret_sz);
+ if (!p)
+ return -ENOMEM;
+ }
+/* Want some local declarations, so start new block ... */
+ { /* lets try and build a scatter gather list */
+ struct scatterlist *sclp;
+ int k, rem_sz, num;
+ int mx_sc_elems;
+ int sg_tablesize = sfp->parentdp->sg_tablesize;
+ int first = 1;
+
+ /* N.B. ret_sz carried into this block ... */
+ mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize);
+ if (mx_sc_elems < 0)
+ return mx_sc_elems; /* most likely -ENOMEM */
+
+ for (k = 0, sclp = schp->buffer, rem_sz = blk_size;
+ (rem_sz > 0) && (k < mx_sc_elems);
+ ++k, rem_sz -= ret_sz, ++sclp) {
+ if (first)
+ first = 0;
+ else {
+ num =
+ (rem_sz >
+ SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz;
+ p = sg_page_malloc(num, sfp->low_dma, &ret_sz);
+ if (!p)
+ break;
+ }
+ sclp->page = virt_to_page(p);
+ sclp->offset = offset_in_page(p);
+ sclp->length = ret_sz;
+
+ SCSI_LOG_TIMEOUT(5, printk("sg_build_build: k=%d, a=0x%p, len=%d\n",
+ k, sg_scatg2virt(sclp), ret_sz));
+ } /* end of for loop */
+ schp->k_use_sg = k;
+ SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k_use_sg=%d, rem_sz=%d\n", k, rem_sz));
+ schp->bufflen = blk_size;
+ if (rem_sz > 0) /* must have failed */
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int
+sg_write_xfer(Sg_request * srp)
+{
+ sg_io_hdr_t *hp = &srp->header;
+ Sg_scatter_hold *schp = &srp->data;
+ int num_xfer = 0;
+ int j, k, onum, usglen, ksglen, res;
+ int iovec_count = (int) hp->iovec_count;
+ int dxfer_dir = hp->dxfer_direction;
+ unsigned char *p;
+ unsigned char __user *up;
+ int new_interface = ('\0' == hp->interface_id) ? 0 : 1;
+
+ if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) ||
+ (SG_DXFER_TO_FROM_DEV == dxfer_dir)) {
+ num_xfer = (int) (new_interface ? hp->dxfer_len : hp->flags);
+ if (schp->bufflen < num_xfer)
+ num_xfer = schp->bufflen;
+ }
+ if ((num_xfer <= 0) || (schp->dio_in_use) ||
+ (new_interface
+ && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags)))
+ return 0;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n",
+ num_xfer, iovec_count, schp->k_use_sg));
+ if (iovec_count) {
+ onum = iovec_count;
+ if (!access_ok(VERIFY_READ, hp->dxferp, SZ_SG_IOVEC * onum))
+ return -EFAULT;
+ } else
+ onum = 1;
+
+ if (0 == schp->k_use_sg) { /* kernel has single buffer */
+ for (j = 0, p = schp->buffer; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up);
+ if (res)
+ return res;
+ usglen = (num_xfer > usglen) ? usglen : num_xfer;
+ if (__copy_from_user(p, up, usglen))
+ return -EFAULT;
+ p += usglen;
+ num_xfer -= usglen;
+ if (num_xfer <= 0)
+ return 0;
+ }
+ } else { /* kernel using scatter gather list */
+ struct scatterlist *sclp = (struct scatterlist *) schp->buffer;
+
+ ksglen = (int) sclp->length;
+ p = sg_scatg2virt(sclp);
+ for (j = 0, k = 0; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up);
+ if (res)
+ return res;
+
+ for (; p; ++sclp, ksglen = (int) sclp->length,
+ p = sg_scatg2virt(sclp)) {
+ if (usglen <= 0)
+ break;
+ if (ksglen > usglen) {
+ if (usglen >= num_xfer) {
+ if (__copy_from_user
+ (p, up, num_xfer))
+ return -EFAULT;
+ return 0;
+ }
+ if (__copy_from_user(p, up, usglen))
+ return -EFAULT;
+ p += usglen;
+ ksglen -= usglen;
+ break;
+ } else {
+ if (ksglen >= num_xfer) {
+ if (__copy_from_user
+ (p, up, num_xfer))
+ return -EFAULT;
+ return 0;
+ }
+ if (__copy_from_user(p, up, ksglen))
+ return -EFAULT;
+ up += ksglen;
+ usglen -= ksglen;
+ }
+ ++k;
+ if (k >= schp->k_use_sg)
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,
+ int wr_xf, int *countp, unsigned char __user **up)
+{
+ int num_xfer = (int) hp->dxfer_len;
+ unsigned char __user *p = hp->dxferp;
+ int count;
+
+ if (0 == sg_num) {
+ if (wr_xf && ('\0' == hp->interface_id))
+ count = (int) hp->flags; /* holds "old" input_size */
+ else
+ count = num_xfer;
+ } else {
+ sg_iovec_t iovec;
+ if (__copy_from_user(&iovec, p + ind*SZ_SG_IOVEC, SZ_SG_IOVEC))
+ return -EFAULT;
+ p = iovec.iov_base;
+ count = (int) iovec.iov_len;
+ }
+ if (!access_ok(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count))
+ return -EFAULT;
+ if (up)
+ *up = p;
+ if (countp)
+ *countp = count;
+ return 0;
+}
+
+static void
+sg_remove_scat(Sg_scatter_hold * schp)
+{
+ SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg));
+ if (schp->buffer && (schp->sglist_len > 0)) {
+ struct scatterlist *sclp = (struct scatterlist *) schp->buffer;
+
+ if (schp->dio_in_use) {
+#ifdef SG_ALLOW_DIO_CODE
+ st_unmap_user_pages(sclp, schp->k_use_sg, TRUE);
+#endif
+ } else {
+ int k;
+
+ for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp);
+ ++k, ++sclp) {
+ SCSI_LOG_TIMEOUT(5, printk(
+ "sg_remove_scat: k=%d, a=0x%p, len=%d\n",
+ k, sg_scatg2virt(sclp), sclp->length));
+ sg_page_free(sg_scatg2virt(sclp), sclp->length);
+ sclp->page = NULL;
+ sclp->offset = 0;
+ sclp->length = 0;
+ }
+ }
+ sg_page_free(schp->buffer, schp->sglist_len);
+ } else if (schp->buffer)
+ sg_page_free(schp->buffer, schp->b_malloc_len);
+ memset(schp, 0, sizeof (*schp));
+}
+
+static int
+sg_read_xfer(Sg_request * srp)
+{
+ sg_io_hdr_t *hp = &srp->header;
+ Sg_scatter_hold *schp = &srp->data;
+ int num_xfer = 0;
+ int j, k, onum, usglen, ksglen, res;
+ int iovec_count = (int) hp->iovec_count;
+ int dxfer_dir = hp->dxfer_direction;
+ unsigned char *p;
+ unsigned char __user *up;
+ int new_interface = ('\0' == hp->interface_id) ? 0 : 1;
+
+ if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_FROM_DEV == dxfer_dir)
+ || (SG_DXFER_TO_FROM_DEV == dxfer_dir)) {
+ num_xfer = hp->dxfer_len;
+ if (schp->bufflen < num_xfer)
+ num_xfer = schp->bufflen;
+ }
+ if ((num_xfer <= 0) || (schp->dio_in_use) ||
+ (new_interface
+ && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags)))
+ return 0;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n",
+ num_xfer, iovec_count, schp->k_use_sg));
+ if (iovec_count) {
+ onum = iovec_count;
+ if (!access_ok(VERIFY_READ, hp->dxferp, SZ_SG_IOVEC * onum))
+ return -EFAULT;
+ } else
+ onum = 1;
+
+ if (0 == schp->k_use_sg) { /* kernel has single buffer */
+ for (j = 0, p = schp->buffer; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up);
+ if (res)
+ return res;
+ usglen = (num_xfer > usglen) ? usglen : num_xfer;
+ if (__copy_to_user(up, p, usglen))
+ return -EFAULT;
+ p += usglen;
+ num_xfer -= usglen;
+ if (num_xfer <= 0)
+ return 0;
+ }
+ } else { /* kernel using scatter gather list */
+ struct scatterlist *sclp = (struct scatterlist *) schp->buffer;
+
+ ksglen = (int) sclp->length;
+ p = sg_scatg2virt(sclp);
+ for (j = 0, k = 0; j < onum; ++j) {
+ res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up);
+ if (res)
+ return res;
+
+ for (; p; ++sclp, ksglen = (int) sclp->length,
+ p = sg_scatg2virt(sclp)) {
+ if (usglen <= 0)
+ break;
+ if (ksglen > usglen) {
+ if (usglen >= num_xfer) {
+ if (__copy_to_user
+ (up, p, num_xfer))
+ return -EFAULT;
+ return 0;
+ }
+ if (__copy_to_user(up, p, usglen))
+ return -EFAULT;
+ p += usglen;
+ ksglen -= usglen;
+ break;
+ } else {
+ if (ksglen >= num_xfer) {
+ if (__copy_to_user
+ (up, p, num_xfer))
+ return -EFAULT;
+ return 0;
+ }
+ if (__copy_to_user(up, p, ksglen))
+ return -EFAULT;
+ up += ksglen;
+ usglen -= ksglen;
+ }
+ ++k;
+ if (k >= schp->k_use_sg)
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer)
+{
+ Sg_scatter_hold *schp = &srp->data;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n",
+ num_read_xfer));
+ if ((!outp) || (num_read_xfer <= 0))
+ return 0;
+ if (schp->k_use_sg > 0) {
+ int k, num;
+ struct scatterlist *sclp = (struct scatterlist *) schp->buffer;
+
+ for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp);
+ ++k, ++sclp) {
+ num = (int) sclp->length;
+ if (num > num_read_xfer) {
+ if (__copy_to_user
+ (outp, sg_scatg2virt(sclp), num_read_xfer))
+ return -EFAULT;
+ break;
+ } else {
+ if (__copy_to_user
+ (outp, sg_scatg2virt(sclp), num))
+ return -EFAULT;
+ num_read_xfer -= num;
+ if (num_read_xfer <= 0)
+ break;
+ outp += num;
+ }
+ }
+ } else {
+ if (__copy_to_user(outp, schp->buffer, num_read_xfer))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void
+sg_build_reserve(Sg_fd * sfp, int req_size)
+{
+ Sg_scatter_hold *schp = &sfp->reserve;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size));
+ do {
+ if (req_size < PAGE_SIZE)
+ req_size = PAGE_SIZE;
+ if (0 == sg_build_indirect(schp, sfp, req_size))
+ return;
+ else
+ sg_remove_scat(schp);
+ req_size >>= 1; /* divide by 2 */
+ } while (req_size > (PAGE_SIZE / 2));
+}
+
+static void
+sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size)
+{
+ Sg_scatter_hold *req_schp = &srp->data;
+ Sg_scatter_hold *rsv_schp = &sfp->reserve;
+
+ srp->res_used = 1;
+ SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size));
+ size = (size + 1) & (~1); /* round to even for aha1542 */
+ if (rsv_schp->k_use_sg > 0) {
+ int k, num;
+ int rem = size;
+ struct scatterlist *sclp =
+ (struct scatterlist *) rsv_schp->buffer;
+
+ for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) {
+ num = (int) sclp->length;
+ if (rem <= num) {
+ if (0 == k) {
+ req_schp->k_use_sg = 0;
+ req_schp->buffer = sg_scatg2virt(sclp);
+ } else {
+ sfp->save_scat_len = num;
+ sclp->length = (unsigned) rem;
+ req_schp->k_use_sg = k + 1;
+ req_schp->sglist_len =
+ rsv_schp->sglist_len;
+ req_schp->buffer = rsv_schp->buffer;
+ }
+ req_schp->bufflen = size;
+ req_schp->b_malloc_len = rsv_schp->b_malloc_len;
+ break;
+ } else
+ rem -= num;
+ }
+ if (k >= rsv_schp->k_use_sg)
+ SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n"));
+ } else {
+ req_schp->k_use_sg = 0;
+ req_schp->bufflen = size;
+ req_schp->buffer = rsv_schp->buffer;
+ req_schp->b_malloc_len = rsv_schp->b_malloc_len;
+ }
+}
+
+static void
+sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp)
+{
+ Sg_scatter_hold *req_schp = &srp->data;
+ Sg_scatter_hold *rsv_schp = &sfp->reserve;
+
+ SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n",
+ (int) req_schp->k_use_sg));
+ if ((rsv_schp->k_use_sg > 0) && (req_schp->k_use_sg > 0)) {
+ struct scatterlist *sclp =
+ (struct scatterlist *) rsv_schp->buffer;
+
+ if (sfp->save_scat_len > 0)
+ (sclp + (req_schp->k_use_sg - 1))->length =
+ (unsigned) sfp->save_scat_len;
+ else
+ SCSI_LOG_TIMEOUT(1, printk ("sg_unlink_reserve: BAD save_scat_len\n"));
+ }
+ req_schp->k_use_sg = 0;
+ req_schp->bufflen = 0;
+ req_schp->buffer = NULL;
+ req_schp->sglist_len = 0;
+ sfp->save_scat_len = 0;
+ srp->res_used = 0;
+}
+
+static Sg_request *
+sg_get_rq_mark(Sg_fd * sfp, int pack_id)
+{
+ Sg_request *resp;
+ unsigned long iflags;
+
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (resp = sfp->headrp; resp; resp = resp->nextrp) {
+ /* look for requests that are ready + not SG_IO owned */
+ if ((1 == resp->done) && (!resp->sg_io_owned) &&
+ ((-1 == pack_id) || (resp->header.pack_id == pack_id))) {
+ resp->done = 2; /* guard against other readers */
+ break;
+ }
+ }
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return resp;
+}
+
+#ifdef CONFIG_SCSI_PROC_FS
+static Sg_request *
+sg_get_nth_request(Sg_fd * sfp, int nth)
+{
+ Sg_request *resp;
+ unsigned long iflags;
+ int k;
+
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (k = 0, resp = sfp->headrp; resp && (k < nth);
+ ++k, resp = resp->nextrp) ;
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return resp;
+}
+#endif
+
+/* always adds to end of list */
+static Sg_request *
+sg_add_request(Sg_fd * sfp)
+{
+ int k;
+ unsigned long iflags;
+ Sg_request *resp;
+ Sg_request *rp = sfp->req_arr;
+
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ resp = sfp->headrp;
+ if (!resp) {
+ memset(rp, 0, sizeof (Sg_request));
+ rp->parentfp = sfp;
+ resp = rp;
+ sfp->headrp = resp;
+ } else {
+ if (0 == sfp->cmd_q)
+ resp = NULL; /* command queuing disallowed */
+ else {
+ for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {
+ if (!rp->parentfp)
+ break;
+ }
+ if (k < SG_MAX_QUEUE) {
+ memset(rp, 0, sizeof (Sg_request));
+ rp->parentfp = sfp;
+ while (resp->nextrp)
+ resp = resp->nextrp;
+ resp->nextrp = rp;
+ resp = rp;
+ } else
+ resp = NULL;
+ }
+ }
+ if (resp) {
+ resp->nextrp = NULL;
+ resp->header.duration = jiffies;
+ resp->my_cmdp = NULL;
+ }
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return resp;
+}
+
+/* Return of 1 for found; 0 for not found */
+static int
+sg_remove_request(Sg_fd * sfp, Sg_request * srp)
+{
+ Sg_request *prev_rp;
+ Sg_request *rp;
+ unsigned long iflags;
+ int res = 0;
+
+ if ((!sfp) || (!srp) || (!sfp->headrp))
+ return res;
+ write_lock_irqsave(&sfp->rq_list_lock, iflags);
+ prev_rp = sfp->headrp;
+ if (srp == prev_rp) {
+ sfp->headrp = prev_rp->nextrp;
+ prev_rp->parentfp = NULL;
+ res = 1;
+ } else {
+ while ((rp = prev_rp->nextrp)) {
+ if (srp == rp) {
+ prev_rp->nextrp = rp->nextrp;
+ rp->parentfp = NULL;
+ res = 1;
+ break;
+ }
+ prev_rp = rp;
+ }
+ }
+ write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return res;
+}
+
+#ifdef CONFIG_SCSI_PROC_FS
+static Sg_fd *
+sg_get_nth_sfp(Sg_device * sdp, int nth)
+{
+ Sg_fd *resp;
+ unsigned long iflags;
+ int k;
+
+ read_lock_irqsave(&sg_dev_arr_lock, iflags);
+ for (k = 0, resp = sdp->headfp; resp && (k < nth);
+ ++k, resp = resp->nextfp) ;
+ read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ return resp;
+}
+#endif
+
+static Sg_fd *
+sg_add_sfp(Sg_device * sdp, int dev)
+{
+ Sg_fd *sfp;
+ unsigned long iflags;
+
+ sfp = (Sg_fd *) sg_page_malloc(sizeof (Sg_fd), 0, NULL);
+ if (!sfp)
+ return NULL;
+ memset(sfp, 0, sizeof (Sg_fd));
+ init_waitqueue_head(&sfp->read_wait);
+ rwlock_init(&sfp->rq_list_lock);
+
+ sfp->timeout = SG_DEFAULT_TIMEOUT;
+ sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
+ sfp->force_packid = SG_DEF_FORCE_PACK_ID;
+ sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
+ sdp->device->host->unchecked_isa_dma : 1;
+ sfp->cmd_q = SG_DEF_COMMAND_Q;
+ sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
+ sfp->parentdp = sdp;
+ write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ if (!sdp->headfp)
+ sdp->headfp = sfp;
+ else { /* add to tail of existing list */
+ Sg_fd *pfp = sdp->headfp;
+ while (pfp->nextfp)
+ pfp = pfp->nextfp;
+ pfp->nextfp = sfp;
+ }
+ write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp));
+ sg_build_reserve(sfp, sg_big_buff);
+ SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n",
+ sfp->reserve.bufflen, sfp->reserve.k_use_sg));
+ return sfp;
+}
+
+static void
+__sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
+{
+ Sg_fd *fp;
+ Sg_fd *prev_fp;
+
+ prev_fp = sdp->headfp;
+ if (sfp == prev_fp)
+ sdp->headfp = prev_fp->nextfp;
+ else {
+ while ((fp = prev_fp->nextfp)) {
+ if (sfp == fp) {
+ prev_fp->nextfp = fp->nextfp;
+ break;
+ }
+ prev_fp = fp;
+ }
+ }
+ if (sfp->reserve.bufflen > 0) {
+ SCSI_LOG_TIMEOUT(6,
+ printk("__sg_remove_sfp: bufflen=%d, k_use_sg=%d\n",
+ (int) sfp->reserve.bufflen, (int) sfp->reserve.k_use_sg));
+ if (sfp->mmap_called)
+ sg_rb_correct4mmap(&sfp->reserve, 0); /* undo correction */
+ sg_remove_scat(&sfp->reserve);
+ }
+ sfp->parentdp = NULL;
+ SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: sfp=0x%p\n", sfp));
+ sg_page_free((char *) sfp, sizeof (Sg_fd));
+}
+
+/* Returns 0 in normal case, 1 when detached and sdp object removed */
+static int
+sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
+{
+ Sg_request *srp;
+ Sg_request *tsrp;
+ int dirty = 0;
+ int res = 0;
+
+ for (srp = sfp->headrp; srp; srp = tsrp) {
+ tsrp = srp->nextrp;
+ if (sg_srp_done(srp, sfp))
+ sg_finish_rem_req(srp);
+ else
+ ++dirty;
+ }
+ if (0 == dirty) {
+ unsigned long iflags;
+
+ write_lock_irqsave(&sg_dev_arr_lock, iflags);
+ __sg_remove_sfp(sdp, sfp);
+ if (sdp->detached && (NULL == sdp->headfp)) {
+ int k, maxd;
+
+ maxd = sg_dev_max;
+ for (k = 0; k < maxd; ++k) {
+ if (sdp == sg_dev_arr[k])
+ break;
+ }
+ if (k < maxd)
+ sg_dev_arr[k] = NULL;
+ kfree((char *) sdp);
+ res = 1;
+ }
+ write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ } else {
+ /* MOD_INC's to inhibit unloading sg and associated adapter driver */
+ /* only bump the access_count if we actually succeeded in
+ * throwing another counter on the host module */
+ scsi_device_get(sdp->device); /* XXX: retval ignored? */
+ sfp->closed = 1; /* flag dirty state on this fd */
+ SCSI_LOG_TIMEOUT(1, printk("sg_remove_sfp: worrisome, %d writes pending\n",
+ dirty));
+ }
+ return res;
+}
+
+static int
+sg_res_in_use(Sg_fd * sfp)
+{
+ const Sg_request *srp;
+ unsigned long iflags;
+
+ read_lock_irqsave(&sfp->rq_list_lock, iflags);
+ for (srp = sfp->headrp; srp; srp = srp->nextrp)
+ if (srp->res_used)
+ break;
+ read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+ return srp ? 1 : 0;
+}
+
+/* If retSzp==NULL want exact size or fail */
+static char *
+sg_page_malloc(int rqSz, int lowDma, int *retSzp)
+{
+ char *resp = NULL;
+ int page_mask;
+ int order, a_size;
+ int resSz = rqSz;
+
+ if (rqSz <= 0)
+ return resp;
+
+ if (lowDma)
+ page_mask = GFP_ATOMIC | GFP_DMA | __GFP_NOWARN;
+ else
+ page_mask = GFP_ATOMIC | __GFP_NOWARN;
+
+ for (order = 0, a_size = PAGE_SIZE; a_size < rqSz;
+ order++, a_size <<= 1) ;
+ resp = (char *) __get_free_pages(page_mask, order);
+ while ((!resp) && order && retSzp) {
+ --order;
+ a_size >>= 1; /* divide by 2, until PAGE_SIZE */
+ resp = (char *) __get_free_pages(page_mask, order); /* try half */
+ resSz = a_size;
+ }
+ if (resp) {
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ memset(resp, 0, resSz);
+ if (retSzp)
+ *retSzp = resSz;
+ }
+ return resp;
+}
+
+static void
+sg_page_free(char *buff, int size)
+{
+ int order, a_size;
+
+ if (!buff)
+ return;
+ for (order = 0, a_size = PAGE_SIZE; a_size < size;
+ order++, a_size <<= 1) ;
+ free_pages((unsigned long) buff, order);
+}
+
+#ifndef MAINTENANCE_IN_CMD
+#define MAINTENANCE_IN_CMD 0xa3
+#endif
+
+static unsigned char allow_ops[] = { TEST_UNIT_READY, REQUEST_SENSE,
+ INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12,
+ READ_16, MODE_SENSE, MODE_SENSE_10, LOG_SENSE, REPORT_LUNS,
+ SERVICE_ACTION_IN, RECEIVE_DIAGNOSTIC, READ_LONG, MAINTENANCE_IN_CMD
+};
+
+static int
+sg_allow_access(unsigned char opcode, char dev_type)
+{
+ int k;
+
+ if (TYPE_SCANNER == dev_type) /* TYPE_ROM maybe burner */
+ return 1;
+ for (k = 0; k < sizeof (allow_ops); ++k) {
+ if (opcode == allow_ops[k])
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_SCSI_PROC_FS
+static int
+sg_last_dev(void)
+{
+ int k;
+ unsigned long iflags;
+
+ read_lock_irqsave(&sg_dev_arr_lock, iflags);
+ for (k = sg_dev_max - 1; k >= 0; --k)
+ if (sg_dev_arr[k] && sg_dev_arr[k]->device)
+ break;
+ read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ return k + 1; /* origin 1 */
+}
+#endif
+
+static Sg_device *
+sg_get_dev(int dev)
+{
+ Sg_device *sdp = NULL;
+ unsigned long iflags;
+
+ if (sg_dev_arr && (dev >= 0)) {
+ read_lock_irqsave(&sg_dev_arr_lock, iflags);
+ if (dev < sg_dev_max)
+ sdp = sg_dev_arr[dev];
+ read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+ }
+ return sdp;
+}
+
+#ifdef CONFIG_SCSI_PROC_FS
+
+static struct proc_dir_entry *sg_proc_sgp = NULL;
+
+static char sg_proc_sg_dirname[] = "scsi/sg";
+
+static int sg_proc_seq_show_int(struct seq_file *s, void *v);
+
+static int sg_proc_single_open_adio(struct inode *inode, struct file *file);
+static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *off);
+static struct file_operations adio_fops = {
+ /* .owner, .read and .llseek added in sg_proc_init() */
+ .open = sg_proc_single_open_adio,
+ .write = sg_proc_write_adio,
+ .release = single_release,
+};
+
+static int sg_proc_single_open_dressz(struct inode *inode, struct file *file);
+static ssize_t sg_proc_write_dressz(struct file *filp,
+ const char __user *buffer, size_t count, loff_t *off);
+static struct file_operations dressz_fops = {
+ .open = sg_proc_single_open_dressz,
+ .write = sg_proc_write_dressz,
+ .release = single_release,
+};
+
+static int sg_proc_seq_show_version(struct seq_file *s, void *v);
+static int sg_proc_single_open_version(struct inode *inode, struct file *file);
+static struct file_operations version_fops = {
+ .open = sg_proc_single_open_version,
+ .release = single_release,
+};
+
+static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v);
+static int sg_proc_single_open_devhdr(struct inode *inode, struct file *file);
+static struct file_operations devhdr_fops = {
+ .open = sg_proc_single_open_devhdr,
+ .release = single_release,
+};
+
+static int sg_proc_seq_show_dev(struct seq_file *s, void *v);
+static int sg_proc_open_dev(struct inode *inode, struct file *file);
+static void * dev_seq_start(struct seq_file *s, loff_t *pos);
+static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos);
+static void dev_seq_stop(struct seq_file *s, void *v);
+static struct file_operations dev_fops = {
+ .open = sg_proc_open_dev,
+ .release = seq_release,
+};
+static struct seq_operations dev_seq_ops = {
+ .start = dev_seq_start,
+ .next = dev_seq_next,
+ .stop = dev_seq_stop,
+ .show = sg_proc_seq_show_dev,
+};
+
+static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v);
+static int sg_proc_open_devstrs(struct inode *inode, struct file *file);
+static struct file_operations devstrs_fops = {
+ .open = sg_proc_open_devstrs,
+ .release = seq_release,
+};
+static struct seq_operations devstrs_seq_ops = {
+ .start = dev_seq_start,
+ .next = dev_seq_next,
+ .stop = dev_seq_stop,
+ .show = sg_proc_seq_show_devstrs,
+};
+
+static int sg_proc_seq_show_debug(struct seq_file *s, void *v);
+static int sg_proc_open_debug(struct inode *inode, struct file *file);
+static struct file_operations debug_fops = {
+ .open = sg_proc_open_debug,
+ .release = seq_release,
+};
+static struct seq_operations debug_seq_ops = {
+ .start = dev_seq_start,
+ .next = dev_seq_next,
+ .stop = dev_seq_stop,
+ .show = sg_proc_seq_show_debug,
+};
+
+
+struct sg_proc_leaf {
+ const char * name;
+ struct file_operations * fops;
+};
+
+static struct sg_proc_leaf sg_proc_leaf_arr[] = {
+ {"allow_dio", &adio_fops},
+ {"debug", &debug_fops},
+ {"def_reserved_size", &dressz_fops},
+ {"device_hdr", &devhdr_fops},
+ {"devices", &dev_fops},
+ {"device_strs", &devstrs_fops},
+ {"version", &version_fops}
+};
+
+static int
+sg_proc_init(void)
+{
+ int k, mask;
+ int num_leaves =
+ sizeof (sg_proc_leaf_arr) / sizeof (sg_proc_leaf_arr[0]);
+ struct proc_dir_entry *pdep;
+ struct sg_proc_leaf * leaf;
+
+ sg_proc_sgp = create_proc_entry(sg_proc_sg_dirname,
+ S_IFDIR | S_IRUGO | S_IXUGO, NULL);
+ if (!sg_proc_sgp)
+ return 1;
+ for (k = 0; k < num_leaves; ++k) {
+ leaf = &sg_proc_leaf_arr[k];
+ mask = leaf->fops->write ? S_IRUGO | S_IWUSR : S_IRUGO;
+ pdep = create_proc_entry(leaf->name, mask, sg_proc_sgp);
+ if (pdep) {
+ leaf->fops->owner = THIS_MODULE,
+ leaf->fops->read = seq_read,
+ leaf->fops->llseek = seq_lseek,
+ pdep->proc_fops = leaf->fops;
+ }
+ }
+ return 0;
+}
+
+static void
+sg_proc_cleanup(void)
+{
+ int k;
+ int num_leaves =
+ sizeof (sg_proc_leaf_arr) / sizeof (sg_proc_leaf_arr[0]);
+
+ if (!sg_proc_sgp)
+ return;
+ for (k = 0; k < num_leaves; ++k)
+ remove_proc_entry(sg_proc_leaf_arr[k].name, sg_proc_sgp);
+ remove_proc_entry(sg_proc_sg_dirname, NULL);
+}
+
+
+static int sg_proc_seq_show_int(struct seq_file *s, void *v)
+{
+ seq_printf(s, "%d\n", *((int *)s->private));
+ return 0;
+}
+
+static int sg_proc_single_open_adio(struct inode *inode, struct file *file)
+{
+ return single_open(file, sg_proc_seq_show_int, &sg_allow_dio);
+}
+
+static ssize_t
+sg_proc_write_adio(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ int num;
+ char buff[11];
+
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ num = (count < 10) ? count : 10;
+ if (copy_from_user(buff, buffer, num))
+ return -EFAULT;
+ buff[num] = '\0';
+ sg_allow_dio = simple_strtoul(buff, NULL, 10) ? 1 : 0;
+ return count;
+}
+
+static int sg_proc_single_open_dressz(struct inode *inode, struct file *file)
+{
+ return single_open(file, sg_proc_seq_show_int, &sg_big_buff);
+}
+
+static ssize_t
+sg_proc_write_dressz(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ int num;
+ unsigned long k = ULONG_MAX;
+ char buff[11];
+
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ num = (count < 10) ? count : 10;
+ if (copy_from_user(buff, buffer, num))
+ return -EFAULT;
+ buff[num] = '\0';
+ k = simple_strtoul(buff, NULL, 10);
+ if (k <= 1048576) { /* limit "big buff" to 1 MB */
+ sg_big_buff = k;
+ return count;
+ }
+ return -ERANGE;
+}
+
+static int sg_proc_seq_show_version(struct seq_file *s, void *v)
+{
+ seq_printf(s, "%d\t%s [%s]\n", sg_version_num, SG_VERSION_STR,
+ sg_version_date);
+ return 0;
+}
+
+static int sg_proc_single_open_version(struct inode *inode, struct file *file)
+{
+ return single_open(file, sg_proc_seq_show_version, NULL);
+}
+
+static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v)
+{
+ seq_printf(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\t"
+ "online\n");
+ return 0;
+}
+
+static int sg_proc_single_open_devhdr(struct inode *inode, struct file *file)
+{
+ return single_open(file, sg_proc_seq_show_devhdr, NULL);
+}
+
+struct sg_proc_deviter {
+ loff_t index;
+ size_t max;
+};
+
+static void * dev_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct sg_proc_deviter * it = kmalloc(sizeof(*it), GFP_KERNEL);
+
+ if (! it)
+ return NULL;
+ if (NULL == sg_dev_arr)
+ goto err1;
+ it->index = *pos;
+ it->max = sg_last_dev();
+ if (it->index >= it->max)
+ goto err1;
+ return it;
+err1:
+ kfree(it);
+ return NULL;
+}
+
+static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
+
+ *pos = ++it->index;
+ return (it->index < it->max) ? it : NULL;
+}
+
+static void dev_seq_stop(struct seq_file *s, void *v)
+{
+ kfree (v);
+}
+
+static int sg_proc_open_dev(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &dev_seq_ops);
+}
+
+static int sg_proc_seq_show_dev(struct seq_file *s, void *v)
+{
+ struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
+ Sg_device *sdp;
+ struct scsi_device *scsidp;
+
+ sdp = it ? sg_get_dev(it->index) : NULL;
+ if (sdp && (scsidp = sdp->device) && (!sdp->detached))
+ seq_printf(s, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ scsidp->host->host_no, scsidp->channel,
+ scsidp->id, scsidp->lun, (int) scsidp->type,
+ 1,
+ (int) scsidp->queue_depth,
+ (int) scsidp->device_busy,
+ (int) scsi_device_online(scsidp));
+ else
+ seq_printf(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
+ return 0;
+}
+
+static int sg_proc_open_devstrs(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &devstrs_seq_ops);
+}
+
+static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v)
+{
+ struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
+ Sg_device *sdp;
+ struct scsi_device *scsidp;
+
+ sdp = it ? sg_get_dev(it->index) : NULL;
+ if (sdp && (scsidp = sdp->device) && (!sdp->detached))
+ seq_printf(s, "%8.8s\t%16.16s\t%4.4s\n",
+ scsidp->vendor, scsidp->model, scsidp->rev);
+ else
+ seq_printf(s, "<no active device>\n");
+ return 0;
+}
+
+static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
+{
+ int k, m, new_interface, blen, usg;
+ Sg_request *srp;
+ Sg_fd *fp;
+ const sg_io_hdr_t *hp;
+ const char * cp;
+
+ for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) {
+ seq_printf(s, " FD(%d): timeout=%dms bufflen=%d "
+ "(res)sgat=%d low_dma=%d\n", k + 1,
+ jiffies_to_msecs(fp->timeout),
+ fp->reserve.bufflen,
+ (int) fp->reserve.k_use_sg,
+ (int) fp->low_dma);
+ seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=%d\n",
+ (int) fp->cmd_q, (int) fp->force_packid,
+ (int) fp->keep_orphan, (int) fp->closed);
+ for (m = 0; (srp = sg_get_nth_request(fp, m)); ++m) {
+ hp = &srp->header;
+ new_interface = (hp->interface_id == '\0') ? 0 : 1;
+ if (srp->res_used) {
+ if (new_interface &&
+ (SG_FLAG_MMAP_IO & hp->flags))
+ cp = " mmap>> ";
+ else
+ cp = " rb>> ";
+ } else {
+ if (SG_INFO_DIRECT_IO_MASK & hp->info)
+ cp = " dio>> ";
+ else
+ cp = " ";
+ }
+ seq_printf(s, cp);
+ blen = srp->my_cmdp ?
+ srp->my_cmdp->sr_bufflen : srp->data.bufflen;
+ usg = srp->my_cmdp ?
+ srp->my_cmdp->sr_use_sg : srp->data.k_use_sg;
+ seq_printf(s, srp->done ?
+ ((1 == srp->done) ? "rcv:" : "fin:")
+ : (srp->my_cmdp ? "act:" : "prior:"));
+ seq_printf(s, " id=%d blen=%d",
+ srp->header.pack_id, blen);
+ if (srp->done)
+ seq_printf(s, " dur=%d", hp->duration);
+ else
+ seq_printf(s, " t_o/elap=%d/%d",
+ new_interface ? hp->timeout : jiffies_to_msecs(fp->timeout),
+ jiffies_to_msecs(hp->duration ? (jiffies - hp->duration) : 0));
+ seq_printf(s, "ms sgat=%d op=0x%02x\n", usg,
+ (int) srp->data.cmd_opcode);
+ }
+ if (0 == m)
+ seq_printf(s, " No requests active\n");
+ }
+}
+
+static int sg_proc_open_debug(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &debug_seq_ops);
+}
+
+static int sg_proc_seq_show_debug(struct seq_file *s, void *v)
+{
+ struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
+ Sg_device *sdp;
+
+ if (it && (0 == it->index)) {
+ seq_printf(s, "dev_max(currently)=%d max_active_device=%d "
+ "(origin 1)\n", sg_dev_max, (int)it->max);
+ seq_printf(s, " def_reserved_size=%d\n", sg_big_buff);
+ }
+ sdp = it ? sg_get_dev(it->index) : NULL;
+ if (sdp) {
+ struct scsi_device *scsidp = sdp->device;
+
+ if (NULL == scsidp) {
+ seq_printf(s, "device %d detached ??\n",
+ (int)it->index);
+ return 0;
+ }
+
+ if (sg_get_nth_sfp(sdp, 0)) {
+ seq_printf(s, " >>> device=%s ",
+ sdp->disk->disk_name);
+ if (sdp->detached)
+ seq_printf(s, "detached pending close ");
+ else
+ seq_printf
+ (s, "scsi%d chan=%d id=%d lun=%d em=%d",
+ scsidp->host->host_no,
+ scsidp->channel, scsidp->id,
+ scsidp->lun,
+ scsidp->host->hostt->emulated);
+ seq_printf(s, " sg_tablesize=%d excl=%d\n",
+ sdp->sg_tablesize, sdp->exclude);
+ }
+ sg_proc_debug_helper(s, sdp);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_SCSI_PROC_FS */
+
+module_init(init_sg);
+module_exit(exit_sg);
+MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR);
diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c
new file mode 100644
index 000000000000..270f2aa88faa
--- /dev/null
+++ b/drivers/scsi/sgiwd93.c
@@ -0,0 +1,337 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
+ * Copyright (C) 2001 Florian Lohoff (flo@rfc822.org)
+ * Copyright (C) 2003 Ralf Baechle (ralf@linux-mips.org)
+ *
+ * (In all truth, Jed Schimmel wrote all this code.)
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/sgi.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "wd33c93.h"
+#include "sgiwd93.h"
+
+#include <linux/stat.h>
+
+#if 0
+#define DPRINTK(args...) printk(args)
+#else
+#define DPRINTK(args...)
+#endif
+
+#define HDATA(ptr) ((struct ip22_hostdata *)((ptr)->hostdata))
+
+struct ip22_hostdata {
+ struct WD33C93_hostdata wh;
+ struct hpc_data {
+ dma_addr_t dma;
+ void * cpu;
+ } hd;
+};
+
+struct hpc_chunk {
+ struct hpc_dma_desc desc;
+ u32 _padding; /* align to quadword boundary */
+};
+
+struct Scsi_Host *sgiwd93_host;
+struct Scsi_Host *sgiwd93_host1;
+
+/* Wuff wuff, wuff, wd33c93.c, wuff wuff, object oriented, bow wow. */
+static inline void write_wd33c93_count(const wd33c93_regs regs,
+ unsigned long value)
+{
+ *regs.SASR = WD_TRANSFER_COUNT_MSB;
+ mb();
+ *regs.SCMD = ((value >> 16) & 0xff);
+ *regs.SCMD = ((value >> 8) & 0xff);
+ *regs.SCMD = ((value >> 0) & 0xff);
+ mb();
+}
+
+static inline unsigned long read_wd33c93_count(const wd33c93_regs regs)
+{
+ unsigned long value;
+
+ *regs.SASR = WD_TRANSFER_COUNT_MSB;
+ mb();
+ value = ((*regs.SCMD & 0xff) << 16);
+ value |= ((*regs.SCMD & 0xff) << 8);
+ value |= ((*regs.SCMD & 0xff) << 0);
+ mb();
+ return value;
+}
+
+static irqreturn_t sgiwd93_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host * host = (struct Scsi_Host *) dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ wd33c93_intr(host);
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static inline
+void fill_hpc_entries(struct hpc_chunk *hcp, Scsi_Cmnd *cmd, int datainp)
+{
+ unsigned long len = cmd->SCp.this_residual;
+ void *addr = cmd->SCp.ptr;
+ dma_addr_t physaddr;
+ unsigned long count;
+
+ physaddr = dma_map_single(NULL, addr, len, cmd->sc_data_direction);
+ cmd->SCp.dma_handle = physaddr;
+
+ while (len) {
+ /*
+ * even cntinfo could be up to 16383, without
+ * magic only 8192 works correctly
+ */
+ count = len > 8192 ? 8192 : len;
+ hcp->desc.pbuf = physaddr;
+ hcp->desc.cntinfo = count;
+ hcp++;
+ len -= count;
+ physaddr += count;
+ }
+
+ /*
+ * To make sure, if we trip an HPC bug, that we transfer every single
+ * byte, we tag on an extra zero length dma descriptor at the end of
+ * the chain.
+ */
+ hcp->desc.pbuf = 0;
+ hcp->desc.cntinfo = HPCDMA_EOX;
+}
+
+static int dma_setup(Scsi_Cmnd *cmd, int datainp)
+{
+ struct ip22_hostdata *hdata = HDATA(cmd->device->host);
+ struct hpc3_scsiregs *hregs =
+ (struct hpc3_scsiregs *) cmd->device->host->base;
+ struct hpc_chunk *hcp = (struct hpc_chunk *) hdata->hd.cpu;
+
+ DPRINTK("dma_setup: datainp<%d> hcp<%p> ", datainp, hcp);
+
+ hdata->wh.dma_dir = datainp;
+
+ /*
+ * wd33c93 shouldn't pass us bogus dma_setups, but it does:-( The
+ * other wd33c93 drivers deal with it the same way (which isn't that
+ * obvious). IMHO a better fix would be, not to do these dma setups
+ * in the first place.
+ */
+ if (cmd->SCp.ptr == NULL || cmd->SCp.this_residual == 0)
+ return 1;
+
+ fill_hpc_entries(hcp, cmd, datainp);
+
+ DPRINTK(" HPCGO\n");
+
+ /* Start up the HPC. */
+ hregs->ndptr = hdata->hd.dma;
+ if (datainp)
+ hregs->ctrl = HPC3_SCTRL_ACTIVE;
+ else
+ hregs->ctrl = HPC3_SCTRL_ACTIVE | HPC3_SCTRL_DIR;
+
+ return 0;
+}
+
+static void dma_stop(struct Scsi_Host *instance, Scsi_Cmnd *SCpnt,
+ int status)
+{
+ struct ip22_hostdata *hdata = HDATA(instance);
+ struct hpc3_scsiregs *hregs;
+
+ if (!SCpnt)
+ return;
+
+ hregs = (struct hpc3_scsiregs *) SCpnt->device->host->base;
+
+ DPRINTK("dma_stop: status<%d> ", status);
+
+ /* First stop the HPC and flush it's FIFO. */
+ if (hdata->wh.dma_dir) {
+ hregs->ctrl |= HPC3_SCTRL_FLUSH;
+ while (hregs->ctrl & HPC3_SCTRL_ACTIVE)
+ barrier();
+ }
+ hregs->ctrl = 0;
+ dma_unmap_single(NULL, SCpnt->SCp.dma_handle, SCpnt->SCp.this_residual,
+ SCpnt->sc_data_direction);
+
+ DPRINTK("\n");
+}
+
+void sgiwd93_reset(unsigned long base)
+{
+ struct hpc3_scsiregs *hregs = (struct hpc3_scsiregs *) base;
+
+ hregs->ctrl = HPC3_SCTRL_CRESET;
+ udelay(50);
+ hregs->ctrl = 0;
+}
+
+static inline void init_hpc_chain(struct hpc_data *hd)
+{
+ struct hpc_chunk *hcp = (struct hpc_chunk *) hd->cpu;
+ struct hpc_chunk *dma = (struct hpc_chunk *) hd->dma;
+ unsigned long start, end;
+
+ start = (unsigned long) hcp;
+ end = start + PAGE_SIZE;
+ while (start < end) {
+ hcp->desc.pnext = (u32) (dma + 1);
+ hcp->desc.cntinfo = HPCDMA_EOX;
+ hcp++; dma++;
+ start += sizeof(struct hpc_chunk);
+ };
+ hcp--;
+ hcp->desc.pnext = hd->dma;
+}
+
+static struct Scsi_Host * __init sgiwd93_setup_scsi(
+ Scsi_Host_Template *SGIblows, int unit, int irq,
+ struct hpc3_scsiregs *hregs, unsigned char *wdregs)
+{
+ struct ip22_hostdata *hdata;
+ struct Scsi_Host *host;
+ wd33c93_regs regs;
+
+ host = scsi_register(SGIblows, sizeof(struct ip22_hostdata));
+ if (!host)
+ return NULL;
+
+ host->base = (unsigned long) hregs;
+ host->irq = irq;
+
+ hdata = HDATA(host);
+ hdata->hd.cpu = dma_alloc_coherent(NULL, PAGE_SIZE, &hdata->hd.dma,
+ GFP_KERNEL);
+ if (!hdata->hd.cpu) {
+ printk(KERN_WARNING "sgiwd93: Could not allocate memory for "
+ "host %d buffer.\n", unit);
+ goto out_unregister;
+ }
+ init_hpc_chain(&hdata->hd);
+
+ regs.SASR = wdregs + 3;
+ regs.SCMD = wdregs + 7;
+
+ wd33c93_init(host, regs, dma_setup, dma_stop, WD33C93_FS_16_20);
+
+ hdata->wh.no_sync = 0;
+
+ if (request_irq(irq, sgiwd93_intr, 0, "SGI WD93", (void *) host)) {
+ printk(KERN_WARNING "sgiwd93: Could not register irq %d "
+ "for host %d.\n", irq, unit);
+ goto out_free;
+ }
+ return host;
+
+out_free:
+ dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma);
+ wd33c93_release();
+
+out_unregister:
+ scsi_unregister(host);
+
+ return NULL;
+}
+
+int __init sgiwd93_detect(Scsi_Host_Template *SGIblows)
+{
+ int found = 0;
+
+ SGIblows->proc_name = "SGIWD93";
+ sgiwd93_host = sgiwd93_setup_scsi(SGIblows, 0, SGI_WD93_0_IRQ,
+ &hpc3c0->scsi_chan0,
+ (unsigned char *)hpc3c0->scsi0_ext);
+ if (sgiwd93_host)
+ found++;
+
+ /* Set up second controller on the Indigo2 */
+ if (ip22_is_fullhouse()) {
+ sgiwd93_host1 = sgiwd93_setup_scsi(SGIblows, 1, SGI_WD93_1_IRQ,
+ &hpc3c0->scsi_chan1,
+ (unsigned char *)hpc3c0->scsi1_ext);
+ if (sgiwd93_host1)
+ found++;
+ }
+
+ return found;
+}
+
+int sgiwd93_release(struct Scsi_Host *instance)
+{
+ struct ip22_hostdata *hdata = HDATA(instance);
+ int irq = 0;
+
+ if (sgiwd93_host && sgiwd93_host == instance)
+ irq = SGI_WD93_0_IRQ;
+ else if (sgiwd93_host1 && sgiwd93_host1 == instance)
+ irq = SGI_WD93_1_IRQ;
+
+ free_irq(irq, sgiwd93_intr);
+ dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma);
+ wd33c93_release();
+
+ return 1;
+}
+
+static int sgiwd93_bus_reset(Scsi_Cmnd *cmd)
+{
+ /* FIXME perform bus-specific reset */
+ wd33c93_host_reset(cmd);
+ return SUCCESS;
+}
+
+/*
+ * Kludge alert - the SCSI code calls the abort and reset method with int
+ * arguments not with pointers. So this is going to blow up beautyfully
+ * on 64-bit systems with memory outside the compat address spaces.
+ */
+static Scsi_Host_Template driver_template = {
+ .proc_name = "SGIWD93",
+ .name = "SGI WD93",
+ .detect = sgiwd93_detect,
+ .release = sgiwd93_release,
+ .queuecommand = wd33c93_queuecommand,
+ .eh_abort_handler = wd33c93_abort,
+ .eh_bus_reset_handler = sgiwd93_bus_reset,
+ .eh_host_reset_handler = wd33c93_host_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/sgiwd93.h b/drivers/scsi/sgiwd93.h
new file mode 100644
index 000000000000..981d0b7a85d4
--- /dev/null
+++ b/drivers/scsi/sgiwd93.h
@@ -0,0 +1,24 @@
+/* $Id: sgiwd93.h,v 1.5 1998/08/25 09:18:50 ralf Exp $
+ * sgiwd93.h: SGI WD93 scsi definitions.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+#ifndef _SGIWD93_H
+#define _SGIWD93_H
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 8
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+int sgiwd93_detect(Scsi_Host_Template *);
+int sgiwd93_release(struct Scsi_Host *instance);
+const char *wd33c93_info(void);
+int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd33c93_abort(Scsi_Cmnd *);
+int wd33c93_host_reset(Scsi_Cmnd * SCpnt);
+
+#endif /* !(_SGIWD93_H) */
diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c
new file mode 100644
index 000000000000..63bf2aecbc57
--- /dev/null
+++ b/drivers/scsi/sim710.c
@@ -0,0 +1,372 @@
+/*
+ * sim710.c - Copyright (C) 1999 Richard Hirst <richard@sleepie.demon.co.uk>
+ *
+ *----------------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ *----------------------------------------------------------------------------
+ *
+ * MCA card detection code by Trent McNair.
+ * Fixes to not explicitly nul bss data from Xavier Bestel.
+ * Some multiboard fixes from Rolf Eike Beer.
+ * Auto probing of EISA config space from Trevor Hemsley.
+ *
+ * Rewritten to use 53c700.c by James.Bottomley@SteelEye.com
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/blkdev.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/mca.h>
+#include <linux/eisa.h>
+#include <linux/interrupt.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+#include "53c700.h"
+
+
+/* Must be enough for both EISA and MCA */
+#define MAX_SLOTS 8
+static __u8 __initdata id_array[MAX_SLOTS] = { [0 ... MAX_SLOTS-1] = 7 };
+
+static char *sim710; /* command line passed by insmod */
+
+MODULE_AUTHOR("Richard Hirst");
+MODULE_DESCRIPTION("Simple NCR53C710 driver");
+MODULE_LICENSE("GPL");
+
+module_param(sim710, charp, 0);
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+static __init int
+param_setup(char *str)
+{
+ char *pos = str, *next;
+ int slot = -1;
+
+ while(pos != NULL && (next = strchr(pos, ':')) != NULL) {
+ int val = (int)simple_strtoul(++next, NULL, 0);
+
+ if(!strncmp(pos, "slot:", 5))
+ slot = val;
+ else if(!strncmp(pos, "id:", 3)) {
+ if(slot == -1) {
+ printk(KERN_WARNING "sim710: Must specify slot for id parameter\n");
+ } else if(slot > MAX_SLOTS) {
+ printk(KERN_WARNING "sim710: Illegal slot %d for id %d\n", slot, val);
+ } else {
+ id_array[slot] = val;
+ }
+ }
+ if((pos = strchr(pos, ARG_SEP)) != NULL)
+ pos++;
+ }
+ return 1;
+}
+__setup("sim710=", param_setup);
+
+static struct scsi_host_template sim710_driver_template = {
+ .name = "LSI (Symbios) 710 MCA/EISA",
+ .proc_name = "sim710",
+ .this_id = 7,
+ .module = THIS_MODULE,
+};
+
+static __devinit int
+sim710_probe_common(struct device *dev, unsigned long base_addr,
+ int irq, int clock, int differential, int scsi_id)
+{
+ struct Scsi_Host * host = NULL;
+ struct NCR_700_Host_Parameters *hostdata =
+ kmalloc(sizeof(struct NCR_700_Host_Parameters), GFP_KERNEL);
+
+ printk(KERN_NOTICE "sim710: %s\n", dev->bus_id);
+ printk(KERN_NOTICE "sim710: irq = %d, clock = %d, base = 0x%lx, scsi_id = %d\n",
+ irq, clock, base_addr, scsi_id);
+
+ if(hostdata == NULL) {
+ printk(KERN_ERR "sim710: Failed to allocate host data\n");
+ goto out;
+ }
+ memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters));
+
+ if(request_region(base_addr, 64, "sim710") == NULL) {
+ printk(KERN_ERR "sim710: Failed to reserve IO region 0x%lx\n",
+ base_addr);
+ goto out_free;
+ }
+
+ /* Fill in the three required pieces of hostdata */
+ hostdata->base = base_addr;
+ hostdata->differential = differential;
+ hostdata->clock = clock;
+ hostdata->chip710 = 1;
+ NCR_700_set_io_mapped(hostdata);
+
+ /* and register the chip */
+ if((host = NCR_700_detect(&sim710_driver_template, hostdata, dev))
+ == NULL) {
+ printk(KERN_ERR "sim710: No host detected; card configuration problem?\n");
+ goto out_release;
+ }
+ host->this_id = scsi_id;
+ host->irq = irq;
+ if (request_irq(irq, NCR_700_intr, SA_SHIRQ, "sim710", host)) {
+ printk(KERN_ERR "sim710: request_irq failed\n");
+ goto out_put_host;
+ }
+
+ scsi_scan_host(host);
+
+ return 0;
+
+ out_put_host:
+ scsi_host_put(host);
+ out_release:
+ release_region(host->base, 64);
+ out_free:
+ kfree(hostdata);
+ out:
+ return -ENODEV;
+}
+
+static __devexit int
+sim710_device_remove(struct device *dev)
+{
+ struct Scsi_Host *host = dev_to_shost(dev);
+ struct NCR_700_Host_Parameters *hostdata =
+ (struct NCR_700_Host_Parameters *)host->hostdata[0];
+
+ scsi_remove_host(host);
+ NCR_700_release(host);
+ kfree(hostdata);
+ free_irq(host->irq, host);
+ return 0;
+}
+
+#ifdef CONFIG_MCA
+
+/* CARD ID 01BB and 01BA use the same pos values */
+#define MCA_01BB_IO_PORTS { 0x0000, 0x0000, 0x0800, 0x0C00, 0x1000, 0x1400, \
+ 0x1800, 0x1C00, 0x2000, 0x2400, 0x2800, \
+ 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, \
+ 0x4000, 0x4400, 0x4800, 0x4C00, 0x5000 }
+
+#define MCA_01BB_IRQS { 3, 5, 11, 14 }
+
+/* CARD ID 004f */
+#define MCA_004F_IO_PORTS { 0x0000, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600 }
+#define MCA_004F_IRQS { 5, 9, 14 }
+
+static short sim710_mca_id_table[] = { 0x01bb, 0x01ba, 0x004f, 0};
+
+static __init int
+sim710_mca_probe(struct device *dev)
+{
+ struct mca_device *mca_dev = to_mca_device(dev);
+ int slot = mca_dev->slot;
+ int pos[3];
+ unsigned int base;
+ int irq_vector;
+ short id = sim710_mca_id_table[mca_dev->index];
+ static int io_004f_by_pos[] = MCA_004F_IO_PORTS;
+ static int irq_004f_by_pos[] = MCA_004F_IRQS;
+ static int io_01bb_by_pos[] = MCA_01BB_IO_PORTS;
+ static int irq_01bb_by_pos[] = MCA_01BB_IRQS;
+ char *name;
+ int clock;
+
+ pos[0] = mca_device_read_stored_pos(mca_dev, 2);
+ pos[1] = mca_device_read_stored_pos(mca_dev, 3);
+ pos[2] = mca_device_read_stored_pos(mca_dev, 4);
+
+ /*
+ * 01BB & 01BA port base by bits 7,6,5,4,3,2 in pos[2]
+ *
+ * 000000 <disabled> 001010 0x2800
+ * 000001 <invalid> 001011 0x2C00
+ * 000010 0x0800 001100 0x3000
+ * 000011 0x0C00 001101 0x3400
+ * 000100 0x1000 001110 0x3800
+ * 000101 0x1400 001111 0x3C00
+ * 000110 0x1800 010000 0x4000
+ * 000111 0x1C00 010001 0x4400
+ * 001000 0x2000 010010 0x4800
+ * 001001 0x2400 010011 0x4C00
+ * 010100 0x5000
+ *
+ * 00F4 port base by bits 3,2,1 in pos[0]
+ *
+ * 000 <disabled> 001 0x200
+ * 010 0x300 011 0x400
+ * 100 0x500 101 0x600
+ *
+ * 01BB & 01BA IRQ is specified in pos[0] bits 7 and 6:
+ *
+ * 00 3 10 11
+ * 01 5 11 14
+ *
+ * 00F4 IRQ specified by bits 6,5,4 in pos[0]
+ *
+ * 100 5 101 9
+ * 110 14
+ */
+
+ if (id == 0x01bb || id == 0x01ba) {
+ base = io_01bb_by_pos[(pos[2] & 0xFC) >> 2];
+ irq_vector =
+ irq_01bb_by_pos[((pos[0] & 0xC0) >> 6)];
+
+ clock = 50;
+ if (id == 0x01bb)
+ name = "NCR 3360/3430 SCSI SubSystem";
+ else
+ name = "NCR Dual SIOP SCSI Host Adapter Board";
+ } else if ( id == 0x004f ) {
+ base = io_004f_by_pos[((pos[0] & 0x0E) >> 1)];
+ irq_vector =
+ irq_004f_by_pos[((pos[0] & 0x70) >> 4) - 4];
+ clock = 50;
+ name = "NCR 53c710 SCSI Host Adapter Board";
+ } else {
+ return -ENODEV;
+ }
+ mca_device_set_name(mca_dev, name);
+ mca_device_set_claim(mca_dev, 1);
+ base = mca_device_transform_ioport(mca_dev, base);
+ irq_vector = mca_device_transform_irq(mca_dev, irq_vector);
+
+ return sim710_probe_common(dev, base, irq_vector, clock,
+ 0, id_array[slot]);
+}
+
+static struct mca_driver sim710_mca_driver = {
+ .id_table = sim710_mca_id_table,
+ .driver = {
+ .name = "sim710",
+ .bus = &mca_bus_type,
+ .probe = sim710_mca_probe,
+ .remove = __devexit_p(sim710_device_remove),
+ },
+};
+
+#endif /* CONFIG_MCA */
+
+#ifdef CONFIG_EISA
+static struct eisa_device_id sim710_eisa_ids[] = {
+ { "CPQ4410" },
+ { "CPQ4411" },
+ { "HWP0C80" },
+ { "" }
+};
+
+static __init int
+sim710_eisa_probe(struct device *dev)
+{
+ struct eisa_device *edev = to_eisa_device(dev);
+ unsigned long io_addr = edev->base_addr;
+ char eisa_cpq_irqs[] = { 11, 14, 15, 10, 9, 0 };
+ char eisa_hwp_irqs[] = { 3, 4, 5, 7, 12, 10, 11, 0};
+ char *eisa_irqs;
+ unsigned char irq_index;
+ unsigned char irq, differential = 0, scsi_id = 7;
+
+ if(strcmp(edev->id.sig, "HWP0C80") == 0) {
+ __u8 val;
+ eisa_irqs = eisa_hwp_irqs;
+ irq_index = (inb(io_addr + 0xc85) & 0x7) - 1;
+
+ val = inb(io_addr + 0x4);
+ scsi_id = ffs(val) - 1;
+
+ if(scsi_id > 7 || (val & ~(1<<scsi_id)) != 0) {
+ printk(KERN_ERR "sim710.c, EISA card %s has incorrect scsi_id, setting to 7\n", dev->bus_id);
+ scsi_id = 7;
+ }
+ } else {
+ eisa_irqs = eisa_cpq_irqs;
+ irq_index = inb(io_addr + 0xc88) & 0x07;
+ }
+
+ if(irq_index >= strlen(eisa_irqs)) {
+ printk("sim710.c: irq nasty\n");
+ return -ENODEV;
+ }
+
+ irq = eisa_irqs[irq_index];
+
+ return sim710_probe_common(dev, io_addr, irq, 50,
+ differential, scsi_id);
+}
+
+static struct eisa_driver sim710_eisa_driver = {
+ .id_table = sim710_eisa_ids,
+ .driver = {
+ .name = "sim710",
+ .probe = sim710_eisa_probe,
+ .remove = __devexit_p(sim710_device_remove),
+ },
+};
+#endif /* CONFIG_EISA */
+
+static int __init sim710_init(void)
+{
+ int err = -ENODEV;
+
+#ifdef MODULE
+ if (sim710)
+ param_setup(sim710);
+#endif
+
+#ifdef CONFIG_MCA
+ err = mca_register_driver(&sim710_mca_driver);
+#endif
+
+#ifdef CONFIG_EISA
+ err = eisa_driver_register(&sim710_eisa_driver);
+#endif
+ /* FIXME: what we'd really like to return here is -ENODEV if
+ * no devices have actually been found. Instead, the err
+ * above actually only reports problems with kobject_register,
+ * so for the moment return success */
+
+ return 0;
+}
+
+static void __exit sim710_exit(void)
+{
+#ifdef CONFIG_MCA
+ if (MCA_bus)
+ mca_unregister_driver(&sim710_mca_driver);
+#endif
+
+#ifdef CONFIG_EISA
+ eisa_driver_unregister(&sim710_eisa_driver);
+#endif
+}
+
+module_init(sim710_init);
+module_exit(sim710_exit);
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
new file mode 100644
index 000000000000..2f259f249522
--- /dev/null
+++ b/drivers/scsi/sr.c
@@ -0,0 +1,965 @@
+/*
+ * sr.c Copyright (C) 1992 David Giller
+ * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
+ *
+ * adapted from:
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Linux scsi disk driver by
+ * Drew Eckhardt <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@andante.org to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@andante.org to support loadable
+ * low-level scsi drivers.
+ *
+ * Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
+ * provide auto-eject.
+ *
+ * Modified by Gerd Knorr <kraxel@cs.tu-berlin.de> to support the
+ * generic cdrom interface
+ *
+ * Modified by Jens Axboe <axboe@suse.de> - Uniform sr_packet()
+ * interface, capabilities probe additions, ioctl cleanups, etc.
+ *
+ * Modified by Richard Gooch <rgooch@atnf.csiro.au> to support devfs
+ *
+ * Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
+ * transparently and lose the GHOST hack
+ *
+ * Modified by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * check resource allocation in sr_init and some cleanups
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/bio.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h> /* For the door lock/unlock commands */
+#include <scsi/scsi_request.h>
+
+#include "scsi_logging.h"
+#include "sr.h"
+
+
+#define SR_DISKS 256
+
+#define MAX_RETRIES 3
+#define SR_TIMEOUT (30 * HZ)
+#define SR_CAPABILITIES \
+ (CDC_CLOSE_TRAY|CDC_OPEN_TRAY|CDC_LOCK|CDC_SELECT_SPEED| \
+ CDC_SELECT_DISC|CDC_MULTI_SESSION|CDC_MCN|CDC_MEDIA_CHANGED| \
+ CDC_PLAY_AUDIO|CDC_RESET|CDC_IOCTLS|CDC_DRIVE_STATUS| \
+ CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_DVD_RAM|CDC_GENERIC_PACKET| \
+ CDC_MRW|CDC_MRW_W|CDC_RAM)
+
+static int sr_probe(struct device *);
+static int sr_remove(struct device *);
+static int sr_init_command(struct scsi_cmnd *);
+
+static struct scsi_driver sr_template = {
+ .owner = THIS_MODULE,
+ .gendrv = {
+ .name = "sr",
+ .probe = sr_probe,
+ .remove = sr_remove,
+ },
+ .init_command = sr_init_command,
+};
+
+static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
+static DEFINE_SPINLOCK(sr_index_lock);
+
+/* This semaphore is used to mediate the 0->1 reference get in the
+ * face of object destruction (i.e. we can't allow a get on an
+ * object after last put) */
+static DECLARE_MUTEX(sr_ref_sem);
+
+static int sr_open(struct cdrom_device_info *, int);
+static void sr_release(struct cdrom_device_info *);
+
+static void get_sectorsize(struct scsi_cd *);
+static void get_capabilities(struct scsi_cd *);
+
+static int sr_media_change(struct cdrom_device_info *, int);
+static int sr_packet(struct cdrom_device_info *, struct packet_command *);
+
+static struct cdrom_device_ops sr_dops = {
+ .open = sr_open,
+ .release = sr_release,
+ .drive_status = sr_drive_status,
+ .media_changed = sr_media_change,
+ .tray_move = sr_tray_move,
+ .lock_door = sr_lock_door,
+ .select_speed = sr_select_speed,
+ .get_last_session = sr_get_last_session,
+ .get_mcn = sr_get_mcn,
+ .reset = sr_reset,
+ .audio_ioctl = sr_audio_ioctl,
+ .dev_ioctl = sr_dev_ioctl,
+ .capability = SR_CAPABILITIES,
+ .generic_packet = sr_packet,
+};
+
+static void sr_kref_release(struct kref *kref);
+
+static inline struct scsi_cd *scsi_cd(struct gendisk *disk)
+{
+ return container_of(disk->private_data, struct scsi_cd, driver);
+}
+
+/*
+ * The get and put routines for the struct scsi_cd. Note this entity
+ * has a scsi_device pointer and owns a reference to this.
+ */
+static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk)
+{
+ struct scsi_cd *cd = NULL;
+
+ down(&sr_ref_sem);
+ if (disk->private_data == NULL)
+ goto out;
+ cd = scsi_cd(disk);
+ kref_get(&cd->kref);
+ if (scsi_device_get(cd->device))
+ goto out_put;
+ goto out;
+
+ out_put:
+ kref_put(&cd->kref, sr_kref_release);
+ cd = NULL;
+ out:
+ up(&sr_ref_sem);
+ return cd;
+}
+
+static inline void scsi_cd_put(struct scsi_cd *cd)
+{
+ struct scsi_device *sdev = cd->device;
+
+ down(&sr_ref_sem);
+ kref_put(&cd->kref, sr_kref_release);
+ scsi_device_put(sdev);
+ up(&sr_ref_sem);
+}
+
+/*
+ * This function checks to see if the media has been changed in the
+ * CDROM drive. It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it. We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit. If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
+ */
+
+int sr_media_change(struct cdrom_device_info *cdi, int slot)
+{
+ struct scsi_cd *cd = cdi->handle;
+ int retval;
+
+ if (CDSL_CURRENT != slot) {
+ /* no changer support */
+ return -EINVAL;
+ }
+
+ retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES);
+ if (retval) {
+ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+ cd->device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ };
+
+ retval = cd->device->changed;
+ cd->device->changed = 0;
+ /* If the disk changed, the capacity will now be different,
+ * so we force a re-read of this information */
+ if (retval) {
+ /* check multisession offset etc */
+ sr_cd_check(cdi);
+
+ /*
+ * If the disk changed, the capacity will now be different,
+ * so we force a re-read of this information
+ * Force 2048 for the sector size so that filesystems won't
+ * be trying to use something that is too small if the disc
+ * has changed.
+ */
+ cd->needs_sector_size = 1;
+ cd->device->sector_size = 2048;
+ }
+ return retval;
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver.
+ *
+ * It will be notified on the end of a SCSI read / write, and will take on
+ * of several actions based on success or failure.
+ */
+static void rw_intr(struct scsi_cmnd * SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->bufflen;
+ int good_bytes = (result == 0 ? this_count : 0);
+ int block_sectors = 0;
+ long error_sector;
+ struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
+
+#ifdef DEBUG
+ printk("sr.c done: %x\n", result);
+#endif
+
+ /*
+ * Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial
+ * success. Since this is a relatively rare error condition, no
+ * care is taken to avoid unnecessary additional work such as
+ * memcpy's that could be avoided.
+ */
+ if (driver_byte(result) != 0 && /* An error occurred */
+ (SCpnt->sense_buffer[0] & 0x7f) == 0x70) { /* Sense current */
+ switch (SCpnt->sense_buffer[2]) {
+ case MEDIUM_ERROR:
+ case VOLUME_OVERFLOW:
+ case ILLEGAL_REQUEST:
+ if (!(SCpnt->sense_buffer[0] & 0x90))
+ break;
+ if (!blk_fs_request(SCpnt->request))
+ break;
+ error_sector = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) |
+ SCpnt->sense_buffer[6];
+ if (SCpnt->request->bio != NULL)
+ block_sectors =
+ bio_sectors(SCpnt->request->bio);
+ if (block_sectors < 4)
+ block_sectors = 4;
+ if (cd->device->sector_size == 2048)
+ error_sector <<= 2;
+ error_sector &= ~(block_sectors - 1);
+ good_bytes = (error_sector - SCpnt->request->sector) << 9;
+ if (good_bytes < 0 || good_bytes >= this_count)
+ good_bytes = 0;
+ /*
+ * The SCSI specification allows for the value
+ * returned by READ CAPACITY to be up to 75 2K
+ * sectors past the last readable block.
+ * Therefore, if we hit a medium error within the
+ * last 75 2K sectors, we decrease the saved size
+ * value.
+ */
+ if (error_sector < get_capacity(cd->disk) &&
+ cd->capacity - error_sector < 4 * 75)
+ set_capacity(cd->disk, error_sector);
+ break;
+
+ case RECOVERED_ERROR:
+
+ /*
+ * An error occured, but it recovered. Inform the
+ * user, but make sure that it's not treated as a
+ * hard error.
+ */
+ scsi_print_sense("sr", SCpnt);
+ SCpnt->result = 0;
+ SCpnt->sense_buffer[0] = 0x0;
+ good_bytes = this_count;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * This calls the generic completion function, now that we know
+ * how many actual sectors finished, and how many sectors we need
+ * to say have failed.
+ */
+ scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
+}
+
+static int sr_init_command(struct scsi_cmnd * SCpnt)
+{
+ int block=0, this_count, s_size, timeout = SR_TIMEOUT;
+ struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
+
+ SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %s, block = %d\n",
+ cd->disk->disk_name, block));
+
+ if (!cd->device || !scsi_device_online(cd->device)) {
+ SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n",
+ SCpnt->request->nr_sectors));
+ SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
+ return 0;
+ }
+
+ if (cd->device->changed) {
+ /*
+ * quietly refuse to do anything to a changed disc until the
+ * changed bit has been reset
+ */
+ return 0;
+ }
+
+ /*
+ * these are already setup, just copy cdb basically
+ */
+ if (SCpnt->request->flags & REQ_BLOCK_PC) {
+ struct request *rq = SCpnt->request;
+
+ if (sizeof(rq->cmd) > sizeof(SCpnt->cmnd))
+ return 0;
+
+ memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd));
+ if (!rq->data_len)
+ SCpnt->sc_data_direction = DMA_NONE;
+ else if (rq_data_dir(rq) == WRITE)
+ SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ else
+ SCpnt->sc_data_direction = DMA_FROM_DEVICE;
+
+ this_count = rq->data_len;
+ if (rq->timeout)
+ timeout = rq->timeout;
+
+ SCpnt->transfersize = rq->data_len;
+ goto queue;
+ }
+
+ if (!(SCpnt->request->flags & REQ_CMD)) {
+ blk_dump_rq_flags(SCpnt->request, "sr unsup command");
+ return 0;
+ }
+
+ /*
+ * we do lazy blocksize switching (when reading XA sectors,
+ * see CDROMREADMODE2 ioctl)
+ */
+ s_size = cd->device->sector_size;
+ if (s_size > 2048) {
+ if (!in_interrupt())
+ sr_set_blocklength(cd, 2048);
+ else
+ printk("sr: can't switch blocksize: in interrupt\n");
+ }
+
+ if (s_size != 512 && s_size != 1024 && s_size != 2048) {
+ printk("sr: bad sector size %d\n", s_size);
+ return 0;
+ }
+
+ if (rq_data_dir(SCpnt->request) == WRITE) {
+ if (!cd->device->writeable)
+ return 0;
+ SCpnt->cmnd[0] = WRITE_10;
+ SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ cd->cdi.media_written = 1;
+ } else if (rq_data_dir(SCpnt->request) == READ) {
+ SCpnt->cmnd[0] = READ_10;
+ SCpnt->sc_data_direction = DMA_FROM_DEVICE;
+ } else {
+ blk_dump_rq_flags(SCpnt->request, "Unknown sr command");
+ return 0;
+ }
+
+ {
+ struct scatterlist *sg = SCpnt->request_buffer;
+ int i, size = 0;
+ for (i = 0; i < SCpnt->use_sg; i++)
+ size += sg[i].length;
+
+ if (size != SCpnt->request_bufflen && SCpnt->use_sg) {
+ printk(KERN_ERR "sr: mismatch count %d, bytes %d\n",
+ size, SCpnt->request_bufflen);
+ if (SCpnt->request_bufflen > size)
+ SCpnt->request_bufflen = SCpnt->bufflen = size;
+ }
+ }
+
+ /*
+ * request doesn't start on hw block boundary, add scatter pads
+ */
+ if (((unsigned int)SCpnt->request->sector % (s_size >> 9)) ||
+ (SCpnt->request_bufflen % s_size)) {
+ printk("sr: unaligned transfer\n");
+ return 0;
+ }
+
+ this_count = (SCpnt->request_bufflen >> 9) / (s_size >> 9);
+
+
+ SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
+ cd->cdi.name,
+ (rq_data_dir(SCpnt->request) == WRITE) ?
+ "writing" : "reading",
+ this_count, SCpnt->request->nr_sectors));
+
+ SCpnt->cmnd[1] = 0;
+ block = (unsigned int)SCpnt->request->sector / (s_size >> 9);
+
+ if (this_count > 0xffff) {
+ this_count = 0xffff;
+ SCpnt->request_bufflen = SCpnt->bufflen =
+ this_count * s_size;
+ }
+
+ SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
+ SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
+ SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
+ SCpnt->cmnd[5] = (unsigned char) block & 0xff;
+ SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0;
+ SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
+ SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
+
+ /*
+ * We shouldn't disconnect in the middle of a sector, so with a dumb
+ * host adapter, it's safe to assume that we can at least transfer
+ * this many bytes between each connect / disconnect.
+ */
+ SCpnt->transfersize = cd->device->sector_size;
+ SCpnt->underflow = this_count << 9;
+
+queue:
+ SCpnt->allowed = MAX_RETRIES;
+ SCpnt->timeout_per_command = timeout;
+
+ /*
+ * This is the completion routine we use. This is matched in terms
+ * of capability to this function.
+ */
+ SCpnt->done = rw_intr;
+
+ /*
+ * This indicates that the command is ready from our end to be
+ * queued.
+ */
+ return 1;
+}
+
+static int sr_block_open(struct inode *inode, struct file *file)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
+ int ret = 0;
+
+ if(!(cd = scsi_cd_get(disk)))
+ return -ENXIO;
+
+ if((ret = cdrom_open(&cd->cdi, inode, file)) != 0)
+ scsi_cd_put(cd);
+
+ return ret;
+}
+
+static int sr_block_release(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
+ ret = cdrom_release(&cd->cdi, file);
+ if(ret)
+ return ret;
+
+ scsi_cd_put(cd);
+
+ return 0;
+}
+
+static int sr_block_ioctl(struct inode *inode, struct file *file, unsigned cmd,
+ unsigned long arg)
+{
+ struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk);
+ struct scsi_device *sdev = cd->device;
+
+ /*
+ * Send SCSI addressing ioctls directly to mid level, send other
+ * ioctls to cdrom/block level.
+ */
+ switch (cmd) {
+ case SCSI_IOCTL_GET_IDLUN:
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ return scsi_ioctl(sdev, cmd, (void __user *)arg);
+ }
+ return cdrom_ioctl(file, &cd->cdi, inode, cmd, arg);
+}
+
+static int sr_block_media_changed(struct gendisk *disk)
+{
+ struct scsi_cd *cd = scsi_cd(disk);
+ return cdrom_media_changed(&cd->cdi);
+}
+
+static struct block_device_operations sr_bdops =
+{
+ .owner = THIS_MODULE,
+ .open = sr_block_open,
+ .release = sr_block_release,
+ .ioctl = sr_block_ioctl,
+ .media_changed = sr_block_media_changed,
+ /*
+ * No compat_ioctl for now because sr_block_ioctl never
+ * seems to pass arbitary ioctls down to host drivers.
+ */
+};
+
+static int sr_open(struct cdrom_device_info *cdi, int purpose)
+{
+ struct scsi_cd *cd = cdi->handle;
+ struct scsi_device *sdev = cd->device;
+ int retval;
+
+ /*
+ * If the device is in error recovery, wait until it is done.
+ * If the device is offline, then disallow any access to it.
+ */
+ retval = -ENXIO;
+ if (!scsi_block_when_processing_errors(sdev))
+ goto error_out;
+
+ /*
+ * If this device did not have media in the drive at boot time, then
+ * we would have been unable to get the sector size. Check to see if
+ * this is the case, and try again.
+ */
+ if (cd->needs_sector_size)
+ get_sectorsize(cd);
+ return 0;
+
+error_out:
+ return retval;
+}
+
+static void sr_release(struct cdrom_device_info *cdi)
+{
+ struct scsi_cd *cd = cdi->handle;
+
+ if (cd->device->sector_size > 2048)
+ sr_set_blocklength(cd, 2048);
+
+}
+
+static int sr_probe(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct gendisk *disk;
+ struct scsi_cd *cd;
+ int minor, error;
+
+ error = -ENODEV;
+ if (sdev->type != TYPE_ROM && sdev->type != TYPE_WORM)
+ goto fail;
+
+ error = -ENOMEM;
+ cd = kmalloc(sizeof(*cd), GFP_KERNEL);
+ if (!cd)
+ goto fail;
+ memset(cd, 0, sizeof(*cd));
+
+ kref_init(&cd->kref);
+
+ disk = alloc_disk(1);
+ if (!disk)
+ goto fail_free;
+
+ spin_lock(&sr_index_lock);
+ minor = find_first_zero_bit(sr_index_bits, SR_DISKS);
+ if (minor == SR_DISKS) {
+ spin_unlock(&sr_index_lock);
+ error = -EBUSY;
+ goto fail_put;
+ }
+ __set_bit(minor, sr_index_bits);
+ spin_unlock(&sr_index_lock);
+
+ disk->major = SCSI_CDROM_MAJOR;
+ disk->first_minor = minor;
+ sprintf(disk->disk_name, "sr%d", minor);
+ disk->fops = &sr_bdops;
+ disk->flags = GENHD_FL_CD;
+
+ cd->device = sdev;
+ cd->disk = disk;
+ cd->driver = &sr_template;
+ cd->disk = disk;
+ cd->capacity = 0x1fffff;
+ cd->needs_sector_size = 1;
+ cd->device->changed = 1; /* force recheck CD type */
+ cd->use = 1;
+ cd->readcd_known = 0;
+ cd->readcd_cdda = 0;
+
+ cd->cdi.ops = &sr_dops;
+ cd->cdi.handle = cd;
+ cd->cdi.mask = 0;
+ cd->cdi.capacity = 1;
+ sprintf(cd->cdi.name, "sr%d", minor);
+
+ sdev->sector_size = 2048; /* A guess, just in case */
+
+ /* FIXME: need to handle a get_capabilities failure properly ?? */
+ get_capabilities(cd);
+ sr_vendor_init(cd);
+
+ snprintf(disk->devfs_name, sizeof(disk->devfs_name),
+ "%s/cd", sdev->devfs_name);
+ disk->driverfs_dev = &sdev->sdev_gendev;
+ set_capacity(disk, cd->capacity);
+ disk->private_data = &cd->driver;
+ disk->queue = sdev->request_queue;
+ cd->cdi.disk = disk;
+
+ if (register_cdrom(&cd->cdi))
+ goto fail_put;
+
+ dev_set_drvdata(dev, cd);
+ disk->flags |= GENHD_FL_REMOVABLE;
+ add_disk(disk);
+
+ printk(KERN_DEBUG
+ "Attached scsi CD-ROM %s at scsi%d, channel %d, id %d, lun %d\n",
+ cd->cdi.name, sdev->host->host_no, sdev->channel,
+ sdev->id, sdev->lun);
+ return 0;
+
+fail_put:
+ put_disk(disk);
+fail_free:
+ kfree(cd);
+fail:
+ return error;
+}
+
+
+static void get_sectorsize(struct scsi_cd *cd)
+{
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ int the_result, retries = 3;
+ int sector_size;
+ struct scsi_request *SRpnt = NULL;
+ request_queue_t *queue;
+
+ buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ goto Enomem;
+ SRpnt = scsi_allocate_request(cd->device, GFP_KERNEL);
+ if (!SRpnt)
+ goto Enomem;
+
+ do {
+ cmd[0] = READ_CAPACITY;
+ memset((void *) &cmd[1], 0, 9);
+ /* Mark as really busy */
+ SRpnt->sr_request->rq_status = RQ_SCSI_BUSY;
+ SRpnt->sr_cmd_len = 0;
+
+ memset(buffer, 0, 8);
+
+ /* Do the command and wait.. */
+ SRpnt->sr_data_direction = DMA_FROM_DEVICE;
+ scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
+ 8, SR_TIMEOUT, MAX_RETRIES);
+
+ the_result = SRpnt->sr_result;
+ retries--;
+
+ } while (the_result && retries);
+
+
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+
+ if (the_result) {
+ cd->capacity = 0x1fffff;
+ sector_size = 2048; /* A guess, just in case */
+ cd->needs_sector_size = 1;
+ } else {
+#if 0
+ if (cdrom_get_last_written(&cd->cdi,
+ &cd->capacity))
+#endif
+ cd->capacity = 1 + ((buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3]);
+ sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+ switch (sector_size) {
+ /*
+ * HP 4020i CD-Recorder reports 2340 byte sectors
+ * Philips CD-Writers report 2352 byte sectors
+ *
+ * Use 2k sectors for them..
+ */
+ case 0:
+ case 2340:
+ case 2352:
+ sector_size = 2048;
+ /* fall through */
+ case 2048:
+ cd->capacity *= 4;
+ /* fall through */
+ case 512:
+ break;
+ default:
+ printk("%s: unsupported sector size %d.\n",
+ cd->cdi.name, sector_size);
+ cd->capacity = 0;
+ cd->needs_sector_size = 1;
+ }
+
+ cd->device->sector_size = sector_size;
+
+ /*
+ * Add this so that we have the ability to correctly gauge
+ * what the device is capable of.
+ */
+ cd->needs_sector_size = 0;
+ set_capacity(cd->disk, cd->capacity);
+ }
+
+ queue = cd->device->request_queue;
+ blk_queue_hardsect_size(queue, sector_size);
+out:
+ kfree(buffer);
+ return;
+
+Enomem:
+ cd->capacity = 0x1fffff;
+ sector_size = 2048; /* A guess, just in case */
+ cd->needs_sector_size = 1;
+ if (SRpnt)
+ scsi_release_request(SRpnt);
+ goto out;
+}
+
+static void get_capabilities(struct scsi_cd *cd)
+{
+ unsigned char *buffer;
+ struct scsi_mode_data data;
+ struct scsi_request *SRpnt;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ unsigned int the_result;
+ int retries, rc, n;
+
+ static char *loadmech[] =
+ {
+ "caddy",
+ "tray",
+ "pop-up",
+ "",
+ "changer",
+ "cartridge changer",
+ "",
+ ""
+ };
+
+ /* allocate a request for the TEST_UNIT_READY */
+ SRpnt = scsi_allocate_request(cd->device, GFP_KERNEL);
+ if (!SRpnt) {
+ printk(KERN_WARNING "(get_capabilities:) Request allocation "
+ "failure.\n");
+ return;
+ }
+
+ /* allocate transfer buffer */
+ buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ printk(KERN_ERR "sr: out of memory.\n");
+ scsi_release_request(SRpnt);
+ return;
+ }
+
+ /* issue TEST_UNIT_READY until the initial startup UNIT_ATTENTION
+ * conditions are gone, or a timeout happens
+ */
+ retries = 0;
+ do {
+ memset((void *)cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_sense_buffer[2] = 0;
+ SRpnt->sr_data_direction = DMA_NONE;
+
+ scsi_wait_req (SRpnt, (void *) cmd, buffer,
+ 0, SR_TIMEOUT, MAX_RETRIES);
+
+ the_result = SRpnt->sr_result;
+ retries++;
+ } while (retries < 5 &&
+ (!scsi_status_is_good(the_result) ||
+ ((driver_byte(the_result) & DRIVER_SENSE) &&
+ SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION)));
+
+ /* ask for mode page 0x2a */
+ rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+ SR_TIMEOUT, 3, &data);
+
+ if (!scsi_status_is_good(rc)) {
+ /* failed, drive doesn't have capabilities mode page */
+ cd->cdi.speed = 1;
+ cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
+ CDC_DVD | CDC_DVD_RAM |
+ CDC_SELECT_DISC | CDC_SELECT_SPEED);
+ scsi_release_request(SRpnt);
+ kfree(buffer);
+ printk("%s: scsi-1 drive\n", cd->cdi.name);
+ return;
+ }
+
+ n = data.header_length + data.block_descriptor_length;
+ cd->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176;
+ cd->readcd_known = 1;
+ cd->readcd_cdda = buffer[n + 5] & 0x01;
+ /* print some capability bits */
+ printk("%s: scsi3-mmc drive: %dx/%dx %s%s%s%s%s%s\n", cd->cdi.name,
+ ((buffer[n + 14] << 8) + buffer[n + 15]) / 176,
+ cd->cdi.speed,
+ buffer[n + 3] & 0x01 ? "writer " : "", /* CD Writer */
+ buffer[n + 3] & 0x20 ? "dvd-ram " : "",
+ buffer[n + 2] & 0x02 ? "cd/rw " : "", /* can read rewriteable */
+ buffer[n + 4] & 0x20 ? "xa/form2 " : "", /* can read xa/from2 */
+ buffer[n + 5] & 0x01 ? "cdda " : "", /* can read audio data */
+ loadmech[buffer[n + 6] >> 5]);
+ if ((buffer[n + 6] >> 5) == 0)
+ /* caddy drives can't close tray... */
+ cd->cdi.mask |= CDC_CLOSE_TRAY;
+ if ((buffer[n + 2] & 0x8) == 0)
+ /* not a DVD drive */
+ cd->cdi.mask |= CDC_DVD;
+ if ((buffer[n + 3] & 0x20) == 0)
+ /* can't write DVD-RAM media */
+ cd->cdi.mask |= CDC_DVD_RAM;
+ if ((buffer[n + 3] & 0x10) == 0)
+ /* can't write DVD-R media */
+ cd->cdi.mask |= CDC_DVD_R;
+ if ((buffer[n + 3] & 0x2) == 0)
+ /* can't write CD-RW media */
+ cd->cdi.mask |= CDC_CD_RW;
+ if ((buffer[n + 3] & 0x1) == 0)
+ /* can't write CD-R media */
+ cd->cdi.mask |= CDC_CD_R;
+ if ((buffer[n + 6] & 0x8) == 0)
+ /* can't eject */
+ cd->cdi.mask |= CDC_OPEN_TRAY;
+
+ if ((buffer[n + 6] >> 5) == mechtype_individual_changer ||
+ (buffer[n + 6] >> 5) == mechtype_cartridge_changer)
+ cd->cdi.capacity =
+ cdrom_number_of_slots(&cd->cdi);
+ if (cd->cdi.capacity <= 1)
+ /* not a changer */
+ cd->cdi.mask |= CDC_SELECT_DISC;
+ /*else I don't think it can close its tray
+ cd->cdi.mask |= CDC_CLOSE_TRAY; */
+
+ /*
+ * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
+ */
+ if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
+ (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
+ cd->device->writeable = 1;
+ }
+
+ scsi_release_request(SRpnt);
+ kfree(buffer);
+}
+
+/*
+ * sr_packet() is the entry point for the generic commands generated
+ * by the Uniform CD-ROM layer.
+ */
+static int sr_packet(struct cdrom_device_info *cdi,
+ struct packet_command *cgc)
+{
+ if (cgc->timeout <= 0)
+ cgc->timeout = IOCTL_TIMEOUT;
+
+ sr_do_ioctl(cdi->handle, cgc);
+
+ return cgc->stat;
+}
+
+/**
+ * sr_kref_release - Called to free the scsi_cd structure
+ * @kref: pointer to embedded kref
+ *
+ * sr_ref_sem must be held entering this routine. Because it is
+ * called on last put, you should always use the scsi_cd_get()
+ * scsi_cd_put() helpers which manipulate the semaphore directly
+ * and never do a direct kref_put().
+ **/
+static void sr_kref_release(struct kref *kref)
+{
+ struct scsi_cd *cd = container_of(kref, struct scsi_cd, kref);
+ struct gendisk *disk = cd->disk;
+
+ spin_lock(&sr_index_lock);
+ clear_bit(disk->first_minor, sr_index_bits);
+ spin_unlock(&sr_index_lock);
+
+ unregister_cdrom(&cd->cdi);
+
+ disk->private_data = NULL;
+
+ put_disk(disk);
+
+ kfree(cd);
+}
+
+static int sr_remove(struct device *dev)
+{
+ struct scsi_cd *cd = dev_get_drvdata(dev);
+
+ del_gendisk(cd->disk);
+
+ down(&sr_ref_sem);
+ kref_put(&cd->kref, sr_kref_release);
+ up(&sr_ref_sem);
+
+ return 0;
+}
+
+static int __init init_sr(void)
+{
+ int rc;
+
+ rc = register_blkdev(SCSI_CDROM_MAJOR, "sr");
+ if (rc)
+ return rc;
+ return scsi_register_driver(&sr_template.gendrv);
+}
+
+static void __exit exit_sr(void)
+{
+ scsi_unregister_driver(&sr_template.gendrv);
+ unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
+}
+
+module_init(init_sr);
+module_exit(exit_sr);
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h
new file mode 100644
index 000000000000..0b3178007203
--- /dev/null
+++ b/drivers/scsi/sr.h
@@ -0,0 +1,68 @@
+/*
+ * sr.h by David Giller
+ * CD-ROM disk driver header file
+ *
+ * adapted from:
+ * sd.h Copyright (C) 1992 Drew Eckhardt
+ * SCSI disk driver header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@andante.org to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+
+#ifndef _SR_H
+#define _SR_H
+
+#include <linux/genhd.h>
+#include <linux/kref.h>
+
+struct scsi_device;
+
+/* The CDROM is fairly slow, so we need a little extra time */
+/* In fact, it is very slow if it has to spin up first */
+#define IOCTL_TIMEOUT 30*HZ
+
+
+typedef struct scsi_cd {
+ struct scsi_driver *driver;
+ unsigned capacity; /* size in blocks */
+ struct scsi_device *device;
+ unsigned int vendor; /* vendor code, see sr_vendor.c */
+ unsigned long ms_offset; /* for reading multisession-CD's */
+ unsigned needs_sector_size:1; /* needs to get sector size */
+ unsigned use:1; /* is this device still supportable */
+ unsigned xa_flag:1; /* CD has XA sectors ? */
+ unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */
+ unsigned readcd_cdda:1; /* reading audio data using READ_CD */
+ struct cdrom_device_info cdi;
+ /* We hold gendisk and scsi_device references on probe and use
+ * the refs on this kref to decide when to release them */
+ struct kref kref;
+ struct gendisk *disk;
+} Scsi_CD;
+
+int sr_do_ioctl(Scsi_CD *, struct packet_command *);
+
+int sr_lock_door(struct cdrom_device_info *, int);
+int sr_tray_move(struct cdrom_device_info *, int);
+int sr_drive_status(struct cdrom_device_info *, int);
+int sr_disk_status(struct cdrom_device_info *);
+int sr_get_last_session(struct cdrom_device_info *, struct cdrom_multisession *);
+int sr_get_mcn(struct cdrom_device_info *, struct cdrom_mcn *);
+int sr_reset(struct cdrom_device_info *);
+int sr_select_speed(struct cdrom_device_info *cdi, int speed);
+int sr_audio_ioctl(struct cdrom_device_info *, unsigned int, void *);
+int sr_dev_ioctl(struct cdrom_device_info *, unsigned int, unsigned long);
+
+int sr_is_xa(Scsi_CD *);
+
+/* sr_vendor.c */
+void sr_vendor_init(Scsi_CD *);
+int sr_cd_check(struct cdrom_device_info *);
+int sr_set_blocklength(Scsi_CD *, int blocklength);
+
+#endif
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
new file mode 100644
index 000000000000..3471be05779a
--- /dev/null
+++ b/drivers/scsi/sr_ioctl.c
@@ -0,0 +1,568 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+#include <linux/cdrom.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
+
+#include "sr.h"
+
+#if 0
+#define DEBUG
+#endif
+
+/* The sr_is_xa() seems to trigger firmware bugs with some drives :-(
+ * It is off by default and can be turned on with this module parameter */
+static int xa_test = 0;
+
+module_param(xa_test, int, S_IRUGO | S_IWUSR);
+
+
+#define IOCTL_RETRIES 3
+
+/* ATAPI drives don't have a SCMD_PLAYAUDIO_TI command. When these drives
+ are emulating a SCSI device via the idescsi module, they need to have
+ CDROMPLAYTRKIND commands translated into CDROMPLAYMSF commands for them */
+
+static int sr_fake_playtrkind(struct cdrom_device_info *cdi, struct cdrom_ti *ti)
+{
+ struct cdrom_tocentry trk0_te, trk1_te;
+ struct cdrom_tochdr tochdr;
+ struct packet_command cgc;
+ int ntracks, ret;
+
+ if ((ret = sr_audio_ioctl(cdi, CDROMREADTOCHDR, &tochdr)))
+ return ret;
+
+ ntracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
+
+ if (ti->cdti_trk1 == ntracks)
+ ti->cdti_trk1 = CDROM_LEADOUT;
+ else if (ti->cdti_trk1 != CDROM_LEADOUT)
+ ti->cdti_trk1 ++;
+
+ trk0_te.cdte_track = ti->cdti_trk0;
+ trk0_te.cdte_format = CDROM_MSF;
+ trk1_te.cdte_track = ti->cdti_trk1;
+ trk1_te.cdte_format = CDROM_MSF;
+
+ if ((ret = sr_audio_ioctl(cdi, CDROMREADTOCENTRY, &trk0_te)))
+ return ret;
+ if ((ret = sr_audio_ioctl(cdi, CDROMREADTOCENTRY, &trk1_te)))
+ return ret;
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+ cgc.cmd[3] = trk0_te.cdte_addr.msf.minute;
+ cgc.cmd[4] = trk0_te.cdte_addr.msf.second;
+ cgc.cmd[5] = trk0_te.cdte_addr.msf.frame;
+ cgc.cmd[6] = trk1_te.cdte_addr.msf.minute;
+ cgc.cmd[7] = trk1_te.cdte_addr.msf.second;
+ cgc.cmd[8] = trk1_te.cdte_addr.msf.frame;
+ cgc.data_direction = DMA_NONE;
+ cgc.timeout = IOCTL_TIMEOUT;
+ return sr_do_ioctl(cdi->handle, &cgc);
+}
+
+/* We do our own retries because we want to know what the specific
+ error code is. Normally the UNIT_ATTENTION code will automatically
+ clear after one error */
+
+int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc)
+{
+ struct scsi_request *SRpnt;
+ struct scsi_device *SDev;
+ struct request *req;
+ int result, err = 0, retries = 0;
+
+ SDev = cd->device;
+ SRpnt = scsi_allocate_request(SDev, GFP_KERNEL);
+ if (!SRpnt) {
+ printk(KERN_ERR "Unable to allocate SCSI request in sr_do_ioctl");
+ err = -ENOMEM;
+ goto out;
+ }
+ SRpnt->sr_data_direction = cgc->data_direction;
+
+ retry:
+ if (!scsi_block_when_processing_errors(SDev)) {
+ err = -ENODEV;
+ goto out_free;
+ }
+
+ scsi_wait_req(SRpnt, cgc->cmd, cgc->buffer, cgc->buflen,
+ cgc->timeout, IOCTL_RETRIES);
+
+ req = SRpnt->sr_request;
+ if (SRpnt->sr_buffer && req->buffer && SRpnt->sr_buffer != req->buffer) {
+ memcpy(req->buffer, SRpnt->sr_buffer, SRpnt->sr_bufflen);
+ kfree(SRpnt->sr_buffer);
+ SRpnt->sr_buffer = req->buffer;
+ }
+
+ result = SRpnt->sr_result;
+
+ /* Minimal error checking. Ignore cases we know about, and report the rest. */
+ if (driver_byte(result) != 0) {
+ switch (SRpnt->sr_sense_buffer[2] & 0xf) {
+ case UNIT_ATTENTION:
+ SDev->changed = 1;
+ if (!cgc->quiet)
+ printk(KERN_INFO "%s: disc change detected.\n", cd->cdi.name);
+ if (retries++ < 10)
+ goto retry;
+ err = -ENOMEDIUM;
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ if (SRpnt->sr_sense_buffer[12] == 0x04 &&
+ SRpnt->sr_sense_buffer[13] == 0x01) {
+ /* sense: Logical unit is in process of becoming ready */
+ if (!cgc->quiet)
+ printk(KERN_INFO "%s: CDROM not ready yet.\n", cd->cdi.name);
+ if (retries++ < 10) {
+ /* sleep 2 sec and try again */
+ ssleep(2);
+ goto retry;
+ } else {
+ /* 20 secs are enough? */
+ err = -ENOMEDIUM;
+ break;
+ }
+ }
+ if (!cgc->quiet)
+ printk(KERN_INFO "%s: CDROM not ready. Make sure there is a disc in the drive.\n", cd->cdi.name);
+#ifdef DEBUG
+ scsi_print_req_sense("sr", SRpnt);
+#endif
+ err = -ENOMEDIUM;
+ break;
+ case ILLEGAL_REQUEST:
+ err = -EIO;
+ if (SRpnt->sr_sense_buffer[12] == 0x20 &&
+ SRpnt->sr_sense_buffer[13] == 0x00)
+ /* sense: Invalid command operation code */
+ err = -EDRIVE_CANT_DO_THIS;
+#ifdef DEBUG
+ __scsi_print_command(cgc->cmd);
+ scsi_print_req_sense("sr", SRpnt);
+#endif
+ break;
+ default:
+ printk(KERN_ERR "%s: CDROM (ioctl) error, command: ", cd->cdi.name);
+ __scsi_print_command(cgc->cmd);
+ scsi_print_req_sense("sr", SRpnt);
+ err = -EIO;
+ }
+ }
+
+ if (cgc->sense)
+ memcpy(cgc->sense, SRpnt->sr_sense_buffer, sizeof(*cgc->sense));
+
+ /* Wake up a process waiting for device */
+ out_free:
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ out:
+ cgc->stat = err;
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+/* interface to cdrom.c */
+
+static int test_unit_ready(Scsi_CD *cd)
+{
+ struct packet_command cgc;
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_TEST_UNIT_READY;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_NONE;
+ cgc.timeout = IOCTL_TIMEOUT;
+ return sr_do_ioctl(cd, &cgc);
+}
+
+int sr_tray_move(struct cdrom_device_info *cdi, int pos)
+{
+ Scsi_CD *cd = cdi->handle;
+ struct packet_command cgc;
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_START_STOP_UNIT;
+ cgc.cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */ ;
+ cgc.data_direction = DMA_NONE;
+ cgc.timeout = IOCTL_TIMEOUT;
+ return sr_do_ioctl(cd, &cgc);
+}
+
+int sr_lock_door(struct cdrom_device_info *cdi, int lock)
+{
+ Scsi_CD *cd = cdi->handle;
+
+ return scsi_set_medium_removal(cd->device, lock ?
+ SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW);
+}
+
+int sr_drive_status(struct cdrom_device_info *cdi, int slot)
+{
+ if (CDSL_CURRENT != slot) {
+ /* we have no changer support */
+ return -EINVAL;
+ }
+ if (0 == test_unit_ready(cdi->handle))
+ return CDS_DISC_OK;
+
+ return CDS_TRAY_OPEN;
+}
+
+int sr_disk_status(struct cdrom_device_info *cdi)
+{
+ Scsi_CD *cd = cdi->handle;
+ struct cdrom_tochdr toc_h;
+ struct cdrom_tocentry toc_e;
+ int i, rc, have_datatracks = 0;
+
+ /* look for data tracks */
+ if (0 != (rc = sr_audio_ioctl(cdi, CDROMREADTOCHDR, &toc_h)))
+ return (rc == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO;
+
+ for (i = toc_h.cdth_trk0; i <= toc_h.cdth_trk1; i++) {
+ toc_e.cdte_track = i;
+ toc_e.cdte_format = CDROM_LBA;
+ if (sr_audio_ioctl(cdi, CDROMREADTOCENTRY, &toc_e))
+ return CDS_NO_INFO;
+ if (toc_e.cdte_ctrl & CDROM_DATA_TRACK) {
+ have_datatracks = 1;
+ break;
+ }
+ }
+ if (!have_datatracks)
+ return CDS_AUDIO;
+
+ if (cd->xa_flag)
+ return CDS_XA_2_1;
+ else
+ return CDS_DATA_1;
+}
+
+int sr_get_last_session(struct cdrom_device_info *cdi,
+ struct cdrom_multisession *ms_info)
+{
+ Scsi_CD *cd = cdi->handle;
+
+ ms_info->addr.lba = cd->ms_offset;
+ ms_info->xa_flag = cd->xa_flag || cd->ms_offset > 0;
+
+ return 0;
+}
+
+/* primitive to determine whether we need to have GFP_DMA set based on
+ * the status of the unchecked_isa_dma flag in the host structure */
+#define SR_GFP_DMA(cd) (((cd)->device->host->unchecked_isa_dma) ? GFP_DMA : 0)
+
+int sr_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
+{
+ Scsi_CD *cd = cdi->handle;
+ struct packet_command cgc;
+ char *buffer = kmalloc(32, GFP_KERNEL | SR_GFP_DMA(cd));
+ int result;
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_READ_SUBCHANNEL;
+ cgc.cmd[2] = 0x40; /* I do want the subchannel info */
+ cgc.cmd[3] = 0x02; /* Give me medium catalog number info */
+ cgc.cmd[8] = 24;
+ cgc.buffer = buffer;
+ cgc.buflen = 24;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = IOCTL_TIMEOUT;
+ result = sr_do_ioctl(cd, &cgc);
+
+ memcpy(mcn->medium_catalog_number, buffer + 9, 13);
+ mcn->medium_catalog_number[13] = 0;
+
+ kfree(buffer);
+ return result;
+}
+
+int sr_reset(struct cdrom_device_info *cdi)
+{
+ return 0;
+}
+
+int sr_select_speed(struct cdrom_device_info *cdi, int speed)
+{
+ Scsi_CD *cd = cdi->handle;
+ struct packet_command cgc;
+
+ if (speed == 0)
+ speed = 0xffff; /* set to max */
+ else
+ speed *= 177; /* Nx to kbyte/s */
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_SET_SPEED; /* SET CD SPEED */
+ cgc.cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */
+ cgc.cmd[3] = speed & 0xff; /* LSB */
+ cgc.data_direction = DMA_NONE;
+ cgc.timeout = IOCTL_TIMEOUT;
+
+ if (sr_do_ioctl(cd, &cgc))
+ return -EIO;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* this is called by the generic cdrom driver. arg is a _kernel_ pointer, */
+/* because the generic cdrom driver does the user access stuff for us. */
+/* only cdromreadtochdr and cdromreadtocentry are left - for use with the */
+/* sr_disk_status interface for the generic cdrom driver. */
+
+int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
+{
+ Scsi_CD *cd = cdi->handle;
+ struct packet_command cgc;
+ int result;
+ unsigned char *buffer = kmalloc(32, GFP_KERNEL | SR_GFP_DMA(cd));
+
+ if (!buffer)
+ return -ENOMEM;
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.timeout = IOCTL_TIMEOUT;
+
+ switch (cmd) {
+ case CDROMREADTOCHDR:
+ {
+ struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+
+ cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cgc.cmd[8] = 12; /* LSB of length */
+ cgc.buffer = buffer;
+ cgc.buflen = 12;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_FROM_DEVICE;
+
+ result = sr_do_ioctl(cd, &cgc);
+
+ tochdr->cdth_trk0 = buffer[2];
+ tochdr->cdth_trk1 = buffer[3];
+
+ break;
+ }
+
+ case CDROMREADTOCENTRY:
+ {
+ struct cdrom_tocentry *tocentry = (struct cdrom_tocentry *) arg;
+
+ cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cgc.cmd[1] |= (tocentry->cdte_format == CDROM_MSF) ? 0x02 : 0;
+ cgc.cmd[6] = tocentry->cdte_track;
+ cgc.cmd[8] = 12; /* LSB of length */
+ cgc.buffer = buffer;
+ cgc.buflen = 12;
+ cgc.data_direction = DMA_FROM_DEVICE;
+
+ result = sr_do_ioctl(cd, &cgc);
+
+ tocentry->cdte_ctrl = buffer[5] & 0xf;
+ tocentry->cdte_adr = buffer[5] >> 4;
+ tocentry->cdte_datamode = (tocentry->cdte_ctrl & 0x04) ? 1 : 0;
+ if (tocentry->cdte_format == CDROM_MSF) {
+ tocentry->cdte_addr.msf.minute = buffer[9];
+ tocentry->cdte_addr.msf.second = buffer[10];
+ tocentry->cdte_addr.msf.frame = buffer[11];
+ } else
+ tocentry->cdte_addr.lba = (((((buffer[8] << 8) + buffer[9]) << 8)
+ + buffer[10]) << 8) + buffer[11];
+
+ break;
+ }
+
+ case CDROMPLAYTRKIND: {
+ struct cdrom_ti* ti = (struct cdrom_ti*)arg;
+
+ cgc.cmd[0] = GPCMD_PLAYAUDIO_TI;
+ cgc.cmd[4] = ti->cdti_trk0;
+ cgc.cmd[5] = ti->cdti_ind0;
+ cgc.cmd[7] = ti->cdti_trk1;
+ cgc.cmd[8] = ti->cdti_ind1;
+ cgc.data_direction = DMA_NONE;
+
+ result = sr_do_ioctl(cd, &cgc);
+ if (result == -EDRIVE_CANT_DO_THIS)
+ result = sr_fake_playtrkind(cdi, ti);
+
+ break;
+ }
+
+ default:
+ result = -EINVAL;
+ }
+
+#if 0
+ if (result)
+ printk("DEBUG: sr_audio: result for ioctl %x: %x\n", cmd, result);
+#endif
+
+ kfree(buffer);
+ return result;
+}
+
+/* -----------------------------------------------------------------------
+ * a function to read all sorts of funny cdrom sectors using the READ_CD
+ * scsi-3 mmc command
+ *
+ * lba: linear block address
+ * format: 0 = data (anything)
+ * 1 = audio
+ * 2 = data (mode 1)
+ * 3 = data (mode 2)
+ * 4 = data (mode 2 form1)
+ * 5 = data (mode 2 form2)
+ * blksize: 2048 | 2336 | 2340 | 2352
+ */
+
+static int sr_read_cd(Scsi_CD *cd, unsigned char *dest, int lba, int format, int blksize)
+{
+ struct packet_command cgc;
+
+#ifdef DEBUG
+ printk("%s: sr_read_cd lba=%d format=%d blksize=%d\n",
+ cd->cdi.name, lba, format, blksize);
+#endif
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_READ_CD; /* READ_CD */
+ cgc.cmd[1] = ((format & 7) << 2);
+ cgc.cmd[2] = (unsigned char) (lba >> 24) & 0xff;
+ cgc.cmd[3] = (unsigned char) (lba >> 16) & 0xff;
+ cgc.cmd[4] = (unsigned char) (lba >> 8) & 0xff;
+ cgc.cmd[5] = (unsigned char) lba & 0xff;
+ cgc.cmd[8] = 1;
+ switch (blksize) {
+ case 2336:
+ cgc.cmd[9] = 0x58;
+ break;
+ case 2340:
+ cgc.cmd[9] = 0x78;
+ break;
+ case 2352:
+ cgc.cmd[9] = 0xf8;
+ break;
+ default:
+ cgc.cmd[9] = 0x10;
+ break;
+ }
+ cgc.buffer = dest;
+ cgc.buflen = blksize;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = IOCTL_TIMEOUT;
+ return sr_do_ioctl(cd, &cgc);
+}
+
+/*
+ * read sectors with blocksizes other than 2048
+ */
+
+static int sr_read_sector(Scsi_CD *cd, int lba, int blksize, unsigned char *dest)
+{
+ struct packet_command cgc;
+ int rc;
+
+ /* we try the READ CD command first... */
+ if (cd->readcd_known) {
+ rc = sr_read_cd(cd, dest, lba, 0, blksize);
+ if (-EDRIVE_CANT_DO_THIS != rc)
+ return rc;
+ cd->readcd_known = 0;
+ printk("CDROM does'nt support READ CD (0xbe) command\n");
+ /* fall & retry the other way */
+ }
+ /* ... if this fails, we switch the blocksize using MODE SELECT */
+ if (blksize != cd->device->sector_size) {
+ if (0 != (rc = sr_set_blocklength(cd, blksize)))
+ return rc;
+ }
+#ifdef DEBUG
+ printk("%s: sr_read_sector lba=%d blksize=%d\n", cd->cdi.name, lba, blksize);
+#endif
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = GPCMD_READ_10;
+ cgc.cmd[2] = (unsigned char) (lba >> 24) & 0xff;
+ cgc.cmd[3] = (unsigned char) (lba >> 16) & 0xff;
+ cgc.cmd[4] = (unsigned char) (lba >> 8) & 0xff;
+ cgc.cmd[5] = (unsigned char) lba & 0xff;
+ cgc.cmd[8] = 1;
+ cgc.buffer = dest;
+ cgc.buflen = blksize;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = IOCTL_TIMEOUT;
+ rc = sr_do_ioctl(cd, &cgc);
+
+ return rc;
+}
+
+/*
+ * read a sector in raw mode to check the sector format
+ * ret: 1 == mode2 (XA), 0 == mode1, <0 == error
+ */
+
+int sr_is_xa(Scsi_CD *cd)
+{
+ unsigned char *raw_sector;
+ int is_xa;
+
+ if (!xa_test)
+ return 0;
+
+ raw_sector = (unsigned char *) kmalloc(2048, GFP_KERNEL | SR_GFP_DMA(cd));
+ if (!raw_sector)
+ return -ENOMEM;
+ if (0 == sr_read_sector(cd, cd->ms_offset + 16,
+ CD_FRAMESIZE_RAW1, raw_sector)) {
+ is_xa = (raw_sector[3] == 0x02) ? 1 : 0;
+ } else {
+ /* read a raw sector failed for some reason. */
+ is_xa = -1;
+ }
+ kfree(raw_sector);
+#ifdef DEBUG
+ printk("%s: sr_is_xa: %d\n", cd->cdi.name, is_xa);
+#endif
+ return is_xa;
+}
+
+int sr_dev_ioctl(struct cdrom_device_info *cdi,
+ unsigned int cmd, unsigned long arg)
+{
+ Scsi_CD *cd = cdi->handle;
+ int ret;
+
+ ret = scsi_nonblockable_ioctl(cd->device, cmd,
+ (void __user *)arg, NULL);
+ /*
+ * ENODEV means that we didn't recognise the ioctl, or that we
+ * cannot execute it in the current device state. In either
+ * case fall through to scsi_ioctl, which will return ENDOEV again
+ * if it doesn't recognise the ioctl
+ */
+ if (ret != -ENODEV)
+ return ret;
+ return scsi_ioctl(cd->device, cmd, (void __user *)arg);
+}
diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c
new file mode 100644
index 000000000000..78274dc91f5c
--- /dev/null
+++ b/drivers/scsi/sr_vendor.c
@@ -0,0 +1,329 @@
+/* -*-linux-c-*-
+
+ * vendor-specific code for SCSI CD-ROM's goes here.
+ *
+ * This is needed becauce most of the new features (multisession and
+ * the like) are too new to be included into the SCSI-II standard (to
+ * be exact: there is'nt anything in my draft copy).
+ *
+ * Aug 1997: Ha! Got a SCSI-3 cdrom spec across my fingers. SCSI-3 does
+ * multisession using the READ TOC command (like SONY).
+ *
+ * Rearranged stuff here: SCSI-3 is included allways, support
+ * for NEC/TOSHIBA/HP commands is optional.
+ *
+ * Gerd Knorr <kraxel@cs.tu-berlin.de>
+ *
+ * --------------------------------------------------------------------------
+ *
+ * support for XA/multisession-CD's
+ *
+ * - NEC: Detection and support of multisession CD's.
+ *
+ * - TOSHIBA: Detection and support of multisession CD's.
+ * Some XA-Sector tweaking, required for older drives.
+ *
+ * - SONY: Detection and support of multisession CD's.
+ * added by Thomas Quinot <thomas@cuivre.freenix.fr>
+ *
+ * - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC, PHILIPS: known to
+ * work with SONY (SCSI3 now) code.
+ *
+ * - HP: Much like SONY, but a little different... (Thomas)
+ * HP-Writers only ??? Maybe other CD-Writers work with this too ?
+ * HP 6020 writers now supported.
+ */
+
+#include <linux/config.h>
+#include <linux/cdrom.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/blkdev.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "sr.h"
+
+#if 0
+#define DEBUG
+#endif
+
+/* here are some constants to sort the vendors into groups */
+
+#define VENDOR_SCSI3 1 /* default: scsi-3 mmc */
+
+#define VENDOR_NEC 2
+#define VENDOR_TOSHIBA 3
+#define VENDOR_WRITER 4 /* pre-scsi3 writers */
+
+#define VENDOR_TIMEOUT 30*HZ
+
+void sr_vendor_init(Scsi_CD *cd)
+{
+#ifndef CONFIG_BLK_DEV_SR_VENDOR
+ cd->vendor = VENDOR_SCSI3;
+#else
+ char *vendor = cd->device->vendor;
+ char *model = cd->device->model;
+
+ /* default */
+ cd->vendor = VENDOR_SCSI3;
+ if (cd->readcd_known)
+ /* this is true for scsi3/mmc drives - no more checks */
+ return;
+
+ if (cd->device->type == TYPE_WORM) {
+ cd->vendor = VENDOR_WRITER;
+
+ } else if (!strncmp(vendor, "NEC", 3)) {
+ cd->vendor = VENDOR_NEC;
+ if (!strncmp(model, "CD-ROM DRIVE:25", 15) ||
+ !strncmp(model, "CD-ROM DRIVE:36", 15) ||
+ !strncmp(model, "CD-ROM DRIVE:83", 15) ||
+ !strncmp(model, "CD-ROM DRIVE:84 ", 16)
+#if 0
+ /* my NEC 3x returns the read-raw data if a read-raw
+ is followed by a read for the same sector - aeb */
+ || !strncmp(model, "CD-ROM DRIVE:500", 16)
+#endif
+ )
+ /* these can't handle multisession, may hang */
+ cd->cdi.mask |= CDC_MULTI_SESSION;
+
+ } else if (!strncmp(vendor, "TOSHIBA", 7)) {
+ cd->vendor = VENDOR_TOSHIBA;
+
+ }
+#endif
+}
+
+
+/* small handy function for switching block length using MODE SELECT,
+ * used by sr_read_sector() */
+
+int sr_set_blocklength(Scsi_CD *cd, int blocklength)
+{
+ unsigned char *buffer; /* the buffer for the ioctl */
+ struct packet_command cgc;
+ struct ccs_modesel_head *modesel;
+ int rc, density = 0;
+
+#ifdef CONFIG_BLK_DEV_SR_VENDOR
+ if (cd->vendor == VENDOR_TOSHIBA)
+ density = (blocklength > 2048) ? 0x81 : 0x83;
+#endif
+
+ buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ return -ENOMEM;
+
+#ifdef DEBUG
+ printk("%s: MODE SELECT 0x%x/%d\n", cd->cdi.name, density, blocklength);
+#endif
+ memset(&cgc, 0, sizeof(struct packet_command));
+ cgc.cmd[0] = MODE_SELECT;
+ cgc.cmd[1] = (1 << 4);
+ cgc.cmd[4] = 12;
+ modesel = (struct ccs_modesel_head *) buffer;
+ memset(modesel, 0, sizeof(*modesel));
+ modesel->block_desc_length = 0x08;
+ modesel->density = density;
+ modesel->block_length_med = (blocklength >> 8) & 0xff;
+ modesel->block_length_lo = blocklength & 0xff;
+ cgc.buffer = buffer;
+ cgc.buflen = sizeof(*modesel);
+ cgc.data_direction = DMA_TO_DEVICE;
+ cgc.timeout = VENDOR_TIMEOUT;
+ if (0 == (rc = sr_do_ioctl(cd, &cgc))) {
+ cd->device->sector_size = blocklength;
+ }
+#ifdef DEBUG
+ else
+ printk("%s: switching blocklength to %d bytes failed\n",
+ cd->cdi.name, blocklength);
+#endif
+ kfree(buffer);
+ return rc;
+}
+
+/* This function gets called after a media change. Checks if the CD is
+ multisession, asks for offset etc. */
+
+int sr_cd_check(struct cdrom_device_info *cdi)
+{
+ Scsi_CD *cd = cdi->handle;
+ unsigned long sector;
+ unsigned char *buffer; /* the buffer for the ioctl */
+ struct packet_command cgc;
+ int rc, no_multi;
+
+ if (cd->cdi.mask & CDC_MULTI_SESSION)
+ return 0;
+
+ buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ return -ENOMEM;
+
+ sector = 0; /* the multisession sector offset goes here */
+ no_multi = 0; /* flag: the drive can't handle multisession */
+ rc = 0;
+
+ memset(&cgc, 0, sizeof(struct packet_command));
+
+ switch (cd->vendor) {
+
+ case VENDOR_SCSI3:
+ cgc.cmd[0] = READ_TOC;
+ cgc.cmd[8] = 12;
+ cgc.cmd[9] = 0x40;
+ cgc.buffer = buffer;
+ cgc.buflen = 12;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = VENDOR_TIMEOUT;
+ rc = sr_do_ioctl(cd, &cgc);
+ if (rc != 0)
+ break;
+ if ((buffer[0] << 8) + buffer[1] < 0x0a) {
+ printk(KERN_INFO "%s: Hmm, seems the drive "
+ "doesn't support multisession CD's\n", cd->cdi.name);
+ no_multi = 1;
+ break;
+ }
+ sector = buffer[11] + (buffer[10] << 8) +
+ (buffer[9] << 16) + (buffer[8] << 24);
+ if (buffer[6] <= 1) {
+ /* ignore sector offsets from first track */
+ sector = 0;
+ }
+ break;
+
+#ifdef CONFIG_BLK_DEV_SR_VENDOR
+ case VENDOR_NEC:{
+ unsigned long min, sec, frame;
+ cgc.cmd[0] = 0xde;
+ cgc.cmd[1] = 0x03;
+ cgc.cmd[2] = 0xb0;
+ cgc.buffer = buffer;
+ cgc.buflen = 0x16;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = VENDOR_TIMEOUT;
+ rc = sr_do_ioctl(cd, &cgc);
+ if (rc != 0)
+ break;
+ if (buffer[14] != 0 && buffer[14] != 0xb0) {
+ printk(KERN_INFO "%s: Hmm, seems the cdrom "
+ "doesn't support multisession CD's\n",
+ cd->cdi.name);
+ no_multi = 1;
+ break;
+ }
+ min = BCD2BIN(buffer[15]);
+ sec = BCD2BIN(buffer[16]);
+ frame = BCD2BIN(buffer[17]);
+ sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame;
+ break;
+ }
+
+ case VENDOR_TOSHIBA:{
+ unsigned long min, sec, frame;
+
+ /* we request some disc information (is it a XA-CD ?,
+ * where starts the last session ?) */
+ cgc.cmd[0] = 0xc7;
+ cgc.cmd[1] = 0x03;
+ cgc.buffer = buffer;
+ cgc.buflen = 4;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = VENDOR_TIMEOUT;
+ rc = sr_do_ioctl(cd, &cgc);
+ if (rc == -EINVAL) {
+ printk(KERN_INFO "%s: Hmm, seems the drive "
+ "doesn't support multisession CD's\n",
+ cd->cdi.name);
+ no_multi = 1;
+ break;
+ }
+ if (rc != 0)
+ break;
+ min = BCD2BIN(buffer[1]);
+ sec = BCD2BIN(buffer[2]);
+ frame = BCD2BIN(buffer[3]);
+ sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame;
+ if (sector)
+ sector -= CD_MSF_OFFSET;
+ sr_set_blocklength(cd, 2048);
+ break;
+ }
+
+ case VENDOR_WRITER:
+ cgc.cmd[0] = READ_TOC;
+ cgc.cmd[8] = 0x04;
+ cgc.cmd[9] = 0x40;
+ cgc.buffer = buffer;
+ cgc.buflen = 0x04;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = VENDOR_TIMEOUT;
+ rc = sr_do_ioctl(cd, &cgc);
+ if (rc != 0) {
+ break;
+ }
+ if ((rc = buffer[2]) == 0) {
+ printk(KERN_WARNING
+ "%s: No finished session\n", cd->cdi.name);
+ break;
+ }
+ cgc.cmd[0] = READ_TOC; /* Read TOC */
+ cgc.cmd[6] = rc & 0x7f; /* number of last session */
+ cgc.cmd[8] = 0x0c;
+ cgc.cmd[9] = 0x40;
+ cgc.buffer = buffer;
+ cgc.buflen = 12;
+ cgc.quiet = 1;
+ cgc.data_direction = DMA_FROM_DEVICE;
+ cgc.timeout = VENDOR_TIMEOUT;
+ rc = sr_do_ioctl(cd, &cgc);
+ if (rc != 0) {
+ break;
+ }
+ sector = buffer[11] + (buffer[10] << 8) +
+ (buffer[9] << 16) + (buffer[8] << 24);
+ break;
+#endif /* CONFIG_BLK_DEV_SR_VENDOR */
+
+ default:
+ /* should not happen */
+ printk(KERN_WARNING
+ "%s: unknown vendor code (%i), not initialized ?\n",
+ cd->cdi.name, cd->vendor);
+ sector = 0;
+ no_multi = 1;
+ break;
+ }
+ cd->ms_offset = sector;
+ cd->xa_flag = 0;
+ if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(cd))
+ cd->xa_flag = 1;
+
+ if (2048 != cd->device->sector_size) {
+ sr_set_blocklength(cd, 2048);
+ }
+ if (no_multi)
+ cdi->mask |= CDC_MULTI_SESSION;
+
+#ifdef DEBUG
+ if (sector)
+ printk(KERN_DEBUG "%s: multisession offset=%lu\n",
+ cd->cdi.name, sector);
+#endif
+ kfree(buffer);
+ return rc;
+}
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
new file mode 100644
index 000000000000..265d1eed64fa
--- /dev/null
+++ b/drivers/scsi/st.c
@@ -0,0 +1,4438 @@
+/*
+ SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+ file Documentation/scsi/st.txt for more information.
+
+ History:
+ Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
+ Contribution and ideas from several people including (in alphabetical
+ order) Klaus Ehrenfried, Eugene Exarevsky, Eric Lee Green, Wolfgang Denk,
+ Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
+ Michael Schaefer, J"org Weule, and Eric Youngdale.
+
+ Copyright 1992 - 2005 Kai Makisara
+ email Kai.Makisara@kolumbus.fi
+
+ Some small formal changes - aeb, 950809
+
+ Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
+ */
+
+static char *verstr = "20050312";
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/moduleparam.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+
+#include <asm/uaccess.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
+
+
+/* The driver prints some debugging information on the console if DEBUG
+ is defined and non-zero. */
+#define DEBUG 0
+
+#if DEBUG
+/* The message level for the debug messages is currently set to KERN_NOTICE
+ so that people can easily see the messages. Later when the debugging messages
+ in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
+#define ST_DEB_MSG KERN_NOTICE
+#define DEB(a) a
+#define DEBC(a) if (debugging) { a ; }
+#else
+#define DEB(a)
+#define DEBC(a)
+#endif
+
+#define ST_KILOBYTE 1024
+
+#include "st_options.h"
+#include "st.h"
+
+static int buffer_kbs;
+static int max_sg_segs;
+static int try_direct_io = TRY_DIRECT_IO;
+static int try_rdio = 1;
+static int try_wdio = 1;
+
+static int st_dev_max;
+static int st_nr_dev;
+
+static struct class_simple *st_sysfs_class;
+
+MODULE_AUTHOR("Kai Makisara");
+MODULE_DESCRIPTION("SCSI Tape Driver");
+MODULE_LICENSE("GPL");
+
+/* Set 'perm' (4th argument) to 0 to disable module_param's definition
+ * of sysfs parameters (which module_param doesn't yet support).
+ * Sysfs parameters defined explicitly later.
+ */
+module_param_named(buffer_kbs, buffer_kbs, int, 0);
+MODULE_PARM_DESC(buffer_kbs, "Default driver buffer size for fixed block mode (KB; 32)");
+module_param_named(max_sg_segs, max_sg_segs, int, 0);
+MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)");
+module_param_named(try_direct_io, try_direct_io, int, 0);
+MODULE_PARM_DESC(try_direct_io, "Try direct I/O between user buffer and tape drive (1)");
+
+/* Extra parameters for testing */
+module_param_named(try_rdio, try_rdio, int, 0);
+MODULE_PARM_DESC(try_rdio, "Try direct read i/o when possible");
+module_param_named(try_wdio, try_wdio, int, 0);
+MODULE_PARM_DESC(try_wdio, "Try direct write i/o when possible");
+
+#ifndef MODULE
+static int write_threshold_kbs; /* retained for compatibility */
+static struct st_dev_parm {
+ char *name;
+ int *val;
+} parms[] __initdata = {
+ {
+ "buffer_kbs", &buffer_kbs
+ },
+ { /* Retained for compatibility with 2.4 */
+ "write_threshold_kbs", &write_threshold_kbs
+ },
+ {
+ "max_sg_segs", NULL
+ },
+ {
+ "try_direct_io", &try_direct_io
+ }
+};
+#endif
+
+/* Restrict the number of modes so that names for all are assigned */
+#if ST_NBR_MODES > 16
+#error "Maximum number of modes is 16"
+#endif
+/* Bit reversed order to get same names for same minors with all
+ mode counts */
+static char *st_formats[] = {
+ "", "r", "k", "s", "l", "t", "o", "u",
+ "m", "v", "p", "x", "a", "y", "q", "z"};
+
+/* The default definitions have been moved to st_options.h */
+
+#define ST_FIXED_BUFFER_SIZE (ST_FIXED_BUFFER_BLOCKS * ST_KILOBYTE)
+
+/* The buffer size should fit into the 24 bits for length in the
+ 6-byte SCSI read and write commands. */
+#if ST_FIXED_BUFFER_SIZE >= (2 << 24 - 1)
+#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
+#endif
+
+static int debugging = DEBUG;
+
+#define MAX_RETRIES 0
+#define MAX_WRITE_RETRIES 0
+#define MAX_READY_RETRIES 0
+#define NO_TAPE NOT_READY
+
+#define ST_TIMEOUT (900 * HZ)
+#define ST_LONG_TIMEOUT (14000 * HZ)
+
+/* Remove mode bits and auto-rewind bit (7) */
+#define TAPE_NR(x) ( ((iminor(x) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \
+ (iminor(x) & ~(-1 << ST_MODE_SHIFT)) )
+#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
+
+/* Construct the minor number from the device (d), mode (m), and non-rewind (n) data */
+#define TAPE_MINOR(d, m, n) (((d & ~(255 >> (ST_NBR_MODE_BITS + 1))) << (ST_NBR_MODE_BITS + 1)) | \
+ (d & (255 >> (ST_NBR_MODE_BITS + 1))) | (m << ST_MODE_SHIFT) | ((n != 0) << 7) )
+
+/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
+ 24 bits) */
+#define SET_DENS_AND_BLK 0x10001
+
+static DEFINE_RWLOCK(st_dev_arr_lock);
+
+static int st_fixed_buffer_size = ST_FIXED_BUFFER_SIZE;
+static int st_max_sg_segs = ST_MAX_SG;
+
+static struct scsi_tape **scsi_tapes = NULL;
+
+static int modes_defined;
+
+static struct st_buffer *new_tape_buffer(int, int, int);
+static int enlarge_buffer(struct st_buffer *, int, int);
+static void normalize_buffer(struct st_buffer *);
+static int append_to_buffer(const char __user *, struct st_buffer *, int);
+static int from_buffer(struct st_buffer *, char __user *, int);
+static void move_buffer_data(struct st_buffer *, int);
+static void buf_to_sg(struct st_buffer *, unsigned int);
+
+static int st_map_user_pages(struct scatterlist *, const unsigned int,
+ unsigned long, size_t, int, unsigned long);
+static int sgl_map_user_pages(struct scatterlist *, const unsigned int,
+ unsigned long, size_t, int);
+static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int);
+
+static int st_probe(struct device *);
+static int st_remove(struct device *);
+static int st_init_command(struct scsi_cmnd *);
+
+static void do_create_driverfs_files(void);
+static void do_remove_driverfs_files(void);
+static void do_create_class_files(struct scsi_tape *, int, int);
+
+static struct scsi_driver st_template = {
+ .owner = THIS_MODULE,
+ .gendrv = {
+ .name = "st",
+ .probe = st_probe,
+ .remove = st_remove,
+ },
+ .init_command = st_init_command,
+};
+
+static int st_compression(struct scsi_tape *, int);
+
+static int find_partition(struct scsi_tape *);
+static int switch_partition(struct scsi_tape *);
+
+static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long);
+
+
+#include "osst_detect.h"
+#ifndef SIGS_FROM_OSST
+#define SIGS_FROM_OSST \
+ {"OnStream", "SC-", "", "osst"}, \
+ {"OnStream", "DI-", "", "osst"}, \
+ {"OnStream", "DP-", "", "osst"}, \
+ {"OnStream", "USB", "", "osst"}, \
+ {"OnStream", "FW-", "", "osst"}
+#endif
+
+struct st_reject_data {
+ char *vendor;
+ char *model;
+ char *rev;
+ char *driver_hint; /* Name of the correct driver, NULL if unknown */
+};
+
+static struct st_reject_data reject_list[] = {
+ /* {"XXX", "Yy-", "", NULL}, example */
+ SIGS_FROM_OSST,
+ {NULL, }};
+
+/* If the device signature is on the list of incompatible drives, the
+ function returns a pointer to the name of the correct driver (if known) */
+static char * st_incompatible(struct scsi_device* SDp)
+{
+ struct st_reject_data *rp;
+
+ for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
+ if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
+ !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
+ !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
+ if (rp->driver_hint)
+ return rp->driver_hint;
+ else
+ return "unknown";
+ }
+ return NULL;
+}
+
+
+static inline char *tape_name(struct scsi_tape *tape)
+{
+ return tape->disk->disk_name;
+}
+
+
+static void st_analyze_sense(struct scsi_request *SRpnt, struct st_cmdstatus *s)
+{
+ const u8 *ucp;
+ const u8 *sense = SRpnt->sr_sense_buffer;
+
+ s->have_sense = scsi_request_normalize_sense(SRpnt, &s->sense_hdr);
+ s->flags = 0;
+
+ if (s->have_sense) {
+ s->deferred = 0;
+ s->remainder_valid =
+ scsi_get_sense_info_fld(sense, SCSI_SENSE_BUFFERSIZE, &s->uremainder64);
+ switch (sense[0] & 0x7f) {
+ case 0x71:
+ s->deferred = 1;
+ case 0x70:
+ s->fixed_format = 1;
+ s->flags = sense[2] & 0xe0;
+ break;
+ case 0x73:
+ s->deferred = 1;
+ case 0x72:
+ s->fixed_format = 0;
+ ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4);
+ s->flags = ucp ? (ucp[3] & 0xe0) : 0;
+ break;
+ }
+ }
+}
+
+
+/* Convert the result to success code */
+static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt)
+{
+ int result = SRpnt->sr_result;
+ u8 scode;
+ DEB(const char *stp;)
+ char *name = tape_name(STp);
+ struct st_cmdstatus *cmdstatp;
+
+ if (!result)
+ return 0;
+
+ cmdstatp = &STp->buffer->cmdstat;
+ st_analyze_sense(STp->buffer->last_SRpnt, cmdstatp);
+
+ if (cmdstatp->have_sense)
+ scode = STp->buffer->cmdstat.sense_hdr.sense_key;
+ else
+ scode = 0;
+
+ DEB(
+ if (debugging) {
+ printk(ST_DEB_MSG "%s: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
+ name, result,
+ SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2],
+ SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5],
+ SRpnt->sr_bufflen);
+ if (cmdstatp->have_sense)
+ scsi_print_req_sense("st", SRpnt);
+ } ) /* end DEB */
+ if (!debugging) { /* Abnormal conditions for tape */
+ if (!cmdstatp->have_sense)
+ printk(KERN_WARNING
+ "%s: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
+ name, result, suggestion(result),
+ driver_byte(result) & DRIVER_MASK, host_byte(result));
+ else if (cmdstatp->have_sense &&
+ scode != NO_SENSE &&
+ scode != RECOVERED_ERROR &&
+ /* scode != UNIT_ATTENTION && */
+ scode != BLANK_CHECK &&
+ scode != VOLUME_OVERFLOW &&
+ SRpnt->sr_cmnd[0] != MODE_SENSE &&
+ SRpnt->sr_cmnd[0] != TEST_UNIT_READY) {
+ printk(KERN_WARNING "%s: Error with sense data: ", name);
+ scsi_print_req_sense("st", SRpnt);
+ }
+ }
+
+ if (cmdstatp->fixed_format &&
+ STp->cln_mode >= EXTENDED_SENSE_START) { /* Only fixed format sense */
+ if (STp->cln_sense_value)
+ STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] &
+ STp->cln_sense_mask) == STp->cln_sense_value);
+ else
+ STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] &
+ STp->cln_sense_mask) != 0);
+ }
+ if (cmdstatp->have_sense &&
+ cmdstatp->sense_hdr.asc == 0 && cmdstatp->sense_hdr.ascq == 0x17)
+ STp->cleaning_req = 1; /* ASC and ASCQ => cleaning requested */
+
+ STp->pos_unknown |= STp->device->was_reset;
+
+ if (cmdstatp->have_sense &&
+ scode == RECOVERED_ERROR
+#if ST_RECOVERED_WRITE_FATAL
+ && SRpnt->sr_cmnd[0] != WRITE_6
+ && SRpnt->sr_cmnd[0] != WRITE_FILEMARKS
+#endif
+ ) {
+ STp->recover_count++;
+ STp->recover_reg++;
+
+ DEB(
+ if (debugging) {
+ if (SRpnt->sr_cmnd[0] == READ_6)
+ stp = "read";
+ else if (SRpnt->sr_cmnd[0] == WRITE_6)
+ stp = "write";
+ else
+ stp = "ioctl";
+ printk(ST_DEB_MSG "%s: Recovered %s error (%d).\n", name, stp,
+ STp->recover_count);
+ } ) /* end DEB */
+
+ if (cmdstatp->flags == 0)
+ return 0;
+ }
+ return (-EIO);
+}
+
+
+/* Wakeup from interrupt */
+static void st_sleep_done(struct scsi_cmnd * SCpnt)
+{
+ struct scsi_tape *STp = container_of(SCpnt->request->rq_disk->private_data,
+ struct scsi_tape, driver);
+
+ (STp->buffer)->cmdstat.midlevel_result = SCpnt->result;
+ SCpnt->request->rq_status = RQ_SCSI_DONE;
+ (STp->buffer)->last_SRpnt = SCpnt->sc_request;
+ DEB( STp->write_pending = 0; )
+
+ complete(SCpnt->request->waiting);
+}
+
+/* Do the scsi command. Waits until command performed if do_wait is true.
+ Otherwise write_behind_check() is used to check that the command
+ has finished. */
+static struct scsi_request *
+st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd,
+ int bytes, int direction, int timeout, int retries, int do_wait)
+{
+ unsigned char *bp;
+
+ if (SRpnt == NULL) {
+ SRpnt = scsi_allocate_request(STp->device, GFP_ATOMIC);
+ if (SRpnt == NULL) {
+ DEBC( printk(KERN_ERR "%s: Can't get SCSI request.\n",
+ tape_name(STp)); );
+ if (signal_pending(current))
+ (STp->buffer)->syscall_result = (-EINTR);
+ else
+ (STp->buffer)->syscall_result = (-EBUSY);
+ return NULL;
+ }
+ }
+
+ init_completion(&STp->wait);
+ SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length);
+ if (SRpnt->sr_use_sg) {
+ if (!STp->buffer->do_dio)
+ buf_to_sg(STp->buffer, bytes);
+ SRpnt->sr_use_sg = (STp->buffer)->sg_segs;
+ bp = (char *) &((STp->buffer)->sg[0]);
+ } else
+ bp = (STp->buffer)->b_data;
+ SRpnt->sr_data_direction = direction;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_request->waiting = &(STp->wait);
+ SRpnt->sr_request->rq_status = RQ_SCSI_BUSY;
+ SRpnt->sr_request->rq_disk = STp->disk;
+ STp->buffer->cmdstat.have_sense = 0;
+
+ scsi_do_req(SRpnt, (void *) cmd, bp, bytes,
+ st_sleep_done, timeout, retries);
+
+ if (do_wait) {
+ wait_for_completion(SRpnt->sr_request->waiting);
+ SRpnt->sr_request->waiting = NULL;
+ (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
+ }
+ return SRpnt;
+}
+
+
+/* Handle the write-behind checking (waits for completion). Returns -ENOSPC if
+ write has been correct but EOM early warning reached, -EIO if write ended in
+ error or zero if write successful. Asynchronous writes are used only in
+ variable block mode. */
+static int write_behind_check(struct scsi_tape * STp)
+{
+ int retval = 0;
+ struct st_buffer *STbuffer;
+ struct st_partstat *STps;
+ struct st_cmdstatus *cmdstatp;
+
+ STbuffer = STp->buffer;
+ if (!STbuffer->writing)
+ return 0;
+
+ DEB(
+ if (STp->write_pending)
+ STp->nbr_waits++;
+ else
+ STp->nbr_finished++;
+ ) /* end DEB */
+
+ wait_for_completion(&(STp->wait));
+ (STp->buffer)->last_SRpnt->sr_request->waiting = NULL;
+
+ (STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt);
+ scsi_release_request((STp->buffer)->last_SRpnt);
+
+ STbuffer->buffer_bytes -= STbuffer->writing;
+ STps = &(STp->ps[STp->partition]);
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0)
+ STps->drv_block++;
+ else
+ STps->drv_block += STbuffer->writing / STp->block_size;
+ }
+
+ cmdstatp = &STbuffer->cmdstat;
+ if (STbuffer->syscall_result) {
+ retval = -EIO;
+ if (cmdstatp->have_sense && !cmdstatp->deferred &&
+ (cmdstatp->flags & SENSE_EOM) &&
+ (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
+ cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR)) {
+ /* EOM at write-behind, has all data been written? */
+ if (!cmdstatp->remainder_valid ||
+ cmdstatp->uremainder64 == 0)
+ retval = -ENOSPC;
+ }
+ if (retval == -EIO)
+ STps->drv_block = -1;
+ }
+ STbuffer->writing = 0;
+
+ DEB(if (debugging && retval)
+ printk(ST_DEB_MSG "%s: Async write error %x, return value %d.\n",
+ tape_name(STp), STbuffer->cmdstat.midlevel_result, retval);) /* end DEB */
+
+ return retval;
+}
+
+
+/* Step over EOF if it has been inadvertently crossed (ioctl not used because
+ it messes up the block number). */
+static int cross_eof(struct scsi_tape * STp, int forward)
+{
+ struct scsi_request *SRpnt;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+
+ cmd[0] = SPACE;
+ cmd[1] = 0x01; /* Space FileMarks */
+ if (forward) {
+ cmd[2] = cmd[3] = 0;
+ cmd[4] = 1;
+ } else
+ cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */
+ cmd[5] = 0;
+
+ DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n",
+ tape_name(STp), forward ? "forward" : "backward"));
+
+ SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
+ STp->device->timeout, MAX_RETRIES, 1);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
+
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+
+ if ((STp->buffer)->cmdstat.midlevel_result != 0)
+ printk(KERN_ERR "%s: Stepping over filemark %s failed.\n",
+ tape_name(STp), forward ? "forward" : "backward");
+
+ return (STp->buffer)->syscall_result;
+}
+
+
+/* Flush the write buffer (never need to write if variable blocksize). */
+static int flush_write_buffer(struct scsi_tape * STp)
+{
+ int offset, transfer, blks;
+ int result;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt;
+ struct st_partstat *STps;
+
+ result = write_behind_check(STp);
+ if (result)
+ return result;
+
+ result = 0;
+ if (STp->dirty == 1) {
+
+ offset = (STp->buffer)->buffer_bytes;
+ transfer = ((offset + STp->block_size - 1) /
+ STp->block_size) * STp->block_size;
+ DEBC(printk(ST_DEB_MSG "%s: Flushing %d bytes.\n",
+ tape_name(STp), transfer));
+
+ memset((STp->buffer)->b_data + offset, 0, transfer - offset);
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_6;
+ cmd[1] = 1;
+ blks = transfer / STp->block_size;
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+
+ SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE,
+ STp->device->timeout, MAX_WRITE_RETRIES, 1);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
+
+ STps = &(STp->ps[STp->partition]);
+ if ((STp->buffer)->syscall_result != 0) {
+ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+
+ if (cmdstatp->have_sense && !cmdstatp->deferred &&
+ (cmdstatp->flags & SENSE_EOM) &&
+ (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
+ cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
+ (!cmdstatp->remainder_valid ||
+ cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */
+ STp->dirty = 0;
+ (STp->buffer)->buffer_bytes = 0;
+ if (STps->drv_block >= 0)
+ STps->drv_block += blks;
+ result = (-ENOSPC);
+ } else {
+ printk(KERN_ERR "%s: Error on flush.\n",
+ tape_name(STp));
+ STps->drv_block = (-1);
+ result = (-EIO);
+ }
+ } else {
+ if (STps->drv_block >= 0)
+ STps->drv_block += blks;
+ STp->dirty = 0;
+ (STp->buffer)->buffer_bytes = 0;
+ }
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ }
+ return result;
+}
+
+
+/* Flush the tape buffer. The tape will be positioned correctly unless
+ seek_next is true. */
+static int flush_buffer(struct scsi_tape *STp, int seek_next)
+{
+ int backspace, result;
+ struct st_buffer *STbuffer;
+ struct st_partstat *STps;
+
+ STbuffer = STp->buffer;
+
+ /*
+ * If there was a bus reset, block further access
+ * to this device.
+ */
+ if (STp->pos_unknown)
+ return (-EIO);
+
+ if (STp->ready != ST_READY)
+ return 0;
+ STps = &(STp->ps[STp->partition]);
+ if (STps->rw == ST_WRITING) /* Writing */
+ return flush_write_buffer(STp);
+
+ if (STp->block_size == 0)
+ return 0;
+
+ backspace = ((STp->buffer)->buffer_bytes +
+ (STp->buffer)->read_pointer) / STp->block_size -
+ ((STp->buffer)->read_pointer + STp->block_size - 1) /
+ STp->block_size;
+ (STp->buffer)->buffer_bytes = 0;
+ (STp->buffer)->read_pointer = 0;
+ result = 0;
+ if (!seek_next) {
+ if (STps->eof == ST_FM_HIT) {
+ result = cross_eof(STp, 0); /* Back over the EOF hit */
+ if (!result)
+ STps->eof = ST_NOEOF;
+ else {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ }
+ }
+ if (!result && backspace > 0)
+ result = st_int_ioctl(STp, MTBSR, backspace);
+ } else if (STps->eof == ST_FM_HIT) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ }
+ return result;
+
+}
+
+/* Set the mode parameters */
+static int set_mode_densblk(struct scsi_tape * STp, struct st_modedef * STm)
+{
+ int set_it = 0;
+ unsigned long arg;
+ char *name = tape_name(STp);
+
+ if (!STp->density_changed &&
+ STm->default_density >= 0 &&
+ STm->default_density != STp->density) {
+ arg = STm->default_density;
+ set_it = 1;
+ } else
+ arg = STp->density;
+ arg <<= MT_ST_DENSITY_SHIFT;
+ if (!STp->blksize_changed &&
+ STm->default_blksize >= 0 &&
+ STm->default_blksize != STp->block_size) {
+ arg |= STm->default_blksize;
+ set_it = 1;
+ } else
+ arg |= STp->block_size;
+ if (set_it &&
+ st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) {
+ printk(KERN_WARNING
+ "%s: Can't set default block size to %d bytes and density %x.\n",
+ name, STm->default_blksize, STm->default_density);
+ if (modes_defined)
+ return (-EINVAL);
+ }
+ return 0;
+}
+
+
+/* Lock or unlock the drive door. Don't use when scsi_request allocated. */
+static int do_door_lock(struct scsi_tape * STp, int do_lock)
+{
+ int retval, cmd;
+ DEB(char *name = tape_name(STp);)
+
+
+ cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK;
+ DEBC(printk(ST_DEB_MSG "%s: %socking drive door.\n", name,
+ do_lock ? "L" : "Unl"));
+ retval = scsi_ioctl(STp->device, cmd, NULL);
+ if (!retval) {
+ STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
+ }
+ else {
+ STp->door_locked = ST_LOCK_FAILS;
+ }
+ return retval;
+}
+
+
+/* Set the internal state after reset */
+static void reset_state(struct scsi_tape *STp)
+{
+ int i;
+ struct st_partstat *STps;
+
+ STp->pos_unknown = 0;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = -1;
+ STps->drv_file = -1;
+ }
+ if (STp->can_partitions) {
+ STp->partition = find_partition(STp);
+ if (STp->partition < 0)
+ STp->partition = 0;
+ STp->new_partition = STp->partition;
+ }
+}
+
+/* Test if the drive is ready. Returns either one of the codes below or a negative system
+ error code. */
+#define CHKRES_READY 0
+#define CHKRES_NEW_SESSION 1
+#define CHKRES_NOT_READY 2
+#define CHKRES_NO_TAPE 3
+
+#define MAX_ATTENTIONS 10
+
+static int test_ready(struct scsi_tape *STp, int do_wait)
+{
+ int attentions, waits, max_wait, scode;
+ int retval = CHKRES_READY, new_session = 0;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt = NULL;
+ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+
+ max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
+
+ for (attentions=waits=0; ; ) {
+ memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
+ STp->long_timeout, MAX_READY_RETRIES, 1);
+
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
+ break;
+ }
+
+ if (cmdstatp->have_sense) {
+
+ scode = cmdstatp->sense_hdr.sense_key;
+
+ if (scode == UNIT_ATTENTION) { /* New media? */
+ new_session = 1;
+ if (attentions < MAX_ATTENTIONS) {
+ attentions++;
+ continue;
+ }
+ else {
+ retval = (-EIO);
+ break;
+ }
+ }
+
+ if (scode == NOT_READY) {
+ if (waits < max_wait) {
+ if (msleep_interruptible(1000)) {
+ retval = (-EINTR);
+ break;
+ }
+ waits++;
+ continue;
+ }
+ else {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ cmdstatp->sense_hdr.asc == 0x3a) /* Check ASC */
+ retval = CHKRES_NO_TAPE;
+ else
+ retval = CHKRES_NOT_READY;
+ break;
+ }
+ }
+ }
+
+ retval = (STp->buffer)->syscall_result;
+ if (!retval)
+ retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY;
+ break;
+ }
+
+ if (SRpnt != NULL)
+ scsi_release_request(SRpnt);
+ return retval;
+}
+
+
+/* See if the drive is ready and gather information about the tape. Return values:
+ < 0 negative error code from errno.h
+ 0 drive ready
+ 1 drive not ready (possibly no tape)
+*/
+static int check_tape(struct scsi_tape *STp, struct file *filp)
+{
+ int i, retval, new_session = 0, do_wait;
+ unsigned char cmd[MAX_COMMAND_SIZE], saved_cleaning;
+ unsigned short st_flags = filp->f_flags;
+ struct scsi_request *SRpnt = NULL;
+ struct st_modedef *STm;
+ struct st_partstat *STps;
+ char *name = tape_name(STp);
+ struct inode *inode = filp->f_dentry->d_inode;
+ int mode = TAPE_MODE(inode);
+
+ STp->ready = ST_READY;
+
+ if (mode != STp->current_mode) {
+ DEBC(printk(ST_DEB_MSG "%s: Mode change from %d to %d.\n",
+ name, STp->current_mode, mode));
+ new_session = 1;
+ STp->current_mode = mode;
+ }
+ STm = &(STp->modes[STp->current_mode]);
+
+ saved_cleaning = STp->cleaning_req;
+ STp->cleaning_req = 0;
+
+ do_wait = ((filp->f_flags & O_NONBLOCK) == 0);
+ retval = test_ready(STp, do_wait);
+
+ if (retval < 0)
+ goto err_out;
+
+ if (retval == CHKRES_NEW_SESSION) {
+ STp->pos_unknown = 0;
+ STp->partition = STp->new_partition = 0;
+ if (STp->can_partitions)
+ STp->nbr_partitions = 1; /* This guess will be updated later
+ if necessary */
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = 0;
+ STps->drv_file = 0;
+ }
+ new_session = 1;
+ }
+ else {
+ STp->cleaning_req |= saved_cleaning;
+
+ if (retval == CHKRES_NOT_READY || retval == CHKRES_NO_TAPE) {
+ if (retval == CHKRES_NO_TAPE)
+ STp->ready = ST_NO_TAPE;
+ else
+ STp->ready = ST_NOT_READY;
+
+ STp->density = 0; /* Clear the erroneous "residue" */
+ STp->write_prot = 0;
+ STp->block_size = 0;
+ STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
+ STp->partition = STp->new_partition = 0;
+ STp->door_locked = ST_UNLOCKED;
+ return CHKRES_NOT_READY;
+ }
+ }
+
+ if (STp->omit_blklims)
+ STp->min_block = STp->max_block = (-1);
+ else {
+ memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
+ cmd[0] = READ_BLOCK_LIMITS;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE,
+ STp->device->timeout, MAX_READY_RETRIES, 1);
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
+ goto err_out;
+ }
+
+ if (!SRpnt->sr_result && !STp->buffer->cmdstat.have_sense) {
+ STp->max_block = ((STp->buffer)->b_data[1] << 16) |
+ ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
+ STp->min_block = ((STp->buffer)->b_data[4] << 8) |
+ (STp->buffer)->b_data[5];
+ if ( DEB( debugging || ) !STp->inited)
+ printk(KERN_WARNING
+ "%s: Block limits %d - %d bytes.\n", name,
+ STp->min_block, STp->max_block);
+ } else {
+ STp->min_block = STp->max_block = (-1);
+ DEBC(printk(ST_DEB_MSG "%s: Can't read block limits.\n",
+ name));
+ }
+ }
+
+ memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SENSE;
+ cmd[4] = 12;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE,
+ STp->device->timeout, MAX_READY_RETRIES, 1);
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
+ goto err_out;
+ }
+
+ if ((STp->buffer)->syscall_result != 0) {
+ DEBC(printk(ST_DEB_MSG "%s: No Mode Sense.\n", name));
+ STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */
+ (STp->buffer)->syscall_result = 0; /* Prevent error propagation */
+ STp->drv_write_prot = 0;
+ } else {
+ DEBC(printk(ST_DEB_MSG
+ "%s: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n",
+ name,
+ (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
+ (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]));
+
+ if ((STp->buffer)->b_data[3] >= 8) {
+ STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7;
+ STp->density = (STp->buffer)->b_data[4];
+ STp->block_size = (STp->buffer)->b_data[9] * 65536 +
+ (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
+ DEBC(printk(ST_DEB_MSG
+ "%s: Density %x, tape length: %x, drv buffer: %d\n",
+ name, STp->density, (STp->buffer)->b_data[5] * 65536 +
+ (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
+ STp->drv_buffer));
+ }
+ STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
+ }
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ STp->inited = 1;
+
+ if (STp->block_size > 0)
+ (STp->buffer)->buffer_blocks =
+ (STp->buffer)->buffer_size / STp->block_size;
+ else
+ (STp->buffer)->buffer_blocks = 1;
+ (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
+
+ DEBC(printk(ST_DEB_MSG
+ "%s: Block size: %d, buffer size: %d (%d blocks).\n", name,
+ STp->block_size, (STp->buffer)->buffer_size,
+ (STp->buffer)->buffer_blocks));
+
+ if (STp->drv_write_prot) {
+ STp->write_prot = 1;
+
+ DEBC(printk(ST_DEB_MSG "%s: Write protected\n", name));
+
+ if (do_wait &&
+ ((st_flags & O_ACCMODE) == O_WRONLY ||
+ (st_flags & O_ACCMODE) == O_RDWR)) {
+ retval = (-EROFS);
+ goto err_out;
+ }
+ }
+
+ if (STp->can_partitions && STp->nbr_partitions < 1) {
+ /* This code is reached when the device is opened for the first time
+ after the driver has been initialized with tape in the drive and the
+ partition support has been enabled. */
+ DEBC(printk(ST_DEB_MSG
+ "%s: Updating partition number in status.\n", name));
+ if ((STp->partition = find_partition(STp)) < 0) {
+ retval = STp->partition;
+ goto err_out;
+ }
+ STp->new_partition = STp->partition;
+ STp->nbr_partitions = 1; /* This guess will be updated when necessary */
+ }
+
+ if (new_session) { /* Change the drive parameters for the new mode */
+ STp->density_changed = STp->blksize_changed = 0;
+ STp->compression_changed = 0;
+ if (!(STm->defaults_for_writes) &&
+ (retval = set_mode_densblk(STp, STm)) < 0)
+ goto err_out;
+
+ if (STp->default_drvbuffer != 0xff) {
+ if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
+ printk(KERN_WARNING
+ "%s: Can't set default drive buffering to %d.\n",
+ name, STp->default_drvbuffer);
+ }
+ }
+
+ return CHKRES_READY;
+
+ err_out:
+ return retval;
+}
+
+
+ /* Open the device. Needs to be called with BKL only because of incrementing the SCSI host
+ module count. */
+static int st_open(struct inode *inode, struct file *filp)
+{
+ int i, retval = (-EIO);
+ struct scsi_tape *STp;
+ struct st_partstat *STps;
+ int dev = TAPE_NR(inode);
+ char *name;
+
+ /*
+ * We really want to do nonseekable_open(inode, filp); here, but some
+ * versions of tar incorrectly call lseek on tapes and bail out if that
+ * fails. So we disallow pread() and pwrite(), but permit lseeks.
+ */
+ filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
+
+ write_lock(&st_dev_arr_lock);
+ if (dev >= st_dev_max || scsi_tapes == NULL ||
+ ((STp = scsi_tapes[dev]) == NULL)) {
+ write_unlock(&st_dev_arr_lock);
+ return (-ENXIO);
+ }
+ filp->private_data = STp;
+ name = tape_name(STp);
+
+ if (STp->in_use) {
+ write_unlock(&st_dev_arr_lock);
+ DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); )
+ return (-EBUSY);
+ }
+
+ if(scsi_device_get(STp->device)) {
+ write_unlock(&st_dev_arr_lock);
+ return (-ENXIO);
+ }
+ STp->in_use = 1;
+ write_unlock(&st_dev_arr_lock);
+ STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0;
+
+ if (!scsi_block_when_processing_errors(STp->device)) {
+ retval = (-ENXIO);
+ goto err_out;
+ }
+
+ /* See that we have at least a one page buffer available */
+ if (!enlarge_buffer(STp->buffer, PAGE_SIZE, STp->restr_dma)) {
+ printk(KERN_WARNING "%s: Can't allocate one page tape buffer.\n",
+ name);
+ retval = (-EOVERFLOW);
+ goto err_out;
+ }
+
+ (STp->buffer)->writing = 0;
+ (STp->buffer)->syscall_result = 0;
+
+ STp->write_prot = ((filp->f_flags & O_ACCMODE) == O_RDONLY);
+
+ STp->dirty = 0;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ }
+ STp->recover_count = 0;
+ DEB( STp->nbr_waits = STp->nbr_finished = 0;
+ STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = STp->nbr_combinable = 0; )
+
+ retval = check_tape(STp, filp);
+ if (retval < 0)
+ goto err_out;
+ if ((filp->f_flags & O_NONBLOCK) == 0 &&
+ retval != CHKRES_READY) {
+ retval = (-EIO);
+ goto err_out;
+ }
+ return 0;
+
+ err_out:
+ normalize_buffer(STp->buffer);
+ STp->in_use = 0;
+ scsi_device_put(STp->device);
+ return retval;
+
+}
+
+
+/* Flush the tape buffer before close */
+static int st_flush(struct file *filp)
+{
+ int result = 0, result2;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt;
+ struct scsi_tape *STp = filp->private_data;
+ struct st_modedef *STm = &(STp->modes[STp->current_mode]);
+ struct st_partstat *STps = &(STp->ps[STp->partition]);
+ char *name = tape_name(STp);
+
+ if (file_count(filp) > 1)
+ return 0;
+
+ if (STps->rw == ST_WRITING && !STp->pos_unknown) {
+ result = flush_write_buffer(STp);
+ if (result != 0 && result != (-ENOSPC))
+ goto out;
+ }
+
+ if (STp->can_partitions &&
+ (result2 = switch_partition(STp)) < 0) {
+ DEBC(printk(ST_DEB_MSG
+ "%s: switch_partition at close failed.\n", name));
+ if (result == 0)
+ result = result2;
+ goto out;
+ }
+
+ DEBC( if (STp->nbr_requests)
+ printk(KERN_WARNING "%s: Number of r/w requests %d, dio used in %d, pages %d (%d).\n",
+ name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable));
+
+ if (STps->rw == ST_WRITING && !STp->pos_unknown) {
+ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+
+ DEBC(printk(ST_DEB_MSG "%s: Async write waits %d, finished %d.\n",
+ name, STp->nbr_waits, STp->nbr_finished);
+ )
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_FILEMARKS;
+ cmd[4] = 1 + STp->two_fm;
+
+ SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
+ STp->device->timeout, MAX_WRITE_RETRIES, 1);
+ if (!SRpnt) {
+ result = (STp->buffer)->syscall_result;
+ goto out;
+ }
+
+ if (STp->buffer->syscall_result == 0 ||
+ (cmdstatp->have_sense && !cmdstatp->deferred &&
+ (cmdstatp->flags & SENSE_EOM) &&
+ (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
+ cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
+ (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0))) {
+ /* Write successful at EOM */
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ if (STp->two_fm)
+ cross_eof(STp, 0);
+ STps->eof = ST_FM;
+ }
+ else { /* Write error */
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ printk(KERN_ERR "%s: Error on write filemark.\n", name);
+ if (result == 0)
+ result = (-EIO);
+ }
+
+ DEBC(printk(ST_DEB_MSG "%s: Buffer flushed, %d EOF(s) written\n",
+ name, cmd[4]));
+ } else if (!STp->rew_at_close) {
+ STps = &(STp->ps[STp->partition]);
+ if (!STm->sysv || STps->rw != ST_READING) {
+ if (STp->can_bsr)
+ result = flush_buffer(STp, 0);
+ else if (STps->eof == ST_FM_HIT) {
+ result = cross_eof(STp, 0);
+ if (result) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ } else
+ STps->eof = ST_NOEOF;
+ }
+ } else if ((STps->eof == ST_NOEOF &&
+ !(result = cross_eof(STp, 1))) ||
+ STps->eof == ST_FM_HIT) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ }
+ }
+
+ out:
+ if (STp->rew_at_close) {
+ result2 = st_int_ioctl(STp, MTREW, 1);
+ if (result == 0)
+ result = result2;
+ }
+ return result;
+}
+
+
+/* Close the device and release it. BKL is not needed: this is the only thread
+ accessing this tape. */
+static int st_release(struct inode *inode, struct file *filp)
+{
+ int result = 0;
+ struct scsi_tape *STp = filp->private_data;
+
+ if (STp->door_locked == ST_LOCKED_AUTO)
+ do_door_lock(STp, 0);
+
+ normalize_buffer(STp->buffer);
+ write_lock(&st_dev_arr_lock);
+ STp->in_use = 0;
+ write_unlock(&st_dev_arr_lock);
+ scsi_device_put(STp->device);
+
+ return result;
+}
+
+/* The checks common to both reading and writing */
+static ssize_t rw_checks(struct scsi_tape *STp, struct file *filp, size_t count)
+{
+ ssize_t retval = 0;
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if (!scsi_block_when_processing_errors(STp->device)) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ retval = (-ENOMEDIUM);
+ else
+ retval = (-EIO);
+ goto out;
+ }
+
+ if (! STp->modes[STp->current_mode].defined) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+
+ /*
+ * If there was a bus reset, block further access
+ * to this device.
+ */
+ if (STp->pos_unknown) {
+ retval = (-EIO);
+ goto out;
+ }
+
+ if (count == 0)
+ goto out;
+
+ DEB(
+ if (!STp->in_use) {
+ printk(ST_DEB_MSG "%s: Incorrect device.\n", tape_name(STp));
+ retval = (-EIO);
+ goto out;
+ } ) /* end DEB */
+
+ if (STp->can_partitions &&
+ (retval = switch_partition(STp)) < 0)
+ goto out;
+
+ if (STp->block_size == 0 && STp->max_block > 0 &&
+ (count < STp->min_block || count > STp->max_block)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+ !do_door_lock(STp, 1))
+ STp->door_locked = ST_LOCKED_AUTO;
+
+ out:
+ return retval;
+}
+
+
+static int setup_buffering(struct scsi_tape *STp, const char __user *buf,
+ size_t count, int is_read)
+{
+ int i, bufsize, retval = 0;
+ struct st_buffer *STbp = STp->buffer;
+
+ if (is_read)
+ i = STp->try_dio && try_rdio;
+ else
+ i = STp->try_dio && try_wdio;
+ if (i && ((unsigned long)buf & queue_dma_alignment(
+ STp->device->request_queue)) == 0) {
+ i = st_map_user_pages(&(STbp->sg[0]), STbp->use_sg,
+ (unsigned long)buf, count, (is_read ? READ : WRITE),
+ STp->max_pfn);
+ if (i > 0) {
+ STbp->do_dio = i;
+ STbp->buffer_bytes = 0; /* can be used as transfer counter */
+ }
+ else
+ STbp->do_dio = 0; /* fall back to buffering with any error */
+ STbp->sg_segs = STbp->do_dio;
+ STbp->frp_sg_current = 0;
+ DEB(
+ if (STbp->do_dio) {
+ STp->nbr_dio++;
+ STp->nbr_pages += STbp->do_dio;
+ for (i=1; i < STbp->do_dio; i++)
+ if (page_to_pfn(STbp->sg[i].page) == page_to_pfn(STbp->sg[i-1].page) + 1)
+ STp->nbr_combinable++;
+ }
+ )
+ } else
+ STbp->do_dio = 0;
+ DEB( STp->nbr_requests++; )
+
+ if (!STbp->do_dio) {
+ if (STp->block_size)
+ bufsize = STp->block_size > st_fixed_buffer_size ?
+ STp->block_size : st_fixed_buffer_size;
+ else
+ bufsize = count;
+ if (bufsize > STbp->buffer_size &&
+ !enlarge_buffer(STbp, bufsize, STp->restr_dma)) {
+ printk(KERN_WARNING "%s: Can't allocate %d byte tape buffer.\n",
+ tape_name(STp), bufsize);
+ retval = (-EOVERFLOW);
+ goto out;
+ }
+ if (STp->block_size)
+ STbp->buffer_blocks = bufsize / STp->block_size;
+ }
+
+ out:
+ return retval;
+}
+
+
+/* Can be called more than once after each setup_buffer() */
+static void release_buffering(struct scsi_tape *STp)
+{
+ struct st_buffer *STbp;
+
+ STbp = STp->buffer;
+ if (STbp->do_dio) {
+ sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, 0);
+ STbp->do_dio = 0;
+ }
+}
+
+
+/* Write command */
+static ssize_t
+st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
+{
+ ssize_t total;
+ ssize_t i, do_count, blks, transfer;
+ ssize_t retval;
+ int undone, retry_eot = 0, scode;
+ int async_write;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ const char __user *b_point;
+ struct scsi_request *SRpnt = NULL;
+ struct scsi_tape *STp = filp->private_data;
+ struct st_modedef *STm;
+ struct st_partstat *STps;
+ struct st_buffer *STbp;
+ char *name = tape_name(STp);
+
+ if (down_interruptible(&STp->lock))
+ return -ERESTARTSYS;
+
+ retval = rw_checks(STp, filp, count);
+ if (retval || count == 0)
+ goto out;
+
+ /* Write must be integral number of blocks */
+ if (STp->block_size != 0 && (count % STp->block_size) != 0) {
+ printk(KERN_WARNING "%s: Write not multiple of tape block size.\n",
+ name);
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
+
+ if (STp->write_prot) {
+ retval = (-EACCES);
+ goto out;
+ }
+
+
+ if (STps->rw == ST_READING) {
+ retval = flush_buffer(STp, 0);
+ if (retval)
+ goto out;
+ STps->rw = ST_WRITING;
+ } else if (STps->rw != ST_WRITING &&
+ STps->drv_file == 0 && STps->drv_block == 0) {
+ if ((retval = set_mode_densblk(STp, STm)) < 0)
+ goto out;
+ if (STm->default_compression != ST_DONT_TOUCH &&
+ !(STp->compression_changed)) {
+ if (st_compression(STp, (STm->default_compression == ST_YES))) {
+ printk(KERN_WARNING "%s: Can't set default compression.\n",
+ name);
+ if (modes_defined) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ }
+ }
+ }
+
+ STbp = STp->buffer;
+ i = write_behind_check(STp);
+ if (i) {
+ if (i == -ENOSPC)
+ STps->eof = ST_EOM_OK;
+ else
+ STps->eof = ST_EOM_ERROR;
+ }
+
+ if (STps->eof == ST_EOM_OK) {
+ STps->eof = ST_EOD_1; /* allow next write */
+ retval = (-ENOSPC);
+ goto out;
+ }
+ else if (STps->eof == ST_EOM_ERROR) {
+ retval = (-EIO);
+ goto out;
+ }
+
+ /* Check the buffer readability in cases where copy_user might catch
+ the problems after some tape movement. */
+ if (STp->block_size != 0 &&
+ !STbp->do_dio &&
+ (copy_from_user(&i, buf, 1) != 0 ||
+ copy_from_user(&i, buf + count - 1, 1) != 0)) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ retval = setup_buffering(STp, buf, count, 0);
+ if (retval)
+ goto out;
+
+ total = count;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = WRITE_6;
+ cmd[1] = (STp->block_size != 0);
+
+ STps->rw = ST_WRITING;
+
+ b_point = buf;
+ while (count > 0 && !retry_eot) {
+
+ if (STbp->do_dio) {
+ do_count = count;
+ }
+ else {
+ if (STp->block_size == 0)
+ do_count = count;
+ else {
+ do_count = STbp->buffer_blocks * STp->block_size -
+ STbp->buffer_bytes;
+ if (do_count > count)
+ do_count = count;
+ }
+
+ i = append_to_buffer(b_point, STbp, do_count);
+ if (i) {
+ retval = i;
+ goto out;
+ }
+ }
+ count -= do_count;
+ b_point += do_count;
+
+ async_write = STp->block_size == 0 && !STbp->do_dio &&
+ STm->do_async_writes && STps->eof < ST_EOM_OK;
+
+ if (STp->block_size != 0 && STm->do_buffer_writes &&
+ !(STp->try_dio && try_wdio) && STps->eof < ST_EOM_OK &&
+ STbp->buffer_bytes < STbp->buffer_size) {
+ STp->dirty = 1;
+ /* Don't write a buffer that is not full enough. */
+ if (!async_write && count == 0)
+ break;
+ }
+
+ retry_write:
+ if (STp->block_size == 0)
+ blks = transfer = do_count;
+ else {
+ if (!STbp->do_dio)
+ blks = STbp->buffer_bytes;
+ else
+ blks = do_count;
+ blks /= STp->block_size;
+ transfer = blks * STp->block_size;
+ }
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
+ STp->device->timeout, MAX_WRITE_RETRIES, !async_write);
+ if (!SRpnt) {
+ retval = STbp->syscall_result;
+ goto out;
+ }
+ if (async_write) {
+ STbp->writing = transfer;
+ STp->dirty = !(STbp->writing ==
+ STbp->buffer_bytes);
+ SRpnt = NULL; /* Prevent releasing this request! */
+ DEB( STp->write_pending = 1; )
+ break;
+ }
+
+ if (STbp->syscall_result != 0) {
+ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+
+ DEBC(printk(ST_DEB_MSG "%s: Error on write:\n", name));
+ if (cmdstatp->have_sense && (cmdstatp->flags & SENSE_EOM)) {
+ scode = cmdstatp->sense_hdr.sense_key;
+ if (cmdstatp->remainder_valid)
+ undone = (int)cmdstatp->uremainder64;
+ else if (STp->block_size == 0 &&
+ scode == VOLUME_OVERFLOW)
+ undone = transfer;
+ else
+ undone = 0;
+ if (STp->block_size != 0)
+ undone *= STp->block_size;
+ if (undone <= do_count) {
+ /* Only data from this write is not written */
+ count += undone;
+ do_count -= undone;
+ if (STp->block_size)
+ blks = (transfer - undone) / STp->block_size;
+ STps->eof = ST_EOM_OK;
+ /* Continue in fixed block mode if all written
+ in this request but still something left to write
+ (retval left to zero)
+ */
+ if (STp->block_size == 0 ||
+ undone > 0 || count == 0)
+ retval = (-ENOSPC); /* EOM within current request */
+ DEBC(printk(ST_DEB_MSG
+ "%s: EOM with %d bytes unwritten.\n",
+ name, (int)count));
+ } else {
+ /* EOT within data buffered earlier (possible only
+ in fixed block mode without direct i/o) */
+ if (!retry_eot && !cmdstatp->deferred &&
+ (scode == NO_SENSE || scode == RECOVERED_ERROR)) {
+ move_buffer_data(STp->buffer, transfer - undone);
+ retry_eot = 1;
+ if (STps->drv_block >= 0) {
+ STps->drv_block += (transfer - undone) /
+ STp->block_size;
+ }
+ STps->eof = ST_EOM_OK;
+ DEBC(printk(ST_DEB_MSG
+ "%s: Retry write of %d bytes at EOM.\n",
+ name, STp->buffer->buffer_bytes));
+ goto retry_write;
+ }
+ else {
+ /* Either error within data buffered by driver or
+ failed retry */
+ count -= do_count;
+ blks = do_count = 0;
+ STps->eof = ST_EOM_ERROR;
+ STps->drv_block = (-1); /* Too cautious? */
+ retval = (-EIO); /* EOM for old data */
+ DEBC(printk(ST_DEB_MSG
+ "%s: EOM with lost data.\n",
+ name));
+ }
+ }
+ } else {
+ count += do_count;
+ STps->drv_block = (-1); /* Too cautious? */
+ retval = (-EIO);
+ }
+
+ }
+
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0)
+ STps->drv_block += (do_count > 0);
+ else
+ STps->drv_block += blks;
+ }
+
+ STbp->buffer_bytes = 0;
+ STp->dirty = 0;
+
+ if (retval || retry_eot) {
+ if (count < total)
+ retval = total - count;
+ goto out;
+ }
+ }
+
+ if (STps->eof == ST_EOD_1)
+ STps->eof = ST_EOM_OK;
+ else if (STps->eof != ST_EOM_OK)
+ STps->eof = ST_NOEOF;
+ retval = total - count;
+
+ out:
+ if (SRpnt != NULL)
+ scsi_release_request(SRpnt);
+ release_buffering(STp);
+ up(&STp->lock);
+
+ return retval;
+}
+
+/* Read data from the tape. Returns zero in the normal case, one if the
+ eof status has changed, and the negative error code in case of a
+ fatal error. Otherwise updates the buffer and the eof state.
+
+ Does release user buffer mapping if it is set.
+*/
+static long read_tape(struct scsi_tape *STp, long count,
+ struct scsi_request ** aSRpnt)
+{
+ int transfer, blks, bytes;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt;
+ struct st_modedef *STm;
+ struct st_partstat *STps;
+ struct st_buffer *STbp;
+ int retval = 0;
+ char *name = tape_name(STp);
+
+ if (count == 0)
+ return 0;
+
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
+ if (STps->eof == ST_FM_HIT)
+ return 1;
+ STbp = STp->buffer;
+
+ if (STp->block_size == 0)
+ blks = bytes = count;
+ else {
+ if (!(STp->try_dio && try_rdio) && STm->do_read_ahead) {
+ blks = (STp->buffer)->buffer_blocks;
+ bytes = blks * STp->block_size;
+ } else {
+ bytes = count;
+ if (!STbp->do_dio && bytes > (STp->buffer)->buffer_size)
+ bytes = (STp->buffer)->buffer_size;
+ blks = bytes / STp->block_size;
+ bytes = blks * STp->block_size;
+ }
+ }
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = READ_6;
+ cmd[1] = (STp->block_size != 0);
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+
+ SRpnt = *aSRpnt;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE,
+ STp->device->timeout, MAX_RETRIES, 1);
+ release_buffering(STp);
+ *aSRpnt = SRpnt;
+ if (!SRpnt)
+ return STbp->syscall_result;
+
+ STbp->read_pointer = 0;
+ STps->at_sm = 0;
+
+ /* Something to check */
+ if (STbp->syscall_result) {
+ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+
+ retval = 1;
+ DEBC(printk(ST_DEB_MSG "%s: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ name,
+ SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[1],
+ SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3],
+ SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5],
+ SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7]));
+ if (cmdstatp->have_sense) {
+
+ if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
+ cmdstatp->flags &= 0xcf; /* No need for EOM in this case */
+
+ if (cmdstatp->flags != 0) { /* EOF, EOM, or ILI */
+ /* Compute the residual count */
+ if (cmdstatp->remainder_valid)
+ transfer = (int)cmdstatp->uremainder64;
+ else
+ transfer = 0;
+ if (STp->block_size == 0 &&
+ cmdstatp->sense_hdr.sense_key == MEDIUM_ERROR)
+ transfer = bytes;
+
+ if (cmdstatp->flags & SENSE_ILI) { /* ILI */
+ if (STp->block_size == 0) {
+ if (transfer <= 0) {
+ if (transfer < 0)
+ printk(KERN_NOTICE
+ "%s: Failed to read %d byte block with %d byte transfer.\n",
+ name, bytes - transfer, bytes);
+ if (STps->drv_block >= 0)
+ STps->drv_block += 1;
+ STbp->buffer_bytes = 0;
+ return (-ENOMEM);
+ }
+ STbp->buffer_bytes = bytes - transfer;
+ } else {
+ scsi_release_request(SRpnt);
+ SRpnt = *aSRpnt = NULL;
+ if (transfer == blks) { /* We did not get anything, error */
+ printk(KERN_NOTICE "%s: Incorrect block size.\n", name);
+ if (STps->drv_block >= 0)
+ STps->drv_block += blks - transfer + 1;
+ st_int_ioctl(STp, MTBSR, 1);
+ return (-EIO);
+ }
+ /* We have some data, deliver it */
+ STbp->buffer_bytes = (blks - transfer) *
+ STp->block_size;
+ DEBC(printk(ST_DEB_MSG
+ "%s: ILI but enough data received %ld %d.\n",
+ name, count, STbp->buffer_bytes));
+ if (STps->drv_block >= 0)
+ STps->drv_block += 1;
+ if (st_int_ioctl(STp, MTBSR, 1))
+ return (-EIO);
+ }
+ } else if (cmdstatp->flags & SENSE_FMK) { /* FM overrides EOM */
+ if (STps->eof != ST_FM_HIT)
+ STps->eof = ST_FM_HIT;
+ else
+ STps->eof = ST_EOD_2;
+ if (STp->block_size == 0)
+ STbp->buffer_bytes = 0;
+ else
+ STbp->buffer_bytes =
+ bytes - transfer * STp->block_size;
+ DEBC(printk(ST_DEB_MSG
+ "%s: EOF detected (%d bytes read).\n",
+ name, STbp->buffer_bytes));
+ } else if (cmdstatp->flags & SENSE_EOM) {
+ if (STps->eof == ST_FM)
+ STps->eof = ST_EOD_1;
+ else
+ STps->eof = ST_EOM_OK;
+ if (STp->block_size == 0)
+ STbp->buffer_bytes = bytes - transfer;
+ else
+ STbp->buffer_bytes =
+ bytes - transfer * STp->block_size;
+
+ DEBC(printk(ST_DEB_MSG "%s: EOM detected (%d bytes read).\n",
+ name, STbp->buffer_bytes));
+ }
+ }
+ /* end of EOF, EOM, ILI test */
+ else { /* nonzero sense key */
+ DEBC(printk(ST_DEB_MSG
+ "%s: Tape error while reading.\n", name));
+ STps->drv_block = (-1);
+ if (STps->eof == ST_FM &&
+ cmdstatp->sense_hdr.sense_key == BLANK_CHECK) {
+ DEBC(printk(ST_DEB_MSG
+ "%s: Zero returned for first BLANK CHECK after EOF.\n",
+ name));
+ STps->eof = ST_EOD_2; /* First BLANK_CHECK after FM */
+ } else /* Some other extended sense code */
+ retval = (-EIO);
+ }
+
+ if (STbp->buffer_bytes < 0) /* Caused by bogus sense data */
+ STbp->buffer_bytes = 0;
+ }
+ /* End of extended sense test */
+ else { /* Non-extended sense */
+ retval = STbp->syscall_result;
+ }
+
+ }
+ /* End of error handling */
+ else /* Read successful */
+ STbp->buffer_bytes = bytes;
+
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0)
+ STps->drv_block++;
+ else
+ STps->drv_block += STbp->buffer_bytes / STp->block_size;
+ }
+ return retval;
+}
+
+
+/* Read command */
+static ssize_t
+st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
+{
+ ssize_t total;
+ ssize_t retval = 0;
+ ssize_t i, transfer;
+ int special, do_dio = 0;
+ struct scsi_request *SRpnt = NULL;
+ struct scsi_tape *STp = filp->private_data;
+ struct st_modedef *STm;
+ struct st_partstat *STps;
+ struct st_buffer *STbp = STp->buffer;
+ DEB( char *name = tape_name(STp); )
+
+ if (down_interruptible(&STp->lock))
+ return -ERESTARTSYS;
+
+ retval = rw_checks(STp, filp, count);
+ if (retval || count == 0)
+ goto out;
+
+ STm = &(STp->modes[STp->current_mode]);
+ if (!(STm->do_read_ahead) && STp->block_size != 0 &&
+ (count % STp->block_size) != 0) {
+ retval = (-EINVAL); /* Read must be integral number of blocks */
+ goto out;
+ }
+
+ STps = &(STp->ps[STp->partition]);
+ if (STps->rw == ST_WRITING) {
+ retval = flush_buffer(STp, 0);
+ if (retval)
+ goto out;
+ STps->rw = ST_READING;
+ }
+ DEB(
+ if (debugging && STps->eof != ST_NOEOF)
+ printk(ST_DEB_MSG "%s: EOF/EOM flag up (%d). Bytes %d\n", name,
+ STps->eof, STbp->buffer_bytes);
+ ) /* end DEB */
+
+ retval = setup_buffering(STp, buf, count, 1);
+ if (retval)
+ goto out;
+ do_dio = STbp->do_dio;
+
+ if (STbp->buffer_bytes == 0 &&
+ STps->eof >= ST_EOD_1) {
+ if (STps->eof < ST_EOD) {
+ STps->eof += 1;
+ retval = 0;
+ goto out;
+ }
+ retval = (-EIO); /* EOM or Blank Check */
+ goto out;
+ }
+
+ if (do_dio) {
+ /* Check the buffer writability before any tape movement. Don't alter
+ buffer data. */
+ if (copy_from_user(&i, buf, 1) != 0 ||
+ copy_to_user(buf, &i, 1) != 0 ||
+ copy_from_user(&i, buf + count - 1, 1) != 0 ||
+ copy_to_user(buf + count - 1, &i, 1) != 0) {
+ retval = (-EFAULT);
+ goto out;
+ }
+ }
+
+ STps->rw = ST_READING;
+
+
+ /* Loop until enough data in buffer or a special condition found */
+ for (total = 0, special = 0; total < count && !special;) {
+
+ /* Get new data if the buffer is empty */
+ if (STbp->buffer_bytes == 0) {
+ special = read_tape(STp, count - total, &SRpnt);
+ if (special < 0) { /* No need to continue read */
+ retval = special;
+ goto out;
+ }
+ }
+
+ /* Move the data from driver buffer to user buffer */
+ if (STbp->buffer_bytes > 0) {
+ DEB(
+ if (debugging && STps->eof != ST_NOEOF)
+ printk(ST_DEB_MSG
+ "%s: EOF up (%d). Left %d, needed %d.\n", name,
+ STps->eof, STbp->buffer_bytes,
+ (int)(count - total));
+ ) /* end DEB */
+ transfer = STbp->buffer_bytes < count - total ?
+ STbp->buffer_bytes : count - total;
+ if (!do_dio) {
+ i = from_buffer(STbp, buf, transfer);
+ if (i) {
+ retval = i;
+ goto out;
+ }
+ }
+ buf += transfer;
+ total += transfer;
+ }
+
+ if (STp->block_size == 0)
+ break; /* Read only one variable length block */
+
+ } /* for (total = 0, special = 0;
+ total < count && !special; ) */
+
+ /* Change the eof state if no data from tape or buffer */
+ if (total == 0) {
+ if (STps->eof == ST_FM_HIT) {
+ STps->eof = ST_FM;
+ STps->drv_block = 0;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ } else if (STps->eof == ST_EOD_1) {
+ STps->eof = ST_EOD_2;
+ STps->drv_block = 0;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ } else if (STps->eof == ST_EOD_2)
+ STps->eof = ST_EOD;
+ } else if (STps->eof == ST_FM)
+ STps->eof = ST_NOEOF;
+ retval = total;
+
+ out:
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ }
+ if (do_dio) {
+ release_buffering(STp);
+ STbp->buffer_bytes = 0;
+ }
+ up(&STp->lock);
+
+ return retval;
+}
+
+
+
+DEB(
+/* Set the driver options */
+static void st_log_options(struct scsi_tape * STp, struct st_modedef * STm, char *name)
+{
+ if (debugging) {
+ printk(KERN_INFO
+ "%s: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+ name, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+ STm->do_read_ahead);
+ printk(KERN_INFO
+ "%s: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
+ name, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
+ printk(KERN_INFO
+ "%s: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+ name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+ STp->scsi2_logical);
+ printk(KERN_INFO
+ "%s: sysv: %d nowait: %d\n", name, STm->sysv, STp->immediate);
+ printk(KERN_INFO "%s: debugging: %d\n",
+ name, debugging);
+ }
+}
+ )
+
+
+static int st_set_options(struct scsi_tape *STp, long options)
+{
+ int value;
+ long code;
+ struct st_modedef *STm;
+ char *name = tape_name(STp);
+ struct cdev *cd0, *cd1;
+
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined) {
+ cd0 = STm->cdevs[0]; cd1 = STm->cdevs[1];
+ memcpy(STm, &(STp->modes[0]), sizeof(struct st_modedef));
+ STm->cdevs[0] = cd0; STm->cdevs[1] = cd1;
+ modes_defined = 1;
+ DEBC(printk(ST_DEB_MSG
+ "%s: Initialized mode %d definition from mode 0\n",
+ name, STp->current_mode));
+ }
+
+ code = options & MT_ST_OPTIONS;
+ if (code == MT_ST_BOOLEANS) {
+ STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+ STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
+ STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
+ STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
+ STp->two_fm = (options & MT_ST_TWO_FM) != 0;
+ STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
+ STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
+ STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
+ STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
+ if ((STp->device)->scsi_level >= SCSI_2)
+ STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
+ STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
+ STp->immediate = (options & MT_ST_NOWAIT) != 0;
+ STm->sysv = (options & MT_ST_SYSV) != 0;
+ DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
+ st_log_options(STp, STm, name); )
+ } else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
+ value = (code == MT_ST_SETBOOLEANS);
+ if ((options & MT_ST_BUFFER_WRITES) != 0)
+ STm->do_buffer_writes = value;
+ if ((options & MT_ST_ASYNC_WRITES) != 0)
+ STm->do_async_writes = value;
+ if ((options & MT_ST_DEF_WRITES) != 0)
+ STm->defaults_for_writes = value;
+ if ((options & MT_ST_READ_AHEAD) != 0)
+ STm->do_read_ahead = value;
+ if ((options & MT_ST_TWO_FM) != 0)
+ STp->two_fm = value;
+ if ((options & MT_ST_FAST_MTEOM) != 0)
+ STp->fast_mteom = value;
+ if ((options & MT_ST_AUTO_LOCK) != 0)
+ STp->do_auto_lock = value;
+ if ((options & MT_ST_CAN_BSR) != 0)
+ STp->can_bsr = value;
+ if ((options & MT_ST_NO_BLKLIMS) != 0)
+ STp->omit_blklims = value;
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (options & MT_ST_CAN_PARTITIONS) != 0)
+ STp->can_partitions = value;
+ if ((options & MT_ST_SCSI2LOGICAL) != 0)
+ STp->scsi2_logical = value;
+ if ((options & MT_ST_NOWAIT) != 0)
+ STp->immediate = value;
+ if ((options & MT_ST_SYSV) != 0)
+ STm->sysv = value;
+ DEB(
+ if ((options & MT_ST_DEBUGGING) != 0)
+ debugging = value;
+ st_log_options(STp, STm, name); )
+ } else if (code == MT_ST_WRITE_THRESHOLD) {
+ /* Retained for compatibility */
+ } else if (code == MT_ST_DEF_BLKSIZE) {
+ value = (options & ~MT_ST_OPTIONS);
+ if (value == ~MT_ST_OPTIONS) {
+ STm->default_blksize = (-1);
+ DEBC( printk(KERN_INFO "%s: Default block size disabled.\n", name));
+ } else {
+ STm->default_blksize = value;
+ DEBC( printk(KERN_INFO "%s: Default block size set to %d bytes.\n",
+ name, STm->default_blksize));
+ if (STp->ready == ST_READY) {
+ STp->blksize_changed = 0;
+ set_mode_densblk(STp, STm);
+ }
+ }
+ } else if (code == MT_ST_TIMEOUTS) {
+ value = (options & ~MT_ST_OPTIONS);
+ if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
+ STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
+ DEBC( printk(KERN_INFO "%s: Long timeout set to %d seconds.\n", name,
+ (value & ~MT_ST_SET_LONG_TIMEOUT)));
+ } else {
+ STp->device->timeout = value * HZ;
+ DEBC( printk(KERN_INFO "%s: Normal timeout set to %d seconds.\n",
+ name, value) );
+ }
+ } else if (code == MT_ST_SET_CLN) {
+ value = (options & ~MT_ST_OPTIONS) & 0xff;
+ if (value != 0 &&
+ value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE)
+ return (-EINVAL);
+ STp->cln_mode = value;
+ STp->cln_sense_mask = (options >> 8) & 0xff;
+ STp->cln_sense_value = (options >> 16) & 0xff;
+ printk(KERN_INFO
+ "%s: Cleaning request mode %d, mask %02x, value %02x\n",
+ name, value, STp->cln_sense_mask, STp->cln_sense_value);
+ } else if (code == MT_ST_DEF_OPTIONS) {
+ code = (options & ~MT_ST_CLEAR_DEFAULT);
+ value = (options & MT_ST_CLEAR_DEFAULT);
+ if (code == MT_ST_DEF_DENSITY) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STm->default_density = (-1);
+ DEBC( printk(KERN_INFO "%s: Density default disabled.\n",
+ name));
+ } else {
+ STm->default_density = value & 0xff;
+ DEBC( printk(KERN_INFO "%s: Density default set to %x\n",
+ name, STm->default_density));
+ if (STp->ready == ST_READY) {
+ STp->density_changed = 0;
+ set_mode_densblk(STp, STm);
+ }
+ }
+ } else if (code == MT_ST_DEF_DRVBUFFER) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STp->default_drvbuffer = 0xff;
+ DEBC( printk(KERN_INFO
+ "%s: Drive buffer default disabled.\n", name));
+ } else {
+ STp->default_drvbuffer = value & 7;
+ DEBC( printk(KERN_INFO
+ "%s: Drive buffer default set to %x\n",
+ name, STp->default_drvbuffer));
+ if (STp->ready == ST_READY)
+ st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer);
+ }
+ } else if (code == MT_ST_DEF_COMPRESSION) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STm->default_compression = ST_DONT_TOUCH;
+ DEBC( printk(KERN_INFO
+ "%s: Compression default disabled.\n", name));
+ } else {
+ if ((value & 0xff00) != 0) {
+ STp->c_algo = (value & 0xff00) >> 8;
+ DEBC( printk(KERN_INFO "%s: Compression algorithm set to 0x%x.\n",
+ name, STp->c_algo));
+ }
+ if ((value & 0xff) != 0xff) {
+ STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+ DEBC( printk(KERN_INFO "%s: Compression default set to %x\n",
+ name, (value & 1)));
+ if (STp->ready == ST_READY) {
+ STp->compression_changed = 0;
+ st_compression(STp, (STm->default_compression == ST_YES));
+ }
+ }
+ }
+ }
+ } else
+ return (-EIO);
+
+ return 0;
+}
+
+#define MODE_HEADER_LENGTH 4
+
+/* Mode header and page byte offsets */
+#define MH_OFF_DATA_LENGTH 0
+#define MH_OFF_MEDIUM_TYPE 1
+#define MH_OFF_DEV_SPECIFIC 2
+#define MH_OFF_BDESCS_LENGTH 3
+#define MP_OFF_PAGE_NBR 0
+#define MP_OFF_PAGE_LENGTH 1
+
+/* Mode header and page bit masks */
+#define MH_BIT_WP 0x80
+#define MP_MSK_PAGE_NBR 0x3f
+
+/* Don't return block descriptors */
+#define MODE_SENSE_OMIT_BDESCS 0x08
+
+#define MODE_SELECT_PAGE_FORMAT 0x10
+
+/* Read a mode page into the tape buffer. The block descriptors are included
+ if incl_block_descs is true. The page control is ored to the page number
+ parameter, if necessary. */
+static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
+{
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt = NULL;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SENSE;
+ if (omit_block_descs)
+ cmd[1] = MODE_SENSE_OMIT_BDESCS;
+ cmd[2] = page;
+ cmd[4] = 255;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE,
+ STp->device->timeout, 0, 1);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
+
+ scsi_release_request(SRpnt);
+
+ return (STp->buffer)->syscall_result;
+}
+
+
+/* Send the mode page in the tape buffer to the drive. Assumes that the mode data
+ in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */
+static int write_mode_page(struct scsi_tape *STp, int page, int slow)
+{
+ int pgo;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt = NULL;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = MODE_SELECT_PAGE_FORMAT;
+ pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
+ cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
+
+ /* Clear reserved fields */
+ (STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
+ (STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
+ (STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
+ (STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
+
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE,
+ (slow ? STp->long_timeout : STp->device->timeout), 0, 1);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
+
+ scsi_release_request(SRpnt);
+
+ return (STp->buffer)->syscall_result;
+}
+
+
+#define COMPRESSION_PAGE 0x0f
+#define COMPRESSION_PAGE_LENGTH 16
+
+#define CP_OFF_DCE_DCC 2
+#define CP_OFF_C_ALGO 7
+
+#define DCE_MASK 0x80
+#define DCC_MASK 0x40
+#define RED_MASK 0x60
+
+
+/* Control the compression with mode page 15. Algorithm not changed if zero.
+
+ The block descriptors are read and written because Sony SDT-7000 does not
+ work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+ Including block descriptors should not cause any harm to other drives. */
+
+static int st_compression(struct scsi_tape * STp, int state)
+{
+ int retval;
+ int mpoffs; /* Offset to mode page start */
+ unsigned char *b_data = (STp->buffer)->b_data;
+ DEB( char *name = tape_name(STp); )
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ /* Read the current page contents */
+ retval = read_mode_page(STp, COMPRESSION_PAGE, 0);
+ if (retval) {
+ DEBC(printk(ST_DEB_MSG "%s: Compression mode page not supported.\n",
+ name));
+ return (-EIO);
+ }
+
+ mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
+ DEBC(printk(ST_DEB_MSG "%s: Compression state is %d.\n", name,
+ (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0)));
+
+ /* Check if compression can be changed */
+ if ((b_data[mpoffs + CP_OFF_DCE_DCC] & DCC_MASK) == 0) {
+ DEBC(printk(ST_DEB_MSG "%s: Compression not supported.\n", name));
+ return (-EIO);
+ }
+
+ /* Do the change */
+ if (state) {
+ b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
+ if (STp->c_algo != 0)
+ b_data[mpoffs + CP_OFF_C_ALGO] = STp->c_algo;
+ }
+ else {
+ b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
+ if (STp->c_algo != 0)
+ b_data[mpoffs + CP_OFF_C_ALGO] = 0; /* no compression */
+ }
+
+ retval = write_mode_page(STp, COMPRESSION_PAGE, 0);
+ if (retval) {
+ DEBC(printk(ST_DEB_MSG "%s: Compression change failed.\n", name));
+ return (-EIO);
+ }
+ DEBC(printk(ST_DEB_MSG "%s: Compression state changed to %d.\n",
+ name, state));
+
+ STp->compression_changed = 1;
+ return 0;
+}
+
+
+/* Process the load and unload commands (does unload if the load code is zero) */
+static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_code)
+{
+ int retval = (-EIO), timeout;
+ DEB( char *name = tape_name(STp); )
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct st_partstat *STps;
+ struct scsi_request *SRpnt;
+
+ if (STp->ready != ST_READY && !load_code) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ cmd[0] = START_STOP;
+ if (load_code)
+ cmd[4] |= 1;
+ /*
+ * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
+ */
+ if (load_code >= 1 + MT_ST_HPLOADER_OFFSET
+ && load_code <= 6 + MT_ST_HPLOADER_OFFSET) {
+ DEBC(printk(ST_DEB_MSG "%s: Enhanced %sload slot %2d.\n",
+ name, (cmd[4]) ? "" : "un",
+ load_code - MT_ST_HPLOADER_OFFSET));
+ cmd[3] = load_code - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
+ }
+ if (STp->immediate) {
+ cmd[1] = 1; /* Don't wait for completion */
+ timeout = STp->device->timeout;
+ }
+ else
+ timeout = STp->long_timeout;
+
+ DEBC(
+ if (!load_code)
+ printk(ST_DEB_MSG "%s: Unloading tape.\n", name);
+ else
+ printk(ST_DEB_MSG "%s: Loading tape.\n", name);
+ );
+
+ SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
+ timeout, MAX_RETRIES, 1);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
+
+ retval = (STp->buffer)->syscall_result;
+ scsi_release_request(SRpnt);
+
+ if (!retval) { /* SCSI command successful */
+
+ if (!load_code) {
+ STp->rew_at_close = 0;
+ STp->ready = ST_NO_TAPE;
+ }
+ else {
+ STp->rew_at_close = STp->autorew_dev;
+ retval = check_tape(STp, filp);
+ if (retval > 0)
+ retval = 0;
+ }
+ }
+ else {
+ STps = &(STp->ps[STp->partition]);
+ STps->drv_file = STps->drv_block = (-1);
+ }
+
+ return retval;
+}
+
+#if DEBUG
+#define ST_DEB_FORWARD 0
+#define ST_DEB_BACKWARD 1
+static void deb_space_print(char *name, int direction, char *units, unsigned char *cmd)
+{
+ s32 sc;
+
+ sc = cmd[2] & 0x80 ? 0xff000000 : 0;
+ sc |= (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+ if (direction)
+ sc = -sc;
+ printk(ST_DEB_MSG "%s: Spacing tape %s over %d %s.\n", name,
+ direction ? "backward" : "forward", sc, units);
+}
+#endif
+
+
+/* Internal ioctl function */
+static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned long arg)
+{
+ int timeout;
+ long ltmp;
+ int ioctl_result;
+ int chg_eof = 1;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt;
+ struct st_partstat *STps;
+ int fileno, blkno, at_sm, undone;
+ int datalen = 0, direction = DMA_NONE;
+ char *name = tape_name(STp);
+
+ WARN_ON(STp->buffer->do_dio != 0);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
+ timeout = STp->long_timeout;
+ STps = &(STp->ps[STp->partition]);
+ fileno = STps->drv_file;
+ blkno = STps->drv_block;
+ at_sm = STps->at_sm;
+
+ memset(cmd, 0, MAX_COMMAND_SIZE);
+ switch (cmd_in) {
+ case MTFSFM:
+ chg_eof = 0; /* Changed from the FSF after this */
+ case MTFSF:
+ cmd[0] = SPACE;
+ cmd[1] = 0x01; /* Space FileMarks */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+ DEBC(deb_space_print(name, ST_DEB_FORWARD, "filemarks", cmd);)
+ if (fileno >= 0)
+ fileno += arg;
+ blkno = 0;
+ at_sm &= (arg == 0);
+ break;
+ case MTBSFM:
+ chg_eof = 0; /* Changed from the FSF after this */
+ case MTBSF:
+ cmd[0] = SPACE;
+ cmd[1] = 0x01; /* Space FileMarks */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+ DEBC(deb_space_print(name, ST_DEB_BACKWARD, "filemarks", cmd);)
+ if (fileno >= 0)
+ fileno -= arg;
+ blkno = (-1); /* We can't know the block number */
+ at_sm &= (arg == 0);
+ break;
+ case MTFSR:
+ cmd[0] = SPACE;
+ cmd[1] = 0x00; /* Space Blocks */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+ DEBC(deb_space_print(name, ST_DEB_FORWARD, "blocks", cmd);)
+ if (blkno >= 0)
+ blkno += arg;
+ at_sm &= (arg == 0);
+ break;
+ case MTBSR:
+ cmd[0] = SPACE;
+ cmd[1] = 0x00; /* Space Blocks */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+ DEBC(deb_space_print(name, ST_DEB_BACKWARD, "blocks", cmd);)
+ if (blkno >= 0)
+ blkno -= arg;
+ at_sm &= (arg == 0);
+ break;
+ case MTFSS:
+ cmd[0] = SPACE;
+ cmd[1] = 0x04; /* Space Setmarks */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+ DEBC(deb_space_print(name, ST_DEB_FORWARD, "setmarks", cmd);)
+ if (arg != 0) {
+ blkno = fileno = (-1);
+ at_sm = 1;
+ }
+ break;
+ case MTBSS:
+ cmd[0] = SPACE;
+ cmd[1] = 0x04; /* Space Setmarks */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+ DEBC(deb_space_print(name, ST_DEB_BACKWARD, "setmarks", cmd);)
+ if (arg != 0) {
+ blkno = fileno = (-1);
+ at_sm = 1;
+ }
+ break;
+ case MTWEOF:
+ case MTWSM:
+ if (STp->write_prot)
+ return (-EACCES);
+ cmd[0] = WRITE_FILEMARKS;
+ if (cmd_in == MTWSM)
+ cmd[1] = 2;
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+ timeout = STp->device->timeout;
+ DEBC(
+ if (cmd_in == MTWEOF)
+ printk(ST_DEB_MSG "%s: Writing %d filemarks.\n", name,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+ else
+ printk(ST_DEB_MSG "%s: Writing %d setmarks.\n", name,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+ )
+ if (fileno >= 0)
+ fileno += arg;
+ blkno = 0;
+ at_sm = (cmd_in == MTWSM);
+ break;
+ case MTREW:
+ cmd[0] = REZERO_UNIT;
+ if (STp->immediate) {
+ cmd[1] = 1; /* Don't wait for completion */
+ timeout = STp->device->timeout;
+ }
+ DEBC(printk(ST_DEB_MSG "%s: Rewinding tape.\n", name));
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTNOP:
+ DEBC(printk(ST_DEB_MSG "%s: No op on tape.\n", name));
+ return 0; /* Should do something ? */
+ break;
+ case MTRETEN:
+ cmd[0] = START_STOP;
+ if (STp->immediate) {
+ cmd[1] = 1; /* Don't wait for completion */
+ timeout = STp->device->timeout;
+ }
+ cmd[4] = 3;
+ DEBC(printk(ST_DEB_MSG "%s: Retensioning tape.\n", name));
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTEOM:
+ if (!STp->fast_mteom) {
+ /* space to the end of tape */
+ ioctl_result = st_int_ioctl(STp, MTFSF, 0x7fffff);
+ fileno = STps->drv_file;
+ if (STps->eof >= ST_EOD_1)
+ return 0;
+ /* The next lines would hide the number of spaced FileMarks
+ That's why I inserted the previous lines. I had no luck
+ with detecting EOM with FSF, so we go now to EOM.
+ Joerg Weule */
+ } else
+ fileno = (-1);
+ cmd[0] = SPACE;
+ cmd[1] = 3;
+ DEBC(printk(ST_DEB_MSG "%s: Spacing to end of recorded medium.\n",
+ name));
+ blkno = -1;
+ at_sm = 0;
+ break;
+ case MTERASE:
+ if (STp->write_prot)
+ return (-EACCES);
+ cmd[0] = ERASE;
+ cmd[1] = (arg ? 1 : 0); /* Long erase with non-zero argument */
+ if (STp->immediate) {
+ cmd[1] |= 2; /* Don't wait for completion */
+ timeout = STp->device->timeout;
+ }
+ else
+ timeout = STp->long_timeout * 8;
+
+ DEBC(printk(ST_DEB_MSG "%s: Erasing tape.\n", name));
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTSETBLK: /* Set block length */
+ case MTSETDENSITY: /* Set tape density */
+ case MTSETDRVBUFFER: /* Set drive buffering */
+ case SET_DENS_AND_BLK: /* Set density and block size */
+ chg_eof = 0;
+ if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
+ return (-EIO); /* Not allowed if data in buffer */
+ if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
+ (arg & MT_ST_BLKSIZE_MASK) != 0 &&
+ STp->max_block > 0 &&
+ ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
+ (arg & MT_ST_BLKSIZE_MASK) > STp->max_block)) {
+ printk(KERN_WARNING "%s: Illegal block size.\n", name);
+ return (-EINVAL);
+ }
+ cmd[0] = MODE_SELECT;
+ if ((STp->use_pf & USE_PF))
+ cmd[1] = MODE_SELECT_PAGE_FORMAT;
+ cmd[4] = datalen = 12;
+ direction = DMA_TO_DEVICE;
+
+ memset((STp->buffer)->b_data, 0, 12);
+ if (cmd_in == MTSETDRVBUFFER)
+ (STp->buffer)->b_data[2] = (arg & 7) << 4;
+ else
+ (STp->buffer)->b_data[2] =
+ STp->drv_buffer << 4;
+ (STp->buffer)->b_data[3] = 8; /* block descriptor length */
+ if (cmd_in == MTSETDENSITY) {
+ (STp->buffer)->b_data[4] = arg;
+ STp->density_changed = 1; /* At least we tried ;-) */
+ } else if (cmd_in == SET_DENS_AND_BLK)
+ (STp->buffer)->b_data[4] = arg >> 24;
+ else
+ (STp->buffer)->b_data[4] = STp->density;
+ if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
+ ltmp = arg & MT_ST_BLKSIZE_MASK;
+ if (cmd_in == MTSETBLK)
+ STp->blksize_changed = 1; /* At least we tried ;-) */
+ } else
+ ltmp = STp->block_size;
+ (STp->buffer)->b_data[9] = (ltmp >> 16);
+ (STp->buffer)->b_data[10] = (ltmp >> 8);
+ (STp->buffer)->b_data[11] = ltmp;
+ timeout = STp->device->timeout;
+ DEBC(
+ if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
+ printk(ST_DEB_MSG
+ "%s: Setting block size to %d bytes.\n", name,
+ (STp->buffer)->b_data[9] * 65536 +
+ (STp->buffer)->b_data[10] * 256 +
+ (STp->buffer)->b_data[11]);
+ if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK)
+ printk(ST_DEB_MSG
+ "%s: Setting density code to %x.\n", name,
+ (STp->buffer)->b_data[4]);
+ if (cmd_in == MTSETDRVBUFFER)
+ printk(ST_DEB_MSG
+ "%s: Setting drive buffer code to %d.\n", name,
+ ((STp->buffer)->b_data[2] >> 4) & 7);
+ )
+ break;
+ default:
+ return (-ENOSYS);
+ }
+
+ SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
+ timeout, MAX_RETRIES, 1);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
+
+ ioctl_result = (STp->buffer)->syscall_result;
+
+ if (!ioctl_result) { /* SCSI command successful */
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ STps->drv_block = blkno;
+ STps->drv_file = fileno;
+ STps->at_sm = at_sm;
+
+ if (cmd_in == MTBSFM)
+ ioctl_result = st_int_ioctl(STp, MTFSF, 1);
+ else if (cmd_in == MTFSFM)
+ ioctl_result = st_int_ioctl(STp, MTBSF, 1);
+
+ if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
+ int old_block_size = STp->block_size;
+ STp->block_size = arg & MT_ST_BLKSIZE_MASK;
+ if (STp->block_size != 0) {
+ if (old_block_size == 0)
+ normalize_buffer(STp->buffer);
+ (STp->buffer)->buffer_blocks =
+ (STp->buffer)->buffer_size / STp->block_size;
+ }
+ (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
+ if (cmd_in == SET_DENS_AND_BLK)
+ STp->density = arg >> MT_ST_DENSITY_SHIFT;
+ } else if (cmd_in == MTSETDRVBUFFER)
+ STp->drv_buffer = (arg & 7);
+ else if (cmd_in == MTSETDENSITY)
+ STp->density = arg;
+
+ if (cmd_in == MTEOM)
+ STps->eof = ST_EOD;
+ else if (cmd_in == MTFSF)
+ STps->eof = ST_FM;
+ else if (chg_eof)
+ STps->eof = ST_NOEOF;
+
+ if (cmd_in == MTWEOF)
+ STps->rw = ST_IDLE;
+ } else { /* SCSI command was not completely successful. Don't return
+ from this block without releasing the SCSI command block! */
+ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+
+ if (cmdstatp->flags & SENSE_EOM) {
+ if (cmd_in != MTBSF && cmd_in != MTBSFM &&
+ cmd_in != MTBSR && cmd_in != MTBSS)
+ STps->eof = ST_EOM_OK;
+ STps->drv_block = 0;
+ }
+
+ if (cmdstatp->remainder_valid)
+ undone = (int)cmdstatp->uremainder64;
+ else
+ undone = 0;
+
+ if (cmd_in == MTWEOF &&
+ cmdstatp->have_sense &&
+ (cmdstatp->flags & SENSE_EOM) &&
+ (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
+ cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
+ undone == 0) {
+ ioctl_result = 0; /* EOF written succesfully at EOM */
+ if (fileno >= 0)
+ fileno++;
+ STps->drv_file = fileno;
+ STps->eof = ST_NOEOF;
+ } else if ((cmd_in == MTFSF) || (cmd_in == MTFSFM)) {
+ if (fileno >= 0)
+ STps->drv_file = fileno - undone;
+ else
+ STps->drv_file = fileno;
+ STps->drv_block = -1;
+ STps->eof = ST_NOEOF;
+ } else if ((cmd_in == MTBSF) || (cmd_in == MTBSFM)) {
+ if (arg > 0 && undone < 0) /* Some drives get this wrong */
+ undone = (-undone);
+ if (STps->drv_file >= 0)
+ STps->drv_file = fileno + undone;
+ STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ } else if (cmd_in == MTFSR) {
+ if (cmdstatp->flags & SENSE_FMK) { /* Hit filemark */
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ } else {
+ if (blkno >= undone)
+ STps->drv_block = blkno - undone;
+ else
+ STps->drv_block = (-1);
+ STps->eof = ST_NOEOF;
+ }
+ } else if (cmd_in == MTBSR) {
+ if (cmdstatp->flags & SENSE_FMK) { /* Hit filemark */
+ STps->drv_file--;
+ STps->drv_block = (-1);
+ } else {
+ if (arg > 0 && undone < 0) /* Some drives get this wrong */
+ undone = (-undone);
+ if (STps->drv_block >= 0)
+ STps->drv_block = blkno + undone;
+ }
+ STps->eof = ST_NOEOF;
+ } else if (cmd_in == MTEOM) {
+ STps->drv_file = (-1);
+ STps->drv_block = (-1);
+ STps->eof = ST_EOD;
+ } else if (cmd_in == MTSETBLK ||
+ cmd_in == MTSETDENSITY ||
+ cmd_in == MTSETDRVBUFFER ||
+ cmd_in == SET_DENS_AND_BLK) {
+ if (cmdstatp->sense_hdr.sense_key == ILLEGAL_REQUEST &&
+ !(STp->use_pf & PF_TESTED)) {
+ /* Try the other possible state of Page Format if not
+ already tried */
+ STp->use_pf = !STp->use_pf | PF_TESTED;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ return st_int_ioctl(STp, cmd_in, arg);
+ }
+ } else if (chg_eof)
+ STps->eof = ST_NOEOF;
+
+ if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
+ STps->eof = ST_EOD;
+
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ }
+
+ return ioctl_result;
+}
+
+
+/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
+ structure. */
+
+static int get_location(struct scsi_tape *STp, unsigned int *block, int *partition,
+ int logical)
+{
+ int result;
+ unsigned char scmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt;
+ DEB( char *name = tape_name(STp); )
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ memset(scmd, 0, MAX_COMMAND_SIZE);
+ if ((STp->device)->scsi_level < SCSI_2) {
+ scmd[0] = QFA_REQUEST_BLOCK;
+ scmd[4] = 3;
+ } else {
+ scmd[0] = READ_POSITION;
+ if (!logical && !STp->scsi2_logical)
+ scmd[1] = 1;
+ }
+ SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE,
+ STp->device->timeout, MAX_READY_RETRIES, 1);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
+
+ if ((STp->buffer)->syscall_result != 0 ||
+ (STp->device->scsi_level >= SCSI_2 &&
+ ((STp->buffer)->b_data[0] & 4) != 0)) {
+ *block = *partition = 0;
+ DEBC(printk(ST_DEB_MSG "%s: Can't read tape position.\n", name));
+ result = (-EIO);
+ } else {
+ result = 0;
+ if ((STp->device)->scsi_level < SCSI_2) {
+ *block = ((STp->buffer)->b_data[0] << 16)
+ + ((STp->buffer)->b_data[1] << 8)
+ + (STp->buffer)->b_data[2];
+ *partition = 0;
+ } else {
+ *block = ((STp->buffer)->b_data[4] << 24)
+ + ((STp->buffer)->b_data[5] << 16)
+ + ((STp->buffer)->b_data[6] << 8)
+ + (STp->buffer)->b_data[7];
+ *partition = (STp->buffer)->b_data[1];
+ if (((STp->buffer)->b_data[0] & 0x80) &&
+ (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */
+ STp->ps[0].drv_block = STp->ps[0].drv_file = 0;
+ }
+ DEBC(printk(ST_DEB_MSG "%s: Got tape pos. blk %d part %d.\n", name,
+ *block, *partition));
+ }
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+
+ return result;
+}
+
+
+/* Set the tape block and partition. Negative partition means that only the
+ block should be set in vendor specific way. */
+static int set_location(struct scsi_tape *STp, unsigned int block, int partition,
+ int logical)
+{
+ struct st_partstat *STps;
+ int result, p;
+ unsigned int blk;
+ int timeout;
+ unsigned char scmd[MAX_COMMAND_SIZE];
+ struct scsi_request *SRpnt;
+ DEB( char *name = tape_name(STp); )
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+ timeout = STp->long_timeout;
+ STps = &(STp->ps[STp->partition]);
+
+ DEBC(printk(ST_DEB_MSG "%s: Setting block to %d and partition to %d.\n",
+ name, block, partition));
+ DEB(if (partition < 0)
+ return (-EIO); )
+
+ /* Update the location at the partition we are leaving */
+ if ((!STp->can_partitions && partition != 0) ||
+ partition >= ST_NBR_PARTITIONS)
+ return (-EINVAL);
+ if (partition != STp->partition) {
+ if (get_location(STp, &blk, &p, 1))
+ STps->last_block_valid = 0;
+ else {
+ STps->last_block_valid = 1;
+ STps->last_block_visited = blk;
+ DEBC(printk(ST_DEB_MSG
+ "%s: Visited block %d for partition %d saved.\n",
+ name, blk, STp->partition));
+ }
+ }
+
+ memset(scmd, 0, MAX_COMMAND_SIZE);
+ if ((STp->device)->scsi_level < SCSI_2) {
+ scmd[0] = QFA_SEEK_BLOCK;
+ scmd[2] = (block >> 16);
+ scmd[3] = (block >> 8);
+ scmd[4] = block;
+ scmd[5] = 0;
+ } else {
+ scmd[0] = SEEK_10;
+ scmd[3] = (block >> 24);
+ scmd[4] = (block >> 16);
+ scmd[5] = (block >> 8);
+ scmd[6] = block;
+ if (!logical && !STp->scsi2_logical)
+ scmd[1] = 4;
+ if (STp->partition != partition) {
+ scmd[1] |= 2;
+ scmd[8] = partition;
+ DEBC(printk(ST_DEB_MSG
+ "%s: Trying to change partition from %d to %d\n",
+ name, STp->partition, partition));
+ }
+ }
+ if (STp->immediate) {
+ scmd[1] |= 1; /* Don't wait for completion */
+ timeout = STp->device->timeout;
+ }
+
+ SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
+ timeout, MAX_READY_RETRIES, 1);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
+
+ STps->drv_block = STps->drv_file = (-1);
+ STps->eof = ST_NOEOF;
+ if ((STp->buffer)->syscall_result != 0) {
+ result = (-EIO);
+ if (STp->can_partitions &&
+ (STp->device)->scsi_level >= SCSI_2 &&
+ (p = find_partition(STp)) >= 0)
+ STp->partition = p;
+ } else {
+ if (STp->can_partitions) {
+ STp->partition = partition;
+ STps = &(STp->ps[partition]);
+ if (!STps->last_block_valid ||
+ STps->last_block_visited != block) {
+ STps->at_sm = 0;
+ STps->rw = ST_IDLE;
+ }
+ } else
+ STps->at_sm = 0;
+ if (block == 0)
+ STps->drv_block = STps->drv_file = 0;
+ result = 0;
+ }
+
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+
+ return result;
+}
+
+
+/* Find the current partition number for the drive status. Called from open and
+ returns either partition number of negative error code. */
+static int find_partition(struct scsi_tape *STp)
+{
+ int i, partition;
+ unsigned int block;
+
+ if ((i = get_location(STp, &block, &partition, 1)) < 0)
+ return i;
+ if (partition >= ST_NBR_PARTITIONS)
+ return (-EIO);
+ return partition;
+}
+
+
+/* Change the partition if necessary */
+static int switch_partition(struct scsi_tape *STp)
+{
+ struct st_partstat *STps;
+
+ if (STp->partition == STp->new_partition)
+ return 0;
+ STps = &(STp->ps[STp->new_partition]);
+ if (!STps->last_block_valid)
+ STps->last_block_visited = 0;
+ return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
+}
+
+/* Functions for reading and writing the medium partition mode page. */
+
+#define PART_PAGE 0x11
+#define PART_PAGE_FIXED_LENGTH 8
+
+#define PP_OFF_MAX_ADD_PARTS 2
+#define PP_OFF_NBR_ADD_PARTS 3
+#define PP_OFF_FLAGS 4
+#define PP_OFF_PART_UNITS 6
+#define PP_OFF_RESERVED 7
+
+#define PP_BIT_IDP 0x20
+#define PP_MSK_PSUM_MB 0x10
+
+/* Get the number of partitions on the tape. As a side effect reads the
+ mode page into the tape buffer. */
+static int nbr_partitions(struct scsi_tape *STp)
+{
+ int result;
+ DEB( char *name = tape_name(STp); )
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ result = read_mode_page(STp, PART_PAGE, 1);
+
+ if (result) {
+ DEBC(printk(ST_DEB_MSG "%s: Can't read medium partition page.\n",
+ name));
+ result = (-EIO);
+ } else {
+ result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
+ PP_OFF_NBR_ADD_PARTS] + 1;
+ DEBC(printk(ST_DEB_MSG "%s: Number of partitions %d.\n", name, result));
+ }
+
+ return result;
+}
+
+
+/* Partition the tape into two partitions if size > 0 or one partition if
+ size == 0.
+
+ The block descriptors are read and written because Sony SDT-7000 does not
+ work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
+
+ My HP C1533A drive returns only one partition size field. This is used to
+ set the size of partition 1. There is no size field for the default partition.
+ Michael Schaefer's Sony SDT-7000 returns two descriptors and the second is
+ used to set the size of partition 1 (this is what the SCSI-3 standard specifies).
+ The following algorithm is used to accommodate both drives: if the number of
+ partition size fields is greater than the maximum number of additional partitions
+ in the mode page, the second field is used. Otherwise the first field is used.
+
+ For Seagate DDS drives the page length must be 8 when no partitions is defined
+ and 10 when 1 partition is defined (information from Eric Lee Green). This is
+ is acceptable also to some other old drives and enforced if the first partition
+ size field is used for the first additional partition size.
+ */
+static int partition_tape(struct scsi_tape *STp, int size)
+{
+ char *name = tape_name(STp);
+ int result;
+ int pgo, psd_cnt, psdo;
+ unsigned char *bp;
+
+ result = read_mode_page(STp, PART_PAGE, 0);
+ if (result) {
+ DEBC(printk(ST_DEB_MSG "%s: Can't read partition mode page.\n", name));
+ return result;
+ }
+ /* The mode page is in the buffer. Let's modify it and write it. */
+ bp = (STp->buffer)->b_data;
+ pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
+ DEBC(printk(ST_DEB_MSG "%s: Partition page length is %d bytes.\n",
+ name, bp[pgo + MP_OFF_PAGE_LENGTH] + 2));
+
+ psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
+ psdo = pgo + PART_PAGE_FIXED_LENGTH;
+ if (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS]) {
+ bp[psdo] = bp[psdo + 1] = 0xff; /* Rest of the tape */
+ psdo += 2;
+ }
+ memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
+
+ DEBC(printk("%s: psd_cnt %d, max.parts %d, nbr_parts %d\n", name,
+ psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
+ bp[pgo + PP_OFF_NBR_ADD_PARTS]));
+
+ if (size <= 0) {
+ bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
+ if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
+ bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
+ DEBC(printk(ST_DEB_MSG "%s: Formatting tape with one partition.\n",
+ name));
+ } else {
+ bp[psdo] = (size >> 8) & 0xff;
+ bp[psdo + 1] = size & 0xff;
+ bp[pgo + 3] = 1;
+ if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8)
+ bp[pgo + MP_OFF_PAGE_LENGTH] = 8;
+ DEBC(printk(ST_DEB_MSG
+ "%s: Formatting tape with two partitions (1 = %d MB).\n",
+ name, size));
+ }
+ bp[pgo + PP_OFF_PART_UNITS] = 0;
+ bp[pgo + PP_OFF_RESERVED] = 0;
+ bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB;
+
+ result = write_mode_page(STp, PART_PAGE, 1);
+ if (result) {
+ printk(KERN_INFO "%s: Partitioning of tape failed.\n", name);
+ result = (-EIO);
+ }
+
+ return result;
+}
+
+
+
+/* The ioctl command */
+static int st_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd_in, unsigned long arg)
+{
+ int i, cmd_nr, cmd_type, bt;
+ int retval = 0;
+ unsigned int blk;
+ struct scsi_tape *STp = file->private_data;
+ struct st_modedef *STm;
+ struct st_partstat *STps;
+ char *name = tape_name(STp);
+ void __user *p = (void __user *)arg;
+
+ if (down_interruptible(&STp->lock))
+ return -ERESTARTSYS;
+
+ DEB(
+ if (debugging && !STp->in_use) {
+ printk(ST_DEB_MSG "%s: Incorrect device.\n", name);
+ retval = (-EIO);
+ goto out;
+ } ) /* end DEB */
+
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ retval = scsi_nonblockable_ioctl(STp->device, cmd_in, p, file);
+ if (!scsi_block_when_processing_errors(STp->device) || retval != -ENODEV)
+ goto out;
+ retval = 0;
+
+ cmd_type = _IOC_TYPE(cmd_in);
+ cmd_nr = _IOC_NR(cmd_in);
+
+ if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+ struct mtop mtc;
+
+ if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ i = copy_from_user(&mtc, p, sizeof(struct mtop));
+ if (i) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
+ printk(KERN_WARNING
+ "%s: MTSETDRVBUFFER only allowed for root.\n", name);
+ retval = (-EPERM);
+ goto out;
+ }
+ if (!STm->defined &&
+ (mtc.mt_op != MTSETDRVBUFFER &&
+ (mtc.mt_count & MT_ST_OPTIONS) == 0)) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if (!STp->pos_unknown) {
+
+ if (STps->eof == ST_FM_HIT) {
+ if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
+ mtc.mt_op == MTEOM) {
+ mtc.mt_count -= 1;
+ if (STps->drv_file >= 0)
+ STps->drv_file += 1;
+ } else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
+ mtc.mt_count += 1;
+ if (STps->drv_file >= 0)
+ STps->drv_file += 1;
+ }
+ }
+
+ if (mtc.mt_op == MTSEEK) {
+ /* Old position must be restored if partition will be
+ changed */
+ i = !STp->can_partitions ||
+ (STp->new_partition != STp->partition);
+ } else {
+ i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+ mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
+ mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+ mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
+ mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
+ mtc.mt_op == MTCOMPRESSION;
+ }
+ i = flush_buffer(STp, i);
+ if (i < 0) {
+ retval = i;
+ goto out;
+ }
+ if (STps->rw == ST_WRITING &&
+ (mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+ mtc.mt_op == MTSEEK ||
+ mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)) {
+ i = st_int_ioctl(STp, MTWEOF, 1);
+ if (i < 0) {
+ retval = i;
+ goto out;
+ }
+ if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)
+ mtc.mt_count++;
+ STps->rw = ST_IDLE;
+ }
+
+ } else {
+ /*
+ * If there was a bus reset, block further access
+ * to this device. If the user wants to rewind the tape,
+ * then reset the flag and allow access again.
+ */
+ if (mtc.mt_op != MTREW &&
+ mtc.mt_op != MTOFFL &&
+ mtc.mt_op != MTRETEN &&
+ mtc.mt_op != MTERASE &&
+ mtc.mt_op != MTSEEK &&
+ mtc.mt_op != MTEOM) {
+ retval = (-EIO);
+ goto out;
+ }
+ reset_state(STp);
+ /* remove this when the midlevel properly clears was_reset */
+ STp->device->was_reset = 0;
+ }
+
+ if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+ mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+ mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
+ STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
+
+ if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
+ do_door_lock(STp, 0); /* Ignore result! */
+
+ if (mtc.mt_op == MTSETDRVBUFFER &&
+ (mtc.mt_count & MT_ST_OPTIONS) != 0) {
+ retval = st_set_options(STp, mtc.mt_count);
+ goto out;
+ }
+
+ if (mtc.mt_op == MTSETPART) {
+ if (!STp->can_partitions ||
+ mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ if (mtc.mt_count >= STp->nbr_partitions &&
+ (STp->nbr_partitions = nbr_partitions(STp)) < 0) {
+ retval = (-EIO);
+ goto out;
+ }
+ if (mtc.mt_count >= STp->nbr_partitions) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ STp->new_partition = mtc.mt_count;
+ retval = 0;
+ goto out;
+ }
+
+ if (mtc.mt_op == MTMKPART) {
+ if (!STp->can_partitions) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 ||
+ (i = partition_tape(STp, mtc.mt_count)) < 0) {
+ retval = i;
+ goto out;
+ }
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STp->ps[i].rw = ST_IDLE;
+ STp->ps[i].at_sm = 0;
+ STp->ps[i].last_block_valid = 0;
+ }
+ STp->partition = STp->new_partition = 0;
+ STp->nbr_partitions = 1; /* Bad guess ?-) */
+ STps->drv_block = STps->drv_file = 0;
+ retval = 0;
+ goto out;
+ }
+
+ if (mtc.mt_op == MTSEEK) {
+ i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
+ if (!STp->can_partitions)
+ STp->ps[0].rw = ST_IDLE;
+ retval = i;
+ goto out;
+ }
+
+ if (mtc.mt_op == MTUNLOAD || mtc.mt_op == MTOFFL) {
+ retval = do_load_unload(STp, file, 0);
+ goto out;
+ }
+
+ if (mtc.mt_op == MTLOAD) {
+ retval = do_load_unload(STp, file, max(1, mtc.mt_count));
+ goto out;
+ }
+
+ if (mtc.mt_op == MTLOCK || mtc.mt_op == MTUNLOCK) {
+ retval = do_door_lock(STp, (mtc.mt_op == MTLOCK));
+ goto out;
+ }
+
+ if (STp->can_partitions && STp->ready == ST_READY &&
+ (i = switch_partition(STp)) < 0) {
+ retval = i;
+ goto out;
+ }
+
+ if (mtc.mt_op == MTCOMPRESSION)
+ retval = st_compression(STp, (mtc.mt_count & 1));
+ else
+ retval = st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
+ goto out;
+ }
+ if (!STm->defined) {
+ retval = (-ENXIO);
+ goto out;
+ }
+
+ if ((i = flush_buffer(STp, 0)) < 0) {
+ retval = i;
+ goto out;
+ }
+ if (STp->can_partitions &&
+ (i = switch_partition(STp)) < 0) {
+ retval = i;
+ goto out;
+ }
+
+ if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+ struct mtget mt_status;
+
+ if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+
+ mt_status.mt_type = STp->tape_type;
+ mt_status.mt_dsreg =
+ ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+ ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+ mt_status.mt_blkno = STps->drv_block;
+ mt_status.mt_fileno = STps->drv_file;
+ if (STp->block_size != 0) {
+ if (STps->rw == ST_WRITING)
+ mt_status.mt_blkno +=
+ (STp->buffer)->buffer_bytes / STp->block_size;
+ else if (STps->rw == ST_READING)
+ mt_status.mt_blkno -=
+ ((STp->buffer)->buffer_bytes +
+ STp->block_size - 1) / STp->block_size;
+ }
+
+ mt_status.mt_gstat = 0;
+ if (STp->drv_write_prot)
+ mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+ if (mt_status.mt_blkno == 0) {
+ if (mt_status.mt_fileno == 0)
+ mt_status.mt_gstat |= GMT_BOT(0xffffffff);
+ else
+ mt_status.mt_gstat |= GMT_EOF(0xffffffff);
+ }
+ mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
+ mt_status.mt_resid = STp->partition;
+ if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
+ mt_status.mt_gstat |= GMT_EOT(0xffffffff);
+ else if (STps->eof >= ST_EOM_OK)
+ mt_status.mt_gstat |= GMT_EOD(0xffffffff);
+ if (STp->density == 1)
+ mt_status.mt_gstat |= GMT_D_800(0xffffffff);
+ else if (STp->density == 2)
+ mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
+ else if (STp->density == 3)
+ mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
+ if (STp->ready == ST_READY)
+ mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
+ if (STp->ready == ST_NO_TAPE)
+ mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
+ if (STps->at_sm)
+ mt_status.mt_gstat |= GMT_SM(0xffffffff);
+ if (STm->do_async_writes ||
+ (STm->do_buffer_writes && STp->block_size != 0) ||
+ STp->drv_buffer != 0)
+ mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+ if (STp->cleaning_req)
+ mt_status.mt_gstat |= GMT_CLN(0xffffffff);
+
+ i = copy_to_user(p, &mt_status, sizeof(struct mtget));
+ if (i) {
+ retval = (-EFAULT);
+ goto out;
+ }
+
+ STp->recover_reg = 0; /* Clear after read */
+ retval = 0;
+ goto out;
+ } /* End of MTIOCGET */
+ if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+ struct mtpos mt_pos;
+ if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
+ retval = (-EINVAL);
+ goto out;
+ }
+ if ((i = get_location(STp, &blk, &bt, 0)) < 0) {
+ retval = i;
+ goto out;
+ }
+ mt_pos.mt_blkno = blk;
+ i = copy_to_user(p, &mt_pos, sizeof(struct mtpos));
+ if (i)
+ retval = (-EFAULT);
+ goto out;
+ }
+ up(&STp->lock);
+ switch (cmd_in) {
+ case SCSI_IOCTL_GET_IDLUN:
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ break;
+ default:
+ if (!capable(CAP_SYS_ADMIN))
+ i = -EPERM;
+ else
+ i = scsi_cmd_ioctl(file, STp->disk, cmd_in, p);
+ if (i != -ENOTTY)
+ return i;
+ break;
+ }
+ if (!capable(CAP_SYS_ADMIN) &&
+ (cmd_in == SCSI_IOCTL_START_UNIT || cmd_in == SCSI_IOCTL_STOP_UNIT))
+ return -EPERM;
+ return scsi_ioctl(STp->device, cmd_in, p);
+
+ out:
+ up(&STp->lock);
+ return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct scsi_tape *STp = file->private_data;
+ struct scsi_device *sdev = STp->device;
+ int ret = -ENOIOCTLCMD;
+ if (sdev->host->hostt->compat_ioctl) {
+
+ ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg);
+
+ }
+ return ret;
+}
+#endif
+
+
+
+/* Try to allocate a new tape buffer. Calling function must not hold
+ dev_arr_lock. */
+static struct st_buffer *
+ new_tape_buffer(int from_initialization, int need_dma, int max_sg)
+{
+ int i, priority, got = 0, segs = 0;
+ struct st_buffer *tb;
+
+ if (from_initialization)
+ priority = GFP_ATOMIC;
+ else
+ priority = GFP_KERNEL;
+
+ i = sizeof(struct st_buffer) + (max_sg - 1) * sizeof(struct scatterlist) +
+ max_sg * sizeof(struct st_buf_fragment);
+ tb = kmalloc(i, priority);
+ if (!tb) {
+ printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n");
+ return NULL;
+ }
+ memset(tb, 0, i);
+ tb->frp_segs = tb->orig_frp_segs = segs;
+ tb->use_sg = max_sg;
+ if (segs > 0)
+ tb->b_data = page_address(tb->sg[0].page);
+ tb->frp = (struct st_buf_fragment *)(&(tb->sg[0]) + max_sg);
+
+ tb->in_use = 1;
+ tb->dma = need_dma;
+ tb->buffer_size = got;
+
+ return tb;
+}
+
+
+/* Try to allocate enough space in the tape buffer */
+static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dma)
+{
+ int segs, nbr, max_segs, b_size, priority, order, got;
+
+ if (new_size <= STbuffer->buffer_size)
+ return 1;
+
+ if (STbuffer->buffer_size <= PAGE_SIZE)
+ normalize_buffer(STbuffer); /* Avoid extra segment */
+
+ max_segs = STbuffer->use_sg;
+ nbr = max_segs - STbuffer->frp_segs;
+ if (nbr <= 0)
+ return 0;
+
+ priority = GFP_KERNEL | __GFP_NOWARN;
+ if (need_dma)
+ priority |= GFP_DMA;
+ for (b_size = PAGE_SIZE, order=0;
+ b_size < new_size - STbuffer->buffer_size;
+ order++, b_size *= 2)
+ ; /* empty */
+
+ for (segs = STbuffer->frp_segs, got = STbuffer->buffer_size;
+ segs < max_segs && got < new_size;) {
+ STbuffer->frp[segs].page = alloc_pages(priority, order);
+ if (STbuffer->frp[segs].page == NULL) {
+ if (new_size - got <= (max_segs - segs) * b_size / 2) {
+ b_size /= 2; /* Large enough for the rest of the buffers */
+ order--;
+ continue;
+ }
+ DEB(STbuffer->buffer_size = got);
+ normalize_buffer(STbuffer);
+ return 0;
+ }
+ STbuffer->frp[segs].length = b_size;
+ STbuffer->frp_segs += 1;
+ got += b_size;
+ STbuffer->buffer_size = got;
+ segs++;
+ }
+ STbuffer->b_data = page_address(STbuffer->frp[0].page);
+
+ return 1;
+}
+
+
+/* Release the extra buffer */
+static void normalize_buffer(struct st_buffer * STbuffer)
+{
+ int i, order;
+
+ for (i = STbuffer->orig_frp_segs; i < STbuffer->frp_segs; i++) {
+ order = get_order(STbuffer->frp[i].length);
+ __free_pages(STbuffer->frp[i].page, order);
+ STbuffer->buffer_size -= STbuffer->frp[i].length;
+ }
+ STbuffer->frp_segs = STbuffer->orig_frp_segs;
+ STbuffer->frp_sg_current = 0;
+}
+
+
+/* Move data from the user buffer to the tape buffer. Returns zero (success) or
+ negative error code. */
+static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count)
+{
+ int i, cnt, res, offset;
+
+ for (i = 0, offset = st_bp->buffer_bytes;
+ i < st_bp->frp_segs && offset >= st_bp->frp[i].length; i++)
+ offset -= st_bp->frp[i].length;
+ if (i == st_bp->frp_segs) { /* Should never happen */
+ printk(KERN_WARNING "st: append_to_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for (; i < st_bp->frp_segs && do_count > 0; i++) {
+ cnt = st_bp->frp[i].length - offset < do_count ?
+ st_bp->frp[i].length - offset : do_count;
+ res = copy_from_user(page_address(st_bp->frp[i].page) + offset, ubp, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) /* Should never happen */
+ return (-EIO);
+
+ return 0;
+}
+
+
+/* Move data from the tape buffer to the user buffer. Returns zero (success) or
+ negative error code. */
+static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count)
+{
+ int i, cnt, res, offset;
+
+ for (i = 0, offset = st_bp->read_pointer;
+ i < st_bp->frp_segs && offset >= st_bp->frp[i].length; i++)
+ offset -= st_bp->frp[i].length;
+ if (i == st_bp->frp_segs) { /* Should never happen */
+ printk(KERN_WARNING "st: from_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for (; i < st_bp->frp_segs && do_count > 0; i++) {
+ cnt = st_bp->frp[i].length - offset < do_count ?
+ st_bp->frp[i].length - offset : do_count;
+ res = copy_to_user(ubp, page_address(st_bp->frp[i].page) + offset, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes -= cnt;
+ st_bp->read_pointer += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) /* Should never happen */
+ return (-EIO);
+
+ return 0;
+}
+
+
+/* Move data towards start of buffer */
+static void move_buffer_data(struct st_buffer * st_bp, int offset)
+{
+ int src_seg, dst_seg, src_offset = 0, dst_offset;
+ int count, total;
+
+ if (offset == 0)
+ return;
+
+ total=st_bp->buffer_bytes - offset;
+ for (src_seg=0; src_seg < st_bp->frp_segs; src_seg++) {
+ src_offset = offset;
+ if (src_offset < st_bp->frp[src_seg].length)
+ break;
+ offset -= st_bp->frp[src_seg].length;
+ }
+
+ st_bp->buffer_bytes = st_bp->read_pointer = total;
+ for (dst_seg=dst_offset=0; total > 0; ) {
+ count = min(st_bp->frp[dst_seg].length - dst_offset,
+ st_bp->frp[src_seg].length - src_offset);
+ memmove(page_address(st_bp->frp[dst_seg].page) + dst_offset,
+ page_address(st_bp->frp[src_seg].page) + src_offset, count);
+ src_offset += count;
+ if (src_offset >= st_bp->frp[src_seg].length) {
+ src_seg++;
+ src_offset = 0;
+ }
+ dst_offset += count;
+ if (dst_offset >= st_bp->frp[dst_seg].length) {
+ dst_seg++;
+ dst_offset = 0;
+ }
+ total -= count;
+ }
+}
+
+
+/* Fill the s/g list up to the length required for this transfer */
+static void buf_to_sg(struct st_buffer *STbp, unsigned int length)
+{
+ int i;
+ unsigned int count;
+ struct scatterlist *sg;
+ struct st_buf_fragment *frp;
+
+ if (length == STbp->frp_sg_current)
+ return; /* work already done */
+
+ sg = &(STbp->sg[0]);
+ frp = STbp->frp;
+ for (i=count=0; count < length; i++) {
+ sg[i].page = frp[i].page;
+ if (length - count > frp[i].length)
+ sg[i].length = frp[i].length;
+ else
+ sg[i].length = length - count;
+ count += sg[i].length;
+ sg[i].offset = 0;
+ }
+ STbp->sg_segs = i;
+ STbp->frp_sg_current = length;
+}
+
+
+/* Validate the options from command line or module parameters */
+static void validate_options(void)
+{
+ if (buffer_kbs > 0)
+ st_fixed_buffer_size = buffer_kbs * ST_KILOBYTE;
+ if (max_sg_segs >= ST_FIRST_SG)
+ st_max_sg_segs = max_sg_segs;
+}
+
+#ifndef MODULE
+/* Set the boot options. Syntax is defined in Documenation/scsi/st.txt.
+ */
+static int __init st_setup(char *str)
+{
+ int i, len, ints[5];
+ char *stp;
+
+ stp = get_options(str, ARRAY_SIZE(ints), ints);
+
+ if (ints[0] > 0) {
+ for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++)
+ if (parms[i].val)
+ *parms[i].val = ints[i + 1];
+ } else {
+ while (stp != NULL) {
+ for (i = 0; i < ARRAY_SIZE(parms); i++) {
+ len = strlen(parms[i].name);
+ if (!strncmp(stp, parms[i].name, len) &&
+ (*(stp + len) == ':' || *(stp + len) == '=')) {
+ if (parms[i].val)
+ *parms[i].val =
+ simple_strtoul(stp + len + 1, NULL, 0);
+ else
+ printk(KERN_WARNING "st: Obsolete parameter %s\n",
+ parms[i].name);
+ break;
+ }
+ }
+ if (i >= sizeof(parms) / sizeof(struct st_dev_parm))
+ printk(KERN_WARNING "st: invalid parameter in '%s'\n",
+ stp);
+ stp = strchr(stp, ',');
+ if (stp)
+ stp++;
+ }
+ }
+
+ validate_options();
+
+ return 1;
+}
+
+__setup("st=", st_setup);
+
+#endif
+
+static struct file_operations st_fops =
+{
+ .owner = THIS_MODULE,
+ .read = st_read,
+ .write = st_write,
+ .ioctl = st_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = st_compat_ioctl,
+#endif
+ .open = st_open,
+ .flush = st_flush,
+ .release = st_release,
+};
+
+static int st_probe(struct device *dev)
+{
+ struct scsi_device *SDp = to_scsi_device(dev);
+ struct gendisk *disk = NULL;
+ struct cdev *cdev = NULL;
+ struct scsi_tape *tpnt = NULL;
+ struct st_modedef *STm;
+ struct st_partstat *STps;
+ struct st_buffer *buffer;
+ int i, j, mode, dev_num, error;
+ char *stp;
+ u64 bounce_limit;
+
+ if (SDp->type != TYPE_TAPE)
+ return -ENODEV;
+ if ((stp = st_incompatible(SDp))) {
+ printk(KERN_INFO
+ "st: Found incompatible tape at scsi%d, channel %d, id %d, lun %d\n",
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_INFO "st: The suggested driver is %s.\n", stp);
+ return -ENODEV;
+ }
+
+ i = SDp->host->sg_tablesize;
+ if (st_max_sg_segs < i)
+ i = st_max_sg_segs;
+ buffer = new_tape_buffer(1, (SDp->host)->unchecked_isa_dma, i);
+ if (buffer == NULL) {
+ printk(KERN_ERR
+ "st: Can't allocate new tape buffer. Device not attached.\n");
+ goto out;
+ }
+
+ disk = alloc_disk(1);
+ if (!disk) {
+ printk(KERN_ERR "st: out of memory. Device not attached.\n");
+ goto out_buffer_free;
+ }
+
+ write_lock(&st_dev_arr_lock);
+ if (st_nr_dev >= st_dev_max) {
+ struct scsi_tape **tmp_da;
+ int tmp_dev_max;
+
+ tmp_dev_max = max(st_nr_dev * 2, 8);
+ if (tmp_dev_max > ST_MAX_TAPES)
+ tmp_dev_max = ST_MAX_TAPES;
+ if (tmp_dev_max <= st_nr_dev) {
+ write_unlock(&st_dev_arr_lock);
+ printk(KERN_ERR "st: Too many tape devices (max. %d).\n",
+ ST_MAX_TAPES);
+ goto out_put_disk;
+ }
+
+ tmp_da = kmalloc(tmp_dev_max * sizeof(struct scsi_tape *), GFP_ATOMIC);
+ if (tmp_da == NULL) {
+ write_unlock(&st_dev_arr_lock);
+ printk(KERN_ERR "st: Can't extend device array.\n");
+ goto out_put_disk;
+ }
+
+ memset(tmp_da, 0, tmp_dev_max * sizeof(struct scsi_tape *));
+ if (scsi_tapes != NULL) {
+ memcpy(tmp_da, scsi_tapes,
+ st_dev_max * sizeof(struct scsi_tape *));
+ kfree(scsi_tapes);
+ }
+ scsi_tapes = tmp_da;
+
+ st_dev_max = tmp_dev_max;
+ }
+
+ for (i = 0; i < st_dev_max; i++)
+ if (scsi_tapes[i] == NULL)
+ break;
+ if (i >= st_dev_max)
+ panic("scsi_devices corrupt (st)");
+
+ tpnt = kmalloc(sizeof(struct scsi_tape), GFP_ATOMIC);
+ if (tpnt == NULL) {
+ write_unlock(&st_dev_arr_lock);
+ printk(KERN_ERR "st: Can't allocate device descriptor.\n");
+ goto out_put_disk;
+ }
+ memset(tpnt, 0, sizeof(struct scsi_tape));
+ tpnt->disk = disk;
+ sprintf(disk->disk_name, "st%d", i);
+ disk->private_data = &tpnt->driver;
+ disk->queue = SDp->request_queue;
+ tpnt->driver = &st_template;
+ scsi_tapes[i] = tpnt;
+ dev_num = i;
+
+ tpnt->device = SDp;
+ if (SDp->scsi_level <= 2)
+ tpnt->tape_type = MT_ISSCSI1;
+ else
+ tpnt->tape_type = MT_ISSCSI2;
+
+ tpnt->buffer = buffer;
+
+ tpnt->inited = 0;
+ tpnt->dirty = 0;
+ tpnt->in_use = 0;
+ tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
+ tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
+ tpnt->use_pf = (SDp->scsi_level >= SCSI_2);
+ tpnt->density = 0;
+ tpnt->do_auto_lock = ST_AUTO_LOCK;
+ tpnt->can_bsr = (SDp->scsi_level > 2 ? 1 : ST_IN_FILE_POS); /* BSR mandatory in SCSI3 */
+ tpnt->can_partitions = 0;
+ tpnt->two_fm = ST_TWO_FM;
+ tpnt->fast_mteom = ST_FAST_MTEOM;
+ tpnt->scsi2_logical = ST_SCSI2LOGICAL;
+ tpnt->immediate = ST_NOWAIT;
+ tpnt->default_drvbuffer = 0xff; /* No forced buffering */
+ tpnt->partition = 0;
+ tpnt->new_partition = 0;
+ tpnt->nbr_partitions = 0;
+ tpnt->device->timeout = ST_TIMEOUT;
+ tpnt->long_timeout = ST_LONG_TIMEOUT;
+ tpnt->try_dio = try_direct_io && !SDp->host->unchecked_isa_dma;
+
+ bounce_limit = scsi_calculate_bounce_limit(SDp->host) >> PAGE_SHIFT;
+ if (bounce_limit > ULONG_MAX)
+ bounce_limit = ULONG_MAX;
+ tpnt->max_pfn = bounce_limit;
+
+ for (i = 0; i < ST_NBR_MODES; i++) {
+ STm = &(tpnt->modes[i]);
+ STm->defined = 0;
+ STm->sysv = ST_SYSV;
+ STm->defaults_for_writes = 0;
+ STm->do_async_writes = ST_ASYNC_WRITES;
+ STm->do_buffer_writes = ST_BUFFER_WRITES;
+ STm->do_read_ahead = ST_READ_AHEAD;
+ STm->default_compression = ST_DONT_TOUCH;
+ STm->default_blksize = (-1); /* No forced size */
+ STm->default_density = (-1); /* No forced density */
+ }
+
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(tpnt->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = 0;
+ STps->drv_block = (-1);
+ STps->drv_file = (-1);
+ }
+
+ tpnt->current_mode = 0;
+ tpnt->modes[0].defined = 1;
+
+ tpnt->density_changed = tpnt->compression_changed =
+ tpnt->blksize_changed = 0;
+ init_MUTEX(&tpnt->lock);
+
+ st_nr_dev++;
+ write_unlock(&st_dev_arr_lock);
+
+ for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+ STm = &(tpnt->modes[mode]);
+ for (j=0; j < 2; j++) {
+ cdev = cdev_alloc();
+ if (!cdev) {
+ printk(KERN_ERR
+ "st%d: out of memory. Device not attached.\n",
+ dev_num);
+ goto out_free_tape;
+ }
+ cdev->owner = THIS_MODULE;
+ cdev->ops = &st_fops;
+
+ error = cdev_add(cdev,
+ MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, j)),
+ 1);
+ if (error) {
+ printk(KERN_ERR "st%d: Can't add %s-rewind mode %d\n",
+ dev_num, j ? "non" : "auto", mode);
+ printk(KERN_ERR "st%d: Device not attached.\n", dev_num);
+ goto out_free_tape;
+ }
+ STm->cdevs[j] = cdev;
+
+ }
+ do_create_class_files(tpnt, dev_num, mode);
+ }
+
+ for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+ /* Make sure that the minor numbers corresponding to the four
+ first modes always get the same names */
+ i = mode << (4 - ST_NBR_MODE_BITS);
+ /* Rewind entry */
+ devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, 0)),
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ "%s/mt%s", SDp->devfs_name, st_formats[i]);
+ /* No-rewind entry */
+ devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, 1)),
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ "%s/mt%sn", SDp->devfs_name, st_formats[i]);
+ }
+ disk->number = devfs_register_tape(SDp->devfs_name);
+
+ printk(KERN_WARNING
+ "Attached scsi tape %s at scsi%d, channel %d, id %d, lun %d\n",
+ tape_name(tpnt), SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_WARNING "%s: try direct i/o: %s (alignment %d B), max page reachable by HBA %lu\n",
+ tape_name(tpnt), tpnt->try_dio ? "yes" : "no",
+ queue_dma_alignment(SDp->request_queue) + 1, tpnt->max_pfn);
+
+ return 0;
+
+out_free_tape:
+ for (mode=0; mode < ST_NBR_MODES; mode++) {
+ STm = &(tpnt->modes[mode]);
+ sysfs_remove_link(&tpnt->device->sdev_gendev.kobj,
+ "tape");
+ for (j=0; j < 2; j++) {
+ if (STm->cdevs[j]) {
+ if (cdev == STm->cdevs[j])
+ cdev = NULL;
+ class_simple_device_remove(MKDEV(SCSI_TAPE_MAJOR,
+ TAPE_MINOR(i, mode, j)));
+ cdev_del(STm->cdevs[j]);
+ }
+ }
+ }
+ if (cdev)
+ cdev_del(cdev);
+ write_lock(&st_dev_arr_lock);
+ scsi_tapes[dev_num] = NULL;
+ st_nr_dev--;
+ write_unlock(&st_dev_arr_lock);
+out_put_disk:
+ put_disk(disk);
+ if (tpnt)
+ kfree(tpnt);
+out_buffer_free:
+ kfree(buffer);
+out:
+ return -ENODEV;
+};
+
+
+static int st_remove(struct device *dev)
+{
+ struct scsi_device *SDp = to_scsi_device(dev);
+ struct scsi_tape *tpnt;
+ int i, j, mode;
+
+ write_lock(&st_dev_arr_lock);
+ for (i = 0; i < st_dev_max; i++) {
+ tpnt = scsi_tapes[i];
+ if (tpnt != NULL && tpnt->device == SDp) {
+ scsi_tapes[i] = NULL;
+ st_nr_dev--;
+ write_unlock(&st_dev_arr_lock);
+ devfs_unregister_tape(tpnt->disk->number);
+ sysfs_remove_link(&tpnt->device->sdev_gendev.kobj,
+ "tape");
+ for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+ j = mode << (4 - ST_NBR_MODE_BITS);
+ devfs_remove("%s/mt%s", SDp->devfs_name, st_formats[j]);
+ devfs_remove("%s/mt%sn", SDp->devfs_name, st_formats[j]);
+ for (j=0; j < 2; j++) {
+ class_simple_device_remove(MKDEV(SCSI_TAPE_MAJOR,
+ TAPE_MINOR(i, mode, j)));
+ cdev_del(tpnt->modes[mode].cdevs[j]);
+ tpnt->modes[mode].cdevs[j] = NULL;
+ }
+ }
+ tpnt->device = NULL;
+
+ if (tpnt->buffer) {
+ tpnt->buffer->orig_frp_segs = 0;
+ normalize_buffer(tpnt->buffer);
+ kfree(tpnt->buffer);
+ }
+ put_disk(tpnt->disk);
+ kfree(tpnt);
+ return 0;
+ }
+ }
+
+ write_unlock(&st_dev_arr_lock);
+ return 0;
+}
+
+static void st_intr(struct scsi_cmnd *SCpnt)
+{
+ scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen), 1);
+}
+
+/*
+ * st_init_command: only called via the scsi_cmd_ioctl (block SG_IO)
+ * interface for REQ_BLOCK_PC commands.
+ */
+static int st_init_command(struct scsi_cmnd *SCpnt)
+{
+ struct request *rq;
+
+ if (!(SCpnt->request->flags & REQ_BLOCK_PC))
+ return 0;
+
+ rq = SCpnt->request;
+ if (sizeof(rq->cmd) > sizeof(SCpnt->cmnd))
+ return 0;
+
+ memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd));
+
+ if (rq_data_dir(rq) == WRITE)
+ SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ else if (rq->data_len)
+ SCpnt->sc_data_direction = DMA_FROM_DEVICE;
+ else
+ SCpnt->sc_data_direction = DMA_NONE;
+
+ SCpnt->timeout_per_command = rq->timeout;
+ SCpnt->transfersize = rq->data_len;
+ SCpnt->done = st_intr;
+ return 1;
+}
+
+static int __init init_st(void)
+{
+ validate_options();
+
+ printk(KERN_INFO
+ "st: Version %s, fixed bufsize %d, s/g segs %d\n",
+ verstr, st_fixed_buffer_size, st_max_sg_segs);
+
+ st_sysfs_class = class_simple_create(THIS_MODULE, "scsi_tape");
+ if (IS_ERR(st_sysfs_class)) {
+ st_sysfs_class = NULL;
+ printk(KERN_ERR "Unable create sysfs class for SCSI tapes\n");
+ return 1;
+ }
+
+ if (!register_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
+ ST_MAX_TAPE_ENTRIES, "st")) {
+ if (scsi_register_driver(&st_template.gendrv) == 0) {
+ do_create_driverfs_files();
+ return 0;
+ }
+ if (st_sysfs_class)
+ class_simple_destroy(st_sysfs_class);
+ unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
+
+ ST_MAX_TAPE_ENTRIES);
+ }
+
+ printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", SCSI_TAPE_MAJOR);
+ return 1;
+}
+
+static void __exit exit_st(void)
+{
+ if (st_sysfs_class)
+ class_simple_destroy(st_sysfs_class);
+ st_sysfs_class = NULL;
+ do_remove_driverfs_files();
+ scsi_unregister_driver(&st_template.gendrv);
+ unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
+ ST_MAX_TAPE_ENTRIES);
+ kfree(scsi_tapes);
+ printk(KERN_INFO "st: Unloaded.\n");
+}
+
+module_init(init_st);
+module_exit(exit_st);
+
+
+/* The sysfs driver interface. Read-only at the moment */
+static ssize_t st_try_direct_io_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", try_direct_io);
+}
+static DRIVER_ATTR(try_direct_io, S_IRUGO, st_try_direct_io_show, NULL);
+
+static ssize_t st_fixed_buffer_size_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", st_fixed_buffer_size);
+}
+static DRIVER_ATTR(fixed_buffer_size, S_IRUGO, st_fixed_buffer_size_show, NULL);
+
+static ssize_t st_max_sg_segs_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", st_max_sg_segs);
+}
+static DRIVER_ATTR(max_sg_segs, S_IRUGO, st_max_sg_segs_show, NULL);
+
+static ssize_t st_version_show(struct device_driver *ddd, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "[%s]\n", verstr);
+}
+static DRIVER_ATTR(version, S_IRUGO, st_version_show, NULL);
+
+static void do_create_driverfs_files(void)
+{
+ struct device_driver *driverfs = &st_template.gendrv;
+
+ driver_create_file(driverfs, &driver_attr_try_direct_io);
+ driver_create_file(driverfs, &driver_attr_fixed_buffer_size);
+ driver_create_file(driverfs, &driver_attr_max_sg_segs);
+ driver_create_file(driverfs, &driver_attr_version);
+}
+
+static void do_remove_driverfs_files(void)
+{
+ struct device_driver *driverfs = &st_template.gendrv;
+
+ driver_remove_file(driverfs, &driver_attr_version);
+ driver_remove_file(driverfs, &driver_attr_max_sg_segs);
+ driver_remove_file(driverfs, &driver_attr_fixed_buffer_size);
+ driver_remove_file(driverfs, &driver_attr_try_direct_io);
+}
+
+
+/* The sysfs simple class interface */
+static ssize_t st_defined_show(struct class_device *class_dev, char *buf)
+{
+ struct st_modedef *STm = (struct st_modedef *)class_get_devdata(class_dev);
+ ssize_t l = 0;
+
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STm->defined);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(defined, S_IRUGO, st_defined_show, NULL);
+
+static ssize_t st_defblk_show(struct class_device *class_dev, char *buf)
+{
+ struct st_modedef *STm = (struct st_modedef *)class_get_devdata(class_dev);
+ ssize_t l = 0;
+
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_blksize);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(default_blksize, S_IRUGO, st_defblk_show, NULL);
+
+static ssize_t st_defdensity_show(struct class_device *class_dev, char *buf)
+{
+ struct st_modedef *STm = (struct st_modedef *)class_get_devdata(class_dev);
+ ssize_t l = 0;
+ char *fmt;
+
+ fmt = STm->default_density >= 0 ? "0x%02x\n" : "%d\n";
+ l = snprintf(buf, PAGE_SIZE, fmt, STm->default_density);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(default_density, S_IRUGO, st_defdensity_show, NULL);
+
+static ssize_t st_defcompression_show(struct class_device *class_dev, char *buf)
+{
+ struct st_modedef *STm = (struct st_modedef *)class_get_devdata(class_dev);
+ ssize_t l = 0;
+
+ l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_compression - 1);
+ return l;
+}
+
+CLASS_DEVICE_ATTR(default_compression, S_IRUGO, st_defcompression_show, NULL);
+
+static void do_create_class_files(struct scsi_tape *STp, int dev_num, int mode)
+{
+ int i, rew, error;
+ char name[10];
+ struct class_device *st_class_member;
+
+ if (!st_sysfs_class)
+ return;
+
+ for (rew=0; rew < 2; rew++) {
+ /* Make sure that the minor numbers corresponding to the four
+ first modes always get the same names */
+ i = mode << (4 - ST_NBR_MODE_BITS);
+ snprintf(name, 10, "%s%s%s", rew ? "n" : "",
+ STp->disk->disk_name, st_formats[i]);
+ st_class_member =
+ class_simple_device_add(st_sysfs_class,
+ MKDEV(SCSI_TAPE_MAJOR,
+ TAPE_MINOR(dev_num, mode, rew)),
+ &STp->device->sdev_gendev, "%s", name);
+ if (IS_ERR(st_class_member)) {
+ printk(KERN_WARNING "st%d: class_simple_device_add failed\n",
+ dev_num);
+ goto out;
+ }
+ class_set_devdata(st_class_member, &STp->modes[mode]);
+
+ class_device_create_file(st_class_member,
+ &class_device_attr_defined);
+ class_device_create_file(st_class_member,
+ &class_device_attr_default_blksize);
+ class_device_create_file(st_class_member,
+ &class_device_attr_default_density);
+ class_device_create_file(st_class_member,
+ &class_device_attr_default_compression);
+ if (mode == 0 && rew == 0) {
+ error = sysfs_create_link(&STp->device->sdev_gendev.kobj,
+ &st_class_member->kobj,
+ "tape");
+ if (error) {
+ printk(KERN_ERR
+ "st%d: Can't create sysfs link from SCSI device.\n",
+ dev_num);
+ }
+ }
+ }
+ out:
+ return;
+}
+
+
+/* Pin down user pages and put them into a scatter gather list. Returns <= 0 if
+ - mapping of all pages not successful
+ - any page is above max_pfn
+ (i.e., either completely successful or fails)
+*/
+static int st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages,
+ unsigned long uaddr, size_t count, int rw,
+ unsigned long max_pfn)
+{
+ int i, nr_pages;
+
+ nr_pages = sgl_map_user_pages(sgl, max_pages, uaddr, count, rw);
+ if (nr_pages <= 0)
+ return nr_pages;
+
+ for (i=0; i < nr_pages; i++) {
+ if (page_to_pfn(sgl[i].page) > max_pfn)
+ goto out_unmap;
+ }
+ return nr_pages;
+
+ out_unmap:
+ sgl_unmap_user_pages(sgl, nr_pages, 0);
+ return 0;
+}
+
+
+/* The following functions may be useful for a larger audience. */
+static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages,
+ unsigned long uaddr, size_t count, int rw)
+{
+ int res, i, j;
+ unsigned int nr_pages;
+ struct page **pages;
+
+ nr_pages = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ /* User attempted Overflow! */
+ if ((uaddr + count) < uaddr)
+ return -EINVAL;
+
+ /* Too big */
+ if (nr_pages > max_pages)
+ return -ENOMEM;
+
+ /* Hmm? */
+ if (count == 0)
+ return 0;
+
+ if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ /* Try to fault in all of the necessary pages */
+ down_read(&current->mm->mmap_sem);
+ /* rw==READ means read from drive, write into memory area */
+ res = get_user_pages(
+ current,
+ current->mm,
+ uaddr,
+ nr_pages,
+ rw == READ,
+ 0, /* don't force */
+ pages,
+ NULL);
+ up_read(&current->mm->mmap_sem);
+
+ /* Errors and no page mapped should return here */
+ if (res < nr_pages)
+ goto out_unmap;
+
+ for (i=0; i < nr_pages; i++) {
+ /* FIXME: flush superflous for rw==READ,
+ * probably wrong function for rw==WRITE
+ */
+ flush_dcache_page(pages[i]);
+ }
+
+ /* Populate the scatter/gather list */
+ sgl[0].page = pages[0];
+ sgl[0].offset = uaddr & ~PAGE_MASK;
+ if (nr_pages > 1) {
+ sgl[0].length = PAGE_SIZE - sgl[0].offset;
+ count -= sgl[0].length;
+ for (i=1; i < nr_pages ; i++) {
+ sgl[i].offset = 0;
+ sgl[i].page = pages[i];
+ sgl[i].length = count < PAGE_SIZE ? count : PAGE_SIZE;
+ count -= PAGE_SIZE;
+ }
+ }
+ else {
+ sgl[0].length = count;
+ }
+
+ kfree(pages);
+ return nr_pages;
+
+ out_unmap:
+ if (res > 0) {
+ for (j=0; j < res; j++)
+ page_cache_release(pages[j]);
+ }
+ kfree(pages);
+ return res;
+}
+
+
+/* And unmap them... */
+static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages,
+ int dirtied)
+{
+ int i;
+
+ for (i=0; i < nr_pages; i++) {
+ if (dirtied && !PageReserved(sgl[i].page))
+ SetPageDirty(sgl[i].page);
+ /* FIXME: cache flush missing for rw==READ
+ * FIXME: call the correct reference counting function
+ */
+ page_cache_release(sgl[i].page);
+ }
+
+ return 0;
+}
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
new file mode 100644
index 000000000000..061da111398e
--- /dev/null
+++ b/drivers/scsi/st.h
@@ -0,0 +1,212 @@
+
+#ifndef _ST_H
+#define _ST_H
+
+#include <linux/completion.h>
+
+
+/* Descriptor for analyzed sense data */
+struct st_cmdstatus {
+ int midlevel_result;
+ struct scsi_sense_hdr sense_hdr;
+ int have_sense;
+ u64 uremainder64;
+ u8 flags;
+ u8 remainder_valid;
+ u8 fixed_format;
+ u8 deferred;
+};
+
+/* The tape buffer descriptor. */
+struct st_buffer {
+ unsigned char in_use;
+ unsigned char dma; /* DMA-able buffer */
+ unsigned char do_dio; /* direct i/o set up? */
+ int buffer_size;
+ int buffer_blocks;
+ int buffer_bytes;
+ int read_pointer;
+ int writing;
+ int syscall_result;
+ struct scsi_request *last_SRpnt;
+ struct st_cmdstatus cmdstat;
+ unsigned char *b_data;
+ unsigned short use_sg; /* zero or max number of s/g segments for this adapter */
+ unsigned short sg_segs; /* number of segments in s/g list */
+ unsigned short orig_frp_segs; /* number of segments allocated at first try */
+ unsigned short frp_segs; /* number of buffer segments */
+ unsigned int frp_sg_current; /* driver buffer length currently in s/g list */
+ struct st_buf_fragment *frp; /* the allocated buffer fragment list */
+ struct scatterlist sg[1]; /* MUST BE last item */
+};
+
+/* The tape buffer fragment descriptor */
+struct st_buf_fragment {
+ struct page *page;
+ unsigned int length;
+};
+
+/* The tape mode definition */
+struct st_modedef {
+ unsigned char defined;
+ unsigned char sysv; /* SYS V semantics? */
+ unsigned char do_async_writes;
+ unsigned char do_buffer_writes;
+ unsigned char do_read_ahead;
+ unsigned char defaults_for_writes;
+ unsigned char default_compression; /* 0 = don't touch, etc */
+ short default_density; /* Forced density, -1 = no value */
+ int default_blksize; /* Forced blocksize, -1 = no value */
+ struct cdev *cdevs[2]; /* Auto-rewind and non-rewind devices */
+};
+
+/* Number of modes can be changed by changing ST_NBR_MODE_BITS. The maximum
+ number of modes is 16 (ST_NBR_MODE_BITS 4) */
+#define ST_NBR_MODE_BITS 2
+#define ST_NBR_MODES (1 << ST_NBR_MODE_BITS)
+#define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
+#define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT)
+
+#define ST_MAX_TAPES 128
+#define ST_MAX_TAPE_ENTRIES (ST_MAX_TAPES << (ST_NBR_MODE_BITS + 1))
+
+/* The status related to each partition */
+struct st_partstat {
+ unsigned char rw;
+ unsigned char eof;
+ unsigned char at_sm;
+ unsigned char last_block_valid;
+ u32 last_block_visited;
+ int drv_block; /* The block where the drive head is */
+ int drv_file;
+};
+
+#define ST_NBR_PARTITIONS 4
+
+/* The tape drive descriptor */
+struct scsi_tape {
+ struct scsi_driver *driver;
+ struct scsi_device *device;
+ struct semaphore lock; /* For serialization */
+ struct completion wait; /* For SCSI commands */
+ struct st_buffer *buffer;
+
+ /* Drive characteristics */
+ unsigned char omit_blklims;
+ unsigned char do_auto_lock;
+ unsigned char can_bsr;
+ unsigned char can_partitions;
+ unsigned char two_fm;
+ unsigned char fast_mteom;
+ unsigned char immediate;
+ unsigned char restr_dma;
+ unsigned char scsi2_logical;
+ unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */
+ unsigned char cln_mode; /* 0 = none, otherwise sense byte nbr */
+ unsigned char cln_sense_value;
+ unsigned char cln_sense_mask;
+ unsigned char use_pf; /* Set Page Format bit in all mode selects? */
+ unsigned char try_dio; /* try direct i/o? */
+ unsigned char c_algo; /* compression algorithm */
+ unsigned char pos_unknown; /* after reset position unknown */
+ int tape_type;
+ int long_timeout; /* timeout for commands known to take long time */
+
+ unsigned long max_pfn; /* the maximum page number reachable by the HBA */
+
+ /* Mode characteristics */
+ struct st_modedef modes[ST_NBR_MODES];
+ int current_mode;
+
+ /* Status variables */
+ int partition;
+ int new_partition;
+ int nbr_partitions; /* zero until partition support enabled */
+ struct st_partstat ps[ST_NBR_PARTITIONS];
+ unsigned char dirty;
+ unsigned char ready;
+ unsigned char write_prot;
+ unsigned char drv_write_prot;
+ unsigned char in_use;
+ unsigned char blksize_changed;
+ unsigned char density_changed;
+ unsigned char compression_changed;
+ unsigned char drv_buffer;
+ unsigned char density;
+ unsigned char door_locked;
+ unsigned char autorew_dev; /* auto-rewind device */
+ unsigned char rew_at_close; /* rewind necessary at close */
+ unsigned char inited;
+ unsigned char cleaning_req; /* cleaning requested? */
+ int block_size;
+ int min_block;
+ int max_block;
+ int recover_count; /* From tape opening */
+ int recover_reg; /* From last status call */
+
+#if DEBUG
+ unsigned char write_pending;
+ int nbr_finished;
+ int nbr_waits;
+ int nbr_requests;
+ int nbr_dio;
+ int nbr_pages;
+ int nbr_combinable;
+ unsigned char last_cmnd[6];
+ unsigned char last_sense[16];
+#endif
+ struct gendisk *disk;
+};
+
+/* Bit masks for use_pf */
+#define USE_PF 1
+#define PF_TESTED 2
+
+/* Values of eof */
+#define ST_NOEOF 0
+#define ST_FM_HIT 1
+#define ST_FM 2
+#define ST_EOM_OK 3
+#define ST_EOM_ERROR 4
+#define ST_EOD_1 5
+#define ST_EOD_2 6
+#define ST_EOD 7
+/* EOD hit while reading => ST_EOD_1 => return zero => ST_EOD_2 =>
+ return zero => ST_EOD, return ENOSPC */
+/* When writing: ST_EOM_OK == early warning found, write OK
+ ST_EOD_1 == allow trying new write after early warning
+ ST_EOM_ERROR == early warning found, not able to write all */
+
+/* Values of rw */
+#define ST_IDLE 0
+#define ST_READING 1
+#define ST_WRITING 2
+
+/* Values of ready state */
+#define ST_READY 0
+#define ST_NOT_READY 1
+#define ST_NO_TAPE 2
+
+/* Values for door lock state */
+#define ST_UNLOCKED 0
+#define ST_LOCKED_EXPLICIT 1
+#define ST_LOCKED_AUTO 2
+#define ST_LOCK_FAILS 3
+
+/* Positioning SCSI-commands for Tandberg, etc. drives */
+#define QFA_REQUEST_BLOCK 0x02
+#define QFA_SEEK_BLOCK 0x0c
+
+/* Setting the binary options */
+#define ST_DONT_TOUCH 0
+#define ST_NO 1
+#define ST_YES 2
+
+#define EXTENDED_SENSE_START 18
+
+/* Masks for some conditions in the sense data */
+#define SENSE_FMK 0x80
+#define SENSE_EOM 0x40
+#define SENSE_ILI 0x20
+
+#endif
diff --git a/drivers/scsi/st_options.h b/drivers/scsi/st_options.h
new file mode 100644
index 000000000000..b6b5c9c37677
--- /dev/null
+++ b/drivers/scsi/st_options.h
@@ -0,0 +1,100 @@
+/*
+ The compile-time configurable defaults for the Linux SCSI tape driver.
+
+ Copyright 1995-2003 Kai Makisara.
+
+ Last modified: Mon Apr 7 22:49:18 2003 by makisara
+*/
+
+#ifndef _ST_OPTIONS_H
+#define _ST_OPTIONS_H
+
+/* If TRY_DIRECT_IO is non-zero, the driver tries to transfer data directly
+ between the user buffer and tape drive. If this is not possible, driver
+ buffer is used. If TRY_DIRECT_IO is zero, driver buffer is always used. */
+#define TRY_DIRECT_IO 1
+
+/* The driver does not wait for some operations to finish before returning
+ to the user program if ST_NOWAIT is non-zero. This helps if the SCSI
+ adapter does not support multiple outstanding commands. However, the user
+ should not give a new tape command before the previous one has finished. */
+#define ST_NOWAIT 0
+
+/* If ST_IN_FILE_POS is nonzero, the driver positions the tape after the
+ record been read by the user program even if the tape has moved further
+ because of buffered reads. Should be set to zero to support also drives
+ that can't space backwards over records. NOTE: The tape will be
+ spaced backwards over an "accidentally" crossed filemark in any case. */
+#define ST_IN_FILE_POS 0
+
+/* If ST_RECOVERED_WRITE_FATAL is non-zero, recovered errors while writing
+ are considered "hard errors". */
+#define ST_RECOVERED_WRITE_FATAL 0
+
+/* The "guess" for the block size for devices that don't support MODE
+ SENSE. */
+#define ST_DEFAULT_BLOCK 0
+
+/* The minimum tape driver buffer size in kilobytes in fixed block mode.
+ Must be non-zero. */
+#define ST_FIXED_BUFFER_BLOCKS 32
+
+/* Maximum number of scatter/gather segments */
+#define ST_MAX_SG 256
+
+/* The number of scatter/gather segments to allocate at first try (must be
+ smaller or equal to the maximum). */
+#define ST_FIRST_SG 8
+
+/* The size of the first scatter/gather segments (determines the maximum block
+ size for SCSI adapters not supporting scatter/gather). The default is set
+ to try to allocate the buffer as one chunk. */
+#define ST_FIRST_ORDER 5
+
+
+/* The following lines define defaults for properties that can be set
+ separately for each drive using the MTSTOPTIONS ioctl. */
+
+/* If ST_TWO_FM is non-zero, the driver writes two filemarks after a
+ file being written. Some drives can't handle two filemarks at the
+ end of data. */
+#define ST_TWO_FM 0
+
+/* If ST_BUFFER_WRITES is non-zero, writes in fixed block mode are
+ buffered until the driver buffer is full or asynchronous write is
+ triggered. May make detection of End-Of-Medium early enough fail. */
+#define ST_BUFFER_WRITES 1
+
+/* If ST_ASYNC_WRITES is non-zero, the SCSI write command may be started
+ without waiting for it to finish. May cause problems in multiple
+ tape backups. */
+#define ST_ASYNC_WRITES 1
+
+/* If ST_READ_AHEAD is non-zero, blocks are read ahead in fixed block
+ mode. */
+#define ST_READ_AHEAD 1
+
+/* If ST_AUTO_LOCK is non-zero, the drive door is locked at the first
+ read or write command after the device is opened. The door is opened
+ when the device is closed. */
+#define ST_AUTO_LOCK 0
+
+/* If ST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the
+ direct SCSI command. The file number status is lost but this method
+ is fast with some drives. Otherwise MTEOM is done by spacing over
+ files and the file number status is retained. */
+#define ST_FAST_MTEOM 0
+
+/* If ST_SCSI2LOGICAL is nonzero, the logical block addresses are used for
+ MTIOCPOS and MTSEEK by default. Vendor addresses are used if ST_SCSI2LOGICAL
+ is zero. */
+#define ST_SCSI2LOGICAL 0
+
+/* If ST_SYSV is non-zero, the tape behaves according to the SYS V semantics.
+ The default is BSD semantics. */
+#define ST_SYSV 0
+
+/* Time to wait for the drive to become ready if blocking open */
+#define ST_BLOCK_SECONDS 120
+
+#endif
diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c
new file mode 100644
index 000000000000..90811390a37d
--- /dev/null
+++ b/drivers/scsi/sun3_NCR5380.c
@@ -0,0 +1,3009 @@
+/* sun3_NCR5380.c -- adapted from atari_NCR5380.c for the sun3 by
+ Sam Creasey. */
+/*
+ * NCR 5380 generic driver routines. These should make it *trivial*
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
+ *
+ * Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
+ * this file, too:
+ *
+ * - Some of the debug statements were incorrect (undefined variables and the
+ * like). I fixed that.
+ *
+ * - In information_transfer(), I think a #ifdef was wrong. Looking at the
+ * possible DMA transfer size should also happen for REAL_DMA. I added this
+ * in the #if statement.
+ *
+ * - When using real DMA, information_transfer() should return in a DATAOUT
+ * phase after starting the DMA. It has nothing more to do.
+ *
+ * - The interrupt service routine should run main after end of DMA, too (not
+ * only after RESELECTION interrupts). Additionally, it should _not_ test
+ * for more interrupts after running main, since a DMA process may have
+ * been started and interrupts are turned on now. The new int could happen
+ * inside the execution of NCR5380_intr(), leading to recursive
+ * calls.
+ *
+ * - I've added a function merge_contiguous_buffers() that tries to
+ * merge scatter-gather buffers that are located at contiguous
+ * physical addresses and can be processed with the same DMA setup.
+ * Since most scatter-gather operations work on a page (4K) of
+ * 4 buffers (1K), in more than 90% of all cases three interrupts and
+ * DMA setup actions are saved.
+ *
+ * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
+ * and USLEEP, because these were messing up readability and will never be
+ * needed for Atari SCSI.
+ *
+ * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
+ * stuff), and 'main' is executed in a bottom half if awoken by an
+ * interrupt.
+ *
+ * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
+ * constructs. In my eyes, this made the source rather unreadable, so I
+ * finally replaced that by the *_PRINTK() macros.
+ *
+ */
+
+/*
+ * Further development / testing that should be done :
+ * 1. Test linked command handling code after Eric is ready with
+ * the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) \
+ { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \
+ if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) \
+ { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \
+ (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \
+ if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip. This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver. To use it on a different platform,
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem,
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system. So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the
+ * NCR5380_print_options command, which should be called from the
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 :
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations.
+ *
+ * The workaround for this is to keep track of devices that have
+ * disconnected. If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ *
+ * Some tweaking of N and M needs to be done. An algorithm based
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function. It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the
+ * issue queue and calling NCR5380_select() if a nexus
+ * is not established.
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work. If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr() which will in turn call NCR5380_reselect
+ * to reestablish a nexus. This will run main if necessary.
+ *
+ * On command termination, the done function will be called as
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips. To use it, you write an architecture specific functions
+ * and macros and include this file in your driver.
+ *
+ * These macros control options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * These macros MUST be defined :
+ *
+ * NCR5380_read(register) - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions :
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes
+ * that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define
+ *
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ * NCR5380_proc_info
+ *
+ * to be the global entry points into the specific driver, ie
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID. If the
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used. Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/* Macros ease life... :-) */
+#define SETUP_HOSTDATA(in) \
+ struct NCR5380_hostdata *hostdata = \
+ (struct NCR5380_hostdata *)(in)->hostdata
+#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
+
+#define NEXT(cmd) ((Scsi_Cmnd *)((cmd)->host_scribble))
+#define NEXTADDR(cmd) ((Scsi_Cmnd **)&((cmd)->host_scribble))
+
+#define HOSTNO instance->host_no
+#define H_NO(cmd) (cmd)->device->host->host_no
+
+#define SGADDR(buffer) (void *)(((unsigned long)page_address((buffer)->page)) + \
+ (buffer)->offset)
+
+#ifdef SUPPORT_TAGS
+
+/*
+ * Functions for handling tagged queuing
+ * =====================================
+ *
+ * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
+ *
+ * Using consecutive numbers for the tags is no good idea in my eyes. There
+ * could be wrong re-usings if the counter (8 bit!) wraps and some early
+ * command has been preempted for a long time. My solution: a bitfield for
+ * remembering used tags.
+ *
+ * There's also the problem that each target has a certain queue size, but we
+ * cannot know it in advance :-( We just see a QUEUE_FULL status being
+ * returned. So, in this case, the driver internal queue size assumption is
+ * reduced to the number of active tags if QUEUE_FULL is returned by the
+ * target. The command is returned to the mid-level, but with status changed
+ * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
+ * correctly.
+ *
+ * We're also not allowed running tagged commands as long as an untagged
+ * command is active. And REQUEST SENSE commands after a contingent allegiance
+ * condition _must_ be untagged. To keep track whether an untagged command has
+ * been issued, the host->busy array is still employed, as it is without
+ * support for tagged queuing.
+ *
+ * One could suspect that there are possible race conditions between
+ * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
+ * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
+ * which already guaranteed to be running at most once. It is also the only
+ * place where tags/LUNs are allocated. So no other allocation can slip
+ * between that pair, there could only happen a reselection, which can free a
+ * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
+ * important: the tag bit must be cleared before 'nr_allocated' is decreased.
+ */
+
+/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
+#undef TAG_NONE
+#define TAG_NONE 0xff
+
+/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */
+#if (MAX_TAGS % 32) != 0
+#error "MAX_TAGS must be a multiple of 32!"
+#endif
+
+typedef struct {
+ char allocated[MAX_TAGS/8];
+ int nr_allocated;
+ int queue_size;
+} TAG_ALLOC;
+
+static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
+
+
+static void __init init_tags( void )
+{
+ int target, lun;
+ TAG_ALLOC *ta;
+
+ if (!setup_use_tagged_queuing)
+ return;
+
+ for( target = 0; target < 8; ++target ) {
+ for( lun = 0; lun < 8; ++lun ) {
+ ta = &TagAlloc[target][lun];
+ memset( &ta->allocated, 0, MAX_TAGS/8 );
+ ta->nr_allocated = 0;
+ /* At the beginning, assume the maximum queue size we could
+ * support (MAX_TAGS). This value will be decreased if the target
+ * returns QUEUE_FULL status.
+ */
+ ta->queue_size = MAX_TAGS;
+ }
+ }
+}
+
+
+/* Check if we can issue a command to this LUN: First see if the LUN is marked
+ * busy by an untagged command. If the command should use tagged queuing, also
+ * check that there is a free tag and the target's queue won't overflow. This
+ * function should be called with interrupts disabled to avoid race
+ * conditions.
+ */
+
+static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+ SETUP_HOSTDATA(cmd->device->host);
+
+ if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))
+ return( 1 );
+ if (!should_be_tagged ||
+ !setup_use_tagged_queuing || !cmd->device->tagged_supported)
+ return( 0 );
+ if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >=
+ TagAlloc[cmd->device->id][cmd->device->lun].queue_size ) {
+ TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n",
+ H_NO(cmd), cmd->device->id, cmd->device->lun );
+ return( 1 );
+ }
+ return( 0 );
+}
+
+
+/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
+ * must be called before!), or reserve the LUN in 'busy' if the command is
+ * untagged.
+ */
+
+static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+ SETUP_HOSTDATA(cmd->device->host);
+
+ /* If we or the target don't support tagged queuing, allocate the LUN for
+ * an untagged command.
+ */
+ if (!should_be_tagged ||
+ !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
+ cmd->tag = TAG_NONE;
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged "
+ "command\n", H_NO(cmd), cmd->device->id, cmd->device->lun );
+ }
+ else {
+ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+
+ cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS );
+ set_bit( cmd->tag, &ta->allocated );
+ ta->nr_allocated++;
+ TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
+ "(now %d tags in use)\n",
+ H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun,
+ ta->nr_allocated );
+ }
+}
+
+
+/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
+ * unlock the LUN.
+ */
+
+static void cmd_free_tag( Scsi_Cmnd *cmd )
+{
+ SETUP_HOSTDATA(cmd->device->host);
+
+ if (cmd->tag == TAG_NONE) {
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n",
+ H_NO(cmd), cmd->device->id, cmd->device->lun );
+ }
+ else if (cmd->tag >= MAX_TAGS) {
+ printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
+ H_NO(cmd), cmd->tag );
+ }
+ else {
+ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+ clear_bit( cmd->tag, &ta->allocated );
+ ta->nr_allocated--;
+ TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
+ H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun );
+ }
+}
+
+
+static void free_all_tags( void )
+{
+ int target, lun;
+ TAG_ALLOC *ta;
+
+ if (!setup_use_tagged_queuing)
+ return;
+
+ for( target = 0; target < 8; ++target ) {
+ for( lun = 0; lun < 8; ++lun ) {
+ ta = &TagAlloc[target][lun];
+ memset( &ta->allocated, 0, MAX_TAGS/8 );
+ ta->nr_allocated = 0;
+ }
+ }
+}
+
+#endif /* SUPPORT_TAGS */
+
+
+/*
+ * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+ *
+ * Purpose: Try to merge several scatter-gather requests into one DMA
+ * transfer. This is possible if the scatter buffers lie on
+ * physical contiguous addresses.
+ *
+ * Parameters: Scsi_Cmnd *cmd
+ * The command to work on. The first scatter buffer's data are
+ * assumed to be already transfered into ptr/this_residual.
+ */
+
+static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+{
+ unsigned long endaddr;
+#if (NDEBUG & NDEBUG_MERGING)
+ unsigned long oldlen = cmd->SCp.this_residual;
+ int cnt = 1;
+#endif
+
+ for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
+ cmd->SCp.buffers_residual &&
+ virt_to_phys(SGADDR(&(cmd->SCp.buffer[1]))) == endaddr; ) {
+
+ MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
+ SGADDR(&(cmd->SCp.buffer[1])), endaddr);
+#if (NDEBUG & NDEBUG_MERGING)
+ ++cnt;
+#endif
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual += cmd->SCp.buffer->length;
+ endaddr += cmd->SCp.buffer->length;
+ }
+#if (NDEBUG & NDEBUG_MERGING)
+ if (oldlen != cmd->SCp.this_residual)
+ MER_PRINTK("merged %d buffers from %p, new length %08x\n",
+ cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
+#endif
+}
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the
+ * start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+ /*
+ * Initialize the Scsi Pointer field so that all of the commands in the
+ * various queues are valid.
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *) SGADDR(cmd->SCp.buffer);
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+
+ /* ++roman: Try to merge some scatter-buffers if they are at
+ * contiguous physical addresses.
+ */
+// merge_contiguous_buffers( cmd );
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+
+}
+
+#include <linux/config.h>
+#include <linux/delay.h>
+
+#if 1
+static struct {
+ unsigned char mask;
+ const char * name;}
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
+ { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
+ { SR_SEL, "SEL" }, {0, NULL}},
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
+ "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
+ {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+ {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
+ {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+ unsigned char status, data, basr, mr, icr, i;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+ status = NCR5380_read(STATUS_REG);
+ mr = NCR5380_read(MODE_REG);
+ icr = NCR5380_read(INITIATOR_COMMAND_REG);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ local_irq_restore(flags);
+ printk("STATUS_REG: %02x ", status);
+ for (i = 0; signals[i].mask ; ++i)
+ if (status & signals[i].mask)
+ printk(",%s", signals[i].name);
+ printk("\nBASR: %02x ", basr);
+ for (i = 0; basrs[i].mask ; ++i)
+ if (basr & basrs[i].mask)
+ printk(",%s", basrs[i].name);
+ printk("\nICR: %02x ", icr);
+ for (i = 0; icrs[i].mask; ++i)
+ if (icr & icrs[i].mask)
+ printk(",%s", icrs[i].name);
+ printk("\nMODE: %02x ", mr);
+ for (i = 0; mrs[i].mask; ++i)
+ if (mr & mrs[i].mask)
+ printk(",%s", mrs[i].name);
+ printk("\n");
+}
+
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+ {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+ {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+ {PHASE_UNKNOWN, "UNKNOWN"}};
+
+/*
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance)
+{
+ unsigned char status;
+ int i;
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i);
+ printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
+ }
+}
+
+#else /* !NDEBUG */
+
+/* dummies... */
+__inline__ void NCR5380_print(struct Scsi_Host *instance) { };
+__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { };
+
+#endif
+
+/*
+ * ++roman: New scheme of calling NCR5380_main()
+ *
+ * If we're not in an interrupt, we can call our main directly, it cannot be
+ * already running. Else, we queue it on a task queue, if not 'main_running'
+ * tells us that a lower level is already executing it. This way,
+ * 'main_running' needs not be protected in a special way.
+ *
+ * queue_main() is a utility function for putting our main onto the task
+ * queue, if main_running is false. It should be called only from a
+ * interrupt or bottom half.
+ */
+
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+
+static volatile int main_running = 0;
+static DECLARE_WORK(NCR5380_tqueue, (void (*)(void*))NCR5380_main, NULL);
+
+static __inline__ void queue_main(void)
+{
+ if (!main_running) {
+ /* If in interrupt and NCR5380_main() not already running,
+ queue it on the 'immediate' task queue, to be processed
+ immediately after the current interrupt processing has
+ finished. */
+ schedule_work(&NCR5380_tqueue);
+ }
+ /* else: nothing to do: the running NCR5380_main() will pick up
+ any newly queued command. */
+}
+
+
+static inline void NCR5380_all_init (void)
+{
+ static int done = 0;
+ if (!done) {
+ INI_PRINTK("scsi : NCR5380_all_init()\n");
+ done = 1;
+ }
+}
+
+
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ * options that were selected.
+ *
+ * Inputs : instance, pointer to this instance. Unused.
+ */
+
+static void __init NCR5380_print_options (struct Scsi_Host *instance)
+{
+ printk(" generic options"
+#ifdef AUTOSENSE
+ " AUTOSENSE"
+#endif
+#ifdef REAL_DMA
+ " REAL DMA"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef SUPPORT_TAGS
+ " SCSI-2 TAGGED QUEUING"
+#endif
+ );
+ printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ * NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance)
+{
+ char *pr_bfr;
+ char *start;
+ int len;
+
+ NCR_PRINT(NDEBUG_ANY);
+ NCR_PRINT_PHASE(NDEBUG_ANY);
+
+ pr_bfr = (char *) __get_free_page(GFP_ATOMIC);
+ if (!pr_bfr) {
+ printk("NCR5380_print_status: no memory for print buffer\n");
+ return;
+ }
+ len = NCR5380_proc_info(instance, pr_bfr, &start, 0, PAGE_SIZE, 0);
+ pr_bfr[len] = 0;
+ printk("\n%s\n", pr_bfr);
+ free_page((unsigned long) pr_bfr);
+}
+
+
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+*/
+
+#undef SPRINTF
+#define SPRINTF(fmt,args...) \
+ do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
+ pos += sprintf(pos, fmt , ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
+
+static int NCR5380_proc_info (struct Scsi_Host *instance, char *buffer, char **start,
+ off_t offset, int length, int inout)
+{
+ char *pos = buffer;
+ struct NCR5380_hostdata *hostdata;
+ Scsi_Cmnd *ptr;
+ unsigned long flags;
+ off_t begin = 0;
+#define check_offset() \
+ do { \
+ if (pos - buffer < offset - begin) { \
+ begin += pos - buffer; \
+ pos = buffer; \
+ } \
+ } while (0)
+
+ hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+
+ if (inout) { /* Has data been written to the file ? */
+ return(-ENOSYS); /* Currently this is a no-op */
+ }
+ SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
+ check_offset();
+ local_irq_save(flags);
+ SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't");
+ check_offset();
+ if (!hostdata->connected)
+ SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
+ else
+ pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
+ pos, buffer, length);
+ SPRINTF("scsi%d: issue_queue\n", HOSTNO);
+ check_offset();
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+ check_offset();
+ }
+
+ SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
+ check_offset();
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = NEXT(ptr)) {
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+ check_offset();
+ }
+
+ local_irq_restore(flags);
+ *start = buffer + (offset - begin);
+ if (pos - buffer < offset - begin)
+ return 0;
+ else if (pos - buffer - (offset - begin) < length)
+ return pos - buffer - (offset - begin);
+ return length;
+}
+
+static char *
+lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
+{
+ int i, s;
+ unsigned char *command;
+ SPRINTF("scsi%d: destination target %d, lun %d\n",
+ H_NO(cmd), cmd->device->id, cmd->device->lun);
+ SPRINTF(" command = ");
+ command = cmd->cmnd;
+ SPRINTF("%2d (0x%02x)", command[0], command[0]);
+ for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ SPRINTF(" %02x", command[i]);
+ SPRINTF("\n");
+ return pos;
+}
+
+
+/*
+ * Function : void NCR5380_init (struct Scsi_Host *instance)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
+ *
+ */
+
+static int NCR5380_init (struct Scsi_Host *instance, int flags)
+{
+ int i;
+ SETUP_HOSTDATA(instance);
+
+ NCR5380_all_init();
+
+ hostdata->aborted = 0;
+ hostdata->id_mask = 1 << instance->this_id;
+ hostdata->id_higher_mask = 0;
+ for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+ if (i > hostdata->id_mask)
+ hostdata->id_higher_mask |= i;
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+#ifdef SUPPORT_TAGS
+ init_tags();
+#endif
+#if defined (REAL_DMA)
+ hostdata->dma_len = 0;
+#endif
+ hostdata->targets_present = 0;
+ hostdata->connected = NULL;
+ hostdata->issue_queue = NULL;
+ hostdata->disconnected_queue = NULL;
+ hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
+
+ if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance;
+ }
+
+
+#ifndef AUTOSENSE
+ if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
+ printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
+ " without AUTOSENSE option, contingent allegiance conditions may\n"
+ " be incorrectly cleared.\n", HOSTNO);
+#endif /* def AUTOSENSE */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
+ *
+ */
+
+/* Only make static if a wrapper function is used */
+static int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+ SETUP_HOSTDATA(cmd->device->host);
+ Scsi_Cmnd *tmp;
+ unsigned long flags;
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
+ H_NO(cmd));
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ return 0;
+ }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+
+#ifdef NCR5380_STATS
+# if 0
+ if (!hostdata->connected && !hostdata->issue_queue &&
+ !hostdata->disconnected_queue) {
+ hostdata->timebase = jiffies;
+ }
+# endif
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_write[cmd->device->id] += cmd->request_bufflen;
+ hostdata->pendingw++;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_read[cmd->device->id] += cmd->request_bufflen;
+ hostdata->pendingr++;
+ break;
+ }
+#endif
+
+ /*
+ * We use the host_scribble field as a pointer to the next command
+ * in a queue
+ */
+
+ NEXT(cmd) = NULL;
+ cmd->scsi_done = done;
+
+ cmd->result = 0;
+
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+ local_irq_save(flags);
+ /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
+ * Otherwise a running NCR5380_main may steal the lock.
+ * Lock before actually inserting due to fairness reasons explained in
+ * atari_scsi.c. If we insert first, then it's impossible for this driver
+ * to release the lock.
+ * Stop timer for this command while waiting for the lock, or timeouts
+ * may happen (and they really do), and it's no good if the command doesn't
+ * appear in any of the queues.
+ * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+ * because also a timer int can trigger an abort or reset, which would
+ * alter queues and touch the lock.
+ */
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
+ NEXT(tmp); tmp = NEXT(tmp))
+ ;
+ LIST(cmd, tmp);
+ NEXT(tmp) = cmd;
+ }
+
+ local_irq_restore(flags);
+
+ QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd),
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+
+ /* If queue_command() is called from an interrupt (real one or bottom
+ * half), we let queue_main() do the job of taking care about main. If it
+ * is already running, this is a no-op, else main will be queued.
+ *
+ * If we're not in an interrupt, we can call NCR5380_main()
+ * unconditionally, because it cannot be already running.
+ */
+ if (in_interrupt() || ((flags >> 8) & 7) >= 6)
+ queue_main();
+ else
+ NCR5380_main(NULL);
+ return 0;
+}
+
+/*
+ * Function : NCR5380_main (void)
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ */
+
+static void NCR5380_main (void *bl)
+{
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *instance = first_instance;
+ struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
+ int done;
+ unsigned long flags;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ * this should prevent any race conditions.
+ *
+ * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+ * because also a timer int can trigger an abort or reset, which can
+ * alter queues and touch the Falcon lock.
+ */
+
+ /* Tell int handlers main() is now already executing. Note that
+ no races are possible here. If an int comes in before
+ 'main_running' is set here, and queues/executes main via the
+ task queue, it doesn't do any harm, just this instance of main
+ won't find any work left to do. */
+ if (main_running)
+ return;
+ main_running = 1;
+
+ local_save_flags(flags);
+ do {
+ local_irq_disable(); /* Freeze request queues */
+ done = 1;
+
+ if (!hostdata->connected) {
+ MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO );
+ /*
+ * Search through the issue_queue for a command destined
+ * for a target that's not busy.
+ */
+#if (NDEBUG & NDEBUG_LISTS)
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
+ tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
+ ;
+ if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {
+
+#if (NDEBUG & NDEBUG_LISTS)
+ if (prev != tmp)
+ printk("MAIN tmp=%p target=%d busy=%d lun=%d\n",
+ tmp, tmp->target, hostdata->busy[tmp->target],
+ tmp->lun);
+#endif
+ /* When we find one, remove it from the issue queue. */
+ /* ++guenther: possible race with Falcon locking */
+ if (
+#ifdef SUPPORT_TAGS
+ !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
+#else
+ !(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))
+#endif
+ ) {
+ /* ++guenther: just to be sure, this must be atomic */
+ local_irq_disable();
+ if (prev) {
+ REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+ NEXT(prev) = NEXT(tmp);
+ } else {
+ REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
+ hostdata->issue_queue = NEXT(tmp);
+ }
+ NEXT(tmp) = NULL;
+
+ /* reenable interrupts after finding one */
+ local_irq_restore(flags);
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+ MAIN_PRINTK("scsi%d: main(): command for target %d "
+ "lun %d removed from issue_queue\n",
+ HOSTNO, tmp->target, tmp->lun);
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+ /* ++roman: ...and the standard also requires that
+ * REQUEST SENSE command are untagged.
+ */
+
+#ifdef SUPPORT_TAGS
+ cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );
+#endif
+ if (!NCR5380_select(instance, tmp,
+ (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
+ TAG_NEXT)) {
+ break;
+ } else {
+ local_irq_disable();
+ LIST(tmp, hostdata->issue_queue);
+ NEXT(tmp) = hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( tmp );
+#endif
+ local_irq_restore(flags);
+ MAIN_PRINTK("scsi%d: main(): select() failed, "
+ "returned to issue_queue\n", HOSTNO);
+ if (hostdata->connected)
+ break;
+ }
+ } /* if target/lun/target queue is not busy */
+ } /* for issue_queue */
+ } /* if (!hostdata->connected) */
+ if (hostdata->connected
+#ifdef REAL_DMA
+ && !hostdata->dma_len
+#endif
+ ) {
+ local_irq_restore(flags);
+ MAIN_PRINTK("scsi%d: main: performing information transfer\n",
+ HOSTNO);
+ NCR5380_information_transfer(instance);
+ MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO);
+ done = 0;
+ }
+ } while (!done);
+
+ /* Better allow ints _after_ 'main_running' has been cleared, else
+ an interrupt could believe we'll pick up the work it left for
+ us, but we won't see it anymore here... */
+ main_running = 0;
+ local_irq_restore(flags);
+}
+
+
+#ifdef REAL_DMA
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : Called by interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would finish the DMA transfer).
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+static void NCR5380_dma_complete( struct Scsi_Host *instance )
+{
+ SETUP_HOSTDATA(instance);
+ int transfered;
+ unsigned char **data;
+ volatile int *count;
+
+ if (!hostdata->connected) {
+ printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
+ "no connected cmd\n", HOSTNO);
+ return;
+ }
+
+ DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
+ HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
+ NCR5380_read(STATUS_REG));
+
+ if((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) {
+ printk("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", HOSTNO);
+ printk("please e-mail sammy@sammy.net with a description of how this\n");
+ printk("error was produced.\n");
+ BUG();
+ }
+
+ /* make sure we're not stuck in a data phase */
+ if((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH |
+ BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK)) {
+ printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG));
+ printk("scsi%d: bus stuck in data phase -- probably a single byte "
+ "overrun!\n", HOSTNO);
+ printk("not prepared for this error!\n");
+ printk("please e-mail sammy@sammy.net with a description of how this\n");
+ printk("error was produced.\n");
+ BUG();
+ }
+
+
+
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
+ hostdata->dma_len = 0;
+
+ data = (unsigned char **) &(hostdata->connected->SCp.ptr);
+ count = &(hostdata->connected->SCp.this_residual);
+ *data += transfered;
+ *count -= transfered;
+
+}
+#endif /* REAL_DMA */
+
+
+/*
+ * Function : void NCR5380_intr (int irq)
+ *
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static irqreturn_t NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *instance = first_instance;
+ int done = 1, handled = 0;
+ unsigned char basr;
+
+ INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO);
+
+ /* Look for pending interrupts */
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr);
+ /* dispatch to appropriate routine if found and done=0 */
+ if (basr & BASR_IRQ) {
+ NCR_PRINT(NDEBUG_INTR);
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
+ done = 0;
+ ENABLE_IRQ();
+ INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO);
+ NCR5380_reselect(instance);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else if (basr & BASR_PARITY_ERROR) {
+ INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+ INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO);
+ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else {
+ /*
+ * The rest of the interrupt conditions can occur only during a
+ * DMA transfer
+ */
+
+#if defined(REAL_DMA)
+ /*
+ * We should only get PHASE MISMATCH and EOP interrupts if we have
+ * DMA enabled, so do a sanity check based on the current setting
+ * of the MODE register.
+ */
+
+ if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
+ ((basr & BASR_END_DMA_TRANSFER) ||
+ !(basr & BASR_PHASE_MATCH))) {
+
+ INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
+ NCR5380_dma_complete( instance );
+ done = 0;
+ ENABLE_IRQ();
+ } else
+#endif /* REAL_DMA */
+ {
+/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
+ if (basr & BASR_PHASE_MATCH)
+ INT_PRINTK("scsi%d: unknown interrupt, "
+ "BASR 0x%x, MR 0x%x, SR 0x%x\n",
+ HOSTNO, basr, NCR5380_read(MODE_REG),
+ NCR5380_read(STATUS_REG));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+ }
+ } /* if !(SELECTION || PARITY) */
+ handled = 1;
+ } /* BASR & IRQ */
+ else {
+
+ printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
+ "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
+ NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+ }
+
+ if (!done) {
+ INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO);
+ /* Put a call to NCR5380_main() on the queue... */
+ queue_main();
+ }
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef NCR5380_STATS
+static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
+{
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_write[cmd->device->id] += cmd->request_bufflen;*/
+ hostdata->pendingw--;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_read[cmd->device->id] += cmd->request_bufflen;*/
+ hostdata->pendingr--;
+ break;
+ }
+}
+#endif
+
+/*
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ * int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ * including ARBITRATION, SELECTION, and initial message out for
+ * IDENTIFY and queue messages.
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this
+ * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
+ * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
+ * the command that is presently connected.
+ *
+ * Returns : -1 if selection could not execute for some reason,
+ * 0 if selection succeeded or failed because the target
+ * did not respond.
+ *
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
+ *
+ * If successful : I_T_L or I_T_L_Q nexus will be established,
+ * instance->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
+ *
+ * If failed (no target) : cmd->scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned char tmp[3], phase;
+ unsigned char *data;
+ int len;
+ unsigned long timeout;
+ unsigned long flags;
+
+ hostdata->restart_select = 0;
+ NCR_PRINT(NDEBUG_ARBITRATION);
+ ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO,
+ instance->this_id);
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION.
+ */
+
+ local_irq_save(flags);
+ if (hostdata->connected) {
+ local_irq_restore(flags);
+ return -1;
+ }
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+ /*
+ * Start arbitration.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+ local_irq_restore(flags);
+
+ /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && time_before(jiffies, timeout) && !hostdata->connected)
+ ;
+ if (time_after_eq(jiffies, timeout))
+ {
+ printk("scsi : arbitration timeout at %d\n", __LINE__);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && !hostdata->connected);
+#endif
+
+ ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO);
+
+ if (hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ return -1;
+ }
+ /*
+ * The arbitration delay is 2.2us, but this is a minimum and there is
+ * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+ * the integral nature of udelay().
+ *
+ */
+
+ udelay(3);
+
+ /* Check for lost arbitration */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+ (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
+ HOSTNO);
+ return -1;
+ }
+
+ /* after/during arbitration, BSY should be asserted.
+ IBM DPES-31080 Version S31Q works now */
+ /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL |
+ ICR_ASSERT_BSY ) ;
+
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
+ HOSTNO);
+ return -1;
+ }
+
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
+ * a minimum so we'll udelay ceil(1.2)
+ */
+
+#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
+ /* ++roman: But some targets (see above :-) seem to need a bit more... */
+ udelay(15);
+#else
+ udelay(2);
+#endif
+
+ if (hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return -1;
+ }
+
+ ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO);
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
+ * the host and target ID's on the SCSI bus.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id)));
+
+ /*
+ * Raise ATN while SEL is true before BSY goes false from arbitration,
+ * since this is the only way to guarantee that we'll get a MESSAGE OUT
+ * phase immediately after selection.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+ NCR5380_write(MODE_REG, MR_BASE);
+
+ /*
+ * Reselect interrupts must be turned off prior to the dropping of BSY,
+ * otherwise we will trigger an interrupt.
+ */
+
+ if (hostdata->connected) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return -1;
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /*
+ * The initiator shall then wait at least two deskew delays and release
+ * the BSY signal.
+ */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+
+ /* Reset BSY */
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+ /*
+ * Something weird happens when we cease to drive BSY - looks
+ * like the board/chip is letting us do another read before the
+ * appropriate propagation delay has expired, and we're confusing
+ * a BSY signal from ourselves as the target's response to SELECTION.
+ *
+ * A small delay (the 'C++' frontend breaks the pipeline with an
+ * unnecessary jump, making it work on my 386-33/Trantor T128, the
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
+ * work there.
+ *
+ * wingel suggests that this could be due to failing to wait
+ * one deskew delay.
+ */
+
+ udelay(1);
+
+ SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->device->id);
+
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
+ * selection.
+ */
+
+ timeout = jiffies + 25;
+
+ /*
+ * XXX very interesting - we're seeing a bounce where the BSY we
+ * asserted is being reflected / still asserted (propagation delay?)
+ * and it's detecting as true. Sigh.
+ */
+
+#if 0
+ /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
+ * IO while SEL is true. But again, there are some disks out the in the
+ * world that do that nevertheless. (Somebody claimed that this announces
+ * reselection capability of the target.) So we better skip that test and
+ * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
+ */
+
+ while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) &
+ (SR_BSY | SR_IO)));
+
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_reselect(instance);
+ printk (KERN_ERR "scsi%d: reselection after won arbitration?\n",
+ HOSTNO);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+#else
+ while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+#endif
+
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
+ * change the DATA BUS. -wingel
+ */
+
+ udelay(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ if (hostdata->targets_present & (1 << cmd->device->id)) {
+ printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
+ if (hostdata->restart_select)
+ printk(KERN_NOTICE "\trestart select\n");
+ NCR_PRINT(NDEBUG_ANY);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ cmd->result = DID_BAD_TARGET << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return 0;
+ }
+
+ hostdata->targets_present |= (1 << cmd->device->id);
+
+ /*
+ * Since we followed the SCSI spec, and raised ATN while SEL
+ * was true but before BSY was false during selection, the information
+ * transfer phase should be a MESSAGE OUT phase so that we can send the
+ * IDENTIFY message.
+ *
+ * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+ * message (2 bytes) with a tag ID that we increment with every command
+ * until it wraps back to 0.
+ *
+ * XXX - it turns out that there are some broken SCSI-II devices,
+ * which claim to support tagged queuing but fail when more than
+ * some number of commands are issued at once.
+ */
+
+ /* Wait for start of REQ/ACK handshake */
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+ SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
+ HOSTNO, cmd->device->id);
+ tmp[0] = IDENTIFY(1, cmd->device->lun);
+
+#ifdef SUPPORT_TAGS
+ if (cmd->tag != TAG_NONE) {
+ tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
+ tmp[2] = cmd->tag;
+ len = 3;
+ } else
+ len = 1;
+#else
+ len = 1;
+ cmd->tag=0;
+#endif /* SUPPORT_TAGS */
+
+ /* Send message(s) */
+ data = tmp;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO);
+ /* XXX need to handle errors here */
+ hostdata->connected = cmd;
+#ifndef SUPPORT_TAGS
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+#endif
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
+ initialize_SCp(cmd);
+
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes are transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio( struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
+ * phase specified in the appropriate bits of the TARGET COMMAND
+ * REGISTER match the STATUS REGISTER
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+ do {
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
+ */
+ while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+ HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO);
+
+ /* Check for phase mismatch */
+ if ((tmp & PHASE_MASK) != p) {
+ PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO);
+ NCR_PRINT_PHASE(NDEBUG_PIO);
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to / from memory */
+ if (!(p & SR_IO))
+ NCR5380_write(OUTPUT_DATA_REG, *d);
+ else
+ *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+ ++d;
+
+ /*
+ * The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ */
+
+ if (!(p & SR_IO)) {
+ if (!((p & SR_MSG) && c > 1)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA);
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ } else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ }
+ } else {
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ }
+
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+ HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO);
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking :
+ * 1. We were in MSGOUT phase, and we are on the last byte of the
+ * message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in a MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear and the target may proceed as normal.
+ */
+ if (!(p == PHASE_MSGIN && c == 1)) {
+ if (p == PHASE_MSGOUT && c > 1)
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ else
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+ } while (--c);
+
+ PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c);
+
+ *count = c;
+ *data = d;
+ tmp = NCR5380_read(STATUS_REG);
+ /* The phase read from the bus is valid if either REQ is (already)
+ * asserted or if ACK hasn't been released yet. The latter is the case if
+ * we're in MSGIN and all wanted bytes have been received. */
+ if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
+ *phase = tmp & PHASE_MASK;
+ else
+ *phase = PHASE_UNKNOWN;
+
+ if (!c || (*phase == p))
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ *
+ * Purpose : abort the currently established nexus. Should only be
+ * called from a routine which can drop into a
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host)
+{
+ unsigned char tmp, *msgptr, phase;
+ int len;
+
+ /* Request message out phase */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
+ * other phase and will have to source/sink data.
+ *
+ * We really don't care what value was on the bus or what value
+ * the target sees, so we just handshake.
+ */
+
+ while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ }
+
+ tmp = ABORT;
+ msgptr = &tmp;
+ len = 1;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+ /*
+ * If we got here, and the command completed successfully,
+ * we're about to go into bus free state.
+ */
+
+ return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ * or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma( struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ SETUP_HOSTDATA(instance);
+ register int c = *count;
+ register unsigned char p = *phase;
+ unsigned long flags;
+
+ /* sanity check */
+ if(!sun3_dma_setup_done) {
+ printk("scsi%d: transfer_dma without setup!\n", HOSTNO);
+ BUG();
+ }
+ hostdata->dma_len = c;
+
+ DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
+ HOSTNO, (p & SR_IO) ? "reading" : "writing",
+ c, (p & SR_IO) ? "to" : "from", *data);
+
+ /* netbsd turns off ints here, why not be safe and do it too */
+ local_irq_save(flags);
+
+ /* send start chain */
+ sun3scsi_dma_start(c, *data);
+
+ if (p & SR_IO) {
+ NCR5380_write(TARGET_COMMAND_REG, 1);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(INITIATOR_COMMAND_REG, 0);
+ NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ } else {
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA);
+ NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+ NCR5380_write(START_DMA_SEND_REG, 0);
+ }
+
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+
+ local_irq_restore(flags);
+
+ sun3_dma_active = 1;
+ return 0;
+}
+#endif /* defined(REAL_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here
+ * to recover from an unexpected bus free condition.
+ */
+
+static void NCR5380_information_transfer (struct Scsi_Host *instance)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned long flags;
+ unsigned char msgout = NOP;
+ int sink = 0;
+ int len;
+#if defined(REAL_DMA)
+ int transfersize;
+#endif
+ unsigned char *data;
+ unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+ Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
+
+ while (1) {
+ tmp = NCR5380_read(STATUS_REG);
+ /* We only have a valid SCSI phase when REQ is asserted */
+ if (tmp & SR_REQ) {
+ phase = (tmp & PHASE_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+ NCR_PRINT_PHASE(NDEBUG_INFORMATION);
+ }
+
+ if(phase == PHASE_CMDOUT) {
+ void *d;
+ unsigned long count;
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ count = cmd->SCp.buffer->length;
+ d = SGADDR(cmd->SCp.buffer);
+ } else {
+ count = cmd->SCp.this_residual;
+ d = cmd->SCp.ptr;
+ }
+#ifdef REAL_DMA
+ /* this command setup for dma yet? */
+ if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
+ != cmd))
+ {
+ if(cmd->request->flags & REQ_CMD) {
+ sun3scsi_dma_setup(d, count,
+ rq_data_dir(cmd->request));
+ sun3_dma_setup_done = cmd;
+ }
+ }
+#endif
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
+ }
+
+
+ if (sink && (phase != PHASE_MSGOUT)) {
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 0;
+ continue;
+ }
+
+ switch (phase) {
+ case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+ printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
+ "aborted\n", HOSTNO);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ return;
+#endif
+ case PHASE_DATAIN:
+ /*
+ * If there is no room left in the current buffer in the
+ * scatter-gather list, move onto the next one.
+ */
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = SGADDR(cmd->SCp.buffer);
+
+ /* ++roman: Try to merge some scatter-buffers if
+ * they are at contiguous physical addresses.
+ */
+// merge_contiguous_buffers( cmd );
+ INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
+ HOSTNO, cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual);
+ }
+
+ /*
+ * The preferred transfer method is going to be
+ * PSEUDO-DMA for systems that are strictly PIO,
+ * since we can let the hardware do the handshaking.
+ *
+ * For this to work, we need to know the transfersize
+ * ahead of time, since the pseudo-DMA code will sit
+ * in an unconditional loop.
+ */
+
+/* ++roman: I suggest, this should be
+ * #if def(REAL_DMA)
+ * instead of leaving REAL_DMA out.
+ */
+
+#if defined(REAL_DMA)
+// if (!cmd->device->borken &&
+ if((transfersize =
+ NCR5380_dma_xfer_len(instance,cmd,phase)) > SUN3_DMA_MINSIZE) {
+ len = transfersize;
+ cmd->SCp.phase = phase;
+
+ if (NCR5380_transfer_dma(instance, &phase,
+ &len, (unsigned char **) &cmd->SCp.ptr)) {
+ /*
+ * If the watchdog timer fires, all future
+ * accesses to this device will use the
+ * polled-IO. */
+ printk(KERN_NOTICE "scsi%d: switching target %d "
+ "lun %d to slow handshake\n", HOSTNO,
+ cmd->device->id, cmd->device->lun);
+ cmd->device->borken = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ /* XXX - need to source or sink data here, as appropriate */
+ } else {
+#ifdef REAL_DMA
+ /* ++roman: When using real DMA,
+ * information_transfer() should return after
+ * starting DMA since it has nothing more to
+ * do.
+ */
+ return;
+#else
+ cmd->SCp.this_residual -= transfersize - len;
+#endif
+ }
+ } else
+#endif /* defined(REAL_DMA) */
+ NCR5380_transfer_pio(instance, &phase,
+ (int *) &cmd->SCp.this_residual, (unsigned char **)
+ &cmd->SCp.ptr);
+#ifdef REAL_DMA
+ /* if we had intended to dma that command clear it */
+ if(sun3_dma_setup_done == cmd)
+ sun3_dma_setup_done = NULL;
+#endif
+
+ break;
+ case PHASE_MSGIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Message = tmp;
+
+ switch (tmp) {
+ /*
+ * Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ *
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands.
+ */
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ LNK_PRINTK("scsi%d: target %d lun %d linked command "
+ "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Sanity check : A linked command should only terminate
+ * with one of these messages if there are more linked
+ * commands available.
+ */
+
+ if (!cmd->next_link) {
+ printk(KERN_NOTICE "scsi%d: target %d lun %d "
+ "linked command complete, no next_link\n",
+ HOSTNO, cmd->device->id, cmd->device->lun);
+ sink = 1;
+ do_abort (instance);
+ return;
+ }
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process; copy it
+ * and don't free it! */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ LNK_PRINTK("scsi%d: target %d lun %d linked request "
+ "done, calling scsi_done().\n",
+ HOSTNO, cmd->device->id, cmd->device->lun);
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+#endif /* def LINKED */
+ case ABORT:
+ case COMMAND_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ hostdata->connected = NULL;
+ QU_PRINTK("scsi%d: command for target %d, lun %d "
+ "completed\n", HOSTNO, cmd->device->id, cmd->device->lun);
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+ if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
+ /* Turn a QUEUE FULL status into BUSY, I think the
+ * mid level cannot handle QUEUE FULL :-( (The
+ * command is retried after BUSY). Also update our
+ * queue size to the number of currently issued
+ * commands now.
+ */
+ /* ++Andreas: the mid level code knows about
+ QUEUE_FULL now. */
+ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+ TAG_PRINTK("scsi%d: target %d lun %d returned "
+ "QUEUE_FULL after %d commands\n",
+ HOSTNO, cmd->device->id, cmd->device->lun,
+ ta->nr_allocated);
+ if (ta->queue_size > ta->nr_allocated)
+ ta->nr_allocated = ta->queue_size;
+ }
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+
+ /*
+ * I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ *
+ * If it was a REQUEST SENSE command, we need some way to
+ * differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the
+ * result code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed
+ */
+
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (status_byte(cmd->SCp.Status) != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+
+#ifdef AUTOSENSE
+ if ((cmd->cmnd[0] != REQUEST_SENSE) &&
+ (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
+ ASEN_PRINTK("scsi%d: performing request sense\n",
+ HOSTNO);
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+
+ cmd->use_sg = 0;
+ /* this is initialized from initialize_SCp
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ */
+ cmd->request_buffer = (char *) cmd->sense_buffer;
+ cmd->request_bufflen = sizeof(cmd->sense_buffer);
+
+ local_irq_save(flags);
+ LIST(cmd,hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ local_irq_restore(flags);
+ QU_PRINTK("scsi%d: REQUEST SENSE added to head of "
+ "issue queue\n", H_NO(cmd));
+ } else
+#endif /* def AUTOSENSE */
+ {
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+
+ return;
+ case MESSAGE_REJECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ switch (hostdata->last_message) {
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ /* The target obviously doesn't support tagged
+ * queuing, even though it announced this ability in
+ * its INQUIRY data ?!? (maybe only this LUN?) Ok,
+ * clear 'tagged_supported' and lock the LUN, since
+ * the command is treated as untagged further on.
+ */
+ cmd->device->tagged_supported = 0;
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+ cmd->tag = TAG_NONE;
+ TAG_PRINTK("scsi%d: target %d lun %d rejected "
+ "QUEUE_TAG message; tagged queuing "
+ "disabled\n",
+ HOSTNO, cmd->device->id, cmd->device->lun);
+ break;
+ }
+ break;
+ case DISCONNECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ local_irq_save(flags);
+ cmd->device->disconnect = 1;
+ LIST(cmd,hostdata->disconnected_queue);
+ NEXT(cmd) = hostdata->disconnected_queue;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = cmd;
+ local_irq_restore(flags);
+ QU_PRINTK("scsi%d: command for target %d lun %d was "
+ "moved from connected to the "
+ "disconnected_queue\n", HOSTNO,
+ cmd->device->id, cmd->device->lun);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /* Wait for bus free to avoid nasty timeouts */
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+ return;
+ /*
+ * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible.
+ */
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ break;
+ case EXTENDED_MESSAGE:
+/*
+ * Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't
+ * include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.
+ */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ /* Accept first byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO);
+
+ len = 2;
+ data = extended_msg + 1;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO,
+ (int)extended_msg[1], (int)extended_msg[2]);
+
+ if (!len && extended_msg[1] <=
+ (sizeof (extended_msg) - 1)) {
+ /* Accept third byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = extended_msg[1] - 1;
+ data = extended_msg + 3;
+ phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ EXT_PRINTK("scsi%d: message received, residual %d\n",
+ HOSTNO, len);
+
+ switch (extended_msg[2]) {
+ case EXTENDED_SDTR:
+ case EXTENDED_WDTR:
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ tmp = 0;
+ }
+ } else if (len) {
+ printk(KERN_NOTICE "scsi%d: error receiving "
+ "extended message\n", HOSTNO);
+ tmp = 0;
+ } else {
+ printk(KERN_NOTICE "scsi%d: extended message "
+ "code %02x length %d is too long\n",
+ HOSTNO, extended_msg[2], extended_msg[1]);
+ tmp = 0;
+ }
+ /* Fall through to reject message */
+
+ /*
+ * If we get something weird that we aren't expecting,
+ * reject it.
+ */
+ default:
+ if (!tmp) {
+ printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
+ print_msg (extended_msg);
+ printk("\n");
+ } else if (tmp != EXTENDED_MESSAGE)
+ printk(KERN_DEBUG "scsi%d: rejecting unknown "
+ "message %02x from target %d, lun %d\n",
+ HOSTNO, tmp, cmd->device->id, cmd->device->lun);
+ else
+ printk(KERN_DEBUG "scsi%d: rejecting unknown "
+ "extended message "
+ "code %02x, length %d from target %d, lun %d\n",
+ HOSTNO, extended_msg[1], extended_msg[0],
+ cmd->device->id, cmd->device->lun);
+
+
+ msgout = MESSAGE_REJECT;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ break;
+ } /* switch (tmp) */
+ break;
+ case PHASE_MSGOUT:
+ len = 1;
+ data = &msgout;
+ hostdata->last_message = msgout;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (msgout == ABORT) {
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ hostdata->connected = NULL;
+ cmd->result = DID_ERROR << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return;
+ }
+ msgout = NOP;
+ break;
+ case PHASE_CMDOUT:
+ len = cmd->cmd_len;
+ data = cmd->cmnd;
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
+ */
+ NCR5380_transfer_pio(instance, &phase, &len,
+ &data);
+ break;
+ case PHASE_STATIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Status = tmp;
+ break;
+ default:
+ printk("scsi%d: unknown phase\n", HOSTNO);
+ NCR_PRINT(NDEBUG_ANY);
+ } /* switch(phase) */
+ } /* if (tmp * SR_REQ) */
+ } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+/* it might eventually prove necessary to do a dma setup on
+ reselection, but it doesn't seem to be needed now -- sam */
+
+static void NCR5380_reselect (struct Scsi_Host *instance)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned char target_mask;
+ unsigned char lun;
+#ifdef SUPPORT_TAGS
+ unsigned char tag;
+#endif
+ unsigned char msg[3];
+ Scsi_Cmnd *tmp = NULL, *prev;
+/* unsigned long flags; */
+
+ /*
+ * Disable arbitration, etc. since the host adapter obviously
+ * lost, and tell an interrupted NCR5380_select() to restart.
+ */
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ hostdata->restart_select = 1;
+
+ target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+ RSL_PRINTK("scsi%d: reselect\n", HOSTNO);
+
+ /*
+ * At this point, we have detected that our SCSI ID is on the bus,
+ * SEL is true and BSY was false for at least one bus settle delay
+ * (400 ns).
+ *
+ * We must assert BSY ourselves, until the target drops the SEL
+ * signal.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+
+ while (NCR5380_read(STATUS_REG) & SR_SEL);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * Wait for target to go into MSGIN.
+ */
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+#if 1
+ // acknowledge toggle to MSGIN
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN));
+
+ // peek at the byte without really hitting the bus
+ msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG);
+#endif
+
+ if (!(msg[0] & 0x80)) {
+ printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
+ print_msg(msg);
+ do_abort(instance);
+ return;
+ }
+ lun = (msg[0] & 0x07);
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = NEXT(tmp) ) {
+ if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
+#ifdef SUPPORT_TAGS
+ && (tag == tmp->tag)
+#endif
+ ) {
+ if (prev) {
+ REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+ NEXT(prev) = NEXT(tmp);
+ } else {
+ REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
+ hostdata->disconnected_queue = NEXT(tmp);
+ }
+ NEXT(tmp) = NULL;
+ break;
+ }
+ }
+
+ if (!tmp) {
+ printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
+#ifdef SUPPORT_TAGS
+ "tag %d "
+#endif
+ "not in disconnected_queue.\n",
+ HOSTNO, target_mask, lun
+#ifdef SUPPORT_TAGS
+ , tag
+#endif
+ );
+ /*
+ * Since we have an established nexus that we can't do anything
+ * with, we must abort it.
+ */
+ do_abort(instance);
+ return;
+ }
+#if 1
+ /* engage dma setup for the command we just saw */
+ {
+ void *d;
+ unsigned long count;
+
+ if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) {
+ count = tmp->SCp.buffer->length;
+ d = SGADDR(tmp->SCp.buffer);
+ } else {
+ count = tmp->SCp.this_residual;
+ d = tmp->SCp.ptr;
+ }
+#ifdef REAL_DMA
+ /* setup this command for dma if not already */
+ if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done != tmp))
+ {
+ sun3scsi_dma_setup(d, count, rq_data_dir(tmp->request));
+ sun3_dma_setup_done = tmp;
+ }
+#endif
+ }
+#endif
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#ifdef SUPPORT_TAGS
+ /* If the phase is still MSGIN, the target wants to send some more
+ * messages. In case it supports tagged queuing, this is probably a
+ * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
+ */
+ tag = TAG_NONE;
+ if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
+ /* Accept previous IDENTIFY message by clearing ACK */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ len = 2;
+ data = msg+1;
+ if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
+ msg[1] == SIMPLE_QUEUE_TAG)
+ tag = msg[2];
+ TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at "
+ "reselection\n", HOSTNO, target_mask, lun, tag);
+ }
+#endif
+
+ hostdata->connected = tmp;
+ RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+ HOSTNO, tmp->target, tmp->lun, tmp->tag);
+}
+
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
+ * used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
+ * a problem, we could implement longjmp() / setjmp(), setjmp()
+ * called where the loop started in NCR5380_main().
+ */
+
+static int NCR5380_abort (Scsi_Cmnd *cmd)
+{
+ struct Scsi_Host *instance = cmd->device->host;
+ SETUP_HOSTDATA(instance);
+ Scsi_Cmnd *tmp, **prev;
+ unsigned long flags;
+
+ printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ local_irq_save(flags);
+
+ ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
+ NCR5380_read(BUS_AND_STATUS_REG),
+ NCR5380_read(STATUS_REG));
+
+#if 1
+/*
+ * Case 1 : If the command is the currently executing command,
+ * we'll set the aborted flag and return control so that
+ * information transfer routine can exit cleanly.
+ */
+
+ if (hostdata->connected == cmd) {
+
+ ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
+/*
+ * Since we can't change phases until we've completed the current
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/*
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */
+
+ if (do_abort(instance) == 0) {
+ hostdata->aborted = 1;
+ hostdata->connected = NULL;
+ cmd->result = DID_ABORT << 16;
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ local_irq_restore(flags);
+ cmd->scsi_done(cmd);
+ return SCSI_ABORT_SUCCESS;
+ } else {
+/* local_irq_restore(flags); */
+ printk("scsi%d: abort of connected command failed!\n", HOSTNO);
+ return SCSI_ABORT_ERROR;
+ }
+ }
+#endif
+
+/*
+ * Case 2 : If the command hasn't been issued yet, we simply remove it
+ * from the issue queue.
+ */
+ for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, NEXT(tmp));
+ (*prev) = NEXT(tmp);
+ NEXT(tmp) = NULL;
+ tmp->result = DID_ABORT << 16;
+ local_irq_restore(flags);
+ ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n",
+ HOSTNO);
+ /* Tagged queuing note: no tag to free here, hasn't been assigned
+ * yet... */
+ tmp->scsi_done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected) {
+ local_irq_restore(flags);
+ ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO);
+ return SCSI_ABORT_SNOOZE;
+ }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select(). The easiest way to implement this
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that
+ * device reselected.
+ *
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+ tmp = NEXT(tmp))
+ if (cmd == tmp) {
+ local_irq_restore(flags);
+ ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO);
+
+ if (NCR5380_select (instance, cmd, (int) cmd->tag))
+ return SCSI_ABORT_BUSY;
+
+ ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO);
+
+ do_abort (instance);
+
+ local_irq_save(flags);
+ for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
+ tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+ tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, NEXT(tmp));
+ *prev = NEXT(tmp);
+ NEXT(tmp) = NULL;
+ tmp->result = DID_ABORT << 16;
+ /* We must unlock the tag/LUN immediately here, since the
+ * target goes to BUS FREE and doesn't send us another
+ * message (COMMAND_COMPLETE or the like)
+ */
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( tmp );
+#else
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+#endif
+ local_irq_restore(flags);
+ tmp->scsi_done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ local_irq_restore(flags);
+ printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n"
+ KERN_INFO " before abortion\n", HOSTNO);
+
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/*
+ * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */
+
+static int NCR5380_bus_reset( Scsi_Cmnd *cmd)
+{
+ SETUP_HOSTDATA(cmd->device->host);
+ int i;
+ unsigned long flags;
+#if 1
+ Scsi_Cmnd *connected, *disconnected_queue;
+#endif
+
+
+ NCR5380_print_status (cmd->device->host);
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+ udelay (40);
+ /* reset NCR registers */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_write( MODE_REG, MR_BASE );
+ NCR5380_write( TARGET_COMMAND_REG, 0 );
+ NCR5380_write( SELECT_ENABLE_REG, 0 );
+ /* ++roman: reset interrupt condition! otherwise no interrupts don't get
+ * through anymore ... */
+ (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */
+ /* XXX see below XXX */
+
+ /* MSch: old-style reset: actually abort all command processing here */
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; to avoid problems with re-inserting the commands
+ * into the issue_queue (via scsi_done()), the aborted commands are
+ * remembered in local variables first.
+ */
+ local_irq_save(flags);
+ connected = (Scsi_Cmnd *)hostdata->connected;
+ hostdata->connected = NULL;
+ disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
+ hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+ free_all_tags();
+#endif
+ for( i = 0; i < 8; ++i )
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dma_len = 0;
+#endif
+ local_irq_restore(flags);
+
+ /* In order to tell the mid-level code which commands were aborted,
+ * set the command status to DID_RESET and call scsi_done() !!!
+ * This ultimately aborts processing of these commands in the mid-level.
+ */
+
+ if ((cmd = connected)) {
+ ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+ cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+ cmd->scsi_done( cmd );
+ }
+
+ for (i = 0; (cmd = disconnected_queue); ++i) {
+ disconnected_queue = NEXT(cmd);
+ NEXT(cmd) = NULL;
+ cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+ cmd->scsi_done( cmd );
+ }
+ if (i > 0)
+ ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i);
+
+
+ /* since all commands have been explicitly terminated, we need to tell
+ * the midlevel code that the reset was SUCCESSFUL, and there is no
+ * need to 'wake up' the commands by a request_sense
+ */
+ return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+#else /* 1 */
+
+ /* MSch: new-style reset handling: let the mid-level do what it can */
+
+ /* ++guenther: MID-LEVEL IS STILL BROKEN.
+ * Mid-level is supposed to requeue all commands that were active on the
+ * various low-level queues. In fact it does this, but that's not enough
+ * because all these commands are subject to timeout. And if a timeout
+ * happens for any removed command, *_abort() is called but all queues
+ * are now empty. Abort then gives up the falcon lock, which is fatal,
+ * since the mid-level will queue more commands and must have the lock
+ * (it's all happening inside timer interrupt handler!!).
+ * Even worse, abort will return NOT_RUNNING for all those commands not
+ * on any queue, so they won't be retried ...
+ *
+ * Conclusion: either scsi.c disables timeout for all resetted commands
+ * immediately, or we lose! As of linux-2.0.20 it doesn't.
+ */
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; so clear the low-level status here to avoid
+ * conflicts when the mid-level code tries to wake up the affected
+ * commands!
+ */
+
+ if (hostdata->issue_queue)
+ ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
+ if (hostdata->connected)
+ ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+ if (hostdata->disconnected_queue)
+ ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
+
+ local_irq_save(flags);
+ hostdata->issue_queue = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+ free_all_tags();
+#endif
+ for( i = 0; i < 8; ++i )
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dma_len = 0;
+#endif
+ local_irq_restore(flags);
+
+ /* we did no complete reset of all commands, so a wakeup is required */
+ return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET;
+#endif /* 1 */
+}
+
+/* Local Variables: */
+/* tab-width: 8 */
+/* End: */
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
new file mode 100644
index 000000000000..e3ea99f23d60
--- /dev/null
+++ b/drivers/scsi/sun3_scsi.c
@@ -0,0 +1,642 @@
+/*
+ * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
+ *
+ * Sun3 DMA routines added by Sam Creasey (sammy@sammy.net)
+ *
+ * Adapted from mac_scsinew.c:
+ */
+/*
+ * Generic Macintosh NCR5380 driver
+ *
+ * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
+ *
+ * derived in part from:
+ */
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+
+/*
+ * This is from mac_scsi.h, but hey, maybe this is useful for Sun3 too! :)
+ *
+ * Options :
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ */
+
+/*
+ * $Log: sun3_NCR5380.c,v $
+ */
+
+#define AUTOSENSE
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <asm/sun3ints.h>
+#include <asm/dvma.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+
+/* dma on! */
+#define REAL_DMA
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "sun3_scsi.h"
+#include "NCR5380.h"
+
+static void NCR5380_print(struct Scsi_Host *instance);
+
+/* #define OLDDMA */
+
+#define USE_WRAPPER
+/*#define RESET_BOOT */
+#define DRIVER_SETUP
+
+#define NDEBUG 0
+
+/*
+ * BUG can be used to trigger a strange code-size related hang on 2.1 kernels
+ */
+#ifdef BUG
+#undef RESET_BOOT
+#undef DRIVER_SETUP
+#endif
+
+/* #define SUPPORT_TAGS */
+
+#define ENABLE_IRQ() enable_irq( IRQ_SUN3_SCSI );
+
+
+static irqreturn_t scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp);
+static inline unsigned char sun3scsi_read(int reg);
+static inline void sun3scsi_write(int reg, int value);
+
+static int setup_can_queue = -1;
+module_param(setup_can_queue, int, 0);
+static int setup_cmd_per_lun = -1;
+module_param(setup_cmd_per_lun, int, 0);
+static int setup_sg_tablesize = -1;
+module_param(setup_sg_tablesize, int, 0);
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+module_param(setup_use_tagged_queuing, int, 0);
+#endif
+static int setup_hostid = -1;
+module_param(setup_hostid, int, 0);
+
+static Scsi_Cmnd *sun3_dma_setup_done = NULL;
+
+#define AFTER_RESET_DELAY (HZ/2)
+
+/* ms to wait after hitting dma regs */
+#define SUN3_DMA_DELAY 10
+
+/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
+#define SUN3_DVMA_BUFSIZE 0xe000
+
+/* minimum number of bytes to do dma on */
+#define SUN3_DMA_MINSIZE 128
+
+static volatile unsigned char *sun3_scsi_regp;
+static volatile struct sun3_dma_regs *dregs;
+#ifdef OLDDMA
+static unsigned char *dmabuf = NULL; /* dma memory buffer */
+#endif
+static struct sun3_udc_regs *udc_regs = NULL;
+static unsigned char *sun3_dma_orig_addr = NULL;
+static unsigned long sun3_dma_orig_count = 0;
+static int sun3_dma_active = 0;
+static unsigned long last_residual = 0;
+
+/*
+ * NCR 5380 register access functions
+ */
+
+static inline unsigned char sun3scsi_read(int reg)
+{
+ return( sun3_scsi_regp[reg] );
+}
+
+static inline void sun3scsi_write(int reg, int value)
+{
+ sun3_scsi_regp[reg] = value;
+}
+
+/* dma controller register access functions */
+
+static inline unsigned short sun3_udc_read(unsigned char reg)
+{
+ unsigned short ret;
+
+ dregs->udc_addr = UDC_CSR;
+ udelay(SUN3_DMA_DELAY);
+ ret = dregs->udc_data;
+ udelay(SUN3_DMA_DELAY);
+
+ return ret;
+}
+
+static inline void sun3_udc_write(unsigned short val, unsigned char reg)
+{
+ dregs->udc_addr = reg;
+ udelay(SUN3_DMA_DELAY);
+ dregs->udc_data = val;
+ udelay(SUN3_DMA_DELAY);
+}
+
+/*
+ * XXX: status debug
+ */
+static struct Scsi_Host *default_instance;
+
+/*
+ * Function : int sun3scsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes mac NCR5380 driver based on the
+ * command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int sun3scsi_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned long ioaddr;
+ static int called = 0;
+ struct Scsi_Host *instance;
+
+ /* check that this machine has an onboard 5380 */
+ switch(idprom->id_machtype) {
+ case SM_SUN3|SM_3_50:
+ case SM_SUN3|SM_3_60:
+ break;
+
+ default:
+ return 0;
+ }
+
+ if(called)
+ return 0;
+
+ tpnt->proc_name = "Sun3 5380 SCSI";
+
+ /* setup variables */
+ tpnt->can_queue =
+ (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
+ tpnt->cmd_per_lun =
+ (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
+ tpnt->sg_tablesize =
+ (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
+
+ if (setup_hostid >= 0)
+ tpnt->this_id = setup_hostid;
+ else {
+ /* use 7 as default */
+ tpnt->this_id = 7;
+ }
+
+ ioaddr = (unsigned long)ioremap(IOBASE_SUN3_SCSI, PAGE_SIZE);
+ sun3_scsi_regp = (unsigned char *)ioaddr;
+
+ dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
+
+ if((udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs)))
+ == NULL) {
+ printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
+ return 0;
+ }
+#ifdef OLDDMA
+ if((dmabuf = dvma_malloc_align(SUN3_DVMA_BUFSIZE, 0x10000)) == NULL) {
+ printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
+ return 0;
+ }
+#endif
+#ifdef SUPPORT_TAGS
+ if (setup_use_tagged_queuing < 0)
+ setup_use_tagged_queuing = USE_TAGGED_QUEUING;
+#endif
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ if(instance == NULL)
+ return 0;
+
+ default_instance = instance;
+
+ instance->io_port = (unsigned long) ioaddr;
+ instance->irq = IRQ_SUN3_SCSI;
+
+ NCR5380_init(instance, 0);
+
+ instance->n_io_port = 32;
+
+ ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+
+ if (request_irq(instance->irq, scsi_sun3_intr,
+ 0, "Sun3SCSI-5380", NULL)) {
+#ifndef REAL_DMA
+ printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+#else
+ printk("scsi%d: IRQ%d not free, bailing out\n",
+ instance->host_no, instance->irq);
+ return 0;
+#endif
+ }
+
+ printk("scsi%d: Sun3 5380 at port %lX irq", instance->host_no, instance->io_port);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk ("s disabled");
+ else
+ printk (" %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ instance->can_queue, instance->cmd_per_lun,
+ SUN3SCSI_PUBLIC_RELEASE);
+ printk("\nscsi%d:", instance->host_no);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ dregs->csr = 0;
+ udelay(SUN3_DMA_DELAY);
+ dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
+ udelay(SUN3_DMA_DELAY);
+ dregs->fifo_count = 0;
+
+ called = 1;
+
+#ifdef RESET_BOOT
+ sun3_scsi_reset_boot(instance);
+#endif
+
+ return 1;
+}
+
+int sun3scsi_release (struct Scsi_Host *shpnt)
+{
+ if (shpnt->irq != SCSI_IRQ_NONE)
+ free_irq (shpnt->irq, NULL);
+
+ iounmap((void *)sun3_scsi_regp);
+
+ return 0;
+}
+
+#ifdef RESET_BOOT
+/*
+ * Our 'bus reset on boot' function
+ */
+
+static void sun3_scsi_reset_boot(struct Scsi_Host *instance)
+{
+ unsigned long end;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ /*
+ * Do a SCSI reset to clean up the bus during initialization. No
+ * messing with the queues, interrupts, or locks necessary here.
+ */
+
+ printk( "Sun3 SCSI: resetting the SCSI bus..." );
+
+ /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
+// sun3_disable_irq( IRQ_SUN3_SCSI );
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+
+ /* The min. reset hold time is 25us, so 40us should be enough */
+ udelay( 50 );
+
+ /* reset RST and interrupt */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
+ barrier();
+
+ /* switch on SCSI IRQ again */
+// sun3_enable_irq( IRQ_SUN3_SCSI );
+
+ printk( " done\n" );
+}
+#endif
+
+const char * sun3scsi_info (struct Scsi_Host *spnt) {
+ return "";
+}
+
+// safe bits for the CSR
+#define CSR_GOOD 0x060f
+
+static irqreturn_t scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned short csr = dregs->csr;
+ int handled = 0;
+
+ if(csr & ~CSR_GOOD) {
+ if(csr & CSR_DMA_BUSERR) {
+ printk("scsi%d: bus error in dma\n", default_instance->host_no);
+ }
+
+ if(csr & CSR_DMA_CONFLICT) {
+ printk("scsi%d: dma conflict\n", default_instance->host_no);
+ }
+ handled = 1;
+ }
+
+ if(csr & (CSR_SDB_INT | CSR_DMA_INT)) {
+ NCR5380_intr(irq, dummy, fp);
+ handled = 1;
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * Debug stuff - to be called on NMI, or sysrq key. Use at your own risk;
+ * reentering NCR5380_print_status seems to have ugly side effects
+ */
+
+/* this doesn't seem to get used at all -- sam */
+#if 0
+void sun3_sun3_debug (void)
+{
+ unsigned long flags;
+ NCR5380_local_declare();
+
+ if (default_instance) {
+ local_irq_save(flags);
+ NCR5380_print_status(default_instance);
+ local_irq_restore(flags);
+ }
+}
+#endif
+
+
+/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */
+static unsigned long sun3scsi_dma_setup(void *data, unsigned long count, int write_flag)
+{
+#ifdef OLDDMA
+ if(write_flag)
+ memcpy(dmabuf, data, count);
+ else {
+ sun3_dma_orig_addr = data;
+ sun3_dma_orig_count = count;
+ }
+#else
+ void *addr;
+
+ if(sun3_dma_orig_addr != NULL)
+ dvma_unmap(sun3_dma_orig_addr);
+
+// addr = sun3_dvma_page((unsigned long)data, (unsigned long)dmabuf);
+ addr = (void *)dvma_map((unsigned long) data, count);
+
+ sun3_dma_orig_addr = addr;
+ sun3_dma_orig_count = count;
+#endif
+ dregs->fifo_count = 0;
+ sun3_udc_write(UDC_RESET, UDC_CSR);
+
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+
+ /* set direction */
+ if(write_flag)
+ dregs->csr |= CSR_SEND;
+ else
+ dregs->csr &= ~CSR_SEND;
+
+ /* byte count for fifo */
+ dregs->fifo_count = count;
+
+ sun3_udc_write(UDC_RESET, UDC_CSR);
+
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+
+ if(dregs->fifo_count != count) {
+ printk("scsi%d: fifo_mismatch %04x not %04x\n",
+ default_instance->host_no, dregs->fifo_count,
+ (unsigned int) count);
+ NCR5380_print(default_instance);
+ }
+
+ /* setup udc */
+#ifdef OLDDMA
+ udc_regs->addr_hi = ((dvma_vtob(dmabuf) & 0xff0000) >> 8);
+ udc_regs->addr_lo = (dvma_vtob(dmabuf) & 0xffff);
+#else
+ udc_regs->addr_hi = (((unsigned long)(addr) & 0xff0000) >> 8);
+ udc_regs->addr_lo = ((unsigned long)(addr) & 0xffff);
+#endif
+ udc_regs->count = count/2; /* count in words */
+ udc_regs->mode_hi = UDC_MODE_HIWORD;
+ if(write_flag) {
+ if(count & 1)
+ udc_regs->count++;
+ udc_regs->mode_lo = UDC_MODE_LSEND;
+ udc_regs->rsel = UDC_RSEL_SEND;
+ } else {
+ udc_regs->mode_lo = UDC_MODE_LRECV;
+ udc_regs->rsel = UDC_RSEL_RECV;
+ }
+
+ /* announce location of regs block */
+ sun3_udc_write(((dvma_vtob(udc_regs) & 0xff0000) >> 8),
+ UDC_CHN_HI);
+
+ sun3_udc_write((dvma_vtob(udc_regs) & 0xffff), UDC_CHN_LO);
+
+ /* set dma master on */
+ sun3_udc_write(0xd, UDC_MODE);
+
+ /* interrupt enable */
+ sun3_udc_write(UDC_INT_ENABLE, UDC_CSR);
+
+ return count;
+
+}
+
+static inline unsigned long sun3scsi_dma_count(struct Scsi_Host *instance)
+{
+ unsigned short resid;
+
+ dregs->udc_addr = 0x32;
+ udelay(SUN3_DMA_DELAY);
+ resid = dregs->udc_data;
+ udelay(SUN3_DMA_DELAY);
+ resid *= 2;
+
+ return (unsigned long) resid;
+}
+
+static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
+{
+ return last_residual;
+}
+
+static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
+ int write_flag)
+{
+ if(cmd->request->flags & REQ_CMD)
+ return wanted;
+ else
+ return 0;
+}
+
+static inline int sun3scsi_dma_start(unsigned long count, unsigned char *data)
+{
+
+ sun3_udc_write(UDC_CHN_START, UDC_CSR);
+
+ return 0;
+}
+
+/* clean up after our dma is done */
+static int sun3scsi_dma_finish(int write_flag)
+{
+ unsigned short count;
+ unsigned short fifo;
+ int ret = 0;
+
+ sun3_dma_active = 0;
+#if 1
+ // check to empty the fifo on a read
+ if(!write_flag) {
+ int tmo = 20000; /* .2 sec */
+
+ while(1) {
+ if(dregs->csr & CSR_FIFO_EMPTY)
+ break;
+
+ if(--tmo <= 0) {
+ printk("sun3scsi: fifo failed to empty!\n");
+ return 1;
+ }
+ udelay(10);
+ }
+ }
+
+#endif
+
+ count = sun3scsi_dma_count(default_instance);
+#ifdef OLDDMA
+
+ /* if we've finished a read, copy out the data we read */
+ if(sun3_dma_orig_addr) {
+ /* check for residual bytes after dma end */
+ if(count && (NCR5380_read(BUS_AND_STATUS_REG) &
+ (BASR_PHASE_MATCH | BASR_ACK))) {
+ printk("scsi%d: sun3_scsi_finish: read overrun baby... ", default_instance->host_no);
+ printk("basr now %02x\n", NCR5380_read(BUS_AND_STATUS_REG));
+ ret = count;
+ }
+
+ /* copy in what we dma'd no matter what */
+ memcpy(sun3_dma_orig_addr, dmabuf, sun3_dma_orig_count);
+ sun3_dma_orig_addr = NULL;
+
+ }
+#else
+
+ fifo = dregs->fifo_count;
+ last_residual = fifo;
+
+ /* empty bytes from the fifo which didn't make it */
+ if((!write_flag) && (count - fifo) == 2) {
+ unsigned short data;
+ unsigned char *vaddr;
+
+ data = dregs->fifo_data;
+ vaddr = (unsigned char *)dvma_btov(sun3_dma_orig_addr);
+
+ vaddr += (sun3_dma_orig_count - fifo);
+
+ vaddr[-2] = (data & 0xff00) >> 8;
+ vaddr[-1] = (data & 0xff);
+ }
+
+ dvma_unmap(sun3_dma_orig_addr);
+ sun3_dma_orig_addr = NULL;
+#endif
+ sun3_udc_write(UDC_RESET, UDC_CSR);
+ dregs->fifo_count = 0;
+ dregs->csr &= ~CSR_SEND;
+
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+
+ sun3_dma_setup_done = NULL;
+
+ return ret;
+
+}
+
+#include "sun3_NCR5380.c"
+
+static Scsi_Host_Template driver_template = {
+ .name = SUN3_SCSI_NAME,
+ .detect = sun3scsi_detect,
+ .release = sun3scsi_release,
+ .info = sun3scsi_info,
+ .queuecommand = sun3scsi_queue_command,
+ .eh_abort_handler = sun3scsi_abort,
+ .eh_bus_reset_handler = sun3scsi_bus_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_TABLESIZE,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/sun3_scsi.h b/drivers/scsi/sun3_scsi.h
new file mode 100644
index 000000000000..155282b92a95
--- /dev/null
+++ b/drivers/scsi/sun3_scsi.h
@@ -0,0 +1,379 @@
+/*
+ * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
+ *
+ * Sun3 DMA additions by Sam Creasey (sammy@sammy.net)
+ *
+ * Adapted from mac_scsinew.h:
+ */
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: cumana_NCR5380.h,v $
+ */
+
+#ifndef SUN3_NCR5380_H
+#define SUN3_NCR5380_H
+
+#define SUN3SCSI_PUBLIC_RELEASE 1
+
+/*
+ * Int: level 2 autovector
+ * IO: type 1, base 0x00140000, 5 bits phys space: A<4..0>
+ */
+#define IRQ_SUN3_SCSI 2
+#define IOBASE_SUN3_SCSI 0x00140000
+
+#define IOBASE_SUN3_VMESCSI 0xff200000
+
+static int sun3scsi_abort (Scsi_Cmnd *);
+static int sun3scsi_detect (Scsi_Host_Template *);
+static const char *sun3scsi_info (struct Scsi_Host *);
+static int sun3scsi_bus_reset(Scsi_Cmnd *);
+static int sun3scsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int sun3scsi_release (struct Scsi_Host *);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifndef SG_TABLESIZE
+#define SG_TABLESIZE SG_NONE
+#endif
+
+#ifndef MAX_TAGS
+#define MAX_TAGS 32
+#endif
+
+#ifndef USE_TAGGED_QUEUING
+#define USE_TAGGED_QUEUING 1
+#endif
+
+#include <scsi/scsicam.h>
+
+#ifdef SUN3_SCSI_VME
+#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI"
+#else
+#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI"
+#endif
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ int port, ctrl
+
+#define NCR5380_local_declare() \
+ struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+ _instance = instance
+
+#define NCR5380_read(reg) sun3scsi_read(reg)
+#define NCR5380_write(reg, value) sun3scsi_write(reg, value)
+
+#define NCR5380_intr sun3scsi_intr
+#define NCR5380_queue_command sun3scsi_queue_command
+#define NCR5380_bus_reset sun3scsi_bus_reset
+#define NCR5380_abort sun3scsi_abort
+#define NCR5380_proc_info sun3scsi_proc_info
+#define NCR5380_dma_xfer_len(i, cmd, phase) \
+ sun3scsi_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
+
+#define NCR5380_dma_write_setup(instance, data, count) sun3scsi_dma_setup(data, count, 1)
+#define NCR5380_dma_read_setup(instance, data, count) sun3scsi_dma_setup(data, count, 0)
+#define NCR5380_dma_residual sun3scsi_dma_residual
+
+#define BOARD_NORMAL 0
+#define BOARD_NCR53C400 1
+
+/* additional registers - mainly DMA control regs */
+/* these start at regbase + 8 -- directly after the NCR regs */
+struct sun3_dma_regs {
+ unsigned short dma_addr_hi; /* vme only */
+ unsigned short dma_addr_lo; /* vme only */
+ unsigned short dma_count_hi; /* vme only */
+ unsigned short dma_count_lo; /* vme only */
+ unsigned short udc_data; /* udc dma data reg (obio only) */
+ unsigned short udc_addr; /* uda dma addr reg (obio only) */
+ unsigned short fifo_data; /* fifo data reg, holds extra byte on
+ odd dma reads */
+ unsigned short fifo_count;
+ unsigned short csr; /* control/status reg */
+ unsigned short bpack_hi; /* vme only */
+ unsigned short bpack_lo; /* vme only */
+ unsigned short ivect; /* vme only */
+ unsigned short fifo_count_hi; /* vme only */
+};
+
+/* ucd chip specific regs - live in dvma space */
+struct sun3_udc_regs {
+ unsigned short rsel; /* select regs to load */
+ unsigned short addr_hi; /* high word of addr */
+ unsigned short addr_lo; /* low word */
+ unsigned short count; /* words to be xfer'd */
+ unsigned short mode_hi; /* high word of channel mode */
+ unsigned short mode_lo; /* low word of channel mode */
+};
+
+/* addresses of the udc registers */
+#define UDC_MODE 0x38
+#define UDC_CSR 0x2e /* command/status */
+#define UDC_CHN_HI 0x26 /* chain high word */
+#define UDC_CHN_LO 0x22 /* chain lo word */
+#define UDC_CURA_HI 0x1a /* cur reg A high */
+#define UDC_CURA_LO 0x0a /* cur reg A low */
+#define UDC_CURB_HI 0x12 /* cur reg B high */
+#define UDC_CURB_LO 0x02 /* cur reg B low */
+#define UDC_MODE_HI 0x56 /* mode reg high */
+#define UDC_MODE_LO 0x52 /* mode reg low */
+#define UDC_COUNT 0x32 /* words to xfer */
+
+/* some udc commands */
+#define UDC_RESET 0
+#define UDC_CHN_START 0xa0 /* start chain */
+#define UDC_INT_ENABLE 0x32 /* channel 1 int on */
+
+/* udc mode words */
+#define UDC_MODE_HIWORD 0x40
+#define UDC_MODE_LSEND 0xc2
+#define UDC_MODE_LRECV 0xd2
+
+/* udc reg selections */
+#define UDC_RSEL_SEND 0x282
+#define UDC_RSEL_RECV 0x182
+
+/* bits in csr reg */
+#define CSR_DMA_ACTIVE 0x8000
+#define CSR_DMA_CONFLICT 0x4000
+#define CSR_DMA_BUSERR 0x2000
+
+#define CSR_FIFO_EMPTY 0x400 /* fifo flushed? */
+#define CSR_SDB_INT 0x200 /* sbc interrupt pending */
+#define CSR_DMA_INT 0x100 /* dma interrupt pending */
+
+#define CSR_LEFT 0xc0
+#define CSR_LEFT_3 0xc0
+#define CSR_LEFT_2 0x80
+#define CSR_LEFT_1 0x40
+#define CSR_PACK_ENABLE 0x20
+
+#define CSR_DMA_ENABLE 0x10
+
+#define CSR_SEND 0x8 /* 1 = send 0 = recv */
+#define CSR_FIFO 0x2 /* reset fifo */
+#define CSR_INTR 0x4 /* interrupt enable */
+#define CSR_SCSI 0x1
+
+#define VME_DATA24 0x3d00
+
+// debugging printk's, taken from atari_scsi.h
+/* Debugging printk definitions:
+ *
+ * ARB -> arbitration
+ * ASEN -> auto-sense
+ * DMA -> DMA
+ * HSH -> PIO handshake
+ * INF -> information transfer
+ * INI -> initialization
+ * INT -> interrupt
+ * LNK -> linked commands
+ * MAIN -> NCR5380_main() control flow
+ * NDAT -> no data-out phase
+ * NWR -> no write commands
+ * PIO -> PIO transfers
+ * PDMA -> pseudo DMA (unused on Atari)
+ * QU -> queues
+ * RSL -> reselections
+ * SEL -> selections
+ * USL -> usleep cpde (unused on Atari)
+ * LBS -> last byte sent (unused on Atari)
+ * RSS -> restarting of selections
+ * EXT -> extended messages
+ * ABRT -> aborting and resetting
+ * TAG -> queue tag handling
+ * MER -> merging of consec. buffers
+ *
+ */
+
+
+
+#if NDEBUG & NDEBUG_ARBITRATION
+#define ARB_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ARB_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_AUTOSENSE
+#define ASEN_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ASEN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_DMA
+#define DMA_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define DMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_HANDSHAKE
+#define HSH_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define HSH_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INFORMATION
+#define INF_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INF_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INIT
+#define INI_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INI_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INTR
+#define INT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LINKED
+#define LNK_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define LNK_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MAIN
+#define MAIN_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define MAIN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_DATAOUT
+#define NDAT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define NDAT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_WRITE
+#define NWR_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define NWR_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PIO
+#define PIO_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define PIO_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PSEUDO_DMA
+#define PDMA_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define PDMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_QUEUES
+#define QU_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define QU_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESELECTION
+#define RSL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define RSL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_SELECTION
+#define SEL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define SEL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_USLEEP
+#define USL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define USL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LAST_BYTE_SENT
+#define LBS_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define LBS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESTART_SELECT
+#define RSS_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define RSS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_EXTENDED
+#define EXT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define EXT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_ABORT
+#define ABRT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ABRT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_TAGS
+#define TAG_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define TAG_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MERGING
+#define MER_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define MER_PRINTK(format, args...)
+#endif
+
+/* conditional macros for NCR5380_print_{,phase,status} */
+
+#define NCR_PRINT(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0)
+
+#define NCR_PRINT_PHASE(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0)
+
+#define NCR_PRINT_STATUS(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
+
+
+
+#endif /* ndef HOSTS_C */
+#endif /* SUN3_NCR5380_H */
+
diff --git a/drivers/scsi/sun3_scsi_vme.c b/drivers/scsi/sun3_scsi_vme.c
new file mode 100644
index 000000000000..9acb5ddebb07
--- /dev/null
+++ b/drivers/scsi/sun3_scsi_vme.c
@@ -0,0 +1,584 @@
+ /*
+ * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
+ *
+ * Sun3 DMA routines added by Sam Creasey (sammy@sammy.net)
+ *
+ * VME support added by Sam Creasey
+ *
+ * Adapted from sun3_scsi.c -- see there for other headers
+ *
+ * TODO: modify this driver to support multiple Sun3 SCSI VME boards
+ *
+ */
+
+#define AUTOSENSE
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <asm/sun3ints.h>
+#include <asm/dvma.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+
+#define SUN3_SCSI_VME
+
+#undef SUN3_SCSI_DEBUG
+
+/* dma on! */
+#define REAL_DMA
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "sun3_scsi.h"
+#include "NCR5380.h"
+
+extern int sun3_map_test(unsigned long, char *);
+
+#define USE_WRAPPER
+/*#define RESET_BOOT */
+#define DRIVER_SETUP
+
+#define NDEBUG 0
+
+/*
+ * BUG can be used to trigger a strange code-size related hang on 2.1 kernels
+ */
+#ifdef BUG
+#undef RESET_BOOT
+#undef DRIVER_SETUP
+#endif
+
+/* #define SUPPORT_TAGS */
+
+//#define ENABLE_IRQ() enable_irq( SUN3_VEC_VMESCSI0 );
+#define ENABLE_IRQ()
+
+
+static irqreturn_t scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp);
+static inline unsigned char sun3scsi_read(int reg);
+static inline void sun3scsi_write(int reg, int value);
+
+static int setup_can_queue = -1;
+module_param(setup_can_queue, int, 0);
+static int setup_cmd_per_lun = -1;
+module_param(setup_cmd_per_lun, int, 0);
+static int setup_sg_tablesize = -1;
+module_param(setup_sg_tablesize, int, 0);
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+module_param(setup_use_tagged_queuing, int, 0);
+#endif
+static int setup_hostid = -1;
+module_param(setup_hostid, int, 0);
+
+static Scsi_Cmnd *sun3_dma_setup_done = NULL;
+
+#define AFTER_RESET_DELAY (HZ/2)
+
+/* ms to wait after hitting dma regs */
+#define SUN3_DMA_DELAY 10
+
+/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
+#define SUN3_DVMA_BUFSIZE 0xe000
+
+/* minimum number of bytes to do dma on */
+#define SUN3_DMA_MINSIZE 128
+
+static volatile unsigned char *sun3_scsi_regp;
+static volatile struct sun3_dma_regs *dregs;
+#ifdef OLDDMA
+static unsigned char *dmabuf = NULL; /* dma memory buffer */
+#endif
+static unsigned char *sun3_dma_orig_addr = NULL;
+static unsigned long sun3_dma_orig_count = 0;
+static int sun3_dma_active = 0;
+static unsigned long last_residual = 0;
+
+/*
+ * NCR 5380 register access functions
+ */
+
+static inline unsigned char sun3scsi_read(int reg)
+{
+ return( sun3_scsi_regp[reg] );
+}
+
+static inline void sun3scsi_write(int reg, int value)
+{
+ sun3_scsi_regp[reg] = value;
+}
+
+/*
+ * XXX: status debug
+ */
+static struct Scsi_Host *default_instance;
+
+/*
+ * Function : int sun3scsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes mac NCR5380 driver based on the
+ * command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+static int sun3scsi_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned long ioaddr, irq = 0;
+ static int called = 0;
+ struct Scsi_Host *instance;
+ int i;
+ unsigned long addrs[3] = { IOBASE_SUN3_VMESCSI,
+ IOBASE_SUN3_VMESCSI + 0x4000,
+ 0 };
+ unsigned long vecs[3] = { SUN3_VEC_VMESCSI0,
+ SUN3_VEC_VMESCSI1,
+ 0 };
+ /* check that this machine has an onboard 5380 */
+ switch(idprom->id_machtype) {
+ case SM_SUN3|SM_3_160:
+ case SM_SUN3|SM_3_260:
+ break;
+
+ default:
+ return 0;
+ }
+
+ if(called)
+ return 0;
+
+ tpnt->proc_name = "Sun3 5380 VME SCSI";
+
+ /* setup variables */
+ tpnt->can_queue =
+ (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
+ tpnt->cmd_per_lun =
+ (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
+ tpnt->sg_tablesize =
+ (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
+
+ if (setup_hostid >= 0)
+ tpnt->this_id = setup_hostid;
+ else {
+ /* use 7 as default */
+ tpnt->this_id = 7;
+ }
+
+ ioaddr = 0;
+ for(i = 0; addrs[i] != 0; i++) {
+ unsigned char x;
+
+ ioaddr = (unsigned long)sun3_ioremap(addrs[i], PAGE_SIZE,
+ SUN3_PAGE_TYPE_VME16);
+ irq = vecs[i];
+ sun3_scsi_regp = (unsigned char *)ioaddr;
+
+ dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
+
+ if(sun3_map_test((unsigned long)dregs, &x)) {
+ unsigned short oldcsr;
+
+ oldcsr = dregs->csr;
+ dregs->csr = 0;
+ udelay(SUN3_DMA_DELAY);
+ if(dregs->csr == 0x1400)
+ break;
+
+ dregs->csr = oldcsr;
+ }
+
+ iounmap((void *)ioaddr);
+ ioaddr = 0;
+ }
+
+ if(!ioaddr)
+ return 0;
+
+#ifdef SUPPORT_TAGS
+ if (setup_use_tagged_queuing < 0)
+ setup_use_tagged_queuing = USE_TAGGED_QUEUING;
+#endif
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ if(instance == NULL)
+ return 0;
+
+ default_instance = instance;
+
+ instance->io_port = (unsigned long) ioaddr;
+ instance->irq = irq;
+
+ NCR5380_init(instance, 0);
+
+ instance->n_io_port = 32;
+
+ ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+
+ if (request_irq(instance->irq, scsi_sun3_intr,
+ 0, "Sun3SCSI-5380VME", NULL)) {
+#ifndef REAL_DMA
+ printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+#else
+ printk("scsi%d: IRQ%d not free, bailing out\n",
+ instance->host_no, instance->irq);
+ return 0;
+#endif
+ }
+
+ printk("scsi%d: Sun3 5380 VME at port %lX irq", instance->host_no, instance->io_port);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk ("s disabled");
+ else
+ printk (" %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ instance->can_queue, instance->cmd_per_lun,
+ SUN3SCSI_PUBLIC_RELEASE);
+ printk("\nscsi%d:", instance->host_no);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ dregs->csr = 0;
+ udelay(SUN3_DMA_DELAY);
+ dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
+ udelay(SUN3_DMA_DELAY);
+ dregs->fifo_count = 0;
+ dregs->fifo_count_hi = 0;
+ dregs->dma_addr_hi = 0;
+ dregs->dma_addr_lo = 0;
+ dregs->dma_count_hi = 0;
+ dregs->dma_count_lo = 0;
+
+ dregs->ivect = VME_DATA24 | (instance->irq & 0xff);
+
+ called = 1;
+
+#ifdef RESET_BOOT
+ sun3_scsi_reset_boot(instance);
+#endif
+
+ return 1;
+}
+
+int sun3scsi_release (struct Scsi_Host *shpnt)
+{
+ if (shpnt->irq != SCSI_IRQ_NONE)
+ free_irq (shpnt->irq, NULL);
+
+ iounmap((void *)sun3_scsi_regp);
+
+ return 0;
+}
+
+#ifdef RESET_BOOT
+/*
+ * Our 'bus reset on boot' function
+ */
+
+static void sun3_scsi_reset_boot(struct Scsi_Host *instance)
+{
+ unsigned long end;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ /*
+ * Do a SCSI reset to clean up the bus during initialization. No
+ * messing with the queues, interrupts, or locks necessary here.
+ */
+
+ printk( "Sun3 SCSI: resetting the SCSI bus..." );
+
+ /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
+// sun3_disable_irq( IRQ_SUN3_SCSI );
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+
+ /* The min. reset hold time is 25us, so 40us should be enough */
+ udelay( 50 );
+
+ /* reset RST and interrupt */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
+ barrier();
+
+ /* switch on SCSI IRQ again */
+// sun3_enable_irq( IRQ_SUN3_SCSI );
+
+ printk( " done\n" );
+}
+#endif
+
+static const char * sun3scsi_info (struct Scsi_Host *spnt) {
+ return "";
+}
+
+// safe bits for the CSR
+#define CSR_GOOD 0x060f
+
+static irqreturn_t scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned short csr = dregs->csr;
+ int handled = 0;
+
+ dregs->csr &= ~CSR_DMA_ENABLE;
+
+
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi_intr csr %x\n", csr);
+#endif
+
+ if(csr & ~CSR_GOOD) {
+ if(csr & CSR_DMA_BUSERR) {
+ printk("scsi%d: bus error in dma\n", default_instance->host_no);
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi: residual %x count %x addr %p dmaaddr %x\n",
+ dregs->fifo_count,
+ dregs->dma_count_lo | (dregs->dma_count_hi << 16),
+ sun3_dma_orig_addr,
+ dregs->dma_addr_lo | (dregs->dma_addr_hi << 16));
+#endif
+ }
+
+ if(csr & CSR_DMA_CONFLICT) {
+ printk("scsi%d: dma conflict\n", default_instance->host_no);
+ }
+ handled = 1;
+ }
+
+ if(csr & (CSR_SDB_INT | CSR_DMA_INT)) {
+ NCR5380_intr(irq, dummy, fp);
+ handled = 1;
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * Debug stuff - to be called on NMI, or sysrq key. Use at your own risk;
+ * reentering NCR5380_print_status seems to have ugly side effects
+ */
+
+/* this doesn't seem to get used at all -- sam */
+#if 0
+void sun3_sun3_debug (void)
+{
+ unsigned long flags;
+ NCR5380_local_declare();
+
+ if (default_instance) {
+ local_irq_save(flags);
+ NCR5380_print_status(default_instance);
+ local_irq_restore(flags);
+ }
+}
+#endif
+
+
+/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */
+static unsigned long sun3scsi_dma_setup(void *data, unsigned long count, int write_flag)
+{
+ void *addr;
+
+ if(sun3_dma_orig_addr != NULL)
+ dvma_unmap(sun3_dma_orig_addr);
+
+// addr = sun3_dvma_page((unsigned long)data, (unsigned long)dmabuf);
+ addr = (void *)dvma_map_vme((unsigned long) data, count);
+
+ sun3_dma_orig_addr = addr;
+ sun3_dma_orig_count = count;
+
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi: dma_setup addr %p count %x\n", addr, count);
+#endif
+
+// dregs->fifo_count = 0;
+#if 0
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+#endif
+ /* set direction */
+ if(write_flag)
+ dregs->csr |= CSR_SEND;
+ else
+ dregs->csr &= ~CSR_SEND;
+
+ /* reset fifo */
+// dregs->csr &= ~CSR_FIFO;
+// dregs->csr |= CSR_FIFO;
+
+ dregs->csr |= CSR_PACK_ENABLE;
+
+ dregs->dma_addr_hi = ((unsigned long)addr >> 16);
+ dregs->dma_addr_lo = ((unsigned long)addr & 0xffff);
+
+ dregs->dma_count_hi = 0;
+ dregs->dma_count_lo = 0;
+ dregs->fifo_count_hi = 0;
+ dregs->fifo_count = 0;
+
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi: dma_setup done csr %x\n", dregs->csr);
+#endif
+ return count;
+
+}
+
+static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
+{
+ return last_residual;
+}
+
+static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
+ int write_flag)
+{
+ if(cmd->request->flags & REQ_CMD)
+ return wanted;
+ else
+ return 0;
+}
+
+static int sun3scsi_dma_start(unsigned long count, char *data)
+{
+
+ unsigned short csr;
+
+ csr = dregs->csr;
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi: dma_start data %p count %x csr %x fifo %x\n", data, count, csr, dregs->fifo_count);
+#endif
+
+ dregs->dma_count_hi = (sun3_dma_orig_count >> 16);
+ dregs->dma_count_lo = (sun3_dma_orig_count & 0xffff);
+
+ dregs->fifo_count_hi = (sun3_dma_orig_count >> 16);
+ dregs->fifo_count = (sun3_dma_orig_count & 0xffff);
+
+// if(!(csr & CSR_DMA_ENABLE))
+// dregs->csr |= CSR_DMA_ENABLE;
+
+ return 0;
+}
+
+/* clean up after our dma is done */
+static int sun3scsi_dma_finish(int write_flag)
+{
+ unsigned short fifo;
+ int ret = 0;
+
+ sun3_dma_active = 0;
+
+ dregs->csr &= ~CSR_DMA_ENABLE;
+
+ fifo = dregs->fifo_count;
+ if(write_flag) {
+ if((fifo > 0) && (fifo < sun3_dma_orig_count))
+ fifo++;
+ }
+
+ last_residual = fifo;
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi: residual %x total %x\n", fifo, sun3_dma_orig_count);
+#endif
+ /* empty bytes from the fifo which didn't make it */
+ if((!write_flag) && (dregs->csr & CSR_LEFT)) {
+ unsigned char *vaddr;
+
+#ifdef SUN3_SCSI_DEBUG
+ printk("scsi: got left over bytes\n");
+#endif
+
+ vaddr = (unsigned char *)dvma_vmetov(sun3_dma_orig_addr);
+
+ vaddr += (sun3_dma_orig_count - fifo);
+ vaddr--;
+
+ switch(dregs->csr & CSR_LEFT) {
+ case CSR_LEFT_3:
+ *vaddr = (dregs->bpack_lo & 0xff00) >> 8;
+ vaddr--;
+
+ case CSR_LEFT_2:
+ *vaddr = (dregs->bpack_hi & 0x00ff);
+ vaddr--;
+
+ case CSR_LEFT_1:
+ *vaddr = (dregs->bpack_hi & 0xff00) >> 8;
+ break;
+ }
+
+
+ }
+
+ dvma_unmap(sun3_dma_orig_addr);
+ sun3_dma_orig_addr = NULL;
+
+ dregs->dma_addr_hi = 0;
+ dregs->dma_addr_lo = 0;
+ dregs->dma_count_hi = 0;
+ dregs->dma_count_lo = 0;
+
+ dregs->fifo_count = 0;
+ dregs->fifo_count_hi = 0;
+
+ dregs->csr &= ~CSR_SEND;
+
+// dregs->csr |= CSR_DMA_ENABLE;
+
+#if 0
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+#endif
+ sun3_dma_setup_done = NULL;
+
+ return ret;
+
+}
+
+#include "sun3_NCR5380.c"
+
+static Scsi_Host_Template driver_template = {
+ .name = SUN3_SCSI_NAME,
+ .detect = sun3scsi_detect,
+ .release = sun3scsi_release,
+ .info = sun3scsi_info,
+ .queuecommand = sun3scsi_queue_command,
+ .eh_abort_handler = sun3scsi_abort,
+ .eh_bus_reset_handler = sun3scsi_bus_reset,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_TABLESIZE,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING
+};
+
+
+#include "scsi_module.c"
+
diff --git a/drivers/scsi/sun3x_esp.c b/drivers/scsi/sun3x_esp.c
new file mode 100644
index 000000000000..5d1dc0e8ba21
--- /dev/null
+++ b/drivers/scsi/sun3x_esp.c
@@ -0,0 +1,394 @@
+/* sun3x_esp.c: EnhancedScsiProcessor Sun3x SCSI driver code.
+ *
+ * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * Based on David S. Miller's esp driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "NCR53C9x.h"
+
+#include <asm/sun3x.h>
+#include <asm/dvma.h>
+#include <asm/irq.h>
+
+extern struct NCR_ESP *espchain;
+
+static void dma_barrier(struct NCR_ESP *esp);
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count);
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_drain(struct NCR_ESP *esp);
+static void dma_invalidate(struct NCR_ESP *esp);
+static void dma_dump_state(struct NCR_ESP *esp);
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length);
+static void dma_ints_off(struct NCR_ESP *esp);
+static void dma_ints_on(struct NCR_ESP *esp);
+static int dma_irq_p(struct NCR_ESP *esp);
+static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr);
+static int dma_ports_p(struct NCR_ESP *esp);
+static void dma_reset(struct NCR_ESP *esp);
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write);
+static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp);
+static void dma_advance_sg (Scsi_Cmnd *sp);
+
+/* Detecting ESP chips on the machine. This is the simple and easy
+ * version.
+ */
+int sun3x_esp_detect(Scsi_Host_Template *tpnt)
+{
+ struct NCR_ESP *esp;
+ struct ConfigDev *esp_dev;
+
+ esp_dev = 0;
+ esp = esp_allocate(tpnt, (void *) esp_dev);
+
+ /* Do command transfer with DMA */
+ esp->do_pio_cmds = 0;
+
+ /* Required functions */
+ esp->dma_bytes_sent = &dma_bytes_sent;
+ esp->dma_can_transfer = &dma_can_transfer;
+ esp->dma_dump_state = &dma_dump_state;
+ esp->dma_init_read = &dma_init_read;
+ esp->dma_init_write = &dma_init_write;
+ esp->dma_ints_off = &dma_ints_off;
+ esp->dma_ints_on = &dma_ints_on;
+ esp->dma_irq_p = &dma_irq_p;
+ esp->dma_ports_p = &dma_ports_p;
+ esp->dma_setup = &dma_setup;
+
+ /* Optional functions */
+ esp->dma_barrier = &dma_barrier;
+ esp->dma_invalidate = &dma_invalidate;
+ esp->dma_drain = &dma_drain;
+ esp->dma_irq_entry = 0;
+ esp->dma_irq_exit = 0;
+ esp->dma_led_on = 0;
+ esp->dma_led_off = 0;
+ esp->dma_poll = &dma_poll;
+ esp->dma_reset = &dma_reset;
+
+ /* virtual DMA functions */
+ esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one;
+ esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl;
+ esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one;
+ esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl;
+ esp->dma_advance_sg = &dma_advance_sg;
+
+ /* SCSI chip speed */
+ esp->cfreq = 20000000;
+ esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE);
+ esp->dregs = (void *)SUN3X_ESP_DMA;
+
+ esp->esp_command = (volatile unsigned char *)dvma_malloc(DVMA_PAGE_SIZE);
+ esp->esp_command_dvma = dvma_vtob((unsigned long)esp->esp_command);
+
+ esp->irq = 2;
+ if (request_irq(esp->irq, esp_intr, SA_INTERRUPT,
+ "SUN3X SCSI", esp->ehost)) {
+ esp_deallocate(esp);
+ return 0;
+ }
+
+ esp->scsi_id = 7;
+ esp->diff = 0;
+
+ esp_initialize(esp);
+
+ /* for reasons beyond my knowledge (and which should likely be fixed)
+ sync mode doesn't work on a 3/80 at 5mhz. but it does at 4. */
+ esp->sync_defp = 0x3f;
+
+ printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,
+ esps_in_use);
+ esps_running = esps_in_use;
+ return esps_in_use;
+}
+
+static void dma_do_drain(struct NCR_ESP *esp)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+
+ int count = 500000;
+
+ while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0))
+ udelay(1);
+
+ if(!count) {
+ printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
+ }
+
+ dregs->cond_reg |= DMA_FIFO_STDRAIN;
+
+ count = 500000;
+
+ while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0))
+ udelay(1);
+
+ if(!count) {
+ printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
+ }
+
+}
+
+static void dma_barrier(struct NCR_ESP *esp)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+ int count = 500000;
+
+ while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0))
+ udelay(1);
+
+ if(!count) {
+ printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
+ }
+
+ dregs->cond_reg &= ~(DMA_ENABLE);
+}
+
+/* This uses various DMA csr fields and the fifo flags count value to
+ * determine how many bytes were successfully sent/received by the ESP.
+ */
+static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+
+ int rval = dregs->st_addr - esp->esp_command_dvma;
+
+ return rval - fifo_count;
+}
+
+static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ return sp->SCp.this_residual;
+}
+
+static void dma_drain(struct NCR_ESP *esp)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+ int count = 500000;
+
+ if(dregs->cond_reg & DMA_FIFO_ISDRAIN) {
+ dregs->cond_reg |= DMA_FIFO_STDRAIN;
+ while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0))
+ udelay(1);
+ if(!count) {
+ printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
+ }
+
+ }
+}
+
+static void dma_invalidate(struct NCR_ESP *esp)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+
+ __u32 tmp;
+ int count = 500000;
+
+ while(((tmp = dregs->cond_reg) & DMA_PEND_READ) && (--count > 0))
+ udelay(1);
+
+ if(!count) {
+ printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg);
+ }
+
+ dregs->cond_reg = tmp | DMA_FIFO_INV;
+ dregs->cond_reg &= ~DMA_FIFO_INV;
+
+}
+
+static void dma_dump_state(struct NCR_ESP *esp)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+
+ ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%08lx>\n",
+ esp->esp_id, dregs->cond_reg, dregs->st_addr));
+}
+
+static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+
+ dregs->st_addr = vaddress;
+ dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE);
+}
+
+static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+
+ /* Set up the DMA counters */
+
+ dregs->st_addr = vaddress;
+ dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE);
+}
+
+static void dma_ints_off(struct NCR_ESP *esp)
+{
+ DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs);
+}
+
+static void dma_ints_on(struct NCR_ESP *esp)
+{
+ DMA_INTSON((struct sparc_dma_registers *) esp->dregs);
+}
+
+static int dma_irq_p(struct NCR_ESP *esp)
+{
+ return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs);
+}
+
+static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr)
+{
+ int count = 50;
+ dma_do_drain(esp);
+
+ /* Wait till the first bits settle. */
+ while((*(volatile unsigned char *)vaddr == 0xff) && (--count > 0))
+ udelay(1);
+
+ if(!count) {
+// printk("%s:%d timeout expire (data %02x)\n", __FILE__, __LINE__,
+// esp_read(esp->eregs->esp_fdata));
+ //mach_halt();
+ vaddr[0] = esp_read(esp->eregs->esp_fdata);
+ vaddr[1] = esp_read(esp->eregs->esp_fdata);
+ }
+
+}
+
+static int dma_ports_p(struct NCR_ESP *esp)
+{
+ return (((struct sparc_dma_registers *) esp->dregs)->cond_reg
+ & DMA_INT_ENAB);
+}
+
+/* Resetting various pieces of the ESP scsi driver chipset/buses. */
+static void dma_reset(struct NCR_ESP *esp)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *)esp->dregs;
+
+ /* Punt the DVMA into a known state. */
+ dregs->cond_reg |= DMA_RST_SCSI;
+ dregs->cond_reg &= ~(DMA_RST_SCSI);
+ DMA_INTSON(dregs);
+}
+
+static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
+{
+ struct sparc_dma_registers *dregs =
+ (struct sparc_dma_registers *) esp->dregs;
+ unsigned long nreg = dregs->cond_reg;
+
+// printk("dma_setup %c addr %08x cnt %08x\n",
+// write ? 'W' : 'R', addr, count);
+
+ dma_do_drain(esp);
+
+ if(write)
+ nreg |= DMA_ST_WRITE;
+ else {
+ nreg &= ~(DMA_ST_WRITE);
+ }
+
+ nreg |= DMA_ENABLE;
+ dregs->cond_reg = nreg;
+ dregs->st_addr = addr;
+}
+
+static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ sp->SCp.have_data_in = dvma_map((unsigned long)sp->SCp.buffer,
+ sp->SCp.this_residual);
+ sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in);
+}
+
+static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ int sz = sp->SCp.buffers_residual;
+ struct scatterlist *sg = sp->SCp.buffer;
+
+ while (sz >= 0) {
+ sg[sz].dvma_address = dvma_map((unsigned long)page_address(sg[sz].page) +
+ sg[sz].offset, sg[sz].length);
+ sz--;
+ }
+ sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dvma_address);
+}
+
+static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ dvma_unmap((char *)sp->SCp.have_data_in);
+}
+
+static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+ int sz = sp->use_sg - 1;
+ struct scatterlist *sg = (struct scatterlist *)sp->buffer;
+
+ while(sz >= 0) {
+ dvma_unmap((char *)sg[sz].dvma_address);
+ sz--;
+ }
+}
+
+static void dma_advance_sg (Scsi_Cmnd *sp)
+{
+ sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address);
+}
+
+static int sun3x_esp_release(struct Scsi_Host *instance)
+{
+ /* this code does not support being compiled as a module */
+ return 1;
+
+}
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "sun3x_esp",
+ .proc_info = &esp_proc_info,
+ .name = "Sun ESP 100/100a/200",
+ .detect = sun3x_esp_detect,
+ .release = sun3x_esp_release,
+ .slave_alloc = esp_slave_alloc,
+ .slave_destroy = esp_slave_destroy,
+ .info = esp_info,
+ .queuecommand = esp_queue,
+ .eh_abort_handler = esp_abort,
+ .eh_bus_reset_handler = esp_reset,
+ .can_queue = 7,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+
+#include "scsi_module.c"
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
new file mode 100644
index 000000000000..f26c3a29e631
--- /dev/null
+++ b/drivers/scsi/sym53c416.c
@@ -0,0 +1,881 @@
+/*
+ * sym53c416.c
+ * Low-level SCSI driver for sym53c416 chip.
+ * Copyright (C) 1998 Lieven Willems (lw_linux@hotmail.com)
+ *
+ * Changes :
+ *
+ * Marcelo Tosatti <marcelo@conectiva.com.br> : Added io_request_lock locking
+ * Alan Cox <alan@redhat.com> : Cleaned up code formatting
+ * Fixed an irq locking bug
+ * Added ISAPnP support
+ * Bjoern A. Zeeb <bzeeb@zabbadoz.net> : Initial irq locking updates
+ * Added another card with ISAPnP support
+ *
+ * LILO command line usage: sym53c416=<PORTBASE>[,<IRQ>]
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blkdev.h>
+#include <linux/isapnp.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "sym53c416.h"
+
+#define VERSION_STRING "Version 1.0.0-ac"
+
+#define TC_LOW 0x00 /* Transfer counter low */
+#define TC_MID 0x01 /* Transfer counter mid */
+#define SCSI_FIFO 0x02 /* SCSI FIFO register */
+#define COMMAND_REG 0x03 /* Command Register */
+#define STATUS_REG 0x04 /* Status Register (READ) */
+#define DEST_BUS_ID 0x04 /* Destination Bus ID (WRITE) */
+#define INT_REG 0x05 /* Interrupt Register (READ) */
+#define TOM 0x05 /* Time out multiplier (WRITE) */
+#define STP 0x06 /* Synchronous Transfer period */
+#define SYNC_OFFSET 0x07 /* Synchronous Offset */
+#define CONF_REG_1 0x08 /* Configuration register 1 */
+#define CONF_REG_2 0x0B /* Configuration register 2 */
+#define CONF_REG_3 0x0C /* Configuration register 3 */
+#define CONF_REG_4 0x0D /* Configuration register 4 */
+#define TC_HIGH 0x0E /* Transfer counter high */
+#define PIO_FIFO_1 0x10 /* PIO FIFO register 1 */
+#define PIO_FIFO_2 0x11 /* PIO FIFO register 2 */
+#define PIO_FIFO_3 0x12 /* PIO FIFO register 3 */
+#define PIO_FIFO_4 0x13 /* PIO FIFO register 4 */
+#define PIO_FIFO_CNT 0x14 /* PIO FIFO count */
+#define PIO_INT_REG 0x15 /* PIO interrupt register */
+#define CONF_REG_5 0x16 /* Configuration register 5 */
+#define FEATURE_EN 0x1D /* Feature Enable register */
+
+/* Configuration register 1 entries: */
+/* Bits 2-0: SCSI ID of host adapter */
+#define SCM 0x80 /* Slow Cable Mode */
+#define SRID 0x40 /* SCSI Reset Interrupt Disable */
+#define PTM 0x20 /* Parity Test Mode */
+#define EPC 0x10 /* Enable Parity Checking */
+#define CTME 0x08 /* Special Test Mode */
+
+/* Configuration register 2 entries: */
+#define FE 0x40 /* Features Enable */
+#define SCSI2 0x08 /* SCSI 2 Enable */
+#define TBPA 0x04 /* Target Bad Parity Abort */
+
+/* Configuration register 3 entries: */
+#define IDMRC 0x80 /* ID Message Reserved Check */
+#define QTE 0x40 /* Queue Tag Enable */
+#define CDB10 0x20 /* Command Descriptor Block 10 */
+#define FSCSI 0x10 /* FastSCSI */
+#define FCLK 0x08 /* FastClock */
+
+/* Configuration register 4 entries: */
+#define RBS 0x08 /* Register bank select */
+#define EAN 0x04 /* Enable Active Negotiation */
+
+/* Configuration register 5 entries: */
+#define LPSR 0x80 /* Lower Power SCSI Reset */
+#define IE 0x20 /* Interrupt Enable */
+#define LPM 0x02 /* Low Power Mode */
+#define WSE0 0x01 /* 0WS Enable */
+
+/* Interrupt register entries: */
+#define SRST 0x80 /* SCSI Reset */
+#define ILCMD 0x40 /* Illegal Command */
+#define DIS 0x20 /* Disconnect */
+#define BS 0x10 /* Bus Service */
+#define FC 0x08 /* Function Complete */
+#define RESEL 0x04 /* Reselected */
+#define SI 0x03 /* Selection Interrupt */
+
+/* Status Register Entries: */
+#define SCI 0x80 /* SCSI Core Int */
+#define GE 0x40 /* Gross Error */
+#define PE 0x20 /* Parity Error */
+#define TC 0x10 /* Terminal Count */
+#define VGC 0x08 /* Valid Group Code */
+#define PHBITS 0x07 /* Phase bits */
+
+/* PIO Interrupt Register Entries: */
+#define SCI 0x80 /* SCSI Core Int */
+#define PFI 0x40 /* PIO FIFO Interrupt */
+#define FULL 0x20 /* PIO FIFO Full */
+#define EMPTY 0x10 /* PIO FIFO Empty */
+#define CE 0x08 /* Collision Error */
+#define OUE 0x04 /* Overflow / Underflow error */
+#define FIE 0x02 /* Full Interrupt Enable */
+#define EIE 0x01 /* Empty Interrupt Enable */
+
+/* SYM53C416 SCSI phases (lower 3 bits of SYM53C416_STATUS_REG) */
+#define PHASE_DATA_OUT 0x00
+#define PHASE_DATA_IN 0x01
+#define PHASE_COMMAND 0x02
+#define PHASE_STATUS 0x03
+#define PHASE_RESERVED_1 0x04
+#define PHASE_RESERVED_2 0x05
+#define PHASE_MESSAGE_OUT 0x06
+#define PHASE_MESSAGE_IN 0x07
+
+/* SYM53C416 core commands */
+#define NOOP 0x00
+#define FLUSH_FIFO 0x01
+#define RESET_CHIP 0x02
+#define RESET_SCSI_BUS 0x03
+#define DISABLE_SEL_RESEL 0x45
+#define RESEL_SEQ 0x40
+#define SEL_WITHOUT_ATN_SEQ 0x41
+#define SEL_WITH_ATN_SEQ 0x42
+#define SEL_WITH_ATN_AND_STOP_SEQ 0x43
+#define ENABLE_SEL_RESEL 0x44
+#define SEL_WITH_ATN3_SEQ 0x46
+#define RESEL3_SEQ 0x47
+#define SND_MSG 0x20
+#define SND_STAT 0x21
+#define SND_DATA 0x22
+#define DISCONNECT_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARGET_COMM_COMPLETE_SEQ 0x25
+#define DISCONN 0x27
+#define RECV_MSG_SEQ 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2A
+#define RECV_CMD_SEQ 0x2B
+#define TARGET_ABORT_PIO 0x04
+#define TRANSFER_INFORMATION 0x10
+#define INIT_COMM_COMPLETE_SEQ 0x11
+#define MSG_ACCEPTED 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1A
+#define RESET_ATN 0x1B
+#define ILLEGAL 0xFF
+
+#define PIO_MODE 0x80
+
+#define IO_RANGE 0x20 /* 0x00 - 0x1F */
+#define ID "sym53c416" /* Attention: copied to the sym53c416.h */
+#define PIO_SIZE 128 /* Size of PIO fifo is 128 bytes */
+
+#define READ_TIMEOUT 150
+#define WRITE_TIMEOUT 150
+
+#ifdef MODULE
+
+#define sym53c416_base sym53c416
+#define sym53c416_base_1 sym53c416_1
+#define sym53c416_base_2 sym53c416_2
+#define sym53c416_base_3 sym53c416_3
+
+static unsigned int sym53c416_base[2] = {0,0};
+static unsigned int sym53c416_base_1[2] = {0,0};
+static unsigned int sym53c416_base_2[2] = {0,0};
+static unsigned int sym53c416_base_3[2] = {0,0};
+
+#endif
+
+#define MAXHOSTS 4
+
+#define SG_ADDRESS(buffer) ((char *) (page_address((buffer)->page)+(buffer)->offset))
+
+enum phases
+{
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+typedef struct
+{
+ int base;
+ int irq;
+ int scsi_id;
+} host;
+
+static host hosts[MAXHOSTS] = {
+ {0, 0, SYM53C416_SCSI_ID},
+ {0, 0, SYM53C416_SCSI_ID},
+ {0, 0, SYM53C416_SCSI_ID},
+ {0, 0, SYM53C416_SCSI_ID}
+ };
+
+static int host_index = 0;
+static char info[120];
+static Scsi_Cmnd *current_command = NULL;
+static int fastpio = 1;
+
+static int probeaddrs[] = {0x200, 0x220, 0x240, 0};
+
+static void sym53c416_set_transfer_counter(int base, unsigned int len)
+{
+ /* Program Transfer Counter */
+ outb(len & 0x0000FF, base + TC_LOW);
+ outb((len & 0x00FF00) >> 8, base + TC_MID);
+ outb((len & 0xFF0000) >> 16, base + TC_HIGH);
+}
+
+static DEFINE_SPINLOCK(sym53c416_lock);
+
+/* Returns the number of bytes read */
+static __inline__ unsigned int sym53c416_read(int base, unsigned char *buffer, unsigned int len)
+{
+ unsigned int orig_len = len;
+ unsigned long flags = 0;
+ unsigned int bytes_left;
+ unsigned long i;
+ int timeout = READ_TIMEOUT;
+
+ /* Do transfer */
+ spin_lock_irqsave(&sym53c416_lock, flags);
+ while(len && timeout)
+ {
+ bytes_left = inb(base + PIO_FIFO_CNT); /* Number of bytes in the PIO FIFO */
+ if(fastpio && bytes_left > 3)
+ {
+ insl(base + PIO_FIFO_1, buffer, bytes_left >> 2);
+ buffer += bytes_left & 0xFC;
+ len -= bytes_left & 0xFC;
+ }
+ else if(bytes_left > 0)
+ {
+ len -= bytes_left;
+ for(; bytes_left > 0; bytes_left--)
+ *(buffer++) = inb(base + PIO_FIFO_1);
+ }
+ else
+ {
+ i = jiffies + timeout;
+ spin_unlock_irqrestore(&sym53c416_lock, flags);
+ while(time_before(jiffies, i) && (inb(base + PIO_INT_REG) & EMPTY) && timeout)
+ if(inb(base + PIO_INT_REG) & SCI)
+ timeout = 0;
+ spin_lock_irqsave(&sym53c416_lock, flags);
+ if(inb(base + PIO_INT_REG) & EMPTY)
+ timeout = 0;
+ }
+ }
+ spin_unlock_irqrestore(&sym53c416_lock, flags);
+ return orig_len - len;
+}
+
+/* Returns the number of bytes written */
+static __inline__ unsigned int sym53c416_write(int base, unsigned char *buffer, unsigned int len)
+{
+ unsigned int orig_len = len;
+ unsigned long flags = 0;
+ unsigned int bufferfree;
+ unsigned long i;
+ unsigned int timeout = WRITE_TIMEOUT;
+
+ /* Do transfer */
+ spin_lock_irqsave(&sym53c416_lock, flags);
+ while(len && timeout)
+ {
+ bufferfree = PIO_SIZE - inb(base + PIO_FIFO_CNT);
+ if(bufferfree > len)
+ bufferfree = len;
+ if(fastpio && bufferfree > 3)
+ {
+ outsl(base + PIO_FIFO_1, buffer, bufferfree >> 2);
+ buffer += bufferfree & 0xFC;
+ len -= bufferfree & 0xFC;
+ }
+ else if(bufferfree > 0)
+ {
+ len -= bufferfree;
+ for(; bufferfree > 0; bufferfree--)
+ outb(*(buffer++), base + PIO_FIFO_1);
+ }
+ else
+ {
+ i = jiffies + timeout;
+ spin_unlock_irqrestore(&sym53c416_lock, flags);
+ while(time_before(jiffies, i) && (inb(base + PIO_INT_REG) & FULL) && timeout)
+ ;
+ spin_lock_irqsave(&sym53c416_lock, flags);
+ if(inb(base + PIO_INT_REG) & FULL)
+ timeout = 0;
+ }
+ }
+ spin_unlock_irqrestore(&sym53c416_lock, flags);
+ return orig_len - len;
+}
+
+static irqreturn_t sym53c416_intr_handle(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct Scsi_Host *dev = dev_id;
+ int base = 0;
+ int i;
+ unsigned long flags = 0;
+ unsigned char status_reg, pio_int_reg, int_reg;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+ unsigned int tot_trans = 0;
+
+ /* We search the base address of the host adapter which caused the interrupt */
+ /* FIXME: should pass dev_id sensibly as hosts[i] */
+ for(i = 0; i < host_index && !base; i++)
+ if(irq == hosts[i].irq)
+ base = hosts[i].base;
+ /* If no adapter found, we cannot handle the interrupt. Leave a message */
+ /* and continue. This should never happen... */
+ if(!base)
+ {
+ printk(KERN_ERR "sym53c416: No host adapter defined for interrupt %d\n", irq);
+ return IRQ_NONE;
+ }
+ /* Now we have the base address and we can start handling the interrupt */
+
+ spin_lock_irqsave(dev->host_lock,flags);
+ status_reg = inb(base + STATUS_REG);
+ pio_int_reg = inb(base + PIO_INT_REG);
+ int_reg = inb(base + INT_REG);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+
+ /* First, we handle error conditions */
+ if(int_reg & SCI) /* SCSI Reset */
+ {
+ printk(KERN_DEBUG "sym53c416: Reset received\n");
+ current_command->SCp.phase = idle;
+ current_command->result = DID_RESET << 16;
+ spin_lock_irqsave(dev->host_lock, flags);
+ current_command->scsi_done(current_command);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ goto out;
+ }
+ if(int_reg & ILCMD) /* Illegal Command */
+ {
+ printk(KERN_WARNING "sym53c416: Illegal Command: 0x%02x.\n", inb(base + COMMAND_REG));
+ current_command->SCp.phase = idle;
+ current_command->result = DID_ERROR << 16;
+ spin_lock_irqsave(dev->host_lock, flags);
+ current_command->scsi_done(current_command);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ goto out;
+ }
+ if(status_reg & GE) /* Gross Error */
+ {
+ printk(KERN_WARNING "sym53c416: Controller reports gross error.\n");
+ current_command->SCp.phase = idle;
+ current_command->result = DID_ERROR << 16;
+ spin_lock_irqsave(dev->host_lock, flags);
+ current_command->scsi_done(current_command);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ goto out;
+ }
+ if(status_reg & PE) /* Parity Error */
+ {
+ printk(KERN_WARNING "sym53c416:SCSI parity error.\n");
+ current_command->SCp.phase = idle;
+ current_command->result = DID_PARITY << 16;
+ spin_lock_irqsave(dev->host_lock, flags);
+ current_command->scsi_done(current_command);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ goto out;
+ }
+ if(pio_int_reg & (CE | OUE))
+ {
+ printk(KERN_WARNING "sym53c416: PIO interrupt error.\n");
+ current_command->SCp.phase = idle;
+ current_command->result = DID_ERROR << 16;
+ spin_lock_irqsave(dev->host_lock, flags);
+ current_command->scsi_done(current_command);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ goto out;
+ }
+ if(int_reg & DIS) /* Disconnect */
+ {
+ if(current_command->SCp.phase != message_in)
+ current_command->result = DID_NO_CONNECT << 16;
+ else
+ current_command->result = (current_command->SCp.Status & 0xFF) | ((current_command->SCp.Message & 0xFF) << 8) | (DID_OK << 16);
+ current_command->SCp.phase = idle;
+ spin_lock_irqsave(dev->host_lock, flags);
+ current_command->scsi_done(current_command);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ goto out;
+ }
+ /* Now we handle SCSI phases */
+
+ switch(status_reg & PHBITS) /* Filter SCSI phase out of status reg */
+ {
+ case PHASE_DATA_OUT:
+ {
+ if(int_reg & BS)
+ {
+ current_command->SCp.phase = data_out;
+ outb(FLUSH_FIFO, base + COMMAND_REG);
+ sym53c416_set_transfer_counter(base, current_command->request_bufflen);
+ outb(TRANSFER_INFORMATION | PIO_MODE, base + COMMAND_REG);
+ if(!current_command->use_sg)
+ tot_trans = sym53c416_write(base, current_command->request_buffer, current_command->request_bufflen);
+ else
+ {
+ sgcount = current_command->use_sg;
+ sglist = current_command->request_buffer;
+ while(sgcount--)
+ {
+ tot_trans += sym53c416_write(base, SG_ADDRESS(sglist), sglist->length);
+ sglist++;
+ }
+ }
+ if(tot_trans < current_command->underflow)
+ printk(KERN_WARNING "sym53c416: Underflow, wrote %d bytes, request for %d bytes.\n", tot_trans, current_command->underflow);
+ }
+ break;
+ }
+
+ case PHASE_DATA_IN:
+ {
+ if(int_reg & BS)
+ {
+ current_command->SCp.phase = data_in;
+ outb(FLUSH_FIFO, base + COMMAND_REG);
+ sym53c416_set_transfer_counter(base, current_command->request_bufflen);
+ outb(TRANSFER_INFORMATION | PIO_MODE, base + COMMAND_REG);
+ if(!current_command->use_sg)
+ tot_trans = sym53c416_read(base, current_command->request_buffer, current_command->request_bufflen);
+ else
+ {
+ sgcount = current_command->use_sg;
+ sglist = current_command->request_buffer;
+ while(sgcount--)
+ {
+ tot_trans += sym53c416_read(base, SG_ADDRESS(sglist), sglist->length);
+ sglist++;
+ }
+ }
+ if(tot_trans < current_command->underflow)
+ printk(KERN_WARNING "sym53c416: Underflow, read %d bytes, request for %d bytes.\n", tot_trans, current_command->underflow);
+ }
+ break;
+ }
+
+ case PHASE_COMMAND:
+ {
+ current_command->SCp.phase = command_ph;
+ printk(KERN_ERR "sym53c416: Unknown interrupt in command phase.\n");
+ break;
+ }
+
+ case PHASE_STATUS:
+ {
+ current_command->SCp.phase = status_ph;
+ outb(FLUSH_FIFO, base + COMMAND_REG);
+ outb(INIT_COMM_COMPLETE_SEQ, base + COMMAND_REG);
+ break;
+ }
+
+ case PHASE_RESERVED_1:
+ case PHASE_RESERVED_2:
+ {
+ printk(KERN_ERR "sym53c416: Reserved phase occurred.\n");
+ break;
+ }
+
+ case PHASE_MESSAGE_OUT:
+ {
+ current_command->SCp.phase = message_out;
+ outb(SET_ATN, base + COMMAND_REG);
+ outb(MSG_ACCEPTED, base + COMMAND_REG);
+ break;
+ }
+
+ case PHASE_MESSAGE_IN:
+ {
+ current_command->SCp.phase = message_in;
+ current_command->SCp.Status = inb(base + SCSI_FIFO);
+ current_command->SCp.Message = inb(base + SCSI_FIFO);
+ if(current_command->SCp.Message == SAVE_POINTERS || current_command->SCp.Message == DISCONNECT)
+ outb(SET_ATN, base + COMMAND_REG);
+ outb(MSG_ACCEPTED, base + COMMAND_REG);
+ break;
+ }
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static void sym53c416_init(int base, int scsi_id)
+{
+ outb(RESET_CHIP, base + COMMAND_REG);
+ outb(NOOP, base + COMMAND_REG);
+ outb(0x99, base + TOM); /* Time out of 250 ms */
+ outb(0x05, base + STP);
+ outb(0x00, base + SYNC_OFFSET);
+ outb(EPC | scsi_id, base + CONF_REG_1);
+ outb(FE | SCSI2 | TBPA, base + CONF_REG_2);
+ outb(IDMRC | QTE | CDB10 | FSCSI | FCLK, base + CONF_REG_3);
+ outb(0x83 | EAN, base + CONF_REG_4);
+ outb(IE | WSE0, base + CONF_REG_5);
+ outb(0, base + FEATURE_EN);
+}
+
+static int sym53c416_probeirq(int base, int scsi_id)
+{
+ int irq, irqs;
+ unsigned long i;
+
+ /* Clear interrupt register */
+ inb(base + INT_REG);
+ /* Start probing for irq's */
+ irqs = probe_irq_on();
+ /* Reinit chip */
+ sym53c416_init(base, scsi_id);
+ /* Cause interrupt */
+ outb(NOOP, base + COMMAND_REG);
+ outb(ILLEGAL, base + COMMAND_REG);
+ outb(0x07, base + DEST_BUS_ID);
+ outb(0x00, base + DEST_BUS_ID);
+ /* Wait for interrupt to occur */
+ i = jiffies + 20;
+ while(time_before(jiffies, i) && !(inb(base + STATUS_REG) & SCI))
+ barrier();
+ if(time_before_eq(i, jiffies)) /* timed out */
+ return 0;
+ /* Get occurred irq */
+ irq = probe_irq_off(irqs);
+ sym53c416_init(base, scsi_id);
+ return irq;
+}
+
+/* Setup: sym53c416=base,irq */
+void sym53c416_setup(char *str, int *ints)
+{
+ int i;
+
+ if(host_index >= MAXHOSTS)
+ {
+ printk(KERN_WARNING "sym53c416: Too many hosts defined\n");
+ return;
+ }
+ if(ints[0] < 1 || ints[0] > 2)
+ {
+ printk(KERN_ERR "sym53c416: Wrong number of parameters:\n");
+ printk(KERN_ERR "sym53c416: usage: sym53c416=<base>[,<irq>]\n");
+ return;
+ }
+ for(i = 0; i < host_index && i >= 0; i++)
+ if(hosts[i].base == ints[1])
+ i = -2;
+ if(i >= 0)
+ {
+ hosts[host_index].base = ints[1];
+ hosts[host_index].irq = (ints[0] == 2)? ints[2] : 0;
+ host_index++;
+ }
+}
+
+static int sym53c416_test(int base)
+{
+ outb(RESET_CHIP, base + COMMAND_REG);
+ outb(NOOP, base + COMMAND_REG);
+ if(inb(base + COMMAND_REG) != NOOP)
+ return 0;
+ if(!inb(base + TC_HIGH) || inb(base + TC_HIGH) == 0xFF)
+ return 0;
+ if((inb(base + PIO_INT_REG) & (FULL | EMPTY | CE | OUE | FIE | EIE)) != EMPTY)
+ return 0;
+ return 1;
+}
+
+
+static struct isapnp_device_id id_table[] __devinitdata = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('S','L','I'), ISAPNP_FUNCTION(0x4161), 0 },
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('S','L','I'), ISAPNP_FUNCTION(0x4163), 0 },
+ { ISAPNP_DEVICE_SINGLE_END }
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static void sym53c416_probe(void)
+{
+ int *base = probeaddrs;
+ int ints[2];
+
+ ints[0] = 1;
+ for(; *base; base++) {
+ if (request_region(*base, IO_RANGE, ID)) {
+ if (sym53c416_test(*base)) {
+ ints[1] = *base;
+ sym53c416_setup(NULL, ints);
+ }
+ release_region(*base, IO_RANGE);
+ }
+ }
+}
+
+int __init sym53c416_detect(Scsi_Host_Template *tpnt)
+{
+ unsigned long flags;
+ struct Scsi_Host * shpnt = NULL;
+ int i;
+ int count;
+ struct pnp_dev *idev = NULL;
+
+#ifdef MODULE
+ int ints[3];
+
+ ints[0] = 2;
+ if(sym53c416_base)
+ {
+ ints[1] = sym53c416_base[0];
+ ints[2] = sym53c416_base[1];
+ sym53c416_setup(NULL, ints);
+ }
+ if(sym53c416_base_1)
+ {
+ ints[1] = sym53c416_base_1[0];
+ ints[2] = sym53c416_base_1[1];
+ sym53c416_setup(NULL, ints);
+ }
+ if(sym53c416_base_2)
+ {
+ ints[1] = sym53c416_base_2[0];
+ ints[2] = sym53c416_base_2[1];
+ sym53c416_setup(NULL, ints);
+ }
+ if(sym53c416_base_3)
+ {
+ ints[1] = sym53c416_base_3[0];
+ ints[2] = sym53c416_base_3[1];
+ sym53c416_setup(NULL, ints);
+ }
+#endif
+ printk(KERN_INFO "sym53c416.c: %s\n", VERSION_STRING);
+
+ for (i=0; id_table[i].vendor != 0; i++) {
+ while((idev=pnp_find_dev(NULL, id_table[i].vendor,
+ id_table[i].function, idev))!=NULL)
+ {
+ int i[3];
+
+ if(pnp_device_attach(idev)<0)
+ {
+ printk(KERN_WARNING "sym53c416: unable to attach PnP device.\n");
+ continue;
+ }
+ if(pnp_activate_dev(idev) < 0)
+ {
+ printk(KERN_WARNING "sym53c416: unable to activate PnP device.\n");
+ pnp_device_detach(idev);
+ continue;
+
+ }
+
+ i[0] = 2;
+ i[1] = pnp_port_start(idev, 0);
+ i[2] = pnp_irq(idev, 0);
+
+ printk(KERN_INFO "sym53c416: ISAPnP card found and configured at 0x%X, IRQ %d.\n",
+ i[1], i[2]);
+ sym53c416_setup(NULL, i);
+ }
+ }
+ sym53c416_probe();
+
+ /* Now we register and set up each host adapter found... */
+ for(count = 0, i = 0; i < host_index; i++) {
+ if (!request_region(hosts[i].base, IO_RANGE, ID))
+ continue;
+ if (!sym53c416_test(hosts[i].base)) {
+ printk(KERN_WARNING "No sym53c416 found at address 0x%03x\n", hosts[i].base);
+ goto fail_release_region;
+ }
+
+ /* We don't have an irq yet, so we should probe for one */
+ if (!hosts[i].irq)
+ hosts[i].irq = sym53c416_probeirq(hosts[i].base, hosts[i].scsi_id);
+ if (!hosts[i].irq)
+ goto fail_release_region;
+
+ shpnt = scsi_register(tpnt, 0);
+ if (!shpnt)
+ goto fail_release_region;
+ /* Request for specified IRQ */
+ if (request_irq(hosts[i].irq, sym53c416_intr_handle, 0, ID, shpnt))
+ goto fail_free_host;
+
+ spin_lock_irqsave(&sym53c416_lock, flags);
+ shpnt->unique_id = hosts[i].base;
+ shpnt->io_port = hosts[i].base;
+ shpnt->n_io_port = IO_RANGE;
+ shpnt->irq = hosts[i].irq;
+ shpnt->this_id = hosts[i].scsi_id;
+ sym53c416_init(hosts[i].base, hosts[i].scsi_id);
+ count++;
+ spin_unlock_irqrestore(&sym53c416_lock, flags);
+ continue;
+
+ fail_free_host:
+ scsi_unregister(shpnt);
+ fail_release_region:
+ release_region(hosts[i].base, IO_RANGE);
+ }
+ return count;
+}
+
+const char *sym53c416_info(struct Scsi_Host *SChost)
+{
+ int i;
+ int base = SChost->io_port;
+ int irq = SChost->irq;
+ int scsi_id = 0;
+ int rev = inb(base + TC_HIGH);
+
+ for(i = 0; i < host_index; i++)
+ if(hosts[i].base == base)
+ scsi_id = hosts[i].scsi_id;
+ sprintf(info, "Symbios Logic 53c416 (rev. %d) at 0x%03x, irq %d, SCSI-ID %d, %s pio", rev, base, irq, scsi_id, (fastpio)? "fast" : "slow");
+ return info;
+}
+
+int sym53c416_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ int base;
+ unsigned long flags = 0;
+ int i;
+
+ /* Store base register as we can have more than one controller in the system */
+ base = SCpnt->device->host->io_port;
+ current_command = SCpnt; /* set current command */
+ current_command->scsi_done = done; /* set ptr to done function */
+ current_command->SCp.phase = command_ph; /* currect phase is the command phase */
+ current_command->SCp.Status = 0;
+ current_command->SCp.Message = 0;
+
+ spin_lock_irqsave(&sym53c416_lock, flags);
+ outb(SCpnt->device->id, base + DEST_BUS_ID); /* Set scsi id target */
+ outb(FLUSH_FIFO, base + COMMAND_REG); /* Flush SCSI and PIO FIFO's */
+ /* Write SCSI command into the SCSI fifo */
+ for(i = 0; i < SCpnt->cmd_len; i++)
+ outb(SCpnt->cmnd[i], base + SCSI_FIFO);
+ /* Start selection sequence */
+ outb(SEL_WITHOUT_ATN_SEQ, base + COMMAND_REG);
+ /* Now an interrupt will be generated which we will catch in out interrupt routine */
+ spin_unlock_irqrestore(&sym53c416_lock, flags);
+ return 0;
+}
+
+static int sym53c416_abort(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+static int sym53c416_bus_reset(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+static int sym53c416_device_reset(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+static int sym53c416_host_reset(Scsi_Cmnd *SCpnt)
+{
+ int base;
+ int scsi_id = -1;
+ int i;
+
+ /* printk("sym53c416_reset\n"); */
+ base = SCpnt->device->host->io_port;
+ /* search scsi_id - fixme, we shouldnt need to iterate for this! */
+ for(i = 0; i < host_index && scsi_id != -1; i++)
+ if(hosts[i].base == base)
+ scsi_id = hosts[i].scsi_id;
+ outb(RESET_CHIP, base + COMMAND_REG);
+ outb(NOOP | PIO_MODE, base + COMMAND_REG);
+ outb(RESET_SCSI_BUS, base + COMMAND_REG);
+ sym53c416_init(base, scsi_id);
+ return SUCCESS;
+}
+
+static int sym53c416_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ return 0;
+}
+
+static int sym53c416_bios_param(struct scsi_device *sdev,
+ struct block_device *dev,
+ sector_t capacity, int *ip)
+{
+ int size;
+
+ size = capacity;
+ ip[0] = 64; /* heads */
+ ip[1] = 32; /* sectors */
+ if((ip[2] = size >> 11) > 1024) /* cylinders, test for big disk */
+ {
+ ip[0] = 255; /* heads */
+ ip[1] = 63; /* sectors */
+ ip[2] = size / (255 * 63); /* cylinders */
+ }
+ return 0;
+}
+
+/* Loadable module support */
+#ifdef MODULE
+
+MODULE_AUTHOR("Lieven Willems");
+MODULE_LICENSE("GPL");
+
+module_param_array(sym53c416, uint, NULL, 0);
+module_param_array(sym53c416_1, uint, NULL, 0);
+module_param_array(sym53c416_2, uint, NULL, 0);
+module_param_array(sym53c416_3, uint, NULL, 0);
+
+#endif
+
+static Scsi_Host_Template driver_template = {
+ .proc_name = "sym53c416",
+ .name = "Symbios Logic 53c416",
+ .detect = sym53c416_detect,
+ .info = sym53c416_info,
+ .queuecommand = sym53c416_queuecommand,
+ .eh_abort_handler = sym53c416_abort,
+ .eh_host_reset_handler =sym53c416_host_reset,
+ .eh_bus_reset_handler = sym53c416_bus_reset,
+ .eh_device_reset_handler =sym53c416_device_reset,
+ .release = sym53c416_release,
+ .bios_param = sym53c416_bios_param,
+ .can_queue = 1,
+ .this_id = SYM53C416_SCSI_ID,
+ .sg_tablesize = 32,
+ .cmd_per_lun = 1,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/sym53c416.h b/drivers/scsi/sym53c416.h
new file mode 100644
index 000000000000..3c0e3f8301f1
--- /dev/null
+++ b/drivers/scsi/sym53c416.h
@@ -0,0 +1,36 @@
+/*
+ * sym53c416.h
+ *
+ * Copyright (C) 1998 Lieven Willems (lw_linux@hotmail.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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef _SYM53C416_H
+#define _SYM53C416_H
+
+#include <linux/types.h>
+
+#define SYM53C416_SCSI_ID 7
+
+static int sym53c416_detect(Scsi_Host_Template *);
+static const char *sym53c416_info(struct Scsi_Host *);
+static int sym53c416_release(struct Scsi_Host *);
+static int sym53c416_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int sym53c416_abort(Scsi_Cmnd *);
+static int sym53c416_host_reset(Scsi_Cmnd *);
+static int sym53c416_bus_reset(Scsi_Cmnd *);
+static int sym53c416_device_reset(Scsi_Cmnd *);
+static int sym53c416_bios_param(struct scsi_device *, struct block_device *,
+ sector_t, int *);
+static void sym53c416_setup(char *str, int *ints);
+#endif
diff --git a/drivers/scsi/sym53c8xx_2/Makefile b/drivers/scsi/sym53c8xx_2/Makefile
new file mode 100644
index 000000000000..873e8ced8252
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/Makefile
@@ -0,0 +1,4 @@
+# Makefile for the NCR/SYMBIOS/LSI 53C8XX PCI SCSI controllers driver.
+
+sym53c8xx-objs := sym_fw.o sym_glue.o sym_hipd.o sym_malloc.o sym_nvram.o
+obj-$(CONFIG_SCSI_SYM53C8XX_2) := sym53c8xx.o
diff --git a/drivers/scsi/sym53c8xx_2/sym53c8xx.h b/drivers/scsi/sym53c8xx_2/sym53c8xx.h
new file mode 100644
index 000000000000..481103769729
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym53c8xx.h
@@ -0,0 +1,217 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM53C8XX_H
+#define SYM53C8XX_H
+
+#include <linux/config.h>
+
+/*
+ * DMA addressing mode.
+ *
+ * 0 : 32 bit addressing for all chips.
+ * 1 : 40 bit addressing when supported by chip.
+ * 2 : 64 bit addressing when supported by chip,
+ * limited to 16 segments of 4 GB -> 64 GB max.
+ */
+#define SYM_CONF_DMA_ADDRESSING_MODE CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE
+
+/*
+ * NVRAM support.
+ */
+#if 1
+#define SYM_CONF_NVRAM_SUPPORT (1)
+#endif
+
+/*
+ * These options are not tunable from 'make config'
+ */
+#if 1
+#define SYM_LINUX_PROC_INFO_SUPPORT
+#define SYM_LINUX_USER_COMMAND_SUPPORT
+#define SYM_LINUX_USER_INFO_SUPPORT
+#define SYM_LINUX_DEBUG_CONTROL_SUPPORT
+#endif
+
+/*
+ * Also handle old NCR chips if not (0).
+ */
+#define SYM_CONF_GENERIC_SUPPORT (1)
+
+/*
+ * Allow tags from 2 to 256, default 8
+ */
+#ifndef CONFIG_SCSI_SYM53C8XX_MAX_TAGS
+#define CONFIG_SCSI_SYM53C8XX_MAX_TAGS (8)
+#endif
+
+#if CONFIG_SCSI_SYM53C8XX_MAX_TAGS < 2
+#define SYM_CONF_MAX_TAG (2)
+#elif CONFIG_SCSI_SYM53C8XX_MAX_TAGS > 256
+#define SYM_CONF_MAX_TAG (256)
+#else
+#define SYM_CONF_MAX_TAG CONFIG_SCSI_SYM53C8XX_MAX_TAGS
+#endif
+
+#ifndef CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS
+#define CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS SYM_CONF_MAX_TAG
+#endif
+
+/*
+ * Anyway, we configure the driver for at least 64 tags per LUN. :)
+ */
+#if SYM_CONF_MAX_TAG <= 64
+#define SYM_CONF_MAX_TAG_ORDER (6)
+#elif SYM_CONF_MAX_TAG <= 128
+#define SYM_CONF_MAX_TAG_ORDER (7)
+#else
+#define SYM_CONF_MAX_TAG_ORDER (8)
+#endif
+
+/*
+ * Max number of SG entries.
+ */
+#define SYM_CONF_MAX_SG (96)
+
+/*
+ * Driver setup structure.
+ *
+ * This structure is initialized from linux config options.
+ * It can be overridden at boot-up by the boot command line.
+ */
+struct sym_driver_setup {
+ u_short max_tag;
+ u_char burst_order;
+ u_char scsi_led;
+ u_char scsi_diff;
+ u_char irq_mode;
+ u_char scsi_bus_check;
+ u_char host_id;
+
+ u_char verbose;
+ u_char settle_delay;
+ u_char use_nvram;
+ u_long excludes[8];
+ char tag_ctrl[100];
+};
+
+#define SYM_SETUP_MAX_TAG sym_driver_setup.max_tag
+#define SYM_SETUP_BURST_ORDER sym_driver_setup.burst_order
+#define SYM_SETUP_SCSI_LED sym_driver_setup.scsi_led
+#define SYM_SETUP_SCSI_DIFF sym_driver_setup.scsi_diff
+#define SYM_SETUP_IRQ_MODE sym_driver_setup.irq_mode
+#define SYM_SETUP_SCSI_BUS_CHECK sym_driver_setup.scsi_bus_check
+#define SYM_SETUP_HOST_ID sym_driver_setup.host_id
+#define boot_verbose sym_driver_setup.verbose
+
+/*
+ * Initial setup.
+ *
+ * Can be overriden at startup by a command line.
+ */
+#define SYM_LINUX_DRIVER_SETUP { \
+ .max_tag = CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS, \
+ .burst_order = 7, \
+ .scsi_led = 1, \
+ .scsi_diff = 1, \
+ .irq_mode = 0, \
+ .scsi_bus_check = 1, \
+ .host_id = 7, \
+ .verbose = 0, \
+ .settle_delay = 3, \
+ .use_nvram = 1, \
+}
+
+extern struct sym_driver_setup sym_driver_setup;
+extern unsigned int sym_debug_flags;
+#define DEBUG_FLAGS sym_debug_flags
+
+/*
+ * Max number of targets.
+ * Maximum is 16 and you are advised not to change this value.
+ */
+#ifndef SYM_CONF_MAX_TARGET
+#define SYM_CONF_MAX_TARGET (16)
+#endif
+
+/*
+ * Max number of logical units.
+ * SPI-2 allows up to 64 logical units, but in real life, target
+ * that implements more that 7 logical units are pretty rare.
+ * Anyway, the cost of accepting up to 64 logical unit is low in
+ * this driver, thus going with the maximum is acceptable.
+ */
+#ifndef SYM_CONF_MAX_LUN
+#define SYM_CONF_MAX_LUN (64)
+#endif
+
+/*
+ * Max number of IO control blocks queued to the controller.
+ * Each entry needs 8 bytes and the queues are allocated contiguously.
+ * Since we donnot want to allocate more than a page, the theorical
+ * maximum is PAGE_SIZE/8. For safety, we announce a bit less to the
+ * access method. :)
+ * When not supplied, as it is suggested, the driver compute some
+ * good value for this parameter.
+ */
+/* #define SYM_CONF_MAX_START (PAGE_SIZE/8 - 16) */
+
+/*
+ * Support for Immediate Arbitration.
+ * Not advised.
+ */
+/* #define SYM_CONF_IARB_SUPPORT */
+
+/*
+ * Only relevant if IARB support configured.
+ * - Max number of successive settings of IARB hints.
+ * - Set IARB on arbitration lost.
+ */
+#define SYM_CONF_IARB_MAX 3
+#define SYM_CONF_SET_IARB_ON_ARB_LOST 1
+
+/*
+ * Returning wrong residuals may make problems.
+ * When zero, this define tells the driver to
+ * always return 0 as transfer residual.
+ * Btw, all my testings of residuals have succeeded.
+ */
+#define SYM_SETUP_RESIDUAL_SUPPORT 1
+
+#endif /* SYM53C8XX_H */
diff --git a/drivers/scsi/sym53c8xx_2/sym_defs.h b/drivers/scsi/sym53c8xx_2/sym_defs.h
new file mode 100644
index 000000000000..15bb89195c09
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_defs.h
@@ -0,0 +1,792 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM_DEFS_H
+#define SYM_DEFS_H
+
+#define SYM_VERSION "2.2.0"
+#define SYM_DRIVER_NAME "sym-" SYM_VERSION
+
+/*
+ * SYM53C8XX device features descriptor.
+ */
+struct sym_chip {
+ u_short device_id;
+ u_short revision_id;
+ char *name;
+ u_char burst_max; /* log-base-2 of max burst */
+ u_char offset_max;
+ u_char nr_divisor;
+ u_char lp_probe_bit;
+ u_int features;
+#define FE_LED0 (1<<0)
+#define FE_WIDE (1<<1) /* Wide data transfers */
+#define FE_ULTRA (1<<2) /* Ultra speed 20Mtrans/sec */
+#define FE_ULTRA2 (1<<3) /* Ultra 2 - 40 Mtrans/sec */
+#define FE_DBLR (1<<4) /* Clock doubler present */
+#define FE_QUAD (1<<5) /* Clock quadrupler present */
+#define FE_ERL (1<<6) /* Enable read line */
+#define FE_CLSE (1<<7) /* Cache line size enable */
+#define FE_WRIE (1<<8) /* Write & Invalidate enable */
+#define FE_ERMP (1<<9) /* Enable read multiple */
+#define FE_BOF (1<<10) /* Burst opcode fetch */
+#define FE_DFS (1<<11) /* DMA fifo size */
+#define FE_PFEN (1<<12) /* Prefetch enable */
+#define FE_LDSTR (1<<13) /* Load/Store supported */
+#define FE_RAM (1<<14) /* On chip RAM present */
+#define FE_VARCLK (1<<15) /* Clock frequency may vary */
+#define FE_RAM8K (1<<16) /* On chip RAM sized 8Kb */
+#define FE_64BIT (1<<17) /* 64-bit PCI BUS interface */
+#define FE_IO256 (1<<18) /* Requires full 256 bytes in PCI space */
+#define FE_NOPM (1<<19) /* Scripts handles phase mismatch */
+#define FE_LEDC (1<<20) /* Hardware control of LED */
+#define FE_ULTRA3 (1<<21) /* Ultra 3 - 80 Mtrans/sec DT */
+#define FE_66MHZ (1<<22) /* 66MHz PCI support */
+#define FE_CRC (1<<23) /* CRC support */
+#define FE_DIFF (1<<24) /* SCSI HVD support */
+#define FE_DFBC (1<<25) /* Have DFBC register */
+#define FE_LCKFRQ (1<<26) /* Have LCKFRQ */
+#define FE_C10 (1<<27) /* Various C10 core (mis)features */
+#define FE_U3EN (1<<28) /* U3EN bit usable */
+#define FE_DAC (1<<29) /* Support PCI DAC (64 bit addressing) */
+#define FE_ISTAT1 (1<<30) /* Have ISTAT1, MBOX0, MBOX1 registers */
+
+#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_CACHE0_SET (FE_CACHE_SET & ~FE_ERL)
+};
+
+/*
+ * SYM53C8XX IO register data structure.
+ */
+struct sym_reg {
+/*00*/ u8 nc_scntl0; /* full arb., ena parity, par->ATN */
+
+/*01*/ u8 nc_scntl1; /* no reset */
+ #define ISCON 0x10 /* connected to scsi */
+ #define CRST 0x08 /* force reset */
+ #define IARB 0x02 /* immediate arbitration */
+
+/*02*/ u8 nc_scntl2; /* no disconnect expected */
+ #define SDU 0x80 /* cmd: disconnect will raise error */
+ #define CHM 0x40 /* sta: chained mode */
+ #define WSS 0x08 /* sta: wide scsi send [W]*/
+ #define WSR 0x01 /* sta: wide scsi received [W]*/
+
+/*03*/ u8 nc_scntl3; /* cnf system clock dependent */
+ #define EWS 0x08 /* cmd: enable wide scsi [W]*/
+ #define ULTRA 0x80 /* cmd: ULTRA enable */
+ /* bits 0-2, 7 rsvd for C1010 */
+
+/*04*/ u8 nc_scid; /* cnf host adapter scsi address */
+ #define RRE 0x40 /* r/w:e enable response to resel. */
+ #define SRE 0x20 /* r/w:e enable response to select */
+
+/*05*/ u8 nc_sxfer; /* ### Sync speed and count */
+ /* bits 6-7 rsvd for C1010 */
+
+/*06*/ u8 nc_sdid; /* ### Destination-ID */
+
+/*07*/ u8 nc_gpreg; /* ??? IO-Pins */
+
+/*08*/ u8 nc_sfbr; /* ### First byte received */
+
+/*09*/ u8 nc_socl;
+ #define CREQ 0x80 /* r/w: SCSI-REQ */
+ #define CACK 0x40 /* r/w: SCSI-ACK */
+ #define CBSY 0x20 /* r/w: SCSI-BSY */
+ #define CSEL 0x10 /* r/w: SCSI-SEL */
+ #define CATN 0x08 /* r/w: SCSI-ATN */
+ #define CMSG 0x04 /* r/w: SCSI-MSG */
+ #define CC_D 0x02 /* r/w: SCSI-C_D */
+ #define CI_O 0x01 /* r/w: SCSI-I_O */
+
+/*0a*/ u8 nc_ssid;
+
+/*0b*/ u8 nc_sbcl;
+
+/*0c*/ u8 nc_dstat;
+ #define DFE 0x80 /* sta: dma fifo empty */
+ #define MDPE 0x40 /* int: master data parity error */
+ #define BF 0x20 /* int: script: bus fault */
+ #define ABRT 0x10 /* int: script: command aborted */
+ #define SSI 0x08 /* int: script: single step */
+ #define SIR 0x04 /* int: script: interrupt instruct. */
+ #define IID 0x01 /* int: script: illegal instruct. */
+
+/*0d*/ u8 nc_sstat0;
+ #define ILF 0x80 /* sta: data in SIDL register lsb */
+ #define ORF 0x40 /* sta: data in SODR register lsb */
+ #define OLF 0x20 /* sta: data in SODL register lsb */
+ #define AIP 0x10 /* sta: arbitration in progress */
+ #define LOA 0x08 /* sta: arbitration lost */
+ #define WOA 0x04 /* sta: arbitration won */
+ #define IRST 0x02 /* sta: scsi reset signal */
+ #define SDP 0x01 /* sta: scsi parity signal */
+
+/*0e*/ u8 nc_sstat1;
+ #define FF3210 0xf0 /* sta: bytes in the scsi fifo */
+
+/*0f*/ u8 nc_sstat2;
+ #define ILF1 0x80 /* sta: data in SIDL register msb[W]*/
+ #define ORF1 0x40 /* sta: data in SODR register msb[W]*/
+ #define OLF1 0x20 /* sta: data in SODL register msb[W]*/
+ #define DM 0x04 /* sta: DIFFSENS mismatch (895/6 only) */
+ #define LDSC 0x02 /* sta: disconnect & reconnect */
+
+/*10*/ u8 nc_dsa; /* --> Base page */
+/*11*/ u8 nc_dsa1;
+/*12*/ u8 nc_dsa2;
+/*13*/ u8 nc_dsa3;
+
+/*14*/ u8 nc_istat; /* --> Main Command and status */
+ #define CABRT 0x80 /* cmd: abort current operation */
+ #define SRST 0x40 /* mod: reset chip */
+ #define SIGP 0x20 /* r/w: message from host to script */
+ #define SEM 0x10 /* r/w: message between host + script */
+ #define CON 0x08 /* sta: connected to scsi */
+ #define INTF 0x04 /* sta: int on the fly (reset by wr)*/
+ #define SIP 0x02 /* sta: scsi-interrupt */
+ #define DIP 0x01 /* sta: host/script interrupt */
+
+/*15*/ u8 nc_istat1; /* 896 only */
+ #define FLSH 0x04 /* sta: chip is flushing */
+ #define SCRUN 0x02 /* sta: scripts are running */
+ #define SIRQD 0x01 /* r/w: disable INT pin */
+
+/*16*/ u8 nc_mbox0; /* 896 only */
+/*17*/ u8 nc_mbox1; /* 896 only */
+
+/*18*/ u8 nc_ctest0;
+/*19*/ u8 nc_ctest1;
+
+/*1a*/ u8 nc_ctest2;
+ #define CSIGP 0x40
+ /* bits 0-2,7 rsvd for C1010 */
+
+/*1b*/ u8 nc_ctest3;
+ #define FLF 0x08 /* cmd: flush dma fifo */
+ #define CLF 0x04 /* cmd: clear dma fifo */
+ #define FM 0x02 /* mod: fetch pin mode */
+ #define WRIE 0x01 /* mod: write and invalidate enable */
+ /* bits 4-7 rsvd for C1010 */
+
+/*1c*/ u32 nc_temp; /* ### Temporary stack */
+
+/*20*/ u8 nc_dfifo;
+/*21*/ u8 nc_ctest4;
+ #define BDIS 0x80 /* mod: burst disable */
+ #define MPEE 0x08 /* mod: master parity error enable */
+
+/*22*/ u8 nc_ctest5;
+ #define DFS 0x20 /* mod: dma fifo size */
+ /* bits 0-1, 3-7 rsvd for C1010 */
+
+/*23*/ u8 nc_ctest6;
+
+/*24*/ u32 nc_dbc; /* ### Byte count and command */
+/*28*/ u32 nc_dnad; /* ### Next command register */
+/*2c*/ u32 nc_dsp; /* --> Script Pointer */
+/*30*/ u32 nc_dsps; /* --> Script pointer save/opcode#2 */
+
+/*34*/ u8 nc_scratcha; /* Temporary register a */
+/*35*/ u8 nc_scratcha1;
+/*36*/ u8 nc_scratcha2;
+/*37*/ u8 nc_scratcha3;
+
+/*38*/ u8 nc_dmode;
+ #define BL_2 0x80 /* mod: burst length shift value +2 */
+ #define BL_1 0x40 /* mod: burst length shift value +1 */
+ #define ERL 0x08 /* mod: enable read line */
+ #define ERMP 0x04 /* mod: enable read multiple */
+ #define BOF 0x02 /* mod: burst op code fetch */
+
+/*39*/ u8 nc_dien;
+/*3a*/ u8 nc_sbr;
+
+/*3b*/ u8 nc_dcntl; /* --> Script execution control */
+ #define CLSE 0x80 /* mod: cache line size enable */
+ #define PFF 0x40 /* cmd: pre-fetch flush */
+ #define PFEN 0x20 /* mod: pre-fetch enable */
+ #define SSM 0x10 /* mod: single step mode */
+ #define IRQM 0x08 /* mod: irq mode (1 = totem pole !) */
+ #define STD 0x04 /* cmd: start dma mode */
+ #define IRQD 0x02 /* mod: irq disable */
+ #define NOCOM 0x01 /* cmd: protect sfbr while reselect */
+ /* bits 0-1 rsvd for C1010 */
+
+/*3c*/ u32 nc_adder;
+
+/*40*/ u16 nc_sien; /* -->: interrupt enable */
+/*42*/ u16 nc_sist; /* <--: interrupt status */
+ #define SBMC 0x1000/* sta: SCSI Bus Mode Change (895/6 only) */
+ #define STO 0x0400/* sta: timeout (select) */
+ #define GEN 0x0200/* sta: timeout (general) */
+ #define HTH 0x0100/* sta: timeout (handshake) */
+ #define MA 0x80 /* sta: phase mismatch */
+ #define CMP 0x40 /* sta: arbitration complete */
+ #define SEL 0x20 /* sta: selected by another device */
+ #define RSL 0x10 /* sta: reselected by another device*/
+ #define SGE 0x08 /* sta: gross error (over/underflow)*/
+ #define UDC 0x04 /* sta: unexpected disconnect */
+ #define RST 0x02 /* sta: scsi bus reset detected */
+ #define PAR 0x01 /* sta: scsi parity error */
+
+/*44*/ u8 nc_slpar;
+/*45*/ u8 nc_swide;
+/*46*/ u8 nc_macntl;
+/*47*/ u8 nc_gpcntl;
+/*48*/ u8 nc_stime0; /* cmd: timeout for select&handshake*/
+/*49*/ u8 nc_stime1; /* cmd: timeout user defined */
+/*4a*/ u16 nc_respid; /* sta: Reselect-IDs */
+
+/*4c*/ u8 nc_stest0;
+
+/*4d*/ u8 nc_stest1;
+ #define SCLK 0x80 /* Use the PCI clock as SCSI clock */
+ #define DBLEN 0x08 /* clock doubler running */
+ #define DBLSEL 0x04 /* clock doubler selected */
+
+
+/*4e*/ u8 nc_stest2;
+ #define ROF 0x40 /* reset scsi offset (after gross error!) */
+ #define EXT 0x02 /* extended filtering */
+
+/*4f*/ u8 nc_stest3;
+ #define TE 0x80 /* c: tolerAnt enable */
+ #define HSC 0x20 /* c: Halt SCSI Clock */
+ #define CSF 0x02 /* c: clear scsi fifo */
+
+/*50*/ u16 nc_sidl; /* Lowlevel: latched from scsi data */
+/*52*/ u8 nc_stest4;
+ #define SMODE 0xc0 /* SCSI bus mode (895/6 only) */
+ #define SMODE_HVD 0x40 /* High Voltage Differential */
+ #define SMODE_SE 0x80 /* Single Ended */
+ #define SMODE_LVD 0xc0 /* Low Voltage Differential */
+ #define LCKFRQ 0x20 /* Frequency Lock (895/6 only) */
+ /* bits 0-5 rsvd for C1010 */
+
+/*53*/ u8 nc_53_;
+/*54*/ u16 nc_sodl; /* Lowlevel: data out to scsi data */
+/*56*/ u8 nc_ccntl0; /* Chip Control 0 (896) */
+ #define ENPMJ 0x80 /* Enable Phase Mismatch Jump */
+ #define PMJCTL 0x40 /* Phase Mismatch Jump Control */
+ #define ENNDJ 0x20 /* Enable Non Data PM Jump */
+ #define DISFC 0x10 /* Disable Auto FIFO Clear */
+ #define DILS 0x02 /* Disable Internal Load/Store */
+ #define DPR 0x01 /* Disable Pipe Req */
+
+/*57*/ u8 nc_ccntl1; /* Chip Control 1 (896) */
+ #define ZMOD 0x80 /* High Impedance Mode */
+ #define DDAC 0x08 /* Disable Dual Address Cycle */
+ #define XTIMOD 0x04 /* 64-bit Table Ind. Indexing Mode */
+ #define EXTIBMV 0x02 /* Enable 64-bit Table Ind. BMOV */
+ #define EXDBMV 0x01 /* Enable 64-bit Direct BMOV */
+
+/*58*/ u16 nc_sbdl; /* Lowlevel: data from scsi data */
+/*5a*/ u16 nc_5a_;
+
+/*5c*/ u8 nc_scr0; /* Working register B */
+/*5d*/ u8 nc_scr1;
+/*5e*/ u8 nc_scr2;
+/*5f*/ u8 nc_scr3;
+
+/*60*/ u8 nc_scrx[64]; /* Working register C-R */
+/*a0*/ u32 nc_mmrs; /* Memory Move Read Selector */
+/*a4*/ u32 nc_mmws; /* Memory Move Write Selector */
+/*a8*/ u32 nc_sfs; /* Script Fetch Selector */
+/*ac*/ u32 nc_drs; /* DSA Relative Selector */
+/*b0*/ u32 nc_sbms; /* Static Block Move Selector */
+/*b4*/ u32 nc_dbms; /* Dynamic Block Move Selector */
+/*b8*/ u32 nc_dnad64; /* DMA Next Address 64 */
+/*bc*/ u16 nc_scntl4; /* C1010 only */
+ #define U3EN 0x80 /* Enable Ultra 3 */
+ #define AIPCKEN 0x40 /* AIP checking enable */
+ /* Also enable AIP generation on C10-33*/
+ #define XCLKH_DT 0x08 /* Extra clock of data hold on DT edge */
+ #define XCLKH_ST 0x04 /* Extra clock of data hold on ST edge */
+ #define XCLKS_DT 0x02 /* Extra clock of data set on DT edge */
+ #define XCLKS_ST 0x01 /* Extra clock of data set on ST edge */
+/*be*/ u8 nc_aipcntl0; /* AIP Control 0 C1010 only */
+/*bf*/ u8 nc_aipcntl1; /* AIP Control 1 C1010 only */
+ #define DISAIP 0x08 /* Disable AIP generation C10-66 only */
+/*c0*/ u32 nc_pmjad1; /* Phase Mismatch Jump Address 1 */
+/*c4*/ u32 nc_pmjad2; /* Phase Mismatch Jump Address 2 */
+/*c8*/ u8 nc_rbc; /* Remaining Byte Count */
+/*c9*/ u8 nc_rbc1;
+/*ca*/ u8 nc_rbc2;
+/*cb*/ u8 nc_rbc3;
+
+/*cc*/ u8 nc_ua; /* Updated Address */
+/*cd*/ u8 nc_ua1;
+/*ce*/ u8 nc_ua2;
+/*cf*/ u8 nc_ua3;
+/*d0*/ u32 nc_esa; /* Entry Storage Address */
+/*d4*/ u8 nc_ia; /* Instruction Address */
+/*d5*/ u8 nc_ia1;
+/*d6*/ u8 nc_ia2;
+/*d7*/ u8 nc_ia3;
+/*d8*/ u32 nc_sbc; /* SCSI Byte Count (3 bytes only) */
+/*dc*/ u32 nc_csbc; /* Cumulative SCSI Byte Count */
+ /* Following for C1010 only */
+/*e0*/ u16 nc_crcpad; /* CRC Value */
+/*e2*/ u8 nc_crccntl0; /* CRC control register */
+ #define SNDCRC 0x10 /* Send CRC Request */
+/*e3*/ u8 nc_crccntl1; /* CRC control register */
+/*e4*/ u32 nc_crcdata; /* CRC data register */
+/*e8*/ u32 nc_e8_;
+/*ec*/ u32 nc_ec_;
+/*f0*/ u16 nc_dfbc; /* DMA FIFO byte count */
+};
+
+/*-----------------------------------------------------------
+ *
+ * Utility macros for the script.
+ *
+ *-----------------------------------------------------------
+ */
+
+#define REGJ(p,r) (offsetof(struct sym_reg, p ## r))
+#define REG(r) REGJ (nc_, r)
+
+/*-----------------------------------------------------------
+ *
+ * SCSI phases
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_DATA_OUT 0x00000000
+#define SCR_DATA_IN 0x01000000
+#define SCR_COMMAND 0x02000000
+#define SCR_STATUS 0x03000000
+#define SCR_DT_DATA_OUT 0x04000000
+#define SCR_DT_DATA_IN 0x05000000
+#define SCR_MSG_OUT 0x06000000
+#define SCR_MSG_IN 0x07000000
+/* DT phases are illegal for non Ultra3 mode */
+#define SCR_ILG_OUT 0x04000000
+#define SCR_ILG_IN 0x05000000
+
+/*-----------------------------------------------------------
+ *
+ * Data transfer via SCSI.
+ *
+ *-----------------------------------------------------------
+ *
+ * MOVE_ABS (LEN)
+ * <<start address>>
+ *
+ * MOVE_IND (LEN)
+ * <<dnad_offset>>
+ *
+ * MOVE_TBL
+ * <<dnad_offset>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define OPC_MOVE 0x08000000
+
+#define SCR_MOVE_ABS(l) ((0x00000000 | OPC_MOVE) | (l))
+/* #define SCR_MOVE_IND(l) ((0x20000000 | OPC_MOVE) | (l)) */
+#define SCR_MOVE_TBL (0x10000000 | OPC_MOVE)
+
+#define SCR_CHMOV_ABS(l) ((0x00000000) | (l))
+/* #define SCR_CHMOV_IND(l) ((0x20000000) | (l)) */
+#define SCR_CHMOV_TBL (0x10000000)
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+/* We steal the `indirect addressing' flag for target mode MOVE in scripts */
+
+#define OPC_TCHMOVE 0x08000000
+
+#define SCR_TCHMOVE_ABS(l) ((0x20000000 | OPC_TCHMOVE) | (l))
+#define SCR_TCHMOVE_TBL (0x30000000 | OPC_TCHMOVE)
+
+#define SCR_TMOV_ABS(l) ((0x20000000) | (l))
+#define SCR_TMOV_TBL (0x30000000)
+#endif
+
+struct sym_tblmove {
+ u32 size;
+ u32 addr;
+};
+
+/*-----------------------------------------------------------
+ *
+ * Selection
+ *
+ *-----------------------------------------------------------
+ *
+ * SEL_ABS | SCR_ID (0..15) [ | REL_JMP]
+ * <<alternate_address>>
+ *
+ * SEL_TBL | << dnad_offset>> [ | REL_JMP]
+ * <<alternate_address>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_SEL_ABS 0x40000000
+#define SCR_SEL_ABS_ATN 0x41000000
+#define SCR_SEL_TBL 0x42000000
+#define SCR_SEL_TBL_ATN 0x43000000
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+#define SCR_RESEL_ABS 0x40000000
+#define SCR_RESEL_ABS_ATN 0x41000000
+#define SCR_RESEL_TBL 0x42000000
+#define SCR_RESEL_TBL_ATN 0x43000000
+#endif
+
+struct sym_tblsel {
+ u_char sel_scntl4; /* C1010 only */
+ u_char sel_sxfer;
+ u_char sel_id;
+ u_char sel_scntl3;
+};
+
+#define SCR_JMP_REL 0x04000000
+#define SCR_ID(id) (((u32)(id)) << 16)
+
+/*-----------------------------------------------------------
+ *
+ * Waiting for Disconnect or Reselect
+ *
+ *-----------------------------------------------------------
+ *
+ * WAIT_DISC
+ * dummy: <<alternate_address>>
+ *
+ * WAIT_RESEL
+ * <<alternate_address>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_WAIT_DISC 0x48000000
+#define SCR_WAIT_RESEL 0x50000000
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+#define SCR_DISCONNECT 0x48000000
+#endif
+
+/*-----------------------------------------------------------
+ *
+ * Bit Set / Reset
+ *
+ *-----------------------------------------------------------
+ *
+ * SET (flags {|.. })
+ *
+ * CLR (flags {|.. })
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_SET(f) (0x58000000 | (f))
+#define SCR_CLR(f) (0x60000000 | (f))
+
+#define SCR_CARRY 0x00000400
+#define SCR_TRG 0x00000200
+#define SCR_ACK 0x00000040
+#define SCR_ATN 0x00000008
+
+
+/*-----------------------------------------------------------
+ *
+ * Memory to memory move
+ *
+ *-----------------------------------------------------------
+ *
+ * COPY (bytecount)
+ * << source_address >>
+ * << destination_address >>
+ *
+ * SCR_COPY sets the NO FLUSH option by default.
+ * SCR_COPY_F does not set this option.
+ *
+ * For chips which do not support this option,
+ * sym_fw_bind_script() will remove this bit.
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_NO_FLUSH 0x01000000
+
+#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n))
+#define SCR_COPY_F(n) (0xc0000000 | (n))
+
+/*-----------------------------------------------------------
+ *
+ * Register move and binary operations
+ *
+ *-----------------------------------------------------------
+ *
+ * SFBR_REG (reg, op, data) reg = SFBR op data
+ * << 0 >>
+ *
+ * REG_SFBR (reg, op, data) SFBR = reg op data
+ * << 0 >>
+ *
+ * REG_REG (reg, op, data) reg = reg op data
+ * << 0 >>
+ *
+ *-----------------------------------------------------------
+ *
+ * On 825A, 875, 895 and 896 chips the content
+ * of SFBR register can be used as data (SCR_SFBR_DATA).
+ * The 896 has additionnal IO registers starting at
+ * offset 0x80. Bit 7 of register offset is stored in
+ * bit 7 of the SCRIPTS instruction first DWORD.
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_REG_OFS(ofs) ((((ofs) & 0x7f) << 16ul) + ((ofs) & 0x80))
+
+#define SCR_SFBR_REG(reg,op,data) \
+ (0x68000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+#define SCR_REG_SFBR(reg,op,data) \
+ (0x70000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+#define SCR_REG_REG(reg,op,data) \
+ (0x78000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+
+#define SCR_LOAD 0x00000000
+#define SCR_SHL 0x01000000
+#define SCR_OR 0x02000000
+#define SCR_XOR 0x03000000
+#define SCR_AND 0x04000000
+#define SCR_SHR 0x05000000
+#define SCR_ADD 0x06000000
+#define SCR_ADDC 0x07000000
+
+#define SCR_SFBR_DATA (0x00800000>>8ul) /* Use SFBR as data */
+
+/*-----------------------------------------------------------
+ *
+ * FROM_REG (reg) SFBR = reg
+ * << 0 >>
+ *
+ * TO_REG (reg) reg = SFBR
+ * << 0 >>
+ *
+ * LOAD_REG (reg, data) reg = <data>
+ * << 0 >>
+ *
+ * LOAD_SFBR(data) SFBR = <data>
+ * << 0 >>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_FROM_REG(reg) \
+ SCR_REG_SFBR(reg,SCR_OR,0)
+
+#define SCR_TO_REG(reg) \
+ SCR_SFBR_REG(reg,SCR_OR,0)
+
+#define SCR_LOAD_REG(reg,data) \
+ SCR_REG_REG(reg,SCR_LOAD,data)
+
+#define SCR_LOAD_SFBR(data) \
+ (SCR_REG_SFBR (gpreg, SCR_LOAD, data))
+
+/*-----------------------------------------------------------
+ *
+ * LOAD from memory to register.
+ * STORE from register to memory.
+ *
+ * Only supported by 810A, 860, 825A, 875, 895 and 896.
+ *
+ *-----------------------------------------------------------
+ *
+ * LOAD_ABS (LEN)
+ * <<start address>>
+ *
+ * LOAD_REL (LEN) (DSA relative)
+ * <<dsa_offset>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_REG_OFS2(ofs) (((ofs) & 0xff) << 16ul)
+#define SCR_NO_FLUSH2 0x02000000
+#define SCR_DSA_REL2 0x10000000
+
+#define SCR_LOAD_R(reg, how, n) \
+ (0xe1000000 | how | (SCR_REG_OFS2(REG(reg))) | (n))
+
+#define SCR_STORE_R(reg, how, n) \
+ (0xe0000000 | how | (SCR_REG_OFS2(REG(reg))) | (n))
+
+#define SCR_LOAD_ABS(reg, n) SCR_LOAD_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_LOAD_REL(reg, n) SCR_LOAD_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2, n)
+#define SCR_LOAD_ABS_F(reg, n) SCR_LOAD_R(reg, 0, n)
+#define SCR_LOAD_REL_F(reg, n) SCR_LOAD_R(reg, SCR_DSA_REL2, n)
+
+#define SCR_STORE_ABS(reg, n) SCR_STORE_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_STORE_REL(reg, n) SCR_STORE_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2,n)
+#define SCR_STORE_ABS_F(reg, n) SCR_STORE_R(reg, 0, n)
+#define SCR_STORE_REL_F(reg, n) SCR_STORE_R(reg, SCR_DSA_REL2, n)
+
+
+/*-----------------------------------------------------------
+ *
+ * Waiting for Disconnect or Reselect
+ *
+ *-----------------------------------------------------------
+ *
+ * JUMP [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<address>>
+ *
+ * JUMPR [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<distance>>
+ *
+ * CALL [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<address>>
+ *
+ * CALLR [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<distance>>
+ *
+ * RETURN [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<dummy>>
+ *
+ * INT [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<ident>>
+ *
+ * INT_FLY [ | IFTRUE/IFFALSE ( ... ) ]
+ * <<ident>>
+ *
+ * Conditions:
+ * WHEN (phase)
+ * IF (phase)
+ * CARRYSET
+ * DATA (data, mask)
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_NO_OP 0x80000000
+#define SCR_JUMP 0x80080000
+#define SCR_JUMP64 0x80480000
+#define SCR_JUMPR 0x80880000
+#define SCR_CALL 0x88080000
+#define SCR_CALLR 0x88880000
+#define SCR_RETURN 0x90080000
+#define SCR_INT 0x98080000
+#define SCR_INT_FLY 0x98180000
+
+#define IFFALSE(arg) (0x00080000 | (arg))
+#define IFTRUE(arg) (0x00000000 | (arg))
+
+#define WHEN(phase) (0x00030000 | (phase))
+#define IF(phase) (0x00020000 | (phase))
+
+#define DATA(D) (0x00040000 | ((D) & 0xff))
+#define MASK(D,M) (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff))
+
+#define CARRYSET (0x00200000)
+
+/*-----------------------------------------------------------
+ *
+ * SCSI constants.
+ *
+ *-----------------------------------------------------------
+ */
+
+/*
+ * Messages
+ */
+
+#define M_COMPLETE COMMAND_COMPLETE
+#define M_EXTENDED EXTENDED_MESSAGE
+#define M_SAVE_DP SAVE_POINTERS
+#define M_RESTORE_DP RESTORE_POINTERS
+#define M_DISCONNECT DISCONNECT
+#define M_ID_ERROR INITIATOR_ERROR
+#define M_ABORT ABORT_TASK_SET
+#define M_REJECT MESSAGE_REJECT
+#define M_NOOP NOP
+#define M_PARITY MSG_PARITY_ERROR
+#define M_LCOMPLETE LINKED_CMD_COMPLETE
+#define M_FCOMPLETE LINKED_FLG_CMD_COMPLETE
+#define M_RESET TARGET_RESET
+#define M_ABORT_TAG ABORT_TASK
+#define M_CLEAR_QUEUE CLEAR_TASK_SET
+#define M_INIT_REC INITIATE_RECOVERY
+#define M_REL_REC RELEASE_RECOVERY
+#define M_TERMINATE (0x11)
+#define M_SIMPLE_TAG SIMPLE_QUEUE_TAG
+#define M_HEAD_TAG HEAD_OF_QUEUE_TAG
+#define M_ORDERED_TAG ORDERED_QUEUE_TAG
+#define M_IGN_RESIDUE IGNORE_WIDE_RESIDUE
+
+#define M_X_MODIFY_DP EXTENDED_MODIFY_DATA_POINTER
+#define M_X_SYNC_REQ EXTENDED_SDTR
+#define M_X_WIDE_REQ EXTENDED_WDTR
+#define M_X_PPR_REQ EXTENDED_PPR
+
+/*
+ * PPR protocol options
+ */
+#define PPR_OPT_IU (0x01)
+#define PPR_OPT_DT (0x02)
+#define PPR_OPT_QAS (0x04)
+#define PPR_OPT_MASK (0x07)
+
+/*
+ * Status
+ */
+
+#define S_GOOD SAM_STAT_GOOD
+#define S_CHECK_COND SAM_STAT_CHECK_CONDITION
+#define S_COND_MET SAM_STAT_CONDITION_MET
+#define S_BUSY SAM_STAT_BUSY
+#define S_INT SAM_STAT_INTERMEDIATE
+#define S_INT_COND_MET SAM_STAT_INTERMEDIATE_CONDITION_MET
+#define S_CONFLICT SAM_STAT_RESERVATION_CONFLICT
+#define S_TERMINATED SAM_STAT_COMMAND_TERMINATED
+#define S_QUEUE_FULL SAM_STAT_TASK_SET_FULL
+#define S_ILLEGAL (0xff)
+
+#endif /* defined SYM_DEFS_H */
diff --git a/drivers/scsi/sym53c8xx_2/sym_fw.c b/drivers/scsi/sym53c8xx_2/sym_fw.c
new file mode 100644
index 000000000000..fd36cf9858cb
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_fw.c
@@ -0,0 +1,568 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+/*
+ * Macros used for all firmwares.
+ */
+#define SYM_GEN_A(s, label) ((short) offsetof(s, label)),
+#define SYM_GEN_B(s, label) ((short) offsetof(s, label)),
+#define SYM_GEN_Z(s, label) ((short) offsetof(s, label)),
+#define PADDR_A(label) SYM_GEN_PADDR_A(struct SYM_FWA_SCR, label)
+#define PADDR_B(label) SYM_GEN_PADDR_B(struct SYM_FWB_SCR, label)
+
+
+#if SYM_CONF_GENERIC_SUPPORT
+/*
+ * Allocate firmware #1 script area.
+ */
+#define SYM_FWA_SCR sym_fw1a_scr
+#define SYM_FWB_SCR sym_fw1b_scr
+#define SYM_FWZ_SCR sym_fw1z_scr
+#ifdef __FreeBSD__
+#include <dev/sym/sym_fw1.h>
+#else
+#include "sym_fw1.h"
+#endif
+static struct sym_fwa_ofs sym_fw1a_ofs = {
+ SYM_GEN_FW_A(struct SYM_FWA_SCR)
+};
+static struct sym_fwb_ofs sym_fw1b_ofs = {
+ SYM_GEN_FW_B(struct SYM_FWB_SCR)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ SYM_GEN_B(struct SYM_FWB_SCR, data_io)
+#endif
+};
+static struct sym_fwz_ofs sym_fw1z_ofs = {
+ SYM_GEN_FW_Z(struct SYM_FWZ_SCR)
+};
+#undef SYM_FWA_SCR
+#undef SYM_FWB_SCR
+#undef SYM_FWZ_SCR
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ * Allocate firmware #2 script area.
+ */
+#define SYM_FWA_SCR sym_fw2a_scr
+#define SYM_FWB_SCR sym_fw2b_scr
+#define SYM_FWZ_SCR sym_fw2z_scr
+#ifdef __FreeBSD__
+#include <dev/sym/sym_fw2.h>
+#else
+#include "sym_fw2.h"
+#endif
+static struct sym_fwa_ofs sym_fw2a_ofs = {
+ SYM_GEN_FW_A(struct SYM_FWA_SCR)
+};
+static struct sym_fwb_ofs sym_fw2b_ofs = {
+ SYM_GEN_FW_B(struct SYM_FWB_SCR)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ SYM_GEN_B(struct SYM_FWB_SCR, data_io)
+#endif
+ SYM_GEN_B(struct SYM_FWB_SCR, start64)
+ SYM_GEN_B(struct SYM_FWB_SCR, pm_handle)
+};
+static struct sym_fwz_ofs sym_fw2z_ofs = {
+ SYM_GEN_FW_Z(struct SYM_FWZ_SCR)
+};
+#undef SYM_FWA_SCR
+#undef SYM_FWB_SCR
+#undef SYM_FWZ_SCR
+
+#undef SYM_GEN_A
+#undef SYM_GEN_B
+#undef SYM_GEN_Z
+#undef PADDR_A
+#undef PADDR_B
+
+#if SYM_CONF_GENERIC_SUPPORT
+/*
+ * Patch routine for firmware #1.
+ */
+static void
+sym_fw1_patch(struct sym_hcb *np)
+{
+ struct sym_fw1a_scr *scripta0;
+ struct sym_fw1b_scr *scriptb0;
+
+ scripta0 = (struct sym_fw1a_scr *) np->scripta0;
+ scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;
+
+ /*
+ * Remove LED support if not needed.
+ */
+ if (!(np->features & FE_LED0)) {
+ scripta0->idle[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->start[0] = cpu_to_scr(SCR_NO_OP);
+ }
+
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If user does not want to use IMMEDIATE ARBITRATION
+ * when we are reselected while attempting to arbitrate,
+ * patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+ */
+ if (!SYM_CONF_SET_IARB_ON_ARB_LOST)
+ scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+#endif
+ /*
+ * Patch some data in SCRIPTS.
+ * - start and done queue initial bus address.
+ * - target bus address table bus address.
+ */
+ scriptb0->startpos[0] = cpu_to_scr(np->squeue_ba);
+ scriptb0->done_pos[0] = cpu_to_scr(np->dqueue_ba);
+ scriptb0->targtbl[0] = cpu_to_scr(np->targtbl_ba);
+}
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ * Patch routine for firmware #2.
+ */
+static void
+sym_fw2_patch(struct sym_hcb *np)
+{
+ struct sym_fw2a_scr *scripta0;
+ struct sym_fw2b_scr *scriptb0;
+
+ scripta0 = (struct sym_fw2a_scr *) np->scripta0;
+ scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;
+
+ /*
+ * Remove LED support if not needed.
+ */
+ if (!(np->features & FE_LED0)) {
+ scripta0->idle[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->start[0] = cpu_to_scr(SCR_NO_OP);
+ }
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+ /*
+ * Remove useless 64 bit DMA specific SCRIPTS,
+ * when this feature is not available.
+ */
+ if (!np->use_dac) {
+ scripta0->is_dmap_dirty[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->is_dmap_dirty[1] = 0;
+ scripta0->is_dmap_dirty[2] = cpu_to_scr(SCR_NO_OP);
+ scripta0->is_dmap_dirty[3] = 0;
+ }
+#endif
+
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If user does not want to use IMMEDIATE ARBITRATION
+ * when we are reselected while attempting to arbitrate,
+ * patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+ */
+ if (!SYM_CONF_SET_IARB_ON_ARB_LOST)
+ scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+#endif
+ /*
+ * Patch some variable in SCRIPTS.
+ * - start and done queue initial bus address.
+ * - target bus address table bus address.
+ */
+ scriptb0->startpos[0] = cpu_to_scr(np->squeue_ba);
+ scriptb0->done_pos[0] = cpu_to_scr(np->dqueue_ba);
+ scriptb0->targtbl[0] = cpu_to_scr(np->targtbl_ba);
+
+ /*
+ * Remove the load of SCNTL4 on reselection if not a C10.
+ */
+ if (!(np->features & FE_C10)) {
+ scripta0->resel_scntl4[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->resel_scntl4[1] = cpu_to_scr(0);
+ }
+
+ /*
+ * Remove a couple of work-arounds specific to C1010 if
+ * they are not desirable. See `sym_fw2.h' for more details.
+ */
+ if (!(np->device_id == PCI_DEVICE_ID_LSI_53C1010_66 &&
+ np->revision_id < 0x1 &&
+ np->pciclk_khz < 60000)) {
+ scripta0->datao_phase[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->datao_phase[1] = cpu_to_scr(0);
+ }
+ if (!(np->device_id == PCI_DEVICE_ID_LSI_53C1010_33 &&
+ /* np->revision_id < 0xff */ 1)) {
+ scripta0->sel_done[0] = cpu_to_scr(SCR_NO_OP);
+ scripta0->sel_done[1] = cpu_to_scr(0);
+ }
+
+ /*
+ * Patch some other variables in SCRIPTS.
+ * These ones are loaded by the SCRIPTS processor.
+ */
+ scriptb0->pm0_data_addr[0] =
+ cpu_to_scr(np->scripta_ba +
+ offsetof(struct sym_fw2a_scr, pm0_data));
+ scriptb0->pm1_data_addr[0] =
+ cpu_to_scr(np->scripta_ba +
+ offsetof(struct sym_fw2a_scr, pm1_data));
+}
+
+/*
+ * Fill the data area in scripts.
+ * To be done for all firmwares.
+ */
+static void
+sym_fw_fill_data (u32 *in, u32 *out)
+{
+ int i;
+
+ for (i = 0; i < SYM_CONF_MAX_SG; i++) {
+ *in++ = SCR_CHMOV_TBL ^ SCR_DATA_IN;
+ *in++ = offsetof (struct sym_dsb, data[i]);
+ *out++ = SCR_CHMOV_TBL ^ SCR_DATA_OUT;
+ *out++ = offsetof (struct sym_dsb, data[i]);
+ }
+}
+
+/*
+ * Setup useful script bus addresses.
+ * To be done for all firmwares.
+ */
+static void
+sym_fw_setup_bus_addresses(struct sym_hcb *np, struct sym_fw *fw)
+{
+ u32 *pa;
+ u_short *po;
+ int i;
+
+ /*
+ * Build the bus address table for script A
+ * from the script A offset table.
+ */
+ po = (u_short *) fw->a_ofs;
+ pa = (u32 *) &np->fwa_bas;
+ for (i = 0 ; i < sizeof(np->fwa_bas)/sizeof(u32) ; i++)
+ pa[i] = np->scripta_ba + po[i];
+
+ /*
+ * Same for script B.
+ */
+ po = (u_short *) fw->b_ofs;
+ pa = (u32 *) &np->fwb_bas;
+ for (i = 0 ; i < sizeof(np->fwb_bas)/sizeof(u32) ; i++)
+ pa[i] = np->scriptb_ba + po[i];
+
+ /*
+ * Same for script Z.
+ */
+ po = (u_short *) fw->z_ofs;
+ pa = (u32 *) &np->fwz_bas;
+ for (i = 0 ; i < sizeof(np->fwz_bas)/sizeof(u32) ; i++)
+ pa[i] = np->scriptz_ba + po[i];
+}
+
+#if SYM_CONF_GENERIC_SUPPORT
+/*
+ * Setup routine for firmware #1.
+ */
+static void
+sym_fw1_setup(struct sym_hcb *np, struct sym_fw *fw)
+{
+ struct sym_fw1a_scr *scripta0;
+ struct sym_fw1b_scr *scriptb0;
+
+ scripta0 = (struct sym_fw1a_scr *) np->scripta0;
+ scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;
+
+ /*
+ * Fill variable parts in scripts.
+ */
+ sym_fw_fill_data(scripta0->data_in, scripta0->data_out);
+
+ /*
+ * Setup bus addresses used from the C code..
+ */
+ sym_fw_setup_bus_addresses(np, fw);
+}
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ * Setup routine for firmware #2.
+ */
+static void
+sym_fw2_setup(struct sym_hcb *np, struct sym_fw *fw)
+{
+ struct sym_fw2a_scr *scripta0;
+ struct sym_fw2b_scr *scriptb0;
+
+ scripta0 = (struct sym_fw2a_scr *) np->scripta0;
+ scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;
+
+ /*
+ * Fill variable parts in scripts.
+ */
+ sym_fw_fill_data(scripta0->data_in, scripta0->data_out);
+
+ /*
+ * Setup bus addresses used from the C code..
+ */
+ sym_fw_setup_bus_addresses(np, fw);
+}
+
+/*
+ * Allocate firmware descriptors.
+ */
+#if SYM_CONF_GENERIC_SUPPORT
+static struct sym_fw sym_fw1 = SYM_FW_ENTRY(sym_fw1, "NCR-generic");
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+static struct sym_fw sym_fw2 = SYM_FW_ENTRY(sym_fw2, "LOAD/STORE-based");
+
+/*
+ * Find the most appropriate firmware for a chip.
+ */
+struct sym_fw *
+sym_find_firmware(struct sym_chip *chip)
+{
+ if (chip->features & FE_LDSTR)
+ return &sym_fw2;
+#if SYM_CONF_GENERIC_SUPPORT
+ else if (!(chip->features & (FE_PFEN|FE_NOPM|FE_DAC)))
+ return &sym_fw1;
+#endif
+ else
+ return NULL;
+}
+
+/*
+ * Bind a script to physical addresses.
+ */
+void sym_fw_bind_script(struct sym_hcb *np, u32 *start, int len)
+{
+ u32 opcode, new, old, tmp1, tmp2;
+ u32 *end, *cur;
+ int relocs;
+
+ cur = start;
+ end = start + len/4;
+
+ while (cur < end) {
+
+ opcode = *cur;
+
+ /*
+ * If we forget to change the length
+ * in scripts, a field will be
+ * padded with 0. This is an illegal
+ * command.
+ */
+ if (opcode == 0) {
+ printf ("%s: ERROR0 IN SCRIPT at %d.\n",
+ sym_name(np), (int) (cur-start));
+ ++cur;
+ continue;
+ };
+
+ /*
+ * We use the bogus value 0xf00ff00f ;-)
+ * to reserve data area in SCRIPTS.
+ */
+ if (opcode == SCR_DATA_ZERO) {
+ *cur++ = 0;
+ continue;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_SCRIPT)
+ printf ("%d: <%x>\n", (int) (cur-start),
+ (unsigned)opcode);
+
+ /*
+ * We don't have to decode ALL commands
+ */
+ switch (opcode >> 28) {
+ case 0xf:
+ /*
+ * LOAD / STORE DSA relative, don't relocate.
+ */
+ relocs = 0;
+ break;
+ case 0xe:
+ /*
+ * LOAD / STORE absolute.
+ */
+ relocs = 1;
+ break;
+ case 0xc:
+ /*
+ * COPY has TWO arguments.
+ */
+ relocs = 2;
+ tmp1 = cur[1];
+ tmp2 = cur[2];
+ if ((tmp1 ^ tmp2) & 3) {
+ printf ("%s: ERROR1 IN SCRIPT at %d.\n",
+ sym_name(np), (int) (cur-start));
+ }
+ /*
+ * If PREFETCH feature not enabled, remove
+ * the NO FLUSH bit if present.
+ */
+ if ((opcode & SCR_NO_FLUSH) &&
+ !(np->features & FE_PFEN)) {
+ opcode = (opcode & ~SCR_NO_FLUSH);
+ }
+ break;
+ case 0x0:
+ /*
+ * MOVE/CHMOV (absolute address)
+ */
+ if (!(np->features & FE_WIDE))
+ opcode = (opcode | OPC_MOVE);
+ relocs = 1;
+ break;
+ case 0x1:
+ /*
+ * MOVE/CHMOV (table indirect)
+ */
+ if (!(np->features & FE_WIDE))
+ opcode = (opcode | OPC_MOVE);
+ relocs = 0;
+ break;
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ case 0x2:
+ /*
+ * MOVE/CHMOV in target role (absolute address)
+ */
+ opcode &= ~0x20000000;
+ if (!(np->features & FE_WIDE))
+ opcode = (opcode & ~OPC_TCHMOVE);
+ relocs = 1;
+ break;
+ case 0x3:
+ /*
+ * MOVE/CHMOV in target role (table indirect)
+ */
+ opcode &= ~0x20000000;
+ if (!(np->features & FE_WIDE))
+ opcode = (opcode & ~OPC_TCHMOVE);
+ relocs = 0;
+ break;
+#endif
+ case 0x8:
+ /*
+ * JUMP / CALL
+ * don't relocate if relative :-)
+ */
+ if (opcode & 0x00800000)
+ relocs = 0;
+ else if ((opcode & 0xf8400000) == 0x80400000)/*JUMP64*/
+ relocs = 2;
+ else
+ relocs = 1;
+ break;
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ relocs = 1;
+ break;
+ default:
+ relocs = 0;
+ break;
+ };
+
+ /*
+ * Scriptify:) the opcode.
+ */
+ *cur++ = cpu_to_scr(opcode);
+
+ /*
+ * If no relocation, assume 1 argument
+ * and just scriptize:) it.
+ */
+ if (!relocs) {
+ *cur = cpu_to_scr(*cur);
+ ++cur;
+ continue;
+ }
+
+ /*
+ * Otherwise performs all needed relocations.
+ */
+ while (relocs--) {
+ old = *cur;
+
+ switch (old & RELOC_MASK) {
+ case RELOC_REGISTER:
+ new = (old & ~RELOC_MASK) + np->mmio_ba;
+ break;
+ case RELOC_LABEL_A:
+ new = (old & ~RELOC_MASK) + np->scripta_ba;
+ break;
+ case RELOC_LABEL_B:
+ new = (old & ~RELOC_MASK) + np->scriptb_ba;
+ break;
+ case RELOC_SOFTC:
+ new = (old & ~RELOC_MASK) + np->hcb_ba;
+ break;
+ case 0:
+ /*
+ * Don't relocate a 0 address.
+ * They are mostly used for patched or
+ * script self-modified areas.
+ */
+ if (old == 0) {
+ new = old;
+ break;
+ }
+ /* fall through */
+ default:
+ new = 0;
+ panic("sym_fw_bind_script: "
+ "weird relocation %x\n", old);
+ break;
+ }
+
+ *cur++ = cpu_to_scr(new);
+ }
+ };
+}
diff --git a/drivers/scsi/sym53c8xx_2/sym_fw.h b/drivers/scsi/sym53c8xx_2/sym_fw.h
new file mode 100644
index 000000000000..43f6810a4045
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_fw.h
@@ -0,0 +1,211 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM_FW_H
+#define SYM_FW_H
+/*
+ * Macro used to generate interfaces for script A.
+ */
+#define SYM_GEN_FW_A(s) \
+ SYM_GEN_A(s, start) SYM_GEN_A(s, getjob_begin) \
+ SYM_GEN_A(s, getjob_end) \
+ SYM_GEN_A(s, select) SYM_GEN_A(s, wf_sel_done) \
+ SYM_GEN_A(s, send_ident) \
+ SYM_GEN_A(s, dispatch) SYM_GEN_A(s, init) \
+ SYM_GEN_A(s, clrack) SYM_GEN_A(s, complete_error) \
+ SYM_GEN_A(s, done) SYM_GEN_A(s, done_end) \
+ SYM_GEN_A(s, idle) SYM_GEN_A(s, ungetjob) \
+ SYM_GEN_A(s, reselect) \
+ SYM_GEN_A(s, resel_tag) SYM_GEN_A(s, resel_dsa) \
+ SYM_GEN_A(s, resel_no_tag) \
+ SYM_GEN_A(s, data_in) SYM_GEN_A(s, data_in2) \
+ SYM_GEN_A(s, data_out) SYM_GEN_A(s, data_out2) \
+ SYM_GEN_A(s, pm0_data) SYM_GEN_A(s, pm1_data)
+
+/*
+ * Macro used to generate interfaces for script B.
+ */
+#define SYM_GEN_FW_B(s) \
+ SYM_GEN_B(s, no_data) \
+ SYM_GEN_B(s, sel_for_abort) SYM_GEN_B(s, sel_for_abort_1) \
+ SYM_GEN_B(s, msg_bad) SYM_GEN_B(s, msg_weird) \
+ SYM_GEN_B(s, wdtr_resp) SYM_GEN_B(s, send_wdtr) \
+ SYM_GEN_B(s, sdtr_resp) SYM_GEN_B(s, send_sdtr) \
+ SYM_GEN_B(s, ppr_resp) SYM_GEN_B(s, send_ppr) \
+ SYM_GEN_B(s, nego_bad_phase) \
+ SYM_GEN_B(s, ident_break) SYM_GEN_B(s, ident_break_atn) \
+ SYM_GEN_B(s, sdata_in) SYM_GEN_B(s, resel_bad_lun) \
+ SYM_GEN_B(s, bad_i_t_l) SYM_GEN_B(s, bad_i_t_l_q) \
+ SYM_GEN_B(s, wsr_ma_helper)
+
+/*
+ * Macro used to generate interfaces for script Z.
+ */
+#define SYM_GEN_FW_Z(s) \
+ SYM_GEN_Z(s, snooptest) SYM_GEN_Z(s, snoopend)
+
+/*
+ * Generates structure interface that contains
+ * offsets within script A, B and Z.
+ */
+#define SYM_GEN_A(s, label) s label;
+#define SYM_GEN_B(s, label) s label;
+#define SYM_GEN_Z(s, label) s label;
+struct sym_fwa_ofs {
+ SYM_GEN_FW_A(u_short)
+};
+struct sym_fwb_ofs {
+ SYM_GEN_FW_B(u_short)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ SYM_GEN_B(u_short, data_io)
+#endif
+ SYM_GEN_B(u_short, start64)
+ SYM_GEN_B(u_short, pm_handle)
+};
+struct sym_fwz_ofs {
+ SYM_GEN_FW_Z(u_short)
+};
+
+/*
+ * Generates structure interface that contains
+ * bus addresses within script A, B and Z.
+ */
+struct sym_fwa_ba {
+ SYM_GEN_FW_A(u32)
+};
+struct sym_fwb_ba {
+ SYM_GEN_FW_B(u32)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ SYM_GEN_B(u32, data_io)
+#endif
+ SYM_GEN_B(u32, start64);
+ SYM_GEN_B(u32, pm_handle);
+};
+struct sym_fwz_ba {
+ SYM_GEN_FW_Z(u32)
+};
+#undef SYM_GEN_A
+#undef SYM_GEN_B
+#undef SYM_GEN_Z
+
+/*
+ * Let cc know about the name of the controller data structure.
+ * We need this for function prototype declarations just below.
+ */
+struct sym_hcb;
+
+/*
+ * Generic structure that defines a firmware.
+ */
+struct sym_fw {
+ char *name; /* Name we want to print out */
+ u32 *a_base; /* Pointer to script A template */
+ int a_size; /* Size of script A */
+ struct sym_fwa_ofs
+ *a_ofs; /* Useful offsets in script A */
+ u32 *b_base; /* Pointer to script B template */
+ int b_size; /* Size of script B */
+ struct sym_fwb_ofs
+ *b_ofs; /* Useful offsets in script B */
+ u32 *z_base; /* Pointer to script Z template */
+ int z_size; /* Size of script Z */
+ struct sym_fwz_ofs
+ *z_ofs; /* Useful offsets in script Z */
+ /* Setup and patch methods for this firmware */
+ void (*setup)(struct sym_hcb *, struct sym_fw *);
+ void (*patch)(struct sym_hcb *);
+};
+
+/*
+ * Macro used to declare a firmware.
+ */
+#define SYM_FW_ENTRY(fw, name) \
+{ \
+ name, \
+ (u32 *) &fw##a_scr, sizeof(fw##a_scr), &fw##a_ofs, \
+ (u32 *) &fw##b_scr, sizeof(fw##b_scr), &fw##b_ofs, \
+ (u32 *) &fw##z_scr, sizeof(fw##z_scr), &fw##z_ofs, \
+ fw##_setup, fw##_patch \
+}
+
+/*
+ * Macros used from the C code to get useful
+ * SCRIPTS bus addresses.
+ */
+#define SCRIPTA_BA(np, label) (np->fwa_bas.label)
+#define SCRIPTB_BA(np, label) (np->fwb_bas.label)
+#define SCRIPTZ_BA(np, label) (np->fwz_bas.label)
+
+/*
+ * Macros used by scripts definitions.
+ *
+ * HADDR_1 generates a reference to a field of the controller data.
+ * HADDR_2 generates a reference to a field of the controller data
+ * with offset.
+ * RADDR_1 generates a reference to a script processor register.
+ * RADDR_2 generates a reference to a script processor register
+ * with offset.
+ * PADDR_A generates a reference to another part of script A.
+ * PADDR_B generates a reference to another part of script B.
+ *
+ * SYM_GEN_PADDR_A and SYM_GEN_PADDR_B are used to define respectively
+ * the PADDR_A and PADDR_B macros for each firmware by setting argument
+ * `s' to the name of the corresponding structure.
+ *
+ * SCR_DATA_ZERO is used to allocate a DWORD of data in scripts areas.
+ */
+
+#define RELOC_SOFTC 0x40000000
+#define RELOC_LABEL_A 0x50000000
+#define RELOC_REGISTER 0x60000000
+#define RELOC_LABEL_B 0x80000000
+#define RELOC_MASK 0xf0000000
+
+#define HADDR_1(label) (RELOC_SOFTC | offsetof(struct sym_hcb, label))
+#define HADDR_2(label,ofs) (RELOC_SOFTC | \
+ (offsetof(struct sym_hcb, label)+(ofs)))
+#define RADDR_1(label) (RELOC_REGISTER | REG(label))
+#define RADDR_2(label,ofs) (RELOC_REGISTER | ((REG(label))+(ofs)))
+
+#define SYM_GEN_PADDR_A(s, label) (RELOC_LABEL_A | offsetof(s, label))
+#define SYM_GEN_PADDR_B(s, label) (RELOC_LABEL_B | offsetof(s, label))
+
+#define SCR_DATA_ZERO 0xf00ff00f
+
+#endif /* SYM_FW_H */
diff --git a/drivers/scsi/sym53c8xx_2/sym_fw1.h b/drivers/scsi/sym53c8xx_2/sym_fw1.h
new file mode 100644
index 000000000000..cdd92d82f4b2
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_fw1.h
@@ -0,0 +1,1838 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Scripts for SYMBIOS-Processor
+ *
+ * We have to know the offsets of all labels before we reach
+ * them (for forward jumps). Therefore we declare a struct
+ * here. If you make changes inside the script,
+ *
+ * DONT FORGET TO CHANGE THE LENGTHS HERE!
+ */
+
+/*
+ * Script fragments which are loaded into the on-chip RAM
+ * of 825A, 875, 876, 895, 895A, 896 and 1010 chips.
+ * Must not exceed 4K bytes.
+ */
+struct SYM_FWA_SCR {
+ u32 start [ 11];
+ u32 getjob_begin [ 4];
+ u32 _sms_a10 [ 5];
+ u32 getjob_end [ 4];
+ u32 _sms_a20 [ 4];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ u32 select [ 8];
+#else
+ u32 select [ 6];
+#endif
+ u32 _sms_a30 [ 5];
+ u32 wf_sel_done [ 2];
+ u32 send_ident [ 2];
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 select2 [ 8];
+#else
+ u32 select2 [ 2];
+#endif
+ u32 command [ 2];
+ u32 dispatch [ 28];
+ u32 sel_no_cmd [ 10];
+ u32 init [ 6];
+ u32 clrack [ 4];
+ u32 datai_done [ 11];
+ u32 datai_done_wsr [ 20];
+ u32 datao_done [ 11];
+ u32 datao_done_wss [ 6];
+ u32 datai_phase [ 5];
+ u32 datao_phase [ 5];
+ u32 msg_in [ 2];
+ u32 msg_in2 [ 10];
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 status [ 14];
+#else
+ u32 status [ 10];
+#endif
+ u32 complete [ 6];
+ u32 complete2 [ 8];
+ u32 _sms_a40 [ 12];
+ u32 done [ 5];
+ u32 _sms_a50 [ 5];
+ u32 _sms_a60 [ 2];
+ u32 done_end [ 4];
+ u32 complete_error [ 5];
+ u32 save_dp [ 11];
+ u32 restore_dp [ 7];
+ u32 disconnect [ 11];
+ u32 disconnect2 [ 5];
+ u32 _sms_a65 [ 3];
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 idle [ 4];
+#else
+ u32 idle [ 2];
+#endif
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 ungetjob [ 7];
+#else
+ u32 ungetjob [ 5];
+#endif
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ u32 reselect [ 4];
+#else
+ u32 reselect [ 2];
+#endif
+ u32 reselected [ 19];
+ u32 _sms_a70 [ 6];
+ u32 _sms_a80 [ 4];
+ u32 reselected1 [ 25];
+ u32 _sms_a90 [ 4];
+ u32 resel_lun0 [ 7];
+ u32 _sms_a100 [ 4];
+ u32 resel_tag [ 8];
+#if SYM_CONF_MAX_TASK*4 > 512
+ u32 _sms_a110 [ 23];
+#elif SYM_CONF_MAX_TASK*4 > 256
+ u32 _sms_a110 [ 17];
+#else
+ u32 _sms_a110 [ 13];
+#endif
+ u32 _sms_a120 [ 2];
+ u32 resel_go [ 4];
+ u32 _sms_a130 [ 7];
+ u32 resel_dsa [ 2];
+ u32 resel_dsa1 [ 4];
+ u32 _sms_a140 [ 7];
+ u32 resel_no_tag [ 4];
+ u32 _sms_a145 [ 7];
+ u32 data_in [SYM_CONF_MAX_SG * 2];
+ u32 data_in2 [ 4];
+ u32 data_out [SYM_CONF_MAX_SG * 2];
+ u32 data_out2 [ 4];
+ u32 pm0_data [ 12];
+ u32 pm0_data_out [ 6];
+ u32 pm0_data_end [ 7];
+ u32 pm_data_end [ 4];
+ u32 _sms_a150 [ 4];
+ u32 pm1_data [ 12];
+ u32 pm1_data_out [ 6];
+ u32 pm1_data_end [ 9];
+};
+
+/*
+ * Script fragments which stay in main memory for all chips
+ * except for chips that support 8K on-chip RAM.
+ */
+struct SYM_FWB_SCR {
+ u32 no_data [ 2];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ u32 sel_for_abort [ 18];
+#else
+ u32 sel_for_abort [ 16];
+#endif
+ u32 sel_for_abort_1 [ 2];
+ u32 msg_in_etc [ 12];
+ u32 msg_received [ 5];
+ u32 msg_weird_seen [ 5];
+ u32 msg_extended [ 17];
+ u32 _sms_b10 [ 4];
+ u32 msg_bad [ 6];
+ u32 msg_weird [ 4];
+ u32 msg_weird1 [ 8];
+ u32 wdtr_resp [ 6];
+ u32 send_wdtr [ 4];
+ u32 sdtr_resp [ 6];
+ u32 send_sdtr [ 4];
+ u32 ppr_resp [ 6];
+ u32 send_ppr [ 4];
+ u32 nego_bad_phase [ 4];
+ u32 msg_out [ 4];
+ u32 msg_out_done [ 4];
+ u32 data_ovrun [ 3];
+ u32 data_ovrun1 [ 22];
+ u32 data_ovrun2 [ 8];
+ u32 abort_resel [ 16];
+ u32 resend_ident [ 4];
+ u32 ident_break [ 4];
+ u32 ident_break_atn [ 4];
+ u32 sdata_in [ 6];
+ u32 resel_bad_lun [ 4];
+ u32 bad_i_t_l [ 4];
+ u32 bad_i_t_l_q [ 4];
+ u32 bad_status [ 7];
+ u32 wsr_ma_helper [ 4];
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ /* Unknown direction handling */
+ u32 data_io [ 2];
+ u32 data_io_com [ 8];
+ u32 data_io_out [ 7];
+#endif
+ /* Data area */
+ u32 zero [ 1];
+ u32 scratch [ 1];
+ u32 scratch1 [ 1];
+ u32 prev_done [ 1];
+ u32 done_pos [ 1];
+ u32 nextjob [ 1];
+ u32 startpos [ 1];
+ u32 targtbl [ 1];
+};
+
+/*
+ * Script fragments used at initialisations.
+ * Only runs out of main memory.
+ */
+struct SYM_FWZ_SCR {
+ u32 snooptest [ 9];
+ u32 snoopend [ 2];
+};
+
+static struct SYM_FWA_SCR SYM_FWA_SCR = {
+/*--------------------------< START >----------------------------*/ {
+ /*
+ * Switch the LED on.
+ * Will be patched with a NO_OP if LED
+ * not needed or not desired.
+ */
+ SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+ 0,
+ /*
+ * Clear SIGP.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ /*
+ * Stop here if the C code wants to perform
+ * some error recovery procedure manually.
+ * (Indicate this by setting SEM in ISTAT)
+ */
+ SCR_FROM_REG (istat),
+ 0,
+ /*
+ * Report to the C code the next position in
+ * the start queue the SCRIPTS will schedule.
+ * The C code must not change SCRATCHA.
+ */
+ SCR_COPY (4),
+ PADDR_B (startpos),
+ RADDR_1 (scratcha),
+ SCR_INT ^ IFTRUE (MASK (SEM, SEM)),
+ SIR_SCRIPT_STOPPED,
+ /*
+ * Start the next job.
+ *
+ * @DSA = start point for this job.
+ * SCRATCHA = address of this job in the start queue.
+ *
+ * We will restore startpos with SCRATCHA if we fails the
+ * arbitration or if it is the idle job.
+ *
+ * The below GETJOB_BEGIN to GETJOB_END section of SCRIPTS
+ * is a critical path. If it is partially executed, it then
+ * may happen that the job address is not yet in the DSA
+ * and the next queue position points to the next JOB.
+ */
+}/*-------------------------< GETJOB_BEGIN >---------------------*/,{
+ /*
+ * Copy to a fixed location both the next STARTPOS
+ * and the current JOB address, using self modifying
+ * SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (scratcha),
+ PADDR_A (_sms_a10),
+ SCR_COPY (8),
+}/*-------------------------< _SMS_A10 >-------------------------*/,{
+ 0,
+ PADDR_B (nextjob),
+ /*
+ * Move the start address to TEMP using self-
+ * modifying SCRIPTS and jump indirectly to
+ * that address.
+ */
+ SCR_COPY (4),
+ PADDR_B (nextjob),
+ RADDR_1 (dsa),
+}/*-------------------------< GETJOB_END >-----------------------*/,{
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a20),
+ SCR_COPY (4),
+}/*-------------------------< _SMS_A20 >-------------------------*/,{
+ 0,
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< SELECT >---------------------------*/,{
+ /*
+ * DSA contains the address of a scheduled
+ * data structure.
+ *
+ * SCRATCHA contains the address of the start queue
+ * entry which points to the next job.
+ *
+ * Set Initiator mode.
+ *
+ * (Target mode is left as an exercise for the reader)
+ */
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ SCR_CLR (SCR_TRG),
+ 0,
+#endif
+ /*
+ * And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct sym_dsb, select),
+ PADDR_A (ungetjob),
+ /*
+ * Now there are 4 possibilities:
+ *
+ * (1) The chip loses arbitration.
+ * This is ok, because it will try again,
+ * when the bus becomes idle.
+ * (But beware of the timeout function!)
+ *
+ * (2) The chip is reselected.
+ * Then the script processor takes the jump
+ * to the RESELECT label.
+ *
+ * (3) The chip wins arbitration.
+ * Then it will execute SCRIPTS instruction until
+ * the next instruction that checks SCSI phase.
+ * Then will stop and wait for selection to be
+ * complete or selection time-out to occur.
+ *
+ * After having won arbitration, the SCRIPTS
+ * processor is able to execute instructions while
+ * the SCSI core is performing SCSI selection.
+ */
+
+ /*
+ * Copy the CCB header to a fixed location
+ * in the HCB using self-modifying SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a30),
+ SCR_COPY (sizeof(struct sym_ccbh)),
+}/*-------------------------< _SMS_A30 >-------------------------*/,{
+ 0,
+ HADDR_1 (ccb_head),
+ /*
+ * Initialize the status register
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.status),
+ RADDR_1 (scr0),
+}/*-------------------------< WF_SEL_DONE >----------------------*/,{
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_SEL_ATN_NO_MSG_OUT,
+}/*-------------------------< SEND_IDENT >-----------------------*/,{
+ /*
+ * Selection complete.
+ * Send the IDENTIFY and possibly the TAG message
+ * and negotiation message if present.
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct sym_dsb, smsg),
+}/*-------------------------< SELECT2 >--------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * Set IMMEDIATE ARBITRATION if we have been given
+ * a hint to do so. (Some job to do after this one).
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ SCR_JUMPR ^ IFFALSE (MASK (HF_HINT_IARB, HF_HINT_IARB)),
+ 8,
+ SCR_REG_REG (scntl1, SCR_OR, IARB),
+ 0,
+#endif
+ /*
+ * Anticipate the COMMAND phase.
+ * This is the PHASE we expect at this point.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)),
+ PADDR_A (sel_no_cmd),
+}/*-------------------------< COMMAND >--------------------------*/,{
+ /*
+ * ... and send the command
+ */
+ SCR_MOVE_TBL ^ SCR_COMMAND,
+ offsetof (struct sym_dsb, cmd),
+}/*-------------------------< DISPATCH >-------------------------*/,{
+ /*
+ * MSG_IN is the only phase that shall be
+ * entered at least once for each (re)selection.
+ * So we test it first.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (msg_in),
+ SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT)),
+ PADDR_A (datao_phase),
+ SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN)),
+ PADDR_A (datai_phase),
+ SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+ PADDR_A (status),
+ SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+ PADDR_A (command),
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+ PADDR_B (msg_out),
+ /*
+ * Discard as many illegal phases as
+ * required and tell the C code about.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_OUT)),
+ 16,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+ HADDR_1 (scratch),
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_OUT)),
+ -16,
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_IN)),
+ 16,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+ HADDR_1 (scratch),
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_IN)),
+ -16,
+ SCR_INT,
+ SIR_BAD_PHASE,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< SEL_NO_CMD >-----------------------*/,{
+ /*
+ * The target does not switch to command
+ * phase after IDENTIFY has been sent.
+ *
+ * If it stays in MSG OUT phase send it
+ * the IDENTIFY again.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (resend_ident),
+ /*
+ * If target does not switch to MSG IN phase
+ * and we sent a negotiation, assert the
+ * failure immediately.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (dispatch),
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+ /*
+ * Jump to dispatcher.
+ */
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< INIT >-----------------------------*/,{
+ /*
+ * Wait for the SCSI RESET signal to be
+ * inactive before restarting operations,
+ * since the chip may hang on SEL_ATN
+ * if SCSI RESET is active.
+ */
+ SCR_FROM_REG (sstat0),
+ 0,
+ SCR_JUMPR ^ IFTRUE (MASK (IRST, IRST)),
+ -16,
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< CLRACK >---------------------------*/,{
+ /*
+ * Terminate possible pending message phase.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE >-----------------------*/,{
+ /*
+ * Save current pointer to LASTP.
+ */
+ SCR_COPY (4),
+ RADDR_1 (temp),
+ HADDR_1 (ccb_head.lastp),
+ /*
+ * If the SWIDE is not full, jump to dispatcher.
+ * We anticipate a STATUS phase.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (WSR, WSR)),
+ PADDR_A (datai_done_wsr),
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+ PADDR_A (status),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE_WSR >-------------------*/,{
+ /*
+ * The SWIDE is full.
+ * Clear this condition.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ /*
+ * We are expecting an IGNORE RESIDUE message
+ * from the device, otherwise we are in data
+ * overrun condition. Check against MSG_IN phase.
+ */
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ SIR_SWIDE_OVERRUN,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR_A (dispatch),
+ /*
+ * We are in MSG_IN phase,
+ * Read the first byte of the message.
+ * If it is not an IGNORE RESIDUE message,
+ * signal overrun and jump to message
+ * processing.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[0]),
+ SCR_INT ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+ SIR_SWIDE_OVERRUN,
+ SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+ PADDR_A (msg_in2),
+ /*
+ * We got the message we expected.
+ * Read the 2nd byte, and jump to dispatcher.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[1]),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE >-----------------------*/,{
+ /*
+ * Save current pointer to LASTP.
+ */
+ SCR_COPY (4),
+ RADDR_1 (temp),
+ HADDR_1 (ccb_head.lastp),
+ /*
+ * If the SODL is not full jump to dispatcher.
+ * We anticipate a STATUS phase.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (WSS, WSS)),
+ PADDR_A (datao_done_wss),
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+ PADDR_A (status),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE_WSS >-------------------*/,{
+ /*
+ * The SODL is full, clear this condition.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSS),
+ 0,
+ /*
+ * And signal a DATA UNDERRUN condition
+ * to the C code.
+ */
+ SCR_INT,
+ SIR_SODL_UNDERRUN,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAI_PHASE >----------------------*/,{
+ /*
+ * Jump to current pointer.
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.lastp),
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< DATAO_PHASE >----------------------*/,{
+ /*
+ * Jump to current pointer.
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.lastp),
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< MSG_IN >---------------------------*/,{
+ /*
+ * Get the first byte of the message.
+ *
+ * The script processor doesn't negate the
+ * ACK signal after this transfer.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[0]),
+}/*-------------------------< MSG_IN2 >--------------------------*/,{
+ /*
+ * Check first against 1 byte messages
+ * that we handle from SCRIPTS.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+ PADDR_A (complete),
+ SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+ PADDR_A (disconnect),
+ SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+ PADDR_A (save_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+ PADDR_A (restore_dp),
+ /*
+ * We handle all other messages from the
+ * C code, so no need to waste on-chip RAM
+ * for those ones.
+ */
+ SCR_JUMP,
+ PADDR_B (msg_in_etc),
+}/*-------------------------< STATUS >---------------------------*/,{
+ /*
+ * get the status
+ */
+ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ HADDR_1 (scratch),
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If STATUS is not GOOD, clear IMMEDIATE ARBITRATION,
+ * since we may have to tamper the start queue from
+ * the C code.
+ */
+ SCR_JUMPR ^ IFTRUE (DATA (S_GOOD)),
+ 8,
+ SCR_REG_REG (scntl1, SCR_AND, ~IARB),
+ 0,
+#endif
+ /*
+ * save status to scsi_status.
+ * mark as complete.
+ */
+ SCR_TO_REG (SS_REG),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+ 0,
+ /*
+ * Anticipate the MESSAGE PHASE for
+ * the TASK COMPLETE message.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (msg_in),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< COMPLETE >-------------------------*/,{
+ /*
+ * Complete message.
+ *
+ * When we terminate the cycle by clearing ACK,
+ * the target may disconnect immediately.
+ *
+ * We don't want to be told of an "unexpected disconnect",
+ * so we disable this feature.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ /*
+ * Terminate cycle ...
+ */
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ * ... and wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+}/*-------------------------< COMPLETE2 >------------------------*/,{
+ /*
+ * Save host status.
+ */
+ SCR_COPY (4),
+ RADDR_1 (scr0),
+ HADDR_1 (ccb_head.status),
+ /*
+ * Move back the CCB header using self-modifying
+ * SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a40),
+ SCR_COPY (sizeof(struct sym_ccbh)),
+ HADDR_1 (ccb_head),
+}/*-------------------------< _SMS_A40 >-------------------------*/,{
+ 0,
+ /*
+ * Some bridges may reorder DMA writes to memory.
+ * We donnot want the CPU to deal with completions
+ * without all the posted write having been flushed
+ * to memory. This DUMMY READ should flush posted
+ * buffers prior to the CPU having to deal with
+ * completions.
+ */
+ SCR_COPY (4), /* DUMMY READ */
+ HADDR_1 (ccb_head.status),
+ RADDR_1 (scr0),
+ /*
+ * If command resulted in not GOOD status,
+ * call the C code if needed.
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+ PADDR_B (bad_status),
+ /*
+ * If we performed an auto-sense, call
+ * the C code to synchronyze task aborts
+ * with UNIT ATTENTION conditions.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ SCR_JUMP ^ IFFALSE (MASK (0 ,(HF_SENSE|HF_EXT_ERR))),
+ PADDR_A (complete_error),
+}/*-------------------------< DONE >-----------------------------*/,{
+ /*
+ * Copy the DSA to the DONE QUEUE and
+ * signal completion to the host.
+ * If we are interrupted between DONE
+ * and DONE_END, we must reset, otherwise
+ * the completed CCB may be lost.
+ */
+ SCR_COPY (4),
+ PADDR_B (done_pos),
+ PADDR_A (_sms_a50),
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+}/*-------------------------< _SMS_A50 >-------------------------*/,{
+ 0,
+ SCR_COPY (4),
+ PADDR_B (done_pos),
+ PADDR_A (_sms_a60),
+ /*
+ * The instruction below reads the DONE QUEUE next
+ * free position from memory.
+ * In addition it ensures that all PCI posted writes
+ * are flushed and so the DSA value of the done
+ * CCB is visible by the CPU before INTFLY is raised.
+ */
+ SCR_COPY (8),
+}/*-------------------------< _SMS_A60 >-------------------------*/,{
+ 0,
+ PADDR_B (prev_done),
+}/*-------------------------< DONE_END >-------------------------*/,{
+ SCR_INT_FLY,
+ 0,
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< COMPLETE_ERROR >-------------------*/,{
+ SCR_COPY (4),
+ PADDR_B (startpos),
+ RADDR_1 (scratcha),
+ SCR_INT,
+ SIR_COMPLETE_ERROR,
+}/*-------------------------< SAVE_DP >--------------------------*/,{
+ /*
+ * Clear ACK immediately.
+ * No need to delay it.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * Keep track we received a SAVE DP, so
+ * we will switch to the other PM context
+ * on the next PM since the DP may point
+ * to the current PM context.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_DP_SAVED),
+ 0,
+ /*
+ * SAVE_DP message:
+ * Copy LASTP to SAVEP.
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.lastp),
+ HADDR_1 (ccb_head.savep),
+ /*
+ * Anticipate the MESSAGE PHASE for
+ * the DISCONNECT message.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (msg_in),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< RESTORE_DP >-----------------------*/,{
+ /*
+ * Clear ACK immediately.
+ * No need to delay it.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * Copy SAVEP to LASTP.
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.savep),
+ HADDR_1 (ccb_head.lastp),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DISCONNECT >-----------------------*/,{
+ /*
+ * DISCONNECTing ...
+ *
+ * disable the "unexpected disconnect" feature,
+ * and remove the ACK signal.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ * Wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ * Status is: DISCONNECTED.
+ */
+ SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+ 0,
+ /*
+ * Save host status.
+ */
+ SCR_COPY (4),
+ RADDR_1 (scr0),
+ HADDR_1 (ccb_head.status),
+}/*-------------------------< DISCONNECT2 >----------------------*/,{
+ /*
+ * Move back the CCB header using self-modifying
+ * SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a65),
+ SCR_COPY (sizeof(struct sym_ccbh)),
+ HADDR_1 (ccb_head),
+}/*-------------------------< _SMS_A65 >-------------------------*/,{
+ 0,
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< IDLE >-----------------------------*/,{
+ /*
+ * Nothing to do?
+ * Switch the LED off and wait for reselect.
+ * Will be patched with a NO_OP if LED
+ * not needed or not desired.
+ */
+ SCR_REG_REG (gpreg, SCR_OR, 0x01),
+ 0,
+#ifdef SYM_CONF_IARB_SUPPORT
+ SCR_JUMPR,
+ 8,
+#endif
+}/*-------------------------< UNGETJOB >-------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * Set IMMEDIATE ARBITRATION, for the next time.
+ * This will give us better chance to win arbitration
+ * for the job we just wanted to do.
+ */
+ SCR_REG_REG (scntl1, SCR_OR, IARB),
+ 0,
+#endif
+ /*
+ * We are not able to restart the SCRIPTS if we are
+ * interrupted and these instruction haven't been
+ * all executed. BTW, this is very unlikely to
+ * happen, but we check that from the C code.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_COPY (4),
+ RADDR_1 (scratcha),
+ PADDR_B (startpos),
+}/*-------------------------< RESELECT >-------------------------*/,{
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ /*
+ * Make sure we are in initiator mode.
+ */
+ SCR_CLR (SCR_TRG),
+ 0,
+#endif
+ /*
+ * Sleep waiting for a reselection.
+ */
+ SCR_WAIT_RESEL,
+ PADDR_A(start),
+}/*-------------------------< RESELECTED >-----------------------*/,{
+ /*
+ * Switch the LED on.
+ * Will be patched with a NO_OP if LED
+ * not needed or not desired.
+ */
+ SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+ 0,
+ /*
+ * load the target id into the sdid
+ */
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+ 0,
+ SCR_TO_REG (sdid),
+ 0,
+ /*
+ * Load the target control block address
+ */
+ SCR_COPY (4),
+ PADDR_B (targtbl),
+ RADDR_1 (dsa),
+ SCR_SFBR_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_AND, 0x3c),
+ 0,
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a70),
+ SCR_COPY (4),
+}/*-------------------------< _SMS_A70 >-------------------------*/,{
+ 0,
+ RADDR_1 (dsa),
+ /*
+ * Copy the TCB header to a fixed place in
+ * the HCB.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a80),
+ SCR_COPY (sizeof(struct sym_tcbh)),
+}/*-------------------------< _SMS_A80 >-------------------------*/,{
+ 0,
+ HADDR_1 (tcb_head),
+ /*
+ * We expect MESSAGE IN phase.
+ * If not, get help from the C code.
+ */
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ SIR_RESEL_NO_MSG_IN,
+}/*-------------------------< RESELECTED1 >----------------------*/,{
+ /*
+ * Load the synchronous transfer registers.
+ */
+ SCR_COPY (1),
+ HADDR_1 (tcb_head.wval),
+ RADDR_1 (scntl3),
+ SCR_COPY (1),
+ HADDR_1 (tcb_head.sval),
+ RADDR_1 (sxfer),
+ /*
+ * Get the IDENTIFY message.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin),
+ /*
+ * If IDENTIFY LUN #0, use a faster path
+ * to find the LCB structure.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0x80, 0xbf)),
+ PADDR_A (resel_lun0),
+ /*
+ * If message isn't an IDENTIFY,
+ * tell the C code about.
+ */
+ SCR_INT ^ IFFALSE (MASK (0x80, 0x80)),
+ SIR_RESEL_NO_IDENTIFY,
+ /*
+ * It is an IDENTIFY message,
+ * Load the LUN control block address.
+ */
+ SCR_COPY (4),
+ HADDR_1 (tcb_head.luntbl_sa),
+ RADDR_1 (dsa),
+ SCR_SFBR_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_AND, 0xfc),
+ 0,
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a90),
+ SCR_COPY (4),
+}/*-------------------------< _SMS_A90 >-------------------------*/,{
+ 0,
+ RADDR_1 (dsa),
+ SCR_JUMPR,
+ 12,
+}/*-------------------------< RESEL_LUN0 >-----------------------*/,{
+ /*
+ * LUN 0 special case (but usual one :))
+ */
+ SCR_COPY (4),
+ HADDR_1 (tcb_head.lun0_sa),
+ RADDR_1 (dsa),
+ /*
+ * Jump indirectly to the reselect action for this LUN.
+ * (lcb.head.resel_sa assumed at offset zero of lcb).
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a100),
+ SCR_COPY (4),
+}/*-------------------------< _SMS_A100 >------------------------*/,{
+ 0,
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+ /* In normal situations, we jump to RESEL_TAG or RESEL_NO_TAG */
+}/*-------------------------< RESEL_TAG >------------------------*/,{
+ /*
+ * ACK the IDENTIFY previously received.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * It shall be a tagged command.
+ * Read SIMPLE+TAG.
+ * The C code will deal with errors.
+ * Agressive optimization, is'nt it? :)
+ */
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ HADDR_1 (msgin),
+ /*
+ * Copy the LCB header to a fixed place in
+ * the HCB using self-modifying SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a110),
+ SCR_COPY (sizeof(struct sym_lcbh)),
+}/*-------------------------< _SMS_A110 >------------------------*/,{
+ 0,
+ HADDR_1 (lcb_head),
+ /*
+ * Load the pointer to the tagged task
+ * table for this LUN.
+ */
+ SCR_COPY (4),
+ HADDR_1 (lcb_head.itlq_tbl_sa),
+ RADDR_1 (dsa),
+ /*
+ * The SIDL still contains the TAG value.
+ * Agressive optimization, isn't it? :):)
+ */
+ SCR_REG_SFBR (sidl, SCR_SHL, 0),
+ 0,
+#if SYM_CONF_MAX_TASK*4 > 512
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+ SCR_REG_REG (dsa1, SCR_OR, 2),
+ 0,
+ SCR_REG_REG (sfbr, SCR_SHL, 0),
+ 0,
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+ SCR_REG_REG (dsa1, SCR_OR, 1),
+ 0,
+#elif SYM_CONF_MAX_TASK*4 > 256
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+ SCR_REG_REG (dsa1, SCR_OR, 1),
+ 0,
+#endif
+ /*
+ * Retrieve the DSA of this task.
+ * JUMP indirectly to the restart point of the CCB.
+ */
+ SCR_SFBR_REG (dsa, SCR_AND, 0xfc),
+ 0,
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a120),
+ SCR_COPY (4),
+}/*-------------------------< _SMS_A120 >------------------------*/,{
+ 0,
+ RADDR_1 (dsa),
+}/*-------------------------< RESEL_GO >-------------------------*/,{
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a130),
+ /*
+ * Move 'ccb.phys.head.go' action to
+ * scratch/scratch1. So scratch1 will
+ * contain the 'restart' field of the
+ * 'go' structure.
+ */
+ SCR_COPY (8),
+}/*-------------------------< _SMS_A130 >------------------------*/,{
+ 0,
+ PADDR_B (scratch),
+ SCR_COPY (4),
+ PADDR_B (scratch1), /* phys.head.go.restart */
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+ /* In normal situations we branch to RESEL_DSA */
+}/*-------------------------< RESEL_DSA >------------------------*/,{
+ /*
+ * ACK the IDENTIFY or TAG previously received.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+}/*-------------------------< RESEL_DSA1 >-----------------------*/,{
+ /*
+ * Copy the CCB header to a fixed location
+ * in the HCB using self-modifying SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a140),
+ SCR_COPY (sizeof(struct sym_ccbh)),
+}/*-------------------------< _SMS_A140 >------------------------*/,{
+ 0,
+ HADDR_1 (ccb_head),
+ /*
+ * Initialize the status register
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.status),
+ RADDR_1 (scr0),
+ /*
+ * Jump to dispatcher.
+ */
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< RESEL_NO_TAG >---------------------*/,{
+ /*
+ * Copy the LCB header to a fixed place in
+ * the HCB using self-modifying SCRIPTS.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ PADDR_A (_sms_a145),
+ SCR_COPY (sizeof(struct sym_lcbh)),
+}/*-------------------------< _SMS_A145 >------------------------*/,{
+ 0,
+ HADDR_1 (lcb_head),
+ /*
+ * Load the DSA with the unique ITL task.
+ */
+ SCR_COPY (4),
+ HADDR_1 (lcb_head.itl_task_sa),
+ RADDR_1 (dsa),
+ SCR_JUMP,
+ PADDR_A (resel_go),
+}/*-------------------------< DATA_IN >--------------------------*/,{
+/*
+ * Because the size depends on the
+ * #define SYM_CONF_MAX_SG parameter,
+ * it is filled in at runtime.
+ *
+ * ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ * || SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ * || offsetof (struct sym_dsb, data[ i]),
+ * ##==========================================
+ */
+0
+}/*-------------------------< DATA_IN2 >-------------------------*/,{
+ SCR_CALL,
+ PADDR_A (datai_done),
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< DATA_OUT >-------------------------*/,{
+/*
+ * Because the size depends on the
+ * #define SYM_CONF_MAX_SG parameter,
+ * it is filled in at runtime.
+ *
+ * ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ * || SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ * || offsetof (struct sym_dsb, data[ i]),
+ * ##==========================================
+ */
+0
+}/*-------------------------< DATA_OUT2 >------------------------*/,{
+ SCR_CALL,
+ PADDR_A (datao_done),
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< PM0_DATA >-------------------------*/,{
+ /*
+ * Read our host flags to SFBR, so we will be able
+ * to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ * Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR_A (pm0_data_out),
+ /*
+ * Actual phase is DATA IN.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+ 0,
+ /*
+ * Move the data to memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.pm0.sg),
+ SCR_JUMP,
+ PADDR_A (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >---------------------*/,{
+ /*
+ * Actual phase is DATA OUT.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+ 0,
+ /*
+ * Move the data from memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ offsetof (struct sym_ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >---------------------*/,{
+ /*
+ * Clear the flag that told we were moving
+ * data from the PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
+ 0,
+ /*
+ * Return to the previous DATA script which
+ * is guaranteed by design (if no bug) to be
+ * the main DATA script for this transfer.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ RADDR_1 (scratcha),
+ SCR_REG_REG (scratcha, SCR_ADD, offsetof (struct sym_ccb,phys.pm0.ret)),
+ 0,
+}/*-------------------------< PM_DATA_END >----------------------*/,{
+ SCR_COPY (4),
+ RADDR_1 (scratcha),
+ PADDR_A (_sms_a150),
+ SCR_COPY (4),
+}/*-------------------------< _SMS_A150 >------------------------*/,{
+ 0,
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< PM1_DATA >-------------------------*/,{
+ /*
+ * Read our host flags to SFBR, so we will be able
+ * to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ * Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR_A (pm1_data_out),
+ /*
+ * Actual phase is DATA IN.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+ 0,
+ /*
+ * Move the data to memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.pm1.sg),
+ SCR_JUMP,
+ PADDR_A (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >---------------------*/,{
+ /*
+ * Actual phase is DATA OUT.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+ 0,
+ /*
+ * Move the data from memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ offsetof (struct sym_ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >---------------------*/,{
+ /*
+ * Clear the flag that told we were moving
+ * data from the PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
+ 0,
+ /*
+ * Return to the previous DATA script which
+ * is guaranteed by design (if no bug) to be
+ * the main DATA script for this transfer.
+ */
+ SCR_COPY (4),
+ RADDR_1 (dsa),
+ RADDR_1 (scratcha),
+ SCR_REG_REG (scratcha, SCR_ADD, offsetof (struct sym_ccb,phys.pm1.ret)),
+ 0,
+ SCR_JUMP,
+ PADDR_A (pm_data_end),
+}/*--------------------------<>----------------------------------*/
+};
+
+static struct SYM_FWB_SCR SYM_FWB_SCR = {
+/*-------------------------< NO_DATA >--------------------------*/ {
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< SEL_FOR_ABORT >--------------------*/,{
+ /*
+ * We are jumped here by the C code, if we have
+ * some target to reset or some disconnected
+ * job to abort. Since error recovery is a serious
+ * busyness, we will really reset the SCSI BUS, if
+ * case of a SCSI interrupt occurring in this path.
+ */
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ /*
+ * Set initiator mode.
+ */
+ SCR_CLR (SCR_TRG),
+ 0,
+#endif
+ /*
+ * And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct sym_hcb, abrt_sel),
+ PADDR_A (reselect),
+ /*
+ * Wait for the selection to complete or
+ * the selection to time out.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ -8,
+ /*
+ * Call the C code.
+ */
+ SCR_INT,
+ SIR_TARGET_SELECTED,
+ /*
+ * The C code should let us continue here.
+ * Send the 'kiss of death' message.
+ * We expect an immediate disconnect once
+ * the target has eaten the message.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct sym_hcb, abrt_tbl),
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ * Tell the C code that we are done.
+ */
+ SCR_INT,
+ SIR_ABORT_SENT,
+}/*-------------------------< SEL_FOR_ABORT_1 >------------------*/,{
+ /*
+ * Jump at scheduler.
+ */
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< MSG_IN_ETC >-----------------------*/,{
+ /*
+ * If it is an EXTENDED (variable size message)
+ * Handle it.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+ PADDR_B (msg_extended),
+ /*
+ * Let the C code handle any other
+ * 1 byte message.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0x00, 0xf0)),
+ PADDR_B (msg_received),
+ SCR_JUMP ^ IFTRUE (MASK (0x10, 0xf0)),
+ PADDR_B (msg_received),
+ /*
+ * We donnot handle 2 bytes messages from SCRIPTS.
+ * So, let the C code deal with these ones too.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (0x20, 0xf0)),
+ PADDR_B (msg_weird_seen),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[1]),
+}/*-------------------------< MSG_RECEIVED >---------------------*/,{
+ SCR_COPY (4), /* DUMMY READ */
+ HADDR_1 (scratch),
+ RADDR_1 (scratcha),
+ SCR_INT,
+ SIR_MSG_RECEIVED,
+}/*-------------------------< MSG_WEIRD_SEEN >-------------------*/,{
+ SCR_COPY (4), /* DUMMY READ */
+ HADDR_1 (scratch),
+ RADDR_1 (scratcha),
+ SCR_INT,
+ SIR_MSG_WEIRD,
+}/*-------------------------< MSG_EXTENDED >---------------------*/,{
+ /*
+ * Clear ACK and get the next byte
+ * assumed to be the message length.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[1]),
+ /*
+ * Try to catch some unlikely situations as 0 length
+ * or too large the length.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (0)),
+ PADDR_B (msg_weird_seen),
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_REG_REG (sfbr, SCR_ADD, (256-8)),
+ 0,
+ SCR_JUMP ^ IFTRUE (CARRYSET),
+ PADDR_B (msg_weird_seen),
+ /*
+ * We donnot handle extended messages from SCRIPTS.
+ * Read the amount of data correponding to the
+ * message length and call the C code.
+ */
+ SCR_COPY (1),
+ RADDR_1 (scratcha),
+ PADDR_B (_sms_b10),
+ SCR_CLR (SCR_ACK),
+ 0,
+}/*-------------------------< _SMS_B10 >-------------------------*/,{
+ SCR_MOVE_ABS (0) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[2]),
+ SCR_JUMP,
+ PADDR_B (msg_received),
+}/*-------------------------< MSG_BAD >--------------------------*/,{
+ /*
+ * unimplemented message - reject it.
+ */
+ SCR_INT,
+ SIR_REJECT_TO_SEND,
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR_A (clrack),
+}/*-------------------------< MSG_WEIRD >------------------------*/,{
+ /*
+ * weird message received
+ * ignore all MSG IN phases and reject it.
+ */
+ SCR_INT,
+ SIR_REJECT_TO_SEND,
+ SCR_SET (SCR_ATN),
+ 0,
+}/*-------------------------< MSG_WEIRD1 >-----------------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR_A (dispatch),
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (scratch),
+ SCR_JUMP,
+ PADDR_B (msg_weird1),
+}/*-------------------------< WDTR_RESP >------------------------*/,{
+ /*
+ * let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_WDTR >------------------------*/,{
+ /*
+ * Send the M_X_WIDE_REQ
+ */
+ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_JUMP,
+ PADDR_B (msg_out_done),
+}/*-------------------------< SDTR_RESP >------------------------*/,{
+ /*
+ * let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_SDTR >------------------------*/,{
+ /*
+ * Send the M_X_SYNC_REQ
+ */
+ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_JUMP,
+ PADDR_B (msg_out_done),
+}/*-------------------------< PPR_RESP >-------------------------*/,{
+ /*
+ * let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_PPR >-------------------------*/,{
+ /*
+ * Send the M_X_PPR_REQ
+ */
+ SCR_MOVE_ABS (8) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_JUMP,
+ PADDR_B (msg_out_done),
+}/*-------------------------< NEGO_BAD_PHASE >-------------------*/,{
+ SCR_INT,
+ SIR_NEGO_PROTO,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< MSG_OUT >--------------------------*/,{
+ /*
+ * The target requests a message.
+ * We donnot send messages that may
+ * require the device to go to bus free.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ /*
+ * ... wait for the next phase
+ * if it's a message out, send it again, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (msg_out),
+}/*-------------------------< MSG_OUT_DONE >---------------------*/,{
+ /*
+ * Let the C code be aware of the
+ * sent message and clear the message.
+ */
+ SCR_INT,
+ SIR_MSG_OUT_DONE,
+ /*
+ * ... and process the next phase
+ */
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATA_OVRUN >-----------------------*/,{
+ /*
+ * Zero scratcha that will count the
+ * extras bytes.
+ */
+ SCR_COPY (4),
+ PADDR_B (zero),
+ RADDR_1 (scratcha),
+}/*-------------------------< DATA_OVRUN1 >----------------------*/,{
+ /*
+ * The target may want to transfer too much data.
+ *
+ * If phase is DATA OUT write 1 byte and count it.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+ 16,
+ SCR_CHMOV_ABS (1) ^ SCR_DATA_OUT,
+ HADDR_1 (scratch),
+ SCR_JUMP,
+ PADDR_B (data_ovrun2),
+ /*
+ * If WSR is set, clear this condition, and
+ * count this byte.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+ 16,
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ SCR_JUMP,
+ PADDR_B (data_ovrun2),
+ /*
+ * Finally check against DATA IN phase.
+ * Signal data overrun to the C code
+ * and jump to dispatcher if not so.
+ * Read 1 byte otherwise and count it.
+ */
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_IN)),
+ 16,
+ SCR_INT,
+ SIR_DATA_OVERRUN,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+ SCR_CHMOV_ABS (1) ^ SCR_DATA_IN,
+ HADDR_1 (scratch),
+}/*-------------------------< DATA_OVRUN2 >----------------------*/,{
+ /*
+ * Count this byte.
+ * This will allow to return a negative
+ * residual to user.
+ */
+ SCR_REG_REG (scratcha, SCR_ADD, 0x01),
+ 0,
+ SCR_REG_REG (scratcha1, SCR_ADDC, 0),
+ 0,
+ SCR_REG_REG (scratcha2, SCR_ADDC, 0),
+ 0,
+ /*
+ * .. and repeat as required.
+ */
+ SCR_JUMP,
+ PADDR_B (data_ovrun1),
+}/*-------------------------< ABORT_RESEL >----------------------*/,{
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * send the abort/abortag/reset message
+ * we expect an immediate disconnect
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ SCR_INT,
+ SIR_RESEL_ABORTED,
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< RESEND_IDENT >---------------------*/,{
+ /*
+ * The target stays in MSG OUT phase after having acked
+ * Identify [+ Tag [+ Extended message ]]. Targets shall
+ * behave this way on parity error.
+ * We must send it again all the messages.
+ */
+ SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the */
+ 0, /* 1rst ACK = 90 ns. Hope the chip isn't too fast */
+ SCR_JUMP,
+ PADDR_A (send_ident),
+}/*-------------------------< IDENT_BREAK >----------------------*/,{
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR_A (select2),
+}/*-------------------------< IDENT_BREAK_ATN >------------------*/,{
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR_A (select2),
+}/*-------------------------< SDATA_IN >-------------------------*/,{
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_dsb, sense),
+ SCR_CALL,
+ PADDR_A (datai_done),
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< RESEL_BAD_LUN >--------------------*/,{
+ /*
+ * Message is an IDENTIFY, but lun is unknown.
+ * Signal problem to C code for logging the event.
+ * Send a M_ABORT to clear all pending tasks.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_LUN,
+ SCR_JUMP,
+ PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L >------------------------*/,{
+ /*
+ * We donnot have a task for that I_T_L.
+ * Signal problem to C code for logging the event.
+ * Send a M_ABORT message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_I_T_L,
+ SCR_JUMP,
+ PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L_Q >----------------------*/,{
+ /*
+ * We donnot have a task that matches the tag.
+ * Signal problem to C code for logging the event.
+ * Send a M_ABORTTAG message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_I_T_L_Q,
+ SCR_JUMP,
+ PADDR_B (abort_resel),
+}/*-------------------------< BAD_STATUS >-----------------------*/,{
+ /*
+ * Anything different from INTERMEDIATE
+ * CONDITION MET should be a bad SCSI status,
+ * given that GOOD status has already been tested.
+ * Call the C code.
+ */
+ SCR_COPY (4),
+ PADDR_B (startpos),
+ RADDR_1 (scratcha),
+ SCR_INT ^ IFFALSE (DATA (S_COND_MET)),
+ SIR_BAD_SCSI_STATUS,
+ SCR_RETURN,
+ 0,
+}/*-------------------------< WSR_MA_HELPER >--------------------*/,{
+ /*
+ * Helper for the C code when WSR bit is set.
+ * Perform the move of the residual byte.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.wresid),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+}/*-------------------------< DATA_IO >--------------------------*/,{
+ /*
+ * We jump here if the data direction was unknown at the
+ * time we had to queue the command to the scripts processor.
+ * Pointers had been set as follow in this situation:
+ * savep --> DATA_IO
+ * lastp --> start pointer when DATA_IN
+ * wlastp --> start pointer when DATA_OUT
+ * This script sets savep and lastp according to the
+ * direction chosen by the target.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ PADDR_B (data_io_out),
+}/*-------------------------< DATA_IO_COM >----------------------*/,{
+ /*
+ * Direction is DATA IN.
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.lastp),
+ HADDR_1 (ccb_head.savep),
+ /*
+ * Jump to the SCRIPTS according to actual direction.
+ */
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.savep),
+ RADDR_1 (temp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< DATA_IO_OUT >----------------------*/,{
+ /*
+ * Direction is DATA OUT.
+ */
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+ 0,
+ SCR_COPY (4),
+ HADDR_1 (ccb_head.wlastp),
+ HADDR_1 (ccb_head.lastp),
+ SCR_JUMP,
+ PADDR_B(data_io_com),
+#endif /* SYM_OPT_HANDLE_DIR_UNKNOWN */
+
+}/*-------------------------< ZERO >-----------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< SCRATCH >--------------------------*/,{
+ SCR_DATA_ZERO, /* MUST BE BEFORE SCRATCH1 */
+}/*-------------------------< SCRATCH1 >-------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< PREV_DONE >------------------------*/,{
+ SCR_DATA_ZERO, /* MUST BE BEFORE DONE_POS ! */
+}/*-------------------------< DONE_POS >-------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< NEXTJOB >--------------------------*/,{
+ SCR_DATA_ZERO, /* MUST BE BEFORE STARTPOS ! */
+}/*-------------------------< STARTPOS >-------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< TARGTBL >--------------------------*/,{
+ SCR_DATA_ZERO,
+}/*--------------------------<>----------------------------------*/
+};
+
+static struct SYM_FWZ_SCR SYM_FWZ_SCR = {
+ /*-------------------------< SNOOPTEST >------------------------*/{
+ /*
+ * Read the variable.
+ */
+ SCR_COPY (4),
+ HADDR_1 (scratch),
+ RADDR_1 (scratcha),
+ /*
+ * Write the variable.
+ */
+ SCR_COPY (4),
+ RADDR_1 (temp),
+ HADDR_1 (scratch),
+ /*
+ * Read back the variable.
+ */
+ SCR_COPY (4),
+ HADDR_1 (scratch),
+ RADDR_1 (temp),
+}/*-------------------------< SNOOPEND >-------------------------*/,{
+ /*
+ * And stop.
+ */
+ SCR_INT,
+ 99,
+}/*--------------------------<>----------------------------------*/
+};
diff --git a/drivers/scsi/sym53c8xx_2/sym_fw2.h b/drivers/scsi/sym53c8xx_2/sym_fw2.h
new file mode 100644
index 000000000000..7ea7151f5d1d
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_fw2.h
@@ -0,0 +1,1927 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Scripts for SYMBIOS-Processor
+ *
+ * We have to know the offsets of all labels before we reach
+ * them (for forward jumps). Therefore we declare a struct
+ * here. If you make changes inside the script,
+ *
+ * DONT FORGET TO CHANGE THE LENGTHS HERE!
+ */
+
+/*
+ * Script fragments which are loaded into the on-chip RAM
+ * of 825A, 875, 876, 895, 895A, 896 and 1010 chips.
+ * Must not exceed 4K bytes.
+ */
+struct SYM_FWA_SCR {
+ u32 start [ 14];
+ u32 getjob_begin [ 4];
+ u32 getjob_end [ 4];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ u32 select [ 6];
+#else
+ u32 select [ 4];
+#endif
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+ u32 is_dmap_dirty [ 4];
+#endif
+ u32 wf_sel_done [ 2];
+ u32 sel_done [ 2];
+ u32 send_ident [ 2];
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 select2 [ 8];
+#else
+ u32 select2 [ 2];
+#endif
+ u32 command [ 2];
+ u32 dispatch [ 28];
+ u32 sel_no_cmd [ 10];
+ u32 init [ 6];
+ u32 clrack [ 4];
+ u32 datai_done [ 10];
+ u32 datai_done_wsr [ 20];
+ u32 datao_done [ 10];
+ u32 datao_done_wss [ 6];
+ u32 datai_phase [ 4];
+ u32 datao_phase [ 6];
+ u32 msg_in [ 2];
+ u32 msg_in2 [ 10];
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 status [ 14];
+#else
+ u32 status [ 10];
+#endif
+ u32 complete [ 6];
+ u32 complete2 [ 12];
+ u32 done [ 14];
+ u32 done_end [ 2];
+ u32 complete_error [ 4];
+ u32 save_dp [ 12];
+ u32 restore_dp [ 8];
+ u32 disconnect [ 12];
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 idle [ 4];
+#else
+ u32 idle [ 2];
+#endif
+#ifdef SYM_CONF_IARB_SUPPORT
+ u32 ungetjob [ 6];
+#else
+ u32 ungetjob [ 4];
+#endif
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ u32 reselect [ 4];
+#else
+ u32 reselect [ 2];
+#endif
+ u32 reselected [ 22];
+ u32 resel_scntl4 [ 20];
+ u32 resel_lun0 [ 6];
+#if SYM_CONF_MAX_TASK*4 > 512
+ u32 resel_tag [ 26];
+#elif SYM_CONF_MAX_TASK*4 > 256
+ u32 resel_tag [ 20];
+#else
+ u32 resel_tag [ 16];
+#endif
+ u32 resel_dsa [ 2];
+ u32 resel_dsa1 [ 4];
+ u32 resel_no_tag [ 6];
+ u32 data_in [SYM_CONF_MAX_SG * 2];
+ u32 data_in2 [ 4];
+ u32 data_out [SYM_CONF_MAX_SG * 2];
+ u32 data_out2 [ 4];
+ u32 pm0_data [ 12];
+ u32 pm0_data_out [ 6];
+ u32 pm0_data_end [ 6];
+ u32 pm1_data [ 12];
+ u32 pm1_data_out [ 6];
+ u32 pm1_data_end [ 6];
+};
+
+/*
+ * Script fragments which stay in main memory for all chips
+ * except for chips that support 8K on-chip RAM.
+ */
+struct SYM_FWB_SCR {
+ u32 start64 [ 2];
+ u32 no_data [ 2];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ u32 sel_for_abort [ 18];
+#else
+ u32 sel_for_abort [ 16];
+#endif
+ u32 sel_for_abort_1 [ 2];
+ u32 msg_in_etc [ 12];
+ u32 msg_received [ 4];
+ u32 msg_weird_seen [ 4];
+ u32 msg_extended [ 20];
+ u32 msg_bad [ 6];
+ u32 msg_weird [ 4];
+ u32 msg_weird1 [ 8];
+
+ u32 wdtr_resp [ 6];
+ u32 send_wdtr [ 4];
+ u32 sdtr_resp [ 6];
+ u32 send_sdtr [ 4];
+ u32 ppr_resp [ 6];
+ u32 send_ppr [ 4];
+ u32 nego_bad_phase [ 4];
+ u32 msg_out [ 4];
+ u32 msg_out_done [ 4];
+ u32 data_ovrun [ 2];
+ u32 data_ovrun1 [ 22];
+ u32 data_ovrun2 [ 8];
+ u32 abort_resel [ 16];
+ u32 resend_ident [ 4];
+ u32 ident_break [ 4];
+ u32 ident_break_atn [ 4];
+ u32 sdata_in [ 6];
+ u32 resel_bad_lun [ 4];
+ u32 bad_i_t_l [ 4];
+ u32 bad_i_t_l_q [ 4];
+ u32 bad_status [ 6];
+ u32 pm_handle [ 20];
+ u32 pm_handle1 [ 4];
+ u32 pm_save [ 4];
+ u32 pm0_save [ 12];
+ u32 pm_save_end [ 4];
+ u32 pm1_save [ 14];
+
+ /* WSR handling */
+ u32 pm_wsr_handle [ 38];
+ u32 wsr_ma_helper [ 4];
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ /* Unknown direction handling */
+ u32 data_io [ 2];
+ u32 data_io_in [ 2];
+ u32 data_io_com [ 6];
+ u32 data_io_out [ 8];
+#endif
+ /* Data area */
+ u32 zero [ 1];
+ u32 scratch [ 1];
+ u32 pm0_data_addr [ 1];
+ u32 pm1_data_addr [ 1];
+ u32 done_pos [ 1];
+ u32 startpos [ 1];
+ u32 targtbl [ 1];
+};
+
+/*
+ * Script fragments used at initialisations.
+ * Only runs out of main memory.
+ */
+struct SYM_FWZ_SCR {
+ u32 snooptest [ 6];
+ u32 snoopend [ 2];
+};
+
+static struct SYM_FWA_SCR SYM_FWA_SCR = {
+/*--------------------------< START >----------------------------*/ {
+ /*
+ * Switch the LED on.
+ * Will be patched with a NO_OP if LED
+ * not needed or not desired.
+ */
+ SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+ 0,
+ /*
+ * Clear SIGP.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ /*
+ * Stop here if the C code wants to perform
+ * some error recovery procedure manually.
+ * (Indicate this by setting SEM in ISTAT)
+ */
+ SCR_FROM_REG (istat),
+ 0,
+ /*
+ * Report to the C code the next position in
+ * the start queue the SCRIPTS will schedule.
+ * The C code must not change SCRATCHA.
+ */
+ SCR_LOAD_ABS (scratcha, 4),
+ PADDR_B (startpos),
+ SCR_INT ^ IFTRUE (MASK (SEM, SEM)),
+ SIR_SCRIPT_STOPPED,
+ /*
+ * Start the next job.
+ *
+ * @DSA = start point for this job.
+ * SCRATCHA = address of this job in the start queue.
+ *
+ * We will restore startpos with SCRATCHA if we fails the
+ * arbitration or if it is the idle job.
+ *
+ * The below GETJOB_BEGIN to GETJOB_END section of SCRIPTS
+ * is a critical path. If it is partially executed, it then
+ * may happen that the job address is not yet in the DSA
+ * and the next queue position points to the next JOB.
+ */
+ SCR_LOAD_ABS (dsa, 4),
+ PADDR_B (startpos),
+ SCR_LOAD_REL (temp, 4),
+ 4,
+}/*-------------------------< GETJOB_BEGIN >---------------------*/,{
+ SCR_STORE_ABS (temp, 4),
+ PADDR_B (startpos),
+ SCR_LOAD_REL (dsa, 4),
+ 0,
+}/*-------------------------< GETJOB_END >-----------------------*/,{
+ SCR_LOAD_REL (temp, 4),
+ 0,
+ SCR_RETURN,
+ 0,
+}/*-------------------------< SELECT >---------------------------*/,{
+ /*
+ * DSA contains the address of a scheduled
+ * data structure.
+ *
+ * SCRATCHA contains the address of the start queue
+ * entry which points to the next job.
+ *
+ * Set Initiator mode.
+ *
+ * (Target mode is left as an exercise for the reader)
+ */
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ SCR_CLR (SCR_TRG),
+ 0,
+#endif
+ /*
+ * And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct sym_dsb, select),
+ PADDR_A (ungetjob),
+ /*
+ * Now there are 4 possibilities:
+ *
+ * (1) The chip loses arbitration.
+ * This is ok, because it will try again,
+ * when the bus becomes idle.
+ * (But beware of the timeout function!)
+ *
+ * (2) The chip is reselected.
+ * Then the script processor takes the jump
+ * to the RESELECT label.
+ *
+ * (3) The chip wins arbitration.
+ * Then it will execute SCRIPTS instruction until
+ * the next instruction that checks SCSI phase.
+ * Then will stop and wait for selection to be
+ * complete or selection time-out to occur.
+ *
+ * After having won arbitration, the SCRIPTS
+ * processor is able to execute instructions while
+ * the SCSI core is performing SCSI selection.
+ */
+ /*
+ * Initialize the status registers
+ */
+ SCR_LOAD_REL (scr0, 4),
+ offsetof (struct sym_ccb, phys.head.status),
+ /*
+ * We may need help from CPU if the DMA segment
+ * registers aren't up-to-date for this IO.
+ * Patched with NOOP for chips that donnot
+ * support DAC addressing.
+ */
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+}/*-------------------------< IS_DMAP_DIRTY >--------------------*/,{
+ SCR_FROM_REG (HX_REG),
+ 0,
+ SCR_INT ^ IFTRUE (MASK (HX_DMAP_DIRTY, HX_DMAP_DIRTY)),
+ SIR_DMAP_DIRTY,
+#endif
+}/*-------------------------< WF_SEL_DONE >----------------------*/,{
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_SEL_ATN_NO_MSG_OUT,
+}/*-------------------------< SEL_DONE >-------------------------*/,{
+ /*
+ * C1010-33 errata work-around.
+ * Due to a race, the SCSI core may not have
+ * loaded SCNTL3 on SEL_TBL instruction.
+ * We reload it once phase is stable.
+ * Patched with a NOOP for other chips.
+ */
+ SCR_LOAD_REL (scntl3, 1),
+ offsetof(struct sym_dsb, select.sel_scntl3),
+}/*-------------------------< SEND_IDENT >-----------------------*/,{
+ /*
+ * Selection complete.
+ * Send the IDENTIFY and possibly the TAG message
+ * and negotiation message if present.
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct sym_dsb, smsg),
+}/*-------------------------< SELECT2 >--------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * Set IMMEDIATE ARBITRATION if we have been given
+ * a hint to do so. (Some job to do after this one).
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ SCR_JUMPR ^ IFFALSE (MASK (HF_HINT_IARB, HF_HINT_IARB)),
+ 8,
+ SCR_REG_REG (scntl1, SCR_OR, IARB),
+ 0,
+#endif
+ /*
+ * Anticipate the COMMAND phase.
+ * This is the PHASE we expect at this point.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)),
+ PADDR_A (sel_no_cmd),
+}/*-------------------------< COMMAND >--------------------------*/,{
+ /*
+ * ... and send the command
+ */
+ SCR_MOVE_TBL ^ SCR_COMMAND,
+ offsetof (struct sym_dsb, cmd),
+}/*-------------------------< DISPATCH >-------------------------*/,{
+ /*
+ * MSG_IN is the only phase that shall be
+ * entered at least once for each (re)selection.
+ * So we test it first.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (msg_in),
+ SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT)),
+ PADDR_A (datao_phase),
+ SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN)),
+ PADDR_A (datai_phase),
+ SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+ PADDR_A (status),
+ SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+ PADDR_A (command),
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+ PADDR_B (msg_out),
+ /*
+ * Discard as many illegal phases as
+ * required and tell the C code about.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_OUT)),
+ 16,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+ HADDR_1 (scratch),
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_OUT)),
+ -16,
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_IN)),
+ 16,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+ HADDR_1 (scratch),
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_IN)),
+ -16,
+ SCR_INT,
+ SIR_BAD_PHASE,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< SEL_NO_CMD >-----------------------*/,{
+ /*
+ * The target does not switch to command
+ * phase after IDENTIFY has been sent.
+ *
+ * If it stays in MSG OUT phase send it
+ * the IDENTIFY again.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (resend_ident),
+ /*
+ * If target does not switch to MSG IN phase
+ * and we sent a negotiation, assert the
+ * failure immediately.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (dispatch),
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+ /*
+ * Jump to dispatcher.
+ */
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< INIT >-----------------------------*/,{
+ /*
+ * Wait for the SCSI RESET signal to be
+ * inactive before restarting operations,
+ * since the chip may hang on SEL_ATN
+ * if SCSI RESET is active.
+ */
+ SCR_FROM_REG (sstat0),
+ 0,
+ SCR_JUMPR ^ IFTRUE (MASK (IRST, IRST)),
+ -16,
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< CLRACK >---------------------------*/,{
+ /*
+ * Terminate possible pending message phase.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE >-----------------------*/,{
+ /*
+ * Save current pointer to LASTP.
+ */
+ SCR_STORE_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ /*
+ * If the SWIDE is not full, jump to dispatcher.
+ * We anticipate a STATUS phase.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (WSR, WSR)),
+ PADDR_A (datai_done_wsr),
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+ PADDR_A (status),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE_WSR >-------------------*/,{
+ /*
+ * The SWIDE is full.
+ * Clear this condition.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ /*
+ * We are expecting an IGNORE RESIDUE message
+ * from the device, otherwise we are in data
+ * overrun condition. Check against MSG_IN phase.
+ */
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ SIR_SWIDE_OVERRUN,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR_A (dispatch),
+ /*
+ * We are in MSG_IN phase,
+ * Read the first byte of the message.
+ * If it is not an IGNORE RESIDUE message,
+ * signal overrun and jump to message
+ * processing.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[0]),
+ SCR_INT ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+ SIR_SWIDE_OVERRUN,
+ SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+ PADDR_A (msg_in2),
+ /*
+ * We got the message we expected.
+ * Read the 2nd byte, and jump to dispatcher.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[1]),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE >-----------------------*/,{
+ /*
+ * Save current pointer to LASTP.
+ */
+ SCR_STORE_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ /*
+ * If the SODL is not full jump to dispatcher.
+ * We anticipate a STATUS phase.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (WSS, WSS)),
+ PADDR_A (datao_done_wss),
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+ PADDR_A (status),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE_WSS >-------------------*/,{
+ /*
+ * The SODL is full, clear this condition.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSS),
+ 0,
+ /*
+ * And signal a DATA UNDERRUN condition
+ * to the C code.
+ */
+ SCR_INT,
+ SIR_SODL_UNDERRUN,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATAI_PHASE >----------------------*/,{
+ /*
+ * Jump to current pointer.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< DATAO_PHASE >----------------------*/,{
+ /*
+ * C1010-66 errata work-around.
+ * Extra clocks of data hold must be inserted
+ * in DATA OUT phase on 33 MHz PCI BUS.
+ * Patched with a NOOP for other chips.
+ */
+ SCR_REG_REG (scntl4, SCR_OR, (XCLKH_DT|XCLKH_ST)),
+ 0,
+ /*
+ * Jump to current pointer.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< MSG_IN >---------------------------*/,{
+ /*
+ * Get the first byte of the message.
+ *
+ * The script processor doesn't negate the
+ * ACK signal after this transfer.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[0]),
+}/*-------------------------< MSG_IN2 >--------------------------*/,{
+ /*
+ * Check first against 1 byte messages
+ * that we handle from SCRIPTS.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+ PADDR_A (complete),
+ SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+ PADDR_A (disconnect),
+ SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+ PADDR_A (save_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+ PADDR_A (restore_dp),
+ /*
+ * We handle all other messages from the
+ * C code, so no need to waste on-chip RAM
+ * for those ones.
+ */
+ SCR_JUMP,
+ PADDR_B (msg_in_etc),
+}/*-------------------------< STATUS >---------------------------*/,{
+ /*
+ * get the status
+ */
+ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ HADDR_1 (scratch),
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If STATUS is not GOOD, clear IMMEDIATE ARBITRATION,
+ * since we may have to tamper the start queue from
+ * the C code.
+ */
+ SCR_JUMPR ^ IFTRUE (DATA (S_GOOD)),
+ 8,
+ SCR_REG_REG (scntl1, SCR_AND, ~IARB),
+ 0,
+#endif
+ /*
+ * save status to scsi_status.
+ * mark as complete.
+ */
+ SCR_TO_REG (SS_REG),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+ 0,
+ /*
+ * Anticipate the MESSAGE PHASE for
+ * the TASK COMPLETE message.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (msg_in),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< COMPLETE >-------------------------*/,{
+ /*
+ * Complete message.
+ *
+ * When we terminate the cycle by clearing ACK,
+ * the target may disconnect immediately.
+ *
+ * We don't want to be told of an "unexpected disconnect",
+ * so we disable this feature.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ /*
+ * Terminate cycle ...
+ */
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ * ... and wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+}/*-------------------------< COMPLETE2 >------------------------*/,{
+ /*
+ * Save host status.
+ */
+ SCR_STORE_REL (scr0, 4),
+ offsetof (struct sym_ccb, phys.head.status),
+ /*
+ * Some bridges may reorder DMA writes to memory.
+ * We donnot want the CPU to deal with completions
+ * without all the posted write having been flushed
+ * to memory. This DUMMY READ should flush posted
+ * buffers prior to the CPU having to deal with
+ * completions.
+ */
+ SCR_LOAD_REL (scr0, 4), /* DUMMY READ */
+ offsetof (struct sym_ccb, phys.head.status),
+
+ /*
+ * If command resulted in not GOOD status,
+ * call the C code if needed.
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+ PADDR_B (bad_status),
+ /*
+ * If we performed an auto-sense, call
+ * the C code to synchronyze task aborts
+ * with UNIT ATTENTION conditions.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ SCR_JUMP ^ IFFALSE (MASK (0 ,(HF_SENSE|HF_EXT_ERR))),
+ PADDR_A (complete_error),
+}/*-------------------------< DONE >-----------------------------*/,{
+ /*
+ * Copy the DSA to the DONE QUEUE and
+ * signal completion to the host.
+ * If we are interrupted between DONE
+ * and DONE_END, we must reset, otherwise
+ * the completed CCB may be lost.
+ */
+ SCR_STORE_ABS (dsa, 4),
+ PADDR_B (scratch),
+ SCR_LOAD_ABS (dsa, 4),
+ PADDR_B (done_pos),
+ SCR_LOAD_ABS (scratcha, 4),
+ PADDR_B (scratch),
+ SCR_STORE_REL (scratcha, 4),
+ 0,
+ /*
+ * The instruction below reads the DONE QUEUE next
+ * free position from memory.
+ * In addition it ensures that all PCI posted writes
+ * are flushed and so the DSA value of the done
+ * CCB is visible by the CPU before INTFLY is raised.
+ */
+ SCR_LOAD_REL (scratcha, 4),
+ 4,
+ SCR_INT_FLY,
+ 0,
+ SCR_STORE_ABS (scratcha, 4),
+ PADDR_B (done_pos),
+}/*-------------------------< DONE_END >-------------------------*/,{
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< COMPLETE_ERROR >-------------------*/,{
+ SCR_LOAD_ABS (scratcha, 4),
+ PADDR_B (startpos),
+ SCR_INT,
+ SIR_COMPLETE_ERROR,
+}/*-------------------------< SAVE_DP >--------------------------*/,{
+ /*
+ * Clear ACK immediately.
+ * No need to delay it.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * Keep track we received a SAVE DP, so
+ * we will switch to the other PM context
+ * on the next PM since the DP may point
+ * to the current PM context.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_DP_SAVED),
+ 0,
+ /*
+ * SAVE_DP message:
+ * Copy LASTP to SAVEP.
+ */
+ SCR_LOAD_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ SCR_STORE_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.savep),
+ /*
+ * Anticipate the MESSAGE PHASE for
+ * the DISCONNECT message.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ PADDR_A (msg_in),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< RESTORE_DP >-----------------------*/,{
+ /*
+ * Clear ACK immediately.
+ * No need to delay it.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * Copy SAVEP to LASTP.
+ */
+ SCR_LOAD_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.savep),
+ SCR_STORE_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DISCONNECT >-----------------------*/,{
+ /*
+ * DISCONNECTing ...
+ *
+ * disable the "unexpected disconnect" feature,
+ * and remove the ACK signal.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ * Wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ * Status is: DISCONNECTED.
+ */
+ SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+ 0,
+ /*
+ * Save host status.
+ */
+ SCR_STORE_REL (scr0, 4),
+ offsetof (struct sym_ccb, phys.head.status),
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< IDLE >-----------------------------*/,{
+ /*
+ * Nothing to do?
+ * Switch the LED off and wait for reselect.
+ * Will be patched with a NO_OP if LED
+ * not needed or not desired.
+ */
+ SCR_REG_REG (gpreg, SCR_OR, 0x01),
+ 0,
+#ifdef SYM_CONF_IARB_SUPPORT
+ SCR_JUMPR,
+ 8,
+#endif
+}/*-------------------------< UNGETJOB >-------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * Set IMMEDIATE ARBITRATION, for the next time.
+ * This will give us better chance to win arbitration
+ * for the job we just wanted to do.
+ */
+ SCR_REG_REG (scntl1, SCR_OR, IARB),
+ 0,
+#endif
+ /*
+ * We are not able to restart the SCRIPTS if we are
+ * interrupted and these instruction haven't been
+ * all executed. BTW, this is very unlikely to
+ * happen, but we check that from the C code.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_STORE_ABS (scratcha, 4),
+ PADDR_B (startpos),
+}/*-------------------------< RESELECT >-------------------------*/,{
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ /*
+ * Make sure we are in initiator mode.
+ */
+ SCR_CLR (SCR_TRG),
+ 0,
+#endif
+ /*
+ * Sleep waiting for a reselection.
+ */
+ SCR_WAIT_RESEL,
+ PADDR_A(start),
+}/*-------------------------< RESELECTED >-----------------------*/,{
+ /*
+ * Switch the LED on.
+ * Will be patched with a NO_OP if LED
+ * not needed or not desired.
+ */
+ SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+ 0,
+ /*
+ * load the target id into the sdid
+ */
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+ 0,
+ SCR_TO_REG (sdid),
+ 0,
+ /*
+ * Load the target control block address
+ */
+ SCR_LOAD_ABS (dsa, 4),
+ PADDR_B (targtbl),
+ SCR_SFBR_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_AND, 0x3c),
+ 0,
+ SCR_LOAD_REL (dsa, 4),
+ 0,
+ /*
+ * We expect MESSAGE IN phase.
+ * If not, get help from the C code.
+ */
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ SIR_RESEL_NO_MSG_IN,
+ /*
+ * Load the legacy synchronous transfer registers.
+ */
+ SCR_LOAD_REL (scntl3, 1),
+ offsetof(struct sym_tcb, head.wval),
+ SCR_LOAD_REL (sxfer, 1),
+ offsetof(struct sym_tcb, head.sval),
+}/*-------------------------< RESEL_SCNTL4 >---------------------*/,{
+ /*
+ * The C1010 uses a new synchronous timing scheme.
+ * Will be patched with a NO_OP if not a C1010.
+ */
+ SCR_LOAD_REL (scntl4, 1),
+ offsetof(struct sym_tcb, head.uval),
+ /*
+ * Get the IDENTIFY message.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin),
+ /*
+ * If IDENTIFY LUN #0, use a faster path
+ * to find the LCB structure.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0x80, 0xbf)),
+ PADDR_A (resel_lun0),
+ /*
+ * If message isn't an IDENTIFY,
+ * tell the C code about.
+ */
+ SCR_INT ^ IFFALSE (MASK (0x80, 0x80)),
+ SIR_RESEL_NO_IDENTIFY,
+ /*
+ * It is an IDENTIFY message,
+ * Load the LUN control block address.
+ */
+ SCR_LOAD_REL (dsa, 4),
+ offsetof(struct sym_tcb, head.luntbl_sa),
+ SCR_SFBR_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_SHL, 0),
+ 0,
+ SCR_REG_REG (dsa, SCR_AND, 0xfc),
+ 0,
+ SCR_LOAD_REL (dsa, 4),
+ 0,
+ SCR_JUMPR,
+ 8,
+}/*-------------------------< RESEL_LUN0 >-----------------------*/,{
+ /*
+ * LUN 0 special case (but usual one :))
+ */
+ SCR_LOAD_REL (dsa, 4),
+ offsetof(struct sym_tcb, head.lun0_sa),
+ /*
+ * Jump indirectly to the reselect action for this LUN.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof(struct sym_lcb, head.resel_sa),
+ SCR_RETURN,
+ 0,
+ /* In normal situations, we jump to RESEL_TAG or RESEL_NO_TAG */
+}/*-------------------------< RESEL_TAG >------------------------*/,{
+ /*
+ * ACK the IDENTIFY previously received.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * It shall be a tagged command.
+ * Read SIMPLE+TAG.
+ * The C code will deal with errors.
+ * Agressive optimization, is'nt it? :)
+ */
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ HADDR_1 (msgin),
+ /*
+ * Load the pointer to the tagged task
+ * table for this LUN.
+ */
+ SCR_LOAD_REL (dsa, 4),
+ offsetof(struct sym_lcb, head.itlq_tbl_sa),
+ /*
+ * The SIDL still contains the TAG value.
+ * Agressive optimization, isn't it? :):)
+ */
+ SCR_REG_SFBR (sidl, SCR_SHL, 0),
+ 0,
+#if SYM_CONF_MAX_TASK*4 > 512
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+ SCR_REG_REG (dsa1, SCR_OR, 2),
+ 0,
+ SCR_REG_REG (sfbr, SCR_SHL, 0),
+ 0,
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+ SCR_REG_REG (dsa1, SCR_OR, 1),
+ 0,
+#elif SYM_CONF_MAX_TASK*4 > 256
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+ SCR_REG_REG (dsa1, SCR_OR, 1),
+ 0,
+#endif
+ /*
+ * Retrieve the DSA of this task.
+ * JUMP indirectly to the restart point of the CCB.
+ */
+ SCR_SFBR_REG (dsa, SCR_AND, 0xfc),
+ 0,
+ SCR_LOAD_REL (dsa, 4),
+ 0,
+ SCR_LOAD_REL (temp, 4),
+ offsetof(struct sym_ccb, phys.head.go.restart),
+ SCR_RETURN,
+ 0,
+ /* In normal situations we branch to RESEL_DSA */
+}/*-------------------------< RESEL_DSA >------------------------*/,{
+ /*
+ * ACK the IDENTIFY or TAG previously received.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+}/*-------------------------< RESEL_DSA1 >-----------------------*/,{
+ /*
+ * Initialize the status registers
+ */
+ SCR_LOAD_REL (scr0, 4),
+ offsetof (struct sym_ccb, phys.head.status),
+ /*
+ * Jump to dispatcher.
+ */
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< RESEL_NO_TAG >---------------------*/,{
+ /*
+ * Load the DSA with the unique ITL task.
+ */
+ SCR_LOAD_REL (dsa, 4),
+ offsetof(struct sym_lcb, head.itl_task_sa),
+ /*
+ * JUMP indirectly to the restart point of the CCB.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof(struct sym_ccb, phys.head.go.restart),
+ SCR_RETURN,
+ 0,
+ /* In normal situations we branch to RESEL_DSA */
+}/*-------------------------< DATA_IN >--------------------------*/,{
+/*
+ * Because the size depends on the
+ * #define SYM_CONF_MAX_SG parameter,
+ * it is filled in at runtime.
+ *
+ * ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ * || SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ * || offsetof (struct sym_dsb, data[ i]),
+ * ##==========================================
+ */
+0
+}/*-------------------------< DATA_IN2 >-------------------------*/,{
+ SCR_CALL,
+ PADDR_A (datai_done),
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< DATA_OUT >-------------------------*/,{
+/*
+ * Because the size depends on the
+ * #define SYM_CONF_MAX_SG parameter,
+ * it is filled in at runtime.
+ *
+ * ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ * || SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ * || offsetof (struct sym_dsb, data[ i]),
+ * ##==========================================
+ */
+0
+}/*-------------------------< DATA_OUT2 >------------------------*/,{
+ SCR_CALL,
+ PADDR_A (datao_done),
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< PM0_DATA >-------------------------*/,{
+ /*
+ * Read our host flags to SFBR, so we will be able
+ * to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ * Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR_A (pm0_data_out),
+ /*
+ * Actual phase is DATA IN.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+ 0,
+ /*
+ * Move the data to memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.pm0.sg),
+ SCR_JUMP,
+ PADDR_A (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >---------------------*/,{
+ /*
+ * Actual phase is DATA OUT.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+ 0,
+ /*
+ * Move the data from memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ offsetof (struct sym_ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >---------------------*/,{
+ /*
+ * Clear the flag that told we were moving
+ * data from the PM0 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
+ 0,
+ /*
+ * Return to the previous DATA script which
+ * is guaranteed by design (if no bug) to be
+ * the main DATA script for this transfer.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.pm0.ret),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< PM1_DATA >-------------------------*/,{
+ /*
+ * Read our host flags to SFBR, so we will be able
+ * to check against the data direction we expect.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ * Check against actual DATA PHASE.
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ PADDR_A (pm1_data_out),
+ /*
+ * Actual phase is DATA IN.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+ 0,
+ /*
+ * Move the data to memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.pm1.sg),
+ SCR_JUMP,
+ PADDR_A (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >---------------------*/,{
+ /*
+ * Actual phase is DATA OUT.
+ * Check against expected direction.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+ PADDR_B (data_ovrun),
+ /*
+ * Keep track we are moving data from the
+ * PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+ 0,
+ /*
+ * Move the data from memory.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ offsetof (struct sym_ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >---------------------*/,{
+ /*
+ * Clear the flag that told we were moving
+ * data from the PM1 DATA mini-script.
+ */
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
+ 0,
+ /*
+ * Return to the previous DATA script which
+ * is guaranteed by design (if no bug) to be
+ * the main DATA script for this transfer.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.pm1.ret),
+ SCR_RETURN,
+ 0,
+}/*-------------------------<>-----------------------------------*/
+};
+
+static struct SYM_FWB_SCR SYM_FWB_SCR = {
+/*--------------------------< START64 >--------------------------*/ {
+ /*
+ * SCRIPT entry point for the 895A, 896 and 1010.
+ * For now, there is no specific stuff for those
+ * chips at this point, but this may come.
+ */
+ SCR_JUMP,
+ PADDR_A (init),
+}/*-------------------------< NO_DATA >--------------------------*/,{
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< SEL_FOR_ABORT >--------------------*/,{
+ /*
+ * We are jumped here by the C code, if we have
+ * some target to reset or some disconnected
+ * job to abort. Since error recovery is a serious
+ * busyness, we will really reset the SCSI BUS, if
+ * case of a SCSI interrupt occurring in this path.
+ */
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+ /*
+ * Set initiator mode.
+ */
+ SCR_CLR (SCR_TRG),
+ 0,
+#endif
+ /*
+ * And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct sym_hcb, abrt_sel),
+ PADDR_A (reselect),
+ /*
+ * Wait for the selection to complete or
+ * the selection to time out.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ -8,
+ /*
+ * Call the C code.
+ */
+ SCR_INT,
+ SIR_TARGET_SELECTED,
+ /*
+ * The C code should let us continue here.
+ * Send the 'kiss of death' message.
+ * We expect an immediate disconnect once
+ * the target has eaten the message.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct sym_hcb, abrt_tbl),
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ * Tell the C code that we are done.
+ */
+ SCR_INT,
+ SIR_ABORT_SENT,
+}/*-------------------------< SEL_FOR_ABORT_1 >------------------*/,{
+ /*
+ * Jump at scheduler.
+ */
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< MSG_IN_ETC >-----------------------*/,{
+ /*
+ * If it is an EXTENDED (variable size message)
+ * Handle it.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+ PADDR_B (msg_extended),
+ /*
+ * Let the C code handle any other
+ * 1 byte message.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0x00, 0xf0)),
+ PADDR_B (msg_received),
+ SCR_JUMP ^ IFTRUE (MASK (0x10, 0xf0)),
+ PADDR_B (msg_received),
+ /*
+ * We donnot handle 2 bytes messages from SCRIPTS.
+ * So, let the C code deal with these ones too.
+ */
+ SCR_JUMP ^ IFFALSE (MASK (0x20, 0xf0)),
+ PADDR_B (msg_weird_seen),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[1]),
+}/*-------------------------< MSG_RECEIVED >---------------------*/,{
+ SCR_LOAD_REL (scratcha, 4), /* DUMMY READ */
+ 0,
+ SCR_INT,
+ SIR_MSG_RECEIVED,
+}/*-------------------------< MSG_WEIRD_SEEN >-------------------*/,{
+ SCR_LOAD_REL (scratcha, 4), /* DUMMY READ */
+ 0,
+ SCR_INT,
+ SIR_MSG_WEIRD,
+}/*-------------------------< MSG_EXTENDED >---------------------*/,{
+ /*
+ * Clear ACK and get the next byte
+ * assumed to be the message length.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (msgin[1]),
+ /*
+ * Try to catch some unlikely situations as 0 length
+ * or too large the length.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (0)),
+ PADDR_B (msg_weird_seen),
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_REG_REG (sfbr, SCR_ADD, (256-8)),
+ 0,
+ SCR_JUMP ^ IFTRUE (CARRYSET),
+ PADDR_B (msg_weird_seen),
+ /*
+ * We donnot handle extended messages from SCRIPTS.
+ * Read the amount of data correponding to the
+ * message length and call the C code.
+ */
+ SCR_STORE_REL (scratcha, 1),
+ offsetof (struct sym_dsb, smsg_ext.size),
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_MOVE_TBL ^ SCR_MSG_IN,
+ offsetof (struct sym_dsb, smsg_ext),
+ SCR_JUMP,
+ PADDR_B (msg_received),
+}/*-------------------------< MSG_BAD >--------------------------*/,{
+ /*
+ * unimplemented message - reject it.
+ */
+ SCR_INT,
+ SIR_REJECT_TO_SEND,
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR_A (clrack),
+}/*-------------------------< MSG_WEIRD >------------------------*/,{
+ /*
+ * weird message received
+ * ignore all MSG IN phases and reject it.
+ */
+ SCR_INT,
+ SIR_REJECT_TO_SEND,
+ SCR_SET (SCR_ATN),
+ 0,
+}/*-------------------------< MSG_WEIRD1 >-----------------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR_A (dispatch),
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ HADDR_1 (scratch),
+ SCR_JUMP,
+ PADDR_B (msg_weird1),
+}/*-------------------------< WDTR_RESP >------------------------*/,{
+ /*
+ * let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_WDTR >------------------------*/,{
+ /*
+ * Send the M_X_WIDE_REQ
+ */
+ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_JUMP,
+ PADDR_B (msg_out_done),
+}/*-------------------------< SDTR_RESP >------------------------*/,{
+ /*
+ * let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_SDTR >------------------------*/,{
+ /*
+ * Send the M_X_SYNC_REQ
+ */
+ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_JUMP,
+ PADDR_B (msg_out_done),
+}/*-------------------------< PPR_RESP >-------------------------*/,{
+ /*
+ * let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_PPR >-------------------------*/,{
+ /*
+ * Send the M_X_PPR_REQ
+ */
+ SCR_MOVE_ABS (8) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_JUMP,
+ PADDR_B (msg_out_done),
+}/*-------------------------< NEGO_BAD_PHASE >-------------------*/,{
+ SCR_INT,
+ SIR_NEGO_PROTO,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< MSG_OUT >--------------------------*/,{
+ /*
+ * The target requests a message.
+ * We donnot send messages that may
+ * require the device to go to bus free.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ /*
+ * ... wait for the next phase
+ * if it's a message out, send it again, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR_B (msg_out),
+}/*-------------------------< MSG_OUT_DONE >---------------------*/,{
+ /*
+ * Let the C code be aware of the
+ * sent message and clear the message.
+ */
+ SCR_INT,
+ SIR_MSG_OUT_DONE,
+ /*
+ * ... and process the next phase
+ */
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< DATA_OVRUN >-----------------------*/,{
+ /*
+ * Use scratcha to count the extra bytes.
+ */
+ SCR_LOAD_ABS (scratcha, 4),
+ PADDR_B (zero),
+}/*-------------------------< DATA_OVRUN1 >----------------------*/,{
+ /*
+ * The target may want to transfer too much data.
+ *
+ * If phase is DATA OUT write 1 byte and count it.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+ 16,
+ SCR_CHMOV_ABS (1) ^ SCR_DATA_OUT,
+ HADDR_1 (scratch),
+ SCR_JUMP,
+ PADDR_B (data_ovrun2),
+ /*
+ * If WSR is set, clear this condition, and
+ * count this byte.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+ 16,
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ SCR_JUMP,
+ PADDR_B (data_ovrun2),
+ /*
+ * Finally check against DATA IN phase.
+ * Signal data overrun to the C code
+ * and jump to dispatcher if not so.
+ * Read 1 byte otherwise and count it.
+ */
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_IN)),
+ 16,
+ SCR_INT,
+ SIR_DATA_OVERRUN,
+ SCR_JUMP,
+ PADDR_A (dispatch),
+ SCR_CHMOV_ABS (1) ^ SCR_DATA_IN,
+ HADDR_1 (scratch),
+}/*-------------------------< DATA_OVRUN2 >----------------------*/,{
+ /*
+ * Count this byte.
+ * This will allow to return a negative
+ * residual to user.
+ */
+ SCR_REG_REG (scratcha, SCR_ADD, 0x01),
+ 0,
+ SCR_REG_REG (scratcha1, SCR_ADDC, 0),
+ 0,
+ SCR_REG_REG (scratcha2, SCR_ADDC, 0),
+ 0,
+ /*
+ * .. and repeat as required.
+ */
+ SCR_JUMP,
+ PADDR_B (data_ovrun1),
+}/*-------------------------< ABORT_RESEL >----------------------*/,{
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ * send the abort/abortag/reset message
+ * we expect an immediate disconnect
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ HADDR_1 (msgout),
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ SCR_INT,
+ SIR_RESEL_ABORTED,
+ SCR_JUMP,
+ PADDR_A (start),
+}/*-------------------------< RESEND_IDENT >---------------------*/,{
+ /*
+ * The target stays in MSG OUT phase after having acked
+ * Identify [+ Tag [+ Extended message ]]. Targets shall
+ * behave this way on parity error.
+ * We must send it again all the messages.
+ */
+ SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the */
+ 0, /* 1rst ACK = 90 ns. Hope the chip isn't too fast */
+ SCR_JUMP,
+ PADDR_A (send_ident),
+}/*-------------------------< IDENT_BREAK >----------------------*/,{
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR_A (select2),
+}/*-------------------------< IDENT_BREAK_ATN >------------------*/,{
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_JUMP,
+ PADDR_A (select2),
+}/*-------------------------< SDATA_IN >-------------------------*/,{
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_dsb, sense),
+ SCR_CALL,
+ PADDR_A (datai_done),
+ SCR_JUMP,
+ PADDR_B (data_ovrun),
+}/*-------------------------< RESEL_BAD_LUN >--------------------*/,{
+ /*
+ * Message is an IDENTIFY, but lun is unknown.
+ * Signal problem to C code for logging the event.
+ * Send a M_ABORT to clear all pending tasks.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_LUN,
+ SCR_JUMP,
+ PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L >------------------------*/,{
+ /*
+ * We donnot have a task for that I_T_L.
+ * Signal problem to C code for logging the event.
+ * Send a M_ABORT message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_I_T_L,
+ SCR_JUMP,
+ PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L_Q >----------------------*/,{
+ /*
+ * We donnot have a task that matches the tag.
+ * Signal problem to C code for logging the event.
+ * Send a M_ABORTTAG message.
+ */
+ SCR_INT,
+ SIR_RESEL_BAD_I_T_L_Q,
+ SCR_JUMP,
+ PADDR_B (abort_resel),
+}/*-------------------------< BAD_STATUS >-----------------------*/,{
+ /*
+ * Anything different from INTERMEDIATE
+ * CONDITION MET should be a bad SCSI status,
+ * given that GOOD status has already been tested.
+ * Call the C code.
+ */
+ SCR_LOAD_ABS (scratcha, 4),
+ PADDR_B (startpos),
+ SCR_INT ^ IFFALSE (DATA (S_COND_MET)),
+ SIR_BAD_SCSI_STATUS,
+ SCR_RETURN,
+ 0,
+}/*-------------------------< PM_HANDLE >------------------------*/,{
+ /*
+ * Phase mismatch handling.
+ *
+ * Since we have to deal with 2 SCSI data pointers
+ * (current and saved), we need at least 2 contexts.
+ * Each context (pm0 and pm1) has a saved area, a
+ * SAVE mini-script and a DATA phase mini-script.
+ */
+ /*
+ * Get the PM handling flags.
+ */
+ SCR_FROM_REG (HF_REG),
+ 0,
+ /*
+ * If no flags (1rst PM for example), avoid
+ * all the below heavy flags testing.
+ * This makes the normal case a bit faster.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0, (HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED))),
+ PADDR_B (pm_handle1),
+ /*
+ * If we received a SAVE DP, switch to the
+ * other PM context since the savep may point
+ * to the current PM context.
+ */
+ SCR_JUMPR ^ IFFALSE (MASK (HF_DP_SAVED, HF_DP_SAVED)),
+ 8,
+ SCR_REG_REG (sfbr, SCR_XOR, HF_ACT_PM),
+ 0,
+ /*
+ * If we have been interrupt in a PM DATA mini-script,
+ * we take the return address from the corresponding
+ * saved area.
+ * This ensure the return address always points to the
+ * main DATA script for this transfer.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (0, (HF_IN_PM0 | HF_IN_PM1))),
+ PADDR_B (pm_handle1),
+ SCR_JUMPR ^ IFFALSE (MASK (HF_IN_PM0, HF_IN_PM0)),
+ 16,
+ SCR_LOAD_REL (ia, 4),
+ offsetof(struct sym_ccb, phys.pm0.ret),
+ SCR_JUMP,
+ PADDR_B (pm_save),
+ SCR_LOAD_REL (ia, 4),
+ offsetof(struct sym_ccb, phys.pm1.ret),
+ SCR_JUMP,
+ PADDR_B (pm_save),
+}/*-------------------------< PM_HANDLE1 >-----------------------*/,{
+ /*
+ * Normal case.
+ * Update the return address so that it
+ * will point after the interrupted MOVE.
+ */
+ SCR_REG_REG (ia, SCR_ADD, 8),
+ 0,
+ SCR_REG_REG (ia1, SCR_ADDC, 0),
+ 0,
+}/*-------------------------< PM_SAVE >--------------------------*/,{
+ /*
+ * Clear all the flags that told us if we were
+ * interrupted in a PM DATA mini-script and/or
+ * we received a SAVE DP.
+ */
+ SCR_SFBR_REG (HF_REG, SCR_AND, (~(HF_IN_PM0|HF_IN_PM1|HF_DP_SAVED))),
+ 0,
+ /*
+ * Choose the current PM context.
+ */
+ SCR_JUMP ^ IFTRUE (MASK (HF_ACT_PM, HF_ACT_PM)),
+ PADDR_B (pm1_save),
+}/*-------------------------< PM0_SAVE >-------------------------*/,{
+ SCR_STORE_REL (ia, 4),
+ offsetof(struct sym_ccb, phys.pm0.ret),
+ /*
+ * If WSR bit is set, either UA and RBC may
+ * have to be changed whether the device wants
+ * to ignore this residue or not.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_CALL ^ IFTRUE (MASK (WSR, WSR)),
+ PADDR_B (pm_wsr_handle),
+ /*
+ * Save the remaining byte count, the updated
+ * address and the return address.
+ */
+ SCR_STORE_REL (rbc, 4),
+ offsetof(struct sym_ccb, phys.pm0.sg.size),
+ SCR_STORE_REL (ua, 4),
+ offsetof(struct sym_ccb, phys.pm0.sg.addr),
+ /*
+ * Set the current pointer at the PM0 DATA mini-script.
+ */
+ SCR_LOAD_ABS (ia, 4),
+ PADDR_B (pm0_data_addr),
+}/*-------------------------< PM_SAVE_END >----------------------*/,{
+ SCR_STORE_REL (ia, 4),
+ offsetof(struct sym_ccb, phys.head.lastp),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+}/*-------------------------< PM1_SAVE >-------------------------*/,{
+ SCR_STORE_REL (ia, 4),
+ offsetof(struct sym_ccb, phys.pm1.ret),
+ /*
+ * If WSR bit is set, either UA and RBC may
+ * have to be changed whether the device wants
+ * to ignore this residue or not.
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+ SCR_CALL ^ IFTRUE (MASK (WSR, WSR)),
+ PADDR_B (pm_wsr_handle),
+ /*
+ * Save the remaining byte count, the updated
+ * address and the return address.
+ */
+ SCR_STORE_REL (rbc, 4),
+ offsetof(struct sym_ccb, phys.pm1.sg.size),
+ SCR_STORE_REL (ua, 4),
+ offsetof(struct sym_ccb, phys.pm1.sg.addr),
+ /*
+ * Set the current pointer at the PM1 DATA mini-script.
+ */
+ SCR_LOAD_ABS (ia, 4),
+ PADDR_B (pm1_data_addr),
+ SCR_JUMP,
+ PADDR_B (pm_save_end),
+}/*-------------------------< PM_WSR_HANDLE >--------------------*/,{
+ /*
+ * Phase mismatch handling from SCRIPT with WSR set.
+ * Such a condition can occur if the chip wants to
+ * execute a CHMOV(size > 1) when the WSR bit is
+ * set and the target changes PHASE.
+ *
+ * We must move the residual byte to memory.
+ *
+ * UA contains bit 0..31 of the address to
+ * move the residual byte.
+ * Move it to the table indirect.
+ */
+ SCR_STORE_REL (ua, 4),
+ offsetof (struct sym_ccb, phys.wresid.addr),
+ /*
+ * Increment UA (move address to next position).
+ */
+ SCR_REG_REG (ua, SCR_ADD, 1),
+ 0,
+ SCR_REG_REG (ua1, SCR_ADDC, 0),
+ 0,
+ SCR_REG_REG (ua2, SCR_ADDC, 0),
+ 0,
+ SCR_REG_REG (ua3, SCR_ADDC, 0),
+ 0,
+ /*
+ * Compute SCRATCHA as:
+ * - size to transfer = 1 byte.
+ * - bit 24..31 = high address bit [32...39].
+ */
+ SCR_LOAD_ABS (scratcha, 4),
+ PADDR_B (zero),
+ SCR_REG_REG (scratcha, SCR_OR, 1),
+ 0,
+ SCR_FROM_REG (rbc3),
+ 0,
+ SCR_TO_REG (scratcha3),
+ 0,
+ /*
+ * Move this value to the table indirect.
+ */
+ SCR_STORE_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.wresid.size),
+ /*
+ * Wait for a valid phase.
+ * While testing with bogus QUANTUM drives, the C1010
+ * sometimes raised a spurious phase mismatch with
+ * WSR and the CHMOV(1) triggered another PM.
+ * Waiting explicitely for the PHASE seemed to avoid
+ * the nested phase mismatch. Btw, this didn't happen
+ * using my IBM drives.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
+ 0,
+ /*
+ * Perform the move of the residual byte.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.wresid),
+ /*
+ * We can now handle the phase mismatch with UA fixed.
+ * RBC[0..23]=0 is a special case that does not require
+ * a PM context. The C code also checks against this.
+ */
+ SCR_FROM_REG (rbc),
+ 0,
+ SCR_RETURN ^ IFFALSE (DATA (0)),
+ 0,
+ SCR_FROM_REG (rbc1),
+ 0,
+ SCR_RETURN ^ IFFALSE (DATA (0)),
+ 0,
+ SCR_FROM_REG (rbc2),
+ 0,
+ SCR_RETURN ^ IFFALSE (DATA (0)),
+ 0,
+ /*
+ * RBC[0..23]=0.
+ * Not only we donnot need a PM context, but this would
+ * lead to a bogus CHMOV(0). This condition means that
+ * the residual was the last byte to move from this CHMOV.
+ * So, we just have to move the current data script pointer
+ * (i.e. TEMP) to the SCRIPTS address following the
+ * interrupted CHMOV and jump to dispatcher.
+ * IA contains the data pointer to save.
+ */
+ SCR_JUMP,
+ PADDR_B (pm_save_end),
+}/*-------------------------< WSR_MA_HELPER >--------------------*/,{
+ /*
+ * Helper for the C code when WSR bit is set.
+ * Perform the move of the residual byte.
+ */
+ SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ offsetof (struct sym_ccb, phys.wresid),
+ SCR_JUMP,
+ PADDR_A (dispatch),
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+}/*-------------------------< DATA_IO >--------------------------*/,{
+ /*
+ * We jump here if the data direction was unknown at the
+ * time we had to queue the command to the scripts processor.
+ * Pointers had been set as follow in this situation:
+ * savep --> DATA_IO
+ * lastp --> start pointer when DATA_IN
+ * wlastp --> start pointer when DATA_OUT
+ * This script sets savep and lastp according to the
+ * direction chosen by the target.
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ PADDR_B (data_io_out),
+}/*-------------------------< DATA_IO_IN >-----------------------*/,{
+ /*
+ * Direction is DATA IN.
+ */
+ SCR_LOAD_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+}/*-------------------------< DATA_IO_COM >----------------------*/,{
+ SCR_STORE_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.savep),
+
+ /*
+ * Jump to the SCRIPTS according to actual direction.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof (struct sym_ccb, phys.head.savep),
+ SCR_RETURN,
+ 0,
+}/*-------------------------< DATA_IO_OUT >----------------------*/,{
+ /*
+ * Direction is DATA OUT.
+ */
+ SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+ 0,
+ SCR_LOAD_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.wlastp),
+ SCR_STORE_REL (scratcha, 4),
+ offsetof (struct sym_ccb, phys.head.lastp),
+ SCR_JUMP,
+ PADDR_B(data_io_com),
+#endif /* SYM_OPT_HANDLE_DIR_UNKNOWN */
+
+}/*-------------------------< ZERO >-----------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< SCRATCH >--------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< PM0_DATA_ADDR >--------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< PM1_DATA_ADDR >--------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< DONE_POS >-------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< STARTPOS >-------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------< TARGTBL >--------------------------*/,{
+ SCR_DATA_ZERO,
+}/*-------------------------<>-----------------------------------*/
+};
+
+static struct SYM_FWZ_SCR SYM_FWZ_SCR = {
+ /*-------------------------< SNOOPTEST >------------------------*/{
+ /*
+ * Read the variable from memory.
+ */
+ SCR_LOAD_REL (scratcha, 4),
+ offsetof(struct sym_hcb, scratch),
+ /*
+ * Write the variable to memory.
+ */
+ SCR_STORE_REL (temp, 4),
+ offsetof(struct sym_hcb, scratch),
+ /*
+ * Read back the variable from memory.
+ */
+ SCR_LOAD_REL (temp, 4),
+ offsetof(struct sym_hcb, scratch),
+}/*-------------------------< SNOOPEND >-------------------------*/,{
+ /*
+ * And stop.
+ */
+ SCR_INT,
+ 99,
+}/*-------------------------<>-----------------------------------*/
+};
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
new file mode 100644
index 000000000000..a1dff6d437bc
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -0,0 +1,2196 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ * Copyright (c) 2003-2005 Matthew Wilcox <matthew@wil.cx>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+
+#include "sym_glue.h"
+#include "sym_nvram.h"
+
+#define NAME53C "sym53c"
+#define NAME53C8XX "sym53c8xx"
+
+/* SPARC just has to be different ... */
+#ifdef __sparc__
+#define IRQ_FMT "%s"
+#define IRQ_PRM(x) __irq_itoa(x)
+#else
+#define IRQ_FMT "%d"
+#define IRQ_PRM(x) (x)
+#endif
+
+struct sym_driver_setup sym_driver_setup = SYM_LINUX_DRIVER_SETUP;
+unsigned int sym_debug_flags = 0;
+
+static char *excl_string;
+static char *safe_string;
+module_param_named(cmd_per_lun, sym_driver_setup.max_tag, ushort, 0);
+module_param_string(tag_ctrl, sym_driver_setup.tag_ctrl, 100, 0);
+module_param_named(burst, sym_driver_setup.burst_order, byte, 0);
+module_param_named(led, sym_driver_setup.scsi_led, byte, 0);
+module_param_named(diff, sym_driver_setup.scsi_diff, byte, 0);
+module_param_named(irqm, sym_driver_setup.irq_mode, byte, 0);
+module_param_named(buschk, sym_driver_setup.scsi_bus_check, byte, 0);
+module_param_named(hostid, sym_driver_setup.host_id, byte, 0);
+module_param_named(verb, sym_driver_setup.verbose, byte, 0);
+module_param_named(debug, sym_debug_flags, uint, 0);
+module_param_named(settle, sym_driver_setup.settle_delay, byte, 0);
+module_param_named(nvram, sym_driver_setup.use_nvram, byte, 0);
+module_param_named(excl, excl_string, charp, 0);
+module_param_named(safe, safe_string, charp, 0);
+
+MODULE_PARM_DESC(cmd_per_lun, "The maximum number of tags to use by default");
+MODULE_PARM_DESC(tag_ctrl, "More detailed control over tags per LUN");
+MODULE_PARM_DESC(burst, "Maximum burst. 0 to disable, 255 to read from registers");
+MODULE_PARM_DESC(led, "Set to 1 to enable LED support");
+MODULE_PARM_DESC(diff, "0 for no differential mode, 1 for BIOS, 2 for always, 3 for not GPIO3");
+MODULE_PARM_DESC(irqm, "0 for open drain, 1 to leave alone, 2 for totem pole");
+MODULE_PARM_DESC(buschk, "0 to not check, 1 for detach on error, 2 for warn on error");
+MODULE_PARM_DESC(hostid, "The SCSI ID to use for the host adapters");
+MODULE_PARM_DESC(verb, "0 for minimal verbosity, 1 for normal, 2 for excessive");
+MODULE_PARM_DESC(debug, "Set bits to enable debugging");
+MODULE_PARM_DESC(settle, "Settle delay in seconds. Default 3");
+MODULE_PARM_DESC(nvram, "Option currently not used");
+MODULE_PARM_DESC(excl, "List ioport addresses here to prevent controllers from being attached");
+MODULE_PARM_DESC(safe, "Set other settings to a \"safe mode\"");
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SYM_VERSION);
+MODULE_AUTHOR("Matthew Wilcox <matthew@wil.cx>");
+MODULE_DESCRIPTION("NCR, Symbios and LSI 8xx and 1010 PCI SCSI adapters");
+
+static void sym2_setup_params(void)
+{
+ char *p = excl_string;
+ int xi = 0;
+
+ while (p && (xi < 8)) {
+ char *next_p;
+ int val = (int) simple_strtoul(p, &next_p, 0);
+ sym_driver_setup.excludes[xi++] = val;
+ p = next_p;
+ }
+
+ if (safe_string) {
+ if (*safe_string == 'y') {
+ sym_driver_setup.max_tag = 0;
+ sym_driver_setup.burst_order = 0;
+ sym_driver_setup.scsi_led = 0;
+ sym_driver_setup.scsi_diff = 1;
+ sym_driver_setup.irq_mode = 0;
+ sym_driver_setup.scsi_bus_check = 2;
+ sym_driver_setup.host_id = 7;
+ sym_driver_setup.verbose = 2;
+ sym_driver_setup.settle_delay = 10;
+ sym_driver_setup.use_nvram = 1;
+ } else if (*safe_string != 'n') {
+ printk(KERN_WARNING NAME53C8XX "Ignoring parameter %s"
+ " passed to safe option", safe_string);
+ }
+ }
+}
+
+/*
+ * We used to try to deal with 64-bit BARs here, but don't any more.
+ * There are many parts of this driver which would need to be modified
+ * to handle a 64-bit base address, including scripts. I'm uncomfortable
+ * with making those changes when I have no way of testing it, so I'm
+ * just going to disable it.
+ *
+ * Note that some machines (eg HP rx8620 and Superdome) have bus addresses
+ * below 4GB and physical addresses above 4GB. These will continue to work.
+ */
+static int __devinit
+pci_get_base_address(struct pci_dev *pdev, int index, unsigned long *basep)
+{
+ u32 tmp;
+ unsigned long base;
+#define PCI_BAR_OFFSET(index) (PCI_BASE_ADDRESS_0 + (index<<2))
+
+ pci_read_config_dword(pdev, PCI_BAR_OFFSET(index++), &tmp);
+ base = tmp;
+ if ((tmp & 0x7) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_read_config_dword(pdev, PCI_BAR_OFFSET(index++), &tmp);
+ if (tmp > 0)
+ dev_err(&pdev->dev,
+ "BAR %d is 64-bit, disabling\n", index - 1);
+ base = 0;
+ }
+
+ if ((base & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ base &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+
+ *basep = base;
+ return index;
+#undef PCI_BAR_OFFSET
+}
+
+static struct scsi_transport_template *sym2_transport_template = NULL;
+
+/*
+ * Used by the eh thread to wait for command completion.
+ * It is allocated on the eh thread stack.
+ */
+struct sym_eh_wait {
+ struct completion done;
+ struct timer_list timer;
+ void (*old_done)(struct scsi_cmnd *);
+ int to_do;
+ int timed_out;
+};
+
+/*
+ * Driver private area in the SCSI command structure.
+ */
+struct sym_ucmd { /* Override the SCSI pointer structure */
+ dma_addr_t data_mapping;
+ u_char data_mapped;
+ struct sym_eh_wait *eh_wait;
+};
+
+#define SYM_UCMD_PTR(cmd) ((struct sym_ucmd *)(&(cmd)->SCp))
+#define SYM_SOFTC_PTR(cmd) sym_get_hcb(cmd->device->host)
+
+static void __unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
+{
+ int dma_dir = cmd->sc_data_direction;
+
+ switch(SYM_UCMD_PTR(cmd)->data_mapped) {
+ case 2:
+ pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ break;
+ case 1:
+ pci_unmap_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping,
+ cmd->request_bufflen, dma_dir);
+ break;
+ }
+ SYM_UCMD_PTR(cmd)->data_mapped = 0;
+}
+
+static dma_addr_t __map_scsi_single_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
+{
+ dma_addr_t mapping;
+ int dma_dir = cmd->sc_data_direction;
+
+ mapping = pci_map_single(pdev, cmd->request_buffer,
+ cmd->request_bufflen, dma_dir);
+ if (mapping) {
+ SYM_UCMD_PTR(cmd)->data_mapped = 1;
+ SYM_UCMD_PTR(cmd)->data_mapping = mapping;
+ }
+
+ return mapping;
+}
+
+static int __map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
+{
+ int use_sg;
+ int dma_dir = cmd->sc_data_direction;
+
+ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+ if (use_sg > 0) {
+ SYM_UCMD_PTR(cmd)->data_mapped = 2;
+ SYM_UCMD_PTR(cmd)->data_mapping = use_sg;
+ }
+
+ return use_sg;
+}
+
+#define unmap_scsi_data(np, cmd) \
+ __unmap_scsi_data(np->s.device, cmd)
+#define map_scsi_single_data(np, cmd) \
+ __map_scsi_single_data(np->s.device, cmd)
+#define map_scsi_sg_data(np, cmd) \
+ __map_scsi_sg_data(np->s.device, cmd)
+/*
+ * Complete a pending CAM CCB.
+ */
+void sym_xpt_done(struct sym_hcb *np, struct scsi_cmnd *cmd)
+{
+ unmap_scsi_data(np, cmd);
+ cmd->scsi_done(cmd);
+}
+
+static void sym_xpt_done2(struct sym_hcb *np, struct scsi_cmnd *cmd, int cam_status)
+{
+ sym_set_cam_status(cmd, cam_status);
+ sym_xpt_done(np, cmd);
+}
+
+
+/*
+ * Tell the SCSI layer about a BUS RESET.
+ */
+void sym_xpt_async_bus_reset(struct sym_hcb *np)
+{
+ printf_notice("%s: SCSI BUS has been reset.\n", sym_name(np));
+ np->s.settle_time = jiffies + sym_driver_setup.settle_delay * HZ;
+ np->s.settle_time_valid = 1;
+ if (sym_verbose >= 2)
+ printf_info("%s: command processing suspended for %d seconds\n",
+ sym_name(np), sym_driver_setup.settle_delay);
+}
+
+/*
+ * Tell the SCSI layer about a BUS DEVICE RESET message sent.
+ */
+void sym_xpt_async_sent_bdr(struct sym_hcb *np, int target)
+{
+ printf_notice("%s: TARGET %d has been reset.\n", sym_name(np), target);
+}
+
+/*
+ * Choose the more appropriate CAM status if
+ * the IO encountered an extended error.
+ */
+static int sym_xerr_cam_status(int cam_status, int x_status)
+{
+ if (x_status) {
+ if (x_status & XE_PARITY_ERR)
+ cam_status = DID_PARITY;
+ else if (x_status &(XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN))
+ cam_status = DID_ERROR;
+ else if (x_status & XE_BAD_PHASE)
+ cam_status = DID_ERROR;
+ else
+ cam_status = DID_ERROR;
+ }
+ return cam_status;
+}
+
+/*
+ * Build CAM result for a failed or auto-sensed IO.
+ */
+void sym_set_cam_result_error(struct sym_hcb *np, struct sym_ccb *cp, int resid)
+{
+ struct scsi_cmnd *cmd = cp->cmd;
+ u_int cam_status, scsi_status, drv_status;
+
+ drv_status = 0;
+ cam_status = DID_OK;
+ scsi_status = cp->ssss_status;
+
+ if (cp->host_flags & HF_SENSE) {
+ scsi_status = cp->sv_scsi_status;
+ resid = cp->sv_resid;
+ if (sym_verbose && cp->sv_xerr_status)
+ sym_print_xerr(cmd, cp->sv_xerr_status);
+ if (cp->host_status == HS_COMPLETE &&
+ cp->ssss_status == S_GOOD &&
+ cp->xerr_status == 0) {
+ cam_status = sym_xerr_cam_status(DID_OK,
+ cp->sv_xerr_status);
+ drv_status = DRIVER_SENSE;
+ /*
+ * Bounce back the sense data to user.
+ */
+ memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ memcpy(cmd->sense_buffer, cp->sns_bbuf,
+ min(sizeof(cmd->sense_buffer),
+ (size_t)SYM_SNS_BBUF_LEN));
+#if 0
+ /*
+ * If the device reports a UNIT ATTENTION condition
+ * due to a RESET condition, we should consider all
+ * disconnect CCBs for this unit as aborted.
+ */
+ if (1) {
+ u_char *p;
+ p = (u_char *) cmd->sense_data;
+ if (p[0]==0x70 && p[2]==0x6 && p[12]==0x29)
+ sym_clear_tasks(np, DID_ABORT,
+ cp->target,cp->lun, -1);
+ }
+#endif
+ } else {
+ /*
+ * Error return from our internal request sense. This
+ * is bad: we must clear the contingent allegiance
+ * condition otherwise the device will always return
+ * BUSY. Use a big stick.
+ */
+ sym_reset_scsi_target(np, cmd->device->id);
+ cam_status = DID_ERROR;
+ }
+ } else if (cp->host_status == HS_COMPLETE) /* Bad SCSI status */
+ cam_status = DID_OK;
+ else if (cp->host_status == HS_SEL_TIMEOUT) /* Selection timeout */
+ cam_status = DID_NO_CONNECT;
+ else if (cp->host_status == HS_UNEXPECTED) /* Unexpected BUS FREE*/
+ cam_status = DID_ERROR;
+ else { /* Extended error */
+ if (sym_verbose) {
+ sym_print_addr(cmd, "COMMAND FAILED (%x %x %x).\n",
+ cp->host_status, cp->ssss_status,
+ cp->xerr_status);
+ }
+ /*
+ * Set the most appropriate value for CAM status.
+ */
+ cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status);
+ }
+ cmd->resid = resid;
+ cmd->result = (drv_status << 24) + (cam_status << 16) + scsi_status;
+}
+
+
+/*
+ * Build the scatter/gather array for an I/O.
+ */
+
+static int sym_scatter_no_sglist(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd)
+{
+ struct sym_tblmove *data = &cp->phys.data[SYM_CONF_MAX_SG-1];
+ int segment;
+
+ cp->data_len = cmd->request_bufflen;
+
+ if (cmd->request_bufflen) {
+ dma_addr_t baddr = map_scsi_single_data(np, cmd);
+ if (baddr) {
+ sym_build_sge(np, data, baddr, cmd->request_bufflen);
+ segment = 1;
+ } else {
+ segment = -2;
+ }
+ } else {
+ segment = 0;
+ }
+
+ return segment;
+}
+
+static int sym_scatter(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd)
+{
+ int segment;
+ int use_sg = (int) cmd->use_sg;
+
+ cp->data_len = 0;
+
+ if (!use_sg)
+ segment = sym_scatter_no_sglist(np, cp, cmd);
+ else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) {
+ struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+ struct sym_tblmove *data;
+
+ if (use_sg > SYM_CONF_MAX_SG) {
+ unmap_scsi_data(np, cmd);
+ return -1;
+ }
+
+ data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg];
+
+ for (segment = 0; segment < use_sg; segment++) {
+ dma_addr_t baddr = sg_dma_address(&scatter[segment]);
+ unsigned int len = sg_dma_len(&scatter[segment]);
+
+ sym_build_sge(np, &data[segment], baddr, len);
+ cp->data_len += len;
+ }
+ } else {
+ segment = -2;
+ }
+
+ return segment;
+}
+
+/*
+ * Queue a SCSI command.
+ */
+static int sym_queue_command(struct sym_hcb *np, struct scsi_cmnd *cmd)
+{
+ struct scsi_device *sdev = cmd->device;
+ struct sym_tcb *tp;
+ struct sym_lcb *lp;
+ struct sym_ccb *cp;
+ int order;
+
+ /*
+ * Minimal checkings, so that we will not
+ * go outside our tables.
+ */
+ if (sdev->id == np->myaddr ||
+ sdev->id >= SYM_CONF_MAX_TARGET ||
+ sdev->lun >= SYM_CONF_MAX_LUN) {
+ sym_xpt_done2(np, cmd, CAM_DEV_NOT_THERE);
+ return 0;
+ }
+
+ /*
+ * Retrieve the target descriptor.
+ */
+ tp = &np->target[sdev->id];
+
+ /*
+ * Complete the 1st INQUIRY command with error
+ * condition if the device is flagged NOSCAN
+ * at BOOT in the NVRAM. This may speed up
+ * the boot and maintain coherency with BIOS
+ * device numbering. Clearing the flag allows
+ * user to rescan skipped devices later.
+ * We also return error for devices not flagged
+ * for SCAN LUNS in the NVRAM since some mono-lun
+ * devices behave badly when asked for some non
+ * zero LUN. Btw, this is an absolute hack.:-)
+ */
+ if (cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 0x0) {
+ if ((tp->usrflags & SYM_SCAN_BOOT_DISABLED) ||
+ ((tp->usrflags & SYM_SCAN_LUNS_DISABLED) &&
+ sdev->lun != 0)) {
+ tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;
+ sym_xpt_done2(np, cmd, CAM_DEV_NOT_THERE);
+ return 0;
+ }
+ }
+
+ /*
+ * Select tagged/untagged.
+ */
+ lp = sym_lp(tp, sdev->lun);
+ order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0;
+
+ /*
+ * Queue the SCSI IO.
+ */
+ cp = sym_get_ccb(np, cmd, order);
+ if (!cp)
+ return 1; /* Means resource shortage */
+ sym_queue_scsiio(np, cmd, cp);
+ return 0;
+}
+
+/*
+ * Setup buffers and pointers that address the CDB.
+ */
+static inline int sym_setup_cdb(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp)
+{
+ u32 cmd_ba;
+ int cmd_len;
+
+ /*
+ * CDB is 16 bytes max.
+ */
+ if (cmd->cmd_len > sizeof(cp->cdb_buf)) {
+ sym_set_cam_status(cp->cmd, CAM_REQ_INVALID);
+ return -1;
+ }
+
+ memcpy(cp->cdb_buf, cmd->cmnd, cmd->cmd_len);
+ cmd_ba = CCB_BA (cp, cdb_buf[0]);
+ cmd_len = cmd->cmd_len;
+
+ cp->phys.cmd.addr = cpu_to_scr(cmd_ba);
+ cp->phys.cmd.size = cpu_to_scr(cmd_len);
+
+ return 0;
+}
+
+/*
+ * Setup pointers that address the data and start the I/O.
+ */
+int sym_setup_data_and_start(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp)
+{
+ int dir;
+ struct sym_tcb *tp = &np->target[cp->target];
+ struct sym_lcb *lp = sym_lp(tp, cp->lun);
+
+ /*
+ * Build the CDB.
+ */
+ if (sym_setup_cdb(np, cmd, cp))
+ goto out_abort;
+
+ /*
+ * No direction means no data.
+ */
+ dir = cmd->sc_data_direction;
+ if (dir != DMA_NONE) {
+ cp->segments = sym_scatter(np, cp, cmd);
+ if (cp->segments < 0) {
+ if (cp->segments == -2)
+ sym_set_cam_status(cmd, CAM_RESRC_UNAVAIL);
+ else
+ sym_set_cam_status(cmd, CAM_REQ_TOO_BIG);
+ goto out_abort;
+ }
+ } else {
+ cp->data_len = 0;
+ cp->segments = 0;
+ }
+
+ /*
+ * Set data pointers.
+ */
+ sym_setup_data_pointers(np, cp, dir);
+
+ /*
+ * When `#ifed 1', the code below makes the driver
+ * panic on the first attempt to write to a SCSI device.
+ * It is the first test we want to do after a driver
+ * change that does not seem obviously safe. :)
+ */
+#if 0
+ switch (cp->cdb_buf[0]) {
+ case 0x0A: case 0x2A: case 0xAA:
+ panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n");
+ break;
+ default:
+ break;
+ }
+#endif
+
+ /*
+ * activate this job.
+ */
+ if (lp)
+ sym_start_next_ccbs(np, lp, 2);
+ else
+ sym_put_start_queue(np, cp);
+ return 0;
+
+out_abort:
+ sym_free_ccb(np, cp);
+ sym_xpt_done(np, cmd);
+ return 0;
+}
+
+
+/*
+ * timer daemon.
+ *
+ * Misused to keep the driver running when
+ * interrupts are not configured correctly.
+ */
+static void sym_timer(struct sym_hcb *np)
+{
+ unsigned long thistime = jiffies;
+
+ /*
+ * Restart the timer.
+ */
+ np->s.timer.expires = thistime + SYM_CONF_TIMER_INTERVAL;
+ add_timer(&np->s.timer);
+
+ /*
+ * If we are resetting the ncr, wait for settle_time before
+ * clearing it. Then command processing will be resumed.
+ */
+ if (np->s.settle_time_valid) {
+ if (time_before_eq(np->s.settle_time, thistime)) {
+ if (sym_verbose >= 2 )
+ printk("%s: command processing resumed\n",
+ sym_name(np));
+ np->s.settle_time_valid = 0;
+ }
+ return;
+ }
+
+ /*
+ * Nothing to do for now, but that may come.
+ */
+ if (np->s.lasttime + 4*HZ < thistime) {
+ np->s.lasttime = thistime;
+ }
+
+#ifdef SYM_CONF_PCIQ_MAY_MISS_COMPLETIONS
+ /*
+ * Some way-broken PCI bridges may lead to
+ * completions being lost when the clearing
+ * of the INTFLY flag by the CPU occurs
+ * concurrently with the chip raising this flag.
+ * If this ever happen, lost completions will
+ * be reaped here.
+ */
+ sym_wakeup_done(np);
+#endif
+}
+
+
+/*
+ * PCI BUS error handler.
+ */
+void sym_log_bus_error(struct sym_hcb *np)
+{
+ u_short pci_sts;
+ pci_read_config_word(np->s.device, PCI_STATUS, &pci_sts);
+ if (pci_sts & 0xf900) {
+ pci_write_config_word(np->s.device, PCI_STATUS, pci_sts);
+ printf("%s: PCI STATUS = 0x%04x\n",
+ sym_name(np), pci_sts & 0xf900);
+ }
+}
+
+/*
+ * queuecommand method. Entered with the host adapter lock held and
+ * interrupts disabled.
+ */
+static int sym53c8xx_queue_command(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct sym_hcb *np = SYM_SOFTC_PTR(cmd);
+ struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd);
+ int sts = 0;
+
+ cmd->scsi_done = done;
+ memset(ucp, 0, sizeof(*ucp));
+
+ /*
+ * Shorten our settle_time if needed for
+ * this command not to time out.
+ */
+ if (np->s.settle_time_valid && cmd->timeout_per_command) {
+ unsigned long tlimit = jiffies + cmd->timeout_per_command;
+ tlimit -= SYM_CONF_TIMER_INTERVAL*2;
+ if (time_after(np->s.settle_time, tlimit)) {
+ np->s.settle_time = tlimit;
+ }
+ }
+
+ if (np->s.settle_time_valid)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ sts = sym_queue_command(np, cmd);
+ if (sts)
+ return SCSI_MLQUEUE_HOST_BUSY;
+ return 0;
+}
+
+/*
+ * Linux entry point of the interrupt handler.
+ */
+static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ unsigned long flags;
+ struct sym_hcb *np = (struct sym_hcb *)dev_id;
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("[");
+
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+ sym_interrupt(np);
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Linux entry point of the timer handler
+ */
+static void sym53c8xx_timer(unsigned long npref)
+{
+ struct sym_hcb *np = (struct sym_hcb *)npref;
+ unsigned long flags;
+
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+ sym_timer(np);
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
+}
+
+
+/*
+ * What the eh thread wants us to perform.
+ */
+#define SYM_EH_ABORT 0
+#define SYM_EH_DEVICE_RESET 1
+#define SYM_EH_BUS_RESET 2
+#define SYM_EH_HOST_RESET 3
+
+/*
+ * What we will do regarding the involved SCSI command.
+ */
+#define SYM_EH_DO_IGNORE 0
+#define SYM_EH_DO_COMPLETE 1
+#define SYM_EH_DO_WAIT 2
+
+/*
+ * Our general completion handler.
+ */
+static void __sym_eh_done(struct scsi_cmnd *cmd, int timed_out)
+{
+ struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait;
+ if (!ep)
+ return;
+
+ /* Try to avoid a race here (not 100% safe) */
+ if (!timed_out) {
+ ep->timed_out = 0;
+ if (ep->to_do == SYM_EH_DO_WAIT && !del_timer(&ep->timer))
+ return;
+ }
+
+ /* Revert everything */
+ SYM_UCMD_PTR(cmd)->eh_wait = NULL;
+ cmd->scsi_done = ep->old_done;
+
+ /* Wake up the eh thread if it wants to sleep */
+ if (ep->to_do == SYM_EH_DO_WAIT)
+ complete(&ep->done);
+}
+
+/*
+ * scsi_done() alias when error recovery is in progress.
+ */
+static void sym_eh_done(struct scsi_cmnd *cmd) { __sym_eh_done(cmd, 0); }
+
+/*
+ * Some timeout handler to avoid waiting too long.
+ */
+static void sym_eh_timeout(u_long p) { __sym_eh_done((struct scsi_cmnd *)p, 1); }
+
+/*
+ * Generic method for our eh processing.
+ * The 'op' argument tells what we have to do.
+ */
+static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
+{
+ struct sym_hcb *np = SYM_SOFTC_PTR(cmd);
+ SYM_QUEHEAD *qp;
+ int to_do = SYM_EH_DO_IGNORE;
+ int sts = -1;
+ struct sym_eh_wait eh, *ep = &eh;
+
+ dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname);
+
+#if 0
+ /* This one should be the result of some race, thus to ignore */
+ if (cmd->serial_number != cmd->serial_number_at_timeout)
+ goto prepare;
+#endif
+
+ /* This one is queued in some place -> to wait for completion */
+ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+ struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ if (cp->cmd == cmd) {
+ to_do = SYM_EH_DO_WAIT;
+ goto prepare;
+ }
+ }
+
+prepare:
+ /* Prepare stuff to either ignore, complete or wait for completion */
+ switch(to_do) {
+ default:
+ case SYM_EH_DO_IGNORE:
+ break;
+ case SYM_EH_DO_WAIT:
+ init_completion(&ep->done);
+ /* fall through */
+ case SYM_EH_DO_COMPLETE:
+ ep->old_done = cmd->scsi_done;
+ cmd->scsi_done = sym_eh_done;
+ SYM_UCMD_PTR(cmd)->eh_wait = ep;
+ }
+
+ /* Try to proceed the operation we have been asked for */
+ sts = -1;
+ switch(op) {
+ case SYM_EH_ABORT:
+ sts = sym_abort_scsiio(np, cmd, 1);
+ break;
+ case SYM_EH_DEVICE_RESET:
+ sts = sym_reset_scsi_target(np, cmd->device->id);
+ break;
+ case SYM_EH_BUS_RESET:
+ sym_reset_scsi_bus(np, 1);
+ sts = 0;
+ break;
+ case SYM_EH_HOST_RESET:
+ sym_reset_scsi_bus(np, 0);
+ sym_start_up (np, 1);
+ sts = 0;
+ break;
+ default:
+ break;
+ }
+
+ /* On error, restore everything and cross fingers :) */
+ if (sts) {
+ SYM_UCMD_PTR(cmd)->eh_wait = NULL;
+ cmd->scsi_done = ep->old_done;
+ to_do = SYM_EH_DO_IGNORE;
+ }
+
+ ep->to_do = to_do;
+ /* Complete the command with locks held as required by the driver */
+ if (to_do == SYM_EH_DO_COMPLETE)
+ sym_xpt_done2(np, cmd, CAM_REQ_ABORTED);
+
+ /* Wait for completion with locks released, as required by kernel */
+ if (to_do == SYM_EH_DO_WAIT) {
+ init_timer(&ep->timer);
+ ep->timer.expires = jiffies + (5*HZ);
+ ep->timer.function = sym_eh_timeout;
+ ep->timer.data = (u_long)cmd;
+ ep->timed_out = 1; /* Be pessimistic for once :) */
+ add_timer(&ep->timer);
+ spin_unlock_irq(np->s.host->host_lock);
+ wait_for_completion(&ep->done);
+ spin_lock_irq(np->s.host->host_lock);
+ if (ep->timed_out)
+ sts = -2;
+ }
+ dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname,
+ sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed");
+ return sts ? SCSI_FAILED : SCSI_SUCCESS;
+}
+
+
+/*
+ * Error handlers called from the eh thread (one thread per HBA).
+ */
+static int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd)
+{
+ return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);
+}
+
+static int sym53c8xx_eh_device_reset_handler(struct scsi_cmnd *cmd)
+{
+ return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);
+}
+
+static int sym53c8xx_eh_bus_reset_handler(struct scsi_cmnd *cmd)
+{
+ return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);
+}
+
+static int sym53c8xx_eh_host_reset_handler(struct scsi_cmnd *cmd)
+{
+ return sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd);
+}
+
+/*
+ * Tune device queuing depth, according to various limits.
+ */
+static void sym_tune_dev_queuing(struct sym_tcb *tp, int lun, u_short reqtags)
+{
+ struct sym_lcb *lp = sym_lp(tp, lun);
+ u_short oldtags;
+
+ if (!lp)
+ return;
+
+ oldtags = lp->s.reqtags;
+
+ if (reqtags > lp->s.scdev_depth)
+ reqtags = lp->s.scdev_depth;
+
+ lp->started_limit = reqtags ? reqtags : 2;
+ lp->started_max = 1;
+ lp->s.reqtags = reqtags;
+
+ if (reqtags != oldtags) {
+ dev_info(&tp->sdev->sdev_target->dev,
+ "tagged command queuing %s, command queue depth %d.\n",
+ lp->s.reqtags ? "enabled" : "disabled",
+ lp->started_limit);
+ }
+}
+
+/*
+ * Linux select queue depths function
+ */
+#define DEF_DEPTH (sym_driver_setup.max_tag)
+#define ALL_TARGETS -2
+#define NO_TARGET -1
+#define ALL_LUNS -2
+#define NO_LUN -1
+
+static int device_queue_depth(struct sym_hcb *np, int target, int lun)
+{
+ int c, h, t, u, v;
+ char *p = sym_driver_setup.tag_ctrl;
+ char *ep;
+
+ h = -1;
+ t = NO_TARGET;
+ u = NO_LUN;
+ while ((c = *p++) != 0) {
+ v = simple_strtoul(p, &ep, 0);
+ switch(c) {
+ case '/':
+ ++h;
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ case 't':
+ if (t != target)
+ t = (target == v) ? v : NO_TARGET;
+ u = ALL_LUNS;
+ break;
+ case 'u':
+ if (u != lun)
+ u = (lun == v) ? v : NO_LUN;
+ break;
+ case 'q':
+ if (h == np->s.unit &&
+ (t == ALL_TARGETS || t == target) &&
+ (u == ALL_LUNS || u == lun))
+ return v;
+ break;
+ case '-':
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ default:
+ break;
+ }
+ p = ep;
+ }
+ return DEF_DEPTH;
+}
+
+static int sym53c8xx_slave_alloc(struct scsi_device *device)
+{
+ struct sym_hcb *np = sym_get_hcb(device->host);
+ struct sym_tcb *tp = &np->target[device->id];
+ if (!tp->sdev)
+ tp->sdev = device;
+
+ return 0;
+}
+
+static void sym53c8xx_slave_destroy(struct scsi_device *device)
+{
+ struct sym_hcb *np = sym_get_hcb(device->host);
+ struct sym_tcb *tp = &np->target[device->id];
+ if (tp->sdev == device)
+ tp->sdev = NULL;
+}
+
+/*
+ * Linux entry point for device queue sizing.
+ */
+static int sym53c8xx_slave_configure(struct scsi_device *device)
+{
+ struct sym_hcb *np = sym_get_hcb(device->host);
+ struct sym_tcb *tp = &np->target[device->id];
+ struct sym_lcb *lp;
+ int reqtags, depth_to_use;
+
+ /*
+ * Allocate the LCB if not yet.
+ * If it fail, we may well be in the sh*t. :)
+ */
+ lp = sym_alloc_lcb(np, device->id, device->lun);
+ if (!lp)
+ return -ENOMEM;
+
+ /*
+ * Get user flags.
+ */
+ lp->curr_flags = lp->user_flags;
+
+ /*
+ * Select queue depth from driver setup.
+ * Donnot use more than configured by user.
+ * Use at least 2.
+ * Donnot use more than our maximum.
+ */
+ reqtags = device_queue_depth(np, device->id, device->lun);
+ if (reqtags > tp->usrtags)
+ reqtags = tp->usrtags;
+ if (!device->tagged_supported)
+ reqtags = 0;
+#if 1 /* Avoid to locally queue commands for no good reasons */
+ if (reqtags > SYM_CONF_MAX_TAG)
+ reqtags = SYM_CONF_MAX_TAG;
+ depth_to_use = (reqtags ? reqtags : 2);
+#else
+ depth_to_use = (reqtags ? SYM_CONF_MAX_TAG : 2);
+#endif
+ scsi_adjust_queue_depth(device,
+ (device->tagged_supported ?
+ MSG_SIMPLE_TAG : 0),
+ depth_to_use);
+ lp->s.scdev_depth = depth_to_use;
+ sym_tune_dev_queuing(tp, device->lun, reqtags);
+
+ if (!spi_initial_dv(device->sdev_target))
+ spi_dv_device(device);
+
+ return 0;
+}
+
+/*
+ * Linux entry point for info() function
+ */
+static const char *sym53c8xx_info (struct Scsi_Host *host)
+{
+ return SYM_DRIVER_NAME;
+}
+
+
+#ifdef SYM_LINUX_PROC_INFO_SUPPORT
+/*
+ * Proc file system stuff
+ *
+ * A read operation returns adapter information.
+ * A write operation is a control command.
+ * The string is parsed in the driver code and the command is passed
+ * to the sym_usercmd() function.
+ */
+
+#ifdef SYM_LINUX_USER_COMMAND_SUPPORT
+
+struct sym_usrcmd {
+ u_long target;
+ u_long lun;
+ u_long data;
+ u_long cmd;
+};
+
+#define UC_SETSYNC 10
+#define UC_SETTAGS 11
+#define UC_SETDEBUG 12
+#define UC_SETWIDE 14
+#define UC_SETFLAG 15
+#define UC_SETVERBOSE 17
+#define UC_RESETDEV 18
+#define UC_CLEARDEV 19
+
+static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc)
+{
+ struct sym_tcb *tp;
+ int t, l;
+
+ switch (uc->cmd) {
+ case 0: return;
+
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+ case UC_SETDEBUG:
+ sym_debug_flags = uc->data;
+ break;
+#endif
+ case UC_SETVERBOSE:
+ np->verbose = uc->data;
+ break;
+ default:
+ /*
+ * We assume that other commands apply to targets.
+ * This should always be the case and avoid the below
+ * 4 lines to be repeated 6 times.
+ */
+ for (t = 0; t < SYM_CONF_MAX_TARGET; t++) {
+ if (!((uc->target >> t) & 1))
+ continue;
+ tp = &np->target[t];
+
+ switch (uc->cmd) {
+
+ case UC_SETSYNC:
+ if (!uc->data || uc->data >= 255) {
+ tp->tgoal.iu = tp->tgoal.dt =
+ tp->tgoal.qas = 0;
+ tp->tgoal.offset = 0;
+ } else if (uc->data <= 9 && np->minsync_dt) {
+ if (uc->data < np->minsync_dt)
+ uc->data = np->minsync_dt;
+ tp->tgoal.iu = tp->tgoal.dt =
+ tp->tgoal.qas = 1;
+ tp->tgoal.width = 1;
+ tp->tgoal.period = uc->data;
+ tp->tgoal.offset = np->maxoffs_dt;
+ } else {
+ if (uc->data < np->minsync)
+ uc->data = np->minsync;
+ tp->tgoal.iu = tp->tgoal.dt =
+ tp->tgoal.qas = 0;
+ tp->tgoal.period = uc->data;
+ tp->tgoal.offset = np->maxoffs;
+ }
+ tp->tgoal.check_nego = 1;
+ break;
+ case UC_SETWIDE:
+ tp->tgoal.width = uc->data ? 1 : 0;
+ tp->tgoal.check_nego = 1;
+ break;
+ case UC_SETTAGS:
+ for (l = 0; l < SYM_CONF_MAX_LUN; l++)
+ sym_tune_dev_queuing(tp, l, uc->data);
+ break;
+ case UC_RESETDEV:
+ tp->to_reset = 1;
+ np->istat_sem = SEM;
+ OUTB(np, nc_istat, SIGP|SEM);
+ break;
+ case UC_CLEARDEV:
+ for (l = 0; l < SYM_CONF_MAX_LUN; l++) {
+ struct sym_lcb *lp = sym_lp(tp, l);
+ if (lp) lp->to_clear = 1;
+ }
+ np->istat_sem = SEM;
+ OUTB(np, nc_istat, SIGP|SEM);
+ break;
+ case UC_SETFLAG:
+ tp->usrflags = uc->data;
+ break;
+ }
+ }
+ break;
+ }
+}
+
+static int skip_spaces(char *ptr, int len)
+{
+ int cnt, c;
+
+ for (cnt = len; cnt > 0 && (c = *ptr++) && isspace(c); cnt--);
+
+ return (len - cnt);
+}
+
+static int get_int_arg(char *ptr, int len, u_long *pv)
+{
+ char *end;
+
+ *pv = simple_strtoul(ptr, &end, 10);
+ return (end - ptr);
+}
+
+static int is_keyword(char *ptr, int len, char *verb)
+{
+ int verb_len = strlen(verb);
+
+ if (len >= verb_len && !memcmp(verb, ptr, verb_len))
+ return verb_len;
+ else
+ return 0;
+}
+
+#define SKIP_SPACES(ptr, len) \
+ if ((arg_len = skip_spaces(ptr, len)) < 1) \
+ return -EINVAL; \
+ ptr += arg_len; len -= arg_len;
+
+#define GET_INT_ARG(ptr, len, v) \
+ if (!(arg_len = get_int_arg(ptr, len, &(v)))) \
+ return -EINVAL; \
+ ptr += arg_len; len -= arg_len;
+
+
+/*
+ * Parse a control command
+ */
+
+static int sym_user_command(struct sym_hcb *np, char *buffer, int length)
+{
+ char *ptr = buffer;
+ int len = length;
+ struct sym_usrcmd cmd, *uc = &cmd;
+ int arg_len;
+ u_long target;
+
+ memset(uc, 0, sizeof(*uc));
+
+ if (len > 0 && ptr[len-1] == '\n')
+ --len;
+
+ if ((arg_len = is_keyword(ptr, len, "setsync")) != 0)
+ uc->cmd = UC_SETSYNC;
+ else if ((arg_len = is_keyword(ptr, len, "settags")) != 0)
+ uc->cmd = UC_SETTAGS;
+ else if ((arg_len = is_keyword(ptr, len, "setverbose")) != 0)
+ uc->cmd = UC_SETVERBOSE;
+ else if ((arg_len = is_keyword(ptr, len, "setwide")) != 0)
+ uc->cmd = UC_SETWIDE;
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+ else if ((arg_len = is_keyword(ptr, len, "setdebug")) != 0)
+ uc->cmd = UC_SETDEBUG;
+#endif
+ else if ((arg_len = is_keyword(ptr, len, "setflag")) != 0)
+ uc->cmd = UC_SETFLAG;
+ else if ((arg_len = is_keyword(ptr, len, "resetdev")) != 0)
+ uc->cmd = UC_RESETDEV;
+ else if ((arg_len = is_keyword(ptr, len, "cleardev")) != 0)
+ uc->cmd = UC_CLEARDEV;
+ else
+ arg_len = 0;
+
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);
+#endif
+
+ if (!arg_len)
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+
+ switch(uc->cmd) {
+ case UC_SETSYNC:
+ case UC_SETTAGS:
+ case UC_SETWIDE:
+ case UC_SETFLAG:
+ case UC_RESETDEV:
+ case UC_CLEARDEV:
+ SKIP_SPACES(ptr, len);
+ if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+ ptr += arg_len; len -= arg_len;
+ uc->target = ~0;
+ } else {
+ GET_INT_ARG(ptr, len, target);
+ uc->target = (1<<target);
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: target=%ld\n", target);
+#endif
+ }
+ break;
+ }
+
+ switch(uc->cmd) {
+ case UC_SETVERBOSE:
+ case UC_SETSYNC:
+ case UC_SETTAGS:
+ case UC_SETWIDE:
+ SKIP_SPACES(ptr, len);
+ GET_INT_ARG(ptr, len, uc->data);
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: data=%ld\n", uc->data);
+#endif
+ break;
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+ case UC_SETDEBUG:
+ while (len > 0) {
+ SKIP_SPACES(ptr, len);
+ if ((arg_len = is_keyword(ptr, len, "alloc")))
+ uc->data |= DEBUG_ALLOC;
+ else if ((arg_len = is_keyword(ptr, len, "phase")))
+ uc->data |= DEBUG_PHASE;
+ else if ((arg_len = is_keyword(ptr, len, "queue")))
+ uc->data |= DEBUG_QUEUE;
+ else if ((arg_len = is_keyword(ptr, len, "result")))
+ uc->data |= DEBUG_RESULT;
+ else if ((arg_len = is_keyword(ptr, len, "scatter")))
+ uc->data |= DEBUG_SCATTER;
+ else if ((arg_len = is_keyword(ptr, len, "script")))
+ uc->data |= DEBUG_SCRIPT;
+ else if ((arg_len = is_keyword(ptr, len, "tiny")))
+ uc->data |= DEBUG_TINY;
+ else if ((arg_len = is_keyword(ptr, len, "timing")))
+ uc->data |= DEBUG_TIMING;
+ else if ((arg_len = is_keyword(ptr, len, "nego")))
+ uc->data |= DEBUG_NEGO;
+ else if ((arg_len = is_keyword(ptr, len, "tags")))
+ uc->data |= DEBUG_TAGS;
+ else if ((arg_len = is_keyword(ptr, len, "pointer")))
+ uc->data |= DEBUG_POINTER;
+ else
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+ }
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: data=%ld\n", uc->data);
+#endif
+ break;
+#endif /* SYM_LINUX_DEBUG_CONTROL_SUPPORT */
+ case UC_SETFLAG:
+ while (len > 0) {
+ SKIP_SPACES(ptr, len);
+ if ((arg_len = is_keyword(ptr, len, "no_disc")))
+ uc->data &= ~SYM_DISC_ENABLED;
+ else
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (len)
+ return -EINVAL;
+ else {
+ unsigned long flags;
+
+ spin_lock_irqsave(np->s.host->host_lock, flags);
+ sym_exec_user_command (np, uc);
+ spin_unlock_irqrestore(np->s.host->host_lock, flags);
+ }
+ return length;
+}
+
+#endif /* SYM_LINUX_USER_COMMAND_SUPPORT */
+
+
+#ifdef SYM_LINUX_USER_INFO_SUPPORT
+/*
+ * Informations through the proc file system.
+ */
+struct info_str {
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+/*
+ * Copy formatted information into the input buffer.
+ */
+static int sym_host_info(struct sym_hcb *np, char *ptr, off_t offset, int len)
+{
+ struct info_str info;
+
+ info.buffer = ptr;
+ info.length = len;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "Chip " NAME53C "%s, device id 0x%x, "
+ "revision id 0x%x\n",
+ np->s.chip_name, np->device_id, np->revision_id);
+ copy_info(&info, "At PCI address %s, IRQ " IRQ_FMT "\n",
+ pci_name(np->s.device), IRQ_PRM(np->s.irq));
+ copy_info(&info, "Min. period factor %d, %s SCSI BUS%s\n",
+ (int) (np->minsync_dt ? np->minsync_dt : np->minsync),
+ np->maxwide ? "Wide" : "Narrow",
+ np->minsync_dt ? ", DT capable" : "");
+
+ copy_info(&info, "Max. started commands %d, "
+ "max. commands per LUN %d\n",
+ SYM_CONF_MAX_START, SYM_CONF_MAX_TAG);
+
+ return info.pos > info.offset? info.pos - info.offset : 0;
+}
+#endif /* SYM_LINUX_USER_INFO_SUPPORT */
+
+/*
+ * Entry point of the scsi proc fs of the driver.
+ * - func = 0 means read (returns adapter infos)
+ * - func = 1 means write (not yet merget from sym53c8xx)
+ */
+static int sym53c8xx_proc_info(struct Scsi_Host *host, char *buffer,
+ char **start, off_t offset, int length, int func)
+{
+ struct sym_hcb *np = sym_get_hcb(host);
+ int retv;
+
+ if (func) {
+#ifdef SYM_LINUX_USER_COMMAND_SUPPORT
+ retv = sym_user_command(np, buffer, length);
+#else
+ retv = -EINVAL;
+#endif
+ } else {
+ if (start)
+ *start = buffer;
+#ifdef SYM_LINUX_USER_INFO_SUPPORT
+ retv = sym_host_info(np, buffer, offset, length);
+#else
+ retv = -EINVAL;
+#endif
+ }
+
+ return retv;
+}
+#endif /* SYM_LINUX_PROC_INFO_SUPPORT */
+
+/*
+ * Free controller resources.
+ */
+static void sym_free_resources(struct sym_hcb *np, struct pci_dev *pdev)
+{
+ /*
+ * Free O/S specific resources.
+ */
+ if (np->s.irq)
+ free_irq(np->s.irq, np);
+ if (np->s.ioaddr)
+ pci_iounmap(pdev, np->s.ioaddr);
+ if (np->s.ramaddr)
+ pci_iounmap(pdev, np->s.ramaddr);
+ /*
+ * Free O/S independent resources.
+ */
+ sym_hcb_free(np);
+
+ sym_mfree_dma(np, sizeof(*np), "HCB");
+}
+
+/*
+ * Ask/tell the system about DMA addressing.
+ */
+static int sym_setup_bus_dma_mask(struct sym_hcb *np)
+{
+#if SYM_CONF_DMA_ADDRESSING_MODE > 0
+#if SYM_CONF_DMA_ADDRESSING_MODE == 1
+#define DMA_DAC_MASK 0x000000ffffffffffULL /* 40-bit */
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
+#define DMA_DAC_MASK DMA_64BIT_MASK
+#endif
+ if ((np->features & FE_DAC) &&
+ !pci_set_dma_mask(np->s.device, DMA_DAC_MASK)) {
+ np->use_dac = 1;
+ return 0;
+ }
+#endif
+
+ if (!pci_set_dma_mask(np->s.device, DMA_32BIT_MASK))
+ return 0;
+
+ printf_warning("%s: No suitable DMA available\n", sym_name(np));
+ return -1;
+}
+
+/*
+ * Host attach and initialisations.
+ *
+ * Allocate host data and ncb structure.
+ * Remap MMIO region.
+ * Do chip initialization.
+ * If all is OK, install interrupt handling and
+ * start the timer daemon.
+ */
+static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
+ int unit, struct sym_device *dev)
+{
+ struct host_data *host_data;
+ struct sym_hcb *np = NULL;
+ struct Scsi_Host *instance = NULL;
+ struct pci_dev *pdev = dev->pdev;
+ unsigned long flags;
+ struct sym_fw *fw;
+
+ printk(KERN_INFO
+ "sym%d: <%s> rev 0x%x at pci %s irq " IRQ_FMT "\n",
+ unit, dev->chip.name, dev->chip.revision_id,
+ pci_name(pdev), IRQ_PRM(pdev->irq));
+
+ /*
+ * Get the firmware for this chip.
+ */
+ fw = sym_find_firmware(&dev->chip);
+ if (!fw)
+ goto attach_failed;
+
+ /*
+ * Allocate host_data structure
+ */
+ instance = scsi_host_alloc(tpnt, sizeof(*host_data));
+ if (!instance)
+ goto attach_failed;
+ host_data = (struct host_data *) instance->hostdata;
+
+ /*
+ * Allocate immediately the host control block,
+ * since we are only expecting to succeed. :)
+ * We keep track in the HCB of all the resources that
+ * are to be released on error.
+ */
+ np = __sym_calloc_dma(&pdev->dev, sizeof(*np), "HCB");
+ if (!np)
+ goto attach_failed;
+ np->s.device = pdev;
+ np->bus_dmat = &pdev->dev; /* Result in 1 DMA pool per HBA */
+ host_data->ncb = np;
+ np->s.host = instance;
+
+ pci_set_drvdata(pdev, np);
+
+ /*
+ * Copy some useful infos to the HCB.
+ */
+ np->hcb_ba = vtobus(np);
+ np->verbose = sym_driver_setup.verbose;
+ np->s.device = pdev;
+ np->s.unit = unit;
+ np->device_id = dev->chip.device_id;
+ np->revision_id = dev->chip.revision_id;
+ np->features = dev->chip.features;
+ np->clock_divn = dev->chip.nr_divisor;
+ np->maxoffs = dev->chip.offset_max;
+ np->maxburst = dev->chip.burst_max;
+ np->myaddr = dev->host_id;
+
+ /*
+ * Edit its name.
+ */
+ strlcpy(np->s.chip_name, dev->chip.name, sizeof(np->s.chip_name));
+ sprintf(np->s.inst_name, "sym%d", np->s.unit);
+
+ if (sym_setup_bus_dma_mask(np))
+ goto attach_failed;
+
+ /*
+ * Try to map the controller chip to
+ * virtual and physical memory.
+ */
+ np->mmio_ba = (u32)dev->mmio_base;
+ np->s.ioaddr = dev->s.ioaddr;
+ np->s.ramaddr = dev->s.ramaddr;
+ np->s.io_ws = (np->features & FE_IO256) ? 256 : 128;
+
+ /*
+ * Map on-chip RAM if present and supported.
+ */
+ if (!(np->features & FE_RAM))
+ dev->ram_base = 0;
+ if (dev->ram_base) {
+ np->ram_ba = (u32)dev->ram_base;
+ np->ram_ws = (np->features & FE_RAM8K) ? 8192 : 4096;
+ }
+
+ if (sym_hcb_attach(instance, fw, dev->nvram))
+ goto attach_failed;
+
+ /*
+ * Install the interrupt handler.
+ * If we synchonize the C code with SCRIPTS on interrupt,
+ * we do not want to share the INTR line at all.
+ */
+ if (request_irq(pdev->irq, sym53c8xx_intr, SA_SHIRQ, NAME53C8XX, np)) {
+ printf_err("%s: request irq %d failure\n",
+ sym_name(np), pdev->irq);
+ goto attach_failed;
+ }
+ np->s.irq = pdev->irq;
+
+ /*
+ * After SCSI devices have been opened, we cannot
+ * reset the bus safely, so we do it here.
+ */
+ spin_lock_irqsave(instance->host_lock, flags);
+ if (sym_reset_scsi_bus(np, 0))
+ goto reset_failed;
+
+ /*
+ * Start the SCRIPTS.
+ */
+ sym_start_up (np, 1);
+
+ /*
+ * Start the timer daemon
+ */
+ init_timer(&np->s.timer);
+ np->s.timer.data = (unsigned long) np;
+ np->s.timer.function = sym53c8xx_timer;
+ np->s.lasttime=0;
+ sym_timer (np);
+
+ /*
+ * Fill Linux host instance structure
+ * and return success.
+ */
+ instance->max_channel = 0;
+ instance->this_id = np->myaddr;
+ instance->max_id = np->maxwide ? 16 : 8;
+ instance->max_lun = SYM_CONF_MAX_LUN;
+ instance->unique_id = pci_resource_start(pdev, 0);
+ instance->cmd_per_lun = SYM_CONF_MAX_TAG;
+ instance->can_queue = (SYM_CONF_MAX_START-2);
+ instance->sg_tablesize = SYM_CONF_MAX_SG;
+ instance->max_cmd_len = 16;
+ BUG_ON(sym2_transport_template == NULL);
+ instance->transportt = sym2_transport_template;
+
+ spin_unlock_irqrestore(instance->host_lock, flags);
+
+ return instance;
+
+ reset_failed:
+ printf_err("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, "
+ "TERMINATION, DEVICE POWER etc.!\n", sym_name(np));
+ spin_unlock_irqrestore(instance->host_lock, flags);
+ attach_failed:
+ if (!instance)
+ return NULL;
+ printf_info("%s: giving up ...\n", sym_name(np));
+ if (np)
+ sym_free_resources(np, pdev);
+ scsi_host_put(instance);
+
+ return NULL;
+ }
+
+
+/*
+ * Detect and try to read SYMBIOS and TEKRAM NVRAM.
+ */
+#if SYM_CONF_NVRAM_SUPPORT
+static void __devinit sym_get_nvram(struct sym_device *devp, struct sym_nvram *nvp)
+{
+ devp->nvram = nvp;
+ devp->device_id = devp->chip.device_id;
+ nvp->type = 0;
+
+ sym_read_nvram(devp, nvp);
+}
+#else
+static inline void sym_get_nvram(struct sym_device *devp, struct sym_nvram *nvp)
+{
+}
+#endif /* SYM_CONF_NVRAM_SUPPORT */
+
+static int __devinit sym_check_supported(struct sym_device *device)
+{
+ struct sym_chip *chip;
+ struct pci_dev *pdev = device->pdev;
+ u_char revision;
+ unsigned long io_port = pci_resource_start(pdev, 0);
+ int i;
+
+ /*
+ * If user excluded this chip, do not initialize it.
+ * I hate this code so much. Must kill it.
+ */
+ if (io_port) {
+ for (i = 0 ; i < 8 ; i++) {
+ if (sym_driver_setup.excludes[i] == io_port)
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Check if the chip is supported. Then copy the chip description
+ * to our device structure so we can make it match the actual device
+ * and options.
+ */
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
+ chip = sym_lookup_chip_table(pdev->device, revision);
+ if (!chip) {
+ dev_info(&pdev->dev, "device not supported\n");
+ return -ENODEV;
+ }
+ memcpy(&device->chip, chip, sizeof(device->chip));
+ device->chip.revision_id = revision;
+
+ return 0;
+}
+
+/*
+ * Ignore Symbios chips controlled by various RAID controllers.
+ * These controllers set value 0x52414944 at RAM end - 16.
+ */
+static int __devinit sym_check_raid(struct sym_device *device)
+{
+ unsigned int ram_size, ram_val;
+
+ if (!device->s.ramaddr)
+ return 0;
+
+ if (device->chip.features & FE_RAM8K)
+ ram_size = 8192;
+ else
+ ram_size = 4096;
+
+ ram_val = readl(device->s.ramaddr + ram_size - 16);
+ if (ram_val != 0x52414944)
+ return 0;
+
+ dev_info(&device->pdev->dev,
+ "not initializing, driven by RAID controller.\n");
+ return -ENODEV;
+}
+
+static int __devinit sym_set_workarounds(struct sym_device *device)
+{
+ struct sym_chip *chip = &device->chip;
+ struct pci_dev *pdev = device->pdev;
+ u_short status_reg;
+
+ /*
+ * (ITEM 12 of a DEL about the 896 I haven't yet).
+ * We must ensure the chip will use WRITE AND INVALIDATE.
+ * The revision number limit is for now arbitrary.
+ */
+ if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && chip->revision_id < 0x4) {
+ chip->features |= (FE_WRIE | FE_CLSE);
+ }
+
+ /* If the chip can do Memory Write Invalidate, enable it */
+ if (chip->features & FE_WRIE) {
+ if (pci_set_mwi(pdev))
+ return -ENODEV;
+ }
+
+ /*
+ * Work around for errant bit in 895A. The 66Mhz
+ * capable bit is set erroneously. Clear this bit.
+ * (Item 1 DEL 533)
+ *
+ * Make sure Config space and Features agree.
+ *
+ * Recall: writes are not normal to status register -
+ * write a 1 to clear and a 0 to leave unchanged.
+ * Can only reset bits.
+ */
+ pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+ if (chip->features & FE_66MHZ) {
+ if (!(status_reg & PCI_STATUS_66MHZ))
+ chip->features &= ~FE_66MHZ;
+ } else {
+ if (status_reg & PCI_STATUS_66MHZ) {
+ status_reg = PCI_STATUS_66MHZ;
+ pci_write_config_word(pdev, PCI_STATUS, status_reg);
+ pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Read and check the PCI configuration for any detected NCR
+ * boards and save data for attaching after all boards have
+ * been detected.
+ */
+static void __devinit
+sym_init_device(struct pci_dev *pdev, struct sym_device *device)
+{
+ int i;
+
+ device->host_id = SYM_SETUP_HOST_ID;
+ device->pdev = pdev;
+
+ i = pci_get_base_address(pdev, 1, &device->mmio_base);
+ pci_get_base_address(pdev, i, &device->ram_base);
+
+#ifndef CONFIG_SCSI_SYM53C8XX_IOMAPPED
+ if (device->mmio_base)
+ device->s.ioaddr = pci_iomap(pdev, 1,
+ pci_resource_len(pdev, 1));
+#endif
+ if (!device->s.ioaddr)
+ device->s.ioaddr = pci_iomap(pdev, 0,
+ pci_resource_len(pdev, 0));
+ if (device->ram_base)
+ device->s.ramaddr = pci_iomap(pdev, i,
+ pci_resource_len(pdev, i));
+}
+
+/*
+ * The NCR PQS and PDS cards are constructed as a DEC bridge
+ * behind which sits a proprietary NCR memory controller and
+ * either four or two 53c875s as separate devices. We can tell
+ * if an 875 is part of a PQS/PDS or not since if it is, it will
+ * be on the same bus as the memory controller. In its usual
+ * mode of operation, the 875s are slaved to the memory
+ * controller for all transfers. To operate with the Linux
+ * driver, the memory controller is disabled and the 875s
+ * freed to function independently. The only wrinkle is that
+ * the preset SCSI ID (which may be zero) must be read in from
+ * a special configuration space register of the 875.
+ */
+static void sym_config_pqs(struct pci_dev *pdev, struct sym_device *sym_dev)
+{
+ int slot;
+ u8 tmp;
+
+ for (slot = 0; slot < 256; slot++) {
+ struct pci_dev *memc = pci_get_slot(pdev->bus, slot);
+
+ if (!memc || memc->vendor != 0x101a || memc->device == 0x0009) {
+ pci_dev_put(memc);
+ continue;
+ }
+
+ /* bit 1: allow individual 875 configuration */
+ pci_read_config_byte(memc, 0x44, &tmp);
+ if ((tmp & 0x2) == 0) {
+ tmp |= 0x2;
+ pci_write_config_byte(memc, 0x44, tmp);
+ }
+
+ /* bit 2: drive individual 875 interrupts to the bus */
+ pci_read_config_byte(memc, 0x45, &tmp);
+ if ((tmp & 0x4) == 0) {
+ tmp |= 0x4;
+ pci_write_config_byte(memc, 0x45, tmp);
+ }
+
+ pci_dev_put(memc);
+ break;
+ }
+
+ pci_read_config_byte(pdev, 0x84, &tmp);
+ sym_dev->host_id = tmp;
+}
+
+/*
+ * Called before unloading the module.
+ * Detach the host.
+ * We have to free resources and halt the NCR chip.
+ */
+static int sym_detach(struct sym_hcb *np, struct pci_dev *pdev)
+{
+ printk("%s: detaching ...\n", sym_name(np));
+
+ del_timer_sync(&np->s.timer);
+
+ /*
+ * Reset NCR chip.
+ * We should use sym_soft_reset(), but we don't want to do
+ * so, since we may not be safe if interrupts occur.
+ */
+ printk("%s: resetting chip\n", sym_name(np));
+ OUTB(np, nc_istat, SRST);
+ udelay(10);
+ OUTB(np, nc_istat, 0);
+
+ sym_free_resources(np, pdev);
+
+ return 1;
+}
+
+/*
+ * Driver host template.
+ */
+static struct scsi_host_template sym2_template = {
+ .module = THIS_MODULE,
+ .name = "sym53c8xx",
+ .info = sym53c8xx_info,
+ .queuecommand = sym53c8xx_queue_command,
+ .slave_alloc = sym53c8xx_slave_alloc,
+ .slave_configure = sym53c8xx_slave_configure,
+ .slave_destroy = sym53c8xx_slave_destroy,
+ .eh_abort_handler = sym53c8xx_eh_abort_handler,
+ .eh_device_reset_handler = sym53c8xx_eh_device_reset_handler,
+ .eh_bus_reset_handler = sym53c8xx_eh_bus_reset_handler,
+ .eh_host_reset_handler = sym53c8xx_eh_host_reset_handler,
+ .this_id = 7,
+ .use_clustering = DISABLE_CLUSTERING,
+#ifdef SYM_LINUX_PROC_INFO_SUPPORT
+ .proc_info = sym53c8xx_proc_info,
+ .proc_name = NAME53C8XX,
+#endif
+};
+
+static int attach_count;
+
+static int __devinit sym2_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sym_device sym_dev;
+ struct sym_nvram nvram;
+ struct Scsi_Host *instance;
+
+ memset(&sym_dev, 0, sizeof(sym_dev));
+ memset(&nvram, 0, sizeof(nvram));
+
+ if (pci_enable_device(pdev))
+ goto leave;
+
+ pci_set_master(pdev);
+
+ if (pci_request_regions(pdev, NAME53C8XX))
+ goto disable;
+
+ sym_init_device(pdev, &sym_dev);
+ if (sym_check_supported(&sym_dev))
+ goto free;
+
+ if (sym_check_raid(&sym_dev))
+ goto leave; /* Don't disable the device */
+
+ if (sym_set_workarounds(&sym_dev))
+ goto free;
+
+ sym_config_pqs(pdev, &sym_dev);
+
+ sym_get_nvram(&sym_dev, &nvram);
+
+ instance = sym_attach(&sym2_template, attach_count, &sym_dev);
+ if (!instance)
+ goto free;
+
+ if (scsi_add_host(instance, &pdev->dev))
+ goto detach;
+ scsi_scan_host(instance);
+
+ attach_count++;
+
+ return 0;
+
+ detach:
+ sym_detach(pci_get_drvdata(pdev), pdev);
+ free:
+ pci_release_regions(pdev);
+ disable:
+ pci_disable_device(pdev);
+ leave:
+ return -ENODEV;
+}
+
+static void __devexit sym2_remove(struct pci_dev *pdev)
+{
+ struct sym_hcb *np = pci_get_drvdata(pdev);
+ struct Scsi_Host *host = np->s.host;
+
+ scsi_remove_host(host);
+ scsi_host_put(host);
+
+ sym_detach(np, pdev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+
+ attach_count--;
+}
+
+static void sym2_get_signalling(struct Scsi_Host *shost)
+{
+ struct sym_hcb *np = sym_get_hcb(shost);
+ enum spi_signal_type type;
+
+ switch (np->scsi_mode) {
+ case SMODE_SE:
+ type = SPI_SIGNAL_SE;
+ break;
+ case SMODE_LVD:
+ type = SPI_SIGNAL_LVD;
+ break;
+ case SMODE_HVD:
+ type = SPI_SIGNAL_HVD;
+ break;
+ default:
+ type = SPI_SIGNAL_UNKNOWN;
+ break;
+ }
+ spi_signalling(shost) = type;
+}
+
+static void sym2_set_offset(struct scsi_target *starget, int offset)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct sym_hcb *np = sym_get_hcb(shost);
+ struct sym_tcb *tp = &np->target[starget->id];
+
+ tp->tgoal.offset = offset;
+ tp->tgoal.check_nego = 1;
+}
+
+static void sym2_set_period(struct scsi_target *starget, int period)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct sym_hcb *np = sym_get_hcb(shost);
+ struct sym_tcb *tp = &np->target[starget->id];
+
+ /* have to have DT for these transfers */
+ if (period <= np->minsync)
+ tp->tgoal.dt = 1;
+
+ tp->tgoal.period = period;
+ tp->tgoal.check_nego = 1;
+}
+
+static void sym2_set_width(struct scsi_target *starget, int width)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct sym_hcb *np = sym_get_hcb(shost);
+ struct sym_tcb *tp = &np->target[starget->id];
+
+ /* It is illegal to have DT set on narrow transfers. If DT is
+ * clear, we must also clear IU and QAS. */
+ if (width == 0)
+ tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0;
+
+ tp->tgoal.width = width;
+ tp->tgoal.check_nego = 1;
+}
+
+static void sym2_set_dt(struct scsi_target *starget, int dt)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct sym_hcb *np = sym_get_hcb(shost);
+ struct sym_tcb *tp = &np->target[starget->id];
+
+ /* We must clear QAS and IU if DT is clear */
+ if (dt)
+ tp->tgoal.dt = 1;
+ else
+ tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0;
+ tp->tgoal.check_nego = 1;
+}
+
+static void sym2_set_iu(struct scsi_target *starget, int iu)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct sym_hcb *np = sym_get_hcb(shost);
+ struct sym_tcb *tp = &np->target[starget->id];
+
+ if (iu)
+ tp->tgoal.iu = tp->tgoal.dt = 1;
+ else
+ tp->tgoal.iu = 0;
+ tp->tgoal.check_nego = 1;
+}
+
+static void sym2_set_qas(struct scsi_target *starget, int qas)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct sym_hcb *np = sym_get_hcb(shost);
+ struct sym_tcb *tp = &np->target[starget->id];
+
+ if (qas)
+ tp->tgoal.dt = tp->tgoal.qas = 1;
+ else
+ tp->tgoal.qas = 0;
+ tp->tgoal.check_nego = 1;
+}
+
+
+static struct spi_function_template sym2_transport_functions = {
+ .set_offset = sym2_set_offset,
+ .show_offset = 1,
+ .set_period = sym2_set_period,
+ .show_period = 1,
+ .set_width = sym2_set_width,
+ .show_width = 1,
+ .set_dt = sym2_set_dt,
+ .show_dt = 1,
+ .set_iu = sym2_set_iu,
+ .show_iu = 1,
+ .set_qas = sym2_set_qas,
+ .show_qas = 1,
+ .get_signalling = sym2_get_signalling,
+};
+
+static struct pci_device_id sym2_id_table[] __devinitdata = {
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C810,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C820,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C825,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C815,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C810AP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C860,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1510,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C896,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C895,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C885,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C1510,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C895A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C875A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1010_33,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1010_66,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875J,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, sym2_id_table);
+
+static struct pci_driver sym2_driver = {
+ .name = NAME53C8XX,
+ .id_table = sym2_id_table,
+ .probe = sym2_probe,
+ .remove = __devexit_p(sym2_remove),
+};
+
+static int __init sym2_init(void)
+{
+ int error;
+
+ sym2_setup_params();
+ sym2_transport_template = spi_attach_transport(&sym2_transport_functions);
+ if (!sym2_transport_template)
+ return -ENODEV;
+
+ error = pci_register_driver(&sym2_driver);
+ if (error)
+ spi_release_transport(sym2_transport_template);
+ return error;
+}
+
+static void __exit sym2_exit(void)
+{
+ pci_unregister_driver(&sym2_driver);
+ spi_release_transport(sym2_transport_template);
+}
+
+module_init(sym2_init);
+module_exit(sym2_exit);
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.h b/drivers/scsi/sym53c8xx_2/sym_glue.h
new file mode 100644
index 000000000000..e943f167fb51
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.h
@@ -0,0 +1,300 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM_GLUE_H
+#define SYM_GLUE_H
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#ifdef __sparc__
+# include <asm/irq.h>
+#endif
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_spi.h>
+#include <scsi/scsi_host.h>
+
+#include "sym53c8xx.h"
+#include "sym_defs.h"
+#include "sym_misc.h"
+
+/*
+ * Configuration addendum for Linux.
+ */
+#define SYM_CONF_TIMER_INTERVAL ((HZ+1)/2)
+
+#define SYM_OPT_HANDLE_DIR_UNKNOWN
+#define SYM_OPT_HANDLE_DEVICE_QUEUEING
+#define SYM_OPT_LIMIT_COMMAND_REORDERING
+
+/*
+ * Print a message with severity.
+ */
+#define printf_emerg(args...) printk(KERN_EMERG args)
+#define printf_alert(args...) printk(KERN_ALERT args)
+#define printf_crit(args...) printk(KERN_CRIT args)
+#define printf_err(args...) printk(KERN_ERR args)
+#define printf_warning(args...) printk(KERN_WARNING args)
+#define printf_notice(args...) printk(KERN_NOTICE args)
+#define printf_info(args...) printk(KERN_INFO args)
+#define printf_debug(args...) printk(KERN_DEBUG args)
+#define printf(args...) printk(args)
+
+/*
+ * A 'read barrier' flushes any data that have been prefetched
+ * by the processor due to out of order execution. Such a barrier
+ * must notably be inserted prior to looking at data that have
+ * been DMAed, assuming that program does memory READs in proper
+ * order and that the device ensured proper ordering of WRITEs.
+ *
+ * A 'write barrier' prevents any previous WRITEs to pass further
+ * WRITEs. Such barriers must be inserted each time another agent
+ * relies on ordering of WRITEs.
+ *
+ * Note that, due to posting of PCI memory writes, we also must
+ * insert dummy PCI read transactions when some ordering involving
+ * both directions over the PCI does matter. PCI transactions are
+ * fully ordered in each direction.
+ */
+
+#define MEMORY_READ_BARRIER() rmb()
+#define MEMORY_WRITE_BARRIER() wmb()
+
+/*
+ * IO functions definition for big/little endian CPU support.
+ * For now, PCI chips are only supported in little endian addressing mode,
+ */
+
+#ifdef __BIG_ENDIAN
+
+#define readw_l2b readw
+#define readl_l2b readl
+#define writew_b2l writew
+#define writel_b2l writel
+
+#else /* little endian */
+
+#define readw_raw readw
+#define readl_raw readl
+#define writew_raw writew
+#define writel_raw writel
+
+#endif /* endian */
+
+#ifdef SYM_CONF_CHIP_BIG_ENDIAN
+#error "Chips in BIG ENDIAN addressing mode are not (yet) supported"
+#endif
+
+/*
+ * If the CPU and the chip use same endian-ness addressing,
+ * no byte reordering is needed for script patching.
+ * Macro cpu_to_scr() is to be used for script patching.
+ * Macro scr_to_cpu() is to be used for getting a DWORD
+ * from the script.
+ */
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+/*
+ * Remap some status field values.
+ */
+#define CAM_REQ_CMP DID_OK
+#define CAM_SEL_TIMEOUT DID_NO_CONNECT
+#define CAM_CMD_TIMEOUT DID_TIME_OUT
+#define CAM_REQ_ABORTED DID_ABORT
+#define CAM_UNCOR_PARITY DID_PARITY
+#define CAM_SCSI_BUS_RESET DID_RESET
+#define CAM_REQUEUE_REQ DID_SOFT_ERROR
+#define CAM_UNEXP_BUSFREE DID_ERROR
+#define CAM_SCSI_BUSY DID_BUS_BUSY
+
+#define CAM_DEV_NOT_THERE DID_NO_CONNECT
+#define CAM_REQ_INVALID DID_ERROR
+#define CAM_REQ_TOO_BIG DID_ERROR
+
+#define CAM_RESRC_UNAVAIL DID_ERROR
+
+/*
+ * Remap data direction values.
+ */
+#define CAM_DIR_NONE DMA_NONE
+#define CAM_DIR_IN DMA_FROM_DEVICE
+#define CAM_DIR_OUT DMA_TO_DEVICE
+#define CAM_DIR_UNKNOWN DMA_BIDIRECTIONAL
+
+/*
+ * These ones are used as return code from
+ * error recovery handlers under Linux.
+ */
+#define SCSI_SUCCESS SUCCESS
+#define SCSI_FAILED FAILED
+
+/*
+ * System specific target data structure.
+ * None for now, under Linux.
+ */
+/* #define SYM_HAVE_STCB */
+
+/*
+ * System specific lun data structure.
+ */
+#define SYM_HAVE_SLCB
+struct sym_slcb {
+ u_short reqtags; /* Number of tags requested by user */
+ u_short scdev_depth; /* Queue depth set in select_queue_depth() */
+};
+
+/*
+ * System specific command data structure.
+ * Not needed under Linux.
+ */
+/* struct sym_sccb */
+
+/*
+ * System specific host data structure.
+ */
+struct sym_shcb {
+ /*
+ * Chip and controller indentification.
+ */
+ int unit;
+ char inst_name[16];
+ char chip_name[8];
+ struct pci_dev *device;
+
+ struct Scsi_Host *host;
+
+ void __iomem * ioaddr; /* MMIO kernel io address */
+ void __iomem * ramaddr; /* RAM kernel io address */
+ u_short io_ws; /* IO window size */
+ int irq; /* IRQ number */
+
+ struct timer_list timer; /* Timer handler link header */
+ u_long lasttime;
+ u_long settle_time; /* Resetting the SCSI BUS */
+ u_char settle_time_valid;
+};
+
+/*
+ * Return the name of the controller.
+ */
+#define sym_name(np) (np)->s.inst_name
+
+struct sym_nvram;
+
+/*
+ * The IO macros require a struct called 's' and are abused in sym_nvram.c
+ */
+struct sym_device {
+ struct pci_dev *pdev;
+ unsigned long mmio_base;
+ unsigned long ram_base;
+ struct {
+ void __iomem *ioaddr;
+ void __iomem *ramaddr;
+ } s;
+ struct sym_chip chip;
+ struct sym_nvram *nvram;
+ u_short device_id;
+ u_char host_id;
+};
+
+/*
+ * Driver host data structure.
+ */
+struct host_data {
+ struct sym_hcb *ncb;
+};
+
+static inline struct sym_hcb * sym_get_hcb(struct Scsi_Host *host)
+{
+ return ((struct host_data *)host->hostdata)->ncb;
+}
+
+#include "sym_fw.h"
+#include "sym_hipd.h"
+
+/*
+ * Set the status field of a CAM CCB.
+ */
+static __inline void
+sym_set_cam_status(struct scsi_cmnd *cmd, int status)
+{
+ cmd->result &= ~(0xff << 16);
+ cmd->result |= (status << 16);
+}
+
+/*
+ * Get the status field of a CAM CCB.
+ */
+static __inline int
+sym_get_cam_status(struct scsi_cmnd *cmd)
+{
+ return host_byte(cmd->result);
+}
+
+/*
+ * Build CAM result for a successful IO and for a failed IO.
+ */
+static __inline void sym_set_cam_result_ok(struct sym_ccb *cp, struct scsi_cmnd *cmd, int resid)
+{
+ cmd->resid = resid;
+ cmd->result = (((DID_OK) << 16) + ((cp->ssss_status) & 0x7f));
+}
+void sym_set_cam_result_error(struct sym_hcb *np, struct sym_ccb *cp, int resid);
+
+void sym_xpt_done(struct sym_hcb *np, struct scsi_cmnd *ccb);
+#define sym_print_addr(cmd, arg...) dev_info(&cmd->device->sdev_gendev , ## arg)
+void sym_xpt_async_bus_reset(struct sym_hcb *np);
+void sym_xpt_async_sent_bdr(struct sym_hcb *np, int target);
+int sym_setup_data_and_start (struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp);
+void sym_log_bus_error(struct sym_hcb *np);
+void sym_sniff_inquiry(struct sym_hcb *np, struct scsi_cmnd *cmd, int resid);
+
+#endif /* SYM_GLUE_H */
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
new file mode 100644
index 000000000000..50a176b3888d
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -0,0 +1,5865 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ * Copyright (c) 2003-2005 Matthew Wilcox <matthew@wil.cx>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "sym_glue.h"
+#include "sym_nvram.h"
+
+#if 0
+#define SYM_DEBUG_GENERIC_SUPPORT
+#endif
+
+/*
+ * Needed function prototypes.
+ */
+static void sym_int_ma (struct sym_hcb *np);
+static void sym_int_sir (struct sym_hcb *np);
+static struct sym_ccb *sym_alloc_ccb(struct sym_hcb *np);
+static struct sym_ccb *sym_ccb_from_dsa(struct sym_hcb *np, u32 dsa);
+static void sym_alloc_lcb_tags (struct sym_hcb *np, u_char tn, u_char ln);
+static void sym_complete_error (struct sym_hcb *np, struct sym_ccb *cp);
+static void sym_complete_ok (struct sym_hcb *np, struct sym_ccb *cp);
+static int sym_compute_residual(struct sym_hcb *np, struct sym_ccb *cp);
+
+/*
+ * Print a buffer in hexadecimal format with a ".\n" at end.
+ */
+static void sym_printl_hex(u_char *p, int n)
+{
+ while (n-- > 0)
+ printf (" %x", *p++);
+ printf (".\n");
+}
+
+/*
+ * Print out the content of a SCSI message.
+ */
+static int sym_show_msg (u_char * msg)
+{
+ u_char i;
+ printf ("%x",*msg);
+ if (*msg==M_EXTENDED) {
+ for (i=1;i<8;i++) {
+ if (i-1>msg[1]) break;
+ printf ("-%x",msg[i]);
+ }
+ return (i+1);
+ } else if ((*msg & 0xf0) == 0x20) {
+ printf ("-%x",msg[1]);
+ return (2);
+ }
+ return (1);
+}
+
+static void sym_print_msg(struct sym_ccb *cp, char *label, u_char *msg)
+{
+ sym_print_addr(cp->cmd, "%s: ", label);
+
+ sym_show_msg(msg);
+ printf(".\n");
+}
+
+static void sym_print_nego_msg(struct sym_hcb *np, int target, char *label, u_char *msg)
+{
+ struct sym_tcb *tp = &np->target[target];
+ dev_info(&tp->sdev->sdev_target->dev, "%s: ", label);
+
+ sym_show_msg(msg);
+ printf(".\n");
+}
+
+/*
+ * Print something that tells about extended errors.
+ */
+void sym_print_xerr(struct scsi_cmnd *cmd, int x_status)
+{
+ if (x_status & XE_PARITY_ERR) {
+ sym_print_addr(cmd, "unrecovered SCSI parity error.\n");
+ }
+ if (x_status & XE_EXTRA_DATA) {
+ sym_print_addr(cmd, "extraneous data discarded.\n");
+ }
+ if (x_status & XE_BAD_PHASE) {
+ sym_print_addr(cmd, "illegal scsi phase (4/5).\n");
+ }
+ if (x_status & XE_SODL_UNRUN) {
+ sym_print_addr(cmd, "ODD transfer in DATA OUT phase.\n");
+ }
+ if (x_status & XE_SWIDE_OVRUN) {
+ sym_print_addr(cmd, "ODD transfer in DATA IN phase.\n");
+ }
+}
+
+/*
+ * Return a string for SCSI BUS mode.
+ */
+static char *sym_scsi_bus_mode(int mode)
+{
+ switch(mode) {
+ case SMODE_HVD: return "HVD";
+ case SMODE_SE: return "SE";
+ case SMODE_LVD: return "LVD";
+ }
+ return "??";
+}
+
+/*
+ * Soft reset the chip.
+ *
+ * Raising SRST when the chip is running may cause
+ * problems on dual function chips (see below).
+ * On the other hand, LVD devices need some delay
+ * to settle and report actual BUS mode in STEST4.
+ */
+static void sym_chip_reset (struct sym_hcb *np)
+{
+ OUTB(np, nc_istat, SRST);
+ udelay(10);
+ OUTB(np, nc_istat, 0);
+ udelay(2000); /* For BUS MODE to settle */
+}
+
+/*
+ * Really soft reset the chip.:)
+ *
+ * Some 896 and 876 chip revisions may hang-up if we set
+ * the SRST (soft reset) bit at the wrong time when SCRIPTS
+ * are running.
+ * So, we need to abort the current operation prior to
+ * soft resetting the chip.
+ */
+static void sym_soft_reset (struct sym_hcb *np)
+{
+ u_char istat = 0;
+ int i;
+
+ if (!(np->features & FE_ISTAT1) || !(INB(np, nc_istat1) & SCRUN))
+ goto do_chip_reset;
+
+ OUTB(np, nc_istat, CABRT);
+ for (i = 100000 ; i ; --i) {
+ istat = INB(np, nc_istat);
+ if (istat & SIP) {
+ INW(np, nc_sist);
+ }
+ else if (istat & DIP) {
+ if (INB(np, nc_dstat) & ABRT)
+ break;
+ }
+ udelay(5);
+ }
+ OUTB(np, nc_istat, 0);
+ if (!i)
+ printf("%s: unable to abort current chip operation, "
+ "ISTAT=0x%02x.\n", sym_name(np), istat);
+do_chip_reset:
+ sym_chip_reset(np);
+}
+
+/*
+ * Start reset process.
+ *
+ * The interrupt handler will reinitialize the chip.
+ */
+static void sym_start_reset(struct sym_hcb *np)
+{
+ sym_reset_scsi_bus(np, 1);
+}
+
+int sym_reset_scsi_bus(struct sym_hcb *np, int enab_int)
+{
+ u32 term;
+ int retv = 0;
+
+ sym_soft_reset(np); /* Soft reset the chip */
+ if (enab_int)
+ OUTW(np, nc_sien, RST);
+ /*
+ * Enable Tolerant, reset IRQD if present and
+ * properly set IRQ mode, prior to resetting the bus.
+ */
+ OUTB(np, nc_stest3, TE);
+ OUTB(np, nc_dcntl, (np->rv_dcntl & IRQM));
+ OUTB(np, nc_scntl1, CRST);
+ udelay(200);
+
+ if (!SYM_SETUP_SCSI_BUS_CHECK)
+ goto out;
+ /*
+ * Check for no terminators or SCSI bus shorts to ground.
+ * Read SCSI data bus, data parity bits and control signals.
+ * We are expecting RESET to be TRUE and other signals to be
+ * FALSE.
+ */
+ term = INB(np, nc_sstat0);
+ term = ((term & 2) << 7) + ((term & 1) << 17); /* rst sdp0 */
+ term |= ((INB(np, nc_sstat2) & 0x01) << 26) | /* sdp1 */
+ ((INW(np, nc_sbdl) & 0xff) << 9) | /* d7-0 */
+ ((INW(np, nc_sbdl) & 0xff00) << 10) | /* d15-8 */
+ INB(np, nc_sbcl); /* req ack bsy sel atn msg cd io */
+
+ if (!np->maxwide)
+ term &= 0x3ffff;
+
+ if (term != (2<<7)) {
+ printf("%s: suspicious SCSI data while resetting the BUS.\n",
+ sym_name(np));
+ printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+ "0x%lx, expecting 0x%lx\n",
+ sym_name(np),
+ (np->features & FE_WIDE) ? "dp1,d15-8," : "",
+ (u_long)term, (u_long)(2<<7));
+ if (SYM_SETUP_SCSI_BUS_CHECK == 1)
+ retv = 1;
+ }
+out:
+ OUTB(np, nc_scntl1, 0);
+ return retv;
+}
+
+/*
+ * Select SCSI clock frequency
+ */
+static void sym_selectclock(struct sym_hcb *np, u_char scntl3)
+{
+ /*
+ * If multiplier not present or not selected, leave here.
+ */
+ if (np->multiplier <= 1) {
+ OUTB(np, nc_scntl3, scntl3);
+ return;
+ }
+
+ if (sym_verbose >= 2)
+ printf ("%s: enabling clock multiplier\n", sym_name(np));
+
+ OUTB(np, nc_stest1, DBLEN); /* Enable clock multiplier */
+ /*
+ * Wait for the LCKFRQ bit to be set if supported by the chip.
+ * Otherwise wait 50 micro-seconds (at least).
+ */
+ if (np->features & FE_LCKFRQ) {
+ int i = 20;
+ while (!(INB(np, nc_stest4) & LCKFRQ) && --i > 0)
+ udelay(20);
+ if (!i)
+ printf("%s: the chip cannot lock the frequency\n",
+ sym_name(np));
+ } else
+ udelay((50+10));
+ OUTB(np, nc_stest3, HSC); /* Halt the scsi clock */
+ OUTB(np, nc_scntl3, scntl3);
+ OUTB(np, nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */
+ OUTB(np, nc_stest3, 0x00); /* Restart scsi clock */
+}
+
+
+/*
+ * Determine the chip's clock frequency.
+ *
+ * This is essential for the negotiation of the synchronous
+ * transfer rate.
+ *
+ * Note: we have to return the correct value.
+ * THERE IS NO SAFE DEFAULT VALUE.
+ *
+ * Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+ * 53C860 and 53C875 rev. 1 support fast20 transfers but
+ * do not have a clock doubler and so are provided with a
+ * 80 MHz clock. All other fast20 boards incorporate a doubler
+ * and so should be delivered with a 40 MHz clock.
+ * The recent fast40 chips (895/896/895A/1010) use a 40 Mhz base
+ * clock and provide a clock quadrupler (160 Mhz).
+ */
+
+/*
+ * calculate SCSI clock frequency (in KHz)
+ */
+static unsigned getfreq (struct sym_hcb *np, int gen)
+{
+ unsigned int ms = 0;
+ unsigned int f;
+
+ /*
+ * Measure GEN timer delay in order
+ * to calculate SCSI clock frequency
+ *
+ * This code will never execute too
+ * many loop iterations (if DELAY is
+ * reasonably correct). It could get
+ * too low a delay (too high a freq.)
+ * if the CPU is slow executing the
+ * loop for some reason (an NMI, for
+ * example). For this reason we will
+ * if multiple measurements are to be
+ * performed trust the higher delay
+ * (lower frequency returned).
+ */
+ OUTW(np, nc_sien, 0); /* mask all scsi interrupts */
+ INW(np, nc_sist); /* clear pending scsi interrupt */
+ OUTB(np, nc_dien, 0); /* mask all dma interrupts */
+ INW(np, nc_sist); /* another one, just to be sure :) */
+ /*
+ * The C1010-33 core does not report GEN in SIST,
+ * if this interrupt is masked in SIEN.
+ * I don't know yet if the C1010-66 behaves the same way.
+ */
+ if (np->features & FE_C10) {
+ OUTW(np, nc_sien, GEN);
+ OUTB(np, nc_istat1, SIRQD);
+ }
+ OUTB(np, nc_scntl3, 4); /* set pre-scaler to divide by 3 */
+ OUTB(np, nc_stime1, 0); /* disable general purpose timer */
+ OUTB(np, nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */
+ while (!(INW(np, nc_sist) & GEN) && ms++ < 100000)
+ udelay(1000/4); /* count in 1/4 of ms */
+ OUTB(np, nc_stime1, 0); /* disable general purpose timer */
+ /*
+ * Undo C1010-33 specific settings.
+ */
+ if (np->features & FE_C10) {
+ OUTW(np, nc_sien, 0);
+ OUTB(np, nc_istat1, 0);
+ }
+ /*
+ * set prescaler to divide by whatever 0 means
+ * 0 ought to choose divide by 2, but appears
+ * to set divide by 3.5 mode in my 53c810 ...
+ */
+ OUTB(np, nc_scntl3, 0);
+
+ /*
+ * adjust for prescaler, and convert into KHz
+ */
+ f = ms ? ((1 << gen) * (4340*4)) / ms : 0;
+
+ /*
+ * The C1010-33 result is biased by a factor
+ * of 2/3 compared to earlier chips.
+ */
+ if (np->features & FE_C10)
+ f = (f * 2) / 3;
+
+ if (sym_verbose >= 2)
+ printf ("%s: Delay (GEN=%d): %u msec, %u KHz\n",
+ sym_name(np), gen, ms/4, f);
+
+ return f;
+}
+
+static unsigned sym_getfreq (struct sym_hcb *np)
+{
+ u_int f1, f2;
+ int gen = 8;
+
+ getfreq (np, gen); /* throw away first result */
+ f1 = getfreq (np, gen);
+ f2 = getfreq (np, gen);
+ if (f1 > f2) f1 = f2; /* trust lower result */
+ return f1;
+}
+
+/*
+ * Get/probe chip SCSI clock frequency
+ */
+static void sym_getclock (struct sym_hcb *np, int mult)
+{
+ unsigned char scntl3 = np->sv_scntl3;
+ unsigned char stest1 = np->sv_stest1;
+ unsigned f1;
+
+ np->multiplier = 1;
+ f1 = 40000;
+ /*
+ * True with 875/895/896/895A with clock multiplier selected
+ */
+ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+ if (sym_verbose >= 2)
+ printf ("%s: clock multiplier found\n", sym_name(np));
+ np->multiplier = mult;
+ }
+
+ /*
+ * If multiplier not found or scntl3 not 7,5,3,
+ * reset chip and get frequency from general purpose timer.
+ * Otherwise trust scntl3 BIOS setting.
+ */
+ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+ OUTB(np, nc_stest1, 0); /* make sure doubler is OFF */
+ f1 = sym_getfreq (np);
+
+ if (sym_verbose)
+ printf ("%s: chip clock is %uKHz\n", sym_name(np), f1);
+
+ if (f1 < 45000) f1 = 40000;
+ else if (f1 < 55000) f1 = 50000;
+ else f1 = 80000;
+
+ if (f1 < 80000 && mult > 1) {
+ if (sym_verbose >= 2)
+ printf ("%s: clock multiplier assumed\n",
+ sym_name(np));
+ np->multiplier = mult;
+ }
+ } else {
+ if ((scntl3 & 7) == 3) f1 = 40000;
+ else if ((scntl3 & 7) == 5) f1 = 80000;
+ else f1 = 160000;
+
+ f1 /= np->multiplier;
+ }
+
+ /*
+ * Compute controller synchronous parameters.
+ */
+ f1 *= np->multiplier;
+ np->clock_khz = f1;
+}
+
+/*
+ * Get/probe PCI clock frequency
+ */
+static int sym_getpciclock (struct sym_hcb *np)
+{
+ int f = 0;
+
+ /*
+ * For now, we only need to know about the actual
+ * PCI BUS clock frequency for C1010-66 chips.
+ */
+#if 1
+ if (np->features & FE_66MHZ) {
+#else
+ if (1) {
+#endif
+ OUTB(np, nc_stest1, SCLK); /* Use the PCI clock as SCSI clock */
+ f = sym_getfreq(np);
+ OUTB(np, nc_stest1, 0);
+ }
+ np->pciclk_khz = f;
+
+ return f;
+}
+
+/*
+ * SYMBIOS chip clock divisor table.
+ *
+ * Divisors are multiplied by 10,000,000 in order to make
+ * calculations more simple.
+ */
+#define _5M 5000000
+static u32 div_10M[] = {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+/*
+ * Get clock factor and sync divisor for a given
+ * synchronous factor period.
+ */
+static int
+sym_getsync(struct sym_hcb *np, u_char dt, u_char sfac, u_char *divp, u_char *fakp)
+{
+ u32 clk = np->clock_khz; /* SCSI clock frequency in kHz */
+ int div = np->clock_divn; /* Number of divisors supported */
+ u32 fak; /* Sync factor in sxfer */
+ u32 per; /* Period in tenths of ns */
+ u32 kpc; /* (per * clk) */
+ int ret;
+
+ /*
+ * Compute the synchronous period in tenths of nano-seconds
+ */
+ if (dt && sfac <= 9) per = 125;
+ else if (sfac <= 10) per = 250;
+ else if (sfac == 11) per = 303;
+ else if (sfac == 12) per = 500;
+ else per = 40 * sfac;
+ ret = per;
+
+ kpc = per * clk;
+ if (dt)
+ kpc <<= 1;
+
+ /*
+ * For earliest C10 revision 0, we cannot use extra
+ * clocks for the setting of the SCSI clocking.
+ * Note that this limits the lowest sync data transfer
+ * to 5 Mega-transfers per second and may result in
+ * using higher clock divisors.
+ */
+#if 1
+ if ((np->features & (FE_C10|FE_U3EN)) == FE_C10) {
+ /*
+ * Look for the lowest clock divisor that allows an
+ * output speed not faster than the period.
+ */
+ while (div > 0) {
+ --div;
+ if (kpc > (div_10M[div] << 2)) {
+ ++div;
+ break;
+ }
+ }
+ fak = 0; /* No extra clocks */
+ if (div == np->clock_divn) { /* Are we too fast ? */
+ ret = -1;
+ }
+ *divp = div;
+ *fakp = fak;
+ return ret;
+ }
+#endif
+
+ /*
+ * Look for the greatest clock divisor that allows an
+ * input speed faster than the period.
+ */
+ while (div-- > 0)
+ if (kpc >= (div_10M[div] << 2)) break;
+
+ /*
+ * Calculate the lowest clock factor that allows an output
+ * speed not faster than the period, and the max output speed.
+ * If fak >= 1 we will set both XCLKH_ST and XCLKH_DT.
+ * If fak >= 2 we will also set XCLKS_ST and XCLKS_DT.
+ */
+ if (dt) {
+ fak = (kpc - 1) / (div_10M[div] << 1) + 1 - 2;
+ /* ret = ((2+fak)*div_10M[div])/np->clock_khz; */
+ } else {
+ fak = (kpc - 1) / div_10M[div] + 1 - 4;
+ /* ret = ((4+fak)*div_10M[div])/np->clock_khz; */
+ }
+
+ /*
+ * Check against our hardware limits, or bugs :).
+ */
+ if (fak > 2) {
+ fak = 2;
+ ret = -1;
+ }
+
+ /*
+ * Compute and return sync parameters.
+ */
+ *divp = div;
+ *fakp = fak;
+
+ return ret;
+}
+
+/*
+ * SYMBIOS chips allow burst lengths of 2, 4, 8, 16, 32, 64,
+ * 128 transfers. All chips support at least 16 transfers
+ * bursts. The 825A, 875 and 895 chips support bursts of up
+ * to 128 transfers and the 895A and 896 support bursts of up
+ * to 64 transfers. All other chips support up to 16
+ * transfers bursts.
+ *
+ * For PCI 32 bit data transfers each transfer is a DWORD.
+ * It is a QUADWORD (8 bytes) for PCI 64 bit data transfers.
+ *
+ * We use log base 2 (burst length) as internal code, with
+ * value 0 meaning "burst disabled".
+ */
+
+/*
+ * Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ * Burst code from io register bits.
+ */
+#define burst_code(dmode, ctest4, ctest5) \
+ (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1
+
+/*
+ * Set initial io register bits from burst code.
+ */
+static __inline void sym_init_burst(struct sym_hcb *np, u_char bc)
+{
+ np->rv_ctest4 &= ~0x80;
+ np->rv_dmode &= ~(0x3 << 6);
+ np->rv_ctest5 &= ~0x4;
+
+ if (!bc) {
+ np->rv_ctest4 |= 0x80;
+ }
+ else {
+ --bc;
+ np->rv_dmode |= ((bc & 0x3) << 6);
+ np->rv_ctest5 |= (bc & 0x4);
+ }
+}
+
+
+/*
+ * Print out the list of targets that have some flag disabled by user.
+ */
+static void sym_print_targets_flag(struct sym_hcb *np, int mask, char *msg)
+{
+ int cnt;
+ int i;
+
+ for (cnt = 0, i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+ if (i == np->myaddr)
+ continue;
+ if (np->target[i].usrflags & mask) {
+ if (!cnt++)
+ printf("%s: %s disabled for targets",
+ sym_name(np), msg);
+ printf(" %d", i);
+ }
+ }
+ if (cnt)
+ printf(".\n");
+}
+
+/*
+ * Save initial settings of some IO registers.
+ * Assumed to have been set by BIOS.
+ * We cannot reset the chip prior to reading the
+ * IO registers, since informations will be lost.
+ * Since the SCRIPTS processor may be running, this
+ * is not safe on paper, but it seems to work quite
+ * well. :)
+ */
+static void sym_save_initial_setting (struct sym_hcb *np)
+{
+ np->sv_scntl0 = INB(np, nc_scntl0) & 0x0a;
+ np->sv_scntl3 = INB(np, nc_scntl3) & 0x07;
+ np->sv_dmode = INB(np, nc_dmode) & 0xce;
+ np->sv_dcntl = INB(np, nc_dcntl) & 0xa8;
+ np->sv_ctest3 = INB(np, nc_ctest3) & 0x01;
+ np->sv_ctest4 = INB(np, nc_ctest4) & 0x80;
+ np->sv_gpcntl = INB(np, nc_gpcntl);
+ np->sv_stest1 = INB(np, nc_stest1);
+ np->sv_stest2 = INB(np, nc_stest2) & 0x20;
+ np->sv_stest4 = INB(np, nc_stest4);
+ if (np->features & FE_C10) { /* Always large DMA fifo + ultra3 */
+ np->sv_scntl4 = INB(np, nc_scntl4);
+ np->sv_ctest5 = INB(np, nc_ctest5) & 0x04;
+ }
+ else
+ np->sv_ctest5 = INB(np, nc_ctest5) & 0x24;
+}
+
+/*
+ * Prepare io register values used by sym_start_up()
+ * according to selected and supported features.
+ */
+static int sym_prepare_setting(struct Scsi_Host *shost, struct sym_hcb *np, struct sym_nvram *nvram)
+{
+ u_char burst_max;
+ u32 period;
+ int i;
+
+ /*
+ * Wide ?
+ */
+ np->maxwide = (np->features & FE_WIDE)? 1 : 0;
+
+ /*
+ * Guess the frequency of the chip's clock.
+ */
+ if (np->features & (FE_ULTRA3 | FE_ULTRA2))
+ np->clock_khz = 160000;
+ else if (np->features & FE_ULTRA)
+ np->clock_khz = 80000;
+ else
+ np->clock_khz = 40000;
+
+ /*
+ * Get the clock multiplier factor.
+ */
+ if (np->features & FE_QUAD)
+ np->multiplier = 4;
+ else if (np->features & FE_DBLR)
+ np->multiplier = 2;
+ else
+ np->multiplier = 1;
+
+ /*
+ * Measure SCSI clock frequency for chips
+ * it may vary from assumed one.
+ */
+ if (np->features & FE_VARCLK)
+ sym_getclock(np, np->multiplier);
+
+ /*
+ * Divisor to be used for async (timer pre-scaler).
+ */
+ i = np->clock_divn - 1;
+ while (--i >= 0) {
+ if (10ul * SYM_CONF_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+ ++i;
+ break;
+ }
+ }
+ np->rv_scntl3 = i+1;
+
+ /*
+ * The C1010 uses hardwired divisors for async.
+ * So, we just throw away, the async. divisor.:-)
+ */
+ if (np->features & FE_C10)
+ np->rv_scntl3 = 0;
+
+ /*
+ * Minimum synchronous period factor supported by the chip.
+ * Btw, 'period' is in tenths of nanoseconds.
+ */
+ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+
+ if (period <= 250) np->minsync = 10;
+ else if (period <= 303) np->minsync = 11;
+ else if (period <= 500) np->minsync = 12;
+ else np->minsync = (period + 40 - 1) / 40;
+
+ /*
+ * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+ */
+ if (np->minsync < 25 &&
+ !(np->features & (FE_ULTRA|FE_ULTRA2|FE_ULTRA3)))
+ np->minsync = 25;
+ else if (np->minsync < 12 &&
+ !(np->features & (FE_ULTRA2|FE_ULTRA3)))
+ np->minsync = 12;
+
+ /*
+ * Maximum synchronous period factor supported by the chip.
+ */
+ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+ np->maxsync = period > 2540 ? 254 : period / 10;
+
+ /*
+ * If chip is a C1010, guess the sync limits in DT mode.
+ */
+ if ((np->features & (FE_C10|FE_ULTRA3)) == (FE_C10|FE_ULTRA3)) {
+ if (np->clock_khz == 160000) {
+ np->minsync_dt = 9;
+ np->maxsync_dt = 50;
+ np->maxoffs_dt = nvram->type ? 62 : 31;
+ }
+ }
+
+ /*
+ * 64 bit addressing (895A/896/1010) ?
+ */
+ if (np->features & FE_DAC) {
+#if SYM_CONF_DMA_ADDRESSING_MODE == 0
+ np->rv_ccntl1 |= (DDAC);
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 1
+ if (!np->use_dac)
+ np->rv_ccntl1 |= (DDAC);
+ else
+ np->rv_ccntl1 |= (XTIMOD | EXTIBMV);
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
+ if (!np->use_dac)
+ np->rv_ccntl1 |= (DDAC);
+ else
+ np->rv_ccntl1 |= (0 | EXTIBMV);
+#endif
+ }
+
+ /*
+ * Phase mismatch handled by SCRIPTS (895A/896/1010) ?
+ */
+ if (np->features & FE_NOPM)
+ np->rv_ccntl0 |= (ENPMJ);
+
+ /*
+ * C1010-33 Errata: Part Number:609-039638 (rev. 1) is fixed.
+ * In dual channel mode, contention occurs if internal cycles
+ * are used. Disable internal cycles.
+ */
+ if (np->device_id == PCI_DEVICE_ID_LSI_53C1010_33 &&
+ np->revision_id < 0x1)
+ np->rv_ccntl0 |= DILS;
+
+ /*
+ * Select burst length (dwords)
+ */
+ burst_max = SYM_SETUP_BURST_ORDER;
+ if (burst_max == 255)
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest4,
+ np->sv_ctest5);
+ if (burst_max > 7)
+ burst_max = 7;
+ if (burst_max > np->maxburst)
+ burst_max = np->maxburst;
+
+ /*
+ * DEL 352 - 53C810 Rev x11 - Part Number 609-0392140 - ITEM 2.
+ * This chip and the 860 Rev 1 may wrongly use PCI cache line
+ * based transactions on LOAD/STORE instructions. So we have
+ * to prevent these chips from using such PCI transactions in
+ * this driver. The generic ncr driver that does not use
+ * LOAD/STORE instructions does not need this work-around.
+ */
+ if ((np->device_id == PCI_DEVICE_ID_NCR_53C810 &&
+ np->revision_id >= 0x10 && np->revision_id <= 0x11) ||
+ (np->device_id == PCI_DEVICE_ID_NCR_53C860 &&
+ np->revision_id <= 0x1))
+ np->features &= ~(FE_WRIE|FE_ERL|FE_ERMP);
+
+ /*
+ * Select all supported special features.
+ * If we are using on-board RAM for scripts, prefetch (PFEN)
+ * does not help, but burst op fetch (BOF) does.
+ * Disabling PFEN makes sure BOF will be used.
+ */
+ if (np->features & FE_ERL)
+ np->rv_dmode |= ERL; /* Enable Read Line */
+ if (np->features & FE_BOF)
+ np->rv_dmode |= BOF; /* Burst Opcode Fetch */
+ if (np->features & FE_ERMP)
+ np->rv_dmode |= ERMP; /* Enable Read Multiple */
+#if 1
+ if ((np->features & FE_PFEN) && !np->ram_ba)
+#else
+ if (np->features & FE_PFEN)
+#endif
+ np->rv_dcntl |= PFEN; /* Prefetch Enable */
+ if (np->features & FE_CLSE)
+ np->rv_dcntl |= CLSE; /* Cache Line Size Enable */
+ if (np->features & FE_WRIE)
+ np->rv_ctest3 |= WRIE; /* Write and Invalidate */
+ if (np->features & FE_DFS)
+ np->rv_ctest5 |= DFS; /* Dma Fifo Size */
+
+ /*
+ * Select some other
+ */
+ np->rv_ctest4 |= MPEE; /* Master parity checking */
+ np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */
+
+ /*
+ * Get parity checking, host ID and verbose mode from NVRAM
+ */
+ np->myaddr = 255;
+ sym_nvram_setup_host(shost, np, nvram);
+
+ /*
+ * Get SCSI addr of host adapter (set by bios?).
+ */
+ if (np->myaddr == 255) {
+ np->myaddr = INB(np, nc_scid) & 0x07;
+ if (!np->myaddr)
+ np->myaddr = SYM_SETUP_HOST_ID;
+ }
+
+ /*
+ * Prepare initial io register bits for burst length
+ */
+ sym_init_burst(np, burst_max);
+
+ /*
+ * Set SCSI BUS mode.
+ * - LVD capable chips (895/895A/896/1010) report the
+ * current BUS mode through the STEST4 IO register.
+ * - For previous generation chips (825/825A/875),
+ * user has to tell us how to check against HVD,
+ * since a 100% safe algorithm is not possible.
+ */
+ np->scsi_mode = SMODE_SE;
+ if (np->features & (FE_ULTRA2|FE_ULTRA3))
+ np->scsi_mode = (np->sv_stest4 & SMODE);
+ else if (np->features & FE_DIFF) {
+ if (SYM_SETUP_SCSI_DIFF == 1) {
+ if (np->sv_scntl3) {
+ if (np->sv_stest2 & 0x20)
+ np->scsi_mode = SMODE_HVD;
+ }
+ else if (nvram->type == SYM_SYMBIOS_NVRAM) {
+ if (!(INB(np, nc_gpreg) & 0x08))
+ np->scsi_mode = SMODE_HVD;
+ }
+ }
+ else if (SYM_SETUP_SCSI_DIFF == 2)
+ np->scsi_mode = SMODE_HVD;
+ }
+ if (np->scsi_mode == SMODE_HVD)
+ np->rv_stest2 |= 0x20;
+
+ /*
+ * Set LED support from SCRIPTS.
+ * Ignore this feature for boards known to use a
+ * specific GPIO wiring and for the 895A, 896
+ * and 1010 that drive the LED directly.
+ */
+ if ((SYM_SETUP_SCSI_LED ||
+ (nvram->type == SYM_SYMBIOS_NVRAM ||
+ (nvram->type == SYM_TEKRAM_NVRAM &&
+ np->device_id == PCI_DEVICE_ID_NCR_53C895))) &&
+ !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01))
+ np->features |= FE_LED0;
+
+ /*
+ * Set irq mode.
+ */
+ switch(SYM_SETUP_IRQ_MODE & 3) {
+ case 2:
+ np->rv_dcntl |= IRQM;
+ break;
+ case 1:
+ np->rv_dcntl |= (np->sv_dcntl & IRQM);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Configure targets according to driver setup.
+ * If NVRAM present get targets setup from NVRAM.
+ */
+ for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+ struct sym_tcb *tp = &np->target[i];
+
+ tp->usrflags |= (SYM_DISC_ENABLED | SYM_TAGS_ENABLED);
+ tp->usrtags = SYM_SETUP_MAX_TAG;
+
+ sym_nvram_setup_target(np, i, nvram);
+
+ if (!tp->usrtags)
+ tp->usrflags &= ~SYM_TAGS_ENABLED;
+ }
+
+ /*
+ * Let user know about the settings.
+ */
+ printf("%s: %s, ID %d, Fast-%d, %s, %s\n", sym_name(np),
+ sym_nvram_type(nvram), np->myaddr,
+ (np->features & FE_ULTRA3) ? 80 :
+ (np->features & FE_ULTRA2) ? 40 :
+ (np->features & FE_ULTRA) ? 20 : 10,
+ sym_scsi_bus_mode(np->scsi_mode),
+ (np->rv_scntl0 & 0xa) ? "parity checking" : "NO parity");
+ /*
+ * Tell him more on demand.
+ */
+ if (sym_verbose) {
+ printf("%s: %s IRQ line driver%s\n",
+ sym_name(np),
+ np->rv_dcntl & IRQM ? "totem pole" : "open drain",
+ np->ram_ba ? ", using on-chip SRAM" : "");
+ printf("%s: using %s firmware.\n", sym_name(np), np->fw_name);
+ if (np->features & FE_NOPM)
+ printf("%s: handling phase mismatch from SCRIPTS.\n",
+ sym_name(np));
+ }
+ /*
+ * And still more.
+ */
+ if (sym_verbose >= 2) {
+ printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ sym_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+ np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+ printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ sym_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+ np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+ }
+ /*
+ * Let user be aware of targets that have some disable flags set.
+ */
+ sym_print_targets_flag(np, SYM_SCAN_BOOT_DISABLED, "SCAN AT BOOT");
+ if (sym_verbose)
+ sym_print_targets_flag(np, SYM_SCAN_LUNS_DISABLED,
+ "SCAN FOR LUNS");
+
+ return 0;
+}
+
+/*
+ * Test the pci bus snoop logic :-(
+ *
+ * Has to be called with interrupts disabled.
+ */
+#ifndef CONFIG_SCSI_SYM53C8XX_IOMAPPED
+static int sym_regtest (struct sym_hcb *np)
+{
+ register volatile u32 data;
+ /*
+ * chip registers may NOT be cached.
+ * write 0xffffffff to a read only register area,
+ * and try to read it back.
+ */
+ data = 0xffffffff;
+ OUTL(np, nc_dstat, data);
+ data = INL(np, nc_dstat);
+#if 1
+ if (data == 0xffffffff) {
+#else
+ if ((data & 0xe2f0fffd) != 0x02000080) {
+#endif
+ printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n",
+ (unsigned) data);
+ return (0x10);
+ }
+ return (0);
+}
+#endif
+
+static int sym_snooptest (struct sym_hcb *np)
+{
+ u32 sym_rd, sym_wr, sym_bk, host_rd, host_wr, pc, dstat;
+ int i, err=0;
+#ifndef CONFIG_SCSI_SYM53C8XX_IOMAPPED
+ err |= sym_regtest (np);
+ if (err) return (err);
+#endif
+restart_test:
+ /*
+ * Enable Master Parity Checking as we intend
+ * to enable it for normal operations.
+ */
+ OUTB(np, nc_ctest4, (np->rv_ctest4 & MPEE));
+ /*
+ * init
+ */
+ pc = SCRIPTZ_BA(np, snooptest);
+ host_wr = 1;
+ sym_wr = 2;
+ /*
+ * Set memory and register.
+ */
+ np->scratch = cpu_to_scr(host_wr);
+ OUTL(np, nc_temp, sym_wr);
+ /*
+ * Start script (exchange values)
+ */
+ OUTL(np, nc_dsa, np->hcb_ba);
+ OUTL_DSP(np, pc);
+ /*
+ * Wait 'til done (with timeout)
+ */
+ for (i=0; i<SYM_SNOOP_TIMEOUT; i++)
+ if (INB(np, nc_istat) & (INTF|SIP|DIP))
+ break;
+ if (i>=SYM_SNOOP_TIMEOUT) {
+ printf ("CACHE TEST FAILED: timeout.\n");
+ return (0x20);
+ }
+ /*
+ * Check for fatal DMA errors.
+ */
+ dstat = INB(np, nc_dstat);
+#if 1 /* Band aiding for broken hardwares that fail PCI parity */
+ if ((dstat & MDPE) && (np->rv_ctest4 & MPEE)) {
+ printf ("%s: PCI DATA PARITY ERROR DETECTED - "
+ "DISABLING MASTER DATA PARITY CHECKING.\n",
+ sym_name(np));
+ np->rv_ctest4 &= ~MPEE;
+ goto restart_test;
+ }
+#endif
+ if (dstat & (MDPE|BF|IID)) {
+ printf ("CACHE TEST FAILED: DMA error (dstat=0x%02x).", dstat);
+ return (0x80);
+ }
+ /*
+ * Save termination position.
+ */
+ pc = INL(np, nc_dsp);
+ /*
+ * Read memory and register.
+ */
+ host_rd = scr_to_cpu(np->scratch);
+ sym_rd = INL(np, nc_scratcha);
+ sym_bk = INL(np, nc_temp);
+ /*
+ * Check termination position.
+ */
+ if (pc != SCRIPTZ_BA(np, snoopend)+8) {
+ printf ("CACHE TEST FAILED: script execution failed.\n");
+ printf ("start=%08lx, pc=%08lx, end=%08lx\n",
+ (u_long) SCRIPTZ_BA(np, snooptest), (u_long) pc,
+ (u_long) SCRIPTZ_BA(np, snoopend) +8);
+ return (0x40);
+ }
+ /*
+ * Show results.
+ */
+ if (host_wr != sym_rd) {
+ printf ("CACHE TEST FAILED: host wrote %d, chip read %d.\n",
+ (int) host_wr, (int) sym_rd);
+ err |= 1;
+ }
+ if (host_rd != sym_wr) {
+ printf ("CACHE TEST FAILED: chip wrote %d, host read %d.\n",
+ (int) sym_wr, (int) host_rd);
+ err |= 2;
+ }
+ if (sym_bk != sym_wr) {
+ printf ("CACHE TEST FAILED: chip wrote %d, read back %d.\n",
+ (int) sym_wr, (int) sym_bk);
+ err |= 4;
+ }
+
+ return (err);
+}
+
+/*
+ * log message for real hard errors
+ *
+ * sym0 targ 0?: ERROR (ds:si) (so-si-sd) (sx/s3/s4) @ name (dsp:dbc).
+ * reg: r0 r1 r2 r3 r4 r5 r6 ..... rf.
+ *
+ * exception register:
+ * ds: dstat
+ * si: sist
+ *
+ * SCSI bus lines:
+ * so: control lines as driven by chip.
+ * si: control lines as seen by chip.
+ * sd: scsi data lines as seen by chip.
+ *
+ * wide/fastmode:
+ * sx: sxfer (see the manual)
+ * s3: scntl3 (see the manual)
+ * s4: scntl4 (see the manual)
+ *
+ * current script command:
+ * dsp: script address (relative to start of script).
+ * dbc: first word of script command.
+ *
+ * First 24 register of the chip:
+ * r0..rf
+ */
+static void sym_log_hard_error(struct sym_hcb *np, u_short sist, u_char dstat)
+{
+ u32 dsp;
+ int script_ofs;
+ int script_size;
+ char *script_name;
+ u_char *script_base;
+ int i;
+
+ dsp = INL(np, nc_dsp);
+
+ if (dsp > np->scripta_ba &&
+ dsp <= np->scripta_ba + np->scripta_sz) {
+ script_ofs = dsp - np->scripta_ba;
+ script_size = np->scripta_sz;
+ script_base = (u_char *) np->scripta0;
+ script_name = "scripta";
+ }
+ else if (np->scriptb_ba < dsp &&
+ dsp <= np->scriptb_ba + np->scriptb_sz) {
+ script_ofs = dsp - np->scriptb_ba;
+ script_size = np->scriptb_sz;
+ script_base = (u_char *) np->scriptb0;
+ script_name = "scriptb";
+ } else {
+ script_ofs = dsp;
+ script_size = 0;
+ script_base = NULL;
+ script_name = "mem";
+ }
+
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x/%x) @ (%s %x:%08x).\n",
+ sym_name(np), (unsigned)INB(np, nc_sdid)&0x0f, dstat, sist,
+ (unsigned)INB(np, nc_socl), (unsigned)INB(np, nc_sbcl),
+ (unsigned)INB(np, nc_sbdl), (unsigned)INB(np, nc_sxfer),
+ (unsigned)INB(np, nc_scntl3),
+ (np->features & FE_C10) ? (unsigned)INB(np, nc_scntl4) : 0,
+ script_name, script_ofs, (unsigned)INL(np, nc_dbc));
+
+ if (((script_ofs & 3) == 0) &&
+ (unsigned)script_ofs < script_size) {
+ printf ("%s: script cmd = %08x\n", sym_name(np),
+ scr_to_cpu((int) *(u32 *)(script_base + script_ofs)));
+ }
+
+ printf ("%s: regdump:", sym_name(np));
+ for (i=0; i<24;i++)
+ printf (" %02x", (unsigned)INB_OFF(np, i));
+ printf (".\n");
+
+ /*
+ * PCI BUS error.
+ */
+ if (dstat & (MDPE|BF))
+ sym_log_bus_error(np);
+}
+
+static struct sym_chip sym_dev_table[] = {
+ {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810", 4, 8, 4, 64,
+ FE_ERL}
+ ,
+#ifdef SYM_DEBUG_GENERIC_SUPPORT
+ {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4, 8, 4, 1,
+ FE_BOF}
+ ,
+#else
+ {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4, 8, 4, 1,
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF}
+ ,
+#endif
+ {PCI_DEVICE_ID_NCR_53C815, 0xff, "815", 4, 8, 4, 64,
+ FE_BOF|FE_ERL}
+ ,
+ {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 6, 8, 4, 64,
+ FE_WIDE|FE_BOF|FE_ERL|FE_DIFF}
+ ,
+ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6, 8, 4, 2,
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|FE_DIFF}
+ ,
+ {PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, 1,
+ FE_ULTRA|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN}
+ ,
+ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+ {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+#ifdef SYM_DEBUG_GENERIC_SUPPORT
+ {PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 6, 31, 7, 2,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|
+ FE_RAM|FE_LCKFRQ}
+ ,
+#else
+ {PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 6, 31, 7, 2,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_LCKFRQ}
+ ,
+#endif
+ {PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ}
+ ,
+ {PCI_DEVICE_ID_LSI_53C895A, 0xff, "895a", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ}
+ ,
+ {PCI_DEVICE_ID_LSI_53C875A, 0xff, "875a", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ}
+ ,
+ {PCI_DEVICE_ID_LSI_53C1010_33, 0x00, "1010-33", 6, 31, 7, 8,
+ FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_CRC|
+ FE_C10}
+ ,
+ {PCI_DEVICE_ID_LSI_53C1010_33, 0xff, "1010-33", 6, 31, 7, 8,
+ FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_CRC|
+ FE_C10|FE_U3EN}
+ ,
+ {PCI_DEVICE_ID_LSI_53C1010_66, 0xff, "1010-66", 6, 31, 7, 8,
+ FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_66MHZ|FE_CRC|
+ FE_C10|FE_U3EN}
+ ,
+ {PCI_DEVICE_ID_LSI_53C1510, 0xff, "1510d", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_IO256|FE_LEDC}
+};
+
+#define sym_num_devs \
+ (sizeof(sym_dev_table) / sizeof(sym_dev_table[0]))
+
+/*
+ * Look up the chip table.
+ *
+ * Return a pointer to the chip entry if found,
+ * zero otherwise.
+ */
+struct sym_chip *
+sym_lookup_chip_table (u_short device_id, u_char revision)
+{
+ struct sym_chip *chip;
+ int i;
+
+ for (i = 0; i < sym_num_devs; i++) {
+ chip = &sym_dev_table[i];
+ if (device_id != chip->device_id)
+ continue;
+ if (revision > chip->revision_id)
+ continue;
+ return chip;
+ }
+
+ return NULL;
+}
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+/*
+ * Lookup the 64 bit DMA segments map.
+ * This is only used if the direct mapping
+ * has been unsuccessful.
+ */
+int sym_lookup_dmap(struct sym_hcb *np, u32 h, int s)
+{
+ int i;
+
+ if (!np->use_dac)
+ goto weird;
+
+ /* Look up existing mappings */
+ for (i = SYM_DMAP_SIZE-1; i > 0; i--) {
+ if (h == np->dmap_bah[i])
+ return i;
+ }
+ /* If direct mapping is free, get it */
+ if (!np->dmap_bah[s])
+ goto new;
+ /* Collision -> lookup free mappings */
+ for (s = SYM_DMAP_SIZE-1; s > 0; s--) {
+ if (!np->dmap_bah[s])
+ goto new;
+ }
+weird:
+ panic("sym: ran out of 64 bit DMA segment registers");
+ return -1;
+new:
+ np->dmap_bah[s] = h;
+ np->dmap_dirty = 1;
+ return s;
+}
+
+/*
+ * Update IO registers scratch C..R so they will be
+ * in sync. with queued CCB expectations.
+ */
+static void sym_update_dmap_regs(struct sym_hcb *np)
+{
+ int o, i;
+
+ if (!np->dmap_dirty)
+ return;
+ o = offsetof(struct sym_reg, nc_scrx[0]);
+ for (i = 0; i < SYM_DMAP_SIZE; i++) {
+ OUTL_OFF(np, o, np->dmap_bah[i]);
+ o += 4;
+ }
+ np->dmap_dirty = 0;
+}
+#endif
+
+/* Enforce all the fiddly SPI rules and the chip limitations */
+static void sym_check_goals(struct sym_hcb *np, struct scsi_target *starget,
+ struct sym_trans *goal)
+{
+ if (!spi_support_wide(starget))
+ goal->width = 0;
+
+ if (!spi_support_sync(starget)) {
+ goal->iu = 0;
+ goal->dt = 0;
+ goal->qas = 0;
+ goal->period = 0;
+ goal->offset = 0;
+ return;
+ }
+
+ if (spi_support_dt(starget)) {
+ if (spi_support_dt_only(starget))
+ goal->dt = 1;
+
+ if (goal->offset == 0)
+ goal->dt = 0;
+ } else {
+ goal->dt = 0;
+ }
+
+ /* Some targets fail to properly negotiate DT in SE mode */
+ if ((np->scsi_mode != SMODE_LVD) || !(np->features & FE_U3EN))
+ goal->dt = 0;
+
+ if (goal->dt) {
+ /* all DT transfers must be wide */
+ goal->width = 1;
+ if (goal->offset > np->maxoffs_dt)
+ goal->offset = np->maxoffs_dt;
+ if (goal->period < np->minsync_dt)
+ goal->period = np->minsync_dt;
+ if (goal->period > np->maxsync_dt)
+ goal->period = np->maxsync_dt;
+ } else {
+ goal->iu = goal->qas = 0;
+ if (goal->offset > np->maxoffs)
+ goal->offset = np->maxoffs;
+ if (goal->period < np->minsync)
+ goal->period = np->minsync;
+ if (goal->period > np->maxsync)
+ goal->period = np->maxsync;
+ }
+}
+
+/*
+ * Prepare the next negotiation message if needed.
+ *
+ * Fill in the part of message buffer that contains the
+ * negotiation and the nego_status field of the CCB.
+ * Returns the size of the message in bytes.
+ */
+static int sym_prepare_nego(struct sym_hcb *np, struct sym_ccb *cp, u_char *msgptr)
+{
+ struct sym_tcb *tp = &np->target[cp->target];
+ struct scsi_target *starget = tp->sdev->sdev_target;
+ struct sym_trans *goal = &tp->tgoal;
+ int msglen = 0;
+ int nego;
+
+ sym_check_goals(np, starget, goal);
+
+ /*
+ * Many devices implement PPR in a buggy way, so only use it if we
+ * really want to.
+ */
+ if (goal->iu || goal->dt || goal->qas || (goal->period < 0xa)) {
+ nego = NS_PPR;
+ } else if (spi_width(starget) != goal->width) {
+ nego = NS_WIDE;
+ } else if (spi_period(starget) != goal->period ||
+ spi_offset(starget) != goal->offset) {
+ nego = NS_SYNC;
+ } else {
+ goal->check_nego = 0;
+ nego = 0;
+ }
+
+ switch (nego) {
+ case NS_SYNC:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 3;
+ msgptr[msglen++] = M_X_SYNC_REQ;
+ msgptr[msglen++] = goal->period;
+ msgptr[msglen++] = goal->offset;
+ break;
+ case NS_WIDE:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 2;
+ msgptr[msglen++] = M_X_WIDE_REQ;
+ msgptr[msglen++] = goal->width;
+ break;
+ case NS_PPR:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 6;
+ msgptr[msglen++] = M_X_PPR_REQ;
+ msgptr[msglen++] = goal->period;
+ msgptr[msglen++] = 0;
+ msgptr[msglen++] = goal->offset;
+ msgptr[msglen++] = goal->width;
+ msgptr[msglen++] = (goal->iu ? PPR_OPT_IU : 0) |
+ (goal->dt ? PPR_OPT_DT : 0) |
+ (goal->qas ? PPR_OPT_QAS : 0);
+ break;
+ }
+
+ cp->nego_status = nego;
+
+ if (nego) {
+ tp->nego_cp = cp; /* Keep track a nego will be performed */
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, cp->target,
+ nego == NS_SYNC ? "sync msgout" :
+ nego == NS_WIDE ? "wide msgout" :
+ "ppr msgout", msgptr);
+ }
+ }
+
+ return msglen;
+}
+
+/*
+ * Insert a job into the start queue.
+ */
+void sym_put_start_queue(struct sym_hcb *np, struct sym_ccb *cp)
+{
+ u_short qidx;
+
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If the previously queued CCB is not yet done,
+ * set the IARB hint. The SCRIPTS will go with IARB
+ * for this job when starting the previous one.
+ * We leave devices a chance to win arbitration by
+ * not using more than 'iarb_max' consecutive
+ * immediate arbitrations.
+ */
+ if (np->last_cp && np->iarb_count < np->iarb_max) {
+ np->last_cp->host_flags |= HF_HINT_IARB;
+ ++np->iarb_count;
+ }
+ else
+ np->iarb_count = 0;
+ np->last_cp = cp;
+#endif
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+ /*
+ * Make SCRIPTS aware of the 64 bit DMA
+ * segment registers not being up-to-date.
+ */
+ if (np->dmap_dirty)
+ cp->host_xflags |= HX_DMAP_DIRTY;
+#endif
+
+ /*
+ * Insert first the idle task and then our job.
+ * The MBs should ensure proper ordering.
+ */
+ qidx = np->squeueput + 2;
+ if (qidx >= MAX_QUEUE*2) qidx = 0;
+
+ np->squeue [qidx] = cpu_to_scr(np->idletask_ba);
+ MEMORY_WRITE_BARRIER();
+ np->squeue [np->squeueput] = cpu_to_scr(cp->ccb_ba);
+
+ np->squeueput = qidx;
+
+ if (DEBUG_FLAGS & DEBUG_QUEUE)
+ printf ("%s: queuepos=%d.\n", sym_name (np), np->squeueput);
+
+ /*
+ * Script processor may be waiting for reselect.
+ * Wake it up.
+ */
+ MEMORY_WRITE_BARRIER();
+ OUTB(np, nc_istat, SIGP|np->istat_sem);
+}
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+/*
+ * Start next ready-to-start CCBs.
+ */
+void sym_start_next_ccbs(struct sym_hcb *np, struct sym_lcb *lp, int maxn)
+{
+ SYM_QUEHEAD *qp;
+ struct sym_ccb *cp;
+
+ /*
+ * Paranoia, as usual. :-)
+ */
+ assert(!lp->started_tags || !lp->started_no_tag);
+
+ /*
+ * Try to start as many commands as asked by caller.
+ * Prevent from having both tagged and untagged
+ * commands queued to the device at the same time.
+ */
+ while (maxn--) {
+ qp = sym_remque_head(&lp->waiting_ccbq);
+ if (!qp)
+ break;
+ cp = sym_que_entry(qp, struct sym_ccb, link2_ccbq);
+ if (cp->tag != NO_TAG) {
+ if (lp->started_no_tag ||
+ lp->started_tags >= lp->started_max) {
+ sym_insque_head(qp, &lp->waiting_ccbq);
+ break;
+ }
+ lp->itlq_tbl[cp->tag] = cpu_to_scr(cp->ccb_ba);
+ lp->head.resel_sa =
+ cpu_to_scr(SCRIPTA_BA(np, resel_tag));
+ ++lp->started_tags;
+ } else {
+ if (lp->started_no_tag || lp->started_tags) {
+ sym_insque_head(qp, &lp->waiting_ccbq);
+ break;
+ }
+ lp->head.itl_task_sa = cpu_to_scr(cp->ccb_ba);
+ lp->head.resel_sa =
+ cpu_to_scr(SCRIPTA_BA(np, resel_no_tag));
+ ++lp->started_no_tag;
+ }
+ cp->started = 1;
+ sym_insque_tail(qp, &lp->started_ccbq);
+ sym_put_start_queue(np, cp);
+ }
+}
+#endif /* SYM_OPT_HANDLE_DEVICE_QUEUEING */
+
+/*
+ * The chip may have completed jobs. Look at the DONE QUEUE.
+ *
+ * On paper, memory read barriers may be needed here to
+ * prevent out of order LOADs by the CPU from having
+ * prefetched stale data prior to DMA having occurred.
+ */
+static int sym_wakeup_done (struct sym_hcb *np)
+{
+ struct sym_ccb *cp;
+ int i, n;
+ u32 dsa;
+
+ n = 0;
+ i = np->dqueueget;
+
+ /* MEMORY_READ_BARRIER(); */
+ while (1) {
+ dsa = scr_to_cpu(np->dqueue[i]);
+ if (!dsa)
+ break;
+ np->dqueue[i] = 0;
+ if ((i = i+2) >= MAX_QUEUE*2)
+ i = 0;
+
+ cp = sym_ccb_from_dsa(np, dsa);
+ if (cp) {
+ MEMORY_READ_BARRIER();
+ sym_complete_ok (np, cp);
+ ++n;
+ }
+ else
+ printf ("%s: bad DSA (%x) in done queue.\n",
+ sym_name(np), (u_int) dsa);
+ }
+ np->dqueueget = i;
+
+ return n;
+}
+
+/*
+ * Complete all CCBs queued to the COMP queue.
+ *
+ * These CCBs are assumed:
+ * - Not to be referenced either by devices or
+ * SCRIPTS-related queues and datas.
+ * - To have to be completed with an error condition
+ * or requeued.
+ *
+ * The device queue freeze count is incremented
+ * for each CCB that does not prevent this.
+ * This function is called when all CCBs involved
+ * in error handling/recovery have been reaped.
+ */
+static void sym_flush_comp_queue(struct sym_hcb *np, int cam_status)
+{
+ SYM_QUEHEAD *qp;
+ struct sym_ccb *cp;
+
+ while ((qp = sym_remque_head(&np->comp_ccbq)) != 0) {
+ struct scsi_cmnd *cmd;
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq);
+ /* Leave quiet CCBs waiting for resources */
+ if (cp->host_status == HS_WAIT)
+ continue;
+ cmd = cp->cmd;
+ if (cam_status)
+ sym_set_cam_status(cmd, cam_status);
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ if (sym_get_cam_status(cmd) == CAM_REQUEUE_REQ) {
+ struct sym_tcb *tp = &np->target[cp->target];
+ struct sym_lcb *lp = sym_lp(tp, cp->lun);
+ if (lp) {
+ sym_remque(&cp->link2_ccbq);
+ sym_insque_tail(&cp->link2_ccbq,
+ &lp->waiting_ccbq);
+ if (cp->started) {
+ if (cp->tag != NO_TAG)
+ --lp->started_tags;
+ else
+ --lp->started_no_tag;
+ }
+ }
+ cp->started = 0;
+ continue;
+ }
+#endif
+ sym_free_ccb(np, cp);
+ sym_xpt_done(np, cmd);
+ }
+}
+
+/*
+ * Complete all active CCBs with error.
+ * Used on CHIP/SCSI RESET.
+ */
+static void sym_flush_busy_queue (struct sym_hcb *np, int cam_status)
+{
+ /*
+ * Move all active CCBs to the COMP queue
+ * and flush this queue.
+ */
+ sym_que_splice(&np->busy_ccbq, &np->comp_ccbq);
+ sym_que_init(&np->busy_ccbq);
+ sym_flush_comp_queue(np, cam_status);
+}
+
+/*
+ * Start chip.
+ *
+ * 'reason' means:
+ * 0: initialisation.
+ * 1: SCSI BUS RESET delivered or received.
+ * 2: SCSI BUS MODE changed.
+ */
+void sym_start_up (struct sym_hcb *np, int reason)
+{
+ int i;
+ u32 phys;
+
+ /*
+ * Reset chip if asked, otherwise just clear fifos.
+ */
+ if (reason == 1)
+ sym_soft_reset(np);
+ else {
+ OUTB(np, nc_stest3, TE|CSF);
+ OUTONB(np, nc_ctest3, CLF);
+ }
+
+ /*
+ * Clear Start Queue
+ */
+ phys = np->squeue_ba;
+ for (i = 0; i < MAX_QUEUE*2; i += 2) {
+ np->squeue[i] = cpu_to_scr(np->idletask_ba);
+ np->squeue[i+1] = cpu_to_scr(phys + (i+2)*4);
+ }
+ np->squeue[MAX_QUEUE*2-1] = cpu_to_scr(phys);
+
+ /*
+ * Start at first entry.
+ */
+ np->squeueput = 0;
+
+ /*
+ * Clear Done Queue
+ */
+ phys = np->dqueue_ba;
+ for (i = 0; i < MAX_QUEUE*2; i += 2) {
+ np->dqueue[i] = 0;
+ np->dqueue[i+1] = cpu_to_scr(phys + (i+2)*4);
+ }
+ np->dqueue[MAX_QUEUE*2-1] = cpu_to_scr(phys);
+
+ /*
+ * Start at first entry.
+ */
+ np->dqueueget = 0;
+
+ /*
+ * Install patches in scripts.
+ * This also let point to first position the start
+ * and done queue pointers used from SCRIPTS.
+ */
+ np->fw_patch(np);
+
+ /*
+ * Wakeup all pending jobs.
+ */
+ sym_flush_busy_queue(np, CAM_SCSI_BUS_RESET);
+
+ /*
+ * Init chip.
+ */
+ OUTB(np, nc_istat, 0x00); /* Remove Reset, abort */
+ udelay(2000); /* The 895 needs time for the bus mode to settle */
+
+ OUTB(np, nc_scntl0, np->rv_scntl0 | 0xc0);
+ /* full arb., ena parity, par->ATN */
+ OUTB(np, nc_scntl1, 0x00); /* odd parity, and remove CRST!! */
+
+ sym_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
+
+ OUTB(np, nc_scid , RRE|np->myaddr); /* Adapter SCSI address */
+ OUTW(np, nc_respid, 1ul<<np->myaddr); /* Id to respond to */
+ OUTB(np, nc_istat , SIGP ); /* Signal Process */
+ OUTB(np, nc_dmode , np->rv_dmode); /* Burst length, dma mode */
+ OUTB(np, nc_ctest5, np->rv_ctest5); /* Large fifo + large burst */
+
+ OUTB(np, nc_dcntl , NOCOM|np->rv_dcntl); /* Protect SFBR */
+ OUTB(np, nc_ctest3, np->rv_ctest3); /* Write and invalidate */
+ OUTB(np, nc_ctest4, np->rv_ctest4); /* Master parity checking */
+
+ /* Extended Sreq/Sack filtering not supported on the C10 */
+ if (np->features & FE_C10)
+ OUTB(np, nc_stest2, np->rv_stest2);
+ else
+ OUTB(np, nc_stest2, EXT|np->rv_stest2);
+
+ OUTB(np, nc_stest3, TE); /* TolerANT enable */
+ OUTB(np, nc_stime0, 0x0c); /* HTH disabled STO 0.25 sec */
+
+ /*
+ * For now, disable AIP generation on C1010-66.
+ */
+ if (np->device_id == PCI_DEVICE_ID_LSI_53C1010_66)
+ OUTB(np, nc_aipcntl1, DISAIP);
+
+ /*
+ * C10101 rev. 0 errata.
+ * Errant SGE's when in narrow. Write bits 4 & 5 of
+ * STEST1 register to disable SGE. We probably should do
+ * that from SCRIPTS for each selection/reselection, but
+ * I just don't want. :)
+ */
+ if (np->device_id == PCI_DEVICE_ID_LSI_53C1010_33 &&
+ np->revision_id < 1)
+ OUTB(np, nc_stest1, INB(np, nc_stest1) | 0x30);
+
+ /*
+ * DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
+ * Disable overlapped arbitration for some dual function devices,
+ * regardless revision id (kind of post-chip-design feature. ;-))
+ */
+ if (np->device_id == PCI_DEVICE_ID_NCR_53C875)
+ OUTB(np, nc_ctest0, (1<<5));
+ else if (np->device_id == PCI_DEVICE_ID_NCR_53C896)
+ np->rv_ccntl0 |= DPR;
+
+ /*
+ * Write CCNTL0/CCNTL1 for chips capable of 64 bit addressing
+ * and/or hardware phase mismatch, since only such chips
+ * seem to support those IO registers.
+ */
+ if (np->features & (FE_DAC|FE_NOPM)) {
+ OUTB(np, nc_ccntl0, np->rv_ccntl0);
+ OUTB(np, nc_ccntl1, np->rv_ccntl1);
+ }
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+ /*
+ * Set up scratch C and DRS IO registers to map the 32 bit
+ * DMA address range our data structures are located in.
+ */
+ if (np->use_dac) {
+ np->dmap_bah[0] = 0; /* ??? */
+ OUTL(np, nc_scrx[0], np->dmap_bah[0]);
+ OUTL(np, nc_drs, np->dmap_bah[0]);
+ }
+#endif
+
+ /*
+ * If phase mismatch handled by scripts (895A/896/1010),
+ * set PM jump addresses.
+ */
+ if (np->features & FE_NOPM) {
+ OUTL(np, nc_pmjad1, SCRIPTB_BA(np, pm_handle));
+ OUTL(np, nc_pmjad2, SCRIPTB_BA(np, pm_handle));
+ }
+
+ /*
+ * Enable GPIO0 pin for writing if LED support from SCRIPTS.
+ * Also set GPIO5 and clear GPIO6 if hardware LED control.
+ */
+ if (np->features & FE_LED0)
+ OUTB(np, nc_gpcntl, INB(np, nc_gpcntl) & ~0x01);
+ else if (np->features & FE_LEDC)
+ OUTB(np, nc_gpcntl, (INB(np, nc_gpcntl) & ~0x41) | 0x20);
+
+ /*
+ * enable ints
+ */
+ OUTW(np, nc_sien , STO|HTH|MA|SGE|UDC|RST|PAR);
+ OUTB(np, nc_dien , MDPE|BF|SSI|SIR|IID);
+
+ /*
+ * For 895/6 enable SBMC interrupt and save current SCSI bus mode.
+ * Try to eat the spurious SBMC interrupt that may occur when
+ * we reset the chip but not the SCSI BUS (at initialization).
+ */
+ if (np->features & (FE_ULTRA2|FE_ULTRA3)) {
+ OUTONW(np, nc_sien, SBMC);
+ if (reason == 0) {
+ mdelay(100);
+ INW(np, nc_sist);
+ }
+ np->scsi_mode = INB(np, nc_stest4) & SMODE;
+ }
+
+ /*
+ * Fill in target structure.
+ * Reinitialize usrsync.
+ * Reinitialize usrwide.
+ * Prepare sync negotiation according to actual SCSI bus mode.
+ */
+ for (i=0;i<SYM_CONF_MAX_TARGET;i++) {
+ struct sym_tcb *tp = &np->target[i];
+
+ tp->to_reset = 0;
+ tp->head.sval = 0;
+ tp->head.wval = np->rv_scntl3;
+ tp->head.uval = 0;
+ }
+
+ /*
+ * Download SCSI SCRIPTS to on-chip RAM if present,
+ * and start script processor.
+ * We do the download preferently from the CPU.
+ * For platforms that may not support PCI memory mapping,
+ * we use simple SCRIPTS that performs MEMORY MOVEs.
+ */
+ phys = SCRIPTA_BA(np, init);
+ if (np->ram_ba) {
+ if (sym_verbose >= 2)
+ printf("%s: Downloading SCSI SCRIPTS.\n", sym_name(np));
+ memcpy_toio(np->s.ramaddr, np->scripta0, np->scripta_sz);
+ if (np->ram_ws == 8192) {
+ memcpy_toio(np->s.ramaddr + 4096, np->scriptb0, np->scriptb_sz);
+ phys = scr_to_cpu(np->scr_ram_seg);
+ OUTL(np, nc_mmws, phys);
+ OUTL(np, nc_mmrs, phys);
+ OUTL(np, nc_sfs, phys);
+ phys = SCRIPTB_BA(np, start64);
+ }
+ }
+
+ np->istat_sem = 0;
+
+ OUTL(np, nc_dsa, np->hcb_ba);
+ OUTL_DSP(np, phys);
+
+ /*
+ * Notify the XPT about the RESET condition.
+ */
+ if (reason != 0)
+ sym_xpt_async_bus_reset(np);
+}
+
+/*
+ * Switch trans mode for current job and its target.
+ */
+static void sym_settrans(struct sym_hcb *np, int target, u_char opts, u_char ofs,
+ u_char per, u_char wide, u_char div, u_char fak)
+{
+ SYM_QUEHEAD *qp;
+ u_char sval, wval, uval;
+ struct sym_tcb *tp = &np->target[target];
+
+ assert(target == (INB(np, nc_sdid) & 0x0f));
+
+ sval = tp->head.sval;
+ wval = tp->head.wval;
+ uval = tp->head.uval;
+
+#if 0
+ printf("XXXX sval=%x wval=%x uval=%x (%x)\n",
+ sval, wval, uval, np->rv_scntl3);
+#endif
+ /*
+ * Set the offset.
+ */
+ if (!(np->features & FE_C10))
+ sval = (sval & ~0x1f) | ofs;
+ else
+ sval = (sval & ~0x3f) | ofs;
+
+ /*
+ * Set the sync divisor and extra clock factor.
+ */
+ if (ofs != 0) {
+ wval = (wval & ~0x70) | ((div+1) << 4);
+ if (!(np->features & FE_C10))
+ sval = (sval & ~0xe0) | (fak << 5);
+ else {
+ uval = uval & ~(XCLKH_ST|XCLKH_DT|XCLKS_ST|XCLKS_DT);
+ if (fak >= 1) uval |= (XCLKH_ST|XCLKH_DT);
+ if (fak >= 2) uval |= (XCLKS_ST|XCLKS_DT);
+ }
+ }
+
+ /*
+ * Set the bus width.
+ */
+ wval = wval & ~EWS;
+ if (wide != 0)
+ wval |= EWS;
+
+ /*
+ * Set misc. ultra enable bits.
+ */
+ if (np->features & FE_C10) {
+ uval = uval & ~(U3EN|AIPCKEN);
+ if (opts) {
+ assert(np->features & FE_U3EN);
+ uval |= U3EN;
+ }
+ } else {
+ wval = wval & ~ULTRA;
+ if (per <= 12) wval |= ULTRA;
+ }
+
+ /*
+ * Stop there if sync parameters are unchanged.
+ */
+ if (tp->head.sval == sval &&
+ tp->head.wval == wval &&
+ tp->head.uval == uval)
+ return;
+ tp->head.sval = sval;
+ tp->head.wval = wval;
+ tp->head.uval = uval;
+
+ /*
+ * Disable extended Sreq/Sack filtering if per < 50.
+ * Not supported on the C1010.
+ */
+ if (per < 50 && !(np->features & FE_C10))
+ OUTOFFB(np, nc_stest2, EXT);
+
+ /*
+ * set actual value and sync_status
+ */
+ OUTB(np, nc_sxfer, tp->head.sval);
+ OUTB(np, nc_scntl3, tp->head.wval);
+
+ if (np->features & FE_C10) {
+ OUTB(np, nc_scntl4, tp->head.uval);
+ }
+
+ /*
+ * patch ALL busy ccbs of this target.
+ */
+ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+ struct sym_ccb *cp;
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ if (cp->target != target)
+ continue;
+ cp->phys.select.sel_scntl3 = tp->head.wval;
+ cp->phys.select.sel_sxfer = tp->head.sval;
+ if (np->features & FE_C10) {
+ cp->phys.select.sel_scntl4 = tp->head.uval;
+ }
+ }
+}
+
+/*
+ * We received a WDTR.
+ * Let everything be aware of the changes.
+ */
+static void sym_setwide(struct sym_hcb *np, int target, u_char wide)
+{
+ struct sym_tcb *tp = &np->target[target];
+ struct scsi_target *starget = tp->sdev->sdev_target;
+
+ if (spi_width(starget) == wide)
+ return;
+
+ sym_settrans(np, target, 0, 0, 0, wide, 0, 0);
+
+ tp->tgoal.width = wide;
+ spi_offset(starget) = 0;
+ spi_period(starget) = 0;
+ spi_width(starget) = wide;
+ spi_iu(starget) = 0;
+ spi_dt(starget) = 0;
+ spi_qas(starget) = 0;
+
+ if (sym_verbose >= 3)
+ spi_display_xfer_agreement(starget);
+}
+
+/*
+ * We received a SDTR.
+ * Let everything be aware of the changes.
+ */
+static void
+sym_setsync(struct sym_hcb *np, int target,
+ u_char ofs, u_char per, u_char div, u_char fak)
+{
+ struct sym_tcb *tp = &np->target[target];
+ struct scsi_target *starget = tp->sdev->sdev_target;
+ u_char wide = (tp->head.wval & EWS) ? BUS_16_BIT : BUS_8_BIT;
+
+ sym_settrans(np, target, 0, ofs, per, wide, div, fak);
+
+ spi_period(starget) = per;
+ spi_offset(starget) = ofs;
+ spi_iu(starget) = spi_dt(starget) = spi_qas(starget) = 0;
+
+ if (!tp->tgoal.dt && !tp->tgoal.iu && !tp->tgoal.qas) {
+ tp->tgoal.period = per;
+ tp->tgoal.offset = ofs;
+ tp->tgoal.check_nego = 0;
+ }
+
+ spi_display_xfer_agreement(starget);
+}
+
+/*
+ * We received a PPR.
+ * Let everything be aware of the changes.
+ */
+static void
+sym_setpprot(struct sym_hcb *np, int target, u_char opts, u_char ofs,
+ u_char per, u_char wide, u_char div, u_char fak)
+{
+ struct sym_tcb *tp = &np->target[target];
+ struct scsi_target *starget = tp->sdev->sdev_target;
+
+ sym_settrans(np, target, opts, ofs, per, wide, div, fak);
+
+ spi_width(starget) = tp->tgoal.width = wide;
+ spi_period(starget) = tp->tgoal.period = per;
+ spi_offset(starget) = tp->tgoal.offset = ofs;
+ spi_iu(starget) = tp->tgoal.iu = !!(opts & PPR_OPT_IU);
+ spi_dt(starget) = tp->tgoal.dt = !!(opts & PPR_OPT_DT);
+ spi_qas(starget) = tp->tgoal.qas = !!(opts & PPR_OPT_QAS);
+ tp->tgoal.check_nego = 0;
+
+ spi_display_xfer_agreement(starget);
+}
+
+/*
+ * generic recovery from scsi interrupt
+ *
+ * The doc says that when the chip gets an SCSI interrupt,
+ * it tries to stop in an orderly fashion, by completing
+ * an instruction fetch that had started or by flushing
+ * the DMA fifo for a write to memory that was executing.
+ * Such a fashion is not enough to know if the instruction
+ * that was just before the current DSP value has been
+ * executed or not.
+ *
+ * There are some small SCRIPTS sections that deal with
+ * the start queue and the done queue that may break any
+ * assomption from the C code if we are interrupted
+ * inside, so we reset if this happens. Btw, since these
+ * SCRIPTS sections are executed while the SCRIPTS hasn't
+ * started SCSI operations, it is very unlikely to happen.
+ *
+ * All the driver data structures are supposed to be
+ * allocated from the same 4 GB memory window, so there
+ * is a 1 to 1 relationship between DSA and driver data
+ * structures. Since we are careful :) to invalidate the
+ * DSA when we complete a command or when the SCRIPTS
+ * pushes a DSA into a queue, we can trust it when it
+ * points to a CCB.
+ */
+static void sym_recover_scsi_int (struct sym_hcb *np, u_char hsts)
+{
+ u32 dsp = INL(np, nc_dsp);
+ u32 dsa = INL(np, nc_dsa);
+ struct sym_ccb *cp = sym_ccb_from_dsa(np, dsa);
+
+ /*
+ * If we haven't been interrupted inside the SCRIPTS
+ * critical pathes, we can safely restart the SCRIPTS
+ * and trust the DSA value if it matches a CCB.
+ */
+ if ((!(dsp > SCRIPTA_BA(np, getjob_begin) &&
+ dsp < SCRIPTA_BA(np, getjob_end) + 1)) &&
+ (!(dsp > SCRIPTA_BA(np, ungetjob) &&
+ dsp < SCRIPTA_BA(np, reselect) + 1)) &&
+ (!(dsp > SCRIPTB_BA(np, sel_for_abort) &&
+ dsp < SCRIPTB_BA(np, sel_for_abort_1) + 1)) &&
+ (!(dsp > SCRIPTA_BA(np, done) &&
+ dsp < SCRIPTA_BA(np, done_end) + 1))) {
+ OUTB(np, nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */
+ OUTB(np, nc_stest3, TE|CSF); /* clear scsi fifo */
+ /*
+ * If we have a CCB, let the SCRIPTS call us back for
+ * the handling of the error with SCRATCHA filled with
+ * STARTPOS. This way, we will be able to freeze the
+ * device queue and requeue awaiting IOs.
+ */
+ if (cp) {
+ cp->host_status = hsts;
+ OUTL_DSP(np, SCRIPTA_BA(np, complete_error));
+ }
+ /*
+ * Otherwise just restart the SCRIPTS.
+ */
+ else {
+ OUTL(np, nc_dsa, 0xffffff);
+ OUTL_DSP(np, SCRIPTA_BA(np, start));
+ }
+ }
+ else
+ goto reset_all;
+
+ return;
+
+reset_all:
+ sym_start_reset(np);
+}
+
+/*
+ * chip exception handler for selection timeout
+ */
+static void sym_int_sto (struct sym_hcb *np)
+{
+ u32 dsp = INL(np, nc_dsp);
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("T");
+
+ if (dsp == SCRIPTA_BA(np, wf_sel_done) + 8)
+ sym_recover_scsi_int(np, HS_SEL_TIMEOUT);
+ else
+ sym_start_reset(np);
+}
+
+/*
+ * chip exception handler for unexpected disconnect
+ */
+static void sym_int_udc (struct sym_hcb *np)
+{
+ printf ("%s: unexpected disconnect\n", sym_name(np));
+ sym_recover_scsi_int(np, HS_UNEXPECTED);
+}
+
+/*
+ * chip exception handler for SCSI bus mode change
+ *
+ * spi2-r12 11.2.3 says a transceiver mode change must
+ * generate a reset event and a device that detects a reset
+ * event shall initiate a hard reset. It says also that a
+ * device that detects a mode change shall set data transfer
+ * mode to eight bit asynchronous, etc...
+ * So, just reinitializing all except chip should be enough.
+ */
+static void sym_int_sbmc (struct sym_hcb *np)
+{
+ u_char scsi_mode = INB(np, nc_stest4) & SMODE;
+
+ /*
+ * Notify user.
+ */
+ printf("%s: SCSI BUS mode change from %s to %s.\n", sym_name(np),
+ sym_scsi_bus_mode(np->scsi_mode), sym_scsi_bus_mode(scsi_mode));
+
+ /*
+ * Should suspend command processing for a few seconds and
+ * reinitialize all except the chip.
+ */
+ sym_start_up (np, 2);
+}
+
+/*
+ * chip exception handler for SCSI parity error.
+ *
+ * When the chip detects a SCSI parity error and is
+ * currently executing a (CH)MOV instruction, it does
+ * not interrupt immediately, but tries to finish the
+ * transfer of the current scatter entry before
+ * interrupting. The following situations may occur:
+ *
+ * - The complete scatter entry has been transferred
+ * without the device having changed phase.
+ * The chip will then interrupt with the DSP pointing
+ * to the instruction that follows the MOV.
+ *
+ * - A phase mismatch occurs before the MOV finished
+ * and phase errors are to be handled by the C code.
+ * The chip will then interrupt with both PAR and MA
+ * conditions set.
+ *
+ * - A phase mismatch occurs before the MOV finished and
+ * phase errors are to be handled by SCRIPTS.
+ * The chip will load the DSP with the phase mismatch
+ * JUMP address and interrupt the host processor.
+ */
+static void sym_int_par (struct sym_hcb *np, u_short sist)
+{
+ u_char hsts = INB(np, HS_PRT);
+ u32 dsp = INL(np, nc_dsp);
+ u32 dbc = INL(np, nc_dbc);
+ u32 dsa = INL(np, nc_dsa);
+ u_char sbcl = INB(np, nc_sbcl);
+ u_char cmd = dbc >> 24;
+ int phase = cmd & 7;
+ struct sym_ccb *cp = sym_ccb_from_dsa(np, dsa);
+
+ printf("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n",
+ sym_name(np), hsts, dbc, sbcl);
+
+ /*
+ * Check that the chip is connected to the SCSI BUS.
+ */
+ if (!(INB(np, nc_scntl1) & ISCON)) {
+ sym_recover_scsi_int(np, HS_UNEXPECTED);
+ return;
+ }
+
+ /*
+ * If the nexus is not clearly identified, reset the bus.
+ * We will try to do better later.
+ */
+ if (!cp)
+ goto reset_all;
+
+ /*
+ * Check instruction was a MOV, direction was INPUT and
+ * ATN is asserted.
+ */
+ if ((cmd & 0xc0) || !(phase & 1) || !(sbcl & 0x8))
+ goto reset_all;
+
+ /*
+ * Keep track of the parity error.
+ */
+ OUTONB(np, HF_PRT, HF_EXT_ERR);
+ cp->xerr_status |= XE_PARITY_ERR;
+
+ /*
+ * Prepare the message to send to the device.
+ */
+ np->msgout[0] = (phase == 7) ? M_PARITY : M_ID_ERROR;
+
+ /*
+ * If the old phase was DATA IN phase, we have to deal with
+ * the 3 situations described above.
+ * For other input phases (MSG IN and STATUS), the device
+ * must resend the whole thing that failed parity checking
+ * or signal error. So, jumping to dispatcher should be OK.
+ */
+ if (phase == 1 || phase == 5) {
+ /* Phase mismatch handled by SCRIPTS */
+ if (dsp == SCRIPTB_BA(np, pm_handle))
+ OUTL_DSP(np, dsp);
+ /* Phase mismatch handled by the C code */
+ else if (sist & MA)
+ sym_int_ma (np);
+ /* No phase mismatch occurred */
+ else {
+ sym_set_script_dp (np, cp, dsp);
+ OUTL_DSP(np, SCRIPTA_BA(np, dispatch));
+ }
+ }
+ else if (phase == 7) /* We definitely cannot handle parity errors */
+#if 1 /* in message-in phase due to the relection */
+ goto reset_all; /* path and various message anticipations. */
+#else
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+#endif
+ else
+ OUTL_DSP(np, SCRIPTA_BA(np, dispatch));
+ return;
+
+reset_all:
+ sym_start_reset(np);
+ return;
+}
+
+/*
+ * chip exception handler for phase errors.
+ *
+ * We have to construct a new transfer descriptor,
+ * to transfer the rest of the current block.
+ */
+static void sym_int_ma (struct sym_hcb *np)
+{
+ u32 dbc;
+ u32 rest;
+ u32 dsp;
+ u32 dsa;
+ u32 nxtdsp;
+ u32 *vdsp;
+ u32 oadr, olen;
+ u32 *tblp;
+ u32 newcmd;
+ u_int delta;
+ u_char cmd;
+ u_char hflags, hflags0;
+ struct sym_pmc *pm;
+ struct sym_ccb *cp;
+
+ dsp = INL(np, nc_dsp);
+ dbc = INL(np, nc_dbc);
+ dsa = INL(np, nc_dsa);
+
+ cmd = dbc >> 24;
+ rest = dbc & 0xffffff;
+ delta = 0;
+
+ /*
+ * locate matching cp if any.
+ */
+ cp = sym_ccb_from_dsa(np, dsa);
+
+ /*
+ * Donnot take into account dma fifo and various buffers in
+ * INPUT phase since the chip flushes everything before
+ * raising the MA interrupt for interrupted INPUT phases.
+ * For DATA IN phase, we will check for the SWIDE later.
+ */
+ if ((cmd & 7) != 1 && (cmd & 7) != 5) {
+ u_char ss0, ss2;
+
+ if (np->features & FE_DFBC)
+ delta = INW(np, nc_dfbc);
+ else {
+ u32 dfifo;
+
+ /*
+ * Read DFIFO, CTEST[4-6] using 1 PCI bus ownership.
+ */
+ dfifo = INL(np, nc_dfifo);
+
+ /*
+ * Calculate remaining bytes in DMA fifo.
+ * (CTEST5 = dfifo >> 16)
+ */
+ if (dfifo & (DFS << 16))
+ delta = ((((dfifo >> 8) & 0x300) |
+ (dfifo & 0xff)) - rest) & 0x3ff;
+ else
+ delta = ((dfifo & 0xff) - rest) & 0x7f;
+ }
+
+ /*
+ * The data in the dma fifo has not been transfered to
+ * the target -> add the amount to the rest
+ * and clear the data.
+ * Check the sstat2 register in case of wide transfer.
+ */
+ rest += delta;
+ ss0 = INB(np, nc_sstat0);
+ if (ss0 & OLF) rest++;
+ if (!(np->features & FE_C10))
+ if (ss0 & ORF) rest++;
+ if (cp && (cp->phys.select.sel_scntl3 & EWS)) {
+ ss2 = INB(np, nc_sstat2);
+ if (ss2 & OLF1) rest++;
+ if (!(np->features & FE_C10))
+ if (ss2 & ORF1) rest++;
+ }
+
+ /*
+ * Clear fifos.
+ */
+ OUTB(np, nc_ctest3, np->rv_ctest3 | CLF); /* dma fifo */
+ OUTB(np, nc_stest3, TE|CSF); /* scsi fifo */
+ }
+
+ /*
+ * log the information
+ */
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printf ("P%x%x RL=%d D=%d ", cmd&7, INB(np, nc_sbcl)&7,
+ (unsigned) rest, (unsigned) delta);
+
+ /*
+ * try to find the interrupted script command,
+ * and the address at which to continue.
+ */
+ vdsp = NULL;
+ nxtdsp = 0;
+ if (dsp > np->scripta_ba &&
+ dsp <= np->scripta_ba + np->scripta_sz) {
+ vdsp = (u32 *)((char*)np->scripta0 + (dsp-np->scripta_ba-8));
+ nxtdsp = dsp;
+ }
+ else if (dsp > np->scriptb_ba &&
+ dsp <= np->scriptb_ba + np->scriptb_sz) {
+ vdsp = (u32 *)((char*)np->scriptb0 + (dsp-np->scriptb_ba-8));
+ nxtdsp = dsp;
+ }
+
+ /*
+ * log the information
+ */
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ printf ("\nCP=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
+ cp, (unsigned)dsp, (unsigned)nxtdsp, vdsp, cmd);
+ }
+
+ if (!vdsp) {
+ printf ("%s: interrupted SCRIPT address not found.\n",
+ sym_name (np));
+ goto reset_all;
+ }
+
+ if (!cp) {
+ printf ("%s: SCSI phase error fixup: CCB already dequeued.\n",
+ sym_name (np));
+ goto reset_all;
+ }
+
+ /*
+ * get old startaddress and old length.
+ */
+ oadr = scr_to_cpu(vdsp[1]);
+
+ if (cmd & 0x10) { /* Table indirect */
+ tblp = (u32 *) ((char*) &cp->phys + oadr);
+ olen = scr_to_cpu(tblp[0]);
+ oadr = scr_to_cpu(tblp[1]);
+ } else {
+ tblp = (u32 *) 0;
+ olen = scr_to_cpu(vdsp[0]) & 0xffffff;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
+ (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
+ tblp,
+ (unsigned) olen,
+ (unsigned) oadr);
+ }
+
+ /*
+ * check cmd against assumed interrupted script command.
+ * If dt data phase, the MOVE instruction hasn't bit 4 of
+ * the phase.
+ */
+ if (((cmd & 2) ? cmd : (cmd & ~4)) != (scr_to_cpu(vdsp[0]) >> 24)) {
+ sym_print_addr(cp->cmd,
+ "internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
+ cmd, scr_to_cpu(vdsp[0]) >> 24);
+
+ goto reset_all;
+ }
+
+ /*
+ * if old phase not dataphase, leave here.
+ */
+ if (cmd & 2) {
+ sym_print_addr(cp->cmd,
+ "phase change %x-%x %d@%08x resid=%d.\n",
+ cmd&7, INB(np, nc_sbcl)&7, (unsigned)olen,
+ (unsigned)oadr, (unsigned)rest);
+ goto unexpected_phase;
+ }
+
+ /*
+ * Choose the correct PM save area.
+ *
+ * Look at the PM_SAVE SCRIPT if you want to understand
+ * this stuff. The equivalent code is implemented in
+ * SCRIPTS for the 895A, 896 and 1010 that are able to
+ * handle PM from the SCRIPTS processor.
+ */
+ hflags0 = INB(np, HF_PRT);
+ hflags = hflags0;
+
+ if (hflags & (HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED)) {
+ if (hflags & HF_IN_PM0)
+ nxtdsp = scr_to_cpu(cp->phys.pm0.ret);
+ else if (hflags & HF_IN_PM1)
+ nxtdsp = scr_to_cpu(cp->phys.pm1.ret);
+
+ if (hflags & HF_DP_SAVED)
+ hflags ^= HF_ACT_PM;
+ }
+
+ if (!(hflags & HF_ACT_PM)) {
+ pm = &cp->phys.pm0;
+ newcmd = SCRIPTA_BA(np, pm0_data);
+ }
+ else {
+ pm = &cp->phys.pm1;
+ newcmd = SCRIPTA_BA(np, pm1_data);
+ }
+
+ hflags &= ~(HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED);
+ if (hflags != hflags0)
+ OUTB(np, HF_PRT, hflags);
+
+ /*
+ * fillin the phase mismatch context
+ */
+ pm->sg.addr = cpu_to_scr(oadr + olen - rest);
+ pm->sg.size = cpu_to_scr(rest);
+ pm->ret = cpu_to_scr(nxtdsp);
+
+ /*
+ * If we have a SWIDE,
+ * - prepare the address to write the SWIDE from SCRIPTS,
+ * - compute the SCRIPTS address to restart from,
+ * - move current data pointer context by one byte.
+ */
+ nxtdsp = SCRIPTA_BA(np, dispatch);
+ if ((cmd & 7) == 1 && cp && (cp->phys.select.sel_scntl3 & EWS) &&
+ (INB(np, nc_scntl2) & WSR)) {
+ u32 tmp;
+
+ /*
+ * Set up the table indirect for the MOVE
+ * of the residual byte and adjust the data
+ * pointer context.
+ */
+ tmp = scr_to_cpu(pm->sg.addr);
+ cp->phys.wresid.addr = cpu_to_scr(tmp);
+ pm->sg.addr = cpu_to_scr(tmp + 1);
+ tmp = scr_to_cpu(pm->sg.size);
+ cp->phys.wresid.size = cpu_to_scr((tmp&0xff000000) | 1);
+ pm->sg.size = cpu_to_scr(tmp - 1);
+
+ /*
+ * If only the residual byte is to be moved,
+ * no PM context is needed.
+ */
+ if ((tmp&0xffffff) == 1)
+ newcmd = pm->ret;
+
+ /*
+ * Prepare the address of SCRIPTS that will
+ * move the residual byte to memory.
+ */
+ nxtdsp = SCRIPTB_BA(np, wsr_ma_helper);
+ }
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ sym_print_addr(cp->cmd, "PM %x %x %x / %x %x %x.\n",
+ hflags0, hflags, newcmd,
+ (unsigned)scr_to_cpu(pm->sg.addr),
+ (unsigned)scr_to_cpu(pm->sg.size),
+ (unsigned)scr_to_cpu(pm->ret));
+ }
+
+ /*
+ * Restart the SCRIPTS processor.
+ */
+ sym_set_script_dp (np, cp, newcmd);
+ OUTL_DSP(np, nxtdsp);
+ return;
+
+ /*
+ * Unexpected phase changes that occurs when the current phase
+ * is not a DATA IN or DATA OUT phase are due to error conditions.
+ * Such event may only happen when the SCRIPTS is using a
+ * multibyte SCSI MOVE.
+ *
+ * Phase change Some possible cause
+ *
+ * COMMAND --> MSG IN SCSI parity error detected by target.
+ * COMMAND --> STATUS Bad command or refused by target.
+ * MSG OUT --> MSG IN Message rejected by target.
+ * MSG OUT --> COMMAND Bogus target that discards extended
+ * negotiation messages.
+ *
+ * The code below does not care of the new phase and so
+ * trusts the target. Why to annoy it ?
+ * If the interrupted phase is COMMAND phase, we restart at
+ * dispatcher.
+ * If a target does not get all the messages after selection,
+ * the code assumes blindly that the target discards extended
+ * messages and clears the negotiation status.
+ * If the target does not want all our response to negotiation,
+ * we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids
+ * bloat for such a should_not_happen situation).
+ * In all other situation, we reset the BUS.
+ * Are these assumptions reasonnable ? (Wait and see ...)
+ */
+unexpected_phase:
+ dsp -= 8;
+ nxtdsp = 0;
+
+ switch (cmd & 7) {
+ case 2: /* COMMAND phase */
+ nxtdsp = SCRIPTA_BA(np, dispatch);
+ break;
+#if 0
+ case 3: /* STATUS phase */
+ nxtdsp = SCRIPTA_BA(np, dispatch);
+ break;
+#endif
+ case 6: /* MSG OUT phase */
+ /*
+ * If the device may want to use untagged when we want
+ * tagged, we prepare an IDENTIFY without disc. granted,
+ * since we will not be able to handle reselect.
+ * Otherwise, we just don't care.
+ */
+ if (dsp == SCRIPTA_BA(np, send_ident)) {
+ if (cp->tag != NO_TAG && olen - rest <= 3) {
+ cp->host_status = HS_BUSY;
+ np->msgout[0] = IDENTIFY(0, cp->lun);
+ nxtdsp = SCRIPTB_BA(np, ident_break_atn);
+ }
+ else
+ nxtdsp = SCRIPTB_BA(np, ident_break);
+ }
+ else if (dsp == SCRIPTB_BA(np, send_wdtr) ||
+ dsp == SCRIPTB_BA(np, send_sdtr) ||
+ dsp == SCRIPTB_BA(np, send_ppr)) {
+ nxtdsp = SCRIPTB_BA(np, nego_bad_phase);
+ if (dsp == SCRIPTB_BA(np, send_ppr)) {
+ struct scsi_device *dev = cp->cmd->device;
+ dev->ppr = 0;
+ }
+ }
+ break;
+#if 0
+ case 7: /* MSG IN phase */
+ nxtdsp = SCRIPTA_BA(np, clrack);
+ break;
+#endif
+ }
+
+ if (nxtdsp) {
+ OUTL_DSP(np, nxtdsp);
+ return;
+ }
+
+reset_all:
+ sym_start_reset(np);
+}
+
+/*
+ * chip interrupt handler
+ *
+ * In normal situations, interrupt conditions occur one at
+ * a time. But when something bad happens on the SCSI BUS,
+ * the chip may raise several interrupt flags before
+ * stopping and interrupting the CPU. The additionnal
+ * interrupt flags are stacked in some extra registers
+ * after the SIP and/or DIP flag has been raised in the
+ * ISTAT. After the CPU has read the interrupt condition
+ * flag from SIST or DSTAT, the chip unstacks the other
+ * interrupt flags and sets the corresponding bits in
+ * SIST or DSTAT. Since the chip starts stacking once the
+ * SIP or DIP flag is set, there is a small window of time
+ * where the stacking does not occur.
+ *
+ * Typically, multiple interrupt conditions may happen in
+ * the following situations:
+ *
+ * - SCSI parity error + Phase mismatch (PAR|MA)
+ * When an parity error is detected in input phase
+ * and the device switches to msg-in phase inside a
+ * block MOV.
+ * - SCSI parity error + Unexpected disconnect (PAR|UDC)
+ * When a stupid device does not want to handle the
+ * recovery of an SCSI parity error.
+ * - Some combinations of STO, PAR, UDC, ...
+ * When using non compliant SCSI stuff, when user is
+ * doing non compliant hot tampering on the BUS, when
+ * something really bad happens to a device, etc ...
+ *
+ * The heuristic suggested by SYMBIOS to handle
+ * multiple interrupts is to try unstacking all
+ * interrupts conditions and to handle them on some
+ * priority based on error severity.
+ * This will work when the unstacking has been
+ * successful, but we cannot be 100 % sure of that,
+ * since the CPU may have been faster to unstack than
+ * the chip is able to stack. Hmmm ... But it seems that
+ * such a situation is very unlikely to happen.
+ *
+ * If this happen, for example STO caught by the CPU
+ * then UDC happenning before the CPU have restarted
+ * the SCRIPTS, the driver may wrongly complete the
+ * same command on UDC, since the SCRIPTS didn't restart
+ * and the DSA still points to the same command.
+ * We avoid this situation by setting the DSA to an
+ * invalid value when the CCB is completed and before
+ * restarting the SCRIPTS.
+ *
+ * Another issue is that we need some section of our
+ * recovery procedures to be somehow uninterruptible but
+ * the SCRIPTS processor does not provides such a
+ * feature. For this reason, we handle recovery preferently
+ * from the C code and check against some SCRIPTS critical
+ * sections from the C code.
+ *
+ * Hopefully, the interrupt handling of the driver is now
+ * able to resist to weird BUS error conditions, but donnot
+ * ask me for any guarantee that it will never fail. :-)
+ * Use at your own decision and risk.
+ */
+
+void sym_interrupt (struct sym_hcb *np)
+{
+ u_char istat, istatc;
+ u_char dstat;
+ u_short sist;
+
+ /*
+ * interrupt on the fly ?
+ * (SCRIPTS may still be running)
+ *
+ * A `dummy read' is needed to ensure that the
+ * clear of the INTF flag reaches the device
+ * and that posted writes are flushed to memory
+ * before the scanning of the DONE queue.
+ * Note that SCRIPTS also (dummy) read to memory
+ * prior to deliver the INTF interrupt condition.
+ */
+ istat = INB(np, nc_istat);
+ if (istat & INTF) {
+ OUTB(np, nc_istat, (istat & SIGP) | INTF | np->istat_sem);
+ istat = INB(np, nc_istat); /* DUMMY READ */
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
+ sym_wakeup_done(np);
+ }
+
+ if (!(istat & (SIP|DIP)))
+ return;
+
+#if 0 /* We should never get this one */
+ if (istat & CABRT)
+ OUTB(np, nc_istat, CABRT);
+#endif
+
+ /*
+ * PAR and MA interrupts may occur at the same time,
+ * and we need to know of both in order to handle
+ * this situation properly. We try to unstack SCSI
+ * interrupts for that reason. BTW, I dislike a LOT
+ * such a loop inside the interrupt routine.
+ * Even if DMA interrupt stacking is very unlikely to
+ * happen, we also try unstacking these ones, since
+ * this has no performance impact.
+ */
+ sist = 0;
+ dstat = 0;
+ istatc = istat;
+ do {
+ if (istatc & SIP)
+ sist |= INW(np, nc_sist);
+ if (istatc & DIP)
+ dstat |= INB(np, nc_dstat);
+ istatc = INB(np, nc_istat);
+ istat |= istatc;
+ } while (istatc & (SIP|DIP));
+
+ if (DEBUG_FLAGS & DEBUG_TINY)
+ printf ("<%d|%x:%x|%x:%x>",
+ (int)INB(np, nc_scr0),
+ dstat,sist,
+ (unsigned)INL(np, nc_dsp),
+ (unsigned)INL(np, nc_dbc));
+ /*
+ * On paper, a memory read barrier may be needed here to
+ * prevent out of order LOADs by the CPU from having
+ * prefetched stale data prior to DMA having occurred.
+ * And since we are paranoid ... :)
+ */
+ MEMORY_READ_BARRIER();
+
+ /*
+ * First, interrupts we want to service cleanly.
+ *
+ * Phase mismatch (MA) is the most frequent interrupt
+ * for chip earlier than the 896 and so we have to service
+ * it as quickly as possible.
+ * A SCSI parity error (PAR) may be combined with a phase
+ * mismatch condition (MA).
+ * Programmed interrupts (SIR) are used to call the C code
+ * from SCRIPTS.
+ * The single step interrupt (SSI) is not used in this
+ * driver.
+ */
+ if (!(sist & (STO|GEN|HTH|SGE|UDC|SBMC|RST)) &&
+ !(dstat & (MDPE|BF|ABRT|IID))) {
+ if (sist & PAR) sym_int_par (np, sist);
+ else if (sist & MA) sym_int_ma (np);
+ else if (dstat & SIR) sym_int_sir (np);
+ else if (dstat & SSI) OUTONB_STD();
+ else goto unknown_int;
+ return;
+ }
+
+ /*
+ * Now, interrupts that donnot happen in normal
+ * situations and that we may need to recover from.
+ *
+ * On SCSI RESET (RST), we reset everything.
+ * On SCSI BUS MODE CHANGE (SBMC), we complete all
+ * active CCBs with RESET status, prepare all devices
+ * for negotiating again and restart the SCRIPTS.
+ * On STO and UDC, we complete the CCB with the corres-
+ * ponding status and restart the SCRIPTS.
+ */
+ if (sist & RST) {
+ printf("%s: SCSI BUS reset detected.\n", sym_name(np));
+ sym_start_up (np, 1);
+ return;
+ }
+
+ OUTB(np, nc_ctest3, np->rv_ctest3 | CLF); /* clear dma fifo */
+ OUTB(np, nc_stest3, TE|CSF); /* clear scsi fifo */
+
+ if (!(sist & (GEN|HTH|SGE)) &&
+ !(dstat & (MDPE|BF|ABRT|IID))) {
+ if (sist & SBMC) sym_int_sbmc (np);
+ else if (sist & STO) sym_int_sto (np);
+ else if (sist & UDC) sym_int_udc (np);
+ else goto unknown_int;
+ return;
+ }
+
+ /*
+ * Now, interrupts we are not able to recover cleanly.
+ *
+ * Log message for hard errors.
+ * Reset everything.
+ */
+
+ sym_log_hard_error(np, sist, dstat);
+
+ if ((sist & (GEN|HTH|SGE)) ||
+ (dstat & (MDPE|BF|ABRT|IID))) {
+ sym_start_reset(np);
+ return;
+ }
+
+unknown_int:
+ /*
+ * We just miss the cause of the interrupt. :(
+ * Print a message. The timeout will do the real work.
+ */
+ printf( "%s: unknown interrupt(s) ignored, "
+ "ISTAT=0x%x DSTAT=0x%x SIST=0x%x\n",
+ sym_name(np), istat, dstat, sist);
+}
+
+/*
+ * Dequeue from the START queue all CCBs that match
+ * a given target/lun/task condition (-1 means all),
+ * and move them from the BUSY queue to the COMP queue
+ * with CAM_REQUEUE_REQ status condition.
+ * This function is used during error handling/recovery.
+ * It is called with SCRIPTS not running.
+ */
+static int
+sym_dequeue_from_squeue(struct sym_hcb *np, int i, int target, int lun, int task)
+{
+ int j;
+ struct sym_ccb *cp;
+
+ /*
+ * Make sure the starting index is within range.
+ */
+ assert((i >= 0) && (i < 2*MAX_QUEUE));
+
+ /*
+ * Walk until end of START queue and dequeue every job
+ * that matches the target/lun/task condition.
+ */
+ j = i;
+ while (i != np->squeueput) {
+ cp = sym_ccb_from_dsa(np, scr_to_cpu(np->squeue[i]));
+ assert(cp);
+#ifdef SYM_CONF_IARB_SUPPORT
+ /* Forget hints for IARB, they may be no longer relevant */
+ cp->host_flags &= ~HF_HINT_IARB;
+#endif
+ if ((target == -1 || cp->target == target) &&
+ (lun == -1 || cp->lun == lun) &&
+ (task == -1 || cp->tag == task)) {
+ sym_set_cam_status(cp->cmd, CAM_REQUEUE_REQ);
+ sym_remque(&cp->link_ccbq);
+ sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq);
+ }
+ else {
+ if (i != j)
+ np->squeue[j] = np->squeue[i];
+ if ((j += 2) >= MAX_QUEUE*2) j = 0;
+ }
+ if ((i += 2) >= MAX_QUEUE*2) i = 0;
+ }
+ if (i != j) /* Copy back the idle task if needed */
+ np->squeue[j] = np->squeue[i];
+ np->squeueput = j; /* Update our current start queue pointer */
+
+ return (i - j) / 2;
+}
+
+/*
+ * chip handler for bad SCSI status condition
+ *
+ * In case of bad SCSI status, we unqueue all the tasks
+ * currently queued to the controller but not yet started
+ * and then restart the SCRIPTS processor immediately.
+ *
+ * QUEUE FULL and BUSY conditions are handled the same way.
+ * Basically all the not yet started tasks are requeued in
+ * device queue and the queue is frozen until a completion.
+ *
+ * For CHECK CONDITION and COMMAND TERMINATED status, we use
+ * the CCB of the failed command to prepare a REQUEST SENSE
+ * SCSI command and queue it to the controller queue.
+ *
+ * SCRATCHA is assumed to have been loaded with STARTPOS
+ * before the SCRIPTS called the C code.
+ */
+static void sym_sir_bad_scsi_status(struct sym_hcb *np, int num, struct sym_ccb *cp)
+{
+ u32 startp;
+ u_char s_status = cp->ssss_status;
+ u_char h_flags = cp->host_flags;
+ int msglen;
+ int i;
+
+ /*
+ * Compute the index of the next job to start from SCRIPTS.
+ */
+ i = (INL(np, nc_scratcha) - np->squeue_ba) / 4;
+
+ /*
+ * The last CCB queued used for IARB hint may be
+ * no longer relevant. Forget it.
+ */
+#ifdef SYM_CONF_IARB_SUPPORT
+ if (np->last_cp)
+ np->last_cp = 0;
+#endif
+
+ /*
+ * Now deal with the SCSI status.
+ */
+ switch(s_status) {
+ case S_BUSY:
+ case S_QUEUE_FULL:
+ if (sym_verbose >= 2) {
+ sym_print_addr(cp->cmd, "%s\n",
+ s_status == S_BUSY ? "BUSY" : "QUEUE FULL\n");
+ }
+ default: /* S_INT, S_INT_COND_MET, S_CONFLICT */
+ sym_complete_error (np, cp);
+ break;
+ case S_TERMINATED:
+ case S_CHECK_COND:
+ /*
+ * If we get an SCSI error when requesting sense, give up.
+ */
+ if (h_flags & HF_SENSE) {
+ sym_complete_error (np, cp);
+ break;
+ }
+
+ /*
+ * Dequeue all queued CCBs for that device not yet started,
+ * and restart the SCRIPTS processor immediately.
+ */
+ sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1);
+ OUTL_DSP(np, SCRIPTA_BA(np, start));
+
+ /*
+ * Save some info of the actual IO.
+ * Compute the data residual.
+ */
+ cp->sv_scsi_status = cp->ssss_status;
+ cp->sv_xerr_status = cp->xerr_status;
+ cp->sv_resid = sym_compute_residual(np, cp);
+
+ /*
+ * Prepare all needed data structures for
+ * requesting sense data.
+ */
+
+ cp->scsi_smsg2[0] = IDENTIFY(0, cp->lun);
+ msglen = 1;
+
+ /*
+ * If we are currently using anything different from
+ * async. 8 bit data transfers with that target,
+ * start a negotiation, since the device may want
+ * to report us a UNIT ATTENTION condition due to
+ * a cause we currently ignore, and we donnot want
+ * to be stuck with WIDE and/or SYNC data transfer.
+ *
+ * cp->nego_status is filled by sym_prepare_nego().
+ */
+ cp->nego_status = 0;
+ msglen += sym_prepare_nego(np, cp, &cp->scsi_smsg2[msglen]);
+ /*
+ * Message table indirect structure.
+ */
+ cp->phys.smsg.addr = cpu_to_scr(CCB_BA(cp, scsi_smsg2));
+ cp->phys.smsg.size = cpu_to_scr(msglen);
+
+ /*
+ * sense command
+ */
+ cp->phys.cmd.addr = cpu_to_scr(CCB_BA(cp, sensecmd));
+ cp->phys.cmd.size = cpu_to_scr(6);
+
+ /*
+ * patch requested size into sense command
+ */
+ cp->sensecmd[0] = REQUEST_SENSE;
+ cp->sensecmd[1] = 0;
+ if (cp->cmd->device->scsi_level <= SCSI_2 && cp->lun <= 7)
+ cp->sensecmd[1] = cp->lun << 5;
+ cp->sensecmd[4] = SYM_SNS_BBUF_LEN;
+ cp->data_len = SYM_SNS_BBUF_LEN;
+
+ /*
+ * sense data
+ */
+ memset(cp->sns_bbuf, 0, SYM_SNS_BBUF_LEN);
+ cp->phys.sense.addr = cpu_to_scr(CCB_BA(cp, sns_bbuf));
+ cp->phys.sense.size = cpu_to_scr(SYM_SNS_BBUF_LEN);
+
+ /*
+ * requeue the command.
+ */
+ startp = SCRIPTB_BA(np, sdata_in);
+
+ cp->phys.head.savep = cpu_to_scr(startp);
+ cp->phys.head.lastp = cpu_to_scr(startp);
+ cp->startp = cpu_to_scr(startp);
+ cp->goalp = cpu_to_scr(startp + 16);
+
+ cp->host_xflags = 0;
+ cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
+ cp->ssss_status = S_ILLEGAL;
+ cp->host_flags = (HF_SENSE|HF_DATA_IN);
+ cp->xerr_status = 0;
+ cp->extra_bytes = 0;
+
+ cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA(np, select));
+
+ /*
+ * Requeue the command.
+ */
+ sym_put_start_queue(np, cp);
+
+ /*
+ * Give back to upper layer everything we have dequeued.
+ */
+ sym_flush_comp_queue(np, 0);
+ break;
+ }
+}
+
+/*
+ * After a device has accepted some management message
+ * as BUS DEVICE RESET, ABORT TASK, etc ..., or when
+ * a device signals a UNIT ATTENTION condition, some
+ * tasks are thrown away by the device. We are required
+ * to reflect that on our tasks list since the device
+ * will never complete these tasks.
+ *
+ * This function move from the BUSY queue to the COMP
+ * queue all disconnected CCBs for a given target that
+ * match the following criteria:
+ * - lun=-1 means any logical UNIT otherwise a given one.
+ * - task=-1 means any task, otherwise a given one.
+ */
+int sym_clear_tasks(struct sym_hcb *np, int cam_status, int target, int lun, int task)
+{
+ SYM_QUEHEAD qtmp, *qp;
+ int i = 0;
+ struct sym_ccb *cp;
+
+ /*
+ * Move the entire BUSY queue to our temporary queue.
+ */
+ sym_que_init(&qtmp);
+ sym_que_splice(&np->busy_ccbq, &qtmp);
+ sym_que_init(&np->busy_ccbq);
+
+ /*
+ * Put all CCBs that matches our criteria into
+ * the COMP queue and put back other ones into
+ * the BUSY queue.
+ */
+ while ((qp = sym_remque_head(&qtmp)) != 0) {
+ struct scsi_cmnd *cmd;
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ cmd = cp->cmd;
+ if (cp->host_status != HS_DISCONNECT ||
+ cp->target != target ||
+ (lun != -1 && cp->lun != lun) ||
+ (task != -1 &&
+ (cp->tag != NO_TAG && cp->scsi_smsg[2] != task))) {
+ sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq);
+ continue;
+ }
+ sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq);
+
+ /* Preserve the software timeout condition */
+ if (sym_get_cam_status(cmd) != CAM_CMD_TIMEOUT)
+ sym_set_cam_status(cmd, cam_status);
+ ++i;
+#if 0
+printf("XXXX TASK @%p CLEARED\n", cp);
+#endif
+ }
+ return i;
+}
+
+/*
+ * chip handler for TASKS recovery
+ *
+ * We cannot safely abort a command, while the SCRIPTS
+ * processor is running, since we just would be in race
+ * with it.
+ *
+ * As long as we have tasks to abort, we keep the SEM
+ * bit set in the ISTAT. When this bit is set, the
+ * SCRIPTS processor interrupts (SIR_SCRIPT_STOPPED)
+ * each time it enters the scheduler.
+ *
+ * If we have to reset a target, clear tasks of a unit,
+ * or to perform the abort of a disconnected job, we
+ * restart the SCRIPTS for selecting the target. Once
+ * selected, the SCRIPTS interrupts (SIR_TARGET_SELECTED).
+ * If it loses arbitration, the SCRIPTS will interrupt again
+ * the next time it will enter its scheduler, and so on ...
+ *
+ * On SIR_TARGET_SELECTED, we scan for the more
+ * appropriate thing to do:
+ *
+ * - If nothing, we just sent a M_ABORT message to the
+ * target to get rid of the useless SCSI bus ownership.
+ * According to the specs, no tasks shall be affected.
+ * - If the target is to be reset, we send it a M_RESET
+ * message.
+ * - If a logical UNIT is to be cleared , we send the
+ * IDENTIFY(lun) + M_ABORT.
+ * - If an untagged task is to be aborted, we send the
+ * IDENTIFY(lun) + M_ABORT.
+ * - If a tagged task is to be aborted, we send the
+ * IDENTIFY(lun) + task attributes + M_ABORT_TAG.
+ *
+ * Once our 'kiss of death' :) message has been accepted
+ * by the target, the SCRIPTS interrupts again
+ * (SIR_ABORT_SENT). On this interrupt, we complete
+ * all the CCBs that should have been aborted by the
+ * target according to our message.
+ */
+static void sym_sir_task_recovery(struct sym_hcb *np, int num)
+{
+ SYM_QUEHEAD *qp;
+ struct sym_ccb *cp;
+ struct sym_tcb *tp = NULL; /* gcc isn't quite smart enough yet */
+ struct scsi_target *starget;
+ int target=-1, lun=-1, task;
+ int i, k;
+
+ switch(num) {
+ /*
+ * The SCRIPTS processor stopped before starting
+ * the next command in order to allow us to perform
+ * some task recovery.
+ */
+ case SIR_SCRIPT_STOPPED:
+ /*
+ * Do we have any target to reset or unit to clear ?
+ */
+ for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+ tp = &np->target[i];
+ if (tp->to_reset ||
+ (tp->lun0p && tp->lun0p->to_clear)) {
+ target = i;
+ break;
+ }
+ if (!tp->lunmp)
+ continue;
+ for (k = 1 ; k < SYM_CONF_MAX_LUN ; k++) {
+ if (tp->lunmp[k] && tp->lunmp[k]->to_clear) {
+ target = i;
+ break;
+ }
+ }
+ if (target != -1)
+ break;
+ }
+
+ /*
+ * If not, walk the busy queue for any
+ * disconnected CCB to be aborted.
+ */
+ if (target == -1) {
+ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+ cp = sym_que_entry(qp,struct sym_ccb,link_ccbq);
+ if (cp->host_status != HS_DISCONNECT)
+ continue;
+ if (cp->to_abort) {
+ target = cp->target;
+ break;
+ }
+ }
+ }
+
+ /*
+ * If some target is to be selected,
+ * prepare and start the selection.
+ */
+ if (target != -1) {
+ tp = &np->target[target];
+ np->abrt_sel.sel_id = target;
+ np->abrt_sel.sel_scntl3 = tp->head.wval;
+ np->abrt_sel.sel_sxfer = tp->head.sval;
+ OUTL(np, nc_dsa, np->hcb_ba);
+ OUTL_DSP(np, SCRIPTB_BA(np, sel_for_abort));
+ return;
+ }
+
+ /*
+ * Now look for a CCB to abort that haven't started yet.
+ * Btw, the SCRIPTS processor is still stopped, so
+ * we are not in race.
+ */
+ i = 0;
+ cp = NULL;
+ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ if (cp->host_status != HS_BUSY &&
+ cp->host_status != HS_NEGOTIATE)
+ continue;
+ if (!cp->to_abort)
+ continue;
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If we are using IMMEDIATE ARBITRATION, we donnot
+ * want to cancel the last queued CCB, since the
+ * SCRIPTS may have anticipated the selection.
+ */
+ if (cp == np->last_cp) {
+ cp->to_abort = 0;
+ continue;
+ }
+#endif
+ i = 1; /* Means we have found some */
+ break;
+ }
+ if (!i) {
+ /*
+ * We are done, so we donnot need
+ * to synchronize with the SCRIPTS anylonger.
+ * Remove the SEM flag from the ISTAT.
+ */
+ np->istat_sem = 0;
+ OUTB(np, nc_istat, SIGP);
+ break;
+ }
+ /*
+ * Compute index of next position in the start
+ * queue the SCRIPTS intends to start and dequeue
+ * all CCBs for that device that haven't been started.
+ */
+ i = (INL(np, nc_scratcha) - np->squeue_ba) / 4;
+ i = sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1);
+
+ /*
+ * Make sure at least our IO to abort has been dequeued.
+ */
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ assert(i && sym_get_cam_status(cp->cmd) == CAM_REQUEUE_REQ);
+#else
+ sym_remque(&cp->link_ccbq);
+ sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq);
+#endif
+ /*
+ * Keep track in cam status of the reason of the abort.
+ */
+ if (cp->to_abort == 2)
+ sym_set_cam_status(cp->cmd, CAM_CMD_TIMEOUT);
+ else
+ sym_set_cam_status(cp->cmd, CAM_REQ_ABORTED);
+
+ /*
+ * Complete with error everything that we have dequeued.
+ */
+ sym_flush_comp_queue(np, 0);
+ break;
+ /*
+ * The SCRIPTS processor has selected a target
+ * we may have some manual recovery to perform for.
+ */
+ case SIR_TARGET_SELECTED:
+ target = INB(np, nc_sdid) & 0xf;
+ tp = &np->target[target];
+
+ np->abrt_tbl.addr = cpu_to_scr(vtobus(np->abrt_msg));
+
+ /*
+ * If the target is to be reset, prepare a
+ * M_RESET message and clear the to_reset flag
+ * since we donnot expect this operation to fail.
+ */
+ if (tp->to_reset) {
+ np->abrt_msg[0] = M_RESET;
+ np->abrt_tbl.size = 1;
+ tp->to_reset = 0;
+ break;
+ }
+
+ /*
+ * Otherwise, look for some logical unit to be cleared.
+ */
+ if (tp->lun0p && tp->lun0p->to_clear)
+ lun = 0;
+ else if (tp->lunmp) {
+ for (k = 1 ; k < SYM_CONF_MAX_LUN ; k++) {
+ if (tp->lunmp[k] && tp->lunmp[k]->to_clear) {
+ lun = k;
+ break;
+ }
+ }
+ }
+
+ /*
+ * If a logical unit is to be cleared, prepare
+ * an IDENTIFY(lun) + ABORT MESSAGE.
+ */
+ if (lun != -1) {
+ struct sym_lcb *lp = sym_lp(tp, lun);
+ lp->to_clear = 0; /* We don't expect to fail here */
+ np->abrt_msg[0] = IDENTIFY(0, lun);
+ np->abrt_msg[1] = M_ABORT;
+ np->abrt_tbl.size = 2;
+ break;
+ }
+
+ /*
+ * Otherwise, look for some disconnected job to
+ * abort for this target.
+ */
+ i = 0;
+ cp = NULL;
+ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ if (cp->host_status != HS_DISCONNECT)
+ continue;
+ if (cp->target != target)
+ continue;
+ if (!cp->to_abort)
+ continue;
+ i = 1; /* Means we have some */
+ break;
+ }
+
+ /*
+ * If we have none, probably since the device has
+ * completed the command before we won abitration,
+ * send a M_ABORT message without IDENTIFY.
+ * According to the specs, the device must just
+ * disconnect the BUS and not abort any task.
+ */
+ if (!i) {
+ np->abrt_msg[0] = M_ABORT;
+ np->abrt_tbl.size = 1;
+ break;
+ }
+
+ /*
+ * We have some task to abort.
+ * Set the IDENTIFY(lun)
+ */
+ np->abrt_msg[0] = IDENTIFY(0, cp->lun);
+
+ /*
+ * If we want to abort an untagged command, we
+ * will send a IDENTIFY + M_ABORT.
+ * Otherwise (tagged command), we will send
+ * a IDENTITFY + task attributes + ABORT TAG.
+ */
+ if (cp->tag == NO_TAG) {
+ np->abrt_msg[1] = M_ABORT;
+ np->abrt_tbl.size = 2;
+ } else {
+ np->abrt_msg[1] = cp->scsi_smsg[1];
+ np->abrt_msg[2] = cp->scsi_smsg[2];
+ np->abrt_msg[3] = M_ABORT_TAG;
+ np->abrt_tbl.size = 4;
+ }
+ /*
+ * Keep track of software timeout condition, since the
+ * peripheral driver may not count retries on abort
+ * conditions not due to timeout.
+ */
+ if (cp->to_abort == 2)
+ sym_set_cam_status(cp->cmd, CAM_CMD_TIMEOUT);
+ cp->to_abort = 0; /* We donnot expect to fail here */
+ break;
+
+ /*
+ * The target has accepted our message and switched
+ * to BUS FREE phase as we expected.
+ */
+ case SIR_ABORT_SENT:
+ target = INB(np, nc_sdid) & 0xf;
+ tp = &np->target[target];
+ starget = tp->sdev->sdev_target;
+
+ /*
+ ** If we didn't abort anything, leave here.
+ */
+ if (np->abrt_msg[0] == M_ABORT)
+ break;
+
+ /*
+ * If we sent a M_RESET, then a hardware reset has
+ * been performed by the target.
+ * - Reset everything to async 8 bit
+ * - Tell ourself to negotiate next time :-)
+ * - Prepare to clear all disconnected CCBs for
+ * this target from our task list (lun=task=-1)
+ */
+ lun = -1;
+ task = -1;
+ if (np->abrt_msg[0] == M_RESET) {
+ tp->head.sval = 0;
+ tp->head.wval = np->rv_scntl3;
+ tp->head.uval = 0;
+ spi_period(starget) = 0;
+ spi_offset(starget) = 0;
+ spi_width(starget) = 0;
+ spi_iu(starget) = 0;
+ spi_dt(starget) = 0;
+ spi_qas(starget) = 0;
+ tp->tgoal.check_nego = 1;
+ }
+
+ /*
+ * Otherwise, check for the LUN and TASK(s)
+ * concerned by the cancelation.
+ * If it is not ABORT_TAG then it is CLEAR_QUEUE
+ * or an ABORT message :-)
+ */
+ else {
+ lun = np->abrt_msg[0] & 0x3f;
+ if (np->abrt_msg[1] == M_ABORT_TAG)
+ task = np->abrt_msg[2];
+ }
+
+ /*
+ * Complete all the CCBs the device should have
+ * aborted due to our 'kiss of death' message.
+ */
+ i = (INL(np, nc_scratcha) - np->squeue_ba) / 4;
+ sym_dequeue_from_squeue(np, i, target, lun, -1);
+ sym_clear_tasks(np, CAM_REQ_ABORTED, target, lun, task);
+ sym_flush_comp_queue(np, 0);
+
+ /*
+ * If we sent a BDR, make upper layer aware of that.
+ */
+ if (np->abrt_msg[0] == M_RESET)
+ sym_xpt_async_sent_bdr(np, target);
+ break;
+ }
+
+ /*
+ * Print to the log the message we intend to send.
+ */
+ if (num == SIR_TARGET_SELECTED) {
+ dev_info(&tp->sdev->sdev_target->dev, "control msgout:");
+ sym_printl_hex(np->abrt_msg, np->abrt_tbl.size);
+ np->abrt_tbl.size = cpu_to_scr(np->abrt_tbl.size);
+ }
+
+ /*
+ * Let the SCRIPTS processor continue.
+ */
+ OUTONB_STD();
+}
+
+/*
+ * Gerard's alchemy:) that deals with with the data
+ * pointer for both MDP and the residual calculation.
+ *
+ * I didn't want to bloat the code by more than 200
+ * lines for the handling of both MDP and the residual.
+ * This has been achieved by using a data pointer
+ * representation consisting in an index in the data
+ * array (dp_sg) and a negative offset (dp_ofs) that
+ * have the following meaning:
+ *
+ * - dp_sg = SYM_CONF_MAX_SG
+ * we are at the end of the data script.
+ * - dp_sg < SYM_CONF_MAX_SG
+ * dp_sg points to the next entry of the scatter array
+ * we want to transfer.
+ * - dp_ofs < 0
+ * dp_ofs represents the residual of bytes of the
+ * previous entry scatter entry we will send first.
+ * - dp_ofs = 0
+ * no residual to send first.
+ *
+ * The function sym_evaluate_dp() accepts an arbitray
+ * offset (basically from the MDP message) and returns
+ * the corresponding values of dp_sg and dp_ofs.
+ */
+
+static int sym_evaluate_dp(struct sym_hcb *np, struct sym_ccb *cp, u32 scr, int *ofs)
+{
+ u32 dp_scr;
+ int dp_ofs, dp_sg, dp_sgmin;
+ int tmp;
+ struct sym_pmc *pm;
+
+ /*
+ * Compute the resulted data pointer in term of a script
+ * address within some DATA script and a signed byte offset.
+ */
+ dp_scr = scr;
+ dp_ofs = *ofs;
+ if (dp_scr == SCRIPTA_BA(np, pm0_data))
+ pm = &cp->phys.pm0;
+ else if (dp_scr == SCRIPTA_BA(np, pm1_data))
+ pm = &cp->phys.pm1;
+ else
+ pm = NULL;
+
+ if (pm) {
+ dp_scr = scr_to_cpu(pm->ret);
+ dp_ofs -= scr_to_cpu(pm->sg.size);
+ }
+
+ /*
+ * If we are auto-sensing, then we are done.
+ */
+ if (cp->host_flags & HF_SENSE) {
+ *ofs = dp_ofs;
+ return 0;
+ }
+
+ /*
+ * Deduce the index of the sg entry.
+ * Keep track of the index of the first valid entry.
+ * If result is dp_sg = SYM_CONF_MAX_SG, then we are at the
+ * end of the data.
+ */
+ tmp = scr_to_cpu(sym_goalp(cp));
+ dp_sg = SYM_CONF_MAX_SG;
+ if (dp_scr != tmp)
+ dp_sg -= (tmp - 8 - (int)dp_scr) / (2*4);
+ dp_sgmin = SYM_CONF_MAX_SG - cp->segments;
+
+ /*
+ * Move to the sg entry the data pointer belongs to.
+ *
+ * If we are inside the data area, we expect result to be:
+ *
+ * Either,
+ * dp_ofs = 0 and dp_sg is the index of the sg entry
+ * the data pointer belongs to (or the end of the data)
+ * Or,
+ * dp_ofs < 0 and dp_sg is the index of the sg entry
+ * the data pointer belongs to + 1.
+ */
+ if (dp_ofs < 0) {
+ int n;
+ while (dp_sg > dp_sgmin) {
+ --dp_sg;
+ tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+ n = dp_ofs + (tmp & 0xffffff);
+ if (n > 0) {
+ ++dp_sg;
+ break;
+ }
+ dp_ofs = n;
+ }
+ }
+ else if (dp_ofs > 0) {
+ while (dp_sg < SYM_CONF_MAX_SG) {
+ tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+ dp_ofs -= (tmp & 0xffffff);
+ ++dp_sg;
+ if (dp_ofs <= 0)
+ break;
+ }
+ }
+
+ /*
+ * Make sure the data pointer is inside the data area.
+ * If not, return some error.
+ */
+ if (dp_sg < dp_sgmin || (dp_sg == dp_sgmin && dp_ofs < 0))
+ goto out_err;
+ else if (dp_sg > SYM_CONF_MAX_SG ||
+ (dp_sg == SYM_CONF_MAX_SG && dp_ofs > 0))
+ goto out_err;
+
+ /*
+ * Save the extreme pointer if needed.
+ */
+ if (dp_sg > cp->ext_sg ||
+ (dp_sg == cp->ext_sg && dp_ofs > cp->ext_ofs)) {
+ cp->ext_sg = dp_sg;
+ cp->ext_ofs = dp_ofs;
+ }
+
+ /*
+ * Return data.
+ */
+ *ofs = dp_ofs;
+ return dp_sg;
+
+out_err:
+ return -1;
+}
+
+/*
+ * chip handler for MODIFY DATA POINTER MESSAGE
+ *
+ * We also call this function on IGNORE WIDE RESIDUE
+ * messages that do not match a SWIDE full condition.
+ * Btw, we assume in that situation that such a message
+ * is equivalent to a MODIFY DATA POINTER (offset=-1).
+ */
+
+static void sym_modify_dp(struct sym_hcb *np, struct sym_tcb *tp, struct sym_ccb *cp, int ofs)
+{
+ int dp_ofs = ofs;
+ u32 dp_scr = sym_get_script_dp (np, cp);
+ u32 dp_ret;
+ u32 tmp;
+ u_char hflags;
+ int dp_sg;
+ struct sym_pmc *pm;
+
+ /*
+ * Not supported for auto-sense.
+ */
+ if (cp->host_flags & HF_SENSE)
+ goto out_reject;
+
+ /*
+ * Apply our alchemy:) (see comments in sym_evaluate_dp()),
+ * to the resulted data pointer.
+ */
+ dp_sg = sym_evaluate_dp(np, cp, dp_scr, &dp_ofs);
+ if (dp_sg < 0)
+ goto out_reject;
+
+ /*
+ * And our alchemy:) allows to easily calculate the data
+ * script address we want to return for the next data phase.
+ */
+ dp_ret = cpu_to_scr(sym_goalp(cp));
+ dp_ret = dp_ret - 8 - (SYM_CONF_MAX_SG - dp_sg) * (2*4);
+
+ /*
+ * If offset / scatter entry is zero we donnot need
+ * a context for the new current data pointer.
+ */
+ if (dp_ofs == 0) {
+ dp_scr = dp_ret;
+ goto out_ok;
+ }
+
+ /*
+ * Get a context for the new current data pointer.
+ */
+ hflags = INB(np, HF_PRT);
+
+ if (hflags & HF_DP_SAVED)
+ hflags ^= HF_ACT_PM;
+
+ if (!(hflags & HF_ACT_PM)) {
+ pm = &cp->phys.pm0;
+ dp_scr = SCRIPTA_BA(np, pm0_data);
+ }
+ else {
+ pm = &cp->phys.pm1;
+ dp_scr = SCRIPTA_BA(np, pm1_data);
+ }
+
+ hflags &= ~(HF_DP_SAVED);
+
+ OUTB(np, HF_PRT, hflags);
+
+ /*
+ * Set up the new current data pointer.
+ * ofs < 0 there, and for the next data phase, we
+ * want to transfer part of the data of the sg entry
+ * corresponding to index dp_sg-1 prior to returning
+ * to the main data script.
+ */
+ pm->ret = cpu_to_scr(dp_ret);
+ tmp = scr_to_cpu(cp->phys.data[dp_sg-1].addr);
+ tmp += scr_to_cpu(cp->phys.data[dp_sg-1].size) + dp_ofs;
+ pm->sg.addr = cpu_to_scr(tmp);
+ pm->sg.size = cpu_to_scr(-dp_ofs);
+
+out_ok:
+ sym_set_script_dp (np, cp, dp_scr);
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+ return;
+
+out_reject:
+ OUTL_DSP(np, SCRIPTB_BA(np, msg_bad));
+}
+
+
+/*
+ * chip calculation of the data residual.
+ *
+ * As I used to say, the requirement of data residual
+ * in SCSI is broken, useless and cannot be achieved
+ * without huge complexity.
+ * But most OSes and even the official CAM require it.
+ * When stupidity happens to be so widely spread inside
+ * a community, it gets hard to convince.
+ *
+ * Anyway, I don't care, since I am not going to use
+ * any software that considers this data residual as
+ * a relevant information. :)
+ */
+
+int sym_compute_residual(struct sym_hcb *np, struct sym_ccb *cp)
+{
+ int dp_sg, dp_sgmin, resid = 0;
+ int dp_ofs = 0;
+
+ /*
+ * Check for some data lost or just thrown away.
+ * We are not required to be quite accurate in this
+ * situation. Btw, if we are odd for output and the
+ * device claims some more data, it may well happen
+ * than our residual be zero. :-)
+ */
+ if (cp->xerr_status & (XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN)) {
+ if (cp->xerr_status & XE_EXTRA_DATA)
+ resid -= cp->extra_bytes;
+ if (cp->xerr_status & XE_SODL_UNRUN)
+ ++resid;
+ if (cp->xerr_status & XE_SWIDE_OVRUN)
+ --resid;
+ }
+
+ /*
+ * If all data has been transferred,
+ * there is no residual.
+ */
+ if (cp->phys.head.lastp == sym_goalp(cp))
+ return resid;
+
+ /*
+ * If no data transfer occurs, or if the data
+ * pointer is weird, return full residual.
+ */
+ if (cp->startp == cp->phys.head.lastp ||
+ sym_evaluate_dp(np, cp, scr_to_cpu(cp->phys.head.lastp),
+ &dp_ofs) < 0) {
+ return cp->data_len;
+ }
+
+ /*
+ * If we were auto-sensing, then we are done.
+ */
+ if (cp->host_flags & HF_SENSE) {
+ return -dp_ofs;
+ }
+
+ /*
+ * We are now full comfortable in the computation
+ * of the data residual (2's complement).
+ */
+ dp_sgmin = SYM_CONF_MAX_SG - cp->segments;
+ resid = -cp->ext_ofs;
+ for (dp_sg = cp->ext_sg; dp_sg < SYM_CONF_MAX_SG; ++dp_sg) {
+ u_int tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+ resid += (tmp & 0xffffff);
+ }
+
+ /*
+ * Hopefully, the result is not too wrong.
+ */
+ return resid;
+}
+
+/*
+ * Negotiation for WIDE and SYNCHRONOUS DATA TRANSFER.
+ *
+ * When we try to negotiate, we append the negotiation message
+ * to the identify and (maybe) simple tag message.
+ * The host status field is set to HS_NEGOTIATE to mark this
+ * situation.
+ *
+ * If the target doesn't answer this message immediately
+ * (as required by the standard), the SIR_NEGO_FAILED interrupt
+ * will be raised eventually.
+ * The handler removes the HS_NEGOTIATE status, and sets the
+ * negotiated value to the default (async / nowide).
+ *
+ * If we receive a matching answer immediately, we check it
+ * for validity, and set the values.
+ *
+ * If we receive a Reject message immediately, we assume the
+ * negotiation has failed, and fall back to standard values.
+ *
+ * If we receive a negotiation message while not in HS_NEGOTIATE
+ * state, it's a target initiated negotiation. We prepare a
+ * (hopefully) valid answer, set our parameters, and send back
+ * this answer to the target.
+ *
+ * If the target doesn't fetch the answer (no message out phase),
+ * we assume the negotiation has failed, and fall back to default
+ * settings (SIR_NEGO_PROTO interrupt).
+ *
+ * When we set the values, we adjust them in all ccbs belonging
+ * to this target, in the controller's register, and in the "phys"
+ * field of the controller's struct sym_hcb.
+ */
+
+/*
+ * chip handler for SYNCHRONOUS DATA TRANSFER REQUEST (SDTR) message.
+ */
+static int
+sym_sync_nego_check(struct sym_hcb *np, int req, struct sym_ccb *cp)
+{
+ int target = cp->target;
+ u_char chg, ofs, per, fak, div;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, target, "sync msgin", np->msgin);
+ }
+
+ /*
+ * Get requested values.
+ */
+ chg = 0;
+ per = np->msgin[3];
+ ofs = np->msgin[4];
+
+ /*
+ * Check values against our limits.
+ */
+ if (ofs) {
+ if (ofs > np->maxoffs)
+ {chg = 1; ofs = np->maxoffs;}
+ }
+
+ if (ofs) {
+ if (per < np->minsync)
+ {chg = 1; per = np->minsync;}
+ }
+
+ /*
+ * Get new chip synchronous parameters value.
+ */
+ div = fak = 0;
+ if (ofs && sym_getsync(np, 0, per, &div, &fak) < 0)
+ goto reject_it;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_addr(cp->cmd,
+ "sdtr: ofs=%d per=%d div=%d fak=%d chg=%d.\n",
+ ofs, per, div, fak, chg);
+ }
+
+ /*
+ * If it was an answer we want to change,
+ * then it isn't acceptable. Reject it.
+ */
+ if (!req && chg)
+ goto reject_it;
+
+ /*
+ * Apply new values.
+ */
+ sym_setsync (np, target, ofs, per, div, fak);
+
+ /*
+ * It was an answer. We are done.
+ */
+ if (!req)
+ return 0;
+
+ /*
+ * It was a request. Prepare an answer message.
+ */
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 3;
+ np->msgout[2] = M_X_SYNC_REQ;
+ np->msgout[3] = per;
+ np->msgout[4] = ofs;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, target, "sync msgout", np->msgout);
+ }
+
+ np->msgin [0] = M_NOOP;
+
+ return 0;
+
+reject_it:
+ sym_setsync (np, target, 0, 0, 0, 0);
+ return -1;
+}
+
+static void sym_sync_nego(struct sym_hcb *np, struct sym_tcb *tp, struct sym_ccb *cp)
+{
+ int req = 1;
+ int result;
+
+ /*
+ * Request or answer ?
+ */
+ if (INB(np, HS_PRT) == HS_NEGOTIATE) {
+ OUTB(np, HS_PRT, HS_BUSY);
+ if (cp->nego_status && cp->nego_status != NS_SYNC)
+ goto reject_it;
+ req = 0;
+ }
+
+ /*
+ * Check and apply new values.
+ */
+ result = sym_sync_nego_check(np, req, cp);
+ if (result) /* Not acceptable, reject it */
+ goto reject_it;
+ if (req) { /* Was a request, send response. */
+ cp->nego_status = NS_SYNC;
+ OUTL_DSP(np, SCRIPTB_BA(np, sdtr_resp));
+ }
+ else /* Was a response, we are done. */
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+ return;
+
+reject_it:
+ OUTL_DSP(np, SCRIPTB_BA(np, msg_bad));
+}
+
+/*
+ * chip handler for PARALLEL PROTOCOL REQUEST (PPR) message.
+ */
+static int
+sym_ppr_nego_check(struct sym_hcb *np, int req, int target)
+{
+ struct sym_tcb *tp = &np->target[target];
+ unsigned char fak, div;
+ int dt, chg = 0;
+
+ unsigned char per = np->msgin[3];
+ unsigned char ofs = np->msgin[5];
+ unsigned char wide = np->msgin[6];
+ unsigned char opts = np->msgin[7] & PPR_OPT_MASK;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, target, "ppr msgin", np->msgin);
+ }
+
+ /*
+ * Check values against our limits.
+ */
+ if (wide > np->maxwide) {
+ chg = 1;
+ wide = np->maxwide;
+ }
+ if (!wide || !(np->features & FE_U3EN))
+ opts = 0;
+
+ if (opts != (np->msgin[7] & PPR_OPT_MASK))
+ chg = 1;
+
+ dt = opts & PPR_OPT_DT;
+
+ if (ofs) {
+ unsigned char maxoffs = dt ? np->maxoffs_dt : np->maxoffs;
+ if (ofs > maxoffs) {
+ chg = 1;
+ ofs = maxoffs;
+ }
+ }
+
+ if (ofs) {
+ unsigned char minsync = dt ? np->minsync_dt : np->minsync;
+ if (per < minsync) {
+ chg = 1;
+ per = minsync;
+ }
+ }
+
+ /*
+ * Get new chip synchronous parameters value.
+ */
+ div = fak = 0;
+ if (ofs && sym_getsync(np, dt, per, &div, &fak) < 0)
+ goto reject_it;
+
+ /*
+ * If it was an answer we want to change,
+ * then it isn't acceptable. Reject it.
+ */
+ if (!req && chg)
+ goto reject_it;
+
+ /*
+ * Apply new values.
+ */
+ sym_setpprot(np, target, opts, ofs, per, wide, div, fak);
+
+ /*
+ * It was an answer. We are done.
+ */
+ if (!req)
+ return 0;
+
+ /*
+ * It was a request. Prepare an answer message.
+ */
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 6;
+ np->msgout[2] = M_X_PPR_REQ;
+ np->msgout[3] = per;
+ np->msgout[4] = 0;
+ np->msgout[5] = ofs;
+ np->msgout[6] = wide;
+ np->msgout[7] = opts;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, target, "ppr msgout", np->msgout);
+ }
+
+ np->msgin [0] = M_NOOP;
+
+ return 0;
+
+reject_it:
+ sym_setpprot (np, target, 0, 0, 0, 0, 0, 0);
+ /*
+ * If it is a device response that should result in
+ * ST, we may want to try a legacy negotiation later.
+ */
+ if (!req && !opts) {
+ tp->tgoal.period = per;
+ tp->tgoal.offset = ofs;
+ tp->tgoal.width = wide;
+ tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0;
+ tp->tgoal.check_nego = 1;
+ }
+ return -1;
+}
+
+static void sym_ppr_nego(struct sym_hcb *np, struct sym_tcb *tp, struct sym_ccb *cp)
+{
+ int req = 1;
+ int result;
+
+ /*
+ * Request or answer ?
+ */
+ if (INB(np, HS_PRT) == HS_NEGOTIATE) {
+ OUTB(np, HS_PRT, HS_BUSY);
+ if (cp->nego_status && cp->nego_status != NS_PPR)
+ goto reject_it;
+ req = 0;
+ }
+
+ /*
+ * Check and apply new values.
+ */
+ result = sym_ppr_nego_check(np, req, cp->target);
+ if (result) /* Not acceptable, reject it */
+ goto reject_it;
+ if (req) { /* Was a request, send response. */
+ cp->nego_status = NS_PPR;
+ OUTL_DSP(np, SCRIPTB_BA(np, ppr_resp));
+ }
+ else /* Was a response, we are done. */
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+ return;
+
+reject_it:
+ OUTL_DSP(np, SCRIPTB_BA(np, msg_bad));
+}
+
+/*
+ * chip handler for WIDE DATA TRANSFER REQUEST (WDTR) message.
+ */
+static int
+sym_wide_nego_check(struct sym_hcb *np, int req, struct sym_ccb *cp)
+{
+ int target = cp->target;
+ u_char chg, wide;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, target, "wide msgin", np->msgin);
+ }
+
+ /*
+ * Get requested values.
+ */
+ chg = 0;
+ wide = np->msgin[3];
+
+ /*
+ * Check values against our limits.
+ */
+ if (wide > np->maxwide) {
+ chg = 1;
+ wide = np->maxwide;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_addr(cp->cmd, "wdtr: wide=%d chg=%d.\n",
+ wide, chg);
+ }
+
+ /*
+ * If it was an answer we want to change,
+ * then it isn't acceptable. Reject it.
+ */
+ if (!req && chg)
+ goto reject_it;
+
+ /*
+ * Apply new values.
+ */
+ sym_setwide (np, target, wide);
+
+ /*
+ * It was an answer. We are done.
+ */
+ if (!req)
+ return 0;
+
+ /*
+ * It was a request. Prepare an answer message.
+ */
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 2;
+ np->msgout[2] = M_X_WIDE_REQ;
+ np->msgout[3] = wide;
+
+ np->msgin [0] = M_NOOP;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, target, "wide msgout", np->msgout);
+ }
+
+ return 0;
+
+reject_it:
+ return -1;
+}
+
+static void sym_wide_nego(struct sym_hcb *np, struct sym_tcb *tp, struct sym_ccb *cp)
+{
+ int req = 1;
+ int result;
+
+ /*
+ * Request or answer ?
+ */
+ if (INB(np, HS_PRT) == HS_NEGOTIATE) {
+ OUTB(np, HS_PRT, HS_BUSY);
+ if (cp->nego_status && cp->nego_status != NS_WIDE)
+ goto reject_it;
+ req = 0;
+ }
+
+ /*
+ * Check and apply new values.
+ */
+ result = sym_wide_nego_check(np, req, cp);
+ if (result) /* Not acceptable, reject it */
+ goto reject_it;
+ if (req) { /* Was a request, send response. */
+ cp->nego_status = NS_WIDE;
+ OUTL_DSP(np, SCRIPTB_BA(np, wdtr_resp));
+ } else { /* Was a response. */
+ /*
+ * Negotiate for SYNC immediately after WIDE response.
+ * This allows to negotiate for both WIDE and SYNC on
+ * a single SCSI command (Suggested by Justin Gibbs).
+ */
+ if (tp->tgoal.offset) {
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 3;
+ np->msgout[2] = M_X_SYNC_REQ;
+ np->msgout[3] = tp->tgoal.period;
+ np->msgout[4] = tp->tgoal.offset;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ sym_print_nego_msg(np, cp->target,
+ "sync msgout", np->msgout);
+ }
+
+ cp->nego_status = NS_SYNC;
+ OUTB(np, HS_PRT, HS_NEGOTIATE);
+ OUTL_DSP(np, SCRIPTB_BA(np, sdtr_resp));
+ return;
+ } else
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+ }
+
+ return;
+
+reject_it:
+ OUTL_DSP(np, SCRIPTB_BA(np, msg_bad));
+}
+
+/*
+ * Reset DT, SYNC or WIDE to default settings.
+ *
+ * Called when a negotiation does not succeed either
+ * on rejection or on protocol error.
+ *
+ * A target that understands a PPR message should never
+ * reject it, and messing with it is very unlikely.
+ * So, if a PPR makes problems, we may just want to
+ * try a legacy negotiation later.
+ */
+static void sym_nego_default(struct sym_hcb *np, struct sym_tcb *tp, struct sym_ccb *cp)
+{
+ switch (cp->nego_status) {
+ case NS_PPR:
+#if 0
+ sym_setpprot (np, cp->target, 0, 0, 0, 0, 0, 0);
+#else
+ if (tp->tgoal.period < np->minsync)
+ tp->tgoal.period = np->minsync;
+ if (tp->tgoal.offset > np->maxoffs)
+ tp->tgoal.offset = np->maxoffs;
+ tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0;
+ tp->tgoal.check_nego = 1;
+#endif
+ break;
+ case NS_SYNC:
+ sym_setsync (np, cp->target, 0, 0, 0, 0);
+ break;
+ case NS_WIDE:
+ sym_setwide (np, cp->target, 0);
+ break;
+ }
+ np->msgin [0] = M_NOOP;
+ np->msgout[0] = M_NOOP;
+ cp->nego_status = 0;
+}
+
+/*
+ * chip handler for MESSAGE REJECT received in response to
+ * PPR, WIDE or SYNCHRONOUS negotiation.
+ */
+static void sym_nego_rejected(struct sym_hcb *np, struct sym_tcb *tp, struct sym_ccb *cp)
+{
+ sym_nego_default(np, tp, cp);
+ OUTB(np, HS_PRT, HS_BUSY);
+}
+
+/*
+ * chip exception handler for programmed interrupts.
+ */
+static void sym_int_sir (struct sym_hcb *np)
+{
+ u_char num = INB(np, nc_dsps);
+ u32 dsa = INL(np, nc_dsa);
+ struct sym_ccb *cp = sym_ccb_from_dsa(np, dsa);
+ u_char target = INB(np, nc_sdid) & 0x0f;
+ struct sym_tcb *tp = &np->target[target];
+ int tmp;
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
+
+ switch (num) {
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+ /*
+ * SCRIPTS tell us that we may have to update
+ * 64 bit DMA segment registers.
+ */
+ case SIR_DMAP_DIRTY:
+ sym_update_dmap_regs(np);
+ goto out;
+#endif
+ /*
+ * Command has been completed with error condition
+ * or has been auto-sensed.
+ */
+ case SIR_COMPLETE_ERROR:
+ sym_complete_error(np, cp);
+ return;
+ /*
+ * The C code is currently trying to recover from something.
+ * Typically, user want to abort some command.
+ */
+ case SIR_SCRIPT_STOPPED:
+ case SIR_TARGET_SELECTED:
+ case SIR_ABORT_SENT:
+ sym_sir_task_recovery(np, num);
+ return;
+ /*
+ * The device didn't go to MSG OUT phase after having
+ * been selected with ATN. We donnot want to handle
+ * that.
+ */
+ case SIR_SEL_ATN_NO_MSG_OUT:
+ printf ("%s:%d: No MSG OUT phase after selection with ATN.\n",
+ sym_name (np), target);
+ goto out_stuck;
+ /*
+ * The device didn't switch to MSG IN phase after
+ * having reseleted the initiator.
+ */
+ case SIR_RESEL_NO_MSG_IN:
+ printf ("%s:%d: No MSG IN phase after reselection.\n",
+ sym_name (np), target);
+ goto out_stuck;
+ /*
+ * After reselection, the device sent a message that wasn't
+ * an IDENTIFY.
+ */
+ case SIR_RESEL_NO_IDENTIFY:
+ printf ("%s:%d: No IDENTIFY after reselection.\n",
+ sym_name (np), target);
+ goto out_stuck;
+ /*
+ * The device reselected a LUN we donnot know about.
+ */
+ case SIR_RESEL_BAD_LUN:
+ np->msgout[0] = M_RESET;
+ goto out;
+ /*
+ * The device reselected for an untagged nexus and we
+ * haven't any.
+ */
+ case SIR_RESEL_BAD_I_T_L:
+ np->msgout[0] = M_ABORT;
+ goto out;
+ /*
+ * The device reselected for a tagged nexus that we donnot
+ * have.
+ */
+ case SIR_RESEL_BAD_I_T_L_Q:
+ np->msgout[0] = M_ABORT_TAG;
+ goto out;
+ /*
+ * The SCRIPTS let us know that the device has grabbed
+ * our message and will abort the job.
+ */
+ case SIR_RESEL_ABORTED:
+ np->lastmsg = np->msgout[0];
+ np->msgout[0] = M_NOOP;
+ printf ("%s:%d: message %x sent on bad reselection.\n",
+ sym_name (np), target, np->lastmsg);
+ goto out;
+ /*
+ * The SCRIPTS let us know that a message has been
+ * successfully sent to the device.
+ */
+ case SIR_MSG_OUT_DONE:
+ np->lastmsg = np->msgout[0];
+ np->msgout[0] = M_NOOP;
+ /* Should we really care of that */
+ if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR) {
+ if (cp) {
+ cp->xerr_status &= ~XE_PARITY_ERR;
+ if (!cp->xerr_status)
+ OUTOFFB(np, HF_PRT, HF_EXT_ERR);
+ }
+ }
+ goto out;
+ /*
+ * The device didn't send a GOOD SCSI status.
+ * We may have some work to do prior to allow
+ * the SCRIPTS processor to continue.
+ */
+ case SIR_BAD_SCSI_STATUS:
+ if (!cp)
+ goto out;
+ sym_sir_bad_scsi_status(np, num, cp);
+ return;
+ /*
+ * We are asked by the SCRIPTS to prepare a
+ * REJECT message.
+ */
+ case SIR_REJECT_TO_SEND:
+ sym_print_msg(cp, "M_REJECT to send for ", np->msgin);
+ np->msgout[0] = M_REJECT;
+ goto out;
+ /*
+ * We have been ODD at the end of a DATA IN
+ * transfer and the device didn't send a
+ * IGNORE WIDE RESIDUE message.
+ * It is a data overrun condition.
+ */
+ case SIR_SWIDE_OVERRUN:
+ if (cp) {
+ OUTONB(np, HF_PRT, HF_EXT_ERR);
+ cp->xerr_status |= XE_SWIDE_OVRUN;
+ }
+ goto out;
+ /*
+ * We have been ODD at the end of a DATA OUT
+ * transfer.
+ * It is a data underrun condition.
+ */
+ case SIR_SODL_UNDERRUN:
+ if (cp) {
+ OUTONB(np, HF_PRT, HF_EXT_ERR);
+ cp->xerr_status |= XE_SODL_UNRUN;
+ }
+ goto out;
+ /*
+ * The device wants us to tranfer more data than
+ * expected or in the wrong direction.
+ * The number of extra bytes is in scratcha.
+ * It is a data overrun condition.
+ */
+ case SIR_DATA_OVERRUN:
+ if (cp) {
+ OUTONB(np, HF_PRT, HF_EXT_ERR);
+ cp->xerr_status |= XE_EXTRA_DATA;
+ cp->extra_bytes += INL(np, nc_scratcha);
+ }
+ goto out;
+ /*
+ * The device switched to an illegal phase (4/5).
+ */
+ case SIR_BAD_PHASE:
+ if (cp) {
+ OUTONB(np, HF_PRT, HF_EXT_ERR);
+ cp->xerr_status |= XE_BAD_PHASE;
+ }
+ goto out;
+ /*
+ * We received a message.
+ */
+ case SIR_MSG_RECEIVED:
+ if (!cp)
+ goto out_stuck;
+ switch (np->msgin [0]) {
+ /*
+ * We received an extended message.
+ * We handle MODIFY DATA POINTER, SDTR, WDTR
+ * and reject all other extended messages.
+ */
+ case M_EXTENDED:
+ switch (np->msgin [2]) {
+ case M_X_MODIFY_DP:
+ if (DEBUG_FLAGS & DEBUG_POINTER)
+ sym_print_msg(cp,"modify DP",np->msgin);
+ tmp = (np->msgin[3]<<24) + (np->msgin[4]<<16) +
+ (np->msgin[5]<<8) + (np->msgin[6]);
+ sym_modify_dp(np, tp, cp, tmp);
+ return;
+ case M_X_SYNC_REQ:
+ sym_sync_nego(np, tp, cp);
+ return;
+ case M_X_PPR_REQ:
+ sym_ppr_nego(np, tp, cp);
+ return;
+ case M_X_WIDE_REQ:
+ sym_wide_nego(np, tp, cp);
+ return;
+ default:
+ goto out_reject;
+ }
+ break;
+ /*
+ * We received a 1/2 byte message not handled from SCRIPTS.
+ * We are only expecting MESSAGE REJECT and IGNORE WIDE
+ * RESIDUE messages that haven't been anticipated by
+ * SCRIPTS on SWIDE full condition. Unanticipated IGNORE
+ * WIDE RESIDUE messages are aliased as MODIFY DP (-1).
+ */
+ case M_IGN_RESIDUE:
+ if (DEBUG_FLAGS & DEBUG_POINTER)
+ sym_print_msg(cp,"ign wide residue", np->msgin);
+ if (cp->host_flags & HF_SENSE)
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+ else
+ sym_modify_dp(np, tp, cp, -1);
+ return;
+ case M_REJECT:
+ if (INB(np, HS_PRT) == HS_NEGOTIATE)
+ sym_nego_rejected(np, tp, cp);
+ else {
+ sym_print_addr(cp->cmd,
+ "M_REJECT received (%x:%x).\n",
+ scr_to_cpu(np->lastmsg), np->msgout[0]);
+ }
+ goto out_clrack;
+ break;
+ default:
+ goto out_reject;
+ }
+ break;
+ /*
+ * We received an unknown message.
+ * Ignore all MSG IN phases and reject it.
+ */
+ case SIR_MSG_WEIRD:
+ sym_print_msg(cp, "WEIRD message received", np->msgin);
+ OUTL_DSP(np, SCRIPTB_BA(np, msg_weird));
+ return;
+ /*
+ * Negotiation failed.
+ * Target does not send us the reply.
+ * Remove the HS_NEGOTIATE status.
+ */
+ case SIR_NEGO_FAILED:
+ OUTB(np, HS_PRT, HS_BUSY);
+ /*
+ * Negotiation failed.
+ * Target does not want answer message.
+ */
+ case SIR_NEGO_PROTO:
+ sym_nego_default(np, tp, cp);
+ goto out;
+ }
+
+out:
+ OUTONB_STD();
+ return;
+out_reject:
+ OUTL_DSP(np, SCRIPTB_BA(np, msg_bad));
+ return;
+out_clrack:
+ OUTL_DSP(np, SCRIPTA_BA(np, clrack));
+ return;
+out_stuck:
+ return;
+}
+
+/*
+ * Acquire a control block
+ */
+struct sym_ccb *sym_get_ccb (struct sym_hcb *np, struct scsi_cmnd *cmd, u_char tag_order)
+{
+ u_char tn = cmd->device->id;
+ u_char ln = cmd->device->lun;
+ struct sym_tcb *tp = &np->target[tn];
+ struct sym_lcb *lp = sym_lp(tp, ln);
+ u_short tag = NO_TAG;
+ SYM_QUEHEAD *qp;
+ struct sym_ccb *cp = NULL;
+
+ /*
+ * Look for a free CCB
+ */
+ if (sym_que_empty(&np->free_ccbq))
+ sym_alloc_ccb(np);
+ qp = sym_remque_head(&np->free_ccbq);
+ if (!qp)
+ goto out;
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ /*
+ * If the LCB is not yet available and the LUN
+ * has been probed ok, try to allocate the LCB.
+ */
+ if (!lp && sym_is_bit(tp->lun_map, ln)) {
+ lp = sym_alloc_lcb(np, tn, ln);
+ if (!lp)
+ goto out_free;
+ }
+#endif
+
+ /*
+ * If the LCB is not available here, then the
+ * logical unit is not yet discovered. For those
+ * ones only accept 1 SCSI IO per logical unit,
+ * since we cannot allow disconnections.
+ */
+ if (!lp) {
+ if (!sym_is_bit(tp->busy0_map, ln))
+ sym_set_bit(tp->busy0_map, ln);
+ else
+ goto out_free;
+ } else {
+ /*
+ * If we have been asked for a tagged command.
+ */
+ if (tag_order) {
+ /*
+ * Debugging purpose.
+ */
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ assert(lp->busy_itl == 0);
+#endif
+ /*
+ * Allocate resources for tags if not yet.
+ */
+ if (!lp->cb_tags) {
+ sym_alloc_lcb_tags(np, tn, ln);
+ if (!lp->cb_tags)
+ goto out_free;
+ }
+ /*
+ * Get a tag for this SCSI IO and set up
+ * the CCB bus address for reselection,
+ * and count it for this LUN.
+ * Toggle reselect path to tagged.
+ */
+ if (lp->busy_itlq < SYM_CONF_MAX_TASK) {
+ tag = lp->cb_tags[lp->ia_tag];
+ if (++lp->ia_tag == SYM_CONF_MAX_TASK)
+ lp->ia_tag = 0;
+ ++lp->busy_itlq;
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ lp->itlq_tbl[tag] = cpu_to_scr(cp->ccb_ba);
+ lp->head.resel_sa =
+ cpu_to_scr(SCRIPTA_BA(np, resel_tag));
+#endif
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+ cp->tags_si = lp->tags_si;
+ ++lp->tags_sum[cp->tags_si];
+ ++lp->tags_since;
+#endif
+ }
+ else
+ goto out_free;
+ }
+ /*
+ * This command will not be tagged.
+ * If we already have either a tagged or untagged
+ * one, refuse to overlap this untagged one.
+ */
+ else {
+ /*
+ * Debugging purpose.
+ */
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ assert(lp->busy_itl == 0 && lp->busy_itlq == 0);
+#endif
+ /*
+ * Count this nexus for this LUN.
+ * Set up the CCB bus address for reselection.
+ * Toggle reselect path to untagged.
+ */
+ ++lp->busy_itl;
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ if (lp->busy_itl == 1) {
+ lp->head.itl_task_sa = cpu_to_scr(cp->ccb_ba);
+ lp->head.resel_sa =
+ cpu_to_scr(SCRIPTA_BA(np, resel_no_tag));
+ }
+ else
+ goto out_free;
+#endif
+ }
+ }
+ /*
+ * Put the CCB into the busy queue.
+ */
+ sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq);
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ if (lp) {
+ sym_remque(&cp->link2_ccbq);
+ sym_insque_tail(&cp->link2_ccbq, &lp->waiting_ccbq);
+ }
+
+#endif
+ /*
+ * Remember all informations needed to free this CCB.
+ */
+ cp->to_abort = 0;
+ cp->tag = tag;
+ cp->order = tag_order;
+ cp->target = tn;
+ cp->lun = ln;
+
+ if (DEBUG_FLAGS & DEBUG_TAGS) {
+ sym_print_addr(cmd, "ccb @%p using tag %d.\n", cp, tag);
+ }
+
+out:
+ return cp;
+out_free:
+ sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
+ return NULL;
+}
+
+/*
+ * Release one control block
+ */
+void sym_free_ccb (struct sym_hcb *np, struct sym_ccb *cp)
+{
+ struct sym_tcb *tp = &np->target[cp->target];
+ struct sym_lcb *lp = sym_lp(tp, cp->lun);
+
+ if (DEBUG_FLAGS & DEBUG_TAGS) {
+ sym_print_addr(cp->cmd, "ccb @%p freeing tag %d.\n",
+ cp, cp->tag);
+ }
+
+ /*
+ * If LCB available,
+ */
+ if (lp) {
+ /*
+ * If tagged, release the tag, set the relect path
+ */
+ if (cp->tag != NO_TAG) {
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+ --lp->tags_sum[cp->tags_si];
+#endif
+ /*
+ * Free the tag value.
+ */
+ lp->cb_tags[lp->if_tag] = cp->tag;
+ if (++lp->if_tag == SYM_CONF_MAX_TASK)
+ lp->if_tag = 0;
+ /*
+ * Make the reselect path invalid,
+ * and uncount this CCB.
+ */
+ lp->itlq_tbl[cp->tag] = cpu_to_scr(np->bad_itlq_ba);
+ --lp->busy_itlq;
+ } else { /* Untagged */
+ /*
+ * Make the reselect path invalid,
+ * and uncount this CCB.
+ */
+ lp->head.itl_task_sa = cpu_to_scr(np->bad_itl_ba);
+ --lp->busy_itl;
+ }
+ /*
+ * If no JOB active, make the LUN reselect path invalid.
+ */
+ if (lp->busy_itlq == 0 && lp->busy_itl == 0)
+ lp->head.resel_sa =
+ cpu_to_scr(SCRIPTB_BA(np, resel_bad_lun));
+ }
+ /*
+ * Otherwise, we only accept 1 IO per LUN.
+ * Clear the bit that keeps track of this IO.
+ */
+ else
+ sym_clr_bit(tp->busy0_map, cp->lun);
+
+ /*
+ * We donnot queue more than 1 ccb per target
+ * with negotiation at any time. If this ccb was
+ * used for negotiation, clear this info in the tcb.
+ */
+ if (cp == tp->nego_cp)
+ tp->nego_cp = NULL;
+
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If we just complete the last queued CCB,
+ * clear this info that is no longer relevant.
+ */
+ if (cp == np->last_cp)
+ np->last_cp = 0;
+#endif
+
+ /*
+ * Make this CCB available.
+ */
+ cp->cmd = NULL;
+ cp->host_status = HS_IDLE;
+ sym_remque(&cp->link_ccbq);
+ sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ if (lp) {
+ sym_remque(&cp->link2_ccbq);
+ sym_insque_tail(&cp->link2_ccbq, &np->dummy_ccbq);
+ if (cp->started) {
+ if (cp->tag != NO_TAG)
+ --lp->started_tags;
+ else
+ --lp->started_no_tag;
+ }
+ }
+ cp->started = 0;
+#endif
+}
+
+/*
+ * Allocate a CCB from memory and initialize its fixed part.
+ */
+static struct sym_ccb *sym_alloc_ccb(struct sym_hcb *np)
+{
+ struct sym_ccb *cp = NULL;
+ int hcode;
+
+ /*
+ * Prevent from allocating more CCBs than we can
+ * queue to the controller.
+ */
+ if (np->actccbs >= SYM_CONF_MAX_START)
+ return NULL;
+
+ /*
+ * Allocate memory for this CCB.
+ */
+ cp = sym_calloc_dma(sizeof(struct sym_ccb), "CCB");
+ if (!cp)
+ goto out_free;
+
+ /*
+ * Count it.
+ */
+ np->actccbs++;
+
+ /*
+ * Compute the bus address of this ccb.
+ */
+ cp->ccb_ba = vtobus(cp);
+
+ /*
+ * Insert this ccb into the hashed list.
+ */
+ hcode = CCB_HASH_CODE(cp->ccb_ba);
+ cp->link_ccbh = np->ccbh[hcode];
+ np->ccbh[hcode] = cp;
+
+ /*
+ * Initialyze the start and restart actions.
+ */
+ cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA(np, idle));
+ cp->phys.head.go.restart = cpu_to_scr(SCRIPTB_BA(np, bad_i_t_l));
+
+ /*
+ * Initilialyze some other fields.
+ */
+ cp->phys.smsg_ext.addr = cpu_to_scr(HCB_BA(np, msgin[2]));
+
+ /*
+ * Chain into free ccb queue.
+ */
+ sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
+
+ /*
+ * Chain into optionnal lists.
+ */
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ sym_insque_head(&cp->link2_ccbq, &np->dummy_ccbq);
+#endif
+ return cp;
+out_free:
+ if (cp)
+ sym_mfree_dma(cp, sizeof(*cp), "CCB");
+ return NULL;
+}
+
+/*
+ * Look up a CCB from a DSA value.
+ */
+static struct sym_ccb *sym_ccb_from_dsa(struct sym_hcb *np, u32 dsa)
+{
+ int hcode;
+ struct sym_ccb *cp;
+
+ hcode = CCB_HASH_CODE(dsa);
+ cp = np->ccbh[hcode];
+ while (cp) {
+ if (cp->ccb_ba == dsa)
+ break;
+ cp = cp->link_ccbh;
+ }
+
+ return cp;
+}
+
+/*
+ * Target control block initialisation.
+ * Nothing important to do at the moment.
+ */
+static void sym_init_tcb (struct sym_hcb *np, u_char tn)
+{
+#if 0 /* Hmmm... this checking looks paranoid. */
+ /*
+ * Check some alignments required by the chip.
+ */
+ assert (((offsetof(struct sym_reg, nc_sxfer) ^
+ offsetof(struct sym_tcb, head.sval)) &3) == 0);
+ assert (((offsetof(struct sym_reg, nc_scntl3) ^
+ offsetof(struct sym_tcb, head.wval)) &3) == 0);
+#endif
+}
+
+/*
+ * Lun control block allocation and initialization.
+ */
+struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln)
+{
+ struct sym_tcb *tp = &np->target[tn];
+ struct sym_lcb *lp = sym_lp(tp, ln);
+
+ /*
+ * Already done, just return.
+ */
+ if (lp)
+ return lp;
+
+ /*
+ * Donnot allow LUN control block
+ * allocation for not probed LUNs.
+ */
+ if (!sym_is_bit(tp->lun_map, ln))
+ return NULL;
+
+ /*
+ * Initialize the target control block if not yet.
+ */
+ sym_init_tcb (np, tn);
+
+ /*
+ * Allocate the LCB bus address array.
+ * Compute the bus address of this table.
+ */
+ if (ln && !tp->luntbl) {
+ int i;
+
+ tp->luntbl = sym_calloc_dma(256, "LUNTBL");
+ if (!tp->luntbl)
+ goto fail;
+ for (i = 0 ; i < 64 ; i++)
+ tp->luntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa));
+ tp->head.luntbl_sa = cpu_to_scr(vtobus(tp->luntbl));
+ }
+
+ /*
+ * Allocate the table of pointers for LUN(s) > 0, if needed.
+ */
+ if (ln && !tp->lunmp) {
+ tp->lunmp = kcalloc(SYM_CONF_MAX_LUN, sizeof(struct sym_lcb *),
+ GFP_KERNEL);
+ if (!tp->lunmp)
+ goto fail;
+ }
+
+ /*
+ * Allocate the lcb.
+ * Make it available to the chip.
+ */
+ lp = sym_calloc_dma(sizeof(struct sym_lcb), "LCB");
+ if (!lp)
+ goto fail;
+ if (ln) {
+ tp->lunmp[ln] = lp;
+ tp->luntbl[ln] = cpu_to_scr(vtobus(lp));
+ }
+ else {
+ tp->lun0p = lp;
+ tp->head.lun0_sa = cpu_to_scr(vtobus(lp));
+ }
+
+ /*
+ * Let the itl task point to error handling.
+ */
+ lp->head.itl_task_sa = cpu_to_scr(np->bad_itl_ba);
+
+ /*
+ * Set the reselect pattern to our default. :)
+ */
+ lp->head.resel_sa = cpu_to_scr(SCRIPTB_BA(np, resel_bad_lun));
+
+ /*
+ * Set user capabilities.
+ */
+ lp->user_flags = tp->usrflags & (SYM_DISC_ENABLED | SYM_TAGS_ENABLED);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ /*
+ * Initialize device queueing.
+ */
+ sym_que_init(&lp->waiting_ccbq);
+ sym_que_init(&lp->started_ccbq);
+ lp->started_max = SYM_CONF_MAX_TASK;
+ lp->started_limit = SYM_CONF_MAX_TASK;
+#endif
+ /*
+ * If we are busy, count the IO.
+ */
+ if (sym_is_bit(tp->busy0_map, ln)) {
+ lp->busy_itl = 1;
+ sym_clr_bit(tp->busy0_map, ln);
+ }
+fail:
+ return lp;
+}
+
+/*
+ * Allocate LCB resources for tagged command queuing.
+ */
+static void sym_alloc_lcb_tags (struct sym_hcb *np, u_char tn, u_char ln)
+{
+ struct sym_tcb *tp = &np->target[tn];
+ struct sym_lcb *lp = sym_lp(tp, ln);
+ int i;
+
+ /*
+ * If LCB not available, try to allocate it.
+ */
+ if (!lp && !(lp = sym_alloc_lcb(np, tn, ln)))
+ goto fail;
+
+ /*
+ * Allocate the task table and and the tag allocation
+ * circular buffer. We want both or none.
+ */
+ lp->itlq_tbl = sym_calloc_dma(SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
+ if (!lp->itlq_tbl)
+ goto fail;
+ lp->cb_tags = kcalloc(SYM_CONF_MAX_TASK, 1, GFP_KERNEL);
+ if (!lp->cb_tags) {
+ sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
+ lp->itlq_tbl = NULL;
+ goto fail;
+ }
+
+ /*
+ * Initialize the task table with invalid entries.
+ */
+ for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++)
+ lp->itlq_tbl[i] = cpu_to_scr(np->notask_ba);
+
+ /*
+ * Fill up the tag buffer with tag numbers.
+ */
+ for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++)
+ lp->cb_tags[i] = i;
+
+ /*
+ * Make the task table available to SCRIPTS,
+ * And accept tagged commands now.
+ */
+ lp->head.itlq_tbl_sa = cpu_to_scr(vtobus(lp->itlq_tbl));
+
+ return;
+fail:
+ return;
+}
+
+/*
+ * Queue a SCSI IO to the controller.
+ */
+int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp)
+{
+ struct scsi_device *sdev = cmd->device;
+ struct sym_tcb *tp;
+ struct sym_lcb *lp;
+ u_char *msgptr;
+ u_int msglen;
+ int can_disconnect;
+
+ /*
+ * Keep track of the IO in our CCB.
+ */
+ cp->cmd = cmd;
+
+ /*
+ * Retrieve the target descriptor.
+ */
+ tp = &np->target[cp->target];
+
+ /*
+ * Retrieve the lun descriptor.
+ */
+ lp = sym_lp(tp, sdev->lun);
+
+ can_disconnect = (cp->tag != NO_TAG) ||
+ (lp && (lp->curr_flags & SYM_DISC_ENABLED));
+
+ msgptr = cp->scsi_smsg;
+ msglen = 0;
+ msgptr[msglen++] = IDENTIFY(can_disconnect, sdev->lun);
+
+ /*
+ * Build the tag message if present.
+ */
+ if (cp->tag != NO_TAG) {
+ u_char order = cp->order;
+
+ switch(order) {
+ case M_ORDERED_TAG:
+ break;
+ case M_HEAD_TAG:
+ break;
+ default:
+ order = M_SIMPLE_TAG;
+ }
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+ /*
+ * Avoid too much reordering of SCSI commands.
+ * The algorithm tries to prevent completion of any
+ * tagged command from being delayed against more
+ * than 3 times the max number of queued commands.
+ */
+ if (lp && lp->tags_since > 3*SYM_CONF_MAX_TAG) {
+ lp->tags_si = !(lp->tags_si);
+ if (lp->tags_sum[lp->tags_si]) {
+ order = M_ORDERED_TAG;
+ if ((DEBUG_FLAGS & DEBUG_TAGS)||sym_verbose>1) {
+ sym_print_addr(cmd,
+ "ordered tag forced.\n");
+ }
+ }
+ lp->tags_since = 0;
+ }
+#endif
+ msgptr[msglen++] = order;
+
+ /*
+ * For less than 128 tags, actual tags are numbered
+ * 1,3,5,..2*MAXTAGS+1,since we may have to deal
+ * with devices that have problems with #TAG 0 or too
+ * great #TAG numbers. For more tags (up to 256),
+ * we use directly our tag number.
+ */
+#if SYM_CONF_MAX_TASK > (512/4)
+ msgptr[msglen++] = cp->tag;
+#else
+ msgptr[msglen++] = (cp->tag << 1) + 1;
+#endif
+ }
+
+ /*
+ * Build a negotiation message if needed.
+ * (nego_status is filled by sym_prepare_nego())
+ */
+ cp->nego_status = 0;
+ if (tp->tgoal.check_nego && !tp->nego_cp && lp) {
+ msglen += sym_prepare_nego(np, cp, msgptr + msglen);
+ }
+
+ /*
+ * Startqueue
+ */
+ cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA(np, select));
+ cp->phys.head.go.restart = cpu_to_scr(SCRIPTA_BA(np, resel_dsa));
+
+ /*
+ * select
+ */
+ cp->phys.select.sel_id = cp->target;
+ cp->phys.select.sel_scntl3 = tp->head.wval;
+ cp->phys.select.sel_sxfer = tp->head.sval;
+ cp->phys.select.sel_scntl4 = tp->head.uval;
+
+ /*
+ * message
+ */
+ cp->phys.smsg.addr = cpu_to_scr(CCB_BA(cp, scsi_smsg));
+ cp->phys.smsg.size = cpu_to_scr(msglen);
+
+ /*
+ * status
+ */
+ cp->host_xflags = 0;
+ cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
+ cp->ssss_status = S_ILLEGAL;
+ cp->xerr_status = 0;
+ cp->host_flags = 0;
+ cp->extra_bytes = 0;
+
+ /*
+ * extreme data pointer.
+ * shall be positive, so -1 is lower than lowest.:)
+ */
+ cp->ext_sg = -1;
+ cp->ext_ofs = 0;
+
+ /*
+ * Build the CDB and DATA descriptor block
+ * and start the IO.
+ */
+ return sym_setup_data_and_start(np, cmd, cp);
+}
+
+/*
+ * Reset a SCSI target (all LUNs of this target).
+ */
+int sym_reset_scsi_target(struct sym_hcb *np, int target)
+{
+ struct sym_tcb *tp;
+
+ if (target == np->myaddr || (u_int)target >= SYM_CONF_MAX_TARGET)
+ return -1;
+
+ tp = &np->target[target];
+ tp->to_reset = 1;
+
+ np->istat_sem = SEM;
+ OUTB(np, nc_istat, SIGP|SEM);
+
+ return 0;
+}
+
+/*
+ * Abort a SCSI IO.
+ */
+static int sym_abort_ccb(struct sym_hcb *np, struct sym_ccb *cp, int timed_out)
+{
+ /*
+ * Check that the IO is active.
+ */
+ if (!cp || !cp->host_status || cp->host_status == HS_WAIT)
+ return -1;
+
+ /*
+ * If a previous abort didn't succeed in time,
+ * perform a BUS reset.
+ */
+ if (cp->to_abort) {
+ sym_reset_scsi_bus(np, 1);
+ return 0;
+ }
+
+ /*
+ * Mark the CCB for abort and allow time for.
+ */
+ cp->to_abort = timed_out ? 2 : 1;
+
+ /*
+ * Tell the SCRIPTS processor to stop and synchronize with us.
+ */
+ np->istat_sem = SEM;
+ OUTB(np, nc_istat, SIGP|SEM);
+ return 0;
+}
+
+int sym_abort_scsiio(struct sym_hcb *np, struct scsi_cmnd *cmd, int timed_out)
+{
+ struct sym_ccb *cp;
+ SYM_QUEHEAD *qp;
+
+ /*
+ * Look up our CCB control block.
+ */
+ cp = NULL;
+ FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+ struct sym_ccb *cp2 = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ if (cp2->cmd == cmd) {
+ cp = cp2;
+ break;
+ }
+ }
+
+ return sym_abort_ccb(np, cp, timed_out);
+}
+
+/*
+ * Complete execution of a SCSI command with extented
+ * error, SCSI status error, or having been auto-sensed.
+ *
+ * The SCRIPTS processor is not running there, so we
+ * can safely access IO registers and remove JOBs from
+ * the START queue.
+ * SCRATCHA is assumed to have been loaded with STARTPOS
+ * before the SCRIPTS called the C code.
+ */
+void sym_complete_error(struct sym_hcb *np, struct sym_ccb *cp)
+{
+ struct scsi_device *sdev;
+ struct scsi_cmnd *cmd;
+ struct sym_tcb *tp;
+ struct sym_lcb *lp;
+ int resid;
+ int i;
+
+ /*
+ * Paranoid check. :)
+ */
+ if (!cp || !cp->cmd)
+ return;
+
+ cmd = cp->cmd;
+ sdev = cmd->device;
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_RESULT)) {
+ dev_info(&sdev->sdev_gendev, "CCB=%p STAT=%x/%x/%x\n", cp,
+ cp->host_status, cp->ssss_status, cp->host_flags);
+ }
+
+ /*
+ * Get target and lun pointers.
+ */
+ tp = &np->target[cp->target];
+ lp = sym_lp(tp, sdev->lun);
+
+ /*
+ * Check for extended errors.
+ */
+ if (cp->xerr_status) {
+ if (sym_verbose)
+ sym_print_xerr(cmd, cp->xerr_status);
+ if (cp->host_status == HS_COMPLETE)
+ cp->host_status = HS_COMP_ERR;
+ }
+
+ /*
+ * Calculate the residual.
+ */
+ resid = sym_compute_residual(np, cp);
+
+ if (!SYM_SETUP_RESIDUAL_SUPPORT) {/* If user does not want residuals */
+ resid = 0; /* throw them away. :) */
+ cp->sv_resid = 0;
+ }
+#ifdef DEBUG_2_0_X
+if (resid)
+ printf("XXXX RESID= %d - 0x%x\n", resid, resid);
+#endif
+
+ /*
+ * Dequeue all queued CCBs for that device
+ * not yet started by SCRIPTS.
+ */
+ i = (INL(np, nc_scratcha) - np->squeue_ba) / 4;
+ i = sym_dequeue_from_squeue(np, i, cp->target, sdev->lun, -1);
+
+ /*
+ * Restart the SCRIPTS processor.
+ */
+ OUTL_DSP(np, SCRIPTA_BA(np, start));
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ if (cp->host_status == HS_COMPLETE &&
+ cp->ssss_status == S_QUEUE_FULL) {
+ if (!lp || lp->started_tags - i < 2)
+ goto weirdness;
+ /*
+ * Decrease queue depth as needed.
+ */
+ lp->started_max = lp->started_tags - i - 1;
+ lp->num_sgood = 0;
+
+ if (sym_verbose >= 2) {
+ sym_print_addr(cmd, " queue depth is now %d\n",
+ lp->started_max);
+ }
+
+ /*
+ * Repair the CCB.
+ */
+ cp->host_status = HS_BUSY;
+ cp->ssss_status = S_ILLEGAL;
+
+ /*
+ * Let's requeue it to device.
+ */
+ sym_set_cam_status(cmd, CAM_REQUEUE_REQ);
+ goto finish;
+ }
+weirdness:
+#endif
+ /*
+ * Build result in CAM ccb.
+ */
+ sym_set_cam_result_error(np, cp, resid);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+finish:
+#endif
+ /*
+ * Add this one to the COMP queue.
+ */
+ sym_remque(&cp->link_ccbq);
+ sym_insque_head(&cp->link_ccbq, &np->comp_ccbq);
+
+ /*
+ * Complete all those commands with either error
+ * or requeue condition.
+ */
+ sym_flush_comp_queue(np, 0);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ /*
+ * Donnot start more than 1 command after an error.
+ */
+ if (lp)
+ sym_start_next_ccbs(np, lp, 1);
+#endif
+}
+
+/*
+ * Complete execution of a successful SCSI command.
+ *
+ * Only successful commands go to the DONE queue,
+ * since we need to have the SCRIPTS processor
+ * stopped on any error condition.
+ * The SCRIPTS processor is running while we are
+ * completing successful commands.
+ */
+void sym_complete_ok (struct sym_hcb *np, struct sym_ccb *cp)
+{
+ struct sym_tcb *tp;
+ struct sym_lcb *lp;
+ struct scsi_cmnd *cmd;
+ int resid;
+
+ /*
+ * Paranoid check. :)
+ */
+ if (!cp || !cp->cmd)
+ return;
+ assert (cp->host_status == HS_COMPLETE);
+
+ /*
+ * Get user command.
+ */
+ cmd = cp->cmd;
+
+ /*
+ * Get target and lun pointers.
+ */
+ tp = &np->target[cp->target];
+ lp = sym_lp(tp, cp->lun);
+
+ /*
+ * Assume device discovered on first success.
+ */
+ if (!lp)
+ sym_set_bit(tp->lun_map, cp->lun);
+
+ /*
+ * If all data have been transferred, given than no
+ * extended error did occur, there is no residual.
+ */
+ resid = 0;
+ if (cp->phys.head.lastp != sym_goalp(cp))
+ resid = sym_compute_residual(np, cp);
+
+ /*
+ * Wrong transfer residuals may be worse than just always
+ * returning zero. User can disable this feature in
+ * sym53c8xx.h. Residual support is enabled by default.
+ */
+ if (!SYM_SETUP_RESIDUAL_SUPPORT)
+ resid = 0;
+#ifdef DEBUG_2_0_X
+if (resid)
+ printf("XXXX RESID= %d - 0x%x\n", resid, resid);
+#endif
+
+ /*
+ * Build result in CAM ccb.
+ */
+ sym_set_cam_result_ok(cp, cmd, resid);
+
+#ifdef SYM_OPT_SNIFF_INQUIRY
+ /*
+ * On standard INQUIRY response (EVPD and CmDt
+ * not set), sniff out device capabilities.
+ */
+ if (cp->cdb_buf[0] == INQUIRY && !(cp->cdb_buf[1] & 0x3))
+ sym_sniff_inquiry(np, cmd, resid);
+#endif
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ /*
+ * If max number of started ccbs had been reduced,
+ * increase it if 200 good status received.
+ */
+ if (lp && lp->started_max < lp->started_limit) {
+ ++lp->num_sgood;
+ if (lp->num_sgood >= 200) {
+ lp->num_sgood = 0;
+ ++lp->started_max;
+ if (sym_verbose >= 2) {
+ sym_print_addr(cmd, " queue depth is now %d\n",
+ lp->started_max);
+ }
+ }
+ }
+#endif
+
+ /*
+ * Free our CCB.
+ */
+ sym_free_ccb (np, cp);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ /*
+ * Requeue a couple of awaiting scsi commands.
+ */
+ if (lp && !sym_que_empty(&lp->waiting_ccbq))
+ sym_start_next_ccbs(np, lp, 2);
+#endif
+ /*
+ * Complete the command.
+ */
+ sym_xpt_done(np, cmd);
+}
+
+/*
+ * Soft-attach the controller.
+ */
+int sym_hcb_attach(struct Scsi_Host *shost, struct sym_fw *fw, struct sym_nvram *nvram)
+{
+ struct sym_hcb *np = sym_get_hcb(shost);
+ int i;
+
+ /*
+ * Get some info about the firmware.
+ */
+ np->scripta_sz = fw->a_size;
+ np->scriptb_sz = fw->b_size;
+ np->scriptz_sz = fw->z_size;
+ np->fw_setup = fw->setup;
+ np->fw_patch = fw->patch;
+ np->fw_name = fw->name;
+
+ /*
+ * Save setting of some IO registers, so we will
+ * be able to probe specific implementations.
+ */
+ sym_save_initial_setting (np);
+
+ /*
+ * Reset the chip now, since it has been reported
+ * that SCSI clock calibration may not work properly
+ * if the chip is currently active.
+ */
+ sym_chip_reset(np);
+
+ /*
+ * Prepare controller and devices settings, according
+ * to chip features, user set-up and driver set-up.
+ */
+ sym_prepare_setting(shost, np, nvram);
+
+ /*
+ * Check the PCI clock frequency.
+ * Must be performed after prepare_setting since it destroys
+ * STEST1 that is used to probe for the clock doubler.
+ */
+ i = sym_getpciclock(np);
+ if (i > 37000 && !(np->features & FE_66MHZ))
+ printf("%s: PCI BUS clock seems too high: %u KHz.\n",
+ sym_name(np), i);
+
+ /*
+ * Allocate the start queue.
+ */
+ np->squeue = sym_calloc_dma(sizeof(u32)*(MAX_QUEUE*2),"SQUEUE");
+ if (!np->squeue)
+ goto attach_failed;
+ np->squeue_ba = vtobus(np->squeue);
+
+ /*
+ * Allocate the done queue.
+ */
+ np->dqueue = sym_calloc_dma(sizeof(u32)*(MAX_QUEUE*2),"DQUEUE");
+ if (!np->dqueue)
+ goto attach_failed;
+ np->dqueue_ba = vtobus(np->dqueue);
+
+ /*
+ * Allocate the target bus address array.
+ */
+ np->targtbl = sym_calloc_dma(256, "TARGTBL");
+ if (!np->targtbl)
+ goto attach_failed;
+ np->targtbl_ba = vtobus(np->targtbl);
+
+ /*
+ * Allocate SCRIPTS areas.
+ */
+ np->scripta0 = sym_calloc_dma(np->scripta_sz, "SCRIPTA0");
+ np->scriptb0 = sym_calloc_dma(np->scriptb_sz, "SCRIPTB0");
+ np->scriptz0 = sym_calloc_dma(np->scriptz_sz, "SCRIPTZ0");
+ if (!np->scripta0 || !np->scriptb0 || !np->scriptz0)
+ goto attach_failed;
+
+ /*
+ * Allocate the array of lists of CCBs hashed by DSA.
+ */
+ np->ccbh = kcalloc(sizeof(struct sym_ccb **), CCB_HASH_SIZE, GFP_KERNEL);
+ if (!np->ccbh)
+ goto attach_failed;
+
+ /*
+ * Initialyze the CCB free and busy queues.
+ */
+ sym_que_init(&np->free_ccbq);
+ sym_que_init(&np->busy_ccbq);
+ sym_que_init(&np->comp_ccbq);
+
+ /*
+ * Initialization for optional handling
+ * of device queueing.
+ */
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ sym_que_init(&np->dummy_ccbq);
+#endif
+ /*
+ * Allocate some CCB. We need at least ONE.
+ */
+ if (!sym_alloc_ccb(np))
+ goto attach_failed;
+
+ /*
+ * Calculate BUS addresses where we are going
+ * to load the SCRIPTS.
+ */
+ np->scripta_ba = vtobus(np->scripta0);
+ np->scriptb_ba = vtobus(np->scriptb0);
+ np->scriptz_ba = vtobus(np->scriptz0);
+
+ if (np->ram_ba) {
+ np->scripta_ba = np->ram_ba;
+ if (np->features & FE_RAM8K) {
+ np->ram_ws = 8192;
+ np->scriptb_ba = np->scripta_ba + 4096;
+#if 0 /* May get useful for 64 BIT PCI addressing */
+ np->scr_ram_seg = cpu_to_scr(np->scripta_ba >> 32);
+#endif
+ }
+ else
+ np->ram_ws = 4096;
+ }
+
+ /*
+ * Copy scripts to controller instance.
+ */
+ memcpy(np->scripta0, fw->a_base, np->scripta_sz);
+ memcpy(np->scriptb0, fw->b_base, np->scriptb_sz);
+ memcpy(np->scriptz0, fw->z_base, np->scriptz_sz);
+
+ /*
+ * Setup variable parts in scripts and compute
+ * scripts bus addresses used from the C code.
+ */
+ np->fw_setup(np, fw);
+
+ /*
+ * Bind SCRIPTS with physical addresses usable by the
+ * SCRIPTS processor (as seen from the BUS = BUS addresses).
+ */
+ sym_fw_bind_script(np, (u32 *) np->scripta0, np->scripta_sz);
+ sym_fw_bind_script(np, (u32 *) np->scriptb0, np->scriptb_sz);
+ sym_fw_bind_script(np, (u32 *) np->scriptz0, np->scriptz_sz);
+
+#ifdef SYM_CONF_IARB_SUPPORT
+ /*
+ * If user wants IARB to be set when we win arbitration
+ * and have other jobs, compute the max number of consecutive
+ * settings of IARB hints before we leave devices a chance to
+ * arbitrate for reselection.
+ */
+#ifdef SYM_SETUP_IARB_MAX
+ np->iarb_max = SYM_SETUP_IARB_MAX;
+#else
+ np->iarb_max = 4;
+#endif
+#endif
+
+ /*
+ * Prepare the idle and invalid task actions.
+ */
+ np->idletask.start = cpu_to_scr(SCRIPTA_BA(np, idle));
+ np->idletask.restart = cpu_to_scr(SCRIPTB_BA(np, bad_i_t_l));
+ np->idletask_ba = vtobus(&np->idletask);
+
+ np->notask.start = cpu_to_scr(SCRIPTA_BA(np, idle));
+ np->notask.restart = cpu_to_scr(SCRIPTB_BA(np, bad_i_t_l));
+ np->notask_ba = vtobus(&np->notask);
+
+ np->bad_itl.start = cpu_to_scr(SCRIPTA_BA(np, idle));
+ np->bad_itl.restart = cpu_to_scr(SCRIPTB_BA(np, bad_i_t_l));
+ np->bad_itl_ba = vtobus(&np->bad_itl);
+
+ np->bad_itlq.start = cpu_to_scr(SCRIPTA_BA(np, idle));
+ np->bad_itlq.restart = cpu_to_scr(SCRIPTB_BA(np,bad_i_t_l_q));
+ np->bad_itlq_ba = vtobus(&np->bad_itlq);
+
+ /*
+ * Allocate and prepare the lun JUMP table that is used
+ * for a target prior the probing of devices (bad lun table).
+ * A private table will be allocated for the target on the
+ * first INQUIRY response received.
+ */
+ np->badluntbl = sym_calloc_dma(256, "BADLUNTBL");
+ if (!np->badluntbl)
+ goto attach_failed;
+
+ np->badlun_sa = cpu_to_scr(SCRIPTB_BA(np, resel_bad_lun));
+ for (i = 0 ; i < 64 ; i++) /* 64 luns/target, no less */
+ np->badluntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa));
+
+ /*
+ * Prepare the bus address array that contains the bus
+ * address of each target control block.
+ * For now, assume all logical units are wrong. :)
+ */
+ for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+ np->targtbl[i] = cpu_to_scr(vtobus(&np->target[i]));
+ np->target[i].head.luntbl_sa =
+ cpu_to_scr(vtobus(np->badluntbl));
+ np->target[i].head.lun0_sa =
+ cpu_to_scr(vtobus(&np->badlun_sa));
+ }
+
+ /*
+ * Now check the cache handling of the pci chipset.
+ */
+ if (sym_snooptest (np)) {
+ printf("%s: CACHE INCORRECTLY CONFIGURED.\n", sym_name(np));
+ goto attach_failed;
+ }
+
+ /*
+ * Sigh! we are done.
+ */
+ return 0;
+
+attach_failed:
+ return -ENXIO;
+}
+
+/*
+ * Free everything that has been allocated for this device.
+ */
+void sym_hcb_free(struct sym_hcb *np)
+{
+ SYM_QUEHEAD *qp;
+ struct sym_ccb *cp;
+ struct sym_tcb *tp;
+ struct sym_lcb *lp;
+ int target, lun;
+
+ if (np->scriptz0)
+ sym_mfree_dma(np->scriptz0, np->scriptz_sz, "SCRIPTZ0");
+ if (np->scriptb0)
+ sym_mfree_dma(np->scriptb0, np->scriptb_sz, "SCRIPTB0");
+ if (np->scripta0)
+ sym_mfree_dma(np->scripta0, np->scripta_sz, "SCRIPTA0");
+ if (np->squeue)
+ sym_mfree_dma(np->squeue, sizeof(u32)*(MAX_QUEUE*2), "SQUEUE");
+ if (np->dqueue)
+ sym_mfree_dma(np->dqueue, sizeof(u32)*(MAX_QUEUE*2), "DQUEUE");
+
+ if (np->actccbs) {
+ while ((qp = sym_remque_head(&np->free_ccbq)) != 0) {
+ cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+ sym_mfree_dma(cp, sizeof(*cp), "CCB");
+ }
+ }
+ kfree(np->ccbh);
+
+ if (np->badluntbl)
+ sym_mfree_dma(np->badluntbl, 256,"BADLUNTBL");
+
+ for (target = 0; target < SYM_CONF_MAX_TARGET ; target++) {
+ tp = &np->target[target];
+ for (lun = 0 ; lun < SYM_CONF_MAX_LUN ; lun++) {
+ lp = sym_lp(tp, lun);
+ if (!lp)
+ continue;
+ if (lp->itlq_tbl)
+ sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4,
+ "ITLQ_TBL");
+ kfree(lp->cb_tags);
+ sym_mfree_dma(lp, sizeof(*lp), "LCB");
+ }
+#if SYM_CONF_MAX_LUN > 1
+ kfree(tp->lunmp);
+#endif
+ }
+ if (np->targtbl)
+ sym_mfree_dma(np->targtbl, 256, "TARGTBL");
+}
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h
new file mode 100644
index 000000000000..a95cbe4b8e39
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h
@@ -0,0 +1,1304 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM_HIPD_H
+#define SYM_HIPD_H
+
+/*
+ * Generic driver options.
+ *
+ * They may be defined in platform specific headers, if they
+ * are useful.
+ *
+ * SYM_OPT_HANDLE_DIR_UNKNOWN
+ * When this option is set, the SCRIPTS used by the driver
+ * are able to handle SCSI transfers with direction not
+ * supplied by user.
+ * (set for Linux-2.0.X)
+ *
+ * SYM_OPT_HANDLE_DEVICE_QUEUEING
+ * When this option is set, the driver will use a queue per
+ * device and handle QUEUE FULL status requeuing internally.
+ *
+ * SYM_OPT_LIMIT_COMMAND_REORDERING
+ * When this option is set, the driver tries to limit tagged
+ * command reordering to some reasonnable value.
+ * (set for Linux)
+ */
+#if 0
+#define SYM_OPT_HANDLE_DIR_UNKNOWN
+#define SYM_OPT_HANDLE_DEVICE_QUEUEING
+#define SYM_OPT_LIMIT_COMMAND_REORDERING
+#endif
+
+/*
+ * Active debugging tags and verbosity.
+ * Both DEBUG_FLAGS and sym_verbose can be redefined
+ * by the platform specific code to something else.
+ */
+#define DEBUG_ALLOC (0x0001)
+#define DEBUG_PHASE (0x0002)
+#define DEBUG_POLL (0x0004)
+#define DEBUG_QUEUE (0x0008)
+#define DEBUG_RESULT (0x0010)
+#define DEBUG_SCATTER (0x0020)
+#define DEBUG_SCRIPT (0x0040)
+#define DEBUG_TINY (0x0080)
+#define DEBUG_TIMING (0x0100)
+#define DEBUG_NEGO (0x0200)
+#define DEBUG_TAGS (0x0400)
+#define DEBUG_POINTER (0x0800)
+
+#ifndef DEBUG_FLAGS
+#define DEBUG_FLAGS (0x0000)
+#endif
+
+#ifndef sym_verbose
+#define sym_verbose (np->verbose)
+#endif
+
+/*
+ * These ones should have been already defined.
+ */
+#ifndef assert
+#define assert(expression) { \
+ if (!(expression)) { \
+ (void)panic( \
+ "assertion \"%s\" failed: file \"%s\", line %d\n", \
+ #expression, \
+ __FILE__, __LINE__); \
+ } \
+}
+#endif
+
+/*
+ * Number of tasks per device we want to handle.
+ */
+#if SYM_CONF_MAX_TAG_ORDER > 8
+#error "more than 256 tags per logical unit not allowed."
+#endif
+#define SYM_CONF_MAX_TASK (1<<SYM_CONF_MAX_TAG_ORDER)
+
+/*
+ * Donnot use more tasks that we can handle.
+ */
+#ifndef SYM_CONF_MAX_TAG
+#define SYM_CONF_MAX_TAG SYM_CONF_MAX_TASK
+#endif
+#if SYM_CONF_MAX_TAG > SYM_CONF_MAX_TASK
+#undef SYM_CONF_MAX_TAG
+#define SYM_CONF_MAX_TAG SYM_CONF_MAX_TASK
+#endif
+
+/*
+ * This one means 'NO TAG for this job'
+ */
+#define NO_TAG (256)
+
+/*
+ * Number of SCSI targets.
+ */
+#if SYM_CONF_MAX_TARGET > 16
+#error "more than 16 targets not allowed."
+#endif
+
+/*
+ * Number of logical units per target.
+ */
+#if SYM_CONF_MAX_LUN > 64
+#error "more than 64 logical units per target not allowed."
+#endif
+
+/*
+ * Asynchronous pre-scaler (ns). Shall be 40 for
+ * the SCSI timings to be compliant.
+ */
+#define SYM_CONF_MIN_ASYNC (40)
+
+/*
+ * Shortest memory chunk is (1<<SYM_MEM_SHIFT), currently 16.
+ * Actual allocations happen as SYM_MEM_CLUSTER_SIZE sized.
+ * (1 PAGE at a time is just fine).
+ */
+#define SYM_MEM_SHIFT 4
+#define SYM_MEM_CLUSTER_SIZE (1UL << SYM_MEM_CLUSTER_SHIFT)
+#define SYM_MEM_CLUSTER_MASK (SYM_MEM_CLUSTER_SIZE-1)
+
+/*
+ * Number of entries in the START and DONE queues.
+ *
+ * We limit to 1 PAGE in order to succeed allocation of
+ * these queues. Each entry is 8 bytes long (2 DWORDS).
+ */
+#ifdef SYM_CONF_MAX_START
+#define SYM_CONF_MAX_QUEUE (SYM_CONF_MAX_START+2)
+#else
+#define SYM_CONF_MAX_QUEUE (7*SYM_CONF_MAX_TASK+2)
+#define SYM_CONF_MAX_START (SYM_CONF_MAX_QUEUE-2)
+#endif
+
+#if SYM_CONF_MAX_QUEUE > SYM_MEM_CLUSTER_SIZE/8
+#undef SYM_CONF_MAX_QUEUE
+#define SYM_CONF_MAX_QUEUE (SYM_MEM_CLUSTER_SIZE/8)
+#undef SYM_CONF_MAX_START
+#define SYM_CONF_MAX_START (SYM_CONF_MAX_QUEUE-2)
+#endif
+
+/*
+ * For this one, we want a short name :-)
+ */
+#define MAX_QUEUE SYM_CONF_MAX_QUEUE
+
+/*
+ * Common definitions for both bus space based and legacy IO methods.
+ */
+
+#define INB_OFF(np, o) ioread8(np->s.ioaddr + (o))
+#define INW_OFF(np, o) ioread16(np->s.ioaddr + (o))
+#define INL_OFF(np, o) ioread32(np->s.ioaddr + (o))
+
+#define OUTB_OFF(np, o, val) iowrite8((val), np->s.ioaddr + (o))
+#define OUTW_OFF(np, o, val) iowrite16((val), np->s.ioaddr + (o))
+#define OUTL_OFF(np, o, val) iowrite32((val), np->s.ioaddr + (o))
+
+#define INB(np, r) INB_OFF(np, offsetof(struct sym_reg, r))
+#define INW(np, r) INW_OFF(np, offsetof(struct sym_reg, r))
+#define INL(np, r) INL_OFF(np, offsetof(struct sym_reg, r))
+
+#define OUTB(np, r, v) OUTB_OFF(np, offsetof(struct sym_reg, r), (v))
+#define OUTW(np, r, v) OUTW_OFF(np, offsetof(struct sym_reg, r), (v))
+#define OUTL(np, r, v) OUTL_OFF(np, offsetof(struct sym_reg, r), (v))
+
+#define OUTONB(np, r, m) OUTB(np, r, INB(np, r) | (m))
+#define OUTOFFB(np, r, m) OUTB(np, r, INB(np, r) & ~(m))
+#define OUTONW(np, r, m) OUTW(np, r, INW(np, r) | (m))
+#define OUTOFFW(np, r, m) OUTW(np, r, INW(np, r) & ~(m))
+#define OUTONL(np, r, m) OUTL(np, r, INL(np, r) | (m))
+#define OUTOFFL(np, r, m) OUTL(np, r, INL(np, r) & ~(m))
+
+/*
+ * We normally want the chip to have a consistent view
+ * of driver internal data structures when we restart it.
+ * Thus these macros.
+ */
+#define OUTL_DSP(np, v) \
+ do { \
+ MEMORY_WRITE_BARRIER(); \
+ OUTL(np, nc_dsp, (v)); \
+ } while (0)
+
+#define OUTONB_STD() \
+ do { \
+ MEMORY_WRITE_BARRIER(); \
+ OUTONB(np, nc_dcntl, (STD|NOCOM)); \
+ } while (0)
+
+/*
+ * Command control block states.
+ */
+#define HS_IDLE (0)
+#define HS_BUSY (1)
+#define HS_NEGOTIATE (2) /* sync/wide data transfer*/
+#define HS_DISCONNECT (3) /* Disconnected by target */
+#define HS_WAIT (4) /* waiting for resource */
+
+#define HS_DONEMASK (0x80)
+#define HS_COMPLETE (4|HS_DONEMASK)
+#define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout */
+#define HS_UNEXPECTED (6|HS_DONEMASK) /* Unexpected disconnect */
+#define HS_COMP_ERR (7|HS_DONEMASK) /* Completed with error */
+
+/*
+ * Software Interrupt Codes
+ */
+#define SIR_BAD_SCSI_STATUS (1)
+#define SIR_SEL_ATN_NO_MSG_OUT (2)
+#define SIR_MSG_RECEIVED (3)
+#define SIR_MSG_WEIRD (4)
+#define SIR_NEGO_FAILED (5)
+#define SIR_NEGO_PROTO (6)
+#define SIR_SCRIPT_STOPPED (7)
+#define SIR_REJECT_TO_SEND (8)
+#define SIR_SWIDE_OVERRUN (9)
+#define SIR_SODL_UNDERRUN (10)
+#define SIR_RESEL_NO_MSG_IN (11)
+#define SIR_RESEL_NO_IDENTIFY (12)
+#define SIR_RESEL_BAD_LUN (13)
+#define SIR_TARGET_SELECTED (14)
+#define SIR_RESEL_BAD_I_T_L (15)
+#define SIR_RESEL_BAD_I_T_L_Q (16)
+#define SIR_ABORT_SENT (17)
+#define SIR_RESEL_ABORTED (18)
+#define SIR_MSG_OUT_DONE (19)
+#define SIR_COMPLETE_ERROR (20)
+#define SIR_DATA_OVERRUN (21)
+#define SIR_BAD_PHASE (22)
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+#define SIR_DMAP_DIRTY (23)
+#define SIR_MAX (23)
+#else
+#define SIR_MAX (22)
+#endif
+
+/*
+ * Extended error bit codes.
+ * xerr_status field of struct sym_ccb.
+ */
+#define XE_EXTRA_DATA (1) /* unexpected data phase */
+#define XE_BAD_PHASE (1<<1) /* illegal phase (4/5) */
+#define XE_PARITY_ERR (1<<2) /* unrecovered SCSI parity error */
+#define XE_SODL_UNRUN (1<<3) /* ODD transfer in DATA OUT phase */
+#define XE_SWIDE_OVRUN (1<<4) /* ODD transfer in DATA IN phase */
+
+/*
+ * Negotiation status.
+ * nego_status field of struct sym_ccb.
+ */
+#define NS_SYNC (1)
+#define NS_WIDE (2)
+#define NS_PPR (3)
+
+/*
+ * A CCB hashed table is used to retrieve CCB address
+ * from DSA value.
+ */
+#define CCB_HASH_SHIFT 8
+#define CCB_HASH_SIZE (1UL << CCB_HASH_SHIFT)
+#define CCB_HASH_MASK (CCB_HASH_SIZE-1)
+#if 1
+#define CCB_HASH_CODE(dsa) \
+ (((dsa) >> (_LGRU16_(sizeof(struct sym_ccb)))) & CCB_HASH_MASK)
+#else
+#define CCB_HASH_CODE(dsa) (((dsa) >> 9) & CCB_HASH_MASK)
+#endif
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+/*
+ * We may want to use segment registers for 64 bit DMA.
+ * 16 segments registers -> up to 64 GB addressable.
+ */
+#define SYM_DMAP_SHIFT (4)
+#define SYM_DMAP_SIZE (1u<<SYM_DMAP_SHIFT)
+#define SYM_DMAP_MASK (SYM_DMAP_SIZE-1)
+#endif
+
+/*
+ * Device flags.
+ */
+#define SYM_DISC_ENABLED (1)
+#define SYM_TAGS_ENABLED (1<<1)
+#define SYM_SCAN_BOOT_DISABLED (1<<2)
+#define SYM_SCAN_LUNS_DISABLED (1<<3)
+
+/*
+ * Host adapter miscellaneous flags.
+ */
+#define SYM_AVOID_BUS_RESET (1)
+
+/*
+ * Misc.
+ */
+#define SYM_SNOOP_TIMEOUT (10000000)
+#define BUS_8_BIT 0
+#define BUS_16_BIT 1
+
+/*
+ * Gather negotiable parameters value
+ */
+struct sym_trans {
+ u8 period;
+ u8 offset;
+ unsigned int width:1;
+ unsigned int iu:1;
+ unsigned int dt:1;
+ unsigned int qas:1;
+ unsigned int check_nego:1;
+};
+
+/*
+ * Global TCB HEADER.
+ *
+ * Due to lack of indirect addressing on earlier NCR chips,
+ * this substructure is copied from the TCB to a global
+ * address after selection.
+ * For SYMBIOS chips that support LOAD/STORE this copy is
+ * not needed and thus not performed.
+ */
+struct sym_tcbh {
+ /*
+ * Scripts bus addresses of LUN table accessed from scripts.
+ * LUN #0 is a special case, since multi-lun devices are rare,
+ * and we we want to speed-up the general case and not waste
+ * resources.
+ */
+ u32 luntbl_sa; /* bus address of this table */
+ u32 lun0_sa; /* bus address of LCB #0 */
+ /*
+ * Actual SYNC/WIDE IO registers value for this target.
+ * 'sval', 'wval' and 'uval' are read from SCRIPTS and
+ * so have alignment constraints.
+ */
+/*0*/ u_char uval; /* -> SCNTL4 register */
+/*1*/ u_char sval; /* -> SXFER io register */
+/*2*/ u_char filler1;
+/*3*/ u_char wval; /* -> SCNTL3 io register */
+};
+
+/*
+ * Target Control Block
+ */
+struct sym_tcb {
+ /*
+ * TCB header.
+ * Assumed at offset 0.
+ */
+/*0*/ struct sym_tcbh head;
+
+ /*
+ * LUN table used by the SCRIPTS processor.
+ * An array of bus addresses is used on reselection.
+ */
+ u32 *luntbl; /* LCBs bus address table */
+
+ /*
+ * LUN table used by the C code.
+ */
+ struct sym_lcb *lun0p; /* LCB of LUN #0 (usual case) */
+#if SYM_CONF_MAX_LUN > 1
+ struct sym_lcb **lunmp; /* Other LCBs [1..MAX_LUN] */
+#endif
+
+ /*
+ * Bitmap that tells about LUNs that succeeded at least
+ * 1 IO and therefore assumed to be a real device.
+ * Avoid useless allocation of the LCB structure.
+ */
+ u32 lun_map[(SYM_CONF_MAX_LUN+31)/32];
+
+ /*
+ * Bitmap that tells about LUNs that haven't yet an LCB
+ * allocated (not discovered or LCB allocation failed).
+ */
+ u32 busy0_map[(SYM_CONF_MAX_LUN+31)/32];
+
+#ifdef SYM_HAVE_STCB
+ /*
+ * O/S specific data structure.
+ */
+ struct sym_stcb s;
+#endif
+
+ /* Transfer goal */
+ struct sym_trans tgoal;
+
+ /*
+ * Keep track of the CCB used for the negotiation in order
+ * to ensure that only 1 negotiation is queued at a time.
+ */
+ struct sym_ccb * nego_cp; /* CCB used for the nego */
+
+ /*
+ * Set when we want to reset the device.
+ */
+ u_char to_reset;
+
+ /*
+ * Other user settable limits and options.
+ * These limits are read from the NVRAM if present.
+ */
+ u_char usrflags;
+ u_short usrtags;
+ struct scsi_device *sdev;
+};
+
+/*
+ * Global LCB HEADER.
+ *
+ * Due to lack of indirect addressing on earlier NCR chips,
+ * this substructure is copied from the LCB to a global
+ * address after selection.
+ * For SYMBIOS chips that support LOAD/STORE this copy is
+ * not needed and thus not performed.
+ */
+struct sym_lcbh {
+ /*
+ * SCRIPTS address jumped by SCRIPTS on reselection.
+ * For not probed logical units, this address points to
+ * SCRIPTS that deal with bad LU handling (must be at
+ * offset zero of the LCB for that reason).
+ */
+/*0*/ u32 resel_sa;
+
+ /*
+ * Task (bus address of a CCB) read from SCRIPTS that points
+ * to the unique ITL nexus allowed to be disconnected.
+ */
+ u32 itl_task_sa;
+
+ /*
+ * Task table bus address (read from SCRIPTS).
+ */
+ u32 itlq_tbl_sa;
+};
+
+/*
+ * Logical Unit Control Block
+ */
+struct sym_lcb {
+ /*
+ * TCB header.
+ * Assumed at offset 0.
+ */
+/*0*/ struct sym_lcbh head;
+
+ /*
+ * Task table read from SCRIPTS that contains pointers to
+ * ITLQ nexuses. The bus address read from SCRIPTS is
+ * inside the header.
+ */
+ u32 *itlq_tbl; /* Kernel virtual address */
+
+ /*
+ * Busy CCBs management.
+ */
+ u_short busy_itlq; /* Number of busy tagged CCBs */
+ u_short busy_itl; /* Number of busy untagged CCBs */
+
+ /*
+ * Circular tag allocation buffer.
+ */
+ u_short ia_tag; /* Tag allocation index */
+ u_short if_tag; /* Tag release index */
+ u_char *cb_tags; /* Circular tags buffer */
+
+ /*
+ * O/S specific data structure.
+ */
+#ifdef SYM_HAVE_SLCB
+ struct sym_slcb s;
+#endif
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ /*
+ * Optionnaly the driver can handle device queueing,
+ * and requeues internally command to redo.
+ */
+ SYM_QUEHEAD waiting_ccbq;
+ SYM_QUEHEAD started_ccbq;
+ int num_sgood;
+ u_short started_tags;
+ u_short started_no_tag;
+ u_short started_max;
+ u_short started_limit;
+#endif
+
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+ /*
+ * Optionally the driver can try to prevent SCSI
+ * IOs from being reordered too much.
+ */
+ u_char tags_si; /* Current index to tags sum */
+ u_short tags_sum[2]; /* Tags sum counters */
+ u_short tags_since; /* # of tags since last switch */
+#endif
+
+ /*
+ * Set when we want to clear all tasks.
+ */
+ u_char to_clear;
+
+ /*
+ * Capabilities.
+ */
+ u_char user_flags;
+ u_char curr_flags;
+};
+
+/*
+ * Action from SCRIPTS on a task.
+ * Is part of the CCB, but is also used separately to plug
+ * error handling action to perform from SCRIPTS.
+ */
+struct sym_actscr {
+ u32 start; /* Jumped by SCRIPTS after selection */
+ u32 restart; /* Jumped by SCRIPTS on relection */
+};
+
+/*
+ * Phase mismatch context.
+ *
+ * It is part of the CCB and is used as parameters for the
+ * DATA pointer. We need two contexts to handle correctly the
+ * SAVED DATA POINTER.
+ */
+struct sym_pmc {
+ struct sym_tblmove sg; /* Updated interrupted SG block */
+ u32 ret; /* SCRIPT return address */
+};
+
+/*
+ * LUN control block lookup.
+ * We use a direct pointer for LUN #0, and a table of
+ * pointers which is only allocated for devices that support
+ * LUN(s) > 0.
+ */
+#if SYM_CONF_MAX_LUN <= 1
+#define sym_lp(tp, lun) (!lun) ? (tp)->lun0p : NULL
+#else
+#define sym_lp(tp, lun) \
+ (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[(lun)] : NULL
+#endif
+
+/*
+ * Status are used by the host and the script processor.
+ *
+ * The last four bytes (status[4]) are copied to the
+ * scratchb register (declared as scr0..scr3) just after the
+ * select/reselect, and copied back just after disconnecting.
+ * Inside the script the XX_REG are used.
+ */
+
+/*
+ * Last four bytes (script)
+ */
+#define HX_REG scr0
+#define HX_PRT nc_scr0
+#define HS_REG scr1
+#define HS_PRT nc_scr1
+#define SS_REG scr2
+#define SS_PRT nc_scr2
+#define HF_REG scr3
+#define HF_PRT nc_scr3
+
+/*
+ * Last four bytes (host)
+ */
+#define host_xflags phys.head.status[0]
+#define host_status phys.head.status[1]
+#define ssss_status phys.head.status[2]
+#define host_flags phys.head.status[3]
+
+/*
+ * Host flags
+ */
+#define HF_IN_PM0 1u
+#define HF_IN_PM1 (1u<<1)
+#define HF_ACT_PM (1u<<2)
+#define HF_DP_SAVED (1u<<3)
+#define HF_SENSE (1u<<4)
+#define HF_EXT_ERR (1u<<5)
+#define HF_DATA_IN (1u<<6)
+#ifdef SYM_CONF_IARB_SUPPORT
+#define HF_HINT_IARB (1u<<7)
+#endif
+
+/*
+ * More host flags
+ */
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+#define HX_DMAP_DIRTY (1u<<7)
+#endif
+
+/*
+ * Global CCB HEADER.
+ *
+ * Due to lack of indirect addressing on earlier NCR chips,
+ * this substructure is copied from the ccb to a global
+ * address after selection (or reselection) and copied back
+ * before disconnect.
+ * For SYMBIOS chips that support LOAD/STORE this copy is
+ * not needed and thus not performed.
+ */
+
+struct sym_ccbh {
+ /*
+ * Start and restart SCRIPTS addresses (must be at 0).
+ */
+/*0*/ struct sym_actscr go;
+
+ /*
+ * SCRIPTS jump address that deal with data pointers.
+ * 'savep' points to the position in the script responsible
+ * for the actual transfer of data.
+ * It's written on reception of a SAVE_DATA_POINTER message.
+ */
+ u32 savep; /* Jump address to saved data pointer */
+ u32 lastp; /* SCRIPTS address at end of data */
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ u32 wlastp;
+#endif
+
+ /*
+ * Status fields.
+ */
+ u8 status[4];
+};
+
+/*
+ * GET/SET the value of the data pointer used by SCRIPTS.
+ *
+ * We must distinguish between the LOAD/STORE-based SCRIPTS
+ * that use directly the header in the CCB, and the NCR-GENERIC
+ * SCRIPTS that use the copy of the header in the HCB.
+ */
+#if SYM_CONF_GENERIC_SUPPORT
+#define sym_set_script_dp(np, cp, dp) \
+ do { \
+ if (np->features & FE_LDSTR) \
+ cp->phys.head.lastp = cpu_to_scr(dp); \
+ else \
+ np->ccb_head.lastp = cpu_to_scr(dp); \
+ } while (0)
+#define sym_get_script_dp(np, cp) \
+ scr_to_cpu((np->features & FE_LDSTR) ? \
+ cp->phys.head.lastp : np->ccb_head.lastp)
+#else
+#define sym_set_script_dp(np, cp, dp) \
+ do { \
+ cp->phys.head.lastp = cpu_to_scr(dp); \
+ } while (0)
+
+#define sym_get_script_dp(np, cp) (cp->phys.head.lastp)
+#endif
+
+/*
+ * Data Structure Block
+ *
+ * During execution of a ccb by the script processor, the
+ * DSA (data structure address) register points to this
+ * substructure of the ccb.
+ */
+struct sym_dsb {
+ /*
+ * CCB header.
+ * Also assumed at offset 0 of the sym_ccb structure.
+ */
+/*0*/ struct sym_ccbh head;
+
+ /*
+ * Phase mismatch contexts.
+ * We need two to handle correctly the SAVED DATA POINTER.
+ * MUST BOTH BE AT OFFSET < 256, due to using 8 bit arithmetic
+ * for address calculation from SCRIPTS.
+ */
+ struct sym_pmc pm0;
+ struct sym_pmc pm1;
+
+ /*
+ * Table data for Script
+ */
+ struct sym_tblsel select;
+ struct sym_tblmove smsg;
+ struct sym_tblmove smsg_ext;
+ struct sym_tblmove cmd;
+ struct sym_tblmove sense;
+ struct sym_tblmove wresid;
+ struct sym_tblmove data [SYM_CONF_MAX_SG];
+};
+
+/*
+ * Our Command Control Block
+ */
+struct sym_ccb {
+ /*
+ * This is the data structure which is pointed by the DSA
+ * register when it is executed by the script processor.
+ * It must be the first entry.
+ */
+ struct sym_dsb phys;
+
+ /*
+ * Pointer to CAM ccb and related stuff.
+ */
+ struct scsi_cmnd *cmd; /* CAM scsiio ccb */
+ u8 cdb_buf[16]; /* Copy of CDB */
+#define SYM_SNS_BBUF_LEN 32
+ u8 sns_bbuf[SYM_SNS_BBUF_LEN]; /* Bounce buffer for sense data */
+ int data_len; /* Total data length */
+ int segments; /* Number of SG segments */
+
+ u8 order; /* Tag type (if tagged command) */
+
+ /*
+ * Miscellaneous status'.
+ */
+ u_char nego_status; /* Negotiation status */
+ u_char xerr_status; /* Extended error flags */
+ u32 extra_bytes; /* Extraneous bytes transferred */
+
+ /*
+ * Message areas.
+ * We prepare a message to be sent after selection.
+ * We may use a second one if the command is rescheduled
+ * due to CHECK_CONDITION or COMMAND TERMINATED.
+ * Contents are IDENTIFY and SIMPLE_TAG.
+ * While negotiating sync or wide transfer,
+ * a SDTR or WDTR message is appended.
+ */
+ u_char scsi_smsg [12];
+ u_char scsi_smsg2[12];
+
+ /*
+ * Auto request sense related fields.
+ */
+ u_char sensecmd[6]; /* Request Sense command */
+ u_char sv_scsi_status; /* Saved SCSI status */
+ u_char sv_xerr_status; /* Saved extended status */
+ int sv_resid; /* Saved residual */
+
+ /*
+ * Other fields.
+ */
+ u32 ccb_ba; /* BUS address of this CCB */
+ u_short tag; /* Tag for this transfer */
+ /* NO_TAG means no tag */
+ u_char target;
+ u_char lun;
+ struct sym_ccb *link_ccbh; /* Host adapter CCB hash chain */
+ SYM_QUEHEAD link_ccbq; /* Link to free/busy CCB queue */
+ u32 startp; /* Initial data pointer */
+ u32 goalp; /* Expected last data pointer */
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ u32 wgoalp;
+#endif
+ int ext_sg; /* Extreme data pointer, used */
+ int ext_ofs; /* to calculate the residual. */
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ SYM_QUEHEAD link2_ccbq; /* Link for device queueing */
+ u_char started; /* CCB queued to the squeue */
+#endif
+ u_char to_abort; /* Want this IO to be aborted */
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+ u_char tags_si; /* Lun tags sum index (0,1) */
+#endif
+};
+
+#define CCB_BA(cp,lbl) (cp->ccb_ba + offsetof(struct sym_ccb, lbl))
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+#define sym_goalp(cp) ((cp->host_flags & HF_DATA_IN) ? cp->goalp : cp->wgoalp)
+#else
+#define sym_goalp(cp) (cp->goalp)
+#endif
+
+typedef struct device *m_pool_ident_t;
+
+/*
+ * Host Control Block
+ */
+struct sym_hcb {
+ /*
+ * Global headers.
+ * Due to poorness of addressing capabilities, earlier
+ * chips (810, 815, 825) copy part of the data structures
+ * (CCB, TCB and LCB) in fixed areas.
+ */
+#if SYM_CONF_GENERIC_SUPPORT
+ struct sym_ccbh ccb_head;
+ struct sym_tcbh tcb_head;
+ struct sym_lcbh lcb_head;
+#endif
+ /*
+ * Idle task and invalid task actions and
+ * their bus addresses.
+ */
+ struct sym_actscr idletask, notask, bad_itl, bad_itlq;
+ u32 idletask_ba, notask_ba, bad_itl_ba, bad_itlq_ba;
+
+ /*
+ * Dummy lun table to protect us against target
+ * returning bad lun number on reselection.
+ */
+ u32 *badluntbl; /* Table physical address */
+ u32 badlun_sa; /* SCRIPT handler BUS address */
+
+ /*
+ * Bus address of this host control block.
+ */
+ u32 hcb_ba;
+
+ /*
+ * Bit 32-63 of the on-chip RAM bus address in LE format.
+ * The START_RAM64 script loads the MMRS and MMWS from this
+ * field.
+ */
+ u32 scr_ram_seg;
+
+ /*
+ * Initial value of some IO register bits.
+ * These values are assumed to have been set by BIOS, and may
+ * be used to probe adapter implementation differences.
+ */
+ u_char sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4,
+ sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4, sv_scntl4,
+ sv_stest1;
+
+ /*
+ * Actual initial value of IO register bits used by the
+ * driver. They are loaded at initialisation according to
+ * features that are to be enabled/disabled.
+ */
+ u_char rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest3, rv_ctest4,
+ rv_ctest5, rv_stest2, rv_ccntl0, rv_ccntl1, rv_scntl4;
+
+ /*
+ * Target data.
+ */
+ struct sym_tcb target[SYM_CONF_MAX_TARGET];
+
+ /*
+ * Target control block bus address array used by the SCRIPT
+ * on reselection.
+ */
+ u32 *targtbl;
+ u32 targtbl_ba;
+
+ /*
+ * DMA pool handle for this HBA.
+ */
+ m_pool_ident_t bus_dmat;
+
+ /*
+ * O/S specific data structure
+ */
+ struct sym_shcb s;
+
+ /*
+ * Physical bus addresses of the chip.
+ */
+ u32 mmio_ba; /* MMIO 32 bit BUS address */
+ int mmio_ws; /* MMIO Window size */
+
+ u32 ram_ba; /* RAM 32 bit BUS address */
+ int ram_ws; /* RAM window size */
+
+ /*
+ * SCRIPTS virtual and physical bus addresses.
+ * 'script' is loaded in the on-chip RAM if present.
+ * 'scripth' stays in main memory for all chips except the
+ * 53C895A, 53C896 and 53C1010 that provide 8K on-chip RAM.
+ */
+ u_char *scripta0; /* Copy of scripts A, B, Z */
+ u_char *scriptb0;
+ u_char *scriptz0;
+ u32 scripta_ba; /* Actual scripts A, B, Z */
+ u32 scriptb_ba; /* 32 bit bus addresses. */
+ u32 scriptz_ba;
+ u_short scripta_sz; /* Actual size of script A, B, Z*/
+ u_short scriptb_sz;
+ u_short scriptz_sz;
+
+ /*
+ * Bus addresses, setup and patch methods for
+ * the selected firmware.
+ */
+ struct sym_fwa_ba fwa_bas; /* Useful SCRIPTA bus addresses */
+ struct sym_fwb_ba fwb_bas; /* Useful SCRIPTB bus addresses */
+ struct sym_fwz_ba fwz_bas; /* Useful SCRIPTZ bus addresses */
+ void (*fw_setup)(struct sym_hcb *np, struct sym_fw *fw);
+ void (*fw_patch)(struct sym_hcb *np);
+ char *fw_name;
+
+ /*
+ * General controller parameters and configuration.
+ */
+ u_short device_id; /* PCI device id */
+ u_char revision_id; /* PCI device revision id */
+ u_int features; /* Chip features map */
+ u_char myaddr; /* SCSI id of the adapter */
+ u_char maxburst; /* log base 2 of dwords burst */
+ u_char maxwide; /* Maximum transfer width */
+ u_char minsync; /* Min sync period factor (ST) */
+ u_char maxsync; /* Max sync period factor (ST) */
+ u_char maxoffs; /* Max scsi offset (ST) */
+ u_char minsync_dt; /* Min sync period factor (DT) */
+ u_char maxsync_dt; /* Max sync period factor (DT) */
+ u_char maxoffs_dt; /* Max scsi offset (DT) */
+ u_char multiplier; /* Clock multiplier (1,2,4) */
+ u_char clock_divn; /* Number of clock divisors */
+ u32 clock_khz; /* SCSI clock frequency in KHz */
+ u32 pciclk_khz; /* Estimated PCI clock in KHz */
+ /*
+ * Start queue management.
+ * It is filled up by the host processor and accessed by the
+ * SCRIPTS processor in order to start SCSI commands.
+ */
+ volatile /* Prevent code optimizations */
+ u32 *squeue; /* Start queue virtual address */
+ u32 squeue_ba; /* Start queue BUS address */
+ u_short squeueput; /* Next free slot of the queue */
+ u_short actccbs; /* Number of allocated CCBs */
+
+ /*
+ * Command completion queue.
+ * It is the same size as the start queue to avoid overflow.
+ */
+ u_short dqueueget; /* Next position to scan */
+ volatile /* Prevent code optimizations */
+ u32 *dqueue; /* Completion (done) queue */
+ u32 dqueue_ba; /* Done queue BUS address */
+
+ /*
+ * Miscellaneous buffers accessed by the scripts-processor.
+ * They shall be DWORD aligned, because they may be read or
+ * written with a script command.
+ */
+ u_char msgout[8]; /* Buffer for MESSAGE OUT */
+ u_char msgin [8]; /* Buffer for MESSAGE IN */
+ u32 lastmsg; /* Last SCSI message sent */
+ u32 scratch; /* Scratch for SCSI receive */
+ /* Also used for cache test */
+ /*
+ * Miscellaneous configuration and status parameters.
+ */
+ u_char usrflags; /* Miscellaneous user flags */
+ u_char scsi_mode; /* Current SCSI BUS mode */
+ u_char verbose; /* Verbosity for this controller*/
+
+ /*
+ * CCB lists and queue.
+ */
+ struct sym_ccb **ccbh; /* CCBs hashed by DSA value */
+ /* CCB_HASH_SIZE lists of CCBs */
+ SYM_QUEHEAD free_ccbq; /* Queue of available CCBs */
+ SYM_QUEHEAD busy_ccbq; /* Queue of busy CCBs */
+
+ /*
+ * During error handling and/or recovery,
+ * active CCBs that are to be completed with
+ * error or requeued are moved from the busy_ccbq
+ * to the comp_ccbq prior to completion.
+ */
+ SYM_QUEHEAD comp_ccbq;
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+ SYM_QUEHEAD dummy_ccbq;
+#endif
+
+ /*
+ * IMMEDIATE ARBITRATION (IARB) control.
+ *
+ * We keep track in 'last_cp' of the last CCB that has been
+ * queued to the SCRIPTS processor and clear 'last_cp' when
+ * this CCB completes. If last_cp is not zero at the moment
+ * we queue a new CCB, we set a flag in 'last_cp' that is
+ * used by the SCRIPTS as a hint for setting IARB.
+ * We donnot set more than 'iarb_max' consecutive hints for
+ * IARB in order to leave devices a chance to reselect.
+ * By the way, any non zero value of 'iarb_max' is unfair. :)
+ */
+#ifdef SYM_CONF_IARB_SUPPORT
+ u_short iarb_max; /* Max. # consecutive IARB hints*/
+ u_short iarb_count; /* Actual # of these hints */
+ struct sym_ccb * last_cp;
+#endif
+
+ /*
+ * Command abort handling.
+ * We need to synchronize tightly with the SCRIPTS
+ * processor in order to handle things correctly.
+ */
+ u_char abrt_msg[4]; /* Message to send buffer */
+ struct sym_tblmove abrt_tbl; /* Table for the MOV of it */
+ struct sym_tblsel abrt_sel; /* Sync params for selection */
+ u_char istat_sem; /* Tells the chip to stop (SEM) */
+
+ /*
+ * 64 bit DMA handling.
+ */
+#if SYM_CONF_DMA_ADDRESSING_MODE != 0
+ u_char use_dac; /* Use PCI DAC cycles */
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+ u_char dmap_dirty; /* Dma segments registers dirty */
+ u32 dmap_bah[SYM_DMAP_SIZE];/* Segment registers map */
+#endif
+#endif
+};
+
+#define HCB_BA(np, lbl) (np->hcb_ba + offsetof(struct sym_hcb, lbl))
+
+
+/*
+ * FIRMWARES (sym_fw.c)
+ */
+struct sym_fw * sym_find_firmware(struct sym_chip *chip);
+void sym_fw_bind_script(struct sym_hcb *np, u32 *start, int len);
+
+/*
+ * Driver methods called from O/S specific code.
+ */
+char *sym_driver_name(void);
+void sym_print_xerr(struct scsi_cmnd *cmd, int x_status);
+int sym_reset_scsi_bus(struct sym_hcb *np, int enab_int);
+struct sym_chip *sym_lookup_chip_table(u_short device_id, u_char revision);
+void sym_put_start_queue(struct sym_hcb *np, struct sym_ccb *cp);
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+void sym_start_next_ccbs(struct sym_hcb *np, struct sym_lcb *lp, int maxn);
+#endif
+void sym_start_up(struct sym_hcb *np, int reason);
+void sym_interrupt(struct sym_hcb *np);
+int sym_clear_tasks(struct sym_hcb *np, int cam_status, int target, int lun, int task);
+struct sym_ccb *sym_get_ccb(struct sym_hcb *np, struct scsi_cmnd *cmd, u_char tag_order);
+void sym_free_ccb(struct sym_hcb *np, struct sym_ccb *cp);
+struct sym_lcb *sym_alloc_lcb(struct sym_hcb *np, u_char tn, u_char ln);
+int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp);
+int sym_abort_scsiio(struct sym_hcb *np, struct scsi_cmnd *ccb, int timed_out);
+int sym_reset_scsi_target(struct sym_hcb *np, int target);
+void sym_hcb_free(struct sym_hcb *np);
+int sym_hcb_attach(struct Scsi_Host *shost, struct sym_fw *fw, struct sym_nvram *nvram);
+
+/*
+ * Build a scatter/gather entry.
+ *
+ * For 64 bit systems, we use the 8 upper bits of the size field
+ * to provide bus address bits 32-39 to the SCRIPTS processor.
+ * This allows the 895A, 896, 1010 to address up to 1 TB of memory.
+ */
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 0
+#define sym_build_sge(np, data, badd, len) \
+do { \
+ (data)->addr = cpu_to_scr(badd); \
+ (data)->size = cpu_to_scr(len); \
+} while (0)
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 1
+#define sym_build_sge(np, data, badd, len) \
+do { \
+ (data)->addr = cpu_to_scr(badd); \
+ (data)->size = cpu_to_scr((((badd) >> 8) & 0xff000000) + len); \
+} while (0)
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
+int sym_lookup_dmap(struct sym_hcb *np, u32 h, int s);
+static __inline void
+sym_build_sge(struct sym_hcb *np, struct sym_tblmove *data, u64 badd, int len)
+{
+ u32 h = (badd>>32);
+ int s = (h&SYM_DMAP_MASK);
+
+ if (h != np->dmap_bah[s])
+ goto bad;
+good:
+ (data)->addr = cpu_to_scr(badd);
+ (data)->size = cpu_to_scr((s<<24) + len);
+ return;
+bad:
+ s = sym_lookup_dmap(np, h, s);
+ goto good;
+}
+#else
+#error "Unsupported DMA addressing mode"
+#endif
+
+/*
+ * Set up data pointers used by SCRIPTS.
+ * Called from O/S specific code.
+ */
+static inline void sym_setup_data_pointers(struct sym_hcb *np,
+ struct sym_ccb *cp, int dir)
+{
+ u32 lastp, goalp;
+
+ /*
+ * No segments means no data.
+ */
+ if (!cp->segments)
+ dir = CAM_DIR_NONE;
+
+ /*
+ * Set the data pointer.
+ */
+ switch(dir) {
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ case CAM_DIR_UNKNOWN:
+#endif
+ case CAM_DIR_OUT:
+ goalp = SCRIPTA_BA(np, data_out2) + 8;
+ lastp = goalp - 8 - (cp->segments * (2*4));
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ cp->wgoalp = cpu_to_scr(goalp);
+ if (dir != CAM_DIR_UNKNOWN)
+ break;
+ cp->phys.head.wlastp = cpu_to_scr(lastp);
+ /* fall through */
+#else
+ break;
+#endif
+ case CAM_DIR_IN:
+ cp->host_flags |= HF_DATA_IN;
+ goalp = SCRIPTA_BA(np, data_in2) + 8;
+ lastp = goalp - 8 - (cp->segments * (2*4));
+ break;
+ case CAM_DIR_NONE:
+ default:
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ cp->host_flags |= HF_DATA_IN;
+#endif
+ lastp = goalp = SCRIPTB_BA(np, no_data);
+ break;
+ }
+
+ /*
+ * Set all pointers values needed by SCRIPTS.
+ */
+ cp->phys.head.lastp = cpu_to_scr(lastp);
+ cp->phys.head.savep = cpu_to_scr(lastp);
+ cp->startp = cp->phys.head.savep;
+ cp->goalp = cpu_to_scr(goalp);
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+ /*
+ * If direction is unknown, start at data_io.
+ */
+ if (dir == CAM_DIR_UNKNOWN)
+ cp->phys.head.savep = cpu_to_scr(SCRIPTB_BA(np, data_io));
+#endif
+}
+
+/*
+ * MEMORY ALLOCATOR.
+ */
+
+#define SYM_MEM_PAGE_ORDER 0 /* 1 PAGE maximum */
+#define SYM_MEM_CLUSTER_SHIFT (PAGE_SHIFT+SYM_MEM_PAGE_ORDER)
+#define SYM_MEM_FREE_UNUSED /* Free unused pages immediately */
+
+#define SYM_MEM_WARN 1 /* Warn on failed operations */
+
+#define sym_get_mem_cluster() \
+ (void *) __get_free_pages(GFP_ATOMIC, SYM_MEM_PAGE_ORDER)
+#define sym_free_mem_cluster(p) \
+ free_pages((unsigned long)p, SYM_MEM_PAGE_ORDER)
+
+/*
+ * Link between free memory chunks of a given size.
+ */
+typedef struct sym_m_link {
+ struct sym_m_link *next;
+} *m_link_p;
+
+/*
+ * Virtual to bus physical translation for a given cluster.
+ * Such a structure is only useful with DMA abstraction.
+ */
+typedef struct sym_m_vtob { /* Virtual to Bus address translation */
+ struct sym_m_vtob *next;
+ void *vaddr; /* Virtual address */
+ dma_addr_t baddr; /* Bus physical address */
+} *m_vtob_p;
+
+/* Hash this stuff a bit to speed up translations */
+#define VTOB_HASH_SHIFT 5
+#define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK (VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m) \
+ ((((unsigned long)(m)) >> SYM_MEM_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+
+/*
+ * Memory pool of a given kind.
+ * Ideally, we want to use:
+ * 1) 1 pool for memory we donnot need to involve in DMA.
+ * 2) The same pool for controllers that require same DMA
+ * constraints and features.
+ * The OS specific m_pool_id_t thing and the sym_m_pool_match()
+ * method are expected to tell the driver about.
+ */
+typedef struct sym_m_pool {
+ m_pool_ident_t dev_dmat; /* Identifies the pool (see above) */
+ void * (*get_mem_cluster)(struct sym_m_pool *);
+#ifdef SYM_MEM_FREE_UNUSED
+ void (*free_mem_cluster)(struct sym_m_pool *, void *);
+#endif
+#define M_GET_MEM_CLUSTER() mp->get_mem_cluster(mp)
+#define M_FREE_MEM_CLUSTER(p) mp->free_mem_cluster(mp, p)
+ int nump;
+ m_vtob_p vtob[VTOB_HASH_SIZE];
+ struct sym_m_pool *next;
+ struct sym_m_link h[SYM_MEM_CLUSTER_SHIFT - SYM_MEM_SHIFT + 1];
+} *m_pool_p;
+
+/*
+ * Alloc, free and translate addresses to bus physical
+ * for DMAable memory.
+ */
+void *__sym_calloc_dma(m_pool_ident_t dev_dmat, int size, char *name);
+void __sym_mfree_dma(m_pool_ident_t dev_dmat, void *m, int size, char *name);
+dma_addr_t __vtobus(m_pool_ident_t dev_dmat, void *m);
+
+/*
+ * Verbs used by the driver code for DMAable memory handling.
+ * The _uvptv_ macro avoids a nasty warning about pointer to volatile
+ * being discarded.
+ */
+#define _uvptv_(p) ((void *)((u_long)(p)))
+
+#define _sym_calloc_dma(np, l, n) __sym_calloc_dma(np->bus_dmat, l, n)
+#define _sym_mfree_dma(np, p, l, n) \
+ __sym_mfree_dma(np->bus_dmat, _uvptv_(p), l, n)
+#define sym_calloc_dma(l, n) _sym_calloc_dma(np, l, n)
+#define sym_mfree_dma(p, l, n) _sym_mfree_dma(np, p, l, n)
+#define vtobus(p) __vtobus(np->bus_dmat, _uvptv_(p))
+
+/*
+ * We have to provide the driver memory allocator with methods for
+ * it to maintain virtual to bus physical address translations.
+ */
+
+#define sym_m_pool_match(mp_id1, mp_id2) (mp_id1 == mp_id2)
+
+static __inline void *sym_m_get_dma_mem_cluster(m_pool_p mp, m_vtob_p vbp)
+{
+ void *vaddr = NULL;
+ dma_addr_t baddr = 0;
+
+ vaddr = dma_alloc_coherent(mp->dev_dmat, SYM_MEM_CLUSTER_SIZE, &baddr,
+ GFP_ATOMIC);
+ if (vaddr) {
+ vbp->vaddr = vaddr;
+ vbp->baddr = baddr;
+ }
+ return vaddr;
+}
+
+static __inline void sym_m_free_dma_mem_cluster(m_pool_p mp, m_vtob_p vbp)
+{
+ dma_free_coherent(mp->dev_dmat, SYM_MEM_CLUSTER_SIZE, vbp->vaddr,
+ vbp->baddr);
+}
+
+#endif /* SYM_HIPD_H */
diff --git a/drivers/scsi/sym53c8xx_2/sym_malloc.c b/drivers/scsi/sym53c8xx_2/sym_malloc.c
new file mode 100644
index 000000000000..a34d403ccc6c
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_malloc.c
@@ -0,0 +1,382 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+/*
+ * Simple power of two buddy-like generic allocator.
+ * Provides naturally aligned memory chunks.
+ *
+ * This simple code is not intended to be fast, but to
+ * provide power of 2 aligned memory allocations.
+ * Since the SCRIPTS processor only supplies 8 bit arithmetic,
+ * this allocator allows simple and fast address calculations
+ * from the SCRIPTS code. In addition, cache line alignment
+ * is guaranteed for power of 2 cache line size.
+ *
+ * This allocator has been developped for the Linux sym53c8xx
+ * driver, since this O/S does not provide naturally aligned
+ * allocations.
+ * It has the advantage of allowing the driver to use private
+ * pages of memory that will be useful if we ever need to deal
+ * with IO MMUs for PCI.
+ */
+static void *___sym_malloc(m_pool_p mp, int size)
+{
+ int i = 0;
+ int s = (1 << SYM_MEM_SHIFT);
+ int j;
+ void *a;
+ m_link_p h = mp->h;
+
+ if (size > SYM_MEM_CLUSTER_SIZE)
+ return NULL;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ j = i;
+ while (!h[j].next) {
+ if (s == SYM_MEM_CLUSTER_SIZE) {
+ h[j].next = (m_link_p) M_GET_MEM_CLUSTER();
+ if (h[j].next)
+ h[j].next->next = NULL;
+ break;
+ }
+ ++j;
+ s <<= 1;
+ }
+ a = h[j].next;
+ if (a) {
+ h[j].next = h[j].next->next;
+ while (j > i) {
+ j -= 1;
+ s >>= 1;
+ h[j].next = (m_link_p) (a+s);
+ h[j].next->next = NULL;
+ }
+ }
+#ifdef DEBUG
+ printf("___sym_malloc(%d) = %p\n", size, (void *) a);
+#endif
+ return a;
+}
+
+/*
+ * Counter-part of the generic allocator.
+ */
+static void ___sym_mfree(m_pool_p mp, void *ptr, int size)
+{
+ int i = 0;
+ int s = (1 << SYM_MEM_SHIFT);
+ m_link_p q;
+ unsigned long a, b;
+ m_link_p h = mp->h;
+
+#ifdef DEBUG
+ printf("___sym_mfree(%p, %d)\n", ptr, size);
+#endif
+
+ if (size > SYM_MEM_CLUSTER_SIZE)
+ return;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ a = (unsigned long)ptr;
+
+ while (1) {
+ if (s == SYM_MEM_CLUSTER_SIZE) {
+#ifdef SYM_MEM_FREE_UNUSED
+ M_FREE_MEM_CLUSTER((void *)a);
+#else
+ ((m_link_p) a)->next = h[i].next;
+ h[i].next = (m_link_p) a;
+#endif
+ break;
+ }
+ b = a ^ s;
+ q = &h[i];
+ while (q->next && q->next != (m_link_p) b) {
+ q = q->next;
+ }
+ if (!q->next) {
+ ((m_link_p) a)->next = h[i].next;
+ h[i].next = (m_link_p) a;
+ break;
+ }
+ q->next = q->next->next;
+ a = a & b;
+ s <<= 1;
+ ++i;
+ }
+}
+
+/*
+ * Verbose and zeroing allocator that wrapps to the generic allocator.
+ */
+static void *__sym_calloc2(m_pool_p mp, int size, char *name, int uflags)
+{
+ void *p;
+
+ p = ___sym_malloc(mp, size);
+
+ if (DEBUG_FLAGS & DEBUG_ALLOC) {
+ printf ("new %-10s[%4d] @%p.\n", name, size, p);
+ }
+
+ if (p)
+ memset(p, 0, size);
+ else if (uflags & SYM_MEM_WARN)
+ printf ("__sym_calloc2: failed to allocate %s[%d]\n", name, size);
+ return p;
+}
+#define __sym_calloc(mp, s, n) __sym_calloc2(mp, s, n, SYM_MEM_WARN)
+
+/*
+ * Its counter-part.
+ */
+static void __sym_mfree(m_pool_p mp, void *ptr, int size, char *name)
+{
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printf ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+ ___sym_mfree(mp, ptr, size);
+}
+
+/*
+ * Default memory pool we donnot need to involve in DMA.
+ *
+ * With DMA abstraction, we use functions (methods), to
+ * distinguish between non DMAable memory and DMAable memory.
+ */
+static void *___mp0_get_mem_cluster(m_pool_p mp)
+{
+ void *m = sym_get_mem_cluster();
+ if (m)
+ ++mp->nump;
+ return m;
+}
+
+#ifdef SYM_MEM_FREE_UNUSED
+static void ___mp0_free_mem_cluster(m_pool_p mp, void *m)
+{
+ sym_free_mem_cluster(m);
+ --mp->nump;
+}
+#else
+#define ___mp0_free_mem_cluster NULL
+#endif
+
+static struct sym_m_pool mp0 = {
+ NULL,
+ ___mp0_get_mem_cluster,
+ ___mp0_free_mem_cluster
+};
+
+/*
+ * Methods that maintains DMAable pools according to user allocations.
+ * New pools are created on the fly when a new pool id is provided.
+ * They are deleted on the fly when they get emptied.
+ */
+/* Get a memory cluster that matches the DMA constraints of a given pool */
+static void * ___get_dma_mem_cluster(m_pool_p mp)
+{
+ m_vtob_p vbp;
+ void *vaddr;
+
+ vbp = __sym_calloc(&mp0, sizeof(*vbp), "VTOB");
+ if (!vbp)
+ goto out_err;
+
+ vaddr = sym_m_get_dma_mem_cluster(mp, vbp);
+ if (vaddr) {
+ int hc = VTOB_HASH_CODE(vaddr);
+ vbp->next = mp->vtob[hc];
+ mp->vtob[hc] = vbp;
+ ++mp->nump;
+ }
+ return vaddr;
+out_err:
+ return NULL;
+}
+
+#ifdef SYM_MEM_FREE_UNUSED
+/* Free a memory cluster and associated resources for DMA */
+static void ___free_dma_mem_cluster(m_pool_p mp, void *m)
+{
+ m_vtob_p *vbpp, vbp;
+ int hc = VTOB_HASH_CODE(m);
+
+ vbpp = &mp->vtob[hc];
+ while (*vbpp && (*vbpp)->vaddr != m)
+ vbpp = &(*vbpp)->next;
+ if (*vbpp) {
+ vbp = *vbpp;
+ *vbpp = (*vbpp)->next;
+ sym_m_free_dma_mem_cluster(mp, vbp);
+ __sym_mfree(&mp0, vbp, sizeof(*vbp), "VTOB");
+ --mp->nump;
+ }
+}
+#endif
+
+/* Fetch the memory pool for a given pool id (i.e. DMA constraints) */
+static __inline m_pool_p ___get_dma_pool(m_pool_ident_t dev_dmat)
+{
+ m_pool_p mp;
+ for (mp = mp0.next;
+ mp && !sym_m_pool_match(mp->dev_dmat, dev_dmat);
+ mp = mp->next);
+ return mp;
+}
+
+/* Create a new memory DMAable pool (when fetch failed) */
+static m_pool_p ___cre_dma_pool(m_pool_ident_t dev_dmat)
+{
+ m_pool_p mp = __sym_calloc(&mp0, sizeof(*mp), "MPOOL");
+ if (mp) {
+ mp->dev_dmat = dev_dmat;
+ mp->get_mem_cluster = ___get_dma_mem_cluster;
+#ifdef SYM_MEM_FREE_UNUSED
+ mp->free_mem_cluster = ___free_dma_mem_cluster;
+#endif
+ mp->next = mp0.next;
+ mp0.next = mp;
+ return mp;
+ }
+ return NULL;
+}
+
+#ifdef SYM_MEM_FREE_UNUSED
+/* Destroy a DMAable memory pool (when got emptied) */
+static void ___del_dma_pool(m_pool_p p)
+{
+ m_pool_p *pp = &mp0.next;
+
+ while (*pp && *pp != p)
+ pp = &(*pp)->next;
+ if (*pp) {
+ *pp = (*pp)->next;
+ __sym_mfree(&mp0, p, sizeof(*p), "MPOOL");
+ }
+}
+#endif
+
+/* This lock protects only the memory allocation/free. */
+static DEFINE_SPINLOCK(sym53c8xx_lock);
+
+/*
+ * Actual allocator for DMAable memory.
+ */
+void *__sym_calloc_dma(m_pool_ident_t dev_dmat, int size, char *name)
+{
+ unsigned long flags;
+ m_pool_p mp;
+ void *m = NULL;
+
+ spin_lock_irqsave(&sym53c8xx_lock, flags);
+ mp = ___get_dma_pool(dev_dmat);
+ if (!mp)
+ mp = ___cre_dma_pool(dev_dmat);
+ if (!mp)
+ goto out;
+ m = __sym_calloc(mp, size, name);
+#ifdef SYM_MEM_FREE_UNUSED
+ if (!mp->nump)
+ ___del_dma_pool(mp);
+#endif
+
+ out:
+ spin_unlock_irqrestore(&sym53c8xx_lock, flags);
+ return m;
+}
+
+void __sym_mfree_dma(m_pool_ident_t dev_dmat, void *m, int size, char *name)
+{
+ unsigned long flags;
+ m_pool_p mp;
+
+ spin_lock_irqsave(&sym53c8xx_lock, flags);
+ mp = ___get_dma_pool(dev_dmat);
+ if (!mp)
+ goto out;
+ __sym_mfree(mp, m, size, name);
+#ifdef SYM_MEM_FREE_UNUSED
+ if (!mp->nump)
+ ___del_dma_pool(mp);
+#endif
+ out:
+ spin_unlock_irqrestore(&sym53c8xx_lock, flags);
+}
+
+/*
+ * Actual virtual to bus physical address translator
+ * for 32 bit addressable DMAable memory.
+ */
+dma_addr_t __vtobus(m_pool_ident_t dev_dmat, void *m)
+{
+ unsigned long flags;
+ m_pool_p mp;
+ int hc = VTOB_HASH_CODE(m);
+ m_vtob_p vp = NULL;
+ void *a = (void *)((unsigned long)m & ~SYM_MEM_CLUSTER_MASK);
+ dma_addr_t b;
+
+ spin_lock_irqsave(&sym53c8xx_lock, flags);
+ mp = ___get_dma_pool(dev_dmat);
+ if (mp) {
+ vp = mp->vtob[hc];
+ while (vp && vp->vaddr != a)
+ vp = vp->next;
+ }
+ if (!vp)
+ panic("sym: VTOBUS FAILED!\n");
+ b = vp->baddr + (m - a);
+ spin_unlock_irqrestore(&sym53c8xx_lock, flags);
+ return b;
+}
diff --git a/drivers/scsi/sym53c8xx_2/sym_misc.h b/drivers/scsi/sym53c8xx_2/sym_misc.h
new file mode 100644
index 000000000000..0433d5d0caf3
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_misc.h
@@ -0,0 +1,192 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM_MISC_H
+#define SYM_MISC_H
+
+/*
+ * A la VMS/CAM-3 queue management.
+ */
+typedef struct sym_quehead {
+ struct sym_quehead *flink; /* Forward pointer */
+ struct sym_quehead *blink; /* Backward pointer */
+} SYM_QUEHEAD;
+
+#define sym_que_init(ptr) do { \
+ (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static __inline struct sym_quehead *sym_que_first(struct sym_quehead *head)
+{
+ return (head->flink == head) ? 0 : head->flink;
+}
+
+static __inline struct sym_quehead *sym_que_last(struct sym_quehead *head)
+{
+ return (head->blink == head) ? 0 : head->blink;
+}
+
+static __inline void __sym_que_add(struct sym_quehead * new,
+ struct sym_quehead * blink,
+ struct sym_quehead * flink)
+{
+ flink->blink = new;
+ new->flink = flink;
+ new->blink = blink;
+ blink->flink = new;
+}
+
+static __inline void __sym_que_del(struct sym_quehead * blink,
+ struct sym_quehead * flink)
+{
+ flink->blink = blink;
+ blink->flink = flink;
+}
+
+static __inline int sym_que_empty(struct sym_quehead *head)
+{
+ return head->flink == head;
+}
+
+static __inline void sym_que_splice(struct sym_quehead *list,
+ struct sym_quehead *head)
+{
+ struct sym_quehead *first = list->flink;
+
+ if (first != list) {
+ struct sym_quehead *last = list->blink;
+ struct sym_quehead *at = head->flink;
+
+ first->blink = head;
+ head->flink = first;
+
+ last->flink = at;
+ at->blink = last;
+ }
+}
+
+static __inline void sym_que_move(struct sym_quehead *orig,
+ struct sym_quehead *dest)
+{
+ struct sym_quehead *first, *last;
+
+ first = orig->flink;
+ if (first != orig) {
+ first->blink = dest;
+ dest->flink = first;
+ last = orig->blink;
+ last->flink = dest;
+ dest->blink = last;
+ orig->flink = orig;
+ orig->blink = orig;
+ } else {
+ dest->flink = dest;
+ dest->blink = dest;
+ }
+}
+
+#define sym_que_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned int)(&((type *)0)->member)))
+
+
+#define sym_insque(new, pos) __sym_que_add(new, pos, (pos)->flink)
+
+#define sym_remque(el) __sym_que_del((el)->blink, (el)->flink)
+
+#define sym_insque_head(new, head) __sym_que_add(new, head, (head)->flink)
+
+static __inline struct sym_quehead *sym_remque_head(struct sym_quehead *head)
+{
+ struct sym_quehead *elem = head->flink;
+
+ if (elem != head)
+ __sym_que_del(head, elem->flink);
+ else
+ elem = NULL;
+ return elem;
+}
+
+#define sym_insque_tail(new, head) __sym_que_add(new, (head)->blink, head)
+
+static __inline struct sym_quehead *sym_remque_tail(struct sym_quehead *head)
+{
+ struct sym_quehead *elem = head->blink;
+
+ if (elem != head)
+ __sym_que_del(elem->blink, head);
+ else
+ elem = 0;
+ return elem;
+}
+
+/*
+ * This one may be useful.
+ */
+#define FOR_EACH_QUEUED_ELEMENT(head, qp) \
+ for (qp = (head)->flink; qp != (head); qp = qp->flink)
+/*
+ * FreeBSD does not offer our kind of queue in the CAM CCB.
+ * So, we have to cast.
+ */
+#define sym_qptr(p) ((struct sym_quehead *) (p))
+
+/*
+ * Simple bitmap operations.
+ */
+#define sym_set_bit(p, n) (((u32 *)(p))[(n)>>5] |= (1<<((n)&0x1f)))
+#define sym_clr_bit(p, n) (((u32 *)(p))[(n)>>5] &= ~(1<<((n)&0x1f)))
+#define sym_is_bit(p, n) (((u32 *)(p))[(n)>>5] & (1<<((n)&0x1f)))
+
+/*
+ * The below round up/down macros are to be used with a constant
+ * as argument (sizeof(...) for example), for the compiler to
+ * optimize the whole thing.
+ */
+#define _U_(a,m) (a)<=(1<<m)?m:
+
+/*
+ * Round up logarithm to base 2 of a 16 bit constant.
+ */
+#define _LGRU16_(a) \
+( \
+ _U_(a, 0)_U_(a, 1)_U_(a, 2)_U_(a, 3)_U_(a, 4)_U_(a, 5)_U_(a, 6)_U_(a, 7) \
+ _U_(a, 8)_U_(a, 9)_U_(a,10)_U_(a,11)_U_(a,12)_U_(a,13)_U_(a,14)_U_(a,15) \
+ 16)
+
+#endif /* SYM_MISC_H */
diff --git a/drivers/scsi/sym53c8xx_2/sym_nvram.c b/drivers/scsi/sym53c8xx_2/sym_nvram.c
new file mode 100644
index 000000000000..1b721e3ec520
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_nvram.c
@@ -0,0 +1,771 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sym_glue.h"
+#include "sym_nvram.h"
+
+#ifdef SYM_CONF_DEBUG_NVRAM
+static u_char Tekram_boot_delay[7] = {3, 5, 10, 20, 30, 60, 120};
+#endif
+
+/*
+ * Get host setup from NVRAM.
+ */
+void sym_nvram_setup_host(struct Scsi_Host *shost, struct sym_hcb *np, struct sym_nvram *nvram)
+{
+ /*
+ * Get parity checking, host ID, verbose mode
+ * and miscellaneous host flags from NVRAM.
+ */
+ switch (nvram->type) {
+ case SYM_SYMBIOS_NVRAM:
+ if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+ np->rv_scntl0 &= ~0x0a;
+ np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+ if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+ np->verbose += 1;
+ if (nvram->data.Symbios.flags1 & SYMBIOS_SCAN_HI_LO)
+ shost->reverse_ordering = 1;
+ if (nvram->data.Symbios.flags2 & SYMBIOS_AVOID_BUS_RESET)
+ np->usrflags |= SYM_AVOID_BUS_RESET;
+ break;
+ case SYM_TEKRAM_NVRAM:
+ np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+ break;
+#ifdef CONFIG_PARISC
+ case SYM_PARISC_PDC:
+ if (nvram->data.parisc.host_id != -1)
+ np->myaddr = nvram->data.parisc.host_id;
+ if (nvram->data.parisc.factor != -1)
+ np->minsync = nvram->data.parisc.factor;
+ if (nvram->data.parisc.width != -1)
+ np->maxwide = nvram->data.parisc.width;
+ switch (nvram->data.parisc.mode) {
+ case 0: np->scsi_mode = SMODE_SE; break;
+ case 1: np->scsi_mode = SMODE_HVD; break;
+ case 2: np->scsi_mode = SMODE_LVD; break;
+ default: break;
+ }
+#endif
+ default:
+ break;
+ }
+}
+
+/*
+ * Get target set-up from Symbios format NVRAM.
+ */
+static void
+sym_Symbios_setup_target(struct sym_hcb *np, int target, Symbios_nvram *nvram)
+{
+ struct sym_tcb *tp = &np->target[target];
+ Symbios_target *tn = &nvram->target[target];
+
+ tp->usrtags =
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SYM_SETUP_MAX_TAG : 0;
+
+ if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+ tp->usrflags &= ~SYM_DISC_ENABLED;
+ if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+ tp->usrflags |= SYM_SCAN_BOOT_DISABLED;
+ if (!(tn->flags & SYMBIOS_SCAN_LUNS))
+ tp->usrflags |= SYM_SCAN_LUNS_DISABLED;
+}
+
+/*
+ * Get target set-up from Tekram format NVRAM.
+ */
+static void
+sym_Tekram_setup_target(struct sym_hcb *np, int target, Tekram_nvram *nvram)
+{
+ struct sym_tcb *tp = &np->target[target];
+ struct Tekram_target *tn = &nvram->target[target];
+
+ if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+ tp->usrtags = 2 << nvram->max_tags_index;
+ }
+
+ if (tn->flags & TEKRAM_DISCONNECT_ENABLE)
+ tp->usrflags |= SYM_DISC_ENABLED;
+
+ /* If any device does not support parity, we will not use this option */
+ if (!(tn->flags & TEKRAM_PARITY_CHECK))
+ np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */
+}
+
+/*
+ * Get target setup from NVRAM.
+ */
+void sym_nvram_setup_target(struct sym_hcb *np, int target, struct sym_nvram *nvp)
+{
+ switch (nvp->type) {
+ case SYM_SYMBIOS_NVRAM:
+ sym_Symbios_setup_target(np, target, &nvp->data.Symbios);
+ break;
+ case SYM_TEKRAM_NVRAM:
+ sym_Tekram_setup_target(np, target, &nvp->data.Tekram);
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef SYM_CONF_DEBUG_NVRAM
+/*
+ * Dump Symbios format NVRAM for debugging purpose.
+ */
+static void sym_display_Symbios_nvram(struct sym_device *np, Symbios_nvram *nvram)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printf("%s: HOST ID=%d%s%s%s%s%s%s\n",
+ sym_name(np), nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERBOSE" :"",
+ (nvram->flags & SYMBIOS_CHS_MAPPING) ? " CHS_ALT" :"",
+ (nvram->flags2 & SYMBIOS_AVOID_BUS_RESET)?" NO_RESET" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ sym_name(np), i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+/*
+ * Dump TEKRAM format NVRAM for debugging purpose.
+ */
+static void sym_display_Tekram_nvram(struct sym_device *np, Tekram_nvram *nvram)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch ((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ sym_name(np), nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = Tekram_sync[j];
+ printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ sym_name(np), i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#else
+static void sym_display_Symbios_nvram(struct sym_device *np, Symbios_nvram *nvram) { (void)np; (void)nvram; }
+static void sym_display_Tekram_nvram(struct sym_device *np, Tekram_nvram *nvram) { (void)np; (void)nvram; }
+#endif /* SYM_CONF_DEBUG_NVRAM */
+
+
+/*
+ * 24C16 EEPROM reading.
+ *
+ * GPOI0 - data in/data out
+ * GPIO1 - clock
+ * Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+static void S24C16_set_bit(struct sym_device *np, u_char write_bit, u_char *gpreg,
+ int bit_mode)
+{
+ udelay(5);
+ switch (bit_mode) {
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB(np, nc_gpreg, *gpreg);
+ udelay(5);
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+static void S24C16_start(struct sym_device *np, u_char *gpreg)
+{
+ S24C16_set_bit(np, 1, gpreg, SET_BIT);
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+ S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void S24C16_stop(struct sym_device *np, u_char *gpreg)
+{
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+static void S24C16_do_bit(struct sym_device *np, u_char *read_bit, u_char write_bit,
+ u_char *gpreg)
+{
+ S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+ S24C16_set_bit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB(np, nc_gpreg);
+ S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+static void S24C16_write_ack(struct sym_device *np, u_char write_bit, u_char *gpreg,
+ u_char *gpcntl)
+{
+ OUTB(np, nc_gpcntl, *gpcntl & 0xfe);
+ S24C16_do_bit(np, NULL, write_bit, gpreg);
+ OUTB(np, nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+static void S24C16_read_ack(struct sym_device *np, u_char *read_bit, u_char *gpreg,
+ u_char *gpcntl)
+{
+ OUTB(np, nc_gpcntl, *gpcntl | 0x01);
+ S24C16_do_bit(np, read_bit, 1, gpreg);
+ OUTB(np, nc_gpcntl, *gpcntl);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+static void S24C16_write_byte(struct sym_device *np, u_char *ack_data, u_char write_data,
+ u_char *gpreg, u_char *gpcntl)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ S24C16_do_bit(np, NULL, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+static void S24C16_read_byte(struct sym_device *np, u_char *read_data, u_char ack_data,
+ u_char *gpreg, u_char *gpcntl)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ S24C16_do_bit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
+
+#if SYM_CONF_NVRAM_WRITE_SUPPORT
+/*
+ * Write 'len' bytes starting at 'offset'.
+ */
+static int sym_write_S24C16_nvram(struct sym_device *np, int offset,
+ u_char *data, int len)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_char ack_data;
+ int x;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB(np, nc_gpreg);
+ old_gpcntl = INB(np, nc_gpcntl);
+ gpcntl = old_gpcntl & 0x1c;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB(np, nc_gpreg, old_gpreg);
+ OUTB(np, nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ S24C16_stop(np, &gpreg);
+
+ /* NVRAM has to be written in segments of 16 bytes */
+ for (x = 0; x < len ; x += 16) {
+ do {
+ S24C16_start(np, &gpreg);
+ S24C16_write_byte(np, &ack_data,
+ 0xa0 | (((offset+x) >> 7) & 0x0e),
+ &gpreg, &gpcntl);
+ } while (ack_data & 0x01);
+
+ S24C16_write_byte(np, &ack_data, (offset+x) & 0xff,
+ &gpreg, &gpcntl);
+
+ for (y = 0; y < 16; y++)
+ S24C16_write_byte(np, &ack_data, data[x+y],
+ &gpreg, &gpcntl);
+ S24C16_stop(np, &gpreg);
+ }
+
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB(np, nc_gpcntl, old_gpcntl);
+ OUTB(np, nc_gpreg, old_gpreg);
+
+ return 0;
+}
+#endif /* SYM_CONF_NVRAM_WRITE_SUPPORT */
+
+/*
+ * Read 'len' bytes starting at 'offset'.
+ */
+static int sym_read_S24C16_nvram(struct sym_device *np, int offset, u_char *data, int len)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_char ack_data;
+ int retv = 1;
+ int x;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB(np, nc_gpreg);
+ old_gpcntl = INB(np, nc_gpcntl);
+ gpcntl = old_gpcntl & 0x1c;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB(np, nc_gpreg, old_gpreg);
+ OUTB(np, nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+ S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ S24C16_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ S24C16_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ S24C16_write_byte(np, &ack_data,
+ 0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ S24C16_write_byte(np, &ack_data,
+ offset & 0xff, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ S24C16_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ S24C16_write_byte(np, &ack_data,
+ 0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB(np, nc_gpcntl, gpcntl);
+
+ /* input all requested data - only part of total NVRAM */
+ for (x = 0; x < len; x++)
+ S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB(np, nc_gpcntl, gpcntl);
+ S24C16_stop(np, &gpreg);
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB(np, nc_gpcntl, old_gpcntl);
+ OUTB(np, nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+#undef SET_BIT
+#undef CLR_BIT
+#undef SET_CLK
+#undef CLR_CLK
+
+/*
+ * Try reading Symbios NVRAM.
+ * Return 0 if OK.
+ */
+static int sym_read_Symbios_nvram(struct sym_device *np, Symbios_nvram *nvram)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char *data = (u_char *) nvram;
+ int len = sizeof(*nvram);
+ u_short csum;
+ int x;
+
+ /* probe the 24c16 and read the SYMBIOS 24c16 area */
+ if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+ return 1;
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->type != 0 ||
+ memcmp(nvram->trailer, Symbios_trailer, 6) ||
+ nvram->byte_count != len - 12)
+ return 1;
+
+ /* verify checksum */
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+ if (csum != nvram->checksum)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * 93C46 EEPROM reading.
+ *
+ * GPOI0 - data in
+ * GPIO1 - data out
+ * GPIO2 - clock
+ * GPIO4 - chip select
+ *
+ * Used by Tekram.
+ */
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+static void T93C46_Clk(struct sym_device *np, u_char *gpreg)
+{
+ OUTB(np, nc_gpreg, *gpreg | 0x04);
+ udelay(2);
+ OUTB(np, nc_gpreg, *gpreg);
+}
+
+/*
+ * Read bit from NVRAM
+ */
+static void T93C46_Read_Bit(struct sym_device *np, u_char *read_bit, u_char *gpreg)
+{
+ udelay(2);
+ T93C46_Clk(np, gpreg);
+ *read_bit = INB(np, nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+static void T93C46_Write_Bit(struct sym_device *np, u_char write_bit, u_char *gpreg)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB(np, nc_gpreg, *gpreg);
+ udelay(2);
+
+ T93C46_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void T93C46_Stop(struct sym_device *np, u_char *gpreg)
+{
+ *gpreg &= 0xef;
+ OUTB(np, nc_gpreg, *gpreg);
+ udelay(2);
+
+ T93C46_Clk(np, gpreg);
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+static void T93C46_Send_Command(struct sym_device *np, u_short write_data,
+ u_char *read_bit, u_char *gpreg)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB(np, nc_gpreg);
+}
+
+/*
+ * READ 2 bytes from the NVRAM
+ */
+static void T93C46_Read_Word(struct sym_device *np,
+ unsigned short *nvram_data, unsigned char *gpreg)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ T93C46_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read Tekram NvRAM data.
+ */
+static int T93C46_Read_Data(struct sym_device *np, unsigned short *data,
+ int len, unsigned char *gpreg)
+{
+ int x;
+
+ for (x = 0; x < len; x++) {
+ unsigned char read_bit;
+ /* output read command and address */
+ T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 1; /* Bad */
+ T93C46_Read_Word(np, &data[x], gpreg);
+ T93C46_Stop(np, gpreg);
+ }
+
+ return 0;
+}
+
+/*
+ * Try reading 93C46 Tekram NVRAM.
+ */
+static int sym_read_T93C46_nvram(struct sym_device *np, Tekram_nvram *nvram)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB(np, nc_gpreg);
+ old_gpcntl = INB(np, nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB(np, nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB(np, nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ retv = T93C46_Read_Data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB(np, nc_gpcntl, old_gpcntl);
+ OUTB(np, nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Try reading Tekram NVRAM.
+ * Return 0 if OK.
+ */
+static int sym_read_Tekram_nvram (struct sym_device *np, Tekram_nvram *nvram)
+{
+ u_char *data = (u_char *) nvram;
+ int len = sizeof(*nvram);
+ u_short csum;
+ int x;
+
+ switch (np->device_id) {
+ case PCI_DEVICE_ID_NCR_53C885:
+ case PCI_DEVICE_ID_NCR_53C895:
+ case PCI_DEVICE_ID_NCR_53C896:
+ x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+ data, len);
+ break;
+ case PCI_DEVICE_ID_NCR_53C875:
+ x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+ data, len);
+ if (!x)
+ break;
+ default:
+ x = sym_read_T93C46_nvram(np, nvram);
+ break;
+ }
+ if (x)
+ return 1;
+
+ /* verify checksum */
+ for (x = 0, csum = 0; x < len - 1; x += 2)
+ csum += data[x] + (data[x+1] << 8);
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_PARISC
+/*
+ * Host firmware (PDC) keeps a table for altering SCSI capabilities.
+ * Many newer machines export one channel of 53c896 chip as SE, 50-pin HD.
+ * Also used for Multi-initiator SCSI clusters to set the SCSI Initiator ID.
+ */
+static int sym_read_parisc_pdc(struct sym_device *np, struct pdc_initiator *pdc)
+{
+ struct hardware_path hwpath;
+ get_pci_node_path(np->pdev, &hwpath);
+ if (!pdc_get_initiator(&hwpath, pdc))
+ return 0;
+
+ return SYM_PARISC_PDC;
+}
+#else
+static int sym_read_parisc_pdc(struct sym_device *np, struct pdc_initiator *x)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Try reading Symbios or Tekram NVRAM
+ */
+int sym_read_nvram(struct sym_device *np, struct sym_nvram *nvp)
+{
+ if (!sym_read_Symbios_nvram(np, &nvp->data.Symbios)) {
+ nvp->type = SYM_SYMBIOS_NVRAM;
+ sym_display_Symbios_nvram(np, &nvp->data.Symbios);
+ } else if (!sym_read_Tekram_nvram(np, &nvp->data.Tekram)) {
+ nvp->type = SYM_TEKRAM_NVRAM;
+ sym_display_Tekram_nvram(np, &nvp->data.Tekram);
+ } else {
+ nvp->type = sym_read_parisc_pdc(np, &nvp->data.parisc);
+ }
+ return nvp->type;
+}
+
+char *sym_nvram_type(struct sym_nvram *nvp)
+{
+ switch (nvp->type) {
+ case SYM_SYMBIOS_NVRAM:
+ return "Symbios NVRAM";
+ case SYM_TEKRAM_NVRAM:
+ return "Tekram NVRAM";
+ case SYM_PARISC_PDC:
+ return "PA-RISC Firmware";
+ default:
+ return "No NVRAM";
+ }
+}
diff --git a/drivers/scsi/sym53c8xx_2/sym_nvram.h b/drivers/scsi/sym53c8xx_2/sym_nvram.h
new file mode 100644
index 000000000000..1538bede5277
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_2/sym_nvram.h
@@ -0,0 +1,214 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000 Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ * Wolfgang Stanglmeier <wolf@cologne.de>
+ * Stefan Esser <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994 Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SYM_NVRAM_H
+#define SYM_NVRAM_H
+
+#include "sym53c8xx.h"
+
+/*
+ * Symbios NVRAM data format
+ */
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+ u_short type; /* 0x0000 */
+ u_short byte_count; /* excluding header/trailer */
+ u_short checksum;
+
+/* Controller set up 20 bytes */
+ u_char v_major; /* 0x00 */
+ u_char v_minor; /* 0x30 */
+ u32 boot_crc;
+ u_short flags;
+#define SYMBIOS_SCAM_ENABLE (1)
+#define SYMBIOS_PARITY_ENABLE (1<<1)
+#define SYMBIOS_VERBOSE_MSGS (1<<2)
+#define SYMBIOS_CHS_MAPPING (1<<3)
+#define SYMBIOS_NO_NVRAM (1<<3) /* ??? */
+ u_short flags1;
+#define SYMBIOS_SCAN_HI_LO (1)
+ u_short term_state;
+#define SYMBIOS_TERM_CANT_PROGRAM (0)
+#define SYMBIOS_TERM_ENABLED (1)
+#define SYMBIOS_TERM_DISABLED (2)
+ u_short rmvbl_flags;
+#define SYMBIOS_RMVBL_NO_SUPPORT (0)
+#define SYMBIOS_RMVBL_BOOT_DEVICE (1)
+#define SYMBIOS_RMVBL_MEDIA_INSTALLED (2)
+ u_char host_id;
+ u_char num_hba; /* 0x04 */
+ u_char num_devices; /* 0x10 */
+ u_char max_scam_devices; /* 0x04 */
+ u_char num_valid_scam_devices; /* 0x00 */
+ u_char flags2;
+#define SYMBIOS_AVOID_BUS_RESET (1<<2)
+
+/* Boot order 14 bytes * 4 */
+ struct Symbios_host{
+ u_short type; /* 4:8xx / 0:nok */
+ u_short device_id; /* PCI device id */
+ u_short vendor_id; /* PCI vendor id */
+ u_char bus_nr; /* PCI bus number */
+ u_char device_fn; /* PCI device/function number << 3*/
+ u_short word8;
+ u_short flags;
+#define SYMBIOS_INIT_SCAN_AT_BOOT (1)
+ u_short io_port; /* PCI io_port address */
+ } host[4];
+
+/* Targets 8 bytes * 16 */
+ struct Symbios_target {
+ u_char flags;
+#define SYMBIOS_DISCONNECT_ENABLE (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1)
+#define SYMBIOS_SCAN_LUNS (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3)
+ u_char rsvd;
+ u_char bus_width; /* 0x08/0x10 */
+ u_char sync_offset;
+ u_short sync_period; /* 4*period factor */
+ u_short timeout;
+ } target[16];
+/* Scam table 8 bytes * 4 */
+ struct Symbios_scam {
+ u_short id;
+ u_short method;
+#define SYMBIOS_SCAM_DEFAULT_METHOD (0)
+#define SYMBIOS_SCAM_DONT_ASSIGN (1)
+#define SYMBIOS_SCAM_SET_SPECIFIC_ID (2)
+#define SYMBIOS_SCAM_USE_ORDER_GIVEN (3)
+ u_short status;
+#define SYMBIOS_SCAM_UNKNOWN (0)
+#define SYMBIOS_SCAM_DEVICE_NOT_FOUND (1)
+#define SYMBIOS_SCAM_ID_NOT_SET (2)
+#define SYMBIOS_SCAM_ID_VALID (3)
+ u_char target_id;
+ u_char rsvd;
+ } scam[4];
+
+ u_char spare_devices[15*8];
+ u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram Symbios_nvram;
+typedef struct Symbios_host Symbios_host;
+typedef struct Symbios_target Symbios_target;
+typedef struct Symbios_scam Symbios_scam;
+
+/*
+ * Tekram NvRAM data format.
+ */
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_93C46_NVRAM_ADDRESS 0
+#define TEKRAM_24C16_NVRAM_ADDRESS 0x40
+
+struct Tekram_nvram {
+ struct Tekram_target {
+ u_char flags;
+#define TEKRAM_PARITY_CHECK (1)
+#define TEKRAM_SYNC_NEGO (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE (1<<2)
+#define TEKRAM_START_CMD (1<<3)
+#define TEKRAM_TAGGED_COMMANDS (1<<4)
+#define TEKRAM_WIDE_NEGO (1<<5)
+ u_char sync_index;
+ u_short word2;
+ } target[16];
+ u_char host_id;
+ u_char flags;
+#define TEKRAM_MORE_THAN_2_DRIVES (1)
+#define TEKRAM_DRIVES_SUP_1GB (1<<1)
+#define TEKRAM_RESET_ON_POWER_ON (1<<2)
+#define TEKRAM_ACTIVE_NEGATION (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK (1<<4)
+#define TEKRAM_SCAN_LUNS (1<<5)
+#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; */
+ /* 1: boot device; 2:all */
+ u_char boot_delay_index;
+ u_char max_tags_index;
+ u_short flags1;
+#define TEKRAM_F2_F6_ENABLED (1)
+ u_short spare[29];
+};
+typedef struct Tekram_nvram Tekram_nvram;
+typedef struct Tekram_target Tekram_target;
+
+#ifndef CONFIG_PARISC
+struct pdc_initiator { int dummy; };
+#endif
+
+/*
+ * Union of supported NVRAM formats.
+ */
+struct sym_nvram {
+ int type;
+#define SYM_SYMBIOS_NVRAM (1)
+#define SYM_TEKRAM_NVRAM (2)
+#define SYM_PARISC_PDC (3)
+#if SYM_CONF_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ struct pdc_initiator parisc;
+ } data;
+#endif
+};
+
+#if SYM_CONF_NVRAM_SUPPORT
+void sym_nvram_setup_host(struct Scsi_Host *shost, struct sym_hcb *np, struct sym_nvram *nvram);
+void sym_nvram_setup_target (struct sym_hcb *np, int target, struct sym_nvram *nvp);
+int sym_read_nvram (struct sym_device *np, struct sym_nvram *nvp);
+char *sym_nvram_type(struct sym_nvram *nvp);
+#else
+static inline void sym_nvram_setup_host(struct Scsi_Host *shost, struct sym_hcb *np, struct sym_nvram *nvram) { }
+static inline void sym_nvram_setup_target(struct sym_hcb *np, struct sym_nvram *nvram) { }
+static inline int sym_read_nvram(struct sym_device *np, struct sym_nvram *nvp)
+{
+ nvp->type = 0;
+ return 0;
+}
+static inline char *sym_nvram_type(struct sym_nvram *nvp)
+{
+ return "No NVRAM";
+}
+#endif
+
+#endif /* SYM_NVRAM_H */
diff --git a/drivers/scsi/sym53c8xx_comm.h b/drivers/scsi/sym53c8xx_comm.h
new file mode 100644
index 000000000000..20ae2b17df58
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_comm.h
@@ -0,0 +1,792 @@
+/******************************************************************************
+** High Performance device driver for the Symbios 53C896 controller.
+**
+** Copyright (C) 1998-2001 Gerard Roudier <groudier@free.fr>
+**
+** This driver also supports all the Symbios 53C8XX controller family,
+** except 53C810 revisions < 16, 53C825 revisions < 16 and all
+** revisions of 53C815 controllers.
+**
+** This driver is based on the Linux port of the FreeBSD ncr driver.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+**-----------------------------------------------------------------------------
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You 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.
+**
+**-----------------------------------------------------------------------------
+**
+** The Linux port of the FreeBSD ncr driver has been achieved in
+** november 1995 by:
+**
+** Gerard Roudier <groudier@free.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+**-----------------------------------------------------------------------------
+**
+** Major contributions:
+** --------------------
+**
+** NVRAM detection and reading.
+** Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+**
+*******************************************************************************
+*/
+
+/*==========================================================
+**
+** Debugging tags
+**
+**==========================================================
+*/
+
+#define DEBUG_ALLOC (0x0001)
+#define DEBUG_PHASE (0x0002)
+#define DEBUG_QUEUE (0x0008)
+#define DEBUG_RESULT (0x0010)
+#define DEBUG_POINTER (0x0020)
+#define DEBUG_SCRIPT (0x0040)
+#define DEBUG_TINY (0x0080)
+#define DEBUG_TIMING (0x0100)
+#define DEBUG_NEGO (0x0200)
+#define DEBUG_TAGS (0x0400)
+#define DEBUG_SCATTER (0x0800)
+#define DEBUG_IC (0x1000)
+
+/*
+** Enable/Disable debug messages.
+** Can be changed at runtime too.
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
+ #define DEBUG_FLAGS ncr_debug
+#else
+ #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
+#endif
+
+static inline struct list_head *ncr_list_pop(struct list_head *head)
+{
+ if (!list_empty(head)) {
+ struct list_head *elem = head->next;
+
+ list_del(elem);
+ return elem;
+ }
+
+ return NULL;
+}
+
+#ifdef __sparc__
+#include <asm/irq.h>
+#endif
+
+/*==========================================================
+**
+** Simple power of two buddy-like allocator.
+**
+** This simple code is not intended to be fast, but to
+** provide power of 2 aligned memory allocations.
+** Since the SCRIPTS processor only supplies 8 bit
+** arithmetic, this allocator allows simple and fast
+** address calculations from the SCRIPTS code.
+** In addition, cache line alignment is guaranteed for
+** power of 2 cache line size.
+** Enhanced in linux-2.3.44 to provide a memory pool
+** per pcidev to support dynamic dma mapping. (I would
+** have preferred a real bus astraction, btw).
+**
+**==========================================================
+*/
+
+#define MEMO_SHIFT 4 /* 16 bytes minimum memory chunk */
+#if PAGE_SIZE >= 8192
+#define MEMO_PAGE_ORDER 0 /* 1 PAGE maximum */
+#else
+#define MEMO_PAGE_ORDER 1 /* 2 PAGES maximum */
+#endif
+#define MEMO_FREE_UNUSED /* Free unused pages immediately */
+#define MEMO_WARN 1
+#define MEMO_GFP_FLAGS GFP_ATOMIC
+#define MEMO_CLUSTER_SHIFT (PAGE_SHIFT+MEMO_PAGE_ORDER)
+#define MEMO_CLUSTER_SIZE (1UL << MEMO_CLUSTER_SHIFT)
+#define MEMO_CLUSTER_MASK (MEMO_CLUSTER_SIZE-1)
+
+typedef u_long m_addr_t; /* Enough bits to bit-hack addresses */
+typedef struct device *m_bush_t; /* Something that addresses DMAable */
+
+typedef struct m_link { /* Link between free memory chunks */
+ struct m_link *next;
+} m_link_s;
+
+typedef struct m_vtob { /* Virtual to Bus address translation */
+ struct m_vtob *next;
+ m_addr_t vaddr;
+ m_addr_t baddr;
+} m_vtob_s;
+#define VTOB_HASH_SHIFT 5
+#define VTOB_HASH_SIZE (1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK (VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m) \
+ ((((m_addr_t) (m)) >> MEMO_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+
+typedef struct m_pool { /* Memory pool of a given kind */
+ m_bush_t bush;
+ m_addr_t (*getp)(struct m_pool *);
+ void (*freep)(struct m_pool *, m_addr_t);
+ int nump;
+ m_vtob_s *(vtob[VTOB_HASH_SIZE]);
+ struct m_pool *next;
+ struct m_link h[PAGE_SHIFT-MEMO_SHIFT+MEMO_PAGE_ORDER+1];
+} m_pool_s;
+
+static void *___m_alloc(m_pool_s *mp, int size)
+{
+ int i = 0;
+ int s = (1 << MEMO_SHIFT);
+ int j;
+ m_addr_t a;
+ m_link_s *h = mp->h;
+
+ if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+ return NULL;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ j = i;
+ while (!h[j].next) {
+ if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+ h[j].next = (m_link_s *)mp->getp(mp);
+ if (h[j].next)
+ h[j].next->next = NULL;
+ break;
+ }
+ ++j;
+ s <<= 1;
+ }
+ a = (m_addr_t) h[j].next;
+ if (a) {
+ h[j].next = h[j].next->next;
+ while (j > i) {
+ j -= 1;
+ s >>= 1;
+ h[j].next = (m_link_s *) (a+s);
+ h[j].next->next = NULL;
+ }
+ }
+#ifdef DEBUG
+ printk("___m_alloc(%d) = %p\n", size, (void *) a);
+#endif
+ return (void *) a;
+}
+
+static void ___m_free(m_pool_s *mp, void *ptr, int size)
+{
+ int i = 0;
+ int s = (1 << MEMO_SHIFT);
+ m_link_s *q;
+ m_addr_t a, b;
+ m_link_s *h = mp->h;
+
+#ifdef DEBUG
+ printk("___m_free(%p, %d)\n", ptr, size);
+#endif
+
+ if (size > (PAGE_SIZE << MEMO_PAGE_ORDER))
+ return;
+
+ while (size > s) {
+ s <<= 1;
+ ++i;
+ }
+
+ a = (m_addr_t) ptr;
+
+ while (1) {
+#ifdef MEMO_FREE_UNUSED
+ if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
+ mp->freep(mp, a);
+ break;
+ }
+#endif
+ b = a ^ s;
+ q = &h[i];
+ while (q->next && q->next != (m_link_s *) b) {
+ q = q->next;
+ }
+ if (!q->next) {
+ ((m_link_s *) a)->next = h[i].next;
+ h[i].next = (m_link_s *) a;
+ break;
+ }
+ q->next = q->next->next;
+ a = a & b;
+ s <<= 1;
+ ++i;
+ }
+}
+
+static DEFINE_SPINLOCK(ncr53c8xx_lock);
+
+static void *__m_calloc2(m_pool_s *mp, int size, char *name, int uflags)
+{
+ void *p;
+
+ p = ___m_alloc(mp, size);
+
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printk ("new %-10s[%4d] @%p.\n", name, size, p);
+
+ if (p)
+ memset(p, 0, size);
+ else if (uflags & MEMO_WARN)
+ printk (NAME53C8XX ": failed to allocate %s[%d]\n", name, size);
+
+ return p;
+}
+
+#define __m_calloc(mp, s, n) __m_calloc2(mp, s, n, MEMO_WARN)
+
+static void __m_free(m_pool_s *mp, void *ptr, int size, char *name)
+{
+ if (DEBUG_FLAGS & DEBUG_ALLOC)
+ printk ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+ ___m_free(mp, ptr, size);
+
+}
+
+/*
+ * With pci bus iommu support, we use a default pool of unmapped memory
+ * for memory we donnot need to DMA from/to and one pool per pcidev for
+ * memory accessed by the PCI chip. `mp0' is the default not DMAable pool.
+ */
+
+static m_addr_t ___mp0_getp(m_pool_s *mp)
+{
+ m_addr_t m = __get_free_pages(MEMO_GFP_FLAGS, MEMO_PAGE_ORDER);
+ if (m)
+ ++mp->nump;
+ return m;
+}
+
+static void ___mp0_freep(m_pool_s *mp, m_addr_t m)
+{
+ free_pages(m, MEMO_PAGE_ORDER);
+ --mp->nump;
+}
+
+static m_pool_s mp0 = {NULL, ___mp0_getp, ___mp0_freep};
+
+/*
+ * DMAable pools.
+ */
+
+/*
+ * With pci bus iommu support, we maintain one pool per pcidev and a
+ * hashed reverse table for virtual to bus physical address translations.
+ */
+static m_addr_t ___dma_getp(m_pool_s *mp)
+{
+ m_addr_t vp;
+ m_vtob_s *vbp;
+
+ vbp = __m_calloc(&mp0, sizeof(*vbp), "VTOB");
+ if (vbp) {
+ dma_addr_t daddr;
+ vp = (m_addr_t) dma_alloc_coherent(mp->bush,
+ PAGE_SIZE<<MEMO_PAGE_ORDER,
+ &daddr, GFP_ATOMIC);
+ if (vp) {
+ int hc = VTOB_HASH_CODE(vp);
+ vbp->vaddr = vp;
+ vbp->baddr = daddr;
+ vbp->next = mp->vtob[hc];
+ mp->vtob[hc] = vbp;
+ ++mp->nump;
+ return vp;
+ }
+ }
+ if (vbp)
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+ return 0;
+}
+
+static void ___dma_freep(m_pool_s *mp, m_addr_t m)
+{
+ m_vtob_s **vbpp, *vbp;
+ int hc = VTOB_HASH_CODE(m);
+
+ vbpp = &mp->vtob[hc];
+ while (*vbpp && (*vbpp)->vaddr != m)
+ vbpp = &(*vbpp)->next;
+ if (*vbpp) {
+ vbp = *vbpp;
+ *vbpp = (*vbpp)->next;
+ dma_free_coherent(mp->bush, PAGE_SIZE<<MEMO_PAGE_ORDER,
+ (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+ __m_free(&mp0, vbp, sizeof(*vbp), "VTOB");
+ --mp->nump;
+ }
+}
+
+static inline m_pool_s *___get_dma_pool(m_bush_t bush)
+{
+ m_pool_s *mp;
+ for (mp = mp0.next; mp && mp->bush != bush; mp = mp->next);
+ return mp;
+}
+
+static m_pool_s *___cre_dma_pool(m_bush_t bush)
+{
+ m_pool_s *mp;
+ mp = __m_calloc(&mp0, sizeof(*mp), "MPOOL");
+ if (mp) {
+ memset(mp, 0, sizeof(*mp));
+ mp->bush = bush;
+ mp->getp = ___dma_getp;
+ mp->freep = ___dma_freep;
+ mp->next = mp0.next;
+ mp0.next = mp;
+ }
+ return mp;
+}
+
+static void ___del_dma_pool(m_pool_s *p)
+{
+ struct m_pool **pp = &mp0.next;
+
+ while (*pp && *pp != p)
+ pp = &(*pp)->next;
+ if (*pp) {
+ *pp = (*pp)->next;
+ __m_free(&mp0, p, sizeof(*p), "MPOOL");
+ }
+}
+
+static void *__m_calloc_dma(m_bush_t bush, int size, char *name)
+{
+ u_long flags;
+ struct m_pool *mp;
+ void *m = NULL;
+
+ spin_lock_irqsave(&ncr53c8xx_lock, flags);
+ mp = ___get_dma_pool(bush);
+ if (!mp)
+ mp = ___cre_dma_pool(bush);
+ if (mp)
+ m = __m_calloc(mp, size, name);
+ if (mp && !mp->nump)
+ ___del_dma_pool(mp);
+ spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
+
+ return m;
+}
+
+static void __m_free_dma(m_bush_t bush, void *m, int size, char *name)
+{
+ u_long flags;
+ struct m_pool *mp;
+
+ spin_lock_irqsave(&ncr53c8xx_lock, flags);
+ mp = ___get_dma_pool(bush);
+ if (mp)
+ __m_free(mp, m, size, name);
+ if (mp && !mp->nump)
+ ___del_dma_pool(mp);
+ spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
+}
+
+static m_addr_t __vtobus(m_bush_t bush, void *m)
+{
+ u_long flags;
+ m_pool_s *mp;
+ int hc = VTOB_HASH_CODE(m);
+ m_vtob_s *vp = NULL;
+ m_addr_t a = ((m_addr_t) m) & ~MEMO_CLUSTER_MASK;
+
+ spin_lock_irqsave(&ncr53c8xx_lock, flags);
+ mp = ___get_dma_pool(bush);
+ if (mp) {
+ vp = mp->vtob[hc];
+ while (vp && (m_addr_t) vp->vaddr != a)
+ vp = vp->next;
+ }
+ spin_unlock_irqrestore(&ncr53c8xx_lock, flags);
+ return vp ? vp->baddr + (((m_addr_t) m) - a) : 0;
+}
+
+#define _m_calloc_dma(np, s, n) __m_calloc_dma(np->dev, s, n)
+#define _m_free_dma(np, p, s, n) __m_free_dma(np->dev, p, s, n)
+#define m_calloc_dma(s, n) _m_calloc_dma(np, s, n)
+#define m_free_dma(p, s, n) _m_free_dma(np, p, s, n)
+#define _vtobus(np, p) __vtobus(np->dev, p)
+#define vtobus(p) _vtobus(np, p)
+
+/*
+ * Deal with DMA mapping/unmapping.
+ */
+
+/* To keep track of the dma mapping (sg/single) that has been set */
+#define __data_mapped SCp.phase
+#define __data_mapping SCp.have_data_in
+
+static void __unmap_scsi_data(struct device *dev, struct scsi_cmnd *cmd)
+{
+ switch(cmd->__data_mapped) {
+ case 2:
+ dma_unmap_sg(dev, cmd->buffer, cmd->use_sg,
+ cmd->sc_data_direction);
+ break;
+ case 1:
+ dma_unmap_single(dev, cmd->__data_mapping,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+ break;
+ }
+ cmd->__data_mapped = 0;
+}
+
+static u_long __map_scsi_single_data(struct device *dev, struct scsi_cmnd *cmd)
+{
+ dma_addr_t mapping;
+
+ if (cmd->request_bufflen == 0)
+ return 0;
+
+ mapping = dma_map_single(dev, cmd->request_buffer,
+ cmd->request_bufflen,
+ cmd->sc_data_direction);
+ cmd->__data_mapped = 1;
+ cmd->__data_mapping = mapping;
+
+ return mapping;
+}
+
+static int __map_scsi_sg_data(struct device *dev, struct scsi_cmnd *cmd)
+{
+ int use_sg;
+
+ if (cmd->use_sg == 0)
+ return 0;
+
+ use_sg = dma_map_sg(dev, cmd->buffer, cmd->use_sg,
+ cmd->sc_data_direction);
+ cmd->__data_mapped = 2;
+ cmd->__data_mapping = use_sg;
+
+ return use_sg;
+}
+
+#define unmap_scsi_data(np, cmd) __unmap_scsi_data(np->dev, cmd)
+#define map_scsi_single_data(np, cmd) __map_scsi_single_data(np->dev, cmd)
+#define map_scsi_sg_data(np, cmd) __map_scsi_sg_data(np->dev, cmd)
+
+/*==========================================================
+**
+** Driver setup.
+**
+** This structure is initialized from linux config
+** options. It can be overridden at boot-up by the boot
+** command line.
+**
+**==========================================================
+*/
+static struct ncr_driver_setup
+ driver_setup = SCSI_NCR_DRIVER_SETUP;
+
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+ driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+
+/*===================================================================
+**
+** Driver setup from the boot command line
+**
+**===================================================================
+*/
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+#define OPT_TAGS 1
+#define OPT_MASTER_PARITY 2
+#define OPT_SCSI_PARITY 3
+#define OPT_DISCONNECTION 4
+#define OPT_SPECIAL_FEATURES 5
+#define OPT_UNUSED_1 6
+#define OPT_FORCE_SYNC_NEGO 7
+#define OPT_REVERSE_PROBE 8
+#define OPT_DEFAULT_SYNC 9
+#define OPT_VERBOSE 10
+#define OPT_DEBUG 11
+#define OPT_BURST_MAX 12
+#define OPT_LED_PIN 13
+#define OPT_MAX_WIDE 14
+#define OPT_SETTLE_DELAY 15
+#define OPT_DIFF_SUPPORT 16
+#define OPT_IRQM 17
+#define OPT_PCI_FIX_UP 18
+#define OPT_BUS_CHECK 19
+#define OPT_OPTIMIZE 20
+#define OPT_RECOVERY 21
+#define OPT_SAFE_SETUP 22
+#define OPT_USE_NVRAM 23
+#define OPT_EXCLUDE 24
+#define OPT_HOST_ID 25
+
+#ifdef SCSI_NCR_IARB_SUPPORT
+#define OPT_IARB 26
+#endif
+
+static char setup_token[] __initdata =
+ "tags:" "mpar:"
+ "spar:" "disc:"
+ "specf:" "ultra:"
+ "fsn:" "revprob:"
+ "sync:" "verb:"
+ "debug:" "burst:"
+ "led:" "wide:"
+ "settle:" "diff:"
+ "irqm:" "pcifix:"
+ "buschk:" "optim:"
+ "recovery:"
+ "safe:" "nvram:"
+ "excl:" "hostid:"
+#ifdef SCSI_NCR_IARB_SUPPORT
+ "iarb:"
+#endif
+ ; /* DONNOT REMOVE THIS ';' */
+
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
+
+static int __init get_setup_token(char *p)
+{
+ char *cur = setup_token;
+ char *pc;
+ int i = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ ++pc;
+ ++i;
+ if (!strncmp(p, cur, pc - cur))
+ return i;
+ cur = pc;
+ }
+ return 0;
+}
+
+
+static int __init sym53c8xx__setup(char *str)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+ char *cur = str;
+ char *pc, *pv;
+ int i, val, c;
+ int xi = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ char *pe;
+
+ val = 0;
+ pv = pc;
+ c = *++pv;
+
+ if (c == 'n')
+ val = 0;
+ else if (c == 'y')
+ val = 1;
+ else
+ val = (int) simple_strtoul(pv, &pe, 0);
+
+ switch (get_setup_token(cur)) {
+ case OPT_TAGS:
+ driver_setup.default_tags = val;
+ if (pe && *pe == '/') {
+ i = 0;
+ while (*pe && *pe != ARG_SEP &&
+ i < sizeof(driver_setup.tag_ctrl)-1) {
+ driver_setup.tag_ctrl[i++] = *pe++;
+ }
+ driver_setup.tag_ctrl[i] = '\0';
+ }
+ break;
+ case OPT_MASTER_PARITY:
+ driver_setup.master_parity = val;
+ break;
+ case OPT_SCSI_PARITY:
+ driver_setup.scsi_parity = val;
+ break;
+ case OPT_DISCONNECTION:
+ driver_setup.disconnection = val;
+ break;
+ case OPT_SPECIAL_FEATURES:
+ driver_setup.special_features = val;
+ break;
+ case OPT_FORCE_SYNC_NEGO:
+ driver_setup.force_sync_nego = val;
+ break;
+ case OPT_REVERSE_PROBE:
+ driver_setup.reverse_probe = val;
+ break;
+ case OPT_DEFAULT_SYNC:
+ driver_setup.default_sync = val;
+ break;
+ case OPT_VERBOSE:
+ driver_setup.verbose = val;
+ break;
+ case OPT_DEBUG:
+ driver_setup.debug = val;
+ break;
+ case OPT_BURST_MAX:
+ driver_setup.burst_max = val;
+ break;
+ case OPT_LED_PIN:
+ driver_setup.led_pin = val;
+ break;
+ case OPT_MAX_WIDE:
+ driver_setup.max_wide = val? 1:0;
+ break;
+ case OPT_SETTLE_DELAY:
+ driver_setup.settle_delay = val;
+ break;
+ case OPT_DIFF_SUPPORT:
+ driver_setup.diff_support = val;
+ break;
+ case OPT_IRQM:
+ driver_setup.irqm = val;
+ break;
+ case OPT_PCI_FIX_UP:
+ driver_setup.pci_fix_up = val;
+ break;
+ case OPT_BUS_CHECK:
+ driver_setup.bus_check = val;
+ break;
+ case OPT_OPTIMIZE:
+ driver_setup.optimize = val;
+ break;
+ case OPT_RECOVERY:
+ driver_setup.recovery = val;
+ break;
+ case OPT_USE_NVRAM:
+ driver_setup.use_nvram = val;
+ break;
+ case OPT_SAFE_SETUP:
+ memcpy(&driver_setup, &driver_safe_setup,
+ sizeof(driver_setup));
+ break;
+ case OPT_EXCLUDE:
+ if (xi < SCSI_NCR_MAX_EXCLUDES)
+ driver_setup.excludes[xi++] = val;
+ break;
+ case OPT_HOST_ID:
+ driver_setup.host_id = val;
+ break;
+#ifdef SCSI_NCR_IARB_SUPPORT
+ case OPT_IARB:
+ driver_setup.iarb = val;
+ break;
+#endif
+ default:
+ printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+ break;
+ }
+
+ if ((cur = strchr(cur, ARG_SEP)) != NULL)
+ ++cur;
+ }
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+ return 1;
+}
+
+/*===================================================================
+**
+** Get device queue depth from boot command line.
+**
+**===================================================================
+*/
+#define DEF_DEPTH (driver_setup.default_tags)
+#define ALL_TARGETS -2
+#define NO_TARGET -1
+#define ALL_LUNS -2
+#define NO_LUN -1
+
+static int device_queue_depth(int unit, int target, int lun)
+{
+ int c, h, t, u, v;
+ char *p = driver_setup.tag_ctrl;
+ char *ep;
+
+ h = -1;
+ t = NO_TARGET;
+ u = NO_LUN;
+ while ((c = *p++) != 0) {
+ v = simple_strtoul(p, &ep, 0);
+ switch(c) {
+ case '/':
+ ++h;
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ case 't':
+ if (t != target)
+ t = (target == v) ? v : NO_TARGET;
+ u = ALL_LUNS;
+ break;
+ case 'u':
+ if (u != lun)
+ u = (lun == v) ? v : NO_LUN;
+ break;
+ case 'q':
+ if (h == unit &&
+ (t == ALL_TARGETS || t == target) &&
+ (u == ALL_LUNS || u == lun))
+ return v;
+ break;
+ case '-':
+ t = ALL_TARGETS;
+ u = ALL_LUNS;
+ break;
+ default:
+ break;
+ }
+ p = ep;
+ }
+ return DEF_DEPTH;
+}
diff --git a/drivers/scsi/sym53c8xx_defs.h b/drivers/scsi/sym53c8xx_defs.h
new file mode 100644
index 000000000000..4c4ae7d4713f
--- /dev/null
+++ b/drivers/scsi/sym53c8xx_defs.h
@@ -0,0 +1,1333 @@
+/******************************************************************************
+** High Performance device driver for the Symbios 53C896 controller.
+**
+** Copyright (C) 1998-2001 Gerard Roudier <groudier@free.fr>
+**
+** This driver also supports all the Symbios 53C8XX controller family,
+** except 53C810 revisions < 16, 53C825 revisions < 16 and all
+** revisions of 53C815 controllers.
+**
+** This driver is based on the Linux port of the FreeBSD ncr driver.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+**-----------------------------------------------------------------------------
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You 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.
+**
+**-----------------------------------------------------------------------------
+**
+** The Linux port of the FreeBSD ncr driver has been achieved in
+** november 1995 by:
+**
+** Gerard Roudier <groudier@free.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+**-----------------------------------------------------------------------------
+**
+** Major contributions:
+** --------------------
+**
+** NVRAM detection and reading.
+** Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+**
+** Added support for MIPS big endian systems.
+** Carsten Langgaard, carstenl@mips.com
+** Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+**
+** Added support for HP PARISC big endian systems.
+** Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+**
+*******************************************************************************
+*/
+
+#ifndef SYM53C8XX_DEFS_H
+#define SYM53C8XX_DEFS_H
+
+#include <linux/config.h>
+
+/*
+** If you want a driver as small as possible, donnot define the
+** following options.
+*/
+#define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+#define SCSI_NCR_DEBUG_INFO_SUPPORT
+
+/*
+** To disable integrity checking, do not define the
+** following option.
+*/
+#ifdef CONFIG_SCSI_NCR53C8XX_INTEGRITY_CHECK
+# define SCSI_NCR_ENABLE_INTEGRITY_CHECK
+#endif
+
+/* ---------------------------------------------------------------------
+** Take into account kernel configured parameters.
+** Most of these options can be overridden at startup by a command line.
+** ---------------------------------------------------------------------
+*/
+
+/*
+ * For Ultra2 and Ultra3 SCSI support option, use special features.
+ *
+ * Value (default) means:
+ * bit 0 : all features enabled, except:
+ * bit 1 : PCI Write And Invalidate.
+ * bit 2 : Data Phase Mismatch handling from SCRIPTS.
+ *
+ * Use boot options ncr53c8xx=specf:1 if you want all chip features to be
+ * enabled by the driver.
+ */
+#define SCSI_NCR_SETUP_SPECIAL_FEATURES (3)
+
+#define SCSI_NCR_MAX_SYNC (80)
+
+/*
+ * Allow tags from 2 to 256, default 8
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#if CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2
+#define SCSI_NCR_MAX_TAGS (2)
+#elif CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 256
+#define SCSI_NCR_MAX_TAGS (256)
+#else
+#define SCSI_NCR_MAX_TAGS CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#endif
+#else
+#define SCSI_NCR_MAX_TAGS (8)
+#endif
+
+/*
+ * Allow tagged command queuing support if configured with default number
+ * of tags set to max (see above).
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+#define SCSI_NCR_SETUP_DEFAULT_TAGS CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+#elif defined CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+#define SCSI_NCR_SETUP_DEFAULT_TAGS SCSI_NCR_MAX_TAGS
+#else
+#define SCSI_NCR_SETUP_DEFAULT_TAGS (0)
+#endif
+
+/*
+ * Immediate arbitration
+ */
+#if defined(CONFIG_SCSI_NCR53C8XX_IARB)
+#define SCSI_NCR_IARB_SUPPORT
+#endif
+
+/*
+ * Sync transfer frequency at startup.
+ * Allow from 5Mhz to 80Mhz default 20 Mhz.
+ */
+#ifndef CONFIG_SCSI_NCR53C8XX_SYNC
+#define CONFIG_SCSI_NCR53C8XX_SYNC (20)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC
+#undef CONFIG_SCSI_NCR53C8XX_SYNC
+#define CONFIG_SCSI_NCR53C8XX_SYNC SCSI_NCR_MAX_SYNC
+#endif
+
+#if CONFIG_SCSI_NCR53C8XX_SYNC == 0
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (255)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 5
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (50)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 20
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (250/(CONFIG_SCSI_NCR53C8XX_SYNC))
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 33
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (11)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 40
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (10)
+#else
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (9)
+#endif
+
+/*
+ * Disallow disconnections at boot-up
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+#define SCSI_NCR_SETUP_DISCONNECTION (0)
+#else
+#define SCSI_NCR_SETUP_DISCONNECTION (1)
+#endif
+
+/*
+ * Force synchronous negotiation for all targets
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (1)
+#else
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (0)
+#endif
+
+/*
+ * Disable master parity checking (flawed hardwares need that)
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
+#define SCSI_NCR_SETUP_MASTER_PARITY (0)
+#else
+#define SCSI_NCR_SETUP_MASTER_PARITY (1)
+#endif
+
+/*
+ * Disable scsi parity checking (flawed devices may need that)
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
+#define SCSI_NCR_SETUP_SCSI_PARITY (0)
+#else
+#define SCSI_NCR_SETUP_SCSI_PARITY (1)
+#endif
+
+/*
+ * Settle time after reset at boot-up
+ */
+#define SCSI_NCR_SETUP_SETTLE_TIME (2)
+
+/*
+** Bridge quirks work-around option defaulted to 1.
+*/
+#ifndef SCSI_NCR_PCIQ_WORK_AROUND_OPT
+#define SCSI_NCR_PCIQ_WORK_AROUND_OPT 1
+#endif
+
+/*
+** Work-around common bridge misbehaviour.
+**
+** - Do not flush posted writes in the opposite
+** direction on read.
+** - May reorder DMA writes to memory.
+**
+** This option should not affect performances
+** significantly, so it is the default.
+*/
+#if SCSI_NCR_PCIQ_WORK_AROUND_OPT == 1
+#define SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM
+#define SCSI_NCR_PCIQ_MAY_REORDER_WRITES
+#define SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS
+
+/*
+** Same as option 1, but also deal with
+** misconfigured interrupts.
+**
+** - Edge triggerred instead of level sensitive.
+** - No interrupt line connected.
+** - IRQ number misconfigured.
+**
+** If no interrupt is delivered, the driver will
+** catch the interrupt conditions 10 times per
+** second. No need to say that this option is
+** not recommended.
+*/
+#elif SCSI_NCR_PCIQ_WORK_AROUND_OPT == 2
+#define SCSI_NCR_PCIQ_MAY_NOT_FLUSH_PW_UPSTREAM
+#define SCSI_NCR_PCIQ_MAY_REORDER_WRITES
+#define SCSI_NCR_PCIQ_MAY_MISS_COMPLETIONS
+#define SCSI_NCR_PCIQ_BROKEN_INTR
+
+/*
+** Some bridge designers decided to flush
+** everything prior to deliver the interrupt.
+** This option tries to deal with such a
+** behaviour.
+*/
+#elif SCSI_NCR_PCIQ_WORK_AROUND_OPT == 3
+#define SCSI_NCR_PCIQ_SYNC_ON_INTR
+#endif
+
+/*
+** Other parameters not configurable with "make config"
+** Avoid to change these constants, unless you know what you are doing.
+*/
+
+#define SCSI_NCR_ALWAYS_SIMPLE_TAG
+#define SCSI_NCR_MAX_SCATTER (127)
+#define SCSI_NCR_MAX_TARGET (16)
+
+/*
+** Compute some desirable value for CAN_QUEUE
+** and CMD_PER_LUN.
+** The driver will use lower values if these
+** ones appear to be too large.
+*/
+#define SCSI_NCR_CAN_QUEUE (8*SCSI_NCR_MAX_TAGS + 2*SCSI_NCR_MAX_TARGET)
+#define SCSI_NCR_CMD_PER_LUN (SCSI_NCR_MAX_TAGS)
+
+#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER)
+#define SCSI_NCR_TIMER_INTERVAL (HZ)
+
+#if 1 /* defined CONFIG_SCSI_MULTI_LUN */
+#define SCSI_NCR_MAX_LUN (16)
+#else
+#define SCSI_NCR_MAX_LUN (1)
+#endif
+
+/*
+** These simple macros limit expression involving
+** kernel time values (jiffies) to some that have
+** chance not to be too much incorrect. :-)
+*/
+#define ktime_get(o) (jiffies + (u_long) o)
+#define ktime_exp(b) ((long)(jiffies) - (long)(b) >= 0)
+#define ktime_dif(a, b) ((long)(a) - (long)(b))
+/* These ones are not used in this driver */
+#define ktime_add(a, o) ((a) + (u_long)(o))
+#define ktime_sub(a, o) ((a) - (u_long)(o))
+
+
+/*
+ * IO functions definition for big/little endian CPU support.
+ * For now, the NCR is only supported in little endian addressing mode,
+ */
+
+#ifdef __BIG_ENDIAN
+
+#define inw_l2b inw
+#define inl_l2b inl
+#define outw_b2l outw
+#define outl_b2l outl
+
+#define readb_raw readb
+#define writeb_raw writeb
+
+#if defined(SCSI_NCR_BIG_ENDIAN)
+#define readw_l2b __raw_readw
+#define readl_l2b __raw_readl
+#define writew_b2l __raw_writew
+#define writel_b2l __raw_writel
+#define readw_raw __raw_readw
+#define readl_raw __raw_readl
+#define writew_raw __raw_writew
+#define writel_raw __raw_writel
+#else /* Other big-endian */
+#define readw_l2b readw
+#define readl_l2b readl
+#define writew_b2l writew
+#define writel_b2l writel
+#define readw_raw readw
+#define readl_raw readl
+#define writew_raw writew
+#define writel_raw writel
+#endif
+
+#else /* little endian */
+
+#define inw_raw inw
+#define inl_raw inl
+#define outw_raw outw
+#define outl_raw outl
+
+#define readb_raw readb
+#define readw_raw readw
+#define readl_raw readl
+#define writeb_raw writeb
+#define writew_raw writew
+#define writel_raw writel
+
+#endif
+
+#if !defined(__hppa__) && !defined(__mips__)
+#ifdef SCSI_NCR_BIG_ENDIAN
+#error "The NCR in BIG ENDIAN addressing mode is not (yet) supported"
+#endif
+#endif
+
+#define MEMORY_BARRIER() mb()
+
+
+/*
+ * If the NCR uses big endian addressing mode over the
+ * PCI, actual io register addresses for byte and word
+ * accesses must be changed according to lane routing.
+ * Btw, ncr_offb() and ncr_offw() macros only apply to
+ * constants and so donnot generate bloated code.
+ */
+
+#if defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o) (o)
+#define ncr_offw(o) (o)
+
+#endif
+
+/*
+ * If the CPU and the NCR use same endian-ness addressing,
+ * no byte reordering is needed for script patching.
+ * Macro cpu_to_scr() is to be used for script patching.
+ * Macro scr_to_cpu() is to be used for getting a DWORD
+ * from the script.
+ */
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
+
+/*
+ * Access to the controller chip.
+ *
+ * If the CPU and the NCR use same endian-ness addressing,
+ * no byte reordering is needed for accessing chip io
+ * registers. Functions suffixed by '_raw' are assumed
+ * to access the chip over the PCI without doing byte
+ * reordering. Functions suffixed by '_l2b' are
+ * assumed to perform little-endian to big-endian byte
+ * reordering, those suffixed by '_b2l' blah, blah,
+ * blah, ...
+ */
+
+/*
+ * MEMORY mapped IO input / output
+ */
+
+#define INB_OFF(o) readb_raw((char __iomem *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val) writeb_raw((val), (char __iomem *)np->reg + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_l2b((char __iomem *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_l2b((char __iomem *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_b2l((val), (char __iomem *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_b2l((val), (char __iomem *)np->reg + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_b2l((char __iomem *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_b2l((char __iomem *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_l2b((val), (char __iomem *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_l2b((val), (char __iomem *)np->reg + (o))
+
+#else
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+/* Only 8 or 32 bit transfers allowed */
+#define INW_OFF(o) (readb((char __iomem *)np->reg + ncr_offw(o)) << 8 | readb((char __iomem *)np->reg + ncr_offw(o) + 1))
+#else
+#define INW_OFF(o) readw_raw((char __iomem *)np->reg + ncr_offw(o))
+#endif
+#define INL_OFF(o) readl_raw((char __iomem *)np->reg + (o))
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+/* Only 8 or 32 bit transfers allowed */
+#define OUTW_OFF(o, val) do { writeb((char)((val) >> 8), (char __iomem *)np->reg + ncr_offw(o)); writeb((char)(val), (char __iomem *)np->reg + ncr_offw(o) + 1); } while (0)
+#else
+#define OUTW_OFF(o, val) writew_raw((val), (char __iomem *)np->reg + ncr_offw(o))
+#endif
+#define OUTL_OFF(o, val) writel_raw((val), (char __iomem *)np->reg + (o))
+
+#endif
+
+#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
+/*
+ * Set bit field ON, OFF
+ */
+
+#define OUTONB(r, m) OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m) OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m) OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
+/*
+ * We normally want the chip to have a consistent view
+ * of driver internal data structures when we restart it.
+ * Thus these macros.
+ */
+#define OUTL_DSP(v) \
+ do { \
+ MEMORY_BARRIER(); \
+ OUTL (nc_dsp, (v)); \
+ } while (0)
+
+#define OUTONB_STD() \
+ do { \
+ MEMORY_BARRIER(); \
+ OUTONB (nc_dcntl, (STD|NOCOM)); \
+ } while (0)
+
+
+/*
+** NCR53C8XX devices features table.
+*/
+struct ncr_chip {
+ unsigned short revision_id;
+ unsigned char burst_max; /* log-base-2 of max burst */
+ unsigned char offset_max;
+ unsigned char nr_divisor;
+ unsigned int features;
+#define FE_LED0 (1<<0)
+#define FE_WIDE (1<<1) /* Wide data transfers */
+#define FE_ULTRA (1<<2) /* Ultra speed 20Mtrans/sec */
+#define FE_DBLR (1<<4) /* Clock doubler present */
+#define FE_QUAD (1<<5) /* Clock quadrupler present */
+#define FE_ERL (1<<6) /* Enable read line */
+#define FE_CLSE (1<<7) /* Cache line size enable */
+#define FE_WRIE (1<<8) /* Write & Invalidate enable */
+#define FE_ERMP (1<<9) /* Enable read multiple */
+#define FE_BOF (1<<10) /* Burst opcode fetch */
+#define FE_DFS (1<<11) /* DMA fifo size */
+#define FE_PFEN (1<<12) /* Prefetch enable */
+#define FE_LDSTR (1<<13) /* Load/Store supported */
+#define FE_RAM (1<<14) /* On chip RAM present */
+#define FE_VARCLK (1<<15) /* SCSI clock may vary */
+#define FE_RAM8K (1<<16) /* On chip RAM sized 8Kb */
+#define FE_64BIT (1<<17) /* Have a 64-bit PCI interface */
+#define FE_IO256 (1<<18) /* Requires full 256 bytes in PCI space */
+#define FE_NOPM (1<<19) /* Scripts handles phase mismatch */
+#define FE_LEDC (1<<20) /* Hardware control of LED */
+#define FE_DIFF (1<<21) /* Support Differential SCSI */
+#define FE_66MHZ (1<<23) /* 66MHz PCI Support */
+#define FE_DAC (1<<24) /* Support DAC cycles (64 bit addressing) */
+#define FE_ISTAT1 (1<<25) /* Have ISTAT1, MBOX0, MBOX1 registers */
+#define FE_DAC_IN_USE (1<<26) /* Platform does DAC cycles */
+#define FE_EHP (1<<27) /* 720: Even host parity */
+#define FE_MUX (1<<28) /* 720: Multiplexed bus */
+#define FE_EA (1<<29) /* 720: Enable Ack */
+
+#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_DBLR|FE_QUAD|F_CLK80)
+#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
+};
+
+
+/*
+** Driver setup structure.
+**
+** This structure is initialized from linux config options.
+** It can be overridden at boot-up by the boot command line.
+*/
+#define SCSI_NCR_MAX_EXCLUDES 8
+struct ncr_driver_setup {
+ u8 master_parity;
+ u8 scsi_parity;
+ u8 disconnection;
+ u8 special_features;
+ u8 force_sync_nego;
+ u8 reverse_probe;
+ u8 pci_fix_up;
+ u8 use_nvram;
+ u8 verbose;
+ u8 default_tags;
+ u16 default_sync;
+ u16 debug;
+ u8 burst_max;
+ u8 led_pin;
+ u8 max_wide;
+ u8 settle_delay;
+ u8 diff_support;
+ u8 irqm;
+ u8 bus_check;
+ u8 optimize;
+ u8 recovery;
+ u8 host_id;
+ u16 iarb;
+ u32 excludes[SCSI_NCR_MAX_EXCLUDES];
+ char tag_ctrl[100];
+};
+
+/*
+** Initial setup.
+** Can be overriden at startup by a command line.
+*/
+#define SCSI_NCR_DRIVER_SETUP \
+{ \
+ SCSI_NCR_SETUP_MASTER_PARITY, \
+ SCSI_NCR_SETUP_SCSI_PARITY, \
+ SCSI_NCR_SETUP_DISCONNECTION, \
+ SCSI_NCR_SETUP_SPECIAL_FEATURES, \
+ SCSI_NCR_SETUP_FORCE_SYNC_NEGO, \
+ 0, \
+ 0, \
+ 1, \
+ 0, \
+ SCSI_NCR_SETUP_DEFAULT_TAGS, \
+ SCSI_NCR_SETUP_DEFAULT_SYNC, \
+ 0x00, \
+ 7, \
+ 0, \
+ 1, \
+ SCSI_NCR_SETUP_SETTLE_TIME, \
+ 0, \
+ 0, \
+ 1, \
+ 0, \
+ 0, \
+ 255, \
+ 0x00 \
+}
+
+/*
+** Boot fail safe setup.
+** Override initial setup from boot command line:
+** ncr53c8xx=safe:y
+*/
+#define SCSI_NCR_DRIVER_SAFE_SETUP \
+{ \
+ 0, \
+ 1, \
+ 0, \
+ 0, \
+ 0, \
+ 0, \
+ 0, \
+ 1, \
+ 2, \
+ 0, \
+ 255, \
+ 0x00, \
+ 255, \
+ 0, \
+ 0, \
+ 10, \
+ 1, \
+ 1, \
+ 1, \
+ 0, \
+ 0, \
+ 255 \
+}
+
+/**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/
+
+/*-----------------------------------------------------------------
+**
+** The ncr 53c810 register structure.
+**
+**-----------------------------------------------------------------
+*/
+
+struct ncr_reg {
+/*00*/ u8 nc_scntl0; /* full arb., ena parity, par->ATN */
+
+/*01*/ u8 nc_scntl1; /* no reset */
+ #define ISCON 0x10 /* connected to scsi */
+ #define CRST 0x08 /* force reset */
+ #define IARB 0x02 /* immediate arbitration */
+
+/*02*/ u8 nc_scntl2; /* no disconnect expected */
+ #define SDU 0x80 /* cmd: disconnect will raise error */
+ #define CHM 0x40 /* sta: chained mode */
+ #define WSS 0x08 /* sta: wide scsi send [W]*/
+ #define WSR 0x01 /* sta: wide scsi received [W]*/
+
+/*03*/ u8 nc_scntl3; /* cnf system clock dependent */
+ #define EWS 0x08 /* cmd: enable wide scsi [W]*/
+ #define ULTRA 0x80 /* cmd: ULTRA enable */
+ /* bits 0-2, 7 rsvd for C1010 */
+
+/*04*/ u8 nc_scid; /* cnf host adapter scsi address */
+ #define RRE 0x40 /* r/w:e enable response to resel. */
+ #define SRE 0x20 /* r/w:e enable response to select */
+
+/*05*/ u8 nc_sxfer; /* ### Sync speed and count */
+ /* bits 6-7 rsvd for C1010 */
+
+/*06*/ u8 nc_sdid; /* ### Destination-ID */
+
+/*07*/ u8 nc_gpreg; /* ??? IO-Pins */
+
+/*08*/ u8 nc_sfbr; /* ### First byte in phase */
+
+/*09*/ u8 nc_socl;
+ #define CREQ 0x80 /* r/w: SCSI-REQ */
+ #define CACK 0x40 /* r/w: SCSI-ACK */
+ #define CBSY 0x20 /* r/w: SCSI-BSY */
+ #define CSEL 0x10 /* r/w: SCSI-SEL */
+ #define CATN 0x08 /* r/w: SCSI-ATN */
+ #define CMSG 0x04 /* r/w: SCSI-MSG */
+ #define CC_D 0x02 /* r/w: SCSI-C_D */
+ #define CI_O 0x01 /* r/w: SCSI-I_O */
+
+/*0a*/ u8 nc_ssid;
+
+/*0b*/ u8 nc_sbcl;
+
+/*0c*/ u8 nc_dstat;
+ #define DFE 0x80 /* sta: dma fifo empty */
+ #define MDPE 0x40 /* int: master data parity error */
+ #define BF 0x20 /* int: script: bus fault */
+ #define ABRT 0x10 /* int: script: command aborted */
+ #define SSI 0x08 /* int: script: single step */
+ #define SIR 0x04 /* int: script: interrupt instruct. */
+ #define IID 0x01 /* int: script: illegal instruct. */
+
+/*0d*/ u8 nc_sstat0;
+ #define ILF 0x80 /* sta: data in SIDL register lsb */
+ #define ORF 0x40 /* sta: data in SODR register lsb */
+ #define OLF 0x20 /* sta: data in SODL register lsb */
+ #define AIP 0x10 /* sta: arbitration in progress */
+ #define LOA 0x08 /* sta: arbitration lost */
+ #define WOA 0x04 /* sta: arbitration won */
+ #define IRST 0x02 /* sta: scsi reset signal */
+ #define SDP 0x01 /* sta: scsi parity signal */
+
+/*0e*/ u8 nc_sstat1;
+ #define FF3210 0xf0 /* sta: bytes in the scsi fifo */
+
+/*0f*/ u8 nc_sstat2;
+ #define ILF1 0x80 /* sta: data in SIDL register msb[W]*/
+ #define ORF1 0x40 /* sta: data in SODR register msb[W]*/
+ #define OLF1 0x20 /* sta: data in SODL register msb[W]*/
+ #define DM 0x04 /* sta: DIFFSENS mismatch (895/6 only) */
+ #define LDSC 0x02 /* sta: disconnect & reconnect */
+
+/*10*/ u8 nc_dsa; /* --> Base page */
+/*11*/ u8 nc_dsa1;
+/*12*/ u8 nc_dsa2;
+/*13*/ u8 nc_dsa3;
+
+/*14*/ u8 nc_istat; /* --> Main Command and status */
+ #define CABRT 0x80 /* cmd: abort current operation */
+ #define SRST 0x40 /* mod: reset chip */
+ #define SIGP 0x20 /* r/w: message from host to ncr */
+ #define SEM 0x10 /* r/w: message between host + ncr */
+ #define CON 0x08 /* sta: connected to scsi */
+ #define INTF 0x04 /* sta: int on the fly (reset by wr)*/
+ #define SIP 0x02 /* sta: scsi-interrupt */
+ #define DIP 0x01 /* sta: host/script interrupt */
+
+/*15*/ u8 nc_istat1; /* 896 and later cores only */
+ #define FLSH 0x04 /* sta: chip is flushing */
+ #define SRUN 0x02 /* sta: scripts are running */
+ #define SIRQD 0x01 /* r/w: disable INT pin */
+
+/*16*/ u8 nc_mbox0; /* 896 and later cores only */
+/*17*/ u8 nc_mbox1; /* 896 and later cores only */
+
+/*18*/ u8 nc_ctest0;
+ #define EHP 0x04 /* 720 even host parity */
+/*19*/ u8 nc_ctest1;
+
+/*1a*/ u8 nc_ctest2;
+ #define CSIGP 0x40
+ /* bits 0-2,7 rsvd for C1010 */
+
+/*1b*/ u8 nc_ctest3;
+ #define FLF 0x08 /* cmd: flush dma fifo */
+ #define CLF 0x04 /* cmd: clear dma fifo */
+ #define FM 0x02 /* mod: fetch pin mode */
+ #define WRIE 0x01 /* mod: write and invalidate enable */
+ /* bits 4-7 rsvd for C1010 */
+
+/*1c*/ u32 nc_temp; /* ### Temporary stack */
+
+/*20*/ u8 nc_dfifo;
+/*21*/ u8 nc_ctest4;
+ #define MUX 0x80 /* 720 host bus multiplex mode */
+ #define BDIS 0x80 /* mod: burst disable */
+ #define MPEE 0x08 /* mod: master parity error enable */
+
+/*22*/ u8 nc_ctest5;
+ #define DFS 0x20 /* mod: dma fifo size */
+ /* bits 0-1, 3-7 rsvd for C1010 */
+/*23*/ u8 nc_ctest6;
+
+/*24*/ u32 nc_dbc; /* ### Byte count and command */
+/*28*/ u32 nc_dnad; /* ### Next command register */
+/*2c*/ u32 nc_dsp; /* --> Script Pointer */
+/*30*/ u32 nc_dsps; /* --> Script pointer save/opcode#2 */
+
+/*34*/ u8 nc_scratcha; /* Temporary register a */
+/*35*/ u8 nc_scratcha1;
+/*36*/ u8 nc_scratcha2;
+/*37*/ u8 nc_scratcha3;
+
+/*38*/ u8 nc_dmode;
+ #define BL_2 0x80 /* mod: burst length shift value +2 */
+ #define BL_1 0x40 /* mod: burst length shift value +1 */
+ #define ERL 0x08 /* mod: enable read line */
+ #define ERMP 0x04 /* mod: enable read multiple */
+ #define BOF 0x02 /* mod: burst op code fetch */
+
+/*39*/ u8 nc_dien;
+/*3a*/ u8 nc_sbr;
+
+/*3b*/ u8 nc_dcntl; /* --> Script execution control */
+ #define CLSE 0x80 /* mod: cache line size enable */
+ #define PFF 0x40 /* cmd: pre-fetch flush */
+ #define PFEN 0x20 /* mod: pre-fetch enable */
+ #define EA 0x20 /* mod: 720 enable-ack */
+ #define SSM 0x10 /* mod: single step mode */
+ #define IRQM 0x08 /* mod: irq mode (1 = totem pole !) */
+ #define STD 0x04 /* cmd: start dma mode */
+ #define IRQD 0x02 /* mod: irq disable */
+ #define NOCOM 0x01 /* cmd: protect sfbr while reselect */
+ /* bits 0-1 rsvd for C1010 */
+
+/*3c*/ u32 nc_adder;
+
+/*40*/ u16 nc_sien; /* -->: interrupt enable */
+/*42*/ u16 nc_sist; /* <--: interrupt status */
+ #define SBMC 0x1000/* sta: SCSI Bus Mode Change (895/6 only) */
+ #define STO 0x0400/* sta: timeout (select) */
+ #define GEN 0x0200/* sta: timeout (general) */
+ #define HTH 0x0100/* sta: timeout (handshake) */
+ #define MA 0x80 /* sta: phase mismatch */
+ #define CMP 0x40 /* sta: arbitration complete */
+ #define SEL 0x20 /* sta: selected by another device */
+ #define RSL 0x10 /* sta: reselected by another device*/
+ #define SGE 0x08 /* sta: gross error (over/underflow)*/
+ #define UDC 0x04 /* sta: unexpected disconnect */
+ #define RST 0x02 /* sta: scsi bus reset detected */
+ #define PAR 0x01 /* sta: scsi parity error */
+
+/*44*/ u8 nc_slpar;
+/*45*/ u8 nc_swide;
+/*46*/ u8 nc_macntl;
+/*47*/ u8 nc_gpcntl;
+/*48*/ u8 nc_stime0; /* cmd: timeout for select&handshake*/
+/*49*/ u8 nc_stime1; /* cmd: timeout user defined */
+/*4a*/ u16 nc_respid; /* sta: Reselect-IDs */
+
+/*4c*/ u8 nc_stest0;
+
+/*4d*/ u8 nc_stest1;
+ #define SCLK 0x80 /* Use the PCI clock as SCSI clock */
+ #define DBLEN 0x08 /* clock doubler running */
+ #define DBLSEL 0x04 /* clock doubler selected */
+
+
+/*4e*/ u8 nc_stest2;
+ #define ROF 0x40 /* reset scsi offset (after gross error!) */
+ #define DIF 0x20 /* 720 SCSI differential mode */
+ #define EXT 0x02 /* extended filtering */
+
+/*4f*/ u8 nc_stest3;
+ #define TE 0x80 /* c: tolerAnt enable */
+ #define HSC 0x20 /* c: Halt SCSI Clock */
+ #define CSF 0x02 /* c: clear scsi fifo */
+
+/*50*/ u16 nc_sidl; /* Lowlevel: latched from scsi data */
+/*52*/ u8 nc_stest4;
+ #define SMODE 0xc0 /* SCSI bus mode (895/6 only) */
+ #define SMODE_HVD 0x40 /* High Voltage Differential */
+ #define SMODE_SE 0x80 /* Single Ended */
+ #define SMODE_LVD 0xc0 /* Low Voltage Differential */
+ #define LCKFRQ 0x20 /* Frequency Lock (895/6 only) */
+ /* bits 0-5 rsvd for C1010 */
+
+/*53*/ u8 nc_53_;
+/*54*/ u16 nc_sodl; /* Lowlevel: data out to scsi data */
+/*56*/ u8 nc_ccntl0; /* Chip Control 0 (896) */
+ #define ENPMJ 0x80 /* Enable Phase Mismatch Jump */
+ #define PMJCTL 0x40 /* Phase Mismatch Jump Control */
+ #define ENNDJ 0x20 /* Enable Non Data PM Jump */
+ #define DISFC 0x10 /* Disable Auto FIFO Clear */
+ #define DILS 0x02 /* Disable Internal Load/Store */
+ #define DPR 0x01 /* Disable Pipe Req */
+
+/*57*/ u8 nc_ccntl1; /* Chip Control 1 (896) */
+ #define ZMOD 0x80 /* High Impedance Mode */
+ #define DIC 0x10 /* Disable Internal Cycles */
+ #define DDAC 0x08 /* Disable Dual Address Cycle */
+ #define XTIMOD 0x04 /* 64-bit Table Ind. Indexing Mode */
+ #define EXTIBMV 0x02 /* Enable 64-bit Table Ind. BMOV */
+ #define EXDBMV 0x01 /* Enable 64-bit Direct BMOV */
+
+/*58*/ u16 nc_sbdl; /* Lowlevel: data from scsi data */
+/*5a*/ u16 nc_5a_;
+
+/*5c*/ u8 nc_scr0; /* Working register B */
+/*5d*/ u8 nc_scr1; /* */
+/*5e*/ u8 nc_scr2; /* */
+/*5f*/ u8 nc_scr3; /* */
+
+/*60*/ u8 nc_scrx[64]; /* Working register C-R */
+/*a0*/ u32 nc_mmrs; /* Memory Move Read Selector */
+/*a4*/ u32 nc_mmws; /* Memory Move Write Selector */
+/*a8*/ u32 nc_sfs; /* Script Fetch Selector */
+/*ac*/ u32 nc_drs; /* DSA Relative Selector */
+/*b0*/ u32 nc_sbms; /* Static Block Move Selector */
+/*b4*/ u32 nc_dbms; /* Dynamic Block Move Selector */
+/*b8*/ u32 nc_dnad64; /* DMA Next Address 64 */
+/*bc*/ u16 nc_scntl4; /* C1010 only */
+ #define U3EN 0x80 /* Enable Ultra 3 */
+ #define AIPEN 0x40 /* Allow check upper byte lanes */
+ #define XCLKH_DT 0x08 /* Extra clock of data hold on DT
+ transfer edge */
+ #define XCLKH_ST 0x04 /* Extra clock of data hold on ST
+ transfer edge */
+
+/*be*/ u8 nc_aipcntl0; /* Epat Control 1 C1010 only */
+/*bf*/ u8 nc_aipcntl1; /* AIP Control C1010_66 Only */
+
+/*c0*/ u32 nc_pmjad1; /* Phase Mismatch Jump Address 1 */
+/*c4*/ u32 nc_pmjad2; /* Phase Mismatch Jump Address 2 */
+/*c8*/ u8 nc_rbc; /* Remaining Byte Count */
+/*c9*/ u8 nc_rbc1; /* */
+/*ca*/ u8 nc_rbc2; /* */
+/*cb*/ u8 nc_rbc3; /* */
+
+/*cc*/ u8 nc_ua; /* Updated Address */
+/*cd*/ u8 nc_ua1; /* */
+/*ce*/ u8 nc_ua2; /* */
+/*cf*/ u8 nc_ua3; /* */
+/*d0*/ u32 nc_esa; /* Entry Storage Address */
+/*d4*/ u8 nc_ia; /* Instruction Address */
+/*d5*/ u8 nc_ia1;
+/*d6*/ u8 nc_ia2;
+/*d7*/ u8 nc_ia3;
+/*d8*/ u32 nc_sbc; /* SCSI Byte Count (3 bytes only) */
+/*dc*/ u32 nc_csbc; /* Cumulative SCSI Byte Count */
+
+ /* Following for C1010 only */
+/*e0*/ u16 nc_crcpad; /* CRC Value */
+/*e2*/ u8 nc_crccntl0; /* CRC control register */
+ #define SNDCRC 0x10 /* Send CRC Request */
+/*e3*/ u8 nc_crccntl1; /* CRC control register */
+/*e4*/ u32 nc_crcdata; /* CRC data register */
+/*e8*/ u32 nc_e8_; /* rsvd */
+/*ec*/ u32 nc_ec_; /* rsvd */
+/*f0*/ u16 nc_dfbc; /* DMA FIFO byte count */
+
+};
+
+/*-----------------------------------------------------------
+**
+** Utility macros for the script.
+**
+**-----------------------------------------------------------
+*/
+
+#define REGJ(p,r) (offsetof(struct ncr_reg, p ## r))
+#define REG(r) REGJ (nc_, r)
+
+typedef u32 ncrcmd;
+
+/*-----------------------------------------------------------
+**
+** SCSI phases
+**
+** DT phases illegal for ncr driver.
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_DATA_OUT 0x00000000
+#define SCR_DATA_IN 0x01000000
+#define SCR_COMMAND 0x02000000
+#define SCR_STATUS 0x03000000
+#define SCR_DT_DATA_OUT 0x04000000
+#define SCR_DT_DATA_IN 0x05000000
+#define SCR_MSG_OUT 0x06000000
+#define SCR_MSG_IN 0x07000000
+
+#define SCR_ILG_OUT 0x04000000
+#define SCR_ILG_IN 0x05000000
+
+/*-----------------------------------------------------------
+**
+** Data transfer via SCSI.
+**
+**-----------------------------------------------------------
+**
+** MOVE_ABS (LEN)
+** <<start address>>
+**
+** MOVE_IND (LEN)
+** <<dnad_offset>>
+**
+** MOVE_TBL
+** <<dnad_offset>>
+**
+**-----------------------------------------------------------
+*/
+
+#define OPC_MOVE 0x08000000
+
+#define SCR_MOVE_ABS(l) ((0x00000000 | OPC_MOVE) | (l))
+#define SCR_MOVE_IND(l) ((0x20000000 | OPC_MOVE) | (l))
+#define SCR_MOVE_TBL (0x10000000 | OPC_MOVE)
+
+#define SCR_CHMOV_ABS(l) ((0x00000000) | (l))
+#define SCR_CHMOV_IND(l) ((0x20000000) | (l))
+#define SCR_CHMOV_TBL (0x10000000)
+
+struct scr_tblmove {
+ u32 size;
+ u32 addr;
+};
+
+/*-----------------------------------------------------------
+**
+** Selection
+**
+**-----------------------------------------------------------
+**
+** SEL_ABS | SCR_ID (0..15) [ | REL_JMP]
+** <<alternate_address>>
+**
+** SEL_TBL | << dnad_offset>> [ | REL_JMP]
+** <<alternate_address>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SEL_ABS 0x40000000
+#define SCR_SEL_ABS_ATN 0x41000000
+#define SCR_SEL_TBL 0x42000000
+#define SCR_SEL_TBL_ATN 0x43000000
+
+
+#ifdef SCSI_NCR_BIG_ENDIAN
+struct scr_tblsel {
+ u8 sel_scntl3;
+ u8 sel_id;
+ u8 sel_sxfer;
+ u8 sel_scntl4;
+};
+#else
+struct scr_tblsel {
+ u8 sel_scntl4;
+ u8 sel_sxfer;
+ u8 sel_id;
+ u8 sel_scntl3;
+};
+#endif
+
+#define SCR_JMP_REL 0x04000000
+#define SCR_ID(id) (((u32)(id)) << 16)
+
+/*-----------------------------------------------------------
+**
+** Waiting for Disconnect or Reselect
+**
+**-----------------------------------------------------------
+**
+** WAIT_DISC
+** dummy: <<alternate_address>>
+**
+** WAIT_RESEL
+** <<alternate_address>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_WAIT_DISC 0x48000000
+#define SCR_WAIT_RESEL 0x50000000
+
+/*-----------------------------------------------------------
+**
+** Bit Set / Reset
+**
+**-----------------------------------------------------------
+**
+** SET (flags {|.. })
+**
+** CLR (flags {|.. })
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SET(f) (0x58000000 | (f))
+#define SCR_CLR(f) (0x60000000 | (f))
+
+#define SCR_CARRY 0x00000400
+#define SCR_TRG 0x00000200
+#define SCR_ACK 0x00000040
+#define SCR_ATN 0x00000008
+
+
+
+
+/*-----------------------------------------------------------
+**
+** Memory to memory move
+**
+**-----------------------------------------------------------
+**
+** COPY (bytecount)
+** << source_address >>
+** << destination_address >>
+**
+** SCR_COPY sets the NO FLUSH option by default.
+** SCR_COPY_F does not set this option.
+**
+** For chips which do not support this option,
+** ncr_copy_and_bind() will remove this bit.
+**-----------------------------------------------------------
+*/
+
+#define SCR_NO_FLUSH 0x01000000
+
+#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n))
+#define SCR_COPY_F(n) (0xc0000000 | (n))
+
+/*-----------------------------------------------------------
+**
+** Register move and binary operations
+**
+**-----------------------------------------------------------
+**
+** SFBR_REG (reg, op, data) reg = SFBR op data
+** << 0 >>
+**
+** REG_SFBR (reg, op, data) SFBR = reg op data
+** << 0 >>
+**
+** REG_REG (reg, op, data) reg = reg op data
+** << 0 >>
+**
+**-----------------------------------------------------------
+** On 810A, 860, 825A, 875, 895 and 896 chips the content
+** of SFBR register can be used as data (SCR_SFBR_DATA).
+** The 896 has additionnal IO registers starting at
+** offset 0x80. Bit 7 of register offset is stored in
+** bit 7 of the SCRIPTS instruction first DWORD.
+**-----------------------------------------------------------
+*/
+
+#define SCR_REG_OFS(ofs) ((((ofs) & 0x7f) << 16ul) + ((ofs) & 0x80))
+
+#define SCR_SFBR_REG(reg,op,data) \
+ (0x68000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+#define SCR_REG_SFBR(reg,op,data) \
+ (0x70000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+#define SCR_REG_REG(reg,op,data) \
+ (0x78000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+
+#define SCR_LOAD 0x00000000
+#define SCR_SHL 0x01000000
+#define SCR_OR 0x02000000
+#define SCR_XOR 0x03000000
+#define SCR_AND 0x04000000
+#define SCR_SHR 0x05000000
+#define SCR_ADD 0x06000000
+#define SCR_ADDC 0x07000000
+
+#define SCR_SFBR_DATA (0x00800000>>8ul) /* Use SFBR as data */
+
+/*-----------------------------------------------------------
+**
+** FROM_REG (reg) SFBR = reg
+** << 0 >>
+**
+** TO_REG (reg) reg = SFBR
+** << 0 >>
+**
+** LOAD_REG (reg, data) reg = <data>
+** << 0 >>
+**
+** LOAD_SFBR(data) SFBR = <data>
+** << 0 >>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_FROM_REG(reg) \
+ SCR_REG_SFBR(reg,SCR_OR,0)
+
+#define SCR_TO_REG(reg) \
+ SCR_SFBR_REG(reg,SCR_OR,0)
+
+#define SCR_LOAD_REG(reg,data) \
+ SCR_REG_REG(reg,SCR_LOAD,data)
+
+#define SCR_LOAD_SFBR(data) \
+ (SCR_REG_SFBR (gpreg, SCR_LOAD, data))
+
+/*-----------------------------------------------------------
+**
+** LOAD from memory to register.
+** STORE from register to memory.
+**
+** Only supported by 810A, 860, 825A, 875, 895 and 896.
+**
+**-----------------------------------------------------------
+**
+** LOAD_ABS (LEN)
+** <<start address>>
+**
+** LOAD_REL (LEN) (DSA relative)
+** <<dsa_offset>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_REG_OFS2(ofs) (((ofs) & 0xff) << 16ul)
+#define SCR_NO_FLUSH2 0x02000000
+#define SCR_DSA_REL2 0x10000000
+
+#define SCR_LOAD_R(reg, how, n) \
+ (0xe1000000 | how | (SCR_REG_OFS2(REG(reg))) | (n))
+
+#define SCR_STORE_R(reg, how, n) \
+ (0xe0000000 | how | (SCR_REG_OFS2(REG(reg))) | (n))
+
+#define SCR_LOAD_ABS(reg, n) SCR_LOAD_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_LOAD_REL(reg, n) SCR_LOAD_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2, n)
+#define SCR_LOAD_ABS_F(reg, n) SCR_LOAD_R(reg, 0, n)
+#define SCR_LOAD_REL_F(reg, n) SCR_LOAD_R(reg, SCR_DSA_REL2, n)
+
+#define SCR_STORE_ABS(reg, n) SCR_STORE_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_STORE_REL(reg, n) SCR_STORE_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2,n)
+#define SCR_STORE_ABS_F(reg, n) SCR_STORE_R(reg, 0, n)
+#define SCR_STORE_REL_F(reg, n) SCR_STORE_R(reg, SCR_DSA_REL2, n)
+
+
+/*-----------------------------------------------------------
+**
+** Waiting for Disconnect or Reselect
+**
+**-----------------------------------------------------------
+**
+** JUMP [ | IFTRUE/IFFALSE ( ... ) ]
+** <<address>>
+**
+** JUMPR [ | IFTRUE/IFFALSE ( ... ) ]
+** <<distance>>
+**
+** CALL [ | IFTRUE/IFFALSE ( ... ) ]
+** <<address>>
+**
+** CALLR [ | IFTRUE/IFFALSE ( ... ) ]
+** <<distance>>
+**
+** RETURN [ | IFTRUE/IFFALSE ( ... ) ]
+** <<dummy>>
+**
+** INT [ | IFTRUE/IFFALSE ( ... ) ]
+** <<ident>>
+**
+** INT_FLY [ | IFTRUE/IFFALSE ( ... ) ]
+** <<ident>>
+**
+** Conditions:
+** WHEN (phase)
+** IF (phase)
+** CARRYSET
+** DATA (data, mask)
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_NO_OP 0x80000000
+#define SCR_JUMP 0x80080000
+#define SCR_JUMP64 0x80480000
+#define SCR_JUMPR 0x80880000
+#define SCR_CALL 0x88080000
+#define SCR_CALLR 0x88880000
+#define SCR_RETURN 0x90080000
+#define SCR_INT 0x98080000
+#define SCR_INT_FLY 0x98180000
+
+#define IFFALSE(arg) (0x00080000 | (arg))
+#define IFTRUE(arg) (0x00000000 | (arg))
+
+#define WHEN(phase) (0x00030000 | (phase))
+#define IF(phase) (0x00020000 | (phase))
+
+#define DATA(D) (0x00040000 | ((D) & 0xff))
+#define MASK(D,M) (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff))
+
+#define CARRYSET (0x00200000)
+
+/*-----------------------------------------------------------
+**
+** SCSI constants.
+**
+**-----------------------------------------------------------
+*/
+
+/*
+** Messages
+*/
+
+#define M_COMPLETE COMMAND_COMPLETE
+#define M_EXTENDED EXTENDED_MESSAGE
+#define M_SAVE_DP SAVE_POINTERS
+#define M_RESTORE_DP RESTORE_POINTERS
+#define M_DISCONNECT DISCONNECT
+#define M_ID_ERROR INITIATOR_ERROR
+#define M_ABORT ABORT_TASK_SET
+#define M_REJECT MESSAGE_REJECT
+#define M_NOOP NOP
+#define M_PARITY MSG_PARITY_ERROR
+#define M_LCOMPLETE LINKED_CMD_COMPLETE
+#define M_FCOMPLETE LINKED_FLG_CMD_COMPLETE
+#define M_RESET TARGET_RESET
+#define M_ABORT_TAG ABORT_TASK
+#define M_CLEAR_QUEUE CLEAR_TASK_SET
+#define M_INIT_REC INITIATE_RECOVERY
+#define M_REL_REC RELEASE_RECOVERY
+#define M_TERMINATE (0x11)
+#define M_SIMPLE_TAG SIMPLE_QUEUE_TAG
+#define M_HEAD_TAG HEAD_OF_QUEUE_TAG
+#define M_ORDERED_TAG ORDERED_QUEUE_TAG
+#define M_IGN_RESIDUE IGNORE_WIDE_RESIDUE
+#define M_IDENTIFY (0x80)
+
+#define M_X_MODIFY_DP EXTENDED_MODIFY_DATA_POINTER
+#define M_X_SYNC_REQ EXTENDED_SDTR
+#define M_X_WIDE_REQ EXTENDED_WDTR
+#define M_X_PPR_REQ EXTENDED_PPR
+
+/*
+** Status
+*/
+
+#define S_GOOD (0x00)
+#define S_CHECK_COND (0x02)
+#define S_COND_MET (0x04)
+#define S_BUSY (0x08)
+#define S_INT (0x10)
+#define S_INT_COND_MET (0x14)
+#define S_CONFLICT (0x18)
+#define S_TERMINATED (0x20)
+#define S_QUEUE_FULL (0x28)
+#define S_ILLEGAL (0xff)
+#define S_SENSE (0x80)
+
+/*
+ * End of ncrreg from FreeBSD
+ */
+
+#endif /* defined SYM53C8XX_DEFS_H */
diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c
new file mode 100644
index 000000000000..6dc2897672a1
--- /dev/null
+++ b/drivers/scsi/t128.c
@@ -0,0 +1,449 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+/*
+ * Trantor T128/T128F/T228 driver
+ * Note : architecturally, the T100 and T130 are different and won't
+ * work
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 3.
+ *
+ * For more information, please consult
+ *
+ * Trantor Systems, Ltd.
+ * T128/T128F/T228 SCSI Host Adapter
+ * Hardware Specifications
+ *
+ * Trantor Systems, Ltd.
+ * 5415 Randall Place
+ * Fremont, CA 94538
+ * 1+ (415) 770-1400, FAX 1+ (415) 770-9910
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
+ * only really want to use this if you're having a problem with
+ * dropped characters during high speed communications, and even
+ * then, you're going to be better off twiddling with transfersize.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - since the board is memory mapped,
+ * a BIOS signature is scanned for to locate the registers.
+ * An interrupt is triggered to autoprobe for the interrupt
+ * line.
+ *
+ * 2. With command line overrides - t128=address,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 3. With the T128_OVERRIDE compile time define. This is
+ * specified as an array of address, irq tuples. Ie, for
+ * one board at the default 0xcc000 address, IRQ5, I could say
+ * -DT128_OVERRIDE={{0xcc000, 5}}
+ *
+ * Note that if the override methods are used, place holders must
+ * be specified for other boards in the system.
+ *
+ * T128/T128F jumper/dipswitch settings (note : on my sample, the switches
+ * were epoxy'd shut, meaning I couldn't change the 0xcc000 base address) :
+ *
+ * T128 Sw7 Sw8 Sw6 = 0ws Sw5 = boot
+ * T128F Sw6 Sw7 Sw5 = 0ws Sw4 = boot Sw8 = floppy disable
+ * cc000 off off
+ * c8000 off on
+ * dc000 on off
+ * d8000 on on
+ *
+ *
+ * Interrupts
+ * There is a 12 pin jumper block, jp1, numbered as follows :
+ * T128 (JP1) T128F (J5)
+ * 2 4 6 8 10 12 11 9 7 5 3 1
+ * 1 3 5 7 9 11 12 10 8 6 4 2
+ *
+ * 3 2-4
+ * 5 1-3
+ * 7 3-5
+ * T128F only
+ * 10 8-10
+ * 12 7-9
+ * 14 10-12
+ * 15 9-11
+ */
+
+/*
+ * $Log: t128.c,v $
+ */
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "t128.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+
+static struct override {
+ unsigned long address;
+ int irq;
+} overrides
+#ifdef T128_OVERRIDE
+ [] __initdata = T128_OVERRIDE;
+#else
+ [4] __initdata = {{0, IRQ_AUTO}, {0, IRQ_AUTO},
+ {0 ,IRQ_AUTO}, {0, IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned int address;
+ int noauto;
+} bases[] __initdata = {
+ { 0xcc000, 0}, { 0xc8000, 0}, { 0xdc000, 0}, { 0xd8000, 0}
+};
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+static struct signature {
+ const char *string;
+ int offset;
+} signatures[] __initdata = {
+{"TSROM: SCSI BIOS, Version 1.12", 0x36},
+};
+
+#define NO_SIGNATURES (sizeof (signatures) / sizeof (struct signature))
+
+/*
+ * Function : t128_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void __init t128_setup(char *str, int *ints){
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("t128_setup : usage t128=address,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].address = ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].address == ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int t128_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes T128,T128F, or T228 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int __init t128_detect(Scsi_Host_Template * tpnt){
+ static int current_override = 0, current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned long base;
+ void __iomem *p;
+ int sig, count;
+
+ tpnt->proc_name = "t128";
+ tpnt->proc_info = &t128_proc_info;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ base = 0;
+ p = NULL;
+
+ if (overrides[current_override].address) {
+ base = overrides[current_override].address;
+ p = ioremap(bases[current_base].address, 0x2000);
+ if (!p)
+ base = 0;
+ } else
+ for (; !base && (current_base < NO_BASES); ++current_base) {
+#if (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : probing address %08x\n", bases[current_base].address);
+#endif
+ if (bases[current_base].noauto)
+ continue;
+ p = ioremap(bases[current_base].address, 0x2000);
+ if (!p)
+ continue;
+ for (sig = 0; sig < NO_SIGNATURES; ++sig)
+ if (check_signature(p + signatures[sig].offset,
+ signatures[sig].string,
+ strlen(signatures[sig].string))) {
+ base = bases[current_base].address;
+#if (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : detected board.\n");
+#endif
+ goto found;
+ }
+ iounmap(p);
+ }
+
+#if defined(TDEBUG) && (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : base = %08x\n", (unsigned int) base);
+#endif
+
+ if (!base)
+ break;
+
+found:
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ if(instance == NULL)
+ break;
+
+ instance->base = base;
+ ((struct NCR5380_hostdata *)instance->hostdata)->base = p;
+
+ NCR5380_init(instance, 0);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, T128_IRQS);
+
+ if (instance->irq != SCSI_IRQ_NONE)
+ if (request_irq(instance->irq, t128_intr, SA_INTERRUPT, "t128", instance)) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = SCSI_IRQ_NONE;
+ }
+
+ if (instance->irq == SCSI_IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+#if defined(TDEBUG) && (TDEBUG & TDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%08lx", instance->host_no, instance->base);
+ if (instance->irq == SCSI_IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, T128_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+static int t128_release(struct Scsi_Host *shost)
+{
+ NCR5380_local_declare();
+ NCR5380_setup(shost);
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ NCR5380_exit(shost);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ iounmap(base);
+ return 0;
+}
+
+/*
+ * Function : int t128_biosparam(Disk * disk, struct block_device *dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int * ip)
+{
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = capacity >> 11;
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
+ int len) {
+ NCR5380_local_declare();
+ void __iomem *reg;
+ unsigned char *d = dst;
+ register int i = len;
+
+ NCR5380_setup(instance);
+ reg = base + T_DATA_REG_OFFSET;
+
+#if 0
+ for (; i; --i) {
+ while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier();
+#else
+ while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier();
+ for (; i; --i) {
+#endif
+ *d++ = readb(reg);
+ }
+
+ if (readb(base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
+ unsigned char tmp;
+ void __iomem *foo = base + T_CONTROL_REG_OFFSET;
+ tmp = readb(foo);
+ writeb(tmp | T_CR_CT, foo);
+ writeb(tmp, foo);
+ printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
+ int len) {
+ NCR5380_local_declare();
+ void __iomem *reg;
+ unsigned char *s = src;
+ register int i = len;
+
+ NCR5380_setup(instance);
+ reg = base + T_DATA_REG_OFFSET;
+
+#if 0
+ for (; i; --i) {
+ while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier();
+#else
+ while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier();
+ for (; i; --i) {
+#endif
+ writeb(*s++, reg);
+ }
+
+ if (readb(base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
+ unsigned char tmp;
+ void __iomem *foo = base + T_CONTROL_REG_OFFSET;
+ tmp = readb(foo);
+ writeb(tmp | T_CR_CT, foo);
+ writeb(tmp, foo);
+ printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+MODULE_LICENSE("GPL");
+
+#include "NCR5380.c"
+
+static Scsi_Host_Template driver_template = {
+ .name = "Trantor T128/T128F/T228",
+ .detect = t128_detect,
+ .release = t128_release,
+ .queuecommand = t128_queue_command,
+ .eh_abort_handler = t128_abort,
+ .eh_bus_reset_handler = t128_bus_reset,
+ .eh_host_reset_handler = t128_host_reset,
+ .eh_device_reset_handler = t128_device_reset,
+ .bios_param = t128_biosparam,
+ .can_queue = CAN_QUEUE,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = CMD_PER_LUN,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h
new file mode 100644
index 000000000000..161ba53d982b
--- /dev/null
+++ b/drivers/scsi/t128.h
@@ -0,0 +1,155 @@
+/*
+ * Trantor T128/T128F/T228 defines
+ * Note : architecturally, the T100 and T128 are different and won't work
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 3.
+ *
+ * For more information, please consult
+ *
+ * Trantor Systems, Ltd.
+ * T128/T128F/T228 SCSI Host Adapter
+ * Hardware Specifications
+ *
+ * Trantor Systems, Ltd.
+ * 5415 Randall Place
+ * Fremont, CA 94538
+ * 1+ (415) 770-1400, FAX 1+ (415) 770-9910
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: t128.h,v $
+ */
+
+#ifndef T128_H
+#define T128_H
+
+#define T128_PUBLIC_RELEASE 3
+
+#define TDEBUG_INIT 0x1
+#define TDEBUG_TRANSFER 0x2
+
+/*
+ * The trantor boards are memory mapped. They use an NCR5380 or
+ * equivalent (my sample board had part second sourced from ZILOG).
+ * NCR's recommended "Pseudo-DMA" architecture is used, where
+ * a PAL drives the DMA signals on the 5380 allowing fast, blind
+ * transfers with proper handshaking.
+ */
+
+/*
+ * Note : a boot switch is provided for the purpose of informing the
+ * firmware to boot or not boot from attached SCSI devices. So, I imagine
+ * there are fewer people who've yanked the ROM like they do on the Seagate
+ * to make bootup faster, and I'll probably use this for autodetection.
+ */
+#define T_ROM_OFFSET 0
+
+/*
+ * Note : my sample board *WAS NOT* populated with the SRAM, so this
+ * can't be used for autodetection without a ROM present.
+ */
+#define T_RAM_OFFSET 0x1800
+
+/*
+ * All of the registers are allocated 32 bytes of address space, except
+ * for the data register (read/write to/from the 5380 in pseudo-DMA mode)
+ */
+#define T_CONTROL_REG_OFFSET 0x1c00 /* rw */
+#define T_CR_INT 0x10 /* Enable interrupts */
+#define T_CR_CT 0x02 /* Reset watchdog timer */
+
+#define T_STATUS_REG_OFFSET 0x1c20 /* ro */
+#define T_ST_BOOT 0x80 /* Boot switch */
+#define T_ST_S3 0x40 /* User settable switches, */
+#define T_ST_S2 0x20 /* read 0 when switch is on, 1 off */
+#define T_ST_S1 0x10
+#define T_ST_PS2 0x08 /* Set for Microchannel 228 */
+#define T_ST_RDY 0x04 /* 5380 DRQ */
+#define T_ST_TIM 0x02 /* indicates 40us watchdog timer fired */
+#define T_ST_ZERO 0x01 /* Always zero */
+
+#define T_5380_OFFSET 0x1d00 /* 8 registers here, see NCR5380.h */
+
+#define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */
+
+#ifndef ASM
+static int t128_abort(Scsi_Cmnd *);
+static int t128_biosparam(struct scsi_device *, struct block_device *,
+ sector_t, int*);
+static int t128_detect(Scsi_Host_Template *);
+static int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int t128_host_reset(Scsi_Cmnd *);
+static int t128_bus_reset(Scsi_Cmnd *);
+static int t128_device_reset(Scsi_Cmnd *);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ void __iomem *base
+
+#define NCR5380_local_declare() \
+ void __iomem *base
+
+#define NCR5380_setup(instance) \
+ base = ((struct NCR5380_hostdata *)(instance->hostdata))->base
+
+#define T128_address(reg) (base + T_5380_OFFSET + ((reg) * 0x20))
+
+#if !(TDEBUG & TDEBUG_TRANSFER)
+#define NCR5380_read(reg) readb(T128_address(reg))
+#define NCR5380_write(reg, value) writeb((value),(T128_address(reg)))
+#else
+#define NCR5380_read(reg) \
+ (((unsigned char) printk("scsi%d : read register %d at address %08x\n"\
+ , instance->hostno, (reg), T128_address(reg))), readb(T128_address(reg)))
+
+#define NCR5380_write(reg, value) { \
+ printk("scsi%d : write %02x to register %d at address %08x\n", \
+ instance->hostno, (value), (reg), T128_address(reg)); \
+ writeb((value), (T128_address(reg))); \
+}
+#endif
+
+#define NCR5380_intr t128_intr
+#define do_NCR5380_intr do_t128_intr
+#define NCR5380_queue_command t128_queue_command
+#define NCR5380_abort t128_abort
+#define NCR5380_host_reset t128_host_reset
+#define NCR5380_device_reset t128_device_reset
+#define NCR5380_bus_reset t128_bus_reset
+#define NCR5380_proc_info t128_proc_info
+
+/* 15 14 12 10 7 5 3
+ 1101 0100 1010 1000 */
+
+#define T128_IRQS 0xc4a8
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* T128_H */
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
new file mode 100644
index 000000000000..ee9df02efd5b
--- /dev/null
+++ b/drivers/scsi/tmscsim.c
@@ -0,0 +1,2693 @@
+/************************************************************************
+ * FILE NAME : TMSCSIM.C *
+ * BY : C.L. Huang, ching@tekram.com.tw *
+ * Description: Device Driver for Tekram DC-390(T) PCI SCSI *
+ * Bus Master Host Adapter *
+ * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. *
+ ************************************************************************
+ * (C) Copyright: put under GNU GPL in 10/96 *
+ * (see Documentation/scsi/tmscsim.txt) *
+ ************************************************************************
+ * $Id: tmscsim.c,v 2.60.2.30 2000/12/20 01:07:12 garloff Exp $ *
+ * Enhancements and bugfixes by *
+ * Kurt Garloff <kurt@garloff.de> <garloff@suse.de> *
+ ************************************************************************
+ * HISTORY: *
+ * *
+ * REV# DATE NAME DESCRIPTION *
+ * 1.00 96/04/24 CLH First release *
+ * 1.01 96/06/12 CLH Fixed bug of Media Change for Removable *
+ * Device, scan all LUN. Support Pre2.0.10 *
+ * 1.02 96/06/18 CLH Fixed bug of Command timeout ... *
+ * 1.03 96/09/25 KG Added tmscsim_proc_info() *
+ * 1.04 96/10/11 CLH Updating for support KV 2.0.x *
+ * 1.05 96/10/18 KG Fixed bug in DC390_abort(null ptr deref)*
+ * 1.06 96/10/25 KG Fixed module support *
+ * 1.07 96/11/09 KG Fixed tmscsim_proc_info() *
+ * 1.08 96/11/18 KG Fixed null ptr in DC390_Disconnect() *
+ * 1.09 96/11/30 KG Added register the allocated IO space *
+ * 1.10 96/12/05 CLH Modified tmscsim_proc_info(), and reset *
+ * pending interrupt in DC390_detect() *
+ * 1.11 97/02/05 KG/CLH Fixeds problem with partitions greater *
+ * than 1GB *
+ * 1.12 98/02/15 MJ Rewritten PCI probing *
+ * 1.13 98/04/08 KG Support for non DC390, __initfunc decls,*
+ * changed max devs from 10 to 16 *
+ * 1.14a 98/05/05 KG Dynamic DCB allocation, add-single-dev *
+ * for LUNs if LUN_SCAN (BIOS) not set *
+ * runtime config using /proc interface *
+ * 1.14b 98/05/06 KG eliminated cli (); sti (); spinlocks *
+ * 1.14c 98/05/07 KG 2.0.x compatibility *
+ * 1.20a 98/05/07 KG changed names of funcs to be consistent *
+ * DC390_ (entry points), dc390_ (internal)*
+ * reworked locking *
+ * 1.20b 98/05/12 KG bugs: version, kfree, _ctmp *
+ * debug output *
+ * 1.20c 98/05/12 KG bugs: kfree, parsing, EEpromDefaults *
+ * 1.20d 98/05/14 KG bugs: list linkage, clear flag after *
+ * reset on startup, code cleanup *
+ * 1.20e 98/05/15 KG spinlock comments, name space cleanup *
+ * pLastDCB now part of ACB structure *
+ * added stats, timeout for 2.1, TagQ bug *
+ * RESET and INQUIRY interface commands *
+ * 1.20f 98/05/18 KG spinlocks fixes, max_lun fix, free DCBs *
+ * for missing LUNs, pending int *
+ * 1.20g 98/05/19 KG Clean up: Avoid short *
+ * 1.20h 98/05/21 KG Remove AdaptSCSIID, max_lun ... *
+ * 1.20i 98/05/21 KG Aiiie: Bug with TagQMask *
+ * 1.20j 98/05/24 KG Handle STAT_BUSY, handle pACB->pLinkDCB *
+ * == 0 in remove_dev and DoingSRB_Done *
+ * 1.20k 98/05/25 KG DMA_INT (experimental) *
+ * 1.20l 98/05/27 KG remove DMA_INT; DMA_IDLE cmds added; *
+ * 1.20m 98/06/10 KG glitch configurable; made some global *
+ * vars part of ACB; use DC390_readX *
+ * 1.20n 98/06/11 KG startup params *
+ * 1.20o 98/06/15 KG added TagMaxNum to boot/module params *
+ * Device Nr -> Idx, TagMaxNum power of 2 *
+ * 1.20p 98/06/17 KG Docu updates. Reset depends on settings *
+ * pci_set_master added; 2.0.xx: pcibios_* *
+ * used instead of MechNum things ... *
+ * 1.20q 98/06/23 KG Changed defaults. Added debug code for *
+ * removable media and fixed it. TagMaxNum *
+ * fixed for DC390. Locking: ACB, DRV for *
+ * better IRQ sharing. Spelling: Queueing *
+ * Parsing and glitch_cfg changes. Display *
+ * real SyncSpeed value. Made DisConn *
+ * functional (!) *
+ * 1.20r 98/06/30 KG Debug macros, allow disabling DsCn, set *
+ * BIT4 in CtrlR4, EN_PAGE_INT, 2.0 module *
+ * param -1 fixed. *
+ * 1.20s 98/08/20 KG Debug info on abort(), try to check PCI,*
+ * phys_to_bus instead of phys_to_virt, *
+ * fixed sel. process, fixed locking, *
+ * added MODULE_XXX infos, changed IRQ *
+ * request flags, disable DMA_INT *
+ * 1.20t 98/09/07 KG TagQ report fixed; Write Erase DMA Stat;*
+ * initfunc -> __init; better abort; *
+ * Timeout for XFER_DONE & BLAST_COMPLETE; *
+ * Allow up to 33 commands being processed *
+ * 2.0a 98/10/14 KG Max Cmnds back to 17. DMA_Stat clearing *
+ * all flags. Clear within while() loops *
+ * in DataIn_0/Out_0. Null ptr in dumpinfo *
+ * for pSRB==0. Better locking during init.*
+ * bios_param() now respects part. table. *
+ * 2.0b 98/10/24 KG Docu fixes. Timeout Msg in DMA Blast. *
+ * Disallow illegal idx in INQUIRY/REMOVE *
+ * 2.0c 98/11/19 KG Cleaned up detect/init for SMP boxes, *
+ * Write Erase DMA (1.20t) caused problems *
+ * 2.0d 98/12/25 KG Christmas release ;-) Message handling *
+ * completely reworked. Handle target ini- *
+ * tiated SDTR correctly. *
+ * 2.0d1 99/01/25 KG Try to handle RESTORE_PTR *
+ * 2.0d2 99/02/08 KG Check for failure of kmalloc, correct *
+ * inclusion of scsicam.h, DelayReset *
+ * 2.0d3 99/05/31 KG DRIVER_OK -> DID_OK, DID_NO_CONNECT, *
+ * detect Target mode and warn. *
+ * pcmd->result handling cleaned up. *
+ * 2.0d4 99/06/01 KG Cleaned selection process. Found bug *
+ * which prevented more than 16 tags. Now: *
+ * 24. SDTR cleanup. Cleaner multi-LUN *
+ * handling. Don't modify ControlRegs/FIFO *
+ * when connected. *
+ * 2.0d5 99/06/01 KG Clear DevID, Fix INQUIRY after cfg chg. *
+ * 2.0d6 99/06/02 KG Added ADD special command to allow cfg. *
+ * before detection. Reset SYNC_NEGO_DONE *
+ * after a bus reset. *
+ * 2.0d7 99/06/03 KG Fixed bugs wrt add,remove commands *
+ * 2.0d8 99/06/04 KG Removed copying of cmnd into CmdBlock. *
+ * Fixed Oops in _release(). *
+ * 2.0d9 99/06/06 KG Also tag queue INQUIRY, T_U_R, ... *
+ * Allow arb. no. of Tagged Cmnds. Max 32 *
+ * 2.0d1099/06/20 KG TagMaxNo changes now honoured! Queueing *
+ * clearified (renamed ..) TagMask handling*
+ * cleaned. *
+ * 2.0d1199/06/28 KG cmd->result now identical to 2.0d2 *
+ * 2.0d1299/07/04 KG Changed order of processing in IRQ *
+ * 2.0d1399/07/05 KG Don't update DCB fields if removed *
+ * 2.0d1499/07/05 KG remove_dev: Move kfree() to the end *
+ * 2.0d1599/07/12 KG use_new_eh_code: 0, ULONG -> UINT where *
+ * appropriate *
+ * 2.0d1699/07/13 KG Reenable StartSCSI interrupt, Retry msg *
+ * 2.0d1799/07/15 KG Remove debug msg. Disable recfg. when *
+ * there are queued cmnds *
+ * 2.0d1899/07/18 KG Selection timeout: Don't requeue *
+ * 2.0d1999/07/18 KG Abort: Only call scsi_done if dequeued *
+ * 2.0d2099/07/19 KG Rst_Detect: DoingSRB_Done *
+ * 2.0d2199/08/15 KG dev_id for request/free_irq, cmnd[0] for*
+ * RETRY, SRBdone does DID_ABORT for the *
+ * cmd passed by DC390_reset() *
+ * 2.0d2299/08/25 KG dev_id fixed. can_queue: 42 *
+ * 2.0d2399/08/25 KG Removed some debugging code. dev_id *
+ * now is set to pACB. Use u8,u16,u32. *
+ * 2.0d2499/11/14 KG Unreg. I/O if failed IRQ alloc. Call *
+ * done () w/ DID_BAD_TARGET in case of *
+ * missing DCB. We are old EH!! *
+ * 2.0d2500/01/15 KG 2.3.3x compat from Andreas Schultz *
+ * set unique_id. Disable RETRY message. *
+ * 2.0d2600/01/29 KG Go to new EH. *
+ * 2.0d2700/01/31 KG ... but maintain 2.0 compat. *
+ * and fix DCB freeing *
+ * 2.0d2800/02/14 KG Queue statistics fixed, dump special cmd*
+ * Waiting_Timer for failed StartSCSI *
+ * New EH: Don't return cmnds to ML on RST *
+ * Use old EH (don't have new EH fns yet) *
+ * Reset: Unlock, but refuse to queue *
+ * 2.3 __setup function *
+ * 2.0e 00/05/22 KG Return residual for 2.3 *
+ * 2.0e1 00/05/25 KG Compile fixes for 2.3.99 *
+ * 2.0e2 00/05/27 KG Jeff Garzik's pci_enable_device() *
+ * 2.0e3 00/09/29 KG Some 2.4 changes. Don't try Sync Nego *
+ * before INQUIRY has reported ability. *
+ * Recognise INQUIRY as scanning command. *
+ * 2.0e4 00/10/13 KG Allow compilation into 2.4 kernel *
+ * 2.0e5 00/11/17 KG Store Inq.flags in DCB *
+ * 2.0e6 00/11/22 KG 2.4 init function (Thx to O.Schumann) *
+ * 2.4 PCI device table (Thx to A.Richter) *
+ * 2.0e7 00/11/28 KG Allow overriding of BIOS settings *
+ * 2.0f 00/12/20 KG Handle failed INQUIRYs during scan *
+ * 2.1a 03/11/29 GL, KG Initial fixing for 2.6. Convert to *
+ * use the current PCI-mapping API, update *
+ * command-queuing. *
+ * 2.1b 04/04/13 GL Fix for 64-bit platforms *
+ * 2.1b1 04/01/31 GL (applied 05.04) Remove internal *
+ * command-queuing. *
+ * 2.1b2 04/02/01 CH (applied 05.04) Fix error-handling *
+ * 2.1c 04/05/23 GL Update to use the new pci_driver API, *
+ * some scsi EH updates, more cleanup. *
+ * 2.1d 04/05/27 GL Moved setting of scan_devices to *
+ * slave_alloc/_configure/_destroy, as *
+ * suggested by CH. *
+ ***********************************************************************/
+
+/* DEBUG options */
+//#define DC390_DEBUG0
+//#define DC390_DEBUG1
+//#define DC390_DCBDEBUG
+//#define DC390_PARSEDEBUG
+//#define DC390_REMOVABLEDEBUG
+//#define DC390_LOCKDEBUG
+
+//#define NOP do{}while(0)
+#define C_NOP
+
+/* Debug definitions */
+#ifdef DC390_DEBUG0
+# define DEBUG0(x) x
+#else
+# define DEBUG0(x) C_NOP
+#endif
+#ifdef DC390_DEBUG1
+# define DEBUG1(x) x
+#else
+# define DEBUG1(x) C_NOP
+#endif
+#ifdef DC390_DCBDEBUG
+# define DCBDEBUG(x) x
+#else
+# define DCBDEBUG(x) C_NOP
+#endif
+#ifdef DC390_PARSEDEBUG
+# define PARSEDEBUG(x) x
+#else
+# define PARSEDEBUG(x) C_NOP
+#endif
+#ifdef DC390_REMOVABLEDEBUG
+# define REMOVABLEDEBUG(x) x
+#else
+# define REMOVABLEDEBUG(x) C_NOP
+#endif
+#define DCBDEBUG1(x) C_NOP
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_tcq.h>
+
+
+#define DC390_BANNER "Tekram DC390/AM53C974"
+#define DC390_VERSION "2.1d 2004-05-27"
+
+#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI
+
+#include "tmscsim.h"
+
+
+static void dc390_DataOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_DataIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_Command_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_Status_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_MsgOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_MsgIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_DataOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_DataInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_CommandPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_StatusPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_MsgOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_MsgInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_Nop_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+static void dc390_Nop_1( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
+
+static void dc390_SetXferRate( struct dc390_acb* pACB, struct dc390_dcb* pDCB );
+static void dc390_Disconnect( struct dc390_acb* pACB );
+static void dc390_Reselect( struct dc390_acb* pACB );
+static void dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB );
+static void dc390_ScsiRstDetect( struct dc390_acb* pACB );
+static void dc390_EnableMsgOut_Abort(struct dc390_acb*, struct dc390_srb*);
+static void dc390_dumpinfo(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB);
+static void dc390_ResetDevParam(struct dc390_acb* pACB);
+
+static u32 dc390_laststatus = 0;
+static u8 dc390_adapterCnt = 0;
+
+/* Startup values, to be overriden on the commandline */
+static int tmscsim[] = {-2, -2, -2, -2, -2, -2};
+
+module_param_array(tmscsim, int, NULL, 0);
+MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)");
+MODULE_AUTHOR("C.L. Huang / Kurt Garloff");
+MODULE_DESCRIPTION("SCSI host adapter driver for Tekram DC390 and other AMD53C974A based PCI SCSI adapters");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
+
+static void *dc390_phase0[]={
+ dc390_DataOut_0,
+ dc390_DataIn_0,
+ dc390_Command_0,
+ dc390_Status_0,
+ dc390_Nop_0,
+ dc390_Nop_0,
+ dc390_MsgOut_0,
+ dc390_MsgIn_0,
+ dc390_Nop_1
+ };
+
+static void *dc390_phase1[]={
+ dc390_DataOutPhase,
+ dc390_DataInPhase,
+ dc390_CommandPhase,
+ dc390_StatusPhase,
+ dc390_Nop_0,
+ dc390_Nop_0,
+ dc390_MsgOutPhase,
+ dc390_MsgInPhase,
+ dc390_Nop_1
+ };
+
+#ifdef DC390_DEBUG1
+static char* dc390_p0_str[] = {
+ "dc390_DataOut_0",
+ "dc390_DataIn_0",
+ "dc390_Command_0",
+ "dc390_Status_0",
+ "dc390_Nop_0",
+ "dc390_Nop_0",
+ "dc390_MsgOut_0",
+ "dc390_MsgIn_0",
+ "dc390_Nop_1"
+ };
+
+static char* dc390_p1_str[] = {
+ "dc390_DataOutPhase",
+ "dc390_DataInPhase",
+ "dc390_CommandPhase",
+ "dc390_StatusPhase",
+ "dc390_Nop_0",
+ "dc390_Nop_0",
+ "dc390_MsgOutPhase",
+ "dc390_MsgInPhase",
+ "dc390_Nop_1"
+ };
+#endif
+
+static u8 dc390_eepromBuf[MAX_ADAPTER_NUM][EE_LEN];
+static u8 dc390_clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+static u8 dc390_clock_speed[] = {100,80,67,57,50, 40, 31, 20};
+
+/***********************************************************************
+ * Functions for the management of the internal structures
+ * (DCBs, SRBs, Queueing)
+ *
+ **********************************************************************/
+static struct dc390_dcb __inline__ *dc390_findDCB ( struct dc390_acb* pACB, u8 id, u8 lun)
+{
+ struct dc390_dcb* pDCB = pACB->pLinkDCB; if (!pDCB) return NULL;
+ while (pDCB->TargetID != id || pDCB->TargetLUN != lun)
+ {
+ pDCB = pDCB->pNextDCB;
+ if (pDCB == pACB->pLinkDCB)
+ return NULL;
+ }
+ DCBDEBUG1( printk (KERN_DEBUG "DCB %p (%02x,%02x) found.\n", \
+ pDCB, pDCB->TargetID, pDCB->TargetLUN));
+ return pDCB;
+}
+
+/* Insert SRB oin top of free list */
+static __inline__ void dc390_Free_insert (struct dc390_acb* pACB, struct dc390_srb* pSRB)
+{
+ DEBUG0(printk ("DC390: Free SRB %p\n", pSRB));
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+}
+
+static __inline__ void dc390_Going_append (struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
+{
+ pDCB->GoingSRBCnt++;
+ DEBUG0(printk("DC390: Append SRB %p to Going\n", pSRB));
+ /* Append to the list of Going commands */
+ if( pDCB->pGoingSRB )
+ pDCB->pGoingLast->pNextSRB = pSRB;
+ else
+ pDCB->pGoingSRB = pSRB;
+
+ pDCB->pGoingLast = pSRB;
+ /* No next one in sent list */
+ pSRB->pNextSRB = NULL;
+}
+
+static __inline__ void dc390_Going_remove (struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
+{
+ DEBUG0(printk("DC390: Remove SRB %p from Going\n", pSRB));
+ if (pSRB == pDCB->pGoingSRB)
+ pDCB->pGoingSRB = pSRB->pNextSRB;
+ else
+ {
+ struct dc390_srb* psrb = pDCB->pGoingSRB;
+ while (psrb && psrb->pNextSRB != pSRB)
+ psrb = psrb->pNextSRB;
+ if (!psrb)
+ { printk (KERN_ERR "DC390: Remove non-ex. SRB %p from Going!\n", pSRB); return; }
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if (pSRB == pDCB->pGoingLast)
+ pDCB->pGoingLast = psrb;
+ }
+ pDCB->GoingSRBCnt--;
+}
+
+static struct scatterlist* dc390_sg_build_single(struct scatterlist *sg, void *addr, unsigned int length)
+{
+ memset(sg, 0, sizeof(struct scatterlist));
+ sg->page = virt_to_page(addr);
+ sg->length = length;
+ sg->offset = (unsigned long)addr & ~PAGE_MASK;
+ return sg;
+}
+
+/* Create pci mapping */
+static int dc390_pci_map (struct dc390_srb* pSRB)
+{
+ int error = 0;
+ struct scsi_cmnd *pcmd = pSRB->pcmd;
+ struct pci_dev *pdev = pSRB->pSRBDCB->pDCBACB->pdev;
+ dc390_cmd_scp_t* cmdp = ((dc390_cmd_scp_t*)(&pcmd->SCp));
+
+ /* Map sense buffer */
+ if (pSRB->SRBFlag & AUTO_REQSENSE) {
+ pSRB->pSegmentList = dc390_sg_build_single(&pSRB->Segmentx, pcmd->sense_buffer, sizeof(pcmd->sense_buffer));
+ pSRB->SGcount = pci_map_sg(pdev, pSRB->pSegmentList, 1,
+ DMA_FROM_DEVICE);
+ cmdp->saved_dma_handle = sg_dma_address(pSRB->pSegmentList);
+
+ /* TODO: error handling */
+ if (pSRB->SGcount != 1)
+ error = 1;
+ DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __FUNCTION__, pcmd->sense_buffer, cmdp->saved_dma_handle));
+ /* Map SG list */
+ } else if (pcmd->use_sg) {
+ pSRB->pSegmentList = (struct scatterlist *) pcmd->request_buffer;
+ pSRB->SGcount = pci_map_sg(pdev, pSRB->pSegmentList, pcmd->use_sg,
+ pcmd->sc_data_direction);
+ /* TODO: error handling */
+ if (!pSRB->SGcount)
+ error = 1;
+ DEBUG1(printk("%s(): Mapped SG %p with %d (%d) elements\n",\
+ __FUNCTION__, pcmd->request_buffer, pSRB->SGcount, pcmd->use_sg));
+ /* Map single segment */
+ } else if (pcmd->request_buffer && pcmd->request_bufflen) {
+ pSRB->pSegmentList = dc390_sg_build_single(&pSRB->Segmentx, pcmd->request_buffer, pcmd->request_bufflen);
+ pSRB->SGcount = pci_map_sg(pdev, pSRB->pSegmentList, 1,
+ pcmd->sc_data_direction);
+ cmdp->saved_dma_handle = sg_dma_address(pSRB->pSegmentList);
+
+ /* TODO: error handling */
+ if (pSRB->SGcount != 1)
+ error = 1;
+ DEBUG1(printk("%s(): Mapped request buffer %p at %x\n", __FUNCTION__, pcmd->request_buffer, cmdp->saved_dma_handle));
+ /* No mapping !? */
+ } else
+ pSRB->SGcount = 0;
+
+ return error;
+}
+
+/* Remove pci mapping */
+static void dc390_pci_unmap (struct dc390_srb* pSRB)
+{
+ struct scsi_cmnd *pcmd = pSRB->pcmd;
+ struct pci_dev *pdev = pSRB->pSRBDCB->pDCBACB->pdev;
+ DEBUG1(dc390_cmd_scp_t* cmdp = ((dc390_cmd_scp_t*)(&pcmd->SCp)));
+
+ if (pSRB->SRBFlag) {
+ pci_unmap_sg(pdev, &pSRB->Segmentx, 1, DMA_FROM_DEVICE);
+ DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __FUNCTION__, cmdp->saved_dma_handle));
+ } else if (pcmd->use_sg) {
+ pci_unmap_sg(pdev, pcmd->request_buffer, pcmd->use_sg, pcmd->sc_data_direction);
+ DEBUG1(printk("%s(): Unmapped SG at %p with %d elements\n", __FUNCTION__, pcmd->request_buffer, pcmd->use_sg));
+ } else if (pcmd->request_buffer && pcmd->request_bufflen) {
+ pci_unmap_sg(pdev, &pSRB->Segmentx, 1, pcmd->sc_data_direction);
+ DEBUG1(printk("%s(): Unmapped request buffer at %x\n", __FUNCTION__, cmdp->saved_dma_handle));
+ }
+}
+
+static void __inline__
+dc390_freetag (struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
+{
+ if (pSRB->TagNumber != SCSI_NO_TAG) {
+ pDCB->TagMask &= ~(1 << pSRB->TagNumber); /* free tag mask */
+ pSRB->TagNumber = SCSI_NO_TAG;
+ }
+}
+
+
+static int
+dc390_StartSCSI( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB )
+{
+ struct scsi_cmnd *scmd = pSRB->pcmd;
+ struct scsi_device *sdev = scmd->device;
+ u8 cmd, disc_allowed, try_sync_nego;
+ char tag[2];
+
+ pSRB->ScsiPhase = SCSI_NOP0;
+
+ if (pACB->Connected)
+ {
+ // Should not happen normally
+ printk (KERN_WARNING "DC390: Can't select when connected! (%08x,%02x)\n",
+ pSRB->SRBState, pSRB->SRBFlag);
+ pSRB->SRBState = SRB_READY;
+ pACB->SelConn++;
+ return 1;
+ }
+ if (time_before (jiffies, pACB->pScsiHost->last_reset))
+ {
+ DEBUG0(printk ("DC390: We were just reset and don't accept commands yet!\n"));
+ return 1;
+ }
+ /* KG: Moved pci mapping here */
+ dc390_pci_map(pSRB);
+ /* TODO: error handling */
+ DC390_write8 (Scsi_Dest_ID, pDCB->TargetID);
+ DC390_write8 (Sync_Period, pDCB->SyncPeriod);
+ DC390_write8 (Sync_Offset, pDCB->SyncOffset);
+ DC390_write8 (CtrlReg1, pDCB->CtrlR1);
+ DC390_write8 (CtrlReg3, pDCB->CtrlR3);
+ DC390_write8 (CtrlReg4, pDCB->CtrlR4);
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); /* Flush FIFO */
+ DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\
+ scmd->cmnd[0], pDCB->SyncMode));
+
+ /* Don't disconnect on AUTO_REQSENSE, cause it might be an
+ * Contingent Allegiance Condition (6.6), where no tags should be used.
+ * All other have to be allowed to disconnect to prevent Incorrect
+ * Initiator Connection (6.8.2/6.5.2) */
+ /* Changed KG, 99/06/06 */
+ if (! (pSRB->SRBFlag & AUTO_REQSENSE))
+ disc_allowed = pDCB->DevMode & EN_DISCONNECT_;
+ else
+ disc_allowed = 0;
+
+ if ((pDCB->SyncMode & SYNC_ENABLE) && pDCB->TargetLUN == 0 && sdev->sdtr &&
+ (((scmd->cmnd[0] == REQUEST_SENSE || (pSRB->SRBFlag & AUTO_REQSENSE)) &&
+ !(pDCB->SyncMode & SYNC_NEGO_DONE)) || scmd->cmnd[0] == INQUIRY))
+ try_sync_nego = 1;
+ else
+ try_sync_nego = 0;
+
+ pSRB->MsgCnt = 0;
+ cmd = SEL_W_ATN;
+ DC390_write8 (ScsiFifo, IDENTIFY(disc_allowed, pDCB->TargetLUN));
+ /* Change 99/05/31: Don't use tags when not disconnecting (BUSY) */
+ if ((pDCB->SyncMode & EN_TAG_QUEUEING) && disc_allowed && scsi_populate_tag_msg(scmd, tag)) {
+ DC390_write8(ScsiFifo, tag[0]);
+ pDCB->TagMask |= 1 << tag[1];
+ pSRB->TagNumber = tag[1];
+ DC390_write8(ScsiFifo, tag[1]);
+ DEBUG1(printk(KERN_INFO "DC390: Select w/DisCn for Cmd %li (SRB %p), block tag %02x\n", scmd->pid, pSRB, tag[1]));
+ cmd = SEL_W_ATN3;
+ } else {
+ /* No TagQ */
+//no_tag:
+ DEBUG1(printk(KERN_INFO "DC390: Select w%s/DisCn for Cmd %li (SRB %p), No TagQ\n", disc_allowed ? "" : "o", scmd->pid, pSRB));
+ }
+
+ pSRB->SRBState = SRB_START_;
+
+ if (try_sync_nego)
+ {
+ u8 Sync_Off = pDCB->SyncOffset;
+ DEBUG0(printk (KERN_INFO "DC390: NEW Sync Nego code triggered (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN));
+ pSRB->MsgOutBuf[0] = EXTENDED_MESSAGE;
+ pSRB->MsgOutBuf[1] = 3;
+ pSRB->MsgOutBuf[2] = EXTENDED_SDTR;
+ pSRB->MsgOutBuf[3] = pDCB->NegoPeriod;
+ if (!(Sync_Off & 0x0f)) Sync_Off = SYNC_NEGO_OFFSET;
+ pSRB->MsgOutBuf[4] = Sync_Off;
+ pSRB->MsgCnt = 5;
+ //pSRB->SRBState = SRB_MSGOUT_;
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ cmd = SEL_W_ATN_STOP;
+ }
+
+ /* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */
+ if (cmd != SEL_W_ATN_STOP)
+ {
+ if( pSRB->SRBFlag & AUTO_REQSENSE )
+ {
+ DC390_write8 (ScsiFifo, REQUEST_SENSE);
+ DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5);
+ DC390_write8 (ScsiFifo, 0);
+ DC390_write8 (ScsiFifo, 0);
+ DC390_write8 (ScsiFifo, sizeof(scmd->sense_buffer));
+ DC390_write8 (ScsiFifo, 0);
+ DEBUG1(printk (KERN_DEBUG "DC390: AutoReqSense !\n"));
+ }
+ else /* write cmnd to bus */
+ {
+ u8 *ptr; u8 i;
+ ptr = (u8 *)scmd->cmnd;
+ for (i = 0; i < scmd->cmd_len; i++)
+ DC390_write8 (ScsiFifo, *(ptr++));
+ }
+ }
+ DEBUG0(if (pACB->pActiveDCB) \
+ printk (KERN_WARNING "DC390: ActiveDCB != 0\n"));
+ DEBUG0(if (pDCB->pActiveSRB) \
+ printk (KERN_WARNING "DC390: ActiveSRB != 0\n"));
+ //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+ if (DC390_read8 (Scsi_Status) & INTERRUPT)
+ {
+ dc390_freetag (pDCB, pSRB);
+ DEBUG0(printk ("DC390: Interrupt during Start SCSI (pid %li, target %02i-%02i)\n",
+ scmd->pid, scmd->device->id, scmd->device->lun));
+ pSRB->SRBState = SRB_READY;
+ //DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ pACB->SelLost++;
+ return 1;
+ }
+ DC390_write8 (ScsiCmd, cmd);
+ pACB->pActiveDCB = pDCB;
+ pDCB->pActiveSRB = pSRB;
+ pACB->Connected = 1;
+ pSRB->ScsiPhase = SCSI_NOP1;
+ return 0;
+}
+
+//#define DMA_INT EN_DMA_INT /*| EN_PAGE_INT*/
+#define DMA_INT 0
+
+#if DMA_INT
+/* This is similar to AM53C974.c ... */
+static u8
+dc390_dma_intr (struct dc390_acb* pACB)
+{
+ struct dc390_srb* pSRB;
+ u8 dstate;
+ DEBUG0(u16 pstate; struct pci_dev *pdev = pACB->pdev);
+
+ DEBUG0(pci_read_config_word(pdev, PCI_STATUS, &pstate));
+ DEBUG0(if (pstate & (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY))\
+ { printk(KERN_WARNING "DC390: PCI state = %04x!\n", pstate); \
+ pci_write_config_word(pdev, PCI_STATUS, (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY));});
+
+ dstate = DC390_read8 (DMA_Status);
+
+ if (! pACB->pActiveDCB || ! pACB->pActiveDCB->pActiveSRB) return dstate;
+ else pSRB = pACB->pActiveDCB->pActiveSRB;
+
+ if (dstate & (DMA_XFER_ABORT | DMA_XFER_ERROR | POWER_DOWN | PCI_MS_ABORT))
+ {
+ printk (KERN_ERR "DC390: DMA error (%02x)!\n", dstate);
+ return dstate;
+ }
+ if (dstate & DMA_XFER_DONE)
+ {
+ u32 residual, xferCnt; int ctr = 6000000;
+ if (! (DC390_read8 (DMA_Cmd) & READ_DIRECTION))
+ {
+ do
+ {
+ DEBUG1(printk (KERN_DEBUG "DC390: read residual bytes ... \n"));
+ dstate = DC390_read8 (DMA_Status);
+ residual = DC390_read8 (CtcReg_Low) | DC390_read8 (CtcReg_Mid) << 8 |
+ DC390_read8 (CtcReg_High) << 16;
+ residual += DC390_read8 (Current_Fifo) & 0x1f;
+ } while (residual && ! (dstate & SCSI_INTERRUPT) && --ctr);
+ if (!ctr) printk (KERN_CRIT "DC390: dma_intr: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr));
+ /* residual = ... */
+ }
+ else
+ residual = 0;
+
+ /* ??? */
+
+ xferCnt = pSRB->SGToBeXferLen - residual;
+ pSRB->SGBusAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = residual;
+# ifdef DC390_DEBUG0
+ printk (KERN_INFO "DC390: DMA: residual = %i, xfer = %i\n",
+ (unsigned int)residual, (unsigned int)xferCnt);
+# endif
+
+ DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+ }
+ dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24;
+ return dstate;
+}
+#endif
+
+
+static void __inline__
+dc390_InvalidCmd(struct dc390_acb* pACB)
+{
+ if (pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_ | SRB_MSGOUT))
+ DC390_write8(ScsiCmd, CLEAR_FIFO_CMD);
+}
+
+
+static irqreturn_t __inline__
+DC390_Interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct dc390_acb *pACB = (struct dc390_acb*)dev_id;
+ struct dc390_dcb *pDCB;
+ struct dc390_srb *pSRB;
+ u8 sstatus=0;
+ u8 phase;
+ void (*stateV)( struct dc390_acb*, struct dc390_srb*, u8 *);
+ u8 istate, istatus;
+#if DMA_INT
+ u8 dstatus;
+#endif
+
+ sstatus = DC390_read8 (Scsi_Status);
+ if( !(sstatus & INTERRUPT) )
+ return IRQ_NONE;
+
+ DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus));
+
+#if DMA_INT
+ spin_lock_irq(pACB->pScsiHost->host_lock);
+ dstatus = dc390_dma_intr (pACB);
+ spin_unlock_irq(pACB->pScsiHost->host_lock);
+
+ DEBUG1(printk (KERN_DEBUG "dstatus=%02x,", dstatus));
+ if (! (dstatus & SCSI_INTERRUPT))
+ {
+ DEBUG0(printk (KERN_WARNING "DC390 Int w/o SCSI actions (only DMA?)\n"));
+ return IRQ_NONE;
+ }
+#else
+ //DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
+ //dstatus = DC390_read8 (DMA_Status);
+ //DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
+#endif
+
+ spin_lock_irq(pACB->pScsiHost->host_lock);
+
+ istate = DC390_read8 (Intern_State);
+ istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */
+
+ DEBUG1(printk (KERN_INFO "Istatus(Res,Inv,Dis,Serv,Succ,ReS,SelA,Sel)=%02x,",istatus));
+ dc390_laststatus &= ~0x00ffffff;
+ dc390_laststatus |= /* dstatus<<24 | */ sstatus<<16 | istate<<8 | istatus;
+
+ if (sstatus & ILLEGAL_OP_ERR)
+ {
+ printk ("DC390: Illegal Operation detected (%08x)!\n", dc390_laststatus);
+ dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB);
+ }
+
+ else if (istatus & INVALID_CMD)
+ {
+ printk ("DC390: Invalid Command detected (%08x)!\n", dc390_laststatus);
+ dc390_InvalidCmd( pACB );
+ goto unlock;
+ }
+
+ if (istatus & SCSI_RESET)
+ {
+ dc390_ScsiRstDetect( pACB );
+ goto unlock;
+ }
+
+ if (istatus & DISCONNECTED)
+ {
+ dc390_Disconnect( pACB );
+ goto unlock;
+ }
+
+ if (istatus & RESELECTED)
+ {
+ dc390_Reselect( pACB );
+ goto unlock;
+ }
+
+ else if (istatus & (SELECTED | SEL_ATTENTION))
+ {
+ printk (KERN_ERR "DC390: Target mode not supported!\n");
+ goto unlock;
+ }
+
+ if (istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) )
+ {
+ pDCB = pACB->pActiveDCB;
+ if (!pDCB)
+ {
+ printk (KERN_ERR "DC390: Suc. op/ Serv. req: pActiveDCB = 0!\n");
+ goto unlock;
+ }
+ pSRB = pDCB->pActiveSRB;
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ dc390_EnableMsgOut_Abort (pACB, pSRB);
+
+ phase = pSRB->ScsiPhase;
+ DEBUG1(printk (KERN_INFO "DC390: [%i]%s(0) (%02x)\n", phase, dc390_p0_str[phase], sstatus));
+ stateV = (void *) dc390_phase0[phase];
+ ( *stateV )( pACB, pSRB, &sstatus );
+
+ pSRB->ScsiPhase = sstatus & 7;
+ phase = (u8) sstatus & 7;
+ DEBUG1(printk (KERN_INFO "DC390: [%i]%s(1) (%02x)\n", phase, dc390_p1_str[phase], sstatus));
+ stateV = (void *) dc390_phase1[phase];
+ ( *stateV )( pACB, pSRB, &sstatus );
+ }
+
+ unlock:
+ spin_unlock_irq(pACB->pScsiHost->host_lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t do_DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
+{
+ irqreturn_t ret;
+ DEBUG1(printk (KERN_INFO "DC390: Irq (%i) caught: ", irq));
+ /* Locking is done in DC390_Interrupt */
+ ret = DC390_Interrupt(irq, dev_id, regs);
+ DEBUG1(printk (".. IRQ returned\n"));
+ return ret;
+}
+
+static void
+dc390_DataOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ u8 sstatus;
+ struct scatterlist *psgl;
+ u32 ResidCnt, xferCnt;
+ u8 dstate = 0;
+
+ sstatus = *psstatus;
+
+ if( !(pSRB->SRBState & SRB_XFERPAD) )
+ {
+ if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR) )
+ pSRB->SRBStatus |= PARITY_ERROR;
+
+ if( sstatus & COUNT_2_ZERO )
+ {
+ unsigned long timeout = jiffies + HZ;
+
+ /* Function called from the ISR with the host_lock held and interrupts disabled */
+ if (pSRB->SGToBeXferLen)
+ while (time_before(jiffies, timeout) && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE)) {
+ spin_unlock_irq(pACB->pScsiHost->host_lock);
+ udelay(50);
+ spin_lock_irq(pACB->pScsiHost->host_lock);
+ }
+ if (!time_before(jiffies, timeout))
+ printk (KERN_CRIT "DC390: Deadlock in DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n",
+ DC390_read32 (DMA_Wk_ByteCntr));
+ dc390_laststatus &= ~0xff000000;
+ dc390_laststatus |= dstate << 24;
+ pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+
+ pSRB->SGBusAddr = cpu_to_le32(pci_dma_lo32(sg_dma_address(psgl)));
+ pSRB->SGToBeXferLen = cpu_to_le32(sg_dma_len(psgl));
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ else
+ {
+ ResidCnt = (u32) DC390_read8 (Current_Fifo) & 0x1f;
+ ResidCnt |= (u32) DC390_read8 (CtcReg_High) << 16;
+ ResidCnt |= (u32) DC390_read8 (CtcReg_Mid) << 8;
+ ResidCnt += (u32) DC390_read8 (CtcReg_Low);
+
+ xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+ pSRB->SGBusAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = ResidCnt;
+ }
+ }
+ if ((*psstatus & 7) != SCSI_DATA_OUT)
+ {
+ DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ }
+}
+
+static void
+dc390_DataIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ u8 sstatus, residual, bval;
+ struct scatterlist *psgl;
+ u32 ResidCnt, i;
+ unsigned long xferCnt;
+ u8 *ptr;
+
+ sstatus = *psstatus;
+
+ if( !(pSRB->SRBState & SRB_XFERPAD) )
+ {
+ if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR))
+ pSRB->SRBStatus |= PARITY_ERROR;
+
+ if( sstatus & COUNT_2_ZERO )
+ {
+ int dstate = 0;
+ unsigned long timeout = jiffies + HZ;
+
+ /* Function called from the ISR with the host_lock held and interrupts disabled */
+ if (pSRB->SGToBeXferLen)
+ while (time_before(jiffies, timeout) && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE)) {
+ spin_unlock_irq(pACB->pScsiHost->host_lock);
+ udelay(50);
+ spin_lock_irq(pACB->pScsiHost->host_lock);
+ }
+ if (!time_before(jiffies, timeout)) {
+ printk (KERN_CRIT "DC390: Deadlock in DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n",
+ DC390_read32 (DMA_Wk_ByteCntr));
+ printk (KERN_CRIT "DC390: DataIn_0: DMA State: %i\n", dstate);
+ }
+ dc390_laststatus &= ~0xff000000;
+ dc390_laststatus |= dstate << 24;
+ DEBUG1(ResidCnt = ((unsigned long) DC390_read8 (CtcReg_High) << 16) \
+ + ((unsigned long) DC390_read8 (CtcReg_Mid) << 8) \
+ + ((unsigned long) DC390_read8 (CtcReg_Low)));
+ DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%i,ToBeXfer=%li),", ResidCnt, pSRB->SGToBeXferLen));
+
+ DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+
+ pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+
+ pSRB->SGBusAddr = cpu_to_le32(pci_dma_lo32(sg_dma_address(psgl)));
+ pSRB->SGToBeXferLen = cpu_to_le32(sg_dma_len(psgl));
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ else /* phase changed */
+ {
+ residual = 0;
+ bval = DC390_read8 (Current_Fifo);
+ while( bval & 0x1f )
+ {
+ DEBUG1(printk (KERN_DEBUG "Check for residuals,"));
+ if( (bval & 0x1f) == 1 )
+ {
+ for(i=0; i < 0x100; i++)
+ {
+ bval = DC390_read8 (Current_Fifo);
+ if( !(bval & 0x1f) )
+ goto din_1;
+ else if( i == 0x0ff )
+ {
+ residual = 1; /* ;1 residual byte */
+ goto din_1;
+ }
+ }
+ }
+ else
+ bval = DC390_read8 (Current_Fifo);
+ }
+din_1:
+ DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_BLAST_CMD);
+ for (i = 0xa000; i; i--)
+ {
+ bval = DC390_read8 (DMA_Status);
+ if (bval & BLAST_COMPLETE)
+ break;
+ }
+ /* It seems a DMA Blast abort isn't that bad ... */
+ if (!i) printk (KERN_ERR "DC390: DMA Blast aborted unfinished!\n");
+ //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+ dc390_laststatus &= ~0xff000000; dc390_laststatus |= bval << 24;
+
+ DEBUG1(printk (KERN_DEBUG "Blast: Read %i times DMA_Status %02x", 0xa000-i, bval));
+ ResidCnt = (u32) DC390_read8 (CtcReg_High);
+ ResidCnt <<= 8;
+ ResidCnt |= (u32) DC390_read8 (CtcReg_Mid);
+ ResidCnt <<= 8;
+ ResidCnt |= (u32) DC390_read8 (CtcReg_Low);
+
+ xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+ pSRB->SGBusAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = ResidCnt;
+
+ if( residual )
+ {
+ bval = DC390_read8 (ScsiFifo); /* get one residual byte */
+ ptr = (u8 *) bus_to_virt( pSRB->SGBusAddr );
+ *ptr = bval;
+ pSRB->SGBusAddr++; xferCnt++;
+ pSRB->TotalXferredLen++;
+ pSRB->SGToBeXferLen--;
+ }
+ DEBUG1(printk (KERN_DEBUG "Xfered: %li, Total: %li, Remaining: %li\n", xferCnt,\
+ pSRB->TotalXferredLen, pSRB->SGToBeXferLen));
+
+ }
+ }
+ if ((*psstatus & 7) != SCSI_DATA_IN)
+ {
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+ }
+}
+
+static void
+dc390_Command_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+}
+
+static void
+dc390_Status_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+
+ pSRB->TargetStatus = DC390_read8 (ScsiFifo);
+ //udelay (1);
+ pSRB->EndMessage = DC390_read8 (ScsiFifo); /* get message */
+
+ *psstatus = SCSI_NOP0;
+ pSRB->SRBState = SRB_COMPLETED;
+ DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);
+}
+
+static void
+dc390_MsgOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
+ *psstatus = SCSI_NOP0;
+ //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+}
+
+
+static void __inline__
+dc390_reprog (struct dc390_acb* pACB, struct dc390_dcb* pDCB)
+{
+ DC390_write8 (Sync_Period, pDCB->SyncPeriod);
+ DC390_write8 (Sync_Offset, pDCB->SyncOffset);
+ DC390_write8 (CtrlReg3, pDCB->CtrlR3);
+ DC390_write8 (CtrlReg4, pDCB->CtrlR4);
+ dc390_SetXferRate (pACB, pDCB);
+}
+
+
+#ifdef DC390_DEBUG0
+static void
+dc390_printMsg (u8 *MsgBuf, u8 len)
+{
+ int i;
+ printk (" %02x", MsgBuf[0]);
+ for (i = 1; i < len; i++)
+ printk (" %02x", MsgBuf[i]);
+ printk ("\n");
+}
+#endif
+
+#define DC390_ENABLE_MSGOUT DC390_write8 (ScsiCmd, SET_ATN_CMD)
+
+/* reject_msg */
+static void __inline__
+dc390_MsgIn_reject (struct dc390_acb* pACB, struct dc390_srb* pSRB)
+{
+ pSRB->MsgOutBuf[0] = MESSAGE_REJECT;
+ pSRB->MsgCnt = 1;
+ DC390_ENABLE_MSGOUT;
+ DEBUG0 (printk (KERN_INFO "DC390: Reject message\n"));
+}
+
+/* abort command */
+static void
+dc390_EnableMsgOut_Abort ( struct dc390_acb* pACB, struct dc390_srb* pSRB )
+{
+ pSRB->MsgOutBuf[0] = ABORT;
+ pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
+ pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_;
+}
+
+static struct dc390_srb*
+dc390_MsgIn_QTag (struct dc390_acb* pACB, struct dc390_dcb* pDCB, s8 tag)
+{
+ struct dc390_srb* pSRB = pDCB->pGoingSRB;
+
+ if (pSRB)
+ {
+ struct scsi_cmnd *scmd = scsi_find_tag(pSRB->pcmd->device, tag);
+ pSRB = (struct dc390_srb *)scmd->host_scribble;
+
+ if (pDCB->DCBFlag & ABORT_DEV_)
+ {
+ pSRB->SRBState = SRB_ABORT_SENT;
+ dc390_EnableMsgOut_Abort( pACB, pSRB );
+ }
+
+ if (!(pSRB->SRBState & SRB_DISCONNECT))
+ goto mingx0;
+
+ pDCB->pActiveSRB = pSRB;
+ pSRB->SRBState = SRB_DATA_XFER;
+ }
+ else
+ {
+ mingx0:
+ pSRB = pACB->pTmpSRB;
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->MsgOutBuf[0] = ABORT_TAG;
+ pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
+ }
+ return pSRB;
+}
+
+
+/* set async transfer mode */
+static void
+dc390_MsgIn_set_async (struct dc390_acb* pACB, struct dc390_srb* pSRB)
+{
+ struct dc390_dcb* pDCB = pSRB->pSRBDCB;
+ if (!(pSRB->SRBState & DO_SYNC_NEGO))
+ printk (KERN_INFO "DC390: Target %i initiates Non-Sync?\n", pDCB->TargetID);
+ pSRB->SRBState &= ~DO_SYNC_NEGO;
+ pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ //pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
+ pDCB->CtrlR3 = FAST_CLK; /* fast clock / normal scsi */
+ pDCB->CtrlR4 &= 0x3f;
+ pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */
+ dc390_reprog (pACB, pDCB);
+}
+
+/* set sync transfer mode */
+static void
+dc390_MsgIn_set_sync (struct dc390_acb* pACB, struct dc390_srb* pSRB)
+{
+ u8 bval;
+ u16 wval, wval1;
+ struct dc390_dcb* pDCB = pSRB->pSRBDCB;
+ u8 oldsyncperiod = pDCB->SyncPeriod;
+ u8 oldsyncoffset = pDCB->SyncOffset;
+
+ if (!(pSRB->SRBState & DO_SYNC_NEGO))
+ {
+ printk (KERN_INFO "DC390: Target %i initiates Sync: %ins %i ... answer ...\n",
+ pDCB->TargetID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]);
+
+ /* reject */
+ //dc390_MsgIn_reject (pACB, pSRB);
+ //return dc390_MsgIn_set_async (pACB, pSRB);
+
+ /* Reply with corrected SDTR Message */
+ if (pSRB->MsgInBuf[4] > 15)
+ {
+ printk (KERN_INFO "DC390: Lower Sync Offset to 15\n");
+ pSRB->MsgInBuf[4] = 15;
+ }
+ if (pSRB->MsgInBuf[3] < pDCB->NegoPeriod)
+ {
+ printk (KERN_INFO "DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2);
+ pSRB->MsgInBuf[3] = pDCB->NegoPeriod;
+ }
+ memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5);
+ pSRB->MsgCnt = 5;
+ DC390_ENABLE_MSGOUT;
+ }
+
+ pSRB->SRBState &= ~DO_SYNC_NEGO;
+ pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
+ pDCB->SyncOffset &= 0x0f0;
+ pDCB->SyncOffset |= pSRB->MsgInBuf[4];
+ pDCB->NegoPeriod = pSRB->MsgInBuf[3];
+
+ wval = (u16) pSRB->MsgInBuf[3];
+ wval = wval << 2; wval -= 3; wval1 = wval / 25; /* compute speed */
+ if( (wval1 * 25) != wval) wval1++;
+ bval = FAST_CLK+FAST_SCSI; /* fast clock / fast scsi */
+
+ pDCB->CtrlR4 &= 0x3f; /* Glitch eater: 12ns less than normal */
+ if (pACB->glitch_cfg != NS_TO_GLITCH(0))
+ pDCB->CtrlR4 |= NS_TO_GLITCH(((GLITCH_TO_NS(pACB->glitch_cfg)) - 1));
+ else
+ pDCB->CtrlR4 |= NS_TO_GLITCH(0);
+ if (wval1 < 4) pDCB->CtrlR4 |= NS_TO_GLITCH(0); /* Ultra */
+
+ if (wval1 >= 8)
+ {
+ wval1--; /* Timing computation differs by 1 from FAST_SCSI */
+ bval = FAST_CLK; /* fast clock / normal scsi */
+ pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */
+ }
+
+ pDCB->CtrlR3 = bval;
+ pDCB->SyncPeriod = (u8)wval1;
+
+ if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->TargetLUN == 0)
+ {
+ if (! (bval & FAST_SCSI)) wval1++;
+ printk (KERN_INFO "DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->TargetID,
+ 40/wval1, ((40%wval1)*10+wval1/2)/wval1, pDCB->SyncOffset & 0x0f);
+ }
+
+ dc390_reprog (pACB, pDCB);
+}
+
+
+/* handle RESTORE_PTR */
+/* I presume, this command is already mapped, so, have to remap. */
+static void
+dc390_restore_ptr (struct dc390_acb* pACB, struct dc390_srb* pSRB)
+{
+ struct scsi_cmnd *pcmd = pSRB->pcmd;
+ struct scatterlist *psgl;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGIndex = 0;
+ if (pcmd->use_sg) {
+ pSRB->pSegmentList = (struct scatterlist *)pcmd->request_buffer;
+ psgl = pSRB->pSegmentList;
+ //dc390_pci_sync(pSRB);
+
+ while (pSRB->TotalXferredLen + (unsigned long) sg_dma_len(psgl) < pSRB->Saved_Ptr)
+ {
+ pSRB->TotalXferredLen += (unsigned long) sg_dma_len(psgl);
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+ pSRB->SGBusAddr = cpu_to_le32(pci_dma_lo32(sg_dma_address(psgl)));
+ pSRB->SGToBeXferLen = cpu_to_le32(sg_dma_len(psgl));
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ pSRB->SGToBeXferLen -= (pSRB->Saved_Ptr - pSRB->TotalXferredLen);
+ pSRB->SGBusAddr += (pSRB->Saved_Ptr - pSRB->TotalXferredLen);
+ printk (KERN_INFO "DC390: Pointer restored. Segment %i, Total %li, Bus %08lx\n",
+ pSRB->SGIndex, pSRB->Saved_Ptr, pSRB->SGBusAddr);
+
+ } else if(pcmd->request_buffer) {
+ //dc390_pci_sync(pSRB);
+
+ sg_dma_len(&pSRB->Segmentx) = pcmd->request_bufflen - pSRB->Saved_Ptr;
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (struct scatterlist *) &pSRB->Segmentx;
+ } else {
+ pSRB->SGcount = 0;
+ printk (KERN_INFO "DC390: RESTORE_PTR message for Transfer without Scatter-Gather ??\n");
+ }
+
+ pSRB->TotalXferredLen = pSRB->Saved_Ptr;
+}
+
+
+/* According to the docs, the AM53C974 reads the message and
+ * generates a Successful Operation IRQ before asserting ACK for
+ * the last byte (how does it know whether it's the last ?) */
+/* The old code handled it in another way, indicating, that on
+ * every message byte an IRQ is generated and every byte has to
+ * be manually ACKed. Hmmm ? (KG, 98/11/28) */
+/* The old implementation was correct. Sigh! */
+
+/* Check if the message is complete */
+static u8 __inline__
+dc390_MsgIn_complete (u8 *msgbuf, u32 len)
+{
+ if (*msgbuf == EXTENDED_MESSAGE)
+ {
+ if (len < 2) return 0;
+ if (len < msgbuf[1] + 2) return 0;
+ }
+ else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages
+ if (len < 2) return 0;
+ return 1;
+}
+
+
+
+/* read and eval received messages */
+static void
+dc390_MsgIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ struct dc390_dcb* pDCB = pACB->pActiveDCB;
+
+ /* Read the msg */
+
+ pSRB->MsgInBuf[pACB->MsgLen++] = DC390_read8 (ScsiFifo);
+ //pSRB->SRBState = 0;
+
+ /* Msg complete ? */
+ if (dc390_MsgIn_complete (pSRB->MsgInBuf, pACB->MsgLen))
+ {
+ DEBUG0 (printk (KERN_INFO "DC390: MsgIn:"); dc390_printMsg (pSRB->MsgInBuf, pACB->MsgLen));
+ /* Now eval the msg */
+ switch (pSRB->MsgInBuf[0])
+ {
+ case DISCONNECT:
+ pSRB->SRBState = SRB_DISCONNECT; break;
+
+ case SIMPLE_QUEUE_TAG:
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ pSRB = dc390_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]);
+ break;
+
+ case MESSAGE_REJECT:
+ DC390_write8 (ScsiCmd, RESET_ATN_CMD);
+ pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
+ if( pSRB->SRBState & DO_SYNC_NEGO)
+ dc390_MsgIn_set_async (pACB, pSRB);
+ break;
+
+ case EXTENDED_MESSAGE:
+ /* reject every extended msg but SDTR */
+ if (pSRB->MsgInBuf[1] != 3 || pSRB->MsgInBuf[2] != EXTENDED_SDTR)
+ dc390_MsgIn_reject (pACB, pSRB);
+ else
+ {
+ if (pSRB->MsgInBuf[3] == 0 || pSRB->MsgInBuf[4] == 0)
+ dc390_MsgIn_set_async (pACB, pSRB);
+ else
+ dc390_MsgIn_set_sync (pACB, pSRB);
+ }
+
+ // nothing has to be done
+ case COMMAND_COMPLETE: break;
+
+ // SAVE POINTER may be ignored as we have the struct dc390_srb* associated with the
+ // scsi command. Thanks, Gerard, for pointing it out.
+ case SAVE_POINTERS:
+ pSRB->Saved_Ptr = pSRB->TotalXferredLen;
+ break;
+ // The device might want to restart transfer with a RESTORE
+ case RESTORE_POINTERS:
+ DEBUG0(printk ("DC390: RESTORE POINTER message received ... try to handle\n"));
+ dc390_restore_ptr (pACB, pSRB);
+ break;
+
+ // reject unknown messages
+ default: dc390_MsgIn_reject (pACB, pSRB);
+ }
+
+ /* Clear counter and MsgIn state */
+ pSRB->SRBState &= ~SRB_MSGIN;
+ pACB->MsgLen = 0;
+ }
+
+ *psstatus = SCSI_NOP0;
+ DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);
+ //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+}
+
+
+static void
+dc390_DataIO_Comm( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 ioDir)
+{
+ struct scatterlist *psgl;
+ unsigned long lval;
+ struct dc390_dcb* pDCB = pACB->pActiveDCB;
+
+ if (pSRB == pACB->pTmpSRB)
+ {
+ if (pDCB)
+ printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (%02i-%i)\n", pDCB->TargetID, pDCB->TargetLUN);
+ else
+ printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (DCB 0!)\n");
+
+ /* Try to recover - some broken disks react badly to tagged INQUIRY */
+ if (pDCB && pACB->scan_devices && pDCB->GoingSRBCnt == 1) {
+ pSRB = pDCB->pGoingSRB;
+ pDCB->pActiveSRB = pSRB;
+ } else {
+ pSRB->pSRBDCB = pDCB;
+ dc390_EnableMsgOut_Abort(pACB, pSRB);
+ if (pDCB)
+ pDCB->DCBFlag |= ABORT_DEV;
+ return;
+ }
+ }
+
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir /* | DMA_INT */);
+ if( !pSRB->SGToBeXferLen )
+ {
+ psgl = pSRB->pSegmentList;
+ pSRB->SGBusAddr = cpu_to_le32(pci_dma_lo32(sg_dma_address(psgl)));
+ pSRB->SGToBeXferLen = cpu_to_le32(sg_dma_len(psgl));
+ DEBUG1(printk (KERN_DEBUG " DC390: Next SG segment."));
+ }
+ lval = pSRB->SGToBeXferLen;
+ DEBUG1(printk (KERN_DEBUG " DC390: Start transfer: %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr));
+ DC390_write8 (CtcReg_Low, (u8) lval);
+ lval >>= 8;
+ DC390_write8 (CtcReg_Mid, (u8) lval);
+ lval >>= 8;
+ DC390_write8 (CtcReg_High, (u8) lval);
+
+ DC390_write32 (DMA_XferCnt, pSRB->SGToBeXferLen);
+ DC390_write32 (DMA_XferAddr, pSRB->SGBusAddr);
+
+ //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); /* | DMA_INT; */
+ pSRB->SRBState = SRB_DATA_XFER;
+
+ DC390_write8 (ScsiCmd, DMA_COMMAND+INFO_XFER_CMD);
+
+ DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT);
+ //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT));
+ //DEBUG1(printk (KERN_DEBUG "DC390: DMA_Status: %02x\n", DC390_read8 (DMA_Status)));
+ //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT));
+ }
+ else /* xfer pad */
+ {
+ if( pSRB->SGcount )
+ {
+ pSRB->AdaptStatus = H_OVER_UNDER_RUN;
+ pSRB->SRBStatus |= OVER_RUN;
+ DEBUG0(printk (KERN_WARNING " DC390: Overrun -"));
+ }
+ DEBUG0(printk (KERN_WARNING " Clear transfer pad \n"));
+ DC390_write8 (CtcReg_Low, 0);
+ DC390_write8 (CtcReg_Mid, 0);
+ DC390_write8 (CtcReg_High, 0);
+
+ pSRB->SRBState |= SRB_XFERPAD;
+ DC390_write8 (ScsiCmd, DMA_COMMAND+XFER_PAD_BYTE);
+/*
+ DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); // | DMA_INT;
+ DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT);
+*/
+ }
+}
+
+
+static void
+dc390_DataOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ dc390_DataIO_Comm (pACB, pSRB, WRITE_DIRECTION);
+}
+
+static void
+dc390_DataInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ dc390_DataIO_Comm (pACB, pSRB, READ_DIRECTION);
+}
+
+static void
+dc390_CommandPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ struct dc390_dcb* pDCB;
+ u8 i, cnt;
+ u8 *ptr;
+
+ DC390_write8 (ScsiCmd, RESET_ATN_CMD);
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ cnt = (u8) pSRB->pcmd->cmd_len;
+ ptr = (u8 *) pSRB->pcmd->cmnd;
+ for(i=0; i < cnt; i++)
+ DC390_write8 (ScsiFifo, *(ptr++));
+ }
+ else
+ {
+ DC390_write8 (ScsiFifo, REQUEST_SENSE);
+ pDCB = pACB->pActiveDCB;
+ DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5);
+ DC390_write8 (ScsiFifo, 0);
+ DC390_write8 (ScsiFifo, 0);
+ DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer));
+ DC390_write8 (ScsiFifo, 0);
+ DEBUG0(printk(KERN_DEBUG "DC390: AutoReqSense (CmndPhase)!\n"));
+ }
+ pSRB->SRBState = SRB_COMMAND;
+ DC390_write8 (ScsiCmd, INFO_XFER_CMD);
+}
+
+static void
+dc390_StatusPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ pSRB->SRBState = SRB_STATUS;
+ DC390_write8 (ScsiCmd, INITIATOR_CMD_CMPLTE);
+ //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+}
+
+static void
+dc390_MsgOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ u8 bval, i, cnt;
+ u8 *ptr;
+ struct dc390_dcb* pDCB;
+
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ pDCB = pACB->pActiveDCB;
+ if( !(pSRB->SRBState & SRB_MSGOUT) )
+ {
+ cnt = pSRB->MsgCnt;
+ if( cnt )
+ {
+ ptr = (u8 *) pSRB->MsgOutBuf;
+ for(i=0; i < cnt; i++)
+ DC390_write8 (ScsiFifo, *(ptr++));
+ pSRB->MsgCnt = 0;
+ if( (pDCB->DCBFlag & ABORT_DEV_) &&
+ (pSRB->MsgOutBuf[0] == ABORT) )
+ pSRB->SRBState = SRB_ABORT_SENT;
+ }
+ else
+ {
+ bval = ABORT; /* ??? MSG_NOP */
+ if( (pSRB->pcmd->cmnd[0] == INQUIRY ) ||
+ (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ goto mop1;
+ }
+ DC390_write8 (ScsiFifo, bval);
+ }
+ DC390_write8 (ScsiCmd, INFO_XFER_CMD);
+ }
+ else
+ {
+mop1:
+ printk (KERN_ERR "DC390: OLD Sync Nego code triggered! (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN);
+ DC390_write8 (ScsiFifo, EXTENDED_MESSAGE);
+ DC390_write8 (ScsiFifo, 3); /* ;length of extended msg */
+ DC390_write8 (ScsiFifo, EXTENDED_SDTR); /* ; sync nego */
+ DC390_write8 (ScsiFifo, pDCB->NegoPeriod);
+ if (pDCB->SyncOffset & 0x0f)
+ DC390_write8 (ScsiFifo, pDCB->SyncOffset);
+ else
+ DC390_write8 (ScsiFifo, SYNC_NEGO_OFFSET);
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ DC390_write8 (ScsiCmd, INFO_XFER_CMD);
+ }
+}
+
+static void
+dc390_MsgInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ if( !(pSRB->SRBState & SRB_MSGIN) )
+ {
+ pSRB->SRBState &= ~SRB_DISCONNECT;
+ pSRB->SRBState |= SRB_MSGIN;
+ }
+ DC390_write8 (ScsiCmd, INFO_XFER_CMD);
+ //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+}
+
+static void
+dc390_Nop_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+}
+
+static void
+dc390_Nop_1( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
+{
+}
+
+
+static void
+dc390_SetXferRate( struct dc390_acb* pACB, struct dc390_dcb* pDCB )
+{
+ u8 bval, i, cnt;
+ struct dc390_dcb* ptr;
+
+ if( !(pDCB->TargetLUN) )
+ {
+ if( !pACB->scan_devices )
+ {
+ ptr = pACB->pLinkDCB;
+ cnt = pACB->DCBCnt;
+ bval = pDCB->TargetID;
+ for(i=0; i<cnt; i++)
+ {
+ if( ptr->TargetID == bval )
+ {
+ ptr->SyncPeriod = pDCB->SyncPeriod;
+ ptr->SyncOffset = pDCB->SyncOffset;
+ ptr->CtrlR3 = pDCB->CtrlR3;
+ ptr->CtrlR4 = pDCB->CtrlR4;
+ ptr->SyncMode = pDCB->SyncMode;
+ }
+ ptr = ptr->pNextDCB;
+ }
+ }
+ }
+ return;
+}
+
+
+static void
+dc390_Disconnect( struct dc390_acb* pACB )
+{
+ struct dc390_dcb *pDCB;
+ struct dc390_srb *pSRB, *psrb;
+ u8 i, cnt;
+
+ DEBUG0(printk(KERN_INFO "DISC,"));
+
+ if (!pACB->Connected) printk(KERN_ERR "DC390: Disconnect not-connected bus?\n");
+ pACB->Connected = 0;
+ pDCB = pACB->pActiveDCB;
+ if (!pDCB)
+ {
+ DEBUG0(printk(KERN_ERR "ACB:%p->ActiveDCB:%p IOPort:%04x IRQ:%02x !\n",\
+ pACB, pDCB, pACB->IOPortBase, pACB->IRQLevel));
+ mdelay(400);
+ DC390_read8 (INT_Status); /* Reset Pending INT */
+ DC390_write8 (ScsiCmd, EN_SEL_RESEL);
+ return;
+ }
+ DC390_write8 (ScsiCmd, EN_SEL_RESEL);
+ pSRB = pDCB->pActiveSRB;
+ pACB->pActiveDCB = NULL;
+ pSRB->ScsiPhase = SCSI_NOP0;
+ if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+ pSRB->SRBState = 0;
+ else if( pSRB->SRBState & SRB_ABORT_SENT )
+ {
+ pDCB->TagMask = 0;
+ pDCB->DCBFlag = 0;
+ cnt = pDCB->GoingSRBCnt;
+ pDCB->GoingSRBCnt = 0;
+ pSRB = pDCB->pGoingSRB;
+ for( i=0; i < cnt; i++)
+ {
+ psrb = pSRB->pNextSRB;
+ dc390_Free_insert (pACB, pSRB);
+ pSRB = psrb;
+ }
+ pDCB->pGoingSRB = NULL;
+ }
+ else
+ {
+ if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
+ !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
+ { /* Selection time out */
+ pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
+ goto disc1;
+ }
+ else if (!(pSRB->SRBState & SRB_DISCONNECT) && (pSRB->SRBState & SRB_COMPLETED))
+ {
+disc1:
+ dc390_freetag (pDCB, pSRB);
+ pDCB->pActiveSRB = NULL;
+ pSRB->SRBState = SRB_FREE;
+ dc390_SRBdone( pACB, pDCB, pSRB);
+ }
+ }
+ pACB->MsgLen = 0;
+}
+
+
+static void
+dc390_Reselect( struct dc390_acb* pACB )
+{
+ struct dc390_dcb* pDCB;
+ struct dc390_srb* pSRB;
+ u8 id, lun;
+
+ DEBUG0(printk(KERN_INFO "RSEL,"));
+ pACB->Connected = 1;
+ pDCB = pACB->pActiveDCB;
+ if( pDCB )
+ { /* Arbitration lost but Reselection won */
+ DEBUG0(printk ("DC390: (ActiveDCB != 0: Arb. lost but resel. won)!\n"));
+ pSRB = pDCB->pActiveSRB;
+ if( !( pACB->scan_devices ) )
+ {
+ struct scsi_cmnd *pcmd = pSRB->pcmd;
+ pcmd->resid = pcmd->request_bufflen;
+ SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
+ dc390_Going_remove(pDCB, pSRB);
+ dc390_Free_insert(pACB, pSRB);
+ pcmd->scsi_done (pcmd);
+ DEBUG0(printk(KERN_DEBUG"DC390: Return SRB %p to free\n", pSRB));
+ }
+ }
+ /* Get ID */
+ lun = DC390_read8 (ScsiFifo);
+ DEBUG0(printk ("Dev %02x,", lun));
+ if (!(lun & (1 << pACB->pScsiHost->this_id)))
+ printk (KERN_ERR "DC390: Reselection must select host adapter: %02x!\n", lun);
+ else
+ lun ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */
+ id = 0; while (lun >>= 1) id++;
+ /* Get LUN */
+ lun = DC390_read8 (ScsiFifo);
+ if (!(lun & IDENTIFY_BASE)) printk (KERN_ERR "DC390: Resel: Expect identify message!\n");
+ lun &= 7;
+ DEBUG0(printk ("(%02i-%i),", id, lun));
+ pDCB = dc390_findDCB (pACB, id, lun);
+ if (!pDCB)
+ {
+ printk (KERN_ERR "DC390: Reselect from non existing device (%02i-%i)\n",
+ id, lun);
+ return;
+ }
+ pACB->pActiveDCB = pDCB;
+ /* TagQ: We expect a message soon, so never mind the exact SRB */
+ if( pDCB->SyncMode & EN_TAG_QUEUEING )
+ {
+ pSRB = pACB->pTmpSRB;
+ pDCB->pActiveSRB = pSRB;
+ }
+ else
+ {
+ pSRB = pDCB->pActiveSRB;
+ if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
+ {
+ pSRB= pACB->pTmpSRB;
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ printk (KERN_ERR "DC390: Reselect without outstanding cmnd (%02i-%i)\n",
+ id, lun);
+ pDCB->pActiveSRB = pSRB;
+ dc390_EnableMsgOut_Abort ( pACB, pSRB );
+ }
+ else
+ {
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ {
+ pSRB->SRBState = SRB_ABORT_SENT;
+ printk (KERN_INFO "DC390: Reselect: Abort (%02i-%i)\n",
+ id, lun);
+ dc390_EnableMsgOut_Abort( pACB, pSRB );
+ }
+ else
+ pSRB->SRBState = SRB_DATA_XFER;
+ }
+ }
+
+ DEBUG1(printk (KERN_DEBUG "Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber));
+ pSRB->ScsiPhase = SCSI_NOP0;
+ DC390_write8 (Scsi_Dest_ID, pDCB->TargetID);
+ DC390_write8 (Sync_Period, pDCB->SyncPeriod);
+ DC390_write8 (Sync_Offset, pDCB->SyncOffset);
+ DC390_write8 (CtrlReg1, pDCB->CtrlR1);
+ DC390_write8 (CtrlReg3, pDCB->CtrlR3);
+ DC390_write8 (CtrlReg4, pDCB->CtrlR4); /* ; Glitch eater */
+ DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); /* ;to release the /ACK signal */
+}
+
+static int __inline__
+dc390_RequestSense(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
+{
+ struct scsi_cmnd *pcmd;
+
+ pcmd = pSRB->pcmd;
+
+ REMOVABLEDEBUG(printk(KERN_INFO "DC390: RequestSense(Cmd %02x, Id %02x, LUN %02x)\n",\
+ pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN));
+
+ pSRB->SRBFlag |= AUTO_REQSENSE;
+ pSRB->SavedSGCount = pcmd->use_sg;
+ pSRB->SavedTotXLen = pSRB->TotalXferredLen;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0; /* CHECK_CONDITION<<1; */
+
+ /* We are called from SRBdone, original PCI mapping has been removed
+ * already, new one is set up from StartSCSI */
+ pSRB->SGIndex = 0;
+
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ return dc390_StartSCSI(pACB, pDCB, pSRB);
+}
+
+
+static void
+dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB )
+{
+ u8 status;
+ struct scsi_cmnd *pcmd;
+
+ pcmd = pSRB->pcmd;
+ /* KG: Moved pci_unmap here */
+ dc390_pci_unmap(pSRB);
+
+ status = pSRB->TargetStatus;
+
+ DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p, pid %li\n", status, pcmd->result,\
+ pSRB, pcmd->pid));
+ if(pSRB->SRBFlag & AUTO_REQSENSE)
+ { /* Last command was a Request Sense */
+ pSRB->SRBFlag &= ~AUTO_REQSENSE;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = CHECK_CONDITION << 1;
+
+ //pcmd->result = MK_RES(DRIVER_SENSE,DID_OK,0,status);
+ if (status == (CHECK_CONDITION << 1))
+ pcmd->result = MK_RES_LNX(0, DID_BAD_TARGET, 0, /*CHECK_CONDITION*/0);
+ else /* Retry */
+ {
+ if( pSRB->pcmd->cmnd[0] == TEST_UNIT_READY /* || pSRB->pcmd->cmnd[0] == START_STOP */)
+ {
+ /* Don't retry on TEST_UNIT_READY */
+ pcmd->result = MK_RES_LNX(DRIVER_SENSE,DID_OK,0,CHECK_CONDITION);
+ REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->pcmd->cmnd[0],\
+ (u32) pcmd->result, (u32) pSRB->TotalXferredLen));
+ } else {
+ SET_RES_DRV(pcmd->result, DRIVER_SENSE);
+ pcmd->use_sg = pSRB->SavedSGCount;
+ //pSRB->ScsiCmdLen = (u8) (pSRB->Segment1[0] >> 8);
+ DEBUG0 (printk ("DC390: RETRY pid %li (%02x), target %02i-%02i\n", pcmd->pid, pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun));
+ pSRB->TotalXferredLen = 0;
+ SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
+ }
+ }
+ goto cmd_done;
+ }
+ if( status )
+ {
+ if( status_byte(status) == CHECK_CONDITION )
+ {
+ if (dc390_RequestSense(pACB, pDCB, pSRB)) {
+ SET_RES_DID(pcmd->result, DID_ERROR);
+ goto cmd_done;
+ }
+ return;
+ }
+ else if( status_byte(status) == QUEUE_FULL )
+ {
+ scsi_track_queue_full(pcmd->device, pDCB->GoingSRBCnt - 1);
+ pcmd->use_sg = pSRB->SavedSGCount;
+ DEBUG0 (printk ("DC390: RETRY pid %li (%02x), target %02i-%02i\n", pcmd->pid, pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun));
+ pSRB->TotalXferredLen = 0;
+ SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
+ }
+ else if(status == SCSI_STAT_SEL_TIMEOUT)
+ {
+ pSRB->AdaptStatus = H_SEL_TIMEOUT;
+ pSRB->TargetStatus = 0;
+ pcmd->result = MK_RES(0,DID_NO_CONNECT,0,0);
+ /* Devices are removed below ... */
+ }
+ else if (status_byte(status) == BUSY &&
+ (pcmd->cmnd[0] == TEST_UNIT_READY || pcmd->cmnd[0] == INQUIRY) &&
+ pACB->scan_devices)
+ {
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = status;
+ pcmd->result = MK_RES(0,0,pSRB->EndMessage,/*status*/0);
+ }
+ else
+ { /* Another error */
+ pSRB->TotalXferredLen = 0;
+ SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
+ goto cmd_done;
+ }
+ }
+ else
+ { /* Target status == 0 */
+ status = pSRB->AdaptStatus;
+ if(status & H_OVER_UNDER_RUN)
+ {
+ pSRB->TargetStatus = 0;
+ SET_RES_DID(pcmd->result,DID_OK);
+ SET_RES_MSG(pcmd->result,pSRB->EndMessage);
+ }
+ else if( pSRB->SRBStatus & PARITY_ERROR)
+ {
+ //pcmd->result = MK_RES(0,DID_PARITY,pSRB->EndMessage,0);
+ SET_RES_DID(pcmd->result,DID_PARITY);
+ SET_RES_MSG(pcmd->result,pSRB->EndMessage);
+ }
+ else /* No error */
+ {
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ SET_RES_DID(pcmd->result,DID_OK);
+ }
+ }
+
+cmd_done:
+ pcmd->resid = pcmd->request_bufflen - pSRB->TotalXferredLen;
+
+ dc390_Going_remove (pDCB, pSRB);
+ /* Add to free list */
+ dc390_Free_insert (pACB, pSRB);
+
+ DEBUG0(printk (KERN_DEBUG "DC390: SRBdone: done pid %li\n", pcmd->pid));
+ pcmd->scsi_done (pcmd);
+
+ return;
+}
+
+
+/* Remove all SRBs from Going list and inform midlevel */
+static void
+dc390_DoingSRB_Done(struct dc390_acb* pACB, struct scsi_cmnd *cmd)
+{
+ struct dc390_dcb *pDCB, *pdcb;
+ struct dc390_srb *psrb, *psrb2;
+ int i;
+ struct scsi_cmnd *pcmd;
+
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ if (! pdcb) return;
+ do
+ {
+ psrb = pdcb->pGoingSRB;
+ for (i = 0; i < pdcb->GoingSRBCnt; i++)
+ {
+ psrb2 = psrb->pNextSRB;
+ pcmd = psrb->pcmd;
+ dc390_Free_insert (pACB, psrb);
+ psrb = psrb2;
+ }
+ pdcb->GoingSRBCnt = 0;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ } while( pdcb != pDCB );
+}
+
+
+static void
+dc390_ResetSCSIBus( struct dc390_acb* pACB )
+{
+ //DC390_write8 (ScsiCmd, RST_DEVICE_CMD);
+ //udelay (250);
+ //DC390_write8 (ScsiCmd, NOP_CMD);
+
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+ DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD);
+ pACB->Connected = 0;
+
+ return;
+}
+
+static void
+dc390_ScsiRstDetect( struct dc390_acb* pACB )
+{
+ printk ("DC390: Rst_Detect: laststat = %08x\n", dc390_laststatus);
+ //DEBUG0(printk(KERN_INFO "RST_DETECT,"));
+
+ DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+ /* Unlock before ? */
+ /* delay half a second */
+ udelay (1000);
+ DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+ pACB->pScsiHost->last_reset = jiffies + 5*HZ/2
+ + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
+ pACB->Connected = 0;
+
+ if( pACB->ACBFlag & RESET_DEV )
+ pACB->ACBFlag |= RESET_DONE;
+ else
+ { /* Reset was issued by sb else */
+ pACB->ACBFlag |= RESET_DETECT;
+
+ dc390_ResetDevParam( pACB );
+ dc390_DoingSRB_Done( pACB, NULL);
+ //dc390_RecoverSRB( pACB );
+ pACB->pActiveDCB = NULL;
+ pACB->ACBFlag = 0;
+ }
+ return;
+}
+
+static int DC390_queuecommand(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct scsi_device *sdev = cmd->device;
+ struct dc390_acb *acb = (struct dc390_acb *)sdev->host->hostdata;
+ struct dc390_dcb *dcb = sdev->hostdata;
+ struct dc390_srb *srb;
+
+ if (sdev->queue_depth <= dcb->GoingSRBCnt)
+ goto device_busy;
+ if (acb->pActiveDCB)
+ goto host_busy;
+ if (acb->ACBFlag & (RESET_DETECT|RESET_DONE|RESET_DEV))
+ goto host_busy;
+
+ srb = acb->pFreeSRB;
+ if (unlikely(srb == NULL))
+ goto host_busy;
+
+ cmd->scsi_done = done;
+ cmd->result = 0;
+ acb->Cmds++;
+
+ acb->pFreeSRB = srb->pNextSRB;
+ srb->pNextSRB = NULL;
+
+ srb->pSRBDCB = dcb;
+ srb->pcmd = cmd;
+ cmd->host_scribble = (char *)srb;
+
+ srb->SGIndex = 0;
+ srb->AdaptStatus = 0;
+ srb->TargetStatus = 0;
+ srb->MsgCnt = 0;
+
+ srb->SRBStatus = 0;
+ srb->SRBFlag = 0;
+ srb->SRBState = 0;
+ srb->TotalXferredLen = 0;
+ srb->SGBusAddr = 0;
+ srb->SGToBeXferLen = 0;
+ srb->ScsiPhase = 0;
+ srb->EndMessage = 0;
+ srb->TagNumber = SCSI_NO_TAG;
+
+ if (dc390_StartSCSI(acb, dcb, srb)) {
+ dc390_Free_insert(acb, srb);
+ goto host_busy;
+ }
+
+ dc390_Going_append(dcb, srb);
+
+ return 0;
+
+ host_busy:
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ device_busy:
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+}
+
+static void dc390_dumpinfo (struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
+{
+ struct pci_dev *pdev;
+ u16 pstat;
+
+ if (!pDCB) pDCB = pACB->pActiveDCB;
+ if (!pSRB && pDCB) pSRB = pDCB->pActiveSRB;
+
+ if (pSRB)
+ {
+ printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08x, Phase %02x\n",
+ pSRB->TotalXferredLen, pSRB->SGToBeXferLen, pSRB->SRBState,
+ pSRB->ScsiPhase);
+ printk ("DC390: AdpaterStatus: %02x, SRB Status %02x\n", pSRB->AdaptStatus, pSRB->SRBStatus);
+ }
+ printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08x\n", dc390_laststatus);
+ printk ("DC390: Register dump: SCSI block:\n");
+ printk ("DC390: XferCnt Cmd Stat IntS IRQS FFIS Ctl1 Ctl2 Ctl3 Ctl4\n");
+ printk ("DC390: %06x %02x %02x %02x",
+ DC390_read8(CtcReg_Low) + (DC390_read8(CtcReg_Mid) << 8) + (DC390_read8(CtcReg_High) << 16),
+ DC390_read8(ScsiCmd), DC390_read8(Scsi_Status), DC390_read8(Intern_State));
+ printk (" %02x %02x %02x %02x %02x %02x\n",
+ DC390_read8(INT_Status), DC390_read8(Current_Fifo), DC390_read8(CtrlReg1),
+ DC390_read8(CtrlReg2), DC390_read8(CtrlReg3), DC390_read8(CtrlReg4));
+ DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
+ if (DC390_read8(Current_Fifo) & 0x1f)
+ {
+ printk ("DC390: FIFO:");
+ while (DC390_read8(Current_Fifo) & 0x1f) printk (" %02x", DC390_read8(ScsiFifo));
+ printk ("\n");
+ }
+ printk ("DC390: Register dump: DMA engine:\n");
+ printk ("DC390: Cmd STrCnt SBusA WrkBC WrkAC Stat SBusCtrl\n");
+ printk ("DC390: %02x %08x %08x %08x %08x %02x %08x\n",
+ DC390_read8(DMA_Cmd), DC390_read32(DMA_XferCnt), DC390_read32(DMA_XferAddr),
+ DC390_read32(DMA_Wk_ByteCntr), DC390_read32(DMA_Wk_AddrCntr),
+ DC390_read8(DMA_Status), DC390_read32(DMA_ScsiBusCtrl));
+ DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
+
+ pdev = pACB->pdev;
+ pci_read_config_word(pdev, PCI_STATUS, &pstat);
+ printk ("DC390: Register dump: PCI Status: %04x\n", pstat);
+ printk ("DC390: In case of driver trouble read Documentation/scsi/tmscsim.txt\n");
+}
+
+
+static int DC390_abort(struct scsi_cmnd *cmd)
+{
+ struct dc390_acb *pACB = (struct dc390_acb*) cmd->device->host->hostdata;
+ struct dc390_dcb *pDCB = (struct dc390_dcb*) cmd->device->hostdata;
+
+ printk("DC390: Abort command (pid %li, Device %02i-%02i)\n",
+ cmd->pid, cmd->device->id, cmd->device->lun);
+
+ /* abort() is too stupid for already sent commands at the moment.
+ * If it's called we are in trouble anyway, so let's dump some info
+ * into the syslog at least. (KG, 98/08/20,99/06/20) */
+ dc390_dumpinfo(pACB, pDCB, NULL);
+
+ pDCB->DCBFlag |= ABORT_DEV_;
+ printk(KERN_INFO "DC390: Aborted pid %li\n", cmd->pid);
+
+ return FAILED;
+}
+
+
+static void dc390_ResetDevParam( struct dc390_acb* pACB )
+{
+ struct dc390_dcb *pDCB, *pdcb;
+
+ pDCB = pACB->pLinkDCB;
+ if (! pDCB) return;
+ pdcb = pDCB;
+ do
+ {
+ pDCB->SyncMode &= ~SYNC_NEGO_DONE;
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->TagMask = 0;
+ pDCB->CtrlR3 = FAST_CLK;
+ pDCB->CtrlR4 &= NEGATE_REQACKDATA | CTRL4_RESERVED | NEGATE_REQACK;
+ pDCB->CtrlR4 |= pACB->glitch_cfg;
+ pDCB = pDCB->pNextDCB;
+ }
+ while( pdcb != pDCB );
+ pACB->ACBFlag &= ~(RESET_DEV | RESET_DONE | RESET_DETECT);
+
+}
+
+static int DC390_bus_reset (struct scsi_cmnd *cmd)
+{
+ struct dc390_acb* pACB = (struct dc390_acb*) cmd->device->host->hostdata;
+ u8 bval;
+
+ bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST;
+ DC390_write8(CtrlReg1, bval); /* disable IRQ on bus reset */
+
+ pACB->ACBFlag |= RESET_DEV;
+ dc390_ResetSCSIBus(pACB);
+
+ dc390_ResetDevParam(pACB);
+ udelay(1000);
+ pACB->pScsiHost->last_reset = jiffies + 3*HZ/2
+ + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
+
+ DC390_write8(ScsiCmd, CLEAR_FIFO_CMD);
+ DC390_read8(INT_Status); /* Reset Pending INT */
+
+ dc390_DoingSRB_Done(pACB, cmd);
+
+ pACB->pActiveDCB = NULL;
+ pACB->ACBFlag = 0;
+
+ bval = DC390_read8(CtrlReg1) & ~DIS_INT_ON_SCSI_RST;
+ DC390_write8(CtrlReg1, bval); /* re-enable interrupt */
+
+ return SUCCESS;
+}
+
+/**
+ * dc390_slave_alloc - Called by the scsi mid layer to tell us about a new
+ * scsi device that we need to deal with.
+ *
+ * @scsi_device: The new scsi device that we need to handle.
+ */
+static int dc390_slave_alloc(struct scsi_device *scsi_device)
+{
+ struct dc390_acb *pACB = (struct dc390_acb*) scsi_device->host->hostdata;
+ struct dc390_dcb *pDCB, *pDCB2 = NULL;
+ uint id = scsi_device->id;
+ uint lun = scsi_device->lun;
+
+ pDCB = kmalloc(sizeof(struct dc390_dcb), GFP_KERNEL);
+ if (!pDCB)
+ return -ENOMEM;
+ memset(pDCB, 0, sizeof(struct dc390_dcb));
+
+ if (!pACB->DCBCnt++) {
+ pACB->pLinkDCB = pDCB;
+ pACB->pDCBRunRobin = pDCB;
+ } else {
+ pACB->pLastDCB->pNextDCB = pDCB;
+ }
+
+ pDCB->pNextDCB = pACB->pLinkDCB;
+ pACB->pLastDCB = pDCB;
+
+ pDCB->pDCBACB = pACB;
+ pDCB->TargetID = id;
+ pDCB->TargetLUN = lun;
+
+ /*
+ * Some values are for all LUNs: Copy them
+ * In a clean way: We would have an own structure for a SCSI-ID
+ */
+ if (lun && (pDCB2 = dc390_findDCB(pACB, id, 0))) {
+ pDCB->DevMode = pDCB2->DevMode;
+ pDCB->SyncMode = pDCB2->SyncMode & SYNC_NEGO_DONE;
+ pDCB->SyncPeriod = pDCB2->SyncPeriod;
+ pDCB->SyncOffset = pDCB2->SyncOffset;
+ pDCB->NegoPeriod = pDCB2->NegoPeriod;
+
+ pDCB->CtrlR3 = pDCB2->CtrlR3;
+ pDCB->CtrlR4 = pDCB2->CtrlR4;
+ } else {
+ u8 index = pACB->AdapterIndex;
+ PEEprom prom = (PEEprom) &dc390_eepromBuf[index][id << 2];
+
+ pDCB->DevMode = prom->EE_MODE1;
+ pDCB->NegoPeriod =
+ (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2;
+ pDCB->CtrlR3 = FAST_CLK;
+ pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED;
+ if (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION)
+ pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK;
+ }
+
+ if (pDCB->DevMode & SYNC_NEGO_)
+ pDCB->SyncMode |= SYNC_ENABLE;
+ else {
+ pDCB->SyncMode = 0;
+ pDCB->SyncOffset &= ~0x0f;
+ }
+
+ pDCB->CtrlR1 = pACB->pScsiHost->this_id;
+ if (pDCB->DevMode & PARITY_CHK_)
+ pDCB->CtrlR1 |= PARITY_ERR_REPO;
+
+ pACB->scan_devices = 1;
+ scsi_device->hostdata = pDCB;
+ return 0;
+}
+
+/**
+ * dc390_slave_destroy - Called by the scsi mid layer to tell us about a
+ * device that is going away.
+ *
+ * @scsi_device: The scsi device that we need to remove.
+ */
+static void dc390_slave_destroy(struct scsi_device *scsi_device)
+{
+ struct dc390_acb* pACB = (struct dc390_acb*) scsi_device->host->hostdata;
+ struct dc390_dcb* pDCB = (struct dc390_dcb*) scsi_device->hostdata;
+ struct dc390_dcb* pPrevDCB = pACB->pLinkDCB;
+
+ pACB->scan_devices = 0;
+
+ BUG_ON(pDCB->GoingSRBCnt > 1);
+
+ if (pDCB == pACB->pLinkDCB) {
+ if (pACB->pLastDCB == pDCB) {
+ pDCB->pNextDCB = NULL;
+ pACB->pLastDCB = NULL;
+ }
+ pACB->pLinkDCB = pDCB->pNextDCB;
+ } else {
+ while (pPrevDCB->pNextDCB != pDCB)
+ pPrevDCB = pPrevDCB->pNextDCB;
+ pPrevDCB->pNextDCB = pDCB->pNextDCB;
+ if (pDCB == pACB->pLastDCB)
+ pACB->pLastDCB = pPrevDCB;
+ }
+
+ if (pDCB == pACB->pActiveDCB)
+ pACB->pActiveDCB = NULL;
+ if (pDCB == pACB->pLinkDCB)
+ pACB->pLinkDCB = pDCB->pNextDCB;
+ if (pDCB == pACB->pDCBRunRobin)
+ pACB->pDCBRunRobin = pDCB->pNextDCB;
+ kfree(pDCB);
+
+ pACB->DCBCnt--;
+}
+
+static int dc390_slave_configure(struct scsi_device *sdev)
+{
+ struct dc390_acb *acb = (struct dc390_acb *)sdev->host->hostdata;
+ struct dc390_dcb *dcb = (struct dc390_dcb *)sdev->hostdata;
+
+ acb->scan_devices = 0;
+ if (sdev->tagged_supported && (dcb->DevMode & TAG_QUEUEING_)) {
+ dcb->SyncMode |= EN_TAG_QUEUEING;
+ scsi_activate_tcq(sdev, acb->TagMaxNum);
+ }
+
+ return 0;
+}
+
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .proc_name = "tmscsim",
+ .name = DC390_BANNER " V" DC390_VERSION,
+ .slave_alloc = dc390_slave_alloc,
+ .slave_configure = dc390_slave_configure,
+ .slave_destroy = dc390_slave_destroy,
+ .queuecommand = DC390_queuecommand,
+ .eh_abort_handler = DC390_abort,
+ .eh_bus_reset_handler = DC390_bus_reset,
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = 1,
+ .use_clustering = DISABLE_CLUSTERING,
+};
+
+/***********************************************************************
+ * Functions for access to DC390 EEPROM
+ * and some to emulate it
+ *
+ **********************************************************************/
+
+static void __devinit dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd)
+{
+ u8 carryFlag = 1, j = 0x80, bval;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ if (carryFlag) {
+ pci_write_config_byte(pdev, 0x80, 0x40);
+ bval = 0xc0;
+ } else
+ bval = 0x80;
+
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, bval);
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, 0);
+ udelay(160);
+
+ carryFlag = (cmd & j) ? 1 : 0;
+ j >>= 1;
+ }
+}
+
+static u16 __devinit dc390_eeprom_get_data(struct pci_dev *pdev)
+{
+ int i;
+ u16 wval = 0;
+ u8 bval;
+
+ for (i = 0; i < 16; i++) {
+ wval <<= 1;
+
+ pci_write_config_byte(pdev, 0x80, 0x80);
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, 0x40);
+ udelay(160);
+ pci_read_config_byte(pdev, 0x00, &bval);
+
+ if (bval == 0x22)
+ wval |= 1;
+ }
+
+ return wval;
+}
+
+static void __devinit dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr)
+{
+ u8 cmd = EEPROM_READ, i;
+
+ for (i = 0; i < 0x40; i++) {
+ pci_write_config_byte(pdev, 0xc0, 0);
+ udelay(160);
+
+ dc390_eeprom_prepare_read(pdev, cmd++);
+ *ptr++ = dc390_eeprom_get_data(pdev);
+
+ pci_write_config_byte(pdev, 0x80, 0);
+ pci_write_config_byte(pdev, 0x80, 0);
+ udelay(160);
+ }
+}
+
+/* Override EEprom values with explicitly set values */
+static void __devinit dc390_eeprom_override(u8 index)
+{
+ u8 *ptr = (u8 *) dc390_eepromBuf[index], id;
+
+ /* Adapter Settings */
+ if (tmscsim[0] != -2)
+ ptr[EE_ADAPT_SCSI_ID] = (u8)tmscsim[0]; /* Adapter ID */
+ if (tmscsim[3] != -2)
+ ptr[EE_MODE2] = (u8)tmscsim[3];
+ if (tmscsim[5] != -2)
+ ptr[EE_DELAY] = tmscsim[5]; /* Reset delay */
+ if (tmscsim[4] != -2)
+ ptr[EE_TAG_CMD_NUM] = (u8)tmscsim[4]; /* Tagged Cmds */
+
+ /* Device Settings */
+ for (id = 0; id < MAX_SCSI_ID; id++) {
+ if (tmscsim[2] != -2)
+ ptr[id << 2] = (u8)tmscsim[2]; /* EE_MODE1 */
+ if (tmscsim[1] != -2)
+ ptr[(id << 2) + 1] = (u8)tmscsim[1]; /* EE_Speed */
+ }
+}
+
+static int __devinitdata tmscsim_def[] = {
+ 7,
+ 0 /* 10MHz */,
+ PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_ | SYNC_NEGO_ | TAG_QUEUEING_,
+ MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION | LUN_CHECK,
+ 3 /* 16 Tags per LUN */,
+ 1 /* s delay after Reset */,
+};
+
+/* Copy defaults over set values where missing */
+static void __devinit dc390_fill_with_defaults (void)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ if (tmscsim[i] < 0 || tmscsim[i] > 255)
+ tmscsim[i] = tmscsim_def[i];
+ }
+
+ /* Sanity checks */
+ if (tmscsim[0] > 7)
+ tmscsim[0] = 7;
+ if (tmscsim[1] > 7)
+ tmscsim[1] = 4;
+ if (tmscsim[4] > 5)
+ tmscsim[4] = 4;
+ if (tmscsim[5] > 180)
+ tmscsim[5] = 180;
+}
+
+static void __devinit dc390_check_eeprom(struct pci_dev *pdev, u8 index)
+{
+ u8 interpd[] = {1, 3, 5, 10, 16, 30, 60, 120};
+ u8 EEbuf[128];
+ u16 *ptr = (u16 *)EEbuf, wval = 0;
+ int i;
+
+ dc390_read_eeprom(pdev, ptr);
+ memcpy(dc390_eepromBuf[index], EEbuf, EE_ADAPT_SCSI_ID);
+ memcpy(&dc390_eepromBuf[index][EE_ADAPT_SCSI_ID],
+ &EEbuf[REAL_EE_ADAPT_SCSI_ID], EE_LEN - EE_ADAPT_SCSI_ID);
+
+ dc390_eepromBuf[index][EE_DELAY] = interpd[dc390_eepromBuf[index][EE_DELAY]];
+
+ for (i = 0; i < 0x40; i++, ptr++)
+ wval += *ptr;
+
+ /* no Tekram EEprom found */
+ if (wval != 0x1234) {
+ int speed;
+
+ printk(KERN_INFO "DC390_init: No EEPROM found! Trying default settings ...\n");
+
+ /*
+ * XXX(hch): bogus, because we might have tekram and
+ * non-tekram hbas in a single machine.
+ */
+ dc390_fill_with_defaults();
+
+ speed = dc390_clock_speed[tmscsim[1]];
+ printk(KERN_INFO "DC390: Used defaults: AdaptID=%i, SpeedIdx=%i (%i.%i MHz), "
+ "DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i), DelayReset=%is\n",
+ tmscsim[0], tmscsim[1], speed / 10, speed % 10,
+ (u8)tmscsim[2], (u8)tmscsim[3], tmscsim[4], 2 << (tmscsim[4]), tmscsim[5]);
+ }
+}
+
+static void __devinit dc390_init_hw(struct dc390_acb *pACB, u8 index)
+{
+ struct Scsi_Host *shost = pACB->pScsiHost;
+ u8 dstate;
+
+ /* Disable SCSI bus reset interrupt */
+ DC390_write8(CtrlReg1, DIS_INT_ON_SCSI_RST | shost->this_id);
+
+ if (pACB->Gmode2 & RST_SCSI_BUS) {
+ dc390_ResetSCSIBus(pACB);
+ udelay(1000);
+ shost->last_reset = jiffies + HZ/2 +
+ HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
+ }
+
+ pACB->ACBFlag = 0;
+
+ /* Reset Pending INT */
+ DC390_read8(INT_Status);
+
+ /* 250ms selection timeout */
+ DC390_write8(Scsi_TimeOut, SEL_TIMEOUT);
+
+ /* Conversion factor = 0 , 40MHz clock */
+ DC390_write8(Clk_Factor, CLK_FREQ_40MHZ);
+
+ /* NOP cmd - clear command register */
+ DC390_write8(ScsiCmd, NOP_CMD);
+
+ /* Enable Feature and SCSI-2 */
+ DC390_write8(CtrlReg2, EN_FEATURE+EN_SCSI2_CMD);
+
+ /* Fast clock */
+ DC390_write8(CtrlReg3, FAST_CLK);
+
+ /* Negation */
+ DC390_write8(CtrlReg4, pACB->glitch_cfg | /* glitch eater */
+ (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) ?
+ NEGATE_REQACKDATA : 0);
+
+ /* Clear Transfer Count High: ID */
+ DC390_write8(CtcReg_High, 0);
+ DC390_write8(DMA_Cmd, DMA_IDLE_CMD);
+ DC390_write8(ScsiCmd, CLEAR_FIFO_CMD);
+ DC390_write32(DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
+
+ dstate = DC390_read8(DMA_Status);
+ DC390_write8(DMA_Status, dstate);
+}
+
+static int __devinit dc390_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct dc390_acb *pACB;
+ struct Scsi_Host *shost;
+ unsigned long io_port;
+ int error = -ENODEV, i;
+
+ if (pci_enable_device(pdev))
+ goto out;
+
+ pci_set_master(pdev);
+
+ error = -ENOMEM;
+ shost = scsi_host_alloc(&driver_template, sizeof(struct dc390_acb));
+ if (!shost)
+ goto out_disable_device;
+
+ pACB = (struct dc390_acb *)shost->hostdata;
+ memset(pACB, 0, sizeof(struct dc390_acb));
+
+ dc390_check_eeprom(pdev, dc390_adapterCnt);
+ dc390_eeprom_override(dc390_adapterCnt);
+
+ io_port = pci_resource_start(pdev, 0);
+
+ shost->this_id = dc390_eepromBuf[dc390_adapterCnt][EE_ADAPT_SCSI_ID];
+ shost->io_port = io_port;
+ shost->n_io_port = 0x80;
+ shost->irq = pdev->irq;
+ shost->base = io_port;
+ shost->unique_id = io_port;
+ shost->last_reset = jiffies;
+
+ pACB->pScsiHost = shost;
+ pACB->IOPortBase = (u16) io_port;
+ pACB->IRQLevel = pdev->irq;
+
+ shost->max_id = 8;
+
+ if (shost->max_id - 1 ==
+ dc390_eepromBuf[dc390_adapterCnt][EE_ADAPT_SCSI_ID])
+ shost->max_id--;
+
+ if (dc390_eepromBuf[dc390_adapterCnt][EE_MODE2] & LUN_CHECK)
+ shost->max_lun = 8;
+ else
+ shost->max_lun = 1;
+
+ pACB->pFreeSRB = pACB->SRB_array;
+ pACB->SRBCount = MAX_SRB_CNT;
+ pACB->AdapterIndex = dc390_adapterCnt;
+ pACB->TagMaxNum =
+ 2 << dc390_eepromBuf[dc390_adapterCnt][EE_TAG_CMD_NUM];
+ pACB->Gmode2 = dc390_eepromBuf[dc390_adapterCnt][EE_MODE2];
+
+ for (i = 0; i < pACB->SRBCount-1; i++)
+ pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
+ pACB->SRB_array[pACB->SRBCount-1].pNextSRB = NULL;
+ pACB->pTmpSRB = &pACB->TmpSRB;
+
+ pACB->sel_timeout = SEL_TIMEOUT;
+ pACB->glitch_cfg = EATER_25NS;
+ pACB->pdev = pdev;
+
+ if (!request_region(io_port, shost->n_io_port, "tmscsim")) {
+ printk(KERN_ERR "DC390: register IO ports error!\n");
+ goto out_host_put;
+ }
+
+ /* Reset Pending INT */
+ DC390_read8_(INT_Status, io_port);
+
+ if (request_irq(pdev->irq, do_DC390_Interrupt, SA_SHIRQ,
+ "tmscsim", pACB)) {
+ printk(KERN_ERR "DC390: register IRQ error!\n");
+ goto out_release_region;
+ }
+
+ dc390_init_hw(pACB, dc390_adapterCnt);
+
+ dc390_adapterCnt++;
+
+ pci_set_drvdata(pdev, shost);
+
+ error = scsi_add_host(shost, &pdev->dev);
+ if (error)
+ goto out_free_irq;
+ scsi_scan_host(shost);
+ return 0;
+
+ out_free_irq:
+ free_irq(pdev->irq, pACB);
+ out_release_region:
+ release_region(io_port, shost->n_io_port);
+ out_host_put:
+ scsi_host_put(shost);
+ out_disable_device:
+ pci_disable_device(pdev);
+ out:
+ return error;
+}
+
+/**
+ * dc390_remove_one - Called to remove a single instance of the adapter.
+ *
+ * @dev: The PCI device to remove.
+ */
+static void __devexit dc390_remove_one(struct pci_dev *dev)
+{
+ struct Scsi_Host *scsi_host = pci_get_drvdata(dev);
+ unsigned long iflags;
+ struct dc390_acb* pACB = (struct dc390_acb*) scsi_host->hostdata;
+ u8 bval;
+
+ scsi_remove_host(scsi_host);
+
+ spin_lock_irqsave(scsi_host->host_lock, iflags);
+ pACB->ACBFlag = RESET_DEV;
+ bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST;
+ DC390_write8 (CtrlReg1, bval); /* disable interrupt */
+ if (pACB->Gmode2 & RST_SCSI_BUS)
+ dc390_ResetSCSIBus(pACB);
+ spin_unlock_irqrestore(scsi_host->host_lock, iflags);
+
+ free_irq(scsi_host->irq, pACB);
+ release_region(scsi_host->io_port, scsi_host->n_io_port);
+
+ pci_disable_device(dev);
+ scsi_host_put(scsi_host);
+ pci_set_drvdata(dev, NULL);
+}
+
+static struct pci_device_id tmscsim_pci_tbl[] = {
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, tmscsim_pci_tbl);
+
+static struct pci_driver dc390_driver = {
+ .name = "tmscsim",
+ .id_table = tmscsim_pci_tbl,
+ .probe = dc390_probe_one,
+ .remove = __devexit_p(dc390_remove_one),
+};
+
+static int __init dc390_module_init(void)
+{
+ if (tmscsim[0] == -1 || tmscsim[0] > 15) {
+ tmscsim[0] = 7;
+ tmscsim[1] = 4;
+ tmscsim[2] = PARITY_CHK_ | TAG_QUEUEING_;
+ tmscsim[3] = MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION;
+ tmscsim[4] = 2;
+ tmscsim[5] = 10;
+ printk (KERN_INFO "DC390: Using safe settings.\n");
+ }
+
+ return pci_module_init(&dc390_driver);
+}
+
+static void __exit dc390_module_exit(void)
+{
+ pci_unregister_driver(&dc390_driver);
+}
+
+module_init(dc390_module_init);
+module_exit(dc390_module_exit);
+
+#ifndef MODULE
+static int __init dc390_setup (char *str)
+{
+ int ints[8],i, im;
+
+ get_options(str, ARRAY_SIZE(ints), ints);
+ im = ints[0];
+
+ if (im > 6) {
+ printk (KERN_NOTICE "DC390: ignore extra params!\n");
+ im = 6;
+ }
+
+ for (i = 0; i < im; i++)
+ tmscsim[i] = ints[i+1];
+ /* dc390_checkparams (); */
+ return 1;
+}
+
+__setup("tmscsim=", dc390_setup);
+#endif
diff --git a/drivers/scsi/tmscsim.h b/drivers/scsi/tmscsim.h
new file mode 100644
index 000000000000..d4495272fb40
--- /dev/null
+++ b/drivers/scsi/tmscsim.h
@@ -0,0 +1,565 @@
+/***********************************************************************
+;* File Name : TMSCSIM.H *
+;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter *
+;* Device Driver *
+;***********************************************************************/
+/* $Id: tmscsim.h,v 2.15.2.3 2000/11/17 20:52:27 garloff Exp $ */
+
+#ifndef _TMSCSIM_H
+#define _TMSCSIM_H
+
+#include <linux/types.h>
+#include <linux/config.h>
+
+#define SCSI_IRQ_NONE 255
+
+#define MAX_ADAPTER_NUM 4
+#define MAX_SG_LIST_BUF 16 /* Not used */
+#define MAX_SCSI_ID 8
+#define MAX_SRB_CNT 50 /* Max number of started commands */
+
+#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */
+
+#define pci_dma_lo32(a) (a & 0xffffffff)
+
+typedef u8 UCHAR; /* 8 bits */
+typedef u16 USHORT; /* 16 bits */
+typedef u32 UINT; /* 32 bits */
+typedef unsigned long ULONG; /* 32/64 bits */
+
+
+/*
+;-----------------------------------------------------------------------
+; SCSI Request Block
+;-----------------------------------------------------------------------
+*/
+struct dc390_srb
+{
+//u8 CmdBlock[12];
+
+struct dc390_srb *pNextSRB;
+struct dc390_dcb *pSRBDCB;
+struct scsi_cmnd *pcmd;
+struct scatterlist *pSegmentList;
+
+struct scatterlist Segmentx; /* make a one entry of S/G list table */
+
+unsigned long SGBusAddr; /*;a segment starting address as seen by AM53C974A*/
+unsigned long SGToBeXferLen; /*; to be xfer length */
+unsigned long TotalXferredLen;
+unsigned long SavedTotXLen;
+unsigned long Saved_Ptr;
+u32 SRBState;
+
+u8 SRBStatus;
+u8 SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */
+ /*; b4-settimeout,b5-Residual valid */
+u8 AdaptStatus;
+u8 TargetStatus;
+
+u8 ScsiPhase;
+s8 TagNumber;
+u8 SGIndex;
+u8 SGcount;
+
+u8 MsgCnt;
+u8 EndMessage;
+u8 SavedSGCount;
+
+u8 MsgInBuf[6];
+u8 MsgOutBuf[6];
+
+//u8 IORBFlag; /*;81h-Reset, 2-retry */
+};
+
+
+/*
+;-----------------------------------------------------------------------
+; Device Control Block
+;-----------------------------------------------------------------------
+*/
+struct dc390_dcb
+{
+struct dc390_dcb *pNextDCB;
+struct dc390_acb *pDCBACB;
+
+/* Queued SRBs */
+struct dc390_srb *pGoingSRB;
+struct dc390_srb *pGoingLast;
+struct dc390_srb *pActiveSRB;
+u8 GoingSRBCnt;
+
+u32 TagMask;
+
+u8 TargetID; /*; SCSI Target ID (SCSI Only) */
+u8 TargetLUN; /*; SCSI Log. Unit (SCSI Only) */
+u8 DevMode;
+u8 DCBFlag;
+
+u8 CtrlR1;
+u8 CtrlR3;
+u8 CtrlR4;
+
+u8 SyncMode; /*; 0:async mode */
+u8 NegoPeriod; /*;for nego. */
+u8 SyncPeriod; /*;for reg. */
+u8 SyncOffset; /*;for reg. and nego.(low nibble) */
+};
+
+
+/*
+;-----------------------------------------------------------------------
+; Adapter Control Block
+;-----------------------------------------------------------------------
+*/
+struct dc390_acb
+{
+struct Scsi_Host *pScsiHost;
+u16 IOPortBase;
+u8 IRQLevel;
+u8 status;
+
+u8 SRBCount;
+u8 AdapterIndex; /*; nth Adapter this driver */
+u8 DCBCnt;
+
+u8 TagMaxNum;
+u8 ACBFlag;
+u8 Gmode2;
+u8 scan_devices;
+
+struct dc390_dcb *pLinkDCB;
+struct dc390_dcb *pLastDCB;
+struct dc390_dcb *pDCBRunRobin;
+
+struct dc390_dcb *pActiveDCB;
+struct dc390_srb *pFreeSRB;
+struct dc390_srb *pTmpSRB;
+
+u8 msgin123[4];
+u8 Connected;
+u8 pad;
+
+#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(CONFIG_SMP) || DEBUG_SPINLOCKS > 0)
+spinlock_t lock;
+#endif
+u8 sel_timeout;
+u8 glitch_cfg;
+
+u8 MsgLen;
+u8 Ignore_IRQ; /* Not used */
+
+struct pci_dev *pdev;
+
+unsigned long Cmds;
+u32 SelLost;
+u32 SelConn;
+u32 CmdInQ;
+u32 CmdOutOfSRB;
+
+struct dc390_srb TmpSRB;
+struct dc390_srb SRB_array[MAX_SRB_CNT]; /* 50 SRBs */
+};
+
+
+/*;-----------------------------------------------------------------------*/
+
+
+#define BIT31 0x80000000
+#define BIT30 0x40000000
+#define BIT29 0x20000000
+#define BIT28 0x10000000
+#define BIT27 0x08000000
+#define BIT26 0x04000000
+#define BIT25 0x02000000
+#define BIT24 0x01000000
+#define BIT23 0x00800000
+#define BIT22 0x00400000
+#define BIT21 0x00200000
+#define BIT20 0x00100000
+#define BIT19 0x00080000
+#define BIT18 0x00040000
+#define BIT17 0x00020000
+#define BIT16 0x00010000
+#define BIT15 0x00008000
+#define BIT14 0x00004000
+#define BIT13 0x00002000
+#define BIT12 0x00001000
+#define BIT11 0x00000800
+#define BIT10 0x00000400
+#define BIT9 0x00000200
+#define BIT8 0x00000100
+#define BIT7 0x00000080
+#define BIT6 0x00000040
+#define BIT5 0x00000020
+#define BIT4 0x00000010
+#define BIT3 0x00000008
+#define BIT2 0x00000004
+#define BIT1 0x00000002
+#define BIT0 0x00000001
+
+/*;---UnitCtrlFlag */
+#define UNIT_ALLOCATED BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY BIT3
+
+/*;---UnitFlags */
+#define DASD_SUPPORT BIT0
+#define SCSI_SUPPORT BIT1
+#define ASPI_SUPPORT BIT2
+
+/*;----SRBState machine definition */
+#define SRB_FREE 0
+#define SRB_WAIT BIT0
+#define SRB_READY BIT1
+#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/
+#define SRB_MSGIN BIT3
+#define SRB_MSGIN_MULTI BIT4
+#define SRB_COMMAND BIT5
+#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/
+#define SRB_DISCONNECT BIT7
+#define SRB_DATA_XFER BIT8
+#define SRB_XFERPAD BIT9
+#define SRB_STATUS BIT10
+#define SRB_COMPLETED BIT11
+#define SRB_ABORT_SENT BIT12
+#define DO_SYNC_NEGO BIT13
+#define SRB_UNEXPECT_RESEL BIT14
+
+/*;---SRBstatus */
+#define SRB_OK BIT0
+#define ABORTION BIT1
+#define OVER_RUN BIT2
+#define UNDER_RUN BIT3
+#define PARITY_ERROR BIT4
+#define SRB_ERROR BIT5
+
+/*;---ACBFlag */
+#define RESET_DEV BIT0
+#define RESET_DETECT BIT1
+#define RESET_DONE BIT2
+
+/*;---DCBFlag */
+#define ABORT_DEV_ BIT0
+
+/*;---SRBFlag */
+#define DATAOUT BIT7
+#define DATAIN BIT6
+#define RESIDUAL_VALID BIT5
+#define ENABLE_TIMER BIT4
+#define RESET_DEV0 BIT2
+#define ABORT_DEV BIT1
+#define AUTO_REQSENSE BIT0
+
+/*;---Adapter status */
+#define H_STATUS_GOOD 0
+#define H_SEL_TIMEOUT 0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD 0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB 0x19
+#define H_BAD_CCB_OR_SG 0x1A
+#define H_ABORT 0x0FF
+
+/*; SCSI Status byte codes*/
+/* The values defined in include/scsi/scsi.h, to be shifted << 1 */
+
+#define SCSI_STAT_UNEXP_BUS_F 0xFD /*; Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE /*; Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT 0xFF /*; Selection Time out */
+
+/* cmd->result */
+#define RES_TARGET 0x000000FF /* Target State */
+#define RES_TARGET_LNX STATUS_MASK /* Only official ... */
+#define RES_ENDMSG 0x0000FF00 /* End Message */
+#define RES_DID 0x00FF0000 /* DID_ codes */
+#define RES_DRV 0xFF000000 /* DRIVER_ codes */
+
+#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt))
+#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1)
+
+#define SET_RES_TARGET(who, tgt) do { who &= ~RES_TARGET; who |= (int)(tgt); } while (0)
+#define SET_RES_TARGET_LNX(who, tgt) do { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; } while (0)
+#define SET_RES_MSG(who, msg) do { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; } while (0)
+#define SET_RES_DID(who, did) do { who &= ~RES_DID; who |= (int)(did) << 16; } while (0)
+#define SET_RES_DRV(who, drv) do { who &= ~RES_DRV; who |= (int)(drv) << 24; } while (0)
+
+/*;---Sync_Mode */
+#define SYNC_DISABLE 0
+#define SYNC_ENABLE BIT0
+#define SYNC_NEGO_DONE BIT1
+#define WIDE_ENABLE BIT2 /* Not used ;-) */
+#define WIDE_NEGO_DONE BIT3 /* Not used ;-) */
+#define EN_TAG_QUEUEING BIT4
+#define EN_ATN_STOP BIT5
+
+#define SYNC_NEGO_OFFSET 15
+
+/*;---SCSI bus phase*/
+#define SCSI_DATA_OUT 0
+#define SCSI_DATA_IN 1
+#define SCSI_COMMAND 2
+#define SCSI_STATUS_ 3
+#define SCSI_NOP0 4
+#define SCSI_NOP1 5
+#define SCSI_MSG_OUT 6
+#define SCSI_MSG_IN 7
+
+/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */ /* One is missing ! */
+#define ABORT_TAG 0x0d
+
+/*
+ * SISC query queue
+ */
+typedef struct {
+ dma_addr_t saved_dma_handle;
+} dc390_cmd_scp_t;
+
+/*
+;==========================================================
+; EEPROM byte offset
+;==========================================================
+*/
+typedef struct _EEprom
+{
+u8 EE_MODE1;
+u8 EE_SPEED;
+u8 xx1;
+u8 xx2;
+} EEprom, *PEEprom;
+
+#define REAL_EE_ADAPT_SCSI_ID 64
+#define REAL_EE_MODE2 65
+#define REAL_EE_DELAY 66
+#define REAL_EE_TAG_CMD_NUM 67
+
+#define EE_ADAPT_SCSI_ID 32
+#define EE_MODE2 33
+#define EE_DELAY 34
+#define EE_TAG_CMD_NUM 35
+
+#define EE_LEN 40
+
+/*; EE_MODE1 bits definition*/
+#define PARITY_CHK_ BIT0
+#define SYNC_NEGO_ BIT1
+#define EN_DISCONNECT_ BIT2
+#define SEND_START_ BIT3
+#define TAG_QUEUEING_ BIT4
+
+/*; EE_MODE2 bits definition*/
+#define MORE2_DRV BIT0
+#define GREATER_1G BIT1
+#define RST_SCSI_BUS BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK BIT4
+#define LUN_CHECK BIT5
+
+#define ENABLE_CE 1
+#define DISABLE_CE 0
+#define EEPROM_READ 0x80
+
+/*
+;==========================================================
+; AMD 53C974 Registers bit Definition
+;==========================================================
+*/
+/*
+;====================
+; SCSI Register
+;====================
+*/
+
+/*; Command Reg.(+0CH) (rw) */
+#define DMA_COMMAND BIT7
+#define NOP_CMD 0
+#define CLEAR_FIFO_CMD 1
+#define RST_DEVICE_CMD 2
+#define RST_SCSI_BUS_CMD 3
+
+#define INFO_XFER_CMD 0x10
+#define INITIATOR_CMD_CMPLTE 0x11
+#define MSG_ACCEPTED_CMD 0x12
+#define XFER_PAD_BYTE 0x18
+#define SET_ATN_CMD 0x1A
+#define RESET_ATN_CMD 0x1B
+
+#define SEL_WO_ATN 0x41 /* currently not used */
+#define SEL_W_ATN 0x42
+#define SEL_W_ATN_STOP 0x43
+#define SEL_W_ATN3 0x46
+#define EN_SEL_RESEL 0x44
+#define DIS_SEL_RESEL 0x45 /* currently not used */
+#define RESEL 0x40 /* " */
+#define RESEL_ATN3 0x47 /* " */
+
+#define DATA_XFER_CMD INFO_XFER_CMD
+
+
+/*; SCSI Status Reg.(+10H) (r) */
+#define INTERRUPT BIT7
+#define ILLEGAL_OP_ERR BIT6
+#define PARITY_ERR BIT5
+#define COUNT_2_ZERO BIT4
+#define GROUP_CODE_VALID BIT3
+#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0)
+/* BIT2: MSG phase; BIT1: C/D physe; BIT0: I/O phase */
+
+/*; Interrupt Status Reg.(+14H) (r) */
+#define SCSI_RESET BIT7
+#define INVALID_CMD BIT6
+#define DISCONNECTED BIT5
+#define SERVICE_REQUEST BIT4
+#define SUCCESSFUL_OP BIT3
+#define RESELECTED BIT2
+#define SEL_ATTENTION BIT1
+#define SELECTED BIT0
+
+/*; Internal State Reg.(+18H) (r) */
+#define SYNC_OFFSET_FLAG BIT3
+#define INTRN_STATE_MASK (BIT2+BIT1+BIT0)
+/* 0x04: Sel. successful (w/o stop), 0x01: Sel. successful (w/ stop) */
+
+/*; Clock Factor Reg.(+24H) (w) */
+#define CLK_FREQ_40MHZ 0
+#define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0)
+#define CLK_FREQ_30MHZ (BIT2+BIT1)
+#define CLK_FREQ_25MHZ (BIT2+BIT0)
+#define CLK_FREQ_20MHZ BIT2
+#define CLK_FREQ_15MHZ (BIT1+BIT0)
+#define CLK_FREQ_10MHZ BIT1
+
+/*; Control Reg. 1(+20H) (rw) */
+#define EXTENDED_TIMING BIT7
+#define DIS_INT_ON_SCSI_RST BIT6
+#define PARITY_ERR_REPO BIT4
+#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0) /* host adapter ID */
+
+/*; Control Reg. 2(+2CH) (rw) */
+#define EN_FEATURE BIT6
+#define EN_SCSI2_CMD BIT3
+
+/*; Control Reg. 3(+30H) (rw) */
+#define ID_MSG_CHECK BIT7
+#define EN_QTAG_MSG BIT6
+#define EN_GRP2_CMD BIT5
+#define FAST_SCSI BIT4 /* ;10MB/SEC */
+#define FAST_CLK BIT3 /* ;25 - 40 MHZ */
+
+/*; Control Reg. 4(+34H) (rw) */
+#define EATER_12NS 0
+#define EATER_25NS BIT7
+#define EATER_35NS BIT6
+#define EATER_0NS (BIT7+BIT6)
+#define REDUCED_POWER BIT5
+#define CTRL4_RESERVED BIT4 /* must be 1 acc. to AM53C974.c */
+#define NEGATE_REQACKDATA BIT2
+#define NEGATE_REQACK BIT3
+
+#define GLITCH_TO_NS(x) (((~x>>6 & 2) >> 1) | ((x>>6 & 1) << 1 ^ (x>>6 & 2)))
+#define NS_TO_GLITCH(y) (((~y<<7) | ~((y<<6) ^ ((y<<5 & 1<<6) | ~0x40))) & 0xc0)
+
+/*
+;====================
+; DMA Register
+;====================
+*/
+/*; DMA Command Reg.(+40H) (rw) */
+#define READ_DIRECTION BIT7
+#define WRITE_DIRECTION 0
+#define EN_DMA_INT BIT6
+#define EN_PAGE_INT BIT5 /* page transfer interrupt enable */
+#define MAP_TO_MDL BIT4
+#define DIAGNOSTIC BIT2
+#define DMA_IDLE_CMD 0
+#define DMA_BLAST_CMD BIT0
+#define DMA_ABORT_CMD BIT1
+#define DMA_START_CMD (BIT1+BIT0)
+
+/*; DMA Status Reg.(+54H) (r) */
+#define PCI_MS_ABORT BIT6
+#define BLAST_COMPLETE BIT5
+#define SCSI_INTERRUPT BIT4
+#define DMA_XFER_DONE BIT3
+#define DMA_XFER_ABORT BIT2
+#define DMA_XFER_ERROR BIT1
+#define POWER_DOWN BIT0
+
+/*; DMA SCSI Bus and Ctrl.(+70H) */
+#define EN_INT_ON_PCI_ABORT BIT25
+#define WRT_ERASE_DMA_STAT BIT24
+#define PW_DOWN_CTRL BIT21
+#define SCSI_BUSY BIT20
+#define SCLK BIT19
+#define SCAM BIT18
+#define SCSI_LINES 0x0003ffff
+
+/*
+;==========================================================
+; SCSI Chip register address offset
+;==========================================================
+;Registers are rw unless declared otherwise
+*/
+#define CtcReg_Low 0x00 /* r curr. transfer count */
+#define CtcReg_Mid 0x04 /* r */
+#define CtcReg_High 0x38 /* r */
+#define ScsiFifo 0x08
+#define ScsiCmd 0x0C
+#define Scsi_Status 0x10 /* r */
+#define INT_Status 0x14 /* r */
+#define Sync_Period 0x18 /* w */
+#define Sync_Offset 0x1C /* w */
+#define Clk_Factor 0x24 /* w */
+#define CtrlReg1 0x20
+#define CtrlReg2 0x2C
+#define CtrlReg3 0x30
+#define CtrlReg4 0x34
+#define DMA_Cmd 0x40
+#define DMA_XferCnt 0x44 /* rw starting transfer count (32 bit) */
+#define DMA_XferAddr 0x48 /* rw starting physical address (32 bit) */
+#define DMA_Wk_ByteCntr 0x4C /* r working byte counter */
+#define DMA_Wk_AddrCntr 0x50 /* r working address counter */
+#define DMA_Status 0x54 /* r */
+#define DMA_MDL_Addr 0x58 /* rw starting MDL address */
+#define DMA_Wk_MDL_Cntr 0x5C /* r working MDL counter */
+#define DMA_ScsiBusCtrl 0x70 /* rw SCSI Bus, PCI/DMA Ctrl */
+
+#define StcReg_Low CtcReg_Low /* w start transfer count */
+#define StcReg_Mid CtcReg_Mid /* w */
+#define StcReg_High CtcReg_High /* w */
+#define Scsi_Dest_ID Scsi_Status /* w */
+#define Scsi_TimeOut INT_Status /* w */
+#define Intern_State Sync_Period /* r */
+#define Current_Fifo Sync_Offset /* r Curr. FIFO / int. state */
+
+
+#define DC390_read8(address) \
+ (inb (pACB->IOPortBase + (address)))
+
+#define DC390_read8_(address, base) \
+ (inb ((u16)(base) + (address)))
+
+#define DC390_read16(address) \
+ (inw (pACB->IOPortBase + (address)))
+
+#define DC390_read32(address) \
+ (inl (pACB->IOPortBase + (address)))
+
+#define DC390_write8(address,value) \
+ outb ((value), pACB->IOPortBase + (address))
+
+#define DC390_write8_(address,value,base) \
+ outb ((value), (u16)(base) + (address))
+
+#define DC390_write16(address,value) \
+ outw ((value), pACB->IOPortBase + (address))
+
+#define DC390_write32(address,value) \
+ outl ((value), pACB->IOPortBase + (address))
+
+
+#endif /* _TMSCSIM_H */
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
new file mode 100644
index 000000000000..dca215411f68
--- /dev/null
+++ b/drivers/scsi/u14-34f.c
@@ -0,0 +1,1987 @@
+/*
+ * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
+ *
+ * 03 Jun 2003 Rev. 8.10 for linux-2.5.70
+ * + Update for new IRQ API.
+ * + Use "goto" when appropriate.
+ * + Drop u14-34f.h.
+ * + Update for new module_param API.
+ * + Module parameters can now be specified only in the
+ * same format as the kernel boot options.
+ *
+ * boot option old module param
+ * ----------- ------------------
+ * addr,... io_port=addr,...
+ * lc:[y|n] linked_comm=[1|0]
+ * mq:xx max_queue_depth=xx
+ * tm:[0|1|2] tag_mode=[0|1|2]
+ * et:[y|n] ext_tran=[1|0]
+ * of:[y|n] have_old_firmware=[1|0]
+ *
+ * A valid example using the new parameter format is:
+ * modprobe u14-34f "u14-34f=0x340,0x330,lc:y,tm:0,mq:4"
+ *
+ * which is equivalent to the old format:
+ * modprobe u14-34f io_port=0x340,0x330 linked_comm=1 tag_mode=0 \
+ * max_queue_depth=4
+ *
+ * With actual module code, u14-34f and u14_34f are equivalent
+ * as module parameter names.
+ *
+ * 12 Feb 2003 Rev. 8.04 for linux 2.5.60
+ * + Release irq before calling scsi_register.
+ *
+ * 12 Nov 2002 Rev. 8.02 for linux 2.5.47
+ * + Release driver_lock before calling scsi_register.
+ *
+ * 11 Nov 2002 Rev. 8.01 for linux 2.5.47
+ * + Fixed bios_param and scsicam_bios_param calling parameters.
+ *
+ * 28 Oct 2002 Rev. 8.00 for linux 2.5.44-ac4
+ * + Use new tcq and adjust_queue_depth api.
+ * + New command line option (tm:[0-2]) to choose the type of tags:
+ * 0 -> disable tagging ; 1 -> simple tags ; 2 -> ordered tags.
+ * Default is tm:0 (tagged commands disabled).
+ * For compatibility the "tc:" option is an alias of the "tm:"
+ * option; tc:n is equivalent to tm:0 and tc:y is equivalent to
+ * tm:1.
+ *
+ * 10 Oct 2002 Rev. 7.70 for linux 2.5.42
+ * + Foreport from revision 6.70.
+ *
+ * 25 Jun 2002 Rev. 6.70 for linux 2.4.19
+ * + Fixed endian-ness problem due to bitfields.
+ *
+ * 21 Feb 2002 Rev. 6.52 for linux 2.4.18
+ * + Backport from rev. 7.22 (use io_request_lock).
+ *
+ * 20 Feb 2002 Rev. 7.22 for linux 2.5.5
+ * + Remove any reference to virt_to_bus().
+ * + Fix pio hang while detecting multiple HBAs.
+ *
+ * 01 Jan 2002 Rev. 7.20 for linux 2.5.1
+ * + Use the dynamic DMA mapping API.
+ *
+ * 19 Dec 2001 Rev. 7.02 for linux 2.5.1
+ * + Use SCpnt->sc_data_direction if set.
+ * + Use sglist.page instead of sglist.address.
+ *
+ * 11 Dec 2001 Rev. 7.00 for linux 2.5.1
+ * + Use host->host_lock instead of io_request_lock.
+ *
+ * 1 May 2001 Rev. 6.05 for linux 2.4.4
+ * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d)
+ *
+ * 25 Jan 2001 Rev. 6.03 for linux 2.4.0
+ * + "check_region" call replaced by "request_region".
+ *
+ * 22 Nov 2000 Rev. 6.02 for linux 2.4.0-test11
+ * + Removed old scsi error handling support.
+ * + The obsolete boot option flag eh:n is silently ignored.
+ * + Removed error messages while a disk drive is powered up at
+ * boot time.
+ * + Improved boot messages: all tagged capable device are
+ * indicated as "tagged".
+ *
+ * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
+ * + Updated to the new __setup interface for boot command line options.
+ * + When loaded as a module, accepts the new parameter boot_options
+ * which value is a string with the same format of the kernel boot
+ * command line options. A valid example is:
+ * modprobe u14-34f 'boot_options="0x230,0x340,lc:y,mq:4"'
+ *
+ * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11
+ * + Removed pre-2.2 source code compatibility.
+ *
+ * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
+ * Added command line option (et:[y|n]) to use the existing
+ * translation (returned by scsicam_bios_param) as disk geometry.
+ * The default is et:n, which uses the disk geometry jumpered
+ * on the board.
+ * The default value et:n is compatible with all previous revisions
+ * of this driver.
+ *
+ * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
+ * Increased busy timeout from 10 msec. to 200 msec. while
+ * processing interrupts.
+ *
+ * 18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
+ * Improved abort handling during the eh recovery process.
+ *
+ * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101
+ * The driver is now fully SMP safe, including the
+ * abort and reset routines.
+ * Added command line options (eh:[y|n]) to choose between
+ * new_eh_code and the old scsi code.
+ * If linux version >= 2.1.101 the default is eh:y, while the eh
+ * option is ignored for previous releases and the old scsi code
+ * is used.
+ *
+ * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
+ * Reworked interrupt handler.
+ *
+ * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
+ * Major reliability improvement: when a batch with overlapping
+ * requests is detected, requests are queued one at a time
+ * eliminating any possible board or drive reordering.
+ *
+ * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
+ * Improved SMP support (if linux version >= 2.1.95).
+ *
+ * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94
+ * Performance improvement: when sequential i/o is detected,
+ * always use direct sort instead of reverse sort.
+ *
+ * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ * io_port is now unsigned long.
+ *
+ * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ * Use new scsi error handling code (if linux version >= 2.1.88).
+ * Use new interrupt code.
+ *
+ * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
+ * Use of udelay inside the wait loops to avoid timeout
+ * problems with fast cpus.
+ * Removed check about useless calls to the interrupt service
+ * routine (reported on SMP systems only).
+ * At initialization time "sorted/unsorted" is displayed instead
+ * of "linked/unlinked" to reinforce the fact that "linking" is
+ * nothing but "elevator sorting" in the actual implementation.
+ *
+ * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ * Use of serial_number_at_timeout in abort and reset processing.
+ * Use of the __initfunc and __initdata macro in setup code.
+ * Minor cleanups in the list_statistics code.
+ *
+ * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ * When loading as a module, parameter passing is now supported
+ * both in 2.0 and in 2.1 style.
+ * Fixed data transfer direction for some SCSI opcodes.
+ * Immediate acknowledge to request sense commands.
+ * Linked commands to each disk device are now reordered by elevator
+ * sorting. Rare cases in which reordering of write requests could
+ * cause wrong results are managed.
+ *
+ * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ * Added command line options to enable/disable linked commands
+ * (lc:[y|n]), old firmware support (of:[y|n]) and to set the max
+ * queue depth (mq:xx). Default is "u14-34f=lc:n,of:n,mq:8".
+ * Improved command linking.
+ *
+ * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
+ * Added linked command support.
+ *
+ * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ * Added queue depth adjustment.
+ *
+ * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
+ * The list of i/o ports to be probed can be overwritten by the
+ * "u14-34f=port0,port1,...." boot command line option.
+ * Scatter/gather lists are now allocated by a number of kmalloc
+ * calls, in order to avoid the previous size limit of 64Kb.
+ *
+ * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25
+ * Added multichannel support.
+ *
+ * 27 Sep 1996 rev. 2.12 for linux 2.1.0
+ * Portability cleanups (virtual/bus addressing, little/big endian
+ * support).
+ *
+ * 09 Jul 1996 rev. 2.11 for linux 2.0.4
+ * "Data over/under-run" no longer implies a redo on all targets.
+ * Number of internal retries is now limited.
+ *
+ * 16 Apr 1996 rev. 2.10 for linux 1.3.90
+ * New argument "reset_flags" to the reset routine.
+ *
+ * 21 Jul 1995 rev. 2.02 for linux 1.3.11
+ * Fixed Data Transfer Direction for some SCSI commands.
+ *
+ * 13 Jun 1995 rev. 2.01 for linux 1.2.10
+ * HAVE_OLD_UX4F_FIRMWARE should be defined for U34F boards when
+ * the firmware prom is not the latest one (28008-006).
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * U14F qualified to run with 32 sglists.
+ * Now DEBUG_RESET is disabled by default.
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ * Auto detects if U14F boards have an old firmware revision.
+ * Max number of scatter/gather lists set to 16 for all boards
+ * (most installation run fine using 33 sglists, while other
+ * has problems when using more than 16).
+ *
+ * 16 Jan 1995 rev. 1.13 for linux 1.1.81
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 15 Dec 1994 rev. 1.12 for linux 1.1.74
+ * The host->block flag is set for all the detected ISA boards.
+ *
+ * 30 Nov 1994 rev. 1.11 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
+ *
+ * 14 Nov 1994 rev. 1.10 for linux 1.1.63
+ *
+ * 28 Oct 1994 rev. 1.09 for linux 1.1.58 Final BETA release.
+ * 16 Jul 1994 rev. 1.00 for linux 1.1.29 Initial ALPHA release.
+ *
+ * This driver is a total replacement of the original UltraStor
+ * scsi driver, but it supports ONLY the 14F and 34F boards.
+ * It can be configured in the same kernel in which the original
+ * ultrastor driver is configured to allow the original U24F
+ * support.
+ *
+ * Multiple U14F and/or U34F host adapters are supported.
+ *
+ * Copyright (C) 1994-2003 Dario Ballabio (ballabio_dario@emc.com)
+ *
+ * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * WARNING: if your 14/34F board has an old firmware revision (see below)
+ * you must change "#undef" into "#define" in the following
+ * statement.
+ */
+#undef HAVE_OLD_UX4F_FIRMWARE
+/*
+ * The UltraStor 14F, 24F, and 34F are a family of intelligent, high
+ * performance SCSI-2 host adapters.
+ * Here is the scoop on the various models:
+ *
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VESA Local-Bus Bus Master HA (no WD1003 emulation).
+ *
+ * This code has been tested with up to two U14F boards, using both
+ * firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
+ * 28004-006/38004-005 (BIOS rev. 2.01).
+ *
+ * The latest firmware is required in order to get reliable operations when
+ * clustering is enabled. ENABLE_CLUSTERING provides a performance increase
+ * up to 50% on sequential access.
+ *
+ * Since the Scsi_Host_Template structure is shared among all 14F and 34F,
+ * the last setting of use_clustering is in effect for all of these boards.
+ *
+ * Here a sample configuration using two U14F boards:
+ *
+ U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, of:n, lc:y, mq:8.
+ U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, of:n, lc:y, mq:8.
+ *
+ * The boot controller must have its BIOS enabled, while other boards can
+ * have their BIOS disabled, or enabled to an higher address.
+ * Boards are named Ux4F0, Ux4F1..., according to the port address order in
+ * the io_port[] array.
+ *
+ * The following facts are based on real testing results (not on
+ * documentation) on the above U14F board.
+ *
+ * - The U14F board should be jumpered for bus on time less or equal to 7
+ * microseconds, while the default is 11 microseconds. This is order to
+ * get acceptable performance while using floppy drive and hard disk
+ * together. The jumpering for 7 microseconds is: JP13 pin 15-16,
+ * JP14 pin 7-8 and pin 9-10.
+ * The reduction has a little impact on scsi performance.
+ *
+ * - If scsi bus length exceeds 3m., the scsi bus speed needs to be reduced
+ * from 10Mhz to 5Mhz (do this by inserting a jumper on JP13 pin 7-8).
+ *
+ * - If U14F on board firmware is older than 28004-006/38004-005,
+ * the U14F board is unable to provide reliable operations if the scsi
+ * request length exceeds 16Kbyte. When this length is exceeded the
+ * behavior is:
+ * - adapter_status equal 0x96 or 0xa3 or 0x93 or 0x94;
+ * - adapter_status equal 0 and target_status equal 2 on for all targets
+ * in the next operation following the reset.
+ * This sequence takes a long time (>3 seconds), so in the meantime
+ * the SD_TIMEOUT in sd.c could expire giving rise to scsi aborts
+ * (SD_TIMEOUT has been increased from 3 to 6 seconds in 1.1.31).
+ * Because of this I had to DISABLE_CLUSTERING and to work around the
+ * bus reset in the interrupt service routine, returning DID_BUS_BUSY
+ * so that the operations are retried without complains from the scsi.c
+ * code.
+ * Any reset of the scsi bus is going to kill tape operations, since
+ * no retry is allowed for tapes. Bus resets are more likely when the
+ * scsi bus is under heavy load.
+ * Requests using scatter/gather have a maximum length of 16 x 1024 bytes
+ * when DISABLE_CLUSTERING is in effect, but unscattered requests could be
+ * larger than 16Kbyte.
+ *
+ * The new firmware has fixed all the above problems.
+ *
+ * For U34F boards the latest bios prom is 38008-002 (BIOS rev. 2.01),
+ * the latest firmware prom is 28008-006. Older firmware 28008-005 has
+ * problems when using more than 16 scatter/gather lists.
+ *
+ * The list of i/o ports to be probed can be totally replaced by the
+ * boot command line option: "u14-34f=port0,port1,port2,...", where the
+ * port0, port1... arguments are ISA/VESA addresses to be probed.
+ * For example using "u14-34f=0x230,0x340", the driver probes only the two
+ * addresses 0x230 and 0x340 in this order; "u14-34f=0" totally disables
+ * this driver.
+ *
+ * After the optional list of detection probes, other possible command line
+ * options are:
+ *
+ * et:y use disk geometry returned by scsicam_bios_param;
+ * et:n use disk geometry jumpered on the board;
+ * lc:y enables linked commands;
+ * lc:n disables linked commands;
+ * tm:0 disables tagged commands (same as tc:n);
+ * tm:1 use simple queue tags (same as tc:y);
+ * tm:2 use ordered queue tags (same as tc:2);
+ * of:y enables old firmware support;
+ * of:n disables old firmware support;
+ * mq:xx set the max queue depth to the value xx (2 <= xx <= 8).
+ *
+ * The default value is: "u14-34f=lc:n,of:n,mq:8,tm:0,et:n".
+ * An example using the list of detection probes could be:
+ * "u14-34f=0x230,0x340,lc:y,tm:2,of:n,mq:4,et:n".
+ *
+ * When loading as a module, parameters can be specified as well.
+ * The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ * modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \
+ * max_queue_depth=4 ext_tran=0 tag_mode=2
+ *
+ * ----------------------------------------------------------------------------
+ * In this implementation, linked commands are designed to work with any DISK
+ * or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ * and reordering by elevator sorting commands directed to each device,
+ * without any relation with the actual SCSI protocol between the controller
+ * and the device.
+ * If Q is the queue depth reported at boot time for each device (also named
+ * cmds/lun) and Q > 2, whenever there is already an active command to the
+ * device all other commands to the same device (up to Q-1) are kept waiting
+ * in the elevator sorting queue. When the active command completes, the
+ * commands in this queue are sorted by sector address. The sort is chosen
+ * between increasing or decreasing by minimizing the seek distance between
+ * the sector of the commands just completed and the sector of the first
+ * command in the list to be sorted.
+ * Trivial math assures that the unsorted average seek distance when doing
+ * random seeks over S sectors is S/3.
+ * When (Q-1) requests are uniformly distributed over S sectors, the average
+ * distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ * average seek distance for (Q-1) random requests over S sectors is S/Q.
+ * The elevator sorting hence divides the seek distance by a factor Q/3.
+ * The above pure geometric remarks are valid in all cases and the
+ * driver effectively reduces the seek distance by the predicted factor
+ * when there are Q concurrent read i/o operations on the device, but this
+ * does not necessarily results in a noticeable performance improvement:
+ * your mileage may vary....
+ *
+ * Note: command reordering inside a batch of queued commands could cause
+ * wrong results only if there is at least one write request and the
+ * intersection (sector-wise) of all requests is not empty.
+ * When the driver detects a batch including overlapping requests
+ * (a really rare event) strict serial (pid) order is enforced.
+ * ----------------------------------------------------------------------------
+ *
+ * The boards are named Ux4F0, Ux4F1,... according to the detection order.
+ *
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+
+static int u14_34f_detect(struct scsi_host_template *);
+static int u14_34f_release(struct Scsi_Host *);
+static int u14_34f_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
+static int u14_34f_eh_abort(struct scsi_cmnd *);
+static int u14_34f_eh_host_reset(struct scsi_cmnd *);
+static int u14_34f_bios_param(struct scsi_device *, struct block_device *,
+ sector_t, int *);
+static int u14_34f_slave_configure(struct scsi_device *);
+
+static struct scsi_host_template driver_template = {
+ .name = "UltraStor 14F/34F rev. 8.10.00 ",
+ .detect = u14_34f_detect,
+ .release = u14_34f_release,
+ .queuecommand = u14_34f_queuecommand,
+ .eh_abort_handler = u14_34f_eh_abort,
+ .eh_device_reset_handler = NULL,
+ .eh_bus_reset_handler = NULL,
+ .eh_host_reset_handler = u14_34f_eh_host_reset,
+ .bios_param = u14_34f_bios_param,
+ .slave_configure = u14_34f_slave_configure,
+ .this_id = 7,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING
+ };
+
+#if !defined(__BIG_ENDIAN_BITFIELD) && !defined(__LITTLE_ENDIAN_BITFIELD)
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+
+/* Values for the PRODUCT_ID ports for the 14/34F */
+#define PRODUCT_ID1 0x56
+#define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */
+
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#undef DEBUG_LINKED_COMMANDS
+#undef DEBUG_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_RESET
+#undef DEBUG_GENERATE_ERRORS
+#undef DEBUG_GENERATE_ABORTS
+#undef DEBUG_GEOMETRY
+
+#define MAX_ISA 3
+#define MAX_VESA 1
+#define MAX_EISA 0
+#define MAX_PCI 0
+#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
+#define MAX_CHANNEL 1
+#define MAX_LUN 8
+#define MAX_TARGET 8
+#define MAX_MAILBOXES 16
+#define MAX_SGLIST 32
+#define MAX_SAFE_SGLIST 16
+#define MAX_INTERNAL_RETRIES 64
+#define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
+
+#define SKIP ULONG_MAX
+#define FALSE 0
+#define TRUE 1
+#define FREE 0
+#define IN_USE 1
+#define LOCKED 2
+#define IN_RESET 3
+#define IGNORE 4
+#define READY 5
+#define ABORTING 6
+#define NO_DMA 0xff
+#define MAXLOOP 10000
+#define TAG_DISABLED 0
+#define TAG_SIMPLE 1
+#define TAG_ORDERED 2
+
+#define REG_LCL_MASK 0
+#define REG_LCL_INTR 1
+#define REG_SYS_MASK 2
+#define REG_SYS_INTR 3
+#define REG_PRODUCT_ID1 4
+#define REG_PRODUCT_ID2 5
+#define REG_CONFIG1 6
+#define REG_CONFIG2 7
+#define REG_OGM 8
+#define REG_ICM 12
+#define REGION_SIZE 13UL
+#define BSY_ASSERTED 0x01
+#define IRQ_ASSERTED 0x01
+#define CMD_RESET 0xc0
+#define CMD_OGM_INTR 0x01
+#define CMD_CLR_INTR 0x01
+#define CMD_ENA_INTR 0x81
+#define ASOK 0x00
+#define ASST 0x91
+
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
+
+#define PACKED __attribute__((packed))
+
+struct sg_list {
+ unsigned int address; /* Segment Address */
+ unsigned int num_bytes; /* Segment Length */
+ };
+
+/* MailBox SCSI Command Packet */
+struct mscp {
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned char sg:1, ca:1, dcn:1, xdir:2, opcode:3;
+ unsigned char lun: 3, channel:2, target:3;
+#else
+ unsigned char opcode: 3, /* type of command */
+ xdir: 2, /* data transfer direction */
+ dcn: 1, /* disable disconnect */
+ ca: 1, /* use cache (if available) */
+ sg: 1; /* scatter/gather operation */
+ unsigned char target: 3, /* SCSI target id */
+ channel: 2, /* SCSI channel number */
+ lun: 3; /* SCSI logical unit number */
+#endif
+
+ unsigned int data_address PACKED; /* transfer data pointer */
+ unsigned int data_len PACKED; /* length in bytes */
+ unsigned int link_address PACKED; /* for linking command chains */
+ unsigned char clink_id; /* identifies command in chain */
+ unsigned char use_sg; /* (if sg is set) 8 bytes per list */
+ unsigned char sense_len;
+ unsigned char cdb_len; /* 6, 10, or 12 */
+ unsigned char cdb[12]; /* SCSI Command Descriptor Block */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ unsigned int sense_addr PACKED;
+
+ /* Additional fields begin here. */
+ struct scsi_cmnd *SCpnt;
+ unsigned int cpp_index; /* cp index */
+
+ /* All the cp structure is zero filled by queuecommand except the
+ following CP_TAIL_SIZE bytes, initialized by detect */
+ dma_addr_t cp_dma_addr; /* dma handle for this cp structure */
+ struct sg_list *sglist; /* pointer to the allocated SG list */
+ };
+
+#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t))
+
+struct hostdata {
+ struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
+ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
+ unsigned int last_cp_used; /* Index of last mailbox used */
+ unsigned int iocount; /* Total i/o done for this board */
+ int board_number; /* Number of this board */
+ char board_name[16]; /* Name of this board */
+ int in_reset; /* True if board is doing a reset */
+ int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */
+ int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */
+ unsigned int retries; /* Number of internal retries */
+ unsigned long last_retried_pid; /* Pid of last retried command */
+ unsigned char subversion; /* Bus type, either ISA or ESA */
+ struct pci_dev *pdev; /* Always NULL */
+ unsigned char heads;
+ unsigned char sectors;
+ char board_id[256]; /* data from INQUIRY on this board */
+ };
+
+static struct Scsi_Host *sh[MAX_BOARDS + 1];
+static const char *driver_name = "Ux4F";
+static char sha[MAX_BOARDS];
+static DEFINE_SPINLOCK(driver_lock);
+
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] = {
+
+ /* Space for MAX_INT_PARAM ports usable while loading as a module */
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+ SKIP, SKIP,
+
+ /* Possible ISA/VESA ports */
+ 0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140,
+
+ /* End of list */
+ 0x0
+ };
+
+#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
+#define BN(board) (HD(board)->board_name)
+
+/* Device is Little Endian */
+#define H2DEV(x) cpu_to_le32(x)
+#define DEV2H(x) le32_to_cpu(x)
+
+static irqreturn_t do_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(struct scsi_device *, unsigned long, unsigned int, unsigned int);
+static int do_trace = FALSE;
+static int setup_done = FALSE;
+static int link_statistics;
+static int ext_tran = FALSE;
+
+#if defined(HAVE_OLD_UX4F_FIRMWARE)
+static int have_old_firmware = TRUE;
+#else
+static int have_old_firmware = FALSE;
+#endif
+
+#if defined(CONFIG_SCSI_U14_34F_TAGGED_QUEUE)
+static int tag_mode = TAG_SIMPLE;
+#else
+static int tag_mode = TAG_DISABLED;
+#endif
+
+#if defined(CONFIG_SCSI_U14_34F_LINKED_COMMANDS)
+static int linked_comm = TRUE;
+#else
+static int linked_comm = FALSE;
+#endif
+
+#if defined(CONFIG_SCSI_U14_34F_MAX_TAGS)
+static int max_queue_depth = CONFIG_SCSI_U14_34F_MAX_TAGS;
+#else
+static int max_queue_depth = MAX_CMD_PER_LUN;
+#endif
+
+#define MAX_INT_PARAM 10
+#define MAX_BOOT_OPTIONS_SIZE 256
+static char boot_options[MAX_BOOT_OPTIONS_SIZE];
+
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+module_param_string(u14_34f, boot_options, MAX_BOOT_OPTIONS_SIZE, 0);
+MODULE_PARM_DESC(u14_34f, " equivalent to the \"u14-34f=...\" kernel boot " \
+"option." \
+" Example: modprobe u14-34f \"u14_34f=0x340,0x330,lc:y,tm:0,mq:4\"");
+MODULE_AUTHOR("Dario Ballabio");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("UltraStor 14F/34F SCSI Driver");
+
+#endif
+
+static int u14_34f_slave_configure(struct scsi_device *dev) {
+ int j, tqd, utqd;
+ char *tag_suffix, *link_suffix;
+ struct Scsi_Host *host = dev->host;
+
+ j = ((struct hostdata *) host->hostdata)->board_number;
+
+ utqd = MAX_CMD_PER_LUN;
+ tqd = max_queue_depth;
+
+ if (TLDEV(dev->type) && dev->tagged_supported)
+
+ if (tag_mode == TAG_SIMPLE) {
+ scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd);
+ tag_suffix = ", simple tags";
+ }
+ else if (tag_mode == TAG_ORDERED) {
+ scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd);
+ tag_suffix = ", ordered tags";
+ }
+ else {
+ scsi_adjust_queue_depth(dev, 0, tqd);
+ tag_suffix = ", no tags";
+ }
+
+ else if (TLDEV(dev->type) && linked_comm) {
+ scsi_adjust_queue_depth(dev, 0, tqd);
+ tag_suffix = ", untagged";
+ }
+
+ else {
+ scsi_adjust_queue_depth(dev, 0, utqd);
+ tag_suffix = "";
+ }
+
+ if (TLDEV(dev->type) && linked_comm && dev->queue_depth > 2)
+ link_suffix = ", sorted";
+ else if (TLDEV(dev->type))
+ link_suffix = ", unsorted";
+ else
+ link_suffix = "";
+
+ printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
+ BN(j), host->host_no, dev->channel, dev->id, dev->lun,
+ dev->queue_depth, link_suffix, tag_suffix);
+
+ return FALSE;
+}
+
+static int wait_on_busy(unsigned long iobase, unsigned int loop) {
+
+ while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED) {
+ udelay(1L);
+ if (--loop == 0) return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int board_inquiry(unsigned int j) {
+ struct mscp *cpp;
+ dma_addr_t id_dma_addr;
+ unsigned int time, limit = 0;
+
+ id_dma_addr = pci_map_single(HD(j)->pdev, HD(j)->board_id,
+ sizeof(HD(j)->board_id), PCI_DMA_BIDIRECTIONAL);
+ cpp = &HD(j)->cp[0];
+ cpp->cp_dma_addr = pci_map_single(HD(j)->pdev, cpp, sizeof(struct mscp),
+ PCI_DMA_BIDIRECTIONAL);
+ memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE);
+ cpp->opcode = OP_HOST_ADAPTER;
+ cpp->xdir = DTD_IN;
+ cpp->data_address = H2DEV(id_dma_addr);
+ cpp->data_len = H2DEV(sizeof(HD(j)->board_id));
+ cpp->cdb_len = 6;
+ cpp->cdb[0] = HA_CMD_INQUIRY;
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: board_inquiry, adapter busy.\n", BN(j));
+ return TRUE;
+ }
+
+ HD(j)->cp_stat[0] = IGNORE;
+
+ /* Clear the interrupt indication */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ /* Store pointer in OGM address bytes */
+ outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ spin_unlock_irq(&driver_lock);
+ time = jiffies;
+ while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L);
+ spin_lock_irq(&driver_lock);
+
+ if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
+ HD(j)->cp_stat[0] = FREE;
+ printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status);
+ return TRUE;
+ }
+
+ pci_unmap_single(HD(j)->pdev, cpp->cp_dma_addr, sizeof(struct mscp),
+ PCI_DMA_BIDIRECTIONAL);
+ pci_unmap_single(HD(j)->pdev, id_dma_addr, sizeof(HD(j)->board_id),
+ PCI_DMA_BIDIRECTIONAL);
+ return FALSE;
+}
+
+static int port_detect \
+ (unsigned long port_base, unsigned int j, struct scsi_host_template *tpnt) {
+ unsigned char irq, dma_channel, subversion, i;
+ unsigned char in_byte;
+ char *bus_type, dma_name[16];
+
+ /* Allowed BIOS base addresses (NULL indicates reserved) */
+ unsigned long bios_segment_table[8] = {
+ 0,
+ 0xc4000, 0xc8000, 0xcc000, 0xd0000,
+ 0xd4000, 0xd8000, 0xdc000
+ };
+
+ /* Allowed IRQs */
+ unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
+
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
+ /* Head/sector mappings */
+ struct {
+ unsigned char heads;
+ unsigned char sectors;
+ } mapping_table[4] = {
+ { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
+ };
+
+ struct config_1 {
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned char dma_channel: 2, interrupt:2,
+ removable_disks_as_fixed:1, bios_segment: 3;
+#else
+ unsigned char bios_segment: 3, removable_disks_as_fixed: 1,
+ interrupt: 2, dma_channel: 2;
+#endif
+
+ } config_1;
+
+ struct config_2 {
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned char tfr_port: 2, bios_drive_number: 1,
+ mapping_mode: 2, ha_scsi_id: 3;
+#else
+ unsigned char ha_scsi_id: 3, mapping_mode: 2,
+ bios_drive_number: 1, tfr_port: 2;
+#endif
+
+ } config_2;
+
+ char name[16];
+
+ sprintf(name, "%s%d", driver_name, j);
+
+ if (!request_region(port_base, REGION_SIZE, driver_name)) {
+#if defined(DEBUG_DETECT)
+ printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
+#endif
+ goto fail;
+ }
+
+ spin_lock_irq(&driver_lock);
+
+ if (inb(port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) goto freelock;
+
+ in_byte = inb(port_base + REG_PRODUCT_ID2);
+
+ if ((in_byte & 0xf0) != PRODUCT_ID2) goto freelock;
+
+ *(char *)&config_1 = inb(port_base + REG_CONFIG1);
+ *(char *)&config_2 = inb(port_base + REG_CONFIG2);
+
+ irq = interrupt_table[config_1.interrupt];
+ dma_channel = dma_channel_table[config_1.dma_channel];
+ subversion = (in_byte & 0x0f);
+
+ /* Board detected, allocate its IRQ */
+ if (request_irq(irq, do_interrupt_handler,
+ SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+ driver_name, (void *) &sha[j])) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
+ goto freelock;
+ }
+
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ goto freeirq;
+ }
+
+ if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING;
+
+ spin_unlock_irq(&driver_lock);
+ sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+ spin_lock_irq(&driver_lock);
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+ goto freedma;
+ }
+
+ sh[j]->io_port = port_base;
+ sh[j]->unique_id = port_base;
+ sh[j]->n_io_port = REGION_SIZE;
+ sh[j]->base = bios_segment_table[config_1.bios_segment];
+ sh[j]->irq = irq;
+ sh[j]->sg_tablesize = MAX_SGLIST;
+ sh[j]->this_id = config_2.ha_scsi_id;
+ sh[j]->can_queue = MAX_MAILBOXES;
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+
+#if defined(DEBUG_DETECT)
+ {
+ unsigned char sys_mask, lcl_mask;
+
+ sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
+ lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
+ printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
+ }
+#endif
+
+ /* Probably a bogus host scsi id, set it to the dummy value */
+ if (sh[j]->this_id == 0) sh[j]->this_id = -1;
+
+ /* If BIOS is disabled, force enable interrupts */
+ if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
+
+ memset(HD(j), 0, sizeof(struct hostdata));
+ HD(j)->heads = mapping_table[config_2.mapping_mode].heads;
+ HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
+ HD(j)->subversion = subversion;
+ HD(j)->pdev = NULL;
+ HD(j)->board_number = j;
+
+ if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+
+ if (HD(j)->subversion == ESA) {
+ sh[j]->unchecked_isa_dma = FALSE;
+ sh[j]->dma_channel = NO_DMA;
+ sprintf(BN(j), "U34F%d", j);
+ bus_type = "VESA";
+ }
+ else {
+ unsigned long flags;
+ sh[j]->unchecked_isa_dma = TRUE;
+
+ flags=claim_dma_lock();
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ release_dma_lock(flags);
+
+ sh[j]->dma_channel = dma_channel;
+ sprintf(BN(j), "U14F%d", j);
+ bus_type = "ISA";
+ }
+
+ sh[j]->max_channel = MAX_CHANNEL - 1;
+ sh[j]->max_id = MAX_TARGET;
+ sh[j]->max_lun = MAX_LUN;
+
+ if (HD(j)->subversion == ISA && !board_inquiry(j)) {
+ HD(j)->board_id[40] = 0;
+
+ if (strcmp(&HD(j)->board_id[32], "06000600")) {
+ printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+ printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
+ BN(j), &HD(j)->board_id[32]);
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+ }
+ }
+
+ if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
+ else sprintf(dma_name, "DMA %u", dma_channel);
+
+ spin_unlock_irq(&driver_lock);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ HD(j)->cp[i].cp_dma_addr = pci_map_single(HD(j)->pdev,
+ &HD(j)->cp[i], sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if (! ((&HD(j)->cp[i])->sglist = kmalloc(
+ sh[j]->sg_tablesize * sizeof(struct sg_list),
+ (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) {
+ printk("%s: kmalloc SGlist failed, mbox %d, detaching.\n", BN(j), i);
+ goto release;
+ }
+
+ if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
+ max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
+
+ if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
+
+ if (tag_mode != TAG_DISABLED && tag_mode != TAG_SIMPLE)
+ tag_mode = TAG_ORDERED;
+
+ if (j == 0) {
+ printk("UltraStor 14F/34F: Copyright (C) 1994-2003 Dario Ballabio.\n");
+ printk("%s config options -> of:%c, tm:%d, lc:%c, mq:%d, et:%c.\n",
+ driver_name, YESNO(have_old_firmware), tag_mode,
+ YESNO(linked_comm), max_queue_depth, YESNO(ext_tran));
+ }
+
+ printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n",
+ BN(j), bus_type, (unsigned long)sh[j]->io_port, (int)sh[j]->base,
+ sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue);
+
+ if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
+ printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
+ BN(j), sh[j]->max_id, sh[j]->max_lun);
+
+ for (i = 0; i <= sh[j]->max_channel; i++)
+ printk("%s: SCSI channel %u enabled, host target ID %d.\n",
+ BN(j), i, sh[j]->this_id);
+
+ return TRUE;
+
+freedma:
+ if (subversion == ISA) free_dma(dma_channel);
+freeirq:
+ free_irq(irq, &sha[j]);
+freelock:
+ spin_unlock_irq(&driver_lock);
+ release_region(port_base, REGION_SIZE);
+fail:
+ return FALSE;
+
+release:
+ u14_34f_release(sh[j]);
+ return FALSE;
+}
+
+static void internal_setup(char *str, int *ints) {
+ int i, argc = ints[0];
+ char *cur = str, *pc;
+
+ if (argc > 0) {
+
+ if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
+
+ for (i = 0; i < argc; i++) io_port[i] = ints[i + 1];
+
+ io_port[i] = 0;
+ setup_done = TRUE;
+ }
+
+ while (cur && (pc = strchr(cur, ':'))) {
+ int val = 0, c = *++pc;
+
+ if (c == 'n' || c == 'N') val = FALSE;
+ else if (c == 'y' || c == 'Y') val = TRUE;
+ else val = (int) simple_strtoul(pc, NULL, 0);
+
+ if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+ else if (!strncmp(cur, "of:", 3)) have_old_firmware = val;
+ else if (!strncmp(cur, "tm:", 3)) tag_mode = val;
+ else if (!strncmp(cur, "tc:", 3)) tag_mode = val;
+ else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val;
+ else if (!strncmp(cur, "ls:", 3)) link_statistics = val;
+ else if (!strncmp(cur, "et:", 3)) ext_tran = val;
+
+ if ((cur = strchr(cur, ','))) ++cur;
+ }
+
+ return;
+}
+
+static int option_setup(char *str) {
+ int ints[MAX_INT_PARAM];
+ char *cur = str;
+ int i = 1;
+
+ while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+ ints[i++] = simple_strtoul(cur, NULL, 0);
+
+ if ((cur = strchr(cur, ',')) != NULL) cur++;
+ }
+
+ ints[0] = i - 1;
+ internal_setup(cur, ints);
+ return 1;
+}
+
+static int u14_34f_detect(struct scsi_host_template *tpnt) {
+ unsigned int j = 0, k;
+
+ tpnt->proc_name = "u14-34f";
+
+ if(strlen(boot_options)) option_setup(boot_options);
+
+#if defined(MODULE)
+ /* io_port could have been modified when loading as a module */
+ if(io_port[0] != SKIP) {
+ setup_done = TRUE;
+ io_port[MAX_INT_PARAM] = 0;
+ }
+#endif
+
+ for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
+
+ for (k = 0; io_port[k]; k++) {
+
+ if (io_port[k] == SKIP) continue;
+
+ if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++;
+ }
+
+ num_boards = j;
+ return j;
+}
+
+static void map_dma(unsigned int i, unsigned int j) {
+ unsigned int data_len = 0;
+ unsigned int k, count, pci_dir;
+ struct scatterlist *sgpnt;
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt;
+ pci_dir = SCpnt->sc_data_direction;
+
+ if (SCpnt->sense_buffer)
+ cpp->sense_addr = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->sense_buffer,
+ sizeof SCpnt->sense_buffer, PCI_DMA_FROMDEVICE));
+
+ cpp->sense_len = sizeof SCpnt->sense_buffer;
+
+ if (!SCpnt->use_sg) {
+
+ /* If we get here with PCI_DMA_NONE, pci_map_single triggers a BUG() */
+ if (!SCpnt->request_bufflen) pci_dir = PCI_DMA_BIDIRECTIONAL;
+
+ if (SCpnt->request_buffer)
+ cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev,
+ SCpnt->request_buffer, SCpnt->request_bufflen, pci_dir));
+
+ cpp->data_len = H2DEV(SCpnt->request_bufflen);
+ return;
+ }
+
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ count = pci_map_sg(HD(j)->pdev, sgpnt, SCpnt->use_sg, pci_dir);
+
+ for (k = 0; k < count; k++) {
+ cpp->sglist[k].address = H2DEV(sg_dma_address(&sgpnt[k]));
+ cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(&sgpnt[k]));
+ data_len += sgpnt[k].length;
+ }
+
+ cpp->sg = TRUE;
+ cpp->use_sg = SCpnt->use_sg;
+ cpp->data_address = H2DEV(pci_map_single(HD(j)->pdev, cpp->sglist,
+ SCpnt->use_sg * sizeof(struct sg_list), pci_dir));
+ cpp->data_len = H2DEV(data_len);
+}
+
+static void unmap_dma(unsigned int i, unsigned int j) {
+ unsigned int pci_dir;
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt;
+ pci_dir = SCpnt->sc_data_direction;
+
+ if (DEV2H(cpp->sense_addr))
+ pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr),
+ DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
+
+ if (SCpnt->use_sg)
+ pci_unmap_sg(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir);
+
+ if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL;
+
+ if (DEV2H(cpp->data_address))
+ pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address),
+ DEV2H(cpp->data_len), pci_dir);
+}
+
+static void sync_dma(unsigned int i, unsigned int j) {
+ unsigned int pci_dir;
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt;
+ pci_dir = SCpnt->sc_data_direction;
+
+ if (DEV2H(cpp->sense_addr))
+ pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->sense_addr),
+ DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
+
+ if (SCpnt->use_sg)
+ pci_dma_sync_sg_for_cpu(HD(j)->pdev, SCpnt->request_buffer,
+ SCpnt->use_sg, pci_dir);
+
+ if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL;
+
+ if (DEV2H(cpp->data_address))
+ pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->data_address),
+ DEV2H(cpp->data_len), pci_dir);
+}
+
+static void scsi_to_dev_dir(unsigned int i, unsigned int j) {
+ unsigned int k;
+
+ static const unsigned char data_out_cmds[] = {
+ 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+ 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
+ 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b, 0x5d
+ };
+
+ static const unsigned char data_none_cmds[] = {
+ 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+ 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+ 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00
+ };
+
+ struct mscp *cpp;
+ struct scsi_cmnd *SCpnt;
+
+ cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt;
+
+ if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
+ cpp->xdir = DTD_IN;
+ return;
+ }
+ else if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
+ cpp->xdir = DTD_OUT;
+ return;
+ }
+ else if (SCpnt->sc_data_direction == DMA_NONE) {
+ cpp->xdir = DTD_NONE;
+ return;
+ }
+
+ if (SCpnt->sc_data_direction != DMA_BIDIRECTIONAL)
+ panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j));
+
+ cpp->xdir = DTD_IN;
+
+ for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+ if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+ cpp->xdir = DTD_OUT;
+ break;
+ }
+
+ if (cpp->xdir == DTD_IN)
+ for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+ if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+ cpp->xdir = DTD_NONE;
+ break;
+ }
+
+}
+
+static int u14_34f_queuecommand(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) {
+ unsigned int i, j, k;
+ struct mscp *cpp;
+
+ /* j is the board number */
+ j = ((struct hostdata *) SCpnt->device->host->hostdata)->board_number;
+
+ if (SCpnt->host_scribble)
+ panic("%s: qcomm, pid %ld, SCpnt %p already active.\n",
+ BN(j), SCpnt->pid, SCpnt);
+
+ /* i is the mailbox number, look for the first free mailbox
+ starting from last_cp_used */
+ i = HD(j)->last_cp_used + 1;
+
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
+
+ if (i >= sh[j]->can_queue) i = 0;
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ HD(j)->last_cp_used = i;
+ break;
+ }
+ }
+
+ if (k == sh[j]->can_queue) {
+ printk("%s: qcomm, no free mailbox.\n", BN(j));
+ return 1;
+ }
+
+ /* Set pointer to control packet structure */
+ cpp = &HD(j)->cp[i];
+
+ memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE);
+ SCpnt->scsi_done = done;
+ cpp->cpp_index = i;
+ SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index;
+
+ if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCpnt->device->channel, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->pid);
+
+ cpp->opcode = OP_SCSI;
+ cpp->channel = SCpnt->device->channel;
+ cpp->target = SCpnt->device->id;
+ cpp->lun = SCpnt->device->lun;
+ cpp->SCpnt = SCpnt;
+ cpp->cdb_len = SCpnt->cmd_len;
+ memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ /* Use data transfer direction SCpnt->sc_data_direction */
+ scsi_to_dev_dir(i, j);
+
+ /* Map DMA buffers and SG list */
+ map_dma(i, j);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type)) {
+ HD(j)->cp_stat[i] = READY;
+ flush_dev(SCpnt->device, SCpnt->request->sector, j, FALSE);
+ return 0;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ unmap_dma(i, j);
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n",
+ BN(j), SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun, SCpnt->pid);
+ return 1;
+ }
+
+ /* Store pointer in OGM address bytes */
+ outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ HD(j)->cp_stat[i] = IN_USE;
+ return 0;
+}
+
+static int u14_34f_eh_abort(struct scsi_cmnd *SCarg) {
+ unsigned int i, j;
+
+ j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL) {
+ printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
+ BN(j), SCarg->device->channel, SCarg->device->id, SCarg->device->lun, SCarg->pid);
+ return SUCCESS;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCarg->device->channel, SCarg->device->id, SCarg->device->lun, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ return SUCCESS;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED)
+ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
+
+ if (SCarg->eh_state == SCSI_STATE_TIMEOUT) {
+ unmap_dma(i, j);
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d, eh_state timeout, pid %ld.\n",
+ BN(j), i, SCarg->pid);
+ return SUCCESS;
+ }
+
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ return SUCCESS;
+ }
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ unmap_dma(i, j);
+ SCarg->result = DID_ABORT << 16;
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+ BN(j), i, SCarg->pid);
+ SCarg->scsi_done(SCarg);
+ return SUCCESS;
+ }
+
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) {
+ unsigned int i, j, time, k, c, limit = 0;
+ int arg_done = FALSE;
+ struct scsi_cmnd *SCpnt;
+
+ j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d.%d:%d, pid %ld.\n",
+ BN(j), SCarg->device->channel, SCarg->device->id, SCarg->device->lun, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ return FAILED;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ HD(j)->retries = 0;
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++) {
+ HD(j)->target_redo[k][c] = TRUE;
+ HD(j)->target_to[k][c] = 0;
+ }
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ if (!(SCpnt = HD(j)->cp[i].SCpnt))
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ HD(j)->cp_stat[i] = ABORTING;
+ printk("%s: reset, mbox %d aborting, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else {
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR);
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined(DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+
+ spin_unlock_irq(sh[j]->host_lock);
+ time = jiffies;
+ while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
+ spin_lock_irq(sh[j]->host_lock);
+
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ unmap_dma(i, j);
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else if (HD(j)->cp_stat[i] == ABORTING) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ unmap_dma(i, j);
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox was never queued to the adapter */
+ HD(j)->cp_stat[i] = FREE;
+
+ printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else
+
+ /* Any other mailbox has already been set free by interrupt */
+ continue;
+
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+
+ if (arg_done) printk("%s: reset, exit, pid %ld done.\n", BN(j), SCarg->pid);
+ else printk("%s: reset, exit.\n", BN(j));
+
+ return SUCCESS;
+}
+
+static int u14_34f_bios_param(struct scsi_device *disk,
+ struct block_device *bdev, sector_t capacity, int *dkinfo) {
+ unsigned int j = 0;
+ unsigned int size = capacity;
+
+ dkinfo[0] = HD(j)->heads;
+ dkinfo[1] = HD(j)->sectors;
+ dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors);
+
+ if (ext_tran && (scsicam_bios_param(bdev, capacity, dkinfo) < 0)) {
+ dkinfo[0] = 255;
+ dkinfo[1] = 63;
+ dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
+ }
+
+#if defined (DEBUG_GEOMETRY)
+ printk ("%s: bios_param, head=%d, sec=%d, cyl=%d.\n", driver_name,
+ dkinfo[0], dkinfo[1], dkinfo[2]);
+#endif
+
+ return FALSE;
+}
+
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+ unsigned int rev) {
+ unsigned int i, j, k, y;
+ unsigned long x;
+
+ for (i = 0; i < n - 1; i++) {
+ k = i;
+
+ for (j = k + 1; j < n; j++)
+ if (rev) {
+ if (sk[j] > sk[k]) k = j;
+ }
+ else {
+ if (sk[j] < sk[k]) k = j;
+ }
+
+ if (k != i) {
+ x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+ y = da[k]; da[k] = da[i]; da[i] = y;
+ }
+ }
+
+ return;
+ }
+
+static int reorder(unsigned int j, unsigned long cursec,
+ unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
+ struct scsi_cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n;
+ unsigned int rev = FALSE, s = TRUE, r = TRUE;
+ unsigned int input_only = TRUE, overlap = FALSE;
+ unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+ unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
+ unsigned long ioseek = 0;
+
+ static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+ static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+ static unsigned int readysorted = 0, revcount = 0;
+ static unsigned long seeksorted = 0, seeknosort = 0;
+
+ if (link_statistics && !(++flushcount % link_statistics))
+ printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+ " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+ ovlcount, readycount, readysorted, sortcount, revcount,
+ seeknosort / (readycount + 1),
+ seeksorted / (readycount + 1));
+
+ if (n_ready <= 1) return FALSE;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (!(cpp->xdir == DTD_IN)) input_only = FALSE;
+
+ if (SCpnt->request->sector < minsec) minsec = SCpnt->request->sector;
+ if (SCpnt->request->sector > maxsec) maxsec = SCpnt->request->sector;
+
+ sl[n] = SCpnt->request->sector;
+ ioseek += SCpnt->request->nr_sectors;
+
+ if (!n) continue;
+
+ if (sl[n] < sl[n - 1]) s = FALSE;
+ if (sl[n] > sl[n - 1]) r = FALSE;
+
+ if (link_statistics) {
+ if (sl[n] > sl[n - 1])
+ seek += sl[n] - sl[n - 1];
+ else
+ seek += sl[n - 1] - sl[n];
+ }
+
+ }
+
+ if (link_statistics) {
+ if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+ }
+
+ if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+ if (ioseek > ((maxsec - minsec) / 2)) rev = FALSE;
+
+ if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+ if (!input_only) for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+ ll[n] = SCpnt->request->nr_sectors; pl[n] = SCpnt->pid;
+
+ if (!n) continue;
+
+ if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+ || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+ }
+
+ if (overlap) sort(pl, il, n_ready, FALSE);
+
+ if (link_statistics) {
+ if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
+ batchcount++; readycount += n_ready; seeknosort += seek / 1024;
+ if (input_only) inputcount++;
+ if (overlap) { ovlcount++; seeksorted += iseek / 1024; }
+ else seeksorted += (iseek + maxsec - minsec) / 1024;
+ if (rev && !r) { revcount++; readysorted += n_ready; }
+ if (!rev && !s) { sortcount++; readysorted += n_ready; }
+ }
+
+#if defined(DEBUG_LINKED_COMMANDS)
+ if (link_statistics && (overlap || !(flushcount % link_statistics)))
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+ printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+ " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+ (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+ SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+ SCpnt->request->sector, SCpnt->request->nr_sectors, cursec,
+ YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
+ YESNO(overlap), cpp->xdir);
+ }
+#endif
+ return overlap;
+}
+
+static void flush_dev(struct scsi_device *dev, unsigned long cursec, unsigned int j,
+ unsigned int ihdlr) {
+ struct scsi_cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
+
+ for (k = 0; k < sh[j]->can_queue; k++) {
+
+ if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
+
+ cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (SCpnt->device != dev) continue;
+
+ if (HD(j)->cp_stat[k] == IN_USE) return;
+
+ il[n_ready++] = k;
+ }
+
+ if (reorder(j, cursec, ihdlr, il, n_ready)) n_ready = 1;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: %s, target %d.%d:%d, pid %ld, mbox %d, adapter"\
+ " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+ SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun, SCpnt->pid, k);
+ HD(j)->cp_stat[k] = ABORTING;
+ continue;
+ }
+
+ outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM);
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+ HD(j)->cp_stat[k] = IN_USE;
+ }
+
+}
+
+static irqreturn_t ihdlr(int irq, unsigned int j) {
+ struct scsi_cmnd *SCpnt;
+ unsigned int i, k, c, status, tstatus, reg, ret;
+ struct mscp *spp, *cpp;
+
+ if (sh[j]->irq != irq)
+ panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+
+ /* Check if this board need to be serviced */
+ if (!((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED)) goto none;
+
+ HD(j)->iocount++;
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ /* Check if this board is still busy */
+ if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) {
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+ printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n",
+ BN(j), irq, reg, HD(j)->iocount);
+ goto none;
+ }
+
+ ret = inl(sh[j]->io_port + REG_ICM);
+
+ /* Clear interrupt pending flag */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ /* Find the mailbox to be serviced on this board */
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if (H2DEV(HD(j)->cp[i].cp_dma_addr) == ret) break;
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j),
+ (void *)ret, (void *)H2DEV(HD(j)->cp[0].cp_dma_addr));
+
+ cpp = &(HD(j)->cp[i]);
+ spp = cpp;
+
+#if defined(DEBUG_GENERATE_ABORTS)
+ if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) goto handled;
+#endif
+
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ goto handled;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ goto handled;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ goto handled;
+ }
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
+ BN(j), i, HD(j)->cp_stat[i]);
+
+ HD(j)->cp_stat[i] = FREE;
+ SCpnt = cpp->SCpnt;
+
+ if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+ SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
+ BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble);
+
+ sync_dma(i, j);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type))
+ flush_dev(SCpnt->device, SCpnt->request->sector, j, TRUE);
+
+ tstatus = status_byte(spp->target_status);
+
+#if defined(DEBUG_GENERATE_ERRORS)
+ if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 200) < 2))
+ spp->adapter_status = 0x01;
+#endif
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_redo[SCpnt->device->id][SCpnt->device->channel])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ HD(j)->target_redo[SCpnt->device->id][SCpnt->device->channel] = FALSE;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK &&
+ (!(tstatus == CHECK_CONDITION && HD(j)->iocount <= 1000 &&
+ (SCpnt->sense_buffer[2] & 0xf) == NOT_READY)))
+ printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+ "target_status 0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun,
+ SCpnt->pid, spp->target_status,
+ SCpnt->sense_buffer[2]);
+
+ HD(j)->target_to[SCpnt->device->id][SCpnt->device->channel] = 0;
+
+ if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+
+ if (HD(j)->target_to[SCpnt->device->id][SCpnt->device->channel] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_to[SCpnt->device->id][SCpnt->device->channel]++;
+ }
+
+ break;
+
+ /* Perform a limited number of internal retries */
+ case 0x93: /* Unexpected bus free */
+ case 0x94: /* Target bus phase sequence failure */
+ case 0x96: /* Illegal SCSI command */
+ case 0xa3: /* SCSI bus reset error */
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++)
+ HD(j)->target_redo[k][c] = TRUE;
+
+
+ case 0x92: /* Data over/under-run */
+
+ if (SCpnt->device->type != TYPE_TAPE
+ && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+
+#if defined(DID_SOFT_ERROR)
+ status = DID_SOFT_ERROR << 16;
+#else
+ status = DID_BUS_BUSY << 16;
+#endif
+
+ HD(j)->retries++;
+ HD(j)->last_retried_pid = SCpnt->pid;
+ }
+ else
+ status = DID_ERROR << 16;
+
+ break;
+ case 0x01: /* Invalid command */
+ case 0x02: /* Invalid parameters */
+ case 0x03: /* Invalid data list */
+ case 0x84: /* SCSI bus abort error */
+ case 0x9b: /* Auto request sense error */
+ case 0x9f: /* Unexpected command complete message error */
+ case 0xff: /* Invalid parameter in the S/G list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+
+#if defined(DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace || msg_byte(spp->target_status))
+#endif
+ printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+ " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun, SCpnt->pid,
+ reg, HD(j)->iocount);
+
+ unmap_dma(i, j);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ SCpnt->scsi_done(SCpnt);
+
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+handled:
+ return IRQ_HANDLED;
+none:
+ return IRQ_NONE;
+}
+
+static irqreturn_t do_interrupt_handler(int irq, void *shap,
+ struct pt_regs *regs) {
+ unsigned int j;
+ unsigned long spin_flags;
+ irqreturn_t ret;
+
+ /* Check if the interrupt must be processed by this handler */
+ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return IRQ_NONE;
+
+ spin_lock_irqsave(sh[j]->host_lock, spin_flags);
+ ret = ihdlr(irq, j);
+ spin_unlock_irqrestore(sh[j]->host_lock, spin_flags);
+ return ret;
+}
+
+static int u14_34f_release(struct Scsi_Host *shpnt) {
+ unsigned int i, j;
+
+ for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++);
+
+ if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n",
+ driver_name);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ pci_unmap_single(HD(j)->pdev, HD(j)->cp[i].cp_dma_addr,
+ sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL);
+
+ free_irq(sh[j]->irq, &sha[j]);
+
+ if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
+
+ release_region(sh[j]->io_port, sh[j]->n_io_port);
+ scsi_unregister(sh[j]);
+ return FALSE;
+}
+
+#include "scsi_module.c"
+
+#ifndef MODULE
+__setup("u14-34f=", option_setup);
+#endif /* end MODULE */
diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c
new file mode 100644
index 000000000000..7484916fe2aa
--- /dev/null
+++ b/drivers/scsi/ultrastor.c
@@ -0,0 +1,1204 @@
+/*
+ * ultrastor.c Copyright (C) 1992 David B. Gentzel
+ * Low-level SCSI driver for UltraStor 14F, 24F, and 34F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
+ * 24F and multiple command support by John F. Carr (jfc@athena.mit.edu)
+ * John's work modified by Caleb Epstein (cae@jpmorgan.com) and
+ * Eric Youngdale (ericy@cais.com).
+ * Thanks to UltraStor for providing the necessary documentation
+ *
+ * This is an old driver, for the 14F and 34F you should be using the
+ * u14-34f driver instead.
+ */
+
+/*
+ * TODO:
+ * 1. Find out why scatter/gather is limited to 16 requests per command.
+ * This is fixed, at least on the 24F, as of version 1.12 - CAE.
+ * 2. Look at command linking (mscp.command_link and
+ * mscp.command_link_id). (Does not work with many disks,
+ * and no performance increase. ERY).
+ * 3. Allow multiple adapters.
+ */
+
+/*
+ * NOTES:
+ * The UltraStor 14F, 24F, and 34F are a family of intelligent, high
+ * performance SCSI-2 host adapters. They all support command queueing
+ * and scatter/gather I/O. Some of them can also emulate the standard
+ * WD1003 interface for use with OS's which don't support SCSI. Here
+ * is the scoop on the various models:
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 14N - ISA HA with floppy support. I think that this is a non-DMA
+ * HA. Nothing further known.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation).
+ *
+ * The 14F, 24F, and 34F are supported by this driver.
+ *
+ * Places flagged with a triple question-mark are things which are either
+ * unfinished, questionable, or wrong.
+ */
+
+/* Changes from version 1.11 alpha to 1.12
+ *
+ * Increased the size of the scatter-gather list to 33 entries for
+ * the 24F adapter (it was 16). I don't have the specs for the 14F
+ * or the 34F, so they may support larger s-g lists as well.
+ *
+ * Caleb Epstein <cae@jpmorgan.com>
+ */
+
+/* Changes from version 1.9 to 1.11
+ *
+ * Patches to bring this driver up to speed with the default kernel
+ * driver which supports only the 14F and 34F adapters. This version
+ * should compile cleanly into 0.99.13, 0.99.12 and probably 0.99.11.
+ *
+ * Fixes from Eric Youngdale to fix a few possible race conditions and
+ * several problems with bit testing operations (insufficient
+ * parentheses).
+ *
+ * Removed the ultrastor_abort() and ultrastor_reset() functions
+ * (enclosed them in #if 0 / #endif). These functions, at least on
+ * the 24F, cause the SCSI bus to do odd things and generally lead to
+ * kernel panics and machine hangs. This is like the Adaptec code.
+ *
+ * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts.
+ */
+
+/* Changes from version 1.8 to version 1.9
+ *
+ * 0.99.11 patches (cae@jpmorgan.com) */
+
+/* Changes from version 1.7 to version 1.8
+ *
+ * Better error reporting.
+ */
+
+/* Changes from version 1.6 to version 1.7
+ *
+ * Removed CSIR command code.
+ *
+ * Better race condition avoidance (xchgb function added).
+ *
+ * Set ICM and OGM status to zero at probe (24F)
+ *
+ * reset sends soft reset to UltraStor adapter
+ *
+ * reset adapter if adapter interrupts with an invalid MSCP address
+ *
+ * handle aborted command interrupt (24F)
+ *
+ */
+
+/* Changes from version 1.5 to version 1.6:
+ *
+ * Read MSCP address from ICM _before_ clearing the interrupt flag.
+ * This fixes a race condition.
+ */
+
+/* Changes from version 1.4 to version 1.5:
+ *
+ * Abort now calls done when multiple commands are enabled.
+ *
+ * Clear busy when aborted command finishes, not when abort is called.
+ *
+ * More debugging messages for aborts.
+ */
+
+/* Changes from version 1.3 to version 1.4:
+ *
+ * Enable automatic request of sense data on error (requires newer version
+ * of scsi.c to be useful).
+ *
+ * Fix PORT_OVERRIDE for 14F.
+ *
+ * Fix abort and reset to work properly (config.aborted wasn't cleared
+ * after it was tested, so after a command abort no further commands would
+ * work).
+ *
+ * Boot time test to enable SCSI bus reset (defaults to not allowing reset).
+ *
+ * Fix test for OGM busy -- the busy bit is in different places on the 24F.
+ *
+ * Release ICM slot by clearing first byte on 24F.
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+
+#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "ultrastor.h"
+
+#define FALSE 0
+#define TRUE 1
+
+#ifndef ULTRASTOR_DEBUG
+#define ULTRASTOR_DEBUG (UD_ABORT|UD_CSIR|UD_RESET)
+#endif
+
+#define VERSION "1.12"
+
+#define PACKED __attribute__((packed))
+#define ALIGNED(x) __attribute__((aligned(x)))
+
+
+/* The 14F uses an array of 4-byte ints for its scatter/gather list.
+ The data can be unaligned, but need not be. It's easier to give
+ the list normal alignment since it doesn't need to fit into a
+ packed structure. */
+
+typedef struct {
+ u32 address;
+ u32 num_bytes;
+} ultrastor_sg_list;
+
+
+/* MailBox SCSI Command Packet. Basic command structure for communicating
+ with controller. */
+struct mscp {
+ unsigned char opcode: 3; /* type of command */
+ unsigned char xdir: 2; /* data transfer direction */
+ unsigned char dcn: 1; /* disable disconnect */
+ unsigned char ca: 1; /* use cache (if available) */
+ unsigned char sg: 1; /* scatter/gather operation */
+ unsigned char target_id: 3; /* target SCSI id */
+ unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */
+ unsigned char lun: 3; /* logical unit number */
+ unsigned int transfer_data PACKED; /* transfer data pointer */
+ unsigned int transfer_data_length PACKED; /* length in bytes */
+ unsigned int command_link PACKED; /* for linking command chains */
+ unsigned char scsi_command_link_id; /* identifies command in chain */
+ unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */
+ unsigned char length_of_sense_byte;
+ unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */
+ unsigned char scsi_cdbs[12]; /* SCSI commands */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ u32 sense_data PACKED;
+ /* The following fields are for software only. They are included in
+ the MSCP structure because they are associated with SCSI requests. */
+ void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCint;
+ ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */
+};
+
+
+/* Port addresses (relative to the base address) */
+#define U14F_PRODUCT_ID(port) ((port) + 0x4)
+#define CONFIG(port) ((port) + 0x6)
+
+/* Port addresses relative to the doorbell base address. */
+#define LCL_DOORBELL_MASK(port) ((port) + 0x0)
+#define LCL_DOORBELL_INTR(port) ((port) + 0x1)
+#define SYS_DOORBELL_MASK(port) ((port) + 0x2)
+#define SYS_DOORBELL_INTR(port) ((port) + 0x3)
+
+
+/* Used to store configuration info read from config i/o registers. Most of
+ this is not used yet, but might as well save it.
+
+ This structure also holds port addresses that are not at the same offset
+ on the 14F and 24F.
+
+ This structure holds all data that must be duplicated to support multiple
+ adapters. */
+
+static struct ultrastor_config
+{
+ unsigned short port_address; /* base address of card */
+ unsigned short doorbell_address; /* base address of doorbell CSRs */
+ unsigned short ogm_address; /* base address of OGM */
+ unsigned short icm_address; /* base address of ICM */
+ const void *bios_segment;
+ unsigned char interrupt: 4;
+ unsigned char dma_channel: 3;
+ unsigned char bios_drive_number: 1;
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned char ha_scsi_id: 3;
+ unsigned char subversion: 4;
+ unsigned char revision;
+ /* The slot number is used to distinguish the 24F (slot != 0) from
+ the 14F and 34F (slot == 0). */
+ unsigned char slot;
+
+#ifdef PRINT_U24F_VERSION
+ volatile int csir_done;
+#endif
+
+ /* A pool of MSCP structures for this adapter, and a bitmask of
+ busy structures. (If ULTRASTOR_14F_MAX_CMDS == 1, a 1 byte
+ busy flag is used instead.) */
+
+#if ULTRASTOR_MAX_CMDS == 1
+ unsigned char mscp_busy;
+#else
+ unsigned long mscp_free;
+#endif
+ volatile unsigned char aborted[ULTRASTOR_MAX_CMDS];
+ struct mscp mscp[ULTRASTOR_MAX_CMDS];
+} config = {0};
+
+/* Set this to 1 to reset the SCSI bus on error. */
+static int ultrastor_bus_reset;
+
+
+/* Allowed BIOS base addresses (NULL indicates reserved) */
+static const void *const bios_segment_table[8] = {
+ NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
+ (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
+};
+
+/* Allowed IRQs for 14f */
+static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
+
+/* Allowed DMA channels for 14f (0 indicates reserved) */
+static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
+
+/* Head/sector mappings allowed by 14f */
+static const struct {
+ unsigned char heads;
+ unsigned char sectors;
+} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } };
+
+#ifndef PORT_OVERRIDE
+/* ??? A probe of address 0x310 screws up NE2000 cards */
+static const unsigned short ultrastor_ports_14f[] = {
+ 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,
+};
+#endif
+
+static void ultrastor_interrupt(int, void *, struct pt_regs *);
+static irqreturn_t do_ultrastor_interrupt(int, void *, struct pt_regs *);
+static inline void build_sg_list(struct mscp *, Scsi_Cmnd *SCpnt);
+
+
+/* Always called with host lock held */
+
+static inline int find_and_clear_bit_16(unsigned long *field)
+{
+ int rv;
+
+ if (*field == 0) panic("No free mscp");
+ asm("xorl %0,%0\n0:\tbsfw %1,%w0\n\tbtr %0,%1\n\tjnc 0b"
+ : "=&r" (rv), "=m" (*field) : "1" (*field));
+ return rv;
+}
+
+/* This has been re-implemented with the help of Richard Earnshaw,
+ <rwe@pegasus.esprit.ec.org> and works with gcc-2.5.8 and gcc-2.6.0.
+ The instability noted by jfc below appears to be a bug in
+ gcc-2.5.x when compiling w/o optimization. --Caleb
+
+ This asm is fragile: it doesn't work without the casts and it may
+ not work without optimization. Maybe I should add a swap builtin
+ to gcc. --jfc */
+static inline unsigned char xchgb(unsigned char reg,
+ volatile unsigned char *mem)
+{
+ __asm__ ("xchgb %0,%1" : "=q" (reg), "=m" (*mem) : "0" (reg));
+ return reg;
+}
+
+#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)
+
+/* Always called with the host lock held */
+static void log_ultrastor_abort(struct ultrastor_config *config,
+ int command)
+{
+ static char fmt[80] = "abort %d (%x); MSCP free pool: %x;";
+ int i;
+
+ for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)
+ {
+ fmt[20 + i*2] = ' ';
+ if (! (config->mscp_free & (1 << i)))
+ fmt[21 + i*2] = '0' + config->mscp[i].target_id;
+ else
+ fmt[21 + i*2] = '-';
+ }
+ fmt[20 + ULTRASTOR_MAX_CMDS * 2] = '\n';
+ fmt[21 + ULTRASTOR_MAX_CMDS * 2] = 0;
+ printk(fmt, command, &config->mscp[command], config->mscp_free);
+
+}
+#endif
+
+static int ultrastor_14f_detect(Scsi_Host_Template * tpnt)
+{
+ size_t i;
+ unsigned char in_byte, version_byte = 0;
+ struct config_1 {
+ unsigned char bios_segment: 3;
+ unsigned char removable_disks_as_fixed: 1;
+ unsigned char interrupt: 2;
+ unsigned char dma_channel: 2;
+ } config_1;
+ struct config_2 {
+ unsigned char ha_scsi_id: 3;
+ unsigned char mapping_mode: 2;
+ unsigned char bios_drive_number: 1;
+ unsigned char tfr_port: 2;
+ } config_2;
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: called\n");
+#endif
+
+ /* If a 24F has already been configured, don't look for a 14F. */
+ if (config.bios_segment)
+ return FALSE;
+
+#ifdef PORT_OVERRIDE
+ if(!request_region(PORT_OVERRIDE, 0xc, "ultrastor")) {
+ printk("Ultrastor I/O space already in use\n");
+ return FALSE;
+ };
+ config.port_address = PORT_OVERRIDE;
+#else
+ for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) {
+ if(!request_region(ultrastor_ports_14f[i], 0x0c, "ultrastor")) continue;
+ config.port_address = ultrastor_ports_14f[i];
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: testing port address %03X\n", config.port_address);
+#endif
+
+ in_byte = inb(U14F_PRODUCT_ID(config.port_address));
+ if (in_byte != US14F_PRODUCT_ID_0) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+# ifdef PORT_OVERRIDE
+ printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte);
+# else
+ printk("US14F: detect: no adapter at port %03X\n", config.port_address);
+# endif
+#endif
+#ifdef PORT_OVERRIDE
+ goto out_release_port;
+#else
+ release_region(config.port_address, 0x0c);
+ continue;
+#endif
+ }
+ in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1);
+ /* Only upper nibble is significant for Product ID 1 */
+ if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+# ifdef PORT_OVERRIDE
+ printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte);
+# else
+ printk("US14F: detect: no adapter at port %03X\n", config.port_address);
+# endif
+#endif
+#ifdef PORT_OVERRIDE
+ goto out_release_port;
+#else
+ release_region(config.port_address, 0x0c);
+ continue;
+#endif
+ }
+ version_byte = in_byte;
+#ifndef PORT_OVERRIDE
+ break;
+ }
+ if (i == ARRAY_SIZE(ultrastor_ports_14f)) {
+# if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: no port address found!\n");
+# endif
+ /* all ports probed already released - we can just go straight out */
+ return FALSE;
+ }
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: adapter found at port address %03X\n",
+ config.port_address);
+#endif
+
+ /* Set local doorbell mask to disallow bus reset unless
+ ultrastor_bus_reset is true. */
+ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address));
+
+ /* All above tests passed, must be the right thing. Get some useful
+ info. */
+
+ /* Register the I/O space that we use */
+
+ *(char *)&config_1 = inb(CONFIG(config.port_address + 0));
+ *(char *)&config_2 = inb(CONFIG(config.port_address + 1));
+ config.bios_segment = bios_segment_table[config_1.bios_segment];
+ config.doorbell_address = config.port_address;
+ config.ogm_address = config.port_address + 0x8;
+ config.icm_address = config.port_address + 0xC;
+ config.interrupt = interrupt_table_14f[config_1.interrupt];
+ config.ha_scsi_id = config_2.ha_scsi_id;
+ config.heads = mapping_table[config_2.mapping_mode].heads;
+ config.sectors = mapping_table[config_2.mapping_mode].sectors;
+ config.bios_drive_number = config_2.bios_drive_number;
+ config.subversion = (version_byte & 0x0F);
+ if (config.subversion == U34F)
+ config.dma_channel = 0;
+ else
+ config.dma_channel = dma_channel_table_14f[config_1.dma_channel];
+
+ if (!config.bios_segment) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: not detected.\n");
+#endif
+ goto out_release_port;
+ }
+
+ /* Final consistency check, verify previous info. */
+ if (config.subversion != U34F)
+ if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: consistency check failed\n");
+#endif
+ goto out_release_port;
+ }
+
+ /* If we were TRULY paranoid, we could issue a host adapter inquiry
+ command here and verify the data returned. But frankly, I'm
+ exhausted! */
+
+ /* Finally! Now I'm satisfied... */
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: detect succeeded\n"
+ " Port address: %03X\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %u\n"
+ " DMA channel: %u\n"
+ " H/A SCSI ID: %u\n"
+ " Subversion: %u\n",
+ config.port_address, config.bios_segment, config.interrupt,
+ config.dma_channel, config.ha_scsi_id, config.subversion);
+#endif
+ tpnt->this_id = config.ha_scsi_id;
+ tpnt->unchecked_isa_dma = (config.subversion != U34F);
+
+#if ULTRASTOR_MAX_CMDS > 1
+ config.mscp_free = ~0;
+#endif
+
+ /*
+ * Brrr, &config.mscp[0].SCint->host) it is something magical....
+ * XXX and FIXME
+ */
+ if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", &config.mscp[0].SCint->device->host)) {
+ printk("Unable to allocate IRQ%u for UltraStor controller.\n",
+ config.interrupt);
+ goto out_release_port;
+ }
+ if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) {
+ printk("Unable to allocate DMA channel %u for UltraStor controller.\n",
+ config.dma_channel);
+ free_irq(config.interrupt, NULL);
+ goto out_release_port;
+ }
+ tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG;
+ printk("UltraStor driver version" VERSION ". Using %d SG lists.\n",
+ ULTRASTOR_14F_MAX_SG);
+
+ return TRUE;
+out_release_port:
+ release_region(config.port_address, 0x0c);
+ return FALSE;
+}
+
+static int ultrastor_24f_detect(Scsi_Host_Template * tpnt)
+{
+ int i;
+ struct Scsi_Host * shpnt = NULL;
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US24F: detect");
+#endif
+
+ /* probe each EISA slot at slot address C80 */
+ for (i = 1; i < 15; i++)
+ {
+ unsigned char config_1, config_2;
+ unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT;
+
+ if (inb(addr) != US24F_PRODUCT_ID_0 &&
+ inb(addr+1) != US24F_PRODUCT_ID_1 &&
+ inb(addr+2) != US24F_PRODUCT_ID_2)
+ continue;
+
+ config.revision = inb(addr+3);
+ config.slot = i;
+ if (! (inb(addr+4) & 1))
+ {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("U24F: found disabled card in slot %u\n", i);
+#endif
+ continue;
+ }
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("U24F: found card in slot %u\n", i);
+#endif
+ config_1 = inb(addr + 5);
+ config.bios_segment = bios_segment_table[config_1 & 7];
+ switch(config_1 >> 4)
+ {
+ case 1:
+ config.interrupt = 15;
+ break;
+ case 2:
+ config.interrupt = 14;
+ break;
+ case 4:
+ config.interrupt = 11;
+ break;
+ case 8:
+ config.interrupt = 10;
+ break;
+ default:
+ printk("U24F: invalid IRQ\n");
+ return FALSE;
+ }
+
+ /* BIOS addr set */
+ /* base port set */
+ config.port_address = addr;
+ config.doorbell_address = addr + 12;
+ config.ogm_address = addr + 0x17;
+ config.icm_address = addr + 0x1C;
+ config_2 = inb(addr + 7);
+ config.ha_scsi_id = config_2 & 7;
+ config.heads = mapping_table[(config_2 >> 3) & 3].heads;
+ config.sectors = mapping_table[(config_2 >> 3) & 3].sectors;
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US24F: detect: detect succeeded\n"
+ " Port address: %03X\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %u\n"
+ " H/A SCSI ID: %u\n",
+ config.port_address, config.bios_segment,
+ config.interrupt, config.ha_scsi_id);
+#endif
+ tpnt->this_id = config.ha_scsi_id;
+ tpnt->unchecked_isa_dma = 0;
+ tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG;
+
+ shpnt = scsi_register(tpnt, 0);
+ if (!shpnt) {
+ printk(KERN_WARNING "(ultrastor:) Could not register scsi device. Aborting registration.\n");
+ free_irq(config.interrupt, do_ultrastor_interrupt);
+ return FALSE;
+ }
+
+ if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", shpnt))
+ {
+ printk("Unable to allocate IRQ%u for UltraStor controller.\n",
+ config.interrupt);
+ return FALSE;
+ }
+
+ shpnt->irq = config.interrupt;
+ shpnt->dma_channel = config.dma_channel;
+ shpnt->io_port = config.port_address;
+
+#if ULTRASTOR_MAX_CMDS > 1
+ config.mscp_free = ~0;
+#endif
+ /* Mark ICM and OGM free */
+ outb(0, addr + 0x16);
+ outb(0, addr + 0x1B);
+
+ /* Set local doorbell mask to disallow bus reset unless
+ ultrastor_bus_reset is true. */
+ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12));
+ outb(0x02, SYS_DOORBELL_MASK(addr+12));
+ printk("UltraStor driver version " VERSION ". Using %d SG lists.\n",
+ tpnt->sg_tablesize);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int ultrastor_detect(Scsi_Host_Template * tpnt)
+{
+ tpnt->proc_name = "ultrastor";
+ return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt);
+}
+
+static int ultrastor_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+static const char *ultrastor_info(struct Scsi_Host * shpnt)
+{
+ static char buf[64];
+
+ if (config.slot)
+ sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u",
+ config.slot, config.interrupt);
+ else if (config.subversion)
+ sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u",
+ config.port_address, (int)config.bios_segment,
+ config.interrupt);
+ else
+ sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u",
+ config.port_address, (int)config.bios_segment,
+ config.interrupt, config.dma_channel);
+ return buf;
+}
+
+static inline void build_sg_list(struct mscp *mscp, Scsi_Cmnd *SCpnt)
+{
+ struct scatterlist *sl;
+ long transfer_length = 0;
+ int i, max;
+
+ sl = (struct scatterlist *) SCpnt->request_buffer;
+ max = SCpnt->use_sg;
+ for (i = 0; i < max; i++) {
+ mscp->sglist[i].address = isa_page_to_bus(sl[i].page) + sl[i].offset;
+ mscp->sglist[i].num_bytes = sl[i].length;
+ transfer_length += sl[i].length;
+ }
+ mscp->number_of_sg_list = max;
+ mscp->transfer_data = isa_virt_to_bus(mscp->sglist);
+ /* ??? May not be necessary. Docs are unclear as to whether transfer
+ length field is ignored or whether it should be set to the total
+ number of bytes of the transfer. */
+ mscp->transfer_data_length = transfer_length;
+}
+
+static int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ struct mscp *my_mscp;
+#if ULTRASTOR_MAX_CMDS > 1
+ int mscp_index;
+#endif
+ unsigned int status;
+
+ /* Next test is for debugging; "can't happen" */
+ if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0)
+ panic("ultrastor_queuecommand: no free MSCP\n");
+ mscp_index = find_and_clear_bit_16(&config.mscp_free);
+
+ /* Has the command been aborted? */
+ if (xchgb(0xff, &config.aborted[mscp_index]) != 0)
+ {
+ status = DID_ABORT << 16;
+ goto aborted;
+ }
+
+ my_mscp = &config.mscp[mscp_index];
+
+ *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3);
+
+ /* Tape drives don't work properly if the cache is used. The SCSI
+ READ command for a tape doesn't have a block offset, and the adapter
+ incorrectly assumes that all reads from the tape read the same
+ blocks. Results will depend on read buffer size and other disk
+ activity.
+
+ ??? Which other device types should never use the cache? */
+ my_mscp->ca = SCpnt->device->type != TYPE_TAPE;
+ my_mscp->target_id = SCpnt->device->id;
+ my_mscp->ch_no = 0;
+ my_mscp->lun = SCpnt->device->lun;
+ if (SCpnt->use_sg) {
+ /* Set scatter/gather flag in SCSI command packet */
+ my_mscp->sg = TRUE;
+ build_sg_list(my_mscp, SCpnt);
+ } else {
+ /* Unset scatter/gather flag in SCSI command packet */
+ my_mscp->sg = FALSE;
+ my_mscp->transfer_data = isa_virt_to_bus(SCpnt->request_buffer);
+ my_mscp->transfer_data_length = SCpnt->request_bufflen;
+ }
+ my_mscp->command_link = 0; /*???*/
+ my_mscp->scsi_command_link_id = 0; /*???*/
+ my_mscp->length_of_sense_byte = sizeof SCpnt->sense_buffer;
+ my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len;
+ memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs);
+ my_mscp->adapter_status = 0;
+ my_mscp->target_status = 0;
+ my_mscp->sense_data = isa_virt_to_bus(&SCpnt->sense_buffer);
+ my_mscp->done = done;
+ my_mscp->SCint = SCpnt;
+ SCpnt->host_scribble = (unsigned char *)my_mscp;
+
+ /* Find free OGM slot. On 24F, look for OGM status byte == 0.
+ On 14F and 34F, wait for local interrupt pending flag to clear.
+
+ FIXME: now we are using new_eh we should punt here and let the
+ midlayer sort it out */
+
+retry:
+ if (config.slot)
+ while (inb(config.ogm_address - 1) != 0 && config.aborted[mscp_index] == 0xff)
+ barrier();
+
+ /* else??? */
+
+ while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) && config.aborted[mscp_index] == 0xff)
+ barrier();
+
+ /* To avoid race conditions, keep the code to write to the adapter
+ atomic. This simplifies the abort code. Right now the
+ scsi mid layer has the host_lock already held
+ */
+
+ if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1))
+ goto retry;
+
+ status = xchgb(0, &config.aborted[mscp_index]);
+ if (status != 0xff) {
+
+#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)
+ printk("USx4F: queuecommand: aborted\n");
+#if ULTRASTOR_MAX_CMDS > 1
+ log_ultrastor_abort(&config, mscp_index);
+#endif
+#endif
+ status <<= 16;
+
+ aborted:
+ set_bit(mscp_index, &config.mscp_free);
+ /* If the driver queues commands, call the done proc here. Otherwise
+ return an error. */
+#if ULTRASTOR_MAX_CMDS > 1
+ SCpnt->result = status;
+ done(SCpnt);
+ return 0;
+#else
+ return status;
+#endif
+ }
+
+ /* Store pointer in OGM address bytes */
+ outl(isa_virt_to_bus(my_mscp), config.ogm_address);
+
+ /* Issue OGM interrupt */
+ if (config.slot) {
+ /* Write OGM command register on 24F */
+ outb(1, config.ogm_address - 1);
+ outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));
+ } else {
+ outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address));
+ }
+
+#if (ULTRASTOR_DEBUG & UD_COMMAND)
+ printk("USx4F: queuecommand: returning\n");
+#endif
+
+ return 0;
+}
+
+/* This code must deal with 2 cases:
+
+ 1. The command has not been written to the OGM. In this case, set
+ the abort flag and return.
+
+ 2. The command has been written to the OGM and is stuck somewhere in
+ the adapter.
+
+ 2a. On a 24F, ask the adapter to abort the command. It will interrupt
+ when it does.
+
+ 2b. Call the command's done procedure.
+
+ */
+
+static int ultrastor_abort(Scsi_Cmnd *SCpnt)
+{
+#if ULTRASTOR_DEBUG & UD_ABORT
+ char out[108];
+ unsigned char icm_status = 0, ogm_status = 0;
+ unsigned int icm_addr = 0, ogm_addr = 0;
+#endif
+ unsigned int mscp_index;
+ unsigned char old_aborted;
+ unsigned long flags;
+ void (*done)(Scsi_Cmnd *);
+ struct Scsi_Host *host = SCpnt->device->host;
+
+ if(config.slot)
+ return FAILED; /* Do not attempt an abort for the 24f */
+
+ /* Simple consistency checking */
+ if(!SCpnt->host_scribble)
+ return FAILED;
+
+ mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp;
+ if (mscp_index >= ULTRASTOR_MAX_CMDS)
+ panic("Ux4F aborting invalid MSCP");
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ if (config.slot)
+ {
+ int port0 = (config.slot << 12) | 0xc80;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ strcpy(out, "OGM %d:%x ICM %d:%x ports: ");
+ for (i = 0; i < 16; i++)
+ {
+ unsigned char p = inb(port0 + i);
+ out[28 + i * 3] = "0123456789abcdef"[p >> 4];
+ out[29 + i * 3] = "0123456789abcdef"[p & 15];
+ out[30 + i * 3] = ' ';
+ }
+ out[28 + i * 3] = '\n';
+ out[29 + i * 3] = 0;
+ ogm_status = inb(port0 + 22);
+ ogm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 23));
+ icm_status = inb(port0 + 27);
+ icm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 28));
+ spin_lock_irqsave(host->host_lock, flags);
+ }
+
+ /* First check to see if an interrupt is pending. I suspect the SiS
+ chipset loses interrupts. (I also suspect is mangles data, but
+ one bug at a time... */
+ if (config.slot ? inb(config.icm_address - 1) == 2 :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+ {
+ printk("Ux4F: abort while completed command pending\n");
+
+ spin_lock_irqsave(host->host_lock, flags);
+ /* FIXME: Ewww... need to think about passing host around properly */
+ ultrastor_interrupt(0, NULL, NULL);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return SUCCESS;
+ }
+#endif
+
+ old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]);
+
+ /* aborted == 0xff is the signal that queuecommand has not yet sent
+ the command. It will notice the new abort flag and fail. */
+ if (old_aborted == 0xff)
+ return SUCCESS;
+
+ /* On 24F, send an abort MSCP request. The adapter will interrupt
+ and the interrupt handler will call done. */
+ if (config.slot && inb(config.ogm_address - 1) == 0)
+ {
+ unsigned long flags;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ outl(isa_virt_to_bus(&config.mscp[mscp_index]), config.ogm_address);
+ udelay(8);
+ outb(0x80, config.ogm_address - 1);
+ outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));
+#if ULTRASTOR_DEBUG & UD_ABORT
+ log_ultrastor_abort(&config, mscp_index);
+ printk(out, ogm_status, ogm_addr, icm_status, icm_addr);
+#endif
+ spin_unlock_irqrestore(host->host_lock, flags);
+ /* FIXME: add a wait for the abort to complete */
+ return SUCCESS;
+ }
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ log_ultrastor_abort(&config, mscp_index);
+#endif
+
+ /* Can't request a graceful abort. Either this is not a 24F or
+ the OGM is busy. Don't free the command -- the adapter might
+ still be using it. Setting SCint = 0 causes the interrupt
+ handler to ignore the command. */
+
+ /* FIXME - devices that implement soft resets will still be running
+ the command after a bus reset. We would probably rather leave
+ the command in the queue. The upper level code will automatically
+ leave the command in the active state instead of requeueing it. ERY */
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ if (config.mscp[mscp_index].SCint != SCpnt)
+ printk("abort: command mismatch, %p != %p\n",
+ config.mscp[mscp_index].SCint, SCpnt);
+#endif
+ if (config.mscp[mscp_index].SCint == 0)
+ return SCSI_ABORT_NOT_RUNNING;
+
+ if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort");
+ config.mscp[mscp_index].SCint = NULL;
+ done = config.mscp[mscp_index].done;
+ config.mscp[mscp_index].done = NULL;
+ SCpnt->result = DID_ABORT << 16;
+
+ /* Take the host lock to guard against scsi layer re-entry */
+ spin_lock_irqsave(host->host_lock, flags);
+ done(SCpnt);
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ /* Need to set a timeout here in case command never completes. */
+ return SUCCESS;
+}
+
+static int ultrastor_host_reset(Scsi_Cmnd * SCpnt)
+{
+ unsigned long flags;
+ int i;
+ struct Scsi_Host *host = SCpnt->device->host;
+
+#if (ULTRASTOR_DEBUG & UD_RESET)
+ printk("US14F: reset: called\n");
+#endif
+
+ if(config.slot)
+ return FAILED;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ /* Reset the adapter and SCSI bus. The SCSI bus reset can be
+ inhibited by clearing ultrastor_bus_reset before probe. */
+ outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address));
+ if (config.slot)
+ {
+ outb(0, config.ogm_address - 1);
+ outb(0, config.icm_address - 1);
+ }
+
+#if ULTRASTOR_MAX_CMDS == 1
+ if (config.mscp_busy && config.mscp->done && config.mscp->SCint)
+ {
+ config.mscp->SCint->result = DID_RESET << 16;
+ config.mscp->done(config.mscp->SCint);
+ }
+ config.mscp->SCint = 0;
+#else
+ for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)
+ {
+ if (! (config.mscp_free & (1 << i)) &&
+ config.mscp[i].done && config.mscp[i].SCint)
+ {
+ config.mscp[i].SCint->result = DID_RESET << 16;
+ config.mscp[i].done(config.mscp[i].SCint);
+ config.mscp[i].done = NULL;
+ }
+ config.mscp[i].SCint = NULL;
+ }
+#endif
+
+ /* FIXME - if the device implements soft resets, then the command
+ will still be running. ERY
+
+ Even bigger deal with new_eh!
+ */
+
+ memset((unsigned char *)config.aborted, 0, sizeof config.aborted);
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = 0;
+#else
+ config.mscp_free = ~0;
+#endif
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return SCSI_RESET_SUCCESS;
+
+}
+
+int ultrastor_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int * dkinfo)
+{
+ int size = capacity;
+ unsigned int s = config.heads * config.sectors;
+
+ dkinfo[0] = config.heads;
+ dkinfo[1] = config.sectors;
+ dkinfo[2] = size / s; /* Ignore partial cylinders */
+#if 0
+ if (dkinfo[2] > 1024)
+ dkinfo[2] = 1024;
+#endif
+ return 0;
+}
+
+static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int status;
+#if ULTRASTOR_MAX_CMDS > 1
+ unsigned int mscp_index;
+#endif
+ struct mscp *mscp;
+ void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCtmp;
+
+#if ULTRASTOR_MAX_CMDS == 1
+ mscp = &config.mscp[0];
+#else
+ mscp = (struct mscp *)isa_bus_to_virt(inl(config.icm_address));
+ mscp_index = mscp - config.mscp;
+ if (mscp_index >= ULTRASTOR_MAX_CMDS) {
+ printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp);
+ /* A command has been lost. Reset and report an error
+ for all commands. */
+ ultrastor_host_reset(dev_id);
+ return;
+ }
+#endif
+
+ /* Clean ICM slot (set ICMINT bit to 0) */
+ if (config.slot) {
+ unsigned char icm_status = inb(config.icm_address - 1);
+#if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT)
+ if (icm_status != 1 && icm_status != 2)
+ printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status,
+ mscp_index, (unsigned int) mscp);
+#endif
+ /* The manual says clear interrupt then write 0 to ICM status.
+ This seems backwards, but I'll do it anyway. --jfc */
+ outb(2, SYS_DOORBELL_INTR(config.doorbell_address));
+ outb(0, config.icm_address - 1);
+ if (icm_status == 4) {
+ printk("UltraStor abort command failed\n");
+ return;
+ }
+ if (icm_status == 3) {
+ void (*done)(Scsi_Cmnd *) = mscp->done;
+ if (done) {
+ mscp->done = NULL;
+ mscp->SCint->result = DID_ABORT << 16;
+ done(mscp->SCint);
+ }
+ return;
+ }
+ } else {
+ outb(1, SYS_DOORBELL_INTR(config.doorbell_address));
+ }
+
+ SCtmp = mscp->SCint;
+ mscp->SCint = NULL;
+
+ if (SCtmp == 0)
+ {
+#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)
+ printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp);
+#endif
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = FALSE;
+#else
+ set_bit(mscp_index, &config.mscp_free);
+#endif
+ config.aborted[mscp_index] = 0;
+ return;
+ }
+
+ /* Save done locally and zero before calling. This is needed as
+ once we call done, we may get another command queued before this
+ interrupt service routine can return. */
+ done = mscp->done;
+ mscp->done = NULL;
+
+ /* Let the higher levels know that we're done */
+ switch (mscp->adapter_status)
+ {
+ case 0:
+ status = DID_OK << 16;
+ break;
+ case 0x01: /* invalid command */
+ case 0x02: /* invalid parameters */
+ case 0x03: /* invalid data list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ case 0x84: /* SCSI bus abort */
+ status = DID_ABORT << 16;
+ break;
+ case 0x91:
+ status = DID_TIME_OUT << 16;
+ break;
+ }
+
+ SCtmp->result = status | mscp->target_status;
+
+ SCtmp->host_scribble = NULL;
+
+ /* Free up mscp block for next command */
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = FALSE;
+#else
+ set_bit(mscp_index, &config.mscp_free);
+#endif
+
+#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)
+ if (config.aborted[mscp_index])
+ printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n",
+ mscp_index, (unsigned int) mscp, config.aborted[mscp_index]);
+#endif
+ config.aborted[mscp_index] = 0;
+
+ if (done)
+ done(SCtmp);
+ else
+ printk("US14F: interrupt: unexpected interrupt\n");
+
+ if (config.slot ? inb(config.icm_address - 1) :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)
+ printk("Ux4F: multiple commands completed\n");
+#else
+ ;
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
+ printk("USx4F: interrupt: returning\n");
+#endif
+}
+
+static irqreturn_t do_ultrastor_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ ultrastor_interrupt(irq, dev_id, regs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+MODULE_LICENSE("GPL");
+
+static Scsi_Host_Template driver_template = {
+ .name = "UltraStor 14F/24F/34F",
+ .detect = ultrastor_detect,
+ .release = ultrastor_release,
+ .info = ultrastor_info,
+ .queuecommand = ultrastor_queuecommand,
+ .eh_abort_handler = ultrastor_abort,
+ .eh_host_reset_handler = ultrastor_host_reset,
+ .bios_param = ultrastor_biosparam,
+ .can_queue = ULTRASTOR_MAX_CMDS,
+ .sg_tablesize = ULTRASTOR_14F_MAX_SG,
+ .cmd_per_lun = ULTRASTOR_MAX_CMDS_PER_LUN,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h
new file mode 100644
index 000000000000..0a0f8df9e871
--- /dev/null
+++ b/drivers/scsi/ultrastor.h
@@ -0,0 +1,79 @@
+/*
+ * ultrastor.c (C) 1991 David B. Gentzel
+ * Low-level scsi driver for UltraStor 14F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
+ * 24F support by John F. Carr (jfc@athena.mit.edu)
+ * John's work modified by Caleb Epstein (cae@jpmorgan.com) and
+ * Eric Youngdale (eric@tantalus.nrl.navy.mil).
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+#ifndef _ULTRASTOR_H
+#define _ULTRASTOR_H
+
+static int ultrastor_detect(Scsi_Host_Template *);
+static const char *ultrastor_info(struct Scsi_Host * shpnt);
+static int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int ultrastor_abort(Scsi_Cmnd *);
+static int ultrastor_host_reset(Scsi_Cmnd *);
+static int ultrastor_biosparam(struct scsi_device *, struct block_device *, sector_t, int *);
+
+
+#define ULTRASTOR_14F_MAX_SG 16
+#define ULTRASTOR_24F_MAX_SG 33
+
+#define ULTRASTOR_MAX_CMDS_PER_LUN 5
+#define ULTRASTOR_MAX_CMDS 16
+
+#define ULTRASTOR_24F_PORT 0xC80
+
+
+#ifdef ULTRASTOR_PRIVATE
+
+#define UD_ABORT 0x0001
+#define UD_COMMAND 0x0002
+#define UD_DETECT 0x0004
+#define UD_INTERRUPT 0x0008
+#define UD_RESET 0x0010
+#define UD_MULTI_CMD 0x0020
+#define UD_CSIR 0x0040
+#define UD_ERROR 0x0080
+
+/* #define PORT_OVERRIDE 0x330 */
+
+/* Values for the PRODUCT_ID ports for the 14F */
+#define US14F_PRODUCT_ID_0 0x56
+#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */
+
+#define US24F_PRODUCT_ID_0 0x56
+#define US24F_PRODUCT_ID_1 0x63
+#define US24F_PRODUCT_ID_2 0x02
+
+/* Subversion values */
+#define U14F 0
+#define U34F 1
+
+/* MSCP field values */
+
+/* Opcode */
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+
+/* Date Transfer Direction */
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+
+/* Host Adapter command subcodes */
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#endif
+
+#endif
diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c
new file mode 100644
index 000000000000..5754445fb36a
--- /dev/null
+++ b/drivers/scsi/wd33c93.c
@@ -0,0 +1,2077 @@
+/*
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * john@geolog.com
+ * jshiffle@netcom.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Drew Eckhardt's excellent 'Generic NCR5380' sources from Linux-PC
+ * provided much of the inspiration and some of the code for this
+ * driver. Everything I know about Amiga DMA was gleaned from careful
+ * reading of Hamish Mcdonald's original wd33c93 driver; in fact, I
+ * borrowed shamelessly from all over that source. Thanks Hamish!
+ *
+ * _This_ driver is (I feel) an improvement over the old one in
+ * several respects:
+ *
+ * - Target Disconnection/Reconnection is now supported. Any
+ * system with more than one device active on the SCSI bus
+ * will benefit from this. The driver defaults to what I
+ * call 'adaptive disconnect' - meaning that each command
+ * is evaluated individually as to whether or not it should
+ * be run with the option to disconnect/reselect (if the
+ * device chooses), or as a "SCSI-bus-hog".
+ *
+ * - Synchronous data transfers are now supported. Because of
+ * a few devices that choke after telling the driver that
+ * they can do sync transfers, we don't automatically use
+ * this faster protocol - it can be enabled via the command-
+ * line on a device-by-device basis.
+ *
+ * - Runtime operating parameters can now be specified through
+ * the 'amiboot' or the 'insmod' command line. For amiboot do:
+ * "amiboot [usual stuff] wd33c93=blah,blah,blah"
+ * The defaults should be good for most people. See the comment
+ * for 'setup_strings' below for more details.
+ *
+ * - The old driver relied exclusively on what the Western Digital
+ * docs call "Combination Level 2 Commands", which are a great
+ * idea in that the CPU is relieved of a lot of interrupt
+ * overhead. However, by accepting a certain (user-settable)
+ * amount of additional interrupts, this driver achieves
+ * better control over the SCSI bus, and data transfers are
+ * almost as fast while being much easier to define, track,
+ * and debug.
+ *
+ *
+ * TODO:
+ * more speed. linked commands.
+ *
+ *
+ * People with bug reports, wish-lists, complaints, comments,
+ * or improvements are asked to pah-leeez email me (John Shifflett)
+ * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get
+ * this thing into as good a shape as possible, and I'm positive
+ * there are lots of lurking bugs and "Stupid Places".
+ *
+ * Updates:
+ *
+ * Added support for pre -A chips, which don't have advanced features
+ * and will generate CSR_RESEL rather than CSR_RESEL_AM.
+ * Richard Hirst <richard@sleepie.demon.co.uk> August 2000
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "wd33c93.h"
+
+
+#define WD33C93_VERSION "1.26"
+#define WD33C93_DATE "22/Feb/2003"
+
+MODULE_AUTHOR("John Shifflett");
+MODULE_DESCRIPTION("Generic WD33C93 SCSI driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with an amiboot or insmod command-line,
+ * those settings are combined with 'setup_args[]'. Note that amiboot
+ * command-lines are prefixed with "wd33c93=" while insmod uses a
+ * "setup_strings=" prefix. The driver recognizes the following keywords
+ * (lower case required) and arguments:
+ *
+ * - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with
+ * the 7 possible SCSI devices. Set a bit to negotiate for
+ * asynchronous transfers on that device. To maintain
+ * backwards compatibility, a command-line such as
+ * "wd33c93=255" will be automatically translated to
+ * "wd33c93=nosync:0xff".
+ * - nodma:x -x = 1 to disable DMA, x = 0 to enable it. Argument is
+ * optional - if not present, same as "nodma:1".
+ * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer
+ * period. Default is 500; acceptable values are 250 - 1000.
+ * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them.
+ * x = 1 does 'adaptive' disconnects, which is the default
+ * and generally the best choice.
+ * - debug:x -If 'DEBUGGING_ON' is defined, x is a bit mask that causes
+ * various types of debug output to printed - see the DB_xxx
+ * defines in wd33c93.h
+ * - clock:x -x = clock input in MHz for WD33c93 chip. Normal values
+ * would be from 8 through 20. Default is 8.
+ * - next -No argument. Used to separate blocks of keywords when
+ * there's more than one host adapter in the system.
+ *
+ * Syntax Notes:
+ * - Numeric arguments can be decimal or the '0x' form of hex notation. There
+ * _must_ be a colon between a keyword and its numeric argument, with no
+ * spaces.
+ * - Keywords are separated by commas, no spaces, in the standard kernel
+ * command-line manner.
+ * - A keyword in the 'nth' comma-separated command-line member will overwrite
+ * the 'nth' element of setup_args[]. A blank command-line member (in
+ * other words, a comma with no preceding keyword) will _not_ overwrite
+ * the corresponding setup_args[] element.
+ * - If a keyword is used more than once, the first one applies to the first
+ * SCSI host found, the second to the second card, etc, unless the 'next'
+ * keyword is used to change the order.
+ *
+ * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'):
+ * - wd33c93=nosync:255
+ * - wd33c93=nodma
+ * - wd33c93=nodma:1
+ * - wd33c93=disconnect:2,nosync:0x08,period:250
+ * - wd33c93=debug:0x1c
+ */
+
+/* Normally, no defaults are specified */
+static char *setup_args[] = { "", "", "", "", "", "", "", "", "" };
+
+static char *setup_strings;
+module_param(setup_strings, charp, 0);
+
+static void wd33c93_execute(struct Scsi_Host *instance);
+
+#ifdef CONFIG_WD33C93_PIO
+static inline uchar
+read_wd33c93(const wd33c93_regs regs, uchar reg_num)
+{
+ uchar data;
+
+ outb(reg_num, regs.SASR);
+ data = inb(regs.SCMD);
+ return data;
+}
+
+static inline unsigned long
+read_wd33c93_count(const wd33c93_regs regs)
+{
+ unsigned long value;
+
+ outb(WD_TRANSFER_COUNT_MSB, regs.SASR);
+ value = inb(regs.SCMD) << 16;
+ value |= inb(regs.SCMD) << 8;
+ value |= inb(regs.SCMD);
+ return value;
+}
+
+static inline uchar
+read_aux_stat(const wd33c93_regs regs)
+{
+ return inb(regs.SASR);
+}
+
+static inline void
+write_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value)
+{
+ outb(reg_num, regs.SASR);
+ outb(value, regs.SCMD);
+}
+
+static inline void
+write_wd33c93_count(const wd33c93_regs regs, unsigned long value)
+{
+ outb(WD_TRANSFER_COUNT_MSB, regs.SASR);
+ outb((value >> 16) & 0xff, regs.SCMD);
+ outb((value >> 8) & 0xff, regs.SCMD);
+ outb( value & 0xff, regs.SCMD);
+}
+
+#define write_wd33c93_cmd(regs, cmd) \
+ write_wd33c93((regs), WD_COMMAND, (cmd))
+
+static inline void
+write_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[])
+{
+ int i;
+
+ outb(WD_CDB_1, regs.SASR);
+ for (i=0; i<len; i++)
+ outb(cmnd[i], regs.SCMD);
+}
+
+#else /* CONFIG_WD33C93_PIO */
+static inline uchar
+read_wd33c93(const wd33c93_regs regs, uchar reg_num)
+{
+ *regs.SASR = reg_num;
+ mb();
+ return (*regs.SCMD);
+}
+
+static unsigned long
+read_wd33c93_count(const wd33c93_regs regs)
+{
+ unsigned long value;
+
+ *regs.SASR = WD_TRANSFER_COUNT_MSB;
+ mb();
+ value = *regs.SCMD << 16;
+ value |= *regs.SCMD << 8;
+ value |= *regs.SCMD;
+ mb();
+ return value;
+}
+
+static inline uchar
+read_aux_stat(const wd33c93_regs regs)
+{
+ return *regs.SASR;
+}
+
+static inline void
+write_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value)
+{
+ *regs.SASR = reg_num;
+ mb();
+ *regs.SCMD = value;
+ mb();
+}
+
+static void
+write_wd33c93_count(const wd33c93_regs regs, unsigned long value)
+{
+ *regs.SASR = WD_TRANSFER_COUNT_MSB;
+ mb();
+ *regs.SCMD = value >> 16;
+ *regs.SCMD = value >> 8;
+ *regs.SCMD = value;
+ mb();
+}
+
+static inline void
+write_wd33c93_cmd(const wd33c93_regs regs, uchar cmd)
+{
+ *regs.SASR = WD_COMMAND;
+ mb();
+ *regs.SCMD = cmd;
+ mb();
+}
+
+static inline void
+write_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[])
+{
+ int i;
+
+ *regs.SASR = WD_CDB_1;
+ for (i = 0; i < len; i++)
+ *regs.SCMD = cmnd[i];
+}
+#endif /* CONFIG_WD33C93_PIO */
+
+static inline uchar
+read_1_byte(const wd33c93_regs regs)
+{
+ uchar asr;
+ uchar x = 0;
+
+ write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO | 0x80);
+ do {
+ asr = read_aux_stat(regs);
+ if (asr & ASR_DBR)
+ x = read_wd33c93(regs, WD_DATA);
+ } while (!(asr & ASR_INT));
+ return x;
+}
+
+static struct sx_period sx_table[] = {
+ {1, 0x20},
+ {252, 0x20},
+ {376, 0x30},
+ {500, 0x40},
+ {624, 0x50},
+ {752, 0x60},
+ {876, 0x70},
+ {1000, 0x00},
+ {0, 0}
+};
+
+static int
+round_period(unsigned int period)
+{
+ int x;
+
+ for (x = 1; sx_table[x].period_ns; x++) {
+ if ((period <= sx_table[x - 0].period_ns) &&
+ (period > sx_table[x - 1].period_ns)) {
+ return x;
+ }
+ }
+ return 7;
+}
+
+static uchar
+calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+ uchar result;
+
+ period *= 4; /* convert SDTR code to ns */
+ result = sx_table[round_period(period)].reg_value;
+ result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;
+ return result;
+}
+
+int
+wd33c93_queuecommand(struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct WD33C93_hostdata *hostdata;
+ struct scsi_cmnd *tmp;
+
+ hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata;
+
+ DB(DB_QUEUE_COMMAND,
+ printk("Q-%d-%02x-%ld( ", cmd->device->id, cmd->cmnd[0], cmd->pid))
+
+/* Set up a few fields in the scsi_cmnd structure for our own use:
+ * - host_scribble is the pointer to the next cmd in the input queue
+ * - scsi_done points to the routine we call when a cmd is finished
+ * - result is what you'd expect
+ */
+ cmd->host_scribble = NULL;
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+/* We use the Scsi_Pointer structure that's included with each command
+ * as a scratchpad (as it's intended to be used!). The handy thing about
+ * the SCp.xxx fields is that they're always associated with a given
+ * cmd, and are preserved across disconnect-reselect. This means we
+ * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
+ * if we keep all the critical pointers and counters in SCp:
+ * - SCp.ptr is the pointer into the RAM buffer
+ * - SCp.this_residual is the size of that buffer
+ * - SCp.buffer points to the current scatter-gather buffer
+ * - SCp.buffers_residual tells us how many S.G. buffers there are
+ * - SCp.have_data_in is not used
+ * - SCp.sent_command is not used
+ * - SCp.phase records this command's SRCID_ER bit setting
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) +
+ cmd->SCp.buffer->offset;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+
+/* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
+
+ cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
+
+ /*
+ * Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE
+ * commands are added to the head of the queue so that the desired
+ * sense data is not lost before REQUEST_SENSE executes.
+ */
+
+ spin_lock_irq(&hostdata->lock);
+
+ if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ cmd->host_scribble = (uchar *) hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ } else { /* find the end of the queue */
+ for (tmp = (struct scsi_cmnd *) hostdata->input_Q;
+ tmp->host_scribble;
+ tmp = (struct scsi_cmnd *) tmp->host_scribble) ;
+ tmp->host_scribble = (uchar *) cmd;
+ }
+
+/* We know that there's at least one command in 'input_Q' now.
+ * Go see if any of them are runnable!
+ */
+
+ wd33c93_execute(cmd->device->host);
+
+ DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->pid))
+
+ spin_unlock_irq(&hostdata->lock);
+ return 0;
+}
+
+/*
+ * This routine attempts to start a scsi command. If the host_card is
+ * already connected, we give up immediately. Otherwise, look through
+ * the input_Q, using the first command we find that's intended
+ * for a currently non-busy target/lun.
+ *
+ * wd33c93_execute() is always called with interrupts disabled or from
+ * the wd33c93_intr itself, which means that a wd33c93 interrupt
+ * cannot occur while we are in here.
+ */
+static void
+wd33c93_execute(struct Scsi_Host *instance)
+{
+ struct WD33C93_hostdata *hostdata =
+ (struct WD33C93_hostdata *) instance->hostdata;
+ const wd33c93_regs regs = hostdata->regs;
+ struct scsi_cmnd *cmd, *prev;
+
+ DB(DB_EXECUTE, printk("EX("))
+ if (hostdata->selecting || hostdata->connected) {
+ DB(DB_EXECUTE, printk(")EX-0 "))
+ return;
+ }
+
+ /*
+ * Search through the input_Q for a command destined
+ * for an idle target/lun.
+ */
+
+ cmd = (struct scsi_cmnd *) hostdata->input_Q;
+ prev = 0;
+ while (cmd) {
+ if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)))
+ break;
+ prev = cmd;
+ cmd = (struct scsi_cmnd *) cmd->host_scribble;
+ }
+
+ /* quit if queue empty or all possible targets are busy */
+
+ if (!cmd) {
+ DB(DB_EXECUTE, printk(")EX-1 "))
+ return;
+ }
+
+ /* remove command from queue */
+
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ else
+ hostdata->input_Q = (struct scsi_cmnd *) cmd->host_scribble;
+
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[cmd->device->id]++;
+#endif
+
+ /*
+ * Start the selection process
+ */
+
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);
+ else
+ write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
+
+/* Now we need to figure out whether or not this command is a good
+ * candidate for disconnect/reselect. We guess to the best of our
+ * ability, based on a set of hierarchical rules. When several
+ * devices are operating simultaneously, disconnects are usually
+ * an advantage. In a single device system, or if only 1 device
+ * is being accessed, transfers usually go faster if disconnects
+ * are not allowed:
+ *
+ * + Commands should NEVER disconnect if hostdata->disconnect =
+ * DIS_NEVER (this holds for tape drives also), and ALWAYS
+ * disconnect if hostdata->disconnect = DIS_ALWAYS.
+ * + Tape drive commands should always be allowed to disconnect.
+ * + Disconnect should be allowed if disconnected_Q isn't empty.
+ * + Commands should NOT disconnect if input_Q is empty.
+ * + Disconnect should be allowed if there are commands in input_Q
+ * for a different target/lun. In this case, the other commands
+ * should be made disconnect-able, if not already.
+ *
+ * I know, I know - this code would flunk me out of any
+ * "C Programming 101" class ever offered. But it's easy
+ * to change around and experiment with for now.
+ */
+
+ cmd->SCp.phase = 0; /* assume no disconnect */
+ if (hostdata->disconnect == DIS_NEVER)
+ goto no;
+ if (hostdata->disconnect == DIS_ALWAYS)
+ goto yes;
+ if (cmd->device->type == 1) /* tape drive? */
+ goto yes;
+ if (hostdata->disconnected_Q) /* other commands disconnected? */
+ goto yes;
+ if (!(hostdata->input_Q)) /* input_Q empty? */
+ goto no;
+ for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev;
+ prev = (struct scsi_cmnd *) prev->host_scribble) {
+ if ((prev->device->id != cmd->device->id) ||
+ (prev->device->lun != cmd->device->lun)) {
+ for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev;
+ prev = (struct scsi_cmnd *) prev->host_scribble)
+ prev->SCp.phase = 1;
+ goto yes;
+ }
+ }
+
+ goto no;
+
+ yes:
+ cmd->SCp.phase = 1;
+
+#ifdef PROC_STATISTICS
+ hostdata->disc_allowed_cnt[cmd->device->id]++;
+#endif
+
+ no:
+
+ write_wd33c93(regs, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0));
+
+ write_wd33c93(regs, WD_TARGET_LUN, cmd->device->lun);
+ write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
+ hostdata->sync_xfer[cmd->device->id]);
+ hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
+
+ if ((hostdata->level2 == L2_NONE) ||
+ (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) {
+
+ /*
+ * Do a 'Select-With-ATN' command. This will end with
+ * one of the following interrupts:
+ * CSR_RESEL_AM: failure - can try again later.
+ * CSR_TIMEOUT: failure - give up.
+ * CSR_SELECT: success - proceed.
+ */
+
+ hostdata->selecting = cmd;
+
+/* Every target has its own synchronous transfer setting, kept in the
+ * sync_xfer array, and a corresponding status byte in sync_stat[].
+ * Each target's sync_stat[] entry is initialized to SX_UNSET, and its
+ * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET
+ * means that the parameters are undetermined as yet, and that we
+ * need to send an SDTR message to this device after selection is
+ * complete: We set SS_FIRST to tell the interrupt routine to do so.
+ * If we've been asked not to try synchronous transfers on this
+ * target (and _all_ luns within it), we'll still send the SDTR message
+ * later, but at that time we'll negotiate for async by specifying a
+ * sync fifo depth of 0.
+ */
+ if (hostdata->sync_stat[cmd->device->id] == SS_UNSET)
+ hostdata->sync_stat[cmd->device->id] = SS_FIRST;
+ hostdata->state = S_SELECTING;
+ write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN);
+ } else {
+
+ /*
+ * Do a 'Select-With-ATN-Xfer' command. This will end with
+ * one of the following interrupts:
+ * CSR_RESEL_AM: failure - can try again later.
+ * CSR_TIMEOUT: failure - give up.
+ * anything else: success - proceed.
+ */
+
+ hostdata->connected = cmd;
+ write_wd33c93(regs, WD_COMMAND_PHASE, 0);
+
+ /* copy command_descriptor_block into WD chip
+ * (take advantage of auto-incrementing)
+ */
+
+ write_wd33c93_cdb(regs, cmd->cmd_len, cmd->cmnd);
+
+ /* The wd33c93 only knows about Group 0, 1, and 5 commands when
+ * it's doing a 'select-and-transfer'. To be safe, we write the
+ * size of the CDB into the OWN_ID register for every case. This
+ * way there won't be problems with vendor-unique, audio, etc.
+ */
+
+ write_wd33c93(regs, WD_OWN_ID, cmd->cmd_len);
+
+ /* When doing a non-disconnect command with DMA, we can save
+ * ourselves a DATA phase interrupt later by setting everything
+ * up ahead of time.
+ */
+
+ if ((cmd->SCp.phase == 0) && (hostdata->no_dma == 0)) {
+ if (hostdata->dma_setup(cmd,
+ (cmd->sc_data_direction == DMA_TO_DEVICE) ?
+ DATA_OUT_DIR : DATA_IN_DIR))
+ write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */
+ else {
+ write_wd33c93_count(regs,
+ cmd->SCp.this_residual);
+ write_wd33c93(regs, WD_CONTROL,
+ CTRL_IDI | CTRL_EDI | CTRL_DMA);
+ hostdata->dma = D_DMA_RUNNING;
+ }
+ } else
+ write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */
+
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
+ }
+
+ /*
+ * Since the SCSI bus can handle only 1 connection at a time,
+ * we get out of here now. If the selection fails, or when
+ * the command disconnects, we'll come back to this routine
+ * to search the input_Q again...
+ */
+
+ DB(DB_EXECUTE,
+ printk("%s%ld)EX-2 ", (cmd->SCp.phase) ? "d:" : "", cmd->pid))
+}
+
+static void
+transfer_pio(const wd33c93_regs regs, uchar * buf, int cnt,
+ int data_in_dir, struct WD33C93_hostdata *hostdata)
+{
+ uchar asr;
+
+ DB(DB_TRANSFER,
+ printk("(%p,%d,%s:", buf, cnt, data_in_dir ? "in" : "out"))
+
+ write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_wd33c93_count(regs, cnt);
+ write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO);
+ if (data_in_dir) {
+ do {
+ asr = read_aux_stat(regs);
+ if (asr & ASR_DBR)
+ *buf++ = read_wd33c93(regs, WD_DATA);
+ } while (!(asr & ASR_INT));
+ } else {
+ do {
+ asr = read_aux_stat(regs);
+ if (asr & ASR_DBR)
+ write_wd33c93(regs, WD_DATA, *buf++);
+ } while (!(asr & ASR_INT));
+ }
+
+ /* Note: we are returning with the interrupt UN-cleared.
+ * Since (presumably) an entire I/O operation has
+ * completed, the bus phase is probably different, and
+ * the interrupt routine will discover this when it
+ * responds to the uncleared int.
+ */
+
+}
+
+static void
+transfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd,
+ int data_in_dir)
+{
+ struct WD33C93_hostdata *hostdata;
+ unsigned long length;
+
+ hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata;
+
+/* Normally, you'd expect 'this_residual' to be non-zero here.
+ * In a series of scatter-gather transfers, however, this
+ * routine will usually be called with 'this_residual' equal
+ * to 0 and 'buffers_residual' non-zero. This means that a
+ * previous transfer completed, clearing 'this_residual', and
+ * now we need to setup the next scatter-gather buffer as the
+ * source or destination for THIS transfer.
+ */
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) +
+ cmd->SCp.buffer->offset;
+ }
+
+ write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
+ hostdata->sync_xfer[cmd->device->id]);
+
+/* 'hostdata->no_dma' is TRUE if we don't even want to try DMA.
+ * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns.
+ */
+
+ if (hostdata->no_dma || hostdata->dma_setup(cmd, data_in_dir)) {
+#ifdef PROC_STATISTICS
+ hostdata->pio_cnt++;
+#endif
+ transfer_pio(regs, (uchar *) cmd->SCp.ptr,
+ cmd->SCp.this_residual, data_in_dir, hostdata);
+ length = cmd->SCp.this_residual;
+ cmd->SCp.this_residual = read_wd33c93_count(regs);
+ cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+ }
+
+/* We are able to do DMA (in fact, the Amiga hardware is
+ * already going!), so start up the wd33c93 in DMA mode.
+ * We set 'hostdata->dma' = D_DMA_RUNNING so that when the
+ * transfer completes and causes an interrupt, we're
+ * reminded to tell the Amiga to shut down its end. We'll
+ * postpone the updating of 'this_residual' and 'ptr'
+ * until then.
+ */
+
+ else {
+#ifdef PROC_STATISTICS
+ hostdata->dma_cnt++;
+#endif
+ write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);
+ write_wd33c93_count(regs, cmd->SCp.this_residual);
+
+ if ((hostdata->level2 >= L2_DATA) ||
+ (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
+ write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else
+ write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO);
+
+ hostdata->dma = D_DMA_RUNNING;
+ }
+}
+
+void
+wd33c93_intr(struct Scsi_Host *instance)
+{
+ struct WD33C93_hostdata *hostdata =
+ (struct WD33C93_hostdata *) instance->hostdata;
+ const wd33c93_regs regs = hostdata->regs;
+ struct scsi_cmnd *patch, *cmd;
+ uchar asr, sr, phs, id, lun, *ucp, msg;
+ unsigned long length, flags;
+
+ asr = read_aux_stat(regs);
+ if (!(asr & ASR_INT) || (asr & ASR_BSY))
+ return;
+
+ spin_lock_irqsave(&hostdata->lock, flags);
+
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt++;
+#endif
+
+ cmd = (struct scsi_cmnd *) hostdata->connected; /* assume we're connected */
+ sr = read_wd33c93(regs, WD_SCSI_STATUS); /* clear the interrupt */
+ phs = read_wd33c93(regs, WD_COMMAND_PHASE);
+
+ DB(DB_INTR, printk("{%02x:%02x-", asr, sr))
+
+/* After starting a DMA transfer, the next interrupt
+ * is guaranteed to be in response to completion of
+ * the transfer. Since the Amiga DMA hardware runs in
+ * in an open-ended fashion, it needs to be told when
+ * to stop; do that here if D_DMA_RUNNING is true.
+ * Also, we have to update 'this_residual' and 'ptr'
+ * based on the contents of the TRANSFER_COUNT register,
+ * in case the device decided to do an intermediate
+ * disconnect (a device may do this if it has to do a
+ * seek, or just to be nice and let other devices have
+ * some bus time during long transfers). After doing
+ * whatever is needed, we go on and service the WD3393
+ * interrupt normally.
+ */
+ if (hostdata->dma == D_DMA_RUNNING) {
+ DB(DB_TRANSFER,
+ printk("[%p/%d:", cmd->SCp.ptr, cmd->SCp.this_residual))
+ hostdata->dma_stop(cmd->device->host, cmd, 1);
+ hostdata->dma = D_DMA_OFF;
+ length = cmd->SCp.this_residual;
+ cmd->SCp.this_residual = read_wd33c93_count(regs);
+ cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+ DB(DB_TRANSFER,
+ printk("%p/%d]", cmd->SCp.ptr, cmd->SCp.this_residual))
+ }
+
+/* Respond to the specific WD3393 interrupt - there are quite a few! */
+ switch (sr) {
+ case CSR_TIMEOUT:
+ DB(DB_INTR, printk("TIMEOUT"))
+
+ if (hostdata->state == S_RUNNING_LEVEL2)
+ hostdata->connected = NULL;
+ else {
+ cmd = (struct scsi_cmnd *) hostdata->selecting; /* get a valid cmd */
+ hostdata->selecting = NULL;
+ }
+
+ cmd->result = DID_NO_CONNECT << 16;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ cmd->scsi_done(cmd);
+
+ /* From esp.c:
+ * There is a window of time within the scsi_done() path
+ * of execution where interrupts are turned back on full
+ * blast and left that way. During that time we could
+ * reconnect to a disconnected command, then we'd bomb
+ * out below. We could also end up executing two commands
+ * at _once_. ...just so you know why the restore_flags()
+ * is here...
+ */
+
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+
+/* We are not connected to a target - check to see if there
+ * are commands waiting to be executed.
+ */
+
+ wd33c93_execute(instance);
+ break;
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+ case CSR_SELECT:
+ DB(DB_INTR, printk("SELECT"))
+ hostdata->connected = cmd =
+ (struct scsi_cmnd *) hostdata->selecting;
+ hostdata->selecting = NULL;
+
+ /* construct an IDENTIFY message with correct disconnect bit */
+
+ hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun);
+ if (cmd->SCp.phase)
+ hostdata->outgoing_msg[0] |= 0x40;
+
+ if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) {
+#ifdef SYNC_DEBUG
+ printk(" sending SDTR ");
+#endif
+
+ hostdata->sync_stat[cmd->device->id] = SS_WAITING;
+
+/* Tack on a 2nd message to ask about synchronous transfers. If we've
+ * been asked to do only asynchronous transfers on this device, we
+ * request a fifo depth of 0, which is equivalent to async - should
+ * solve the problems some people have had with GVP's Guru ROM.
+ */
+
+ hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[2] = 3;
+ hostdata->outgoing_msg[3] = EXTENDED_SDTR;
+ if (hostdata->no_sync & (1 << cmd->device->id)) {
+ hostdata->outgoing_msg[4] =
+ hostdata->default_sx_per / 4;
+ hostdata->outgoing_msg[5] = 0;
+ } else {
+ hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4;
+ hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF;
+ }
+ hostdata->outgoing_len = 6;
+ } else
+ hostdata->outgoing_len = 1;
+
+ hostdata->state = S_CONNECTED;
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ case CSR_XFER_DONE | PHS_DATA_IN:
+ case CSR_UNEXP | PHS_DATA_IN:
+ case CSR_SRV_REQ | PHS_DATA_IN:
+ DB(DB_INTR,
+ printk("IN-%d.%d", cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual))
+ transfer_bytes(regs, cmd, DATA_IN_DIR);
+ if (hostdata->state != S_RUNNING_LEVEL2)
+ hostdata->state = S_CONNECTED;
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ case CSR_XFER_DONE | PHS_DATA_OUT:
+ case CSR_UNEXP | PHS_DATA_OUT:
+ case CSR_SRV_REQ | PHS_DATA_OUT:
+ DB(DB_INTR,
+ printk("OUT-%d.%d", cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual))
+ transfer_bytes(regs, cmd, DATA_OUT_DIR);
+ if (hostdata->state != S_RUNNING_LEVEL2)
+ hostdata->state = S_CONNECTED;
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+ case CSR_XFER_DONE | PHS_COMMAND:
+ case CSR_UNEXP | PHS_COMMAND:
+ case CSR_SRV_REQ | PHS_COMMAND:
+ DB(DB_INTR, printk("CMND-%02x,%ld", cmd->cmnd[0], cmd->pid))
+ transfer_pio(regs, cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR,
+ hostdata);
+ hostdata->state = S_CONNECTED;
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ case CSR_XFER_DONE | PHS_STATUS:
+ case CSR_UNEXP | PHS_STATUS:
+ case CSR_SRV_REQ | PHS_STATUS:
+ DB(DB_INTR, printk("STATUS="))
+ cmd->SCp.Status = read_1_byte(regs);
+ DB(DB_INTR, printk("%02x", cmd->SCp.Status))
+ if (hostdata->level2 >= L2_BASIC) {
+ sr = read_wd33c93(regs, WD_SCSI_STATUS); /* clear interrupt */
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_wd33c93(regs, WD_COMMAND_PHASE, 0x50);
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
+ } else {
+ hostdata->state = S_CONNECTED;
+ }
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ case CSR_XFER_DONE | PHS_MESS_IN:
+ case CSR_UNEXP | PHS_MESS_IN:
+ case CSR_SRV_REQ | PHS_MESS_IN:
+ DB(DB_INTR, printk("MSG_IN="))
+
+ msg = read_1_byte(regs);
+ sr = read_wd33c93(regs, WD_SCSI_STATUS); /* clear interrupt */
+
+ hostdata->incoming_msg[hostdata->incoming_ptr] = msg;
+ if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE)
+ msg = EXTENDED_MESSAGE;
+ else
+ hostdata->incoming_ptr = 0;
+
+ cmd->SCp.Message = msg;
+ switch (msg) {
+
+ case COMMAND_COMPLETE:
+ DB(DB_INTR, printk("CCMP-%ld", cmd->pid))
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_PRE_CMP_DISC;
+ break;
+
+ case SAVE_POINTERS:
+ DB(DB_INTR, printk("SDP"))
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+
+ case RESTORE_POINTERS:
+ DB(DB_INTR, printk("RDP"))
+ if (hostdata->level2 >= L2_BASIC) {
+ write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else {
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+ case DISCONNECT:
+ DB(DB_INTR, printk("DIS"))
+ cmd->device->disconnect = 1;
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_PRE_TMP_DISC;
+ break;
+
+ case MESSAGE_REJECT:
+ DB(DB_INTR, printk("REJ"))
+#ifdef SYNC_DEBUG
+ printk("-REJ-");
+#endif
+ if (hostdata->sync_stat[cmd->device->id] == SS_WAITING)
+ hostdata->sync_stat[cmd->device->id] = SS_SET;
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+
+ case EXTENDED_MESSAGE:
+ DB(DB_INTR, printk("EXT"))
+
+ ucp = hostdata->incoming_msg;
+
+#ifdef SYNC_DEBUG
+ printk("%02x", ucp[hostdata->incoming_ptr]);
+#endif
+ /* Is this the last byte of the extended message? */
+
+ if ((hostdata->incoming_ptr >= 2) &&
+ (hostdata->incoming_ptr == (ucp[1] + 1))) {
+
+ switch (ucp[2]) { /* what's the EXTENDED code? */
+ case EXTENDED_SDTR:
+ id = calc_sync_xfer(ucp[3], ucp[4]);
+ if (hostdata->sync_stat[cmd->device->id] !=
+ SS_WAITING) {
+
+/* A device has sent an unsolicited SDTR message; rather than go
+ * through the effort of decoding it and then figuring out what
+ * our reply should be, we're just gonna say that we have a
+ * synchronous fifo depth of 0. This will result in asynchronous
+ * transfers - not ideal but so much easier.
+ * Actually, this is OK because it assures us that if we don't
+ * specifically ask for sync transfers, we won't do any.
+ */
+
+ write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ hostdata->outgoing_msg[0] =
+ EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 3;
+ hostdata->outgoing_msg[2] =
+ EXTENDED_SDTR;
+ hostdata->outgoing_msg[3] =
+ hostdata->default_sx_per /
+ 4;
+ hostdata->outgoing_msg[4] = 0;
+ hostdata->outgoing_len = 5;
+ hostdata->sync_xfer[cmd->device->id] =
+ calc_sync_xfer(hostdata->
+ default_sx_per
+ / 4, 0);
+ } else {
+ hostdata->sync_xfer[cmd->device->id] = id;
+ }
+#ifdef SYNC_DEBUG
+ printk("sync_xfer=%02x",
+ hostdata->sync_xfer[cmd->device->id]);
+#endif
+ hostdata->sync_stat[cmd->device->id] =
+ SS_SET;
+ write_wd33c93_cmd(regs,
+ WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ case EXTENDED_WDTR:
+ write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("sending WDTR ");
+ hostdata->outgoing_msg[0] =
+ EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 2;
+ hostdata->outgoing_msg[2] =
+ EXTENDED_WDTR;
+ hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */
+ hostdata->outgoing_len = 4;
+ write_wd33c93_cmd(regs,
+ WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ default:
+ write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk
+ ("Rejecting Unknown Extended Message(%02x). ",
+ ucp[2]);
+ hostdata->outgoing_msg[0] =
+ MESSAGE_REJECT;
+ hostdata->outgoing_len = 1;
+ write_wd33c93_cmd(regs,
+ WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ }
+ hostdata->incoming_ptr = 0;
+ }
+
+ /* We need to read more MESS_IN bytes for the extended message */
+
+ else {
+ hostdata->incoming_ptr++;
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+ default:
+ printk("Rejecting Unknown Message(%02x) ", msg);
+ write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+ hostdata->outgoing_len = 1;
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+ case CSR_SEL_XFER_DONE:
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
+ if (phs == 0x60) {
+ DB(DB_INTR, printk("SX-DONE-%ld", cmd->pid))
+ cmd->SCp.Message = COMMAND_COMPLETE;
+ lun = read_wd33c93(regs, WD_TARGET_LUN);
+ DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+ cmd->SCp.Status = lun;
+ if (cmd->cmnd[0] == REQUEST_SENSE
+ && cmd->SCp.Status != GOOD)
+ cmd->result =
+ (cmd->
+ result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result =
+ cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ wd33c93_execute(instance);
+ } else {
+ printk
+ ("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---",
+ asr, sr, phs, cmd->pid);
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ }
+ break;
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+ case CSR_SDP:
+ DB(DB_INTR, printk("SDP"))
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_wd33c93(regs, WD_COMMAND_PHASE, 0x41);
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ case CSR_XFER_DONE | PHS_MESS_OUT:
+ case CSR_UNEXP | PHS_MESS_OUT:
+ case CSR_SRV_REQ | PHS_MESS_OUT:
+ DB(DB_INTR, printk("MSG_OUT="))
+
+/* To get here, we've probably requested MESSAGE_OUT and have
+ * already put the correct bytes in outgoing_msg[] and filled
+ * in outgoing_len. We simply send them out to the SCSI bus.
+ * Sometimes we get MESSAGE_OUT phase when we're not expecting
+ * it - like when our SDTR message is rejected by a target. Some
+ * targets send the REJECT before receiving all of the extended
+ * message, and then seem to go back to MESSAGE_OUT for a byte
+ * or two. Not sure why, or if I'm doing something wrong to
+ * cause this to happen. Regardless, it seems that sending
+ * NOP messages in these situations results in no harm and
+ * makes everyone happy.
+ */
+ if (hostdata->outgoing_len == 0) {
+ hostdata->outgoing_len = 1;
+ hostdata->outgoing_msg[0] = NOP;
+ }
+ transfer_pio(regs, hostdata->outgoing_msg,
+ hostdata->outgoing_len, DATA_OUT_DIR, hostdata);
+ DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0]))
+ hostdata->outgoing_len = 0;
+ hostdata->state = S_CONNECTED;
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ case CSR_UNEXP_DISC:
+
+/* I think I've seen this after a request-sense that was in response
+ * to an error condition, but not sure. We certainly need to do
+ * something when we get this interrupt - the question is 'what?'.
+ * Let's think positively, and assume some command has finished
+ * in a legal manner (like a command that provokes a request-sense),
+ * so we treat it as a normal command-complete-disconnect.
+ */
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ return;
+ }
+ DB(DB_INTR, printk("UNEXP_DISC-%ld", cmd->pid))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result =
+ (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+ /* look above for comments on scsi_done() */
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ wd33c93_execute(instance);
+ break;
+
+ case CSR_DISC:
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
+ DB(DB_INTR, printk("DISC-%ld", cmd->pid))
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+ }
+ switch (hostdata->state) {
+ case S_PRE_CMP_DISC:
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->state = S_UNCONNECTED;
+ DB(DB_INTR, printk(":%d", cmd->SCp.Status))
+ if (cmd->cmnd[0] == REQUEST_SENSE
+ && cmd->SCp.Status != GOOD)
+ cmd->result =
+ (cmd->
+ result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result =
+ cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+ break;
+ case S_PRE_TMP_DISC:
+ case S_RUNNING_LEVEL2:
+ cmd->host_scribble = (uchar *) hostdata->disconnected_Q;
+ hostdata->disconnected_Q = cmd;
+ hostdata->connected = NULL;
+ hostdata->state = S_UNCONNECTED;
+
+#ifdef PROC_STATISTICS
+ hostdata->disc_done_cnt[cmd->device->id]++;
+#endif
+
+ break;
+ default:
+ printk("*** Unexpected DISCONNECT interrupt! ***");
+ hostdata->state = S_UNCONNECTED;
+ }
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ wd33c93_execute(instance);
+ break;
+
+ case CSR_RESEL_AM:
+ case CSR_RESEL:
+ DB(DB_INTR, printk("RESEL%s", sr == CSR_RESEL_AM ? "_AM" : ""))
+
+ /* Old chips (pre -A ???) don't have advanced features and will
+ * generate CSR_RESEL. In that case we have to extract the LUN the
+ * hard way (see below).
+ * First we have to make sure this reselection didn't
+ * happen during Arbitration/Selection of some other device.
+ * If yes, put losing command back on top of input_Q.
+ */
+ if (hostdata->level2 <= L2_NONE) {
+
+ if (hostdata->selecting) {
+ cmd = (struct scsi_cmnd *) hostdata->selecting;
+ hostdata->selecting = NULL;
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ cmd->host_scribble =
+ (uchar *) hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ }
+ }
+
+ else {
+
+ if (cmd) {
+ if (phs == 0x00) {
+ hostdata->busy[cmd->device->id] &=
+ ~(1 << cmd->device->lun);
+ cmd->host_scribble =
+ (uchar *) hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ } else {
+ printk
+ ("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---",
+ asr, sr, phs);
+ while (1)
+ printk("\r");
+ }
+ }
+
+ }
+
+ /* OK - find out which device reselected us. */
+
+ id = read_wd33c93(regs, WD_SOURCE_ID);
+ id &= SRCID_MASK;
+
+ /* and extract the lun from the ID message. (Note that we don't
+ * bother to check for a valid message here - I guess this is
+ * not the right way to go, but...)
+ */
+
+ if (sr == CSR_RESEL_AM) {
+ lun = read_wd33c93(regs, WD_DATA);
+ if (hostdata->level2 < L2_RESELECT)
+ write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
+ lun &= 7;
+ } else {
+ /* Old chip; wait for msgin phase to pick up the LUN. */
+ for (lun = 255; lun; lun--) {
+ if ((asr = read_aux_stat(regs)) & ASR_INT)
+ break;
+ udelay(10);
+ }
+ if (!(asr & ASR_INT)) {
+ printk
+ ("wd33c93: Reselected without IDENTIFY\n");
+ lun = 0;
+ } else {
+ /* Verify this is a change to MSG_IN and read the message */
+ sr = read_wd33c93(regs, WD_SCSI_STATUS);
+ if (sr == (CSR_ABORT | PHS_MESS_IN) ||
+ sr == (CSR_UNEXP | PHS_MESS_IN) ||
+ sr == (CSR_SRV_REQ | PHS_MESS_IN)) {
+ /* Got MSG_IN, grab target LUN */
+ lun = read_1_byte(regs);
+ /* Now we expect a 'paused with ACK asserted' int.. */
+ asr = read_aux_stat(regs);
+ if (!(asr & ASR_INT)) {
+ udelay(10);
+ asr = read_aux_stat(regs);
+ if (!(asr & ASR_INT))
+ printk
+ ("wd33c93: No int after LUN on RESEL (%02x)\n",
+ asr);
+ }
+ sr = read_wd33c93(regs, WD_SCSI_STATUS);
+ if (sr != CSR_MSGIN)
+ printk
+ ("wd33c93: Not paused with ACK on RESEL (%02x)\n",
+ sr);
+ lun &= 7;
+ write_wd33c93_cmd(regs,
+ WD_CMD_NEGATE_ACK);
+ } else {
+ printk
+ ("wd33c93: Not MSG_IN on reselect (%02x)\n",
+ sr);
+ lun = 0;
+ }
+ }
+ }
+
+ /* Now we look for the command that's reconnecting. */
+
+ cmd = (struct scsi_cmnd *) hostdata->disconnected_Q;
+ patch = NULL;
+ while (cmd) {
+ if (id == cmd->device->id && lun == cmd->device->lun)
+ break;
+ patch = cmd;
+ cmd = (struct scsi_cmnd *) cmd->host_scribble;
+ }
+
+ /* Hmm. Couldn't find a valid command.... What to do? */
+
+ if (!cmd) {
+ printk
+ ("---TROUBLE: target %d.%d not in disconnect queue---",
+ id, lun);
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ return;
+ }
+
+ /* Ok, found the command - now start it up again. */
+
+ if (patch)
+ patch->host_scribble = cmd->host_scribble;
+ else
+ hostdata->disconnected_Q =
+ (struct scsi_cmnd *) cmd->host_scribble;
+ hostdata->connected = cmd;
+
+ /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'
+ * because these things are preserved over a disconnect.
+ * But we DO need to fix the DPD bit so it's correct for this command.
+ */
+
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);
+ else
+ write_wd33c93(regs, WD_DESTINATION_ID,
+ cmd->device->id | DSTID_DPD);
+ if (hostdata->level2 >= L2_RESELECT) {
+ write_wd33c93_count(regs, 0); /* we want a DATA_PHASE interrupt */
+ write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
+ write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ } else
+ hostdata->state = S_CONNECTED;
+
+ DB(DB_INTR, printk("-%ld", cmd->pid))
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ break;
+
+ default:
+ printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs);
+ spin_unlock_irqrestore(&hostdata->lock, flags);
+ }
+
+ DB(DB_INTR, printk("} "))
+
+}
+
+static void
+reset_wd33c93(struct Scsi_Host *instance)
+{
+ struct WD33C93_hostdata *hostdata =
+ (struct WD33C93_hostdata *) instance->hostdata;
+ const wd33c93_regs regs = hostdata->regs;
+ uchar sr;
+
+#ifdef CONFIG_SGI_IP22
+ {
+ int busycount = 0;
+ extern void sgiwd93_reset(unsigned long);
+ /* wait 'til the chip gets some time for us */
+ while ((read_aux_stat(regs) & ASR_BSY) && busycount++ < 100)
+ udelay (10);
+ /*
+ * there are scsi devices out there, which manage to lock up
+ * the wd33c93 in a busy condition. In this state it won't
+ * accept the reset command. The only way to solve this is to
+ * give the chip a hardware reset (if possible). The code below
+ * does this for the SGI Indy, where this is possible
+ */
+ /* still busy ? */
+ if (read_aux_stat(regs) & ASR_BSY)
+ sgiwd93_reset(instance->base); /* yeah, give it the hard one */
+ }
+#endif
+
+ write_wd33c93(regs, WD_OWN_ID, OWNID_EAF | OWNID_RAF |
+ instance->this_id | hostdata->clock_freq);
+ write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
+ calc_sync_xfer(hostdata->default_sx_per / 4,
+ DEFAULT_SX_OFF));
+ write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET);
+
+
+#ifdef CONFIG_MVME147_SCSI
+ udelay(25); /* The old wd33c93 on MVME147 needs this, at least */
+#endif
+
+ while (!(read_aux_stat(regs) & ASR_INT))
+ ;
+ sr = read_wd33c93(regs, WD_SCSI_STATUS);
+
+ hostdata->microcode = read_wd33c93(regs, WD_CDB_1);
+ if (sr == 0x00)
+ hostdata->chip = C_WD33C93;
+ else if (sr == 0x01) {
+ write_wd33c93(regs, WD_QUEUE_TAG, 0xa5); /* any random number */
+ sr = read_wd33c93(regs, WD_QUEUE_TAG);
+ if (sr == 0xa5) {
+ hostdata->chip = C_WD33C93B;
+ write_wd33c93(regs, WD_QUEUE_TAG, 0);
+ } else
+ hostdata->chip = C_WD33C93A;
+ } else
+ hostdata->chip = C_UNKNOWN_CHIP;
+
+ write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
+ write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+}
+
+int
+wd33c93_host_reset(struct scsi_cmnd * SCpnt)
+{
+ struct Scsi_Host *instance;
+ struct WD33C93_hostdata *hostdata;
+ int i;
+
+ instance = SCpnt->device->host;
+ hostdata = (struct WD33C93_hostdata *) instance->hostdata;
+
+ printk("scsi%d: reset. ", instance->host_no);
+ disable_irq(instance->irq);
+
+ hostdata->dma_stop(instance, NULL, 0);
+ for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_xfer[i] =
+ calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
+ hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->dma = D_DMA_OFF;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+
+ reset_wd33c93(instance);
+ SCpnt->result = DID_RESET << 16;
+ enable_irq(instance->irq);
+ return SUCCESS;
+}
+
+int
+wd33c93_abort(struct scsi_cmnd * cmd)
+{
+ struct Scsi_Host *instance;
+ struct WD33C93_hostdata *hostdata;
+ wd33c93_regs regs;
+ struct scsi_cmnd *tmp, *prev;
+
+ disable_irq(cmd->device->host->irq);
+
+ instance = cmd->device->host;
+ hostdata = (struct WD33C93_hostdata *) instance->hostdata;
+ regs = hostdata->regs;
+
+/*
+ * Case 1 : If the command hasn't been issued yet, we simply remove it
+ * from the input_Q.
+ */
+
+ tmp = (struct scsi_cmnd *) hostdata->input_Q;
+ prev = 0;
+ while (tmp) {
+ if (tmp == cmd) {
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ else
+ hostdata->input_Q =
+ (struct scsi_cmnd *) cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ cmd->result = DID_ABORT << 16;
+ printk
+ ("scsi%d: Abort - removing command %ld from input_Q. ",
+ instance->host_no, cmd->pid);
+ enable_irq(cmd->device->host->irq);
+ cmd->scsi_done(cmd);
+ return SUCCESS;
+ }
+ prev = tmp;
+ tmp = (struct scsi_cmnd *) tmp->host_scribble;
+ }
+
+/*
+ * Case 2 : If the command is connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected == cmd) {
+ uchar sr, asr;
+ unsigned long timeout;
+
+ printk("scsi%d: Aborting connected command %ld - ",
+ instance->host_no, cmd->pid);
+
+ printk("stopping DMA - ");
+ if (hostdata->dma == D_DMA_RUNNING) {
+ hostdata->dma_stop(instance, cmd, 0);
+ hostdata->dma = D_DMA_OFF;
+ }
+
+ printk("sending wd33c93 ABORT command - ");
+ write_wd33c93(regs, WD_CONTROL,
+ CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_wd33c93_cmd(regs, WD_CMD_ABORT);
+
+/* Now we have to attempt to flush out the FIFO... */
+
+ printk("flushing fifo - ");
+ timeout = 1000000;
+ do {
+ asr = read_aux_stat(regs);
+ if (asr & ASR_DBR)
+ read_wd33c93(regs, WD_DATA);
+ } while (!(asr & ASR_INT) && timeout-- > 0);
+ sr = read_wd33c93(regs, WD_SCSI_STATUS);
+ printk
+ ("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",
+ asr, sr, read_wd33c93_count(regs), timeout);
+
+ /*
+ * Abort command processed.
+ * Still connected.
+ * We must disconnect.
+ */
+
+ printk("sending wd33c93 DISCONNECT command - ");
+ write_wd33c93_cmd(regs, WD_CMD_DISCONNECT);
+
+ timeout = 1000000;
+ asr = read_aux_stat(regs);
+ while ((asr & ASR_CIP) && timeout-- > 0)
+ asr = read_aux_stat(regs);
+ sr = read_wd33c93(regs, WD_SCSI_STATUS);
+ printk("asr=%02x, sr=%02x.", asr, sr);
+
+ hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
+ hostdata->connected = NULL;
+ hostdata->state = S_UNCONNECTED;
+ cmd->result = DID_ABORT << 16;
+
+/* sti();*/
+ wd33c93_execute(instance);
+
+ enable_irq(cmd->device->host->irq);
+ cmd->scsi_done(cmd);
+ return SUCCESS;
+ }
+
+/*
+ * Case 3: If the command is currently disconnected from the bus,
+ * we're not going to expend much effort here: Let's just return
+ * an ABORT_SNOOZE and hope for the best...
+ */
+
+ tmp = (struct scsi_cmnd *) hostdata->disconnected_Q;
+ while (tmp) {
+ if (tmp == cmd) {
+ printk
+ ("scsi%d: Abort - command %ld found on disconnected_Q - ",
+ instance->host_no, cmd->pid);
+ printk("Abort SNOOZE. ");
+ enable_irq(cmd->device->host->irq);
+ return FAILED;
+ }
+ tmp = (struct scsi_cmnd *) tmp->host_scribble;
+ }
+
+/*
+ * Case 4 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+/* sti();*/
+ wd33c93_execute(instance);
+
+ enable_irq(cmd->device->host->irq);
+ printk("scsi%d: warning : SCSI command probably completed successfully"
+ " before abortion. ", instance->host_no);
+ return FAILED;
+}
+
+#define MAX_WD33C93_HOSTS 4
+#define MAX_SETUP_ARGS ((int)(sizeof(setup_args) / sizeof(char *)))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
+
+int
+wd33c93_setup(char *str)
+{
+ int i;
+ char *p1, *p2;
+
+ /* The kernel does some processing of the command-line before calling
+ * this function: If it begins with any decimal or hex number arguments,
+ * ints[0] = how many numbers found and ints[1] through [n] are the values
+ * themselves. str points to where the non-numeric arguments (if any)
+ * start: We do our own parsing of those. We construct synthetic 'nosync'
+ * keywords out of numeric args (to maintain compatibility with older
+ * versions) and then add the rest of the arguments.
+ */
+
+ p1 = setup_buffer;
+ *p1 = '\0';
+ if (str)
+ strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer));
+ setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
+ p1 = setup_buffer;
+ i = 0;
+ while (*p1 && (i < MAX_SETUP_ARGS)) {
+ p2 = strchr(p1, ',');
+ if (p2) {
+ *p2 = '\0';
+ if (p1 != p2)
+ setup_args[i] = p1;
+ p1 = p2 + 1;
+ i++;
+ } else {
+ setup_args[i] = p1;
+ break;
+ }
+ }
+ for (i = 0; i < MAX_SETUP_ARGS; i++)
+ setup_used[i] = 0;
+ done_setup = 1;
+
+ return 1;
+}
+__setup("wd33c93=", wd33c93_setup);
+
+/* check_setup_args() returns index if key found, 0 if not
+ */
+static int
+check_setup_args(char *key, int *flags, int *val, char *buf)
+{
+ int x;
+ char *cp;
+
+ for (x = 0; x < MAX_SETUP_ARGS; x++) {
+ if (setup_used[x])
+ continue;
+ if (!strncmp(setup_args[x], key, strlen(key)))
+ break;
+ if (!strncmp(setup_args[x], "next", strlen("next")))
+ return 0;
+ }
+ if (x == MAX_SETUP_ARGS)
+ return 0;
+ setup_used[x] = 1;
+ cp = setup_args[x] + strlen(key);
+ *val = -1;
+ if (*cp != ':')
+ return ++x;
+ cp++;
+ if ((*cp >= '0') && (*cp <= '9')) {
+ *val = simple_strtoul(cp, NULL, 0);
+ }
+ return ++x;
+}
+
+void
+wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs,
+ dma_setup_t setup, dma_stop_t stop, int clock_freq)
+{
+ struct WD33C93_hostdata *hostdata;
+ int i;
+ int flags;
+ int val;
+ char buf[32];
+
+ if (!done_setup && setup_strings)
+ wd33c93_setup(setup_strings);
+
+ hostdata = (struct WD33C93_hostdata *) instance->hostdata;
+
+ hostdata->regs = regs;
+ hostdata->clock_freq = clock_freq;
+ hostdata->dma_setup = setup;
+ hostdata->dma_stop = stop;
+ hostdata->dma_bounce_buffer = NULL;
+ hostdata->dma_bounce_len = 0;
+ for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_xfer[i] =
+ calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
+ hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[i] = 0;
+ hostdata->disc_allowed_cnt[i] = 0;
+ hostdata->disc_done_cnt[i] = 0;
+#endif
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->dma = D_DMA_OFF;
+ hostdata->level2 = L2_BASIC;
+ hostdata->disconnect = DIS_ADAPTIVE;
+ hostdata->args = DEBUG_DEFAULTS;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+ hostdata->default_sx_per = DEFAULT_SX_PER;
+ hostdata->no_sync = 0xff; /* sync defaults to off */
+ hostdata->no_dma = 0; /* default is DMA enabled */
+
+#ifdef PROC_INTERFACE
+ hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS |
+ PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP;
+#ifdef PROC_STATISTICS
+ hostdata->dma_cnt = 0;
+ hostdata->pio_cnt = 0;
+ hostdata->int_cnt = 0;
+#endif
+#endif
+
+ if (check_setup_args("nosync", &flags, &val, buf))
+ hostdata->no_sync = val;
+
+ if (check_setup_args("nodma", &flags, &val, buf))
+ hostdata->no_dma = (val == -1) ? 1 : val;
+
+ if (check_setup_args("period", &flags, &val, buf))
+ hostdata->default_sx_per =
+ sx_table[round_period((unsigned int) val)].period_ns;
+
+ if (check_setup_args("disconnect", &flags, &val, buf)) {
+ if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
+ hostdata->disconnect = val;
+ else
+ hostdata->disconnect = DIS_ADAPTIVE;
+ }
+
+ if (check_setup_args("level2", &flags, &val, buf))
+ hostdata->level2 = val;
+
+ if (check_setup_args("debug", &flags, &val, buf))
+ hostdata->args = val & DB_MASK;
+
+ if (check_setup_args("clock", &flags, &val, buf)) {
+ if (val > 7 && val < 11)
+ val = WD33C93_FS_8_10;
+ else if (val > 11 && val < 16)
+ val = WD33C93_FS_12_15;
+ else if (val > 15 && val < 21)
+ val = WD33C93_FS_16_20;
+ else
+ val = WD33C93_FS_8_10;
+ hostdata->clock_freq = val;
+ }
+
+ if ((i = check_setup_args("next", &flags, &val, buf))) {
+ while (i)
+ setup_used[--i] = 1;
+ }
+#ifdef PROC_INTERFACE
+ if (check_setup_args("proc", &flags, &val, buf))
+ hostdata->proc = val;
+#endif
+
+ spin_lock_irq(&hostdata->lock);
+ reset_wd33c93(instance);
+ spin_unlock_irq(&hostdata->lock);
+
+ printk("wd33c93-%d: chip=%s/%d no_sync=0x%x no_dma=%d",
+ instance->host_no,
+ (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip ==
+ C_WD33C93A) ?
+ "WD33c93A" : (hostdata->chip ==
+ C_WD33C93B) ? "WD33c93B" : "unknown",
+ hostdata->microcode, hostdata->no_sync, hostdata->no_dma);
+#ifdef DEBUGGING_ON
+ printk(" debug_flags=0x%02x\n", hostdata->args);
+#else
+ printk(" debugging=OFF\n");
+#endif
+ printk(" setup_args=");
+ for (i = 0; i < MAX_SETUP_ARGS; i++)
+ printk("%s,", setup_args[i]);
+ printk("\n");
+ printk(" Version %s - %s, Compiled %s at %s\n",
+ WD33C93_VERSION, WD33C93_DATE, __DATE__, __TIME__);
+}
+
+int
+wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off, int len, int in)
+{
+
+#ifdef PROC_INTERFACE
+
+ char *bp;
+ char tbuf[128];
+ struct WD33C93_hostdata *hd;
+ struct scsi_cmnd *cmd;
+ int x, i;
+ static int stop = 0;
+
+ hd = (struct WD33C93_hostdata *) instance->hostdata;
+
+/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
+ * keywords (same format as command-line, but only ONE per read):
+ * debug
+ * disconnect
+ * period
+ * resync
+ * proc
+ * nodma
+ */
+
+ if (in) {
+ buf[len] = '\0';
+ bp = buf;
+ if (!strncmp(bp, "debug:", 6)) {
+ bp += 6;
+ hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK;
+ } else if (!strncmp(bp, "disconnect:", 11)) {
+ bp += 11;
+ x = simple_strtoul(bp, NULL, 0);
+ if (x < DIS_NEVER || x > DIS_ALWAYS)
+ x = DIS_ADAPTIVE;
+ hd->disconnect = x;
+ } else if (!strncmp(bp, "period:", 7)) {
+ bp += 7;
+ x = simple_strtoul(bp, NULL, 0);
+ hd->default_sx_per =
+ sx_table[round_period((unsigned int) x)].period_ns;
+ } else if (!strncmp(bp, "resync:", 7)) {
+ bp += 7;
+ x = simple_strtoul(bp, NULL, 0);
+ for (i = 0; i < 7; i++)
+ if (x & (1 << i))
+ hd->sync_stat[i] = SS_UNSET;
+ } else if (!strncmp(bp, "proc:", 5)) {
+ bp += 5;
+ hd->proc = simple_strtoul(bp, NULL, 0);
+ } else if (!strncmp(bp, "nodma:", 6)) {
+ bp += 6;
+ hd->no_dma = simple_strtoul(bp, NULL, 0);
+ } else if (!strncmp(bp, "level2:", 7)) {
+ bp += 7;
+ hd->level2 = simple_strtoul(bp, NULL, 0);
+ }
+ return len;
+ }
+
+ spin_lock_irq(&hd->lock);
+ bp = buf;
+ *bp = '\0';
+ if (hd->proc & PR_VERSION) {
+ sprintf(tbuf, "\nVersion %s - %s. Compiled %s %s",
+ WD33C93_VERSION, WD33C93_DATE, __DATE__, __TIME__);
+ strcat(bp, tbuf);
+ }
+ if (hd->proc & PR_INFO) {
+ sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d",
+ hd->clock_freq, hd->no_sync, hd->no_dma);
+ strcat(bp, tbuf);
+ strcat(bp, "\nsync_xfer[] = ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%02x", hd->sync_xfer[x]);
+ strcat(bp, tbuf);
+ }
+ strcat(bp, "\nsync_stat[] = ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%02x", hd->sync_stat[x]);
+ strcat(bp, tbuf);
+ }
+ }
+#ifdef PROC_STATISTICS
+ if (hd->proc & PR_STATISTICS) {
+ strcat(bp, "\ncommands issued: ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%ld", hd->cmd_cnt[x]);
+ strcat(bp, tbuf);
+ }
+ strcat(bp, "\ndisconnects allowed:");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%ld", hd->disc_allowed_cnt[x]);
+ strcat(bp, tbuf);
+ }
+ strcat(bp, "\ndisconnects done: ");
+ for (x = 0; x < 7; x++) {
+ sprintf(tbuf, "\t%ld", hd->disc_done_cnt[x]);
+ strcat(bp, tbuf);
+ }
+ sprintf(tbuf,
+ "\ninterrupts: %ld, DATA_PHASE ints: %ld DMA, %ld PIO",
+ hd->int_cnt, hd->dma_cnt, hd->pio_cnt);
+ strcat(bp, tbuf);
+ }
+#endif
+ if (hd->proc & PR_CONNECTED) {
+ strcat(bp, "\nconnected: ");
+ if (hd->connected) {
+ cmd = (struct scsi_cmnd *) hd->connected;
+ sprintf(tbuf, " %ld-%d:%d(%02x)",
+ cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ strcat(bp, tbuf);
+ }
+ }
+ if (hd->proc & PR_INPUTQ) {
+ strcat(bp, "\ninput_Q: ");
+ cmd = (struct scsi_cmnd *) hd->input_Q;
+ while (cmd) {
+ sprintf(tbuf, " %ld-%d:%d(%02x)",
+ cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ strcat(bp, tbuf);
+ cmd = (struct scsi_cmnd *) cmd->host_scribble;
+ }
+ }
+ if (hd->proc & PR_DISCQ) {
+ strcat(bp, "\ndisconnected_Q:");
+ cmd = (struct scsi_cmnd *) hd->disconnected_Q;
+ while (cmd) {
+ sprintf(tbuf, " %ld-%d:%d(%02x)",
+ cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ strcat(bp, tbuf);
+ cmd = (struct scsi_cmnd *) cmd->host_scribble;
+ }
+ }
+ strcat(bp, "\n");
+ spin_unlock_irq(&hd->lock);
+ *start = buf;
+ if (stop) {
+ stop = 0;
+ return 0;
+ }
+ if (off > 0x40000) /* ALWAYS stop after 256k bytes have been read */
+ stop = 1;
+ if (hd->proc & PR_STOP) /* stop every other time */
+ stop = 1;
+ return strlen(bp);
+
+#else /* PROC_INTERFACE */
+
+ return 0;
+
+#endif /* PROC_INTERFACE */
+
+}
+
+void
+wd33c93_release(void)
+{
+}
+
+EXPORT_SYMBOL(wd33c93_host_reset);
+EXPORT_SYMBOL(wd33c93_init);
+EXPORT_SYMBOL(wd33c93_release);
+EXPORT_SYMBOL(wd33c93_abort);
+EXPORT_SYMBOL(wd33c93_queuecommand);
+EXPORT_SYMBOL(wd33c93_intr);
+EXPORT_SYMBOL(wd33c93_proc_info);
diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h
new file mode 100644
index 000000000000..193ec517d252
--- /dev/null
+++ b/drivers/scsi/wd33c93.h
@@ -0,0 +1,348 @@
+/*
+ * wd33c93.h - Linux device driver definitions for the
+ * Commodore Amiga A2091/590 SCSI controller card
+ *
+ * IMPORTANT: This file is for version 1.25 - 09/Jul/1997
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * john@geolog.com
+ * jshiffle@netcom.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef WD33C93_H
+#define WD33C93_H
+
+#include <linux/config.h>
+
+#define PROC_INTERFACE /* add code for /proc/scsi/wd33c93/xxx interface */
+#ifdef PROC_INTERFACE
+#define PROC_STATISTICS /* add code for keeping various real time stats */
+#endif
+
+#define SYNC_DEBUG /* extra info on sync negotiation printed */
+#define DEBUGGING_ON /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0 /* default debugging bitmask */
+
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#else
+#define DB(f,a)
+#endif
+
+#define uchar unsigned char
+
+
+/* wd register names */
+#define WD_OWN_ID 0x00
+#define WD_CONTROL 0x01
+#define WD_TIMEOUT_PERIOD 0x02
+#define WD_CDB_1 0x03
+#define WD_CDB_2 0x04
+#define WD_CDB_3 0x05
+#define WD_CDB_4 0x06
+#define WD_CDB_5 0x07
+#define WD_CDB_6 0x08
+#define WD_CDB_7 0x09
+#define WD_CDB_8 0x0a
+#define WD_CDB_9 0x0b
+#define WD_CDB_10 0x0c
+#define WD_CDB_11 0x0d
+#define WD_CDB_12 0x0e
+#define WD_TARGET_LUN 0x0f
+#define WD_COMMAND_PHASE 0x10
+#define WD_SYNCHRONOUS_TRANSFER 0x11
+#define WD_TRANSFER_COUNT_MSB 0x12
+#define WD_TRANSFER_COUNT 0x13
+#define WD_TRANSFER_COUNT_LSB 0x14
+#define WD_DESTINATION_ID 0x15
+#define WD_SOURCE_ID 0x16
+#define WD_SCSI_STATUS 0x17
+#define WD_COMMAND 0x18
+#define WD_DATA 0x19
+#define WD_QUEUE_TAG 0x1a
+#define WD_AUXILIARY_STATUS 0x1f
+
+/* WD commands */
+#define WD_CMD_RESET 0x00
+#define WD_CMD_ABORT 0x01
+#define WD_CMD_ASSERT_ATN 0x02
+#define WD_CMD_NEGATE_ACK 0x03
+#define WD_CMD_DISCONNECT 0x04
+#define WD_CMD_RESELECT 0x05
+#define WD_CMD_SEL_ATN 0x06
+#define WD_CMD_SEL 0x07
+#define WD_CMD_SEL_ATN_XFER 0x08
+#define WD_CMD_SEL_XFER 0x09
+#define WD_CMD_RESEL_RECEIVE 0x0a
+#define WD_CMD_RESEL_SEND 0x0b
+#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
+#define WD_CMD_TRANS_ADDR 0x18
+#define WD_CMD_TRANS_INFO 0x20
+#define WD_CMD_TRANSFER_PAD 0x21
+#define WD_CMD_SBT_MODE 0x80
+
+/* ASR register */
+#define ASR_INT (0x80)
+#define ASR_LCI (0x40)
+#define ASR_BSY (0x20)
+#define ASR_CIP (0x10)
+#define ASR_PE (0x02)
+#define ASR_DBR (0x01)
+
+/* SCSI Bus Phases */
+#define PHS_DATA_OUT 0x00
+#define PHS_DATA_IN 0x01
+#define PHS_COMMAND 0x02
+#define PHS_STATUS 0x03
+#define PHS_MESS_OUT 0x06
+#define PHS_MESS_IN 0x07
+
+/* Command Status Register definitions */
+
+ /* reset state interrupts */
+#define CSR_RESET 0x00
+#define CSR_RESET_AF 0x01
+
+ /* successful completion interrupts */
+#define CSR_RESELECT 0x10
+#define CSR_SELECT 0x11
+#define CSR_SEL_XFER_DONE 0x16
+#define CSR_XFER_DONE 0x18
+
+ /* paused or aborted interrupts */
+#define CSR_MSGIN 0x20
+#define CSR_SDP 0x21
+#define CSR_SEL_ABORT 0x22
+#define CSR_RESEL_ABORT 0x25
+#define CSR_RESEL_ABORT_AM 0x27
+#define CSR_ABORT 0x28
+
+ /* terminated interrupts */
+#define CSR_INVALID 0x40
+#define CSR_UNEXP_DISC 0x41
+#define CSR_TIMEOUT 0x42
+#define CSR_PARITY 0x43
+#define CSR_PARITY_ATN 0x44
+#define CSR_BAD_STATUS 0x45
+#define CSR_UNEXP 0x48
+
+ /* service required interrupts */
+#define CSR_RESEL 0x80
+#define CSR_RESEL_AM 0x81
+#define CSR_DISC 0x85
+#define CSR_SRV_REQ 0x88
+
+ /* Own ID/CDB Size register */
+#define OWNID_EAF 0x08
+#define OWNID_EHP 0x10
+#define OWNID_RAF 0x20
+#define OWNID_FS_8 0x00
+#define OWNID_FS_12 0x40
+#define OWNID_FS_16 0x80
+
+ /* define these so we don't have to change a2091.c, etc. */
+#define WD33C93_FS_8_10 OWNID_FS_8
+#define WD33C93_FS_12_15 OWNID_FS_12
+#define WD33C93_FS_16_20 OWNID_FS_16
+
+ /* Control register */
+#define CTRL_HSP 0x01
+#define CTRL_HA 0x02
+#define CTRL_IDI 0x04
+#define CTRL_EDI 0x08
+#define CTRL_HHP 0x10
+#define CTRL_POLLED 0x00
+#define CTRL_BURST 0x20
+#define CTRL_BUS 0x40
+#define CTRL_DMA 0x80
+
+ /* Timeout Period register */
+#define TIMEOUT_PERIOD_VALUE 20 /* 20 = 200 ms */
+
+ /* Synchronous Transfer Register */
+#define STR_FSS 0x80
+
+ /* Destination ID register */
+#define DSTID_DPD 0x40
+#define DATA_OUT_DIR 0
+#define DATA_IN_DIR 1
+#define DSTID_SCC 0x80
+
+ /* Source ID register */
+#define SRCID_MASK 0x07
+#define SRCID_SIV 0x08
+#define SRCID_DSP 0x20
+#define SRCID_ES 0x40
+#define SRCID_ER 0x80
+
+ /* This is what the 3393 chip looks like to us */
+typedef struct {
+#ifdef CONFIG_WD33C93_PIO
+ unsigned int SASR;
+ unsigned int SCMD;
+#else
+ volatile unsigned char *SASR;
+ volatile unsigned char *SCMD;
+#endif
+} wd33c93_regs;
+
+
+typedef int (*dma_setup_t) (struct scsi_cmnd *SCpnt, int dir_in);
+typedef void (*dma_stop_t) (struct Scsi_Host *instance,
+ struct scsi_cmnd *SCpnt, int status);
+
+
+#define ILLEGAL_STATUS_BYTE 0xff
+
+#define DEFAULT_SX_PER 376 /* (ns) fairly safe */
+#define DEFAULT_SX_OFF 0 /* aka async */
+
+#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */
+#define OPTIMUM_SX_OFF 12 /* size of wd3393 fifo */
+
+struct sx_period {
+ unsigned int period_ns;
+ uchar reg_value;
+ };
+
+/* FEF: defines for hostdata->dma_buffer_pool */
+
+#define BUF_CHIP_ALLOCED 0
+#define BUF_SCSI_ALLOCED 1
+
+struct WD33C93_hostdata {
+ struct Scsi_Host *next;
+ wd33c93_regs regs;
+ spinlock_t lock;
+ uchar clock_freq;
+ uchar chip; /* what kind of wd33c93? */
+ uchar microcode; /* microcode rev */
+ uchar dma_buffer_pool; /* FEF: buffer from chip_ram? */
+ int dma_dir; /* data transfer dir. */
+ dma_setup_t dma_setup;
+ dma_stop_t dma_stop;
+ unsigned int dma_xfer_mask;
+ uchar *dma_bounce_buffer;
+ unsigned int dma_bounce_len;
+ volatile uchar busy[8]; /* index = target, bit = lun */
+ volatile struct scsi_cmnd *input_Q; /* commands waiting to be started */
+ volatile struct scsi_cmnd *selecting; /* trying to select this command */
+ volatile struct scsi_cmnd *connected; /* currently connected command */
+ volatile struct scsi_cmnd *disconnected_Q;/* commands waiting for reconnect */
+ uchar state; /* what we are currently doing */
+ uchar dma; /* current state of DMA (on/off) */
+ uchar level2; /* extent to which Level-2 commands are used */
+ uchar disconnect; /* disconnect/reselect policy */
+ unsigned int args; /* set from command-line argument */
+ uchar incoming_msg[8]; /* filled during message_in phase */
+ int incoming_ptr; /* mainly used with EXTENDED messages */
+ uchar outgoing_msg[8]; /* send this during next message_out */
+ int outgoing_len; /* length of outgoing message */
+ unsigned int default_sx_per; /* default transfer period for SCSI bus */
+ uchar sync_xfer[8]; /* sync_xfer reg settings per target */
+ uchar sync_stat[8]; /* status of sync negotiation per target */
+ uchar no_sync; /* bitmask: don't do sync on these targets */
+ uchar no_dma; /* set this flag to disable DMA */
+#ifdef PROC_INTERFACE
+ uchar proc; /* bitmask: what's in proc output */
+#ifdef PROC_STATISTICS
+ unsigned long cmd_cnt[8]; /* # of commands issued per target */
+ unsigned long int_cnt; /* # of interrupts serviced */
+ unsigned long pio_cnt; /* # of pio data transfers */
+ unsigned long dma_cnt; /* # of DMA data transfers */
+ unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */
+ unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/
+#endif
+#endif
+ };
+
+
+/* defines for hostdata->chip */
+
+#define C_WD33C93 0
+#define C_WD33C93A 1
+#define C_WD33C93B 2
+#define C_UNKNOWN_CHIP 100
+
+/* defines for hostdata->state */
+
+#define S_UNCONNECTED 0
+#define S_SELECTING 1
+#define S_RUNNING_LEVEL2 2
+#define S_CONNECTED 3
+#define S_PRE_TMP_DISC 4
+#define S_PRE_CMP_DISC 5
+
+/* defines for hostdata->dma */
+
+#define D_DMA_OFF 0
+#define D_DMA_RUNNING 1
+
+/* defines for hostdata->level2 */
+/* NOTE: only the first 3 are implemented so far */
+
+#define L2_NONE 1 /* no combination commands - we get lots of ints */
+#define L2_SELECT 2 /* start with SEL_ATN_XFER, but never resume it */
+#define L2_BASIC 3 /* resume after STATUS ints & RDP messages */
+#define L2_DATA 4 /* resume after DATA_IN/OUT ints */
+#define L2_MOST 5 /* resume after anything except a RESELECT int */
+#define L2_RESELECT 6 /* resume after everything, including RESELECT ints */
+#define L2_ALL 7 /* always resume */
+
+/* defines for hostdata->disconnect */
+
+#define DIS_NEVER 0
+#define DIS_ADAPTIVE 1
+#define DIS_ALWAYS 2
+
+/* defines for hostdata->args */
+
+#define DB_TEST1 1<<0
+#define DB_TEST2 1<<1
+#define DB_QUEUE_COMMAND 1<<2
+#define DB_EXECUTE 1<<3
+#define DB_INTR 1<<4
+#define DB_TRANSFER 1<<5
+#define DB_MASK 0x3f
+
+/* defines for hostdata->sync_stat[] */
+
+#define SS_UNSET 0
+#define SS_FIRST 1
+#define SS_WAITING 2
+#define SS_SET 3
+
+/* defines for hostdata->proc */
+
+#define PR_VERSION 1<<0
+#define PR_INFO 1<<1
+#define PR_STATISTICS 1<<2
+#define PR_CONNECTED 1<<3
+#define PR_INPUTQ 1<<4
+#define PR_DISCQ 1<<5
+#define PR_TEST 1<<6
+#define PR_STOP 1<<7
+
+
+void wd33c93_init (struct Scsi_Host *instance, const wd33c93_regs regs,
+ dma_setup_t setup, dma_stop_t stop, int clock_freq);
+int wd33c93_abort (struct scsi_cmnd *cmd);
+int wd33c93_queuecommand (struct scsi_cmnd *cmd,
+ void (*done)(struct scsi_cmnd *));
+void wd33c93_intr (struct Scsi_Host *instance);
+int wd33c93_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+int wd33c93_host_reset (struct scsi_cmnd *);
+void wd33c93_release(void);
+
+#endif /* WD33C93_H */
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
new file mode 100644
index 000000000000..bf4a758e2801
--- /dev/null
+++ b/drivers/scsi/wd7000.c
@@ -0,0 +1,1667 @@
+/* $Id: $
+ * linux/drivers/scsi/wd7000.c
+ *
+ * Copyright (C) 1992 Thomas Wuensche
+ * closely related to the aha1542 driver from Tommy Thorn
+ * ( as close as different hardware allows on a lowlevel-driver :-) )
+ *
+ * Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to
+ * accommodate Eric Youngdale's modifications to scsi.c. Nov 1992.
+ *
+ * Additional changes to support scatter/gather. Dec. 1992. tw/jb
+ *
+ * No longer tries to reset SCSI bus at boot (it wasn't working anyway).
+ * Rewritten to support multiple host adapters.
+ * Miscellaneous cleanup.
+ * So far, still doesn't do reset or abort correctly, since I have no idea
+ * how to do them with this board (8^(. Jan 1994 jb
+ *
+ * This driver now supports both of the two standard configurations (per
+ * the 3.36 Owner's Manual, my latest reference) by the same method as
+ * before; namely, by looking for a BIOS signature. Thus, the location of
+ * the BIOS signature determines the board configuration. Until I have
+ * time to do something more flexible, users should stick to one of the
+ * following:
+ *
+ * Standard configuration for single-adapter systems:
+ * - BIOS at CE00h
+ * - I/O base address 350h
+ * - IRQ level 15
+ * - DMA channel 6
+ * Standard configuration for a second adapter in a system:
+ * - BIOS at C800h
+ * - I/O base address 330h
+ * - IRQ level 11
+ * - DMA channel 5
+ *
+ * Anyone who can recompile the kernel is welcome to add others as need
+ * arises, but unpredictable results may occur if there are conflicts.
+ * In any event, if there are multiple adapters in a system, they MUST
+ * use different I/O bases, IRQ levels, and DMA channels, since they will be
+ * indistinguishable (and in direct conflict) otherwise.
+ *
+ * As a point of information, the NO_OP command toggles the CMD_RDY bit
+ * of the status port, and this fact could be used as a test for the I/O
+ * base address (or more generally, board detection). There is an interrupt
+ * status port, so IRQ probing could also be done. I suppose the full
+ * DMA diagnostic could be used to detect the DMA channel being used. I
+ * haven't done any of this, though, because I think there's too much of
+ * a chance that such explorations could be destructive, if some other
+ * board's resources are used inadvertently. So, call me a wimp, but I
+ * don't want to try it. The only kind of exploration I trust is memory
+ * exploration, since it's more certain that reading memory won't be
+ * destructive.
+ *
+ * More to my liking would be a LILO boot command line specification, such
+ * as is used by the aha152x driver (and possibly others). I'll look into
+ * it, as I have time...
+ *
+ * I get mail occasionally from people who either are using or are
+ * considering using a WD7000 with Linux. There is a variety of
+ * nomenclature describing WD7000's. To the best of my knowledge, the
+ * following is a brief summary (from an old WD doc - I don't work for
+ * them or anything like that):
+ *
+ * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS
+ * installed. Last I heard, the BIOS was actually done by Columbia
+ * Data Products. The BIOS is only used by this driver (and thus
+ * by Linux) to identify the board; none of it can be executed under
+ * Linux.
+ *
+ * WD7000-ASC: This is the original adapter board, with or without BIOS.
+ * The board uses a WD33C93 or WD33C93A SBIC, which in turn is
+ * controlled by an onboard Z80 processor. The board interface
+ * visible to the host CPU is defined effectively by the Z80's
+ * firmware, and it is this firmware's revision level that is
+ * determined and reported by this driver. (The version of the
+ * on-board BIOS is of no interest whatsoever.) The host CPU has
+ * no access to the SBIC; hence the fact that it is a WD33C93 is
+ * also of no interest to this driver.
+ *
+ * WD7000-AX:
+ * WD7000-MX:
+ * WD7000-EX: These are newer versions of the WD7000-ASC. The -ASC is
+ * largely built from discrete components; these boards use more
+ * integration. The -AX is an ISA bus board (like the -ASC),
+ * the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an
+ * EISA bus board.
+ *
+ * At the time of my documentation, the -?X boards were "future" products,
+ * and were not yet available. However, I vaguely recall that Thomas
+ * Wuensche had an -AX, so I believe at least it is supported by this
+ * driver. I have no personal knowledge of either -MX or -EX boards.
+ *
+ * P.S. Just recently, I've discovered (directly from WD and Future
+ * Domain) that all but the WD7000-EX have been out of production for
+ * two years now. FD has production rights to the 7000-EX, and are
+ * producing it under a new name, and with a new BIOS. If anyone has
+ * one of the FD boards, it would be nice to come up with a signature
+ * for it.
+ * J.B. Jan 1994.
+ *
+ *
+ * Revisions by Miroslav Zagorac <zaga@fly.cc.fer.hr>
+ *
+ * 08/24/1996.
+ *
+ * Enhancement for wd7000_detect function has been made, so you don't have
+ * to enter BIOS ROM address in initialisation data (see struct Config).
+ * We cannot detect IRQ, DMA and I/O base address for now, so we have to
+ * enter them as arguments while wd_7000 is detected. If someone has IRQ,
+ * DMA or I/O base address set to some other value, he can enter them in
+ * configuration without any problem. Also I wrote a function wd7000_setup,
+ * so now you can enter WD-7000 definition as kernel arguments,
+ * as in lilo.conf:
+ *
+ * append="wd7000=IRQ,DMA,IO"
+ *
+ * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize
+ * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is
+ * useless for Linux. B^)
+ *
+ *
+ * 09/06/1996.
+ *
+ * Autodetecting of I/O base address from wd7000_detect function is removed,
+ * some little bugs removed, etc...
+ *
+ * Thanks to Roger Scott for driver debugging.
+ *
+ * 06/07/1997
+ *
+ * Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ * Now, driver can handle hard disks with capacity >1GB.
+ *
+ * 01/15/1998
+ *
+ * Added support for BUS_ON and BUS_OFF parameters in config line.
+ * Miscellaneous cleanup.
+ *
+ * 03/01/1998
+ *
+ * WD7000 driver now work on kernels >= 2.1.x
+ *
+ *
+ * 12/31/2001 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * use host->host_lock, not io_request_lock, cleanups
+ *
+ * 2002/10/04 - Alan Cox <alan@redhat.com>
+ *
+ * Use dev_id for interrupts, kill __FUNCTION__ pasting
+ * Add a lock for the scb pool, clean up all other cli/sti usage stuff
+ * Use the adapter lock for the other places we had the cli's
+ *
+ * 2002/10/06 - Alan Cox <alan@redhat.com>
+ *
+ * Switch to new style error handling
+ * Clean up delay to udelay, and yielding sleeps
+ * Make host reset actually reset the card
+ * Make everything static
+ *
+ * 2003/02/12 - Christoph Hellwig <hch@infradead.org>
+ *
+ * Cleaned up host template defintion
+ * Removed now obsolete wd7000.h
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsicam.h>
+
+
+#undef WD7000_DEBUG /* general debug */
+#ifdef WD7000_DEBUG
+#define dprintk printk
+#else
+#define dprintk(format,args...)
+#endif
+
+/*
+ * Mailbox structure sizes.
+ * I prefer to keep the number of ICMBs much larger than the number of
+ * OGMBs. OGMBs are used very quickly by the driver to start one or
+ * more commands, while ICMBs are used by the host adapter per command.
+ */
+#define OGMB_CNT 16
+#define ICMB_CNT 32
+
+/*
+ * Scb's are shared by all active adapters. So, if they all become busy,
+ * callers may be made to wait in alloc_scbs for them to free. That can
+ * be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd
+ * rather conserve memory, use a smaller number (> 0, of course) - things
+ * will should still work OK.
+ */
+#define MAX_SCBS 32
+
+/*
+ * In this version, sg_tablesize now defaults to WD7000_SG, and will
+ * be set to SG_NONE for older boards. This is the reverse of the
+ * previous default, and was changed so that the driver-level
+ * scsi_host_template would reflect the driver's support for scatter/
+ * gather.
+ *
+ * Also, it has been reported that boards at Revision 6 support scatter/
+ * gather, so the new definition of an "older" board has been changed
+ * accordingly.
+ */
+#define WD7000_Q 16
+#define WD7000_SG 16
+
+
+/*
+ * WD7000-specific mailbox structure
+ *
+ */
+typedef volatile struct mailbox {
+ unchar status;
+ unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */
+} Mailbox;
+
+/*
+ * This structure should contain all per-adapter global data. I.e., any
+ * new global per-adapter data should put in here.
+ */
+typedef struct adapter {
+ struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */
+ int iobase; /* This adapter's I/O base address */
+ int irq; /* This adapter's IRQ level */
+ int dma; /* This adapter's DMA channel */
+ int int_counter; /* This adapter's interrupt counter */
+ int bus_on; /* This adapter's BUS_ON time */
+ int bus_off; /* This adapter's BUS_OFF time */
+ struct { /* This adapter's mailboxes */
+ Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */
+ Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */
+ } mb;
+ int next_ogmb; /* to reduce contention at mailboxes */
+ unchar control; /* shadows CONTROL port value */
+ unchar rev1, rev2; /* filled in by wd7000_revision */
+} Adapter;
+
+/*
+ * (linear) base address for ROM BIOS
+ */
+static const long wd7000_biosaddr[] = {
+ 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000
+};
+#define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long))
+
+static const unsigned short wd7000_iobase[] = {
+ 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
+ 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
+ 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
+ 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8
+};
+#define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short))
+
+static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 };
+#define NUM_IRQS (sizeof(wd7000_irq)/sizeof(short))
+
+static const short wd7000_dma[] = { 5, 6, 7 };
+#define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short))
+
+/*
+ * The following is set up by wd7000_detect, and used thereafter for
+ * proc and other global ookups
+ */
+
+#define UNITS 8
+static struct Scsi_Host *wd7000_host[UNITS];
+
+#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */
+
+/*
+ * Standard Adapter Configurations - used by wd7000_detect
+ */
+typedef struct {
+ short irq; /* IRQ level */
+ short dma; /* DMA channel */
+ unsigned iobase; /* I/O base address */
+ short bus_on; /* Time that WD7000 spends on the AT-bus when */
+ /* transferring data. BIOS default is 8000ns. */
+ short bus_off; /* Time that WD7000 spends OFF THE BUS after */
+ /* while it is transferring data. */
+ /* BIOS default is 1875ns */
+} Config;
+
+/*
+ * Add here your configuration...
+ */
+static Config configs[] = {
+ {15, 6, 0x350, BUS_ON, BUS_OFF}, /* defaults for single adapter */
+ {11, 5, 0x320, BUS_ON, BUS_OFF}, /* defaults for second adapter */
+ {7, 6, 0x350, BUS_ON, BUS_OFF}, /* My configuration (Zaga) */
+ {-1, -1, 0x0, BUS_ON, BUS_OFF} /* Empty slot */
+};
+#define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
+
+/*
+ * The following list defines strings to look for in the BIOS that identify
+ * it as the WD7000-FASST2 SST BIOS. I suspect that something should be
+ * added for the Future Domain version.
+ */
+typedef struct signature {
+ const char *sig; /* String to look for */
+ unsigned long ofs; /* offset from BIOS base address */
+ unsigned len; /* length of string */
+} Signature;
+
+static const Signature signatures[] = {
+ {"SSTBIOS", 0x0000d, 7} /* "SSTBIOS" @ offset 0x0000d */
+};
+#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
+
+
+/*
+ * I/O Port Offsets and Bit Definitions
+ * 4 addresses are used. Those not defined here are reserved.
+ */
+#define ASC_STAT 0 /* Status, Read */
+#define ASC_COMMAND 0 /* Command, Write */
+#define ASC_INTR_STAT 1 /* Interrupt Status, Read */
+#define ASC_INTR_ACK 1 /* Acknowledge, Write */
+#define ASC_CONTROL 2 /* Control, Write */
+
+/*
+ * ASC Status Port
+ */
+#define INT_IM 0x80 /* Interrupt Image Flag */
+#define CMD_RDY 0x40 /* Command Port Ready */
+#define CMD_REJ 0x20 /* Command Port Byte Rejected */
+#define ASC_INIT 0x10 /* ASC Initialized Flag */
+#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */
+
+/*
+ * COMMAND opcodes
+ *
+ * Unfortunately, I have no idea how to properly use some of these commands,
+ * as the OEM manual does not make it clear. I have not been able to use
+ * enable/disable unsolicited interrupts or the reset commands with any
+ * discernible effect whatsoever. I think they may be related to certain
+ * ICB commands, but again, the OEM manual doesn't make that clear.
+ */
+#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */
+#define INITIALIZATION 1 /* initialization (10 bytes) */
+#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */
+#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */
+#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */
+#define SOFT_RESET 5 /* SCSI bus soft reset */
+#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */
+#define START_OGMB 0x80 /* start command in OGMB (n) */
+#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */
+ /* where (n) = lower 6 bits */
+/*
+ * For INITIALIZATION:
+ */
+typedef struct initCmd {
+ unchar op; /* command opcode (= 1) */
+ unchar ID; /* Adapter's SCSI ID */
+ unchar bus_on; /* Bus on time, x 125ns (see below) */
+ unchar bus_off; /* Bus off time, "" "" */
+ unchar rsvd; /* Reserved */
+ unchar mailboxes[3]; /* Address of Mailboxes, MSB first */
+ unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */
+ unchar icmbs; /* Number of incoming MBs, "" "" */
+} InitCmd;
+
+/*
+ * Interrupt Status Port - also returns diagnostic codes at ASC reset
+ *
+ * if msb is zero, the lower bits are diagnostic status
+ * Diagnostics:
+ * 01 No diagnostic error occurred
+ * 02 RAM failure
+ * 03 FIFO R/W failed
+ * 04 SBIC register read/write failed
+ * 05 Initialization D-FF failed
+ * 06 Host IRQ D-FF failed
+ * 07 ROM checksum error
+ * Interrupt status (bitwise):
+ * 10NNNNNN outgoing mailbox NNNNNN is free
+ * 11NNNNNN incoming mailbox NNNNNN needs service
+ */
+#define MB_INTR 0xC0 /* Mailbox Service possible/required */
+#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */
+#define MB_MASK 0x3f /* mask for mailbox number */
+
+/*
+ * CONTROL port bits
+ */
+#define INT_EN 0x08 /* Interrupt Enable */
+#define DMA_EN 0x04 /* DMA Enable */
+#define SCSI_RES 0x02 /* SCSI Reset */
+#define ASC_RES 0x01 /* ASC Reset */
+
+/*
+ * Driver data structures:
+ * - mb and scbs are required for interfacing with the host adapter.
+ * An SCB has extra fields not visible to the adapter; mb's
+ * _cannot_ do this, since the adapter assumes they are contiguous in
+ * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
+ * to access them.
+ * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each;
+ * the additional bytes are used only by the driver.
+ * - For now, a pool of SCBs are kept in global storage by this driver,
+ * and are allocated and freed as needed.
+ *
+ * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
+ * not when it has finished. Since the SCB must be around for completion,
+ * problems arise when SCBs correspond to OGMBs, which may be reallocated
+ * earlier (or delayed unnecessarily until a command completes).
+ * Mailboxes are used as transient data structures, simply for
+ * carrying SCB addresses to/from the 7000-FASST2.
+ *
+ * Note also since SCBs are not "permanently" associated with mailboxes,
+ * there is no need to keep a global list of scsi_cmnd pointers indexed
+ * by OGMB. Again, SCBs reference their scsi_cmnds directly, so mailbox
+ * indices need not be involved.
+ */
+
+/*
+ * WD7000-specific scatter/gather element structure
+ */
+typedef struct sgb {
+ unchar len[3];
+ unchar ptr[3]; /* Also SCSI-style - MSB first */
+} Sgb;
+
+typedef struct scb { /* Command Control Block 5.4.1 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked */
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdb[12]; /* SCSI Command Block */
+ volatile unchar status; /* SCSI Return Status */
+ volatile unchar vue; /* Vendor Unique Error Code */
+ unchar maxlen[3]; /* Maximum Data Transfer Length */
+ unchar dataptr[3]; /* SCSI Data Block Pointer */
+ unchar linkptr[3]; /* Next Command Link Pointer */
+ unchar direc; /* Transfer Direction */
+ unchar reserved2[6]; /* SCSI Command Descriptor Block */
+ /* end of hardware SCB */
+ struct scsi_cmnd *SCpnt;/* scsi_cmnd using this SCB */
+ Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */
+ Adapter *host; /* host adapter */
+ struct scb *next; /* for lists of scbs */
+} Scb;
+
+/*
+ * This driver is written to allow host-only commands to be executed.
+ * These use a 16-byte block called an ICB. The format is extended by the
+ * driver to 18 bytes, to support the status returned in the ICMB and
+ * an execution phase code.
+ *
+ * There are other formats besides these; these are the ones I've tried
+ * to use. Formats for some of the defined ICB opcodes are not defined
+ * (notably, get/set unsolicited interrupt status) in my copy of the OEM
+ * manual, and others are ambiguous/hard to follow.
+ */
+#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */
+#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */
+#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */
+#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */
+#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */
+#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */
+#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */
+ /* 0x87 is reserved */
+#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */
+#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */
+#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */
+#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */
+#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */
+#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */
+#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */
+#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */
+
+typedef struct icbRecvCmd {
+ unchar op;
+ unchar IDlun; /* Initiator SCSI ID/lun */
+ unchar len[3]; /* command buffer length */
+ unchar ptr[3]; /* command buffer address */
+ unchar rsvd[7]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbRecvCmd;
+
+typedef struct icbSendStat {
+ unchar op;
+ unchar IDlun; /* Target SCSI ID/lun */
+ unchar stat; /* (outgoing) completion status byte 1 */
+ unchar rsvd[12]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbSendStat;
+
+typedef struct icbRevLvl {
+ unchar op;
+ volatile unchar primary; /* primary revision level (returned) */
+ volatile unchar secondary; /* secondary revision level (returned) */
+ unchar rsvd[12]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbRevLvl;
+
+typedef struct icbUnsMask { /* I'm totally guessing here */
+ unchar op;
+ volatile unchar mask[14]; /* mask bits */
+#if 0
+ unchar rsvd[12]; /* reserved */
+#endif
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbUnsMask;
+
+typedef struct icbDiag {
+ unchar op;
+ unchar type; /* diagnostics type code (0-3) */
+ unchar len[3]; /* buffer length */
+ unchar ptr[3]; /* buffer address */
+ unchar rsvd[7]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbDiag;
+
+#define ICB_DIAG_POWERUP 0 /* Power-up diags only */
+#define ICB_DIAG_WALKING 1 /* walking 1's pattern */
+#define ICB_DIAG_DMA 2 /* DMA - system memory diags */
+#define ICB_DIAG_FULL 3 /* do both 1 & 2 */
+
+typedef struct icbParms {
+ unchar op;
+ unchar rsvd1; /* reserved */
+ unchar len[3]; /* parms buffer length */
+ unchar ptr[3]; /* parms buffer address */
+ unchar idx[2]; /* index (MSB-LSB) */
+ unchar rsvd2[5]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbParms;
+
+typedef struct icbAny {
+ unchar op;
+ unchar data[14]; /* format-specific data */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbAny;
+
+typedef union icb {
+ unchar op; /* ICB opcode */
+ IcbRecvCmd recv_cmd; /* format for receive command */
+ IcbSendStat send_stat; /* format for send status */
+ IcbRevLvl rev_lvl; /* format for get revision level */
+ IcbDiag diag; /* format for execute diagnostics */
+ IcbParms eparms; /* format for get/set exec parms */
+ IcbAny icb; /* generic format */
+ unchar data[18];
+} Icb;
+
+#ifdef MODULE
+static char *wd7000;
+module_param(wd7000, charp, 0);
+#endif
+
+/*
+ * Driver SCB structure pool.
+ *
+ * The SCBs declared here are shared by all host adapters; hence, this
+ * structure is not part of the Adapter structure.
+ */
+static Scb scbs[MAX_SCBS];
+static Scb *scbfree; /* free list */
+static int freescbs = MAX_SCBS; /* free list counter */
+static spinlock_t scbpool_lock; /* guards the scb free list and count */
+
+/*
+ * END of data/declarations - code follows.
+ */
+static void __init setup_error(char *mesg, int *ints)
+{
+ if (ints[0] == 3)
+ printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n", ints[1], ints[2], ints[3], mesg);
+ else if (ints[0] == 4)
+ printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], mesg);
+ else
+ printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], ints[5], mesg);
+}
+
+
+/*
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ * wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]]
+ *
+ * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values
+ * are 8000ns for BUS_ON and 1875ns for BUS_OFF.
+ * eg:
+ * wd7000=7,6,0x350
+ *
+ * will configure the driver for a WD-7000 controller
+ * using IRQ 15 with a DMA channel 6, at IO base address 0x350.
+ */
+static int __init wd7000_setup(char *str)
+{
+ static short wd7000_card_num; /* .bss will zero this */
+ short i;
+ int ints[6];
+
+ (void) get_options(str, ARRAY_SIZE(ints), ints);
+
+ if (wd7000_card_num >= NUM_CONFIGS) {
+ printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __FUNCTION__);
+ return 0;
+ }
+
+ if ((ints[0] < 3) || (ints[0] > 5)) {
+ printk(KERN_ERR "%s: Error in command line! " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __FUNCTION__);
+ } else {
+ for (i = 0; i < NUM_IRQS; i++)
+ if (ints[1] == wd7000_irq[i])
+ break;
+
+ if (i == NUM_IRQS) {
+ setup_error("invalid IRQ.", ints);
+ return 0;
+ } else
+ configs[wd7000_card_num].irq = ints[1];
+
+ for (i = 0; i < NUM_DMAS; i++)
+ if (ints[2] == wd7000_dma[i])
+ break;
+
+ if (i == NUM_DMAS) {
+ setup_error("invalid DMA channel.", ints);
+ return 0;
+ } else
+ configs[wd7000_card_num].dma = ints[2];
+
+ for (i = 0; i < NUM_IOPORTS; i++)
+ if (ints[3] == wd7000_iobase[i])
+ break;
+
+ if (i == NUM_IOPORTS) {
+ setup_error("invalid I/O base address.", ints);
+ return 0;
+ } else
+ configs[wd7000_card_num].iobase = ints[3];
+
+ if (ints[0] > 3) {
+ if ((ints[4] < 500) || (ints[4] > 31875)) {
+ setup_error("BUS_ON value is out of range (500" " to 31875 nanoseconds)!", ints);
+ configs[wd7000_card_num].bus_on = BUS_ON;
+ } else
+ configs[wd7000_card_num].bus_on = ints[4] / 125;
+ } else
+ configs[wd7000_card_num].bus_on = BUS_ON;
+
+ if (ints[0] > 4) {
+ if ((ints[5] < 500) || (ints[5] > 31875)) {
+ setup_error("BUS_OFF value is out of range (500" " to 31875 nanoseconds)!", ints);
+ configs[wd7000_card_num].bus_off = BUS_OFF;
+ } else
+ configs[wd7000_card_num].bus_off = ints[5] / 125;
+ } else
+ configs[wd7000_card_num].bus_off = BUS_OFF;
+
+ if (wd7000_card_num) {
+ for (i = 0; i < (wd7000_card_num - 1); i++) {
+ int j = i + 1;
+
+ for (; j < wd7000_card_num; j++)
+ if (configs[i].irq == configs[j].irq) {
+ setup_error("duplicated IRQ!", ints);
+ return 0;
+ }
+ if (configs[i].dma == configs[j].dma) {
+ setup_error("duplicated DMA " "channel!", ints);
+ return 0;
+ }
+ if (configs[i].iobase == configs[j].iobase) {
+ setup_error("duplicated I/O " "base address!", ints);
+ return 0;
+ }
+ }
+ }
+
+ dprintk(KERN_DEBUG "wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, "
+ "BUS_ON=%dns, BUS_OFF=%dns\n", configs[wd7000_card_num].irq, configs[wd7000_card_num].dma, configs[wd7000_card_num].iobase, configs[wd7000_card_num].bus_on * 125, configs[wd7000_card_num].bus_off * 125);
+
+ wd7000_card_num++;
+ }
+ return 1;
+}
+
+__setup("wd7000=", wd7000_setup);
+
+static inline void any2scsi(unchar * scsi, int any)
+{
+ *scsi++ = (unsigned)any >> 16;
+ *scsi++ = (unsigned)any >> 8;
+ *scsi++ = any;
+}
+
+static inline int scsi2int(unchar * scsi)
+{
+ return (scsi[0] << 16) | (scsi[1] << 8) | scsi[2];
+}
+
+static inline void wd7000_enable_intr(Adapter * host)
+{
+ host->control |= INT_EN;
+ outb(host->control, host->iobase + ASC_CONTROL);
+}
+
+
+static inline void wd7000_enable_dma(Adapter * host)
+{
+ unsigned long flags;
+ host->control |= DMA_EN;
+ outb(host->control, host->iobase + ASC_CONTROL);
+
+ flags = claim_dma_lock();
+ set_dma_mode(host->dma, DMA_MODE_CASCADE);
+ enable_dma(host->dma);
+ release_dma_lock(flags);
+
+}
+
+
+#define WAITnexttimeout 200 /* 2 seconds */
+
+static inline short WAIT(unsigned port, unsigned mask, unsigned allof, unsigned noneof)
+{
+ unsigned WAITbits;
+ unsigned long WAITtimeout = jiffies + WAITnexttimeout;
+
+ while (time_before_eq(jiffies, WAITtimeout)) {
+ WAITbits = inb(port) & mask;
+
+ if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0))
+ return (0);
+ }
+
+ return (1);
+}
+
+
+static inline int command_out(Adapter * host, unchar * cmd, int len)
+{
+ if (!WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+ while (len--) {
+ do {
+ outb(*cmd, host->iobase + ASC_COMMAND);
+ WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+ } while (inb(host->iobase + ASC_STAT) & CMD_REJ);
+
+ cmd++;
+ }
+
+ return (1);
+ }
+
+ printk(KERN_WARNING "wd7000 command_out: WAIT failed(%d)\n", len + 1);
+
+ return (0);
+}
+
+
+/*
+ * This version of alloc_scbs is in preparation for supporting multiple
+ * commands per lun and command chaining, by queueing pending commands.
+ * We will need to allocate Scbs in blocks since they will wait to be
+ * executed so there is the possibility of deadlock otherwise.
+ * Also, to keep larger requests from being starved by smaller requests,
+ * we limit access to this routine with an internal busy flag, so that
+ * the satisfiability of a request is not dependent on the size of the
+ * request.
+ */
+static inline Scb *alloc_scbs(struct Scsi_Host *host, int needed)
+{
+ Scb *scb, *p = NULL;
+ unsigned long flags;
+ unsigned long timeout = jiffies + WAITnexttimeout;
+ unsigned long now;
+ int i;
+
+ if (needed <= 0)
+ return (NULL); /* sanity check */
+
+ spin_unlock_irq(host->host_lock);
+
+ retry:
+ while (freescbs < needed) {
+ timeout = jiffies + WAITnexttimeout;
+ do {
+ /* FIXME: can we actually just yield here ?? */
+ for (now = jiffies; now == jiffies;)
+ cpu_relax(); /* wait a jiffy */
+ } while (freescbs < needed && time_before_eq(jiffies, timeout));
+ /*
+ * If we get here with enough free Scbs, we can take them.
+ * Otherwise, we timed out and didn't get enough.
+ */
+ if (freescbs < needed) {
+ printk(KERN_ERR "wd7000: can't get enough free SCBs.\n");
+ return (NULL);
+ }
+ }
+
+ /* Take the lock, then check we didnt get beaten, if so try again */
+ spin_lock_irqsave(&scbpool_lock, flags);
+ if (freescbs < needed) {
+ spin_unlock_irqrestore(&scbpool_lock, flags);
+ goto retry;
+ }
+
+ scb = scbfree;
+ freescbs -= needed;
+ for (i = 0; i < needed; i++) {
+ p = scbfree;
+ scbfree = p->next;
+ }
+ p->next = NULL;
+
+ spin_unlock_irqrestore(&scbpool_lock, flags);
+
+ spin_lock_irq(host->host_lock);
+ return (scb);
+}
+
+
+static inline void free_scb(Scb * scb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scbpool_lock, flags);
+
+ memset(scb, 0, sizeof(Scb));
+ scb->next = scbfree;
+ scbfree = scb;
+ freescbs++;
+
+ spin_unlock_irqrestore(&scbpool_lock, flags);
+}
+
+
+static inline void init_scbs(void)
+{
+ int i;
+
+ spin_lock_init(&scbpool_lock);
+
+ /* This is only ever called before the SCB pool is active */
+
+ scbfree = &(scbs[0]);
+ memset(scbs, 0, sizeof(scbs));
+ for (i = 0; i < MAX_SCBS - 1; i++) {
+ scbs[i].next = &(scbs[i + 1]);
+ scbs[i].SCpnt = NULL;
+ }
+ scbs[MAX_SCBS - 1].next = NULL;
+ scbs[MAX_SCBS - 1].SCpnt = NULL;
+}
+
+
+static int mail_out(Adapter * host, Scb * scbptr)
+/*
+ * Note: this can also be used for ICBs; just cast to the parm type.
+ */
+{
+ int i, ogmb;
+ unsigned long flags;
+ unchar start_ogmb;
+ Mailbox *ogmbs = host->mb.ogmb;
+ int *next_ogmb = &(host->next_ogmb);
+
+ dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr);
+
+ /* We first look for a free outgoing mailbox */
+ spin_lock_irqsave(host->sh->host_lock, flags);
+ ogmb = *next_ogmb;
+ for (i = 0; i < OGMB_CNT; i++) {
+ if (ogmbs[ogmb].status == 0) {
+ dprintk(" using OGMB 0x%x", ogmb);
+ ogmbs[ogmb].status = 1;
+ any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
+
+ *next_ogmb = (ogmb + 1) % OGMB_CNT;
+ break;
+ } else
+ ogmb = (ogmb + 1) % OGMB_CNT;
+ }
+ spin_unlock_irqrestore(host->sh->host_lock, flags);
+
+ dprintk(", scb is 0x%06lx", (long) scbptr);
+
+ if (i >= OGMB_CNT) {
+ /*
+ * Alternatively, we might issue the "interrupt on free OGMB",
+ * and sleep, but it must be ensured that it isn't the init
+ * task running. Instead, this version assumes that the caller
+ * will be persistent, and try again. Since it's the adapter
+ * that marks OGMB's free, waiting even with interrupts off
+ * should work, since they are freed very quickly in most cases.
+ */
+ dprintk(", no free OGMBs.\n");
+ return (0);
+ }
+
+ wd7000_enable_intr(host);
+
+ start_ogmb = START_OGMB | ogmb;
+ command_out(host, &start_ogmb, 1);
+
+ dprintk(", awaiting interrupt.\n");
+
+ return (1);
+}
+
+
+static int make_code(unsigned hosterr, unsigned scsierr)
+{
+#ifdef WD7000_DEBUG
+ int in_error = hosterr;
+#endif
+
+ switch ((hosterr >> 8) & 0xff) {
+ case 0: /* Reserved */
+ hosterr = DID_ERROR;
+ break;
+ case 1: /* Command Complete, no errors */
+ hosterr = DID_OK;
+ break;
+ case 2: /* Command complete, error logged in scb status (scsierr) */
+ hosterr = DID_OK;
+ break;
+ case 4: /* Command failed to complete - timeout */
+ hosterr = DID_TIME_OUT;
+ break;
+ case 5: /* Command terminated; Bus reset by external device */
+ hosterr = DID_RESET;
+ break;
+ case 6: /* Unexpected Command Received w/ host as target */
+ hosterr = DID_BAD_TARGET;
+ break;
+ case 80: /* Unexpected Reselection */
+ case 81: /* Unexpected Selection */
+ hosterr = DID_BAD_INTR;
+ break;
+ case 82: /* Abort Command Message */
+ hosterr = DID_ABORT;
+ break;
+ case 83: /* SCSI Bus Software Reset */
+ case 84: /* SCSI Bus Hardware Reset */
+ hosterr = DID_RESET;
+ break;
+ default: /* Reserved */
+ hosterr = DID_ERROR;
+ }
+#ifdef WD7000_DEBUG
+ if (scsierr || hosterr)
+ dprintk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr);
+#endif
+ return (scsierr | (hosterr << 16));
+}
+
+#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK)
+
+
+static irqreturn_t wd7000_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ Adapter *host = (Adapter *) dev_id;
+ int flag, icmb, errstatus, icmb_status;
+ int host_error, scsi_error;
+ Scb *scb; /* for SCSI commands */
+ IcbAny *icb; /* for host commands */
+ struct scsi_cmnd *SCpnt;
+ Mailbox *icmbs = host->mb.icmb;
+ unsigned long flags;
+
+ spin_lock_irqsave(host->sh->host_lock, flags);
+ host->int_counter++;
+
+ dprintk("wd7000_intr: irq = %d, host = 0x%06lx\n", irq, (long) host);
+
+ flag = inb(host->iobase + ASC_INTR_STAT);
+
+ dprintk("wd7000_intr: intr stat = 0x%02x\n", flag);
+
+ if (!(inb(host->iobase + ASC_STAT) & INT_IM)) {
+ /* NB: these are _very_ possible if IRQ 15 is being used, since
+ * it's the "garbage collector" on the 2nd 8259 PIC. Specifically,
+ * any interrupt signal into the 8259 which can't be identified
+ * comes out as 7 from the 8259, which is 15 to the host. Thus, it
+ * is a good thing the WD7000 has an interrupt status port, so we
+ * can sort these out. Otherwise, electrical noise and other such
+ * problems would be indistinguishable from valid interrupts...
+ */
+ dprintk("wd7000_intr: phantom interrupt...\n");
+ goto ack;
+ }
+
+ if (!(flag & MB_INTR))
+ goto ack;
+
+ /* The interrupt is for a mailbox */
+ if (!(flag & IMB_INTR)) {
+ dprintk("wd7000_intr: free outgoing mailbox\n");
+ /*
+ * If sleep_on() and the "interrupt on free OGMB" command are
+ * used in mail_out(), wake_up() should correspondingly be called
+ * here. For now, we don't need to do anything special.
+ */
+ goto ack;
+ }
+
+ /* The interrupt is for an incoming mailbox */
+ icmb = flag & MB_MASK;
+ icmb_status = icmbs[icmb].status;
+ if (icmb_status & 0x80) { /* unsolicited - result in ICMB */
+ dprintk("wd7000_intr: unsolicited interrupt 0x%02x\n", icmb_status);
+ goto ack;
+ }
+
+ /* Aaaargh! (Zaga) */
+ scb = isa_bus_to_virt(scsi2int((unchar *) icmbs[icmb].scbptr));
+ icmbs[icmb].status = 0;
+ if (scb->op & ICB_OP_MASK) { /* an SCB is done */
+ icb = (IcbAny *) scb;
+ icb->status = icmb_status;
+ icb->phase = 0;
+ goto ack;
+ }
+
+ SCpnt = scb->SCpnt;
+ if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */
+ host_error = scb->vue | (icmb_status << 8);
+ scsi_error = scb->status;
+ errstatus = make_code(host_error, scsi_error);
+ SCpnt->result = errstatus;
+
+ free_scb(scb);
+
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ ack:
+ dprintk("wd7000_intr: return from interrupt handler\n");
+ wd7000_intr_ack(host);
+
+ spin_unlock_irqrestore(host->sh->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int wd7000_queuecommand(struct scsi_cmnd *SCpnt,
+ void (*done)(struct scsi_cmnd *))
+{
+ Scb *scb;
+ Sgb *sgb;
+ unchar *cdb = (unchar *) SCpnt->cmnd;
+ unchar idlun;
+ short cdblen;
+ Adapter *host = (Adapter *) SCpnt->device->host->hostdata;
+
+ cdblen = SCpnt->cmd_len;
+ idlun = ((SCpnt->device->id << 5) & 0xe0) | (SCpnt->device->lun & 7);
+ SCpnt->scsi_done = done;
+ SCpnt->SCp.phase = 1;
+ scb = alloc_scbs(SCpnt->device->host, 1);
+ scb->idlun = idlun;
+ memcpy(scb->cdb, cdb, cdblen);
+ scb->direc = 0x40; /* Disable direction check */
+
+ scb->SCpnt = SCpnt; /* so we can find stuff later */
+ SCpnt->host_scribble = (unchar *) scb;
+ scb->host = host;
+
+ if (SCpnt->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+ unsigned i;
+
+ if (SCpnt->device->host->sg_tablesize == SG_NONE) {
+ panic("wd7000_queuecommand: scatter/gather not supported.\n");
+ }
+ dprintk("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
+
+ sgb = scb->sgb;
+ scb->op = 1;
+ any2scsi(scb->dataptr, (int) sgb);
+ any2scsi(scb->maxlen, SCpnt->use_sg * sizeof(Sgb));
+
+ for (i = 0; i < SCpnt->use_sg; i++) {
+ any2scsi(sgb[i].ptr, isa_page_to_bus(sg[i].page) + sg[i].offset);
+ any2scsi(sgb[i].len, sg[i].length);
+ }
+ } else {
+ scb->op = 0;
+ any2scsi(scb->dataptr, isa_virt_to_bus(SCpnt->request_buffer));
+ any2scsi(scb->maxlen, SCpnt->request_bufflen);
+ }
+
+ /* FIXME: drop lock and yield here ? */
+
+ while (!mail_out(host, scb))
+ cpu_relax(); /* keep trying */
+
+ return 0;
+}
+
+static int wd7000_diagnostics(Adapter * host, int code)
+{
+ static IcbDiag icb = { ICB_OP_DIAGNOSTICS };
+ static unchar buf[256];
+ unsigned long timeout;
+
+ icb.type = code;
+ any2scsi(icb.len, sizeof(buf));
+ any2scsi(icb.ptr, (int) &buf);
+ icb.phase = 1;
+ /*
+ * This routine is only called at init, so there should be OGMBs
+ * available. I'm assuming so here. If this is going to
+ * fail, I can just let the timeout catch the failure.
+ */
+ mail_out(host, (struct scb *) &icb);
+ timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */
+ while (icb.phase && time_before(jiffies, timeout)) {
+ cpu_relax(); /* wait for completion */
+ barrier();
+ }
+
+ if (icb.phase) {
+ printk("wd7000_diagnostics: timed out.\n");
+ return (0);
+ }
+ if (make_code(icb.vue | (icb.status << 8), 0)) {
+ printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status);
+ return (0);
+ }
+
+ return (1);
+}
+
+
+static int wd7000_adapter_reset(Adapter * host)
+{
+ InitCmd init_cmd = {
+ INITIALIZATION,
+ 7,
+ host->bus_on,
+ host->bus_off,
+ 0,
+ {0, 0, 0},
+ OGMB_CNT,
+ ICMB_CNT
+ };
+ int diag;
+ /*
+ * Reset the adapter - only. The SCSI bus was initialized at power-up,
+ * and we need to do this just so we control the mailboxes, etc.
+ */
+ outb(ASC_RES, host->iobase + ASC_CONTROL);
+ udelay(40); /* reset pulse: this is 40us, only need 25us */
+ outb(0, host->iobase + ASC_CONTROL);
+ host->control = 0; /* this must always shadow ASC_CONTROL */
+
+ if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+ printk(KERN_ERR "wd7000_init: WAIT timed out.\n");
+ return -1; /* -1 = not ok */
+ }
+
+ if ((diag = inb(host->iobase + ASC_INTR_STAT)) != 1) {
+ printk("wd7000_init: ");
+
+ switch (diag) {
+ case 2:
+ printk(KERN_ERR "RAM failure.\n");
+ break;
+ case 3:
+ printk(KERN_ERR "FIFO R/W failed\n");
+ break;
+ case 4:
+ printk(KERN_ERR "SBIC register R/W failed\n");
+ break;
+ case 5:
+ printk(KERN_ERR "Initialization D-FF failed.\n");
+ break;
+ case 6:
+ printk(KERN_ERR "Host IRQ D-FF failed.\n");
+ break;
+ case 7:
+ printk(KERN_ERR "ROM checksum error.\n");
+ break;
+ default:
+ printk(KERN_ERR "diagnostic code 0x%02Xh received.\n", diag);
+ }
+ return -1;
+ }
+ /* Clear mailboxes */
+ memset(&(host->mb), 0, sizeof(host->mb));
+
+ /* Execute init command */
+ any2scsi((unchar *) & (init_cmd.mailboxes), (int) &(host->mb));
+ if (!command_out(host, (unchar *) & init_cmd, sizeof(init_cmd))) {
+ printk(KERN_ERR "wd7000_adapter_reset: adapter initialization failed.\n");
+ return -1;
+ }
+
+ if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {
+ printk("wd7000_adapter_reset: WAIT timed out.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int wd7000_init(Adapter * host)
+{
+ if (wd7000_adapter_reset(host) == -1)
+ return 0;
+
+
+ if (request_irq(host->irq, wd7000_intr, SA_INTERRUPT, "wd7000", host)) {
+ printk("wd7000_init: can't get IRQ %d.\n", host->irq);
+ return (0);
+ }
+ if (request_dma(host->dma, "wd7000")) {
+ printk("wd7000_init: can't get DMA channel %d.\n", host->dma);
+ free_irq(host->irq, host);
+ return (0);
+ }
+ wd7000_enable_dma(host);
+ wd7000_enable_intr(host);
+
+ if (!wd7000_diagnostics(host, ICB_DIAG_FULL)) {
+ free_dma(host->dma);
+ free_irq(host->irq, NULL);
+ return (0);
+ }
+
+ return (1);
+}
+
+
+static void wd7000_revision(Adapter * host)
+{
+ static IcbRevLvl icb = { ICB_OP_GET_REVISION };
+
+ icb.phase = 1;
+ /*
+ * Like diagnostics, this is only done at init time, in fact, from
+ * wd7000_detect, so there should be OGMBs available. If it fails,
+ * the only damage will be that the revision will show up as 0.0,
+ * which in turn means that scatter/gather will be disabled.
+ */
+ mail_out(host, (struct scb *) &icb);
+ while (icb.phase) {
+ cpu_relax(); /* wait for completion */
+ barrier();
+ }
+ host->rev1 = icb.primary;
+ host->rev2 = icb.secondary;
+}
+
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+static int wd7000_set_info(char *buffer, int length, struct Scsi_Host *host)
+{
+ dprintk("Buffer = <%.*s>, length = %d\n", length, buffer, length);
+
+ /*
+ * Currently this is a no-op
+ */
+ dprintk("Sorry, this function is currently out of order...\n");
+ return (length);
+}
+
+
+static int wd7000_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout)
+{
+ Adapter *adapter = (Adapter *)host->hostdata;
+ unsigned long flags;
+ char *pos = buffer;
+#ifdef WD7000_DEBUG
+ Mailbox *ogmbs, *icmbs;
+ short count;
+#endif
+
+ /*
+ * Has data been written to the file ?
+ */
+ if (inout)
+ return (wd7000_set_info(buffer, length, host));
+
+ spin_lock_irqsave(host->host_lock, flags);
+ SPRINTF("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", host->host_no, adapter->rev1, adapter->rev2);
+ SPRINTF(" IO base: 0x%x\n", adapter->iobase);
+ SPRINTF(" IRQ: %d\n", adapter->irq);
+ SPRINTF(" DMA channel: %d\n", adapter->dma);
+ SPRINTF(" Interrupts: %d\n", adapter->int_counter);
+ SPRINTF(" BUS_ON time: %d nanoseconds\n", adapter->bus_on * 125);
+ SPRINTF(" BUS_OFF time: %d nanoseconds\n", adapter->bus_off * 125);
+
+#ifdef WD7000_DEBUG
+ ogmbs = adapter->mb.ogmb;
+ icmbs = adapter->mb.icmb;
+
+ SPRINTF("\nControl port value: 0x%x\n", adapter->control);
+ SPRINTF("Incoming mailbox:\n");
+ SPRINTF(" size: %d\n", ICMB_CNT);
+ SPRINTF(" queued messages: ");
+
+ for (i = count = 0; i < ICMB_CNT; i++)
+ if (icmbs[i].status) {
+ count++;
+ SPRINTF("0x%x ", i);
+ }
+
+ SPRINTF(count ? "\n" : "none\n");
+
+ SPRINTF("Outgoing mailbox:\n");
+ SPRINTF(" size: %d\n", OGMB_CNT);
+ SPRINTF(" next message: 0x%x\n", adapter->next_ogmb);
+ SPRINTF(" queued messages: ");
+
+ for (i = count = 0; i < OGMB_CNT; i++)
+ if (ogmbs[i].status) {
+ count++;
+ SPRINTF("0x%x ", i);
+ }
+
+ SPRINTF(count ? "\n" : "none\n");
+#endif
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+
+/*
+ * Returns the number of adapters this driver is supporting.
+ *
+ * The source for hosts.c says to wait to call scsi_register until 100%
+ * sure about an adapter. We need to do it a little sooner here; we
+ * need the storage set up by scsi_register before wd7000_init, and
+ * changing the location of an Adapter structure is more trouble than
+ * calling scsi_unregister.
+ *
+ */
+
+static int wd7000_detect(struct scsi_host_template *tpnt)
+{
+ short present = 0, biosaddr_ptr, sig_ptr, i, pass;
+ short biosptr[NUM_CONFIGS];
+ unsigned iobase;
+ Adapter *host = NULL;
+ struct Scsi_Host *sh;
+ int unit = 0;
+
+ dprintk("wd7000_detect: started\n");
+
+#ifdef MODULE
+ if (wd7000)
+ wd7000_setup(wd7000);
+#endif
+
+ for (i = 0; i < UNITS; wd7000_host[i++] = NULL);
+ for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
+
+ tpnt->proc_name = "wd7000";
+ tpnt->proc_info = &wd7000_proc_info;
+
+ /*
+ * Set up SCB free list, which is shared by all adapters
+ */
+ init_scbs();
+
+ for (pass = 0; pass < NUM_CONFIGS; pass++) {
+ /*
+ * First, search for BIOS SIGNATURE...
+ */
+ for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
+ for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) {
+ for (i = 0; i < pass; i++)
+ if (biosptr[i] == biosaddr_ptr)
+ break;
+
+ if (i == pass) {
+ void __iomem *biosaddr = ioremap(wd7000_biosaddr[biosaddr_ptr] + signatures[sig_ptr].ofs,
+ signatures[sig_ptr].len);
+ short bios_match = 1;
+
+ if (biosaddr)
+ bios_match = check_signature(biosaddr, signatures[sig_ptr].sig, signatures[sig_ptr].len);
+
+ iounmap(biosaddr);
+
+ if (bios_match)
+ goto bios_matched;
+ }
+ }
+
+ bios_matched:
+ /*
+ * BIOS SIGNATURE has been found.
+ */
+#ifdef WD7000_DEBUG
+ dprintk("wd7000_detect: pass %d\n", pass + 1);
+
+ if (biosaddr_ptr == NUM_ADDRS)
+ dprintk("WD-7000 SST BIOS not detected...\n");
+ else
+ dprintk("WD-7000 SST BIOS detected at 0x%lx: checking...\n", wd7000_biosaddr[biosaddr_ptr]);
+#endif
+
+ if (configs[pass].irq < 0)
+ continue;
+
+ if (unit == UNITS)
+ continue;
+
+ iobase = configs[pass].iobase;
+
+ dprintk("wd7000_detect: check IO 0x%x region...\n", iobase);
+
+ if (request_region(iobase, 4, "wd7000")) {
+
+ dprintk("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
+ /*
+ * ASC reset...
+ */
+ outb(ASC_RES, iobase + ASC_CONTROL);
+ msleep(10);
+ outb(0, iobase + ASC_CONTROL);
+
+ if (WAIT(iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+ dprintk("failed!\n");
+ goto err_release;
+ } else
+ dprintk("ok!\n");
+
+ if (inb(iobase + ASC_INTR_STAT) == 1) {
+ /*
+ * We register here, to get a pointer to the extra space,
+ * which we'll use as the Adapter structure (host) for
+ * this adapter. It is located just after the registered
+ * Scsi_Host structure (sh), and is located by the empty
+ * array hostdata.
+ */
+ sh = scsi_register(tpnt, sizeof(Adapter));
+ if (sh == NULL)
+ goto err_release;
+
+ host = (Adapter *) sh->hostdata;
+
+ dprintk("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
+ memset(host, 0, sizeof(Adapter));
+
+ host->irq = configs[pass].irq;
+ host->dma = configs[pass].dma;
+ host->iobase = iobase;
+ host->int_counter = 0;
+ host->bus_on = configs[pass].bus_on;
+ host->bus_off = configs[pass].bus_off;
+ host->sh = wd7000_host[unit] = sh;
+ unit++;
+
+ dprintk("wd7000_detect: Trying init WD-7000 card at IO " "0x%x, IRQ %d, DMA %d...\n", host->iobase, host->irq, host->dma);
+
+ if (!wd7000_init(host)) /* Initialization failed */
+ goto err_unregister;
+
+ /*
+ * OK from here - we'll use this adapter/configuration.
+ */
+ wd7000_revision(host); /* important for scatter/gather */
+
+ /*
+ * For boards before rev 6.0, scatter/gather isn't supported.
+ */
+ if (host->rev1 < 6)
+ sh->sg_tablesize = SG_NONE;
+
+ present++; /* count it */
+
+ if (biosaddr_ptr != NUM_ADDRS)
+ biosptr[pass] = biosaddr_ptr;
+
+ printk(KERN_INFO "Western Digital WD-7000 (rev %d.%d) ", host->rev1, host->rev2);
+ printk("using IO 0x%x, IRQ %d, DMA %d.\n", host->iobase, host->irq, host->dma);
+ printk(" BUS_ON time: %dns, BUS_OFF time: %dns\n", host->bus_on * 125, host->bus_off * 125);
+ }
+ } else
+ dprintk("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
+
+ continue;
+
+ err_unregister:
+ scsi_unregister(sh);
+ err_release:
+ release_region(iobase, 4);
+
+ }
+
+ if (!present)
+ printk("Failed initialization of WD-7000 SCSI card!\n");
+
+ return (present);
+}
+
+static int wd7000_release(struct Scsi_Host *shost)
+{
+ if (shost->irq)
+ free_irq(shost->irq, NULL);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+ scsi_unregister(shost);
+ return 0;
+}
+
+#if 0
+/*
+ * I have absolutely NO idea how to do an abort with the WD7000...
+ */
+static int wd7000_abort(Scsi_Cmnd * SCpnt)
+{
+ Adapter *host = (Adapter *) SCpnt->device->host->hostdata;
+
+ if (inb(host->iobase + ASC_STAT) & INT_IM) {
+ printk("wd7000_abort: lost interrupt\n");
+ wd7000_intr_handle(host->irq, NULL, NULL);
+ return FAILED;
+ }
+ return FAILED;
+}
+#endif
+
+/*
+ * Last resort. Reinitialize the board.
+ */
+
+static int wd7000_host_reset(struct scsi_cmnd *SCpnt)
+{
+ Adapter *host = (Adapter *) SCpnt->device->host->hostdata;
+
+ if (wd7000_adapter_reset(host) < 0)
+ return FAILED;
+ wd7000_enable_intr(host);
+ return SUCCESS;
+}
+
+/*
+ * This was borrowed directly from aha1542.c. (Zaga)
+ */
+
+static int wd7000_biosparam(struct scsi_device *sdev,
+ struct block_device *bdev, sector_t capacity, int *ip)
+{
+ char b[BDEVNAME_SIZE];
+
+ dprintk("wd7000_biosparam: dev=%s, size=%d, ",
+ bdevname(bdev, b), capacity);
+ (void)b; /* unused var warning? */
+
+ /*
+ * try default translation
+ */
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = capacity >> 11;
+
+ /*
+ * for disks >1GB do some guessing
+ */
+ if (ip[2] >= 1024) {
+ int info[3];
+
+ /*
+ * try to figure out the geometry from the partition table
+ */
+ if ((scsicam_bios_param(bdev, capacity, info) < 0) || !(((info[0] == 64) && (info[1] == 32)) || ((info[0] == 255) && (info[1] == 63)))) {
+ printk("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n" " using extended translation.\n");
+
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = (unsigned long) capacity / (255 * 63);
+ } else {
+ ip[0] = info[0];
+ ip[1] = info[1];
+ ip[2] = info[2];
+
+ if (info[0] == 255)
+ printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __FUNCTION__);
+ }
+ }
+
+ dprintk("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+ dprintk("WARNING: check, if the bios geometry is correct.\n");
+
+ return (0);
+}
+
+MODULE_AUTHOR("Thomas Wuensche, John Boyd, Miroslav Zagorac");
+MODULE_DESCRIPTION("Driver for the WD7000 series ISA controllers");
+MODULE_LICENSE("GPL");
+
+static struct scsi_host_template driver_template = {
+ .proc_name = "wd7000",
+ .proc_info = wd7000_proc_info,
+ .name = "Western Digital WD-7000",
+ .detect = wd7000_detect,
+ .release = wd7000_release,
+ .queuecommand = wd7000_queuecommand,
+ .eh_host_reset_handler = wd7000_host_reset,
+ .bios_param = wd7000_biosparam,
+ .can_queue = WD7000_Q,
+ .this_id = 7,
+ .sg_tablesize = WD7000_SG,
+ .cmd_per_lun = 1,
+ .unchecked_isa_dma = 1,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/zalon.c b/drivers/scsi/zalon.c
new file mode 100644
index 000000000000..5a51051e31f0
--- /dev/null
+++ b/drivers/scsi/zalon.c
@@ -0,0 +1,205 @@
+/*
+ * Zalon 53c7xx device driver.
+ * By Richard Hirst (rhirst@linuxcare.com)
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+#include "../parisc/gsc.h"
+
+#include "ncr53c8xx.h"
+
+MODULE_AUTHOR("Richard Hirst");
+MODULE_DESCRIPTION("Bluefish/Zalon 720 SCSI Driver");
+MODULE_LICENSE("GPL");
+
+#define GSC_SCSI_ZALON_OFFSET 0x800
+
+#define IO_MODULE_EIM (1*4)
+#define IO_MODULE_DC_ADATA (2*4)
+#define IO_MODULE_II_CDATA (3*4)
+#define IO_MODULE_IO_COMMAND (12*4)
+#define IO_MODULE_IO_STATUS (13*4)
+
+#define IOSTATUS_RY 0x40
+#define IOSTATUS_FE 0x80
+#define IOIIDATA_SMINT5L 0x40000000
+#define IOIIDATA_MINT5EN 0x20000000
+#define IOIIDATA_PACKEN 0x10000000
+#define IOIIDATA_PREFETCHEN 0x08000000
+#define IOIIDATA_IOII 0x00000020
+
+#define CMD_RESET 5
+
+static struct ncr_chip zalon720_chip __initdata = {
+ .revision_id = 0x0f,
+ .burst_max = 3,
+ .offset_max = 8,
+ .nr_divisor = 4,
+ .features = FE_WIDE | FE_DIFF | FE_EHP| FE_MUX | FE_EA,
+};
+
+
+
+#if 0
+/* FIXME:
+ * Is this function dead code? or is someone planning on using it in the
+ * future. The clock = (int) pdc_result[16] does not look correct to
+ * me ... I think it should be iodc_data[16]. Since this cause a compile
+ * error with the new encapsulated PDC, I'm not compiling in this function.
+ * - RB
+ */
+/* poke SCSI clock out of iodc data */
+
+static u8 iodc_data[32] __attribute__ ((aligned (64)));
+static unsigned long pdc_result[32] __attribute__ ((aligned (16))) ={0,0,0,0};
+
+static int
+lasi_scsi_clock(void * hpa, int defaultclock)
+{
+ int clock, status;
+
+ status = pdc_iodc_read(&pdc_result, hpa, 0, &iodc_data, 32 );
+ if (status == PDC_RET_OK) {
+ clock = (int) pdc_result[16];
+ } else {
+ printk(KERN_WARNING "%s: pdc_iodc_read returned %d\n", __FUNCTION__, status);
+ clock = defaultclock;
+ }
+
+ printk(KERN_DEBUG "%s: SCSI clock %d\n", __FUNCTION__, clock);
+ return clock;
+}
+#endif
+
+static struct scsi_host_template zalon7xx_template = {
+ .module = THIS_MODULE,
+ .proc_name = "zalon7xx",
+};
+
+static int __init
+zalon_probe(struct parisc_device *dev)
+{
+ struct gsc_irq gsc_irq;
+ u32 zalon_vers;
+ int error = -ENODEV;
+ void __iomem *zalon = ioremap(dev->hpa, 4096);
+ void __iomem *io_port = zalon + GSC_SCSI_ZALON_OFFSET;
+ static int unit = 0;
+ struct Scsi_Host *host;
+ struct ncr_device device;
+
+ __raw_writel(CMD_RESET, zalon + IO_MODULE_IO_COMMAND);
+ while (!(__raw_readl(zalon + IO_MODULE_IO_STATUS) & IOSTATUS_RY))
+ cpu_relax();
+ __raw_writel(IOIIDATA_MINT5EN | IOIIDATA_PACKEN | IOIIDATA_PREFETCHEN,
+ zalon + IO_MODULE_II_CDATA);
+
+ /* XXX: Save the Zalon version for bug workarounds? */
+ zalon_vers = (__raw_readl(zalon + IO_MODULE_II_CDATA) >> 24) & 0x07;
+
+ /* Setup the interrupts first.
+ ** Later on request_irq() will register the handler.
+ */
+ dev->irq = gsc_alloc_irq(&gsc_irq);
+
+ printk(KERN_INFO "%s: Zalon version %d, IRQ %d\n", __FUNCTION__,
+ zalon_vers, dev->irq);
+
+ __raw_writel(gsc_irq.txn_addr | gsc_irq.txn_data, zalon + IO_MODULE_EIM);
+
+ if (zalon_vers == 0)
+ printk(KERN_WARNING "%s: Zalon 1.1 or earlier\n", __FUNCTION__);
+
+ memset(&device, 0, sizeof(struct ncr_device));
+
+ /* The following three are needed before any other access. */
+ __raw_writeb(0x20, io_port + 0x38); /* DCNTL_REG, EA */
+ __raw_writeb(0x04, io_port + 0x1b); /* CTEST0_REG, EHP */
+ __raw_writeb(0x80, io_port + 0x22); /* CTEST4_REG, MUX */
+
+ /* Initialise ncr_device structure with items required by ncr_attach. */
+ device.chip = zalon720_chip;
+ device.host_id = 7;
+ device.dev = &dev->dev;
+ device.slot.base = dev->hpa + GSC_SCSI_ZALON_OFFSET;
+ device.slot.base_v = io_port;
+ device.slot.irq = dev->irq;
+ device.differential = 2;
+
+ host = ncr_attach(&zalon7xx_template, unit, &device);
+ if (!host)
+ goto fail;
+
+ if (request_irq(dev->irq, ncr53c8xx_intr, SA_SHIRQ, "zalon", host)) {
+ printk(KERN_ERR "%s: irq problem with %d, detaching\n ",
+ dev->dev.bus_id, dev->irq);
+ goto fail;
+ }
+
+ unit++;
+
+ dev_set_drvdata(&dev->dev, host);
+
+ error = scsi_add_host(host, &dev->dev);
+ if (error)
+ goto fail_free_irq;
+
+ scsi_scan_host(host);
+ return 0;
+
+ fail_free_irq:
+ free_irq(dev->irq, host);
+ fail:
+ ncr53c8xx_release(host);
+ return error;
+}
+
+static struct parisc_device_id zalon_tbl[] = {
+ { HPHW_A_DMA, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00089 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, zalon_tbl);
+
+static int __exit zalon_remove(struct parisc_device *dev)
+{
+ struct Scsi_Host *host = dev_get_drvdata(&dev->dev);
+
+ scsi_remove_host(host);
+ ncr53c8xx_release(host);
+ free_irq(dev->irq, host);
+
+ return 0;
+}
+
+static struct parisc_driver zalon_driver = {
+ .name = "zalon",
+ .id_table = zalon_tbl,
+ .probe = zalon_probe,
+ .remove = __devexit_p(zalon_remove),
+};
+
+static int __init zalon7xx_init(void)
+{
+ int ret = ncr53c8xx_init();
+ if (!ret)
+ ret = register_parisc_driver(&zalon_driver);
+ if (ret)
+ ncr53c8xx_exit();
+ return ret;
+}
+
+static void __exit zalon7xx_exit(void)
+{
+ unregister_parisc_driver(&zalon_driver);
+ ncr53c8xx_exit();
+}
+
+module_init(zalon7xx_init);
+module_exit(zalon7xx_exit);